[
  {
    "path": ".github/workflows/maven.yml",
    "content": "name: Java CI\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v1\n    - name: Set up JDK 1.8\n      uses: actions/setup-java@v1\n      with:\n        java-version: 1.8\n    - name: Build with Maven\n      run: mvn -B package --file pom.xml\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\n*.iml\n.svn/*\ntarget/\n*.class\n.settings\n.classpath\n.project\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "[中文](README_CN.md) | [EN](README_EN.md)\r\n\r\n![cachecloud云平台](cachecloud-web/src/main/resources/static/img/readme/cachecloud-head.png)\r\n\r\n[![CI checks on main badge]][CI checks on main link] [![latest release badge]][latest release link] [![github stars badge]][github stars link] [![github forks badge]][github forks link] [![github open issues badge]][github open issues link] [![github open prs badge]][github open prs link] [![latest commit to main badge]][latest commit to main link]\r\n\r\n[CI checks on main badge]: https://flat.badgen.net/github/checks/sohutv/cachecloud/main?label=CI%20status%20on%20main&cache=900&icon=github\r\n[CI checks on main link]:https://github.com/sohutv/cachecloud/actions?query=branch%3Amain\r\n[github forks badge]: https://flat.badgen.net/github/forks/sohutv/cachecloud?icon=github\r\n[github forks link]: https://useful-forks.github.io/?repo=sohutv%2Fcachecloud\r\n[github open issues badge]: https://flat.badgen.net/github/open-issues/sohutv/cachecloud?icon=github\r\n[github open issues link]: https://github.com/sohutv/cachecloud/issues?q=is%3Aissue+is%3Aopen\r\n[github open prs badge]: https://flat.badgen.net/github/open-prs/sohutv/cachecloud?icon=github\r\n[github open prs link]: https://github.com/sohutv/cachecloud/pulls?q=is%3Apr+is%3Aopen\r\n[github stars badge]: https://flat.badgen.net/github/stars/sohutv/cachecloud?icon=github\r\n[github stars link]: https://github.com/sohutv/cachecloud/stargazers\r\n[latest commit to main badge]: https://flat.badgen.net/github/last-commit/sohutv/cachecloud/main?icon=github&color=yellow&label=last%20dev%20commit&cache=900\r\n[latest commit to main link]: https://github.com/sohutv/cachecloud/commits/main\r\n[latest release badge]: https://flat.badgen.net/github/release/sohutv/cachecloud/development?icon=github\r\n[latest release link]: https://github.com/sohutv/cachecloud/releases\r\n\r\n<div align=\"center\">\r\n  <h2>CacheCloud云平台</h2>\r\n  <a href=\"https://github.com/sohutv/cachecloud/blob/main/cachecloud-web/src/main/resources/static/wiki/quickstart/index.md\">Quickstart</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/blob/main/cachecloud-web/src/main/resources/static/wiki/access/client.md\">Client</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/wiki\">Docs</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/wiki#%E5%85%ADfaq%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98\">FAQ</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"http://43.137.44.6:8080/admin/app/list\">Demo</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/issues/289\">Feedback</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud#contact\">Contact</a>\r\n  <hr />\r\n</div>\r\n\r\n\r\n## CacheCloud是什么？\r\n\r\nCacheCloud是一个Redis云管理平台：支持Redis多种架构(Standalone、Sentinel、Cluster)高效管理、有效降低大规模redis运维成本，提升资源管控能力和利用率。平台提供快速搭建/迁移，运维管理，弹性伸缩，统计监控，客户端整合接入等功能。\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/cachecloud-info.png\" width=\"100%\"/>\r\n\r\n## CacheCloud功能架构\r\n\r\n+ Redis搭建：宿主环境初始化、实例部署安装、类型架构支持；\r\n+ 运维管理：宿主环境、资源管理、应用审计、应用运维、质量监控、诊断分析；\r\n+ 统计监控：日志采集、实例采集、机器采集、应用统计、监控告警、问题诊断；\r\n+ 客户端接入：SDK接入、语言接入、客户端监控；\r\n+ 弹性伸缩：资源收缩、应用伸缩、外部接入；\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/CacheCloud功能架构.png\" width=\"100%\"/>\r\n\r\n<a name=\"cc4\"/>\r\n\r\n##  CacheCloud使用规模\r\n\r\n+ 800亿+ commands/day\r\n+ 18T+ Memory Total\r\n+ 420+ app Total / 4800+ Instances Total\r\n+ 80+ Physical machine/ 360+ K8s Pod Total\r\n\r\n<a name=\"cc5\"/>\r\n\r\n## CacheCloud VS 云厂商\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/sentinel-cost.png\" width=\"50%\"/><img src=\"cachecloud-web/src/main/resources/static/img/readme/cluster-cost.png\" width=\"50%\"/>\r\n\r\n<div align=\"center\">Redis 主从/集群部署成本</div>\r\n\r\n## 贡献成员\r\n\r\n<a href=\"https://github.com/sohutv/cachecloud/graphs/contributors\">\r\n  <img src=\"https://contrib.rocks/image?repo=sohutv/cachecloud\" />\r\n</a>\r\n\r\n## 感谢支持者\r\n\r\n![Stargazers repo roster for @sohutv/cachecloud](https://bytecrank.com/nastyox/reporoster/php/stargazersSVG.php?user=sohutv&repo=cachecloud)\r\n![Forkers repo roster for @sohutv/cachecloud](https://bytecrank.com/nastyox/reporoster/php/forkersSVG.php?user=sohutv&repo=cachecloud)\r\n\r\n<a name=\"contact\"/>\r\n\r\n## 联系我们\r\n\r\n+ QQ群: 534429768(已满) / 2群:894022242 / 3群:908821300\r\n\r\n+ 微信群:\r\n\r\n<img src=\"http://photocdn.tv.sohu.com/img/cachecloud/weixin.jpg\" width=\"30%\"/>\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/subcribe.png\" width=\"30%\"/>\r\n\r\n+ 微信：如果大家有公网资源可以联系我，会加入到开源版本服务资源部署试用，提高大家的用户体验。\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/wechat.png\" width=\"30%\"/>\r\n\r\n如果你觉得CacheCloud对你有帮助，欢迎Star⭐。\r\n"
  },
  {
    "path": "README_CN.md",
    "content": "[中文](README_CN.md) | [EN](README_EN.md)\r\n\r\n![cachecloud云平台](cachecloud-web/src/main/resources/static/img/readme/cachecloud-head.png)\r\n\r\n[![CI checks on main badge]][CI checks on main link] [![latest release badge]][latest release link] [![github stars badge]][github stars link] [![github forks badge]][github forks link] [![github open issues badge]][github open issues link] [![github open prs badge]][github open prs link] [![latest commit to main badge]][latest commit to main link]\r\n\r\n[CI checks on main badge]: https://flat.badgen.net/github/checks/sohutv/cachecloud/main?label=CI%20status%20on%20main&cache=900&icon=github\r\n[CI checks on main link]:https://github.com/sohutv/cachecloud/actions?query=branch%3Amain\r\n[github forks badge]: https://flat.badgen.net/github/forks/sohutv/cachecloud?icon=github\r\n[github forks link]: https://useful-forks.github.io/?repo=sohutv%2Fcachecloud\r\n[github open issues badge]: https://flat.badgen.net/github/open-issues/sohutv/cachecloud?icon=github\r\n[github open issues link]: https://github.com/sohutv/cachecloud/issues?q=is%3Aissue+is%3Aopen\r\n[github open prs badge]: https://flat.badgen.net/github/open-prs/sohutv/cachecloud?icon=github\r\n[github open prs link]: https://github.com/sohutv/cachecloud/pulls?q=is%3Apr+is%3Aopen\r\n[github stars badge]: https://flat.badgen.net/github/stars/sohutv/cachecloud?icon=github\r\n[github stars link]: https://github.com/sohutv/cachecloud/stargazers\r\n[latest commit to main badge]: https://flat.badgen.net/github/last-commit/sohutv/cachecloud/main?icon=github&color=yellow&label=last%20dev%20commit&cache=900\r\n[latest commit to main link]: https://github.com/sohutv/cachecloud/commits/main\r\n[latest release badge]: https://flat.badgen.net/github/release/sohutv/cachecloud/development?icon=github\r\n[latest release link]: https://github.com/sohutv/cachecloud/releases\r\n\r\n<div align=\"center\">\r\n  <h2>CacheCloud云平台</h2>\r\n  <a href=\"https://github.com/sohutv/cachecloud/blob/main/cachecloud-web/src/main/resources/static/wiki/quickstart/index.md\">Quickstart</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/blob/main/cachecloud-web/src/main/resources/static/wiki/access/client.md\">Client</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/wiki\">Docs</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/wiki#%E5%85%ADfaq%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98\">FAQ</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"http://43.137.44.6:8080/admin/app/list\">Demo</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/issues/289\">Feedback</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud#contact\">Contact</a>\r\n  <hr />\r\n</div>\r\n\r\n\r\n## CacheCloud是什么？\r\n\r\nCacheCloud是一个Redis云管理平台：支持Redis多种架构(Standalone、Sentinel、Cluster)高效管理、有效降低大规模redis运维成本，提升资源管控能力和利用率。平台提供快速搭建/迁移，运维管理，弹性伸缩，统计监控，客户端整合接入等功能。\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/cachecloud-info.png\" width=\"100%\"/>\r\n\r\n## CacheCloud功能架构\r\n\r\n+ Redis搭建：宿主环境初始化、实例部署安装、类型架构支持；\r\n+ 运维管理：宿主环境、资源管理、应用审计、应用运维、质量监控、诊断分析；\r\n+ 统计监控：日志采集、实例采集、机器采集、应用统计、监控告警、问题诊断；\r\n+ 客户端接入：SDK接入、语言接入、客户端监控；\r\n+ 弹性伸缩：资源收缩、应用伸缩、外部接入；\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/CacheCloud功能架构.png\" width=\"100%\"/>\r\n\r\n<a name=\"cc4\"/>\r\n\r\n##  CacheCloud使用规模\r\n\r\n+ 800亿+ commands/day\r\n+ 18T+ Memory Total\r\n+ 420+ app Total / 4800+ Instances Total\r\n+ 80+ Physical machine/ 360+ K8s Pod Total\r\n\r\n<a name=\"cc5\"/>\r\n\r\n## CacheCloud VS 云厂商 \r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/sentinel-cost.png\" width=\"50%\"/><img src=\"cachecloud-web/src/main/resources/static/img/readme/cluster-cost.png\" width=\"50%\"/>\r\n\r\n<div align=\"center\">Redis 主从/集群部署成本</div>\r\n\r\n## 贡献成员\r\n\r\n<a href=\"https://github.com/sohutv/cachecloud/graphs/contributors\">\r\n  <img src=\"https://contrib.rocks/image?repo=sohutv/cachecloud\" />\r\n</a>\r\n\r\n## 感谢支持者\r\n\r\n[![Stargazers repo roster for @sohutv/cachecloud](https://reporoster.com/stars/sohutv/cachecloud)](https://github.com/sohutv/cachecloud/stargazers)\r\n[![Forkers repo roster for @sohutv/cachecloud](https://reporoster.com/forks/sohutv/cachecloud)](https://github.com/sohutv/cachecloud/network/members)\r\n\r\n<a name=\"contact\"/>\r\n\r\n## 联系我们\r\n\r\n+ QQ群: 534429768(已满) / 2群:894022242 / 3群:908821300\r\n   \r\n+ 微信群:\r\n\r\n<img src=\"http://photocdn.tv.sohu.com/img/cachecloud/weixin.jpg\" width=\"30%\"/>\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/subcribe.png\" width=\"30%\"/>\r\n\r\n+ 微信：如果大家有公网资源可以联系我，会加入到开源版本服务资源部署试用，提高大家的用户体验。\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/wechat.png\" width=\"30%\"/>\r\n\r\n如果你觉得CacheCloud对你有帮助，欢迎Star⭐。\r\n"
  },
  {
    "path": "README_EN.md",
    "content": "[中文](README_CN.md) | [EN](README_EN.md)\r\n\r\n![cachecloud云平台](cachecloud-web/src/main/resources/static/img/readme/cachecloud-head.png)\r\n\r\n[![CI checks on main badge]][CI checks on main link] [![latest release badge]][latest release link] [![github stars badge]][github stars link] [![github forks badge]][github forks link] [![github open issues badge]][github open issues link] [![github open prs badge]][github open prs link] [![latest commit to main badge]][latest commit to main link]\r\n\r\n[CI checks on main badge]: https://flat.badgen.net/github/checks/sohutv/cachecloud/main?label=CI%20status%20on%20main&cache=900&icon=github\r\n[CI checks on main link]:https://github.com/sohutv/cachecloud/actions?query=branch%3Amain\r\n[github forks badge]: https://flat.badgen.net/github/forks/sohutv/cachecloud?icon=github\r\n[github forks link]: https://useful-forks.github.io/?repo=sohutv%2Fcachecloud\r\n[github open issues badge]: https://flat.badgen.net/github/open-issues/sohutv/cachecloud?icon=github\r\n[github open issues link]: https://github.com/sohutv/cachecloud/issues?q=is%3Aissue+is%3Aopen\r\n[github open prs badge]: https://flat.badgen.net/github/open-prs/sohutv/cachecloud?icon=github\r\n[github open prs link]: https://github.com/sohutv/cachecloud/pulls?q=is%3Apr+is%3Aopen\r\n[github stars badge]: https://flat.badgen.net/github/stars/sohutv/cachecloud?icon=github\r\n[github stars link]: https://github.com/sohutv/cachecloud/stargazers\r\n[latest commit to main badge]: https://flat.badgen.net/github/last-commit/sohutv/cachecloud/main?icon=github&color=yellow&label=last%20dev%20commit&cache=900\r\n[latest commit to main link]: https://github.com/sohutv/cachecloud/commits/main\r\n[latest release badge]: https://flat.badgen.net/github/release/sohutv/cachecloud/development?icon=github\r\n[latest release link]: https://github.com/sohutv/cachecloud/releases\r\n\r\n<div align=\"center\">\r\n  <h2>CacheCloud云平台</h2>\r\n  <a href=\"https://github.com/sohutv/cachecloud/blob/main/cachecloud-web/src/main/resources/static/wiki/quickstart/index.md\">Quickstart</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/blob/main/cachecloud-web/src/main/resources/static/wiki/access/client.md\">Client</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/wiki\">Docs</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/wiki#%E5%85%ADfaq%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98\">FAQ</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"http://43.137.44.6:8080/admin/app/list\">Demo</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud/issues/289\">Feedback</a>\r\n  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>\r\n  <a href=\"https://github.com/sohutv/cachecloud#contact\">Contact</a>\r\n  <hr />\r\n</div>\r\n\r\n\r\n## What is CacheCloud？\r\n\r\nCacheCloud is a Redis cloud management platform that supports efficient management of multiple Redis architectures (Standalone, Sentinel, and Cluster), effectively reduces large-scale redis Operation and Maintenance costs, and improves resource management capabilities and utilization. The platform provides functions such as rapid construction/migration, operation and maintenance management, elastic scaling, statistical monitoring, and client integration and access.\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/cachecloud-info.png\" width=\"100%\"/>\r\n\r\n## CacheCloud Function Architecture\r\n\r\n+ Redis build: Machine environment initialization, Instance deployment and installation, Redis architecture support;\r\n+ DevOps: Machine environment, Resource management, Application audit, Application operation and maintenance, Quality monitoring, Diagnostic analysis;\r\n+ Statistical monitoring: Redis log collection, Instance indicator collection, Machine indicator collection, Application statistics, Monitoring alarms, Problem diagnosis;\r\n+ Client access: SDK access support, Language access support, Client monitoring;\r\n+ Elastic scaling: Memory Resource shrinkage, Application scaling, External access;\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/CacheCloud功能架构.png\" width=\"100%\"/>\r\n\r\n<a name=\"cc4\"/>\r\n\r\n## CacheCloud Scale\r\n\r\n+ 800亿+ commands/day\r\n+ 18T+ Memory Total\r\n+ 420+ app Total / 4800+ Instances Total\r\n+ 80+ Physical machine/ 360+ K8s Pod Total\r\n\r\n<a name=\"cc5\"/>\r\n\r\n## CacheCloud VS Cloud vendor\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/sentinel-cost.png\" width=\"50%\"/><img src=\"cachecloud-web/src/main/resources/static/img/readme/cluster-cost.png\" width=\"50%\"/>\r\n\r\n<div align=\"center\">Redis Sentinel/Cluster deploy cost</div>\r\n\r\n## Contributing member\r\n\r\n<a href=\"https://github.com/sohutv/cachecloud/graphs/contributors\">\r\n  <img src=\"https://contrib.rocks/image?repo=sohutv/cachecloud\" />\r\n</a>\r\n\r\n## Supporters\r\n\r\n[![Stargazers repo roster for @sohutv/cachecloud](https://reporoster.com/stars/sohutv/cachecloud)](https://github.com/sohutv/cachecloud/stargazers)\r\n[![Forkers repo roster for @sohutv/cachecloud](https://reporoster.com/forks/sohutv/cachecloud)](https://github.com/sohutv/cachecloud/network/members)\r\n\r\n<a name=\"contact\"/>\r\n\r\n## Contact us\r\n\r\n+ QQ: 534429768(Full) / 2群:894022242 / 3群:908821300\r\n\r\n+ Wechat Group:\r\n\r\n<img src=\"http://photocdn.tv.sohu.com/img/cachecloud/weixin.jpg\" width=\"40%\"/>\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/subcribe.png\" width=\"40%\"/>\r\n\r\n+ Wechat：If you have public network resources can contact me, I will join the open source version service resource deployment trial, improve everyone's user experience.\r\n\r\n<img src=\"cachecloud-web/src/main/resources/static/img/readme/wechat.png\" width=\"30%\"/>\r\n\r\nIf you find CacheCloud is helpful, welcome to Star⭐️.\r\n"
  },
  {
    "path": "cachecloud-custom/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>cachecloud-parent</artifactId>\n        <groupId>com.sohu.tv</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.sohu.tv.custom</groupId>\n    <artifactId>cachecloud-custom</artifactId>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <scope>compile</scope>\n            <exclusions>\n                <exclusion>\n                    <artifactId>log4j-api</artifactId>\n                    <groupId>org.apache.logging.log4j</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-deploy-plugin</artifactId>\n                <configuration>\n                    <skip>true</skip>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/DefaultCustomConfiguration.java",
    "content": "package com.sohu.cache;\n\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.alert.WeChatComponent;\nimport com.sohu.cache.alert.impl.DefaultEmailComponent;\nimport com.sohu.cache.alert.impl.DefaultWeChatComponent;\nimport com.sohu.cache.login.LoginComponent;\nimport com.sohu.cache.login.impl.DefaultLoginComponent;\nimport com.sohu.cache.report.ReportDataComponent;\nimport com.sohu.cache.report.impl.DefaultReportDataComponent;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Created by yijunzhang\n */\n@Configuration\npublic class DefaultCustomConfiguration {\n\n    @Bean(\"emailComponent\")\n    @ConditionalOnMissingBean\n    public EmailComponent emailComponent() {\n        return new DefaultEmailComponent();\n    }\n\n    @Bean(\"weChatComponent\")\n    @ConditionalOnMissingBean\n    public WeChatComponent weChatComponent() {\n        return new DefaultWeChatComponent();\n    }\n\n    @Bean(\"loginComponent\")\n    @ConditionalOnMissingBean\n    public LoginComponent loginComponent() {\n        return new DefaultLoginComponent();\n    }\n\n    @Bean(\"reportDataComponent\")\n    @ConditionalOnMissingBean\n    public ReportDataComponent reportDataComponent() {\n        return new DefaultReportDataComponent();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/alert/EmailComponent.java",
    "content": "package com.sohu.cache.alert;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 邮件服务\r\n * @author leifu\r\n */\r\npublic interface EmailComponent {\r\n\t/**\r\n     * 发送邮件\r\n     * @param title\r\n     * @param content\r\n     * @param emailList\r\n     * @param ccList(抄送)\r\n     * @return\r\n     */\r\n    boolean sendMail(String title, String content, List<String> emailList, List<String> ccList);\r\n\r\n    boolean sendDailyMail(String title, String content, List<String> emailList, List<String> ccList);\r\n\r\n    /**\r\n     * 发送邮件\r\n     * @param title\r\n     * @param content\r\n     * @param emailList\r\n     * @return\r\n     */\r\n    boolean sendMail(String title, String content, List<String> emailList);\r\n    \r\n    /**\r\n     * 发送管理员邮件\r\n     * @param title\r\n     * @param content\r\n     * @return\r\n     */\r\n    boolean sendMailToAdmin(String title, String content);\r\n\r\n\r\n    /**\r\n     * 获取管理员邮件组\r\n     * @return\r\n     */\r\n    String getAdminEmail();\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/alert/WeChatComponent.java",
    "content": "package com.sohu.cache.alert;\n\nimport java.util.List;\n\n/**\n * 微信报警\n * Created by rucao\n */\npublic interface WeChatComponent {\n\n    /**\n     * 发送微信报警\n     * @param message\n     * @param weChatList\n     * @return\n     */\n    boolean sendWeChat(String title, String message, List<String> weChatList);\n\n    /**\n     * 发送微信报警给所有相关人员\n     * @param title\n     * @param message\n     * @param weChatList\n     * @return\n     */\n    boolean sendWeChatToAll(String title, String message, List<String> weChatList);\n\n    /**\n     * 发送微信报警给管理员\n     * @param message\n     * @return\n     */\n    boolean sendWeChatToAdmin(String title, String message);\n\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/alert/impl/DefaultEmailComponent.java",
    "content": "package com.sohu.cache.alert.impl;\n\n\nimport com.sohu.cache.alert.EmailComponent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\n\n/**\n * 邮件报警组件默认空实现\n * Created by yijunzhang\n */\npublic class DefaultEmailComponent implements EmailComponent {\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Override\n    public boolean sendMail(String title, String content, List<String> emailList, List<String> ccList) {\n        logger.warn(\"Please implement the sendMail logic.\");\n        return true;\n    }\n\n\t@Override\n    public boolean sendDailyMail(String title, String content, List<String> emailList, List<String> ccList) {\n        logger.warn(\"Please implement the sendDailyMail logic.\");\n        return true;\n    }\n\n    @Override\n    public boolean sendMail(String title, String content, List<String> emailList) {\n        logger.warn(\"Please implement the sendMail logic.\");\n        return true;\n    }\n\n    @Override\n    public boolean sendMailToAdmin(String title, String content) {\n        logger.warn(\"Please implement the sendMailToAdmin logic.\");\n        return true;\n    }\n\n    @Override\n    public String getAdminEmail() {\n        logger.warn(\"Please implement the getAdminEmail logic.\");\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/alert/impl/DefaultWeChatComponent.java",
    "content": "package com.sohu.cache.alert.impl;\n\nimport com.sohu.cache.alert.WeChatComponent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\n\n/**\n * Created by yijunzhang\n */\npublic class DefaultWeChatComponent implements WeChatComponent {\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Override\n    public boolean sendWeChat(String title, String message, List<String> weChatList) {\n        logger.warn(\"Please implement the sendWeChat logic.\");\n        return true;\n    }\n\n    @Override\n    public boolean sendWeChatToAll(String title, String message, List<String> weChatList) {\n        logger.warn(\"Please implement the sendWeChatToAll logic.\");\n        return true;\n    }\n\n    @Override\n    public boolean sendWeChatToAdmin(String title, String message) {\n        logger.warn(\"Please implement the sendWeChatToAdmin logic.\");\n        return true;\n    }\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/alert/utils/AlertUtils.java",
    "content": "package com.sohu.cache.alert.utils;\n\n/**\n * Created by yijunzhang\n */\npublic class AlertUtils {\n    /**\n     * 邮箱报警接口\n     */\n    public static String EMAIL_ALERT_INTERFACE;\n    /**\n     * 短信报警接口\n     */\n    public static String MOBILE_ALERT_INTERFACE;\n    /**\n     * 微信报警接口\n     */\n    public static String WECHAT_ALERT_INTERFACE;\n    /**\n     * 报警邮箱\n     */\n    public static String EMAILS;\n    /**\n     * 报警电话\n     */\n    public static String PHONES;\n    /**\n     * 报警微信\n     */\n    public static String WECHAT;\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/configuration/DefaultRestTemplateConfig.java",
    "content": "package com.sohu.cache.configuration;\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.client.HttpComponentsClientHttpRequestFactory;\nimport org.springframework.web.client.RestTemplate;\n\n\n/**\n * Created by rucao on 2020/4/2\n */\n@Configuration\npublic class DefaultRestTemplateConfig {\n    private static int connectTimeout = 4000;\n    private static int readTimeout = 5000;\n\n    @ConditionalOnMissingBean(name = \"restTemplate\")\n    @Bean\n    RestTemplate restTemplate() {\n        HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory();\n        f.setConnectTimeout(connectTimeout);\n        f.setReadTimeout(readTimeout);\n        f.setConnectionRequestTimeout(connectTimeout);\n        RestTemplate restTemplate = new RestTemplate(f);\n        return restTemplate;\n    }\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/login/LoginComponent.java",
    "content": "package com.sohu.cache.login;\n\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * Created by yijunzhang\n */\npublic interface LoginComponent {\n\n    /**\n     * 检测登录状态\n     *\n     * @param userName\n     * @param password\n     * @return\n     */\n    boolean passportCheck(String userName, String password);\n\n    /**\n     * 根据ticket获取email\n     * @param ticket\n     * @return\n     */\n    String getEmail(String ticket);\n\n    /**\n     * 获取登录跳转地址\n     *\n     * @param request\n     * @return\n     * @throws Exception\n     */\n    String getRedirectUrl(HttpServletRequest request);\n\n    /**\n     * SSO logout url\n     *\n     * @return\n     * @throws Exception\n     */\n    String getLogoutUrl();\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/login/impl/DefaultLoginComponent.java",
    "content": "package com.sohu.cache.login.impl;\n\nimport com.sohu.cache.login.LoginComponent;\nimport com.sohu.cache.utils.EnvCustomUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.client.RestTemplate;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.io.UnsupportedEncodingException;\nimport java.net.InetAddress;\nimport java.net.URLEncoder;\nimport java.net.UnknownHostException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Created by yijunzhang\n */\npublic class DefaultLoginComponent implements LoginComponent {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @Value(value = \"${server.port:8080}\")\n    private String serverPort;\n\n    private static final String RELATIVE_URL = \"/manage/loginCheck\";\n\n    /**\n     * it is open for change\n     * @param userName\n     * @param password\n     * @return\n     */\n    @Override\n    public boolean passportCheck(String userName, String password) {\n        //default password login check\n        if(EnvCustomUtil.pwdswitch){\n            String url = getUrl() + RELATIVE_URL;\n            Map<String, Object> requestMap = new HashMap<>();\n            requestMap.put(\"name\", userName);\n            requestMap.put(\"password\", password);\n            Map<String, Object> map = restTemplate.postForObject(url, requestMap, Map.class);\n            if(map != null && map.get(\"status\") != null && Integer.parseInt(map.get(\"status\").toString()) == 200){\n                return true;\n            }\n            return false;\n        }\n        //todo need to implement by your own business\n        return true;\n    }\n\n    private String getUrl() {\n        InetAddress address = null;\n        try {\n            address = InetAddress.getLocalHost();\n        } catch (UnknownHostException e) {\n            logger.error(e.getMessage(), e);\n        }\n        if(address != null){\n            return \"http://\" + address.getHostAddress() + \":\" + this.serverPort;\n        }\n        return \"http://127.0.0.1:\" + this.serverPort;\n    }\n\n    @Override\n    public String getEmail(String ticket) {\n        return null;\n    }\n\n    @Override\n    public String getRedirectUrl(HttpServletRequest request) {\n        StringBuffer redirectUrl = new StringBuffer();\n        redirectUrl.append(request.getSession(true).getServletContext().getContextPath());\n        redirectUrl.append(\"/manage/login?\");\n        // 跳转地址\n        redirectUrl.append(\"redirectUrl\");\n        redirectUrl.append(\"=\");\n        redirectUrl.append(request.getRequestURI());\n        // 跳转参数\n        String query = request.getQueryString();\n        if (StringUtils.isNotBlank(query)) {\n            redirectUrl.append(\"?\");\n            try {\n                redirectUrl.append(URLEncoder.encode(request.getQueryString(), \"UTF-8\"));\n            } catch (UnsupportedEncodingException e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        return redirectUrl.toString();\n    }\n\n    @Override\n    public String getLogoutUrl() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/report/ReportDataComponent.java",
    "content": "package com.sohu.cache.report;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2022/2/21 11:16\n * @Description: 上报数据服务\n */\npublic interface ReportDataComponent {\n\n    //上报命令数据\n    void reportCommandData(Object msg);\n\n    //上报异常数据\n    void reportExceptionData(Object msg);\n\n    //上报redis info信息\n    void reportRedisInfoData(Object msg);\n\n    //上报慢查询数据\n    void reportSlowLogData(Object msg);\n\n    //上报延迟事件数据\n    void reportLatencyData(Object msg);\n\n    //上报机器监控数据\n    void reportMachineData(Object msg);\n\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/report/impl/DefaultReportDataComponent.java",
    "content": "package com.sohu.cache.report.impl;\n\nimport com.sohu.cache.report.ReportDataComponent;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2022/2/21 11:22\n * @Description: 上报数据默认实现\n */\npublic class DefaultReportDataComponent implements ReportDataComponent {\n\n    @Override\n    public void reportCommandData(Object msg) {\n        //todo\n    }\n\n    @Override\n    public void reportExceptionData(Object msg) {\n        //todo\n    }\n\n    @Override\n    public void reportRedisInfoData(Object msg) {\n        //todo\n    }\n\n    @Override\n    public void reportSlowLogData(Object msg) {\n        //todo\n    }\n\n    @Override\n    public void reportLatencyData(Object msg) {\n        //todo\n    }\n\n    @Override\n    public void reportMachineData(Object msg) {\n        //todo\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-custom/src/main/java/com/sohu/cache/utils/EnvCustomUtil.java",
    "content": "package com.sohu.cache.utils;\n\n/**\n * custom environment setting\n */\npublic class EnvCustomUtil {\n    //是否启用默认密码\n    public static boolean pwdswitch = false;\n}\n"
  },
  {
    "path": "cachecloud-web/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>com.sohu.tv</groupId>\n        <artifactId>cachecloud-parent</artifactId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <name>cachecloud-web</name>\n    <groupId>com.sohu.tv</groupId>\n    <artifactId>cachecloud-web</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>war</packaging>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-logging</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>log4j-api</artifactId>\n                    <groupId>org.apache.logging.log4j</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-tomcat</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-freemarker</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.tomcat.embed</groupId>\n            <artifactId>tomcat-embed-jasper</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context-support</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>javax.servlet</groupId>\n            <artifactId>jstl</artifactId>\n            <version>1.2</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.quartz-scheduler</groupId>\n            <artifactId>quartz</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>slf4j-api</artifactId>\n                    <groupId>org.slf4j</groupId>\n                </exclusion>\n                <exclusion>\n                    <artifactId>HikariCP-java7</artifactId>\n                    <groupId>com.zaxxer</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>ch.qos.logback</groupId>\n            <artifactId>logback-classic</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.jolokia</groupId>\n            <artifactId>jolokia-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n            <version>${jedis.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.dyuproject.protostuff</groupId>\n            <artifactId>protostuff-runtime</artifactId>\n            <version>${protostuff.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.dyuproject.protostuff</groupId>\n            <artifactId>protostuff-core</artifactId>\n            <version>${protostuff.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.30</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- ssh -->\n        <dependency>\n            <groupId>org.apache.sshd</groupId>\n            <artifactId>sshd-core</artifactId>\n            <version>${ssh-version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.sshd</groupId>\n            <artifactId>sshd-scp</artifactId>\n            <version>${ssh-version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-collections4</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.zaxxer</groupId>\n            <artifactId>HikariCP</artifactId>\n            <version>3.3.1</version>\n        </dependency>\n\n        <dependency>\n            <groupId>net.sf.json-lib</groupId>\n            <artifactId>json-lib</artifactId>\n            <classifier>jdk15</classifier>\n        </dependency>\n        <dependency>\n            <groupId>commons-configuration</groupId>\n            <artifactId>commons-configuration</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.struts</groupId>\n            <artifactId>struts-taglib</artifactId>\n            <version>1.3.10</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.netflix.hystrix</groupId>\n            <artifactId>hystrix-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.codehaus.janino</groupId>\n            <artifactId>janino</artifactId>\n            <version>${janino.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>net.logstash.logback</groupId>\n            <artifactId>logstash-logback-encoder</artifactId>\n            <version>${logstash-logback-encoder.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.vladsch.flexmark</groupId>\n            <artifactId>flexmark</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.vladsch.flexmark</groupId>\n            <artifactId>flexmark-ext-tables</artifactId>\n        </dependency>\n        <!-- open dependency -->\n        <dependency>\n            <groupId>com.sohu.tv.custom</groupId>\n            <artifactId>cachecloud-custom</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger-ui</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.ant</groupId>\n            <artifactId>ant</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <finalName>cachecloud-web</finalName>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-deploy-plugin</artifactId>\n                <configuration>\n                    <!-- 此模块忽略deploy -->\n                    <skip>true</skip>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n            </plugin>\n\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <configuration>\n                    <mainClass>com.sohu.cache.ApplicationStarter</mainClass>\n                    <addResources>false</addResources>\n                    <executable>true</executable>\n                    <fork>false</fork>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                        <id>1</id>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-resources-plugin</artifactId>\n                <configuration>\n                    <encoding>UTF-8</encoding>\n                    <useDefaultDelimiters>true</useDefaultDelimiters>\n                    <nonFilteredFileExtensions>\n                        <nonFilteredFileExtension>ttf</nonFilteredFileExtension>\n                        <nonFilteredFileExtension>woff</nonFilteredFileExtension>\n                        <nonFilteredFileExtension>woff2</nonFilteredFileExtension>\n                    </nonFilteredFileExtensions>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <configuration>\n                    <source>1.8</source>\n                    <target>1.8</target>\n                </configuration>\n            </plugin>\n        </plugins>\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <filtering>true</filtering>\n            </resource>\n            <resource>\n                <directory>${project.basedir}/bin</directory>\n                <targetPath>${project.build.directory}</targetPath>\n            </resource>\n        </resources>\n    </build>\n</project>"
  },
  {
    "path": "cachecloud-web/sql/2.0.sql",
    "content": "-- MySQL dump 10.15  Distrib 10.0.16-MariaDB, for Linux (x86_64)\n--\n-- Host: localhost    Database: cachecloud_open\n-- ------------------------------------------------------\n-- Server version\t10.0.16-MariaDB-log\n\nSET NAMES utf8;\nSET FOREIGN_KEY_CHECKS = 0;\n\n--\n-- Table structure for table `app_audit`\n--\n\nDROP TABLE IF EXISTS `app_audit`;\nCREATE TABLE `app_audit` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人的id',\n  `user_name` varchar(64) NOT NULL COMMENT '用户名',\n  `type` tinyint(4) NOT NULL COMMENT '申请类型:0:申请应用,1:应用扩容,2:修改配置',\n  `param1` varchar(600) DEFAULT NULL COMMENT '预留参数1',\n  `param2` varchar(600) DEFAULT NULL COMMENT '预留参数2',\n  `param3` varchar(600) DEFAULT NULL COMMENT '预留参数3',\n  `info` varchar(360) NOT NULL COMMENT '申请描述',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:等待审批; 1:审批通过; -1:驳回',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `refuse_reason` varchar(360) DEFAULT NULL COMMENT '驳回理由',\n  `task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '任务id',\n  `operate_id` bigint(20) DEFAULT NULL COMMENT '工单处理人',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid` (`app_id`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_status_create_time` (`status`,`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用审核表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_audit_log`\n--\n\nDROP TABLE IF EXISTS `app_audit_log`;\nCREATE TABLE `app_audit_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '审批操作人id',\n  `info` longtext NOT NULL COMMENT 'app审批的详细信息',\n  `type` tinyint(4) NOT NULL,\n  `create_time` datetime NOT NULL,\n  `app_audit_id` bigint(20) NOT NULL COMMENT '审批id',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit_appid` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app审核日志表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_client_command_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_command_minute_statistics`;\nCREATE TABLE `app_client_command_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `command` varchar(20) NOT NULL COMMENT '命令明文',\n  `cost` bigint(20) DEFAULT NULL COMMENT '命令累计毫秒耗时',\n  `bytes_in` bigint(20) DEFAULT NULL COMMENT '输入流量',\n  `bytes_out` bigint(20) DEFAULT NULL COMMENT '输出流量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `count` int(11) DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__appid_client_command_currentMin` (`app_id`,`client_ip`,`command`,`current_min`),\n  KEY `idx_currentmin_appid_count_cost` (`current_min`,`app_id`,`count`,`cost`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟命令调用上报数据';\n\n--\n-- Table structure for table `app_client_exception_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_exception_minute_statistics`;\nCREATE TABLE `app_client_exception_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `type` tinyint(4) NOT NULL COMMENT '0:connect exception;1:command exception',\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `node` varchar(30) NOT NULL COMMENT '节点信息host:port',\n  `count` bigint(20) DEFAULT NULL COMMENT '累计连接失败次数',\n  `cost` bigint(20) DEFAULT NULL COMMENT '累计连接失败毫秒耗时',\n  `latency_commands` varchar(255) DEFAULT NULL COMMENT '统计命令topN id,逗号分隔',\n  `redis_pool_config` varchar(255) DEFAULT NULL COMMENT 'redis连接池配置信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__client_node_type_currentMin` (`client_ip`,`node`,`type`,`current_min`),\n  KEY `idx_appid_current_min` (`app_id`,`current_min`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟异常上报数据';\n\n--\n-- Table structure for table `app_client_latency_command`\n--\n\nDROP TABLE IF EXISTS `app_client_latency_command`;\nCREATE TABLE `app_client_latency_command` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `command` varchar(255) NOT NULL COMMENT '命令明文',\n  `size` bigint(20) DEFAULT NULL COMMENT '参数长度',\n  `args` varchar(255) DEFAULT NULL COMMENT '裁剪后参数明文',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `invoke_time` bigint(20) DEFAULT NULL COMMENT '命令调用时间戳',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端异常命令调用详情';\n\n--\n-- Table structure for table `app_client_statistic_gather`\n--\n\nDROP TABLE IF EXISTS `app_client_statistic_gather`;\nCREATE TABLE `app_client_statistic_gather` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gather_time` varchar(20) NOT NULL COMMENT '统计时间，格式yyyy-mm-dd',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `cmd_count` bigint(20) DEFAULT '0' COMMENT '命令调用次数',\n  `conn_exp_count` bigint(20) DEFAULT '0' COMMENT '连接异常次数',\n  `avg_cmd_cost` double DEFAULT '0' COMMENT '命令调用平均耗时，单位毫秒',\n  `avg_cmd_exp_cost` double DEFAULT '0' COMMENT '命令超时平均耗时，单位毫秒',\n  `avg_conn_exp_cost` double DEFAULT '0' COMMENT '连接异常平均耗时，单位毫秒',\n  `cmd_exp_count` bigint(20) DEFAULT '0' COMMENT '命令超时次数',\n  `instance_count` int(11) DEFAULT NULL COMMENT '应用实例数',\n  `avg_mem_frag_ratio` double DEFAULT NULL COMMENT '平均碎片率',\n  `mem_used_ratio` double DEFAULT NULL COMMENT '内存使用率',\n  `exception_count` bigint(20) DEFAULT '0' COMMENT '异常数（旧，待下线）',\n  `slow_log_count` bigint(20) DEFAULT '0' COMMENT '慢查询次数',\n  `latency_count` bigint(20) DEFAULT '0' COMMENT '延迟事件次数',\n  `object_size` bigint(20) DEFAULT '0' COMMENT '存储对象数',\n  `used_memory` bigint(20) DEFAULT '0' COMMENT '内存占用 byte',\n  `used_memory_rss` bigint(20) DEFAULT '0' COMMENT '物理内存占用 byte',\n  `max_cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗(单位:秒)',\n  `max_cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗(单位:秒)',\n  `connected_clients` bigint(20) DEFAULT '0' COMMENT '应用客户端连接数',\n  `topology_exam_result` tinyint(4) DEFAULT NULL COMMENT '拓扑诊断结果，0：正常，1：异常',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_appid_gathertime` (`app_id`,`gather_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报数据全天统计';\n\n--\n-- Table structure for table `app_client_value_minute_stat`\n--\n\nDROP TABLE IF EXISTS `app_client_value_minute_stat`;\nCREATE TABLE `app_client_value_minute_stat` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用appid',\n  `collect_time` bigint(20) NOT NULL COMMENT '数据收集时间yyyyMMddHHmm00',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `command` varchar(20) NOT NULL COMMENT '执行命令',\n  `distribute_type` tinyint(4) NOT NULL COMMENT '值分布',\n  `count` int(11) NOT NULL COMMENT '命令执行次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_collect_command_dis` (`app_id`,`collect_time`,`command`,`distribute_type`),\n  KEY `idx_collect_time` (`collect_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟值分布上报数据统计';\n\n--\n-- Table structure for table `app_client_version_statistic`\n--\n\nDROP TABLE IF EXISTS `app_client_version_statistic`;\nCREATE TABLE `app_client_version_statistic` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip地址',\n  `client_version` varchar(20) NOT NULL COMMENT '客户端版本',\n  `report_time` datetime DEFAULT NULL COMMENT '上报时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_client_ip` (`app_id`,`client_ip`),\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报版本信息统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_daily`\n--\n\nDROP TABLE IF EXISTS `app_daily`;\nCREATE TABLE `app_daily` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `date` date NOT NULL COMMENT '日期',\n  `create_time` datetime NOT NULL,\n  `slow_log_count` bigint(20) NOT NULL COMMENT '慢查询个数',\n  `client_exception_count` bigint(20) NOT NULL COMMENT '客户端异常个数',\n  `max_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟最大客户端连接数',\n  `avg_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟平均客户端连接数',\n  `max_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟最大命令数',\n  `avg_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟平均命令数',\n  `avg_hit_ratio` double NOT NULL COMMENT '平均命中率',\n  `min_minute_hit_ratio` double NOT NULL COMMENT '每分钟最小命中率',\n  `max_minute_hit_ratio` double NOT NULL COMMENT '每分钟最大命中率',\n  `avg_used_memory` bigint(20) NOT NULL COMMENT '最大内存使用量',\n  `max_used_memory` bigint(20) NOT NULL COMMENT '平均内存使用量',\n  `expired_keys_count` bigint(20) NOT NULL COMMENT '过期键个数',\n  `evicted_keys_count` bigint(20) NOT NULL COMMENT '剔除键个数',\n  `avg_minute_net_input_byte` double NOT NULL COMMENT '每分钟平均网络input量',\n  `max_minute_net_input_byte` double NOT NULL COMMENT '每分钟最大网络input量',\n  `avg_minute_net_output_byte` double NOT NULL COMMENT '每分钟平均网络output量',\n  `max_minute_net_output_byte` double NOT NULL COMMENT '每分钟最大网络output量',\n  `avg_object_size` bigint(20) NOT NULL COMMENT '键个数平均值',\n  `max_object_size` bigint(20) NOT NULL COMMENT '键个数最大值',\n  `big_key_times` bigint(20) NOT NULL COMMENT 'bigkey次数',\n  `big_key_info` varchar(512) COLLATE utf8_bin NOT NULL COMMENT 'bigkey详情',\n  `client_cmd_count` bigint(20) NOT NULL COMMENT '累计命令调用次数',\n  `client_avg_cmd_cost` double NOT NULL COMMENT '平均命令调用耗时',\n  `client_conn_exp_count` bigint(20) NOT NULL COMMENT '累计连接异常事件次数',\n  `client_avg_conn_exp_cost` double NOT NULL COMMENT '平均连接异常事件耗时',\n  `client_cmd_exp_count` bigint(20) NOT NULL COMMENT '累计命令超时事件次数',\n  `client_avg_cmd_exp_cost` double NOT NULL COMMENT '平均命令超时事件耗时',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid_date` (`app_id`,`date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='app日报';\n\n--\n-- Table structure for table `app_data_migrate_status`\n--\n\nDROP TABLE IF EXISTS `app_data_migrate_status`;\nCREATE TABLE `app_data_migrate_status` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `migrate_machine_ip` varchar(255) NOT NULL COMMENT '迁移工具所在机器ip',\n  `migrate_machine_port` int(11) NOT NULL COMMENT '迁移工具所占port',\n  `source_migrate_type` tinyint(4) NOT NULL COMMENT '源迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `source_servers` varchar(2048) NOT NULL COMMENT '源实例列表',\n  `target_migrate_type` tinyint(4) NOT NULL COMMENT '目标迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `target_servers` varchar(2048) NOT NULL COMMENT '目标实例列表',\n  `source_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '源应用id',\n  `target_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '目标应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '操作人',\n  `status` tinyint(4) NOT NULL COMMENT '迁移执行状态,0:开始,1:结束,2:异常',\n  `start_time` datetime NOT NULL COMMENT '迁移开始执行时间',\n  `end_time` datetime DEFAULT NULL COMMENT '迁移结束执行时间',\n  `log_path` varchar(255) NOT NULL COMMENT '日志文件路径',\n  `config_path` varchar(255) NOT NULL COMMENT '配置文件路径',\n  `migrate_id` varchar(50) DEFAULT NULL COMMENT 'migrate id',\n  `migrate_tool` tinyint(4) DEFAULT NULL COMMENT 'migrate_tool, 0:redis-shake,1:redis-migrate-tool',\n  `redis_source_version` varchar(20) DEFAULT NULL,\n  `redis_target_version` varchar(20) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用迁移记录详情';\n\n--\n-- Table structure for table `app_desc`\n--\n\nDROP TABLE IF EXISTS `app_desc`;\nCREATE TABLE `app_desc` (\n  `app_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '应用id',\n  `name` varchar(36) NOT NULL COMMENT '应用名',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人id',\n  `status` tinyint(4) NOT NULL COMMENT '应用状态, 0未分配，1申请未审批，2审批并发布 3:应用下线',\n  `intro` varchar(255) NOT NULL COMMENT '应用描述',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `passed_time` datetime NOT NULL COMMENT '审批通过时间',\n  `type` int(10) NOT NULL DEFAULT '0' COMMENT 'cache类型，1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud ,5. redis-sentinel ,6.redis-standalone ',\n  `officer` varchar(32) NOT NULL COMMENT '负责人，中文',\n  `ver_id` int(11) NOT NULL COMMENT '版本',\n  `is_test` tinyint(4) DEFAULT '0' COMMENT '是否测试：1是0否',\n  `need_persistence` tinyint(4) DEFAULT '1' COMMENT '是否需要持久化: 1是0否',\n  `need_hot_back_up` tinyint(4) DEFAULT '1' COMMENT '是否需要热备: 1是0否',\n  `has_back_store` tinyint(4) DEFAULT '1' COMMENT '是否有后端数据源: 1是0否',\n  `forecase_qps` int(11) DEFAULT NULL COMMENT '预估qps',\n  `forecast_obj_num` int(11) DEFAULT NULL COMMENT '预估条目数',\n  `mem_alert_value` int(11) DEFAULT NULL COMMENT '内存报警阀值',\n  `client_machine_room` varchar(36) DEFAULT NULL COMMENT '客户端机房信息',\n  `client_conn_alert_value` int(11) DEFAULT '2000' COMMENT '客户端连接报警阀值',\n  `app_key` varchar(255) DEFAULT NULL COMMENT '应用秘钥',\n  `important_level` tinyint(4) NOT NULL DEFAULT '2' COMMENT '应用级别，1:最重要，2:一般重要，3:一般',\n  `password` varchar(255) DEFAULT '' COMMENT 'redis密码',\n  `hit_precent_alert_value` int(11) DEFAULT '0' COMMENT '命中率报警阀值 0:不报警 ',\n  `is_access_monitor` int(11) DEFAULT '0' COMMENT '是否接入全局监控报警 默认0,0:不接入监控 1:接入监控',\n  `app_fsync_value` int(11) DEFAULT '1' COMMENT '应用刷盘策略 1:主从节点appdendfsync=everysec 2:主从节点 appdendfsync=no',\n  `version_id` int(11) NOT NULL DEFAULT '1' COMMENT 'Redis版本表主键id',\n  PRIMARY KEY (`app_id`),\n  UNIQUE KEY `uidx_app_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app应用描述' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_command_statistics`;\nCREATE TABLE `app_hour_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHH',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`command_name`,`collect_time`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每小时命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_statistics`;\nCREATE TABLE `app_hour_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHH',\n  `hits` bigint(20) NOT NULL COMMENT '每小时命中数量和',\n  `misses` bigint(20) NOT NULL COMMENT '每小时未命中数量和',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '每小时内存占用最大值',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '每小时过期key数量和',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '每小时驱逐key数量和',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '每小时客户端连接数最大值',\n  `object_size` bigint(20) NOT NULL COMMENT '每小时存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '每小时参与累加实例数最小值',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '每小时修改时间最大值',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用统计数据每小时统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_command_statistics`;\nCREATE TABLE `app_minute_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHHmm',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`,`command_name`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每分钟命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_statistics`;\nCREATE TABLE `app_minute_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `hits` bigint(20) NOT NULL COMMENT '命中数量',\n  `misses` bigint(20) NOT NULL COMMENT '未命中数量',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '内存占用',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '过期key数量',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '驱逐key数量',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '客户端连接数',\n  `object_size` bigint(20) NOT NULL COMMENT '每分钟存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '参与累加实例数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_to_user`\n--\n\nDROP TABLE IF EXISTS `app_to_user`;\nCREATE TABLE `app_to_user` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_user`\n--\n\nDROP TABLE IF EXISTS `app_user`;\nCREATE TABLE `app_user` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL COMMENT '用户名',\n  `ch_name` varchar(255) NOT NULL COMMENT '中文名',\n  `email` varchar(64) NOT NULL COMMENT '邮箱',\n  `mobile` varchar(16) NOT NULL COMMENT '手机',\n  `type` int(4) NOT NULL DEFAULT '2' COMMENT '0管理员，1预留，2普通用户，-1无效',\n  `weChat` varchar(32) DEFAULT NULL COMMENT '微信号',\n  `isAlert` tinyint(4) NOT NULL DEFAULT '1' COMMENT '用户是否接收报警 0:不接收 1:接收',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_user_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表' /* `compression`='tokudb_zlib' */;\n\n-- ----------------------------\n--  Records of `app_user`\n-- ----------------------------\nBEGIN;\nINSERT INTO `app_user` VALUES ('1', 'admin', 'admin', 'admin@xxx.com', '13500000000', '0', null, '1');\nCOMMIT;\n\n--\n-- Table structure for table `brevity_schedule_resources`\n--\n\nDROP TABLE IF EXISTS `brevity_schedule_resources`;\nCREATE TABLE `brevity_schedule_resources` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `type` tinyint(4) NOT NULL COMMENT '类型,见:BrevityScheduleType',\n  `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '时间版本',\n  `host` varchar(16) NOT NULL COMMENT '资源ip',\n  `port` int(11) NOT NULL DEFAULT '0' COMMENT '端口',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_type_host_port` (`type`,`host`,`port`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='短频任务表';\n\n--\n-- Table structure for table `diagnostic_task_record`\n--\n\nDROP TABLE IF EXISTS `diagnostic_task_record`;\nCREATE TABLE `diagnostic_task_record` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `type` int(11) DEFAULT NULL COMMENT '诊断类型：0scan 1bigkey 2idle key 3hotkey 4del key 5slot analysis 6topology exam',\n  `task_id` bigint(20) DEFAULT NULL COMMENT '任务流id',\n  `audit_id` bigint(20) DEFAULT NULL COMMENT '审批id',\n  `status` int(11) DEFAULT NULL COMMENT '诊断状态：0开始 1结束 2异常',\n  `cost` bigint(20) DEFAULT NULL COMMENT '耗时，毫秒',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `redis_key` varchar(100) DEFAULT NULL COMMENT '结果的key',\n  `node` varchar(100) DEFAULT NULL COMMENT '实例，host:port',\n  `parent_task_id` bigint(20) DEFAULT NULL COMMENT '父任务id',\n  `diagnostic_condition` varchar(100) DEFAULT NULL COMMENT '诊断条件',\n  `param1` varchar(100) DEFAULT NULL COMMENT '备用参数1',\n  `param2` varchar(100) DEFAULT NULL COMMENT '备用参数2',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用诊断记录';\n\n--\n-- Table structure for table `instance_alert_configs`\n--\n\nDROP TABLE IF EXISTS `instance_alert_configs`;\nCREATE TABLE `instance_alert_configs` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `alert_config` varchar(255) NOT NULL COMMENT '报警配置',\n  `alert_value` varchar(512) NOT NULL COMMENT '报警阀值',\n  `config_info` varchar(255) NOT NULL COMMENT '配置说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:全局报警,2:实例报警',\n  `instance_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '0:全局配置，其他代表实例id',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `compare_type` tinyint(4) NOT NULL COMMENT '比较类型：1小于,2等于,3大于,4不等于',\n  `check_cycle` tinyint(4) NOT NULL COMMENT '1:一分钟,2:五分钟,3:半小时4:一个小时,5:一天',\n  `update_time` datetime NOT NULL COMMENT '报警配置更新时间',\n  `last_check_time` datetime NOT NULL COMMENT '上次检查时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`type`,`instance_id`,`alert_config`,`compare_type`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例报警阀值配置';\n\n-- ----------------------------\n--  Records of `instance_alert_configs`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_alert_configs` VALUES ('9', 'aof_current_size', '6000', 'aof当前尺寸(单位：MB)', '1', '0', '1', '3', '3', '2017-06-19 09:43:22', '2020-09-17 10:52:00'), ('10', 'aof_delayed_fsync', '3', '分钟aof阻塞个数', '1', '0', '1', '3', '1', '2017-06-19 10:38:19', '2020-09-17 11:09:00'), ('11', 'client_biggest_input_buf', '10', '输入缓冲区最大buffer大小(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 10:47:03', '2020-09-17 11:09:00'), ('12', 'client_longest_output_list', '50000', '输出缓冲区最大队列长度', '1', '0', '1', '3', '1', '2017-06-19 10:55:45', '2020-09-17 11:09:00'), ('13', 'instantaneous_ops_per_sec', '60000', '实时ops', '1', '0', '1', '3', '1', '2017-06-19 11:02:38', '2020-09-17 11:09:00'), ('14', 'latest_fork_usec', '400000', '上次fork所用时间(单位：微秒)', '1', '0', '1', '3', '5', '2017-06-19 11:21:35', '2020-09-16 16:51:00'), ('15', 'mem_fragmentation_ratio', '1.5', '内存碎片率(检测大于500MB)', '1', '0', '1', '3', '5', '2017-06-19 12:49:16', '2020-09-16 16:51:00'), ('16', 'rdb_last_bgsave_status', 'ok', '上一次bgsave状态', '1', '0', '1', '4', '4', '2017-06-19 14:15:21', '2020-09-17 10:19:00'), ('17', 'total_net_output_bytes', '5000', '分钟网络输出流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:39:44', '2020-09-17 11:09:00'), ('19', 'total_net_input_bytes', '1200', '分钟网络输入流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:45:44', '2020-09-17 11:09:00'), ('20', 'sync_partial_err', '0', '分钟部分复制失败次数', '1', '0', '1', '3', '1', '2017-06-19 18:34:41', '2020-09-17 11:09:00'), ('21', 'sync_partial_ok', '0', '分钟部分复制成功次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:01', '2020-09-17 11:09:00'), ('22', 'sync_full', '0', '分钟全量复制执行次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:17', '2020-09-17 11:09:00'), ('23', 'rejected_connections', '0', '分钟拒绝连接数', '1', '0', '1', '3', '1', '2017-06-19 18:35:36', '2020-09-17 11:09:00'), ('54', 'master_slave_offset_diff', '20000000', '主从节点偏移量差(单位：字节)', '1', '0', '1', '3', '2', '2017-06-20 18:58:56', '2020-09-17 11:06:00'), ('56', 'cluster_state', 'ok', '集群状态', '1', '0', '1', '4', '1', '2017-06-21 18:01:52', '2020-09-17 11:09:00'), ('57', 'cluster_slots_ok', '16384', '集群成功分配槽个数', '1', '0', '1', '4', '1', '2017-06-21 18:02:04', '2020-09-17 11:09:00');\nCOMMIT;\n\n--\n-- Table structure for table `instance_big_key`\n--\n\nDROP TABLE IF EXISTS `instance_big_key`;\nCREATE TABLE `instance_big_key` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `audit_id` bigint(20) NOT NULL COMMENT 'audit id',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从，详见InstanceRoleEnum',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `big_key` varchar(255) NOT NULL COMMENT '键',\n  `type` varchar(16) NOT NULL COMMENT '类型:string,hash,list,set,zset',\n  `length` int(11) NOT NULL COMMENT '长度',\n  `create_time` datetime NOT NULL COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_audit` (`app_id`,`audit_id`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例bigkey列表';\n\n--\n-- Table structure for table `instance_config`\n--\n\nDROP TABLE IF EXISTS `instance_config`;\nCREATE TABLE `instance_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '配置名',\n  `config_value` varchar(512) NOT NULL COMMENT '配置值',\n  `info` varchar(512) NOT NULL COMMENT '配置说明',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n  `status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n  `version_id` int(11) NOT NULL COMMENT 'Redis版本表主键id',\n  `refresh` mediumint(9) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例配置模板';\n\n-- ----------------------------\n--  Records of `instance_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_config` VALUES ('1', 'cluster-enabled', 'yes', '是否开启集群模式', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('2', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('3', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('4', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('5', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('6', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2016-07-05 15:08:31', '2', '1', '29', '0'), ('7', 'port', '%d', 'sentinel实例端口', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('8', 'dir', '%s', '工作目录', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('9', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('10', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('11', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('12', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('13', 'daemonize', 'no', '是否守护进程', '2016-07-14 14:00:05', '6', '1', '29', '0'), ('14', 'tcp-backlog', '511', 'TCP连接完成队列', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('15', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('16', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2016-12-06 11:40:46', '6', '1', '29', '0'), ('17', 'loglevel', 'notice', '日志级别', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('18', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('19', 'dir', '%s', 'redis工作目录', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('20', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('21', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('22', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('23', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('24', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('25', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('26', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('27', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('28', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('29', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('30', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('31', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('32', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('33', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('34', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('35', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('36', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('37', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('38', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('39', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('40', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('41', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2016-11-24 10:24:21', '6', '1', '29', '0'), ('42', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('43', 'hz', '10', '执行后台task数量,默认:10', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('44', 'port', '%d', '端口', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('45', 'maxmemory', '%dmb', '当前实例最大可用内存', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('46', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('47', 'appendonly', 'yes', '开启append only持久化模式', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('48', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('49', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('50', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('51', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('52', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('53', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('54', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('55', 'maxclients', '10000', '客户端最大连接数', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('126', 'cluster-enabled', 'yes', '是否开启集群模式', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('127', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('128', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('129', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('130', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('131', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('132', 'port', '%d', 'sentinel实例端口', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('133', 'dir', '%s', '工作目录', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('134', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('135', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('136', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('137', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('138', 'daemonize', 'no', '是否守护进程', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('139', 'tcp-backlog', '511', 'TCP连接完成队列', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('140', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('141', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('142', 'loglevel', 'notice', '日志级别', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('143', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('144', 'dir', '%s', 'redis工作目录', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('145', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('146', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('147', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('148', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('149', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('150', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('151', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('152', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('153', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('154', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('155', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('156', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('157', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('158', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('159', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2018-09-18 18:25:32', '6', '0', '31', '0'), ('160', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2018-09-18 18:25:40', '6', '0', '31', '0'), ('161', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('162', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('163', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('164', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('165', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('166', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('167', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('168', 'hz', '10', '执行后台task数量,默认:10', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('169', 'port', '%d', '端口', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('170', 'maxmemory', '%dmb', '当前实例最大可用内存', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('171', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('172', 'appendonly', 'yes', '开启append only持久化模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('173', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('174', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('175', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('176', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('177', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('178', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('179', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('180', 'maxclients', '10000', '客户端最大连接数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('181', 'protected-mode', 'yes', '开启保护模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('182', 'bind', '0.0.0.0', '默认客户端都可连接', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('185', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2018-09-18 18:26:32', '6', '1', '31', '0'), ('186', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2018-09-18 18:27:12', '6', '1', '31', '0'), ('253', 'protected-mode', 'no', '关闭保护模式', '2018-11-01 16:10:59', '5', '1', '31', '0'), ('354', 'cluster-enabled', 'yes', '是否开启集群模式', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('355', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('356', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('357', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('358', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('359', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('360', 'port', '%d', 'sentinel实例端口', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('361', 'dir', '%s', '工作目录', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('362', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('363', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('364', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('365', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('366', 'daemonize', 'no', '是否守护进程', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('367', 'tcp-backlog', '511', 'TCP连接完成队列', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('368', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('369', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('370', 'loglevel', 'notice', '日志级别', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('371', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('372', 'dir', '%s', 'redis工作目录', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('373', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('374', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('375', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('376', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('377', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('378', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('379', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('380', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('381', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('382', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('383', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('384', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('385', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('386', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('387', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('388', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('389', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('390', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('391', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('392', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('393', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('394', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('395', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('396', 'hz', '10', '执行后台task数量,默认:10', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('397', 'port', '%d', '端口', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('398', 'maxmemory', '%dmb', '当前实例最大可用内存', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('399', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('400', 'appendonly', 'yes', '开启append only持久化模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('401', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('402', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('403', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('404', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('405', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('406', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('407', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('408', 'maxclients', '10000', '客户端最大连接数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('409', 'protected-mode', 'yes', '开启保护模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('410', 'bind', '0.0.0.0', '默认客户端都可连接', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('411', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('412', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('413', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('414', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('415', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('416', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('417', 'slave-lazy-flush', 'yes', 'slave发起全量复制,是否采用flushall async清理老数据 默认值 no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('418', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2019-10-31 11:15:57', '6', '1', '12', '0'), ('419', 'protected-mode', 'no', '关闭sentinel保护模式', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('420', 'activedefrag', 'no', '碎片整理开启', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('421', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('422', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('423', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('424', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('425', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('506', 'cluster-enabled', 'yes', '是否开启集群模式', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('507', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('508', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('509', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('510', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('511', 'port', '%d', 'sentinel实例端口', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('512', 'dir', '%s', '工作目录', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('513', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('514', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('515', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('516', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('517', 'daemonize', 'no', '是否守护进程', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('518', 'tcp-backlog', '511', 'TCP连接完成队列', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('519', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('520', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('521', 'loglevel', 'notice', '日志级别', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('522', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('523', 'dir', '%s', 'redis工作目录', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('524', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('525', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('526', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('527', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('528', 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('529', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('530', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('531', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('532', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('533', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('534', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('535', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('536', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('537', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('538', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('539', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('540', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('541', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('542', 'hz', '10', '执行后台task数量,默认:10', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('543', 'port', '%d', '端口', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('544', 'maxmemory', '%dmb', '当前实例最大可用内存', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('545', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('546', 'appendonly', 'yes', '开启append only持久化模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('547', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('548', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('549', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('550', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('551', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('552', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('553', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('554', 'maxclients', '10000', '客户端最大连接数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('555', 'protected-mode', 'yes', '开启保护模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('556', 'bind', '0.0.0.0', '默认客户端都可连接', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('557', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('558', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('559', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('560', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('561', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('562', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('563', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('564', 'protected-mode', 'no', '关闭sentinel保护模式', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('565', 'activedefrag', 'yes', '碎片整理开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('566', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('567', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('568', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('569', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('570', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('571', 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('572', 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('573', 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('574', 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('575', 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('576', 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('577', 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('578', 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('579', 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('580', 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('581', 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('582', 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('583', 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('585', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:45:22', '6', '1', '37', '0'), ('587', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:18', '6', '1', '12', '0'), ('589', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:49', '6', '1', '31', '0'), ('590', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:49:47', '6', '1', '29', '0');\nCOMMIT;\n\n\n--\n-- Table structure for table `instance_fault`\n--\n\nDROP TABLE IF EXISTS `instance_fault`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `instance_fault` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例id',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0:心跳停止,1:心跳恢复',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `type` mediumint(4) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `reason` mediumtext NOT NULL COMMENT '故障原因描述',\n  PRIMARY KEY (`id`),\n  KEY `idx_ip_port` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `inst_id` (`inst_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8927 DEFAULT CHARSET=utf8 COMMENT='实例故障表' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `instance_host`\n--\n\nDROP TABLE IF EXISTS `instance_host`;\nCREATE TABLE `instance_host` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `ssh_user` varchar(32) DEFAULT NULL COMMENT 'ssh用户',\n  `ssh_pwd` varchar(32) DEFAULT NULL COMMENT 'ssh密码',\n  `warn` int(5) DEFAULT '1' COMMENT '0不报警，1报警',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_host_ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_info`\n--\n\nDROP TABLE IF EXISTS `instance_info`;\nCREATE TABLE `instance_info` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'memcached instance id',\n  `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '对等实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id，与app_desc关联',\n  `host_id` bigint(20) NOT NULL COMMENT '对应的主机id，与instance_host关联',\n  `ip` varchar(16) NOT NULL COMMENT '实例的ip',\n  `port` int(11) NOT NULL COMMENT '实例端口',\n  `status` tinyint(4) NOT NULL COMMENT '是否启用:0:节点异常,1:正常启用,2:节点下线',\n  `mem` int(11) NOT NULL COMMENT '内存大小',\n  `conn` int(11) NOT NULL COMMENT '连接数',\n  `cmd` varchar(255) NOT NULL COMMENT '启动实例的命令/redis-sentinel的masterName',\n  `type` mediumint(11) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_inst_ipport` (`ip`,`port`) USING BTREE,\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_latency_history`\n--\n\nDROP TABLE IF EXISTS `instance_latency_history`;\nCREATE TABLE `instance_latency_history` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `event` varchar(255) NOT NULL COMMENT '事件名称',\n  `execute_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `execution_cost` bigint(20) NOT NULL COMMENT '耗时(微妙)',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `latencyistorykey` (`instance_id`,`event`,`execute_date`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executedate` (`app_id`,`execute_date`),\n  KEY `idx_executedate` (`execute_date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例延迟事件信息表';\n\n--\n-- Table structure for table `instance_minute_stats`\n--\n\nDROP TABLE IF EXISTS `instance_minute_stats`;\nCREATE TABLE `instance_minute_stats` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `json` text NOT NULL COMMENT '统计json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_collect_time` (`collect_time`),\n  KEY `idx_created_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例分钟统计表';\n\n--\n-- Table structure for table `instance_reshard_process`\n--\n\nDROP TABLE IF EXISTS `instance_reshard_process`;\nCREATE TABLE `instance_reshard_process` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `audit_id` bigint(20) NOT NULL COMMENT '审核id',\n  `source_instance_id` int(11) NOT NULL COMMENT '源实例id',\n  `target_instance_id` int(11) NOT NULL COMMENT '目标实例id',\n  `start_slot` int(11) NOT NULL COMMENT '开始slot',\n  `end_slot` int(11) NOT NULL COMMENT '结束slot',\n  `migrating_slot` int(11) NOT NULL COMMENT '正在迁移的slot',\n  `is_pipeline` tinyint(4) NOT NULL COMMENT '是否为pipeline,0:否,1:是',\n  `finish_slot_num` int(11) NOT NULL COMMENT '已经完成迁移的slot数量',\n  `status` tinyint(4) NOT NULL COMMENT '0:运行中 1:完成 2:出错',\n  `start_time` datetime NOT NULL COMMENT '迁移开始时间',\n  `end_time` datetime NOT NULL COMMENT '迁移结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit` (`audit_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例Reshard进度';\n\n--\n-- Table structure for table `instance_slow_log`\n--\n\nDROP TABLE IF EXISTS `instance_slow_log`;\nCREATE TABLE `instance_slow_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `slow_log_id` bigint(20) NOT NULL COMMENT '慢查询id',\n  `cost_time` int(11) NOT NULL COMMENT '耗时(微妙)',\n  `command` varchar(255) NOT NULL COMMENT '执行命令',\n  `execute_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `slowlogkey` (`instance_id`,`slow_log_id`,`execute_time`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executetime` (`app_id`,`execute_time`),\n  KEY `idx_executetime` (`execute_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例慢查询列表';\n\n--\n-- Table structure for table `instance_statistics`\n--\n\nDROP TABLE IF EXISTS `instance_statistics`;\nCREATE TABLE `instance_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `host_id` bigint(20) NOT NULL COMMENT '机器的id',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `port` int(255) NOT NULL COMMENT 'port',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从',\n  `max_memory` bigint(255) NOT NULL COMMENT '预分配内存，单位byte',\n  `used_memory` bigint(255) NOT NULL COMMENT '已使用内存，单位byte',\n  `curr_items` bigint(255) NOT NULL COMMENT '当前item数量',\n  `curr_connections` int(255) NOT NULL COMMENT '当前连接数',\n  `misses` bigint(255) NOT NULL COMMENT 'miss数',\n  `hits` bigint(255) NOT NULL COMMENT '命中数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `mem_fragmentation_ratio` double DEFAULT '0' COMMENT '碎片率',\n  `aof_delayed_fsync` int(11) DEFAULT '0' COMMENT 'aof阻塞次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `machine_id` (`host_id`),\n  KEY `idx_inst_id` (`inst_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='实例的最新统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_info`\n--\n\nDROP TABLE IF EXISTS `machine_info`;\nCREATE TABLE `machine_info` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '机器的id',\n  `ssh_user` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh用户',\n  `ssh_passwd` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh密码',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `room` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '所属机房',\n  `mem` int(11) unsigned NOT NULL COMMENT '内存大小，单位G',\n  `cpu` mediumint(24) unsigned NOT NULL COMMENT 'cpu数量',\n  `virtual` tinyint(8) unsigned NOT NULL DEFAULT '1' COMMENT '是否虚拟，0表示否，1表示是',\n  `real_ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT '宿主机ip',\n  `service_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上线时间',\n  `fault_count` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '故障次数',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n  `warn` tinyint(255) unsigned NOT NULL DEFAULT '1' COMMENT '是否启用报警，0不启用，1启用',\n  `available` tinyint(255) NOT NULL COMMENT '表示机器是否可用，1表示可用，0表示不可用；',\n  `groupId` int(11) NOT NULL DEFAULT '0' COMMENT '机器分组，默认为0，表示原生资源，非0表示外部提供的资源(可扩展)',\n  `type` int(11) NOT NULL DEFAULT '0' COMMENT '0原生 1 其他',\n  `extra_desc` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '对于机器的额外说明(例如机器安装的其他服务(web,mysql,queue等等))',\n  `collect` int(11) DEFAULT '1' COMMENT 'switch of collect server status, 1:open, 0:close',\n  `version_install` varchar(512) COLLATE utf8_bin DEFAULT NULL COMMENT '机器安装redis版本状态',\n  `use_type` tinyint(4) DEFAULT '2' COMMENT '使用类型：Redis专用机器(0)，Redis测试机器(1)，混合部署机器(2)，Redis-Sentinel机器(3)',\n  `k8s_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否k8s容器：0:不是 1:是',\n  `rack` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '机器所在机架信息',\n  `is_allocating` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否在分配中,1是0否',\n  `disk` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '磁盘空间:G',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='机器信息表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_relation`\n--\n\nDROP TABLE IF EXISTS `machine_relation`;\nCREATE TABLE `machine_relation` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',\n  `ip` varchar(64) NOT NULL COMMENT '虚拟机ip',\n  `real_ip` varchar(64) NOT NULL COMMENT '宿主机ip',\n  `extra_desc` varchar(128) DEFAULT NULL COMMENT '实例描述信息',\n  `status` int(255) NOT NULL COMMENT '实例变更状态 0:offline ,1:online',\n  `is_sync` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据同步状态 0: 未同步数据  -1:同步中 1:数据已同步 -2:同步失败 ',\n  `sync_time` timestamp NULL DEFAULT NULL COMMENT '同步时间',\n  `update_time` timestamp NULL DEFAULT NULL COMMENT 'pod最后更新时间',\n  `taskid` bigint(11) DEFAULT NULL COMMENT '任务id',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `machine_room`\n--\n\nDROP TABLE IF EXISTS `machine_room`;\nCREATE TABLE `machine_room` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '机房id',\n  `name` varchar(255) NOT NULL COMMENT '机房名称',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `desc` varchar(255) DEFAULT NULL COMMENT '机房描述信息',\n  `ip_network` varchar(32) NOT NULL DEFAULT '' COMMENT '机房网段信息',\n  `operator` varchar(255) DEFAULT NULL COMMENT '运营商',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `machine_room`\n-- ----------------------------\nBEGIN;\nINSERT INTO `machine_room` VALUES ('1', '阿里云杭州', '1', '阿里云-杭州机房', '172.27.*.*', '阿里云');\nCOMMIT;\n\n--\n-- Table structure for table `machine_statistics`\n--\n\nDROP TABLE IF EXISTS `machine_statistics`;\nCREATE TABLE `machine_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `host_id` bigint(20) NOT NULL COMMENT '机器id',\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `cpu_usage` varchar(120) NOT NULL COMMENT 'cpu使用率',\n  `load` varchar(120) NOT NULL COMMENT '机器负载',\n  `traffic` varchar(120) NOT NULL COMMENT 'io网络流量',\n  `memory_usage_ratio` varchar(120) NOT NULL COMMENT '内存使用率',\n  `memory_free` varchar(120) NOT NULL COMMENT '内存剩余',\n  `memory_total` varchar(120) NOT NULL COMMENT '总内存量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  `max_memory` int(11) DEFAULT '0' COMMENT '机器分配内存,单位MB',\n  `instance_count` int(11) DEFAULT '0' COMMENT '机器实例数量',\n  `machine_memory` int(11) DEFAULT '0' COMMENT '机器入库总内存,单位MB',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_ip` (`ip`),\n  KEY `host_id` (`host_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器状态统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_blob_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_blob_triggers`;\nCREATE TABLE `qrtz_blob_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `BLOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_calendars`\n--\n\nDROP TABLE IF EXISTS `qrtz_calendars`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `qrtz_calendars` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `CALENDAR_NAME` varchar(200) NOT NULL COMMENT 'calendar名称',\n  `CALENDAR` blob NOT NULL COMMENT 'calendar信息',\n  PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='以 Blob 类型存储 Quartz 的 Calendar 信息' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `qrtz_cron_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_cron_triggers`;\nCREATE TABLE `qrtz_cron_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `TRIGGER_NAME` varchar(200) NOT NULL COMMENT 'trigger名',\n  `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT 'trigger组',\n  `CRON_EXPRESSION` varchar(120) NOT NULL COMMENT 'cron表达式',\n  `TIME_ZONE_ID` varchar(80) DEFAULT NULL COMMENT '时区',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储 Cron Trigger，包括 Cron 表达式和时区信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_fired_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_fired_triggers`;\nCREATE TABLE `qrtz_fired_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `ENTRY_ID` varchar(195) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL,\n  `FIRED_TIME` bigint(13) NOT NULL,\n  `SCHED_TIME` bigint(13) NOT NULL,\n  `PRIORITY` int(11) NOT NULL,\n  `STATE` varchar(16) NOT NULL,\n  `JOB_NAME` varchar(200) DEFAULT NULL,\n  `JOB_GROUP` varchar(200) DEFAULT NULL,\n  `IS_NONCONCURRENT` varchar(1) DEFAULT NULL COMMENT '是否非并行执行',\n  `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL COMMENT '是否持久化',\n  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),\n  KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),\n  KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已触发的 Trigger相关的状态信息，以及关联 Job 的执行信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_job_details`\n--\n\nDROP TABLE IF EXISTS `qrtz_job_details`;\nCREATE TABLE `qrtz_job_details` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `JOB_CLASS_NAME` varchar(250) NOT NULL,\n  `IS_DURABLE` varchar(1) NOT NULL COMMENT '是否持久化，0不持久化，1持久化',\n  `IS_NONCONCURRENT` varchar(1) NOT NULL COMMENT '是否非并发，0非并发，1并发',\n  `IS_UPDATE_DATA` varchar(1) NOT NULL,\n  `REQUESTS_RECOVERY` varchar(1) NOT NULL COMMENT '是否可恢复，0不恢复，1恢复',\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储每一个已配置的 Job 的详细信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_locks`\n--\n\nDROP TABLE IF EXISTS `qrtz_locks`;\nCREATE TABLE `qrtz_locks` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `LOCK_NAME` varchar(40) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储程序的悲观锁的信息(假如使用了悲观锁)' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_paused_trigger_grps`\n--\n\nDROP TABLE IF EXISTS `qrtz_paused_trigger_grps`;\nCREATE TABLE `qrtz_paused_trigger_grps` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已暂停的 Trigger 组的信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_scheduler_state`\n--\n\nDROP TABLE IF EXISTS `qrtz_scheduler_state`;\nCREATE TABLE `qrtz_scheduler_state` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL COMMENT '执行quartz实例的主机名',\n  `LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '实例将状态报告给集群中的其它实例的上一次时间',\n  `CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '实例间状态报告的时间频率',\n  PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储少量的有关 Scheduler 的状态信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_simple_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_simple_triggers`;\nCREATE TABLE `qrtz_simple_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复次数',\n  `REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复间隔',\n  `TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '已出发次数',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储简单的 Trigger，包括重复次数，间隔，以及已触的次数' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_simprop_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_simprop_triggers`;\nCREATE TABLE `qrtz_simprop_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `STR_PROP_1` varchar(512) DEFAULT NULL,\n  `STR_PROP_2` varchar(512) DEFAULT NULL,\n  `STR_PROP_3` varchar(512) DEFAULT NULL,\n  `INT_PROP_1` int(11) DEFAULT NULL,\n  `INT_PROP_2` int(11) DEFAULT NULL,\n  `LONG_PROP_1` bigint(20) DEFAULT NULL,\n  `LONG_PROP_2` bigint(20) DEFAULT NULL,\n  `DEC_PROP_1` decimal(13,4) DEFAULT NULL,\n  `DEC_PROP_2` decimal(13,4) DEFAULT NULL,\n  `BOOL_PROP_1` varchar(1) DEFAULT NULL,\n  `BOOL_PROP_2` varchar(1) DEFAULT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_triggers`;\nCREATE TABLE `qrtz_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PRIORITY` int(11) DEFAULT NULL,\n  `TRIGGER_STATE` varchar(16) NOT NULL,\n  `TRIGGER_TYPE` varchar(8) NOT NULL,\n  `START_TIME` bigint(13) NOT NULL,\n  `END_TIME` bigint(13) DEFAULT NULL,\n  `CALENDAR_NAME` varchar(200) DEFAULT NULL,\n  `MISFIRE_INSTR` smallint(2) DEFAULT NULL,\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),\n  KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已配置的 Trigger 的信息' /* `compression`='tokudb_zlib' */;\n\n\n--\n-- Table structure for table `server`\n--\n\nDROP TABLE IF EXISTS `server`;\nCREATE TABLE `server` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `host` varchar(255) DEFAULT NULL COMMENT 'host',\n  `nmon` varchar(255) DEFAULT NULL COMMENT 'nmon version',\n  `cpus` tinyint(4) DEFAULT NULL COMMENT 'logic cpu num',\n  `cpu_model` varchar(255) DEFAULT NULL COMMENT 'cpu 型号',\n  `dist` varchar(255) DEFAULT NULL COMMENT '发行版信息',\n  `kernel` varchar(255) DEFAULT NULL COMMENT '内核信息',\n  `ulimit` varchar(255) DEFAULT NULL COMMENT 'ulimit -n,ulimit -u',\n  `updatetime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ip`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `server_stat`\n--\n\nDROP TABLE IF EXISTS `server_stat`;\nCREATE TABLE `server_stat` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `cdate` date NOT NULL COMMENT '数据收集天',\n  `ctime` char(4) NOT NULL COMMENT '数据收集小时分钟',\n  `cuser` float DEFAULT NULL COMMENT '用户态占比',\n  `csys` float DEFAULT NULL COMMENT '内核态占比',\n  `cwio` float DEFAULT NULL COMMENT 'wio占比',\n  `c_ext` text COMMENT '子cpu占比',\n  `cload1` float DEFAULT NULL COMMENT '1分钟load',\n  `cload5` float DEFAULT NULL COMMENT '5分钟load',\n  `cload15` float DEFAULT NULL COMMENT '15分钟load',\n  `mtotal` float DEFAULT NULL COMMENT '总内存,单位M',\n  `mfree` float DEFAULT NULL COMMENT '空闲内存',\n  `mcache` float DEFAULT NULL COMMENT 'cache',\n  `mbuffer` float DEFAULT NULL COMMENT 'buffer',\n  `mswap` float DEFAULT NULL COMMENT 'cache',\n  `mswap_free` float DEFAULT NULL COMMENT 'cache',\n  `nin` float DEFAULT NULL COMMENT '网络入流量 单位K/s',\n  `nout` float DEFAULT NULL COMMENT '网络出流量 单位k/s',\n  `nin_ext` text COMMENT '各网卡入流量详情',\n  `nout_ext` text COMMENT '各网卡出流量详情',\n  `tuse` int(11) DEFAULT NULL COMMENT 'tcp estab连接数',\n  `torphan` int(11) DEFAULT NULL COMMENT 'tcp orphan连接数',\n  `twait` int(11) DEFAULT NULL COMMENT 'tcp time wait连接数',\n  `dread` float DEFAULT NULL COMMENT '磁盘读速率 单位K/s',\n  `dwrite` float DEFAULT NULL COMMENT '磁盘写速率 单位K/s',\n  `diops` float DEFAULT NULL COMMENT '磁盘io速率 交互次数/s',\n  `dbusy` float DEFAULT NULL COMMENT '磁盘io带宽使用百分比',\n  `d_ext` text COMMENT '磁盘各分区占比',\n  `dspace` text COMMENT '磁盘各分区空间使用率',\n  PRIMARY KEY (`ip`,`cdate`,`ctime`),\n  KEY `idx_cdate` (`cdate`),\n  KEY `idx_cdate_ctime` (`cdate`,`ctime`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `system_config`\n--\n\nDROP TABLE IF EXISTS `system_config`;\nCREATE TABLE `system_config` (\n  `config_key` varchar(255) NOT NULL COMMENT '配置key',\n  `config_value` varchar(512) NOT NULL COMMENT '配置value',\n  `info` varchar(255) NOT NULL COMMENT '配置说明',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `order_id` int(11) NOT NULL COMMENT '顺序',\n  PRIMARY KEY (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统配置';\n\n-- ----------------------------\n--  Records of `system_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_config` VALUES ('cachecloud.admin.user.name','admin','cachecloud-admin用户名',1,11),('cachecloud.admin.user.password','admin','cachelcoud-admin密码',1,12),('cachecloud.app.client.conn.threshold','2000','应用连接数报警阀值',1,33),('cachecloud.base.dir','/opt','cachecloud根目录，要和cachecloud-init.sh脚本中的目录一致',1,31),('cachecloud.contact','user1:(xx@zz.com, user1:135xxxxxxxx)<br/>user2: (user2@zz.com, user2:138xxxxxxxx)','值班联系人信息',1,14),('cachecloud.cookie.domain','','cookie登录方式所需要的域名',1,22),('cachecloud.email.alert.interface','','邮件报警接口(参考报警接口规范)',1,24),('cachecloud.machine.ssh.name','cachecloud-open','机器ssh用户名',1,2),('cachecloud.machine.ssh.password','cachecloud-open','机器ssh密码',1,3),('cachecloud.machine.ssh.port','22','机器ssh端口',1,10),('cachecloud.machine.stats.cron.minute','1','机器性能统计周期(分钟)',1,35),('cachecloud.nmon.dir','/opt/cachecloud','nmon安装目录',1,32),('cachecloud.owner.email','xx@sohu.com,yy@qq.com','邮件报警(逗号隔开)',1,21),('cachecloud.owner.phone','xxx,yyy','手机号报警(逗号隔开)',1,21),('cachecloud.owner.weChat','xxx,yyy','微信号报警(逗号隔开)',1,21),('cachecloud.public.key.pem','/opt/ssh/id_rsa','密钥路径',1,5),('cachecloud.public.user.name','cachecloud-open','公钥用户名',1,4),('cachecloud.ssh.auth.type','1','ssh授权方式',1,1),('cachecloud.superAdmin','admin,xx,yy','超级管理员组',1,13),('cachecloud.user.login.type','1','用户登录状态保存方式(session或cookie)',1,22),('cachecloud.weChat.alert.interface','','微信报警接口(参考报警接口规范)',1,23),('cachecloud.whether.schedule.clean.data','false','是否定期清理统计数据',1,34),('machine.load.alert.ratio','8.0','机器负载报警阀值',1,32);\nCOMMIT;\n\n-- ----------------------------\n--  Table structure for `system_resource`\n-- ----------------------------\nDROP TABLE IF EXISTS `system_resource`;\nCREATE TABLE `system_resource` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '资源ID',\n  `name` varchar(64) NOT NULL COMMENT '资源名称',\n  `intro` varchar(255) DEFAULT NULL COMMENT '资源说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:仓库地址 2:脚本 3:资源包 4:公钥/私钥 6:目录管理 7:迁移工具管理',\n  `lastmodify` datetime DEFAULT NULL COMMENT '最后更新时间',\n  `dir` varchar(128) DEFAULT NULL COMMENT '资源路径',\n  `url` varchar(128) DEFAULT NULL COMMENT '仓库地址',\n  `ispush` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:未推送 1:已推送',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `username` varchar(255) DEFAULT NULL COMMENT '最后修改人',\n  `task_id` bigint(11) DEFAULT NULL COMMENT '迁移任务id',\n  `compile_info` varchar(255) DEFAULT NULL COMMENT '编译信息',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `system_resource`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_resource` VALUES (1,'cachecloud-init.sh','容器初始化脚本',2,'2020-07-15 18:35:41','/script','',0,1,NULL,NULL,NULL),(2,'x.x.x.x',NULL,1,'2020-08-10 10:31:51','/opt/download/software/cachecloud/resource','http://x.x.x.x/software/cachecloud/resource',0,1,'admin',0,NULL),(4,'cachecloud-env.sh','宿主环境脚本',2,'2020-07-15 18:36:28','/script','',0,1,NULL,NULL,NULL),(5,'id_rsa','私钥文件',4,'2020-07-07 10:45:39','/ssh','',0,1,NULL,NULL,NULL),(6,'id_rsa.pub','公钥文件',4,'2020-07-07 10:45:45','/ssh','',0,1,NULL,NULL,NULL),(12,'redis-4.0.14','redis 4.0.14资源包',3,'2020-08-10 09:52:41','/redis','http://download.redis.io/releases/redis-4.0.14.tar.gz',0,1,'admin',532,NULL),(21,'/script','脚本目录管理',6,'2020-08-10 10:51:34','',NULL,0,1,'admin',0,NULL),(28,'/ssh','ssh目录',6,'2020-07-20 17:55:03',NULL,NULL,0,1,'admin',0,NULL),(29,'redis-3.0.7','redis3.0.7 资源包',3,'2020-08-10 09:53:32','/redis','http://download.redis.io/releases/redis-3.0.7.tar.gz',0,1,'admin',529,NULL),(31,'redis-3.2.12','redis 3.2.12 资源包',3,'2020-08-10 15:08:21','/redis','http://download.redis.io/releases/redis-3.2.12.tar.gz',0,1,'admin',530,NULL),(32,'/redis','redis资源包管理',6,'2020-07-20 17:54:59',NULL,NULL,0,1,'admin',0,NULL),(33,'/tool','迁移工具资源包',6,'2020-07-20 17:54:53',NULL,NULL,0,1,'admin',0,NULL),(37,'redis-5.0.9','redis5.0.9 资源包',3,'2020-08-10 09:51:41','/redis','http://download.redis.io/releases/redis-5.0.9.tar.gz',0,1,'admin',533,NULL),(40,'redis-shake-2.0.3','redis 2.0.3\\n修复fix 5.0迁移类型问题',7,'2020-08-11 10:53:26','/tool','https://github.com/alibaba/RedisShake/releases/download/release-v2.0.3-20200724/redis-shake-v2.0.3.tar.gz',0,1,'admin',518,NULL);\nCOMMIT;\n\n--\n-- Table structure for table `task_queue`\n--\n\nDROP TABLE IF EXISTS `task_queue`;\nCREATE TABLE `task_queue` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `important_info` varchar(255) NOT NULL DEFAULT '' COMMENT '重要信息',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `param` longtext NOT NULL COMMENT '任务参数(json):随着任务变化',\n  `init_param` longtext NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2中断，3失败',\n  `parent_task_id` bigint(20) NOT NULL COMMENT '父任务id',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `priority` int(11) NOT NULL COMMENT '优先级',\n  `error_code` int(11) NOT NULL COMMENT '错误代码',\n  `error_msg` varchar(255) NOT NULL COMMENT '错误消息',\n  `task_note` varchar(255) NOT NULL COMMENT '备注',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务表';\n\n--\n-- Table structure for table `task_step_flow`\n--\n\nDROP TABLE IF EXISTS `task_step_flow`;\nCREATE TABLE `task_step_flow` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `task_id` bigint(20) NOT NULL COMMENT '任务id',\n  `child_task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '子任务id',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0未开始、1成功、2中断、3跳过、4失败',\n  `log` text COMMENT '日志',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_task_class_step` (`task_id`,`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤流表';\n\n--\n-- Table structure for table `task_step_meta`\n--\n\nDROP TABLE IF EXISTS `task_step_meta`;\nCREATE TABLE `task_step_meta` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `step_desc` varchar(255) NOT NULL COMMENT '步骤描述',\n  `ops_device` varchar(255) NOT NULL COMMENT '运维建议',\n  `timeout` int(11) NOT NULL COMMENT '超时时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_class_step` (`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤元数据表';\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n-- ----------------------------\n--  Table structure for `standard_statistics`\n-- ----------------------------\nDROP TABLE IF EXISTS `standard_statistics`;\nCREATE TABLE `standard_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `info_json` text NOT NULL COMMENT '收集的json数据',\n  `diff_json` text NOT NULL COMMENT '上一次收集差异的json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `cluster_info_json` varchar(20480) NOT NULL DEFAULT '' COMMENT '收集的cluster info json数据',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_create_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;"
  },
  {
    "path": "cachecloud-web/sql/3.0.sql",
    "content": "-- MySQL dump 10.15  Distrib 10.0.16-MariaDB, for Linux (x86_64)\n--\n-- Host: localhost    Database: cachecloud_open\n-- ------------------------------------------------------\n-- Server version\t10.0.16-MariaDB-log\n\nSET NAMES utf8;\nSET FOREIGN_KEY_CHECKS = 0;\n\n--\n-- Table structure for table `app_audit`\n--\n\nDROP TABLE IF EXISTS `app_audit`;\nCREATE TABLE `app_audit` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人的id',\n  `user_name` varchar(64) NOT NULL COMMENT '用户名',\n  `type` tinyint(4) NOT NULL COMMENT '申请类型:0:申请应用,1:应用扩容,2:修改配置',\n  `param1` varchar(600) DEFAULT NULL COMMENT '预留参数1',\n  `param2` varchar(600) DEFAULT NULL COMMENT '预留参数2',\n  `param3` varchar(600) DEFAULT NULL COMMENT '预留参数3',\n  `info` varchar(360) NOT NULL COMMENT '申请描述',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:等待审批; 1:审批通过; -1:驳回',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `refuse_reason` varchar(360) DEFAULT NULL COMMENT '驳回理由',\n  `task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '任务id',\n  `operate_id` bigint(20) DEFAULT NULL COMMENT '工单处理人',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid` (`app_id`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_status_create_time` (`status`,`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用审核表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_audit_log`\n--\n\nDROP TABLE IF EXISTS `app_audit_log`;\nCREATE TABLE `app_audit_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '审批操作人id',\n  `info` longtext NOT NULL COMMENT 'app审批的详细信息',\n  `type` tinyint(4) NOT NULL,\n  `create_time` datetime NOT NULL,\n  `app_audit_id` bigint(20) NOT NULL COMMENT '审批id',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit_appid` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app审核日志表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_client_command_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_command_minute_statistics`;\nCREATE TABLE `app_client_command_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `command` varchar(20) NOT NULL COMMENT '命令明文',\n  `cost` bigint(20) DEFAULT NULL COMMENT '命令累计毫秒耗时',\n  `bytes_in` bigint(20) DEFAULT NULL COMMENT '输入流量',\n  `bytes_out` bigint(20) DEFAULT NULL COMMENT '输出流量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `count` int(11) DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__appid_client_command_currentMin` (`app_id`,`client_ip`,`command`,`current_min`),\n  KEY `idx_currentmin_appid_count_cost` (`current_min`,`app_id`,`count`,`cost`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟命令调用上报数据';\n\n--\n-- Table structure for table `app_client_exception_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_exception_minute_statistics`;\nCREATE TABLE `app_client_exception_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `type` tinyint(4) NOT NULL COMMENT '0:connect exception;1:command exception',\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `node` varchar(30) NOT NULL COMMENT '节点信息host:port',\n  `count` bigint(20) DEFAULT NULL COMMENT '累计连接失败次数',\n  `cost` bigint(20) DEFAULT NULL COMMENT '累计连接失败毫秒耗时',\n  `latency_commands` varchar(255) DEFAULT NULL COMMENT '统计命令topN id,逗号分隔',\n  `redis_pool_config` varchar(255) DEFAULT NULL COMMENT 'redis连接池配置信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__client_node_type_currentMin` (`client_ip`,`node`,`type`,`current_min`),\n  KEY `idx_appid_current_min` (`app_id`,`current_min`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟异常上报数据';\n\n--\n-- Table structure for table `app_client_latency_command`\n--\n\nDROP TABLE IF EXISTS `app_client_latency_command`;\nCREATE TABLE `app_client_latency_command` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `command` varchar(255) NOT NULL COMMENT '命令明文',\n  `size` bigint(20) DEFAULT NULL COMMENT '参数长度',\n  `args` varchar(255) DEFAULT NULL COMMENT '裁剪后参数明文',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `invoke_time` bigint(20) DEFAULT NULL COMMENT '命令调用时间戳',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端异常命令调用详情';\n\n--\n-- Table structure for table `app_client_statistic_gather`\n--\n\nDROP TABLE IF EXISTS `app_client_statistic_gather`;\nCREATE TABLE `app_client_statistic_gather` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gather_time` varchar(20) NOT NULL COMMENT '统计时间，格式yyyy-mm-dd',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `cmd_count` bigint(20) DEFAULT '0' COMMENT '命令调用次数',\n  `conn_exp_count` bigint(20) DEFAULT '0' COMMENT '连接异常次数',\n  `avg_cmd_cost` double DEFAULT '0' COMMENT '命令调用平均耗时，单位毫秒',\n  `avg_cmd_exp_cost` double DEFAULT '0' COMMENT '命令超时平均耗时，单位毫秒',\n  `avg_conn_exp_cost` double DEFAULT '0' COMMENT '连接异常平均耗时，单位毫秒',\n  `cmd_exp_count` bigint(20) DEFAULT '0' COMMENT '命令超时次数',\n  `instance_count` int(11) DEFAULT NULL COMMENT '应用实例数',\n  `avg_mem_frag_ratio` double DEFAULT NULL COMMENT '平均碎片率',\n  `mem_used_ratio` double DEFAULT NULL COMMENT '内存使用率',\n  `exception_count` bigint(20) DEFAULT '0' COMMENT '异常数（旧，待下线）',\n  `slow_log_count` bigint(20) DEFAULT '0' COMMENT '慢查询次数',\n  `latency_count` bigint(20) DEFAULT '0' COMMENT '延迟事件次数',\n  `object_size` bigint(20) DEFAULT '0' COMMENT '存储对象数',\n  `used_memory` bigint(20) DEFAULT '0' COMMENT '内存占用 byte',\n  `used_memory_rss` bigint(20) DEFAULT '0' COMMENT '物理内存占用 byte',\n  `max_cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗(单位:秒)',\n  `max_cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗(单位:秒)',\n  `connected_clients` bigint(20) DEFAULT '0' COMMENT '应用客户端连接数',\n  `topology_exam_result` tinyint(4) DEFAULT NULL COMMENT '拓扑诊断结果，0：正常，1：异常',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_appid_gathertime` (`app_id`,`gather_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报数据全天统计';\n\n--\n-- Table structure for table `app_client_value_minute_stat`\n--\n\nDROP TABLE IF EXISTS `app_client_value_minute_stat`;\nCREATE TABLE `app_client_value_minute_stat` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用appid',\n  `collect_time` bigint(20) NOT NULL COMMENT '数据收集时间yyyyMMddHHmm00',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `command` varchar(20) NOT NULL COMMENT '执行命令',\n  `distribute_type` tinyint(4) NOT NULL COMMENT '值分布',\n  `count` int(11) NOT NULL COMMENT '命令执行次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_collect_command_dis` (`app_id`,`collect_time`,`command`,`distribute_type`),\n  KEY `idx_collect_time` (`collect_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟值分布上报数据统计';\n\n--\n-- Table structure for table `app_client_version_statistic`\n--\n\nDROP TABLE IF EXISTS `app_client_version_statistic`;\nCREATE TABLE `app_client_version_statistic` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip地址',\n  `client_version` varchar(20) NOT NULL COMMENT '客户端版本',\n  `report_time` datetime DEFAULT NULL COMMENT '上报时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_client_ip` (`app_id`,`client_ip`),\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报版本信息统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_daily`\n--\n\nDROP TABLE IF EXISTS `app_daily`;\nCREATE TABLE `app_daily` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `date` date NOT NULL COMMENT '日期',\n  `create_time` datetime NOT NULL,\n  `slow_log_count` bigint(20) NOT NULL COMMENT '慢查询个数',\n  `client_exception_count` bigint(20) NOT NULL COMMENT '客户端异常个数',\n  `max_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟最大客户端连接数',\n  `avg_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟平均客户端连接数',\n  `max_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟最大命令数',\n  `avg_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟平均命令数',\n  `avg_hit_ratio` double NOT NULL COMMENT '平均命中率',\n  `min_minute_hit_ratio` double NOT NULL COMMENT '每分钟最小命中率',\n  `max_minute_hit_ratio` double NOT NULL COMMENT '每分钟最大命中率',\n  `avg_used_memory` bigint(20) NOT NULL COMMENT '最大内存使用量',\n  `max_used_memory` bigint(20) NOT NULL COMMENT '平均内存使用量',\n  `expired_keys_count` bigint(20) NOT NULL COMMENT '过期键个数',\n  `evicted_keys_count` bigint(20) NOT NULL COMMENT '剔除键个数',\n  `avg_minute_net_input_byte` double NOT NULL COMMENT '每分钟平均网络input量',\n  `max_minute_net_input_byte` double NOT NULL COMMENT '每分钟最大网络input量',\n  `avg_minute_net_output_byte` double NOT NULL COMMENT '每分钟平均网络output量',\n  `max_minute_net_output_byte` double NOT NULL COMMENT '每分钟最大网络output量',\n  `avg_object_size` bigint(20) NOT NULL COMMENT '键个数平均值',\n  `max_object_size` bigint(20) NOT NULL COMMENT '键个数最大值',\n  `big_key_times` bigint(20) NOT NULL COMMENT 'bigkey次数',\n  `big_key_info` varchar(512) COLLATE utf8_bin NOT NULL COMMENT 'bigkey详情',\n  `client_cmd_count` bigint(20) NOT NULL COMMENT '累计命令调用次数',\n  `client_avg_cmd_cost` double NOT NULL COMMENT '平均命令调用耗时',\n  `client_conn_exp_count` bigint(20) NOT NULL COMMENT '累计连接异常事件次数',\n  `client_avg_conn_exp_cost` double NOT NULL COMMENT '平均连接异常事件耗时',\n  `client_cmd_exp_count` bigint(20) NOT NULL COMMENT '累计命令超时事件次数',\n  `client_avg_cmd_exp_cost` double NOT NULL COMMENT '平均命令超时事件耗时',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid_date` (`app_id`,`date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='app日报';\n\n--\n-- Table structure for table `app_data_migrate_status`\n--\n\nDROP TABLE IF EXISTS `app_data_migrate_status`;\nCREATE TABLE `app_data_migrate_status` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `migrate_machine_ip` varchar(255) NOT NULL COMMENT '迁移工具所在机器ip',\n  `migrate_machine_port` int(11) NOT NULL COMMENT '迁移工具所占port',\n  `source_migrate_type` tinyint(4) NOT NULL COMMENT '源迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `source_servers` varchar(2048) NOT NULL COMMENT '源实例列表',\n  `target_migrate_type` tinyint(4) NOT NULL COMMENT '目标迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `target_servers` varchar(2048) NOT NULL COMMENT '目标实例列表',\n  `source_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '源应用id',\n  `target_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '目标应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '操作人',\n  `status` tinyint(4) NOT NULL COMMENT '迁移执行状态,0:开始,1:结束,2:异常',\n  `start_time` datetime NOT NULL COMMENT '迁移开始执行时间',\n  `end_time` datetime DEFAULT NULL COMMENT '迁移结束执行时间',\n  `log_path` varchar(255) NOT NULL COMMENT '日志文件路径',\n  `config_path` varchar(255) NOT NULL COMMENT '配置文件路径',\n  `migrate_id` varchar(50) DEFAULT NULL COMMENT 'migrate id',\n  `migrate_tool` tinyint(4) DEFAULT NULL COMMENT 'migrate_tool, 0:redis-shake,1:redis-migrate-tool',\n  `redis_source_version` varchar(20) DEFAULT NULL,\n  `redis_target_version` varchar(20) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用迁移记录详情';\n\n--\n-- Table structure for table `app_desc`\n--\n\nDROP TABLE IF EXISTS `app_desc`;\nCREATE TABLE `app_desc` (\n  `app_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '应用id',\n  `name` varchar(36) NOT NULL COMMENT '应用名',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人id',\n  `status` tinyint(4) NOT NULL COMMENT '应用状态, 0未分配，1申请未审批，2审批并发布 3:应用下线',\n  `intro` varchar(255) NOT NULL COMMENT '应用描述',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `passed_time` datetime NOT NULL COMMENT '审批通过时间',\n  `type` int(10) NOT NULL DEFAULT '0' COMMENT 'cache类型，1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud ,5. redis-sentinel ,6.redis-standalone ',\n  `officer` varchar(32) NOT NULL COMMENT '负责人，中文',\n  `ver_id` int(11) NOT NULL COMMENT '版本',\n  `is_test` tinyint(4) DEFAULT '0' COMMENT '是否测试：1是0否',\n  `need_persistence` tinyint(4) DEFAULT '1' COMMENT '是否需要持久化: 1是0否',\n  `need_hot_back_up` tinyint(4) DEFAULT '1' COMMENT '是否需要热备: 1是0否',\n  `has_back_store` tinyint(4) DEFAULT '1' COMMENT '是否有后端数据源: 1是0否',\n  `forecase_qps` int(11) DEFAULT NULL COMMENT '预估qps',\n  `forecast_obj_num` int(11) DEFAULT NULL COMMENT '预估条目数',\n  `mem_alert_value` int(11) DEFAULT NULL COMMENT '内存报警阀值',\n  `client_machine_room` varchar(36) DEFAULT NULL COMMENT '客户端机房信息',\n  `client_conn_alert_value` int(11) DEFAULT '2000' COMMENT '客户端连接报警阀值',\n  `app_key` varchar(255) DEFAULT NULL COMMENT '应用秘钥',\n  `important_level` tinyint(4) NOT NULL DEFAULT '2' COMMENT '应用级别，1:最重要，2:一般重要，3:一般',\n  `password` varchar(255) DEFAULT '' COMMENT 'redis密码',\n  `custom_password` varchar(255) DEFAULT NULL COMMENT '自定义密码',\n  `hit_precent_alert_value` int(11) DEFAULT '0' COMMENT '命中率报警阀值 0:不报警 ',\n  `is_access_monitor` int(11) DEFAULT '0' COMMENT '是否接入全局监控报警 默认0,0:不接入监控 1:接入监控',\n  `app_fsync_value` int(11) DEFAULT '1' COMMENT '应用刷盘策略 1:主从节点appdendfsync=everysec 2:主从节点 appdendfsync=no',\n  `version_id` int(11) NOT NULL DEFAULT '1' COMMENT 'Redis版本表主键id',\n  PRIMARY KEY (`app_id`),\n  UNIQUE KEY `uidx_app_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app应用描述' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_command_statistics`;\nCREATE TABLE `app_hour_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHH',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`command_name`,`collect_time`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每小时命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_statistics`;\nCREATE TABLE `app_hour_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHH',\n  `hits` bigint(20) NOT NULL COMMENT '每小时命中数量和',\n  `misses` bigint(20) NOT NULL COMMENT '每小时未命中数量和',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '每小时内存占用最大值',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '每小时过期key数量和',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '每小时驱逐key数量和',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '每小时客户端连接数最大值',\n  `object_size` bigint(20) NOT NULL COMMENT '每小时存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '每小时参与累加实例数最小值',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '每小时修改时间最大值',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用统计数据每小时统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_command_statistics`;\nCREATE TABLE `app_minute_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHHmm',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`,`command_name`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每分钟命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_statistics`;\nCREATE TABLE `app_minute_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `hits` bigint(20) NOT NULL COMMENT '命中数量',\n  `misses` bigint(20) NOT NULL COMMENT '未命中数量',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '内存占用',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '过期key数量',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '驱逐key数量',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '客户端连接数',\n  `object_size` bigint(20) NOT NULL COMMENT '每分钟存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '参与累加实例数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_to_user`\n--\n\nDROP TABLE IF EXISTS `app_to_user`;\nCREATE TABLE `app_to_user` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_user`\n--\n\nDROP TABLE IF EXISTS `app_user`;\nCREATE TABLE `app_user` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL COMMENT '用户名',\n  `ch_name` varchar(255) NOT NULL COMMENT '中文名',\n  `email` varchar(64) NOT NULL COMMENT '邮箱',\n  `mobile` varchar(16) NOT NULL COMMENT '手机',\n  `type` int(4) NOT NULL DEFAULT '2' COMMENT '0管理员，1预留，2普通用户，-1无效',\n  `weChat` varchar(32) DEFAULT NULL COMMENT '微信号',\n  `isAlert` tinyint(4) NOT NULL DEFAULT '1' COMMENT '用户是否接收报警 0:不接收 1:接收',\n  `password` varchar(64) DEFAULT NULL COMMENT '密码',\n  `register_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',\n  `purpose` varchar(255) DEFAULT NULL COMMENT '使用目的',\n  `company` varchar(255) DEFAULT NULL COMMENT '公司名称',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_user_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表' /* `compression`='tokudb_zlib' */;\n\n-- ----------------------------\n--  Records of `app_user`\n-- ----------------------------\nBEGIN;\nINSERT INTO `app_user` VALUES ('1', 'admin', 'admin', 'admin@xxx.com', '13500000000', '0', null, '1', NULL, current_timestamp(), NULL, NULL);\nCOMMIT;\n\n--\n-- Table structure for table `brevity_schedule_resources`\n--\n\nDROP TABLE IF EXISTS `brevity_schedule_resources`;\nCREATE TABLE `brevity_schedule_resources` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `type` tinyint(4) NOT NULL COMMENT '类型,见:BrevityScheduleType',\n  `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '时间版本',\n  `host` varchar(16) NOT NULL COMMENT '资源ip',\n  `port` int(11) NOT NULL DEFAULT '0' COMMENT '端口',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_type_host_port` (`type`,`host`,`port`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='短频任务表';\n\n--\n-- Table structure for table `diagnostic_task_record`\n--\n\nDROP TABLE IF EXISTS `diagnostic_task_record`;\nCREATE TABLE `diagnostic_task_record` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `type` int(11) DEFAULT NULL COMMENT '诊断类型：0scan 1bigkey 2idle key 3hotkey 4del key 5slot analysis 6topology exam',\n  `task_id` bigint(20) DEFAULT NULL COMMENT '任务流id',\n  `audit_id` bigint(20) DEFAULT NULL COMMENT '审批id',\n  `status` int(11) DEFAULT NULL COMMENT '诊断状态：0开始 1结束 2异常',\n  `cost` bigint(20) DEFAULT NULL COMMENT '耗时，毫秒',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `redis_key` varchar(100) DEFAULT NULL COMMENT '结果的key',\n  `node` varchar(100) DEFAULT NULL COMMENT '实例，host:port',\n  `parent_task_id` bigint(20) DEFAULT NULL COMMENT '父任务id',\n  `diagnostic_condition` varchar(100) DEFAULT NULL COMMENT '诊断条件',\n  `param1` varchar(100) DEFAULT NULL COMMENT '备用参数1',\n  `param2` varchar(100) DEFAULT NULL COMMENT '备用参数2',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用诊断记录';\n\n--\n-- Table structure for table `instance_alert_configs`\n--\n\nDROP TABLE IF EXISTS `instance_alert_configs`;\nCREATE TABLE `instance_alert_configs` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `alert_config` varchar(255) NOT NULL COMMENT '报警配置',\n  `alert_value` varchar(512) NOT NULL COMMENT '报警阀值',\n  `config_info` varchar(255) NOT NULL COMMENT '配置说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:全局报警,2:实例报警',\n  `instance_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '0:全局配置，其他代表实例id',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `compare_type` tinyint(4) NOT NULL COMMENT '比较类型：1小于,2等于,3大于,4不等于',\n  `check_cycle` tinyint(4) NOT NULL COMMENT '1:一分钟,2:五分钟,3:半小时4:一个小时,5:一天',\n  `update_time` datetime NOT NULL COMMENT '报警配置更新时间',\n  `last_check_time` datetime NOT NULL COMMENT '上次检查时间',\n  `important_level` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '重要程度（0：一般；1：重要；2：紧急）',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`type`,`instance_id`,`alert_config`,`compare_type`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例报警阀值配置';\n\n-- ----------------------------\n--  Records of `instance_alert_configs`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_alert_configs` VALUES ('9', 'aof_current_size', '6000', 'aof当前尺寸(单位：MB)', '1', '0', '1', '3', '3', '2017-06-19 09:43:22', '2020-09-17 10:52:00', 0), ('10', 'aof_delayed_fsync', '3', '分钟aof阻塞个数', '1', '0', '1', '3', '1', '2017-06-19 10:38:19', '2020-09-17 11:09:00', 1), ('11', 'client_biggest_input_buf', '10', '输入缓冲区最大buffer大小(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 10:47:03', '2020-09-17 11:09:00', 1), ('12', 'client_longest_output_list', '50000', '输出缓冲区最大队列长度', '1', '0', '1', '3', '1', '2017-06-19 10:55:45', '2020-09-17 11:09:00', 1), ('13', 'instantaneous_ops_per_sec', '60000', '实时ops', '1', '0', '1', '3', '1', '2017-06-19 11:02:38', '2020-09-17 11:09:00', 1),('14', 'latest_fork_usec', '400000', '上次fork所用时间(单位：微秒)', '1', '0', '1', '3', '5', '2017-06-19 11:21:35', '2020-09-16 16:51:00', 1), ('15', 'mem_fragmentation_ratio', '1.5', '内存碎片率(检测大于500MB)', '1', '0', '1', '3', '5', '2017-06-19 12:49:16', '2020-09-16 16:51:00', 0), ('16', 'rdb_last_bgsave_status', 'ok', '上一次bgsave状态', '1', '0', '1', '4', '4', '2017-06-19 14:15:21', '2020-09-17 10:19:00', 0), ('17', 'total_net_output_bytes', '5000', '分钟网络输出流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:39:44', '2020-09-17 11:09:00', 0), ('19', 'total_net_input_bytes', '1200', '分钟网络输入流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:45:44', '2020-09-17 11:09:00', 0), ('20', 'sync_partial_err', '0', '分钟部分复制失败次数', '1', '0', '1', '3', '1', '2017-06-19 18:34:41', '2020-09-17 11:09:00', 1), ('21', 'sync_partial_ok', '0', '分钟部分复制成功次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:01', '2020-09-17 11:09:00', 1), ('22', 'sync_full', '0', '分钟全量复制执行次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:17', '2020-09-17 11:09:00', 1), ('23', 'rejected_connections', '0', '分钟拒绝连接数', '1', '0', '1', '3', '1', '2017-06-19 18:35:36', '2020-09-17 11:09:00', 2), ('54', 'master_slave_offset_diff', '20000000', '主从节点偏移量差(单位：字节)', '1', '0', '1', '3', '2', '2017-06-20 18:58:56', '2020-09-17 11:06:00', 0), ('56', 'cluster_state', 'ok', '集群状态', '1', '0', '1', '4', '1', '2017-06-21 18:01:52', '2020-09-17 11:09:00', 2), ('57', 'cluster_slots_ok', '16384', '集群成功分配槽个数', '1', '0', '1', '4', '1', '2017-06-21 18:02:04', '2020-09-17 11:09:00', 2);\nCOMMIT;\n\n--\n-- Table structure for table `instance_big_key`\n--\n\nDROP TABLE IF EXISTS `instance_big_key`;\nCREATE TABLE `instance_big_key` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `audit_id` bigint(20) NOT NULL COMMENT 'audit id',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从，详见InstanceRoleEnum',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `big_key` varchar(255) NOT NULL COMMENT '键',\n  `type` varchar(16) NOT NULL COMMENT '类型:string,hash,list,set,zset',\n  `length` int(11) NOT NULL COMMENT '长度',\n  `create_time` datetime NOT NULL COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_audit` (`app_id`,`audit_id`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例bigkey列表';\n\n--\n-- Table structure for table `instance_config`\n--\n\nDROP TABLE IF EXISTS `instance_config`;\nCREATE TABLE `instance_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '配置名',\n  `config_value` varchar(512) NOT NULL COMMENT '配置值',\n  `info` varchar(512) NOT NULL COMMENT '配置说明',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n  `status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n  `version_id` int(11) NOT NULL COMMENT 'Redis版本表主键id',\n  `refresh` mediumint(9) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例配置模板';\n\n-- ----------------------------\n--  Records of `instance_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_config` VALUES ('1', 'cluster-enabled', 'yes', '是否开启集群模式', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('2', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('3', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('4', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('5', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('6', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2016-07-05 15:08:31', '2', '1', '29', '0'), ('7', 'port', '%d', 'sentinel实例端口', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('8', 'dir', '%s', '工作目录', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('9', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('10', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('11', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('12', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('13', 'daemonize', 'no', '是否守护进程', '2016-07-14 14:00:05', '6', '1', '29', '0'), ('14', 'tcp-backlog', '511', 'TCP连接完成队列', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('15', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('16', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2016-12-06 11:40:46', '6', '1', '29', '0'), ('17', 'loglevel', 'notice', '日志级别', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('18', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('19', 'dir', '%s', 'redis工作目录', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('20', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('21', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('22', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('23', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('24', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('25', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('26', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('27', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('28', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('29', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('30', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('31', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('32', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('33', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('34', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('35', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('36', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('37', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('38', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('39', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('40', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('41', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2016-11-24 10:24:21', '6', '1', '29', '0'), ('42', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('43', 'hz', '10', '执行后台task数量,默认:10', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('44', 'port', '%d', '端口', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('45', 'maxmemory', '%dmb', '当前实例最大可用内存', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('46', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('47', 'appendonly', 'yes', '开启append only持久化模式', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('48', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('49', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('50', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('51', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('52', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('53', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('54', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('55', 'maxclients', '10000', '客户端最大连接数', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('126', 'cluster-enabled', 'yes', '是否开启集群模式', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('127', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('128', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('129', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('130', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('131', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('132', 'port', '%d', 'sentinel实例端口', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('133', 'dir', '%s', '工作目录', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('134', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('135', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('136', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('137', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('138', 'daemonize', 'no', '是否守护进程', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('139', 'tcp-backlog', '511', 'TCP连接完成队列', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('140', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('141', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('142', 'loglevel', 'notice', '日志级别', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('143', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('144', 'dir', '%s', 'redis工作目录', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('145', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('146', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('147', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('148', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('149', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('150', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('151', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('152', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('153', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('154', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('155', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('156', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('157', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('158', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('159', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2018-09-18 18:25:32', '6', '0', '31', '0'), ('160', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2018-09-18 18:25:40', '6', '0', '31', '0'), ('161', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('162', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('163', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('164', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('165', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('166', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('167', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('168', 'hz', '10', '执行后台task数量,默认:10', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('169', 'port', '%d', '端口', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('170', 'maxmemory', '%dmb', '当前实例最大可用内存', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('171', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('172', 'appendonly', 'yes', '开启append only持久化模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('173', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('174', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('175', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('176', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('177', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('178', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('179', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('180', 'maxclients', '10000', '客户端最大连接数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('181', 'protected-mode', 'yes', '开启保护模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('182', 'bind', '0.0.0.0', '默认客户端都可连接', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('185', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2018-09-18 18:26:32', '6', '1', '31', '0'), ('186', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2018-09-18 18:27:12', '6', '1', '31', '0'), ('253', 'protected-mode', 'no', '关闭保护模式', '2018-11-01 16:10:59', '5', '1', '31', '0'), ('354', 'cluster-enabled', 'yes', '是否开启集群模式', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('355', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('356', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('357', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('358', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('359', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('360', 'port', '%d', 'sentinel实例端口', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('361', 'dir', '%s', '工作目录', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('362', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('363', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('364', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('365', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('366', 'daemonize', 'no', '是否守护进程', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('367', 'tcp-backlog', '511', 'TCP连接完成队列', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('368', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('369', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('370', 'loglevel', 'notice', '日志级别', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('371', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('372', 'dir', '%s', 'redis工作目录', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('373', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('374', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('375', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('376', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('377', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('378', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('379', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('380', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('381', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('382', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('383', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('384', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('385', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('386', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('387', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('388', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('389', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('390', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('391', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('392', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('393', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('394', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('395', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('396', 'hz', '10', '执行后台task数量,默认:10', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('397', 'port', '%d', '端口', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('398', 'maxmemory', '%dmb', '当前实例最大可用内存', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('399', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('400', 'appendonly', 'yes', '开启append only持久化模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('401', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('402', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('403', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('404', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('405', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('406', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('407', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('408', 'maxclients', '10000', '客户端最大连接数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('409', 'protected-mode', 'yes', '开启保护模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('410', 'bind', '0.0.0.0', '默认客户端都可连接', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('411', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('412', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('413', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('414', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('415', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('416', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('417', 'slave-lazy-flush', 'yes', 'slave发起全量复制,是否采用flushall async清理老数据 默认值 no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('418', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2019-10-31 11:15:57', '6', '1', '12', '0'), ('419', 'protected-mode', 'no', '关闭sentinel保护模式', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('420', 'activedefrag', 'no', '碎片整理开启', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('421', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('422', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('423', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('424', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('425', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('506', 'cluster-enabled', 'yes', '是否开启集群模式', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('507', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('508', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('509', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('510', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('511', 'port', '%d', 'sentinel实例端口', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('512', 'dir', '%s', '工作目录', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('513', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('514', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('515', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('516', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('517', 'daemonize', 'no', '是否守护进程', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('518', 'tcp-backlog', '511', 'TCP连接完成队列', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('519', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('520', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('521', 'loglevel', 'notice', '日志级别', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('522', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('523', 'dir', '%s', 'redis工作目录', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('524', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('525', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('526', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('527', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('528', 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('529', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('530', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('531', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('532', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('533', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('534', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('535', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('536', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('537', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('538', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('539', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('540', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('541', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('542', 'hz', '10', '执行后台task数量,默认:10', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('543', 'port', '%d', '端口', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('544', 'maxmemory', '%dmb', '当前实例最大可用内存', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('545', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('546', 'appendonly', 'yes', '开启append only持久化模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('547', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('548', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('549', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('550', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('551', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('552', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('553', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('554', 'maxclients', '10000', '客户端最大连接数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('555', 'protected-mode', 'yes', '开启保护模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('556', 'bind', '0.0.0.0', '默认客户端都可连接', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('557', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('558', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('559', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('560', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('561', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('562', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('563', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('564', 'protected-mode', 'no', '关闭sentinel保护模式', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('565', 'activedefrag', 'yes', '碎片整理开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('566', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('567', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('568', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('569', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('570', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('571', 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('572', 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('573', 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('574', 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('575', 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('576', 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('577', 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('578', 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('579', 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('580', 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('581', 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('582', 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('583', 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('585', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:45:22', '6', '1', '37', '0'), ('587', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:18', '6', '1', '12', '0'), ('589', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:49', '6', '1', '31', '0'), ('590', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:49:47', '6', '1', '29', '0');\nCOMMIT;\n\n\n--\n-- Table structure for table `instance_fault`\n--\n\nDROP TABLE IF EXISTS `instance_fault`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `instance_fault` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例id',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0:心跳停止,1:心跳恢复',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `type` mediumint(4) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `reason` mediumtext NOT NULL COMMENT '故障原因描述',\n  PRIMARY KEY (`id`),\n  KEY `idx_ip_port` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `inst_id` (`inst_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8927 DEFAULT CHARSET=utf8 COMMENT='实例故障表' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `instance_host`\n--\n\nDROP TABLE IF EXISTS `instance_host`;\nCREATE TABLE `instance_host` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `ssh_user` varchar(32) DEFAULT NULL COMMENT 'ssh用户',\n  `ssh_pwd` varchar(32) DEFAULT NULL COMMENT 'ssh密码',\n  `warn` int(5) DEFAULT '1' COMMENT '0不报警，1报警',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_host_ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_info`\n--\n\nDROP TABLE IF EXISTS `instance_info`;\nCREATE TABLE `instance_info` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'memcached instance id',\n  `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '对等实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id，与app_desc关联',\n  `host_id` bigint(20) NOT NULL COMMENT '对应的主机id，与instance_host关联',\n  `ip` varchar(16) NOT NULL COMMENT '实例的ip',\n  `port` int(11) NOT NULL COMMENT '实例端口',\n  `status` tinyint(4) NOT NULL COMMENT '是否启用:0:节点异常,1:正常启用,2:节点下线',\n  `mem` int(11) NOT NULL COMMENT '内存大小',\n  `conn` int(11) NOT NULL COMMENT '连接数',\n  `cmd` varchar(255) NOT NULL COMMENT '启动实例的命令/redis-sentinel的masterName',\n  `type` mediumint(11) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_inst_ipport` (`ip`,`port`) USING BTREE,\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_latency_history`\n--\n\nDROP TABLE IF EXISTS `instance_latency_history`;\nCREATE TABLE `instance_latency_history` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `event` varchar(255) NOT NULL COMMENT '事件名称',\n  `execute_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `execution_cost` bigint(20) NOT NULL COMMENT '耗时(微妙)',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `latencyistorykey` (`instance_id`,`event`,`execute_date`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executedate` (`app_id`,`execute_date`),\n  KEY `idx_executedate` (`execute_date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例延迟事件信息表';\n\n--\n-- Table structure for table `instance_minute_stats`\n--\n\nDROP TABLE IF EXISTS `instance_minute_stats`;\nCREATE TABLE `instance_minute_stats` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `json` text NOT NULL COMMENT '统计json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_collect_time` (`collect_time`),\n  KEY `idx_created_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例分钟统计表';\n\n--\n-- Table structure for table `instance_reshard_process`\n--\n\nDROP TABLE IF EXISTS `instance_reshard_process`;\nCREATE TABLE `instance_reshard_process` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `audit_id` bigint(20) NOT NULL COMMENT '审核id',\n  `source_instance_id` int(11) NOT NULL COMMENT '源实例id',\n  `target_instance_id` int(11) NOT NULL COMMENT '目标实例id',\n  `start_slot` int(11) NOT NULL COMMENT '开始slot',\n  `end_slot` int(11) NOT NULL COMMENT '结束slot',\n  `migrating_slot` int(11) NOT NULL COMMENT '正在迁移的slot',\n  `is_pipeline` tinyint(4) NOT NULL COMMENT '是否为pipeline,0:否,1:是',\n  `finish_slot_num` int(11) NOT NULL COMMENT '已经完成迁移的slot数量',\n  `status` tinyint(4) NOT NULL COMMENT '0:运行中 1:完成 2:出错',\n  `start_time` datetime NOT NULL COMMENT '迁移开始时间',\n  `end_time` datetime NOT NULL COMMENT '迁移结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit` (`audit_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例Reshard进度';\n\n--\n-- Table structure for table `instance_slow_log`\n--\n\nDROP TABLE IF EXISTS `instance_slow_log`;\nCREATE TABLE `instance_slow_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `slow_log_id` bigint(20) NOT NULL COMMENT '慢查询id',\n  `cost_time` int(11) NOT NULL COMMENT '耗时(微妙)',\n  `command` varchar(255) NOT NULL COMMENT '执行命令',\n  `execute_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `slowlogkey` (`instance_id`,`slow_log_id`,`execute_time`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executetime` (`app_id`,`execute_time`),\n  KEY `idx_executetime` (`execute_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例慢查询列表';\n\n--\n-- Table structure for table `instance_statistics`\n--\n\nDROP TABLE IF EXISTS `instance_statistics`;\nCREATE TABLE `instance_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `host_id` bigint(20) NOT NULL COMMENT '机器的id',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `port` int(255) NOT NULL COMMENT 'port',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从',\n  `max_memory` bigint(255) NOT NULL COMMENT '预分配内存，单位byte',\n  `used_memory` bigint(255) NOT NULL COMMENT '已使用内存，单位byte',\n  `curr_items` bigint(255) NOT NULL COMMENT '当前item数量',\n  `curr_connections` int(255) NOT NULL COMMENT '当前连接数',\n  `misses` bigint(255) NOT NULL COMMENT 'miss数',\n  `hits` bigint(255) NOT NULL COMMENT '命中数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `mem_fragmentation_ratio` double DEFAULT '0' COMMENT '碎片率',\n  `aof_delayed_fsync` int(11) DEFAULT '0' COMMENT 'aof阻塞次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `machine_id` (`host_id`),\n  KEY `idx_inst_id` (`inst_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='实例的最新统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_info`\n--\n\nDROP TABLE IF EXISTS `machine_info`;\nCREATE TABLE `machine_info` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '机器的id',\n  `ssh_user` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh用户',\n  `ssh_passwd` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh密码',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `room` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '所属机房',\n  `mem` int(11) unsigned NOT NULL COMMENT '内存大小，单位G',\n  `cpu` mediumint(24) unsigned NOT NULL COMMENT 'cpu数量',\n  `virtual` tinyint(8) unsigned NOT NULL DEFAULT '1' COMMENT '是否虚拟，0表示否，1表示是',\n  `real_ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT '宿主机ip',\n  `service_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上线时间',\n  `fault_count` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '故障次数',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n  `warn` tinyint(255) unsigned NOT NULL DEFAULT '1' COMMENT '是否启用报警，0不启用，1启用',\n  `available` tinyint(255) NOT NULL COMMENT '表示机器是否可用，1表示可用，0表示不可用；',\n  `groupId` int(11) NOT NULL DEFAULT '0' COMMENT '机器分组，默认为0，表示原生资源，非0表示外部提供的资源(可扩展)',\n  `type` int(11) NOT NULL DEFAULT '0' COMMENT '0原生 1 其他',\n  `extra_desc` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '对于机器的额外说明(例如机器安装的其他服务(web,mysql,queue等等))',\n  `collect` int(11) DEFAULT '1' COMMENT 'switch of collect server status, 1:open, 0:close',\n  `version_install` varchar(512) COLLATE utf8_bin DEFAULT NULL COMMENT '机器安装redis版本状态',\n  `use_type` tinyint(4) DEFAULT '2' COMMENT '使用类型：Redis专用机器(0)，Redis测试机器(1)，混合部署机器(2)，Redis-Sentinel机器(3)',\n  `k8s_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否k8s容器：0:不是 1:是',\n  `rack` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '机器所在机架信息',\n  `is_allocating` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否在分配中,1是0否',\n  `disk` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '磁盘空间:G',\n  `dis_type` tinyint(4) DEFAULT 0 NOT NULL COMMENT '操作系统发行版本，0:centos;1:ubuntu',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='机器信息表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_relation`\n--\n\nDROP TABLE IF EXISTS `machine_relation`;\nCREATE TABLE `machine_relation` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',\n  `ip` varchar(64) NOT NULL COMMENT '虚拟机ip',\n  `real_ip` varchar(64) NOT NULL COMMENT '宿主机ip',\n  `extra_desc` varchar(128) DEFAULT NULL COMMENT '实例描述信息',\n  `status` int(255) NOT NULL COMMENT '实例变更状态 0:offline ,1:online',\n  `is_sync` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据同步状态 0: 未同步数据  -1:同步中 1:数据已同步 -2:同步失败 ',\n  `sync_time` timestamp NULL DEFAULT NULL COMMENT '同步时间',\n  `update_time` timestamp NULL DEFAULT NULL COMMENT 'pod最后更新时间',\n  `taskid` bigint(11) DEFAULT NULL COMMENT '任务id',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `machine_room`\n--\n\nDROP TABLE IF EXISTS `machine_room`;\nCREATE TABLE `machine_room` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '机房id',\n  `name` varchar(255) NOT NULL COMMENT '机房名称',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `desc` varchar(255) DEFAULT NULL COMMENT '机房描述信息',\n  `ip_network` varchar(32) NOT NULL DEFAULT '' COMMENT '机房网段信息',\n  `operator` varchar(255) DEFAULT NULL COMMENT '运营商',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `machine_room`\n-- ----------------------------\nBEGIN;\nINSERT INTO `machine_room` VALUES ('1', '阿里云杭州', '1', '阿里云-杭州机房', '172.27.*.*', '阿里云');\nCOMMIT;\n\n--\n-- Table structure for table `machine_statistics`\n--\n\nDROP TABLE IF EXISTS `machine_statistics`;\nCREATE TABLE `machine_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `host_id` bigint(20) NOT NULL COMMENT '机器id',\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `cpu_usage` varchar(120) NOT NULL COMMENT 'cpu使用率',\n  `load` varchar(120) NOT NULL COMMENT '机器负载',\n  `traffic` varchar(120) NOT NULL COMMENT 'io网络流量',\n  `memory_usage_ratio` varchar(120) NOT NULL COMMENT '内存使用率',\n  `memory_free` varchar(120) NOT NULL COMMENT '内存剩余',\n  `memory_total` varchar(120) NOT NULL COMMENT '总内存量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  `max_memory` int(11) DEFAULT '0' COMMENT '机器分配内存,单位MB',\n  `instance_count` int(11) DEFAULT '0' COMMENT '机器实例数量',\n  `machine_memory` int(11) DEFAULT '0' COMMENT '机器入库总内存,单位MB',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_ip` (`ip`),\n  KEY `host_id` (`host_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器状态统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_blob_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_blob_triggers`;\nCREATE TABLE `qrtz_blob_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `BLOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_calendars`\n--\n\nDROP TABLE IF EXISTS `qrtz_calendars`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `qrtz_calendars` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `CALENDAR_NAME` varchar(200) NOT NULL COMMENT 'calendar名称',\n  `CALENDAR` blob NOT NULL COMMENT 'calendar信息',\n  PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='以 Blob 类型存储 Quartz 的 Calendar 信息' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `qrtz_cron_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_cron_triggers`;\nCREATE TABLE `qrtz_cron_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `TRIGGER_NAME` varchar(200) NOT NULL COMMENT 'trigger名',\n  `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT 'trigger组',\n  `CRON_EXPRESSION` varchar(120) NOT NULL COMMENT 'cron表达式',\n  `TIME_ZONE_ID` varchar(80) DEFAULT NULL COMMENT '时区',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储 Cron Trigger，包括 Cron 表达式和时区信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_fired_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_fired_triggers`;\nCREATE TABLE `qrtz_fired_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `ENTRY_ID` varchar(195) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL,\n  `FIRED_TIME` bigint(13) NOT NULL,\n  `SCHED_TIME` bigint(13) NOT NULL,\n  `PRIORITY` int(11) NOT NULL,\n  `STATE` varchar(16) NOT NULL,\n  `JOB_NAME` varchar(200) DEFAULT NULL,\n  `JOB_GROUP` varchar(200) DEFAULT NULL,\n  `IS_NONCONCURRENT` varchar(1) DEFAULT NULL COMMENT '是否非并行执行',\n  `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL COMMENT '是否持久化',\n  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),\n  KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),\n  KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已触发的 Trigger相关的状态信息，以及关联 Job 的执行信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_job_details`\n--\n\nDROP TABLE IF EXISTS `qrtz_job_details`;\nCREATE TABLE `qrtz_job_details` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `JOB_CLASS_NAME` varchar(250) NOT NULL,\n  `IS_DURABLE` varchar(1) NOT NULL COMMENT '是否持久化，0不持久化，1持久化',\n  `IS_NONCONCURRENT` varchar(1) NOT NULL COMMENT '是否非并发，0非并发，1并发',\n  `IS_UPDATE_DATA` varchar(1) NOT NULL,\n  `REQUESTS_RECOVERY` varchar(1) NOT NULL COMMENT '是否可恢复，0不恢复，1恢复',\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储每一个已配置的 Job 的详细信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_locks`\n--\n\nDROP TABLE IF EXISTS `qrtz_locks`;\nCREATE TABLE `qrtz_locks` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `LOCK_NAME` varchar(40) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储程序的悲观锁的信息(假如使用了悲观锁)' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_paused_trigger_grps`\n--\n\nDROP TABLE IF EXISTS `qrtz_paused_trigger_grps`;\nCREATE TABLE `qrtz_paused_trigger_grps` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已暂停的 Trigger 组的信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_scheduler_state`\n--\n\nDROP TABLE IF EXISTS `qrtz_scheduler_state`;\nCREATE TABLE `qrtz_scheduler_state` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL COMMENT '执行quartz实例的主机名',\n  `LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '实例将状态报告给集群中的其它实例的上一次时间',\n  `CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '实例间状态报告的时间频率',\n  PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储少量的有关 Scheduler 的状态信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_simple_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_simple_triggers`;\nCREATE TABLE `qrtz_simple_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复次数',\n  `REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复间隔',\n  `TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '已出发次数',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储简单的 Trigger，包括重复次数，间隔，以及已触的次数' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_simprop_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_simprop_triggers`;\nCREATE TABLE `qrtz_simprop_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `STR_PROP_1` varchar(512) DEFAULT NULL,\n  `STR_PROP_2` varchar(512) DEFAULT NULL,\n  `STR_PROP_3` varchar(512) DEFAULT NULL,\n  `INT_PROP_1` int(11) DEFAULT NULL,\n  `INT_PROP_2` int(11) DEFAULT NULL,\n  `LONG_PROP_1` bigint(20) DEFAULT NULL,\n  `LONG_PROP_2` bigint(20) DEFAULT NULL,\n  `DEC_PROP_1` decimal(13,4) DEFAULT NULL,\n  `DEC_PROP_2` decimal(13,4) DEFAULT NULL,\n  `BOOL_PROP_1` varchar(1) DEFAULT NULL,\n  `BOOL_PROP_2` varchar(1) DEFAULT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `qrtz_triggers`\n--\n\nDROP TABLE IF EXISTS `qrtz_triggers`;\nCREATE TABLE `qrtz_triggers` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PRIORITY` int(11) DEFAULT NULL,\n  `TRIGGER_STATE` varchar(16) NOT NULL,\n  `TRIGGER_TYPE` varchar(8) NOT NULL,\n  `START_TIME` bigint(13) NOT NULL,\n  `END_TIME` bigint(13) DEFAULT NULL,\n  `CALENDAR_NAME` varchar(200) DEFAULT NULL,\n  `MISFIRE_INSTR` smallint(2) DEFAULT NULL,\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),\n  KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已配置的 Trigger 的信息' /* `compression`='tokudb_zlib' */;\n\n\n--\n-- Table structure for table `server`\n--\n\nDROP TABLE IF EXISTS `server`;\nCREATE TABLE `server` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `host` varchar(255) DEFAULT NULL COMMENT 'host',\n  `nmon` varchar(255) DEFAULT NULL COMMENT 'nmon version',\n  `cpus` tinyint(4) DEFAULT NULL COMMENT 'logic cpu num',\n  `cpu_model` varchar(255) DEFAULT NULL COMMENT 'cpu 型号',\n  `dist` varchar(255) DEFAULT NULL COMMENT '发行版信息',\n  `kernel` varchar(255) DEFAULT NULL COMMENT '内核信息',\n  `ulimit` varchar(255) DEFAULT NULL COMMENT 'ulimit -n,ulimit -u',\n  `updatetime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ip`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `server_stat`\n--\n\nDROP TABLE IF EXISTS `server_stat`;\nCREATE TABLE `server_stat` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `cdate` date NOT NULL COMMENT '数据收集天',\n  `ctime` char(4) NOT NULL COMMENT '数据收集小时分钟',\n  `cuser` float DEFAULT NULL COMMENT '用户态占比',\n  `csys` float DEFAULT NULL COMMENT '内核态占比',\n  `cwio` float DEFAULT NULL COMMENT 'wio占比',\n  `c_ext` text COMMENT '子cpu占比',\n  `cload1` float DEFAULT NULL COMMENT '1分钟load',\n  `cload5` float DEFAULT NULL COMMENT '5分钟load',\n  `cload15` float DEFAULT NULL COMMENT '15分钟load',\n  `mtotal` float DEFAULT NULL COMMENT '总内存,单位M',\n  `mfree` float DEFAULT NULL COMMENT '空闲内存',\n  `mcache` float DEFAULT NULL COMMENT 'cache',\n  `mbuffer` float DEFAULT NULL COMMENT 'buffer',\n  `mswap` float DEFAULT NULL COMMENT 'cache',\n  `mswap_free` float DEFAULT NULL COMMENT 'cache',\n  `nin` float DEFAULT NULL COMMENT '网络入流量 单位K/s',\n  `nout` float DEFAULT NULL COMMENT '网络出流量 单位k/s',\n  `nin_ext` text COMMENT '各网卡入流量详情',\n  `nout_ext` text COMMENT '各网卡出流量详情',\n  `tuse` int(11) DEFAULT NULL COMMENT 'tcp estab连接数',\n  `torphan` int(11) DEFAULT NULL COMMENT 'tcp orphan连接数',\n  `twait` int(11) DEFAULT NULL COMMENT 'tcp time wait连接数',\n  `dread` float DEFAULT NULL COMMENT '磁盘读速率 单位K/s',\n  `dwrite` float DEFAULT NULL COMMENT '磁盘写速率 单位K/s',\n  `diops` float DEFAULT NULL COMMENT '磁盘io速率 交互次数/s',\n  `dbusy` float DEFAULT NULL COMMENT '磁盘io带宽使用百分比',\n  `d_ext` text COMMENT '磁盘各分区占比',\n  `dspace` text COMMENT '磁盘各分区空间使用率',\n  PRIMARY KEY (`ip`,`cdate`,`ctime`),\n  KEY `idx_cdate` (`cdate`),\n  KEY `idx_cdate_ctime` (`cdate`,`ctime`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `system_config`\n--\n\nDROP TABLE IF EXISTS `system_config`;\nCREATE TABLE `system_config` (\n  `config_key` varchar(255) NOT NULL COMMENT '配置key',\n  `config_value` varchar(512) NOT NULL COMMENT '配置value',\n  `info` varchar(255) NOT NULL COMMENT '配置说明',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `order_id` int(11) NOT NULL COMMENT '顺序',\n  PRIMARY KEY (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统配置';\n\n-- ----------------------------\n--  Records of `system_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_config` VALUES ('cachecloud.admin.user.name','admin','cachecloud-admin用户名',1,11),('cachecloud.admin.user.password','admin','cachelcoud-admin密码',1,12),('cachecloud.app.client.conn.threshold','2000','应用连接数报警阀值',1,33),('cachecloud.base.dir','/opt','cachecloud根目录，要和cachecloud-init.sh脚本中的目录一致',1,31),('cachecloud.contact','user1:(xx@zz.com, user1:135xxxxxxxx)<br/>user2: (user2@zz.com, user2:138xxxxxxxx)','值班联系人信息',1,14),('cachecloud.cookie.domain','','cookie登录方式所需要的域名',1,22),('cachecloud.email.alert.interface','','邮件报警接口(参考报警接口规范)',1,24),('cachecloud.machine.ssh.name','cachecloud-open','机器ssh用户名',1,2),('cachecloud.machine.ssh.password','cachecloud-open','机器ssh密码',1,3),('cachecloud.machine.ssh.port','22','机器ssh端口',1,10),('cachecloud.machine.stats.cron.minute','1','机器性能统计周期(分钟)',1,35),('cachecloud.nmon.dir','/opt/cachecloud','nmon安装目录',1,32),('cachecloud.owner.email','xx@sohu.com,yy@qq.com','邮件报警(逗号隔开)',1,21),('cachecloud.owner.phone','xxx,yyy','手机号报警(逗号隔开)',1,21),('cachecloud.owner.weChat','xxx,yyy','微信号报警(逗号隔开)',1,21),('cachecloud.public.key.pem','/opt/ssh/id_rsa','密钥路径',1,5),('cachecloud.public.user.name','cachecloud-open','公钥用户名',1,4),('cachecloud.ssh.auth.type','1','ssh授权方式',1,1),('cachecloud.superAdmin','admin,xx,yy','超级管理员组',1,13),('cachecloud.user.login.type','1','用户登录状态保存方式(session或cookie)',1,22),('cachecloud.weChat.alert.interface','','微信报警接口(参考报警接口规范)',1,23),('cachecloud.whether.schedule.clean.data','false','是否定期清理统计数据',1,34),('machine.load.alert.ratio','8.0','机器负载报警阀值',1,32);\nCOMMIT;\n\n-- ----------------------------\n--  Table structure for `system_resource`\n-- ----------------------------\nDROP TABLE IF EXISTS `system_resource`;\nCREATE TABLE `system_resource` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '资源ID',\n  `name` varchar(64) NOT NULL COMMENT '资源名称',\n  `intro` varchar(255) DEFAULT NULL COMMENT '资源说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:仓库地址 2:脚本 3:资源包 4:公钥/私钥 6:目录管理 7:迁移工具管理',\n  `lastmodify` datetime DEFAULT NULL COMMENT '最后更新时间',\n  `dir` varchar(128) DEFAULT NULL COMMENT '资源路径',\n  `url` varchar(128) DEFAULT NULL COMMENT '仓库地址',\n  `ispush` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:未推送 1:已推送',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `username` varchar(255) DEFAULT NULL COMMENT '最后修改人',\n  `task_id` bigint(11) DEFAULT NULL COMMENT '迁移任务id',\n  `compile_info` varchar(255) DEFAULT NULL COMMENT '编译信息',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `system_resource`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_resource` VALUES (1,'cachecloud-init.sh','容器初始化脚本',2,'2020-07-15 18:35:41','/script','',0,1,NULL,NULL,NULL),(2,'x.x.x.x',NULL,1,'2020-08-10 10:31:51','/opt/download/software/cachecloud/resource','http://x.x.x.x/software/cachecloud/resource',0,1,'admin',0,NULL),(4,'cachecloud-env.sh','宿主环境脚本',2,'2020-07-15 18:36:28','/script','',0,1,NULL,NULL,NULL),(5,'id_rsa','私钥文件',4,'2020-07-07 10:45:39','/ssh','',0,1,NULL,NULL,NULL),(6,'id_rsa.pub','公钥文件',4,'2020-07-07 10:45:45','/ssh','',0,1,NULL,NULL,NULL),(12,'redis-4.0.14','redis 4.0.14资源包',3,'2020-08-10 09:52:41','/redis','http://download.redis.io/releases/redis-4.0.14.tar.gz',0,1,'admin',532,NULL),(21,'/script','脚本目录管理',6,'2020-08-10 10:51:34','',NULL,0,1,'admin',0,NULL),(28,'/ssh','ssh目录',6,'2020-07-20 17:55:03',NULL,NULL,0,1,'admin',0,NULL),(29,'redis-3.0.7','redis3.0.7 资源包',3,'2020-08-10 09:53:32','/redis','http://download.redis.io/releases/redis-3.0.7.tar.gz',0,1,'admin',529,NULL),(31,'redis-3.2.12','redis 3.2.12 资源包',3,'2020-08-10 15:08:21','/redis','http://download.redis.io/releases/redis-3.2.12.tar.gz',0,1,'admin',530,NULL),(32,'/redis','redis资源包管理',6,'2020-07-20 17:54:59',NULL,NULL,0,1,'admin',0,NULL),(33,'/tool','迁移工具资源包',6,'2020-07-20 17:54:53',NULL,NULL,0,1,'admin',0,NULL),(37,'redis-5.0.9','redis5.0.9 资源包',3,'2020-08-10 09:51:41','/redis','http://download.redis.io/releases/redis-5.0.9.tar.gz',0,1,'admin',533,NULL),(40,'redis-shake-2.0.3','redis 2.0.3\\n修复fix 5.0迁移类型问题',7,'2020-08-11 10:53:26','/tool','https://github.com/alibaba/RedisShake/releases/download/release-v2.0.3-20200724/redis-shake-v2.0.3.tar.gz',0,1,'admin',518,NULL);\nCOMMIT;\n\n--\n-- Table structure for table `task_queue`\n--\n\nDROP TABLE IF EXISTS `task_queue`;\nCREATE TABLE `task_queue` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `important_info` varchar(255) NOT NULL DEFAULT '' COMMENT '重要信息',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `param` longtext NOT NULL COMMENT '任务参数(json):随着任务变化',\n  `init_param` longtext NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2中断，3失败',\n  `parent_task_id` bigint(20) NOT NULL COMMENT '父任务id',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `priority` int(11) NOT NULL COMMENT '优先级',\n  `error_code` int(11) NOT NULL COMMENT '错误代码',\n  `error_msg` varchar(255) NOT NULL COMMENT '错误消息',\n  `task_note` varchar(255) NOT NULL COMMENT '备注',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务表';\n\n--\n-- Table structure for table `task_step_flow`\n--\n\nDROP TABLE IF EXISTS `task_step_flow`;\nCREATE TABLE `task_step_flow` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `task_id` bigint(20) NOT NULL COMMENT '任务id',\n  `child_task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '子任务id',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0未开始、1成功、2中断、3跳过、4失败',\n  `log` text COMMENT '日志',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_task_class_step` (`task_id`,`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤流表';\n\n--\n-- Table structure for table `task_step_meta`\n--\n\nDROP TABLE IF EXISTS `task_step_meta`;\nCREATE TABLE `task_step_meta` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `step_desc` varchar(255) NOT NULL COMMENT '步骤描述',\n  `ops_device` varchar(255) NOT NULL COMMENT '运维建议',\n  `timeout` int(11) NOT NULL COMMENT '超时时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_class_step` (`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤元数据表';\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n-- ----------------------------\n--  Table structure for `standard_statistics`\n-- ----------------------------\nDROP TABLE IF EXISTS `standard_statistics`;\nCREATE TABLE `standard_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `info_json` text NOT NULL COMMENT '收集的json数据',\n  `diff_json` text NOT NULL COMMENT '上一次收集差异的json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `cluster_info_json` varchar(20480) NOT NULL DEFAULT '' COMMENT '收集的cluster info json数据',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_create_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_alert_record`\n--\nDROP TABLE IF EXISTS `app_alert_record`;\nCREATE TABLE `app_alert_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `visible_type` int(1) NOT NULL COMMENT '可见类型（0：均可见；1：仅管理员可见；）',\n  `important_level` int(1) NOT NULL COMMENT '重要类型（0：一般；1：重要；2：紧急）',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `app_id` bigint(20) DEFAULT NULL COMMENT 'app id',\n  `instance_id` bigint(20) DEFAULT NULL COMMENT '实例id',\n  `ip` varchar(16) COLLATE utf8_bin DEFAULT NULL COMMENT '机器ip',\n  `port` int(10) DEFAULT NULL COMMENT '端口号',\n  `title` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '报警标题',\n  `content` varchar(500) COLLATE utf8_bin NOT NULL COMMENT '报警内容',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`),\n  KEY `ip` (`ip`),\n  KEY `idx_inst_id` (`instance_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='报警记录表';\n\n--\n-- Table structure for table `config_restart_record`\n--\nDROP TABLE IF EXISTS `config_restart_record`;\nCREATE TABLE `config_restart_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `app_name` varchar(36) NOT NULL COMMENT '应用名称',\n  `operate_type` char(1) NOT NULL COMMENT '操作类型（0:滚动重启，1:修改配置强制重启；2：修改配置）',\n  `param` varchar(2000) NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2成功，3失败，4配置修改待重启',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `log` longtext COMMENT '日志信息',\n  `user_name` varchar(64) DEFAULT NULL COMMENT '操作人员姓名',\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `instances` varchar(1000) DEFAULT NULL COMMENT '涉及实例id列表的json格式',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='重启记录表';\n\n--\n-- Table structure for table `module_info`\n--\nDROP TABLE IF EXISTS `module_info`;\nCREATE TABLE `module_info` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL,\n  `git_url` varchar(255) NOT NULL DEFAULT '' COMMENT 'git resource',\n  `info` varchar(128) DEFAULT NULL COMMENT '模块信息说明',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `NAMEKEY` (`name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Redis模块信息表';\n\n--\n-- Table structure for table `module_version`\n--\nDROP TABLE IF EXISTS `module_version`;\nCREATE TABLE `module_version` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `module_id` int(11) NOT NULL,\n  `version_id` int(11) NOT NULL COMMENT '关联版本号',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  `so_path` varchar(255) DEFAULT NULL COMMENT '编译后so库的地址',\n  `tag` varchar(64) NOT NULL COMMENT '模块版本号',\n  `status` int(255) NOT NULL DEFAULT '0' COMMENT '是否可用(关联so地址)：0 不可用 1：可用',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Redis模块版本管理表';\n\n--\n-- Table structure for table `app_import`\n--\nDROP TABLE IF EXISTS `app_import`;\nCREATE TABLE `app_import` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '目标应用id',\n  `instance_info` text COMMENT '源redis实例信息',\n  `redis_password` varchar(200) DEFAULT NULL COMMENT '源redis密码',\n  `status` int(11) DEFAULT NULL COMMENT '迁移状态：PREPARE(0, \"准备\", \"应用导入-未开始\"),     START(1, \"进行中...\", \"应用导入-开始\"),     ERROR(2, \"error\", \"应用导入-出错\"),     VERSION_BUILD_START(11, \"进行中...\", \"新建redis版本-进行中\"),     VERSION_BUILD_ERROR(12, \"error\", \"新建redis版本-出错\"),     VERSION_BUILD_END(20, \"成功\", \"新建redis版本-完成\"),     APP_BUILD_INIT(21, \"准备就绪\", \"新建redis应用-准备就绪\"),     APP_BUILD_START(22, \"进行中...\", \"新建redis应用-进行中\"),     APP_BUILD_ERROR(23, \"error\", \"新建redis应用-出错\"),     APP_BUILD_END(30, \"成功\", \"新建redis应用-完成\"),     MIGRATE_INIT(31, \"准备就绪\", \"数据迁移-准备就绪\"),     MIGRATE_START(32, \"进行中...\", \"数据迁移-进行中\"),     MIGRATE_ERROR(33, \"error\", \"数据迁移-出错\"),     MIGRATE_END(3, \"成功\", \"应用导入-成功\")',\n  `step` int(11) DEFAULT NULL COMMENT '导入阶段',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `migrate_id` bigint(20) DEFAULT NULL COMMENT '数据迁移id',\n  `mem_size` int(11) DEFAULT NULL COMMENT '目标应用内存大小，单位G',\n  `redis_version_name` varchar(20) DEFAULT NULL COMMENT '目标应用redis版本，格式：redis-x.x.x',\n  `app_build_task_id` bigint(20) DEFAULT NULL COMMENT '目标应用部署任务id',\n  `source_type` int(11) DEFAULT NULL COMMENT '源redis类型：7:cluster, 6:sentinel, 5:standalone',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- redis_module_config definition\n\nCREATE TABLE `redis_module_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '配置名',\n  `config_value` varchar(512) NOT NULL COMMENT '配置值',\n  `info` varchar(512) NOT NULL COMMENT '配置说明',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n  `status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n  `version_id` int(11) NOT NULL COMMENT 'Module version版本表主键id',\n  `refresh` tinyint(4) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n  `module_id` int(11) NOT NULL DEFAULT '7' COMMENT 'Module 信息表id',\n  `config_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '配置类型，0：加载和运行配置；1：加载时配置；2：运行时配置',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='redis模块配置表';\n\n\n-- app_to_module definition\n\nCREATE TABLE `app_to_module` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `module_id` int(11) NOT NULL COMMENT '模块info id',\n  `module_version_id` int(11) NOT NULL COMMENT '模块版本id',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_to_module_un` (`app_id`,`module_id`,`module_version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='应用与模块关系表';\n"
  },
  {
    "path": "cachecloud-web/sql/3.1.sql",
    "content": "-- MySQL dump 10.15  Distrib 10.0.16-MariaDB, for Linux (x86_64)\n--\n-- Host: localhost    Database: cachecloud_open\n-- ------------------------------------------------------\n-- Server version\t10.0.16-MariaDB-log\n\nSET NAMES utf8;\nSET FOREIGN_KEY_CHECKS = 0;\n\n--\n-- Table structure for table `app_audit`\n--\n\nDROP TABLE IF EXISTS `app_audit`;\nCREATE TABLE `app_audit` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人的id',\n  `user_name` varchar(64) NOT NULL COMMENT '用户名',\n  `type` tinyint(4) NOT NULL COMMENT '申请类型:0:申请应用,1:应用扩容,2:修改配置',\n  `param1` varchar(600) DEFAULT NULL COMMENT '预留参数1',\n  `param2` varchar(600) DEFAULT NULL COMMENT '预留参数2',\n  `param3` varchar(600) DEFAULT NULL COMMENT '预留参数3',\n  `info` varchar(360) NOT NULL COMMENT '申请描述',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:等待审批; 1:审批通过; -1:驳回',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `refuse_reason` varchar(360) DEFAULT NULL COMMENT '驳回理由',\n  `task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '任务id',\n  `operate_id` bigint(20) DEFAULT NULL COMMENT '工单处理人',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid` (`app_id`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_status_create_time` (`status`,`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用审核表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_audit_log`\n--\n\nDROP TABLE IF EXISTS `app_audit_log`;\nCREATE TABLE `app_audit_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '审批操作人id',\n  `info` longtext NOT NULL COMMENT 'app审批的详细信息',\n  `type` tinyint(4) NOT NULL,\n  `create_time` datetime NOT NULL,\n  `app_audit_id` bigint(20) NOT NULL COMMENT '审批id',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit_appid` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app审核日志表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_client_command_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_command_minute_statistics`;\nCREATE TABLE `app_client_command_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `command` varchar(20) NOT NULL COMMENT '命令明文',\n  `cost` bigint(20) DEFAULT NULL COMMENT '命令累计毫秒耗时',\n  `bytes_in` bigint(20) DEFAULT NULL COMMENT '输入流量',\n  `bytes_out` bigint(20) DEFAULT NULL COMMENT '输出流量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `count` int(11) DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__appid_client_command_currentMin` (`app_id`,`client_ip`,`command`,`current_min`),\n  KEY `idx_currentmin_appid_count_cost` (`current_min`,`app_id`,`count`,`cost`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟命令调用上报数据';\n\n--\n-- Table structure for table `app_client_exception_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_exception_minute_statistics`;\nCREATE TABLE `app_client_exception_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `type` tinyint(4) NOT NULL COMMENT '0:connect exception;1:command exception',\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `node` varchar(30) NOT NULL COMMENT '节点信息host:port',\n  `count` bigint(20) DEFAULT NULL COMMENT '累计连接失败次数',\n  `cost` bigint(20) DEFAULT NULL COMMENT '累计连接失败毫秒耗时',\n  `latency_commands` varchar(255) DEFAULT NULL COMMENT '统计命令topN id,逗号分隔',\n  `redis_pool_config` varchar(255) DEFAULT NULL COMMENT 'redis连接池配置信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__client_node_type_currentMin` (`client_ip`,`node`,`type`,`current_min`),\n  KEY `idx_appid_current_min` (`app_id`,`current_min`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟异常上报数据';\n\n--\n-- Table structure for table `app_client_latency_command`\n--\n\nDROP TABLE IF EXISTS `app_client_latency_command`;\nCREATE TABLE `app_client_latency_command` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `command` varchar(255) NOT NULL COMMENT '命令明文',\n  `size` bigint(20) DEFAULT NULL COMMENT '参数长度',\n  `args` varchar(255) DEFAULT NULL COMMENT '裁剪后参数明文',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `invoke_time` bigint(20) DEFAULT NULL COMMENT '命令调用时间戳',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端异常命令调用详情';\n\n--\n-- Table structure for table `app_client_statistic_gather`\n--\n\nDROP TABLE IF EXISTS `app_client_statistic_gather`;\nCREATE TABLE `app_client_statistic_gather` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gather_time` varchar(20) NOT NULL COMMENT '统计时间，格式yyyy-mm-dd',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `cmd_count` bigint(20) DEFAULT '0' COMMENT '命令调用次数',\n  `conn_exp_count` bigint(20) DEFAULT '0' COMMENT '连接异常次数',\n  `avg_cmd_cost` double DEFAULT '0' COMMENT '命令调用平均耗时，单位毫秒',\n  `avg_cmd_exp_cost` double DEFAULT '0' COMMENT '命令超时平均耗时，单位毫秒',\n  `avg_conn_exp_cost` double DEFAULT '0' COMMENT '连接异常平均耗时，单位毫秒',\n  `cmd_exp_count` bigint(20) DEFAULT '0' COMMENT '命令超时次数',\n  `instance_count` int(11) DEFAULT NULL COMMENT '应用实例数',\n  `avg_mem_frag_ratio` double DEFAULT NULL COMMENT '平均碎片率',\n  `mem_used_ratio` double DEFAULT NULL COMMENT '内存使用率',\n  `exception_count` bigint(20) DEFAULT '0' COMMENT '异常数（旧，待下线）',\n  `slow_log_count` bigint(20) DEFAULT '0' COMMENT '慢查询次数',\n  `latency_count` bigint(20) DEFAULT '0' COMMENT '延迟事件次数',\n  `object_size` bigint(20) DEFAULT '0' COMMENT '存储对象数',\n  `used_memory` bigint(20) DEFAULT '0' COMMENT '内存占用 byte',\n  `used_memory_rss` bigint(20) DEFAULT '0' COMMENT '物理内存占用 byte',\n  `max_cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗(单位:秒)',\n  `max_cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗(单位:秒)',\n  `connected_clients` bigint(20) DEFAULT '0' COMMENT '应用客户端连接数',\n  `topology_exam_result` tinyint(4) DEFAULT NULL COMMENT '拓扑诊断结果，0：正常，1：异常',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_appid_gathertime` (`app_id`,`gather_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报数据全天统计';\n\n--\n-- Table structure for table `app_client_value_minute_stat`\n--\n\nDROP TABLE IF EXISTS `app_client_value_minute_stat`;\nCREATE TABLE `app_client_value_minute_stat` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用appid',\n  `collect_time` bigint(20) NOT NULL COMMENT '数据收集时间yyyyMMddHHmm00',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `command` varchar(20) NOT NULL COMMENT '执行命令',\n  `distribute_type` tinyint(4) NOT NULL COMMENT '值分布',\n  `count` int(11) NOT NULL COMMENT '命令执行次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_collect_command_dis` (`app_id`,`collect_time`,`command`,`distribute_type`),\n  KEY `idx_collect_time` (`collect_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟值分布上报数据统计';\n\n--\n-- Table structure for table `app_client_version_statistic`\n--\n\nDROP TABLE IF EXISTS `app_client_version_statistic`;\nCREATE TABLE `app_client_version_statistic` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip地址',\n  `client_version` varchar(20) NOT NULL COMMENT '客户端版本',\n  `report_time` datetime DEFAULT NULL COMMENT '上报时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_client_ip` (`app_id`,`client_ip`),\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报版本信息统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_daily`\n--\n\nDROP TABLE IF EXISTS `app_daily`;\nCREATE TABLE `app_daily` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `date` date NOT NULL COMMENT '日期',\n  `create_time` datetime NOT NULL,\n  `slow_log_count` bigint(20) NOT NULL COMMENT '慢查询个数',\n  `client_exception_count` bigint(20) NOT NULL COMMENT '客户端异常个数',\n  `max_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟最大客户端连接数',\n  `avg_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟平均客户端连接数',\n  `max_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟最大命令数',\n  `avg_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟平均命令数',\n  `avg_hit_ratio` double NOT NULL COMMENT '平均命中率',\n  `min_minute_hit_ratio` double NOT NULL COMMENT '每分钟最小命中率',\n  `max_minute_hit_ratio` double NOT NULL COMMENT '每分钟最大命中率',\n  `avg_used_memory` bigint(20) NOT NULL COMMENT '最大内存使用量',\n  `max_used_memory` bigint(20) NOT NULL COMMENT '平均内存使用量',\n  `expired_keys_count` bigint(20) NOT NULL COMMENT '过期键个数',\n  `evicted_keys_count` bigint(20) NOT NULL COMMENT '剔除键个数',\n  `avg_minute_net_input_byte` double NOT NULL COMMENT '每分钟平均网络input量',\n  `max_minute_net_input_byte` double NOT NULL COMMENT '每分钟最大网络input量',\n  `avg_minute_net_output_byte` double NOT NULL COMMENT '每分钟平均网络output量',\n  `max_minute_net_output_byte` double NOT NULL COMMENT '每分钟最大网络output量',\n  `avg_object_size` bigint(20) NOT NULL COMMENT '键个数平均值',\n  `max_object_size` bigint(20) NOT NULL COMMENT '键个数最大值',\n  `big_key_times` bigint(20) NOT NULL COMMENT 'bigkey次数',\n  `big_key_info` varchar(512) COLLATE utf8_bin NOT NULL COMMENT 'bigkey详情',\n  `client_cmd_count` bigint(20) NOT NULL COMMENT '累计命令调用次数',\n  `client_avg_cmd_cost` double NOT NULL COMMENT '平均命令调用耗时',\n  `client_conn_exp_count` bigint(20) NOT NULL COMMENT '累计连接异常事件次数',\n  `client_avg_conn_exp_cost` double NOT NULL COMMENT '平均连接异常事件耗时',\n  `client_cmd_exp_count` bigint(20) NOT NULL COMMENT '累计命令超时事件次数',\n  `client_avg_cmd_exp_cost` double NOT NULL COMMENT '平均命令超时事件耗时',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid_date` (`app_id`,`date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='app日报';\n\n--\n-- Table structure for table `app_data_migrate_status`\n--\n\nDROP TABLE IF EXISTS `app_data_migrate_status`;\nCREATE TABLE `app_data_migrate_status` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `migrate_machine_ip` varchar(255) NOT NULL COMMENT '迁移工具所在机器ip',\n  `migrate_machine_port` int(11) NOT NULL COMMENT '迁移工具所占port',\n  `source_migrate_type` tinyint(4) NOT NULL COMMENT '源迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `source_servers` varchar(2048) NOT NULL COMMENT '源实例列表',\n  `target_migrate_type` tinyint(4) NOT NULL COMMENT '目标迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `target_servers` varchar(2048) NOT NULL COMMENT '目标实例列表',\n  `source_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '源应用id',\n  `target_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '目标应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '操作人',\n  `status` tinyint(4) NOT NULL COMMENT '迁移执行状态,0:开始,1:结束,2:异常',\n  `start_time` datetime NOT NULL COMMENT '迁移开始执行时间',\n  `end_time` datetime DEFAULT NULL COMMENT '迁移结束执行时间',\n  `log_path` varchar(255) NOT NULL COMMENT '日志文件路径',\n  `config_path` varchar(255) NOT NULL COMMENT '配置文件路径',\n  `migrate_id` varchar(50) DEFAULT NULL COMMENT 'migrate id',\n  `migrate_tool` tinyint(4) DEFAULT NULL COMMENT 'migrate_tool, 0:redis-shake,1:redis-migrate-tool',\n  `redis_source_version` varchar(20) DEFAULT NULL,\n  `redis_target_version` varchar(20) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用迁移记录详情';\n\n--\n-- Table structure for table `app_desc`\n--\n\nDROP TABLE IF EXISTS `app_desc`;\nCREATE TABLE `app_desc` (\n  `app_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '应用id',\n  `name` varchar(36) NOT NULL COMMENT '应用名',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人id',\n  `status` tinyint(4) NOT NULL COMMENT '应用状态, 0未分配，1申请未审批，2审批并发布 3:应用下线',\n  `intro` varchar(255) NOT NULL COMMENT '应用描述',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `passed_time` datetime NOT NULL COMMENT '审批通过时间',\n  `type` int(10) NOT NULL DEFAULT '0' COMMENT 'cache类型，1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud ,5. redis-sentinel ,6.redis-standalone ',\n  `officer` varchar(32) NOT NULL COMMENT '负责人，中文',\n  `ver_id` int(11) NOT NULL COMMENT '版本',\n  `is_test` tinyint(4) DEFAULT '0' COMMENT '是否测试：1是0否',\n  `need_persistence` tinyint(4) DEFAULT '1' COMMENT '是否需要持久化: 1是0否',\n  `need_hot_back_up` tinyint(4) DEFAULT '1' COMMENT '是否需要热备: 1是0否',\n  `has_back_store` tinyint(4) DEFAULT '1' COMMENT '是否有后端数据源: 1是0否',\n  `forecase_qps` int(11) DEFAULT NULL COMMENT '预估qps',\n  `forecast_obj_num` int(11) DEFAULT NULL COMMENT '预估条目数',\n  `mem_alert_value` int(11) DEFAULT NULL COMMENT '内存报警阀值',\n  `client_machine_room` varchar(36) DEFAULT NULL COMMENT '客户端机房信息',\n  `client_conn_alert_value` int(11) DEFAULT '2000' COMMENT '客户端连接报警阀值',\n  `app_key` varchar(255) DEFAULT NULL COMMENT '应用秘钥',\n  `important_level` tinyint(4) NOT NULL DEFAULT '2' COMMENT '应用级别，1:最重要，2:一般重要，3:一般',\n  `password` varchar(255) DEFAULT '' COMMENT 'redis密码',\n  `custom_password` varchar(255) DEFAULT NULL COMMENT '自定义密码',\n  `hit_precent_alert_value` int(11) DEFAULT '0' COMMENT '命中率报警阀值 0:不报警 ',\n  `is_access_monitor` int(11) DEFAULT '0' COMMENT '是否接入全局监控报警 默认0,0:不接入监控 1:接入监控',\n  `app_fsync_value` int(11) DEFAULT '1' COMMENT '应用刷盘策略 1:主从节点appdendfsync=everysec 2:主从节点 appdendfsync=no',\n  `version_id` int(11) NOT NULL DEFAULT '1' COMMENT 'Redis版本表主键id',\n  PRIMARY KEY (`app_id`),\n  UNIQUE KEY `uidx_app_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app应用描述' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_command_statistics`;\nCREATE TABLE `app_hour_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHH',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`command_name`,`collect_time`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每小时命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_statistics`;\nCREATE TABLE `app_hour_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHH',\n  `hits` bigint(20) NOT NULL COMMENT '每小时命中数量和',\n  `misses` bigint(20) NOT NULL COMMENT '每小时未命中数量和',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '每小时内存占用最大值',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '每小时过期key数量和',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '每小时驱逐key数量和',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '每小时客户端连接数最大值',\n  `object_size` bigint(20) NOT NULL COMMENT '每小时存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '每小时参与累加实例数最小值',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '每小时修改时间最大值',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用统计数据每小时统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_command_statistics`;\nCREATE TABLE `app_minute_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHHmm',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`,`command_name`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每分钟命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_statistics`;\nCREATE TABLE `app_minute_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `hits` bigint(20) NOT NULL COMMENT '命中数量',\n  `misses` bigint(20) NOT NULL COMMENT '未命中数量',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '内存占用',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '过期key数量',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '驱逐key数量',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '客户端连接数',\n  `object_size` bigint(20) NOT NULL COMMENT '每分钟存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '参与累加实例数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_to_user`\n--\n\nDROP TABLE IF EXISTS `app_to_user`;\nCREATE TABLE `app_to_user` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_user`\n--\n\nDROP TABLE IF EXISTS `app_user`;\nCREATE TABLE `app_user` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL COMMENT '用户名',\n  `ch_name` varchar(255) NOT NULL COMMENT '中文名',\n  `email` varchar(64) NOT NULL COMMENT '邮箱',\n  `mobile` varchar(16) NOT NULL COMMENT '手机',\n  `type` int(4) NOT NULL DEFAULT '2' COMMENT '0管理员，1预留，2普通用户，-1无效',\n  `weChat` varchar(32) DEFAULT NULL COMMENT '微信号',\n  `isAlert` tinyint(4) NOT NULL DEFAULT '1' COMMENT '用户是否接收报警 0:不接收 1:接收',\n  `password` varchar(64) DEFAULT NULL COMMENT '密码',\n  `register_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',\n  `purpose` varchar(255) DEFAULT NULL COMMENT '使用目的',\n  `company` varchar(255) DEFAULT NULL COMMENT '公司名称',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_user_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表' /* `compression`='tokudb_zlib' */;\n\n-- ----------------------------\n--  Records of `app_user`\n-- ----------------------------\nBEGIN;\nINSERT INTO `app_user` VALUES ('1', 'admin', 'admin', 'admin@xxx.com', '13500000000', '0', null, '1', NULL, current_timestamp(), NULL, NULL);\nCOMMIT;\n\n--\n-- Table structure for table `brevity_schedule_resources`\n--\n\nDROP TABLE IF EXISTS `brevity_schedule_resources`;\nCREATE TABLE `brevity_schedule_resources` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `type` tinyint(4) NOT NULL COMMENT '类型,见:BrevityScheduleType',\n  `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '时间版本',\n  `host` varchar(16) NOT NULL COMMENT '资源ip',\n  `port` int(11) NOT NULL DEFAULT '0' COMMENT '端口',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_type_host_port` (`type`,`host`,`port`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='短频任务表';\n\n--\n-- Table structure for table `diagnostic_task_record`\n--\n\nDROP TABLE IF EXISTS `diagnostic_task_record`;\nCREATE TABLE `diagnostic_task_record` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `type` int(11) DEFAULT NULL COMMENT '诊断类型：0scan 1bigkey 2idle key 3hotkey 4del key 5slot analysis 6topology exam',\n  `task_id` bigint(20) DEFAULT NULL COMMENT '任务流id',\n  `audit_id` bigint(20) DEFAULT NULL COMMENT '审批id',\n  `status` int(11) DEFAULT NULL COMMENT '诊断状态：0开始 1结束 2异常',\n  `cost` bigint(20) DEFAULT NULL COMMENT '耗时，毫秒',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `redis_key` varchar(100) DEFAULT NULL COMMENT '结果的key',\n  `node` varchar(100) DEFAULT NULL COMMENT '实例，host:port',\n  `parent_task_id` bigint(20) DEFAULT NULL COMMENT '父任务id',\n  `diagnostic_condition` varchar(100) DEFAULT NULL COMMENT '诊断条件',\n  `param1` varchar(100) DEFAULT NULL COMMENT '备用参数1',\n  `param2` varchar(100) DEFAULT NULL COMMENT '备用参数2',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用诊断记录';\n\n--\n-- Table structure for table `instance_alert_configs`\n--\n\nDROP TABLE IF EXISTS `instance_alert_configs`;\nCREATE TABLE `instance_alert_configs` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `alert_config` varchar(255) NOT NULL COMMENT '报警配置',\n  `alert_value` varchar(512) NOT NULL COMMENT '报警阀值',\n  `config_info` varchar(255) NOT NULL COMMENT '配置说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:全局报警,2:实例报警',\n  `instance_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '0:全局配置，其他代表实例id',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `compare_type` tinyint(4) NOT NULL COMMENT '比较类型：1小于,2等于,3大于,4不等于',\n  `check_cycle` tinyint(4) NOT NULL COMMENT '1:一分钟,2:五分钟,3:半小时4:一个小时,5:一天',\n  `update_time` datetime NOT NULL COMMENT '报警配置更新时间',\n  `last_check_time` datetime NOT NULL COMMENT '上次检查时间',\n  `important_level` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '重要程度（0：一般；1：重要；2：紧急）',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`type`,`instance_id`,`alert_config`,`compare_type`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例报警阀值配置';\n\n-- ----------------------------\n--  Records of `instance_alert_configs`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_alert_configs` VALUES ('9', 'aof_current_size', '6000', 'aof当前尺寸(单位：MB)', '1', '0', '1', '3', '3', '2017-06-19 09:43:22', '2020-09-17 10:52:00', 0), ('10', 'aof_delayed_fsync', '3', '分钟aof阻塞个数', '1', '0', '1', '3', '1', '2017-06-19 10:38:19', '2020-09-17 11:09:00', 1), ('11', 'client_biggest_input_buf', '10', '输入缓冲区最大buffer大小(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 10:47:03', '2020-09-17 11:09:00', 1), ('12', 'client_longest_output_list', '50000', '输出缓冲区最大队列长度', '1', '0', '1', '3', '1', '2017-06-19 10:55:45', '2020-09-17 11:09:00', 1), ('13', 'instantaneous_ops_per_sec', '60000', '实时ops', '1', '0', '1', '3', '1', '2017-06-19 11:02:38', '2020-09-17 11:09:00', 1),('14', 'latest_fork_usec', '400000', '上次fork所用时间(单位：微秒)', '1', '0', '1', '3', '5', '2017-06-19 11:21:35', '2020-09-16 16:51:00', 1), ('15', 'mem_fragmentation_ratio', '1.5', '内存碎片率(检测大于500MB)', '1', '0', '1', '3', '5', '2017-06-19 12:49:16', '2020-09-16 16:51:00', 0), ('16', 'rdb_last_bgsave_status', 'ok', '上一次bgsave状态', '1', '0', '1', '4', '4', '2017-06-19 14:15:21', '2020-09-17 10:19:00', 0), ('17', 'total_net_output_bytes', '5000', '分钟网络输出流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:39:44', '2020-09-17 11:09:00', 0), ('19', 'total_net_input_bytes', '1200', '分钟网络输入流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:45:44', '2020-09-17 11:09:00', 0), ('20', 'sync_partial_err', '0', '分钟部分复制失败次数', '1', '0', '1', '3', '1', '2017-06-19 18:34:41', '2020-09-17 11:09:00', 1), ('21', 'sync_partial_ok', '0', '分钟部分复制成功次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:01', '2020-09-17 11:09:00', 1), ('22', 'sync_full', '0', '分钟全量复制执行次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:17', '2020-09-17 11:09:00', 1), ('23', 'rejected_connections', '0', '分钟拒绝连接数', '1', '0', '1', '3', '1', '2017-06-19 18:35:36', '2020-09-17 11:09:00', 2), ('54', 'master_slave_offset_diff', '20000000', '主从节点偏移量差(单位：字节)', '1', '0', '1', '3', '2', '2017-06-20 18:58:56', '2020-09-17 11:06:00', 0), ('56', 'cluster_state', 'ok', '集群状态', '1', '0', '1', '4', '1', '2017-06-21 18:01:52', '2020-09-17 11:09:00', 2), ('57', 'cluster_slots_ok', '16384', '集群成功分配槽个数', '1', '0', '1', '4', '1', '2017-06-21 18:02:04', '2020-09-17 11:09:00', 2);\nCOMMIT;\n\n--\n-- Table structure for table `instance_big_key`\n--\n\nDROP TABLE IF EXISTS `instance_big_key`;\nCREATE TABLE `instance_big_key` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `audit_id` bigint(20) NOT NULL COMMENT 'audit id',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从，详见InstanceRoleEnum',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `big_key` varchar(255) NOT NULL COMMENT '键',\n  `type` varchar(16) NOT NULL COMMENT '类型:string,hash,list,set,zset',\n  `length` int(11) NOT NULL COMMENT '长度',\n  `create_time` datetime NOT NULL COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_audit` (`app_id`,`audit_id`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例bigkey列表';\n\n--\n-- Table structure for table `instance_config`\n--\n\nDROP TABLE IF EXISTS `instance_config`;\nCREATE TABLE `instance_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '配置名',\n  `config_value` varchar(512) NOT NULL COMMENT '配置值',\n  `info` varchar(512) NOT NULL COMMENT '配置说明',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n  `status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n  `version_id` int(11) NOT NULL COMMENT 'Redis版本表主键id',\n  `refresh` mediumint(9) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例配置模板';\n\n-- ----------------------------\n--  Records of `instance_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_config` VALUES ('1', 'cluster-enabled', 'yes', '是否开启集群模式', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('2', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('3', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('4', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('5', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('6', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2016-07-05 15:08:31', '2', '1', '29', '0'), ('7', 'port', '%d', 'sentinel实例端口', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('8', 'dir', '%s', '工作目录', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('9', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('10', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('11', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('12', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('13', 'daemonize', 'no', '是否守护进程', '2016-07-14 14:00:05', '6', '1', '29', '0'), ('14', 'tcp-backlog', '511', 'TCP连接完成队列', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('15', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('16', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2016-12-06 11:40:46', '6', '1', '29', '0'), ('17', 'loglevel', 'notice', '日志级别', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('18', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('19', 'dir', '%s', 'redis工作目录', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('20', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('21', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('22', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('23', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('24', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('25', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('26', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('27', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('28', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('29', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('30', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('31', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('32', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('33', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('34', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('35', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('36', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('37', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('38', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('39', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('40', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('41', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2016-11-24 10:24:21', '6', '1', '29', '0'), ('42', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('43', 'hz', '10', '执行后台task数量,默认:10', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('44', 'port', '%d', '端口', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('45', 'maxmemory', '%dmb', '当前实例最大可用内存', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('46', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('47', 'appendonly', 'yes', '开启append only持久化模式', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('48', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('49', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('50', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('51', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('52', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('53', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('54', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('55', 'maxclients', '10000', '客户端最大连接数', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('126', 'cluster-enabled', 'yes', '是否开启集群模式', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('127', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('128', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('129', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('130', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('131', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('132', 'port', '%d', 'sentinel实例端口', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('133', 'dir', '%s', '工作目录', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('134', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('135', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('136', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('137', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('138', 'daemonize', 'no', '是否守护进程', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('139', 'tcp-backlog', '511', 'TCP连接完成队列', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('140', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('141', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('142', 'loglevel', 'notice', '日志级别', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('143', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('144', 'dir', '%s', 'redis工作目录', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('145', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('146', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('147', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('148', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('149', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('150', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('151', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('152', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('153', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('154', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('155', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('156', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('157', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('158', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('159', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2018-09-18 18:25:32', '6', '0', '31', '0'), ('160', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2018-09-18 18:25:40', '6', '0', '31', '0'), ('161', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('162', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('163', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('164', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('165', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('166', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('167', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('168', 'hz', '10', '执行后台task数量,默认:10', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('169', 'port', '%d', '端口', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('170', 'maxmemory', '%dmb', '当前实例最大可用内存', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('171', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('172', 'appendonly', 'yes', '开启append only持久化模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('173', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('174', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('175', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('176', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('177', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('178', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('179', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('180', 'maxclients', '10000', '客户端最大连接数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('181', 'protected-mode', 'yes', '开启保护模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('182', 'bind', '0.0.0.0', '默认客户端都可连接', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('185', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2018-09-18 18:26:32', '6', '1', '31', '0'), ('186', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2018-09-18 18:27:12', '6', '1', '31', '0'), ('253', 'protected-mode', 'no', '关闭保护模式', '2018-11-01 16:10:59', '5', '1', '31', '0'), ('354', 'cluster-enabled', 'yes', '是否开启集群模式', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('355', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('356', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('357', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('358', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('359', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('360', 'port', '%d', 'sentinel实例端口', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('361', 'dir', '%s', '工作目录', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('362', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('363', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('364', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('365', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('366', 'daemonize', 'no', '是否守护进程', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('367', 'tcp-backlog', '511', 'TCP连接完成队列', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('368', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('369', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('370', 'loglevel', 'notice', '日志级别', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('371', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('372', 'dir', '%s', 'redis工作目录', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('373', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('374', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('375', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('376', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('377', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('378', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('379', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('380', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('381', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('382', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('383', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('384', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('385', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('386', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('387', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('388', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('389', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('390', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('391', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('392', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('393', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('394', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('395', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('396', 'hz', '10', '执行后台task数量,默认:10', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('397', 'port', '%d', '端口', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('398', 'maxmemory', '%dmb', '当前实例最大可用内存', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('399', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('400', 'appendonly', 'yes', '开启append only持久化模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('401', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('402', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('403', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('404', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('405', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('406', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('407', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('408', 'maxclients', '10000', '客户端最大连接数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('409', 'protected-mode', 'yes', '开启保护模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('410', 'bind', '0.0.0.0', '默认客户端都可连接', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('411', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('412', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('413', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('414', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('415', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('416', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('417', 'slave-lazy-flush', 'yes', 'slave发起全量复制,是否采用flushall async清理老数据 默认值 no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('418', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2019-10-31 11:15:57', '6', '1', '12', '0'), ('419', 'protected-mode', 'no', '关闭sentinel保护模式', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('420', 'activedefrag', 'no', '碎片整理开启', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('421', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('422', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('423', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('424', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('425', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('506', 'cluster-enabled', 'yes', '是否开启集群模式', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('507', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('508', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('509', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('510', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('511', 'port', '%d', 'sentinel实例端口', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('512', 'dir', '%s', '工作目录', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('513', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('514', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('515', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('516', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('517', 'daemonize', 'no', '是否守护进程', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('518', 'tcp-backlog', '511', 'TCP连接完成队列', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('519', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('520', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('521', 'loglevel', 'notice', '日志级别', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('522', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('523', 'dir', '%s', 'redis工作目录', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('524', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('525', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('526', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('527', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('528', 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('529', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('530', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('531', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('532', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('533', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('534', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('535', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('536', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('537', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('538', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('539', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('540', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('541', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('542', 'hz', '10', '执行后台task数量,默认:10', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('543', 'port', '%d', '端口', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('544', 'maxmemory', '%dmb', '当前实例最大可用内存', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('545', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('546', 'appendonly', 'yes', '开启append only持久化模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('547', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('548', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('549', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('550', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('551', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('552', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('553', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('554', 'maxclients', '10000', '客户端最大连接数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('555', 'protected-mode', 'yes', '开启保护模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('556', 'bind', '0.0.0.0', '默认客户端都可连接', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('557', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('558', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('559', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('560', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('561', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('562', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('563', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('564', 'protected-mode', 'no', '关闭sentinel保护模式', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('565', 'activedefrag', 'yes', '碎片整理开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('566', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('567', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('568', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('569', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('570', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('571', 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('572', 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('573', 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('574', 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('575', 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('576', 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('577', 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('578', 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('579', 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('580', 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('581', 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('582', 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('583', 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('585', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:45:22', '6', '1', '37', '0'), ('587', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:18', '6', '1', '12', '0'), ('589', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:49', '6', '1', '31', '0'), ('590', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:49:47', '6', '1', '29', '0');\nCOMMIT;\n\n\n--\n-- Table structure for table `instance_fault`\n--\n\nDROP TABLE IF EXISTS `instance_fault`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `instance_fault` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例id',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0:心跳停止,1:心跳恢复',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `type` mediumint(4) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `reason` mediumtext NOT NULL COMMENT '故障原因描述',\n  PRIMARY KEY (`id`),\n  KEY `idx_ip_port` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `inst_id` (`inst_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8927 DEFAULT CHARSET=utf8 COMMENT='实例故障表' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `instance_host`\n--\n\nDROP TABLE IF EXISTS `instance_host`;\nCREATE TABLE `instance_host` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `ssh_user` varchar(32) DEFAULT NULL COMMENT 'ssh用户',\n  `ssh_pwd` varchar(32) DEFAULT NULL COMMENT 'ssh密码',\n  `warn` int(5) DEFAULT '1' COMMENT '0不报警，1报警',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_host_ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_info`\n--\n\nDROP TABLE IF EXISTS `instance_info`;\nCREATE TABLE `instance_info` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'memcached instance id',\n  `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '对等实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id，与app_desc关联',\n  `host_id` bigint(20) NOT NULL COMMENT '对应的主机id，与instance_host关联',\n  `ip` varchar(16) NOT NULL COMMENT '实例的ip',\n  `port` int(11) NOT NULL COMMENT '实例端口',\n  `status` tinyint(4) NOT NULL COMMENT '是否启用:0:节点异常,1:正常启用,2:节点下线',\n  `mem` int(11) NOT NULL COMMENT '内存大小',\n  `conn` int(11) NOT NULL COMMENT '连接数',\n  `cmd` varchar(255) NOT NULL COMMENT '启动实例的命令/redis-sentinel的masterName',\n  `type` mediumint(11) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_inst_ipport` (`ip`,`port`) USING BTREE,\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_latency_history`\n--\n\nDROP TABLE IF EXISTS `instance_latency_history`;\nCREATE TABLE `instance_latency_history` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `event` varchar(255) NOT NULL COMMENT '事件名称',\n  `execute_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `execution_cost` bigint(20) NOT NULL COMMENT '耗时(微妙)',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `latencyistorykey` (`instance_id`,`event`,`execute_date`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executedate` (`app_id`,`execute_date`),\n  KEY `idx_executedate` (`execute_date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例延迟事件信息表';\n\n--\n-- Table structure for table `instance_minute_stats`\n--\n\nDROP TABLE IF EXISTS `instance_minute_stats`;\nCREATE TABLE `instance_minute_stats` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `json` text NOT NULL COMMENT '统计json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_collect_time` (`collect_time`),\n  KEY `idx_created_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例分钟统计表';\n\n--\n-- Table structure for table `instance_reshard_process`\n--\n\nDROP TABLE IF EXISTS `instance_reshard_process`;\nCREATE TABLE `instance_reshard_process` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `audit_id` bigint(20) NOT NULL COMMENT '审核id',\n  `source_instance_id` int(11) NOT NULL COMMENT '源实例id',\n  `target_instance_id` int(11) NOT NULL COMMENT '目标实例id',\n  `start_slot` int(11) NOT NULL COMMENT '开始slot',\n  `end_slot` int(11) NOT NULL COMMENT '结束slot',\n  `migrating_slot` int(11) NOT NULL COMMENT '正在迁移的slot',\n  `is_pipeline` tinyint(4) NOT NULL COMMENT '是否为pipeline,0:否,1:是',\n  `finish_slot_num` int(11) NOT NULL COMMENT '已经完成迁移的slot数量',\n  `status` tinyint(4) NOT NULL COMMENT '0:运行中 1:完成 2:出错',\n  `start_time` datetime NOT NULL COMMENT '迁移开始时间',\n  `end_time` datetime NOT NULL COMMENT '迁移结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit` (`audit_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例Reshard进度';\n\n--\n-- Table structure for table `instance_slow_log`\n--\n\nDROP TABLE IF EXISTS `instance_slow_log`;\nCREATE TABLE `instance_slow_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `slow_log_id` bigint(20) NOT NULL COMMENT '慢查询id',\n  `cost_time` int(11) NOT NULL COMMENT '耗时(微妙)',\n  `command` varchar(255) NOT NULL COMMENT '执行命令',\n  `execute_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `slowlogkey` (`instance_id`,`slow_log_id`,`execute_time`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executetime` (`app_id`,`execute_time`),\n  KEY `idx_executetime` (`execute_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例慢查询列表';\n\n--\n-- Table structure for table `instance_statistics`\n--\n\nDROP TABLE IF EXISTS `instance_statistics`;\nCREATE TABLE `instance_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `host_id` bigint(20) NOT NULL COMMENT '机器的id',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `port` int(255) NOT NULL COMMENT 'port',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从',\n  `max_memory` bigint(255) NOT NULL COMMENT '预分配内存，单位byte',\n  `used_memory` bigint(255) NOT NULL COMMENT '已使用内存，单位byte',\n  `curr_items` bigint(255) NOT NULL COMMENT '当前item数量',\n  `curr_connections` int(255) NOT NULL COMMENT '当前连接数',\n  `misses` bigint(255) NOT NULL COMMENT 'miss数',\n  `hits` bigint(255) NOT NULL COMMENT '命中数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `mem_fragmentation_ratio` double DEFAULT '0' COMMENT '碎片率',\n  `aof_delayed_fsync` int(11) DEFAULT '0' COMMENT 'aof阻塞次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `machine_id` (`host_id`),\n  KEY `idx_inst_id` (`inst_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='实例的最新统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_info`\n--\n\nDROP TABLE IF EXISTS `machine_info`;\nCREATE TABLE `machine_info` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '机器的id',\n  `ssh_user` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh用户',\n  `ssh_passwd` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh密码',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `room` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '所属机房',\n  `mem` int(11) unsigned NOT NULL COMMENT '内存大小，单位G',\n  `cpu` mediumint(24) unsigned NOT NULL COMMENT 'cpu数量',\n  `virtual` tinyint(8) unsigned NOT NULL DEFAULT '1' COMMENT '是否虚拟，0表示否，1表示是',\n  `real_ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT '宿主机ip',\n  `service_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上线时间',\n  `fault_count` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '故障次数',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n  `warn` tinyint(255) unsigned NOT NULL DEFAULT '1' COMMENT '是否启用报警，0不启用，1启用',\n  `available` tinyint(255) NOT NULL COMMENT '表示机器是否可用，1表示可用，0表示不可用；',\n  `groupId` int(11) NOT NULL DEFAULT '0' COMMENT '机器分组，默认为0，表示原生资源，非0表示外部提供的资源(可扩展)',\n  `type` int(11) NOT NULL DEFAULT '0' COMMENT '0原生 1 其他',\n  `extra_desc` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '对于机器的额外说明(例如机器安装的其他服务(web,mysql,queue等等))',\n  `collect` int(11) DEFAULT '1' COMMENT 'switch of collect server status, 1:open, 0:close',\n  `version_install` varchar(512) COLLATE utf8_bin DEFAULT NULL COMMENT '机器安装redis版本状态',\n  `use_type` tinyint(4) DEFAULT '2' COMMENT '使用类型：Redis专用机器(0)，Redis测试机器(1)，混合部署机器(2)，Redis-Sentinel机器(3)',\n  `k8s_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否k8s容器：0:不是 1:是',\n  `rack` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '机器所在机架信息',\n  `is_allocating` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否在分配中,1是0否',\n  `disk` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '磁盘空间:G',\n  `dis_type` tinyint(4) DEFAULT 0 NOT NULL COMMENT '操作系统发行版本，0:centos;1:ubuntu',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='机器信息表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_relation`\n--\n\nDROP TABLE IF EXISTS `machine_relation`;\nCREATE TABLE `machine_relation` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',\n  `ip` varchar(64) NOT NULL COMMENT '虚拟机ip',\n  `real_ip` varchar(64) NOT NULL COMMENT '宿主机ip',\n  `extra_desc` varchar(128) DEFAULT NULL COMMENT '实例描述信息',\n  `status` int(255) NOT NULL COMMENT '实例变更状态 0:offline ,1:online',\n  `is_sync` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据同步状态 0: 未同步数据  -1:同步中 1:数据已同步 -2:同步失败 ',\n  `sync_time` timestamp NULL DEFAULT NULL COMMENT '同步时间',\n  `update_time` timestamp NULL DEFAULT NULL COMMENT 'pod最后更新时间',\n  `taskid` bigint(11) DEFAULT NULL COMMENT '任务id',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `machine_room`\n--\n\nDROP TABLE IF EXISTS `machine_room`;\nCREATE TABLE `machine_room` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '机房id',\n  `name` varchar(255) NOT NULL COMMENT '机房名称',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `desc` varchar(255) DEFAULT NULL COMMENT '机房描述信息',\n  `ip_network` varchar(32) NOT NULL DEFAULT '' COMMENT '机房网段信息',\n  `operator` varchar(255) DEFAULT NULL COMMENT '运营商',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `machine_room`\n-- ----------------------------\nBEGIN;\nINSERT INTO `machine_room` VALUES ('1', '阿里云杭州', '1', '阿里云-杭州机房', '172.27.*.*', '阿里云');\nCOMMIT;\n\n--\n-- Table structure for table `machine_statistics`\n--\n\nDROP TABLE IF EXISTS `machine_statistics`;\nCREATE TABLE `machine_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `host_id` bigint(20) NOT NULL COMMENT '机器id',\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `cpu_usage` varchar(120) NOT NULL COMMENT 'cpu使用率',\n  `load` varchar(120) NOT NULL COMMENT '机器负载',\n  `traffic` varchar(120) NOT NULL COMMENT 'io网络流量',\n  `memory_usage_ratio` varchar(120) NOT NULL COMMENT '内存使用率',\n  `memory_free` varchar(120) NOT NULL COMMENT '内存剩余',\n  `memory_total` varchar(120) NOT NULL COMMENT '总内存量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  `max_memory` int(11) DEFAULT '0' COMMENT '机器分配内存,单位MB',\n  `instance_count` int(11) DEFAULT '0' COMMENT '机器实例数量',\n  `machine_memory` int(11) DEFAULT '0' COMMENT '机器入库总内存,单位MB',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_ip` (`ip`),\n  KEY `host_id` (`host_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器状态统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_BLOB_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;\nCREATE TABLE `QRTZ_BLOB_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `BLOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_CALENDARS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_CALENDARS`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `QRTZ_CALENDARS` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `CALENDAR_NAME` varchar(200) NOT NULL COMMENT 'calendar名称',\n  `CALENDAR` blob NOT NULL COMMENT 'calendar信息',\n  PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='以 Blob 类型存储 Quartz 的 Calendar 信息' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `QRTZ_CRON_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;\nCREATE TABLE `QRTZ_CRON_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `TRIGGER_NAME` varchar(200) NOT NULL COMMENT 'trigger名',\n  `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT 'trigger组',\n  `CRON_EXPRESSION` varchar(120) NOT NULL COMMENT 'cron表达式',\n  `TIME_ZONE_ID` varchar(80) DEFAULT NULL COMMENT '时区',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储 Cron Trigger，包括 Cron 表达式和时区信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_FIRED_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;\nCREATE TABLE `QRTZ_FIRED_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `ENTRY_ID` varchar(195) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL,\n  `FIRED_TIME` bigint(13) NOT NULL,\n  `SCHED_TIME` bigint(13) NOT NULL,\n  `PRIORITY` int(11) NOT NULL,\n  `STATE` varchar(16) NOT NULL,\n  `JOB_NAME` varchar(200) DEFAULT NULL,\n  `JOB_GROUP` varchar(200) DEFAULT NULL,\n  `IS_NONCONCURRENT` varchar(1) DEFAULT NULL COMMENT '是否非并行执行',\n  `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL COMMENT '是否持久化',\n  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),\n  KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),\n  KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已触发的 Trigger相关的状态信息，以及关联 Job 的执行信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_JOB_DETAILS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;\nCREATE TABLE `QRTZ_JOB_DETAILS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `JOB_CLASS_NAME` varchar(250) NOT NULL,\n  `IS_DURABLE` varchar(1) NOT NULL COMMENT '是否持久化，0不持久化，1持久化',\n  `IS_NONCONCURRENT` varchar(1) NOT NULL COMMENT '是否非并发，0非并发，1并发',\n  `IS_UPDATE_DATA` varchar(1) NOT NULL,\n  `REQUESTS_RECOVERY` varchar(1) NOT NULL COMMENT '是否可恢复，0不恢复，1恢复',\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储每一个已配置的 Job 的详细信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_LOCKS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_LOCKS`;\nCREATE TABLE `QRTZ_LOCKS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `LOCK_NAME` varchar(40) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储程序的悲观锁的信息(假如使用了悲观锁)' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_PAUSED_TRIGGER_GRPS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;\nCREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已暂停的 Trigger 组的信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SCHEDULER_STATE`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;\nCREATE TABLE `QRTZ_SCHEDULER_STATE` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL COMMENT '执行quartz实例的主机名',\n  `LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '实例将状态报告给集群中的其它实例的上一次时间',\n  `CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '实例间状态报告的时间频率',\n  PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储少量的有关 Scheduler 的状态信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SIMPLE_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复次数',\n  `REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复间隔',\n  `TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '已出发次数',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储简单的 Trigger，包括重复次数，间隔，以及已触的次数' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SIMPROP_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `STR_PROP_1` varchar(512) DEFAULT NULL,\n  `STR_PROP_2` varchar(512) DEFAULT NULL,\n  `STR_PROP_3` varchar(512) DEFAULT NULL,\n  `INT_PROP_1` int(11) DEFAULT NULL,\n  `INT_PROP_2` int(11) DEFAULT NULL,\n  `LONG_PROP_1` bigint(20) DEFAULT NULL,\n  `LONG_PROP_2` bigint(20) DEFAULT NULL,\n  `DEC_PROP_1` decimal(13,4) DEFAULT NULL,\n  `DEC_PROP_2` decimal(13,4) DEFAULT NULL,\n  `BOOL_PROP_1` varchar(1) DEFAULT NULL,\n  `BOOL_PROP_2` varchar(1) DEFAULT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_TRIGGERS`;\nCREATE TABLE `QRTZ_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PRIORITY` int(11) DEFAULT NULL,\n  `TRIGGER_STATE` varchar(16) NOT NULL,\n  `TRIGGER_TYPE` varchar(8) NOT NULL,\n  `START_TIME` bigint(13) NOT NULL,\n  `END_TIME` bigint(13) DEFAULT NULL,\n  `CALENDAR_NAME` varchar(200) DEFAULT NULL,\n  `MISFIRE_INSTR` smallint(2) DEFAULT NULL,\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),\n  KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已配置的 Trigger 的信息' /* `compression`='tokudb_zlib' */;\n\n\n--\n-- Table structure for table `server`\n--\n\nDROP TABLE IF EXISTS `server`;\nCREATE TABLE `server` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `host` varchar(255) DEFAULT NULL COMMENT 'host',\n  `nmon` varchar(255) DEFAULT NULL COMMENT 'nmon version',\n  `cpus` tinyint(4) DEFAULT NULL COMMENT 'logic cpu num',\n  `cpu_model` varchar(255) DEFAULT NULL COMMENT 'cpu 型号',\n  `dist` varchar(255) DEFAULT NULL COMMENT '发行版信息',\n  `kernel` varchar(255) DEFAULT NULL COMMENT '内核信息',\n  `ulimit` varchar(255) DEFAULT NULL COMMENT 'ulimit -n,ulimit -u',\n  `updatetime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ip`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `server_stat`\n--\n\nDROP TABLE IF EXISTS `server_stat`;\nCREATE TABLE `server_stat` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `cdate` date NOT NULL COMMENT '数据收集天',\n  `ctime` char(4) NOT NULL COMMENT '数据收集小时分钟',\n  `cuser` float DEFAULT NULL COMMENT '用户态占比',\n  `csys` float DEFAULT NULL COMMENT '内核态占比',\n  `cwio` float DEFAULT NULL COMMENT 'wio占比',\n  `c_ext` text COMMENT '子cpu占比',\n  `cload1` float DEFAULT NULL COMMENT '1分钟load',\n  `cload5` float DEFAULT NULL COMMENT '5分钟load',\n  `cload15` float DEFAULT NULL COMMENT '15分钟load',\n  `mtotal` float DEFAULT NULL COMMENT '总内存,单位M',\n  `mfree` float DEFAULT NULL COMMENT '空闲内存',\n  `mcache` float DEFAULT NULL COMMENT 'cache',\n  `mbuffer` float DEFAULT NULL COMMENT 'buffer',\n  `mswap` float DEFAULT NULL COMMENT 'cache',\n  `mswap_free` float DEFAULT NULL COMMENT 'cache',\n  `nin` float DEFAULT NULL COMMENT '网络入流量 单位K/s',\n  `nout` float DEFAULT NULL COMMENT '网络出流量 单位k/s',\n  `nin_ext` text COMMENT '各网卡入流量详情',\n  `nout_ext` text COMMENT '各网卡出流量详情',\n  `tuse` int(11) DEFAULT NULL COMMENT 'tcp estab连接数',\n  `torphan` int(11) DEFAULT NULL COMMENT 'tcp orphan连接数',\n  `twait` int(11) DEFAULT NULL COMMENT 'tcp time wait连接数',\n  `dread` float DEFAULT NULL COMMENT '磁盘读速率 单位K/s',\n  `dwrite` float DEFAULT NULL COMMENT '磁盘写速率 单位K/s',\n  `diops` float DEFAULT NULL COMMENT '磁盘io速率 交互次数/s',\n  `dbusy` float DEFAULT NULL COMMENT '磁盘io带宽使用百分比',\n  `d_ext` text COMMENT '磁盘各分区占比',\n  `dspace` text COMMENT '磁盘各分区空间使用率',\n  PRIMARY KEY (`ip`,`cdate`,`ctime`),\n  KEY `idx_cdate` (`cdate`),\n  KEY `idx_cdate_ctime` (`cdate`,`ctime`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `system_config`\n--\n\nDROP TABLE IF EXISTS `system_config`;\nCREATE TABLE `system_config` (\n  `config_key` varchar(255) NOT NULL COMMENT '配置key',\n  `config_value` varchar(512) NOT NULL COMMENT '配置value',\n  `info` varchar(255) NOT NULL COMMENT '配置说明',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `order_id` int(11) NOT NULL COMMENT '顺序',\n  PRIMARY KEY (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统配置';\n\n-- ----------------------------\n--  Records of `system_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_config` VALUES ('cachecloud.admin.user.name','admin','cachecloud-admin用户名',1,11),('cachecloud.admin.user.password','admin','cachelcoud-admin密码',1,12),('cachecloud.app.client.conn.threshold','2000','应用连接数报警阀值',1,33),('cachecloud.base.dir','/opt','cachecloud根目录，要和cachecloud-init.sh脚本中的目录一致',1,31),('cachecloud.contact','user1:(xx@zz.com, user1:135xxxxxxxx)<br/>user2: (user2@zz.com, user2:138xxxxxxxx)','值班联系人信息',1,14),('cachecloud.cookie.domain','','cookie登录方式所需要的域名',1,22),('cachecloud.email.alert.interface','','邮件报警接口(参考报警接口规范)',1,24),('cachecloud.machine.ssh.name','cachecloud-open','机器ssh用户名',1,2),('cachecloud.machine.ssh.password','cachecloud-open','机器ssh密码',1,3),('cachecloud.machine.ssh.port','22','机器ssh端口',1,10),('cachecloud.machine.stats.cron.minute','1','机器性能统计周期(分钟)',1,35),('cachecloud.nmon.dir','/opt/cachecloud','nmon安装目录',1,32),('cachecloud.owner.email','xx@sohu.com,yy@qq.com','邮件报警(逗号隔开)',1,21),('cachecloud.owner.phone','xxx,yyy','手机号报警(逗号隔开)',1,21),('cachecloud.owner.weChat','xxx,yyy','微信号报警(逗号隔开)',1,21),('cachecloud.public.key.pem','/opt/ssh/id_rsa','密钥路径',1,5),('cachecloud.public.user.name','cachecloud-open','公钥用户名',1,4),('cachecloud.ssh.auth.type','1','ssh授权方式',1,1),('cachecloud.superAdmin','admin,xx,yy','超级管理员组',1,13),('cachecloud.user.login.type','1','用户登录状态保存方式(session或cookie)',1,22),('cachecloud.weChat.alert.interface','','微信报警接口(参考报警接口规范)',1,23),('cachecloud.whether.schedule.clean.data','false','是否定期清理统计数据',1,34),('machine.load.alert.ratio','8.0','机器负载报警阀值',1,32);\nCOMMIT;\n\n-- ----------------------------\n--  Table structure for `system_resource`\n-- ----------------------------\nDROP TABLE IF EXISTS `system_resource`;\nCREATE TABLE `system_resource` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '资源ID',\n  `name` varchar(64) NOT NULL COMMENT '资源名称',\n  `intro` varchar(255) DEFAULT NULL COMMENT '资源说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:仓库地址 2:脚本 3:资源包 4:公钥/私钥 6:目录管理 7:迁移工具管理',\n  `lastmodify` datetime DEFAULT NULL COMMENT '最后更新时间',\n  `dir` varchar(128) DEFAULT NULL COMMENT '资源路径',\n  `url` varchar(128) DEFAULT NULL COMMENT '仓库地址',\n  `ispush` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:未推送 1:已推送',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `username` varchar(255) DEFAULT NULL COMMENT '最后修改人',\n  `task_id` bigint(11) DEFAULT NULL COMMENT '迁移任务id',\n  `compile_info` varchar(255) DEFAULT NULL COMMENT '编译信息',\n  `order_num` int(6) NOT NULL DEFAULT '0' COMMENT '排序',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `system_resource`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_resource` VALUES (1,'cachecloud-init.sh','容器初始化脚本',2,'2020-07-15 18:35:41','/script','',0,1,NULL,NULL,NULL,0),(2,'x.x.x.x',NULL,1,'2020-08-10 10:31:51','/opt/download/software/cachecloud/resource','http://x.x.x.x/software/cachecloud/resource',0,1,'admin',0,NULL,0),(4,'cachecloud-env.sh','宿主环境脚本',2,'2020-07-15 18:36:28','/script','',0,1,NULL,NULL,NULL,0),(5,'id_rsa','私钥文件',4,'2020-07-07 10:45:39','/ssh','',0,1,NULL,NULL,NULL,0),(6,'id_rsa.pub','公钥文件',4,'2020-07-07 10:45:45','/ssh','',0,1,NULL,NULL,NULL,0),(12,'redis-4.0.14','redis 4.0.14资源包',3,'2020-08-10 09:52:41','/redis','http://download.redis.io/releases/redis-4.0.14.tar.gz',0,1,'admin',532,NULL,0),(21,'/script','脚本目录管理',6,'2020-08-10 10:51:34','',NULL,0,1,'admin',0,NULL,0),(28,'/ssh','ssh目录',6,'2020-07-20 17:55:03',NULL,NULL,0,1,'admin',0,NULL,0),(29,'redis-3.0.7','redis3.0.7 资源包',3,'2020-08-10 09:53:32','/redis','http://download.redis.io/releases/redis-3.0.7.tar.gz',0,1,'admin',529,NULL,0),(31,'redis-3.2.12','redis 3.2.12 资源包',3,'2020-08-10 15:08:21','/redis','http://download.redis.io/releases/redis-3.2.12.tar.gz',0,1,'admin',530,NULL,0),(32,'/redis','redis资源包管理',6,'2020-07-20 17:54:59',NULL,NULL,0,1,'admin',0,NULL,0),(33,'/tool','迁移工具资源包',6,'2020-07-20 17:54:53',NULL,NULL,0,1,'admin',0,NULL,0),(37,'redis-5.0.9','redis5.0.9 资源包',3,'2020-08-10 09:51:41','/redis','http://download.redis.io/releases/redis-5.0.9.tar.gz',0,1,'admin',533,NULL,0),(40,'redis-shake-2.0.3','redis 2.0.3\\n修复fix 5.0迁移类型问题',7,'2020-08-11 10:53:26','/tool','https://github.com/alibaba/RedisShake/releases/download/release-v2.0.3-20200724/redis-shake-v2.0.3.tar.gz',0,1,'admin',518,NULL,0);\nCOMMIT;\n\n--\n-- Table structure for table `task_queue`\n--\n\nDROP TABLE IF EXISTS `task_queue`;\nCREATE TABLE `task_queue` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `important_info` varchar(255) NOT NULL DEFAULT '' COMMENT '重要信息',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `param` longtext NOT NULL COMMENT '任务参数(json):随着任务变化',\n  `init_param` longtext NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2中断，3失败',\n  `parent_task_id` bigint(20) NOT NULL COMMENT '父任务id',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `priority` int(11) NOT NULL COMMENT '优先级',\n  `error_code` int(11) NOT NULL COMMENT '错误代码',\n  `error_msg` varchar(255) NOT NULL COMMENT '错误消息',\n  `task_note` varchar(255) NOT NULL COMMENT '备注',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务表';\n\n--\n-- Table structure for table `task_step_flow`\n--\n\nDROP TABLE IF EXISTS `task_step_flow`;\nCREATE TABLE `task_step_flow` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `task_id` bigint(20) NOT NULL COMMENT '任务id',\n  `child_task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '子任务id',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0未开始、1成功、2中断、3跳过、4失败',\n  `log` text COMMENT '日志',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_task_class_step` (`task_id`,`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤流表';\n\n--\n-- Table structure for table `task_step_meta`\n--\n\nDROP TABLE IF EXISTS `task_step_meta`;\nCREATE TABLE `task_step_meta` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `step_desc` varchar(255) NOT NULL COMMENT '步骤描述',\n  `ops_device` varchar(255) NOT NULL COMMENT '运维建议',\n  `timeout` int(11) NOT NULL COMMENT '超时时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_class_step` (`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤元数据表';\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n-- ----------------------------\n--  Table structure for `standard_statistics`\n-- ----------------------------\nDROP TABLE IF EXISTS `standard_statistics`;\nCREATE TABLE `standard_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `info_json` text NOT NULL COMMENT '收集的json数据',\n  `diff_json` text NOT NULL COMMENT '上一次收集差异的json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `cluster_info_json` varchar(20480) NOT NULL DEFAULT '' COMMENT '收集的cluster info json数据',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_create_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_alert_record`\n--\nDROP TABLE IF EXISTS `app_alert_record`;\nCREATE TABLE `app_alert_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `visible_type` int(1) NOT NULL COMMENT '可见类型（0：均可见；1：仅管理员可见；）',\n  `important_level` int(1) NOT NULL COMMENT '重要类型（0：一般；1：重要；2：紧急）',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `app_id` bigint(20) DEFAULT NULL COMMENT 'app id',\n  `instance_id` bigint(20) DEFAULT NULL COMMENT '实例id',\n  `ip` varchar(16) COLLATE utf8_bin DEFAULT NULL COMMENT '机器ip',\n  `port` int(10) DEFAULT NULL COMMENT '端口号',\n  `title` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '报警标题',\n  `content` varchar(500) COLLATE utf8_bin NOT NULL COMMENT '报警内容',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`),\n  KEY `ip` (`ip`),\n  KEY `idx_inst_id` (`instance_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='报警记录表';\n\n--\n-- Table structure for table `config_restart_record`\n--\nDROP TABLE IF EXISTS `config_restart_record`;\nCREATE TABLE `config_restart_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `app_name` varchar(36) NOT NULL COMMENT '应用名称',\n  `operate_type` char(1) NOT NULL COMMENT '操作类型（0:滚动重启，1:修改配置强制重启；2：修改配置）',\n  `param` varchar(2000) NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2成功，3失败，4配置修改待重启',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `log` longtext COMMENT '日志信息',\n  `user_name` varchar(64) DEFAULT NULL COMMENT '操作人员姓名',\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `instances` varchar(1000) DEFAULT NULL COMMENT '涉及实例id列表的json格式',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='重启记录表';\n\n--\n-- Table structure for table `module_info`\n--\nDROP TABLE IF EXISTS `module_info`;\nCREATE TABLE `module_info` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL,\n  `git_url` varchar(255) NOT NULL DEFAULT '' COMMENT 'git resource',\n  `info` varchar(128) DEFAULT NULL COMMENT '模块信息说明',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `NAMEKEY` (`name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Redis模块信息表';\n\n--\n-- Table structure for table `module_version`\n--\nDROP TABLE IF EXISTS `module_version`;\nCREATE TABLE `module_version` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `module_id` int(11) NOT NULL,\n  `version_id` int(11) NOT NULL COMMENT '关联版本号',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  `so_path` varchar(255) DEFAULT NULL COMMENT '编译后so库的地址',\n  `tag` varchar(64) NOT NULL COMMENT '模块版本号',\n  `status` int(255) NOT NULL DEFAULT '0' COMMENT '是否可用(关联so地址)：0 不可用 1：可用',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Redis模块版本管理表';\n\n--\n-- Table structure for table `app_import`\n--\nDROP TABLE IF EXISTS `app_import`;\nCREATE TABLE `app_import` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '目标应用id',\n  `instance_info` text COMMENT '源redis实例信息',\n  `redis_password` varchar(200) DEFAULT NULL COMMENT '源redis密码',\n  `status` int(11) DEFAULT NULL COMMENT '迁移状态：PREPARE(0, \"准备\", \"应用导入-未开始\"),     START(1, \"进行中...\", \"应用导入-开始\"),     ERROR(2, \"error\", \"应用导入-出错\"),     VERSION_BUILD_START(11, \"进行中...\", \"新建redis版本-进行中\"),     VERSION_BUILD_ERROR(12, \"error\", \"新建redis版本-出错\"),     VERSION_BUILD_END(20, \"成功\", \"新建redis版本-完成\"),     APP_BUILD_INIT(21, \"准备就绪\", \"新建redis应用-准备就绪\"),     APP_BUILD_START(22, \"进行中...\", \"新建redis应用-进行中\"),     APP_BUILD_ERROR(23, \"error\", \"新建redis应用-出错\"),     APP_BUILD_END(30, \"成功\", \"新建redis应用-完成\"),     MIGRATE_INIT(31, \"准备就绪\", \"数据迁移-准备就绪\"),     MIGRATE_START(32, \"进行中...\", \"数据迁移-进行中\"),     MIGRATE_ERROR(33, \"error\", \"数据迁移-出错\"),     MIGRATE_END(3, \"成功\", \"应用导入-成功\")',\n  `step` int(11) DEFAULT NULL COMMENT '导入阶段',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `migrate_id` bigint(20) DEFAULT NULL COMMENT '数据迁移id',\n  `mem_size` int(11) DEFAULT NULL COMMENT '目标应用内存大小，单位G',\n  `redis_version_name` varchar(20) DEFAULT NULL COMMENT '目标应用redis版本，格式：redis-x.x.x',\n  `app_build_task_id` bigint(20) DEFAULT NULL COMMENT '目标应用部署任务id',\n  `source_type` int(11) DEFAULT NULL COMMENT '源redis类型：7:cluster, 6:sentinel, 5:standalone',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- redis_module_config definition\n\nCREATE TABLE `redis_module_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '配置名',\n  `config_value` varchar(512) NOT NULL COMMENT '配置值',\n  `info` varchar(512) NOT NULL COMMENT '配置说明',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n  `status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n  `version_id` int(11) NOT NULL COMMENT 'Module version版本表主键id',\n  `refresh` tinyint(4) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n  `module_id` int(11) NOT NULL DEFAULT '7' COMMENT 'Module 信息表id',\n  `config_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '配置类型，0：加载和运行配置；1：加载时配置；2：运行时配置',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='redis模块配置表';\n\n\n-- app_to_module definition\n\nCREATE TABLE `app_to_module` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `module_id` int(11) NOT NULL COMMENT '模块info id',\n  `module_version_id` int(11) NOT NULL COMMENT '模块版本id',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_to_module_un` (`app_id`,`module_id`,`module_version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='应用与模块关系表';\n"
  },
  {
    "path": "cachecloud-web/sql/3.2.sql",
    "content": "-- MySQL dump 10.15  Distrib 10.0.16-MariaDB, for Linux (x86_64)\n--\n-- Host: localhost    Database: cachecloud_open\n-- ------------------------------------------------------\n-- Server version\t10.0.16-MariaDB-log\n\nSET NAMES utf8;\nSET FOREIGN_KEY_CHECKS = 0;\n\n--\n-- Table structure for table `app_audit`\n--\n\nDROP TABLE IF EXISTS `app_audit`;\nCREATE TABLE `app_audit` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人的id',\n  `user_name` varchar(64) NOT NULL COMMENT '用户名',\n  `type` tinyint(4) NOT NULL COMMENT '申请类型:0:申请应用,1:应用扩容,2:修改配置',\n  `param1` varchar(600) DEFAULT NULL COMMENT '预留参数1',\n  `param2` varchar(600) DEFAULT NULL COMMENT '预留参数2',\n  `param3` varchar(600) DEFAULT NULL COMMENT '预留参数3',\n  `info` varchar(360) NOT NULL COMMENT '申请描述',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:等待审批; 1:审批通过; -1:驳回',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `refuse_reason` varchar(360) DEFAULT NULL COMMENT '驳回理由',\n  `task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '任务id',\n  `operate_id` bigint(20) DEFAULT NULL COMMENT '工单处理人',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid` (`app_id`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_status_create_time` (`status`,`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用审核表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_audit_log`\n--\n\nDROP TABLE IF EXISTS `app_audit_log`;\nCREATE TABLE `app_audit_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '审批操作人id',\n  `info` longtext NOT NULL COMMENT 'app审批的详细信息',\n  `type` tinyint(4) NOT NULL,\n  `create_time` datetime NOT NULL,\n  `app_audit_id` bigint(20) NOT NULL COMMENT '审批id',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit_appid` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app审核日志表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_client_command_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_command_minute_statistics`;\nCREATE TABLE `app_client_command_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `command` varchar(20) NOT NULL COMMENT '命令明文',\n  `cost` bigint(20) DEFAULT NULL COMMENT '命令累计毫秒耗时',\n  `bytes_in` bigint(20) DEFAULT NULL COMMENT '输入流量',\n  `bytes_out` bigint(20) DEFAULT NULL COMMENT '输出流量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `count` int(11) DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__appid_client_command_currentMin` (`app_id`,`client_ip`,`command`,`current_min`),\n  KEY `idx_currentmin_appid_count_cost` (`current_min`,`app_id`,`count`,`cost`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟命令调用上报数据';\n\n--\n-- Table structure for table `app_client_exception_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_exception_minute_statistics`;\nCREATE TABLE `app_client_exception_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `type` tinyint(4) NOT NULL COMMENT '0:connect exception;1:command exception',\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `node` varchar(30) NOT NULL COMMENT '节点信息host:port',\n  `count` bigint(20) DEFAULT NULL COMMENT '累计连接失败次数',\n  `cost` bigint(20) DEFAULT NULL COMMENT '累计连接失败毫秒耗时',\n  `latency_commands` varchar(255) DEFAULT NULL COMMENT '统计命令topN id,逗号分隔',\n  `redis_pool_config` varchar(255) DEFAULT NULL COMMENT 'redis连接池配置信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__client_node_type_currentMin` (`client_ip`,`node`,`type`,`current_min`),\n  KEY `idx_appid_current_min` (`app_id`,`current_min`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟异常上报数据';\n\n--\n-- Table structure for table `app_client_latency_command`\n--\n\nDROP TABLE IF EXISTS `app_client_latency_command`;\nCREATE TABLE `app_client_latency_command` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `command` varchar(255) NOT NULL COMMENT '命令明文',\n  `size` bigint(20) DEFAULT NULL COMMENT '参数长度',\n  `args` varchar(255) DEFAULT NULL COMMENT '裁剪后参数明文',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `invoke_time` bigint(20) DEFAULT NULL COMMENT '命令调用时间戳',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端异常命令调用详情';\n\n--\n-- Table structure for table `app_client_statistic_gather`\n--\n\nDROP TABLE IF EXISTS `app_client_statistic_gather`;\nCREATE TABLE `app_client_statistic_gather` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gather_time` varchar(20) NOT NULL COMMENT '统计时间，格式yyyy-mm-dd',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `cmd_count` bigint(20) DEFAULT '0' COMMENT '命令调用次数',\n  `conn_exp_count` bigint(20) DEFAULT '0' COMMENT '连接异常次数',\n  `avg_cmd_cost` double DEFAULT '0' COMMENT '命令调用平均耗时，单位毫秒',\n  `avg_cmd_exp_cost` double DEFAULT '0' COMMENT '命令超时平均耗时，单位毫秒',\n  `avg_conn_exp_cost` double DEFAULT '0' COMMENT '连接异常平均耗时，单位毫秒',\n  `cmd_exp_count` bigint(20) DEFAULT '0' COMMENT '命令超时次数',\n  `instance_count` int(11) DEFAULT NULL COMMENT '应用实例数',\n  `avg_mem_frag_ratio` double DEFAULT NULL COMMENT '平均碎片率',\n  `mem_used_ratio` double DEFAULT NULL COMMENT '内存使用率',\n  `exception_count` bigint(20) DEFAULT '0' COMMENT '异常数（旧，待下线）',\n  `slow_log_count` bigint(20) DEFAULT '0' COMMENT '慢查询次数',\n  `latency_count` bigint(20) DEFAULT '0' COMMENT '延迟事件次数',\n  `object_size` bigint(20) DEFAULT '0' COMMENT '存储对象数',\n  `used_memory` bigint(20) DEFAULT '0' COMMENT '内存占用 byte',\n  `used_memory_rss` bigint(20) DEFAULT '0' COMMENT '物理内存占用 byte',\n  `max_cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗(单位:秒)',\n  `max_cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗(单位:秒)',\n  `connected_clients` bigint(20) DEFAULT '0' COMMENT '应用客户端连接数',\n  `topology_exam_result` tinyint(4) DEFAULT NULL COMMENT '拓扑诊断结果，0：正常，1：异常',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_appid_gathertime` (`app_id`,`gather_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报数据全天统计';\n\n--\n-- Table structure for table `app_client_value_minute_stat`\n--\n\nDROP TABLE IF EXISTS `app_client_value_minute_stat`;\nCREATE TABLE `app_client_value_minute_stat` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用appid',\n  `collect_time` bigint(20) NOT NULL COMMENT '数据收集时间yyyyMMddHHmm00',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `command` varchar(20) NOT NULL COMMENT '执行命令',\n  `distribute_type` tinyint(4) NOT NULL COMMENT '值分布',\n  `count` int(11) NOT NULL COMMENT '命令执行次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_collect_command_dis` (`app_id`,`collect_time`,`command`,`distribute_type`),\n  KEY `idx_collect_time` (`collect_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟值分布上报数据统计';\n\n--\n-- Table structure for table `app_client_version_statistic`\n--\n\nDROP TABLE IF EXISTS `app_client_version_statistic`;\nCREATE TABLE `app_client_version_statistic` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip地址',\n  `client_version` varchar(20) NOT NULL COMMENT '客户端版本',\n  `report_time` datetime DEFAULT NULL COMMENT '上报时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_client_ip` (`app_id`,`client_ip`),\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报版本信息统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_daily`\n--\n\nDROP TABLE IF EXISTS `app_daily`;\nCREATE TABLE `app_daily` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `date` date NOT NULL COMMENT '日期',\n  `create_time` datetime NOT NULL,\n  `slow_log_count` bigint(20) NOT NULL COMMENT '慢查询个数',\n  `client_exception_count` bigint(20) NOT NULL COMMENT '客户端异常个数',\n  `max_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟最大客户端连接数',\n  `avg_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟平均客户端连接数',\n  `max_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟最大命令数',\n  `avg_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟平均命令数',\n  `avg_hit_ratio` double NOT NULL COMMENT '平均命中率',\n  `min_minute_hit_ratio` double NOT NULL COMMENT '每分钟最小命中率',\n  `max_minute_hit_ratio` double NOT NULL COMMENT '每分钟最大命中率',\n  `avg_used_memory` bigint(20) NOT NULL COMMENT '最大内存使用量',\n  `max_used_memory` bigint(20) NOT NULL COMMENT '平均内存使用量',\n  `expired_keys_count` bigint(20) NOT NULL COMMENT '过期键个数',\n  `evicted_keys_count` bigint(20) NOT NULL COMMENT '剔除键个数',\n  `avg_minute_net_input_byte` double NOT NULL COMMENT '每分钟平均网络input量',\n  `max_minute_net_input_byte` double NOT NULL COMMENT '每分钟最大网络input量',\n  `avg_minute_net_output_byte` double NOT NULL COMMENT '每分钟平均网络output量',\n  `max_minute_net_output_byte` double NOT NULL COMMENT '每分钟最大网络output量',\n  `avg_object_size` bigint(20) NOT NULL COMMENT '键个数平均值',\n  `max_object_size` bigint(20) NOT NULL COMMENT '键个数最大值',\n  `big_key_times` bigint(20) NOT NULL COMMENT 'bigkey次数',\n  `big_key_info` varchar(512) COLLATE utf8_bin NOT NULL COMMENT 'bigkey详情',\n  `client_cmd_count` bigint(20) NOT NULL COMMENT '累计命令调用次数',\n  `client_avg_cmd_cost` double NOT NULL COMMENT '平均命令调用耗时',\n  `client_conn_exp_count` bigint(20) NOT NULL COMMENT '累计连接异常事件次数',\n  `client_avg_conn_exp_cost` double NOT NULL COMMENT '平均连接异常事件耗时',\n  `client_cmd_exp_count` bigint(20) NOT NULL COMMENT '累计命令超时事件次数',\n  `client_avg_cmd_exp_cost` double NOT NULL COMMENT '平均命令超时事件耗时',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid_date` (`app_id`,`date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='app日报';\n\n--\n-- Table structure for table `app_data_migrate_status`\n--\n\nDROP TABLE IF EXISTS `app_data_migrate_status`;\nCREATE TABLE `app_data_migrate_status` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `migrate_machine_ip` varchar(255) NOT NULL COMMENT '迁移工具所在机器ip',\n  `migrate_machine_port` int(11) NOT NULL COMMENT '迁移工具所占port',\n  `source_migrate_type` tinyint(4) NOT NULL COMMENT '源迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `source_servers` varchar(2048) NOT NULL COMMENT '源实例列表',\n  `target_migrate_type` tinyint(4) NOT NULL COMMENT '目标迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `target_servers` varchar(2048) NOT NULL COMMENT '目标实例列表',\n  `source_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '源应用id',\n  `target_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '目标应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '操作人',\n  `status` tinyint(4) NOT NULL COMMENT '迁移执行状态,0:开始,1:结束,2:异常',\n  `start_time` datetime NOT NULL COMMENT '迁移开始执行时间',\n  `end_time` datetime DEFAULT NULL COMMENT '迁移结束执行时间',\n  `log_path` varchar(255) NOT NULL COMMENT '日志文件路径',\n  `config_path` varchar(255) NOT NULL COMMENT '配置文件路径',\n  `migrate_id` varchar(50) DEFAULT NULL COMMENT 'migrate id',\n  `migrate_tool` tinyint(4) DEFAULT NULL COMMENT 'migrate_tool, 0:redis-shake,1:redis-migrate-tool',\n  `redis_source_version` varchar(20) DEFAULT NULL,\n  `redis_target_version` varchar(20) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用迁移记录详情';\n\n--\n-- Table structure for table `app_desc`\n--\n\nDROP TABLE IF EXISTS `app_desc`;\nCREATE TABLE `app_desc` (\n  `app_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '应用id',\n  `name` varchar(36) NOT NULL COMMENT '应用名',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人id',\n  `status` tinyint(4) NOT NULL COMMENT '应用状态, 0未分配，1申请未审批，2审批并发布 3:应用下线',\n  `intro` varchar(255) NOT NULL COMMENT '应用描述',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `passed_time` datetime NOT NULL COMMENT '审批通过时间',\n  `type` int(10) NOT NULL DEFAULT '0' COMMENT 'cache类型，1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud ,5. redis-sentinel ,6.redis-standalone ',\n  `officer` varchar(32) NOT NULL COMMENT '负责人，中文',\n  `ver_id` int(11) NOT NULL COMMENT '版本',\n  `is_test` tinyint(4) DEFAULT '0' COMMENT '是否测试：1是0否',\n  `need_persistence` tinyint(4) DEFAULT '1' COMMENT '是否需要持久化: 1是0否',\n  `need_hot_back_up` tinyint(4) DEFAULT '1' COMMENT '是否需要热备: 1是0否',\n  `has_back_store` tinyint(4) DEFAULT '1' COMMENT '是否有后端数据源: 1是0否',\n  `forecase_qps` int(11) DEFAULT NULL COMMENT '预估qps',\n  `forecast_obj_num` int(11) DEFAULT NULL COMMENT '预估条目数',\n  `mem_alert_value` int(11) DEFAULT NULL COMMENT '内存报警阀值',\n  `client_machine_room` varchar(36) DEFAULT NULL COMMENT '客户端机房信息',\n  `client_conn_alert_value` int(11) DEFAULT '2000' COMMENT '客户端连接报警阀值',\n  `app_key` varchar(255) DEFAULT NULL COMMENT '应用秘钥',\n  `important_level` tinyint(4) NOT NULL DEFAULT '2' COMMENT '应用级别，1:最重要，2:一般重要，3:一般',\n  `password` varchar(255) DEFAULT '' COMMENT 'redis密码',\n  `custom_password` varchar(255) DEFAULT NULL COMMENT '自定义密码',\n  `hit_precent_alert_value` int(11) DEFAULT '0' COMMENT '命中率报警阀值 0:不报警 ',\n  `is_access_monitor` int(11) DEFAULT '0' COMMENT '是否接入全局监控报警 默认0,0:不接入监控 1:接入监控',\n  `app_fsync_value` int(11) DEFAULT '1' COMMENT '应用刷盘策略 1:主从节点appdendfsync=everysec 2:主从节点 appdendfsync=no',\n  `version_id` int(11) NOT NULL DEFAULT '1' COMMENT 'Redis版本表主键id',\n  `maxmemory_policy` tinyint(4) DEFAULT NULL COMMENT '淘汰策略(0：noeviction; 1:allkeys-lru;2:allkeys-lfu;3:volatile-lru;4:volatile-lfu;5:allkeys-random;6:volatile-random;7:volatile-ttl)',\n  PRIMARY KEY (`app_id`),\n  UNIQUE KEY `uidx_app_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app应用描述' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_command_statistics`;\nCREATE TABLE `app_hour_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHH',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`command_name`,`collect_time`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每小时命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_statistics`;\nCREATE TABLE `app_hour_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHH',\n  `hits` bigint(20) NOT NULL COMMENT '每小时命中数量和',\n  `misses` bigint(20) NOT NULL COMMENT '每小时未命中数量和',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '每小时内存占用最大值',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '每小时过期key数量和',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '每小时驱逐key数量和',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '每小时客户端连接数最大值',\n  `object_size` bigint(20) NOT NULL COMMENT '每小时存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '每小时参与累加实例数最小值',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '每小时修改时间最大值',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用统计数据每小时统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_command_statistics`;\nCREATE TABLE `app_minute_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHHmm',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`,`command_name`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每分钟命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_statistics`;\nCREATE TABLE `app_minute_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `hits` bigint(20) NOT NULL COMMENT '命中数量',\n  `misses` bigint(20) NOT NULL COMMENT '未命中数量',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '内存占用',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '过期key数量',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '驱逐key数量',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '客户端连接数',\n  `object_size` bigint(20) NOT NULL COMMENT '每分钟存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '参与累加实例数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_to_user`\n--\n\nDROP TABLE IF EXISTS `app_to_user`;\nCREATE TABLE `app_to_user` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_user`\n--\n\nDROP TABLE IF EXISTS `app_user`;\nCREATE TABLE `app_user` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL COMMENT '用户名',\n  `ch_name` varchar(255) NOT NULL COMMENT '中文名',\n  `email` varchar(64) NOT NULL COMMENT '邮箱',\n  `mobile` varchar(16) NOT NULL COMMENT '手机',\n  `type` int(4) NOT NULL DEFAULT '2' COMMENT '0管理员，1预留，2普通用户，-1无效',\n  `weChat` varchar(32) DEFAULT NULL COMMENT '微信号',\n  `isAlert` tinyint(4) NOT NULL DEFAULT '1' COMMENT '用户是否接收报警 0:不接收 1:接收',\n  `password` varchar(64) DEFAULT NULL COMMENT '密码',\n  `register_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',\n  `purpose` varchar(255) DEFAULT NULL COMMENT '使用目的',\n  `company` varchar(255) DEFAULT NULL COMMENT '公司名称',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_user_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表' /* `compression`='tokudb_zlib' */;\n\n-- ----------------------------\n--  Records of `app_user`\n-- ----------------------------\nBEGIN;\nINSERT INTO `app_user` VALUES ('1', 'admin', 'admin', 'admin@xxx.com', '13500000000', '0', null, '1', NULL, current_timestamp(), NULL, NULL);\nCOMMIT;\n\n--\n-- Table structure for table `brevity_schedule_resources`\n--\n\nDROP TABLE IF EXISTS `brevity_schedule_resources`;\nCREATE TABLE `brevity_schedule_resources` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `type` tinyint(4) NOT NULL COMMENT '类型,见:BrevityScheduleType',\n  `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '时间版本',\n  `host` varchar(16) NOT NULL COMMENT '资源ip',\n  `port` int(11) NOT NULL DEFAULT '0' COMMENT '端口',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_type_host_port` (`type`,`host`,`port`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='短频任务表';\n\n--\n-- Table structure for table `diagnostic_task_record`\n--\n\nDROP TABLE IF EXISTS `diagnostic_task_record`;\nCREATE TABLE `diagnostic_task_record` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `type` int(11) DEFAULT NULL COMMENT '诊断类型：0scan 1bigkey 2idle key 3hotkey 4del key 5slot analysis 6topology exam',\n  `task_id` bigint(20) DEFAULT NULL COMMENT '任务流id',\n  `audit_id` bigint(20) DEFAULT NULL COMMENT '审批id',\n  `status` int(11) DEFAULT NULL COMMENT '诊断状态：0开始 1结束 2异常',\n  `cost` bigint(20) DEFAULT NULL COMMENT '耗时，毫秒',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `redis_key` varchar(100) DEFAULT NULL COMMENT '结果的key',\n  `node` varchar(100) DEFAULT NULL COMMENT '实例，host:port',\n  `parent_task_id` bigint(20) DEFAULT NULL COMMENT '父任务id',\n  `diagnostic_condition` varchar(100) DEFAULT NULL COMMENT '诊断条件',\n  `param1` varchar(100) DEFAULT NULL COMMENT '备用参数1',\n  `param2` varchar(100) DEFAULT NULL COMMENT '备用参数2',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用诊断记录';\n\n--\n-- Table structure for table `instance_alert_configs`\n--\n\nDROP TABLE IF EXISTS `instance_alert_configs`;\nCREATE TABLE `instance_alert_configs` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `alert_config` varchar(255) NOT NULL COMMENT '报警配置',\n  `alert_value` varchar(512) NOT NULL COMMENT '报警阀值',\n  `config_info` varchar(255) NOT NULL COMMENT '配置说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:全局报警,2:实例报警',\n  `instance_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '0:全局配置，其他代表实例id',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `compare_type` tinyint(4) NOT NULL COMMENT '比较类型：1小于,2等于,3大于,4不等于',\n  `check_cycle` tinyint(4) NOT NULL COMMENT '1:一分钟,2:五分钟,3:半小时4:一个小时,5:一天',\n  `update_time` datetime NOT NULL COMMENT '报警配置更新时间',\n  `last_check_time` datetime NOT NULL COMMENT '上次检查时间',\n  `important_level` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '重要程度（0：一般；1：重要；2：紧急）',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`type`,`instance_id`,`alert_config`,`compare_type`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例报警阀值配置';\n\n-- ----------------------------\n--  Records of `instance_alert_configs`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_alert_configs` VALUES ('9', 'aof_current_size', '6000', 'aof当前尺寸(单位：MB)', '1', '0', '1', '3', '3', '2017-06-19 09:43:22', '2020-09-17 10:52:00', 0), ('10', 'aof_delayed_fsync', '3', '分钟aof阻塞个数', '1', '0', '1', '3', '1', '2017-06-19 10:38:19', '2020-09-17 11:09:00', 1), ('11', 'client_biggest_input_buf', '10', '输入缓冲区最大buffer大小(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 10:47:03', '2020-09-17 11:09:00', 1), ('12', 'client_longest_output_list', '50000', '输出缓冲区最大队列长度', '1', '0', '1', '3', '1', '2017-06-19 10:55:45', '2020-09-17 11:09:00', 1), ('13', 'instantaneous_ops_per_sec', '60000', '实时ops', '1', '0', '1', '3', '1', '2017-06-19 11:02:38', '2020-09-17 11:09:00', 1),('14', 'latest_fork_usec', '400000', '上次fork所用时间(单位：微秒)', '1', '0', '1', '3', '5', '2017-06-19 11:21:35', '2020-09-16 16:51:00', 1), ('15', 'mem_fragmentation_ratio', '1.5', '内存碎片率(检测大于500MB)', '1', '0', '1', '3', '5', '2017-06-19 12:49:16', '2020-09-16 16:51:00', 0), ('16', 'rdb_last_bgsave_status', 'ok', '上一次bgsave状态', '1', '0', '1', '4', '4', '2017-06-19 14:15:21', '2020-09-17 10:19:00', 0), ('17', 'total_net_output_bytes', '5000', '分钟网络输出流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:39:44', '2020-09-17 11:09:00', 0), ('19', 'total_net_input_bytes', '1200', '分钟网络输入流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:45:44', '2020-09-17 11:09:00', 0), ('20', 'sync_partial_err', '0', '分钟部分复制失败次数', '1', '0', '1', '3', '1', '2017-06-19 18:34:41', '2020-09-17 11:09:00', 1), ('21', 'sync_partial_ok', '0', '分钟部分复制成功次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:01', '2020-09-17 11:09:00', 1), ('22', 'sync_full', '0', '分钟全量复制执行次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:17', '2020-09-17 11:09:00', 1), ('23', 'rejected_connections', '0', '分钟拒绝连接数', '1', '0', '1', '3', '1', '2017-06-19 18:35:36', '2020-09-17 11:09:00', 2), ('54', 'master_slave_offset_diff', '20000000', '主从节点偏移量差(单位：字节)', '1', '0', '1', '3', '2', '2017-06-20 18:58:56', '2020-09-17 11:06:00', 0), ('56', 'cluster_state', 'ok', '集群状态', '1', '0', '1', '4', '1', '2017-06-21 18:01:52', '2020-09-17 11:09:00', 2), ('57', 'cluster_slots_ok', '16384', '集群成功分配槽个数', '1', '0', '1', '4', '1', '2017-06-21 18:02:04', '2020-09-17 11:09:00', 2);\nCOMMIT;\n\n--\n-- Table structure for table `instance_big_key`\n--\n\nDROP TABLE IF EXISTS `instance_big_key`;\nCREATE TABLE `instance_big_key` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `audit_id` bigint(20) NOT NULL COMMENT 'audit id',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从，详见InstanceRoleEnum',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `big_key` varchar(255) NOT NULL COMMENT '键',\n  `type` varchar(16) NOT NULL COMMENT '类型:string,hash,list,set,zset',\n  `length` int(11) NOT NULL COMMENT '长度',\n  `create_time` datetime NOT NULL COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_audit` (`app_id`,`audit_id`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例bigkey列表';\n\n--\n-- Table structure for table `instance_config`\n--\n\nDROP TABLE IF EXISTS `instance_config`;\nCREATE TABLE `instance_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '配置名',\n  `config_value` varchar(512) NOT NULL COMMENT '配置值',\n  `info` varchar(512) NOT NULL COMMENT '配置说明',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n  `status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n  `version_id` int(11) NOT NULL COMMENT 'Redis版本表主键id',\n  `refresh` mediumint(9) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例配置模板';\n\n-- ----------------------------\n--  Records of `instance_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_config` VALUES ('1', 'cluster-enabled', 'yes', '是否开启集群模式', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('2', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('3', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('4', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('5', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2016-07-05 15:08:30', '2', '1', '29', '0'), ('6', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2016-07-05 15:08:31', '2', '1', '29', '0'), ('7', 'port', '%d', 'sentinel实例端口', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('8', 'dir', '%s', '工作目录', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('9', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('10', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('11', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('12', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2016-07-05 15:08:31', '5', '1', '29', '0'), ('13', 'daemonize', 'no', '是否守护进程', '2016-07-14 14:00:05', '6', '1', '29', '0'), ('14', 'tcp-backlog', '511', 'TCP连接完成队列', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('15', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('16', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2016-12-06 11:40:46', '6', '1', '29', '0'), ('17', 'loglevel', 'notice', '日志级别', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('18', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('19', 'dir', '%s', 'redis工作目录', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('20', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('21', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('22', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('23', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('24', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('25', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('26', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('27', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('28', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('29', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('30', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('31', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('32', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('33', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('34', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('35', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('36', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('37', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('38', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('39', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('40', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('41', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2016-11-24 10:24:21', '6', '1', '29', '0'), ('42', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('43', 'hz', '10', '执行后台task数量,默认:10', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('44', 'port', '%d', '端口', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('45', 'maxmemory', '%dmb', '当前实例最大可用内存', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('46', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2016-07-05 15:08:31', '6', '1', '29', '0'), ('47', 'appendonly', 'yes', '开启append only持久化模式', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('48', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('49', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('50', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('51', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('52', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('53', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('54', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('55', 'maxclients', '10000', '客户端最大连接数', '2016-07-05 15:08:32', '6', '1', '29', '0'), ('126', 'cluster-enabled', 'yes', '是否开启集群模式', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('127', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('128', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('129', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('130', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('131', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2018-09-18 18:23:03', '2', '1', '31', '0'), ('132', 'port', '%d', 'sentinel实例端口', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('133', 'dir', '%s', '工作目录', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('134', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('135', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('136', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('137', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2018-09-18 18:23:03', '5', '1', '31', '0'), ('138', 'daemonize', 'no', '是否守护进程', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('139', 'tcp-backlog', '511', 'TCP连接完成队列', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('140', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('141', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('142', 'loglevel', 'notice', '日志级别', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('143', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('144', 'dir', '%s', 'redis工作目录', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('145', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('146', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('147', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('148', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('149', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('150', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('151', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('152', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('153', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('154', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('155', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('156', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('157', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('158', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('159', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2018-09-18 18:25:32', '6', '0', '31', '0'), ('160', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2018-09-18 18:25:40', '6', '0', '31', '0'), ('161', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('162', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('163', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('164', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('165', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('166', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('167', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('168', 'hz', '10', '执行后台task数量,默认:10', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('169', 'port', '%d', '端口', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('170', 'maxmemory', '%dmb', '当前实例最大可用内存', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('171', 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('172', 'appendonly', 'yes', '开启append only持久化模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('173', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('174', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('175', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('176', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('177', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('178', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('179', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('180', 'maxclients', '10000', '客户端最大连接数', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('181', 'protected-mode', 'yes', '开启保护模式', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('182', 'bind', '0.0.0.0', '默认客户端都可连接', '2018-09-18 18:23:03', '6', '1', '31', '0'), ('185', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2018-09-18 18:26:32', '6', '1', '31', '0'), ('186', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2018-09-18 18:27:12', '6', '1', '31', '0'), ('253', 'protected-mode', 'no', '关闭保护模式', '2018-11-01 16:10:59', '5', '1', '31', '0'), ('354', 'cluster-enabled', 'yes', '是否开启集群模式', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('355', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('356', 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('357', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('358', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('359', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2019-10-24 17:33:26', '2', '1', '12', '0'), ('360', 'port', '%d', 'sentinel实例端口', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('361', 'dir', '%s', '工作目录', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('362', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('363', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('364', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('365', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('366', 'daemonize', 'no', '是否守护进程', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('367', 'tcp-backlog', '511', 'TCP连接完成队列', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('368', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('369', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('370', 'loglevel', 'notice', '日志级别', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('371', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('372', 'dir', '%s', 'redis工作目录', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('373', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('374', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('375', 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('376', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('377', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('378', 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('379', 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('380', 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('381', 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('382', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('383', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('384', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('385', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('386', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('387', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('388', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2019-10-24 17:33:26', '6', '0', '12', '0'), ('389', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('390', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('391', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('392', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('393', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('394', 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('395', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('396', 'hz', '10', '执行后台task数量,默认:10', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('397', 'port', '%d', '端口', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('398', 'maxmemory', '%dmb', '当前实例最大可用内存', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('399', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('400', 'appendonly', 'yes', '开启append only持久化模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('401', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('402', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('403', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('404', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('405', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('406', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('407', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('408', 'maxclients', '10000', '客户端最大连接数', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('409', 'protected-mode', 'yes', '开启保护模式', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('410', 'bind', '0.0.0.0', '默认客户端都可连接', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('411', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('412', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('413', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('414', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('415', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('416', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('417', 'slave-lazy-flush', 'yes', 'slave发起全量复制,是否采用flushall async清理老数据 默认值 no', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('418', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2019-10-31 11:15:57', '6', '1', '12', '0'), ('419', 'protected-mode', 'no', '关闭sentinel保护模式', '2019-10-24 17:33:26', '5', '1', '12', '0'), ('420', 'activedefrag', 'no', '碎片整理开启', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('421', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('422', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('423', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('424', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('425', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2019-10-24 17:33:26', '6', '1', '12', '0'), ('506', 'cluster-enabled', 'yes', '是否开启集群模式', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('507', 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('508', 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('509', 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('510', 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('511', 'port', '%d', 'sentinel实例端口', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('512', 'dir', '%s', '工作目录', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('513', 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('514', 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('515', 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('516', 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('517', 'daemonize', 'no', '是否守护进程', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('518', 'tcp-backlog', '511', 'TCP连接完成队列', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('519', 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('520', 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('521', 'loglevel', 'notice', '日志级别', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('522', 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('523', 'dir', '%s', 'redis工作目录', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('524', 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('525', 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('526', 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('527', 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('528', 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('529', 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('530', 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('531', 'slowlog-max-len', '128', '最多记录慢查询的条数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('532', 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('533', 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('534', 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('535', 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2020-04-26 18:12:55', '6', '0', '37', '0'), ('536', 'set-max-intset-entries', '512', 'set数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('537', 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('538', 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('539', 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('540', 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('541', 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('542', 'hz', '10', '执行后台task数量,默认:10', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('543', 'port', '%d', '端口', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('544', 'maxmemory', '%dmb', '当前实例最大可用内存', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('545', 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('546', 'appendonly', 'yes', '开启append only持久化模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('547', 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('548', 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('549', 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('550', 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('551', 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('552', 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('553', 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('554', 'maxclients', '10000', '客户端最大连接数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('555', 'protected-mode', 'yes', '开启保护模式', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('556', 'bind', '0.0.0.0', '默认客户端都可连接', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('557', 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('558', 'list-compress-depth', '0', '压缩方式，0:不压缩', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('559', 'always-show-logo', 'yes', 'redis启动是否显示logo', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('560', 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('561', 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('562', 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('563', 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('564', 'protected-mode', 'no', '关闭sentinel保护模式', '2020-04-26 18:12:55', '5', '1', '37', '0'), ('565', 'activedefrag', 'yes', '碎片整理开启', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('566', 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('567', 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('568', 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('569', 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('570', 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('571', 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('572', 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('573', 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2020-04-26 18:12:55', '2', '1', '37', '0'), ('574', 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('575', 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('576', 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('577', 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('578', 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('579', 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('580', 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('581', 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('582', 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('583', 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2020-04-26 18:12:55', '6', '1', '37', '0'), ('585', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:45:22', '6', '1', '37', '0'), ('587', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:18', '6', '1', '12', '0'), ('589', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:49', '6', '1', '31', '0'), ('590', 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:49:47', '6', '1', '29', '0');\nCOMMIT;\n\n\n--\n-- Table structure for table `instance_fault`\n--\n\nDROP TABLE IF EXISTS `instance_fault`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `instance_fault` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例id',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0:心跳停止,1:心跳恢复',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `type` mediumint(4) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `reason` mediumtext NOT NULL COMMENT '故障原因描述',\n  PRIMARY KEY (`id`),\n  KEY `idx_ip_port` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `inst_id` (`inst_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8927 DEFAULT CHARSET=utf8 COMMENT='实例故障表' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `instance_host`\n--\n\nDROP TABLE IF EXISTS `instance_host`;\nCREATE TABLE `instance_host` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `ssh_user` varchar(32) DEFAULT NULL COMMENT 'ssh用户',\n  `ssh_pwd` varchar(32) DEFAULT NULL COMMENT 'ssh密码',\n  `warn` int(5) DEFAULT '1' COMMENT '0不报警，1报警',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_host_ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_info`\n--\n\nDROP TABLE IF EXISTS `instance_info`;\nCREATE TABLE `instance_info` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'memcached instance id',\n  `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '对等实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id，与app_desc关联',\n  `host_id` bigint(20) NOT NULL COMMENT '对应的主机id，与instance_host关联',\n  `ip` varchar(16) NOT NULL COMMENT '实例的ip',\n  `port` int(11) NOT NULL COMMENT '实例端口',\n  `status` tinyint(4) NOT NULL COMMENT '是否启用:0:节点异常,1:正常启用,2:节点下线',\n  `mem` int(11) NOT NULL COMMENT '内存大小',\n  `conn` int(11) NOT NULL COMMENT '连接数',\n  `cmd` varchar(255) NOT NULL COMMENT '启动实例的命令/redis-sentinel的masterName',\n  `type` mediumint(11) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_inst_ipport` (`ip`,`port`) USING BTREE,\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_latency_history`\n--\n\nDROP TABLE IF EXISTS `instance_latency_history`;\nCREATE TABLE `instance_latency_history` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `event` varchar(255) NOT NULL COMMENT '事件名称',\n  `execute_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `execution_cost` bigint(20) NOT NULL COMMENT '耗时(微妙)',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `latencyistorykey` (`instance_id`,`event`,`execute_date`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executedate` (`app_id`,`execute_date`),\n  KEY `idx_executedate` (`execute_date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例延迟事件信息表';\n\n--\n-- Table structure for table `instance_minute_stats`\n--\n\nDROP TABLE IF EXISTS `instance_minute_stats`;\nCREATE TABLE `instance_minute_stats` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `json` text NOT NULL COMMENT '统计json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_collect_time` (`collect_time`),\n  KEY `idx_created_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例分钟统计表';\n\n--\n-- Table structure for table `instance_reshard_process`\n--\n\nDROP TABLE IF EXISTS `instance_reshard_process`;\nCREATE TABLE `instance_reshard_process` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `audit_id` bigint(20) NOT NULL COMMENT '审核id',\n  `source_instance_id` int(11) NOT NULL COMMENT '源实例id',\n  `target_instance_id` int(11) NOT NULL COMMENT '目标实例id',\n  `start_slot` int(11) NOT NULL COMMENT '开始slot',\n  `end_slot` int(11) NOT NULL COMMENT '结束slot',\n  `migrating_slot` int(11) NOT NULL COMMENT '正在迁移的slot',\n  `is_pipeline` tinyint(4) NOT NULL COMMENT '是否为pipeline,0:否,1:是',\n  `finish_slot_num` int(11) NOT NULL COMMENT '已经完成迁移的slot数量',\n  `status` tinyint(4) NOT NULL COMMENT '0:运行中 1:完成 2:出错',\n  `start_time` datetime NOT NULL COMMENT '迁移开始时间',\n  `end_time` datetime NOT NULL COMMENT '迁移结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit` (`audit_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例Reshard进度';\n\n--\n-- Table structure for table `instance_slow_log`\n--\n\nDROP TABLE IF EXISTS `instance_slow_log`;\nCREATE TABLE `instance_slow_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `slow_log_id` bigint(20) NOT NULL COMMENT '慢查询id',\n  `cost_time` int(11) NOT NULL COMMENT '耗时(微妙)',\n  `command` varchar(255) NOT NULL COMMENT '执行命令',\n  `execute_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `slowlogkey` (`instance_id`,`slow_log_id`,`execute_time`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executetime` (`app_id`,`execute_time`),\n  KEY `idx_executetime` (`execute_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例慢查询列表';\n\n--\n-- Table structure for table `instance_statistics`\n--\n\nDROP TABLE IF EXISTS `instance_statistics`;\nCREATE TABLE `instance_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `host_id` bigint(20) NOT NULL COMMENT '机器的id',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `port` int(255) NOT NULL COMMENT 'port',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从',\n  `max_memory` bigint(255) NOT NULL COMMENT '预分配内存，单位byte',\n  `used_memory` bigint(255) NOT NULL COMMENT '已使用内存，单位byte',\n  `curr_items` bigint(255) NOT NULL COMMENT '当前item数量',\n  `curr_connections` int(255) NOT NULL COMMENT '当前连接数',\n  `misses` bigint(255) NOT NULL COMMENT 'miss数',\n  `hits` bigint(255) NOT NULL COMMENT '命中数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `mem_fragmentation_ratio` double DEFAULT '0' COMMENT '碎片率',\n  `aof_delayed_fsync` int(11) DEFAULT '0' COMMENT 'aof阻塞次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `machine_id` (`host_id`),\n  KEY `idx_inst_id` (`inst_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='实例的最新统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_info`\n--\n\nDROP TABLE IF EXISTS `machine_info`;\nCREATE TABLE `machine_info` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '机器的id',\n  `ssh_user` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh用户',\n  `ssh_passwd` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh密码',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `room` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '所属机房',\n  `mem` int(11) unsigned NOT NULL COMMENT '内存大小，单位G',\n  `cpu` mediumint(24) unsigned NOT NULL COMMENT 'cpu数量',\n  `virtual` tinyint(8) unsigned NOT NULL DEFAULT '1' COMMENT '是否虚拟，0表示否，1表示是',\n  `real_ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT '宿主机ip',\n  `service_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上线时间',\n  `fault_count` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '故障次数',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n  `warn` tinyint(255) unsigned NOT NULL DEFAULT '1' COMMENT '是否启用报警，0不启用，1启用',\n  `available` tinyint(255) NOT NULL COMMENT '表示机器是否可用，1表示可用，0表示不可用；',\n  `groupId` int(11) NOT NULL DEFAULT '0' COMMENT '机器分组，默认为0，表示原生资源，非0表示外部提供的资源(可扩展)',\n  `type` int(11) NOT NULL DEFAULT '0' COMMENT '0原生 1 其他',\n  `extra_desc` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '对于机器的额外说明(例如机器安装的其他服务(web,mysql,queue等等))',\n  `collect` int(11) DEFAULT '1' COMMENT 'switch of collect server status, 1:open, 0:close',\n  `version_install` varchar(512) COLLATE utf8_bin DEFAULT NULL COMMENT '机器安装redis版本状态',\n  `use_type` tinyint(4) DEFAULT '2' COMMENT '使用类型：Redis专用机器(0)，Redis测试机器(1)，混合部署机器(2)，Redis-Sentinel机器(3)',\n  `k8s_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否k8s容器：0:不是 1:是',\n  `rack` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '机器所在机架信息',\n  `is_allocating` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否在分配中,1是0否',\n  `disk` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '磁盘空间:G',\n  `dis_type` tinyint(4) DEFAULT 0 NOT NULL COMMENT '操作系统发行版本，0:centos;1:ubuntu',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='机器信息表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_relation`\n--\n\nDROP TABLE IF EXISTS `machine_relation`;\nCREATE TABLE `machine_relation` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',\n  `ip` varchar(64) NOT NULL COMMENT '虚拟机ip',\n  `real_ip` varchar(64) NOT NULL COMMENT '宿主机ip',\n  `extra_desc` varchar(128) DEFAULT NULL COMMENT '实例描述信息',\n  `status` int(255) NOT NULL COMMENT '实例变更状态 0:offline ,1:online',\n  `is_sync` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据同步状态 0: 未同步数据  -1:同步中 1:数据已同步 -2:同步失败 ',\n  `sync_time` timestamp NULL DEFAULT NULL COMMENT '同步时间',\n  `update_time` timestamp NULL DEFAULT NULL COMMENT 'pod最后更新时间',\n  `taskid` bigint(11) DEFAULT NULL COMMENT '任务id',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `machine_room`\n--\n\nDROP TABLE IF EXISTS `machine_room`;\nCREATE TABLE `machine_room` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '机房id',\n  `name` varchar(255) NOT NULL COMMENT '机房名称',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `desc` varchar(255) DEFAULT NULL COMMENT '机房描述信息',\n  `ip_network` varchar(32) NOT NULL DEFAULT '' COMMENT '机房网段信息',\n  `operator` varchar(255) DEFAULT NULL COMMENT '运营商',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `machine_room`\n-- ----------------------------\nBEGIN;\nINSERT INTO `machine_room` VALUES ('1', '阿里云杭州', '1', '阿里云-杭州机房', '172.27.*.*', '阿里云');\nCOMMIT;\n\n--\n-- Table structure for table `machine_statistics`\n--\n\nDROP TABLE IF EXISTS `machine_statistics`;\nCREATE TABLE `machine_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `host_id` bigint(20) NOT NULL COMMENT '机器id',\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `cpu_usage` varchar(120) NOT NULL COMMENT 'cpu使用率',\n  `load` varchar(120) NOT NULL COMMENT '机器负载',\n  `traffic` varchar(120) NOT NULL COMMENT 'io网络流量',\n  `memory_usage_ratio` varchar(120) NOT NULL COMMENT '内存使用率',\n  `memory_free` varchar(120) NOT NULL COMMENT '内存剩余',\n  `memory_total` varchar(120) NOT NULL COMMENT '总内存量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  `max_memory` int(11) DEFAULT '0' COMMENT '机器分配内存,单位MB',\n  `instance_count` int(11) DEFAULT '0' COMMENT '机器实例数量',\n  `machine_memory` int(11) DEFAULT '0' COMMENT '机器入库总内存,单位MB',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_ip` (`ip`),\n  KEY `host_id` (`host_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器状态统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_BLOB_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;\nCREATE TABLE `QRTZ_BLOB_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `BLOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_CALENDARS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_CALENDARS`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `QRTZ_CALENDARS` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `CALENDAR_NAME` varchar(200) NOT NULL COMMENT 'calendar名称',\n  `CALENDAR` blob NOT NULL COMMENT 'calendar信息',\n  PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='以 Blob 类型存储 Quartz 的 Calendar 信息' /* `compression`='tokudb_zlib' */;\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n--\n-- Table structure for table `QRTZ_CRON_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;\nCREATE TABLE `QRTZ_CRON_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `TRIGGER_NAME` varchar(200) NOT NULL COMMENT 'trigger名',\n  `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT 'trigger组',\n  `CRON_EXPRESSION` varchar(120) NOT NULL COMMENT 'cron表达式',\n  `TIME_ZONE_ID` varchar(80) DEFAULT NULL COMMENT '时区',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储 Cron Trigger，包括 Cron 表达式和时区信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_FIRED_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;\nCREATE TABLE `QRTZ_FIRED_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `ENTRY_ID` varchar(195) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL,\n  `FIRED_TIME` bigint(13) NOT NULL,\n  `SCHED_TIME` bigint(13) NOT NULL,\n  `PRIORITY` int(11) NOT NULL,\n  `STATE` varchar(16) NOT NULL,\n  `JOB_NAME` varchar(200) DEFAULT NULL,\n  `JOB_GROUP` varchar(200) DEFAULT NULL,\n  `IS_NONCONCURRENT` varchar(1) DEFAULT NULL COMMENT '是否非并行执行',\n  `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL COMMENT '是否持久化',\n  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),\n  KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),\n  KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已触发的 Trigger相关的状态信息，以及关联 Job 的执行信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_JOB_DETAILS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;\nCREATE TABLE `QRTZ_JOB_DETAILS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `JOB_CLASS_NAME` varchar(250) NOT NULL,\n  `IS_DURABLE` varchar(1) NOT NULL COMMENT '是否持久化，0不持久化，1持久化',\n  `IS_NONCONCURRENT` varchar(1) NOT NULL COMMENT '是否非并发，0非并发，1并发',\n  `IS_UPDATE_DATA` varchar(1) NOT NULL,\n  `REQUESTS_RECOVERY` varchar(1) NOT NULL COMMENT '是否可恢复，0不恢复，1恢复',\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储每一个已配置的 Job 的详细信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_LOCKS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_LOCKS`;\nCREATE TABLE `QRTZ_LOCKS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `LOCK_NAME` varchar(40) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储程序的悲观锁的信息(假如使用了悲观锁)' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_PAUSED_TRIGGER_GRPS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;\nCREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已暂停的 Trigger 组的信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SCHEDULER_STATE`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;\nCREATE TABLE `QRTZ_SCHEDULER_STATE` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL COMMENT '执行quartz实例的主机名',\n  `LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '实例将状态报告给集群中的其它实例的上一次时间',\n  `CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '实例间状态报告的时间频率',\n  PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储少量的有关 Scheduler 的状态信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SIMPLE_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复次数',\n  `REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复间隔',\n  `TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '已出发次数',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储简单的 Trigger，包括重复次数，间隔，以及已触的次数' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SIMPROP_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `STR_PROP_1` varchar(512) DEFAULT NULL,\n  `STR_PROP_2` varchar(512) DEFAULT NULL,\n  `STR_PROP_3` varchar(512) DEFAULT NULL,\n  `INT_PROP_1` int(11) DEFAULT NULL,\n  `INT_PROP_2` int(11) DEFAULT NULL,\n  `LONG_PROP_1` bigint(20) DEFAULT NULL,\n  `LONG_PROP_2` bigint(20) DEFAULT NULL,\n  `DEC_PROP_1` decimal(13,4) DEFAULT NULL,\n  `DEC_PROP_2` decimal(13,4) DEFAULT NULL,\n  `BOOL_PROP_1` varchar(1) DEFAULT NULL,\n  `BOOL_PROP_2` varchar(1) DEFAULT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_TRIGGERS`;\nCREATE TABLE `QRTZ_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PRIORITY` int(11) DEFAULT NULL,\n  `TRIGGER_STATE` varchar(16) NOT NULL,\n  `TRIGGER_TYPE` varchar(8) NOT NULL,\n  `START_TIME` bigint(13) NOT NULL,\n  `END_TIME` bigint(13) DEFAULT NULL,\n  `CALENDAR_NAME` varchar(200) DEFAULT NULL,\n  `MISFIRE_INSTR` smallint(2) DEFAULT NULL,\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),\n  KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已配置的 Trigger 的信息' /* `compression`='tokudb_zlib' */;\n\n\n--\n-- Table structure for table `server`\n--\n\nDROP TABLE IF EXISTS `server`;\nCREATE TABLE `server` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `host` varchar(255) DEFAULT NULL COMMENT 'host',\n  `nmon` varchar(255) DEFAULT NULL COMMENT 'nmon version',\n  `cpus` tinyint(4) DEFAULT NULL COMMENT 'logic cpu num',\n  `cpu_model` varchar(255) DEFAULT NULL COMMENT 'cpu 型号',\n  `dist` varchar(255) DEFAULT NULL COMMENT '发行版信息',\n  `kernel` varchar(255) DEFAULT NULL COMMENT '内核信息',\n  `ulimit` varchar(255) DEFAULT NULL COMMENT 'ulimit -n,ulimit -u',\n  `updatetime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ip`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `server_stat`\n--\n\nDROP TABLE IF EXISTS `server_stat`;\nCREATE TABLE `server_stat` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `cdate` date NOT NULL COMMENT '数据收集天',\n  `ctime` char(4) NOT NULL COMMENT '数据收集小时分钟',\n  `cuser` float DEFAULT NULL COMMENT '用户态占比',\n  `csys` float DEFAULT NULL COMMENT '内核态占比',\n  `cwio` float DEFAULT NULL COMMENT 'wio占比',\n  `c_ext` text COMMENT '子cpu占比',\n  `cload1` float DEFAULT NULL COMMENT '1分钟load',\n  `cload5` float DEFAULT NULL COMMENT '5分钟load',\n  `cload15` float DEFAULT NULL COMMENT '15分钟load',\n  `mtotal` float DEFAULT NULL COMMENT '总内存,单位M',\n  `mfree` float DEFAULT NULL COMMENT '空闲内存',\n  `mcache` float DEFAULT NULL COMMENT 'cache',\n  `mbuffer` float DEFAULT NULL COMMENT 'buffer',\n  `mswap` float DEFAULT NULL COMMENT 'cache',\n  `mswap_free` float DEFAULT NULL COMMENT 'cache',\n  `nin` float DEFAULT NULL COMMENT '网络入流量 单位K/s',\n  `nout` float DEFAULT NULL COMMENT '网络出流量 单位k/s',\n  `nin_ext` text COMMENT '各网卡入流量详情',\n  `nout_ext` text COMMENT '各网卡出流量详情',\n  `tuse` int(11) DEFAULT NULL COMMENT 'tcp estab连接数',\n  `torphan` int(11) DEFAULT NULL COMMENT 'tcp orphan连接数',\n  `twait` int(11) DEFAULT NULL COMMENT 'tcp time wait连接数',\n  `dread` float DEFAULT NULL COMMENT '磁盘读速率 单位K/s',\n  `dwrite` float DEFAULT NULL COMMENT '磁盘写速率 单位K/s',\n  `diops` float DEFAULT NULL COMMENT '磁盘io速率 交互次数/s',\n  `dbusy` float DEFAULT NULL COMMENT '磁盘io带宽使用百分比',\n  `d_ext` text COMMENT '磁盘各分区占比',\n  `dspace` text COMMENT '磁盘各分区空间使用率',\n  PRIMARY KEY (`ip`,`cdate`,`ctime`),\n  KEY `idx_cdate` (`cdate`),\n  KEY `idx_cdate_ctime` (`cdate`,`ctime`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `system_config`\n--\n\nDROP TABLE IF EXISTS `system_config`;\nCREATE TABLE `system_config` (\n  `config_key` varchar(255) NOT NULL COMMENT '配置key',\n  `config_value` varchar(512) NOT NULL COMMENT '配置value',\n  `info` varchar(255) NOT NULL COMMENT '配置说明',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `order_id` int(11) NOT NULL COMMENT '顺序',\n  PRIMARY KEY (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统配置';\n\n-- ----------------------------\n--  Records of `system_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_config` VALUES ('cachecloud.admin.user.name','admin','cachecloud-admin用户名',1,11),('cachecloud.admin.user.password','admin','cachelcoud-admin密码',1,12),('cachecloud.app.client.conn.threshold','2000','应用连接数报警阀值',1,33),('cachecloud.base.dir','/opt','cachecloud根目录，要和cachecloud-init.sh脚本中的目录一致',1,31),('cachecloud.contact','user1:(xx@zz.com, user1:135xxxxxxxx)<br/>user2: (user2@zz.com, user2:138xxxxxxxx)','值班联系人信息',1,14),('cachecloud.cookie.domain','','cookie登录方式所需要的域名',1,22),('cachecloud.email.alert.interface','','邮件报警接口(参考报警接口规范)',1,24),('cachecloud.machine.ssh.name','cachecloud-open','机器ssh用户名',1,2),('cachecloud.machine.ssh.password','cachecloud-open','机器ssh密码',1,3),('cachecloud.machine.ssh.port','22','机器ssh端口',1,10),('cachecloud.machine.stats.cron.minute','1','机器性能统计周期(分钟)',1,35),('cachecloud.nmon.dir','/opt/cachecloud','nmon安装目录',1,32),('cachecloud.owner.email','xx@sohu.com,yy@qq.com','邮件报警(逗号隔开)',1,21),('cachecloud.owner.phone','xxx,yyy','手机号报警(逗号隔开)',1,21),('cachecloud.owner.weChat','xxx,yyy','微信号报警(逗号隔开)',1,21),('cachecloud.public.key.pem','/opt/ssh/id_rsa','密钥路径',1,5),('cachecloud.public.user.name','cachecloud-open','公钥用户名',1,4),('cachecloud.ssh.auth.type','1','ssh授权方式',1,1),('cachecloud.superAdmin','admin,xx,yy','超级管理员组',1,13),('cachecloud.user.login.type','1','用户登录状态保存方式(session或cookie)',1,22),('cachecloud.weChat.alert.interface','','微信报警接口(参考报警接口规范)',1,23),('cachecloud.whether.schedule.clean.data','false','是否定期清理统计数据',1,34),('machine.load.alert.ratio','8.0','机器负载报警阀值',1,32);\nCOMMIT;\n\n-- ----------------------------\n--  Table structure for `system_resource`\n-- ----------------------------\nDROP TABLE IF EXISTS `system_resource`;\nCREATE TABLE `system_resource` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '资源ID',\n  `name` varchar(64) NOT NULL COMMENT '资源名称',\n  `intro` varchar(255) DEFAULT NULL COMMENT '资源说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:仓库地址 2:脚本 3:资源包 4:公钥/私钥 6:目录管理 7:迁移工具管理',\n  `lastmodify` datetime DEFAULT NULL COMMENT '最后更新时间',\n  `dir` varchar(128) DEFAULT NULL COMMENT '资源路径',\n  `url` varchar(128) DEFAULT NULL COMMENT '仓库地址',\n  `ispush` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:未推送 1:已推送',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `username` varchar(255) DEFAULT NULL COMMENT '最后修改人',\n  `task_id` bigint(11) DEFAULT NULL COMMENT '迁移任务id',\n  `compile_info` varchar(255) DEFAULT NULL COMMENT '编译信息',\n  `order_num` int(6) NOT NULL DEFAULT '0' COMMENT '排序',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `system_resource`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_resource` VALUES (1,'cachecloud-init.sh','容器初始化脚本',2,'2020-07-15 18:35:41','/script','',0,1,NULL,NULL,NULL,0),(2,'x.x.x.x',NULL,1,'2020-08-10 10:31:51','/opt/download/software/cachecloud/resource','http://x.x.x.x/software/cachecloud/resource',0,1,'admin',0,NULL,0),(4,'cachecloud-env.sh','宿主环境脚本',2,'2020-07-15 18:36:28','/script','',0,1,NULL,NULL,NULL,0),(5,'id_rsa','私钥文件',4,'2020-07-07 10:45:39','/ssh','',0,1,NULL,NULL,NULL,0),(6,'id_rsa.pub','公钥文件',4,'2020-07-07 10:45:45','/ssh','',0,1,NULL,NULL,NULL,0),(12,'redis-4.0.14','redis 4.0.14资源包',3,'2020-08-10 09:52:41','/redis','http://download.redis.io/releases/redis-4.0.14.tar.gz',0,1,'admin',532,NULL,0),(21,'/script','脚本目录管理',6,'2020-08-10 10:51:34','',NULL,0,1,'admin',0,NULL,0),(28,'/ssh','ssh目录',6,'2020-07-20 17:55:03',NULL,NULL,0,1,'admin',0,NULL,0),(29,'redis-3.0.7','redis3.0.7 资源包',3,'2020-08-10 09:53:32','/redis','http://download.redis.io/releases/redis-3.0.7.tar.gz',0,1,'admin',529,NULL,0),(31,'redis-3.2.12','redis 3.2.12 资源包',3,'2020-08-10 15:08:21','/redis','http://download.redis.io/releases/redis-3.2.12.tar.gz',0,1,'admin',530,NULL,0),(32,'/redis','redis资源包管理',6,'2020-07-20 17:54:59',NULL,NULL,0,1,'admin',0,NULL,0),(33,'/tool','迁移工具资源包',6,'2020-07-20 17:54:53',NULL,NULL,0,1,'admin',0,NULL,0),(37,'redis-5.0.9','redis5.0.9 资源包',3,'2020-08-10 09:51:41','/redis','http://download.redis.io/releases/redis-5.0.9.tar.gz',0,1,'admin',533,NULL,0),(40,'redis-shake-2.0.3','redis 2.0.3\\n修复fix 5.0迁移类型问题',7,'2020-08-11 10:53:26','/tool','https://github.com/alibaba/RedisShake/releases/download/release-v2.0.3-20200724/redis-shake-v2.0.3.tar.gz',0,1,'admin',518,NULL,0);\nCOMMIT;\n\n--\n-- Table structure for table `task_queue`\n--\n\nDROP TABLE IF EXISTS `task_queue`;\nCREATE TABLE `task_queue` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `important_info` varchar(255) NOT NULL DEFAULT '' COMMENT '重要信息',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `param` longtext NOT NULL COMMENT '任务参数(json):随着任务变化',\n  `init_param` longtext NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2中断，3失败',\n  `parent_task_id` bigint(20) NOT NULL COMMENT '父任务id',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `priority` int(11) NOT NULL COMMENT '优先级',\n  `error_code` int(11) NOT NULL COMMENT '错误代码',\n  `error_msg` varchar(255) NOT NULL COMMENT '错误消息',\n  `task_note` varchar(255) NOT NULL COMMENT '备注',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务表';\n\n--\n-- Table structure for table `task_step_flow`\n--\n\nDROP TABLE IF EXISTS `task_step_flow`;\nCREATE TABLE `task_step_flow` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `task_id` bigint(20) NOT NULL COMMENT '任务id',\n  `child_task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '子任务id',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0未开始、1成功、2中断、3跳过、4失败',\n  `log` text COMMENT '日志',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_task_class_step` (`task_id`,`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤流表';\n\n--\n-- Table structure for table `task_step_meta`\n--\n\nDROP TABLE IF EXISTS `task_step_meta`;\nCREATE TABLE `task_step_meta` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `step_desc` varchar(255) NOT NULL COMMENT '步骤描述',\n  `ops_device` varchar(255) NOT NULL COMMENT '运维建议',\n  `timeout` int(11) NOT NULL COMMENT '超时时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_class_step` (`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤元数据表';\n/*!40101 SET character_set_client = @saved_cs_client */;\n\n-- ----------------------------\n--  Table structure for `standard_statistics`\n-- ----------------------------\nDROP TABLE IF EXISTS `standard_statistics`;\nCREATE TABLE `standard_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口/hostId',\n  `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n  `info_json` text NOT NULL COMMENT '收集的json数据',\n  `diff_json` text NOT NULL COMMENT '上一次收集差异的json数据',\n  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `cluster_info_json` varchar(20480) NOT NULL DEFAULT '' COMMENT '收集的cluster info json数据',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n  KEY `idx_create_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_alert_record`\n--\nDROP TABLE IF EXISTS `app_alert_record`;\nCREATE TABLE `app_alert_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `visible_type` int(1) NOT NULL COMMENT '可见类型（0：均可见；1：仅管理员可见；）',\n  `important_level` int(1) NOT NULL COMMENT '重要类型（0：一般；1：重要；2：紧急）',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `app_id` bigint(20) DEFAULT NULL COMMENT 'app id',\n  `instance_id` bigint(20) DEFAULT NULL COMMENT '实例id',\n  `ip` varchar(16) COLLATE utf8_bin DEFAULT NULL COMMENT '机器ip',\n  `port` int(10) DEFAULT NULL COMMENT '端口号',\n  `title` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '报警标题',\n  `content` varchar(500) COLLATE utf8_bin NOT NULL COMMENT '报警内容',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`),\n  KEY `ip` (`ip`),\n  KEY `idx_inst_id` (`instance_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='报警记录表';\n\n--\n-- Table structure for table `config_restart_record`\n--\nDROP TABLE IF EXISTS `config_restart_record`;\nCREATE TABLE `config_restart_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `app_name` varchar(36) NOT NULL COMMENT '应用名称',\n  `operate_type` char(1) NOT NULL COMMENT '操作类型（0:滚动重启，1:修改配置强制重启；2：修改配置）',\n  `param` varchar(2000) NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2成功，3失败，4配置修改待重启',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `log` longtext COMMENT '日志信息',\n  `user_name` varchar(64) DEFAULT NULL COMMENT '操作人员姓名',\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `instances` varchar(1000) DEFAULT NULL COMMENT '涉及实例id列表的json格式',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='重启记录表';\n\n--\n-- Table structure for table `module_info`\n--\nDROP TABLE IF EXISTS `module_info`;\nCREATE TABLE `module_info` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL,\n  `git_url` varchar(255) NOT NULL DEFAULT '' COMMENT 'git resource',\n  `info` varchar(128) DEFAULT NULL COMMENT '模块信息说明',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `NAMEKEY` (`name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Redis模块信息表';\n\n--\n-- Table structure for table `module_version`\n--\nDROP TABLE IF EXISTS `module_version`;\nCREATE TABLE `module_version` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `module_id` int(11) NOT NULL,\n  `version_id` int(11) NOT NULL COMMENT '关联版本号',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  `so_path` varchar(255) DEFAULT NULL COMMENT '编译后so库的地址',\n  `tag` varchar(64) NOT NULL COMMENT '模块版本号',\n  `status` int(255) NOT NULL DEFAULT '0' COMMENT '是否可用(关联so地址)：0 不可用 1：可用',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Redis模块版本管理表';\n\n--\n-- Table structure for table `app_import`\n--\nDROP TABLE IF EXISTS `app_import`;\nCREATE TABLE `app_import` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '目标应用id',\n  `instance_info` text COMMENT '源redis实例信息',\n  `redis_password` varchar(200) DEFAULT NULL COMMENT '源redis密码',\n  `status` int(11) DEFAULT NULL COMMENT '迁移状态：PREPARE(0, \"准备\", \"应用导入-未开始\"),     START(1, \"进行中...\", \"应用导入-开始\"),     ERROR(2, \"error\", \"应用导入-出错\"),     VERSION_BUILD_START(11, \"进行中...\", \"新建redis版本-进行中\"),     VERSION_BUILD_ERROR(12, \"error\", \"新建redis版本-出错\"),     VERSION_BUILD_END(20, \"成功\", \"新建redis版本-完成\"),     APP_BUILD_INIT(21, \"准备就绪\", \"新建redis应用-准备就绪\"),     APP_BUILD_START(22, \"进行中...\", \"新建redis应用-进行中\"),     APP_BUILD_ERROR(23, \"error\", \"新建redis应用-出错\"),     APP_BUILD_END(30, \"成功\", \"新建redis应用-完成\"),     MIGRATE_INIT(31, \"准备就绪\", \"数据迁移-准备就绪\"),     MIGRATE_START(32, \"进行中...\", \"数据迁移-进行中\"),     MIGRATE_ERROR(33, \"error\", \"数据迁移-出错\"),     MIGRATE_END(3, \"成功\", \"应用导入-成功\")',\n  `step` int(11) DEFAULT NULL COMMENT '导入阶段',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `migrate_id` bigint(20) DEFAULT NULL COMMENT '数据迁移id',\n  `mem_size` int(11) DEFAULT NULL COMMENT '目标应用内存大小，单位G',\n  `redis_version_name` varchar(20) DEFAULT NULL COMMENT '目标应用redis版本，格式：redis-x.x.x',\n  `app_build_task_id` bigint(20) DEFAULT NULL COMMENT '目标应用部署任务id',\n  `source_type` int(11) DEFAULT NULL COMMENT '源redis类型：7:cluster, 6:sentinel, 5:standalone',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- redis_module_config definition\n\nCREATE TABLE `redis_module_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '配置名',\n  `config_value` varchar(512) NOT NULL COMMENT '配置值',\n  `info` varchar(512) NOT NULL COMMENT '配置说明',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  `type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n  `status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n  `version_id` int(11) NOT NULL COMMENT 'Module version版本表主键id',\n  `refresh` tinyint(4) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n  `module_id` int(11) NOT NULL DEFAULT '7' COMMENT 'Module 信息表id',\n  `config_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '配置类型，0：加载和运行配置；1：加载时配置；2：运行时配置',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='redis模块配置表';\n\n\n-- app_to_module definition\n\nCREATE TABLE `app_to_module` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `module_id` int(11) NOT NULL COMMENT '模块info id',\n  `module_version_id` int(11) NOT NULL COMMENT '模块版本id',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_to_module_un` (`app_id`,`module_id`,`module_version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='应用与模块关系表';\n"
  },
  {
    "path": "cachecloud-web/sql/3.3.sql",
    "content": "-- MySQL dump 10.15\n-- Database: cachecloud_open\n\nSET NAMES utf8;\nSET FOREIGN_KEY_CHECKS = 0;\n\n--\n-- Table structure for table `app_audit`\n--\n\nDROP TABLE IF EXISTS `app_audit`;\nCREATE TABLE `app_audit` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人的id',\n  `user_name` varchar(64) NOT NULL COMMENT '用户名',\n  `type` tinyint(4) NOT NULL COMMENT '申请类型:0:申请应用,1:应用扩容,2:修改配置',\n  `param1` varchar(600) DEFAULT NULL COMMENT '预留参数1',\n  `param2` varchar(600) DEFAULT NULL COMMENT '预留参数2',\n  `param3` varchar(600) DEFAULT NULL COMMENT '预留参数3',\n  `info` varchar(360) NOT NULL COMMENT '申请描述',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:等待审批; 1:审批通过; -1:驳回',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `refuse_reason` varchar(360) DEFAULT NULL COMMENT '驳回理由',\n  `task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '任务id',\n  `operate_id` bigint(20) DEFAULT NULL COMMENT '工单处理人',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid` (`app_id`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_status_create_time` (`status`,`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用审核表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_audit_log`\n--\n\nDROP TABLE IF EXISTS `app_audit_log`;\nCREATE TABLE `app_audit_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '审批操作人id',\n  `info` longtext NOT NULL COMMENT 'app审批的详细信息',\n  `type` tinyint(4) NOT NULL,\n  `create_time` datetime NOT NULL,\n  `app_audit_id` bigint(20) NOT NULL COMMENT '审批id',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit_appid` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app审核日志表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_client_command_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_command_minute_statistics`;\nCREATE TABLE `app_client_command_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `command` varchar(20) NOT NULL COMMENT '命令明文',\n  `cost` bigint(20) DEFAULT NULL COMMENT '命令累计毫秒耗时',\n  `bytes_in` bigint(20) DEFAULT NULL COMMENT '输入流量',\n  `bytes_out` bigint(20) DEFAULT NULL COMMENT '输出流量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `count` int(11) DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__appid_client_command_currentMin` (`app_id`,`client_ip`,`command`,`current_min`),\n  KEY `idx_currentmin_appid_count_cost` (`current_min`,`app_id`,`count`,`cost`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟命令调用上报数据';\n\n--\n-- Table structure for table `app_client_exception_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_client_exception_minute_statistics`;\nCREATE TABLE `app_client_exception_minute_statistics` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `current_min` bigint(20) NOT NULL COMMENT '统计时间',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip',\n  `type` tinyint(4) NOT NULL COMMENT '0:connect exception;1:command exception',\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `node` varchar(30) NOT NULL COMMENT '节点信息host:port',\n  `count` bigint(20) DEFAULT NULL COMMENT '累计连接失败次数',\n  `cost` bigint(20) DEFAULT NULL COMMENT '累计连接失败毫秒耗时',\n  `latency_commands` varchar(255) DEFAULT NULL COMMENT '统计命令topN id,逗号分隔',\n  `redis_pool_config` varchar(255) DEFAULT NULL COMMENT 'redis连接池配置信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx__client_node_type_currentMin` (`client_ip`,`node`,`type`,`current_min`),\n  KEY `idx_appid_current_min` (`app_id`,`current_min`),\n  KEY `idx_current_min` (`current_min`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端每分钟异常上报数据';\n\n--\n-- Table structure for table `app_client_latency_command`\n--\n\nDROP TABLE IF EXISTS `app_client_latency_command`;\nCREATE TABLE `app_client_latency_command` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `command` varchar(255) NOT NULL COMMENT '命令明文',\n  `size` bigint(20) DEFAULT NULL COMMENT '参数长度',\n  `args` varchar(255) DEFAULT NULL COMMENT '裁剪后参数明文',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `invoke_time` bigint(20) DEFAULT NULL COMMENT '命令调用时间戳',\n  PRIMARY KEY (`id`),\n  KEY `idx_createtime` (`create_time`),\n  KEY `idx_command` (`command`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端异常命令调用详情';\n\n--\n-- Table structure for table `app_client_statistic_gather`\n--\n\nDROP TABLE IF EXISTS `app_client_statistic_gather`;\nCREATE TABLE `app_client_statistic_gather` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gather_time` varchar(20) NOT NULL COMMENT '统计时间，格式yyyy-mm-dd',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `cmd_count` bigint(20) DEFAULT '0' COMMENT '命令调用次数',\n  `conn_exp_count` bigint(20) DEFAULT '0' COMMENT '连接异常次数',\n  `avg_cmd_cost` double DEFAULT '0' COMMENT '命令调用平均耗时，单位毫秒',\n  `avg_cmd_exp_cost` double DEFAULT '0' COMMENT '命令超时平均耗时，单位毫秒',\n  `avg_conn_exp_cost` double DEFAULT '0' COMMENT '连接异常平均耗时，单位毫秒',\n  `cmd_exp_count` bigint(20) DEFAULT '0' COMMENT '命令超时次数',\n  `instance_count` int(11) DEFAULT NULL COMMENT '应用实例数',\n  `avg_mem_frag_ratio` double DEFAULT NULL COMMENT '平均碎片率',\n  `mem_used_ratio` double DEFAULT NULL COMMENT '内存使用率',\n  `exception_count` bigint(20) DEFAULT '0' COMMENT '异常数（旧，待下线）',\n  `slow_log_count` bigint(20) DEFAULT '0' COMMENT '慢查询次数',\n  `latency_count` bigint(20) DEFAULT '0' COMMENT '延迟事件次数',\n  `object_size` bigint(20) DEFAULT '0' COMMENT '存储对象数',\n  `used_memory` bigint(20) DEFAULT '0' COMMENT '内存占用 byte',\n  `used_memory_rss` bigint(20) DEFAULT '0' COMMENT '物理内存占用 byte',\n  `max_cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗(单位:秒)',\n  `max_cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗(单位:秒)',\n  `connected_clients` bigint(20) DEFAULT '0' COMMENT '应用客户端连接数',\n  `topology_exam_result` tinyint(4) DEFAULT NULL COMMENT '拓扑诊断结果，0：正常，1：异常',\n  `used_disk` bigint(20) DEFAULT '0' COMMENT '磁盘占用byte',\n  `server_cmd_count` bigint(20) NOT NULL DEFAULT '0' COMMENT 'server端统计的命令调用次数',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_appid_gathertime` (`app_id`,`gather_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报数据全天统计';\n\n\n\n--\n-- Table structure for table `app_client_version_statistic`\n--\n\nDROP TABLE IF EXISTS `app_client_version_statistic`;\nCREATE TABLE `app_client_version_statistic` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `client_ip` varchar(20) NOT NULL COMMENT '客户端ip地址',\n  `client_version` varchar(20) NOT NULL COMMENT '客户端版本',\n  `report_time` datetime DEFAULT NULL COMMENT '上报时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_client_ip` (`app_id`,`client_ip`),\n  KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端上报版本信息统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_daily`\n--\n\nDROP TABLE IF EXISTS `app_daily`;\nCREATE TABLE `app_daily` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `date` date NOT NULL COMMENT '日期',\n  `create_time` datetime NOT NULL,\n  `slow_log_count` bigint(20) NOT NULL COMMENT '慢查询个数',\n  `client_exception_count` bigint(20) NOT NULL COMMENT '客户端异常个数',\n  `max_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟最大客户端连接数',\n  `avg_minute_client_count` bigint(20) NOT NULL COMMENT '每分钟平均客户端连接数',\n  `max_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟最大命令数',\n  `avg_minute_command_count` bigint(20) NOT NULL COMMENT '每分钟平均命令数',\n  `avg_hit_ratio` double NOT NULL COMMENT '平均命中率',\n  `min_minute_hit_ratio` double NOT NULL COMMENT '每分钟最小命中率',\n  `max_minute_hit_ratio` double NOT NULL COMMENT '每分钟最大命中率',\n  `avg_used_memory` bigint(20) NOT NULL COMMENT '最大内存使用量',\n  `max_used_memory` bigint(20) NOT NULL COMMENT '平均内存使用量',\n  `expired_keys_count` bigint(20) NOT NULL COMMENT '过期键个数',\n  `evicted_keys_count` bigint(20) NOT NULL COMMENT '剔除键个数',\n  `avg_minute_net_input_byte` double NOT NULL COMMENT '每分钟平均网络input量',\n  `max_minute_net_input_byte` double NOT NULL COMMENT '每分钟最大网络input量',\n  `avg_minute_net_output_byte` double NOT NULL COMMENT '每分钟平均网络output量',\n  `max_minute_net_output_byte` double NOT NULL COMMENT '每分钟最大网络output量',\n  `avg_object_size` bigint(20) NOT NULL COMMENT '键个数平均值',\n  `max_object_size` bigint(20) NOT NULL COMMENT '键个数最大值',\n  `big_key_times` bigint(20) NOT NULL COMMENT 'bigkey次数',\n  `big_key_info` varchar(512) COLLATE utf8_bin NOT NULL COMMENT 'bigkey详情',\n  `client_cmd_count` bigint(20) NOT NULL COMMENT '累计命令调用次数',\n  `client_avg_cmd_cost` double NOT NULL COMMENT '平均命令调用耗时',\n  `client_conn_exp_count` bigint(20) NOT NULL COMMENT '累计连接异常事件次数',\n  `client_avg_conn_exp_cost` double NOT NULL COMMENT '平均连接异常事件耗时',\n  `client_cmd_exp_count` bigint(20) NOT NULL COMMENT '累计命令超时事件次数',\n  `client_avg_cmd_exp_cost` double NOT NULL COMMENT '平均命令超时事件耗时',\n  `avg_used_disk` bigint(20) NOT NULL COMMENT '平均磁盘使用量',\n  `max_used_disk` bigint(20) NOT NULL COMMENT '最大磁盘使用量',\n  PRIMARY KEY (`id`),\n  KEY `idx_appid_date` (`app_id`,`date`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='app日报';\n\n--\n-- Table structure for table `app_data_migrate_status`\n--\n\nDROP TABLE IF EXISTS `app_data_migrate_status`;\nCREATE TABLE `app_data_migrate_status` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `migrate_machine_ip` varchar(255) NOT NULL COMMENT '迁移工具所在机器ip',\n  `migrate_machine_port` int(11) NOT NULL COMMENT '迁移工具所占port',\n  `source_migrate_type` tinyint(4) NOT NULL COMMENT '源迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `source_servers` varchar(2048) NOT NULL COMMENT '源实例列表',\n  `target_migrate_type` tinyint(4) NOT NULL COMMENT '目标迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy',\n  `target_servers` varchar(2048) NOT NULL COMMENT '目标实例列表',\n  `source_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '源应用id',\n  `target_app_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '目标应用id',\n  `user_id` bigint(20) NOT NULL COMMENT '操作人',\n  `status` tinyint(4) NOT NULL COMMENT '迁移执行状态,0:开始,1:结束,2:异常',\n  `start_time` datetime NOT NULL COMMENT '迁移开始执行时间',\n  `end_time` datetime DEFAULT NULL COMMENT '迁移结束执行时间',\n  `log_path` varchar(255) NOT NULL COMMENT '日志文件路径',\n  `config_path` varchar(255) NOT NULL COMMENT '配置文件路径',\n  `migrate_id` varchar(50) DEFAULT NULL COMMENT 'migrate id',\n  `migrate_tool` tinyint(4) DEFAULT NULL COMMENT 'migrate_tool, 0:redis-shake,1:redis-migrate-tool',\n  `redis_source_version` varchar(20) DEFAULT NULL,\n  `redis_target_version` varchar(20) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用迁移记录详情';\n\n--\n-- Table structure for table `app_desc`\n--\n\nDROP TABLE IF EXISTS `app_desc`;\nCREATE TABLE `app_desc` (\n  `app_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '应用id',\n  `name` varchar(36) NOT NULL COMMENT '应用名',\n  `user_id` bigint(20) NOT NULL COMMENT '申请人id',\n  `status` tinyint(4) NOT NULL COMMENT '应用状态, 0未分配，1申请未审批，2审批并发布 3:应用下线',\n  `intro` varchar(255) NOT NULL COMMENT '应用描述',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `passed_time` datetime NOT NULL COMMENT '审批通过时间',\n  `type` int(10) NOT NULL DEFAULT '0' COMMENT 'cache类型，1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud ,5. redis-sentinel ,6.redis-standalone ',\n  `officer` varchar(32) NOT NULL COMMENT '负责人，中文',\n  `ver_id` int(11) NOT NULL COMMENT '版本',\n  `is_test` tinyint(4) DEFAULT '0' COMMENT '是否测试：1是0否',\n  `need_persistence` tinyint(4) DEFAULT '1' COMMENT '是否需要持久化: 1是0否',\n  `need_hot_back_up` tinyint(4) DEFAULT '1' COMMENT '是否需要热备: 1是0否',\n  `has_back_store` tinyint(4) DEFAULT '1' COMMENT '是否有后端数据源: 1是0否',\n  `forecase_qps` int(11) DEFAULT NULL COMMENT '预估qps',\n  `forecast_obj_num` int(11) DEFAULT NULL COMMENT '预估条目数',\n  `mem_alert_value` int(11) DEFAULT NULL COMMENT '内存报警阀值',\n  `client_machine_room` varchar(36) DEFAULT NULL COMMENT '客户端机房信息',\n  `client_conn_alert_value` int(11) DEFAULT '2000' COMMENT '客户端连接报警阀值',\n  `app_key` varchar(255) DEFAULT NULL COMMENT '应用秘钥',\n  `important_level` tinyint(4) NOT NULL DEFAULT '2' COMMENT '应用级别，1:最重要，2:一般重要，3:一般',\n  `password` varchar(255) DEFAULT '' COMMENT 'redis密码',\n  `custom_password` varchar(255) DEFAULT NULL COMMENT '自定义密码',\n  `hit_precent_alert_value` int(11) DEFAULT '0' COMMENT '命中率报警阀值 0:不报警 ',\n  `is_access_monitor` int(11) DEFAULT '0' COMMENT '是否接入全局监控报警 默认0,0:不接入监控 1:接入监控',\n  `app_fsync_value` int(11) DEFAULT '1' COMMENT '应用刷盘策略 1:主从节点appdendfsync=everysec 2:主从节点 appdendfsync=no',\n  `version_id` int(11) NOT NULL DEFAULT '1' COMMENT 'Redis版本表主键id',\n  `maxmemory_policy` tinyint(4) DEFAULT NULL COMMENT '淘汰策略(0：noeviction; 1:allkeys-lru;2:allkeys-lfu;3:volatile-lru;4:volatile-lfu;5:allkeys-random;6:volatile-random;7:volatile-ttl)',\n  `persistence_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '持久化类型（0：常规；1：主aof自动刷盘；从常规；2：主关闭aof，从常规）',\n  PRIMARY KEY (`app_id`),\n  UNIQUE KEY `uidx_app_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='app应用描述' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_command_statistics`;\nCREATE TABLE `app_hour_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHH',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`command_name`,`collect_time`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每小时命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_hour_statistics`\n--\n\nDROP TABLE IF EXISTS `app_hour_statistics`;\nCREATE TABLE `app_hour_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHH',\n  `hits` bigint(20) NOT NULL COMMENT '每小时命中数量和',\n  `misses` bigint(20) NOT NULL COMMENT '每小时未命中数量和',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '每小时内存占用最大值',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '每小时过期key数量和',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '每小时驱逐key数量和',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '每小时客户端连接数最大值',\n  `object_size` bigint(20) NOT NULL COMMENT '每小时存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '每小时参与累加实例数最小值',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '每小时修改时间最大值',\n  `used_disk` bigint(20) NOT NULL DEFAULT '0' COMMENT '磁盘占用（字节）',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用统计数据每小时统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_command_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_command_statistics`;\nCREATE TABLE `app_minute_command_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '统计时间:格式yyyyMMddHHmm',\n  `command_name` varchar(60) NOT NULL COMMENT '命令名称',\n  `command_count` bigint(20) NOT NULL COMMENT '命令执行次数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`,`command_name`),\n  KEY `idx_create_time` (`create_time`),\n  KEY `idx_modify_time` (`modify_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用的每分钟命令统计' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_minute_statistics`\n--\n\nDROP TABLE IF EXISTS `app_minute_statistics`;\nCREATE TABLE `app_minute_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n  `hits` bigint(20) NOT NULL COMMENT '命中数量',\n  `misses` bigint(20) NOT NULL COMMENT '未命中数量',\n  `command_count` bigint(20) DEFAULT '0' COMMENT '命令总数',\n  `used_memory` bigint(20) NOT NULL COMMENT '内存占用',\n  `used_memory_rss` bigint(20) NOT NULL DEFAULT '0' COMMENT '物理内存占用',\n  `expired_keys` bigint(20) NOT NULL COMMENT '过期key数量',\n  `evicted_keys` bigint(20) NOT NULL COMMENT '驱逐key数量',\n  `net_input_byte` bigint(20) DEFAULT '0' COMMENT '网络输入字节',\n  `net_output_byte` bigint(20) DEFAULT '0' COMMENT '网络输出字节',\n  `connected_clients` int(10) NOT NULL COMMENT '客户端连接数',\n  `object_size` bigint(20) NOT NULL COMMENT '每分钟存储对象数最大值',\n  `cpu_sys` bigint(20) DEFAULT '0' COMMENT '进程系统态消耗',\n  `cpu_user` bigint(20) DEFAULT '0' COMMENT '进程用户态消耗',\n  `cpu_sys_children` bigint(20) DEFAULT '0' COMMENT '子进程系统态消耗',\n  `cpu_user_children` bigint(20) DEFAULT '0' COMMENT '子进程用户态消耗',\n  `accumulation` int(10) NOT NULL DEFAULT '0' COMMENT '参与累加实例数',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  `used_disk` bigint(20) NOT NULL DEFAULT '0' COMMENT '磁盘占用（字节）',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_id` (`app_id`,`collect_time`),\n  KEY `idx_create_time` (`create_time`) USING BTREE,\n  KEY `idx_modify_time` (`modify_time`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;\n\n--\n-- Table structure for table `app_to_user`\n--\n\nDROP TABLE IF EXISTS `app_to_user`;\nCREATE TABLE `app_to_user` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `user_id` bigint(20) NOT NULL COMMENT '用户id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `app_user`\n--\n\nDROP TABLE IF EXISTS `app_user`;\nCREATE TABLE `app_user` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL COMMENT '用户名',\n  `ch_name` varchar(255) NOT NULL COMMENT '中文名',\n  `email` varchar(64) NOT NULL COMMENT '邮箱',\n  `mobile` varchar(16) NOT NULL COMMENT '手机',\n  `type` int(4) NOT NULL DEFAULT '2' COMMENT '0管理员，1预留，2普通用户，-1无效',\n  `weChat` varchar(32) DEFAULT NULL COMMENT '微信号',\n  `isAlert` tinyint(4) NOT NULL DEFAULT '1' COMMENT '用户是否接收报警 0:不接收 1:接收',\n  `password` varchar(64) DEFAULT NULL COMMENT '密码',\n  `register_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',\n  `purpose` varchar(255) DEFAULT NULL COMMENT '使用目的',\n  `company` varchar(255) DEFAULT NULL COMMENT '公司名称',\n  `biz_id` bigint(20) DEFAULT NULL COMMENT '所属业务组id（app_biz）',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_user_name` (`name`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表' /* `compression`='tokudb_zlib' */;\n\n-- ----------------------------\n--  Records of `app_user`\n-- ----------------------------\nBEGIN;\nINSERT INTO `app_user` VALUES ('1', 'admin', 'admin', 'admin@xxx.com', '13500000000', '0', null, '1', NULL, current_timestamp(), NULL, NULL, NULL);\nCOMMIT;\n\n--\n-- Table structure for table `brevity_schedule_resources`\n--\n\nDROP TABLE IF EXISTS `brevity_schedule_resources`;\nCREATE TABLE `brevity_schedule_resources` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `type` tinyint(4) NOT NULL COMMENT '类型,见:BrevityScheduleType',\n  `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '时间版本',\n  `host` varchar(16) NOT NULL COMMENT '资源ip',\n  `port` int(11) NOT NULL DEFAULT '0' COMMENT '端口',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_type_host_port` (`type`,`host`,`port`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='短频任务表';\n\n--\n-- Table structure for table `diagnostic_task_record`\n--\n\nDROP TABLE IF EXISTS `diagnostic_task_record`;\nCREATE TABLE `diagnostic_task_record` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '应用id',\n  `type` int(11) DEFAULT NULL COMMENT '诊断类型：0scan 1bigkey 2idle key 3hotkey 4del key 5slot analysis 6topology exam',\n  `task_id` bigint(20) DEFAULT NULL COMMENT '任务流id',\n  `audit_id` bigint(20) DEFAULT NULL COMMENT '审批id',\n  `status` int(11) DEFAULT NULL COMMENT '诊断状态：0开始 1结束 2异常',\n  `cost` bigint(20) DEFAULT NULL COMMENT '耗时，毫秒',\n  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `modify_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `redis_key` varchar(100) DEFAULT NULL COMMENT '结果的key',\n  `node` varchar(100) DEFAULT NULL COMMENT '实例，host:port',\n  `parent_task_id` bigint(20) DEFAULT NULL COMMENT '父任务id',\n  `diagnostic_condition` varchar(1000) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '诊断条件',\n  `param1` varchar(100) DEFAULT NULL COMMENT '备用参数1',\n  `param2` varchar(100) DEFAULT NULL COMMENT '备用参数2',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='应用诊断记录';\n\n--\n-- Table structure for table `instance_alert_configs`\n--\n\nDROP TABLE IF EXISTS `instance_alert_configs`;\nCREATE TABLE `instance_alert_configs` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `alert_config` varchar(255) NOT NULL COMMENT '报警配置',\n  `alert_value` varchar(512) NOT NULL COMMENT '报警阀值',\n  `config_info` varchar(255) NOT NULL COMMENT '配置说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:全局报警,2:实例报警',\n  `instance_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '0:全局配置，其他代表实例id',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `compare_type` tinyint(4) NOT NULL COMMENT '比较类型：1小于,2等于,3大于,4不等于',\n  `check_cycle` tinyint(4) NOT NULL COMMENT '1:一分钟,2:五分钟,3:半小时4:一个小时,5:一天',\n  `update_time` datetime NOT NULL COMMENT '报警配置更新时间',\n  `last_check_time` datetime NOT NULL COMMENT '上次检查时间',\n  `important_level` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '重要程度（0：一般；1：重要；2：紧急）',\n  `app_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '应用类型(0：redis;)',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_index` (`type`,`instance_id`,`alert_config`,`compare_type`,`app_type`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例报警阀值配置';\n\n-- ----------------------------\n--  Records of `instance_alert_configs`\n-- ----------------------------\nBEGIN;\nINSERT INTO `instance_alert_configs` VALUES\n                                         ('9', 'aof_current_size', '6000', 'aof当前尺寸(单位：MB)', '1', '0', '1', '3', '3', '2017-06-19 09:43:22', '2020-09-17 10:52:00', 0, 0),\n                                         ('10', 'aof_delayed_fsync', '3', '分钟aof阻塞个数', '1', '0', '1', '3', '1', '2017-06-19 10:38:19', '2020-09-17 11:09:00', 1, 0),\n                                         ('11', 'client_biggest_input_buf', '10', '输入缓冲区最大buffer大小(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 10:47:03', '2020-09-17 11:09:00', 1, 0),\n                                         ('12', 'client_longest_output_list', '50000', '输出缓冲区最大队列长度', '1', '0', '1', '3', '1', '2017-06-19 10:55:45', '2020-09-17 11:09:00', 1, 0),\n                                         ('13', 'instantaneous_ops_per_sec', '60000', '实时ops', '1', '0', '1', '3', '1', '2017-06-19 11:02:38', '2020-09-17 11:09:00', 1, 0),\n                                         ('14', 'latest_fork_usec', '400000', '上次fork所用时间(单位：微秒)', '1', '0', '1', '3', '5', '2017-06-19 11:21:35', '2020-09-16 16:51:00', 1, 0),\n                                         ('15', 'mem_fragmentation_ratio', '1.5', '内存碎片率(检测大于500MB)', '1', '0', '1', '3', '5', '2017-06-19 12:49:16', '2020-09-16 16:51:00', 0, 0),\n                                         ('16', 'rdb_last_bgsave_status', 'ok', '上一次bgsave状态', '1', '0', '1', '4', '4', '2017-06-19 14:15:21', '2020-09-17 10:19:00', 0, 0),\n                                         ('17', 'total_net_output_bytes', '5000', '分钟网络输出流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:39:44', '2020-09-17 11:09:00', 0, 0),\n                                         ('19', 'total_net_input_bytes', '1200', '分钟网络输入流量(单位：MB)', '1', '0', '1', '3', '1', '2017-06-19 16:45:44', '2020-09-17 11:09:00', 0, 0),\n                                         ('20', 'sync_partial_err', '0', '分钟部分复制失败次数', '1', '0', '1', '3', '1', '2017-06-19 18:34:41', '2020-09-17 11:09:00', 1, 0),\n                                         ('21', 'sync_partial_ok', '0', '分钟部分复制成功次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:01', '2020-09-17 11:09:00', 1, 0),\n                                         ('22', 'sync_full', '0', '分钟全量复制执行次数', '1', '0', '1', '3', '1', '2017-06-19 18:35:17', '2020-09-17 11:09:00', 1, 0),\n                                         ('23', 'rejected_connections', '0', '分钟拒绝连接数', '1', '0', '1', '3', '1', '2017-06-19 18:35:36', '2020-09-17 11:09:00', 2, 0),\n                                         ('54', 'master_slave_offset_diff', '20000000', '主从节点偏移量差(单位：字节)', '1', '0', '1', '3', '2', '2017-06-20 18:58:56', '2020-09-17 11:06:00', 0, 0),\n                                         ('56', 'cluster_state', 'ok', '集群状态', '1', '0', '1', '4', '1', '2017-06-21 18:01:52', '2020-09-17 11:09:00', 2, 0),\n                                         ('57', 'cluster_slots_ok', '16384', '集群成功分配槽个数', '1', '0', '1', '4', '1', '2017-06-21 18:02:04', '2020-09-17 11:09:00', 2, 0);\nCOMMIT;\n\n--\n-- Table structure for table `instance_big_key`\n--\n\nDROP TABLE IF EXISTS `instance_big_key`;\nCREATE TABLE `instance_big_key` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `audit_id` bigint(20) NOT NULL COMMENT 'audit id',\n  `role` tinyint(255) NOT NULL COMMENT '主从，1主2从，详见InstanceRoleEnum',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `big_key` varchar(255) NOT NULL COMMENT '键',\n  `type` varchar(16) NOT NULL COMMENT '类型:string,hash,list,set,zset',\n  `length` int(11) NOT NULL COMMENT '长度',\n  `create_time` datetime NOT NULL COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_audit` (`app_id`,`audit_id`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_create_time` (`create_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例bigkey列表';\n\n--\n-- Table structure for table `instance_config`\n--\n\nDROP TABLE IF EXISTS `instance_config`;\nCREATE TABLE `instance_config` (\n`id` int(11) NOT NULL AUTO_INCREMENT,\n`config_key` varchar(128) NOT NULL COMMENT '配置名',\n`config_value` varchar(512) NOT NULL COMMENT '配置值',\n`info` varchar(512) NOT NULL COMMENT '配置说明',\n`update_time` datetime NOT NULL COMMENT '更新时间',\n`type` mediumint(9) NOT NULL COMMENT '类型：2.cluster节点特殊配置, 5:sentinel节点配置, 6:redis普通节点',\n`status` tinyint(4) NOT NULL COMMENT '1有效,0无效',\n`version_id` int(11) NOT NULL COMMENT 'Redis版本表主键id',\n`refresh` mediumint(9) DEFAULT '0' COMMENT '是否可重置：0不可，1可重置',\n`value_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '取值类型（0：默认值 config_value；1：从主节点拷贝）',\nPRIMARY KEY (`id`),\nUNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例配置模板';\n\n-- ----------------------------\n--  Records of `instance_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO instance_config (id, config_key, config_value, info, update_time, `type`, status, version_id, refresh, value_type) VALUES\n    (1, 'cluster-enabled', 'yes', '是否开启集群模式', '2016-07-05 15:08:30', 2, 1, 29, 0, 0)\n    ,(2, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2016-07-05 15:08:30', 2, 1, 29, 0, 1)\n    ,(3, 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2016-07-05 15:08:30', 2, 1, 29, 0, 0)\n    ,(4, 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2016-07-05 15:08:30', 2, 1, 29, 0, 0)\n    ,(5, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2016-07-05 15:08:30', 2, 1, 29, 0, 0)\n    ,(6, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2016-07-05 15:08:31', 2, 1, 29, 0, 1)\n    ,(7, 'port', '%d', 'sentinel实例端口', '2016-07-05 15:08:31', 5, 1, 29, 0, 0)\n    ,(8, 'dir', '%s', '工作目录', '2016-07-05 15:08:31', 5, 1, 29, 0, 0)\n    ,(9, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2016-07-05 15:08:31', 5, 1, 29, 0, 0)\n    ,(10, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2016-07-05 15:08:31', 5, 1, 29, 0, 0)\n    ,(11, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2016-07-05 15:08:31', 5, 1, 29, 0, 0)\n    ,(12, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2016-07-05 15:08:31', 5, 1, 29, 0, 0)\n    ,(13, 'daemonize', 'no', '是否守护进程', '2016-07-14 14:00:05', 6, 1, 29, 0, 0)\n    ,(14, 'tcp-backlog', '511', 'TCP连接完成队列', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(15, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(16, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2016-12-06 11:40:46', 6, 1, 29, 0, 1)\n    ,(17, 'loglevel', 'notice', '日志级别', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(18, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(19, 'dir', '%s', 'redis工作目录', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(20, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(21, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(22, 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(23, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(24, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(25, 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(26, 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(27, 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(28, 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(29, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(30, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(31, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(32, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(33, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(34, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(35, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(36, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(37, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(38, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(39, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(40, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(41, 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2016-11-24 10:24:21', 6, 1, 29, 0, 0)\n    ,(42, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(43, 'hz', '10', '执行后台task数量,默认:10', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(44, 'port', '%d', '端口', '2016-07-05 15:08:31', 6, 1, 29, 0, 0)\n    ,(45, 'maxmemory', '%dmb', '当前实例最大可用内存', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(46, 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2016-07-05 15:08:31', 6, 1, 29, 0, 1)\n    ,(47, 'appendonly', 'yes', '开启append only持久化模式', '2016-07-05 15:08:32', 6, 1, 29, 0, 1)\n    ,(48, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(49, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(50, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(51, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(52, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(53, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(54, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(55, 'maxclients', '10000', '客户端最大连接数', '2016-07-05 15:08:32', 6, 1, 29, 0, 0)\n    ,(126, 'cluster-enabled', 'yes', '是否开启集群模式', '2018-09-18 18:23:03', 2, 1, 31, 0, 0)\n    ,(127, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2018-09-18 18:23:03', 2, 1, 31, 0, 1)\n    ,(128, 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2018-09-18 18:23:03', 2, 1, 31, 0, 0)\n    ,(129, 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2018-09-18 18:23:03', 2, 1, 31, 0, 0)\n    ,(130, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2018-09-18 18:23:03', 2, 1, 31, 0, 0)\n    ,(131, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2018-09-18 18:23:03', 2, 1, 31, 0, 1)\n    ,(132, 'port', '%d', 'sentinel实例端口', '2018-09-18 18:23:03', 5, 1, 31, 0, 0)\n    ,(133, 'dir', '%s', '工作目录', '2018-09-18 18:23:03', 5, 1, 31, 0, 0)\n    ,(134, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2018-09-18 18:23:03', 5, 1, 31, 0, 0)\n    ,(135, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2018-09-18 18:23:03', 5, 1, 31, 0, 0)\n    ,(136, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2018-09-18 18:23:03', 5, 1, 31, 0, 0)\n    ,(137, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2018-09-18 18:23:03', 5, 1, 31, 0, 0)\n    ,(138, 'daemonize', 'no', '是否守护进程', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(139, 'tcp-backlog', '511', 'TCP连接完成队列', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(140, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(141, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(142, 'loglevel', 'notice', '日志级别', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(143, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(144, 'dir', '%s', 'redis工作目录', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(145, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(146, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(147, 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(148, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(149, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(150, 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(151, 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(152, 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(153, 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(154, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(155, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(156, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(157, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(158, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(159, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2018-09-18 18:25:32', 6, 0, 31, 0, 1)\n    ,(160, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2018-09-18 18:25:40', 6, 0, 31, 0, 1)\n    ,(161, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(162, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(163, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(164, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(165, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(166, 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(167, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(168, 'hz', '10', '执行后台task数量,默认:10', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(169, 'port', '%d', '端口', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(170, 'maxmemory', '%dmb', '当前实例最大可用内存', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(171, 'maxmemory-policy', 'volatile-lru', '内存不够时,淘汰策略,默认:volatile-lru', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(172, 'appendonly', 'yes', '开启append only持久化模式', '2018-09-18 18:23:03', 6, 1, 31, 0, 1)\n    ,(173, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(174, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(175, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(176, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(177, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(178, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(179, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(180, 'maxclients', '10000', '客户端最大连接数', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(181, 'protected-mode', 'yes', '开启保护模式', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(182, 'bind', '0.0.0.0', '默认客户端都可连接', '2018-09-18 18:23:03', 6, 1, 31, 0, 0)\n    ,(185, 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2018-09-18 18:26:32', 6, 1, 31, 0, 0)\n    ,(186, 'list-compress-depth', '0', '压缩方式，0:不压缩', '2018-09-18 18:27:12', 6, 1, 31, 0, 0)\n    ,(253, 'protected-mode', 'no', '关闭保护模式', '2018-11-01 16:10:59', 5, 1, 31, 0, 0)\n    ,(354, 'cluster-enabled', 'yes', '是否开启集群模式', '2019-10-24 17:33:26', 2, 1, 12, 0, 0)\n    ,(355, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2019-10-24 17:33:26', 2, 1, 12, 0, 1)\n    ,(356, 'cluster-slave-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2019-10-24 17:33:26', 2, 1, 12, 0, 0)\n    ,(357, 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2019-10-24 17:33:26', 2, 1, 12, 0, 0)\n    ,(358, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2019-10-24 17:33:26', 2, 1, 12, 0, 0)\n    ,(359, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2019-10-24 17:33:26', 2, 1, 12, 0, 1)\n    ,(360, 'port', '%d', 'sentinel实例端口', '2019-10-24 17:33:26', 5, 1, 12, 0, 0)\n    ,(361, 'dir', '%s', '工作目录', '2019-10-24 17:33:26', 5, 1, 12, 0, 0)\n    ,(362, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2019-10-24 17:33:26', 5, 1, 12, 0, 0)\n    ,(363, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2019-10-24 17:33:26', 5, 1, 12, 0, 0)\n    ,(364, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2019-10-24 17:33:26', 5, 1, 12, 0, 0)\n    ,(365, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2019-10-24 17:33:26', 5, 1, 12, 0, 0)\n    ,(366, 'daemonize', 'no', '是否守护进程', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(367, 'tcp-backlog', '511', 'TCP连接完成队列', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(368, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(369, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(370, 'loglevel', 'notice', '日志级别', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(371, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(372, 'dir', '%s', 'redis工作目录', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(373, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(374, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(375, 'repl-ping-slave-period', '10', '指定slave定期ping master的周期,默认:10秒', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(376, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(377, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(378, 'repl-backlog-ttl', '7200', 'master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(379, 'slave-serve-stale-data', 'yes', '当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(380, 'slave-read-only', 'yes', 'slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(381, 'slave-priority', '100', 'slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(382, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(383, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(384, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(385, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(386, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(387, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2019-10-24 17:33:26', 6, 0, 12, 0, 1)\n    ,(388, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2019-10-24 17:33:26', 6, 0, 12, 0, 1)\n    ,(389, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(390, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(391, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(392, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(393, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(394, 'client-output-buffer-limit slave', '512mb 256mb 60', '客户端输出缓冲区限制(复制)', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(395, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(396, 'hz', '10', '执行后台task数量,默认:10', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(397, 'port', '%d', '端口', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(398, 'maxmemory', '%dmb', '当前实例最大可用内存', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(399, 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(400, 'appendonly', 'yes', '开启append only持久化模式', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(401, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(402, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(403, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(404, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(405, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(406, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(407, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(408, 'maxclients', '10000', '客户端最大连接数', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(409, 'protected-mode', 'yes', '开启保护模式', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(410, 'bind', '0.0.0.0', '默认客户端都可连接', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(411, 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(412, 'list-compress-depth', '0', '压缩方式，0:不压缩', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(413, 'always-show-logo', 'yes', 'redis启动是否显示logo', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(414, 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(415, 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(416, 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(417, 'slave-lazy-flush', 'yes', 'slave发起全量复制,是否采用flushall async清理老数据 默认值 no', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(418, 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2019-10-31 11:15:57', 6, 1, 12, 0, 0)\n    ,(419, 'protected-mode', 'no', '关闭sentinel保护模式', '2019-10-24 17:33:26', 5, 1, 12, 0, 0)\n    ,(420, 'activedefrag', 'no', '碎片整理开启', '2019-10-24 17:33:26', 6, 1, 12, 0, 1)\n    ,(421, 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(422, 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(423, 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(424, 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(425, 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2019-10-24 17:33:26', 6, 1, 12, 0, 0)\n    ,(506, 'cluster-enabled', 'yes', '是否开启集群模式', '2020-04-26 18:12:55', 2, 1, 37, 0, 0)\n    ,(507, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2020-04-26 18:12:55', 2, 1, 37, 0, 1)\n    ,(508, 'cluster-migration-barrier', '1', '主从迁移至少需要的从节点数,默认1个', '2020-04-26 18:12:55', 2, 1, 37, 0, 0)\n    ,(509, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2020-04-26 18:12:55', 2, 1, 37, 0, 0)\n    ,(510, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2020-04-26 18:12:55', 2, 1, 37, 0, 1)\n    ,(511, 'port', '%d', 'sentinel实例端口', '2020-04-26 18:12:55', 5, 1, 37, 0, 0)\n    ,(512, 'dir', '%s', '工作目录', '2020-04-26 18:12:55', 5, 1, 37, 0, 0)\n    ,(513, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2020-04-26 18:12:55', 5, 1, 37, 0, 0)\n    ,(514, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2020-04-26 18:12:55', 5, 1, 37, 0, 0)\n    ,(515, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2020-04-26 18:12:55', 5, 1, 37, 0, 0)\n    ,(516, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2020-04-26 18:12:55', 5, 1, 37, 0, 0)\n    ,(517, 'daemonize', 'no', '是否守护进程', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(518, 'tcp-backlog', '511', 'TCP连接完成队列', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(519, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(520, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(521, 'loglevel', 'notice', '日志级别', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(522, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(523, 'dir', '%s', 'redis工作目录', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(524, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(525, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(526, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(527, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(528, 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(529, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(530, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(531, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(532, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(533, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(534, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2020-04-26 18:12:55', 6, 0, 37, 0, 1)\n    ,(535, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2020-04-26 18:12:55', 6, 0, 37, 0, 1)\n    ,(536, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(537, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(538, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(539, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(540, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(541, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(542, 'hz', '10', '执行后台task数量,默认:10', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(543, 'port', '%d', '端口', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(544, 'maxmemory', '%dmb', '当前实例最大可用内存', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(545, 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(546, 'appendonly', 'yes', '开启append only持久化模式', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(547, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(548, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(549, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(550, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(551, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(552, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(553, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(554, 'maxclients', '10000', '客户端最大连接数', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(555, 'protected-mode', 'yes', '开启保护模式', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(556, 'bind', '0.0.0.0', '默认客户端都可连接', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(557, 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(558, 'list-compress-depth', '0', '压缩方式，0:不压缩', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(559, 'always-show-logo', 'yes', 'redis启动是否显示logo', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(560, 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(561, 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(562, 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(563, 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(564, 'protected-mode', 'no', '关闭sentinel保护模式', '2020-04-26 18:12:55', 5, 1, 37, 0, 0)\n    ,(565, 'activedefrag', 'yes', '碎片整理开启', '2020-04-26 18:12:55', 6, 1, 37, 0, 1)\n    ,(566, 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(567, 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(568, 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(569, 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(570, 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(571, 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(572, 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(573, 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2020-04-26 18:12:55', 2, 1, 37, 0, 0)\n    ,(574, 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(575, 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(576, 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(577, 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(578, 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(579, 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(580, 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(581, 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(582, 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(583, 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2020-04-26 18:12:55', 6, 1, 37, 0, 0)\n    ,(585, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:45:22', 6, 1, 37, 0, 0)\n    ,(587, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:18', 6, 1, 12, 0, 0)\n    ,(589, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:46:49', 6, 1, 31, 0, 0)\n    ,(590, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2020-05-26 15:49:47', 6, 1, 29, 0, 0)\n    ,(868, 'cluster-enabled', 'yes', '是否开启集群模式', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(869, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2021-06-09 10:12:50', 2, 1, 51, 0, 1)\n    ,(870, 'cluster-migration-barrier', '3', '从节点自动迁移至少需要的可用节点数,默认1个', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(871, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(872, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2021-06-09 10:12:50', 2, 1, 51, 0, 1)\n    ,(873, 'port', '%d', 'sentinel实例端口', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(874, 'dir', '%s', '工作目录', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(875, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(876, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(877, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(878, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(879, 'daemonize', 'no', '是否守护进程', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(880, 'tcp-backlog', '511', 'TCP连接完成队列', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(881, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(882, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(883, 'loglevel', 'notice', '日志级别', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(884, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(885, 'dir', '%s', 'redis工作目录', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(886, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(887, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(888, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(889, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(890, 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(891, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(892, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(893, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(894, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(895, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(896, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2021-06-09 10:12:50', 6, 0, 51, 0, 1)\n    ,(897, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2021-06-09 10:12:50', 6, 0, 51, 0, 1)\n    ,(898, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(899, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(900, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(901, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(902, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(903, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(904, 'hz', '10', '执行后台task数量,默认:10', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(905, 'port', '%d', '端口', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(906, 'maxmemory', '%dmb', '当前实例最大可用内存', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(907, 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(908, 'appendonly', 'yes', '开启append only持久化模式', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(909, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(910, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(911, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(912, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(913, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(914, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(915, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(916, 'maxclients', '10000', '客户端最大连接数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(917, 'protected-mode', 'yes', '开启保护模式', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(918, 'bind', '0.0.0.0', '默认客户端都可连接', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(919, 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(920, 'list-compress-depth', '0', '压缩方式，0:不压缩', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(921, 'always-show-logo', 'yes', 'redis启动是否显示logo', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(922, 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(923, 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(924, 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(925, 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(926, 'protected-mode', 'no', '关闭sentinel保护模式', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(927, 'activedefrag', 'no', '碎片整理开启', '2022-06-07 10:16:26', 6, 1, 51, 0, 1)\n    ,(928, 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(929, 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(930, 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(931, 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(932, 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(933, 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(934, 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(935, 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(936, 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(937, 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(938, 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(939, 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(940, 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(941, 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(942, 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(943, 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(944, 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(945, 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(946, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(947, 'repl-diskless-load', 'on-empty-db', '完全安全的情况下才使用无磁盘加载', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(948, 'tracking-table-max-keys', '1000000', '无效表键的最大填充数量', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(949, 'rdb-del-sync-files', 'yes', '默认:no 不删除rdb文件,删除实例中复制使用的不持久的RDB文件', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(950, 'lazyfree-lazy-user-del', 'yes', '默认值no,设置del操作命令同unlink一致', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(951, 'io-threads', '1', '读写io线程数量', '2021-06-09 15:22:48', 6, 1, 51, 0, 0)\n    ,(952, 'io-threads-do-reads', 'no', '开启io读线程', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(953, 'jemalloc-bg-thread', 'yes', '启用Jemalloc后台线程清理', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(954, 'server_cpulist', '0-7:2', '设置redis服务器/io线程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(955, 'bio_cpulist', '1,3', '设置bio线程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(956, 'aof_rewrite_cpulist', '8-11', '设置aof重写子进程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(957, 'bgsave_cpulist', '1,10-11', '设置bgsave子进程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(959, 'save', '', '关闭同步操作', '2021-07-01 10:31:11', 6, 1, 51, 0, 0)\n    ,(1579, 'enable-module-command', 'yes', '是否支持module命令', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1582, 'save', '', '关闭同步操作', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1584, 'cluster-enabled', 'yes', '是否开启集群模式', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1585, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2024-01-17 10:23:00', 2, 1, 62, 0, 1)\n    ,(1586, 'cluster-migration-barrier', '3', '从节点自动迁移至少需要的可用节点数,默认1个', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1587, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1588, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2024-01-17 10:23:00', 2, 1, 62, 0, 1)\n    ,(1589, 'port', '%d', 'sentinel实例端口', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1590, 'dir', '%s', '工作目录', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1591, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1592, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1593, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1594, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1595, 'daemonize', 'no', '是否守护进程', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1596, 'tcp-backlog', '511', 'TCP连接完成队列', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1597, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1598, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1599, 'loglevel', 'notice', '日志级别', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1600, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1601, 'dir', '%s', 'redis工作目录', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1602, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1603, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1604, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1605, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1606, 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1607, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1608, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1609, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1610, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1611, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1612, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2024-01-17 10:23:00', 6, 0, 62, 0, 1)\n    ,(1613, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2024-01-17 10:23:00', 6, 0, 62, 0, 1)\n    ,(1614, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1615, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1616, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1617, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1618, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1619, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1620, 'hz', '10', '执行后台task数量,默认:10', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1621, 'port', '%d', '端口', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1622, 'maxmemory', '%dmb', '当前实例最大可用内存', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1623, 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1624, 'appendonly', 'yes', '开启append only持久化模式', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1625, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1626, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1627, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1628, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1629, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1630, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1631, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1632, 'maxclients', '10000', '客户端最大连接数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1633, 'protected-mode', 'no', '开启保护模式', '2024-06-05 11:55:57', 6, 1, 62, 0, 0)\n    ,(1634, 'bind', '0.0.0.0 -::*', '默认客户端都可连接', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1635, 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1636, 'list-compress-depth', '0', '压缩方式，0:不压缩', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1637, 'always-show-logo', 'yes', 'redis启动是否显示logo', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1638, 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1639, 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1640, 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1641, 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1642, 'protected-mode', 'no', '关闭sentinel保护模式', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1643, 'activedefrag', 'no', '碎片整理开启', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1644, 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1645, 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1646, 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1647, 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1648, 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1649, 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1650, 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1651, 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1652, 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1653, 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1654, 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1655, 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1656, 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1657, 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1658, 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1659, 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1660, 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1661, 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1662, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1663, 'repl-diskless-load', 'on-empty-db', '完全安全的情况下才使用无磁盘加载', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1664, 'tracking-table-max-keys', '1000000', '无效表键的最大填充数量', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1665, 'rdb-del-sync-files', 'yes', '默认:no 不删除rdb文件,删除实例中复制使用的不持久的RDB文件', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1666, 'lazyfree-lazy-user-del', 'yes', '默认值no,设置del操作命令同unlink一致', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1667, 'io-threads', '1', '读写io线程数量', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1668, 'io-threads-do-reads', 'no', '开启io读线程', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1669, 'jemalloc-bg-thread', 'yes', '启用Jemalloc后台线程清理', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1670, 'server_cpulist', '0-7:2', '设置redis服务器/io线程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1671, 'bio_cpulist', '1,3', '设置bio线程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1672, 'aof_rewrite_cpulist', '8-11', '设置aof重写子进程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1673, 'bgsave_cpulist', '1,10-11', '设置bgsave子进程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0);\nCOMMIT;\n\n\n--\n-- Table structure for table `instance_fault`\n--\n\nDROP TABLE IF EXISTS `instance_fault`;\nCREATE TABLE `instance_fault` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `inst_id` bigint(20) NOT NULL COMMENT '实例id',\n  `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n  `port` int(11) NOT NULL COMMENT '端口',\n  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0:心跳停止,1:心跳恢复',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `type` mediumint(4) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n  `reason` mediumtext NOT NULL COMMENT '故障原因描述',\n  PRIMARY KEY (`id`),\n  KEY `idx_ip_port` (`ip`,`port`),\n  KEY `app_id` (`app_id`),\n  KEY `inst_id` (`inst_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例故障表' /* `compression`='tokudb_zlib' */;\n\n\n--\n-- Table structure for table `instance_info`\n--\n\nDROP TABLE IF EXISTS `instance_info`;\nCREATE TABLE `instance_info` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'memcached instance id',\n `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '对等实例的id',\n `app_id` bigint(20) NOT NULL COMMENT '应用id，与app_desc关联',\n `host_id` bigint(20) NOT NULL COMMENT '对应的主机id，与instance_host关联',\n `ip` varchar(16) NOT NULL COMMENT '实例的ip',\n `port` int(11) NOT NULL COMMENT '实例端口',\n `status` tinyint(4) NOT NULL COMMENT '是否启用:0:节点异常,1:正常启用,2:节点下线',\n `mem` int(11) NOT NULL COMMENT '内存大小',\n `conn` int(11) NOT NULL COMMENT '连接数',\n `cmd` varchar(255) NOT NULL COMMENT '启动实例的命令/redis-sentinel的masterName',\n `type` mediumint(11) NOT NULL COMMENT '类型：1. memcached, 2. redis-cluster, 3. memcacheq, 4. 非cache-cloud 5. redis-sentinel 6.redis-standalone',\n `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',\n PRIMARY KEY (`id`),\n UNIQUE KEY `uidx_inst_ipport` (`ip`,`port`) USING BTREE,\n KEY `app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `instance_latency_history`\n--\n\nDROP TABLE IF EXISTS `instance_latency_history`;\nCREATE TABLE `instance_latency_history` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',\n    `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n    `app_id` bigint(20) NOT NULL COMMENT 'app id',\n    `ip` varchar(32) NOT NULL COMMENT 'ip',\n    `port` int(11) NOT NULL COMMENT 'port',\n    `event` varchar(255) NOT NULL COMMENT '事件名称',\n    `execute_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n    `execution_cost` bigint(20) NOT NULL COMMENT '耗时(微妙)',\n    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `latencyistorykey` (`instance_id`,`event`,`execute_date`),\n    KEY `idx_app_create_time` (`app_id`,`create_time`),\n    KEY `idx_app_executedate` (`app_id`,`execute_date`),\n    KEY `idx_executedate` (`execute_date`),\n    KEY `idx_executedate_app` (`execute_date`,`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例慢延迟事件信息列表';\n\n--\n-- Table structure for table `instance_minute_stats`\n--\n\nDROP TABLE IF EXISTS `instance_minute_stats`;\nCREATE TABLE `instance_minute_stats` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n `port` int(11) NOT NULL COMMENT '端口/hostId',\n `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n `json` text NOT NULL COMMENT '统计json数据',\n `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n PRIMARY KEY (`id`),\n UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n KEY `idx_collect_time` (`collect_time`),\n KEY `idx_created_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例分钟统计表';\n\n--\n-- Table structure for table `instance_reshard_process`\n--\n\nDROP TABLE IF EXISTS `instance_reshard_process`;\nCREATE TABLE `instance_reshard_process` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `audit_id` bigint(20) NOT NULL COMMENT '审核id',\n  `source_instance_id` int(11) NOT NULL COMMENT '源实例id',\n  `target_instance_id` int(11) NOT NULL COMMENT '目标实例id',\n  `start_slot` int(11) NOT NULL COMMENT '开始slot',\n  `end_slot` int(11) NOT NULL COMMENT '结束slot',\n  `migrating_slot` int(11) NOT NULL COMMENT '正在迁移的slot',\n  `is_pipeline` tinyint(4) NOT NULL COMMENT '是否为pipeline,0:否,1:是',\n  `finish_slot_num` int(11) NOT NULL COMMENT '已经完成迁移的slot数量',\n  `status` tinyint(4) NOT NULL COMMENT '0:运行中 1:完成 2:出错',\n  `start_time` datetime NOT NULL COMMENT '迁移开始时间',\n  `end_time` datetime NOT NULL COMMENT '迁移结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '更新时间',\n  PRIMARY KEY (`id`),\n  KEY `idx_audit` (`audit_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例Reshard进度';\n\n--\n-- Table structure for table `instance_slow_log`\n--\n\nDROP TABLE IF EXISTS `instance_slow_log`;\nCREATE TABLE `instance_slow_log` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `instance_id` bigint(20) NOT NULL COMMENT '实例的id',\n  `app_id` bigint(20) NOT NULL COMMENT 'app id',\n  `ip` varchar(32) NOT NULL COMMENT 'ip',\n  `port` int(11) NOT NULL COMMENT 'port',\n  `slow_log_id` bigint(20) NOT NULL COMMENT '慢查询id',\n  `cost_time` int(11) NOT NULL COMMENT '耗时(微妙)',\n  `command` varchar(255) NOT NULL COMMENT '执行命令',\n  `execute_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '执行时间点',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `slowlogkey` (`instance_id`,`slow_log_id`,`execute_time`),\n  KEY `idx_app_create_time` (`app_id`,`create_time`),\n  KEY `idx_app_executetime` (`app_id`,`execute_time`),\n  KEY `idx_executetime` (`execute_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='实例慢查询列表';\n\n--\n-- Table structure for table `instance_statistics`\n--\n\nDROP TABLE IF EXISTS `instance_statistics`;\nCREATE TABLE `instance_statistics` (\n   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n   `inst_id` bigint(20) NOT NULL COMMENT '实例的id',\n   `app_id` bigint(20) NOT NULL COMMENT 'app id',\n   `host_id` bigint(20) NOT NULL COMMENT '机器的id',\n   `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n   `port` int(255) NOT NULL COMMENT 'port',\n   `role` tinyint(255) NOT NULL COMMENT '主从，1主2从',\n   `max_memory` bigint(255) NOT NULL COMMENT '预分配内存，单位byte',\n   `used_memory` bigint(255) NOT NULL COMMENT '已使用内存，单位byte',\n   `curr_items` bigint(255) NOT NULL COMMENT '当前item数量',\n   `curr_connections` int(255) NOT NULL COMMENT '当前连接数',\n   `misses` bigint(255) NOT NULL COMMENT 'miss数',\n   `hits` bigint(255) NOT NULL COMMENT '命中数',\n   `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n   `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n   `mem_fragmentation_ratio` double DEFAULT '0' COMMENT '碎片率',\n   `aof_delayed_fsync` int(11) DEFAULT '0' COMMENT 'aof阻塞次数',\n   `used_disk` bigint(255) NOT NULL DEFAULT '0' COMMENT '已使用磁盘，单位byte',\n   PRIMARY KEY (`id`),\n   UNIQUE KEY `ip` (`ip`,`port`),\n   KEY `app_id` (`app_id`),\n   KEY `machine_id` (`host_id`),\n   KEY `idx_inst_id` (`inst_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='实例的最新统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_info`\n--\n\nDROP TABLE IF EXISTS `machine_info`;\nCREATE TABLE `machine_info` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '机器的id',\n  `ssh_user` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh用户',\n  `ssh_passwd` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT 'cachecloud' COMMENT 'ssh密码',\n  `ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT 'ip',\n  `room` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '所属机房',\n  `mem` int(11) unsigned NOT NULL COMMENT '内存大小，单位G',\n  `cpu` mediumint(24) unsigned NOT NULL COMMENT 'cpu数量',\n  `virtual` tinyint(8) unsigned NOT NULL DEFAULT '1' COMMENT '是否虚拟，0表示否，1表示是',\n  `real_ip` varchar(16) COLLATE utf8_bin NOT NULL COMMENT '宿主机ip',\n  `service_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上线时间',\n  `fault_count` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '故障次数',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',\n  `warn` tinyint(255) unsigned NOT NULL DEFAULT '1' COMMENT '是否启用报警，0不启用，1启用',\n  `available` tinyint(255) NOT NULL COMMENT '表示机器是否可用，1表示可用，0表示不可用；',\n  `groupId` int(11) NOT NULL DEFAULT '0' COMMENT '机器分组，默认为0，表示原生资源，非0表示外部提供的资源(可扩展)',\n  `type` int(11) NOT NULL DEFAULT '0' COMMENT '0原生 1 其他',\n  `extra_desc` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '对于机器的额外说明(例如机器安装的其他服务(web,mysql,queue等等))',\n  `collect` int(11) DEFAULT '1' COMMENT 'switch of collect server status, 1:open, 0:close',\n  `version_install` varchar(512) COLLATE utf8_bin DEFAULT NULL COMMENT '机器安装redis版本状态',\n  `use_type` tinyint(4) DEFAULT '2' COMMENT '使用类型：Redis专用机器(0)，Redis测试机器(1)，混合部署机器(2)，Redis-Sentinel机器(3)',\n  `k8s_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否k8s容器：0:不是 1:是',\n  `rack` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '机器所在机架信息',\n  `is_allocating` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否在分配中,1是0否',\n  `disk` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '磁盘空间:G',\n  `dis_type` tinyint(4) DEFAULT 0 NOT NULL COMMENT '操作系统发行版本，0:centos;1:ubuntu',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `ip` (`ip`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='机器信息表' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `machine_relation`\n--\n\nDROP TABLE IF EXISTS `machine_relation`;\nCREATE TABLE `machine_relation` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',\n  `ip` varchar(64) NOT NULL COMMENT '虚拟机ip',\n  `real_ip` varchar(64) NOT NULL COMMENT '宿主机ip',\n  `extra_desc` varchar(128) DEFAULT NULL COMMENT '实例描述信息',\n  `status` int(255) NOT NULL COMMENT '实例变更状态 0:offline ,1:online',\n  `is_sync` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据同步状态 0: 未同步数据  -1:同步中 1:数据已同步 -2:同步失败 ',\n  `sync_time` timestamp NULL DEFAULT NULL COMMENT '同步时间',\n  `update_time` timestamp NULL DEFAULT NULL COMMENT 'pod最后更新时间',\n  `taskid` bigint(11) DEFAULT NULL COMMENT '任务id',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `machine_room`\n--\n\nDROP TABLE IF EXISTS `machine_room`;\nCREATE TABLE `machine_room` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '机房id',\n  `name` varchar(255) NOT NULL COMMENT '机房名称',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `desc` varchar(255) DEFAULT NULL COMMENT '机房描述信息',\n  `ip_network` varchar(32) NOT NULL DEFAULT '' COMMENT '机房网段信息',\n  `operator` varchar(255) DEFAULT NULL COMMENT '运营商',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `machine_room`\n-- ----------------------------\nBEGIN;\nINSERT INTO `machine_room` VALUES ('1', '阿里云杭州', '1', '阿里云-杭州机房', '172.27.*.*', '阿里云');\nCOMMIT;\n\n--\n-- Table structure for table `machine_statistics`\n--\n\nDROP TABLE IF EXISTS `machine_statistics`;\nCREATE TABLE `machine_statistics` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `host_id` bigint(20) NOT NULL COMMENT '机器id',\n  `ip` varchar(16) NOT NULL COMMENT '机器ip',\n  `cpu_usage` varchar(120) NOT NULL COMMENT 'cpu使用率',\n  `load` varchar(120) NOT NULL COMMENT '机器负载',\n  `traffic` varchar(120) NOT NULL COMMENT 'io网络流量',\n  `memory_usage_ratio` varchar(120) NOT NULL COMMENT '内存使用率',\n  `memory_free` varchar(120) NOT NULL COMMENT '内存剩余',\n  `memory_total` varchar(120) NOT NULL COMMENT '总内存量',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',\n  `max_memory` int(11) DEFAULT '0' COMMENT '机器分配内存,单位MB',\n  `instance_count` int(11) DEFAULT '0' COMMENT '机器实例数量',\n  `machine_memory` int(11) DEFAULT '0' COMMENT '机器入库总内存,单位MB',\n  `disk_total` varchar(120) DEFAULT NULL COMMENT '机器分配磁盘，单位MB',\n  `disk_available` varchar(120) DEFAULT NULL COMMENT '机器空闲磁盘，单位MB',\n  `disk_usage_ratio` varchar(15) DEFAULT NULL COMMENT '机器磁盘使用率，百分比（无需乘100）',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uidx_ip` (`ip`),\n  KEY `host_id` (`host_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机器状态统计信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_BLOB_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;\nCREATE TABLE `QRTZ_BLOB_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `BLOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_CALENDARS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_CALENDARS`;\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\n/*!40101 SET character_set_client = utf8 */;\nCREATE TABLE `QRTZ_CALENDARS` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `CALENDAR_NAME` varchar(200) NOT NULL COMMENT 'calendar名称',\n  `CALENDAR` blob NOT NULL COMMENT 'calendar信息',\n  PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='以 Blob 类型存储 Quartz 的 Calendar 信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_CRON_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;\nCREATE TABLE `QRTZ_CRON_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL COMMENT 'scheduler名称',\n  `TRIGGER_NAME` varchar(200) NOT NULL COMMENT 'trigger名',\n  `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT 'trigger组',\n  `CRON_EXPRESSION` varchar(120) NOT NULL COMMENT 'cron表达式',\n  `TIME_ZONE_ID` varchar(80) DEFAULT NULL COMMENT '时区',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储 Cron Trigger，包括 Cron 表达式和时区信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_FIRED_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;\nCREATE TABLE `QRTZ_FIRED_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `ENTRY_ID` varchar(195) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL,\n  `FIRED_TIME` bigint(13) NOT NULL,\n  `SCHED_TIME` bigint(13) NOT NULL,\n  `PRIORITY` int(11) NOT NULL,\n  `STATE` varchar(16) NOT NULL,\n  `JOB_NAME` varchar(200) DEFAULT NULL,\n  `JOB_GROUP` varchar(200) DEFAULT NULL,\n  `IS_NONCONCURRENT` varchar(1) DEFAULT NULL COMMENT '是否非并行执行',\n  `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL COMMENT '是否持久化',\n  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),\n  KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),\n  KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已触发的 Trigger相关的状态信息，以及关联 Job 的执行信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_JOB_DETAILS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;\nCREATE TABLE `QRTZ_JOB_DETAILS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `JOB_CLASS_NAME` varchar(250) NOT NULL,\n  `IS_DURABLE` varchar(1) NOT NULL COMMENT '是否持久化，0不持久化，1持久化',\n  `IS_NONCONCURRENT` varchar(1) NOT NULL COMMENT '是否非并发，0非并发，1并发',\n  `IS_UPDATE_DATA` varchar(1) NOT NULL,\n  `REQUESTS_RECOVERY` varchar(1) NOT NULL COMMENT '是否可恢复，0不恢复，1恢复',\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),\n  KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储每一个已配置的 Job 的详细信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_LOCKS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_LOCKS`;\nCREATE TABLE `QRTZ_LOCKS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `LOCK_NAME` varchar(40) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储程序的悲观锁的信息(假如使用了悲观锁)' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_PAUSED_TRIGGER_GRPS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;\nCREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已暂停的 Trigger 组的信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SCHEDULER_STATE`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;\nCREATE TABLE `QRTZ_SCHEDULER_STATE` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `INSTANCE_NAME` varchar(200) NOT NULL COMMENT '执行quartz实例的主机名',\n  `LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '实例将状态报告给集群中的其它实例的上一次时间',\n  `CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '实例间状态报告的时间频率',\n  PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储少量的有关 Scheduler 的状态信息' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SIMPLE_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复次数',\n  `REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复间隔',\n  `TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '已出发次数',\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储简单的 Trigger，包括重复次数，间隔，以及已触的次数' /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_SIMPROP_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;\nCREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `STR_PROP_1` varchar(512) DEFAULT NULL,\n  `STR_PROP_2` varchar(512) DEFAULT NULL,\n  `STR_PROP_3` varchar(512) DEFAULT NULL,\n  `INT_PROP_1` int(11) DEFAULT NULL,\n  `INT_PROP_2` int(11) DEFAULT NULL,\n  `LONG_PROP_1` bigint(20) DEFAULT NULL,\n  `LONG_PROP_2` bigint(20) DEFAULT NULL,\n  `DEC_PROP_1` decimal(13,4) DEFAULT NULL,\n  `DEC_PROP_2` decimal(13,4) DEFAULT NULL,\n  `BOOL_PROP_1` varchar(1) DEFAULT NULL,\n  `BOOL_PROP_2` varchar(1) DEFAULT NULL,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `QRTZ_TRIGGERS`\n--\n\nDROP TABLE IF EXISTS `QRTZ_TRIGGERS`;\nCREATE TABLE `QRTZ_TRIGGERS` (\n  `SCHED_NAME` varchar(120) NOT NULL,\n  `TRIGGER_NAME` varchar(200) NOT NULL,\n  `TRIGGER_GROUP` varchar(200) NOT NULL,\n  `JOB_NAME` varchar(200) NOT NULL,\n  `JOB_GROUP` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(250) DEFAULT NULL,\n  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL,\n  `PRIORITY` int(11) DEFAULT NULL,\n  `TRIGGER_STATE` varchar(16) NOT NULL,\n  `TRIGGER_TYPE` varchar(8) NOT NULL,\n  `START_TIME` bigint(13) NOT NULL,\n  `END_TIME` bigint(13) DEFAULT NULL,\n  `CALENDAR_NAME` varchar(200) DEFAULT NULL,\n  `MISFIRE_INSTR` smallint(2) DEFAULT NULL,\n  `JOB_DATA` blob,\n  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`),\n  KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),\n  KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),\n  KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),\n  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储已配置的 Trigger 的信息' /* `compression`='tokudb_zlib' */;\n\n\n--\n-- Table structure for table `server`\n--\n\nDROP TABLE IF EXISTS `server`;\nCREATE TABLE `server` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `host` varchar(255) DEFAULT NULL COMMENT 'host',\n  `nmon` varchar(255) DEFAULT NULL COMMENT 'nmon version',\n  `cpus` tinyint(4) DEFAULT NULL COMMENT 'logic cpu num',\n  `cpu_model` varchar(255) DEFAULT NULL COMMENT 'cpu 型号',\n  `dist` varchar(255) DEFAULT NULL COMMENT '发行版信息',\n  `kernel` varchar(255) DEFAULT NULL COMMENT '内核信息',\n  `ulimit` varchar(255) DEFAULT NULL COMMENT 'ulimit -n,ulimit -u',\n  `updatetime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ip`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n--\n-- Table structure for table `server_stat`\n--\n\nDROP TABLE IF EXISTS `server_stat`;\nCREATE TABLE `server_stat` (\n  `ip` varchar(16) NOT NULL COMMENT 'ip',\n  `cdate` date NOT NULL COMMENT '数据收集天',\n  `ctime` char(4) NOT NULL COMMENT '数据收集小时分钟',\n  `cuser` float DEFAULT NULL COMMENT '用户态占比',\n  `csys` float DEFAULT NULL COMMENT '内核态占比',\n  `cwio` float DEFAULT NULL COMMENT 'wio占比',\n  `c_ext` text COMMENT '子cpu占比',\n  `cload1` float DEFAULT NULL COMMENT '1分钟load',\n  `cload5` float DEFAULT NULL COMMENT '5分钟load',\n  `cload15` float DEFAULT NULL COMMENT '15分钟load',\n  `mtotal` float DEFAULT NULL COMMENT '总内存,单位M',\n  `mfree` float DEFAULT NULL COMMENT '空闲内存',\n  `mcache` float DEFAULT NULL COMMENT 'cache',\n  `mbuffer` float DEFAULT NULL COMMENT 'buffer',\n  `mswap` float DEFAULT NULL COMMENT 'cache',\n  `mswap_free` float DEFAULT NULL COMMENT 'cache',\n  `nin` float DEFAULT NULL COMMENT '网络入流量 单位K/s',\n  `nout` float DEFAULT NULL COMMENT '网络出流量 单位k/s',\n  `nin_ext` text COMMENT '各网卡入流量详情',\n  `nout_ext` text COMMENT '各网卡出流量详情',\n  `tuse` int(11) DEFAULT NULL COMMENT 'tcp estab连接数',\n  `torphan` int(11) DEFAULT NULL COMMENT 'tcp orphan连接数',\n  `twait` int(11) DEFAULT NULL COMMENT 'tcp time wait连接数',\n  `dread` float DEFAULT NULL COMMENT '磁盘读速率 单位K/s',\n  `dwrite` float DEFAULT NULL COMMENT '磁盘写速率 单位K/s',\n  `diops` float DEFAULT NULL COMMENT '磁盘io速率 交互次数/s',\n  `dbusy` float DEFAULT NULL COMMENT '磁盘io带宽使用百分比',\n  `d_ext` text COMMENT '磁盘各分区占比',\n  `dspace` text COMMENT '磁盘各分区空间使用率',\n  PRIMARY KEY (`ip`,`cdate`,`ctime`),\n  KEY `idx_cdate` (`cdate`),\n  KEY `idx_cdate_ctime` (`cdate`,`ctime`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\n--\n-- Table structure for table `standard_statistics`\n--\n\nCREATE TABLE `standard_statistics` (\n   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n   `collect_time` bigint(20) NOT NULL COMMENT '收集时间:格式yyyyMMddHHmm',\n   `ip` varchar(16) NOT NULL COMMENT 'ip地址',\n   `port` int(11) NOT NULL COMMENT '端口/hostId',\n   `db_type` varchar(16) NOT NULL COMMENT '收集的数据类型',\n   `info_json` text NOT NULL COMMENT '收集的json数据',\n   `diff_json` text NOT NULL COMMENT '上一次收集差异的json数据',\n   `cluster_info_json` varchar(20480) NOT NULL DEFAULT '' COMMENT '收集的cluster info json数据',\n   `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n   PRIMARY KEY (`id`),\n   UNIQUE KEY `uniq_index` (`ip`,`port`,`db_type`,`collect_time`),\n   KEY `idx_collect_time` (`collect_time`),\n   KEY `idx_created_time` (`created_time`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 /* `compression`='tokudb_zlib' */;\n\n--\n-- Table structure for table `system_config`\n--\n\nDROP TABLE IF EXISTS `system_config`;\nCREATE TABLE `system_config` (\n  `config_key` varchar(255) NOT NULL COMMENT '配置key',\n  `config_value` varchar(512) NOT NULL COMMENT '配置value',\n  `info` varchar(255) NOT NULL COMMENT '配置说明',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `order_id` int(11) NOT NULL COMMENT '顺序',\n  PRIMARY KEY (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统配置';\n\n-- ----------------------------\n--  Records of `system_config`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_config` VALUES ('cachecloud.admin.user.name','admin','cachecloud-admin用户名',1,11),('cachecloud.admin.user.password','admin','cachelcoud-admin密码',1,12),('cachecloud.app.client.conn.threshold','2000','应用连接数报警阀值',1,33),('cachecloud.base.dir','/opt','cachecloud根目录，要和cachecloud-init.sh脚本中的目录一致',1,31),('cachecloud.contact','user1:(xx@zz.com, user1:135xxxxxxxx)<br/>user2: (user2@zz.com, user2:138xxxxxxxx)','值班联系人信息',1,14),('cachecloud.cookie.domain','','cookie登录方式所需要的域名',1,22),('cachecloud.email.alert.interface','','邮件报警接口(参考报警接口规范)',1,24),('cachecloud.machine.ssh.name','cachecloud-open','机器ssh用户名',1,2),('cachecloud.machine.ssh.password','cachecloud-open','机器ssh密码',1,3),('cachecloud.machine.ssh.port','22','机器ssh端口',1,10),('cachecloud.machine.stats.cron.minute','1','机器性能统计周期(分钟)',1,35),('cachecloud.nmon.dir','/opt/cachecloud','nmon安装目录',1,32),('cachecloud.owner.email','xx@sohu.com,yy@qq.com','邮件报警(逗号隔开)',1,21),('cachecloud.owner.phone','xxx,yyy','手机号报警(逗号隔开)',1,21),('cachecloud.owner.weChat','xxx,yyy','微信号报警(逗号隔开)',1,21),('cachecloud.public.key.pem','/opt/ssh/id_rsa','密钥路径',1,5),('cachecloud.public.user.name','cachecloud-open','公钥用户名',1,4),('cachecloud.ssh.auth.type','1','ssh授权方式',1,1),('cachecloud.superAdmin','admin,xx,yy','超级管理员组',1,13),('cachecloud.user.login.type','1','用户登录状态保存方式(session或cookie)',1,22),('cachecloud.weChat.alert.interface','','微信报警接口(参考报警接口规范)',1,23),('cachecloud.whether.schedule.clean.data','false','是否定期清理统计数据',1,34),('machine.load.alert.ratio','8.0','机器负载报警阀值',1,32);\nCOMMIT;\n\n-- ----------------------------\n--  Table structure for `system_resource`\n-- ----------------------------\nDROP TABLE IF EXISTS `system_resource`;\nCREATE TABLE `system_resource` (\n  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '资源ID',\n  `name` varchar(64) NOT NULL COMMENT '资源名称',\n  `intro` varchar(255) DEFAULT NULL COMMENT '资源说明',\n  `type` tinyint(4) NOT NULL COMMENT '1:仓库地址 2:脚本 3:资源包 4:公钥/私钥 6:目录管理 7:迁移工具管理',\n  `lastmodify` datetime DEFAULT NULL COMMENT '最后更新时间',\n  `dir` varchar(128) DEFAULT NULL COMMENT '资源路径',\n  `url` varchar(128) DEFAULT NULL COMMENT '仓库地址',\n  `ispush` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:未推送 1:已推送',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:无效 1:有效',\n  `username` varchar(255) DEFAULT NULL COMMENT '最后修改人',\n  `task_id` bigint(11) DEFAULT NULL COMMENT '迁移任务id',\n  `compile_info` varchar(255) DEFAULT NULL COMMENT '编译信息',\n  `order_num` int(6) NOT NULL DEFAULT '0' COMMENT '排序',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n--  Records of `system_resource`\n-- ----------------------------\nBEGIN;\nINSERT INTO `system_resource` VALUES\n                                  (1,'cachecloud-init.sh','容器初始化脚本',2,'2020-07-15 18:35:41','/script','',0,1,NULL,NULL,NULL,0),\n                                  (2,'x.x.x.x',NULL,1,'2020-08-10 10:31:51','/opt/download/software/cachecloud/resource','http://x.x.x.x/software/cachecloud/resource',0,1,'admin',0,NULL,0),\n                                  (4,'cachecloud-env.sh','宿主环境脚本',2,'2020-07-15 18:36:28','/script','',0,1,NULL,NULL,NULL,0),\n                                  (5,'id_rsa','私钥文件',4,'2020-07-07 10:45:39','/ssh','',0,1,NULL,NULL,NULL,0),\n                                  (6,'id_rsa.pub','公钥文件',4,'2020-07-07 10:45:45','/ssh','',0,1,NULL,NULL,NULL,0),\n                                  (12,'redis-4.0.14','redis 4.0.14资源包',3,'2020-08-10 09:52:41','/redis','http://download.redis.io/releases/redis-4.0.14.tar.gz',0,1,'admin',532,NULL,0),\n                                  (21,'/script','脚本目录管理',6,'2020-08-10 10:51:34','',NULL,0,1,'admin',0,NULL,0),\n                                  (28,'/ssh','ssh目录',6,'2020-07-20 17:55:03',NULL,NULL,0,1,'admin',0,NULL,0),\n                                  (29,'redis-3.0.7','redis3.0.7 资源包',3,'2020-08-10 09:53:32','/redis','http://download.redis.io/releases/redis-3.0.7.tar.gz',0,1,'admin',529,NULL,0),\n                                  (31,'redis-3.2.12','redis 3.2.12 资源包',3,'2020-08-10 15:08:21','/redis','http://download.redis.io/releases/redis-3.2.12.tar.gz',0,1,'admin',530,NULL,0),\n                                  (32,'/redis','redis资源包管理',6,'2020-07-20 17:54:59',NULL,NULL,0,1,'admin',0,NULL,0),\n                                  (33,'/tool','迁移工具资源包',6,'2020-07-20 17:54:53',NULL,NULL,0,1,'admin',0,NULL,0),\n                                  (37,'redis-5.0.9','redis5.0.9 资源包',3,'2020-08-10 09:51:41','/redis','http://download.redis.io/releases/redis-5.0.9.tar.gz',0,1,'admin',533,NULL,0),\n                                  (40,'redis-shake-2.0.3','redis 2.0.3\\n修复fix 5.0迁移类型问题',7,'2020-08-11 10:53:26','/tool','https://github.com/alibaba/RedisShake/releases/download/release-v2.0.3-20200724/redis-shake-v2.0.3.tar.gz',0,1,'admin',518,NULL,0),\n                                  (51, 'redis-6.2.4', 'redis-6.2.4 资源包', 3, '2023-02-09 10:24:16', '/redis', 'http://download.redis.io/releases/redis-6.2.4.tar.gz', 1, 1, 'admin', 3507, NULL, 0),\n                                  (62, 'redis-7.2.4', 'redis 7.2.4 资源包', 3, '2024-01-18 15:26:02', '/redis', 'http://download.redis.io/releases/redis-7.2.4.tar.gz', 1, 1, 'admin', 11687, NULL, 700);\nCOMMIT;\n\n--\n-- Table structure for table `task_queue`\n--\n\nDROP TABLE IF EXISTS `task_queue`;\nCREATE TABLE `task_queue` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT '应用id',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `important_info` varchar(255) NOT NULL DEFAULT '' COMMENT '重要信息',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `param` longtext NOT NULL COMMENT '任务参数(json):随着任务变化',\n  `init_param` longtext NOT NULL COMMENT '初始化任务参数(json):不变',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2中断，3失败',\n  `parent_task_id` bigint(20) NOT NULL COMMENT '父任务id',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `priority` int(11) NOT NULL COMMENT '优先级',\n  `error_code` int(11) NOT NULL COMMENT '错误代码',\n  `error_msg` varchar(255) NOT NULL COMMENT '错误消息',\n  `task_note` varchar(255) NOT NULL COMMENT '备注',\n  PRIMARY KEY (`id`),\n  KEY `idx_app_id` (`app_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务表';\n\n--\n-- Table structure for table `task_step_flow`\n--\n\nDROP TABLE IF EXISTS `task_step_flow`;\nCREATE TABLE `task_step_flow` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `task_id` bigint(20) NOT NULL COMMENT '任务id',\n  `child_task_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '子任务id',\n  `execute_ip_port` varchar(255) DEFAULT '' COMMENT '执行任务的ip:port',\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  `status` tinyint(4) NOT NULL COMMENT '状态：0未开始、1成功、2中断、3跳过、4失败',\n  `log` text COMMENT '日志',\n  `start_time` datetime NOT NULL COMMENT '开始时间',\n  `end_time` datetime NOT NULL COMMENT '结束时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_task_class_step` (`task_id`,`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤流表';\n\n--\n-- Table structure for table `task_step_meta`\n--\n\nDROP TABLE IF EXISTS `task_step_meta`;\nCREATE TABLE `task_step_meta` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `class_name` varchar(255) NOT NULL COMMENT '类名',\n  `step_name` varchar(255) NOT NULL COMMENT '步骤名',\n  `step_desc` varchar(255) NOT NULL COMMENT '步骤描述',\n  `ops_device` varchar(255) NOT NULL COMMENT '运维建议',\n  `timeout` int(11) NOT NULL COMMENT '超时时间',\n  `create_time` datetime NOT NULL COMMENT '创建时间',\n  `update_time` datetime NOT NULL COMMENT '修改时间',\n  `order_no` int(11) NOT NULL COMMENT '序号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_class_step` (`class_name`,`step_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任务步骤元数据表';\n\n\n--\n-- Table structure for table `app_alert_record`\n--\nDROP TABLE IF EXISTS `app_alert_record`;\nCREATE TABLE `app_alert_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',\n  `visible_type` int(1) NOT NULL COMMENT '可见类型（0：均可见；1：仅管理员可见；）',\n  `important_level` int(1) NOT NULL COMMENT '重要类型（0：一般；1：重要；2：紧急）',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `app_id` bigint(20) DEFAULT NULL COMMENT 'app id',\n  `instance_id` bigint(20) DEFAULT NULL COMMENT '实例id',\n  `ip` varchar(16) COLLATE utf8_bin DEFAULT NULL COMMENT '机器ip',\n  `port` int(10) DEFAULT NULL COMMENT '端口号',\n  `title` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '报警标题',\n  `content` varchar(500) COLLATE utf8_bin NOT NULL COMMENT '报警内容',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`),\n  KEY `ip` (`ip`),\n  KEY `idx_inst_id` (`instance_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='报警记录表';\n\n--\n-- Table structure for table `config_restart_record`\n--\nDROP TABLE IF EXISTS `config_restart_record`;\nCREATE TABLE `config_restart_record` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n `app_id` bigint(20) NOT NULL COMMENT '应用id',\n `app_name` varchar(36) NOT NULL COMMENT '应用名称',\n `operate_type` char(1) NOT NULL COMMENT '操作类型（0:滚动重启，1:修改配置强制重启；2：修改配置）',\n `param` varchar(2000) NOT NULL COMMENT '初始化任务参数(json):不变',\n `status` tinyint(4) NOT NULL COMMENT '状态：0等待，1运行，2成功，3失败，4配置修改待重启，5修改配置后重启，6被停止',\n `start_time` datetime NOT NULL COMMENT '开始时间',\n `end_time` datetime NOT NULL COMMENT '结束时间',\n `create_time` datetime NOT NULL COMMENT '创建时间',\n `update_time` datetime NOT NULL COMMENT '修改时间',\n `log` longtext COMMENT '日志信息',\n `user_name` varchar(64) DEFAULT NULL COMMENT '操作人员姓名',\n `user_id` bigint(20) NOT NULL COMMENT '用户id',\n `instances` varchar(1000) DEFAULT NULL COMMENT '涉及实例id列表的json格式',\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='重启记录表';\n\n--\n-- Table structure for table `app_import`\n--\nDROP TABLE IF EXISTS `app_import`;\nCREATE TABLE `app_import` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT '目标应用id',\n  `instance_info` text COMMENT '源redis实例信息',\n  `redis_password` varchar(200) DEFAULT NULL COMMENT '源redis密码',\n  `status` int(11) DEFAULT NULL COMMENT '迁移状态：PREPARE(0, \"准备\", \"应用导入-未开始\"),     START(1, \"进行中...\", \"应用导入-开始\"),     ERROR(2, \"error\", \"应用导入-出错\"),     VERSION_BUILD_START(11, \"进行中...\", \"新建redis版本-进行中\"),     VERSION_BUILD_ERROR(12, \"error\", \"新建redis版本-出错\"),     VERSION_BUILD_END(20, \"成功\", \"新建redis版本-完成\"),     APP_BUILD_INIT(21, \"准备就绪\", \"新建redis应用-准备就绪\"),     APP_BUILD_START(22, \"进行中...\", \"新建redis应用-进行中\"),     APP_BUILD_ERROR(23, \"error\", \"新建redis应用-出错\"),     APP_BUILD_END(30, \"成功\", \"新建redis应用-完成\"),     MIGRATE_INIT(31, \"准备就绪\", \"数据迁移-准备就绪\"),     MIGRATE_START(32, \"进行中...\", \"数据迁移-进行中\"),     MIGRATE_ERROR(33, \"error\", \"数据迁移-出错\"),     MIGRATE_END(3, \"成功\", \"应用导入-成功\")',\n  `step` int(11) DEFAULT NULL COMMENT '导入阶段',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `migrate_id` bigint(20) DEFAULT NULL COMMENT '数据迁移id',\n  `mem_size` int(11) DEFAULT NULL COMMENT '目标应用内存大小，单位G',\n  `redis_version_name` varchar(20) DEFAULT NULL COMMENT '目标应用redis版本，格式：redis-x.x.x',\n  `app_build_task_id` bigint(20) DEFAULT NULL COMMENT '目标应用部署任务id',\n  `source_type` int(11) DEFAULT NULL COMMENT '源redis类型：7:cluster, 6:sentinel, 5:standalone',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\n--  -------------------------新增加的表---------------------------\n--\n-- Table structure for table `app_biz`\n--\n\nCREATE TABLE `app_biz` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n    `name` varchar(64) NOT NULL COMMENT '业务组名称',\n    `biz_desc` varchar(255) NOT NULL COMMENT '业务组描述',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `bidx_name` (`name`)\n) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='业务组表';\n\n--\n-- Table structure for table `app_capacity_monitor`\n--\nCREATE TABLE `app_capacity_monitor` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n    `app_id` bigint(20) NOT NULL COMMENT '应用id',\n    `sharding_master_num` int(10) NOT NULL DEFAULT '0' COMMENT '主分片数',\n    `mem` bigint(20) NOT NULL COMMENT '应用初始内存(字节)',\n    `cur_mem` bigint(20) NOT NULL COMMENT '应用当前内存(字节)',\n    `mem_used` bigint(20) NOT NULL DEFAULT '0' COMMENT '应用已使用内存(字节)',\n    `mem_used_history` bigint(20) DEFAULT '0' COMMENT '应用已使用内存（历史最大值）',\n    `sharding_mem` bigint(20) NOT NULL COMMENT '应用分片初始内存(字节)',\n    `cur_sharding_mem` bigint(20) NOT NULL COMMENT '应用分片当前内存(字节)',\n    `sharding_mem_used` bigint(20) NOT NULL DEFAULT '0' COMMENT '分片已使用内存（最大值）',\n    `expand_mem_percent` tinyint(4) NOT NULL COMMENT '应用扩容内存使用百分比',\n    `expand_ratio` tinyint(4) NOT NULL COMMENT '扩容比率',\n    `expand_ratio_total` int(10) NOT NULL COMMENT '当日最大扩容比率（超出不可扩容）',\n    `is_expand` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否可扩容：0否；1是',\n    `is_reduce` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否可缩容: 0否，1是',\n    `update_time` datetime DEFAULT NULL COMMENT '更新时间',\n    `expand_time` datetime DEFAULT NULL COMMENT '上次扩容时间',\n    `schedule_status` tinyint(4) DEFAULT '0' COMMENT '计划状态：0：无意义；1：待缩容；2：待扩容',\n    `schedule_time` date DEFAULT NULL COMMENT '计划处理时间',\n    `reduce_ratio_min` tinyint(4) NOT NULL DEFAULT '40' COMMENT '缩容内存使用率最小值',\n    `reduce_ratio_max` tinyint(4) NOT NULL DEFAULT '60' COMMENT '缩容内存使用率最大值',\n    `expand_count` int(10) NOT NULL DEFAULT '0' COMMENT '当日自动扩容次数',\n    PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=596 DEFAULT CHARSET=utf8 COMMENT='app应用容量监控';\n"
  },
  {
    "path": "cachecloud-web/sql/function-support_custom_password.sql",
    "content": "-- app_desc change\nALTER TABLE app_desc ADD custom_password varchar(255) DEFAULT NULL COMMENT '自定义密码';\n"
  },
  {
    "path": "cachecloud-web/sql/update 2.0 to 3.0.sql",
    "content": "CREATE TABLE `app_alert_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',\n  `visible_type` int(1) NOT NULL COMMENT 'ɼͣ0ɼ1Աɼ',\n  `important_level` int(1) NOT NULL COMMENT 'Ҫͣ0һ㣻1Ҫ2',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `app_id` bigint(20) DEFAULT NULL COMMENT 'app id',\n  `instance_id` bigint(20) DEFAULT NULL COMMENT 'ʵid',\n  `ip` varchar(16) COLLATE utf8_bin DEFAULT NULL COMMENT 'ip',\n  `port` int(10) DEFAULT NULL COMMENT '˿ں',\n  `title` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '',\n  `content` varchar(500) COLLATE utf8_bin NOT NULL COMMENT '',\n  PRIMARY KEY (`id`),\n  KEY `app_id` (`app_id`),\n  KEY `ip` (`ip`),\n  KEY `idx_inst_id` (`instance_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='¼';\n\nCREATE TABLE `config_restart_record` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) NOT NULL COMMENT 'Ӧid',\n  `app_name` varchar(36) NOT NULL COMMENT 'Ӧ',\n  `operate_type` char(1) NOT NULL COMMENT 'ͣ0:1:޸ǿ2޸ã',\n  `param` varchar(2000) NOT NULL COMMENT 'ʼ(json):',\n  `status` tinyint(4) NOT NULL COMMENT '״̬0ȴ1У2ɹ3ʧܣ4޸Ĵ',\n  `start_time` datetime NOT NULL COMMENT 'ʼʱ',\n  `end_time` datetime NOT NULL COMMENT 'ʱ',\n  `create_time` datetime NOT NULL COMMENT 'ʱ',\n  `update_time` datetime NOT NULL COMMENT '޸ʱ',\n  `log` longtext COMMENT '־Ϣ',\n  `user_name` varchar(64) DEFAULT NULL COMMENT 'Ա',\n  `user_id` bigint(20) NOT NULL COMMENT 'ûid',\n  `instances` varchar(1000) DEFAULT NULL COMMENT '漰ʵidбjsonʽ',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='¼';\n\nCREATE TABLE `module_info` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(64) NOT NULL,\n  `git_url` varchar(255) NOT NULL DEFAULT '' COMMENT 'git resource',\n  `info` varchar(128) DEFAULT NULL COMMENT 'ģϢ˵',\n  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0:Ч 1:Ч',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='RedisģϢ';\n\nCREATE TABLE `module_version` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `module_id` int(11) NOT NULL,\n  `version_id` int(11) NOT NULL COMMENT '汾',\n  `create_time` datetime DEFAULT NULL COMMENT 'ʱ',\n  `so_path` varchar(255) DEFAULT NULL COMMENT 'soĵַ',\n  `tag` varchar(64) NOT NULL COMMENT 'ģ汾',\n  `status` int(255) NOT NULL DEFAULT '0' COMMENT 'Ƿ(soַ)0  1',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Redisģ汾';\n\nCREATE TABLE `app_import` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `app_id` bigint(20) DEFAULT NULL COMMENT 'ĿӦid',\n  `instance_info` text COMMENT 'ԴredisʵϢ',\n  `redis_password` varchar(200) DEFAULT NULL COMMENT 'Դredis',\n  `status` int(11) DEFAULT NULL COMMENT 'Ǩ״̬PREPARE(0, \"׼\", \"Ӧõ-δʼ\"),     START(1, \"...\", \"Ӧõ-ʼ\"),     ERROR(2, \"error\", \"Ӧõ-\"),     VERSION_BUILD_START(11, \"...\", \"½redis汾-\"),     VERSION_BUILD_ERROR(12, \"error\", \"½redis汾-\"),     VERSION_BUILD_END(20, \"ɹ\", \"½redis汾-\"),     APP_BUILD_INIT(21, \"׼\", \"½redisӦ-׼\"),     APP_BUILD_START(22, \"...\", \"½redisӦ-\"),     APP_BUILD_ERROR(23, \"error\", \"½redisӦ-\"),     APP_BUILD_END(30, \"ɹ\", \"½redisӦ-\"),     MIGRATE_INIT(31, \"׼\", \"Ǩ-׼\"),     MIGRATE_START(32, \"...\", \"Ǩ-\"),     MIGRATE_ERROR(33, \"error\", \"Ǩ-\"),     MIGRATE_END(3, \"ɹ\", \"Ӧõ-ɹ\")',\n  `step` int(11) DEFAULT NULL COMMENT '׶',\n  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n  `migrate_id` bigint(20) DEFAULT NULL COMMENT 'Ǩid',\n  `mem_size` int(11) DEFAULT NULL COMMENT 'ĿӦڴСλG',\n  `redis_version_name` varchar(20) DEFAULT NULL COMMENT 'ĿӦredis汾ʽredis-x.x.x',\n  `app_build_task_id` bigint(20) DEFAULT NULL COMMENT 'ĿӦòid',\n  `source_type` int(11) DEFAULT NULL COMMENT 'Դredisͣ7:cluster, 6:sentinel, 5:standalone',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- instance_alert_configs change\nALTER TABLE instance_alert_configs ADD important_level TINYINT(4) DEFAULT 0 NOT NULL COMMENT 'Ҫ̶ȣ0һ㣻1Ҫ2';\n\n-- app_user change\nALTER TABLE app_user ADD password varchar(64) NULL COMMENT '';\nALTER TABLE app_user ADD register_time DATETIME DEFAULT CURRENT_TIMESTAMP NULL COMMENT 'עʱ';\nALTER TABLE app_user ADD purpose varchar(255) NULL COMMENT 'ʹĿ';\nALTER TABLE app_user ADD company varchar(255) NULL COMMENT '˾';\n\n-- module_info change\nALTER TABLE module_info ADD CONSTRAINT `NAMEKEY` UNIQUE KEY (name);\n\n-- app_desc change\nALTER TABLE app_desc ADD custom_password varchar(255) DEFAULT NULL COMMENT 'Զ';\n\n-- redis_module_config definition\n\nCREATE TABLE `redis_module_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `config_key` varchar(128) NOT NULL COMMENT '',\n  `config_value` varchar(512) NOT NULL COMMENT 'ֵ',\n  `info` varchar(512) NOT NULL COMMENT '˵',\n  `update_time` datetime NOT NULL COMMENT 'ʱ',\n  `type` mediumint(9) NOT NULL COMMENT 'ͣ2.clusterڵ, 5:sentinelڵ, 6:redisͨڵ',\n  `status` tinyint(4) NOT NULL COMMENT '1Ч,0Ч',\n  `version_id` int(11) NOT NULL COMMENT 'Module version汾id',\n  `refresh` tinyint(4) DEFAULT '0' COMMENT 'Ƿã0ɣ1',\n  `module_id` int(11) NOT NULL DEFAULT '7' COMMENT 'Module Ϣid',\n  `config_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'ͣ0غã1ʱã2ʱ',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uniq_configkey_type_version_id` (`config_key`,`type`,`version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='redisģñ';\n\n\n-- app_to_module definition\n\nCREATE TABLE `app_to_module` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '',\n  `app_id` bigint(20) NOT NULL COMMENT 'Ӧid',\n  `module_id` int(11) NOT NULL COMMENT 'ģinfo id',\n  `module_version_id` int(11) NOT NULL COMMENT 'ģ汾id',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_to_module_un` (`app_id`,`module_id`,`module_version_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Ӧģϵ';\n\nALTER TABLE machine_info ADD dis_type tinyint(4) DEFAULT 0 NOT NULL COMMENT 'ϵͳа汾0:centos;1:ubuntu';\n\n"
  },
  {
    "path": "cachecloud-web/sql/update 3.0 to 3.1.sql",
    "content": "-- system_resource change\nALTER TABLE system_resource ADD order_num int(6) NOT NULL DEFAULT '0' COMMENT '排序';\n"
  },
  {
    "path": "cachecloud-web/sql/update 3.1 to 3.2.sql",
    "content": "-- app_desc change\nALTER TABLE app_desc ADD maxmemory_policy tinyint(4) DEFAULT NULL COMMENT '淘汰策略(0：noeviction; 1:allkeys-lru;2:allkeys-lfu;3:volatile-lru;4:volatile-lfu;5:allkeys-random;6:volatile-random;7:volatile-ttl)';"
  },
  {
    "path": "cachecloud-web/sql/update 3.2 to 3.3.sql",
    "content": "ALTER TABLE instance_config ADD value_type TINYINT(4) DEFAULT 0 NOT NULL COMMENT '取值类型（0：默认值 config_value；1：从主节点拷贝）';\n\nALTER TABLE app_client_statistic_gather ADD used_disk BIGINT(20) DEFAULT 0 NULL COMMENT '磁盘占用byte';\nALTER TABLE app_client_statistic_gather ADD server_cmd_count bigint(20) DEFAULT 0 NOT NULL COMMENT 'server端统计的命令调用次数';\n\nALTER TABLE instance_statistics ADD used_disk bigint(255) DEFAULT 0 NOT NULL COMMENT '已使用磁盘，单位byte';\n\nALTER TABLE app_minute_statistics ADD used_disk bigint(20) DEFAULT 0 NOT NULL COMMENT '磁盘占用（字节）';\n\nALTER TABLE app_hour_statistics ADD used_disk bigint(20) DEFAULT 0 NOT NULL COMMENT '磁盘占用（字节）';\n\nALTER TABLE machine_statistics ADD disk_total varchar(120) NULL COMMENT '机器分配磁盘，单位MB';\nALTER TABLE machine_statistics ADD disk_available varchar(120) NULL COMMENT '机器空闲磁盘，单位MB';\nALTER TABLE machine_statistics ADD disk_usage_ratio varchar(15) NULL COMMENT '机器磁盘使用率，百分比（无需乘100）';\n\nALTER TABLE app_daily ADD avg_used_disk BIGINT(20) NOT NULL COMMENT '平均磁盘使用量';\nALTER TABLE app_daily ADD max_used_disk BIGINT(20) NOT NULL COMMENT '最大磁盘使用量';\n\nALTER TABLE app_desc ADD persistence_type TINYINT(4) DEFAULT 0 NOT NULL COMMENT '持久化类型（0：常规；1：主aof自动刷盘；从常规；2：主关闭aof，从常规）';\n\nALTER TABLE app_user ADD biz_id BIGINT(20) DEFAULT NULL COMMENT '所属业务组id（app_biz）';\n\nALTER TABLE instance_alert_configs ADD app_type TINYINT(4) DEFAULT 0 NOT NULL COMMENT '应用类型(0：redis;)';\nALTER TABLE instance_alert_configs DROP KEY uniq_index;\nALTER TABLE instance_alert_configs ADD CONSTRAINT uniq_index UNIQUE KEY (`type`,instance_id,alert_config,compare_type,app_type);\n\n--\n-- Table structure for table `app_biz`\n--\n\nCREATE TABLE `app_biz` (\n    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n    `name` varchar(64) NOT NULL COMMENT '业务组名称',\n    `biz_desc` varchar(255) NOT NULL COMMENT '业务组描述',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `bidx_name` (`name`)\n) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='业务组表';\n\n--\n-- Table structure for table `app_capacity_monitor`\n--\nCREATE TABLE `app_capacity_monitor` (\n                                        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n                                        `app_id` bigint(20) NOT NULL COMMENT '应用id',\n                                        `sharding_master_num` int(10) NOT NULL DEFAULT '0' COMMENT '主分片数',\n                                        `mem` bigint(20) NOT NULL COMMENT '应用初始内存(字节)',\n                                        `cur_mem` bigint(20) NOT NULL COMMENT '应用当前内存(字节)',\n                                        `mem_used` bigint(20) NOT NULL DEFAULT '0' COMMENT '应用已使用内存(字节)',\n                                        `mem_used_history` bigint(20) DEFAULT '0' COMMENT '应用已使用内存（历史最大值）',\n                                        `sharding_mem` bigint(20) NOT NULL COMMENT '应用分片初始内存(字节)',\n                                        `cur_sharding_mem` bigint(20) NOT NULL COMMENT '应用分片当前内存(字节)',\n                                        `sharding_mem_used` bigint(20) NOT NULL DEFAULT '0' COMMENT '分片已使用内存（最大值）',\n                                        `expand_mem_percent` tinyint(4) NOT NULL COMMENT '应用扩容内存使用百分比',\n                                        `expand_ratio` tinyint(4) NOT NULL COMMENT '扩容比率',\n                                        `expand_ratio_total` int(10) NOT NULL COMMENT '当日最大扩容比率（超出不可扩容）',\n                                        `is_expand` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否可扩容：0否；1是',\n                                        `is_reduce` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否可缩容: 0否，1是',\n                                        `update_time` datetime DEFAULT NULL COMMENT '更新时间',\n                                        `expand_time` datetime DEFAULT NULL COMMENT '上次扩容时间',\n                                        `schedule_status` tinyint(4) DEFAULT '0' COMMENT '计划状态：0：无意义；1：待缩容；2：待扩容',\n                                        `schedule_time` date DEFAULT NULL COMMENT '计划处理时间',\n                                        `reduce_ratio_min` tinyint(4) NOT NULL DEFAULT '40' COMMENT '缩容内存使用率最小值',\n                                        `reduce_ratio_max` tinyint(4) NOT NULL DEFAULT '60' COMMENT '缩容内存使用率最大值',\n                                        `expand_count` int(10) NOT NULL DEFAULT '0' COMMENT '当日自动扩容次数',\n                                        PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=596 DEFAULT CHARSET=utf8 COMMENT='app应用容量监控';\n\n\n\n\n------------------------------- add for redis 6.2 and 7.2----------------------------------------------------------\n# below two insert sql are related, note the ids related, system_resource.id and instance_config.version_id\n# 注意两个表之间的id相关联，system_resource表id字段 和 instance_config表version_id字段\n\nBEGIN;\nINSERT INTO `system_resource` VALUES\n      (51, 'redis-6.2.4', 'redis-6.2.4 资源包', 3, '2023-02-09 10:24:16', '/redis', 'http://download.redis.io/releases/redis-6.2.4.tar.gz', 1, 1, 'admin', 3507, NULL, 0),\n      (62, 'redis-7.2.4', 'redis 7.2.4 资源包', 3, '2024-01-18 15:26:02', '/redis', 'http://download.redis.io/releases/redis-7.2.4.tar.gz', 1, 1, 'admin', 11687, NULL, 700);\nCOMMIT;\n\nBEGIN;\nINSERT INTO instance_config (id, config_key, config_value, info, update_time, `type`, status, version_id, refresh, value_type) VALUES\n    (868, 'cluster-enabled', 'yes', '是否开启集群模式', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(869, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2021-06-09 10:12:50', 2, 1, 51, 0, 1)\n    ,(870, 'cluster-migration-barrier', '3', '从节点自动迁移至少需要的可用节点数,默认1个', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(871, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(872, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2021-06-09 10:12:50', 2, 1, 51, 0, 1)\n    ,(873, 'port', '%d', 'sentinel实例端口', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(874, 'dir', '%s', '工作目录', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(875, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(876, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(877, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(878, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(879, 'daemonize', 'no', '是否守护进程', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(880, 'tcp-backlog', '511', 'TCP连接完成队列', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(881, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(882, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(883, 'loglevel', 'notice', '日志级别', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(884, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(885, 'dir', '%s', 'redis工作目录', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(886, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(887, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(888, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(889, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(890, 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(891, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(892, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(893, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(894, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(895, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(896, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2021-06-09 10:12:50', 6, 0, 51, 0, 1)\n    ,(897, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2021-06-09 10:12:50', 6, 0, 51, 0, 1)\n    ,(898, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(899, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(900, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(901, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(902, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(903, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(904, 'hz', '10', '执行后台task数量,默认:10', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(905, 'port', '%d', '端口', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(906, 'maxmemory', '%dmb', '当前实例最大可用内存', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(907, 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(908, 'appendonly', 'yes', '开启append only持久化模式', '2021-06-09 10:12:50', 6, 1, 51, 0, 1)\n    ,(909, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(910, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(911, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(912, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(913, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(914, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(915, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(916, 'maxclients', '10000', '客户端最大连接数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(917, 'protected-mode', 'yes', '开启保护模式', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(918, 'bind', '0.0.0.0', '默认客户端都可连接', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(919, 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(920, 'list-compress-depth', '0', '压缩方式，0:不压缩', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(921, 'always-show-logo', 'yes', 'redis启动是否显示logo', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(922, 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(923, 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(924, 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(925, 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(926, 'protected-mode', 'no', '关闭sentinel保护模式', '2021-06-09 10:12:50', 5, 1, 51, 0, 0)\n    ,(927, 'activedefrag', 'no', '碎片整理开启', '2022-06-07 10:16:26', 6, 1, 51, 0, 1)\n    ,(928, 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(929, 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(930, 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(931, 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(932, 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(933, 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(934, 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(935, 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2021-06-09 10:12:50', 2, 1, 51, 0, 0)\n    ,(936, 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(937, 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(938, 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(939, 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(940, 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(941, 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(942, 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(943, 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(944, 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(945, 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(946, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(947, 'repl-diskless-load', 'on-empty-db', '完全安全的情况下才使用无磁盘加载', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(948, 'tracking-table-max-keys', '1000000', '无效表键的最大填充数量', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(949, 'rdb-del-sync-files', 'yes', '默认:no 不删除rdb文件,删除实例中复制使用的不持久的RDB文件', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(950, 'lazyfree-lazy-user-del', 'yes', '默认值no,设置del操作命令同unlink一致', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(951, 'io-threads', '1', '读写io线程数量', '2021-06-09 15:22:48', 6, 1, 51, 0, 0)\n    ,(952, 'io-threads-do-reads', 'no', '开启io读线程', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(953, 'jemalloc-bg-thread', 'yes', '启用Jemalloc后台线程清理', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(954, 'server_cpulist', '0-7:2', '设置redis服务器/io线程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(955, 'bio_cpulist', '1,3', '设置bio线程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(956, 'aof_rewrite_cpulist', '8-11', '设置aof重写子进程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(957, 'bgsave_cpulist', '1,10-11', '设置bgsave子进程的cpu使用权重', '2021-06-09 10:12:50', 6, 1, 51, 0, 0)\n    ,(959, 'save', '', '关闭同步操作', '2021-07-01 10:31:11', 6, 1, 51, 0, 0)\n    ,(1579, 'enable-module-command', 'yes', '是否支持module命令', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1582, 'save', '', '关闭同步操作', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1584, 'cluster-enabled', 'yes', '是否开启集群模式', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1585, 'cluster-node-timeout', '15000', '集群节点超时时间,默认15秒', '2024-01-17 10:23:00', 2, 1, 62, 0, 1)\n    ,(1586, 'cluster-migration-barrier', '3', '从节点自动迁移至少需要的可用节点数,默认1个', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1587, 'cluster-config-file', 'nodes-%d.conf', '集群配置文件名称,格式:nodes-{port}.conf', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1588, 'cluster-require-full-coverage', 'no', '节点部分失败期间,其他节点是否继续工作', '2024-01-17 10:23:00', 2, 1, 62, 0, 1)\n    ,(1589, 'port', '%d', 'sentinel实例端口', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1590, 'dir', '%s', '工作目录', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1591, 'sentinel monitor', '%s %s %d 1', 'master名称定义和最少参与监控的sentinel数,格式:masterName ip port num', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1592, 'sentinel down-after-milliseconds', '%s 20000', 'Sentinel判定服务器断线的毫秒数', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1593, 'sentinel failover-timeout', '%s 180000', '故障迁移超时时间,默认:3分钟', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1594, 'sentinel parallel-syncs', '%s 1', '在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1595, 'daemonize', 'no', '是否守护进程', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1596, 'tcp-backlog', '511', 'TCP连接完成队列', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1597, 'timeout', '0', '客户端闲置多少秒后关闭连接,默认为0,永不关闭', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1598, 'tcp-keepalive', '60', '检测客户端是否健康周期,默认关闭', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1599, 'loglevel', 'notice', '日志级别', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1600, 'databases', '16', '可用的数据库数，默认值为16个,默认数据库为0', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1601, 'dir', '%s', 'redis工作目录', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1602, 'stop-writes-on-bgsave-error', 'no', 'bgsave出错了不停写', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1603, 'repl-timeout', '60', 'master批量数据传输时间或者ping回复时间间隔,默认:60秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1604, 'repl-disable-tcp-nodelay', 'no', '是否禁用socket的NO_DELAY,默认关闭，影响主从延迟', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1605, 'repl-backlog-size', '10M', '复制缓存区,默认:1mb,配置为:10Mb', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1606, 'repl-backlog-ttl', '7200', 'master在没有从节点的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1607, 'lua-time-limit', '5000', 'Lua脚本最长的执行时间，单位为毫秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1608, 'slowlog-log-slower-than', '10000', '慢查询被记录的阀值,默认10毫秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1609, 'slowlog-max-len', '128', '最多记录慢查询的条数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1610, 'hash-max-ziplist-entries', '512', 'hash数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1611, 'hash-max-ziplist-value', '64', 'hash数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1612, 'list-max-ziplist-entries', '512', 'list数据结构优化参数', '2024-01-17 10:23:00', 6, 0, 62, 0, 1)\n    ,(1613, 'list-max-ziplist-value', '64', 'list数据结构优化参数', '2024-01-17 10:23:00', 6, 0, 62, 0, 1)\n    ,(1614, 'set-max-intset-entries', '512', 'set数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1615, 'zset-max-ziplist-entries', '128', 'zset数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1616, 'zset-max-ziplist-value', '64', 'zset数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1617, 'activerehashing', 'yes', '是否激活重置哈希,默认:yes', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1618, 'client-output-buffer-limit normal', '0 0 0', '客户端输出缓冲区限制(客户端)', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1619, 'client-output-buffer-limit pubsub', '32mb 8mb 60', '客户端输出缓冲区限制(发布订阅)', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1620, 'hz', '10', '执行后台task数量,默认:10', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1621, 'port', '%d', '端口', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1622, 'maxmemory', '%dmb', '当前实例最大可用内存', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1623, 'maxmemory-policy', 'volatile-lfu', '内存不够时,淘汰策略,默认:volatile-lfu', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1624, 'appendonly', 'yes', '开启append only持久化模式', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1625, 'appendfsync', 'everysec', '默认:aof每秒同步一次', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1626, 'appendfilename', 'appendonly-%d.aof', 'aof文件名称,默认:appendonly-{port}.aof', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1627, 'dbfilename', 'dump-%d.rdb', 'RDB文件默认名称,默认dump-{port}.rdb', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1628, 'aof-rewrite-incremental-fsync', 'yes', 'aof rewrite过程中,是否采取增量文件同步策略,默认:yes', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1629, 'no-appendfsync-on-rewrite', 'yes', '是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1630, 'auto-aof-rewrite-min-size', '64m', '触发rewrite的aof文件最小阀值,默认64m', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1631, 'auto-aof-rewrite-percentage', '%d', 'Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1632, 'maxclients', '10000', '客户端最大连接数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1633, 'protected-mode', 'no', '开启保护模式', '2024-06-05 11:55:57', 6, 1, 62, 0, 0)\n    ,(1634, 'bind', '0.0.0.0 -::*', '默认客户端都可连接', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1635, 'list-max-ziplist-size', '-2', '8Kb对象以内采用ziplist', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1636, 'list-compress-depth', '0', '压缩方式，0:不压缩', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1637, 'always-show-logo', 'yes', 'redis启动是否显示logo', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1638, 'lazyfree-lazy-eviction', 'yes', '在被动淘汰键时，是否采用lazy free机制,默认:no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1639, 'lazyfree-lazy-expire', 'yes', 'TTL的键过期是否采用lazyfree机制 默认值:no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1640, 'lazyfree-lazy-server-del', 'yes', '隐式的DEL键(rename)是否采用lazyfree机制 默认值:no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1641, 'aof-use-rdb-preamble', 'yes', '是否开启混合持久化,默认值 no 不开启', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1642, 'protected-mode', 'no', '关闭sentinel保护模式', '2024-01-17 10:23:00', 5, 1, 62, 0, 0)\n    ,(1643, 'activedefrag', 'no', '碎片整理开启', '2024-01-17 10:23:00', 6, 1, 62, 0, 1)\n    ,(1644, 'active-defrag-threshold-lower', '10', '碎片率达到百分之多少开启整理', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1645, 'active-defrag-threshold-upper', '100', '碎片率小余多少百分比开启整理', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1646, 'active-defrag-ignore-bytes', '300mb', '内存碎片达到多少兆开启碎片', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1647, 'active-defrag-cycle-min', '10', '碎片整理最小cpu百分比', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1648, 'active-defrag-cycle-max', '30', '碎片整理最大cpu百分比', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1649, 'active-defrag-max-scan-fields', '1000', '内存碎片处理set/hash/zset/list 中的最大数量的项', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1650, 'replica-serve-stale-data', 'yes', '从节点与master断连或复制命令响应：yes 继续响应 no:相关命令返回异常信息', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1651, 'cluster-replica-validity-factor', '10', '从节点延迟有效性判断因子,默认10秒', '2024-01-17 10:23:00', 2, 1, 62, 0, 0)\n    ,(1652, 'replica-priority', '100', '从节点的优先级,影响sentinel/cluster晋升master操作,0永远不晋升', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1653, 'replica-read-only', 'yes', '从节点是否只读: yes 只读', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1654, 'replica-lazy-flush', 'yes', '从节点发起全量复制,是否采用flushall async清理老数据 默认值 no', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1655, 'client-output-buffer-limit replica', '512mb 256mb 60', '客户端输出缓冲区限制', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1656, 'replica-ignore-maxmemory', 'yes', '从节点是否开启最大内存，避免一些过大缓冲区导致oom', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1657, 'stream-node-max-bytes', '4096', 'stream数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1658, 'stream-node-max-entries', '100', 'stream数据结构优化参数', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1659, 'dynamic-hz', 'yes', '自适应平衡空闲CPU的使用率和响应', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1660, 'rdb-save-incremental-fsync', 'yes', 'rdb同步刷盘是否采用增量fsync，每32MB执行一次fsync', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1661, 'repl-ping-replica-period', '10', '指定从节点定期ping master的周期,默认:10秒', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1662, 'latency-monitor-threshold', '30', '延迟事件阀值，单位ms', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1663, 'repl-diskless-load', 'on-empty-db', '完全安全的情况下才使用无磁盘加载', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1664, 'tracking-table-max-keys', '1000000', '无效表键的最大填充数量', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1665, 'rdb-del-sync-files', 'yes', '默认:no 不删除rdb文件,删除实例中复制使用的不持久的RDB文件', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1666, 'lazyfree-lazy-user-del', 'yes', '默认值no,设置del操作命令同unlink一致', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1667, 'io-threads', '1', '读写io线程数量', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1668, 'io-threads-do-reads', 'no', '开启io读线程', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1669, 'jemalloc-bg-thread', 'yes', '启用Jemalloc后台线程清理', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1670, 'server_cpulist', '0-7:2', '设置redis服务器/io线程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1671, 'bio_cpulist', '1,3', '设置bio线程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1672, 'aof_rewrite_cpulist', '8-11', '设置aof重写子进程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0)\n    ,(1673, 'bgsave_cpulist', '1,10-11', '设置bgsave子进程的cpu使用权重', '2024-01-17 10:23:00', 6, 1, 62, 0, 0);"
  },
  {
    "path": "cachecloud-web/sql/update-system_resource.sql",
    "content": "-- system_resource change\nALTER TABLE system_resource ADD order_num int(6) NOT NULL DEFAULT '0' COMMENT '排序';\n"
  },
  {
    "path": "cachecloud-web/sql/update2.0-2.1.sql",
    "content": "create or replace table app_import(\n    id bigint auto_increment\n    primary key,\n    app_id bigint null comment '目标应用id',\n    instance_info text null comment '源redis实例信息',\n    redis_password varchar (200) null comment '源redis密码',\n    status int null comment '迁移状态：PREPARE(0, \"准备\", \"应用导入-未开始\"),\n    START(1, \"进行中...\", \"应用导入-开始\"),\n    ERROR(2, \"error\", \"应用导入-出错\"),\n    VERSION_BUILD_START(11, \"进行中...\", \"新建redis版本-进行中\"),\n    VERSION_BUILD_ERROR(12, \"error\", \"新建redis版本-出错\"),\n    VERSION_BUILD_END(20, \"成功\", \"新建redis版本-完成\"),\n    APP_BUILD_INIT(21, \"准备就绪\", \"新建redis应用-准备就绪\"),\n    APP_BUILD_START(22, \"进行中...\", \"新建redis应用-进行中\"),\n    APP_BUILD_ERROR(23, \"error\", \"新建redis应用-出错\"),\n    APP_BUILD_END(30, \"成功\", \"新建redis应用-完成\"),\n    MIGRATE_INIT(31, \"准备就绪\", \"数据迁移-准备就绪\"),\n    MIGRATE_START(32, \"进行中...\", \"数据迁移-进行中\"),\n    MIGRATE_ERROR(33, \"error\", \"数据迁移-出错\"),\n    MIGRATE_END(3, \"成功\", \"应用导入-成功\")',\n    step int null comment '导入阶段',\n    create_time timestamp default CURRENT_TIMESTAMP not null,\n    update_time timestamp default CURRENT_TIMESTAMP null,\n    migrate_id bigint null comment '数据迁移id',\n    mem_size int null comment '目标应用内存大小，单位G',\n    redis_version_name varchar (20) null comment '目标应用redis版本，格式：redis-x.x.x',\n    app_build_task_id bigint null comment '目标应用部署任务id',\n    source_type int null comment '源redis类型：7:cluster, 6:sentinel, 5:standalone'\n);"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ApplicationStarter.java",
    "content": "package com.sohu.cache;\n\nimport org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;\nimport org.springframework.boot.Banner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.web.servlet.ServletComponentScan;\nimport org.springframework.context.annotation.ImportResource;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n/**\n * Created by zhangyijun\n */\n@SpringBootApplication\n@EnableAutoConfiguration(exclude = {MybatisAutoConfiguration.class})\n@ImportResource(\"${spring.application.import}\")\n@EnableAsync\npublic class ApplicationStarter {\n\n    public static void main(String[] args) {\n        SpringApplication app = new SpringApplication(ApplicationStarter.class);\n        app.setAdditionalProfiles();\n        app.setBannerMode(Banner.Mode.LOG);\n        app.run(args);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/InstanceAlertService.java",
    "content": "package com.sohu.cache.alert;\r\n\r\nimport com.sohu.cache.entity.InstanceFault;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 实例报警检测\r\n * @author leifu\r\n * @Date 2014年12月16日\r\n * @Time 下午1:56:35\r\n */\r\npublic interface InstanceAlertService {\r\n    \r\n    /**\r\n     * 实例故障列表\r\n     *\r\n     * @param instId\r\n     * @return\r\n     */\r\n    List<InstanceFault> getListByInstId(int instId);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/bean/AlertConfigBaseData.java",
    "content": "package com.sohu.cache.alert.bean;\n\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.StandardStats;\n\n/**\n * 报警基础数据\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:19:10\n */\npublic class AlertConfigBaseData {\n    /**\n     * 基准数据\n     */\n    private StandardStats standardStats;\n    \n    /**\n     * 实例信息\n     */\n    private InstanceInfo instanceInfo;\n\n    public StandardStats getStandardStats() {\n        return standardStats;\n    }\n\n    public void setStandardStats(StandardStats standardStats) {\n        this.standardStats = standardStats;\n    }\n\n    public InstanceInfo getInstanceInfo() {\n        return instanceInfo;\n    }\n\n    public void setInstanceInfo(InstanceInfo instanceInfo) {\n        this.instanceInfo = instanceInfo;\n    }\n    \n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/impl/BaseAlertService.java",
    "content": "package com.sohu.cache.alert.impl;\r\n\r\nimport com.sohu.cache.alert.EmailComponent;\r\nimport com.sohu.cache.alert.WeChatComponent;\r\nimport com.sohu.cache.web.service.AppAlertRecordService;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\n\r\n/**\r\n * 报警基类\r\n *\r\n * @author leifu\r\n * @Date 2014年12月16日\r\n * @Time 下午4:15:11\r\n */\r\npublic class BaseAlertService {\r\n    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\r\n\r\n    /**\r\n     * 邮箱报警\r\n     */\r\n    @Autowired(required = false)\r\n    protected EmailComponent emailComponent;\r\n\r\n    /**\r\n     * 报警记录\r\n     */\r\n    @Autowired(required = false)\r\n    protected AppAlertRecordService appAlertRecordService;\r\n\r\n    /**\r\n     * 微信报警\r\n     */\r\n    @Autowired(required = false)\r\n    protected WeChatComponent weChatComponent;\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/impl/InstanceAlertServiceImpl.java",
    "content": "package com.sohu.cache.alert.impl;\r\n\r\nimport com.sohu.cache.alert.InstanceAlertService;\r\nimport com.sohu.cache.dao.InstanceFaultDao;\r\nimport com.sohu.cache.entity.InstanceFault;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 实例报警\r\n *\r\n * @author leifu\r\n */\r\n@Service\r\npublic class InstanceAlertServiceImpl implements InstanceAlertService {\r\n\r\n    @Autowired\r\n    private InstanceFaultDao instanceFaultDao;\r\n\r\n    @Override\r\n    public List<InstanceFault> getListByInstId(int instId) {\r\n        return instanceFaultDao.getListByInstId(instId);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/AlertConfigStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.StandardStats;\nimport com.sohu.cache.redis.enums.InstanceAlertCompareTypeEnum;\nimport com.sohu.cache.util.JsonUtil;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\n/**\n * @author leifu\n * @Date 2017年6月2日\n * @Time 下午3:24:53\n */\npublic abstract class AlertConfigStrategy {\n    \n    protected final static String MB_STRING = \"MB\";\n    protected final static String EMPTY = \"\";\n\n    /**\n     * 检查配置\n     * \n     * @param instanceAlertConfig\n     * @param alertConfigBaseData\n     */\n    public abstract List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig,\n            AlertConfigBaseData alertConfigBaseData);\n\n    /**\n     * 比较long类型\n     * \n     * @param instanceAlertConfig 报警配置\n     * @param currentValue 当前值\n     * @return\n     */\n    protected boolean isCompareLongRight(InstanceAlertConfig instanceAlertConfig, long currentValue) {\n        long alertValue = NumberUtils.toLong(instanceAlertConfig.getAlertValue());\n        int compareType = instanceAlertConfig.getCompareType();\n        if (compareType == InstanceAlertCompareTypeEnum.LESS_THAN.getValue() && currentValue < alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.MORE_THAN.getValue() && currentValue > alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.EQUAL.getValue() && currentValue == alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.NOT_EQUAL.getValue() && currentValue != alertValue) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 比较int类型\n     * \n     * @param instanceAlertConfig 报警配置\n     * @param currentValue 当前值\n     * @return\n     */\n    protected boolean isCompareIntRight(InstanceAlertConfig instanceAlertConfig, int currentValue) {\n        int alertValue = NumberUtils.toInt(instanceAlertConfig.getAlertValue());\n        int compareType = instanceAlertConfig.getCompareType();\n        if (compareType == InstanceAlertCompareTypeEnum.LESS_THAN.getValue() && currentValue < alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.MORE_THAN.getValue() && currentValue > alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.EQUAL.getValue() && currentValue == alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.NOT_EQUAL.getValue() && currentValue != alertValue) {\n            return false;\n        }\n        return true;\n    }\n    \n    /**\n     * 比较double类型\n     * \n     * @param instanceAlertConfig 报警配置\n     * @param currentValue 当前值\n     * @return\n     */\n    protected boolean isCompareDoubleRight(InstanceAlertConfig instanceAlertConfig, double currentValue) {\n        double alertValue = NumberUtils.toDouble(instanceAlertConfig.getAlertValue());\n        int compareType = instanceAlertConfig.getCompareType();\n        if (compareType == InstanceAlertCompareTypeEnum.LESS_THAN.getValue() && currentValue < alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.MORE_THAN.getValue() && currentValue > alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.EQUAL.getValue() && currentValue == alertValue) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.NOT_EQUAL.getValue() && currentValue != alertValue) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 比较字符串类型\n     * \n     * @param instanceAlertConfig 报警配置\n     * @param currentValue 当期值\n     * @return\n     */\n    protected boolean isCompareStringRight(InstanceAlertConfig instanceAlertConfig, String currentValue) {\n        String alertValue = instanceAlertConfig.getAlertValue();\n        int compareType = instanceAlertConfig.getCompareType();\n        if (compareType == InstanceAlertCompareTypeEnum.EQUAL.getValue() && currentValue.equals(alertValue)) {\n            return false;\n        } else if (compareType == InstanceAlertCompareTypeEnum.NOT_EQUAL.getValue()\n                && !currentValue.equals(alertValue)) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 获取全量统计项中的内容\n     */\n    protected static Object getValueFromRedisInfo(StandardStats standardStats, String attribute) {\n        if (standardStats == null) {\n            return null;\n        }\n        // 转换成Map\n        Map<String, Object> infoMap = JsonUtil.fromJson(standardStats.getInfoJson(), Map.class);\n        if (MapUtils.isEmpty(infoMap)) {\n            return null;\n        }\n        for (Entry<String, Object> entry : infoMap.entrySet()) {\n            Object object = entry.getValue();\n            // 转换成Map<String, Map<String,Object>>\n            if (!(object instanceof Map)) {\n                continue;\n            }\n            Map<String, Object> sectionInfoMap = (Map<String, Object>) object;\n            if (sectionInfoMap.containsKey(attribute)) {\n                return MapUtils.getObject(sectionInfoMap, attribute);\n            }\n        }\n        return null;\n    }\n    \n    /**\n     * 获取差值统计项中的内容\n     * @param redisInfo\n     * @param attribute\n     * @return\n     */\n    protected static Object getValueFromDiffInfo(StandardStats standardStats, String attribute) {\n        if (standardStats == null) {\n            return null;\n        }\n        Map<String, Object> diffInfoMap = JsonUtil.fromJson(standardStats.getDiffJson(), Map.class);\n        if (MapUtils.isEmpty(diffInfoMap)) {\n            return null;\n        }\n        return MapUtils.getObject(diffInfoMap, attribute);\n    }\n    \n    /**\n     * 获取cluster info统计项中的内容\n     * @param redisInfo\n     * @param attribute\n     * @return\n     */\n    protected static Object getValueFromClusterInfo(StandardStats standardStats, String attribute) {\n        if (standardStats == null) {\n            return null;\n        }\n        Map<String, Object> clusterInfoMap = JsonUtil.fromJson(standardStats.getClusterInfoJson(), Map.class);\n        if (MapUtils.isEmpty(clusterInfoMap)) {\n            return null;\n        }\n        return MapUtils.getObject(clusterInfoMap, attribute);\n    }\n    \n    /**\n     * 把字节变为兆\n     * @param value\n     * @return\n     */\n    protected long changeByteToMB(long value) {\n        return value / 1024 / 1024;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/AofCurrentSizeAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * aof当前尺寸检测\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class AofCurrentSizeAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.aof_current_size.getValue());\n        // 没有配置Aof\n        if (object == null) {\n            return null;\n        }\n        long aofCurrentSize = NumberUtils.toLong(object.toString());\n        aofCurrentSize = changeByteToMB(aofCurrentSize);\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, aofCurrentSize);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(aofCurrentSize),\n                instanceInfo.getAppId(), MB_STRING));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/ClientBiggestInputBufAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 客户端输入缓冲区最大buffer\n * \n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class ClientBiggestInputBufAlertStrategy extends AlertConfigStrategy {\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.client_biggest_input_buf.getValue());\n        if (object == null) {\n            return null;\n        }\n        // 关系比对\n        long clientBiggestInputBuf = NumberUtils.toLong(object.toString()) ;\n        clientBiggestInputBuf = changeByteToMB(clientBiggestInputBuf);\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, clientBiggestInputBuf);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(clientBiggestInputBuf),\n                instanceInfo.getAppId(), MB_STRING));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/ClientLongestOutputListAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 客户端输出缓冲区最大队列长度\n * \n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class ClientLongestOutputListAlertStrategy extends AlertConfigStrategy {\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.client_longest_output_list.getValue());\n        if (object == null) {\n            return null;\n        }\n        // 关系比对\n        long clientLongestOutputList = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, clientLongestOutputList);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(clientLongestOutputList),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/ClusterSlotsOkAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisClusterInfoEnum;\n\n/**\n * 集群成功分配槽个数监控\n * @author leifu\n * @Date 2017年6月21日\n * @Time 下午3:01:21\n */\npublic class ClusterSlotsOkAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromClusterInfo(alertConfigBaseData.getStandardStats(), RedisClusterInfoEnum.cluster_slots_ok.getValue());\n        if (object == null) {\n            return null;\n        }\n        // 关系比对\n        int clusterSlotsOk = NumberUtils.toInt(object.toString());\n        boolean compareRight = isCompareIntRight(instanceAlertConfig, clusterSlotsOk);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(clusterSlotsOk),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/ClusterStateAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisClusterInfoEnum;\n\n/**\n * 集群状态监控\n * @author leifu\n * @Date 2017年6月21日\n * @Time 下午3:01:21\n */\npublic class ClusterStateAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromClusterInfo(alertConfigBaseData.getStandardStats(), RedisClusterInfoEnum.cluster_state.getValue());\n        if (object == null) {\n            return null;\n        }\n        // 关系比对\n        String clusterState = object.toString();\n        boolean compareRight = isCompareStringRight(instanceAlertConfig, clusterState);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(clusterState),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/DefaultCommonAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * aof当前尺寸检测\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class DefaultCommonAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), instanceAlertConfig.getAlertConfig());\n        if (object == null) {\n            return null;\n        }\n        if(judgeNumber(object)){\n            if(judegNumberIsDouble(object)){\n                double currentValue= NumberUtils.toDouble(object.toString());\n                boolean compareRight = isCompareDoubleRight(instanceAlertConfig, currentValue);\n                if (compareRight) {\n                    return null;\n                }\n            }else{\n                long currentValue = NumberUtils.toLong(object.toString());\n                boolean compareRight = isCompareLongRight(instanceAlertConfig, currentValue);\n                if (compareRight) {\n                    return null;\n                }\n            }\n        }else{\n            String currentValue = object.toString();\n            boolean compareRight = isCompareStringRight(instanceAlertConfig, currentValue);\n            if (compareRight) {\n                return null;\n            }\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, object.toString(),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n    private boolean judgeNumber(Object object){\n        Pattern numberPattern = Pattern.compile(\"^-?(([0-9]|([1-9][0-9]*))(\\\\.[0-9]+)?)$\");\n        Matcher matcher = numberPattern.matcher(object.toString());\n        return matcher.matches();\n    }\n\n    private boolean judegNumberIsDouble(Object object){\n        return object.toString().contains(\".\");\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/InstantaneousOpsPerSecAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 实时ops\n * \n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class InstantaneousOpsPerSecAlertStrategy extends AlertConfigStrategy {\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.instantaneous_ops_per_sec.getValue());\n        if (object == null) {\n            return null;\n        }\n        // 关系比对\n        long instantaneousOpsPerSec = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, instantaneousOpsPerSec);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(instantaneousOpsPerSec),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/LatestForkUsecAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 上一次fork的微秒\n * \n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class LatestForkUsecAlertStrategy extends AlertConfigStrategy {\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.latest_fork_usec.getValue());\n        if (object == null) {\n            return null;\n        }\n        // 关系比对\n        long latestForkUsec = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, latestForkUsec);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(latestForkUsec),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MasterSlaveOffsetAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 主从偏移量监控\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MasterSlaveOffsetAlertStrategy extends AlertConfigStrategy {\n\n    /**\n     * 格式：\n     *     connected_slaves:2\n     *     slave0:ip=x.x.x.x,port=6380,state=online,offset=33119690469561,lag=1\n     *     slave1:ip=x.x.x.x,port=6380,state=online,offset=33119690513578,lag=0\n     *     master_repl_offset:33119653194425\n     */\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object connectedSlavesObject = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.connected_slaves.getValue());\n        if (connectedSlavesObject == null) {\n            return null;\n        }\n        int connectedSlaves = NumberUtils.toInt(connectedSlavesObject.toString());\n        if (connectedSlaves == 0) {\n            return null;\n        }\n        Object masterReplOffsetObject = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.master_repl_offset.getValue());\n        if (masterReplOffsetObject == null) {\n            return null;\n        }\n        List<InstanceAlertValueResult> instanceAlertValueResultList = new ArrayList<InstanceAlertValueResult>();\n        for (int i = 0; i < connectedSlaves; i++) {\n            Object slaveInfo = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), \"slave\" + i);\n            if (slaveInfo == null) {\n                continue;\n            }\n            String[] arr = slaveInfo.toString().split(\",\");\n            if (arr.length < 5) {\n                continue;\n            }\n            String state = arr[2];\n            if (!\"state=online\".equals(state)) {\n                continue;\n            }\n            String slaveHostPort = arr[0] + \",\" + arr[1];\n            String slaveOffsetStr = arr[3];\n            String[] slaveOffsetArr = slaveOffsetStr.split(\"=\");\n            if (slaveOffsetArr.length != 2) {\n                continue;\n            }\n            String slaveOffset = slaveOffsetArr[1];\n            long diffOffset = Math.abs(NumberUtils.toLong(masterReplOffsetObject.toString()) - NumberUtils.toLong(slaveOffset));\n            boolean compareRight = isCompareDoubleRight(instanceAlertConfig, diffOffset);\n            if (compareRight) {\n                return null;\n            }\n            InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n            InstanceAlertValueResult instanceAlertValueResult = new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(diffOffset),\n                    instanceInfo.getAppId(), EMPTY);\n            String otherInfo = String.format(\"masterOffset is %s<br/>slaveOffset  is %s<br/>%s\", masterReplOffsetObject.toString(), slaveOffset, slaveHostPort);\n            instanceAlertValueResult.setOtherInfo(otherInfo);\n            instanceAlertValueResultList.add(instanceAlertValueResult);\n        }\n        return instanceAlertValueResultList;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MemFragmentationRatioAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 内存碎片率\n * \n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MemFragmentationRatioAlertStrategy extends AlertConfigStrategy {\n    \n    /**\n     * 实例最小500MB才进行内存碎片率检查，否则价值不是很大\n     */\n    private final static long MIN_CHECK_MEMORY = 500L * 1024 * 1024;\n    \n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        // 检查内存\n        Object usedMemoryObject = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.used_memory.getValue());\n        long usedMemory = NumberUtils.toLong(usedMemoryObject.toString());\n        if (usedMemory < MIN_CHECK_MEMORY) {\n            return null;\n        }\n        \n        // 内存碎片率\n        Object memFragmentationRatioObject = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.mem_fragmentation_ratio.getValue());\n        if (memFragmentationRatioObject == null) {\n            return null;\n        }\n        \n        // 关系比对\n        double memFragmentationRatio = NumberUtils.toDouble(memFragmentationRatioObject.toString());\n        boolean compareRight = isCompareDoubleRight(instanceAlertConfig, memFragmentationRatio);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        InstanceAlertValueResult instanceAlertValueResult = new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(memFragmentationRatio),\n                instanceInfo.getAppId(), EMPTY);\n        instanceAlertValueResult.setOtherInfo(String.format(\"内存使用为%s MB\", String.valueOf(changeByteToMB(usedMemory))));\n        return Arrays.asList(instanceAlertValueResult);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteAofDelayedFsyncAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 分钟aof delay fsync检测\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MinuteAofDelayedFsyncAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.aof_delayed_fsync.getValue());\n        if (object == null) {\n            return null;\n        }\n        long aofDelayedFsync = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, aofDelayedFsync);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(aofDelayedFsync),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteRejectedConnectionsAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 分钟拒绝客户端连接数\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MinuteRejectedConnectionsAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.rejected_connections.getValue());\n        if (object == null) {\n            return null;\n        }\n        long minuteRejectedConnections = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, minuteRejectedConnections);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(minuteRejectedConnections),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteSyncFullAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 分钟全量复制次数\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MinuteSyncFullAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.sync_full.getValue());\n        if (object == null) {\n            return null;\n        }\n        long minuteSyncFull = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, minuteSyncFull);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(minuteSyncFull),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteSyncPartialErrAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 分钟部分复制失败次数\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MinuteSyncPartialErrAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.sync_partial_err.getValue());\n        if (object == null) {\n            return null;\n        }\n        long minuteSyncPartialErr = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, minuteSyncPartialErr);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(minuteSyncPartialErr),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteSyncPartialOkAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 分钟部分复制成功次数\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MinuteSyncPartialOkAlertStrategy extends AlertConfigStrategy {\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.sync_partial_ok.getValue());\n        if (object == null) {\n            return null;\n        }\n        long minuteSyncPartialOk = NumberUtils.toLong(object.toString());\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, minuteSyncPartialOk);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(minuteSyncPartialOk),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteTotalNetInputMBytesAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 分钟输入网络流量\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MinuteTotalNetInputMBytesAlertStrategy extends AlertConfigStrategy {\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object totalNetInputBytesObject = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.total_net_input_bytes.getValue());\n        if (totalNetInputBytesObject == null) {\n            return null;\n        }\n        // 关系比对\n        long totalNetInputBytes = NumberUtils.toLong(totalNetInputBytesObject.toString()) ;\n        totalNetInputBytes = changeByteToMB(totalNetInputBytes);\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, totalNetInputBytes);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(totalNetInputBytes),\n                instanceInfo.getAppId(), MB_STRING));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteTotalNetOutputMBytesAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * 分钟输出网络流量\n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class MinuteTotalNetOutputMBytesAlertStrategy extends AlertConfigStrategy {\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object totalNetOutputBytesObject = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.total_net_output_bytes.getValue());\n        if (totalNetOutputBytesObject == null) {\n            return null;\n        }\n        // 关系比对\n        long totalNetOutputBytes = NumberUtils.toLong(totalNetOutputBytesObject.toString());\n        totalNetOutputBytes = changeByteToMB(totalNetOutputBytes);\n        boolean compareRight = isCompareLongRight(instanceAlertConfig, totalNetOutputBytes);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(totalNetOutputBytes),\n                instanceInfo.getAppId(), MB_STRING));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteUsedCpuSysChStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2021/6/9 上午11:03\n */\npublic class MinuteUsedCpuSysChStrategy extends AlertConfigStrategy{\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.used_cpu_sys_children.getValue());\n        if (object == null) {\n            return null;\n        }\n        double min_used_cpu_sys_children_err= NumberUtils.toDouble(object.toString());\n        boolean compareRight = isCompareDoubleRight(instanceAlertConfig, min_used_cpu_sys_children_err);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(min_used_cpu_sys_children_err),\n                instanceInfo.getAppId(), EMPTY));\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteUsedCpuSysStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2021/6/9 上午11:03\n */\npublic class MinuteUsedCpuSysStrategy extends AlertConfigStrategy{\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.used_cpu_sys.getValue());\n        if (object == null) {\n            return null;\n        }\n        double min_used_cpu_sys_err= NumberUtils.toDouble(object.toString());\n        boolean compareRight = isCompareDoubleRight(instanceAlertConfig, min_used_cpu_sys_err);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(min_used_cpu_sys_err),\n                instanceInfo.getAppId(), EMPTY));\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteUsedCpuUserChStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2021/6/9 上午11:03\n */\npublic class MinuteUsedCpuUserChStrategy extends AlertConfigStrategy{\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.used_cpu_user_children.getValue());\n        if (object == null) {\n            return null;\n        }\n        double min_used_cpu_user_children_err= NumberUtils.toDouble(object.toString());\n        boolean compareRight = isCompareDoubleRight(instanceAlertConfig, min_used_cpu_user_children_err);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(min_used_cpu_user_children_err),\n                instanceInfo.getAppId(), EMPTY));\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/MinuteUsedCpuUserStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2021/6/9 上午11:03\n */\npublic class MinuteUsedCpuUserStrategy extends AlertConfigStrategy{\n\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromDiffInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.used_cpu_user.getValue());\n        if (object == null) {\n            return null;\n        }\n        double min_used_cpu_user_err= NumberUtils.toDouble(object.toString());\n        boolean compareRight = isCompareDoubleRight(instanceAlertConfig, min_used_cpu_user_err);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(min_used_cpu_user_err),\n                instanceInfo.getAppId(), EMPTY));\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/alert/strategy/RdbLastBgsaveStatusAlertStrategy.java",
    "content": "package com.sohu.cache.alert.strategy;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.entity.InstanceAlertConfig;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\n\n/**\n * RDB最近一次bgsave的执行状态\n * \n * @author leifu\n * @Date 2017年6月16日\n * @Time 下午2:34:10\n */\npublic class RdbLastBgsaveStatusAlertStrategy extends AlertConfigStrategy {\n    @Override\n    public List<InstanceAlertValueResult> checkConfig(InstanceAlertConfig instanceAlertConfig, AlertConfigBaseData alertConfigBaseData) {\n        Object object = getValueFromRedisInfo(alertConfigBaseData.getStandardStats(), RedisInfoEnum.rdb_last_bgsave_status.getValue());\n        if (object == null) {\n            return null;\n        }\n        // 关系比对\n        String rdbLastBgsaveStatus = object.toString();\n        boolean compareRight = isCompareStringRight(instanceAlertConfig, rdbLastBgsaveStatus);\n        if (compareRight) {\n            return null;\n        }\n        InstanceInfo instanceInfo = alertConfigBaseData.getInstanceInfo();\n        return Arrays.asList(new InstanceAlertValueResult(instanceAlertConfig, instanceInfo, String.valueOf(rdbLastBgsaveStatus),\n                instanceInfo.getAppId(), EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/async/AsyncService.java",
    "content": "package com.sohu.cache.async;\r\n\r\nimport java.util.concurrent.Callable;\r\nimport java.util.concurrent.Future;\r\nimport java.util.concurrent.ThreadPoolExecutor;\r\n\r\n/**\r\n * 异步服务类\r\n * Created by yijunzhang on 14-6-18.\r\n */\r\npublic interface AsyncService {\r\n\r\n    /**\r\n     * 提交任务\r\n     *\r\n     * @param callable\r\n     * @return 返回是否提交成功\r\n     */\r\n    public boolean submitFuture(KeyCallable<?> callable);\r\n\r\n    /**\r\n     * 提交任务并可拿到返回结果\r\n     * @param callable\r\n     * @return\r\n     */\r\n    public Future<?> submitFutureWithRst(KeyCallable<?> callable);\r\n\r\n    /**\r\n     * 提交任务\r\n     *\r\n     * @param threadPoolKey\r\n     * @param callable\r\n     * @return 返回是否提交成功\r\n     */\r\n    public boolean submitFuture(String threadPoolKey, KeyCallable<?> callable);\r\n\r\n    /**\r\n     * 提交任务\r\n     *\r\n     * @param threadPoolKey\r\n     * @param callable\r\n     * @return 返回任务结果\r\n     */\r\n    public Future<?> submitFutureWithRst(String threadPoolKey, KeyCallable<?> callable);\r\n\r\n    /**\r\n     * 提交任务\r\n     *\r\n     * @param callable\r\n     * @return 返回成功结果\r\n     */\r\n    public Future<?> submitFuture(Callable<?> callable);\r\n\r\n    /**\r\n     * 装配key对应的线程池\r\n     *\r\n     * @param threadPoolKey\r\n     * @param threadPool\r\n     */\r\n    public void assemblePool(String threadPoolKey, ThreadPoolExecutor threadPool);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/async/AsyncThreadPoolFactory.java",
    "content": "package com.sohu.cache.async;\r\n\r\nimport java.util.concurrent.LinkedBlockingQueue;\r\nimport java.util.concurrent.SynchronousQueue;\r\nimport java.util.concurrent.ThreadPoolExecutor;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\n/**\r\n * 异步线程池 Created by yijunzhang on 14-7-10.\r\n */\r\npublic class AsyncThreadPoolFactory {\r\n\r\n    public static final String DEFAULT_ASYNC_POOL = \"async-pool\";\r\n    public static final ThreadPoolExecutor DEFAULT_ASYNC_THREAD_POOL = new ThreadPoolExecutor(256, 256,\r\n            0L, TimeUnit.MILLISECONDS,\r\n            new LinkedBlockingQueue<Runnable>(1024),\r\n                new NamedThreadFactory(DEFAULT_ASYNC_POOL, true),new CounterRejectedExecutionHandler());\r\n\r\n    public static final String TASK_EXECUTE_POOL = \"task-execute-pool\";\r\n    public static final ThreadPoolExecutor TASK_EXECUTE_THREAD_POOL =\r\n            new ThreadPoolExecutor(200, 200, 0L, TimeUnit.MILLISECONDS,\r\n                    new LinkedBlockingQueue<Runnable>(500), new NamedThreadFactory(TASK_EXECUTE_POOL, true), new CounterRejectedExecutionHandler());\r\n\r\n    public static final String CLIENT_REPORT_POOL =\"client-report-pool\";\r\n    public static final ThreadPoolExecutor CLIENT_REPORT_THREAD_POOL = new ThreadPoolExecutor(100,\r\n            100, 0L, TimeUnit.MILLISECONDS,\r\n            new SynchronousQueue<Runnable>(), new NamedThreadFactory(CLIENT_REPORT_POOL, true),new CounterRejectedExecutionHandler());\r\n\r\n    public static final String REDIS_SLOWLOG_POOL = \"redis-slowlog-pool\";\r\n    public static final ThreadPoolExecutor REDIS_SLOWLOG_THREAD_POOL = new ThreadPoolExecutor(30,\r\n            30, 0L, TimeUnit.MILLISECONDS,\r\n            new LinkedBlockingQueue<Runnable>(256), new NamedThreadFactory(REDIS_SLOWLOG_POOL, true),new CounterRejectedExecutionHandler());\r\n\r\n    public static final String MACHINE_POOL = \"machine-ssh-pool\";\r\n    public static final ThreadPoolExecutor MACHINE_THREAD_POOL = new ThreadPoolExecutor(256,\r\n            512, 0L, TimeUnit.MILLISECONDS,\r\n            new LinkedBlockingQueue<Runnable>(2048), new NamedThreadFactory(MACHINE_POOL, true));\r\n\r\n    public static final String APP_POOL = \"app-pool\";\r\n    public static final ThreadPoolExecutor APP_THREAD_POOL = new ThreadPoolExecutor(10,\r\n            10, 0L, TimeUnit.MILLISECONDS,\r\n            new LinkedBlockingQueue<Runnable>(256), new NamedThreadFactory(APP_POOL, true));\r\n\r\n    public static final String BREVITY_SCHEDULER_POOL = \"brevity-scheduler-pool\";\r\n    public static final ThreadPoolExecutor BREVITY_SCHEDULER_ASYNC_THREAD_POOL = new ThreadPoolExecutor(10, 50,\r\n            0L, TimeUnit.MILLISECONDS,\r\n            new LinkedBlockingQueue<Runnable>(2048),\r\n            new NamedThreadFactory(BREVITY_SCHEDULER_POOL, true),new CounterRejectedExecutionHandler());\r\n\r\n    public static final String RESHARD_PROCESS_POOL = \"redis-cluster-reshard\";\r\n    public static final ThreadPoolExecutor RESHARD_PROCESS_THREAD_POOL = new ThreadPoolExecutor(10, 100,\r\n            0L, TimeUnit.MILLISECONDS,\r\n            new LinkedBlockingQueue<Runnable>(256),\r\n            new NamedThreadFactory(RESHARD_PROCESS_POOL, false),new CounterRejectedExecutionHandler());\r\n\r\n    public static final String ALERT_RECORD_POOL = \"alert-record-pool\";\r\n    public static final ThreadPoolExecutor ALERT_RECORD_THREAD_POOL = new ThreadPoolExecutor(3, 10,\r\n            0L, TimeUnit.MILLISECONDS,\r\n            new LinkedBlockingQueue<Runnable>(1024),\r\n            new NamedThreadFactory(ALERT_RECORD_POOL, false));\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/async/CounterRejectedExecutionHandler.java",
    "content": "package com.sohu.cache.async;\n\nimport com.google.common.util.concurrent.AtomicLongMap;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.concurrent.RejectedExecutionException;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * @author fulei\n * @date 2018年8月18日\n * @time 上午10:29:59\n */\npublic class CounterRejectedExecutionHandler implements RejectedExecutionHandler {\n\t\n    private Logger logger = LoggerFactory.getLogger(CounterRejectedExecutionHandler.class);\n\t\n    public static final AtomicLongMap<String> THREAD_POOL_REJECT_MAP = AtomicLongMap.create();\n    \n\t@Override\n\tpublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {\n\t\tNamedThreadFactory namedThreadFactory = (NamedThreadFactory) executor.getThreadFactory();\n\t\tString threadPoolName = namedThreadFactory.getThreadPoolName();\n\t\tif (StringUtils.isBlank(threadPoolName)) {\n\t\t\tlogger.warn(\"threadPoolName is null\");\n\t\t\treturn;\n\t\t}\n\t\tTHREAD_POOL_REJECT_MAP.getAndIncrement(threadPoolName);\n\t\tthrow new RejectedExecutionException(\"Task \" + r.toString() +\n                \" rejected from \" +\n                executor.toString());\n\t}\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/async/KeyCallable.java",
    "content": "package com.sohu.cache.async;\r\n\r\nimport java.util.concurrent.Callable;\r\n\r\n/**\r\n * Created by yijunzhang on 14-6-18.\r\n */\r\npublic abstract class KeyCallable<V> implements Callable<V> {\r\n    private final String key;\r\n\r\n    private volatile boolean cancelled = false;\r\n\r\n    public KeyCallable(String key) {\r\n        this.key = key;\r\n    }\r\n\r\n    public abstract V execute();\r\n\r\n    @Override\r\n    public V call() throws Exception {\r\n        if (!cancelled) {\r\n            V v =  execute();\r\n            return v;\r\n        }\r\n        return null;\r\n    }\r\n\r\n    public void cancel() {\r\n        this.cancelled = true;\r\n    }\r\n\r\n    public String getKey() {\r\n        return key;\r\n    }\r\n\r\n    public boolean isCancelled() {\r\n        return cancelled;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/async/KeyFuture.java",
    "content": "package com.sohu.cache.async;\r\n\r\nimport java.util.concurrent.Future;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic class KeyFuture<V> {\r\n    private final String key;\r\n    private final Future<V> future;\r\n\r\n    public KeyFuture(String key, Future<V> future) {\r\n        this.key = key;\r\n        this.future = future;\r\n    }\r\n\r\n    public String getKey() {\r\n        return key;\r\n    }\r\n\r\n    public Future<V> getFuture() {\r\n        return future;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/async/NamedThreadFactory.java",
    "content": "package com.sohu.cache.async;\r\n\r\nimport java.util.concurrent.ThreadFactory;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\n\r\n/**\r\n * Created by yijunzhang on 14-6-18.\r\n */\r\npublic class NamedThreadFactory implements ThreadFactory {\r\n    private static final AtomicInteger POOL_SEQ = new AtomicInteger(1);\r\n\r\n    private final AtomicInteger mThreadNum = new AtomicInteger(1);\r\n\r\n    private final String mPrefix;\r\n\r\n    private final boolean mDaemo;\r\n\r\n    private final ThreadGroup mGroup;\r\n\r\n    public NamedThreadFactory() {\r\n        this(\"pool-\" + POOL_SEQ.getAndIncrement(), false);\r\n    }\r\n\r\n    public NamedThreadFactory(String prefix) {\r\n        this(prefix, false);\r\n    }\r\n\r\n    public NamedThreadFactory(String prefix, boolean daemo) {\r\n        mPrefix = prefix + \"-thread-\";\r\n        mDaemo = daemo;\r\n        SecurityManager s = System.getSecurityManager();\r\n        mGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();\r\n    }\r\n\r\n    public Thread newThread(Runnable runnable) {\r\n        String name = mPrefix + mThreadNum.getAndIncrement();\r\n        Thread ret = new Thread(mGroup, runnable, name, 0);\r\n        ret.setDaemon(mDaemo);\r\n        return ret;\r\n    }\r\n\r\n    public ThreadGroup getThreadGroup() {\r\n        return mGroup;\r\n    }\r\n\r\n    public String getThreadPoolName() {\r\n        return mPrefix;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/async/impl/AsyncServiceImpl.java",
    "content": "package com.sohu.cache.async.impl;\n\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.PreDestroy;\nimport java.util.concurrent.*;\n\n/**\n * Created by yijunzhang on 14-6-18.\n */\n@Service\npublic class AsyncServiceImpl implements AsyncService {\n    private final static String DEFAULT_THREAD_POOL = \"default_thread_pool\";\n    public final ConcurrentMap<String, ExecutorService> threadPoolMap = new ConcurrentSkipListMap<String, ExecutorService>();\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    private final ExecutorService defaultThreadPool = AsyncThreadPoolFactory.DEFAULT_ASYNC_THREAD_POOL;\n\n    public AsyncServiceImpl() {\n        threadPoolMap.put(DEFAULT_THREAD_POOL, defaultThreadPool);\n    }\n\n    @Override\n    public boolean submitFuture(KeyCallable<?> callable) {\n        return submitFuture(DEFAULT_THREAD_POOL, callable);\n    }\n\n    @Override\n    public Future<?> submitFutureWithRst(KeyCallable<?> callable) {\n        return submitFuture((Callable)callable);\n    }\n\n    @Override\n    public boolean submitFuture(String threadPoolKey, KeyCallable<?> callable) {\n        try {\n            ExecutorService executorService = threadPoolMap.get(threadPoolKey);\n            if (executorService == null) {\n                logger.warn(\"threadPoolKey={} not found , used defaultThreadPool\", threadPoolKey);\n                executorService = defaultThreadPool;\n            }\n            Future future = executorService.submit(callable);\n            return true;\n        } catch (Exception e) {\n            logger.error(callable.getKey(), e);\n            return false;\n        }\n    }\n\n    @Override\n    public Future<?> submitFutureWithRst(String threadPoolKey, KeyCallable<?> callable) {\n        try {\n            ExecutorService executorService = threadPoolMap.get(threadPoolKey);\n            if (executorService == null) {\n                logger.warn(\"threadPoolKey={} not found , used defaultThreadPool\", threadPoolKey);\n                executorService = defaultThreadPool;\n            }\n            Future<?> future = executorService.submit(callable);\n            return future;\n        } catch (Exception e) {\n            logger.error(callable.getKey(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public Future<?> submitFuture(Callable<?> callable) {\n        try {\n            Future<?> future = defaultThreadPool.submit(callable);\n            return future;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public void assemblePool(String threadPoolKey, ThreadPoolExecutor threadPool) {\n        ExecutorService executorService = threadPoolMap.putIfAbsent(threadPoolKey, threadPool);\n        if (executorService != null) {\n            logger.warn(\"{} is assembled\", threadPoolKey);\n        }\n    }\n\n    @PreDestroy\n    public void destory() {\n        for (ExecutorService executorService : threadPoolMap.values()) {\n            if (!executorService.isShutdown()) {\n                executorService.shutdown();\n            }\n        }\n        threadPoolMap.clear();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/AppClientReportConstant.java",
    "content": "package com.sohu.cache.client;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * Created by rucao on 2019/12/18\n */\npublic class AppClientReportConstant {\n    public final static List<String> ClientReportCommandBlacklist = Arrays.asList(\"info\", \"ping\");\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/AppClientReportModel.java",
    "content": "package com.sohu.cache.client;\n\nimport lombok.Data;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/16\n */\n@Data\npublic class AppClientReportModel {\n    /**\n     * appId\n     */\n    private long appId;\n    /**\n     * 客户端版本\n     */\n    private String clientVersion;\n    /**\n     * 客户端ip\n     */\n    private String clientIp;\n    /**\n     * 客户端连接池配置信息\n     */\n    private Map<String, Object> config;\n    /**\n     * 上报数据时间\n     */\n    private long currentMin;\n    /**\n     * 统计耗时\n     */\n    private long cost;\n    /**\n     * 上报异常数据\n     */\n    private List<Map<String, Object>> exceptionModels;\n\n    /**\n     * 上报命令数据\n     */\n    private List<Map<String, Object>> commandStatsModels;\n\n    /**\n     * 其他信息\n     */\n    private Map<String, Object> otherInfo;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/command/AppClientCommand.java",
    "content": "package com.sohu.cache.client.command;\n\nimport com.google.common.collect.Maps;\nimport com.netflix.hystrix.*;\nimport com.sohu.cache.client.service.ClientVersionService;\nimport com.sohu.cache.constant.ClientStatusEnum;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.ObjectConvert;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * Created by zhangyijun on 2017/8/7.\n */\npublic class AppClientCommand extends HystrixCommand<Map<String, Object>> {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    public final static int DEFAULT_TIMEOUT = 4000;\n\n    public final static int DEFAULT_POOL_SIZE = 30;\n\n    private static final String commandKey = \"cacheClient\";\n\n    private static final String groupKey = \"appClientService\";\n\n    private static final String poolKey = \"appClientPool\";\n\n    private final AppClientParams appClientParams;\n\n    private final InstanceDao instanceDao;\n\n    private final AppDao appDao;\n\n    private final ClientVersionService clientVersionService;\n\n    private final ConcurrentMap<Long, Map<String, Object>> appClientMap;\n\n    public AppClientCommand(AppClientParams appClientParams, AppDao appDao, InstanceDao instanceDao,\n                            ClientVersionService clientVersionService, ConcurrentMap<Long, Map<String, Object>> appClientMap) {\n        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))\n                .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))\n                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(poolKey))\n                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()\n                        .withFallbackIsolationSemaphoreMaxConcurrentRequests(100)\n                        .withExecutionTimeoutInMilliseconds(DEFAULT_TIMEOUT))\n                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(DEFAULT_POOL_SIZE)));\n        this.appClientParams = appClientParams;\n        this.instanceDao = instanceDao;\n        this.appDao = appDao;\n        this.clientVersionService = clientVersionService;\n        this.appClientMap = appClientMap;\n    }\n\n    @Override\n    protected Map<String, Object> run() throws Exception {\n        Map<String, Object> model = Maps.newHashMap();\n        Integer type = appClientParams.getType();\n        long appId = appClientParams.getAppId();\n        boolean isCheck = checkRedisApp(model);\n        if (!isCheck) {\n            return model;\n        }\n        if(type == null){\n            type = appClientParams.getCacheAppDesc().getType();\n        }\n        if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n            model.putAll(getRedisClusterInfo(true));\n        } else if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            model.putAll(getRedisSentinelInfo(true));\n        } else if (type == ConstUtils.CACHE_REDIS_STANDALONE) {\n            model.putAll(getRedisStandaloneInfo(true));\n        }\n        model.put(\"type\", type);\n        //每次数据库操作成功，更新缓存\n        addAppClient(appId, model);\n\n        return model;\n    }\n\n    public void addAppClient(long appId, Map<String, Object> model) {\n        int status = MapUtils.getIntValue(model, \"status\", ClientStatusEnum.ERROR.getStatus());\n        if (status != ClientStatusEnum.ERROR.getStatus()) {\n            appClientMap.put(appId, model);\n        }\n    }\n\n    @Override\n    protected Map<String, Object> getFallback() {\n        //抛出异常\n        if (this.isFailedExecution()) {\n            Throwable throwable = this.getFailedExecutionException();\n            logger.error(throwable.getMessage(), throwable);\n        }\n        //判断是否为调用超时\n        if (this.isResponseTimedOut()) {\n            long time = this.getExecutionTimeInMilliseconds();\n            logger.warn(\"commandKey={} groupKey={} poolKey={} timeout cost={} ms\", commandKey, groupKey, poolKey, time);\n        }\n        Map<String, Object> model = appClientMap.get(appClientParams.getAppId());\n\n        int status = MapUtils.getIntValue(model, \"status\", ClientStatusEnum.ERROR.getStatus());\n        if (status == ClientStatusEnum.ERROR.getStatus()) {\n            logger.error(\"app-fallback-error: appId={} ,clientIp={},stat={}\", appClientParams.getAppId(), appClientParams.getAppClientIp(), model);\n        }\n        return model;\n    }\n\n    private boolean checkRedisApp(Map<String, Object> model) {\n        long appId = appClientParams.getAppId();\n        Integer type = appClientParams.getType();\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (type == null && appDesc != null) {\n            if (appDesc.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                type = appDesc.getType();\n            }\n        }\n        if (appDesc == null) {\n            model.put(\"status\", ClientStatusEnum.ERROR.getStatus());\n            model.put(\"message\", String.format(\"appId:%s 不存在\", appId));\n            return false;\n        } else if (type == null || appDesc.getType() != type) {\n            model.put(\"status\", ClientStatusEnum.ERROR.getStatus());\n            model.put(\"message\",\n                    String.format(\"appId:%s 类型不符,期望类型:%s,实际类型%s,请联系管理员!\", appId, type, appDesc.getType()));\n            return false;\n        }\n        appClientParams.setCacheAppDesc(appDesc);\n        return true;\n    }\n\n    public Map<String, Object> getRedisStandaloneInfo(boolean clientRequest) {\n        long appId = appClientParams.getAppId();\n\n        Map<String, Object> model = Maps.newHashMap();\n        boolean isPass = beforeProcess(clientRequest, model);\n        if (!isPass) {\n            return model;\n        }\n        List<InstanceInfo> instanceList = appClientParams.getCacheInstanceInfos();\n        if (CollectionUtils.isEmpty(instanceList)) {\n            instanceList = instanceDao.getInstListByAppId(appId);\n        }\n        String shardsInfo = ObjectConvert.assembleInstance(instanceList);\n        if (StringUtils.isBlank(shardsInfo)) {\n            model.put(\"status\", ClientStatusEnum.ERROR.getStatus());\n            model.put(\"message\", \"ERROR: appId:\" + appId + \"shardsInfo为空 \");\n            return model;\n        }\n        String standalone = null;\n        for (InstanceInfo instanceInfo : instanceList) {\n            if (instanceInfo.isOffline()) {\n                continue;\n            }\n            standalone = instanceInfo.getIp() + \":\" + instanceInfo.getPort();\n        }\n        model.put(\"standalone\", standalone);\n        model.put(\"status\", ClientStatusEnum.GOOD.getStatus());\n\n        return model;\n    }\n\n    public Map<String, Object> getRedisSentinelInfo(boolean clientRequest) {\n        Map<String, Object> model = Maps.newHashMap();\n        boolean isPass = beforeProcess(clientRequest, model);\n        if (!isPass) {\n            return model;\n        }\n\n        long appId = appClientParams.getAppId();\n        List<InstanceInfo> instanceList = appClientParams.getCacheInstanceInfos();\n        if (CollectionUtils.isEmpty(instanceList)) {\n            instanceList = instanceDao.getInstListByAppId(appId);\n        }\n        if (instanceList == null || instanceList.isEmpty()) {\n            model.put(\"status\", ClientStatusEnum.ERROR.getStatus());\n            model.put(\"message\", \"appId: \" + appId + \" 实例集合为空 \");\n            return model;\n        }\n        String shardsInfo = ObjectConvert.assembleInstance(instanceList);\n        if (StringUtils.isBlank(shardsInfo)) {\n            model.put(\"status\", ClientStatusEnum.ERROR.getStatus());\n            model.put(\"message\", \"ERROR: appId:\" + appId + \"shardsInfo为空 \");\n            return model;\n        }\n        String masterName = null;\n        List<String> sentinelList = new ArrayList<String>();\n        for (InstanceInfo instance : instanceList) {\n            if (instance.isOffline()) {\n                continue;\n            }\n            if (instance.getType() == ConstUtils.CACHE_REDIS_SENTINEL\n                    && masterName == null\n                    && StringUtils.isNotBlank(instance.getCmd())) {\n                masterName = instance.getCmd();\n            }\n            if (instance.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {\n                sentinelList.add(instance.getIp() + \":\" + instance.getPort());\n            }\n        }\n        String sentinels = StringUtils.join(sentinelList, \" \");\n        model.put(\"sentinels\", sentinels);\n        model.put(\"masterName\", masterName);\n        model.put(\"appId\", appId);\n        model.put(\"status\", ClientStatusEnum.GOOD.getStatus());\n\n        return model;\n    }\n\n    public Map<String, Object> getRedisClusterInfo(boolean clientRequest) {\n        long appId = appClientParams.getAppId();\n\n        Map<String, Object> model = Maps.newHashMap();\n        boolean isPass = beforeProcess(clientRequest, model);\n        if (!isPass) {\n            return model;\n        }\n\n        List<InstanceInfo> instanceList = appClientParams.getCacheInstanceInfos();\n        if (CollectionUtils.isEmpty(instanceList)) {\n            instanceList = instanceDao.getInstListByAppId(appId);\n        }\n        if (instanceList == null || instanceList.isEmpty()) {\n            model.put(\"status\", ClientStatusEnum.ERROR.getStatus());\n            model.put(\"message\", \"ERROR: appId:\" + appId + \"实例集合为空 \");\n            return model;\n        }\n        String shardsInfo = ObjectConvert.assembleInstance(instanceList);\n        if (StringUtils.isBlank(shardsInfo)) {\n            model.put(\"status\", ClientStatusEnum.ERROR.getStatus());\n            model.put(\"message\", \"ERROR: appId:\" + appId + \"shardsInfo为空 \");\n            return model;\n        }\n        int shardNum = shardsInfo.split(\" \").length;\n        model.put(\"appId\", appId);\n        model.put(\"shardNum\", shardNum);\n        model.put(\"shardInfo\", shardsInfo);\n        model.put(\"status\", ClientStatusEnum.GOOD.getStatus());\n\n        return model;\n    }\n\n    private boolean beforeProcess(boolean clientRequest, Map<String, Object> model) {\n        long appId = appClientParams.getAppId();\n        String clientVersion = appClientParams.getClientVersion();\n        if (StringUtils.isBlank(clientVersion)) {\n            clientVersion = appClientParams.getCacheMaxVersion();\n            if (StringUtils.isBlank(clientVersion)) {\n                clientVersion = clientVersionService.getAppMaxClientVersion(appClientParams.getAppId());\n            }\n            if (StringUtils.isBlank(clientVersion)) {\n                clientVersion = \"1.7-SNAPSHOT\";\n            }\n        }\n        addPkey(clientVersion, model);\n        if (clientRequest) {\n            //保存版本信息\n            try {\n                String appClientIp = appClientParams.getAppClientIp();\n                clientVersionService.saveOrUpdateClientVersion(appId, appClientIp, clientVersion);\n            } catch (Exception e) {\n                logger.error(\"redis heart error:\" + e.getMessage(), e);\n            }\n        }\n        return true;\n    }\n\n    // 处理pkey\n    private void addPkey(String clientVersion, Map<String, Object> model) {\n        Double partVersion = NumberUtils.toDouble(clientVersion.substring(0, 3), 1.7);\n        if (partVersion >= 1.7) {\n            AppDesc appDesc = appClientParams.getCacheAppDesc();\n            if (appDesc == null) {\n                appDesc = appDao.getAppDescById(appClientParams.getAppId());\n            }\n            String pkey = appDesc.getPkey();\n            if (StringUtils.isNotBlank(pkey)) {\n                model.put(\"pkey\", pkey);\n            } else {\n                model.put(\"pkey\", \"\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/command/AppClientParams.java",
    "content": "package com.sohu.cache.client.command;\n\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\n\nimport java.util.List;\n\n/**\n * Created by zhangyijun on 2017/8/7.\n */\npublic class AppClientParams {\n\n    private final long appId;\n\n    // 调用级别cache,加速初始化.\n    private AppDesc cacheAppDesc;\n    private List<InstanceInfo> cacheInstanceInfos;\n    private String cacheMaxVersion;\n\n    private final Integer type;\n\n    private final String appClientIp;\n\n    private final String clientVersion;\n\n    public AppClientParams(long appId, Integer type, String appClientIp, String clientVersion) {\n        this.appId = appId;\n        this.type = type;\n        this.appClientIp = appClientIp;\n        this.clientVersion = clientVersion;\n    }\n\n    public long getAppId() {\n        return appId;\n    }\n\n    public Integer getType() {\n        return type;\n    }\n\n    public String getAppClientIp() {\n        return appClientIp;\n    }\n\n    public String getClientVersion() {\n        return clientVersion;\n    }\n\n    public AppDesc getCacheAppDesc() {\n        return cacheAppDesc;\n    }\n\n    public void setCacheAppDesc(AppDesc cacheAppDesc) {\n        this.cacheAppDesc = cacheAppDesc;\n    }\n\n    public List<InstanceInfo> getCacheInstanceInfos() {\n        return cacheInstanceInfos;\n    }\n\n    public void setCacheInstanceInfos(List<InstanceInfo> cacheInstanceInfos) {\n        this.cacheInstanceInfos = cacheInstanceInfos;\n    }\n\n    public String getCacheMaxVersion() {\n        return cacheMaxVersion;\n    }\n\n    public void setCacheMaxVersion(String cacheMaxVersion) {\n        this.cacheMaxVersion = cacheMaxVersion;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/heartbeat/RedisClientController.java",
    "content": "package com.sohu.cache.client.heartbeat;\n\nimport com.sohu.cache.client.command.AppClientParams;\nimport com.sohu.cache.client.service.AppClientService;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.util.IpUtil;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Map;\n\n/**\n * Created by zhangyijun on 2017/8/7.\n */\n@RestController\n@RequestMapping(value = \"/cache/client\")\npublic class RedisClientController {\n\n    @Autowired\n    private AppClientService appClientService;\n\n    /**\n     * 通过appId返回RedisCluster实例信息\n     *\n     * @param appId\n     */\n    @RequestMapping(value = \"/redis/cluster/{appId}.json\", method = RequestMethod.GET)\n    public Map<String, Object> getClusterByAppIdAndKey(HttpServletRequest request, @PathVariable long appId) {\n        AppClientParams clientParams = wrapClientParams(request, appId, ConstUtils.CACHE_TYPE_REDIS_CLUSTER);\n        return appClientService.getAppClientInfo(clientParams);\n    }\n\n    /**\n     * 通过appId返回RedisSentinel实例信息\n     *\n     * @param appId\n     */\n    @RequestMapping(value = \"/redis/sentinel/{appId}.json\")\n    public Map<String, Object> getSentinelAppById(HttpServletRequest request, @PathVariable long appId, Model model) {\n        AppClientParams clientParams = wrapClientParams(request, appId, ConstUtils.CACHE_REDIS_SENTINEL);\n        return appClientService.getAppClientInfo(clientParams);\n    }\n\n    /**\n     * 通过appId返回RedisStandalone实例信息\n     *\n     * @param appId\n     */\n    @RequestMapping(value = \"/redis/standalone/{appId}.json\")\n    public Map<String, Object> getStandaloneAppById(HttpServletRequest request, @PathVariable long appId, Model model) {\n        AppClientParams clientParams = wrapClientParams(request, appId, ConstUtils.CACHE_REDIS_STANDALONE);\n        return appClientService.getAppClientInfo(clientParams);\n    }\n\n    private AppClientParams wrapClientParams(HttpServletRequest request, long appId, int type) {\n        String appClientIp = IpUtil.getIpAddr(request);\n        String clientVersion = request.getParameter(\"clientVersion\");\n        return new AppClientParams(appId, type, appClientIp, clientVersion);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/heartbeat/RedisClientOldController.java",
    "content": "package com.sohu.cache.client.heartbeat;\r\n\r\nimport com.google.common.collect.Lists;\r\nimport com.sohu.cache.client.service.ClientVersionService;\r\nimport com.sohu.cache.constant.ClientStatusEnum;\r\nimport com.sohu.cache.dao.AppDao;\r\nimport com.sohu.cache.dao.InstanceDao;\r\nimport com.sohu.cache.entity.AppDesc;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.ObjectConvert;\r\nimport com.sohu.cache.web.util.IpUtil;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.PathVariable;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\n/**\r\n * redis 客户端连接类\r\n */\r\n@Controller\r\n@RequestMapping(value = \"/cache/client\")\r\npublic class RedisClientOldController {\r\n    private final Logger logger = LoggerFactory.getLogger(RedisClientOldController.class);\r\n\r\n    @Resource\r\n    private AppDao appDao;\r\n\r\n    @Resource\r\n    private InstanceDao instanceDao;\r\n\r\n    @Resource(name = \"clientVersionService\")\r\n    private ClientVersionService clientVersionService;\r\n\r\n    /**\r\n     * 通过appId返回RedisCluster实例信息\r\n     *\r\n     * @param appId\r\n     */\r\n    @RequestMapping(value = \"/redis/cluster/old/{appId}.json\")\r\n    public void getClusterByAppIdAndKey(HttpServletRequest request, @PathVariable long appId, Model model) {\r\n        if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_TYPE_REDIS_CLUSTER, false)) {\r\n            return;\r\n        }\r\n        getRedisClusterInfo(request, appId, model);\r\n    }\r\n\r\n    private void getRedisClusterInfo(HttpServletRequest request, long appId, Model model) {\r\n        String clientVersion = request.getParameter(\"clientVersion\");\r\n\r\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\r\n        if (instanceList == null || instanceList.isEmpty()) {\r\n            model.addAttribute(\"status\", ClientStatusEnum.ERROR.getStatus());\r\n            model.addAttribute(\"message\", \"ERROR: appId:\" + appId + \"实例集合为空 \");\r\n            return;\r\n        }\r\n        String shardsInfo = ObjectConvert.assembleInstance(instanceList);\r\n        if (StringUtils.isBlank(shardsInfo)) {\r\n            model.addAttribute(\"status\", ClientStatusEnum.ERROR.getStatus());\r\n            model.addAttribute(\"message\", \"ERROR: appId:\" + appId + \"shardsInfo为空 \");\r\n            return;\r\n        }\r\n        int shardNum = shardsInfo.split(\" \").length;\r\n        model.addAttribute(\"appId\", appId);\r\n        model.addAttribute(\"shardNum\", shardNum);\r\n        model.addAttribute(\"shardInfo\", shardsInfo);\r\n\r\n        addPkey(clientVersion, appId, model);\r\n\r\n        //保存版本信息\r\n        try {\r\n            clientVersionService.saveOrUpdateClientVersion(appId, IpUtil.getIpAddr(request), clientVersion);\r\n        } catch (Exception e) {\r\n            logger.error(\"redisCluster heart error:\" + e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    private void addPkey(String clientVersion, long appId, Model model) {\r\n        if (clientVersion.startsWith(\"1.7\")) {\r\n            AppDesc appDesc = appDao.getAppDescById(appId);\r\n            String pkey = appDesc.getPkey();\r\n            if (StringUtils.isNotBlank(pkey)) {\r\n                model.addAttribute(\"pkey\", pkey);\r\n            } else {\r\n                model.addAttribute(\"pkey\", \"\");\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 通过appId返回RedisSentinel实例信息\r\n     *\r\n     * @param appId\r\n     */\r\n    @RequestMapping(value = \"/redis/sentinel/old/{appId}.json\")\r\n    public void getSentinelAppById(HttpServletRequest request, @PathVariable long appId, Model model) {\r\n        if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_REDIS_SENTINEL, false)) {\r\n            return;\r\n        }\r\n        getRedisSentinelInfo(request, appId, model);\r\n    }\r\n\r\n    private void getRedisSentinelInfo(HttpServletRequest request, long appId, Model model) {\r\n        String clientVersion = request.getParameter(\"clientVersion\");\r\n\r\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\r\n        if (instanceList == null || instanceList.isEmpty()) {\r\n            model.addAttribute(\"status\", ClientStatusEnum.ERROR.getStatus());\r\n            model.addAttribute(\"message\", \"appId: \" + appId + \" 实例集合为空 \");\r\n            return;\r\n        }\r\n        String masterName = null;\r\n        List<String> sentinelList = new ArrayList<String>();\r\n        for (InstanceInfo instance : instanceList) {\r\n            if (instance.isOffline()) {\r\n                continue;\r\n            }\r\n            if (instance.getType() == ConstUtils.CACHE_REDIS_SENTINEL\r\n                    && masterName == null\r\n                    && StringUtils.isNotBlank(instance.getCmd())) {\r\n                masterName = instance.getCmd();\r\n            }\r\n            if (instance.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {\r\n                sentinelList.add(instance.getIp() + \":\" + instance.getPort());\r\n            }\r\n        }\r\n        String sentinels = StringUtils.join(sentinelList, \" \");\r\n        model.addAttribute(\"sentinels\", sentinels);\r\n        model.addAttribute(\"masterName\", masterName);\r\n        model.addAttribute(\"appId\", appId);\r\n        model.addAttribute(\"status\", ClientStatusEnum.GOOD.getStatus());\r\n\r\n        addPkey(clientVersion, appId, model);\r\n\r\n        //保存版本信息\r\n        try {\r\n            clientVersionService.saveOrUpdateClientVersion(appId, IpUtil.getIpAddr(request), clientVersion);\r\n        } catch (Exception e) {\r\n            logger.error(\"redisSentinel heart error:\" + e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 通过appId返回RedisStandalone实例信息\r\n     *\r\n     * @param appId\r\n     */\r\n    @RequestMapping(value = \"/redis/standalone/old/{appId}.json\")\r\n    public void getStandaloneAppById(HttpServletRequest request, @PathVariable long appId, Model model) {\r\n        if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_REDIS_STANDALONE, false)) {\r\n            return;\r\n        }\r\n        getRedisStandaloneInfo(request, appId, model);\r\n    }\r\n\r\n\r\n    private void getRedisStandaloneInfo(HttpServletRequest request, long appId, Model model) {\r\n        String clientVersion = request.getParameter(\"clientVersion\");\r\n\r\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\r\n        String standalone = null;\r\n        for (InstanceInfo instanceInfo : instanceList) {\r\n            if (instanceInfo.isOffline()) {\r\n                continue;\r\n            }\r\n            standalone = instanceInfo.getIp() + \":\" + instanceInfo.getPort();\r\n        }\r\n        model.addAttribute(\"standalone\", standalone);\r\n        model.addAttribute(\"status\", ClientStatusEnum.GOOD.getStatus());\r\n\r\n        addPkey(clientVersion, appId, model);\r\n\r\n        //保存版本信息\r\n        try {\r\n            clientVersionService.saveOrUpdateClientVersion(appId, IpUtil.getIpAddr(request), clientVersion);\r\n        } catch (Exception e) {\r\n            logger.error(\"redisStandalone heart error:\" + e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 检查客户端相关参数\r\n     *\r\n     * @param appId         应用id\r\n     * @param request\r\n     * @param model\r\n     * @param type          应用类型\r\n     * @param isCheckAppKey 是否检测appKey\r\n     * @return\r\n     */\r\n    private boolean handleRedisApp(long appId, HttpServletRequest request, Model model, int type,\r\n            boolean isCheckAppKey) {\r\n        AppDesc appDesc = appDao.getAppDescById(appId);\r\n\r\n        if (appDesc == null) {\r\n            model.addAttribute(\"status\", ClientStatusEnum.ERROR.getStatus());\r\n            model.addAttribute(\"message\", String.format(\"appId:%s 不存在\", appId));\r\n            return false;\r\n        } else if (appDesc.getType() != type) {\r\n            model.addAttribute(\"status\", ClientStatusEnum.ERROR.getStatus());\r\n            model.addAttribute(\"message\",\r\n                    String.format(\"appId:%s 类型不符,期望类型:%s,实际类型%s,请联系管理员!\", appId, type, appDesc.getType()));\r\n            return false;\r\n        } else if (isCheckAppKey) {\r\n            String appKey = request.getParameter(\"appKey\");\r\n            if (StringUtils.isBlank(appKey)) {\r\n                model.addAttribute(\"status\", ClientStatusEnum.ERROR.getStatus());\r\n                model.addAttribute(\"message\", String.format(\"appId=%s,appKey参数为空\", appId));\r\n                return false;\r\n            }\r\n            if (!appKey.equals(appDesc.getAppKey())) {\r\n                model.addAttribute(\"status\", ClientStatusEnum.ERROR.getStatus());\r\n                model.addAttribute(\"message\", String.format(\"appId=%s,appKey:%s错误,与服务端不匹配\", appId, appKey));\r\n                return false;\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/heartbeat/RedisClientReportDataController.java",
    "content": "package com.sohu.cache.client.heartbeat;\n\nimport com.google.common.collect.Lists;\nimport com.sohu.cache.client.AppClientReportModel;\nimport com.sohu.cache.client.service.DealClientReportService;\nimport com.sohu.cache.constant.ClientStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.JsonUtil;\nimport com.sohu.cache.util.StringUtil;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\n/**\n * cachecloud客户端上报数据接口\n *\n * @author leifu\n * @Date 2015年1月16日\n * @Time 下午2:10:25\n */\n@Controller\n@RequestMapping(value = \"/cachecloud/client\")\npublic class RedisClientReportDataController {\n    private final Logger logger = LoggerFactory.getLogger(RedisClientReportDataController.class);\n\n    @Autowired\n    private DealClientReportService dealClientReportService;\n\n    /**\n     * 上报客户端上传数据\n     *\n     * @param\n     * @param model\n     */\n    @RequestMapping(value = \"/reportData.json\", method = RequestMethod.POST)\n    public void reportData(HttpServletRequest request, HttpServletResponse response, Model model) {\n        return;\n    }\n\n    @RequestMapping(value = \"/v1/reportData/exception\", method = RequestMethod.POST)\n    public ResponseEntity<String> reportExceptionData(@RequestParam(\"clientVersion\") String clientVersion,\n                                                      @RequestParam(\"stats\") String json) {\n        return dealAppClientReportData(clientVersion, json);\n    }\n\n    @RequestMapping(value = \"/v1/reportData/command\", method = RequestMethod.POST)\n    public ResponseEntity<String> reportCommandData(@RequestParam(\"clientVersion\") String clientVersion,\n                                                    @RequestParam(\"stats\") String json) {\n        return dealAppClientReportData(clientVersion, json);\n    }\n\n\n    /**\n     * 统一统一上报数据\n     *\n     * @param clientVersion\n     * @param json\n     * @return\n     */\n    private ResponseEntity<String> dealAppClientReportData(String clientVersion, String json) {\n        HttpStatus status = HttpStatus.CREATED;\n        // 验证json的正确性\n        AppClientReportModel appClientReportModel = checkAppClientReportJson(json);\n        if (appClientReportModel == null) {\n            logger.error(\"reportWrong message: {}\", json);\n            status = HttpStatus.BAD_REQUEST;\n            return ResponseEntity.status(status).body(\"reportWrong message\");\n        } else if (clientIpFilter(appClientReportModel.getClientIp())) {\n            logger.debug(\"discard report data, clientIp:{}\", appClientReportModel.getClientIp());\n            return ResponseEntity.status(status).body(\"success\");\n        }\n        // 处理数据\n        boolean result = dealClientReportService.deal(appClientReportModel);\n        if (!result) {\n            logger.error(\"appClientReportCommandService deal fail, appClientReportModel is {}\", appClientReportModel);\n            status = HttpStatus.INTERNAL_SERVER_ERROR;\n            return ResponseEntity.status(status).body(\"message deal fail\");\n        }\n        return ResponseEntity.status(status).body(\"success\");\n    }\n\n    private boolean clientIpFilter(String clientIp) {\n        if (StringUtil.isBlank(clientIp)) {\n            return true;\n        }\n        //todo 可自行实现客户端ip过滤逻辑\n        return false;\n    }\n\n\n    /**\n     * 检验json正确性，返回AppClientReportModel\n     *\n     * @param json\n     * @return\n     */\n    private AppClientReportModel checkAppClientReportJson(String json) {\n        if (StringUtils.isNotBlank(json)) {\n            try {\n                return JsonUtil.fromJson(json, AppClientReportModel.class);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/AppClientReportCommandService.java",
    "content": "package com.sohu.cache.client.service;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/16\n */\npublic interface AppClientReportCommandService {\n\n    /**\n     * @param clientIp\n     * @param currentMin\n     * @param commandStatsModels\n     */\n    void batchSave(long appId, String clientIp, long currentMin, List<Map<String, Object>> commandStatsModels);\n\n    /**\n     * 获取一段时间内某个应用执行的命令列表\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<String> getAppDistinctCommand(Long appId, long startTime, long endTime);\n\n    /**\n     * 获取某个应用一段时间内某个命令的单个客户端统计信息, key-clientIp\n     *\n     * @param appId\n     * @param command\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    Map<String, List<Map<String, Object>>> getAppCommandClientStatistics(Long appId, String command, long startTime, long endTime, String clientIp);\n\n    /**\n     * 锁定命令，查看某个命令被哪些client调用, key-command\n     *\n     * @param appId\n     * @param command\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<Map<String, Object>> getAppClientStatisticsByCommand(Long appId, String command, long startTime, long endTime);\n\n    /**\n     * 取一段时间内某个应用的客户端ip列表\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<String> getAppDistinctClients(Long appId, long startTime, long endTime);\n\n    /**\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @param command\n     * @return\n     */\n    List<Map<String, Object>> getSumCmdStatByCmd(Long appId, long startTime, long endTime, String command);\n\n    List<Map<String, Object>> getSumCmdStatByClient(Long appId, long startTime, long endTime, String clientIp);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/AppClientReportExceptionService.java",
    "content": "package com.sohu.cache.client.service;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Created by rucao on 2019/12/13\n */\npublic interface AppClientReportExceptionService {\n\n    /**\n     * 处理上报数据\n     *\n     * @param exceptionModels\n     * @return\n     */\n    void batchSave(long appId, String clientIp, String redisPoolConfig, long currentMin, List<Map<String, Object>> exceptionModels);\n\n    /**\n     * 获取某个应用一段时间内的异常信息列表\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @param type\n     * @return\n     */\n    Map<String, List<Map<String, Object>>> getAppExceptionStatisticsMap(Long appId, String clientIp, long startTime, long endTime, Integer type);\n\n    /**\n     * client_ip, node, sum(count) sum_count, sum(cost) sum_cost\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<Map<String, Object>> getDistinctClientNodeStatistics(Long appId, String clientIp, long startTime, long endTime, Integer type);\n\n    /**\n     * client-redisPoolConfig 列表\n     *\n     * @param appId\n     * @param type\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    Map<String, String> getAppDistinctClientConfig(Long appId, Integer type, long startTime, long endTime);\n\n    Map<String, List<String>> getAppClientConfigs(Long appId, Integer type, long startTime, long endTime);\n\n    /**\n     * @param clientIp\n     * @param node\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<Map<String, Object>> getLatencyCommandDetailByNode(String clientIp, String node, long startTime, long endTime);\n\n    List<Map<String, Object>> getLatencyCommandDetailByNodeV2(String node, long searchTime);\n\n\n    /**\n     * @param appId\n     * @param searchTime\n     * @return\n     */\n    Map<String, Map<String, Object>> getSumCmdExpStatGroupByNode(long appId, long searchTime);\n\n    /**\n     * @param nodeSet\n     * @param searchTime\n     * @return\n     */\n    Map<String, List<Map<String, Object>>> getLatencyCommandDetails(Set<String> nodeSet, long searchTime);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/AppClientService.java",
    "content": "package com.sohu.cache.client.service;\n\nimport com.sohu.cache.client.command.AppClientParams;\n\nimport java.util.Map;\n\n/**\n * 应用级别客户端查询服务\n * Created by zhangyijun on 2017/8/4.\n */\npublic interface AppClientService {\n\n    /**\n     * 根据appId查询客户端信息\n     *\n     * @param appClientParams\n     * @return\n     */\n    Map<String, Object> getAppClientInfo(AppClientParams appClientParams);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/AppClientStatisticGatherService.java",
    "content": "package com.sohu.cache.client.service;\n\n/**\n * Created by rucao on 2019/12/29\n */\npublic interface AppClientStatisticGatherService {\n    void bathSave(long startTime, long endTime);\n    void bathAdd(long startTime, long endTime);\n    void bathAddServerCmdCount(long startTime, long endTime);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/AppInstanceClientRelationService.java",
    "content": "package com.sohu.cache.client.service;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\nimport com.sohu.cache.entity.AppClientCostTimeStat;\r\nimport com.sohu.cache.entity.AppInstanceClientRelation;\r\n\r\n/**\r\n * 应用下节点和客户端关系服务\r\n * \r\n * @author leifu\r\n * @Date 2016年5月3日\r\n * @Time 下午6:48:40\r\n */\r\npublic interface AppInstanceClientRelationService {\r\n\r\n    void batchSave(List<AppClientCostTimeStat> appClientCostTimeStatList);\r\n\r\n    List<AppInstanceClientRelation> getAppInstanceClientRelationList(Long appId, Date date);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/ClientReportExceptionService.java",
    "content": "package com.sohu.cache.client.service;\r\n\r\nimport com.sohu.cache.entity.AppClientExceptionStat;\r\nimport com.sohu.cache.entity.ClientInstanceException;\r\nimport com.sohu.cache.web.util.Page;\r\n\r\nimport java.util.List;\r\n\r\n\r\n/**\r\n * 客户端上报异常记录\r\n * @author leifu\r\n * @Date 2015年1月19日\r\n * @Time 上午10:02:32\r\n */\r\npublic interface ClientReportExceptionService {\r\n    \r\n    /**\r\n     * 获取客户端异常列表\r\n     * @param appId 应用id\r\n     * @param startTime 开始收集时间\r\n     * @param endTime 结束收集时间\r\n     * @param type 异常类型(ClientExceptionType)\r\n     * @param clientIp 客户端ip\r\n     * @return\r\n     */\r\n    List<AppClientExceptionStat> getAppExceptionList(Long appId, long startTime, long endTime, int type, String clientIp, Page page);\r\n\r\n    /**\r\n     * 获取客户端异常个数\r\n     * @param appId 应用id\r\n     * @param startTime 开始收集时间\r\n     * @param endTime 结束收集时间\r\n     * @param type 异常类型(ClientExceptionType)\r\n     * @param clientIp 客户端ip\r\n     * @return\r\n     */\r\n    int getAppExceptionCount(Long appId, long startTime, long endTime, int type, String clientIp);\r\n\r\n    /**\r\n     * 大于collectTime期间各个实例的异常统计\r\n     * @param ip\r\n     * @param collectTime\r\n     * @return\r\n     */\r\n    List<ClientInstanceException> getInstanceExceptionStat(String ip, long collectTime);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/ClientReportInstanceService.java",
    "content": "package com.sohu.cache.client.service;\r\n\r\nimport com.sohu.cache.entity.InstanceInfo;\r\n\r\n/**\r\n * \r\n * @author leifu\r\n * @Date 2015年1月19日\r\n * @Time 上午10:02:28\r\n */\r\npublic interface ClientReportInstanceService {\r\n    \r\n    /**\r\n     * 根据host:port获取instance信息(缓存，不要求一致性)\r\n     * @param host\r\n     * @param port\r\n     * @return\r\n     */\r\n    InstanceInfo getInstanceInfoByHostPort(String host, int port);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/ClientVersionService.java",
    "content": "package com.sohu.cache.client.service;\r\n\r\nimport com.sohu.cache.entity.AppClientVersion;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 客户端版本信息\r\n *\r\n * @author leifu\r\n * @Date 2015年2月2日\r\n * @Time 上午10:19:59\r\n */\r\npublic interface ClientVersionService {\r\n\r\n    /**\r\n     * 保存客户端版本信息\r\n     *\r\n     * @param appId\r\n     * @param appClientIp\r\n     * @param clientVersion\r\n     */\r\n    void saveOrUpdateClientVersion(long appId, String appClientIp, String clientVersion);\r\n\r\n    /**\r\n     * 获取应用的所有客户端版本信息\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    List<AppClientVersion> getAppAllClientVersion(long appId);\r\n\r\n    /**\r\n     * 获取应用客户端最大版本\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    public String getAppMaxClientVersion(long appId);\r\n\r\n    /**\r\n     * 获取应用所有客户端最大版本\r\n     *\r\n     * @return\r\n     */\r\n    public List<Map<String, Object>> getAllMaxClientVersion();\r\n\r\n    /**\r\n     * 获取所有客户端版本\r\n     *\r\n     * @return\r\n     */\r\n    List<AppClientVersion> getAll(long appId);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/DealClientReportService.java",
    "content": "package com.sohu.cache.client.service;\n\nimport com.sohu.cache.client.AppClientReportModel;\n\n/**\n * Created by rucao on 2019/12/13\n */\npublic interface DealClientReportService {\n    void init();\n\n    /**\n     * 处理上报数据\n     *\n     * @param\n     * @return\n     */\n    boolean deal(AppClientReportModel appClientReportModel);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/AppClientReportCommandServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\n\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.client.service.AppClientReportCommandService;\nimport com.sohu.cache.dao.AppClientCommandStatisticsDao;\nimport com.sohu.cache.entity.AppClientCommandStatistics;\nimport com.sohu.cache.report.ReportDataComponent;\nimport com.sohu.cache.util.MapUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.assertj.core.util.Lists;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * Created by rucao on 2019/12/16\n */\n@Service\n@Slf4j\npublic class AppClientReportCommandServiceImpl implements AppClientReportCommandService {\n    @Autowired\n    private AppClientCommandStatisticsDao appClientCommandStatisticsDao;\n\n    @Autowired\n    private ReportDataComponent reportDataComponent;\n\n    @Override\n    public void batchSave(long appId, String clientIp, long currentMin, List<Map<String, Object>> commandStatsModels) {\n        try {\n            // 1.client上报\n            if (CollectionUtils.isEmpty(commandStatsModels)) {\n                log.warn(\"commandStatsModels is empty:{},{}\", clientIp, currentMin);\n                return;\n            }\n            // 2.解析\n            List<AppClientCommandStatistics> appClientCommandStatisticsList = commandStatsModels.stream()\n                    .filter(appClientCommandStatistics -> generate(appId, clientIp, currentMin, appClientCommandStatistics) != null)\n                    .map(appClientCommandStatistics -> generate(appId, clientIp, currentMin, appClientCommandStatistics))\n                    .collect(Collectors.toList());\n            // 4.批量保存\n            if (CollectionUtils.isNotEmpty(appClientCommandStatisticsList)) {\n                appClientCommandStatisticsDao.batchSave(appClientCommandStatisticsList);\n                //上报数据\n                reportDataComponent.reportCommandData(appClientCommandStatisticsList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public List<String> getAppDistinctCommand(Long appId, long startTime, long endTime) {\n        try {\n            return appClientCommandStatisticsDao.getAppDistinctCommand(appId, startTime, endTime);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public Map<String, List<Map<String, Object>>> getAppCommandClientStatistics(Long appId, String command, long startTime, long endTime, String clientIp) {\n        try {\n            List<Map<String, Object>> appClientCommandStatisticsList = appClientCommandStatisticsDao.getAppCommandStatistics(appId, startTime, endTime, command, clientIp);\n            Map<String, List<Map<String, Object>>> commandStatisticsMap = Maps.newHashMap();\n            appClientCommandStatisticsList.stream().forEach(commandStatistic -> {\n                String client_ip = MapUtils.getString(commandStatistic, \"client_ip\");\n                ArrayList commandStatisticList = (ArrayList) MapUtils.getObject(commandStatisticsMap, client_ip);\n                if (CollectionUtils.isEmpty(commandStatisticList)) {\n                    commandStatisticList = Lists.newArrayList();\n                    commandStatisticsMap.put(client_ip, commandStatisticList);\n                }\n                commandStatisticList.add(commandStatistic);\n            });\n            return commandStatisticsMap;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public List<Map<String, Object>> getAppClientStatisticsByCommand(Long appId, String command, long startTime, long endTime) {\n        try {\n            List<Map<String, Object>> getAppClientStatisticsByCommand = appClientCommandStatisticsDao.getAppCommandStatistics(appId, startTime, endTime, command, null);\n            return getAppClientStatisticsByCommand;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public List<String> getAppDistinctClients(Long appId, long startTime, long endTime) {\n        try {\n            return appClientCommandStatisticsDao.getAppDistinctClients(appId, startTime, endTime);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public List<Map<String, Object>> getSumCmdStatByCmd(Long appId, long startTime, long endTime, String command) {\n        try {\n            return appClientCommandStatisticsDao.getSumCmdStatByCmd(appId, startTime, endTime, command);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public List<Map<String, Object>> getSumCmdStatByClient(Long appId, long startTime, long endTime, String clientIp) {\n        try {\n            return appClientCommandStatisticsDao.getSumCmdStatByClient(appId, startTime, endTime, clientIp);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    private AppClientCommandStatistics generate(long appId, String clientIp, long currentMin, Map<String, Object> commandStatsModel) {\n        try {\n            AppClientCommandStatistics appClientCommandStatistics = (AppClientCommandStatistics) MapUtil.mapToObject(commandStatsModel, AppClientCommandStatistics.class);\n            if (appClientCommandStatistics != null) {\n                appClientCommandStatistics.setAppId(appId);\n                appClientCommandStatistics.setClientIp(clientIp);\n                appClientCommandStatistics.setCurrentMin(currentMin);\n            }\n            return appClientCommandStatistics;\n        } catch (Exception e) {\n            log.error(\"generate appClientCommandStatistics error {}, {}\", e.getMessage(), e);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/AppClientReportExceptionServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\n\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.client.service.AppClientReportExceptionService;\nimport com.sohu.cache.dao.AppClientExceptionStatisticsDao;\nimport com.sohu.cache.dao.AppClientLatencyCommandDao;\nimport com.sohu.cache.entity.AppClientExceptionStatistics;\nimport com.sohu.cache.entity.AppClientLatencyCommand;\nimport com.sohu.cache.report.ReportDataComponent;\nimport com.sohu.cache.util.MapUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.assertj.core.util.Lists;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Created by rucao on 2019/12/13\n */\n@Slf4j\n@Service(\"appClientReportExceptionService\")\npublic class AppClientReportExceptionServiceImpl implements AppClientReportExceptionService {\n\n    private static int ARGS_MAX_LEN = 255;\n    @Autowired\n    private AppClientExceptionStatisticsDao appClientExceptionStatisticsDao;\n    @Autowired\n    private AppClientLatencyCommandDao appClientLatencyCommandDao;\n\n    @Autowired\n    private ReportDataComponent reportDataComponent;\n\n    @Override\n    public void batchSave(long appId, String clientIp, String redisPoolConfig, long currentMin, List<Map<String, Object>> exceptionModels) {\n        try {\n            // 1.client上报\n            if (CollectionUtils.isEmpty(exceptionModels)) {\n                log.warn(\"exceptionModels is empty:{},{}\", clientIp, currentMin);\n                return;\n            }\n            // 2.解析\n            List<AppClientExceptionStatistics> appClientExceptionStatisticsList = exceptionModels.stream()\n                    .map(exceptionModel -> generate(appId, clientIp, redisPoolConfig, currentMin, exceptionModel))\n                    .filter(exceptionStatistics -> (exceptionStatistics != null))\n                    .collect(Collectors.toList());\n            // 4.批量保存\n            if (CollectionUtils.isNotEmpty(appClientExceptionStatisticsList)) {\n                appClientExceptionStatisticsDao.batchSave(appClientExceptionStatisticsList);\n                //上报数据\n                reportDataComponent.reportExceptionData(appClientExceptionStatisticsList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public Map<String, List<Map<String, Object>>> getAppExceptionStatisticsMap(Long appId, String clientIp, long startTime, long endTime, Integer type) {\n        try {\n            List<Map<String, Object>> appClientExceptionStatisticsList = appClientExceptionStatisticsDao.getAppExceptionStatistics(appId, clientIp, startTime, endTime, type);\n            Map<String, List<Map<String, Object>>> exceptionStatisticsMap = Maps.newHashMap();\n            appClientExceptionStatisticsList.stream().forEach(exceptionStatistic -> {\n                String client_ip = MapUtils.getString(exceptionStatistic, \"client_ip\");\n                ArrayList commandStatisticList = (ArrayList) MapUtils.getObject(exceptionStatisticsMap, client_ip);\n                if (CollectionUtils.isEmpty(commandStatisticList)) {\n                    commandStatisticList = Lists.newArrayList();\n                    exceptionStatisticsMap.put(client_ip, commandStatisticList);\n                }\n                commandStatisticList.add(exceptionStatistic);\n            });\n            return exceptionStatisticsMap;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return Collections.emptyMap();\n    }\n\n    @Override\n    public List<Map<String, Object>> getDistinctClientNodeStatistics(Long appId, String clientIp, long startTime, long endTime, Integer type) {\n        try {\n            return appClientExceptionStatisticsDao.getDistinctClientNodeStatistics(appId, clientIp, startTime, endTime, type);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return Collections.EMPTY_LIST;\n    }\n\n    @Override\n    public Map<String, String> getAppDistinctClientConfig(Long appId, Integer type, long startTime, long endTime) {\n        try {\n            List<Map<String, String>> clientConfigList = appClientExceptionStatisticsDao.getAppDistinctClientConfig(appId, type, startTime, endTime);\n            Map<String, String> clientConfigMap = clientConfigList.stream().collect(Collectors.toMap\n                    (clientConfig -> MapUtils.getString(clientConfig, \"client_ip\"),\n                            clientConfig -> MapUtils.getString(clientConfig, \"redis_pool_config\"),\n                            (key1, key2) -> key2));\n            return clientConfigMap;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public Map<String, List<String>> getAppClientConfigs(Long appId, Integer type, long startTime, long endTime) {\n        try {\n            List<Map<String, String>> clientConfigList = appClientExceptionStatisticsDao.getAppClientConfigs(appId, type, startTime, endTime);\n            Map<String, List<String>> clientConfigMap = Maps.newHashMap();\n            clientConfigList.stream().forEach(clientConfig -> {\n                        String client_ip = MapUtils.getString(clientConfig, \"client_ip\");\n                        String redis_pool_config = MapUtils.getString(clientConfig, \"redis_pool_config\");\n                        String change_time = MapUtils.getString(clientConfig, \"change_time\");\n\n                        String time_config = \"变更时间：\" + change_time + \"   配置：\" + redis_pool_config;\n                        List<String> configTimeList;\n                        if (clientConfigMap.containsKey(client_ip)) {\n                            configTimeList = clientConfigMap.get(client_ip);\n                        } else {\n                            configTimeList = Lists.newArrayList();\n                            clientConfigMap.put(client_ip, configTimeList);\n                        }\n                        configTimeList.add(time_config);\n                    }\n            );\n            return clientConfigMap;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public List<Map<String, Object>> getLatencyCommandDetailByNode(String clientIp, String node, long startTime, long endTime) {\n        try {\n            List<String> latencyCommandIdsList = appClientExceptionStatisticsDao.getLatencyCommandsByNode(clientIp, startTime, endTime, node);\n            String latencyCommandIdsStr = StringUtils.join(latencyCommandIdsList, \",\");\n            List<Long> ids = Arrays.stream(latencyCommandIdsStr.split(\",\")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());\n            return appClientLatencyCommandDao.getLatencyCommandByIds(ids);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return Collections.EMPTY_LIST;\n    }\n\n    @Override\n    public List<Map<String, Object>> getLatencyCommandDetailByNodeV2(String node, long searchTime) {\n        try {\n            List<String> latencyCommandIdsList = appClientExceptionStatisticsDao.getLatencyCommandsByNodeV2(node, searchTime);\n            String latencyCommandIdsStr = StringUtils.join(latencyCommandIdsList, \",\");\n            List<Long> ids = Arrays.stream(latencyCommandIdsStr.split(\",\")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());\n            List<Map<String, Object>> latencyCommandList = appClientLatencyCommandDao.getLatencyCommandByIds(ids);\n            return latencyCommandList;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return Collections.EMPTY_LIST;\n    }\n\n    @Override\n    public Map<String, Map<String, Object>> getSumCmdExpStatGroupByNode(long appId, long searchTime) {\n        try {\n            List<Map<String, Object>> sumCmdExpStatList = appClientExceptionStatisticsDao.getSumCmdExpStatGroupByNode(appId, searchTime);\n            return sumCmdExpStatList.stream().collect(Collectors.toMap(sumCmdExpStat -> MapUtils.getString(sumCmdExpStat, \"node\", \"\"), sumCmdExpStat -> sumCmdExpStat, (key1, key2) -> key2));\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return Collections.EMPTY_MAP;\n    }\n\n    @Override\n    public Map<String, List<Map<String, Object>>> getLatencyCommandDetails(Set<String> nodeSet, long searchTime) {\n        try {\n            return nodeSet.stream().collect(Collectors.toMap(node -> node, node -> getLatencyCommandDetailByNodeV2(node, searchTime)));\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return Collections.EMPTY_MAP;\n    }\n\n\n    private AppClientExceptionStatistics generate(long appId, String clientIp, String redisPoolConfig, long currentMin, Map<String, Object> exceptionModel) {\n        try {\n            AppClientExceptionStatistics appClientExceptionStatistics = (AppClientExceptionStatistics) MapUtil.mapToObject(exceptionModel, AppClientExceptionStatistics.class);\n            if (appClientExceptionStatistics != null) {\n                appClientExceptionStatistics.setAppId(appId);\n                appClientExceptionStatistics.setClientIp(clientIp);\n                appClientExceptionStatistics.setRedisPoolConfig(redisPoolConfig);\n                appClientExceptionStatistics.setCurrentMin(currentMin);\n                List<Map<String, Object>> commandFailedModels = (List) exceptionModel.get(\"commandFailedModels\");\n                if (CollectionUtils.isNotEmpty(commandFailedModels)) {\n                    List<AppClientLatencyCommand> appClientLatencyCommandList = commandFailedModels.stream()\n                            .map(commandFailedModel -> {\n                                try {\n                                    String args = MapUtils.getString(commandFailedModel, \"args\", \"\");\n                                    if (StringUtils.isNotBlank(args) && args.length() > ARGS_MAX_LEN) {\n                                        String args_new = args.substring(0, ARGS_MAX_LEN);\n                                        commandFailedModel.put(\"args\", args_new);\n                                    }\n                                    return (AppClientLatencyCommand) MapUtil.mapToObject(commandFailedModel, AppClientLatencyCommand.class);\n                                } catch (Exception e) {\n                                    log.error(e.getMessage(), e);\n                                    return null;\n                                }\n                            })\n                            .filter(latencyCommand -> (latencyCommand != null))\n                            .collect(Collectors.toList());\n                    if(CollectionUtils.isNotEmpty(appClientLatencyCommandList)){\n                        appClientLatencyCommandDao.batchSave(appClientLatencyCommandList);\n                        String latencyCommands = appClientLatencyCommandList.stream()\n                                .map(appClientLatencyCommand -> String.valueOf(appClientLatencyCommand.getId()))\n                                .collect(Collectors.joining(\",\"));\n                        appClientExceptionStatistics.setLatencyCommands(latencyCommands);\n                    }\n                }\n            }\n            return appClientExceptionStatistics;\n        } catch (Exception e) {\n            log.error(\"generate appClientCommandStatistics exceptionModel: {}, error {}, {}\", exceptionModel, e.getMessage(), e);\n            return null;\n        }\n\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/AppClientServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.client.command.AppClientCommand;\nimport com.sohu.cache.client.command.AppClientParams;\nimport com.sohu.cache.client.service.AppClientService;\nimport com.sohu.cache.client.service.ClientVersionService;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport javax.annotation.PostConstruct;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * Created by zhangyijun on 2017/8/4.\n */\npublic class AppClientServiceImpl implements AppClientService {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private InstanceDao instanceDao;\n\n    @Autowired\n    private AppDao appDao;\n\n    @Autowired\n    private ClientVersionService clientVersionService;\n\n    private ConcurrentMap<Long, Map<String, Object>> appClientMap = Maps.newConcurrentMap();\n\n    @PostConstruct\n    public void initApps() {\n        long start = System.currentTimeMillis();\n\n        List<AppDesc> onlineApps = appDao.getOnlineApps();\n        if (onlineApps == null) {\n            return;\n        }\n        Map<Long, List<InstanceInfo>> cacheInstances = Maps.newHashMap();\n        List<InstanceInfo> allInstances = instanceDao.getAllInsts();\n        for (InstanceInfo instanceInfo : allInstances) {\n            long appId = instanceInfo.getAppId();\n            if (instanceInfo.isOnline()) {\n                List<InstanceInfo> instances = cacheInstances.get(appId);\n                if (instances == null) {\n                    instances = Lists.newArrayList();\n                    cacheInstances.put(appId, instances);\n                }\n                instances.add(instanceInfo);\n            }\n        }\n        Map<Long, String> cacheMaxVersions = Maps.newHashMap();\n        List<Map<String, Object>> appMaxVersions = clientVersionService.getAllMaxClientVersion();\n        for (Map<String, Object> map : appMaxVersions) {\n            long appId = MapUtils.getLongValue(map, \"appId\");\n            String clientVersion = MapUtils.getString(map, \"clientVersion\");\n            cacheMaxVersions.put(appId, clientVersion);\n        }\n        for (AppDesc appDesc : onlineApps) {\n            int type = appDesc.getType();\n            long appId = appDesc.getAppId();\n            AppClientParams appClientParams = new AppClientParams(appId, type, null, null);\n            appClientParams.setCacheAppDesc(appDesc);\n            appClientParams.setCacheInstanceInfos(cacheInstances.get(appId));\n            appClientParams.setCacheMaxVersion(cacheMaxVersions.get(appId));\n\n            AppClientCommand command = new AppClientCommand(appClientParams, appDao, instanceDao, clientVersionService,\n                    appClientMap);\n            Map<String, Object> model;\n            if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                model = command.getRedisClusterInfo(false);\n                command.addAppClient(appId, model);\n            } else if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n                model = command.getRedisSentinelInfo(false);\n                command.addAppClient(appId, model);\n            } else if (type == ConstUtils.CACHE_REDIS_STANDALONE) {\n                model = command.getRedisStandaloneInfo(false);\n                command.addAppClient(appId, model);\n            }\n        }\n        long end = System.currentTimeMillis();\n        logger.warn(\"AppClientService: app-client-size={} cost={} ms\", appClientMap.size(), (end - start));\n    }\n\n    @Override\n    public Map<String, Object> getAppClientInfo(AppClientParams appClientParams) {\n        AppClientCommand command = new AppClientCommand(appClientParams, appDao, instanceDao, clientVersionService,\n                appClientMap);\n        return command.execute();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/AppClientStatisticGatherServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\n\nimport com.sohu.cache.client.service.AppClientStatisticGatherService;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport com.sohu.cache.entity.AppStats;\nimport com.sohu.cache.entity.TimeBetween;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.task.tasks.daily.TopologyExamTask;\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by rucao on 2019/12/29\n */\n@Slf4j\n@Service(\"appClientStatisticGatherService\")\npublic class AppClientStatisticGatherServiceImpl implements AppClientStatisticGatherService {\n    @Autowired\n    private AppClientExceptionStatisticsDao appClientExceptionStatisticsDao;\n    @Autowired\n    private AppClientCommandStatisticsDao appClientCommandStatisticsDao;\n    @Resource\n    private AppStatsDao appStatsDao;\n    @Resource\n    private InstanceSlowLogDao instanceSlowLogDao;\n    @Autowired\n    private InstanceLatencyHistoryDao instanceLatencyHistoryDao;\n    @Autowired\n    private AppClientStatisticGatherDao appClientStatisticGatherDao;\n    @Autowired\n    private AppStatsCenter appStatsCenter;\n    @Autowired\n    TopologyExamTask topologyExamTask;\n\n    @Override\n    public void bathSave(long startTime, long endTime) {\n        try {\n            List<AppClientStatisticGather> appClientConnExpStatList = appClientExceptionStatisticsDao.getAppClientConnExpStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appClientConnExpStatList)) {\n                appClientStatisticGatherDao.batchSaveConnExpStats(appClientConnExpStatList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appClientCmdExpStatList = appClientExceptionStatisticsDao.getAppClientCmdExpStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appClientCmdExpStatList)) {\n                appClientStatisticGatherDao.batchSaveCmdExpStats(appClientCmdExpStatList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appClientCmdStatList = appClientCommandStatisticsDao.getAppClientCmdStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appClientCmdStatList)) {\n                appClientStatisticGatherDao.batchSaveCmdStats(appClientCmdStatList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n//        try {\n//            List<AppClientStatisticGather> appMemFragRatioList = appStatsDao.getMemFragRatios(startTime, endTime);\n//            if (CollectionUtils.isNotEmpty(appMemFragRatioList)) {\n//                appClientStatisticGatherDao.batchSaveMemFragRatio(appMemFragRatioList);\n//            }\n//        } catch (Exception e) {\n//            log.error(e.getMessage(), e);\n//        }\n        try {\n            List<AppClientStatisticGather> appSlowLogCountList = instanceSlowLogDao.getAppSlowLogCountStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appSlowLogCountList)) {\n                appClientStatisticGatherDao.batchSaveSlowLogCount(appSlowLogCountList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appLatencyCountList = instanceLatencyHistoryDao.getAppLatencyCountStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appLatencyCountList)) {\n                appClientStatisticGatherDao.batchSaveLatencyCount(appLatencyCountList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appStatsList = appStatsDao.gatherAppsStats(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appStatsList)) {\n                appClientStatisticGatherDao.batchSaveAppStats(appStatsList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> connClientList = appStatsCenter.getOnlineAppConnClients();\n            if (CollectionUtils.isNotEmpty(connClientList)) {\n                appClientStatisticGatherDao.batchSaveConnClients(connClientList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> topologyExamList = topologyExamTask.checkAppsTopology(null);\n            if (CollectionUtils.isNotEmpty(topologyExamList)) {\n                appClientStatisticGatherDao.batchSaveTopologyExam(topologyExamList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public void bathAdd(long startTime, long endTime) {\n        try {\n            List<AppClientStatisticGather> appClientConnExpStatList = appClientExceptionStatisticsDao.getAppClientConnExpStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appClientConnExpStatList)) {\n                appClientStatisticGatherDao.batchAddConnExpStats(appClientConnExpStatList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appClientCmdExpStatList = appClientExceptionStatisticsDao.getAppClientCmdExpStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appClientCmdExpStatList)) {\n                appClientStatisticGatherDao.batchAddCmdExpStats(appClientCmdExpStatList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appClientCmdStatList = appClientCommandStatisticsDao.getAppClientCmdStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appClientCmdStatList)) {\n                appClientStatisticGatherDao.batchAddCmdStats(appClientCmdStatList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n//        try {\n//            TimeBetween timeBetween = fillWithDateFormat(startTime);\n//            long startTime_new = timeBetween.getStartTime();\n//            long endTime_new = timeBetween.getEndTime();\n//            List<AppClientStatisticGather> appMemFragRatioList = appStatsDao.getMemFragRatios(startTime_new, endTime_new);\n//            if (CollectionUtils.isNotEmpty(appMemFragRatioList)) {\n//                appClientStatisticGatherDao.batchSaveMemFragRatio(appMemFragRatioList);\n//            }\n//        } catch (Exception e) {\n//            log.error(e.getMessage(), e);\n//        }\n        try {\n            List<AppClientStatisticGather> appSlowLogCountList = instanceSlowLogDao.getAppSlowLogCountStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appSlowLogCountList)) {\n                appClientStatisticGatherDao.batchAddSlowLogCount(appSlowLogCountList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appLatencyCountList = instanceLatencyHistoryDao.getAppLatencyCountStat(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appLatencyCountList)) {\n                appClientStatisticGatherDao.batchAddLatencyCount(appLatencyCountList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        try {\n            List<AppClientStatisticGather> appStatsList = appStatsDao.gatherAppsStats(startTime, endTime);\n            if (CollectionUtils.isNotEmpty(appStatsList)) {\n                appClientStatisticGatherDao.batchSaveAppStats(appStatsList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n\n    }\n\n    @Override\n    public void bathAddServerCmdCount(long startTime, long endTime) {\n        //添加server端统计的命令调用次数\n        //每小时执行一次\n        try {\n            String gatherTimePre = null;\n            if(startTime == endTime){\n                gatherTimePre = DateUtil.formatYYYY_MM_dd(DateUtil.parseYYYYMMddHH(String.valueOf(startTime)));\n            }\n            String gatherTime = gatherTimePre;\n            List<AppStats> appStatsList = appStatsDao.getAppHourStatsByTime(startTime, endTime);\n            List<AppClientStatisticGather> gatherList = new ArrayList<>(appStatsList.size());\n            appStatsList.forEach(appStats -> {\n                AppClientStatisticGather appClientStatisticGather = new AppClientStatisticGather();\n                appClientStatisticGather.setAppId(appStats.getAppId());\n                appClientStatisticGather.setServerCmdCount(appStats.getCommandCount());\n                if(gatherTime != null){\n                    appClientStatisticGather.setGatherTime(gatherTime);\n                } else {\n                    try {\n                        String gatherTimeOne = DateUtil.formatYYYY_MM_dd(DateUtil.parseYYYYMMddHH(String.valueOf(appStats.getCollectTime())));\n                        appClientStatisticGather.setGatherTime(gatherTimeOne);\n                    } catch (Exception e) {\n                        log.error(e.getMessage(), e);\n                        return;\n                    }\n                }\n                gatherList.add(appClientStatisticGather);\n            });\n            if (CollectionUtils.isNotEmpty(appStatsList)) {\n                appClientStatisticGatherDao.batchAddAppServerCmdCount(gatherList);\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n    }\n\n    private TimeBetween fillWithDateFormat(long startTime) throws Exception {\n        String startTimeStr = Long.toString(startTime);\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\n        Date date = sdf.parse(startTimeStr);\n\n        Date startDate_new = DateUtils.addMinutes(date, 5);\n        Date endDate_new = DateUtils.addMinutes(startDate_new, 5);\n        long startTime_new = NumberUtils.toLong(DateUtil.formatDate(startDate_new, \"yyyyMMddHHmm00\"));\n        long endTime_new = NumberUtils.toLong(DateUtil.formatDate(endDate_new, \"yyyyMMddHHmm00\"));\n        return new TimeBetween(startTime_new, endTime_new, startDate_new, endDate_new);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/AppInstanceClientRelationServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collections;\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.sohu.cache.client.service.AppInstanceClientRelationService;\r\nimport com.sohu.cache.dao.AppInstanceClientRelationDao;\r\nimport com.sohu.cache.entity.AppClientCostTimeStat;\r\nimport com.sohu.cache.entity.AppInstanceClientRelation;\r\n\r\n/**\r\n * 应用下节点和客户端关系服务\r\n * \r\n * @author leifu\r\n * @Date 2016年5月3日\r\n * @Time 下午6:48:40\r\n */\r\npublic class AppInstanceClientRelationServiceImpl implements AppInstanceClientRelationService {\r\n\r\n    private Logger logger = LoggerFactory.getLogger(AppInstanceClientRelationServiceImpl.class);\r\n\r\n    private AppInstanceClientRelationDao appInstanceClientRelationDao;\r\n\r\n    @Override\r\n    public void batchSave(List<AppClientCostTimeStat> appClientCostTimeStatList) {\r\n        if (CollectionUtils.isEmpty(appClientCostTimeStatList)) {\r\n            return;\r\n        }\r\n        try {\r\n            List<AppInstanceClientRelation> appInstanceClientRelationList = new ArrayList<AppInstanceClientRelation>();\r\n            for (AppClientCostTimeStat appClientCostTimeStat : appClientCostTimeStatList) {\r\n                AppInstanceClientRelation appInstanceClientRelation = AppInstanceClientRelation.generateFromAppClientCostTimeStat(appClientCostTimeStat);\r\n                if (appInstanceClientRelation != null) {\r\n                    appInstanceClientRelationList.add(appInstanceClientRelation);\r\n                }\r\n            }\r\n            if (CollectionUtils.isNotEmpty(appInstanceClientRelationList)) {\r\n                appInstanceClientRelationDao.batchSave(appInstanceClientRelationList);\r\n            }\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public List<AppInstanceClientRelation> getAppInstanceClientRelationList(Long appId, Date date) {\r\n        try {\r\n            return appInstanceClientRelationDao.getAppInstanceClientRelationList(appId, date);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    public void setAppInstanceClientRelationDao(AppInstanceClientRelationDao appInstanceClientRelationDao) {\r\n        this.appInstanceClientRelationDao = appInstanceClientRelationDao;\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/ClientReportExceptionServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\r\n\r\nimport com.sohu.cache.client.service.ClientReportExceptionService;\r\nimport com.sohu.cache.client.service.ClientReportInstanceService;\r\nimport com.sohu.cache.dao.AppClientExceptionStatDao;\r\nimport com.sohu.cache.dao.AppClientVersionDao;\r\nimport com.sohu.cache.entity.AppClientExceptionStat;\r\nimport com.sohu.cache.entity.ClientInstanceException;\r\nimport com.sohu.cache.web.util.Page;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport java.util.Collections;\r\nimport java.util.List;\r\n\r\n/**\r\n * 客户端上报异常service\r\n * \r\n * @author leifu\r\n * @Date 2015年1月19日\r\n * @Time 上午10:02:32\r\n */\r\npublic class ClientReportExceptionServiceImpl implements ClientReportExceptionService {\r\n\r\n    private final Logger logger = LoggerFactory.getLogger(ClientReportExceptionServiceImpl.class);\r\n\r\n    /**\r\n     * 客户端异常操作\r\n     */\r\n    private AppClientExceptionStatDao appClientExceptionStatDao;\r\n\r\n    /**\r\n     * host:port与instanceInfo简单缓存\r\n     */\r\n    private ClientReportInstanceService clientReportInstanceService;\r\n    \r\n    /**\r\n     * 客户端ip,版本查询\r\n     */\r\n    private AppClientVersionDao appClientVersionDao;\r\n\r\n    \r\n\r\n    @Override\r\n    public List<AppClientExceptionStat> getAppExceptionList(Long appId, long startTime, long endTime, int type,\r\n            String clientIp, Page page) {\r\n        try {\r\n            return appClientExceptionStatDao.getAppExceptionList(appId, startTime, endTime, type, clientIp, page);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n    \r\n    @Override\r\n    public int getAppExceptionCount(Long appId, long startTime, long endTime, int type, String clientIp) {\r\n        try {\r\n            return appClientExceptionStatDao.getAppExceptionCount(appId, startTime, endTime, type, clientIp);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return 0;\r\n        }\r\n    }\r\n    \r\n    @Override\r\n    public List<ClientInstanceException> getInstanceExceptionStat(String ip, long collectTime) {\r\n        try {\r\n            return appClientExceptionStatDao.getInstanceExceptionStat(ip, collectTime);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    public void setAppClientExceptionStatDao(AppClientExceptionStatDao appClientExceptionStatDao) {\r\n        this.appClientExceptionStatDao = appClientExceptionStatDao;\r\n    }\r\n\r\n    public void setAppClientVersionDao(AppClientVersionDao appClientVersionDao) {\r\n        this.appClientVersionDao = appClientVersionDao;\r\n    }\r\n\r\n    public void setClientReportInstanceService(ClientReportInstanceService clientReportInstanceService) {\r\n        this.clientReportInstanceService = clientReportInstanceService;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/ClientReportInstanceServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\r\n\r\nimport java.util.concurrent.ConcurrentHashMap;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.sohu.cache.client.service.ClientReportInstanceService;\r\nimport com.sohu.cache.dao.InstanceDao;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\n\r\n/**\r\n * @author leifu\r\n * @Date 2016年5月5日\r\n * @Time 上午11:05:35\r\n */\r\npublic class ClientReportInstanceServiceImpl implements ClientReportInstanceService {\r\n\r\n    private Logger logger = LoggerFactory.getLogger(ClientReportInstanceServiceImpl.class);\r\n\r\n    /**\r\n     * 不要求一致性的本地缓存(hostport<=>instanceInfo)\r\n     */\r\n    private final static ConcurrentHashMap<String, InstanceInfo> hostPortInstanceMap = new ConcurrentHashMap<String, InstanceInfo>();\r\n\r\n    private InstanceDao instanceDao;\r\n\r\n    @Override\r\n    public InstanceInfo getInstanceInfoByHostPort(String host, int port) {\r\n        String hostPort = host + \":\" + port;\r\n        try {\r\n            InstanceInfo instanceInfo = hostPortInstanceMap.get(hostPort);\r\n            if (instanceInfo == null) {\r\n                instanceInfo = instanceDao.getInstByIpAndPort(host, port);\r\n                if (instanceInfo != null) {\r\n                    hostPortInstanceMap.putIfAbsent(hostPort, instanceInfo);\r\n                }\r\n            }\r\n            return instanceInfo;\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return null;\r\n        }\r\n    }\r\n\r\n    public void setInstanceDao(InstanceDao instanceDao) {\r\n        this.instanceDao = instanceDao;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/ClientVersionServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\r\n\r\nimport com.sohu.cache.client.service.ClientVersionService;\r\nimport com.sohu.cache.dao.AppClientVersionDao;\r\nimport com.sohu.cache.entity.AppClientVersion;\r\nimport com.sohu.cache.util.StringUtil;\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Value;\r\n\r\nimport java.util.*;\r\n\r\n/**\r\n * 客户端版本信息\r\n *\r\n * @author leifu\r\n * @Date 2015年2月2日\r\n * @Time 上午10:19:59\r\n */\r\npublic class ClientVersionServiceImpl implements ClientVersionService {\r\n\r\n    private final Logger logger = LoggerFactory.getLogger(ClientVersionServiceImpl.class);\r\n\r\n    private AppClientVersionDao appClientVersionDao;\r\n\r\n    @Override\r\n    public void saveOrUpdateClientVersion(long appId, String appClientIp, String clientVersion) {\r\n        try {\r\n            AppClientVersion appClientVersion = new AppClientVersion();\r\n            appClientVersion.setAppId(appId);\r\n            appClientVersion.setClientIp(appClientIp);\r\n            appClientVersion.setClientVersion(clientVersion);\r\n            appClientVersion.setReportTime(new Date());\r\n            appClientVersionDao.saveOrUpdateClientVersion(appClientVersion);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public List<AppClientVersion> getAppAllClientVersion(long appId) {\r\n        try {\r\n            return appClientVersionDao.getAppAllClientVersion(appId);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    public String getAppMaxClientVersion(long appId) {\r\n        try {\r\n            return appClientVersionDao.getAppMaxClientVersion(appId);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return null;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public List<Map<String, Object>> getAllMaxClientVersion() {\r\n        try {\r\n            return appClientVersionDao.getAllMaxClientVersion();\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public List<AppClientVersion> getAll(long appId) {\r\n        try {\r\n            return appClientVersionDao.getAll(appId);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    public void setAppClientVersionDao(AppClientVersionDao appClientVersionDao) {\r\n        this.appClientVersionDao = appClientVersionDao;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/client/service/impl/DealClientReportServiceImpl.java",
    "content": "package com.sohu.cache.client.service.impl;\n\nimport com.alibaba.fastjson.JSON;\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.client.AppClientReportModel;\nimport com.sohu.cache.client.service.AppClientReportCommandService;\nimport com.sohu.cache.client.service.AppClientReportExceptionService;\nimport com.sohu.cache.client.service.DealClientReportService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/13\n */\n@Slf4j\npublic class DealClientReportServiceImpl implements DealClientReportService {\n    @Autowired\n    private AsyncService asyncService;\n    @Autowired\n    private AppClientReportCommandService appClientReportCommandService;\n    @Autowired\n    private AppClientReportExceptionService appClientReportExceptionService;\n\n    @Override\n    public void init() {\n        asyncService.assemblePool(getThreadPoolKey(), AsyncThreadPoolFactory.CLIENT_REPORT_THREAD_POOL);\n    }\n\n    @Override\n    public boolean deal(final AppClientReportModel appClientReportModel) {\n        try {\n            // 上报的数据\n            final long appId = appClientReportModel.getAppId();\n            final String clientIp = appClientReportModel.getClientIp();\n            final String redisPoolConfig = JSON.toJSONString(appClientReportModel.getConfig());\n            final long currentMin = appClientReportModel.getCurrentMin();\n            final List<Map<String, Object>> commandStatsModels = appClientReportModel.getCommandStatsModels();\n            final List<Map<String, Object>> exceptionModels = appClientReportModel.getExceptionModels();\n            String key = getThreadPoolKey() + \"_\" + clientIp;\n            asyncService.submitFuture(getThreadPoolKey(), new KeyCallable<Boolean>(key) {\n                @Override\n                public Boolean execute() {\n                    try {\n                        if (CollectionUtils.isNotEmpty(commandStatsModels)) {\n                            appClientReportCommandService.batchSave(appId, clientIp, currentMin, commandStatsModels);\n                        }\n                        if (CollectionUtils.isNotEmpty(exceptionModels)) {\n                            appClientReportExceptionService.batchSave(appId, clientIp, redisPoolConfig, currentMin, exceptionModels);\n                        }\n                        return true;\n                    } catch (Exception e) {\n                        log.error(e.getMessage(), e);\n                        return false;\n                    }\n                }\n            });\n            return true;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    private String getThreadPoolKey() {\n        return AsyncThreadPoolFactory.CLIENT_REPORT_POOL;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/configuration/AppClientReportBean.java",
    "content": "package com.sohu.cache.configuration;\n\nimport com.sohu.cache.client.service.DealClientReportService;\nimport com.sohu.cache.client.service.impl.DealClientReportServiceImpl;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Created by rucao on 2019/12/16\n */\n@Configuration\npublic class AppClientReportBean {\n    @Bean(initMethod = \"init\")\n    DealClientReportService dealClientReportService() {\n        return new DealClientReportServiceImpl();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/configuration/AsynExecutorConfiguration.java",
    "content": "package com.sohu.cache.configuration;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.annotation.EnableAsync;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2022/10/9 14:52\n * @Description:\n */\n@Configuration\n@EnableAsync\npublic class AsynExecutorConfiguration {\n\n    /** Set the ThreadPoolExecutor's core pool size. */\n    private int corePoolSize = 10;\n    /** Set the ThreadPoolExecutor's maximum pool size. */\n    private int maxPoolSize = 50;\n    /** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */\n    private int queueCapacity = 500;\n\n    @Bean\n    public Executor asyncExecutor() {\n        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n        executor.setCorePoolSize(corePoolSize);\n        executor.setMaxPoolSize(maxPoolSize);\n        executor.setQueueCapacity(queueCapacity);\n        executor.setThreadNamePrefix(\"AsyncExecutor-\");\n\n        // rejection-policy：当pool已经达到max size的时候，如何处理新任务\n        // CALLER_RUNS：不在新线程中执行任务，而是有调用者所在的线程来执行\n        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());\n        executor.initialize();\n        return executor;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/configuration/AsyncConfiguration.java",
    "content": "package com.sohu.cache.configuration;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.concurrent.ForkJoinPool;\n\n@Configuration\n@Slf4j\npublic class AsyncConfiguration {\n\n    private int parallelism=256;\n\n    @Bean\n    public ForkJoinPool forkJoinPool() {\n        log.info(\"availableProcessors:{}, parallelism:{}\", Runtime.getRuntime().availableProcessors(), parallelism);\n        ForkJoinPool forkJoinPool = new ForkJoinPool(parallelism);\n        return forkJoinPool;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/configuration/DataSourceConfig.java",
    "content": "package com.sohu.cache.configuration;\n\nimport com.zaxxer.hikari.HikariDataSource;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Created by zengyizhao on 2022/7/27.\n */\n@Configuration\npublic class DataSourceConfig {\n\n    @Bean(name = \"cacheCloudDB\")\n    @ConfigurationProperties(prefix = \"cachecloud.primary\")\n    public HikariDataSource quartzDataSource() {\n        return DataSourceBuilder.create()\n                .type(HikariDataSource.class)\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/configuration/FreemakerCustomConfig.java",
    "content": "package com.sohu.cache.configuration;\n\nimport freemarker.template.DefaultObjectWrapper;\nimport freemarker.template.TemplateExceptionHandler;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2023/4/10 15:23\n * @Description: freemaker 定制配置\n * @Version: 1.0\n */\n@Configuration\npublic class FreemakerCustomConfig {\n\n    @Bean\n    public freemarker.template.Configuration configuration(freemarker.template.Configuration configuration) {\n        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);\n        DefaultObjectWrapper objectWrapper = (DefaultObjectWrapper) configuration.getObjectWrapper();\n        objectWrapper.setUseAdaptersForContainers(true);\n        return configuration;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/configuration/SSHPoolConfig.java",
    "content": "package com.sohu.cache.configuration;\n\nimport com.sohu.cache.ssh.SSHMachineInfo;\nimport com.sohu.cache.ssh.SSHSessionPooledObjectFactory;\nimport org.apache.commons.pool2.impl.GenericKeyedObjectPool;\nimport org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;\nimport org.apache.sshd.client.session.ClientSession;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2024/2/22 15:20\n * @Description: ssh session 连接池配置\n * @Version: 1.0\n */\n@Configuration\npublic class SSHPoolConfig {\n\n    /**\n     * ssh连接池配置\n     * @return\n     */\n    @Bean\n    public GenericKeyedObjectPool<SSHMachineInfo, ClientSession> clientSessionPool() throws GeneralSecurityException, IOException {\n        GenericKeyedObjectPoolConfig genericKeyedObjectPoolConfig = new GenericKeyedObjectPoolConfig();\n        genericKeyedObjectPoolConfig.setTestWhileIdle(true);\n        genericKeyedObjectPoolConfig.setTestOnReturn(true);\n        genericKeyedObjectPoolConfig.setMaxTotalPerKey(5);\n        genericKeyedObjectPoolConfig.setMaxIdlePerKey(1);\n        genericKeyedObjectPoolConfig.setMinIdlePerKey(1);\n        genericKeyedObjectPoolConfig.setMaxWaitMillis(30000);\n        genericKeyedObjectPoolConfig.setTimeBetweenEvictionRunsMillis(20000);\n        genericKeyedObjectPoolConfig.setJmxEnabled(false);\n        SSHSessionPooledObjectFactory factory = new SSHSessionPooledObjectFactory();\n        GenericKeyedObjectPool<SSHMachineInfo, ClientSession> genericKeyedObjectPool = new GenericKeyedObjectPool<>(\n                factory,\n                genericKeyedObjectPoolConfig);\n        return genericKeyedObjectPool;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/configuration/WebAntPathConfig.java",
    "content": "package com.sohu.cache.configuration;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;\nimport org.springframework.web.servlet.config.annotation.PathMatchConfigurer;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class WebAntPathConfig implements WebMvcConfigurer {\n    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {\n        configurer.favorPathExtension(true);\n    }\n    public void configurePathMatch(PathMatchConfigurer configurer) {\n        configurer.setUseSuffixPatternMatch(true);\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppAuditLogTypeEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 日志类型\r\n * @author leifu\r\n */\r\npublic enum AppAuditLogTypeEnum {\r\n    // 申请应用\r\n    APP_DESC_APPLY(1),\r\n    // 扩容申请\r\n    APP_SCALE_APPLY(2),\r\n    // 应用配置修改申请\r\n    APP_CONFIG_APPLY(3),\r\n    // 审批\r\n    APP_CHECK(4),\r\n    // 修改报警阀值\r\n    APP_CHANGE_ALERT(5),\r\n    // 清理数据\r\n    APP_CLEAN_DATA(6),\r\n    // 实例配置修改申请\r\n    INSTANCE_CONFIG_APPLY(7),\r\n    // 键值分析\r\n    KEY_VALUE_ANALYSIS(8),\r\n    // flush全部数据\r\n    FLUSHALL_DATA(9),\r\n    // 应用诊断申请\r\n    APP_DIAGNOSTIC_APPLY(10);\r\n\r\n    private int value;\r\n    \r\n\r\n    private AppAuditLogTypeEnum(int value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public int value() {\r\n        return value;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return String.valueOf(value);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppAuditType.java",
    "content": "package com.sohu.cache.constant;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n/**\r\n * Created by yijunzhang on 14-10-20.\r\n */\r\npublic enum AppAuditType {\r\n    APP_AUDIT(0, \"申请集群\"),\r\n    APP_SCALE(1, \"集群容量变更\"),\r\n    APP_MODIFY_CONFIG(2, \"集群修改配置\"),\r\n    REGISTER_USER_APPLY(3, \"用户注册\"),\r\n    INSTANCE_MODIFY_CONFIG(4, \"实例修改配置\"),\r\n    APP_MONITOR_CONFIG(5, \"全局报警配置修改\"),\r\n    KEY_ANALYSIS(6, \"键值分析\"),\r\n    FLUSHALL_DATA(7, \"清理数据\"),\r\n    APP_DIAGNOSTIC(8, \"应用诊断\"),\r\n    APP_OFFLINE(10, \"应用下线\"),\r\n    APP_MIGRATE(11, \"应用数据迁移\"),\r\n    APP_IMPORT(12, \"应用导入\"),\r\n    SCAN_CLEAN(13, \"数据分析清理\");\r\n\r\n    private final static Map<Integer, AppAuditType> MAP = new HashMap<Integer, AppAuditType>();\r\n\r\n    static {\r\n        for (AppAuditType appAuditType : AppAuditType.values()) {\r\n            MAP.put(appAuditType.getValue(), appAuditType);\r\n        }\r\n    }\r\n\r\n    public static AppAuditType getAppAuditType(int value) {\r\n        return MAP.get(value);\r\n    }\r\n\r\n    private int value;\r\n\r\n    private String info;\r\n\r\n    private AppAuditType(int value, String info) {\r\n        this.value = value;\r\n        this.info = info;\r\n    }\r\n\r\n    public int getValue() {\r\n        return value;\r\n    }\r\n\r\n    public String getInfo() {\r\n        return info;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppCheckEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 审批状态\r\n * \r\n * @author leifu\r\n * @Time 2014年10月20日\r\n */\r\npublic enum AppCheckEnum {\r\n\r\n    // 通过审批\r\n    APP_PASS(1),\r\n    // 驳回审批\r\n    APP_REJECT(-1),\r\n    // 等待审批\r\n    APP_WATING_CHECK(0),\r\n    //分配资源完毕\r\n    APP_ALLOCATE_RESOURCE(2);\r\n\r\n    private Integer value;\r\n\r\n    public Integer value() {\r\n        return value;\r\n    }\r\n    \r\n    private AppCheckEnum(Integer value) {\r\n        this.value = value;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return String.valueOf(value);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppDataMigrateEnum.java",
    "content": "package com.sohu.cache.constant;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Redis迁移类型枚举\n * @author leifu\n * @Date 2016-6-8\n * @Time 下午3:02:50\n */\npublic enum AppDataMigrateEnum {\n    //redis-migrate-tool\n    REDIS_NODE(0, \"single\"),\n    REDIS_CLUSTER_NODE(1, \"redis cluster\"),\n    RDB_FILE(2, \"rdb file\"),\n    TWEMPROXY(3, \"twemproxy\"),\n    AOF_FILE(4, \"aof file\"),\n    //redis-shake\n    standalone(5, \"standalone\"),\n    sentinel(6,\"sentinel\"),\n    cluster(7,\"cluster\");\n\n    private int index;\n\n    private String type;\n\n    private static Map<Integer, AppDataMigrateEnum> MAP = new HashMap<Integer, AppDataMigrateEnum>();\n    static {\n        for (AppDataMigrateEnum redisMigrateEnum : AppDataMigrateEnum.values()) {\n            MAP.put(redisMigrateEnum.getIndex(), redisMigrateEnum);\n        }\n    }\n\n    public static AppDataMigrateEnum getByIndex(int index) {\n        return MAP.get(index);\n    }\n\n    private AppDataMigrateEnum(int index, String type) {\n        this.index = index;\n        this.type = type;\n    }\n    \n    public static boolean isFileType(AppDataMigrateEnum appDataMigrateEnum) {\n        if (RDB_FILE.equals(appDataMigrateEnum) || AOF_FILE.equals(appDataMigrateEnum)) {\n            return true;\n        }\n        return false;\n    }\n\n    public int getIndex() {\n        return index;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppDataMigrateResult.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 迁移结果\r\n * \r\n * @author leifu\r\n * @Date 2016-6-8\r\n * @Time 下午3:15:08\r\n */\r\npublic class AppDataMigrateResult {\r\n\r\n    private int status;\r\n\r\n    private String message;\r\n\r\n    public AppDataMigrateResult(int status, String message) {\r\n        this.status = status;\r\n        this.message = message;\r\n    }\r\n\r\n    public boolean isSuccess() {\r\n        if (status == 1) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static AppDataMigrateResult success() {\r\n        return new AppDataMigrateResult(1, \"所有检查都成功，可以迁移啦!\");\r\n    }\r\n    \r\n    public static AppDataMigrateResult success(String message) {\r\n        return new AppDataMigrateResult(1, message);\r\n    }\r\n\r\n    public static AppDataMigrateResult fail(String message) {\r\n        return new AppDataMigrateResult(0, message);\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n\r\n    public void setStatus(int status) {\r\n        this.status = status;\r\n    }\r\n\r\n    public String getMessage() {\r\n        return message;\r\n    }\r\n\r\n    public void setMessage(String message) {\r\n        this.message = message;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"RedisMigrateResult [status=\" + status + \", message=\" + message + \"]\";\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppDataMigrateStatusEnum.java",
    "content": "package com.sohu.cache.constant;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 迁移状态\n * @author leifu\n * @Date 2016-6-9\n * @Time 下午7:53:28\n */\npublic enum AppDataMigrateStatusEnum {\n\n\n    PREPARE(-1,\"准备阶段\"),\n    START(0, \"全量同步\"),\n    FULL_END(3,\"增量同步\"),\n    END(1, \"同步结束\"),\n    ERROR(2, \"同步异常\");\n\n    private int status;\n\n    private String info;\n\n    private static Map<Integer, AppDataMigrateStatusEnum> MAP = new HashMap<Integer, AppDataMigrateStatusEnum>();\n    static {\n        for (AppDataMigrateStatusEnum rppDataMigrateStatusEnum : AppDataMigrateStatusEnum.values()) {\n            MAP.put(rppDataMigrateStatusEnum.getStatus(), rppDataMigrateStatusEnum);\n        }\n    }\n\n    public static AppDataMigrateStatusEnum getByStatus(int status) {\n        return MAP.get(status);\n    }\n\n    private AppDataMigrateStatusEnum(int status, String info) {\n        this.status = status;\n        this.info = info;\n    }\n\n    public int getStatus() {\n        return status;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppDescEnum.java",
    "content": "package com.sohu.cache.constant;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * app详情枚举(以后有关app详情的都往这里面迁)\n * \n * @author leifu\n * @Date 2015年1月26日\n * @Time 上午11:34:22\n */\npublic class AppDescEnum {\n    \n    /**\n     * 应用类型：0 正式，1：测试 2：试用\n     */\n    public static enum AppTest {\n        IS_TEST(1),\n        NOT_TEST(0),\n        IS_TRAIL(2);\n\n        private int value;\n\n        private AppTest(int value) {\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n    }\n    \n    /**\n     * 应用重要度\n     */\n    public static enum AppImportantLevel {\n        SUPER_IMPORTANT(1, \"S\"),\n        VERY_IMPORTANT(2, \"A\"),\n        IMPORTANT(3, \"B\"),\n        COMMON(4, \"C\");\n\n        private int value;\n        \n        private String info;\n\n        private AppImportantLevel(int value, String info) {\n            this.value = value;\n            this.info = info;\n        }\n\n        public int getValue() {\n            return value;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n\n\n    }\n\n    /**\n     * 应用aof持久化刷盘策略\n     */\n    public static enum AppPersistenceType {\n        GENERAL(0, \"主从均aof每秒刷盘\"),\n        PERFORMANCE_DATASAFE_UNITY(1, \"主aof系统自动刷盘，从每秒刷盘\"),\n        PERFORMANCE_BEST(2, \"主aof关闭，从每秒刷盘\"),\n        NO_PERSISTENCE(3, \"不持久化\");\n\n        private int value;\n\n        private String info;\n\n        private AppPersistenceType(int value, String info) {\n            this.value = value;\n            this.info = info;\n        }\n\n        public int getValue() {\n            return value;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n\n        public static AppPersistenceType getByType(Integer persisType){\n            if(persisType == null){\n                return null;\n            }\n            AppPersistenceType[] values = AppPersistenceType.values();\n            Optional<AppPersistenceType> typeOptional = Arrays.asList(values).stream().filter(type -> type.getValue() == persisType).findFirst();\n            if(typeOptional.isPresent()){\n                return typeOptional.get();\n            }\n            return null;\n        }\n    }\n\n    /**\n     * 应用内存淘汰策略\n     */\n    public static enum MaxmemoryPolicyType {\n        NOEVICTION(0, \"noeviction\",\"不淘汰，占满写入失败\"),\n        ALLKEYSLRU(1, \"allkeys-lru\",\"所有键-最近最少使用\"),\n        ALLKEYSLFU(2, \"allkeys-lfu\",\"所有键-最少频率使用\"),\n        VOLATILELRU(3, \"volatile-lru\",\"有过期时间的键-最近最少使用\"),\n        VOLATILELFU(4, \"volatile-lfu\",\"有过期时间的键-最少频率使用\"),\n        ALLKEYSRANDOM(5, \"allkeys-random\",\"所有键-随机\"),\n        VOLATILERANDOM(6, \"volatile-random\",\"有过期时间的键-随机\"),\n        VOLATILETTL(7, \"volatile-ttl\",\"有过期时间的键-剩余时间最短\");\n\n        private int type;\n\n        private String name;\n\n        private String desc;\n\n        private MaxmemoryPolicyType(int type, String name, String desc) {\n           this.type = type;\n           this.name = name;\n           this.desc = desc;\n        }\n\n        public int getType(){\n            return type;\n        }\n\n        public String getName(){\n            return name;\n        }\n\n        public String getDesc(){\n            return desc;\n        }\n\n        public static MaxmemoryPolicyType getByType(int type){\n            Optional<MaxmemoryPolicyType> policyTypeOptional = Arrays.asList(MaxmemoryPolicyType.values()).stream().filter(maxmemoryPolicyType -> maxmemoryPolicyType.type == type).findFirst();\n            if(policyTypeOptional.isPresent()){\n                return policyTypeOptional.get();\n            }\n            return null;\n        }\n\n        public static MaxmemoryPolicyType getByName(String maxmemoryPolicyName){\n            Optional<MaxmemoryPolicyType> policyTypeOptional = Arrays.asList(MaxmemoryPolicyType.values()).stream().filter(maxmemoryPolicyType -> maxmemoryPolicyType.name.equals(maxmemoryPolicyName)).findFirst();\n            if(policyTypeOptional.isPresent()){\n                return policyTypeOptional.get();\n            }\n            return null;\n        }\n\n        public static List<MaxmemoryPolicyType> getAll(){\n            MaxmemoryPolicyType[] values = MaxmemoryPolicyType.values();\n            return Arrays.asList(values);\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppEnvNameEnum.java",
    "content": "package com.sohu.cache.constant;\n\n/**\n * @author fulei\n */\npublic enum AppEnvNameEnum {\n\n\tlocal(\"local\"),\n\ttest(\"test\"),\n\tonline_web(\"online_web\"),\n\tonline_stat(\"online_stat\"),\n\tonline_backup(\"online_backup\"),\n\tafun(\"afun\"),\n\tbrazil(\"brazil\"),\n\t;\n\t\n\tprivate String name;\n\n\tprivate AppEnvNameEnum(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\t\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppStatusEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 应用发布状态\r\n * \r\n * @author leifu\r\n * @Time 2014年6月28日\r\n */\r\npublic enum AppStatusEnum {\r\n    STATUS_INITIALIZE(0, \"未分配\"),\r\n    STATUS_ALLOCATED(1, \"已申请未审批\"),\r\n    STATUS_PUBLISHED(2, \"运行中\"),\r\n    STATUS_OFFLINE(3, \"已下线\"),\r\n    STATUS_DENY(4, \"驳回\");\r\n\r\n    private int status;\r\n\r\n    private String info;\r\n\r\n    private static Map<Integer, AppStatusEnum> MAP = new HashMap<Integer, AppStatusEnum>();\r\n    static {\r\n        for (AppStatusEnum appStatusEnum : AppStatusEnum.values()) {\r\n            MAP.put(appStatusEnum.getStatus(), appStatusEnum);\r\n        }\r\n    }\r\n\r\n    private AppStatusEnum(int status, String info) {\r\n        this.status = status;\r\n        this.info = info;\r\n    }\r\n\r\n    public static AppStatusEnum getByStatus(int status) {\r\n        return MAP.get(status);\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n\r\n    public String getInfo() {\r\n        return info;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppTopology.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 应用的配置、节点信息\r\n *\r\n * Created by lingguo on 14-6-26.\r\n */\r\npublic enum AppTopology {\r\n    TOTAL_MEMORY(\"total_memory\"),           /* 应用的总内存 */\r\n    MACHINE_COUNT(\"machine_count\"),         /* 应用的机器数量 */\r\n    MASTER_COUNT(\"master_count\"),           /* 主节点的数量 */\r\n    SLAVE_COUNT(\"slave_count\");             /* 从节点的数量 */\r\n\r\n    private String value;\r\n\r\n    AppTopology(String value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public String getValue() {\r\n        return this.value;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppUserAlertEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 用户类型\r\n * @author leifu\r\n * @Time 2014年10月21日\r\n */\r\npublic enum AppUserAlertEnum {\r\n\r\n    //0:不报警\r\n    NO(0),\r\n    //1:接收报警\r\n    YES(1);\r\n\r\n    private Integer value;\r\n\r\n    private AppUserAlertEnum(Integer value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public Integer value() {\r\n        return value;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return String.valueOf(value);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/AppUserTypeEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 用户类型\r\n * @author leifu\r\n * @Time 2014年10月21日\r\n */\r\npublic enum AppUserTypeEnum {\r\n\r\n    //管理员\r\n    ADMIN_USER(0),\r\n    //普通用户\r\n    REGULAR_USER(2),\r\n    //不存在用户\r\n    NO_USER(-1);\r\n    \r\n    private Integer value;\r\n\r\n    private AppUserTypeEnum(Integer value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public Integer value() {\r\n        return value;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return String.valueOf(value);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/BaseConstant.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * @author leifu\r\n * @Date 2016-1-26\r\n * @Time 下午9:26:58\r\n */\r\npublic class BaseConstant {\r\n\r\n    /** mill seconds of one day */\r\n    public static long MILLISECONDS_OF_ONE_SECOND = 1000;\r\n\r\n    /** mill seconds of one day */\r\n    public static long MILLISECONDS_OF_ONE_MINUTE = 1000L * 60;\r\n\r\n    /** mill seconds of one day */\r\n    public static long MILLISECONDS_OF_ONE_DAY = 1000L * 60 * 60 * 24;\r\n\r\n    /** mill seconds of one hour */\r\n    public static long MILLISECONDS_OF_ONE_HOUR = 1000L * 60 * 60;\r\n\r\n    public static String EMPTY_STRING = \"\";\r\n\r\n    public static String[] EMPTY_STRING_ARRAY = new String[0];\r\n\r\n    /**\r\n     * WORD_SEPARATOR ( char )2\r\n     */\r\n    public static final String WORD_SEPARATOR = Character.toString((char) 2);\r\n\r\n    public static final String SYSTEM_PROPERTY_CONFIG_FILE_PATH = \"configFilePath\";\r\n\r\n    public static final int ONE = 1;\r\n\r\n    public static final int ZERO = 0;\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/ChmodEnum.java",
    "content": "package com.sohu.cache.constant;\n\n/**\n * \n * @author fulei\n */\npublic enum ChmodEnum {\n\t\n\tEXECUTE(\"x\"),\n\tREAD(\"r\"),\n\tWRITE(\"w\");\n\n\tprivate String op;\n\n\tprivate ChmodEnum(String op) {\n\t\tthis.op = op;\n\t}\n\n\tpublic String getOp() {\n\t\treturn op;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/ClientStatusEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 检查客户端的版本是否ok的枚举\r\n *\r\n * @author: lingguo\r\n * @time: 2014/10/22 10:13\r\n */\r\npublic enum ClientStatusEnum {\r\n    GOOD(1),\r\n    WARN(0),\r\n    ERROR(-1);\r\n\r\n    int status;\r\n\r\n    ClientStatusEnum(int status) {\r\n        this.status = status;\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/ClusterOperateResult.java",
    "content": "package com.sohu.cache.constant;\n\n\n/**\n * Cluster Operate Result\n * @author leifu\n * @Date 2017年6月27日\n * @Time 上午8:43:10\n */\npublic class ClusterOperateResult {\n\n    private int status;\n\n    private String message;\n\n    public ClusterOperateResult(int status, String message) {\n        this.status = status;\n        this.message = message;\n    }\n\n    public static ClusterOperateResult success() {\n        return new ClusterOperateResult(1, \"\");\n    }\n\n    public static ClusterOperateResult fail(String message) {\n        return new ClusterOperateResult(0, message);\n    }\n    \n    public boolean isSuccess() {\n        return status == 1;\n    }\n\n    public int getStatus() {\n        return status;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusterOperateResult [status=\" + status + \", message=\" + message + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/CommandResult.java",
    "content": "package com.sohu.cache.constant;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * ssh命令简单封装\r\n * @author leifu\r\n * @Date 2016年7月27日\r\n * @Time 下午3:06:42\r\n */\r\npublic class CommandResult {\r\n    private String command;\r\n    \r\n    private String result;\r\n    \r\n    private List<String> resultLines;\r\n    \r\n    public CommandResult(String command, String result) {\r\n        super();\r\n        this.command = command;\r\n        this.result = result;\r\n    }\r\n\r\n    public String getCommand() {\r\n        return command;\r\n    }\r\n\r\n    public void setCommand(String command) {\r\n        this.command = command;\r\n    }\r\n\r\n    public String getResult() {\r\n        return result;\r\n    }\r\n\r\n    public void setResult(String result) {\r\n        this.result = result;\r\n    }\r\n\r\n    public List<String> getResultLines() {\r\n        return resultLines;\r\n    }\r\n\r\n    public void setResultLines(List<String> resultLines) {\r\n        this.resultLines = resultLines;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"CommandResult [command=\" + command + \", result=\" + result + \", resultLines=\" + resultLines + \"]\";\r\n    }\r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/DataFormatCheckResult.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 数据格式检测\r\n * @author leifu\r\n * @Date 2016年7月4日\r\n * @Time 下午5:37:03\r\n */\r\npublic class DataFormatCheckResult {\r\n\r\n    private int status;\r\n\r\n    private String message;\r\n    \r\n    private final static int SUCCESS = 1;\r\n    private final static int FAIL = 0;\r\n\r\n    public DataFormatCheckResult(int status, String message) {\r\n        this.status = status;\r\n        this.message = message;\r\n    }\r\n\r\n    public boolean isSuccess() {\r\n        if (status == SUCCESS) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static DataFormatCheckResult success(String message) {\r\n        return new DataFormatCheckResult(SUCCESS, message);\r\n    }\r\n\r\n    public static DataFormatCheckResult fail(String message) {\r\n        return new DataFormatCheckResult(FAIL, message);\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n\r\n    public void setStatus(int status) {\r\n        this.status = status;\r\n    }\r\n\r\n    public String getMessage() {\r\n        return message;\r\n    }\r\n\r\n    public void setMessage(String message) {\r\n        this.message = message;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"DataFormatCheckResult [status=\" + status + \", message=\" + message + \"]\";\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/DiagnosticTypeEnum.java",
    "content": "package com.sohu.cache.constant;\n\nimport com.sohu.cache.util.StringUtil;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 17:11\n */\npublic enum DiagnosticTypeEnum {\n    SCAN_KEY(0, \"scan\", \"扫描键\"),\n    BIG_KEY(1, \"memoryUsed\", \"键内存诊断\"),\n    IDLE_KEY(2, \"idlekey\", \"空闲键扫描\"),\n    HOT_KEY(3, \"hotkey\", \"热点键诊断\"),\n    DEL_KEY(4, \"deleteKey\", \"删除键\"),\n    SLOT_ANALYSIS(5, \"slotAnalysis\", \"集群槽分析\"),\n    SCAN_CLEAN(6, \"scanClean\", \"数据分析清理\");\n\n    int type;\n    String desc;\n    String more;\n\n    DiagnosticTypeEnum(int type, String desc, String more) {\n        this.type = type;\n        this.desc = desc;\n        this.more = more;\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public String getMore() {\n        return more;\n    }\n\n    public static int getDescKey(String desc) {\n        for (DiagnosticTypeEnum diagnosticTypeEnum : DiagnosticTypeEnum.values()) {\n            if (!StringUtil.isBlank(desc) && diagnosticTypeEnum.getDesc().equals(desc)) {\n                return diagnosticTypeEnum.getType();\n            }\n        }\n        return -1;\n    }\n\n    public static String getKeyDesc(int type) {\n        for (DiagnosticTypeEnum diagnosticTypeEnum : DiagnosticTypeEnum.values()) {\n            if (type == diagnosticTypeEnum.getType()) {\n                return diagnosticTypeEnum.getDesc();\n            }\n        }\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/EmptyObjectConstant.java",
    "content": "package com.sohu.cache.constant;\n\n/**\n * 空对象常量\n * @author leifu\n * @Date 2016-1-26\n * @Time 下午9:27:23\n */\npublic class EmptyObjectConstant {\n\n\tpublic final static String EMPTY_STRING = \"\";\n\tpublic static String[] EMPTY_STRING_ARRAY = new String[0];\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/ErrorMessageEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 系统错误提示\r\n * @author leifu\r\n * @Date 2016-6-25\r\n * @Time 下午2:50:07\r\n */\r\npublic enum ErrorMessageEnum {\r\n\r\n    INNER_ERROR_MSG(1, \"系统异常，请观察系统日志!\"),\r\n    PARAM_ERROR_MSG(2, \"参数错误!\"),\r\n    REPEAT_INSERT_MSG(3, \"重复插入!\"),\r\n    VERSION_NOT_EXIST(4, \"版本不存在!\"),\r\n    ALERT_CONFIG_CONSTRAINT_ERROR_MSG(5, \"该报警项已存在!\")\r\n    ;\r\n    \r\n    private int id;\r\n    \r\n    private String message;\r\n\r\n    private ErrorMessageEnum(int id, String message) {\r\n        this.id = id;\r\n        this.message = message;\r\n    }\r\n\r\n    public int getId() {\r\n        return id;\r\n    }\r\n\r\n    public String getMessage() {\r\n        return message;\r\n    }\r\n    \r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/HorizontalResult.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 水平扩容结果标识\r\n * @author leifu\r\n * @Date 2016年12月4日\r\n * @Time 下午1:30:19\r\n */\r\npublic class HorizontalResult {\r\n\r\n    private int status;\r\n\r\n    private String message;\r\n\r\n    public HorizontalResult(int status, String message) {\r\n        this.status = status;\r\n        this.message = message;\r\n    }\r\n\r\n    public static HorizontalResult checkSuccess() {\r\n        return new HorizontalResult(1, \"所有检查都成功，可以开始水平扩容了!\");\r\n    }\r\n    \r\n    public static HorizontalResult scaleSuccess() {\r\n        return new HorizontalResult(1, \"水平扩容已经成功开始!\");\r\n    }\r\n\r\n    public static HorizontalResult fail(String message) {\r\n        return new HorizontalResult(0, message);\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n\r\n    public void setStatus(int status) {\r\n        this.status = status;\r\n    }\r\n\r\n    public String getMessage() {\r\n        return message;\r\n    }\r\n\r\n    public void setMessage(String message) {\r\n        this.message = message;\r\n    }\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"HorizontalResult [status=\" + status + \", message=\" + message + \"]\";\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/ImportAppResult.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 导入结果\r\n * \r\n * @author leifu\r\n * @Date 2016-4-16\r\n * @Time 下午3:41:37\r\n */\r\npublic class ImportAppResult {\r\n\r\n    private int status;\r\n\r\n    private String message;\r\n\r\n    public ImportAppResult(int status, String message) {\r\n        this.status = status;\r\n        this.message = message;\r\n    }\r\n\r\n    public static ImportAppResult success() {\r\n        return new ImportAppResult(1, \"所有检查都成功，可以添加啦!\");\r\n    }\r\n\r\n    public static ImportAppResult fail(String message) {\r\n        return new ImportAppResult(0, message);\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n\r\n    public void setStatus(int status) {\r\n        this.status = status;\r\n    }\r\n\r\n    public String getMessage() {\r\n        return message;\r\n    }\r\n\r\n    public void setMessage(String message) {\r\n        this.message = message;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"ImportAppResult [status=\" + status + \", message=\" + message + \"]\";\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/InstanceStatusEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 实例状态\r\n * @author leifu\r\n * @Date 2014年11月26日\r\n * @Time 下午5:05:35\r\n */\r\npublic enum InstanceStatusEnum {\r\n    ALLOCATE_STATUS(-1, \"待分配\"),\r\n    ERROR_STATUS(0, \"心跳停止\"),\r\n    GOOD_STATUS(1, \"运行中\"),\r\n    OFFLINE_STATUS(2, \"已下线\"),\r\n    FORGET_STATUS(3, \"永久下线\");\r\n\r\n    private int status;\r\n    \r\n    private String info;\r\n    \r\n    private static Map<Integer, InstanceStatusEnum> MAP = new HashMap<Integer, InstanceStatusEnum>();\r\n    static {\r\n        for(InstanceStatusEnum instanceStatusEnum : InstanceStatusEnum.values()) {\r\n            MAP.put(instanceStatusEnum.getStatus(), instanceStatusEnum);\r\n        }\r\n    }\r\n    \r\n    public static InstanceStatusEnum getByStatus(int status) {\r\n        return MAP.get(status);\r\n    }\r\n    \r\n    private InstanceStatusEnum(int status, String info) {\r\n        this.status = status;\r\n        this.info = info;\r\n    }\r\n\r\n    public int getStatus() {\r\n        return status;\r\n    }\r\n\r\n    public String getInfo() {\r\n        return info;\r\n    }\r\n    \r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/MachineConstant.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * User: lingguo\r\n * Date: 14-6-12\r\n * Time: 上午11:42\r\n */\r\npublic enum MachineConstant {\r\n    Ip(\"ip\"),\r\n    Load(\"load\"),\r\n    Traffic(\"traffic\"),\r\n    CpuUsage(\"cpuUsage\"),\r\n    MemoryUsageRatio(\"memoryUsageRatio\"),\r\n    MemoryFree(\"memoryFree\"),\r\n    MemoryTotal(\"memoryTotal\"),\r\n    DiskUsage(\"diskUsageMap\");\r\n\r\n    private String value;\r\n\r\n    MachineConstant(String value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public String getValue() {\r\n        return this.value;\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/MachineInfoEnum.java",
    "content": "package com.sohu.cache.constant;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 机器信息枚举\n *\n * @author leifu\n * @Date 2016年7月9日\n * @Time 下午4:38:15\n */\npublic class MachineInfoEnum {\n\n    /**\n     * 是否为可用\n     *\n     * @author leifu\n     * @Date 2016年7月9日\n     * @Time 下午4:43:49\n     */\n    public static enum AvailableEnum {\n        YES(1),\n        NO(0);\n\n        private int value;\n\n        private AvailableEnum(int value) {\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n\n    }\n\n    /**\n     * 机器类型\n     *\n     * @author leifu\n     * @Date 2016年7月9日\n     * @Time 下午4:42:58\n     */\n    public static enum TypeEnum {\n        REDIS_NODE(0, \"Redis类型\"),\n        REDIS_MIGRATE_TOOL(2, \"Redis迁移工具类型\"),\n        SENTINEL_NODE(3, \"Sentinel类型\"),\n        TWEMPROXY_NODE(4, \"Twemproxy类型\"),\n        PIKA_NODE(5, \"Pika类型\");\n\n        private int type;\n\n        private String info;\n\n        private static Map<Integer, TypeEnum> MAP = new HashMap<Integer, TypeEnum>();\n\n        static {\n            for (TypeEnum typeEnum : TypeEnum.values()) {\n                MAP.put(typeEnum.getType(), typeEnum);\n            }\n        }\n\n        public static TypeEnum getByType(int type) {\n            return MAP.get(type);\n        }\n\n        private TypeEnum(int type, String info) {\n            this.type = type;\n            this.info = info;\n        }\n\n        public int getType() {\n            return type;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n\n\n    }\n\n    /**\n     * 机器发现操作系统类型\n     *\n     * @author zengyizhao\n     * @Date 2022年9月5日\n     * @Time 下午4:26:58\n     */\n    public static enum DisTypeEnum {\n        CENTOS(0, \"centos\"),\n        UBUNTU(1, \"ubuntu\");\n\n        private int type;\n\n        private String info;\n\n        private static Map<Integer, DisTypeEnum> MAP = new HashMap<>();\n\n        static {\n            for (DisTypeEnum typeEnum : DisTypeEnum.values()) {\n                MAP.put(typeEnum.getType(), typeEnum);\n            }\n        }\n\n        public static DisTypeEnum getByType(int type) {\n            return MAP.get(type);\n        }\n\n        private DisTypeEnum(int type, String info) {\n            this.type = type;\n            this.info = info;\n        }\n\n        public int getType() {\n            return type;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n\n\n    }\n\n\n    public static enum MachineEnum {\n        HOST(\"host\"),\n        CONTAINER(\"container\");\n\n        private String value;\n\n        MachineEnum(String value) {\n            this.value = value;\n        }\n\n        public String getValue() {\n            return value;\n        }\n    }\n\n    public static enum MachineTypeEnum {\n        HOST(1),\n        CONTAINER(2),\n        ALL(3);\n\n        private int value;\n\n        MachineTypeEnum(int value) {\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/OperateResult.java",
    "content": "package com.sohu.cache.constant;\n\n/**\n * Created by yijunzhang on 2018-12-26\n */\npublic class OperateResult {\n    private boolean isSuccess;\n\n    private String message;\n\n    private OperateResult(boolean isSuccess, String message) {\n        this.isSuccess = isSuccess;\n        this.message = message;\n    }\n\n    public static OperateResult success() {\n        return new OperateResult(true, \"\");\n    }\n\n    public static OperateResult fail(String message) {\n        return new OperateResult(false, message);\n    }\n\n    public boolean isSuccess() {\n        return isSuccess;\n    }\n\n    public void setSuccess(boolean isSuccess) {\n        this.isSuccess = isSuccess;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    @Override\n    public String toString() {\n        return \"OperateResult [isSuccess=\" + isSuccess + \", message=\" + message + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/PipelineEnum.java",
    "content": "package com.sohu.cache.constant;\n\n/**\n * @author leifu\n * @Date 2017年7月13日\n * @Time 下午3:26:03\n */\npublic enum PipelineEnum {\n    NO(0), YES(1);\n    \n    private int value;\n\n    private PipelineEnum(int value) {\n        this.value = value;\n    }\n    \n    public static PipelineEnum getPipelineEnum(int pipelineInt) {\n        for (PipelineEnum pipelineEnum : PipelineEnum.values()) {\n            if (pipelineInt == pipelineEnum.value) {\n                return pipelineEnum;\n            }\n        }\n        return null;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/RedisConfigTemplateChangeEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 配置模板改变行为枚举\r\n * \r\n * @author leifu\r\n * @Date 2016年7月27日\r\n * @Time 下午3:51:13\r\n */\r\npublic enum RedisConfigTemplateChangeEnum {\r\n\r\n    UPDATE(1, \"更新\"),\r\n    ADD(2, \"添加\"),\r\n    DELETE(3, \"删除\");\r\n\r\n    private int index;\r\n\r\n    private String info;\r\n\r\n    private RedisConfigTemplateChangeEnum(int index, String info) {\r\n        this.index = index;\r\n        this.info = info;\r\n    }\r\n\r\n    public int getIndex() {\r\n        return index;\r\n    }\r\n\r\n    public String getInfo() {\r\n        return info;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/RedisConstant.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * redis 相关常量\r\n * Created by yijunzhang on 14-6-10.\r\n */\r\npublic enum RedisConstant {\r\n    Stats(\"Stats\"),\r\n    Keyspace(\"Keyspace\"),\r\n    Commandstats(\"Commandstats\"),\r\n    Replication(\"Replication\"),\r\n    Clients(\"Clients\"),\r\n    CPU(\"CPU\"),\r\n    Memory(\"Memory\"),\r\n    Server(\"Server\"),\r\n    Persistence(\"Persistence\"),\r\n    CollectTime(\"CollectTime\"),\r\n    DIFF(\"diff\");\r\n\r\n    private String value;\r\n\r\n    RedisConstant(String value) {\r\n        this.value = value;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return this.value;\r\n    }\r\n\r\n    public static RedisConstant value(String input) {\r\n        RedisConstant[] constants = RedisConstant.values();\r\n        for (RedisConstant constant : constants) {\r\n            if (constant.value.equals(input)) {\r\n                return constant;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n\r\n    public String getValue() {\r\n        return value;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/RedisExcludeCommand.java",
    "content": "package com.sohu.cache.constant;\r\n\r\nimport java.util.HashSet;\r\nimport java.util.Set;\r\n\r\n/**\r\n * Created by yijunzhang on 14-6-25.\r\n */\r\npublic class RedisExcludeCommand {\r\n\r\n    private static Set<String> excludeCommands = new HashSet<String>();\r\n\r\n    static {\r\n        //排除这些redis命令\r\n        excludeCommands.add(\"ping\");\r\n        excludeCommands.add(\"cluster\");\r\n        excludeCommands.add(\"config\");\r\n        excludeCommands.add(\"ttl\");\r\n        excludeCommands.add(\"client\");\r\n        excludeCommands.add(\"bgrewriteaof\");\r\n        excludeCommands.add(\"bgsave\");\r\n        excludeCommands.add(\"dbsize\");\r\n        excludeCommands.add(\"debug\");\r\n        excludeCommands.add(\"flushall\");\r\n        excludeCommands.add(\"flushdb\");\r\n        excludeCommands.add(\"flush\");\r\n        excludeCommands.add(\"info\");\r\n        excludeCommands.add(\"lastsave\");\r\n        excludeCommands.add(\"monitor\");\r\n        excludeCommands.add(\"psync\");\r\n        excludeCommands.add(\"save\");\r\n        excludeCommands.add(\"shutdown\");\r\n        excludeCommands.add(\"slaveof\");\r\n        excludeCommands.add(\"slowlog\");\r\n        excludeCommands.add(\"sync\");\r\n        excludeCommands.add(\"time\");\r\n        excludeCommands.add(\"replconf\");\r\n        excludeCommands.add(\"asking\");\r\n        excludeCommands.add(\"restore-asking\");\r\n        excludeCommands.add(\"restore\");\r\n        excludeCommands.add(\"select\");\r\n    }\r\n\r\n    public static boolean isExcludeCommand(String command) {\r\n        return excludeCommands.contains(command);\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/RedisMigrateToolConstant.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * Redis-Migrate-Tool常量\r\n * @author leifu\r\n * @Date 2016-6-10\r\n * @Time 上午9:23:30\r\n */\r\npublic enum RedisMigrateToolConstant {\r\n    Stats(\"Stats\"),\r\n    Keyspace(\"Group\"),\r\n    Clients(\"Clients\"),\r\n    Memory(\"Memory\"),\r\n    Server(\"Server\");\r\n\r\n    private String value;\r\n\r\n    RedisMigrateToolConstant(String value) {\r\n        this.value = value;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return this.value;\r\n    }\r\n\r\n    public static RedisMigrateToolConstant value(String input) {\r\n        RedisMigrateToolConstant[] constants = RedisMigrateToolConstant.values();\r\n        for (RedisMigrateToolConstant constant : constants) {\r\n            if (constant.value.equals(input)) {\r\n                return constant;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n\r\n    public String getValue() {\r\n        return value;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/RedisShakeEnum.java",
    "content": "package com.sohu.cache.constant;\n\n/**\n * Created by rucao on 2019/10/30\n */\npublic enum RedisShakeEnum {\n    LOG_WAITING_SOURCE_RDB(\"waiting source rdb\", \"等待源端save rdb完毕\"),\n    LOG_SYNCING(\"total\", \"全量同步阶段，显示百分比\"),\n    LOG_SYNC_RDB_DONE(\"sync rdb done\", \"全量同步完成\"),\n    LOG_FORWARD_COMMANDS(\"forwardCommands\",\"当前dbSyncer进入增量同步\"),\n    LOG_ERROR(\"error\",\"迁移任务发生异常，程序中断，详情请查看日志\");\n\n\n    private String keyword;\n    private String description;\n\n    RedisShakeEnum(String keyword, String description) {\n        this.keyword = keyword;\n        this.description = description;\n    }\n\n    public String getKeyword() {\n        return keyword;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/ReshardStatusEnum.java",
    "content": "package com.sohu.cache.constant;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Reshard状态\n */\npublic enum ReshardStatusEnum {\n    RUNNING(0, \"运行中\"), \n    FINISH(1, \"完成\"),\n    ERROR(2, \"出错\");\n\n    private int value;\n    private String info;\n\n    private final static Map<Integer, ReshardStatusEnum> MAP = new HashMap<Integer, ReshardStatusEnum>();\n    static {\n        for (ReshardStatusEnum reshardStatusEnum : ReshardStatusEnum.values()) {\n            MAP.put(reshardStatusEnum.getValue(), reshardStatusEnum);\n        }\n    }\n\n    public static ReshardStatusEnum getReshardStatusEnum(int value) {\n        return MAP.get(value);\n    }\n\n    private ReshardStatusEnum(int value, String info) {\n        this.value = value;\n        this.info = info;\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/SymbolConstant.java",
    "content": "package com.sohu.cache.constant;\r\n\r\n/**\r\n * 符号常量\r\n * \r\n * @author leifu\r\n * @Date 2016-1-26\r\n * @Time 下午9:25:32\r\n */\r\npublic class SymbolConstant {\r\n\r\n    /** , */\r\n    public final static String COMMA = \",\";\r\n\r\n    public final static String SLASH = \"/\";\r\n    public final static String BACK_SLASH = \"\\\\\";\r\n    /** : */\r\n    public final static String COLON = \":\";\r\n    public final static String SPACE = \" \";\r\n    public final static String DOT = \".\";\r\n    public final static String ENTER = System.lineSeparator();\r\n\r\n\r\n    /** ; */\r\n    public final static String SEMICOLON = \";\";\r\n\r\n    /** [ */\r\n    public final static String SQUARE_BRACKETS_LEFT = \"[\";\r\n    /** ] */\r\n    public final static String SQUARE_BRACKETS_RIGHT = \"]\";\r\n\r\n    public final static String CURLY_BRACKETS_LEFT = \"{\";\r\n    public final static String CURLY_BRACKETS_RIGHT = \"}\";\r\n    /** ( */\r\n    public final static String PARENTHESES_BRACKETS_LEFT = \"(\";\r\n    /** ) */\r\n    public final static String PARENTHESES_BRACKETS_RIGHT = \")\";\r\n\r\n    public final static String QUESTION_SIGN = \"?\";\r\n    public final static String AND_SIGN = \"&\";\r\n    /** # */\r\n    public final static String POUND = \"#\";\r\n    /** * */\r\n    public final static String ASTERISK = \"*\";\r\n\r\n    /** $ */\r\n    public final static String DOLLAR_SIGN = \"$\";\r\n\r\n    public final static String ELLIPSIS_THREE = \"...\";\r\n    public final static String ELLIPSIS_SIX = \"......\";\r\n\r\n    /** = */\r\n    public final static String EQUAL_SIGN = \"=\";\r\n    /** - */\r\n    public final static String MINUS_SIGN = \"-\";\r\n    public final static String PLUS_SIGN = \"+\";\r\n    public final static String MULTIPLICATION_SIGN = \"*\";\r\n    public final static String PERCENT = \"%\";\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/TimeDimensionalityEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\n/**\r\n * 时间维度枚举\r\n * \r\n * @author leifu\r\n * @Date 2016年8月1日\r\n * @Time 下午3:50:38\r\n */\r\npublic enum TimeDimensionalityEnum {\r\n    MINUTE(0, \"以分钟为维度\"),\r\n    HOUR(1, \"以小时为维度\");\r\n\r\n    private int index;\r\n\r\n    private String info;\r\n\r\n    private static final Map<Integer, TimeDimensionalityEnum> MAP = new HashMap<Integer, TimeDimensionalityEnum>();\r\n    static {\r\n        for (TimeDimensionalityEnum timeDimensionalityEnum : TimeDimensionalityEnum.values()) {\r\n            MAP.put(timeDimensionalityEnum.getIndex(), timeDimensionalityEnum);\r\n        }\r\n    }\r\n\r\n    private TimeDimensionalityEnum(int index, String info) {\r\n        this.index = index;\r\n        this.info = info;\r\n    }\r\n\r\n    public int getIndex() {\r\n        return index;\r\n    }\r\n\r\n    public String getInfo() {\r\n        return info;\r\n    }\r\n\r\n    public static TimeDimensionalityEnum getTimeDimensionalityEnumByIndex(String index) {\r\n        return MAP.get(NumberUtils.toInt(index));\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/UserLoginTypeEnum.java",
    "content": "package com.sohu.cache.constant;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 登录类型\r\n * \r\n * @author leifu\r\n * @Date 2016年6月15日\r\n * @Time 下午12:03:05\r\n */\r\npublic enum UserLoginTypeEnum {\r\n\r\n    SESSION(1, \"session\"),\r\n    COOKIE(2, \"cookie\");\r\n\r\n    private int type;\r\n\r\n    private String desc;\r\n    \r\n    private final static Map<Integer, UserLoginTypeEnum> MAP = new HashMap<Integer, UserLoginTypeEnum>();\r\n    static {\r\n        for (UserLoginTypeEnum userLoginTypeEnum : UserLoginTypeEnum.values()) {\r\n            MAP.put(userLoginTypeEnum.getType(), userLoginTypeEnum);\r\n        }\r\n    }\r\n\r\n    public static UserLoginTypeEnum getLoginTypeEnum(int type) {\r\n        return MAP.get(type);\r\n    }\r\n    \r\n    private UserLoginTypeEnum(int type, String desc) {\r\n        this.type = type;\r\n        this.desc = desc;\r\n    }\r\n\r\n    public int getType() {\r\n        return type;\r\n    }\r\n\r\n    public String getDesc() {\r\n        return desc;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/constant/ValueSizeDistriEnum.java",
    "content": "package com.sohu.cache.constant;\n\nimport com.sohu.cache.util.NumberUtil;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 值分布\n *\n * @author leifu\n */\npublic enum ValueSizeDistriEnum {\n    // 单位字节\n    BETWEEN_MIN_TO_0_BYTE(\"-2147483648_0\", \"wrong\", 0),\n    BETWEEN_0_TO_50_BYTE(\"0_50\", \"0-0.05k\", 1),\n    BETWEEN_50_TO_100_BYTE(\"50_100\", \"0.05k-0.1k\", 2),\n    BETWEEN_100_TO_200_BYTE(\"100_200\", \"0.1k-0.2k\", 3),\n    BETWEEN_200_TO_500_BYTE(\"200_500\", \"0.2k-0.5k\", 4),\n    BETWEEN_500_TO_1024_BYTE(\"500_1024\", \"0.5k-1k\", 5),\n    BETWEEN_1024_TO_2048_BYTE(\"1024_2048\", \"1-2k\", 6),\n    BETWEEN_2048_TO_5120_BYTE(\"2048_5120\", \"2-5k\", 7),\n    BETWEEN_5120_TO_10240_BYTE(\"5120_10240\", \"5-10k\", 8),\n    BETWEEN_10240_TO_20480_BYTE(\"10240_20480\", \"10-20k\", 9),\n    BETWEEN_20480_TO_51200_BYTE(\"20480_51200\", \"20-50k\", 10),\n    BETWEEN_51200_TO_102400_BYTE(\"51200_102400\", \"50-100k\", 11),\n    BETWEEN_102400_TO_204800_BYTE(\"102400_204800\", \"100-200k\", 12),\n    BETWEEN_204800_TO_512000_BYTE(\"204800_512000\", \"200-500k\", 13),\n    BETWEEN_512000_TO_1024000_BYTE(\"512000_1024000\", \"500k-1M\", 15),\n    BETWEEN_1024000_TO_2048000_BYTE(\"1024000_2048000\", \"1M-2M\", 16),\n    BETWEEN_2048000_TO_5120000_BYTE(\"2048000_5120000\", \"2M-5M\", 17),\n    BETWEEN_5120000_TO_10240000_BYTE(\"5120000_10240000\", \"5M-10M\", 18),\n    BETWEEN_10240000_TO_ABOVE_BYTE(\"10240000_2147483647\", \"above 10M\", 19),\n    //兼容老的\n    BETWEEN_512000_TO_ABOVE_BYTE(\"512000_2147483647\", \"above 500k\", 14);\n\n    private String value;\n    private String info;\n    private int type;\n\n    private ValueSizeDistriEnum(String value, String info, int type) {\n        this.value = value;\n        this.info = info;\n        this.type = type;\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public final static Map<String, ValueSizeDistriEnum> VALUE_MAP = new HashMap<String, ValueSizeDistriEnum>();\n    static {\n        for (ValueSizeDistriEnum enumObject : ValueSizeDistriEnum.values()) {\n            VALUE_MAP.put(enumObject.getValue(), enumObject);\n        }\n    }\n\n    public static ValueSizeDistriEnum getByValue(String targetValue){\n        return VALUE_MAP.get(targetValue);\n    }\n\n    public final static Map<Integer, ValueSizeDistriEnum> TYPE_MAP = new HashMap<Integer, ValueSizeDistriEnum>();\n    static {\n        for (ValueSizeDistriEnum enumObject : ValueSizeDistriEnum.values()) {\n            TYPE_MAP.put(enumObject.getType(), enumObject);\n        }\n    }\n\n    public static ValueSizeDistriEnum getByType(int targetType){\n        return TYPE_MAP.get(targetType);\n    }\n\n    /**\n     * 查看length在哪个区间\n     *\n     * @return\n     */\n    public static ValueSizeDistriEnum getRightSizeBetween(int size) {\n        ValueSizeDistriEnum[] enumArr = ValueSizeDistriEnum.values();\n        for (ValueSizeDistriEnum enumObject : enumArr) {\n            if (isInSize(enumObject, size)) {\n                return enumObject;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 确定length在指定区间\n     *\n     * @param enumObject\n     * @param size\n     * @return\n     */\n    private static boolean isInSize(ValueSizeDistriEnum enumObject, int size) {\n        String value = enumObject.getValue();\n        int index = value.indexOf(\"_\");\n        int start = NumberUtil.toInt(value.substring(0, index));\n        int end = NumberUtil.toInt(value.substring(index + 1));\n        if (size >= start && size < end) {\n            return true;\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppAlertRecordDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppAlertRecord;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/3 13:38\n * @Description: 报警记录\n */\npublic interface AppAlertRecordDao {\n\n    /**\n     * 保存报警信息\n     *\n     * @param appAlertRecord\n     * @return\n     */\n    public int save(AppAlertRecord appAlertRecord);\n\n    /**\n     * 批量保存报警信息\n     *\n     * @param appAlertRecordList\n     * @return\n     */\n    public int batchSave(List<AppAlertRecord> appAlertRecordList);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppAuditDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.AppAudit;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * Created by yijunzhang on 14-10-20.\r\n */\r\npublic interface AppAuditDao {\r\n\r\n    /**\r\n     * 新增审核记录\r\n     *\r\n     * @param appAudit\r\n     */\r\n    public void insertAppAudit(AppAudit appAudit);\r\n\r\n    /**\r\n     * 查询所有等待审批的记录\r\n     */\r\n    public List<AppAudit> selectWaitAppAudits(@Param(\"status\") Integer status, @Param(\"type\") Integer type, @Param(\"auditId\") Long auditId, @Param(\"userId\") Long userId, @Param(\"operateId\") Long operateId);\r\n\r\n    /**\r\n     * 按id查询\r\n     *\r\n     * @param id\r\n     * @return\r\n     */\r\n    public AppAudit getAppAudit(@Param(\"id\") long id);\r\n\r\n    /**\r\n     * 更新审核状态\r\n     */\r\n    void updateAppAudit(@Param(\"id\") long id, @Param(\"status\") int status);\r\n\r\n    void updateAppAuditUser(@Param(\"id\") long id, @Param(\"status\") int status, @Param(\"operateId\") Long operateId);\r\n\r\n    void updateAppAuditOperateUser(@Param(\"id\") long id, @Param(\"operateId\") Long operateId);\r\n\r\n    /**\r\n     * 更新驳回理由\r\n     */\r\n    public void updateRefuseReason(@Param(\"id\") long id, @Param(\"refuseReason\") String refuseReason);\r\n\r\n    /**\r\n     * 通过appId获取所有审批记录\r\n     */\r\n    public List<AppAudit> getAppAuditByAppId(@Param(\"appId\") Long appId);\r\n\r\n    /**\r\n     * 通过appId获取所有审批记录\r\n     */\r\n    public List<AppAudit> getAppAuditByCondition(@Param(\"appId\") Long appId, @Param(\"type\") Integer type);\r\n\r\n    /**\r\n     * 通过appId，type, 和时间范围获取所有审批记录\r\n     */\r\n    public List<AppAudit> getAppAuditByTypeAndTimeRange(@Param(\"appId\") Long appId, @Param(\"type\") Integer type, @Param(\"startTime\") Date startTime, @Param(\"endTime\") Date endTime);\r\n\r\n    /**\r\n     * @param id\r\n     * @param taskId\r\n     */\r\n    public void updateTaskId(@Param(\"id\") long id, @Param(\"taskId\") long taskId);\r\n\r\n    List<Map<String, Object>> getStatisticGroupByStatus(@Param(\"userId\") Long userId, @Param(\"operateId\") Long operateId, @Param(\"startTime\") Date startTime, @Param(\"endTime\") Date endTime);\r\n\r\n    List<Map<String, Object>> getStatisticGroupByType(@Param(\"userId\") Long userId, @Param(\"operateId\") Long operateId, @Param(\"startTime\") Date startTime, @Param(\"endTime\") Date endTime);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppAuditLogDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.AppAuditLog;\r\n\r\nimport java.util.List;\r\n\r\n\r\n/**\r\n * 应用审批日志记录\r\n * \r\n * @author leifu\r\n * @Time 2014年6月5日\r\n */\r\npublic interface AppAuditLogDao {\r\n\r\n    public int save(AppAuditLog appAuditLog);\r\n\r\n    /**\r\n     * 根据类型type(参考AppAuditLogTypeEnum)获取审批日志\r\n     * @param appAuditId\r\n     * @param type\r\n     * @return\r\n     */\r\n    public List<AppAuditLog> getAuditByType(@Param(\"appAuditId\") Long appAuditId, @Param(\"type\") int type);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppBizDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppBiz;\nimport com.sohu.cache.entity.AppUser;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 业务组管理dao\n * @author zengyizhao\n * @Time 2023年1月16日\n */\npublic interface AppBizDao {\n\n\tpublic AppBiz get(@Param(\"id\") Long id);\n\t\n\tpublic int save(AppBiz appBiz);\n\t\n\tpublic int update(AppBiz appBiz);\n\t\n\tpublic int delete(@Param(\"id\") Long id);\n\n    public List<AppBiz> getBizList();\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppCapacityMonitorDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppCapacityMonitor;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2022/10/10 9:38\n * @Description: 应用容量（自动扩容、缩容审计报表）\n */\npublic interface AppCapacityMonitorDao {\n\n    /**\n     * 保存应用容量监控信息\n     *\n     * @param appCapacityMonitor\n     * @return\n     */\n    public int save(AppCapacityMonitor appCapacityMonitor);\n\n    /**\n     * 批量保存应用容量监控信息\n     *\n     * @param appCapacityMonitorList\n     * @return\n     */\n    public int batchSave(List<AppCapacityMonitor> appCapacityMonitorList);\n\n    /**\n     * 更新应用容量监控信息\n     *\n     * @param appCapacityMonitor\n     * @return\n     */\n    public int update(AppCapacityMonitor appCapacityMonitor);\n\n    /**\n     * 更新应用缩容计划\n     *\n     * @param appCapacityMonitor\n     * @return\n     */\n    public int updateAppCapacityReduceSchedule(AppCapacityMonitor appCapacityMonitor);\n\n    public int updateAppUsedMemHistory(@Param(\"appId\") long appId, @Param(\"memUsedHistory\") long memUsedHistory);\n\n    /**\n     * 根据appId查询应用容量监控信息\n     * @param appId\n     * @return\n     */\n    public AppCapacityMonitor getAppCapacityMonitorByAppId(long appId);\n\n    /**\n     * 根据appId查询所有在线应用容量监控信息\n     * @return\n     */\n    public List<AppCapacityMonitor> getAppCapacityMonitorAll();\n\n\n    /**\n     * 根据appId查询应用容量监控信息\n     * @param appCapacityMonitor\n     * @return\n     */\n//    public List<AppCapacityMonitor> getAppCapacityMonitorByCondition(AppCapacityMonitor appCapacityMonitor);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientCommandStatisticsDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppClientCommandStatistics;\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/16\n */\n@Repository\npublic interface AppClientCommandStatisticsDao {\n    /**\n     * 批量保存\n     *\n     * @param appClientCommandStatisticsList\n     * @return\n     */\n    int batchSave(List<AppClientCommandStatistics> appClientCommandStatisticsList);\n\n    /**\n     * 获取应用指定时间内调用过的命令\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<String> getAppDistinctCommand(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 获取应用指定时间内d的客户端ip\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<String> getAppDistinctClients(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 获取某个应用一段时间内某个命令的统计信息列表\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @param command\n     * @return\n     */\n    List<Map<String, Object>> getAppCommandStatistics(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime, @Param(\"command\") String command, @Param(\"clientIp\") String clientIp);\n\n\n    /**\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<AppClientStatisticGather> getAppClientCmdStat(@Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @param command\n     * @return\n     */\n    List<Map<String, Object>> getSumCmdStatByCmd(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime, @Param(\"command\") String command);\n\n    List<Map<String, Object>> getSumCmdStatByClient(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime, @Param(\"clientIp\") String clientIp);\n\n    int cleanCommandStatisticsBeforeCurrentMin(@Param(\"currentMin\") long currentMin);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientCostTimeStatDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.AppClientCostTimeStat;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 客户端耗时dao\r\n * \r\n * @author leifu\r\n * @Date 2015年1月20日\r\n * @Time 上午11:50:01\r\n */\r\npublic interface AppClientCostTimeStatDao {\r\n\r\n    /**\r\n     * \r\n     * @param appClientCostTimeStat\r\n     */\r\n    void save(AppClientCostTimeStat appClientCostTimeStat);\r\n\r\n    /**\r\n     * 查询应用一段时间内某个命令、某对客户端和实例的耗时统计信息\r\n     * @param appId\r\n     * @param command\r\n     * @param instanceId\r\n     * @param clientIp\r\n     * @param startTime\r\n     * @param endTime\r\n     * @return\r\n     */\r\n    List<AppClientCostTimeStat> getAppCommandClientToInstanceStat(@Param(\"appId\") Long appId,\r\n                                                                  @Param(\"command\") String command, @Param(\"instanceId\") long instanceId,\r\n                                                                  @Param(\"clientIp\") String clientIp, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\r\n\r\n    /**\r\n     * 批量更新\r\n     * @param appClientCostTimeStatList\r\n     * @return\r\n     */\r\n    int batchSave(List<AppClientCostTimeStat> appClientCostTimeStatList);\r\n\r\n    /**\r\n     * 获取最小id\r\n     * @return\r\n     */\r\n    long getTableMinimumId();\r\n\r\n    /**\r\n     * 按照collectTime获取最小id\r\n     * @param collectTime\r\n     * @return\r\n     */\r\n    long getMinimumIdByCollectTime(@Param(\"collectTime\") long collectTime);\r\n\r\n    /**\r\n     * 按照id区间删除\r\n     * @param startId\r\n     * @param endId\r\n     */\r\n    long deleteByIds(@Param(\"startId\") long startId, @Param(\"endId\") long endId);\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientCostTimeTotalStatDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.AppClientCostTimeTotalStat;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 基于应用全局耗时统计(uniquekey: app_id, command, collect_time)\r\n * @author leifu\r\n * @Date 2015年6月26日\r\n * @Time 下午4:24:24\r\n */\r\npublic interface AppClientCostTimeTotalStatDao {\r\n\r\n    /**\r\n     * 保存基于应用的耗时统计\r\n     * @param appClientCostTimeTotalStat\r\n     */\r\n    void save(AppClientCostTimeTotalStat appClientCostTimeTotalStat);\r\n\r\n    /**\r\n     * 获取应用指定时间内调用过的命令\r\n     * @param appId\r\n     * @param startTime\r\n     * @param endTime\r\n     * @return\r\n     */\r\n    List<String> getAppDistinctCommand(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime,\r\n                                       @Param(\"endTime\") long endTime);\r\n\r\n    /**\r\n     * 获取应用指定时间内某个命令的耗时统计\r\n     * @param appId\r\n     * @param command\r\n     * @param startTime\r\n     * @param endTime\r\n     * @return\r\n     */\r\n    List<AppClientCostTimeTotalStat> getAppClientCommandStat(@Param(\"appId\") Long appId,\r\n                                                             @Param(\"command\") String command, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\r\n\r\n    /**\r\n     * 批量保存\r\n     * @param appClientCostTimeTotalStatList\r\n     */\r\n    void batchSave(List<AppClientCostTimeTotalStat> appClientCostTimeTotalStatList);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientExceptionStatDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.AppClientExceptionStat;\r\nimport com.sohu.cache.entity.ClientInstanceException;\r\nimport com.sohu.cache.web.util.Page;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 客户端异常dao\r\n * \r\n * @author leifu\r\n * @Date 2015年1月20日\r\n * @Time 上午11:50:06\r\n */\r\npublic interface AppClientExceptionStatDao {\r\n\r\n    /**\r\n     * 保存上报异常\r\n     * \r\n     * @param appClientExceptionStat\r\n     */\r\n    void save(AppClientExceptionStat appClientExceptionStat);\r\n\r\n    /**\r\n     * 获取客户端异常列表\r\n     * \r\n     * @param appId 应用id\r\n     * @param startTime 开始收集时间\r\n     * @param endTime 结束收集时间\r\n     * @param type 异常类型(ClientExceptionType)\r\n     * @param clientIp 客户端ip\r\n     * @return\r\n     */\r\n    List<AppClientExceptionStat> getAppExceptionList(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime,\r\n                                                     @Param(\"endTime\") long endTime, @Param(\"type\") int type, @Param(\"clientIp\") String clientIp, @Param(\"page\") Page page);\r\n\r\n    /**\r\n     * 获取客户端异常个数\r\n     * \r\n     * @param appId 应用id\r\n     * @param startTime 开始收集时间\r\n     * @param endTime 结束收集时间\r\n     * @param type 异常类型(ClientExceptionType)\r\n     * @param clientIp 客户端ip\r\n     * @return\r\n     */\r\n    int getAppExceptionCount(@Param(\"appId\") Long appId, @Param(\"startTime\") long startTime,\r\n                             @Param(\"endTime\") long endTime, @Param(\"type\") int type, @Param(\"clientIp\") String clientIp);\r\n\r\n    /**\r\n     * 大于collectTime后实例的异常统计\r\n     * @param ip\r\n     * @param collectTime\r\n     * @return\r\n     */\r\n    List<ClientInstanceException> getInstanceExceptionStat(@Param(\"ip\") String ip, @Param(\"collectTime\") long collectTime);\r\n\r\n    /**\r\n     * 批量保存\r\n     * @param appClientExceptionStatList\r\n     * @return\r\n     */\r\n    int batchSave(List<AppClientExceptionStat> appClientExceptionStatList);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientExceptionStatisticsDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppClientExceptionStatistics;\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/16\n */\n@Repository\npublic interface AppClientExceptionStatisticsDao {\n    /**\n     * 批量保存\n     *\n     * @param appClientExceptionStatisticsList\n     * @return\n     */\n    int batchSave(List<AppClientExceptionStatistics> appClientExceptionStatisticsList);\n\n    /**\n     * 获取某个应用一段时间内的异常信息列表\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @param type\n     * @return\n     */\n    List<Map<String, Object>> getAppExceptionStatistics(@Param(\"appId\") Long appId, @Param(\"clientIp\") String clientIp, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime, @Param(\"type\") Integer type);\n\n    /**\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<Map<String, Object>> getDistinctClientNodeStatistics(@Param(\"appId\") Long appId, @Param(\"clientIp\") String clientIp, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime, @Param(\"type\") Integer type);\n\n    /**\n     * 获取应用指定时间内d的客户端ip\n     *\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<Map<String, String>> getAppDistinctClientConfig(@Param(\"appId\") Long appId, @Param(\"type\") Integer type, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    List<Map<String, String>> getAppClientConfigs(@Param(\"appId\") Long appId, @Param(\"type\") Integer type, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * @param clientIp\n     * @param startTime\n     * @param endTime\n     * @param node\n     * @return\n     */\n    List<String> getLatencyCommandsByNode(@Param(\"clientIp\") String clientIp, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime, @Param(\"node\") String node);\n\n    List<String> getLatencyCommandsByNodeV2(@Param(\"node\") String node, @Param(\"searchTime\") long searchTime);\n\n    /**\n     * @param appId\n     * @param searchTime\n     * @return\n     */\n    List<Map<String, Object>> getSumCmdExpStatGroupByNode(@Param(\"appId\") long appId, @Param(\"searchTime\") long searchTime);\n\n    /**\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<AppClientStatisticGather> getAppClientConnExpStat(@Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 获取指定日期连接异常个数\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    int getAppClientConnExpCount(@Param(\"appId\") long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<AppClientStatisticGather> getAppClientCmdExpStat(@Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 获取指定日期超时异常个数\n     * @param appId\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    int getAppClientCmdExpCount(@Param(\"appId\") long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientLatencyCommandDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppClientLatencyCommand;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/16\n */\n@Repository\npublic interface AppClientLatencyCommandDao {\n    int batchSave(List<AppClientLatencyCommand> appClientLatencyCommandList);\n\n    List<Map<String, Object>> getLatencyCommandByIds(@Param(\"ids\") List<Long> ids);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientReportDataSizeDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.AppClientDataSizeStat;\r\n\r\n/**\r\n * 客户端内收集数据map的尺寸查询\r\n * @author leifu\r\n * @Date 2015年7月13日\r\n * @Time 下午3:26:48\r\n */\r\npublic interface AppClientReportDataSizeDao {\r\n    \r\n    /**\r\n     * 保存-客户端收集map的尺寸信息\r\n     * @param appClientDataSizeStat\r\n     */\r\n    void save(AppClientDataSizeStat appClientDataSizeStat);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientStatisticGatherDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/29\n */\n@Repository\npublic interface AppClientStatisticGatherDao {\n    @Deprecated\n    int batchSave();\n\n    int batchSaveCmdStats(List<AppClientStatisticGather> list);\n    int batchAddCmdStats(List<AppClientStatisticGather> list);\n\n    int batchSaveConnExpStats(List<AppClientStatisticGather> list);\n    int batchAddConnExpStats(List<AppClientStatisticGather> list);\n\n    int batchSaveCmdExpStats(List<AppClientStatisticGather> list);\n    int batchAddCmdExpStats(List<AppClientStatisticGather> list);\n\n    int batchSaveMemFragRatio(List<AppClientStatisticGather> list);\n\n    int batchSaveSlowLogCount(List<AppClientStatisticGather> list);\n    int batchAddSlowLogCount(List<AppClientStatisticGather> list);\n\n    int batchSaveLatencyCount(List<AppClientStatisticGather> list);\n    int batchAddLatencyCount(List<AppClientStatisticGather> list);\n\n    int batchSaveAppStats(List<AppClientStatisticGather> list);\n\n    int batchAddAppServerCmdCount(List<AppClientStatisticGather> list);\n\n    int batchSaveConnClients(List<AppClientStatisticGather> list);\n\n    int batchSaveTopologyExam(List<AppClientStatisticGather> list);\n\n    List<Map<String, Object>> getAppClientStatisticByGatherTime(@Param(\"appId\") long appId, @Param(\"gatherTime\") String gatherTime);\n\n    List<AppClientStatisticGather> getTopologyExamFailedByGatherTime(@Param(\"gatherTime\") String gatherTime);\n\n    List<Map<String, Object>> getExpAppStatisticByGatherTime(@Param(\"gatherTime\") String gatherTime);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppClientVersionDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.AppClientVersion;\r\n\r\n/**\r\n * 客户端版本dao\r\n * @author leifu\r\n * @Date 2015年2月2日\r\n * @Time 上午10:36:20\r\n */\r\npublic interface AppClientVersionDao {\r\n\r\n    /**\r\n     * 保存或者更新客户端版本信息\r\n     * @param appClientVersion\r\n     */\r\n    void saveOrUpdateClientVersion(AppClientVersion appClientVersion);\r\n\r\n    /**\r\n     * 根据客户端ip获取所有的appId\r\n     * @param clientIp\r\n     * @return\r\n     */\r\n    List<AppClientVersion> getByClientIp(@Param(\"clientIp\") String clientIp);\r\n\r\n    /**\r\n     * 获取应用的所有客户端版本信息\r\n     * @param appId\r\n     * @return\r\n     */\r\n    List<AppClientVersion> getAppAllClientVersion(@Param(\"appId\") long appId);\r\n\r\n    /**\r\n     * 获取应用的客户端最大版本信息\r\n     * @param appId\r\n     * @return\r\n     */\r\n    String getAppMaxClientVersion(@Param(\"appId\") long appId);\r\n\r\n    /**\r\n     * 获取所有应用的最大客户端版本\r\n     * @return\r\n     */\r\n    List<Map<String, Object>> getAllMaxClientVersion();\r\n    /**\r\n     * 获取所有版本，暂时不分页\r\n     * @return\r\n     */\r\n    List<AppClientVersion> getAll(@Param(\"appId\") long appId);\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppDailyDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.AppDailyData;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 应用日报\r\n * \r\n * @author leifu\r\n * @Date 2017年1月19日\r\n * @Time 上午10:25:39\r\n */\r\npublic interface AppDailyDao {\r\n\r\n    void save(AppDailyData appDailyData);\r\n    \r\n    AppDailyData getAppDaily(@Param(\"appId\") long appId, @Param(\"date\") String date);\r\n\r\n    List<AppDailyData> getAppDailyList(@Param(\"appId\") long appId, @Param(\"startDate\") long startDate, @Param(\"endDate\") long endDate);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppCapacityStatisticsResult;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppMonitorStatisticsResult;\nimport com.sohu.cache.entity.AppSearch;\nimport com.sohu.cache.entity.AppStatisticsSearch;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 基于app的dao操作\n *\n * @author leifu\n * @Date 2014年5月15日\n * @Time 下午1:58:22\n */\npublic interface AppDao {\n    /**\n     * 通过appId获取对应的app\n     *\n     * @param appId\n     * @return\n     */\n    public AppDesc getAppDescById(@Param(\"appId\") long appId);\n\n    AppDesc getOnlineAppDescById(@Param(\"appId\") long appId);\n\n    /**\n     * 通过所有在线的应用\n     *\n     * @return\n     */\n    public List<AppDesc> getOnlineApps();\n\n    List<AppDesc> getOnlineAppsNonTest();\n\n    /**\n     * 通过所有在线的应用\n     *\n     * @return\n     */\n    public List<AppDesc> getAllApps();\n\n    /**\n     * 通过应用名获取对应app\n     *\n     * @param appName\n     * @return\n     */\n    public AppDesc getByAppName(@Param(\"appName\") String appName);\n\n    /**\n     * 保存app\n     *\n     * @param appDesc\n     * @return\n     */\n    public int save(AppDesc appDesc);\n\n    /**\n     * 更新app\n     *\n     * @param appDesc\n     * @return\n     */\n    public int update(AppDesc appDesc);\n\n    /**\n     * 更新appPwd\n     * @param appId\n     * @param appPwd\n     * @return\n     */\n    int updateAppPwd(@Param(\"appId\") long appId, @Param(\"pkey\") String appPwd);\n\n    /**\n     * 更新app,包含自定义密码\n     *\n     * @param appDesc\n     * @return\n     */\n    public int updateWithCustomPwd(AppDesc appDesc);\n\n\n    /**\n     * 删除app\n     *\n     * @param id\n     * @return\n     */\n    public int delete(@Param(\"id\") Long id);\n\n    /**\n     * 获取用户拥有的应用\n     *\n     * @param userId\n     * @return\n     */\n    public List<AppDesc> getAppDescList(@Param(\"userId\") long userId);\n\n    /**\n     * 获取应用拥有的应用个数\n     *\n     * @param userId\n     * @return\n     */\n    public int getUserAppCount(@Param(\"userId\") long userId);\n\n    /**\n     * 获取所有应用\n     *\n     * @param appSearch\n     * @return\n     */\n    public List<AppDesc> getAllAppDescList(AppSearch appSearch);\n\n    /**\n     * 获取应用个数(有效状态)\n     *\n     * @param appSearch\n     * @return\n     */\n    public int getAllAppCount(AppSearch appSearch);\n\n    /**\n     * <p>\n     * Description:获取应用个数\n     * </p>\n     * @param\n     * @return\n     */\n    public int getTotalAppCount();\n\n    /**\n     * 更新appKey\n     *\n     * @param appId\n     * @param appKey\n     */\n    public void updateAppKey(@Param(\"appId\") long appId, @Param(\"appKey\") String appKey);\n\n    /**\n     * 更新持久化类型\n     *\n     * @param appId\n     * @param persistenceType\n     */\n    public int updateAppPersistenceType(@Param(\"appId\") long appId, @Param(\"persistenceType\") int persistenceType);\n\n    /**\n     * 更新内存策略\n     *\n     * @param appId\n     * @param maxmemoryPolicy\n     */\n    public int updateAppMaxmemoryPolicy(@Param(\"appId\") long appId, @Param(\"maxmemoryPolicy\") int maxmemoryPolicy);\n\n    /**\n     * 获取app安装不同版本的数量\n     */\n    @Select(\"SELECT version_id,count(version_id) as num from app_desc where status=2 GROUP BY version_id\")\n    public List<Map<String,Integer>> getVersionStat();\n\n    public List<AppDesc> getAppDescByIds(@Param(\"appIds\") List<String> appIds);\n\n    public List<AppMonitorStatisticsResult> getMonitorStatistics(AppStatisticsSearch search);\n\n    public List<AppCapacityStatisticsResult> getCapacityStatistics(AppStatisticsSearch search);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppDataMigrateStatusDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Param;\n\nimport com.sohu.cache.entity.AppDataMigrateSearch;\nimport com.sohu.cache.entity.AppDataMigrateStatus;\n\n/**\n * 迁移状态Dao\n * \n * @author leifu\n * @Date 2016-6-9\n * @Time 下午5:25:53\n */\npublic interface AppDataMigrateStatusDao {\n\n    int save(AppDataMigrateStatus appDataMigrateStatus);\n\n    AppDataMigrateStatus get(@Param(\"id\") long id);\n\n    AppDataMigrateStatus getByMigrateId(@Param(\"migrateId\") long migrateId);\n\n    int updateStatus(@Param(\"id\") long id, @Param(\"status\") int status);\n\n    int getMigrateTaskCount(@Param(\"appDataMigrateSearch\") AppDataMigrateSearch appDataMigrateSearch);\n\n    List<AppDataMigrateStatus> search(@Param(\"appDataMigrateSearch\") AppDataMigrateSearch appDataMigrateSearch);\n\n    List<Long> getAllOnMigrateId();\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppImportDao.java",
    "content": "package com.sohu.cache.dao;\n\n\nimport com.sohu.cache.entity.AppImport;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2021/1/7 下午6:03\n */\npublic interface AppImportDao {\n    AppImport get(@Param(\"id\") Long id);\n\n    int save(AppImport appImport);\n\n    int update(AppImport appImport);\n\n    List<AppImport> getAppImports(@Param(\"status\") int status);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppInstanceClientRelationDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.AppInstanceClientRelation;\r\n\r\n/**\r\n * 应用下节点和客户端关系Dao\r\n * \r\n * @author leifu\r\n * @Date 2016年5月4日\r\n * @Time 上午9:19:44\r\n */\r\npublic interface AppInstanceClientRelationDao {\r\n\r\n    int save(AppInstanceClientRelation appInstanceClientRelation);\r\n\r\n    int batchSave(List<AppInstanceClientRelation> appInstanceClientRelationList);\r\n\r\n    int isExist(AppInstanceClientRelation appInstanceClientRelation);\r\n\r\n    List<AppInstanceClientRelation> getAppInstanceClientRelationList(@Param(\"appId\") Long appId, @Param(\"day\") Date day);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppStatsDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.*;\n\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by yijunzhang on 14-6-9.\n */\npublic interface AppStatsDao {\n\n    public static final int MINUTE_DIMENSIONALITY = 0;\n\n    public static final int HOUR_DIMENSIONALITY = 1;\n\n    /**\n     * 插入或更新AppStats分钟统计\n     */\n    public void mergeMinuteAppStats(AppStats appStats);\n\n    /**\n     * 插入或更新AppCommandStats分钟统计\n     */\n    public void mergeMinuteCommandStatus(AppCommandStats commandStats);\n\n    /**\n     * 插入或更新AppStats小时统计\n     */\n    public void mergeHourAppStats(AppStats appStats);\n\n    /**\n     * 插入或更新AppCommandStats小时统计\n     */\n    public void mergeHourCommandStatus(AppCommandStats commandStats);\n    \n    /**\n     * 按时间查询应用统计\n     *\n     * @param appId 应用id\n     * @param td    时间维度\n     * @return\n     */\n    public List<AppStats> getAppStatsList(@Param(\"appId\") long appId, @Param(\"td\") TimeDimensionality td);\n\n    /**\n     * 按照分钟查询应用统计\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public List<AppStats> getAppStatsByMinute(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 按照分钟查询应用历史最大使用内存\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public Long getUsedMemoryMaxByTimeBetween(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 按照分钟查询应用统计\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public AppStats getOneAppStatsByMinute(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n\n    List<AppClientStatisticGather> gatherAppsStats(@Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 按照小时查询应用统计\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public List<AppStats> getAppStatsByHour(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 按照小时查询应用统计\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public List<AppStats> getAppHourStatsByTime(@Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 按时间查询应用命令统计\n     *\n     * @param appId       应用id\n     * @param commandName 命令名称\n     * @param td          时间维度\n     * @return\n     */\n    public List<AppCommandStats> getAppCommandStatsList(@Param(\"appId\") long appId, @Param(\"commandName\") String commandName,\n                                                        @Param(\"td\") TimeDimensionality td);\n\n    /**\n     * 按应用命令统计\n     *\n     * @param appId       应用id\n     * @param td          时间维度\n     * @return\n     */\n    public List<AppCommandStats> getAppAllCommandStatsList(@Param(\"appId\") long appId,@Param(\"td\") TimeDimensionality td);\n\n    /**\n     * \n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @param commandName\n     * @return\n     */\n    public List<AppCommandStats> getAppCommandStatsListByMinuteWithCommand(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime, @Param(\"commandName\") String commandName);\n\n    /**\n     * \n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @param commandName\n     * @return\n     */\n    public List<AppCommandStats> getAppCommandStatsListByHourWithCommand(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime, @Param(\"commandName\") String commandName);\n\n    /**\n     * \n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public List<AppCommandStats> getAppAllCommandStatsListByMinute(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * \n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public List<AppCommandStats> getAppAllCommandStatsListByHour(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 查询一天中应用的命令执行次数的topN\n     *\n     * @param appId 应用id\n     * @param td    时间维度\n     * @return\n     */\n    public List<AppCommandStats> getTopAppCommandStatsList(@Param(\"appId\") long appId, @Param(\"td\") TimeDimensionality td, @Param(\"top\") int top);\n\n    /**\n     * 查询一段时间内，各个命令执行次数分布\n     *\n     * @param appId 应用id\n     * @param td    时间维度\n     * @return\n     */\n    public List<AppCommandStats> getTopAppCommandGroupSum(@Param(\"appId\") long appId, @Param(\"td\") TimeDimensionality td, @Param(\"top\") int top);\n    \n    /**\n     * 获取一定时间内命令峰值\n     *\n     * @param appId\n     * @param commandName\n     * @param td          时间维度\n     * @return\n     */\n    public AppCommandStats getCommandClimax(@Param(\"appId\") long appId, @Param(\"commandName\") String commandName, @Param(\"td\") TimeDimensionality td);\n    \n    public AppCommandStats getCommandClimaxCount(@Param(\"appId\") long appId, @Param(\"commandName\") String commandName, @Param(\"td\") TimeDimensionality td);\n    \n    public AppCommandStats getCommandClimaxCreateTime(@Param(\"appId\") long appId, @Param(\"commandName\") String commandName, @Param(\"commandCount\") long commandCount, @Param(\"td\") TimeDimensionality td);\n    \n\n    /**\n     * 获取应用命令调用次数分布\n     *\n     * @param appId\n     * @param td    时间维度\n     * @return\n     */\n    public List<AppCommandGroup> getAppCommandGroup(@Param(\"appId\") long appId, @Param(\"td\") TimeDimensionality td);\n\n\n    public Long getAppCommandCount(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n\n    /**\n     * 应用分钟统计\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    public Map<String, Object> getAppMinuteStat(@Param(\"appId\") long appId, @Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime);\n\n    /**\n     * 碎片率最高的应用\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    public List<AppTopMemFragRatio> getTopMemFragRatioApps(@Param(\"startTime\") long startTime,@Param(\"endTime\") long endTime);\n\n    List<AppClientStatisticGather> getMemFragRatios(@Param(\"startTime\") long startTime,@Param(\"endTime\") long endTime);\n\n    /**\n     * todo 待下线\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<AppClientStatisticGather> getExceptionCount(@Param(\"startTime\") long startTime,@Param(\"endTime\") long endTime);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppToUserDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport java.util.List;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.AppToUser;\r\n\r\n\r\n/**\r\n * 用户-应用对应关系\r\n * \r\n * @author leifu\r\n * @Time 2014年6月5日\r\n */\r\npublic interface AppToUserDao {\r\n    \r\n    List<AppToUser> getByUserId(@Param(\"userId\") Long userId);\r\n\r\n    Long save(AppToUser appToUser);\r\n\r\n    void deleteByAppId(@Param(\"appId\") Long appId);\r\n\r\n    List<AppToUser> getByAppId(@Param(\"appId\") Long appId);\r\n\r\n    void deleteAppToUser(@Param(\"appId\") long appId, @Param(\"userId\") long userId);\r\n\r\n    void takeOverAppToUser(@Param(\"appId\") long appId, @Param(\"originUserId\") long originUserId, @Param(\"newUserId\") long newUserId);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/AppUserDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport java.util.List;\n\nimport com.sohu.cache.web.vo.AppUserVo;\nimport org.apache.ibatis.annotations.Param;\n\nimport com.sohu.cache.entity.AppUser;\n\n/**\n * 用户管理dao\n * @author leifu\n * @Time 2014年6月5日\n */\npublic interface AppUserDao {\n\n\tpublic AppUser get(@Param(\"id\") Long id);\n\t\n\tpublic int save(AppUser user);\n\t\n\tpublic int update(AppUser user);\n\t\n\tpublic int delete(@Param(\"id\") Long id);\n\n\tpublic int updatePwd(@Param(\"id\") Long id, @Param(\"password\") String password);\n\n\tpublic AppUser getByName(@Param(\"name\") String name);\n\n\tAppUser getByEmail(@Param(\"email\") String email);\n\n    public List<AppUser> getUserList(@Param(\"chName\") String chName);\n\n    public List<AppUserVo> getUserWithBizList(@Param(\"chName\") String chName, @Param(\"bizName\") String bizName);\n\n\tList<AppUser> getAdminList();\n\n    public List<AppUser> getAllUser();\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/ConfigDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport java.util.List;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.SystemConfig;\r\n\r\n/**\r\n * 配置修改dao\r\n * \r\n * @author leifu\r\n * @Date 2016年5月23日\r\n * @Time 下午12:51:45\r\n */\r\npublic interface ConfigDao {\r\n\r\n    /**\r\n     * 更新配置对key-value\r\n     * \r\n     * @param configKey\r\n     * @param configValue\r\n     */\r\n    public void update(@Param(\"configKey\") String configKey, @Param(\"configValue\") String configValue);\r\n\r\n    /**\r\n     * 获取配置列表\r\n     * \r\n     * @param status\r\n     * @return\r\n     */\r\n    public List<SystemConfig> getConfigList(@Param(\"status\") int status);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/ConfigRestartRecordDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.ConfigRestartRecord;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/10/14 15:58\n * @Description: 重启记录\n */\npublic interface ConfigRestartRecordDao {\n\n    /**\n     * @param configRestartRecord\n     */\n    void save(ConfigRestartRecord configRestartRecord);\n\n    /**\n     * @param id\n     * @return\n     */\n    ConfigRestartRecord getById(@Param(\"id\") long id);\n\n    /**\n     * 更新记录状态\n     *\n     * @param id\n     * @param status\n     */\n    void updateStatus(@Param(\"id\") long id, @Param(\"status\") int status);\n\n    /**\n     * 根据条件更新\n     *\n     * @param configRestartRecord\n     */\n    void updateByCondition(ConfigRestartRecord configRestartRecord);\n\n    /**\n     * 获取重启记录列表\n     * @param configRestartRecord\n     * @return\n     */\n    List<ConfigRestartRecord> getListByCondition(ConfigRestartRecord configRestartRecord);\n\n    /**\n     * 获取重启记录条数\n     * @param configRestartRecord\n     * @return\n     */\n    int getCountByCondition(ConfigRestartRecord configRestartRecord);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/DiagnosticTaskRecordDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 16:49\n */\npublic interface DiagnosticTaskRecordDao {\n\n    long insertDiagnosticTaskRecord(DiagnosticTaskRecord diagnosticTaskRecord);\n\n    int updateDiagnosticStatus(@Param(\"id\") long id, @Param(\"redisKey\") String redisKey, @Param(\"status\") int status, @Param(\"cost\") long cost);\n\n    List<DiagnosticTaskRecord> getDiagnosticTaskRecords(@Param(\"appId\") Long appId, @Param(\"parentTaskId\") Long parentTaskId, @Param(\"auditId\") Long auditId, @Param(\"type\") Integer type, @Param(\"status\") Integer status);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceAlertConfigDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.InstanceAlertConfig;\r\n\r\n/**\r\n * 实例报警配置Dao\r\n * @author leifu\r\n * @Date 2017年5月19日\r\n * @Time 上午11:56:56\r\n */\r\npublic interface InstanceAlertConfigDao {\r\n    \r\n    int save(InstanceAlertConfig instanceAlertConfig);\r\n\r\n    int batchSave(List<InstanceAlertConfig> instanceAlertConfigList);\r\n\r\n    List<InstanceAlertConfig> getAll();\r\n    \r\n    List<InstanceAlertConfig> getByType(@Param(\"type\") int type);\r\n\r\n    List<InstanceAlertConfig> getByTypeAndAppType(@Param(\"type\") int type, @Param(\"appType\") int appType);\r\n\r\n    List<InstanceAlertConfig> getByAlertConfig(@Param(\"alertConfig\") String alertConfig);\r\n\r\n    List<InstanceAlertConfig> getByAlertConfigAndType(@Param(\"alertConfig\") String alertConfig, @Param(\"type\") int type, @Param(\"appType\") Integer appType);\r\n\r\n    InstanceAlertConfig get(@Param(\"id\") int id);\r\n\r\n    int remove(@Param(\"id\") int id);\r\n    \r\n    void update(@Param(\"id\") long id, @Param(\"alertValue\") String alertValue, @Param(\"checkCycle\") int checkCycle,  @Param(\"compareType\") int compareType, @Param(\"importantLevel\") int importantLevel);\r\n\r\n    void updateImportantLevel(@Param(\"alertConfig\") String alertConfig, @Param(\"compareType\") int compareType, @Param(\"importantLevel\") int importantLevel, @Param(\"appType\") int appType);\r\n\r\n    void updateLastCheckTime(@Param(\"id\") long id, @Param(\"lastCheckTime\") Date lastCheckTime);\r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceBigKeyDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.task.entity.InstanceBigKey;\nimport com.sohu.cache.web.util.Page;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\npublic interface InstanceBigKeyDao {\n\n\t/**\n\t * 批量保存bigkey\n\t * @param instanceBigKeyList\n\t * @return\n\t */\n    int batchSave(@Param(\"instanceBigKeyList\") List<InstanceBigKey> instanceBigKeyList);\n\n    /**\n     * @param appId\n     * @param auditId\n     * @return\n     */\n    int getAppBigKeyCount(@Param(\"appId\") long appId, @Param(\"auditId\") long auditId);\n\n    /**\n     * @param appId\n     * @param auditId\n     * @param page\n     * @return\n     */\n\tList<InstanceBigKey> getAppBigKeyList(@Param(\"appId\") long appId, @Param(\"auditId\") long auditId,\n            @Param(\"page\") Page page);\n    \n    \n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceConfigDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport java.util.List;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.InstanceConfig;\r\n\r\n/**\r\n * 配置模板Dao\r\n * \r\n * @author leifu\r\n * @Date 2016年6月22日\r\n * @Time 下午5:46:37\r\n */\r\npublic interface InstanceConfigDao {\r\n    \r\n    /**\r\n     * 获取所有配置模板\r\n     * @return\r\n     */\r\n    List<InstanceConfig> getAllInstanceConfig();\r\n\r\n    /**\r\n     * 根据redis版本获取对应模板配置\r\n     * @param versionId redis版本主键id\r\n     * @return redis版本所有配置项\r\n     */\r\n    List<InstanceConfig> getConfigByRedisVersionId(@Param(\"versionId\") int versionId);\r\n\r\n    /**\r\n     * 根据type获取配置模板列表\r\n     * \r\n     * @param type\r\n     * @return\r\n     */\r\n    List<InstanceConfig> getByType(@Param(\"type\") int type);\r\n\r\n    /**\r\n     * 根据redis version 和 type获取配置模板列表\r\n     * @param type\r\n     * @return\r\n     */\r\n    List<InstanceConfig> getByVersionAndType(@Param(\"type\") int type,@Param(\"versionId\") int versionId);\r\n\r\n    /**\r\n     * 根据redis version获取配置模板列表\r\n     * @param versionId\r\n     * @return\r\n     */\r\n    List<InstanceConfig> getByVersion(@Param(\"versionId\") int versionId);\r\n\r\n    /**\r\n     * 保存或者更新配置模板\r\n     * \r\n     * @param instanceConfig\r\n     * @return\r\n     */\r\n    int saveOrUpdate(InstanceConfig instanceConfig);\r\n\r\n    /**\r\n     * 根据id获取配置模板\r\n     * \r\n     * @param id\r\n     * @return\r\n     */\r\n    InstanceConfig getById(@Param(\"id\") long id);\r\n\r\n    /**\r\n     * 根据configKey和type获取配置\r\n     * \r\n     * @param configKey\r\n     * @param type\r\n     * @return\r\n     */\r\n    InstanceConfig getByConfigKeyAndType(@Param(\"configKey\") String configKey, @Param(\"type\") int type);\r\n\r\n    /**\r\n     * 更改配置状态\r\n     * @param id\r\n     * @param status\r\n     * @return\r\n     */\r\n    int updateStatus(@Param(\"id\") long id, @Param(\"status\") int status);\r\n\r\n    /**\r\n     * 删除配置\r\n     * @param id\r\n     * @return\r\n     */\r\n    int remove(@Param(\"id\") long id);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.InstanceInfo;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 基于instance的dao操作\r\n * <p/>\r\n * User: lingguo\r\n * Date: 14-6-3\r\n * Time: 下午3:58\r\n */\r\npublic interface InstanceDao {\r\n    /**\r\n     * 通过type查询实例列表\r\n     *\r\n     * @param type\r\n     * @return\r\n     */\r\n    public List<InstanceInfo> getInstListByType(@Param(\"type\") int type);\r\n\r\n    /**\r\n     * 查询appId下的所有instance\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    public List<InstanceInfo> getInstListByAppId(@Param(\"appId\") long appId);\r\n\r\n    /**\r\n     * 查询appId下的有效的instance\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    public List<InstanceInfo> getEffectiveInstListByAppId(@Param(\"appId\") long appId);\r\n\r\n    /**\r\n     * 通过host和port查询一个实例信息\r\n     *\r\n     * @param ip\r\n     * @param port\r\n     * @return\r\n     */\r\n    public InstanceInfo getInstByIpAndPort(@Param(\"ip\") String ip, @Param(\"port\") int port);\r\n\r\n    /**\r\n     * 通过host和port查询一个实例信息\r\n     *\r\n     * @param ip\r\n     * @param port\r\n     * @return\r\n     */\r\n    public InstanceInfo getAllInstByIpAndPort(@Param(\"ip\") String ip, @Param(\"port\") int port);\r\n\r\n    /**\r\n     * 通过所有实例列表(包括:0:节点异常,1:正常启用)\r\n     */\r\n    public List<InstanceInfo> getAllInsts();\r\n\r\n    /**\r\n     * 检测当前机器心跳停止的实例\r\n     */\r\n    public List<InstanceInfo> checkHeartStopInstance(@Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * 获取机器所有心跳停止的实例\r\n     */\r\n    public List<InstanceInfo> getAllHeartStopInstance();\r\n\r\n    /**\r\n     * 通过host和port查询一个实例信息\r\n     *\r\n     * @param ip\r\n     * @param port\r\n     * @return\r\n     */\r\n    public int getCountByIpAndPort(@Param(\"ip\") String ip, @Param(\"port\") int port);\r\n\r\n    /**\r\n     * 保存一个实例\r\n     *\r\n     * @param instanceInfo\r\n     */\r\n    public void saveInstance(InstanceInfo instanceInfo);\r\n\r\n    /**\r\n     * 根据ip和type查询实例数量\r\n     *\r\n     * @param ip\r\n     * @param type\r\n     * @return\r\n     */\r\n    public int getInstanceTypeCount(@Param(\"ip\") String ip, @Param(\"type\") int type);\r\n\r\n    public List<InstanceInfo> getInstancesByType(@Param(\"app_id\") long app_id, @Param(\"type\") int type);\r\n\r\n    public InstanceInfo getInstanceInfoById(@Param(\"id\") long id);\r\n\r\n    public int getMemoryByHost(String host);\r\n\r\n    public int getInstanceCountByHost(@Param(\"host\") String host);\r\n\r\n    public int update(InstanceInfo instanceInfo);\r\n\r\n    /**\r\n     * 获取一台机器的所有实例\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    public List<InstanceInfo> getInstListByIp(@Param(\"ip\") String ip);\r\n\r\n\r\n    /**\r\n     * 机器实例数map\r\n     *\r\n     * @return\r\n     */\r\n    public List<Map<String, Object>> getMachineInstanceCountMap();\r\n\r\n    /**\r\n     * <p>\r\n     * Description:获取有效的实例id\r\n     * </p>\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2017/8/14\r\n     */\r\n    public List<Map<String, Object>> getTotalEffectiveInst();\r\n\r\n    List<Long> getAppIdListByIp(List<String> ipList);\r\n\r\n    /**\r\n     * 更新实例状态\r\n     * @param appId\r\n     * @param ip\r\n     * @param port\r\n     * @param status\r\n     */\r\n    public void updateStatus(@Param(\"appId\") long appId,@Param(\"ip\") String ip,@Param(\"port\") int port,@Param(\"status\") int status);\r\n\r\n    /**\r\n     * 根据条件查询实例列表\r\n     * @param instanceInfo\r\n     * @return\r\n     */\r\n    public List<InstanceInfo> getInstancesByCondition(InstanceInfo instanceInfo);\r\n\r\n    /**\r\n     * 根据条件(应用ids, 实例ip:port)查询已下线实例列表\r\n     * @param appIds\r\n     * @param hostports\r\n     * @return\r\n     */\r\n    public List<InstanceInfo> getOfflineInstByAppIdAndHostport(@Param(\"appIds\") List<Long> appIds, @Param(\"hostports\") List<String> hostports);\r\n\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceFaultDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.InstanceFault;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * Created by yijunzhang on 14-12-29.\r\n */\r\npublic interface InstanceFaultDao {\r\n\r\n    /**\r\n     * 添加InstanceFault实例\r\n     *\r\n     * @return\r\n     */\r\n    int insert(InstanceFault instanceFault);\r\n\r\n    /**\r\n     * 实例故障列表\r\n     *\r\n     * @param instId\r\n     * @return\r\n     */\r\n    List<InstanceFault> getListByInstId(int instId);\r\n\r\n    /**\r\n     * 应用故障列表\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    List<InstanceFault> getListByAppId(long appId);\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceLatencyHistoryDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport com.sohu.cache.entity.InstanceLatencyHistory;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @Author: rucao\n * @Date: 2020/5/7 4:57 下午\n */\n@Repository\npublic interface InstanceLatencyHistoryDao {\n    int batchSave(List<InstanceLatencyHistory> instanceLatencyHistoryList);\n\n    List<Map<String, Object>> getAppLatencyStats(@Param(\"appId\") long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    int getAppLatencyStatsCount(@Param(\"appId\") long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    List<Map<String, Object>> getAppLatencyStatsGroupByInstance(@Param(\"appId\") long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n\n    List<Map<String, Object>> getAppLatencyInfo(@Param(\"appId\") long appId, @Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime, @Param(\"event\") String event);\n\n    List<AppClientStatisticGather> getAppLatencyCountStat(@Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceReshardProcessDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Param;\n\nimport com.sohu.cache.entity.InstanceReshardProcess;\n\n\n/**\n * 实例Reshard进度保存\n * @author leifu\n * @Date 2017年6月24日\n * @Time 下午7:17:36\n */\npublic interface InstanceReshardProcessDao {\n\n    int save(InstanceReshardProcess instanceReshardProcess);\n    \n    List<InstanceReshardProcess> getByAuditId(@Param(\"auditId\") long auditId);\n    \n    int updateStatus(@Param(\"id\") long id, @Param(\"status\") int status);\n    \n    int updateEndTime(@Param(\"id\") long id, @Param(\"endTime\") Date endTime);\n    \n    int increaseFinishSlotNum(@Param(\"id\") long id);\n\n    int updateMigratingSlot(@Param(\"id\") int id, @Param(\"migratingSlot\") int migratingSlot);\n\n    InstanceReshardProcess get(@Param(\"id\") int id);\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceSlowLogDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport com.sohu.cache.entity.InstanceSlowLog;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.ibatis.annotations.Param;\n\n/**\n * 实例慢查询dao\n *\n * @author leifu\n * @Date 2016年2月22日\n * @Time 下午1:48:43\n */\npublic interface InstanceSlowLogDao {\n\n    /**\n     * 批量报错实例慢查询\n     * @param instanceSlowLogList\n     */\n    int batchSave(List<InstanceSlowLog> instanceSlowLogList);\n\n    /**\n     * 按照应用id获取慢查询列表\n     * @param appId\n     * @return\n     */\n    List<InstanceSlowLog> getByAppId(@Param(\"appId\") long appId);\n\n    /**\n     * 搜索慢查询日志\n     */\n    List<InstanceSlowLog> search(@Param(\"appId\") long appId, @Param(\"startDate\") Date startDate, @Param(\"endDate\") Date endDate);\n\n    List<InstanceSlowLog> getByInstanceExecuteTime(@Param(\"instanceId\") long instanceId, @Param(\"executeDate\") String executeDate);\n\n    /**\n     *\n     * @param appId\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    List<Map<String, Object>> getInstanceSlowLogCountMapByAppId(@Param(\"appId\") long appId, @Param(\"startDate\") Date startDate, @Param(\"endDate\") Date endDate);\n\n\n    /**\n     * 获取指定日期慢查询个数\n     * @param appId\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    int getAppSlowLogCount(@Param(\"appId\") long appId, @Param(\"startDate\") Date startDate, @Param(\"endDate\") Date endDate);\n\n    List<AppClientStatisticGather> getAppSlowLogCountStat(@Param(\"startTime\") long startTime, @Param(\"endTime\") long endTime);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/InstanceStatsDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.InstanceStats;\r\nimport com.sohu.cache.entity.MachineInstanceStat;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 实例统计相关DAO\r\n */\r\npublic interface InstanceStatsDao {\r\n\r\n    public void updateInstanceStats(InstanceStats instanceStats);\r\n\r\n    public InstanceStats getInstanceStatsByHost(@Param(\"ip\") String ip, @Param(\"port\") long port);\r\n\r\n    public InstanceStats getInstanceStatsByInsId(@Param(\"id\") long id);\r\n\r\n    public List<InstanceStats> getInstanceStatsByAppId(@Param(\"appId\") long appId);\r\n\r\n    public List<InstanceStats> getInstanceStats();\r\n\r\n    public List<InstanceStats> getInstanceStatsByIp(@Param(\"ip\") String ip);\r\n\r\n    Map getMachineMemByIp(@Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * <p>\r\n     * Description:获取所有的memory\r\n     * </p>\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2017/8/14\r\n     */\r\n    public Map<String, Object> getTotalMem();\r\n\r\n    /**\r\n     * <p>\r\n     * Description:获取有效redis实例 master使用的memory\r\n     * </p>\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2017/8/14\r\n     */\r\n    public Map<String, Object> getTotalAppMem(List<String> instancelist);\r\n\r\n    /**\r\n     * <p>\r\n     * Description: 获取实例统计信息\r\n     * </p>\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2019/2/22\r\n     */\r\n    public List<MachineInstanceStat> getMachineInstanceStatList();\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/MachineDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.MachineInfo;\r\nimport com.sohu.cache.entity.MachineMemStatInfo;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 机器相关的操作\r\n * <p>\r\n * User: lingguo\r\n * Date: 14-6-12\r\n * Time: 下午2:33\r\n */\r\npublic interface MachineDao {\r\n\r\n    /**\r\n     * 返回所有可用的机器资源\r\n     *\r\n     * @return\r\n     */\r\n    public List<MachineInfo> getAllMachines();\r\n\r\n    /**\r\n     * 通过ip查询机器信息\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    public MachineInfo getMachineInfoByIp(@Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * 通过ip&版本安装情况机器信息\r\n     *\r\n     * @param ipLike\r\n     * @param versionStr\r\n     * @return\r\n     */\r\n    public List<MachineInfo> getMachineInfoByCondition(@Param(\"ipLike\") String ipLike, @Param(\"useType\") int useType, @Param(\"type\") int type, @Param(\"versionStr\") String versionStr, @Param(\"k8sType\") int k8sType, @Param(\"realip\") String realip);\r\n\r\n    /**\r\n     * 通过ip模糊查找机器\r\n     * @param ipLike\r\n     * @param realIpLike\r\n     * @return\r\n     */\r\n    public List<MachineInfo> getMachineListByCondition(@Param(\"ipLike\") String ipLike, @Param(\"realIpLike\") String realIpLike);\r\n\r\n    /**\r\n     * @Description: 通过room&useType查询机器信息\r\n     * @Author: caoru\r\n     * @CreateDate: 2018/10/12 11:39\r\n     */\r\n    List<MachineMemStatInfo> getMachineMemStatInfoByCondition(@Param(\"room\") String room, @Param(\"useType\") int useType);\r\n\r\n    List<MachineMemStatInfo> getMachineMemStatInfoByIpList(@Param(\"list\") List list);\r\n\r\n    /**\r\n     * 通过ip列表查询机器信息\r\n     * @param list\r\n     * @return\r\n     */\r\n    List<MachineInfo> getMachineInfoByIpList(@Param(\"list\") List list);\r\n\r\n    /**\r\n     * 通过ip模糊查询机器信息\r\n     *\r\n     * @param ipLike\r\n     * @return\r\n     */\r\n    public List<MachineInfo> getMachineInfoByLikeIp(@Param(\"ipLike\") String ipLike);\r\n\r\n    /**\r\n     * 保存一条机器信息\r\n     *\r\n     * @param machineInfo\r\n     */\r\n    public void saveMachineInfo(MachineInfo machineInfo);\r\n\r\n    /**\r\n     * 根据ip删除一台机器的信息；\r\n     *\r\n     * @param ip\r\n     */\r\n    public void removeMachineInfoByIp(@Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * 根据ip更新物理机ip\r\n     *\r\n     * @param real_ip\r\n     * @param ip\r\n     * @return\r\n     */\r\n    int updateMachineRealIpByIp(@Param(\"real_ip\") String real_ip, @Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * k8s容器：ip+k8s_type\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    MachineInfo existk8sMachine(@Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * 获取k8s机器的信息\r\n     *\r\n     * @return\r\n     */\r\n    List<MachineInfo> getK8sMachineList();\r\n\r\n    /**\r\n     * 获取k8s容器的信息\r\n     *\r\n     * @return\r\n     */\r\n    List<MachineInfo> getMachineList();\r\n\r\n    /**\r\n     * 通过type查询机器列表\r\n     *\r\n     * @param type\r\n     * @return\r\n     */\r\n    public List<MachineInfo> getMachineInfoByType(@Param(\"type\") int type);\r\n\r\n    /**\r\n     * <p>\r\n     * Description:获取所有机器的内存统计情况\r\n     * </p>\r\n     *\r\n     * @param\r\n     * @return\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2017/8/14\r\n     */\r\n    public Map<String, Object> getTotalMachineMem();\r\n\r\n    /**\r\n     * <p>\r\n     * Description: 获取机房分布情况\r\n     * </p>\r\n     *\r\n     * @param\r\n     * @return\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2019/2/27\r\n     */\r\n    public List<Map<String, Object>> getRoomStat();\r\n\r\n    /**\r\n     * 更新机器分配状态\r\n     *\r\n     * @param ip\r\n     * @param status\r\n     */\r\n    public void updateMachineAllocate(@Param(\"ip\") String ip, @Param(\"status\") int status);\r\n\r\n    /**\r\n     * 获取机器配置信息及已分布redis实例（数量、申请内存、使用内存、使用内存rss等）\r\n     */\r\n    public List<MachineMemStatInfo> getMachineInfoAndUsedInfo(@Param(\"room\") String room, @Param(\"type\") Integer type, @Param(\"useType\") Integer useType, @Param(\"disType\") Integer disType, @Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * 通过realIp查找机器\r\n     * @param realIp\r\n     * @return\r\n     */\r\n    public List<MachineInfo> getMachineListByRealIp(@Param(\"realIp\") String realIp);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/MachineRelationDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.MachineRelation;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * Created by chenshi on 2019/5/21.\n */\npublic interface MachineRelationDao {\n\n    /**\n     * 保存pod实例变更状态\n     *\n     * @param machineRelation\n     */\n    public void saveOrUpdateMachineRelation(MachineRelation machineRelation);\n\n    /**\n     * 更新任务taskid\n     */\n    public void updateMachineRelation(@Param(\"id\") int id, @Param(\"taskid\") long taskid, @Param(\"is_sync\") long is_sync);\n\n    /**\n     * 获取容器的trace记录\n     *\n     * @param ip\n     */\n    public List<MachineRelation> getRelationList(@Param(\"ip\") String ip);\n\n    /**\n     * 获取pod online的容器记录\n     * @param ip\n     * @param status\n     */\n    public List<MachineRelation> getOnlinePodList(@Param(\"ip\") String ip,@Param(\"status\") int status);\n\n    /**\n     * 获取容器的trace记录\n     *\n     * @param ip\n     * @param real_ip\n     */\n    public List<MachineRelation> getUnSyncRelationList(@Param(\"ip\") String ip, @Param(\"real_ip\") String real_ip);\n\n    /**\n     * 更新机器同步状态\n     *\n     * @param id\n     * @param is_sync\n     */\n    public void updateMachineSyncStatus(@Param(\"id\") int id, @Param(\"is_sync\") int is_sync);\n\n    /**\n     * 检测任务的状态\n     * @param ip\n     * @param real_ip\n     * @param is_sync\n     * @return\n     */\n    public List<MachineRelation> getMachineSyncStatus(@Param(\"ip\") String ip, @Param(\"real_ip\") String real_ip, @Param(\"is_sync\") long is_sync);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/MachineRoomDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.MachineRoom;\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Options;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\n/**\n * Created by chenshi on 2018/10/16.\n */\npublic interface MachineRoomDao {\n\n    @Select(\"select * from machine_room where status=1\")\n    List<MachineRoom> getEffectiveRoom();\n\n    @Select(\"select * from machine_room\")\n    List<MachineRoom> getAllRoom();\n\n    @Insert(\"insert into machine_room(id, name, status, `desc`, ip_network, operator) values (#{id},#{name},#{status},#{desc},#{ipNetwork},#{operator})\" +\n            \" on duplicate key update name=#{name},status=#{status},`desc`=#{desc},ip_network=#{ipNetwork},operator=#{operator}\")\n    @Options(useGeneratedKeys = true, keyProperty = \"id\", keyColumn = \"id\")\n    int saveRoom(MachineRoom room);\n\n    @Delete(\"delete from machine_room where id=#{id}\")\n    void removeRoom(long id);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/MachineStatsDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.entity.InstanceStats;\r\nimport com.sohu.cache.entity.MachineStats;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * Created by yijunzhang on 14-6-25.\r\n */\r\npublic interface MachineStatsDao {\r\n\r\n    /**\r\n     * 插入或更新machineInfo\r\n     */\r\n    public void mergeMachineStats(MachineStats machineStats);\r\n\r\n    /**\r\n     * 查询机器下的所有实例的信息\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    public List<InstanceInfo> getInstInfoOfMachine(@Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * 查询ip所在的机器的最新状态信息\r\n     *\r\n     * @param ip    ip\r\n     * @return      机器的最新状态\r\n     */\r\n    public MachineStats getMachineStatsByIp(@Param(\"ip\") String ip);\r\n\r\n    /**\r\n     * 根据机器的hostId查询机器的最新状态信息\r\n     *\r\n     * @param hostId\r\n     * @return\r\n     */\r\n    public MachineStats getMachineStatsByHostId(@Param(\"hostId\") long hostId);\r\n\r\n    /**\r\n     * 查询机器下的所有实例的最新状态信息\r\n     *\r\n     * @param hostId    机器的hostId\r\n     * @return          该机器下所有实例的最新统计状态\r\n     */\r\n    public List<InstanceStats> getInstStatOfMachine(@Param(\"hostId\") long hostId);\r\n\r\n    /**\r\n     * 分页查询机器统计\r\n     * @param ipLike\r\n     * @return\r\n     */\r\n    public List<MachineStats> getMachineStats(@Param(\"ipLike\") String ipLike);\r\n\r\n    /**\r\n     * 获取全部机器统计\r\n     * @return\r\n     */\r\n    public List<MachineStats> getAllMachineStats();\r\n\r\n    /**\r\n     * 删除机器统计信息\r\n     * @param ip\r\n     * @return\r\n     */\r\n    public void deleteMachineStatsByIp(@Param(\"ip\") String ip);\r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/QuartzDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.TriggerInfo;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * quartz相关的dao操作\r\n *\r\n * @author: lingguo\r\n * @time: 2014/10/13 14:44\r\n */\r\npublic interface QuartzDao {\r\n\r\n    public List<TriggerInfo> getTriggersByJobGroup(String jobGroup);\r\n\r\n    public List<TriggerInfo> getAllTriggers();\r\n\r\n    public List<TriggerInfo> searchTriggerByNameOrGroup(String queryString);\r\n\r\n    public int getMisFireTriggerCount();\r\n\r\n    public int getTriggerStateCount(@Param(\"triggerState\") String triggerState);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/ResourceDao.java",
    "content": "package com.sohu.cache.dao;\n\nimport com.sohu.cache.entity.SystemResource;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by chenshi on 2020/7/6.\n */\npublic interface ResourceDao {\n\n    List<SystemResource> getResourceList(@Param(\"resourceType\") int resourceType);\n\n    List<SystemResource> getResourceListByName(@Param(\"resourceType\") int resourceType,@Param(\"searchName\") String searchName);\n\n    SystemResource getResourceById(@Param(\"resourceId\") int resourceId);\n\n    SystemResource getResourceByName(@Param(\"resourceName\") String resourceName);\n\n    void save(SystemResource systemResource);\n\n    void update(SystemResource systemResource);\n\n    List<Map<Integer,Integer>> getAppUseRedis();\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/ServerStatusDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport java.util.List;\r\n\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport com.sohu.cache.entity.ServerInfo;\r\nimport com.sohu.cache.entity.ServerStatus;\r\nimport com.sohu.cache.server.data.Server;\r\n\r\n/**\r\n * 服务器状态信息持久化\r\n */\r\npublic interface ServerStatusDao {\r\n\r\n\t/**\r\n\t * 查询服务器基本信息\r\n\t * @param ip\r\n\t * @return @ServerInfo\r\n\t */\r\n\tpublic ServerInfo queryServerInfo(@Param(\"ip\") String ip);\r\n\r\n\tpublic List<ServerInfo> getAllServerInfo();\r\n\r\n\t/**\r\n\t * 保存服务器发行版信息\r\n\t * @param ip\r\n\t * @param dist from /etc/issue\r\n\t */\r\n\tpublic void saveServerInfo(@Param(\"ip\") String ip, @Param(\"dist\") String dist);\r\n\t\r\n\t/**\r\n\t * 删除服务器信息\r\n\t * @param ip\r\n\t * @return 删除的数量\r\n\t */\r\n\tpublic Integer deleteServerInfo(@Param(\"ip\") String ip);\r\n\t\r\n\t/**\r\n\t * 保存/更新服务器信息\r\n\t * @param server\r\n\t * @return 影响的行数\r\n\t */\r\n\tpublic Integer saveAndUpdateServerInfo(@Param(\"server\")Server server);\r\n\t\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerStatus(@Param(\"ip\") String ip, \r\n\t\t\t@Param(\"cdate\") String date);\r\n\t\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerOverview(@Param(\"ip\") String ip, \r\n\t\t\t@Param(\"cdate\") String date);\r\n\t\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerCpu(@Param(\"ip\") String ip, \r\n\t\t\t@Param(\"cdate\") String date);\r\n\t\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerNet(@Param(\"ip\") String ip, \r\n\t\t\t@Param(\"cdate\") String date);\r\n\t\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerDisk(@Param(\"ip\") String ip, \r\n\t\t\t@Param(\"cdate\") String date);\r\n\t\r\n\t/**\r\n\t * 保存服务器状态\r\n\t * @param Server\r\n\t */\r\n\tpublic void saveServerStat(@Param(\"server\") Server server);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/StandardStatsDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.entity.StandardStats;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\n/**\r\n * 实例统计相关DAO\r\n */\r\npublic interface StandardStatsDao {\r\n\r\n    public int mergeStandardStats(StandardStats standardStats);\r\n\r\n    public int mergeInstanceMinuteStats(StandardStats standardStats);\r\n\r\n    public StandardStats getStandardStats(@Param(\"collectTime\") long collectTime, @Param(\"ip\") String ip,\r\n            @Param(\"port\") int port, @Param(\"dbType\") String dbType);\r\n\r\n    public List<StandardStats> getDiffJsonList(@Param(\"beginTime\") long beginTime, @Param(\"endTime\") long endTime,\r\n            @Param(\"ip\") String ip, @Param(\"port\") int port, @Param(\"dbType\") String dbType);\r\n\r\n    public int deleteStandardStatsLessCreatedTime(@Param(\"createdTime\") Date createdTime);\r\n\r\n    public List<StandardStats> getStandardStatsByCreateTime(@Param(\"beginTime\") Date beginTime,\r\n            @Param(\"endTime\") Date endTime, @Param(\"dbType\") String dbType);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/TaskQueueDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.task.entity.TaskQueue;\r\nimport com.sohu.cache.task.entity.TaskSearch;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\npublic interface TaskQueueDao {\r\n    /**\r\n     * @param taskQueue\r\n     */\r\n    void save(TaskQueue taskQueue);\r\n\r\n    /**\r\n     * @param taskId\r\n     * @return\r\n     */\r\n    TaskQueue getById(@Param(\"taskId\") long taskId);\r\n\r\n    /**\r\n     * 更新任务状态\r\n     *\r\n     * @param taskId\r\n     * @param status\r\n     */\r\n    void updateStatus(@Param(\"taskId\") long taskId, @Param(\"status\") int status);\r\n\r\n    /**\r\n     * 更新任务参数\r\n     *\r\n     * @param taskId\r\n     * @param param\r\n     */\r\n    void updateParam(@Param(\"taskId\") long taskId, @Param(\"param\") String param);\r\n\r\n    /**\r\n     * 根据status获取任务列表\r\n     *\r\n     * @param status\r\n     * @return\r\n     */\r\n    List<TaskQueue> getTaskQueueListByStatus(@Param(\"status\") int status);\r\n\r\n    /**\r\n     * 更新开始时间\r\n     *\r\n     * @param taskId\r\n     * @param startTime\r\n     */\r\n    void updateStartTime(@Param(\"taskId\") long taskId, @Param(\"startTime\") Date startTime);\r\n\r\n    /**\r\n     * 更新结束时间\r\n     *\r\n     * @param taskId\r\n     * @param endTime\r\n     */\r\n    void updateEndTime(@Param(\"taskId\") long taskId, @Param(\"endTime\") Date endTime);\r\n\r\n    /**\r\n     * @param taskSearch\r\n     * @return\r\n     */\r\n    int getTaskQueueCount(TaskSearch taskSearch);\r\n\r\n    /**\r\n     * @param taskSearch\r\n     * @return\r\n     */\r\n    List<TaskQueue> getTaskQueueList(TaskSearch taskSearch);\r\n\r\n    /**\r\n     * @param taskId\r\n     * @return\r\n     */\r\n    List<TaskQueue> getChildTaskQueueList(@Param(\"taskId\") long taskId);\r\n\r\n    /**\r\n     * @param status\r\n     * @return\r\n     */\r\n    int getStatusCount(int status);\r\n\r\n    /**\r\n     * @param taskId\r\n     * @param executeIpPort\r\n     */\r\n    void updateExecuteIpPort(@Param(\"taskId\") long taskId, @Param(\"executeIpPort\") String executeIpPort);\r\n\r\n    /**\r\n     * @param appId\r\n     * @param className\r\n     * @return\r\n     */\r\n    List<TaskQueue> getByAppAndClass(@Param(\"appId\") long appId, @Param(\"className\") String className);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/TaskStepFlowDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.task.entity.TaskStepFlow;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\npublic interface TaskStepFlowDao {\r\n\r\n    /**\r\n     * @param taskStepFlow\r\n     */\r\n    void save(TaskStepFlow taskStepFlow);\r\n\r\n    /**\r\n     * @param taskId\r\n     * @param className\r\n     * @param stepName\r\n     * @return\r\n     */\r\n    TaskStepFlow getByTaskClassStep(@Param(\"taskId\") long taskId, @Param(\"className\") String className,\r\n            @Param(\"stepName\") String stepName);\r\n\r\n    /**\r\n     * 更新状态\r\n     *\r\n     * @param id\r\n     * @param status\r\n     */\r\n    void updateStatus(@Param(\"id\") long id, @Param(\"status\") int status);\r\n\r\n    /**\r\n     * 更新日志\r\n     *\r\n     * @param id\r\n     * @param log\r\n     */\r\n    void updateLog(@Param(\"id\") long id, @Param(\"log\") String log);\r\n\r\n    /**\r\n     * 更新开始时间\r\n     *\r\n     * @param id\r\n     * @param startTime\r\n     */\r\n    void updateStartTime(@Param(\"id\") long id, @Param(\"startTime\") Date startTime);\r\n\r\n    /**\r\n     * 更新结束时间\r\n     *\r\n     * @param id\r\n     * @param endTime\r\n     */\r\n    void updateEndTime(@Param(\"id\") long id, @Param(\"endTime\") Date endTime);\r\n\r\n    /**\r\n     * 获取任务的流列表\r\n     *\r\n     * @param taskId\r\n     * @return\r\n     */\r\n    List<TaskStepFlow> getTaskStepFlowList(@Param(\"taskId\") long taskId);\r\n\r\n    /**\r\n     * @param id\r\n     * @param childTaskId\r\n     */\r\n    void updateChildTaskId(@Param(\"id\") long id, @Param(\"childTaskId\") long childTaskId);\r\n\r\n    /**\r\n     * @param taskId\r\n     * @param executeIpPort\r\n     */\r\n    void updateExecuteIpPort(@Param(\"taskId\") long taskId, @Param(\"executeIpPort\") String executeIpPort);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/dao/TaskStepMetaDao.java",
    "content": "package com.sohu.cache.dao;\r\n\r\nimport com.sohu.cache.task.entity.TaskStepMeta;\r\nimport org.apache.ibatis.annotations.Param;\r\n\r\nimport java.util.List;\r\n\r\npublic interface TaskStepMetaDao {\r\n\r\n    /**\r\n     * @param taskStepMeta\r\n     */\r\n    void save(TaskStepMeta taskStepMeta);\r\n\r\n    /**\r\n     * @param className\r\n     * @return\r\n     */\r\n    List<TaskStepMeta> getTaskStepMetaList(@Param(\"className\") String className);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppAlertRecord.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/3 12:00\n * @Description: 报警记录\n */\n@Data\npublic class AppAlertRecord {\n\n    /**\n     * 记录id\n     */\n    private long id;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 重要度（0：一般；1：重要；2：紧急）\n     */\n    private int importantLevel;\n\n    /**\n     * 可见类型（0：均可见；1：仅管理员可见；）\n     */\n    private int visibleType;\n\n    /**\n     * 应用id\n     */\n    private Long appId;\n\n    /**\n     * 实例id\n     */\n    private Long instanceId;\n\n    /**\n     * ip地址\n     */\n    private String ip;\n\n    /**\n     * 端口\n     */\n    private Integer port;\n\n    /**\n     * 报警标题\n     */\n    private String title;\n\n    /**\n     * 报警内容\n     */\n    private String content;\n\n    public String getCreateTimeDesc(){\n        return DateUtil.formatYYYYMMddHHMMSS(createTime);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppAudit.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport com.sohu.cache.constant.AppAuditType;\r\nimport lombok.Data;\r\n\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\n\r\n/**\r\n * Created by yijunzhang\r\n */\r\n@Data\r\npublic class AppAudit {\r\n\r\n    private long id;\r\n\r\n    private long appId;\r\n\r\n    private long userId;\r\n\r\n    private long operateId;\r\n\r\n    private String userName;\r\n\r\n    /**\r\n     * 申请类型:AppAuditType\r\n     */\r\n    private int type;\r\n\r\n    /**\r\n     * 预留参数1\r\n     */\r\n    private String param1;\r\n    /**\r\n     * 预留参数2\r\n     */\r\n    private String param2;\r\n    /**\r\n     * 预留参数3\r\n     */\r\n    private String param3;\r\n\r\n    /**\r\n     * 申请描述\r\n     */\r\n    private String info;\r\n\r\n    /**\r\n     * 0:等待审批; 1:审批通过; -1:驳回\r\n     */\r\n    private int status;\r\n\r\n    private Date createTime;\r\n\r\n    private Date modifyTime;\r\n\r\n    /**\r\n     * 驳回原因\r\n     */\r\n    private String refuseReason;\r\n\r\n    /**\r\n     * 任务ID\r\n     */\r\n    private long taskId;\r\n\r\n    private AppDesc appDesc;\r\n\r\n    private AppAuditLog appAuditLog;\r\n\r\n    public Date getCreateTime() {\r\n        if (createTime != null) {\r\n            return (Date) createTime.clone();\r\n        }\r\n        return null;\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public Date getModifyTime() {\r\n        if (modifyTime == null) {\r\n            return null;\r\n        }\r\n        return (Date) modifyTime.clone();\r\n    }\r\n\r\n    public void setModifyTime(Date modifyTime) {\r\n        this.modifyTime = (Date) modifyTime.clone();\r\n    }\r\n\r\n    public String getTypeDesc() {\r\n        if (AppAuditType.getAppAuditType(type) != null) {\r\n            return AppAuditType.getAppAuditType(type).getInfo();\r\n        }\r\n        return \"\";\r\n    }\r\n\r\n    public String getStatusDesc() {\r\n        //        0:等待审批; 1:审批通过; -1:驳回\r\n        if (status == 0) {\r\n            return \"等待审批\";\r\n        } else if (status == 1) {\r\n            return \"审批通过\";\r\n        } else if (status == -1) {\r\n            return \"驳回\";\r\n        } else {\r\n            return status + \"\";\r\n        }\r\n    }\r\n\r\n    public String getCreateTimeFormat() {\r\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\r\n        if (createTime != null) {\r\n            return sdf.format(createTime);\r\n        }\r\n        return \"\";\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppAuditLog.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.Date;\r\n\r\nimport lombok.Data;\r\nimport net.sf.json.JSONObject;\r\nimport com.sohu.cache.constant.AppAuditLogTypeEnum;\r\n\r\n/**\r\n * 资源的审批发布日志\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppAuditLog implements Serializable {\r\n\r\n    /**\r\n     * 日志id\r\n     */\r\n    private Long id;\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private Long appId;\r\n    \r\n    /**\r\n     * 审批id\r\n     */\r\n    private Long appAuditId;\r\n    \r\n    /**\r\n     * 用户id\r\n     */\r\n    private Long userId;\r\n    \r\n    /**\r\n     * 用户\r\n     */\r\n    private AppUser appUser;\r\n    \r\n    /**\r\n     * 日志详情 是个json\r\n     */\r\n    private String info;\r\n    \r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n    \r\n    /**\r\n     * 日志类型\r\n     */\r\n    private Integer type;\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    /**\r\n     * 生成日志\r\n     * @param appDesc\r\n     * @param appUser\r\n     * @param appAuditId\r\n     * @param type\r\n     * @return\r\n     */\r\n    public static AppAuditLog generate(AppDesc appDesc, AppUser appUser, Long appAuditId, AppAuditLogTypeEnum type){\r\n        if(appDesc == null || appUser == null || appAuditId == null){\r\n            return null;\r\n        }\r\n        AppAuditLog log = new AppAuditLog();\r\n        log.setAppId(appDesc.getAppId());\r\n        log.setUserId(appUser.getId());\r\n        log.setAppAuditId(appAuditId);\r\n        log.setType(type.value());\r\n        log.setCreateTime(new Date());\r\n        log.setInfo(JSONObject.fromObject(appDesc).toString());\r\n        return log;\r\n    }\r\n\r\n}\r\n\r\n\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppBiz.java",
    "content": "package com.sohu.cache.entity;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.NonNull;\nimport lombok.RequiredArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * 系统用户所属业务组\n * \n * @author zengyizhao\n */\n@Data\n@ApiModel\n@RequiredArgsConstructor(staticName = \"of\")\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AppBiz implements Serializable {\n\n    /**\n     * 自增id\n     */\n    @ApiModelProperty(value = \"业务组id\",hidden = true)\n    private Long id;\n\n    /**\n     * 业务组名称\n     */\n    @NonNull\n    @ApiModelProperty(value = \"业务组名称\",required = true)\n    private String name;\n\n    /**\n     * 备注\n     */\n    @NonNull\n    @ApiModelProperty(value = \"备注\")\n    private String bizDesc;\n\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppCapacityMonitor.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2022/10/10 9:38\n * @Description: 应用容量（自动扩容、缩容审计报表）\n */\n@Data\npublic class AppCapacityMonitor {\n\n    /**\n     * 记录id\n     */\n    private long id;\n\n    /**\n     * 应用id\n     */\n    private Long appId;\n\n    /**\n     * 应用主节点数\n     */\n    private Integer shardingMasterNum;\n\n    /**\n     * 应用初始内存\n     */\n    private long mem;\n\n    /**\n     * 应用当前内存\n     */\n    private long curMem;\n\n    /**\n     * 应用使用内存\n     */\n    private long memUsed;\n\n    /**\n     * 应用已使用内存（历史最大值）\n     */\n    private long memUsedHistory;\n\n    /**\n     * 应用分片初始内存\n     */\n    private long shardingMem;\n\n    /**\n     * 应用分片当前内存\n     */\n    private long curShardingMem;\n\n    /**\n     * 分片已使用内存（最大值）\n     */\n    private long shardingMemUsed;\n\n    /**\n     * 应用扩容内存使用百分比\n     */\n    private Integer expandMemPercent;\n\n    /**\n     * 扩容比率，单次扩容比率\n     */\n    private Integer expandRatio;\n\n    /**\n     * 当日总扩容比率（超出不可扩容），0：不限制\n     */\n    private Integer expandRatioTotal;\n\n    /**\n     * 是否可扩容: 0否，1是\n     */\n    private Integer isExpand;\n\n    /**\n     * 是否可缩容: 0否，1是\n     */\n    private Integer isReduce;\n\n    /**\n     * 缩容内存使用率最小值\n     */\n    private Integer reduceRatioMin;\n\n    /**\n     * 缩容内存使用率最大值\n     */\n    private Integer reduceRatioMax;\n\n    /**\n     * 更新时间\n     */\n    private Date updateTime;\n\n    /**\n     * 扩容时间（上次扩容时间）\n     */\n    private Date expandTime;\n\n    /**\n     * 计划状态：0:无意义；1：待缩容；2：待扩容\n     */\n    private Integer scheduleStatus;\n\n    /**\n     * 计划处理时间\n     */\n    private Date scheduleTime;\n\n    /**\n     * 当日扩容次数限制\n     */\n    private int expandCount;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppCapacityStatisticsResult.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2023/6/14 16:20\n * @Description: 查询应用内存、内存审计统计信息\n * @Version: 1.0\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AppCapacityStatisticsResult {\n\n    private long appId;\n\n    private int versionId;\n\n    private Long curMem;\n\n    private Long memUsed;\n\n    private Integer shardingMasterNum;\n\n    private double memUsedRatio;\n\n    public double getMemUsedRatio() {\n        double ratio = 0D;\n        if(curMem != null && memUsed != null && curMem != 0){\n            String format = String.format(\"%.2f\", memUsed * 1D/ curMem * 100);\n            return Double.parseDouble(format);\n        }\n        return ratio;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientCommandStatistics.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.sql.Date;\n\n/**\n * Created by rucao on 2019/12/13\n */\n@Data\npublic class AppClientCommandStatistics {\n    private long id;\n    /**\n     * 格式yyyyMMddHHmm00\n     */\n    private long currentMin;\n    /**\n     * 客户端ip\n     */\n    private String clientIp;\n    /**\n     * 应用id\n     */\n    private long appId;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 命令明文\n     */\n    private String command;\n    /**\n     * 命令累计毫秒耗时\n     */\n    private long cost;\n    /**\n     * 命令调用量\n     */\n    private long count;\n    /**\n     * 输入流量\n     */\n    private long bytesIn;\n    /**\n     * 输出流量\n     */\n    private long bytesOut;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientCostTimeStat.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.text.ParseException;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\n\r\n/**\r\n * 客户端耗时统计\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppClientCostTimeStat {\r\n    \r\n    private long id;\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * 格式yyyyMMddHHmm00\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 客户端ip\r\n     */\r\n    private String clientIp;\r\n\r\n    /**\r\n     * 上报时间\r\n     */\r\n    private Date reportTime;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n\r\n    /**\r\n     * 命令\r\n     */\r\n    private String command;\r\n\r\n    /**\r\n     * 调用次数\r\n     */\r\n    private int count;\r\n\r\n    /**\r\n     * 实例ip\r\n     */\r\n    private String instanceHost;\r\n\r\n    /**\r\n     * 实例port\r\n     */\r\n    private int instancePort;\r\n    \r\n    /**\r\n     * 实例id\r\n     */\r\n    private long instanceId;\r\n    \r\n    /**\r\n     * 中位值\r\n     */\r\n    private int median;\r\n\r\n    /**\r\n     * 平均值\r\n     */\r\n    private double mean;\r\n\r\n    /**\r\n     * 90%最大值\r\n     */\r\n    private int ninetyPercentMax;\r\n\r\n    /**\r\n     * 99%最大值\r\n     */\r\n    private int ninetyNinePercentMax;\r\n\r\n    /**\r\n     * 100%最大值\r\n     */\r\n    private int hundredMax;\r\n\r\n    public Date getReportTime() {\r\n        return (Date) reportTime.clone();\r\n    }\r\n\r\n    public void setReportTime(Date reportTime) {\r\n        this.reportTime = (Date) reportTime.clone();\r\n    }\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public Long getCollectTimeStamp() throws ParseException{\r\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\r\n        Date date;\r\n        try {\r\n            date = sdf.parse(String.valueOf(this.collectTime));\r\n            return date.getTime();\r\n        } catch (Exception e) {\r\n            return 0L;\r\n        }\r\n    }\r\n\r\n    public Long getTimeStamp() throws ParseException{\r\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\r\n        Date date = sdf.parse(String.valueOf(this.collectTime));\r\n        return date.getTime();\r\n    }\r\n\r\n    @Override\r\n    public int hashCode() {\r\n        final int prime = 31;\r\n        int result = 1;\r\n        result = prime * result + ((clientIp == null) ? 0 : clientIp.hashCode());\r\n        result = prime * result + (int) (instanceId ^ (instanceId >>> 32));\r\n        return result;\r\n    }\r\n\r\n    @Override\r\n    public boolean equals(Object obj) {\r\n        if (this == obj)\r\n            return true;\r\n        if (obj == null)\r\n            return false;\r\n        if (getClass() != obj.getClass())\r\n            return false;\r\n        AppClientCostTimeStat other = (AppClientCostTimeStat) obj;\r\n        if (clientIp == null) {\r\n            if (other.clientIp != null)\r\n                return false;\r\n        } else if (!clientIp.equals(other.clientIp))\r\n            return false;\r\n        if (instanceId != other.instanceId)\r\n            return false;\r\n        return true;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientCostTimeTotalStat.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport java.text.DecimalFormat;\r\nimport java.text.ParseException;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\n\r\nimport lombok.Data;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\n/**\r\n * 基于应用全局耗时统计(uniquekey: app_id, command, collect_time)\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppClientCostTimeTotalStat {\r\n    \r\n    private long id;\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * 格式yyyyMMddHHmm00\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n\r\n    /**\r\n     * 命令\r\n     */\r\n    private String command;\r\n\r\n    /**\r\n     * 调用总次数\r\n     */\r\n    private long totalCount;\r\n    \r\n    /**\r\n     * 调用总耗时\r\n     */\r\n    private double totalCost;\r\n\r\n    /**\r\n     * 中位值\r\n     */\r\n    private int median;\r\n\r\n    /**\r\n     * 平均值\r\n     */\r\n    private double mean;\r\n\r\n    /**\r\n     * 90%最大值\r\n     */\r\n    private int ninetyPercentMax;\r\n\r\n    /**\r\n     * 99%最大值\r\n     */\r\n    private int ninetyNinePercentMax;\r\n\r\n    /**\r\n     * 100%最大值\r\n     */\r\n    private int hundredMax;\r\n    \r\n    /**\r\n     * 实例ip\r\n     */\r\n    private String maxInstanceHost;\r\n\r\n    /**\r\n     * 实例port\r\n     */\r\n    private int maxInstancePort;\r\n    \r\n    /**\r\n     * 实例id\r\n     */\r\n    private long maxInstanceId;\r\n    \r\n    /**\r\n     * 客户端\r\n     */\r\n    private String maxClientIp;\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public AppClientCostTimeTotalStat(long id, long appId, long collectTime, Date createTime, String command,\r\n            long totalCount, double totalCost, int median, double mean, int ninetyPercentMax, int ninetyNinePercentMax,\r\n            int hundredMax, String maxInstanceHost, int maxInstancePort, long maxInstanceId, String maxClientIp) {\r\n        this.id = id;\r\n        this.appId = appId;\r\n        this.collectTime = collectTime;\r\n        this.createTime = createTime;\r\n        this.command = command;\r\n        this.totalCount = totalCount;\r\n        this.totalCost = totalCost;\r\n        this.median = median;\r\n        this.mean = mean;\r\n        this.ninetyPercentMax = ninetyPercentMax;\r\n        this.ninetyNinePercentMax = ninetyNinePercentMax;\r\n        this.hundredMax = hundredMax;\r\n        this.maxInstanceHost = maxInstanceHost;\r\n        this.maxInstancePort = maxInstancePort;\r\n        this.maxInstanceId = maxInstanceId;\r\n        this.maxClientIp = maxClientIp;\r\n    }\r\n\r\n    public AppClientCostTimeTotalStat() {\r\n    }\r\n\r\n    public Long getTimeStamp() {\r\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\r\n        try {\r\n            Date date = sdf.parse(String.valueOf(this.collectTime));\r\n            return date.getTime();\r\n        } catch (ParseException e) {\r\n            return 0L;\r\n        }\r\n        \r\n    }\r\n\r\n    public static AppClientCostTimeTotalStat getFromAppClientCostTimeStat(AppClientCostTimeStat stat) {\r\n        AppClientCostTimeTotalStat appClientCostTimeTotalStat = new AppClientCostTimeTotalStat();\r\n        appClientCostTimeTotalStat.setAppId(stat.getAppId());\r\n        appClientCostTimeTotalStat.setCollectTime(stat.getCollectTime());\r\n        appClientCostTimeTotalStat.setCommand(stat.getCommand());\r\n        appClientCostTimeTotalStat.setCreateTime(stat.getCreateTime());\r\n        appClientCostTimeTotalStat.setMean(stat.getMean());\r\n        appClientCostTimeTotalStat.setMedian(stat.getMedian());\r\n        appClientCostTimeTotalStat.setHundredMax(stat.getHundredMax());\r\n        appClientCostTimeTotalStat.setNinetyPercentMax(stat.getNinetyPercentMax());\r\n        appClientCostTimeTotalStat.setNinetyNinePercentMax(stat.getNinetyNinePercentMax());\r\n        appClientCostTimeTotalStat.setMaxClientIp(stat.getClientIp());\r\n        appClientCostTimeTotalStat.setMaxInstanceHost(stat.getInstanceHost());\r\n        appClientCostTimeTotalStat.setMaxInstancePort(stat.getInstancePort());\r\n        appClientCostTimeTotalStat.setMaxInstanceId(stat.getInstanceId());\r\n        appClientCostTimeTotalStat.setMaxClientIp(stat.getClientIp());\r\n        //保留两位小数\r\n        DecimalFormat df = new DecimalFormat(\"#.00\");\r\n        appClientCostTimeTotalStat.setTotalCost(NumberUtils.toDouble(df.format(stat.getMean() * stat.getCount())));\r\n        appClientCostTimeTotalStat.setTotalCount(stat.getCount());\r\n        return appClientCostTimeTotalStat;\r\n    }\r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientDataSizeStat.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * 客户端内收集数据map的尺寸\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppClientDataSizeStat {\r\n\r\n    private long id;\r\n\r\n    /**\r\n     * 格式yyyyMMddHHmm00\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 客户端ip\r\n     */\r\n    private String clientIp;\r\n\r\n    /**\r\n     * 上报时间\r\n     */\r\n    private Date reportTime;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n\r\n    /**\r\n     * 耗时map尺寸\r\n     */\r\n    private int costMapSize;\r\n\r\n    /**\r\n     * 值map尺寸\r\n     */\r\n    private int valueMapSize;\r\n\r\n    /**\r\n     * 异常map尺寸\r\n     */\r\n    private int exceptionMapSize;\r\n\r\n    /**\r\n     * 收集map尺寸\r\n     */\r\n    private int collectMapSize;\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public Date getReportTime() {\r\n        return (Date) reportTime.clone();\r\n    }\r\n\r\n    public void setReportTime(Date reportTime) {\r\n        this.reportTime = (Date) reportTime.clone();\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientExceptionStat.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport java.text.ParseException;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\n\r\nimport lombok.Data;\r\n\r\n/**\r\n * 客户端异常统计\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppClientExceptionStat {\r\n    \r\n    private long id;\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * 格式yyyyMMddHHmm00\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 客户端ip\r\n     */\r\n    private String clientIp;\r\n\r\n    /**\r\n     * 上报时间\r\n     */\r\n    private Date reportTime;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n    \r\n    /**\r\n     * 异常类\r\n     */\r\n    private String exceptionClass;\r\n    \r\n    /**\r\n     * 异常数\r\n     */\r\n    private Long exceptionCount;\r\n\r\n    /**\r\n     * 实例ip\r\n     */\r\n    private String instanceHost;\r\n\r\n    /**\r\n     * 实例port\r\n     */\r\n    private Integer instancePort;\r\n    \r\n    /**\r\n     * 实例id\r\n     */\r\n    private Integer instanceId;\r\n    \r\n    /**\r\n     * 异常类型，参考ClientExceptionType.type\r\n     */\r\n    private Integer type;\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public Date getReportTime() {\r\n        return (Date) reportTime.clone();\r\n    }\r\n\r\n    public void setReportTime(Date reportTime) {\r\n        this.reportTime = (Date) reportTime.clone();\r\n    }\r\n\r\n    public String getCollectTimeFormat(){\r\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\r\n        try {\r\n            Date date = sdf.parse(String.valueOf(collectTime));\r\n            SimpleDateFormat newSdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\r\n            return newSdf.format(date);\r\n        } catch (ParseException e) {\r\n            return \"\";\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientExceptionStatistics.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.sql.Date;\n\n/**\n * Created by rucao on 2019/12/13\n */\n@Data\npublic class AppClientExceptionStatistics {\n    private long id;\n    /**\n     * 格式yyyyMMddHHmm00\n     */\n    private long currentMin;\n    /**\n     * 客户端ip\n     */\n    private String clientIp;\n    /**\n     * 客户端连接池配置信息\n     */\n    private String redisPoolConfig;\n\n    /**\n     * 0：连接失败；1：命令调用超时\n     */\n    private int type;\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 节点信息host:port\n     */\n    private String node;\n    /**\n     * type=4(连接失败):累计连接失败次数\n     * type=5(命令调用超时):累计超时次数\n     */\n    private long count;\n    /**\n     * type=4(连接失败):累计连接失败毫秒耗时\n     * type=5(命令调用超时):累计超时毫秒耗时\n     */\n    private long cost;\n    /**\n     * type=5(命令调用超时):统计命令topN id,逗号分隔\n     */\n    private String latencyCommands;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientLatencyCommand.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * Created by rucao on 2019/12/13\n */\n@Data\npublic class AppClientLatencyCommand {\n    private long id;\n    /**\n     * 长命令裁剪后明文\n     */\n    private String command;\n    /**\n     * args 截取的命令明文\n     */\n    private String args;\n    /**\n     * 命令参数大小\n     */\n    private long size;\n\n    /**\n     * 命令执行时间\n     */\n    private long invokeTime;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientStatisticGather.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.sql.Date;\n\n/**\n * Created by rucao on 2019/12/29\n */\n@Data\npublic class AppClientStatisticGather {\n    private long id;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 应用id\n     */\n    private long appId;\n    /**\n     * 格式yyyy-MM-dd 收集时间\n     */\n    private String gatherTime;\n    /**\n     * 累计命令调用次数\n     */\n    private long cmdCount;\n    /**\n     * 平均命令调用耗时\n     */\n    private double avgCmdCost;\n    /**\n     * 累计连接异常事件次数\n     */\n    private int connExpCount;\n    /**\n     * 平均连接异常事件耗时\n     */\n    private double avgConnExpCost;\n    /**\n     * 累计命令超时事件次数\n     */\n    private int cmdExpCount;\n    /**\n     * 平均命令超时事件耗时\n     */\n    private double avgCmdExpCost;\n    /**\n     * 应用实例数\n     */\n    private int instanceCount;\n    /**\n     * 平均碎片率\n     */\n    private double avgMemFragRatio;\n    /**\n     * 内存使用率\n     */\n    private double memUsedRatio;\n    /**\n     * 异常数（旧，待下线）\n     */\n    private int exceptionCount;\n    /**\n     * 慢查询次数\n     */\n    private int slowLogCount;\n    /**\n     * 延迟事件统计\n     */\n    private int latencyCount;\n\n    /**\n     * 客户端连接数\n     */\n\n    /**\n     * 存储对象数\n     */\n    private long objectSize;\n    /**\n     * 应用客户端连接数\n     */\n    private long connectedClients;\n\n    /**\n     * 内存占用 byte\n     */\n    private long usedMemory;\n\n    /**\n     * 物理内存占用 byte\n     */\n    private long usedMemoryRss;\n\n    /**\n     * 进程系统态消耗(单位:秒)\n     */\n    private long maxCpuSys;\n\n    /**\n     * 进程用户态消耗(单位:秒)\n     */\n    private long maxCpuUser;\n\n    /**\n     * 拓扑诊断结果，0：正常，1：异常\n     */\n    private int topologyExamResult;\n\n    /**\n     * 磁盘使用量 byte\n     */\n    private long usedDisk;\n\n    /**\n     * server端统计的命令调用次数\n     */\n    private long serverCmdCount;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientValueDistriSimple.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport com.sohu.cache.constant.ValueSizeDistriEnum;\r\nimport lombok.Data;\r\n\r\n/**\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppClientValueDistriSimple {\r\n\r\n    /**\r\n     * 值分布类型\r\n     */\r\n    private int distributeType;\r\n\r\n    /**\r\n     * 调用次数\r\n     */\r\n    private long count;\r\n\r\n    public String getDistributeDesc() {\r\n        ValueSizeDistriEnum valueSizeDistriEnum = ValueSizeDistriEnum.getByType(distributeType);\r\n        return valueSizeDistriEnum == null ? \"\" : valueSizeDistriEnum.getInfo();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientValueDistriStatTotal.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * 客户端值分布统计\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppClientValueDistriStatTotal {\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * 格式yyyyMMddHHmm00\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date updateTime;\r\n\r\n    /**\r\n     * 命令\r\n     */\r\n    private String command;\r\n\r\n    /**\r\n     * 值分布类型\r\n     */\r\n    private int distributeType;\r\n\r\n    /**\r\n     * 调用次数\r\n     */\r\n    private int count;\r\n\r\n    public Date getUpdateTime() {\r\n        return (Date) updateTime.clone();\r\n    }\r\n\r\n    public void setUpdateTime(Date updateTime) {\r\n        this.updateTime = (Date) updateTime.clone();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppClientVersion.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * 客户端版本\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppClientVersion {\r\n    \r\n    /**\r\n     * 自增id\r\n     */\r\n    private long id;\r\n    \r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n    \r\n    /**\r\n     * 客户端ip\r\n     */\r\n    private String clientIp;\r\n    \r\n    /**\r\n     * 客户端版本\r\n     */\r\n    private String clientVersion;\r\n    \r\n    /**\r\n     * 上报时间\r\n     */\r\n    private Date reportTime;\r\n\r\n    public Date getReportTime() {\r\n        return (Date) reportTime.clone();\r\n    }\r\n\r\n    public void setReportTime(Date reportTime) {\r\n        this.reportTime = (Date) reportTime.clone();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppCommandGroup.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\n/**\r\n * 命令分布\r\n */\r\n@Data\r\npublic class AppCommandGroup {\r\n\t/**\r\n\t * 命令名\r\n\t */\r\n\tprivate String commandName;\r\n\t\r\n\t/**\r\n\t * 调用次数\r\n\t */\r\n\tprivate long count;\r\n\t\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppCommandStats.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * Created by yijunzhang\r\n */\r\n@Data\r\npublic class AppCommandStats implements Comparable<AppCommandStats> {\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * 收集时间:格式yyyyMMddHHmm/yyyyMMdd/yyyyMMddHH\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 命令名称\r\n     */\r\n    private String commandName;\r\n\r\n    /**\r\n     * 命令执行次数\r\n     */\r\n    private long commandCount;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n\r\n    /**\r\n     * 修改时间\r\n     */\r\n    private Date modifyTime;\r\n    @Override\r\n    public int compareTo(AppCommandStats o) {\r\n        if (o.commandCount > this.commandCount) {\r\n            return 1;\r\n        } else if (o.commandCount < this.commandCount) {\r\n            return -1;\r\n        }\r\n        return 0;\r\n    }\r\n\r\n    public Date getCreateTime() {\r\n        if(createTime == null){\r\n            return createTime;\r\n        }\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public Date getModifyTime() {\r\n        if(modifyTime == null){\r\n            return modifyTime;\r\n        }\r\n        return (Date) modifyTime.clone();\r\n    }\r\n\r\n    public void setModifyTime(Date modifyTime) {\r\n        this.modifyTime = (Date) modifyTime.clone();\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppDailyData.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport lombok.Data;\nimport org.apache.commons.collections.MapUtils;\n\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * 应用日报数据\n *\n * @author leifu\n */\n@Data\npublic class AppDailyData {\n\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 开始日期\n     */\n    private Date startDate;\n\n    /**\n     * 结束日期\n     */\n    private Date endDate;\n\n    /**\n     * 日期\n     */\n    private Date date;\n\n    /**\n     * bigkey次数\n     */\n    private long bigKeyTimes;\n\n    /**\n     * bigkey信息\n     */\n    private String bigKeyInfo;\n\n    /**\n     * 慢查询次数\n     */\n    private long slowLogCount;\n\n    /**\n     * 延迟事件个数\n     */\n    private long latencyCount;\n\n    /**\n     * 客户端异常个数\n     */\n    private long clientExceptionCount;\n    /**\n     * 累计命令调用次数\n     */\n    private long clientCmdCount;\n    /**\n     * 平均命令调用耗时\n     */\n    private double clientAvgCmdCost;\n    /**\n     * 累计连接异常事件次数\n     */\n    private long clientConnExpCount;\n    /**\n     * 平均连接异常事件耗时\n     */\n    private double clientAvgConnExpCost;\n    /**\n     * 累计命令超时事件次数\n     */\n    private long clientCmdExpCount;\n    /**\n     * 平均命令超时事件耗时\n     */\n    private double clientAvgCmdExpCost;\n\n    /**\n     * 每分钟最大客户端连接数\n     */\n    private long maxMinuteClientCount;\n\n    /**\n     * 每分钟平均客户端连接数\n     */\n    private long avgMinuteClientCount;\n\n    /**\n     * 每分钟最大命令数\n     */\n    private long maxMinuteCommandCount;\n\n    /**\n     * 每分钟平均命令数\n     */\n    private long avgMinuteCommandCount;\n\n    /**\n     * 平均命中率\n     */\n    private double avgHitRatio;\n\n    /**\n     * 每分钟最小命中率\n     */\n    private double minMinuteHitRatio;\n\n    /**\n     * 每分钟最大命中率\n     */\n    private double maxMinuteHitRatio;\n\n    /**\n     * 平均内存使用量\n     */\n    private long avgUsedMemory;\n\n    /**\n     * 最大内存使用量\n     */\n    private long maxUsedMemory;\n\n    /**\n     * 过期键个数\n     */\n    private long expiredKeysCount;\n\n    /**\n     * 剔除键个数\n     */\n    private long evictedKeysCount;\n\n    /**\n     * 每分钟平均网络input量\n     */\n    private double avgMinuteNetInputByte;\n\n    /**\n     * 每分钟最大网络input量\n     */\n    private double maxMinuteNetInputByte;\n\n    /**\n     * 每分钟平均网络output量\n     */\n    private double avgMinuteNetOutputByte;\n\n    /**\n     * 每分钟最大网络output量\n     */\n    private double maxMinuteNetOutputByte;\n\n    /**\n     * 键个数平均值\n     */\n    private long avgObjectSize;\n\n    /**\n     * 键个数最大值\n     */\n    private long maxObjectSize;\n\n    /**\n     * 平均磁盘使用量\n     */\n    private long avgUsedDisk;\n\n    /**\n     * 最大磁盘使用量\n     */\n    private long maxUsedDisk;\n\n    /**\n     * 值分布\n     */\n    private Map<String, Long> valueSizeDistributeCountMap;\n\n    /**\n     * 应用详情\n     */\n    private AppDetailVO appDetailVO;\n\n    public String getValueSizeDistributeCountDesc() {\n        if (MapUtils.isEmpty(valueSizeDistributeCountMap)) {\n            return \"无\";\n        }\n        StringBuffer desc = new StringBuffer();\n        for (Map.Entry<String, Long> entry : valueSizeDistributeCountMap.entrySet()) {\n            desc.append(entry.getKey()).append(\":\").append(entry.getValue()).append(\"次<br/>\");\n        }\n        return desc.toString();\n    }\n\n    public String getValueSizeDistributeCountDescHtml() {\n        return bigKeyInfo.replace(\"\\n\", \"<br/>\").replace(\":\", \":\\t\");\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppDataMigrateSearch.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.web.util.Page;\nimport lombok.Data;\n\n/**\n * 应用数据迁移搜索\n *\n * @author leifu\n */\n@Data\npublic class AppDataMigrateSearch {\n    /**\n     * 迁移id\n     */\n    private Long migrateId;\n    /**\n     * 源应用id\n     */\n    private Long sourceAppId;\n\n    /**\n     * 目标应用id\n     */\n    private Long targetAppId;\n\n    /**\n     * 源实例\n     */\n    private String sourceInstanceIp;\n\n    /**\n     * 目标实例\n     */\n    private String targetInstanceIp;\n\n    /**\n     * 迁移机器\n     */\n    private String migrateMachine;\n    /**\n     * 操作人\n     */\n    private Long userId;\n\n    /**\n     * 开始时间\n     */\n    private String startDate;\n\n    /**\n     * 结束时间\n     */\n    private String endDate;\n\n    /**\n     * 状态 -2全部，0全量同步，1同步结束，2同步异常，3增量同步\n     */\n    private int status = 3;\n\n    /**\n     * 分页\n     */\n    private Page page;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppDataMigrateStatus.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.constant.AppDataMigrateEnum;\nimport com.sohu.cache.constant.AppDataMigrateStatusEnum;\nimport lombok.Data;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n/**\n * 迁移状态\n */\n@Data\npublic class AppDataMigrateStatus {\n\n    /**\n     * 自增id\n     */\n    private long id;\n\n    /**\n     * 迁移任务id\n     */\n    private String migrateId;\n\n    /**\n     * 迁移工具 0：redis-shake；1：redis-migrate-tool\n     */\n    private int migrateTool;\n    /**\n     * 迁移工具所在机器ip\n     */\n    private String migrateMachineIp;\n\n    /**\n     * 迁移工具所占port\n     */\n    private int migrateMachinePort;\n\n    /**\n     * 目标实例列表\n     */\n    private String sourceServers;\n\n    /**\n     * 源迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy\n     */\n    private int sourceMigrateType;\n\n    /**\n     * 目标实例列表\n     */\n    private String targetServers;\n\n    /**\n     * 目标迁移类型,0:single,1:redis cluster,2:rdb file,3:twemproxy\n     */\n    private int targetMigrateType;\n\n    /**\n     * 源应用id\n     */\n    private long sourceAppId;\n\n    /**\n     * 目标应用id\n     */\n    private long targetAppId;\n\n    /**\n     * 源redis版本\n     */\n    private String redisSourceVersion;\n\n    /**\n     * 目标redis版本\n     */\n    private String redisTargetVersion;\n\n    /**\n     * 操作人id\n     */\n    private long userId;\n    /**\n     * 操作人name\n     */\n    private String userName;\n\n    /**\n     * 迁移执行状态\n     */\n    private int status;\n\n    /**\n     * 迁移开始执行时间\n     */\n    private Date startTime;\n\n    /**\n     * 迁移结束执行时间\n     */\n    private Date endTime;\n\n    /**\n     * 日志路径\n     */\n    private String logPath;\n\n    /**\n     * 配置路径\n     */\n    private String configPath;\n\n    public String getStatusDesc() {\n        AppDataMigrateStatusEnum appDataMigrateStatusEnum = AppDataMigrateStatusEnum.getByStatus(status);\n        return appDataMigrateStatusEnum == null ? \"异常\" : appDataMigrateStatusEnum.getInfo();\n    }\n\n    public String getTargetMigrateTypeDesc() {\n        AppDataMigrateEnum appDataMigrateEnum = AppDataMigrateEnum.getByIndex(targetMigrateType);\n        return appDataMigrateEnum == null ? \"异常\" : appDataMigrateEnum.getType();\n    }\n\n    public String getSourceMigrateTypeDesc() {\n        AppDataMigrateEnum appDataMigrateEnum = AppDataMigrateEnum.getByIndex(sourceMigrateType);\n        return appDataMigrateEnum == null ? \"异常\" : appDataMigrateEnum.getType();\n    }\n\n    public String getStartTimeFormat() {\n        if (startTime == null) {\n            return \"\";\n        }\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        return sdf.format(startTime);\n    }\n\n    public String getEndTimeFormat() {\n        if (endTime == null) {\n            return \"\";\n        }\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        return sdf.format(endTime);\n    }\n\n    public Date getStartTime() {\n        if (null == startTime) {\n            return null;\n        }\n        return (Date) startTime.clone();\n    }\n\n    public void setStartTime(Date startTime) {\n        if (null == startTime) {\n            this.startTime = null;\n        } else {\n            this.startTime = (Date) startTime.clone();\n        }\n    }\n\n    public Date getEndTime() {\n        if (null == endTime) {\n            return null;\n        }\n        return (Date) endTime.clone();\n    }\n\n    public void setEndTime(Date endTime) {\n        if (null == endTime) {\n            this.endTime = null;\n        } else {\n            this.endTime = (Date) endTime.clone();\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppDesc.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.constant.AppDescEnum;\nimport com.sohu.cache.constant.AppDescEnum.AppImportantLevel;\nimport com.sohu.cache.constant.AppStatusEnum;\nimport com.sohu.cache.redis.util.AuthUtil;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.AppTypeEnum;\nimport lombok.Data;\nimport org.apache.commons.lang.StringUtils;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 应用的信息\n * User: lingguo\n */\n@Data\npublic class AppDesc implements Serializable {\n    private static final long serialVersionUID = -3507970915810652761L;\n\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 应用秘钥\n     */\n    private String appKey;\n\n    /**\n     * 应用名称\n     */\n    private String name;\n\n    /**\n     * 用户id\n     */\n    private long userId;\n\n    /**\n     * 应用状态, 0未分配，1是申请了未审批，2是审批并发布, 3应用下线 4 驳回\n     */\n    private int status;\n\n    /**\n     * 应用描述\n     */\n    private String intro;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 申请通过的时间\n     */\n    private Date passedTime;\n\n    /**\n     * 类型\n     */\n    private int type;\n\n    /**\n     * 类型描述\n     */\n    private String typeDesc;\n\n    /**\n     * 负责人，用户id，逗号分隔\n     */\n    private String officer;\n\n    /**\n     * 版本号\n     */\n    private int verId;\n\n    /**\n     * 类型：0：正式 1：测试 2：试用\n     */\n    private int isTest;\n\n    /**\n     * 是否有后端数据源: 1是0否\n     */\n    private int hasBackStore;\n\n    /**\n     * 是否需要持久化: 1是0否\n     */\n    private int needPersistence;\n\n    /**\n     * 预估qps\n     */\n    private int forecaseQps;\n\n    /**\n     * 是否需要热备: 1是0否\n     */\n    private int needHotBackUp;\n\n    /**\n     * 预估条目数\n     */\n    private int forecastObjNum;\n\n    /**\n     * 内存报警阀值\n     */\n    private int memAlertValue;\n\n    /**\n     * 客户端连接数报警阀值\n     */\n    private int clientConnAlertValue;\n\n    /**\n     * 客户端命中率报警阀值 0:默认不监控\n     */\n    private int hitPrecentAlertValue;\n\n    /**\n     * 客户端是否接入 全局监控\n     */\n    private int isAccessMonitor;\n\n    /**\n     * 客户端机器机房\n     */\n    private String clientMachineRoom;\n\n    /**\n     * redis密码\n     */\n    private String pkey;\n\n    /**\n     * 重要度，默认重要\n     */\n    private int importantLevel = AppImportantLevel.IMPORTANT.getValue();\n\n    /**\n     * Redis版本id,名称\n     */\n    private int versionId;\n    private String versionName;\n\n    /**\n     * Redis小版本是否可以升级 0:最新版本  1:可升小版本\n     */\n    private int isVersionUpgrade = 0;\n\n    /**\n     * 自定义密码，优先级最高\n     */\n    private String customPassword;\n\n    /**\n     * 持久化类型（0：常规；1：主aof自动刷盘；从常规；2：主关闭aof，从常规）\n     * 见AppDescEnum.AppPersistenceType\n     */\n    private Integer persistenceType;\n\n    /**\n     * 最大内存淘汰策略\n     * 见AppDescEnum.MaxmemoryPolicyType\n     */\n    private Integer maxmemoryPolicy;\n\n    /**\n     * 所属业务组，多个之间以，分割\n     */\n    private String bizGroup;\n\n    /**\n     * 是否开启rdb备份\n     */\n    private Integer backupType;\n\n    public String getAuthPassword() {\n        if(StringUtils.isNotBlank(customPassword)){\n            return customPassword;\n        }\n        String authPassword = appId + AuthUtil.SPLIT_KEY;\n        if (StringUtils.isNotBlank(pkey)) {\n            authPassword += getAppPassword();\n        }\n        return authPassword;\n    }\n\n    public boolean isSetCustomPassword(){\n        if(StringUtils.isNotBlank(customPassword)){\n            return true;\n        }\n        return false;\n    }\n\n    public String getPasswordMd5() {\n        if (StringUtils.isNotBlank(pkey)) {\n            return AuthUtil.getAppIdMD5(pkey);\n        }\n        return null;\n    }\n\n    public String getAppPassword() {\n        if(StringUtils.isNotBlank(customPassword)){\n            return customPassword;\n        }\n        if (StringUtils.isNotBlank(pkey)) {\n            return AuthUtil.getAppIdMD5(pkey);\n        }\n        return null;\n    }\n\n    /**\n     * 应用运行天数\n     */\n    public int getAppRunDays() {\n        if (createTime == null) {\n            return -1;\n        }\n        Date now = new Date();\n        long diff = now.getTime() - createTime.getTime();\n        return (int) (diff / TimeUnit.DAYS.toMillis(1));\n    }\n\n    public String getTypeDesc() {\n        if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n            return \"redis-cluster\";\n        } else if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            return \"redis-sentinel\";\n        } else if (type == ConstUtils.CACHE_REDIS_STANDALONE) {\n            return \"redis-standalone\";\n        } else if (type == ConstUtils.CACHE_REDIS_TWEMPROXY) {\n            return \"redis-twemproxy\";\n        } else if (type == ConstUtils.CACHE_PIKA_SENTINEL) {\n            return \"pika-sentinel\";\n        } else if (type == ConstUtils.CACHE_PIKA_TWEMPROXY) {\n            return \"pika-twemproxy\";\n        }\n        return \"\";\n    }\n\n    public String getStatusDesc() {\n        AppStatusEnum appStatusEnum = AppStatusEnum.getByStatus(status);\n        if (appStatusEnum != null) {\n            return appStatusEnum.getInfo();\n        }\n        return \"\";\n    }\n\n    /**\n     * 是否上线\n     *\n     * @return\n     */\n    public boolean isOnline() {\n        return status == AppStatusEnum.STATUS_PUBLISHED.getStatus();\n    }\n\n    /**\n     * 是否下线\n     *\n     * @return\n     */\n    public boolean isOffline() {\n        return status == AppStatusEnum.STATUS_OFFLINE.getStatus();\n    }\n\n    /**\n     * 是否是测试\n     *\n     * @return\n     */\n    public boolean isTestOk() {\n        return isTest == AppDescEnum.AppTest.IS_TEST.getValue();\n    }\n\n    /**\n     * 非常重要\n     *\n     * @return\n     */\n    public boolean isVeryImportant() {\n        return importantLevel == AppDescEnum.AppImportantLevel.VERY_IMPORTANT.getValue();\n    }\n\n    /**\n     * 超级重要\n     *\n     * @return\n     */\n    public boolean isSuperImportant() {\n        return importantLevel == AppDescEnum.AppImportantLevel.SUPER_IMPORTANT.getValue();\n    }\n\n    /**\n     * memcache相关\n     *\n     * @return\n     */\n    public boolean isMemcached() {\n        return AppTypeEnum.MEMCACHED.getType() == type;\n    }\n\n    public Date getCreateTime() {\n        if (createTime != null) {\n            return (Date) createTime.clone();\n        }\n        return null;\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n\n    public Date getPassedTime() {\n        if (passedTime != null) {\n            return (Date) passedTime.clone();\n        }\n        return null;\n    }\n\n    public void setPassedTime(Date passedTime) {\n        this.passedTime = (Date) passedTime.clone();\n    }\n\n    public String getMaxmemoryPolicyDesc() {\n        if(maxmemoryPolicy != null){\n            AppDescEnum.MaxmemoryPolicyType policyType = AppDescEnum.MaxmemoryPolicyType.getByType(maxmemoryPolicy);\n            if(policyType != null){\n                return policyType.getName();\n\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppImport.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * @Author: rucao\n * @Date: 2021/1/7 下午6:00\n */\n@Data\npublic class AppImport {\n    private long id;\n    private long appId;\n    private int memSize;\n    private int sourceType;\n    private String redisVersionName;\n    private String instanceInfo;\n    private String redisPassword;\n    private int status;\n    private int step;\n    private long appBuildTaskId;\n    private long migrateId;\n    private Date createTime;\n    private Date updateTime;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppInfoApi.java",
    "content": "package com.sohu.cache.entity;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * 构建应用对象\n *\n * @author chenshi\n */\n@Data\n@ApiModel\npublic class AppInfoApi {\n\n    /**\n     * 应用名称\n     */\n    @ApiModelProperty(value = \"应用名称\", required = true)\n    private String name;\n\n    /**\n     * 应用介绍\n     */\n    @ApiModelProperty(value = \"应用详情\", required = true)\n    private String desc;\n\n    /**\n     * 申请人信息\n     */\n    @ApiModelProperty(value = \"申请人信息\", required = true)\n    private AppUser user;\n\n    /**\n     * 机器列表信息\n     */\n    @ApiModelProperty(value = \"应用部署机器信息\", required = true)\n    private List<String> iplist;\n\n    /**\n     * 应用内存总量\n     */\n    @ApiModelProperty(value = \"应用申请总内存，单位G\", required = true)\n    private int memTotalSize;\n\n    /**\n     * 是否有从节点 0:无 1:有\n     */\n    @ApiModelProperty(value = \"是否有从节点 0:无 1:有\", required = true)\n    private int hasSlave;\n\n    /**\n     * 应用申请类型: 2:cluster 5:sentinel 6:standalone\n     */\n    @ApiModelProperty(value = \"应用申请类型: 2:cluster 5:sentinel 6:standalone\", required = true)\n    private int type;\n\n    /**\n     * 是否测试: 0:测试 1:正式\n     */\n    @ApiModelProperty(value = \"是否测试: 0:正式 1:测试 2:试用集群\", required = true)\n    private int isTest;\n\n    /**\n     * redis version版本\n     */\n    @ApiModelProperty(value = \"redis version版本,可选 redis-3.0.7/redis-3.2.12/redis-4.0.11\", required = true)\n    private String redisVersion;\n\n    /**\n     * 机房名称\n     */\n    @ApiModelProperty(value = \"机房名称,亦庄/兆维/北显/上海归侨/腾讯黑石\", required = false)\n    private String room;\n\n    @ApiModelProperty(value = \"redis实例数量\", hidden = true)\n    private int instanceNum;\n\n    @ApiModelProperty(value = \"sentinel实例数量,sentinel应用可传，默认值:3\")\n    private int sentinelNum;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppInstanceClientRelation.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * 应用下实例与客户端对应关系\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppInstanceClientRelation {\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * 客户端ip\r\n     */\r\n    private String clientIp;\r\n\r\n    /**\r\n     * 节点ip\r\n     */\r\n    private String instanceHost;\r\n\r\n    /**\r\n     * 节点端口\r\n     */\r\n    private int instancePort;\r\n\r\n    /**\r\n     * 节点端口\r\n     */\r\n    private long instanceId;\r\n\r\n    /**\r\n     * 日期\r\n     */\r\n    private Date day;\r\n\r\n    public AppInstanceClientRelation(long appId, String clientIp, String instanceHost, int instancePort,\r\n            long instanceId, Date day) {\r\n        this.appId = appId;\r\n        this.clientIp = clientIp;\r\n        this.instanceHost = instanceHost;\r\n        this.instancePort = instancePort;\r\n        this.instanceId = instanceId;\r\n        this.day = day;\r\n    }\r\n\r\n    public AppInstanceClientRelation() {\r\n    }\r\n\r\n    public static AppInstanceClientRelation generateFromAppClientCostTimeStat(\r\n            AppClientCostTimeStat appClientCostTimeStat) {\r\n        if (appClientCostTimeStat == null) {\r\n            return null;\r\n        } else {\r\n            return new AppInstanceClientRelation(appClientCostTimeStat.getAppId(),\r\n                    appClientCostTimeStat.getClientIp(), appClientCostTimeStat.getInstanceHost(), appClientCostTimeStat\r\n                    .getInstancePort(), appClientCostTimeStat.getInstanceId(), new Date(\r\n                    System.currentTimeMillis()));\r\n        }\r\n    }\r\n\r\n    public Date getDay() {\r\n        return (Date) day.clone();\r\n    }\r\n\r\n    public void setDay(Date day) {\r\n        this.day = (Date) day.clone();\r\n    }\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppMonitorStatisticsResult.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2023/6/14 16:20\n * @Description: 查询应用统计信息（前端）实体类\n * @Version: 1.0\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AppMonitorStatisticsResult {\n\n    private long appId;\n\n    private Long connExpCount;\n\n    private Long cmdExpCount;\n\n    private Long slowLogCount;\n\n    private Long latencyCount;\n\n    private Long cmdCount;\n\n    private String gatherTime;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppSearch.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.web.util.Page;\nimport lombok.Data;\n\n/**\n * 搜索实体\n *\n * @author leifu\n */\n@Data\npublic class AppSearch {\n    /**\n     * 应用id\n     */\n    private Long appId;\n\n    /**\n     * 应用名\n     */\n    private String appName;\n\n    /**\n     * 应用类型\n     */\n    private Integer appType;\n\n    /**\n     * 申请状态\n     */\n    private Integer appStatus;\n    /**\n     * 应用类型 0：正式 1：测试 2：试用\n     */\n    private Integer isTest;\n\n    /**\n     * 命中率排序\n     */\n    private String orderBy;\n\n    /**\n     * 重要度\n     */\n    private Integer importantLevel;\n    /**\n     * Redis版本 ,默认查询所有版本\n     */\n    private Integer versionId = -1;\n\n    /**\n     * 相关用户id\n     */\n    private long userId;\n\n    /**\n     * 相关业务组id\n     */\n    private long bizId;\n\n    /**\n     * 持久化类型\n     */\n    private Integer persistenceType;\n\n    /**\n     * 是否备份\n     */\n    private Integer backupType;\n\n    /**\n     * 分页\n     */\n    private Page page;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppStatisticsSearch.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2023/6/14 16:20\n * @Description: 查询应用统计信息（前端）实体类\n * @Version: 1.0\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@Builder\npublic class AppStatisticsSearch {\n\n    private Long userId;\n\n    private Boolean isAdmin;\n\n    private String startTime;\n\n    private String endTime;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppStats.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\nimport java.text.DecimalFormat;\r\nimport java.text.NumberFormat;\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\n/**\r\n * Created by yijunzhang on 14-6-9.\r\n */\r\n@Data\r\npublic class AppStats {\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * 收集时间:格式yyyyMMddHHmm/yyyyMMdd/yyyyMMddHH\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 命中数量\r\n     */\r\n    private long hits;\r\n\r\n    /**\r\n     * 未命中数量\r\n     */\r\n    private long misses;\r\n\r\n    /**\r\n     * 命令执行次数\r\n     */\r\n    private long commandCount;\r\n\r\n    /**\r\n     * 内存占用\r\n     */\r\n    private long usedMemory;\r\n\r\n    /**\r\n     * 物理内存占用\r\n     */\r\n    private long usedMemoryRss;\r\n\r\n    /**\r\n     * 过期key数量\r\n     */\r\n    private long expiredKeys;\r\n\r\n    /**\r\n     * 驱逐key数量\r\n     */\r\n    private long evictedKeys;\r\n\r\n    /**\r\n     * 网络输入字节\r\n     */\r\n    private long netInputByte;\r\n\r\n    /**\r\n     * 网络输出字节\r\n     */\r\n    private long netOutputByte;\r\n\r\n    /**\r\n     * 客户端连接数\r\n     */\r\n    private int connectedClients;\r\n\r\n    /**\r\n     * 存储对象数\r\n     */\r\n    private long objectSize;\r\n\r\n    /**\r\n     * 进程系统态消耗(单位:秒)\r\n     */\r\n    private long cpuSys;\r\n    /**\r\n     * 进程用户态消耗(单位:秒)\r\n     */\r\n    private long cpuUser;\r\n\r\n    /**\r\n     * 子进程内核态消耗(单位:秒)\r\n     */\r\n    private long cpuSysChildren;\r\n    /**\r\n     * 子进程用户态消耗(单位:秒)\r\n     */\r\n    private long cpuUserChildren;\r\n\r\n    /**\r\n     * 累加的实例数\r\n     */\r\n    private int accumulation;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n    /**\r\n     * 修改时间\r\n     */\r\n    private Date modifyTime;\r\n\r\n    /**\r\n     * 命令统计集合\r\n     */\r\n    private List<AppCommandStats> commandStatsList;\r\n\r\n    /**\r\n     * 磁盘占用\r\n     */\r\n    private long usedDisk;\r\n\r\n    /**\r\n     * 命中率\r\n     *\r\n     * @return\r\n     */\r\n    public long getHitPercent() {\r\n        long total = hits + misses;\r\n        if (total == 0) {\r\n            return 0;\r\n        } else {\r\n            NumberFormat formatter = new DecimalFormat(\"0\");\r\n            return NumberUtils.toLong(formatter.format(hits * 100.0 / total));\r\n        }\r\n    }\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public Date getModifyTime() {\r\n        return (Date) modifyTime.clone();\r\n    }\r\n\r\n    public void setModifyTime(Date modifyTime) {\r\n        this.modifyTime = (Date) modifyTime.clone();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppToUser.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.io.Serializable;\r\n\r\n/**\r\n * 应用和用户的对应关系\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class AppToUser implements Serializable {\r\n\r\n    private static final long serialVersionUID = 1326072190198022633L;\r\n\r\n    /**\r\n     * 自增id\r\n     */\r\n    private Long id;\r\n\r\n    /**\r\n     * 用户id\r\n     */\r\n    private Long userId;\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private Long appId;\r\n\r\n    public AppToUser() {\r\n    }\r\n\r\n    public AppToUser(Long userId, Long appId) {\r\n        super();\r\n        this.userId = userId;\r\n        this.appId = appId;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppTopMemFragRatio.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * @author wenruiwu\n * @create 2019/11/13 11:09\n * @description\n */\n@Data\npublic class AppTopMemFragRatio extends AppDesc{\n\n    /**\n     * 应用实例数\n     */\n    private int instanceCount;      //应用实例数\n    /**\n     * 平均碎片率\n     */\n    private double avgMemFragRatio; //平均碎片率\n    /**\n     * 内存使用率\n     */\n    private double memUsedRatio;        //内存使用率百分比\n\n    /**\n     * 异常数\n     */\n    private int exceptionCount;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/AppUser.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport io.swagger.annotations.ApiModel;\r\nimport io.swagger.annotations.ApiModelProperty;\r\nimport lombok.Data;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.Date;\r\n\r\n/**\r\n * 系统用户信息\r\n * \r\n * @author leifu\r\n */\r\n@Data\r\n@ApiModel\r\npublic class AppUser implements Serializable {\r\n\r\n    private static final long serialVersionUID = 7425158151337667662L;\r\n\r\n    /**\r\n     * 自增id\r\n     */\r\n    @ApiModelProperty(value = \"用户id\",hidden = true)\r\n    private Long id;\r\n\r\n    /**\r\n     * 用户名(英文，域账户)\r\n     */\r\n    @ApiModelProperty(value = \"用户名(英文，域账户)\",required = true)\r\n    private String name;\r\n\r\n    /**\r\n     * 密码\r\n     */\r\n    @ApiModelProperty(value = \"密码\")\r\n    private String password;\r\n\r\n    /**\r\n     * 中文名\r\n     */\r\n    @ApiModelProperty(value = \"中文名\",required = true)\r\n    private String chName;\r\n    \r\n    /**\r\n     * 用户域账户邮箱\r\n     */\r\n    @ApiModelProperty(value = \"用户域账户邮箱\",required = true)\r\n    private String email;\r\n\r\n    /**\r\n     * 用户手机\r\n     */\r\n    @ApiModelProperty(value = \"用户手机\",required = true)\r\n    private String mobile;\r\n\r\n    /**\r\n     * 用户微信号\r\n     */\r\n    @ApiModelProperty(value = \"用户微信号\",required = true)\r\n    private String weChat;\r\n\r\n    /**\r\n     * 用户类型(类型参考AppUserTypeEnum)\r\n     */\r\n    @ApiModelProperty(value = \"用户类型\",hidden = true)\r\n    private int type;\r\n\r\n    /**\r\n     * 是否接收报警\r\n     */\r\n    @ApiModelProperty(value = \"是否接收报警 0:不接收报警 1:接收报警\",hidden = true)\r\n    private int isAlert;\r\n\r\n    /**\r\n     * 公司名\r\n     */\r\n    @ApiModelProperty(value = \"公司名\")\r\n    private String company;\r\n\r\n    /**\r\n     * 目的\r\n     */\r\n    @ApiModelProperty(value = \"目的\")\r\n    private String purpose;\r\n\r\n    /**\r\n     * 注册时间\r\n     */\r\n    @ApiModelProperty(value = \"注册时间\")\r\n    private Date registerTime;\r\n\r\n    /**\r\n     * 所属业务组id\r\n     */\r\n    @ApiModelProperty(value = \"所属业务组id\")\r\n    private Long bizId;\r\n\r\n    public static AppUser buildFrom(Long userId, String name, String chName, String email, String mobile, String weChat,\r\n            Integer type) {\r\n        AppUser appUser = new AppUser();\r\n        appUser.setId(userId);\r\n        appUser.setName(name);\r\n        appUser.setChName(chName);\r\n        appUser.setEmail(email);\r\n        appUser.setMobile(mobile);\r\n        appUser.setWeChat(weChat);\r\n        appUser.setType(type);\r\n        return appUser;\r\n    }\r\n\r\n    public static AppUser buildFrom(Long userId, String name, String chName, String email, String mobile, String weChat,\r\n                                    Integer type,Integer isAlert) {\r\n        AppUser appUser = new AppUser();\r\n        appUser.setId(userId);\r\n        appUser.setName(name);\r\n        appUser.setChName(chName);\r\n        appUser.setEmail(email);\r\n        appUser.setMobile(mobile);\r\n        appUser.setWeChat(weChat);\r\n        appUser.setType(type);\r\n        appUser.setIsAlert(isAlert);\r\n        return appUser;\r\n    }\r\n\r\n    public static AppUser buildFrom(Long userId, String name, String chName, String email, String mobile, String weChat,\r\n                                    Integer type,Integer isAlert, String company, String purpose, Long bizId) {\r\n        AppUser appUser = new AppUser();\r\n        appUser.setId(userId);\r\n        appUser.setName(name);\r\n        appUser.setChName(chName);\r\n        appUser.setEmail(email);\r\n        appUser.setMobile(mobile);\r\n        appUser.setWeChat(weChat);\r\n        appUser.setType(type);\r\n        appUser.setIsAlert(isAlert);\r\n        appUser.setCompany(company);\r\n        appUser.setPurpose(purpose);\r\n        appUser.setBizId(bizId);\r\n        return appUser;\r\n    }\r\n\r\n    public static AppUser buildFrom(Long userId, String name, String chName, String email, String mobile, String weChat,\r\n                                    Integer type,Integer isAlert, String password, String company, String purpose, Long bizId) {\r\n        AppUser appUser = new AppUser();\r\n        appUser.setId(userId);\r\n        appUser.setName(name);\r\n        appUser.setChName(chName);\r\n        appUser.setEmail(email);\r\n        appUser.setMobile(mobile);\r\n        appUser.setWeChat(weChat);\r\n        appUser.setType(type);\r\n        appUser.setIsAlert(isAlert);\r\n        appUser.setPassword(password);\r\n        appUser.setCompany(company);\r\n        appUser.setPurpose(purpose);\r\n        appUser.setBizId(bizId);\r\n        return appUser;\r\n    }\r\n\r\n    public AppUser() {\r\n    }\r\n\r\n    public AppUser(String name, String chName, String email, String mobile, String weChat, int type) {\r\n        this.name = name;\r\n        this.chName = chName;\r\n        this.email = email;\r\n        this.mobile = mobile;\r\n        this.weChat = weChat;\r\n        this.type = type;\r\n    }\r\n\r\n    public AppUser(String name, String chName, String email, String mobile, String weChat, int type, int isAlert) {\r\n        this.name = name;\r\n        this.chName = chName;\r\n        this.email = email;\r\n        this.mobile = mobile;\r\n        this.weChat = weChat;\r\n        this.type = type;\r\n        this.isAlert = isAlert;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/BrevityScheduleTask.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.alibaba.fastjson.annotation.JSONField;\nimport com.sohu.cache.schedule.brevity.BrevityScheduleType;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n/**\n * Created by yijunzhang\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class BrevityScheduleTask {\n\n    @JSONField(serialize = false)\n    private Integer id;\n\n    private Integer type;\n\n    private Long version;\n\n    private String host;\n\n    private Integer port;\n\n    private Date createTime;\n\n    private BrevityScheduleType brevityScheduleType;\n\n    private Integer instanceType;\n\n    @JSONField(serialize = false)\n    public BrevityScheduleType getBrevityScheduleType() {\n        return BrevityScheduleType.typeOf(type);\n    }\n\n    public Date getCreateTime() {\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n\n    @JSONField(serialize = false)\n    public String getHostPort(){\n        return this.getHost() + \":\" + this.getPort();\n    }\n\n    @JSONField(serialize = false)\n    public String getKeyField(){\n        return this.getType() + \"_\" + this.getHost() + \"_\" + this.getPort();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/ClientInstanceException.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\n/**\r\n * 客户端实例异常\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class ClientInstanceException {\r\n\r\n    private long appId;\r\n\r\n    private long instanceId;\r\n\r\n    private String instanceHost;\r\n\r\n    private int instancePort;\r\n\r\n    private int exceptionCount;\r\n\r\n    public ClientInstanceException(long appId, long instanceId, String instanceHost, int instancePort,\r\n            int exceptionCount) {\r\n        this.appId = appId;\r\n        this.instanceId = instanceId;\r\n        this.instanceHost = instanceHost;\r\n        this.instancePort = instancePort;\r\n        this.exceptionCount = exceptionCount;\r\n    }\r\n\r\n    public ClientInstanceException() {\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/ConfigRestartRecord.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.web.util.Page;\nimport lombok.Data;\n\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/10/14 15:52\n * @Description: 配置重启记录\n */\n@Data\npublic class ConfigRestartRecord {\n\n    /**\n     *\n     */\n    private long id;\n\n    /**\n     * 应用id\n     */\n    private Long appId;\n\n    /**\n     * 应用名称\n     */\n    private String appName;\n\n    /**\n     * 重启类型（滚动重启，修改配置强制重启）\n     */\n    private int operateType;\n\n    /**\n     * 初始化任务参数(json):不变\n     */\n    private String param;\n\n    /**\n     * 状态：0等待，1运行，2失败\n     */\n    private Integer status;\n\n    /**\n     * 日志信息\n     */\n    private String log;\n\n    /**\n     * 实例信息\n     */\n    private String instances;\n\n    /**\n     * 操作人员id\n     */\n    private long userId;\n\n    /**\n     * 操作人员名称\n     */\n    private String userName;\n\n    /**\n     * 开始时间\n     */\n    private Date startTime;\n\n    /**\n     * 结束时间\n     */\n    private Date endTime;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 修改时间\n     */\n    private Date updateTime;\n\n    /**\n     * 分页参数\n     */\n    private Page page;\n\n    private List<String> logList;\n\n    private List<Integer> instanceIdList;\n\n    public List<String> getLogList(){\n        List<String> logList = JSONObject.parseArray(log, String.class);\n        return logList;\n    }\n\n    public List<Integer> getInstanceIdList(){\n        List<Integer> logList = JSONObject.parseArray(instances, Integer.class);\n        return logList;\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/DbPoolStat.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * 数据库连接池统计\n * @author fulei\n * @date 2018年8月17日\n * @time 上午11:40:05\n */\n@Data\npublic class DbPoolStat {\n\n    private long id;\n\t\n\t/**\n\t * 进程ip\n\t */\n\tprivate String ip;\n\t\n\t/**\n\t * 进程port\n\t */\n\tprivate int port;\n\t\n\tprivate String dbPoolName;\n\t\n\tprivate long collectTime;\n\t\n\tprivate Date collectDate;\n\t\n\tprivate int maxSize;\n\t\n\tprivate int minSize;\n\t\n\tprivate int busySize;\n\t\n\tprivate int idleSize;\n\t\n\tprivate int totalSize;\n\t\n\tprivate Date createTime;\n\t\n\tprivate Date updateTime;\n\n\tpublic Date getCreateTime() {\n\t\treturn (Date) createTime.clone();\n\t}\n\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = (Date) createTime.clone();\n\t}\n\n\tpublic Date getUpdateTime() {\n\t\treturn (Date) updateTime.clone();\n\t}\n\n\tpublic void setUpdateTime(Date updateTime) {\n\t\tthis.updateTime = (Date) updateTime.clone();\n\t}\n\n\tpublic Date getCollectDate() {\n\t\treturn (Date) collectDate.clone();\n\t}\n\n\tpublic void setCollectDate(Date collectDate) {\n\t\tthis.collectDate =  (Date) collectDate.clone();\n\t}\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/DeployInfo.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.web.enums.NodeEnum;\nimport lombok.Data;\n\n/**\n * Created by rucao\n */\n@Data\npublic class DeployInfo {\n    /**\n     * 部署形态: 参考 ConstUtils\n     * 2: Redis Cluster\n     * 5: Redis+Sentinel\n     * 7: Redis+Twemproxy\n     * 8: Pika+Sentinel\n     * 9: Pika+Twemproxy\n     * 6: Redis Standalone\n     */\n    private Integer deployType;\n    /**\n     * 分配内存大小，单位M\n     */\n    private Integer memSize;\n    /**\n     * Redis node\n     */\n    private String masterIp;\n    private String slaveIp;\n    /**\n     * Pika node\n     */\n    private String masterPikaIp;\n    private String slavePikaIp;\n    /**\n     * sentinel/twemproxy node\n     */\n    private String sentinelIp;\n    private String twemproxyIp;\n\n    public DeployInfo() {\n    }\n\n    public DeployInfo(Integer deployType, String masterIp, Integer memSize) {\n        this.deployType = deployType;\n        this.masterIp = masterIp;\n        this.memSize = memSize;\n    }\n\n    public DeployInfo(Integer deployType, String masterIp, Integer memSize, String slaveIp) {\n        this.deployType = deployType;\n        this.masterIp = masterIp;\n        this.memSize = memSize;\n        this.slaveIp = slaveIp;\n    }\n\n    public DeployInfo(Integer deployType, String sentinelIp) {\n        this.deployType = deployType;\n        this.sentinelIp = sentinelIp;\n    }\n\n    /**\n     * 获取Redis实例信息\n     */\n    public static DeployInfo getRedisInfo(Integer deployType, String masterIp, Integer memSize, String slaveIp) {\n        DeployInfo deployInfo = new DeployInfo();\n        deployInfo.setDeployType(deployType);\n        deployInfo.setMasterIp(masterIp);\n        deployInfo.setMemSize(memSize);\n        deployInfo.setSlaveIp(slaveIp);\n        return deployInfo;\n    }\n\n    /**\n     * 获取Pika实例信息\n     */\n    public static DeployInfo getPikaInfo(Integer deployType, String masterPikaIp, Integer memSize, String slavePikaIp) {\n        DeployInfo deployInfo = new DeployInfo();\n        deployInfo.setDeployType(deployType);\n        deployInfo.setMasterPikaIp(masterPikaIp);\n        deployInfo.setMemSize(memSize);\n        deployInfo.setSlavePikaIp(slavePikaIp);\n        return deployInfo;\n    }\n\n    /**\n     * 获取Sentinel实例信息\n     */\n    public static DeployInfo getSentinelInfo(Integer deployType, String sentinelIp) {\n        DeployInfo deployInfo = new DeployInfo();\n        deployInfo.setDeployType(deployType);\n        deployInfo.setSentinelIp(sentinelIp);\n        return deployInfo;\n    }\n\n    /**\n     * 获取Twemproxy实例信息\n     */\n    public static DeployInfo getTwemproxyInfo(Integer deployType, String twemproxyIp) {\n        DeployInfo deployInfo = new DeployInfo();\n        deployInfo.setDeployType(deployType);\n        deployInfo.setTwemproxyIp(twemproxyIp);\n        return deployInfo;\n    }\n\n    /**\n     * 判断是否Redis/Pika节点\n     */\n    public static Boolean isRedisNode(int type){\n        if(type == NodeEnum.REDIS_NODE.getValue()){\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 判断是否Sentinel/Twemproxy节点\n     */\n    public static Boolean isSentinelNode(int type){\n        if(type == NodeEnum.SENTINEL_NODE.getValue() ){\n            return true;\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/DeployInfoStat.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * Created by rucao on 2018/10/10\n */\n@Data\npublic class DeployInfoStat {\n    private Integer masterNum;\n    private Integer slaveNum;\n    private Integer sentinelNum;\n    private Integer twemproxyNum;\n\n    public DeployInfoStat(Integer masterNum, Integer slaveNum, Integer sentinelNum) {\n        this.masterNum = masterNum;\n        this.slaveNum = slaveNum;\n        this.sentinelNum = sentinelNum;\n    }\n\n    public DeployInfoStat(Integer masterNum, Integer slaveNum, Integer sentinelNum, Integer twemproxyNum) {\n        this.masterNum = masterNum;\n        this.slaveNum = slaveNum;\n        this.sentinelNum = sentinelNum;\n        this.twemproxyNum = twemproxyNum;\n    }\n\n    public DeployInfoStat() {\n        this.masterNum = 0;\n        this.slaveNum = 0;\n        this.sentinelNum = 0;\n        this.twemproxyNum = 0;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/DiagnosticTaskRecord.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 16:42\n */\n@Data\npublic class DiagnosticTaskRecord {\n    private long id;\n    /**\n     * 任务流id\n     */\n    private long taskId;\n    /**\n     * 父任务id\n     */\n    private long parentTaskId;\n    /**\n     * 审批id\n     */\n    private long auditId;\n\n    /**\n     * 诊断类型：0scan 1bigkey 2idle key 3hotkey 4del key 5slot analysis 6topology exam\n     */\n    private int type;\n    /**\n     * 诊断状态：0开始 1结束 2异常\n     */\n    private int status;\n    /**\n     * 应用id\n     */\n    private long appId;\n    /**\n     * ip:port\n     */\n    private String node;\n    /**\n     * 诊断条件\n     */\n    private String diagnosticCondition;\n    /**\n     * 备用参数1\n     */\n    private String param1;\n    /**\n     * 备用参数2\n     */\n    private String param2;\n    /**\n     * 结果的key\n     */\n    private String redisKey;\n\n    /**\n     * 耗时，毫秒\n     */\n    private long cost;\n\n    private String formatCostTime;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 修改时间\n     */\n    private Date modifyTime;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceAlertConfig.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport java.util.Date;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport com.sohu.cache.redis.enums.InstanceAlertCheckCycleEnum;\r\nimport com.sohu.cache.redis.enums.InstanceAlertCompareTypeEnum;\r\nimport com.sohu.cache.redis.enums.InstanceAlertTypeEnum;\r\nimport lombok.Data;\r\n\r\n/**\r\n * 实例报警阀值配置\r\n * @author leifu\r\n */\r\n@Data\r\npublic class InstanceAlertConfig {\r\n\r\n    /**\r\n     * 自增id\r\n     */\r\n    private long id;\r\n\r\n    /**\r\n     * 报警配置\r\n     */\r\n    private String alertConfig;\r\n\r\n    /**\r\n     * 报警阀值\r\n     */\r\n    private String alertValue;\r\n\r\n    /**\r\n     * 详见CompareTypeEnumNew\r\n     */\r\n    private int compareType;\r\n\r\n    /**\r\n     * 配置说明\r\n     */\r\n    private String configInfo;\r\n\r\n    /**\r\n     * 详见TypeEnum\r\n     */\r\n    private int type;\r\n\r\n    /**\r\n     * -1全局配置，其他代表实例id\r\n     */\r\n    private long instanceId;\r\n\r\n    /**\r\n     * 实例信息\r\n     */\r\n    private InstanceInfo instanceInfo;\r\n\r\n    /**\r\n     * 相关StatusEnum\r\n     */\r\n    private int status;\r\n\r\n    /**\r\n     * 详见CheckCycleEnum\r\n     */\r\n    private int checkCycle;\r\n\r\n    /**\r\n     * 配置更新时间\r\n     */\r\n    private Date updateTime;\r\n\r\n    /**\r\n     * 上次检测时间\r\n     */\r\n    private Date lastCheckTime;\r\n\r\n    /**\r\n     * 重要度（参照ImportantLevelTypeEnum）（0:一般；1：重要；2：紧急）\r\n     */\r\n    private Integer importantLevel;\r\n\r\n    /**\r\n     * 应用类型（0：redis;)\r\n     */\r\n    private Integer appType;\r\n\r\n    public Date getUpdateTime() {\r\n        return (Date) updateTime.clone();\r\n    }\r\n\r\n    public void setUpdateTime(Date updateTime) {\r\n        this.updateTime = (Date) updateTime.clone();\r\n    }\r\n\r\n    public Date getLastCheckTime() {\r\n        return (Date) lastCheckTime.clone();\r\n    }\r\n\r\n    public void setLastCheckTime(Date lastCheckTime) {\r\n        this.lastCheckTime = (Date) lastCheckTime.clone();\r\n    }\r\n\r\n    public String getCompareInfo() {\r\n        return InstanceAlertCompareTypeEnum.getInstanceAlertCompareTypeEnum(compareType).getInfo();\r\n    }\r\n\r\n    public Long getCheckCycleMillionTime() {\r\n        if (InstanceAlertCheckCycleEnum.ONE_MINUTE.getValue() == checkCycle) {\r\n            return TimeUnit.MINUTES.toMillis(1);\r\n        } else if (InstanceAlertCheckCycleEnum.FIVE_MINUTE.getValue() == checkCycle) {\r\n            return TimeUnit.MINUTES.toMillis(5);\r\n        } else if (InstanceAlertCheckCycleEnum.HALF_HOUR.getValue() == checkCycle) {\r\n            return TimeUnit.MINUTES.toMillis(30);\r\n        } else if (InstanceAlertCheckCycleEnum.ONE_HOUR.getValue() == checkCycle) {\r\n            return TimeUnit.MINUTES.toMillis(60);\r\n        } else if (InstanceAlertCheckCycleEnum.ONE_DAY.getValue() == checkCycle) {\r\n            return TimeUnit.DAYS.toMillis(1);\r\n        }\r\n        return null;\r\n    }\r\n\r\n    public boolean isSpecail() {\r\n        return instanceId > 0 &&\r\n                (type == InstanceAlertTypeEnum.INSTANCE_ALERT.getValue() ||\r\n                        type == InstanceAlertTypeEnum.APP_ALERT.getValue());\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return JSONObject.toJSONString(this);\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceAlertValueResult.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport lombok.Data;\r\n\r\n/**\r\n * 实例报警结果\r\n * @author leifu\r\n */\r\n@Data\r\npublic class InstanceAlertValueResult {\r\n    \r\n    /**\r\n     * 实例报警配置\r\n     */\r\n    private InstanceAlertConfig instanceAlertConfig;\r\n    \r\n    /**\r\n     * 实例信息\r\n     */\r\n    private InstanceInfo instanceInfo;\r\n\r\n    /**\r\n     * 当前值\r\n     */\r\n    private String currentValue;\r\n    \r\n    /**\r\n     * 应用id\r\n     */\r\n    private long appId;\r\n    \r\n    /**\r\n     * 单位\r\n     */\r\n    private String unit;\r\n\r\n    /**\r\n     * 应用信息\r\n     */\r\n    private AppDesc appDesc;\r\n    \r\n    /**\r\n     * 其他信息\r\n     */\r\n    private String otherInfo;\r\n\r\n    public InstanceAlertValueResult() {\r\n    }\r\n\r\n    public InstanceAlertValueResult(InstanceAlertConfig instanceAlertConfig, InstanceInfo instanceInfo,\r\n            String currentValue, long appId, String unit) {\r\n        this.instanceAlertConfig = instanceAlertConfig;\r\n        this.instanceInfo = instanceInfo;\r\n        this.currentValue = currentValue;\r\n        this.appId = appId;\r\n        this.unit = unit;\r\n    }\r\n\r\n    public String getAlertMessage() {\r\n        return String.format(\"实际值为%s%s,%s预设值%s%s\", currentValue, unit, instanceAlertConfig.getCompareInfo(),\r\n                instanceAlertConfig.getAlertValue(), unit);\r\n    }\r\n    \r\n    @Override\r\n    public String toString() {\r\n        return JSONObject.toJSONString(this);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceCommandStats.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.text.ParseException;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\n\r\n\r\n@Data\r\npublic class InstanceCommandStats implements Comparable<InstanceCommandStats> {\r\n\r\n    /**\r\n     * 应用id\r\n     */\r\n    private long instanceId;\r\n\r\n    /**\r\n     * 收集时间:格式yyyyMMddHHmm/yyyyMMdd/yyyyMMddHH\r\n     */\r\n    private long collectTime;\r\n\r\n    /**\r\n     * 命令名称\r\n     */\r\n    private String commandName;\r\n\r\n    /**\r\n     * 命令执行次数\r\n     */\r\n    private long commandCount;\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n\r\n    /**\r\n     * 修改时间\r\n     */\r\n    private Date modifyTime;\r\n\r\n    @Override\r\n    public int compareTo(InstanceCommandStats o) {\r\n        if (o.commandCount > this.commandCount) {\r\n            return 1;\r\n        } else if (o.commandCount < this.commandCount) {\r\n            return -1;\r\n        }\r\n        return 0;\r\n    }\r\n    \r\n    public Long getTimeStamp() throws ParseException{\r\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmm\");\r\n        Date date = sdf.parse(String.valueOf(this.collectTime));\r\n        return date.getTime();\r\n    }\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n    public Date getModifyTime() {\r\n        return (Date) modifyTime.clone();\r\n    }\r\n\r\n    public void setModifyTime(Date modifyTime) {\r\n        this.modifyTime = (Date) modifyTime.clone();\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceConfig.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * 实例配置模板\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class InstanceConfig {\r\n\r\n    private long id;\r\n\r\n    /**\r\n     * 配置名:为了防止与key冲突\r\n     */\r\n    private String configKey;\r\n\r\n    /**\r\n     * 配置值:为了防止与value冲突\r\n     */\r\n    private String configValue;\r\n\r\n    /**\r\n     * 配置说明\r\n     */\r\n    private String info;\r\n\r\n    /**\r\n     * 更新时间\r\n     */\r\n    private Date updateTime;\r\n\r\n    /**\r\n     * Redis类型(参考ConstUtil)\r\n     */\r\n    private int type;\r\n\r\n    /**\r\n     * 状态，1有效0无效\r\n     */\r\n    private int status;\r\n\r\n    /**\r\n     * 配置项绑定redis版本主键id\r\n     */\r\n    private int versionId;\r\n\r\n    /**\r\n     * 是否可重置：0不可，1可重置 (默认值：1)\r\n     */\r\n    private int refresh = 1;\r\n\r\n    /**\r\n     * 取值类型（0：默认值 config_value；1：从主节点拷贝），默认0\r\n     */\r\n    private int valueType;\r\n\r\n    public InstanceConfig() {\r\n    }\r\n\r\n    public InstanceConfig(String configKey, String configValue){\r\n        this.configKey = configKey;\r\n        this.configValue = configValue;\r\n    }\r\n\r\n    public String getStatusDesc() {\r\n        if (1 == status) {\r\n            return \"有效\";\r\n        } else if (0 == status) {\r\n            return \"无效\";\r\n        } else {\r\n            return \"\";\r\n        }\r\n    }\r\n\r\n    public boolean isEffective() {\r\n        if (1 == getStatus()) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public Date getUpdateTime() {\r\n        return (Date) updateTime.clone();\r\n    }\r\n\r\n    public void setUpdateTime(Date updateTime) {\r\n        this.updateTime = (Date) updateTime.clone();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceFault.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.Date;\r\n\r\n@Data\r\npublic class InstanceFault implements Serializable {\r\n\r\n    private static final long serialVersionUID = 8141174905675892249L;\r\n\r\n    private int id;\r\n\r\n    private int appId;\r\n\r\n    private int instId;\r\n\r\n    private String ip;\r\n\r\n    private int port;\r\n\r\n    private int status;\r\n\r\n    private int type;\r\n\r\n    private Date createTime;\r\n\r\n    private String reason;\r\n\r\n    public String getTypeDesc() {\r\n        InstanceInfo info = new InstanceInfo();\r\n        info.setType(type);\r\n        return info.getTypeDesc();\r\n    }\r\n\r\n    public String getStatusDesc() {\r\n        InstanceInfo info = new InstanceInfo();\r\n        info.setStatus(this.status);\r\n        return info.getStatusDesc();\r\n    }\r\n\r\n    public Date getCreateTime() {\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        this.createTime = (Date) createTime.clone();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceInfo.java",
    "content": "package com.sohu.cache.entity;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.task.constant.InstanceInfoEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n/**\n * 实例信息\n * User: lingguo\n */\n@Data\npublic class InstanceInfo implements Serializable {\n    private static final long serialVersionUID = -903896025243493024L;\n    /**\n     * 实例id\n     */\n    private int id;\n    /**\n     * 应用id\n     */\n    private long appId;\n    /**\n     * host id\n     */\n    private long hostId;\n    /**\n     * ip\n     */\n    private String ip;\n    /**\n     * 端口\n     */\n    private int port;\n    /**\n     * 是否启用 0:节点异常,1:正常启用,2:节点下线\n     */\n    private int status;\n    /**\n     * 开启的内存\n     */\n    private int mem;\n    /**\n     * 连接数\n     */\n    private int conn;\n    /**\n     * 启动命令 或者 redis-sentinel的masterName\n     */\n    private String cmd;\n    private int type;\n    private String typeDesc;\n    private int masterInstanceId;\n    private String masterHost;\n    private int masterPort;\n    private String roleDesc;\n    private int groupId;\n    private Date updateTime;\n\n    public String getTypeDesc() {\n        if (type <= 0) {\n            typeDesc = \"\";\n        } else if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n            typeDesc = \"redis-cluster\";\n        } else if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            typeDesc = \"redis-sentinel\";\n        } else if (type == ConstUtils.CACHE_REDIS_STANDALONE) {\n            typeDesc = \"redis-standalone\";\n        }\n        return typeDesc;\n    }\n    public String getStatusDesc() {\n        InstanceStatusEnum instanceStatusEnum = InstanceStatusEnum.getByStatus(status);\n        if (instanceStatusEnum != null) {\n            return instanceStatusEnum.getInfo();\n        }\n        return \"\";\n    }\n    /**\n     * 判断当前节点是否下线\n     *\n     * @return\n     */\n    public boolean isOffline() {\n        return status == InstanceStatusEnum.OFFLINE_STATUS.getStatus() || status == InstanceStatusEnum.FORGET_STATUS.getStatus();\n    }\n    /**\n     * 判断当前节点是否在线\n     */\n    public boolean isOnline() {\n        return status == InstanceStatusEnum.GOOD_STATUS.getStatus();\n    }\n    public String getRoleDesc() {\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            return \"sentinel\";\n        } else {\n            return roleDesc;\n        }\n    }\n    public void setRoleDesc(BooleanEnum isMaster) {\n        if (isMaster == BooleanEnum.OTHER) {\n            roleDesc = \"未知\";\n        } else if (isMaster == BooleanEnum.TRUE) {\n            roleDesc = \"master\";\n        } else if (isMaster == BooleanEnum.FALSE) {\n            roleDesc = \"slave\";\n        }\n    }\n    public String getHostPort() {\n        return ip + \":\" + port;\n    }\n    public boolean isMemcached() {\n        return InstanceInfoEnum.InstanceTypeEnum.MEMCACHE.getType() == type;\n    }\n    public boolean isNutCracker() {\n        return InstanceInfoEnum.InstanceTypeEnum.NUTCRACKER.getType() == type;\n    }\n    public boolean isPika() {\n        return InstanceInfoEnum.InstanceTypeEnum.PIKA.getType() == type;\n    }\n    /**\n     * 是否是redis数据节点\n     *\n     * @return\n     */\n    public boolean isRedisData() {\n        if (type == InstanceInfoEnum.InstanceTypeEnum.REDIS_SERVER.getType()\n                || type == InstanceInfoEnum.InstanceTypeEnum.REDIS_CLUSTER.getType()) {\n            return true;\n        }\n        return false;\n    }\n    public Date getUpdateTime() {\n        if(updateTime != null){\n            return (Date) updateTime.clone();\n        }\n        return null;\n    }\n    public String getUpdateTimeDesc() {\n        if(updateTime != null){\n            SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n            return sdf.format((Date) updateTime.clone());\n        } else {\n            return \"\";\n        }\n    }\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = (Date) updateTime.clone();\n    }\n\n    @Override\n    public String toString() {\n        return \"InstanceInfo{\" +\n                \"id=\" + id +\n                \", appId=\" + appId +\n                \", hostId=\" + hostId +\n                \", ip='\" + ip + '\\'' +\n                \", port=\" + port +\n                \", status=\" + status +\n                \", mem=\" + mem +\n                \", conn=\" + conn +\n                \", cmd='\" + cmd + '\\'' +\n                \", type=\" + type +\n                \", typeDesc='\" + typeDesc + '\\'' +\n                \", masterInstanceId=\" + masterInstanceId +\n                \", masterHost='\" + masterHost + '\\'' +\n                \", masterPort=\" + masterPort +\n                \", roleDesc='\" + roleDesc + '\\'' +\n                \", groupId=\" + groupId +\n                \", updateTime=\" + updateTime +\n                '}';\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceLatencyHistory.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * @Author: rucao\n * @Date: 2020/5/7 3:12 下午\n */\n@Data\npublic class InstanceLatencyHistory {\n    private long id;\n    /**\n     * 实例id\n     */\n    private long instanceId;\n    /**\n     * app id\n     */\n    private long appId;\n    /**\n     * ip地址\n     */\n    private String ip;\n    /**\n     * port\n     */\n    private int port;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 延迟事件名称\n     */\n    private String event;\n    /**\n     * 事件出现延迟毛刺日期时间\n     */\n    private Date executeDate;\n    /**\n     * 延迟（单位：毫秒）\n     */\n    private long executionCost;\n\n    public InstanceLatencyHistory(long instanceId, long appId, String ip, int port, String event, Date executeDate, long executionCost) {\n        this.instanceId = instanceId;\n        this.appId = appId;\n        this.ip = ip;\n        this.port = port;\n        this.event = event;\n        this.executeDate = executeDate;\n        this.executionCost = executionCost;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceMinuteStats.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class InstanceMinuteStats extends InstanceCommandStats {\n\n    /**\n     * 内存碎片率\n     */\n    private double memFragmentationRatio;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceReshardProcess.java",
    "content": "package com.sohu.cache.entity;\n\nimport com.sohu.cache.constant.ReshardStatusEnum;\nimport lombok.Data;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n/**\n * 进度持久化\n *\n * @author leifu\n */\n@Data\npublic class InstanceReshardProcess {\n\n    private int id;\n\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 审批id\n     */\n    private long auditId;\n\n    /**\n     * 源实例id\n     */\n    private int sourceInstanceId;\n\n    /**\n     * 源实例\n     */\n    private InstanceInfo sourceInstanceInfo;\n\n    /**\n     * 目标实例id\n     */\n    private int targetInstanceId;\n\n    /**\n     * 目标实例\n     */\n    private InstanceInfo targetInstanceInfo;\n\n    /**\n     * 开始slot\n     */\n    private int startSlot;\n\n    /**\n     * 结束slot\n     */\n    private int endSlot;\n\n    /**\n     * 正在迁移的slot\n     */\n    private int migratingSlot;\n\n    /**\n     * 0是,1否\n     */\n    private int isPipeline;\n\n    /**\n     * 已完成迁移的slot数量\n     */\n    private int finishSlotNum;\n\n    /**\n     * 0:运行中 1:完成 2:出错\n     */\n    private int status;\n\n    /**\n     * 迁移开始时间\n     */\n    private Date startTime;\n\n    /**\n     * 迁移结束时间\n     */\n    private Date endTime;\n\n    /**\n     * 创建时间\n     *\n     * @return\n     */\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    private Date updateTime;\n\n    private final static String dateTimeFormat = \"yyyy-MM-dd HH:mm:ss\";\n\n    public String getStartTimeFormat() {\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateTimeFormat);\n        return simpleDateFormat.format(startTime);\n    }\n\n    public String getEndTimeFormat() {\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateTimeFormat);\n        return simpleDateFormat.format(endTime);\n    }\n\n    public String getCreateTimeFormat() {\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateTimeFormat);\n        return simpleDateFormat.format(createTime);\n    }\n\n    public String getUpdateTimeFormat() {\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateTimeFormat);\n        return simpleDateFormat.format(updateTime);\n    }\n\n    public int getTotalSlot() {\n        return endSlot - startSlot + 1;\n    }\n\n    public String getStatusDesc() {\n        ReshardStatusEnum reshardStatusEnum = ReshardStatusEnum.getReshardStatusEnum(status);\n        return reshardStatusEnum == null ? \"\" : reshardStatusEnum.getInfo();\n    }\n\n    public Date getCreateTime() {\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n\n    public Date getUpdateTime() {\n        return (Date) updateTime.clone();\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = (Date) updateTime.clone();\n    }\n\n    public Date getStartTime() {\n        return (Date) startTime.clone();\n    }\n\n    public void setStartTime(Date startTime) {\n        this.startTime = (Date) startTime.clone();\n    }\n\n    public Date getEndTime() {\n        return (Date) endTime.clone();\n    }\n\n    public void setEndTime(Date endTime) {\n        this.endTime = (Date) endTime.clone();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceSlotModel.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 实例slot\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class InstanceSlotModel {\r\n    /**\r\n     * slot分布，例如： 0-4096 或者0-8 9-4096\r\n     */\r\n    private List<String> slotDistributeList;\r\n\r\n    /**\r\n     * slot列表\r\n     */\r\n    private List<Integer> slotList;\r\n\r\n    /**\r\n     * ip\r\n     */\r\n    private String host;\r\n\r\n    /**\r\n     * 端口\r\n     */\r\n    private int port;\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceSlowLog.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\nimport org.apache.commons.lang.StringUtils;\r\n\r\nimport java.sql.Timestamp;\r\n\r\n/**\r\n * 实例慢查询日志\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class InstanceSlowLog {\r\n\r\n    private long id;\r\n\r\n    /**\r\n     * 实例id\r\n     */\r\n    private long instanceId;\r\n\r\n    /**\r\n     * app id\r\n     */\r\n    private long appId;\r\n\r\n    /**\r\n     * ip地址\r\n     */\r\n    private String ip;\r\n\r\n    /**\r\n     * port\r\n     */\r\n    private int port;\r\n\r\n    /**\r\n     * 慢查询日志id\r\n     */\r\n    private long slowLogId;\r\n\r\n    /**\r\n     * 耗时\r\n     */\r\n    private int costTime;\r\n\r\n    /**\r\n     * 命令\r\n     */\r\n    private String command;\r\n\r\n    /**\r\n     * 记录创建时间\r\n     */\r\n    private Timestamp createTime;\r\n\r\n    /**\r\n     * 慢查询发生时间\r\n     */\r\n    private Timestamp executeTime;\r\n\r\n    public String getCommand() {\r\n        int maxLength = 30;\r\n        if (StringUtils.isNotBlank(command) && command.length() > maxLength) {\r\n            return command.substring(0, maxLength) + \"...\";\r\n        }\r\n        return command;\r\n    }\r\n\r\n    public Timestamp getCreateTime() {\r\n        return (Timestamp) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Timestamp createTime) {\r\n        this.createTime = (Timestamp) createTime.clone();\r\n    }\r\n\r\n    public Timestamp getExecuteTime() {\r\n        return (Timestamp) executeTime.clone();\r\n    }\r\n\r\n    public void setExecuteTime(Timestamp executeTime) {\r\n        this.executeTime = (Timestamp) executeTime.clone();\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceStats.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.sql.Timestamp;\r\nimport java.text.DecimalFormat;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 实例的简化的统计信息\r\n *\r\n * User: lingguo\r\n */\r\n@Data\r\npublic class InstanceStats {\r\n    /* id */\r\n    private long id;\r\n\r\n    /* 实例id */\r\n    private long instId;\r\n\r\n    /* app id */\r\n    private long appId;\r\n\r\n    /* host id */\r\n    private long hostId;\r\n\r\n    /* ip地址 */\r\n    private String ip;\r\n\r\n    /* port */\r\n    private int port;\r\n\r\n    /* 主从，1主2从 */\r\n    private byte role;\r\n\r\n    /* 启用实例时设置的内存，单位：byte */\r\n    private long maxMemory;\r\n\r\n    /* 实例当前已用的内存，单位：byte */\r\n    private long usedMemory;\r\n\r\n    /*\r\n     * 实例内存使用率\r\n     */\r\n    private double memUsePercent;\r\n\r\n    /* 当前的item数 */\r\n    private long currItems;\r\n\r\n    /* 当前的连接数 */\r\n    private int currConnections;\r\n\r\n    /* 未命中数*/\r\n    private long misses;\r\n\r\n    /* 命中数 */\r\n    private long hits;\r\n\r\n    /* 开始收集时间 */\r\n    private Timestamp createTime;\r\n\r\n    /* 最后更新时间 */\r\n    private Timestamp modifyTime;\r\n    \r\n    /**\r\n     * 内存碎片率\r\n     */\r\n    private double memFragmentationRatio;\r\n    \r\n    /**\r\n     * aof阻塞次数\r\n     */\r\n    private int aofDelayedFsync;\r\n\r\n    private boolean isRun;\r\n\r\n    /**\r\n     * 实例相关全部统计指标\r\n     */\r\n    private Map<String,Object> infoMap;\r\n\r\n    /* 实例当前已用的磁盘空间，单位：byte */\r\n    private long usedDisk;\r\n\r\n    public double getMemUsePercent() {\r\n        if(maxMemory<=0){\r\n            return 0.0D;\r\n        }\r\n        double percent = 100 * (double) usedMemory / (maxMemory);\r\n        DecimalFormat df = new DecimalFormat(\"##.##\");\r\n        return Double.parseDouble(df.format(percent));\r\n    }\r\n    \r\n    /**\r\n     * 命中率\r\n     * @return\r\n     */\r\n    public String getHitPercent(){\r\n\t\tlong totalHits = hits + misses;\r\n\t\tif (totalHits <= 0) {\r\n\t\t\treturn \"无命令执行\";\r\n\t\t}\r\n\t\tdouble percent = 100 * (double) hits / totalHits;\r\n\t\tDecimalFormat df = new DecimalFormat(\"##.##\");\r\n\t\treturn df.format(percent) + \"%\";\r\n    }\r\n\r\n    public Timestamp getCreateTime() {\r\n        if(createTime != null){\r\n            return (Timestamp) createTime.clone();\r\n        }\r\n        return null;\r\n    }\r\n\r\n    public void setCreateTime(Timestamp createTime) {\r\n        this.createTime = (Timestamp) createTime.clone();\r\n    }\r\n\r\n    public Timestamp getModifyTime() {\r\n        return (Timestamp) modifyTime.clone();\r\n    }\r\n\r\n    public void setModifyTime(Timestamp modifyTime) {\r\n        this.modifyTime = (Timestamp) modifyTime.clone();\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/LoginResult.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport com.sohu.cache.web.enums.AdminEnum;\r\nimport com.sohu.cache.web.enums.LoginEnum;\r\nimport lombok.Data;\r\n\r\n/**\r\n * 登录结果\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class LoginResult {\r\n    /**\r\n     * 登录验证结果\r\n     */\r\n    private LoginEnum loginEnum;\r\n\r\n    /**\r\n     * 是否是管理员\r\n     */\r\n    private AdminEnum adminEnum;\r\n\r\n    public LoginResult(LoginEnum loginEnum, AdminEnum adminEnum) {\r\n        this.loginEnum = loginEnum;\r\n        this.adminEnum = adminEnum;\r\n    }\r\n\r\n    public LoginResult() {\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/MachineInfo.java",
    "content": "package com.sohu.cache.entity;\r\n\r\n\r\nimport com.sohu.cache.constant.MachineInfoEnum;\r\nimport io.swagger.annotations.ApiModel;\r\nimport io.swagger.annotations.ApiModelProperty;\r\nimport lombok.Data;\r\n\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\n\r\n/**\r\n * 机器的属性信息\r\n * <p>\r\n */\r\n@Data\r\n@ApiModel\r\npublic class MachineInfo {\r\n    /**\r\n     * 机器id\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private long id;\r\n\r\n    /**\r\n     * ssh用户名\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private String sshUser;\r\n\r\n    /**\r\n     * ssh密码\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private String sshPasswd;\r\n\r\n    /**\r\n     * ip地址\r\n     */\r\n    @ApiModelProperty(value = \"机器ip\", required = true)\r\n    private String ip;\r\n\r\n    /**\r\n     * 机房\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private String room;\r\n\r\n    /**\r\n     * 内存，单位G\r\n     */\r\n    @ApiModelProperty(value = \"内存，单位G\", required = true)\r\n    private int mem;\r\n\r\n    /**\r\n     * cpu数量\r\n     */\r\n    @ApiModelProperty(value = \"cpu核数\", required = true)\r\n    private int cpu;\r\n\r\n    /**\r\n     * 磁盘空间, 单位G\r\n     */\r\n    @ApiModelProperty(value = \"磁盘空间\",required = true)\r\n    private int disk;\r\n\r\n    /**\r\n     * 是否虚机，0否，1是\r\n     */\r\n    @ApiModelProperty(value = \"是否虚拟机\", required = true)\r\n    private int virtual;\r\n\r\n    /**\r\n     * 宿主机ip\r\n     */\r\n    @ApiModelProperty(value = \"宿主机ip（虚机需要填写）\", required = true)\r\n    private String realIp;\r\n\r\n    /**\r\n     * 上线时间\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private Date serviceTime;\r\n\r\n    /**\r\n     * 故障次数\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private int faultCount;\r\n\r\n    /**\r\n     * 修改时间\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private Date modifyTime;\r\n\r\n    /**\r\n     * 是否启用报警，0否，1是\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private int warn;\r\n\r\n    /**\r\n     * 是否可用，MachineInfoEnum.AvailableEnum\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private int available;\r\n\r\n    /**\r\n     * 机器类型：详见MachineInfoEnum.TypeEnum\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private int type;\r\n\r\n\r\n    /**\r\n     * 机器类型：详见MachineInfoEnum.DisTypeEnum\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private int disType;\r\n\r\n\r\n    /**\r\n     * groupId\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private int groupId;\r\n\r\n    /**\r\n     * 额外说明:(例如本机器有其他web或者其他服务)\r\n     */\r\n    @ApiModelProperty(value = \"备注说明\")\r\n    private String extraDesc;\r\n\r\n    @ApiModelProperty(value = \"如是专用机器,请填写项目名称\")\r\n    private String projectName;\r\n\r\n    /**\r\n     * 是否收集服务器信息，0否，1是\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private int collect;\r\n\r\n    /**\r\n     * redis版本安装情况 版本号#安装标识  -1:获取安装信息异常 0:未安装 1:安装成功\r\n     */\r\n    @ApiModelProperty(hidden = true)\r\n    private String versionInstall;\r\n\r\n    /**\r\n     * 使用类型：Redis专用机器（0），Redis测试机器（1），混合部署机器（2）\r\n     */\r\n    @ApiModelProperty(value = \"使用类型：Redis专用机器（0），Redis测试机器（1），混合部署机器（2），Redis sentienl部署（3）\", required = true)\r\n    private int useType;\r\n\r\n    @ApiModelProperty(value = \"机器是否分配，1是0否\")\r\n    private int isAllocating;\r\n\r\n    /**\r\n     * 是否k8s容器：0:不是 1:是\r\n     */\r\n    @ApiModelProperty(value = \"是否k8s容器：0:不是 1:是\", required = false)\r\n    private int k8sType;\r\n\r\n    @ApiModelProperty(value = \"pod变更时间，单位 ms\", required = false)\r\n    private long podUpdateTime;\r\n\r\n    @ApiModelProperty(value = \"机架信息\", required = false)\r\n    private String rack;\r\n\r\n    /**\r\n     * 判断机器是否已经下线\r\n     *\r\n     * @return\r\n     */\r\n    public boolean isOffline() {\r\n        return MachineInfoEnum.AvailableEnum.NO.getValue() == this.available;\r\n    }\r\n\r\n    /**\r\n     * 是否是云主机\r\n     *\r\n     * @return\r\n     */\r\n    public boolean isYunMachine() {\r\n        //return isYun == 1;\r\n        return false;\r\n    }\r\n\r\n    public boolean isK8sMachine(int k8sType) {\r\n        if (k8sType == 1) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    /**\r\n     * 时间格式化\r\n     */\r\n    public String getUpdateTimeFormat() {\r\n        return new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(modifyTime);\r\n    }\r\n\r\n    public Date getModifyTime() {\r\n        if(modifyTime == null){\r\n            return null;\r\n        }\r\n        return (Date) modifyTime.clone();\r\n    }\r\n\r\n    public void setModifyTime(Date modifyTime) {\r\n        if(modifyTime != null){\r\n            this.modifyTime = (Date) modifyTime.clone();\r\n        }\r\n    }\r\n\r\n    public Date getServiceTime() {\r\n        if(serviceTime == null){\r\n            return null;\r\n        }\r\n        return (Date) serviceTime.clone();\r\n    }\r\n\r\n    public void setServiceTime(Date serviceTime) {\r\n        if(serviceTime != null){\r\n            this.serviceTime = (Date) serviceTime.clone();\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/MachineInstanceStat.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n@Data\npublic class MachineInstanceStat {\n\n\tprivate String ip;\n\n    private long maxMemory;\n\t\n\tprivate long usedMemory;\n\n\tprivate long applyDisk;\n\n\tprivate long usedDisk;\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/MachineMemInfo.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * Created by hym on 14-10-30.\n */\n@Data\npublic class MachineMemInfo {\n    private String ip;\n    private long applyMem;\n    private long usedMem;\n    private long applyDisk;//磁盘分配\n\n    private long usedDisk;//磁盘实例使用\n    private double usedMemRss;\n\n    public long getLockedMem() {\n        return this.applyMem - this.usedMem;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/MachineMemStatInfo.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * Created by rucao\n */\n@Data\npublic class MachineMemStatInfo {\n    /**\n     * 机器id\n     */\n    private long id;\n    /**\n     * ip地址\n     */\n    private String ip;\n    /**\n     * 机房\n     */\n    private String room;\n    /**\n     * 使用类型：Redis专用机器（0），Redis测试机器（1），混合部署机器（2），Redis迁移工具机器（3）\n     */\n    private int useType;\n    /**\n     * cpu数量\n     */\n    private int cpu;\n\n    private int instanceNum;\n    /**\n     * 内存，单位G\n     */\n    private int mem;\n    /**\n     * 该机器已分配的内存, 单位byte\n     */\n    private long applyMem;\n    /**\n     * 该机器的已使用的内存, 单位byte\n     */\n    private long usedMem;\n    /**\n     * 宿主机ip\n     */\n    private String realIp;\n    /**\n     * 机架信息\n     */\n    private String rack;\n\n    /**\n     * 该机器的redis部署实际使用内存, 单位byte\n     */\n    private Long usedMemRss;\n\n    /**\n     * 操作系统发行版本，0:centos;1:ubuntu\n     */\n    private Integer disType;\n\n    /**\n     * 该机器的已使用的磁盘, 单位byte\n     */\n    private long usedDisk;\n\n    /**\n     * 机器磁盘总大小, 单位G\n     */\n    private long disk;\n\n    /**\n     * 类型：如Redis机器（0），RediSSD机器（6）\n     * 详见 MachineInfoEnum.TypeEnum\n     */\n    private int type;\n\n    public double getCpuUsage(){\n        if(cpu != 0){\n            return ((double)instanceNum)/cpu;\n        }\n        return 0;\n    }\n\n    public long getFreeMem(){\n        if(usedMemRss == null){\n            return mem * 1024;\n        }\n        return mem * 1024 - usedMemRss/1024/1024;\n    }\n\n    public long getFreeApplyMem(){\n        return mem * 1024 - applyMem/1024/1024;\n    }\n\n    public long getFreeDisk(){\n        return disk * 1024 - usedDisk/1024/1024;\n    }\n\n    public void addUsedMem(long mem){\n        this.usedMem += mem;\n        this.usedMemRss += mem;\n    }\n\n    public void addApplyMem(long mem){\n        this.applyMem += mem;\n    }\n\n    public void addUsedDisk(long disk){\n        this.usedDisk += disk;\n    }\n\n    public void addInstanceNum(int instanceNum){\n        this.instanceNum += instanceNum;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/MachineRelation.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * Created by chenshi on 2019/5/21.\n */\n@Data\npublic class MachineRelation {\n\n    /**\n     * 主键id\n     */\n    private int id;\n\n    /**\n     * 虚拟ip\n     */\n    private String ip;\n\n    /**\n     * 宿主机ip\n     */\n    private String realIp;\n\n    /**\n     * 更新时间\n     */\n    private Date updateTime;\n\n    /**\n     * pod 描述信息\n     */\n    private String extraDesc;\n\n    /**\n     * pod状态 0:offline 下线  1：online 上线  2：pending\n     * PodStatusEnum.OFFLINE   PodStatusEnum.ONLINE\n     */\n    private int status;\n\n    private int isSync;\n\n    private String syncTime;\n\n    /**\n     * 任务id\n     */\n    private long taskid;\n\n    public MachineRelation() {\n    }\n\n    public MachineRelation(String ip, String realIp, Date updateTime, String extraDesc, int status) {\n        this.ip = ip;\n        this.realIp = realIp;\n        this.updateTime = updateTime;\n        this.extraDesc = extraDesc;\n        this.status = status;\n    }\n\n    public Date getUpdateTime() {\n        return (Date) updateTime.clone();\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = (Date) updateTime.clone();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/MachineRoom.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * <p>\n * Description: 机房信息\n * </p>\n *\n * @author chenshi\n */\n@Data\npublic class MachineRoom {\n\n    /**\n     * 机房id\n     */\n    private int id;\n\n    /**\n     * 机房名称\n     */\n    private String name;\n\n    /**\n     * 是否有效 0:无效 1:有效\n     */\n    private int status;\n\n    /**\n     * 机房信息\n     */\n    private String desc;\n\n    /**\n     * 网段信息\n     */\n    private String ipNetwork;\n\n    /**\n     * 运营商\n     */\n    private String operator;\n\n    public MachineRoom() {\n    }\n\n    public MachineRoom(String name, int status, String desc, String ipNetwork, String operator) {\n        this.name = name;\n        this.status = status;\n        this.desc = desc;\n        this.ipNetwork = ipNetwork;\n        this.operator = operator;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/MachineStats.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\nimport org.apache.commons.lang.StringUtils;\r\n\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\nimport java.util.Map;\r\n\r\n/**\r\n * Created by yijunzhang\r\n */\r\n@Data\r\npublic class MachineStats{\r\n    private long id;\r\n\r\n    private long hostId;\r\n\r\n    private String ip;\r\n\r\n    private String cpuUsage;\r\n\r\n    private String load;\r\n\r\n    private String traffic;\r\n\r\n    private String memoryUsageRatio;\r\n\r\n    /**\r\n     * 如果机器是物理机/虚机，收集内存占用空间正常;如果是容器，收集可能不是实际内存占用空间\r\n     */\r\n    private String memoryFree;\r\n\r\n    private String memoryTotal;\r\n\r\n    private int memoryAllocated;\r\n    /**\r\n     * 机器入库内存 MB\r\n     */\r\n    private int machineMemory;\r\n    /**\r\n     * 分配内存 MB\r\n     */\r\n    private int maxMemory;\r\n    /**\r\n     * 机器实例使用内存总和 MB\r\n     */\r\n    private int usedMemory;\r\n\r\n    private MachineInfo info;\r\n\r\n    private MachineMemInfo machineMemInfo;\r\n\r\n    private String versionInfo;\r\n\r\n    private Map<String/**挂载点*/, String/**使用百分比*/> diskUsageMap;\r\n\r\n    /**\r\n     * 实例个数\r\n     */\r\n    private int instanceCount;\r\n\r\n\r\n\r\n    /**\r\n     * 创建时间\r\n     */\r\n    private Date createTime;\r\n\r\n    /**\r\n     * 修改时间\r\n     */\r\n    private Date modifyTime;\r\n\r\n    /**\r\n     * Redis是否全部安装成功 0:未全部安装  1:全部安装成功\r\n     */\r\n    private int isInstall;\r\n\r\n    private String diskTotal;\r\n\r\n    private String diskAvailable;\r\n\r\n    private String diskUsageRatio;\r\n\r\n    /**\r\n     * 时间格式化\r\n     */\r\n    public String getUpdateTimeFormat() {\r\n        return new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(modifyTime);\r\n    }\r\n\r\n    //统计数据是否有效\r\n    public boolean validate() {\r\n        if (StringUtils.isBlank(load)\r\n                && StringUtils.isBlank(cpuUsage)\r\n                && StringUtils.isBlank(memoryFree)\r\n                && StringUtils.isBlank(memoryTotal)) {\r\n            return false;\r\n        }\r\n        return true;\r\n    }\r\n\r\n    public Date getCreateTime() {\r\n        if(null == createTime){\r\n            return null;\r\n        }\r\n        return (Date) createTime.clone();\r\n    }\r\n\r\n    public void setCreateTime(Date createTime) {\r\n        if(null == createTime){\r\n            this.createTime = null;\r\n        }else{\r\n            this.createTime = (Date) createTime.clone();\r\n        }\r\n    }\r\n\r\n    public Date getModifyTime() {\r\n        if(null == modifyTime){\r\n            return null;\r\n        }\r\n        return (Date) modifyTime.clone();\r\n    }\r\n\r\n    public void setModifyTime(Date modifyTime) {\r\n        if(null == modifyTime){\r\n            this.modifyTime = null;\r\n        }else{\r\n            this.modifyTime = (Date) modifyTime.clone();\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/OperationAlertValueResult.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * Created by rucao\n */\n@Data\npublic class OperationAlertValueResult {\n    String ip;\n    MachineInfo machineInfo;\n    String type;\n    String status;\n    String message;\n\n    public OperationAlertValueResult(String ip, MachineInfo machineInfo, String type, String status, String message) {\n        this.ip = ip;\n        this.machineInfo = machineInfo;\n        this.type = type;\n        this.status = status;\n        this.message = message;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/ParamCount.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * 用于group by结果\n * @author fulei\n */\n@Data\npublic class ParamCount {\n\n\tprivate String param;\n\t\n\tprivate double count;\n\t\n\tprivate String url;\n\n\tpublic ParamCount() {\n\t}\n\n\t/**\n\t * @param param\n\t * @param count\n\t * @param url\n\t */\n\tpublic ParamCount(String param, double count, String url) {\n\t\tsuper();\n\t\tthis.param = param;\n\t\tthis.count = count;\n\t\tthis.url = url;\n\t}\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/RedisVersion.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * Created by chenshi on 2018/8/23.\n */\n@Data\npublic class RedisVersion {\n    /**\n     * 主键id\n     */\n    private int id;\n    /**\n     * redis版本名称 格式:redis-主.子.小版本号\n     */\n    private String name;\n    /**\n     * 是否有效 1:有效 0:无效\n     */\n    private int status;\n    /**\n     * 安装目录\n     */\n    private String dir;\n    /**\n     * redis大版本号  格式:redis-主.子版本号\n     */\n    private String groups;\n    /**\n     * 大版本默认版本号(推荐)\n     */\n    private int isBind;\n\n    /**\n     * 资源id\n     */\n    private int resourceId;\n\n    public RedisVersion() {\n    }\n\n    public RedisVersion(int id) {\n        this.id = id;\n    }\n\n    public RedisVersion(String name, int status) {\n        this.name = name;\n        this.status = status;\n    }\n\n    public RedisVersion(String name, int status, String dir) {\n        this.name = name;\n        this.status = status;\n        this.dir = dir;\n    }\n\n    public RedisVersion(String name, int status, String dir, String groups, int isBind, int resourceId) {\n        this.name = name;\n        this.status = status;\n        this.dir = dir;\n        this.groups = groups;\n        this.isBind = isBind;\n        this.resourceId = resourceId;\n    }\n\n    public RedisVersion(int id, String name, int status, String dir, String groups, int isBind, int resourceId) {\n        this.id = id;\n        this.name = name;\n        this.status = status;\n        this.dir = dir;\n        this.groups = groups;\n        this.isBind = isBind;\n        this.resourceId = resourceId;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/RedisVersionStat.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\n/**\n * Redis版本管理统计信息\n */\n@Data\npublic class RedisVersionStat {\n\n    /**\n     * Redis版本信息\n     */\n    SystemResource redisVersion;\n\n    /**\n     * 已安装机器数量\n     */\n    int installNum;\n\n    /**\n     * 未安装机器数量\n     */\n    int uninstallNum;\n\n    /**\n     * 安装异常机器数量\n     */\n    int installExceptionNum;\n\n    /**\n     * 机器总量\n     */\n    int totalMachineNum;\n\n    /**\n     * 已安装比例 num%\n     */\n    int installRatio;\n\n    /**\n     * 应用使用数量\n     */\n    int appUsedNum;\n\n    /**\n     * 总应用数量\n     */\n    int totalAppNum;\n\n    /**\n     * 未安装机器ip\n     */\n    int unInstallIp;\n\n    public RedisVersionStat() {\n    }\n\n    public RedisVersionStat(SystemResource redisVersion) {\n        this.redisVersion = redisVersion;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/ServerInfo.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\n/**\r\n * 服务器信息\r\n */\r\n@Data\r\npublic class ServerInfo {\r\n\tprivate String ip;\r\n\tprivate String host;\r\n\t//逻辑cpu个数\r\n\tprivate int cpus;\r\n\t//nmon版本\r\n\tprivate String nmon;\r\n\t//cpu型号\r\n\tprivate String cpuModel;\r\n\t//内核版本\r\n\tprivate String kernel;\r\n\t//发行版本\r\n\tprivate String dist;\r\n\t//ulimit\r\n\tprivate String ulimit;\r\n\t// gcc version\r\n\tprivate String gcc;\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/ServerStatus.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\n/**\r\n * 服务器状态\r\n */\r\n@Data\r\npublic class ServerStatus {\r\n\tprivate String cdate;\r\n\tprivate String ctime;\r\n\tprivate float cuser;\r\n\tprivate float csys;\r\n\tprivate float cwio;\r\n\tprivate String cExt;\r\n\tprivate float cload1;\r\n\tprivate float cload5;\r\n\tprivate float cload15;\r\n\tprivate float mtotal;\r\n\tprivate float mfree;\r\n\tprivate float mcache;\r\n\tprivate float mbuffer;\r\n\tprivate float mswap;\r\n\tprivate float mswapFree;\r\n\tprivate float nin;\r\n\tprivate float nout;\r\n\tprivate int tuse;\r\n\tprivate int torphan;\r\n\tprivate int twait;\r\n\tprivate String ninExt;\r\n\tprivate String noutExt;\r\n\tprivate float dread;\r\n\tprivate float dwrite;\r\n\tprivate float diops;\r\n\tprivate float dbusy;\r\n\tprivate String dExt;\r\n\tprivate String dspace;\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/StandardStats.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\nimport net.sf.json.JSONObject;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * 实例基准统计\n * Created by zhangyijun\n */\n@Data\npublic class StandardStats {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    /**\n     * id\n     */\n    private long id;\n\n    /**\n     * 实例IP\n     */\n    private String ip;\n\n    /**\n     * 实例端口号/hostId\n     */\n    private int port;\n\n    /**\n     * 实例类型\n     */\n    private String dbType;\n\n    /**\n     * 收集时间:格式yyyyMMddHHmm\n     */\n    private long collectTime;\n\n    /**\n     * 实例收集的json数据\n     */\n    private String infoJson;\n\n    /**\n     * 与上一次收集差异的json数据\n     */\n    private String diffJson;\n\n    /**\n     * 实例收集的cluster info json数据\n     */\n    private String clusterInfoJson;\n\n    /**\n     * infoJson的Map输出\n     */\n    private Map<String, Object> infoMap;\n\n    /**\n     * diffJson的Map输出\n     */\n    private Map<String, Object> diffMap;\n\n    /**\n     * clusterInfoJson的Map输出\n     */\n    private Map<String, Object> clusterInfoMap;\n\n    private Date createdTime;\n\n    public Map<String, Object> getInfoMap() {\n        if (infoMap != null) {\n            return infoMap;\n        } else {\n            if (StringUtils.isNotBlank(infoJson)) {\n                JSONObject jsonObject;\n                try {\n                    jsonObject = JSONObject.fromObject(infoJson);\n                    Map<String, Object> map = transferMapByJson(jsonObject);\n                    infoMap = map;\n                } catch (Exception e) {\n                    logger.error(e.getMessage());\n                }\n            }\n        }\n        return infoMap;\n    }\n\n    public void setInfoMap(Map<String, Object> infoMap) {\n        if (infoJson == null) {\n            JSONObject jsonObject;\n            try {\n                jsonObject = JSONObject.fromObject(infoMap);\n                infoJson = jsonObject.toString();\n            } catch (Exception e) {\n                logger.error(e.getMessage());\n            }\n        }\n        this.infoMap = infoMap;\n    }\n\n    public Map<String, Object> getDiffMap() {\n        if (diffMap != null) {\n            return diffMap;\n        } else {\n            if (StringUtils.isNotBlank(diffJson)) {\n                JSONObject jsonObject;\n                try {\n                    jsonObject = JSONObject.fromObject(diffJson);\n                    Map<String, Object> map = transferMapByJson(jsonObject);\n                    diffMap = map;\n                } catch (Exception e) {\n                    logger.error(e.getMessage());\n                }\n            }\n        }\n        return diffMap;\n    }\n\n    /**\n     * 递归转换JsonObject\n     *\n     * @param jsonObject\n     * @return\n     */\n    private Map<String, Object> transferMapByJson(JSONObject jsonObject) {\n        Map<String, Object> map = new LinkedHashMap<String, Object>();\n        for (Iterator keys = jsonObject.keys(); keys.hasNext(); ) {\n            String key = String.valueOf(keys.next());\n            Object value = jsonObject.get(key);\n            if (value instanceof JSONObject) {\n                JSONObject subJsonObject = (JSONObject) value;\n                Map<String, Object> subMap = transferMapByJson(subJsonObject);\n                map.put(key, subMap);\n            } else {\n                map.put(key, value);\n            }\n        }\n        return map;\n    }\n\n    public void setDiffMap(Map<String, Object> diffMap) {\n        if (diffJson == null) {\n            JSONObject jsonObject;\n            try {\n                jsonObject = JSONObject.fromObject(diffMap);\n                diffJson = jsonObject.toString();\n            } catch (Exception e) {\n                logger.error(e.getMessage());\n            }\n        }\n        this.diffMap = diffMap;\n    }\n\n    public Map<String, Object> getClusterInfoMap() {\n        if (clusterInfoMap != null) {\n            return clusterInfoMap;\n        } else {\n            if (StringUtils.isNotBlank(clusterInfoJson)) {\n                JSONObject jsonObject;\n                try {\n                    jsonObject = JSONObject.fromObject(clusterInfoJson);\n                    Map<String, Object> map = transferMapByJson(jsonObject);\n                    clusterInfoMap = map;\n                } catch (Exception e) {\n                    logger.error(e.getMessage());\n                }\n            }\n        }\n        return clusterInfoMap;\n    }\n\n    public void setClusterInfoMap(Map<String, Object> clusterInfoMap) {\n        if (clusterInfoJson == null) {\n            JSONObject jsonObject;\n            try {\n                jsonObject = JSONObject.fromObject(clusterInfoMap);\n                clusterInfoJson = jsonObject.toString();\n            } catch (Exception e) {\n                logger.error(e.getMessage());\n            }\n        }\n        this.clusterInfoMap = clusterInfoMap;\n    }\n\n    public Date getCreatedTime() {\n        return (Date) createdTime.clone();\n    }\n\n    public void setCreatedTime(Date createdTime) {\n        this.createdTime = (Date) createdTime.clone();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/SystemConfig.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport lombok.Data;\r\n\r\n/**\r\n * 系统配置\r\n *\r\n * @author leifu\r\n */\r\n@Data\r\npublic class SystemConfig {\r\n\r\n    private String configKey;\r\n\r\n    private String configValue;\r\n\r\n    private String info;\r\n\r\n    private int status;\r\n\r\n    private int orderId;\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/SystemResource.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * Created by chenshi on 2020/7/6.\n */\n@Data\npublic class SystemResource {\n\n    private int id;\n\n    private String name;\n\n    private String intro;\n\n    private int type;\n\n    private String dir;\n\n    private Date lastmodify;\n\n    private String url;\n\n    private int status;\n\n    private int ispush;\n\n    private String username;\n\n    private long taskId;\n\n    private String compileInfo;\n\n    private int orderNum;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/ThreadPoolStat.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * @author fulei\n * @date 2018年8月11日\n * @time 上午11:16:22\n */\n@Data\npublic class ThreadPoolStat {\n\n    private long id;\n\n    /**\n     * 进程ip\n     */\n    private String ip;\n\n    /**\n     * 进程port\n     */\n    private int port;\n\n    private String threadPoolName;\n\n    private long collectTime;\n\n    private Date collectDate;\n\n    private int maximumPoolSize;\n\n    private int corePoolSize;\n\n    private int activeCount;\n\n    private long diffTaskCount;\n\n    private long taskCount;\n\n    private long completedTaskCount;\n\n    private int queueSize;\n\n    private Date createTime;\n\n    private Date updateTime;\n\n    public Date getCreateTime() {\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n\n    public Date getUpdateTime() {\n        return (Date) updateTime.clone();\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = (Date) updateTime.clone();\n    }\n\n    public Date getCollectDate() {\n        return (Date) collectDate.clone();\n    }\n\n    public void setCollectDate(Date collectDate) {\n        this.collectDate = (Date) collectDate.clone();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/TimeBetween.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n/**\n * @author leifu\n */\n@Data\npublic class TimeBetween {\n\n    private long startTime;\n\n    private long endTime;\n\n    private Date startDate;\n\n    private Date endDate;\n\n    public TimeBetween() {\n    }\n\n    public TimeBetween(long startTime, long endTime, Date startDate, Date endDate) {\n        this.startTime = startTime;\n        this.endTime = endTime;\n        this.startDate = startDate;\n        this.endDate = endDate;\n    }\n\n    public String getFormatStartDate() {\n        String dateFormat = \"yyyy-MM-dd\";\n        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);\n        if (startDate != null) {\n            return sdf.format(startDate);\n        }\n        return \"\";\n    }\n\n    public String getFormatStartDate(String dateFormat) {\n        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);\n        if (startDate != null) {\n            return sdf.format(startDate);\n        }\n        return \"\";\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/TimeDimensionality.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport com.sohu.cache.dao.AppStatsDao;\r\nimport com.sohu.cache.web.util.DateUtil;\r\nimport lombok.Data;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * Created by yijunzhang\r\n */\r\n@Data\r\npublic class TimeDimensionality {\r\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\r\n\r\n    private final long begin;\r\n\r\n    private final long end;\r\n\r\n    private final int dimensionality;\r\n\r\n    public TimeDimensionality(long begin, long end, String format) {\r\n        Date beginDate = DateUtil.getDateByFormat(String.valueOf(begin), format);\r\n        Date endDate = DateUtil.getDateByFormat(String.valueOf(end), format);\r\n        this.dimensionality = getSuitableDimensionality(beginDate, endDate);\r\n        if (dimensionality == AppStatsDao.MINUTE_DIMENSIONALITY) {\r\n            this.begin = Long.parseLong(DateUtil.formatDate(beginDate, \"yyyyMMddHHmm\"));\r\n        } else {\r\n            this.begin = Long.parseLong(DateUtil.formatDate(beginDate, \"yyyyMMddHH\"));\r\n        }\r\n\r\n        if (dimensionality == AppStatsDao.MINUTE_DIMENSIONALITY) {\r\n            this.end = Long.parseLong(DateUtil.formatDate(endDate, \"yyyyMMddHHmm\"));\r\n        } else {\r\n            this.end = Long.parseLong(DateUtil.formatDate(endDate, \"yyyyMMddHH\"));\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 获取合适的维度\r\n     */\r\n    private int getSuitableDimensionality(Date begin, Date end) {\r\n        try {\r\n            long s1 = begin.getTime();\r\n            long s2 = end.getTime();\r\n            long hour = (s2 - s1) / 1000 / 60 / 60;\r\n            if (hour >= 48) {\r\n                return AppStatsDao.HOUR_DIMENSIONALITY;\r\n            } else {\r\n                return AppStatsDao.MINUTE_DIMENSIONALITY;\r\n            }\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        return AppStatsDao.MINUTE_DIMENSIONALITY;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/TopologyExamResult.java",
    "content": "package com.sohu.cache.entity;\n\nimport lombok.Data;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n/**\n * Created by rucao on 2019/1/31\n */\n@Data\npublic class TopologyExamResult {\n    private long id;\n\n    private long appId;\n\n    private String type;\n\n    private String status;\n\n    private String description;\n\n    private Date createTime;\n\n    public TopologyExamResult(long appId, String type, String status, String description){\n        this.appId=appId;\n        this.type=type;\n        this.status=status;\n        this.description=description;\n        this.createTime=new Date();\n    }\n\n    public String getCreateTimeFormat() {\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        if (createTime != null) {\n            return sdf.format(createTime);\n        }\n        return \"\";\n    }\n\n    public Date getCreateTime() {\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/entity/TriggerInfo.java",
    "content": "package com.sohu.cache.entity;\r\n\r\nimport com.sohu.cache.web.util.DateUtil;\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * trigger的信息及状态\r\n */\r\n@Data\r\npublic class TriggerInfo {\r\n    private String schedName;\r\n    private String triggerName;\r\n    private String triggerGroup;\r\n    private String jobName;\r\n    private String jobGroup;\r\n    private String description;\r\n    private long nextFireTime;\r\n    private String nextFireDate;\r\n    private long prevFireTime;\r\n    private String prevFireDate;\r\n    private int priority;\r\n    private String triggerState;\r\n    private String triggerType;\r\n    private long startTime;\r\n    private String startDate;\r\n    private long endTime;\r\n    private String endDate;\r\n    private String calendarName;\r\n    private short misfireInstr;\r\n    private String cron;\r\n\r\n    public String getNextFireDate() {\r\n        if (nextFireTime > 0) {\r\n            return DateUtil.formatYYYYMMddHHMMSS(new Date(nextFireTime));\r\n        }\r\n        return \"\";\r\n    }\r\n\r\n    public String getPrevFireDate() {\r\n        if (prevFireTime > 0) {\r\n            return DateUtil.formatYYYYMMddHHMMSS(new Date(prevFireTime));\r\n        }\r\n        return \"\";\r\n    }\r\n\r\n    public String getStartDate() {\r\n        if (startTime > 0) {\r\n            return DateUtil.formatYYYYMMddHHMMSS(new Date(startTime));\r\n        }\r\n        return \"\";\r\n    }\r\n\r\n    public String getEndDate() {\r\n        if (endTime > 0) {\r\n            return DateUtil.formatYYYYMMddHHMMSS(new Date(endTime));\r\n        }\r\n        return \"\";\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/exception/IllegalParamException.java",
    "content": "package com.sohu.cache.exception;\r\n\r\n/**\r\n * 参数异常\r\n * \r\n * @author leifu\r\n * @Date 2016-1-26\r\n * @Time 下午9:21:03\r\n */\r\npublic class IllegalParamException extends Exception {\r\n    \r\n    private static final long serialVersionUID = -1148039976867829902L;\r\n\r\n    public IllegalParamException() {\r\n        super();\r\n    }\r\n\r\n    public IllegalParamException(String message) {\r\n        super(message);\r\n    }\r\n\r\n    public IllegalParamException(String message, Throwable cause) {\r\n        super(message, cause);\r\n    }\r\n\r\n    public IllegalParamException(Throwable cause) {\r\n        super(cause);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/exception/SSHException.java",
    "content": "package com.sohu.cache.exception;\r\n\r\n/**\r\n * ssh异常\r\n * @author leifu\r\n * @Date 2016-1-26\r\n * @Time 下午9:18:54\r\n */\r\npublic class SSHException extends Exception {\r\n\r\n    private static final long serialVersionUID = -6213665149000064880L;\r\n\r\n    public SSHException() {\r\n        super();\r\n    }\r\n\r\n    public SSHException(String message) {\r\n        super(message);\r\n    }\r\n\r\n    public SSHException(String message, Throwable cause) {\r\n        super(message, cause);\r\n    }\r\n\r\n    public SSHException(Throwable cause) {\r\n        super(cause);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/InspectHandler.java",
    "content": "package com.sohu.cache.inspect;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic interface InspectHandler {\r\n\r\n    public void handle();\r\n\r\n    public void setInspectorList(List<Inspector> inspectorList);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/InspectParamEnum.java",
    "content": "package com.sohu.cache.inspect;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic enum InspectParamEnum {\r\n    /**\r\n     * 分组字段:\r\n     * HostInspectHandler 表示host\r\n     * AppInspectHandler 表示appId\r\n     */\r\n    SPLIT_KEY(\"split_key\"),\r\n    INSTANCE_LIST(\"instance_list\");\r\n\r\n    private String value;\r\n\r\n    InspectParamEnum(String value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public String value(){\r\n        return value;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/Inspector.java",
    "content": "package com.sohu.cache.inspect;\r\n\r\nimport java.util.Map;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic interface Inspector {\r\n\r\n    /**\r\n     * 执行检测逻辑\r\n     *\r\n     * @return\r\n     */\r\n    public boolean inspect(Map<InspectParamEnum, Object> paramMap);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/InspectorJob.java",
    "content": "package com.sohu.cache.inspect;\r\n\r\nimport com.sohu.cache.schedule.jobs.CacheBaseJob;\r\nimport com.sohu.cache.util.EnvUtil;\r\nimport org.apache.commons.collections.MapUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.quartz.JobDataMap;\r\nimport org.quartz.JobExecutionContext;\r\nimport org.quartz.SchedulerContext;\r\nimport org.springframework.context.ApplicationContext;\r\nimport org.springframework.core.env.Environment;\r\n\r\n/**\r\n * Created by yijunzhang\r\n */\r\npublic class InspectorJob extends CacheBaseJob {\r\n    private static final long serialVersionUID = -4277329946053271489L;\r\n\r\n    @Override\r\n    public void action(JobExecutionContext context) {\r\n        try {\r\n            long start = System.currentTimeMillis();\r\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\r\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\r\n\r\n            Environment env = applicationContext.getBean(Environment.class);\r\n            if (EnvUtil.isDev(env)) {\r\n                logger.warn(\"environment is dev ignored\");\r\n                return;\r\n            }\r\n            // 应用相关\r\n            InspectHandler inspectHandler;\r\n            JobDataMap jobDataMap = context.getMergedJobDataMap();\r\n            String inspectorType = MapUtils.getString(jobDataMap, \"inspectorType\");\r\n            if (StringUtils.isBlank(inspectorType)) {\r\n                logger.error(\"=====================InspectorJob:inspectorType is null=====================\");\r\n                return;\r\n            } else if (inspectorType.equals(\"host\")) {\r\n                inspectHandler = applicationContext.getBean(\"hostInspectHandler\", InspectHandler.class);\r\n            } else if (inspectorType.equals(\"app\")) {\r\n                inspectHandler = applicationContext.getBean(\"appInspectHandler\", InspectHandler.class);\r\n            } else {\r\n                logger.error(\"=====================InspectorJob:inspectorType not match:{}=====================\", inspectorType);\r\n                return;\r\n            }\r\n            inspectHandler.handle();\r\n            long end = System.currentTimeMillis();\r\n            logger.info(\"=====================InspectorJob {} Done! cost={} ms=====================\",\r\n                    inspectHandler.getClass().getSimpleName(), (end - start));\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            throw new RuntimeException(e);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/AbstractInspectHandler.java",
    "content": "package com.sohu.cache.inspect.impl;\r\n\r\nimport com.sohu.cache.async.AsyncService;\r\nimport com.sohu.cache.async.KeyCallable;\r\nimport com.sohu.cache.async.NamedThreadFactory;\r\nimport com.sohu.cache.dao.InstanceDao;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.inspect.InspectHandler;\r\nimport com.sohu.cache.inspect.InspectParamEnum;\r\nimport com.sohu.cache.inspect.Inspector;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.concurrent.LinkedBlockingQueue;\r\nimport java.util.concurrent.ThreadPoolExecutor;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic abstract class AbstractInspectHandler implements InspectHandler {\r\n    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\r\n    @Autowired\r\n    protected InstanceDao instanceDao;\r\n    @Autowired\r\n    protected AsyncService asyncService;\r\n\r\n    protected List<Inspector> inspectorList;\r\n\r\n    protected abstract String getThreadPoolKey();\r\n\r\n    protected abstract Map<String, List<InstanceInfo>> getSplitMap();\r\n\r\n    public void init() {\r\n        asyncService.assemblePool(getThreadPoolKey(), new ThreadPoolExecutor(10, 100,\r\n                0L, TimeUnit.MILLISECONDS,\r\n                new LinkedBlockingQueue<Runnable>(1024),\r\n                new NamedThreadFactory(getThreadPoolKey(), true)));\r\n    }\r\n\r\n    public void handle() {\r\n        if (inspectorList == null || inspectorList.isEmpty()) {\r\n            logger.warn(\"inspectorList is null\");\r\n            return;\r\n        }\r\n        Map<String, List<InstanceInfo>> splitMap = getSplitMap();\r\n        for (Map.Entry<String, List<InstanceInfo>> entry : splitMap.entrySet()) {\r\n            String splitKey = entry.getKey();\r\n            List<InstanceInfo> instances = entry.getValue();\r\n            final Map<InspectParamEnum, Object> paramMap = new HashMap<InspectParamEnum, Object>();\r\n            paramMap.put(InspectParamEnum.SPLIT_KEY, splitKey);\r\n            paramMap.put(InspectParamEnum.INSTANCE_LIST, instances);\r\n            String key = getThreadPoolKey() + \"-\" + splitKey;\r\n            asyncService.submitFuture(getThreadPoolKey(), new KeyCallable<Boolean>(key) {\r\n                @Override\r\n                public Boolean execute() {\r\n                    for (Inspector inspector : inspectorList) {\r\n                        boolean isSuccess = false;\r\n                        try {\r\n                            isSuccess = inspector.inspect(paramMap);\r\n                        } catch (Throwable e) {\r\n                            logger.error(e.getMessage(), e);\r\n                        }\r\n                        if (!isSuccess) {\r\n                            logger.error(getThreadPoolKey() + \"-failed:\" + inspector.getClass().getName());\r\n                            return false;\r\n                        }\r\n                    }\r\n                    return true;\r\n                }\r\n            });\r\n        }\r\n    }\r\n\r\n    public void setInspectorList(List<Inspector> inspectorList) {\r\n        this.inspectorList = inspectorList;\r\n    }\r\n\r\n    public List<InstanceInfo> getAllInstanceList() {\r\n        return instanceDao.getAllInsts();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/AppClientConnInspector.java",
    "content": "package com.sohu.cache.inspect.impl;\r\n\r\nimport com.sohu.cache.alert.impl.BaseAlertService;\r\nimport com.sohu.cache.entity.AppDesc;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.entity.InstanceStats;\r\nimport com.sohu.cache.inspect.InspectParamEnum;\r\nimport com.sohu.cache.inspect.Inspector;\r\nimport com.sohu.cache.stats.app.AppStatsCenter;\r\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.TypeUtil;\r\nimport com.sohu.cache.web.enums.AlertTypeEnum;\r\nimport com.sohu.cache.web.vo.AppDetailVO;\r\n\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.apache.commons.collections.MapUtils;\r\n\r\nimport java.util.Arrays;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 应用客户端连接监控\r\n * \r\n * @author leifu\r\n * @Date 2016年6月16日\r\n * @Time 上午9:44:34\r\n */\r\npublic class AppClientConnInspector extends BaseAlertService implements Inspector {\r\n\r\n    /**\r\n     * app统计相关\r\n     */\r\n    private AppStatsCenter appStatsCenter;\r\n\r\n    /**\r\n     * 实例统计相关\r\n     */\r\n    private InstanceStatsCenter instanceStatsCenter;\r\n\r\n    @Override\r\n    public boolean inspect(Map<InspectParamEnum, Object> paramMap) {\r\n        Long appId = MapUtils.getLong(paramMap, InspectParamEnum.SPLIT_KEY);\r\n        AppDetailVO appDetailVO = appStatsCenter.getAppDetail(appId);\r\n        if (appDetailVO == null) {\r\n            logger.warn(\"appId {} appDetailVO is empty\", appId);\r\n            return true;\r\n        }\r\n        List<InstanceInfo> appInstanceInfoList = (List<InstanceInfo>) paramMap.get(InspectParamEnum.INSTANCE_LIST);\r\n        if (CollectionUtils.isEmpty(appInstanceInfoList)) {\r\n            logger.warn(\"appId {} instanceList is empty\", appId);\r\n            return true;\r\n        }\r\n        // 报警阀值\r\n        int appClientConnThreshold = getClientConnThreshold(appDetailVO.getAppDesc());\r\n        int appClientConnNum = appDetailVO.getConn();\r\n        // 阀值乘以分片个数\r\n        int instanceCount = appInstanceInfoList.size();\r\n        if (appClientConnNum > appClientConnThreshold * instanceCount) {\r\n            alertAppClientConn(appDetailVO, appClientConnThreshold, instanceCount);\r\n        } else {\r\n            for (InstanceInfo instanceInfo : appInstanceInfoList) {\r\n                if (instanceInfo == null) {\r\n                    continue;\r\n                }\r\n                if (instanceInfo.isOffline()) {\r\n                    continue;\r\n                }\r\n                if (!TypeUtil.isRedisType(instanceInfo.getType())) {\r\n                    continue;\r\n                }\r\n                // 忽略sentinel观察者\r\n                if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\r\n                    continue;\r\n                }\r\n                long instanceId = instanceInfo.getId();\r\n                InstanceStats instanceStats = instanceStatsCenter.getInstanceStats(instanceId);\r\n                if (instanceStats == null) {\r\n                    continue;\r\n                }\r\n                double instanceClientConnNum = instanceStats.getCurrConnections();\r\n                // 大于标准值\r\n                if (instanceClientConnNum > appClientConnThreshold) {\r\n                    alertInstanceClientConn(instanceStats, appDetailVO, appClientConnThreshold);\r\n                }\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n\r\n    /**\r\n     * 获取报警阀值(如果用户预设超过系统预设，以系统为准，反之以用户为准)\r\n     * @param appDesc\r\n     * @return\r\n     */\r\n    private int getClientConnThreshold(AppDesc appDesc) {\r\n        int userClientConnThreshold = appDesc.getClientConnAlertValue();\r\n        int systemClientConnThreshold =  ConstUtils.APP_CLIENT_CONN_THRESHOLD;\r\n        return userClientConnThreshold > systemClientConnThreshold ? systemClientConnThreshold : userClientConnThreshold;\r\n    }\r\n\r\n    /**\r\n     * 应用连接数报警\r\n     * @param appDetailVO\r\n     * @param appClientConnThreshold\r\n     * @param instanceCount\r\n     */\r\n    private void alertAppClientConn(final AppDetailVO appDetailVO, final int appClientConnThreshold, final int instanceCount) {\r\n        AppDesc appDesc = appDetailVO.getAppDesc();\r\n        String content = String.format(\"应用(%s)-客户端连接数报警-预设阀值每个分片为%s-现已达到%s(分片个数:%s)-请及时关注\",\r\n                appDesc.getAppId(), appClientConnThreshold, appDetailVO.getConn(), instanceCount);\r\n        String title = \"CacheCloud系统-客户端连接数报警\";\r\n        logger.warn(\"app title {}\", title);\r\n        logger.warn(\"app content {}\", content);\r\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.APP_CLIENT_CONNECTION, title, content, appDetailVO);\r\n        emailComponent.sendMail(title, content, appDetailVO.getEmailList(),\r\n                Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\r\n        weChatComponent.sendWeChatToAll(title,content,appDetailVO.getWeChatList());\r\n    }\r\n\r\n    /**\r\n     * 单个分片连接数报警\r\n     * @param instanceStats\r\n     * @param appDetailVO\r\n     * @param appClientConnThreshold\r\n     */\r\n    private void alertInstanceClientConn(final InstanceStats instanceStats, final AppDetailVO appDetailVO,\r\n            final int appClientConnThreshold) {\r\n        String instanceHostPort = instanceStats.getIp() + \":\" + instanceStats.getPort();\r\n        String content = String.format(\"分片(%s,应用(%s))客户端连接数报警-预设%s-现已达到%s-请及时关注\", instanceHostPort,\r\n                instanceStats.getAppId(), appClientConnThreshold, instanceStats.getCurrConnections());\r\n        String title = \"CacheCloud系统-分片客户端连接数报警\";\r\n        logger.warn(\"instance title {}\", title);\r\n        logger.warn(\"instace content {}\", content);\r\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.APP_SHARD_CLENT_CONNECTION, title, content, appDetailVO, instanceStats);\r\n        emailComponent.sendMail(title, content, appDetailVO.getEmailList(),\r\n                Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\r\n        weChatComponent.sendWeChatToAll(title,content,appDetailVO.getWeChatList());\r\n    }\r\n\r\n    public void setAppStatsCenter(AppStatsCenter appStatsCenter) {\r\n        this.appStatsCenter = appStatsCenter;\r\n    }\r\n\r\n    public void setInstanceStatsCenter(InstanceStatsCenter instanceStatsCenter) {\r\n        this.instanceStatsCenter = instanceStatsCenter;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/AppHitPrecentInspector.java",
    "content": "package com.sohu.cache.inspect.impl;\n\nimport com.sohu.cache.alert.impl.BaseAlertService;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.inspect.InspectParamEnum;\nimport com.sohu.cache.inspect.Inspector;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by chenshi on 2017/9/11.\n */\npublic class AppHitPrecentInspector extends BaseAlertService implements Inspector {\n\n    /**\n     * app统计相关\n     */\n    private AppStatsCenter appStatsCenter;\n\n    /**\n     * 应用相关dao\n     */\n    private AppDao appDao;\n\n    @Override\n    public boolean inspect(Map<InspectParamEnum, Object> paramMap) {\n\n        Long appId = MapUtils.getLong(paramMap, InspectParamEnum.SPLIT_KEY);\n        List<AppDesc> appDescList = new ArrayList<AppDesc>();\n        AppDesc app = appDao.getAppDescById(appId);\n        if (app != null) {\n            appDescList.add(app);\n        }\n        if (CollectionUtils.isEmpty(appDescList)) {\n            logger.error(\"appList is empty, appId={}\", appId);\n            return true;\n        }\n        // 执行检查逻辑\n        for (AppDesc appDesc : appDescList) {\n            // 测试不检查\n            if (appDesc.getIsTest() == 1) {\n                continue;\n            }\n            long checkAppid = appDesc.getAppId();\n            // 监控命中率阀值(阀值为0不监控)\n            int hitprecent_alertValue = appDesc.getHitPrecentAlertValue();\n            if (hitprecent_alertValue == 0) {\n                //logger.error(\"ignore hitprcent monitor, appId={}\", appId);\n                return true;\n            }\n\n            AppDetailVO appDetailVO = appStatsCenter.getAppDetail(checkAppid);\n            if (appDetailVO == null) {\n                continue;\n            }\n            // 全局命中率\n            double hitPercent = appDetailVO.getHitPercent();\n            if (hitPercent < hitprecent_alertValue) {\n                // 报警\n                alertAppHitPrecnt(appDetailVO);\n            }\n        }\n        return false;\n    }\n\n    /**\n     * <p>\n     * Description:命中率低于监控阀值\n     * </p>\n     *\n     * @param appDetailVO 应用信息\n     * @return void\n     * @author chenshi\n     * @version 1.0\n     * @date 2017/9/11\n     */\n    private void alertAppHitPrecnt(final AppDetailVO appDetailVO) {\n        AppDesc appDesc = appDetailVO.getAppDesc();\n        String content = String.format(\"应用(%s)-应用平均命中率报警-当前命中率百分之%s-现已低于预设百分之%s-请及时关注\",\n                appDesc.getAppId(), appDetailVO.getHitPercent(), appDesc.getHitPrecentAlertValue());\n        String title = \"CacheCloud系统-应用平均命中率报警\";\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.APP_HIT_RATIO, title, content, appDetailVO);\n        emailComponent.sendMail(title, content, appDetailVO.getEmailList(),\n                Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n        weChatComponent.sendWeChatToAll(title,content,appDetailVO.getWeChatList());\n    }\n\n    public void setAppStatsCenter(AppStatsCenter appStatsCenter) {\n        this.appStatsCenter = appStatsCenter;\n    }\n\n    public void setAppDao(AppDao appDao) {\n        this.appDao = appDao;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/AppInspectHandler.java",
    "content": "package com.sohu.cache.inspect.impl;\r\n\r\nimport com.sohu.cache.entity.InstanceInfo;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.TreeMap;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic class AppInspectHandler extends AbstractInspectHandler{\r\n    private final static String inspectPoolKey=\"inspector-app-pool\";\r\n\r\n    @Override\r\n    public String getThreadPoolKey() {\r\n        return inspectPoolKey;\r\n    }\r\n\r\n    @Override\r\n    protected Map<String, List<InstanceInfo>> getSplitMap() {\r\n        List<InstanceInfo> list = getAllInstanceList();\r\n        Map<String, List<InstanceInfo>> hostMap = new TreeMap<String, List<InstanceInfo>>();\r\n        for (InstanceInfo instanceInfo : list) {\r\n            String appId = String.valueOf(instanceInfo.getAppId());\r\n            if (hostMap.containsKey(appId)) {\r\n                hostMap.get(appId).add(instanceInfo);\r\n            } else {\r\n                List<InstanceInfo> hostInstances = new ArrayList<InstanceInfo>();\r\n                hostInstances.add(instanceInfo);\r\n                hostMap.put(appId, hostInstances);\r\n            }\r\n        }\r\n        return hostMap;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/AppMemInspector.java",
    "content": "package com.sohu.cache.inspect.impl;\r\n\r\nimport com.sohu.cache.alert.impl.BaseAlertService;\r\nimport com.sohu.cache.dao.AppDao;\r\nimport com.sohu.cache.entity.AppDesc;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.entity.InstanceStats;\r\nimport com.sohu.cache.inspect.InspectParamEnum;\r\nimport com.sohu.cache.inspect.Inspector;\r\nimport com.sohu.cache.stats.app.AppStatsCenter;\r\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.TypeUtil;\r\nimport com.sohu.cache.web.enums.AlertTypeEnum;\r\nimport com.sohu.cache.web.service.AppAutoCapacityService;\r\nimport com.sohu.cache.web.vo.AppDetailVO;\r\n\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.apache.commons.collections.MapUtils;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Arrays;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic class AppMemInspector extends BaseAlertService implements Inspector {\r\n\r\n    /**\r\n     * app统计相关\r\n     */\r\n    private AppStatsCenter appStatsCenter;\r\n\r\n    /**\r\n     * 应用相关dao\r\n     */\r\n    private AppDao appDao;\r\n\r\n    /**\r\n     * 实例统计相关\r\n     */\r\n    private InstanceStatsCenter instanceStatsCenter;\r\n\r\n    /**\r\n     * 自动扩容\r\n     */\r\n    private AppAutoCapacityService appAutoCapacityService;\r\n\r\n    @Override\r\n    public boolean inspect(Map<InspectParamEnum, Object> paramMap) {\r\n        Long appId = MapUtils.getLong(paramMap, InspectParamEnum.SPLIT_KEY);\r\n        List<AppDesc> appDescList = new ArrayList<AppDesc>();\r\n        AppDesc app = appDao.getAppDescById(appId);\r\n        if (app != null) {\r\n            appDescList.add(app);\r\n        }\r\n        if (CollectionUtils.isEmpty(appDescList)) {\r\n            logger.error(\"appList is empty, appId={}\", appId);\r\n            return true;\r\n        }\r\n        for (AppDesc appDesc : appDescList) {\r\n            //测试不检查\r\n            if(appDesc.getIsTest() == 1){\r\n                continue;\r\n            }\r\n            long checkAppId = appDesc.getAppId();\r\n            AppDetailVO appDetailVO = appStatsCenter.getAppDetail(checkAppId);\r\n            if (appDetailVO == null) {\r\n                continue;\r\n            }\r\n            double appMemUsePercent = appDetailVO.getMemUsePercent();\r\n            double appDiskUsePercent = appDetailVO.getDiskUsePercent();\r\n            int appUseSetMemAlertValue = appDesc.getMemAlertValue();\r\n\r\n            Map<InstanceInfo, InstanceStats> instanceStatsMap = new HashMap<>();\r\n            List<InstanceInfo> appInstanceInfoList = (List<InstanceInfo>) paramMap.get(InspectParamEnum.INSTANCE_LIST);\r\n            if (CollectionUtils.isNotEmpty(appInstanceInfoList)) {\r\n                for (InstanceInfo instanceInfo : appInstanceInfoList) {\r\n                    if (instanceInfo == null) {\r\n                        continue;\r\n                    }\r\n                    if (!TypeUtil.isRedisType(instanceInfo.getType())) {\r\n                        continue;\r\n                    }\r\n                    // 忽略sentinel观察者\r\n                    if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\r\n                        continue;\r\n                    }\r\n                    long instanceId = instanceInfo.getId();\r\n                    InstanceStats instanceStats = instanceStatsCenter.getInstanceStats(instanceId);\r\n                    instanceStatsMap.put(instanceInfo, instanceStats);\r\n                }\r\n            }\r\n            // 先检查应用的内存使用率是否超过阀值，如果没有再检查分片\r\n            if (appMemUsePercent > appUseSetMemAlertValue) {\r\n                // 报警\r\n                alertAppMemUse(appDetailVO);\r\n            } else {\r\n                if (CollectionUtils.isNotEmpty(appInstanceInfoList)) {\r\n                    for (InstanceInfo instanceInfo : appInstanceInfoList) {\r\n                        if (instanceInfo == null) {\r\n                            continue;\r\n                        }\r\n                        if (!TypeUtil.isRedisType(instanceInfo.getType())) {\r\n                            continue;\r\n                        }\r\n                        // 忽略sentinel观察者\r\n                        if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\r\n                            continue;\r\n                        }\r\n                        long instanceId = instanceInfo.getId();\r\n                        InstanceStats instanceStats = instanceStatsMap.get(instanceInfo);\r\n                        if(instanceStats == null){\r\n                            continue;\r\n                        }\r\n                        double instanceMemUsePercent = instanceStats.getMemUsePercent();\r\n                        // 大于标准值\r\n                        if (instanceMemUsePercent > appUseSetMemAlertValue) {\r\n                            alertInstanceMemUse(instanceStats, appDetailVO);\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            appAutoCapacityService.checkAndExpandCapacity(appDesc, Double.valueOf(appMemUsePercent).intValue(), instanceStatsMap);\r\n        }\r\n        return true;\r\n    }\r\n\r\n    /**\r\n     * @param appDetailVO\r\n     */\r\n    private void alertAppMemUse(final AppDetailVO appDetailVO) {\r\n        AppDesc appDesc = appDetailVO.getAppDesc();\r\n        String content = String.format(\"应用(%s)-内存使用率报警-预设百分之%s-现已达到百分之%s-请及时关注\",\r\n                appDesc.getAppId(), appDesc.getMemAlertValue(), appDetailVO.getMemUsePercent());\r\n        String title = \"CacheCloud系统-应用内存使用率报警\";\r\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.APP_MEM_USED_RATIO, title, content, appDetailVO);\r\n        emailComponent.sendMail(title, content, appDetailVO.getEmailList(),\r\n                Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\r\n        weChatComponent.sendWeChatToAll(title,content,appDetailVO.getWeChatList());\r\n\r\n    }\r\n\r\n    private void alertInstanceMemUse(final InstanceStats instanceStats, final AppDetailVO appDetailVO) {\r\n        String instanceInfo = instanceStats.getIp() + \":\" + instanceStats.getPort();\r\n        String content = String.format(\"分片(%s,应用(%s))内存使用率报警-预设百分之%s-现已达到百分之%s-应用的内存使用率百分之%s-请及时关注\",\r\n                instanceInfo,\r\n                instanceStats.getAppId(), appDetailVO.getAppDesc().getMemAlertValue(),\r\n                instanceStats.getMemUsePercent(), appDetailVO.getMemUsePercent());\r\n        String title = \"CacheCloud系统-分片内存使用率报警\";\r\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.APP_SHARD_MEM_USED_RATIO, title, content, appDetailVO, instanceStats);\r\n        emailComponent.sendMail(title, content, appDetailVO.getEmailList(),\r\n                Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\r\n        weChatComponent.sendWeChatToAll(title,content,appDetailVO.getWeChatList());\r\n    }\r\n\r\n    public void setAppStatsCenter(AppStatsCenter appStatsCenter) {\r\n        this.appStatsCenter = appStatsCenter;\r\n    }\r\n\r\n    public void setAppDao(AppDao appDao) {\r\n        this.appDao = appDao;\r\n    }\r\n\r\n    public void setInstanceStatsCenter(InstanceStatsCenter instanceStatsCenter) {\r\n        this.instanceStatsCenter = instanceStatsCenter;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/HostInspectHandler.java",
    "content": "package com.sohu.cache.inspect.impl;\r\n\r\nimport com.sohu.cache.entity.InstanceInfo;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.TreeMap;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-20.\r\n */\r\npublic class HostInspectHandler extends AbstractInspectHandler{\r\n    private final static String inspectPoolKey=\"inspector-host-pool\";\r\n\r\n    @Override\r\n    public String getThreadPoolKey() {\r\n        return inspectPoolKey;\r\n    }\r\n\r\n    @Override\r\n    protected Map<String, List<InstanceInfo>> getSplitMap() {\r\n        List<InstanceInfo> list = getAllInstanceList();\r\n        Map<String, List<InstanceInfo>> hostMap = new TreeMap<String, List<InstanceInfo>>();\r\n        for (InstanceInfo instanceInfo : list) {\r\n            String host = instanceInfo.getIp();\r\n            if (hostMap.containsKey(host)) {\r\n                hostMap.get(host).add(instanceInfo);\r\n            } else {\r\n                List<InstanceInfo> hostInstances = new ArrayList<InstanceInfo>();\r\n                hostInstances.add(instanceInfo);\r\n                hostMap.put(host, hostInstances);\r\n            }\r\n        }\r\n        return hostMap;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/InstanceRunInspector.java",
    "content": "package com.sohu.cache.inspect.impl;\n\nimport com.sohu.cache.alert.impl.BaseAlertService;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.dao.InstanceFaultDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceFault;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.inspect.InspectParamEnum;\nimport com.sohu.cache.inspect.Inspector;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport org.apache.commons.collections4.MapUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by yijunzhang on 15-1-20.\n */\npublic class InstanceRunInspector extends BaseAlertService implements Inspector {\n\n    /**\n     * 实例相关\n     */\n    @Autowired\n    private InstanceDao instanceDao;\n\n    /**\n     * redis相关\n     */\n    @Autowired\n    private RedisCenter redisCenter;\n\n    /**\n     * 应用相关dao\n     */\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private InstanceFaultDao instanceFaultDao;\n\n    @Override\n    public boolean inspect(Map<InspectParamEnum, Object> paramMap) {\n        String host = MapUtils.getString(paramMap, InspectParamEnum.SPLIT_KEY);\n        List<InstanceInfo> list = (List<InstanceInfo>) paramMap.get(InspectParamEnum.INSTANCE_LIST);\n        for (InstanceInfo info : list) {\n            final int port = info.getPort();\n            final int type = info.getType();\n            long appId = info.getAppId();\n            if (TypeUtil.isRedisType(type)) {\n                boolean isRun;\n                if (TypeUtil.isRedisSentinel(type)) {\n                    isRun = redisCenter.isRun(host, port);\n                } else {\n                    isRun = redisCenter.isRun(appId, host, port);\n                }\n                BooleanEnum isUpdate = updateInstanceByRun(isRun, info);\n                // 错误\n                if (isUpdate != BooleanEnum.OTHER) {\n                    alertInstanceInfo(info);\n                }\n\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * 邮箱+短信\n     *\n     * @param info\n     */\n    private void alertInstanceInfo(InstanceInfo info) {\n        sendEmailAlert(info);\n    }\n\n    /**\n     * 发送邮箱报警\n     *\n     * @param info\n     */\n    private void sendEmailAlert(InstanceInfo info) {\n        if (info == null) {\n            return;\n        }\n        String title = \"实例(\" + info.getIp() + \":\" + info.getPort() + \")状态发生变化\";\n        String message = generateMessage(info, true);\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.INSTANCE_RUNNING_STATE_CHANGE, title, message, info);\n        emailComponent.sendMailToAdmin(title, message);\n    }\n\n    /**\n     * 返回示例消息\n     *\n     * @param info\n     * @return\n     */\n    private String generateMessage(InstanceInfo info, boolean isEmail) {\n        StringBuffer message = new StringBuffer();\n        long appId = info.getAppId();\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        message.append(\"CacheCloud系统-实例(\" + info.getIp() + \":\" + info.getPort() + \")-\");\n        if (info.getStatus() == InstanceStatusEnum.ERROR_STATUS.getStatus()) {\n            message.append(\"由运行中变为心跳停止\");\n        } else if (info.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n            message.append(\"由心跳停止变为运行中\");\n        }\n        if (isEmail) {\n            message.append(\", appId:\");\n            message.append(appId + \"-\" + appDesc.getName());\n        } else {\n            message.append(\"-appId(\" + appId + \"-\" + appDesc.getName() + \")\");\n        }\n\n        return message.toString();\n    }\n\n    private void saveFault(InstanceInfo info, boolean isRun) {\n        InstanceFault instanceFault = new InstanceFault();\n        instanceFault.setAppId((int) info.getAppId());\n        instanceFault.setInstId(info.getId());\n        instanceFault.setIp(info.getIp());\n        instanceFault.setPort(info.getPort());\n        instanceFault.setType(info.getType());\n        instanceFault.setCreateTime(new Date());\n        if (isRun) {\n            instanceFault.setReason(\"恢复运行\");\n        } else {\n            instanceFault.setReason(\"心跳停止\");\n        }\n        instanceFaultDao.insert(instanceFault);\n    }\n\n    private BooleanEnum updateInstanceByRun(boolean isRun, InstanceInfo info) {\n        try {\n            InstanceInfo info_new = instanceDao.getInstanceInfoById(info.getId());\n            info.setStatus(info_new.getStatus());\n            if (isRun) {\n                if (info.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    info.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\n                    instanceDao.update(info);\n                    logger.warn(\"instance:{} instance is run\", info);\n                    saveFault(info, isRun);\n                    return BooleanEnum.TRUE;\n                }\n            } else {\n                if (info.getStatus() != InstanceStatusEnum.ERROR_STATUS.getStatus()\n                        && info.getStatus() != InstanceStatusEnum.OFFLINE_STATUS.getStatus()) {\n                    info.setStatus(InstanceStatusEnum.ERROR_STATUS.getStatus());\n                    instanceDao.update(info);\n                    logger.error(\"instance:{} instance failed\", info);\n                    saveFault(info, isRun);\n                    return BooleanEnum.FALSE;\n                }else if(info.getStatus() == InstanceStatusEnum.OFFLINE_STATUS.getStatus()){\n                    logger.error(\"instance:{} instance is offline\", info);\n                    saveFault(info, isRun);\n                    return BooleanEnum.OTHER;\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return BooleanEnum.OTHER;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/InstanceStateInspector.java",
    "content": "package com.sohu.cache.inspect.impl;\n\nimport com.sohu.cache.alert.impl.BaseAlertService;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceAlertValueResult;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.FreemakerUtils;\nimport freemarker.template.Configuration;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@Component\npublic class InstanceStateInspector extends BaseAlertService {\n\n    /**\n     * 实例相关\n     */\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private UserService userService;\n    @Autowired\n    private Configuration configuration;\n\n    public boolean inspect() {\n\n        List<InstanceAlertValueResult> alertInstInfo = new ArrayList<>();\n        List<InstanceInfo> heartStopInstances = instanceDao.getAllHeartStopInstance();\n        if (!CollectionUtils.isEmpty(heartStopInstances)) {\n            for (InstanceInfo info : heartStopInstances) {\n                long appId = info.getAppId();\n                AppDesc appDesc = appService.getByAppId(appId);\n                appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));\n                InstanceAlertValueResult instanceAlert = new InstanceAlertValueResult();\n                instanceAlert.setInstanceInfo(info);\n                instanceAlert.setAppId(appId);\n                instanceAlert.setAppDesc(appDesc);\n                instanceAlert.setOtherInfo(InstanceStatusEnum.getByStatus(info.getStatus()).getInfo());\n                alertInstInfo.add(instanceAlert);\n            }\n            String emailTitle = String.format(\"Redis实例异常状态监控报警\");\n            Map<String, Object> context = new HashMap<>();\n            context.put(\"instanceAlertValueResultList\", alertInstInfo);\n            String emailContent = FreemakerUtils.createText(\"instanceState.ftl\", configuration, context);\n            appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.INATANCE_EXCEPTION_STATE_MONITOR, emailTitle, null, alertInstInfo);\n            emailComponent.sendMailToAdmin(emailTitle, emailContent);\n            logger.info(emailContent);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/inspect/impl/RedisIsolationPersistenceInspector.java",
    "content": "package com.sohu.cache.inspect.impl;\r\n\r\nimport com.sohu.cache.alert.impl.BaseAlertService;\r\nimport com.sohu.cache.constant.InstanceStatusEnum;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.inspect.InspectParamEnum;\r\nimport com.sohu.cache.inspect.Inspector;\r\nimport com.sohu.cache.redis.RedisCenter;\r\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\r\nimport com.sohu.cache.util.IdempotentConfirmer;\r\nimport com.sohu.cache.util.TypeUtil;\r\n\r\nimport org.apache.commons.collections.MapUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\n\r\nimport org.springframework.beans.factory.annotation.Value;\r\nimport redis.clients.jedis.Jedis;\r\n\r\nimport java.util.*;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\n/**\r\n * Created by yijunzhang on 15-1-30.\r\n */\r\npublic class RedisIsolationPersistenceInspector extends BaseAlertService implements Inspector {\r\n    \r\n    public static final int REDIS_DEFAULT_TIME = 5000;\r\n    \r\n    private RedisCenter redisCenter;\r\n\r\n    @Value(\"${aof.rewrite.ignore-appIds:#{null}}\")\r\n    private String aofRewriteIgnoreAppIds;\r\n\r\n    @Override\r\n    public boolean inspect(Map<InspectParamEnum, Object> paramMap) {\r\n        Set<String> aofRewriteIgnoreSet = new HashSet<>();\r\n        if(StringUtils.isNotEmpty(aofRewriteIgnoreAppIds)){\r\n            String[] appIds = aofRewriteIgnoreAppIds.split(\";\");\r\n            if(appIds != null && appIds.length > 0){\r\n                aofRewriteIgnoreSet.addAll(Arrays.asList(appIds));\r\n            }\r\n        }\r\n        final String host = MapUtils.getString(paramMap, InspectParamEnum.SPLIT_KEY);\r\n        List<InstanceInfo> list = (List<InstanceInfo>) paramMap.get(InspectParamEnum.INSTANCE_LIST);\r\n        outer:\r\n        for (InstanceInfo info : list) {\r\n            final int port = info.getPort();\r\n            final int type = info.getType();\r\n            final long appId = info.getAppId();\r\n            int status = info.getStatus();\r\n            //非正常节点\r\n            if (status != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\r\n                continue;\r\n            }\r\n\r\n            //过滤不重写appId\r\n            if(aofRewriteIgnoreSet.contains(String.valueOf(appId))){\r\n                continue;\r\n            }\r\n\r\n            if (TypeUtil.isRedisDataType(type)) {\r\n                Jedis jedis = redisCenter.getAdminJedis(appId, host, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\r\n                try {\r\n                    Map<String, String> persistenceMap = parseMap(jedis);\r\n                    if (persistenceMap.isEmpty()) {\r\n                        logger.error(\"{}:{} get persistenceMap failed\", host, port);\r\n                        continue;\r\n                    }\r\n                    if (!isAofEnabled(persistenceMap)) {\r\n                        continue;\r\n                    }\r\n                    long aofCurrentSize = MapUtils.getLongValue(persistenceMap, RedisInfoEnum.aof_current_size.getValue());\r\n                    long aofBaseSize = MapUtils.getLongValue(persistenceMap, RedisInfoEnum.aof_base_size.getValue());\r\n                    //阀值大于60%\r\n                    long aofThresholdSize = (long) (aofBaseSize * 1.6);\r\n                    double percentage = getPercentage(aofCurrentSize, aofBaseSize);\r\n                    if (aofCurrentSize >= aofThresholdSize\r\n                            //大于64Mb\r\n                            && aofCurrentSize > (64 * 1024 * 1024)) {\r\n                        //bgRewriteAof\r\n                        boolean isInvoke = invokeBgRewriteAof(jedis);\r\n                        if (!isInvoke) {\r\n                            logger.error(\"{}:{} invokeBgRewriteAof failed\", host, port);\r\n                            continue;\r\n                        } else {\r\n                            logger.warn(\"{}:{} invokeBgRewriteAof started percentage={}\", host, port, percentage);\r\n                        }\r\n                        while (true) {\r\n                            try {\r\n                                //before wait 1s\r\n                                TimeUnit.SECONDS.sleep(1);\r\n                                Map<String, String> loopMap = parseMap(jedis);\r\n                                Integer aofRewriteInProgress = MapUtils.getInteger(loopMap, \"aof_rewrite_in_progress\", null);\r\n                                if (aofRewriteInProgress == null) {\r\n                                    logger.error(\"loop watch:{}:{} return failed\", host, port);\r\n                                    break;\r\n                                } else if (aofRewriteInProgress <= 0) {\r\n                                    //bgrewriteaof Done\r\n                                    logger.warn(\"{}:{} bgrewriteaof Done lastSize:{}Mb,currentSize:{}Mb\", host, port, getMb(aofCurrentSize), getMb(MapUtils.getLongValue(loopMap, \"aof_current_size\")));\r\n                                    break;\r\n                                } else {\r\n                                    //wait 1s\r\n                                    TimeUnit.SECONDS.sleep(1);\r\n                                }\r\n                            } catch (Exception e) {\r\n                                logger.error(e.getMessage(), e);\r\n                            }\r\n                        }\r\n                    } else {\r\n                        if (percentage > 50D) {\r\n                            long currentSize = getMb(aofCurrentSize);\r\n                            logger.debug(\"checked {}:{} aof increase percentage:{}% currentSize:{}Mb\", host, port, percentage, currentSize > 0 ? currentSize : \"<1\");\r\n                        }\r\n                    }\r\n                } finally {\r\n                    jedis.close();\r\n                }\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n\r\n    private long getMb(long bytes) {\r\n        return (long) (bytes / 1024 / 1024);\r\n    }\r\n\r\n    private boolean isAofEnabled(Map<String, String> infoMap) {\r\n        Integer aofEnabled = MapUtils.getInteger(infoMap, \"aof_enabled\", null);\r\n        return aofEnabled != null && aofEnabled == 1;\r\n    }\r\n\r\n    private double getPercentage(long aofCurrentSize, long aofBaseSize) {\r\n        if (aofBaseSize == 0) {\r\n            return 0.0D;\r\n        }\r\n        String format = String.format(\"%.2f\", (Double.valueOf(String.valueOf(aofCurrentSize - aofBaseSize)) * 100 / aofBaseSize));\r\n        return Double.parseDouble(format);\r\n    }\r\n\r\n    private Map<String, String> parseMap(final Jedis jedis) {\r\n        final StringBuilder builder = new StringBuilder();\r\n        boolean isInfo = new IdempotentConfirmer() {\r\n            @Override\r\n            public boolean execute() {\r\n                String persistenceInfo = null;\r\n                try {\r\n                    persistenceInfo = jedis.info(\"Persistence\");\r\n                } catch (Exception e) {\r\n                    logger.warn(e.getMessage() + \"-{}:{}\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage());\r\n                }\r\n                boolean isOk = StringUtils.isNotBlank(persistenceInfo);\r\n                if (isOk) {\r\n                    builder.append(persistenceInfo);\r\n                }\r\n                return isOk;\r\n            }\r\n        }.run();\r\n        if (!isInfo) {\r\n            logger.error(\"{}:{} info Persistence failed\", jedis.getClient().getHost(), jedis.getClient().getPort());\r\n            return Collections.emptyMap();\r\n        }\r\n        String persistenceInfo = builder.toString();\r\n        if (StringUtils.isBlank(persistenceInfo)) {\r\n            return Collections.emptyMap();\r\n        }\r\n        Map<String, String> map = new LinkedHashMap<String, String>();\r\n        String[] array = persistenceInfo.split(\"\\r\\n\");\r\n        for (String line : array) {\r\n            String[] cells = line.split(\":\");\r\n            if (cells.length > 1) {\r\n                map.put(cells[0], cells[1]);\r\n            }\r\n        }\r\n\r\n        return map;\r\n    }\r\n\r\n    public boolean invokeBgRewriteAof(final Jedis jedis) {\r\n        return new IdempotentConfirmer() {\r\n            @Override\r\n            public boolean execute() {\r\n                try {\r\n                    String response = jedis.bgrewriteaof();\r\n                    if (response != null && response.contains(\"rewriting started\")) {\r\n                        return true;\r\n                    }\r\n                } catch (Exception e) {\r\n                    String message = e.getMessage();\r\n                    if (message.contains(\"rewriting already\")) {\r\n                        return true;\r\n                    }\r\n                    logger.error(message, e);\r\n                }\r\n                return false;\r\n            }\r\n        }.run();\r\n    }\r\n\r\n\tpublic void setRedisCenter(RedisCenter redisCenter) {\r\n\t\tthis.redisCenter = redisCenter;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/interceptor/AppAndInstanceAuthorityInterceptor.java",
    "content": "package com.sohu.cache.interceptor;\r\n\r\nimport com.sohu.cache.constant.AppUserTypeEnum;\r\nimport com.sohu.cache.entity.AppToUser;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\r\nimport com.sohu.cache.web.service.AppService;\r\nimport com.sohu.cache.web.service.UserLoginStatusService;\r\nimport com.sohu.cache.web.service.UserService;\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.web.servlet.ModelAndView;\r\nimport org.springframework.web.servlet.handler.HandlerInterceptorAdapter;\r\n\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport javax.servlet.http.HttpSession;\r\nimport java.io.IOException;\r\nimport java.util.List;\r\n\r\n/**\r\n * 应用和实例权限验证\r\n *\r\n * @author leifu\r\n * @Date 2014年10月29日\r\n * @Time 下午3:18:00\r\n */\r\npublic class AppAndInstanceAuthorityInterceptor extends HandlerInterceptorAdapter {\r\n    private Logger logger = LoggerFactory.getLogger(AppAndInstanceAuthorityInterceptor.class);\r\n    @Autowired\r\n    private AppService appService;\r\n    @Autowired\r\n    private UserService userService;\r\n    @Autowired\r\n    private InstanceStatsCenter instanceStatsCenter;\r\n    @Autowired\r\n    private UserLoginStatusService userLoginStatusService;\r\n\r\n    @Override\r\n    public boolean preHandle(HttpServletRequest request,\r\n            HttpServletResponse response, Object handler) throws Exception {\r\n        // 1. 获取用户\r\n        String userName = userLoginStatusService.getUserNameFromLoginStatus(request);\r\n        //未登录\r\n        if (StringUtils.isBlank(userName)) {\r\n            String redirectUrl = userLoginStatusService.getRedirectUrl(request);\r\n            response.sendRedirect(redirectUrl);\r\n            return false;\r\n        }\r\n        AppUser user = userService.getByName(userName);\r\n        if (user == null || user.getType() == -1) {\r\n            String redirectUrl = userLoginStatusService.getRegisterUrl(user);\r\n            response.sendRedirect(redirectUrl);\r\n            return false;\r\n        }\r\n\r\n        // 2. 管理员直接跳过\r\n        if (AppUserTypeEnum.ADMIN_USER.value().equals(user.getType())) {\r\n            return true;\r\n        }\r\n\r\n        // 3. 应用id\r\n        String appId = request.getParameter(\"appId\");\r\n        if (StringUtils.isNotBlank(appId)) {\r\n            checkUserAppPower(response, request.getSession(true), user, NumberUtils.toLong(appId));\r\n        }\r\n\r\n        // 4. 实例权限检测(其实也是应用)\r\n        String instanceId = request.getParameter(\"instanceId\");\r\n        if (StringUtils.isNotBlank(instanceId)) {\r\n            InstanceInfo instanceInfo = instanceStatsCenter.getInstanceInfo(Long.parseLong(instanceId));\r\n            checkUserAppPower(response, request.getSession(true), user, instanceInfo.getAppId());\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    /**\r\n     * 检查用户应用的权限\r\n     *\r\n     * @param response\r\n     * @param session\r\n     * @param user\r\n     * @param appId\r\n     * @return\r\n     */\r\n    private void checkUserAppPower(HttpServletResponse response, HttpSession session, AppUser user, Long appId) {\r\n        // 应用下的用户\r\n        List<AppToUser> appToUsers = appService.getAppToUserList(appId);\r\n        if (CollectionUtils.isNotEmpty(appToUsers)) {\r\n            for (AppToUser tempAppToUser : appToUsers) {\r\n                if (user.getId().equals(tempAppToUser.getUserId())) {\r\n                    return;\r\n                }\r\n            }\r\n            // 没权限\r\n            String path = session.getServletContext().getContextPath();\r\n            try {\r\n                response.sendRedirect(path + \"/web/resource/noPower?appId=\" + appId);\r\n            } catch (IOException e) {\r\n                logger.error(e.getMessage(), e);\r\n            }\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void postHandle(HttpServletRequest request,\r\n            HttpServletResponse response, Object handler,\r\n            ModelAndView modelAndView) throws Exception {\r\n    }\r\n\r\n    @Override\r\n    public void afterCompletion(HttpServletRequest request,\r\n            HttpServletResponse response, Object handler, Exception ex)\r\n            throws Exception {\r\n    }\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/interceptor/FrontUserLoginInterceptor.java",
    "content": "package com.sohu.cache.interceptor;\r\n\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.web.service.UserLoginStatusService;\r\nimport com.sohu.cache.web.service.UserService;\r\nimport org.apache.commons.lang3.StringUtils;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.web.servlet.handler.HandlerInterceptorAdapter;\r\n\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\n/**\r\n * 前置登陆验证\r\n *\r\n * @author leifu\r\n */\r\npublic class FrontUserLoginInterceptor extends HandlerInterceptorAdapter {\r\n    @Autowired\r\n    private UserService userService;\r\n    @Autowired\r\n    private UserLoginStatusService userLoginStatusService;\r\n\r\n    @Override\r\n    public boolean preHandle(HttpServletRequest request,\r\n            HttpServletResponse response, Object handler) throws Exception {\r\n        String userName = userLoginStatusService.getUserNameFromLoginStatus(request);\r\n        //未登录\r\n        if (StringUtils.isBlank(userName)) {\r\n            String redirectUrl = userLoginStatusService.getRedirectUrl(request);\r\n            response.sendRedirect(redirectUrl);\r\n            return false;\r\n        }\r\n        AppUser user = userService.getByName(userName);\r\n        //新用户\r\n        if (user == null || user.getType() == -1) {\r\n            String redirectUrl = userLoginStatusService.getRegisterUrl(user);\r\n            response.sendRedirect(redirectUrl);\r\n            return false;\r\n        }\r\n        request.setAttribute(\"userInfo\", user);\r\n        request.setAttribute(\"uri\", request.getRequestURI());\r\n\r\n        return true;\r\n    }\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/interceptor/ManageUserLoginInterceptor.java",
    "content": "package com.sohu.cache.interceptor;\r\n\r\nimport com.sohu.cache.constant.AppUserTypeEnum;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.web.service.UserLoginStatusService;\r\nimport com.sohu.cache.web.service.UserService;\r\nimport org.apache.commons.lang3.StringUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.web.servlet.ModelAndView;\r\nimport org.springframework.web.servlet.handler.HandlerInterceptorAdapter;\r\n\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\n/**\r\n * 管理端登录验证\r\n * @author leifu\r\n */\r\npublic class ManageUserLoginInterceptor extends HandlerInterceptorAdapter {\r\n    private Logger logger = LoggerFactory.getLogger(ManageUserLoginInterceptor.class);\r\n    @Autowired\r\n    private UserService userService;\r\n    @Autowired\r\n    private UserLoginStatusService userLoginStatusService;\r\n    \r\n    @Override\r\n    public boolean preHandle(HttpServletRequest request,\r\n            HttpServletResponse response, Object handler) throws Exception {\r\n\r\n        String userName = userLoginStatusService.getUserNameFromLoginStatus(request);\r\n        //未登录\r\n        if (StringUtils.isBlank(userName)) {\r\n            String redirectUrl = userLoginStatusService.getRedirectUrl(request);\r\n            response.sendRedirect(redirectUrl);\r\n            return false;\r\n        }\r\n        AppUser user = userService.getByName(userName);\r\n        //新用户\r\n        if (user == null || user.getType() == -1) {\r\n            String redirectUrl = userLoginStatusService.getRegisterUrl(user);\r\n            response.sendRedirect(redirectUrl);\r\n            return false;\r\n        }\r\n        //必须是管理员\r\n        if (user.getType() != AppUserTypeEnum.ADMIN_USER.value()) {\r\n            String redirectUrl = userLoginStatusService.getRedirectUrl(request);\r\n            response.sendRedirect(redirectUrl);\r\n            return false;\r\n        }\r\n        request.setAttribute(\"userInfo\", user);\r\n        request.setAttribute(\"uri\", request.getRequestURI());\r\n        return true;\r\n\r\n    }\r\n\r\n    @Override\r\n    public void postHandle(HttpServletRequest request,\r\n            HttpServletResponse response, Object handler,\r\n            ModelAndView modelAndView) throws Exception {\r\n    }\r\n\r\n    @Override\r\n    public void afterCompletion(HttpServletRequest request,\r\n            HttpServletResponse response, Object handler, Exception ex)\r\n            throws Exception {\r\n    }\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/log/CustLogAppenderInit.java",
    "content": "package com.sohu.cache.log;\n\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.LoggerContext;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CustLogAppenderInit implements ApplicationRunner{\n\n    @Autowired\n    private TaskFlowRecordAppender taskFlowRecordAppender;\n\n    private final org.slf4j.Logger logger = LoggerFactory.getLogger(CustLogAppenderInit.class);\n\n    @Override\n    public void run(ApplicationArguments args) throws Exception {\n        logger.warn(\"custLogAppender init begin!\");\n        if (LoggerFactory.getILoggerFactory() instanceof LoggerContext) {\n            LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();\n            Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);\n            //自定义任务流appender\n            taskFlowRecordAppender.setContext(loggerContext);\n            taskFlowRecordAppender.start();\n            rootLogger.addAppender(taskFlowRecordAppender);\n            logger.warn(\"custLogAppender init Done!\");\n        } else {\n            logger.error(\"custLogAppender init failed , LoggerFactory.getILoggerFactory()={}\",\n                    LoggerFactory.getILoggerFactory());\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/log/TaskFlowRecordAppender.java",
    "content": "package com.sohu.cache.log;\n\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.core.AppenderBase;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Marker;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.sql.Date;\nimport java.text.SimpleDateFormat;\n\n@Component\npublic class TaskFlowRecordAppender extends AppenderBase<ILoggingEvent> {\n\n    @Autowired\n    private AssistRedisService assistRedisService;\n\n    @Override\n    protected void append(ILoggingEvent event) {\n        if (event == null) {\n            return;\n        }\n        Marker marker = event.getMarker();\n        if (marker != null && marker.getName().equals(BaseTask.MARKER_NAME)) {\n            String taskFlowId = event.getMDCPropertyMap().get(TaskConstants.TASK_STEP_FLOW_ID);\n            if (StringUtils.isBlank(taskFlowId)) {\n                return;\n            }\n            String customLog = generateCustomLog(event);\n            String taskFlowIdKey = ConstUtils.getTaskFlowRedisKey(taskFlowId);\n            assistRedisService.rpush(taskFlowIdKey, customLog);\n        }\n    }\n\n    private String generateCustomLog(ILoggingEvent event) {\n        Date date = new Date(event.getTimeStamp());\n        String formatDate = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss.SSS\").format(date);\n        String threadName = event.getThreadName();\n        String logLevel = event.getLevel().toString();\n        String className = event.getLoggerName();\n        String simpleClassName = getSimpleClassName(className);\n        String formatMessage = event.getFormattedMessage();\n        return String.format(\"%s {%s} %s %s  - %s\", formatDate, threadName, logLevel, simpleClassName, formatMessage);\n    }\n\n    private String getSimpleClassName(String className) {\n        int index = className.lastIndexOf(\".\");\n        if (index >= 0) {\n            return className.substring(index + 1);\n        }\n        return className;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/machine/MachineCenter.java",
    "content": "package com.sohu.cache.machine;\r\n\r\nimport com.sohu.cache.constant.MachineInfoEnum.TypeEnum;\r\nimport com.sohu.cache.entity.*;\r\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\r\nimport com.sohu.cache.web.enums.MachineMemoryDistriEnum;\r\nimport com.sohu.cache.web.vo.MachineStatsVo;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 基于host的操作\r\n * User: lingguo\r\n */\r\npublic interface MachineCenter {\r\n\r\n    /**\r\n     * 收集host的状态信息\r\n     *\r\n     * @param hostId      机器id\r\n     * @param collectTime 收集时间\r\n     * @param ip          ip\r\n     * @return 机器的信息\r\n     */\r\n    public Map<String, Object> collectMachineInfo(final long hostId, final long collectTime, final String ip);\r\n\r\n    /**\r\n     * 异步收集host的状态信息\r\n     *\r\n     * @param hostId      机器id\r\n     * @param collectTime 收集时间\r\n     * @param ip          ip\r\n     */\r\n    public void asyncCollectMachineInfo(final long hostId, final long collectTime, final String ip);\r\n\r\n    /**\r\n     * 监控机器的状态信息，向上层汇报或者报警\r\n     *\r\n     * @param hostId 机器id\r\n     * @param ip     ip\r\n     * @return\r\n     */\r\n    public void monitorMachineStats(final long hostId, final String ip);\r\n\r\n    /**\r\n     * 异步监控机器的状态信息，向上层汇报或者报警\r\n     *\r\n     * @param hostId 机器id\r\n     * @param ip     ip\r\n     * @return\r\n     */\r\n    public void asyncMonitorMachineStats(final long hostId, final String ip);\r\n\r\n    /**\r\n     * 在主机ip上的端口port上启动一个进程，并check是否启动成功；\r\n     *\r\n     * @param ip    ip\r\n     * @param port  端口\r\n     * @param shell shell命令\r\n     * @return 是否成功\r\n     */\r\n    public boolean startProcessAtPort(String ip, int port, final String shell);\r\n\r\n    /**\r\n     * 执行shell命令并获取返回结果\r\n     *\r\n     * @param ip\r\n     * @param shell\r\n     * @return\r\n     */\r\n    public String executeShell(final String ip, String shell);\r\n\r\n    String executeShell(final String ip, String shell, Integer timeout);\r\n\r\n    /**\r\n     * 根据类型返回机器可用端口\r\n     *\r\n     * @param ip\r\n     * @param type\r\n     * @return\r\n     */\r\n    public Integer getAvailablePort(final String ip, final int type);\r\n\r\n    /**\r\n     * 创建远程文件\r\n     *\r\n     * @param host\r\n     * @param fileName\r\n     * @param content\r\n     * @return 是否创建成功\r\n     */\r\n    public String createRemoteFile(final String host, String fileName, List<String> content);\r\n\r\n\r\n    /**\r\n     * 获取机器列表\r\n     *\r\n     * @param ipLike\r\n     * @return\r\n     */\r\n    public List<MachineStats> getMachineStats(String ipLike);\r\n\r\n    /**\r\n     * 获取机器列表\r\n     *\r\n     * @param ipLike\r\n     * @param versionId\r\n     * @param isInstall\r\n     */\r\n    public List<MachineStats> getMachineStats(String ipLike, Integer useType,Integer type, Integer versionId, Integer isInstall,Integer k8sType,String realip);\r\n\r\n    /**\r\n     * 获取全部机器列表\r\n     *\r\n     * @return\r\n     */\r\n    public List<MachineStats> getAllMachineStats();\r\n\r\n    List<MachineMemStatInfo> getAllValidMachineMem(List<String> excludeMachineList, String room, Integer useType);\r\n\r\n    List<MachineMemStatInfo> getValidMachineMemByIpList(List<String> ipList);\r\n\r\n    /**\r\n     * 根据ip获取机器信息\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    public MachineInfo getMachineInfoByIp(String ip);\r\n\r\n\r\n    MachineStats getMachineMemoryDetail(String ip);\r\n\r\n    /**\r\n     * 获取一台机器的所有实例\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    List<InstanceInfo> getMachineInstanceInfo(String ip);\r\n\r\n\r\n    /**\r\n     * 获取一台机器的所有实例统计信息\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    List<InstanceStats> getMachineInstanceStatsByIp(String ip);\r\n\r\n    /**\r\n     * 获取指定机器某个redis端口的最近日志\r\n     *\r\n     * @param maxLineNum\r\n     * @return\r\n     */\r\n    String showInstanceRecentLog(InstanceInfo instanceInfo, int maxLineNum);\r\n\r\n    /**\r\n     * 根据机器类型获取机器列表\r\n     *\r\n     * @param typeEnum\r\n     * @return\r\n     */\r\n    List<MachineInfo> getMachineInfoByType(TypeEnum typeEnum);\r\n\r\n    /**\r\n     * 获取机器下实例数map\r\n     *\r\n     * @return\r\n     */\r\n    public Map<String, Integer> getMachineInstanceCountMap();\r\n\r\n    public Map<String,MachineInfo> getK8sMachineMap();\r\n\r\n    /**\r\n     * <p>\r\n     * Description: 获取有效机房\r\n     * </p>\r\n     *\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2018/10/16\r\n     */\r\n    public List<MachineRoom> getEffectiveRoom();\r\n\r\n    List<MachineRoom> getAllRoom();\r\n\r\n    /**\r\n     * 机器使用内存分布\r\n     * @return\r\n     */\r\n    public Map<MachineMemoryDistriEnum, Integer> getMaxMemoryDistribute();\r\n\r\n    /**\r\n     * 机器最大内存分布\r\n     * @return\r\n     */\r\n    public Map<MachineMemoryDistriEnum, Integer> getUsedMemoryDistribute();\r\n\r\n    public int getMachineNum(int type);\r\n    /**\r\n     * 获取机器性能统计分布\r\n     * @return\r\n     */\r\n    public List<MachineStatsVo> getmachineStatsVoList();\r\n\r\n    /**\r\n\t * @param appId\r\n\t * @param port\r\n\t * @param instanceTypeEnum\r\n\t * @return\r\n\t */\r\n\tpublic String getInstanceRemoteBasePath(long appId, int port, InstanceTypeEnum instanceTypeEnum);\r\n\r\n    /**\r\n     * 获取机器的部署路径\r\n     *  1.物理机/虚机/docker: /opt/cachecloud/conf  /opt/cachecloud/data /opt/cachecloud/logs\r\n     *  1.k8s容器: /opt/cachecloud/conf/${host}/  /opt/cachecloud/data/${host}/ /opt/cachecloud/logs/${host}\r\n     * @param host 机器ip\r\n     * @return\r\n     */\r\n    public String getMachineRelativeDir(String host,int dirType);\r\n\r\n    /**\r\n     * 是否k8s机器\r\n     * @param host 机器ip\r\n     * @return  true：是  false：否\r\n     */\r\n    public Boolean isK8sMachine(String host);\r\n\r\n    public Map<String,Object> getAllMachineEnv(Date searchDate,int type);\r\n\r\n    public Map<String,Object> getExceptionMachineEnv(Date searchDate);\r\n\r\n    /**\r\n     * 获取机器列表的第一台机器资源\r\n     * @return\r\n     */\r\n    public String getFirstMachineIp();\r\n\r\n    /**\r\n     * 检查机器内存是否充足 容器内存*0.85\r\n     * @param ip 检测机器\r\n     * true: 内存充足  false:内存使用率>85%\r\n     */\r\n    public boolean checkMachineMemory(String ip);\r\n\r\n    public List<MachineInfo> getMachineListByRealIp(String realIp);\r\n\r\n    /**\r\n     * 获取机器配置信息及已分布redis实例（数量、申请内存、使用内存、使用内存rss等）\r\n     */\r\n    public List<MachineMemStatInfo> getMachineInfoAndUsedInfo(String room, Integer type, Integer useType, Integer disType, String ip);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/machine/MachineDeployCenter.java",
    "content": "package com.sohu.cache.machine;\n\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.entity.MachineRelation;\nimport com.sohu.cache.entity.MachineRoom;\nimport com.sohu.cache.web.enums.SuccessEnum;\n\nimport java.util.List;\n\n/**\n * 机器部署相关\n * @author leifu\n * changed @Date 2016-4-24\n * @Time 下午5:07:30\n */\npublic interface MachineDeployCenter {\n\n    /**\n     * 增加一台机器:入db和开启统计\n     *\n     * @param machineInfo\n     */\n    public boolean addMachine(MachineInfo machineInfo);\n\n    boolean addMachineRoom(MachineRoom room);\n\n    boolean removeMachineRoom(int roomId);\n\n    /**\n     * 移除一台机器：删db数据和关闭统计\n     *\n     * @param machineInfo\n     */\n    public boolean removeMachine(MachineInfo machineInfo);\n\n    /**\n     * 记录pod每次变更虚ip和宿主机关系\n     */\n    public void updateMachineRelation(int id, Long taskid, int is_sync);\n\n    /**\n     *  查询容器 pod每次变更节点\n     */\n    public List<MachineRelation> getMachineRelationList(String containerIp);\n\n    /**\n     * 检测是同步任务状态\n     * @param containerIp\n     * @param sourceIp\n     * @return true:有同步中任务  false:没有同步中任务\n     */\n    public SuccessEnum checkMachineSyncStatus(String containerIp, String sourceIp, int is_sync);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/machine/MachineProperty.java",
    "content": "package com.sohu.cache.machine;\n\nimport com.google.common.collect.ComparisonChain;\n\nimport java.io.Serializable;\nimport java.util.Comparator;\n\n/**\n * 选择机器时根据机器的memory、traffic和load进行排序；\n * 在memory满足的情况，排序规则是：traffic > load > memory\n *\n * @author: lingguo\n * @time: 2014/9/17 12:04\n */\npublic class MachineProperty implements Comparator<MachineProperty>, Serializable {\n\n    private static final long serialVersionUID = 2498956982032225654L;\n    private long hostId;\n    private long memory;\n    private double traffic;\n    private double load;\n\n    public MachineProperty() {\n    }\n\n    public MachineProperty(long hostId, long memory, double traffic, double load) {\n        this.hostId = hostId;\n        this.memory = memory;\n        this.traffic = traffic;\n        this.load = load;\n    }\n\n    public long getHostId() {\n        return hostId;\n    }\n\n    public void setHostId(long hostId) {\n        this.hostId = hostId;\n    }\n\n    public long getMemory() {\n        return memory;\n    }\n\n    public void setMemory(long memory) {\n        this.memory = memory;\n    }\n\n    public double getTraffic() {\n        return traffic;\n    }\n\n    public void setTraffic(double traffic) {\n        this.traffic = traffic;\n    }\n\n    public double getLoad() {\n        return load;\n    }\n\n    public void setLoad(double load) {\n        this.load = load;\n    }\n\n    @Override\n    public int compare(MachineProperty o1, MachineProperty o2) {\n        return ComparisonChain.start()\n                .compare(o1.traffic, o2.traffic)\n                .compare(o1.load, o2.load)\n                .compare(o2.memory, o1.memory)\n                .result();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/machine/PortGenerator.java",
    "content": "package com.sohu.cache.machine;\r\n\r\nimport com.sohu.cache.constant.EmptyObjectConstant;\r\nimport com.sohu.cache.constant.SymbolConstant;\r\nimport com.sohu.cache.exception.SSHException;\r\nimport com.sohu.cache.ssh.SSHUtil;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.google.common.util.concurrent.AtomicLongMap;\r\n\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\n/**\r\n * 生成一个redis可用端口\r\n *\r\n * @author: lingguo\r\n * @time: 2014/8/25 20:57\r\n */\r\npublic class PortGenerator {\r\n    private static Logger logger = LoggerFactory.getLogger(PortGenerator.class);\r\n    /**\r\n     * redis port常量\r\n     */\r\n    private static final Integer REDIS_START_PORT = 6379;\r\n    private static AtomicLongMap<String> redisPortHolder = AtomicLongMap.create();\r\n    private static AtomicLongMap<String> redisSentinelPortHolder = AtomicLongMap.create();\r\n\r\n    /**\r\n     * 返回一个redis的可用端口：\r\n     *  - 1. 通过shell查询redis当前已用的最大port；\r\n     *  - 2. 为什么同步：防止多线程访问时获取到同样的端口；\r\n     *  - 3. 为什么还用原子计数：连续两次调用时，如果进程还没启动，则拿到的仍然是相同的端口；\r\n     *\r\n     * @param ip\r\n     * @return\r\n     */\r\n    public static synchronized Integer getRedisPort(final String ip) {\r\n        if (redisPortHolder.get(ip) == 0L) {\r\n            redisPortHolder.put(ip, REDIS_START_PORT);\r\n        }\r\n        String maxPortStr = \"\";\r\n        try {\r\n            int sshPort = SSHUtil.getSshPort(ip);\r\n            maxPortStr = getMaxPortStr(ip, sshPort);\r\n        } catch (SSHException e) {\r\n            logger.error(\"cannot get max port of redis by ssh, ip: {}\", ip, e);\r\n        }\r\n        logger.warn(\"{} maxPort is {}\", ip, maxPortStr);\r\n        if (StringUtils.isBlank(maxPortStr) || !StringUtils.isNumeric(maxPortStr)) {\r\n            logger.warn(\"{} the max port of redis is invalid, maxPortStr: {}\", ip, maxPortStr);\r\n            return (int)redisPortHolder.getAndIncrement(ip);\r\n        }\r\n\r\n        int availablePort = Integer.parseInt(maxPortStr) + 1;\r\n        // 兼容连续调用的情况\r\n        if (availablePort < redisPortHolder.get(ip)) {\r\n            availablePort = (int)redisPortHolder.getAndIncrement(ip);\r\n        } else {    // 正常情况，以及兼容系统重启和当前端口不可用的情形\r\n            redisPortHolder.put(ip, availablePort + 1L);\r\n        }\r\n\r\n        logger.warn(\"first {} maxPort is {}\", ip, availablePort);\r\n        try {\r\n            while (SSHUtil.isPortUsed(ip, availablePort)) {\r\n                availablePort++;\r\n            }\r\n        } catch (SSHException e) {\r\n            logger.error(\"check port error, ip: {}, port: {}\", ip, availablePort, e);\r\n        }\r\n        logger.warn(\"final {} maxPort is {}\", ip, availablePort);\r\n        redisPortHolder.put(ip, availablePort + 1L);\r\n        return availablePort;\r\n    }\r\n\r\n    @Deprecated\r\n    public static String getMaxPortStrOld(String ip, int sshPort) throws SSHException {\r\n        String redisPidCmd = \"ps -ef | grep redis | grep -v 'grep' |  awk -F '*:' '{print $2}' \" +\r\n                \" | awk -F ' ' '{print $1}' | sort -r | head -1\";\r\n        return SSHUtil.execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, redisPidCmd);\r\n    }\r\n    \r\n    /**\r\n     * 直接解析ps -ef | grep redis | grep -v 'grep'\r\n     * @param ip\r\n     * @param sshPort\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n     public static String getMaxPortStr(String ip, int sshPort) throws SSHException {\r\n        String redisPidCmd = \"ps -ef | grep redis-server | grep -v 'grep'\";\r\n        String redisProcessStr = SSHUtil.execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, redisPidCmd);\r\n        if (StringUtils.isBlank(redisProcessStr)) {\r\n            logger.warn(\"{} excute {}, result is empty\", ip, redisPidCmd);\r\n            return EmptyObjectConstant.EMPTY_STRING;\r\n        }\r\n        int maxPort = 0;\r\n        String[] lines = redisProcessStr.split(SymbolConstant.ENTER);\r\n        for (String line : lines) {\r\n            if (StringUtils.isBlank(line)) {\r\n                continue;\r\n            }\r\n            int redisServerIndex = line.indexOf(\"redis-server\");\r\n            if (redisServerIndex >= 0) {\r\n                line = line.substring(redisServerIndex);\r\n            }\r\n            if (redisServerIndex < 0) {\r\n                continue;\r\n            }\r\n            String[] items = line.split(SymbolConstant.SPACE);\r\n            if (items.length >= 2) {\r\n                String hostPort = items[1];\r\n                if (StringUtils.isBlank(hostPort)) {\r\n                    continue;\r\n                }\r\n                String[] hostPortArr = hostPort.split(SymbolConstant.COLON);\r\n                if (hostPortArr.length != 2) {\r\n                    continue;\r\n                }\r\n                String portStr = hostPortArr[1];\r\n                if (!NumberUtils.isDigits(portStr)) {\r\n                    continue;\r\n                }\r\n                int port = NumberUtils.toInt(portStr);\r\n                if (port > maxPort) {\r\n                    maxPort = port;\r\n                }\r\n            }\r\n        }\r\n        return maxPort == 0 ? EmptyObjectConstant.EMPTY_STRING : String.valueOf(maxPort);\r\n    }\r\n\r\n    public static synchronized Integer getRedisSentinelPort(final String ip) {\r\n        if (redisSentinelPortHolder.get(ip) == 0L) {\r\n            redisSentinelPortHolder.put(ip, ConstUtils.REDIS_SENTINEL_BASE_PORT);\r\n        }\r\n        String maxPortStr = \"\";\r\n        try {\r\n            int sshPort = SSHUtil.getSshPort(ip);\r\n            maxPortStr = getMaxSentinelPortStr(ip, sshPort);\r\n        } catch (SSHException e) {\r\n            logger.error(\"cannot get max port of redis by ssh, ip: {}\", ip, e);\r\n        }\r\n        logger.warn(\"{} maxPort is {}\", ip, maxPortStr);\r\n        if (StringUtils.isBlank(maxPortStr) || !StringUtils.isNumeric(maxPortStr)) {\r\n            logger.warn(\"{} the max port of redis is invalid, maxPortStr: {}\", ip, maxPortStr);\r\n            return (int)redisSentinelPortHolder.getAndIncrement(ip);\r\n        }\r\n\r\n        int availablePort = Integer.parseInt(maxPortStr) + 1;\r\n        // 兼容连续调用的情况\r\n        if (availablePort < redisSentinelPortHolder.get(ip)) {\r\n            availablePort = (int)redisSentinelPortHolder.getAndIncrement(ip);\r\n        } else {    // 正常情况，以及兼容系统重启和当前端口不可用的情形\r\n            redisSentinelPortHolder.put(ip, availablePort + 1L);\r\n        }\r\n\r\n        logger.warn(\"first {} maxPort is {}\", ip, availablePort);\r\n        try {\r\n            while (SSHUtil.isPortUsed(ip, availablePort)) {\r\n                availablePort++;\r\n            }\r\n        } catch (SSHException e) {\r\n            logger.error(\"check port error, ip: {}, port: {}\", ip, availablePort, e);\r\n        }\r\n        logger.warn(\"final {} maxPort is {}\", ip, availablePort);\r\n        redisSentinelPortHolder.put(ip, availablePort + 1L);\r\n        return availablePort;\r\n    }\r\n\r\n    /**\r\n     * 直接解析ps -ef | grep redis-sentinel | grep -v 'grep'\r\n     * @param ip\r\n     * @param sshPort\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    public static String getMaxSentinelPortStr(String ip, int sshPort) throws SSHException {\r\n        String redisPidCmd = \"ps -ef | grep redis-sentinel | grep -v 'grep'\";\r\n        String redisProcessStr = SSHUtil.execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, redisPidCmd);\r\n        if (StringUtils.isBlank(redisProcessStr)) {\r\n            logger.warn(\"{} excute {}, result is empty\", ip, redisPidCmd);\r\n            return EmptyObjectConstant.EMPTY_STRING;\r\n        }\r\n        int maxPort = 0;\r\n        String[] lines = redisProcessStr.split(SymbolConstant.ENTER);\r\n        for (String line : lines) {\r\n            if (StringUtils.isBlank(line)) {\r\n                continue;\r\n            }\r\n            int redisSentinelIndex = line.indexOf(\"redis-sentinel\");\r\n            if (redisSentinelIndex >= 0) {\r\n                line = line.substring(redisSentinelIndex);\r\n            }\r\n            if (redisSentinelIndex < 0) {\r\n                continue;\r\n            }\r\n            String[] items = line.split(SymbolConstant.SPACE);\r\n            if (items.length >= 2) {\r\n                String hostPort = items[1];\r\n                if (StringUtils.isBlank(hostPort)) {\r\n                    continue;\r\n                }\r\n                String[] hostPortArr = hostPort.split(SymbolConstant.COLON);\r\n                if (hostPortArr.length != 2) {\r\n                    continue;\r\n                }\r\n                String portStr = hostPortArr[1];\r\n                if (!NumberUtils.isDigits(portStr)) {\r\n                    continue;\r\n                }\r\n                int port = NumberUtils.toInt(portStr);\r\n                if (port > maxPort) {\r\n                    maxPort = port;\r\n                }\r\n            }\r\n        }\r\n        return maxPort == 0 ? EmptyObjectConstant.EMPTY_STRING : String.valueOf(maxPort);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/machine/impl/MachineCenterImpl.java",
    "content": "package com.sohu.cache.machine.impl;\n\nimport com.google.common.base.Strings;\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.constant.MachineConstant;\nimport com.sohu.cache.constant.MachineInfoEnum;\nimport com.sohu.cache.constant.MachineInfoEnum.TypeEnum;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.machine.PortGenerator;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.enums.DirEnum;\nimport com.sohu.cache.report.ReportDataComponent;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.util.*;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.CheckEnum;\nimport com.sohu.cache.web.enums.MachineMemoryDistriEnum;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\nimport com.sohu.cache.web.service.AppAlertRecordService;\nimport com.sohu.cache.web.vo.MachineEnv;\nimport com.sohu.cache.web.vo.MachineStatsVo;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\nimport redis.clients.jedis.HostAndPort;\n\nimport javax.annotation.PostConstruct;\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.text.DecimalFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.stream.Collectors;\n\nimport static com.google.common.base.Preconditions.checkArgument;\n\n/**\n * 机器接口的实现\n * User: lingguo\n */\n@Service(\"machineCenter\")\npublic class MachineCenterImpl implements MachineCenter {\n    private final Logger logger = LoggerFactory.getLogger(MachineCenterImpl.class);\n    @Autowired\n    private InstanceStatsCenter instanceStatsCenter;\n    @Autowired\n    private MachineStatsDao machineStatsDao;\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private InstanceStatsDao instanceStatsDao;\n    @Autowired\n    private MachineDao machineDao;\n    @Autowired\n    private RedisCenter redisCenter;\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private ResourceDao resourceDao;\n    @Autowired\n    private MachineRoomDao machineRoomDao;\n    @Autowired\n    private SSHService sshService;\n    @Autowired\n    protected AsyncService asyncService;\n    @Autowired\n    private ForkJoinPool forkJoinPool;\n    @Autowired\n    private AppAlertRecordService appAlertRecordService;\n    @Autowired\n    private ReportDataComponent reportDataComponent;\n\n    /**\n     * 邮箱报警\n     */\n    @Autowired\n    private EmailComponent emailComponent;\n\n    @PostConstruct\n    public void init() {\n        asyncService.assemblePool(AsyncThreadPoolFactory.MACHINE_POOL,\n                AsyncThreadPoolFactory.MACHINE_THREAD_POOL);\n    }\n\n    //异步执行任务\n    public void asyncCollectMachineInfo(final long hostId, final long collectTime, final String ip) {\n        String key = \"collect-machine-\" + hostId + \"-\" + ip + \"-\" + collectTime;\n        asyncService.submitFuture(AsyncThreadPoolFactory.MACHINE_POOL, new KeyCallable<Boolean>(key) {\n            public Boolean execute() {\n                try {\n                    Map<String, Object> map = collectMachineInfo(hostId, collectTime, ip);\n                    return true;\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n        });\n    }\n\n    /**\n     * 收集当前host的状态信息，保存到mysql；\n     * 这里将hostId作为参数传入，mysql中集合名为：ip:hostId\n     *\n     * @param hostId      机器id\n     * @param collectTime 收集时间，格式：yyyyMMddHHmm\n     * @param ip          ip\n     * @return 机器的统计信息\n     */\n    @Override\n    public Map<String, Object> collectMachineInfo(final long hostId, final long collectTime, final String ip) {\n        Map<String, Object> infoMap = new HashMap<String, Object>();\n        MachineStats machineStats = null;\n        try {\n            int sshPort = SSHUtil.getSshPort(ip);\n            // todo 合并SSHUTIL 到 SSHService\n            machineStats = SSHUtil.getMachineInfo(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD);\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(ip);\n            machineStats.setHostId(hostId);\n            if (machineStats != null && machineStats.validate()) {\n                infoMap.put(MachineConstant.Ip.getValue(), machineStats.getIp());\n                infoMap.put(MachineConstant.CpuUsage.getValue(), machineStats.getCpuUsage());\n                infoMap.put(MachineConstant.MemoryUsageRatio.getValue(), machineStats.getMemoryUsageRatio());\n                /**\n                 * SSHUtil返回的内存单位为k，由于实例的内存基本存储单位都是byte，所以统一为byte\n                 */\n                if (machineStats.getMemoryFree() != null) {\n                    infoMap.put(MachineConstant.MemoryFree.getValue(),\n                            Long.parseLong(machineStats.getMemoryFree()) * ConstUtils._1024);\n                } else {\n                    infoMap.put(MachineConstant.MemoryFree.getValue(), 0);\n                }\n                if (machineStats.getMemoryTotal() != null) {\n                    infoMap.put(MachineConstant.MemoryTotal.getValue(),\n                            Long.parseLong(machineStats.getMemoryTotal()) * ConstUtils._1024);\n                } else {\n                    infoMap.put(MachineConstant.MemoryTotal.getValue(), 0);\n                }\n\n                infoMap.put(MachineConstant.Load.getValue(), machineStats.getLoad());\n                infoMap.put(MachineConstant.Traffic.getValue(), machineStats.getTraffic());\n                infoMap.put(MachineConstant.DiskUsage.getValue(), machineStats.getDiskUsageMap());\n                infoMap.put(ConstUtils.COLLECT_TIME, collectTime);\n                instanceStatsCenter.saveStandardStats(infoMap, new HashMap<String, Object>(0), ip, (int) hostId,\n                        ConstUtils.MACHINE);\n                machineStats.setMemoryFree(Long.parseLong(machineStats.getMemoryFree()) * ConstUtils._1024 + \"\");\n                machineStats.setMemoryTotal(Long.parseLong(machineStats.getMemoryTotal()) * ConstUtils._1024 + \"\");\n                machineStats.setModifyTime(new Date());\n                // 获取maxmemory和运行实例总数\n                int maxMemory = instanceDao.getMemoryByHost(ip);\n                machineStats.setMaxMemory(maxMemory);\n                int instanceCount = instanceDao.getInstanceCountByHost(ip);\n                machineStats.setInstanceCount(instanceCount);\n                // 获取物理机入库内存\n                machineStats.setMachineMemory(machineInfo.getMem() * 1024);\n                machineStatsDao.mergeMachineStats(machineStats);\n\n                //上报数据\n                reportDataComponent.reportMachineData(machineStats);\n\n                logger.debug(\"collect machine info done, host: {}, time: {}\", ip, collectTime);\n            }\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            logger.error(\"host:{} collectMachineErrorStats=>{}\", ip, machineStats);\n            logger.error(e.getMessage(), e);\n        }\n        return infoMap;\n    }\n\n    //异步执行任务\n    public void asyncMonitorMachineStats(final long hostId, final String ip) {\n        String key = \"monitor-machine-\" + hostId + \"-\" + ip;\n        asyncService.submitFuture(AsyncThreadPoolFactory.MACHINE_POOL, new KeyCallable<Boolean>(key) {\n            public Boolean execute() {\n                try {\n                    monitorMachineStats(hostId, ip);\n                    return true;\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n        });\n    }\n\n    /**\n     * 监控机器的状态\n     *\n     * @param hostId 机器id\n     * @param ip     ip\n     */\n    @Override\n    public void monitorMachineStats(final long hostId, final String ip) {\n        Assert.isTrue(hostId > 0);\n        Assert.hasText(ip);\n\n        MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip);\n        if (machineStats == null) {\n            logger.warn(\"machine stats is null, ip: {}, time: {}\", ip, new Date());\n            return;\n        }\n        double cpuUsage = ObjectConvert.percentToDouble(machineStats.getCpuUsage(), 0);\n        int memTotal = 0;\n        double memoryUsage = 0.0;\n        try {\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(ip);\n            memTotal = machineInfo.getMem() * 1024;\n            Map machineMemDetail = instanceStatsDao.getMachineMemByIp(ip);\n            long memUsed = MapUtils.getLongValue(machineMemDetail, \"usedMem\", 0l);\n            double memUsedRss = MapUtils.getDoubleValue(machineMemDetail, \"usedMemRss\", 0d);\n            double memAllocRatio = memUsedRss / 1024 / 1024 / memTotal * 100;\n            DecimalFormat df = new DecimalFormat(\"#.00\");\n            memoryUsage = ObjectConvert.percentToDouble(df.format(memAllocRatio), 0);\n        } catch (Exception e) {\n            logger.info(\"get memoryUsage error:{}\", e.getMessage());\n        }\n\n        double memoryThreshold = ConstUtils.MEMORY_USAGE_RATIO_THRESHOLD;\n        /**\n         * 当机器的状态超过预设的阀值时，向上汇报或者报警\n         */\n        StringBuilder alertContent = new StringBuilder();\n        // 内存使用率\n        if (memoryUsage > memoryThreshold) {\n            logger.warn(\"memoryUsageRatio is above security line, ip: {}, memTotal: {}, memoryUsage: {}%\", ip, memTotal, memoryUsage);\n            alertContent.append(\"ip:\").append(ip).append(\",memTotal(G):\").append(memTotal / 1024).append(\",memUse(%):\").append(memoryUsage);\n        }\n        // 报警\n        if (StringUtils.isNotBlank(alertContent.toString())) {\n            String title = \"cachecloud机器内存报警:\";\n            appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.MACHINE_MEMORY_OVER_PRESET, title, alertContent.toString(), ip);\n            emailComponent.sendMailToAdmin(title, alertContent.toString());\n        }\n    }\n\n    /**\n     * 在主机ip上的端口port上启动一个进程，并check是否启动成功；\n     *\n     * @param ip    ip\n     * @param port  port\n     * @param shell shell命令\n     * @return 成功返回true，否则返回false；\n     */\n    @Override\n    public boolean startProcessAtPort(final String ip, final int port, final String shell) {\n        checkArgument(!Strings.isNullOrEmpty(ip), \"invalid ip.\");\n        checkArgument(port > 0 && port < 65536, \"invalid port\");\n        checkArgument(!Strings.isNullOrEmpty(shell), \"invalid shell.\");\n\n        boolean success = true;\n\n        try {\n            // 执行shell命令，有的是后台执行命令，没有返回值; 如果端口被占用，表示启动成功；\n            SSHUtil.execute(ip, shell);\n            success = isPortUsed(ip, port);\n        } catch (SSHException e) {\n            logger.error(\"execute shell command error, ip: {}, port: {}, shell: {}\", ip, port, shell);\n            logger.error(e.getMessage(), e);\n        }\n        return success;\n    }\n\n    /**\n     * 多次验证是否进程已经启动\n     *\n     * @param ip\n     * @param port\n     * @return\n     */\n    private boolean isPortUsed(final String ip, final int port) {\n        boolean isPortUsed = new IdempotentConfirmer() {\n            private int sleepTime = 100;\n\n            @Override\n            public boolean execute() {\n                try {\n                    boolean success = SSHUtil.isPortUsed(ip, port);\n                    if (!success) {\n                        TimeUnit.MILLISECONDS.sleep(sleepTime);\n                        sleepTime += 100;\n                    }\n                    return success;\n                } catch (SSHException e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                } catch (InterruptedException e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n        }.run();\n        return isPortUsed;\n    }\n\n    /**\n     * 执行shell命令，并将结果返回；\n     *\n     * @param ip    机器ip\n     * @param shell shell命令\n     * @return 命令的返回值\n     */\n    @Override\n    public String executeShell(final String ip, final String shell) {\n        checkArgument(!Strings.isNullOrEmpty(ip), \"invalid ip.\");\n        checkArgument(!Strings.isNullOrEmpty(shell), \"invalid shell.\");\n\n        String result = null;\n        try {\n            result = SSHUtil.execute(ip, shell);\n        } catch (SSHException e) {\n            logger.error(\"execute shell: {} at ip: {} error.\", shell, ip, e);\n            result = ConstUtils.INNER_ERROR;\n        }\n\n        return result;\n    }\n\n    @Override\n    public String executeShell(String ip, String shell, Integer timeout) {\n        checkArgument(!Strings.isNullOrEmpty(ip), \"invalid ip.\");\n        checkArgument(!Strings.isNullOrEmpty(shell), \"invalid shell.\");\n\n        String result = null;\n        try {\n            result = SSHUtil.execute(ip, shell, timeout);\n        } catch (SSHException e) {\n            logger.error(\"execute shell: {} at ip: {} error, timeout: {}\", shell, ip, timeout, e);\n            result = ConstUtils.INNER_ERROR;\n        }\n\n        return result;\n    }\n\n    /**\n     * 获取指定server上的一个可用的端口；type表示cache的类型；\n     * PortGenerator是线程安全的；\n     *\n     * @param ip   目标server；\n     * @param type cache类型\n     * @return 可用端口，如果为null，则表示发生异常；\n     */\n    @Override\n    public Integer getAvailablePort(final String ip, final int type) {\n        Integer availablePort = null;\n        if (type == ConstUtils.CACHE_REDIS_STANDALONE || type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n            availablePort = PortGenerator.getRedisPort(ip);\n            // 去实例表中再check一下，该端口是否从来没被使用过\n            while (instanceDao.getCountByIpAndPort(ip, availablePort) > 0) {\n                availablePort++;\n            }\n        } else if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            availablePort = PortGenerator.getRedisSentinelPort(ip);\n            // 去实例表中再check一下，该端口是否从来没被使用过\n            while (instanceDao.getCountByIpAndPort(ip, availablePort) > 0) {\n                availablePort++;\n            }\n        }\n        return availablePort;\n    }\n\n    /**\n     * 根据content的配置内容创建配置文件，并推送到目标server的约定目录下；\n     * 文件内容有更新，会覆写；\n     *\n     * @param host     要推送到的目标server；\n     * @param fileName 配置文件名\n     * @param content  配置文件的内容\n     * @return 配置文件在远程server上的绝对路径，如果为null则表示失败；\n     */\n    @Override\n    public String createRemoteFile(final String host, String fileName, List<String> content) {\n        checkArgument(!Strings.isNullOrEmpty(host), \"invalid host.\");\n        checkArgument(!Strings.isNullOrEmpty(fileName), \"invalid fileName.\");\n        checkArgument(content != null && content.size() > 0, \"content is empty.\");\n\n        String tmpDirectory = MachineProtocol.TMP_DIR + host + \"/\";\n\n        String localAbsolutePath = tmpDirectory + fileName;\n        File tmpDir = new File(tmpDirectory);\n        if (!tmpDir.exists()) {\n            if (!tmpDir.mkdirs()) {\n                logger.error(\"cannot create dir:{} directory. \", tmpDir.getAbsolutePath());\n            }\n        }\n\n        Path path = Paths.get(tmpDirectory + fileName);\n        String confDir = getMachineRelativeDir(host, DirEnum.CONF_DIR.getValue());\n        String remotePath = confDir + fileName;\n        /**\n         * 将配置文件的内容写到本地\n         */\n        try {\n            BufferedWriter bufferedWriter = Files\n                    .newBufferedWriter(path, Charset.forName(MachineProtocol.ENCODING_UTF8));\n            try {\n                for (String line : content) {\n                    bufferedWriter.write(line);\n                    bufferedWriter.newLine();\n                }\n            } finally {\n                if (bufferedWriter != null)\n                    bufferedWriter.close();\n            }\n        } catch (IOException e) {\n            logger.error(\"write redis config file error, ip: {}, filename: {}, content: {}, e\", host, fileName, content,\n                    e);\n            return null;\n        } finally {\n\n        }\n\n        /**\n         * 将配置文件推送到目标机器上\n         */\n        try {\n            // k8s资源创建相关目录\n            if (isK8sMachine(host)) {\n                String mkdirCommand = \"mkdir \" + MachineProtocol.getK8sConfDir(host) + \" \" + MachineProtocol.getK8sDataDir(host) + \" \" + MachineProtocol.getK8sLogDir(host);\n                String mkdirResult = SSHUtil.execute(host, mkdirCommand);\n                logger.info(\"execute mkdir :\" + mkdirResult);\n            }\n            SSHUtil.scpFileToRemote(host, localAbsolutePath, confDir);\n        } catch (SSHException e) {\n            logger.error(\"scp config file to remote server error: ip: {}, fileName: {}\", host, fileName, e);\n            return null;\n        }\n\n        /**\n         * 删除临时文件\n         */\n        File file = new File(localAbsolutePath);\n        if (file.exists()) {\n            boolean del = file.delete();\n            if (!del) {\n                logger.warn(\"file.delete:{}\", del);\n            }\n        }\n\n        return remotePath;\n    }\n\n    @Override\n    public List<MachineStats> getMachineStats(String ipLike) {\n        List<MachineInfo> machineInfoList = machineDao.getMachineInfoByLikeIp(ipLike);\n        List<SystemResource> versionList = resourceDao.getResourceList(ResourceEnum.REDIS.getValue());\n\n        String versionStr = \"\";//获取Redis所有有效安装版本\n        StringBuilder sb = new StringBuilder();\n        if (versionList != null && versionList.size() > 0) {\n            for (SystemResource version : versionList) {\n                sb.append(version.getName()).append(\"#1;\");\n            }\n            versionStr = sb.toString();\n        }\n        List<MachineStats> machineStatsList = new ArrayList<MachineStats>();\n        for (MachineInfo machineInfo : machineInfoList) {\n            String ip = machineInfo.getIp();\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip);\n            if (machineStats == null) {\n                machineStats = new MachineStats();\n            }\n            machineStats.setMemoryAllocated(instanceDao.getMemoryByHost(ip));\n            machineStats.setInfo(machineInfo);\n            machineStats.setIsInstall(1);//设置Redis都已经安装\n            // 判断是否已安装完成Redis\n            if (!StringUtils.isEmpty(versionStr)) {\n                for (String version : versionStr.split(\";\")) {\n                    if (StringUtils.isEmpty(machineInfo.getVersionInstall())) {\n                        machineStats.setIsInstall(0);\n                    }\n                    if (!StringUtils.isEmpty(version) && !StringUtils.isEmpty(machineInfo.getVersionInstall()) && machineInfo.getVersionInstall().indexOf(version) == -1) {\n                        machineStats.setIsInstall(0);\n                    }\n                }\n            }\n            machineStatsList.add(machineStats);\n        }\n        return machineStatsList;\n    }\n\n    /**\n     * 获取机器列表\n     *\n     * @param ipLike\n     * @param versionId\n     * @param isInstall\n     */\n    public List<MachineStats> getMachineStats(String ipLike, Integer useType, Integer type, Integer versionId, Integer isInstall, Integer k8sType, String realip) {\n\n        // 版本安装条件过滤\n        String versionName = (versionId == null || versionId == -1) ? \"\" : resourceDao.getResourceById(versionId).getName();\n        String installInfo = (isInstall == null || isInstall == -1) ? \"\" : String.valueOf(isInstall);\n        String versionCondition = \"\";\n        if (!StringUtil.isBlank(versionName) || !StringUtil.isBlank(installInfo)) {\n            versionCondition = versionName + \"#\" + installInfo;\n        }\n        List<MachineInfo> machineList = machineDao.getMachineInfoByCondition(ipLike, useType == null ? -1 : useType.intValue(), type == null ? -1 : type.intValue(), versionCondition, k8sType == null ? -1 : k8sType.intValue(), realip);\n\n        List<SystemResource> versionList = resourceDao.getResourceList(ResourceEnum.REDIS.getValue());\n        //获取Redis所有有效安装版本\n        final String versionStr = versionList.stream().map(version -> version.getName() + \"#1\").collect(Collectors.joining(\";\"));\n\n        return machineList.parallelStream().map(machineInfo -> {\n            String ip = machineInfo.getIp();\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip);\n            if (machineStats == null) {\n                machineStats = new MachineStats();\n                machineStats.setIp(ip);\n            }\n            machineStats.setMemoryAllocated(instanceDao.getMemoryByHost(ip));\n            machineStats.setInfo(machineInfo);\n            machineStats.setIsInstall(1);\n            // 判断是否已安装完成Redis\n            if (!StringUtils.isEmpty(versionStr)) {\n                for (String version : versionStr.split(\";\")) {\n                    if (StringUtils.isEmpty(machineInfo.getVersionInstall()) ||\n                            (!StringUtils.isEmpty(version) && machineInfo.getVersionInstall().indexOf(version) == -1)) {\n                        machineStats.setIsInstall(0);\n                    }\n                }\n            }\n            //填充machineMemInfo信息\n            MachineMemInfo machineMemInfo = new MachineMemInfo();\n            Map memRes = instanceStatsDao.getMachineMemByIp(ip);\n            machineMemInfo.setIp(ip);\n            machineMemInfo.setApplyMem(MapUtils.getLongValue(memRes, \"applyMem\", 0l));\n            machineMemInfo.setUsedMem(MapUtils.getLongValue(memRes, \"usedMem\", 0l));\n            machineMemInfo.setUsedMemRss(MapUtils.getDoubleValue(memRes, \"usedMemRss\", 0d));\n            machineStats.setMachineMemInfo(machineMemInfo);\n\n            return machineStats;\n        }).collect(Collectors.toList());\n    }\n\n    @Override\n    public List<MachineStats> getAllMachineStats() {\n        List<MachineStats> list = machineStatsDao.getAllMachineStats();\n        for (MachineStats ms : list) {\n            String ip = ms.getIp();\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(ip);\n            if (machineInfo == null || machineInfo.isOffline()) {\n                continue;\n            }\n\n            int memoryHost = instanceDao.getMemoryByHost(ip);\n            getMachineMemoryDetail(ms.getIp());\n\n            //获取机器申请和使用内存\n            long applyMem = 0;\n            long usedMem = 0;\n            List<InstanceStats> instanceStats = instanceStatsDao.getInstanceStatsByIp(ip);\n            for (InstanceStats instance : instanceStats) {\n                applyMem += instance.getMaxMemory();\n                usedMem += instance.getUsedMemory();\n            }\n            MachineMemInfo machineMemInfo = new MachineMemInfo();\n            machineMemInfo.setIp(ip);\n            machineMemInfo.setApplyMem(applyMem);\n            machineMemInfo.setUsedMem(usedMem);\n            ms.setMachineMemInfo(machineMemInfo);\n\n            ms.setMemoryAllocated(memoryHost);\n            ms.setInfo(machineInfo);\n        }\n        return list;\n    }\n\n    @Override\n    public List<MachineMemStatInfo> getAllValidMachineMem(List<String> excludeMachineList, String room,\n                                                          Integer useType) {\n        List<MachineMemStatInfo> machineMemStatInfoList = machineDao.getMachineMemStatInfoByCondition(room, useType);\n        return machineMemStatInfoList.parallelStream().filter(memStatInfo -> !excludeMachineList.contains(memStatInfo.getIp()))\n                .map(memStatInfo -> {\n                    MachineMemStatInfo memStatInfoNew = memStatInfo;\n                    Map memRes = instanceStatsDao.getMachineMemByIp(memStatInfo.getIp());\n                    memStatInfoNew.setApplyMem(MapUtils.getLong(memRes, \"applyMem\", 0l));\n                    memStatInfoNew.setUsedMem(MapUtils.getLong(memRes, \"usedMem\", 0l));\n                    memStatInfoNew.setInstanceNum(MapUtils.getIntValue(memRes, \"instanceNum\", 0));\n                    return memStatInfoNew;\n                })\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public List<MachineMemStatInfo> getValidMachineMemByIpList(List<String> ipList) {\n        if (ipList != null) {\n            try {\n                List<MachineMemStatInfo> memStatInfoList = machineDao.getMachineMemStatInfoByIpList(ipList);\n                for (MachineMemStatInfo memStatInfo : memStatInfoList) {\n                    Map memRes = instanceStatsDao.getMachineMemByIp(memStatInfo.getIp());\n                    memStatInfo.setApplyMem(MapUtils.getLong(memRes, \"applyMem\", 0l));\n                    memStatInfo.setUsedMem(MapUtils.getLong(memRes, \"usedMem\", 0l));\n                    memStatInfo.setInstanceNum(MapUtils.getIntValue(memRes, \"instanceNum\", 0));\n                }\n                return memStatInfoList;\n            } catch (Exception e) {\n                logger.info(\"getValidMachineMemByIpList error: {}\", e.getMessage());\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public MachineInfo getMachineInfoByIp(String ip) {\n        return machineDao.getMachineInfoByIp(ip);\n    }\n\n    @Override\n    public MachineStats getMachineMemoryDetail(String ip) {\n        long applyMem = 0;\n        long usedMem = 0;\n        long usedMemRss = 0;\n        long usedDisk = 0;\n        List<InstanceStats> instanceStats = instanceStatsDao.getInstanceStatsByIp(ip);\n        for (InstanceStats instance : instanceStats) {\n            applyMem += instance.getMaxMemory();\n            usedMem += instance.getUsedMemory();\n            double memFragmentationRatio = instance.getMemFragmentationRatio();\n            if (memFragmentationRatio > 1d) {\n                usedMemRss += instance.getUsedMemory() * memFragmentationRatio;\n            } else {\n                usedMemRss += instance.getUsedMemory();\n            }\n            usedDisk += instance.getUsedDisk();\n        }\n\n        MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip);\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(ip);\n        // 机器下线 查不到相关信息\n        if (machineStats == null) {\n            return null;\n        }\n        // 机器下线 查不到相关信息\n        if (machineInfo != null) {\n            machineStats.setInfo(machineInfo);\n        }\n        MachineMemInfo machineMemInfo = new MachineMemInfo();\n        machineMemInfo.setIp(ip);\n        machineMemInfo.setApplyMem(applyMem);\n        machineMemInfo.setUsedMem(usedMem);\n        machineMemInfo.setUsedMemRss(usedMemRss);\n        machineMemInfo.setUsedDisk(usedDisk);\n        machineStats.setMachineMemInfo(machineMemInfo);\n\n        int memoryHost = instanceDao.getMemoryByHost(ip);\n        machineStats.setMemoryAllocated(memoryHost);\n\n        return machineStats;\n    }\n\n    public List<InstanceStats> getMachineInstanceStatsByIp(String ip) {\n        return instanceStatsDao.getInstanceStatsByIp(ip);\n    }\n\n    @Override\n    public List<InstanceInfo> getMachineInstanceInfo(String ip) {\n        List<InstanceInfo> resultList = instanceDao.getInstListByIp(ip);\n        if (resultList != null && resultList.size() > 0) {\n            for (InstanceInfo instanceInfo : resultList) {\n                int type = instanceInfo.getType();\n                if (instanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    continue;\n                }\n                if (TypeUtil.isRedisType(type)) {\n                    if (TypeUtil.isRedisSentinel(type)) {\n                        continue;\n                    }\n                    String host = instanceInfo.getIp();\n                    int port = instanceInfo.getPort();\n                    long appId = instanceInfo.getAppId();\n                    AppDesc appDesc = appDao.getAppDescById(appId);\n                    String password = appDesc.getAppPassword();\n                    BooleanEnum isMaster = redisCenter.isMaster(appId, host, port);\n                    instanceInfo.setRoleDesc(isMaster);\n                    if (isMaster == BooleanEnum.FALSE) {\n                        HostAndPort hap = redisCenter.getMaster(host, port, password);\n                        if (hap != null) {\n                            instanceInfo.setMasterHost(hap.getHost());\n                            instanceInfo.setMasterPort(hap.getPort());\n                            for (InstanceInfo innerInfo : resultList) {\n                                if (innerInfo.getIp().equals(hap.getHost())\n                                        && innerInfo.getPort() == hap.getPort()) {\n                                    instanceInfo.setMasterInstanceId(innerInfo.getId());\n                                    break;\n                                }\n                            }\n                        }\n                    }\n\n                }\n            }\n        } else {\n            return resultList;\n        }\n        return resultList;\n    }\n\n    @Override\n    public String showInstanceRecentLog(InstanceInfo instanceInfo, int maxLineNum) {\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        int type = instanceInfo.getType();\n        String logType = \"\";\n        if (TypeUtil.isRedisDataType(type)) {\n            logType = \"redis-\";\n        } else if (TypeUtil.isRedisSentinel(type)) {\n            logType = \"redis-sentinel-\";\n        }\n\n        String remoteFilePath = getMachineRelativeDir(host, DirEnum.LOG_DIR.getValue()) + logType + port + \"-*.log\";\n        StringBuilder command = new StringBuilder();\n        command.append(\"/usr/bin/tail -n\").append(maxLineNum).append(\" \").append(remoteFilePath);\n        try {\n            return SSHUtil.execute(host, command.toString());\n        } catch (SSHException e) {\n            logger.error(e.getMessage(), e);\n            return \"\";\n        }\n    }\n\n    @Override\n    public List<MachineInfo> getMachineInfoByType(TypeEnum typeEnum) {\n        try {\n            return machineDao.getMachineInfoByType(typeEnum.getType());\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public Map<String, Integer> getMachineInstanceCountMap() {\n        List<Map<String, Object>> mapList = instanceDao.getMachineInstanceCountMap();\n        if (CollectionUtils.isEmpty(mapList)) {\n            return Collections.emptyMap();\n        }\n\n        Map<String, Integer> resultMap = new HashMap<String, Integer>();\n        for (Map<String, Object> map : mapList) {\n            String ip = MapUtils.getString(map, \"ip\", \"\");\n            if (StringUtils.isBlank(ip)) {\n                continue;\n            }\n            int count = MapUtils.getIntValue(map, \"count\");\n            resultMap.put(ip, count);\n        }\n        return resultMap;\n    }\n\n    public Map<String, MachineInfo> getK8sMachineMap() {\n        Map<String, MachineInfo> k8sMachineMaps = new HashMap<String, MachineInfo>();\n        List<MachineInfo> k8sMachineList = machineDao.getK8sMachineList();\n        if (!CollectionUtils.isEmpty(k8sMachineList)) {\n            for (MachineInfo machineInfo : k8sMachineList) {\n                k8sMachineMaps.put(machineInfo.getIp(), machineInfo);\n            }\n        }\n        return k8sMachineMaps;\n    }\n\n    public List<MachineRoom> getEffectiveRoom() {\n        return machineRoomDao.getEffectiveRoom();\n    }\n\n    @Override\n    public List<MachineRoom> getAllRoom() {\n        return machineRoomDao.getAllRoom();\n    }\n\n    @Override\n    public Map<MachineMemoryDistriEnum, Integer> getMaxMemoryDistribute() {\n        Map<MachineMemoryDistriEnum, Integer> resultMap = new HashMap<MachineMemoryDistriEnum, Integer>();\n        List<MachineStats> machineStatsList = machineStatsDao.getAllMachineStats();\n        for (MachineStats machineStats : machineStatsList) {\n            int percent = 0;\n            if (machineStats.getMachineMemory() > 0) {\n                percent = machineStats.getMaxMemory() * 100 / machineStats.getMachineMemory();\n            }\n            MachineMemoryDistriEnum machineMemoryDistriEnum = MachineMemoryDistriEnum.getRightPercentDistri(percent);\n            if (resultMap.containsKey(machineMemoryDistriEnum)) {\n                resultMap.put(machineMemoryDistriEnum, resultMap.get(machineMemoryDistriEnum) + 1);\n            } else {\n                resultMap.put(machineMemoryDistriEnum, 1);\n            }\n        }\n        return resultMap;\n    }\n\n    @Override\n    public Map<MachineMemoryDistriEnum, Integer> getUsedMemoryDistribute() {\n        Map<MachineMemoryDistriEnum, Integer> resultMap = new HashMap<MachineMemoryDistriEnum, Integer>();\n        //机器自身统计map\n        List<MachineStats> machineStatsList = machineStatsDao.getAllMachineStats();\n        //机器实例统计map\n        List<MachineInstanceStat> machineInstanceStatList = instanceStatsDao.getMachineInstanceStatList();\n        Map<String, MachineInstanceStat> machineInstanceStatMap = new HashMap<String, MachineInstanceStat>();\n        for (MachineInstanceStat machineInstanceStat : machineInstanceStatList) {\n            machineInstanceStatMap.put(machineInstanceStat.getIp(), machineInstanceStat);\n        }\n        for (MachineStats machineStats : machineStatsList) {\n            int machineMemory = machineStats.getMachineMemory();\n            long usedMemory = machineInstanceStatMap.containsKey(machineStats.getIp()) ? machineInstanceStatMap.get(machineStats.getIp()).getUsedMemory() / 1024 / 1024 : 0;\n            int percent = 0;\n            if (machineMemory > 0) {\n                percent = (int) (usedMemory * 100 / machineMemory);\n            }\n            MachineMemoryDistriEnum machineMemoryDistriEnum = MachineMemoryDistriEnum.getRightPercentDistri(percent);\n            if (machineMemoryDistriEnum == null) {\n                logger.warn(\"=======ip {} percent {} is not is MachineMemoryDistriEnum========\", machineStats.getIp(), percent);\n                continue;\n            }\n            if (resultMap.containsKey(machineMemoryDistriEnum)) {\n                resultMap.put(machineMemoryDistriEnum, resultMap.get(machineMemoryDistriEnum) + 1);\n            } else {\n                resultMap.put(machineMemoryDistriEnum, 1);\n            }\n        }\n        return resultMap;\n    }\n\n    public int getMachineNum(int type) {\n        List<MachineInfo> machineInfoList = machineDao.getMachineInfoByType(type);\n        return machineInfoList.size();\n    }\n\n    @Override\n    public List<MachineStatsVo> getmachineStatsVoList() {\n        List<MachineInfo> machineInfoList = machineDao.getAllMachines();\n\n        //机器自身统计map\n        List<MachineStats> machineStatsList = machineStatsDao.getAllMachineStats();\n        Map<String, MachineStats> machineStatsMap = new HashMap<String, MachineStats>();\n        for (MachineStats machineStats : machineStatsList) {\n            machineStatsMap.put(machineStats.getIp(), machineStats);\n        }\n\n        //机器实例统计map\n        List<MachineInstanceStat> machineInstanceStatList = instanceStatsDao.getMachineInstanceStatList();\n        Map<String, MachineInstanceStat> machineInstanceStatMap = new HashMap<String, MachineInstanceStat>();\n        for (MachineInstanceStat machineInstanceStat : machineInstanceStatList) {\n            machineInstanceStatMap.put(machineInstanceStat.getIp(), machineInstanceStat);\n        }\n\n        Map<String, MachineStatsVo> machineRoomMachineStatsVoMap = new HashMap<String, MachineStatsVo>();\n        Set<String> realIpSet = new HashSet<>();\n        for (MachineInfo machineInfo : machineInfoList) {\n            String ip = machineInfo.getIp();\n            String realIp = machineInfo.getRealIp();\n\n            //机器统计\n            MachineStats machineStats = machineStatsMap.get(ip);\n            if (machineStats == null) {\n                machineStats = new MachineStats();\n            }\n            // 单位MB\n            long machineMemoryTotal = machineInfo.getMem() * 1024L;\n            long machineFreeTotal = machineMemoryTotal - machineStats.getMaxMemory();\n\n            //实例统计\n            MachineInstanceStat machineInstanceStat = machineInstanceStatMap.get(ip);\n            if (machineInstanceStat == null) {\n                machineInstanceStat = new MachineInstanceStat();\n            }\n            long instanceMaxMemory = machineInstanceStat.getMaxMemory();\n            long instanceUsedmemory = machineInstanceStat.getUsedMemory();\n            long instanceApplyDisk = machineInstanceStat.getApplyDisk();\n            long instanceUsedDisk = machineInstanceStat.getUsedDisk();\n            String diskTotal = machineStats.getDiskTotal();\n            String diskAvailable = machineStats.getDiskAvailable();\n            long machineDiskTotal = 0;\n            long machineDiskAvailable = 0;\n            if (StringUtils.isEmpty(realIp) || !realIpSet.contains(realIp)) {\n                if (StringUtils.isNotEmpty(diskTotal)) {\n                    machineDiskTotal = Long.parseLong(diskTotal);\n                }\n                if (StringUtils.isNotEmpty(diskAvailable)) {\n                    machineDiskAvailable = Long.parseLong(diskAvailable);\n                }\n            }\n            realIpSet.add(realIp);\n\n            String machineRoom = machineInfo.getRoom();\n            if (machineRoomMachineStatsVoMap.containsKey(machineRoom)) {\n                MachineStatsVo machineStatsVo = machineRoomMachineStatsVoMap.get(machineRoom);\n                machineStatsVo.setTotalMachineMem(machineMemoryTotal + machineStatsVo.getTotalMachineMem());\n                machineStatsVo.setTotalMachineFreeMem(machineFreeTotal + machineStatsVo.getTotalMachineFreeMem());\n                machineStatsVo.setTotalInstanceMaxMem(instanceMaxMemory + machineStatsVo.getTotalInstanceMaxMem());\n                machineStatsVo.setTotalInstanceUsedMem(instanceUsedmemory + machineStatsVo.getTotalInstanceUsedMem());\n\n                machineStatsVo.setTotalMachineDisk(machineDiskTotal + machineStatsVo.getTotalMachineDisk());\n                machineStatsVo.setTotalMachineFreeDisk(machineDiskAvailable + machineStatsVo.getTotalMachineFreeDisk());\n                machineStatsVo.setTotalInstanceApplyDisk(instanceApplyDisk + machineStatsVo.getTotalInstanceApplyDisk());\n                machineStatsVo.setTotalInstanceUsedDisk(instanceUsedDisk + machineStatsVo.getTotalInstanceUsedDisk());\n            } else {\n                MachineStatsVo machineStatsVo = new MachineStatsVo();\n                machineStatsVo.setMachineRoom(machineRoom);\n                machineStatsVo.setTotalMachineMem(machineMemoryTotal);\n                machineStatsVo.setTotalMachineFreeMem(machineFreeTotal);\n                machineStatsVo.setTotalInstanceMaxMem(instanceMaxMemory);\n                machineStatsVo.setTotalInstanceUsedMem(instanceUsedmemory);\n\n                machineStatsVo.setTotalMachineDisk(machineDiskTotal);\n                machineStatsVo.setTotalMachineFreeDisk(machineDiskAvailable);\n                machineStatsVo.setTotalInstanceApplyDisk(instanceApplyDisk);\n                machineStatsVo.setTotalInstanceUsedDisk(instanceUsedDisk);\n                machineRoomMachineStatsVoMap.put(machineRoom, machineStatsVo);\n            }\n        }\n\n        List<MachineStatsVo> machineStatsVoList = new ArrayList<MachineStatsVo>(machineRoomMachineStatsVoMap.values());\n\n        MachineStatsVo totalMachineStatsVo = new MachineStatsVo();\n        totalMachineStatsVo.setMachineRoom(\"total\");\n        for (MachineStatsVo machineStatsVo : machineStatsVoList) {\n            totalMachineStatsVo.setTotalMachineMem(totalMachineStatsVo.getTotalMachineMem() + machineStatsVo.getTotalMachineMem());\n            totalMachineStatsVo.setTotalMachineFreeMem(totalMachineStatsVo.getTotalMachineFreeMem() + machineStatsVo.getTotalMachineFreeMem());\n            totalMachineStatsVo.setTotalInstanceMaxMem(totalMachineStatsVo.getTotalInstanceMaxMem() + machineStatsVo.getTotalInstanceMaxMem());\n            totalMachineStatsVo.setTotalInstanceUsedMem(totalMachineStatsVo.getTotalInstanceUsedMem() + machineStatsVo.getTotalInstanceUsedMem());\n\n            totalMachineStatsVo.setTotalMachineDisk(totalMachineStatsVo.getTotalMachineDisk() + machineStatsVo.getTotalMachineDisk());\n            totalMachineStatsVo.setTotalMachineFreeDisk(totalMachineStatsVo.getTotalMachineFreeDisk() + machineStatsVo.getTotalMachineFreeDisk());\n            totalMachineStatsVo.setTotalInstanceApplyDisk(totalMachineStatsVo.getTotalInstanceApplyDisk() + machineStatsVo.getTotalInstanceApplyDisk());\n            totalMachineStatsVo.setTotalInstanceUsedDisk(totalMachineStatsVo.getTotalInstanceUsedDisk() + machineStatsVo.getTotalInstanceUsedDisk());\n        }\n        machineStatsVoList.add(0, totalMachineStatsVo);\n\n        return machineStatsVoList;\n    }\n\n    /**\n     * 获取redis基准目录\n     * 例如/media/disk1/fordata/redis_server/redis-cluster/cluster_mmuIllegalDupUnhandledCount_zw/sentinel-22090\n     *\n     * @param appId\n     * @param port\n     * @param instanceTypeEnum\n     * @return\n     */\n    @Override\n    public String getInstanceRemoteBasePath(long appId, int port, InstanceTypeEnum instanceTypeEnum) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.warn(BaseTask.marker, \"appId {} appDesc is null\", appId);\n            return \"\";\n        }\n        String instanceTypeName = instanceTypeEnum.getName();\n        String basePath = InstanceTypeEnum.PIKA.getType() == instanceTypeEnum.getType() ? ConstUtils.PIKA_INSTALL_BASE_DIR : ConstUtils.REDIS_INSTALL_BASE_DIR;\n        return String.format(\"%s/%s/%s-%d\", basePath, appDesc.getName(), instanceTypeName, port);\n    }\n\n    public String getMachineRelativeDir(String host, int dirType) {\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n        if (machineInfo != null && machineInfo.isK8sMachine(machineInfo.getK8sType())) {\n            return MachineProtocol.getK8sDir(host, dirType);\n        }\n        return MachineProtocol.getDir(dirType);\n    }\n\n    public Boolean isK8sMachine(String host) {\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n        if (machineInfo != null && machineInfo.isK8sMachine(machineInfo.getK8sType())) {\n            return true;\n        }\n        return false;\n    }\n\n    public Map<String, Object> getExceptionMachineEnv(Date searchDate) {\n\n        Map<String, Object> exceptionMap = new HashMap<String, Object>();\n        Map<String, Object> allMachineEnvMap = getAllMachineEnv(searchDate, MachineInfoEnum.MachineTypeEnum.ALL.getValue());\n        // 过滤需要监控的数据\n        List<Map<String, Object>> containerlist = (List<Map<String, Object>>) allMachineEnvMap.get(MachineInfoEnum.MachineEnum.CONTAINER.getValue());\n        List<Map<String, Object>> hostlist = (List<Map<String, Object>>) allMachineEnvMap.get(MachineInfoEnum.MachineEnum.HOST.getValue());\n\n        exceptionMap.put(MachineInfoEnum.MachineEnum.CONTAINER.getValue(), containerlist.stream().filter(map -> MapUtils.getInteger(map, \"status\") != CheckEnum.CONSISTENCE.getValue()).collect(Collectors.toList()));\n        exceptionMap.put(MachineInfoEnum.MachineEnum.HOST.getValue(), hostlist.stream().filter(map -> MapUtils.getInteger(map, \"status\") != CheckEnum.CONSISTENCE.getValue()).collect(Collectors.toList()));\n        return exceptionMap;\n    }\n\n    public Map<String, Object> getAllMachineEnv(Date searchDate, int type) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n\n        List<MachineInfo> allMachines = machineDao.getAllMachines();\n        Map<String, MachineInfo> ipMap = new HashMap<>();\n        Set<String> hostlist = new HashSet<String>();\n\n        SimpleDateFormat dateFormat = new SimpleDateFormat(\"dd MMM yyyy\", Locale.ENGLISH);\n        if (!CollectionUtils.isEmpty(allMachines)) {\n            for (MachineInfo machineInfo : allMachines) {\n                ipMap.put(machineInfo.getIp(), machineInfo);\n                if (StringUtils.isNotBlank(machineInfo.getRealIp())) {\n                    hostlist.add(machineInfo.getRealIp());\n                }\n            }\n        }\n        /**\n         *  检测容器:\n         *  1.内存分配策略\n         *  2.thp大内存页配置\n         *  3.内存swap配置\n         *  4.容器nproc配置\n         */\n        String container_cmd =\n                \"cat /proc/sys/vm/overcommit_memory;\" +\n                        \"cat /proc/sys/vm/swappiness;\" +\n                        \"cat /sys/kernel/mm/transparent_hugepage/enabled;\" +\n                        \"cat /sys/kernel/mm/transparent_hugepage/defrag;\" +\n                        \"cat /etc/security/limits.d/*-nproc.conf | grep '*          soft    nproc'\" +\n                        \"\";\n\n        String ubuntu_container_cmd =\n                \"cat /proc/sys/vm/overcommit_memory;\" +\n                        \"cat /proc/sys/vm/swappiness;\" +\n                        \"cat /sys/kernel/mm/transparent_hugepage/enabled;\" +\n                        \"cat /sys/kernel/mm/transparent_hugepage/defrag;\" +\n                        \"cat /etc/security/limits.conf | grep '*          soft    nproc'\" +\n                        \"\";\n        /**\n         * 检测宿主机:\n         * 1.检测用户连接的进程数 大于>=1024\n         * 2.检测宿主机所有实例aof写盘阻塞 >=3次\n         * 3.检测 somaxconn 512\n         * 4.检测 sshpass安装\n         * 5.运行redis实例总数\n         * 6.ulimit 打开文件句柄检测\n         * 7.磁盘/内存使用情况\n         */\n        String machine_cmd =\n                \"cat /proc/sys/net/core/somaxconn;\" +\n                        \"cat /data/redis/logs/*/* | grep '\" + dateFormat.format(searchDate) + \"' | grep 'slow down Redis'  | wc -l;\" +\n                        \"ps -u cachecloud -L | wc -l;\" +\n                        \"sshpass -V | head -1;\" +\n                        \"ulimit -n;\" +\n                        \"echo 0;\" +\n                        \"df -h | grep '/dev' | grep '/data' | awk '{print $5\\\"(\\\"$3\\\"/\\\"$2\\\")\\\"}';\" +\n                        \"ps -ef | grep redis | wc -l;\" +\n                        \"cat /etc/security/limits.d/*-nproc.conf | grep '*          soft    nproc';\" +\n                        \"\";\n\n        List<Map<String, Object>> containerInfo = new ArrayList<>();\n        List<Map<String, Object>> machineInfo = new ArrayList<>();\n        long phase1 = System.currentTimeMillis();\n        if (type == MachineInfoEnum.MachineTypeEnum.CONTAINER.getValue() || type == MachineInfoEnum.MachineTypeEnum.ALL.getValue()) {\n            if (MapUtils.isNotEmpty(ipMap)) {\n                ForkJoinTask<Map<String, Map<String, Object>>> container_task = forkJoinPool.submit(() -> ipMap.entrySet().parallelStream()\n                        .collect(Collectors.toMap(ipEntry -> ipEntry.getKey(), ipEntry ->\n                        {\n                            if (ipEntry.getValue().getDisType() == MachineInfoEnum.DisTypeEnum.CENTOS.getType()) {\n                                return new MachinetaskCallable(ipEntry.getKey(), container_cmd, sshService, MachineInfoEnum.MachineEnum.CONTAINER.getValue()).call();\n                            } else {\n                                return new MachinetaskCallable(ipEntry.getKey(), ubuntu_container_cmd, sshService, MachineInfoEnum.MachineEnum.CONTAINER.getValue()).call();\n                            }\n                        })));\n                try {\n                    Map<String, Map<String, Object>> container_result = container_task.get(30, TimeUnit.SECONDS);\n                    if (!MapUtils.isEmpty(container_result)) {\n                        for (Map.Entry<String, Map<String, Object>> container : container_result.entrySet()) {\n                            Map<String, Object> res = container.getValue();\n                            if (!MapUtils.isEmpty(res)) {\n                                containerInfo.add(res);\n                            }\n                        }\n                    }\n                    logger.info(\"container result size:{}\", container_result.size());\n                } catch (InterruptedException e) {\n                    logger.error(\"container error\", e);\n                } catch (ExecutionException e) {\n                    logger.error(\"container error\", e);\n                } catch (TimeoutException e) {\n                    logger.error(\"container error\", e);\n                }\n            }\n        }\n        long phase2 = System.currentTimeMillis();\n        logger.info(\"container check env cost time:{} ms\", phase2 - phase1);\n\n        if (type == MachineInfoEnum.MachineTypeEnum.HOST.getValue() || type == MachineInfoEnum.MachineTypeEnum.ALL.getValue()) {\n            if (!CollectionUtils.isEmpty(hostlist)) {\n                ForkJoinTask<Map<String, Map<String, Object>>> machine_task = forkJoinPool.submit(() -> hostlist.parallelStream().collect(Collectors.toMap(machineIp -> machineIp, machineIp -> new MachinetaskCallable(machineIp, machine_cmd, sshService, MachineInfoEnum.MachineEnum.HOST.getValue()).call())));\n                try {\n                    Map<String, Map<String, Object>> host_result = machine_task.get(30, TimeUnit.SECONDS);\n                    if (!MapUtils.isEmpty(host_result)) {\n                        for (Map.Entry<String, Map<String, Object>> host : host_result.entrySet()) {\n                            Map<String, Object> res = host.getValue();\n                            if (!MapUtils.isEmpty(res)) {\n                                machineInfo.add(res);\n                            }\n                        }\n                    }\n                    logger.info(\"machine result size:{}\", host_result.size());\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                } catch (ExecutionException e) {\n                    e.printStackTrace();\n                } catch (TimeoutException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        logger.info(\"host check env cost time:{} ms\", System.currentTimeMillis() - phase2);\n\n        resultMap.put(MachineInfoEnum.MachineEnum.CONTAINER.getValue(), containerInfo);\n        resultMap.put(MachineInfoEnum.MachineEnum.HOST.getValue(), machineInfo);\n        return resultMap;\n    }\n\n    private class MachinetaskCallable implements Callable<Map<String, Object>> {\n\n        private String ip;\n        private String cmd;\n        private SSHService sshService;\n        private String type;\n\n        public MachinetaskCallable(String ip, String cmd, SSHService sshService, String type) {\n            this.ip = ip;\n            this.cmd = cmd;\n            this.sshService = sshService;\n            this.type = type;\n        }\n\n\n        @Override\n        public Map<String, Object> call() {\n\n            Map<String, Object> machineResult = new HashMap<String, Object>();\n            String info = null;\n            try {\n                info = sshService.execute(ip, cmd);\n                machineResult.put(\"ip\", ip);\n                if (!StringUtil.isBlank(info)) {\n                    if (type.equals(MachineInfoEnum.MachineEnum.CONTAINER.getValue())) {\n                        MachineEnv containerEnv = convertContainer(info);\n                        if (containerEnv != null) {\n                            machineResult.put(\"envs\", containerEnv);\n                            machineResult.put(\"status\", MachineEnv.checkContainer(containerEnv));\n                        } else {\n                            machineResult.put(\"status\", CheckEnum.EXCEPTION.getValue());\n                            machineResult.put(\"envs\", MachineEnv.getDefaultEnv());\n                        }\n                    } else if (type.equals(MachineInfoEnum.MachineEnum.HOST.getValue())) {\n                        MachineEnv hostEnv = convertHost(info);\n                        if (hostEnv != null) {\n                            machineResult.put(\"envs\", hostEnv);\n                            machineResult.put(\"status\", MachineEnv.checkHost(hostEnv));\n                        } else {\n                            machineResult.put(\"status\", CheckEnum.EXCEPTION.getValue());\n                            machineResult.put(\"envs\", MachineEnv.getDefaultEnv());\n                        }\n                    }\n                } else {\n                    machineResult.put(\"status\", CheckEnum.EXCEPTION.getValue());\n                    machineResult.put(\"envs\", MachineEnv.getDefaultEnv());\n                }\n            } catch (SSHException e) {\n                logger.error(\"MachinetaskCallable ip:{} error msg :{}\", ip, e.getMessage());\n                machineResult.put(\"status\", CheckEnum.EXCEPTION.getValue());\n                machineResult.put(\"envs\", MachineEnv.getDefaultEnv());\n            }\n\n            return machineResult;\n        }\n    }\n\n\n    public MachineEnv convertContainer(String cmdResult) {\n\n        String[] envs = cmdResult.split(\"\\n\");\n        String nproc = \"\";\n        try {\n            if(envs.length >= 5){\n                nproc = StringUtils.isBlank(envs[4]) ? \"\" : envs[4];\n            }\n        } catch (Exception e) {\n            logger.error(\"MachineEnv convertContainer cmdResult:{} error {}:\", cmdResult, e.getMessage());\n        }\n        return new MachineEnv(envs[0], envs[1], envs[2], envs[3], nproc);\n\n    }\n\n    public MachineEnv convertHost(String cmdResult) {\n\n        int fsync_delay_times = -1;\n        int nproc_threads = -1;\n        int unlimit = -1;\n        int unlimit_used = -1;\n        int instanceNum = -1;\n        try {\n            String[] envs = cmdResult.split(System.lineSeparator());\n            fsync_delay_times = StringUtils.isBlank(envs[1]) ? -1 : Integer.parseInt(envs[1]);\n            nproc_threads = StringUtils.isBlank(envs[2]) ? -1 : Integer.parseInt(envs[2]);\n            unlimit = StringUtils.isBlank(envs[4]) ? -1 : Integer.parseInt(envs[4]);\n            unlimit_used = StringUtils.isBlank(envs[5]) ? -1 : Integer.parseInt(envs[5]);\n            instanceNum = StringUtils.isBlank(envs[7]) ? -1 : Integer.parseInt(envs[7]);\n            return new MachineEnv(envs[0], fsync_delay_times, nproc_threads, envs[3], unlimit_used, unlimit, envs[6], instanceNum);\n        } catch (Exception e) {\n            logger.error(\"convertMachine error :{} {}\", cmdResult, e.getMessage(), e);\n            return new MachineEnv(\"-1\", fsync_delay_times, nproc_threads, \"\", unlimit_used, unlimit, \"\", instanceNum);\n\n        }\n    }\n\n    public String getFirstMachineIp() {\n        List<MachineInfo> machines = machineDao.getAllMachines();\n        if (!CollectionUtils.isEmpty(machines)) {\n            return machines.get(0).getIp();\n        }\n        return null;\n    }\n\n    public boolean checkMachineMemory(String ip) {\n\n        MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip);\n        float memThreshold = Float.parseFloat(machineStats.getMemoryFree()) / Float.parseFloat(machineStats.getMemoryTotal());\n        if (machineStats == null || memThreshold < 0.15) {\n            return false;\n        }\n        return true;\n    }\n\n    public List<MachineInfo> getMachineListByRealIp(String realIp){\n        return machineDao.getMachineListByRealIp(realIp);\n    }\n\n    /**\n     * 获取机器配置信息及已分布redis实例（数量、申请内存、使用内存、使用内存rss等）\n     */\n    public List<MachineMemStatInfo> getMachineInfoAndUsedInfo(String room, Integer type, Integer useType, Integer disType, String ip){\n        return machineDao.getMachineInfoAndUsedInfo(room, type, useType, disType, ip);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/machine/impl/MachineDeployCenterImpl.java",
    "content": "package com.sohu.cache.machine.impl;\n\nimport com.google.common.base.Strings;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.entity.MachineRelation;\nimport com.sohu.cache.entity.MachineRoom;\nimport com.sohu.cache.machine.MachineDeployCenter;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 机器部署相关\n *\n * @author leifu\n * changed @Date 2016-4-24\n * @Time 下午5:07:30\n */\n@Service(\"machineDeployCenter\")\npublic class MachineDeployCenterImpl implements MachineDeployCenter {\n    private Logger logger = LoggerFactory.getLogger(MachineDeployCenterImpl.class);\n    @Autowired\n    private MachineDao machineDao;\n    @Autowired\n    private MachineRoomDao machineRoomDao;\n    @Autowired\n    private MachineStatsDao machineStatsDao;\n    @Autowired\n    private ServerStatusDao serverStatusDao;\n    @Autowired\n    private MachineRelationDao machineRelationDao;\n\n    /**\n     * 将机器加入资源池并统计、监控\n     *\n     * @param machineInfo\n     * @return\n     */\n    @Override\n    public boolean addMachine(MachineInfo machineInfo) {\n        boolean success = true;\n\n        if (machineInfo == null || Strings.isNullOrEmpty(machineInfo.getIp())) {\n            logger.error(\"machineInfo is null or ip is valid.\");\n            return false;\n        }\n        // 将机器信息保存到db中\n        try {\n            machineDao.saveMachineInfo(machineInfo);\n        } catch (Exception e) {\n            logger.error(\"save machineInfo: {} to db error.\", machineInfo.toString(), e);\n            return false;\n        }\n\n        if (success) {\n            logger.info(\"save and deploy machine ok, machineInfo: {}\", machineInfo.toString());\n        }\n        return success;\n    }\n\n    @Override\n    public boolean addMachineRoom(MachineRoom room) {\n        boolean success = true;\n        try {\n            machineRoomDao.saveRoom(room);\n        } catch (Exception e) {\n            logger.error(\"save machineRoom: {} to db error.\", room.toString(), e);\n            return false;\n        }\n\n        if (success) {\n            logger.info(\"save machineRoom ok, machineRoom: {}\", room.toString());\n        }\n        return success;\n    }\n\n    @Override\n    public boolean removeMachineRoom(int roomId) {\n        try {\n            machineRoomDao.removeRoom(roomId);\n        } catch (Exception e) {\n            logger.error(\"remove machineRoom from db error, machineRoom: {}\", roomId, e);\n            return false;\n        }\n        logger.info(\"remove machineRoom ok: {}\", roomId);\n        return true;\n    }\n\n    /**\n     * 删除机器，并删除相关的定时任务\n     *\n     * @param machineInfo\n     * @return\n     */\n    @Override\n    public boolean removeMachine(MachineInfo machineInfo) {\n        if (machineInfo == null || Strings.isNullOrEmpty(machineInfo.getIp())) {\n            logger.warn(\"machineInfo is null or ip is empty.\");\n            return false;\n        }\n        String machineIp = machineInfo.getIp();\n\n        // 从db中删除machine和相关统计信息\n        try {\n            machineDao.removeMachineInfoByIp(machineIp);\n            machineStatsDao.deleteMachineStatsByIp(machineIp);\n            serverStatusDao.deleteServerInfo(machineIp);\n        } catch (Exception e) {\n            logger.error(\"remove machineInfo from db error, machineInfo: {}\", machineInfo.toString(), e);\n            return false;\n        }\n        logger.info(\"remove and undeploy machine ok: {}\", machineInfo.toString());\n        return true;\n    }\n\n    @Override\n    public void updateMachineRelation(int id, Long taskid, int is_sync) {\n        try {\n            machineRelationDao.updateMachineRelation(id, taskid, is_sync);\n        } catch (Exception e) {\n            logger.error(\"update machineRelation id:{} taskid :{} error, machineRelation: {}\", id, taskid, e.getMessage());\n        }\n    }\n\n    public List<MachineRelation> getMachineRelationList(String ip) {\n        List<MachineRelation> relationList = new ArrayList<MachineRelation>();\n        try {\n            relationList = machineRelationDao.getRelationList(ip);\n        } catch (Exception e) {\n            logger.error(\"get machine relation : containerIp:{} , error message:{}\", ip, e.getMessage(), e);\n        }\n        return relationList;\n    }\n\n    public SuccessEnum checkMachineSyncStatus(String containerIp, String sourceIp, int is_sync) {\n        List<MachineRelation> machineRelationList = null;\n        try {\n            machineRelationList = machineRelationDao.getMachineSyncStatus(containerIp, sourceIp, is_sync);\n        } catch (Exception e) {\n            logger.error(\"check machine relation error : containerIp: {} sourceIp:{} is_sync:{} ,error message: {}\", containerIp, sourceIp, is_sync, e.getMessage(), e);\n            return SuccessEnum.ERROR;\n        }\n        if (machineRelationList != null && machineRelationList.size() > 0) {\n            logger.info(\"check machine relation containerIp: {} sourceIp:{} is_sync:{} size :{} \", containerIp, sourceIp, is_sync, machineRelationList.size());\n            return SuccessEnum.REPEAT;\n        } else {\n            return SuccessEnum.NO_REPEAT;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/protocol/MachineProtocol.java",
    "content": "package com.sohu.cache.protocol;\r\n\r\nimport com.sohu.cache.redis.enums.DirEnum;\r\nimport com.sohu.cache.util.ConstUtils;\r\n\r\n/**\r\n * 机器相关的一些常量\r\n *\r\n * @author: lingguo\r\n * @time: 2014/8/26 16:18\r\n */\r\npublic class MachineProtocol {\r\n\r\n    /**\r\n     * 统一的目录结构\r\n     */\r\n    public static final String CONF_DIR = ConstUtils.CACHECLOUD_BASE_DIR + \"/cachecloud/conf/\";\r\n    public static final String DATA_DIR = ConstUtils.CACHECLOUD_BASE_DIR + \"/cachecloud/data/\";\r\n    public static final String LOG_DIR = ConstUtils.CACHECLOUD_BASE_DIR + \"/cachecloud/logs/\";\r\n\r\n\r\n    /**\r\n     * k8s容器目录结构\r\n     */\r\n    public static final String K8S_CONF_DIR = ConstUtils.CACHECLOUD_BASE_DIR + \"/cachecloud/conf/%s/\";\r\n    public static final String K8S_DATA_DIR = ConstUtils.CACHECLOUD_BASE_DIR + \"/cachecloud/data/%s/\";\r\n    public static final String K8S_LOG_DIR = ConstUtils.CACHECLOUD_BASE_DIR + \"/cachecloud/logs/%s/\";\r\n\r\n    /**\r\n     * 配置文件的临时目录；\r\n     */\r\n    public static final String TMP_DIR = \"/tmp/cachecloud/\";\r\n\r\n    /**\r\n     * 编码\r\n     */\r\n    public static final String ENCODING_UTF8 = \"UTF-8\";\r\n\r\n    /**\r\n\t * 配置目录\r\n\t * @param instanceBasePath\r\n\t * @return\r\n\t */\r\n\tpublic static String getConfPath(String instanceBasePath) {\r\n\t\treturn instanceBasePath + \"/conf\";\r\n\t}\r\n\r\n    /**\r\n     * 支持k8s挂载宿主机文件\r\n     *\r\n     * @param host\r\n     * @return\r\n     */\r\n    public static String getK8sConfDir(String host) {\r\n        return String.format(K8S_CONF_DIR, host);\r\n    }\r\n\r\n    public static String getK8sDataDir(String host) {\r\n        return String.format(K8S_DATA_DIR, host);\r\n    }\r\n\r\n    public static String getK8sLogDir(String host) {\r\n        return String.format(K8S_LOG_DIR, host);\r\n    }\r\n\r\n    /**\r\n     * 获取k8s相关目录\r\n     *\r\n     * @param host\r\n     * @param dirType\r\n     * @return\r\n     */\r\n    public static String getK8sDir(String host, int dirType) {\r\n        if (dirType == DirEnum.CONF_DIR.getValue()) {\r\n            return String.format(K8S_CONF_DIR, host);\r\n        } else if (dirType == DirEnum.DATA_DIR.getValue()) {\r\n            return String.format(K8S_DATA_DIR, host);\r\n        } else if (dirType == DirEnum.LOG_DIR.getValue()) {\r\n            return String.format(K8S_LOG_DIR, host);\r\n        }\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * 获取普通容器目录\r\n     *\r\n     * @param dirType\r\n     * @return\r\n     */\r\n    public static String getDir(int dirType) {\r\n        if (dirType == DirEnum.CONF_DIR.getValue()) {\r\n            return CONF_DIR;\r\n        } else if (dirType == DirEnum.DATA_DIR.getValue()) {\r\n            return DATA_DIR;\r\n        } else if (dirType == DirEnum.LOG_DIR.getValue()) {\r\n            return LOG_DIR;\r\n        }\r\n        return null;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/protocol/RedisProtocol.java",
    "content": "package com.sohu.cache.protocol;\r\n\r\nimport com.sohu.cache.web.util.DateUtil;\r\nimport org.apache.commons.lang.StringUtils;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * Created by yijunzhang on 14-11-26.\r\n */\r\npublic class RedisProtocol {\r\n\r\n    private static final String RUN_SHELL_VERSION = \"%s/src/redis-server %s > \" + MachineProtocol.LOG_DIR + \"redis-%d-%s.log 2>&1 &\";\r\n\r\n    private static final String SENTINEL_SHELL_VERSION = \"%s/src/redis-sentinel %s --sentinel > \" + MachineProtocol.LOG_DIR + \"redis-sentinel-%d-%s.log 2>&1 &\";\r\n\r\n    private static final String K8S_RUN_SHELL_VERSION = \"%s/src/redis-server %s > %sredis-%d-%s.log 2>&1 &\";\r\n\r\n    private static final String K8S_SENTINEL_SHELL_VERSION = \"%s/src/redis-sentinel %s --sentinel > %sredis-sentinel-%d-%s.log 2>&1 &\";\r\n\r\n    private static final String CLUSTER_CONFIG = \"redis-cluster-%d.conf\";\r\n\r\n    private static final String COMMON_CONFIG = \"redis-sentinel-%d.conf\";\r\n\r\n    /**\r\n     * 2018-08-28 根据不同版本路径启动redis-cluster\r\n     *\r\n     * @param port\r\n     * @param isCluster\r\n     * @param dir       redis启动路径\r\n     * @return 启动redis命令\r\n     */\r\n    public static String getRunShellByVersion(int port, boolean isCluster, String dir) {\r\n        return String.format(RUN_SHELL_VERSION, dir, MachineProtocol.CONF_DIR + getConfig(port, isCluster), port, DateUtil.formatYYYYMMddHHMM(new Date()));\r\n    }\r\n\r\n    /**\r\n     * 2018-08-28 根据不同版本路径启动redis-sentinel\r\n     *\r\n     * @param port\r\n     * @param dir  redis启动路径\r\n     * @return 启动redis命令\r\n     */\r\n    public static String getSentinelShellByVersion(int port, String dir) {\r\n        return String.format(SENTINEL_SHELL_VERSION, dir, MachineProtocol.CONF_DIR + getConfig(port, false), port, DateUtil.formatYYYYMMddHHMM(new Date()));\r\n    }\r\n\r\n    /**\r\n     * 2019-05-14 k8s容器启动路径  /opt/cachecloud/conf/${host}/redis-${port}.conf\r\n     *\r\n     * @param host\r\n     * @param port\r\n     * @param isCluster\r\n     * @param dir\r\n     * @return 启动redis命令\r\n     */\r\n    public static String getK8sRunShellByVersion(String host, int port, boolean isCluster, String dir) {\r\n        return String.format(K8S_RUN_SHELL_VERSION, dir, MachineProtocol.getK8sConfDir(host) + getConfig(port, isCluster), MachineProtocol.getK8sLogDir(host), port, DateUtil.formatYYYYMMddHHMM(new Date()));\r\n    }\r\n\r\n    /**\r\n     * 2019-05-14  k8s容器启动路径  /opt/cachecloud/conf/${host}/redis-${port}.conf\r\n     *\r\n     * @param host\r\n     * @param port\r\n     * @param dir\r\n     * @return 启动redis sentinel命令\r\n     */\r\n    public static String getK8sSentinelShellByVersion(String host, int port, String dir) {\r\n        return String.format(K8S_SENTINEL_SHELL_VERSION, dir, MachineProtocol.getK8sConfDir(host) + getConfig(port, false), MachineProtocol.getK8sLogDir(host), port, DateUtil.formatYYYYMMddHHMM(new Date()));\r\n    }\r\n\r\n    public static String getExecuteCommandShell(String host, int port, String password, String command) {\r\n        StringBuffer shell = new StringBuffer();\r\n        shell.append(String.format(\"redis-cli -h %s -p %s\", host, port));\r\n        if (StringUtils.isNotBlank(password)) {\r\n            shell.append(String.format(\" -a %s\", password));\r\n        }\r\n        shell.append(String.format(\" --raw %s\", command));\r\n        return shell.toString();\r\n    }\r\n\r\n\r\n    public static String getExecuteAdminCommandShell(String host, int port, String password, String command) {\r\n        StringBuffer shell = new StringBuffer();\r\n        shell.append(String.format(\"redis-cli -h %s -p %s\", host, port));\r\n        if (StringUtils.isNotBlank(password)) {\r\n            shell.append(String.format(\" -a %s\", password));\r\n        }\r\n        shell.append(String.format(\" %s\", command));\r\n        return shell.toString();\r\n    }\r\n\r\n    public static String getConfig(int port, boolean isCluster) {\r\n        if (isCluster) {\r\n            return String.format(CLUSTER_CONFIG, port);\r\n        } else {\r\n            return String.format(COMMON_CONFIG, port);\r\n        }\r\n    }\r\n\r\n    public static String getRedisPortPidFilePath() {\r\n        return \"logs/redis-port.pid\";\r\n    }\r\n\r\n    private static final String NUT_CRACKER_SHELL = \"bin/nutcracker -c %s/%s -p %s/%s -o %s/%s -s %d  -v %d\";\r\n    public static String getNutCrackerConfName() {\r\n    \treturn \"nutcracker.conf\";\r\n    }\r\n    public static String getNutCrackerPidName() {\r\n    \treturn \"nutcracker.pid\";\r\n    }\r\n    public static String getNutCrackerLogName() {\r\n    \treturn \"nutcracker.log\";\r\n    }\r\n    public static String getNutCrackerShell(String confFilePath, String pidPath, String logPath, int statPort, int logLevel) {\r\n\t\treturn String.format(NUT_CRACKER_SHELL, confFilePath, getNutCrackerConfName(), pidPath,\r\n\t\t\t\tgetNutCrackerPidName(), logPath, getNutCrackerLogName(), statPort, logLevel);\r\n    }\r\n    public static String getNutCrackerStartCmd(String confFilePath, String pidPath, String logPath, int statPort, int logLevel) {\r\n    \treturn String.format(\"bin/mon -a 60 -d \\\"%s\\\"\", getNutCrackerShell(confFilePath, pidPath, logPath, statPort, logLevel));\r\n    }\r\n    public static String getNutCrackerRunCmd(String confFilePath, String pidPath, String logPath, int statPort, int logLevel) {\r\n    \treturn getNutCrackerShell(confFilePath, pidPath, logPath, statPort, logLevel);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/AssistRedisService.java",
    "content": "package com.sohu.cache.redis;\n\nimport redis.clients.jedis.Tuple;\nimport redis.clients.jedis.params.SetParams;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * CacheCloud 内部使用的辅助redis\n */\npublic interface AssistRedisService {\n\n    boolean reloadSentinel();\n\n    <T> boolean set(String key, T value);\n\n    <T> boolean set(String key, T value, int timeout);\n\n    boolean setNx(String key, String value) ;\n\n    String set(String key, String value, SetParams params) ;\n\n    <T> boolean setWithNoSerialize(String key, T value);\n\n    String getWithNoSerialize(String key);\n\n    <T> boolean setWithNoSerialize(String key, T value, int seconds);\n\n    boolean remove(String key);\n\n    <T> T get(String key);\n\n    boolean rpush(String key, String item);\n\n    boolean rpush(String key, String... item);\n\n    boolean rpushList(String key, List<String> items);\n\n    boolean saddSet(String key, Set<String> items);\n\n    boolean sadd(String key, String item);\n\n    Set<String> smembers(String key);\n\n    boolean srem(String key, String item);\n\n    List<String> lrange(String key, int start, int end);\n\n    Long llen(final String key);\n\n    String lpop(final String key);\n\n    Long lrem(final String key, long count, String element);\n\n    String ltrim(final String key, long start, long end);\n\n    boolean zadd(String key, long score, String member);\n\n    String hget(String key, String field);\n\n    boolean hset(String key, String field, String value);\n\n    Long hsetnx(String key, String field, String value);\n\n    boolean hmset(String key, Map<String, String> map);\n\n    Map<String, String> hgetAll(String key);\n\n    Long hdel(String key, String field);\n\n    boolean del(String key);\n\n    boolean delMulti(String... keys);\n\n    void zincrby(String key, double score, String member);\n\n    Set<Tuple> zrangeWithScores(String key, long start, long end);\n\n    boolean exists(String key);\n\n    boolean setNEX(String key, String value, int seconds);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/RedisCenter.java",
    "content": "package com.sohu.cache.redis;\n\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.vo.RedisSlowLog;\nimport org.apache.commons.lang3.tuple.Pair;\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisSentinelPool;\nimport redis.clients.jedis.commands.ProtocolCommand;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Future;\n\n/**\n * redis相关操作接口\n * Created by yijunzhang on 14-6-10.\n */\npublic interface RedisCenter {\n\n    /**\n     * 收集redis统计信息\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @return\n     */\n    public Map<Object, Map<String, Object>> collectRedisInfo(long appId, long collectTime, String host,\n                                                             int port);\n\n    /**\n     * 收集redis统计信息\n     *\n     * @param host\n     * @param port\n     * @return\n     */\n    public Map<Object, Map<String, Object>> getInfoStats(long appId, String host, int port);\n\n    /**\n     * 节点cluster info信息\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @return\n     */\n    public Map<String, Object> getClusterInfoStats(long appId, String host, int port);\n\n    /**\n     * 节点cluster info信息\n     *\n     * @param appId\n     * @param instanceInfo\n     * @return\n     */\n    public Map<String, Object> getClusterInfoStats(long appId, InstanceInfo instanceInfo);\n\n    /**\n     * 根据ip和port判断redis实例当前是主还是从\n     *\n     * @param ip   ip\n     * @param port port\n     * @return 主返回true，从返回false,返回null代表未知；\n     */\n    public BooleanEnum isMaster(long appId, String ip, int port);\n\n    /**\n     * 判断实例是否为从节点，并且与主节点连接有效\n     *\n     * @param appDesc\n     * @param slaveInstance\n     * @param masterInstance\n     * @return\n     */\n    public BooleanEnum isSlaveAndPointedMasterUp(AppDesc appDesc, InstanceInfo slaveInstance, InstanceInfo masterInstance);\n\n\n    /**\n     * 获取行数\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @return\n     */\n    long getDbSize(long appId, String ip, int port);\n\n\n    Future<List<String>> findInstancePatternKeys(long appId, String ip, int port, String pattern);\n\n    List<String> findInstanceBigKey(long appId, String ip, int port, long startBytes, long endBytes);\n\n    List<String> findClusterBigKey(long appId, long startBytes, long endBytes);\n\n    List<String> findInstanceIdleKeys(long appId, String ip, int port, long idleDays);\n\n    List<String> findClusterIdleKeys(long appId, long idleDays);\n\n    void delInstancePatternKeys(long appId, String ip, int port, String pattern);\n\n    void delClusterPatternKey(long appId, String pattern);\n\n    /**\n     * 根据ip和port判断redis实例当前是否有从节点\n     *\n     * @param ip   ip\n     * @param port port\n     * @return 主返回true，从返回false；\n     */\n    public BooleanEnum hasSlaves(long appId, String ip, int port);\n\n    /**\n     * 获取从节点的主节点地址\n     *\n     * @param ip\n     * @param port\n     * @param password\n     * @return\n     */\n    public HostAndPort getMaster(String ip, int port, String password);\n\n    public HostAndPort getSlave0(String ip, int port, String password);\n\n    /**\n     * 判断实例是否运行\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @return\n     */\n    public boolean isRun(final long appId, String ip, int port);\n\n    /**\n     * 判断实例是否运行\n     *\n     * @param ip\n     * @param port\n     * @param retryTimes 重试次数\n     * @return\n     */\n    public boolean isRun(final String ip, final int port, final int retryTimes);\n\n    /**\n     * 判断实例是否运行\n     *\n     * @param ip\n     * @param port\n     * @return\n     */\n    public boolean isRun(String ip, int port);\n\n    /**\n     * 判断实例是否运行\n     *\n     * @param ip\n     * @param port\n     * @param password\n     * @return\n     */\n    public boolean isRun(String ip, int port, String password);\n\n    /**\n     * 下线指定实例\n     *\n     * @param ip\n     * @param port\n     * @return\n     */\n    public boolean shutdown(String ip, int port);\n\n    /**\n     * 下线指定实例\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @return\n     */\n    public boolean shutdown(long appId, String ip, int port);\n\n    /**\n     * 校验下线指定实例是否成功\n     *\n     * @param instanceInfo\n     * @return\n     */\n    public boolean checkShutdownSuccess(InstanceInfo instanceInfo);\n\n    /**\n     * forget指定实例\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @param nodeClusterId\n     * @return\n     */\n    public boolean forget(long appId, String ip, int port, String nodeClusterId);\n\n    /**\n     * 获取cluster myid\n     *\n     * @param ip\n     * @param port\n     * @return\n     */\n    public String getClusterMyId(long appId, String ip, int port);\n\n    /**\n     * 获取cluster nodes\n     *\n     * @param ip\n     * @param port\n     * @return\n     */\n    public String getClusterNodes(long appId, String ip, int port);\n\n    /**\n     * 执行redis命令返回结果\n     *\n     * @param appDesc\n     * @param command\n     * @param userName\n     * @return\n     */\n    public String executeCommand(AppDesc appDesc, String command, String userName);\n\n    /**\n     * 实例执行redis命令\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param command\n     * @return\n     */\n    public String executeCommand(long appId, String host, int port, String command);\n\n    /**\n     * 执行redis命令，无白名单限制，仅供管理员使用\n     *\n     * @param appDesc\n     * @param command\n     * @param args\n     * @return\n     */\n    Object executeAdminCommand(AppDesc appDesc, ProtocolCommand command, String... args);\n\n\n    /**\n     * 实例执行redis命令，无白名单限制，仅供管理员使用\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param command\n     * @param timeout\n     * @return\n     */\n    String executeAdminCommand(long appId, String host, int port, String command, Integer timeout);\n\n    /**\n     * 执行redis命令， 无黑白名单限制，仅供管理员使用\n     *\n     * @param jedis\n     * @param command\n     * @param args\n     * @return\n     */\n    Object executeAdminRedisCommandByJedis(Jedis jedis, ProtocolCommand command, String... args);\n\n    /**\n     * 获取jedisSentinelPool实例,必须是sentinel类型应用\n     *\n     * @param appDesc\n     * @return\n     */\n    public JedisSentinelPool getJedisSentinelPool(AppDesc appDesc);\n\n    /**\n     * 获取redis实例配置信息\n     *\n     * @param instanceId\n     * @return\n     */\n    public Map<String, String> getRedisConfigList(int instanceId);\n\n    /**\n     * 获取redis 命令列表\n     * @param instanceId\n     * @return\n     */\n    public List<String> getRedisCommand(int instanceId);\n\n    /**\n     * 获取重命名命令列表\n     * @param instanceId\n     * @return\n     */\n    List<Pair<String, String>> getConfigsInConfigFile(int instanceId, String configName);\n\n    /**\n     * 获取redis实例慢查询\n     *\n     * @param instanceId\n     * @return\n     */\n    public List<RedisSlowLog> getRedisSlowLogs(int instanceId, int maxCount);\n\n    /**\n     * 获取client连接信息\n     *\n     * @param instanceId\n     * @return\n     */\n    List<String> getClientList(int instanceId);\n\n    List<Map<String, Object>> formatClientList(List<String> clientList);\n\n    List<Map<String, Object>> getAppClientList(long appId, int condition);\n\n    /**\n     * 配置重写\n     *\n     * @return\n     */\n    public boolean configRewrite(final long appId, final String host, final int port);\n\n    /**\n     * 配置重写\n     *\n     * @param jedis\n     * @return\n     */\n    public boolean configRewrite(Jedis jedis);\n\n    /**\n     * 获取maxmemory配置\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @return\n     */\n    public Long getRedisMaxMemory(long appId, String host, int port);\n\n    /**\n     * 清理app数据\n     *\n     * @param appDesc\n     * @param appUser\n     * @return\n     */\n    public boolean cleanAppData(AppDesc appDesc, AppUser appUser);\n\n    /**\n     * 判断是否为孤立节点\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @return\n     */\n    public boolean isSingleClusterNode(long appId, String host, int port);\n\n    /**\n     * 获取集群中失联的slots\n     *\n     * @param appId\n     * @return\n     */\n    public Map<String, String> getClusterLossSlots(long appId);\n\n    /**\n     * 获取集群中失联的slots\n     * @param appId\n     * @param instanceInfo\n     * @return\n     */\n    public Map<String, String> getClusterLossSlots(long appId, InstanceInfo instanceInfo);\n\n\n        /**\n         * 获取集群中失联的slots\n         *\n         * @param appId\n         * @param host\n         * @param port\n         * @return\n         */\n    public List<Integer> getClusterLossSlots(long appId, String host, int port);\n\n    /**\n     * 获取集群中失联的slots\n     *\n     * @param appId\n     * @param healthyHost\n     * @param healthyPort\n     * @param lossSlotsHost\n     * @param lossSlotsPort\n     * @return\n     */\n    public List<Integer> getInstanceSlots(long appId, String healthyHost, int healthyPort, String lossSlotsHost,\n                                          int lossSlotsPort);\n\n    /**\n     * 从一个应用中获取一个健康的实例\n     *\n     * @param appId\n     * @return\n     */\n    public InstanceInfo getHealthyInstanceInfo(long appId);\n\n    /**\n     * 从一个应用中获取所有健康的master实例\n     *\n     * @param appId\n     * @return\n     */\n    public List<InstanceInfo> getAllHealthyInstanceInfo(long appId);\n\n    /**\n     * 收集redis延迟信息\n     *\n     * @param appId\n     * @param collectTime\n     * @param host\n     * @param port\n     * @return\n     */\n    List<InstanceLatencyHistory> collectRedisLatencyInfo(long appId, long collectTime, String host,\n                                                         int port);\n\n    /**\n     * 收集redis慢查询日志\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @return\n     */\n    public List<InstanceSlowLog> collectRedisSlowLog(long appId, long collectTime, String host,\n                                                     int port);\n\n    /**\n     * 按照appid获取慢查询日志\n     *\n     * @param appId\n     * @return\n     */\n    public List<InstanceSlowLog> getInstanceSlowLogByAppId(long appId);\n\n    /**\n     * 按照appid获取慢查询日志\n     *\n     * @param appId\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    public List<InstanceSlowLog> getInstanceSlowLogByAppId(long appId, Date startDate, Date endDate);\n\n    /**\n     * 按照appid获取慢查询日志数关系\n     *\n     * @param appId\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    public Map<String, Long> getInstanceSlowLogCountMapByAppId(Long appId, Date startDate, Date endDate);\n\n    /**\n     * 获取集群的slots分布\n     *\n     * @param appId\n     * @return\n     */\n    Map<String, InstanceSlotModel> getClusterSlotsMap(long appId);\n\n\n    /**\n     * 获取集群slot分布\n     * <K,V> -> <slot start-slot end, List<IP:PORT>>\n     *\n     * @param appId\n     * @param instanceInfo\n     * @return\n     */\n    Map<String, List<HostAndPort>> getClusterSlotMap(long appId, InstanceInfo instanceInfo);\n\n    /**\n     * 获取Redis版本\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @return\n     */\n    public String getRedisVersion(long appId, String ip, int port);\n\n    /**\n     * <p>\n     * Description: 获取redis failover之后数据状态，判断是否failover完成\n     * </p>\n     *\n     * @param ip   当前failover slave ip\n     * @param port 当前failover slave port\n     * @return false:定时轮询检测 true:检测完毕\n     * @version 1.0\n     * @date 2018/9/17\n     */\n    public Boolean getRedisReplicationStatus(long appId, String ip, int port);\n\n    /**\n     * @param appId\n     * @param ip\n     * @param port\n     * @return\n     */\n    public Map<String, String> getRedisRoleAndMasterStatus(long appId, String ip, int port);\n\n    /**\n     * <p>\n     * Description: 获取redis failover之后状态，判断是否failover完成\n     * </p>\n     *\n     * @param ip   当前failover slave ip\n     * @param port 当前failover slave port\n     * @return false:定时轮询检测 true:检测完毕\n     * @version 1.0\n     * @date 2023/2/28\n     */\n    public Boolean getRedisFailoverForceStatus(long appId, String ip, int port);\n\n    /**\n     * 获取nodeId\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @return\n     */\n    public String getNodeId(long appId, String ip, int port);\n\n    Jedis getJedis(String host, int port, String password);\n\n    Jedis getAdminJedis(String host, int port, String password);\n\n    Jedis getJedis(String host, int port);\n\n    Jedis getJedis(long appId, String host, int port);\n\n    Jedis getAdminJedis(long appId, String host, int port);\n\n    Jedis getJedis(long appId, String host, int port, int connectionTimeout, int soTimeout);\n\n    Jedis getAdminJedis(long appId, String host, int port, int connectionTimeout, int soTimeout);\n\n    Jedis getJedis(String host, int port, String password, int connectionTimeout, int soTimeout);\n\n    public boolean sendDeployRedisRelateCollectionMsg(long appId, String host, int port);\n\n    /**\n     * 检查配置nutcracker配置是否一致\n     *\n     * @param appId\n     * @return\n     */\n    public boolean checkNutCrackerConfIsSame(long appId);\n\n    /**\n     * 检查nutcracker哈希是否一致\n     *\n     * @param appId\n     * @param isDelete 检查后删除数据\n     * @return\n     */\n    public List<InstanceInfo> checkNutCrackerHashIsSame(long appId, boolean isDelete);\n\n    String configGet(long appId, String host, int port, String key);\n\n    boolean configSetAndRewrite(long appId, String host, int port, String key, String value);\n\n    //检测从节点是否准备OK\n    boolean checkSlaveReady(long appId, String ip, int port, long offset);\n\n    //检测从节点是否准备OK\n    boolean checkSlaveReady(Jedis jedis, Long offset);\n\n    //获取节点角色描述\n    String getInstanceRole(Jedis jedis);\n\n    //获取节点角色描述\n    String getInstanceRole(long appId, String ip, int port);\n\n    //检测bgsave是否完成\n    boolean checkBgsaveFinish(Jedis jedis, int checkTimes);\n\n    //检测load rdb是否完成\n    boolean checkLoadFinish(Jedis jedis, int checkTimes);\n\n    //获取rdb文件名\n    String getRdbFileName(Jedis jedis);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/RedisClusterNode.java",
    "content": "package com.sohu.cache.redis;\r\n\r\n/**\r\n * Created by yijunzhang on 14-8-25.\r\n */\r\npublic class RedisClusterNode {\r\n\r\n    /**\r\n     * 主节点地址\r\n     */\r\n    private String masterHost;\r\n\r\n    /**\r\n     * 从节点地址\r\n     */\r\n    private String slaveHost;\r\n\r\n    public String getMasterHost() {\r\n        return masterHost;\r\n    }\r\n\r\n    public void setMasterHost(String masterHost) {\r\n        this.masterHost = masterHost;\r\n    }\r\n\r\n    public String getSlaveHost() {\r\n        return slaveHost;\r\n    }\r\n\r\n    public void setSlaveHost(String slaveHost) {\r\n        this.slaveHost = slaveHost;\r\n    }\r\n\r\n    public RedisClusterNode(String masterHost, String slaveHost) {\r\n        this.masterHost = masterHost;\r\n        this.slaveHost = slaveHost;\r\n    }\r\n\r\n    public RedisClusterNode() {\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"RedisClusterNode{\" +\r\n                \"masterHost='\" + masterHost + '\\'' +\r\n                \", slaveHost='\" + slaveHost + '\\'' +\r\n                '}';\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/RedisClusterReshard.java",
    "content": "package com.sohu.cache.redis;\n\nimport com.sohu.cache.constant.ReshardStatusEnum;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.InstanceReshardProcessDao;\nimport com.sohu.cache.dao.ResourceDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.InstanceReshardProcess;\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.util.IdempotentConfirmer;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Protocol;\nimport redis.clients.jedis.exceptions.JedisException;\nimport redis.clients.jedis.params.MigrateParams;\nimport redis.clients.jedis.util.SafeEncoder;\n\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * 水平扩容重构\n *\n * @author leifu\n * @Date 2016年12月7日\n * @Time 上午10:13:00\n */\npublic class RedisClusterReshard {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    private final Map<String, String> nodeIdCachedMap = new HashMap<String, String>();\n    /**\n     * migrate超时时间\n     */\n    private int migrateTimeout = 10000;\n    /**\n     * 普通jedis操作超时时间\n     */\n    private int defaultTimeout = Protocol.DEFAULT_TIMEOUT * 5;\n    /**\n     * 每次迁移key个数\n     */\n    private int migrateBatch = 10;\n    /**\n     * 所有有效节点\n     */\n    private Set<HostAndPort> hosts;\n    /**\n     * redis操作封装\n     */\n    private RedisCenter redisCenter;\n    private InstanceReshardProcessDao instanceReshardProcessDao;\n    private AppDao appDao;\n    private ResourceDao resourceDao;\n\n    public RedisClusterReshard(Set<HostAndPort> hosts, RedisCenter redisCenter, InstanceReshardProcessDao instanceReshardProcessDao) {\n        this.hosts = hosts;\n        this.redisCenter = redisCenter;\n        this.instanceReshardProcessDao = instanceReshardProcessDao;\n    }\n\n    /**\n     * <p>\n     * Description: 需要获取应用密码\n     * </p>\n     *\n     * @param\n     * @return\n     * @author chenshi\n     * @version 1.0\n     * @date 2017/12/25\n     */\n    public RedisClusterReshard(Set<HostAndPort> hosts, RedisCenter redisCenter, InstanceReshardProcessDao instanceReshardProcessDao, AppDao appDao, ResourceDao resourceDao) {\n        this.hosts = hosts;\n        this.redisCenter = redisCenter;\n        this.instanceReshardProcessDao = instanceReshardProcessDao;\n        this.appDao = appDao;\n        this.resourceDao = resourceDao;\n    }\n\n    /**\n     * 加入主从分片\n     */\n    public boolean joinCluster(long appId, String masterHost, int masterPort, final String slaveHost, final int slavePort) {\n        Jedis mJedis = null;\n        Jedis sJedis = null;\n        try{\n            //1. 确认主从节点是否正常\n            mJedis = redisCenter.getAdminJedis(appId, masterHost, masterPort, defaultTimeout, defaultTimeout);\n            final Jedis masterJedis = mJedis;\n            boolean isRun = redisCenter.isRun(appId, masterHost, masterPort);\n            if (!isRun) {\n                logger.error(String.format(\"joinCluster: master host=%s,port=%s is not run\", masterHost, masterPort));\n                return false;\n            }\n            boolean hasSlave = StringUtils.isNotBlank(slaveHost) && slavePort > 0;\n            sJedis = hasSlave ? redisCenter.getAdminJedis(appId, slaveHost, slavePort, defaultTimeout, defaultTimeout) : null;\n            final Jedis slaveJedis = sJedis;\n            if (hasSlave) {\n                isRun = redisCenter.isRun(appId, slaveHost, slavePort);\n                if (!isRun) {\n                    logger.error(String.format(\"joinCluster: slave host=%s,port=%s is not run\", slaveHost, slavePort));\n                    return false;\n                }\n            }\n\n            //2. 对主从节点进行meet操作\n            //获取所有主节点\n            List<HostAndPort> masterHostAndPostList = getMasterNodeList(appId);\n            //meet master\n            boolean isClusterMeet = clusterMeet(appId, masterHostAndPostList, masterHost, masterPort);\n            if (!isClusterMeet) {\n                logger.error(\"master isClusterMeet failed {}:{}\", masterHost, masterPort);\n                return false;\n            }\n            if (hasSlave) {\n                isClusterMeet = clusterMeet(appId, masterHostAndPostList, slaveHost, slavePort);\n                if (!isClusterMeet) {\n                    logger.error(\"slave isClusterMeet failed {}:{}\", slaveHost, slavePort);\n                    return false;\n                }\n            }\n\n            //3.复制\n            if (hasSlave) {\n                final String masterNodeId = getNodeId(appId, masterJedis);\n                if (masterNodeId == null) {\n                    logger.error(String.format(\"joinCluster:host=%s,port=%s nodeId is null\", masterHost, masterPort));\n                    return false;\n                }\n                return new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        try {\n                            //等待广播节点\n                            TimeUnit.SECONDS.sleep(2);\n                        } catch (Exception e) {\n                            logger.error(e.getMessage(), e);\n                        }\n                        String response = slaveJedis.clusterReplicate(masterNodeId);\n                        logger.info(\"clusterReplicate-{}:{}={}\", slaveHost, slavePort, response);\n                        return response != null && response.equalsIgnoreCase(\"OK\");\n                    }\n                }.run();\n            } else {\n                return true;\n            }\n        }finally {\n            if(mJedis != null){\n                mJedis.close();\n            }\n            if(sJedis != null){\n                sJedis.close();\n            }\n        }\n    }\n\n    /**\n     * 将source中的startSlot到endSlot迁移到target\n     *\n     */\n//    public boolean migrateSlotOld(long appId, long appAuditId, InstanceInfo sourceInstanceInfo, InstanceInfo targetInstanceInfo, int startSlot, int endSlot, PipelineEnum pipelineEnum) {\n//        long startTime = System.currentTimeMillis();\n//        InstanceReshardProcess instanceReshardProcess = saveInstanceReshardProcess(appId, appAuditId, sourceInstanceInfo, targetInstanceInfo, startSlot, endSlot, pipelineEnum);\n//        //源和目标Jedis\n//        Jedis sourceJedis = redisCenter.getJedis(appId, sourceInstanceInfo.getIp(), sourceInstanceInfo.getPort(), defaultTimeout, defaultTimeout);\n//        Jedis targetJedis = redisCenter.getJedis(appId, targetInstanceInfo.getIp(), targetInstanceInfo.getPort(), defaultTimeout, defaultTimeout);\n//        //逐个slot迁移\n//        boolean hasError = false;\n//        for (int slot = startSlot; slot <= endSlot; slot++) {\n//            long slotStartTime = System.currentTimeMillis();\n//            try {\n//                instanceReshardProcessDao.updateMigratingSlot(instanceReshardProcess.getId(), slot);\n//                //num是迁移key的总数\n//                int num = migrateSlotData(appId, sourceJedis, targetJedis, slot, pipelineEnum);\n//                instanceReshardProcessDao.increaseFinishSlotNum(instanceReshardProcess.getId());\n//                logger.warn(\"clusterReshard:{}->{}, slot={}, keys={}, costTime={} ms\", sourceInstanceInfo.getHostPort(),\n//                        targetInstanceInfo.getHostPort(), slot, num, (System.currentTimeMillis() - slotStartTime));\n//            } catch (Exception e) {\n//                logger.error(e.getMessage(), e);\n//                hasError = true;\n//                break;\n//            }\n//        }\n//        long endTime = System.currentTimeMillis();\n//        logger.warn(\"clusterReshard:{}->{}, slot:{}->{}, costTime={} ms\", sourceInstanceInfo.getHostPort(),\n//                targetInstanceInfo.getHostPort(), startSlot, endSlot, (endTime - startTime));\n//        if (hasError) {\n//            instanceReshardProcessDao.updateStatus(instanceReshardProcess.getId(), ReshardStatusEnum.ERROR.getValue());\n//            return false;\n//        } else {\n//            instanceReshardProcessDao.updateStatus(instanceReshardProcess.getId(), ReshardStatusEnum.FINISH.getValue());\n//            instanceReshardProcessDao.updateEndTime(instanceReshardProcess.getId(), new Date());\n//            return true;\n//        }\n//    }\n\n    /**\n     * 节点meet\n     *\n     * @param masterHostAndPostList\n     * @param host\n     * @param port\n     * @return\n     */\n    private boolean clusterMeet(long appId, List<HostAndPort> masterHostAndPostList, final String host, final int port) {\n        boolean isSingleNode = redisCenter.isSingleClusterNode(appId, host, port);\n        if (!isSingleNode) {\n            logger.error(\"{}:{} isNotSingleNode\", host, port);\n            return false;\n        } else {\n            logger.warn(\"{}:{} isSingleNode\", host, port);\n        }\n        for (HostAndPort hostAndPort : masterHostAndPostList) {\n            String clusterHost = hostAndPort.getHost();\n            int clusterPort = hostAndPort.getPort();\n            final Jedis jedis = redisCenter.getAdminJedis(appId, clusterHost, clusterPort, defaultTimeout, defaultTimeout);\n            //logger.info(\"jedis {}:{} meet singlenode\",clusterHost,clusterPort);\n            try {\n                boolean isClusterMeet = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        //将新节点添加到集群当中,成为集群中已知新节点\n                        String meet = jedis.clusterMeet(host, port);\n                        return meet != null && meet.equalsIgnoreCase(\"OK\");\n                    }\n                }.run();\n                if (isClusterMeet) {\n                    return true;\n                }\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            } finally {\n                if (jedis != null)\n                    jedis.close();\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 将source中的startSlot到endSlot迁移到target\n     */\n    public boolean migrateSlot(InstanceReshardProcess instanceReshardProcess) {\n        long appId = instanceReshardProcess.getAppId();\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String password = null;\n        List<Integer> versionList = new ArrayList<>();\n        boolean isRedisType = TypeUtil.isRedisType(appDesc.getType());\n        if(appDesc != null && isRedisType){\n            password = appDesc.getPasswordMd5();\n            // Redis版本信息\n            SystemResource resource = resourceDao.getResourceById(appDesc.getVersionId());\n            String name = resource.getName();\n            if(name != null){\n                String[] split = name.split(\"-\");\n                if(split != null && split.length == 2){\n                    String[] versionArray = split[1].split(\"\\\\.\");\n                    versionList = Arrays.asList(versionArray).stream().map(ver -> Integer.valueOf(ver)).collect(Collectors.toList());\n                }\n            }\n        }\n        int migratingSlot = instanceReshardProcess.getMigratingSlot();\n        int endSlot = instanceReshardProcess.getEndSlot();\n        int isPipeline = instanceReshardProcess.getIsPipeline();\n        InstanceInfo sourceInstanceInfo = instanceReshardProcess.getSourceInstanceInfo();\n        InstanceInfo targetInstanceInfo = instanceReshardProcess.getTargetInstanceInfo();\n\n        long startTime = System.currentTimeMillis();\n\n        //源和目标Jedis\n        try(Jedis sourceJedis = redisCenter.getAdminJedis(appId, sourceInstanceInfo.getIp(), sourceInstanceInfo.getPort(), defaultTimeout, defaultTimeout);\n            Jedis targetJedis = redisCenter.getAdminJedis(appId, targetInstanceInfo.getIp(), targetInstanceInfo.getPort(), defaultTimeout, defaultTimeout);){\n            //逐个slot迁移\n            boolean hasError = false;\n            for (int slot = migratingSlot; slot <= endSlot; slot++) {\n                long slotStartTime = System.currentTimeMillis();\n                try {\n                    instanceReshardProcessDao.updateMigratingSlot(instanceReshardProcess.getId(), slot);\n                    //num是迁移key的总数\n                    int num = 0;\n                    if(isRedisType){\n                        num = migrateSlotData(appId, sourceJedis, targetJedis, slot, isPipeline, password, versionList);\n                    }\n                    instanceReshardProcessDao.increaseFinishSlotNum(instanceReshardProcess.getId());\n                    logger.warn(\"clusterReshard:{}->{}, slot={}, keys={}, costTime={} ms\", sourceInstanceInfo.getHostPort(),\n                            targetInstanceInfo.getHostPort(), slot, num, (System.currentTimeMillis() - slotStartTime));\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    hasError = true;\n                    break;\n                }\n            }\n            long endTime = System.currentTimeMillis();\n            logger.warn(\"clusterReshard:{}->{}, slot:{}->{}, costTime={} ms\", sourceInstanceInfo.getHostPort(),\n                    targetInstanceInfo.getHostPort(), migratingSlot, endSlot, (endTime - startTime));\n            if (hasError) {\n                instanceReshardProcessDao.updateStatus(instanceReshardProcess.getId(), ReshardStatusEnum.ERROR.getValue());\n                return false;\n            } else {\n                instanceReshardProcessDao.updateStatus(instanceReshardProcess.getId(), ReshardStatusEnum.FINISH.getValue());\n                instanceReshardProcessDao.updateEndTime(instanceReshardProcess.getId(), new Date());\n                return true;\n            }\n        }\n    }\n\n    /**\n     * 迁移slot数据，并稳定slot配置\n     *\n     * @throws Exception\n     */\n    private int moveSlotData(final long appId, final Jedis source, final Jedis target, final int slot, int isPipeline, String password, List<Integer> versionList) throws Exception {\n        int num = 0;\n        while (true) {\n            final Set<String> keys = new HashSet<String>();\n            boolean isGetKeysInSlot = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    List<String> perKeys = source.clusterGetKeysInSlot(slot, migrateBatch);\n                    if (perKeys != null && perKeys.size() > 0) {\n                        keys.addAll(perKeys);\n                    }\n                    return true;\n                }\n            }.run();\n            if (!isGetKeysInSlot) {\n                throw new RuntimeException(String.format(\"get keys failed slot=%d num=%d\", slot, num));\n            }\n            if (keys.isEmpty()) {\n                break;\n            }\n            for (final String key : keys) {\n                boolean isKeyMigrate = new IdempotentConfirmer() {\n                    // 失败后，迁移时限加倍\n                    private int migrateTimeOutFactor = 1;\n\n                    @Override\n                    public boolean execute() {\n                        String response = null;\n                        Integer bigVer = 0;\n                        Integer smallVer = 0;\n                        Integer fixVer = 0;\n                        if(CollectionUtils.isNotEmpty(versionList)){\n                            bigVer = versionList.get(0);\n                            smallVer = versionList.get(1);\n                            fixVer = versionList.get(2);\n                        }\n                        if(bigVer < 4 || (bigVer == 4 && smallVer == 0 && fixVer < 7)){\n                            response = source.migrate(target.getClient().getHost(), target.getClient().getPort(),\n                                    key, 0, migrateTimeout * (migrateTimeOutFactor++));\n                        }else{\n                            response = source.migrate(target.getClient().getHost(), target.getClient().getPort(),\n                                    0, migrateTimeout * (migrateTimeOutFactor++), MigrateParams.migrateParams().auth(password), key);\n                        }\n                        return response != null && (response.equalsIgnoreCase(\"OK\") || response.equalsIgnoreCase(\"NOKEY\"));\n                    }\n                }.run();\n                if (!isKeyMigrate) {\n                    throw new RuntimeException(\"migrate key=\" + key + failedInfo(source, slot));\n                } else {\n                    num++;\n//                    logger.info(\"migrate key={};response=OK\", key);\n                }\n            }\n        }\n        final String targetNodeId = getNodeId(appId, target);\n        boolean isClusterSetSlotNode;\n        //设置 slot新归属节点\n        isClusterSetSlotNode = new IdempotentConfirmer() {\n            @Override\n            public boolean execute() {\n                boolean isOk = false;\n                List<HostAndPort> masterNodesList = getMasterNodeList(appId);\n                // 增加cluster SETSLOT slot NODE node-id sequence\n                // target, source, and optional other master nodes\n                HostAndPort targetHostPort = new HostAndPort(target.getClient().getHost(), target.getClient().getPort());\n                HostAndPort sourceHostPort = new HostAndPort(source.getClient().getHost(), source.getClient().getPort());\n                masterNodesList = masterNodesList.stream().\n                        filter(hostAndPort -> !hostAndPort.equals(targetHostPort) && !hostAndPort.equals(sourceHostPort))\n                        .collect(Collectors.toList());\n                masterNodesList.add(0, targetHostPort);\n                masterNodesList.add(1, sourceHostPort);\n                for (HostAndPort hostAndPort : masterNodesList) {\n                    Jedis jedis = null;\n                    try {\n                        jedis = redisCenter.getAdminJedis(appId, hostAndPort.getHost(), hostAndPort.getPort());\n                        String response = jedis.clusterSetSlotNode(slot, targetNodeId);\n                        isOk = response != null && response.equalsIgnoreCase(\"OK\");\n                        if (!isOk) {\n                            logger.error(\"clusterSetSlotNode-{}={}\", getNodeId(appId, target), response);\n                            break;\n                        }\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                    } finally {\n                        if (jedis != null)\n                            jedis.close();\n                    }\n                }\n                return isOk;\n            }\n        }.run();\n        if (!isClusterSetSlotNode) {\n            throw new RuntimeException(\"clusterSetSlotNode:\" + failedInfo(target, slot));\n        }\n        return num;\n    }\n\n    /**\n     * 指派迁移节点数据\n     * CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。\n     * CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中。\n     * CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键。\n     * MIGRATE host port key destination-db timeout [COPY] [REPLACE]\n     * CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点，如果槽已经指派给另一个节点，那么先让另一个节点删除该槽>，然后再进行指派。\n     */\n    private int migrateSlotData(long appId, final Jedis source, final Jedis target, final int slot, int isPipeline, String password, List<Integer> versionList) {\n        int num = 0;\n        final String sourceNodeId = getNodeId(appId, source);\n        final String targetNodeId = getNodeId(appId, target);\n        boolean isError = false;\n        if (sourceNodeId == null || targetNodeId == null) {\n            throw new JedisException(String.format(\"sourceNodeId = %s || targetNodeId = %s\", sourceNodeId, targetNodeId));\n        }\n        boolean isImport = new IdempotentConfirmer() {\n            @Override\n            public boolean execute() {\n                String importing = target.clusterSetSlotImporting(slot, sourceNodeId);\n                logger.info(\"slot={},clusterSetSlotImporting={}\", slot, importing);\n                return importing != null && importing.equalsIgnoreCase(\"OK\");\n            }\n        }.run();\n        if (!isImport) {\n            isError = true;\n            logger.error(\"clusterSetSlotImporting\" + failedInfo(target, slot));\n        }\n        boolean isMigrate = new IdempotentConfirmer() {\n            @Override\n            public boolean execute() {\n                String migrating = source.clusterSetSlotMigrating(slot, targetNodeId);\n                logger.info(\"slot={},clusterSetSlotMigrating={}\", slot, migrating);\n                return migrating != null && migrating.equalsIgnoreCase(\"OK\");\n            }\n        }.run();\n\n        if (!isMigrate) {\n            isError = true;\n            logger.error(\"clusterSetSlotMigrating\" + failedInfo(source, slot));\n        }\n\n        try {\n            num = moveSlotData(appId, source, target, slot, isPipeline, password, versionList);\n        } catch (Exception e) {\n            isError = true;\n            logger.error(e.getMessage(), e);\n        }\n        if (!isError) {\n            return num;\n        } else {\n            String errorMessage = \"source=%s target=%s slot=%d num=%d reShard failed\";\n            throw new RuntimeException(String.format(errorMessage, getNodeKey(source), getNodeKey(target), slot, num));\n        }\n    }\n\n    private String failedInfo(Jedis jedis, int slot) {\n        return String.format(\" failed %s:%d slot=%d\", jedis.getClient().getHost(), jedis.getClient().getPort(), slot);\n    }\n\n    /**\n     * 获取所有主节点\n     *\n     * @return\n     */\n    private List<HostAndPort> getMasterNodeList(long appId) {\n        List<HostAndPort> masterNodeList = new ArrayList<HostAndPort>();\n        /**\n         * 获取RedisCluster所有节点：新加节点从jedisClusterInfoCache获取master node不全, 如果新加节点且没有分配slot,jedisClusterInfoCache node节点不会缓存该节点\n         * 解决:直接遍历 host 节点,判断是否master节点\n         */\n        // 获取master节点列表\n        try {\n            for (HostAndPort host : hosts) {\n                String ip = host.getHost();\n                int port = host.getPort();\n                if (redisCenter.isMaster(appId, ip, port) != BooleanEnum.TRUE) {\n                    continue;\n                }\n                // 添加当前所有master节点\n                masterNodeList.add(new HostAndPort(ip, port));\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            throw new RuntimeException(\"migrate get host:\" + hosts + \" masterNodelist error!\");\n        }\n\n        return masterNodeList;\n    }\n\n    public String getNodeId(final long appId, final Jedis jedis) {\n        String nodeKey = getNodeKey(jedis);\n        if (nodeIdCachedMap.get(nodeKey) != null) {\n            return nodeIdCachedMap.get(nodeKey);\n        } else {\n            String nodeId = redisCenter.getNodeId(appId, jedis.getClient().getHost(), jedis.getClient().getPort());\n            nodeIdCachedMap.put(nodeKey, nodeId);\n            return nodeId;\n        }\n    }\n\n    protected String getNodeKey(Jedis jedis) {\n        return jedis.getClient().getHost() + \":\" + jedis.getClient().getPort();\n    }\n\n    public void setMigrateTimeout(int migrateTimeout) {\n        this.migrateTimeout = migrateTimeout;\n    }\n\n    public void setDefaultTimeout(int defaultTimeout) {\n        this.defaultTimeout = defaultTimeout;\n    }\n\n    public void setInstanceReshardProcessDao(InstanceReshardProcessDao instanceReshardProcessDao) {\n        this.instanceReshardProcessDao = instanceReshardProcessDao;\n    }\n\n    public void setRedisCenter(RedisCenter redisCenter) {\n        this.redisCenter = redisCenter;\n    }\n\n    public void setAppDao(AppDao appDao) {\n        this.appDao = appDao;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/RedisConfigTemplateService.java",
    "content": "package com.sohu.cache.redis;\r\n\r\nimport com.sohu.cache.entity.InstanceConfig;\r\nimport com.sohu.cache.entity.SystemResource;\r\nimport org.apache.commons.lang3.tuple.Pair;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * redis配置模板服务\r\n * @author leifu\r\n * @Date 2016年6月23日\r\n * @Time 下午2:08:03\r\n */\r\npublic interface RedisConfigTemplateService {\r\n\r\n    /**\r\n     * 获取所有配置模板列表\r\n     * @return\r\n     */\r\n    List<InstanceConfig> getAllInstanceConfig();\r\n\r\n    /**\r\n     * 根据type获取配置模板列表\r\n     * \r\n     * @param type\r\n     * @return\r\n     */\r\n    List<InstanceConfig> getByType(int type);\r\n\r\n    /**\r\n     * 根据type,versionId获取配置模板列表\r\n     *\r\n     * @param type\r\n     * @return\r\n     */\r\n    List<InstanceConfig> getByVesionAndType(int type,int versionId);\r\n\r\n    /**\r\n     * 根据versionId获取模板所有配置\r\n     *\r\n     * @param versionId\r\n     * @return 版本对应所有有效配置项\r\n     */\r\n    List<InstanceConfig> getByVesion(int versionId);\r\n\r\n    /**\r\n     * 保存或者更新配置模板\r\n     * \r\n     * @param instanceConfig\r\n     * @return\r\n     */\r\n    int saveOrUpdate(InstanceConfig instanceConfig);\r\n\r\n    /**\r\n     * 根据id获取配置模板\r\n     * \r\n     * @param id\r\n     * @return\r\n     */\r\n    InstanceConfig getById(long id);\r\n\r\n    /**\r\n     * 根据configKey和type获取配置\r\n     * \r\n     * @param configKey\r\n     * @param type\r\n     * @return\r\n     */\r\n    InstanceConfig getByConfigKeyAndType(String configKey, int type);\r\n\r\n    /**\r\n     * 更改配置状态\r\n     * @param id\r\n     * @param status\r\n     * @return\r\n     */\r\n    int updateStatus(long id, int status);\r\n    \r\n    /**\r\n     * 删除配置\r\n     * @param id\r\n     */\r\n    int remove(long id);\r\n    \r\n    /**\r\n     * 普通节点配置\r\n     * @param port\r\n     * @param maxMemory\r\n     * @param maxMemoryPolicy\r\n     * @param versionId Redis版本\r\n     * @return 普通配置列表\r\n     */\r\n    List<String> handleCommonConfig(String host, int port, int maxMemory, String maxMemoryPolicy, int versionId);\r\n\r\n    /**\r\n     * sentinel节点配置(兼容k8s)\r\n     * @param masterName\r\n     * @param host  master节点ip\r\n     * @param port  master节点port\r\n     * @param sentinelHost sentinel host\r\n     * @param sentinelPort sentinel port\r\n     * @param versionId\r\n     * @param customConfigs 自定义配置\r\n     * @return\r\n     */\r\n    List<String> handleSentinelConfig(String masterName, String host, int port,String sentinelHost, int sentinelPort, int versionId, List<Pair<String, String>> customConfigs);\r\n\r\n    /**\r\n     * cluster节点配置\r\n     * @param port\r\n     * @param versionId Redis版本\r\n     * @return\r\n     */\r\n    List<String> handleClusterConfig(int port, int versionId);\r\n    \r\n    \r\n//    /**\r\n//     * 普通节点默认配置\r\n//     * @param port\r\n//     * @param maxMemory\r\n//     * @return\r\n//     */\r\n//    List<String> handleCommonDefaultConfig(int port, int maxMemory);\r\n\r\n    /**\r\n     * sentinel节点默认配置\r\n     * @param masterName\r\n     * @param host\r\n     * @param port\r\n     * @param sentinelPort\r\n     * @return\r\n     */\r\n    List<String> handleSentinelDefaultConfig(String masterName, String host, int port, int sentinelPort);\r\n    \r\n//    /**\r\n//     * cluster节点默认配置\r\n//     * @param port\r\n//     * @return\r\n//     */\r\n//    List<String> handleClusterDefaultConfig(int port);\r\n\r\n\r\n    /**\r\n     * 通过redis名称查询是否重复\r\n     */\r\n    public SystemResource getRedisVersionByName(String versionName);\r\n\r\n    /**\r\n     * 202007\r\n     */\r\n    public String copyRedisConfig(int versionCopyId,SystemResource resource);\r\n\r\n    /**\r\n     * 更新机器安装redis版本情况\r\n     * @return SuccessEnum.SUCCESS SuccessEnum.FAIL\r\n     */\r\n    public String updateMachineInstallRedis(String host);\r\n\r\n    public Boolean checkAndInstallRedisResource(String host,SystemResource redisResource);\r\n\r\n    public Boolean checkAndInstallRedisTool(String host,SystemResource redisResource);\r\n\r\n    /**\r\n     * slave更新为新版本配置\r\n     */\r\n    public Map<String,Object> slaveUpdateConfig(long appId,Integer upgradeVersionId,String upgradeVersionName);\r\n\r\n    /**\r\n     * master-slave failover\r\n     */\r\n    public Map<String,Object> slaveFailover(long appid);\r\n\r\n    /**\r\n     * 检查slave psync是否成功\r\n     */\r\n    public Boolean slaveIsPsync(long appId,String ip,int port);\r\n\r\n    List<String> handleRedisConfig(String host, int port, int versionId, int maxMemory, String maxMemoryPolicy, boolean isCluster, List<Pair<String, String>> customConfigs, Map<String, String> copyFromConfigMap);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/RedisDeployCenter.java",
    "content": "package com.sohu.cache.redis;\n\nimport com.sohu.cache.constant.ClusterOperateResult;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.web.enums.RedisOperateEnum;\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.Jedis;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * redis 部署配置\n * Created by yijunzhang on 14-7-1.\n */\npublic interface RedisDeployCenter {\n\n    /**\n     * 部署cluster 集群\n     *\n     * @param appId        应用id\n     * @param clusterNodes redis实例集合\n     * @param maxMemory    实例最大内存,单位MB\n     * @return 实例是否部署成功\n     */\n    public boolean deployClusterInstance(long appId, List<RedisClusterNode> clusterNodes, int maxMemory);\n\n    public boolean startCluster(final long appId, Map<Jedis, Jedis> clusterMap);\n\n    public boolean startClusterMaster(final long appId, Map<Jedis, Jedis> clusterMap);\n\n    public boolean startClusterSlave(final long appId, Map<Jedis, Jedis> clusterMap);\n\n    /**\n     * 部署redis sentinel实例 实例组\n     *\n     * @param appId        应用id\n     * @param masterHost   主节点地址\n     * @param slaveHost    从节点地址\n     * @param maxMemory    实例最大内存,单位MB\n     * @param sentinelList sentinel-host列表\n     * @param\n     * @return 实例是否部署成功\n     */\n    public boolean deploySentinelInstance(long appId, String masterHost, String slaveHost, int maxMemory,\n                                          List<String> sentinelList);\n\n    /**\n     * 部署Standalone redis实例\n     *\n     * @param appId     应用id\n     * @param host      节点地址\n     * @param maxMemory 实例最大内存,单位MB\n     * @return 实例是否部署成功\n     */\n    public boolean deployStandaloneInstance(long appId, String host, int maxMemory);\n\n    /**\n     * 修改app下所有实例的配置\n     *\n     * @param appId\n     * @param parameter\n     * @param value\n     * @return\n     */\n    public boolean modifyAppConfig(long appId, String parameter, String value);\n\n    /**\n     * 修改实例配置\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parameter\n     * @param value\n     * @return\n     */\n    public boolean modifyInstanceConfig(long appId, String host, int port, String parameter, String value);\n\n    /**\n     * 获取实例配置\n     * @param appId\n     * @param host\n     * @param port\n     * @param parameter\n     * @return\n     */\n    public String getInstanceConfig(final long appId, final String host, final int port, final String parameter);\n\n\n    /**\n     * 为应用appId添加sentinel服务器\n     *\n     * @param appId\n     * @param sentinelHost\n     * @return\n     */\n    public boolean addSentinel(long appId, String sentinelHost) throws Exception;\n\n    /**\n     * 为主节点添加从节点\n     *\n     * @param appId\n     * @param masterInstanceId\n     * @param slaveHost\n     * @return\n     */\n    public boolean addSlave(long appId, int masterInstanceId, String slaveHost) throws Exception;\n\n    /**\n     * 为主节点添加从节点\n     *\n     * @param appDesc\n     * @param masterHost\n     * @param masterPort\n     * @param mem 单位Mb\n     * @param slaveInstance 配置有slaveHost，添加成功，将设置slavePort\n     * @return\n     */\n    public boolean addSlave(AppDesc appDesc, String masterHost, int masterPort, int mem, InstanceInfo slaveInstance) throws Exception;\n\n    String genSlaveIp(long appId, int instanceId) throws Exception;\n\n    /**\n     * 填充集群中失败的slots，添加一个master节点\n     *\n     * @param appId\n     * @param instanceId\n     * @param masterHost\n     * @return\n     * @throws Exception\n     */\n    public RedisOperateEnum addSlotsFailMaster(long appId, int instanceId, String masterHost) throws Exception;\n\n    /**\n     * 创建一个redis实例\n     *\n     * @param appDesc\n     * @param host\n     * @param port\n     * @param maxMemory\n     * @return\n     */\n    public boolean createRunNode(AppDesc appDesc, String host, Integer port, int maxMemory, boolean isCluster);\n\n\n    /**\n     * 获取Redis执行的runshell\n     *\n     * @param host\n     * @param port\n     * @param redisDir\n     * @return\n     */\n    public String getRedisRunShell(boolean isCluster, String host, int port, String redisDir);\n\n    /**\n     * 获取Sentinel执行的runshell\n     *\n     * @param host\n     * @param port\n     * @param redisDir\n     * @return\n     */\n    public String getSentinelRunShell(String host, int port, String redisDir);\n\n    /**\n     * <p>\n     * Description: 生成新配置\n     * </p>\n     *\n     * @param\n     * @return\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/9/12\n     */\n    public boolean bornConfigAndRunNode(AppDesc appDesc, InstanceInfo instanceInfo, String host, Integer port, int maxMemory, boolean isCluster);\n\n    /**\n     * sentinel类型应用执行Failover,主从切换\n     *\n     * @param appId\n     * @return\n     */\n    public boolean sentinelFailover(long appId) throws Exception;\n\n    /**\n     * sentinel类型应用执行Reset,重置状态\n     *\n     * @param appId\n     * @return\n     */\n    public boolean sentinelReset(long appId) throws Exception;\n\n    /**\n     * cluster类型应用执行Failover,主从切换,只能在从节点执行\n     *\n     * @param appId\n     * @param slaveInstanceId\n     * @param failoverParam\n     * @return\n     */\n    public boolean clusterFailover(long appId, int slaveInstanceId, String failoverParam) throws Exception;\n\n    /**\n     * 根据 host port进行\n     * @param appId\n     * @param failoverParam\n     * @return\n     * @throws Exception\n     */\n    public boolean clusterFailover(long appId, HostAndPort hostAndPort, String failoverParam) throws Exception;\n\n    /**\n     * 检查是否具备forget的条件\n     *\n     * @param appId\n     * @param forgetInstanceId\n     * @return\n     */\n    public ClusterOperateResult checkClusterForget(Long appId, int forgetInstanceId);\n\n    /**\n     * 删除节点\n     *\n     * @param appId\n     * @param delNodeInstanceId\n     * @return\n     */\n    public ClusterOperateResult delNode(Long appId, int delNodeInstanceId);\n\n    /**\n     * 应用级别配置密码并将密码更新到应用信息中。\n     * 步骤:\n     * 1:根据应用类型设置密码\n     * 2:确保配置持久化\n     * 3:更新密码pkey到应用信息中\n     * 4:检查所有节点密码是否一致。\n     *\n     * @param appId\n     * @param pkey  如果为空，表示清空密码，默认传递appId\n     * @return\n     */\n    public boolean fixPassword(Long appId, String pkey);\n\n    /**\n     * 应用级别配置密码并将密码更新到应用信息中。\n     * 步骤:\n     * 1:根据应用类型设置密码\n     * 2:确保配置持久化\n     * 3:更新密码pkey到应用信息中\n     * 4:检查所有节点密码是否一致。\n     *\n     * @param appId\n     * @param password  如果为空，表示默认逻辑，默认传递appId，也可传自定义密码\n     * @param customPwdFlag  是否为自定义密码\n     * @param initRedisFlag  是否为初始redis服务\n     * @return\n     */\n    public boolean fixPassword(Long appId, String password, Boolean customPwdFlag, boolean initRedisFlag);\n\n\n        /**\n         * 检查应用密码是否有效并且一致\n         *\n         * @param appId\n         * @return\n         */\n    public boolean checkAuths(Long appId);\n\n    /**\n     * slaveof 用管理员\n     *\n     * @param appId\n     * @param masterHost\n     * @param masterPort\n     * @param slaveHost\n     * @param slavePort\n     * @return\n     */\n    public boolean slaveOf(final long appId, final String masterHost, final int masterPort, final String slaveHost,\n                           final int slavePort);\n\n    /**\n     * 用默认用户slaveof\n     * @param appId\n     * @param masterHost\n     * @param masterPort\n     * @param slaveHost\n     * @param slavePort\n     * @return\n     */\n    public boolean slaveOfByDefaultUser(final long appId, final String masterHost, final int masterPort, final String slaveHost,\n                           final int slavePort);\n\n    /**\n     * 添加实例，是否可以指定rdb，及slots\n     * @param appId\n     * @param newInstHost\n     * @param newInstPort\n     * @param pointedRdb 是否已在目录下导入了rdb\n     * @param mem        实例最大内存（与runInstId 必须至少一个不为空）\n     * @param runInstId  运行实例信息\n     * @param slots\n     * @return\n     */\n    public boolean addInstanceToUnhealthyApp(long appId, final String newInstHost, final Integer newInstPort,\n                                             final boolean pointedRdb, Integer mem, final int[] slots, Integer runInstId);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/AppTypeToAlertTypeEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\nimport com.sohu.cache.util.ConstUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * 实例报警类型枚举\n * @author leifu\n * @Date 2017年6月14日\n * @Time 上午10:22:10\n */\npublic enum AppTypeToAlertTypeEnum {\n    REDIS_ALERT(0, ConstUtils.REDIS);\n\n    private final static List<AppTypeToAlertTypeEnum> appTypeToAlertTypeEnumList = new ArrayList<>();\n    static {\n        for (AppTypeToAlertTypeEnum instanceAlertTypeEnum : AppTypeToAlertTypeEnum.values()) {\n            appTypeToAlertTypeEnumList.add(instanceAlertTypeEnum);\n        }\n    }\n\n    /**\n     * 报警应用类型\n     */\n    private int type;\n\n    /**\n     * 报警应用描述\n     */\n    private String info;\n\n    private AppTypeToAlertTypeEnum(int type, String info) {\n        this.type = type;\n        this.info = info;\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public static Optional<AppTypeToAlertTypeEnum> getAppTypeToAlertTypeEnum(String appType) {\n        return appTypeToAlertTypeEnumList.stream().filter(appTypeToAlertTypeEnum -> appTypeToAlertTypeEnum.getInfo().equals(appType)).findFirst();\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/DirEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\n/**\n * Created by chenshi on 2019/5/14.\n */\npublic enum DirEnum {\n\n    CONF_DIR(1, \"配置文件目录\"),\n    DATA_DIR(2, \"数据文件目录\"),\n    LOG_DIR(3, \"日志文件目录\");\n\n    private int value;\n    private String info;\n\n    DirEnum(int value, String info) {\n        this.value = value;\n        this.info = info;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/InstanceAlertCheckCycleEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 实例报警检测周期枚举\n * @author leifu\n * @Date 2017年6月14日\n * @Time 上午10:21:29\n */\npublic enum InstanceAlertCheckCycleEnum {\n    ONE_MINUTE(1, \"1分钟\"),\n    FIVE_MINUTE(2, \"5分钟\"),\n    HALF_HOUR(3, \"30分钟\"),\n    ONE_HOUR(4, \"1小时\"),\n    ONE_DAY(5, \"1天\"),\n    ;\n    \n    private final static List<InstanceAlertCheckCycleEnum> instanceAlertCheckCycleEnumList = new ArrayList<InstanceAlertCheckCycleEnum>();\n    static {\n        for (InstanceAlertCheckCycleEnum instanceAlertCheckCycleEnum : InstanceAlertCheckCycleEnum.values()) {\n            instanceAlertCheckCycleEnumList.add(instanceAlertCheckCycleEnum);\n        }\n    }\n\n    private int value;\n    \n    private String info;\n\n    private InstanceAlertCheckCycleEnum(int value, String info) {\n        this.value = value;\n        this.info = info;\n    }\n\n    public static List<InstanceAlertCheckCycleEnum> getInstanceAlertCheckCycleEnumList() {\n        return instanceAlertCheckCycleEnumList;\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/InstanceAlertCompareTypeEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 实例报警比较枚举\n * @author leifu\n * @Date 2017年6月14日\n * @Time 上午10:20:47\n */\npublic enum InstanceAlertCompareTypeEnum {\n    LESS_THAN(1, \"小于\"),\n    EQUAL(2, \"等于\"),\n    MORE_THAN(3, \"大于\"),\n    NOT_EQUAL(4, \"不等于\");\n    \n    private final static List<InstanceAlertCompareTypeEnum> instanceAlertCompareTypeEnumList = new ArrayList<InstanceAlertCompareTypeEnum>();\n    static {\n        for (InstanceAlertCompareTypeEnum instanceAlertCompareTypeEnum : InstanceAlertCompareTypeEnum.values()) {\n            instanceAlertCompareTypeEnumList.add(instanceAlertCompareTypeEnum);\n        }\n    }\n    \n    private final static Map<Integer, InstanceAlertCompareTypeEnum> instanceAlertCompareTypeEnumMap = new HashMap<Integer, InstanceAlertCompareTypeEnum>();\n    static {\n        for (InstanceAlertCompareTypeEnum instanceAlertCompareTypeEnum : InstanceAlertCompareTypeEnum.values()) {\n            instanceAlertCompareTypeEnumMap.put(instanceAlertCompareTypeEnum.getValue(), instanceAlertCompareTypeEnum);\n        }\n    }\n    \n    private int value;\n    \n    private String info;\n\n    private InstanceAlertCompareTypeEnum(int value, String info) {\n        this.value = value;\n        this.info = info;\n    }\n\n    public static List<InstanceAlertCompareTypeEnum> getInstanceAlertCompareTypeEnumList() {\n        return instanceAlertCompareTypeEnumList;\n    }\n    \n    public static InstanceAlertCompareTypeEnum getInstanceAlertCompareTypeEnum(int value) {\n        return instanceAlertCompareTypeEnumMap.get(value);\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/InstanceAlertStatusEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\n/**\n * 实例报警有效性枚举\n * @author leifu\n * @Date 2017年6月14日\n * @Time 上午10:22:39\n */\npublic enum InstanceAlertStatusEnum {\n    YES(1, \"有效\"),\n    NO(0, \"无效\");\n\n    private int value;\n    \n    private String info;\n\n    private InstanceAlertStatusEnum(int value, String info) {\n        this.value = value;\n        this.info = info;\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/InstanceAlertTypeEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 实例报警类型枚举\n * @author leifu\n * @Date 2017年6月14日\n * @Time 上午10:22:10\n */\npublic enum InstanceAlertTypeEnum {\n    ALL_ALERT(1, \"全局报警\"),\n    INSTANCE_ALERT(2, \"实例报警\"),\n    APP_ALERT(3, \"应用报警\");\n\n    \n    private final static List<InstanceAlertTypeEnum> instanceAlertTypeEnumList = new ArrayList<InstanceAlertTypeEnum>();\n    static {\n        for (InstanceAlertTypeEnum instanceAlertTypeEnum : InstanceAlertTypeEnum.values()) {\n            instanceAlertTypeEnumList.add(instanceAlertTypeEnum);\n        }\n    }\n    \n    private int value;\n    \n    private String info;\n\n    private InstanceAlertTypeEnum(int value, String info) {\n        this.value = value;\n        this.info = info;\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/RedisAlertConfigEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Redis报警配置枚举\n * @author leifu\n * @Date 2017年6月13日\n * @Time 下午5:34:42\n */\npublic enum RedisAlertConfigEnum {\n    aof_current_size(\"aof_current_size\", \"aof当前尺寸(单位：MB)\"),\n    minute_aof_delayed_fsync(\"aof_delayed_fsync\", \"分钟aof阻塞个数\"),\n    client_biggest_input_buf(\"client_biggest_input_buf\", \"输入缓冲区最大buffer大小(单位：MB)\"),\n    client_longest_output_list(\"client_longest_output_list\", \"输出缓冲区最大队列长度\"),\n    instantaneous_ops_per_sec(\"instantaneous_ops_per_sec\", \"实时ops\"),\n    latest_fork_usec(\"latest_fork_usec\", \"上次fork所用时间(单位：微秒)\"),\n    mem_fragmentation_ratio(\"mem_fragmentation_ratio\", \"内存碎片率(检测大于500MB)\"),\n    rdb_last_bgsave_status(\"rdb_last_bgsave_status\", \"上一次bgsave状态\"),\n    minute_rejected_connections(\"rejected_connections\", \"分钟拒绝连接数\"),\n    minute_sync_partial_err(\"sync_partial_err\", \"分钟部分复制失败次数\"),\n    minute_sync_partial_ok(\"sync_partial_ok\", \"分钟部分复制成功次数\"),\n    minute_sync_full(\"sync_full\", \"分钟全量复制执行次数\"),\n    minute_total_net_input_bytes(\"total_net_input_bytes\", \"分钟网络输入流量(单位：MB)\"),\n    minute_total_net_output_bytes(\"total_net_output_bytes\", \"分钟网络输出流量(单位：MB)\"),\n    master_slave_offset_diff(\"master_slave_offset_diff\", \"主从节点偏移量差(单位：字节)\"),\n    cluster_state(\"cluster_state\", \"集群状态\"),\n    cluster_slots_ok(\"cluster_slots_ok\", \"集群成功分配槽个数\"),\n    used_cpu_sys(\"used_cpu_sys\",\"系统cpu消耗(单位：秒)\"),\n    used_cpu_user(\"used_cpu_user\",\"用户cpu消耗(单位：秒)\"),\n    used_cpu_sys_children(\"used_cpu_sys_children\",\"系统子进程cpu消耗(单位：秒)\"),\n    used_cpu_user_children(\"used_cpu_user_children\",\"用户子进程cpu消耗(单位：秒)\"),\n    other_default_common_config(\"other_default_common_config\",\"除上述之外的其他默认通用配置\")\n    ;\n    private final static List<RedisAlertConfigEnum> redisAlertConfigEnumList = new ArrayList<RedisAlertConfigEnum>();\n    static {\n        for (RedisAlertConfigEnum redisAlertConfigEnum : RedisAlertConfigEnum.values()) {\n            redisAlertConfigEnumList.add(redisAlertConfigEnum);\n        }\n    }\n    \n    private final static Map<String, RedisAlertConfigEnum> redisAlertConfigEnumMap = new HashMap<String, RedisAlertConfigEnum>();\n    static {\n        for (RedisAlertConfigEnum redisAlertConfigEnum : RedisAlertConfigEnum.values()) {\n            redisAlertConfigEnumMap.put(redisAlertConfigEnum.getValue(), redisAlertConfigEnum);\n        }\n    }\n    \n    private String value;\n    \n    private String info;\n    \n\n    public static List<RedisAlertConfigEnum> getRedisAlertConfigEnumList() {\n        return redisAlertConfigEnumList;\n    }\n\n    public static Map<String, RedisAlertConfigEnum> getRedisAlertConfigEnumMap() {\n        return redisAlertConfigEnumMap;\n    }\n    \n    public static RedisAlertConfigEnum getRedisAlertConfig(String alertConfig) {\n        return redisAlertConfigEnumMap.get(alertConfig);\n    }\n\n    private RedisAlertConfigEnum(String value, String info) {\n        this.value = value;\n        this.info = info;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n    \n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/RedisClusterConfigEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\n/**\n * Created by yijunzhang on 14-8-25.\n */\npublic enum RedisClusterConfigEnum {\n    CLUSTER_ENABLED(\"cluster-enabled\", \"yes\", \"是否开启集群模式\"),\n    CLUSTER_NODE_TIMEOUT(\"cluster-node-timeout\", \"15000\", \"集群节点超时时间,默认15秒\"),\n    CLUSTER_SLAVE_VALIDITY_FACTOR(\"cluster-slave-validity-factor\", \"10\", \"集群从节点,延迟有效性判断因子,默认10秒:(node-timeout * slave-validity-factor) + repl-ping-slave-period\"),\n    CLUSTER_MIGRATION_BARRIER(\"cluster-migration-barrier\", \"1\", \"cluster主从迁移至少需要的从节点数,默认1个\"),\n    CLUSTER_CONFIG_FILE(\"cluster-config-file\", \"nodes-%d.conf\", \"集群配置文件名称,格式:nodes-{port}.conf\"),\n    CLUSTER_REQUIRE_FULL_COVERAGE(\"cluster-require-full-coverage\", \"no\", \"节点部分失败期间,其他节点是否继续工作\");\n\n    private String key;\n\n    private String value;\n\n    private String desc;\n\n    RedisClusterConfigEnum(String key, String value, String desc) {\n        this.key = key;\n        this.value = value;\n        this.desc = desc;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public static RedisClusterConfigEnum get(String key) {\n        if (key == null) {\n            return null;\n        }\n        for (RedisClusterConfigEnum config : RedisClusterConfigEnum.values()) {\n            if (config.key.equals(key)) {\n                return config;\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/RedisClusterInfoEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\n/**\n * cluster info枚举\n * @author leifu\n * @Date 2017年6月21日\n * @Time 下午2:36:47\n */\npublic enum RedisClusterInfoEnum {\n    \n    cluster_state(\"cluster_state\", \"集群状态\", false),\n    cluster_slots_assigned(\"cluster_slots_assigned\", \"分配slot个数\", false),\n    cluster_slots_ok(\"cluster_slots_ok\", \"成功分配slot个数\", false),\n    cluster_slots_pfail(\"cluster_slots_pfail\", \"pfail个数\", false),\n    cluster_slots_fail(\"cluster_slots_fail\", \"fail个数\", false),\n    cluster_stats_messages_sent(\"cluster_stats_messages_sent\", \"发送消息字节数\", false),\n    cluster_stats_messages_received(\"cluster_stats_messages_received\", \"接收消息字节数\", false),\n    ;\n    \n    private String value;\n    \n    private String info;\n    \n    private boolean needCalDif;\n\n    private RedisClusterInfoEnum(String value, String info, boolean needCalDif) {\n        this.value = value;\n        this.info = info;\n        this.needCalDif = needCalDif;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public boolean isNeedCalDif() {\n        return needCalDif;\n    }\n    \n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/RedisConfigEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\nimport com.sohu.cache.protocol.MachineProtocol;\n\n/**\n * Created by yijunzhang on 14-7-27.\n */\npublic enum RedisConfigEnum {\n    DAEMONIZE(\"daemonize\", \"no\", \"是否守护进程\"),\n    TCP_BACKLOG(\"tcp-backlog\", \"511\", \"TCP连接完成队列\"),\n    TIMEOUT(\"timeout\", \"0\", \"客户端闲置多少秒后关闭连接,默认为0,永不关闭\"),\n    TCP_KEEPALIVE(\"tcp-keepalive\", \"0\", \"检测客户端是否健康周期,默认关闭\"),\n    LOGLEVEL(\"loglevel\", \"notice\", \"默认普通的verbose\"),\n    DATABASES(\"databases\", \"16\", \"可用的数据库数，默认值为16个,默认数据库为0\"),\n    DIR(\"dir\", MachineProtocol.DATA_DIR, \"redis工作目录,默认:\" + MachineProtocol.DATA_DIR),\n    STOP_WRITES_ON_BGSAVE_ERROR(\"stop-writes-on-bgsave-error\", \"no\", \"bgsave出错了不停写\"),\n    REPL_TIMEOUT(\"repl-timeout\", \"60\", \"master批量数据传输时间或者ping回复时间间隔,默认:60秒\"),\n    REPL_PING_SLAVE_PERIOD(\"repl-ping-slave-period\", \"10\", \"指定slave定期ping master的周期,默认:10秒\"),\n    REPL_DISABLE_TCP_NODELAY(\"repl-disable-tcp-nodelay\", \"no\", \"是否禁用socket的NO_DELAY,默认关闭，影响主从延迟\"),\n    REPL_BACKLOG_SIZE(\"repl-backlog-size\", \"10M\", \"复制缓存区,默认:1mb,配置为:10Mb\"),\n    REPL_BACKLOG_TTL(\"repl-backlog-ttl\", \"7200\", \"master在没有Slave的情况下释放BACKLOG的时间多久:默认:3600,配置为:7200\"),\n    SLAVE_SERVE_STALE_DATA(\"slave-serve-stale-data\", \"yes\", \"当slave服务器和master服务器失去连接后，或者当数据正在复制传输的时候，如果此参数值设置“yes”，slave服务器可以继续接受客户端的请求\"),\n    SLAVE_READ_ONLY(\"slave-read-only\", \"yes\", \"slave服务器节点是否只读,cluster的slave节点默认读写都不可用,需要调用readonly开启可读模式\"),\n    SLAVE_PRIORITY(\"slave-priority\", \"100\", \"slave的优先级,影响sentinel/cluster晋升master操作,0永远不晋升\"),\n    LUA_TIME_LIMIT(\"lua-time-limit\", \"5000\", \"Lua脚本最长的执行时间，单位为毫秒\"),\n    SLOWLOG_LOG_SLOWER_THAN(\"slowlog-log-slower-than\", \"10000\", \"慢查询被记录的阀值,默认10毫秒\"),\n    SLOWLOG_MAX_LEN(\"slowlog-max-len\", \"128\", \"最多记录慢查询的条数\"),\n    HASH_MAX_ZIPLIST_ENTRIES(\"hash-max-ziplist-entries\", \"512\", \"hash数据结构优化参数\"),\n    HASH_MAX_ZIPLIST_VALUE(\"hash-max-ziplist-value\", \"64\", \"hash数据结构优化参数\"),\n    LIST_MAX_ZIPLIST_ENTRIES(\"list-max-ziplist-entries\", \"512\", \"list数据结构优化参数\"),\n    LIST_MAX_ZIPLIST_VALUE(\"list-max-ziplist-value\", \"64\", \"list数据结构优化参数\"),\n    SET_MAX_INTSET_ENTRIES(\"set-max-intset-entries\", \"512\", \"set数据结构优化参数\"),\n    ZSET_MAX_ZIPLIST_ENTRIES(\"zset-max-ziplist-entries\", \"128\", \"zset数据结构优化参数\"),\n    ZSET_MAX_ZIPLIST_VALUE(\"zset-max-ziplist-value\", \"64\", \"zset数据结构优化参数\"),\n    ACTIVEREHASHING(\"activerehashing\", \"yes\", \"是否激活重置哈希,默认:yes\"),\n    CLIENT_OUTPUT_BUFFER_LIMIT_NORMAL(\"client-output-buffer-limit normal\", \"0 0 0\", \"\"),\n    CLIENT_OUTPUT_BUFFER_LIMIT_SLAVE(\"client-output-buffer-limit slave\", \"512mb 128mb 60\", \"\"),\n    CLIENT_OUTPUT_BUFFER_LIMIT_PUBSUB(\"client-output-buffer-limit pubsub\", \"32mb 8mb 60\", \"\"),\n    HZ(\"hz\", \"10\", \"执行后台task数量,默认:10\"),\n    PORT(\"port\", \"%d\", \"端口\"),\n    MAXMEMORY(\"maxmemory\", \"%dmb\", \"当前实例最大可用内存\"),\n    MAXMEMORY_POLICY(\"maxmemory-policy\", \"volatile-lru\", \"内存不够时,淘汰策略,默认:volatile-lru\"),\n    REQUIREPASS(\"requirepass\", \"\", \"密码\"),\n    MASTERAUTH(\"masterauth\", \"\", \"从节点需要配置的主节点密码\"),\n    AUTH_PASS(\"auth-pass\", \"\", \"sentinel节点需要配置的主节点密码\"),\n    APPENDONLY(\"appendonly\", \"yes\", \"开启append only持久化模式\"),\n    APPENDFSYNC(\"appendfsync\", \"everysec\", \"默认:aof每秒同步一次\"),\n    APPENDFILENAME(\"appendfilename\", \"appendonly-%d.aof\", \"aof文件名称,默认:appendonly-{port}.aof\"),\n    DBFILENAME(\"dbfilename\", \"dump-%d.rdb\", \"RDB文件默认名称,默认dump-{port}.rdb\"),\n    AOF_REWRITE_INCREMENTAL_FSYNC(\"aof-rewrite-incremental-fsync\",\"yes\",\"aof rewrite过程中,是否采取增量文件同步策略,默认:yes\"),\n    NO_APPENDFSYNC_ON_REWRITE(\"no-appendfsync-on-rewrite\", \"yes\", \"是否在后台aof文件rewrite期间调用fsync,默认调用,修改为yes,防止可能fsync阻塞,但可能丢失rewrite期间的数据\"),\n    AUTO_AOF_REWRITE_MIN_SIZE(\"auto-aof-rewrite-min-size\", \"64m\", \"触发rewrite的aof文件最小阀值,默认64m\"),\n    AUTO_AOF_REWRITE_PERCENTAGE(\"auto-aof-rewrite-percentage\", \"%d\", \"Redis重写aof文件的比例条件,默认从100开始,统一机器下不同实例按4%递减\"),\n    BIND(\"bind\", \"0.0.0.0\", \"设置绑定内网ip\");\n\n    private String key;\n\n    private String value;\n\n    private String desc;\n\n    RedisConfigEnum(String key, String value, String desc) {\n        this.key = key;\n        this.value = value;\n        this.desc = desc;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public static RedisConfigEnum get(String key) {\n        if (key == null) {\n            return null;\n        }\n        for (RedisConfigEnum config : RedisConfigEnum.values()) {\n            if (config.key.equals(key)) {\n                return config;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/RedisInfoEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.sohu.cache.constant.RedisConstant;\n\n/**\n * Redis报警配置枚举\n * @author leifu\n * @Date 2017年6月13日\n * @Time 下午5:34:42\n */\npublic enum RedisInfoEnum {\n    \n    /**\n     * Stats\n     */\n    client_biggest_input_buf(RedisConstant.Stats, \"client_biggest_input_buf\", \"输入缓冲区最大buffer大小(单位：字节)\", false),\n    client_longest_output_list(RedisConstant.Stats, \"client_longest_output_list\", \"输出缓冲区最大队列长度\", false),\n    instantaneous_ops_per_sec(RedisConstant.Stats, \"instantaneous_ops_per_sec\", \"实时ops\", false),\n    rejected_connections(RedisConstant.Stats, \"rejected_connections\", \"拒绝客户端连接数\", true),\n    sync_partial_err(RedisConstant.Stats, \"sync_partial_err\", \"部分复制失败次数\", true),\n    sync_partial_ok(RedisConstant.Stats, \"sync_partial_ok\", \"部分复制成功次数\", true),\n    sync_full(RedisConstant.Stats, \"sync_full\", \"全量复制执行次数\", true),\n    total_net_input_bytes(RedisConstant.Stats, \"total_net_input_bytes\", \"网络输入流量(单位：字节)\", true),\n    total_net_output_bytes(RedisConstant.Stats, \"total_net_output_bytes\", \"网络输出流量(单位：字节)\", true),\n    keyspace_hits(RedisConstant.Stats, \"keyspace_hits\", \"键命中数\", true),\n    keyspace_misses(RedisConstant.Stats, \"keyspace_misses\", \"键未命中数\", true),\n    evicted_keys(RedisConstant.Stats, \"evicted_keys\", \"键剔除数\", true),\n    expired_keys(RedisConstant.Stats, \"expired_keys\", \"键过期数\", true),\n    connected_clients(RedisConstant.Clients, \"connected_clients\", \"客户端连接数\", false),\n\n    /**\n     * Persistence\n     */\n    aof_current_size(RedisConstant.Persistence, \"aof_current_size\", \"aof当前尺寸(单位：字节)\", false),\n    aof_base_size(RedisConstant.Persistence, \"aof_base_size\", \"aof基准尺寸(单位：字节)\", false),\n    aof_delayed_fsync(RedisConstant.Persistence, \"aof_delayed_fsync\", \"aof阻塞次数\", true),\n    latest_fork_usec(RedisConstant.Persistence, \"latest_fork_usec\", \"上次fork所用时间(单位：微秒)\", false),\n    rdb_last_bgsave_status(RedisConstant.Persistence, \"rdb_last_bgsave_status\", \"上一次bgsave状态\", false),\n    rdb_bgsave_in_progress(RedisConstant.Persistence, \"rdb_bgsave_in_progress\", \"是否正在进行bgsave\", false),\n    rdb_last_save_time(RedisConstant.Persistence, \"rdb_last_save_time\", \"上一次bgsave时间\", false),\n    rdb_last_bgsave_time_sec(RedisConstant.Persistence, \"rdb_last_bgsave_time_sec\", \"上一次bgsave花费时间\", false),\n    loading(RedisConstant.Persistence, \"loading\", \"指示是否正在进行dump(rdb/aof)文件加载\", false),\n    loading_eta_seconds(RedisConstant.Persistence, \"loading_eta_seconds\", \"dump加载完成预计完成时间（秒）\", false),\n\n    /**\n     * CPU\n     */\n    used_cpu_sys(RedisConstant.CPU, \"used_cpu_sys\", \"redis进程系统态消耗(单位-秒)\", true),\n    used_cpu_user(RedisConstant.CPU, \"used_cpu_user\", \"redis进程用户态消耗(单位-秒)\", true),\n    used_cpu_sys_children(RedisConstant.CPU, \"used_cpu_sys_children\", \"redis子进程系统态消耗(单位-秒)\", true),\n    used_cpu_user_children(RedisConstant.CPU, \"used_cpu_user_children\", \"redis子进程用户态消耗(单位-秒)\", true),\n\n    /**\n     * Memory\n     */\n    used_memory(RedisConstant.Memory, \"used_memory\", \"内存使用(单位：字节)\", false),\n    used_memory_rss(RedisConstant.Memory, \"used_memory_rss\", \"物理内存使用(单位：字节)\", false),\n    mem_fragmentation_ratio(RedisConstant.Memory, \"mem_fragmentation_ratio\", \"内存碎片率\", false),\n    \n    /**\n     * Replication\n     */\n    role(RedisConstant.Replication, \"role\", \"主从角色\", false),\n    master_host(RedisConstant.Replication, \"master_host\", \"主节点host\", false),\n    master_port(RedisConstant.Replication, \"master_port\", \"主节点端口\", false),\n    connected_slaves(RedisConstant.Replication, \"connected_slaves\", \"从节点数量\", false),\n    master_repl_offset(RedisConstant.Replication, \"master_repl_offset\", \"主节点偏移量\", false),\n    master_link_status(RedisConstant.Replication, \"master_link_status\", \"主节点连接状态\", false),\n    slave_repl_offset(RedisConstant.Replication, \"slave_repl_offset\", \"从节点偏移量\", false),\n    master_sync_in_progress(RedisConstant.Replication, \"master_sync_in_progress\", \"从节点是否正在同步主节点\", false)\n\n    ;\n    \n    private final static List<RedisInfoEnum> RedisInfoEnumList = new ArrayList<RedisInfoEnum>();\n    static {\n        for (RedisInfoEnum redisInfoEnum : RedisInfoEnum.values()) {\n            RedisInfoEnumList.add(redisInfoEnum);\n        }\n    }\n    \n    private RedisConstant redisConstant;\n    \n    private String value;\n    \n    private String info;\n    \n    private boolean needCalDif;\n\n    private RedisInfoEnum(RedisConstant redisConstant, String value, String info, boolean needCalDif) {\n        this.redisConstant = redisConstant;\n        this.value = value;\n        this.info = info;\n        this.needCalDif = needCalDif;\n    }\n    \n    /**\n     * 获取需要计算差值的统计属性\n     * @return\n     */\n    public static List<RedisInfoEnum> getNeedCalDifRedisInfoEnumList() {\n        List<RedisInfoEnum> resultList = new ArrayList<RedisInfoEnum>();\n        for (RedisInfoEnum redisInfoEnum : RedisInfoEnumList) {\n            if (redisInfoEnum.isNeedCalDif()) {\n                resultList.add(redisInfoEnum);\n            }\n        }\n        return resultList;\n    }\n    \n    public RedisConstant getRedisConstant() {\n        return redisConstant;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public boolean isNeedCalDif() {\n        return needCalDif;\n    }\n\n\n    \n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/RedisReadOnlyCommandEnum.java",
    "content": "package com.sohu.cache.redis.enums;\r\n\r\nimport com.sohu.cache.constant.SymbolConstant;\r\nimport org.apache.commons.lang.StringUtils;\r\n\r\n/**\r\n * Created by yijunzhang on 14-10-14.\r\n */\r\npublic enum RedisReadOnlyCommandEnum {\r\n//    debug(\"debug\"), //去除debug命令\r\n    exists(\"exists\"),\r\n    object(\"object\"),\r\n    ttl(\"ttl\"),\r\n    type(\"type\"),\r\n    scan(\"scan\"),\r\n    get(\"get\"),\r\n    getbit(\"getbit\"),\r\n    getrange(\"getrange\"),\r\n    mget(\"mget\"),\r\n    setrange(\"setrange\"),\r\n    strlen(\"strlen\"),\r\n    hexists(\"hexists\"),\r\n    hget(\"hget\"),\r\n    hgetall(\"hgetall\"),\r\n    hkeys(\"hkeys\"),\r\n    hlen(\"hlen\"),\r\n    hmget(\"hmget\"),\r\n    hvals(\"hvals\"),\r\n    hscan(\"hscan\"),\r\n    lindex(\"lindex\"),\r\n    llen(\"llen\"),\r\n    lrange(\"lrange\"),\r\n    scard(\"scard\"),\r\n    sismember(\"sismember\"),\r\n    sscan(\"sscan\"),\r\n    srandmember(\"srandmember\"),\r\n    zcard(\"zcard\"),\r\n    zcount(\"zcount\"),\r\n    zrange(\"zrange\"),\r\n    zrangebyscore(\"zrangebyscore\"),\r\n    zrank(\"zrank\"),\r\n    zrevrange(\"zrevrange\"),\r\n    zscore(\"zscore\"),\r\n    zscan(\"zscan\"),\r\n    dbsize(\"dbsize\"),\r\n    info(\"info\"),\r\n    time(\"time\"),\r\n    lastsave(\"lastsave\"),\r\n    memory(\"memory\"),\r\n\r\n    JSON_GET(\"JSON.GET\"),\r\n    JSON_MGET(\"JSON.MGET\"),\r\n    JSON_ARRLEN(\"JSON.ARRLEN\"),\r\n    JSON_DEBUG_MEMORY(\"JSON.DEBUGMEMORY\"),\r\n    JSON_OBJKEYS(\"JSON.OBJKEYS\"),\r\n    JSON_OBJLEN(\"JSON.OBJLEN\"),\r\n    JSON_RESP(\"JSON.RESP\"),\r\n    JSON_TOGGLE(\"JSON.TOGGLE\"),\r\n    JSON_TYPE(\"JSON.TYPE\"),\r\n\r\n    FT_LIST(\"FT._LIST\"),\r\n    FT_CONFIG_GET(\"FT.CONFIGGET\"),\r\n    FT_EXPLAIN(\"FT.EXPLAIN\"),\r\n    FT_EXPLAINCLI(\"FT.EXPLAINCLI\"),\r\n    FT_INFO(\"FT.INFO\"),\r\n    FT_CURSOR_READ(\"FT.CURSORREAD\"),\r\n    FT_DICTDUMP(\"FT.DICTDUMP\"),\r\n    FT_SEARCH(\"FT.SEARCH\"),\r\n    FT_AGGREGATE(\"FT.AGGREGATE\"),\r\n    FT_PROFILE(\"FT.PROFILE\"),\r\n    FT_SYNDUMP(\"FT.SYNDUMP\"),\r\n    FT_TAGVALS(\"FT.TAGVALS\"),\r\n\r\n    BF_EXISTS(\"BF.EXISTS\"),\r\n    BF_MEXISTS(\"BF.MEXISTS\"),\r\n    BF_INFO(\"BF.INFO\"),\r\n\r\n    CF_COUNT(\"CF.COUNT\"),\r\n    CF_EXISTS(\"CF.EXISTS\"),\r\n    CF_INFO(\"CF.INFO\"),\r\n    CF_MEXISTS(\"CF.MEXISTS\"),\r\n\r\n    CMS_INFO(\"CMS.INFO\"),\r\n    CMS_QUERY(\"CMS.QUERY\");\r\n\r\n    private String command;\r\n\r\n    RedisReadOnlyCommandEnum(String command){\r\n        this.command = command;\r\n    }\r\n\r\n    public String getCommand() {\r\n        return command;\r\n    }\r\n\r\n    public static boolean contains(String command) {\r\n        if (StringUtils.isBlank(command)) {\r\n            return false;\r\n        }\r\n        for (RedisReadOnlyCommandEnum readEnum : RedisReadOnlyCommandEnum.values()) {\r\n            String readCommand = readEnum.getCommand();\r\n            command = StringUtils.remove(command, SymbolConstant.SPACE);\r\n            if (command.length() < readCommand.toString().length()) {\r\n                continue;\r\n            }\r\n            String head = StringUtils.substring(command, 0, readCommand.length());\r\n            if (readCommand.equalsIgnoreCase(head)) {\r\n                return true;\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/enums/RedisSentinelConfigEnum.java",
    "content": "package com.sohu.cache.redis.enums;\n\n/**\n * Created by yijunzhang on 14-8-25.\n */\npublic enum RedisSentinelConfigEnum {\n    PORT(\"port\", \"%d\", \"sentinel实例端口\"),\n    DIR(\"dir\", \"/tmp\", \"文件目录\"),\n    MONITOR(\"sentinel monitor\", \"%s %s %d %d\", \"master名称定义和最少参与监控的sentinel数,格式:masterName ip port num\"),\n    DOWN_AFTER_MILLISECONDS(\"sentinel down-after-milliseconds\", \"%s 20000\", \"Sentinel判定服务器断线的毫秒数,默认:20秒\"),\n    FAILOVER_TIMEOUT(\"sentinel failover-timeout\", \"%s 180000\", \"故障迁移超时时间,默认:3分钟\"),\n    PARALLEL_SYNCS(\"sentinel parallel-syncs\", \"%s 1\", \"在执行故障转移时,最多有多少个从服务器同时对新的主服务器进行同步,默认:1\");\n\n    private String key;\n\n    private String value;\n\n    private String desc;\n\n    RedisSentinelConfigEnum(String key, String value, String desc) {\n        this.key = key;\n        this.value = value;\n        this.desc = desc;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public static RedisSentinelConfigEnum get(String key) {\n        if (key == null) {\n            return null;\n        }\n        for (RedisSentinelConfigEnum config : RedisSentinelConfigEnum.values()) {\n            if (config.key.equals(key)) {\n                return config;\n            }\n        }\n        return null;\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/impl/AssistRedisServiceImpl.java",
    "content": "package com.sohu.cache.redis.impl;\n\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.util.Command;\nimport com.sohu.cache.redis.util.ProtostuffSerializer;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.pool2.impl.GenericObjectPoolConfig;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.Protocol;\nimport redis.clients.jedis.Tuple;\nimport redis.clients.jedis.commands.ProtocolCommand;\nimport redis.clients.jedis.exceptions.JedisConnectionException;\nimport redis.clients.jedis.params.SetParams;\nimport redis.clients.jedis.util.SafeEncoder;\n\nimport javax.annotation.PostConstruct;\nimport java.nio.charset.Charset;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n@Component\npublic class AssistRedisServiceImpl implements AssistRedisService {\n    private Logger logger = LoggerFactory.getLogger(AssistRedisServiceImpl.class);\n\n    @Value(\"${cachecloud.redis.main.host:127.0.0.1}\")\n    private String mainHost;\n\n    @Value(\"${cachecloud.redis.main.port:6379}\")\n    private int mainPort;\n\n    @Value(\"${cachecloud.redis.main.password:}\")\n    private String mainPassword;\n\n    private JedisPool jedisPoolMain;\n\n    private ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();\n\n    @PostConstruct\n    public void init() {\n        GenericObjectPoolConfig config = new GenericObjectPoolConfig();\n        if(StringUtils.isNotBlank(mainPassword)){\n            jedisPoolMain = new JedisPool(config, mainHost, mainPort, Protocol.DEFAULT_TIMEOUT, mainPassword);\n        }else{\n            logger.error(\"The assist redis password is not configured, please confirm and strongly recommend config it.\");\n            jedisPoolMain = new JedisPool(config, mainHost, mainPort, Protocol.DEFAULT_TIMEOUT);\n        }\n    }\n\n    /**\n     * low版本，应该用vip或者hystrix，这里是以防万一\n     *\n     * @return\n     */\n    private Jedis getFromJedisPool() throws Exception{\n        try {\n            return jedisPoolMain.getResource();\n        } catch (JedisConnectionException ce){\n            logger.warn(\"Please Make sure the file:application-${profile}.yml connection pool is configured correctly !  cachecloud.redis.main.host:{} cachecloud.redis.main.port:{} cachecloud.redis.main.password:{}\",mainHost,mainPort,mainPassword);\n            throw ce;\n        } catch (Exception e) {\n            logger.warn(e.getMessage(),e);\n            throw e;\n        }\n    }\n\n    @Override\n    public boolean rpush(String key, String item) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.rpush(key, item);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"rpush {} {} error \" + e.getMessage(), key, item, e);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean rpush(String key, String... items) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.rpush(key, items);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"rpush {} {} error \" + e.getMessage(), key, items, e);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public List<String> lrange(String key, int start, int end) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.lrange(key, start, end);\n        } catch (Exception e) {\n            logger.warn(\"lrange {} {} {} error \" + e.getMessage(), key, start, end);\n            return Collections.emptyList();\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean rpushList(String key, List<String> items) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.rpush(key, items.toArray(new String[items.size()]));\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"rpushList {} {} error \" + e.getMessage(), key, items);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public Long llen(final String key){\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            Long llen = jedis.llen(key);\n            return llen;\n        } catch (Exception e) {\n            logger.warn(\"llen {} {} error \" + e.getMessage(), key);\n            return 0L;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public String lpop(final String key){\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            String lpop = jedis.lpop(key);\n            return lpop;\n        } catch (Exception e) {\n            logger.warn(\"rpushList {} {} error \" + e.getMessage(), key);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public Long lrem(final String key, long count, String element){\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            Long lrem = jedis.lrem(key, count, element);\n            return lrem;\n        } catch (Exception e) {\n            logger.warn(\"lrem {} {} {} error \" + e.getMessage(), key, count, element);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public String ltrim(final String key, long start, long end){\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            String lrem = jedis.ltrim(key, start, end);\n            return lrem;\n        } catch (Exception e) {\n            logger.warn(\"ltrim {} {} {} error \" + e.getMessage(), key, start, end);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n\n    @Override\n    public boolean saddSet(String key, Set<String> items) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.sadd(key, items.toArray(new String[items.size()]));\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"saddList {} {} error \" + e.getMessage(), key, items);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean sadd(String key, String item) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.sadd(key, item);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"sadd {} {} error \" + e.getMessage(), key, item);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public Set<String> smembers(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.smembers(key);\n        } catch (Exception e) {\n            logger.warn(\"smembers {} error \" + e.getMessage(), key);\n            return Collections.emptySet();\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean srem(String key, String item) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.srem(key, item);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"srem {} {} error \" + e.getMessage(), key, item);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean reloadSentinel() {\n        return false;\n    }\n\n    @Override\n    public <T> boolean set(String key, T value) {\n        if (value == null) {\n            return false;\n        }\n        byte[] bytes = protostuffSerializer.serialize(value);\n\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.set(key.getBytes(Charset.forName(\"UTF-8\")), bytes);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"set {} error \" + e.getMessage(), key, e);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public <T> boolean set(String key, T value, int seconds) {\n        if (value == null) {\n            return false;\n        }\n        byte[] bytes = protostuffSerializer.serialize(value);\n\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.setex(key.getBytes(Charset.forName(\"UTF-8\")), seconds, bytes);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"setex {} {} error \" + e.getMessage(), key, seconds);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    public boolean setNx(String key, String value) {\n        Jedis jedis = null;\n        Long result = 0l;\n        try {\n            jedis = getFromJedisPool();\n            result = jedis.setnx(key, value);\n        } catch (Exception e) {\n            logger.warn(\"setnx {} {} error:{} \", key, value, e.getMessage());\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n        return result == 1 ? true : false;\n    }\n\n    public String set(String key, String value, SetParams params) {\n\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.set(key, value, params);\n        } catch (Exception e) {\n            logger.warn(\"set {} {} {} error \" + e.getMessage(), key, value, params);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public <T> boolean setWithNoSerialize(String key, T value) {\n        if (value == null) {\n            return false;\n        }\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.set(key, value.toString());\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"setWithNoSerialize {} error \" + e.getMessage(), key);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public <T> boolean setWithNoSerialize(String key, T value, int seconds) {\n        if (value == null) {\n            return false;\n        }\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.setex(key, seconds, value.toString());\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"setWithNoSerialize {} error \" + e.getMessage(), key);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public String getWithNoSerialize(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.get(key);\n        } catch (Exception e) {\n            logger.warn(\"getWithNoSerialize {} error \" + e.getMessage(), key);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean remove(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.del(key);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"remove {} error \" + e.getMessage(), key);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean zadd(String key, long score, String member) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.zadd(key, score, member);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"zadd {} {} {} error \" + e.getMessage(), key, score, member);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public String hget(String key, String field){\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.hget(key, field);\n        } catch (Exception e) {\n            logger.warn(\"hget {} {} error \" + e.getMessage(), key, field);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean hset(String key, String field, String value) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.hset(key, field, value);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"hset {} {} {} error \" + e.getMessage(), key, field, value);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public Long hsetnx(String key, String field, String value){\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.hsetnx(key, field, value);\n        } catch (Exception e) {\n            logger.warn(\"hsetnx {} {} {} error \" + e.getMessage(), key, field, value);\n            return 0L;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n\n\n    @Override\n    public boolean hmset(String key, Map<String, String> map) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.hmset(key, map);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"hset {} {} error \" + e.getMessage(), key, map);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public Map<String, String> hgetAll(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.hgetAll(key);\n        } catch (Exception e) {\n            logger.warn(\"hgetAll {} error \" + e.getMessage(), key);\n            return Collections.emptyMap();\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public Long hdel(String key, String field) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.hdel(key, field);\n        } catch (Exception e) {\n            logger.warn(\"hdel {} error \" + e.getMessage(), key);\n            return 0L;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public <T> T get(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            byte[] bytes = jedis.get(key.getBytes(Charset.forName(\"UTF-8\")));\n            if (bytes == null) {\n                return null;\n            }\n            T t = protostuffSerializer.deserialize(bytes);\n            return t;\n        } catch (Exception e) {\n            logger.warn(\"get {} error \" + e.getMessage(), key);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean del(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.del(key);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"del {} error \" + e.getMessage(), key);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean delMulti(String... keys) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.del(keys);\n            return true;\n        } catch (Exception e) {\n            logger.warn(\"delMulti {} error \" + e.getMessage(), keys);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public void zincrby(String key, double score, String member) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            jedis.zincrby(key, score, member);\n        } catch (Exception e) {\n            logger.warn(\"zincrby {} {} {} error \" + e.getMessage(), key, score, member);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public Set<Tuple> zrangeWithScores(String key, long start, long end) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.zrangeWithScores(key, start, end);\n        } catch (Exception e) {\n            logger.warn(\"zrangeWithScores {} {} {}error \" + e.getMessage(), key, start, end);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean exists(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            return jedis.exists(key);\n        } catch (Exception e) {\n            logger.warn(\"del {} error \" + e.getMessage(), key);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    public void setProtostuffSerializer(ProtostuffSerializer protostuffSerializer) {\n        this.protostuffSerializer = protostuffSerializer;\n    }\n\n    @Override\n    public boolean setNEX(String key, String value, int seconds) {\n        Jedis jedis = null;\n        try {\n            jedis = getFromJedisPool();\n            Object rst = jedis.sendCommand(Command.SET, key, value, \"NX\", \"EX\", String.valueOf(seconds));\n            if(rst != null){\n                String encode = SafeEncoder.encode((byte[]) rst);\n                if(\"OK\".equals(encode)){\n                    return true;\n                }\n            }\n            return false;\n        } catch (Exception e) {\n            logger.warn(\"smembers {} error \" + e.getMessage(), key);\n            return false;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisCenterImpl.java",
    "content": "package com.sohu.cache.redis.impl;\n\nimport com.google.common.collect.HashBasedTable;\nimport com.google.common.collect.Table;\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.protocol.RedisProtocol;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.enums.DirEnum;\nimport com.sohu.cache.redis.enums.RedisInfoEnum;\nimport com.sohu.cache.redis.enums.RedisReadOnlyCommandEnum;\nimport com.sohu.cache.redis.util.*;\nimport com.sohu.cache.report.ReportDataComponent;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.util.*;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.ClientTypeEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.ResourceService;\nimport com.sohu.cache.web.service.WebClientComponent;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.vo.RedisSlowLog;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.pool2.impl.GenericObjectPoolConfig;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.scheduling.annotation.AsyncResult;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\nimport redis.clients.jedis.Client;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.JedisSentinelPool;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.Protocol;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.commands.ProtocolCommand;\nimport redis.clients.jedis.exceptions.JedisAskDataException;\nimport redis.clients.jedis.exceptions.JedisDataException;\nimport redis.clients.jedis.exceptions.JedisException;\nimport redis.clients.jedis.exceptions.JedisMovedDataException;\nimport redis.clients.jedis.util.SafeEncoder;\nimport redis.clients.jedis.util.Slowlog;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.PreDestroy;\nimport java.sql.Timestamp;\nimport java.text.DecimalFormat;\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.stream.Collectors;\n\n/**\n * Created by yijunzhang on 14-6-10.\n */\n@Service(\"redisCenter\")\npublic class RedisCenterImpl implements RedisCenter {\n    public static final int REDIS_DEFAULT_TIME = 1000;\n    public static final String REDIS_SLOWLOG_POOL = \"redis-slowlog-pool\";\n    private static final int COUNT = 1000;\n    private static List<RedisInfoEnum> otherNeedCalDifRedisInfoEnumList = new ArrayList<>();\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    private final Lock lock = new ReentrantLock();\n    @Autowired\n    private AppStatsDao appStatsDao;\n    @Autowired\n    private AsyncService asyncService;\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private InstanceStatsDao instanceStatsDao;\n    @Autowired\n    private InstanceStatsCenter instanceStatsCenter;\n    @Autowired\n    @Lazy\n    private MachineCenter machineCenter;\n    private volatile Map<String, JedisPool> jedisPoolMap = new HashMap<String, JedisPool>();\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private AppAuditLogDao appAuditLogDao;\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private InstanceSlowLogDao instanceSlowLogDao;\n    @Autowired\n    private InstanceLatencyHistoryDao instanceLatencyHistoryDao;\n    @Autowired\n    private WebClientComponent webClientComponent;\n    @Autowired\n    SSHService sshService;\n    @Autowired\n    private ReportDataComponent reportDataComponent;\n\n    @PostConstruct\n    public void init() {\n        asyncService.assemblePool(getThreadPoolKey(), AsyncThreadPoolFactory.REDIS_SLOWLOG_THREAD_POOL);\n        otherNeedCalDifRedisInfoEnumList.add(RedisInfoEnum.mem_fragmentation_ratio);\n    }\n\n\n    private JedisPool maintainJedisPool(String host, int port, String password) {\n        String hostAndPort = ObjectConvert.linkIpAndPort(host, port);\n        JedisPool jedisPool = jedisPoolMap.get(hostAndPort);\n        if (jedisPool == null) {\n            lock.lock();\n            try {\n                //double check\n                jedisPool = jedisPoolMap.get(hostAndPort);\n                if (jedisPool == null) {\n                    try {\n                        if (StringUtils.isNotBlank(password)) {\n                            jedisPool = new JedisPool(new GenericObjectPoolConfig(), host, port,\n                                    Protocol.DEFAULT_TIMEOUT, password);\n                        } else {\n                            jedisPool = new JedisPool(new GenericObjectPoolConfig(), host, port,\n                                    Protocol.DEFAULT_TIMEOUT);\n                        }\n                        jedisPoolMap.put(hostAndPort, jedisPool);\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                    } finally {\n\n                    }\n                }\n            } finally {\n                lock.unlock();\n            }\n        }\n        return jedisPool;\n    }\n\n    private String buildFutureKey(long appId, long collectTime, String host, int port) {\n        StringBuilder keyBuffer = new StringBuilder(\"redis-\");\n        keyBuffer.append(collectTime);\n        keyBuffer.append(\"-\");\n        keyBuffer.append(appId);\n        keyBuffer.append(\"-\");\n        keyBuffer.append(host + \":\" + port);\n        return keyBuffer.toString();\n    }\n\n    @Override\n    public List<InstanceSlowLog> collectRedisSlowLog(long appId, long collectTime, String host, int port) {\n        Assert.isTrue(appId > 0);\n        Assert.hasText(host);\n        Assert.isTrue(port > 0);\n        InstanceInfo instanceInfo = instanceDao.getInstByIpAndPort(host, port);\n        //不存在实例/实例异常/下线\n        if (instanceInfo == null) {\n            return null;\n        }\n        if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\n            //忽略sentinel redis实例\n            return null;\n        }\n        // 从redis中获取慢查询日志\n        List<RedisSlowLog> redisLowLogList = getRedisSlowLogs(appId, host, port, 100);\n        if (CollectionUtils.isEmpty(redisLowLogList)) {\n            return Collections.emptyList();\n        }\n\n        // transfer\n        final List<InstanceSlowLog> instanceSlowLogList = new ArrayList<InstanceSlowLog>();\n        for (RedisSlowLog redisSlowLog : redisLowLogList) {\n            InstanceSlowLog instanceSlowLog = transferRedisSlowLogToInstance(redisSlowLog, instanceInfo);\n            if (instanceSlowLog == null) {\n                continue;\n            }\n            instanceSlowLogList.add(instanceSlowLog);\n        }\n\n        if (CollectionUtils.isEmpty(instanceSlowLogList)) {\n            return Collections.emptyList();\n        }\n\n        //处理\n        String key = getThreadPoolKey() + \"_\" + host + \"_\" + port;\n        boolean isOk = asyncService.submitFuture(getThreadPoolKey(), new KeyCallable<Boolean>(key) {\n            @Override\n            public Boolean execute() {\n                try {\n                    instanceSlowLogDao.batchSave(instanceSlowLogList);\n                    return true;\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n        });\n        if (!isOk) {\n            logger.error(\"slowlog submitFuture failed,appId:{},collectTime:{},host:{},port:{}\", appId, collectTime,\n                    host, port);\n        }\n        return instanceSlowLogList;\n    }\n\n    private InstanceSlowLog transferRedisSlowLogToInstance(RedisSlowLog redisSlowLog, InstanceInfo instanceInfo) {\n        if (redisSlowLog == null) {\n            return null;\n        }\n        String command = redisSlowLog.getCommand();\n        long executionTime = redisSlowLog.getExecutionTime();\n        //如果command=BGREWRITEAOF并且小于50毫秒,则忽略\n        if (command.equalsIgnoreCase(\"BGREWRITEAOF\") && executionTime < 50000) {\n            return null;\n        }\n        InstanceSlowLog instanceSlowLog = new InstanceSlowLog();\n        instanceSlowLog.setAppId(instanceInfo.getAppId());\n        instanceSlowLog.setCommand(redisSlowLog.getCommand());\n        instanceSlowLog.setCostTime((int) redisSlowLog.getExecutionTime());\n        instanceSlowLog.setCreateTime(new Timestamp(System.currentTimeMillis()));\n        instanceSlowLog.setExecuteTime(new Timestamp(redisSlowLog.getDate().getTime()));\n        instanceSlowLog.setInstanceId(instanceInfo.getId());\n        instanceSlowLog.setIp(instanceInfo.getIp());\n        instanceSlowLog.setPort(instanceInfo.getPort());\n        instanceSlowLog.setSlowLogId(redisSlowLog.getId());\n\n        return instanceSlowLog;\n    }\n\n    private String getThreadPoolKey() {\n        return REDIS_SLOWLOG_POOL;\n    }\n\n    @Override\n    public Map<Object, Map<String, Object>> collectRedisInfo(long appId, long collectTime, String host,\n                                                                    int port) {\n        long start = System.currentTimeMillis();\n\n        InstanceInfo instanceInfo = instanceDao.getInstByIpAndPort(host, port);\n        //不存在实例/实例异常/下线\n        if (instanceInfo == null) {\n            return null;\n        }\n        if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\n            //忽略sentinel redis实例\n            return null;\n        }\n        Map<Object, Map<String, Object>> infoMap = this.getInfoStats(appId, host, port);\n        if (infoMap == null || infoMap.isEmpty()) {\n            logger.error(\"appId:{},collectTime:{},host:{},port:{} cost={} ms redis infoMap is null\",\n                    new Object[]{appId, collectTime, host, port, (System.currentTimeMillis() - start)});\n            return infoMap;\n        }\n\n        //上报数据\n        Map<String, Object> redisInfoMap = new HashMap<>();\n        redisInfoMap.put(\"instanceInfo\", instanceInfo);\n        redisInfoMap.put(\"collectTime\", collectTime);\n        redisInfoMap.put(\"info\", infoMap);\n        reportDataComponent.reportRedisInfoData(redisInfoMap);\n\n        // cluster info统计\n        Map<String, Object> clusterInfoMap = getClusterInfoStats(appId, instanceInfo);\n\n        boolean isOk = asyncService\n                .submitFuture(new RedisKeyCallable(appId, collectTime, host, port, infoMap, clusterInfoMap));\n        if (!isOk) {\n            logger.error(\"submitFuture failed,appId:{},collectTime:{},host:{},port:{} cost={} ms\",\n                    new Object[]{appId, collectTime, host, port, (System.currentTimeMillis() - start)});\n        }\n        return infoMap;\n    }\n\n    @Override\n    public Map<Object, Map<String, Object>> getInfoStats(final long appId, final String host, final int port) {\n        Map<Object, Map<String, Object>> infoMap = null;\n        final StringBuilder infoBuilder = new StringBuilder();\n        try {\n            boolean isOk = new IdempotentConfirmer() {\n                private int timeOutFactor = 1;\n\n                @Override\n                public boolean execute() {\n                    Jedis jedis = null;\n                    try {\n                        jedis = getJedis(appId, host, port);\n                        jedis.getClient().setConnectionTimeout(REDIS_DEFAULT_TIME * (timeOutFactor++));\n                        jedis.getClient().setSoTimeout(REDIS_DEFAULT_TIME * (timeOutFactor++));\n                        String info = jedis.info(\"all\");\n                        infoBuilder.append(info);\n                        return StringUtils.isNotBlank(info);\n                    } catch (Exception e) {\n                        logger.warn(\"{}:{}, redis-getInfoStats errorMsg:{}\", host, port, e.getMessage());\n                        return false;\n                    } finally {\n                        if (jedis != null)\n                            jedis.close();\n                    }\n                }\n            }.run();\n            if (!isOk) {\n                return infoMap;\n            }\n            infoMap = processRedisStats(infoBuilder.toString());\n        } catch (Exception e) {\n            logger.error(e.getMessage() + \" {}:{}\", host, port, e);\n        }\n        if (infoMap == null || infoMap.isEmpty()) {\n            logger.error(\"host:{},port:{} redis infoMap is null\", host, port);\n            return infoMap;\n        }\n        return infoMap;\n    }\n\n    @Override\n    public Map<String, Object> getClusterInfoStats(final long appId, final String host, final int port) {\n        InstanceInfo instanceInfo = instanceDao.getAllInstByIpAndPort(host, port);\n        return getClusterInfoStats(appId, instanceInfo);\n    }\n\n    @Override\n    public Map<String, Object> getClusterInfoStats(final long appId, final InstanceInfo instanceInfo) {\n        long startTime = System.currentTimeMillis();\n        if (instanceInfo == null) {\n            logger.warn(\"getClusterInfoStats instanceInfo is null\");\n            return Collections.emptyMap();\n        }\n        if (!TypeUtil.isRedisCluster(instanceInfo.getType())) {\n            return Collections.emptyMap();\n        }\n        final String host = instanceInfo.getIp();\n        final int port = instanceInfo.getPort();\n        Map<String, Object> clusterInfoMap = null;\n        final StringBuilder infoBuilder = new StringBuilder();\n        try {\n            boolean isOk = new IdempotentConfirmer() {\n                private int timeOutFactor = 1;\n\n                @Override\n                public boolean execute() {\n                    Jedis jedis = null;\n                    try {\n                        jedis = getJedis(appId, host, port);\n                        jedis.getClient().setConnectionTimeout(REDIS_DEFAULT_TIME * (timeOutFactor++));\n                        jedis.getClient().setSoTimeout(REDIS_DEFAULT_TIME * (timeOutFactor++));\n                        String clusterInfo = jedis.clusterInfo();\n                        infoBuilder.append(clusterInfo);\n                        return StringUtils.isNotBlank(clusterInfo);\n                    } catch (Exception e) {\n                        logger.warn(\"{}:{}, redis-getInfoStats errorMsg:{}\", host, port, e.getMessage());\n                        return false;\n                    } finally {\n                        if (jedis != null)\n                            jedis.close();\n                    }\n                }\n            }.run();\n            if (!isOk) {\n                return clusterInfoMap;\n            }\n            clusterInfoMap = processClusterInfoStats(infoBuilder.toString());\n        } catch (Exception e) {\n            logger.error(e.getMessage() + \" {}:{}\", host, port, e);\n        }\n        if (MapUtils.isEmpty(clusterInfoMap)) {\n            logger.error(\"{}:{} redis clusterInfoMap is null\", host, port);\n            return Collections.emptyMap();\n        }\n        long costTime = System.currentTimeMillis() - startTime;\n        if (costTime > 1000) {\n            logger.warn(\"{}:{} cluster info cost time {} ms\", host, port, costTime);\n        }\n        return clusterInfoMap;\n    }\n\n    private void fillAccumulationMap(Map<Object, Map<String, Object>> infoMap,\n                                     Table<RedisConstant, String, Long> table) {\n        if (table == null || table.isEmpty()) {\n            return;\n        }\n        Map<String, Object> accMap = infoMap.get(RedisConstant.DIFF);\n        if (accMap == null) {\n            accMap = new LinkedHashMap<String, Object>();\n            infoMap.put(RedisConstant.DIFF, accMap);\n        }\n        for (RedisConstant constant : table.rowKeySet()) {\n            Map<String, Long> rowMap = table.row(constant);\n            accMap.putAll(rowMap);\n        }\n    }\n\n    private void fillDoubleAccumulationMap(Map<RedisConstant, Map<String, Object>> infoMap,\n                                           Table<RedisConstant, String, Double> table) {\n        if (table == null || table.isEmpty()) {\n            return;\n        }\n        Map<String, Object> accMap = infoMap.get(RedisConstant.DIFF);\n        if (accMap == null) {\n            accMap = new LinkedHashMap<String, Object>();\n            infoMap.put(RedisConstant.DIFF, accMap);\n        }\n        for (RedisConstant constant : table.rowKeySet()) {\n            Map<String, Double> rowMap = table.row(constant);\n            accMap.putAll(rowMap);\n        }\n    }\n\n    /**\n     * 内存碎片率统计最新值，不计算差值\n     */\n    private void fillMemFragRatioMap(Map<Object, Map<String, Object>> infoMap) {\n\n        Map<String, Double> currentMap = new LinkedHashMap<>();\n        RedisInfoEnum acc = RedisInfoEnum.mem_fragmentation_ratio;\n        Double count = getDoubleCount(infoMap, acc.getRedisConstant(), acc.getValue());\n        if (count != null) {\n            currentMap.put(acc.getValue(), count);\n        }\n        //DecimalFormat df = new DecimalFormat(\"##.##\");\n        Map<String, Object> accMap = infoMap.get(RedisConstant.DIFF);\n        if (accMap == null) {\n            accMap = new LinkedHashMap<>();\n            infoMap.put(RedisConstant.DIFF, accMap);\n        }\n        accMap.putAll(currentMap);\n    }\n\n    /**\n     * 获取累加参数值\n     *\n     * @param currentInfoMap\n     * @return 累加差值map\n     */\n    private Table<RedisConstant, String, Long> getAccumulationDiff(\n            Map<Object, Map<String, Object>> currentInfoMap,\n            Map<String, Object> lastInfoMap) {\n        //没有上一次统计快照，忽略差值统计\n        if (lastInfoMap == null || lastInfoMap.isEmpty()) {\n            return HashBasedTable.create();\n        }\n        Map<RedisInfoEnum, Long> currentMap = new LinkedHashMap<RedisInfoEnum, Long>();\n        for (RedisInfoEnum acc : RedisInfoEnum.getNeedCalDifRedisInfoEnumList()) {\n            Long count = getCommonCount(currentInfoMap, acc.getRedisConstant(), acc.getValue());\n            if (count != null) {\n                currentMap.put(acc, count);\n            }\n        }\n        Map<RedisInfoEnum, Long> lastMap = new LinkedHashMap<RedisInfoEnum, Long>();\n        for (RedisInfoEnum acc : RedisInfoEnum.getNeedCalDifRedisInfoEnumList()) {\n            Long lastCount = getCommonCount(lastInfoMap, acc.getRedisConstant(), acc.getValue());\n            if (lastCount != null) {\n                lastMap.put(acc, lastCount);\n            }\n        }\n        Table<RedisConstant, String, Long> resultTable = HashBasedTable.create();\n        for (RedisInfoEnum key : currentMap.keySet()) {\n            Long value = MapUtils.getLong(currentMap, key, null);\n            Long lastValue = MapUtils.getLong(lastMap, key, null);\n            if (value == null || lastValue == null) {\n                //忽略\n                continue;\n            }\n            long diff = 0L;\n            if (value > lastValue) {\n                diff = value - lastValue;\n            }\n            resultTable.put(key.getRedisConstant(), key.getValue(), diff);\n        }\n        return resultTable;\n    }\n\n    /**\n     * 获取累加参数值\n     *\n     * @param currentInfoMap\n     * @return 累加差值map\n     */\n    private Table<RedisConstant, String, Double> getDoubleAccumulationDiff(\n            Map<RedisConstant, Map<String, Object>> currentInfoMap,\n            Map<String, Object> lastInfoMap) {\n        //没有上一次统计快照，忽略差值统计\n        if (lastInfoMap == null || lastInfoMap.isEmpty()) {\n            return HashBasedTable.create();\n        }\n        Map<RedisInfoEnum, Double> currentMap = new LinkedHashMap<RedisInfoEnum, Double>();\n        for (RedisInfoEnum acc : otherNeedCalDifRedisInfoEnumList) {\n            Double count = getDoubleCount(currentInfoMap, acc.getRedisConstant(), acc.getValue());\n            if (count != null) {\n                currentMap.put(acc, count);\n            }\n        }\n        Map<RedisInfoEnum, Double> lastMap = new LinkedHashMap<RedisInfoEnum, Double>();\n        for (RedisInfoEnum acc : otherNeedCalDifRedisInfoEnumList) {\n            Double lastCount = getDoubleCount(lastInfoMap, acc.getRedisConstant(), acc.getValue());\n            if (lastCount != null) {\n                lastMap.put(acc, lastCount);\n            }\n        }\n        DecimalFormat df = new DecimalFormat(\"##.##\");\n        Table<RedisConstant, String, Double> resultTable = HashBasedTable.create();\n        for (RedisInfoEnum key : currentMap.keySet()) {\n            Double value = MapUtils.getDouble(currentMap, key, null);\n            Double lastValue = MapUtils.getDouble(lastMap, key, null);\n            if (value == null || lastValue == null) {\n                //忽略\n                continue;\n            }\n            double diff = 0D;\n            if (value > lastValue) {\n                diff = value - lastValue;\n            }\n            diff = Double.parseDouble(df.format(diff));\n            resultTable.put(key.getRedisConstant(), key.getValue(), diff);\n        }\n        return resultTable;\n    }\n\n    /**\n     * 获取命令差值统计\n     *\n     * @param currentInfoMap\n     * @param lastInfoMap\n     * @return 命令统计\n     */\n    private Table<RedisConstant, String, Long> getCommandsDiff(Map<Object, Map<String, Object>> currentInfoMap,\n                                                               Map<String, Object> lastInfoMap) {\n        //没有上一次统计快照，忽略差值统计\n        if (lastInfoMap == null || lastInfoMap.isEmpty()) {\n            return HashBasedTable.create();\n        }\n        Map<String, Object> map = currentInfoMap.get(RedisConstant.Commandstats);\n        Map<String, Long> currentMap = transferLongMap(map);\n        Map<String, Object> lastObjectMap;\n        if (lastInfoMap.get(RedisConstant.Commandstats.getValue()) == null) {\n            lastObjectMap = new HashMap<String, Object>();\n        } else {\n            lastObjectMap = (Map<String, Object>) lastInfoMap.get(RedisConstant.Commandstats.getValue());\n        }\n        Map<String, Long> lastMap = transferLongMap(lastObjectMap);\n\n        Table<RedisConstant, String, Long> resultTable = HashBasedTable.create();\n        for (String command : currentMap.keySet()) {\n            long lastCount = MapUtils.getLong(lastMap, command, 0L);\n            long currentCount = MapUtils.getLong(currentMap, command, 0L);\n            if (currentCount > lastCount) {\n                resultTable.put(RedisConstant.Commandstats, command, currentCount - lastCount);\n            }\n        }\n        return resultTable;\n    }\n\n    private AppStats getAppStats(long appId, long collectTime, Table<RedisConstant, String, Long> table,\n                                 Map<Object, Map<String, Object>> infoMap) {\n        AppStats appStats = new AppStats();\n        appStats.setAppId(appId);\n        appStats.setCollectTime(collectTime);\n        appStats.setModifyTime(new Date());\n        appStats.setUsedMemory(\n                MapUtils.getLong(infoMap.get(RedisConstant.Memory), RedisInfoEnum.used_memory.getValue(), 0L));\n        appStats.setUsedMemoryRss(\n                MapUtils.getLong(infoMap.get(RedisConstant.Memory), RedisInfoEnum.used_memory_rss.getValue(), 0L));\n        appStats.setHits(MapUtils.getLong(table.row(RedisConstant.Stats), RedisInfoEnum.keyspace_hits.getValue(), 0L));\n        appStats.setMisses(\n                MapUtils.getLong(table.row(RedisConstant.Stats), RedisInfoEnum.keyspace_misses.getValue(), 0L));\n        appStats.setEvictedKeys(\n                MapUtils.getLong(table.row(RedisConstant.Stats), RedisInfoEnum.evicted_keys.getValue(), 0L));\n        appStats.setExpiredKeys(\n                MapUtils.getLong(table.row(RedisConstant.Stats), RedisInfoEnum.expired_keys.getValue(), 0L));\n        appStats.setNetInputByte(\n                MapUtils.getLong(table.row(RedisConstant.Stats), RedisInfoEnum.total_net_input_bytes.getValue(), 0L));\n        appStats.setNetOutputByte(\n                MapUtils.getLong(table.row(RedisConstant.Stats), RedisInfoEnum.total_net_output_bytes.getValue(), 0L));\n\n        appStats.setConnectedClients(MapUtils.getIntValue(infoMap.get(RedisConstant.Clients),\n                RedisInfoEnum.connected_clients.getValue(), 0));\n        appStats.setObjectSize(getObjectSize(infoMap));\n\n        appStats.setCpuSys(MapUtils.getLongValue(table.row(RedisConstant.CPU), RedisInfoEnum.used_cpu_sys.getValue(),\n                0));\n        appStats.setCpuUser(\n                MapUtils.getLongValue(table.row(RedisConstant.CPU), RedisInfoEnum.used_cpu_user.getValue(),\n                        0));\n        appStats.setCpuSysChildren(\n                MapUtils.getLongValue(table.row(RedisConstant.CPU), RedisInfoEnum.used_cpu_sys_children.getValue(),\n                        0));\n        appStats.setCpuUserChildren(\n                MapUtils.getLongValue(table.row(RedisConstant.CPU), RedisInfoEnum.used_cpu_user_children.getValue(),\n                        0));\n        logger.debug(\"appStats={} table={}\", appStats, table);\n        return appStats;\n    }\n\n    private AppStats getDefaultAppStats(long appId, long collectTime) {\n        AppStats appStats = new AppStats();\n        appStats.setAppId(appId);\n        appStats.setCollectTime(collectTime);\n        appStats.setModifyTime(new Date());\n        appStats.setUsedMemory(0L);\n        appStats.setUsedMemoryRss(0L);\n        appStats.setHits(0);\n        appStats.setMisses(0L);\n        appStats.setEvictedKeys(0L);\n        appStats.setExpiredKeys( 0L);\n        appStats.setNetInputByte( 0L);\n        appStats.setNetOutputByte( 0L);\n        appStats.setConnectedClients(0);\n        appStats.setObjectSize(0L);\n        appStats.setCpuSys(0L);\n        appStats.setCpuUser(0L);\n        appStats.setCpuSysChildren(0L);\n        appStats.setCpuUserChildren(0L);\n        return appStats;\n    }\n\n    private long getObjectSize(Map<Object, Map<String, Object>> currentInfoMap) {\n        Map<String, Object> sizeMap = currentInfoMap.get(RedisConstant.Keyspace);\n        if (sizeMap == null || sizeMap.isEmpty()) {\n            return 0L;\n        }\n        long result = 0L;\n        Map<String, Long> longSizeMap = transferLongMap(sizeMap);\n\n        for (Map.Entry<String, Long> entry : longSizeMap.entrySet()) {\n            result += entry.getValue();\n        }\n        return result;\n    }\n\n    private Long getCommonCount(Map<?, ?> infoMap, RedisConstant redisConstant, String commond) {\n        Object constantObject =\n                infoMap.get(redisConstant) == null ? infoMap.get(redisConstant.getValue()) : infoMap.get(redisConstant);\n        if (constantObject != null && (constantObject instanceof Map)) {\n            Map constantMap = (Map) constantObject;\n            if (constantMap.get(commond) == null) {\n                return null;\n            }\n            return MapUtils.getLongValue(constantMap, commond);\n        }\n        return null;\n    }\n\n    private Double getDoubleCount(Map<?, ?> infoMap, RedisConstant redisConstant, String commond) {\n        Object constantObject =\n                infoMap.get(redisConstant) == null ? infoMap.get(redisConstant.getValue()) : infoMap.get(redisConstant);\n        if (constantObject != null && (constantObject instanceof Map)) {\n            Map constantMap = (Map) constantObject;\n            return MapUtils.getDoubleValue(constantMap, commond);\n        }\n        return null;\n    }\n\n    /**\n     * 转换redis 命令行统计结果\n     *\n     * @param commandMap\n     * @return\n     */\n    private Map<String, Long> transferLongMap(Map<String, Object> commandMap) {\n        Map<String, Long> resultMap = new HashMap<String, Long>();\n        if (commandMap == null || commandMap.isEmpty()) {\n            return resultMap;\n        }\n        for (Map.Entry<String, Object> entry : commandMap.entrySet()) {\n            if (entry.getValue() == null) {\n                continue;\n            }\n            String key = entry.getKey();\n            String value = entry.getValue().toString();\n            String[] stats = value.split(\",\");\n            if (stats.length == 0) {\n                continue;\n            }\n            String[] calls = stats[0].split(\"=\");\n            if (calls == null || calls.length < 2) {\n                continue;\n            }\n            long callCount = Long.parseLong(calls[1]);\n            resultMap.put(key, callCount);\n        }\n        return resultMap;\n    }\n\n    private List<AppCommandStats> getCommandStatsList(long appId, long collectTime,\n                                                      Table<RedisConstant, String, Long> table) {\n        Map<String, Long> commandMap = table.row(RedisConstant.Commandstats);\n        List<AppCommandStats> list = new ArrayList<AppCommandStats>();\n        if (commandMap == null) {\n            return list;\n        }\n        for (String key : commandMap.keySet()) {\n            String commandName = key.replace(\"cmdstat_\", \"\");\n            long callCount = MapUtils.getLong(commandMap, key, 0L);\n            if (callCount == 0L) {\n                continue;\n            }\n            AppCommandStats commandStats = new AppCommandStats();\n            commandStats.setAppId(appId);\n            commandStats.setCollectTime(collectTime);\n            commandStats.setCommandName(commandName);\n            commandStats.setCommandCount(callCount);\n            commandStats.setModifyTime(new Date());\n            list.add(commandStats);\n        }\n        return list;\n    }\n\n    /**\n     * 处理clusterinfo统计信息\n     *\n     * @param clusterInfo\n     * @return\n     */\n    private Map<String, Object> processClusterInfoStats(String clusterInfo) {\n        Map<String, Object> clusterInfoMap = new HashMap<String, Object>();\n        String[] lines = clusterInfo.split(\"\\r\\n\");\n        for (String line : lines) {\n            String[] pair = line.split(\":\");\n            if (pair.length == 2) {\n                clusterInfoMap.put(pair[0], pair[1]);\n            }\n        }\n        return clusterInfoMap;\n    }\n\n    /**\n     * 处理redis统计信息\n     *\n     * @param statResult 统计结果串\n     */\n    private Map<Object, Map<String, Object>> processRedisStats(String statResult) {\n        Map<Object, Map<String, Object>> redisStatMap = new HashMap<>();\n        String[] data = statResult.split(\"\\r\\n\");\n        String key;\n        int i = 0;\n        int length = data.length;\n        while (i < length) {\n            if (data[i].contains(\"#\")) {\n                int index = data[i].indexOf('#');\n                key = data[i].substring(index + 1);\n                ++i;\n                RedisConstant redisConstant = RedisConstant.value(key.trim());\n                if (redisConstant == null) {\n                    continue;\n                }\n                Map<String, Object> sectionMap = new LinkedHashMap<String, Object>();\n                while (i < length && data[i].contains(\":\")) {\n                    String[] pair = StringUtils.splitByWholeSeparator(data[i], \":\");\n                    sectionMap.put(pair[0], pair[1]);\n                    i++;\n                }\n                redisStatMap.put(redisConstant, sectionMap);\n            } else {\n                i++;\n            }\n        }\n        return redisStatMap;\n    }\n\n    /**\n     * 根据infoMap的结果判断实例的主从\n     *\n     * @param infoMap\n     * @return\n     */\n    private BooleanEnum hasSlaves(Map<Object, Map<String, Object>> infoMap) {\n        Map<String, Object> replicationMap = infoMap.get(RedisConstant.Replication);\n        if (MapUtils.isEmpty(replicationMap)) {\n            return BooleanEnum.OTHER;\n        }\n        for (Entry<String, Object> entry : replicationMap.entrySet()) {\n            String key = entry.getKey();\n            //判断一个即可\n            if (key != null && key.contains(\"slave0\")) {\n                return BooleanEnum.TRUE;\n            }\n        }\n        return BooleanEnum.FALSE;\n    }\n\n    /**\n     * 根据infoMap的结果判断实例的主从\n     *\n     * @param infoMap\n     * @return\n     */\n    private BooleanEnum isMaster(Map<Object, Map<String, Object>> infoMap) {\n        Map<String, Object> map = infoMap.get(RedisConstant.Replication);\n        if (map == null || map.get(RedisInfoEnum.role.getValue()) == null) {\n            //return null;\n            return BooleanEnum.OTHER;\n        }\n        if (String.valueOf(map.get(RedisInfoEnum.role.getValue())).equals(\"master\")) {\n            //return true;\n            return BooleanEnum.TRUE;\n        }\n        //return false;\n        return BooleanEnum.FALSE;\n    }\n\n    /**\n     * 根据ip和port判断某一个实例当前是主还是从\n     *\n     * @param ip   ip\n     * @param port port\n     * @return 主返回true， 从返回false；\n     */\n    @Override\n    public BooleanEnum isMaster(long appId, String ip, int port) {\n        Jedis jedis = null;\n        try {\n            jedis = getJedis(appId, ip, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n            String info = jedis.info(\"all\");\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            return isMaster(infoMap);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return BooleanEnum.OTHER;\n        } finally {\n            if (jedis != null)\n                try{\n                    jedis.close();\n                }catch (Exception e){\n                    logger.error(e.getMessage(), e);\n                }\n        }\n    }\n\n    /**\n     * 根据infoMap的结果判断实例的主从\n     *\n     * @param infoMap\n     * @return\n     */\n    private BooleanEnum isSlaveAndPointedMasterUp(Map<Object, Map<String, Object>> infoMap, InstanceInfo masterInstance) {\n        if(masterInstance == null){\n            return BooleanEnum.FALSE;\n        }\n        Map<String, Object> map = infoMap.get(RedisConstant.Replication);\n        if (map == null || map.get(RedisInfoEnum.role.getValue()) == null) {\n            //return null;\n            return BooleanEnum.FALSE;\n        }\n        if (String.valueOf(map.get(RedisInfoEnum.role.getValue())).equals(\"slave\")\n                && (String.valueOf(map.get(RedisInfoEnum.master_link_status.getValue())).equals(\"up\"))\n                && (String.valueOf(map.get(RedisInfoEnum.master_host.getValue())).equals(masterInstance.getIp()))\n                && (String.valueOf(map.get(RedisInfoEnum.master_port.getValue())).equals(String.valueOf(masterInstance.getPort())))\n        ){\n            return BooleanEnum.TRUE;\n        }\n        return BooleanEnum.FALSE;\n    }\n\n    /**\n     * 判断实例是否为从节点，并且与主节点连接有效\n     * @param appDesc\n     * @param slaveInstance\n     * @param masterInstance\n     * @return\n     */\n    @Override\n    public BooleanEnum isSlaveAndPointedMasterUp(AppDesc appDesc, InstanceInfo slaveInstance, InstanceInfo masterInstance){\n        Jedis jedis = null;\n        try {\n            jedis = getJedis(slaveInstance.getIp(), slaveInstance.getPort(), appDesc.getAppPassword());\n            String info = jedis.info(\"all\");\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            return isSlaveAndPointedMasterUp(infoMap, masterInstance);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return BooleanEnum.FALSE;\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n    }\n\n    @Override\n    public long getDbSize(long appId, String ip, int port) {\n        Jedis jedis = getJedis(appId, ip, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n        try {\n            return jedis.dbSize();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return -1;\n        } finally {\n            jedis.close();\n        }\n    }\n\n    /**\n     * @Description: scan key\n     * @Author: caoru\n     * @CreateDate: 2018/11/13 16:08\n     */\n    @Async\n    public Future<List<String>> findInstancePatternKeys(long appId, String ip, int port, String pattern) {\n        List<String> list = new ArrayList<String>();\n        Jedis jedis = getAdminJedis(appId, ip, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n        try {\n            String cursor = ScanParams.SCAN_POINTER_START;\n            ScanParams params = new ScanParams().match(pattern).count(COUNT);\n            do {\n                ScanResult<String> result = jedis.scan(cursor, params);\n                list.addAll(result.getResult());\n                cursor = result.getCursor();\n            } while (!\"0\".equals(cursor));\n            return new AsyncResult<List<String>>(list);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return new AsyncResult<List<String>>(list);\n        } finally {\n            jedis.close();\n        }\n    }\n\n    /**\n     * @Description: 查询单实例的big key\n     * @Author: caoru\n     * @CreateDate: 2018/11/13 16:08\n     */\n    public List<String> findInstanceBigKey(long appId, String ip, int port, long startBytes, long endBytes) {\n        List<String> list = new ArrayList<String>();\n        Jedis jedis = getAdminJedis(appId, ip, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n        try {\n            String cursor = ScanParams.SCAN_POINTER_START;\n            ScanParams params = new ScanParams().count(COUNT);\n            do {\n                ScanResult<String> result = jedis.scan(cursor, params);\n                for (String key : result.getResult()) {\n                    String keyType = jedis.type(key);\n                    if (\"string\".equals(keyType)) {\n                        long len = jedis.strlen(key);\n                        if (len > startBytes && len < endBytes) {\n                            list.add(key);\n                        }\n                    } else {\n                        Long memoryUsageRes = jedis.memoryUsage(key);\n                        if (memoryUsageRes != null && memoryUsageRes > startBytes && memoryUsageRes < endBytes) {\n                            list.add(key);\n                        }\n                    }\n                }\n                cursor = result.getCursor();\n            } while (!\"0\".equals(cursor));\n            return list;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return list;\n        } finally {\n            jedis.close();\n        }\n    }\n\n    /**\n     * @Description: 查询应用的big key\n     * @Author: caoru\n     * @CreateDate: 2018/11/13 16:08\n     */\n    public List<String> findClusterBigKey(long appId, long startBytes, long endBytes) {\n        List<String> list = new ArrayList<String>();\n        List<InstanceInfo> allMasterInstance = getAllHealthyInstanceInfo(appId);\n        for (InstanceInfo masterInstance : allMasterInstance) {\n            String ip = masterInstance.getIp();\n            int port = masterInstance.getPort();\n            List<String> res = findInstanceBigKey(appId, ip, port, startBytes, endBytes);\n            list.addAll(res);\n        }\n        return list;\n    }\n\n    /**\n     * @Description: 查询单实例的idle key\n     * @Author: caoru\n     * @CreateDate: 2018/11/13 16:08\n     */\n    public List<String> findInstanceIdleKeys(long appId, String ip, int port, long idleDays) {\n        List<String> list = new ArrayList<String>();\n        Jedis jedis = getAdminJedis(appId, ip, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n        try {\n            String cursor = ScanParams.SCAN_POINTER_START;\n            ScanParams params = new ScanParams().count(COUNT);\n            do {\n                ScanResult<String> result = jedis.scan(cursor, params);\n                for (String key : result.getResult()) {\n                    Long ideltime = jedis.objectIdletime(key);\n                    if (ideltime != null && ideltime > idleDays * 3600 * 24) {\n                        list.add(key);\n                    }\n                }\n                cursor = result.getCursor();\n            } while (!\"0\".equals(cursor));\n            return list;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return list;\n        } finally {\n            jedis.close();\n        }\n    }\n\n    /**\n     * @Description: 查询应用的idle key\n     * @Author: caoru\n     * @CreateDate: 2018/11/13 16:08\n     */\n    public List<String> findClusterIdleKeys(long appId, long idleDays) {\n        List<String> list = new ArrayList<String>();\n        List<InstanceInfo> allMasterInstance = getAllHealthyInstanceInfo(appId);\n        for (InstanceInfo masterInstance : allMasterInstance) {\n            String ip = masterInstance.getIp();\n            int port = masterInstance.getPort();\n            List<String> res = findInstanceIdleKeys(appId, ip, port, idleDays);\n            list.addAll(res);\n        }\n        return list;\n    }\n\n    /**\n     * @Description: 查询单实例匹配的pattern\n     * @Author: caoru\n     * @CreateDate: 2018/11/13 16:08\n     */\n    public void delInstancePatternKeys(long appId, String ip, int port, String pattern) {\n        Jedis jedis = getAdminJedis(appId, ip, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n        try {\n            String cursor = ScanParams.SCAN_POINTER_START;\n            ScanParams params = new ScanParams().match(pattern).count(COUNT);\n            do {\n                ScanResult<String> result = jedis.scan(cursor, params);\n                for (String key : result.getResult()) {\n                    jedis.del(key);\n                }\n                cursor = result.getCursor();\n            } while (!\"0\".equals(cursor));\n\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n            jedis.close();\n        }\n    }\n\n    /**\n     * @Description: 查询应用的匹配的pattern\n     * @Author: caoru\n     * @CreateDate: 2018/11/13 16:08\n     */\n    public void delClusterPatternKey(long appId, String pattern) {\n        List<InstanceInfo> allMasterInstance = getAllHealthyInstanceInfo(appId);\n        for (InstanceInfo masterInstance : allMasterInstance) {\n            String ip = masterInstance.getIp();\n            int port = masterInstance.getPort();\n            delInstancePatternKeys(appId, ip, port, pattern);\n        }\n    }\n\n    /**\n     * 根据ip和port判断redis实例当前是否有从节点\n     *\n     * @param ip   ip\n     * @param port port\n     * @return 主返回true，从返回false；\n     */\n    public BooleanEnum hasSlaves(long appId, String ip, int port) {\n        Jedis jedis = getAdminJedis(appId, ip, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n        try {\n            String info = jedis.info(\"all\");\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            return hasSlaves(infoMap);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        } finally {\n            jedis.close();\n        }\n    }\n\n    @Override\n    public HostAndPort getMaster(String ip, int port, String password) {\n        JedisPool jedisPool = maintainJedisPool(ip, port, password);\n        Jedis jedis = null;\n        try {\n            jedis = jedisPool.getResource();\n            String info = jedis.info(RedisConstant.Replication.getValue());\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            Map<String, Object> map = infoMap.get(RedisConstant.Replication);\n            if (map == null) {\n                return null;\n            }\n            String masterHost = MapUtils.getString(map, RedisInfoEnum.master_host.getValue(), null);\n            int masterPort = MapUtils.getInteger(map, RedisInfoEnum.master_port.getValue(), 0);\n            if (StringUtils.isNotBlank(masterHost) && masterPort > 0) {\n                return new HostAndPort(masterHost, masterPort);\n            }\n            return null;\n        } catch (Exception e) {\n            logger.error(\"{}:{} getMaster failed {}\", ip, port, e.getMessage(), e);\n            return null;\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n    }\n\n    public HostAndPort getSlave0(String ip, int port, String password) {\n        JedisPool jedisPool = maintainJedisPool(ip, port, password);\n        Jedis jedis = null;\n        try {\n            jedis = jedisPool.getResource();\n            String info = jedis.info(RedisConstant.Replication.getValue());\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            Map<String, Object> map = infoMap.get(RedisConstant.Replication);\n            if (map == null) {\n                return null;\n            }\n            String slaveInfo = MapUtils.getString(map, \"slave0\");\n            String slaveHost = \"\";\n            int slavePort = 0;\n            if (!StringUtil.isBlank(slaveInfo)) {\n                for (String slave0 : slaveInfo.split(\",\")) {\n                    if (slave0.indexOf(\"ip\") > -1) {\n                        slaveHost = slave0.replaceAll(\"ip=\", \"\");\n                    }\n                    if (slave0.indexOf(\"port\") > -1) {\n                        slavePort = Integer.parseInt(slave0.replaceAll(\"port=\", \"\"));\n                    }\n                }\n            }\n            if (StringUtils.isNotBlank(slaveHost) && slavePort > 0) {\n                return new HostAndPort(slaveHost, slavePort);\n            }\n            return null;\n        } catch (Exception e) {\n            logger.error(\"{}:{} getMaster failed {}\", ip, port, e.getMessage(), e);\n            return null;\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n    }\n\n    @Override\n    public boolean isRun(final String ip, final int port, final int retryTimes) {\n        return isRun(ip, port, null, retryTimes);\n    }\n\n    public boolean isRun(final String ip, final int port, final String password, final int retryTimes) {\n        boolean isRun = new IdempotentConfirmer(retryTimes) {\n            private int timeOutFactor = 1;\n\n            @Override\n            public boolean execute() {\n                Jedis jedis = null;\n                try {\n                    jedis = getJedis(ip, port, password);\n                    jedis.getClient().setConnectionTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));\n                    jedis.getClient().setSoTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));\n                    String pong = jedis.ping();\n                    return pong != null && pong.equalsIgnoreCase(\"PONG\");\n                } catch (JedisDataException e) {\n                    String message = e.getMessage();\n                    logger.warn(e.getMessage());\n                    if (StringUtils.isNotBlank(message) && message.startsWith(\"LOADING\")) {\n                        return true;\n                    }\n                    return false;\n                } catch (Exception e) {\n                    logger.warn(\"{}:{} error message is {} \", ip, port, e.getMessage());\n                    return false;\n                } finally {\n                    if (jedis != null) {\n                        jedis.close();\n                    }\n                }\n            }\n        }.run();\n        return isRun;\n    }\n\n    @Override\n    public boolean isRun(final String ip, final int port) {\n        return isRun(ip, port, null);\n    }\n\n    @Override\n    public boolean isRun(final long appId, final String ip, final int port) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        return isRun(ip, port, appDesc.getAppPassword());\n    }\n\n    @Override\n    public boolean isRun(final String ip, final int port, final String password) {\n        boolean isRun = new IdempotentConfirmer() {\n            private int timeOutFactor = 1;\n\n            @Override\n            public boolean execute() {\n                Jedis jedis = null;\n                try {\n                    jedis = getJedis(ip, port, password);\n                    jedis.getClient().setConnectionTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));\n                    jedis.getClient().setSoTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));\n                    String pong = jedis.ping();\n                    return pong != null && pong.equalsIgnoreCase(\"PONG\");\n                } catch (JedisDataException e) {\n                    String message = e.getMessage();\n                    logger.warn(e.getMessage());\n                    if (StringUtils.isNotBlank(message) && message.startsWith(\"LOADING\")) {\n                        return true;\n                    }\n                    return false;\n                } catch (Exception e) {\n                    logger.warn(\"{}:{} error count={} message is {} \", ip, port, timeOutFactor, e.getMessage());\n                    return false;\n                } finally {\n                    if (jedis != null) {\n                        jedis.close();\n                    }\n                }\n            }\n        }.run();\n        return isRun;\n    }\n\n    @Override\n    public boolean shutdown(long appId, String ip, int port) {\n        boolean isRun = isRun(appId, ip, port);\n        if (!isRun) {\n            return true;\n        }\n        final Jedis jedis = getAdminJedis(appId, ip, port);\n        try {\n            //关闭实例节点\n            boolean isShutdown = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    try{\n                        jedis.shutdown();\n                    }catch (JedisException e){\n                        if(e.getMessage().contains(\"OK\")){\n                            return true;\n                        }else {\n                            throw e;\n                        }\n                    }\n                    return true;\n                }\n            }.run();\n            if (!isShutdown) {\n                logger.error(\"{}:{} redis not shutdown!\", ip, port);\n            }\n            return isShutdown;\n        } finally {\n            jedis.close();\n        }\n    }\n\n    @Override\n    public boolean forget(long appId, String ip, int port, String nodeId) {\n        boolean isRun = isRun(appId, ip, port);     //todo: 除了isRun，是否还需要其他判断条件\n        if (!isRun) {\n            return true;\n        }\n\n        boolean isForget = new IdempotentConfirmer() {\n            @Override\n            public boolean execute() {\n                String response = null;\n                Jedis jedis = null;\n                try {\n                    jedis = getAdminJedis(appId, ip, port);\n                    response = jedis.clusterForget(nodeId);\n                } catch (JedisDataException jde) {\n                    //处于handshake状态的节点会抛异常：ERR Unknown node 92e90269c5f86a663a692c5bcf766ecdda80aa9e\n                    logger.error(jde.getMessage(), jde);\n                    response = \"OK\";\n                } catch (Exception e) {\n                    logger.error(\"appId {} instance {}:{}  forget instance {} error!\", appId, ip, port, nodeId, e);\n                } finally {\n                    if (jedis != null) {\n                        jedis.close();\n                    }\n                }\n                return response != null && response.equalsIgnoreCase(\"OK\");\n            }\n        }.run();\n        return isForget;\n    }\n\n    @Override\n    public boolean shutdown(String ip, int port) {\n        boolean isRun = isRun(ip, port);\n        if (!isRun) {\n            return true;\n        }\n        final Jedis jedis = getJedis(ip, port);\n        try {\n            //关闭实例节点\n            boolean isShutdown = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    jedis.shutdown();\n                    return true;\n                }\n            }.run();\n            if (!isShutdown) {\n                logger.error(\"{}:{} redis not shutdown!\", ip, port);\n            }\n            return isShutdown;\n        } finally {\n            jedis.close();\n        }\n    }\n\n    /**\n     * 判断节点是否正常关闭，默认为关闭成功，仅在查到配置文件句柄一直未释放时，认为关闭失败\n     * @param instanceInfo\n     * @return\n     */\n    @Override\n    public boolean checkShutdownSuccess(InstanceInfo instanceInfo){\n        if(instanceInfo == null){\n            return false;\n        }\n        //关闭节点后，判断配置文件句柄是否释放\n        boolean existeFlag = true;\n        int tryTimes = 3;\n        long sleepTime = 2L;\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        StringBuilder command = new StringBuilder();\n        command.append(\"ps -ef | grep redis | grep redis-server | grep :\").append(port).append(\"  | grep -v \\\"grep\\\"\");\n        while(tryTimes-- > 0){\n            try{\n                String execute = SSHUtil.execute(host, command.toString());\n                if(StringUtils.isEmpty(execute)){\n                    existeFlag = true;\n                    break;\n                }else{\n                    existeFlag = false;\n                }\n                logger.info(String.format(\"check Instance shutdown not success, will one more time, appId:%s, instance:%s, command:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), command));\n                TimeUnit.SECONDS.sleep(sleepTime);\n            }catch (Exception e){\n                logger.error(String.format(\"check Instance shutdown error, appId:%s, instance:%s, command:%s, error: \", instanceInfo.getAppId(), instanceInfo.getHostPort(), command), e);\n            }\n        }\n        return existeFlag;\n    }\n\n    @Override\n    public String getClusterMyId(long appId, String ip, int port) {\n        final Jedis jedis = getAdminJedis(appId, ip, port);\n        try {\n            return jedis.clusterMyId();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return \"\";\n        } finally {\n            jedis.close();\n        }\n    }\n\n    @Override\n    public String getClusterNodes(long appId, String ip, int port) {\n        final Jedis jedis = getAdminJedis(appId, ip, port);\n        try {\n            return jedis.clusterNodes();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return \"\";\n        } finally {\n            jedis.close();\n        }\n    }\n\n    /**\n     * 返回当前实例的一些关键指标\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @param infoMap\n     * @return\n     */\n    public InstanceStats getInstanceStats(long appId, String ip, int port,\n                                          Map<Object, Map<String, Object>> infoMap) {\n        if (infoMap == null) {\n            return null;\n        }\n        // 查询最大内存限制\n        Long maxMemory = this.getRedisMaxMemory(appId, ip, port);\n        /**\n         * 将实例的一些关键指标返回\n         */\n        InstanceStats instanceStats = new InstanceStats();\n        instanceStats.setAppId(appId);\n        InstanceInfo curInst = instanceDao.getInstByIpAndPort(ip, port);\n        if (curInst != null) {\n            instanceStats.setHostId(curInst.getHostId());\n            instanceStats.setInstId(curInst.getId());\n        } else {\n            logger.error(\"redis={}:{} not found\", ip, port);\n            return null;\n        }\n        instanceStats.setIp(ip);\n        instanceStats.setPort(port);\n        if (maxMemory != null) {\n            instanceStats.setMaxMemory(maxMemory);\n        }\n        instanceStats.setUsedMemory(\n                MapUtils.getLongValue(infoMap.get(RedisConstant.Memory), RedisInfoEnum.used_memory.getValue(), 0));\n        instanceStats.setHits(\n                MapUtils.getLongValue(infoMap.get(RedisConstant.Stats), RedisInfoEnum.keyspace_hits.getValue(), 0));\n        instanceStats.setMisses(\n                MapUtils.getLongValue(infoMap.get(RedisConstant.Stats), RedisInfoEnum.keyspace_misses.getValue(), 0));\n        instanceStats.setCurrConnections(\n                MapUtils.getIntValue(infoMap.get(RedisConstant.Clients), RedisInfoEnum.connected_clients.getValue(),\n                        0));\n        instanceStats.setCurrItems(getObjectSize(infoMap));\n        instanceStats.setRole((byte) 1);\n        if (MapUtils.getString(infoMap.get(RedisConstant.Replication), RedisInfoEnum.role.getValue()).equals(\"slave\")) {\n            instanceStats.setRole((byte) 2);\n        }\n        instanceStats.setModifyTime(new Timestamp(System.currentTimeMillis()));\n        instanceStats.setMemFragmentationRatio(MapUtils.getDoubleValue(infoMap.get(RedisConstant.Memory),\n                RedisInfoEnum.mem_fragmentation_ratio.getValue(), 0.0));\n        instanceStats.setAofDelayedFsync(\n                MapUtils.getIntValue(infoMap.get(RedisConstant.Persistence), RedisInfoEnum.aof_delayed_fsync.getValue(),\n                        0));\n        return instanceStats;\n    }\n\n    @Override\n    public Long getRedisMaxMemory(final long appId, final String ip, final int port) {\n        final String key = \"maxmemory\";\n        final Map<String, Long> resultMap = new HashMap<String, Long>();\n        boolean isSuccess = new IdempotentConfirmer() {\n            private int timeOutFactor = 1;\n\n            @Override\n            public boolean execute() {\n                Jedis jedis = null;\n                try {\n                    jedis = getJedis(appId, ip, port);\n                    jedis.getClient().setConnectionTimeout(REDIS_DEFAULT_TIME * (timeOutFactor++));\n                    jedis.getClient().setSoTimeout(REDIS_DEFAULT_TIME * (timeOutFactor++));\n                    List<String> maxMemoryList = jedis.configGet(key); // 返回结果：list中是2个字符串，如：\"maxmemory\",\n                    // \"4096000000\"\n                    if (maxMemoryList != null && maxMemoryList.size() >= 2) {\n                        resultMap.put(key, Long.valueOf(maxMemoryList.get(1)));\n                    }\n                    return MapUtils.isNotEmpty(resultMap);\n                } catch (Exception e) {\n                    logger.warn(\"{}:{} errorMsg: {}\", ip, port, e.getMessage());\n                    return false;\n                } finally {\n                    if (jedis != null) {\n                        jedis.close();\n                    }\n                }\n            }\n        }.run();\n        if (isSuccess) {\n            return MapUtils.getLong(resultMap, key);\n        } else {\n            logger.error(\"{}:{} getMaxMemory failed!\", ip, port);\n            return null;\n        }\n    }\n\n    @Override\n    public String executeCommand(AppDesc appDesc, String command, String userName) {\n        //非测试应用只能执行白名单里面的命令\n        if (AppDescEnum.AppTest.NOT_TEST.getValue() == appDesc.getIsTest()) {\n            if (!RedisReadOnlyCommandEnum.contains(command)) {\n                return \"online app only support read-only and safe command\";\n            }\n        }\n        int type = appDesc.getType();\n        long appId = appDesc.getAppId();\n        String password = appDesc.getAppPassword();\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            JedisSentinelPool jedisSentinelPool = getJedisSentinelPool(appDesc);\n            if (jedisSentinelPool == null) {\n                return \"sentinel can not execute \";\n            }\n            Jedis jedis = null;\n            try {\n                jedis = jedisSentinelPool.getResource();\n                String host = jedis.getClient().getHost();\n                int port = jedis.getClient().getPort();\n                return executeCommand(appId, host, port, command);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                return \"运行出错:\" + e.getMessage();\n            } finally {\n                if (jedis != null)\n                    jedis.close();\n                jedisSentinelPool.destroy();\n            }\n        } else if (type == ConstUtils.CACHE_REDIS_STANDALONE) {\n            List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n            if (instanceList == null || instanceList.isEmpty()) {\n                return \"应用没有运行的实例\";\n            }\n            String host = null;\n            int port = 0;\n            for (InstanceInfo instanceInfo : instanceList) {\n                host = instanceInfo.getIp();\n                port = instanceInfo.getPort();\n                break;\n            }\n            try {\n                return executeCommand(appId, host, port, command);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                return \"运行出错:\" + e.getMessage();\n            }\n        } else if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n            List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n            if (instanceList == null || instanceList.isEmpty()) {\n                return \"应用没有运行的实例\";\n            }\n            Set<HostAndPort> clusterHosts = new LinkedHashSet<HostAndPort>();\n            for (InstanceInfo instance : instanceList) {\n                if (instance != null && instance.isOnline()) {\n                    clusterHosts.add(new HostAndPort(instance.getIp(), instance.getPort()));\n                }\n            }\n            if (clusterHosts.isEmpty()) {\n                return \"no run instance\";\n            }\n            String commandKey = getCommandKey(command);\n            if(StringUtils.isEmpty(commandKey)){\n                logger.error(String.format(\"executeCommand with empty commandKey, appDesc is : %s, command is: %s, user is : %s\", appDesc.getAppId(), command, userName));\n            }\n            for (HostAndPort hostAndPort : clusterHosts) {\n                HostAndPort rightHostAndPort = null;\n                if(commandKey != null){\n                    rightHostAndPort = getClusterRightHostAndPort(hostAndPort.getHost(), hostAndPort.getPort(),\n                            password, command, commandKey);\n                }else{\n                    rightHostAndPort = hostAndPort;\n                }\n                if (rightHostAndPort != null) {\n                    try {\n                        return executeCommand(appId, rightHostAndPort.getHost(), rightHostAndPort.getPort(), command);\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                        return \"运行出错:\" + e.getMessage();\n                    }\n                }\n            }\n\n        }\n        return \"不支持应用类型\";\n    }\n\n    @Override\n    public Object executeAdminCommand(AppDesc appDesc, ProtocolCommand command, String... args) {\n        int type = appDesc.getType();\n        long appId = appDesc.getAppId();\n        String password = appDesc.getAppPassword();\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n            if (instanceList == null || instanceList.isEmpty()) {\n                return \"应用没有运行的实例\";\n            }\n            String host = null;\n            int port = 0;\n            for (InstanceInfo instanceInfo : instanceList) {\n                if(instanceInfo.getType() == ConstUtils.CACHE_REDIS_SENTINEL){\n                    continue;\n                }\n                if(instanceInfo.isOffline()){\n                    continue;\n                }\n                host = instanceInfo.getIp();\n                port = instanceInfo.getPort();\n                BooleanEnum isMaster = this.isMaster(appId, host, port);\n                if(isMaster.equals(BooleanEnum.TRUE)){\n                    break;\n                }else{\n                    host = null;\n                    port = 0;\n                }\n            }\n            Jedis jedis = null;\n            try {\n                jedis = this.getAdminJedis(appId, host, port);\n                return executeAdminRedisCommandByJedis(jedis, command, args);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                return \"运行出错:\" + e.getMessage();\n            } finally {\n                if (jedis != null){\n                    jedis.close();\n                }\n            }\n        } else if (type == ConstUtils.CACHE_REDIS_STANDALONE) {\n            List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n            if (instanceList == null || instanceList.isEmpty()) {\n                return \"应用没有运行的实例\";\n            }\n            String host = null;\n            int port = 0;\n            for (InstanceInfo instanceInfo : instanceList) {\n                host = instanceInfo.getIp();\n                port = instanceInfo.getPort();\n                BooleanEnum isMaster = this.isMaster(appId, host, port);\n                if(isMaster.equals(BooleanEnum.TRUE)){\n                    break;\n                }else{\n                    host = null;\n                    port = 0;\n                }\n            }\n            Jedis jedis = null;\n            try {\n                jedis = this.getAdminJedis(appId, host, port);\n                return executeAdminRedisCommandByJedis(jedis, command, args);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                return \"运行出错:\" + e.getMessage();\n            } finally {\n                if (jedis != null){\n                    jedis.close();\n                }\n            }\n        } else if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n            List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n            if (instanceList == null || instanceList.isEmpty()) {\n                return \"应用没有运行的实例\";\n            }\n            Set<HostAndPort> clusterHosts = new LinkedHashSet<HostAndPort>();\n            for (InstanceInfo instance : instanceList) {\n                if (instance != null && instance.isOnline()) {\n                    clusterHosts.add(new HostAndPort(instance.getIp(), instance.getPort()));\n                }\n            }\n            if (clusterHosts.isEmpty()) {\n                return \"no run instance\";\n            }\n            String commandKey = null;\n            if(args != null && args.length > 0){\n                commandKey = args[0];\n            }\n            HostAndPort rightHostAndPort = null;\n            for (HostAndPort hostAndPort : clusterHosts) {\n                if(commandKey != null){\n                    rightHostAndPort = getClusterRightHostAndPort(hostAndPort.getHost(), hostAndPort.getPort(),\n                            password, command.toString(), commandKey);\n                    if(rightHostAndPort != null){\n                        break;\n                    }\n                }else{\n                    BooleanEnum isMaster = this.isMaster(appId, hostAndPort.getHost(), hostAndPort.getPort());\n                    if(isMaster.equals(BooleanEnum.TRUE)){\n                        rightHostAndPort = hostAndPort;\n                        break;\n                    }\n                }\n\n            }\n            if (rightHostAndPort != null) {\n                Jedis jedis = null;\n                try {\n                    jedis = this.getAdminJedis(appId, rightHostAndPort.getHost(), rightHostAndPort.getPort());\n                    return executeAdminRedisCommandByJedis(jedis, command, args);\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return \"运行出错:\" + e.getMessage();\n                } finally {\n                    if (jedis != null){\n                        jedis.close();\n                    }\n                }\n            }\n        }\n        return \"不支持应用类型\";\n    }\n\n    /**\n     * 获取key对应的节点\n     *\n     * @param host\n     * @param port\n     * @param password\n     * @param command\n     * @param key\n     * @return\n     */\n    private HostAndPort getClusterRightHostAndPort(String host, int port, String password, String command, String key) {\n        Jedis jedis = null;\n        try {\n            jedis = getJedis(host, port, password);\n            jedis.type(key);\n            return new HostAndPort(host, port);\n        } catch (JedisMovedDataException e) {\n            return e.getTargetNode();\n        } catch (JedisAskDataException e) {\n            return e.getTargetNode();\n        } catch (Exception e) {\n            logger.error(\"command {} is error\", command, e.getMessage(), e);\n            return null;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    private String getCommandKey(String command) {\n        String[] array = StringUtils.trim(command).split(\"\\\\s+\");\n        if (array.length > 1) {\n            return array[1];\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public String executeCommand(long appId, String host, int port, String command) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return \"not exist appId\";\n        }\n        //非测试应用只能执行白名单里面的命令\n        if (AppDescEnum.AppTest.NOT_TEST.getValue() == appDesc.getIsTest()) {\n            if (!RedisReadOnlyCommandEnum.contains(command)) {\n                return \"online app only support read-only and safe command \";\n            }\n        }\n        String password = appDesc.getAppPassword();\n        String shell = RedisProtocol.getExecuteCommandShell(host, port, password, command);\n        //记录客户端发送日志\n        logger.warn(\"executeRedisShell={}\", shell);\n        return machineCenter.executeShell(host, shell);\n    }\n\n    @Override\n    public String executeAdminCommand(long appId, String host, int port, String command, Integer timeout) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return \"not exist appId\";\n        }\n        String password = appDesc.getAppPassword();\n        String shell = RedisProtocol.getExecuteAdminCommandShell(host, port, password, command);\n        //记录客户端发送日志\n        logger.warn(\"executeRedisShell={}\", shell);\n        return machineCenter.executeShell(host, shell, timeout);\n    }\n\n    @Override\n    public Object executeAdminRedisCommandByJedis(Jedis jedis, ProtocolCommand command, String... args) {\n        Object o = jedis.sendCommand(command, args);\n        return o;\n    }\n\n\n    @Override\n    public JedisSentinelPool getJedisSentinelPool(AppDesc appDesc) {\n        if (appDesc == null) {\n            logger.error(\"appDesc is null\");\n            return null;\n        }\n        if (appDesc.getType() != ConstUtils.CACHE_REDIS_SENTINEL) {\n            logger.error(\"type={} is not sentinel\", appDesc.getType());\n            return null;\n        }\n        long appId = appDesc.getAppId();\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        instanceInfos = instanceInfos.stream().filter(instanceInfo -> instanceInfo.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()).collect(Collectors.toList());\n\n        String masterName = null;\n        for (Iterator<InstanceInfo> i = instanceInfos.iterator(); i.hasNext(); ) {\n            InstanceInfo instanceInfo = i.next();\n            if (instanceInfo.getType() != ConstUtils.CACHE_REDIS_SENTINEL) {\n                i.remove();\n                continue;\n            }\n            if (masterName == null && StringUtils.isNotBlank(instanceInfo.getCmd())) {\n                masterName = instanceInfo.getCmd();\n            }\n        }\n        Set<String> sentinels = new HashSet<String>();\n        for (InstanceInfo instanceInfo : instanceInfos) {\n            sentinels.add(instanceInfo.getIp() + \":\" + instanceInfo.getPort());\n        }\n        JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels);\n        return jedisSentinelPool;\n    }\n\n    @Override\n    public Map<String, String> getRedisConfigList(int instanceId) {\n        if (instanceId <= 0) {\n            return Collections.emptyMap();\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        if (instanceInfo == null) {\n            return Collections.emptyMap();\n        }\n        if (TypeUtil.isRedisType(instanceInfo.getType())) {\n            Jedis jedis = null;\n            try {\n                jedis = getJedis(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(),\n                        REDIS_DEFAULT_TIME * 2, REDIS_DEFAULT_TIME * 2);\n                List<String> configs = jedis.configGet(\"*\");\n                Map<String, String> configMap = new LinkedHashMap<String, String>();\n                for (int i = 0; i < configs.size(); i += 2) {\n                    if (i < configs.size()) {\n                        String key = configs.get(i);\n                        String value = configs.get(i + 1);\n                        if (StringUtils.isBlank(value)) {\n                            continue;\n                        }\n                        configMap.put(key, value);\n                    }\n                }\n                return configMap;\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            } finally {\n                if (jedis != null) {\n                    jedis.close();\n                }\n            }\n        }\n\n        return Collections.emptyMap();\n    }\n\n    @Override\n    public List<String> getRedisCommand(int instanceId) {\n        List<String> commandList = new ArrayList<>();\n        if (instanceId <= 0) {\n            return commandList;\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        if (instanceInfo == null) {\n            return commandList;\n        }\n        if (TypeUtil.isRedisType(instanceInfo.getType())) {\n            Jedis jedis = null;\n            try {\n                if(TypeUtil.isRedisDataType(instanceInfo.getType())){\n                    jedis = getAdminJedis(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(),\n                            REDIS_DEFAULT_TIME * 2, REDIS_DEFAULT_TIME * 2);\n                }else{\n                    jedis = getJedis(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(),\n                            REDIS_DEFAULT_TIME * 2, REDIS_DEFAULT_TIME * 2);\n                }\n                Client client = jedis.getClient();\n                client.sendCommand(Command.COMMAND);\n                List<Object> nestedMultiBulkReply = client.getObjectMultiBulkReply();\n                for (Object obj : nestedMultiBulkReply) {\n                    List<Object> properties = (List<Object>) obj;\n                    commandList.add(new String((byte[])properties.get(0)));\n                }\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            } finally {\n                if (jedis != null) {\n                    jedis.close();\n                }\n            }\n        }\n        return commandList;\n    }\n\n    /**\n     * 获取重命名命令列表\n     * @param instanceId\n     * @return\n     */\n    @Override\n    public List<Pair<String, String>> getConfigsInConfigFile(int instanceId, String configName){\n        List<Pair<String, String>> configs = new ArrayList<>();\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        if (instanceInfo == null) {\n            return configs;\n        }\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        StringBuilder command = new StringBuilder();\n        String configFileName = null;\n        if (TypeUtil.isRedisCluster(instanceInfo.getType())) {\n            configFileName = RedisProtocol.getConfig(port, true);\n        } else {\n            configFileName = RedisProtocol.getConfig(port, false);\n        }\n        String confDir = machineCenter.getMachineRelativeDir(host, DirEnum.CONF_DIR.getValue());\n        String remotePath = confDir + configFileName;\n        command.append(\"cat \").append(remotePath);\n        if(StringUtils.isNotEmpty(configName)){\n            command.append(\" | grep '\").append(configName).append(\"'\");\n        }\n        command.append(\" | grep -v '^#'\");\n        try{\n            String sshRst = SSHUtil.execute(host, command.toString());\n            if(StringUtils.isNotEmpty(sshRst)){\n                String[] configStrs = sshRst.split(System.lineSeparator());\n                if(configStrs != null && configStrs.length > 0){\n                    for (int i = 0; i < configStrs.length; i++) {\n                        String configStr = configStrs[i];\n                        if(configStr.contains(\" \")){\n                            int index = configStr.indexOf(\" \");\n                            configs.add(Pair.of(configStr.substring(0, index), configStr.substring(index + 1, configStr.length())));\n                        }\n                    }\n                }\n            }\n            logger.info(String.format(\"get Instance config from file success, appId:%s, instance:%s, command:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), command));\n        }catch (Exception e){\n            logger.error(String.format(\"get Instance config from file error, appId:%s, instance:%s, command:%s, error: \", instanceInfo.getAppId(), instanceInfo.getHostPort(), command), e);\n        }\n        return configs;\n    }\n\n    @Override\n    public List<RedisSlowLog> getRedisSlowLogs(int instanceId, int maxCount) {\n        if (instanceId <= 0) {\n            return Collections.emptyList();\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        if (instanceInfo == null) {\n            return Collections.emptyList();\n        }\n        if (TypeUtil.isRedisType(instanceInfo.getType())) {\n            return getRedisSlowLogs(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), maxCount);\n        }\n        return Collections.emptyList();\n    }\n\n    private List<RedisSlowLog> getRedisSlowLogs(long appId, String host, int port, int maxCount) {\n        Jedis jedis = null;\n        try {\n            jedis = getAdminJedis(appId, host, port, REDIS_DEFAULT_TIME * 2, REDIS_DEFAULT_TIME * 2);\n            List<RedisSlowLog> resultList = new ArrayList<RedisSlowLog>();\n            List<Slowlog> slowlogs = null;\n            if (maxCount > 0) {\n                slowlogs = jedis.slowlogGet(maxCount);\n            } else {\n                slowlogs = jedis.slowlogGet();\n            }\n            if (slowlogs != null && slowlogs.size() > 0) {\n                for (Slowlog sl : slowlogs) {\n                    RedisSlowLog rs = new RedisSlowLog();\n                    rs.setId(sl.getId());\n                    rs.setExecutionTime(sl.getExecutionTime());\n                    long time = sl.getTimeStamp() * 1000L;\n                    rs.setDate(new Date(time));\n                    rs.setTimeStamp(DateUtil.formatYYYYMMddHHMMSS(new Date(time)));\n                    rs.setCommand(StringUtils.join(sl.getArgs(), \" \"));\n                    resultList.add(rs);\n                }\n            }\n            return resultList;\n        } catch (Exception e) {\n            logger.error(String.format(\"get slowLog error, appId:%s host:%s port:%s, error:%s\", appId, host, port, e.getMessage()), e);\n            return Collections.emptyList();\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    @Override\n    public boolean configRewrite(final long appId, final String host, final int port) {\n        return new IdempotentConfirmer() {\n            @Override\n            public boolean execute() {\n                Jedis jedis = getAdminJedis(appId, host, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n                try {\n                    String response = jedis.configRewrite();\n                    return response != null && response.equalsIgnoreCase(\"OK\");\n                } finally {\n                    jedis.close();\n                }\n            }\n        }.run();\n    }\n\n    @Override\n    public boolean configRewrite(Jedis jedis) {\n        return new IdempotentConfirmer() {\n            @Override\n            public boolean execute() {\n                String response = jedis.configRewrite();\n                return response != null && response.equalsIgnoreCase(\"OK\");\n            }\n        }.run();\n    }\n\n    @Override\n    public boolean cleanAppData(AppDesc appDesc, AppUser appUser) {\n        if (appDesc == null) {\n            return false;\n        }\n\n        long appId = appDesc.getAppId();\n\n        // 线上应用不能清理数据\n        if (AppDescEnum.AppTest.IS_TEST.getValue() != appDesc.getIsTest()) {\n            logger.error(\"appId {} profile must be test\", appId);\n            return false;\n        }\n\n        // 必须是redis应用\n        if (!TypeUtil.isRedisType(appDesc.getType())) {\n            logger.error(\"appId {} type must be redis\", appId);\n            return false;\n        }\n\n        // 实例验证\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n        if (CollectionUtils.isEmpty(instanceList)) {\n            logger.error(\"appId {} instanceList is empty\", appId);\n            return false;\n        }\n\n        // 开始清除\n        for (InstanceInfo instance : instanceList) {\n            if (instance.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                continue;\n            }\n            String host = instance.getIp();\n            int port = instance.getPort();\n            // master + 非sentinel节点\n            BooleanEnum isMater = isMaster(appId, host, port);\n            if (isMater == BooleanEnum.TRUE && !TypeUtil.isRedisSentinel(instance.getType())) {\n                //异步线程处理\n                AsyncThreadPoolFactory.DEFAULT_ASYNC_THREAD_POOL.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        Jedis jedis = getAdminJedis(appId, host, port);\n                        jedis.getClient().setConnectionTimeout(REDIS_DEFAULT_TIME);\n                        jedis.getClient().setSoTimeout(60000);\n                        try {\n                            logger.warn(\"{}:{} start clear data\", host, port);\n                            long start = System.currentTimeMillis();\n                            String result = jedis.flushAll();\n                            logger.warn(\"{}:{} finish clear data :{}, cost time:{} ms\", host, port, result,\n                                    (System.currentTimeMillis() - start));\n                        } catch (Exception e) {\n                            logger.error(\"clear redis: \" + e.getMessage(), e);\n                        } finally {\n                            jedis.close();\n                        }\n                    }\n                });\n            }\n        }\n\n        //记录日志\n        AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, 0L, AppAuditLogTypeEnum.APP_CLEAN_DATA);\n        appAuditLogDao.save(appAuditLog);\n\n        return true;\n    }\n\n    @Override\n    public boolean isSingleClusterNode(long appId, String host, int port) {\n        final Jedis jedis = getJedis(appId, host, port);\n        try {\n            String clusterNodes = jedis.clusterNodes();\n            if (StringUtils.isBlank(clusterNodes)) {\n                throw new RuntimeException(host + \":\" + port + \"clusterNodes is null\");\n            }\n            String[] nodeInfos = clusterNodes.split(\"\\n\");\n            if (nodeInfos.length == 1) {\n                return true;\n            }\n            return false;\n        } finally {\n            jedis.close();\n        }\n    }\n\n    @Override\n    public List<String> getClientList(int instanceId) {\n        if (instanceId <= 0) {\n            return Collections.emptyList();\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        if (instanceInfo == null) {\n            return Collections.emptyList();\n        }\n        if (TypeUtil.isRedisType(instanceInfo.getType())) {\n            Jedis jedis = null;\n            try {\n                jedis = getJedis(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(),\n                        REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n                jedis.clientList();\n                List<String> resultList = new ArrayList<String>();\n                String clientList = jedis.clientList();\n                if (StringUtils.isNotBlank(clientList)) {\n                    String[] array = clientList.split(\"\\n\");\n                    resultList.addAll(Arrays.asList(array));\n                }\n                return resultList;\n            } catch (Exception e) {\n                logger.error(\"instance getClientList error, instance:[{}:{}] error:{}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), e.getMessage());\n                logger.error(e.getMessage(), e);\n            } finally {\n                if (jedis != null) {\n                    jedis.close();\n                }\n            }\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List<Map<String, Object>> formatClientList(List<String> clientList) {\n        List<Map<String, Object>> clientMapList = clientList.stream().map(clientInfo -> parseClientInfo(clientInfo)).collect(Collectors.toList());\n        Map<String, List<Map<String, Object>>> result = new HashMap<>();\n        clientMapList.stream().forEach(clientMap -> {\n            String addr = MapUtils.getString(clientMap, \"addr\");\n            List<Map<String, Object>> list = result.get(addr);\n            if (CollectionUtils.isEmpty(list)) {\n                list = new ArrayList<>();\n                result.put(addr, list);\n            }\n            list.add(clientMap);\n        });\n        List<Map<String, Object>> finalResult = getClientInfoMap(result);\n\n        return finalResult;\n    }\n\n    @Override\n    public List<Map<String, Object>> getAppClientList(long appId, int condition) {\n        Map<String, Map<Integer, Object>> finalResult = new HashMap<>();\n\n        List<InstanceInfo> instanceInfoList = appService.getAppOnlineInstanceInfo(appId);\n        instanceInfoList.stream().forEach(instanceInfo -> {\n            int instanceId = instanceInfo.getId();\n            List<Map<String, Object>> instanceClientList = formatClientList(getClientList(instanceId));\n\n            for (Map<String, Object> map : instanceClientList) {\n                String addr = MapUtils.getString(map, \"addr\");\n                Map<Integer, Object> result = MapUtils.getMap(finalResult, addr);\n                if (MapUtils.isEmpty(result)) {\n                    result = new HashMap<>();\n                    finalResult.put(addr, result);\n                }\n                result.put(instanceId, map);\n            }\n\n        });\n\n        return formatAppClientList(finalResult, appId, condition);\n    }\n\n\n    private List<Map<String, Object>> formatAppClientList(Map<String, Map<Integer, Object>> addrClientListMap, long appId, int condition) {\n        List<Map<String, Object>> finalResult = new ArrayList<>();\n        List<String> ccWebClientList = webClientComponent.getWebClientIps();\n        List<String> redisClientList = appService.getAppOnlineInstanceInfo(appId).stream().map(instanceInfo -> instanceInfo.getIp()).collect(Collectors.toList());\n\n        for (String addr : addrClientListMap.keySet()) {\n            Map<Integer, Object> instanceClientListMap = addrClientListMap.get(addr);\n            Map<String, Object> map = new HashMap<>();\n            map.put(\"addr\", addr);\n\n            Set<String> flags = new HashSet<>();\n            int size = 0;\n            for (Integer instanceId : instanceClientListMap.keySet()) {\n                Map<String, Object> clientInfo = (HashMap) instanceClientListMap.get(instanceId);\n                Set<String> instanceFlags = (HashSet) clientInfo.get(\"clientTypeSet\");\n                flags.addAll(instanceFlags);\n                int count = MapUtils.getIntValue(clientInfo, \"count\");\n                size += count;\n            }\n            map.put(\"flags\", flags);\n            map.put(\"size\", size);\n\n            map.put(\"instanceClientStats\", instanceClientListMap);\n\n            switch (condition) {\n                case 0:\n                    if (!ccWebClientList.contains(addr) && !redisClientList.contains(addr)) {\n                        finalResult.add(map);\n                    }\n                    break;\n                case 1:\n                    if (ccWebClientList.contains(addr)) {\n                        finalResult.add(map);\n                    }\n                    break;\n                case 2:\n                    if (redisClientList.contains(addr)) {\n                        finalResult.add(map);\n                    }\n                    break;\n                case 3:\n                    finalResult.add(map);\n                    break;\n            }\n        }\n\n        return finalResult;\n    }\n\n    private List<Map<String, Object>> getClientInfoMap(Map<String, List<Map<String, Object>>> map) {\n        List<Map<String, Object>> finalResult = new ArrayList<>();\n\n        for (String addr : map.keySet()) {\n            List<Map<String, Object>> clients = map.get(addr);\n            Set<String> flagsSet = clients.stream().map(clientInfo ->\n                    ClientTypeEnum.Method.getDesc(MapUtils.getString(clientInfo, \"flags\", \"\")))\n                    .collect(Collectors.toSet());\n\n            Map<String, Object> clientMap = new HashMap<>();\n            clientMap.put(\"addr\", addr);\n            clientMap.put(\"clientTypeSet\", flagsSet);\n            clientMap.put(\"count\", clients.size());\n            clientMap.put(\"clientInfoList\", clients);\n\n            finalResult.add(clientMap);\n        }\n\n        return finalResult;\n    }\n\n    private Map<String, Object> parseClientInfo(String clientInfo) {\n        Map<String, Object> clientInfoMap = new HashMap<>();\n        String[] tmpArray1 = clientInfo.split(\" \");\n        if (tmpArray1 != null) {\n            for (String tmp : tmpArray1) {\n                String[] tmpArray2 = tmp.split(\"=|:\");\n                if (tmpArray2.length == 3) {\n                    clientInfoMap.put(tmpArray2[0], tmpArray2[1]);\n                    clientInfoMap.put(\"port\", tmpArray2[2]);\n                } else if (tmpArray2.length == 2) {\n                    clientInfoMap.put(tmpArray2[0], tmpArray2[1]);\n                }\n            }\n        }\n        return clientInfoMap;\n    }\n\n    @Override\n    public Map<String, String> getClusterLossSlots(long appId) {\n        // 1.从应用中获取一个健康的主节点\n        InstanceInfo sourceMasterInstance = getHealthyInstanceInfo(appId);\n        if (sourceMasterInstance == null) {\n            return Collections.emptyMap();\n        }\n        // 2. 获取所有slot和节点的对应关系\n        Map<Integer, String> slotHostPortMap = getSlotsHostPortMap(appId, sourceMasterInstance.getIp(),\n                sourceMasterInstance.getPort());\n        // 3. 获取集群中失联的slot\n        List<Integer> lossSlotList = getClusterLossSlots(appId, sourceMasterInstance.getIp(),\n                sourceMasterInstance.getPort());\n        // 3.1 将失联的slot列表组装成Map<String host:port,List<Integer> lossSlotList>\n        Map<String, List<Integer>> hostPortSlotMap = new HashMap<String, List<Integer>>();\n        if (CollectionUtils.isNotEmpty(lossSlotList)) {\n            for (Integer lossSlot : lossSlotList) {\n                String key = slotHostPortMap.get(lossSlot);\n                if (hostPortSlotMap.containsKey(key)) {\n                    hostPortSlotMap.get(key).add(lossSlot);\n                } else {\n                    List<Integer> list = new ArrayList<Integer>();\n                    list.add(lossSlot);\n                    hostPortSlotMap.put(key, list);\n                }\n            }\n        }\n        // 3.2 hostPortSlotMap组装成Map<String host:port,String startSlot-endSlot>\n        Map<String, String> slotSegmentsMap = new HashMap<String, String>();\n        for (Entry<String, List<Integer>> entry : hostPortSlotMap.entrySet()) {\n            List<Integer> list = entry.getValue();\n            List<String> slotSegments = new ArrayList<String>();\n            int min = list.get(0);\n            int max = min;\n            for (int i = 1; i < list.size(); i++) {\n                int temp = list.get(i);\n                if (temp == max + 1) {\n                    max = temp;\n                } else {\n                    slotSegments.add(String.valueOf(min) + \"-\" + String.valueOf(max));\n                    min = temp;\n                    max = temp;\n                }\n            }\n            slotSegments.add(String.valueOf(min) + \"-\" + String.valueOf(max));\n            slotSegmentsMap.put(entry.getKey(), slotSegments.toString());\n        }\n        return slotSegmentsMap;\n    }\n\n    /**\n     * 从一个应用中获取一个健康的主节点\n     *\n     * @param appId\n     * @return\n     */\n    public InstanceInfo getHealthyInstanceInfo(long appId) {\n        InstanceInfo sourceMasterInstance = null;\n        List<InstanceInfo> appInstanceInfoList = instanceDao.getInstListByAppId(appId);\n        if (CollectionUtils.isEmpty(appInstanceInfoList)) {\n            logger.error(\"appId {} has not instances\", appId);\n            return null;\n        }\n        for (InstanceInfo instanceInfo : appInstanceInfoList) {\n            int instanceType = instanceInfo.getType();\n            if (!TypeUtil.isRedisCluster(instanceType)) {\n                continue;\n            }\n            final String host = instanceInfo.getIp();\n            final int port = instanceInfo.getPort();\n            if (instanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                continue;\n            }\n            boolean isRun = isRun(appId, host, port);\n            if (!isRun) {\n                logger.warn(\"{}:{} is not run\", host, port);\n                continue;\n            }\n            BooleanEnum isMaster = isMaster(appId, host, port);\n            if (isMaster != BooleanEnum.TRUE) {\n                logger.warn(\"{}:{} is not master\", host, port);\n                continue;\n            }\n            sourceMasterInstance = instanceInfo;\n            break;\n        }\n        return sourceMasterInstance;\n    }\n\n    @Override\n    public Map<String, String> getClusterLossSlots(long appId, InstanceInfo instanceInfo) {\n        // 2. 获取所有slot和节点的对应关系\n        Map<Integer, String> slotHostPortMap = getSlotsHostPortMap(appId, instanceInfo.getIp(),\n                instanceInfo.getPort());\n        // 3. 获取集群中失联的slot\n        List<Integer> lossSlotList = getClusterLossSlots(appId, instanceInfo.getIp(),\n                instanceInfo.getPort());\n        // 3.1 将失联的slot列表组装成Map<String host:port,List<Integer> lossSlotList>\n        Map<String, List<Integer>> hostPortSlotMap = new HashMap<String, List<Integer>>();\n        if (CollectionUtils.isNotEmpty(lossSlotList)) {\n            for (Integer lossSlot : lossSlotList) {\n                String key = slotHostPortMap.get(lossSlot);\n                if (hostPortSlotMap.containsKey(key)) {\n                    hostPortSlotMap.get(key).add(lossSlot);\n                } else {\n                    List<Integer> list = new ArrayList<Integer>();\n                    list.add(lossSlot);\n                    hostPortSlotMap.put(key, list);\n                }\n            }\n        }\n        // 3.2 hostPortSlotMap组装成Map<String host:port,String startSlot-endSlot>\n        Map<String, String> slotSegmentsMap = new HashMap<String, String>();\n        for (Entry<String, List<Integer>> entry : hostPortSlotMap.entrySet()) {\n            List<Integer> list = entry.getValue();\n            List<String> slotSegments = new ArrayList<String>();\n            int min = list.get(0);\n            int max = min;\n            for (int i = 1; i < list.size(); i++) {\n                int temp = list.get(i);\n                if (temp == max + 1) {\n                    max = temp;\n                } else {\n                    slotSegments.add(String.valueOf(min) + \"-\" + String.valueOf(max));\n                    min = temp;\n                    max = temp;\n                }\n            }\n            slotSegments.add(String.valueOf(min) + \"-\" + String.valueOf(max));\n            slotSegmentsMap.put(entry.getKey(), slotSegments.toString());\n        }\n        return slotSegmentsMap;\n    }\n\n    /**\n     * 从一个应用中获取所有健康master节点\n     *\n     * @param appId\n     * @return 应用对应master节点列表\n     */\n    public List<InstanceInfo> getAllHealthyInstanceInfo(long appId) {\n        // return instances\n        List<InstanceInfo> allInstance = new ArrayList<InstanceInfo>();\n        List<InstanceInfo> appInstanceInfoList = instanceDao.getInstListByAppId(appId);\n        if (CollectionUtils.isEmpty(appInstanceInfoList)) {\n            logger.error(\"appId {} has not instances\", appId);\n            return null;\n        }\n        for (InstanceInfo instanceInfo : appInstanceInfoList) {\n            int instanceType = instanceInfo.getType();\n            if (!TypeUtil.isRedisCluster(instanceType)) {\n                continue;\n            }\n            final String host = instanceInfo.getIp();\n            final int port = instanceInfo.getPort();\n            if (instanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                continue;\n            }\n            boolean isRun = isRun(appId, host, port);\n            if (!isRun) {\n                logger.warn(\"{}:{} is not run\", host, port);\n                continue;\n            }\n            BooleanEnum isMaster = isMaster(appId, host, port);\n            if (isMaster != BooleanEnum.TRUE) {\n                logger.warn(\"{}:{} is not master\", host, port);\n                continue;\n            }\n            // add exist redis\n            allInstance.add(instanceInfo);\n        }\n        return allInstance;\n    }\n\n    @Override\n    public List<InstanceLatencyHistory> collectRedisLatencyInfo(long appId, long collectTime, String host, int port) {\n        Assert.isTrue(appId > 0);\n        Assert.hasText(host);\n        Assert.isTrue(port > 0);\n        InstanceInfo instanceInfo = instanceDao.getInstByIpAndPort(host, port);\n        //不存在实例/实例异常/下线\n        if (instanceInfo == null) {\n            return null;\n        }\n        if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\n            //忽略sentinel redis实例\n            return null;\n        }\n\n        // 从redis中获取延迟信息\n        List<InstanceLatencyHistory> latencyHistoryList = getLatencyLatest(instanceInfo.getId(), appId, host, port);\n        if (CollectionUtils.isEmpty(latencyHistoryList)) {\n            return Collections.emptyList();\n        }\n\n        //入库\n        String key = getThreadPoolKey() + \"_\" + host + \"_\" + port;\n        boolean isOk = asyncService.submitFuture(getThreadPoolKey(), new KeyCallable<Boolean>(key) {\n            @Override\n            public Boolean execute() {\n                try {\n                    instanceLatencyHistoryDao.batchSave(latencyHistoryList);\n                    return true;\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n        });\n        if (!isOk) {\n            logger.error(\"latencyHistory submitFuture failed,appId:{},collectTime:{},host:{},port:{}\", appId, collectTime,\n                    host, port);\n        }\n        return latencyHistoryList;\n    }\n\n    private List<InstanceLatencyHistory> getLatencyLatest(long instanceId, long appId, String host, int port) {\n        Jedis jedis = null;\n        try {\n            jedis = getAdminJedis(appId, host, port, REDIS_DEFAULT_TIME * 2, REDIS_DEFAULT_TIME * 2);\n\n            List<InstanceLatencyHistory> resultList = new ArrayList<>();\n            List<LatencyItem> latencyItems = JedisUtil.latencyLatest(jedis);\n            List<Object> subResultList = null;\n            if (CollectionUtils.isNotEmpty(latencyItems)) {\n                List<String> eventList = latencyItems.stream().map(latencyItem -> latencyItem.getEvent()).collect(Collectors.toList());\n\n                Pipeline pipeline = jedis.pipelined();\n                for (String event : eventList) {\n                    PipelineUtil.latencyHistory(pipeline, event);\n                    PipelineUtil.latencyReset(pipeline, event);\n                }\n                subResultList = pipeline.syncAndReturnAll();\n\n                if (CollectionUtils.isNotEmpty(subResultList)) {\n                    for (int i = 0; i < subResultList.size(); i++) {\n                        Object o = subResultList.get(i);\n                        if (o instanceof List) {\n                            String event = eventList.get(i / 2);\n                            List<Object> latencyHistoryItems = (List<Object>) o;\n                            List<InstanceLatencyHistory> instanceLatencyHistoryList = latencyHistoryItems.stream()\n                                    .map(data -> {\n                                        List<Object> properties = (List<Object>) data;\n                                        LatencyHistoryItem latencyHistory = new LatencyHistoryItem(properties);\n                                        return new InstanceLatencyHistory(\n                                                instanceId, appId, host, port, event,\n                                                new Date(latencyHistory.getTimeStamp() * 1000L),\n                                                latencyHistory.getExecutionTime());\n                                    })\n                                    .collect(Collectors.toList());\n                            resultList.addAll(instanceLatencyHistoryList);\n                        }\n                    }\n                }\n            }\n            return resultList;\n        } catch (Exception e) {\n            logger.error(String.format(\"appId:%s, host:%s,port:%s,error:%s\", appId, host, port, e.getMessage()), e);\n            return Collections.emptyList();\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    /**\n     * clusterslots命令拼接成Map<Integer slot, String host:port>\n     *\n     * @param host\n     * @param port\n     * @return\n     */\n    private Map<Integer, String> getSlotsHostPortMap(long appId, String host, int port) {\n        Map<Integer, String> slotHostPortMap = new HashMap<Integer, String>();\n        Jedis jedis = null;\n        try {\n            jedis = getAdminJedis(appId, host, port);\n            List<Object> slots = jedis.clusterSlots();\n            for (Object slotInfoObj : slots) {\n                List<Object> slotInfo = (List<Object>) slotInfoObj;\n                if (slotInfo.size() <= 2) {\n                    continue;\n                }\n                List<Integer> slotNums = getAssignedSlotArray(slotInfo);\n\n                // hostInfos\n                List<Object> hostInfos = (List<Object>) slotInfo.get(2);\n                if (hostInfos.size() <= 0) {\n                    continue;\n                }\n                HostAndPort targetNode = generateHostAndPort(hostInfos);\n\n                for (Integer slot : slotNums) {\n                    slotHostPortMap.put(slot, targetNode.getHost() + \":\" + targetNode.getPort());\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n        return slotHostPortMap;\n    }\n\n    private HostAndPort generateHostAndPort(List<Object> hostInfos) {\n        return new HostAndPort(SafeEncoder.encode((byte[]) hostInfos.get(0)),\n                ((Long) hostInfos.get(1)).intValue());\n    }\n\n    private List<Integer> getAssignedSlotArray(List<Object> slotInfo) {\n        List<Integer> slotNums = new ArrayList<Integer>();\n        for (int slot = ((Long) slotInfo.get(0)).intValue(); slot <= ((Long) slotInfo.get(1))\n                .intValue(); slot++) {\n            slotNums.add(slot);\n        }\n        return slotNums;\n    }\n\n    @Override\n    public List<Integer> getClusterLossSlots(long appId, String host, int port) {\n        InstanceInfo instanceInfo = instanceDao.getAllInstByIpAndPort(host, port);\n        if (instanceInfo == null) {\n            logger.warn(\"{}:{} instanceInfo is null\", host, port);\n            return Collections.emptyList();\n        }\n        if (!TypeUtil.isRedisCluster(instanceInfo.getType())) {\n            logger.warn(\"{}:{} is not rediscluster type\", host, port);\n            return Collections.emptyList();\n        }\n        List<Integer> clusterLossSlots = new ArrayList<Integer>();\n        Jedis jedis = null;\n        try {\n            jedis = getAdminJedis(appId, host, port, REDIS_DEFAULT_TIME, REDIS_DEFAULT_TIME);\n            String clusterNodes = jedis.clusterNodes();\n            if (StringUtils.isBlank(clusterNodes)) {\n                throw new RuntimeException(host + \":\" + port + \"clusterNodes is null\");\n            }\n            Set<Integer> allSlots = new LinkedHashSet<Integer>();\n            for (int i = 0; i <= 16383; i++) {\n                allSlots.add(i);\n            }\n\n            // 解析\n            ClusterNodeInformationParser nodeInfoParser = new ClusterNodeInformationParser();\n            for (String nodeInfo : clusterNodes.split(\"\\n\")) {\n                if (StringUtils.isNotBlank(nodeInfo) && !nodeInfo.contains(\"disconnected\") && !nodeInfo\n                        .contains(\"fail\")) {\n                    if (nodeInfo.contains(\"@\")) {\n                        // redis4.0 兼容集群协议 6397@16397\n                        nodeInfo = nodeInfo.replaceAll(nodeInfo.substring(nodeInfo.indexOf(\"@\"),\n                                nodeInfo.indexOf(\"@\") + nodeInfo.split(\"@\")[1].indexOf(\" \") + 1), \"\");\n                    }\n                    ClusterNodeInformation clusterNodeInfo = nodeInfoParser\n                            .parse(nodeInfo, new HostAndPort(host, port));\n                    List<Integer> availableSlots = clusterNodeInfo.getAvailableSlots();\n                    for (Integer slot : availableSlots) {\n                        allSlots.remove(slot);\n                    }\n                }\n            }\n            clusterLossSlots = new ArrayList<Integer>(allSlots);\n        } catch (Exception e) {\n            logger.error(\"getClusterLossSlots: \" + e.getMessage(), e);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n        return clusterLossSlots;\n    }\n\n    @Override\n    public List<Integer> getInstanceSlots(long appId, String healthHost, int healthPort, String lossSlotsHost,\n                                          int lossSlotsPort) {\n        InstanceInfo instanceInfo = instanceDao.getAllInstByIpAndPort(healthHost, healthPort);\n        if (instanceInfo == null) {\n            logger.warn(\"{}:{} instanceInfo is null\", healthHost, healthPort);\n            return Collections.emptyList();\n        }\n        if (!TypeUtil.isRedisCluster(instanceInfo.getType())) {\n            logger.warn(\"{}:{} is not rediscluster type\", healthHost, healthPort);\n            return Collections.emptyList();\n        }\n        List<Integer> clusterLossSlots = new ArrayList<Integer>();\n        Jedis jedis = null;\n        try {\n            jedis = getAdminJedis(appId, healthHost, healthPort, REDIS_DEFAULT_TIME * 2, REDIS_DEFAULT_TIME * 2);\n            String clusterNodes = jedis.clusterNodes();\n            if (StringUtils.isBlank(clusterNodes)) {\n                throw new RuntimeException(healthHost + \":\" + healthPort + \"clusterNodes is null\");\n            }\n            // 解析获取丢失slots\n            ClusterNodeInformationParser nodeInfoParser = new ClusterNodeInformationParser();\n            for (String nodeInfo : clusterNodes.split(\"\\n\")) {\n                if (StringUtils.isNotBlank(nodeInfo) && nodeInfo.contains(\"fail\") && nodeInfo\n                        .contains(lossSlotsHost + \":\" + lossSlotsPort)) {\n                    if (nodeInfo.contains(\"@\")) {\n                        // redis4.0 兼容集群协议 6397@16397\n                        nodeInfo = nodeInfo.replaceAll(nodeInfo.substring(nodeInfo.indexOf(\"@\"),\n                                nodeInfo.indexOf(\"@\") + nodeInfo.split(\"@\")[1].indexOf(\" \") + 1), \"\");\n                    }\n                    ClusterNodeInformation clusterNodeInfo = nodeInfoParser\n                            .parse(nodeInfo, new HostAndPort(healthHost, healthPort));\n                    clusterLossSlots = clusterNodeInfo.getAvailableSlots();\n                }\n            }\n        } catch (Exception e) {\n            logger.error(\"getClusterLossSlots: \" + e.getMessage(), e);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n        return clusterLossSlots;\n    }\n\n    @PreDestroy\n    public void destory() {\n        for (JedisPool jedisPool : jedisPoolMap.values()) {\n            jedisPool.destroy();\n        }\n    }\n\n    @Override\n    public List<InstanceSlowLog> getInstanceSlowLogByAppId(long appId) {\n        try {\n            return instanceSlowLogDao.getByAppId(appId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public List<InstanceSlowLog> getInstanceSlowLogByAppId(long appId, Date startDate, Date endDate) {\n        try {\n            return instanceSlowLogDao.search(appId, startDate, endDate);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public Map<String, Long> getInstanceSlowLogCountMapByAppId(Long appId, Date startDate, Date endDate) {\n        try {\n            List<Map<String, Object>> list = instanceSlowLogDao\n                    .getInstanceSlowLogCountMapByAppId(appId, startDate, endDate);\n            if (CollectionUtils.isEmpty(list)) {\n                return Collections.emptyMap();\n            }\n            Map<String, Long> resultMap = new LinkedHashMap<String, Long>();\n            for (Map<String, Object> map : list) {\n                long count = MapUtils.getLongValue(map, \"count\");\n                String hostPort = MapUtils.getString(map, \"hostPort\");\n                if (StringUtils.isNotBlank(hostPort)) {\n                    resultMap.put(hostPort, count);\n                }\n            }\n            return resultMap;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public Map<String, InstanceSlotModel> getClusterSlotsMap(long appId) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (!TypeUtil.isRedisCluster(appDesc.getType())) {\n            return Collections.emptyMap();\n        }\n        // 最终结果\n        Map<String, InstanceSlotModel> resultMap = new HashMap<String, InstanceSlotModel>();\n\n        // 找到一个运行的节点用来执行cluster slots\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n        String host = null;\n        int port = 0;\n        for (InstanceInfo instanceInfo : instanceList) {\n            // 下线和心跳停止 均跳过\n            if (instanceInfo.isOffline() || instanceInfo.getStatus() == InstanceStatusEnum.ERROR_STATUS.getStatus()) {\n                continue;\n            }\n            host = instanceInfo.getIp();\n            port = instanceInfo.getPort();\n            boolean isRun = isRun(appId, host, port);\n            if (isRun) {\n                break;\n            }\n        }\n        if (StringUtils.isBlank(host) || port <= 0) {\n            return Collections.emptyMap();\n        }\n\n        // 获取cluster slots\n        List<Object> clusterSlotList = null;\n        Jedis jedis = null;\n        try {\n            jedis = getAdminJedis(appId, host, port);\n            clusterSlotList = jedis.clusterSlots();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        if (clusterSlotList == null || clusterSlotList.size() == 0) {\n            return Collections.emptyMap();\n        }\n        //clusterSlotList形如：\n        //\t\t[0, 1, [[B@5caf905d, 6380], [[B@27716f4, 6379]]\n        //\t\t[3, 4096, [[B@8efb846, 6380], [[B@2a84aee7, 6379]]\n        //\t\t[12291, 16383, [[B@a09ee92, 6383], [[B@30f39991, 6382]]\n        //\t\t[2, 2, [[B@452b3a41, 6381], [[B@4a574795, 6382]]\n        //\t\t[8194, 12290, [[B@f6f4d33, 6381], [[B@23fc625e, 6382]]\n        //\t\t[4097, 8193, [[B@3f99bd52, 6380], [[B@4f023edb, 6381]]\n\n        for (Object clusterSlotObj : clusterSlotList) {\n            List<Object> slotInfoList = (List<Object>) clusterSlotObj;\n            if (slotInfoList.size() <= 2) {\n                continue;\n            }\n            //获取slot的start到end相关\n            int startSlot = ((Long) slotInfoList.get(0)).intValue();\n            int endSlot = ((Long) slotInfoList.get(1)).intValue();\n            String slotDistribute = getStartToEndSlotDistribute(startSlot, endSlot);\n            List<Integer> slotList = getStartToEndSlotList(startSlot, endSlot);\n\n            List<Object> masterInfoList = (List<Object>) slotInfoList.get(2);\n            String tempHost = SafeEncoder.encode((byte[]) masterInfoList.get(0));\n            int tempPort = ((Long) masterInfoList.get(1)).intValue();\n            String hostPort = tempHost + \":\" + tempPort;\n            if (resultMap.containsKey(hostPort)) {\n                InstanceSlotModel instanceSlotModel = resultMap.get(hostPort);\n                instanceSlotModel.getSlotDistributeList().add(slotDistribute);\n                instanceSlotModel.getSlotList().addAll(slotList);\n            } else {\n                InstanceSlotModel instanceSlotModel = new InstanceSlotModel();\n                instanceSlotModel.setHost(tempHost);\n                instanceSlotModel.setPort(tempPort);\n                List<String> slotDistributeList = new ArrayList<String>();\n                slotDistributeList.add(slotDistribute);\n                instanceSlotModel.setSlotDistributeList(slotDistributeList);\n                instanceSlotModel.setSlotList(slotList);\n                resultMap.put(hostPort, instanceSlotModel);\n            }\n        }\n        return resultMap;\n    }\n\n    /**\n     * 获取集群slot分布\n     * <K,V> -> <slot start-slot end, List<IP:PORT>>\n     * @param instanceInfo\n     * @return\n     */\n    public Map<String, List<HostAndPort>> getClusterSlotMap(long appId, InstanceInfo instanceInfo){\n        Map<String, List<HostAndPort>> resultMap = new HashMap<>();\n\n        // 如未传入实例，则查找一个健康的实例\n        if(instanceInfo == null){\n            // 找到一个运行的节点来执行\n            List<InstanceInfo> instanceList = instanceDao.getEffectiveInstListByAppId(appId);\n            for (InstanceInfo instance : instanceList) {\n                // 下线和心跳停止 均跳过\n                if (instance.isOffline() || instance.getStatus() == InstanceStatusEnum.ERROR_STATUS.getStatus()) {\n                    continue;\n                }\n                boolean isRun = isRun(appId, instance.getIp(), instance.getPort());\n                if (isRun) {\n                    instanceInfo = instance;\n                    break;\n                }\n            }\n        }\n\n        if(instanceInfo != null){\n            try (Jedis jedis = this.getJedis(appId, instanceInfo.getIp(), instanceInfo.getPort())) {\n                List<Object> slots = jedis.clusterSlots();\n                for (Object clusterSlotObj : slots) {\n                    List<Object> slotInfoList = (List<Object>) clusterSlotObj;\n                    if (slotInfoList.size() <= 2) {\n                        continue;\n                    }\n                    //获取slot的start到end相关\n                    String key = slotInfoList.get(0) + \"-\" + slotInfoList.get(1);\n                    List<HostAndPort> slotInstances = new ArrayList<>();\n                    for(int i = 2; i < slotInfoList.size(); i++){\n                        List<Object> nodeInfo = (List<Object>) slotInfoList.get(i);\n                        if(nodeInfo.size() <= 2){\n                            continue;\n                        }\n                        String tempHost = SafeEncoder.encode((byte[]) nodeInfo.get(0));\n                        int tempPort = ((Long) nodeInfo.get(1)).intValue();\n                        slotInstances.add(new HostAndPort(tempHost, tempPort));\n                    }\n                    if(CollectionUtils.isNotEmpty(slotInstances)){\n                        resultMap.put(key, slotInstances);\n                    }\n                }\n                String clusterNodes = jedis.clusterNodes();\n                List<String> needDeleteKeys = new ArrayList<>();\n                for (String nodeInfo : clusterNodes.split(\"\\n\")) {\n                    if (StringUtils.isNotBlank(nodeInfo) && nodeInfo.contains(\"fail\")) {\n                        Set<Entry<String, List<HostAndPort>>> entrySet = resultMap.entrySet();\n                        entrySet.forEach(entry -> {\n                            List<HostAndPort> hostAndPorts = entry.getValue();\n                            if(CollectionUtils.isNotEmpty(hostAndPorts) && nodeInfo.contains(hostAndPorts.get(0).toString())){\n                                needDeleteKeys.add(entry.getKey());\n                            }\n                        });\n                    }\n                }\n                needDeleteKeys.forEach(key -> resultMap.remove(key));\n            }catch (Exception e){\n                logger.error(\"getClusterSlotMap error,\", e);\n            }\n        }\n        return resultMap;\n    }\n\n    /**\n     * 获取slot列表\n     *\n     * @param startSlot\n     * @param endSlot\n     * @return\n     */\n    private List<Integer> getStartToEndSlotList(int startSlot, int endSlot) {\n        List<Integer> slotList = new ArrayList<Integer>();\n        if (startSlot == endSlot) {\n            slotList.add(startSlot);\n        } else {\n            for (int i = startSlot; i <= endSlot; i++) {\n                slotList.add(i);\n            }\n        }\n        return slotList;\n    }\n\n    /**\n     * 0,4096 0-4096\n     * 2,2 2-2\n     *\n     * @return\n     */\n    private String getStartToEndSlotDistribute(int startSlot, int endSlot) {\n        if (startSlot == endSlot) {\n            return String.valueOf(startSlot);\n        } else {\n            return startSlot + \"-\" + endSlot;\n        }\n    }\n\n    @Override\n    public String getRedisVersion(long appId, String ip, int port) {\n        Map<Object, Map<String, Object>> infoAllMap = getInfoStats(appId, ip, port);\n        if (MapUtils.isEmpty(infoAllMap)) {\n            return null;\n        }\n        Map<String, Object> serverMap = infoAllMap.get(RedisConstant.Server);\n        if (MapUtils.isEmpty(serverMap)) {\n            return null;\n        }\n        return MapUtils.getString(serverMap, \"redis_version\");\n    }\n\n    public Boolean getRedisReplicationStatus(long appId, String ip, int port) {\n\n        Map<Object, Map<String, Object>> infoAllMap = getInfoStats(appId, ip, port);\n        if (MapUtils.isEmpty(infoAllMap)) {\n            return false;\n        }\n        Map<String, Object> serverMap = infoAllMap.get(RedisConstant.Replication);\n        if (MapUtils.isEmpty(serverMap)) {\n            return false;\n        }\n        /**\n         * 主从failover (info replication) slave0 state 状态变化: wait_bgsave -> send_bulk -> online\n         * 1.slave0\tip=${ip},port=${port},state=online,offset=413125529634,lag=1\n         * 2.master_repl_offset\t413125537241\n         */\n        String slave0 = MapUtils.getString(serverMap, \"slave0\");\n        String master_repl_offset = MapUtils.getString(serverMap, \"master_repl_offset\");\n        String role = MapUtils.getString(serverMap, \"role\");\n\n        logger.info(\"salve0 :{} ,master_repl_offset :{}\", slave0, master_repl_offset);\n        try {\n            if (!StringUtils.isEmpty(slave0) && slave0.indexOf(\"state=online\") > -1 && role.equals(\"master\")\n                    && !StringUtils.isEmpty(master_repl_offset)) {\n\n                long slave_offset = 0l;\n                for (String info : slave0.split(\",\")) {\n                    if (info.indexOf(\"offset\") > -1) {\n                        logger.info(\" slave offset = {} \", info.replaceAll(\"offset=\", \"\"));\n                        slave_offset = Long.parseLong(info.replaceAll(\"offset=\", \"\"));\n                    }\n                }\n                // 从偏移量差值 ,load内存数据 offset\n                if (slave_offset == 0) {\n                    return false;\n                }\n                return true;\n            } else {\n                return false;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    /**\n     *\n     * @param appId\n     * @param ip\n     * @param port\n     * @return\n     */\n    @Override\n    public Map<String, String> getRedisRoleAndMasterStatus(long appId, String ip, int port){\n        Map<String, String> resultMap = new HashMap<>();\n        try (Jedis jedis = getJedis(appId, ip, port);){\n            String info = jedis.info(RedisConstant.Replication.getValue());\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            Map<String, Object> map = infoMap.get(RedisConstant.Replication);\n            if (map == null) {\n                return resultMap;\n            }\n            String role = MapUtils.getString(map, RedisInfoEnum.role.getValue());\n            resultMap.put(RedisInfoEnum.role.getValue(), role);\n            if(InstanceRoleEnum.SLAVE.getInfo().equals(role)){\n                String masterLinkStatus = MapUtils.getString(map, RedisInfoEnum.master_link_status.getValue());\n                resultMap.put(RedisInfoEnum.master_link_status.getValue(), masterLinkStatus);\n            }\n        }\n        return resultMap;\n    }\n\n    @Override\n    public Boolean getRedisFailoverForceStatus(long appId, String ip, int port){\n        Map<Object, Map<String, Object>> infoAllMap = getInfoStats(appId, ip, port);\n        if (MapUtils.isEmpty(infoAllMap)) {\n            return false;\n        }\n        Map<String, Object> serverMap = infoAllMap.get(RedisConstant.Replication);\n        if (MapUtils.isEmpty(serverMap)) {\n            return false;\n        }\n        /**\n         * failover force 只管role角色变更， 且master_failover_state 未在进行中\n         */\n        String role = MapUtils.getString(serverMap, \"role\");\n        String master_repl_offset = MapUtils.getString(serverMap, \"master_repl_offset\");\n        String master_failover_state = MapUtils.getString(serverMap, \"master_failover_state\");\n        logger.info(\"redis failover force slave: {}:{} ,master_repl_offset :{}\", ip, port, master_repl_offset);\n        try {\n            if (role.equals(\"master\") && !StringUtils.isEmpty(master_repl_offset)\n                && (StringUtils.isBlank(master_failover_state) || master_failover_state.equals(\"no-failover\"))) {\n                return true;\n            } else {\n                return false;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    @Override\n    public String getNodeId(long appId, String ip, int port) {\n        final Jedis jedis = getAdminJedis(appId, ip, port);\n        try {\n            final StringBuilder clusterNodes = new StringBuilder();\n            boolean isGetNodes = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    String nodes = jedis.clusterNodes();\n                    if (nodes != null && nodes.length() > 0) {\n                        clusterNodes.append(nodes);\n                        return true;\n                    }\n                    return false;\n                }\n            }.run();\n            if (!isGetNodes) {\n                logger.error(\"{}:{} clusterNodes failed\", jedis.getClient().getHost(), jedis.getClient().getPort());\n                return null;\n            }\n            for (String infoLine : clusterNodes.toString().split(\"\\n\")) {\n                if (infoLine.contains(\"myself\")) {\n                    String nodeId = infoLine.split(\" \")[0];\n                    return nodeId;\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return null;\n    }\n\n    @Override\n    public Jedis getJedis(long appId, String host, int port) {\n        Jedis jedis = getJedis(appId, host, port, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);\n        return jedis;\n    }\n\n    @Override\n    public Jedis getJedis(long appId, String host, int port, int connectionTimeout, int soTimeout) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String password = appDesc.getAppPassword();\n        Jedis jedis = getJedis(host, port, connectionTimeout, soTimeout, password);\n        return jedis;\n    }\n\n    @Override\n    public Jedis getJedis(String host, int port, String authPassword) {\n        return getJedis(host, port, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, authPassword);\n    }\n\n    @Override\n    public Jedis getAdminJedis(long appId, String host, int port) {\n        Jedis jedis = getAdminJedis(appId, host, port, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);\n        return jedis;\n    }\n\n    @Override\n    public Jedis getAdminJedis(long appId, String host, int port, int connectionTimeout, int soTimeout) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String password = appDesc.getAppPassword();\n        Jedis jedis = getJedis(host, port, connectionTimeout, soTimeout, password);\n        return jedis;\n    }\n\n    @Override\n    public Jedis getAdminJedis(String host, int port, String adminPassword) {\n        return getAdminJedis(host, port, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, adminPassword);\n    }\n\n    @Override\n    public Jedis getJedis(String host, int port) {\n        return getJedis(host, port, null);\n    }\n\n    @Override\n    public Jedis getJedis(String host, int port, String password, int connectionTimeout, int soTimeout){\n        return getJedis(host, port, connectionTimeout, soTimeout, password);\n    }\n\n    private Jedis getJedis(String host, int port, int connectionTimeout, int soTimeout, String authPassword) {\n        Jedis jedis = new Jedis(host, port);\n        jedis.getClient().setConnectionTimeout(connectionTimeout);\n        jedis.getClient().setSoTimeout(soTimeout);\n        try {\n            if (StringUtils.isBlank(authPassword)) {\n                // 保证存活性\n                jedis.ping();\n            } else {\n                AuthUtil.auth(jedis, authPassword);\n            }\n        } catch (Exception e) {\n            //防止加载RBD期间报:JedisDataException: LOADING Redis is loading the dataset in memory错误\n            logger.error(String.format(\"getJedis exception host:%s, port:%s, conTimeout:%s, soTimeout:%s, error:%s\", host, port, connectionTimeout, soTimeout, e.getMessage()), e);\n        }\n        return jedis;\n    }\n\n    private Jedis getAdminJedis(String host, int port, int connectionTimeout, int soTimeout, String adminPassword) {\n        return getJedis(host, port, connectionTimeout, soTimeout, adminPassword);\n    }\n\n    private void fixReadOnlyOfCluster(long appId, Jedis jedis) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return;\n        }\n    }\n\n    @Override\n    public boolean sendDeployRedisRelateCollectionMsg(long appId, String host, int port) {\n        return true;\n    }\n\n    @Override\n    public boolean checkNutCrackerConfIsSame(long appId) {\n        List<String> masterNameList = getMasterNameListFromNutCrackerConf(appId);\n        if (CollectionUtils.isNotEmpty(masterNameList)) {\n            return true;\n        }\n        return false;\n    }\n\n    private List<String> getMasterNameListFromNutCrackerConf(long appId) {\n        List<List<String>> appNutCrackerMasterList = getFullInstanceListFromNutCrackerConf(appId);\n        if (CollectionUtils.isEmpty(appNutCrackerMasterList)) {\n            return Collections.emptyList();\n        }\n        List<String> nutCrackerMasterList = appNutCrackerMasterList.get(0);\n        List<String> masterNameList = new ArrayList<String>();\n        for (String nutCrackerMaster : nutCrackerMasterList) {\n            String[] arr = nutCrackerMaster.split(\"\\\\s+\");\n            masterNameList.add(arr[arr.length - 1].trim());\n        }\n        return masterNameList;\n    }\n\n    public List<List<String>> getFullInstanceListFromNutCrackerConf(long appId) {\n        Map<String, List<String>> appNutCrackerMasterMap = getAppNutCrackerMasterList(appId);\n        if (MapUtils.isEmpty(appNutCrackerMasterMap)) {\n            logger.error(BaseTask.marker, \"appId {} appNutCrackerMasterListMap is empty\", appId);\n            return Collections.emptyList();\n        }\n        List<String> ipPortList = new ArrayList<String>();\n        List<List<String>> appNutCrackerMasterList = new ArrayList<List<String>>();\n        for (Entry<String, List<String>> entry : appNutCrackerMasterMap.entrySet()) {\n            ipPortList.add(entry.getKey());\n            appNutCrackerMasterList.add(entry.getValue());\n        }\n        for (int i = 0; i < appNutCrackerMasterList.size() - 1; i++) {\n            List<String> appNutCrackerMasterList1 = appNutCrackerMasterList.get(i);\n            List<String> appNutCrackerMasterList2 = appNutCrackerMasterList.get(i + 1);\n            if (appNutCrackerMasterList1.size() != appNutCrackerMasterList2.size()) {\n                logger.error(BaseTask.marker, \"{} and {} config size is not same\", ipPortList.get(i), ipPortList.get(i + 1));\n                return Collections.emptyList();\n            }\n            for (int j = 0; j < appNutCrackerMasterList1.size(); j++) {\n                if (!appNutCrackerMasterList1.get(j).trim().equals(appNutCrackerMasterList2.get(j).trim())) {\n                    logger.error(BaseTask.marker, \"{} and {} config content is not same\", ipPortList.get(i), ipPortList.get(i + 1));\n                    return Collections.emptyList();\n                }\n            }\n        }\n        return appNutCrackerMasterList;\n    }\n\n    /**\n     * 获取所有在线的proxy配置\n     *\n     * @param appId\n     * @return\n     */\n    private Map<String, List<String>> getAppNutCrackerMasterList(long appId) {\n        Map<String, List<String>> resultMap = new HashMap<String, List<String>>();\n        List<InstanceInfo> instanceInfoList = appService.getAppInstanceByType(appId, InstanceTypeEnum.NUTCRACKER);\n        for (InstanceInfo instanceInfo : instanceInfoList) {\n            if (!instanceInfo.isOnline()) {\n                continue;\n            }\n            String host = instanceInfo.getIp();\n            int port = instanceInfo.getPort();\n            String remoteBasePath = machineCenter.getInstanceRemoteBasePath(appId, instanceInfo.getPort(),\n                    InstanceTypeEnum.NUTCRACKER);\n            String confPath = MachineProtocol.getConfPath(remoteBasePath) + \"/\" + RedisProtocol.getNutCrackerConfName();\n            String commandResult = machineCenter.executeShell(instanceInfo.getIp(), \"cat \" + confPath);\n            if (StringUtils.isBlank(commandResult)) {\n                logger.error(BaseTask.marker, \"appId {} {}:{} nutcrack conf {} is empty\", appId, host, port, confPath);\n                return Collections.emptyMap();\n            }\n            List<String> masterNameList = new ArrayList<String>();\n            String[] lines = commandResult.split(\"\\n\");\n            for (String line : lines) {\n                if (StringUtils.isBlank(line)) {\n                    continue;\n                }\n                if (!line.contains(\"-\")) {\n                    continue;\n                }\n                if (line.split(\":\").length < 2) {\n                    continue;\n                }\n                masterNameList.add(line);\n            }\n            resultMap.put(host + \":\" + port, masterNameList);\n        }\n        return resultMap;\n    }\n\n    @Override\n    public List<InstanceInfo> checkNutCrackerHashIsSame(long appId, boolean isDelete) {\n        return null;\n    }\n\n    private class RedisKeyCallable extends KeyCallable<Boolean> {\n        private final long appId;\n        private final long collectTime;\n        private final String host;\n        private final int port;\n        private final Map<Object, Map<String, Object>> infoMap;\n        private final Map<String, Object> clusterInfoMap;\n\n        private RedisKeyCallable(long appId, long collectTime, String host, int port,\n                                 Map<Object, Map<String, Object>> infoMap, Map<String, Object> clusterInfoMap) {\n            super(buildFutureKey(appId, collectTime, host, port));\n            this.appId = appId;\n            this.collectTime = collectTime;\n            this.host = host;\n            this.port = port;\n            this.infoMap = infoMap;\n            this.clusterInfoMap = clusterInfoMap;\n        }\n\n        @Override\n        public Boolean execute() {\n            //比对currentInfoMap和lastInfoMap,计算差值\n            long lastCollectTime = ScheduleUtil.getLastCollectTime(collectTime);\n            Map<String, Object> lastInfoMap = instanceStatsCenter\n                    .queryStandardInfoMap(lastCollectTime, host, port, ConstUtils.REDIS);\n\n            if (lastInfoMap == null || lastInfoMap.isEmpty()) {\n                logger.error(\"[redis-lastInfoMap] : lastCollectTime = {} appId={} host:port = {}:{} is null\",\n                        lastCollectTime, appId, host, port);\n            }\n            //基本统计累加差值\n            Table<RedisConstant, String, Long> baseDiffTable = getAccumulationDiff(infoMap, lastInfoMap);\n            fillAccumulationMap(infoMap, baseDiffTable);\n\n            //命令累加差值\n            Table<RedisConstant, String, Long> commandDiffTable = getCommandsDiff(infoMap, lastInfoMap);\n            fillAccumulationMap(infoMap, commandDiffTable);\n\n            //内存碎片率差值计算\n            //Table<RedisConstant, String, Double> otherDiffTable = getDoubleAccumulationDiff(infoMap, lastInfoMap);\n            //fillDoubleAccumulationMap(infoMap, otherDiffTable);\n            fillMemFragRatioMap(infoMap);\n\n            Map<String, Object> currentInfoMap = new LinkedHashMap<String, Object>();\n            for (Map.Entry<Object, Map<String, Object>> entry : infoMap.entrySet()) {\n                currentInfoMap.put(((RedisConstant)(entry.getKey())).getValue(), entry.getValue());\n            }\n            currentInfoMap.put(ConstUtils.COLLECT_TIME, collectTime);\n            instanceStatsCenter.saveStandardStats(currentInfoMap, clusterInfoMap, host, port, ConstUtils.REDIS);\n\n            // 更新实例在db中的状态\n            InstanceStats instanceStats = getInstanceStats(appId, host, port, infoMap);\n            if (instanceStats != null) {\n                instanceStatsDao.updateInstanceStats(instanceStats);\n            }\n\n            BooleanEnum isMaster = isMaster(infoMap);\n            Table<RedisConstant, String, Long> diffTable = HashBasedTable.create();\n            diffTable.putAll(baseDiffTable);\n            diffTable.putAll(commandDiffTable);\n            //更新命令统计\n            List<AppCommandStats> commandStatsList = getCommandStatsList(appId, collectTime, diffTable);\n            //写入app分钟统计\n            AppStats appStats = null;\n            if (isMaster == BooleanEnum.TRUE){\n                //写入app分钟统计\n                appStats = getAppStats(appId, collectTime, diffTable, infoMap);\n                this.mergeCommandStats(commandStatsList, appStats);\n                logger.debug(\"collect redis info done, appId: {}, instance: {}:{}, time: {}\", appId, host, port,\n                        collectTime);\n            }\n            return true;\n        }\n\n        private void mergeCommandStats(List<AppCommandStats> commandStatsList, AppStats appStats){\n            long allCommandCount = 0L;\n            for (AppCommandStats commandStats : commandStatsList) {\n                //排除无效命令且存储有累加的数据\n                if (RedisExcludeCommand.isExcludeCommand(commandStats.getCommandName())\n                        || commandStats.getCommandCount() <= 0L) {\n                    continue;\n                }\n                allCommandCount += commandStats.getCommandCount();\n                try {\n                    // todo 数据库(on duplicate key update)竞争优化\n                    appStatsDao.mergeMinuteCommandStatus(commandStats);\n                    appStatsDao.mergeHourCommandStatus(commandStats);\n                } catch (Exception e) {\n                    logger.error(e.getMessage() + appId, e);\n                }\n            }\n            if(appStats != null){\n                try {\n                    appStats.setCommandCount(allCommandCount);\n                    // todo 数据库(on duplicate key update)竞争优化\n                    appStatsDao.mergeMinuteAppStats(appStats);\n                    appStatsDao.mergeHourAppStats(appStats);\n                } catch (Exception e) {\n                    logger.error(e.getMessage() + appId, e);\n                }\n            }\n        }\n    }\n\n    @Override\n    public String configGet(long appId, String host, int port, String key) {\n        Jedis jedis = this.getAdminJedis(appId, host, port, Protocol.DEFAULT_TIMEOUT * 3, Protocol.DEFAULT_TIMEOUT * 3);\n        try {\n            List<String> values = jedis.configGet(key);\n            if (values == null || values.size() < 1) {\n                return null;\n            }\n            return values.get(1);\n        } catch (Exception e) {\n            logger.error(\"config get {} from host:{} port:{} error: {}\", key, host, port, e.getMessage());\n        } finally {\n            jedis.close();\n        }\n        return null;\n    }\n\n    @Override\n    public boolean configSetAndRewrite(long appId, String host, int port, String key, String value) {\n        Jedis jedis = this.getAdminJedis(appId, host, port, Protocol.DEFAULT_TIMEOUT * 3, Protocol.DEFAULT_TIMEOUT * 3);\n        try {\n            boolean isConfig = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    String result = jedis.configSet(key, value);\n                    boolean isConfig = result != null && result.equalsIgnoreCase(\"OK\");\n                    if (!isConfig) {\n                        logger.error(\"config set key:{} value:{} host:{} port:{} result:{}\", key, value, host, port, result);\n                        return false;\n                    }\n                    return isConfig;\n                }\n            }.run();\n            String response = jedis.configRewrite();\n            boolean isRewrite = response != null && response.equalsIgnoreCase(\"OK\");\n            if (!isRewrite) {\n                logger.error(\"config rewrite after config set key:{} value:{} host:{} port:{} result:{}\", key, value, host, port);\n            }\n            return isConfig;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n    }\n\n    /**\n     * 检测从节点是否准备OK\n     * @param appId\n     * @param ip\n     * @param port\n     * @param offset\n     * @return\n     */\n    @Override\n    public boolean checkSlaveReady(long appId, String ip, int port, long offset) {\n        Jedis jedis = null;\n        try {\n            jedis = getJedis(appId, ip, port);\n            return this.checkSlaveReady(jedis, offset);\n        } catch (Exception e) {\n            logger.error(\"{}:{} checkSlaveReady failed {}\", ip, port, e.getMessage(), e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return false;\n    }\n\n    /**\n     * 获取节点角色描述\n     * @param jedis\n     * @return\n     */\n    @Override\n    public String getInstanceRole(Jedis jedis) {\n        try {\n            String info = jedis.info(RedisConstant.Replication.getValue());\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            Map<String, Object> map = infoMap.get(RedisConstant.Replication);\n            if (map != null) {\n                String role = MapUtils.getString(map, RedisInfoEnum.role.getValue(), null);\n                return role;\n            }\n        } catch (Exception e) {\n            logger.error(\"{}:{} getInstanceRole failed {}\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage(), e);\n        }\n        return null;\n    }\n\n    public String getInstanceRole(long appId, String ip, int port){\n        Jedis jedis = null;\n        try {\n            jedis = getJedis(appId, ip, port);\n            return getInstanceRole(jedis);\n        } catch (Exception e) {\n            logger.error(\"{}:{} getInstanceRole failed {}\", ip, port, e.getMessage(), e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return null;\n    }\n\n\n    /**\n     * 检测从节点是否准备OK\n     * @param jedis\n     * @param offset\n     * @return\n     */\n    @Override\n    public boolean checkSlaveReady(Jedis jedis, Long offset) {\n        try {\n            String info = jedis.info(RedisConstant.Replication.getValue());\n            Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n            Map<String, Object> map = infoMap.get(RedisConstant.Replication);\n            if (map == null) {\n                return false;\n            }\n            String role = MapUtils.getString(map, RedisInfoEnum.role.getValue(), null);\n            String master_link_status = MapUtils.getString(map, RedisInfoEnum.master_link_status.getValue(), null);\n            long slaveReplOffset = MapUtils.getLong(map, RedisInfoEnum.slave_repl_offset.getValue(), 0L);\n            long masterReplOffset = MapUtils.getLong(map, RedisInfoEnum.master_repl_offset.getValue(), 0L);\n            long masterSyncInProgress = MapUtils.getLong(map, RedisInfoEnum.master_sync_in_progress.getValue(), -1L);\n            if(\"slave\".equals(role) && \"up\".equals(master_link_status)\n                    && masterSyncInProgress == 0){\n                if(offset != null && slaveReplOffset >= 0 && masterReplOffset >= 0\n                    && masterReplOffset - slaveReplOffset > offset){\n                    return false;\n                }\n                return true;\n            }\n        } catch (Exception e) {\n            logger.error(String.format(\"%s:%s  checkSlaveReady failed %s\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage()), e);\n        }\n        return false;\n    }\n\n    /**\n     * 检测bgsave是否完成\n     * @param jedis\n     * @return\n     */\n    @Override\n    public boolean checkBgsaveFinish(Jedis jedis, int checkTimes) {\n        try {\n            long rdb_last_bgsave_time_sec = 0L;\n            while (checkTimes > 0){\n                checkTimes--;\n                String info = jedis.info(RedisConstant.Persistence.getValue());\n                Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n                Map<String, Object> map = infoMap.get(RedisConstant.Persistence);\n                if (map == null) {\n                    return false;\n                }\n                Integer inProgress = MapUtils.getInteger(map, RedisInfoEnum.rdb_bgsave_in_progress.getValue(), null);\n                long lastSaveTime = MapUtils.getLong(map, RedisInfoEnum.rdb_last_save_time.getValue(), 0L);\n                rdb_last_bgsave_time_sec = MapUtils.getLong(map, RedisInfoEnum.rdb_last_bgsave_time_sec.getValue(), 0L);\n                String lastBgsaveStatus = MapUtils.getString(map, RedisInfoEnum.rdb_last_bgsave_status.getValue(), null);\n                if(inProgress != null && inProgress == 0 && \"ok\".equals(lastBgsaveStatus)\n                        && System.currentTimeMillis()/1000 - lastSaveTime <= 300){\n                    return true;\n                }\n                if(inProgress != null && inProgress == 1){\n                    try{\n                        TimeUnit.SECONDS.sleep(rdb_last_bgsave_time_sec > 10 ? rdb_last_bgsave_time_sec : 5);\n                    }catch (Exception e){\n                        logger.error(String.format(\"%s:%s  checkBgsaveFinish sleep failed %s\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage()), e);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            logger.error(String.format(\"%s:%s  checkBgsaveFinish failed %s\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage()), e);\n        }\n        return false;\n    }\n\n    /**\n     * 检测load rdb/aof是否完成\n     * @param jedis\n     * @return\n     */\n    @Override\n    public boolean checkLoadFinish(Jedis jedis, int checkTimes) {\n        try {\n            long sleepTime = 0L;\n            while (checkTimes-- > 0){\n                String info = jedis.info(RedisConstant.Persistence.getValue());\n                Map<Object, Map<String, Object>> infoMap = processRedisStats(info);\n                Map<String, Object> map = infoMap.get(RedisConstant.Persistence);\n                if (map == null) {\n                    logger.error(\"{}:{} checkLoadFinish info persistence null, infoMap: {}\", jedis.getClient().getHost(), jedis.getClient().getPort(), infoMap);\n                    continue;\n                }\n                Integer inProgress = MapUtils.getInteger(map, RedisInfoEnum.loading.getValue(), null);\n                Integer etaSeconds = MapUtils.getInteger(map, RedisInfoEnum.loading_eta_seconds.getValue(), null);\n                logger.error(\"{}:{} checkLoadFinish inProgress: {}, etaSeconds: {}\", jedis.getClient().getHost(), jedis.getClient().getPort(), inProgress, etaSeconds);\n                if(inProgress != null && inProgress == 0){\n                    return true;\n                }\n                if(inProgress != null && inProgress == 1){\n                    if(etaSeconds != null && etaSeconds > 0){\n                        sleepTime = etaSeconds;\n                    }else {\n                        sleepTime = 5;\n                    }\n                    try{\n                        TimeUnit.SECONDS.sleep(sleepTime);\n                    }catch (Exception e){\n                        logger.error(String.format(\"%s:%s checkLoadFinish sleep failed %s\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage()), e);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            logger.error(String.format(\"%s:%s checkLoadFinish failed %s\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage()), e);\n        }\n        return false;\n    }\n\n    /**\n     * 获取rdb文件名\n     * @param jedis\n     * @return\n     */\n    @Override\n    public String getRdbFileName(Jedis jedis) {\n        try {\n            List<String> list = jedis.configGet(\"dbfilename\");\n            if (CollectionUtils.isNotEmpty(list) && list.size() >= 2) {\n                return list.get(1);\n            }\n        }catch (Exception e){\n            logger.error(String.format(\"%s:%s getRdbFileName failed %s\", jedis.getClient().getHost(), jedis.getClient().getPort(), e.getMessage()), e);\n        }\n        return null;\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisConfigTemplateServiceImpl.java",
    "content": "package com.sohu.cache.redis.impl;\r\n\r\nimport com.sohu.cache.constant.BaseConstant;\r\nimport com.sohu.cache.constant.InstanceStatusEnum;\r\nimport com.sohu.cache.dao.InstanceConfigDao;\r\nimport com.sohu.cache.dao.MachineDao;\r\nimport com.sohu.cache.dao.ResourceDao;\r\nimport com.sohu.cache.entity.*;\r\nimport com.sohu.cache.exception.SSHException;\r\nimport com.sohu.cache.machine.MachineCenter;\r\nimport com.sohu.cache.protocol.MachineProtocol;\r\nimport com.sohu.cache.protocol.RedisProtocol;\r\nimport com.sohu.cache.redis.RedisCenter;\r\nimport com.sohu.cache.redis.RedisConfigTemplateService;\r\nimport com.sohu.cache.redis.RedisDeployCenter;\r\nimport com.sohu.cache.redis.enums.DirEnum;\r\nimport com.sohu.cache.redis.enums.RedisClusterConfigEnum;\r\nimport com.sohu.cache.redis.enums.RedisConfigEnum;\r\nimport com.sohu.cache.redis.enums.RedisSentinelConfigEnum;\r\nimport com.sohu.cache.ssh.SSHService;\r\nimport com.sohu.cache.ssh.SSHTemplate;\r\nimport com.sohu.cache.ssh.SSHUtil;\r\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\r\nimport com.sohu.cache.task.constant.ResourceEnum;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.RedisConstUtils;\r\nimport com.sohu.cache.util.TypeUtil;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.service.AppService;\r\nimport com.sohu.cache.web.service.ResourceService;\r\nimport com.sohu.cache.web.util.DateUtil;\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.apache.commons.collections.MapUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.apache.commons.lang3.tuple.Pair;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.context.annotation.Lazy;\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport java.util.*;\r\nimport java.util.concurrent.TimeUnit;\r\nimport java.util.stream.Collectors;\r\nimport java.util.stream.IntStream;\r\n\r\n/**\r\n * redis配置模板服务\r\n *\r\n * @author leifu\r\n * @Date 2016年6月23日\r\n * @Time 下午2:08:03\r\n */\r\n@Service(\"redisConfigTemplateService\")\r\npublic class RedisConfigTemplateServiceImpl implements RedisConfigTemplateService {\r\n\r\n    private Logger logger = LoggerFactory.getLogger(RedisConfigTemplateServiceImpl.class);\r\n\r\n    private final static String SPECIAL_EMPTY_STR = \"\\\"\\\"\";\r\n    @Autowired\r\n    private InstanceConfigDao instanceConfigDao;\r\n    @Autowired\r\n    private ResourceService resourceService;\r\n    @Autowired\r\n    private ResourceDao resourceDao;\r\n    @Autowired\r\n    private MachineDao machineDao;\r\n    @Autowired\r\n    private AppService appService;\r\n    @Autowired\r\n    @Lazy\r\n    private RedisCenter redisCenter;\r\n    @Autowired\r\n    private RedisDeployCenter redisDeployCenter;\r\n    @Autowired\r\n    private InstanceDeployCenter instanceDeployCenter;\r\n    @Autowired\r\n    @Lazy\r\n    private MachineCenter machineCenter;\r\n    @Autowired\r\n    SSHService sshService;\r\n\r\n    private static int RETRY_TIMES = 30;\r\n\r\n    private static long SLEEP_MILLS = 2000;\r\n\r\n    private static int WAITING_RESOURCE_SECOND = 5;\r\n\r\n    private static int WAITING_RETRY_TIMES = 10;\r\n\r\n    private static int DOWNLOAD_SECONDS = 60 * 5 * 1000;\r\n\r\n    private static String DOWNLOAD_CMD = \"cd %s && wget %s && tar -xvf %s && rm -rf %s \";\r\n\r\n    @Override\r\n    public List<InstanceConfig> getAllInstanceConfig() {\r\n        try {\r\n            return instanceConfigDao.getAllInstanceConfig();\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public List<InstanceConfig> getByType(int type) {\r\n        try {\r\n            return instanceConfigDao.getByType(type);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    public List<InstanceConfig> getByVesionAndType(int type, int versionId) {\r\n        try {\r\n            return instanceConfigDao.getByVersionAndType(type, versionId);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    public List<InstanceConfig> getByVesion(int versionId) {\r\n        try {\r\n            return instanceConfigDao.getByVersion(versionId);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return Collections.emptyList();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public int saveOrUpdate(InstanceConfig instanceConfig) {\r\n        return instanceConfigDao.saveOrUpdate(instanceConfig);\r\n    }\r\n\r\n    @Override\r\n    public InstanceConfig getById(long id) {\r\n        try {\r\n            return instanceConfigDao.getById(id);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return null;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public InstanceConfig getByConfigKeyAndType(String configKey, int type) {\r\n        try {\r\n            return instanceConfigDao.getByConfigKeyAndType(configKey, type);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return null;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public int remove(long id) {\r\n        return instanceConfigDao.remove(id);\r\n    }\r\n\r\n    @Override\r\n    public int updateStatus(long id, int status) {\r\n        return instanceConfigDao.updateStatus(id, status);\r\n    }\r\n\r\n\r\n    @Override\r\n    public List<String> handleCommonConfig(String host, int port, int maxMemory, String maxMemoryPolicy, int versionId) {\r\n        //2018-08-24\r\n        List<InstanceConfig> instanceConfigList = getByVesionAndType(ConstUtils.CACHE_REDIS_STANDALONE, versionId);\r\n        if (CollectionUtils.isEmpty(instanceConfigList)) {\r\n            return Collections.emptyList();\r\n        }\r\n        List<String> configs = new ArrayList<String>();\r\n        for (InstanceConfig instanceConfig : instanceConfigList) {\r\n            // 无效配置过滤\r\n            if (!instanceConfig.isEffective()) {\r\n                continue;\r\n            }\r\n            String configKey = instanceConfig.getConfigKey();\r\n            String configValue = instanceConfig.getConfigValue();\r\n            if (StringUtils.isBlank(configValue)) {\r\n                configValue = SPECIAL_EMPTY_STR;\r\n            }\r\n            if (RedisConfigEnum.MAXMEMORY.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, maxMemory);\r\n            } else if (RedisConfigEnum.DBFILENAME.getKey().equals(configKey)\r\n                    || RedisConfigEnum.APPENDFILENAME.getKey().equals(configKey) || RedisConfigEnum.PORT.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, port);\r\n            } else if (RedisConfigEnum.DIR.getKey().equals(configKey)) {\r\n                configValue = machineCenter.getMachineRelativeDir(host, DirEnum.DATA_DIR.getValue());\r\n            } else if (RedisConfigEnum.AUTO_AOF_REWRITE_PERCENTAGE.getKey().equals(configKey)) {\r\n                //随机比例 auto-aof-rewrite-percentage\r\n                int percent = 69 + new Random().nextInt(30);\r\n                configValue = String.format(configValue, percent);\r\n            } else if (RedisConfigEnum.BIND.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, host);\r\n            } else if (RedisConfigEnum.MAXMEMORY_POLICY.getKey().equals(configKey)) {\r\n                if (StringUtils.isNotEmpty(maxMemoryPolicy)) {\r\n                    configValue = maxMemoryPolicy;\r\n                }\r\n            }\r\n            configs.add(combineConfigKeyValue(configKey, configValue));\r\n        }\r\n        return configs;\r\n    }\r\n\r\n    @Override\r\n    public List<String> handleSentinelConfig(String masterName, String host, int port, String sentinelHost, int sentinelPort, int versionId, List<Pair<String, String>> customConfigs) {\r\n        List<InstanceConfig> instanceConfigList = getByVesionAndType(ConstUtils.CACHE_REDIS_SENTINEL, versionId);\r\n        instanceConfigList = instanceConfigList.stream().filter(InstanceConfig::isEffective).collect(Collectors.toList());\r\n        if (CollectionUtils.isEmpty(instanceConfigList)) {\r\n            return Collections.emptyList();\r\n        }\r\n        Map<String, List<String>> notExistConfigMap = new HashMap<>();\r\n        Map<String, List<String>> customConfigMap = new HashMap<>();\r\n        this.getCustomMapParam(instanceConfigList, customConfigs, customConfigMap, notExistConfigMap);\r\n        List<String> configs = new ArrayList<String>();\r\n        for (InstanceConfig instanceConfig : instanceConfigList) {\r\n            String configKey = instanceConfig.getConfigKey();\r\n            String configValue = instanceConfig.getConfigValue();\r\n            if (StringUtils.isBlank(configValue)) {\r\n                configValue = SPECIAL_EMPTY_STR;\r\n            }\r\n            if (RedisSentinelConfigEnum.PORT.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, sentinelPort);\r\n            } else if (RedisSentinelConfigEnum.MONITOR.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, masterName, host, port);\r\n            } else if (RedisSentinelConfigEnum.DOWN_AFTER_MILLISECONDS.getKey().equals(configKey) || RedisSentinelConfigEnum.FAILOVER_TIMEOUT.getKey().equals(configKey) || RedisSentinelConfigEnum.PARALLEL_SYNCS.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, masterName);\r\n            } else if (RedisConfigEnum.DIR.getKey().equals(configKey)) {\r\n                configValue = machineCenter.getMachineRelativeDir(sentinelHost, DirEnum.DATA_DIR.getValue());\r\n            } else {\r\n                if(customConfigMap.containsKey(configKey)){\r\n                    configValue = this.setCustomConfigs(configKey, configValue, configs, customConfigMap);\r\n                }\r\n            }\r\n            configs.add(combineConfigKeyValue(configKey, configValue));\r\n        }\r\n        this.setNewConfigDirectly(configs, notExistConfigMap);\r\n        return configs;\r\n    }\r\n\r\n    private void getCustomMapParam(List<InstanceConfig> instanceConfigList, List<Pair<String, String>> customConfigs, Map<String, List<String>> customConfigMap, Map<String, List<String>> notExistConfigMap){\r\n        Map<String, String> instanceConfigMap = instanceConfigList.stream().collect(Collectors.toMap(InstanceConfig::getConfigKey, InstanceConfig::getConfigValue));\r\n        if(CollectionUtils.isNotEmpty(customConfigs)){\r\n            customConfigs.forEach(customConfig -> {\r\n                if(customConfigMap.containsKey(customConfig.getKey())){\r\n                    customConfigMap.get(customConfig.getKey()).add(customConfig.getValue());\r\n                }else{\r\n                    List<String> confiValues = new ArrayList<>();\r\n                    confiValues.add(customConfig.getValue());\r\n                    customConfigMap.put(customConfig.getKey(), confiValues);\r\n                }\r\n            });\r\n        }\r\n        customConfigMap.keySet().forEach(key -> {\r\n            if (!instanceConfigMap.containsKey(key)) {\r\n                notExistConfigMap.put(key, customConfigMap.get(key));\r\n            }\r\n        });\r\n    }\r\n\r\n    private String setCustomConfigs(String configKey, String configValue, List<String> configs, Map<String, List<String>> customConfigMap){\r\n        List<String> configValues = customConfigMap.get(configKey);\r\n        if(configValues.size() == 1){\r\n            configValue = configValues.get(0);\r\n        } else {\r\n            IntStream.range(0, configValues.size() - 1).forEach(i ->\r\n                    configs.add(combineConfigKeyValue(configKey, configValues.get(i)))\r\n            );\r\n            configValue = configValues.get(configValues.size() - 1);\r\n        }\r\n        return configValue;\r\n    }\r\n\r\n    private void setNewConfigDirectly(List<String> configs, Map<String, List<String>> notExistConfigMap){\r\n        notExistConfigMap.forEach((configKey, configValues) ->\r\n                configValues.forEach(configValue ->\r\n                        configs.add(combineConfigKeyValue(configKey, configValue))\r\n                )\r\n        );\r\n    }\r\n\r\n    @Override\r\n    public List<String> handleClusterConfig(int port, int versionId) {\r\n        List<InstanceConfig> instanceConfigList = getByVesionAndType(ConstUtils.CACHE_TYPE_REDIS_CLUSTER, versionId);\r\n        if (CollectionUtils.isEmpty(instanceConfigList)) {\r\n            return Collections.emptyList();\r\n        }\r\n        List<String> configs = new ArrayList<String>();\r\n        for (InstanceConfig instanceConfig : instanceConfigList) {\r\n            if (!instanceConfig.isEffective()) {\r\n                continue;\r\n            }\r\n            String configKey = instanceConfig.getConfigKey();\r\n            String configValue = instanceConfig.getConfigValue();\r\n            if (StringUtils.isBlank(configValue)) {\r\n                configValue = SPECIAL_EMPTY_STR;\r\n            }\r\n            if (RedisClusterConfigEnum.CLUSTER_CONFIG_FILE.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, port);\r\n            }\r\n            configs.add(combineConfigKeyValue(configKey, configValue));\r\n\r\n        }\r\n        return configs;\r\n    }\r\n\r\n//    @Override\r\n//    public List<String> handleCommonDefaultConfig(int port, int maxMemory) {\r\n//        List<String> configs = new ArrayList<String>();\r\n//        for (RedisConfigEnum config : RedisConfigEnum.values()) {\r\n//            if (RedisConfigEnum.MAXMEMORY.equals(config)) {\r\n//                configs.add(config.getKey() + \" \" + String.format(config.getValue(), maxMemory));\r\n//            } else if (RedisConfigEnum.DBFILENAME.equals(config) ||\r\n//                    RedisConfigEnum.APPENDFILENAME.equals(config) || RedisConfigEnum.PORT.equals(config)) {\r\n//                configs.add(config.getKey() + \" \" + String.format(config.getValue(), port));\r\n//            } else if (RedisConfigEnum.DIR.equals(config)) {\r\n//                configs.add(config.getKey() + \" \" + MachineProtocol.DATA_DIR);\r\n//            } else if (RedisConfigEnum.AUTO_AOF_REWRITE_PERCENTAGE.equals(config)) {\r\n//                //随机比例 auto-aof-rewrite-percentage\r\n//                int percent = 69 + new Random().nextInt(30);\r\n//                configs.add(config.getKey() + \" \" + String.format(RedisConfigEnum.AUTO_AOF_REWRITE_PERCENTAGE.getValue(), percent));\r\n//            } else {\r\n//                configs.add(config.getKey() + \" \" + config.getValue());\r\n//            }\r\n//        }\r\n//        return configs;\r\n//    }\r\n\r\n    @Override\r\n    public List<String> handleSentinelDefaultConfig(String masterName, String host, int port, int sentinelPort) {\r\n        List<String> configs = new ArrayList<String>();\r\n        configs.add(RedisSentinelConfigEnum.PORT.getKey() + \" \" + String.format(RedisSentinelConfigEnum.PORT.getValue(), sentinelPort));\r\n        configs.add(RedisSentinelConfigEnum.DIR.getKey() + \" \" + RedisSentinelConfigEnum.DIR.getValue());\r\n        configs.add(RedisSentinelConfigEnum.MONITOR.getKey() + \" \" + String.format(RedisSentinelConfigEnum.MONITOR.getValue(), masterName, host, port, 1));\r\n        configs.add(RedisSentinelConfigEnum.DOWN_AFTER_MILLISECONDS.getKey() + \" \" + String\r\n                .format(RedisSentinelConfigEnum.DOWN_AFTER_MILLISECONDS.getValue(), masterName));\r\n        configs.add(RedisSentinelConfigEnum.FAILOVER_TIMEOUT.getKey() + \" \" + String\r\n                .format(RedisSentinelConfigEnum.FAILOVER_TIMEOUT.getValue(), masterName));\r\n        configs.add(RedisSentinelConfigEnum.PARALLEL_SYNCS.getKey() + \" \" + String\r\n                .format(RedisSentinelConfigEnum.PARALLEL_SYNCS.getValue(), masterName));\r\n        return configs;\r\n    }\r\n\r\n//    @Override\r\n//    public List<String> handleClusterDefaultConfig(int port) {\r\n//        List<String> configs = new ArrayList<String>();\r\n//        for (RedisClusterConfigEnum config : RedisClusterConfigEnum.values()) {\r\n//            if (config.equals(RedisClusterConfigEnum.CLUSTER_CONFIG_FILE)) {\r\n//                configs.add(RedisClusterConfigEnum.CLUSTER_CONFIG_FILE.getKey() + \" \"\r\n//                        + String.format(RedisClusterConfigEnum.CLUSTER_CONFIG_FILE.getValue(), port));\r\n//            } else {\r\n//                configs.add(config.getKey() + \" \"\r\n//                        + config.getValue());\r\n//            }\r\n//        }\r\n//        return configs;\r\n//    }\r\n\r\n    public SystemResource getRedisVersionByName(String versionName) {\r\n        return resourceDao.getResourceByName(versionName);\r\n    }\r\n\r\n    public String copyRedisConfig(int versionCopyId, SystemResource resource) {\r\n\r\n        SuccessEnum successEnum = null;\r\n        try {\r\n            //1.获取拷贝redis所有配置项\r\n            List<InstanceConfig> configByRedisVersionId = instanceConfigDao.getConfigByRedisVersionId(versionCopyId);\r\n            int index = 1;\r\n            for (InstanceConfig instanceConfig : configByRedisVersionId) {\r\n                logger.info(\"[\" + (index++) + \"] key :\" + instanceConfig.getConfigKey() + \" , value :\" + instanceConfig.getConfigValue());\r\n                //2.插入到新的redis版本模板中\r\n                instanceConfig.setId(0);\r\n                instanceConfig.setVersionId(resource.getId());\r\n                instanceConfigDao.saveOrUpdate(instanceConfig);\r\n            }\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            // 需要当前清除状态\r\n            logger.info(e.getMessage(), e);\r\n        }\r\n\r\n        return successEnum.info();\r\n    }\r\n\r\n    public String updateMachineInstallRedis(String host) {\r\n        // 1.获取所有Redis有效版本\r\n        List<SystemResource> resourceList = resourceDao.getResourceList(ResourceEnum.REDIS.getValue());\r\n        // 2.拼装命令cmd\r\n        String cmds = \"\"; // 执行命令\r\n        String versions = \"\";//版本信息\r\n        String result = \"\"; // ssh结果\r\n        StringBuilder versionsBuilder = new StringBuilder();\r\n        StringBuilder cmdsBuilder = new StringBuilder();\r\n        if (resourceList != null && resourceList.size() > 0) {\r\n            for (SystemResource redisVersion : resourceList) {\r\n                versionsBuilder.append(redisVersion.getName()).append(\";\");\r\n                cmdsBuilder.append(\"cat \").append(redisVersion.getDir()).append(\"/src/redis-server | wc -l \").append(\";\");\r\n            }\r\n            versions = versionsBuilder.toString();\r\n            cmds = cmdsBuilder.toString();\r\n            versions = versions.substring(0, versions.lastIndexOf(\";\"));\r\n        }\r\n        // 3.执行ssh命令\r\n        try {\r\n            result = SSHUtil.execute(host, cmds);\r\n            logger.info(\"execute cmd result：\" + result);\r\n        } catch (SSHException e) {\r\n            e.printStackTrace();\r\n            logger.error(\"check redis-server machine ip:{}  execute cmds:{} error\", host, cmds);\r\n            return SuccessEnum.FAIL.info();\r\n        }\r\n\r\n        // 4.更新机器版本状态\r\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\r\n        if (machineInfo != null) {\r\n            StringBuilder versionStrBuilder = new StringBuilder();\r\n            // 版本#flag;\r\n            String[] versionRes = result.split(\"\\n\");\r\n            for (int index = 0; index < versionRes.length; index++) {\r\n                try {\r\n                    // 0:未安装 1:安装成功 -1:安装过程中异常\r\n                    int flag = NumberUtils.toInt(versionRes[index]) > 0 ? 1 : 0;\r\n                    versionStrBuilder.append(versions.split(\";\")[index])\r\n                            .append(\"#\")\r\n                            .append(flag)\r\n                            .append(\";\");\r\n                } catch (Exception e) {\r\n                    versionStrBuilder.append(versions.split(\";\")[index])\r\n                            .append(\"#-1;\");\r\n                }\r\n            }\r\n            machineInfo.setVersionInstall(versionStrBuilder.toString());\r\n            // 更新机器的版本情况\r\n            machineDao.saveMachineInfo(machineInfo);\r\n        } else {\r\n            logger.error(\"machine ip:{}  is not exist\", host);\r\n            return SuccessEnum.FAIL.info();\r\n        }\r\n        return SuccessEnum.SUCCESS.info();\r\n    }\r\n\r\n    /**\r\n     * 检查redis编译版本\r\n     *\r\n     * @param host\r\n     * @param redisDir\r\n     */\r\n    public Boolean checkMachineRedisVersion(String host, String redisDir) {\r\n        // 1.param check\r\n        if (StringUtils.isEmpty(redisDir)) {\r\n            logger.warn(\"redisDir is empty :{}\", redisDir);\r\n            return false;\r\n        }\r\n        String cmds = \"cat \" + redisDir + \"/src/redis-server | wc -l \";\r\n        // 2.执行ssh命令\r\n        try {\r\n            String result = SSHUtil.execute(host, cmds);\r\n            logger.info(\"execute cmds:{} result:{}\", cmds, result);\r\n            return NumberUtils.toInt(result) > 0 ? true : false;\r\n        } catch (SSHException e) {\r\n            e.printStackTrace();\r\n            logger.error(\"check redis-server machine ip:{}  execute cmds:{} error\", host, cmds);\r\n            return false;\r\n        }\r\n    }\r\n\r\n    public Boolean checkMachineRedisTool(String host, String redisDir) {\r\n        // 1.param check\r\n        if (StringUtils.isEmpty(redisDir)) {\r\n            logger.warn(\"redisDir is empty :{}\", redisDir);\r\n            return false;\r\n        }\r\n        String cmds = \"ls -l \" + redisDir + \"/redis-shake.linux | wc -l \";\r\n        // 2.执行ssh命令\r\n        try {\r\n            String result = SSHUtil.execute(host, cmds);\r\n            logger.info(\"execute cmds:{} result:{}\", cmds, result);\r\n            return NumberUtils.toInt(result) > 0 ? true : false;\r\n        } catch (SSHException e) {\r\n            e.printStackTrace();\r\n            logger.error(\"check checkMachineRedisTool machine ip:{}  execute cmds:{} error\", host, cmds);\r\n            return false;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public Boolean checkAndInstallRedisResource(String host, SystemResource redisResource) {\r\n\r\n        Boolean redisInstallFlag = true;\r\n        // 检测是否安装需要redis版本\r\n        String redisDir = ConstUtils.getRedisDir(redisResource.getName());\r\n        if (!checkMachineRedisVersion(host, redisDir)) {\r\n            installRedisOnMachine(host, redisResource);\r\n            // 验证安装是否成功,最多重试10次\r\n            for (int retry = 1; retry <= WAITING_RETRY_TIMES; retry++) {\r\n                if (checkMachineRedisVersion(host, redisDir)) {\r\n                    logger.info(\"checkAndInstallRedisResource machine:{} install {} ok!\", host, redisResource.getName());\r\n                    redisInstallFlag = true;\r\n                    break;\r\n                }\r\n                redisInstallFlag = false;\r\n                logger.info(\"checkAndInstallRedisResource machine:{} install {} fail status:{} ,retry times :{} !\", host, redisResource.getName(), redisInstallFlag, retry);\r\n                try {\r\n                    TimeUnit.SECONDS.sleep(WAITING_RESOURCE_SECOND);\r\n                } catch (InterruptedException e) {\r\n                    e.printStackTrace();\r\n                }\r\n            }\r\n        }\r\n        return redisInstallFlag;\r\n    }\r\n\r\n    public Boolean checkAndInstallRedisTool(String host, SystemResource redisResource) {\r\n\r\n        Boolean redisInstallFlag = true;\r\n        // 检测是否安装需要redis版本\r\n        String redisDir = ConstUtils.getRedisDir(redisResource.getName());\r\n        if (!checkMachineRedisTool(host, redisDir)) {\r\n            installRedisOnMachine(host, redisResource);\r\n            // 验证安装是否成功,最多重试10次\r\n            for (int retry = 1; retry <= WAITING_RETRY_TIMES; retry++) {\r\n                if (checkMachineRedisTool(host, redisDir)) {\r\n                    logger.info(\"checkAndInstallRedisResource machine:{} install {} ok!\", host, redisResource.getName());\r\n                    redisInstallFlag = true;\r\n                    break;\r\n                }\r\n                redisInstallFlag = false;\r\n                logger.info(\"checkAndInstallRedisResource machine:{} install {} fail status:{} ,retry times :{} !\", host, redisResource.getName(), redisInstallFlag, retry);\r\n                try {\r\n                    TimeUnit.SECONDS.sleep(WAITING_RESOURCE_SECOND);\r\n                } catch (InterruptedException e) {\r\n                    e.printStackTrace();\r\n                }\r\n            }\r\n        }\r\n        return redisInstallFlag;\r\n    }\r\n\r\n    /**\r\n     * @param host\r\n     * @param systemResource\r\n     * @return\r\n     */\r\n    public String installRedisOnMachine(String host, SystemResource systemResource) {\r\n\r\n        // 1.远程仓库获取资源\r\n        SystemResource repository = resourceService.getRepository();\r\n        if (repository == null) {\r\n            logger.error(\"repository is empty :{}  \", repository);\r\n            return SuccessEnum.FAIL.info();\r\n        }\r\n        // 2.资源信息拼接\r\n        String fileName = systemResource.getName() + RedisConstUtils.REDIS_INSTALL_MAKE_PACKAGE_SUFFIX;\r\n        String downloadurl = repository.getUrl() + systemResource.getDir() + \"/\" + fileName;\r\n        String makeFilePath = ConstUtils.REDIS_INSTALL_BASE_DIR + \"/\" + fileName;\r\n        String download_cmd = String.format(DOWNLOAD_CMD, ConstUtils.REDIS_INSTALL_BASE_DIR, downloadurl, makeFilePath, makeFilePath);\r\n        String temp_file = RedisConstUtils.REDIS_SHELL_DIR + \"execute.sh\";\r\n\r\n        String execute_cmd = \"mkdir -p \" + RedisConstUtils.REDIS_SHELL_DIR + \" && echo \\\"\" + download_cmd + \"\\\" > \" + temp_file + \" && sh \" + temp_file + \"> \" + String.format(RedisConstUtils.REDIS_INSTALL_LOG, DateUtil.formatYYYYMMddHHMMss(new Date())) + \" 2>&1 &\";\r\n        // 3.执行ssh\r\n        try {\r\n            SSHTemplate.Result result = sshService.executeWithResult(host, execute_cmd, DOWNLOAD_SECONDS);\r\n            logger.info(\"execute_cmd :{} result:{}\", execute_cmd, result);\r\n        } catch (SSHException e) {\r\n            logger.info(e.getMessage(), e);\r\n            logger.error(\"install redis machine ip:{}  execute cmd:{} error\", host, download_cmd);\r\n            return SuccessEnum.FAIL.info();\r\n        }\r\n        return SuccessEnum.SUCCESS.info();\r\n    }\r\n\r\n    public Map<String, Object> slaveUpdateConfig(long appId, Integer upgradeVersionId, String upgradeVersionName) {\r\n\r\n        Map<String, Object> resultMap = new HashMap<String, Object>();\r\n        SuccessEnum successEnum = null;\r\n        // 1.获取所有配置项\r\n        AppDesc appDesc = appService.getByAppId(appId);\r\n        /**\r\n         *  2.配置更新步骤\r\n         *  2.1 备份slave配置 -> 2.2 slave shutdown -> 2.3 替换slave配置 -> 2.4 slave start\r\n         */\r\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\r\n        // 2.1 备份配置\r\n        String instanceInfo = \"\";\r\n        String instanceLog = \"\";\r\n        StringBuilder instanceInfoBuilder = new StringBuilder();\r\n        StringBuilder instanceLogBuilder = new StringBuilder();\r\n        if (instanceList != null && instanceList.size() > 0) {\r\n            for (InstanceInfo instance : instanceList) {\r\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"slave\")) {\r\n                    // slave节点 & instanceId\r\n                    String ip = instance.getIp();\r\n                    int port = instance.getPort();\r\n                    int mem = instance.getMem();\r\n                    int instanceId = instance.getId();\r\n                    String bakTime = DateUtil.formatYYYYMMddHHMMss(new Date());\r\n\r\n                    // redis实例版本检查,比较版本号\r\n                    String redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\r\n                    int versionTag = Integer.parseInt(redisVersion.substring(redisVersion.lastIndexOf(\".\") + 1));\r\n                    int targetVersionTag = Integer.parseInt(upgradeVersionName.substring(upgradeVersionName.lastIndexOf(\".\") + 1));\r\n                    logger.info(\"current redis version:{} , target redis version:{}\", versionTag, targetVersionTag);\r\n                    if (versionTag == targetVersionTag) {\r\n                        instanceInfoBuilder.append(instance.getIp())\r\n                                .append(\":\")\r\n                                .append(instance.getPort())\r\n                                .append(\" \")\r\n                                .append(instance.getRoleDesc())\r\n                                .append(\" version:\")\r\n                                .append(redisVersion)\r\n                                .append(\" 等于当前版本,不需升级\\n\");\r\n                        //instanceLog += \"<br/>\";\r\n                        instanceLogBuilder.append(\"<br/>\");\r\n                        continue;\r\n                    } else if (versionTag > targetVersionTag) {\r\n                        instanceInfoBuilder.append(instance.getIp())\r\n                                .append(\":\")\r\n                                .append(instance.getPort())\r\n                                .append(\" \")\r\n                                .append(instance.getRoleDesc())\r\n                                .append(\" version:\")\r\n                                .append(redisVersion)\r\n                                .append(\" 高于当前版本,不需升级\\n\");\r\n                        //instanceLog += \"<br/>\";\r\n                        instanceLogBuilder.append(\"<br/>\");\r\n                        continue;\r\n                    }\r\n\r\n                    // 1).备份配置\r\n                    String bakCommonConfig = \"\";\r\n                    Boolean isCluster = true;\r\n                    try {\r\n                        //1.1) 备份配置\r\n                        String confDir = machineCenter.getMachineRelativeDir(instance.getIp(), DirEnum.CONF_DIR.getValue());\r\n                        String confPath = \"\";\r\n                        if (TypeUtil.isRedisCluster(instance.getType())) {\r\n                            bakCommonConfig = \"cp -rf \" + confDir + RedisProtocol.getConfig(port, true) + \" \" + confDir + RedisProtocol.getConfig(port, true) + \".bak\" + bakTime;\r\n                            confPath = confDir + RedisProtocol.getConfig(port, true);\r\n                        } else {\r\n                            bakCommonConfig = \"cp -rf \" + confDir + RedisProtocol.getConfig(port, false) + \" \" + \".conf \" + confDir + RedisProtocol.getConfig(port, false) + \".bak\" + bakTime;\r\n                            confPath = confDir + RedisProtocol.getConfig(port, false);\r\n                            isCluster = false;\r\n                        }\r\n                        SSHUtil.execute(ip, bakCommonConfig);\r\n                    } catch (SSHException e) {\r\n                        logger.error(String.format(\"ip：%s bak config error:%s\", ip, e.getMessage()));\r\n                        resultMap.put(\"message\", \"备份配置异常,请查看日志!\");\r\n                        break;\r\n                    }\r\n                    // 2).关闭redis\r\n                    boolean closeOp = instanceDeployCenter.shutdownExistInstance(appId, instanceId);\r\n                    // 3).生成新配置 & 4).启动redis\r\n                    try {\r\n                        appDesc.setVersionId(upgradeVersionId);\r\n                        boolean bornConf = redisDeployCenter.bornConfigAndRunNode(appDesc, instance, ip, port, mem, isCluster);\r\n                        if (bornConf == false) {\r\n                            resultMap.put(\"message\", \"启动失败,查看日志!\");\r\n                            break;\r\n                        }\r\n                    } catch (Exception e) {\r\n                        resultMap.put(\"message\", \"启动失败,查看日志!\");\r\n                        break;\r\n                    }\r\n                    // 5).记录日志\r\n                    redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\r\n                    instanceInfoBuilder.append(instance.getIp())\r\n                            .append(\":\")\r\n                            .append(instance.getPort())\r\n                            .append(\" \")\r\n                            .append(instance.getRoleDesc())\r\n                            .append(\" version:\")\r\n                            .append(redisVersion)\r\n                            .append(\" 更新成功\\n\");\r\n                    instanceInfo = instanceInfoBuilder.toString();\r\n                    instanceLogBuilder.append(\"<a target='_blank' href=/manage/instance/log?instanceId=\")\r\n                            .append(instance.getId())\r\n                            .append(\">日志</a><br/>\");\r\n                    instanceLog = instanceLogBuilder.toString();\r\n                }\r\n            }\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } else {\r\n            // return error! 实例无slave节    -1: slave节点不够\r\n            successEnum = SuccessEnum.ERROR;\r\n            resultMap.put(\"message\", \"slave节点少于master节点数!\");\r\n        }\r\n        resultMap.put(\"status\", successEnum.value());\r\n        resultMap.put(\"instanceInfo\", instanceInfo);\r\n        resultMap.put(\"instanceLog\", instanceLog);\r\n//        logger.info(\"result:{}\", resultMap);\r\n        return resultMap;\r\n    }\r\n\r\n    public Map<String, Object> slaveFailover(long appId) {\r\n\r\n        Map<String, Object> resultMap = new HashMap<String, Object>();\r\n        SuccessEnum successEnum = null;\r\n        /**\r\n         *  2.配置更新步骤\r\n         *  2.1 备份slave配置 -> 2.2 slave shutdown -> 2.3 替换slave配置 -> 2.4 slave start\r\n         */\r\n        AppDesc appDesc = appService.getByAppId(appId);\r\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\r\n        // 2.1 备份配置\r\n        if (instanceList != null && instanceList.size() > 0) {\r\n            for (InstanceInfo instance : instanceList) {\r\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"slave\")) {\r\n                    // slave节点 & instanceId\r\n                    String ip = instance.getIp();\r\n                    int port = instance.getPort();\r\n\r\n                    int times = 0; //最多重试15次\r\n                    boolean checkFailover = false;//检测是否failover成功  false:轮询检测 true:检测完毕\r\n                    try {\r\n                        boolean failoverStatus = false;\r\n                        if (appDesc.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\r\n                            failoverStatus = redisDeployCenter.clusterFailover(appId, instance.getId(), \"force\");\r\n                        } else if (appDesc.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {\r\n                            failoverStatus = redisDeployCenter.sentinelFailover(appId);\r\n                        }\r\n                        logger.info(\"type:{},{} {} slave execute failover, execute {}\", appDesc.getTypeDesc(), ip, port, failoverStatus);\r\n                        if (!failoverStatus) {\r\n                            // failover 失败\r\n                            resultMap.put(\"status\", SuccessEnum.ERROR.value());\r\n                            resultMap.put(\"message\", \"failover失败，请查看日志!\");\r\n                            return resultMap;\r\n                        }\r\n\r\n                        // 根据主从偏移量 & 新slave节点状态 & 重试次数30次 判断是否failover完成。\r\n                        while (!checkFailover && times++ <= RETRY_TIMES) {\r\n                            Boolean status = redisCenter.getRedisReplicationStatus(appId, ip, port);\r\n                            if (status) {\r\n                                checkFailover = status;\r\n                            } else {\r\n                                TimeUnit.MILLISECONDS.sleep(SLEEP_MILLS);\r\n                                logger.info(\" check slave replication status ,waiting 2s ....\");\r\n                            }\r\n                        }\r\n                    } catch (Exception e) {\r\n                        e.printStackTrace();\r\n                        logger.error(e.getMessage(), e);\r\n                        resultMap.put(\"status\", SuccessEnum.ERROR.value());\r\n                        resultMap.put(\"message\", \"从节点\" + ip + \":\" + port + \" failover异常!\");\r\n                        return resultMap;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\r\n        return resultMap;\r\n    }\r\n\r\n    /**\r\n     * <p>\r\n     * Description: 从节点发起psync之后是否load data\r\n     * </p>\r\n     *\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2018/11/29\r\n     */\r\n    public Boolean slaveIsPsync(long appId, String ip, int port) {\r\n        Boolean psyncFlag = false;// 未开始load data\r\n        int times = 0; //最多重试15次\r\n        // 根据主从偏移量 & 新slave节点状态 & 重试次数30次 判断是否failover完成。\r\n        while (!psyncFlag && times++ <= RETRY_TIMES) {\r\n            try {\r\n                Boolean status = redisCenter.getRedisReplicationStatus(appId, ip, port);\r\n                if (status) {\r\n                    psyncFlag = status;\r\n                } else {\r\n                    TimeUnit.MILLISECONDS.sleep(SLEEP_MILLS);\r\n                    logger.info(\" check slave psync replication status ,waiting 2s ....\");\r\n                }\r\n            } catch (Exception e) {\r\n                e.printStackTrace();\r\n                logger.error(e.getMessage(), e);\r\n            }\r\n        }\r\n        return psyncFlag;\r\n    }\r\n\r\n    /**\r\n     * 组合\r\n     *\r\n     * @param configKey\r\n     * @param configValue\r\n     * @return\r\n     */\r\n    private String combineConfigKeyValue(String configKey, String configValue) {\r\n        return configKey + ConstUtils.SPACE + configValue;\r\n    }\r\n\r\n    /**\r\n     *\r\n     * @param host 新加节点ip\r\n     * @param port 新加节点port\r\n     * @param versionId 部署实例版本\r\n     * @param maxMemory 最大内存\r\n     * @param maxMemoryPolicy 内存淘汰策略\r\n     * @param isCluster 是否集群\r\n     * @param customConfigs 自定义配置\r\n     * @param copyFromConfigMap 主节点实例配置（从主节点拷贝配置值）\r\n     * @return\r\n     */\r\n    @Override\r\n    public List<String> handleRedisConfig(String host, int port, int versionId, int maxMemory, String maxMemoryPolicy, boolean isCluster, List<Pair<String, String>> customConfigs, Map<String, String> copyFromConfigMap) {\r\n\r\n        List<InstanceConfig> instanceConfigList = getByVesionAndType(ConstUtils.CACHE_REDIS_STANDALONE, versionId);\r\n        if (CollectionUtils.isEmpty(instanceConfigList)) {\r\n            instanceConfigList = handleRedisCommonDefaultConfig(port, maxMemory);\r\n        }\r\n        if(isCluster){\r\n            List<InstanceConfig> instanceClusterConfigs = getByVesionAndType(ConstUtils.CACHE_TYPE_REDIS_CLUSTER, versionId);\r\n            if (CollectionUtils.isEmpty(instanceClusterConfigs)) {\r\n                instanceClusterConfigs = handleRedisClusterDefaultConfig(port);\r\n            }\r\n            instanceConfigList.addAll(instanceClusterConfigs);\r\n        }\r\n\r\n        Map<String, List<String>> notExistConfigMap = new HashMap<>();\r\n        Map<String, String> instanceConfigMap = instanceConfigList.stream().filter(instanceConfig -> instanceConfig.isEffective())\r\n                .collect(Collectors.toMap(instanceConfig -> instanceConfig.getConfigKey(), instanceConfig -> instanceConfig.getConfigValue()));\r\n        Map<String, List<String>> customConfigMap = new HashMap<>();\r\n        if(CollectionUtils.isNotEmpty(customConfigs)){\r\n            customConfigs.forEach(customConfig -> {\r\n                if(customConfigMap.containsKey(customConfig.getKey())){\r\n                    customConfigMap.get(customConfig.getKey()).add(customConfig.getValue());\r\n                }else{\r\n                    List<String> confiValues = new ArrayList<>();\r\n                    confiValues.add(customConfig.getValue());\r\n                    customConfigMap.put(customConfig.getKey(), confiValues);\r\n                }\r\n            });\r\n        }\r\n        customConfigMap.keySet().forEach(key -> {\r\n            if (!instanceConfigMap.containsKey(key)) {\r\n                notExistConfigMap.put(key, customConfigMap.get(key));\r\n            }\r\n        });\r\n\r\n        //是否有源节点的配置map\r\n        boolean isCopy = MapUtils.isNotEmpty(copyFromConfigMap);\r\n\r\n        List<String> configs = new ArrayList<String>();\r\n        for (InstanceConfig instanceConfig : instanceConfigList) {\r\n            // 无效配置过滤\r\n            if (!instanceConfig.isEffective()) {\r\n                continue;\r\n            }\r\n            String configKey = instanceConfig.getConfigKey();\r\n            String configValue = instanceConfig.getConfigValue();\r\n            if (StringUtils.isBlank(configValue)) {\r\n                configValue = SPECIAL_EMPTY_STR;\r\n            }\r\n            if (RedisConfigEnum.MAXMEMORY.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, maxMemory);\r\n            } else if (RedisConfigEnum.DBFILENAME.getKey().equals(configKey)\r\n                    || RedisConfigEnum.APPENDFILENAME.getKey().equals(configKey) || RedisConfigEnum.PORT.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, port);\r\n            } else if (RedisConfigEnum.DIR.getKey().equals(configKey)) {\r\n                configValue = machineCenter.getMachineRelativeDir(host, DirEnum.DATA_DIR.getValue());\r\n            } else if (RedisConfigEnum.AUTO_AOF_REWRITE_PERCENTAGE.getKey().equals(configKey)) {\r\n                //随机比例 auto-aof-rewrite-percentage\r\n                int percent = 69 + new Random().nextInt(30);\r\n                configValue = String.format(configValue, percent);\r\n            } else if (RedisConfigEnum.BIND.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, host);\r\n            } else if (RedisConfigEnum.MAXMEMORY_POLICY.getKey().equals(configKey)) {\r\n                if (StringUtils.isNotEmpty(maxMemoryPolicy)) {\r\n                    configValue = maxMemoryPolicy;\r\n                }\r\n            } else if (RedisClusterConfigEnum.CLUSTER_CONFIG_FILE.getKey().equals(configKey)) {\r\n                configValue = String.format(configValue, port);\r\n            } else {\r\n                if(customConfigMap.containsKey(configKey)){\r\n                    List<String> configValues = customConfigMap.get(configKey);\r\n                    if(configValues.size() == 1){\r\n                        configValue = configValues.get(0);\r\n                    } else {\r\n                        IntStream.range(0, configValues.size() - 1).forEach(i -> {\r\n                            configs.add(combineConfigKeyValue(configKey, configValues.get(i)));\r\n                        });\r\n                        configValue = configValues.get(configValues.size() - 1);\r\n                    }\r\n                } else {\r\n                    if(isCopy){\r\n                        if(instanceConfig.getValueType() == BaseConstant.ONE){\r\n                            String copyValue = copyFromConfigMap.get(configKey);\r\n                            if(StringUtils.isNotBlank(copyValue)){\r\n                                configValue = copyValue;\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            configs.add(combineConfigKeyValue(configKey, configValue));\r\n        }\r\n        notExistConfigMap.forEach((configKey, configValues) ->\r\n                configValues.forEach(configValue -> {\r\n                    configs.add(combineConfigKeyValue(configKey, configValue));\r\n                })\r\n        );\r\n        return configs;\r\n    }\r\n\r\n    private List<InstanceConfig> handleRedisCommonDefaultConfig(int port, int maxMemory) {\r\n        List<InstanceConfig> configs = new ArrayList<>();\r\n        for (RedisConfigEnum config : RedisConfigEnum.values()) {\r\n            if (RedisConfigEnum.MAXMEMORY.equals(config)) {\r\n                configs.add(new InstanceConfig(config.getKey(), String.format(config.getValue(), maxMemory)));\r\n            } else if (RedisConfigEnum.DBFILENAME.equals(config) ||\r\n                    RedisConfigEnum.APPENDFILENAME.equals(config) || RedisConfigEnum.PORT.equals(config)) {\r\n                configs.add(new InstanceConfig(config.getKey(), String.format(config.getValue(), port)));\r\n            } else if (RedisConfigEnum.DIR.equals(config)) {\r\n                configs.add(new InstanceConfig(config.getKey(), MachineProtocol.DATA_DIR));\r\n            } else if (RedisConfigEnum.AUTO_AOF_REWRITE_PERCENTAGE.equals(config)) {\r\n                //随机比例 auto-aof-rewrite-percentage\r\n                int percent = 69 + new Random().nextInt(30);\r\n                configs.add(new InstanceConfig(config.getKey(), String.format(RedisConfigEnum.AUTO_AOF_REWRITE_PERCENTAGE.getValue(), percent)));\r\n            } else {\r\n                configs.add(new InstanceConfig(config.getKey(), config.getValue()));\r\n            }\r\n        }\r\n        return configs;\r\n    }\r\n\r\n    private List<InstanceConfig> handleRedisClusterDefaultConfig(int port) {\r\n        List<InstanceConfig> configs = new ArrayList<>();\r\n        for (RedisClusterConfigEnum config : RedisClusterConfigEnum.values()) {\r\n            if (config.equals(RedisClusterConfigEnum.CLUSTER_CONFIG_FILE)) {\r\n                configs.add(new InstanceConfig(RedisClusterConfigEnum.CLUSTER_CONFIG_FILE.getKey(),\r\n                        String.format(RedisClusterConfigEnum.CLUSTER_CONFIG_FILE.getValue(), port)));\r\n            } else {\r\n                configs.add(new InstanceConfig(config.getKey(), config.getValue()));\r\n            }\r\n        }\r\n        return configs;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisDeployCenterImpl.java",
    "content": "package com.sohu.cache.redis.impl;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport com.google.common.collect.UnmodifiableIterator;\nimport com.sohu.cache.constant.AppDescEnum;\nimport com.sohu.cache.constant.ClusterOperateResult;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.dao.MachineDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.protocol.RedisProtocol;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.RedisClusterNode;\nimport com.sohu.cache.redis.RedisConfigTemplateService;\nimport com.sohu.cache.redis.RedisDeployCenter;\nimport com.sohu.cache.redis.enums.DirEnum;\nimport com.sohu.cache.redis.enums.RedisConfigEnum;\nimport com.sohu.cache.redis.util.AuthUtil;\nimport com.sohu.cache.redis.util.JedisUtil;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.ssh.SSHTemplate;\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.IdempotentConfirmer;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.AppTypeEnum;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.RedisOperateEnum;\nimport com.sohu.cache.web.enums.UseTypeEnum;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.ResourceService;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\nimport redis.clients.jedis.*;\nimport redis.clients.jedis.args.ClusterFailoverOption;\nimport redis.clients.jedis.exceptions.JedisDataException;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.Resource;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * Created by yijunzhang on 14-8-25.\n */\n@Service(\"redisDeployCenter\")\npublic class RedisDeployCenterImpl implements RedisDeployCenter {\n    // 重试次数\n    private static int RETRY_TIMES = 10;\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private MachineDao machineDao;\n    @Autowired\n    @Lazy\n    private MachineCenter machineCenter;\n    @Autowired\n    @Lazy\n    private RedisCenter redisCenter;\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private RedisConfigTemplateService redisConfigTemplateService;\n    @Autowired\n    private InstanceDeployCenter instanceDeployCenter;\n    @Resource(name = \"appService\")\n    private AppService appService;\n    @Autowired\n    private ResourceService resourceService;\n    @Autowired\n    private SSHService sshService;\n\n    @Override\n    public boolean deployClusterInstance(long appId, List<RedisClusterNode> clusterNodes, int maxMemory) {\n        if (!isExist(appId)) {\n            return false;\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String host = null;\n        Integer port = null;\n        Map<Jedis, Jedis> clusterMap = new LinkedHashMap<Jedis, Jedis>();\n        for (RedisClusterNode node : clusterNodes) {\n            String masterHost = node.getMasterHost();\n            String slaveHost = node.getSlaveHost();\n            Integer masterPort = machineCenter.getAvailablePort(masterHost, ConstUtils.CACHE_TYPE_REDIS_CLUSTER);\n            if (masterPort == null) {\n                logger.error(\"masterHost={} getAvailablePort is null\", masterHost);\n                return false;\n            }\n\n            if (host == null || port == null) {\n                host = masterHost;\n                port = masterPort;\n            }\n            boolean isMasterRun = runInstance(appDesc, masterHost, masterPort, maxMemory, true);\n            if (!isMasterRun) {\n                return false;\n            }\n            if (StringUtils.isNotBlank(slaveHost)) {\n                Integer slavePort = machineCenter.getAvailablePort(slaveHost, ConstUtils.CACHE_TYPE_REDIS_CLUSTER);\n                if (slavePort == null) {\n                    logger.error(\"slaveHost={} getAvailablePort is null\", slaveHost);\n                    return false;\n                }\n                boolean isSlaveRun = runInstance(appDesc, slaveHost, slavePort, maxMemory, true);\n                if (!isSlaveRun) {\n                    return false;\n                }\n                clusterMap.put(redisCenter.getJedis(appId, masterHost, masterPort),\n                        redisCenter.getJedis(appId, slaveHost, slavePort));\n            } else {\n                clusterMap.put(redisCenter.getJedis(appId, masterHost, masterPort), null);\n            }\n        }\n\n        boolean isCluster;\n        try {\n            isCluster = startCluster(appId, clusterMap);\n            if (!isCluster) {\n                logger.error(\"startCluster create error!\");\n                return false;\n            }\n            for (Map.Entry<Jedis, Jedis> entry : clusterMap.entrySet()) {\n                Jedis master = entry.getKey();\n                Jedis slave = entry.getValue();\n                //保存实例信息 & 触发收集\n                saveInstance(appId, master.getClient().getHost(),\n                        master.getClient().getPort(), maxMemory, ConstUtils.CACHE_TYPE_REDIS_CLUSTER, \"\");\n                if (slave != null) {\n                    saveInstance(appId, slave.getClient().getHost(), slave.getClient().getPort(),\n                            maxMemory, ConstUtils.CACHE_TYPE_REDIS_CLUSTER, \"\");\n                }\n            }\n        } finally {\n            //关闭jedis连接\n            for (Map.Entry<Jedis, Jedis> entry : clusterMap.entrySet()) {\n                entry.getKey().close();\n                if (entry.getValue() != null) {\n                    entry.getValue().close();\n                }\n            }\n        }\n\n        return true;\n    }\n\n    private boolean clusterMeet(Jedis jedis, long appId, String host, int port) {\n        boolean isSingleNode = redisCenter.isSingleClusterNode(appId, host, port);\n        if (!isSingleNode) {\n            logger.error(\"{}:{} isNotSingleNode\", host, port);\n            return false;\n        } else {\n            logger.warn(\"{}:{} isSingleNode\", host, port);\n        }\n\n        String response = jedis.clusterMeet(host, port);\n        boolean isMeet = response != null && response.equalsIgnoreCase(\"OK\");\n        if (!isMeet) {\n            logger.error(\"{}:{} meet error\", host, port);\n            return false;\n        }\n        return true;\n    }\n\n    public boolean startCluster(final long appId, Map<Jedis, Jedis> clusterMap) {\n        final Jedis jedis = new ArrayList<Jedis>(clusterMap.keySet()).get(0);\n        //meet集群节点\n        for (final Map.Entry<Jedis, Jedis> entry : clusterMap.entrySet()) {\n            final Jedis master = entry.getKey();\n            boolean isMeet = new IdempotentConfirmer() {\n\n                @Override\n                public boolean execute() {\n                    boolean isMeet = clusterMeet(jedis, appId, master.getClient().getHost(),\n                            master.getClient().getPort());\n                    if (!isMeet) {\n                        return false;\n                    }\n                    return true;\n                }\n            }.run();\n            if (!isMeet) {\n                return false;\n            }\n            final Jedis slave = entry.getValue();\n            if (slave != null) {\n                isMeet = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        boolean isMeet = clusterMeet(jedis, appId, slave.getClient().getHost(),\n                                slave.getClient().getPort());\n                        if (!isMeet) {\n                            return false;\n                        }\n                        return true;\n                    }\n                }.run();\n                if (!isMeet) {\n                    return false;\n                }\n            }\n        }\n        int masterSize = clusterMap.size();\n        int perSize = (int) Math.ceil(16384 * 1.0D / masterSize);\n        int index = 0;\n        int masterIndex = 0;\n        final ArrayList<Integer> slots = new ArrayList<Integer>();\n        List<Jedis> masters = new ArrayList<Jedis>(clusterMap.keySet());\n        //分配slot\n        for (int slot = 0; slot <= 16383; slot++) {\n            slots.add(slot);\n            if (index++ >= perSize || slot == 16383) {\n                final int[] slotArr = new int[slots.size()];\n                for (int i = 0; i < slotArr.length; i++) {\n                    slotArr[i] = slots.get(i);\n                }\n                final Jedis masterJedis = masters.get(masterIndex++);\n                boolean isSlot = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        String response = masterJedis.clusterAddSlots(slotArr);\n                        boolean isSlot = response != null && response.equalsIgnoreCase(\"OK\");\n                        if (!isSlot) {\n                            return false;\n                        }\n                        return true;\n                    }\n                }.run();\n                if (!isSlot) {\n                    logger.error(\"{}:{} set slots:{}\", masterJedis.getClient().getHost(),\n                            masterJedis.getClient().getPort(), slots);\n                    return false;\n                }\n                slots.clear();\n                index = 0;\n            }\n        }\n        //设置从节点\n        for (Map.Entry<Jedis, Jedis> entry : clusterMap.entrySet()) {\n            final Jedis masterJedis = entry.getKey();\n            final Jedis slaveJedis = entry.getValue();\n            if (slaveJedis == null) {\n                continue;\n            }\n            final String nodeId = getClusterNodeId(masterJedis);\n            boolean isReplicate = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    try {\n                        //等待广播节点\n                        TimeUnit.SECONDS.sleep(2);\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                    }\n                    String response = null;\n                    try {\n                        response = slaveJedis.clusterReplicate(nodeId);\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                    }\n                    boolean isReplicate = response != null && response.equalsIgnoreCase(\"OK\");\n                    if (!isReplicate) {\n                        try {\n                            //等待广播节点\n                            TimeUnit.SECONDS.sleep(2);\n                        } catch (Exception e) {\n                            logger.error(e.getMessage(), e);\n                        }\n                        return false;\n                    }\n                    return true;\n                }\n            }.run();\n\n            if (!isReplicate) {\n                logger.error(\"{}:{} set replicate:{}\", slaveJedis.getClient().getHost(),\n                        slaveJedis.getClient().getPort(), isReplicate);\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public boolean startClusterMaster(final long appId, Map<Jedis, Jedis> clusterMap) {\n        final Jedis jedis = new ArrayList<Jedis>(clusterMap.keySet()).get(0);\n        //meet集群节点\n        for (final Map.Entry<Jedis, Jedis> entry : clusterMap.entrySet()) {\n            final Jedis master = entry.getKey();\n            boolean isMeet = new IdempotentConfirmer() {\n\n                @Override\n                public boolean execute() {\n                    boolean isMeet = clusterMeet(jedis, appId, master.getClient().getHost(),\n                            master.getClient().getPort());\n                    if (!isMeet) {\n                        return false;\n                    }\n                    return true;\n                }\n            }.run();\n            if (!isMeet) {\n                return false;\n            }\n        }\n        int masterSize = clusterMap.size();\n        int perSize = (int) Math.ceil(16384 * 1.0D / masterSize);\n        int index = 0;\n        int masterIndex = 0;\n        final ArrayList<Integer> slots = new ArrayList<Integer>();\n        List<Jedis> masters = new ArrayList<Jedis>(clusterMap.keySet());\n        //分配slot\n        for (int slot = 0; slot <= 16383; slot++) {\n            slots.add(slot);\n            if (index++ >= perSize || slot == 16383) {\n                final int[] slotArr = new int[slots.size()];\n                for (int i = 0; i < slotArr.length; i++) {\n                    slotArr[i] = slots.get(i);\n                }\n                final Jedis masterJedis = masters.get(masterIndex++);\n                boolean isSlot = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        String response = masterJedis.clusterAddSlots(slotArr);\n                        boolean isSlot = response != null && response.equalsIgnoreCase(\"OK\");\n                        if (!isSlot) {\n                            return false;\n                        }\n                        return true;\n                    }\n                }.run();\n                if (!isSlot) {\n                    logger.error(\"{}:{} set slots:{}\", masterJedis.getClient().getHost(),\n                            masterJedis.getClient().getPort(), slots);\n                    return false;\n                }\n                slots.clear();\n                index = 0;\n            }\n        }\n        return true;\n    }\n\n    public boolean startClusterSlave(final long appId, Map<Jedis, Jedis> clusterMap) {\n        final Jedis jedis = new ArrayList<Jedis>(clusterMap.keySet()).get(0);\n        //meet集群节点\n        for (final Map.Entry<Jedis, Jedis> entry : clusterMap.entrySet()) {\n            final Jedis slave = entry.getValue();\n            if (slave != null) {\n                boolean isMeet = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        boolean isMeet = clusterMeet(jedis, appId, slave.getClient().getHost(),\n                                slave.getClient().getPort());\n                        if (!isMeet) {\n                            return false;\n                        }\n                        return true;\n                    }\n                }.run();\n                if (!isMeet) {\n                    return false;\n                }\n            }\n        }\n        //设置从节点\n        for (Map.Entry<Jedis, Jedis> entry : clusterMap.entrySet()) {\n            final Jedis masterJedis = entry.getKey();\n            final Jedis slaveJedis = entry.getValue();\n            if (slaveJedis == null) {\n                continue;\n            }\n            final String nodeId = getClusterNodeId(masterJedis);\n            boolean isReplicate = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    try {\n                        //等待广播节点\n                        TimeUnit.SECONDS.sleep(2);\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                    }\n                    String response = null;\n                    try {\n                        response = slaveJedis.clusterReplicate(nodeId);\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                    }\n                    boolean isReplicate = response != null && response.equalsIgnoreCase(\"OK\");\n                    if (!isReplicate) {\n                        try {\n                            //等待广播节点\n                            TimeUnit.SECONDS.sleep(2);\n                        } catch (Exception e) {\n                            logger.error(e.getMessage(), e);\n                        }\n                        return false;\n                    }\n                    return true;\n                }\n            }.run();\n\n            if (!isReplicate) {\n                logger.error(\"{}:{} set replicate:{}\", slaveJedis.getClient().getHost(),\n                        slaveJedis.getClient().getPort(), isReplicate);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private String getClusterNodeId(Jedis jedis) {\n        try {\n            String infoOutput = jedis.clusterNodes();\n            for (String infoLine : infoOutput.split(\"\\n\")) {\n                if (infoLine.contains(\"myself\")) {\n                    return infoLine.split(\" \")[0];\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    @Override\n    public boolean deploySentinelInstance(long appId, String masterHost, String slaveHost, int maxMemory,\n                                          List<String> sentinelList) {\n        if (!isExist(appId)) {\n            return false;\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        //获取端口\n        Integer masterPort = machineCenter.getAvailablePort(masterHost, ConstUtils.CACHE_REDIS_STANDALONE);\n        if (masterPort == null) {\n            logger.error(\"masterHost={} getAvailablePort is null\", masterHost);\n            return false;\n        }\n        Integer slavePort = machineCenter.getAvailablePort(slaveHost, ConstUtils.CACHE_REDIS_STANDALONE);\n        if (slavePort == null) {\n            logger.error(\"slaveHost={} getAvailablePort is null\", slaveHost);\n            return false;\n        }\n        //运行实例\n        boolean isMasterRun = runInstance(appDesc, masterHost, masterPort, maxMemory, false);\n        if (!isMasterRun) {\n            return false;\n        }\n        boolean isSlaveRun = runInstance(appDesc, slaveHost, slavePort, maxMemory, false);\n        if (!isSlaveRun) {\n            return false;\n        }\n        //添加slaveof配置\n        boolean isSlave = slaveOf(appDesc.getAppId(), masterHost, masterPort, slaveHost, slavePort);\n        if (!isSlave) {\n            return false;\n        }\n\n        //运行sentinel实例组\n        boolean isRunSentinel = runSentinelGroup(appDesc, sentinelList, masterHost, masterPort, appId,\n                appDesc.getAppPassword());\n        if (!isRunSentinel) {\n            return false;\n        }\n\n        //写入instanceInfo 信息\n        saveInstance(appId, masterHost, masterPort, maxMemory,\n                ConstUtils.CACHE_REDIS_STANDALONE, \"\");\n        saveInstance(appId, slaveHost, slavePort, maxMemory, ConstUtils.CACHE_REDIS_STANDALONE, \"\");\n\n        return true;\n    }\n\n    @Override\n    public boolean deployStandaloneInstance(long appId, String host, int maxMemory) {\n        if (!isExist(appId)) {\n            return false;\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        //获取端口\n        Integer port = machineCenter.getAvailablePort(host, ConstUtils.CACHE_REDIS_STANDALONE);\n        if (port == null) {\n            logger.error(\"masterHost={} getAvailablePort is null\", host);\n            return false;\n        }\n\n        //运行实例\n        boolean isMasterRun = runInstance(appDesc, host, port, maxMemory, false);\n        if (!isMasterRun) {\n            return false;\n        }\n\n        //写入instanceInfo 信息\n        saveInstance(appId, host, port, maxMemory, ConstUtils.CACHE_REDIS_STANDALONE,\n                \"\");\n        return true;\n    }\n\n    private InstanceInfo saveInstance(long appId, String host, int port, int maxMemory, int type,\n                                      String cmd) {\n        InstanceInfo instanceInfo = new InstanceInfo();\n        instanceInfo.setAppId(appId);\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n        instanceInfo.setHostId(machineInfo.getId());\n        instanceInfo.setConn(0);\n        instanceInfo.setMem(maxMemory);\n        instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\n        instanceInfo.setPort(port);\n        instanceInfo.setType(type);\n        instanceInfo.setCmd(cmd);\n        instanceInfo.setIp(host);\n        instanceDao.saveInstance(instanceInfo);\n        return instanceInfo;\n    }\n\n    private boolean runSentinelGroup(AppDesc appDesc, List<String> sentinelList, String masterHost, int masterPort,\n                                     long appId, String password) {\n        for (String sentinelHost : sentinelList) {\n            boolean isRun = runSentinel(appDesc, sentinelHost, getMasterName(masterHost, masterPort), masterHost,\n                    masterPort);\n            if (!isRun) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public boolean createRunNode(AppDesc appDesc, String host, Integer port, int maxMemory, boolean isCluster) {\n        return runInstance(appDesc, host, port, maxMemory, isCluster);\n    }\n\n    @Override\n    public String getRedisRunShell(boolean isCluster, String host, int port, String redisDir) {\n        String runShell = machineCenter.isK8sMachine(host) == true\n                ? RedisProtocol.getK8sRunShellByVersion(host, port, isCluster, redisDir)\n                : RedisProtocol.getRunShellByVersion(port, isCluster, redisDir);\n        return runShell;\n    }\n\n    @Override\n    public String getSentinelRunShell(String host, int port, String redisDir) {\n        String runShell = machineCenter.isK8sMachine(host) == true\n                ? RedisProtocol.getK8sSentinelShellByVersion(host, port, redisDir)\n                : RedisProtocol.getSentinelShellByVersion(port, redisDir);\n        return runShell;\n    }\n\n    private boolean runInstance(AppDesc appDesc, String host, Integer port, int maxMemory, boolean isCluster) {\n        return this.runInstance(appDesc, host, port, null, null, maxMemory, isCluster, null);\n    }\n\n    private boolean runInstance(AppDesc appDesc, String host, Integer port, @Nullable String masterHost, @Nullable Integer masterPort, int maxMemory, boolean isCluster, List<Pair<String, String>> customConfigs) {\n        long appId = appDesc.getAppId();\n        String password = appDesc.getAppPassword();\n        // 获取redis路径\n        SystemResource redisResource = resourceService.getResourceById(appDesc.getVersionId());\n        String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName());\n        // Redis资源校验&推包\n        Boolean installStatus = redisConfigTemplateService.checkAndInstallRedisResource(host, redisResource);\n        if (!installStatus) {\n            throw new RuntimeException(String.format(\"machine: %s version :%s is installed %s\", host, redisResource, installStatus));\n        }\n\n        // 生成配置（获取配置，拷贝配置，定制化配置）\n        List<String> configs = handleInstanceConfig(appId, host, port, appDesc.getVersionId(), isCluster, maxMemory, appDesc.getMaxmemoryPolicy(), masterHost, masterPort, customConfigs);\n        if (StringUtils.isNotBlank(password)) {\n            //加两个选项\n            configs.add(RedisConfigEnum.REQUIREPASS.getKey() + ConstUtils.SPACE + password);\n            configs.add(RedisConfigEnum.MASTERAUTH.getKey() + ConstUtils.SPACE + password);\n        }\n        printConfig(configs);\n        String fileName;\n        String runShell;\n        if (isCluster) {\n            runShell = getRedisRunShell(true, host, port, redisDir);\n            fileName = RedisProtocol.getConfig(port, true);\n        } else {\n            runShell = getRedisRunShell(false, host, port, redisDir);\n            fileName = RedisProtocol.getConfig(port, false);\n        }\n\n        String pathFile = machineCenter.createRemoteFile(host, fileName, configs);\n        if (StringUtils.isBlank(pathFile)) {\n            logger.error(\"createFile={} error\", pathFile);\n            return false;\n        }\n        if (isCluster) {\n            //删除cluster节点配置\n            String deleteNodeShell = String.format(\"rm -rf %s/nodes-%s.conf\", machineCenter.getMachineRelativeDir(host, DirEnum.DATA_DIR.getValue()), port);\n            String deleteNodeResult = machineCenter.executeShell(host, deleteNodeShell);\n            if (!ConstUtils.INNER_ERROR.equals(deleteNodeResult)) {\n                logger.warn(\"runDeleteNodeShell={} at host {}\", deleteNodeShell, host);\n            }\n        }\n        //启动实例\n        logger.info(\"masterShell:host={};shell={}\", host, runShell);\n        boolean isMasterShell = machineCenter.startProcessAtPort(host, port, runShell);\n        if (!isMasterShell) {\n            logger.error(\"runShell={} error,{}:{}\", runShell, host, port);\n            return false;\n        }\n        //验证实例\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(\"host:{};port:{} not run\", host, port);\n            return false;\n        } else {\n            logger.warn(\"runInstance-fallback : redis-cli -h {} -p {} shutdown\", host, port);\n        }\n        return true;\n    }\n\n    public boolean bornConfigAndRunNode(AppDesc appDesc, InstanceInfo instanceInfo, String host, Integer port, int maxMemory, boolean isCluster) {\n\n        long appId = appDesc.getAppId();\n        String password = appDesc.getAppPassword();\n        // 获取redis路径\n        SystemResource redisResource = resourceService.getResourceById(appDesc.getVersionId());\n        String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName());\n        // 生成配置\n        List<String> configs = handleInstanceConfig(appId, host, port, appDesc.getVersionId(), isCluster, maxMemory, appDesc.getMaxmemoryPolicy(), null, null, null);\n        if (StringUtils.isNotBlank(password)) {\n            //加两个选项\n            configs.add(RedisConfigEnum.REQUIREPASS.getKey() + ConstUtils.SPACE + password);\n            configs.add(RedisConfigEnum.MASTERAUTH.getKey() + ConstUtils.SPACE + password);\n        }\n        printConfig(configs);\n        String fileName;\n        String runShell;\n        if (isCluster) {\n            runShell = getRedisRunShell(true, host, port, redisDir);\n            fileName = RedisProtocol.getConfig(port, true);\n        } else {\n            runShell = getRedisRunShell(false, host, port, redisDir);\n            fileName = RedisProtocol.getConfig(port, false);\n        }\n\n        // 删除redis配置\n        String deleteConfShell = String.format(\"rm -rf %sredis-cluster-%s.conf\", MachineProtocol.CONF_DIR, port);\n        String deleteConfResult = machineCenter.executeShell(host, deleteConfShell);\n        if (!ConstUtils.INNER_ERROR.equals(deleteConfResult)) {\n            logger.warn(\"runDeleteConfShell={} at host {}\", deleteConfResult, host);\n        }\n        // 写入新的配置文件\n        String pathFile = machineCenter.createRemoteFile(host, fileName, configs);\n        if (StringUtils.isBlank(pathFile)) {\n            logger.error(\"createFile={} error\", pathFile);\n            return false;\n        }\n        //启动实例\n        logger.info(\"masterShell:host={};shell={}\", host, runShell);\n        boolean isMasterShell = machineCenter.startProcessAtPort(host, port, runShell);\n        if (!isMasterShell) {\n            logger.error(\"runShell={} error,{}:{}\", runShell, host, port);\n            return false;\n        }\n        //验证实例\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(\"host:{};port:{} not run\", host, port);\n            return false;\n        } else {\n            logger.warn(\"runInstance-restart success : redis-cli -h {} -p {} \", host, port);\n            instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\n            instanceDao.update(instanceInfo);\n        }\n        return true;\n    }\n\n    private boolean runSentinel(AppDesc appDesc, String sentinelHost, String masterName, String masterHost,\n                                Integer masterPort) {\n        //应用信息\n        long appId = appDesc.getAppId();\n        String password = appDesc.getAppPassword();\n        // 获取redis路径\n//        RedisVersion redisVersion = redisConfigTemplateService.getRedisVersionById(appDesc.getVersionId());\n//        String redisDir = redisVersion == null ? ConstUtils.REDIS_DEFAULT_DIR : redisVersion.getDir();\n        SystemResource redisResource = resourceService.getResourceById(appDesc.getVersionId());\n        String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName());\n        // Redis资源校验&推包\n        Boolean installStatus = redisConfigTemplateService.checkAndInstallRedisResource(sentinelHost, redisResource);\n        if (!installStatus) {\n            throw new RuntimeException(String.format(\"machine: %s version :%s is installed %s\", sentinelHost, redisResource, installStatus));\n        }\n        //启动sentinel实例\n        Integer sentinelPort = machineCenter.getAvailablePort(sentinelHost, ConstUtils.CACHE_REDIS_SENTINEL);\n        if (sentinelPort == null) {\n            logger.error(\"host={} getAvailablePort is null\", sentinelHost);\n            return false;\n        }\n        List<String> masterSentinelConfigs = handleSentinelConfig(masterName, masterHost, masterPort, sentinelHost, sentinelPort, appDesc.getVersionId());\n        if (StringUtils.isNotBlank(password)) {\n            masterSentinelConfigs.add(\"sentinel \" + RedisConfigEnum.AUTH_PASS.getKey() + ConstUtils.SPACE + masterName\n                    + ConstUtils.SPACE + password);\n        }\n\n        printConfig(masterSentinelConfigs);\n        String masterSentinelFileName = RedisProtocol.getConfig(sentinelPort, false);\n        String sentinelPathFile = machineCenter\n                .createRemoteFile(sentinelHost, masterSentinelFileName, masterSentinelConfigs);\n        if (StringUtils.isBlank(sentinelPathFile)) {\n            return false;\n        }\n        String sentinelShell = getSentinelRunShell(sentinelHost, sentinelPort, redisDir);\n        logger.info(\"sentinelMasterShell:{}\", sentinelShell);\n        boolean isSentinelMasterShell = machineCenter.startProcessAtPort(sentinelHost, sentinelPort, sentinelShell);\n        if (!isSentinelMasterShell) {\n            logger.error(\"sentinelMasterShell={} error\", sentinelShell);\n            return false;\n        }\n        //验证实例\n        if (!redisCenter.isRun(sentinelHost, sentinelPort)) {\n            logger.error(\"host:{};port:{} not run\", sentinelHost, sentinelPort);\n            return false;\n        } else {\n            logger.warn(\"runSentinel-fallback : redis-cli -h {} -p {} shutdown\", sentinelHost, sentinelPort);\n        }\n        //save sentinel\n        /**\n         *  getMasterName(masterHost, masterPort) 存在问题:(instance_info表cmd 集群名称不一致问题)\n         *  1).如果已经存在一个sentinel集群,masterName:master-node1(主节点node1,从节点node2),\n         *  2).当主从节点failover(主节点为node2),再添加新的sentinel节点：master-node2\n         */\n        String sentinelMasterName = StringUtils.isEmpty(masterName) ? getMasterName(masterHost, masterPort) : masterName;\n        saveInstance(appId, sentinelHost, sentinelPort, 0, ConstUtils.CACHE_REDIS_SENTINEL, sentinelMasterName);\n        return true;\n    }\n\n//    /**\n//     * 获取redis 基础配置\n//     *\n//     * @param port\n//     * @param maxMemory\n//     * @return\n//     */\n//    public List<String> handleCommonConfig(String host, int port, int maxMemory, Integer maxMemoryPolicyType, int versionId) {\n//        List<String> configs = null;\n//        try {\n//            String maxMemoryPolicy = null;\n//            if(maxMemoryPolicyType != null){\n//                AppDescEnum.MaxmemoryPolicyType policyType = AppDescEnum.MaxmemoryPolicyType.getByType(maxMemoryPolicyType);\n//                if(policyType != null){\n//                    maxMemoryPolicy = policyType.getName();\n//                }\n//            }\n//            configs = redisConfigTemplateService.handleCommonConfig(host, port, maxMemory, maxMemoryPolicy, versionId);\n//        } catch (Exception e) {\n//            logger.error(e.getMessage(), e);\n//        }\n//        if (CollectionUtils.isEmpty(configs)) {\n//            configs = redisConfigTemplateService.handleCommonDefaultConfig(port, maxMemory);\n//        }\n//        return configs;\n//    }\n\n    private List<String> handleSentinelConfig(String masterName, String host, int port, String sentinelHost, int sentinelPort, int versionId) {\n        List<String> configs = null;\n        try {\n            configs = redisConfigTemplateService.handleSentinelConfig(masterName, host, port, sentinelHost, sentinelPort, versionId, null);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (CollectionUtils.isEmpty(configs)) {\n            configs = redisConfigTemplateService.handleSentinelDefaultConfig(masterName, host, port, sentinelPort);\n        }\n        return configs;\n    }\n\n//    private List<String> handleClusterConfig(int port, int versionId) {\n//        List<String> configs = null;\n//        try {\n//            configs = redisConfigTemplateService.handleClusterConfig(port, versionId);\n//        } catch (Exception e) {\n//            logger.error(e.getMessage(), e);\n//        }\n//        if (CollectionUtils.isEmpty(configs)) {\n//            configs = redisConfigTemplateService.handleClusterDefaultConfig(port);\n//        }\n//        return configs;\n//    }\n\n    /**\n     *\n     * @param appId 应用id\n     * @param host 节点ip\n     * @param port 节点port\n     * @param versionId 部署实例版本\n     * @param isCluster 是否集群\n     * @param maxMemory 最大内存\n     * @param maxMemoryPolicyType 内存淘汰策略\n     * @param masterHost 主节点ip\n     * @param masterPort 主节点port\n     * @param customConfigs 自定义配置\n     * @return\n     */\n    private List<String> handleInstanceConfig(long appId, String host, int port, int versionId, boolean isCluster, int maxMemory, Integer maxMemoryPolicyType, @Nullable String masterHost, @Nullable Integer masterPort, List<Pair<String, String>> customConfigs) {\n        //获取主节点的配置\n        Map<String, String> configInfoMap = new HashMap<>();\n        if(StringUtils.isNotBlank(masterHost) && masterPort != null && masterPort > 0){\n            configInfoMap = this.getConfigInfo(appId, masterHost, masterPort);\n        }\n\n        String maxMemoryPolicy = null;\n        if(maxMemoryPolicyType != null){\n            AppDescEnum.MaxmemoryPolicyType policyType = AppDescEnum.MaxmemoryPolicyType.getByType(maxMemoryPolicyType);\n            if(policyType != null){\n                maxMemoryPolicy = policyType.getName();\n            }\n        }\n        //获取模板配置\n        try {\n            return redisConfigTemplateService.handleRedisConfig(host, port, versionId, maxMemory, maxMemoryPolicy, isCluster, customConfigs, configInfoMap);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    private Map<String, String> getConfigInfo(long appId, String host, int port) {\n        Map<String, String> configInfo = new HashMap<>();\n        Jedis jedis = redisCenter\n                .getAdminJedis(appId, host, port, Protocol.DEFAULT_TIMEOUT * 3, Protocol.DEFAULT_TIMEOUT * 3);\n        try {\n            List<String> values = jedis.configGet(\"*\");\n            if (values == null || values.size() < 1) {\n                return configInfo;\n            }\n            for (int i = 0; i < values.size(); i += 2) {\n                configInfo.put(values.get(i), values.get(i + 1));\n            }\n            return configInfo;\n        } catch (Exception e) {\n            throw new RuntimeException(e.getMessage(), e);\n        } finally {\n            jedis.close();\n        }\n    }\n\n    private String getMasterName(String host, int port) {\n        String masterSentinelName = String.format(\"sentinel-%s-%s\", host, port);\n        return masterSentinelName;\n    }\n\n    private void printConfig(List<String> masterConfigs) {\n        logger.info(\"==================redis-{}-config==================\", masterConfigs);\n        for (String line : masterConfigs) {\n            logger.info(line);\n        }\n    }\n\n    private boolean isExist(long appId) {\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        if (instanceInfos != null && instanceInfos.size() > 0) {\n            logger.error(\"appId={} instances is exist , instanceInfos={}\", appId, instanceInfos);\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public boolean modifyAppConfig(long appId, String parameter, String value) {\n        List<InstanceInfo> list = instanceDao.getInstListByAppId(appId);\n        if (list == null || list.isEmpty()) {\n            logger.error(String.format(\"appId=%s no instances\", appId));\n            return false;\n        }\n        for (InstanceInfo instance : list) {\n            int type = instance.getType();\n            if (!TypeUtil.isRedisType(type)) {\n                logger.error(\"appId={};type={};is not redisType\", appId, type);\n                return false;\n            }\n            //忽略sentinel\n            if (TypeUtil.isRedisSentinel(type)) {\n                continue;\n            }\n            //忽略下线\n            if (instance.isOffline()) {\n                continue;\n            }\n            String host = instance.getIp();\n            int port = instance.getPort();\n            if (!modifyInstanceConfig(appId, host, port, parameter, value)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public boolean modifyInstanceConfig(final long appId, final String host, final int port, final String parameter,\n                                        final String value) {\n        final Jedis jedis = redisCenter.getAdminJedis(appId, host, port, 5000, 5000);\n        try {\n            boolean isConfig = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    boolean isRun = redisCenter.isRun(appId, host, port);\n                    if (!isRun) {\n                        logger.warn(\"modifyInstanceConfig{}:{} is shutdown\", host, port);\n                        return true;\n                    }\n                    String result = jedis.configSet(parameter, value);\n                    boolean isConfig = result != null && result.equalsIgnoreCase(\"OK\");\n                    if (!isConfig) {\n                        logger.error(String.format(\"modifyConfigError:ip=%s,port=%s,result=%s\", host, port, result));\n                        return false;\n                    }\n                    return isConfig;\n                }\n            }.run();\n            boolean isRewrite = redisCenter.configRewrite(appId, host, port);\n            if (!isRewrite) {\n                logger.error(\"configRewrite={}:{} failed\", host, port);\n            }\n            return isConfig;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n    }\n\n    @Override\n    public String getInstanceConfig(final long appId, final String host, final int port, final String parameter) {\n        try (Jedis jedis = redisCenter.getAdminJedis(appId, host, port, 5000, 5000);){\n            List<String> values = jedis.configGet(parameter);\n            if (values == null || values.size() < 1) {\n                return null;\n            }\n            return values.get(1);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public boolean addSentinel(long appId, String sentinelHost) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        JedisSentinelPool jedisSentinelPool = redisCenter.getJedisSentinelPool(appDesc);\n        if (jedisSentinelPool == null) {\n            return false;\n        }\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        String masterName = null;\n        for (Iterator<InstanceInfo> i = instanceInfos.iterator(); i.hasNext(); ) {\n            InstanceInfo instanceInfo = i.next();\n            if (instanceInfo.getType() != ConstUtils.CACHE_REDIS_SENTINEL) {\n                i.remove();\n                continue;\n            }\n            if (masterName == null && StringUtils.isNotBlank(instanceInfo.getCmd())) {\n                masterName = instanceInfo.getCmd();\n            }\n        }\n        Jedis jedis = null;\n        String masterHost = null;\n        Integer masterPort = null;\n        try {\n            jedis = jedisSentinelPool.getResource();\n            masterHost = jedis.getClient().getHost();\n            masterPort = jedis.getClient().getPort();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n                jedisSentinelPool.destroy();\n            }\n        }\n        boolean isRun = runSentinel(appDesc, sentinelHost, masterName, masterHost, masterPort);\n        if (!isRun) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public RedisOperateEnum addSlotsFailMaster(final long appId, int lossSlotsInstanceId, final String newMasterHost)\n            throws Exception {\n        // 1.参数、应用、实例信息确认\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(lossSlotsInstanceId > 0);\n        Assert.isTrue(StringUtils.isNotBlank(newMasterHost));\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        Assert.isTrue(appDesc != null);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisCluster(type)) {\n            logger.error(\"{} is not redis cluster type\", appDesc);\n            return RedisOperateEnum.FAIL;\n        }\n        //获取失联slots的实例信息\n        InstanceInfo lossSlotsInstanceInfo = instanceDao.getInstanceInfoById(lossSlotsInstanceId);\n        Assert.isTrue(lossSlotsInstanceInfo != null);\n\n        // 2.获取集群中一个健康的master作为clusterInfo Nodes的数据源\n        List<InstanceInfo> allInstanceInfo = redisCenter.getAllHealthyInstanceInfo(appId);\n        //InstanceInfo sourceMasterInstance = redisCenter.getHealthyInstanceInfo(appId);\n        if (allInstanceInfo == null || allInstanceInfo.size() == 0) {\n            logger.warn(\"appId {} get all instance is zero\", appId);\n            return RedisOperateEnum.FAIL;\n        }\n        //默认获取第一个master节点\n        InstanceInfo sourceMasterInstance = allInstanceInfo.get(0);\n        // 并未找到一个合适的实例可以\n        if (sourceMasterInstance == null) {\n            logger.warn(\"appId {} does not have right instance\", appId);\n            return RedisOperateEnum.FAIL;\n        }\n\n        // 3. 找到丢失的slots，如果没找到就说明集群正常，直接返回\n        String healthyMasterHost = sourceMasterInstance.getIp();\n        int healthyMasterPort = sourceMasterInstance.getPort();\n        int healthyMasterMem = sourceMasterInstance.getMem();\n        // 3.1 查看整个集群中是否有丢失的slots\n        List<Integer> allLossSlots = redisCenter.getClusterLossSlots(appId, healthyMasterHost, healthyMasterPort);\n        if (CollectionUtils.isEmpty(allLossSlots)) {\n            logger.warn(\"appId {} all slots is regular and assigned\", appId);\n            return RedisOperateEnum.ALREADY_SUCCESS;\n        }\n        // 3.2 查看目标实例丢失slots\n        final List<Integer> clusterLossSlots = redisCenter\n                .getInstanceSlots(appId, healthyMasterHost, healthyMasterPort, lossSlotsInstanceInfo.getIp(),\n                        lossSlotsInstanceInfo.getPort());\n        // 4.开启新的节点\n        // 4.1 从newMasterHost找到可用的端口newMasterPort\n        final Integer newMasterPort = machineCenter\n                .getAvailablePort(newMasterHost, ConstUtils.CACHE_TYPE_REDIS_CLUSTER);\n        if (newMasterPort == null) {\n            logger.error(\"host={} getAvailablePort is null\", newMasterHost);\n            return RedisOperateEnum.FAIL;\n        }\n        // 4.2 按照sourceMasterInstance的内存启动\n        boolean isRun = runInstance(appDesc, newMasterHost, newMasterPort, healthyMasterMem, true);\n        if (!isRun) {\n            logger.error(\"{}:{} is not run\", newMasterHost, newMasterPort);\n            return RedisOperateEnum.FAIL;\n        }\n        // 4.3 拷贝配置\n        boolean isCopy = copyCommonConfig(appId, healthyMasterHost, healthyMasterPort, newMasterHost, newMasterPort);\n        if (!isCopy) {\n            logger.error(\"{}:{} copy config {}:{} is error\", healthyMasterHost, healthyMasterPort, newMasterHost,\n                    newMasterPort);\n            return RedisOperateEnum.FAIL;\n        }\n\n        // 5. meet\n        boolean isClusterMeet = false;\n        try (Jedis sourceMasterJedis = redisCenter.getAdminJedis(appId, healthyMasterHost, healthyMasterPort);){\n            isClusterMeet = clusterMeet(sourceMasterJedis, appId, newMasterHost, newMasterPort);\n            if (!isClusterMeet) {\n                logger.error(\"{}:{} cluster is failed\", newMasterHost, newMasterPort);\n                return RedisOperateEnum.FAIL;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (!isClusterMeet) {\n            logger.warn(\"{}:{} meet {}:{} is fail\", healthyMasterHost, healthyMasterPort, newMasterHost, newMasterPort);\n            return RedisOperateEnum.FAIL;\n        }\n\n        // 6. 分配slots\n        //String addSlotsResult = \"\";\n        Jedis newMasterJedis = null;\n        //Jedis healthyMasterJedis = null;\n        try {\n            newMasterJedis = redisCenter.getJedis(appId, newMasterHost, newMasterPort, 5000, 5000);\n            //获取新的补救节点的nodeid\n            final String nodeId = getClusterNodeId(newMasterJedis);\n            //healthyMasterJedis = redisCenter.getJedis(appId, healthyMasterHost, healthyMasterPort, 5000, 5000);\n            // 新加节点也需要addsolts\n            InstanceInfo addInstance = new InstanceInfo();\n            addInstance.setIp(newMasterHost);\n            addInstance.setPort(newMasterPort);\n            allInstanceInfo.add(addInstance);\n\n            for (InstanceInfo instance : allInstanceInfo) {\n                final Jedis masterJedis = redisCenter.getJedis(appId, instance.getIp(), instance.getPort(), 5000, 5000);\n                logger.warn(\"{}:{} set {}:{} slots start\", instance.getIp(), instance.getPort(), newMasterHost,\n                        newMasterPort);\n                // 1. nodes meet 2. nodes set\n                boolean setSlotStatus = true;\n                try {\n                    setSlotStatus = new IdempotentConfirmer() {\n                        @Override\n                        public boolean execute() {\n                            String setSlotsResult = null;\n                            try {\n                                for (final Integer slot : clusterLossSlots) {\n                                    setSlotsResult = masterJedis.clusterSetSlotNode(slot, nodeId);\n                                    logger.warn(\"set slot {}, result is {}\", slot, setSlotsResult);\n                                }\n                            } catch (JedisDataException exception) {\n                                logger.warn(exception.getMessage());\n                                // unkown jedis node\n                                try {\n                                    TimeUnit.SECONDS.sleep(2);\n                                } catch (InterruptedException e) {\n                                    logger.error(e.getMessage(), e);\n                                }\n                            }\n                            // result\n                            boolean nodeSetStatus = setSlotsResult != null && setSlotsResult.equalsIgnoreCase(\"OK\");\n                            return nodeSetStatus;\n                        }\n                    }.run();\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                } finally {\n                    //close jedis\n                    if (masterJedis != null) {\n                        masterJedis.close();\n                    }\n                }\n                // set slots result\n                if (setSlotStatus) {\n                    logger.warn(\"{}:{} set {}:{} slots success\", instance.getIp(), instance.getPort(), newMasterHost,\n                            newMasterPort);\n                } else {\n                    logger.warn(\"{}:{} set {}:{} slots faily\", instance.getIp(), instance.getPort(), newMasterHost,\n                            newMasterPort);\n                    return RedisOperateEnum.FAIL;\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n            if (newMasterJedis != null) {\n                newMasterJedis.close();\n            }\n        }\n\n        // 7.保存实例信息、并开启收集信息\n        saveInstance(appId, newMasterHost, newMasterPort, healthyMasterMem, ConstUtils.CACHE_TYPE_REDIS_CLUSTER, \"\");\n\n        // 休息一段时间，同步clusterNodes信息\n        TimeUnit.SECONDS.sleep(2);\n\n        // 8.最终打印出当前还没有补充的slots\n        List<Integer> currentLossSlots = redisCenter.getClusterLossSlots(appId, newMasterHost, newMasterPort);\n        logger.warn(\"appId {} failslots assigned unsuccessfully, lossslots is {}\", appId, currentLossSlots);\n\n        return RedisOperateEnum.OP_SUCCESS;\n    }\n\n    @Override\n    public boolean addSlave(long appId, int instanceId, final String slaveHost) {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(instanceId > 0);\n        Assert.isTrue(StringUtils.isNotBlank(slaveHost));\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        Assert.isTrue(appDesc != null);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisType(type)) {\n            logger.error(\"{} is not redis type\", appDesc);\n            return false;\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        Assert.isTrue(instanceInfo != null);\n        String masterHost = instanceInfo.getIp();\n        int masterPort = instanceInfo.getPort();\n        final Integer slavePort = machineCenter.getAvailablePort(slaveHost, ConstUtils.CACHE_REDIS_STANDALONE);\n        if (slavePort == null) {\n            logger.error(\"host={} getAvailablePort is null\", slaveHost);\n            return false;\n        }\n        List<Pair<String, String>> customConfigs = new ArrayList<>();\n        boolean isRun;\n        if (TypeUtil.isRedisCluster(type)) {\n            isRun = runInstance(appDesc, slaveHost, slavePort, masterHost, masterPort, instanceInfo.getMem(), true, customConfigs);\n        } else {\n            isRun = runInstance(appDesc, slaveHost, slavePort, masterHost, masterPort, instanceInfo.getMem(), false, customConfigs);\n        }\n\n        if (!isRun) {\n            logger.error(\"{}:{} is not run\", slaveHost, slavePort);\n            return false;\n        }\n\n        // 注意redis高低版本复制config的问题\n        boolean isCopy = copyCommonConfig(appId, masterHost, masterPort, slaveHost, slavePort);\n        if (!isCopy) {\n            logger.error(\"{}:{} copy config {}:{} is error\", masterHost, masterPort, slaveHost, slavePort);\n            return false;\n        }\n        if (TypeUtil.isRedisCluster(type)) {\n            final Jedis masterJedis = redisCenter\n                    .getJedis(appId, masterHost, masterPort, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);\n            final Jedis slaveJedis = redisCenter\n                    .getJedis(appId, slaveHost, slavePort, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);\n            try {\n                boolean isClusterMeet = clusterMeet(masterJedis, appId, slaveHost, slavePort);\n                if (!isClusterMeet) {\n                    logger.error(\"{}:{} cluster is failed\", slaveHost, slaveHost);\n                    return isClusterMeet;\n                }\n                final String nodeId = redisCenter.getNodeId(appId, masterHost, masterPort);\n                if (StringUtils.isBlank(nodeId)) {\n                    logger.error(\"{}:{} getNodeId failed\", masterHost, masterPort);\n                    return false;\n                }\n\n                boolean isClusterReplicate = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        try {\n                            //等待广播节点\n                            TimeUnit.SECONDS.sleep(2);\n                        } catch (Exception e) {\n                            logger.error(e.getMessage(), e);\n                        }\n                        String response = slaveJedis.clusterReplicate(nodeId);\n                        logger.info(\"clusterReplicate-{}:{}={}\", slaveHost, slavePort, response);\n                        return response != null && response.equalsIgnoreCase(\"OK\");\n                    }\n                }.run();\n                if (!isClusterReplicate) {\n                    logger.error(\"{}:{} clusterReplicate {} is failed \", slaveHost, slavePort, nodeId);\n                    return false;\n                }\n\n                // 工具迁移添加slave节点时 master可能出现阻塞\n                int times = 1; //最多重试10次\n                boolean blockingFlag = true;//检测master节点是否阻塞  true:阻塞 false:不阻塞\n                while (blockingFlag && times++ <= RETRY_TIMES) {\n                    try {\n                        String masterPong = masterJedis.ping();\n                        String slavePong = slaveJedis.ping();\n                        logger.info(\"master ping :{}\", masterPong);\n                        logger.info(\"slave ping :{}\", slavePong);\n                        blockingFlag = false;\n                    } catch (Exception e) {\n                        try {\n                            TimeUnit.SECONDS.sleep(2);\n                        } catch (InterruptedException e1) {\n                            e1.printStackTrace();\n                        }\n                        logger.error(\" waiting  master/slave blocking status ,waiting 2s .... exception:{}\", e.getMessage());\n                    }\n                }\n                //保存配置\n                masterJedis.clusterSaveConfig();\n                slaveJedis.clusterSaveConfig();\n                redisCenter.configRewrite(appId, masterHost, masterPort);\n                redisCenter.configRewrite(appId, slaveHost, slavePort);\n            } finally {\n                masterJedis.close();\n                slaveJedis.close();\n            }\n        } else {\n            boolean isSlave = slaveOf(appId, masterHost, masterPort, slaveHost, slavePort);\n            if (!isSlave) {\n                logger.error(\"{}:{} sync {}:{} is error\", slaveHost, slavePort, masterHost, masterPort);\n                return false;\n            }\n        }\n\n        //写入instanceInfo 信息\n        if (TypeUtil.isRedisCluster(type)) {\n            saveInstance(appId, slaveHost, slavePort, instanceInfo.getMem(),\n                    ConstUtils.CACHE_TYPE_REDIS_CLUSTER, \"\");\n        } else {\n            saveInstance(appId, slaveHost, slavePort, instanceInfo.getMem(),\n                    ConstUtils.CACHE_REDIS_STANDALONE, \"\");\n        }\n        return true;\n    }\n\n    /**\n     * 为主节点添加从节点\n     *\n     * @param appDesc\n     * @param masterHost\n     * @param masterPort\n     * @param mem 单位Mb\n     * @param slaveInstance 配置有slaveHost，添加成功，将设置slavePort\n     * @return\n     */\n    public boolean addSlave(AppDesc appDesc, String masterHost, int masterPort, int mem, InstanceInfo slaveInstance) throws Exception{\n        Assert.isTrue(appDesc != null);\n        Assert.isTrue(StringUtils.isNotBlank(masterHost));\n        Assert.isTrue(masterPort > 0);\n        Assert.isTrue(mem > 0);\n        Assert.isTrue(StringUtils.isNotBlank(slaveInstance.getIp()));\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisType(type)) {\n            logger.error(\"{} is not redis type\", appDesc);\n            return false;\n        }\n        final Integer slavePort = machineCenter.getAvailablePort(slaveInstance.getIp(), ConstUtils.CACHE_REDIS_STANDALONE);\n        if (slavePort == null) {\n            logger.error(\"host={} getAvailablePort is null\", slaveInstance.getIp());\n            return false;\n        }\n        List<Pair<String, String>> customConfigs = new ArrayList<>();\n        boolean isRun;\n        if (TypeUtil.isRedisCluster(type)) {\n            isRun = runInstance(appDesc, slaveInstance.getIp(), slavePort, masterHost, masterPort, mem, true, customConfigs);\n        } else {\n            isRun = runInstance(appDesc, slaveInstance.getIp(), slavePort, masterHost, masterPort, mem, false, customConfigs);\n        }\n\n        if (!isRun) {\n            logger.error(\"{}:{} is not run\", slaveInstance.getIp(), slavePort);\n            return false;\n        }\n\n//        // 注意redis高低版本复制config的问题\n//        boolean isCopy = copyCommonConfig(appId, masterHost, masterPort, slaveHost, slavePort);\n//        if (!isCopy) {\n//            logger.error(\"{}:{} copy config {}:{} is error\", masterHost, masterPort, slaveHost, slavePort);\n//            return false;\n//        }\n        if (TypeUtil.isRedisCluster(type)) {\n            final Jedis masterJedis = redisCenter\n                    .getAdminJedis(appDesc.getAppId(), masterHost, masterPort, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);\n            final Jedis slaveJedis = redisCenter\n                    .getAdminJedis(appDesc.getAppId(), slaveInstance.getIp(), slavePort, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);\n            try {\n                boolean isClusterMeet = clusterMeet(masterJedis, appDesc.getAppId(), slaveInstance.getIp(), slavePort);\n                if (!isClusterMeet) {\n                    logger.error(\"{}:{} cluster is failed\", slaveInstance.getIp(), slavePort);\n                    return isClusterMeet;\n                }\n                final String nodeId = redisCenter.getNodeId(appDesc.getAppId(), masterHost, masterPort);\n                if (StringUtils.isBlank(nodeId)) {\n                    logger.error(\"{}:{} getNodeId failed\", masterHost, masterPort);\n                    return false;\n                }\n\n                boolean isClusterReplicate = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        try {\n                            //等待广播节点\n                            TimeUnit.SECONDS.sleep(2);\n                        } catch (Exception e) {\n                            logger.error(e.getMessage(), e);\n                        }\n                        String response = slaveJedis.clusterReplicate(nodeId);\n                        logger.info(\"clusterReplicate-{}:{}={}\", slaveInstance.getIp(), slavePort, response);\n                        return response != null && response.equalsIgnoreCase(\"OK\");\n                    }\n                }.run();\n                if (!isClusterReplicate) {\n                    logger.error(\"{}:{} clusterReplicate {} is failed \", slaveInstance.getIp(), slavePort, nodeId);\n                    return false;\n                }\n\n                // 工具迁移添加slave节点时 master可能出现阻塞\n                int times = 1; //最多重试10次\n                boolean blockingFlag = true;//检测master节点是否阻塞  true:阻塞 false:不阻塞\n                while (blockingFlag && times++ <= RETRY_TIMES) {\n                    try {\n                        String masterPong = masterJedis.ping();\n                        String slavePong = slaveJedis.ping();\n                        logger.info(\"master ping :{}\", masterPong);\n                        logger.info(\"slave ping :{}\", slavePong);\n                        blockingFlag = false;\n                    } catch (Exception e) {\n                        try {\n                            TimeUnit.SECONDS.sleep(2);\n                        } catch (InterruptedException e1) {\n                            e1.printStackTrace();\n                        }\n                        logger.error(\" waiting  master/slave blocking status ,waiting 2s .... exception:{}\", e.getMessage());\n                    }\n                }\n                //保存配置\n                masterJedis.clusterSaveConfig();\n                slaveJedis.clusterSaveConfig();\n                redisCenter.configRewrite(appDesc.getAppId(), masterHost, masterPort);\n                redisCenter.configRewrite(appDesc.getAppId(), slaveInstance.getIp(), slavePort);\n            } finally {\n                masterJedis.close();\n                slaveJedis.close();\n            }\n        } else {\n            boolean isSlave = slaveOf(appDesc.getAppId(), masterHost, masterPort, slaveInstance.getIp(), slavePort);\n            if (!isSlave) {\n                logger.error(\"{}:{} sync {}:{} is error\", slaveInstance.getIp(), slavePort, masterHost, masterPort);\n                return false;\n            }\n        }\n        slaveInstance.setPort(slavePort);\n        //写入instanceInfo 信息\n        if (TypeUtil.isRedisCluster(type)) {\n            saveInstance(appDesc.getAppId(), slaveInstance.getIp(), slavePort, mem,\n                    ConstUtils.CACHE_TYPE_REDIS_CLUSTER, \"\");\n        } else {\n            saveInstance(appDesc.getAppId(), slaveInstance.getIp(), slavePort, mem,\n                    ConstUtils.CACHE_REDIS_STANDALONE, \"\");\n        }\n        return true;\n    }\n\n    @Override\n    public String genSlaveIp(long appId, int instanceId) throws Exception {\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        String ip = instanceInfo.getIp();\n        MachineInfo machineInfo = machineCenter.getMachineInfoByIp(ip);\n        //先从混合部署机器进行选择\n        List<MachineMemStatInfo> machineMixCandiList = machineCenter.getAllValidMachineMem(Arrays.asList(ip), machineInfo.getRoom(), UseTypeEnum.Machine_mix.getValue());\n        String slaveIp = this.getSlaveIp(machineMixCandiList, ip, machineInfo, instanceInfo);\n        if(StringUtils.isEmpty(slaveIp)){\n            //再从专用部署机器进行选择\n            List<MachineMemStatInfo> machineSpecialCandiList = machineCenter.getAllValidMachineMem(Arrays.asList(ip), machineInfo.getRoom(), UseTypeEnum.Machine_special.getValue());\n            slaveIp = this.getSlaveIp(machineSpecialCandiList, ip, machineInfo, instanceInfo);\n        }\n        if(StringUtils.isEmpty(slaveIp)){\n            //再从测试部署机器进行选择\n            List<MachineMemStatInfo> machineTestCandiList = machineCenter.getAllValidMachineMem(Arrays.asList(ip), machineInfo.getRoom(), UseTypeEnum.Machine_test.getValue());\n            slaveIp = this.getSlaveIp(machineTestCandiList, ip, machineInfo, instanceInfo);\n        }\n        //若该应用下的机器没有满足条件的，选择其他机器\n        return slaveIp;\n    }\n\n    private String getSlaveIp(List<MachineMemStatInfo> machineList, String ip, MachineInfo machineInfo, InstanceInfo instanceInfo){\n        if (CollectionUtils.isNotEmpty(machineList)) {\n            List<String> machineResList = getMachineCandi(machineList, ip, instanceInfo.getMem(), machineInfo.getRack());\n            if (CollectionUtils.isNotEmpty(machineResList)) {\n                int random = new Random().nextInt(machineResList.size());\n                return machineResList.get(random);\n            }\n        }\n        return \"\";\n    }\n\n    private boolean isSameRealMachine(String ip1, String ip2) {\n        if (ip1.equals(ip2)) return true;\n        MachineInfo machine1 = machineDao.getMachineInfoByIp(ip1);\n        MachineInfo machine2 = machineDao.getMachineInfoByIp(ip2);\n        String realIp1 = machine1.getVirtual() == 1 ? machine1.getRealIp() : machine1.getIp();\n        String realIp2 = machine2.getVirtual() == 1 ? machine2.getRealIp() : machine2.getIp();\n        return realIp1.equals(realIp2);\n    }\n\n    private List<String> getMachineCandi(List<MachineMemStatInfo> machineCandiList, String ip, int reqMem, String rack) {\n        return machineCandiList.stream()\n                .filter(machineCandi -> !isSameRealMachine(ip, machineCandi.getIp()))\n                .filter(machineCandi -> StringUtils.isEmpty(rack) || StringUtils.isEmpty(machineCandi.getRack()) ||\n                        !rack.equals(machineCandi.getRack()))\n                .filter(machineCandi -> (machineCandi.getMem() * 1024 - machineCandi.getUsedMem() / 1024 / 1024) > reqMem)\n                .map(MachineMemStatInfo::getIp)\n                .collect(Collectors.toList());\n    }\n\n    @Override\n    public boolean sentinelFailover(final long appId) throws Exception {\n        Assert.isTrue(appId > 0);\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        Assert.isTrue(appDesc != null);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisSentinel(type)) {\n            logger.warn(\"app={} is not sentinel\", appDesc);\n            return false;\n        }\n        final List<InstanceInfo> instanceList = instanceDao.getEffectiveInstListByAppId(appId);\n        if (instanceList == null || instanceList.isEmpty()) {\n            logger.warn(\"app={} instances is empty\", appId);\n            return false;\n        }\n        for (InstanceInfo instanceInfo : instanceList) {\n            int instanceType = instanceInfo.getType();\n            if (TypeUtil.isRedisSentinel(instanceType)) {\n                final String host = instanceInfo.getIp();\n                final int port = instanceInfo.getPort();\n                final String masterName = instanceInfo.getCmd();\n                if (StringUtils.isBlank(masterName)) {\n                    logger.warn(\"{} cmd is null\", instanceInfo);\n                    continue;\n                }\n                boolean isRun = redisCenter.isRun(host, port);\n                if (!isRun) {\n                    logger.warn(\"{}:{} is not run\", host, port);\n                    continue;\n                }\n                boolean isSentinelFailOver = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        Jedis jedis = redisCenter.getJedis(host, port);\n                        try {\n                            String response = jedis.sentinelFailover(masterName);\n                            return response != null && response.equalsIgnoreCase(\"OK\");\n                        } finally {\n                            jedis.close();\n                        }\n                    }\n                }.run();\n                if (!isSentinelFailOver) {\n                    logger.warn(\"{}:{} sentienl isSentinelFailOver error\", host, port);\n                    return false;\n                } else {\n                    logger.warn(\"SentinelFailOver done! \");\n                    break;\n                }\n            }\n        }\n        return true;\n    }\n\n    public boolean sentinelReset(long app_id) throws Exception {\n\n        AppDesc appDesc = appService.getByAppId(app_id);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisSentinel(type)) {\n            logger.warn(\"app={} is not sentinel\", appDesc);\n            return false;\n        }\n        final List<InstanceInfo> instanceList = instanceDao.getInstancesByType(app_id, ConstUtils.CACHE_REDIS_SENTINEL);\n        if (instanceList == null || instanceList.isEmpty()) {\n            logger.warn(\"app={} instances is empty\", app_id);\n            return false;\n        }\n        for (InstanceInfo instanceInfo : instanceList) {\n            int instanceType = instanceInfo.getType();\n            if (TypeUtil.isRedisSentinel(instanceType)) {\n                final String host = instanceInfo.getIp();\n                final int port = instanceInfo.getPort();\n                final String masterName = instanceInfo.getCmd();\n                if (StringUtils.isBlank(masterName)) {\n                    logger.warn(\"{} sentinel masterName is null\", instanceInfo);\n                    continue;\n                }\n                boolean isRun = redisCenter.isRun(host, port);\n                if (!isRun) {\n                    logger.warn(\"{}:{} is not run\", host, port);\n                    continue;\n                }\n                boolean isSentinelReset = new IdempotentConfirmer() {\n                    @Override\n                    public boolean execute() {\n                        Jedis jedis = redisCenter.getJedis(host, port);\n                        try {\n                            Long response = jedis.sentinelReset(\"*\");\n                            if (response == 1) {\n                                return true;\n                            }\n                        } finally {\n                            jedis.close();\n                        }\n                        return false;\n                    }\n                }.run();\n                if (!isSentinelReset) {\n                    logger.warn(\"{}:{} sentinel isSentinelReset error\", host, port);\n                    return false;\n                } else {\n                    logger.warn(\"{}:{} SentinelReset done! \", host, port);\n                }\n            }\n        }\n\n        return true;\n    }\n\n    public boolean clusterFailover(long appId, HostAndPort hostAndPort, String failoverParam) throws Exception {\n        InstanceInfo inst = instanceDao.getInstByIpAndPort(hostAndPort.getHost(), hostAndPort.getPort());\n        if (inst != null && inst.getId() > 0) {\n            return clusterFailover(appId, inst.getId(), failoverParam);\n        } else {\n            logger.warn(\"appid:{} clusterFailover error : {} get instanceinfo is empty,inst:{} \", appId, hostAndPort, inst);\n            return false;\n        }\n    }\n\n    @Override\n    public boolean clusterFailover(final long appId, int slaveInstanceId, final String failoverParam) throws Exception {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(slaveInstanceId > 0);\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        Assert.isTrue(appDesc != null);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisCluster(type)) {\n            logger.error(\"{} is not redis cluster type\", appDesc);\n            return false;\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(slaveInstanceId);\n        Assert.isTrue(instanceInfo != null);\n        String slaveHost = instanceInfo.getIp();\n        int slavePort = instanceInfo.getPort();\n        final Jedis slaveJedis = redisCenter.getJedis(appId, slaveHost, slavePort);\n        boolean isClusterFailOver = new IdempotentConfirmer() {\n            @Override\n            public boolean execute() {\n                String response = null;\n                if (StringUtils.isBlank(failoverParam)) {\n                    response = slaveJedis.clusterFailover();\n                } else if (\"force\".equals(failoverParam)) {\n                    response = slaveJedis.clusterFailover(ClusterFailoverOption.FORCE);\n                } else if (\"takeover\".equals(failoverParam)) {\n                    response = slaveJedis.clusterFailover(ClusterFailoverOption.TAKEOVER);\n                } else {\n                    logger.error(\"appId {} failoverParam {} is wrong\", appId, failoverParam);\n                }\n                return response != null && response.equalsIgnoreCase(\"OK\");\n            }\n        }.run();\n        if (!isClusterFailOver) {\n            logger.error(\"{}:{} clusterFailover {} failed\", slaveHost, slavePort, failoverParam);\n            return false;\n        } else {\n            logger.warn(\"{}:{} clusterFailover {} Done! \", slaveHost, slavePort, failoverParam);\n        }\n        return true;\n    }\n\n    @Override\n    public ClusterOperateResult delNode(final Long appId, int delNodeInstanceId) {\n        final InstanceInfo forgetInstanceInfo = instanceDao.getInstanceInfoById(delNodeInstanceId);\n        final String forgetNodeId = redisCenter.getNodeId(appId, forgetInstanceInfo.getIp(),\n                forgetInstanceInfo.getPort());\n        if (StringUtils.isBlank(forgetNodeId)) {\n            logger.warn(\"{} nodeId is null\", forgetInstanceInfo.getHostPort());\n            return ClusterOperateResult.fail(String.format(\"%s nodeId is null\", forgetInstanceInfo.getHostPort()));\n        }\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        for (InstanceInfo instanceInfo : instanceInfos) {\n            if (instanceInfo == null) {\n                continue;\n            }\n            if (instanceInfo.isOffline()) {\n                continue;\n            }\n            // 过滤当前节点\n            if (forgetInstanceInfo.getHostPort().equals(instanceInfo.getHostPort())) {\n                continue;\n            }\n            final String instanceHost = instanceInfo.getIp();\n            final int instancePort = instanceInfo.getPort();\n            boolean isForget = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    String response = null;\n                    Jedis jedis = null;\n                    try {\n                        jedis = redisCenter.getAdminJedis(appId, instanceHost, instancePort);\n                        logger.warn(\"{}:{} is forgetting {}\", instanceHost, instancePort, forgetNodeId);\n                        response = jedis.clusterForget(forgetNodeId);\n                        boolean success = response != null && response.equalsIgnoreCase(\"OK\");\n                        logger.warn(\"{}:{} is forgetting {} result is {}\", instanceHost, instancePort, forgetNodeId,\n                                success);\n                        return success;\n                    } catch (Exception e) {\n                        logger.error(e.getMessage());\n                    } finally {\n                        if (jedis != null) {\n                            jedis.close();\n                        }\n                    }\n                    return response != null && response.equalsIgnoreCase(\"OK\");\n                }\n            }.run();\n            if (!isForget) {\n                logger.warn(\"{}:{} forget {} failed\", instanceHost, instancePort, forgetNodeId);\n                return ClusterOperateResult\n                        .fail(String.format(\"%s:%s forget %s failed\", instanceHost, instancePort, forgetNodeId));\n            }\n        }\n\n        // shutdown\n        boolean isShutdown = instanceDeployCenter.shutdownExistInstance(appId, delNodeInstanceId);\n        if (!isShutdown) {\n            logger.warn(\"{} shutdown failed\", forgetInstanceInfo.getHostPort());\n            return ClusterOperateResult.fail(String.format(\"%s shutdown failed\", forgetInstanceInfo.getHostPort()));\n        }\n\n        return ClusterOperateResult.success();\n    }\n\n    /**\n     * 1. 被forget的节点必须在线(这个条件有待验证)\n     * 2. 被forget的节点不能有从节点\n     * 3. 被forget的节点不能有slots\n     */\n    @Override\n    public ClusterOperateResult checkClusterForget(Long appId, int forgetInstanceId) {\n        // 0.各种验证\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(forgetInstanceId > 0);\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        Assert.isTrue(appDesc != null);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisCluster(type)) {\n            logger.error(\"{} is not redis cluster type\", appDesc);\n            return ClusterOperateResult.fail(String.format(\"instanceId: %s must be cluster type\", forgetInstanceId));\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(forgetInstanceId);\n        Assert.isTrue(instanceInfo != null);\n        String forgetHost = instanceInfo.getIp();\n        int forgetPort = instanceInfo.getPort();\n        // 1.是否在线\n        boolean isRun = redisCenter.isRun(appId, forgetHost, forgetPort);\n        if (!isRun) {\n            logger.warn(\"{}:{} is not run\", forgetHost, forgetPort);\n            return ClusterOperateResult.fail(String.format(\"被forget的节点(%s:%s)必须在线\", forgetHost, forgetPort));\n        }\n        // 2.被forget的节点不能有从节点\n        BooleanEnum hasSlaves = redisCenter.hasSlaves(appId, forgetHost, forgetPort);\n//        if (hasSlaves == null || hasSlaves) {\n//            logger.warn(\"{}:{} has slave\", forgetHost, forgetPort);\n//            return ClusterOperateResult.fail(String.format(\"被forget的节点(%s:%s)不能有从节点\", forgetHost, forgetPort));\n//        }\n        if (hasSlaves == BooleanEnum.OTHER || hasSlaves == BooleanEnum.TRUE) {\n            logger.warn(\"{}:{} has slave\", forgetHost, forgetPort);\n            return ClusterOperateResult.fail(String.format(\"被forget的节点(%s:%s)不能有从节点\", forgetHost, forgetPort));\n        }\n\n        // 3.被forget的节点不能有slots\n        Map<String, InstanceSlotModel> clusterSlotsMap = redisCenter.getClusterSlotsMap(appId);\n        InstanceSlotModel instanceSlotModel = clusterSlotsMap.get(instanceInfo.getHostPort());\n        if (instanceSlotModel != null && instanceSlotModel.getSlotList() != null\n                && instanceSlotModel.getSlotList().size() > 0) {\n            logger.warn(\"{}:{} has slots\", forgetHost, forgetPort);\n            return ClusterOperateResult.fail(String.format(\"被forget的节点(%s:%s)不能持有slot\", forgetHost, forgetPort));\n        }\n\n        return ClusterOperateResult.success();\n    }\n\n    /**\n     * 拷贝redis配置\n     *\n     * @param sourceHost\n     * @param sourcePort\n     * @param targetHost\n     * @param targetPort\n     * @return\n     */\n    private boolean copyCommonConfig(long appId, String sourceHost, int sourcePort, String targetHost, int targetPort) {\n        String[] compareConfigs = new String[]{\"maxmemory-policy\", \"maxmemory\", \"cluster-node-timeout\",\n                \"cluster-require-full-coverage\", \"repl-backlog-size\", \"appendonly\", \"hash-max-ziplist-entries\",\n                \"hash-max-ziplist-value\", \"list-max-ziplist-entries\", \"list-max-ziplist-value\",\n                \"set-max-intset-entries\",\n                \"zset-max-ziplist-entries\", \"zset-max-ziplist-value\", \"timeout\", \"tcp-keepalive\"};\n        try {\n            for (String config : compareConfigs) {\n                String sourceValue = getConfigValue(appId, sourceHost, sourcePort, config);\n                if (StringUtils.isBlank(sourceValue)) {\n                    continue;\n                }\n                String targetValue = getConfigValue(appId, targetHost, targetPort, config);\n                /**\n                 * todo chenshi\n                 * 上面参数配置是按照redis3.0版本配置，高版本redis 3.2的参数配置有变化(需要做映射或默认配置)\n                 * 3.0.7 : list-max-ziplist-entries   list-max-ziplist-value\n                 * 3.2.10: 无上面两个参数配置list-max-ziplist-size list-compress-depth\n                 */\n                if (StringUtils.isNotBlank(targetValue)) {\n                    if (!targetValue.equals(sourceValue)) {\n                        this.modifyInstanceConfig(appId, targetHost, targetPort, config, sourceValue);\n                    }\n                }\n            }\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    private String getConfigValue(long appId, String host, int port, String key) {\n        Jedis jedis = redisCenter\n                .getAdminJedis(appId, host, port, Protocol.DEFAULT_TIMEOUT * 3, Protocol.DEFAULT_TIMEOUT * 3);\n        try {\n            List<String> values = jedis.configGet(key);\n            if (values == null || values.size() < 1) {\n                return null;\n            }\n            return values.get(1);\n        } catch (Exception e) {\n            throw new RuntimeException(e.getMessage(), e);\n        } finally {\n            jedis.close();\n        }\n    }\n\n    @Override\n    public boolean fixPassword(Long appId, String newPkey) {\n        if (appId == null) {\n            logger.warn(\"appId is null\");\n            return false;\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(\"appId = {} not exist\", appId);\n            return false;\n        }\n        if (StringUtils.isBlank(newPkey)) {\n            logger.warn(\"newPkey is null, fix empty password\");\n        }\n        String newPasswordMD5 = \"\";\n        if (StringUtils.isNotBlank(newPkey)) {\n            newPasswordMD5 = AuthUtil.getAppIdMD5(newPkey);\n        }\n\n        String oldPasswordMD5 = appDesc.getAppPassword();\n\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n\n        boolean isSuccess = batchFixPasswordByAdmin(instanceInfos, oldPasswordMD5, newPasswordMD5, AuthUtil.getAppIdMD5(StringUtils.isNotEmpty(newPkey) ? newPkey : String.valueOf(appId)), appDesc.getVersionId());\n        if (isSuccess) {\n            appDesc.setPkey(newPkey);\n            appDao.update(appDesc);\n        }\n\n        return isSuccess;\n    }\n\n    @Override\n    public boolean fixPassword(Long appId, String password, Boolean customPwdFlag, boolean initRedisFlag) {\n        if (appId == null) {\n            logger.warn(\"appId is null\");\n            return false;\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(\"appId = {} not exist\", appId);\n            return false;\n        }\n        if(customPwdFlag == null){\n            if(initRedisFlag){\n                customPwdFlag = appDesc.isSetCustomPassword();\n            }else{\n                customPwdFlag = false;\n            }\n        }\n\n        String newPassword = null;\n        String newPkey = null;\n        if(customPwdFlag){\n            if(StringUtils.isBlank(password)){\n                if(initRedisFlag){\n                    newPassword = appDesc.getCustomPassword();\n                }else{\n                    newPassword = password;\n                }\n            }else{\n                newPassword = password;\n            }\n        }else{\n            if(StringUtils.isBlank(password)){\n                if(initRedisFlag){\n                    newPkey = String.valueOf(appId);\n                    newPassword = AuthUtil.getAppIdMD5(newPkey);\n                }else{\n                    newPkey = null;\n                    newPassword = null;\n                }\n            }else{\n                newPkey = password;\n                newPassword = AuthUtil.getAppIdMD5(password);\n            }\n        }\n        if(newPassword == null){\n            newPassword = \"\";\n        }\n\n        String oldPassword = appDesc.getAppPassword();\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        boolean isSuccess = false;\n        if(!initRedisFlag){\n            isSuccess = batchFixPasswordByAdmin(instanceInfos, oldPassword, newPassword,\n                AuthUtil.getAppIdMD5(StringUtils.isNotEmpty(newPkey) ? newPkey : String.valueOf(appId)), appDesc.getVersionId());\n        }else{\n            isSuccess = batchFixPassword(instanceInfos, oldPassword, newPassword,\n                    AuthUtil.getAppIdMD5(StringUtils.isNotEmpty(newPkey) ? newPkey : String.valueOf(appId)), appDesc.getVersionId());\n        }\n        if(!customPwdFlag){\n            if (isSuccess) {\n                appDesc.setPkey(newPkey);\n                appDesc.setCustomPassword(null);\n                appDao.updateWithCustomPwd(appDesc);\n            }\n        }else{\n            if (isSuccess) {\n                newPkey = String.valueOf(appId);\n                appDesc.setPkey(newPkey);\n                appDesc.setCustomPassword(newPassword);\n                appDao.updateWithCustomPwd(appDesc);\n            }\n        }\n        return isSuccess;\n    }\n\n    @Override\n    public boolean checkAuths(Long appId) {\n        if (appId == null) {\n            logger.warn(\"appId is null\");\n            return false;\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(\"appId = {} not exist\", appId);\n            return false;\n        }\n\n        String password = appDesc.getAppPassword();\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        List<Jedis> nodeList = Lists.newArrayList();\n        try{\n            for (InstanceInfo instanceInfo : instanceInfos) {\n                String host = instanceInfo.getIp();\n                try {\n                    int port = instanceInfo.getPort();\n                    int type = instanceInfo.getType();\n                    if (instanceInfo.isOffline()) {\n                        logger.info(\"instanceInfo {}:{} is offline\", host, port);\n                        continue;\n                    }\n                    if (TypeUtil.isRedisCluster(type) || TypeUtil.isRedisStandalone(type)) {\n                        Jedis jedis = redisCenter.getJedis(host, port, password);\n                        nodeList.add(jedis);\n                    }\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n            return checkAuthNodes(nodeList, password);\n        }finally {\n            nodeList.forEach(jedis -> {\n                try{\n                    jedis.close();\n                }catch (Exception e){\n\n                }\n            });\n        }\n\n\n    }\n\n    private boolean batchFixPasswordByAdmin(List<InstanceInfo> instanceInfos, String oldPasswordMD5, String passwordMD5, String adminPwd, Integer versionId) {\n        if (CollectionUtils.isEmpty(instanceInfos)) {\n            return false;\n        }\n        //修改密码之前，提取所有存活节点。\n        List<Jedis> nodeList = Lists.newArrayList();\n        List<Jedis> rollbackNodeList = Lists.newArrayList();\n        String masterName = null;\n        List<Jedis> sentinelList = Lists.newArrayList();\n        List<Jedis> rollbackSentinelList = Lists.newArrayList();\n        try{\n            for (InstanceInfo instanceInfo : instanceInfos) {\n                String host = instanceInfo.getIp();\n                try {\n                    int port = instanceInfo.getPort();\n                    int type = instanceInfo.getType();\n                    if (instanceInfo.isOffline()) {\n                        logger.info(\"instanceInfo {}:{} is offline\", host, port);\n                        continue;\n                    }\n                    if (TypeUtil.isRedisCluster(type) || TypeUtil.isRedisStandalone(type)) {\n                        Jedis jedis = redisCenter.getAdminJedis(host, port, adminPwd);\n                        nodeList.add(jedis);\n                    } else if (TypeUtil.isRedisSentinel(type)) {\n                        if (StringUtils.isNotBlank(instanceInfo.getCmd())) {\n                            masterName = instanceInfo.getCmd();\n                        }\n                        Jedis jedis = redisCenter.getJedis(host, port);\n                        sentinelList.add(jedis);\n                    }\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n            logger.warn(\"collect nodes done,list:\");\n            return this.doBatchFixPassword(nodeList, sentinelList, rollbackNodeList, rollbackSentinelList, masterName, oldPasswordMD5, passwordMD5, adminPwd, versionId, true);\n        } finally {\n            close(nodeList);\n            close(sentinelList);\n        }\n    }\n\n    private boolean batchFixPassword(List<InstanceInfo> instanceInfos, String oldPasswordMD5, String passwordMD5, String adminPwd, Integer versionId) {\n        if (CollectionUtils.isEmpty(instanceInfos)) {\n            return false;\n        }\n        //修改密码之前，提取所有存活节点。\n        List<Jedis> nodeList = Lists.newArrayList();\n        List<Jedis> rollbackNodeList = Lists.newArrayList();\n        String masterName = null;\n        List<Jedis> sentinelList = Lists.newArrayList();\n        List<Jedis> rollbackSentinelList = Lists.newArrayList();\n        try{\n            for (InstanceInfo instanceInfo : instanceInfos) {\n                String host = instanceInfo.getIp();\n                try {\n                    int port = instanceInfo.getPort();\n                    int type = instanceInfo.getType();\n                    if (instanceInfo.isOffline()) {\n                        logger.info(\"instanceInfo {}:{} is offline\", host, port);\n                        continue;\n                    }\n                    if (TypeUtil.isRedisCluster(type) || TypeUtil.isRedisStandalone(type)) {\n                        Jedis jedis = redisCenter.getJedis(host, port, oldPasswordMD5);\n                        nodeList.add(jedis);\n                    } else if (TypeUtil.isRedisSentinel(type)) {\n                        if (StringUtils.isNotBlank(instanceInfo.getCmd())) {\n                            masterName = instanceInfo.getCmd();\n                        }\n                        Jedis jedis = redisCenter.getJedis(host, port);\n                        sentinelList.add(jedis);\n                    }\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n            //打印需要配置密码的节点信息\n            logger.warn(\"collect nodes done,list:\");\n            return this.doBatchFixPassword(nodeList, sentinelList, rollbackNodeList, rollbackSentinelList, masterName, oldPasswordMD5, passwordMD5, adminPwd, versionId, false);\n        } finally {\n            close(nodeList);\n            close(sentinelList);\n        }\n    }\n\n    private boolean doBatchFixPassword(List<Jedis> nodeList, List<Jedis> sentinelList,\n                                       List<Jedis> rollbackNodeList, List<Jedis> rollbackSentinelList,\n                                       String masterName, String oldPasswordMD5, String passwordMD5, String adminPwd, Integer versionId, boolean doByAdmin) {\n\n        //打印需要配置密码的节点信息\n        for (Jedis jedis : nodeList) {\n            logger.warn(\"fix-password-node:\" + JedisUtil.getHostPort(jedis) + \" isRun:\" + redisCenter.isRun(jedis.getClient().getHost(), jedis.getClient().getPort()));\n        }\n        for (Jedis jedis : sentinelList) {\n            logger.warn(\"fix-sentinel-password-node:\" + JedisUtil.getHostPort(jedis) + \" isRun:\" + redisCenter.isRun(jedis.getClient().getHost(), jedis.getClient().getPort()));\n        }\n        for (Jedis jedis : nodeList) {\n            boolean isFix = false;\n            if(doByAdmin){\n                isFix = fixNodeDefaultPasswordByAdmin(jedis, passwordMD5);\n            }else{\n                isFix = fixNodePassword(jedis, passwordMD5);\n            }\n            if (isFix) {\n                //加入待回滚列表\n                rollbackNodeList.add(jedis);\n            } else {\n                //设置node密码错误回滚node\n                rollbackNodes(rollbackNodeList, oldPasswordMD5);\n                return false;\n            }\n        }\n\n        if (StringUtils.isNotBlank(masterName)) {\n            for (Jedis jedis : sentinelList) {\n                boolean isFix = fixSentinelPassword(jedis, masterName, passwordMD5);\n                if (isFix) {\n                    //加入待回滚列表\n                    rollbackSentinelList.add(jedis);\n                } else {\n                    //设置sentinel密码错误回滚node+sentinel\n                    rollbackNodes(rollbackNodeList, oldPasswordMD5);\n                    rollbackSentinels(rollbackSentinelList, masterName, oldPasswordMD5);\n                    return false;\n                }\n            }\n        }\n        //检测所有节点密码发是否生效\n        boolean allAuth = checkAuthNodes(nodeList, passwordMD5);\n        if (!allAuth) {\n            logger.warn(\"base-auth-error: batch-rollback={}\", passwordMD5);\n            //设置sentinel密码错误回滚node+sentinel\n            rollbackNodes(rollbackNodeList, oldPasswordMD5);\n            rollbackSentinels(rollbackSentinelList, masterName, oldPasswordMD5);\n        }\n        return allAuth;\n    }\n\n    private boolean fixSentinelPassword(Jedis jedis, String masterName, String passwordMD5) {\n        String hostPort = JedisUtil.getHostPort(jedis);\n        /**\n         * sentinel set {masterName} auth-pass {passwordMD5}\n         * sentinel flushconfig\n         */\n        try {\n            Map<String, String> params = new HashMap<String, String>();\n            params.put(\"auth-pass\", passwordMD5);\n            String setResult = jedis.sentinelSet(masterName, params);\n            if (setResult.equals(\"OK\")) {\n                logger.warn(\"config-pass sentinel success: sentinel={} master={} , auth-pass={}\",\n                        hostPort, masterName, passwordMD5);\n                String flushResult = JedisUtil.sentinelFlushConfig(jedis);\n                if (flushResult.equals(\"OK\")) {\n                    logger.warn(\"config rewrite success,sentinel={}\", hostPort);\n                } else {\n                    logger.error(\"config rewrite success sentinel={}\", hostPort);\n                    return false;\n                }\n            } else {\n                logger.error(\"sentinel-config-error:sentinel={} result={}\",\n                        JedisUtil.getHostPort(jedis), setResult);\n                return false;\n            }\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    private void rollbackNodes(List<Jedis> rollbackNodeList, String oldPasswordMD5) {\n        //设置密码错误回滚\n        // rollback-nodes\n        for (Jedis rollbackJedis : rollbackNodeList) {\n            boolean rollback = fixNodePassword(rollbackJedis, oldPasswordMD5);\n            logger.warn(\"node-rollback: node={} rollback={}\",\n                    JedisUtil.getHostPort(rollbackJedis), rollback);\n        }\n    }\n\n    private void rollbackSentinels(List<Jedis> rollbackSentinelList, String masterName, String oldPasswordMD5) {\n        // rollback-sentinels\n        for (Jedis rollbackJedis : rollbackSentinelList) {\n            boolean rollback = fixSentinelPassword(rollbackJedis, masterName, oldPasswordMD5);\n            logger.warn(\"sentinel-rollback: node={} rollback={}\",\n                    JedisUtil.getHostPort(rollbackJedis), rollback);\n        }\n    }\n\n    private boolean fixNodePassword(Jedis jedis, String passwordMD5) {\n        String hostPort = JedisUtil.getHostPort(jedis);\n        try {\n            List<String> results = Lists.newArrayList();\n\n            results.add(jedis.configSet(\"requirepass\", passwordMD5));\n            //密码设置后,无密码连接需要重新认证\n            if (StringUtils.isNotBlank(passwordMD5)) {\n                jedis.auth(passwordMD5);\n            }\n            results.add(jedis.configSet(\"masterauth\", passwordMD5));\n            if (results.get(0).equals(\"OK\") && results.get(1).equals(\"OK\")) {\n                logger.warn(\"config-pass success: node={} results={}\", hostPort, results);\n                String rewrite = jedis.configRewrite();\n                if (rewrite.equals(\"OK\")) {\n                    logger.warn(\"node-rewrite success: node={} result={}\", hostPort, rewrite);\n                } else {\n                    logger.error(\"config rewrite error node={}\", hostPort);\n                }\n            } else {\n                logger.error(\"config-pass error: node={} results={}\", hostPort, results);\n                return false;\n            }\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    private boolean fixNodeDefaultPasswordByAdmin(Jedis jedis, String passwordMD5) {\n        String hostPort = JedisUtil.getHostPort(jedis);\n        try {\n            List<String> results = Lists.newArrayList();\n\n            results.add(jedis.configSet(\"requirepass\", passwordMD5));\n            //管理员用户修改默认用户密码，无需要重新密码认证\n            results.add(jedis.configSet(\"masterauth\", passwordMD5));\n            if (results.get(0).equals(\"OK\") && results.get(1).equals(\"OK\")) {\n                logger.warn(\"config-pass success: node={} results={}\", hostPort, results);\n                String rewrite = jedis.configRewrite();\n                if (rewrite.equals(\"OK\")) {\n                    logger.warn(\"node-rewrite success: node={} result={}\", hostPort, rewrite);\n                } else {\n                    logger.error(\"config rewrite error node={}\", hostPort);\n                }\n            } else {\n                logger.error(\"config-pass error: node={} results={}\", hostPort, results);\n                return false;\n            }\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    // 判断密码是否匹配\n    private boolean checkAuthNodes(List<Jedis> nodes, String passwordMD5) {\n        for (Jedis jedis : nodes) {\n            try {\n                String auth = jedis.auth(passwordMD5);\n                if (!auth.equals(\"OK\")) {\n                    return false;\n                }\n            } catch (JedisDataException e) {\n                //忽略无密码设置异常\n                if (e.getMessage().contains(\"no password is set\") || e.getMessage().contains(\"without any password configured for the default user\")) {\n                    logger.info(\"ignore ERR Client sent AUTH, but no password is set\");\n                } else {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            } catch (Exception e) {\n                logger.error(\"node-auth-failed: node={} password={} error={}\", JedisUtil.getHostPort(jedis),\n                        passwordMD5, e.getMessage());\n                logger.error(e.getMessage(), e);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public boolean slaveOf(final long appId, final String masterHost, final int masterPort, final String slaveHost,\n                           final int slavePort) {\n        final Jedis slave = redisCenter.getAdminJedis(appId, slaveHost, slavePort, Protocol.DEFAULT_TIMEOUT * 3, Protocol.DEFAULT_TIMEOUT * 3);\n        try {\n            boolean isSlave = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    String result = slave.slaveof(masterHost, masterPort);\n                    //也有可能是OK Already connected to specified master\n                    return result != null && result.startsWith(\"OK\");\n                }\n            }.run();\n            if (!isSlave) {\n                logger.error(String.format(\"modifyAppConfig:ip=%s,port=%s failed\", slaveHost, slavePort));\n                return false;\n            }\n            redisCenter.configRewrite(appId, slaveHost, slavePort);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        } finally {\n            if (slave != null)\n                slave.close();\n        }\n        return true;\n    }\n\n    @Override\n    public boolean slaveOfByDefaultUser(final long appId, final String masterHost, final int masterPort, final String slaveHost,\n                           final int slavePort) {\n        final Jedis slave = redisCenter.getJedis(appId, slaveHost, slavePort, Protocol.DEFAULT_TIMEOUT * 3, Protocol.DEFAULT_TIMEOUT * 3);\n        try {\n            boolean isSlave = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    String result = slave.slaveof(masterHost, masterPort);\n                    //也有可能是OK Already connected to specified master\n                    return result != null && result.startsWith(\"OK\");\n                }\n            }.run();\n            if (!isSlave) {\n                logger.error(String.format(\"modifyAppConfig:ip=%s,port=%s failed\", slaveHost, slavePort));\n                return false;\n            }\n            redisCenter.configRewrite(slave);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        } finally {\n            if (slave != null)\n                slave.close();\n        }\n        return true;\n    }\n\n    private void close(List<Jedis> list) {\n        for (Jedis jedis : list) {\n            if (jedis != null)\n                jedis.close();\n        }\n    }\n\n    /**\n     *\n     * @param appId\n     * @param newInstHost\n     * @param newInstPort\n     * @param pointedRdb 是否已在目录下导入了rdb\n     * @param mem        实例最大内存（与runInstId 必须至少一个不为空）\n     * @param runInstId  运行实例信息\n     * @param slots\n     * @return\n     */\n    @Override\n    public boolean addInstanceToUnhealthyApp(long appId, final String newInstHost, final Integer newInstPort,\n                                             final boolean pointedRdb, Integer mem, int[] slots, Integer runInstId) {\n        Assert.isTrue(appId > 0 && StringUtils.isNotBlank(newInstHost)\n                && newInstPort != null);\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        Assert.isTrue(appDesc != null);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisType(type)) {\n            logger.error(\"{} is not redis type\", appDesc);\n            return false;\n        }\n        List<Pair<String, String>> customConfigs = new ArrayList<>();\n        //启动实例，用默认配置文件\n        boolean isRun;\n        //如果有已有实例，则复制其指定配置\n        InstanceInfo existInstanceInfo = null;\n        String existInstanceHost = null;\n        Integer existInstancePort = null;\n        if(runInstId != null){\n            existInstanceInfo = instanceDao.getInstanceInfoById(runInstId);\n            Assert.isTrue(existInstanceInfo != null);\n            existInstanceHost = existInstanceInfo.getIp();\n            existInstancePort = existInstanceInfo.getPort();\n            // 注意redis高低版本复制config的问题\n//            Map<String, String> configMap = copyCommonConfig(appId, existInstanceInfo.getIp(), existInstanceInfo.getPort());\n//            customConfigMap.putAll(configMap);\n            if(mem == null){\n                mem = existInstanceInfo.getMem();\n            }\n        }\n        customConfigs.add(Pair.of(RedisConfigEnum.APPENDONLY.getKey(), \"no\"));\n        logger.info(\"to do run {}:{} with custom config {}\", newInstHost, newInstPort, customConfigs);\n\n        if (TypeUtil.isRedisCluster(type)) {\n            isRun = runInstance(appDesc, newInstHost, newInstPort, existInstanceHost, existInstancePort, mem, true, customConfigs);\n        } else {\n            isRun = runInstance(appDesc, newInstHost, newInstPort, existInstanceHost, existInstancePort, mem, false, customConfigs);\n        }\n        if (!isRun) {\n            logger.error(\"{}:{} is not run\", newInstHost, newInstPort);\n            return false;\n        }\n\n        Set<Integer> existsSlotSet = new HashSet<>();\n        try(Jedis  jedis = redisCenter\n                .getJedis(appId, newInstHost, newInstPort, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);) {\n            //加载rdb，并等等rdb加载完成，然后确认节点 slot分配\n            if (pointedRdb) {\n                boolean loadFinish = redisCenter.checkLoadFinish(jedis, RETRY_TIMES);\n                if (!loadFinish) {\n                    return false;\n                } else {\n                    if(appDesc.getType() == AppTypeEnum.REDIS_CLUSTER.getType()){\n                        //获取当前实例分配节点\n                        InstanceInfo instanceInfo = new InstanceInfo();\n                        instanceInfo.setIp(newInstHost);\n                        instanceInfo.setPort(newInstPort);\n                        Map<String, List<HostAndPort>> clusterSlotMap = redisCenter.getClusterSlotMap(appId, instanceInfo);\n                        logger.info(\"instance {}:{} cluster slot info is {}\", newInstHost, newInstPort, clusterSlotMap);\n                        clusterSlotMap.keySet().forEach(key -> {\n                            List<HostAndPort> hostAndPorts = clusterSlotMap.get(key);\n                            if (hostAndPorts.contains(new HostAndPort(newInstHost, newInstPort))\n                                || hostAndPorts.contains(new HostAndPort(\"\", newInstPort))) {\n                                String[] split = key.split(\"-\");\n                                if (split != null && split.length == 2) {\n                                    for (int i = Integer.valueOf(split[0]); i <= Integer.valueOf(split[1]); i++) {\n                                        existsSlotSet.add(i);\n                                    }\n                                }\n                            }\n                        });\n                    }\n                }\n                logger.info(\"{}:{} load rdb finish\", newInstHost, newInstPort);\n            }\n            // 集群分配slot\n            if(appDesc.getType() == AppTypeEnum.REDIS_CLUSTER.getType()){\n                logger.info(\"exists slots is {} due to load rdb\", existsSlotSet);\n                //计算需添加的slot\n                if(CollectionUtils.isNotEmpty(existsSlotSet)){\n                    Set<Integer> originalToAddSlot = Arrays.stream(slots).boxed().collect(Collectors.toSet());\n                    Sets.SetView<Integer> setView = Sets.difference(originalToAddSlot, existsSlotSet);\n                    if(setView != null && setView.size() > 0){\n                        int[] toAddSlots = new int[setView.size()];\n                        int i = 0;\n                        UnmodifiableIterator<Integer> iterator = setView.iterator();\n                        while(iterator.hasNext()){\n                            toAddSlots[i++] = iterator.next();\n                        }\n                        slots = toAddSlots;\n                    }\n                }\n\n                logger.info(\"to add slots is {}\", slots);\n                //添加slot\n                if(slots != null && slots.length > 0){\n                    final int[] toAddSlots = slots;\n                    // 添加slots\n                    boolean isSlot = new IdempotentConfirmer() {\n                        @Override\n                        public boolean execute() {\n                            String response = jedis.clusterAddSlots(toAddSlots);\n                            boolean isSlot = response != null && response.equalsIgnoreCase(\"OK\");\n                            if (!isSlot) {\n                                logger.error(\"add slot {} to node {}:{} failed\", toAddSlots, newInstHost, newInstPort);\n                                return false;\n                            }\n                            logger.info(\"add slot {} to node {}:{} success\", toAddSlots, newInstHost, newInstPort);\n                            return true;\n                        }\n                    }.run();\n                    if(!isSlot){\n                        return false;\n                    }\n                    logger.info(\"add slots {} success\", slots);\n                }\n            }\n        }\n        // 开启aof\n        boolean configSetAndRewrite = redisCenter.configSetAndRewrite(appId, newInstHost, newInstPort, RedisConfigEnum.APPENDONLY.getKey(), \"yes\");\n        if (!configSetAndRewrite) {\n            logger.error(\"appId {} instanceInfo {}:{} enable aof fail\", appId, newInstHost, newInstPort);\n            return configSetAndRewrite;\n        }\n        logger.info(\"enable {}:{} appendonly config to yes success\", newInstHost, newInstPort);\n\n        //判断是否指定链接实例，并meet到集群\n        if(existInstanceInfo != null){\n            if (TypeUtil.isRedisCluster(type)) {\n                try (Jedis existJedis = redisCenter\n                        .getJedis(appId, existInstanceInfo.getIp(), existInstanceInfo.getPort(), Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT);) {\n                    boolean isClusterMeet = clusterMeet(existJedis, appId, newInstHost, newInstPort);\n                    if (!isClusterMeet) {\n                        logger.error(\"meet node {}:{} to cluster failed\", newInstHost, newInstPort);\n                        return isClusterMeet;\n                    }\n                    logger.info(\"meet node {}:{} to cluster success\", newInstHost, newInstPort);\n                }\n            }\n        }\n\n        //写入instanceInfo 信息\n        if (TypeUtil.isRedisCluster(type)) {\n            saveInstance(appId, newInstHost, newInstPort, mem,\n                    ConstUtils.CACHE_TYPE_REDIS_CLUSTER, \"\");\n        } else {\n            saveInstance(appId, newInstHost, newInstPort, mem,\n                    ConstUtils.CACHE_REDIS_STANDALONE, \"\");\n        }\n        return true;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/AuthUtil.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.exceptions.JedisConnectionException;\nimport redis.clients.jedis.exceptions.JedisDataException;\n\nimport java.security.MessageDigest;\n\n/**\n * Created by zhangyijun on 2017/7/31.\n */\npublic class AuthUtil {\n    //客户端埋点SECRET_KEY，如果使用动态密码，建议客户端自行修改\n    public final static String SECRET_KEY = \"asdfjlajrl2k3jflsdafjal$$$asfdf\";\n    public final static String SPLIT_KEY = \":cc:\";\n\n    /**\n     * 密码判定\n     * 初始化用例:\n     * 1:redis未设置密码，未设置password参数\n     * 2:redis未设置密码，设置password参数:原文\n     * 3:redis未设置密码，设置password参数:appId\n     * 4:redis未设置密码，设置password参数:appId+MD5\n     * 5:redis设置密码, 设置password参数:原文\n     * 6:redis设置密码, 设置password参数:appId\n     * 7:redis设置密码, 设置password参数:appId+MD5\n     * 运行期修改密码用例:\n     * 1:redis无密码状态，后端设置原文密码后，连接报错重连通信正常\n     * 2:redis无密码状态，后端设置appId+MD5密码后，连接报错重连通信正常\n     * 3:redis存在appId密码，后端修改为md5密码后通信正常\n     * 期望结果:\n     * 1:验证通过\n     * 2:原文密码,appId,MD5匹配一个，验证通过。\n     * 3:密码错误，抛出异常。\n     * tip: update this method without reset isbroken to true and it will fail when previous condition\n     * @param jedis\n     * @return\n     */\n    public static void auth(Jedis jedis, String password) {\n        if (password == null || password.trim().length() == 0) {\n            //password为空，认为通过\n            return;\n        }\n        boolean isAuth = authForPassword(jedis, password);\n        if (isAuth) {\n            if (jedis.getClient().isBroken()) {\n                jedis.close();\n            }\n        } else {\n            throw new JedisConnectionException(\"invalid password\");\n        }\n    }\n\n    private static boolean authForPassword(Jedis jedis, String password) {\n        if (password.contains(SPLIT_KEY)) {\n            //password layout: appId+SPLIT_KEY+MD5\n            String[] split = password.split(SPLIT_KEY);\n            String appId;\n            String md5 = null;\n            if (split.length == 1) {\n                appId = split[0];\n            } else {\n                appId = split[0];\n                md5 = split[1];\n            }\n            // 如果存在md5,优先使用md5验证\n            if (md5 != null && md5.length() > 0) {\n                boolean auth = checkAuth(jedis, md5, true);\n                if (auth) {\n                    return true;\n                }\n            }\n            md5 = getAppIdMD5(appId);\n            return checkAuth(jedis, md5, false);\n        } else {\n            //不包含{SPLIT_KEY}，直接认为是密码。\n            return \"OK\".equals(jedis.auth(password));\n        }\n    }\n\n    public static boolean checkAuth(Jedis jedis, String pass, boolean check) throws JedisDataException {\n        try {\n            jedis.auth(pass);\n            return true;\n        } catch (JedisDataException e) {\n            if (check && e.getMessage() != null && e.getMessage().contains(\"invalid password\")) {\n                // 密码错误，重新验证\n                return false;\n            }\n            throw e;\n        }\n    }\n\n    public static String getAppIdMD5(String appId) {\n        String key = SECRET_KEY + \":\" + appId;\n        try {\n            //确定计算方法\n            MessageDigest md5 = MessageDigest.getInstance(\"MD5\");\n            byte[] md5Bytes = md5.digest(key.getBytes(\"UTF-8\"));\n            StringBuffer hexValue = new StringBuffer();\n            for (int i = 0; i < md5Bytes.length; i++) {\n                int val = ((int) md5Bytes[i]) & 0xff;\n                if (val < 16) {\n                    hexValue.append(\"0\");\n                }\n                hexValue.append(Integer.toHexString(val));\n            }\n            return hexValue.toString();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/ClusterNodeInformation.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport redis.clients.jedis.HostAndPort;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ClusterNodeInformation {\n  private HostAndPort node;\n  private List<Integer> availableSlots;\n  private List<Integer> slotsBeingImported;\n  private List<Integer> slotsBeingMigrated;\n\n  public ClusterNodeInformation(HostAndPort node) {\n    this.node = node;\n    this.availableSlots = new ArrayList<Integer>();\n    this.slotsBeingImported = new ArrayList<Integer>();\n    this.slotsBeingMigrated = new ArrayList<Integer>();\n  }\n\n  public void addAvailableSlot(int slot) {\n    availableSlots.add(slot);\n  }\n\n  public void addSlotBeingImported(int slot) {\n    slotsBeingImported.add(slot);\n  }\n\n  public void addSlotBeingMigrated(int slot) {\n    slotsBeingMigrated.add(slot);\n  }\n\n  public HostAndPort getNode() {\n    return node;\n  }\n\n  public List<Integer> getAvailableSlots() {\n    return availableSlots;\n  }\n\n  public List<Integer> getSlotsBeingImported() {\n    return slotsBeingImported;\n  }\n\n  public List<Integer> getSlotsBeingMigrated() {\n    return slotsBeingMigrated;\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/ClusterNodeInformationParser.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport redis.clients.jedis.HostAndPort;\n\npublic class ClusterNodeInformationParser {\n  private static final String SLOT_IMPORT_IDENTIFIER = \"-<-\";\n  private static final String SLOT_IN_TRANSITION_IDENTIFIER = \"[\";\n  public static final int SLOT_INFORMATIONS_START_INDEX = 8;\n  public static final int HOST_AND_PORT_INDEX = 1;\n\n  public ClusterNodeInformation parse(String nodeInfo, HostAndPort current) {\n    String[] nodeInfoPartArray = nodeInfo.split(\" \");\n\n    HostAndPort node = getHostAndPortFromNodeLine(nodeInfoPartArray, current);\n    ClusterNodeInformation info = new ClusterNodeInformation(node);\n\n    if (nodeInfoPartArray.length >= SLOT_INFORMATIONS_START_INDEX) {\n      String[] slotInfoPartArray = extractSlotParts(nodeInfoPartArray);\n      fillSlotInformation(slotInfoPartArray, info);\n    }\n\n    return info;\n  }\n\n  private String[] extractSlotParts(String[] nodeInfoPartArray) {\n    String[] slotInfoPartArray = new String[nodeInfoPartArray.length\n        - SLOT_INFORMATIONS_START_INDEX];\n    for (int i = SLOT_INFORMATIONS_START_INDEX; i < nodeInfoPartArray.length; i++) {\n      slotInfoPartArray[i - SLOT_INFORMATIONS_START_INDEX] = nodeInfoPartArray[i];\n    }\n    return slotInfoPartArray;\n  }\n\n  public HostAndPort getHostAndPortFromNodeLine(String[] nodeInfoPartArray, HostAndPort current) {\n    String stringHostAndPort = nodeInfoPartArray[HOST_AND_PORT_INDEX];\n\n    String[] arrayHostAndPort = stringHostAndPort.split(\":\");\n    return new HostAndPort(arrayHostAndPort[0].isEmpty() ? current.getHost() : arrayHostAndPort[0],\n        arrayHostAndPort[1].isEmpty() ? current.getPort() : Integer.parseInt(arrayHostAndPort[1]));\n  }\n\n  private void fillSlotInformation(String[] slotInfoPartArray, ClusterNodeInformation info) {\n    for (String slotRange : slotInfoPartArray) {\n      fillSlotInformationFromSlotRange(slotRange, info);\n    }\n  }\n\n  private void fillSlotInformationFromSlotRange(String slotRange, ClusterNodeInformation info) {\n    if (slotRange.startsWith(SLOT_IN_TRANSITION_IDENTIFIER)) {\n      // slot is in transition\n      int slot = Integer.parseInt(slotRange.substring(1).split(\"-\")[0]);\n\n      if (slotRange.contains(SLOT_IMPORT_IDENTIFIER)) {\n        // import\n        info.addSlotBeingImported(slot);\n      } else {\n        // migrate (->-)\n        info.addSlotBeingMigrated(slot);\n      }\n    } else if (slotRange.contains(\"-\")) {\n      // slot range\n      String[] slotRangePart = slotRange.split(\"-\");\n      for (int slot = Integer.parseInt(slotRangePart[0]); slot <= Integer.parseInt(slotRangePart[1]); slot++) {\n        info.addAvailableSlot(slot);\n      }\n    } else {\n      // single slot\n      info.addAvailableSlot(Integer.parseInt(slotRange));\n    }\n  }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/Command.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport redis.clients.jedis.commands.ProtocolCommand;\nimport redis.clients.jedis.util.SafeEncoder;\n\npublic enum Command implements ProtocolCommand {\n    PING, SET, GET, QUIT, EXISTS, DEL, UNLINK, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX,\n    RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX,\n    MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET,\n    HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX,\n    LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER,\n    SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY,\n    ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, ZPOPMAX, ZPOPMIN, MULTI, DISCARD, EXEC, WATCH,\n    UNWATCH, SORT, BLPOP, BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE,\n    PUBSUB, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE,\n    ZINTERSTORE, ZLEXCOUNT, ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEX, SAVE, BGSAVE, BGREWRITEAOF,\n    LASTSAVE, SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO,\n    LINSERT, DEBUG, BRPOPLPUSH, SETBIT, GETBIT, BITPOS, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT,\n    SLOWLOG, OBJECT, BITCOUNT, BITOP, SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT,\n    PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING,\n    PFADD, PFCOUNT, PFMERGE, READONLY, GEOADD, GEODIST, GEOHASH, GEOPOS, GEORADIUS, GEORADIUS_RO,\n    GEORADIUSBYMEMBER, GEORADIUSBYMEMBER_RO, MODULE, BITFIELD, HSTRLEN, TOUCH, SWAPDB, MEMORY,\n    XADD, XLEN, XDEL, XTRIM, XRANGE, XREVRANGE, XREAD, XACK, XGROUP, XREADGROUP, XPENDING, XCLAIM, LATENCY,\n    ACL, XINFO, BITFIELD_RO, COMMAND;\n\n    private final byte[] raw;\n\n    Command() {\n      raw = SafeEncoder.encode(this.name());\n    }\n\n    @Override\n    public byte[] getRaw() {\n      return raw;\n    }\n\n\n\n    public enum SearchCommand implements ProtocolCommand {\n\n        CREATE(\"FT.CREATE\"),\n        ALTER(\"FT.ALTER\"),\n        INFO(\"FT.INFO\"),\n        SEARCH(\"FT.SEARCH\"),\n        EXPLAIN(\"FT.EXPLAIN\"),\n        EXPLAINCLI(\"FT.EXPLAINCLI\"),\n        AGGREGATE(\"FT.AGGREGATE\"),\n        CURSOR(\"FT.CURSOR\"),\n        CONFIG(\"FT.CONFIG\"),\n        CLUSTER_CONFIG(\"_FT.CONFIG\"),\n        ALIASADD(\"FT.ALIASADD\"),\n        ALIASUPDATE(\"FT.ALIASUPDATE\"),\n        ALIASDEL(\"FT.ALIASDEL\"),\n        SYNUPDATE(\"FT.SYNUPDATE\"),\n        SYNDUMP(\"FT.SYNDUMP\"),\n        SUGADD(\"FT.SUGADD\"),\n        SUGGET(\"FT.SUGGET\"),\n        SUGDEL(\"FT.SUGDEL\"),\n        SUGLEN(\"FT.SUGLEN\"),\n        DROPINDEX(\"FT.DROPINDEX\"),\n        LIST(\"FT._LIST\");\n\n        private final byte[] raw;\n\n        private SearchCommand(String alt) {\n            raw = SafeEncoder.encode(alt);\n        }\n\n        @Override\n        public byte[] getRaw() {\n            return raw;\n        }\n    }\n\n    public enum JSONCommand implements ProtocolCommand {\n\n        OBJLEN(\"JSON.OBJLEN\"),\n        JSONGET(\"JSON.GET\");\n\n        private final byte[] raw;\n\n        private JSONCommand(String alt) {\n            raw = SafeEncoder.encode(alt);\n        }\n\n        @Override\n        public byte[] getRaw() {\n            return raw;\n        }\n    }\n  }"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/JedisUtil.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport redis.clients.jedis.Client;\nimport redis.clients.jedis.Jedis;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/11/25 17:43\n * @Description:\n */\npublic class JedisUtil {\n\n    public static final String SENTINEL_FLUSH_CONFIG = \"flushconfig\";\n\n    public static List<LatencyItem> latencyLatest(Jedis jedis){\n        Client client = jedis.getClient();\n        client.sendCommand(Command.LATENCY, Keyword.LATEST.raw);\n        List<LatencyItem> latencyItems = LatencyItem.from(client.getObjectMultiBulkReply());\n        return latencyItems;\n    }\n\n    public static String sentinelFlushConfig(Jedis jedis){\n        Client client = jedis.getClient();\n        client.sentinel(SENTINEL_FLUSH_CONFIG);\n        return client.getStatusCodeReply();\n    }\n\n    public static String getHostPort(Jedis jedis){\n        Client client = jedis.getClient();\n        return client.getHost() + \":\" + client.getPort();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/Keyword.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport redis.clients.jedis.util.SafeEncoder;\n\nimport java.util.Locale;\n\npublic enum Keyword {\n    AGGREGATE, ALPHA, ASC, BY, DESC, GET, LIMIT, MESSAGE, NO, NOSORT, PMESSAGE, PSUBSCRIBE,\n    PUNSUBSCRIBE, OK, ONE, QUEUED, SET, STORE, SUBSCRIBE, UNSUBSCRIBE, WEIGHTS, WITHSCORES,\n    RESETSTAT, REWRITE, RESET, FLUSH, EXISTS, LOAD, KILL, LEN, REFCOUNT, ENCODING, IDLETIME,\n    GETNAME, SETNAME, LIST, MATCH, COUNT, PING, PONG, UNLOAD, REPLACE, KEYS, PAUSE, DOCTOR,\n    BLOCK, NOACK, STREAMS, KEY, CREATE, MKSTREAM, SETID, DESTROY, DELCONSUMER, MAXLEN, GROUP,\n    IDLE, TIME, RETRYCOUNT, FORCE, LATEST, HISTORY, GRAPH, USAGE, STATS, PURGE, TRACKING, GETREDIR,\n    ID, ON, OFF, STREAM, GROUPS, CONSUMERS, HELP, FREQ,\n    SETUSER, GETUSER, DELUSER, WHOAMI, CAT, GENPASS, USERS;\n\n    public final byte[] raw;\n\n    Keyword() {\n      raw = SafeEncoder.encode(this.name().toLowerCase(Locale.ENGLISH));\n    }\n  }"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/LatencyHistoryItem.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author wenruiwu\n * @create 2020/5/6 10:25\n * @description Latency history event\n */\npublic class LatencyHistoryItem {\n\n    private final long timeStamp;\n    private final long executionTime;\n    private static final String COMMA = \",\";\n\n    @SuppressWarnings(\"unchecked\")\n    public LatencyHistoryItem(List<Object> properties) {\n        super();\n        this.timeStamp = (Long) properties.get(0);\n        this.executionTime = (Long) properties.get(1);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static List<LatencyHistoryItem> from(List<Object> nestedMultiBulkReply) {\n        List<LatencyHistoryItem> items = new ArrayList<>(nestedMultiBulkReply.size());\n        for (Object obj : nestedMultiBulkReply) {\n            List<Object> properties = (List<Object>) obj;\n            items.add(new LatencyHistoryItem(properties));\n        }\n\n        return items;\n    }\n\n    public long getTimeStamp() {\n        return timeStamp;\n    }\n\n    public long getExecutionTime() {\n        return executionTime;\n    }\n\n\n    @Override\n    public String toString() {\n        return new StringBuilder().append(timeStamp).append(COMMA).append(executionTime).toString();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/LatencyItem.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author wenruiwu\n * @create 2020/4/29 17:24\n * @description\n */\npublic class LatencyItem {\n\n    private final String event;\n    private final long timeStamp;\n    private final long latestExecutionTime;\n    private final long maxExecutionTime;\n    private static final String COMMA = \",\";\n\n    @SuppressWarnings(\"unchecked\")\n    private LatencyItem(List<Object> properties) {\n        super();\n        this.event = new String((byte[])properties.get(0));\n        this.timeStamp = (Long) properties.get(1);\n        this.latestExecutionTime = (Long) properties.get(2);\n        this.maxExecutionTime = (Long) properties.get(3);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static List<LatencyItem> from(List<Object> nestedMultiBulkReply) {\n        List<LatencyItem> items = new ArrayList<>(nestedMultiBulkReply.size());\n        for (Object obj : nestedMultiBulkReply) {\n            List<Object> properties = (List<Object>) obj;\n            items.add(new LatencyItem(properties));\n        }\n\n        return items;\n    }\n\n    public String getEvent() {\n        return event;\n    }\n\n    public long getTimeStamp() {\n        return timeStamp;\n    }\n\n    public long getLatestExecutionTime() {\n        return latestExecutionTime;\n    }\n\n    public long getMaxExecutionTime() {\n        return maxExecutionTime;\n    }\n\n    @Override\n    public String toString() {\n        return new StringBuilder().append(event).append(COMMA).append(timeStamp).append(COMMA)\n                .append(latestExecutionTime).append(COMMA).append(maxExecutionTime).toString();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/PipelineUtil.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport redis.clients.jedis.DebugParams;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.Protocol;\nimport redis.clients.jedis.Response;\nimport redis.clients.jedis.util.SafeEncoder;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/11/25 17:45\n * @Description:\n */\npublic class PipelineUtil {\n\n    public static Response<Object> latencyHistory(Pipeline pipeline, String event){\n        return pipeline.sendCommand(Command.LATENCY, Keyword.HISTORY.raw, SafeEncoder.encode(event));\n    }\n\n    public static Response<Object> latencyReset(Pipeline pipeline, String event){\n        return pipeline.sendCommand(Command.LATENCY, joinParameters(Keyword.RESET.raw, SafeEncoder.encodeMany(event)));\n    }\n\n    public static Response<Object> clusterCountKeysInSlot(Pipeline pipeline, int slot){\n        byte[][] args = new byte[2][];\n        args[0] = SafeEncoder.encode(Protocol.CLUSTER_COUNTKEYINSLOT);\n        args[1] = SafeEncoder.encode(String.valueOf(slot));\n        return pipeline.sendCommand(Command.CLUSTER, args);\n    }\n\n    public static Response<Object> debug(Pipeline pipeline, DebugParams params){\n        return pipeline.sendCommand(Command.DEBUG, params.getCommand());\n    }\n\n    public static Response<Object> objectIdletime(Pipeline pipeline, String key) {\n        return pipeline.sendCommand(Command.OBJECT, Keyword.IDLETIME.raw, SafeEncoder.encode(key));\n    }\n\n    public static Response<Object> memoryUsage(Pipeline pipeline, String key){\n        return pipeline.sendCommand(Command.MEMORY, Keyword.USAGE.raw, SafeEncoder.encode(key));\n    }\n\n    private static byte[][] joinParameters(byte[] first, byte[][] rest) {\n        byte[][] result = new byte[rest.length + 1][];\n        result[0] = first;\n        System.arraycopy(rest, 0, result, 1, rest.length);\n        return result;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/ProtostuffSerializer.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport com.dyuproject.protostuff.LinkedBuffer;\nimport com.dyuproject.protostuff.ProtostuffIOUtil;\nimport com.dyuproject.protostuff.Schema;\nimport com.dyuproject.protostuff.runtime.RuntimeSchema;\n\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Created by yijunzhang on 14-4-10.\n */\npublic class ProtostuffSerializer {\n\n    private static ConcurrentHashMap<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();\n\n    public <T> byte[] serialize(final T source) {\n        VO<T> vo = new VO<T>(source);\n\n        final LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);\n        try {\n            final Schema<VO> schema = getSchema(VO.class);\n            return serializeInternal(vo, schema, buffer);\n        } catch (final Exception e) {\n            throw new IllegalStateException(e.getMessage(), e);\n        } finally {\n            buffer.clear();\n        }\n    }\n\n    public <T> T deserialize(final byte[] bytes) {\n        try {\n            Schema<VO> schema = getSchema(VO.class);\n            VO vo = deserializeInternal(bytes, schema.newMessage(), schema);\n            if (vo != null && vo.getValue() != null) {\n                return (T) vo.getValue();\n            }\n        } catch (final Exception e) {\n            throw new IllegalStateException(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    private <T> byte[] serializeInternal(final T source, final Schema<T> schema, final LinkedBuffer buffer) {\n        return ProtostuffIOUtil.toByteArray(source, schema, buffer);\n    }\n\n    private <T> T deserializeInternal(final byte[] bytes, final T result, final Schema<T> schema) {\n        ProtostuffIOUtil.mergeFrom(bytes, result, schema);\n        return result;\n    }\n\n    private static <T> Schema<T> getSchema(Class<T> clazz) {\n        @SuppressWarnings(\"unchecked\")\n        Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);\n        if (schema == null) {\n            schema = RuntimeSchema.createFrom(clazz);\n            cachedSchema.put(clazz, schema);\n        }\n        return schema;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/redis/util/VO.java",
    "content": "package com.sohu.cache.redis.util;\n\nimport java.io.Serializable;\n\n/**\n * Created by yijunzhang on 14-4-2.\n */\npublic class VO<T> implements Serializable {\n\n    private T value;\n\n    public VO(T value) {\n        this.value = value;\n    }\n\n    public VO() {\n    }\n\n    public T getValue() {\n        return value;\n    }\n\n    @Override\n    public String toString() {\n        return \"VO{\" +\n                \"value=\" + value +\n                '}';\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof VO)) return false;\n        VO vo = (VO) o;\n        if (value != null ? !value.equals(vo.value) : vo.value != null) return false;\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        return value != null ? value.hashCode() : 0;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/SchedulerCenter.java",
    "content": "package com.sohu.cache.schedule;\r\n\r\nimport com.sohu.cache.entity.TriggerInfo;\r\nimport org.quartz.Trigger;\r\nimport org.quartz.TriggerKey;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 控制job、trigger和scheduler的基类\r\n * User: lingguo\r\n */\r\npublic interface SchedulerCenter {\r\n\r\n    public Trigger getTrigger(TriggerKey triggerKey);\r\n\r\n    /**\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    public boolean unscheduleJob(TriggerKey triggerKey);\r\n\r\n    /**\r\n     * 获取所有trigger\r\n     *\r\n     * @return\r\n     */\r\n    public List<TriggerInfo> getAllTriggers();\r\n\r\n    /**\r\n     * 模糊查询trigger\r\n     *\r\n     * @return\r\n     */\r\n    public List<TriggerInfo> getTriggersByNameOrGroup(String query);\r\n\r\n    /**\r\n     * 暂定trigger\r\n     *\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    public boolean pauseTrigger(TriggerKey triggerKey);\r\n\r\n    /**\r\n     * 恢复trigger\r\n     *\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    public boolean resumeTrigger(TriggerKey triggerKey);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/TriggerCenter.java",
    "content": "package com.sohu.cache.schedule;\r\n\r\nimport com.sohu.cache.entity.TriggerInfo;\r\n\r\nimport org.quartz.TriggerKey;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * trigger管理接口\r\n *\r\n * @author: lingguo\r\n * @time: 2014/10/13 14:02\r\n */\r\npublic interface TriggerCenter {\r\n\r\n\r\n    /**\r\n     * 暂停trigger\r\n     *\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    public boolean pauseTrigger(TriggerKey triggerKey);\r\n\r\n    /**\r\n     * 恢复trigger\r\n     *\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    public boolean resumeTrigger(TriggerKey triggerKey);\r\n\r\n    /**\r\n     * 删除trigger(从db中删除了)\r\n     *\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    public boolean removeTrigger(TriggerKey triggerKey);\r\n\r\n    /**\r\n     * 查询某一job类型下的所有trigger\r\n     *\r\n     * @param jobGroup job类型：redis/machine/machineMonitor\r\n     * @return\r\n     */\r\n    public List<TriggerInfo> getTriggersByJobGroup(String jobGroup);\r\n\r\n    /**\r\n     * 返回所有的trigger\r\n     *\r\n     * @return\r\n     */\r\n    public List<TriggerInfo> getAllTriggers();\r\n\r\n    /**\r\n     * 查询trigger，模糊匹配trigger name或trigger group\r\n     *\r\n     * @param queryString   trigger name或trigger group的关键字\r\n     * @return\r\n     */\r\n    public List<TriggerInfo> searchTriggerByNameOrGroup(String queryString);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/brevity/BrevityScheduleType.java",
    "content": "package com.sohu.cache.schedule.brevity;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 短频任务类型\n * Created by yijunzhang\n */\npublic enum BrevityScheduleType {\n\n    REDIS_INFO(10, 1, \"redis基本信息采集\"),\n    REDIS_SLOWLOG(11, 20, \"redis慢查询采集\"),\n    REDIS_LATENCY(12, 1, \"redis延迟事件信息采集\"),\n    MACHINE_INFO(50, 1, \"机器信息采集\"),\n    MACHINE_MONITOR(51, 20, \"机器监控报警任务\"),\n    MACHINE_NMON(52, 5, \"机器nmon数据采集\");\n\n    private static Map<Integer, BrevityScheduleType> MAP = new HashMap();\n\n    static {\n        for (BrevityScheduleType scheduleType : BrevityScheduleType.values()) {\n            MAP.put(scheduleType.type, scheduleType);\n        }\n    }\n\n    BrevityScheduleType(int type, int minutes, String info) {\n        this.type = type;\n        this.minutes = minutes;\n        this.info = info;\n    }\n\n    //类型\n    private int type;\n\n    //分钟分段\n    private int minutes;\n\n    //任务说明\n    private String info;\n\n    public static BrevityScheduleType typeOf(int type) {\n        return MAP.get(type);\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public int getMinutes() {\n        return minutes;\n    }\n\n\n    public String getInfo() {\n        return info;\n    }\n\n    @Override\n    public String toString() {\n        return \"BrevityScheduleType{\" +\n                \"type=\" + type +\n                \", minutes=\" + minutes +\n                \", info='\" + info + '\\'' +\n                '}';\n    }}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/brevity/BrevityScheduler.java",
    "content": "package com.sohu.cache.schedule.brevity;\n\n/**\n * 短频任务调度\n * Created by yijunzhang\n */\npublic interface BrevityScheduler {\n\n    /**\n     * 维护全量定时任务逻辑\n     */\n    void maintainTasks();\n\n    /**\n     * 异步竞争执行短频任务\n     *\n     * @return\n     */\n    void dispatcherTasks();\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/brevity/BrevitySchedulerJob.java",
    "content": "package com.sohu.cache.schedule.brevity;\n\nimport com.sohu.cache.schedule.jobs.CacheBaseJob;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * Created by yijunzhang\n */\npublic class BrevitySchedulerJob extends CacheBaseJob {\n    private static final long serialVersionUID = 2626836144949582163L;\n\n    /**\n     * 维护短频任务\n     *\n     * @param context\n     */\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            BrevityScheduler brevityScheduler = applicationContext.getBean(BrevityScheduler.class);\n            brevityScheduler.maintainTasks();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/brevity/DispatcherBrevityScheduleJob.java",
    "content": "package com.sohu.cache.schedule.brevity;\n\nimport com.sohu.cache.schedule.jobs.CacheBaseJob;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * Created by yijunzhang\n */\npublic class DispatcherBrevityScheduleJob extends CacheBaseJob {\n    private static final long serialVersionUID = 2626836144949582163L;\n\n    /**\n     * 维护短频任务\n     *\n     * @param context\n     */\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            BrevityScheduler brevityScheduler = applicationContext.getBean(BrevityScheduler.class);\n            brevityScheduler.dispatcherTasks();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/brevity/impl/BrevitySchedulerImpl.java",
    "content": "package com.sohu.cache.schedule.brevity.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.google.common.collect.Lists;\nimport com.google.common.util.concurrent.AtomicLongMap;\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.entity.BrevityScheduleTask;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.schedule.brevity.BrevityScheduleType;\nimport com.sohu.cache.schedule.brevity.BrevityScheduler;\nimport com.sohu.cache.server.ServerStatusCollector;\nimport com.sohu.cache.util.ScheduleUtil;\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * Created by zengyizhao\n */\n@ConditionalOnProperty(name = \"cachecloud.redis.enable\", havingValue = \"true\")\n@Component\n@Slf4j\npublic class BrevitySchedulerImpl implements BrevityScheduler {\n    private static final String _yyyyMMddHHmm = \"yyyyMMddHHmm\";\n    private static final int BATCH = 100;\n\n    @Autowired\n    private AsyncService asyncService;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Lazy\n    @Autowired\n    private RedisCenter redisCenter;\n\n    @Autowired\n    @Lazy\n    private MachineCenter machineCenter;\n\n    @Autowired\n    private ServerStatusCollector serverStatusCollector;\n\n    @Autowired\n    private AssistRedisService assistRedisService;\n\n    public static final AtomicLongMap<String> BREVITY_SCHEDULER_MAP = AtomicLongMap.create();\n\n    public static final String REDIS_KEY_PREFIX = \"cc:brevity:schedule:\";\n\n    public static final String REDIS_LOCK_KEY_PREFIX = \"cc:brevity:schedule:lock:\";\n\n    public static final int REDIS_LOCK_EXPIRE_SECONDS = 90;\n\n    @PostConstruct\n    private void init() {\n        asyncService.assemblePool(AsyncThreadPoolFactory.BREVITY_SCHEDULER_POOL,\n                AsyncThreadPoolFactory.BREVITY_SCHEDULER_ASYNC_THREAD_POOL);\n    }\n\n    @Override\n    public void maintainTasks() {\n        try {\n            BrevityScheduleType redisInfoType = BrevityScheduleType.REDIS_INFO;\n            maintainNodes(redisInfoType);\n\n            BrevityScheduleType redisSlowlogType = BrevityScheduleType.REDIS_SLOWLOG;\n            maintainNodes(redisSlowlogType);\n\n            BrevityScheduleType redisLatencyType = BrevityScheduleType.REDIS_LATENCY;\n            maintainNodes(redisLatencyType);\n\n            BrevityScheduleType machineInfoType = BrevityScheduleType.MACHINE_INFO;\n            maintainMachines(machineInfoType);\n\n            BrevityScheduleType machineMonitorType = BrevityScheduleType.MACHINE_MONITOR;\n            maintainMachines(machineMonitorType);\n\n            BrevityScheduleType machineNmonType = BrevityScheduleType.MACHINE_NMON;\n            maintainMachines(machineNmonType);\n\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n\n    }\n\n    private void maintainMachines(BrevityScheduleType scheduleType) {\n        List<BrevityScheduleTask> originalNodes = this.getOriginalMachinesOfType(scheduleType.getType());\n        List<BrevityScheduleTask> latestNodes = this.getLatestMachinesOfType(scheduleType.getType());\n        List<BrevityScheduleTask> addList = this.shouldAddMachinesOfType(originalNodes, latestNodes);\n        if (CollectionUtils.isNotEmpty(addList)) {\n            long version = getVersion();\n            Date date = new Date();\n            for (BrevityScheduleTask scheduleTask : addList) {\n                scheduleTask.setType(scheduleType.getType());\n                scheduleTask.setVersion(version);\n                scheduleTask.setCreateTime(date);\n                boolean insertRst = insertBrevityScheduler(scheduleTask);\n                if (insertRst) {\n                    log.warn(\"shouldAddMachinesOfType: task={}\", scheduleTask);\n                }\n            }\n        }\n        List<BrevityScheduleTask> removeList = this.shouldRemoveMachinesOfType(originalNodes, latestNodes);\n        if (CollectionUtils.isNotEmpty(removeList)) {\n            for (BrevityScheduleTask  scheduleTask : removeList) {\n                Long result = delBrevityScheduler(scheduleTask);\n                if (result != null && result > 0) {\n                    log.warn(\"shouldRemoveMachinesOfType: type={} task={}\", scheduleType, scheduleTask);\n                }\n            }\n        }\n    }\n\n    private void maintainNodes(BrevityScheduleType scheduleType) {\n        List<BrevityScheduleTask> originalNodes = this.getOriginalNodesOfType(scheduleType.getType());\n        List<BrevityScheduleTask> latestNodes = this.getLatestNodesOfType(scheduleType.getType());\n        List<BrevityScheduleTask> addList = this.shouldAddNodesOfType(originalNodes, latestNodes);\n        if (CollectionUtils.isNotEmpty(addList)) {\n            long version = getVersion();\n            Date date = new Date();\n            for (BrevityScheduleTask scheduleTask : addList) {\n                scheduleTask.setType(scheduleType.getType());\n                scheduleTask.setVersion(version);\n                scheduleTask.setCreateTime(date);\n                boolean insertRst = insertBrevityScheduler(scheduleTask);\n                if (insertRst) {\n                    log.warn(\"shouldAddNodesOfType: task={}\", scheduleTask);\n                }\n            }\n        }\n        List<BrevityScheduleTask> removeList = this.shouldRemoveNodesOfType(originalNodes, latestNodes);\n        if (CollectionUtils.isNotEmpty(removeList)) {\n            for (BrevityScheduleTask  scheduleTask : removeList) {\n                Long result = delBrevityScheduler(scheduleTask);\n                if (result != null && result > 0) {\n                    log.warn(\"shouldRemoveNodesOfType: type={} task={}\", scheduleType, scheduleTask);\n                }\n            }\n        }\n    }\n\n    private long getVersion() {\n        long version = Long.parseLong(DateUtil.formatDate(new Date(), _yyyyMMddHHmm));\n        return ScheduleUtil.getLastCollectTime(version);\n    }\n\n    private boolean insertBrevityScheduler(BrevityScheduleTask task) {\n        return assistRedisService.hset(REDIS_KEY_PREFIX + task.getType(), task.getKeyField(), JSONObject.toJSONString(task));\n    }\n\n    private Long delBrevityScheduler(BrevityScheduleTask  task) {\n        return assistRedisService.hdel(REDIS_KEY_PREFIX + task.getType(), task.getKeyField());\n    }\n\n    //查询原有节点\n    private List<BrevityScheduleTask> getOriginalNodesOfType(int type) {\n        Map<String, String> originalMap = assistRedisService.hgetAll(REDIS_KEY_PREFIX + type);\n        Collection<String> originalNodes = originalMap.values();\n        List<BrevityScheduleTask> originalNodeList = originalNodes.stream()\n                .map(originalNode -> JSONObject.parseObject(originalNode, BrevityScheduleTask.class))\n                .collect(Collectors.toList());\n        return originalNodeList;\n    }\n\n    //查询最新的节点\n    private List<BrevityScheduleTask> getLatestNodesOfType(int type) {\n        String sql = \"select t1.ip host,t1.port port, t1.type instanceType\"\n                + \" from instance_info  t1\"\n                + \" where t1.status != 2 and t1.type in (2,6,11,12)\";\n        List<BrevityScheduleTask> list = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(BrevityScheduleTask.class));\n        return list;\n    }\n\n    //查询应该被添加的节点\n    private List<BrevityScheduleTask> shouldAddNodesOfType(List<BrevityScheduleTask> originalNodeList,\n                                                           List<BrevityScheduleTask> latestNodeList) {\n        List<BrevityScheduleTask> shouldAdds = new ArrayList<>();\n        Set<String> originals = originalNodeList.stream()\n                .map(original -> original.getHostPort()).collect(Collectors.toSet());\n        latestNodeList.forEach(latest -> {\n            if(!originals.contains(latest.getHostPort())){\n                shouldAdds.add(latest);\n            }\n        });\n        return shouldAdds;\n    }\n\n\n    //查询应该被删除的节点\n    private List<BrevityScheduleTask> shouldRemoveNodesOfType(List<BrevityScheduleTask> originalNodeList,\n                                                              List<BrevityScheduleTask> latestNodeList) {\n        List<BrevityScheduleTask> shouldRemoves = new ArrayList<>();\n        Set<String> latests = latestNodeList.stream()\n                .map(latest -> latest.getHostPort()).collect(Collectors.toSet());\n        originalNodeList.forEach(original -> {\n            if(!latests.contains(original.getHostPort())){\n                shouldRemoves.add(original);\n            }\n        });\n        return shouldRemoves;\n    }\n\n    //查询原有节点\n    private List<BrevityScheduleTask> getOriginalMachinesOfType(int type) {\n        Map<String, String> originalMap = assistRedisService.hgetAll(REDIS_KEY_PREFIX + type);\n        Collection<String> originalMachines = originalMap.values();\n        List<BrevityScheduleTask> originalMachineList = originalMachines.stream()\n                .map(originalMachine -> JSONObject.parseObject(originalMachine, BrevityScheduleTask.class))\n                .collect(Collectors.toList());\n        return originalMachineList;\n    }\n\n    //查询最新的节点\n    private List<BrevityScheduleTask> getLatestMachinesOfType(int type) {\n        String sql = \"select t1.ip host\"\n                + \" from machine_info t1\"\n                + \" where t1.available = 1\";\n        List<BrevityScheduleTask> list = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(BrevityScheduleTask.class));\n        return list;\n    }\n\n    //查询应该被添加的机器\n    private List<BrevityScheduleTask> shouldAddMachinesOfType(\n            List<BrevityScheduleTask> originalMachineList, List<BrevityScheduleTask> latestMachineList) {\n        List<BrevityScheduleTask> shouldAdds = new ArrayList<>();\n        Set<String> originals = originalMachineList.stream()\n                .map(original -> original.getHost()).collect(Collectors.toSet());\n        latestMachineList.forEach(latest -> {\n            if(!originals.contains(latest.getHost())){\n                shouldAdds.add(latest);\n            }\n        });\n        return shouldAdds;\n    }\n\n    //查询应该被删除的机器\n    private List<BrevityScheduleTask> shouldRemoveMachinesOfType(\n            List<BrevityScheduleTask> originalMachineList, List<BrevityScheduleTask> latestMachineList) {\n        List<BrevityScheduleTask> shouldRemoves = new ArrayList<>();\n        Set<String> latests = latestMachineList.stream()\n                .map(latest -> latest.getHost()).collect(Collectors.toSet());\n        originalMachineList.forEach(original -> {\n            if(!latests.contains(original.getHost())){\n                shouldRemoves.add(original);\n            }\n        });\n        return shouldRemoves;\n    }\n\n    private List<BrevityScheduleTask> getTasks(int type, long version) {\n        Map<String, String> originalMap = assistRedisService.hgetAll(REDIS_KEY_PREFIX + type);\n        Collection<String> tasks = originalMap.values();\n        List<BrevityScheduleTask> scheduleTasks = tasks.stream().map(task -> JSONObject.parseObject(task, BrevityScheduleTask.class))\n                .filter(brevityScheduleTask -> brevityScheduleTask.getVersion() < version)\n                .collect(Collectors.toList());\n        return scheduleTasks;\n    }\n\n    private int[] batchUpdate(List<Integer> ids, long version) {\n        String sql = \"update brevity_schedule_resources set version = ? where id = ? and version < ?\";\n        List<Object[]> batchArgs = Lists.newArrayListWithCapacity(ids.size());\n        for (Integer id : ids) {\n            Object[] args = new Object[3];\n            args[0] = version;\n            args[1] = id;\n            args[2] = version;\n            batchArgs.add(args);\n        }\n        //primary-key+version 竞争\n        return jdbcTemplate.batchUpdate(sql, batchArgs);\n    }\n\n    @Override\n    public void dispatcherTasks() {\n        log.warn(\"dispatcherTasks:brevity_scheduler_map={}\", BREVITY_SCHEDULER_MAP);\n        BREVITY_SCHEDULER_MAP.clear();\n\n        long currentVersion = getVersion();\n        for (BrevityScheduleType scheduleType : BrevityScheduleType.values()) {\n            int minutes = scheduleType.getMinutes();\n            int type = scheduleType.getType();\n            log.debug(\"dispatcherTasks: type={} version={}\", type, currentVersion);\n            // 以minutes作为分段，整除代表可以被执行\n            if (currentVersion % minutes == 0) {\n                List<BrevityScheduleTask> tasks = getTasks(type, currentVersion);\n                if (CollectionUtils.isEmpty(tasks)) {\n                    log.info(\"dispatcherTasks-empty: type={} version={}\", type, currentVersion);\n                    continue;\n                }\n                //乱序\n                Collections.shuffle(tasks);\n                long dispatcherSize = 0;\n                List<BrevityScheduleTask> subTasks = new ArrayList<>();\n                for (int index = 0; index < tasks.size(); index++) {\n                    if (index > 0 && index % BATCH == 0) {\n                        log.debug(\"dispatcherTasks-subTaskIds: index={} size={}\", index, subTasks.size());\n                        // copy for async\n                        asyncSubTaskIds(Lists.newArrayList(subTasks), currentVersion, scheduleType);\n                        dispatcherSize += subTasks.size();\n                        subTasks.clear();\n                        // if machine collect ,per 100 task sleep serve times\n                        if (scheduleType.getType() == BrevityScheduleType.MACHINE_INFO.getType() ||\n                                scheduleType.getType() == BrevityScheduleType.MACHINE_MONITOR.getType() ||\n                                scheduleType.getType() == BrevityScheduleType.MACHINE_NMON.getType()) {\n                            try {\n                                TimeUnit.MILLISECONDS.sleep(5000l);\n                                log.warn(\"machine task sleep {} ms\", 5000);\n                            } catch (InterruptedException e) {\n                                e.printStackTrace();\n                            }\n                        }\n                    }\n                    subTasks.add(tasks.get(index));\n                }\n                if (subTasks.size() > 0) {\n                    dispatcherSize += subTasks.size();\n                    asyncSubTaskIds(Lists.newArrayList(subTasks), currentVersion, scheduleType);\n                }\n                log.warn(\"dispatcherTasks-dispatched: size={} type={} version={}\", dispatcherSize, type,\n                        currentVersion);\n            }\n        }\n    }\n\n    public Map<String, Object> getNodeInfoByTask(BrevityScheduleTask task) {\n        String sql = \"select t2.app_id appId,t2.ip host,t2.port port\"\n                + \" from instance_info t2 \"\n                + \" where t2.status != 2 \"\n                + \" and t2.ip = '\" + task.getHost()\n                + \"' and t2.port = \" + task.getPort();\n        return jdbcTemplate.queryForMap(sql);\n    }\n\n    public Map<String, Object> getMachineInfoByTask(BrevityScheduleTask task) {\n        String sql = \"select t2.id hostId,t2.ip host\"\n                + \" from machine_info t2 \"\n                + \" where t2.available = 1 \"\n                + \" and t2.ip = '\" + task.getHost()\n                + \"'\";\n        return jdbcTemplate.queryForMap(sql);\n    }\n\n    private void asyncSubTaskIds(final List<BrevityScheduleTask> subTasks, final long currentVersion,\n                                 final BrevityScheduleType scheduleType) {\n        asyncService.submitFuture(AsyncThreadPoolFactory.BREVITY_SCHEDULER_POOL,\n                new KeyCallable<Integer>(\"version:\" + currentVersion) {\n                    @Override\n                    public Integer execute() {\n                        //执行分组\n                        List<BrevityScheduleTask> toUnlockTasks = Lists.newArrayList();\n                        List<BrevityScheduleTask> lockTasks = Lists.newArrayList();\n                        try{\n                            for (int i = 0; i < subTasks.size(); i++) {\n                                boolean updated = assistRedisService\n                                        .setNEX(REDIS_LOCK_KEY_PREFIX + subTasks.get(i).getKeyField(), String.valueOf(subTasks.get(i).getVersion()), REDIS_LOCK_EXPIRE_SECONDS);\n                                //获得行锁\n                                if (updated) {\n                                    toUnlockTasks.add(subTasks.get(i));\n                                    String latestTaskStr = assistRedisService.hget(REDIS_KEY_PREFIX + scheduleType.getType(), subTasks.get(i).getKeyField());\n                                    BrevityScheduleTask brevityScheduleTask = JSONObject.parseObject(latestTaskStr, BrevityScheduleTask.class);\n                                    if (brevityScheduleTask.getVersion() != null && brevityScheduleTask.getVersion() >= currentVersion) {\n                                        continue;\n                                    }\n                                    subTasks.get(i).setVersion(currentVersion);\n                                    boolean hset = assistRedisService.hset(REDIS_KEY_PREFIX + scheduleType.getType(), subTasks.get(i).getKeyField(), JSONObject.toJSONString(subTasks.get(i)));\n                                    if(hset){\n                                        lockTasks.add(subTasks.get(i));\n                                        BREVITY_SCHEDULER_MAP.incrementAndGet(scheduleType.getType() + \"-\" + currentVersion);\n                                    }\n                                }\n                            }\n                            for (BrevityScheduleTask task : lockTasks) {\n                                if (scheduleType == BrevityScheduleType.REDIS_INFO) {\n                                    Map<String, Object> map = getNodeInfoByTask(task);\n                                    long appId = MapUtils.getLong(map, \"appId\");\n                                    String host = MapUtils.getString(map, \"host\");\n                                    int port = MapUtils.getIntValue(map, \"port\");\n                                    Integer instanceType = MapUtils.getInteger(map, \"instanceType\");\n                                    redisCenter.collectRedisInfo(appId, currentVersion, host, port);\n                                } else if (scheduleType == BrevityScheduleType.REDIS_SLOWLOG) {\n                                    Map<String, Object> map = getNodeInfoByTask(task);\n                                    long appId = MapUtils.getLong(map, \"appId\");\n                                    String host = MapUtils.getString(map, \"host\");\n                                    int port = MapUtils.getIntValue(map, \"port\");\n                                    Integer instanceType = MapUtils.getInteger(map, \"instanceType\");\n                                    redisCenter.collectRedisSlowLog(appId, currentVersion, host, port);\n                                } else if (scheduleType == BrevityScheduleType.MACHINE_INFO) {\n                                    Map<String, Object> map = getMachineInfoByTask(task);\n                                    long hostId = MapUtils.getLong(map, \"hostId\");\n                                    String host = MapUtils.getString(map, \"host\");\n                                    machineCenter.asyncCollectMachineInfo(hostId, currentVersion, host);\n                                } else if (scheduleType == BrevityScheduleType.MACHINE_MONITOR) {\n                                    Map<String, Object> map = getMachineInfoByTask(task);\n                                    long hostId = MapUtils.getLong(map, \"hostId\");\n                                    String host = MapUtils.getString(map, \"host\");\n                                    machineCenter.asyncMonitorMachineStats(hostId, host);\n                                } else if (scheduleType == BrevityScheduleType.MACHINE_NMON) {\n                                    Map<String, Object> map = getMachineInfoByTask(task);\n                                    String host = MapUtils.getString(map, \"host\");\n                                    serverStatusCollector.asyncFetchServerStatus(host);\n                                } else if (scheduleType == BrevityScheduleType.REDIS_LATENCY) {\n                                    Map<String, Object> map = getNodeInfoByTask(task);\n                                    long appId = MapUtils.getLong(map, \"appId\");\n                                    String host = MapUtils.getString(map, \"host\");\n                                    int port = MapUtils.getIntValue(map, \"port\");\n                                    Integer instanceType = MapUtils.getInteger(map, \"instanceType\");\n                                    redisCenter.collectRedisLatencyInfo(appId, currentVersion, host, port);\n                                }\n                            }\n\n                        } finally {\n                            List<String> keys = new ArrayList<>();\n                            toUnlockTasks.forEach(task -> keys.add(REDIS_LOCK_KEY_PREFIX + task.getKeyField()));\n                            String[] keyArray = new String[keys.size()];\n                            assistRedisService.delMulti(keys.toArray(keyArray));\n                        }\n                        log.warn(\"dispatcherTasks asyncSubTask this batch end time: {}, scheduleType:{}, provideSize:{}, handleSize:{}\"\n                                , System.currentTimeMillis(), scheduleType.getInfo(), subTasks.size(), lockTasks.size());\n                        return lockTasks.size();\n                    }\n                });\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/brevity/impl/BrevitySchedulerImpl_Original.java",
    "content": "package com.sohu.cache.schedule.brevity.impl;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.util.concurrent.AtomicLongMap;\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.entity.BrevityScheduleTask;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.schedule.brevity.BrevityScheduleType;\nimport com.sohu.cache.schedule.brevity.BrevityScheduler;\nimport com.sohu.cache.server.ServerStatusCollector;\nimport com.sohu.cache.util.ScheduleUtil;\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by yijunzhang\n */\n@ConditionalOnProperty(name = \"cachecloud.redis.enable\", havingValue = \"false\", matchIfMissing = true)\n@Component\n@Slf4j\npublic class BrevitySchedulerImpl_Original implements BrevityScheduler {\n    private static final String _yyyyMMddHHmm = \"yyyyMMddHHmm\";\n    private static final int BATCH = 100;\n\n    @Autowired\n    private AsyncService asyncService;\n\n    @Autowired\n    private JdbcTemplate jdbcTemplate;\n\n    @Lazy\n    @Autowired\n    private RedisCenter redisCenter;\n\n    @Autowired\n    @Lazy\n    private MachineCenter machineCenter;\n\n    @Autowired\n    private ServerStatusCollector serverStatusCollector;\n\n    public static final AtomicLongMap<String> BREVITY_SCHEDULER_MAP = AtomicLongMap.create();\n\n    @PostConstruct\n    private void init() {\n        asyncService.assemblePool(AsyncThreadPoolFactory.BREVITY_SCHEDULER_POOL,\n                AsyncThreadPoolFactory.BREVITY_SCHEDULER_ASYNC_THREAD_POOL);\n    }\n\n    @Override\n    public void maintainTasks() {\n        try {\n            BrevityScheduleType redisInfoType = BrevityScheduleType.REDIS_INFO;\n            maintainNodes(redisInfoType);\n\n            BrevityScheduleType redisSlowlogType = BrevityScheduleType.REDIS_SLOWLOG;\n            maintainNodes(redisSlowlogType);\n\n            BrevityScheduleType redisLatencyType = BrevityScheduleType.REDIS_LATENCY;\n            maintainNodes(redisLatencyType);\n\n            BrevityScheduleType machineInfoType = BrevityScheduleType.MACHINE_INFO;\n            maintainMachines(machineInfoType);\n\n            BrevityScheduleType machineMonitorType = BrevityScheduleType.MACHINE_MONITOR;\n            maintainMachines(machineMonitorType);\n\n            BrevityScheduleType machineNmonType = BrevityScheduleType.MACHINE_NMON;\n            maintainMachines(machineNmonType);\n\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n\n    }\n\n    private void maintainMachines(BrevityScheduleType scheduleType) {\n        List<Map<String, Object>> addList = shouldAddMachinesOfType(scheduleType.getType());\n        if (CollectionUtils.isNotEmpty(addList)) {\n            for (Map<String, Object> map : addList) {\n                BrevityScheduleTask task = BrevityScheduleTask.builder()\n                        .type(scheduleType.getType())\n                        .version(getVersion())\n                        .host(MapUtils.getString(map, \"host\"))\n                        .port(MapUtils.getIntValue(map, \"port\", 0))\n                        .createTime(new Date())\n                        .build();\n                int result = insertBrevityScheduler(task);\n                if (result > 0) {\n                    log.warn(\"shouldAddMachinesOfType: task={}\", task);\n                }\n            }\n        }\n        List<Map<String, Object>> removeList = shouldRemoveMachinesOfType(scheduleType.getType());\n        if (CollectionUtils.isNotEmpty(removeList)) {\n            for (Map<String, Object> map : removeList) {\n                int id = MapUtils.getIntValue(map, \"id\");\n                int result = delBrevityScheduler(id);\n                if (result > 0) {\n                    log.warn(\"shouldRemoveMachinesOfType: type={} id={}\", scheduleType, id);\n                }\n            }\n        }\n\n    }\n\n    private void maintainNodes(BrevityScheduleType scheduleType) {\n        List<Map<String, Object>> addList = shouldAddNodesOfType(scheduleType.getType());\n        if (CollectionUtils.isNotEmpty(addList)) {\n            for (Map<String, Object> map : addList) {\n                BrevityScheduleTask task = BrevityScheduleTask.builder()\n                        .type(scheduleType.getType())\n                        .version(getVersion())\n                        .host(MapUtils.getString(map, \"host\"))\n                        .port(MapUtils.getIntValue(map, \"port\"))\n                        .createTime(new Date())\n                        .build();\n                int result = insertBrevityScheduler(task);\n                if (result > 0) {\n                    log.warn(\"shouldAddNodesOfType: task={}\", task);\n                }\n            }\n        }\n\n        List<Map<String, Object>> removeList = shouldRemoveNodesOfType(scheduleType.getType());\n        if (CollectionUtils.isNotEmpty(removeList)) {\n            for (Map<String, Object> map : removeList) {\n                int id = MapUtils.getIntValue(map, \"id\");\n                int result = delBrevityScheduler(id);\n                if (result > 0) {\n                    log.warn(\"shouldRemoveNodesOfType: type={} id={}\", scheduleType, id);\n                }\n            }\n        }\n    }\n\n    private long getVersion() {\n        long version = Long.parseLong(DateUtil.formatDate(new Date(), _yyyyMMddHHmm));\n        return ScheduleUtil.getLastCollectTime(version);\n    }\n\n    private int insertBrevityScheduler(BrevityScheduleTask task) {\n        String sql = \"insert into brevity_schedule_resources(type,version,host,port,create_time)\"\n                + \" values(?,?,?,?,?)\";\n        return jdbcTemplate.update(sql, task.getType(), task.getVersion(), task.getHost(),\n                task.getPort(), task.getCreateTime());\n    }\n\n    private int delBrevityScheduler(int id) {\n        String sql = \"delete from  brevity_schedule_resources where id = ?\";\n        return jdbcTemplate.update(sql, id);\n    }\n\n    //查询应该被添加的节点\n    private List<Map<String, Object>> shouldAddNodesOfType(int type) {\n        String sql = \"select t1.ip host,t1.port port\"\n                + \" from instance_info  t1\"\n                + \" left join brevity_schedule_resources t2 on (t2.type=? and t1.ip=t2.host and t1.port=t2.port)\\n\"\n                + \" where t1.status != 2 and t1.type in (2,6) and t2.id is null\";\n        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, type);\n        return list;\n    }\n\n    //查询应该被删除的节点\n    private List<Map<String, Object>> shouldRemoveNodesOfType(int type) {\n        String sql = \"select t2.id id\"\n                + \" from brevity_schedule_resources t2\"\n                + \" left join instance_info  t1 on (t1.status != 2 and t1.ip=t2.host and t1.port=t2.port)\"\n                + \" where t2.type=? and t1.ip is null\";\n        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, type);\n        return list;\n    }\n\n    //查询应该被添加的机器\n    private List<Map<String, Object>> shouldAddMachinesOfType(int type) {\n        String sql = \"select t1.ip host\"\n                + \" from machine_info t1\"\n                + \" left join brevity_schedule_resources t2 on (t2.type=? and t1.ip=t2.host)\"\n                + \" where t1.available = 1 and t2.id is null\";\n        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, type);\n        return list;\n    }\n\n    //查询应该被删除的机器\n    private List<Map<String, Object>> shouldRemoveMachinesOfType(int type) {\n        String sql = \"select t2.id id\"\n                + \" from brevity_schedule_resources t2\"\n                + \" left join machine_info t1 on (t1.available = 1 and t1.ip=t2.host)\"\n                + \" where t2.type=? and t1.ip is null\";\n        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, type);\n        return list;\n    }\n\n    private List<Integer> getTaskIds(int type, long version) {\n        String sql = \"select id from brevity_schedule_resources \"\n                + \"where type=\" + type + \" and version < \" + version;\n        List<Integer> ids = jdbcTemplate.queryForList(sql, Integer.class);\n        return ids;\n    }\n\n    private int[] batchUpdate(List<Integer> ids, long version) {\n        String sql = \"update brevity_schedule_resources set version = ? where id = ? and version < ?\";\n        List<Object[]> batchArgs = Lists.newArrayListWithCapacity(ids.size());\n        for (Integer id : ids) {\n            Object[] args = new Object[3];\n            args[0] = version;\n            args[1] = id;\n            args[2] = version;\n            batchArgs.add(args);\n        }\n        //primary-key+version 竞争\n        return jdbcTemplate.batchUpdate(sql, batchArgs);\n    }\n\n    @Override\n    public void dispatcherTasks() {\n        log.warn(\"dispatcherTasks:brevity_scheduler_map={}\", BREVITY_SCHEDULER_MAP);\n        BREVITY_SCHEDULER_MAP.clear();\n\n        long currentVersion = getVersion();\n        for (BrevityScheduleType scheduleType : BrevityScheduleType.values()) {\n            int minutes = scheduleType.getMinutes();\n            int type = scheduleType.getType();\n            log.debug(\"dispatcherTasks: type={} version={}\", type, currentVersion);\n            // 以minutes作为分段，整除代表可以被执行\n            if (currentVersion % minutes == 0) {\n                List<Integer> taskIds = getTaskIds(type, currentVersion);\n                if (CollectionUtils.isEmpty(taskIds)) {\n                    log.info(\"dispatcherTasks-empty: type={} version={}\", type, currentVersion);\n                    continue;\n                }\n                //乱序\n                Collections.shuffle(taskIds);\n                long dispatcherSize = 0;\n                List<Integer> subTaskIds = new ArrayList<>();\n                for (int index = 0; index < taskIds.size(); index++) {\n                    if (index > 0 && index % BATCH == 0) {\n                        log.debug(\"dispatcherTasks-subTaskIds: index={} size={}\", index, subTaskIds.size());\n                        // copy for async\n                        asyncSubTaskIds(Lists.newArrayList(subTaskIds), currentVersion, scheduleType);\n                        dispatcherSize += subTaskIds.size();\n                        subTaskIds.clear();\n                        // if machine collect ,per 100 task sleep serve times\n                        if (scheduleType.getType() == BrevityScheduleType.MACHINE_INFO.getType() ||\n                                scheduleType.getType() == BrevityScheduleType.MACHINE_MONITOR.getType() ||\n                                scheduleType.getType() == BrevityScheduleType.MACHINE_NMON.getType()) {\n                            try {\n                                TimeUnit.MILLISECONDS.sleep(5000l);\n                                log.warn(\"machine task sleep {} ms\", 5000);\n                            } catch (InterruptedException e) {\n                                e.printStackTrace();\n                            }\n                        }\n                    }\n                    subTaskIds.add(taskIds.get(index));\n                }\n                if (subTaskIds.size() > 0) {\n                    dispatcherSize += subTaskIds.size();\n                    asyncSubTaskIds(Lists.newArrayList(subTaskIds), currentVersion, scheduleType);\n                }\n                log.warn(\"dispatcherTasks-dispatched: size={} type={} version={}\", dispatcherSize, type,\n                        currentVersion);\n            }\n        }\n    }\n\n    public Map<String, Object> getNodeInfoByLockId(int lockId) {\n        String sql = \"select t2.app_id appId,t2.ip host,t2.port port\"\n                + \" from brevity_schedule_resources t1\"\n                + \" inner join instance_info t2 on (t1.host=t2.ip and t1.port=t2.port)\"\n                + \" where t1.id = \" + lockId;\n        return jdbcTemplate.queryForMap(sql);\n    }\n\n    public Map<String, Object> getMachineInfoByLockId(int lockId) {\n        String sql = \"select t2.id hostId,t2.ip host\"\n                + \" from brevity_schedule_resources t1\"\n                + \" inner join machine_info t2 on t1.host=t2.ip\"\n                + \" where t1.id = \" + lockId;\n        return jdbcTemplate.queryForMap(sql);\n    }\n\n    private void asyncSubTaskIds(final List<Integer> subTaskIds, final long currentVersion,\n                                 final BrevityScheduleType scheduleType) {\n        asyncService.submitFuture(AsyncThreadPoolFactory.BREVITY_SCHEDULER_POOL,\n                new KeyCallable<Integer>(\"version:\" + currentVersion) {\n                    @Override\n                    public Integer execute() {\n                        //执行分组\n                        int[] batchUpdates = batchUpdate(subTaskIds, currentVersion);\n                        List<Integer> lockIds = Lists.newArrayList();\n                        for (int i = 0; i < subTaskIds.size(); i++) {\n                            int updated = batchUpdates[i];\n                            //获得行锁\n                            if (updated > 0) {\n                                lockIds.add(subTaskIds.get(i));\n                                BREVITY_SCHEDULER_MAP.incrementAndGet(scheduleType.getType() + \"-\" + currentVersion);\n                            }\n                        }\n                        for (Integer lockId : lockIds) {\n                            if (scheduleType == BrevityScheduleType.REDIS_INFO) {\n                                Map<String, Object> map = getNodeInfoByLockId(lockId);\n                                long appId = MapUtils.getLong(map, \"appId\");\n                                String host = MapUtils.getString(map, \"host\");\n                                int port = MapUtils.getIntValue(map, \"port\");\n                                redisCenter.collectRedisInfo(appId, currentVersion, host, port);\n                            } else if (scheduleType == BrevityScheduleType.REDIS_SLOWLOG) {\n                                Map<String, Object> map = getNodeInfoByLockId(lockId);\n                                long appId = MapUtils.getLong(map, \"appId\");\n                                String host = MapUtils.getString(map, \"host\");\n                                int port = MapUtils.getIntValue(map, \"port\");\n                                redisCenter.collectRedisSlowLog(appId, currentVersion, host, port);\n                            } else if (scheduleType == BrevityScheduleType.MACHINE_INFO) {\n                                Map<String, Object> map = getMachineInfoByLockId(lockId);\n                                long hostId = MapUtils.getLong(map, \"hostId\");\n                                String host = MapUtils.getString(map, \"host\");\n                                machineCenter.asyncCollectMachineInfo(hostId, currentVersion, host);\n                            } else if (scheduleType == BrevityScheduleType.MACHINE_MONITOR) {\n                                Map<String, Object> map = getMachineInfoByLockId(lockId);\n                                long hostId = MapUtils.getLong(map, \"hostId\");\n                                String host = MapUtils.getString(map, \"host\");\n                                machineCenter.asyncMonitorMachineStats(hostId, host);\n                            } else if (scheduleType == BrevityScheduleType.MACHINE_NMON) {\n                                Map<String, Object> map = getMachineInfoByLockId(lockId);\n                                String host = MapUtils.getString(map, \"host\");\n                                serverStatusCollector.asyncFetchServerStatus(host);\n                            } else if (scheduleType == BrevityScheduleType.REDIS_LATENCY) {\n                                Map<String, Object> map = getNodeInfoByLockId(lockId);\n                                long appId = MapUtils.getLong(map, \"appId\");\n                                String host = MapUtils.getString(map, \"host\");\n                                int port = MapUtils.getIntValue(map, \"port\");\n                                redisCenter.collectRedisLatencyInfo(appId, currentVersion, host, port);\n                            }\n                        }\n                        return lockIds.size();\n                    }\n                });\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/impl/SchedulerCenterImpl.java",
    "content": "package com.sohu.cache.schedule.impl;\r\n\r\nimport com.sohu.cache.dao.QuartzDao;\r\nimport com.sohu.cache.entity.TriggerInfo;\r\nimport com.sohu.cache.schedule.SchedulerCenter;\r\nimport org.quartz.Scheduler;\r\nimport org.quartz.SchedulerException;\r\nimport org.quartz.Trigger;\r\nimport org.quartz.TriggerKey;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 根据scheduler控制job、trigger和scheduler的执行和状态\r\n * <p/>\r\n * User: lingguo\r\n */\r\n@Service(\"schedulerCenter\")\r\npublic class SchedulerCenterImpl implements SchedulerCenter {\r\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\r\n\r\n    // 注入预定义的scheduler\r\n    @Autowired\r\n    private Scheduler clusterScheduler;\r\n    @Autowired\r\n    private QuartzDao quartzDao;\r\n\r\n    /**\r\n     * 删除trigger\r\n     *\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    @Override\r\n    public boolean unscheduleJob(TriggerKey triggerKey) {\r\n        boolean opResult = true;\r\n        try {\r\n            opResult = clusterScheduler.checkExists(triggerKey);\r\n            if (opResult) {\r\n                opResult = clusterScheduler.unscheduleJob(triggerKey);\r\n            }\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n            opResult = false;\r\n        }\r\n        return opResult;\r\n    }\r\n\r\n    @Override\r\n    public Trigger getTrigger(TriggerKey triggerKey) {\r\n        Trigger trigger = null;\r\n        try {\r\n            trigger = clusterScheduler.getTrigger(triggerKey);\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        return trigger;\r\n    }\r\n\r\n    @Override\r\n    public List<TriggerInfo> getAllTriggers() {\r\n        return quartzDao.getAllTriggers();\r\n    }\r\n\r\n    @Override\r\n    public List<TriggerInfo> getTriggersByNameOrGroup(String query) {\r\n        return quartzDao.searchTriggerByNameOrGroup(query);\r\n    }\r\n\r\n    @Override\r\n    public boolean pauseTrigger(TriggerKey triggerKey) {\r\n        try {\r\n            boolean exists = clusterScheduler.checkExists(triggerKey);\r\n            if (exists) {\r\n                clusterScheduler.pauseTrigger(triggerKey);\r\n                return true;\r\n            }\r\n            logger.error(\"triggerKey={} not exists\", triggerKey);\r\n            return false;\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return false;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean resumeTrigger(TriggerKey triggerKey) {\r\n        try {\r\n            boolean exists = clusterScheduler.checkExists(triggerKey);\r\n            if (exists) {\r\n                clusterScheduler.resumeTrigger(triggerKey);\r\n                return true;\r\n            }\r\n            logger.error(\"triggerKey={} not exists\", triggerKey);\r\n            return false;\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return false;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/impl/TriggerCenterImpl.java",
    "content": "package com.sohu.cache.schedule.impl;\r\n\r\nimport com.sohu.cache.dao.QuartzDao;\r\nimport com.sohu.cache.entity.TriggerInfo;\r\nimport com.sohu.cache.schedule.TriggerCenter;\r\nimport org.quartz.Scheduler;\r\nimport org.quartz.SchedulerException;\r\nimport org.quartz.TriggerKey;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * trigger管理接口的实现\r\n *\r\n * @author: lingguo\r\n * @time: 2014/10/13 14:03\r\n */\r\n@Service(\"triggerCenter\")\r\npublic class TriggerCenterImpl implements TriggerCenter {\r\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\r\n    @Autowired\r\n    private Scheduler clusterScheduler;\r\n    @Autowired\r\n    private QuartzDao quartzDao;\r\n\r\n    /**\r\n     * 暂停trigger\r\n     *\r\n     * @param triggerKey\r\n     * @return 操作成功返回true，否则返回false；\r\n     */\r\n    @Override\r\n    public boolean pauseTrigger(TriggerKey triggerKey) {\r\n        boolean opResult = true;\r\n        try {\r\n            clusterScheduler.pauseTrigger(triggerKey);\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n            opResult = false;\r\n        }\r\n        return opResult;\r\n    }\r\n\r\n    /**\r\n     * 恢复暂停的trigger\r\n     *\r\n     * @param triggerKey\r\n     */\r\n    @Override\r\n    public boolean resumeTrigger(TriggerKey triggerKey) {\r\n        boolean opResult = true;\r\n        try {\r\n            clusterScheduler.resumeTrigger(triggerKey);\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n            opResult = false;\r\n        }\r\n        return opResult;\r\n    }\r\n\r\n    /**\r\n     * 删除一个trigger\r\n     *\r\n     * @param triggerKey\r\n     * @return\r\n     */\r\n    @Override\r\n    public boolean removeTrigger(TriggerKey triggerKey) {\r\n        boolean opResult = true;\r\n        try {\r\n            clusterScheduler.unscheduleJob(triggerKey);\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n            opResult = false;\r\n        }\r\n        return opResult;\r\n    }\r\n\r\n    /**\r\n     * 查询特定job类型下的所有trigger\r\n     *\r\n     * @param jobGroup job类型：redis/machine/machineMonitor\r\n     * @return\r\n     */\r\n    @Override\r\n    public List<TriggerInfo> getTriggersByJobGroup(String jobGroup) {\r\n        List<TriggerInfo> triggersOfGroup = null;\r\n        try {\r\n            triggersOfGroup = quartzDao.getTriggersByJobGroup(jobGroup);\r\n        } catch (Exception e) {\r\n            logger.error(\"jobGroup: {}\", jobGroup, e);\r\n        }\r\n        return triggersOfGroup;\r\n    }\r\n\r\n    /**\r\n     * 返回所有的trigger\r\n     *\r\n     * @return\r\n     */\r\n    @Override\r\n    public List<TriggerInfo> getAllTriggers() {\r\n        List<TriggerInfo> allTriggers = null;\r\n        try {\r\n            allTriggers = quartzDao.getAllTriggers();\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        return allTriggers;\r\n    }\r\n\r\n    /**\r\n     * 查询trigger，模糊匹配trigger name或trigger group\r\n     *\r\n     * @param queryString   trigger name或trigger group的关键字\r\n     * @return\r\n     */\r\n    @Override\r\n    public List<TriggerInfo> searchTriggerByNameOrGroup(String queryString) {\r\n        List<TriggerInfo> matchTriggers = null;\r\n        try {\r\n            matchTriggers = quartzDao.searchTriggerByNameOrGroup(queryString);\r\n        } catch (Exception e) {\r\n            logger.error(\"queryString: {}\", queryString, e);\r\n        }\r\n        return matchTriggers;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/AppCapacityMonitorJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.web.service.AppAutoCapacityService;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.quartz.SchedulerException;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * Description: 定时更新内存使用最大值\n * @author zengyizhao\n * @version 1.0\n * @date 2022/10/11\n */\npublic class AppCapacityMonitorJob extends CacheBaseJob {\n\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            try {\n                AppAutoCapacityService appAutoCapacityService = applicationContext.getBean(\"appAutoCapacityService\", AppAutoCapacityService.class);\n                appAutoCapacityService.updateAppMemUsedHistory();\n            } catch (Exception e) {\n                logger.error(\"updateAppMemUsedHistory error {}\", e.getMessage());\n            }\n        } catch (SchedulerException e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/AppDailyJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.stats.app.AppDailyDataCenter;\nimport com.sohu.cache.util.EnvUtil;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.quartz.SchedulerException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.env.Environment;\n\n/**\n * 发送日报\n *\n * @author leifu\n * @Date 2016年8月12日\n * @Time 上午11:25:09\n */\npublic class AppDailyJob extends CacheBaseJob {\n\n    private static final long serialVersionUID = 7751425759758902400L;\n\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            Environment env = applicationContext.getBean(Environment.class);\n            if (EnvUtil.isDev(env)) {\n                logger.warn(\"environment is dev ignored\");\n                return;\n            }\n\n            try {\n                AppDailyDataCenter appDailyDataCenter = applicationContext.getBean(\"appDailyDataCenter\", AppDailyDataCenter.class);\n                appDailyDataCenter.sendAppDailyEmail();\n            } catch (Exception e) {\n                logger.error(\"sendAppDailyEmail error\", e.getMessage());\n            }\n        } catch (SchedulerException e) {\n            logger.error(e.getMessage(), e);\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/AppPersistenceCheckJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.stats.app.AppPersistenceCheckCenter;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.quartz.SchedulerException;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * Description: 应用持久化配置检查修正\n * @author zengyizhao\n * @version 1.0\n * @date 2022/11/3\n */\npublic class AppPersistenceCheckJob extends CacheBaseJob {\n\n    private static final long serialVersionUID = 7751425759758902400L;\n\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            try {\n                AppPersistenceCheckCenter appPersistenceCheckCenter = applicationContext.getBean(\"appPersistenceCheckCenter\", AppPersistenceCheckCenter.class);\n                appPersistenceCheckCenter.checkAndFixAppPersistence();\n            } catch (Exception e) {\n                logger.error(\"checkAndFixAppPersistence error\", e.getMessage());\n            }\n        } catch (SchedulerException e) {\n            logger.error(e.getMessage(), e);\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CacheBaseJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\r\n\r\nimport org.quartz.Job;\r\nimport org.quartz.JobExecutionContext;\r\nimport org.quartz.JobExecutionException;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport java.io.Serializable;\r\n\r\n/**\r\n * job父类，包含一个抽象函方法，将实现推迟到具体的子类\r\n * User: lingguo\r\n */\r\npublic abstract class CacheBaseJob implements Job, Serializable {\r\n    private static final long serialVersionUID = -6605766126594260961L;\r\n    protected Logger logger = LoggerFactory.getLogger(this.getClass());\r\n\r\n    protected final static String APPLICATION_CONTEXT_KEY = \"applicationContext\";\r\n\r\n    // 抽象方法，由子类实现，即具体的业务逻辑\r\n    public abstract void action(JobExecutionContext context);\r\n\r\n    /**\r\n     * 统计时间\r\n     *\r\n     * @param context\r\n     * @throws JobExecutionException\r\n     */\r\n    @Override\r\n    public void execute(JobExecutionContext context) throws JobExecutionException {\r\n        long start = System.currentTimeMillis();\r\n        this.action(context);\r\n        long cost = System.currentTimeMillis() - start;\r\n        if (cost > 2000) {\r\n            logger.warn(\"slowJob: job: {}, trigger: {}, cost: {} ms\", context.getJobDetail().getKey(),\r\n                    context.getTrigger().getKey(), cost);\r\n        } else {\r\n            logger.debug(\"job: {}, trigger: {}, cost: {} ms\", context.getJobDetail().getKey(),\r\n                    context.getTrigger().getKey(), cost);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CleanupDayAppClientStatJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\n\n/**\n * Created by rucao on 2020/1/8\n */\npublic class CleanupDayAppClientStatJob extends CacheBaseJob {\n    private static final long serialVersionUID = 8815839394475276540L;\n    /**\n     * 清除命令统计&异常统计\n     */\n    private static int BATCH_SIZE = 1000;\n    private static final String CLEAN_APP_CLIENT_COMMAND_MINUTE_STATISTICS = \"delete from app_client_command_minute_statistics where current_min < ? limit \" + BATCH_SIZE;\n    private static final String CLEAN_APP_CLIENT_EXCEPTION_MINUTE_STATISTICS = \"delete from app_client_exception_minute_statistics where current_min < ? limit \" + BATCH_SIZE;\n    private static final String CLEAN_APP_CLIENT_LATENCY_COMMAND = \"delete from app_client_latency_command where create_time < ? limit \" + BATCH_SIZE;\n    private static final String CLEAN_APP_CLIENT_STATISTIC_GATHER = \"delete from app_client_statistic_gather where gather_time < ? limit \" + BATCH_SIZE;\n    private static final String CLEAN_INSTANCE_LATENCY_HISTORY = \"delete from instance_latency_history where execute_date < ? limit \" + BATCH_SIZE;\n\n    JdbcTemplate jdbcTemplate = null;\n\n    @Override\n    public void action(JobExecutionContext context) {\n        if (!ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA) {\n            logger.warn(\"whether_schedule_clean_data is false , ignored\");\n            return;\n        }\n        try {\n            logger.warn(\"begin-CleanupDayAppClientStatJob\");\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            jdbcTemplate = applicationContext.getBean(\"jdbcTemplate\", JdbcTemplate.class);\n\n            Calendar calendar = Calendar.getInstance();\n            calendar.setTime(new Date());\n            calendar.add(Calendar.DAY_OF_MONTH, -14);\n            long timeFormat = NumberUtils.toLong(new SimpleDateFormat(\"yyyyMMddHHmm00\").format(calendar.getTime()));\n            String date = new SimpleDateFormat(\"yyyy-MM-dd\").format(calendar.getTime());\n\n            /**\n             * 清除命令统计&异常统计（保存14天）\n             */\n            long cleanCount = 0;\n            try{\n                cleanCount = scrollDelete(CLEAN_APP_CLIENT_COMMAND_MINUTE_STATISTICS, timeFormat);\n                logger.warn(\"clean_app_client_command_minute_statistics count={}\", cleanCount);\n            }catch (Exception e){\n                logger.error(\"clean_app_client_command_minute_statistics error, \", e);\n            }\n            try{\n                cleanCount = scrollDelete(CLEAN_APP_CLIENT_EXCEPTION_MINUTE_STATISTICS, timeFormat);\n                logger.warn(\"clean_app_client_exception_minute_statistics count={}\", cleanCount);\n            }catch (Exception e){\n                logger.error(\"clean_app_client_exception_minute_statistics error, \", e);\n            }\n            try{\n                cleanCount = scrollDelete(CLEAN_APP_CLIENT_LATENCY_COMMAND, calendar.getTime());\n                logger.warn(\"clean_app_client_latency_command count={}\", cleanCount);\n            }catch (Exception e){\n                logger.error(\"clean_app_client_latency_command error, \", e);\n            }\n            try{\n                cleanCount = scrollDelete(CLEAN_APP_CLIENT_STATISTIC_GATHER, date);\n                logger.warn(\"clean_app_client_statistic_gather count={}\", cleanCount);\n            }catch (Exception e){\n                logger.error(\"clean_app_client_statistic_gather error, \", e);\n            }\n            try{\n                cleanCount = scrollDelete(CLEAN_INSTANCE_LATENCY_HISTORY, timeFormat);\n                logger.warn(\"clean_instance_latency_history count={}\", cleanCount);\n            }catch (Exception e){\n                logger.error(\"clean_instance_latency_history error, \", e);\n            }\n            logger.warn(\"end-CleanupDayAppClientStatJob\");\n        } catch (Exception e) {\n            logger.error(\"CleanupDayAppClientStatJob error, \", e);\n        }\n    }\n\n    /**\n     * 滚动删除表数据\n     */\n    private long scrollDelete(String sql, Object time) {\n        long totalCount = 0;\n        while (true) {\n            int cleanCount = jdbcTemplate.update(sql, time);\n            totalCount += cleanCount;\n            if (cleanCount == 0) {\n                break;\n            }\n        }\n        return totalCount;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CleanupDayDimensionalityJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\n\n/**\n * 清理天维度数据任务\n * Created by yijunzhang\n */\npublic class CleanupDayDimensionalityJob extends CacheBaseJob {\n\n    private static final long serialVersionUID = 8815839394475276540L;\n\n    private static int BATCH_SIZE = 1000;\n\n    private static final String CLEAN_APP_HOUR_COMMAND_STATISTICS = \"delete from app_hour_command_statistics where create_time < ? limit \" + BATCH_SIZE;\n\n    private static final String CLEAN_APP_MINUTE_COMMAND_STATISTICS = \"delete from app_minute_command_statistics where create_time < ? limit \" + BATCH_SIZE;\n\n    private static final String CLEAN_APP_HOUR_STATISTICS = \"delete from app_hour_statistics where create_time < ? limit \" + BATCH_SIZE;\n\n    private static final String CLEAN_APP_MINUTE_STATISTICS = \"delete from app_minute_statistics where create_time < ? limit \" + BATCH_SIZE;\n    /**\n     * 清除客户端耗时汇总数据\n     */\n    private static final String CLEAN_APP_CLIENT_MINUTE_COST_TOTAL = \"delete from app_client_costtime_minute_stat_total where collect_time < ? limit \" + BATCH_SIZE;\n\n    //清除服务器统计数据\n    private static final String CLEAN_SERVER_STAT_STATISTICS = \"delete from server_stat where cdate < ? limit \" + BATCH_SIZE;\n\n    /**\n     * 清除实例基础统计\n     */\n    private static final String CLEAN_INSTANCE_MINUTE_STATS = \"delete from instance_minute_stats where collect_time < ? limit \" + BATCH_SIZE;\n\n    JdbcTemplate jdbcTemplate = null;\n\n    @Override\n    public void action(JobExecutionContext context) {\n        if (!ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA) {\n            logger.warn(\"whether_schedule_clean_data is false , ignored\");\n            return;\n        }\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            jdbcTemplate = applicationContext.getBean(\"jdbcTemplate\", JdbcTemplate.class);\n\n            Calendar calendar = Calendar.getInstance();\n            calendar.setTime(new Date());\n\n            // 清除应用&命令统计数据(保存31天)\n            calendar.add(Calendar.DAY_OF_MONTH, -31);\n            Date time = calendar.getTime();\n            long cleanCount = 0;\n            cleanCount = scrollDelete(CLEAN_APP_HOUR_COMMAND_STATISTICS, time);\n            logger.warn(\"clean_app_hour_command_statistics count={}\", cleanCount);\n            cleanCount = scrollDelete(CLEAN_APP_MINUTE_COMMAND_STATISTICS, time);\n            logger.warn(\"clean_app_minute_command_statistics count={}\", cleanCount);\n            cleanCount = scrollDelete(CLEAN_APP_HOUR_STATISTICS, time);\n            logger.warn(\"clean_app_hour_statistics count={}\", cleanCount);\n            cleanCount = scrollDelete(CLEAN_APP_MINUTE_STATISTICS, time);\n            logger.warn(\"clean_app_minute_statistics count={}\", cleanCount);\n\n            //清除服务器统计数据\n            calendar.setTime(new Date());\n            calendar.add(Calendar.DAY_OF_MONTH, -7);\n            String date = new SimpleDateFormat(\"yyyy-MM-dd\").format(calendar.getTime());\n            cleanCount = scrollDelete(CLEAN_SERVER_STAT_STATISTICS, date);\n            logger.warn(\"clean_server_stat_total count={}\", cleanCount);\n\n            long timeFormat = NumberUtils.toLong(new SimpleDateFormat(\"yyyyMMddHHmm00\").format(calendar.getTime()));\n            //清除进程级别统计数据(保存5天)\n            long start = System.currentTimeMillis();\n            timeFormat = NumberUtils.toLong(new SimpleDateFormat(\"yyyyMMddHHmm\").format(DateUtils.addDays(new Date(), -5)));\n            cleanCount = scrollDelete(CLEAN_INSTANCE_MINUTE_STATS, timeFormat);\n            logger.warn(\"clean_instance_minute_stats timeFormat={} count={} cost={}s\", timeFormat, cleanCount, (System.currentTimeMillis() - start) / 1000);\n\n            //清除客户端耗时汇总数据(保存14天)\n            calendar.setTime(new Date());\n            calendar.add(Calendar.DAY_OF_MONTH, -14);\n            timeFormat = NumberUtils.toLong(new SimpleDateFormat(\"yyyyMMddHHmm00\").format(calendar.getTime()));\n            cleanCount = jdbcTemplate.update(CLEAN_APP_CLIENT_MINUTE_COST_TOTAL, timeFormat);\n            logger.warn(\"clean_app_client_costtime_minute_stat_total count={}\", cleanCount);\n\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n    }\n\n    /**\n     * 滚动删除表数据\n     */\n    private long scrollDelete(String sql, Object time) {\n        long totalCount = 0;\n        while (true) {\n            int cleanCount = jdbcTemplate.update(sql, time);\n            totalCount += cleanCount;\n            if (cleanCount == 0) {\n                break;\n            }\n        }\n        return totalCount;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CleanupMinuteDimensionalityJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\r\n\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.apache.commons.lang.time.DateUtils;\r\nimport org.quartz.JobExecutionContext;\r\nimport org.quartz.SchedulerContext;\r\nimport org.springframework.context.ApplicationContext;\r\nimport org.springframework.jdbc.core.JdbcTemplate;\r\n\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\n\r\n/**\r\n * 清理分钟维度数据任务\r\n * Created by yijunzhang\r\n */\r\npublic class CleanupMinuteDimensionalityJob extends CacheBaseJob {\r\n\r\n    private static final long serialVersionUID = 8815839394475276540L;\r\n\r\n    /**\r\n     * 实例基准数据，主要用于报警\r\n     */\r\n    private static final String CLEAN_STANDARD_STATISTICS = \"delete from standard_statistics where collect_time < ?\";\r\n\r\n    @Override\r\n    public void action(JobExecutionContext context) {\r\n        if (!ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA) {\r\n            logger.warn(\"whether_schedule_clean_data is false , ignored\");\r\n            return;\r\n        }\r\n        try {\r\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\r\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\r\n            JdbcTemplate jdbcTemplate = applicationContext.getBean(\"jdbcTemplate\", JdbcTemplate.class);\r\n\r\n            //清除进程级别统计数据(保存最近10分钟)\r\n            long start = System.currentTimeMillis();\r\n            Date date = DateUtils.addMinutes(new Date(), -10);\r\n            long timeFormat = NumberUtils.toLong(new SimpleDateFormat(\"yyyyMMddHHmm\").format(date));\r\n            int cleanCount = jdbcTemplate.update(CLEAN_STANDARD_STATISTICS, timeFormat);\r\n            logger.warn(\"clean_standard_statistics timeFormat={} count={} cost:{} ms\", timeFormat, cleanCount, (System.currentTimeMillis() - start));\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/ExpAppsDailyJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.stats.admin.CoreAppsStatCenter;\nimport com.sohu.cache.util.EnvUtil;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.quartz.SchedulerException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.env.Environment;\n\n/**\n * 发送日报\n *\n * @author leifu\n * @Date 2016年8月12日\n * @Time 上午11:25:09\n */\npublic class ExpAppsDailyJob extends CacheBaseJob {\n\n\n    private static final long serialVersionUID = 8864245475347417291L;\n\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            Environment env = applicationContext.getBean(Environment.class);\n            if (EnvUtil.isDev(env)) {\n                logger.warn(\"environment is dev ignored\");\n                return;\n            }\n\n            try {\n                CoreAppsStatCenter coreAppsStatCenter = applicationContext.getBean(\"coreAppsStatCenter\", CoreAppsStatCenter.class);\n                coreAppsStatCenter.sendExpAppsStatDataEmail(null);\n                logger.info(\"expAppsStatData daily email\");\n            } catch (Exception e) {\n                logger.error(\"expAppClientStat daily report error\", e.getMessage());\n            }\n\n        } catch (SchedulerException e) {\n            logger.error(e.getMessage(), e);\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/GatherAppClientStatisticsJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.client.service.AppClientStatisticGatherService;\nimport com.sohu.cache.entity.TimeBetween;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\n\nimport java.util.Date;\n\n/**\n * Created by rucao on 2019/12/30\n */\npublic class GatherAppClientStatisticsJob extends CacheBaseJob {\n    private static final long serialVersionUID = 8815839394475276540L;\n    private final static String COLLECT_TIME_FORMAT = \"yyyyMMddHHmm00\";\n\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            logger.warn(\"begin-gatherAppClientStatisticsJob\");\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            AppClientStatisticGatherService appClientStatisticGatherService = applicationContext.getBean(\"appClientStatisticGatherService\", AppClientStatisticGatherService.class);\n\n            //前5-10分钟\n            TimeBetween timeBetween = fillWithDateFormat();\n            long startTime = timeBetween.getStartTime();\n            long endTime = timeBetween.getEndTime();\n\n            appClientStatisticGatherService.bathAdd(startTime, endTime);\n            logger.warn(\"end-gatherAppClientStatisticsJob, startTime={} endTime:{}\", startTime, endTime);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    private TimeBetween fillWithDateFormat() {\n        Date endDate = DateUtils.addMinutes(new Date(), -5);\n        Date startDate = DateUtils.addMinutes(endDate, -5);\n        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n        long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));\n        return new TimeBetween(startTime, endTime, startDate, endDate);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/GatherAppClientStatisticsServerCmdCountJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.client.service.AppClientStatisticGatherService;\nimport com.sohu.cache.entity.TimeBetween;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\n\nimport java.util.Date;\n\n/**\n * Description: server端 命令调用次数统计\n * @author zengyizhao\n * @version 1.0\n * @date 2024/05/14\n */\npublic class GatherAppClientStatisticsServerCmdCountJob extends CacheBaseJob {\n    private final static String COLLECT_TIME_FORMAT = \"yyyyMMddHH\";\n\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            logger.warn(\"begin-gatherAppClientStatisticsServerCmdCountJob\");\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            AppClientStatisticGatherService appClientStatisticGatherService = applicationContext.getBean(\"appClientStatisticGatherService\", AppClientStatisticGatherService.class);\n\n            //前5-10分钟\n            TimeBetween timeBetween = fillWithDateFormat();\n            long startTime = timeBetween.getStartTime();\n            long endTime = timeBetween.getEndTime();\n\n            appClientStatisticGatherService.bathAddServerCmdCount(startTime, endTime);\n            logger.warn(\"end-gatherAppClientStatisticsServerCmdCountJob, startTime={} endTime:{}\", startTime, endTime);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    private TimeBetween fillWithDateFormat() {\n        Date endDate = DateUtils.addHours(new Date(), -1);\n        Date startDate = endDate;\n        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n        long endTime = startTime;\n        return new TimeBetween(startTime, endTime, startDate, endDate);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/InstanceAlertValueJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\r\n\r\nimport com.sohu.cache.util.EnvUtil;\r\nimport org.quartz.JobExecutionContext;\r\nimport org.quartz.SchedulerContext;\r\nimport org.quartz.SchedulerException;\r\nimport org.springframework.context.ApplicationContext;\r\n\r\nimport com.sohu.cache.schedule.jobs.CacheBaseJob;\r\nimport com.sohu.cache.stats.instance.InstanceAlertConfigService;\r\nimport org.springframework.core.env.Environment;\r\n\r\n/**\r\n * 实例分钟报警\r\n * @author leifu\r\n * @Date 2016年9月13日\r\n * @Time 下午3:53:04\r\n */\r\npublic class InstanceAlertValueJob extends CacheBaseJob {\r\n\r\n    private static final long serialVersionUID = 1035952011763660681L;\r\n\r\n    @Override\r\n    public void action(JobExecutionContext context) {\r\n        try {\r\n            long startTime = System.currentTimeMillis();\r\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\r\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\r\n\r\n            Environment env = applicationContext.getBean(Environment.class);\r\n            if (EnvUtil.isDev(env)) {\r\n                logger.warn(\"environment is dev ignored\");\r\n                return;\r\n            }\r\n\r\n            InstanceAlertConfigService instanceAlertConfigService = applicationContext.getBean(\"instanceAlertConfigService\", InstanceAlertConfigService.class);\r\n            instanceAlertConfigService.monitorLastMinuteAllInstanceInfo();\r\n            logger.info(\"InstanceAlertValueJob cost time {} ms\", (System.currentTimeMillis() - startTime));\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        \r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/InstanceStatJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.inspect.impl.InstanceStateInspector;\nimport com.sohu.cache.util.EnvUtil;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.quartz.SchedulerException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.env.Environment;\n\n/**\n * Created by chenshi on 2020/5/27.\n */\npublic class InstanceStatJob extends CacheBaseJob{\n    @Override\n    public void action(JobExecutionContext context) {\n\n        try {\n            long startTime = System.currentTimeMillis();\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n\n            Environment env = applicationContext.getBean(Environment.class);\n            if (EnvUtil.isDev(env)) {\n                logger.warn(\"environment is dev ignored\");\n                return;\n            }\n\n            InstanceStateInspector instanceStateInspector = applicationContext.getBean(\"instanceStateInspector\", InstanceStateInspector.class);\n            instanceStateInspector.inspect();\n            logger.info(\"InstanceAlertValueJob cost time {} ms\", (System.currentTimeMillis() - startTime));\n        } catch (SchedulerException e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/ReviseAppClientStatisGatherJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.client.service.AppClientStatisticGatherService;\nimport com.sohu.cache.entity.TimeBetween;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\n\nimport java.util.Date;\n\n/**\n * Created by rucao on 2020/3/27\n */\npublic class ReviseAppClientStatisGatherJob extends CacheBaseJob {\n\n    private static final long serialVersionUID = -5968147536403452672L;\n    private final static String COLLECT_TIME_FORMAT = \"yyyyMMdd000000\";\n\n    @Override\n    public void action(JobExecutionContext context) {\n        try {\n            logger.warn(\"begin-reviseAppClientStatisGatherJob\");\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            AppClientStatisticGatherService appClientStatisticGatherService = applicationContext.getBean(\"appClientStatisticGatherService\", AppClientStatisticGatherService.class);\n\n            //前一天\n            TimeBetween timeBetween = fillWithDateFormat();\n            long startTime = timeBetween.getStartTime();\n            long endTime = timeBetween.getEndTime();\n            appClientStatisticGatherService.bathSave(startTime, endTime);\n            logger.warn(\"end-reviseAppClientStatisGatherJob, startTime={} endTime:{}\", startTime, endTime);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    private TimeBetween fillWithDateFormat() {\n        Date endDate = new Date();\n        Date startDate = DateUtils.addDays(endDate, -1);\n        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n        long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));\n        return new TimeBetween(startTime, endTime, startDate, endDate);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/SystemConfigRefreshJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\r\n\r\nimport org.quartz.JobExecutionContext;\r\nimport org.quartz.SchedulerContext;\r\nimport org.quartz.SchedulerException;\r\nimport org.springframework.context.ApplicationContext;\r\n\r\nimport com.sohu.cache.schedule.jobs.CacheBaseJob;\r\nimport com.sohu.cache.web.service.ConfigService;\r\n\r\n/**\r\n * 刷新系统配置\r\n * @author leifu\r\n * @Date 2016年6月30日\r\n * @Time 下午5:30:42\r\n */\r\npublic class SystemConfigRefreshJob extends CacheBaseJob {\r\n\r\n    private static final long serialVersionUID = 7751425759758902400L;\r\n\r\n    @Override\r\n    public void action(JobExecutionContext context) {\r\n        try {\r\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\r\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\r\n            ConfigService configService = applicationContext.getBean(\"configService\", ConfigService.class);\r\n            configService.reloadSystemConfig();\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        \r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/TaskExecuteJob.java",
    "content": "package com.sohu.cache.schedule.jobs;\n\nimport com.sohu.cache.task.TaskService;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.SchedulerContext;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * @author fulei\n */\npublic class TaskExecuteJob extends CacheBaseJob {\n\n\tprivate static final long serialVersionUID = -1697673324465500314L;\n\n\t@Override\n    public void action(JobExecutionContext context) {\n\t\tlong startTime = System.currentTimeMillis();\n\t\tlogger.warn(\"TaskExecuteJob start\");\n        try {\n            SchedulerContext schedulerContext = context.getScheduler().getContext();\n            ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY);\n            TaskService taskService = applicationContext.getBean(TaskService.class);\n            taskService.executeNewTask();\n        } catch (Exception e) {\n        \tlogger.error(e.getMessage(), e);\n\t\t}\n\t\tlogger.warn(\"TaskExecuteJob end, cost time is {} ms\", (System.currentTimeMillis() - startTime));\n    }\n\t\n\t\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/ServerStatusCollector.java",
    "content": "package com.sohu.cache.server;\r\n\r\nimport com.sohu.cache.async.AsyncService;\r\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\r\nimport com.sohu.cache.async.KeyCallable;\r\nimport com.sohu.cache.server.data.OSInfo;\r\nimport com.sohu.cache.server.data.Server;\r\nimport com.sohu.cache.server.nmon.NMONService;\r\nimport com.sohu.cache.ssh.SSHTemplate;\r\nimport com.sohu.cache.ssh.SSHTemplate.DefaultLineProcessor;\r\nimport com.sohu.cache.ssh.SSHTemplate.Result;\r\nimport com.sohu.cache.ssh.SSHTemplate.SSHCallback;\r\nimport com.sohu.cache.ssh.SSHTemplate.SSHSession;\r\nimport com.sohu.cache.web.service.ServerDataService;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport javax.annotation.PostConstruct;\r\n\r\n/**\r\n * 服务器状态监控服务\r\n */\r\n@Component\r\npublic class ServerStatusCollector {\r\n    private static final Logger logger = LoggerFactory.getLogger(ServerStatusCollector.class);\r\n\r\n    //获取监控结果\r\n    public static final String COLLECT_SERVER_STATUS =\r\n            \"[ -e \\\"\" + NMONService.SOCK_LOG + \"\\\" ] && /bin/cat \" + NMONService.SOCK_LOG + \" >> \" + NMONService.NMON_LOG\r\n                    + \";[ -e \\\"\" + NMONService.ULIMIT_LOG + \"\\\" ] && /bin/cat \" + NMONService.ULIMIT_LOG + \" >> \" + NMONService.NMON_LOG\r\n                    + \";/bin/mv \" + NMONService.NMON_LOG + \" \" + NMONService.NMON_OLD_LOG\r\n                    + \";[ $? -eq 0 ] && /bin/cat \" + NMONService.NMON_OLD_LOG;\r\n\r\n    //nmon服务\r\n    @Autowired\r\n    private NMONService nmonService;\r\n    //ssh 模板类\r\n    @Autowired\r\n    private SSHTemplate sshTemplate;\r\n    //持久化\r\n    @Autowired\r\n    private ServerDataService serverDataService;\r\n    @Autowired\r\n    private AsyncService asyncService;\r\n\r\n    @PostConstruct\r\n    public void init() {\r\n        asyncService.assemblePool(AsyncThreadPoolFactory.MACHINE_POOL,\r\n                AsyncThreadPoolFactory.MACHINE_THREAD_POOL);\r\n    }\r\n\r\n    //异步执行任务\r\n    public void asyncFetchServerStatus(final String ip) {\r\n        String key = \"collect-server-\" + ip;\r\n        asyncService.submitFuture(AsyncThreadPoolFactory.MACHINE_POOL, new KeyCallable<Boolean>(key) {\r\n            public Boolean execute() {\r\n                try {\r\n                    fetchServerStatus(ip);\r\n                    return true;\r\n                } catch (Exception e) {\r\n                    logger.error(e.getMessage(), e);\r\n                    return false;\r\n                }\r\n            }\r\n        });\r\n    }\r\n\r\n    /**\r\n     * 抓取服务器状态\r\n     *\r\n     * @param ip\r\n     */\r\n    public void fetchServerStatus(final String ip) {\r\n        try {\r\n            sshTemplate.execute(ip, new SSHCallback() {\r\n                public Result call(SSHSession session) {\r\n                    //尝试收集服务器运行状况\r\n                    collectServerStatus(ip, session);\r\n                    //启动nmon收集服务器运行状况\r\n                    OSInfo info = nmonService.start(ip, session);\r\n                    saveServerStatus(ip, info);\r\n                    return null;\r\n                }\r\n            });\r\n        } catch (Exception e) {\r\n            logger.error(\"fetchServerStatus \" + ip + \" err\", e);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 收集系统状况\r\n     *\r\n     * @param ip\r\n     * @param session\r\n     */\r\n    private void collectServerStatus(String ip, SSHSession session) {\r\n        final Server server = new Server();\r\n        server.setIp(ip);\r\n        Result result = session.executeCommand(COLLECT_SERVER_STATUS, new DefaultLineProcessor() {\r\n            public void process(String line, int lineNum) throws Exception {\r\n                server.parse(line, null);\r\n            }\r\n        });\r\n        if (!result.isSuccess()) {\r\n            logger.error(\"collect \" + ip + \" err:\" + result.getResult(), result.getExcetion());\r\n        }\r\n        //保存服务器静态信息\r\n        serverDataService.saveAndUpdateServerInfo(server);\r\n        //保存服务器状况信息\r\n        serverDataService.saveServerStat(server);\r\n    }\r\n\r\n    /**\r\n     * 保存服务器dist信息\r\n     *\r\n     * @param ip\r\n     * @param osInfo\r\n     */\r\n    private void saveServerStatus(String ip, OSInfo osInfo) {\r\n        if (osInfo == null) {\r\n            return;\r\n        }\r\n        serverDataService.saveServerInfo(ip, osInfo.getIssue());\r\n    }\r\n\r\n    public void setNmonService(NMONService nmonService) {\r\n        this.nmonService = nmonService;\r\n    }\r\n\r\n    public void setSshTemplate(SSHTemplate sshTemplate) {\r\n        this.sshTemplate = sshTemplate;\r\n    }\r\n\r\n    public void setServerDataService(ServerDataService serverDataService) {\r\n        this.serverDataService = serverDataService;\r\n    }\r\n\r\n    public void setAsyncService(AsyncService asyncService) {\r\n        this.asyncService = asyncService;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/CPU.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n/**\r\n *\tcpu状况\r\n */\r\npublic class CPU implements LineParser{\r\n\tpublic static final String FLAG = \"CPU\";\r\n\tpublic static final String CPU_ALL = \"CPU_ALL\";\r\n\tprivate Usage allUsage;\r\n\t//包含各个虚拟cpu的情况\r\n\tprivate List<Usage> cpuList = new ArrayList<Usage>();\r\n\t\r\n\t/**\r\n\t * line format:\r\n\t * CPU001,CPU 1 bx-50-13,User%,Sys%,Wait%,Idle%\r\n\t * CPU002,CPU 2 bx-50-13,User%,Sys%,Wait%,Idle%\r\n\t * CPU_ALL,CPU Total bx-50-13,User%,Sys%,Wait%,Idle%,Busy,CPUs\r\n\t * CPU001,T0001,1.8,0.9,4.5,92.9\r\n\t * CPU002,T0001,3.6,1.8,0.0,94.6\r\n\t * CPU_ALL,T0001,2.1,1.3,0.6,95.9,,16\r\n\t */\r\n\tpublic void parse(String line, String timeKey) throws Exception{\r\n\t\tif(line.startsWith(FLAG)) {\r\n\t\t\tString[] items = line.split(\",\", 6);\r\n\t\t\tif(items.length != 6) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif(!items[1].equals(timeKey)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tUsage usage = new Usage();\r\n\t\t\tusage.setUser(NumberUtils.toFloat(items[2]));\r\n\t\t\tusage.setSys(NumberUtils.toFloat(items[3]));\r\n\t\t\tusage.setWait(NumberUtils.toFloat(items[4]));\r\n\t\t\tif(CPU_ALL.equals(items[0])) {\r\n\t\t\t\tallUsage = usage;\r\n\t\t\t} else {\r\n\t\t\t\tusage.setName(items[0]);\r\n\t\t\t\tcpuList.add(usage);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tpublic List<Usage> getCpuList() {\r\n\t\treturn cpuList;\r\n\t}\r\n\t\r\n\tpublic Usage getAllUsage() {\r\n\t\treturn allUsage;\r\n\t}\r\n\t\r\n\tpublic float getUser() {\r\n\t\treturn allUsage == null ? 0 : allUsage.getUser();\r\n\t}\r\n\t\r\n\tpublic float getSys() {\r\n\t\treturn allUsage == null ? 0 : allUsage.getSys();\r\n\t}\r\n\t\r\n\tpublic float getWait() {\r\n\t\treturn allUsage == null ? 0 : allUsage.getWait();\r\n\t}\r\n\t\r\n\tpublic String getExt(){\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tfor(Usage usage : cpuList) {\r\n\t\t\tsb.append(usage.getName());\r\n\t\t\tsb.append(\",\");\r\n\t\t\tsb.append(usage.getUser());\r\n\t\t\tsb.append(\",\");\r\n\t\t\tsb.append(usage.getSys());\r\n\t\t\tsb.append(\",\");\r\n\t\t\tsb.append(usage.getWait());\r\n\t\t\tsb.append(\";\");\r\n\t\t}\r\n\t\treturn sb.toString();\r\n\t}\r\n\tpublic String toString() {\r\n\t\treturn \"CPU [cpuList=\" + cpuList + \"]\";\r\n\t}\r\n\r\n\t/**\r\n\t * cpu使用率\r\n\t */\r\n\tstatic class Usage{\r\n\t\t//代表那个cpu\r\n\t\tprivate String name;\r\n\t\t//用户空间使用率\r\n\t\tprivate float user;\r\n\t\t//内核空间使用率\r\n\t\tprivate float sys;\r\n\t\t//wio\r\n\t\tprivate float wait;\r\n\t\tpublic float getUser() {\r\n\t\t\treturn user;\r\n\t\t}\r\n\t\tpublic void setUser(float user) {\r\n\t\t\tthis.user = user;\r\n\t\t}\r\n\t\tpublic float getSys() {\r\n\t\t\treturn sys;\r\n\t\t}\r\n\t\tpublic void setSys(float sys) {\r\n\t\t\tthis.sys = sys;\r\n\t\t}\r\n\t\tpublic float getWait() {\r\n\t\t\treturn wait;\r\n\t\t}\r\n\t\tpublic void setWait(float wait) {\r\n\t\t\tthis.wait = wait;\r\n\t\t}\r\n\t\tpublic String getName() {\r\n\t\t\treturn name;\r\n\t\t}\r\n\t\tpublic void setName(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\t@Override\r\n\t\tpublic String toString() {\r\n\t\t\treturn \"Usage [name=\" + name + \", user=\" + user + \", sys=\" + sys\r\n\t\t\t\t\t+ \", wait=\" + wait + \"]\";\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/Connection.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\n/**\r\n * tcp连接\r\n */\r\npublic class Connection implements LineParser{\r\n\tpublic static final String FLAG = \"TCP\";\r\n\t\r\n\tprivate int established;\r\n\tprivate int timeWait;\r\n\tprivate int orphan;\r\n\t\r\n\t/**\r\n\t * line format:\r\n\t * TCP: inuse 454 orphan 0 tw 159620 alloc 454 mem 79\r\n\t */\r\n\tpublic void parse(String line, String timeKey) throws Exception{\r\n\t\tif(line.startsWith(FLAG)) {\r\n\t\t\tString[] items = line.split(\"\\\\s+\");\r\n\t\t\tfor(int i = 0; i < items.length; ++i) {\r\n\t\t\t\tif(items[i].equals(\"inuse\")) {\r\n\t\t\t\t\testablished = NumberUtils.toInt(items[i+1]);\r\n\t\t\t\t} else if(items[i].equals(\"orphan\")) {\r\n\t\t\t\t\torphan = NumberUtils.toInt(items[i+1]);\r\n\t\t\t\t} else if(items[i].equals(\"tw\")) {\r\n\t\t\t\t\ttimeWait = NumberUtils.toInt(items[i+1]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tpublic int getEstablished() {\r\n\t\treturn established;\r\n\t}\r\n\r\n\tpublic int getTimeWait() {\r\n\t\treturn timeWait;\r\n\t}\r\n\r\n\tpublic int getOrphan() {\r\n\t\treturn orphan;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/Disk.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.regex.Pattern;\r\n/**\r\n * io读写情况\r\n */\r\npublic class Disk implements LineParser{\r\n\tpublic static final String FLAG = \"DISK\";\r\n\tpublic static final Pattern PATTERN = Pattern.compile(\"^BBBP,[0-9]+,/bin/df-m,\");\r\n\tpublic static final Pattern SUBPARTITION_PATTERN = Pattern.compile(\"[0-9]+$\");\r\n\t//包括总的状况及各个分区的状况\r\n\tprivate Map<DiskUsageType, List<Usage>> diskMap = new HashMap<Disk.DiskUsageType, List<Usage>>();\r\n\t\r\n\t/**\r\n\t * line format:\r\n\t * DISKBUSY,Disk %Busy iZ256oe4w5bZ,xvda,xvda1,xvdb,xvdb1\r\n\t * DISKREAD,Disk Read KB/s iZ256oe4w5bZ,xvda,xvda1,xvdb,xvdb1\r\n\t * DISKWRITE,Disk Write KB/s iZ256oe4w5bZ,xvda,xvda1,xvdb,xvdb1\r\n\t * DISKXFER,Disk transfers per second iZ256oe4w5bZ,xvda,xvda1,xvdb,xvdb1\r\n\t * DISKBSIZE,Disk Block Size iZ256oe4w5bZ,xvda,xvda1,xvdb,xvdb1\r\n\t * DISKBUSY,T0001,0.0,0.0,0.0,0.0\r\n\t * DISKREAD,T0001,0.0,0.0,0.0,0.0\r\n\t * DISKWRITE,T0001,0.0,0.0,0.0,0.0\r\n\t * DISKXFER,T0001,0.0,0.0,0.0,0.0\r\n\t * DISKBSIZE,T0001,0.0,0.0,0.0,0.0\r\n\t * BBBP,173,/bin/df-m,\"ddev/xvda1         20158 16018      3117  84% /\"\r\n\t */\r\n\tpublic void parse(String line, String timeKey) throws Exception{\r\n\t\tif(line.startsWith(FLAG)) {\r\n\t\t\tString[] items = line.split(\",\");\r\n\t\t\tif(!items[1].equals(timeKey)) {\r\n\t\t\t\tDiskUsageType type = DiskUsageType.getType(items[0]);\r\n\t\t\t\tif(type == null) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tList<Usage> list = diskMap.get(type);\r\n\t\t\t\tif(list == null) {\r\n\t\t\t\t\tlist = new ArrayList<Usage>();\r\n\t\t\t\t\tdiskMap.put(type, list);\r\n\t\t\t\t}\r\n\t\t\t\tfor(int i = 2; i < items.length; ++i) {\r\n\t\t\t\t\tUsage usage = new Usage();\r\n\t\t\t\t\tusage.setDiskUsageTyp(type);\r\n\t\t\t\t\tusage.setName(items[i]);\r\n\t\t\t\t\tlist.add(usage);\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tDiskUsageType type = DiskUsageType.getType(items[0]);\r\n\t\t\t\tif(type == null) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tList<Usage> list = diskMap.get(type);\r\n\t\t\t\tif(list == null) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tfor(int i = 2; i < items.length; ++i) {\r\n\t\t\t\t\tfloat value = NumberUtils.toFloat(items[i]);\r\n\t\t\t\t\tif(value > 0) {\r\n\t\t\t\t\t\tlist.get(i-2).setValue(value);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else if(PATTERN.matcher(line).find()) {\r\n\t\t\tString[] tmp = line.split(\",\\\"\");\r\n\t\t\tif(tmp.length > 0) {\r\n\t\t\t\tString[] item = tmp[tmp.length - 1].split(\"\\\\s+\");\r\n\t\t\t\tif(item.length != 6 || !item[4].contains(\"%\")) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tList<Usage> list = diskMap.get(DiskUsageType.busy);\r\n\t\t\t\tString[] tp = item[0].split(\"/\");\r\n\t\t\t\tString mount = tp[tp.length - 1];\r\n\t\t\t\tfor(Usage usage : list) {\r\n\t\t\t\t\tif(usage.getName().equals(mount)) {\r\n\t\t\t\t\t\tUsage spaceUsage = new Usage();\r\n\t\t\t\t\t\tspaceUsage.setDiskUsageTyp(DiskUsageType.space);\r\n\t\t\t\t\t\tspaceUsage.setName(usage.getName());\r\n\t\t\t\t\t\tspaceUsage.setValue(NumberUtils.toFloat(item[4].split(\"%\")[0]));\r\n\t\t\t\t\t\tdiskMap.computeIfAbsent(DiskUsageType.space, k -> new ArrayList<>()).add(spaceUsage);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic Map<DiskUsageType, List<Usage>> getDiskMap() {\r\n\t\treturn diskMap;\r\n\t}\r\n\t\r\n\tpublic float getRead() {\r\n\t\tList<Usage> usageList = diskMap.get(DiskUsageType.read);\r\n\t\treturn getUsage(usageList);\r\n\t}\r\n\t\r\n\tpublic float getWrite() {\r\n\t\tList<Usage> usageList = diskMap.get(DiskUsageType.write);\r\n\t\treturn getUsage(usageList);\r\n\t}\r\n\t\r\n\tpublic float getIops() {\r\n\t\tList<Usage> usageList = diskMap.get(DiskUsageType.transfer);\r\n\t\treturn getUsage(usageList);\r\n\t}\r\n\t\r\n\tpublic float getBusy() {\r\n\t\tList<Usage> usageList = diskMap.get(DiskUsageType.busy);\r\n\t\treturn getUsage(usageList);\r\n\t}\r\n\t\r\n\tpublic String getExt() {\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tfor(Map.Entry<DiskUsageType, List<Usage>> entry: diskMap.entrySet()) {\r\n\t\t\tDiskUsageType type = entry.getKey();\r\n\t\t\tif(DiskUsageType.space == type) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tsb.append(type.getValue());\r\n\t\t\tsb.append(\"=\");\r\n\t\t\tList<Usage> usageList = entry.getValue();\r\n\t\t\tfor(Usage use : usageList) {\r\n                sb.append(use.getName());\r\n                sb.append(\":\");\r\n                sb.append(use.getValue());\r\n                sb.append(\",\");\r\n            }\r\n\t\t\tsb.append(\";\");\r\n\t\t}\r\n\t\treturn sb.toString();\r\n\t}\r\n\t\r\n\tpublic String getSpace() {\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tList<Usage> usageList = diskMap.get(DiskUsageType.space);\r\n\t\tif(usageList == null) {\r\n\t\t\treturn sb.toString();\r\n\t\t}\r\n\t\tfor(Usage use : usageList) {\r\n\t\t\tsb.append(use.getName());\r\n\t\t\tsb.append(\":\");\r\n\t\t\tsb.append(use.getValue());\r\n\t\t\tsb.append(\",\");\r\n\t\t}\r\n\t\treturn sb.toString();\r\n\t}\r\n\t\r\n\tprivate float getUsage(List<Usage> usageList) {\r\n\t\tfloat usage = 0;\r\n\t\tif(usageList != null) {\r\n\t\t\tfor(Usage u : usageList) {\r\n\t\t\t\tif(!SUBPARTITION_PATTERN.matcher(u.getName()).find()) {\r\n\t\t\t\t\tusage += u.getValue();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn usage;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"Disk [diskMap=\" + diskMap + \"]\";\r\n\t}\r\n\t\r\n\t/**\r\n\t * 使用率\r\n\t */\r\n\tstatic class Usage{\r\n\t\t//分区名字\r\n\t\tprivate String name;\r\n\t\t//使用率类型\r\n\t\tprivate DiskUsageType diskUsageType;\r\n\t\t//使用率\r\n\t\tprivate float value;\r\n\t\tpublic DiskUsageType getDiskUsageType() {\r\n\t\t\treturn diskUsageType;\r\n\t\t}\r\n\t\tpublic void setDiskUsageTyp(DiskUsageType diskUsageType) {\r\n\t\t\tthis.diskUsageType = diskUsageType;\r\n\t\t}\r\n\t\tpublic float getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\t\tpublic void setValue(float value) {\r\n\t\t\tthis.value = value;\r\n\t\t}\r\n\t\tpublic String getName() {\r\n\t\t\treturn name;\r\n\t\t}\r\n\t\tpublic void setName(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\t@Override\r\n\t\tpublic String toString() {\r\n\t\t\treturn \"Usage [name=\" + name + \", diskUsageType=\" + diskUsageType\r\n\t\t\t\t\t+ \", value=\" + value + \"]\";\r\n\t\t}\r\n\t}\r\n\t/**\r\n\t * 使用率类型\r\n\t */\r\n\tenum DiskUsageType{\r\n\t\t//繁忙程度\r\n\t\tbusy(\"DISKBUSY\"),\r\n\t\t//读\r\n\t\tread(\"DISKREAD\"),\r\n\t\t//写\r\n\t\twrite(\"DISKWRITE\"),\r\n\t\t//io次数\r\n\t\ttransfer(\"DISKXFER\"),\r\n\t\t//空间使用率\r\n\t\tspace(\"df\"),\r\n\t\t;\r\n\t\tprivate String value;\r\n\t\t\r\n\t\tprivate DiskUsageType(String value) {\r\n\t\t\tthis.value = value;\r\n\t\t}\r\n\t\tpublic String getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\t\tpublic static DiskUsageType getType(String type) {\r\n\t\t\tfor(DiskUsageType dut : DiskUsageType.values()) {\r\n\t\t\t\tif(dut.getValue().equals(type)) {\r\n\t\t\t\t\treturn dut;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/LineParser.java",
    "content": "package com.sohu.cache.server.data;\r\n/**\r\n * 行解析器\r\n */\r\npublic interface LineParser {\r\n\t/**\r\n\t * 解析nmon行\r\n\t * @param line     nmon行内容\r\n\t * @param timeKey  时间戳\r\n\t * @throws Exception\r\n\t */\r\n\tvoid parse(String line, String timeKey) throws Exception;\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/Load.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\n/**\r\n * 系统负载\r\n */\r\npublic class Load implements LineParser{\r\n\tpublic static final Pattern PATTERN = Pattern.compile(\r\n\t\t\t\"^BBBP,[0-9]+,uptime,.*(\\\\d+\\\\.\\\\d+), (\\\\d+\\\\.\\\\d+), (\\\\d+\\\\.\\\\d+)\");\r\n\t//1分钟负载\r\n\tprivate float load1;\r\n\t//5分钟负载\r\n\tprivate float load5;\r\n\t//15分钟负载\r\n\tprivate float load15;\r\n\t\r\n\t/**\r\n\t * line format:\r\n\t * BBBP,585,uptime,\" 09:35:00 up 567 days, 15:07,  0 users,  load average: 0.60, 0.63, 0.67\"\r\n\t */\r\n\tpublic void parse(String line, String timeKey) throws Exception{\r\n\t\tMatcher matcher = PATTERN.matcher(line);\r\n\t\tif(matcher.find()) {\r\n\t\t\tload1 = NumberUtils.toFloat(matcher.group(1));\r\n\t\t\tload5 = NumberUtils.toFloat(matcher.group(2));\r\n\t\t\tload15 = NumberUtils.toFloat(matcher.group(3));\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic float getLoad1() {\r\n\t\treturn load1;\r\n\t}\r\n\tpublic float getLoad5() {\r\n\t\treturn load5;\r\n\t}\r\n\tpublic float getLoad15() {\r\n\t\treturn load15;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"Load [load1=\" + load1 + \", load5=\" + load5 + \", load15=\"\r\n\t\t\t\t+ load15 + \"]\";\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/Memory.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n/**\r\n * 内存使用情况\r\n */\r\npublic class Memory implements LineParser{\r\n\tpublic static final String FLAG = \"MEM\";\r\n\t//总内存，单位M\r\n\tprivate float total;\r\n\t//总空闲内存，单位M\r\n\tprivate float totalFree;\r\n\t//buffer，单位M\r\n\tprivate float buffer;\r\n\t//cache，单位M\r\n\tprivate float cache;\r\n\t//swap，单位M\r\n\tprivate float swap;\r\n\t//swap空闲内存，单位M\r\n\tprivate float swapFree;\r\n\t\r\n\t/**\r\n\t * line format:\r\n\t * MEM,Memory MB bx-50-13,memtotal,hightotal,lowtotal,swaptotal,memfree,highfree,lowfree,swapfree,memshared,cached,active,bigfree,buffers,swapcached,inactive\r\n\t * MEM,T0001,48288.7,0.0,48288.7,8189.4,132.6,0.0,132.6,8189.1,-0.0,24210.6,30819.7,-1.0,153.9,0.0,16451.1\r\n\t */\r\n\tpublic void parse(String line, String timeKey) throws Exception{\r\n\t\tif(line.startsWith(FLAG)) {\r\n\t\t\tString[] items = line.split(\",\");\r\n\t\t\tif(!items[1].equals(timeKey)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\ttotal = NumberUtils.toFloat(items[2]);\r\n\t\t\tswap = NumberUtils.toFloat(items[5]);\r\n\t\t\ttotalFree = NumberUtils.toFloat(items[6]);\r\n\t\t\tswapFree = NumberUtils.toFloat(items[9]);\r\n\t\t\tcache = NumberUtils.toFloat(items[11]);\r\n\t\t\tbuffer = NumberUtils.toFloat(items[14]);\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic float getTotal() {\r\n\t\treturn total;\r\n\t}\r\n\r\n\tpublic float getTotalFree() {\r\n\t\treturn totalFree;\r\n\t}\r\n\r\n\tpublic float getBuffer() {\r\n\t\treturn buffer;\r\n\t}\r\n\r\n\tpublic float getCache() {\r\n\t\treturn cache;\r\n\t}\r\n\r\n\tpublic float getSwap() {\r\n\t\treturn swap;\r\n\t}\r\n\r\n\tpublic float getSwapFree() {\r\n\t\treturn swapFree;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"Memory [total=\" + total + \", totalFree=\" + totalFree\r\n\t\t\t\t+ \", buffer=\" + buffer + \", cache=\" + cache + \", swap=\" + swap\r\n\t\t\t\t+ \", swapFree=\" + swapFree + \"]\";\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/Net.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n\r\nimport java.math.BigDecimal;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\n\r\n/**\r\n * 网络流量\r\n */\r\npublic class Net implements LineParser{\r\n\tpublic static final String FLAG = \"NET,\";\r\n\t\r\n\tprivate float nin;\r\n\tprivate float nout;\r\n\tprivate StringBuilder ninDetail = new StringBuilder();\r\n\tprivate StringBuilder noutDetail = new StringBuilder();\r\n\t\r\n\tprivate List<NetworkInterfaceCard> ncList = new ArrayList<NetworkInterfaceCard>();\r\n\t\r\n\t/**\r\n\t * line format:\r\n\t * NET,Network I/O bx-50-13,lo-read-KB/s,eth0-read-KB/s,eth1-read-KB/s,eth2-read-KB/s,eth3-read-KB/s,lo-write-KB/s,eth0-write-KB/s,eth1-write-KB/s,eth2-write-KB/s,eth3-write-KB/s,\r\n\t * NET,T0001,190.3,3317.8,0.0,0.0,0.0,190.3,3377.7,0.0,0.0,0.0,\r\n\t */\r\n\tpublic void parse(String line, String timeKey) throws Exception{\r\n\t\tif(line.startsWith(FLAG)) {\r\n\t\t\tString[] items = line.split(\",\");\r\n\t\t\tif(items[1].startsWith(\"Network\")) {\r\n\t\t\t\tfor(int i = 0; i < items.length; ++i) {\r\n\t\t\t\t\tif(items[i].startsWith(\"eth\")) {\r\n\t\t\t\t\t\tNetworkInterfaceCard nic = new NetworkInterfaceCard();\r\n\t\t\t\t\t\tnic.setName(items[i]);\r\n\t\t\t\t\t\tnic.setIdx(i);\r\n\t\t\t\t\t\tncList.add(nic);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tfor(NetworkInterfaceCard nic : ncList) {\r\n\t\t\t\t\tnic.setValue(NumberUtils.toFloat(items[nic.getIdx()]));\r\n\t\t\t\t}\r\n\t\t\t\tcaculate();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate void caculate() {\r\n\t\tfloat totalIn = 0;\r\n\t\tfloat totalOut = 0;\r\n\t\tfor(NetworkInterfaceCard nic : ncList) {\r\n\t\t\tString[] array = nic.getName().split(\"-\");\r\n\t\t\tif(\"read\".equals(array[1])) {\r\n\t\t\t\tninDetail.append(array[0]);\r\n\t\t\t\tninDetail.append(\",\");\r\n\t\t\t\tninDetail.append(nic.getValue());\r\n\t\t\t\tninDetail.append(\";\");\r\n\t\t\t\ttotalIn += nic.getValue();\r\n\t\t\t} else if(\"write\".equals(array[1])) {\r\n\t\t\t\tnoutDetail.append(array[0]);\r\n\t\t\t\tnoutDetail.append(\",\");\r\n\t\t\t\tnoutDetail.append(nic.getValue());\r\n\t\t\t\tnoutDetail.append(\";\");\r\n\t\t\t\ttotalOut += nic.getValue();\r\n\t\t\t}\r\n\t\t}\r\n\t\tnin = BigDecimal.valueOf(totalIn).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();\r\n\t\tnout = BigDecimal.valueOf(totalOut).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();\r\n\t}\r\n\t\r\n\tpublic float getNin() {\r\n\t\treturn nin;\r\n\t}\r\n\r\n\tpublic float getNout() {\r\n\t\treturn nout;\r\n\t}\r\n\tpublic String getNinDetail() {\r\n\t\treturn ninDetail.toString();\r\n\t}\r\n\r\n\tpublic String getNoutDetail() {\r\n\t\treturn noutDetail.toString();\r\n\t}\r\n\r\n\tstatic class NetworkInterfaceCard{\r\n\t\tprivate String name;\r\n\t\tprivate float value;\r\n\t\tprivate int idx;\r\n\t\tpublic String getName() {\r\n\t\t\treturn name;\r\n\t\t}\r\n\t\tpublic void setName(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\tpublic float getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\t\tpublic void setValue(float value) {\r\n\t\t\tthis.value = value;\r\n\t\t}\r\n\t\tpublic int getIdx() {\r\n\t\t\treturn idx;\r\n\t\t}\r\n\t\tpublic void setIdx(int idx) {\r\n\t\t\tthis.idx = idx;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/OS.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport com.sohu.cache.server.data.OSInfo.DistributionType;\r\nimport com.sohu.cache.server.data.OSInfo.DistributionVersion;\r\nimport com.sohu.cache.server.data.OSInfo.OSType;\r\nimport com.sohu.cache.server.data.OSInfo.ProcessorArchitecture;\r\n/**\r\n * 从OSInfo解析后的OS\r\n */\r\npublic class OS {\r\n\t//操作系统类型\r\n\tprivate OSType osType;\r\n\t//发行版本\r\n\tprivate DistributionType distributionType;\r\n\t//发行版本号\r\n\tprivate DistributionVersion distributionVersion;\r\n\t//处理器架构\r\n\tprivate ProcessorArchitecture processorArchitecture;\r\n\t\r\n\tpublic OS(OSType osType, DistributionType distributionType,\r\n\t\t\tDistributionVersion distributionVersion,\r\n\t\t\tProcessorArchitecture processorArchitecture) {\r\n\t\tthis.osType = osType;\r\n\t\tthis.distributionType = distributionType;\r\n\t\tthis.distributionVersion = distributionVersion;\r\n\t\tthis.processorArchitecture = processorArchitecture;\r\n\t}\r\n\r\n\tpublic OSType getOsType() {\r\n\t\treturn osType;\r\n\t}\r\n\r\n\tpublic void setOsType(OSType osType) {\r\n\t\tthis.osType = osType;\r\n\t}\r\n\r\n\tpublic DistributionType getDistributionType() {\r\n\t\treturn distributionType;\r\n\t}\r\n\r\n\tpublic void setDistributionType(DistributionType distributionType) {\r\n\t\tthis.distributionType = distributionType;\r\n\t}\r\n\t\r\n\tpublic DistributionVersion getDistributionVersion() {\r\n\t\treturn distributionVersion;\r\n\t}\r\n\r\n\tpublic void setDistributionVersion(DistributionVersion distributionVersion) {\r\n\t\tthis.distributionVersion = distributionVersion;\r\n\t}\r\n\r\n\tpublic ProcessorArchitecture getProcessorArchitecture() {\r\n\t\treturn processorArchitecture;\r\n\t}\r\n\r\n\tpublic void setProcessorArchitecture(ProcessorArchitecture processorArchitecture) {\r\n\t\tthis.processorArchitecture = processorArchitecture;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"OS [osType=\" + osType + \", dist=\"\r\n\t\t\t\t+ distributionType + \", version=\"\r\n\t\t\t\t+ distributionVersion + \", bit=\"\r\n\t\t\t\t+ processorArchitecture + \"]\";\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/OSInfo.java",
    "content": "package com.sohu.cache.server.data;\r\n/**\r\n * 代表操作系统的原始信息\r\n * 及一些类型定义\r\n */\r\npublic class OSInfo {\r\n\t//操作系统信息  uname -a\r\n\tprivate String uname;\r\n\t//发布版本 - /etc/issue\r\n\tprivate String issue;\r\n\tpublic String getUname() {\r\n\t\treturn uname;\r\n\t}\r\n\tpublic void setUname(String uname) {\r\n\t\tthis.uname = uname;\r\n\t}\r\n\tpublic String getIssue() {\r\n\t\treturn issue;\r\n\t}\r\n\tpublic void setIssue(String issue) {\r\n\t\tthis.issue = issue;\r\n\t}\r\n\tpublic String toString() {\r\n\t\treturn \"OSInfo [uname=\" + uname + \", issue=\" + issue + \"]\";\r\n\t}\r\n\t/**\r\n\t * 操作系统类型\r\n\t */\r\n\tpublic enum OSType{\r\n\t\tLINUX(\"linux\"),\r\n\t\t;\r\n\t\tprivate String value;\r\n\t\tprivate OSType(String value) {\r\n\t\t\tthis.value = value;\r\n\t\t}\r\n\t\tpublic String getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\t\tpublic static OSType findByValue(String value) {\r\n\t\t\tfor(OSType os : values()) {\r\n\t\t\t\tif(os.getValue().equals(value)) {\r\n\t\t\t\t\treturn os;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * 操作系统的发行版本\r\n\t */\r\n\tpublic enum DistributionType{\r\n\t\t//通用系列\r\n\t\tLINUX(\"linux\", \"@@linux@@\", new DistributionVersion[]{DistributionVersion.DEFAULT}),\r\n\t\tLINUX_OLD(\"linux_old\", \"@@linux_old@@\", new DistributionVersion[]{DistributionVersion.DEFAULT}),\r\n\t\t\r\n\t\t//红帽系列\r\n\t\tREDHAT(\"rhel\", \"red hat\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.REDHAT_4,\r\n\t\t\t\tDistributionVersion.REDHAT_45,\r\n\t\t\t\tDistributionVersion.REDHAT_5,\r\n\t\t\t\tDistributionVersion.REDHAT_52,\r\n\t\t\t\tDistributionVersion.REDHAT_54,\r\n\t\t\t\tDistributionVersion.REDHAT_6,\r\n\t\t\t\tDistributionVersion.REDHAT_65,\r\n\t\t\t\tDistributionVersion.REDHAT_7,\r\n\t\t\t\tDistributionVersion.REDHAT_71,\r\n\t\t\t\tDistributionVersion.REDHAT_72,\r\n\t\t\t\t}),\r\n\t\t\r\n\t\t//centos系列\r\n\t\tCENTOS(\"centos\", \"centos\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.CENTOS_6,\r\n\t\t\t\tDistributionVersion.CENTOS_7,\r\n\t\t\t\t}),\r\n\t\t\r\n\t\t//ubuntu系列\r\n\t\tUBUNTU(\"ubuntu\", \"ubuntu\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.UBUNTU_6,\r\n\t\t\t\tDistributionVersion.UBUNTU_7,\r\n\t\t\t\tDistributionVersion.UBUNTU_8,\r\n\t\t\t\tDistributionVersion.UBUNTU_810,\r\n\t\t\t\tDistributionVersion.UBUNTU_9,\r\n\t\t\t\tDistributionVersion.UBUNTU_910,\r\n\t\t\t\tDistributionVersion.UBUNTU_10,\r\n\t\t\t\tDistributionVersion.UBUNTU_1004,\r\n\t\t\t\tDistributionVersion.UBUNTU_1010,\r\n\t\t\t\tDistributionVersion.UBUNTU_1104,\r\n\t\t\t\tDistributionVersion.UBUNTU_1110,\r\n\t\t\t\tDistributionVersion.UBUNTU_13,\r\n\t\t\t\tDistributionVersion.UBUNTU_14,\r\n\t\t\t\tDistributionVersion.UBUNTU_1404,\r\n\t\t\t\tDistributionVersion.UBUNTU_1410,\r\n\t\t\t\tDistributionVersion.UBUNTU_15,\r\n\t\t\t\tDistributionVersion.UBUNTU_1504,\r\n\t\t\t\tDistributionVersion.UBUNTU_1510,\r\n\t\t}),\r\n\t\t\r\n\t\t//debian系列\r\n\t\tDEBIAN(\"debian\", \"debian\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.DEBIAN_5,\r\n\t\t\t\tDistributionVersion.DEBIAN_50,\r\n\t\t\t\tDistributionVersion.DEBIAN_6,\r\n\t\t\t\tDistributionVersion.DEBIAN_60,\r\n\t\t\t\tDistributionVersion.DEBIAN_7,\r\n\t\t\t\tDistributionVersion.DEBIAN_8,\r\n\t\t}),\r\n\t\t\r\n\t\t//fedora系列\r\n\t\tFEDORA(\"fedora\", \"fedora\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.FEDORA_14,\r\n\t\t\t\tDistributionVersion.FEDORA_15,\r\n\t\t\t\tDistributionVersion.FEDORA_16,\r\n\t\t\t\tDistributionVersion.FEDORA_17,\r\n\t\t\t\tDistributionVersion.FEDORA_18,\r\n\t\t\t\tDistributionVersion.FEDORA_19,\r\n\t\t\t\tDistributionVersion.FEDORA_20,\r\n\t\t\t\tDistributionVersion.FEDORA_21,\r\n\t\t\t\tDistributionVersion.FEDORA_22,\r\n\t\t}),\r\n\t\t\r\n\t\t//mint系列\r\n\t\tMINT(\"mint\", \"mint\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.MINT_7,\r\n\t\t\t\tDistributionVersion.MINT_8,\r\n\t\t\t\tDistributionVersion.MINT_12,\r\n\t\t\t\tDistributionVersion.MINT_14,\r\n\t\t\t\tDistributionVersion.MINT_15,\r\n\t\t\t\tDistributionVersion.MINT_16,\r\n\t\t\t\tDistributionVersion.MINT_17,\r\n\t\t}),\r\n\t\t\r\n\t\t//opensuse系列\r\n\t\tOPENSUSE(\"opensuse\", \"opensuse\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.OPENSUSE_11,\r\n\t\t\t\tDistributionVersion.OPENSUSE_12,\r\n\t\t\t\tDistributionVersion.OPENSUSE_13,\r\n\t\t}),\r\n\t\t\r\n\t\t//sles系列(SuSE Linux Enterprise Server)\r\n\t\tSLES(\"sles\", \"sles\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.SLES_11,\r\n\t\t\t\tDistributionVersion.SLES_12,\r\n\t\t\t\tDistributionVersion.SLES_13,\r\n\t\t}),\r\n\t\t\r\n\t\t//knoppix\r\n\t\tKNOPPIX(\"knoppix\", \"knoppix\", new DistributionVersion[]{\r\n\t\t\t\tDistributionVersion.KNOPPIX_4,\r\n\t\t\t\tDistributionVersion.KNOPPIX_5,\r\n\t\t\t\tDistributionVersion.KNOPPIX_6,\r\n\t\t\t\tDistributionVersion.KNOPPIX_7,\r\n\t\t}),\r\n\t\t\r\n\t\t;\r\n\t\t//nmon文件对应的名字\r\n\t\tprivate String nmonName;\r\n\t\t//发行版本对应的标志\r\n\t\tprivate String distSign;\r\n\t\tprivate DistributionVersion[] versions;\r\n\t\t\r\n\t\tprivate DistributionType(String nmonName, String distSign, \r\n\t\t\t\tDistributionVersion[] versions) {\r\n\t\t\tthis.nmonName = nmonName;\r\n\t\t\tthis.distSign = distSign;\r\n\t\t\tthis.versions = versions;\r\n\t\t}\r\n\t\t\r\n\t\tpublic String getNmonName() {\r\n\t\t\treturn nmonName;\r\n\t\t}\r\n\r\n\t\tpublic String getDistSign() {\r\n\t\t\treturn distSign;\r\n\t\t}\r\n\r\n\t\tpublic DistributionVersion[] getVersions() {\r\n\t\t\treturn versions;\r\n\t\t}\r\n\t\t\r\n\t\tpublic static DistributionType findByContains(String value) {\r\n\t\t\tfor(DistributionType type : values()) {\r\n\t\t\t\tif(value.contains(type.getDistSign())) {\r\n\t\t\t\t\treturn type;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * 操作系统的发行版本号\r\n\t */\r\n\tpublic enum DistributionVersion{\r\n\t\t//通用系列无版本\r\n\t\tDEFAULT(\"\"),\r\n\t\t\r\n\t\t//红帽系列\r\n\t\tREDHAT_4(\"4\"),\r\n\t\tREDHAT_45(\"45\"),\r\n\t\tREDHAT_5(\"5\"),\r\n\t\tREDHAT_52(\"52\"),\r\n\t\tREDHAT_54(\"54\"),\r\n\t\tREDHAT_6(\"6\"),\r\n\t\tREDHAT_65(\"65\"),\r\n\t\tREDHAT_7(\"7\"),\r\n\t\tREDHAT_71(\"71\"),\r\n\t\tREDHAT_72(\"72\"),\r\n\t\t\r\n\t\t//centos系列\r\n\t\tCENTOS_6(\"6\"),\r\n\t\tCENTOS_7(\"7\"),\r\n\t\t\r\n\t\t//ubuntu系列\r\n\t\tUBUNTU_6(\"6\"),\r\n\t\tUBUNTU_7(\"7\"),\r\n\t\tUBUNTU_8(\"8\"),\r\n\t\tUBUNTU_810(\"810\"),\r\n\t\tUBUNTU_9(\"9\"),\r\n\t\tUBUNTU_910(\"910\"),\r\n\t\tUBUNTU_10(\"10\"),\r\n\t\tUBUNTU_1004(\"1004\"),\r\n\t\tUBUNTU_1010(\"1010\"),\r\n\t\tUBUNTU_1104(\"1104\"),\r\n\t\tUBUNTU_1110(\"1110\"),\r\n\t\tUBUNTU_13(\"13\"),\r\n\t\tUBUNTU_14(\"14\"),\r\n\t\tUBUNTU_1404(\"1404\"),\r\n\t\tUBUNTU_1410(\"1410\"),\r\n\t\tUBUNTU_15(\"15\"),\r\n\t\tUBUNTU_1504(\"1504\"),\r\n\t\tUBUNTU_1510(\"1510\"),\r\n\t\t\r\n\t\t//debian系列\r\n\t\tDEBIAN_5(\"5\"),\r\n\t\tDEBIAN_50(\"50\"),\r\n\t\tDEBIAN_6(\"6\"),\r\n\t\tDEBIAN_60(\"60\"),\r\n\t\tDEBIAN_7(\"7\"),\r\n\t\tDEBIAN_8(\"8\"),\r\n\t\t\r\n\t\t//fedora系列\r\n\t\tFEDORA_14(\"14\"),\r\n\t\tFEDORA_15(\"15\"),\r\n\t\tFEDORA_16(\"16\"),\r\n\t\tFEDORA_17(\"17\"),\r\n\t\tFEDORA_18(\"18\"),\r\n\t\tFEDORA_19(\"19\"),\r\n\t\tFEDORA_20(\"20\"),\r\n\t\tFEDORA_21(\"21\"),\r\n\t\tFEDORA_22(\"22\"),\r\n\t\t\r\n\t\t//mint系列\r\n\t\tMINT_7(\"7\"),\r\n\t\tMINT_8(\"8\"),\r\n\t\tMINT_12(\"12\"),\r\n\t\tMINT_14(\"14\"),\r\n\t\tMINT_15(\"15\"),\r\n\t\tMINT_16(\"16\"),\r\n\t\tMINT_17(\"17\"),\r\n\t\t\r\n\t\t//opensuse系列\r\n\t\tOPENSUSE_11(\"11\"),\r\n\t\tOPENSUSE_12(\"12\"),\r\n\t\tOPENSUSE_13(\"13\"),\r\n\t\t\r\n\t\t//sles系列(SuSE Linux Enterprise Server)\r\n\t\tSLES_11(\"11\"),\r\n\t\tSLES_12(\"12\"),\r\n\t\tSLES_13(\"13\"),\r\n\t\t\r\n\t\t//knoppix\r\n\t\tKNOPPIX_4(\"4\"),\r\n\t\tKNOPPIX_5(\"5\"),\r\n\t\tKNOPPIX_6(\"6\"),\r\n\t\tKNOPPIX_7(\"7\"),\r\n\t\t;\r\n\t\t\r\n\t\tprivate String value;\r\n\t\t\r\n\t\tprivate DistributionVersion(String value) {\r\n\t\t\tthis.value = value;\r\n\t\t}\r\n\t\tpublic String getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * 处理器架构\r\n\t */\r\n\tpublic enum ProcessorArchitecture{\r\n\t\tX86_64(\"x86_64\"),\r\n\t\tX86(\"x86\"),\r\n\t\tUNKONW(\"\"),\r\n\t\t;\r\n\t\tprivate String value;\r\n\t\t\r\n\t\tprivate ProcessorArchitecture(String value) {\r\n\t\t\tthis.value = value;\r\n\t\t}\r\n\t\tpublic String getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\t\t\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/data/Server.java",
    "content": "package com.sohu.cache.server.data;\r\n\r\nimport java.text.ParseException;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\nimport java.util.Locale;\r\nimport java.util.regex.Pattern;\r\n\r\nimport org.apache.commons.lang.math.NumberUtils;\r\n/**\r\n *\t服务器基本状态\r\n */\r\npublic class Server implements LineParser{\r\n\tpublic static final String TIME_FLAG = \"ZZZZ\";\r\n\tpublic static final String FLAG = \"AAA\";\r\n\tpublic static final Pattern pattern = Pattern.compile(\"^BBBP,[0-9]+,/proc/cpuinfo,\\\"model name\");\r\n\tpublic static final String OPEN_FILES = \"open files\";\r\n\tpublic static final String MX_PROCESS = \"max user processes\";\r\n\t//标志时间的字段\r\n\tprivate String timeKey;\r\n\t//收集日期 类似18:49:21,31-MAY-2016格式\r\n\tprivate String dateTime;\r\n\tprivate Date collectTime;\r\n\tprivate String ip;\r\n\t//host\r\n\tprivate String host;\r\n\t//逻辑cpu个数\r\n\tprivate int cpus;\r\n\t//nmon版本\r\n\tprivate String nmon;\r\n\t//cpu型号\r\n\tprivate String cpuModel;\r\n\t//内核版本\r\n\tprivate String kernel;\r\n\t//发行版本\r\n\tprivate String dist;\r\n\t//ulimit\r\n\tprivate String ulimit = \"\";\r\n\t\r\n\tprivate CPU cpu;\r\n\tprivate Memory mem;\r\n\tprivate Load load;\r\n\tprivate Disk disk;\r\n\tprivate Net net;\r\n\tprivate Connection connection;\r\n\t\r\n\tpublic Server() {\r\n\t\tcpu = new CPU();\r\n\t\tmem = new Memory();\r\n\t\tload = new Load();\r\n\t\tdisk = new Disk();\r\n\t\tnet = new Net();\r\n\t\tconnection = new Connection();\r\n\t}\r\n\t\r\n\tpublic static void main(String[] args) throws ParseException {\r\n\t\tString s = \"18:49:21,31-MAY-2016\";\r\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\r\n\t\t\t\t\"HH:mm:ss,dd-MMM-yyyy\", Locale.ENGLISH);\r\n\t\tSystem.out.println(sdf.parse(s));\r\n\t}\r\n\r\n\t/**\r\n\t * line format:\r\n\t * ZZZZ,T0001,09:50:01,01-JUL-2016\r\n\t * AAA,host,localhost\r\n\t * AAA,version,14g\r\n\t * AAA,cpus,16\r\n\t * AAA,OS,Linux,2.6.18-348.el5,#1 SMP Wed Nov 28 21:22:00 EST 2012,x86_64\r\n\t * BBBP,374,/proc/cpuinfo,\"stat name      : Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz\"\r\n\t * open files                      (-n) 65535\r\n\t * max user processes              (-u) 65535\r\n\t */\r\n\tpublic void parse(String line, String key) throws Exception {\r\n\t\tif(line.startsWith(TIME_FLAG)) {\r\n\t\t\tString[] items = line.split(\",\", 3);\r\n\t\t\tif(items.length == 3) {\r\n\t\t\t\tthis.timeKey = items[1];\r\n\t\t\t\tthis.dateTime = items[2];\r\n\t\t\t\tSimpleDateFormat sdf = new SimpleDateFormat(\r\n\t\t\t\t\t\t\"HH:mm:ss,dd-MMM-yyyy\", Locale.ENGLISH);\r\n\t\t\t\tthis.collectTime = sdf.parse(dateTime);\r\n\t\t\t}\r\n\t\t} else if(line.startsWith(FLAG)) {\r\n\t\t\tString[] items = line.split(\",\", 3);\r\n\t\t\tif(items.length > 2) {\r\n\t\t\t\tif(\"host\".equals(items[1])) {\r\n\t\t\t\t\thost = items[2];\r\n\t\t\t\t} else if(\"version\".equals(items[1])) {\r\n\t\t\t\t\tnmon = items[2];\r\n\t\t\t\t} else if(\"cpus\".equals(items[1])) {\r\n\t\t\t\t\tcpus = NumberUtils.toInt(items[2]);\r\n\t\t\t\t} else if(\"OS\".equals(items[1])) {\r\n\t\t\t\t\tkernel = items[2];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else if(cpuModel == null && pattern.matcher(line).find()) {\r\n\t\t\tString[] tmp = line.split(\"model name\");\r\n\t\t\tif(tmp.length == 2) {\r\n\t\t\t\tcpuModel = tmp[1].trim();\r\n\t\t\t\tcpuModel = cpuModel.substring(1, cpuModel.length() - 1);\r\n\t\t\t}\r\n\t\t}\r\n\t\tString mxFile = parseULimit(line, OPEN_FILES, \"f\");\r\n\t\tif(mxFile != null) {\r\n\t\t\tulimit += mxFile;\r\n\t\t}\r\n\t\tString mxProcess = parseULimit(line, MX_PROCESS, \"p\");\r\n\t\tif(mxProcess != null) {\r\n\t\t\tulimit += mxProcess;\r\n\t\t}\r\n\t\t\r\n\t\tload.parse(line, timeKey);\r\n\t\tcpu.parse(line, timeKey);\r\n\t\tmem.parse(line, timeKey);\r\n\t\tnet.parse(line, timeKey);\r\n\t\tdisk.parse(line, timeKey);\r\n\t\tconnection.parse(line, timeKey);\r\n\t}\r\n\t\r\n\tprivate String parseULimit(String line, String prefix, String flag) {\r\n\t\tString result = null;\r\n\t\tif(line.startsWith(prefix)) {\r\n\t\t\tString[] tmp = line.split(\"\\\\s+\");\r\n\t\t\tif(tmp.length > 0) {\r\n\t\t\t\tint v = NumberUtils.toInt(tmp[tmp.length - 1]);\r\n\t\t\t\tif(v > 0) {\r\n\t\t\t\t\tresult = flag + \",\" + v +\";\";\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\tpublic String getDateTime() {\r\n\t\treturn dateTime;\r\n\t}\r\n\tpublic Date getCollectTime() {\r\n\t\treturn (Date) collectTime.clone();\r\n\t}\r\n\r\n\tpublic Connection getConnection() {\r\n\t\treturn connection;\r\n\t}\r\n\tpublic String getIp() {\r\n\t\treturn ip;\r\n\t}\r\n\tpublic void setIp(String ip) {\r\n\t\tthis.ip = ip;\r\n\t}\r\n\r\n\tpublic String getHost() {\r\n\t\treturn host;\r\n\t}\r\n\r\n\tpublic int getCpus() {\r\n\t\treturn cpus;\r\n\t}\r\n\r\n\tpublic String getUlimit() {\r\n\t\treturn ulimit;\r\n\t}\r\n\r\n\tpublic String getNmon() {\r\n\t\treturn nmon;\r\n\t}\r\n\r\n\tpublic String getCpuModel() {\r\n\t\treturn cpuModel;\r\n\t}\r\n\r\n\tpublic String getKernel() {\r\n\t\treturn kernel;\r\n\t}\r\n\r\n\tpublic String getDist() {\r\n\t\treturn dist;\r\n\t}\r\n\r\n\tpublic CPU getCpu() {\r\n\t\treturn cpu;\r\n\t}\r\n\r\n\tpublic Memory getMem() {\r\n\t\treturn mem;\r\n\t}\r\n\tpublic String getTime() {\r\n\t\treturn new SimpleDateFormat(\"HHmm\").format(collectTime);\r\n\t}\r\n\tpublic Load getLoad() {\r\n\t\treturn load;\r\n\t}\r\n\r\n\tpublic Disk getDisk() {\r\n\t\treturn disk;\r\n\t}\r\n\r\n\tpublic Net getNet() {\r\n\t\treturn net;\r\n\t}\r\n\tpublic void setHost(String host) {\r\n\t\tthis.host = host;\r\n\t}\r\n\r\n\tpublic void setCpus(int cpus) {\r\n\t\tthis.cpus = cpus;\r\n\t}\r\n\r\n\tpublic void setNmon(String nmon) {\r\n\t\tthis.nmon = nmon;\r\n\t}\r\n\r\n\tpublic void setCpuModel(String cpuModel) {\r\n\t\tthis.cpuModel = cpuModel;\r\n\t}\r\n\tpublic void setDist(String dist) {\r\n\t\tthis.dist = dist;\r\n\t}\r\n\r\n\tpublic void setKernel(String kernel) {\r\n\t\tthis.kernel = kernel;\r\n\t}\r\n\r\n\tpublic void setUlimit(String ulimit) {\r\n\t\tthis.ulimit = ulimit;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"Server [timeKey=\" + timeKey + \", dateTime=\" + dateTime\r\n\t\t\t\t+ \", collectTime=\" + collectTime + \", ip=\" + ip + \", host=\"\r\n\t\t\t\t+ host + \", cpus=\" + cpus + \", nmon=\" + nmon + \", cpuModel=\"\r\n\t\t\t\t+ cpuModel + \", kernel=\" + kernel + \", dist=\" + dist\r\n\t\t\t\t+ \", ulimit=\" + ulimit + \", cpu=\" + cpu + \", mem=\" + mem\r\n\t\t\t\t+ \", load=\" + load + \", disk=\" + disk + \", net=\" + net\r\n\t\t\t\t+ \", connection=\" + connection + \"]\";\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/server/nmon/NMONService.java",
    "content": "package com.sohu.cache.server.nmon;\r\n\r\nimport com.sohu.cache.server.data.OS;\r\nimport com.sohu.cache.server.data.OSInfo;\r\nimport com.sohu.cache.ssh.SSHTemplate.DefaultLineProcessor;\r\nimport com.sohu.cache.ssh.SSHTemplate.Result;\r\nimport com.sohu.cache.ssh.SSHTemplate.SSHSession;\r\nimport com.sohu.cache.util.NMONFileFactory;\r\nimport com.sohu.cache.util.OSFactory;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport java.io.File;\r\n\r\n/**\r\n * 服务器监控脚本服务(nmon识别和监控)\r\n */\r\n@Component\r\npublic class NMONService {\r\n    private static final Logger logger = LoggerFactory.getLogger(NMONService.class);\r\n    //获取系统版本位数命令\r\n    public static final String OS_INFO_CMD = \"/bin/uname -a; /bin/cat /etc/issue\";\r\n    //nmon路径\r\n    public static final String NMON_DIR = \"/opt/cachecloud/soft/\";\r\n    //nmon文件名\r\n    public static final String NMON = \"nmon\";\r\n    //nmon完整路径\r\n    public static final String NMON_FILE = NMON_DIR + NMON;\r\n    //获取nmon版本\r\n    public static final String NMON_VERSION = \"[ -e \\\"\" + NMON_FILE + \"\\\" ] && \" + NMON_FILE + \" -V\";\r\n    //nmon输出的结果文件\r\n    public static final String NMON_LOG = \"/tmp/nmon.log\";\r\n    //nmon输出的老结果文件\r\n    public static final String NMON_OLD_LOG = \"/tmp/nmon.old.log\";\r\n    //tcp输出的结果文件\r\n    public static final String SOCK_LOG = \"/tmp/sock.log\";\r\n    //ulimit输出的结果文件\r\n    public static final String ULIMIT_LOG = \"/tmp/ulimit.log\";\r\n\r\n    //nmon监控启动\r\n    public static final String START_SERVER_COLLECT = NMON_FILE + \" -F \" + NMON_LOG + \" -s0 -c1;\" +\r\n            \"/bin/grep TCP /proc/net/sockstat > \" + SOCK_LOG +\r\n            \";ulimit -n -u > \" + ULIMIT_LOG ;\r\n    //创建nmon路径\r\n    public static final String MK_NMON_DIR = \"/bin/mkdir -p /opt/cachecloud/soft/\";\r\n\r\n    /**\r\n     * 启动nmon收集系统状况\r\n     *\r\n     * @param ip\r\n     * @param session\r\n     * @return @OSInfo 收集到的操作系统信息\r\n     */\r\n    public OSInfo start(String ip, SSHSession session) {\r\n        Result startCollectResult = session.executeCommand(START_SERVER_COLLECT);\r\n        if (!startCollectResult.isSuccess()) {\r\n            logger.error(\"start nmon \" + ip + \" err:\" + startCollectResult.getResult(),\r\n                    startCollectResult.getExcetion());\r\n            //执行命令没有发生异常，则nmon可能不存在或有问题\r\n            if (startCollectResult.getExcetion() == null) {\r\n                //尝试处理出错信息\r\n                return initNmon(ip, session);\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * 尝试修复启动失败的错误\r\n     *\r\n     * @param ip\r\n     * @param session\r\n     */\r\n    private OSInfo initNmon(String ip, SSHSession session) {\r\n        //获取nmon版本\r\n        String version = getNMONVersion(ip, session);\r\n        //获取操作系统原始信息\r\n        OSInfo osInfo = getOSInfo(ip, session);\r\n        OS os = null;\r\n        //nmon文件不存在，需要根据操作系统识别是否支持\r\n        if (null == version) {\r\n            logger.warn(\"{} not exist {}\", ip, NMON_FILE);\r\n            //将原始信息转换为可识别的操作系统\r\n            os = OSFactory.getOS(osInfo);\r\n        } else {\r\n            //nmon存在，但是版本有问题，此时不应该再判断系统信息了，直接用默认的\r\n            logger.warn(\"{} {} version err:\" + version, ip, NMON_FILE);\r\n            os = OSFactory.getDefaultOS(osInfo);\r\n        }\r\n        if (os == null) {\r\n            logger.error(\"unkonw os info={}\", osInfo);\r\n            return null;\r\n        }\r\n        //获取nmon文件\r\n        File nmonFile = NMONFileFactory.getNMONFile(os);\r\n        if (nmonFile == null) {\r\n            logger.warn(\"{} no corresponding nmon file\", os);\r\n            nmonFile = NMONFileFactory.getNMONFile(OSFactory.getDefaultOS(osInfo));\r\n        }\r\n        //将nmon文件传输至服务器\r\n        sendNMONToServer(ip, session, nmonFile);\r\n\r\n        return osInfo;\r\n    }\r\n\r\n    /**\r\n     * 获取nmon文件版本\r\n     *\r\n     * @param ip\r\n     * @param session\r\n     * @return 存在返回版本，不存在返回null, 执行错误返回异常\r\n     */\r\n    private String getNMONVersion(String ip, SSHSession session) {\r\n        Result nmonVersionResult = session.executeCommand(NMON_VERSION);\r\n        if (nmonVersionResult.isSuccess()) {\r\n            return nmonVersionResult.getResult();\r\n        } else {\r\n            logger.error(NMON_VERSION + \" err:\" + nmonVersionResult.getResult(), nmonVersionResult.getExcetion());\r\n        }\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * 获取操作系统信息\r\n     *\r\n     * @param ip\r\n     * @param session\r\n     * @return OSInfo\r\n     */\r\n    private OSInfo getOSInfo(String ip, SSHSession session) {\r\n        final OSInfo osInfo = new OSInfo();\r\n        session.executeCommand(OS_INFO_CMD, new DefaultLineProcessor() {\r\n            public void process(String line, int lineNum) throws Exception {\r\n                switch (lineNum) {\r\n                    case 1:\r\n                        osInfo.setUname(line);\r\n                        break;\r\n                    case 2:\r\n                        osInfo.setIssue(line);\r\n                        break;\r\n                    default:\r\n                        break;\r\n                }\r\n            }\r\n        });\r\n        return osInfo;\r\n    }\r\n\r\n\r\n    /**\r\n     * 将nmon文件scp到服务器上\r\n     *\r\n     * @param ip\r\n     * @param session\r\n     * @param nmonFile\r\n     */\r\n    private void sendNMONToServer(String ip, SSHSession session, File nmonFile) {\r\n        Result mkResult = session.executeCommand(MK_NMON_DIR);\r\n        if (!mkResult.isSuccess()) {\r\n            logger.error(\"mkdir err:\" + mkResult.getResult(), mkResult.getExcetion());\r\n            return;\r\n        }\r\n        Result scpRst = session.scpToFile(nmonFile.getAbsolutePath(), NMON, NMON_DIR);\r\n        if (scpRst.isSuccess()) {\r\n            logger.info(\"scp {} to {} success\", nmonFile.getAbsolutePath(), ip);\r\n        } else {\r\n            logger.error(\"scp to \" + ip + \" err\", scpRst.getExcetion());\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ssh/SSHClient.java",
    "content": "package com.sohu.cache.ssh;\n\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.sshd.client.SshClient;\nimport org.apache.sshd.client.auth.password.PasswordIdentityProvider;\nimport org.apache.sshd.client.session.ClientSession;\nimport org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;\nimport org.apache.sshd.common.keyprovider.KeyIdentityProvider;\nimport org.apache.sshd.common.session.Session;\nimport org.apache.sshd.common.util.security.SecurityUtils;\n\nimport java.io.IOException;\nimport java.nio.file.FileSystemException;\nimport java.nio.file.Paths;\nimport java.security.GeneralSecurityException;\nimport java.security.KeyPair;\nimport java.util.Collection;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * ssh client\n *\n * @Auther: yongfeigao\n * @Date: 2023/10/23\n */\n@Slf4j\n@Data\npublic class SSHClient {\n\n    // 服务器 ssh 用户\n    private String serverUser;\n\n    // 服务器 ssh 密码\n    private String serverPassword;\n\n    // 服务器 ssh 端口\n    private Integer serverPort;\n\n    // 服务器 ssh 链接建立超时时间\n    private Integer serverConnectTimeout;\n\n    // 服务器 ssh 操作超时时间\n    private Integer serverOPTimeout;\n\n    // 服务器 ssh 私钥\n    private String privateKeyPath;\n\n    private SshClient client;\n\n    public void init() throws GeneralSecurityException, IOException {\n        client = buildSshClient();\n        if (StringUtils.isNotEmpty(privateKeyPath)) {\n            setAuthByKey(client);\n        }\n        client.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(getServerPassword()));\n        client.start();\n    }\n\n    private SshClient buildSshClient() {\n        SshClient client = SshClient.setUpDefaultClient();\n        client.setSessionHeartbeat(Session.HeartbeatType.IGNORE, TimeUnit.SECONDS, 10);\n        return client;\n    }\n\n    private void setAuthByKey(SshClient client) throws GeneralSecurityException, IOException {\n        try{\n            KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceParser();\n            Collection<KeyPair> keys = loader.loadKeyPairs(null, Paths.get(privateKeyPath), null);\n            client.setKeyIdentityProvider(KeyIdentityProvider.wrapKeyPairs(keys));\n        }catch (FileSystemException e){\n            log.error(\"setAuthByKey error, please check ssh type and key path and key. \", e);\n        }\n    }\n\n    /**\n     * 连接服务器\n     *\n     * @param ip\n     * @return\n     * @throws IOException\n     */\n    public ClientSession connect(String ip) throws IOException {\n        ClientSession session = getClient().connect(getServerUser(), ip, getServerPort()).verify(getServerConnectTimeout(), TimeUnit.MILLISECONDS).getSession();\n        session.auth().verify(getServerConnectTimeout(), TimeUnit.MILLISECONDS);\n        return session;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ssh/SSHMachineInfo.java",
    "content": "package com.sohu.cache.ssh;\n\nimport lombok.Builder;\nimport lombok.Data;\n\nimport java.util.Objects;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2024/4/3 12:38\n * @Description: ssh session key\n * @Version: 1.0\n */\n@Data\n@Builder\npublic class SSHMachineInfo {\n\n    /**\n     * ip\n     */\n    private String ip;\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    /**\n     * 登录方式\n     */\n    private int authType;\n\n    /**\n     * 密码\n     */\n    private String password;\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        SSHMachineInfo that = (SSHMachineInfo) o;\n        return authType == that.authType && Objects.equals(ip, that.ip) && Objects.equals(password, that.password) && Objects.equals(username, that.username);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(ip, username, authType, password);\n    }\n\n    @Override\n    public String toString() {\n        return \"SSHMachineInfo{\" +\n                \"ip='\" + ip + '\\'' +\n                \", authType=\" + authType +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ssh/SSHService.java",
    "content": "package com.sohu.cache.ssh;\r\n\r\nimport com.sohu.cache.exception.SSHException;\r\n\r\n\r\n/**\r\n * @author leifu\r\n */\r\npublic interface SSHService {\r\n\r\n    /**\r\n     * 执行ssh命令\r\n     * @param ip\r\n     * @param port\r\n     * @param username\r\n     * @param password\r\n     * @param command\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    String execute(String ip, int port, String username, String password, final String command) throws SSHException;\r\n\r\n    /**\r\n     * 执行ssh命令\r\n     * @param ip\r\n     * @param port\r\n     * @param username\r\n     * @param password\r\n     * @param command\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    SSHTemplate.Result executeWithResult(String ip, int port, String username, String password, final String command) throws SSHException;\r\n\r\n    /**\r\n     * 执行ssh命令,可设置超时时间\r\n     * @param ip\r\n     * @param port\r\n     * @param username\r\n     * @param password\r\n     * @param command\r\n     * @param timeoutMills\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    SSHTemplate.Result executeWithResult(String ip, int port, String username, String password, final String command, int timeoutMills) throws SSHException;\r\n\r\n    /**\r\n     * 拷贝文件到远程目录\r\n     * @param ip\r\n     * @param port\r\n     * @param username\r\n     * @param password\r\n     * @param localPath\r\n     * @param remoteDir\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    SSHTemplate.Result scpFileToRemote(String ip, int port, String username,\r\n            String password, final String localPath, final String remoteDir) throws SSHException;\r\n\r\n    /**\r\n     * 拷贝文件到远程目录\r\n     * @param ip\r\n     * @param localPath\r\n     * @param remoteDir\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    SSHTemplate.Result scpFileToRemote(String ip, String localPath, String remoteDir) throws SSHException;\r\n\r\n    /**\r\n     * 执行命令\r\n     * @param ip\r\n     * @param cmd\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    String execute(String ip, String cmd) throws SSHException;\r\n\r\n    /**\r\n     * 执行命令\r\n     * @param ip\r\n     * @param cmd\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    SSHTemplate.Result executeWithResult(String ip, String cmd) throws SSHException;\r\n\r\n    /**\r\n     * 执行命令,可设置超时时间\r\n     * @param ip\r\n     * @param cmd\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    SSHTemplate.Result executeWithResult(String ip, String cmd, int millsSecond) throws SSHException;\r\n\r\n    /**\r\n     * 查看机器ip上的端口port是否已被占用；\r\n     * @param ip\r\n     * @param port\r\n     * @return\r\n     * @throws SSHException\r\n     */\r\n    boolean isPortUsed(String ip, int port) throws SSHException;\r\n\r\n    /**\r\n     * 通过ip来判断ssh端口\r\n     * @param ip\r\n     * @return\r\n     */\r\n    int getSshPort(String ip);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ssh/SSHServiceImpl.java",
    "content": "package com.sohu.cache.ssh;\r\n\r\nimport com.sohu.cache.dao.MachineDao;\r\nimport com.sohu.cache.entity.MachineInfo;\r\nimport com.sohu.cache.exception.SSHException;\r\nimport com.sohu.cache.ssh.SSHTemplate.Result;\r\nimport com.sohu.cache.ssh.SSHTemplate.SSHCallback;\r\nimport com.sohu.cache.ssh.SSHTemplate.SSHSession;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport java.util.Map;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\n\r\nimport static com.sohu.cache.constant.EmptyObjectConstant.EMPTY_STRING;\r\nimport static com.sohu.cache.constant.SymbolConstant.COMMA;\r\n\r\n/**\r\n * @author fulei\r\n * @date 2018年6月25日\r\n */\r\n@Service\r\npublic class SSHServiceImpl implements SSHService {\r\n\r\n    private Logger logger = LoggerFactory.getLogger(SSHServiceImpl.class);\r\n\r\n    @Autowired\r\n    private MachineDao machineDao;\r\n\r\n    private Map<String, MachineInfo> machineIpInfoMap = new ConcurrentHashMap<String, MachineInfo>();\r\n\r\n    private Map<String, Long> appNameIdMap = new ConcurrentHashMap<String, Long>();\r\n\r\n    @Autowired(required = false)\r\n    private SSHTemplate sshTemplate;\r\n\r\n    @Override\r\n    public String execute(String ip, int port, String username, String password, final String command)\r\n            throws SSHException {\r\n        Result rst = executeWithResult(ip, port, username, password, command);\r\n        if (rst.isSuccess()) {\r\n            return rst.getResult();\r\n        }\r\n        return \"\";\r\n    }\r\n\r\n    @Override\r\n    public Result executeWithResult(String ip, int port, String username, String password, final String command)\r\n            throws SSHException {\r\n        return sshTemplate.execute(ip, port, username, password, new SSHCallback() {\r\n            public Result call(SSHSession session) {\r\n                return session.executeCommand(command);\r\n            }\r\n        });\r\n    }\r\n\r\n    @Override\r\n    public Result executeWithResult(String ip, int port, String username, String password, final String command, int timeoutMills)\r\n            throws SSHException {\r\n        return sshTemplate.execute(ip, port, username, password, new SSHCallback() {\r\n            public Result call(SSHSession session) {\r\n                return session.executeCommand(command, timeoutMills);\r\n            }\r\n        });\r\n    }\r\n\r\n    @Override\r\n    public Result executeWithResult(String ip, String cmd) throws SSHException {\r\n        return executeWithResult(ip, getSshPort(ip), ConstUtils.USERNAME, ConstUtils.PASSWORD, cmd);\r\n    }\r\n\r\n    public Result executeWithResult(String ip, String cmd, int millsSecond) throws SSHException {\r\n        return executeWithResult(ip, getSshPort(ip), ConstUtils.USERNAME, ConstUtils.PASSWORD, cmd, millsSecond);\r\n    }\r\n\r\n    @Override\r\n    public Result scpFileToRemote(String ip, int port, String username,\r\n                                  String password, final String localPath, final String remoteDir) throws SSHException {\r\n        return sshTemplate.execute(ip, port, username, password, new SSHCallback() {\r\n            public Result call(SSHSession session) {\r\n                return session.scpToDir(localPath, remoteDir, \"0644\");\r\n            }\r\n        });\r\n    }\r\n\r\n    @Override\r\n    public Result scpFileToRemote(String ip, String localPath, String remoteDir) throws SSHException {\r\n        return scpFileToRemote(ip, getSshPort(ip), ConstUtils.USERNAME, ConstUtils.PASSWORD, localPath, remoteDir);\r\n    }\r\n\r\n    @Override\r\n    public String execute(String ip, String cmd) throws SSHException {\r\n        return execute(ip, getSshPort(ip), ConstUtils.USERNAME, ConstUtils.PASSWORD, cmd);\r\n    }\r\n\r\n    @Override\r\n    public boolean isPortUsed(String ip, int port) throws SSHException {\r\n        /**\r\n         * 执行ps命令，查看端口，以确认刚才执行的shell命令是否成功，返回一般是这样的：\r\n         *  root     12510 12368  0 14:34 pts/0    00:00:00 redis-server *:6379\r\n         */\r\n        String psCmd = \"/bin/ps -ef | grep %s | grep -v grep\";\r\n        psCmd = String.format(psCmd, port);\r\n        String psResponse = execute(ip, psCmd);\r\n        boolean isUsed = false;\r\n\r\n        if (StringUtils.isNotBlank(psResponse)) {\r\n            String[] resultArr = psResponse.split(System.lineSeparator());\r\n            for (String resultLine : resultArr) {\r\n                if (resultLine.contains(String.valueOf(port))) {\r\n                    isUsed = true;\r\n                    break;\r\n                }\r\n            }\r\n        }\r\n        return isUsed;\r\n    }\r\n\r\n    @Override\r\n    public int getSshPort(String ip) {\r\n        /**\r\n         * 如果ssh默认端口不是22,请自行实现该逻辑\r\n         */\r\n        return ConstUtils.SSH_PORT_DEFAULT;\r\n    }\r\n\r\n    /**\r\n     * 匹配字符串中的数字\r\n     *\r\n     * @param content\r\n     * @return\r\n     */\r\n    private static String matchMemLineNumber(String content) {\r\n        String result = EMPTY_STRING;\r\n        if (content == null || EMPTY_STRING.equals(content.trim())) {\r\n            return result;\r\n        }\r\n        Pattern pattern = Pattern.compile(\"(\\\\d+)\");\r\n        Matcher matcher = pattern.matcher(content);\r\n        if (matcher.find()) {\r\n            result = matcher.group(1);\r\n        }\r\n        return result;\r\n    }\r\n\r\n    /**\r\n     * 从top的cpuLine解析出us\r\n     *\r\n     * @param cpuLine\r\n     * @return\r\n     */\r\n    private static double getUsCpu(String cpuLine) {\r\n        if (cpuLine == null || EMPTY_STRING.equals(cpuLine.trim())) {\r\n            return 0;\r\n        }\r\n        String[] items = cpuLine.split(COMMA);\r\n        if (items.length < 1) {\r\n            return 0;\r\n        }\r\n        String usCpuStr = items[0];\r\n        return NumberUtils.toDouble(matchCpuLine(usCpuStr));\r\n    }\r\n\r\n    private static String matchCpuLine(String content) {\r\n        String result = EMPTY_STRING;\r\n        if (content == null || EMPTY_STRING.equals(content.trim())) {\r\n            return result;\r\n        }\r\n        Pattern pattern = Pattern.compile(\"(\\\\d+).(\\\\d+)\");\r\n        Matcher matcher = pattern.matcher(content);\r\n        if (matcher.find()) {\r\n            result = matcher.group();\r\n        }\r\n        return result;\r\n    }\r\n\r\n    public MachineInfo getMachineInfo(String ip) {\r\n        MachineInfo machineInfo = machineIpInfoMap.get(ip);\r\n        if (machineInfo == null) {\r\n            machineInfo = machineDao.getMachineInfoByIp(ip);\r\n            machineIpInfoMap.put(ip, machineInfo);\r\n        }\r\n        return machineInfo;\r\n    }\r\n\r\n    public Long getAppId(String name) {\r\n        Long appId = appNameIdMap.get(name);\r\n        return appId;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ssh/SSHSessionPooledObjectFactory.java",
    "content": "package com.sohu.cache.ssh;\n\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.SshAuthTypeEnum;\nimport org.apache.commons.pool2.KeyedPooledObjectFactory;\nimport org.apache.commons.pool2.PooledObject;\nimport org.apache.commons.pool2.impl.DefaultPooledObject;\nimport org.apache.sshd.client.session.ClientSession;\nimport org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;\nimport org.apache.sshd.common.util.security.SecurityUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.security.GeneralSecurityException;\nimport java.security.KeyPair;\nimport java.util.Collection;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * ssh session链接池工厂\n *\n * @Auther: yongfeigao\n * @Date: 2023/10/20\n */\npublic class SSHSessionPooledObjectFactory implements KeyedPooledObjectFactory<SSHMachineInfo, ClientSession> {\n\n    private final Logger logger = LoggerFactory.getLogger(getClass());\n\n    private SSHClient sshClient;\n\n    public SSHSessionPooledObjectFactory() throws GeneralSecurityException, IOException {\n        sshClient = new SSHClient();\n        sshClient.setServerUser(ConstUtils.USERNAME);\n        sshClient.setServerPassword(ConstUtils.PASSWORD);\n        sshClient.setServerPort(ConstUtils.SSH_PORT_DEFAULT);\n        sshClient.setServerConnectTimeout(ConstUtils.SSH_CONNECTION_TIMEOUT);\n        if (ConstUtils.SSH_AUTH_TYPE == SshAuthTypeEnum.PUBLIC_KEY.getValue()) {\n            sshClient.setPrivateKeyPath(ConstUtils.PUBLIC_KEY_PEM);\n        }\n        sshClient.init();\n    }\n\n    @Override\n    public PooledObject<ClientSession> makeObject(SSHMachineInfo sshMachineInfo) throws Exception {\n        int port = ConstUtils.SSH_PORT_DEFAULT;\n        ClientSession session = sshClient.getClient().connect(ConstUtils.USERNAME, sshMachineInfo.getIp(),\n                port).verify(ConstUtils.SSH_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS).getSession();\n        session.setUsername(sshMachineInfo.getUsername());\n        if(sshMachineInfo.getAuthType() == SshAuthTypeEnum.PASSWORD.getValue()){\n            session.addPasswordIdentity(sshMachineInfo.getPassword());\n        }else if(sshMachineInfo.getAuthType() == SshAuthTypeEnum.PUBLIC_KEY.getValue()){\n            KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceParser();\n            Collection<KeyPair> keys = loader.loadKeyPairs(null, Paths.get(ConstUtils.PUBLIC_KEY_PEM), null);\n            session.addPublicKeyIdentity(keys.iterator().next());\n        }\n        session.auth().verify(ConstUtils.SSH_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);\n        logger.info(\"create object, key:{}\", sshMachineInfo.getIp());\n        return new DefaultPooledObject<>(session);\n    }\n\n    @Override\n    public void destroyObject(SSHMachineInfo sshMachineInfo, PooledObject<ClientSession> pooledObject) throws Exception {\n        ClientSession clientSession = pooledObject.getObject();\n        if (clientSession != null) {\n            try {\n                clientSession.close();\n            } catch (Exception e) {\n                logger.warn(\"close err, key:{}\", sshMachineInfo.getIp(), e);\n            }\n        }\n        logger.info(\"destroy object {}\", sshMachineInfo.getIp());\n    }\n\n    @Override\n    public boolean validateObject(SSHMachineInfo sshMachineInfo, PooledObject<ClientSession> pooledObject) {\n        boolean closed = pooledObject.getObject().isClosed();\n        if (closed) {\n            logger.warn(\"{} session closed\", sshMachineInfo.getIp());\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public void activateObject(SSHMachineInfo sshMachineInfo, PooledObject<ClientSession> pooledObject) throws Exception {\n\n    }\n\n    @Override\n    public void passivateObject(SSHMachineInfo sshMachineInfo, PooledObject<ClientSession> pooledObject) throws Exception {\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ssh/SSHTemplate.java",
    "content": "package com.sohu.cache.ssh;\n\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.pool2.impl.GenericKeyedObjectPool;\nimport org.apache.sshd.client.channel.ClientChannel;\nimport org.apache.sshd.client.channel.ClientChannelEvent;\nimport org.apache.sshd.client.session.ClientSession;\nimport org.apache.sshd.scp.client.ScpClient;\nimport org.apache.sshd.scp.client.ScpClientCreator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.io.*;\nimport java.nio.file.FileSystems;\nimport java.nio.file.attribute.PosixFilePermission;\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * SSH操作模板类\n */\n@Component\npublic class SSHTemplate {\n\tprivate static final Logger logger = LoggerFactory.getLogger(SSHTemplate.class);\n\n    public static final List<PosixFilePermission> PERMS = Arrays.asList(PosixFilePermission.OWNER_READ,\n            PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.OTHERS_READ);\n\n    @Autowired\n    private GenericKeyedObjectPool<SSHMachineInfo, ClientSession> clientSessionPool;\n\n    private static final int CONNCET_TIMEOUT = 5000;\n\n\tprivate static final int OP_TIMEOUT = 10000;\n\n\tpublic Result execute(String ip, SSHCallback callback) throws SSHException{\n\t\treturn execute(ip,ConstUtils.SSH_PORT_DEFAULT, ConstUtils.USERNAME,\n\t\t\t\tConstUtils.PASSWORD, callback);\n\t}\n\n\t/**\n\t * 通过回调执行命令\n\t * @param ip\n\t * @param port\n\t * @param username\n\t * @param password\n\t * @param callback 可以使用Session执行多个命令\n\t * @throws SSHException\n\t */\n    public Result execute(String ip, int port, String username, String password,\n    \t\tSSHCallback callback) throws SSHException{\n        ClientSession session = null;\n        SSHMachineInfo sshMachineInfo = SSHMachineInfo.builder().ip(ip).username(username)\n                .authType(ConstUtils.SSH_AUTH_TYPE).password(password).build();\n        try {\n            session = clientSessionPool.borrowObject(sshMachineInfo);\n            return callback.call(new SSHSession(session, ip));\n        } catch (Exception e) {\n            throw new SSHException(\"SSH exception: \" + e.getMessage(), e);\n        } finally {\n            close(sshMachineInfo, session);\n        }\n    }\n\n    private DefaultLineProcessor generateDefaultLineProcessor(StringBuilder buffer) {\n        return new DefaultLineProcessor() {\n            public void process(String line, int lineNum) throws Exception {\n                if (lineNum > 1) {\n                    buffer.append(System.lineSeparator());\n                }\n                buffer.append(line);\n            }\n        };\n    }\n\n    /**\n     * 获取调用命令后的返回结果\n     * @param is 输入流\n     * @return 如果获取结果有异常或者无结果，那么返回null\n     */\n    private String getResult(InputStream is) {\n    \tfinal StringBuilder buffer = new StringBuilder();\n    \tLineProcessor lp = new DefaultLineProcessor() {\n\t\t\tpublic void process(String line, int lineNum) throws Exception {\n\t\t\t\tif(lineNum > 1) {\n\t\t\t\t\tbuffer.append(System.lineSeparator());\n\t\t\t\t}\n\t\t\t\tbuffer.append(line);\n\t\t\t}\n    \t};\n    \tprocessStream(is, lp);\n    \treturn buffer.length() > 0 ? buffer.toString() : null;\n    }\n\n    /**\n     * 从流中获取内容\n     * @param is\n     */\n    private void processStream(InputStream is, LineProcessor lineProcessor) {\n    \tBufferedReader reader = null;\n        try {\n        \treader = new BufferedReader(new InputStreamReader(is));\n\t    \tString line = null;\n\t    \tint lineNum = 1;\n\t        while ((line = reader.readLine()) != null) {\n\t        \ttry {\n\t\t\t\t\tlineProcessor.process(line, lineNum);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlogger.error(\"err line:\" + line, e);\n\t\t\t\t}\n                if (lineProcessor instanceof DefaultLineProcessor) {\n                    ((DefaultLineProcessor) lineProcessor).setLineNum(lineNum);\n                }\n\t\t\t\tlineNum++;\n\t        }\n            lineProcessor.finish();\n        } catch (IOException e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n        \tclose(reader);\n        }\n    }\n\n    private void close(BufferedReader read) {\n    \tif (read != null) {\n            try {\n                read.close();\n            } catch (IOException e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n    }\n\n    private void close(SSHMachineInfo sshMachineInfo, ClientSession session) {\n        if (session != null) {\n            try {\n                clientSessionPool.returnObject(sshMachineInfo, session);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n    }\n\n    /**\n     * 可以调用多次executeCommand， 并返回结果\n     */\n    public class SSHSession {\n\n        private String address;\n        private ClientSession clientSession;\n\n        private SSHSession(ClientSession clientSession, String address) {\n            this.clientSession = clientSession;\n            this.address = address;\n        }\n\n        /**\n         * 执行命令并返回结果，可以执行多次\n         * @param cmd\n         * @return 执行成功Result为true，并携带返回信息,返回信息可能为null\n         *         执行失败Result为false，并携带失败信息\n         *         执行异常Result为false，并携带异常\n         */\n        public Result executeCommand(String cmd) {\n            return executeCommand(cmd, OP_TIMEOUT);\n        }\n\n        public Result executeCommand(String cmd, int timoutMillis) {\n            return executeCommand(cmd, null, timoutMillis);\n        }\n\n        public Result executeCommand(String cmd, LineProcessor lineProcessor) {\n            return executeCommand(cmd, lineProcessor, OP_TIMEOUT);\n        }\n\n        /**\n         * 执行命令并返回结果，可以执行多次\n         * @param cmd\n         * @param lineProcessor 回调处理行\n         * @return 如果lineProcessor不为null,那么永远返回Result.true\n         */\n        public Result executeCommand(String cmd, LineProcessor lineProcessor, int timoutMillis) {\n            try (ByteArrayOutputStream stdout = new ByteArrayOutputStream();\n                 ByteArrayOutputStream stderr = new ByteArrayOutputStream();\n                 ClientChannel channel = clientSession.createExecChannel(cmd)) {\n                channel.setOut(stdout);\n                channel.setErr(stderr);\n                channel.open().verify(timoutMillis);\n                // Wait (forever) for the channel to close - signalling command finished\n                channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L);\n                LineProcessor tmpLP = lineProcessor;\n                // 如果客户端需要进行行处理，则直接进行回调\n                if (tmpLP != null) {\n                    processStream(new ByteArrayInputStream(stdout.toByteArray()), tmpLP);\n                } else {\n                    StringBuilder buffer = new StringBuilder();\n                    tmpLP = generateDefaultLineProcessor(buffer);\n                    processStream(new ByteArrayInputStream(stdout.toByteArray()), tmpLP);\n                    if (buffer.length() > 0) {\n                        return new Result(true, buffer.toString());\n                    }\n                }\n                if(tmpLP.lineNum() == 0) {\n                    // 返回为null代表可能有异常，需要检测标准错误输出，以便记录日志\n                    Result errResult = tryLogError(new ByteArrayInputStream(stderr.toByteArray()), cmd);\n                    if (errResult != null) {\n                        return errResult;\n                    }\n                }\n                return new Result(true, null);\n            } catch (Exception e) {\n                logger.error(\"execute ip:{} cmd:{}\", address, cmd, e);\n                return new Result(e);\n            }\n        }\n\n        private Result tryLogError(InputStream is, String cmd) {\n            StringBuilder buffer = new StringBuilder();\n            LineProcessor lp = generateDefaultLineProcessor(buffer);\n            processStream(is, lp);\n            String errInfo = buffer.length() > 0 ? buffer.toString() : null;\n            if (errInfo != null) {\n                logger.error(\"address \" + address + \" execute cmd:({}), err:{}\", cmd, errInfo);\n                return new Result(false, errInfo);\n            }\n            return null;\n        }\n\n        /**\n         * Copy a set of local files to a remote directory, uses the specified mode when\n         * creating the file on the remote side.\n         * @param localFiles\n         *            Path and name of local file.\n         * @param remoteFile\n         *            name of remote file.\n         * @param remoteTargetDirectory\n         *            Remote target directory. Use an empty string to specify the default directory.\n         * @param mode\n         *            a four digit string (e.g., 0644, see \"man chmod\", \"man open\")\n         * @throws IOException\n         */\n        public Result scp(String[] localFiles, String remoteFile, String remoteTargetDirectory, String mode) {\n            try {\n                ScpClient client = ScpClientCreator.instance().createScpClient(clientSession);\n                String separator = FileSystems.getDefault().getSeparator();\n                if(localFiles.length == 1){\n                    if(StringUtils.isBlank(remoteFile)){\n                        client.upload(localFiles, remoteTargetDirectory, ScpClient.Option.TargetIsDirectory);\n                        int index = localFiles[0].lastIndexOf(separator);\n                        if(index <= 0){\n                            index = 0;\n                        }else{\n                            index = index + 1;\n                        }\n                        String fileName = localFiles[0].substring(index);\n                        clientSession.executeRemoteCommand(\"chmod \" + mode + \" \\\"\" + remoteTargetDirectory + \"/\" + fileName + \"\\\"\");\n                    } else {\n                        client.upload(localFiles, remoteTargetDirectory + \"/\" + remoteFile);\n                        clientSession.executeRemoteCommand(\"chmod \" + mode + \" \\\"\" + remoteTargetDirectory + \"/\" + remoteFile + \"\\\"\");\n                    }\n                } else {\n                    client.upload(localFiles, remoteTargetDirectory, ScpClient.Option.TargetIsDirectory);\n                    StringBuffer sb = new StringBuffer();\n                    List<String> files = Arrays.asList(localFiles);\n                    String remoteFiles = files.stream().map(file -> {\n                        int index = file.lastIndexOf(separator);\n                        if(index <= 0){\n                            index = 0;\n                        }else{\n                            index = index + 1;\n                        }\n                        return \" \\\"\" + remoteTargetDirectory + \"/\" + file.substring(index) + \"\\\"\";\n                    }).collect(Collectors.joining(\" \"));\n                    clientSession.executeRemoteCommand(\"chmod \" + mode + \" \" + remoteFiles);\n                }\n                return new Result(true);\n            } catch (Exception e) {\n                logger.error(\"scp local=\"+Arrays.toString(localFiles) + \" to \" +\n                        remoteTargetDirectory + \" remote=\" + remoteFile + \" err\", e);\n                return new Result(e);\n            }\n        }\n\n        public Result scpToDir(String localFile, String remoteTargetDirectory) {\n            return scpToDir(localFile, remoteTargetDirectory, \"0744\");\n        }\n\n        public Result scpToDir(String localFile, String remoteTargetDirectory, String mode) {\n            return scp(new String[] { localFile }, null, remoteTargetDirectory, mode);\n        }\n\n        public Result scpToDir(String[] localFile, String remoteTargetDirectory) {\n            return scp(localFile, null, remoteTargetDirectory, \"0744\");\n        }\n        public Result scpToFile(String localFile, String remoteFile, String remoteTargetDirectory) {\n            return scpToFile(localFile, remoteFile, remoteTargetDirectory, \"0744\");\n        }\n        public Result scpToFile(String localFile, String remoteFile, String remoteTargetDirectory, String mode) {\n            return scp(new String[] { localFile }, remoteFile, remoteTargetDirectory, \"0744\");\n        }\n    }\n\n    /**\n     * 结果封装\n     */\n    public static class Result{\n        private boolean success;\n        private String result;\n        private Exception excetion;\n\n        public Result(boolean success) {\n            this.success = success;\n        }\n\n        public Result(boolean success, String result) {\n            this.success = success;\n            this.result = result;\n        }\n\n        public Result(Exception excetion) {\n            this.success = false;\n            this.excetion = excetion;\n        }\n\n        public Exception getExcetion() {\n            return excetion;\n        }\n\n        public void setExcetion(Exception excetion) {\n            this.excetion = excetion;\n        }\n\n        public boolean isSuccess() {\n            return success;\n        }\n\n        public void setSuccess(boolean success) {\n            this.success = success;\n        }\n\n        public String getResult() {\n            return result;\n        }\n\n        public void setResult(String result) {\n            this.result = result;\n        }\n\n        @Override\n        public String toString() {\n            return \"Result [success=\" + success + \", result=\" + result\n                    + \", excetion=\" + excetion + \"]\";\n        }\n    }\n\n    /**\n     *\t执行命令回调\n     */\n    public interface SSHCallback{\n        /**\n         * 执行回调\n         * @param session\n         */\n        Result call(SSHSession session);\n    }\n\n    /**\n     * 从流中直接解析数据\n     */\n    public static interface LineProcessor {\n        /**\n         * 处理行\n         * @param line  内容\n         * @param lineNum   行号，从1开始\n         * @throws Exception\n         */\n        void process(String line, int lineNum) throws Exception;\n\n        /**\n         * 返回内容的行数，如果为0需要检测错误流\n         * @return\n         */\n        int lineNum();\n\n        /**\n         * 所有的行处理完毕回调该方法\n         */\n        void finish();\n    }\n\n    public static abstract class DefaultLineProcessor implements LineProcessor {\n        protected int lineNum;\n\n        @Override\n        public int lineNum() {\n            return lineNum;\n        }\n\n        public void setLineNum(int lineNum) {\n            this.lineNum = lineNum;\n        }\n\n        public void finish() {}\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/ssh/SSHUtil.java",
    "content": "package com.sohu.cache.ssh;\n\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.entity.MachineStats;\nimport com.sohu.cache.exception.IllegalParamException;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.ssh.SSHTemplate.DefaultLineProcessor;\nimport com.sohu.cache.ssh.SSHTemplate.Result;\nimport com.sohu.cache.ssh.SSHTemplate.SSHCallback;\nimport com.sohu.cache.ssh.SSHTemplate.SSHSession;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.IntegerUtil;\nimport com.sohu.cache.util.StringUtil;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport java.text.DecimalFormat;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static com.sohu.cache.constant.BaseConstant.WORD_SEPARATOR;\nimport static com.sohu.cache.constant.EmptyObjectConstant.EMPTY_STRING;\nimport static com.sohu.cache.constant.SymbolConstant.COMMA;\nimport static com.sohu.cache.constant.SymbolConstant.PERCENT;\nimport static com.sohu.cache.constant.SymbolConstant.SPACE;\n\n/**\n * Created by yijunzhang on 14-6-20.\n */\n@Component\npublic class SSHUtil {\n    private static final Logger logger = LoggerFactory.getLogger(SSHUtil.class);\n\n    private final static String COMMAND_TOP = \"top -b -n 1 | head -5\";\n    private final static String COMMAND_DF_LH = \"df -lh\";\n    private final static String LOAD_AVERAGE_STRING = \"load average: \";\n    private final static String COMMAND_MEM = \"cat /proc/meminfo | grep -E -w 'MemTotal|MemFree|Buffers|Cached'\";\n\n    private final static String COMMAND_CPU_MEM_DF = \"(top -b -n 1 | head -5) && (cat /proc/meminfo | grep -E -w 'MemTotal|MemFree|Buffers|Cached') && ((mount | grep '/opt/cachecloud/data');(df -aBM | grep -v '-'))\";\n\n    private final static String MEM_TOTAL = \"MemTotal\";\n    private final static String MEM_FREE = \"MemFree\";\n    private final static String MEM_BUFFERS = \"Buffers\";\n    private final static String MEM_CACHED = \"Cached\";\n\n    private final static String MOUNT_CACHECLOUD_PATH = \"/opt/cachecloud/data\";\n\n    private final static String M_SIZE = \"M\";\n\n    @Autowired\n    private SSHTemplate sshTemplate;\n\n    //使用 @SSHTemplate 重构SSHUtil\n    private static SSHTemplate sshTemplateNew;\n\n    @PostConstruct\n    private void initSSHTemplate(){\n        sshTemplateNew = sshTemplate;\n    }\n\n    /**\n     * Get HostPerformanceEntity[cpuUsage, memUsage, load] by ssh.<br>\n     * 方法返回前已经释放了所有资源，调用方不需要关心\n     *\n     * @param ip\n     * @param userName\n     * @param password\n     * @throws Exception\n     * @since 1.0.0\n     */\n    public static MachineStats getMachineInfo(String ip, int port, String userName,\n                                              String password) throws SSHException {\n        if (StringUtil.isBlank(ip)) {\n            try {\n                throw new IllegalParamException(\"Param ip is empty!\");\n            } catch (IllegalParamException e) {\n                throw new SSHException(e.getMessage(), e);\n            }\n        }\n        port = IntegerUtil.defaultIfSmallerThan0(port, ConstUtils.SSH_PORT_DEFAULT);\n        final MachineStats machineStats = new MachineStats();\n        machineStats.setIp(ip);\n\n        sshTemplateNew.execute(ip, port, userName, password, session -> {\n            //解析top命令\n            session.executeCommand(COMMAND_CPU_MEM_DF, new DefaultLineProcessor() {\n                private String totalMem;\n                private String freeMem;\n                private String buffersMem;\n                private String cachedMem;\n\n                private String totalDisk;\n                private String availableDisk;\n                private String usedDiskRatio;\n                private String mountDisk;\n                private Map<String, String> diskUsageMap = Maps.newHashMap();\n\n                public void process(String line, int lineNum) throws Exception {\n                    if (1 == lineNum) {\n                        // 第一行，通常是这样：\n                        // top - 19:58:52 up 416 days, 30 min, 1 user, load average:\n                        // 0.00, 0.00, 0.00\n                        int loadAverageIndex = line.indexOf(LOAD_AVERAGE_STRING);\n                        String loadAverages = line.substring(loadAverageIndex)\n                                .replace(LOAD_AVERAGE_STRING, EMPTY_STRING);\n                        String[] loadAverageArray = loadAverages.split(\",\");\n                        if (3 == loadAverageArray.length) {\n                            machineStats.setLoad(StringUtil.trimToEmpty(loadAverageArray[0]));\n                        }\n                    } else if (3 == lineNum) {\n                        // 第三行通常是这样：\n                        // , 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa,\n                        // 0.0% hi, 0.0% si\n                        // redhat:%Cpu(s):  0.0 us\n                        // centos7:Cpu(s): 0.0% us\n                        double cpuUs = getUsCpu(line);\n                        machineStats.setCpuUsage(String.valueOf(cpuUs));\n                    } else if (lineNum > 5 && lineNum < 10) {\n                        if (line.contains(MEM_TOTAL)) {\n                            totalMem = matchMemLineNumber(line).trim();\n                        } else if (line.contains(MEM_FREE)) {\n                            freeMem = matchMemLineNumber(line).trim();\n                        } else if (line.contains(MEM_BUFFERS)) {\n                            buffersMem = matchMemLineNumber(line).trim();\n                        } else if (line.contains(MEM_CACHED)) {\n                            cachedMem = matchMemLineNumber(line).trim();\n                        }\n                    } else if (lineNum >= 10) {\n                        if(lineNum == 10){\n                            if(line.contains(MOUNT_CACHECLOUD_PATH)){\n                                String[] mountArray = line.split(SPACE);\n                                if(mountArray != null && mountArray.length > 1){\n                                    mountDisk = mountArray[0];\n                                }\n                                return;\n                            }\n                        }\n                        /**\n                         * 内容通常是这样： Filesystem 容量 已用 可用 已用% 挂载点 /dev/xvda2 5.8G 3.2G 2.4G\n                         * 57% / /dev/xvda1 99M 8.0M 86M 9% /boot none 769M 0 769M 0%\n                         * /dev/shm /dev/xvda7 68G 7.1G 57G 12% /home /dev/xvda6 2.0G 36M\n                         * 1.8G 2% /tmp /dev/xvda5 2.0G 199M 1.7G 11% /var\n                         **/\n                        line = line.replaceAll(\" {1,}\", WORD_SEPARATOR);\n                        String[] lineArray = line.split(WORD_SEPARATOR);\n                        if (6 == lineArray.length) {\n                            String diskUsage = lineArray[4];\n                            String mountedOn = lineArray[5];\n                            diskUsageMap.put(mountedOn, diskUsage);\n                            if(StringUtils.isNotEmpty(mountDisk) && totalDisk == null){\n                                String fileDisk = lineArray[0];\n                                if(mountDisk.equals(fileDisk)){\n                                    totalDisk = lineArray[1];\n                                    if(totalDisk != null){\n                                        totalDisk = totalDisk.replace(M_SIZE, EMPTY_STRING);\n                                    }\n                                    availableDisk = lineArray[3];\n                                    if(availableDisk != null){\n                                        availableDisk = availableDisk.replace(M_SIZE, EMPTY_STRING);\n                                    }\n                                    usedDiskRatio = lineArray[4];\n                                    if(usedDiskRatio != null){\n                                        usedDiskRatio = usedDiskRatio.replace(PERCENT, EMPTY_STRING);\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n\n                public void finish() {\n                    if (!StringUtil.isBlank(totalMem, freeMem, buffersMem)) {\n                        Long totalMemLong = NumberUtils.toLong(totalMem);\n                        Long freeMemLong = NumberUtils.toLong(freeMem);\n                        Long buffersMemLong = NumberUtils.toLong(buffersMem);\n                        Long cachedMemLong = NumberUtils.toLong(cachedMem);\n                        Long usedMemFree = freeMemLong + buffersMemLong + cachedMemLong;\n                        Double memoryUsage = 1 - (NumberUtils.toDouble(usedMemFree.toString()) / NumberUtils\n                                .toDouble(totalMemLong.toString()) / 1.0);\n                        machineStats.setMemoryTotal(String.valueOf(totalMemLong));\n                        machineStats.setMemoryFree(String.valueOf(usedMemFree));\n                        DecimalFormat df = new DecimalFormat(\"0.00\");\n                        machineStats.setMemoryUsageRatio(df.format(memoryUsage * 100));\n                        machineStats.setDiskTotal(totalDisk);\n                        machineStats.setDiskAvailable(availableDisk);\n                        machineStats.setDiskUsageRatio(usedDiskRatio);\n                    }\n                    machineStats.setDiskUsageMap(diskUsageMap);\n                }\n            });\n            return null;\n        });\n\n        // 统计当前网络流量 @TODO \n        Double traffic = 0.0;\n        machineStats.setTraffic(traffic.toString());\n\n        return machineStats;\n    }\n\n    /**\n     * SSH 方式登录远程主机，执行命令,方法内部会关闭所有资源，调用方无须关心。\n     *\n     * @param ip       主机ip\n     * @param username 用户名\n     * @param password 密码\n     * @param command  要执行的命令\n     */\n    public static String execute(String ip, int port, String username, String password,\n                                 final String command) throws SSHException {\n\n        if (StringUtil.isBlank(command)) {\n            return EMPTY_STRING;\n        }\n        port = IntegerUtil.defaultIfSmallerThan0(port, ConstUtils.SSH_PORT_DEFAULT);\n\n        Result rst = sshTemplateNew.execute(ip, port, username, password, new SSHCallback() {\n            public Result call(SSHSession session) {\n                return session.executeCommand(command);\n            }\n        });\n        if (rst.isSuccess()) {\n            return rst.getResult();\n        }\n        return \"\";\n    }\n\n\n    public static String execute(String ip, int port, String username, String password,\n                                 final String command, int timeout) throws SSHException {\n\n        if (StringUtil.isBlank(command)) {\n            return EMPTY_STRING;\n        }\n        port = IntegerUtil.defaultIfSmallerThan0(port, ConstUtils.SSH_PORT_DEFAULT);\n\n        Result rst = sshTemplateNew.execute(ip, port, username, password, new SSHCallback() {\n            public Result call(SSHSession session) {\n                return session.executeCommand(command, timeout);\n            }\n        });\n        if (rst != null) {\n            return rst.getResult();\n        }\n        return \"\";\n    }\n\n    /**\n     * @param ip\n     * @param port\n     * @param username\n     * @param password\n     * @param localPath\n     * @param remoteDir\n     * @return\n     * @throws SSHException\n     */\n    public static boolean scpFileToRemote(String ip, int port, String username,\n                                          String password, final String localPath, final String remoteDir) throws SSHException {\n        Result rst = sshTemplateNew.execute(ip, port, username, password, new SSHCallback() {\n            public Result call(SSHSession session) {\n                return session.scpToDir(localPath, remoteDir);\n            }\n        });\n        if (rst.isSuccess()) {\n            return true;\n        }\n        if (rst.getExcetion() != null) {\n            throw new SSHException(rst.getExcetion());\n        }\n        return false;\n    }\n\n    /**\n     * 重载，使用默认端口、用户名和密码\n     *\n     * @param ip\n     * @param localPath\n     * @param remoteDir\n     * @return\n     * @throws SSHException\n     */\n    public static boolean scpFileToRemote(String ip, String localPath, String remoteDir) throws SSHException {\n        int sshPort = SSHUtil.getSshPort(ip);\n        return scpFileToRemote(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, localPath, remoteDir);\n    }\n\n    /**\n     * 重载，使用默认端口、用户名和密码\n     *\n     * @param ip\n     * @param cmd\n     * @return\n     * @throws SSHException\n     */\n    public static String execute(String ip, String cmd) throws SSHException {\n        int sshPort = SSHUtil.getSshPort(ip);\n        return execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, cmd);\n    }\n\n    public static String execute(String ip, String cmd, Integer timeout) throws SSHException {\n        int sshPort = SSHUtil.getSshPort(ip);\n        if (timeout == null) {\n            return execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, cmd, 30000);\n        } else {\n            return execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, cmd, timeout);\n        }\n\n    }\n\n    /**\n     * 查看机器ip上的端口port是否已被占用；\n     *\n     * @param ip   机器ip\n     * @param port 要检查的端口\n     * @return 如果被占用返回true，否则返回false；\n     * @throws SSHException\n     */\n    public static boolean isPortUsed(String ip, int port) throws SSHException {\n        /**\n         * 执行ps命令，查看端口，以确认刚才执行的shell命令是否成功，返回一般是这样的：\n         *  root     12510 12368  0 14:34 pts/0    00:00:00 redis-server *:6379\n         */\n        String psCmd = \"/bin/ps -ef | grep %s | grep -v grep\";\n        psCmd = String.format(psCmd, port);\n        String psResponse = execute(ip, psCmd);\n        boolean isUsed = false;\n\n        if (StringUtils.isNotBlank(psResponse)) {\n            String[] resultArr = psResponse.split(System.lineSeparator());\n            for (String resultLine : resultArr) {\n                if (resultLine.contains(String.valueOf(port))) {\n                    isUsed = true;\n                    break;\n                }\n            }\n        }\n        return isUsed;\n    }\n\n    /**\n     * 通过ip来判断ssh端口\n     *\n     * @param ip\n     * @return\n     */\n    public static int getSshPort(String ip) {\n        /**\n         * 如果ssh默认端口不是22,请自行实现该逻辑\n         */\n        return ConstUtils.SSH_PORT_DEFAULT;\n    }\n\n    /**\n     * 匹配字符串中的数字\n     *\n     * @param content\n     * @return\n     */\n    private static String matchMemLineNumber(String content) {\n        String result = EMPTY_STRING;\n        if (content == null || EMPTY_STRING.equals(content.trim())) {\n            return result;\n        }\n        Pattern pattern = Pattern.compile(\"(\\\\d+)\");\n        Matcher matcher = pattern.matcher(content);\n        if (matcher.find()) {\n            result = matcher.group(1);\n        }\n        return result;\n    }\n\n    /**\n     * 从top的cpuLine解析出us\n     *\n     * @param cpuLine\n     * @return\n     */\n    public static double getUsCpu(String cpuLine) {\n        if (cpuLine == null || EMPTY_STRING.equals(cpuLine.trim())) {\n            return 0;\n        }\n        String[] items = cpuLine.split(COMMA);\n        if (items.length < 1) {\n            return 0;\n        }\n        String usCpuStr = items[0];\n        return NumberUtils.toDouble(matchCpuLine(usCpuStr));\n    }\n\n    private static String matchCpuLine(String content) {\n        String result = EMPTY_STRING;\n        if (content == null || EMPTY_STRING.equals(content.trim())) {\n            return result;\n        }\n        Pattern pattern = Pattern.compile(\"(\\\\d+).(\\\\d+)\");\n        Matcher matcher = pattern.matcher(content);\n        if (matcher.find()) {\n            result = matcher.group();\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/admin/CoreAppsStatCenter.java",
    "content": "package com.sohu.cache.stats.admin;\n\n/**\n * Created by rucao on 2020/3/25\n */\npublic interface CoreAppsStatCenter {\n    boolean sendExpAppsStatDataEmail(String searchDate);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/admin/impl/CoreAppsStatCenterImpl.java",
    "content": "package com.sohu.cache.stats.admin.impl;\n\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.alert.utils.AlertUtils;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.ResourceDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.stats.admin.CoreAppsStatCenter;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.util.FreemakerUtils;\nimport freemarker.template.Configuration;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * Created by rucao on 2020/3/25\n */\n@Service(\"coreAppsStatCenter\")\n@Slf4j\npublic class CoreAppsStatCenterImpl implements CoreAppsStatCenter {\n\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private UserService userService;\n    @Autowired\n    private ResourceDao resourceDao;\n    @Autowired\n    private EmailComponent emailComponent;\n    @Autowired\n    private Configuration configuration;\n    @Autowired\n    private MachineCenter machineCenter;\n\n    @Override\n    public boolean sendExpAppsStatDataEmail(String searchDate) {\n        try {\n            log.info(\"sendExpAppsStatDataEmail\");\n            if (StringUtil.isBlank(searchDate)) {\n                //默认发送前一天的日报\n                Date startDate = DateUtils.addDays(new Date(), -1);\n                searchDate = DateUtil.formatDate(startDate, \"yyyy-MM-dd\");\n            }\n            List<AppDesc> appDescList = appDao.getOnlineAppsNonTest();\n            appDescList.forEach(appDesc -> {\n                String versionName = Optional.ofNullable(resourceDao.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n                appDesc.setVersionName(versionName);\n                appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));\n            });\n            Map<String, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(appDesc -> String.valueOf(appDesc.getAppId()), Function.identity()));\n\n\n            Map<String, List<Map<String, Object>>> appClientGatherStatGroup = appService.getFilterAppClientStatGather(-1, searchDate);\n\n            // 获取异常的宿主或容器信息\n            Map<String, Object> exceptionMachineEnv = machineCenter.getExceptionMachineEnv(DateUtils.addDays(new Date(), -1));\n\n            noticeExpAppsDaily(searchDate, appDescMap, appClientGatherStatGroup,exceptionMachineEnv);\n            return true;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    public void noticeExpAppsDaily(String searchDate, Map<String, AppDesc> appDescMap, Map<String, List<Map<String, Object>>> appClientGatherStatGroup,Map<String,Object> exceptionMachineEnv) {\n        String title = String.format(\"【CacheCloud】%s应用日报\", searchDate);\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"appDescMap\", appDescMap);\n        context.put(\"appClientGatherStatGroup\", appClientGatherStatGroup);\n        context.put(\"exceptionMachineEnv\", exceptionMachineEnv);\n        context.put(\"searchDate\", searchDate);\n        String mailContent = FreemakerUtils.createText(\"expAppsDaily.ftl\", configuration, context);\n        log.info(\"noticeExpAppsDaily sendMailToAdmin, title:{}, mailContent:{}\", title, mailContent);\n        // 发送管理员\n        emailComponent.sendDailyMail(title, mailContent, Arrays.asList(AlertUtils.EMAILS.split(\",\")), null);\n        log.info(\"noticeExpAppsDaily success\");\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/AppDailyDataCenter.java",
    "content": "package com.sohu.cache.stats.app;\r\n\r\nimport java.util.Date;\r\n\r\nimport com.sohu.cache.entity.AppDailyData;\r\n\r\n\r\n/**\r\n * 应用日数据统计\r\n * @author leifu\r\n * @Date 2016年8月10日\r\n * @Time 下午5:11:03\r\n */\r\npublic interface AppDailyDataCenter {\r\n\r\n    /**\r\n     * 发送所有应用日报\r\n     */\r\n    int sendAppDailyEmail();\r\n\r\n    /**\r\n     * 发送单个应用日报\r\n     */\r\n    boolean sendAppDailyEmail(long appId, Date startDate, Date endDate);\r\n    \r\n    /**\r\n     * 获取单天应用日报\r\n     */\r\n    AppDailyData getAppDailyData(long appId, Date date);\r\n    \r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/AppDataMigrateCenter.java",
    "content": "package com.sohu.cache.stats.app;\n\nimport com.sohu.cache.constant.RedisMigrateToolConstant;\nimport com.sohu.cache.entity.AppDataMigrateSearch;\nimport com.sohu.cache.entity.AppDataMigrateStatus;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/10/24\n */\npublic interface AppDataMigrateCenter {\n    /**\n     * 查看日志\n     *\n     * @param id\n     * @param pageSize\n     * @return\n     */\n    String showDataMigrateLog(long id, int pageSize);\n\n    /**\n     * 查看校验日志\n     *\n     * @param id\n     * @param pageSize\n     * @return\n     */\n    String showCheckDataLog(long id, int pageSize);\n\n    /**\n     * 查看配置\n     *\n     * @param id\n     * @return\n     */\n    String showDataMigrateConf(long id);\n\n    /**\n     * 查询迁移工具的实时状态\n     *\n     * @param id\n     * @return\n     */\n    Map<RedisMigrateToolConstant, Map<String, Object>> showMiragteToolProcess(long id);\n\n    /**\n     * 搜索列表\n     *\n     * @param appDataMigrateSearch\n     * @return\n     */\n    List<AppDataMigrateStatus> search(AppDataMigrateSearch appDataMigrateSearch);\n\n    int getMigrateTaskCount(AppDataMigrateSearch appDataMigrateSearch);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/AppDeployCenter.java",
    "content": "package com.sohu.cache.stats.app;\n\nimport com.sohu.cache.constant.DataFormatCheckResult;\nimport com.sohu.cache.constant.HorizontalResult;\nimport com.sohu.cache.entity.*;\n\nimport java.util.List;\n\n/**\n * app相关发布操作\n * Created by yijunzhang on 14-10-20.\n */\npublic interface AppDeployCenter {\n\n    /**\n     * 新建应用\n     *\n     * @param appDesc\n     * @param appUser\n     * @param memSize\n     */\n    public boolean createApp(AppDesc appDesc, AppUser appUser, String memSize);\n\n    /**\n     * 为应用分配资源\n     *\n     * @param appAuditId\n     * @param nodeInfoList <br/>格式=masterIp:空间:slaveIp\n     * @param auditUser\n     * @return\n     */\n    public boolean allocateResourceApp(Long appAuditId, List<String> nodeInfoList, AppUser auditUser);\n\n\n    /**\n     * 为API应用分配的资源格式检测\n     *\n     * @param appDeployText\n     * @return\n     */\n    public DataFormatCheckResult checkAppDeployDetail4Api(AppInfoApi appInfoApi, String appDeployText, RedisVersion redisVersion);\n\n    /**\n     * 下线应用\n     *\n     * @param appId\n     * @return\n     */\n    public long offLineApp(Long appId, AppUser userInfo, Long auditId);\n\n    /**\n     * 修改应用下节点配置\n     *\n     * @param appId\n     * @param appAuditId\n     * @param key\n     * @param value\n     * @return\n     */\n    public boolean modifyAppConfig(Long appId, Long appAuditId, String key, String value);\n\n    /**\n     * 垂直扩展\n     *\n     * @param appId\n     * @param appAuditId\n     * @param memory     单位MB\n     * @return\n     */\n    public boolean verticalExpansion(Long appId, Long appAuditId, long operateId, int memory);\n\n    /**\n     * 检测水平扩容节点\n     *\n     * @param appAuditId\n     * @param masterSizeSlave\n     * @return\n     */\n    public DataFormatCheckResult checkHorizontalNodes(Long appAuditId, String masterSizeSlave);\n\n    /**\n     * 检查水平扩容的格式\n     *\n     * @param appId\n     * @param appAuditId\n     * @param sourceId\n     * @param targetId\n     * @param startSlot\n     * @param endSlot\n     * @param migrateType\n     * @return\n     */\n    public HorizontalResult checkHorizontal(long appId, long appAuditId, long sourceId, long targetId, int startSlot,\n                                            int endSlot, int migrateType);\n\n\n    /**\n     * 开始水平扩容\n     *\n     * @param appId\n     * @param appAuditId\n     * @param sourceId\n     * @param targetId\n     * @param startSlot\n     * @param endSlot\n     * @param migrateType\n     * @return\n     */\n    public HorizontalResult startHorizontal(long appId, long appAuditId, long sourceId, long targetId, int startSlot,\n                                            int endSlot, int migrateType);\n\n    /**\n     * 重试水平扩容\n     *\n     * @param instanceReshardProcessId\n     * @return\n     */\n    public HorizontalResult retryHorizontal(final int instanceReshardProcessId);\n\n    /**\n     * 添加cluster一个主(从)节点\n     *\n     * @param appId\n     * @param masterHost\n     * @param slaveHost  从节点可为空\n     * @param memory\n     * @return\n     */\n    public boolean addHorizontalNodes(Long appId, String masterHost, String slaveHost, int memory);\n\n\n    /**\n     * 获取当前水平扩展进度列表\n     *\n     * @return\n     */\n    public List<InstanceReshardProcess> getHorizontalProcess(long auditId);\n\n\n    /**\n     * 清理应用数据\n     *\n     * @param appId\n     * @param appUser\n     * @return\n     */\n    public boolean cleanAppData(long appId, AppUser appUser);\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/AppPersistenceCheckCenter.java",
    "content": "package com.sohu.cache.stats.app;\n\n/**\n * Description: 应用持久化配置检查修正\n * @author zengyizhao\n * @version 1.0\n * @date 2022/11/3\n */\npublic interface AppPersistenceCheckCenter {\n\n    /**\n     * 发送所有应用日报\n     */\n    void checkAndFixAppPersistence();\n    \n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/AppStatsCenter.java",
    "content": "package com.sohu.cache.stats.app;\r\n\r\nimport com.sohu.cache.constant.AppTopology;\r\nimport com.sohu.cache.constant.TimeDimensionalityEnum;\r\nimport com.sohu.cache.entity.*;\r\nimport com.sohu.cache.web.vo.AppDetailVO;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * app级别的查询、统计和汇总\r\n * <p>\r\n * Created by lingguo on 14-6-26.\r\n */\r\npublic interface AppStatsCenter {\r\n\r\n    /**\r\n     * 通过时间区间查询app的分钟统计数据\r\n     *\r\n     * @param appId\r\n     * @param beginTime 时间，格式：yyyyMMddHHmm\r\n     * @param endTime   时间，格式：yyyyMMddHHmm\r\n     * @return\r\n     */\r\n    public List<AppStats> getAppStatsListByMinuteTime(final long appId, long beginTime, long endTime);\r\n\r\n    /**\r\n     * 通过时间区间查询app的分钟统计数据\r\n     *\r\n     * @param appId\r\n     * @param beginTime 时间，格式：yyyyMMddHHmm\r\n     * @param endTime   时间，格式：yyyyMMddHHmm\r\n     * @return\r\n     */\r\n    public List<AppStats> getAppStatsList(final long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum);\r\n\r\n    /**\r\n     * 通过时间区间查询app的每日日报数据\r\n     *\r\n     * @param appId\r\n     * @param beginDate 时间，格式：yyyyMMdd\r\n     * @param endDate   时间，格式：yyyyMMdd\r\n     * @return\r\n     */\r\n    public List<AppDailyData> getAppHitRatioList(final long appId, long beginDate, long endDate);\r\n\r\n    /**\r\n     * 按照分钟查询应用历史最大使用内存\r\n     * @param appId\r\n     * @param beginTime\r\n     * @param endTime\r\n     * @return\r\n     */\r\n    public Long getUsedMemoryMaxByTimeBetween(final long appId, long beginTime, long endTime);\r\n\r\n    /**\r\n     * 通过时间区间查询一条app的分钟统计数据\r\n     *\r\n     * @param appId\r\n     * @param beginTime 时间，格式：yyyyMMddHHmm\r\n     * @param endTime   时间，格式：yyyyMMddHHmm\r\n     * @return\r\n     */\r\n    AppStats getOneAppStats(long appId, long beginTime, long endTime);\r\n\r\n    /**\r\n     * 查询一天中应用的命令执行次数的top5\r\n     *\r\n     * @param appId 应用id\r\n     * @param begin 日期格式：yyyyMMddHHmm\r\n     * @param end   日期格式：yyyyMMddHHmm\r\n     * @return\r\n     */\r\n    public List<AppCommandStats> getTop5AppCommandStatsList(final long appId, long begin, long end);\r\n\r\n    /**\r\n     * 查询一天中应用的命令执行次数的top5\r\n     *\r\n     * @param appId 应用id\r\n     * @param begin 日期格式：yyyyMMddHHmm\r\n     * @param end   日期格式：yyyyMMddHHmm\r\n     * @return\r\n     */\r\n    public List<AppCommandStats> getTopLimitAppCommandStatsList(final long appId, long begin, long end, int limit);\r\n\r\n    /**\r\n     * 查询应用的配置和节点信息\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    public Map<AppTopology, Object> queryAppTopology(final long appId);\r\n\r\n\r\n    /**\r\n     * 查询应用指定时间段，指定命令名的结果集合\r\n     *\r\n     * @param appId       应用id\r\n     * @param beginTime   时间，格式：yyyyMMddHHmm\r\n     * @param endTime     时间，格式：yyyyMMddHHmm\r\n     * @param commandName 命令名\r\n     * @return\r\n     */\r\n    public List<AppCommandStats> getCommandStatsListV2(long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum, String commandName);\r\n\r\n    /**\r\n     * 查询应用指定时间段，指定命令名的结果集合\r\n     *\r\n     * @param appId       应用id\r\n     * @param beginTime   时间，格式：yyyyMMddHHmm\r\n     * @param endTime     时间，格式：yyyyMMddHHmm\r\n     * @param commandName 命令名\r\n     * @return\r\n     */\r\n    public List<AppCommandStats> getCommandStatsList(long appId, long beginTime, long endTime, String commandName);\r\n\r\n    /**\r\n     * 查询应用指定时间段，指定命令名的结果集合\r\n     *\r\n     * @param appId     应用id\r\n     * @param beginTime 时间，格式：yyyyMMddHHmm\r\n     * @param endTime   时间，格式：yyyyMMddHHmm\r\n     * @return\r\n     */\r\n    public List<AppCommandStats> getCommandStatsList(long appId, long beginTime, long endTime);\r\n\r\n    /**\r\n     * 查询应用指定时间段，指定命令名的结果集合\r\n     *\r\n     * @param appId     应用id\r\n     * @param beginTime 时间，格式：yyyyMMddHHmm\r\n     * @param endTime   时间，格式：yyyyMMddHHmm\r\n     * @return\r\n     */\r\n    public List<AppCommandStats> getCommandStatsListV2(long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum);\r\n\r\n    /**\r\n     * 查询应用指定命令的峰值\r\n     *\r\n     * @param appId       应用id\r\n     * @param beginTime   时间，格式：yyyyMMddHHmm\r\n     * @param endTime     时间，格式：yyyyMMddHHmm\r\n     * @param commandName 命令名\r\n     * @return\r\n     */\r\n    public AppCommandStats getCommandClimax(long appId, Long beginTime, Long endTime, String commandName);\r\n\r\n    /**\r\n     * 获取应用详细信息\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    public AppDetailVO getAppDetail(long appId);\r\n\r\n    /**\r\n     * 通过所有在线的应用详细信息\r\n     * @return\r\n     */\r\n    Map<Long,AppDetailVO> getOnlineAppDetails();\r\n\r\n    /**\r\n     * 获取所有在线应用的应用客户端连接总数\r\n     * @return\r\n     */\r\n    List<AppClientStatisticGather> getOnlineAppConnClients();\r\n\r\n    \r\n\r\n    /**\r\n     * 获取应用命令调用次数分布\r\n     *\r\n     * @param appId\r\n     * @param beginTime\r\n     * @param endTime\r\n     * @return\r\n     */\r\n    public List<AppCommandGroup> getAppCommandGroup(long appId, Long beginTime, Long endTime);\r\n\r\n    /**\r\n     * 获取应用命令调用总次数\r\n     *\r\n     * @param appId\r\n     * @param beginTime\r\n     * @param endTime\r\n     * @return\r\n     */\r\n    public Long getAppCommandCount(long appId, long beginTime, long endTime);\r\n\r\n    /**\r\n     * 在appId级别执行命令\r\n     *\r\n     * @param appId\r\n     * @param command\r\n     * @param userName\r\n     * @return\r\n     */\r\n    public String executeCommand(long appId, String command, String userName);\r\n\r\n\r\n    /**\r\n     * 按照appId获取实例所有慢查询日志\r\n     *\r\n     * @param appId\r\n     * @param startDate\r\n     * @param endDate\r\n     * @return\r\n     */\r\n    public List<InstanceSlowLog> getInstanceSlowLogByAppId(long appId, Date startDate, Date endDate);\r\n\r\n\r\n    Map<String, List<Map<String, Object>>> getAppLatencyStats(long appId, long startTime, long endTime);\r\n\r\n    Map<String, Long> getAppLatencyStatsGroupByInstance(long appId, long startTime, long endTime);\r\n\r\n    Map<String, List<Map<String, Object>>> getAppLatencyInfo(long appId, long startTime, long endTime);\r\n\r\n    List<InstanceSlowLog> getByInstanceExecuteTime(long instanceId, String executeDate);\r\n\r\n    /**\r\n     * 按照appId获取每个实例慢查询个数\r\n     *\r\n     * @param appId\r\n     * @param startDate\r\n     * @param endDate\r\n     * @return\r\n     */\r\n    public Map<String, Long> getInstanceSlowLogCountMapByAppId(Long appId, Date startDate, Date endDate);\r\n\r\n    /**\r\n     * <p>\r\n     * Description:获取总览相关统计数\r\n     * </p>\r\n     *\r\n     * @param\r\n     * @return\r\n     * @author chenshi\r\n     * @version 1.0\r\n     * @date 2017/8/14\r\n     */\r\n    public Map<String, Object> getAppTotalStat();\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/ImportAppCenter.java",
    "content": "package com.sohu.cache.stats.app;\r\n\r\nimport com.sohu.cache.constant.ImportAppResult;\r\nimport com.sohu.cache.entity.AppDesc;\r\n\r\n/**\r\n * 导入应用\r\n *\r\n * @author leifu\r\n * @Date 2016-4-16\r\n * @Time 下午3:42:49\r\n */\r\npublic interface ImportAppCenter {\r\n\r\n    /**\r\n     * 检查应用和实例\r\n     *\r\n     * @param appInstanceInfo\r\n     * @return\r\n     */\r\n    ImportAppResult check(int type, String appInstanceInfo, String password);\r\n\r\n    /**\r\n     * 导入应用和相关实例\r\n     *\r\n     * @param appDesc\r\n     * @param appInstanceInfo\r\n     * @return\r\n     */\r\n    boolean importAppAndInstance(AppDesc appDesc, String appInstanceInfo);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisMigrateToolCenter.java",
    "content": "package com.sohu.cache.stats.app;\n\nimport com.sohu.cache.constant.AppDataMigrateEnum;\nimport com.sohu.cache.constant.AppDataMigrateResult;\nimport com.sohu.cache.constant.CommandResult;\nimport com.sohu.cache.entity.AppDataMigrateStatus;\nimport com.sohu.cache.entity.SystemResource;\n\n/**\n * 数据迁移\n *\n * @author leifu\n * @Date 2016-6-8\n * @Time 下午2:54:33\n */\npublic interface RedisMigrateToolCenter {\n\n    /**\n     * 检查配置\n     *\n     * @param migrateMachineIp\n     * @param sourceRedisMigrateEnum\n     * @param sourceServers\n     * @param targetRedisMigrateEnum\n     * @param targetServers\n     * @param redisSourcePass\n     * @param redisTargetPass\n     * @return\n     */\n    AppDataMigrateResult check(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                               AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, String redisSourcePass, String redisTargetPass,SystemResource resource);\n\n    /**\n     * 开始迁移\n     *\n     * @param migrateMachineIp\n     * @param sourceRedisMigrateEnum\n     * @param sourceServers\n     * @param targetRedisMigrateEnum\n     * @param targetServers\n     * @param redisSourcePass\n     * @param targetSourcePass\n     * @return\n     */\n    AppDataMigrateStatus migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                                 AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, long sourceAppId, long targetAppId, String redisSourcePass, String targetSourcePass, long userId, SystemResource resource);\n\n    /**\n     * 比较源和目标的样本数据\n     *\n     * @param id\n     * @param nums\n     * @return\n     */\n    CommandResult sampleCheckData(long id, int nums);\n\n    /**\n     * 关闭迁移\n     *\n     * @param id\n     * @return\n     */\n    AppDataMigrateResult stopMigrate(long id);\n\n    /**\n     * @param appId\n     * @return\n     */\n    String getAppInstanceListForRedisMigrateTool(long appId);\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisShakeCenter.java",
    "content": "package com.sohu.cache.stats.app;\n\nimport com.sohu.cache.constant.AppDataMigrateEnum;\nimport com.sohu.cache.constant.AppDataMigrateResult;\nimport com.sohu.cache.constant.CommandResult;\nimport com.sohu.cache.entity.AppDataMigrateStatus;\nimport com.sohu.cache.entity.SystemResource;\n\n/**\n * 数据迁移工具 redis-shake\n * Created by rucao on 2019/10/23\n */\npublic interface RedisShakeCenter {\n    /**\n     * 检查配置\n     *\n     * @param migrateMachineIp\n     * @param sourceRedisMigrateEnum\n     * @param sourceServers\n     * @param targetRedisMigrateEnum\n     * @param targetServers\n     * @param redisSourcePass\n     * @param redisTargetPass\n     * @return\n     */\n    AppDataMigrateResult check(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                               AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, String redisSourcePass, String redisTargetPass,SystemResource resource);\n\n    /**\n     * 开始迁移\n     *\n     * @param migrateMachineIp\n     * @param sourceRedisMigrateEnum\n     * @param sourceServers\n     * @param targetRedisMigrateEnum\n     * @param targetServers\n     * @param sourceAppId\n     * @param targetAppId\n     * @param redisSourcePass\n     * @param redisTargetPass\n     * @param userId\n     * @return\n     */\n    AppDataMigrateStatus migrate(String migrateMachineIp, int source_rdb_parallel, int parallel,\n                                 AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                                 AppDataMigrateEnum targetRedisMigrateEnum, String targetServers,\n                                 long sourceAppId, long targetAppId,\n                                 String redisSourcePass, String redisTargetPass,\n                                 String redisSourceVersion, String redisTargetVersion,\n                                 long userId, SystemResource resource);\n\n    /**\n     * 关闭迁移\n     *\n     * @param id\n     * @return\n     */\n    AppDataMigrateResult stopMigrate(long id);\n\n    /**\n     * 比较源和目标的样本数据\n     *\n     * @param id\n     * @param batchcount\n     * @return\n     */\n    CommandResult checkData(long id, int batchcount, int comparemode);\n\n    /**\n     * @param appId\n     * @return\n     */\n    String getAppInstanceListForRedisShake(long appId);\n\n    /**\n     * 显示迁移进度\n     *\n     * @param id\n     * @return\n     */\n    String showProcess(long id);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppDailyDataCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\n\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.alert.utils.AlertUtils;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.AppDailyData;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.stats.app.AppDailyDataCenter;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.util.FreemakerUtils;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport freemarker.template.Configuration;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\n\nimport javax.annotation.Resource;\nimport java.text.DecimalFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.Map.Entry;\n\n/**\n * 应用日报\n *\n * @author leifu\n * @Date 2016年8月10日\n * @Time 下午5:17:02\n */\n@Service(\"appDailyDataCenter\")\npublic class AppDailyDataCenterImpl implements AppDailyDataCenter {\n\n    private final static int STAT_ERROR = 0;\n    @Resource\n    UserService userService;\n    private Logger logger = LoggerFactory.getLogger(AppDailyDataCenterImpl.class);\n    @Autowired\n    private EmailComponent emailComponent;\n    @Autowired\n    private AppStatsCenter appStatsCenter;\n    @Autowired\n    private Configuration configuration;\n    @Autowired\n    private InstanceSlowLogDao instanceSlowLogDao;\n    @Autowired\n    private AppClientExceptionStatDao appClientExceptionStatDao;\n    @Autowired\n    private AppStatsDao appStatsDao;\n    @Autowired\n    private AppDailyDao appDailyDao;\n    @Autowired\n    private AppService appService;\n\n    @Override\n    public int sendAppDailyEmail() {\n        Date endDate = new Date();\n        Date startDate = DateUtils.addDays(endDate, -1);\n        int successCount = 0;\n        List<AppDesc> appDescList = appService.getAllAppDesc();\n        for (AppDesc appDesc : appDescList) {\n            try {\n                boolean result = sendAppDailyEmail(appDesc.getAppId(), startDate, endDate);\n                if (result) {\n                    successCount++;\n                }\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        return successCount;\n    }\n\n    @Override\n    public boolean sendAppDailyEmail(long appId, Date startDate, Date endDate) {\n        try {\n            AppDailyData appDailyData = generateAppDaily(appId, startDate, endDate);\n            if (appDailyData == null) {\n                return false;\n            }\n            fillAppDailyData(appDailyData);\n            //保存每天的日报，后期查询和分析\n            appDailyDao.save(appDailyData);\n            AppDetailVO appDetailVO = appDailyData.getAppDetailVO();\n            noticeAppDaily(startDate, appDetailVO, appDailyData);\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    /**\n     * 填充信息\n     *\n     * @param appDailyData\n     */\n    private void fillAppDailyData(AppDailyData appDailyData) {\n        appDailyData.setAppId(appDailyData.getAppDetailVO().getAppDesc().getAppId());\n        appDailyData.setDate(appDailyData.getStartDate());\n        Map<String, Long> valueSizeDistributeCountMap = appDailyData.getValueSizeDistributeCountMap();\n        //@TODO 暂时不计数\n        long bigKeyTimes = 0;\n        StringBuffer bigKeyInfo = new StringBuffer();\n        for (Entry<String, Long> entry : valueSizeDistributeCountMap.entrySet()) {\n            String key = entry.getKey();\n            long times = entry.getValue();\n            bigKeyInfo.append(key + \":\" + times + \"\\n\");\n        }\n        appDailyData.setBigKeyInfo(bigKeyInfo.toString());\n        appDailyData.setBigKeyTimes(bigKeyTimes);\n    }\n\n    public AppDailyData generateAppDaily(long appId, Date startDate, Date endDate) {\n        Assert.isTrue(appId > 0L);\n        AppDetailVO appDetailVO = appStatsCenter.getAppDetail(appId);\n        if (appDetailVO == null) {\n            logger.error(\"appId={} not exist\", appId);\n            return null;\n        }\n        AppDesc appDesc = appDetailVO.getAppDesc();\n        appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));\n        if (appDesc.isOffline()) {\n            return null;\n        }\n        if (appDesc.isTestOk()) {\n            return null;\n        }\n        AppDailyData appDailyData = new AppDailyData();\n        appDailyData.setStartDate(startDate);\n        appDailyData.setEndDate(endDate);\n\n        // 应用详情\n        appDailyData.setAppDetailVO(appDetailVO);\n\n        // 慢查询\n        int slowLogCount = getSlowLogCount(appId, startDate, endDate);\n        appDailyData.setSlowLogCount(slowLogCount);\n\n        String searchDate = DateUtil.formatDate(startDate, \"yyyy-MM-dd\");\n        Map<Long, Map<String, Object>> appClientGatherStatMap = appService.getAppClientStatGather(appId, searchDate);\n        Map<String, Object> appClientGatherStat = MapUtils.getMap(appClientGatherStatMap, appId);\n        if (MapUtils.isNotEmpty(appClientGatherStat)) {\n            appDailyData.setClientCmdCount(MapUtils.getLongValue(appClientGatherStat, \"cmd_count\", -0l));\n            appDailyData.setClientAvgCmdCost(MapUtils.getDoubleValue(appClientGatherStat, \"avg_cmd_cost\", -0));\n            appDailyData.setClientConnExpCount(MapUtils.getLongValue(appClientGatherStat, \"conn_exp_count\", -0l));\n            appDailyData.setClientAvgConnExpCost(MapUtils.getDoubleValue(appClientGatherStat, \"avg_conn_exp_cost\", -0));\n            appDailyData.setClientCmdExpCount(MapUtils.getLongValue(appClientGatherStat, \"cmd_exp_count\", -0l));\n            appDailyData.setClientAvgCmdExpCost(MapUtils.getDoubleValue(appClientGatherStat, \"avg_cmd_exp_cost\", -0));\n            appDailyData.setLatencyCount(MapUtils.getLongValue(appClientGatherStat, \"latency_count\", -0l));\n        }\n\n        // 客户端值分布\n        Map<String, Long> valueSizeDistributeCountMap = new HashMap<>();\n        appDailyData.setValueSizeDistributeCountMap(valueSizeDistributeCountMap);\n\n\n        // 应用相关统计\n        Map<String, Object> appMinuteStatMap = getAppMinuteStat(appId, startDate, endDate);\n        appDailyData.setMaxMinuteClientCount(MapUtils.getIntValue(appMinuteStatMap, \"maxClientCount\"));\n        appDailyData.setAvgMinuteClientCount(MapUtils.getIntValue(appMinuteStatMap, \"avgClientCount\"));\n        appDailyData.setMaxMinuteCommandCount(MapUtils.getIntValue(appMinuteStatMap, \"maxCommandCount\"));\n        appDailyData.setAvgMinuteCommandCount(MapUtils.getIntValue(appMinuteStatMap, \"avgCommandCount\"));\n        appDailyData.setMaxMinuteHitRatio(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, \"maxHitRatio\") * 100.0));\n        appDailyData.setMinMinuteHitRatio(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, \"minHitRatio\") * 100.0));\n        appDailyData.setAvgHitRatio(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, \"avgHitRatio\") * 100.0));\n        appDailyData.setAvgUsedMemory(MapUtils.getLongValue(appMinuteStatMap, \"avgUsedMemory\") / 1024 / 1024);\n        appDailyData.setMaxUsedMemory(MapUtils.getLongValue(appMinuteStatMap, \"maxUsedMemory\") / 1024 / 1024);\n        appDailyData.setAvgUsedDisk(MapUtils.getLongValue(appMinuteStatMap, \"avgUsedDisk\") / 1024 / 1024);\n        appDailyData.setMaxUsedDisk(MapUtils.getLongValue(appMinuteStatMap, \"maxUsedDisk\") / 1024 / 1024);\n        appDailyData.setExpiredKeysCount(MapUtils.getIntValue(appMinuteStatMap, \"expiredKeys\"));\n        appDailyData.setEvictedKeysCount(MapUtils.getIntValue(appMinuteStatMap, \"evictedKeys\"));\n        appDailyData.setAvgMinuteNetOutputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, \"avgNetOutputByte\") / 1024.0 / 1024.0));\n        appDailyData.setMaxMinuteNetOutputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, \"maxNetOutputByte\") / 1024.0 / 1024.0));\n        appDailyData.setAvgMinuteNetInputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, \"avgNetInputByte\") / 1024.0 / 1024.0));\n        appDailyData.setMaxMinuteNetInputByte(remainNumberTwoPoint(MapUtils.getDoubleValue(appMinuteStatMap, \"maxNetInputByte\") / 1024.0 / 1024.0));\n        appDailyData.setAvgObjectSize(MapUtils.getIntValue(appMinuteStatMap, \"avgObjectSize\"));\n        appDailyData.setMaxObjectSize(MapUtils.getIntValue(appMinuteStatMap, \"maxObjectSize\"));\n\n        return appDailyData;\n    }\n\n    /**\n     * 保留两位\n     *\n     * @param num\n     * @return\n     */\n    private double remainNumberTwoPoint(double num) {\n        DecimalFormat df = new DecimalFormat(\"0.00\");\n        return NumberUtils.toDouble(df.format(num));\n    }\n\n    /**\n     * 获取客户端连接数统计\n     *\n     * @param appId\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    private Map<String, Object> getAppMinuteStat(long appId, Date startDate, Date endDate) {\n        try {\n            String COLLECT_TIME_FORMAT = \"yyyyMMddHHmm\";\n            long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n            long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));\n            return appStatsDao.getAppMinuteStat(appId, startTime, endTime);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n    /**\n     * 获取应用在指定日期内慢查询次数\n     *\n     * @param appId\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    private int getSlowLogCount(long appId, Date startDate, Date endDate) {\n        try {\n            return instanceSlowLogDao.getAppSlowLogCount(appId, startDate, endDate);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return STAT_ERROR;\n        }\n    }\n\n    /**\n     * 日报通知\n     *\n     * @param startDate\n     * @param appDetailVO\n     * @param appDailyData\n     */\n    public void noticeAppDaily(Date startDate, AppDetailVO appDetailVO, AppDailyData appDailyData) {\n        List<String> ccEmailList = getCCEmailList(appDetailVO.getAppDesc());\n        String startDateFormat = DateUtil.formatYYYYMMdd(startDate);\n        String title = String.format(\"【CacheCloud】%s日报(appId=%s)\", startDateFormat, appDetailVO.getAppDesc().getAppId());\n        AppDesc appDesc = appDetailVO.getAppDesc();\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"appDesc\", appDesc);\n        context.put(\"appDailyData\", appDailyData);\n        String mailContent = FreemakerUtils.createText(\"appDaily.ftl\", configuration, context);\n        // 发送日报\n        emailComponent.sendDailyMail(title, mailContent, appDetailVO.getEmailList(), ccEmailList);\n    }\n\n\n    public void noteAppTopologyDaily(Date startDate, List<Map> topologyExamResult) {\n        String startDateFormat = DateUtil.formatYYYYMMdd(startDate);\n        String title = String.format(\"【CacheCloud】应用拓扑结构检查结果%s日报\", startDateFormat);\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"examResult\", topologyExamResult);\n        String mailContent = FreemakerUtils.createText(\"topologyExam.ftl\", configuration, context);\n        emailComponent.sendDailyMail(title, mailContent, Arrays.asList(AlertUtils.EMAILS.split(\",\")), null);\n    }\n\n    /**\n     * A级以上抄送管理员，S级抄送领导\n     *\n     * @param appDesc\n     * @return\n     */\n    private List<String> getCCEmailList(AppDesc appDesc) {\n        Set<String> ccEmailSet = new LinkedHashSet<String>();\n        //A级\n        if (appDesc.isVeryImportant()) {\n            for (String email : emailComponent.getAdminEmail().split(ConstUtils.COMMA)) {\n                ccEmailSet.add(email);\n            }\n        }\n        //S级\n        if (appDesc.isSuperImportant()) {\n            ccEmailSet.addAll(ConstUtils.LEADER_EMAIL_LIST);\n        }\n        return new ArrayList<String>(ccEmailSet);\n    }\n\n    @Override\n    public AppDailyData getAppDailyData(long appId, Date date) {\n        try {\n            return appDailyDao.getAppDaily(appId, new SimpleDateFormat(\"yyyy-MM-dd\").format(date));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppDataMigrateCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\n\nimport com.sohu.cache.constant.AppDataMigrateStatusEnum;\nimport com.sohu.cache.constant.MachineInfoEnum;\nimport com.sohu.cache.constant.RedisMigrateToolConstant;\nimport com.sohu.cache.constant.RedisShakeEnum;\nimport com.sohu.cache.dao.AppDataMigrateStatusDao;\nimport com.sohu.cache.entity.AppDataMigrateSearch;\nimport com.sohu.cache.entity.AppDataMigrateStatus;\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.stats.app.AppDataMigrateCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport redis.clients.jedis.Jedis;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Created by rucao on 2019/10/24[]\n */\n@Slf4j\n@Service\npublic class AppDataMigrateCenterImpl implements AppDataMigrateCenter {\n    @Autowired\n    private AppDataMigrateStatusDao appDataMigrateStatusDao;\n    @Autowired\n    private MachineCenter machineCenter;\n\n    @Override\n    public String showDataMigrateLog(long id, int pageSize) {\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return \"\";\n        }\n\n        try {\n            int migrateStatus = appDataMigrateStatus.getStatus();\n            String logPath = appDataMigrateStatus.getLogPath();\n            String host = appDataMigrateStatus.getMigrateMachineIp();\n\n            MachineInfo machineInfo = machineCenter.getMachineInfoByIp(host);\n            if (machineInfo != null && machineInfo.getAvailable() == MachineInfoEnum.AvailableEnum.YES.getValue()) {\n                StringBuilder command = new StringBuilder();\n                command.append(\"tail -n\").append(pageSize).append(\" \").append(logPath);\n\n                String result = SSHUtil.execute(host, command.toString());\n                int logStatus = migrateStatus;\n                if (StringUtils.isNotEmpty(result) && result.contains(RedisShakeEnum.LOG_ERROR.getKeyword())) {\n                    logStatus = AppDataMigrateStatusEnum.ERROR.getStatus();\n                } else if (StringUtils.isNotEmpty(result) && (result.contains(RedisShakeEnum.LOG_SYNC_RDB_DONE.getKeyword()) || result.contains(RedisShakeEnum.LOG_FORWARD_COMMANDS.getKeyword()))) {\n                    logStatus = AppDataMigrateStatusEnum.FULL_END.getStatus();\n                } else if (StringUtils.isNotEmpty(result) && result.contains(RedisShakeEnum.LOG_SYNCING.getKeyword())) {\n                    logStatus = AppDataMigrateStatusEnum.START.getStatus();\n                } else if (StringUtils.isNotEmpty(result) && result.contains(RedisShakeEnum.LOG_WAITING_SOURCE_RDB.getKeyword())) {\n                    logStatus = AppDataMigrateStatusEnum.PREPARE.getStatus();\n                }\n                if (migrateStatus != logStatus) {\n                    appDataMigrateStatusDao.updateStatus(id, logStatus);\n                }\n                return result;\n            } else {\n                log.warn(\"machine ip:{} is offline\", host);\n                return \"\";\n            }\n        } catch (SSHException e) {\n            log.error(e.getMessage(), e);\n            return \"\";\n        }\n    }\n\n    @Override\n    public String showCheckDataLog(long id, int pageSize) {\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return \"\";\n        }\n        String migrateId = appDataMigrateStatus.getMigrateId();\n        String logPath = ConstUtils.getRedisFullCheckResultDir() + \"log-\" + migrateId + \".log\";\n        String host = appDataMigrateStatus.getMigrateMachineIp();\n        StringBuilder command = new StringBuilder();\n        command.append(\"tail -n\").append(pageSize).append(\" \").append(logPath);\n        try {\n            return SSHUtil.execute(host, command.toString());\n        } catch (SSHException e) {\n            log.error(e.getMessage(), e);\n            return \"\";\n        }\n    }\n\n    @Override\n    public String showDataMigrateConf(long id) {\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return \"\";\n        }\n        String configPath = appDataMigrateStatus.getConfigPath();\n        String host = appDataMigrateStatus.getMigrateMachineIp();\n        String command = \"cat \" + configPath;\n        try {\n            return SSHUtil.execute(host, command);\n        } catch (SSHException e) {\n            log.error(e.getMessage(), e);\n            return \"\";\n        }\n    }\n\n    @Override\n    public Map<RedisMigrateToolConstant, Map<String, Object>> showMiragteToolProcess(long id) {\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return Collections.emptyMap();\n        }\n        String info = \"\";\n        String host = appDataMigrateStatus.getMigrateMachineIp();\n        int port = appDataMigrateStatus.getMigrateMachinePort();\n        Jedis jedis = null;\n        try {\n            jedis = new Jedis(host, port, 5000);\n            info = jedis.info();\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n        if (StringUtils.isBlank(info)) {\n            return Collections.emptyMap();\n        }\n        return processRedisMigrateToolStats(info);\n    }\n\n    @Override\n    public List<AppDataMigrateStatus> search(AppDataMigrateSearch appDataMigrateSearch) {\n        try {\n//            List<Long> onMigrateIds = appDataMigrateStatusDao.getAllOnMigrateId();\n//            onMigrateIds.parallelStream().map(migrateId -> showDataMigrateLog(migrateId, 100)).collect(Collectors.toList());\n            return appDataMigrateStatusDao.search(appDataMigrateSearch);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public int getMigrateTaskCount(AppDataMigrateSearch appDataMigrateSearch) {\n        try {\n            return appDataMigrateStatusDao.getMigrateTaskCount(appDataMigrateSearch);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return 0;\n        }\n    }\n\n    /**\n     * 处理迁移工具状态\n     *\n     * @param statResult\n     * @return\n     */\n    private Map<RedisMigrateToolConstant, Map<String, Object>> processRedisMigrateToolStats(String statResult) {\n        Map<RedisMigrateToolConstant, Map<String, Object>> redisStatMap = new HashMap<RedisMigrateToolConstant, Map<String, Object>>();\n        String[] data = statResult.split(\"\\r\\n\");\n        String key;\n        int i = 0;\n        int length = data.length;\n        while (i < length) {\n            if (data[i].contains(\"#\")) {\n                int index = data[i].indexOf('#');\n                key = data[i].substring(index + 1);\n                ++i;\n                RedisMigrateToolConstant redisMigrateToolConstant = RedisMigrateToolConstant.value(key.trim());\n                if (redisMigrateToolConstant == null) {\n                    continue;\n                }\n                Map<String, Object> sectionMap = new LinkedHashMap<String, Object>();\n                while (i < length && data[i].contains(\":\")) {\n                    String[] pair = data[i].split(\":\");\n                    sectionMap.put(pair[0], pair[1]);\n                    i++;\n                }\n                redisStatMap.put(redisMigrateToolConstant, sectionMap);\n            } else {\n                i++;\n            }\n        }\n        return redisStatMap;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppDeployCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\n\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.*;\nimport com.sohu.cache.stats.app.AppDeployCenter;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.util.AppEmailUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\nimport redis.clients.jedis.HostAndPort;\n\nimport java.util.*;\n\n/**\n * Created by yijunzhang on 14-10-20.\n */\n@Service(\"appDeployCenter\")\npublic class AppDeployCenterImpl implements AppDeployCenter {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private RedisDeployCenter redisDeployCenter;\n    @Autowired\n    private RedisCenter redisCenter;\n    @Autowired\n    private AppEmailUtil appEmailUtil;\n    @Autowired\n    private AppAuditDao appAuditDao;\n    @Autowired\n    private MachineCenter machineCenter;\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private AppAuditLogDao appAuditLogDao;\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private InstanceReshardProcessDao instanceReshardProcessDao;\n    @Autowired\n    private ResourceDao resourceDao;\n    @Autowired\n    private TaskService taskService;\n    @Autowired\n    private RedisConfigTemplateService redisConfigTemplateService;\n\n    @Override\n    public boolean createApp(AppDesc appDesc, AppUser appUser, String memSize) {\n        try {\n            appService.save(appDesc);\n            // 保存应用和用户的关系\n            String officers = appDesc.getOfficer();\n            if (!StringUtils.isEmpty(officers)) {\n                for (String officerId : officers.split(\",\")) {\n                    if (!StringUtils.isEmpty(officerId)) {\n                        appService.saveAppToUser(appDesc.getAppId(), Long.parseLong(officerId));\n                    }\n                }\n            }\n            // 更新appKey\n            long appId = appDesc.getAppId();\n            appService.updateAppKey(appId);\n            //处理memSize\n            memSize = memSize.replaceAll(\"\\\\s*\", \"\");\n            memSize += \"G\";\n\n            // 保存应用审批信息\n            AppAudit appAudit = new AppAudit();\n            appAudit.setAppId(appId);\n            appAudit.setUserId(appUser.getId());\n            appAudit.setUserName(appUser.getName());\n            appAudit.setCreateTime(new Date());\n            appAudit.setModifyTime(new Date());\n            appAudit.setParam1(memSize);\n            appAudit.setParam2(appDesc.getTypeDesc());\n            appAudit.setInfo(\"类型:\" + appDesc.getTypeDesc() + \";初始申请空间:\" + memSize);\n            appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n            appAudit.setType(AppAuditType.APP_AUDIT.getValue());\n            appAuditDao.insertAppAudit(appAudit);\n\n            // 发邮件\n            appEmailUtil.noticeAppResult(appDesc, appAudit);\n\n            // 保存申请日志\n            AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n                    AppAuditLogTypeEnum.APP_DESC_APPLY);\n            if (appAuditLog != null) {\n                appAuditLogDao.save(appAuditLog);\n            }\n\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    public DataFormatCheckResult checkAppDeployDetail4Api(AppInfoApi appInfoApi, String appDeployText, com.sohu.cache.entity.RedisVersion redisVersion) {\n\n        if (StringUtils.isBlank(appDeployText)) {\n            logger.error(\"appDeployText is null\");\n            return DataFormatCheckResult.fail(\"部署节点列表不能为空!\");\n        }\n        String[] nodeInfoList = appDeployText.split(ConstUtils.NEXT_LINE);\n        if (nodeInfoList == null || nodeInfoList.length == 0) {\n            logger.error(\"nodeInfoList is null\");\n            return DataFormatCheckResult.fail(\"部署节点列表不能为空!\");\n        }\n        int type = appInfoApi.getType();\n        //检查每一行\n        for (String nodeInfo : nodeInfoList) {\n            nodeInfo = StringUtils.trim(nodeInfo);\n            if (StringUtils.isBlank(nodeInfo)) {\n                return DataFormatCheckResult.fail(String.format(\"部署列表%s中存在空行!\", appDeployText));\n            }\n            String[] array = nodeInfo.split(ConstUtils.COLON);\n            if (array == null || array.length == 0) {\n                return DataFormatCheckResult.fail(String.format(\"部署列表%s中存在空行!\", appDeployText));\n            }\n            String masterHost = null;\n            String memSize = null;\n            String slaveHost = null;\n            if (TypeUtil.isRedisCluster(type)) {\n                if (array.length == 2) {\n                    masterHost = array[0];\n                    memSize = array[1];\n                } else if (array.length == 3) {\n                    masterHost = array[0];\n                    memSize = array[1];\n                    slaveHost = array[2];\n                } else {\n                    return DataFormatCheckResult.fail(String.format(\"部署列表中%s,格式错误!\", nodeInfo));\n                }\n            } else if (TypeUtil.isRedisSentinel(type)) {\n                if (array.length == 3) {\n                    masterHost = array[0];\n                    memSize = array[1];\n                    slaveHost = array[2];\n                } else if (array.length == 1) {\n                    masterHost = array[0];\n                } else {\n                    return DataFormatCheckResult.fail(String.format(\"部署列表中%s,格式错误!\", nodeInfo));\n                }\n            } else if (TypeUtil.isRedisStandalone(type)) {\n                if (array.length == 2) {\n                    masterHost = array[0];\n                    memSize = array[1];\n                } else {\n                    return DataFormatCheckResult.fail(String.format(\"部署列表中%s,格式错误!\", nodeInfo));\n                }\n            }\n            if (!checkHostExist(masterHost)) {\n                return DataFormatCheckResult.fail(String.format(\"%s中的ip=%s不存在，请在机器管理中添加!\", nodeInfo, masterHost));\n            }\n            if (StringUtils.isNotBlank(memSize) && !NumberUtils.isDigits(memSize)) {\n                return DataFormatCheckResult.fail(String.format(\"%s中的中的memSize=%s不是整数!\", nodeInfo, memSize));\n            }\n            if (StringUtils.isNotBlank(slaveHost) && !checkHostExist(slaveHost)) {\n                return DataFormatCheckResult.fail(String.format(\"%s中的ip=%s不存在，请在机器管理中添加!\", nodeInfo, slaveHost));\n            }\n            // 20180828 检查机器的redis版本是否安装\n            if (redisVersion == null) {\n                return DataFormatCheckResult.fail(String.format(\"redis版本不存在，请添加正确Redis版本!\"));\n            }\n        }\n        //检查sentinel类型:数据节点一行，sentinel节点多行\n        if (TypeUtil.isRedisSentinel(type)) {\n            return checkSentinelAppDeploy(nodeInfoList);\n            //检查单点类型:只能有一行数据节点\n        } else if (TypeUtil.isRedisStandalone(type)) {\n            return checkStandaloneAppDeploy(nodeInfoList);\n        }\n        return DataFormatCheckResult.success(\"应用部署格式正确，可以开始部署了!\");\n    }\n\n    /**\n     * 检查单点格式\n     *\n     * @param nodeInfoList\n     * @return\n     */\n    private DataFormatCheckResult checkStandaloneAppDeploy(String[] nodeInfoList) {\n        int redisLineNum = 0;\n        for (String nodeInfo : nodeInfoList) {\n            nodeInfo = StringUtils.trim(nodeInfo);\n            String[] array = nodeInfo.split(ConstUtils.COLON);\n            if (array.length == 2) {\n                redisLineNum++;\n            }\n        }\n        // redis节点只有一行\n        if (redisLineNum != 1) {\n            return DataFormatCheckResult.fail(\"应用部署格式错误, Standalone格式必须是一行masterIp:memSize(M)\");\n        }\n        return DataFormatCheckResult.success(\"应用部署格式正确，可以开始部署了!\");\n    }\n\n    /**\n     * 检查redis sentinel格式\n     *\n     * @param nodeInfoList\n     * @return\n     */\n    private DataFormatCheckResult checkSentinelAppDeploy(String[] nodeInfoList) {\n        int redisLineNum = 0;\n        int sentinelLineNum = 0;\n        for (String nodeInfo : nodeInfoList) {\n            nodeInfo = StringUtils.trim(nodeInfo);\n            String[] array = nodeInfo.split(ConstUtils.COLON);\n            if (array.length == 3) {\n                redisLineNum++;\n            } else if (array.length == 1) {\n                sentinelLineNum++;\n            }\n        }\n        // redis节点只有redisLineMustNum行\n        final int redisLineMustNum = 1;\n        if (redisLineNum < redisLineMustNum) {\n            return DataFormatCheckResult.fail(\"应用部署格式错误, Sentinel应用中必须有Redis数据节点!\");\n        } else if (redisLineNum > redisLineMustNum) {\n            return DataFormatCheckResult.fail(\"应用部署格式错误, Sentinel应用中Redis数据节点只能有一行!\");\n        }\n\n        // sentinel节点至少有sentinelLessNum个\n        final int sentinelLessNum = 3;\n        if (sentinelLineNum < sentinelLessNum) {\n            return DataFormatCheckResult.fail(\"应用部署格式错误, Sentinel应用中Sentinel节点至少要有\" + sentinelLessNum + \"个!\");\n        }\n        return DataFormatCheckResult.success(\"应用部署格式正确，可以开始部署了!\");\n    }\n\n    /**\n     * 查看host是否存在\n     *\n     * @param host\n     * @return\n     */\n    private boolean checkHostExist(String host) {\n        try {\n            MachineInfo machineInfo = machineCenter.getMachineInfoByIp(host);\n            if (machineInfo == null) {\n                return false;\n            }\n            if (machineInfo.isOffline()) {\n                logger.warn(\"host {} is offline\", host);\n                return false;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public boolean allocateResourceApp(Long appAuditId, List<String> nodeInfoList, AppUser auditUser) {\n        if (appAuditId == null || appAuditId <= 0L) {\n            logger.error(\"appAuditId is null\");\n            return false;\n        }\n        if (nodeInfoList == null || nodeInfoList.isEmpty()) {\n            logger.error(\"nodeInfoList is null\");\n            return false;\n        }\n        AppAudit appAudit = appAuditDao.getAppAudit(appAuditId);\n        if (appAudit == null) {\n            logger.error(\"appAudit:id={} is not exist\", appAuditId);\n            return false;\n        }\n        long appId = appAudit.getAppId();\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc == null) {\n            logger.error(\"appDesc:id={} is not exist\", appId);\n            return false;\n        }\n        int type = appDesc.getType();\n        List<String[]> nodes = new ArrayList<String[]>();\n        for (String nodeInfo : nodeInfoList) {\n            nodeInfo = StringUtils.trim(nodeInfo);\n            if (StringUtils.isBlank(nodeInfo)) {\n                continue;\n            }\n            String[] array = nodeInfo.split(\":\");\n            nodes.add(array);\n        }\n\n        boolean isAudited = false;\n        if (TypeUtil.isRedisType(type)) {\n            if (TypeUtil.isRedisCluster(type)) {\n                isAudited = deployCluster(appId, nodes);\n            } else if (nodes.size() > 0) {\n                if (TypeUtil.isRedisSentinel(type)) {\n                    isAudited = deploySentinel(appId, nodes);\n                } else {\n                    isAudited = deployStandalone(appId, nodes.get(0));\n                }\n            } else {\n                logger.error(\"nodeInfoList={} is error\", nodeInfoList);\n            }\n        } else {\n            logger.error(\"unknown type : {}\", type);\n            return false;\n        }\n\n        //审核通过\n        if (isAudited) {\n            // 改变审核状态\n            appAuditDao.updateAppAudit(appAudit.getId(), AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        }\n\n        return true;\n    }\n\n    @Override\n    public long offLineApp(Long appId, AppUser userInfo, Long auditId) {\n        Assert.isTrue(appId != null && appId > 0L);\n        return taskService.addOffLineAppTask(appId, auditId, 0, userInfo);\n    }\n\n    @Override\n    public boolean modifyAppConfig(Long appId, Long appAuditId, String key, String value) {\n        Assert.isTrue(appId != null && appId > 0L);\n        Assert.isTrue(appAuditId != null && appAuditId > 0L);\n        Assert.isTrue(StringUtils.isNotBlank(key));\n        Assert.isTrue(StringUtils.isNotBlank(value));\n        boolean isModify = redisDeployCenter.modifyAppConfig(appId, key, value);\n        if (isModify) {\n            // 改变审核状态\n            appAuditDao.updateAppAudit(appAuditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        }\n        return isModify;\n    }\n\n    private boolean deploySentinel(long appId, List<String[]> nodes) {\n        //数据节点\n        String[] dataNodeInfo = nodes.get(0);\n        String master = dataNodeInfo[0];\n        int memory = NumberUtils.createInteger(dataNodeInfo[1]);\n        String slave = dataNodeInfo[2];\n        // sentinel节点\n        List<String> sentinelList = new ArrayList<String>();\n        if (nodes.size() < 2) {\n            logger.error(\"sentinelList is none,don't generate sentinel app!\");\n            return false;\n        }\n\n        // sentinel节点\n        for (int i = 1; i < nodes.size(); i++) {\n            String[] nodeInfo = nodes.get(i);\n            if (nodeInfo.length == 0 || StringUtils.isBlank(nodeInfo[0])) {\n                logger.error(\"sentinel line {} may be empty\", i);\n                return false;\n            }\n            sentinelList.add(nodeInfo[0]);\n        }\n\n        return redisDeployCenter.deploySentinelInstance(appId, master, slave, memory, sentinelList);\n    }\n\n    private boolean deployCluster(long appId, List<String[]> nodes) {\n        List<RedisClusterNode> clusterNodes = new ArrayList<RedisClusterNode>();\n        int maxMemory = 0;\n        for (String[] array : nodes) {\n            String master = array[0];\n            int memory = NumberUtils.createInteger(array[1]);\n            String slave = null;\n            if (array.length > 2) {\n                slave = array[2];\n            }\n            RedisClusterNode node = new RedisClusterNode(master, slave);\n            maxMemory = memory;\n            clusterNodes.add(node);\n        }\n        return redisDeployCenter.deployClusterInstance(appId, clusterNodes, maxMemory);\n    }\n\n    private boolean deployStandalone(long appId, String[] nodeInfo) {\n        String host = nodeInfo[0];\n        int memory = NumberUtils.createInteger(nodeInfo[1]);\n        return redisDeployCenter.deployStandaloneInstance(appId, host, memory);\n    }\n\n    @Override\n    public boolean verticalExpansion(Long appId, Long appAuditId, long operateId, final int memory) {\n        Assert.isTrue(appId != null && appId > 0L);\n        Assert.isTrue(appAuditId != null && appAuditId > 0L);\n        Assert.isTrue(memory > 0);\n        AppDesc appDesc = appService.getByAppId(appId);\n        Assert.isTrue(appDesc != null);\n        int type = appDesc.getType();\n        if (!TypeUtil.isRedisType(type)) {\n            logger.error(\"appId={};type={} is not redis!\", appDesc, type);\n            return false;\n        }\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        if (instanceInfos == null || instanceInfos.isEmpty()) {\n            logger.error(\"instanceInfos is null\");\n            return false;\n        }\n        for (InstanceInfo instanceInfo : instanceInfos) {\n            int instanceType = instanceInfo.getType();\n            if (TypeUtil.isRedisSentinel(instanceType)) {\n                continue;\n            }\n            // 下线实例不做操作\n            if (instanceInfo.isOffline()) {\n                continue;\n            }\n            String host = instanceInfo.getIp();\n            int port = instanceInfo.getPort();\n\n            final long maxMemoryBytes = Long.valueOf(memory) * 1024 * 1024;\n            boolean isConfig = redisDeployCenter.modifyInstanceConfig(appId, host, port, \"maxmemory\", String.valueOf(maxMemoryBytes));\n            if (!isConfig) {\n                logger.error(\"{}:{} set maxMemory error\", host, port);\n                return false;\n            }\n            //更新instanceInfo配置\n            instanceInfo.setMem(memory);\n            instanceDao.update(instanceInfo);\n        }\n        // 改变审核状态\n        appAuditDao.updateAppAuditUser(appAuditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value(), operateId);\n        return true;\n    }\n\n    @Override\n    public boolean addHorizontalNodes(Long appId, String masterHost, String slaveHost, int memory) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        //1. 寻找主从节点的可用端口\n        Integer masterPort = machineCenter.getAvailablePort(masterHost, ConstUtils.CACHE_TYPE_REDIS_CLUSTER);\n        if (masterPort == null) {\n            logger.error(\"master host={} getAvailablePort is null\", masterHost);\n            return false;\n        }\n        Integer slavePort = 0;\n        boolean hasSlave = StringUtils.isNotBlank(slaveHost);\n        if (hasSlave) {\n            slavePort = machineCenter.getAvailablePort(slaveHost, ConstUtils.CACHE_TYPE_REDIS_CLUSTER);\n            if (slavePort == null) {\n                logger.error(\"slave host={} getAvailablePort is null\", slaveHost);\n                return false;\n            }\n        }\n\n        //2. 启动主从节点\n        boolean isMasterCreate = redisDeployCenter.createRunNode(appDesc, masterHost, masterPort, memory, true);\n        if (!isMasterCreate) {\n            logger.error(\"createRunNode master failed {}:{}\", masterHost, masterPort);\n            return false;\n        }\n        if (hasSlave) {\n            //运行节点\n            boolean isSlaveCreate = redisDeployCenter.createRunNode(appDesc, slaveHost, slavePort, memory, true);\n            if (!isSlaveCreate) {\n                logger.error(\"createRunNode slave failed {}:{}\", slaveHost, slavePort);\n                return false;\n            }\n        }\n\n        //3. 获取应用下有效节点\n        Set<HostAndPort> clusterHosts = getEffectiveInstanceList(appId);\n\n\n        //4. 添加新节点: meet,复制，不做slot分配\n//        RedisClusterReshard clusterReshard = new RedisClusterReshard(clusterHosts, redisCenter, instanceReshardProcessDao);\n        RedisClusterReshard clusterReshard = new RedisClusterReshard(clusterHosts, redisCenter, instanceReshardProcessDao, appDao, resourceDao);\n        boolean joinCluster = clusterReshard.joinCluster(appId, masterHost, masterPort, slaveHost, slavePort);\n        if (joinCluster) {\n            //5. 保存实例,开启统计功能\n            saveInstance(appId, masterHost, masterPort, memory);\n            if (hasSlave) {\n                saveInstance(appId, slaveHost, slavePort, memory);\n            }\n        }\n        return joinCluster;\n    }\n\n    @Override\n    public boolean cleanAppData(long appId, AppUser appUser) {\n        try {\n            AppDesc appDesc = appDao.getAppDescById(appId);\n            if (appDesc == null) {\n                return false;\n            }\n            if (TypeUtil.isRedisType(appDesc.getType())) {\n                return redisCenter.cleanAppData(appDesc, appUser);\n            } else {\n                return false;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    /**\n     * @param appId\n     * @param appAuditId\n     * @param startSlot\n     * @param endSlot\n     * @return\n     */\n    private boolean isInProcess(Long appId, long appAuditId, int startSlot, int endSlot) {\n        return false;\n    }\n\n\n    private InstanceInfo saveInstance(long appId, String host, int port, int maxMemory) {\n        InstanceInfo instanceInfo = new InstanceInfo();\n        instanceInfo.setAppId(appId);\n        MachineInfo machineInfo = machineCenter.getMachineInfoByIp(host);\n        instanceInfo.setHostId(machineInfo.getId());\n        instanceInfo.setConn(0);\n        instanceInfo.setMem(maxMemory);\n        instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\n        instanceInfo.setPort(port);\n        instanceInfo.setType(ConstUtils.CACHE_TYPE_REDIS_CLUSTER);\n        instanceInfo.setCmd(\"\");\n        instanceInfo.setIp(host);\n        instanceDao.saveInstance(instanceInfo);\n        return instanceInfo;\n    }\n\n    @Override\n    public HorizontalResult checkHorizontal(long appId, long appAuditId, long sourceId, long targetId, int startSlot,\n                                            int endSlot, int migrateType) {\n        boolean isInProcess = isInProcess(appId, appAuditId, startSlot, endSlot);\n        if (isInProcess) {\n            return HorizontalResult.fail(String.format(\"appId=%s %s:%s正在迁移!\", appId, startSlot, endSlot));\n        }\n        // 1.应用信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc == null) {\n            return HorizontalResult.fail(\"应用信息为空\");\n        }\n\n        // 2.0 源实例ID不能等于目标实例ID\n        if (sourceId == targetId) {\n            return HorizontalResult.fail(String.format(\"源实例ID=%s不能等于目标实例ID=%s\", sourceId, targetId));\n        }\n\n        // 2.1 源实例信息\n        InstanceInfo sourceInstanceInfo = instanceDao.getInstanceInfoById(sourceId);\n        if (sourceInstanceInfo == null) {\n            return HorizontalResult.fail(String.format(\"源实例id=%s为空\", sourceId));\n        }\n        // 2.2 对比源实例的appId是否正确\n        long sourceAppId = sourceInstanceInfo.getAppId();\n        if (sourceAppId != appId) {\n            return HorizontalResult.fail(String.format(\"源实例id=%s不属于appId=%s\", sourceId, appId));\n        }\n        // 2.3 源实例是否在线\n        boolean sourceIsRun = redisCenter.isRun(appId, sourceInstanceInfo.getIp(), sourceInstanceInfo.getPort());\n        if (!sourceIsRun) {\n            return HorizontalResult.fail(String.format(\"源实例%s必须运行中\", sourceInstanceInfo.getHostPort()));\n        }\n        // 2.4必须是master节点\n        BooleanEnum sourceIsMaster = redisCenter.isMaster(appId, sourceInstanceInfo.getIp(), sourceInstanceInfo.getPort());\n        if (sourceIsMaster != BooleanEnum.TRUE) {\n            return HorizontalResult.fail(String.format(\"源实例%s必须是主节点\", sourceInstanceInfo.getHostPort()));\n        }\n\n\n        // 3.1 目标实例信息\n        InstanceInfo targetInstanceInfo = instanceDao.getInstanceInfoById(targetId);\n        if (targetInstanceInfo == null) {\n            return HorizontalResult.fail(String.format(\"目标实例id=%s为空\", targetId));\n        }\n        // 3.2 对比目标实例的appId是否正确\n        long targetAppId = targetInstanceInfo.getAppId();\n        if (targetAppId != appId) {\n            return HorizontalResult.fail(String.format(\"目标实例id=%s不属于appId=%s\", targetId, appId));\n        }\n        // 3.3 目标实例是否在线\n        boolean targetIsRun = redisCenter.isRun(appId, targetInstanceInfo.getIp(), targetInstanceInfo.getPort());\n        if (!targetIsRun) {\n            return HorizontalResult.fail(String.format(\"目标实例%s必须运行中\", targetInstanceInfo.getHostPort()));\n        }\n        // 3.4 必须是master节点\n        BooleanEnum targetIsMaster = redisCenter.isMaster(appId, targetInstanceInfo.getIp(), targetInstanceInfo.getPort());\n        if (targetIsMaster != BooleanEnum.TRUE) {\n            return HorizontalResult.fail(String.format(\"目标实例%s必须是主节点\", targetInstanceInfo.getHostPort()));\n        }\n\n        // 4.startSlot和endSlot是否在源实例中\n        // 4.1 判断数值\n        int maxSlot = 16383;\n        if (startSlot < 0 || startSlot > maxSlot) {\n            return HorizontalResult.fail(String.format(\"startSlot=%s必须在0-%s\", startSlot, maxSlot));\n        }\n        if (endSlot < 0 || endSlot > maxSlot) {\n            return HorizontalResult.fail(String.format(\"endSlot=%s必须在0-%s\", endSlot, maxSlot));\n        }\n        if (startSlot > endSlot) {\n            return HorizontalResult.fail(\"startSlot不能大于endSlot\");\n        }\n\n        // 4.2 判断startSlot和endSlot属于sourceId\n        // 获取所有slot分布\n        Map<String, InstanceSlotModel> clusterSlotsMap = redisCenter.getClusterSlotsMap(appId);\n        if (MapUtils.isEmpty(clusterSlotsMap)) {\n            return HorizontalResult.fail(\"无法获取slot分布!\");\n        }\n        // 获取源实例负责的slot\n        String sourceHostPort = sourceInstanceInfo.getHostPort();\n        InstanceSlotModel instanceSlotModel = clusterSlotsMap.get(sourceHostPort);\n        if (instanceSlotModel == null || CollectionUtils.isEmpty(instanceSlotModel.getSlotList())) {\n            return HorizontalResult.fail(\"源实例上没有slot!\");\n        }\n        List<Integer> slotList = instanceSlotModel.getSlotList();\n        for (int i = startSlot; i <= endSlot; i++) {\n            if (!slotList.contains(i)) {\n                return HorizontalResult.fail(String.format(\"源实例没有包含尽startSlot=%s到endSlot=%s\", startSlot, endSlot));\n            }\n        }\n\n        //5.是否支持批量，版本要大于等于3.0.6\n        String sourceRedisVersion = redisCenter.getRedisVersion(sourceAppId, sourceInstanceInfo.getIp(), sourceInstanceInfo.getPort());\n        if (StringUtils.isBlank(sourceRedisVersion)) {\n            return HorizontalResult.fail(String.format(\"源实例%s版本为空\", sourceInstanceInfo.getHostPort()));\n        }\n        String targetRedisVersion = redisCenter.getRedisVersion(targetAppId, targetInstanceInfo.getIp(), targetInstanceInfo.getPort());\n        if (StringUtils.isBlank(targetRedisVersion)) {\n            return HorizontalResult.fail(String.format(\"目标实例%s版本为空\", targetInstanceInfo.getHostPort()));\n        }\n        RedisVersion sourceRedisVersionModel = getRedisVersion(sourceRedisVersion);\n        //选择了批量，但是当前版本不支持pipeline\n        if (migrateType == 1 && !sourceRedisVersionModel.isSupportPipelineMigrate()) {\n            return HorizontalResult.fail(String.format(\"源实例%s版本为%s,不支持pipeline migrate!\", sourceInstanceInfo.getHostPort(), sourceRedisVersion));\n        }\n\n        RedisVersion targetRedisVersionModel = getRedisVersion(targetRedisVersion);\n        //选择了批量，但是当前版本不支持pipeline\n        if (migrateType == 1 && !targetRedisVersionModel.isSupportPipelineMigrate()) {\n            return HorizontalResult.fail(String.format(\"目标实例%s版本为%s,不支持pipeline migrate!\", targetInstanceInfo.getHostPort(), targetRedisVersion));\n        }\n\n        return HorizontalResult.checkSuccess();\n    }\n\n    private RedisVersion getRedisVersion(String redisVersion) {\n        String[] versionArr = redisVersion.split(\"\\\\.\");\n        if (versionArr.length == 1) {\n            return new RedisVersion(NumberUtils.toInt(versionArr[0]), 0, 0);\n        } else if (versionArr.length == 2) {\n            return new RedisVersion(NumberUtils.toInt(versionArr[0]), NumberUtils.toInt(versionArr[1]), 0);\n        } else if (versionArr.length >= 3) {\n            return new RedisVersion(NumberUtils.toInt(versionArr[0]), NumberUtils.toInt(versionArr[1]),\n                    NumberUtils.toInt(versionArr[2]));\n        }\n        return null;\n    }\n\n    /**\n     * 获取应用下有效节点\n     *\n     * @param appId\n     * @return\n     */\n    private Set<HostAndPort> getEffectiveInstanceList(long appId) {\n        Set<HostAndPort> clusterHosts = new HashSet<HostAndPort>();\n        //全部节点\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        for (InstanceInfo instance : instanceInfos) {\n            if (instance.isOffline()) {\n                continue;\n            }\n            clusterHosts.add(new HostAndPort(instance.getIp(), instance.getPort()));\n        }\n        return clusterHosts;\n    }\n\n    @Override\n    public HorizontalResult startHorizontal(final long appId, final long appAuditId, long sourceId, final long targetId, final int startSlot,\n                                            final int endSlot, final int migrateType) {\n        InstanceInfo sourceInstanceInfo = instanceDao.getInstanceInfoById(sourceId);\n        InstanceInfo targetInstanceInfo = instanceDao.getInstanceInfoById(targetId);\n        InstanceReshardProcess instanceReshardProcess = saveInstanceReshardProcess(appId, appAuditId, sourceInstanceInfo, targetInstanceInfo, startSlot, endSlot, PipelineEnum.getPipelineEnum(migrateType));\n        instanceReshardProcess.setSourceInstanceInfo(sourceInstanceInfo);\n        instanceReshardProcess.setTargetInstanceInfo(targetInstanceInfo);\n        startMigrateSlot(instanceReshardProcess);\n        logger.warn(\"start reshard appId={} instance={}:{} deploy done\", instanceReshardProcess.getAppId(), targetInstanceInfo.getIp(), targetInstanceInfo.getPort());\n        return HorizontalResult.scaleSuccess();\n    }\n\n    @Override\n    public HorizontalResult retryHorizontal(final int instanceReshardProcessId) {\n        InstanceReshardProcess instanceReshardProcess = instanceReshardProcessDao.get(instanceReshardProcessId);\n        instanceReshardProcess.setStatus(ReshardStatusEnum.RUNNING.getValue());\n        instanceReshardProcessDao.updateStatus(instanceReshardProcess.getId(), ReshardStatusEnum.RUNNING.getValue());\n        InstanceInfo sourceInstanceInfo = instanceDao.getInstanceInfoById(instanceReshardProcess.getSourceInstanceId());\n        InstanceInfo targetInstanceInfo = instanceDao.getInstanceInfoById(instanceReshardProcess.getTargetInstanceId());\n        instanceReshardProcess.setSourceInstanceInfo(sourceInstanceInfo);\n        instanceReshardProcess.setTargetInstanceInfo(targetInstanceInfo);\n        startMigrateSlot(instanceReshardProcess);\n        logger.warn(\"retry reshard appId={} instance={}:{} deploy done\", instanceReshardProcess.getAppId(), targetInstanceInfo.getIp(), targetInstanceInfo.getPort());\n        return HorizontalResult.scaleSuccess();\n    }\n\n    private void startMigrateSlot(final InstanceReshardProcess instanceReshardProcess) {\n        final long appId = instanceReshardProcess.getAppId();\n        final long appAuditId = instanceReshardProcess.getAuditId();\n        final InstanceInfo targetInstanceInfo = instanceReshardProcess.getTargetInstanceInfo();\n        AsyncThreadPoolFactory.RESHARD_PROCESS_THREAD_POOL.execute(new Runnable() {\n            @Override\n            public void run() {\n                //所有节点用户clustersetslot\n                Set<HostAndPort> clusterHosts = getEffectiveInstanceList(appId);\n//                RedisClusterReshard clusterReshard = new RedisClusterReshard(clusterHosts, redisCenter, instanceReshardProcessDao);\n                RedisClusterReshard clusterReshard = new RedisClusterReshard(clusterHosts, redisCenter, instanceReshardProcessDao, appDao, resourceDao);\n                //添加进度\n                boolean joinCluster = clusterReshard.migrateSlot(instanceReshardProcess);\n                if (joinCluster) {\n                    // 改变审核状态\n                    appAuditDao.updateAppAudit(appAuditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n                    if (targetInstanceInfo != null && targetInstanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                        targetInstanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\n                        instanceDao.update(targetInstanceInfo);\n                    }\n                }\n            }\n        });\n    }\n\n    /**\n     * 保存进度\n     *\n     * @param appId\n     * @param appAuditId\n     * @param sourceInstanceInfo\n     * @param targetInstanceInfo\n     * @param startSlot\n     * @param endSlot\n     * @return\n     */\n    private InstanceReshardProcess saveInstanceReshardProcess(long appId, long appAuditId,\n                                                              InstanceInfo sourceInstanceInfo, InstanceInfo targetInstanceInfo, int startSlot, int endSlot, PipelineEnum pipelineEnum) {\n        Date now = new Date();\n        InstanceReshardProcess instanceReshardProcess = new InstanceReshardProcess();\n        instanceReshardProcess.setAppId(appId);\n        instanceReshardProcess.setAuditId(appAuditId);\n        instanceReshardProcess.setFinishSlotNum(0);\n        instanceReshardProcess.setIsPipeline(pipelineEnum.getValue());\n        instanceReshardProcess.setSourceInstanceId(sourceInstanceInfo.getId());\n        instanceReshardProcess.setTargetInstanceId(targetInstanceInfo.getId());\n        instanceReshardProcess.setMigratingSlot(startSlot);\n        instanceReshardProcess.setStartSlot(startSlot);\n        instanceReshardProcess.setEndSlot(endSlot);\n        instanceReshardProcess.setStatus(ReshardStatusEnum.RUNNING.getValue());\n        instanceReshardProcess.setStartTime(now);\n        //用status控制显示结束时间\n        instanceReshardProcess.setEndTime(now);\n        instanceReshardProcess.setCreateTime(now);\n        instanceReshardProcess.setUpdateTime(now);\n\n        instanceReshardProcessDao.save(instanceReshardProcess);\n        return instanceReshardProcess;\n    }\n\n    @Override\n    public DataFormatCheckResult checkHorizontalNodes(Long appAuditId, String masterSizeSlave) {\n        if (appAuditId == null) {\n            logger.error(\"appAuditId is null\");\n            return DataFormatCheckResult.fail(\"审核id不能为空!\");\n        }\n        if (StringUtils.isBlank(masterSizeSlave)) {\n            logger.error(\"masterSizeSlave is null\");\n            return DataFormatCheckResult.fail(\"添加节点不能为空!\");\n        }\n        AppAudit appAudit = appAuditDao.getAppAudit(appAuditId);\n        if (appAudit == null) {\n            logger.error(\"appAudit:id={} is not exist\", appAuditId);\n            return DataFormatCheckResult.fail(String.format(\"审核id=%s不存在\", appAuditId));\n        }\n        long appId = appAudit.getAppId();\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc == null) {\n            logger.error(\"appDesc:id={} is not exist\");\n            return DataFormatCheckResult.fail(String.format(\"appId=%s不存在\", appId));\n        }\n        // 多个节点\n        String[] nodes = masterSizeSlave.split(ConstUtils.NEXT_LINE);\n        for (String node : nodes) {\n            if (!StringUtils.isEmpty(node.trim())) {\n                //节点数组 master:memSize:slave\n                String[] array = node.trim().split(ConstUtils.COLON);\n                if (array == null || array.length == 0) {\n                    return DataFormatCheckResult.fail(String.format(\"添加节点%s格式错误\", masterSizeSlave));\n                }\n                //检查格式\n                String masterHost;\n                String memSize;\n                String slaveHost = null;\n                if (array.length == 2) {\n                    masterHost = array[0];\n                    memSize = array[1];\n                } else if (array.length == 3) {\n                    masterHost = array[0];\n                    memSize = array[1];\n                    slaveHost = array[2];\n                } else {\n                    return DataFormatCheckResult.fail(String.format(\"添加节点%s, 格式错误!\", masterSizeSlave));\n                }\n                //检查主节点机器是否存在\n                if (!checkHostExist(masterHost)) {\n                    return DataFormatCheckResult.fail(String.format(\"%s中的ip=%s不存在，请在机器管理中添加!\", masterSizeSlave, masterHost));\n                }\n                //检查memSize格式\n                if (StringUtils.isNotBlank(memSize) && !NumberUtils.isDigits(memSize)) {\n                    return DataFormatCheckResult.fail(String.format(\"%s中的中的memSize=%s不是整数!\", masterSizeSlave, memSize));\n                }\n                //检查从节点格式\n                if (StringUtils.isNotBlank(slaveHost) && !checkHostExist(slaveHost)) {\n                    return DataFormatCheckResult.fail(String.format(\"%s中的ip=%s不存在，请在机器管理中添加!\", masterSizeSlave, slaveHost));\n                }\n            }\n        }\n        return DataFormatCheckResult.success(\"添加节点格式正确，可以开始部署了!\");\n    }\n\n    @Override\n    public List<InstanceReshardProcess> getHorizontalProcess(long auditId) {\n        try {\n            return instanceReshardProcessDao.getByAuditId(auditId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    private static class RedisVersion {\n        int majorVersion;\n        int minorVersion;\n        int patchVersion;\n\n        public RedisVersion(int majorVersion, int minorVersion, int patchVersion) {\n            super();\n            this.majorVersion = majorVersion;\n            this.minorVersion = minorVersion;\n            this.patchVersion = patchVersion;\n        }\n\n        /**\n         * 大于等于3.0.6\n         *\n         * @return\n         */\n        public boolean isSupportPipelineMigrate() {\n            if (majorVersion < 3) {\n                return false;\n            } else if (majorVersion == 3) {\n                if (minorVersion > 0) {\n                    return true;\n                } else {\n                    return patchVersion >= 6;\n                }\n            } else {\n                return true;\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"RedisVersion [majorVersion=\" + majorVersion + \", minorVersion=\" + minorVersion + \", patchVersion=\"\n                    + patchVersion + \"]\";\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppPersistenceCheckCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.constant.AppDescEnum;\nimport com.sohu.cache.constant.AppStatusEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.stats.app.AppPersistenceCheckCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.service.AppService;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * Description: 应用持久化配置检查修正\n * @author zengyizhao\n * @version 1.0\n * @date 2022/11/3\n */\n@Slf4j\n@Service(\"appPersistenceCheckCenter\")\npublic class AppPersistenceCheckCenterImpl implements AppPersistenceCheckCenter {\n\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private EmailComponent emailComponent;\n\n    @Override\n    public void checkAndFixAppPersistence() {\n        List<AppDesc> appDescList = appService.getAllAppDesc();\n        List<AppDesc> onlineAppDescs = appDescList.stream()\n                .filter(appDesc -> AppStatusEnum.STATUS_PUBLISHED.getStatus() == appDesc.getStatus()\n                        && appDesc.getPersistenceType() != AppDescEnum.AppPersistenceType.GENERAL.getValue())\n                .collect(Collectors.toList());\n        onlineAppDescs.forEach(appDesc -> {\n            String failMessage = appService.checkAppPersistenceConfigAndFix(appDesc.getAppId(), getCurAppPersistenceMaps().get(0).getPersistenceMap().get(Integer.valueOf(appDesc.getPersistenceType())), getCurAppPersistenceMaps().get(1).getPersistenceMap().get(Integer.valueOf(appDesc.getPersistenceType())));\n            if(StringUtils.isNotEmpty(failMessage)){\n                log.error(\"checkAndFixAppPersistence job fail: {}\", failMessage);\n                emailComponent.sendMailToAdmin(\"【CacheCloud】持久化报警\", this.generateAlertInfo(appDesc, failMessage));\n            }\n        });\n    }\n\n    private String generateAlertInfo(AppDesc appDesc, String failMessage){\n        return String.format(\"应用id：%s 应用名：%s 应用环境：%s 应用类型：%s 持久化类型：%s <br> 持久化检测报警：%s<br>请及时关注。\",\n                appDesc.getAppId(), appDesc.getName(),\n                (appDesc.getIsTest() == 0 ? \"测试\" : \"正式\"),\n                appDesc.getTypeDesc(),\n                AppDescEnum.AppPersistenceType.getByType(appDesc.getPersistenceType()).getInfo(),\n                failMessage);\n    }\n\n    @Data\n    @AllArgsConstructor\n    @NoArgsConstructor\n    class AppPersistenceTypeMap {\n\n        private Boolean isMaster;\n\n        private Map<Integer, Map<String, String>> persistenceMap;\n\n    }\n\n    public List<AppPersistenceTypeMap> getCurAppPersistenceMaps(){\n        List<AppPersistenceTypeMap> appPersistenceMaps = new ArrayList<>();\n        String configStr = ConstUtils.APP_PERSISTENCE_CONFIG_MAP;\n        JSONArray objects = JSON.parseArray(configStr);\n        JSONObject jsonObject = objects.getJSONObject(0);\n        AppPersistenceTypeMap curPersistenceMap = getCurPersistenceMap(jsonObject);\n        appPersistenceMaps.add(curPersistenceMap);\n        jsonObject = objects.getJSONObject(1);\n        curPersistenceMap = getCurPersistenceMap(jsonObject);\n        if(curPersistenceMap.isMaster){\n            appPersistenceMaps.add(0, curPersistenceMap);\n        }else{\n            appPersistenceMaps.add(curPersistenceMap);\n        }\n        return appPersistenceMaps;\n    }\n\n    private AppPersistenceTypeMap getCurPersistenceMap(JSONObject jsonObject){\n        Map<Integer, Map<String, String>> persistenceMap = new HashMap<>();\n        Set<String> keySet = jsonObject.keySet();\n        boolean isMaster = jsonObject.getBoolean(\"isMaster\");\n        JSONObject persistenceJson = jsonObject.getJSONObject(\"persistenceMap\");\n        Set<String> persisTypeSet = persistenceJson.keySet();\n        persisTypeSet.forEach(persisType -> {\n                    Map<String, String> configMap = persistenceJson.getObject(persisType, Map.class);\n                    persistenceMap.put(Integer.valueOf(persisType), configMap);\n                }\n        );\n        return new AppPersistenceTypeMap(isMaster, persistenceMap);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppStatsCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.constant.AppTopology;\nimport com.sohu.cache.constant.TimeDimensionalityEnum;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.PandectUtil;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.MachineMemoryDistriEnum;\nimport com.sohu.cache.web.enums.StatEnum;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections4.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.assertj.core.util.Lists;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\n\nimport java.text.DecimalFormat;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 基于app的统计信息的接口：包括app详情、app配置以及基于app的统计\n *\n * @author leifu\n * @Date 2015年3月2日\n * @Time 下午1:50:09\n */\n@Service(\"appStatsCenter\")\npublic class AppStatsCenterImpl implements AppStatsCenter {\n\n    private final static String COLLECT_DATE_FORMAT = \"yyyyMMddHHmm\";\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private MachineDao machineDao;\n    @Autowired\n    private AppStatsDao appStatsDao;\n    @Autowired\n    private InstanceLatencyHistoryDao instanceLatencyHistoryDao;\n    @Autowired\n    private InstanceSlowLogDao instanceSlowLogDao;\n    @Autowired\n    private InstanceStatsDao instanceStatsDao;\n    @Autowired\n    @Lazy\n    private RedisCenter redisCenter;\n    @Autowired\n    private UserService userService;\n    @Autowired\n    @Lazy\n    private MachineCenter machineCenter;\n    @Autowired\n    private ResourceDao resourceDao;\n    @Autowired\n    private AppDailyDao appDailyDao;\n\n    @Override\n    public List<AppStats> getAppStatsListByMinuteTime(long appId, long beginTime, long endTime) {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(beginTime > 0 && endTime > 0);\n\n        List<AppStats> appStatsList = null;\n        try {\n            appStatsList = appStatsDao.getAppStatsList(appId, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return appStatsList;\n    }\n\n    /**\n     * 通过时间区间查询app的分钟统计数据\n     *\n     * @param appId\n     * @param beginTime 时间，格式：yyyyMMddHHmm\n     * @param endTime   时间，格式：yyyyMMddHHmm\n     * @return\n     */\n    @Override\n    public List<AppStats> getAppStatsList(final long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum) {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(beginTime > 0 && endTime > 0);\n\n        List<AppStats> appStatsList = null;\n        try {\n            if (TimeDimensionalityEnum.MINUTE.equals(timeDimensionalityEnum)) {\n                appStatsList = appStatsDao.getAppStatsByMinute(appId, beginTime, endTime);\n            } else if (TimeDimensionalityEnum.HOUR.equals(timeDimensionalityEnum)) {\n                appStatsList = appStatsDao.getAppStatsByHour(appId, beginTime, endTime);\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return appStatsList;\n    }\n\n    /**\n     * 通过时间区间查询app的每日日报数据\n     *\n     * @param appId\n     * @param beginDate 时间，格式：yyyyMMdd\n     * @param endDate   时间，格式：yyyyMMdd\n     * @return\n     */\n    @Override\n    public List<AppDailyData> getAppHitRatioList(final long appId, long beginDate, long endDate) {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(beginDate > 0 && endDate > 0);\n\n        List<AppDailyData> appStatsList = null;\n        try {\n                appStatsList = appDailyDao.getAppDailyList(appId, beginDate, endDate);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return appStatsList;\n    }\n\n    /**\n     * 按照分钟查询应用历史最大使用内存，时间跨度不可过大，会有性能问题\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    @Override\n    public Long getUsedMemoryMaxByTimeBetween(final long appId, long beginTime, long endTime){\n        return appStatsDao.getUsedMemoryMaxByTimeBetween(appId, beginTime, endTime);\n    }\n\n    /**\n     * 通过时间区间查询一条app的分钟统计数据\n     *\n     * @param appId\n     * @param beginTime 时间，格式：yyyyMMddHHmm\n     * @param endTime   时间，格式：yyyyMMddHHmm\n     * @return\n     */\n    @Override\n    public AppStats getOneAppStats(final long appId, long beginTime, long endTime) {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(beginTime > 0 && endTime > 0);\n        AppStats appStats = appStatsDao.getOneAppStatsByMinute(appId, beginTime, endTime);\n        return appStats;\n    }\n\n    @Override\n    public List<AppCommandStats> getTop5AppCommandStatsList(final long appId, long begin, long end) {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(begin > 0L);\n        Assert.isTrue(end > 0L);\n\n        List<AppCommandStats> topAppCmdList = null;\n        try {\n            topAppCmdList = appStatsDao.getTopAppCommandGroupSum(appId, new TimeDimensionality(begin, end, COLLECT_DATE_FORMAT), 5);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return topAppCmdList;\n    }\n\n    @Override\n    public List<AppCommandStats> getTopLimitAppCommandStatsList(long appId, long begin, long end, int limit) {\n        Assert.isTrue(appId > 0);\n        Assert.isTrue(begin > 0L);\n        Assert.isTrue(end > 0L);\n\n        List<AppCommandStats> topAppCmdList = null;\n        try {\n            topAppCmdList = appStatsDao.getTopAppCommandStatsList(appId, new TimeDimensionality(begin, end, COLLECT_DATE_FORMAT), limit);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return topAppCmdList;\n    }\n\n    /**\n     * 查询应用的配置和节点信息\n     *\n     * @param appId\n     * @return\n     */\n    @Override\n    public Map<AppTopology, Object> queryAppTopology(final long appId) {\n        Assert.isTrue(appId > 0);\n\n        Map<AppTopology, Object> appTopologyMap = new HashMap<AppTopology, Object>();\n        AppDesc appDesc = null;\n        double totalMemory = 0.0;\n        Set<Long> machineSet = new HashSet<Long>();\n        int masterCount = 0;\n        int slaveCount = 0;\n\n        List<InstanceInfo> instanceInfoList = null;\n        try {\n            appDesc = appDao.getAppDescById(appId);\n            instanceInfoList = instanceDao.getInstListByAppId(appId);\n            if (appDesc == null || instanceInfoList == null || instanceInfoList.isEmpty()) {\n                logger.error(\"get app and it's instances error， appId = {}\", appId);\n                return null;\n            }\n            if (appDesc.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                for (InstanceInfo instance : instanceInfoList) {\n                    machineSet.add(instance.getHostId());\n                    totalMemory += instance.getMem();\n                    BooleanEnum isMaster = redisCenter.isMaster(appId, instance.getIp(), instance.getPort());\n                    if (isMaster == BooleanEnum.OTHER) {\n                        continue;\n                    }\n                    if (isMaster == BooleanEnum.TRUE) {\n                        masterCount++;\n                    } else {\n                        slaveCount++;\n                    }\n                }\n            }\n            appTopologyMap.put(AppTopology.TOTAL_MEMORY, totalMemory / ConstUtils._1024);\n            appTopologyMap.put(AppTopology.MACHINE_COUNT, machineSet.size());\n            appTopologyMap.put(AppTopology.MASTER_COUNT, masterCount);\n            appTopologyMap.put(AppTopology.SLAVE_COUNT, slaveCount);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        return appTopologyMap;\n    }\n\n    /**\n     * 查询应用指定时间段，指定命令名的结果集合\n     *\n     * @param appId       应用id\n     * @param beginTime   时间，格式：yyyyMMddHHmm\n     * @param endTime     时间，格式：yyyyMMddHHmm\n     * @param commandName 命令名\n     * @return\n     */\n    @Override\n    public List<AppCommandStats> getCommandStatsList(long appId, long beginTime, long endTime, String commandName) {\n        return appStatsDao.getAppCommandStatsList(appId, commandName, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));\n    }\n\n    /**\n     * 查询应用指定时间段，指定命令名的结果集合\n     *\n     * @param appId     应用id\n     * @param beginTime 时间，格式：yyyyMMddHHmm\n     * @param endTime   时间，格式：yyyyMMddHHmm\n     * @return\n     */\n    @Override\n    public List<AppCommandStats> getCommandStatsList(long appId, long beginTime, long endTime) {\n        return appStatsDao.getAppAllCommandStatsList(appId, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));\n    }\n\n    @Override\n    public List<AppCommandStats> getCommandStatsListV2(long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum, String commandName) {\n        if (TimeDimensionalityEnum.MINUTE.equals(timeDimensionalityEnum)) {\n            return appStatsDao.getAppCommandStatsListByMinuteWithCommand(appId, beginTime, endTime, commandName);\n        } else if (TimeDimensionalityEnum.HOUR.equals(timeDimensionalityEnum)) {\n            return appStatsDao.getAppCommandStatsListByHourWithCommand(appId, beginTime, endTime, commandName);\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List<AppCommandStats> getCommandStatsListV2(long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum) {\n        if (TimeDimensionalityEnum.MINUTE.equals(timeDimensionalityEnum)) {\n            return appStatsDao.getAppAllCommandStatsListByMinute(appId, beginTime, endTime);\n        } else if (TimeDimensionalityEnum.HOUR.equals(timeDimensionalityEnum)) {\n            return appStatsDao.getAppAllCommandStatsListByHour(appId, beginTime, endTime);\n        }\n        return Collections.emptyList();\n    }\n\n\n    /**\n     * 查询应用指定命令的峰值\n     *\n     * @param appId       应用id\n     * @param beginTime   时间，格式：yyyyMMddHHmm\n     * @param endTime     时间，格式：yyyyMMddHHmm\n     * @param commandName 命令名\n     * @return\n     */\n    @Override\n    public AppCommandStats getCommandClimax(long appId, Long beginTime, Long endTime, String commandName) {\n        TimeDimensionality td = new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT);\n        AppCommandStats appCommandStats = appStatsDao.getCommandClimaxCount(appId, commandName, td);\n        if (appCommandStats == null) {\n            return null;\n        }\n        appCommandStats.setCommandName(commandName);\n        AppCommandStats appCommandStatsTemp = appStatsDao.getCommandClimaxCreateTime(appId, commandName, appCommandStats.getCommandCount(), td);\n        if (appCommandStatsTemp != null) {\n            appCommandStats.setCreateTime(appCommandStatsTemp.getCreateTime());\n        }\n        return appCommandStats;\n    }\n\n    /**\n     * 获取应用命令调用次数分布\n     *\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    @Override\n    public List<AppCommandGroup> getAppCommandGroup(long appId, Long beginTime, Long endTime) {\n        return appStatsDao.getAppCommandGroup(appId, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));\n    }\n\n    /**\n     * 获取应用命令调用次数分布\n     *\n     * @param appId\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    @Override\n    public Long getAppCommandCount(long appId, long beginTime, long endTime) {\n        Long count = appStatsDao.getAppCommandCount(appId, beginTime, endTime);\n        return count == null ? 0L : count;\n    }\n\n    /**\n     * 获取应用详细信息\n     */\n    @Override\n    public AppDetailVO getAppDetail(long appId) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return null;\n        }\n        //1.获取Redis版本名称\n        SystemResource redisResource = resourceDao.getResourceById(appDesc.getVersionId());\n        if (redisResource != null) {\n            appDesc.setVersionName(redisResource.getName());\n            //2.判断版本是否可以升级\n            List<SystemResource> versionList = resourceDao.getResourceList(ResourceEnum.REDIS.getValue());\n            for (SystemResource version : versionList) {\n                try {\n                    // 大版本同一版本且大于当前版本号\n                    int versionTag = Integer.parseInt(version.getName().replaceAll(\"redis-\",\"\").replaceAll(\"\\\\.\",\"\"));\n                    int currentTag = Integer.parseInt(redisResource.getName().replaceAll(\"redis-\",\"\").replaceAll(\"\\\\.\",\"\"));\n                    // 支持小版本号升级\n                    String versionStr = redisResource.getName().substring(0, redisResource.getName().lastIndexOf(\".\"));\n                    if (version.getName().indexOf(versionStr) > -1 && versionTag > currentTag) {\n                        appDesc.setIsVersionUpgrade(1);\n                        break;\n                    }\n                } catch (Exception e) {\n                    logger.error(\"parse version:{} {} exception : {}\", redisResource.getName(), version.getName(), e.getMessage(), e);\n                    appDesc.setIsVersionUpgrade(0);\n                }\n            }\n        } else {\n            appDesc.setIsVersionUpgrade(0);\n        }\n\n        AppDetailVO resultVO = new AppDetailVO();\n        resultVO.setAppDesc(appDesc);\n        Set<String> machines = new HashSet<String>();\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n\n        if (instanceList == null || instanceList.isEmpty()) {\n            return resultVO;\n        }\n        long hits = 0L;\n        long miss = 0L;\n        long allUsedMemory = 0L;\n        long allUsedDisk = 0L;\n        long allMaxDisk = 0L;\n        long allMaxMemory = 0L;\n        double highestMemFragRatio = 0D;        //碎片率最大值\n        long instId = 0L;           //碎片率最大值的实例id\n        List<InstanceStats> instanceStatsList = instanceStatsDao.getInstanceStatsByAppId(appId);\n        if (instanceStatsList != null && instanceStatsList.size() > 0) {\n            Map<Long, InstanceStats> instanceStatMap = new HashMap<Long, InstanceStats>();\n            for (InstanceStats stats : instanceStatsList) {\n                instanceStatMap.put(stats.getInstId(), stats);\n            }\n\n            for (InstanceInfo instanceInfo : instanceList) {\n                if (instanceInfo.isOffline()) {\n                    continue;\n                }\n                machines.add(instanceInfo.getIp());\n                InstanceStats instanceStats = instanceStatMap.get(Long.valueOf(instanceInfo.getId()));\n                if (instanceStats == null) {\n                    continue;\n                }\n                boolean isMaster = isMaster(instanceStats);\n\n                long usedMemory = instanceStats.getUsedMemory();\n                long usedMemoryMB = usedMemory / 1024 / 1024;\n                long usedDisk = instanceStats.getUsedDisk();\n                long usedDiskMB = usedDisk / 1024 / 1024;\n                allUsedDisk += usedDisk;\n                allMaxDisk += instanceInfo.getMem() * 1024L * 1024;\n\n                allUsedMemory += usedMemory;\n                allMaxMemory += instanceStats.getMaxMemory();\n\n                hits += instanceStats.getHits();\n                miss += instanceStats.getMisses();\n                //碎片率最大值\n                double memFragRatio = instanceStats.getMemFragmentationRatio();\n                if (memFragRatio > highestMemFragRatio) {\n                    highestMemFragRatio = memFragRatio;\n                    instId = instanceStats.getInstId();\n                }\n                if (isMaster) {\n                    resultVO.setMem(resultVO.getMem() + instanceInfo.getMem());\n                    resultVO.setCurrentMem(resultVO.getCurrentMem() + usedMemoryMB);\n                    resultVO.setCurrentObjNum(resultVO.getCurrentObjNum() + instanceStats.getCurrItems());\n                    resultVO.setMasterNum(resultVO.getMasterNum() + 1);\n                    resultVO.setCurrentDisk(resultVO.getCurrentDisk() + usedDiskMB);\n                    //按instanceStats计算conn\n                    resultVO.setConn(resultVO.getConn() + instanceStats.getCurrConnections());\n                } else {\n                    resultVO.setSlaveNum(resultVO.getSlaveNum() + 1);\n                }\n            }\n        }\n        // 授权用户\n        List<AppUser> userList = userService.getByAppId(appId);\n        if (userList != null && userList.size() > 0) {\n            resultVO.setAppUsers(userList);\n        }\n        // 报警用户\n        List<AppUser> alertUsedrList = userService.getAlertByAppId(appId);\n        if (alertUsedrList != null && alertUsedrList.size() > 0) {\n            resultVO.setAlertUsers(alertUsedrList);\n        }\n\n        resultVO.setMachineNum(machines.size());\n        if (allMaxMemory == 0L) {\n            resultVO.setMemUsePercent(0.0D);\n        } else {\n            double percent = 100 * (double) allUsedMemory / (allMaxMemory);\n            DecimalFormat df = new DecimalFormat(\"##.##\");\n            resultVO.setMemUsePercent(Double.parseDouble(df.format(percent)));\n        }\n        if (allMaxDisk == 0L) {\n            resultVO.setDiskUsePercent(0.0D);\n        } else {\n            double diskPercent = 100 * (double) allUsedDisk / (allMaxDisk);\n            DecimalFormat df = new DecimalFormat(\"##.##\");\n            resultVO.setDiskUsePercent(Double.parseDouble(df.format(diskPercent)));\n        }\n        //最大碎片率及对应实例Id\n        resultVO.setHighestMemFragRatio(highestMemFragRatio);\n        resultVO.setInstIdWithHighestMemFragRatio(instId);\n\n        if (miss == 0L) {\n            if (hits > 0) {\n                resultVO.setHitPercent(100.0D);\n            } else {\n                resultVO.setHitPercent(0.0D);\n            }\n        } else {\n            double percent = 100 * (double) hits / (hits + miss);\n            DecimalFormat df = new DecimalFormat(\"##.##\");\n            resultVO.setHitPercent(Double.parseDouble(df.format(percent)));\n        }\n\n        return resultVO;\n    }\n\n    @Override\n    public Map<Long, AppDetailVO> getOnlineAppDetails() {\n        List<AppDesc> appDescList = appDao.getOnlineApps();\n        Map<Long, AppDetailVO> appDetailVOMap = appDescList.stream()\n                .map(appDesc -> getAppDetail(appDesc.getAppId()))\n                .collect(Collectors.toMap(appDetail -> appDetail.getAppDesc().getAppId(), appDetail -> appDetail));\n        return appDetailVOMap;\n    }\n\n    @Override\n    public List<AppClientStatisticGather> getOnlineAppConnClients() {\n        List<AppClientStatisticGather> result = new ArrayList<>();\n        List<AppDesc> appDescList = appDao.getOnlineApps();\n        appDescList.forEach(appDesc -> {\n            AppClientStatisticGather gather = new AppClientStatisticGather();\n            long appId = appDesc.getAppId();\n            List<Map<String, Object>> addrInstanceList = redisCenter.getAppClientList(appId, 0);\n            int totalConnectedClients = 0;\n            for (Map<String, Object> addrInstance : addrInstanceList) {\n                totalConnectedClients += MapUtils.getIntValue(addrInstance, \"size\", 0);\n            }\n\n            gather.setGatherTime(DateUtil.formatYYYY_MM_dd(new Date()));\n            gather.setAppId(appId);\n            gather.setConnectedClients(totalConnectedClients);\n            result.add(gather);\n        });\n        return result;\n    }\n\n    private boolean isMaster(InstanceStats instanceStats) {\n        return instanceStats.getRole() == 1 ? true : false;\n    }\n\n    @Override\n    public String executeCommand(long appId, String command, String userName) {\n        if (StringUtils.isBlank(command)) {\n            return \"命令不能为空\";\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return \"app not found\";\n        }\n        if (TypeUtil.isRedisType(appDesc.getType())) {\n            return redisCenter.executeCommand(appDesc, command, userName);\n        }\n        return \"not support app\";\n    }\n\n    @Override\n    public Map<String, Long> getInstanceSlowLogCountMapByAppId(Long appId, Date startDate, Date endDate) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return Collections.emptyMap();\n        }\n        if (TypeUtil.isRedisType(appDesc.getType())) {\n            return redisCenter.getInstanceSlowLogCountMapByAppId(appId, startDate, endDate);\n        }\n        return Collections.emptyMap();\n    }\n\n    @Override\n    public List<InstanceSlowLog> getInstanceSlowLogByAppId(long appId, Date startDate, Date endDate) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return Collections.emptyList();\n        }\n        if (TypeUtil.isRedisType(appDesc.getType())) {\n            return redisCenter.getInstanceSlowLogByAppId(appId, startDate, endDate);\n        }\n        return Collections.emptyList();\n    }\n\n    @Override\n    public Map<String, List<Map<String, Object>>> getAppLatencyStats(long appId, long startTime, long endTime) {\n        try {\n            List<Map<String, Object>> appLatencyInfoList = instanceLatencyHistoryDao.getAppLatencyStats(appId, startTime, endTime);\n            Map<String, List<Map<String, Object>>> appLatencyInfoMap = Maps.newHashMap();\n\n            appLatencyInfoList.stream().forEach(appLatencyInfo -> {\n                String event = MapUtils.getString(appLatencyInfo, \"event\");\n                ArrayList appEventLatency = (ArrayList) MapUtils.getObject(appLatencyInfoMap, event);\n                if (CollectionUtils.isEmpty(appEventLatency)) {\n                    appEventLatency = Lists.newArrayList();\n                    appLatencyInfoMap.put(event, appEventLatency);\n                }\n                appEventLatency.add(appLatencyInfo);\n            });\n            return appLatencyInfoMap;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public Map<String, Long> getAppLatencyStatsGroupByInstance(long appId, long startTime, long endTime) {\n        try {\n            List<Map<String, Object>> appLatencyInfoList = instanceLatencyHistoryDao.getAppLatencyStatsGroupByInstance(appId, startTime, endTime);\n            Map<String, Long> appInstanceLatencyStats = appLatencyInfoList.stream()\n                    .collect(Collectors.toMap(latencyInfo -> MapUtils.getString(latencyInfo, \"host_port\"), latencyInfo -> MapUtils.getLong(latencyInfo, \"count\")));\n            return appInstanceLatencyStats;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n\n    @Override\n    public Map<String, List<Map<String, Object>>> getAppLatencyInfo(long appId, long startTime, long endTime) {\n        try {\n            List<Map<String, Object>> appLatencyInfoList = instanceLatencyHistoryDao.getAppLatencyInfo(appId, startTime, endTime, \"\");\n            Map<String, List<Map<String, Object>>> appLatencyInfoMap = Maps.newHashMap();\n\n            appLatencyInfoList.stream().forEach(appLatencyInfo -> {\n                String host_port = MapUtils.getString(appLatencyInfo, \"host_port\");\n                ArrayList appEventLatency = (ArrayList) MapUtils.getObject(appLatencyInfoMap, host_port);\n                if (CollectionUtils.isEmpty(appEventLatency)) {\n                    appEventLatency = Lists.newArrayList();\n                    appLatencyInfoMap.put(host_port, appEventLatency);\n                }\n                appEventLatency.add(appLatencyInfo);\n            });\n            return appLatencyInfoMap;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public List<InstanceSlowLog> getByInstanceExecuteTime(long instanceId, String executeDate) {\n        try {\n            return instanceSlowLogDao.getByInstanceExecuteTime(instanceId, executeDate);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    public Map<String, Object> getAppTotalStat() {\n\n        Map<String, Object> totalMap = new HashMap<String, Object>();\n\n        /**\n         * 1.从mysql获取\n         * 2.从jvm获取\n         */\n        if (PandectUtil.getFromMysql() || MapUtils.isEmpty(PandectUtil.getPandectMap())) {\n            // 1.基础统计\n            // 1.1 在线应用数\n            int appCount = appDao.getAllAppCount(null);\n            totalMap.put(StatEnum.TOTAL_EFFETIVE_APP.value(), appCount);\n            // 1.2 机器数量\n            List<MachineInfo> machineInfoList = machineDao.getAllMachines();\n            totalMap.put(StatEnum.TOTAL_MACHINE_NUM.value(), machineInfoList.size());\n            //1.2.1 物理机数量，增加物理机数量\n            Set<String> physicalMachineSet = machineInfoList.stream()\n                    .map(machineInfo -> machineInfo.getRealIp())\n                    .filter(realIp -> StringUtils.isNotEmpty(realIp))\n                    .collect(Collectors.toSet());\n            totalMap.put(StatEnum.TOTAL_PHYSICAL_MACHINE_NUM.value(), physicalMachineSet.size());\n            // 1.3 获取实例数量\n            List<InstanceInfo> allInsts = instanceDao.getAllInsts();\n            totalMap.put(StatEnum.TOTAL_INSTANCE_NUM.value(), allInsts.size());\n\n            // 2.获取redis版本分布情况\n            List<ParamCount> redisDistribute = new ArrayList<ParamCount>();\n            List<Map<String, Integer>> appVersionStats = appDao.getVersionStat();\n            Map<Integer, String> versionMap = new HashMap<Integer, String>();\n            List<SystemResource> versionList = resourceDao.getResourceList(ResourceEnum.REDIS.getValue());\n            if (!CollectionUtils.isEmpty(versionList)) {\n                for (SystemResource  version : versionList) {\n                    versionMap.put(version.getId(), version.getName());\n                }\n            }\n            if (!CollectionUtils.isEmpty(appVersionStats)) {\n                for (Map<String, Integer> appVersion : appVersionStats) {\n                    Integer version_id = MapUtils.getInteger(appVersion, \"version_id\");\n                    Integer num = MapUtils.getInteger(appVersion, \"num\");\n                    if (versionMap.containsKey(version_id)) {\n                        ParamCount paramCount = new ParamCount(versionMap.get(version_id), num, \"\");\n                        redisDistribute.add(paramCount);\n                    }\n                }\n            }\n            totalMap.put(StatEnum.REDIS_VERSION_DISTRIBUTE.value(), JSONObject.toJSONString(redisDistribute));\n            totalMap.put(StatEnum.REDIS_VERSION_COUNT.value(), versionList.size());\n\n            // 4.获取机器内存分配/机器内存使用分布\n            // 4.1 机器内存使用分布\n            Map<MachineMemoryDistriEnum, Integer> machineMemoryDistributeMap = machineCenter.getUsedMemoryDistribute();\n            List<ParamCount> machineMemoryDistributeList = new ArrayList<ParamCount>();\n            for (Map.Entry<MachineMemoryDistriEnum, Integer> entry : machineMemoryDistributeMap.entrySet()) {\n                ParamCount paramCount = new ParamCount(entry.getKey().getInfo(), entry.getValue(), \"\");\n                machineMemoryDistributeList.add(paramCount);\n            }\n            totalMap.put(StatEnum.MACHINE_USEDMEMORY_DISTRIBUTE.value(), JSONObject.toJSONString(machineMemoryDistributeList));\n            // 4.2 机器内存分配分布\n            Map<MachineMemoryDistriEnum, Integer> maxMemoryDistributeMap = machineCenter.getMaxMemoryDistribute();\n            List<ParamCount> maxMemoryDistributeList = new ArrayList<ParamCount>();\n            for (Map.Entry<MachineMemoryDistriEnum, Integer> entry : maxMemoryDistributeMap.entrySet()) {\n                ParamCount paramCount = new ParamCount(entry.getKey().getInfo(), entry.getValue(), \"\");\n                maxMemoryDistributeList.add(paramCount);\n            }\n            totalMap.put(StatEnum.MACHINE_MAXMEMORY_DISTRIBUTE.value(), JSONObject.toJSONString(maxMemoryDistributeList));\n            // 4.3 机房分布\n            List<Map<String, Object>> roomStat = machineDao.getRoomStat();\n            List<ParamCount> roomDistribute = new ArrayList<ParamCount>();\n            if (roomStat != null && roomStat.size() > 0) {\n                for (Map<String, Object> room : roomStat) {\n                    ParamCount paramCount = new ParamCount(MapUtils.getString(room, \"name\"), MapUtils.getInteger(room, \"num\"), \"\");\n                    roomDistribute.add(paramCount);\n                }\n            }\n            totalMap.put(StatEnum.MACHIEN_ROOM_DISTRIBUTE.value(), JSONObject.toJSONString(roomDistribute));\n\n            // 5.最后一次从数据库获取时间\n            totalMap.put(PandectUtil.KEY_LASTTIME, System.currentTimeMillis());\n            // 6.暂存到jvm\n            PandectUtil.setPandectMap(totalMap);\n        } else {\n            totalMap.putAll(PandectUtil.getPandectMap());\n        }\n        return totalMap;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/ImportAppCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\r\n\r\nimport com.sohu.cache.constant.ImportAppResult;\r\nimport com.sohu.cache.constant.InstanceStatusEnum;\r\nimport com.sohu.cache.dao.InstanceDao;\r\nimport com.sohu.cache.dao.InstanceStatsDao;\r\nimport com.sohu.cache.entity.AppDesc;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.entity.InstanceStats;\r\nimport com.sohu.cache.entity.MachineInfo;\r\nimport com.sohu.cache.machine.MachineCenter;\r\nimport com.sohu.cache.redis.RedisCenter;\r\nimport com.sohu.cache.stats.app.ImportAppCenter;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.IdempotentConfirmer;\r\nimport com.sohu.cache.web.service.AppService;\r\nimport org.apache.commons.collections.MapUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.context.annotation.Lazy;\r\nimport org.springframework.stereotype.Service;\r\nimport redis.clients.jedis.Jedis;\r\nimport redis.clients.jedis.Protocol;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 导入应用\r\n *\r\n * @author leifu\r\n * @Date 2016-4-16\r\n * @Time 下午3:42:49\r\n */\r\n@Service(\"importAppCenter\")\r\npublic class ImportAppCenterImpl implements ImportAppCenter {\r\n\r\n    private Logger logger = LoggerFactory.getLogger(ImportAppCenterImpl.class);\r\n    @Autowired\r\n    private AppService appService;\r\n    @Autowired\r\n    @Lazy\r\n    private RedisCenter redisCenter;\r\n    @Autowired\r\n    @Lazy\r\n    private MachineCenter machineCenter;\r\n    @Autowired\r\n    private InstanceDao instanceDao;\r\n    @Autowired\r\n    private InstanceStatsDao instanceStatsDao;\r\n\r\n    @Override\r\n    public ImportAppResult check(int type, String appInstanceInfo, String password) {\r\n        // 1.实例信息是否为空\r\n        if (StringUtils.isBlank(appInstanceInfo)) {\r\n            return ImportAppResult.fail(\"实例详情为空\");\r\n        }\r\n\r\n        String[] appInstanceDetails = appInstanceInfo.split(\"\\n\");\r\n\r\n        String masterNameInput = \"\";\r\n        // 2.检查实例信息格式是否正确\r\n        for (String appInstance : appInstanceDetails) {\r\n            if (StringUtils.isBlank(appInstance)) {\r\n                return ImportAppResult.fail(\"应用实例信息有空行\");\r\n            }\r\n            String[] instanceItems = appInstance.split(\":\");\r\n            if (instanceItems.length != 2) {\r\n                return ImportAppResult.fail(\"应用实例信息\" + appInstance + \"格式错误，必须以冒号分隔\");\r\n            }\r\n\r\n            // 2.1检查端口是否为整数\r\n            String ip = instanceItems[0];\r\n            String portStr = instanceItems[1];\r\n            boolean portIsDigit = NumberUtils.isDigits(portStr);\r\n            if ((!portIsDigit) && (type != ConstUtils.CACHE_REDIS_SENTINEL)) {\r\n                return ImportAppResult.fail(appInstance + \"中的port不是整数\");\r\n            } else if ((!portIsDigit) && (type == ConstUtils.CACHE_REDIS_SENTINEL)) {\r\n                masterNameInput = instanceItems[0];\r\n                continue;\r\n            }\r\n\r\n            int port = NumberUtils.toInt(portStr);\r\n            // 2.2检查ip:port是否已经在instance_info表和instance_statistics中\r\n            int count = instanceDao.getCountByIpAndPort(ip, port);\r\n            if (count > 0) {\r\n                return ImportAppResult.fail(appInstance + \"中ip:port已经在instance_info存在\");\r\n            }\r\n            InstanceStats instanceStats = instanceStatsDao.getInstanceStatsByHost(ip, port);\r\n            if (instanceStats != null) {\r\n                return ImportAppResult.fail(appInstance + \"中ip:port已经在instance_statistics存在\");\r\n            }\r\n            // 3.2检查Redis实例是否存活\r\n            boolean isRun;\r\n            if (StringUtils.isNotEmpty(password)) {\r\n                // 外部导入密码以外部密码为主(cc内部采用一定规则加密)\r\n                isRun = redisCenter.isRun(ip, port, password);\r\n            } else {\r\n                isRun = redisCenter.isRun(ip, port);\r\n            }\r\n            if (!isRun) {\r\n                return ImportAppResult.fail(appInstance + \"中的节点不是存活的\");\r\n            }\r\n\r\n            //3.3判断sentinel模式下，masterName是否正确\r\n            if (StringUtils.isNotEmpty(masterNameInput) && (type == ConstUtils.CACHE_REDIS_SENTINEL)) {\r\n                String masterName = getSentinelMasterName(ip, port);\r\n                if (StringUtils.isEmpty(masterName) || !masterNameInput.equals(masterName)) {\r\n                    return ImportAppResult.fail(ip + \":\" + port + \", masterName:\" + masterName + \"与所填\"\r\n                            + masterNameInput + \"不一致\");\r\n                }\r\n            }\r\n\r\n        }\r\n        return ImportAppResult.success();\r\n    }\r\n\r\n\r\n    @Override\r\n    public boolean importAppAndInstance(AppDesc appDesc, String appInstanceInfo) {\r\n        boolean isSuccess = true;\r\n        try {\r\n            // 1.1 保存应用信息\r\n            appService.save(appDesc);\r\n            long appId = appDesc.getAppId();\r\n            // 1.2 更新appKey\r\n            appService.updateAppKey(appId);\r\n\r\n            int type = appDesc.getType();\r\n            // 2.保存应用和用户的关系\r\n            appService.saveAppToUser(appId, appDesc.getUserId());\r\n            // 3.保存实例信息并开启统计\r\n            String[] appInstanceDetails = appInstanceInfo.split(\"\\n\");\r\n            // 4.检查实例信息格式是否正确\r\n            for (String appInstance : appInstanceDetails) {\r\n                String[] instanceItems = appInstance.split(\":\");\r\n                String host = instanceItems[0];\r\n                int port = NumberUtils.toInt(instanceItems[1]);\r\n\r\n                String memoryOrMasterName = instanceItems[2];\r\n                boolean isSentinelNode = NumberUtils.toInt(memoryOrMasterName) <= 0;\r\n                if (isSentinelNode) {\r\n                    saveInstance(appId, host, port, 0, ConstUtils.CACHE_REDIS_SENTINEL, memoryOrMasterName);\r\n                } else {\r\n                    if (ConstUtils.CACHE_REDIS_STANDALONE == type || ConstUtils.CACHE_REDIS_SENTINEL == type) {\r\n                        saveInstance(appId, host, port, NumberUtils.toInt(memoryOrMasterName), ConstUtils.CACHE_REDIS_STANDALONE, \"\");\r\n                    } else if (ConstUtils.CACHE_TYPE_REDIS_CLUSTER == type) {\r\n                        saveInstance(appId, host, port, NumberUtils.toInt(memoryOrMasterName), ConstUtils.CACHE_TYPE_REDIS_CLUSTER, \"\");\r\n                    }\r\n                }\r\n            }\r\n\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            isSuccess = false;\r\n        }\r\n        return isSuccess;\r\n    }\r\n\r\n    /**\r\n     * 获取sentinel的masterName\r\n     *\r\n     * @param ip\r\n     * @param port\r\n     * @return\r\n     */\r\n    private String getSentinelMasterName(final String ip, final int port) {\r\n        final StringBuilder masterName = new StringBuilder();\r\n        new IdempotentConfirmer() {\r\n            private int timeOutFactor = 1;\r\n\r\n            @Override\r\n            public boolean execute() {\r\n                Jedis jedis = null;\r\n                try {\r\n                    // 预留\r\n                    String password = null;\r\n                    jedis = redisCenter.getJedis(ip, port, password);\r\n                    jedis.getClient().setConnectionTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));\r\n                    jedis.getClient().setSoTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));\r\n                    List<Map<String, String>> mapList = jedis.sentinelMasters();\r\n                    String targetKey = \"name\";\r\n                    for (Map<String, String> map : mapList) {\r\n                        if (map.containsKey(targetKey)) {\r\n                            masterName.append(MapUtils.getString(map, targetKey, \"\"));\r\n                        }\r\n                    }\r\n                    return true;\r\n                } catch (Exception e) {\r\n                    logger.warn(\"{}:{} error message is {} \", ip, port, e.getMessage());\r\n                    return false;\r\n                } finally {\r\n                    if (jedis != null) {\r\n                        jedis.close();\r\n                    }\r\n                }\r\n            }\r\n        }.run();\r\n        return masterName.toString();\r\n    }\r\n\r\n    /**\r\n     * 保存实例信息\r\n     *\r\n     * @param appId\r\n     * @param host\r\n     * @param port\r\n     * @param maxMemory\r\n     * @param type\r\n     * @param cmd\r\n     * @return\r\n     */\r\n    private InstanceInfo saveInstance(long appId, String host, int port, int maxMemory, int type,\r\n                                      String cmd) {\r\n        InstanceInfo instanceInfo = new InstanceInfo();\r\n        instanceInfo.setAppId(appId);\r\n        MachineInfo machineInfo = machineCenter.getMachineInfoByIp(host);\r\n        instanceInfo.setHostId(machineInfo.getId());\r\n        instanceInfo.setConn(0);\r\n        instanceInfo.setMem(maxMemory);\r\n        instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\r\n        instanceInfo.setPort(port);\r\n        instanceInfo.setType(type);\r\n        instanceInfo.setCmd(cmd);\r\n        instanceInfo.setIp(host);\r\n        instanceDao.saveInstance(instanceInfo);\r\n        return instanceInfo;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisMigrateToolCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\n\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.AppDataMigrateStatusDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.stats.app.AppDataMigrateCenter;\nimport com.sohu.cache.stats.app.RedisMigrateToolCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.service.AppService;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 数据迁移(使用唯品会的开源工具redis-migrate-tool进行迁移)\n *\n * @author leifu\n * @Date 2016-6-8\n * @Time 下午2:54:33\n */\n@Service(\"redisMigrateToolCenter\")\npublic class RedisMigrateToolCenterImpl implements RedisMigrateToolCenter {\n    private Logger logger = LoggerFactory.getLogger(RedisMigrateToolCenterImpl.class);\n    @Autowired\n    private AppService appService;\n    @Autowired\n    @Lazy\n    private RedisCenter redisCenter;\n    @Autowired\n    private MachineCenter machineCenter;\n    @Autowired\n    private AppDataMigrateCenter appDataMigrateCenter;\n    @Autowired\n    private AppDataMigrateStatusDao appDataMigrateStatusDao;\n\n    @Override\n    public AppDataMigrateResult check(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum,\n                                      String sourceServers,\n                                      AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, String redisSourcePass, String redisTargetPass,SystemResource resource) {\n        // 1. 检查migrateMachineIp是否安装\n        AppDataMigrateResult migrateMachineResult = checkMigrateMachine(migrateMachineIp,resource);\n        if (!migrateMachineResult.isSuccess()) {\n            return migrateMachineResult;\n        }\n        // 2. 检查源配置\n        AppDataMigrateResult sourceResult = checkMigrateConfig(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, redisSourcePass, true);\n        if (!sourceResult.isSuccess()) {\n            return sourceResult;\n        }\n        // 3. 检查目标\n        AppDataMigrateResult targetResult = checkMigrateConfig(migrateMachineIp, targetRedisMigrateEnum, targetServers, redisTargetPass, false);\n        if (!targetResult.isSuccess()) {\n            return targetResult;\n        }\n        return AppDataMigrateResult.success();\n    }\n\n    /**\n     * 检查迁移的机器是否正常\n     *\n     * @param migrateMachineIp\n     * @return\n     */\n    private AppDataMigrateResult checkMigrateMachine(String migrateMachineIp, SystemResource resource) {\n        if (StringUtils.isBlank(migrateMachineIp)) {\n            return AppDataMigrateResult.fail(\"redis-migrate-tool所在机器的IP不能为空\");\n        }\n        // 1. 检查机器是否存在在机器列表中\n        try {\n            MachineInfo machineInfo = machineCenter.getMachineInfoByIp(migrateMachineIp);\n            if (machineInfo == null) {\n                return AppDataMigrateResult.fail(migrateMachineIp + \"没有在机器管理列表中\");\n            } else if (machineInfo.isOffline()) {\n                return AppDataMigrateResult.fail(migrateMachineIp + \",该机器已经被删除\");\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return AppDataMigrateResult.fail(\"检测发生异常，请观察日志\");\n        }\n        // 2. 检查是否安装redis-migrate-tool\n        try {\n            String cmd = ConstUtils.getRedisMigrateToolCmd(resource.getName());\n            String response = SSHUtil.execute(migrateMachineIp, cmd);\n            if (StringUtils.isBlank(response) || !response.contains(\"source\") || !response.contains(\"target\")) {\n                return AppDataMigrateResult.fail(migrateMachineIp + \"下，\" + cmd + \"执行失败，请确保redis-migrate-tool安装正确!\");\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return AppDataMigrateResult.fail(\"检测发生异常，请观察日志\");\n        }\n        // 3. 检查是否有运行的redis-migrate-tool\n        // 3.2 查看进程是否存在\n        try {\n            String cmd = \"/bin/ps -ef | grep redis-migrate-tool | grep -v grep | grep -v tail\";\n            String response = SSHUtil.execute(migrateMachineIp, cmd);\n            if (StringUtils.isNotBlank(response)) {\n                return AppDataMigrateResult.fail(migrateMachineIp + \"下有redis-migrate-tool进程，请确保只有一台机器只有一个迁移任务进行\");\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return AppDataMigrateResult.fail(\"检测发生异常，请观察日志\");\n        }\n        return AppDataMigrateResult.success();\n    }\n\n    /**\n     * 检测配置\n     *\n     * @param migrateMachineIp\n     * @param redisMigrateEnum\n     * @param servers\n     * @return\n     */\n    private AppDataMigrateResult checkMigrateConfig(String migrateMachineIp, AppDataMigrateEnum redisMigrateEnum,\n                                                    String servers, String redisPassword, boolean isSource) {\n        //target如果是rdb是没有路径的，不需要检测\n        if (isSource || !AppDataMigrateEnum.isFileType(redisMigrateEnum)) {\n            if (StringUtils.isBlank(servers)) {\n                return AppDataMigrateResult.fail(\"服务器信息不能为空!\");\n            }\n        }\n        List<String> serverList = Arrays.asList(servers.split(ConstUtils.NEXT_LINE));\n        if (CollectionUtils.isEmpty(serverList)) {\n            return AppDataMigrateResult.fail(\"服务器信息格式有问题!\");\n        }\n        for (String server : serverList) {\n            if (AppDataMigrateEnum.isFileType(redisMigrateEnum)) {\n                if (!isSource) {\n                    continue;\n                }\n                // 检查文件是否存在\n                String filePath = server;\n                String cmd = \"head \" + filePath;\n                try {\n                    String headResult = SSHUtil.execute(migrateMachineIp, cmd);\n                    if (StringUtils.isBlank(headResult)) {\n                        return AppDataMigrateResult.fail(migrateMachineIp + \"上的rdb:\" + filePath + \"不存在或者为空!\");\n                    }\n                } catch (Exception e) {\n                    logger.error(e.getMessage());\n                    return AppDataMigrateResult.fail(migrateMachineIp + \"上的rdb:\" + filePath + \"读取异常!\");\n                }\n            } else {\n                // 1. 检查是否为ip:port格式(简单检查一下，无需正则表达式)\n                // 2. 检查Redis节点是否存在\n                String[] instanceItems = server.split(\":\");\n                if (instanceItems.length != 2) {\n                    return AppDataMigrateResult.fail(\"实例信息\" + server + \"格式错误，必须为ip:port格式\");\n                }\n                String ip = instanceItems[0];\n                String portStr = instanceItems[1];\n                boolean portIsDigit = NumberUtils.isDigits(portStr);\n                if (!portIsDigit) {\n                    return AppDataMigrateResult.fail(server + \"中的port不是整数\");\n                }\n                int port = NumberUtils.toInt(portStr);\n                boolean isRun = redisCenter.isRun(ip, port, redisPassword);\n                if (!isRun) {\n                    return AppDataMigrateResult.fail(server + \"不是存活的或者密码错误!\");\n                }\n            }\n        }\n        return AppDataMigrateResult.success();\n    }\n\n    @Override\n    public AppDataMigrateStatus migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                                        AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, long sourceAppId, long targetAppId,\n                                        String redisSourcePass, String redisTargetPass, long userId, SystemResource resource) {\n        // 1. 生成配置\n        int migrateMachinePort = ConstUtils.REDIS_MIGRATE_TOOL_PORT;\n        String configContent = generateConfig(migrateMachinePort, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum,\n                targetServers, redisSourcePass, redisTargetPass);\n        // 2. 上传配置\n        String timestamp = new SimpleDateFormat(\"yyyyMMddHHmmss\").format(new Date());\n        String confileFileName = \"rmt-\" + timestamp + \".conf\";\n        String logFileName = \"rmt-\" + timestamp + \".log\";\n        boolean uploadConfig = createRemoteFile(migrateMachineIp, confileFileName, configContent);\n        if (!uploadConfig) {\n            return null;\n        }\n        // 3. 开始执行: 指定的配置名、目录、日志名\n        String cmd = ConstUtils.getRedisMigrateToolCmd(resource.getName()) + \" -c \" + ConstUtils.getRedisMigrateToolDir() + confileFileName\n                + \" -o \" + ConstUtils.getRedisMigrateToolDir() + logFileName + \" -d\";\n        logger.warn(cmd);\n        try {\n            SSHUtil.execute(migrateMachineIp, cmd);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n        // 4. 记录执行记录\n        AppDataMigrateStatus appDataMigrateStatus = new AppDataMigrateStatus();\n        appDataMigrateStatus.setMigrateId(timestamp);\n        appDataMigrateStatus.setMigrateTool(1);\n        appDataMigrateStatus.setMigrateMachineIp(migrateMachineIp);\n        appDataMigrateStatus.setMigrateMachinePort(migrateMachinePort);\n        appDataMigrateStatus.setStartTime(new Date());\n        appDataMigrateStatus.setSourceMigrateType(sourceRedisMigrateEnum.getIndex());\n        appDataMigrateStatus.setSourceServers(sourceServers);\n        appDataMigrateStatus.setTargetMigrateType(targetRedisMigrateEnum.getIndex());\n        appDataMigrateStatus.setTargetServers(targetServers);\n        appDataMigrateStatus.setLogPath(ConstUtils.getRedisMigrateToolDir() + logFileName);\n        appDataMigrateStatus.setConfigPath(ConstUtils.getRedisMigrateToolDir() + confileFileName);\n        appDataMigrateStatus.setUserId(userId);\n        appDataMigrateStatus.setSourceAppId(sourceAppId);\n        appDataMigrateStatus.setTargetAppId(targetAppId);\n        appDataMigrateStatus.setStatus(AppDataMigrateStatusEnum.PREPARE.getStatus());\n\n        appDataMigrateStatusDao.save(appDataMigrateStatus);\n        return appDataMigrateStatus;\n    }\n\n    /**\n     * 生成配置\n     *\n     * @param sourceRedisMigrateEnum\n     * @param sourceServers\n     * @param targetRedisMigrateEnum\n     * @param targetServers\n     * @return\n     */\n    public String generateConfig(int listenPort, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                                 AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, String redisSourcePass, String redisTargetPass) {\n        // source\n        StringBuffer config = new StringBuffer();\n        config.append(\"[source]\" + ConstUtils.NEXT_LINE);\n        config.append(\"type: \" + sourceRedisMigrateEnum.getType() + ConstUtils.NEXT_LINE);\n        config.append(\"servers:\" + ConstUtils.NEXT_LINE);\n        List<String> sourceServerList = Arrays.asList(sourceServers.split(ConstUtils.NEXT_LINE));\n        for (String server : sourceServerList) {\n            config.append(\" - \" + server + ConstUtils.NEXT_LINE);\n        }\n        if (StringUtils.isNotBlank(redisSourcePass)) {\n            config.append(\"redis_auth: \" + redisSourcePass + ConstUtils.NEXT_LINE);\n        }\n        config.append(ConstUtils.NEXT_LINE);\n        // target\n        config.append(\"[target]\" + ConstUtils.NEXT_LINE);\n        config.append(\"type: \" + targetRedisMigrateEnum.getType() + ConstUtils.NEXT_LINE);\n        if (!AppDataMigrateEnum.isFileType(targetRedisMigrateEnum)) {\n            config.append(\"servers:\" + ConstUtils.NEXT_LINE);\n            List<String> targetServerList = Arrays.asList(targetServers.split(ConstUtils.NEXT_LINE));\n            for (String server : targetServerList) {\n                config.append(\" - \" + server + ConstUtils.NEXT_LINE);\n            }\n            if (StringUtils.isNotBlank(redisTargetPass)) {\n                config.append(\"redis_auth: \" + redisTargetPass + ConstUtils.NEXT_LINE);\n            }\n            config.append(ConstUtils.NEXT_LINE);\n        }\n        // common:使用最简配置\n        config.append(\"[common]\" + ConstUtils.NEXT_LINE);\n        config.append(\"listen: 0.0.0.0:\" + listenPort + ConstUtils.NEXT_LINE);\n        config.append(\"dir: \" + ConstUtils.getRedisMigrateToolDir());\n        return config.toString();\n    }\n\n    /**\n     * 创建远程文件\n     *\n     * @param host\n     * @param fileName\n     * @param content\n     */\n    public boolean createRemoteFile(String host, String fileName, String content) {\n        /**\n         * 1. 创建本地文件\n         */\n        // 确认目录\n        String localAbsolutePath = MachineProtocol.TMP_DIR + fileName;\n        File tmpDir = new File(MachineProtocol.TMP_DIR);\n        if (!tmpDir.exists()) {\n            if (!tmpDir.mkdirs()) {\n                logger.error(\"cannot create /tmp/cachecloud directory.\");\n            }\n        }\n        Path path = Paths.get(MachineProtocol.TMP_DIR + fileName);\n        // 将配置文件的内容写到本地\n        BufferedWriter bufferedWriter = null;\n        try {\n            bufferedWriter = Files.newBufferedWriter(path, Charset.forName(MachineProtocol.ENCODING_UTF8));\n            bufferedWriter.write(content);\n        } catch (IOException e) {\n            logger.error(\"write rmt file error, ip: {}, filename: {}, content: {}\", host, fileName, content, e);\n            return false;\n        } finally {\n            if (bufferedWriter != null) {\n                try {\n                    bufferedWriter.close();\n                } catch (IOException e) {\n                    logger.error(e.getMessage(), e);\n                }\n            }\n        }\n        /**\n         * 2. 将配置文件推送到目标机器上\n         */\n        try {\n            SSHUtil.scpFileToRemote(host, localAbsolutePath, ConstUtils.getRedisMigrateToolDir());\n        } catch (SSHException e) {\n            logger.error(\"scp rmt file to remote server error: ip: {}, fileName: {}\", host, fileName, e);\n            return false;\n        }\n        /**\n         * 3. 删除临时文件\n         */\n        File file = new File(localAbsolutePath);\n        if (file.exists()) {\n            boolean del = file.delete();\n            if (!del) {\n                logger.warn(\"file.delete:{}\", del);\n            }\n        }\n        return true;\n    }\n\n\n    @Override\n    public CommandResult sampleCheckData(long id, int nums) {\n        /*AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return null;\n        }\n        String ip = appDataMigrateStatus.getMigrateMachineIp();\n        String configPath = appDataMigrateStatus.getConfigPath();\n        String sampleCheckDataCmd = ConstUtils.getRedisMigrateToolCmd() + \" -c \" + configPath + \" -C\" + \" 'redis_check \" + nums + \"'\";\n        logger.warn(\"sampleCheckDataCmd: {}\", sampleCheckDataCmd);\n        try {\n            return new CommandResult(sampleCheckDataCmd, SSHUtil.execute(ip, sampleCheckDataCmd));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return new CommandResult(sampleCheckDataCmd, ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n        }*/\n        return null;\n    }\n\n    @Override\n    public AppDataMigrateResult stopMigrate(long id) {\n        // 获取基本信息\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return AppDataMigrateResult.fail(\"id=\" + id + \"迁移记录不存在!\");\n        }\n        // 获取进程号\n        String migrateMachineIp = appDataMigrateStatus.getMigrateMachineIp();\n        String migrateMachineHostPort = migrateMachineIp + \":\" + appDataMigrateStatus.getMigrateMachinePort();\n        Map<RedisMigrateToolConstant, Map<String, Object>> redisMigrateToolStatMap = appDataMigrateCenter.showMiragteToolProcess(id);\n        if (MapUtils.isEmpty(redisMigrateToolStatMap)) {\n            return AppDataMigrateResult.fail(\"获取\" + migrateMachineHostPort + \"相关信息失败，可能是进程不存在或者客户端超时，请查找原因或重试!\");\n        }\n        Map<String, Object> serverMap = redisMigrateToolStatMap.get(RedisMigrateToolConstant.Server);\n        int pid = MapUtils.getInteger(serverMap, \"process_id\", -1);\n        if (pid <= 0) {\n            return AppDataMigrateResult.fail(\"获取\" + migrateMachineHostPort + \"的进程号\" + pid + \"异常\");\n        }\n        // 确认进程号是redis-migrate-tool进程\n        BooleanEnum exist = checkPidWhetherIsRmt(migrateMachineIp, pid);\n        if (exist == BooleanEnum.OTHER) {\n            return AppDataMigrateResult.fail(\"执行过程中发生异常,请查看系统日志!\");\n        } else if (exist == BooleanEnum.FALSE) {\n            return AppDataMigrateResult.fail(migrateMachineIp + \"进程号\" + pid + \"不存在,请确认!\");\n        }\n        // kill掉进程\n        try {\n            String cmd = \"kill \" + pid;\n            SSHUtil.execute(migrateMachineIp, cmd);\n            exist = checkPidWhetherIsRmt(migrateMachineIp, pid);\n            if (exist == BooleanEnum.OTHER) {\n                return AppDataMigrateResult.fail(ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n            } else if (exist == BooleanEnum.FALSE) {\n                // 更新记录完成更新\n                appDataMigrateStatusDao.updateStatus(id, AppDataMigrateStatusEnum.END.getStatus());\n                return AppDataMigrateResult.success(\"已经成功停止了id=\" + id + \"的迁移任务\");\n            } else {\n                return AppDataMigrateResult.fail(migrateMachineIp + \"进程号\" + pid + \"仍然存在,没有kill掉,请确认!\");\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage());\n            return AppDataMigrateResult.fail(ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n        }\n    }\n\n    @Override\n    public String getAppInstanceListForRedisMigrateTool(long appId) {\n        AppDesc appDesc = appService.getByAppId(appId);\n        StringBuffer instances = new StringBuffer();\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        if (CollectionUtils.isNotEmpty(instanceList)) {\n            for (int i = 0; i < instanceList.size(); i++) {\n                InstanceInfo instanceInfo = instanceList.get(i);\n                if (instanceInfo == null) {\n                    continue;\n                }\n                if (instanceInfo.isOffline()) {\n                    continue;\n                }\n                // 如果是sentinel类型的应用只出master\n                if (TypeUtil.isRedisSentinel(appDesc.getType())) {\n                    if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\n                        continue;\n                    }\n                    if (redisCenter.isMaster(appId, instanceInfo.getIp(), instanceInfo.getPort()) != BooleanEnum.TRUE) {\n                        continue;\n                    }\n                }\n                instances.append(instanceInfo.getIp() + \":\" + instanceInfo.getPort());\n                if (i != instanceList.size() - 1) {\n                    instances.append(ConstUtils.NEXT_LINE);\n                }\n            }\n        }\n        return instances.toString();\n    }\n\n    /**\n     * 检查pid是否是redis-migrate-tool进程\n     *\n     * @param migrateMachineIp\n     * @param pid\n     * @return\n     * @throws SSHException\n     */\n    private BooleanEnum checkPidWhetherIsRmt(String migrateMachineIp, int pid) {\n        try {\n            String cmd = \"/bin/ps -ef | grep redis-migrate-tool | grep -v grep | grep \" + pid;\n            String response = SSHUtil.execute(migrateMachineIp, cmd);\n            if (StringUtils.isNotBlank(response)) {\n                return BooleanEnum.TRUE;\n            } else {\n                return BooleanEnum.FALSE;\n            }\n        } catch (SSHException e) {\n            logger.error(e.getMessage(), e);\n            return BooleanEnum.OTHER;\n        }\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisShakeCenterImpl.java",
    "content": "package com.sohu.cache.stats.app.impl;\n\nimport com.google.common.base.Joiner;\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.AppDataMigrateStatusDao;\nimport com.sohu.cache.entity.AppDataMigrateStatus;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.ssh.SSHTemplate;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.stats.app.RedisShakeCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.service.AppService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by rucao on 2019/10/23\n */\n@Slf4j\n@Service\npublic class RedisShakeCenterImpl implements RedisShakeCenter {\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private RedisCenter redisCenter;\n    @Autowired\n    private SSHService sshService;\n    @Autowired\n    private AppDataMigrateStatusDao appDataMigrateStatusDao;\n\n    @Override\n    public AppDataMigrateResult check(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, String redisSourcePass, String redisTargetPass, SystemResource resource) {\n        // 1. 检查migrateMachineIp是否安装\n        AppDataMigrateResult migrateMachineResult = checkRedisShakeMachine(migrateMachineIp, resource);\n        if (!migrateMachineResult.isSuccess()) {\n            return migrateMachineResult;\n        }\n\n        // 2. 检查源配置\n        AppDataMigrateResult sourceResult = checkMigrateConfigOfRedisShake(sourceRedisMigrateEnum, sourceServers, redisSourcePass);\n        if (!sourceResult.isSuccess()) {\n            return sourceResult;\n        }\n\n        // 3. 检查目标\n        AppDataMigrateResult targetResult = checkMigrateConfigOfRedisShake(targetRedisMigrateEnum, targetServers, redisTargetPass);\n        if (!targetResult.isSuccess()) {\n            return targetResult;\n        }\n\n        return AppDataMigrateResult.success();\n    }\n\n    @Override\n    public AppDataMigrateStatus migrate(String migrateMachineIp, int source_rdb_parallel, int parallel,\n                                        AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                                        AppDataMigrateEnum targetRedisMigrateEnum, String targetServers,\n                                        long sourceAppId, long targetAppId,\n                                        String redisSourcePass, String redisTargetPass,\n                                        String redisSourceVersion, String redisTargetVersion,\n                                        long userId, SystemResource resource) {\n        String timestamp = new SimpleDateFormat(\"yyyyMMddHHmmss\").format(new Date());\n        // 1. 生成配置\n        String configContent = generateRedisShakeConfig(timestamp, source_rdb_parallel, parallel,\n                sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum,\n                targetServers, redisSourcePass, redisTargetPass, resource);\n        // 2. 上传配置\n        String fileName = \"redis-shake-\";\n        String confileFileName = fileName + timestamp + \".conf\";\n        String logFileName = fileName + timestamp + \".log\";\n        boolean uploadConfig = createRemoteFile(migrateMachineIp, confileFileName, configContent, resource);\n        if (!uploadConfig) {\n            return null;\n        }\n        // 3. 开始执行: 指定的配置名、目录、日志名\n        String cmd = \"nohup \" + ConstUtils.getRedisShakeLinuxCmd(resource.getName()) + \" -conf=\" + ConstUtils.getRedisShakeConfDir(resource.getName()) + confileFileName + \" -type=sync > \"+ConstUtils.REDIS_INSTALL_BASE_DIR+\"/shake.out 2>&1 &\";\n\n        log.warn(cmd);\n        try {\n            SSHTemplate.Result cmdResult = sshService.executeWithResult(migrateMachineIp, cmd);\n\n            // 4. 记录执行记录\n            if (cmdResult.isSuccess()) {\n                AppDataMigrateStatus appDataMigrateStatus = new AppDataMigrateStatus();\n                appDataMigrateStatus.setMigrateId(timestamp);\n                appDataMigrateStatus.setMigrateTool(0);\n                appDataMigrateStatus.setMigrateMachineIp(migrateMachineIp);\n                appDataMigrateStatus.setStartTime(new Date());\n                appDataMigrateStatus.setSourceMigrateType(sourceRedisMigrateEnum.getIndex());\n                appDataMigrateStatus.setSourceServers(sourceServers);\n                appDataMigrateStatus.setTargetMigrateType(targetRedisMigrateEnum.getIndex());\n                appDataMigrateStatus.setTargetServers(targetServers);\n                appDataMigrateStatus.setLogPath(ConstUtils.getRedisShakeLogsDir(resource.getName()) + logFileName);\n                appDataMigrateStatus.setConfigPath(ConstUtils.getRedisShakeConfDir(resource.getName()) + confileFileName);\n                appDataMigrateStatus.setUserId(userId);\n                appDataMigrateStatus.setSourceAppId(sourceAppId);\n                appDataMigrateStatus.setTargetAppId(targetAppId);\n                appDataMigrateStatus.setRedisSourceVersion(redisSourceVersion);\n                appDataMigrateStatus.setRedisTargetVersion(redisTargetVersion);\n                appDataMigrateStatus.setStatus(AppDataMigrateStatusEnum.PREPARE.getStatus());\n                appDataMigrateStatusDao.save(appDataMigrateStatus);\n                return appDataMigrateStatus;\n            } else {\n                return null;\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public AppDataMigrateResult stopMigrate(long id) {\n        // 获取基本信息\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return AppDataMigrateResult.fail(\"id=\" + id + \"迁移记录不存在!\");\n        }\n        String migrateMachineIp = appDataMigrateStatus.getMigrateMachineIp();\n        // redis-shake stop.sh\n        try {\n            String configPath = appDataMigrateStatus.getConfigPath();\n            ConstUtils.REDIS_SHAKE_HOME = configPath.substring(0, configPath.indexOf(\"conf\"));\n\n            String migrateId = appDataMigrateStatus.getMigrateId();\n            String stopCmd = ConstUtils.getRedisShakeStopCmd() + \" \" + String.format(\"%s/pid/redis-shake-%s.pid\", ConstUtils.REDIS_SHAKE_HOME, migrateId);\n            String stopResult = SSHUtil.execute(migrateMachineIp, stopCmd);\n            if (StringUtils.isNotEmpty(stopResult) && stopResult.contains(\"Fail\")) {\n                if (stopResult.contains(\"No process number\")) {\n                    String migrateLog = showProcess(id);\n                    if (StringUtils.isNotEmpty(migrateId) && migrateLog.contains(RedisShakeEnum.LOG_ERROR.getKeyword())) {\n                        appDataMigrateStatusDao.updateStatus(id, AppDataMigrateStatusEnum.ERROR.getStatus());\n                        return AppDataMigrateResult.fail(\"迁移任务migrate id:\" + migrateId + \"异常，任务终止\");\n                    } else {\n                        appDataMigrateStatusDao.updateStatus(id, AppDataMigrateStatusEnum.END.getStatus());\n                        return AppDataMigrateResult.fail(\"迁移任务migrate id:\" + migrateId + \"已经停止\");\n                    }\n                }\n                return AppDataMigrateResult.fail(\"迁移任务id:\" + id + stopResult);\n            }\n            appDataMigrateStatusDao.updateStatus(id, AppDataMigrateStatusEnum.END.getStatus());\n            return AppDataMigrateResult.success(\"已经成功停止了id=\" + id + \"的迁移任务\");\n        } catch (Exception e) {\n            log.error(e.getMessage());\n            return AppDataMigrateResult.fail(ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n        }\n    }\n\n    @Override\n    public CommandResult checkData(long id, int batchcount, int comparemode) {\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return null;\n        }\n        String ip = appDataMigrateStatus.getMigrateMachineIp();\n        String sampleCheckDataCmd = generateRedisFullCheckConfig(appDataMigrateStatus, batchcount, comparemode);\n        log.warn(\"checkDataCmd: {}\", sampleCheckDataCmd);\n        try {\n            String checkResult = SSHUtil.execute(ip, sampleCheckDataCmd);\n            if (StringUtils.isNotEmpty(checkResult)) {\n                return new CommandResult(sampleCheckDataCmd, ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n            }\n            return new CommandResult(sampleCheckDataCmd, checkResult);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return new CommandResult(sampleCheckDataCmd, ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n        }\n    }\n\n    @Override\n    public String getAppInstanceListForRedisShake(long appId) {\n        AppDesc appDesc = appService.getByAppId(appId);\n        StringBuffer instances = new StringBuffer();\n        List<InstanceInfo> instanceList = appService.getAppOnlineInstanceInfo(appId);\n        int sentinelFlag = 0;\n        if (CollectionUtils.isNotEmpty(instanceList)) {\n            for (int i = 0; i < instanceList.size(); i++) {\n                InstanceInfo instanceInfo = instanceList.get(i);\n                if (instanceInfo == null) {\n                    continue;\n                }\n                if (instanceInfo.isOffline()) {\n                    continue;\n                }\n                // 如果是sentinel类型的应用\n                // master_name:master/slave\n                // sentinelIp1:sentinelPort1\n                if (TypeUtil.isRedisSentinel(appDesc.getType())) {\n                    if (\"slave\".equals(instanceInfo.getRoleDesc())) {\n                        continue;\n                    } else if (\"sentinel\".equals(instanceInfo.getRoleDesc())) {\n                        if (sentinelFlag == 0) {\n                            String sentinel_master_name = instanceInfo.getCmd() + \":master\" + ConstUtils.NEXT_LINE;\n                            instances.insert(0, sentinel_master_name);\n                            sentinelFlag = 1;\n                        }\n                    } else if (\"master\".equals(instanceInfo.getRoleDesc())) {\n                        continue;\n                    }\n                }\n                if (TypeUtil.isRedisCluster(appDesc.getType())) {\n                    if (\"slave\".equals(instanceInfo.getRoleDesc())) {\n                        continue;\n                    }\n                }\n                instances.append(instanceInfo.getIp() + \":\" + instanceInfo.getPort());\n                if (i != instanceList.size() - 1) {\n                    instances.append(ConstUtils.NEXT_LINE);\n                }\n            }\n        }\n        return instances.toString();\n    }\n\n    @Override\n    public String showProcess(long id) {\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return \"\";\n        }\n        String logPath = appDataMigrateStatus.getLogPath();\n        String host = appDataMigrateStatus.getMigrateMachineIp();\n        StringBuilder command = new StringBuilder();\n        String keyword;\n        String result;\n        try {\n            keyword = RedisShakeEnum.LOG_ERROR.getKeyword();\n            command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n            result = SSHUtil.execute(host, command.toString());\n            if (StringUtils.isNotEmpty(result)) {\n                return RedisShakeEnum.LOG_ERROR.getDescription() + ConstUtils.NEXT_LINE + result;\n            } else {\n                command.delete(0, command.length());\n                keyword = RedisShakeEnum.LOG_FORWARD_COMMANDS.getKeyword();\n                command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n                result = SSHUtil.execute(host, command.toString());\n                if (StringUtils.isNotEmpty(result)) {\n                    return RedisShakeEnum.LOG_FORWARD_COMMANDS.getDescription() + ConstUtils.NEXT_LINE + result;\n                } else {\n                    command.delete(0, command.length());\n                    keyword = RedisShakeEnum.LOG_SYNCING.getKeyword();\n                    command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n                    result = SSHUtil.execute(host, command.toString());\n                    if (StringUtils.isNotEmpty(result)) {\n                        return RedisShakeEnum.LOG_SYNCING.getDescription() + ConstUtils.NEXT_LINE + result;\n                    } else {\n                        command.delete(0, command.length());\n                        keyword = RedisShakeEnum.LOG_WAITING_SOURCE_RDB.getKeyword();\n                        command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n                        result = SSHUtil.execute(host, command.toString());\n                        if (StringUtils.isNotEmpty(result)) {\n                            return RedisShakeEnum.LOG_WAITING_SOURCE_RDB.getDescription() + ConstUtils.NEXT_LINE + result;\n                        }\n                    }\n                }\n            }\n            return result;\n        } catch (SSHException e) {\n            log.error(e.getMessage(), e);\n            return \"\";\n        }\n    }\n\n\n    public int updateProcess(long id) {\n        AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.get(id);\n        if (appDataMigrateStatus == null) {\n            return 0;\n        }\n        int migrateStatus = appDataMigrateStatus.getStatus();\n        String logPath = appDataMigrateStatus.getLogPath();\n        String host = appDataMigrateStatus.getMigrateMachineIp();\n        StringBuilder command = new StringBuilder();\n        String keyword;\n        String result;\n        try {\n            int logStatus = migrateStatus;\n            keyword = RedisShakeEnum.LOG_ERROR.getKeyword();\n            command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n            result = SSHUtil.execute(host, command.toString());\n            if (StringUtils.isNotEmpty(result)) {\n                logStatus = AppDataMigrateStatusEnum.ERROR.getStatus();\n            } else {\n                command.delete(0, command.length());\n                keyword = RedisShakeEnum.LOG_FORWARD_COMMANDS.getKeyword();\n                command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n                result = SSHUtil.execute(host, command.toString());\n                if (StringUtils.isNotEmpty(result)) {\n                    logStatus = AppDataMigrateStatusEnum.FULL_END.getStatus();\n                } else {\n                    command.delete(0, command.length());\n                    keyword = RedisShakeEnum.LOG_SYNCING.getKeyword();\n                    command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n                    result = SSHUtil.execute(host, command.toString());\n                    if (StringUtils.isNotEmpty(result)) {\n                        logStatus = AppDataMigrateStatusEnum.START.getStatus();\n                    } else {\n                        command.delete(0, command.length());\n                        keyword = RedisShakeEnum.LOG_WAITING_SOURCE_RDB.getKeyword();\n                        command.append(\"grep \").append(keyword).append(\" \").append(logPath).append(\" | tail -n 10\").toString();\n                        result = SSHUtil.execute(host, command.toString());\n                        if (StringUtils.isNotEmpty(result)) {\n                            logStatus = AppDataMigrateStatusEnum.PREPARE.getStatus();\n                        }\n                    }\n                }\n            }\n            if (migrateStatus != logStatus) {\n                return appDataMigrateStatusDao.updateStatus(id, logStatus);\n            }\n\n        } catch (SSHException e) {\n            log.error(e.getMessage(), e);\n        }\n        return 0;\n    }\n\n    /**\n     * 检查用redis-shake迁移的机器是否正常\n     *\n     * @param migrateMachineIp\n     * @return\n     */\n    private AppDataMigrateResult checkRedisShakeMachine(String migrateMachineIp, SystemResource resource) {\n        if (StringUtils.isBlank(migrateMachineIp)) {\n            return AppDataMigrateResult.fail(\"redis-migrate-tool所在机器的IP不能为空\");\n        }\n        // 1. 检查机器是否存在在机器列表中\n        /*try {\n            MachineInfo machineInfo = machineCenter.getMachineInfoByIp(migrateMachineIp);\n            if (machineInfo == null) {\n                return AppDataMigrateResult.fail(migrateMachineIp + \"没有在机器管理列表中\");\n            } else if (machineInfo.isOffline()) {\n                return AppDataMigrateResult.fail(migrateMachineIp + \",该机器已经被删除\");\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return AppDataMigrateResult.fail(\"检测发生异常，请观察日志\");\n        }*/\n        //2. 检查是否安装redis-shake\n        /*try {\n            String cmd = ConstUtils.getRedisShakeLinuxCmd(resource.getName());\n            String response = SSHUtil.execute(migrateMachineIp, cmd);\n            if (StringUtils.isBlank(response) || !response.contains(\"start.sh\") || !response.contains(\"conf\")) {\n                return AppDataMigrateResult.fail(migrateMachineIp + \"下，\" + cmd + \"执行失败，请确保redis-shake安装正确!\");\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return AppDataMigrateResult.fail(\"检测发生异常，请观察日志\");\n        }*/\n        //3. 查看进程是数量\n        try {\n            String cmd = \"ps -ef | grep redis-shake | grep -v grep | grep -v tail\";\n            String response = SSHUtil.execute(migrateMachineIp, cmd);\n            if (StringUtils.isNotEmpty(response)) {\n                String[] redis_shake_count = response.split(ConstUtils.NEXT_LINE);\n                if (redis_shake_count.length >= 3) {\n                    return AppDataMigrateResult.fail(migrateMachineIp + \"下有\" + redis_shake_count.length + \"个redis-shake进程，\" +\n                            \"请切换迁移机器或等待其他迁移任务执行完毕后重试\");\n                }\n            }\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n            return AppDataMigrateResult.fail(\"检测发生异常，请观察日志\");\n        }\n        return AppDataMigrateResult.success();\n    }\n\n    /**\n     * redis-shake 检测配置\n     *\n     * @param redisMigrateEnum\n     * @param servers\n     * @param redisPassword\n     * @return\n     */\n    private AppDataMigrateResult checkMigrateConfigOfRedisShake(AppDataMigrateEnum redisMigrateEnum,\n                                                                String servers, String redisPassword) {\n        List<String> serverList = Arrays.asList(servers.split(ConstUtils.NEXT_LINE));\n        if (CollectionUtils.isEmpty(serverList)) {\n            return AppDataMigrateResult.fail(\"服务器信息格式有问题!\");\n        }\n        for (int i = 0; i < serverList.size(); i++) {\n            // 1. 检查是否为ip:port格式(简单检查一下，无需正则表达式)\n            // 2. 检查Redis节点是否存在\n            String server = serverList.get(i);\n            String[] instanceItems = server.split(\":\");\n            if (instanceItems.length != 2) {\n                return AppDataMigrateResult.fail(\"实例信息\" + server + \"格式错误，必须为ip:port格式\");\n            }\n            if (redisMigrateEnum == AppDataMigrateEnum.sentinel && i == 0) {\n                String sentinel_master_name = instanceItems[0];\n                String master_or_slave = instanceItems[1];\n                if (!(master_or_slave.equalsIgnoreCase(\"master\") || master_or_slave.equalsIgnoreCase(\"slave\"))) {\n                    return AppDataMigrateResult.fail(server + \"不是sentinel_master_name:master_or_slave格式\");\n                }\n            } else {\n                String ip = instanceItems[0];\n                String portStr = instanceItems[1];\n                boolean portIsDigit = NumberUtils.isDigits(portStr);\n                if (!portIsDigit) {\n                    return AppDataMigrateResult.fail(server + \"中的port不是整数\");\n                }\n                int port = NumberUtils.toInt(portStr);\n                boolean isRun = redisCenter.isRun(ip, port, redisPassword);\n                if (!isRun) {\n                    return AppDataMigrateResult.fail(server + \"不是存活的或者密码错误!\");\n                }\n            }\n        }\n        return AppDataMigrateResult.success();\n    }\n\n    /**\n     * 生成redis-shake配置\n     *\n     * @param migrateId\n     * @param sourceRedisMigrateEnum\n     * @param sourceServers\n     * @param targetRedisMigrateEnum\n     * @param targetServers\n     * @param redisSourcePass\n     * @param redisTargetPass\n     * @return\n     */\n    private String generateRedisShakeConfig(String migrateId, int source_rdb_parallel, int parallel,\n                                            AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers,\n                                            AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, String redisSourcePass, String redisTargetPass,\n                                            SystemResource resource) {\n        StringBuffer config = new StringBuffer();\n        config.append(\"id = redis-shake-\" + migrateId + ConstUtils.NEXT_LINE);\n        config.append(\"log.file = \" + ConstUtils.getRedisShakeLogsDir(resource.getName()) + \"redis-shake-\" + migrateId + \".log\" + ConstUtils.NEXT_LINE);\n        config.append(\"pid_path = \" + ConstUtils.getRedisShakePidDir(resource.getName()) + ConstUtils.NEXT_LINE);\n        config.append(\"rewrite = true\" + ConstUtils.NEXT_LINE);\n        config.append(\"metric = false\" + ConstUtils.NEXT_LINE);\n        //config.append(\"http_profile = \" + ConstUtils.getRedisShakeHttpPort() + ConstUtils.NEXT_LINE);\n        config.append(\"http_profile = -1\" + ConstUtils.NEXT_LINE);\n        config.append(\"metric.print_log = false\" + ConstUtils.NEXT_LINE);\n        config.append(\"parallel = \" + parallel + ConstUtils.NEXT_LINE);\n        config.append(\"source.rdb.parallel = \" + source_rdb_parallel + ConstUtils.NEXT_LINE);\n\n        //source\n        config.append(\"source.type = \" + sourceRedisMigrateEnum.getType() + ConstUtils.NEXT_LINE);\n        config.append(\"source.address = \" + formatAddress(sourceServers, sourceRedisMigrateEnum.getIndex()) + ConstUtils.NEXT_LINE);\n        config.append(\"source.auth_type = auth\" + ConstUtils.NEXT_LINE);\n        config.append(\"source.password_raw = \" + redisSourcePass + ConstUtils.NEXT_LINE);\n\n        //target\n        config.append(\"target.type = \" + targetRedisMigrateEnum.getType() + ConstUtils.NEXT_LINE);\n        config.append(\"target.address = \" + formatAddress(targetServers, targetRedisMigrateEnum.getIndex()) + ConstUtils.NEXT_LINE);\n        config.append(\"target.auth_type = auth\" + ConstUtils.NEXT_LINE);\n        config.append(\"target.password_raw = \" + redisTargetPass + ConstUtils.NEXT_LINE);\n\n        return config.toString();\n    }\n\n    private String formatAddress(String servers, int migrateType) {\n        List<String> sourceServerList = Arrays.asList(servers.split(ConstUtils.NEXT_LINE));\n        String address = Joiner.on(ConstUtils.SEMICOLON).join(sourceServerList);\n        if (migrateType == AppDataMigrateEnum.sentinel.getIndex()) {\n            return address.replaceFirst(ConstUtils.SEMICOLON, ConstUtils.AT);\n        }\n        return address;\n    }\n\n    private String formatAddressForRedisFullCheck(long appId, String servers, int migrateType) {\n        if (migrateType == AppDataMigrateEnum.sentinel.getIndex()) {\n            List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n            if (CollectionUtils.isNotEmpty(instanceList)) {\n                for (int i = 0; i < instanceList.size(); i++) {\n                    InstanceInfo instanceInfo = instanceList.get(i);\n                    if (\"master\".equals(instanceInfo.getRoleDesc())) {\n                        return instanceInfo.getIp() + \":\" + instanceInfo.getPort();\n                    }\n                }\n            }\n            return \"\";\n        } else {\n            return formatAddress(servers, migrateType);\n        }\n    }\n\n    private String generateRedisFullCheckConfig(AppDataMigrateStatus appDataMigrateStatus, int nums, int comparemode) {\n        StringBuffer cmd = new StringBuffer();\n        String migrateId = appDataMigrateStatus.getMigrateId();\n        cmd.append(ConstUtils.getRedisFullCheckCmd() + ConstUtils.SPACE);// 异步命令\n        cmd.append(\"-s \" + \"\\\"\" + formatAddressForRedisFullCheck(appDataMigrateStatus.getSourceAppId(), appDataMigrateStatus.getSourceServers(), appDataMigrateStatus.getSourceMigrateType()) + \"\\\"\" + ConstUtils.SPACE);\n        cmd.append(\"-p \" + appService.getByAppId(appDataMigrateStatus.getSourceAppId()).getAppPassword() + ConstUtils.SPACE);\n        cmd.append(\"--sourcedbtype=\" + getCompareType(appDataMigrateStatus.getSourceMigrateType()) + ConstUtils.SPACE);//源库的类别，0：db(standalone单节点、主从)，1: cluster（集群版），2: 阿里云\n        cmd.append(\"-t \" + \"\\\"\" + formatAddressForRedisFullCheck(appDataMigrateStatus.getTargetAppId(), appDataMigrateStatus.getTargetServers(), appDataMigrateStatus.getTargetMigrateType()) + \"\\\"\" + ConstUtils.SPACE);\n        cmd.append(\"-a \" + appService.getByAppId(appDataMigrateStatus.getTargetAppId()).getAppPassword() + ConstUtils.SPACE);\n        cmd.append(\"--targetdbtype=\" + getCompareType(appDataMigrateStatus.getTargetMigrateType()) + ConstUtils.SPACE);\n        cmd.append(\"--comparetimes=1\" + ConstUtils.SPACE);//比较轮数\n        cmd.append(\"-m \" + comparemode + ConstUtils.SPACE);//比较模式，1表示全量比较，2表示只对比value的长度，3只对比key是否存在，4全量比较的情况下，忽略大key的比较\n        cmd.append(\"--batchcount=\" + nums + ConstUtils.SPACE);//批量聚合的数量\n        cmd.append(\"--log=\" + ConstUtils.getRedisFullCheckResultDir() + \"log-\" + migrateId + \".log\" + ConstUtils.SPACE);//log文件\n        //cmd.append(\"--metric=\" + ConstUtils.getRedisFullCheckResultDir() + \"metric-\" + migrateId + ConstUtils.SPACE); // todo metric文件，报错\n        cmd.append(\"-d \" + ConstUtils.getRedisFullCheckResultDir() + \"key-\" + migrateId + \".db\" + ConstUtils.SPACE); //异常数据列表保存的文件名称\n        cmd.append(\"--result=\" + ConstUtils.getRedisFullCheckResultDir() + \"result-\" + migrateId + \".log\" + ConstUtils.SPACE);//不一致结果记录到result文件中，格式：'db diff-type key field';\n        return cmd.toString();\n    }\n\n    private int getCompareType(int migrateType) {\n        if (migrateType == AppDataMigrateEnum.standalone.getIndex()\n                || migrateType == AppDataMigrateEnum.sentinel.getIndex()\n                || migrateType == AppDataMigrateEnum.REDIS_NODE.getIndex()) {\n            return 0;\n        } else if (migrateType == AppDataMigrateEnum.cluster.getIndex()) {\n            return 1;\n        }\n        return -1;\n    }\n\n    /**\n     * 创建远程文件\n     *\n     * @param host\n     * @param fileName\n     * @param content\n     */\n    public boolean createRemoteFile(String host, String fileName, String content, SystemResource resource) {\n        /**\n         * 1. 创建本地文件\n         */\n        // 确认目录\n        String localAbsolutePath = MachineProtocol.TMP_DIR + fileName;\n        File tmpDir = new File(MachineProtocol.TMP_DIR);\n        if (!tmpDir.exists()) {\n            if (!tmpDir.mkdirs()) {\n                log.error(\"cannot create /tmp/cachecloud directory.\");\n            }\n        }\n        Path path = Paths.get(MachineProtocol.TMP_DIR + fileName);\n        // 将配置文件的内容写到本地\n        BufferedWriter bufferedWriter = null;\n        try {\n            bufferedWriter = Files.newBufferedWriter(path, Charset.forName(MachineProtocol.ENCODING_UTF8));\n            bufferedWriter.write(content);\n        } catch (IOException e) {\n            log.error(\"write rmt file error, ip: {}, filename: {}, content: {}\", host, fileName, content, e);\n            return false;\n        } finally {\n            if (bufferedWriter != null) {\n                try {\n                    bufferedWriter.close();\n                } catch (IOException e) {\n                    log.error(e.getMessage(), e);\n                }\n            }\n        }\n\n        /**\n         * 2. 将配置文件推送到目标机器上\n         */\n        try {\n            String redisShakeConfDir = ConstUtils.getRedisShakeConfDir(resource.getName());\n            String redisShakeLogDir = ConstUtils.getRedisShakeLogsDir(resource.getName());\n            String redisShakePidDir = ConstUtils.getRedisShakePidDir(resource.getName());\n            SSHUtil.execute(host, String.format(\"mkdir -p %s %s %s\", redisShakeConfDir, redisShakeLogDir, redisShakePidDir));\n            SSHUtil.scpFileToRemote(host, localAbsolutePath, redisShakeConfDir);\n        } catch (SSHException e) {\n            log.error(\"scp rmt file to remote server error: ip: {}, fileName: {}\", host, fileName, e);\n            return false;\n        }\n\n        /**\n         * 3. 删除临时文件\n         */\n        File file = new File(localAbsolutePath);\n        if (file.exists()) {\n            boolean del = file.delete();\n            if (!del) {\n                log.warn(\"file.delete:{}\", del);\n            }\n        }\n\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/instance/InstanceAlertConfigService.java",
    "content": "package com.sohu.cache.stats.instance;\r\n\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\nimport com.sohu.cache.entity.InstanceAlertConfig;\r\n\r\n/**\r\n * 实例报警阀值配置\r\n * @author leifu\r\n * @Date 2017年5月19日\r\n * @Time 下午2:12:29\r\n */\r\npublic interface InstanceAlertConfigService {\r\n\r\n    /**\r\n     * 获取所有实例报警配置列表\r\n     * @return\r\n     */\r\n    List<InstanceAlertConfig> getAll();\r\n\r\n    /**\r\n     * 根据类型获取实例报警配置列表\r\n     * @param type\r\n     * @return\r\n     */\r\n    List<InstanceAlertConfig> getByType(int type);\r\n\r\n    /**\r\n     * 根据类型获取实例报警配置列表\r\n     * @param type 报警类型\r\n     * @param appType 应用类型\r\n     * @return\r\n     */\r\n    List<InstanceAlertConfig> getByTypeAndAppType(int type, int appType);\r\n\r\n    /**\r\n     * 保存\r\n     * @param instanceAlertConfig 报警实例\r\n     * @return\r\n     */\r\n    int save(InstanceAlertConfig instanceAlertConfig);\r\n\r\n    /**\r\n     * 保存\r\n     * @param instanceAlertConfigList 报警实例列表\r\n     * @return\r\n     */\r\n    int batchSave(List<InstanceAlertConfig> instanceAlertConfigList);\r\n\r\n    /**\r\n     * 根据id获取\r\n     * @param id\r\n     * @return\r\n     */\r\n    InstanceAlertConfig get(int id);\r\n\r\n    /**\r\n     * 根据id删除\r\n     * @param id\r\n     * @return\r\n     */\r\n    int remove(int id);\r\n\r\n    /**\r\n     * 根据alertConfig 获取全局配置，并根据比较类型进行筛选出紧急程度\r\n     * @param alertConfig\r\n     * @param compareType\r\n     * @param appType\r\n     * @return\r\n     */\r\n    InstanceAlertConfig getGlobalAlertConfigByCondition(String alertConfig, int compareType, Integer appType);\r\n\r\n    /**\r\n     * 更新alertValue和checkCycle\r\n     * @param id\r\n     * @param alertValue\r\n     * @param checkCycle\r\n     * @param compareType\r\n     * @param importantLevel\r\n     */\r\n    void update(long id, String alertValue, int checkCycle, int compareType, int importantLevel);\r\n\r\n    /**\r\n     * 根据alertConfig和compareType更新所有报警配置的紧急程度\r\n     * @param alertConfig\r\n     * @param compareType\r\n     * @param importantLevel\r\n     * @param appType\r\n     */\r\n    void updateImportantLevel(String alertConfig, int compareType, int importantLevel, Integer appType);\r\n\r\n    /**\r\n     * 更新配置的最后检测时间\r\n     * @param id\r\n     * @param lastCheckTime\r\n     */\r\n    void updateLastCheckTime(long id, Date lastCheckTime);\r\n\r\n    /**\r\n     * 监控所有Redis上一分钟状态\r\n     */\r\n    void monitorLastMinuteAllInstanceInfo();\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/instance/InstanceDeployCenter.java",
    "content": "package com.sohu.cache.stats.instance;\r\n\r\nimport com.sohu.cache.entity.InstanceAlertValueResult;\r\nimport com.sohu.cache.task.constant.MachineSyncEnum;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * Created by yijunzhang on 14-11-26.\r\n */\r\npublic interface InstanceDeployCenter {\r\n\r\n    /**\r\n     * 启动已经存在的实例\r\n     * @param appId\r\n     * @param instanceId\r\n     * @return\r\n     */\r\n    boolean startExistInstance(long appId, int instanceId);\r\n\r\n    /**\r\n     * 启动已存在的实例，无需进行redis资源包校验\r\n     * @param appId\r\n     * @param instanceId\r\n     * @return\r\n     */\r\n    boolean startExistInstanceWithoutResourceCheck(long appId, int instanceId);\r\n\r\n    /**\r\n     * 下线已经存在的实例\r\n     * @param appId\r\n     * @param instanceId\r\n     * @return\r\n     */\r\n    boolean shutdownExistInstance(long appId, int instanceId);\r\n\r\n    /**\r\n     * cluster forget\r\n     * @param appId\r\n     * @param instanceId\r\n     * @return\r\n     */\r\n    boolean forgetInstance(long appId, int instanceId);\r\n\r\n    /**\r\n     * 清理（cluster forget）集群内所有fail节点\r\n     * @param appId\r\n     * @param instanceId\r\n     * @return\r\n     */\r\n    boolean clearFailInstances(long appId);\r\n    \r\n    /**\r\n     * 展示实例最近的日志\r\n     * @param instanceId\r\n     * @param maxLineNum\r\n     * @return\r\n     */\r\n    String showInstanceRecentLog(int instanceId, int maxLineNum);\r\n\r\n    /**\r\n     * 修改实例配置\r\n     * @param appId\r\n     * @param appAuditId\r\n     * @param host\r\n     * @param port\r\n     * @param instanceConfigKey\r\n     * @param instanceConfigValue\r\n     * @return\r\n     */\r\n    boolean modifyInstanceConfig(long appId, Long appAuditId, String host, int port, String instanceConfigKey,\r\n            String instanceConfigValue);\r\n\r\n    /**\r\n     * 检测pod是否有被调度其他宿主机\r\n     * @param ip\r\n     */\r\n    MachineSyncEnum podChangeStatus(String ip);\r\n\r\n    /**\r\n     * 检测pod是否有心跳停止实例&启动\r\n     * @return\r\n     */\r\n    List<InstanceAlertValueResult> checkAndStartExceptionInstance(String ip, Boolean isAlert);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/instance/InstanceStatsCenter.java",
    "content": "package com.sohu.cache.stats.instance;\r\n\r\nimport com.google.common.collect.Table;\r\nimport com.sohu.cache.entity.InstanceCommandStats;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.entity.InstanceMinuteStats;\r\nimport com.sohu.cache.entity.InstanceStats;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 实例统计方法\r\n * Created by yijunzhang on 14-9-17.\r\n */\r\npublic interface InstanceStatsCenter {\r\n\r\n    /**\r\n     * 根据实例id获取实例的静态信息\r\n     *\r\n     * @param instanceId 实例id\r\n     * @return 实例信息对象\r\n     */\r\n    InstanceInfo getInstanceInfo(long instanceId);\r\n\r\n    /**\r\n     * 根据实例id获取实例的实施统计&是否在线&redis实时统计信息\r\n     *\r\n     * @param instanceId 实例id\r\n     * @return 实例实时状态信息\r\n     */\r\n    InstanceStats getInstanceStats(long instanceId);\r\n\r\n    /**\r\n     * 根据实例id，起止时间，命令名称获取实例对应命令的执行次数曲线\r\n     *\r\n     * @param instanceId  实例id\r\n     * @param beginTime   起始时间 闭区间 yyyyMMddHHmm\r\n     * @param endTime     结束时间 闭区间 yyyyMMddHHmm\r\n     * @param commandName 命令名称\r\n     * @return 实例命令执行对象的列表\r\n     */\r\n    List<InstanceCommandStats> getCommandStatsList(Long instanceId, long beginTime, long endTime, String commandName);\r\n\r\n    /**\r\n     * 根据appId，起止时间，统计指标名称 获取实例对应指标统计列表\r\n     *\r\n     * @param appId  实例id\r\n     * @param beginTime   起始时间 闭区间 yyyyMMddHHmm\r\n     * @param endTime     结束时间 闭区间 yyyyMMddHHmm\r\n     * @param commands 命令名称\r\n     * @return 应用下 所有实例 命令执行对象的列表\r\n     * <br/>\r\n     * Map<Integer, Map<String, List<InstanceCommandStats>>> : Map<InstanceId,Map<CommandName,List<InstanceCommandStats>>>\r\n     */\r\n    Map<Integer, Map<String, List<InstanceCommandStats>>> getStandardStatsList(Long appId, long beginTime, long endTime,\r\n            List<String> commands);\r\n\r\n\r\n    /**\r\n     * 根据appId，起止时间，统计指标名称 获取实例对应指标统计列表\r\n     *\r\n     * @param appId  实例id\r\n     * @param beginTime   起始时间 闭区间 yyyyMMddHHmm\r\n     * @param endTime     结束时间 闭区间 yyyyMMddHHmm\r\n     * @param commands 命令名称\r\n     * @return 应用下 所有实例统计信息\r\n     * <br/>\r\n     * Table<Integer, String, Map<String, List<InstanceMinuteStats>>>\r\n     */\r\n    Table<Integer, String, Map<String, List<InstanceMinuteStats>>> getInstanceMinuteStatsList(Long appId, long beginTime, long endTime,\r\n                                                                                              List<String> commands);\r\n\r\n    /**\r\n     * 在实例上执行命令\r\n     *\r\n     * @param host\r\n     * @param port\r\n     * @param command\r\n     * @return\r\n     */\r\n    public String executeCommand(String host, int port, String command);\r\n\r\n    /**\r\n     * 在实例上执行命令\r\n     *\r\n     * @param instanceId\r\n     * @param command\r\n     * @return\r\n     */\r\n    public String executeCommand(Long instanceId, String command);\r\n\r\n    /**\r\n     * 获取所有统计信息\r\n     * @return\r\n     */\r\n    public List<InstanceStats> getInstanceStats();\r\n\r\n    /**\r\n     * 按照机器获取实例列表\r\n     * @param ip\r\n     * @return\r\n     */\r\n    List<InstanceStats> getInstanceStats(String ip);\r\n\r\n    /**\r\n     * 将一个对象（如统计信息）保存到mysql里\r\n     *\r\n     * @param infoMap 统计信息对象\r\n     * @param clusterInfoMap clusterinfo统计信息\r\n     * @param ip      ip\r\n     * @param port    port\r\n     * @param dbType\r\n     * @return 成功保存返回true，否则返回false\r\n     */\r\n    public boolean saveStandardStats(Map<String, Object> infoMap, Map<String, Object> clusterInfoMap, String ip, int port, String dbType);\r\n\r\n    /**\r\n     * 根据收集时间查询某一个实例或机器的统计信息\r\n     *\r\n     * @param collectTime 时间点，格式：yyyyMMddHHmm\r\n     * @param ip          ip\r\n     * @param port        port\r\n     * @param dbType\r\n     * @return 该时间点对应的统计信息\r\n     */\r\n    public Map<String, Object> queryStandardInfoMap(long collectTime, String ip, int port, String dbType);\r\n\r\n    /**\r\n     * 查询一段时间内，实例或机器的统计信息的列表，如实例或机器一天中每分钟的统计数据\r\n     *\r\n     * @param beginTime 起始时间，格式：yyyyMMddHHmm\r\n     * @param endTime   结束时间，格式：yyyyMMddHHmm\r\n     * @param ip        ip\r\n     * @param port      port\r\n     * @param dbType\r\n     * @return 该时间区间内，对应的统计信息的列表\r\n     */\r\n    public List<Map<String, Object>> queryDiffMapList(long beginTime, long endTime, String ip, int port, String dbType);\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/instance/impl/InstanceAlertConfigServiceImpl.java",
    "content": "package com.sohu.cache.stats.instance.impl;\n\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.alert.bean.AlertConfigBaseData;\nimport com.sohu.cache.alert.strategy.*;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.InstanceAlertConfigDao;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.dao.StandardStatsDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.redis.enums.AppTypeToAlertTypeEnum;\nimport com.sohu.cache.redis.enums.InstanceAlertStatusEnum;\nimport com.sohu.cache.redis.enums.InstanceAlertTypeEnum;\nimport com.sohu.cache.redis.enums.RedisAlertConfigEnum;\nimport com.sohu.cache.stats.instance.InstanceAlertConfigService;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.EnvUtil;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\nimport com.sohu.cache.web.enums.ImportantLevelTypeEnum;\nimport com.sohu.cache.web.service.AppAlertRecordService;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.FreemakerUtils;\nimport freemarker.template.Configuration;\nimport org.apache.commons.beanutils.BeanUtils;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.env.Environment;\nimport org.springframework.stereotype.Service;\n\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * @author leifu\n * @Date 2017年5月19日\n * @Time 下午2:16:36\n */\n@Service(\"instanceAlertConfigService\")\npublic class InstanceAlertConfigServiceImpl implements InstanceAlertConfigService {\n\n    private Logger logger = LoggerFactory.getLogger(InstanceAlertConfigServiceImpl.class);\n    @Autowired\n    private InstanceAlertConfigDao instanceAlertConfigDao;\n    @Autowired\n    private StandardStatsDao standardStatsDao;\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private EmailComponent emailComponent;\n    @Autowired\n    private Configuration configuration;\n    @Autowired\n    private AppDao appDao;\n    @Autowired\n    private UserService userService;\n    @Autowired\n    private Environment environment;\n    @Autowired\n    private AppAlertRecordService appAlertRecordService;\n\n    private static Map<RedisAlertConfigEnum, AlertConfigStrategy> alertConfigStrategyMap = new HashMap<RedisAlertConfigEnum, AlertConfigStrategy>();\n\n    static {\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.aof_current_size, new AofCurrentSizeAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.client_biggest_input_buf, new ClientBiggestInputBufAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.client_longest_output_list, new ClientLongestOutputListAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.instantaneous_ops_per_sec, new InstantaneousOpsPerSecAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.latest_fork_usec, new LatestForkUsecAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.mem_fragmentation_ratio, new MemFragmentationRatioAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.rdb_last_bgsave_status, new RdbLastBgsaveStatusAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.minute_aof_delayed_fsync, new MinuteAofDelayedFsyncAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.minute_rejected_connections, new RdbLastBgsaveStatusAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.minute_sync_partial_err, new MinuteSyncPartialErrAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.minute_sync_partial_ok, new MinuteSyncPartialOkAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.minute_sync_full, new MinuteSyncFullAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.minute_total_net_input_bytes, new MinuteTotalNetInputMBytesAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.minute_total_net_output_bytes, new MinuteTotalNetOutputMBytesAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.master_slave_offset_diff, new MasterSlaveOffsetAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.cluster_state, new ClusterStateAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.cluster_slots_ok, new ClusterSlotsOkAlertStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.used_cpu_sys, new MinuteUsedCpuSysStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.used_cpu_user, new MinuteUsedCpuUserStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.used_cpu_sys_children, new MinuteUsedCpuSysChStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.used_cpu_user_children, new MinuteUsedCpuUserChStrategy());\n        alertConfigStrategyMap.put(RedisAlertConfigEnum.other_default_common_config, new DefaultCommonAlertStrategy());\n    }\n\n    @Override\n    public List<InstanceAlertConfig> getAll() {\n        try {\n            return instanceAlertConfigDao.getAll();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public int save(InstanceAlertConfig instanceAlertConfig) {\n        try {\n            return instanceAlertConfigDao.save(instanceAlertConfig);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return -1;\n        }\n    }\n\n    @Override\n    public int batchSave(List<InstanceAlertConfig> instanceAlertConfigList) {\n        try {\n            return instanceAlertConfigDao.batchSave(instanceAlertConfigList);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return -1;\n        }\n    }\n\n    @Override\n    public InstanceAlertConfig get(int id) {\n        try {\n            return instanceAlertConfigDao.get(id);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public int remove(int id) {\n        try {\n            return instanceAlertConfigDao.remove(id);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return -1;\n        }\n    }\n\n    @Override\n    public List<InstanceAlertConfig> getByType(int type) {\n        try {\n            return instanceAlertConfigDao.getByType(type);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public List<InstanceAlertConfig> getByTypeAndAppType(int type, int appType) {\n        try {\n            return instanceAlertConfigDao.getByTypeAndAppType(type, appType);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public InstanceAlertConfig getGlobalAlertConfigByCondition(String alertConfig, int compareType, Integer appType) {\n        try {\n            List<InstanceAlertConfig> configList = instanceAlertConfigDao.getByAlertConfigAndType(alertConfig, InstanceAlertTypeEnum.ALL_ALERT.getValue(), appType);\n            for (InstanceAlertConfig instanceAlertConfig : configList) {\n                if(instanceAlertConfig.getStatus() == InstanceAlertStatusEnum.YES.getValue() && compareType == instanceAlertConfig.getCompareType()){\n                    return instanceAlertConfig;\n                }\n            }\n            if(CollectionUtils.isNotEmpty(configList)){\n                return configList.get(0);\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    @Override\n    public void update(long id, String alertValue, int checkCycle, int compareType, int importantLevel) {\n        instanceAlertConfigDao.update(id, alertValue, checkCycle, compareType, importantLevel);\n    }\n\n    @Override\n    public void updateImportantLevel(String alertConfig, int compareType, int importantLevel, Integer appType) {\n        if(appType == null){\n            appType = AppTypeToAlertTypeEnum.REDIS_ALERT.getType();\n        }\n        instanceAlertConfigDao.updateImportantLevel(alertConfig, compareType, importantLevel, appType);\n    }\n\n    @Override\n    public void updateLastCheckTime(long id, Date lastCheckTime) {\n        try {\n            instanceAlertConfigDao.updateLastCheckTime(id, lastCheckTime);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public void monitorLastMinuteAllInstanceInfo() {\n        long startTime = System.currentTimeMillis();\n        // 1.全部和特殊实例报警配置\n        List<InstanceAlertConfig> commonInstanceAlertConfigList = getByType(InstanceAlertTypeEnum.ALL_ALERT.getValue());\n        List<InstanceAlertConfig> specialInstanceAlertConfigList = getByType(InstanceAlertTypeEnum.INSTANCE_ALERT.getValue());\n        //1.2查询应用报警配置\n        List<InstanceAlertConfig> appInstanceAlertConfigList = getByType(InstanceAlertTypeEnum.APP_ALERT.getValue());\n\n        // 2.所有实例信息\n        List<InstanceInfo> allInstanceInfoList = instanceDao.getAllInsts();\n        if (CollectionUtils.isEmpty(allInstanceInfoList)) {\n            return;\n        }\n\n        List<InstanceAlertConfig> transferAppInstanceAlertConfigList = new ArrayList<>();\n        List<InstanceInfo> appInstanceInfo = null;\n        for (InstanceAlertConfig instanceAlertConfig: appInstanceAlertConfigList) {\n            appInstanceInfo = allInstanceInfoList.stream().filter(instanceInfo -> instanceInfo.getAppId() == instanceAlertConfig.getInstanceId()).collect(Collectors.toList());\n            addAppInstanceToTransferList(transferAppInstanceAlertConfigList, instanceAlertConfig, appInstanceInfo);\n        }\n        if(CollectionUtils.isNotEmpty(transferAppInstanceAlertConfigList)){\n            specialInstanceAlertConfigList.addAll(transferAppInstanceAlertConfigList);\n        }\n\n        List<InstanceAlertConfig> allInstanceAlertConfigList = new ArrayList<InstanceAlertConfig>();\n        allInstanceAlertConfigList.addAll(commonInstanceAlertConfigList);\n        allInstanceAlertConfigList.addAll(specialInstanceAlertConfigList);\n        if (CollectionUtils.isEmpty(allInstanceAlertConfigList)) {\n            return;\n        }\n        // 3. 取上1分钟Redis实例统计信息\n        Date currentTime = new Date();\n        Date beginTime = DateUtils.addMinutes(currentTime, -2);\n        Date endTime = DateUtils.addMinutes(currentTime, -1);\n        Map<String, StandardStats> standardStatMap = getStandardStatsMap(beginTime, endTime);\n        if (MapUtils.isEmpty(standardStatMap)) {\n            logger.warn(\"standardStatMap is empty!\");\n            return;\n        }\n\n        // 4.检测所有配置\n        List<InstanceAlertValueResult> instanceAlertValueResultList = new ArrayList<InstanceAlertValueResult>();\n\n        for (InstanceAlertConfig instanceAlertConfig : allInstanceAlertConfigList) {\n            if (!checkInCycle(instanceAlertConfig)) {\n                continue;\n            }\n            List<InstanceInfo> tempInstanceInfoList = allInstanceInfoList;\n            if (instanceAlertConfig.isSpecail()) {\n                tempInstanceInfoList.clear();\n                InstanceInfo instanceInfo = null;\n                if(instanceAlertConfig.getInstanceInfo() == null){\n                    instanceInfo = instanceDao.getInstanceInfoById(instanceAlertConfig.getInstanceId());\n                }else{\n                    instanceInfo = instanceAlertConfig.getInstanceInfo();\n                }\n                if (instanceInfo == null) {\n                    continue;\n                }\n                tempInstanceInfoList.add(instanceInfo);\n            }\n            for (InstanceInfo instanceInfo : tempInstanceInfoList) {\n                List<InstanceAlertValueResult> InstanceAlertValueResultTempList = dealInstanceAlert(specialInstanceAlertConfigList, instanceAlertConfig, instanceInfo, standardStatMap, currentTime);\n                if (CollectionUtils.isNotEmpty(InstanceAlertValueResultTempList)) {\n                    instanceAlertValueResultList.addAll(InstanceAlertValueResultTempList);\n                }\n            }\n            // 更新配置最后检测时间\n            updateLastCheckTime(instanceAlertConfig.getId(), currentTime);\n        }\n        if (CollectionUtils.isNotEmpty(instanceAlertValueResultList)) {\n            // 发送邮件\n            sendInstanceAlertEmail(beginTime, endTime, instanceAlertValueResultList);\n        }\n        long costTime = System.currentTimeMillis() - startTime;\n        if (costTime > 20000) {\n            logger.warn(\"monitorLastMinuteAllInstanceInfo cost {} ms\", costTime);\n        }\n    }\n\n    /**\n     * 将应用报警转化为应用下实例报警\n     * @param transferAppInstanceAlertConfigList 转换后结果存储\n     * @param instanceAlertConfig 应用报警配置\n     * @param appInstanceInfo 应用下实例信息列表\n     */\n    private void addAppInstanceToTransferList(List<InstanceAlertConfig> transferAppInstanceAlertConfigList, InstanceAlertConfig instanceAlertConfig, List<InstanceInfo> appInstanceInfo) {\n        InstanceAlertConfig appInstanceAlertConfig = null;\n        for (InstanceInfo instanceInfo : appInstanceInfo) {\n            appInstanceAlertConfig = new InstanceAlertConfig();\n            try {\n                BeanUtils.copyProperties(appInstanceAlertConfig, instanceAlertConfig);\n            } catch (Exception e) {\n                logger.error(\"addAppInstanceToTransferList error:\", e);\n            }\n            appInstanceAlertConfig.setInstanceId(instanceInfo.getId());\n            appInstanceAlertConfig.setInstanceInfo(instanceInfo);\n            transferAppInstanceAlertConfigList.add(appInstanceAlertConfig);\n        }\n    }\n\n    /**\n     * 处理实例\n     *\n     * @param instanceAlertConfig\n     * @param instanceInfo\n     * @param standardStatMap\n     * @param currentTime\n     */\n    private List<InstanceAlertValueResult> dealInstanceAlert(List<InstanceAlertConfig> specialInstanceAlertConfigList, InstanceAlertConfig instanceAlertConfig, InstanceInfo instanceInfo, Map<String, StandardStats> standardStatMap, Date currentTime) {\n        if (instanceInfo.isOffline()) {\n            return null;\n        }\n        // 单个实例的统计信息\n        String hostPort = instanceInfo.getHostPort();\n        StandardStats standardStats = standardStatMap.get(hostPort);\n        if (standardStats == null) {\n            return null;\n        }\n        // 判断是不是特殊实例\n        InstanceAlertConfig finalInstanceConfig = filterSpecial(specialInstanceAlertConfigList, instanceAlertConfig, instanceInfo);\n        // 普通配置，但finalInstanceConfig不等于instanceAlertConfig，跳过\n        if (!instanceAlertConfig.isSpecail() && finalInstanceConfig.getId() != instanceAlertConfig.getId()) {\n            return null;\n        }\n        // 是否进入检测周期\n        boolean isInCycle = checkInCycle(finalInstanceConfig);\n        if (!isInCycle) {\n            return null;\n        }\n        // 枚举检测\n        String alertConfig = finalInstanceConfig.getAlertConfig();\n        RedisAlertConfigEnum redisAlertConfigEnum = RedisAlertConfigEnum.getRedisAlertConfig(alertConfig);\n        if(redisAlertConfigEnum == null){\n            redisAlertConfigEnum = RedisAlertConfigEnum.other_default_common_config;\n        }\n\n        // 策略检测\n        AlertConfigStrategy alertConfigStrategy = alertConfigStrategyMap.get(redisAlertConfigEnum);\n        if (alertConfigStrategy == null) {\n            return null;\n        }\n\n        // 获取基准数据\n        AlertConfigBaseData alertConfigBaseData = new AlertConfigBaseData();\n        alertConfigBaseData.setInstanceInfo(instanceInfo);\n        alertConfigBaseData.setStandardStats(standardStats);\n        // 开始检测\n        try {\n            return alertConfigStrategy.checkConfig(finalInstanceConfig, alertConfigBaseData);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    /**\n     * 发送邮件\n     *\n     * @param instanceAlertValueResultList\n     */\n    private void sendInstanceAlertEmail(Date beginTime, Date endTime,\n                                        List<InstanceAlertValueResult> instanceAlertValueResultList) {\n        if (CollectionUtils.isEmpty(instanceAlertValueResultList)) {\n            return;\n        }\n        Collections.sort(instanceAlertValueResultList, new Comparator<InstanceAlertValueResult>() {\n\n            @Override\n            public int compare(InstanceAlertValueResult o1, InstanceAlertValueResult o2) {\n                return (int) (o1.getAppId() - o2.getAppId());\n            }\n        });\n\n        // 1.客户端应用定制报警\n        Map<Long, List<InstanceAlertValueResult>> appAlertMap = new HashMap<Long, List<InstanceAlertValueResult>>();\n        Set<Long> appSets = new HashSet<Long>();\n\n        Map<Long, AppDesc> appDescMap = new HashMap<Long, AppDesc>();\n        // 2.遍历报警实例\n        for (InstanceAlertValueResult instanceAlertValueResult : instanceAlertValueResultList) {\n            long appId = instanceAlertValueResult.getAppId();\n            AppDesc appDesc = null;\n            if (appDescMap.containsKey(appId)) {\n                appDesc = appDescMap.get(appId);\n            } else {\n                appDesc = appDao.getAppDescById(instanceAlertValueResult.getAppId());\n                appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));\n                appDescMap.put(appId, appDesc);\n            }\n            instanceAlertValueResult.setAppDesc(appDesc);\n            // 找出定制报警的客户端\n            if (appDesc.getIsAccessMonitor() == 1) {\n                appSets.add(appId);\n            }\n        }\n        // 3.按客户端定制报警应用分组发送\n        for (Long appid : appSets) {\n            List<InstanceAlertValueResult> instanceAlertList = new ArrayList<InstanceAlertValueResult>();\n            for (InstanceAlertValueResult instanceAlert : instanceAlertValueResultList) {\n                if (appid == instanceAlert.getAppId()) {\n                    instanceAlertList.add(instanceAlert);\n                }\n            }\n            if (instanceAlertList.size() > 0) {\n                appAlertMap.put(appid, instanceAlertList);\n                logger.warn(\"monitor alert appid:{},instance size ={}\", appid, instanceAlertList.size());\n            }\n        }\n        // 4.发送给管理员报警\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\");\n        int importantLevel = this.getAlertImportantLevel(instanceAlertValueResultList);\n        String emailTitle = String.format(\"Redis实例分钟报警(%s~%s)\", sdf.format(beginTime), sdf.format(endTime));\n        if(importantLevel == ImportantLevelTypeEnum.URGENT.getType()\n                || importantLevel == ImportantLevelTypeEnum.IMPORTANT.getType()\n                || importantLevel == ImportantLevelTypeEnum.NORMAL.getType()){\n            emailTitle = String.format(\"[%s]Redis实例分钟报警(%s~%s)\", ImportantLevelTypeEnum.getInfoByType(importantLevel), sdf.format(beginTime), sdf.format(endTime));\n        }\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"instanceAlertValueResultList\", instanceAlertValueResultList);\n        String emailContent = FreemakerUtils.createText(\"instanceAlert.ftl\", configuration, context);\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.INSTANCE_MINUTE_MONITOR, emailTitle, null, instanceAlertValueResultList);\n        emailComponent.sendMailToAdmin(emailTitle, emailContent.replaceAll(\"\\t\",\"\"));\n\n        // 5.发送给客户端定制报警\n        for (Map.Entry<Long, List<InstanceAlertValueResult>> appAlert : appAlertMap.entrySet()) {\n            Long appId = appAlert.getKey();\n            List<InstanceAlertValueResult> instanceAlertList = appAlert.getValue();\n\n            emailTitle = String.format(\"应用Redis分钟报警(%s~%s)\", sdf.format(beginTime), sdf.format(endTime));\n            Map<String, Object> context1 = new HashMap<>();\n            context1.put(\"instanceAlertValueResultList\", instanceAlertList);\n            emailContent = FreemakerUtils.createText(\"appAlert.ftl\", configuration, context1);\n            // 获取报警用户列表\n            List<AppUser> appUsers = userService.getAlertByAppId(appId);\n            List<String> emailList = getEmailList(appUsers);\n            if (emailList != null && emailList.size() > 0) {\n                emailComponent.sendMail(emailTitle, emailContent, emailList);\n            }\n        }\n\n    }\n\n    /**\n     * 根据报警结果，获取报警重要程度\n     * @param instanceAlertValueResultList\n     * @return\n     */\n    private int getAlertImportantLevel(List<InstanceAlertValueResult> instanceAlertValueResultList){\n        int importantLevel = ImportantLevelTypeEnum.NORMAL.getType();\n        for(InstanceAlertValueResult alertValueResult : instanceAlertValueResultList){\n            if(alertValueResult.getInstanceAlertConfig() != null && alertValueResult.getInstanceAlertConfig().getImportantLevel() != null){\n                if(importantLevel < alertValueResult.getInstanceAlertConfig().getImportantLevel()){\n                    importantLevel = alertValueResult.getInstanceAlertConfig().getImportantLevel();\n                }\n            }\n        }\n        return importantLevel;\n    }\n\n    /**\n     * <p>\n     * Description: 获取邮件列表\n     * </p>\n     *\n     * @param\n     * @return\n     * @author chenshi\n     * @version 1.0\n     * @date 2017/9/25\n     */\n    public List<String> getEmailList(List<AppUser> appUsers) {\n        List<String> emailList = new ArrayList<String>();\n        if (appUsers != null && appUsers.size() > 0) {\n            for (AppUser appUser : appUsers) {\n                String email = appUser.getEmail();\n                if (StringUtils.isNotBlank(email)) {\n                    emailList.add(appUser.getEmail());\n                }\n            }\n        }\n        return emailList;\n    }\n\n    /**\n     * 检测是否在周期内\n     *\n     * @param finalInstanceConfig\n     * @return\n     */\n    private boolean checkInCycle(InstanceAlertConfig finalInstanceConfig) {\n        if (EnvUtil.isLocal(environment)) {\n            return true;\n        }\n        // 检测周期转换为毫秒\n        long checkCycleMillionTime = finalInstanceConfig.getCheckCycleMillionTime();\n        // 当前距离上一次检测过去的毫秒\n        long betweenTime = new Date().getTime() - finalInstanceConfig.getLastCheckTime().getTime();\n        // 超过说明需要进行再测检测了\n        if (betweenTime >= checkCycleMillionTime) {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 判断当前实例是否在特殊报警配置中\n     *\n     * @param specialInstanceAlertConfigList\n     * @param instanceAlertConfig\n     * @param instanceInfo\n     * @return\n     */\n    private InstanceAlertConfig filterSpecial(List<InstanceAlertConfig> specialInstanceAlertConfigList, InstanceAlertConfig instanceAlertConfig, InstanceInfo instanceInfo) {\n        // 如果没有则返回原来的配置\n        if (CollectionUtils.isEmpty(specialInstanceAlertConfigList)) {\n            return instanceAlertConfig;\n        }\n        // 寻找特殊配置 \n        for (InstanceAlertConfig specialInstanceAlertConfig : specialInstanceAlertConfigList) {\n            String specialAlertConfig = specialInstanceAlertConfig.getAlertConfig();\n            long instanceId = specialInstanceAlertConfig.getInstanceId();\n            // 配置名和实例id对上\n            if (instanceAlertConfig.getAlertConfig().equals(specialAlertConfig) && instanceInfo.getId() == instanceId) {\n                return specialInstanceAlertConfig;\n            }\n        }\n        return instanceAlertConfig;\n    }\n\n    /**\n     * 获取指定时间内的标准统计信息Map\n     *\n     * @param beginTime\n     * @param endTime\n     * @return\n     */\n    private Map<String, StandardStats> getStandardStatsMap(Date beginTime, Date endTime) {\n        List<StandardStats> standardStatsList = standardStatsDao.getStandardStatsByCreateTime(beginTime, endTime, ConstUtils.REDIS);\n        // 按照host:port做分组\n        Map<String, StandardStats> resultMap = new HashMap<String, StandardStats>();\n        for (StandardStats standardStats : standardStatsList) {\n            String hostPort = standardStats.getIp() + \":\" + standardStats.getPort();\n            resultMap.put(hostPort, standardStats);\n        }\n        return resultMap;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/instance/impl/InstanceDeployCenterImpl.java",
    "content": "package com.sohu.cache.stats.instance.impl;\r\n\r\nimport com.sohu.cache.alert.EmailComponent;\r\nimport com.sohu.cache.constant.AppCheckEnum;\r\nimport com.sohu.cache.constant.InstanceStatusEnum;\r\nimport com.sohu.cache.dao.AppAuditDao;\r\nimport com.sohu.cache.dao.AppDao;\r\nimport com.sohu.cache.dao.InstanceDao;\r\nimport com.sohu.cache.dao.MachineRelationDao;\r\nimport com.sohu.cache.entity.*;\r\nimport com.sohu.cache.entity.InstanceAlertValueResult;\r\nimport com.sohu.cache.entity.AppDesc;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.entity.MachineRelation;\r\nimport com.sohu.cache.machine.MachineCenter;\r\nimport com.sohu.cache.machine.MachineDeployCenter;\r\nimport com.sohu.cache.redis.AssistRedisService;\r\nimport com.sohu.cache.redis.RedisCenter;\r\nimport com.sohu.cache.redis.RedisConfigTemplateService;\r\nimport com.sohu.cache.redis.RedisDeployCenter;\r\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\r\nimport com.sohu.cache.task.TaskService;\r\nimport com.sohu.cache.task.constant.MachineSyncEnum;\r\nimport com.sohu.cache.task.constant.TaskQueueEnum;\r\nimport com.sohu.cache.task.entity.TaskQueue;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.TypeUtil;\r\nimport com.sohu.cache.web.enums.AlertTypeEnum;\r\nimport com.sohu.cache.web.enums.MachineTaskEnum;\r\nimport com.sohu.cache.web.enums.PodStatusEnum;\r\nimport com.sohu.cache.web.service.AppAlertRecordService;\r\nimport com.sohu.cache.web.service.AppService;\r\nimport com.sohu.cache.web.service.ResourceService;\r\nimport com.sohu.cache.web.util.DateUtil;\r\nimport com.sohu.cache.web.util.FreemakerUtils;\r\nimport freemarker.template.Configuration;\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.context.annotation.Lazy;\r\nimport org.springframework.stereotype.Service;\r\nimport org.springframework.util.Assert;\r\nimport org.springframework.web.client.RestTemplate;\r\nimport redis.clients.jedis.Jedis;\r\nimport redis.clients.jedis.exceptions.JedisConnectionException;\r\nimport redis.clients.jedis.exceptions.JedisDataException;\r\n\r\nimport java.util.*;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\n/**\r\n * Created by yijunzhang on 14-11-26.\r\n */\r\n@Service(\"instanceDeployCenter\")\r\npublic class InstanceDeployCenterImpl implements InstanceDeployCenter {\r\n\r\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\r\n    @Autowired\r\n    private InstanceDao instanceDao;\r\n    @Autowired\r\n    private MachineRelationDao machineRelationDao;\r\n    @Autowired\r\n    @Lazy\r\n    private RedisCenter redisCenter;\r\n    @Autowired\r\n    private RedisDeployCenter redisDeployCenter;\r\n    @Autowired\r\n    @Lazy\r\n    private MachineCenter machineCenter;\r\n    @Autowired\r\n    private AppAuditDao appAuditDao;\r\n    @Autowired\r\n    private AppDao appDao;\r\n    @Autowired\r\n    private RedisConfigTemplateService redisConfigTemplateService;\r\n    @Autowired\r\n    EmailComponent emailComponent;\r\n    @Autowired(required = false)\r\n    RestTemplate restTemplate;\r\n    @Autowired\r\n    MachineDeployCenter machineDeployCenter;\r\n    @Autowired\r\n    TaskService taskService;\r\n    @Autowired\r\n    AssistRedisService assistRedisService;\r\n    @Autowired\r\n    private Configuration configuration;\r\n    @Autowired\r\n    private AppService appService;\r\n    @Autowired\r\n    private ResourceService resourceService;\r\n    @Autowired\r\n    private AppAlertRecordService appAlertRecordService;\r\n\r\n    private final static String LINE_SEP = \"\\\\n\";\r\n    private final static String FAIL = \"fail\";\r\n\r\n    @Override\r\n    public boolean startExistInstance(long appId, int instanceId) {\r\n        Assert.isTrue(instanceId > 0L);\r\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\r\n        Assert.isTrue(instanceInfo != null);\r\n        int type = instanceInfo.getType();\r\n        String host = instanceInfo.getIp();\r\n        int port = instanceInfo.getPort();\r\n        // 获取redis路径\r\n        SystemResource redisResource = resourceService.getResourceById(appDao.getAppDescById(appId).getVersionId());\r\n        String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName());\r\n        // Redis资源校验&推包\r\n        Boolean installStatus = redisConfigTemplateService.checkAndInstallRedisResource(host, redisResource);\r\n        if (!installStatus) {\r\n            logger.info(\"{} is install :{}\", host, redisResource.getName());\r\n            return false;\r\n        }\r\n        boolean isRun;\r\n        if (TypeUtil.isRedisType(type)) {\r\n            if (TypeUtil.isRedisSentinel(type)) {\r\n                isRun = redisCenter.isRun(host, port);\r\n            } else {\r\n                isRun = redisCenter.isRun(appId, host, port);\r\n            }\r\n            if (isRun) {\r\n                logger.warn(\"{}:{} instance is Running\", host, port);\r\n            } else {\r\n                String runShell = \"\";\r\n                if (TypeUtil.isRedisCluster(type)) {\r\n                    runShell = redisDeployCenter.getRedisRunShell(true, host, port, redisDir);\r\n                } else if (TypeUtil.isRedisSentinel(type)) {\r\n                    runShell = redisDeployCenter.getSentinelRunShell(host, port, redisDir);\r\n                } else {\r\n                    runShell = redisDeployCenter.getRedisRunShell(false, host, port, redisDir);\r\n                }\r\n                boolean isRunShell = machineCenter.startProcessAtPort(host, port, runShell);\r\n                if (!isRunShell) {\r\n                    logger.error(\"startProcessAtPort-> {}:{} shell= {} failed\", host, port, runShell);\r\n                    return false;\r\n                } else {\r\n                    logger.warn(\"{}:{} instance has Run\", host, port);\r\n                }\r\n                if (TypeUtil.isRedisSentinel(type)) {\r\n                    isRun = redisCenter.isRun(host, port);\r\n                } else {\r\n                    isRun = redisCenter.isRun(appId, host, port);\r\n                }\r\n            }\r\n        } else {\r\n            logger.error(\"type={} not match!\", type);\r\n            isRun = false;\r\n        }\r\n        if (isRun) {\r\n            instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\r\n            instanceDao.update(instanceInfo);\r\n        }\r\n        // 重置所有sentinel实例状态\r\n        if (TypeUtil.isRedisSentinel(type)) {\r\n            try {\r\n                redisDeployCenter.sentinelReset(appId);\r\n            } catch (Exception e) {\r\n                logger.error(\"redis sentinel reset error :{}\", e.getMessage(), e);\r\n            }\r\n        }\r\n\r\n        return isRun;\r\n    }\r\n\r\n    @Override\r\n    public boolean startExistInstanceWithoutResourceCheck(long appId, int instanceId) {\r\n        Assert.isTrue(instanceId > 0L);\r\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\r\n        Assert.isTrue(instanceInfo != null);\r\n        int type = instanceInfo.getType();\r\n        String host = instanceInfo.getIp();\r\n        int port = instanceInfo.getPort();\r\n        // 获取redis路径\r\n        SystemResource redisResource = resourceService.getResourceById(appDao.getAppDescById(appId).getVersionId());\r\n        String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName());\r\n        boolean isRun;\r\n        if (TypeUtil.isRedisType(type)) {\r\n            if (TypeUtil.isRedisSentinel(type)) {\r\n                isRun = redisCenter.isRun(host, port);\r\n            } else {\r\n                isRun = redisCenter.isRun(appId, host, port);\r\n            }\r\n            if (isRun) {\r\n                logger.warn(\"{}:{} instance is Running\", host, port);\r\n            } else {\r\n                String runShell = \"\";\r\n                if (TypeUtil.isRedisCluster(type)) {\r\n                    runShell = redisDeployCenter.getRedisRunShell(true, host, port, redisDir);\r\n                } else if (TypeUtil.isRedisSentinel(type)) {\r\n                    runShell = redisDeployCenter.getSentinelRunShell(host, port, redisDir);\r\n                } else {\r\n                    runShell = redisDeployCenter.getRedisRunShell(false, host, port, redisDir);\r\n                }\r\n                boolean isRunShell = machineCenter.startProcessAtPort(host, port, runShell);\r\n                if (!isRunShell) {\r\n                    logger.error(\"startProcessAtPort-> {}:{} shell= {} failed\", host, port, runShell);\r\n                    return false;\r\n                } else {\r\n                    logger.warn(\"{}:{} instance has Run\", host, port);\r\n                }\r\n                if (TypeUtil.isRedisSentinel(type)) {\r\n                    isRun = redisCenter.isRun(host, port);\r\n                } else {\r\n                    isRun = redisCenter.isRun(appId, host, port);\r\n                }\r\n            }\r\n        } else {\r\n            logger.error(\"type={} not match!\", type);\r\n            isRun = false;\r\n        }\r\n        if (isRun) {\r\n            instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());\r\n            instanceDao.update(instanceInfo);\r\n        }\r\n        // 重置所有sentinel实例状态\r\n        if (TypeUtil.isRedisSentinel(type)) {\r\n            try {\r\n                redisDeployCenter.sentinelReset(appId);\r\n            } catch (Exception e) {\r\n                logger.error(\"redis sentinel reset error :{}\", e.getMessage(), e);\r\n            }\r\n        }\r\n        return isRun;\r\n    }\r\n\r\n    @Override\r\n    public boolean shutdownExistInstance(long appId, int instanceId) {\r\n        Assert.isTrue(instanceId > 0L);\r\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\r\n        Assert.isTrue(instanceInfo != null);\r\n        int type = instanceInfo.getType();\r\n        String host = instanceInfo.getIp();\r\n        int port = instanceInfo.getPort();\r\n        boolean isShutdown;\r\n        if (TypeUtil.isRedisType(type)) {\r\n            if (TypeUtil.isRedisSentinel(type)) {\r\n                isShutdown = redisCenter.shutdown(host, port);\r\n                // 重置reset sentinel实例状态\r\n                try {\r\n                    redisDeployCenter.sentinelReset(appId);\r\n                } catch (Exception e) {\r\n                    logger.error(\"redis sentinel reset error :{}\", e.getMessage(), e);\r\n                }\r\n            } else {\r\n                isShutdown = redisCenter.shutdown(appId, host, port);\r\n            }\r\n            if (isShutdown) {\r\n                logger.warn(\"{}:{} redis is shutdown\", host, port);\r\n            } else {\r\n                logger.error(\"{}:{} redis shutdown error\", host, port);\r\n            }\r\n        } else {\r\n            logger.error(\"type={} not match!\", type);\r\n            isShutdown = false;\r\n        }\r\n\r\n        //check shutdown success\r\n        if(isShutdown){\r\n            isShutdown = redisCenter.checkShutdownSuccess(instanceInfo);\r\n        }\r\n\r\n        if (isShutdown) {\r\n            instanceInfo.setStatus(InstanceStatusEnum.OFFLINE_STATUS.getStatus());\r\n            instanceDao.update(instanceInfo);\r\n        }\r\n        return isShutdown;\r\n    }\r\n\r\n    @Override\r\n    public boolean forgetInstance(long appId, int instanceId) {\r\n        Assert.isTrue(instanceId > 0L);\r\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\r\n        Assert.isTrue(instanceInfo != null);\r\n        String host = instanceInfo.getIp();\r\n        int port = instanceInfo.getPort();\r\n        String nodeId = null;\r\n        //获取应用下所有在线实例\r\n        List<InstanceInfo> instanceList = appService.getAppOnlineInstanceInfo(appId);\r\n        //找到一个运行的节点用来执行cluster nodes\r\n        InstanceInfo firstRunning = null;\r\n        for (InstanceInfo instance : instanceList) {\r\n            //todo: 节点cluster node不能包含handshake？否则nodeId一直在变\r\n            if (redisCenter.isRun(appId, instance.getIp(), instance.getPort())) {\r\n                firstRunning = instance;\r\n                break;\r\n            }\r\n        }\r\n        //先获取被forget节点的node id(cluster myid)\r\n        String clusterNodes = redisCenter.getClusterNodes(appId, firstRunning.getIp(), firstRunning.getPort());     //从其他在线节点拿到cluster nodes\r\n        nodeId = getNodeIdFromClusterNodes(clusterNodes, host, port);\r\n        logger.warn(\"app {} instance {}:{} instanceId {} cluster node id {} will be forgot. cluster nodes\\n{}\", appId, host, port,\r\n                instanceId, nodeId, clusterNodes);\r\n        if (StringUtils.isNotBlank(nodeId)) {\r\n            int forgetCount = 0;\r\n            String instanceIp;\r\n            int instancePort;\r\n            for (InstanceInfo instanceInfo1 : instanceList) {\r\n                instanceIp = instanceInfo1.getIp();\r\n                instancePort = instanceInfo1.getPort();\r\n                if (redisCenter.forget(appId, instanceIp, instancePort, nodeId)) {\r\n                    forgetCount++;\r\n                    logger.warn(\"instance {}:{} in app {} forget instance {}:{}-{} successfully\", instanceIp,\r\n                            instancePort, appId, host, port, nodeId);\r\n                } else {\r\n                    logger.error(\"instance {}:{} in app {} forget instance {}:{}-{} failed\", instanceIp,\r\n                            instancePort, appId, host, port, nodeId);\r\n                }\r\n            }\r\n            if (forgetCount == instanceList.size()) { //所有在线节点forget成功\r\n                instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.FORGET_STATUS.getStatus());\r\n                logger.warn(\"app {} forget&update instance {}:{} successfully. current cluster nodes\\n{}\", appId,\r\n                        host, port, redisCenter.getClusterNodes(appId, firstRunning.getIp(), firstRunning.getPort()));\r\n                return true;\r\n            }\r\n        } else {        //cluster nodes已经没有该节点，则直接将数据库状态改为InstanceStatusEnum.FORGET_STATUS.getStatus()\r\n            instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.FORGET_STATUS.getStatus());\r\n            logger.warn(\"app {} update instance(nodeId is null) {}:{} successfully. current cluster nodes\\n{}\", appId,\r\n                    host, port, redisCenter.getClusterNodes(appId, firstRunning.getIp(), firstRunning.getPort()));\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    @Override\r\n    public boolean clearFailInstances(long appId) {\r\n        try {\r\n            //验证app类型是cluster\r\n            AppDesc appDesc = appService.getByAppId(appId);\r\n            if (appDesc.getType() != ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\r\n                logger.warn(\"app {} is not cluster!\", appId);\r\n                return false;\r\n            }\r\n            //找到一个运行的节点用来执行cluster nodes\r\n            List<InstanceInfo> instanceList = appService.getAppOnlineInstanceInfo(appId);\r\n            InstanceInfo firstRun = null;\r\n            for (InstanceInfo instanceInfo : instanceList) {\r\n                if (redisCenter.isRun(appId, instanceInfo.getIp(), instanceInfo.getPort())) {\r\n                    firstRun = instanceInfo;\r\n                    break;\r\n                }\r\n            }\r\n            if (firstRun == null) {\r\n                logger.warn(\"app {} can not find an running instance!\", appId);\r\n                return false;\r\n            }\r\n            String clusterNodes = redisCenter.getClusterNodes(appId, firstRun.getIp(), firstRun.getPort());     //拿到cluster nodes\r\n            if (StringUtils.isNotBlank(clusterNodes)) {\r\n                String nodeId, hostPort, host, role;\r\n                int port, total = 0;      //total统计该应用共forget的节点数\r\n                String[] lines = clusterNodes.split(LINE_SEP);\r\n                if (lines != null && lines.length > 0) {\r\n                    for (String line : lines) {\r\n                        if (line.contains(FAIL)) {        //todo:其他判断条件\r\n                            int count = 0;\r\n                            String[] items = line.split(\" \");\r\n                            if (items != null && items.length > 0) {\r\n                                nodeId = items[0];\r\n                                hostPort = items[1].split(\"@\")[0];\r\n                                host = hostPort.split(\":\")[0];\r\n                                port = Integer.parseInt(hostPort.split(\":\")[1]);\r\n                                role = items[2];\r\n                                for (InstanceInfo instance : instanceList) {\r\n                                    if (redisCenter.forget(appId, instance.getIp(), instance.getPort(), nodeId)) {\r\n                                        count++;\r\n                                    }\r\n                                }\r\n                                if (count == instanceList.size()) {   //所有节点都forget了该nodeId\r\n                                    total++;\r\n                                    instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.FORGET_STATUS.getStatus());\r\n                                    logger.warn(\"instance {}:{} id {} role {} was forgot by all online instances\", host, port, nodeId, role);\r\n                                }\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                clusterNodes = redisCenter.getClusterNodes(appId, firstRun.getIp(), firstRun.getPort());     //拿到cluster nodes\r\n                logger.warn(\"app {} clear total {} fail nodes. current cluster nodes\\n{}\", appId, total, clusterNodes);\r\n                return true;\r\n            }\r\n        } catch (Exception e) {\r\n            logger.error(\"app {} clear fail nodes error!\", appId, e);\r\n        }\r\n        return false;\r\n    }\r\n\r\n    private String getNodeIdFromClusterNodes(String clusterNodes, String host, int port) {\r\n        String nodeId = null;\r\n        String hostPort = host + \":\" + port;\r\n        if (StringUtils.isNotBlank(clusterNodes)) {\r\n            String[] lines = clusterNodes.split(LINE_SEP);\r\n            if (lines != null && lines.length > 0) {\r\n                for (String line : lines) {\r\n                    if (line.contains(hostPort)) {\r\n                        String[] items = line.split(\" \");\r\n                        if (items != null && items.length > 0) {\r\n                            nodeId = items[0];\r\n                            break;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return nodeId;\r\n    }\r\n\r\n    @Override\r\n    public String showInstanceRecentLog(int instanceId, int maxLineNum) {\r\n        Assert.isTrue(instanceId > 0L);\r\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\r\n        Assert.isTrue(instanceInfo != null);\r\n        try {\r\n            return machineCenter.showInstanceRecentLog(instanceInfo, maxLineNum);\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n            return \"\";\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean modifyInstanceConfig(long appId, Long appAuditId, String host, int port, String instanceConfigKey,\r\n                                        String instanceConfigValue) {\r\n//        Assert.isTrue(appAuditId != null && appAuditId > 0L);\r\n        Assert.isTrue(StringUtils.isNotBlank(host));\r\n        Assert.isTrue(port > 0);\r\n        Assert.isTrue(StringUtils.isNotBlank(instanceConfigKey));\r\n//        Assert.isTrue(StringUtils.isNotBlank(instanceConfigValue));\r\n        boolean isModify = redisDeployCenter.modifyInstanceConfig(appId, host, port, instanceConfigKey, instanceConfigValue);\r\n        if (isModify && appAuditId != null) {\r\n            // 改变审核状态\r\n            appAuditDao.updateAppAudit(appAuditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\r\n        }\r\n        return isModify;\r\n    }\r\n\r\n    public MachineSyncEnum podChangeStatus(String ip) {\r\n\r\n        // 获取pod变更历史\r\n        MachineSyncEnum syncEnum = null;\r\n        List<MachineRelation> relationList = machineRelationDao.getOnlinePodList(ip, PodStatusEnum.ONLINE.getValue());\r\n        if (CollectionUtils.isEmpty(relationList) || relationList.size() == 1) {\r\n            return MachineSyncEnum.NO_CHANGE;\r\n        } else {\r\n            // 1.当前pod的变更信息\r\n            MachineRelation current_podinfo = relationList.get(0);\r\n            // 2.上一次pod的变更信息\r\n            MachineRelation pre_podinfo = relationList.get(1);\r\n            // 3.判定宿主机是否变更\r\n            String sourceIp = pre_podinfo.getRealIp();//源宿主机\r\n            String targetIp = current_podinfo.getRealIp();//目标宿主机\r\n            if (!sourceIp.equals(targetIp)) {\r\n                int relationId = pre_podinfo.getId();\r\n\r\n                String key = String.format(\"%s-%s\", sourceIp, targetIp);\r\n                try {\r\n                    // 3.1 保证只有同一个宿主机/目标宿主机执行同步任务 (多个pod ip对应一个宿主机)\r\n                    boolean setlock = assistRedisService.setNx(key, \"sync\");\r\n                    if (setlock) {\r\n                        long taskId = taskService.addMachineSyncTask(sourceIp, targetIp, ip, String.format(\"sourceMachine:%s tagetMachine:%s \", sourceIp, targetIp), 0);\r\n                        if (taskId > 0) {\r\n                            logger.info(\"add machine sync task tashid:{} \", taskId);\r\n                            machineDeployCenter.updateMachineRelation(relationId, taskId, MachineTaskEnum.SYNCING.getValue());\r\n                            // 3.2 探测 taskId 执行情况  10s轮训一次\r\n                            long start = System.currentTimeMillis();\r\n\r\n                            while (true) {\r\n                                TaskQueue taskQueue = taskService.getTaskQueueById(taskId);\r\n                                if (taskQueue.getStatus() == TaskQueueEnum.TaskStatusEnum.SUCCESS.getStatus()) {\r\n                                    // a).同步任务执行完成退出\r\n                                    syncEnum = MachineSyncEnum.SYNC_SUCCESS;\r\n                                    break;\r\n                                }\r\n                                if (taskQueue.getStatus() == TaskQueueEnum.TaskStatusEnum.ABORT.getStatus()) {\r\n                                    // b).机器同步数据异常 发送邮件通知管理员\r\n                                    syncEnum = MachineSyncEnum.SYNC_ABORT;\r\n                                    break;\r\n                                }\r\n                                logger.info(\"machine sync taskid:{} status:{} waitting .... sleep 10s \", taskId, taskQueue.getStatus());\r\n                                TimeUnit.SECONDS.sleep(10);\r\n                            }\r\n                            String emailTitle = String.format(\"Pod重启机器同步任务报警\");\r\n                            String emailContent = String.format(\"Pod ip:%s 发生重启, 宿主机发生变更[%s]->[%s],机器同步任务id:(%s) 状态:(%s), 数据同步时间开销:(%s s)\", ip, sourceIp, targetIp, taskId, syncEnum.getDesc(), (System.currentTimeMillis() - start) / 1000);\r\n                            appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.POD_RESTART_SYNC_TASK, emailTitle, emailContent, ip);\r\n                            emailComponent.sendMailToAdmin(emailTitle, emailContent);\r\n                        }\r\n                    } else {\r\n                        return MachineSyncEnum.SYNC_EXECUTING;\r\n                    }\r\n                } catch (Exception e) {\r\n                    logger.error(\"machine sync get lock error :{}\", e.getMessage(), e);\r\n                    return MachineSyncEnum.SYNC_ERROR;\r\n                } finally {\r\n                    assistRedisService.del(key);\r\n                }\r\n            } else {\r\n                syncEnum = MachineSyncEnum.NO_CHANGE;\r\n                String emailTitle = String.format(\"Pod重启机器同步任务报警\");\r\n                String emailContent = String.format(\"Pod ip:%s 发生重启, 宿主机未变更[%s]->[%s] 状态:(%s)\", ip, sourceIp, targetIp, syncEnum.getDesc());\r\n                appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.POD_RESTART_SYNC_TASK, emailTitle, emailContent, ip);\r\n                emailComponent.sendMailToAdmin(emailTitle, emailContent);\r\n            }\r\n            return syncEnum;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public List<InstanceAlertValueResult> checkAndStartExceptionInstance(String ip, Boolean isAlert) {\r\n\r\n        // 1.获取容器心跳停止/运行中的实例列表\r\n        List<InstanceInfo> instanceInfos = instanceDao.checkHeartStopInstance(ip);\r\n        List<InstanceAlertValueResult> recoverInstInfo = new ArrayList<>();\r\n        // 2.滚动检测实例列表\r\n        if (!CollectionUtils.isEmpty(instanceInfos)) {\r\n            for (InstanceInfo instance : instanceInfos) {\r\n                long appId = instance.getAppId();\r\n                int instanceId = instance.getId();\r\n                String host = instance.getIp();\r\n                int port = instance.getPort();\r\n                int type = instance.getType();\r\n\r\n                logger.info(\"checkAndStartExceptionInstance scroll check instance {}:{} \", host, port);\r\n                //2.1 检测实例是否启动\r\n                if (redisCenter.isRun(appId, ip, port)) {\r\n                    continue;\r\n                }\r\n                // 2.2 异常实例恢复\r\n                // 2.3 检测异常实例启动成功 & 加载完成数据 & 启动下一个实例\r\n                Jedis jedis = null;\r\n                int retry = 1;//重试次数\r\n                try {\r\n                    startExistInstance(appId, instanceId);\r\n                    TimeUnit.SECONDS.sleep(5);//等待实例启动\r\n                    if (TypeUtil.isRedisSentinel(type)) {\r\n                        jedis = redisCenter.getJedis(host, port);\r\n                    } else {\r\n                        jedis = redisCenter.getAdminJedis(appId, host, port);\r\n                    }\r\n                    while (true) {\r\n                        // 等待节点加载数据 & PONG\r\n                        String ping = \"\";\r\n                        try {\r\n                            ping = jedis.ping();\r\n                        } catch (JedisDataException e) {\r\n                            String message = e.getMessage();\r\n                            logger.warn(e.getMessage());\r\n                            if (StringUtils.isNotBlank(message) && message.startsWith(\"LOADING\")) {\r\n                                logger.warn(\"scroll restart {}:{} waiting loading data ,sleep 2s\", host, port);\r\n                                TimeUnit.SECONDS.sleep(2);\r\n                            }\r\n                        } catch (JedisConnectionException e){\r\n                            logger.error(\"scroll restart {}:{} ping connect exception :{} \", host, port, e.getMessage(), e);\r\n                            break; //启动异常兼容\r\n                        } catch (Exception e) {\r\n                            logger.error(\"scroll restart {}:{} ping exception sleep 200ms :{} \", host, port, e.getMessage(), e);\r\n                            TimeUnit.MILLISECONDS.sleep(200);\r\n                        }\r\n                        if (ping.equalsIgnoreCase(\"PONG\") || retry++ >= 15) {\r\n                            InstanceAlertValueResult instanceAlertValueResult = new InstanceAlertValueResult();\r\n                            instanceAlertValueResult.setInstanceInfo(instance);\r\n                            instanceAlertValueResult.setOtherInfo(DateUtil.formatYYYYMMddHHMMSS(new Date()));//实例恢复时间\r\n\r\n                            recoverInstInfo.add(instanceAlertValueResult);\r\n                            break;\r\n                        }\r\n                    }\r\n                } catch (Exception e) {\r\n                    logger.error(\"checkAndStartExceptionInstance {}:{} {} \", host, port, e.getMessage(), e);\r\n                } finally {\r\n                    if (jedis != null) {\r\n                        try {\r\n                            jedis.close();\r\n                        } catch (Exception e) {\r\n                            logger.error(\"jedis close exception {}:{} {} \", host, port, e.getMessage(), e);\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n\r\n            if (isAlert) {\r\n                // 邮件通知：实例信息 恢复时间\r\n                String emailTitle = String.format(\"Pod重启探测Redis实例报警\");\r\n                Map<String, Object> context = new HashMap<>();\r\n                context.put(\"instanceAlertValueResultList\", recoverInstInfo);\r\n                String emailContent = FreemakerUtils.createText(\"instanceRecover.ftl\", configuration, context);\r\n                appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.POD_RESTART_INSTANCE_RECOVER, emailTitle, null, recoverInstInfo);\r\n                emailComponent.sendMailToAdmin(emailTitle, emailContent.toString());\r\n            }\r\n        } else {\r\n            logger.info(\"checkAndStartExceptionInstance ip:{} has instances is empty \", ip);\r\n        }\r\n        return recoverInstInfo;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/stats/instance/impl/InstanceStatsCenterImpl.java",
    "content": "package com.sohu.cache.stats.instance.impl;\n\nimport com.google.common.collect.HashBasedTable;\nimport com.google.common.collect.Table;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.constant.RedisConstant;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.dao.InstanceStatsDao;\nimport com.sohu.cache.dao.StandardStatsDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.collections4.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\n\nimport java.util.*;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.concurrent.ForkJoinTask;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * Created by yijunzhang on 14-9-17.\n */\n@Service(\"instanceStatsCenter\")\npublic class InstanceStatsCenterImpl implements InstanceStatsCenter {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n    private final ForkJoinPool forkJoinPool = new ForkJoinPool(20);\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private InstanceStatsDao instanceStatsDao;\n    @Autowired\n    private StandardStatsDao standardStatsDao;\n    @Autowired\n    @Lazy\n    private RedisCenter redisCenter;\n\n    @Override\n    public InstanceInfo getInstanceInfo(long instanceId) {\n        return instanceDao.getInstanceInfoById(instanceId);\n    }\n\n    @Override\n    public InstanceStats getInstanceStats(long instanceId) {\n        InstanceStats instanceStats = instanceStatsDao.getInstanceStatsByInsId(instanceId);\n        if (instanceStats == null) {\n            logger.error(\"instanceStats id={} is null\", instanceId);\n            return null;\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        int type = instanceInfo.getType();\n        boolean isRun = redisCenter.isRun(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n        instanceStats.setRun(isRun);\n        if (isRun) {\n            Map<String, Object> infoMap = getInfoMap(instanceInfo.getAppId(), type, instanceInfo.getIp(),\n                    instanceInfo.getPort());\n            instanceStats.setInfoMap(infoMap);\n            if (infoMap == null || infoMap.isEmpty()) {\n                instanceStats.setRun(false);\n            }\n        }\n        return instanceStats;\n    }\n\n    private Map<String, Object> getInfoMap(long appId, int type, String ip, int port) {\n        Map<Object, Map<String, Object>> infoMap = redisCenter.getInfoStats(appId, ip, port);\n        Map<String, Object> resultMap = new LinkedHashMap<String, Object>();\n        if (infoMap != null) {\n            for (Map.Entry<Object, Map<String, Object>> entry : infoMap.entrySet()) {\n                resultMap.put(((RedisConstant) entry.getKey()).getValue(), entry.getValue());\n            }\n        }\n        return resultMap;\n    }\n\n    @Override\n    public List<InstanceCommandStats> getCommandStatsList(Long instanceId, long beginTime, long endTime,\n                                                          String commandName) {\n        if (instanceId == null) {\n            return Collections.emptyList();\n        }\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        List<InstanceCommandStats> resultList = new ArrayList<InstanceCommandStats>();\n        String ip = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        int type = instanceInfo.getType();\n        List<Map<String, Object>> objectList = this.queryDiffMapList(beginTime, endTime, ip, port, ConstUtils.REDIS);\n        ;\n        if (objectList != null) {\n            for (Map<String, Object> map : objectList) {\n                InstanceCommandStats stats = parseCommand(instanceId, commandName, map, true, type);\n                if (stats != null) {\n                    resultList.add(stats);\n                }\n            }\n        }\n\n        return resultList;\n    }\n\n    @Override\n    public Map<Integer, Map<String, List<InstanceCommandStats>>> getStandardStatsList(Long appId, long beginTime,\n                                                                                      long endTime, List<String> commands) {\n        if (appId == null) {\n            return Collections.emptyMap();\n        }\n        List<InstanceInfo> list = instanceDao.getInstListByAppId(appId);\n        if (list == null || list.isEmpty()) {\n            return Collections.emptyMap();\n        }\n        Map<Integer, Map<String, List<InstanceCommandStats>>> resultMap = new LinkedHashMap<Integer, Map<String, List<InstanceCommandStats>>>();\n        for (InstanceInfo instance : list) {\n            if (instance.isOffline()) {\n                continue;\n            }\n            int instanceId = instance.getId();\n            String ip = instance.getIp();\n            int port = instance.getPort();\n            int type = instance.getType();\n//            BooleanEnum isMaster = redisCenter.isMaster(appId, ip, port);\n//            if (isMaster != BooleanEnum.TRUE) {\n//                continue;\n//            }\n            List<Map<String, Object>> objectList = this\n                    .queryDiffMapList(beginTime, endTime, ip, port, ConstUtils.REDIS);\n\n            if (objectList != null) {\n                Map<String, List<InstanceCommandStats>> commandMap = new LinkedHashMap<String, List<InstanceCommandStats>>();\n                for (String commandName : commands) {\n                    List<InstanceCommandStats> resultList = new ArrayList<InstanceCommandStats>(objectList.size());\n                    for (Map<String, Object> map : objectList) {\n                        InstanceCommandStats stats = parseCommand(instanceId, commandName, map, false, type);\n                        if (stats != null) {\n                            resultList.add(stats);\n                        }\n                    }\n                    commandMap.put(commandName, resultList);\n                }\n                resultMap.put(instanceId, commandMap);\n            }\n        }\n        return resultMap;\n    }\n\n    @Override\n    public Table<Integer, String, Map<String, List<InstanceMinuteStats>>> getInstanceMinuteStatsList(Long appId, long beginTime,\n                                                                                                     long endTime, List<String> commands) {\n        if (appId == null) {\n            return HashBasedTable.create();\n        }\n        List<InstanceInfo> list = instanceDao.getInstListByAppId(appId);\n\n        if (list == null || list.isEmpty()) {\n            return HashBasedTable.create();\n        }\n        List<InstanceInfo> onlineMasterList = list.stream()\n                .filter(x -> !x.isOffline())\n                .filter(x -> BooleanEnum.TRUE == redisCenter.isMaster(appId, x.getIp(), x.getPort()))\n                .collect(Collectors.toList());\n\n        long start = System.currentTimeMillis();\n        List<ForkJoinTask> taskList = onlineMasterList.stream()\n                .map(x -> forkJoinPool.submit(new QueryDiffMapListTask(x, beginTime, endTime, commands, ConstUtils.REDIS)))\n                .collect(Collectors.toList());\n\n        Table<Integer, String, Map<String, List<InstanceMinuteStats>>> resultTable = HashBasedTable.create();\n        for (ForkJoinTask<Table<Integer, String, Map<String, List<InstanceMinuteStats>>>> task : taskList) {\n            try {\n                Table<Integer, String, Map<String, List<InstanceMinuteStats>>> instanceTable = task.get(3000, TimeUnit.MILLISECONDS);\n                resultTable.putAll(instanceTable);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n\n        logger.warn(\"get total {} online master instances diffMapList cost sum {} ms\", onlineMasterList.size(), (System.currentTimeMillis() - start));\n\n        return resultTable;\n    }\n\n\n    private InstanceCommandStats parseCommand(long instanceId, String command,\n                                              Map<String, Object> commandMap, boolean isCommand, int type) {\n        Long collectTime = MapUtils.getLong(commandMap, ConstUtils.COLLECT_TIME, null);\n        if (collectTime == null) {\n            return null;\n        }\n        Long count;\n        if (isCommand) {\n            count = MapUtils.getLong(commandMap, \"cmdstat_\" + command.toLowerCase(), null);\n        } else {\n            count = MapUtils.getLong(commandMap, command.toLowerCase(), null);\n        }\n        if (count == null) {\n            return null;\n        }\n        InstanceCommandStats stats = new InstanceCommandStats();\n        stats.setCommandCount(count);\n        stats.setCommandName(command);\n        stats.setCollectTime(collectTime);\n        stats.setCreateTime(DateUtil.getDateByFormat(String.valueOf(collectTime), \"yyyyMMddHHmm\"));\n        stats.setModifyTime(DateUtil.getDateByFormat(String.valueOf(collectTime), \"yyyyMMddHHmm\"));\n        stats.setInstanceId(instanceId);\n\n        return stats;\n    }\n\n    private InstanceMinuteStats parseMinuteStats(long instanceId, String command,\n                                                 Map<String, Object> commandMap, boolean isCommand, int type) {\n        Long collectTime = MapUtils.getLong(commandMap, ConstUtils.COLLECT_TIME, null);\n        if (collectTime == null) {\n            return null;\n        }\n        Double count;\n        if (isCommand) {\n            count = MapUtils.getDouble(commandMap, \"cmdstat_\" + command.toLowerCase(), null);\n        } else {\n            count = MapUtils.getDouble(commandMap, command.toLowerCase(), null);\n        }\n        if (count == null) {\n            return null;\n        }\n        InstanceMinuteStats stats = new InstanceMinuteStats();\n        stats.setMemFragmentationRatio(count);\n        stats.setCommandName(command);\n        stats.setCollectTime(collectTime);\n        stats.setCreateTime(DateUtil.getDateByFormat(String.valueOf(collectTime), \"yyyyMMddHHmm\"));\n        stats.setModifyTime(DateUtil.getDateByFormat(String.valueOf(collectTime), \"yyyyMMddHHmm\"));\n        stats.setInstanceId(instanceId);\n\n        return stats;\n    }\n\n    @Override\n    public String executeCommand(String host, int port, String command) {\n        if (StringUtils.isBlank(host) || port == 0) {\n            return \"host or port is null\";\n        }\n        InstanceInfo instanceInfo = instanceDao.getAllInstByIpAndPort(host, port);\n        if (instanceInfo == null) {\n            return \"instance not exist\";\n        }\n        if (TypeUtil.isRedisType(instanceInfo.getType())) {\n            return redisCenter.executeCommand(instanceInfo.getAppId(), host, port, command);\n        }\n        return \"not support type\";\n    }\n\n    @Override\n    public String executeCommand(Long instanceId, String command) {\n        InstanceInfo instanceInfo = getInstanceInfo(instanceId);\n        return executeCommand(instanceInfo.getIp(), instanceInfo.getPort(), command);\n    }\n\n    @Override\n    public List<InstanceStats> getInstanceStats() {\n        return instanceStatsDao.getInstanceStats();\n    }\n\n    @Override\n    public List<InstanceStats> getInstanceStats(String ip) {\n        List<InstanceStats> instanceStatsList = instanceStatsDao.getInstanceStatsByIp(ip);\n        return instanceStatsList;\n    }\n\n    @Override\n    public boolean saveStandardStats(Map<String, Object> infoMap, Map<String, Object> clusterInfoMap, String ip,\n                                     int port, String dbType) {\n        Assert.isTrue(infoMap != null && infoMap.size() > 0);\n        Assert.isTrue(StringUtils.isNotBlank(ip));\n        Assert.isTrue(port > 0);\n        Assert.isTrue(infoMap.containsKey(ConstUtils.COLLECT_TIME), ConstUtils.COLLECT_TIME + \" not in infoMap\");\n        long collectTime = MapUtils.getLong(infoMap, ConstUtils.COLLECT_TIME);\n        StandardStats ss = new StandardStats();\n        ss.setCollectTime(collectTime);\n        ss.setIp(ip);\n        ss.setPort(port);\n        ss.setDbType(dbType);\n        if (infoMap.containsKey(RedisConstant.DIFF.getValue())) {\n            Map<String, Object> diffMap = (Map<String, Object>) infoMap.get(RedisConstant.DIFF.getValue());\n            ss.setDiffMap(diffMap);\n            infoMap.remove(RedisConstant.DIFF.getValue());\n        } else {\n            ss.setDiffMap(new HashMap<String, Object>(0));\n        }\n        ss.setInfoMap(infoMap);\n        ss.setClusterInfoMap(clusterInfoMap);\n\n        int mergeStandardCount = standardStatsDao.mergeStandardStats(ss);\n        int mergeInstanceCount = standardStatsDao.mergeInstanceMinuteStats(ss);\n        return mergeStandardCount > 0 && mergeInstanceCount > 0;\n    }\n\n    @Override\n    public Map<String, Object> queryStandardInfoMap(long collectTime, String ip, int port, String dbType) {\n        Assert.isTrue(StringUtils.isNotBlank(ip));\n        Assert.isTrue(port > 0);\n        Assert.isTrue(collectTime > 0);\n        StandardStats ss = standardStatsDao.getStandardStats(collectTime, ip, port, dbType);\n        if (ss != null) {\n            Map<String, Object> infoMap = ss.getInfoMap();\n            Map<String, Object> diffMap = ss.getDiffMap();\n            infoMap.put(RedisConstant.DIFF.getValue(), diffMap);\n            return infoMap;\n        } else {\n            return Collections.emptyMap();\n        }\n    }\n\n    @Override\n    public List<Map<String, Object>> queryDiffMapList(long beginTime, long endTime, String ip, int port,\n                                                      String dbType) {\n        Assert.isTrue(StringUtils.isNotBlank(ip));\n        Assert.isTrue(port > 0);\n        Assert.isTrue(beginTime > 0);\n        Assert.isTrue(endTime > 0);\n        List<StandardStats> list = standardStatsDao.getDiffJsonList(beginTime, endTime, ip, port, dbType);\n        if (list == null || list.isEmpty()) {\n            return new ArrayList<Map<String, Object>>(0);\n        }\n        List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>(list.size());\n        for (StandardStats ss : list) {\n            Map<String, Object> diffMap = ss.getDiffMap();\n            diffMap.put(ConstUtils.COLLECT_TIME, ss.getCollectTime());\n            resultList.add(diffMap);\n        }\n        return resultList;\n    }\n\n    class QueryDiffMapListTask extends KeyCallable<Table<Integer, String, Map<String, List<InstanceMinuteStats>>>> {\n\n        private InstanceInfo instance;\n        private long beginTime;\n        private long endTime;\n        private List<String> commands;\n        private String dbType;\n\n        public QueryDiffMapListTask(InstanceInfo instance, long beginTime, long endTime,\n                                    List<String> commands, String dbType) {\n            super(\"QueryDiffMapListTask-\" + instance.getIp() + \":\" + instance.getPort());\n            this.instance = instance;\n            this.beginTime = beginTime;\n            this.endTime = endTime;\n            this.commands = commands;\n            this.dbType = dbType;\n        }\n\n        @Override\n        public Table<Integer, String, Map<String, List<InstanceMinuteStats>>> execute() {\n            int instanceId = instance.getId();\n            String ip = instance.getIp();\n            int port = instance.getPort();\n            int type = instance.getType();\n            List<Map<String, Object>> objectList = queryDiffMapList(beginTime, endTime, ip, port, dbType);\n            Table<Integer, String, Map<String, List<InstanceMinuteStats>>> table = HashBasedTable.create();\n            if (objectList != null) {\n                Map<String, List<InstanceMinuteStats>> commandMap = new LinkedHashMap<>();\n                for (String commandName : commands) {\n                    List<InstanceMinuteStats> resultList = new ArrayList<>(objectList.size());\n                    for (Map<String, Object> map : objectList) {\n                        InstanceMinuteStats stats = parseMinuteStats(instanceId, commandName, map, false, type);\n                        if (stats != null) {\n                            resultList.add(stats);\n                        }\n                    }\n                    commandMap.put(commandName, resultList);\n                }\n                table.put(instanceId, instance.getHostPort(), commandMap);\n            }\n            return table;\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/swagger/Swagger2.java",
    "content": "package com.sohu.cache.swagger;\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.builders.ApiInfoBuilder;\nimport springfox.documentation.builders.PathSelectors;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.service.ApiInfo;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n@ConditionalOnProperty(name = \"springfox.documentation.enabled\", havingValue = \"true\")\n@Configuration\n@EnableSwagger2\npublic class Swagger2 {\n\n    @Bean\n    public Docket createRestApi() {\n        return new Docket(DocumentationType.SWAGGER_2)\n                .apiInfo(apiInfo())\n                .select()\n                .apis(RequestHandlerSelectors.basePackage(\"com.sohu.cache.web.controller\"))\n                .paths(PathSelectors.any())\n                .build();\n    }\n\n    private ApiInfo apiInfo() {\n        return new ApiInfoBuilder()\n                .title(\"CacheCloud项目 RESTful APIs\")\n                .description(\"CacheCloud项目后台api接口文档\")\n                .version(\"3.0\")\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/BaseTask.java",
    "content": "package com.sohu.cache.task;\n\nimport com.google.common.collect.Lists;\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.protocol.RedisProtocol;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.RedisConfigTemplateService;\nimport com.sohu.cache.redis.RedisDeployCenter;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.ssh.SSHTemplate;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\nimport com.sohu.cache.task.constant.InstanceInfoEnum;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceStatusEnum;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.NutCrackerNode;\nimport com.sohu.cache.task.entity.RedisSentinelNode;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.task.entity.TaskQueue;\nimport com.sohu.cache.task.util.AppWechatUtil;\nimport com.sohu.cache.web.service.AppScrollRestartService;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.InstancePortService;\nimport com.sohu.cache.web.service.ResourceService;\nimport com.sohu.cache.web.service.ToolService;\nimport com.sohu.cache.web.util.AppEmailUtil;\nimport com.sohu.cache.web.util.SimpleFileUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.Marker;\nimport org.slf4j.MarkerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.core.env.Environment;\n\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * @author fulei\n */\npublic abstract class BaseTask {\n\n    protected Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    public final static String MARKER_NAME = \"task_logger\";\n\n    public final static Marker marker = MarkerFactory.getMarker(MARKER_NAME);\n\n    @Value(\"${spring.profiles.active}\")\n    protected String appEnvName;\n\n    /**\n     * 任务参数\n     */\n    protected Map<String, Object> paramMap;\n\n    @Autowired\n    protected InstanceBigKeyDao instanceBigKeyDao;\n\n    @Autowired\n    protected InstanceStatsDao instanceStatsDao;\n\n    @Autowired\n    protected AppEmailUtil appEmailUtil;\n\n    @Autowired\n    protected AppWechatUtil appWechatUtil;\n\n    @Autowired\n    protected MachineCenter machineCenter;\n\n    @Autowired\n    protected RedisConfigTemplateService redisConfigTemplateService;\n\n    @Autowired\n    protected AppService appService;\n\n    @Autowired\n    protected AppStatsCenter appStatsCenter;\n\n    @Autowired\n    protected SSHService sshService;\n\n    @Autowired\n    protected AppDao appDao;\n\n    @Autowired\n    protected RedisCenter redisCenter;\n\n    @Autowired\n    protected MachineDao machineDao;\n\n    @Autowired\n    protected InstanceDao instanceDao;\n\n    @Autowired\n    protected AppAuditDao appAuditDao;\n\n    @Autowired\n    protected TaskService taskService;\n\n    @Autowired\n    protected RedisDeployCenter redisDeployCenter;\n\n    @Autowired\n    protected MachineRoomDao machineRoomDao;\n\n    @Autowired\n    protected MachineStatsDao machineStatsDao;\n\n    @Autowired\n    protected MachineRelationDao machineRelationDao;\n\n    @Autowired\n    protected AssistRedisService assistRedisService;\n\n    @Autowired\n    protected InstancePortService instancePortService;\n\n    @Autowired\n    protected Environment environment;\n\n    @Autowired\n    protected InstanceDeployCenter instanceDeployCenter;\n\n    @Autowired\n    protected DiagnosticTaskRecordDao diagnosticTaskRecordDao;\n\n    @Autowired\n    protected ResourceService resourceService;\n\n    @Autowired\n    protected ResourceDao resourceDao;\n\n    @Autowired\n    protected ToolService toolService;\n\n    @Autowired\n    protected AppScrollRestartService appScrollRestartService;\n\n    @Autowired\n    protected AsyncService asyncService;\n\n    /**\n     * 任务id\n     */\n    protected long taskId;\n\n    /**\n     * redis common启动模板文件\n     */\n    private final static String REDIS_COMMON_TEMPLATE_FILE = \"scripts/redis_common_control.txt\";\n    private final static String REDIS_COMMON_TEMPLATE_NO_CPUIDX_FILE = \"scripts/redis_common_control_no_cpuidx.txt\";\n\n    /**\n     * nutcracker启动模板文件\n     */\n    private final static String NUT_CRACKER_TEMPLATE_FILE = \"scripts/nutcracker_control.txt\";\n    private final static String NUT_CRACKER_TEMPLATE_NO_CPUIDX_FILE = \"scripts/nutcracker_control_no_cpuidx.txt\";\n\n    /**\n     * redis port启动模板文件\n     */\n    private final static String REDIS_PORT_TEMPLATE_FILE = \"scripts/redis-port.txt\";\n\n    /**\n     * redis migrate启动模板文件\n     */\n    private final static String REDIS_MIGRATE_TOOL_TEMPLATE_FILE = \"scripts/redis-migrate-tool.txt\";\n\n    /**\n     * pika启动模板文件\n     */\n    private final static String PIKA_TEMPLATE_FILE = \"scripts/pika.txt\";\n\n    public abstract List<String> getTaskSteps();\n\n    public TaskFlowStatusEnum init() {\n        taskId = MapUtils.getLongValue(paramMap, TaskConstants.TASK_ID_KEY);\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public Map<String, Object> getParamMap() {\n        return paramMap;\n    }\n\n    public void setParamMap(Map<String, Object> paramMap) {\n        this.paramMap = paramMap;\n    }\n\n    /**\n     * 准备实例相关目录\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param instanceTypeEnum\n     * @return\n     */\n    protected TaskFlowStatusEnum prepareRelateDir(long appId, String host, int port,\n                                                  InstanceInfoEnum.InstanceTypeEnum instanceTypeEnum) {\n        //准备实例基准目录 todo\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * @param cpuidx\n     * @param startCmd\n     * @param runCmd\n     * @param serviceShellFileName\n     * @return\n     */\n    private List<String> generateRedisPortServiceShell(int cpuidx, String startCmd, String runCmd,\n                                                       String serviceShellFileName) {\n        Map<String, String> replaceMap = new HashMap<String, String>();\n        replaceMap.put(\"${cpuidx}\", String.valueOf(cpuidx));\n        replaceMap.put(\"${startcmd}\", startCmd);\n        replaceMap.put(\"${runcmd}\", runCmd);\n        replaceMap.put(\"${PID_FILE}\", RedisProtocol.getRedisPortPidFilePath());\n\n        List<String> resultList = generateRealShell(REDIS_PORT_TEMPLATE_FILE, replaceMap);\n        if (CollectionUtils.isEmpty(resultList)) {\n            logger.error(marker, \"{} {} {} service shell is empty!\", cpuidx, startCmd, runCmd);\n        }\n        return resultList;\n    }\n\n    /**\n     * 生成实际shell\n     *\n     * @param replaceMap\n     * @return\n     */\n    private List<String> generateRealShell(String templateFile, Map<String, String> replaceMap) {\n        List<String> resultList = new ArrayList<String>();\n        List<String> lines = SimpleFileUtil.getListFromFile(templateFile, \"utf-8\");\n        for (String line : lines) {\n            for (Entry<String, String> entry : replaceMap.entrySet()) {\n                String key = entry.getKey();\n                String value = entry.getValue();\n                if (line.contains(key)) {\n                    line = line.replace(key, value);\n                }\n            }\n            resultList.add(line);\n        }\n        return resultList;\n    }\n\n    /**\n     * @param host\n     * @param cpuidx\n     * @param startCmd\n     * @param runCmd\n     * @param serviceShellFileName\n     * @return\n     */\n    private List<String> generateRedisCommonServiceShell(String host, int cpuidx, String startCmd, String runCmd,\n                                                         String serviceShellFileName) {\n        Map<String, String> replaceMap = new HashMap<String, String>();\n        replaceMap.put(\"${cpuidx}\", String.valueOf(cpuidx));\n        replaceMap.put(\"${startcmd}\", startCmd);\n        replaceMap.put(\"${runcmd}\", runCmd);\n\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n        if (machineInfo == null) {\n            logger.error(marker, \"machine {} is empty\", host);\n            return Collections.emptyList();\n        }\n\n        String templateFile = machineInfo.isYunMachine() ?\n                REDIS_COMMON_TEMPLATE_NO_CPUIDX_FILE :\n                REDIS_COMMON_TEMPLATE_FILE;\n        List<String> resultList = generateRealShell(templateFile, replaceMap);\n        if (CollectionUtils.isEmpty(resultList)) {\n            logger.error(marker, \"{} {} {} service shell is empty!\", cpuidx, startCmd, runCmd);\n        }\n        return resultList;\n    }\n\n    /**\n     * @param host\n     * @param cpuidx\n     * @param startCmd\n     * @param runCmd\n     * @param serviceShellFileName\n     * @return\n     */\n    private List<String> generateNutCrackerServiceShell(String host, int cpuidx, String startCmd, String runCmd,\n                                                        String serviceShellFileName) {\n        Map<String, String> replaceMap = new HashMap<String, String>();\n        replaceMap.put(\"${cpuidx}\", String.valueOf(cpuidx));\n        replaceMap.put(\"${startcmd}\", startCmd);\n        replaceMap.put(\"${runcmd}\", runCmd);\n\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n        if (machineInfo == null) {\n            logger.error(marker, \"machine {} is empty\", host);\n            return Collections.emptyList();\n        }\n\n        String templateFile = machineInfo.isYunMachine() ?\n                NUT_CRACKER_TEMPLATE_NO_CPUIDX_FILE :\n                NUT_CRACKER_TEMPLATE_FILE;\n        List<String> resultList = generateRealShell(templateFile, replaceMap);\n        if (CollectionUtils.isEmpty(resultList)) {\n            logger.error(marker, \"{} {} {} service shell is empty!\", cpuidx, startCmd, runCmd);\n        }\n        return resultList;\n    }\n\n    /**\n     * @param cpuidx\n     * @param startCmd\n     * @param runCmd\n     * @param serviceShellFileName\n     * @return\n     */\n    private List<String> generateRedisMigrateServiceShell(int cpuidx, String startCmd, String runCmd,\n                                                          String serviceShellFileName) {\n        Map<String, String> replaceMap = new HashMap<String, String>();\n        replaceMap.put(\"${cpuidx}\", String.valueOf(cpuidx));\n        replaceMap.put(\"${startcmd}\", startCmd);\n        replaceMap.put(\"${runcmd}\", runCmd);\n\n        List<String> resultList = generateRealShell(REDIS_MIGRATE_TOOL_TEMPLATE_FILE, replaceMap);\n        if (CollectionUtils.isEmpty(resultList)) {\n            logger.error(marker, \"{} {} {} service shell is empty!\", cpuidx, startCmd, runCmd);\n        }\n        return resultList;\n    }\n\n    /**\n     * @param cpuidx\n     * @param startCmd\n     * @param runCmd\n     * @param serviceShellFileName\n     * @return\n     */\n    private List<String> generatePikaServiceShell(int cpuidx, String startCmd, String runCmd,\n                                                  String serviceShellFileName) {\n        Map<String, String> replaceMap = new HashMap<String, String>();\n        replaceMap.put(\"${cpuidx}\", String.valueOf(cpuidx));\n        replaceMap.put(\"${startcmd}\", startCmd);\n        replaceMap.put(\"${runcmd}\", runCmd);\n\n        List<String> resultList = generateRealShell(PIKA_TEMPLATE_FILE, replaceMap);\n        if (CollectionUtils.isEmpty(resultList)) {\n            logger.error(marker, \"{} {} {} service shell is empty!\", cpuidx, startCmd, runCmd);\n        }\n        return resultList;\n    }\n\n    /**\n     * 生成并推service\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param instanceRemoteBasePath\n     * @param instanceLocalTmpPath\n     * @param cpuidx\n     * @param startCmd\n     * @param runCmd\n     * @param serviceShellFileName\n     * @return\n     */\n    protected TaskFlowStatusEnum pushService(long appId, InstanceTypeEnum instanceTypeEnum, String host, int port,\n                                             String instanceRemoteBasePath,\n                                             String instanceLocalTmpPath, int cpuidx, String startCmd, String runCmd, String serviceShellFileName) {\n        //生成和推送脚本\n        List<String> serviceShell;\n        if (instanceTypeEnum.equals(InstanceTypeEnum.REDIS_PORT)) {\n            serviceShell = generateRedisPortServiceShell(cpuidx, startCmd, runCmd, serviceShellFileName);\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.REDIS_MIGRATE_TOOL)) {\n            serviceShell = generateRedisMigrateServiceShell(cpuidx, startCmd, runCmd, serviceShellFileName);\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.NUTCRACKER)) {\n            serviceShell = generateNutCrackerServiceShell(host, cpuidx, startCmd, runCmd, serviceShellFileName);\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.PIKA)) {\n            serviceShell = generatePikaServiceShell(cpuidx, startCmd, runCmd, serviceShellFileName);\n        } else {\n            serviceShell = generateRedisCommonServiceShell(host, cpuidx, startCmd, runCmd, serviceShellFileName);\n        }\n\n        String remoteFile = machineCenter.createRemoteFile(host, serviceShellFileName, serviceShell);\n        if (StringUtils.isBlank(remoteFile)) {\n            logger.error(marker, \"{} {}:{} {} push fail\", appId, host, port, remoteFile);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //修改执行权限\n        //todo\n        //ChmodEnum chmodEnum = ChmodEnum.EXECUTE;\n        //String remoteFilePath = instanceRemoteBasePath + \"/\" + serviceShellFileName;\n        //boolean isSuccess = machineCenter.chmod(host, remoteFilePath, chmodEnum);\n        //        if (!isSuccess) {\n        //            logger.error(marker, \"{} {}:{} {} chmod +x fail\", appId, host, port, remoteFilePath);\n        //            return TaskFlowStatusEnum.ABORT;\n        //        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 准备相关bin\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param instanceTypeEnum\n     * @param version\n     * @return\n     */\n    protected TaskFlowStatusEnum prepareRelateBin(long appId, String host, int port, InstanceTypeEnum instanceTypeEnum,\n                                                  String version) {\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} get appDesc is empty\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 准备相关bin\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @return\n     */\n    protected TaskFlowStatusEnum preparePikaBin(long appId, String host, int port, InstanceTypeEnum instanceTypeEnum) {\n        // todo\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 实例进程是否存在\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param instanceTypeEnum\n     * @return\n     */\n    protected TaskFlowStatusEnum checkInstanceIsExist(long appId, String host, int port,\n                                                      InstanceTypeEnum instanceTypeEnum) {\n        //1. 检查目录是否存在\n        //        String remoteBasePath = machineCenter.getInstanceRemoteBasePath(appId, port, instanceTypeEnum);\n        //        if (StringUtils.isBlank(remoteBasePath)) {\n        //            logger.error(marker, \"appId {} host {} port {} remoteBasePath is empty\", appId, host, port);\n        //            return TaskFlowStatusEnum.ABORT;\n        //        }\n        //        boolean isExist = machineCenter.checkExistDir(host, remoteBasePath);\n        //        if (isExist) {\n        //            logger.error(marker, \"{} {} already exists\", host, remoteBasePath);\n        //            return TaskFlowStatusEnum.ABORT;\n        //        }\n        //2. 检查进程是否存在\n        if (instanceTypeEnum.equals(InstanceTypeEnum.REDIS_PORT)) {\n            if (redisCenter.isRun(host, port)) {\n                logger.error(marker, \"{} {}:{} is already run\", instanceTypeEnum.getInfo(), host, port);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.REDIS_SERVER)\n                || instanceTypeEnum.equals(InstanceTypeEnum.REDIS_CLUSTER)\n                || instanceTypeEnum.equals(InstanceTypeEnum.REDIS_SENTINEL)\n                || instanceTypeEnum.equals(InstanceTypeEnum.PIKA)\n                || instanceTypeEnum.equals(InstanceTypeEnum.NUTCRACKER)\n                || instanceTypeEnum.equals(InstanceTypeEnum.CODIS_SERVER)\n                || instanceTypeEnum.equals(InstanceTypeEnum.CODIS_PROXY)) {\n            if (redisCenter.isRun(host, port)) {\n                logger.error(marker, \"{} {}:{} is already run\", instanceTypeEnum.getInfo(), host, port);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 实例进程是否不存在（不和上面写一起了，虽然冗余了）\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param instanceTypeEnum\n     * @return\n     */\n    protected TaskFlowStatusEnum checkInstanceIsNotExist(long appId, String host, int port,\n                                                         InstanceTypeEnum instanceTypeEnum) {\n        //todo\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 保存实例信息\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param maxMemory\n     * @param instanceTypeEnum\n     * @param instanceStatusEnum\n     * @param cmd\n     * @return\n     */\n    protected Integer saveInstance(long appId, String host, int port, int maxMemory,\n                                   InstanceTypeEnum instanceTypeEnum, InstanceStatusEnum instanceStatusEnum, String cmd) {\n        InstanceInfo instanceInfo;\n        try {\n            instanceInfo = new InstanceInfo();\n            instanceInfo.setAppId(appId);\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n            if (machineInfo == null) {\n                logger.error(marker, \"host {} machineInfo is empty\", host);\n                return null;\n            }\n            instanceInfo.setHostId(machineInfo.getId());\n            instanceInfo.setMem(maxMemory);\n            instanceInfo.setStatus(InstanceStatusEnum.NEW_STATUS.getStatus());\n            instanceInfo.setPort(port);\n            instanceInfo.setType(instanceTypeEnum.getType());\n            instanceInfo.setCmd(cmd);\n            instanceInfo.setIp(host);\n\n\n            instanceDao.saveInstance(instanceInfo);\n            logger.info(marker, \"{} {}:{} save success\", instanceTypeEnum.getInfo(), host, port);\n            return instanceInfo.getId();\n        } catch (Exception e) {\n            logger.error(marker, \"{} {}:{} save error\" + e.getMessage(), instanceTypeEnum.getInfo(), host, port, e);\n            return null;\n        }\n    }\n\n    /**\n     * low b 版本\n     *\n     * @param currentTaskId\n     * @param timeoutSeconds 超时时间\n     * @return\n     */\n    protected TaskFlowStatusEnum waitTaskFinish(long currentTaskId, int timeoutSeconds) {\n        // 耗时\n        int totalSeconds = 0;\n        while (true) {\n            // 简单的计时器\n            long startTime = System.currentTimeMillis();\n            TaskQueue taskQueue = taskService.getTaskQueueById(currentTaskId);\n            logger.warn(\"task {} totalSeconds is {}, timeout is {}, status:{}\", currentTaskId, totalSeconds, timeoutSeconds, taskQueue.getStatusDesc());\n            if (taskQueue.isSuccess()) {\n                return TaskFlowStatusEnum.SUCCESS;\n            } else if (taskQueue.isAbort()) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n            try {\n                TimeUnit.SECONDS.sleep(1);\n            } catch (InterruptedException e) {\n                logger.error(marker, e.getMessage(), e);\n            }\n            // 简单的计时器(秒)\n            long costTime = System.currentTimeMillis() - startTime;\n            totalSeconds += (costTime / 1000);\n            if (totalSeconds > timeoutSeconds) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n    }\n\n    protected void sleepSeconds(int seconds) {\n        try {\n            TimeUnit.SECONDS.sleep(seconds);\n        } catch (InterruptedException e) {\n            logger.error(marker, e.getMessage(), e);\n        }\n    }\n\n    /**\n     * 获取所有master节点\n     *\n     * @return\n     */\n    protected List<RedisServerNode> getAppMasterRedisServerNodes(long appId) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String appFullName = appDesc.getName();\n        if (StringUtils.isBlank(appFullName)) {\n            logger.error(marker, \"appId {} fullName is empty\", appId);\n            return Collections.emptyList();\n        }\n        List<InstanceInfo> masterRedisServerInfoList = appService.getAppMasterInstanceInfoList(appId);\n        List<RedisServerNode> masterRedisServerNodes = new ArrayList<RedisServerNode>();\n        for (InstanceInfo instanceInfo : masterRedisServerInfoList) {\n            RedisServerNode masterRedisServerNode = new RedisServerNode();\n            masterRedisServerNode.setIp(instanceInfo.getIp());\n            masterRedisServerNode.setPort(instanceInfo.getPort());\n            String masterName = generateMasterName(appDesc.getName(), instanceInfo.getPort());\n            masterRedisServerNode.setMasterName(masterName);\n            masterRedisServerNodes.add(masterRedisServerNode);\n        }\n        return masterRedisServerNodes;\n    }\n\n    /**\n     * 清理falcon\n     *\n     * @param instanceTypeEnum\n     * @return\n     */\n    protected TaskFlowStatusEnum clearFalconConfig(String host, InstanceTypeEnum instanceTypeEnum) {\n        String config = \"\";\n        if (instanceTypeEnum.equals(InstanceTypeEnum.REDIS_SERVER)) {\n            config = \"/tmp/redisPort.txt\";\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.NUTCRACKER)) {\n            config = \"/tmp/nutcrackerPort.txt\";\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.REDIS_SENTINEL)) {\n            config = \"/tmp/redis_sentinel_port.txt\";\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.PIKA)) {\n            config = \"/tmp/pika_port.txt\";\n        } else if (instanceTypeEnum.equals(InstanceTypeEnum.MEMCACHE)) {\n            config = \"/tmp/memcache_port.txt\";\n        }\n        if (StringUtils.isNotBlank(config)) {\n            try {\n                String command = \"echo '' > \" + config;\n                logger.info(marker, \"{} execute command {}\", host, command);\n                machineCenter.executeShell(host, command);\n            } catch (Exception e) {\n                logger.error(marker, e.getMessage(), e);\n            }\n        } else {\n            logger.info(marker, \"instanceTypeEnum {} falcon config is empty\", instanceTypeEnum.getInfo());\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 机器统计更新判断\n     *\n     * @param machineStats\n     * @return\n     */\n    protected boolean checkMachineStatIsUpdate(MachineStats machineStats) {\n        //todo\n        return true;\n        //Date updateTime = machineStats.getUpdateTime();\n        //return updateTime.getTime() > DateUtils.addHours(new Date(), -5).getTime();\n    }\n\n    protected TaskFlowStatusEnum checkResourceAllow(List<String> redisServerMachineList, long memoryNeed){\n        for (String redisServerIp : redisServerMachineList) {\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(redisServerIp);\n            if (machineStats == null) {\n                logger.error(marker, \"{} redis server machineStats is null\", redisServerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(redisServerIp);\n            if (machineInfo == null) {\n                logger.error(marker, \"redis server machine info is null\");\n                return TaskFlowStatusEnum.ABORT;\n            }\n            // 机器是否分配 isAllocate\n            if (machineInfo.getIsAllocating() == 1) {\n                logger.error(marker, \"redis server machine info {} {} allocating is 1\", machineInfo.getIp(), redisServerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            if (!checkMachineStatIsUpdate(machineStats)) {\n                logger.error(marker, \"redis server machine stats {} update_time is {}, may be not updated recently\", machineInfo.getIp(), machineStats.getUpdateTimeFormat());\n                return TaskFlowStatusEnum.ABORT;\n            }\n            //兆\n            long memoryFree = NumberUtils.toLong(machineStats.getMemoryFree()) / 1024 / 1024;\n            if (memoryNeed > memoryFree * 0.85) {\n                logger.error(marker, \"{} need {} MB, but memoryFree is {} MB\", redisServerIp, memoryNeed, memoryFree);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 计算quorum\n     *\n     * @return\n     */\n    protected int getQuorum(int sentinelSize) {\n        int quorum;\n        if (sentinelSize % 2 == 0) {\n            quorum = sentinelSize / 2;\n        } else {\n            quorum = sentinelSize / 2 + 1;\n        }\n        return Math.max(quorum, 1);\n    }\n\n    /**\n     * 执行一个ls命令，确认ssh以及不是只读盘\n     *\n     * @return\n     */\n    protected boolean checkMachineIsConnect(String ip) {\n        try {\n            SSHTemplate.Result result = sshService.executeWithResult(ip, \"ls / | wc -l\");\n            if (result.isSuccess()) {\n                return true;\n            }\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage());\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    protected TaskFlowStatusEnum checkMachineConnect(List<String> ipList, String errorTip) {\n        for (String redisServerIp : ipList) {\n            boolean isConnected = checkMachineIsConnect(redisServerIp);\n            if (!isConnected) {\n                logger.error(marker, errorTip, redisServerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 获取复制master需要等待的秒数\n     *\n     * @param masterHost\n     * @param masterPort\n     * @return\n     */\n    protected int getSlaveOfSleepSeconds(String masterHost, int masterPort) {\n        InstanceStats instanceStats = instanceStatsDao.getInstanceStatsByHost(masterHost, masterPort);\n        long usedMemoryMB = instanceStats.getUsedMemory() / 1024 / 1024;\n        //40MB一秒，一分钟2GB\n        int seconds = (int) (usedMemoryMB / 40 + 5);\n        logger.info(BaseTask.marker, \"{}:{} usedMemory is {}MB need sleep {} seconds\", masterHost, masterPort,\n                usedMemoryMB, seconds);\n        return seconds;\n    }\n\n    protected List<NutCrackerNode> transformNutCrackerFromInstance(List<InstanceInfo> instanceInfoList) {\n        List<NutCrackerNode> nutCrackerNodes = new ArrayList<NutCrackerNode>();\n        for (InstanceInfo instanceInfo : instanceInfoList) {\n            NutCrackerNode nutCrackerNode = new NutCrackerNode(instanceInfo.getIp(), instanceInfo.getPort());\n            nutCrackerNodes.add(nutCrackerNode);\n        }\n        return nutCrackerNodes;\n    }\n\n    protected List<RedisSentinelNode> transformRedisSentinelFromInstance(List<InstanceInfo> instanceInfoList) {\n        List<RedisSentinelNode> redisSentinelNodes = new ArrayList<RedisSentinelNode>();\n        for (InstanceInfo instanceInfo : instanceInfoList) {\n            RedisSentinelNode redisSentinelNode = new RedisSentinelNode(instanceInfo.getIp(), instanceInfo.getPort());\n            redisSentinelNodes.add(redisSentinelNode);\n        }\n        return redisSentinelNodes;\n    }\n\n    /**\n     * 生成masterName\n     *\n     * @param appFullName\n     * @param port\n     * @return\n     */\n    public static String generateMasterName(String appFullName, int port) {\n        return appFullName + \"-\" + port;\n    }\n\n\n    /**\n     * @param redisServerNodes\n     * @param appId\n     * @return\n     */\n    protected List<RedisServerNode> buildRedisServerNodes(List<RedisServerNode> redisServerNodes, long appId) {\n        List<RedisServerNode> list = Lists.newArrayList();\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            List<InstanceInfo> instanceInfoList = appService.getAppMasterInstanceInfoList(appId);\n            redisServerNodes = transformRedisServerFromInstance(instanceInfoList);\n            list.addAll(redisServerNodes);\n        } else {\n            for (RedisServerNode redisServerNode : redisServerNodes) {\n                list.add(new RedisServerNode(redisServerNode.getIp(), redisServerNode.getPort()));\n            }\n        }\n        return list;\n    }\n\n    private List<RedisServerNode> transformRedisServerFromInstance(List<InstanceInfo> instanceInfoList) {\n        List<RedisServerNode> redisServerNodes = new ArrayList<>();\n        for (int i = 0; i < instanceInfoList.size(); i++) {\n            InstanceInfo instanceInfo = instanceInfoList.get(i);\n            RedisServerNode redisServerNode = new RedisServerNode();\n            redisServerNode.setIp(instanceInfo.getIp());\n            redisServerNode.setPort(instanceInfo.getPort());\n            redisServerNodes.add(redisServerNode);\n        }\n        return redisServerNodes;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/TaskService.java",
    "content": "package com.sohu.cache.task;\n\nimport com.sohu.cache.constant.OperateResult;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.task.constant.PikaNode;\nimport com.sohu.cache.task.constant.TaskQueueEnum.TaskStatusEnum;\nimport com.sohu.cache.task.entity.*;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 任务相关\n */\npublic interface TaskService {\n\n    /**\n     * 执行任务\n     *\n     * @param taskId\n     * @return\n     */\n    TaskStatusEnum executeTask(long taskId);\n\n    /**\n     * @param taskId\n     * @return\n     */\n    List<TaskStepFlow> getTaskStepFlowList(long taskId);\n\n    /**\n     * @param taskId\n     * @return\n     */\n    TaskQueue getTaskQueueById(long taskId);\n\n    /**\n     * 任务流描述信息\n     *\n     * @param className\n     * @return\n     */\n    List<TaskStepMeta> getTaskStepMetaList(String className);\n\n    /**\n     * 获取当前任务流\n     *\n     * @param taskId\n     * @return\n     */\n    TaskStepFlow getCurrentTaskStepFlow(long taskId);\n\n    /**\n     * 添加pika实例安装任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addPikaInstallTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加redis server实例安装任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param maxMemory\n     * @param version\n     * @param parentTaskId\n     */\n    long addRedisServerInstallTask(long appId, String host, int port, int maxMemory, String version, Boolean isCluster, long parentTaskId);\n\n    /**\n     * 添加redis server实例stop任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addRedisServerStopTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加memcache实例stop任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addMemcacheStopTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加memcache集群下线\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     */\n    long addMemcacheClusterOfflineTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * 添加pika实例stop任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addPikaStopTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加redis server实例start任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addRedisServerStartTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加redis sentinel实例安装任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param masterRedisServerNodes\n     * @param quorum\n     * @param dbVersion\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisSentinelInstallTask(long appId, String host, int port,\n                                     List<RedisServerNode> masterRedisServerNodes, int quorum, String dbVersion, long parentTaskId);\n\n    /**\n     * 添加redis sentinel实例stop任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisSentinelStopTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加redis port实例安装任务\n     *\n     * @param appId\n     * @param redisPortHost\n     * @param redisPortHttpPort\n     * @param sourceHost\n     * @param sourcePort\n     * @param targetHost\n     * @param targetPort\n     * @param parentTaskId\n     * @return\n     */\n//    long addRedisPortInstallTask(long appId, String redisPortHost, int redisPortHttpPort, String sourceHost,\n//                                 int sourcePort, String targetHost, int targetPort, long parentTaskId);\n\n    /**\n     * 添加redis migrate tool任务\n     *\n     * @param migrateToolHost\n     * @param migrateToolPort\n     * @param sourceAppId\n     * @param targetAppId\n     * @param sourceRedisServerNodes\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisMigrateToolInstallTask(String migrateToolHost, int migrateToolPort, long sourceAppId, long targetAppId,\n                                        List<RedisServerNode> sourceRedisServerNodes, long parentTaskId);\n\n    /**\n     * 添加nutcracker实例\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param masterRedisServerNodes\n     * @param parentTaskId\n     */\n    long addNutCrackerInstallTask(long appId, String host, int port,\n                                  List<RedisServerNode> masterRedisServerNodes, long parentTaskId);\n\n    /**\n     * 添加codis proxy实例\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addCodisProxyInstallTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加codis dashboard实例\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addCodisDashboardTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加nutcracker实例stop任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     */\n    long addNutCrackerStopTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 添加清理redisserver数据任务\n     *\n     * @param appId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisServerFlushDataTask(long appId, String host, int port, long parentTaskId);\n\n    /**\n     * 应用刷新配置\n     *\n     * @param appId\n     * @param appIsNew\n     * @param parentTaskId\n     * @return\n     */\n    long addAppConfigFlushZkTask(long appId, Boolean appIsNew, long parentTaskId);\n\n    /**\n     * 添加twemproxy应用\n     *\n     * @param appId\n     * @param auditId\n     * @param maxMemory\n     * @param redisServerMachineList\n     * @param redisSentinelMachineList\n     * @param nutCrackerMachineList\n     * @param masterPerMachine\n     * @param sentinelPerMachine\n     * @param nutCrackerPerMachine\n     * @param isNeedFlushZkConfig      是否要刷新zk配置\n     * @param version\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyAppTask(long appId, long auditId, int maxMemory, List<String> redisServerMachineList,\n                             List<String> redisSentinelMachineList, List<String> nutCrackerMachineList, int masterPerMachine,\n                             int sentinelPerMachine, int nutCrackerPerMachine, Boolean isNeedFlushZkConfig, String version,\n                             long parentTaskId);\n\n    /**\n     * redis standalone\n     *\n     * @param appId                  应用id\n     * @param appAuditId             审核id\n     * @param maxMemory              实例内存\n     * @param redisServerMachineList 部署节点\n     * @param masterPerMachine       部署实例数\n     * @param dbVersion              redis版本\n     * @param parentTaskId           父任务id\n     * @return\n     */\n    long addRedisStandaloneAppTask(long appId, long appAuditId, int maxMemory, List<String> redisServerMachineList,\n                                   int masterPerMachine, String dbVersion, long parentTaskId);\n\n    /**\n     * redis cluster集群\n     *\n     * @param appId                  应用id\n     * @param appAuditId             审核id\n     * @param maxMemory              实例内存\n     * @param appDeployInfoList      部署节点信息（masterIp:memory:slaveIp）\n     * @param redisServerMachineList 部署节点\n     * @param masterPerMachine       部署实例数\n     * @param dbVersion              redis版本\n     * @param parentTaskId           父任务id\n     * @return\n     */\n    long addRedisClusterAppTask(long appId, long appAuditId, int maxMemory, List<String> appDeployInfoList,\n                                List<String> redisServerMachineList, int masterPerMachine,\n                                String dbVersion, long parentTaskId);\n\n    /**\n     * redis sentinel集群\n     *\n     * @param appId\n     * @param appAuditId\n     * @param maxMemory\n     * @param redisServerMachineList\n     * @param redisSentinelMachineList\n     * @param masterPerMachine\n     * @param sentinelPerMachine\n     * @param dbVersion\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisSentinelAppTask(long appId, long appAuditId, int maxMemory, List<String> redisServerMachineList,\n                                 List<String> redisSentinelMachineList, int masterPerMachine, int sentinelPerMachine, String dbVersion,\n                                 long parentTaskId);\n\n    /**\n     * 添加twemproxy pika\n     *\n     * @param appId\n     * @param auditId\n     * @param maxMemory\n     * @param pikaMachineList\n     * @param redisSentinelMachineList\n     * @param nutCrackerMachineList\n     * @param masterPerMachine\n     * @param sentinelPerMachine\n     * @param nutCrackerPerMachine\n     * @param isNeedFlushZkConfig      是否要刷新zk配置\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyPikaTask(long appId, long auditId, int maxMemory, List<String> pikaMachineList,\n                              List<String> redisSentinelMachineList, List<String> nutCrackerMachineList, int masterPerMachine,\n                              int sentinelPerMachine, int nutCrackerPerMachine, Boolean isNeedFlushZkConfig, long parentTaskId);\n\n    /**\n     * pika sentinel\n     *\n     * @param appId\n     * @param appAuditId\n     * @param maxMemory\n     * @param pikaMachineList\n     * @param redisSentinelMachineList\n     * @param masterPerMachine\n     * @param sentinelPerMachine\n     * @param parentTaskId\n     * @return\n     */\n    long addPikaSentinelAppTask(long appId, long appAuditId, int maxMemory, List<String> pikaMachineList,\n                                List<String> redisSentinelMachineList, int masterPerMachine, int sentinelPerMachine, long parentTaskId);\n\n    /**\n     * twemproxy redis下线\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyOfflineTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * redis sentinel app下线\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisSentinelAppOfflineTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * pika sentinel app下线\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addPikaSentinelAppOfflineTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * twemproxy pika下线\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyPikaOfflineTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * twemproxy -> twemproxy\n     *\n     * @param sourceAppId\n     * @param targetAppId\n     * @param appAuditId\n     * @param isScaleOut\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyToTwemproxyTask(long sourceAppId, long targetAppId, long appAuditId, boolean isScaleOut,\n                                     long parentTaskId);\n\n    /**\n     * twemproxy -> twemproxy(v2)\n     *\n     * @param sourceAppId\n     * @param targetAppId\n     * @param appAuditId\n     * @param isScaleOut\n     * @param onlyMigrate\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyToTwemproxyTaskV2(long sourceAppId, long targetAppId, long appAuditId, boolean isScaleOut,\n                                       boolean onlyMigrate, long parentTaskId);\n\n    /**\n     * 备库重搭\n     *\n     * @param appId\n     * @param masterHost\n     * @param masterPort\n     * @param slaveMachineHost\n     * @param parentTaskId\n     * @return\n     */\n    long addSlaveRedisServerRebuildTask(long appId, String masterHost, int masterPort,\n                                        String slaveMachineHost, long parentTaskId);\n\n    /**\n     * pika备库重搭\n     *\n     * @param appId\n     * @param masterHost\n     * @param masterPort\n     * @param slaveMachineHost\n     * @param parentTaskId\n     * @return\n     */\n    long addSlavePikaRebuildTask(long appId, String masterHost, int masterPort,\n                                 String slaveMachineHost, long parentTaskId);\n\n    /**\n     * sentinel failover\n     *\n     * @param appId\n     * @param masterHost\n     * @param masterPort\n     * @param parentTaskId\n     * @return\n     */\n    long addSlaveRedisSentinelFailoverTask(long appId, String masterHost, int masterPort, long parentTaskId);\n\n    /**\n     * 实例idle key分析任务\n     *\n     * @param appId\n     * @param auditId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisServerIdleKeyAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId);\n\n    /**\n     * 实例idle key分析任务\n     *\n     * @param appId\n     * @param auditId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisServerKeyTypeAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId);\n\n    /**\n     * 实例key ttl分析任务\n     *\n     * @param appId\n     * @param auditId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisServerKeyTtlAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId);\n\n    /**\n     * 实例key value size分析任务\n     *\n     * @param appId\n     * @param auditId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisServerKeyValueAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId);\n\n    /**\n     * 实例big key分析任务\n     *\n     * @param appId\n     * @param auditId\n     * @param host\n     * @param port\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisServerBigKeyAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId);\n\n    /**\n     * twemproxy key分析任务\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addAppKeyAnalysisTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * twemproxy flushall任务\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyFlushAllDataTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * twemproxy pika flushall任务\n     *\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyPikaFlushAllDataTask(long appId, long auditId, long parentTaskId);\n\n    /**\n     * @param host\n     * @param port\n     * @param sourceAppId\n     * @param targetAppId\n     * @param parentTaskId\n     * @return\n     */\n    long addRemoveRedisMigrateToolTask(String host, int port, long sourceAppId, long targetAppId, long parentTaskId);\n\n    /**\n     * 添加nut cracker扩容任务\n     *\n     * @param appId\n     * @param nutCrackerMachineList\n     * @param nutCrackerPerMachine\n     * @param parentTaskId\n     * @return\n     */\n    long addNutCrackerScaleOutTask(long appId, List<String> nutCrackerMachineList, int nutCrackerPerMachine,\n                                   long parentTaskId);\n\n    /**\n     * 添加nut cracker批量下线任务\n     *\n     * @param appId\n     * @param nutCrackerNodes\n     * @param parentTaskId\n     * @return\n     */\n    long addNutCrackerListOfflineTask(long appId, List<NutCrackerNode> nutCrackerNodes, long parentTaskId);\n\n    /**\n     * 添加redis sentinel批量下线任务\n     *\n     * @param appId\n     * @param redisSentinelNodes\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisSentinelListOfflineTask(long appId, List<RedisSentinelNode> redisSentinelNodes, long parentTaskId);\n\n    /**\n     * 添加redis slave server批量下线任务\n     *\n     * @param appId\n     * @param redisServerNodes\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisSlaveServerOfflineTask(long appId, List<RedisServerNode> redisServerNodes, long parentTaskId);\n\n    /**\n     * 添加pika slave批量下线任务\n     *\n     * @param appId\n     * @param pikaNodes\n     * @param parentTaskId\n     * @return\n     */\n    long addPikaSlaveOfflineTask(long appId, List<PikaNode> pikaNodes, long parentTaskId);\n\n    /**\n     * 添加redis sentinel添加任务\n     *\n     * @param appId\n     * @param redisSentinelMachineList\n     * @param dbVersion\n     * @param parentTaskId\n     * @return\n     */\n    long addRedisSentinelAddTask(long appId, List<String> redisSentinelMachineList, String dbVersion,\n                                 long parentTaskId);\n\n    /**\n     * 为故障机器添加批量failover\n     *\n     * @param appId\n     * @param host\n     * @param parentTaskId\n     * @return\n     */\n    long addTwemproxyFaultMachineFailoverTask(long appId, String host, long parentTaskId);\n\n    /**\n     * 为应用故障机器做备库重搭\n     *\n     * @param appId\n     * @param host\n     * @param parentTaskId\n     * @return\n     */\n    long addMachineSlaveRebuildTask(long appId, String host, long parentTaskId);\n\n    long addAppTopologyExamTask(boolean auto, int examType, long appId, long parentTaskId);\n\n    long addMachineExamTask(List<String> ipList, Integer useType, long parentTaskId);\n\n    long addOffLineAppTask(long appId, Long auditId, long parentTaskId, AppUser userInfo);\n\n\n    /**\n     * scan key\n     *\n     * @param appId\n     * @param auditId\n     * @param pattern\n     * @param size\n     * @param parentTaskId\n     * @return\n     */\n    long addAppScanKeyTask(long appId, long auditId, String nodes, String pattern, int size, long parentTaskId);\n\n    long addInstanceScanKeyTask(long appId, long auditId, String host, int port, String pattern, int size, long parentTaskId);\n\n    long addInstanceScanCleanKeyTask(long appId, long auditId, String host, int port, Map<String, Object> params, long parentTaskId);\n\n    /**\n     * delete key\n     *\n     * @param appId\n     * @param nodes\n     * @param pattern\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addAppDelKeyTask(long appId, String nodes, String pattern, long auditId, long parentTaskId);\n\n    long addInstanceDelKeyTask(long appId, String host, int port, String pattern, long auditId, long parentTaskId);\n\n    /**\n     * bigkey\n     *\n     * @param appId\n     * @param nodes\n     * @param fromBytes\n     * @param toBytes\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addAppBigKeyTask(long appId, String nodes, long fromBytes, long toBytes, int size, long auditId, long parentTaskId);\n\n    long addInstanceBigKeyTask(long appId, String host, int port, long fromBytes, long toBytes, int size, long auditId, long parentTaskId);\n\n    /**\n     * idle key\n     *\n     * @param appId\n     * @param nodes\n     * @param idleTime\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addAppIdleKeyTask(long appId, String nodes, long idleTime, int size, long auditId, long parentTaskId);\n\n    long addInstanceIdleKeyTask(long appId, String host, int port, long idleTime, int size, long auditId, long parentTaskId);\n\n\n    /**\n     * hot key\n     *\n     * @param appId\n     * @param nodes\n     * @param command\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addAppHotKeyTask(long appId, String nodes, String command, long auditId, long parentTaskId);\n\n    long addInstanceHotKeyTask(long appId, String host, int port, String command, long auditId, long parentTaskId);\n\n\n    /**\n     * slotAnalysis\n     *\n     * @param appId\n     * @param nodes\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addAppSlotAnalysisTask(long appId, String nodes, long auditId, long parentTaskId);\n\n    long addInstanceSlotAnalysisTask(long appId, String host, int port, long auditId, long parentTaskId);\n\n    /**\n     * scan key\n     * @param appId\n     * @param auditId\n     * @param parentTaskId\n     * @return\n     */\n    long addAppScanCleanTask(long appId, Map<String, Object> params, long auditId, long parentTaskId);\n\n    /**\n     * 更新childTaskId\n     *\n     * @param taskStepFlowId\n     * @param masterChildTaskId\n     */\n    void updateTaskStepFlowChildTaskId(long taskStepFlowId, long masterChildTaskId);\n\n    /**\n     * 任务状态\n     *\n     * @param taskStatusEnum\n     */\n    List<TaskQueue> getTaskQueueList(TaskStatusEnum taskStatusEnum);\n\n    /**\n     * 更新任务状态\n     *\n     * @param taskId\n     * @param taskStatusEnum\n     */\n    void updateTaskQueueStatus(long taskId, TaskStatusEnum taskStatusEnum);\n\n    /**\n     * @param taskSearch\n     * @return\n     */\n    int getTaskQueueCount(TaskSearch taskSearch);\n\n    /**\n     * @param taskSearch\n     * @return\n     */\n    List<TaskQueue> getTaskQueueList(TaskSearch taskSearch);\n\n    /**\n     * @param searchTaskId\n     * @return\n     */\n    List<TaskQueue> getTaskQueueTreeByTaskId(long searchTaskId);\n\n    /**\n     * 修改任务参数\n     *\n     * @param taskId\n     * @param param\n     */\n    OperateResult updateParam(long taskId, String param);\n\n    /**\n     * 执行新任务\n     */\n    void executeNewTask();\n\n    /**\n     * @param taskFlowId\n     * @param status\n     * @return\n     */\n    OperateResult updateTaskFlowStatus(long taskFlowId, int status);\n\n    /**\n     * @param appId\n     * @param className\n     * @return\n     */\n    List<TaskQueue> getByAppAndClass(long appId, String className);\n\n    /**\n     * 添加机器同步数据任务task\n     */\n    long addMachineSyncTask(String sourceIp, String targetIp, String containerIp, String important_Info, long parentTaskId);\n\n    long addResourceCompileTask(Integer resourceId, Integer repositoryId, String containerIp, AppUser userInfo);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/IdleTimeDistriEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\nimport com.sohu.cache.util.NumberUtil;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * ilde time时间范围\n *\n * @author fulei\n */\npublic enum IdleTimeDistriEnum {\n    BETWEEN_MIN_TO_0_HOURS(\"-2147483648_0\", \"非法\", 1),\n    BETWEEN_0_TO_1_HOURS(\"0_1\", \"0-1小时\", 2),\n    BETWEEN_1_TO_2_HOURS(\"1_2\", \"1-2小时\", 3),\n    BETWEEN_2_TO_5_HOURS(\"2_5\", \"2-5小时\", 4),\n    BETWEEN_5_TO_10_HOURS(\"5_10\", \"5-10小时\", 5),\n    BETWEEN_10_TO_24_HOURS(\"10_24\", \"10-24小时\", 6),\n    BETWEEN_24_TO_120_HOURS(\"24_120\", \"1-5天\", 7),\n    BETWEEN_120_TO_240_HOURS(\"120_240\", \"5-10天\", 8),\n    BETWEEN_240_TO_720_HOURS(\"240_720\", \"10-30天\", 9),\n    BETWEEN_720_TO_MAX_HOURS(\"720_2147483647\", \"30天以上\", 10);\n\n    public static final Map<String, IdleTimeDistriEnum> MAP;\n\n    static {\n        Map<String, IdleTimeDistriEnum> tmpMap = new HashMap<>();\n        for (IdleTimeDistriEnum enumObject : IdleTimeDistriEnum.values()) {\n            tmpMap.put(enumObject.getValue(), enumObject);\n        }\n        MAP = Collections.unmodifiableMap(tmpMap);\n    }\n\n    private String value;\n    private String info;\n    private int type;\n\n    private IdleTimeDistriEnum(String value, String info, int type) {\n        this.value = value;\n        this.info = info;\n        this.type = type;\n    }\n\n    public static IdleTimeDistriEnum getByValue(String targetValue) {\n        return MAP.get(targetValue);\n    }\n\n    /**\n     * 查看Idle在哪个区间\n     *\n     * @param costTime\n     * @return\n     */\n    public static IdleTimeDistriEnum getRightIdleDistri(long costTime) {\n        IdleTimeDistriEnum[] enumArr = IdleTimeDistriEnum.values();\n        for (IdleTimeDistriEnum enumObject : enumArr) {\n            if (isInSize(enumObject, costTime)) {\n                return enumObject;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 确定length在指定区间\n     *\n     * @param enumObject\n     * @param costTime\n     * @return\n     */\n    private static boolean isInSize(IdleTimeDistriEnum enumObject, long costTime) {\n        String value = enumObject.getValue();\n        int index = value.indexOf(\"_\");\n        int start = NumberUtil.toInt(value.substring(0, index));\n        int end = NumberUtil.toInt(value.substring(index + 1));\n        if (costTime >= start && costTime < end) {\n            return true;\n        }\n        return false;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public int getType() {\n        return type;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/InstanceInfoEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author fulei\n */\npublic class InstanceInfoEnum {\n\n    /**\n     * 实例状态\n     *\n     * @author fulei\n     * @date 2018年6月22日\n     * @time 下午1:53:07\n     */\n    public enum InstanceStatusEnum {\n        NEW_STATUS(-1, \"元数据,没有真实实例\"),\n        ERROR_STATUS(0, \"心跳停止\"),\n        GOOD_STATUS(1, \"运行中\"),\n        OFFLINE_STATUS(2, \"已下线\"),\n        PAUSE_STATUS(3, \"暂停\");\n\n        private int status;\n\n        private String info;\n\n        private static Map<Integer, InstanceStatusEnum> MAP = new HashMap<Integer, InstanceStatusEnum>();\n\n        static {\n            for (InstanceStatusEnum instanceStatusEnum : InstanceStatusEnum.values()) {\n                MAP.put(instanceStatusEnum.getStatus(), instanceStatusEnum);\n            }\n        }\n\n        public static InstanceStatusEnum getByStatus(int status) {\n            return MAP.get(status);\n        }\n\n        private InstanceStatusEnum(int status, String info) {\n            this.status = status;\n            this.info = info;\n        }\n\n        public int getStatus() {\n            return status;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n    }\n\n    /**\n     * redis-port状态\n     */\n    public enum RedisPortStatusEnum {\n        NEW_STATUS(0, \"新节点\"),\n        GOOD_STATUS(1, \"运行中\"),\n        SYNC_FINISH_STATUS(2, \"同步完成\"),\n        PAUSE_STATUS(3, \"暂停\"),\n        OFFLINE(4, \"下线\"),\n        ERROR(5, \"异常\"),\n        ;\n        private int status;\n\n        private String info;\n\n        private static Map<Integer, RedisPortStatusEnum> MAP = new HashMap<Integer, RedisPortStatusEnum>();\n\n        static {\n            for (RedisPortStatusEnum redisPortStatusEnum : RedisPortStatusEnum.values()) {\n                MAP.put(redisPortStatusEnum.getStatus(), redisPortStatusEnum);\n            }\n        }\n\n        public static RedisPortStatusEnum getByStatus(int status) {\n            return MAP.get(status);\n        }\n\n        RedisPortStatusEnum(int status, String info) {\n            this.status = status;\n            this.info = info;\n        }\n\n        public int getStatus() {\n            return status;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n    }\n\n    /**\n     * redis-port状态\n     *\n     * @author fulei\n     * @date 2018年6月22日\n     * @time 下午1:53:07\n     */\n    public enum RedisMigrateToolStatusEnum {\n        NEW_STATUS(0, \"新节点\"),\n        GOOD_STATUS(1, \"运行中\"),\n        SYNC_FINISH_STATUS(2, \"同步完成\"),\n        PAUSE_STATUS(3, \"暂停\"),\n        OFFLINE(4, \"下线\"),\n        ERROR(5, \"异常\"),\n        ;\n        private int status;\n\n        private String info;\n\n        private static Map<Integer, RedisMigrateToolStatusEnum> MAP = new HashMap<Integer, RedisMigrateToolStatusEnum>();\n\n        static {\n            for (RedisMigrateToolStatusEnum redisMigrateToolStatusEnum : RedisMigrateToolStatusEnum.values()) {\n                MAP.put(redisMigrateToolStatusEnum.getStatus(), redisMigrateToolStatusEnum);\n            }\n        }\n\n        public static RedisMigrateToolStatusEnum getByStatus(int status) {\n            return MAP.get(status);\n        }\n\n        RedisMigrateToolStatusEnum(int status, String info) {\n            this.status = status;\n            this.info = info;\n        }\n\n        public int getStatus() {\n            return status;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n    }\n\n    /**\n     * 实例类型\n     *\n     */\n    public enum InstanceTypeEnum {\n        NUTCRACKER(1, \"nutcracker\", \"nutcracker\", \"nutcracker\"),\n        REDIS_CLUSTER(2, \"redis-cluster\", \"redis-cluster\", \"redis\"),\n        CODIS_PROXY(3, \"codis-proxy\", \"codis-proxy\", \"codis\"),\n        PIKA(4, \"pika\", \"pika\", \"pika\"),\n        REDIS_SENTINEL(5, \"sentinel\", \"sentinel\", \"redis\"),\n        REDIS_SERVER(6, \"redis\", \"redis-server\", \"redis\"),\n        MEMCACHE(7, \"memcache\", \"memcache\", \"memcache\"),\n        REDIS_MIGRATE_TOOL(8, \"redis-migrate-tool\", \"redis-migrate-tool\", \"redis-migrate-tool\"),\n        CODIS_SERVER(9, \"codis-server\", \"codis-server\", \"codis\"),\n        CODIS_DASHBOARD(10, \"codis-dashboard\", \"codis-dashboard\", \"codis\"),\n        REDIS_PORT(11, \"redis-port\", \"redis-port\", \"redis-port\"),\n        ;\n\n        private int type;\n\n        /**\n         * 目录用，不要改\n         */\n        private String name;\n\n        private String info;\n\n        private String localDir;\n\n        private static Map<Integer, InstanceTypeEnum> MAP = new HashMap<Integer, InstanceTypeEnum>();\n\n        static {\n            for (InstanceTypeEnum instanceTypeEnum : InstanceTypeEnum.values()) {\n                MAP.put(instanceTypeEnum.getType(), instanceTypeEnum);\n            }\n        }\n\n        public static InstanceTypeEnum getByType(int type) {\n            return MAP.get(type);\n        }\n\n        private InstanceTypeEnum(int type, String name, String info, String localDir) {\n            this.type = type;\n            this.name = name;\n            this.info = info;\n            this.localDir = localDir;\n        }\n\n        public int getType() {\n            return type;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n\n        public String getLocalDir() {\n            return localDir;\n        }\n\n    }\n\n    /**\n     * 是否报警\n     */\n    public static enum InstanceWarnEnum {\n        YES(1),\n        NO(0);\n\n        private int value;\n\n        private InstanceWarnEnum(int value) {\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n    }\n\n    /**\n     * 是否是临时实例\n     */\n    public static enum InstanceTempEumn {\n        YES(1),\n        NO(0);\n\n        private int value;\n\n        private InstanceTempEumn(int value) {\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/InstanceRoleEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author fulei\n * @date 2018年6月27日\n */\npublic enum InstanceRoleEnum {\n\n\tMASTER(1, \"master\"), \n\tSLAVE(2, \"slave\"),\n\tSENTINEL(3,\"sentinel\");\n\n\tprivate static Map<Integer, InstanceRoleEnum> MAP = new HashMap<Integer, InstanceRoleEnum>();\n\n\tstatic {\n\t\tfor (InstanceRoleEnum instanceRoleEnum : InstanceRoleEnum.values()) {\n\t\t\tMAP.put(instanceRoleEnum.getRole(), instanceRoleEnum);\n\t\t}\n\t}\n\n\tpublic static InstanceRoleEnum getInstanceRoleEnum(int role) {\n\t\treturn MAP.get(role);\n\t}\n\n\tprivate int role;\n\n\tprivate String info;\n\n\tprivate InstanceRoleEnum(int role, String info) {\n\t\tthis.role = role;\n\t\tthis.info = info;\n\t}\n\n\tpublic int getRole() {\n\t\treturn role;\n\t}\n\n\tpublic String getInfo() {\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/MachineExamContants.java",
    "content": "package com.sohu.cache.task.constant;\n\n/**\n * Created by rucao on 2019/1/23\n */\npublic class MachineExamContants {\n    public final static double BASE_RATIO=1.0;\n    public final static double defult_memUseThreshold=0.80;\n    public final static double middle_memUseThreshold=0.75;\n    public final static double small_memUseThreshold=0.70;\n    public final static String  MACHINE_IP=\"machineIp\";\n    public final static String MEM=\"mem\";\n    public final static String CPU=\"cpu\";\n    public final static String USED_MEM=\"used_mem\";\n    public final static String APPLY_MEM=\"apply_mem\";\n    public final static String USED_CPU=\"used_cpu\";\n    public final static String USED_MEM_RATIO=\"used_mem_ratio\";\n    public final static String APPLY_MEM_RATIO=\"apply_mem_ratio\";\n    public final static String USED_CPU_RATIO=\"used_cpu_ratio\";\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/MachineSyncEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\n/**\n * Created by chenshi on 2020/5/18.\n */\npublic enum MachineSyncEnum {\n\n    NO_CHANGE(0, \"不执行同步任务\"),\n    SYNC_EXECUTING(4, \"同步任务执行中...\"),\n    SYNC_SUCCESS(1, \"同步任务成功\"),\n    SYNC_ABORT(2, \"同步任务中断\"),\n    SYNC_ERROR(3, \"同步任务异常\");\n\n    private int value;\n\n    private String desc;\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    MachineSyncEnum(int value, String desc) {\n        this.value = value;\n        this.desc = desc;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/PikaNode.java",
    "content": "package com.sohu.cache.task.constant;\n\nimport lombok.Data;\n\n/**\n * @author fulei\n * @date 2018年6月26日\n */\n@Data\npublic class PikaNode {\n\n    private long taskId;\n\n    private String ip;\n\n    private int port;\n\n    private int role;\n\n    private String masterHost;\n\n    private int masterPort;\n\n    private String masterName;\n\n    public PikaNode() {\n    }\n\n    public PikaNode(String ip, int port) {\n        this.ip = ip;\n        this.port = port;\n    }\n\n    public PikaNode(String ip, int port, String masterName) {\n        this.ip = ip;\n        this.port = port;\n        this.masterName = masterName;\n    }\n\n    public PikaNode(String ip, int port, int role, String masterHost, int masterPort) {\n        this.ip = ip;\n        this.port = port;\n        this.role = role;\n        this.masterHost = masterHost;\n        this.masterPort = masterPort;\n    }\n\n    public String genHostAndPort() {\n        return masterHost + \":\" + masterPort;\n    }\n\n    public String getUniqKey() {\n        return ip + \"-\" + port + \"-\" + masterName;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/PushEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\n/**\n * Created by chenshi on 2020/7/7.\n */\npublic enum PushEnum {\n\n    NO(0, \"未推送\"),\n    YES(1, \"已推送\"),\n    NO_WITH_MODIFY(2, \"未推送，有新修改\"),\n    YES_WITH_MODIFY(3, \"已推送，有新修改\"),\n    COMPILEING(4,\"编译中\");\n\n    private int value;\n\n    private String desc;\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    PushEnum(int value, String desc) {\n        this.value = value;\n        this.desc = desc;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/RedisDataStructureTypeEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\npublic enum RedisDataStructureTypeEnum {\n    string(\"string\"),\n    hash(\"hash\"),\n    list(\"list\"),\n    set(\"set\"),\n    zset(\"zset\");\n\n    private String value;\n\n    private RedisDataStructureTypeEnum(String value) {\n        this.value = value;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/ResourceEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\n/**\n * Created by chenshi on 2020/7/6.\n */\npublic enum ResourceEnum {\n\n    ALL(0, \"所有资源\"),\n    Repository(1, \"仓库管理\"),\n    SCRIPT(2, \"脚本管路\"),\n    REDIS(3, \"Redis资源管理\"),\n    SSHKEY(4, \"sshkey管理\"),\n    DOCKERFILE(5, \"镜像管理\"),\n    DIR(6, \"目录管理\"),\n    TOOL(7, \"迁移工具管理\");\n\n    private int value;\n\n    private String desc;\n\n    public int getValue() {\n        return value;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    ResourceEnum(int value, String desc) {\n        this.value = value;\n        this.desc = desc;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/ScanCleanConstants.java",
    "content": "package com.sohu.cache.task.constant;\n\n/**\n * 键匹配相关常量\n *\n * @author zengyizhao\n */\npublic class ScanCleanConstants {\n\n    /**\n     * 操作类型，仅分析，分析清理，分析重置ttl\n     */\n    public final static String OPERATE_TYPE = \"operateType\";\n\n    /**\n     * 指定节点\n     */\n    public final static String POINTED_NODES = \"nodes\";\n\n    /**\n     * 匹配串\n     */\n    public final static String PATTERN = \"pattern\";\n\n    /**\n     * ttl 超时\n     */\n    public final static String TTL_LESS = \"ttlLess\";\n\n    public final static String TTL_MORE = \"ttlMore\";\n\n    /**\n     * 重置ttl超时设置\n     */\n    public final static String TTL_RESET_LESS = \"ttlResetLess\";\n\n    public final static String TTL_RESET_MORE = \"ttlResetMore\";\n\n    /**\n     * 每次scan数量\n     */\n    public final static String PER_COUNT = \"perCount\";\n\n    /**\n     * 单个实例最大处理数量\n     */\n    public final static String MAX_HANDLE_COUNT = \"maxHandleCount\";\n\n    public final static String OPERATE_ANALYSE = \"0\";\n\n    public final static String OPERATE_CLEAN = \"1\";\n\n    public final static String OPERATE_TTL_RESET = \"2\";\n\n    public final static Integer COMPARE_TYPE_LESS_THAN = 0;\n\n    public final static Integer COMPARE_TYPE_MORE_THAN = 1;\n\n    public final static String NODE_TYPE_SLAVE = \"allSlave\";\n\n    public final static String NODE_TYPE_MASTER = \"allMaster\";\n\n    public final static int REDIS_SCAN_CLEAN_TIMEOUT = 7200;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/TaskConstants.java",
    "content": "package com.sohu.cache.task.constant;\n\nimport com.sohu.cache.task.tasks.MachineSyncTask;\nimport com.sohu.cache.task.tasks.RedisClusterAppDeployTask;\nimport com.sohu.cache.task.tasks.RedisSentinelAppDeployTask;\nimport com.sohu.cache.task.tasks.RedisStandaloneAppDeployTask;\nimport com.sohu.cache.task.tasks.daily.MachineExamTask;\nimport com.sohu.cache.task.tasks.daily.TopologyExamTask;\nimport com.sohu.cache.task.tasks.install.RedisSentinelInstallTask;\nimport com.sohu.cache.task.tasks.install.RedisServerInstallTask;\nimport com.sohu.cache.task.tasks.resource.PackCompileTask;\n\n/**\n * 任务相关常量\n *\n * @author fulei\n */\npublic class TaskConstants {\n\n    /**\n     * init方法\n     */\n    public final static String INIT_METHOD_KEY = \"init\";\n\n    /**\n     * 任务id\n     */\n    public final static String TASK_ID_KEY = \"taskId\";\n\n    /**\n     * 任务流id\n     */\n    public final static String TASK_STEP_FLOW_ID = \"taskStepFlowId\";\n\n    /**\n     * 各种类型实例列表\n     */\n    //单个实例\n    public final static String REDIS_SERVER_NODE_KEY = \"redisServerNode\";\n    public final static String REDIS_SERVER_NODES_KEY = \"redisServerNodes\";\n    public final static String REDIS_SENTINEL_NODES_KEY = \"redisSentinelNodes\";\n    public final static String NUT_CRACKER_NODES_KEY = \"nutCrackerNodes\";\n    public final static String REDIS_PORT_NODES_KEY = \"redisPortNodes\";\n    public final static String REDIS_MIGRATE_TOOL_NODES_KEY = \"redisMigrateToolNodes\";\n    public final static String PIKA_NODES_KEY = \"pikaNodes\";\n    public final static String PIKA_NODE_KEY = \"pikaNode\";\n    public final static String MEMCACHE_NODES_KEY = \"memcacheNodes\";\n\n\n    /**\n     * for codis\n     */\n    public final static String CODIS_SERVER_NODES_KEY = \"codisServerNodes\";\n    public final static String CODIS_PROXY_NODES_KEY = \"codisProxyNodes\";\n    public final static String CODIS_DASHBOARD_NODES_KEY = \"codisDashboardNodes\";\n\n    /**\n     * 主节点列表\n     */\n    public final static String MASTER_REDIS_SERVER_NODES = \"masterRedisServerNodes\";\n\n    /**\n     * 实例信息\n     */\n    public final static String APPID_KEY = \"appId\";\n    public final static String AUDIT_ID_KEY = \"auditId\";\n    public final static String HOST_KEY = \"host\";\n    public final static String PORT_KEY = \"port\";\n    public final static String VERSION_KEY = \"db_version\";\n    public final static String IS_CLUSTER_KEY = \"is_cluster\";\n\n    public final static String MASTER_HOST_KEY = \"master_host\";\n    public final static String MASTER_PORT_KEY = \"master_port\";\n    public final static String SLAVE_MACHINE_KEY = \"slave_machine\";\n    public final static String SLAVE_MACHINE_LIST_KEY = \"slave_machine_list\";\n    public final static String MACHINE_IP_LIST_KEY = \"machineIpList\";\n    public final static String USE_TYPE_KEY = \"useType\";\n    public final static String EXAM_TYPE_KEY = \"examType\";\n    public final static String USER_INFO_KEY = \"user_info_key\";\n\n    /**\n     * redis-port相关\n     */\n    public final static String HTTP_PORT_KEY = \"http_port\";\n    public final static String SOURCE_HOST_KEY = \"source_host\";\n    public final static String SOURCE_PORT_KEY = \"source_port\";\n    public final static String SOURCE_PASSWORD_KEY = \"source_password\";\n    public final static String TARGET_HOST_KEY = \"target_host\";\n    public final static String TARGET_PORT_KEY = \"target_port\";\n    public final static String TARGET_PASSWORD_KEY = \"target_password\";\n\n    /**\n     * 机器相关\n     */\n    public final static String CONTAINER_IP = \"container_ip\";\n\n    /**\n     * 资源信息\n     */\n    public final static String RESOURCE_ID = \"resource_id\";\n    public final static String REPOSITORY_ID = \"repository_id\";\n\n    /**\n     * flush config\n     */\n    public final static String APP_IS_NEW_KEY = \"appIsNew\";\n\n    /**\n     * twemproxy\n     */\n    public final static String APP_DEPLOY_INFO_LIST_KEY = \"appDeployInfoList\";\n    public final static String REDIS_SERVER_MACHINE_LIST_KEY = \"redisServerMachineList\";\n    public final static String REDIS_SENTINEL_MACHINE_LIST_KEY = \"redisSentinelMachineList\";\n    public final static String NUT_CRACKER_MACHINE_LIST_KEY = \"nutCrackerMachineList\";\n    public final static String MASTER_PER_MACHINE_KEY = \"masterPerMachine\";\n    public final static String SENTINEL_PER_MACHINE_KEY = \"sentinelPerMachine\";\n    public final static String NUT_CRACKER_PER_MACHINE_KEY = \"nutCrackerPerMachine\";\n\n    /**\n     * pika\n     */\n    public final static String PIKA_MACHINE_LIST_KEY = \"pikaMachineList\";\n\n    /**\n     * for codis\n     */\n    public final static String CODIS_SERVER_MACHINE_LIST_KEY = \"codisServerMachineList\";\n    public final static String CODIS_PRORY_MACHINE_LIST_KEY = \"codisProxyMachineList\";\n    public final static String CODIS_DASHBOARD_MACHINE_LIST_KEY = \"codisDashboardMachineList\";\n    public final static String CODIS_PROXY_PER_MACHINE_KEY = \"codisProxyPerMachine\";\n\n\n    /**\n     * redis sentinel quorum\n     */\n    public final static String REDIS_SENTINEL_QUORUM_KEY = \"quorum\";\n\n    /**\n     * redis server maxmemory\n     */\n    public final static String REDIS_SERVER_MAX_MEMORY_KEY = \"maxMemory\";\n\n    /**\n     * flush zk config taskId\n     */\n    public final static String FLUSH_ZK_CONFIG_TASKID_KEY = \"flushZkConfigTaskId\";\n\n\n    /**\n     * offline slave taskId\n     */\n    public final static String OFFLINE_SLAVE_TASKID_KEY = \"offlineSlaveTaskId\";\n\n    /**\n     * 是否需要刷新zk\n     */\n    public static final String IS_NEED_FLUSH_ZK_CONFIG_KEY = \"isNeedFlushZkConfig\";\n\n\n    /**\n     * stopRmtTaskId\n     */\n    public final static String STOP_RMT_TASKID_KEY = \"stopRmtTaskId\";\n\n    /**\n     * offlineSourceAppTaskId\n     */\n    public final static String OFFLINE_SOURCE_APP_TASKID_KEY = \"offlineSourceAppTaskId\";\n\n    /**\n     * 扩容\n     */\n    public final static String IS_SCALE_OUT_KEY = \"isScaleOut\";//是否是扩容\n    public final static String IS_ONLY_MIGRATE_KEY = \"isOnlyMigrate\";//是否仅是迁移\n    public final static String SOURCE_APP_ID_KEY = \"sourceAppId\";\n    public final static String TARGET_APP_ID_KEY = \"targetAppId\";\n\n    /**\n     * redis server安装超时时间\n     */\n    public final static int REDIS_SERVER_INSTALL_TIMEOUT = 600;\n\n\n    /**\n     * pika安装超时时间\n     */\n    public final static int PIKA_INSTALL_TIMEOUT = 600;\n\n    /**\n     * codis server安装超时时间\n     */\n    public final static int CODIS_SERVER_INSTALL_TIMEOUT = 300;\n\n    /**\n     * redis server下线超时时间\n     */\n    public final static int REDIS_SERVER_OFFLINE_TIMEOUT = 300;\n\n    /**\n     * memcache下线超时时间\n     */\n    public final static int MEMCACHE_OFFLINE_TIMEOUT = 300;\n\n    /**\n     * pika下线超时时间\n     */\n    public final static int PIKA_OFFLINE_TIMEOUT = 300;\n\n    /**\n     * redis sentinel安装超时时间\n     */\n    public final static int REDIS_SENTINEL_INSTALL_TIMEOUT = 600;\n\n    /**\n     * redis sentinel下线超时时间\n     */\n    public final static int REDIS_SENTINEL_OFFLINE_TIMEOUT = 300;\n\n    /**\n     * nutcracker安装超时时间\n     */\n    public final static int NUT_CRACKER_INSTALL_TIMEOUT = 600;\n\n    /**\n     * codis proxy安装超时时间\n     */\n    public final static int CODIS_PROXY_INSTALL_TIMEOUT = 300;\n\n    /**\n     * codis dashboard安装超时时间\n     */\n    public final static int CODIS_DASHBOARD_INSTALL_TIMEOUT = 300;\n\n    /**\n     * nutcracker下线装超时时间\n     */\n    public final static int NUT_CRACKER_OFFLINE_TIMEOUT = 300;\n\n    /**\n     * redis port安装超时时间\n     */\n    public final static int REDIS_PORT_INSTALL_TIMEOUT = 300;\n\n    /**\n     * 刷zk配置超时\n     */\n    public final static int FLUSH_ZK_CONFIG_TIMEOUT = 300;\n\n    /**\n     * 下线slave超时\n     */\n    public final static int OFFLINE_SLAVE_TIMEOUT = 600;\n\n    /**\n     * rmt remove超时\n     */\n    public final static int RMT_REMOVE_TIMEOUT = 300;\n\n    /**\n     * rmt start超时\n     */\n    public final static int RMT_INSTALL_TIMEOUT = 300;\n\n\n    /**\n     * 应用下线超时\n     */\n    public final static int APP_OFFLINE_TIMEOUT = 600;\n\n    /**\n     * rmt同步超时时间\n     */\n    public final static int RMT_SYNC_TIMEOUT = 3600 * 4;\n\n    /**\n     * 等待proxy重启完成时间\n     */\n    public final static int NUTCRACKER_ALL_RESTART_TIMEOUT = 600;\n\n    /**\n     * redis server idle key分析超时时间\n     */\n    public final static int REDIS_SERVER_IDLE_KEY_ANALYSIS_TIMEOUT = 3600;\n\n    /**\n     * redis server key type分析超时时间\n     */\n    public final static int REDIS_SERVER_KEY_TYPE_ANALYSIS_TIMEOUT = 3600;\n\n    /**\n     * redis server key ttl分析超时时间\n     */\n    public final static int REDIS_SERVER_KEY_TTL_ANALYSIS_TIMEOUT = 3600;\n\n    /**\n     * redis server key value size分析超时时间\n     */\n    public final static int REDIS_SERVER_KEY_VALUE_SIZE_ANALYSIS_TIMEOUT = 10800;\n\n    /**\n     * redis server big key分析超时时间\n     */\n    public final static int REDIS_SERVER_BIG_KEY_ANALYSIS_TIMEOUT = 3600;\n\n    /**\n     * single rmt max used memory\n     */\n    public final static int SINGLE_RMT_USED_MEMORY_GB = 400;\n\n\n    /**\n     * max rmt count\n     */\n    public final static int MAX_RMT_COUNT = 3;\n\n\n    /**\n     * redis server big key分析超时时间\n     */\n    public final static int REDIS_SERVER_DIAGNOSTIC_TIMEOUT = 3600;\n\n//\t/**\n//\t * pika实例安装class name\n//\t */\n//\tpublic final static String PIKA_INSTANCE_INSTALL_CLASS = PikaInstanceInstallTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy pika实例安装class name\n//\t */\n//\tpublic final static String TWEMPROXY_PIKA_OFFLINE_CLASS = TwemproxyPikaOfflineTask.class.getSimpleName();\n//\n//\n    /**\n     * redis server实例安装class name\n     */\n    public final static String REDIS_SERVER_INSTANCE_INSTALL_CLASS = RedisServerInstallTask.class.getSimpleName();\n\n    /**\n     * redis sentinel实例安装class name\n     */\n    public final static String REDIS_SENTINEL_INSTANCE_INSTALL_CLASS = RedisSentinelInstallTask.class.getSimpleName();\n\n//\t/**\n//\t * redis port实例安装class name\n//\t */\n//\tpublic final static String REDIS_PORT_INSTANCE_INSTALL_CLASS = RedisPortInstallTask.class.getSimpleName();\n//\n//\t/**\n//\t * redis migrate tool实例安装class name\n//\t */\n//\tpublic final static String REDIS_MIGRATE_TOOL_INSTANCE_INSTALL_CLASS = RedisMigrateToolInstallTask.class.getSimpleName();\n//\n//\t/**\n//\t * nutcracker实例安装class name\n//\t */\n//\tpublic final static String NUT_CRACKER_INSTANCE_INSTALL_CLASS = NutCrackerInstallTask.class.getSimpleName();\n//\n    /**\n     * redis sentinel应用安装class name\n     */\n    public final static String REDIS_SENTINEL_APP_DEPLOY_CLASS = RedisSentinelAppDeployTask.class.getSimpleName();\n\n    /**\n     * redis cluster应用安装class name\n     */\n    public final static String REDIS_CLUSTER_APP_DEPLOY_CLASS = RedisClusterAppDeployTask.class.getSimpleName();\n\n    /**\n     * redis standalone应用安装class name\n     */\n    public final static String REDIS_STANDALONE_APP_DEPLOY_CLASS = RedisStandaloneAppDeployTask.class.getSimpleName();\n\n//\n//\t/**\n//\t * redis sentinel应用安装class name\n//\t */\n//\tpublic final static String PIKA_SENTINEL_APP_INSTALL_CLASS = PikaSentinelInstallTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy应用安装class name\n//\t */\n//\tpublic final static String TWEM_PROXY_APP_INSTALL_CLASS = TwemproxyAppInstallTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy pika应用安装class name\n//\t */\n//\tpublic final static String TWEM_PROXY_PIKA_INSTALL_CLASS = TwemproxyPikaInstallTask.class.getSimpleName();\n//\n//\t/**\n//\t * nutcracker扩容\n//\t */\n//\tpublic final static String NUT_CRACKER_SCALE_OUT_CLASS = NutCrackerScaleOutTask.class.getSimpleName();\n//\n//\t/**\n//\t * nutcracker下线\n//\t */\n//\tpublic final static String NUT_CRACKER_LIST_OFFLINE_CLASS = NutCrackerListOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * sentinel下线\n//\t */\n//\tpublic final static String REDIS_SENTINEL_LIST_OFFLINE_CLASS = AppRedisSentinelOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * redis slave server下线\n//\t */\n//\tpublic final static String REDIS_SLAVE_SERVER_OFFLINE_CLASS = RedisSlaveServerOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * pika slave下线\n//\t */\n//\tpublic final static String PIKA_SLAVE_OFFLINE_CLASS = PikaSlaveOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * 添加redis sentinel\n//\t */\n//\tpublic final static String REDIS_SENTINEL_ADD_CLASS = RedisSentinelAddTask.class.getSimpleName();\n//\n//\t/**\n//\t * 应用刷新配置\n//\t */\n//\tpublic final static String APP_CONFIG_FLUSH_ZK_CLASS = AppConfigFlushZkTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy -> twemproxy\n//\t */\n//\tpublic final static String TWEMPROXY_TO_TWEMPROXY_CLASS = TwemproxyToTwemproxyTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy -> twemproxy(v2)\n//\t */\n//\tpublic final static String TWEMPROXY_TO_TWEMPROXY_V2_CLASS = TwemproxyToTwemproxyTaskV2.class.getSimpleName();\n//\n//\t/**\n//\t * 删除redis migrate tool\n//\t */\n//\tpublic final static String REDIS_MIGRATE_TOOL_REMOVE_CLASS = RedisMigrateToolRemoveTask.class.getSimpleName();\n//\n//\n//\t/**\n//\t * flush redis server data\n//\t */\n//\tpublic final static String REDIS_SERVER_FLUSH_CLASS = RedisServerFlushDataTask.class.getSimpleName();\n//\n//\t/**\n//\t * analysis redis server idle key\n//\t */\n//\tpublic final static String REDIS_SERVER_IDLE_KEY_ANALYSIS_CLASS = RedisServerIdleKeyAnalysisTask.class.getSimpleName();\n//\n//\t/**\n//\t * analysis redis server key type\n//\t */\n//\tpublic final static String REDIS_SERVER_KEY_TYPE_ANALYSIS_CLASS = RedisServerKeyTypeAnalysisTask.class.getSimpleName();\n//\n//\t/**\n//\t * analysis redis server key ttl\n//\t */\n//\tpublic final static String REDIS_SERVER_KEY_TTL_ANALYSIS_CLASS = RedisServerKeyTtlAnalysisTask.class.getSimpleName();\n//\n//\t/**\n//\t * analysis redis server key value size\n//\t */\n//\tpublic final static String REDIS_SERVER_KEY_VALUE_SIZE_ANALYSIS_CLASS = RedisServerKeyValueAnalysisTask.class.getSimpleName();\n//\n//\n//\n//\t/**\n//\t * analysis twemproxy key\n//\t */\n//\tpublic final static String TWEMPROXY_KEY_ANALYSIS_CLASS = TwemproxyKeyAnalysisTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy flushall data\n//\t */\n//\tpublic final static String TWEMPROXY_FLUSHALL_DATA_CLASS = TwemproxyFlushDataTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy pika flushall data\n//\t */\n//\tpublic final static String TWEMPROXY_PIKA_FLUSHALL_DATA_CLASS = TwemproxyPikaFlushDataTask.class.getSimpleName();\n//\n//\t/**\n//\t * stop redis server\n//\t */\n//\tpublic final static String REDIS_SERVER_STOP_CLASS = RedisServerStopTask.class.getSimpleName();\n//\n//\t/**\n//\t * stop memcache\n//\t */\n//\tpublic final static String MEMCACHE_STOP_CLASS = MemcacheStopTask.class.getSimpleName();\n//\n//\t/**\n//\t * offline memcache\n//\t */\n//\tpublic final static String MEMCACHE_CLUSTER_OFFLINE_CLASS = MemcacheClusterOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * stop redis server\n//\t */\n//\tpublic final static String REDIS_SERVER_START_CLASS = RedisServerStartTask.class.getSimpleName();\n//\n//\t/**\n//\t * stop pika\n//\t */\n//\tpublic final static String PIKA_STOP_CLASS = PikaStopTask.class.getSimpleName();\n//\n//\t/**\n//\t * stop redis sentinel\n//\t */\n//\tpublic final static String REDIS_SENTINEL_STOP_CLASS = RedisSentinelStopTask.class.getSimpleName();\n//\n//\t/**\n//\t * stop nutcracker\n//\t */\n//\tpublic final static String NUT_CRACKER_STOP_CLASS = NutCrackerStopTask.class.getSimpleName();\n//\n//\t/**\n//\t * 下线twemproxy\n//\t */\n//\tpublic final static String TWEMPROXY_OFFLINE_CLASS = TwemproxyOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * 下线redis sentinel app\n//\t */\n//\tpublic final static String REDIS_SENTINEL_APP_OFFLINE_CLASS = RedisSentinelAppOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * 下线pika sentinel app\n//\t */\n//\tpublic final static String PIKA_SENTINEL_APP_OFFLINE_CLASS = PikaSentinelAppOfflineTask.class.getSimpleName();\n//\n//\t/**\n//\t * 备库重搭redis\n//\t */\n//\tpublic final static String REDIS_SLAVE_SERVER_REBUILD_CLASS = RedisSlaveServerRebuildTask.class.getSimpleName();\n//\n//\t/**\n//\t * 备库重搭pika\n//\t */\n//\tpublic final static String PIKA_SLAVE_REBUILD_CLASS = PikaSlaveRebuildTask.class.getSimpleName();\n//\n//\t/**\n//\t * sentinel failover\n//\t */\n//\tpublic final static String REDIS_SENTINEL_FAILOVER_CLASS = RedisSentinelFailoverTask.class.getSimpleName();\n//\n//\t/**\n//\t * machine slave rebuild\n//\t */\n//\tpublic final static String MACHINE_SLAVE_REBUILD_CLASS = MachineSlaveRebuildTask.class.getSimpleName();\n//\n//\t/**\n//\t * twemproxy fault machine failover\n//\t */\n//\tpublic final static String TWEMPROXY_FAULT_MACHINE_FAILOVER_CLASS = TwemproxyFaultMachineFailoverTask.class.getSimpleName();\n//\n//\n//\t/**\n//\t * codis server实例安装class name\n//\t */\n//\tpublic final static String CODIS_SERVER_INSTANCE_INSTALL_CLASS = CodisServerInstallTask.class.getSimpleName();\n//\n//\t/**\n//\t * codis proxy实例安装class name\n//\t */\n//\tpublic final static String CODIS_PROXY_INSTANCE_INSTALL_CLASS = CodisProxyInstallTask.class.getSimpleName();\n//\n//\n//\t/**\n//\t * codis dashboard实例安装class name\n//\t */\n//\tpublic final static String CODIS_DASHBOARD_INSTANCE_INSTALL_CLASS = CodisDashboardInstallTask.class.getSimpleName();\n\n    /**\n     * 应用拓扑故障检查class name\n     */\n    public final static String TOPOLOGY_EXAM_CLASS = TopologyExamTask.class.getSimpleName();\n\n    /**\n     * 机器使用情况检查 class name\n     */\n    public final static String MACHINE_EXAM_CLASS = MachineExamTask.class.getSimpleName();\n    /**\n     * 机器同步数据任务 className\n     */\n    public final static String MACHINE_SYNC_CLASS = MachineSyncTask.class.getSimpleName();\n\n    public final static String PACK_COMPILE_TASK = PackCompileTask.class.getSimpleName();\n\n    /**\n     * 最大内存限制(MB)\n     */\n    public final static int MAX_MEMORY_LIMIT = 1024 * 10;\n\n    /**\n     * 一次分配每个机器最大rediserver实例数\n     */\n    public final static int MAX_MASTER_PER_MACHINE = 50;\n\n    /**\n     * 一份分配每个机器最大nutcracker数\n     */\n    public final static int MAX_NUT_CRACK_PER_MACHINE = 50;\n\n    /**\n     * 一份分配每个机器最大codis proxy数\n     */\n    public final static int MAX_CODIS_PROXY_PER_MACHINE = 15;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/TaskQueueEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 任务队列状态枚举\n */\npublic class TaskQueueEnum {\n\n    public enum TaskStatusEnum {\n\n        NEW(0, \"新任务\"),\n        RUNNING(1, \"运行中\"),\n        ABORT(2, \"中断\"),\n        //FAIL(3, \"失败\"),\n        SUCCESS(4, \"成功\"),\n        READY(5, \"准备执行\"),\n        TERMINATE(6, \"终止\");\n\n        private static Map<Integer, TaskStatusEnum> MAP = new HashMap<Integer, TaskStatusEnum>();\n\n        static {\n            for (TaskStatusEnum taskStatusEnum : TaskStatusEnum.values()) {\n                MAP.put(taskStatusEnum.getStatus(), taskStatusEnum);\n            }\n        }\n\n        public static TaskStatusEnum getTaskStatusEnum(int status) {\n            return MAP.get(status);\n        }\n\n        private int status;\n\n        private String info;\n\n        private TaskStatusEnum(int status, String info) {\n            this.status = status;\n            this.info = info;\n        }\n\n        public int getStatus() {\n            return status;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n\n    }\n\n    /**\n     * 错误代码\n     */\n    public enum TaskErrorCodeEnum {\n\n        RIGHT(0, \"正确\");\n\n        private int code;\n\n        private String info;\n\n        TaskErrorCodeEnum(int code, String info) {\n            this.code = code;\n            this.info = info;\n        }\n\n        public int getCode() {\n            return code;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/TaskStepFlowEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\nimport com.google.common.collect.Maps;\n\nimport java.util.Map;\n\n/**\n * 任务步骤流程\n * @author fulei\n */\npublic class TaskStepFlowEnum {\n\n    /**\n     * 任务流状态\n     */\n    public enum TaskFlowStatusEnum {\n\n        READY(0, \"准备\"),\n        RUNNING(1, \"运行中\"),\n        ABORT(2, \"中断\"),\n        //\t\tFAIL(3, \"失败\"),\n        SUCCESS(4, \"成功\"),\n        SKIP(5, \"跳过\");\n\n        private static Map<Integer, TaskFlowStatusEnum> MAP = Maps.newHashMap();\n\n        static {\n            for (TaskFlowStatusEnum taskFlowStatusEnum : TaskFlowStatusEnum.values()) {\n                MAP.put(taskFlowStatusEnum.getStatus(), taskFlowStatusEnum);\n            }\n        }\n\n        public static TaskFlowStatusEnum getTaskFlowStatusEnum(int status) {\n            return MAP.get(status);\n        }\n\n        private int status;\n\n        private String info;\n\n        TaskFlowStatusEnum(int status, String info) {\n            this.status = status;\n            this.info = info;\n        }\n\n        public int getStatus() {\n            return status;\n        }\n\n        public String getInfo() {\n            return info;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/TopoloyExamContants.java",
    "content": "package com.sohu.cache.task.constant;\n\n/**\n * Created by rucao on 2019/1/22\n */\npublic class TopoloyExamContants {\n    public final static String APPID=\"appId\";\n    public final static String TYPE=\"type\";\n    public final static String STATUS=\"status\";\n    public final static String DESC=\"desc\";\n\n    public final static String REDIS_STANDALONE=\"redis-standalone\";\n    public final static String REDIS_CLUSTER=\"redis-cluster\";\n    public final static String REDIS_SENTINEL=\"redis-sentinel\";\n\n    public final static String INSTANCE_FORMAT=\"{0}:{1}:{2} 宿主机:{3}<br/>\";\n    public final static String CLUSTER_INSTANCE_FORMAT=\"{0}:{1}:{2} 宿主机:{3}<br/>\";\n    public final static String NETSEGMENT_FORMAT=\"存在至少两个网段，网段1：{0}，网段2：{1}\";\n\n    public final static String MASTER_SLAVE_DESC=\"主从节点分布同一台物理机\";\n    public final static String SLAVE_NOT_EXIST=\"主节点没有从节点\";\n    public final static String NODESNUM_DESC=\"集群节点分布在少于3台物理机\";\n    public final static String CLUSTER_FAILOVER_DESC=\"集群中一台物理机宕机不满足故障转移条件\";\n    public final static String NETSEGMENT_DESC=\"集群节点不在同网段\";\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/TtlTimeDistriEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\n\nimport com.sohu.cache.util.NumberUtil;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * ilde time时间范围\n *\n * @author fulei\n */\npublic enum TtlTimeDistriEnum {\n    BETWEEN_PERSIST_HOURS(\"-1_-1\", \"不过期\", 1),\n    BETWEEN_0_TO_1_HOURS(\"0_1\", \"0-1小时\", 2),\n    BETWEEN_1_TO_2_HOURS(\"1_2\", \"1-2小时\", 3),\n    BETWEEN_2_TO_5_HOURS(\"2_5\", \"2-5小时\", 4),\n    BETWEEN_5_TO_10_HOURS(\"5_10\", \"5-10小时\", 5),\n    BETWEEN_10_TO_24_HOURS(\"10_24\", \"10-24小时\", 6),\n    BETWEEN_24_TO_120_HOURS(\"24_120\", \"1-5天\", 7),\n    BETWEEN_120_TO_240_HOURS(\"120_240\", \"5-10天\", 8),\n    BETWEEN_240_TO_720_HOURS(\"240_720\", \"10-30天\", 9),\n    BETWEEN_720_TO_MAX_HOURS(\"720_2147483647\", \"30天以上\", 10),\n    ;\n\n    public final static Map<String, TtlTimeDistriEnum> MAP;\n\n    static {\n        Map<String, TtlTimeDistriEnum> tmpMap = new HashMap<>();\n        for (TtlTimeDistriEnum enumObject : TtlTimeDistriEnum.values()) {\n            tmpMap.put(enumObject.getValue(), enumObject);\n        }\n        MAP = Collections.unmodifiableMap(tmpMap);\n    }\n\n    private String value;\n    private String info;\n    private int type;\n\n    private TtlTimeDistriEnum(String value, String info, int type) {\n        this.value = value;\n        this.info = info;\n        this.type = type;\n    }\n\n    public static TtlTimeDistriEnum getByValue(String targetValue) {\n        return MAP.get(targetValue);\n    }\n\n    /**\n     * 计算ttl所属于的区间\n     *\n     * @return\n     */\n    public static TtlTimeDistriEnum getRightTtlDistri(long ttl) {\n        TtlTimeDistriEnum[] enumArr = TtlTimeDistriEnum.values();\n        for (TtlTimeDistriEnum enumObject : enumArr) {\n            if (isInSize(enumObject, ttl)) {\n                return enumObject;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * @param enumObject\n     * @param costTime\n     * @return\n     */\n    private static boolean isInSize(TtlTimeDistriEnum enumObject, long costTime) {\n        String value = enumObject.getValue();\n        int index = value.indexOf(\"_\");\n        int start = NumberUtil.toInt(value.substring(0, index));\n        int end = NumberUtil.toInt(value.substring(index + 1));\n        if (costTime >= start && costTime < end) {\n            return true;\n        }\n        return false;\n    }\n\n    public static void main(String[] args) {\n        System.out.println(getRightTtlDistri(-2));\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public int getType() {\n        return type;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/constant/ValueSizeDistriEnum.java",
    "content": "package com.sohu.cache.task.constant;\n\n\nimport com.sohu.cache.util.NumberUtil;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 值分布memory usage 结果\n *\n * @author fulei\n */\npublic enum ValueSizeDistriEnum {\n    // 单位字节\n    BETWEEN_MIN_TO_0_BYTE(\"-2147483648_0\", \"非法\", 0),\n    BETWEEN_0_TO_50_BYTE(\"0_50\", \"0-0.05k\", 1),\n    BETWEEN_50_TO_100_BYTE(\"50_100\", \"0.05k-0.1k\", 2),\n    BETWEEN_100_TO_200_BYTE(\"100_200\", \"0.1k-0.2k\", 3),\n    BETWEEN_200_TO_500_BYTE(\"200_500\", \"0.2k-0.5k\", 4),\n    BETWEEN_500_TO_1024_BYTE(\"500_1024\", \"0.5k-1k\", 5),\n    BETWEEN_1024_TO_2048_BYTE(\"1024_2048\", \"1-2k\", 6),\n    BETWEEN_2048_TO_5120_BYTE(\"2048_5120\", \"2-5k\", 7),\n    BETWEEN_5120_TO_10240_BYTE(\"5120_10240\", \"5-10k\", 8),\n    BETWEEN_10240_TO_20480_BYTE(\"10240_20480\", \"10-20k\", 9),\n    BETWEEN_20480_TO_51200_BYTE(\"20480_51200\", \"20-50k\", 10),\n    BETWEEN_51200_TO_102400_BYTE(\"51200_102400\", \"50-100k\", 11),\n    BETWEEN_102400_TO_204800_BYTE(\"102400_204800\", \"100-200k\", 12),\n    BETWEEN_204800_TO_512000_BYTE(\"204800_512000\", \"200-500k\", 13),\n    BETWEEN_512000_TO_1024000_BYTE(\"512000_1024000\", \"500k-1MB\", 14),\n    BETWEEN_1024000_TO_2048000_BYTE(\"1024000_2048000\", \"1MB-2MB\", 15),\n    BETWEEN_2048000_TO_5120000_BYTE(\"2048000_5120000\", \"2MB-5MB\", 16),\n    BETWEEN_5120000_TO_MAX_BYTE(\"5120000_2147483647\", \"5MB以上\", 17),\n    ;\n\n    public final static Map<String, ValueSizeDistriEnum> VALUE_MAP;\n    public final static Map<Integer, ValueSizeDistriEnum> TYPE_MAP;\n\n    static {\n        Map<String, ValueSizeDistriEnum> tmpMap = new HashMap<>();\n        for (ValueSizeDistriEnum enumObject : ValueSizeDistriEnum.values()) {\n            tmpMap.put(enumObject.getValue(), enumObject);\n        }\n        VALUE_MAP = Collections.unmodifiableMap(tmpMap);\n    }\n\n    static {\n        Map<Integer, ValueSizeDistriEnum> tmpMap = new HashMap<>();\n        for (ValueSizeDistriEnum enumObject : ValueSizeDistriEnum.values()) {\n            tmpMap.put(enumObject.getType(), enumObject);\n        }\n        TYPE_MAP = Collections.unmodifiableMap(tmpMap);\n    }\n\n    private String value;\n    private String info;\n    private int type;\n\n    private ValueSizeDistriEnum(String value, String info, int type) {\n        this.value = value;\n        this.info = info;\n        this.type = type;\n    }\n\n    public static ValueSizeDistriEnum getByValue(String targetValue) {\n        return VALUE_MAP.get(targetValue);\n    }\n\n    public static ValueSizeDistriEnum getByType(int targetType) {\n        return TYPE_MAP.get(targetType);\n    }\n\n    /**\n     * 查看区间\n     */\n    public static ValueSizeDistriEnum getRightSizeBetween(long size) {\n        ValueSizeDistriEnum[] enumArr = ValueSizeDistriEnum.values();\n        for (ValueSizeDistriEnum enumObject : enumArr) {\n            if (isInSize(enumObject, size)) {\n                return enumObject;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 确定length在指定区间\n     *\n     * @param enumObject\n     * @param size\n     * @return\n     */\n    private static boolean isInSize(ValueSizeDistriEnum enumObject, long size) {\n        String value = enumObject.getValue();\n        int index = value.indexOf(\"_\");\n        long start = NumberUtil.toLong(value.substring(0, index));\n        long end = NumberUtil.toLong(value.substring(index + 1));\n        if (size >= start && size < end) {\n            return true;\n        }\n        return false;\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/InstanceBigKey.java",
    "content": "package com.sohu.cache.task.entity;\n\nimport com.sohu.cache.task.constant.RedisDataStructureTypeEnum;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\nimport java.text.DecimalFormat;\nimport java.util.Date;\n\n@Data\n@EqualsAndHashCode\npublic class InstanceBigKey {\n\n    private long id;\n\n    /**\n     * 实例id\n     */\n    private long instanceId;\n\n    /**\n     * app id\n     */\n    private long appId;\n\n    /**\n     * 工单id\n     */\n    private long auditId;\n\n    /**\n     * ip地址\n     */\n    private String ip;\n\n    /**\n     * port\n     */\n    private int port;\n\n    /**\n     * 1主2从\n     */\n    private int role;\n\n    /**\n     * bigkey\n     */\n    private String bigKey;\n\n    /**\n     * 类型\n     */\n    private String type;\n\n    /**\n     * 长度\n     */\n    private long length;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n\n    public String getLengthFormat() {\n        if (RedisDataStructureTypeEnum.string.getValue().equals(type)) {\n            return new DecimalFormat(\"#,###\").format(length);\n        } else {\n            return length + \"个元素\";\n        }\n    }\n\n    /*public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public long getInstanceId() {\n        return instanceId;\n    }\n\n    public void setInstanceId(long instanceId) {\n        this.instanceId = instanceId;\n    }\n\n    public long getAppId() {\n        return appId;\n    }\n\n    public void setAppId(long appId) {\n        this.appId = appId;\n    }\n\n    public long getAuditId() {\n        return auditId;\n    }\n\n    public void setAuditId(long auditId) {\n        this.auditId = auditId;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public int getRole() {\n        return role;\n    }\n\n    public void setRole(int role) {\n        this.role = role;\n    }\n\n    public String getBigKey() {\n        return bigKey;\n    }\n\n    public void setBigKey(String bigKey) {\n        this.bigKey = bigKey;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public long getLength() {\n        return length;\n    }\n\n    public void setLength(long length) {\n        this.length = length;\n    }*/\n\n    public Date getCreateTime() {\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/NutCrackerNode.java",
    "content": "package com.sohu.cache.task.entity;\n\nimport lombok.Data;\n\n/**\n * @author fulei\n */\n@Data\npublic class NutCrackerNode {\n\n    private long taskId;\n\n    private String ip;\n\n    private int port;\n\n    public NutCrackerNode() {\n    }\n\n    public NutCrackerNode(String ip, int port) {\n        this.ip = ip;\n        this.port = port;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/RedisSentinelNode.java",
    "content": "package com.sohu.cache.task.entity;\n\nimport lombok.Data;\n\n/**\n * @author fulei\n * @date 2018年6月26日\n */\n@Data\npublic class RedisSentinelNode {\n\n    private long taskId;\n\n    private String ip;\n\n    private int port;\n\n    public RedisSentinelNode() {\n    }\n\n    public RedisSentinelNode(String ip, int port) {\n        this.ip = ip;\n        this.port = port;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/RedisServerNode.java",
    "content": "package com.sohu.cache.task.entity;\n\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.task.constant.PikaNode;\nimport lombok.Data;\n\n/**\n * @author fulei\n * @date 2018年6月26日\n * @time 下午5:52:49\n */\n@Data\npublic class RedisServerNode {\n\n    private long taskId;\n\n    private String ip;\n\n    private int port;\n\n    private int role;\n\n    private int maxmemory;\n\n    private String masterHost;\n\n    private int masterPort;\n\n    private String masterName;\n\n    public RedisServerNode() {\n    }\n\n    public RedisServerNode(String ip, int port) {\n        this.ip = ip;\n        this.port = port;\n    }\n\n    public RedisServerNode(String ip, int port, String masterName) {\n        this.ip = ip;\n        this.port = port;\n        this.masterName = masterName;\n    }\n\n    public RedisServerNode(String ip, int port, int maxmemory) {\n        this.ip = ip;\n        this.port = port;\n        this.maxmemory = maxmemory;\n    }\n\n    public RedisServerNode(String ip, int port, int role, int maxmemory, String masterHost, int masterPort) {\n        this.ip = ip;\n        this.port = port;\n        this.role = role;\n        this.maxmemory = maxmemory;\n        this.masterHost = masterHost;\n        this.masterPort = masterPort;\n    }\n\n    public String genHostAndPort() {\n        return masterHost + \":\" + masterPort;\n    }\n\n    public boolean isMaster() {\n        return role == InstanceRoleEnum.MASTER.getRole();\n    }\n\n    public boolean isSlave() {\n        return role == InstanceRoleEnum.SLAVE.getRole();\n    }\n\n\n    @Override\n    public String toString() {\n        return \"RedisServerNode [taskId=\" + taskId + \", ip=\" + ip + \", port=\" + port + \", role=\" + role + \", maxmemory=\"\n                + maxmemory + \", masterHost=\" + masterHost + \", masterPort=\" + masterPort + \", masterName=\" + masterName\n                + \"]\";\n    }\n\n    public String getUniqKey() {\n        return ip + \"-\" + port + \"-\" + masterName;\n    }\n\n    public static RedisServerNode transferFromPika(PikaNode pikaNode) {\n        RedisServerNode redisServerNode = new RedisServerNode();\n        redisServerNode.setIp(pikaNode.getIp());\n        redisServerNode.setMasterHost(pikaNode.getMasterHost());\n        redisServerNode.setMasterName(pikaNode.getMasterName());\n        redisServerNode.setMasterPort(pikaNode.getMasterPort());\n        redisServerNode.setPort(pikaNode.getPort());\n        redisServerNode.setRole(pikaNode.getRole());\n        redisServerNode.setTaskId(pikaNode.getTaskId());\n        return redisServerNode;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/TaskQueue.java",
    "content": "package com.sohu.cache.task.entity;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.sohu.cache.task.constant.TaskQueueEnum.TaskStatusEnum;\nimport lombok.Data;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\n\nimport java.text.DecimalFormat;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 任务队列\n */\n@Data\npublic class TaskQueue {\n\n    /**\n     * 自增id\n     */\n    private long id;\n\n    /**\n     * 进行任务的ip:port\n     */\n    private String executeIpPort;\n\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 类名\n     */\n    private String className;\n\n    /**\n     * 全局参数:json格式，内容会变\n     */\n    private String param;\n\n    /**\n     * 初始化参数:json格式，内容不变\n     */\n    private String initParam;\n\n    /**\n     * 状态，详见com.sohu.cache.task.constant.TaskQueueEnum.TaskStatusEnum\n     */\n    private int status;\n\n    /**\n     * 父任务id\n     */\n    private long parentTaskId;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    private Date updateTime;\n\n    /**\n     * 开始时间\n     */\n    private Date startTime;\n\n    /**\n     * 结束时间\n     */\n    private Date endTime;\n\n    /**\n     * 优先级\n     */\n    private int priority = 50;\n\n    /**\n     * 错误代码，详见 TaskQueueEnum.TaskErrorCodeEnum\n     */\n    private int errorCode;\n\n    /**\n     * 错误消息\n     */\n    private String errorMsg = \"\";\n\n    /**\n     * 备注\n     */\n    private String taskNote = \"\";\n\n    /**\n     * 重要信息\n     */\n    private String importantInfo = \"\";\n\n    /**\n     * 任务流\n     */\n    private List<TaskStepFlow> taskStepFlowList;\n\n    public Map<String, Object> getParamMap() {\n        if (StringUtils.isEmpty(param)) {\n            return Collections.emptyMap();\n        }\n        return JSONObject.parseObject(param);\n    }\n\n    public String getPrettyParam() {\n        if (StringUtils.isEmpty(param)) {\n            return null;\n        }\n        JSONObject jsonObject = JSONObject.parseObject(param);\n        return JSONObject.toJSONString(jsonObject, SerializerFeature.PrettyFormat);\n    }\n\n    public String getStatusDesc() {\n        TaskStatusEnum taskStatusEnum = TaskStatusEnum.getTaskStatusEnum(status);\n        if (taskStatusEnum != null) {\n            return taskStatusEnum.getInfo();\n        }\n        return \"\";\n    }\n\n    public String getProgress() {\n        if (CollectionUtils.isEmpty(taskStepFlowList)) {\n            return \"\";\n        }\n        int success = 0;\n        int total = taskStepFlowList.size();\n        for (TaskStepFlow taskStepFlow : taskStepFlowList) {\n            if (taskStepFlow.isSkip() || taskStepFlow.isSuccess()) {\n                success++;\n            }\n        }\n        double percent = success * 100.0 / total * 1.0;\n        return new DecimalFormat(\"#.00\").format(percent) + \"%\";\n    }\n\n    public String getCostSeconds() {\n        if (status != TaskStatusEnum.SUCCESS.getStatus()) {\n            return \"\";\n        }\n        if (endTime != null && startTime != null) {\n            long ms = (endTime.getTime() - startTime.getTime()) / 1000;\n            return String.valueOf(ms);\n        }\n        return \"\";\n    }\n\n    public boolean isSuccess() {\n        return TaskStatusEnum.SUCCESS.getStatus() == status;\n    }\n\n    public boolean isRunning() {\n        return TaskStatusEnum.RUNNING.getStatus() == status;\n    }\n\n    public boolean isAbort() {\n        return TaskStatusEnum.ABORT.getStatus() == status;\n    }\n\n    public Date getCreateTime() {\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n\n    public Date getUpdateTime() {\n        return (Date) updateTime.clone();\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = (Date) updateTime.clone();\n    }\n\n    public Date getStartTime() {\n        return (Date) startTime.clone();\n    }\n\n    public void setStartTime(Date startTime) {\n        this.startTime = (Date) startTime.clone();\n    }\n\n    public Date getEndTime() {\n        return (Date) endTime.clone();\n    }\n\n    public void setEndTime(Date endTime) {\n        this.endTime = (Date) endTime.clone();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/TaskSearch.java",
    "content": "package com.sohu.cache.task.entity;\r\n\r\nimport com.sohu.cache.web.util.Page;\r\nimport lombok.Data;\r\n\r\n/**\r\n * @author fulei\r\n * @date 2018年7月11日\r\n */\r\n@Data\r\npublic class TaskSearch {\r\n\r\n    /**\r\n     * appId\r\n     */\r\n    private Long appId;\r\n\r\n    /**\r\n     * 类名\r\n     */\r\n    private String className;\r\n\r\n    /**\r\n     * 状态\r\n     */\r\n    private int status = -1;\r\n\r\n    /**\r\n     * 分页\r\n     */\r\n    private Page page;\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/TaskStepFlow.java",
    "content": "package com.sohu.cache.task.entity;\n\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * 任务步骤流\n */\n@Data\npublic class TaskStepFlow {\n\n    /**\n     * 自增id\n     */\n    private long id;\n\n    /**\n     * 任务id\n     */\n    private long taskId;\n\n    /**\n     * 子任务id\n     */\n    private long childTaskId;\n\n    /**\n     * 类名\n     */\n    private String className;\n\n    /**\n     * 步骤名\n     */\n    private String stepName;\n\n    /**\n     * 序号\n     */\n    private int orderNo;\n\n    /**\n     * 状态, 参考：TaskFlowStatusEnum\n     */\n    private int status;\n\n    /**\n     * 日志\n     */\n    private String log = \"\";\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    private Date updateTime;\n\n    /**\n     * 开始时间\n     */\n    private Date startTime;\n\n    /**\n     * 结束时间\n     */\n    private Date endTime;\n\n    /**\n     * 执行ip:port\n     */\n    private String executeIpPort;\n\n    /**\n     * 任务流描述\n     */\n    private TaskStepMeta taskStepMeta;\n\n    public boolean isSuccess() {\n        return status == TaskFlowStatusEnum.SUCCESS.getStatus();\n    }\n\n    public boolean isSkip() {\n        return status == TaskFlowStatusEnum.SKIP.getStatus();\n    }\n\n    public String getExecuteIpPort() {\n        return executeIpPort;\n    }\n\n    public void setExecuteIpPort(String executeIpPort) {\n        this.executeIpPort = executeIpPort;\n    }\n\n    public TaskStepMeta getTaskStepMeta() {\n        return taskStepMeta;\n    }\n\n    public void setTaskStepMeta(TaskStepMeta taskStepMeta) {\n        this.taskStepMeta = taskStepMeta;\n    }\n\n    public String getStatusDesc() {\n        TaskFlowStatusEnum taskFlowStatusEnum = TaskFlowStatusEnum.getTaskFlowStatusEnum(status);\n        if (taskFlowStatusEnum != null) {\n            return taskFlowStatusEnum.getInfo();\n        }\n        return \"\";\n    }\n\n    public String getCostSeconds() {\n        if (status != TaskFlowStatusEnum.SUCCESS.getStatus()) {\n            return \"\";\n        }\n        if (endTime != null && startTime != null) {\n            long ms = (endTime.getTime() - startTime.getTime()) / 1000;\n            return String.valueOf(ms);\n        }\n        return \"\";\n    }\n\n    public Date getCreateTime() {\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n\n    public Date getUpdateTime() {\n        return (Date) updateTime.clone();\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = (Date) updateTime.clone();\n    }\n\n    public Date getStartTime() {\n        return (Date) startTime.clone();\n    }\n\n    public void setStartTime(Date startTime) {\n        this.startTime = (Date) startTime.clone();\n    }\n\n    public Date getEndTime() {\n        return (Date) endTime.clone();\n    }\n\n    public void setEndTime(Date endTime) {\n        this.endTime = (Date) endTime.clone();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/entity/TaskStepMeta.java",
    "content": "package com.sohu.cache.task.entity;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * 任务步骤元数据\n */\n@Data\npublic class TaskStepMeta {\n\n    /**\n     * 自增id\n     */\n    private long id;\n\n    /**\n     * 类名\n     */\n    private String className;\n\n    /**\n     * 步骤名\n     */\n    private String stepName;\n\n    /**\n     * 步骤描述\n     */\n    private String stepDesc;\n\n    /**\n     * 运维建议\n     */\n    private String opsDevice;\n\n    /**\n     * 超时时间\n     */\n    private int timeout;\n\n    /**\n     * 顺序\n     */\n    private int orderNo;\n\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    /**\n     * 更新时间\n     */\n    private Date updateTime;\n\n    public Date getCreateTime() {\n        if(createTime == null){\n            return createTime;\n        }\n        return (Date) createTime.clone();\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = (Date) createTime.clone();\n    }\n\n    public Date getUpdateTime() {\n        if(updateTime == null){\n            return updateTime;\n        }\n        return (Date) updateTime.clone();\n    }\n\n    public void setUpdateTime(Date updateTime) {\n        this.updateTime = (Date) updateTime.clone();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/impl/TaskServiceImpl.java",
    "content": "package com.sohu.cache.task.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.constant.OperateResult;\nimport com.sohu.cache.dao.AppAuditDao;\nimport com.sohu.cache.dao.TaskQueueDao;\nimport com.sohu.cache.dao.TaskStepFlowDao;\nimport com.sohu.cache.dao.TaskStepMetaDao;\nimport com.sohu.cache.entity.AppAudit;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.task.constant.PikaNode;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskQueueEnum.TaskErrorCodeEnum;\nimport com.sohu.cache.task.constant.TaskQueueEnum.TaskStatusEnum;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.TaskQueue;\nimport com.sohu.cache.task.entity.*;\nimport com.sohu.cache.task.tasks.AppKeyAnalysisTask;\nimport com.sohu.cache.task.tasks.OffLineAppTask;\nimport com.sohu.cache.task.tasks.analysis.*;\nimport com.sohu.cache.task.tasks.daily.MachineExamTask;\nimport com.sohu.cache.task.tasks.daily.TopologyExamTask;\nimport com.sohu.cache.task.tasks.diagnosticTask.*;\nimport com.sohu.cache.task.util.AppWechatUtil;\nimport com.sohu.cache.task.util.SpringContextUtil;\nimport com.sohu.cache.util.RedisConstUtils;\nimport com.sohu.cache.web.enums.ExamToolEnum;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.util.IpUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.apache.commons.lang.math.NumberUtils;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport javax.annotation.PostConstruct;\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * @author fulei\n */\n@Service\npublic class TaskServiceImpl implements TaskService {\n\n    private Logger logger = LoggerFactory.getLogger(TaskServiceImpl.class);\n\n    @Autowired\n    private TaskQueueDao taskQueueDao;\n\n    @Autowired\n    private TaskStepFlowDao taskStepFlowDao;\n\n    @Autowired\n    private TaskStepMetaDao taskStepMetaDao;\n\n    @Autowired\n    private AppAuditDao appAuditDao;\n\n    @Autowired\n    private SpringContextUtil springContextUtil;\n\n    @Autowired\n    private AsyncService asyncService;\n\n    @Autowired\n    private AppService appService;\n\n    @Autowired\n    private AppWechatUtil appWechatUtil;\n\n    @Autowired\n    IpUtil ipUtil;\n\n    private int port;\n\n    private String host;\n\n    @PostConstruct\n    public void init() {\n        asyncService.assemblePool(getThreadPoolKey(), AsyncThreadPoolFactory.TASK_EXECUTE_THREAD_POOL);\n        host = ipUtil.getLocalIP();\n        port = ipUtil.getLocalPort();\n    }\n\n    @Override\n    public void executeNewTask() {\n        try {\n            List<TaskQueue> newTaskQueueList = getTaskQueueList(TaskStatusEnum.NEW);\n            int limit = 20;\n            if (CollectionUtils.isEmpty(newTaskQueueList)) {\n                logger.warn(\"current newTaskQueueList is empty\");\n                return;\n            } else {\n                logger.warn(\"current newTaskQueueList size is {}\", newTaskQueueList.size());\n            }\n\n            for (int i = 0; i < limit && i < newTaskQueueList.size(); i++) {\n                final TaskQueue taskQueue = newTaskQueueList.get(i);\n                updateTaskQueueStatus(taskQueue.getId(), TaskStatusEnum.READY);\n                logger.warn(\"task {} ready to execute\", taskQueue.getId());\n                String key = getThreadPoolKey() + \"_\" + taskQueue.getId();\n                asyncService.submitFuture(getThreadPoolKey(), new KeyCallable<Boolean>(key) {\n                    @Override\n                    public Boolean execute() {\n                        logger.warn(\"===================={} start task {}==================\",\n                                Thread.currentThread().getName(), taskQueue.getId());\n                        executeTask(taskQueue.getId());\n                        return true;\n                    }\n                });\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public TaskStatusEnum executeTask(long taskId) {\n        TaskStatusEnum taskStatusEnum = null;\n        TaskQueue taskQueue = taskQueueDao.getById(taskId);\n        if (taskQueue == null) {\n            logger.error(\"taskId {} is null\", taskId);\n            taskStatusEnum = TaskStatusEnum.ABORT;\n            taskQueueDao.updateStatus(taskId, taskStatusEnum.getStatus());\n            return taskStatusEnum;\n        }\n        if (taskQueue.isSuccess()) {\n            logger.error(\"taskId {} already success\", taskId);\n            return TaskStatusEnum.SUCCESS;\n        }\n        //todo\n//        if (taskQueue.isRunning()) {\n//            logger.error(\"taskId {} is running\", taskId);\n//            return TaskStatusEnum.RUNNING;\n//        }\n        //正在执行\n        taskStatusEnum = TaskStatusEnum.RUNNING;\n        taskQueueDao.updateStatus(taskId, taskStatusEnum.getStatus());\n        taskQueueDao.updateStartTime(taskId, new Date());\n\n        String executeIpPort = host + \":\" + port;\n        taskQueueDao.updateExecuteIpPort(taskId, executeIpPort);\n\n        //实际是beanId\n        String className = taskQueue.getClassName();\n        //parse参数\n        Map<String, Object> paramMap = taskQueue.getParamMap();\n\n        try {\n            //任务列表\n            List<TaskStepFlow> taskStepFlowList = new ArrayList<TaskStepFlow>();\n\n            //获取任务\n            BaseTask task = (BaseTask) springContextUtil.getBeanById(className);\n            paramMap.put(TaskConstants.TASK_ID_KEY, taskQueue.getId());\n            task.setParamMap(paramMap);\n\n            //任务步骤\n            List<String> stepNameList = task.getTaskSteps();\n\n            //这块要考虑好重试的问题 @TODO\n            for (int i = 0; i < stepNameList.size(); i++) {\n                String stepName = stepNameList.get(i);\n                TaskStepFlow oldTaskStepFlow = taskStepFlowDao.getByTaskClassStep(taskId, className, stepName);\n                if (oldTaskStepFlow != null) {\n                    taskStepFlowList.add(oldTaskStepFlow);\n                } else {\n                    TaskStepFlow taskStepFlow = new TaskStepFlow();\n                    taskStepFlow.setClassName(className);\n                    taskStepFlow.setStepName(stepName);\n                    taskStepFlow.setOrderNo(i + 1);\n                    taskStepFlow.setTaskId(taskId);\n                    taskStepFlow.setStatus(TaskFlowStatusEnum.READY.getStatus());\n                    Date date = new Date();\n                    taskStepFlow.setStartTime(date);\n                    taskStepFlow.setEndTime(date);\n                    taskStepFlow.setCreateTime(date);\n                    taskStepFlow.setUpdateTime(date);\n                    taskStepFlowList.add(taskStepFlow);\n\n                    taskStepFlowDao.save(taskStepFlow);\n                }\n            }\n            // 执行任务\n            TaskFlowStatusEnum taskFlowStatusEnum = null;\n            for (TaskStepFlow taskStepFlow : taskStepFlowList) {\n                long id = taskStepFlow.getId();\n                if ((taskStepFlow.isSuccess() || taskStepFlow.isSkip()) && !TaskConstants.INIT_METHOD_KEY\n                        .equals(taskStepFlow.getStepName())) {\n                    continue;\n                }\n                task.getParamMap().put(TaskConstants.TASK_STEP_FLOW_ID, id);\n                try {\n                    // 更新开始时间\n                    taskStepFlowDao.updateStartTime(id, new Date());\n                    taskStepFlowDao.updateStatus(id, TaskFlowStatusEnum.RUNNING.getStatus());\n                    taskStepFlowDao.updateExecuteIpPort(id, host + \":\" + port);\n                    // 日志\n                    MDC.put(TaskConstants.TASK_STEP_FLOW_ID, String.valueOf(id));\n\n                    // 执行日志\n                    logger.warn(BaseTask.marker, \"task {} {} start\", taskId, taskStepFlow.getStepName());\n                    // 执行任务\n                    Method method = task.getClass().getDeclaredMethod(taskStepFlow.getStepName());\n                    taskFlowStatusEnum = (TaskFlowStatusEnum) method.invoke(task);\n                    // 执行日志\n                    logger.warn(BaseTask.marker, \"task {} {} finish, result is {}\", taskId, taskStepFlow.getStepName(),\n                            taskFlowStatusEnum.getInfo());\n                    taskStepFlowDao.updateStatus(id, taskFlowStatusEnum.getStatus());\n                    if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                        appWechatUtil.noticeTaskAbort(taskId, taskStepFlow.getStepName());\n                        break;\n                    }\n                    taskStepFlowDao.updateEndTime(id, new Date());\n                    // 更新参数\n                    String paramJson = JSONObject.toJSONString(task.getParamMap());\n                    taskQueueDao.updateParam(taskQueue.getId(), paramJson);\n                } catch (Exception e) {\n                    taskFlowStatusEnum = TaskFlowStatusEnum.ABORT;\n                    appWechatUtil.noticeTaskAbort(taskId, taskStepFlow.getStepName());\n                    taskStepFlowDao.updateStatus(id, taskFlowStatusEnum.getStatus());\n                    taskStepFlowDao.updateLog(id, ExceptionUtils.getFullStackTrace(e));\n                    logger.error(BaseTask.marker, \"{} step {} error: \" + e.getMessage(), className,\n                            taskStepFlow.getStepName(), e);\n                    break;\n                }\n            }\n            if (taskFlowStatusEnum != null && taskFlowStatusEnum.equals(TaskFlowStatusEnum.SUCCESS)) {\n                taskQueueDao.updateEndTime(taskId, new Date());\n                taskStatusEnum = TaskStatusEnum.SUCCESS;\n            } else {\n                taskStatusEnum = TaskStatusEnum.ABORT;\n            }\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            taskStatusEnum = TaskStatusEnum.ABORT;\n        }\n        taskQueueDao.updateStatus(taskId, taskStatusEnum.getStatus());\n        return taskStatusEnum;\n    }\n\n    @Override\n    public List<TaskStepFlow> getTaskStepFlowList(long taskId) {\n        try {\n            return taskStepFlowDao.getTaskStepFlowList(taskId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public TaskQueue getTaskQueueById(long taskId) {\n        try {\n            return taskQueueDao.getById(taskId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public List<TaskStepMeta> getTaskStepMetaList(String className) {\n        try {\n            return taskStepMetaDao.getTaskStepMetaList(className);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public TaskStepFlow getCurrentTaskStepFlow(long taskId) {\n        List<TaskStepFlow> taskStepFlowList = taskStepFlowDao.getTaskStepFlowList(taskId);\n        if (CollectionUtils.isEmpty(taskStepFlowList)) {\n            return null;\n        }\n        for (TaskStepFlow taskStepFlow : taskStepFlowList) {\n            if (taskStepFlow.isSuccess() || taskStepFlow.isSkip()) {\n                continue;\n            }\n            return taskStepFlow;\n        }\n        // 如果是空，则返回最后一步\n        return taskStepFlowList.get(taskStepFlowList.size() - 1);\n    }\n\n    @Override\n    public long addRedisServerInstallTask(long appId, String host, int port, int maxMemory, String version, Boolean isCluster,\n                                          long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        paramMap.put(TaskConstants.VERSION_KEY, version);\n        paramMap.put(TaskConstants.IS_CLUSTER_KEY, isCluster);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n        String className = TaskConstants.REDIS_SERVER_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addPikaInstallTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = \"pika-\" + host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.PIKA_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    /*@Override\n    public long addCodisServerInstallTask(long appId, String host, int port, int maxMemory, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.CODIS_SERVER_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }*/\n\n    @Override\n    public long addRedisSentinelInstallTask(long appId, String host, int port,\n                                            List<RedisServerNode> masterRedisServerNodes, int quorum, String dbVersion, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_QUORUM_KEY, quorum);\n        paramMap.put(TaskConstants.MASTER_REDIS_SERVER_NODES, masterRedisServerNodes);\n        paramMap.put(TaskConstants.VERSION_KEY, dbVersion);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = TaskConstants.REDIS_SENTINEL_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    /*@Override\n    public long addRedisPortInstallTask(long appId, String redisPortHost, int redisPortHttpPort, String sourceHost,\n                                        int sourcePort, String targetHost, int targetPort, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, redisPortHost);\n        paramMap.put(TaskConstants.HTTP_PORT_KEY, redisPortHttpPort);\n        paramMap.put(TaskConstants.SOURCE_HOST_KEY, sourceHost);\n        paramMap.put(TaskConstants.SOURCE_PORT_KEY, sourcePort);\n        paramMap.put(TaskConstants.TARGET_HOST_KEY, targetHost);\n        paramMap.put(TaskConstants.TARGET_PORT_KEY, targetPort);\n        String importantInfo = redisPortHost + \":\" + redisPortHttpPort;\n        String param = JSONObject.toJSONString(paramMap);\n        //todo TaskConstants.REDIS_PORT_INSTANCE_INSTALL_CLASS;\n        String className = null;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }*/\n\n    @Override\n    public long addRedisMigrateToolInstallTask(String migrateToolHost, int migrateToolPort, long sourceAppId,\n                                               long targetAppId, List<RedisServerNode> sourceRedisServerNodes, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.HOST_KEY, migrateToolHost);\n        paramMap.put(TaskConstants.PORT_KEY, migrateToolPort);\n        paramMap.put(TaskConstants.SOURCE_APP_ID_KEY, sourceAppId);\n        paramMap.put(TaskConstants.TARGET_APP_ID_KEY, targetAppId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, sourceRedisServerNodes);\n        String importantInfo = String\n                .format(\"%s:%s(%s->%s)\", migrateToolHost, migrateToolPort, sourceAppId, targetAppId);\n        String param = JSONObject.toJSONString(paramMap);\n        long appId = sourceAppId;\n\n        String className = null;//todo TaskConstants.REDIS_MIGRATE_TOOL_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addCodisProxyInstallTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.CODIS_PROXY_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addCodisDashboardTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.CODIS_DASHBOARD_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addNutCrackerInstallTask(long appId, String host, int port,\n                                         List<RedisServerNode> masterRedisServerNodes, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        paramMap.put(TaskConstants.MASTER_REDIS_SERVER_NODES, masterRedisServerNodes);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.NUT_CRACKER_INSTANCE_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyPikaTask(long appId, long auditId, int maxMemory, List<String> pikaMachineList,\n                                     List<String> redisSentinelMachineList, List<String> nutCrackerMachineList, int masterPerMachine,\n                                     int sentinelPerMachine, int nutCrackerPerMachine, Boolean isNeedFlushZkConfig, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        paramMap.put(TaskConstants.PIKA_MACHINE_LIST_KEY, pikaMachineList);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_MACHINE_LIST_KEY, redisSentinelMachineList);\n        paramMap.put(TaskConstants.NUT_CRACKER_MACHINE_LIST_KEY, nutCrackerMachineList);\n        paramMap.put(TaskConstants.MASTER_PER_MACHINE_KEY, masterPerMachine);\n        paramMap.put(TaskConstants.SENTINEL_PER_MACHINE_KEY, sentinelPerMachine);\n        paramMap.put(TaskConstants.NUT_CRACKER_PER_MACHINE_KEY, nutCrackerPerMachine);\n        paramMap.put(TaskConstants.IS_NEED_FLUSH_ZK_CONFIG_KEY, isNeedFlushZkConfig);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName() + \"-pika\";//appDesc.getFullName() + \"-pika\"\n        String param = JSONObject.toJSONString(paramMap);\n        String className = null;//todo TaskConstants.TWEM_PROXY_PIKA_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyAppTask(long appId, long auditId, int maxMemory, List<String> redisServerMachineList,\n                                    List<String> redisSentinelMachineList, List<String> nutCrackerMachineList, int masterPerMachine,\n                                    int sentinelPerMachine, int nutCrackerPerMachine, Boolean isNeedFlushZkConfig, String version,\n                                    long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        paramMap.put(TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY, redisServerMachineList);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_MACHINE_LIST_KEY, redisSentinelMachineList);\n        paramMap.put(TaskConstants.NUT_CRACKER_MACHINE_LIST_KEY, nutCrackerMachineList);\n        paramMap.put(TaskConstants.MASTER_PER_MACHINE_KEY, masterPerMachine);\n        paramMap.put(TaskConstants.SENTINEL_PER_MACHINE_KEY, sentinelPerMachine);\n        paramMap.put(TaskConstants.NUT_CRACKER_PER_MACHINE_KEY, nutCrackerPerMachine);\n        paramMap.put(TaskConstants.IS_NEED_FLUSH_ZK_CONFIG_KEY, isNeedFlushZkConfig);\n        paramMap.put(TaskConstants.VERSION_KEY, version);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.TWEM_PROXY_APP_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisStandaloneAppTask(long appId, long appAuditId, int maxMemory, List<String> redisServerMachineList, int masterPerMachine, String dbVersion, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, appAuditId);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        paramMap.put(TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY, redisServerMachineList);\n        paramMap.put(TaskConstants.MASTER_PER_MACHINE_KEY, masterPerMachine);\n        paramMap.put(TaskConstants.VERSION_KEY, dbVersion);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName() + \"-redis-standalone\";\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = TaskConstants.REDIS_STANDALONE_APP_DEPLOY_CLASS;\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n        appAuditDao.updateTaskId(appAuditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addRedisClusterAppTask(long appId, long appAuditId, int maxMemory, List<String> appDeployInfoList, List<String> redisServerMachineList, int masterPerMachine, String dbVersion, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, appAuditId);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        paramMap.put(TaskConstants.APP_DEPLOY_INFO_LIST_KEY,appDeployInfoList);\n        paramMap.put(TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY, redisServerMachineList);\n        paramMap.put(TaskConstants.MASTER_PER_MACHINE_KEY, masterPerMachine);\n        paramMap.put(TaskConstants.VERSION_KEY, dbVersion.replaceAll(RedisConstUtils.REDIS_VERSION_PREFIX,\"\"));\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName() + \"-redis-cluster\";\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = TaskConstants.REDIS_CLUSTER_APP_DEPLOY_CLASS;\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n        appAuditDao.updateTaskId(appAuditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addRedisSentinelAppTask(long appId, long appAuditId, int maxMemory, List<String> redisServerMachineList,\n                                        List<String> redisSentinelMachineList, int masterPerMachine, int sentinelPerMachine, String dbVersion,\n                                        long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, appAuditId);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        paramMap.put(TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY, redisServerMachineList);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_MACHINE_LIST_KEY, redisSentinelMachineList);\n        paramMap.put(TaskConstants.MASTER_PER_MACHINE_KEY, masterPerMachine);\n        paramMap.put(TaskConstants.SENTINEL_PER_MACHINE_KEY, sentinelPerMachine);\n        paramMap.put(TaskConstants.VERSION_KEY, dbVersion);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = TaskConstants.REDIS_SENTINEL_APP_DEPLOY_CLASS;\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n        appAuditDao.updateTaskId(appAuditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addPikaSentinelAppTask(long appId, long appAuditId, int maxMemory, List<String> pikaMachineList,\n                                       List<String> redisSentinelMachineList, int masterPerMachine, int sentinelPerMachine, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, appAuditId);\n        paramMap.put(TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY, maxMemory);\n        paramMap.put(TaskConstants.PIKA_MACHINE_LIST_KEY, pikaMachineList);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_MACHINE_LIST_KEY, redisSentinelMachineList);\n        paramMap.put(TaskConstants.MASTER_PER_MACHINE_KEY, masterPerMachine);\n        paramMap.put(TaskConstants.SENTINEL_PER_MACHINE_KEY, sentinelPerMachine);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName() + \"-pika-sentinel\";\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.PIKA_SENTINEL_APP_INSTALL_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addNutCrackerScaleOutTask(long appId, List<String> nutCrackerMachineList, int nutCrackerPerMachine,\n                                          long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.NUT_CRACKER_MACHINE_LIST_KEY, nutCrackerMachineList);\n        paramMap.put(TaskConstants.NUT_CRACKER_PER_MACHINE_KEY, nutCrackerPerMachine);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.NUT_CRACKER_SCALE_OUT_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addNutCrackerListOfflineTask(long appId, List<NutCrackerNode> nutCrackerNodes, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.NUT_CRACKER_NODES_KEY, nutCrackerNodes);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n\n        String className = null;//todo TaskConstants.NUT_CRACKER_LIST_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisSentinelListOfflineTask(long appId, List<RedisSentinelNode> redisSentinelNodes,\n                                                long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_NODES_KEY, redisSentinelNodes);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.REDIS_SENTINEL_LIST_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisSlaveServerOfflineTask(long appId, List<RedisServerNode> redisServerNodes, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.REDIS_SLAVE_SERVER_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addPikaSlaveOfflineTask(long appId, List<PikaNode> pikaNodes, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.PIKA_NODES_KEY, pikaNodes);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.PIKA_SLAVE_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisSentinelAddTask(long appId, List<String> redisSentinelMachineList, String dbVersion,\n                                        long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_MACHINE_LIST_KEY, redisSentinelMachineList);\n        paramMap.put(TaskConstants.VERSION_KEY, dbVersion);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.PIKA_SLAVE_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyOfflineTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.TWEMPROXY_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisSentinelAppOfflineTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.REDIS_SENTINEL_APP_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addPikaSentinelAppOfflineTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.PIKA_SENTINEL_APP_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addMemcacheClusterOfflineTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.MEMCACHE_CLUSTER_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyPikaOfflineTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n\n        String importantInfo = appDesc.getName() + \"-pika\";\n        String className = null;//todo TaskConstants.TWEMPROXY_PIKA_OFFLINE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addAppConfigFlushZkTask(long appId, Boolean appIsNew, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.APP_IS_NEW_KEY, appIsNew);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n\n        String importantInfo = appDesc.getName();\n        String className = null;//todo TaskConstants.APP_CONFIG_FLUSH_ZK_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyToTwemproxyTask(long sourceAppId, long targetAppId, long appAuditId, boolean isScaleOut,\n                                            long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.SOURCE_APP_ID_KEY, sourceAppId);\n        paramMap.put(TaskConstants.TARGET_APP_ID_KEY, targetAppId);\n        paramMap.put(TaskConstants.IS_SCALE_OUT_KEY, isScaleOut);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, appAuditId);\n        String importantInfo = String.format(\"%s->%s\", sourceAppId, targetAppId);\n        String param = JSONObject.toJSONString(paramMap);\n        long appId = sourceAppId;\n\n        String className = null;//todo TaskConstants.TWEMPROXY_TO_TWEMPROXY_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyToTwemproxyTaskV2(long sourceAppId, long targetAppId, long appAuditId, boolean isScaleOut,\n                                              boolean isOnlyMigrate, long parentTaskId) {\n        AppDesc appDesc = appService.getByAppId(sourceAppId);\n\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.SOURCE_APP_ID_KEY, sourceAppId);\n        paramMap.put(TaskConstants.TARGET_APP_ID_KEY, targetAppId);\n        paramMap.put(TaskConstants.IS_SCALE_OUT_KEY, isScaleOut);\n        paramMap.put(TaskConstants.IS_ONLY_MIGRATE_KEY, isOnlyMigrate);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, appAuditId);\n        String param = JSONObject.toJSONString(paramMap);\n        String importantInfo = String.format(\"%s->%s(%s)\", sourceAppId, targetAppId, appDesc.getName());\n        long appId = sourceAppId;\n        String className = null;//todo TaskConstants.TWEMPROXY_TO_TWEMPROXY_V2_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRemoveRedisMigrateToolTask(String host, int port, long sourceAppId,\n                                              long targetAppId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        paramMap.put(TaskConstants.SOURCE_APP_ID_KEY, sourceAppId);\n        paramMap.put(TaskConstants.TARGET_APP_ID_KEY, targetAppId);\n        String importantInfo = String.format(\"%s:%s(%s->%s)\", host, port, sourceAppId, targetAppId);\n        String param = JSONObject.toJSONString(paramMap);\n\n        long appId = sourceAppId;\n        String className = null;//todo TaskConstants.REDIS_MIGRATE_TOOL_REMOVE_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerStopTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.REDIS_SERVER_STOP_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addMemcacheStopTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.MEMCACHE_STOP_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerStartTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.REDIS_SERVER_START_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerFlushDataTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.REDIS_SERVER_FLUSH_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerIdleKeyAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = RedisServerIdleKeyAnalysisTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerBigKeyAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = RedisServerBigKeyAnalysisTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerKeyTypeAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = RedisServerKeyTypeAnalysisTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerKeyTtlAnalysisTask(long appId, long auditId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = RedisServerKeyTtlAnalysisTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisServerKeyValueAnalysisTask(long appId, long auditId, String host, int port,\n                                                   long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = RedisServerKeyValueAnalysisTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    @Transactional\n    public long addAppKeyAnalysisTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n\n        String className = AppKeyAnalysisTask.class.getSimpleName();\n        AppAudit appAudit = appAuditDao.getAppAudit(auditId);\n\n        String nodeInfos = StringUtils.isNotBlank(appAudit.getParam1()) ? appAudit.getParam1() :\n                appService.getAppMasterInstanceInfoList(appId).stream().map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.joining(\",\"));\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfos)));\n//        if (StringUtils.isNotBlank(nodeInfo)) {\n//            paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfo)));\n//            logger.info(\"node-analysis-task: {}\", nodeInfo);\n//        }\n\n        appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n        appAuditDao.updateTaskId(auditId, taskId);\n        return taskId;\n    }\n\n    private static List<RedisServerNode> parseNodeInfo(String nodeInfo) {\n        List<RedisServerNode> list = Lists.newArrayList();\n        String[] nodeList = nodeInfo.split(\",\");\n        for (String node : nodeList) {\n            RedisServerNode redisNode = new RedisServerNode();\n            String[] array = node.replace(\"{\", \"\").replace(\"}\", \"\").split(\":\");\n            if (array.length != 2) {\n                throw new RuntimeException(nodeInfo + \" format error\");\n            }\n            redisNode.setIp(array[0]);\n            redisNode.setPort(NumberUtils.toInt(array[1]));\n            list.add(redisNode);\n        }\n        return list;\n    }\n\n    @Override\n    public long addTwemproxyFlushAllDataTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.TWEMPROXY_KEY_ANALYSIS_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyPikaFlushAllDataTask(long appId, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName() + \"-pika\";\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.TWEMPROXY_PIKA_FLUSHALL_DATA_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addRedisSentinelStopTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.REDIS_SENTINEL_STOP_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addNutCrackerStopTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.NUT_CRACKER_STOP_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addPikaStopTask(long appId, String host, int port, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.PIKA_STOP_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addSlaveRedisServerRebuildTask(long appId, String masterHost, int masterPort,\n                                               String slaveMachineHost, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.MASTER_HOST_KEY, masterHost);\n        paramMap.put(TaskConstants.MASTER_PORT_KEY, masterPort);\n        paramMap.put(TaskConstants.SLAVE_MACHINE_KEY, slaveMachineHost);\n        String importantInfo = slaveMachineHost;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.REDIS_SLAVE_SERVER_REBUILD_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addSlavePikaRebuildTask(long appId, String masterHost, int masterPort, String slaveMachineHost,\n                                        long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.MASTER_HOST_KEY, masterHost);\n        paramMap.put(TaskConstants.MASTER_PORT_KEY, masterPort);\n        paramMap.put(TaskConstants.SLAVE_MACHINE_KEY, slaveMachineHost);\n        String importantInfo = slaveMachineHost;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.PIKA_SLAVE_REBUILD_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addSlaveRedisSentinelFailoverTask(long appId, String masterHost, int masterPort, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.MASTER_HOST_KEY, masterHost);\n        paramMap.put(TaskConstants.MASTER_PORT_KEY, masterPort);\n        String importantInfo = masterHost + \":\" + masterPort;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.REDIS_SENTINEL_FAILOVER_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addTwemproxyFaultMachineFailoverTask(long appId, String host, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        String importantInfo = host;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.TWEMPROXY_FAULT_MACHINE_FAILOVER_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addMachineSlaveRebuildTask(long appId, String host, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        String importantInfo = host;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = null;//todo TaskConstants.MACHINE_SLAVE_REBUILD_CLASS;\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo,\n                parentTaskId);\n    }\n\n    @Override\n    public long addAppTopologyExamTask(boolean auto, int examType, long appId, long parentTaskId) {\n        Map<String, Object> paramMap = Maps.newHashMap();\n        paramMap.put(TaskConstants.EXAM_TYPE_KEY, examType);\n        paramMap.put(\"auto\", auto);\n        if (examType == ExamToolEnum.EXAM_APPID.getValue()) {\n            paramMap.put(TaskConstants.APPID_KEY, appId);\n        }\n        String importantInfo = \"topology exam, examType:\" + String.valueOf(examType);\n        String className = TopologyExamTask.class.getSimpleName();\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(-1, className, param, importantInfo, parentTaskId);\n        return taskId;\n    }\n\n    @Override\n    public long addMachineExamTask(List<String> ipList, Integer useType, long parentTaskId) {\n        Map<String, Object> paramMap = Maps.newHashMap();\n        paramMap.put(TaskConstants.MACHINE_IP_LIST_KEY, ipList);\n        paramMap.put(TaskConstants.USE_TYPE_KEY, useType);\n        String importantInfo = \"machine cpu/mem exam\";\n        String className = MachineExamTask.class.getSimpleName();\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(-1, className, param, importantInfo, parentTaskId);\n        return taskId;\n    }\n\n    @Override\n    public long addOffLineAppTask(long appId, Long auditId, long parentTaskId, AppUser userInfo) {\n        Map<String, Object> paramMap = Maps.newHashMap();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId == null ? -1 : auditId);\n        paramMap.put(TaskConstants.USER_INFO_KEY, userInfo);\n        String param = JSONObject.toJSONString(paramMap);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = OffLineAppTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addAppScanKeyTask(long appId, long auditId, String nodes, String pattern, int size, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        String nodeInfos = StringUtils.isNotBlank(nodes) ? nodes :\n                appService.getAppMasterInstanceInfoList(appId).stream().map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.joining(\",\"));\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfos)));\n        paramMap.put(\"pattern\", pattern);\n        paramMap.put(\"size\", size);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = AppScanKeyTask.class.getSimpleName();\n\n        appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n        appAuditDao.updateTaskId(auditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addInstanceScanKeyTask(long appId, long auditId, String host, int port, String pattern, int size, long parentTaskId) {\n\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n\n        paramMap.put(\"pattern\", pattern);\n        paramMap.put(\"size\", size);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = InstanceScanKeyTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addInstanceScanCleanKeyTask(long appId, long auditId, String host, int port, Map<String, Object> params, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.putAll(params);\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = InstanceScanCleanKeyTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addAppDelKeyTask(long appId, String nodes, String pattern, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        String nodeInfos = StringUtils.isNotBlank(nodes) ? nodes :\n                appService.getAppMasterInstanceInfoList(appId).stream().map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.joining(\",\"));\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfos)));\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"pattern\", pattern);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = AppDelKeyTask.class.getSimpleName();\n\n        appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n        appAuditDao.updateTaskId(auditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addInstanceDelKeyTask(long appId, String host, int port, String pattern, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"pattern\", pattern);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = InstanceDelKeyTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addAppBigKeyTask(long appId, String nodes, long fromBytes, long toBytes, int size, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        String nodeInfos = StringUtils.isNotBlank(nodes) ? nodes :\n                appService.getAppMasterInstanceInfoList(appId).stream().map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.joining(\",\"));\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfos)));\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"fromBytes\", fromBytes);\n        paramMap.put(\"toBytes\", toBytes);\n        paramMap.put(\"size\", size);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = AppBigKeyTask.class.getSimpleName();\n\n        appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n        appAuditDao.updateTaskId(auditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addInstanceBigKeyTask(long appId, String host, int port, long fromBytes, long toBytes, int size, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"fromBytes\", fromBytes);\n        paramMap.put(\"toBytes\", toBytes);\n        paramMap.put(\"size\", size);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = InstanceBigKeyTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addAppIdleKeyTask(long appId, String nodes, long idleTime, int size, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        String nodeInfos = StringUtils.isNotBlank(nodes) ? nodes :\n                appService.getAppMasterInstanceInfoList(appId).stream().map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.joining(\",\"));\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfos)));\n        paramMap.put(\"idleTime\", idleTime);\n        paramMap.put(\"size\", size);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = AppIdleKeyTask.class.getSimpleName();\n\n        appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n        appAuditDao.updateTaskId(auditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addInstanceIdleKeyTask(long appId, String host, int port, long idleTime, int size, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        paramMap.put(\"idleTime\", idleTime);\n        paramMap.put(\"size\", size);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = InstanceIdleKeyTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addAppHotKeyTask(long appId, String nodes, String command, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        String nodeInfos = StringUtils.isNotBlank(nodes) ? nodes :\n                appService.getAppMasterInstanceInfoList(appId).stream().map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.joining(\",\"));\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfos)));\n        paramMap.put(\"command\", command);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = AppHotKeyTask.class.getSimpleName();\n\n        appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n        appAuditDao.updateTaskId(auditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addInstanceHotKeyTask(long appId, String host, int port, String command, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        paramMap.put(\"command\", command);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = InstanceHotKeyTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addAppSlotAnalysisTask(long appId, String nodes, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        String nodeInfos = StringUtils.isNotBlank(nodes) ? nodes : appService.getAppMasterInstanceInfoList(appId).stream().map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.joining(\",\"));\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, JSONObject.toJSONString(parseNodeInfo(nodeInfos)));\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n        String className = AppSlotAnalysisTask.class.getSimpleName();\n\n        appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n        String param = JSONObject.toJSONString(paramMap);\n        long taskId = generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n        appAuditDao.updateTaskId(auditId, taskId);\n        return taskId;\n    }\n\n    @Override\n    public long addInstanceSlotAnalysisTask(long appId, String host, int port, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        paramMap.put(TaskConstants.HOST_KEY, host);\n        paramMap.put(TaskConstants.PORT_KEY, port);\n        String importantInfo = host + \":\" + port;\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = InstanceSlotAnalysisTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    @Override\n    public long addAppScanCleanTask(long appId, Map<String, Object> params, long auditId, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.putAll(params);\n        paramMap.put(TaskConstants.APPID_KEY, appId);\n        paramMap.put(TaskConstants.AUDIT_ID_KEY, auditId);\n        paramMap.put(\"parentTaskId\", parentTaskId);\n        String param = JSONObject.toJSONString(paramMap);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        String importantInfo = appDesc.getName();\n\n        String className = AppScanCleanKeyTask.class.getSimpleName();\n        return generateAndSaveTaskQueue(appId, className, param, importantInfo, parentTaskId);\n    }\n\n    /**\n     * 生成并保存taskqueue\n     *\n     * @param appId\n     * @param className\n     * @param param\n     * @param parentTaskId\n     * @return\n     */\n    private long generateAndSaveTaskQueue(long appId, String className, String param, String importantInfo,\n                                          long parentTaskId) {\n        TaskQueue taskQueue = generateTaskQueue(appId, className, param, importantInfo, parentTaskId);\n        try {\n            taskQueueDao.save(taskQueue);\n            return taskQueue.getId();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public void updateTaskStepFlowChildTaskId(long taskStepFlowId, long childTaskId) {\n        try {\n            taskStepFlowDao.updateChildTaskId(taskStepFlowId, childTaskId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public List<TaskQueue> getTaskQueueList(TaskStatusEnum taskStatusEnum) {\n        try {\n            return taskQueueDao.getTaskQueueListByStatus(taskStatusEnum.getStatus());\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public void updateTaskQueueStatus(long taskId, TaskStatusEnum taskStatusEnum) {\n        try {\n            taskQueueDao.updateStatus(taskId, taskStatusEnum.getStatus());\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public int getTaskQueueCount(TaskSearch taskSearch) {\n        try {\n            return taskQueueDao.getTaskQueueCount(taskSearch);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return 0;\n        }\n    }\n\n    @Override\n    public List<TaskQueue> getTaskQueueList(TaskSearch taskSearch) {\n        try {\n            return taskQueueDao.getTaskQueueList(taskSearch);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public List<TaskQueue> getTaskQueueTreeByTaskId(long searchTaskId) {\n        List<TaskQueue> taskQueueList = new ArrayList<TaskQueue>();\n        fillTaskQueueList(taskQueueList, searchTaskId);\n        return taskQueueList;\n    }\n\n    /**\n     * 递归获取任务\n     *\n     * @param taskQueueList\n     * @param taskId\n     */\n    private void fillTaskQueueList(List<TaskQueue> taskQueueList, long taskId) {\n        TaskQueue parentTaskQueue = taskQueueDao.getById(taskId);\n        if (parentTaskQueue != null) {\n            taskQueueList.add(parentTaskQueue);\n        } else {\n            return;\n        }\n        List<TaskQueue> childTaskQueueList = taskQueueDao.getChildTaskQueueList(taskId);\n        if (CollectionUtils.isEmpty(childTaskQueueList)) {\n            return;\n        } else {\n            for (TaskQueue taskQueue : childTaskQueueList) {\n                fillTaskQueueList(taskQueueList, taskQueue.getId());\n            }\n        }\n    }\n\n    @Override\n    public OperateResult updateParam(long taskId, String param) {\n        try {\n            taskQueueDao.updateParam(taskId, param);\n            return OperateResult.success();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return OperateResult.fail(e.getMessage());\n        }\n    }\n\n    /**\n     * 生成新的任务\n     *\n     * @param appId\n     * @param className\n     * @param param\n     * @param importantInfo\n     * @param parentTaskId\n     * @return\n     */\n    private TaskQueue generateTaskQueue(long appId, String className, String param, String importantInfo,\n                                        long parentTaskId) {\n        TaskQueue taskQueue = new TaskQueue();\n        taskQueue.setAppId(appId);\n        taskQueue.setClassName(className);\n        taskQueue.setParam(param);\n        taskQueue.setInitParam(param);\n        taskQueue.setStatus(TaskStatusEnum.NEW.getStatus());\n        taskQueue.setParentTaskId(parentTaskId);\n        Date now = new Date();\n        taskQueue.setStartTime(now);\n        taskQueue.setEndTime(now);\n        taskQueue.setCreateTime(now);\n        taskQueue.setUpdateTime(now);\n        taskQueue.setErrorCode(TaskErrorCodeEnum.RIGHT.getCode());\n        taskQueue.setTaskNote(\"\");\n        taskQueue.setErrorMsg(\"\");\n        taskQueue.setImportantInfo(importantInfo);\n        return taskQueue;\n    }\n\n    @Override\n    public OperateResult updateTaskFlowStatus(long taskFlowId, int status) {\n        try {\n            taskStepFlowDao.updateStatus(taskFlowId, status);\n            return OperateResult.success();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return OperateResult.fail(e.getMessage());\n        }\n    }\n\n    private String getThreadPoolKey() {\n        return AsyncThreadPoolFactory.TASK_EXECUTE_POOL;\n    }\n\n    @Override\n    public List<TaskQueue> getByAppAndClass(long appId, String className) {\n        try {\n            return taskQueueDao.getByAppAndClass(appId, className);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public long addMachineSyncTask(String sourceIp, String targetIp, String containerIp, String important_info, long parentTaskId) {\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.SOURCE_HOST_KEY, sourceIp);\n        paramMap.put(TaskConstants.TARGET_HOST_KEY, targetIp);\n        paramMap.put(TaskConstants.CONTAINER_IP, containerIp);\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = TaskConstants.MACHINE_SYNC_CLASS;\n        return generateAndSaveTaskQueue(-1, className, param, important_info, parentTaskId);\n    }\n\n    public long addResourceCompileTask(Integer resourceId, Integer repositoryId, String containerIp, AppUser userInfo){\n        Map<String, Object> paramMap = new HashMap<String, Object>();\n        paramMap.put(TaskConstants.RESOURCE_ID, resourceId);\n        paramMap.put(TaskConstants.REPOSITORY_ID, repositoryId);\n        paramMap.put(TaskConstants.CONTAINER_IP, containerIp);\n        paramMap.put(TaskConstants.USER_INFO_KEY, userInfo.getName());\n        String param = JSONObject.toJSONString(paramMap);\n\n        String className = TaskConstants.PACK_COMPILE_TASK;\n        return generateAndSaveTaskQueue(-1, className, param, \"\", -1);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/AppKeyAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.google.common.collect.Lists;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Tuple;\n\nimport java.util.*;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @author fulei\n */\n@Component(\"AppKeyAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppKeyAnalysisTask extends BaseTask {\n\n    private long appId;\n\n    private long auditId;\n\n    private List<RedisServerNode> redisServerNodes;\n\n    private final static int MAX_ANALYSIS_COUNT = 1;\n\n    private final static long TYPE_SLEEP_BASE = 20000000;\n\n    private final static long TTL_SLEEP_BASE = 20000000;\n\n    private final static long IDLE_SLEEP_BASE = 20000000;\n\n    private final static long BIGKEY_SLEEP_BASE = 20000000;\n\n    private final static long VALUE_SIZE_SLEEP_BASE = 20000000;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2. redis server key type分析\n        taskStepList.add(\"createRedisServerKeyTypeTask\");\n        taskStepList.add(\"waitRedisServerKeyTypeFinish\");\n        taskStepList.add(\"showKeyTypeResult\");\n        // 3. redis server big key分析\n        taskStepList.add(\"createRedisServerBigKeyTask\");\n        taskStepList.add(\"waitRedisServerBigKeyFinish\");\n        // 4. redis server key ttl分析\n        taskStepList.add(\"createRedisServerKeyTtlTask\");\n        taskStepList.add(\"waitRedisServerKeyTtlFinish\");\n        taskStepList.add(\"showKeyTtlResult\");\n        // 5. redis server idle key分析\n        taskStepList.add(\"createRedisServerIdleKeyTask\");\n        taskStepList.add(\"waitRedisServerIdleKeyFinish\");\n        taskStepList.add(\"showIdleKeyResult\");\n        // 6. redis server key value分析\n        taskStepList.add(\"createRedisServerKeyValueSizeTask\");\n        taskStepList.add(\"waitRedisServerKeyValueSizeFinish\");\n        taskStepList.add(\"showKeyValueSizeResult\");\n        // 7. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"user paramMap node: {}\", redisServerNodesStr);\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        //@TODO 其他比如考虑qps之类\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 6.1.创建分析闲置key子任务(master)\n     */\n    public TaskFlowStatusEnum createRedisServerIdleKeyTask() {\n        redisServerNodes = buildRedisServerNodes();\n\n        // 每个server的dbsize\n        Map<String, Long> redisServerDbSizeMap = new HashMap<String, Long>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long dbSize = redisCenter.getDbSize(appId, redisServerNode.getIp(), redisServerNode.getPort());\n            logger.info(marker, \"appId {} {}:{} dbSize is {} \", appId, host, port, dbSize);\n            if (dbSize < 0) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n            redisServerDbSizeMap.put(host + \":\" + port, dbSize);\n        }\n\n        long keyCounter = 0;\n        int factor = 1;\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //可能已经执行过\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n            long sleepCounter = factor * IDLE_SLEEP_BASE;\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            try {\n                long dbSize = redisServerDbSizeMap.get(host + \":\" + port);\n                keyCounter += dbSize;\n\n                long childTaskId = taskService.addRedisServerIdleKeyAnalysisTask(appId, auditId, host, port, taskId);\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"appId {} {}:{}  redis server idle key analysis task create successfully\", appId,\n                        host, port);\n\n                // 超额就sleep\n                if (keyCounter > sleepCounter) {\n                    factor++;\n                    sleepSeconds(300);\n                }\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{}  redis server idle key analysis create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 6.2.等待分析闲置key子任务(master)\n     */\n    public TaskFlowStatusEnum waitRedisServerIdleKeyFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId,\n                    TaskConstants.REDIS_SERVER_IDLE_KEY_ANALYSIS_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} redis server idle key analysis task execute fail\", appId, host,\n                        port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} redis server idle key analysis task execute successfully\", appId,\n                        host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 6.3.展示闲置key分析结果\n     */\n    public TaskFlowStatusEnum showIdleKeyResult() {\n        String idleKeyResultKey = ConstUtils.getRedisServerIdleKey(appId, auditId);\n        Set<Tuple> tuples = assistRedisService.zrangeWithScores(idleKeyResultKey, 0, -1);\n        for (Tuple tuple : tuples) {\n            String member = tuple.getElement();\n            double score = tuple.getScore();\n            logger.info(marker, \"{} {} idle distri {} {}\", idleKeyResultKey, appId, member, score);\n            assistRedisService.zadd(idleKeyResultKey, (long) score, member);\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.1.创建分析key类型子任务\n     */\n    public TaskFlowStatusEnum createRedisServerKeyTypeTask() {\n        redisServerNodes = buildRedisServerNodes();\n\n        Map<String, Long> redisServerDbSizeMap = new HashMap<String, Long>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long dbSize = redisCenter.getDbSize(appId, redisServerNode.getIp(), redisServerNode.getPort());\n            logger.info(marker, \"appId {} {}:{} dbSize is {} \", appId, host, port, dbSize);\n            redisServerDbSizeMap.put(host + \":\" + port, dbSize);\n        }\n\n        long keyCounter = 0;\n        int factor = 1;\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //可能已经执行过\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n            long sleepCounter = factor * TYPE_SLEEP_BASE;\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            try {\n                long dbSize = redisServerDbSizeMap.get(host + \":\" + port);\n                keyCounter += dbSize;\n\n                long childTaskId = taskService.addRedisServerKeyTypeAnalysisTask(appId, auditId, host, port, taskId);\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"appId {} {}:{}  redis server key type analysis task create successfully\", appId,\n                        host, port);\n\n                // 超额就sleep\n                if (keyCounter > sleepCounter) {\n                    factor++;\n                    sleepSeconds(120);\n                }\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{}  redis server key type analysis create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待key类分析结束\n     */\n    public TaskFlowStatusEnum waitRedisServerKeyTypeFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId,\n                    TaskConstants.REDIS_SERVER_KEY_TYPE_ANALYSIS_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} redis server key type analysis task execute fail\", appId, host,\n                        port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} redis server key type analysis task execute successfully\", appId,\n                        host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.3.展示key类分析结束\n     */\n    public TaskFlowStatusEnum showKeyTypeResult() {\n        String keyTypeResultKey = ConstUtils.getRedisServerTypeKey(appId, auditId);\n        Set<Tuple> tuples = assistRedisService.zrangeWithScores(keyTypeResultKey, 0, -1);\n        for (Tuple tuple : tuples) {\n            String member = tuple.getElement();\n            double score = tuple.getScore();\n            logger.info(marker, \"{} {} type distri {} {}\", keyTypeResultKey, appId, member, score);\n            assistRedisService.zadd(keyTypeResultKey, (long) score, member);\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 5.1.创建分析key ttl子任务\n     */\n    public TaskFlowStatusEnum createRedisServerKeyTtlTask() {\n        redisServerNodes = buildRedisServerNodes();\n\n        Map<String, Long> redisServerDbSizeMap = new HashMap<String, Long>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long dbSize = redisCenter.getDbSize(appId, redisServerNode.getIp(), redisServerNode.getPort());\n            logger.info(marker, \"appId {} {}:{} dbSize is {} \", appId, host, port, dbSize);\n            redisServerDbSizeMap.put(host + \":\" + port, dbSize);\n        }\n\n        long keyCounter = 0;\n        int factor = 1;\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //可能已经执行过\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n            long sleepCounter = factor * TTL_SLEEP_BASE;\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            try {\n                long dbSize = redisServerDbSizeMap.get(host + \":\" + port);\n                keyCounter += dbSize;\n\n                long childTaskId = taskService.addRedisServerKeyTtlAnalysisTask(appId, auditId, host, port, taskId);\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"appId {} {}:{}  redis server key ttl analysis task create successfully\", appId,\n                        host, port);\n\n                // 超额就sleep\n                if (keyCounter > sleepCounter) {\n                    factor++;\n                    sleepSeconds(120);\n                }\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{}  redis server key ttl analysis create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 5.2.等待key ttl分析结束\n     */\n    public TaskFlowStatusEnum waitRedisServerKeyTtlFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId,\n                    TaskConstants.REDIS_SERVER_KEY_TTL_ANALYSIS_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} redis server key ttl analysis task execute fail\", appId, host,\n                        port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} redis server key ttl analysis task execute successfully\", appId,\n                        host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 5.3.展示key ttl分析结束\n     */\n    public TaskFlowStatusEnum showKeyTtlResult() {\n        String keyTtlResultKey = ConstUtils.getRedisServerTtlKey(appId, auditId);\n        Set<Tuple> tuples = assistRedisService.zrangeWithScores(keyTtlResultKey, 0, -1);\n        for (Tuple tuple : tuples) {\n            String member = tuple.getElement();\n            double score = tuple.getScore();\n            logger.info(marker, \"{} {} ttl distri {} {}\", keyTtlResultKey, appId, member, score);\n            assistRedisService.zadd(keyTtlResultKey, (long) score, member);\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.1.创建分析big key子任务\n     */\n    public TaskFlowStatusEnum createRedisServerBigKeyTask() {\n        redisServerNodes = buildRedisServerNodes();\n\n        // 每个server的dbsize\n        Map<String, Long> redisServerDbSizeMap = new HashMap<String, Long>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long dbSize = redisCenter.getDbSize(appId, redisServerNode.getIp(), redisServerNode.getPort());\n            logger.info(marker, \"appId {} {}:{} dbSize is {} \", appId, host, port, dbSize);\n            redisServerDbSizeMap.put(host + \":\" + port, dbSize);\n        }\n\n        long keyCounter = 0;\n        int factor = 1;\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //可能已经执行过\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n            long sleepCounter = factor * BIGKEY_SLEEP_BASE;\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            try {\n                long dbSize = redisServerDbSizeMap.get(host + \":\" + port);\n                keyCounter += dbSize;\n\n                long childTaskId = taskService.addRedisServerBigKeyAnalysisTask(appId, auditId, host, port, taskId);\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"appId {} {}:{}  redis server big key analysis task create successfully\", appId,\n                        host, port);\n\n                // 超额就sleep\n                if (keyCounter > sleepCounter) {\n                    factor++;\n                    sleepSeconds(120);\n                }\n\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{}  redis server big key analysis create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.2.等待bigkey子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitRedisServerBigKeyFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId,\n                    TaskConstants.REDIS_SERVER_BIG_KEY_ANALYSIS_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} redis server big key analysis task execute fail\", appId, host,\n                        port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} redis server big key analysis task execute successfully\", appId,\n                        host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 6.1.创建分析key value size子任务\n     */\n    public TaskFlowStatusEnum createRedisServerKeyValueSizeTask() {\n        redisServerNodes = buildRedisServerNodes();\n\n        Map<String, Long> redisServerDbSizeMap = new HashMap<String, Long>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long dbSize = redisCenter.getDbSize(appId, redisServerNode.getIp(), redisServerNode.getPort());\n            logger.info(marker, \"appId {} {}:{} dbSize is {} \", appId, host, port, dbSize);\n            redisServerDbSizeMap.put(host + \":\" + port, dbSize);\n        }\n\n        long keyCounter = 0;\n        int factor = 1;\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //可能已经执行过\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n            long sleepCounter = factor * VALUE_SIZE_SLEEP_BASE;\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            try {\n                long dbSize = redisServerDbSizeMap.get(host + \":\" + port);\n                keyCounter += dbSize;\n\n                long childTaskId = taskService.addRedisServerKeyValueAnalysisTask(appId, auditId, host, port, taskId);\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"appId {} {}:{}  redis server key value size analysis task create successfully\",\n                        appId, host, port);\n\n                // 超额就sleep\n                if (keyCounter > sleepCounter) {\n                    factor++;\n                    sleepSeconds(120);\n                }\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{}  redis server value size analysis create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 6.2.等待key value size分析结束\n     */\n    public TaskFlowStatusEnum waitRedisServerKeyValueSizeFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId,\n                    TaskConstants.REDIS_SERVER_KEY_VALUE_SIZE_ANALYSIS_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} redis server key value size analysis task execute fail\", appId,\n                        host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} redis server key value size analysis task execute successfully\",\n                        appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 6.3.展示key value size分析结束\n     */\n    public TaskFlowStatusEnum showKeyValueSizeResult() {\n        String keyValueSizeResultKey = ConstUtils.getRedisServerValueSizeKey(appId, auditId);\n        Set<Tuple> tuples = assistRedisService.zrangeWithScores(keyValueSizeResultKey, 0, -1);\n        for (Tuple tuple : tuples) {\n            String member = tuple.getElement();\n            double score = tuple.getScore();\n            logger.info(marker, \"{} {} value size distri {} {}\", keyValueSizeResultKey, appId, member, score);\n            assistRedisService.zadd(keyValueSizeResultKey, (long) score, member);\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 7.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"集群(%s-%s)的键值分析完成\", appDesc.getAppId(), appDesc.getName()));\n            //appWechatUtil.noticeAuditFinish(auditId, content.toString());\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n    private List<RedisServerNode> transformRedisServerFromInstance(List<InstanceInfo> instanceInfoList, int nodeCount) {\n        List<RedisServerNode> redisServerNodes = new ArrayList<RedisServerNode>();\n        for (int i = 0; i < instanceInfoList.size() && i < nodeCount; i++) {\n            InstanceInfo instanceInfo = instanceInfoList.get(i);\n            RedisServerNode redisServerNode = new RedisServerNode();\n            redisServerNode.setIp(instanceInfo.getIp());\n            redisServerNode.setPort(instanceInfo.getPort());\n            redisServerNodes.add(redisServerNode);\n        }\n        return redisServerNodes;\n    }\n\n    private List<RedisServerNode> buildRedisServerNodes() {\n        List<RedisServerNode> list = Lists.newArrayList();\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            List<InstanceInfo> instanceInfoList = appService.getAppMasterInstanceInfoList(appId);\n            redisServerNodes = transformRedisServerFromInstance(instanceInfoList, MAX_ANALYSIS_COUNT);\n            list.addAll(redisServerNodes);\n        } else {\n            for (RedisServerNode redisServerNode : redisServerNodes) {\n                list.add(new RedisServerNode(redisServerNode.getIp(), redisServerNode.getPort()));\n            }\n        }\n        return list;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/MachineSyncTask.java",
    "content": "package com.sohu.cache.task.tasks;\n\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.entity.MachineRelation;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.ssh.SSHTemplate.Result;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.MachineTaskEnum;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * Created by chenshi on 2019/5/24.\n */\n@Component(\"MachineSyncTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class MachineSyncTask extends BaseTask {\n\n    /**\n     * 源宿主机ip\n     */\n    private String sourceIp;\n    /**\n     * 目标宿主机ip\n     */\n    private String targetIp;\n    /**\n     * 容器ip\n     */\n    private String containerIp;\n    /**\n     * k8s 持久化/配置/日志 目录\n     */\n    private static String baseConfDir = \"/data/redis/conf/\";\n    private static String baseDataDir = \"/data/redis/data/\";\n    private static String baseLogDir = \"/data/redis/logs/\";\n    private static String backupDir = \"/data/redis/bak/\";\n\n    /**\n     * 同步/校验数据 超时时间\n     */\n    private static int SYNC_DATA_TIMEOUT = 30 * 60 * 1000;\n    private static int MD5_CHECK_TIMEOUT = 2 * 60 * 1000;\n    private static int BACKUP_DATA_TIMEOUT = 5 * 60 * 1000;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n\n        List<String> taskStepList = new ArrayList<String>();\n        //1. 参数初始化\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        //2. 检查源主机同步环境\n        taskStepList.add(\"checkSourceMachineEnv\");\n        //3. 检查目标主机连通性及目录检查\n        taskStepList.add(\"checkTargetMachineEnv\");\n        //4. scp同步数据 source->target host machine\n        taskStepList.add(\"startSyncData\");\n        //5. 校验目的文件是否一致\n        taskStepList.add(\"checkDirMd5\");\n        //6. 备份历史数据\n        taskStepList.add(\"backupSourceData\");\n        //7. 同步完成\n        taskStepList.add(\"syncOver\");\n        return taskStepList;\n    }\n\n    @Override\n    public TaskFlowStatusEnum init() {\n\n        super.init();\n        // 1.源宿主机\n        sourceIp = MapUtils.getString(paramMap, TaskConstants.SOURCE_HOST_KEY);\n        if (StringUtils.isEmpty(sourceIp)) {\n            logger.error(marker, \"task {} source machine ip {} is empty\", taskId, sourceIp);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // 2.目标宿主机\n        targetIp = MapUtils.getString(paramMap, TaskConstants.TARGET_HOST_KEY);\n        if (StringUtils.isEmpty(targetIp)) {\n            logger.error(marker, \"task {} target machine ip {} is empty\", taskId, targetIp);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // 3.容器ip\n        containerIp = MapUtils.getString(paramMap, TaskConstants.CONTAINER_IP);\n        if (!StringUtils.isEmpty(containerIp)) {\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(containerIp);\n            if (machineInfo == null) {\n                logger.error(marker, \"task {} container ip {} is not exist\", taskId, containerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } else {\n            logger.error(marker, \"task {} container ip {} is empty\", taskId, containerIp);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.源主机:\n     * 2.1.机器连通性\n     * 2.2.sshpass安装\n     * 2.3.源数据目录是否存在\n     */\n    public TaskFlowStatusEnum checkSourceMachineEnv() {\n\n        // 1.连通性 + sshpass\n        String sshpass_command = \"sshpass -V | head -1 \";\n        // 源数据目录\n        String checkDir_command = \"ls -l \" + baseConfDir.concat(containerIp) + \" | wc -l && ls -l \" + baseDataDir.concat(containerIp) + \" | wc -l && ls -l \" + baseLogDir.concat(containerIp) + \" | wc -l\";\n\n        try {\n            Result checkResult = sshService.executeWithResult(sourceIp, sshpass_command);\n            if (checkResult.isSuccess() && checkResult.getResult().indexOf(\"sshpass\") > -1) {\n                logger.info(marker, \"sourceMachine ip :{} check sshpass env result:{}\", sourceIp, checkResult.getResult());\n            } else {\n                logger.error(marker, \"sourceMachine ip :{} check sshpass env error message:{}\", sourceIp, checkResult.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n            Result checkDirResult = sshService.executeWithResult(sourceIp, checkDir_command);\n            if (checkDirResult.isSuccess()) {\n                logger.info(marker, \"sourceMachine ip :{} check dir env command:{} , result:{}\", sourceIp, checkDir_command, checkDirResult.getResult());\n                if (checkDirResult.getResult() != null && checkDirResult.getResult().indexOf(\"0 0 0\") > -1) {\n                    logger.error(marker, \"sourceMachine ip :{} check dir not exist result:{}\", sourceIp, checkDirResult.getResult());\n                    return TaskFlowStatusEnum.ABORT;\n                }\n            } else {\n                logger.error(marker, \"sourceMachine ip :{} check dir env error message:{}\", sourceIp, checkDirResult.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        } catch (SSHException e) {\n            logger.error(marker, \"sourceMachine ip :{} check env error message:{}\", sourceIp, e.getMessage());\n            e.printStackTrace();\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.目标主机检查:\n     * 3.1. 机器连通性\n     * 3.2. cachecloud用户是否存在\n     * 3.3. 检测容器上是否有redis进程\n     * 3.4. 同步目录存在且无redis进程 删除数据目录\n     */\n    public TaskFlowStatusEnum checkTargetMachineEnv() {\n\n        String checkUser_command = \"cat /etc/passwd | grep cachecloud \";\n        String checkRedis_command = \"ps -ef | grep redis | grep -v \\\"grep redis\\\" | wc -l \";\n        String delDir_command = \"rm -rf \" + baseConfDir.concat(containerIp) + \" \" + baseDataDir.concat(containerIp) + \" \" + baseLogDir.concat(containerIp);\n\n        try {\n            // 等待镜像启动\n            TimeUnit.SECONDS.sleep(30);\n\n            // 1.检查用户是否存在\n            Result checkResult = sshService.executeWithResult(targetIp, checkUser_command);\n            if (checkResult.isSuccess() && checkResult.getResult().indexOf(\"cachecloud\") > -1) {\n                logger.info(marker, \"targetMachine ip :{} check user:{} , result:{}\", targetIp, checkUser_command, checkResult.getResult());\n            } else {\n                logger.error(marker, \"targetMachine ip :{} check user:{} , error message:{}\", targetIp, checkUser_command, checkResult.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n            // 2.检测容器是否还有redis进程\n            Result checkRedisResult = sshService.executeWithResult(containerIp, checkRedis_command);\n            if (checkRedisResult.isSuccess() && checkRedisResult.getResult().equals(\"0\")) {\n                logger.info(marker, \"container ip :{} check redis:{} size:{} is not exist \", containerIp, checkRedis_command, checkRedisResult.getResult());\n            } else {\n                logger.error(marker, \"container ip :{} check redis:{} size:{} , message:{}\", containerIp, checkRedis_command, checkRedisResult.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n            // 3.清除目标主机的目录数据(防止冲突)\n            Result delResult = sshService.executeWithResult(targetIp, delDir_command);\n            if (delResult.isSuccess()) {\n                logger.info(marker, \"targetMachine ip :{} , del success command:{} \", targetIp, delDir_command, delResult.getResult());\n            } else {\n                logger.error(marker, \"targetMachine ip :{} , del command:{}, error message:{}\", targetIp, delDir_command, delResult.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } catch (SSHException e) {\n            logger.error(marker, \"sourceMachine ip :{} check env error message:{}\", sourceIp, e.getMessage(),e);\n            return TaskFlowStatusEnum.ABORT;\n        } catch (InterruptedException e) {\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 4.持久化数据、配置目录同步任务\n     */\n    public TaskFlowStatusEnum startSyncData() {\n\n        /*String sync_command = \" sshpass -p \" + ConstUtils.PASSWORD + \" scp -oStrictHostKeyChecking=no -r \" + baseConfDir.concat(containerIp) + \" \" + ConstUtils.USERNAME + \"@\" + targetIp + \":\" + baseConfDir +\n                \" && sshpass -p \" + ConstUtils.PASSWORD + \" scp -oStrictHostKeyChecking=no -r \" + baseDataDir.concat(containerIp) + \" \" + ConstUtils.USERNAME + \"@\" + targetIp + \":\" + baseDataDir +\n                \" && sshpass -p \" + ConstUtils.PASSWORD + \" scp -oStrictHostKeyChecking=no -r \" + baseLogDir.concat(containerIp) + \" \" + ConstUtils.USERNAME + \"@\" + targetIp + \":\" + baseLogDir;\n        */\n        String sync_command = \" scp -i \"+ConstUtils.DEFAULT_PUBLIC_KEY_PEM+\" -oStrictHostKeyChecking=no -r \" + baseConfDir.concat(containerIp) + \" \" + ConstUtils.USERNAME + \"@\" + targetIp + \":\" + baseConfDir +\n                \" && scp -i \"+ConstUtils.DEFAULT_PUBLIC_KEY_PEM+\"  -oStrictHostKeyChecking=no -r \" + baseDataDir.concat(containerIp) + \" \" + ConstUtils.USERNAME + \"@\" + targetIp + \":\" + baseDataDir +\n                \" && scp -i \"+ConstUtils.DEFAULT_PUBLIC_KEY_PEM+\"  -oStrictHostKeyChecking=no -r \" + baseLogDir.concat(containerIp) + \" \" + ConstUtils.USERNAME + \"@\" + targetIp + \":\" + baseLogDir;\n        logger.info(marker, \"source ip :{} ,execute command :{}\", sourceIp, sync_command);\n        Result syncResult = null;\n        try {\n            long start = System.currentTimeMillis();\n            syncResult = sshService.executeWithResult(sourceIp, sync_command, SYNC_DATA_TIMEOUT);\n            if (syncResult.isSuccess() || syncResult.getResult().indexOf(\"Permission denied\") == -1) {\n                logger.info(marker, \"sync data result:{} costTime:{} s\", syncResult.getResult(), (System.currentTimeMillis() - start) / 1000);\n            } else {\n                logger.error(marker, \"sync data error message:{}\", syncResult.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } catch (SSHException e) {\n            logger.error(marker, \"sync data error message:{} {}\", syncResult,e.getMessage(),e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 5.同步数据目录文件md5校验\n     */\n    public TaskFlowStatusEnum checkDirMd5() {\n\n//        String sourceShell = \"find \" + baseConfDir.concat(containerIp) + \" \" + baseDataDir.concat(containerIp) + \" \" + baseLogDir.concat(containerIp) + \" -type f -exec md5sum {} \\\\; | sort -k 2\";\n//        String targetShell = \"find \" + baseConfDir.concat(containerIp) + \" \" + baseDataDir.concat(containerIp) + \" \" + baseLogDir.concat(containerIp) + \" -type f -exec md5sum {} \\\\; | sort -k 2\";\n        String sourceShell = \"find \" + baseConfDir.concat(containerIp) + \"/*.conf \" + baseDataDir.concat(containerIp) + \"/*.conf -type f -exec md5sum {} \\\\; | sort -k 2\";\n        String targetShell = \"find \" + baseConfDir.concat(containerIp) + \"/*.conf \" + baseDataDir.concat(containerIp) + \"/*.conf -type f -exec md5sum {} \\\\; | sort -k 2\";\n\n        //1.源宿主机和目标宿主机 目录文件列表的md5值\n        Result sourceMd5Result;\n        Result targetMd5Result;\n        try {\n            sourceMd5Result = sshService.executeWithResult(sourceIp, sourceShell, MD5_CHECK_TIMEOUT);\n            if(sourceMd5Result == null){\n                logger.error(marker, \" source ip:{} validate md5 result is null\", sourceIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            if (sourceMd5Result.isSuccess()) {\n                logger.info(marker, \"source ip:{} validate md5 result: \", sourceIp);\n                for (String sourceMd5 : sourceMd5Result.getResult().split(\"\\n\")) {\n                    logger.info(marker, \"source file md5 {} : \", sourceMd5);\n                }\n            } else {\n                logger.error(marker, \" source ip:{} validate md5 error:{}\", sourceIp, sourceMd5Result.getExcetion());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } catch (SSHException e) {\n            logger.error(marker, \" source ip:{} validate md5 error:{}\", sourceIp, e.getMessage());\n            return TaskFlowStatusEnum.ABORT;\n        }\n        try {\n            targetMd5Result = sshService.executeWithResult(targetIp, targetShell, MD5_CHECK_TIMEOUT);\n            if(targetMd5Result == null){\n                logger.error(marker, \" target ip:{} validate md5 result is null\", targetIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            if (targetMd5Result.isSuccess()) {\n                logger.info(marker, \"target ip:{} validate md5 result:{} \", targetIp, targetMd5Result.getResult());\n                for (String targetMd5 : targetMd5Result.getResult().split(\"\\n\")) {\n                    logger.info(marker, \"target file md5 {} : \", targetMd5);\n                }\n            } else {\n                logger.error(marker, \" target ip:{} validate md5 error:{}\", targetIp, targetMd5Result.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } catch (SSHException e) {\n            logger.error(marker, \" target ip:{} validate md5 error:{}\", sourceIp, e.getMessage());\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        // 2.print md5 compare result\n        if (sourceMd5Result != null && targetMd5Result != null && targetMd5Result.getResult().equals(sourceMd5Result.getResult())) {\n            logger.info(marker, \"===== compare source ip:{} & target ip:{} is equal =====\", sourceIp, targetIp);\n        } else {\n            logger.error(marker, \"===== compare source ip:{} & target ip:{} is diffrent =====\", sourceIp, targetIp);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum backupSourceData() {\n\n        // 1. backup data :  /data/redis/bak/${ip}_${time}/...\n        SimpleDateFormat time = new SimpleDateFormat(\"yyyyMMddHHmm\");\n        String timeFormat = time.format(new Date());\n        // 2. backup command\n        String backup_command = \" mkdir -p \" + backupDir.concat(containerIp + \"_\" + timeFormat) + \" && \" +\n                \" mv \" + baseConfDir.concat(containerIp) + \" \" + backupDir.concat(containerIp + \"_\" + timeFormat).concat(\"/conf\") + \" && \" +\n                \" mv \" + baseDataDir.concat(containerIp) + \" \" + backupDir.concat(containerIp + \"_\" + timeFormat).concat(\"/data\") + \" && \" +\n                \" mv \" + baseLogDir.concat(containerIp) + \" \" + backupDir.concat(containerIp + \"_\" + timeFormat).concat(\"/logs\");\n\n        Result backupResult;\n        try {\n            backupResult = sshService.executeWithResult(sourceIp, backup_command, BACKUP_DATA_TIMEOUT);\n            if (backupResult.isSuccess()) {\n                logger.info(marker, \"source ip:{} backup date success ,result:{} \", sourceIp, backupResult.getResult());\n            } else {\n                logger.error(marker, \" source ip:{} backup date error:{}\", sourceIp, backupResult.getResult());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } catch (SSHException e) {\n            logger.error(marker, \" source ip:{} backup date error:{}\", sourceIp, e.getMessage());\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 7.更新机器同步状态\n     */\n    public TaskFlowStatusEnum syncOver() {\n        // update machine SYNC status\n        try {\n            List<MachineRelation> relationList = machineRelationDao.getUnSyncRelationList(containerIp, sourceIp);\n            if (!CollectionUtils.isEmpty(relationList)) {\n                for (MachineRelation machineRelation : relationList) {\n                    machineRelationDao.updateMachineSyncStatus(machineRelation.getId(), MachineTaskEnum.SYNCED.getValue());\n                    logger.info(marker, \"update machine realtion id:{}, ip:{},realIp:{} sync status success !\", machineRelation.getId(), sourceIp, containerIp);\n                }\n            }\n        } catch (Exception e) {\n            logger.error(marker, \"update machine relation  \", sourceIp, targetIp);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 执行一个ls命令，确认ssh以及不是只读盘\n     *\n     * @return\n     */\n    protected boolean checkMachineIsConnect(String ip) {\n        try {\n            Result result = sshService.executeWithResult(ip, \"ls / | wc -l\");\n            if (result.isSuccess()) {\n                return true;\n            }\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage());\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/OffLineAppTask.java",
    "content": "package com.sohu.cache.task.tasks;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.AppStatusEnum;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.util.AppEmailUtil;\nimport org.apache.commons.collections.MapUtils;\nimport org.assertj.core.util.Lists;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * Created by rucao on 2019/11/13\n */\n@Component(\"OffLineAppTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class OffLineAppTask extends BaseTask {\n    private long appId;\n    private AppDesc appDesc;\n    private AppUser userInfo;\n    private long auditId;\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private InstanceDao instanceDao;\n    @Resource(name = \"appEmailUtil\")\n    private AppEmailUtil appEmailUtil;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = Lists.newArrayList();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 2. 执行应用下线处理\n        taskStepList.add(\"executeOffLineApp\");\n        // 3. 更新应用信息\n        taskStepList.add(\"updateAppStatus\");\n\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        TaskFlowStatusEnum taskFlowStatusEnum = super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            taskFlowStatusEnum = TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY, -1);\n        if (auditId <= 0) {\n            logger.info(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n        }\n\n        appDesc = appService.getByAppId(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"task {} appId {} appDesc is not exist\", taskId, appId);\n            taskFlowStatusEnum = TaskFlowStatusEnum.ABORT;\n        }\n        Object userObject = MapUtils.getObject(paramMap, TaskConstants.USER_INFO_KEY, null);\n        if (userObject instanceof JSONObject) {\n            JSONObject userJson = (JSONObject) userObject;\n            userInfo = JSONObject.parseObject(JSON.toJSONString(userJson), AppUser.class);\n        }\n        if (userInfo == null) {\n            logger.error(marker, \"task {} appId {} userInfo is not exist\", taskId, appId);\n            taskFlowStatusEnum = TaskFlowStatusEnum.ABORT;\n        } else {\n            if (!ConstUtils.SUPER_MANAGER.contains(userInfo.getName())) {\n                logger.error(\"task {} appId {} user {} who hope to offline hasn't privilege\", taskId, appId, userInfo.getName());\n                taskFlowStatusEnum = TaskFlowStatusEnum.ABORT;\n            }\n        }\n        if (taskFlowStatusEnum == TaskFlowStatusEnum.ABORT) {\n            appEmailUtil.noticeOfflineApp(userInfo, appId, false);\n        }\n        return taskFlowStatusEnum;\n    }\n\n    /**\n     * 2. 执行应用下线操作\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum executeOffLineApp() {\n        logger.info(\"executeOffLineApp\");\n        if (auditId > 0) {\n            appAuditDao.updateAppAuditUser(auditId, 2, userInfo.getId());\n        }\n        List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n        int type = appDesc.getType();\n        List<Boolean> isShutDownList = Lists.newArrayList();\n        if (instanceInfos != null) {\n            isShutDownList = instanceInfos.parallelStream()\n                    .filter(instanceInfo -> !instanceInfo.isOffline())\n                    .map(instanceInfo -> instanceOffline(instanceInfo, type))\n                    .collect(Collectors.toList());\n        }\n        if (isShutDownList.contains(false)) {\n            appEmailUtil.noticeOfflineApp(userInfo, appId, false);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    private boolean instanceOffline(InstanceInfo instanceInfo, int type) {\n        final String ip = instanceInfo.getIp();\n        final int port = instanceInfo.getPort();\n        boolean isShutdown = TypeUtil.isRedisType(type) ? redisCenter.shutdown(appId, ip, port) : true;\n        if(isShutdown){\n            isShutdown = redisCenter.checkShutdownSuccess(instanceInfo);\n        }\n        if (isShutdown) {\n            instanceInfo.setStatus(InstanceStatusEnum.OFFLINE_STATUS.getStatus());\n            instanceDao.update(instanceInfo);\n        } else {\n            logger.error(\"task {} appId {} {}:{} redis not shutdown!\", taskId, appId, ip, port);\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 3. 更新应用信息，发送邮件\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateAppStatus() {\n        logger.info(\"updateAppStatus\");\n        appDesc.setStatus(AppStatusEnum.STATUS_OFFLINE.getStatus());\n        if (auditId > 0) {\n            appAuditDao.updateAppAudit(auditId, 1);\n        }\n        int count = appService.update(appDesc);\n        if (count > 0) {\n            appEmailUtil.noticeOfflineApp(userInfo, appId, true);\n            return TaskFlowStatusEnum.SUCCESS;\n        } else {\n            appEmailUtil.noticeOfflineApp(userInfo, appId, false);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisClusterAppDeployTask.java",
    "content": "package com.sohu.cache.task.tasks;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.constant.RedisConstant;\nimport com.sohu.cache.redis.util.JedisUtil;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceStatusEnum;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.util.EnvUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\n\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * <p>\n * Description: 部署redis-cluster任务\n * </p>\n *\n * @author chenshi\n * @version 1.0\n * @date 2019/1/9\n */\n@Component(\"RedisClusterAppDeployTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisClusterAppDeployTask extends BaseTask {\n\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 审核id\n     */\n    private long auditId;\n\n    /**\n     * redis server部署实例列表\n     */\n    private List<String> appDeployInfoList;\n\n    /**\n     * redis server机器列表\n     */\n    private List<String> redisServerMachineList;\n\n    /**\n     * 每台机器redis server实例个数\n     */\n    private int masterPerMachine;\n\n    /**\n     * 每个实例的最大内存(MB)\n     */\n    private int maxMemory;\n\n    private List<RedisServerNode> redisServerNodes;\n\n    /**\n     * Redis版本\n     */\n    private String version;\n\n    /**\n     * 集群的节点信息\n     */\n    private Map<Jedis, Jedis> clusterMap = new LinkedHashMap<Jedis, Jedis>();\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        //1. 参数初始化\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        //2. 检查资源\n        taskStepList.add(\"checkResourceAllow\");\n        //3. 检查机器可用性\n        taskStepList.add(\"checkMachineConnect\");\n        //4. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateTrue\");\n        //5. 获取实例列表\n        taskStepList.add(\"generateInstanceNodes\");\n        //6. 保存实例列表\n        taskStepList.add(\"saveInstanceNodes\");\n        //7. 创建redis server job\n        taskStepList.add(\"createRedisServerTask\");\n        //8. 等待redis server job完成\n        taskStepList.add(\"waitRedisServerFinish\");\n        /*\n            9. 设置密码已放到实例配置文件中，启动后自动带有密码，\n            此步骤是设置app_desc表的密码字段，避免后续访问操作失败\n         */\n        taskStepList.add(\"setPasswd\");\n        //10. 创建redis cluster job\n        taskStepList.add(\"startRedisCluster\");\n        //11. 等待redis cluster job完成\n        taskStepList.add(\"waitRedisClusterFinish\");\n        //12. 更改实例状态\n        taskStepList.add(\"updateInstanceStatus\");\n        //13. 开始收集部署\n        taskStepList.add(\"deployCollection\");\n        //14. rdb\n        taskStepList.add(\"checkFullSync\");\n        //14. 审核\n        taskStepList.add(\"updateAudit\");\n        //15. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateFalse\");\n        return taskStepList;\n    }\n\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //审核id\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n//        if (auditId <= 0) {\n//            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n//            return TaskFlowStatusEnum.ABORT;\n//        }\n\n        //maxMemory\n        maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY);\n        if (maxMemory <= 0 || maxMemory > TaskConstants.MAX_MEMORY_LIMIT) {\n            logger.error(marker, \"task {} maxMemory {} is wrong\", taskId, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //masterPerMachine\n        masterPerMachine = MapUtils.getIntValue(paramMap, TaskConstants.MASTER_PER_MACHINE_KEY);\n        if (masterPerMachine < 0 || masterPerMachine > TaskConstants.MAX_MASTER_PER_MACHINE) {\n            logger.error(marker, \"task {} masterPerMachine {} is wrong\", taskId, masterPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n       //appDeployInfo\n        String appDeployInfoStr = MapUtils.getString(paramMap, TaskConstants.APP_DEPLOY_INFO_LIST_KEY);\n        appDeployInfoList = JSONArray.parseArray(appDeployInfoStr, String.class);\n        if (CollectionUtils.isEmpty(appDeployInfoList)) {\n            logger.error(marker, \"task {} appDeployInfoList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server machine\n        String redisServerMachineStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY);\n        redisServerMachineList = JSONArray.parseArray(redisServerMachineStr, String.class);\n        if (CollectionUtils.isEmpty(redisServerMachineList)) {\n            logger.error(marker, \"task {} redisServerMachineList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n            // init cluster map\n            if (MapUtils.isEmpty(clusterMap)) {\n                setClusterMap(redisServerNodes);\n                logger.info(marker, \"init cluster map :{} successfully\", clusterMap);\n                printClusterMap(clusterMap);\n            }\n        }\n        // redis版本\n        version = MapUtils.getString(paramMap, TaskConstants.VERSION_KEY);\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 检查资源是否充足\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkResourceAllow() {\n        if (EnvUtil.isLocal(environment)) {\n            return TaskFlowStatusEnum.SUCCESS;\n        }\n        long memoryNeed = 0L;\n        if (masterPerMachine == 0) {\n            memoryNeed = maxMemory;\n        } else {\n            memoryNeed = Long.valueOf(masterPerMachine) * maxMemory;\n        }\n        return checkResourceAllow(redisServerMachineList, memoryNeed);\n    }\n\n    public TaskFlowStatusEnum checkMachineConnect() {\n        // redis server\n        return checkMachineConnect(redisServerMachineList, \"cluster: redisServer {} is not connected\");\n    }\n\n    /**\n     * 生成实例\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum generateInstanceNodes() {\n\n        redisServerNodes = instancePortService.generateRedisServerNodeListWithDeployInfo(appId, appDeployInfoList, masterPerMachine, maxMemory);\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty, appId is {}, redisServerMachineList is {}, masterPerMachine is {}, maxMemory is {}\",\n                    appId, redisServerMachineList, masterPerMachine, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //设置环境变量\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 保存实例\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum saveInstanceNodes() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty\");\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            Integer instanceId = saveInstance(appId, redisServerNode.getIp(), redisServerNode.getPort(), maxMemory,\n                    InstanceTypeEnum.REDIS_CLUSTER, InstanceStatusEnum.NEW_STATUS, \"\");\n            if (instanceId == null || instanceId.equals(0)) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 创建任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum createRedisServerTask() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"{} redisServerNodes is emtpy\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addRedisServerInstallTask(appId, host, port, redisServerNode.getMaxmemory(), version, true, taskId);\n                if (childTaskId <= 0) {\n                    logger.error(marker, \"{} {} {} redis server childTaskId is {}\", appId, host, port, childTaskId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"{}:{} redis server task created successfully\", host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"{}:{} redis server task created failly\", host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 等待任务完成\n     */\n    public TaskFlowStatusEnum waitRedisServerFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            long childTaskId = redisServerNode.getTaskId();\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_INSTALL_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"{}:{} redis server task fail\", host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{}:{} redis server task finish successfully\", host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 创建redis cluster任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum startRedisCluster() {\n        // 1.param验证\n        if (MapUtils.isEmpty(clusterMap)) {\n            setClusterMap(redisServerNodes);\n            printClusterMap(clusterMap);\n        }\n        // 2.构建集群\n        boolean isCluster = redisDeployCenter.startCluster(appId, clusterMap);\n        if (!isCluster) {\n            logger.error(marker, \"create redis Cluster error :{}!\", clusterMap);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 确认集群redis cluster构建是否成功\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitRedisClusterFinish() {\n        Boolean clusterOk = false;\n        while (!clusterOk) {\n            boolean isOk = true;\n            for (Map.Entry<Jedis, Jedis> node : clusterMap.entrySet()) {\n                Jedis jedis = null;\n                try {\n                    jedis = node.getKey();\n                    String clusterInfo = jedis.clusterInfo();\n                    if (!clusterInfo.split(\"\\n\")[0].contains(\"ok\")) {\n                        isOk = false;\n                        break;\n                    }\n                    //  验证实例的集群是否ok\n                    if (isOk) {\n                        clusterOk = true;\n                    } else {\n                        logger.error(marker, \" app :{} is not cluster , clusterInfo :{}\", appId, clusterInfo);\n                        return TaskFlowStatusEnum.ABORT;\n                    }\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                } finally {\n                    if (jedis != null) {\n                        jedis.close();\n                    }\n                }\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 更新实例状态\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateInstanceStatus() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.GOOD_STATUS.getStatus());\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 部署实例收集\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum deployCollection() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            boolean isDeploy = redisCenter.sendDeployRedisRelateCollectionMsg(appId, host, port);\n            if (!isDeploy) {\n                logger.error(marker, \"{} {}:{} deploy fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{} {}:{} deploy sucessfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 设置密码\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum setPasswd() {\n        try {\n            if (appId > 0) {\n                // 设置密码\n                appService.updateAppPwd(appId, String.valueOf(appId));\n                // 密码校验逻辑\n                boolean checkFlag = redisDeployCenter.checkAuths(appId);\n                logger.info(marker, \"check app clutser passwd:{}\", checkFlag);\n                if (!checkFlag) {\n                    logger.error(marker, \"check app clutser passwd error, appId:{}!\", appId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkFullSync(){\n        Map<RedisServerNode, Boolean> fullSyncSet = new HashMap<>();\n        Map<RedisServerNode, Boolean> slaveOnLoadingSet = new HashMap<>();\n        // 耗时\n        int totalSeconds = 0;\n        redisServerNodes.stream().filter(redisServerNode -> StringUtils.isBlank(redisServerNode.getMasterHost()) && redisServerNode.getMasterPort() == 0)\n                .forEach(redisServerNode -> fullSyncSet.put(redisServerNode, false));\n        redisServerNodes.stream().filter(redisServerNode -> StringUtils.isNotBlank(redisServerNode.getMasterHost()) && redisServerNode.getMasterPort() != 0)\n                .forEach(redisServerNode -> slaveOnLoadingSet.put(redisServerNode, false));\n        while (true) {\n            // 简单的计时器\n            long startTime = System.currentTimeMillis();\n            boolean syncFlag = true;\n            for (Map.Entry<RedisServerNode, Boolean> entry : fullSyncSet.entrySet()) {\n                if(entry.getValue().equals(false)){\n                    syncFlag = false;\n                    Jedis jedis = null;\n                    try {\n                        jedis = redisCenter.getAdminJedis(appId, entry.getKey().getIp(), entry.getKey().getPort());\n                        String statsInfo = jedis.info(RedisConstant.Stats.getValue());\n                        if(StringUtils.isNotBlank(statsInfo)){\n                            String[] split = statsInfo.split(\"\\r\\n\");\n                            for (String str : split){\n                                if(StringUtils.isNotBlank(str) && str.contains(\"sync_full:\")){\n                                    String[] pair = StringUtils.splitByWholeSeparator(str, \":\");\n                                    Long aLong = Long.valueOf(pair[1]);\n                                    if(aLong > 0){\n                                        fullSyncSet.put(entry.getKey(), true);\n                                        break;\n                                    }\n                                }\n                            }\n                        }\n                    }catch (Exception e){\n                        logger.error(marker, \"check full sync, appId:{}, error:{}!\", appId, e.getMessage());\n                    }finally {\n                        if (jedis != null) {\n                            jedis.close();\n                        }\n                    }\n                }else{\n                    continue;\n                }\n            }\n            if(syncFlag){\n                break;\n            }\n            try {\n                TimeUnit.SECONDS.sleep(1);\n            } catch (InterruptedException e) {\n                logger.error(marker, e.getMessage(), e);\n            }\n            // 简单的计时器(秒)\n            long costTime = System.currentTimeMillis() - startTime;\n            totalSeconds += (costTime / 1000);\n            if (totalSeconds > TaskConstants.REDIS_SERVER_INSTALL_TIMEOUT) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n\n        try {\n            TimeUnit.SECONDS.sleep(20);\n        } catch (InterruptedException e) {\n            logger.error(marker, \"check full sync, appId:{}, error:{}!\", appId, e.getMessage());\n            logger.error(marker, e.getMessage());\n        }\n        while (true) {\n            // 简单的计时器\n            long startTime = System.currentTimeMillis();\n            boolean loadingFlag = true;\n            for (Map.Entry<RedisServerNode, Boolean> entry : slaveOnLoadingSet.entrySet()) {\n                if (entry.getValue().equals(false)) {\n                    loadingFlag = false;\n                    Jedis jedis = null;\n                    try {\n                        jedis = redisCenter.getAdminJedis(appId, entry.getKey().getIp(), entry.getKey().getPort());\n                        String statsInfo = jedis.info(RedisConstant.Replication.getValue());\n                        if (StringUtils.isNotBlank(statsInfo)) {\n                            String[] split = statsInfo.split(\"\\r\\n\");\n                            for (String str : split) {\n                                if (StringUtils.isNotBlank(str) && str.contains(\"master_sync_in_progress:\")) {\n                                    String[] pair = StringUtils.splitByWholeSeparator(str, \":\");\n                                    Long aLong = Long.valueOf(pair[1]);\n                                    if (aLong == 0) {\n                                        slaveOnLoadingSet.put(entry.getKey(), true);\n                                        break;\n                                    }\n                                }\n                            }\n                        }\n                    } catch (Exception e) {\n                        logger.error(marker, \"check not on sync progress, appId:{}, error:{}!\", appId, e.getMessage());\n                    } finally {\n                        if (jedis != null) {\n                            jedis.close();\n                        }\n                    }\n                } else {\n                    continue;\n                }\n            }\n            if(loadingFlag){\n                break;\n            }\n            try {\n                TimeUnit.SECONDS.sleep(1);\n            } catch (InterruptedException e) {\n                logger.error(marker, e.getMessage(), e);\n            }\n            // 简单的计时器(秒)\n            long costTime = System.currentTimeMillis() - startTime;\n            totalSeconds += (costTime / 1000);\n            if (totalSeconds > TaskConstants.REDIS_SERVER_INSTALL_TIMEOUT) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 通过初审：资源分配\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            if (auditId > 0){\n                appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n            }\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateFalse() {\n        return updateMachineAllocateStatus(0);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateTrue() {\n        return updateMachineAllocateStatus(1);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateStatus(int status) {\n        try {\n            Set<String> allMachineSet = new HashSet<String>();\n            allMachineSet.addAll(redisServerMachineList);\n            for (String ip : allMachineSet) {\n                machineDao.updateMachineAllocate(ip, status);\n            }\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n    /**\n     * 组装 clusterMap\n     *\n     * @param redisServerNodes\n     */\n    public void setClusterMap(List<RedisServerNode> redisServerNodes) {\n        if (!CollectionUtils.isEmpty(redisServerNodes)) {\n            for (RedisServerNode redisServerNode : redisServerNodes) {\n                String masterHost = redisServerNode.getMasterHost();\n                int masterPort = redisServerNode.getMasterPort();\n                if (masterHost == null || masterPort <= 0) {\n                    continue;\n                }\n                String slaveHost = redisServerNode.getIp();\n                int slavePort = redisServerNode.getPort();\n\n                if (StringUtils.isNotBlank(slaveHost)) {\n                    Jedis masterJedis = redisCenter.getJedis(appId, masterHost, masterPort);\n                    Jedis slaveJedis = redisCenter.getJedis(appId, slaveHost, slavePort);\n                    clusterMap.put(masterJedis, slaveJedis);\n                } else {\n                    clusterMap.put(redisCenter.getJedis(appId, masterHost, masterPort), null);\n                }\n            }\n        }\n    }\n\n    /**\n     * 输出集群节点关系\n     */\n    public void printClusterMap(Map<Jedis, Jedis> clusterMap) {\n        if (!MapUtils.isEmpty(clusterMap)) {\n            for (Map.Entry<Jedis, Jedis> msNode : clusterMap.entrySet()) {\n                Jedis masterNode = msNode.getKey();\n                Jedis slaveNode = msNode.getValue();\n                String masterInfo = masterNode != null ? JedisUtil.getHostPort(masterNode) : null;\n                String slaveInfo = slaveNode != null ? JedisUtil.getHostPort(slaveNode) : null;\n\n                logger.info(marker, \"master :{} -> slave :{}\", masterInfo, slaveInfo);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisSentinelAppDeployTask.java",
    "content": "package com.sohu.cache.task.tasks;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.entity.MachineStats;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceStatusEnum;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisSentinelNode;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.util.EnvUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * <p>\n * Description: Redis Sentinel应用部署任务流\n * </p>\n *\n * @author chenshi\n * @version 1.0\n * @date 2019/1/10\n */\n@Component(\"RedisSentinelAppDeployTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisSentinelAppDeployTask extends BaseTask {\n\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 审核id\n     */\n    private long auditId;\n\n    /**\n     * redis server机器列表\n     */\n    private List<String> redisServerMachineList;\n\n    /**\n     * redis sentinel机器列表\n     */\n    private List<String> redisSentinelMachineList;\n\n    /**\n     * 每台机器redis server实例个数\n     */\n    private int masterPerMachine;\n\n    /**\n     * 每台机器sentinel实例个数\n     */\n    private int sentinelPerMachine;\n\n    /**\n     * 每个实例的最大内存(MB)\n     */\n    private int maxMemory;\n\n    private List<RedisServerNode> redisServerNodes;\n\n    private List<RedisSentinelNode> redisSentinelNodes;\n\n    private String version;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        //1. 参数初始化\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        //2. 检查资源\n        taskStepList.add(\"checkResourceAllow\");\n        //3. 检查机器可用性\n        taskStepList.add(\"checkMachineConnect\");\n        //4. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateTrue\");\n        //5. 获取实例列表\n        taskStepList.add(\"generateInstanceNodes\");\n        //6. 保存实例列表\n        taskStepList.add(\"saveRedisInstanceNodes\");\n        taskStepList.add(\"saveSentinelInstanceNodes\");\n        //7. 创建redis server job\n        taskStepList.add(\"createRedisServerTask\");\n        //8. 等待redis server job完成\n        taskStepList.add(\"waitRedisServerFinish\");\n        /*\n            9. 设置密码已放到实例配置文件中，启动后自动带有密码，\n            此步骤是设置app_desc表的密码字段，避免后续访问操作失败\n         */\n        taskStepList.add(\"setPasswd\");\n        //10. 更改实例状态\n        taskStepList.add(\"updateInstanceStatus\");\n        //11. 主从复制\n        taskStepList.add(\"configReplication\");\n        //12. 创建redis sentinel job\n        taskStepList.add(\"createRedisSentinelTask\");\n        //13. 等待redis sentinel job完成\n        taskStepList.add(\"waitRedisSentinelFinish\");\n        //14. 更改实例状态\n        taskStepList.add(\"updateSentinelInstanceStatus\");\n        //15. 开始收集部署\n        taskStepList.add(\"deployCollection\");\n        //16. 重置sentinel实例状态\n        taskStepList.add(\"sentinelReset\");\n        //17. 审核\n        taskStepList.add(\"updateAudit\");\n        //18. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateFalse\");\n        return taskStepList;\n    }\n\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //审核id\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n//        if (auditId <= 0) {\n//            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n//            return TaskFlowStatusEnum.ABORT;\n//        }\n\n        //maxMemory\n        maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY);\n        if (maxMemory <= 0 || maxMemory > TaskConstants.MAX_MEMORY_LIMIT) {\n            logger.error(marker, \"task {} maxMemory {} is wrong\", taskId, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //masterPerMachine\n        masterPerMachine = MapUtils.getIntValue(paramMap, TaskConstants.MASTER_PER_MACHINE_KEY);\n        if (masterPerMachine <= 0 || masterPerMachine > TaskConstants.MAX_MASTER_PER_MACHINE) {\n            logger.error(marker, \"task {} masterPerMachine {} is wrong\", taskId, masterPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //sentinelPerMachine\n        sentinelPerMachine = MapUtils.getIntValue(paramMap, TaskConstants.SENTINEL_PER_MACHINE_KEY);\n        if (sentinelPerMachine <= 0) {\n            logger.error(marker, \"task {} sentinelPerMachine {} is wrong\", taskId, sentinelPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server machine\n        String redisServerMachineStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY);\n        redisServerMachineList = JSONArray.parseArray(redisServerMachineStr, String.class);\n        if (CollectionUtils.isEmpty(redisServerMachineList)) {\n            logger.error(marker, \"task {} redisServerMachineList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n\n        //redis sentinel machine\n        String redisSentinelMachineStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SENTINEL_MACHINE_LIST_KEY);\n        redisSentinelMachineList = JSONArray.parseArray(redisSentinelMachineStr, String.class);\n        if (CollectionUtils.isEmpty(redisSentinelMachineList)) {\n            logger.error(marker, \"task {} redisSentinelMachineList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis sentinel list\n        String redisSentinelNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SENTINEL_NODES_KEY);\n        if (StringUtils.isNotBlank(redisSentinelNodesStr)) {\n            redisSentinelNodes = JSONArray.parseArray(redisSentinelNodesStr, RedisSentinelNode.class);\n            if (CollectionUtils.isEmpty(redisSentinelNodes)) {\n                logger.error(marker, \"task {} redisSentinelNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        version = MapUtils.getString(paramMap, TaskConstants.VERSION_KEY);\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 检查资源是否充足\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkResourceAllow() {\n        if (EnvUtil.isLocal(environment)) {\n            return TaskFlowStatusEnum.SUCCESS;\n        }\n        // 容量和代理\n        long memoryNeed = Long.valueOf(masterPerMachine) * maxMemory;\n        TaskFlowStatusEnum taskFlowStatusEnum = checkResourceAllow(redisServerMachineList, memoryNeed);\n        if(taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)){\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // sentinel\n        for (String redisSentinelIp : redisSentinelMachineList) {\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(redisSentinelIp);\n            if (machineStats == null) {\n                logger.error(marker, \"{} redis sentinel machineStats is null\", redisSentinelIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(redisSentinelIp);\n            if (machineInfo == null || machineInfo.getIsAllocating() == 1) {\n                logger.error(marker, \"redis sentinel machine {} allocating is 1\", redisSentinelIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            if (!checkMachineStatIsUpdate(machineStats)) {\n                logger.error(marker, \"redis sentinel machine stats {} update_time is {}, may be not updated recently\", machineInfo.getIp(), machineStats.getUpdateTimeFormat());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    public TaskFlowStatusEnum checkMachineConnect() {\n        // redis server\n        TaskFlowStatusEnum taskFlowStatusEnum = checkMachineConnect(redisServerMachineList, \"sentinelredisServer {} is not connected\");\n        if(taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)){\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // redis sentinel\n        return checkMachineConnect(redisSentinelMachineList, \"redisSentinel {} is not connected\");\n    }\n\n    /**\n     * 生成实例\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum generateInstanceNodes() {\n\n        redisServerNodes = instancePortService.generateRedisServerNodeList(appId, redisServerMachineList, masterPerMachine, maxMemory);\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty, appId is {}, redisServerMachineList is {}, masterPerMachine is {}, maxMemory is {}\",\n                    appId, redisServerMachineList, masterPerMachine, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        //只取一对\n        redisServerNodes = redisServerNodes.subList(0, 2);\n\n        redisSentinelNodes = instancePortService.generateRedisSentinelNodeList(appId, redisSentinelMachineList, sentinelPerMachine);\n        if (CollectionUtils.isEmpty(redisSentinelNodes)) {\n            logger.warn(marker, \"redisSentinelNodes is empty, appId is {}, redisSentinelMachineList is {}, masterPerMachine is {}\",\n                    appId, redisSentinelMachineList, sentinelPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //设置环境变量\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_NODES_KEY, redisSentinelNodes);\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 保存redis实例信息\n     */\n    public TaskFlowStatusEnum saveRedisInstanceNodes() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty\");\n            return TaskFlowStatusEnum.ABORT;\n        }\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            Integer instanceId = saveInstance(appId, redisServerNode.getIp(), redisServerNode.getPort(), maxMemory,\n                    InstanceTypeEnum.REDIS_SERVER, InstanceStatusEnum.NEW_STATUS, \"\");\n            if (instanceId == null || instanceId.equals(0)) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 保存sentinel实例信息\n     */\n    public TaskFlowStatusEnum saveSentinelInstanceNodes() {\n        if (CollectionUtils.isEmpty(redisSentinelNodes)) {\n            logger.warn(marker, \"redisSentinelNodes is empty\");\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // 获取sentinel masterName\n        String masterName = \"\";\n        List<RedisServerNode> masterRedisServerNodes = getMasterRedisServerNodes();\n        if (masterRedisServerNodes.size() == 1) {\n            masterName = masterRedisServerNodes.get(0).getMasterName();\n            logger.info(\"sentinel masterName :\" + masterName);\n        } else {\n            logger.error(marker, \"appId {} masterRedisServerNodes {} is empty\", appId, masterRedisServerNodes);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // 保存sentinel实例信息\n        if (!StringUtils.isEmpty(masterName)) {\n            for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n                Integer instanceId = saveInstance(appId, redisSentinelNode.getIp(), redisSentinelNode.getPort(), 0,\n                        InstanceTypeEnum.REDIS_SENTINEL, InstanceStatusEnum.NEW_STATUS, masterName);\n                if (instanceId == null || instanceId.equals(0)) {\n                    return TaskFlowStatusEnum.ABORT;\n                }\n            }\n        } else {\n            logger.error(marker, \"appId {} masterName {} is empty\", appId, masterName);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 创建任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum createRedisServerTask() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"{} redisServerNodes is emtpy\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addRedisServerInstallTask(appId, host, port, redisServerNode.getMaxmemory(), version, false, taskId);\n                if (childTaskId <= 0) {\n                    logger.error(marker, \"{} {} {} redis server childTaskId is {}\", appId, host, port, childTaskId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"{}:{} redis server task created successfully\", host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"{}:{} redis server task created failly\", host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 等待任务完成\n     *\n     * @return\n     * @TODO 超时时间\n     */\n    public TaskFlowStatusEnum waitRedisServerFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            long childTaskId = redisServerNode.getTaskId();\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_INSTALL_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"{}:{} redis server task fail\", host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{}:{} redis server task finish successfully\", host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 建立主从关系\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum configReplication() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String masterHost = redisServerNode.getMasterHost();\n            int masterPort = redisServerNode.getMasterPort();\n            if (masterHost == null || masterPort <= 0) {\n                continue;\n            }\n            String slaveHost = redisServerNode.getIp();\n            int slavePort = redisServerNode.getPort();\n            //幂等\n            boolean isSlaveOf = redisDeployCenter.slaveOf(appId, masterHost, masterPort, slaveHost, slavePort);\n            if (!isSlaveOf) {\n                logger.error(marker, \"{}:{} slaveof {}:{} fail\", slaveHost, slavePort, masterHost, masterPort);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"{}:{} slaveof {}:{} successfully\", slaveHost, slavePort, masterHost, masterPort);\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 创建redis sentinel任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum createRedisSentinelTask() {\n        List<RedisServerNode> masterRedisServerNodes = getMasterRedisServerNodes();\n\n        // 计算quorum\n        int quorum = getQuorum(redisSentinelNodes.size());\n        for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n            String sentinelHost = redisSentinelNode.getIp();\n            int sentinelPort = redisSentinelNode.getPort();\n            try {\n                long childTaskId = taskService.addRedisSentinelInstallTask(appId, sentinelHost, sentinelPort, masterRedisServerNodes, quorum, version, taskId);\n                if (childTaskId <= 0) {\n                    logger.error(marker, \"{} {} {} sentinel childTaskId is {}\", appId, sentinelHost, sentinelPort, childTaskId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n                redisSentinelNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SENTINEL_NODES_KEY, redisSentinelNodes);\n                logger.info(marker, \"{}:{} redis sentinel task created successfully, childTaskId is {}\", sentinelHost, sentinelPort, childTaskId);\n            } catch (Exception e) {\n                logger.error(marker, \"{}:{} redis sentinel task created failly\", sentinelHost, sentinelPort);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 等待所有redis sentinel启动\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitRedisSentinelFinish() {\n        for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n            long childTaskId = redisSentinelNode.getTaskId();\n            String host = redisSentinelNode.getIp();\n            int port = redisSentinelNode.getPort();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SENTINEL_INSTALL_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"{} {}:{} redis sentinel task fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{} {}:{} redis sentinel task success\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 获取所有master节点\n     *\n     * @return\n     */\n    private List<RedisServerNode> getMasterRedisServerNodes() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String appFullName = appDesc.getName();\n        if (StringUtils.isBlank(appFullName)) {\n            logger.error(marker, \"appId {} fullName is empty\", appId);\n            return Collections.emptyList();\n        }\n        List<RedisServerNode> masterRedisServerNodes = new ArrayList<RedisServerNode>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            if (!redisServerNode.isMaster()) {\n                continue;\n            }\n            String masterName = generateMasterName(appFullName, redisServerNode.getPort());\n            redisServerNode.setMasterName(masterName);\n            masterRedisServerNodes.add(redisServerNode);\n        }\n        return masterRedisServerNodes;\n    }\n\n    /**\n     * 更新实例状态\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateInstanceStatus() {\n        redisServerNodes.forEach(redisServerNode ->\n            instanceDao.updateStatus(appId, redisServerNode.getIp(), redisServerNode.getPort(), InstanceStatusEnum.GOOD_STATUS.getStatus())\n        );\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 更新实例状态\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateSentinelInstanceStatus() {\n        redisSentinelNodes.forEach(redisSentinelNode ->\n            instanceDao.updateStatus(appId, redisSentinelNode.getIp(), redisSentinelNode.getPort(), InstanceStatusEnum.GOOD_STATUS.getStatus())\n        );\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 部署实例收集\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum deployCollection() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            boolean isDeploy = redisCenter.sendDeployRedisRelateCollectionMsg(appId, host, port);\n            if (!isDeploy) {\n                logger.error(marker, \"{} {}:{} deploy fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{} {}:{} deploy sucessfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum setPasswd() {\n\n        try {\n            if (appId > 0) {\n                // 设置密码\n                appService.updateAppPwd(appId, String.valueOf(appId));\n                // 密码校验逻辑\n                boolean checkFlag = redisDeployCenter.checkAuths(appId);\n                logger.info(marker, \"check app sentinel passwd:{}\", checkFlag);\n                if (!checkFlag) {\n                    logger.error(marker, \"check app sentinel passwd error, appId:{}!\", appId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 清理sentinel实例状态\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum sentinelReset() {\n\n        try {\n            redisDeployCenter.sentinelReset(appId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 通过初审：资源分配\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            if (auditId > 0) {\n                appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n            }\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateFalse() {\n        return updateMachineAllocateStatus(0);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateTrue() {\n        return updateMachineAllocateStatus(1);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateStatus(int status) {\n        try {\n            Set<String> allMachineSet = new HashSet<String>();\n            allMachineSet.addAll(redisServerMachineList);\n            for (String ip : allMachineSet) {\n                machineDao.updateMachineAllocate(ip, status);\n            }\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisStandaloneAppDeployTask.java",
    "content": "package com.sohu.cache.task.tasks;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceStatusEnum;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.util.EnvUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * <p>\n * Description:RedisStandalone部署任务流\n * </p>\n *\n * @author chenshi\n * @version 1.0\n * @date 2019/1/9\n */\n@Component(\"RedisStandaloneAppDeployTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisStandaloneAppDeployTask extends BaseTask {\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 审核id\n     */\n    private long auditId;\n\n    /**\n     * redis server机器列表\n     */\n    private List<String> redisServerMachineList;\n\n    /**\n     * 每台机器redis server实例个数\n     */\n    private int masterPerMachine;\n\n    /**\n     * 每个实例的最大内存(MB)\n     */\n    private int maxMemory;\n\n    private List<RedisServerNode> redisServerNodes;\n\n    /**\n     * Redis版本\n     */\n    private String version;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        //1. 参数初始化\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        //2. 检查资源\n        taskStepList.add(\"checkResourceAllow\");\n        //3. 检查机器可用性\n        taskStepList.add(\"checkMachineConnect\");\n        //4. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateTrue\");\n        //5. 获取实例列表\n        taskStepList.add(\"generateInstanceNodes\");\n        //6. 保存实例列表\n        taskStepList.add(\"saveInstanceNodes\");\n        //7. 创建redis server job\n        taskStepList.add(\"createRedisServerTask\");\n        //8. 等待redis server job完成\n        taskStepList.add(\"waitRedisServerFinish\");\n        /*\n            9. 设置密码已放到实例配置文件中，启动后自动带有密码，\n            此步骤是设置app_desc表的密码字段，避免后续访问操作失败\n         */\n        taskStepList.add(\"setPasswd\");\n        //10. 更改实例状态\n        taskStepList.add(\"updateInstanceStatus\");\n        //11. 开始收集部署\n        taskStepList.add(\"deployCollection\");\n        //12. 审核\n        taskStepList.add(\"updateAudit\");\n        //13. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateFalse\");\n\n        return taskStepList;\n    }\n\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //审核id\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n//        if (auditId <= 0) {\n//            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n//            return TaskFlowStatusEnum.ABORT;\n//        }\n\n        //maxMemory\n        maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY);\n        if (maxMemory <= 0 || maxMemory > TaskConstants.MAX_MEMORY_LIMIT) {\n            logger.error(marker, \"task {} maxMemory {} is wrong\", taskId, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //masterPerMachine\n        masterPerMachine = MapUtils.getIntValue(paramMap, TaskConstants.MASTER_PER_MACHINE_KEY);\n        if (masterPerMachine <= 0 || masterPerMachine > TaskConstants.MAX_MASTER_PER_MACHINE) {\n            logger.error(marker, \"task {} masterPerMachine {} is wrong\", taskId, masterPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server machine\n        String redisServerMachineStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY);\n        redisServerMachineList = JSONArray.parseArray(redisServerMachineStr, String.class);\n        if (CollectionUtils.isEmpty(redisServerMachineList)) {\n            logger.error(marker, \"task {} redisServerMachineList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"standalone task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        // redis版本\n        version = MapUtils.getString(paramMap, TaskConstants.VERSION_KEY);\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 检查资源是否充足\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkResourceAllow() {\n        if (EnvUtil.isLocal(environment)) {\n            return TaskFlowStatusEnum.SUCCESS;\n        }\n        // 容量和代理\n        long memoryNeed = Long.valueOf(masterPerMachine) * maxMemory;\n        return checkResourceAllow(redisServerMachineList, memoryNeed);\n    }\n\n    public TaskFlowStatusEnum checkMachineConnect() {\n        // redis server\n        return checkMachineConnect(redisServerMachineList, \"standalone: redisServer {} is not connected\");\n    }\n\n    /**\n     * 生成实例\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum generateInstanceNodes() {\n\n        redisServerNodes = instancePortService.generateRedisServerNodeList(appId, redisServerMachineList, masterPerMachine, maxMemory);\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty, appId is {}, redisServerMachineList is {}, masterPerMachine is {}, maxMemory is {}\",\n                    appId, redisServerMachineList, masterPerMachine, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // 设置master节点\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            if (redisServerNode.isMaster()) {\n                redisServerNodes.remove(redisServerNode);\n            }\n        }\n        //设置环境变量\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 保存实例\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum saveInstanceNodes() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty\");\n            return TaskFlowStatusEnum.ABORT;\n        }\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            Integer instanceId = saveInstance(appId, redisServerNode.getIp(), redisServerNode.getPort(), maxMemory,\n                    InstanceTypeEnum.REDIS_SERVER, InstanceStatusEnum.NEW_STATUS, \"\");\n            if (instanceId == null || instanceId.equals(0)) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 创建任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum createRedisServerTask() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"{} redisServerNodes is emtpy\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addRedisServerInstallTask(appId, host, port, redisServerNode.getMaxmemory(), version, false, taskId);\n                if (childTaskId <= 0) {\n                    logger.error(marker, \"standalone : {} {} {} redis server childTaskId is {}\", appId, host, port, childTaskId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"{}:{} redis server task created successfully\", host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"{}:{} redis server task created failly\", host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 等待任务完成\n     */\n    public TaskFlowStatusEnum waitRedisServerFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            long childTaskId = redisServerNode.getTaskId();\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_INSTALL_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"standalone : {}:{} redis server task fail\", host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"standalone : {}:{} redis server task finish successfully\", host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 更新实例状态\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateInstanceStatus() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.GOOD_STATUS.getStatus());\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 部署实例收集\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum deployCollection() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            boolean isDeploy = redisCenter.sendDeployRedisRelateCollectionMsg(appId, host, port);\n            if (!isDeploy) {\n                logger.error(marker, \"{} {}:{} deploy fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{} {}:{} deploy sucessfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 设置密码\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum setPasswd() {\n        try {\n            if (appId > 0) {\n                // 设置密码\n                appService.updateAppPwd(appId, String.valueOf(appId));\n                // 密码校验逻辑\n                boolean checkFlag = redisDeployCenter.checkAuths(appId);\n                logger.info(marker, \"check app standalone passwd:{}\", checkFlag);\n                if (!checkFlag) {\n                    logger.error(marker, \"check app standalone passwd error, appId:{}!\", appId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 通过初审：资源分配\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            if (auditId > 0) {\n                appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n            }\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateFalse() {\n        return updateMachineAllocateStatus(0);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateTrue() {\n        return updateMachineAllocateStatus(1);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateStatus(int status) {\n        try {\n            Set<String> allMachineSet = new HashSet<String>();\n            allMachineSet.addAll(redisServerMachineList);\n            for (String ip : allMachineSet) {\n                machineDao.updateMachineAllocate(ip, status);\n            }\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/TwemproxyAppDeployTask.java",
    "content": "package com.sohu.cache.task.tasks;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.entity.MachineStats;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceStatusEnum;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.NutCrackerNode;\nimport com.sohu.cache.task.entity.RedisSentinelNode;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.util.EnvUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * twemproxy安装\n *\n * @author fulei\n * @date 2018年7月3日\n * @time 下午3:42:53\n */\n@Component(\"TwemproxyAppDeployTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class TwemproxyAppDeployTask extends BaseTask {\n\n    /**\n     * 应用id\n     */\n    private long appId;\n\n    /**\n     * 审核id\n     */\n    private long auditId;\n\n    /**\n     * redis server机器列表\n     */\n    private List<String> redisServerMachineList;\n\n    /**\n     * nut cracker机器列表\n     */\n    private List<String> nutCrackerMachineList;\n\n    /**\n     * redis sentinel机器列表\n     */\n    private List<String> redisSentinelMachineList;\n\n    /**\n     * 每台机器redis server实例个数\n     */\n    private int masterPerMachine;\n\n    /**\n     * 每台机器nutcracker实例个数\n     */\n    private int nutcrackerPerMachine;\n\n    /**\n     * 每台机器sentinel实例个数\n     */\n    private int sentinelPerMachine;\n\n    /**\n     * 每个实例的最大内存(MB)\n     */\n    private int maxMemory;\n\n    private List<RedisServerNode> redisServerNodes;\n\n    private List<RedisSentinelNode> redisSentinelNodes;\n\n    private List<NutCrackerNode> nutCrackerNodes;\n\n    private String version;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        //1. 参数初始化\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        //2. 检查资源，例如内存和代理个数\n        taskStepList.add(\"checkResourceAllow\");\n        //3. 检查机器可用性\n        taskStepList.add(\"checkMachineConnect\");\n        //4. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateTrue\");\n        //5. 获取实例列表\n        taskStepList.add(\"generateInstanceNodes\");\n        //6. 保存实例列表\n        taskStepList.add(\"saveInstanceNodes\");\n        //7. 创建redis server job\n        taskStepList.add(\"createRedisServerTask\");\n        //8. 等待redis server job完成\n        taskStepList.add(\"waitRedisServerFinish\");\n        //9. 主从复制\n        taskStepList.add(\"configReplication\");\n        //10. 创建redis sentinel job\n        taskStepList.add(\"createRedisSentinelTask\");\n        //11. 等待redis sentinel job完成\n        taskStepList.add(\"waitRedisSentinelFinish\");\n        //12. 创建nutcracker job\n        taskStepList.add(\"createNutCrackerTask\");\n        //13. 等待nutcracker job完成\n        taskStepList.add(\"waitNutCrackerFinish\");\n        //14. 更改实例状态\n        taskStepList.add(\"updateInstanceStatus\");\n        //15. 开始收集部署\n        taskStepList.add(\"deployCollection\");\n        //16. 检查proxy是否一致\n        taskStepList.add(\"checkNutCrackerConfIsSame\");\n        //17.检查nut cracker 哈希一致性(进程内)\n        taskStepList.add(\"checkNutCrackerHashIsSame\");\n        //18. 审核\n        taskStepList.add(\"updateAudit\");\n        //19. 更新机器分配状态\n        taskStepList.add(\"updateMachineAllocateFalse\");\n        return taskStepList;\n    }\n\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //审核id\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //maxMemory\n        maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY);\n        if (maxMemory <= 0 || maxMemory > TaskConstants.MAX_MEMORY_LIMIT) {\n            logger.error(marker, \"task {} maxMemory {} is wrong\", taskId, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //masterPerMachine\n        masterPerMachine = MapUtils.getIntValue(paramMap, TaskConstants.MASTER_PER_MACHINE_KEY);\n        if (masterPerMachine <= 0 || masterPerMachine > TaskConstants.MAX_MASTER_PER_MACHINE) {\n            logger.error(marker, \"task {} masterPerMachine {} is wrong\", taskId, masterPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //sentinelPerMachine\n        sentinelPerMachine = MapUtils.getIntValue(paramMap, TaskConstants.SENTINEL_PER_MACHINE_KEY);\n        if (sentinelPerMachine <= 0) {\n            logger.error(marker, \"task {} sentinelPerMachine {} is wrong\", taskId, sentinelPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //nutcrackerPerMachine\n        nutcrackerPerMachine = MapUtils.getIntValue(paramMap, TaskConstants.NUT_CRACKER_PER_MACHINE_KEY);\n        if (nutcrackerPerMachine <= 0 || nutcrackerPerMachine > TaskConstants.MAX_NUT_CRACK_PER_MACHINE) {\n            logger.error(marker, \"task {} nutcrackerPerMachine {} is wrong\", taskId, nutcrackerPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server machine\n        String redisServerMachineStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_MACHINE_LIST_KEY);\n        redisServerMachineList = JSONArray.parseArray(redisServerMachineStr, String.class);\n        if (CollectionUtils.isEmpty(redisServerMachineList)) {\n            logger.error(marker, \"task {} redisServerMachineList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n\n        //redis sentinel machine\n        String redisSentinelMachineStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SENTINEL_MACHINE_LIST_KEY);\n        redisSentinelMachineList = JSONArray.parseArray(redisSentinelMachineStr, String.class);\n        if (CollectionUtils.isEmpty(redisSentinelMachineList)) {\n            logger.error(marker, \"task {} redisSentinelMachineList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis sentinel list\n        String redisSentinelNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SENTINEL_NODES_KEY);\n        if (StringUtils.isNotBlank(redisSentinelNodesStr)) {\n            redisSentinelNodes = JSONArray.parseArray(redisSentinelNodesStr, RedisSentinelNode.class);\n            if (CollectionUtils.isEmpty(redisSentinelNodes)) {\n                logger.error(marker, \"task {} redisSentinelNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n\n        //nut cracker machine\n        String nutCrackerMachineStr = MapUtils.getString(paramMap, TaskConstants.NUT_CRACKER_MACHINE_LIST_KEY);\n        nutCrackerMachineList = JSONArray.parseArray(nutCrackerMachineStr, String.class);\n        if (CollectionUtils.isEmpty(nutCrackerMachineList)) {\n            logger.error(marker, \"task {} nutCrackerMachineList is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //nut cracker list\n        String nutCrackerNodesStr = MapUtils.getString(paramMap, TaskConstants.NUT_CRACKER_NODES_KEY);\n        if (StringUtils.isNotBlank(nutCrackerNodesStr)) {\n            nutCrackerNodes = JSONArray.parseArray(nutCrackerNodesStr, NutCrackerNode.class);\n            if (CollectionUtils.isEmpty(nutCrackerNodes)) {\n                logger.error(marker, \"task {} nutCrackerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n\n        version = MapUtils.getString(paramMap, TaskConstants.VERSION_KEY);\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 检查资源是否充足\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkResourceAllow() {\n        if (EnvUtil.isLocal(environment)) {\n            return TaskFlowStatusEnum.SUCCESS;\n        }\n        // 容量和代理\n        for (String redisServerIp : redisServerMachineList) {\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(redisServerIp);\n            if (machineStats == null) {\n                logger.error(marker, \"{} redis server machineStats is null\", redisServerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(redisServerIp);\n            if (machineInfo.getIsAllocating() == 1) {\n                logger.error(marker, \"redis server machine info {} {} allocating is 1\", machineInfo.getIp(), redisServerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            if (!checkMachineStatIsUpdate(machineStats)) {\n                logger.error(marker, \"redis server machine stats {} {} update_time is {}, may be not updated recently\", machineInfo.getIp(), machineInfo.getIp(), machineStats.getUpdateTimeFormat());\n                return TaskFlowStatusEnum.ABORT;\n            }\n            //兆\n            long memoryFree = NumberUtils.toLong(machineStats.getMemoryFree()) / 1024 / 1024;\n            long memoryNeed = Long.valueOf(masterPerMachine) * maxMemory;\n            if (memoryNeed > memoryFree * 0.7) {\n                logger.error(marker, \"{} need {} MB, but memoryFree is {} MB\", redisServerIp, memoryNeed, memoryFree);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n\n        // sentinel\n        for (String redisSentinelIp : redisSentinelMachineList) {\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(redisSentinelIp);\n            if (machineStats == null) {\n                logger.error(marker, \"{} redis sentinel machineStats is null\", redisSentinelIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(redisSentinelIp);\n            if (machineInfo == null || machineInfo.getIsAllocating() == 1) {\n                logger.error(marker, \"redis sentinel machine {} allocating is 1\", redisSentinelIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            if (!checkMachineStatIsUpdate(machineStats)) {\n                logger.error(marker, \"redis sentinel machine stats {} {} update_time is {}, may be not updated recently\", machineInfo.getIp(), machineInfo.getIp(), machineStats.getUpdateTimeFormat());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n\n        // 代理\n        for (String nutCrackerIp : nutCrackerMachineList) {\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(nutCrackerIp);\n            if (machineStats == null) {\n                logger.error(marker, \"{} nutcracker machineStats is null\", nutCrackerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            MachineInfo machineInfo = machineDao.getMachineInfoByIp(nutCrackerIp);\n            if (machineInfo == null || machineInfo.getIsAllocating() == 1) {\n                logger.error(marker, \"nut cracker machine info {} allocating is 1\", nutCrackerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            if (!checkMachineStatIsUpdate(machineStats)) {\n                logger.error(marker, \"nut cracker machine stats {} {} update_time is {}, may be not updated recently\", machineInfo.getIp(), machineInfo.getIp(), machineStats.getUpdateTimeFormat());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkMachineConnect() {\n        // redis server\n        for (String redisServerIp : redisServerMachineList) {\n            boolean isConnected = checkMachineIsConnect(redisServerIp);\n            if (!isConnected) {\n                logger.error(marker, \"redisServer {} is not connected\", redisServerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        // redis sentinel\n        for (String redisSentinelIp : redisSentinelMachineList) {\n            boolean isConnected = checkMachineIsConnect(redisSentinelIp);\n            if (!isConnected) {\n                logger.error(marker, \"redisSentinel {} is not connected\", redisSentinelIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        // 代理\n        for (String nutCrackerIp : nutCrackerMachineList) {\n            boolean isConnected = checkMachineIsConnect(nutCrackerIp);\n            if (!isConnected) {\n                logger.error(marker, \"nutCrackerIp {} is not connected\", nutCrackerIp);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 生成实例\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum generateInstanceNodes() {\n\n        redisServerNodes = instancePortService.generateRedisServerNodeList(appId, redisServerMachineList, masterPerMachine, maxMemory);\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty, appId is {}, redisServerMachineList is {}, masterPerMachine is {}, maxMemory is {}\",\n                    appId, redisServerMachineList, masterPerMachine, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        redisSentinelNodes = instancePortService.generateRedisSentinelNodeList(appId, redisSentinelMachineList, sentinelPerMachine);\n        if (CollectionUtils.isEmpty(redisSentinelNodes)) {\n            logger.warn(marker, \"redisSentinelNodes is empty, appId is {}, redisSentinelMachineList is {}, masterPerMachine is {}\",\n                    appId, redisSentinelMachineList, sentinelPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        nutCrackerNodes = instancePortService.generateNutCrackerNodeList(appId, nutCrackerMachineList, nutcrackerPerMachine);\n        if (CollectionUtils.isEmpty(nutCrackerNodes)) {\n            logger.warn(marker, \"nutCrackerNodes is empty, appId is {}, nutCrackerMachineList is {}, nutcrackerPerMachine is {}\",\n                    appId, nutCrackerMachineList, nutcrackerPerMachine);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //设置环境变量\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n        paramMap.put(TaskConstants.REDIS_SENTINEL_NODES_KEY, redisSentinelNodes);\n        paramMap.put(TaskConstants.NUT_CRACKER_NODES_KEY, nutCrackerNodes);\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 保存实例\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum saveInstanceNodes() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"redisServerNodes is empty\");\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (CollectionUtils.isEmpty(redisSentinelNodes)) {\n            logger.warn(marker, \"redisSentinelNodes is empty\");\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (CollectionUtils.isEmpty(nutCrackerNodes)) {\n            logger.warn(marker, \"nutCrackerNodes is empty\");\n            return TaskFlowStatusEnum.ABORT;\n        }\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            Integer instanceId = saveInstance(appId, redisServerNode.getIp(), redisServerNode.getPort(), maxMemory,\n                    InstanceTypeEnum.REDIS_SERVER, InstanceStatusEnum.NEW_STATUS, \"\");\n            if (instanceId == null || instanceId.equals(0)) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n            Integer instanceId = saveInstance(appId, redisSentinelNode.getIp(), redisSentinelNode.getPort(), 0,\n                    InstanceTypeEnum.REDIS_SENTINEL, InstanceStatusEnum.NEW_STATUS, \"\");\n            if (instanceId == null || instanceId.equals(0)) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        for (NutCrackerNode nutCrackerNode : nutCrackerNodes) {\n            Integer instanceId = saveInstance(appId, nutCrackerNode.getIp(), nutCrackerNode.getPort(), 0, InstanceTypeEnum.NUTCRACKER,\n                    InstanceStatusEnum.NEW_STATUS, \"\");\n            if (instanceId == null || instanceId.equals(0)) {\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 创建任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum createRedisServerTask() {\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.warn(marker, \"{} redisServerNodes is emtpy\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addRedisServerInstallTask(appId, host, port, redisServerNode.getMaxmemory(), version, false, taskId);\n                if (childTaskId <= 0) {\n                    logger.error(marker, \"{} {} {} redis server childTaskId is {}\", appId, host, port, childTaskId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n                redisServerNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n                logger.info(marker, \"{}:{} redis server task created successfully\", host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"{}:{} redis server task created failly\", host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 等待任务完成\n     *\n     * @return\n     * @TODO 超时时间\n     */\n    public TaskFlowStatusEnum waitRedisServerFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            long childTaskId = redisServerNode.getTaskId();\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_INSTALL_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"{}:{} redis server task fail\", host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{}:{} redis server task finish successfully\", host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 建立主从关系\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum configReplication() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String masterHost = redisServerNode.getMasterHost();\n            int masterPort = redisServerNode.getMasterPort();\n            if (masterHost == null || masterPort <= 0) {\n                continue;\n            }\n            String slaveHost = redisServerNode.getIp();\n            int slavePort = redisServerNode.getPort();\n            //幂等\n            boolean isSlaveOf = redisDeployCenter.slaveOf(appId, masterHost, masterPort, slaveHost, slavePort);\n            if (!isSlaveOf) {\n                logger.error(marker, \"{}:{} slaveof {}:{} fail\", slaveHost, slavePort, masterHost, masterPort);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"{}:{} slaveof {}:{} successfully\", slaveHost, slavePort, masterHost, masterPort);\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 创建redis sentinel任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum createRedisSentinelTask() {\n        List<RedisServerNode> masterRedisServerNodes = getMasterRedisServerNodes();\n        // 计算quorum\n        int quorum = getQuorum(redisSentinelNodes.size());\n        for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n            String sentinelHost = redisSentinelNode.getIp();\n            int sentinelPort = redisSentinelNode.getPort();\n            try {\n                long childTaskId = taskService.addRedisSentinelInstallTask(appId, sentinelHost, sentinelPort, masterRedisServerNodes, quorum, version, taskId);\n                if (childTaskId <= 0) {\n                    logger.error(marker, \"{} {} {} sentinel childTaskId is {}\", appId, sentinelHost, sentinelPort, childTaskId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n                redisSentinelNode.setTaskId(childTaskId);\n                paramMap.put(TaskConstants.REDIS_SENTINEL_NODES_KEY, redisSentinelNodes);\n                logger.info(marker, \"{}:{} redis sentinel task created successfully, childTaskId is {}\", sentinelHost, sentinelPort, childTaskId);\n            } catch (Exception e) {\n                logger.error(marker, \"{}:{} redis sentinel task created failly\", sentinelHost, sentinelPort);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 等待所有redis sentinel启动\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitRedisSentinelFinish() {\n        for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n            long childTaskId = redisSentinelNode.getTaskId();\n            String host = redisSentinelNode.getIp();\n            int port = redisSentinelNode.getPort();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SENTINEL_INSTALL_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"{} {}:{} redis sentinel task fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{} {}:{} redis sentinel task success\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 获取所有master节点\n     *\n     * @return\n     */\n    private List<RedisServerNode> getMasterRedisServerNodes() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String appFullName = appDesc.getName();\n        if (StringUtils.isBlank(appFullName)) {\n            logger.error(marker, \"appId {} fullName is empty\", appId);\n            return Collections.emptyList();\n        }\n        List<RedisServerNode> masterRedisServerNodes = new ArrayList<RedisServerNode>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            if (!redisServerNode.isMaster()) {\n                continue;\n            }\n            String masterName = generateMasterName(appDesc.getName(), redisServerNode.getPort());\n            redisServerNode.setMasterName(masterName);\n            masterRedisServerNodes.add(redisServerNode);\n        }\n        return masterRedisServerNodes;\n    }\n\n    /**\n     * 创建nutcracker任务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum createNutCrackerTask() {\n        List<RedisServerNode> masterRedisServerNodes = getMasterRedisServerNodes();\n        if (CollectionUtils.isEmpty(masterRedisServerNodes)) {\n            return TaskFlowStatusEnum.ABORT;\n        }\n        //防止集中ssh一个host\n        Collections.shuffle(nutCrackerNodes);\n        for (NutCrackerNode nutCrackerNode : nutCrackerNodes) {\n            //控制并发\n            String nutCrackerHost = nutCrackerNode.getIp();\n            int nutCrackerPort = nutCrackerNode.getPort();\n            try {\n                long childTaskId = taskService.addNutCrackerInstallTask(appId, nutCrackerHost, nutCrackerPort, masterRedisServerNodes, taskId);\n                if (childTaskId <= 0) {\n                    logger.error(marker, \"{} {} {} nut cracker childTaskId is {}\", appId, nutCrackerHost, nutCrackerPort, childTaskId);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n                nutCrackerNode.setTaskId(childTaskId);\n                //回写配置\n                paramMap.put(TaskConstants.NUT_CRACKER_NODES_KEY, nutCrackerNodes);\n                logger.info(marker, \"{}:{} nut cracker task created successfully\", nutCrackerHost, nutCrackerPort);\n            } catch (Exception e) {\n                logger.error(marker, \"{}:{} nut cracker task created failly\", nutCrackerHost, nutCrackerPort);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 等待所有nut cracker启动\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitNutCrackerFinish() {\n        for (NutCrackerNode nutCrackerNode : nutCrackerNodes) {\n            long childTaskId = nutCrackerNode.getTaskId();\n            String host = nutCrackerNode.getIp();\n            int port = nutCrackerNode.getPort();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.NUT_CRACKER_INSTALL_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"{}:{} nut cracker task fail\", host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{}:{} nut cracker task success\", host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 更新实例状态\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateInstanceStatus() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.GOOD_STATUS.getStatus());\n        }\n        for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n            String host = redisSentinelNode.getIp();\n            int port = redisSentinelNode.getPort();\n            instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.GOOD_STATUS.getStatus());\n        }\n        for (NutCrackerNode nutCrackerNode : nutCrackerNodes) {\n            String host = nutCrackerNode.getIp();\n            int port = nutCrackerNode.getPort();\n            instanceDao.updateStatus(appId, host, port, InstanceStatusEnum.GOOD_STATUS.getStatus());\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 部署实例收集\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum deployCollection() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            boolean isDeploy = redisCenter.sendDeployRedisRelateCollectionMsg(appId, host, port);\n            if (!isDeploy) {\n                logger.error(marker, \"{} {}:{} deploy fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"{} {}:{} deploy sucessfully\", appId, host, port);\n            }\n        }\n//\t\tfor (RedisSentinelNode redisSentinelNode : redisSentinelNodes) {\n//\t\t\tString host = redisSentinelNode.getIp();\n//\t\t\tint port = redisSentinelNode.getPort();\n//\t\t\tinstanceDao.updateStatus(appId, host, port, InstanceStatusEnum.GOOD_STATUS.getStatus());\n//\t\t}\n//\t\tfor (NutCrackerNode nutCrackerNode : nutCrackerNodes) {\n//\t\t\tString host = nutCrackerNode.getIp();\n//\t\t\tint port = nutCrackerNode.getPort();\n//\t\t\tinstanceDao.updateStatus(appId, host, port, InstanceStatusEnum.GOOD_STATUS.getStatus());\n//\t\t}\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 通过初审：资源分配\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value());\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n    /**\n     * 检查proxy配置的一致性\n     */\n    public TaskFlowStatusEnum checkNutCrackerConfIsSame() {\n        boolean isSame = redisCenter.checkNutCrackerConfIsSame(appId);\n        if (!isSame) {\n            logger.error(marker, \"appId {} nutcracker config is not same, please check!!!\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 6.检查proxy hash的一致性\n     */\n    public TaskFlowStatusEnum checkNutCrackerHashIsSame() {\n        List<InstanceInfo> instanceInfoList = redisCenter.checkNutCrackerHashIsSame(appId, true);\n        if (CollectionUtils.isNotEmpty(instanceInfoList)) {\n            for (InstanceInfo instanceInfo : instanceInfoList) {\n                logger.error(marker, \"appId {} {}:{} hash is wrong\", appId, instanceInfo.getIp(), instanceInfo.getPort());\n            }\n            return TaskFlowStatusEnum.ABORT;\n        } else {\n            logger.info(marker, \"appId {} proxy hash is same\", appId);\n            return TaskFlowStatusEnum.SUCCESS;\n        }\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateFalse() {\n        return updateMachineAllocateStatus(0);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateTrue() {\n        return updateMachineAllocateStatus(1);\n    }\n\n    public TaskFlowStatusEnum updateMachineAllocateStatus(int status) {\n        try {\n            Set<String> allMachineSet = new HashSet<String>();\n            allMachineSet.addAll(redisServerMachineList);\n            allMachineSet.addAll(nutCrackerMachineList);\n            for (String ip : allMachineSet) {\n                machineDao.updateMachineAllocate(ip, status);\n            }\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/analysis/RedisServerBigKeyAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks.analysis;\n\nimport com.sohu.cache.entity.InstanceStats;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.RedisDataStructureTypeEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.InstanceBigKey;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.Pair;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.nio.charset.Charset;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * bigkey分析\n *\n * @author fulei\n */\n@Component(\"RedisServerBigKeyAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisServerBigKeyAnalysisTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private long auditId;\n\n    private final static int SCAN_COUNT = 100;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // big key分析\n        taskStepList.add(\"bigKeyAnalysis\");\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.检查run以及slave\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3. bigkey分析\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum bigKeyAnalysis() {\n        long startTime = System.currentTimeMillis();\n\n        InstanceStats instanceStats = instanceStatsDao.getInstanceStatsByHost(host, port);\n\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            jedis.readonly();\n\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            // scan参数\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n            ScanParams scanParams = new ScanParams().count(SCAN_COUNT);\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    // 使用pipeline获取type\n                    Pipeline pipeline = jedis.pipelined();\n                    keyList.stream().forEach(key -> pipeline.type(key));\n\n                    List<Object> typeObjectList;\n                    try {\n                        typeObjectList = pipeline.syncAndReturnAll();\n                    } catch (JedisRedirectionException e) {\n                        continue; // ignore\n                    }\n\n                    List<Object> typeList = typeObjectList;\n                    // key type Map\n                    Map<byte[], String> keyTypeMap = IntStream.range(0, keyList.size())\n                            .filter(i -> !\"none\".equalsIgnoreCase(String.valueOf(typeList.get(i)))\n                                    && (typeList.get(i) instanceof String))\n                            .mapToObj(i -> new Pair<>(keyList.get(i), String.valueOf(typeList.get(i))))\n                            .collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n\n                    Pipeline pipeline2 = jedis.pipelined();\n                    // 计算长度pipeline\n                    keyTypeMap.entrySet().stream().forEach(entry -> {\n                        byte[] key = entry.getKey();\n                        String type = entry.getValue();\n                        if (RedisDataStructureTypeEnum.string.getValue().equals(type)) {\n                            pipeline2.strlen(key);\n                        } else if (RedisDataStructureTypeEnum.hash.getValue().equals(type)) {\n                            pipeline2.hlen(key);\n                        } else if (RedisDataStructureTypeEnum.list.getValue().equals(type)) {\n                            pipeline2.llen(key);\n                        } else if (RedisDataStructureTypeEnum.set.getValue().equals(type)) {\n                            pipeline2.scard(key);\n                        } else if (RedisDataStructureTypeEnum.zset.getValue().equals(type)) {\n                            pipeline2.zcard(key);\n                        }\n                    });\n\n                    List<Object> lengthList;\n                    try {\n                        lengthList = pipeline2.syncAndReturnAll();\n                    } catch (JedisRedirectionException e) {\n                        continue;// ignore\n                    }\n                    List<InstanceBigKey> instanceBigKeyList = IntStream.range(0, lengthList.size())\n                            .filter(i -> (typeList.get(i) instanceof String) && (lengthList.get(i) instanceof Long))\n                            .mapToObj(i -> {\n                                long length = (long) lengthList.get(i);\n                                String type = String.valueOf(typeList.get(i));\n                                byte[] key = keyList.get(i);\n                                boolean isBigKey = checkIsBigKey(type, length);\n                                if (!isBigKey) {\n                                    return null;\n                                }\n                                InstanceBigKey instanceBigKey = new InstanceBigKey();\n                                instanceBigKey.setAppId(appId);\n                                instanceBigKey.setInstanceId(instanceStats.getInstId());\n                                instanceBigKey.setAuditId(auditId);\n                                instanceBigKey.setIp(host);\n                                instanceBigKey.setPort(port);\n                                instanceBigKey.setRole(instanceStats.getRole());\n                                instanceBigKey.setBigKey(new String(key));\n                                instanceBigKey.setLength(length);\n                                instanceBigKey.setType(type);\n                                instanceBigKey.setCreateTime(new Date());\n                                return instanceBigKey;\n                            })\n                            .filter(instanceBigKey -> instanceBigKey != null)\n                            .collect(Collectors.toList());\n\n                    if (CollectionUtils.isNotEmpty(instanceBigKeyList)) {\n                        instanceBigKeyDao.batchSave(instanceBigKeyList);\n                    }\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already anlysis {}% {} key \", appId, host, port,\n                                curSplit * 10, count);\n                        curSplit++;\n                    }\n                    // @TODO暂时写死\n                    TimeUnit.MILLISECONDS.sleep(2);\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if (Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            logger.info(marker, \"{} {}:{} analysis bigkey key successfully, cost time is {} ms, total key is {}\", appId,\n                    host, port, (System.currentTimeMillis() - startTime), count);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, \"redis-cli -h {} -p {} adminauth error\", host, port);\n            logger.error(marker, \"bigkey custinsId {} {}:{} bigkey connect error:\" + e.getMessage(), appId, host, port,\n                    e);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n\n    }\n\n    /**\n     * 按照类型判断是否为bigkey\n     *\n     * @param type\n     * @param length\n     * @return\n     */\n    private boolean checkIsBigKey(String type, long length) {\n        if (RedisDataStructureTypeEnum.string.getValue().equals(type)) {\n            return length > ConstUtils.STRING_MAX_LENGTH;\n        } else if (RedisDataStructureTypeEnum.hash.getValue().equals(type)) {\n            return length > ConstUtils.HASH_MAX_LENGTH;\n        } else if (RedisDataStructureTypeEnum.list.getValue().equals(type)) {\n            return length > ConstUtils.LIST_MAX_LENGTH;\n        } else if (RedisDataStructureTypeEnum.set.getValue().equals(type)) {\n            return length > ConstUtils.SET_MAX_LENGTH;\n        } else if (RedisDataStructureTypeEnum.zset.getValue().equals(type)) {\n            return length > ConstUtils.ZSET_MAX_LENGTH;\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/analysis/RedisServerIdleKeyAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks.analysis;\n\nimport com.google.common.util.concurrent.AtomicLongMap;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.IdleTimeDistriEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * redis server空闲key分析\n *\n * @author fulei\n */\n@Component(\"RedisServerIdleKeyAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisServerIdleKeyAnalysisTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private long auditId;\n\n    /**\n     * 扫描master\n     */\n    private final static int SCAN_COUNT = 100;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        //检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        //idle key分析\n        taskStepList.add(\"idleKeyAnalysis\");\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum idleKeyAnalysis() {\n        long startTime = System.currentTimeMillis();\n        //本地结果集\n        AtomicLongMap<IdleTimeDistriEnum> idleTimeCountMap = AtomicLongMap.create();\n        Jedis jedis = null;\n\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            jedis.readonly();\n\n            //如果\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            ScanParams scanParams = new ScanParams().count(SCAN_COUNT);\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n            while (true) {\n                int retryTimes = 10;\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    //pipeline object idle\n                    Pipeline pipeline = jedis.pipelined();\n                    keyList.stream().forEach(key -> pipeline.objectIdletime(key));\n                    List<Object> idleTimeList;\n                    try {\n                        idleTimeList = pipeline.syncAndReturnAll();\n                    } catch (JedisRedirectionException e) {\n                        continue;// ignore\n                    }\n                    idleTimeList.stream()\n                            .filter(obj -> obj != null && (obj instanceof Long))\n                            .forEach(obj -> {\n                                long idleSeconds = (long) obj;\n                                long idleHours = idleSeconds / 3600;\n                                IdleTimeDistriEnum idleTimeDistriEnum = IdleTimeDistriEnum\n                                        .getRightIdleDistri(idleHours);\n                                if (idleTimeDistriEnum == null) {\n                                    logger.error(marker, \"idleHours {} {} IdleTimeDistriEnum is null\", idleHours,\n                                            idleSeconds);\n                                } else {\n                                    idleTimeCountMap.incrementAndGet(idleTimeDistriEnum);\n                                }\n                            });\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already anlysis {}% {} key \", appId, host, port,\n                                curSplit * 10, count);\n                        curSplit++;\n                    }\n                    // @TODO暂时写死\n                    TimeUnit.MILLISECONDS.sleep(2);\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if (Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            logger.info(marker, \"{} {}:{} analysis idle key successfully, cost time is {} ms, total key is {}\", appId,\n                    host, port, (System.currentTimeMillis() - startTime), count);\n\n            String idleKeyResultKey = ConstUtils.getRedisServerIdleKey(appId, auditId);\n            idleTimeCountMap.asMap().entrySet().stream().forEach(entry -> {\n                String member = entry.getKey().getValue();\n                assistRedisService.zincrby(idleKeyResultKey, entry.getValue(), member);\n                logger.info(marker, \"{} {} {}:{} idle distri {} {}\", idleKeyResultKey, appId, host, port,\n                        entry.getKey(), entry.getValue());\n            });\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/analysis/RedisServerKeyTtlAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks.analysis;\n\nimport com.google.common.util.concurrent.AtomicLongMap;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.constant.TtlTimeDistriEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * key过期时间分析\n *\n * @author fulei\n */\n@Component(\"RedisServerKeyTtlAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisServerKeyTtlAnalysisTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private long auditId;\n\n    /**\n     * 扫描slave\n     */\n    private final static int SCAN_COUNT = 100;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // key类型分析\n        taskStepList.add(\"keyTtlAnalysis\");\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum keyTtlAnalysis() {\n        long startTime = System.currentTimeMillis();\n\n        //本地结果集\n        AtomicLongMap<TtlTimeDistriEnum> ttlTimeCountMap = AtomicLongMap.create();\n\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            jedis.readonly();\n\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            ScanParams scanParams = new ScanParams().count(SCAN_COUNT);\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    Pipeline pipeline = jedis.pipelined();\n                    keyList.stream().forEach(key -> pipeline.ttl(key));\n\n                    List<Object> ttlObjectList;\n                    try {\n                        ttlObjectList = pipeline.syncAndReturnAll();\n                    } catch (JedisRedirectionException e) {\n                        continue;// ignore\n                    }\n                    ttlObjectList.stream()\n                            .filter(obj -> obj != null && (obj instanceof Long))\n                            .forEach(obj -> {\n                                long ttlSeconds = (long) obj;\n                                TtlTimeDistriEnum ttlTimeDistriEnum;\n                                if (ttlSeconds == -1) {\n                                    ttlTimeDistriEnum = TtlTimeDistriEnum.BETWEEN_PERSIST_HOURS;\n                                } else {\n                                    long ttlHours = ttlSeconds / 3600;\n                                    ttlTimeDistriEnum = TtlTimeDistriEnum.getRightTtlDistri(ttlHours);\n                                }\n                                if (ttlTimeDistriEnum == null) {\n                                    logger.error(marker, \"ttlSeconds {} TtlTimeDistriEnum is null\", ttlSeconds);\n                                }\n                                ttlTimeCountMap.incrementAndGet(ttlTimeDistriEnum);\n                            });\n\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already anlysis {}% {} key \", appId, host, port,\n                                curSplit * 10, count);\n                        curSplit++;\n                    }\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if (Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            logger.info(marker, \"{} {}:{} analysis key ttl successfully, cost time is {} ms, total key is {}\", appId,\n                    host, port, (System.currentTimeMillis() - startTime), count);\n\n            String keyTtlResultKey = ConstUtils.getRedisServerTtlKey(appId, auditId);\n            ttlTimeCountMap.asMap().entrySet().stream().forEach(entry -> {\n                String ttlDistri = entry.getKey().getValue();\n                assistRedisService.zincrby(keyTtlResultKey, entry.getValue(), ttlDistri);\n                logger.info(marker, \"{} {} {}:{} ttl distri {} {}\", keyTtlResultKey, appId, host, port, ttlDistri,\n                        entry.getValue());\n            });\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/analysis/RedisServerKeyTypeAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks.analysis;\n\nimport com.google.common.util.concurrent.AtomicLongMap;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * key类型分析\n *\n * @author fulei\n */\n@Component(\"RedisServerKeyTypeAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisServerKeyTypeAnalysisTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private long auditId;\n\n    /**\n     * 扫描slave\n     */\n    private final static int SCAN_COUNT = 100;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // key类型分析\n        taskStepList.add(\"keyTypeAnalysis\");\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum keyTypeAnalysis() {\n        long startTime = System.currentTimeMillis();\n\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            jedis.readonly();\n\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            ScanParams scanParams = new ScanParams().count(SCAN_COUNT);\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n\n            AtomicLongMap<String> typeCountMap = AtomicLongMap.create();\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    Pipeline pipeline = jedis.pipelined();\n                    keyList.stream().forEach(key -> pipeline.type(key));\n\n                    List<Object> typeObjectList;\n                    try {\n                        typeObjectList = pipeline.syncAndReturnAll();\n                    } catch (JedisRedirectionException e) {\n                        continue; // ignore\n                    }\n\n                    typeObjectList.stream()\n                            .filter(type -> !\"none\".equalsIgnoreCase(String.valueOf(type)) && (type instanceof String))\n                            .forEach(type -> typeCountMap.incrementAndGet(String.valueOf(type)));\n\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already anlysis {}% {} key \", appId, host, port,\n                                curSplit * 10, count);\n                        curSplit++;\n                    }\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if (Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            logger.info(marker, \"{} {}:{} analysis key type successfully, cost time is {} ms, total key is {}\", appId,\n                    host, port, (System.currentTimeMillis() - startTime), count);\n\n            String keyTypeResultKey = ConstUtils.getRedisServerTypeKey(appId, auditId);\n\n            typeCountMap.asMap().entrySet().stream().forEach(entry -> {\n                String type = entry.getKey();\n                assistRedisService.zincrby(keyTypeResultKey, entry.getValue(), type);\n                logger.info(marker, \"{} {} {}:{} type distri {} {}\", keyTypeResultKey, appId, host, port, type,\n                        entry.getValue());\n            });\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/analysis/RedisServerKeyValueAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks.analysis;\n\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.constant.ValueSizeDistriEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.DebugParams;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.exceptions.JedisException;\n\nimport java.nio.charset.Charset;\nimport java.util.*;\nimport java.util.Map.Entry;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * value size分析 memory usage结果\n *\n * @author fulei\n */\n@Component(\"RedisServerKeyValueAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisServerKeyValueAnalysisTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private long auditId;\n\n    /**\n     * 扫描slave\n     */\n    private final static int SCAN_COUNT = 100;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // key类型分析\n        taskStepList.add(\"keyValueAnalysis\");\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum keyValueAnalysis() {\n        long startTime = System.currentTimeMillis();\n\n        //本地结果集\n        Map<ValueSizeDistriEnum, Long> valueSizeCountMap = new HashMap<ValueSizeDistriEnum, Long>();\n\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getJedis(appId, host, port);;\n            jedis.readonly();\n            \n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            ScanParams scanParams = new ScanParams().count(SCAN_COUNT);\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    for (byte[] key : keyList) {\n                        Long memoryUsage = null;\n                        try {\n                            // key 可能不存在,报 ERR no such key\n                            memoryUsage = jedis.memoryUsage(new String(key, Charset.forName(\"UTF-8\")));\n                        } catch (JedisException e) {\n                            logger.warn(\"memoryUsage-error: key={}\", new String(key, Charset.forName(\"UTF-8\")));\n                            logger.error(\"memoryUsage-error: \", e);\n                            //ignore\n                        }\n                        if (memoryUsage == null) {\n                            continue;\n                        }\n                        ValueSizeDistriEnum valueSizeDistriEnum = ValueSizeDistriEnum.getRightSizeBetween(memoryUsage);\n                        if (valueSizeDistriEnum == null) {\n                            logger.warn(\"key {} valueBytes {} is wrong\", key, memoryUsage);\n                            continue;\n                        }\n                        if (valueSizeCountMap.containsKey(valueSizeDistriEnum)) {\n                            valueSizeCountMap.put(valueSizeDistriEnum, valueSizeCountMap.get(valueSizeDistriEnum) + 1);\n                        } else {\n                            valueSizeCountMap.put(valueSizeDistriEnum, 1L);\n                        }\n                    }\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already anlysis {}% {} key \", appId, host, port,\n                                curSplit * 10, count);\n                        curSplit++;\n                    }\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if (Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            logger.info(marker, \"{} {}:{} analysis key value size successfully, cost time is {} ms, total key is {}\",\n                    appId, host, port, (System.currentTimeMillis() - startTime), count);\n\n            if (MapUtils.isNotEmpty(valueSizeCountMap)) {\n                String keyValueSizeResultKey = ConstUtils.getRedisServerValueSizeKey(appId, auditId);\n                for (Entry<ValueSizeDistriEnum, Long> entry : valueSizeCountMap.entrySet()) {\n                    String valueSizeDistri = entry.getKey().getValue();\n                    assistRedisService.zincrby(keyValueSizeResultKey, entry.getValue(), valueSizeDistri);\n                    logger.info(marker, \"{} {} {}:{} value size distri {} {}\", keyValueSizeResultKey, appId, host, port,\n                            valueSizeDistri, entry.getValue());\n                }\n            } else {\n                logger.error(marker, \"{} {}:{} value size distri is empty\", appId, host, port);\n            }\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/daily/MachineExamTask.java",
    "content": "package com.sohu.cache.task.tasks.daily;\n\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.entity.MachineStats;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.MachineExamContants;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport org.apache.commons.collections.MapUtils;\nimport org.assertj.core.util.Lists;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * Created by rucao on 2019/1/17\n */\n@Component(\"MachineExamTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class MachineExamTask extends BaseTask {\n\n    private List<String> machineIpList;\n    private Integer useType;\n    private List<MachineStats> machineStatsList = Lists.newArrayList();\n    private Map<String, Integer> machineInstanceCountMap = Maps.newHashMap();\n    private List<Map> examResult = Lists.newArrayList();\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 准备任务参数\n        taskStepList.add(\"prepareAppParam\");\n        // 2. 执行机器故障检查\n        taskStepList.add(\"executeMachineExam\");\n        // 3. 检查结果展示\n        taskStepList.add(\"showMachineExamResult\");\n        return taskStepList;\n    }\n\n    @Override\n    public TaskStepFlowEnum.TaskFlowStatusEnum init() {\n        super.init();\n        useType = MapUtils.getInteger(paramMap, TaskConstants.USE_TYPE_KEY, null);\n        machineIpList = (List) MapUtils.getObject(paramMap, TaskConstants.MACHINE_IP_LIST_KEY);\n        return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1. 准备任务参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum prepareAppParam() {\n        for (String ip : machineIpList) {\n            List<MachineStats> list = machineCenter.getMachineStats(ip, useType, null,null, null, -1, null);\n            machineStatsList.addAll(list);\n        }\n        machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n        if (machineStatsList.isEmpty() || machineInstanceCountMap.isEmpty()) {\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2. 执行机器故障检查\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum executeMachineExam() {\n        for (MachineStats machineStats : machineStatsList) {\n            String ip = machineStats.getIp();\n            double mem = (double) machineStats.getInfo().getMem();//G\n            double usedMem = machineStats.getMachineMemInfo().getUsedMemRss() / 1024 / 1024 / 1024;//byte\n            double applyMem = (double) machineStats.getMachineMemInfo().getApplyMem() / 1024 / 1024 / 1024;//byte\n            double usedMemRatio = usedMem / mem;\n            double applyMemRatio = applyMem / mem;\n\n            int cpu = machineStats.getInfo().getCpu();\n            int usedCpu = machineInstanceCountMap.get(ip);\n            double usedCpuRatio = (double) usedCpu / cpu;\n\n            if (judgeMemUsed(usedMemRatio, mem) || judgeMemUsed(applyMemRatio, mem) || judgeCpuUsed(usedCpuRatio)) {\n                examResult.add(\n                        new HashMap<String, String>() {{\n                            put(MachineExamContants.MACHINE_IP, ip);\n                            put(MachineExamContants.MEM, String.valueOf(mem));\n                            put(MachineExamContants.APPLY_MEM, String.valueOf(applyMem));\n                            put(MachineExamContants.APPLY_MEM_RATIO, String.valueOf(applyMemRatio));\n                            put(MachineExamContants.USED_MEM, String.valueOf(usedMem));\n                            put(MachineExamContants.USED_MEM_RATIO, String.valueOf(usedMemRatio));\n                            put(MachineExamContants.CPU, String.valueOf(cpu));\n                            put(MachineExamContants.USED_CPU, String.valueOf(usedCpu));\n                            put(MachineExamContants.USED_CPU_RATIO, String.valueOf(usedCpuRatio));\n                        }}\n                );\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 3. 检查结果展示\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum showMachineExamResult() {\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    private boolean judgeMemUsed(double ratio, double mem) {\n        if (mem > 20 && ratio > MachineExamContants.defult_memUseThreshold) {\n            return true;\n        } else if (mem > 10 && mem <= 20 && ratio > MachineExamContants.middle_memUseThreshold) {\n            return true;\n        } else if (mem <= 10 && ratio > MachineExamContants.small_memUseThreshold) {\n            return true;\n        }\n        return false;\n    }\n\n    private boolean judgeCpuUsed(double ratio) {\n        if (ratio >= MachineExamContants.BASE_RATIO) {\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/daily/TopologyExamTask.java",
    "content": "package com.sohu.cache.task.tasks.daily;\n\nimport com.google.common.collect.Maps;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.stats.app.impl.AppDailyDataCenterImpl;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.constant.TopoloyExamContants;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.ExamToolEnum;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.assertj.core.util.Lists;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.text.MessageFormat;\nimport java.util.*;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * Created by rucao on 2019/1/17\n */\n@Component(\"TopologyExamTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class TopologyExamTask extends BaseTask {\n    private Logger logger = LoggerFactory.getLogger(TopologyExamTask.class);\n\n    @Resource\n    private AppDailyDataCenterImpl appDailyDataCenter;\n\n    private boolean auto;\n    private int examType;\n    private List<AppDesc> appDescList = Lists.newArrayList();\n    private List<Map> examResult = Lists.newArrayList();\n    private Map<String, Object> examInfo = Maps.newHashMap();\n\n    private int appType;\n    private long appId;\n    /**\n     * instance list\n     */\n    private List<InstanceInfo> instances;\n    /**\n     * entry <masterInst,slaveInstList>\n     */\n    private Map<InstanceInfo, List<InstanceInfo>> master_slaves;\n    /**\n     * machine realip / 机房信息\n     */\n    private Map<String, List<InstanceInfo>> machineInstancesMap;\n    private Map<String, MachineInfo> instanceInfoMap;\n    private Map<String, MachineInfo> machineInfoMap;\n\n    private int slaveNum;\n    /**\n     * sentinelInst\n     */\n    private List<InstanceInfo> sentinels;\n\n    private static final String DOT = \".\";\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = Lists.newArrayList();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查应用参数\n        taskStepList.add(\"prepareAppParam\");\n        // 2. 执行应用拓扑故障检查\n        taskStepList.add(\"executeAppTopologyExam\");\n        // 3. 检查结果展示/发送结果邮件\n        taskStepList.add(\"showAppTopologyExamResult\");\n\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n        auto = MapUtils.getBooleanValue(paramMap, \"auto\");\n        examType = MapUtils.getIntValue(paramMap, TaskConstants.EXAM_TYPE_KEY);\n        appDescList.clear();\n        examResult.clear();\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.准备应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum prepareAppParam() {\n        logger.info(\"prepareAppParam\");\n        if (examType == ExamToolEnum.EXAM_NON_TEST.getValue()) {\n            appDescList = appDao.getOnlineAppsNonTest();\n        } else if (examType == ExamToolEnum.EXAM_ALL.getValue()) {\n            appDescList = appDao.getOnlineApps();\n        } else if (examType == ExamToolEnum.EXAM_APPID.getValue()) {\n            long appId = MapUtils.getIntValue(paramMap, TaskConstants.APPID_KEY);\n            appDescList.add(appDao.getAppDescById(appId));\n        }\n        if (CollectionUtils.isEmpty(appDescList)) {\n            logger.error(marker, \"task {} appDesc is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2. 执行应用拓扑故障检查\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum executeAppTopologyExam() {\n        logger.info(\"executeAppTopologyExam\");\n        check(appDescList);\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    public Map<String, Object> check(List<AppDesc> appDescList) {\n        examResult.clear();\n        examInfo.clear();\n\n        if (!CollectionUtils.isEmpty(appDescList)) {\n            for (AppDesc appDesc : appDescList) {\n                try {\n                    getAppParam(appDesc);\n                    switch (appType) {\n                        case ConstUtils.CACHE_REDIS_STANDALONE:\n                            standaloneExam();\n                            break;\n                        case ConstUtils.CACHE_TYPE_REDIS_CLUSTER:\n                            clusterExam();\n                            break;\n                        case ConstUtils.CACHE_REDIS_SENTINEL:\n                            sentinelExam();\n                            break;\n                        default:\n                            break;\n                    }\n                } catch (Exception e) {\n                    logger.error(\"executeAppTopologyExam error. appId:{}\", appDesc.getAppId());\n                    logger.error(e.getMessage(), e);\n                }\n            }\n            examInfo.put(\"appDesc\", appDescList.get(0));\n            examInfo.put(\"tips\", examResult);\n        }\n        return examInfo;\n    }\n\n    public List<AppClientStatisticGather> checkAppsTopology(Date date) {\n        List<AppClientStatisticGather> result = new ArrayList<>();\n        if (date == null) {\n            //获取前一天日期\n            date = new Date();\n            Calendar calendar = Calendar.getInstance();\n            calendar.setTime(date);\n            calendar.add(Calendar.DAY_OF_MONTH, -1);\n            date = calendar.getTime();\n        }\n        Date checkDate = date;\n\n        List<AppDesc> appDescList = appDao.getOnlineApps();\n        appDescList.forEach(appDesc -> {\n            AppClientStatisticGather gather = new AppClientStatisticGather();\n            Map<String, Object> info = check(new ArrayList<AppDesc>() {{\n                add(appDesc);\n            }});\n            gather.setGatherTime(DateUtil.formatYYYY_MM_dd(checkDate));\n\n            gather.setAppId(appDesc.getAppId());\n            if (MapUtils.isNotEmpty(info)) {\n                List tips = (List) info.get(\"tips\");\n                int topologyExamResult = CollectionUtils.isNotEmpty(tips) && tips.size() > 0 ? 1 : 0;\n                gather.setTopologyExamResult(topologyExamResult);\n            } else {\n                gather.setTopologyExamResult(-1);\n            }\n            result.add(gather);\n        });\n        return result;\n    }\n\n    /**\n     * 3. 检查结果显示\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum showAppTopologyExamResult() {\n        logger.info(\"showAppTopologyExamResult\");\n        Date startDate = DateUtils.addDays(new Date(), -1);\n        appDailyDataCenter.noteAppTopologyDaily(startDate, examResult);\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * getAppParam 获取应用检查参数\n     */\n    private boolean getAppParam(AppDesc appDesc) {\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return false;\n        }\n\n        instances = Lists.newArrayList();\n        master_slaves = Maps.newHashMap();\n        sentinels = Lists.newArrayList();\n        machineInfoMap = Maps.newHashMap();\n        instanceInfoMap = Maps.newHashMap();\n        machineInstancesMap = Maps.newHashMap();\n        slaveNum = 0;\n\n        appId = appDesc.getAppId();\n\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return false;\n        }\n        appType = appDesc.getType();\n        instances = appService.getAppOnlineInstanceInfo(appId);\n        if (instances.isEmpty()) {\n            logger.error(marker, \"appId {} : instance list is null \", appId);\n            return false;\n        }\n        for (InstanceInfo inst : instances) {\n            if (inst.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && inst.getRoleDesc().equals(InstanceRoleEnum.MASTER.getInfo())) {\n                master_slaves.put(inst, Lists.newArrayList());\n            } else if (inst.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && inst.getRoleDesc().equals(InstanceRoleEnum.SENTINEL.getInfo())) {\n                sentinels.add(inst);\n            }\n        }\n        for (InstanceInfo inst : instances) {\n            if (inst.getStatus() == 1 && inst.getRoleDesc().equals(InstanceRoleEnum.SLAVE.getInfo())) {\n                slaveNum++;\n                for (Map.Entry<InstanceInfo, List<InstanceInfo>> entry : master_slaves.entrySet()) {\n                    InstanceInfo masterInst = entry.getKey();\n                    if (inst.getMasterInstanceId() == masterInst.getId()) {\n                        List<InstanceInfo> slaveInstList = entry.getValue();\n                        slaveInstList.add(inst);\n                        master_slaves.put(masterInst, slaveInstList);\n                    }\n                }\n            }\n        }\n        // machineInfo\n        for (InstanceInfo inst : instances) {\n            if (inst.getStatus() == 1) {\n                MachineInfo machineInfo = machineCenter.getMachineInfoByIp(inst.getIp());\n                if (machineInfo != null) {\n                    List<InstanceInfo> instanceInfos = new ArrayList<>();\n                    // realip\n                    String realIp = StringUtils.isEmpty(machineInfo.getRealIp()) ? machineInfo.getIp() : machineInfo.getRealIp();\n                    if (!CollectionUtils.isEmpty(machineInstancesMap.get(realIp))) {\n                        instanceInfos = machineInstancesMap.get(realIp);\n                    }\n                    instanceInfos.add(inst);\n                    machineInstancesMap.put(realIp, instanceInfos);\n                    machineInfoMap.put(realIp, machineInfo);\n                    instanceInfoMap.put(inst.getIp(), machineInfo);\n                }\n            }\n        }\n        // master-slave validate\n        boolean msFlag = false;\n        for (Map.Entry<InstanceInfo, List<InstanceInfo>> ms : master_slaves.entrySet()) {\n            InstanceInfo masterNode = ms.getKey();\n            if (!CollectionUtils.isEmpty(ms.getValue())) {\n                for (InstanceInfo slaveNode : ms.getValue()) {\n                    String masterRealIp = getRealIp(masterNode.getIp());\n                    String slaveRealIp = getRealIp(slaveNode.getIp());\n                    if (!StringUtils.isEmpty(masterRealIp) && !StringUtils.isEmpty(slaveRealIp) && masterRealIp.equals(slaveRealIp)) {\n                        msFlag = true;\n                        break;\n                    }\n                }\n            }\n            if (msFlag) {\n                break;\n            }\n        }\n\n        examInfo.put(\"instances\", instances);\n        examInfo.put(\"master_slaves\", master_slaves);\n        examInfo.put(\"sentinels\", sentinels);\n        examInfo.put(\"sameNetSegment\", checkSameNetSegment());\n        examInfo.put(\"slaveNum\", slaveNum);\n        examInfo.put(\"machineInfoMap\", machineInfoMap);\n        examInfo.put(\"instanceInfoMap\", instanceInfoMap);\n        examInfo.put(\"machineInstancesMap\", machineInstancesMap);\n        examInfo.put(\"msFlag\", msFlag);\n\n        return true;\n    }\n\n    private boolean checkSameNetSegment(){\n        boolean sameFlag = true;\n        if (appType == ConstUtils.CACHE_TYPE_REDIS_CLUSTER  && CollectionUtils.isNotEmpty(instances)) {\n            Optional<InstanceInfo> masterOptional = instances.stream().filter(instanceInfo -> instanceInfo.getRoleDesc().equals(InstanceRoleEnum.MASTER.getInfo())).findFirst();\n            if(masterOptional.isPresent()){\n                String ip = masterOptional.get().getIp();\n                String[] split = ip.split(\"\\\\.\");\n                if(split != null && split.length > 2){\n                    String netSegment = split[0] + DOT  + split[1];\n                    Optional<InstanceInfo> notMatchInstance = instances.stream().filter(instanceInfo -> InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc()) || InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())).filter(instanceInfo -> !instanceInfo.getIp().startsWith(netSegment)).findFirst();\n                    if(notMatchInstance.isPresent()){\n                        String notMatchIp = notMatchInstance.get().getIp();\n                        String notMatchNetSegment = notMatchIp;\n                        if(notMatchIp != null){\n                            String[] split1 = notMatchIp.split(\"\\\\.\");\n                            notMatchNetSegment = split1[0] + DOT + split1[1];\n                        }\n                        String diffNetSegment = notMatchNetSegment;\n                        sameFlag = false;\n                        examResult.add(\n                                new HashMap<String, String>() {{\n                                    put(TopoloyExamContants.APPID, String.valueOf(appId));\n                                    put(TopoloyExamContants.TYPE, appType == ConstUtils.CACHE_TYPE_REDIS_CLUSTER ? TopoloyExamContants.REDIS_CLUSTER : TopoloyExamContants.REDIS_SENTINEL);\n                                    put(TopoloyExamContants.STATUS, TopoloyExamContants.NETSEGMENT_DESC);\n                                    put(TopoloyExamContants.DESC, MessageFormat.format(TopoloyExamContants.NETSEGMENT_FORMAT, netSegment, diffNetSegment));\n                                }}\n                        );\n                    }\n                }\n            }\n        }\n        return sameFlag;\n    }\n\n    /**\n     * redis-standalone类型应用故障检查\n     */\n    private void standaloneExam() {\n        for (Map.Entry<InstanceInfo, List<InstanceInfo>> entry : master_slaves.entrySet()) {\n            masterSlaveExam(TopoloyExamContants.REDIS_STANDALONE, entry.getKey(), entry.getValue());\n            break;\n        }\n    }\n\n    /**\n     * redis-cluster类型应用故障检查\n     */\n    private void clusterExam() {\n        Map<String, List<InstanceInfo>> masterMealIpMap = Maps.newHashMap();\n        for (Map.Entry<InstanceInfo, List<InstanceInfo>> entry : master_slaves.entrySet()) {\n            InstanceInfo masterInst = entry.getKey();\n            //1.master-slave检查\n            masterSlaveExam(TopoloyExamContants.REDIS_CLUSTER, masterInst, entry.getValue());\n\n            //for failover exam\n            String realIp = getRealIp(masterInst.getIp());\n            List<InstanceInfo> masterInstList = (List) MapUtils.getObject(masterMealIpMap, realIp, Lists.newArrayList());\n            masterInstList.add(masterInst);\n            masterMealIpMap.put(realIp, masterInstList);\n        }\n        //2.master节点数检查\n        if (masterMealIpMap.size() < 3) {\n            StringBuilder tmpBuilder = new StringBuilder();\n            for (Map.Entry<InstanceInfo, List<InstanceInfo>> entry : master_slaves.entrySet()) {\n                InstanceInfo masterInst = entry.getKey();\n                tmpBuilder.append(MessageFormat.format(TopoloyExamContants.INSTANCE_FORMAT, masterInst.getRoleDesc(), masterInst.getIp(), String.valueOf(masterInst.getPort()), getRealIp(masterInst.getIp())));\n            }\n            final String descStr = tmpBuilder.toString();\n            examResult.add(\n                    new HashMap<String, String>() {{\n                        put(TopoloyExamContants.APPID, String.valueOf(appId));\n                        put(TopoloyExamContants.TYPE, TopoloyExamContants.REDIS_CLUSTER);\n                        put(TopoloyExamContants.STATUS, TopoloyExamContants.NODESNUM_DESC);\n                        put(TopoloyExamContants.DESC, descStr);\n                    }}\n            );\n        }\n        //3.failover检查\n        failoverExam(masterMealIpMap);\n    }\n\n    /**\n     * redis-sentinel类型应用故障检查\n     */\n    private void sentinelExam() {\n        Set<String> sentinelRealIpSet = new HashSet<>();\n        for (InstanceInfo sentinelInst : sentinels) {\n            sentinelRealIpSet.add(getRealIp(sentinelInst.getIp()));\n        }\n        //sentinel result\n        if (sentinelRealIpSet.size() < 3) {\n            logger.info(marker, \"appId：{}, sentinel节点分布少于3台物理机\", appId);\n            StringBuilder tmpBuilder = new StringBuilder();\n            for (InstanceInfo sentinelInst : sentinels) {\n                tmpBuilder.append(MessageFormat.format(TopoloyExamContants.INSTANCE_FORMAT, sentinelInst.getRoleDesc(), sentinelInst.getIp(), String.valueOf(sentinelInst.getPort()), getRealIp(sentinelInst.getIp())));\n            }\n            final String descStr = tmpBuilder.toString();\n            examResult.add(\n                    new HashMap<String, String>() {{\n                        put(TopoloyExamContants.APPID, String.valueOf(appId));\n                        put(TopoloyExamContants.TYPE, TopoloyExamContants.REDIS_SENTINEL);\n                        put(TopoloyExamContants.STATUS, TopoloyExamContants.NODESNUM_DESC);\n                        put(TopoloyExamContants.DESC, descStr);\n                    }}\n            );\n        }\n        //master-slave result\n        for (Map.Entry<InstanceInfo, List<InstanceInfo>> entry : master_slaves.entrySet()) {\n            masterSlaveExam(TopoloyExamContants.REDIS_SENTINEL, entry.getKey(), entry.getValue());\n            break;\n        }\n    }\n\n    private void masterSlaveExam(String appType, InstanceInfo masterInst, List<InstanceInfo> slaveInstList) {\n\n        String masterIp = masterInst.getIp();\n        String port = String.valueOf(masterInst.getPort());\n        String masterRealIp = getRealIp(masterIp);\n\n        StringBuilder tmpBuilder = new StringBuilder(MessageFormat.format(TopoloyExamContants.CLUSTER_INSTANCE_FORMAT, masterInst.getRoleDesc(), masterIp, port, masterRealIp));\n        if (slaveNum != 0 && slaveInstList.size() == 0) {\n            //double check\n            BooleanEnum hasSlaves = redisCenter.hasSlaves(appId, masterInst.getIp(), masterInst.getPort());\n            if (hasSlaves == BooleanEnum.FALSE) {\n                final String descStr = tmpBuilder.toString();\n                examResult.add(\n                        new HashMap<String, String>() {{\n                            put(TopoloyExamContants.APPID, String.valueOf(appId));\n                            put(TopoloyExamContants.TYPE, appType);\n                            put(TopoloyExamContants.STATUS, TopoloyExamContants.SLAVE_NOT_EXIST);\n                            put(TopoloyExamContants.DESC, descStr);\n                        }}\n                );\n                logger.info(marker, \"appId：{}, master没有对应的slave\", appId);\n            }\n        }\n\n\n        boolean flag = false;\n        for (InstanceInfo slaveInst : slaveInstList) {\n            String slaveIp = slaveInst.getIp();\n            String slaveRealIp = getRealIp(slaveIp);\n            if (masterRealIp.equals(slaveRealIp)) {\n                flag = true;\n                tmpBuilder.append(MessageFormat.format(TopoloyExamContants.INSTANCE_FORMAT, InstanceRoleEnum.SLAVE.getInfo(), slaveIp, String.valueOf(slaveInst.getPort()), slaveRealIp));\n            }\n        }\n        if (flag) {\n            final String descStr = tmpBuilder.toString();\n            examResult.add(\n                    new HashMap<String, String>() {{\n                        put(TopoloyExamContants.APPID, String.valueOf(appId));\n                        put(TopoloyExamContants.TYPE, appType);\n                        put(TopoloyExamContants.STATUS, TopoloyExamContants.MASTER_SLAVE_DESC);\n                        put(TopoloyExamContants.DESC, descStr);\n                    }}\n            );\n        }\n    }\n\n    private void failoverExam(Map<String, List<InstanceInfo>> masterMealIpMap) {\n        for (Map.Entry<String, List<InstanceInfo>> entry : masterMealIpMap.entrySet()) {\n            String masterRealIp = entry.getKey();\n            StringBuilder tmpBuilder = new StringBuilder(\"\");\n            for (InstanceInfo masterInst : entry.getValue()) {\n                tmpBuilder.append(MessageFormat.format(TopoloyExamContants.CLUSTER_INSTANCE_FORMAT, masterInst.getRoleDesc(), masterInst.getIp(), String.valueOf(masterInst.getPort()), masterRealIp));\n            }\n            final String descStr = tmpBuilder.toString();\n            if (master_slaves.size() - entry.getValue().size() < master_slaves.size() / 2 + 1) {\n                examResult.add(\n                        new HashMap<String, String>() {{\n                            put(TopoloyExamContants.APPID, String.valueOf(appId));\n                            put(TopoloyExamContants.TYPE, TopoloyExamContants.REDIS_CLUSTER);\n                            put(TopoloyExamContants.STATUS, TopoloyExamContants.CLUSTER_FAILOVER_DESC);\n                            put(TopoloyExamContants.DESC, descStr);\n                        }}\n                );\n                examInfo.put(\"failoverStatus\", false);\n            }\n        }\n    }\n\n    private String getRealIp(String ip) {\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(ip);\n        if (machineInfo.getVirtual() == 1 && !StringUtils.isEmpty(machineInfo.getRealIp())) {\n            return machineInfo.getRealIp();\n        } else {\n            return ip;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/AppBigKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 17:36\n */\n@Component(\"AppBigKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppBigKeyTask extends BaseTask {\n    private long appId;\n    private long auditId;\n    private long fromBytes;\n    private long toBytes;\n    private int size;\n    private List<RedisServerNode> redisServerNodes;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2.1 创建delete key\n        taskStepList.add(\"createAppBigKeyTask\");\n        // 2.2 等到delete key完成\n        taskStepList.add(\"waitAppBigKeyTaskFinish\");\n        // 4. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        fromBytes = MapUtils.getLongValue(paramMap, \"fromBytes\");\n        if (fromBytes <= 0) {\n            logger.info(marker, \"task {} fromBytes is empty\", taskId);\n        }\n\n        size = MapUtils.getIntValue(paramMap, \"size\");\n        if (size <= -2) {\n            logger.error(marker, \"task {} size {} is wrong\", taskId, size);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"user paramMap node: {}\", redisServerNodesStr);\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 2.1.创建delete key子任务\n     */\n    public TaskFlowStatusEnum createAppBigKeyTask() {\n        redisServerNodes = buildRedisServerNodes(redisServerNodes, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //跳过已经执行完毕的节点\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addInstanceBigKeyTask(appId, host, port, fromBytes, toBytes, size, auditId, taskId);\n\n                redisServerNode.setTaskId(childTaskId);\n                logger.info(marker, \"appId {} {}:{} instanceBigKeyTask create successfully\", appId, host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{} instanceBigKeyTask create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待delkey子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitAppBigKeyTaskFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_DIAGNOSTIC_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} instanceBigKeyTask execute fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} instanceBigKeyTask execute successfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"应用(%s-%s) appBigKeyTask 完成\", appDesc.getAppId(), appDesc.getName()));\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/AppDelKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 17:36\n */\n@Component(\"AppDelKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppDelKeyTask extends BaseTask {\n    private long appId;\n\n    private long auditId;\n\n    private String pattern;\n\n    private List<RedisServerNode> redisServerNodes;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2.1 创建delete key\n        taskStepList.add(\"createAppDelKeyTask\");\n        // 2.2 等到delete key完成\n        taskStepList.add(\"waitAppDelKeyTaskFinish\");\n        // 4. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        pattern = MapUtils.getString(paramMap, \"pattern\");\n        if (StringUtils.isBlank(pattern)) {\n            logger.info(marker, \"task {} pattern is empty\", taskId);\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"user paramMap node: {}\", redisServerNodesStr);\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 2.1.创建delete key子任务\n     */\n    public TaskFlowStatusEnum createAppDelKeyTask() {\n        redisServerNodes = buildRedisServerNodes(redisServerNodes, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //跳过已经执行完毕的节点\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addInstanceDelKeyTask(appId, host, port, pattern, auditId, taskId);\n\n                redisServerNode.setTaskId(childTaskId);\n                logger.info(marker, \"appId {} {}:{} instanceDelKeyTask create successfully\", appId, host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{} instanceDelKeyTask create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待delkey子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitAppDelKeyTaskFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_DIAGNOSTIC_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} instanceDelKeyTask execute fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} instanceDelKeyTask execute successfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"应用(%s-%s)的del key完成\", appDesc.getAppId(), appDesc.getName()));\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/AppHotKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 17:36\n */\n@Component(\"AppHotKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppHotKeyTask extends BaseTask {\n    private long appId;\n\n    private long auditId;\n\n    private String command;\n\n    private List<RedisServerNode> redisServerNodes;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2.1 创建delete key\n        taskStepList.add(\"createAppHotKeyTask\");\n        // 2.2 等到delete key完成\n        taskStepList.add(\"waitAppHotKeyTaskFinish\");\n        // 4. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        command = MapUtils.getString(paramMap, \"command\");\n        if (StringUtils.isBlank(command)) {\n            logger.error(marker, \"task {} command {} is wrong\", taskId, command);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"user paramMap node: {}\", redisServerNodesStr);\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 2.1.创建hotkey子任务\n     */\n    public TaskFlowStatusEnum createAppHotKeyTask() {\n        redisServerNodes = buildRedisServerNodes(redisServerNodes, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //跳过已经执行完毕的节点\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addInstanceHotKeyTask(appId, host, port, command, auditId, taskId);\n\n                redisServerNode.setTaskId(childTaskId);\n                logger.info(marker, \"appId {} {}:{} instanceHotKeyTask create successfully\", appId, host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{} instanceHotKeyTask create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待hotkey子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitAppHotKeyTaskFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_DIAGNOSTIC_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} instanceHotKeyTask execute fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} instanceHotKeyTask execute successfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"应用(%s-%s)的hotkey完成\", appDesc.getAppId(), appDesc.getName()));\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/AppIdleKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 17:36\n */\n@Component(\"AppIdleKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppIdleKeyTask extends BaseTask {\n    private long appId;\n    private long idleTime;\n    private int size;\n    private long auditId;\n    private List<RedisServerNode> redisServerNodes;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2.1 创建delete key\n        taskStepList.add(\"createAppIdleKeyTask\");\n        // 2.2 等到delete key完成\n        taskStepList.add(\"waitAppIdleKeyTaskFinish\");\n        // 4. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        idleTime = MapUtils.getIntValue(paramMap, \"idleTime\");\n        if (idleTime <= 0) {\n            logger.error(marker, \"task {} idleTime {} is wrong\", taskId, idleTime);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        size = MapUtils.getIntValue(paramMap, \"size\");\n        if (size <= -2) {\n            logger.error(marker, \"task {} size {} is wrong\", taskId, size);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"user paramMap node: {}\", redisServerNodesStr);\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 2.1.创建delete key子任务\n     */\n    public TaskFlowStatusEnum createAppIdleKeyTask() {\n        redisServerNodes = buildRedisServerNodes(redisServerNodes, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //跳过已经执行完毕的节点\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addInstanceIdleKeyTask(appId, host, port, idleTime, size, auditId, taskId);\n\n                redisServerNode.setTaskId(childTaskId);\n                logger.info(marker, \"appId {} {}:{} instanceIdleKeyTask create successfully\", appId, host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{} instanceIdleKeyTask create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待delkey子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitAppIdleKeyTaskFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_DIAGNOSTIC_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} instanceIdleKeyTask execute fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} instanceIdleKeyTask execute successfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"应用(%s-%s) appIdleKeyTask 完成\", appDesc.getAppId(), appDesc.getName()));\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/AppScanCleanKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.task.constant.ScanCleanConstants;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: zengyizhao\n * @Date: 2022/5/26\n */\n@Component(\"AppScanCleanKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppScanCleanKeyTask extends BaseTask {\n    private long appId;\n\n    private long auditId;\n\n    private String pattern;\n\n    private String nodes;\n\n    private List<InstanceInfo> instanceList;\n\n    private List<RedisServerNode> redisServerNodes;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2. redis server big key分析\n        taskStepList.add(\"createAppScanCleanKeyTask\");\n        taskStepList.add(\"waitAppScanCleanKeyTaskFinish\");\n        // 3. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        pattern = MapUtils.getString(paramMap, \"pattern\");\n        if (StringUtils.isBlank(pattern)) {\n            logger.info(marker, \"task {} pattern is empty\", taskId);\n        }\n\n        instanceList = appService.getAppOnlineInstanceInfo(appId);\n\n        //redis server list\n        nodes = MapUtils.getString(paramMap, ScanCleanConstants.POINTED_NODES);\n        Integer operateType = MapUtils.getInteger(paramMap, ScanCleanConstants.OPERATE_TYPE);\n        if(operateType == null){\n            logger.error(marker, \"task {} operateType is illegal\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        boolean nodesAndJudge = getNodesAndJudge(operateType);\n        if(!nodesAndJudge){\n            logger.error(marker, \"task {} pointed nodes is illegal\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (CollectionUtils.isEmpty(redisServerNodes)) {\n            logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        logger.info(marker, \"task {} user paramMap node: {}\", taskId, nodes);\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.1.创建scan clean key子任务\n     */\n    public TaskFlowStatusEnum createAppScanCleanKeyTask() {\n        redisServerNodes = buildRedisServerNodes(redisServerNodes, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n\n        // 每个server的dbsize\n        Map<String, Long> redisServerDbSizeMap = new HashMap<>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long dbSize = redisCenter.getDbSize(appId, redisServerNode.getIp(), redisServerNode.getPort());\n            logger.info(marker, \"appId {} {}:{} dbSize is {} \", appId, host, port, dbSize);\n            redisServerDbSizeMap.put(host + \":\" + port, dbSize);\n        }\n\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //跳过已经执行完毕的节点\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long dbSize = redisServerDbSizeMap.get(host + \":\" + port);\n                long childTaskId = taskService.addInstanceScanCleanKeyTask(appId, auditId, host, port, paramMap, taskId);\n                redisServerNode.setTaskId(childTaskId);\n                logger.info(marker, \"appId {} {}:{} dbsize:{} redis scanCleanKeyTask create successfully\", appId, host, port, dbSize);\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{}  redis scanCleanKeyTask create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待scanCleankey子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitAppScanCleanKeyTaskFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, ScanCleanConstants.REDIS_SCAN_CLEAN_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} instanceScanKeyTask execute fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} instanceScanKeyTask execute successfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"应用(%s-%s)的scan clean key完成\", appDesc.getAppId(), appDesc.getName()));\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n    private boolean getNodesAndJudge(int operateType){\n        boolean judgePointedNodeFlag = true;\n        List<InstanceInfo> instanceInfoList = new ArrayList<>();\n        redisServerNodes = new ArrayList<>();\n        if(ScanCleanConstants.NODE_TYPE_MASTER.equals(nodes)){\n            instanceInfoList = instanceList.stream().filter(instanceInfo -> InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc())).collect(Collectors.toList());\n\n        }else if(ScanCleanConstants.NODE_TYPE_SLAVE.equals(nodes)){\n            instanceInfoList = instanceList.stream().filter(instanceInfo -> InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())).collect(Collectors.toList());\n        }else if(nodes != null){\n            List<InstanceInfo> instanceInfos = new ArrayList<>();\n            String[] split = nodes.split(\",\");\n            for (String nodeStr : split){\n                instanceList.forEach(instanceInfo -> {\n                    if(instanceInfo.getHostPort().equals(nodeStr)){\n                        instanceInfos.add(instanceInfo);\n                    }\n                });\n            }\n            instanceInfoList = instanceInfos;\n        }\n        instanceInfoList.forEach(instanceInfo -> redisServerNodes.add(new RedisServerNode(instanceInfo.getIp(), instanceInfo.getPort())));\n        if(operateType == 1 || operateType == 2){\n            Optional<InstanceInfo> slaveExist = instanceInfoList.stream().filter(instanceInfo -> InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())).findFirst();\n            if(slaveExist.isPresent()){\n                judgePointedNodeFlag = false;\n            }\n        }\n        //仅扫描时，不增加节点角色类型校验\n//        else if(operateType == 0){\n//            Optional<InstanceInfo> masterExist = instanceInfoList.stream().filter(instanceInfo -> InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc())).findFirst();\n//            if(masterExist.isPresent()){\n//                judgePointedNodeFlag = false;\n//            }\n//        }\n        return judgePointedNodeFlag;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/AppScanKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 17:36\n */\n@Component(\"AppScanKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppScanKeyTask extends BaseTask {\n    private long appId;\n\n    private long auditId;\n\n    private String pattern;\n\n    private int size;\n\n    private List<RedisServerNode> redisServerNodes;\n\n    private final static long SCANKEY_SLEEP_BASE = 20000000;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2. redis server big key分析\n        taskStepList.add(\"createAppScanKeyTask\");\n        taskStepList.add(\"waitAppScanKeyTaskFinish\");\n        // 3. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        pattern = MapUtils.getString(paramMap, \"pattern\");\n        if (StringUtils.isBlank(pattern)) {\n            logger.info(marker, \"task {} pattern is empty\", taskId);\n        }\n\n        size = MapUtils.getIntValue(paramMap, \"size\");\n        if (size <= 0) {\n            logger.error(marker, \"task {} size {} is wrong\", taskId, size);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"user paramMap node: {}\", redisServerNodesStr);\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 2.1.创建scan key子任务\n     */\n    public TaskFlowStatusEnum createAppScanKeyTask() {\n        redisServerNodes = buildRedisServerNodes(redisServerNodes, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n\n        // 每个server的dbsize\n        Map<String, Long> redisServerDbSizeMap = new HashMap<>();\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long dbSize = redisCenter.getDbSize(appId, redisServerNode.getIp(), redisServerNode.getPort());\n            logger.info(marker, \"appId {} {}:{} dbSize is {} \", appId, host, port, dbSize);\n            redisServerDbSizeMap.put(host + \":\" + port, dbSize);\n        }\n\n        long keyCounter = 0;\n        int factor = 1;\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //跳过已经执行完毕的节点\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n            long sleepCounter = factor * SCANKEY_SLEEP_BASE;\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long dbSize = redisServerDbSizeMap.get(host + \":\" + port);\n                keyCounter += dbSize;\n\n                long childTaskId = taskService.addInstanceScanKeyTask(appId, auditId, host, port, pattern, size, taskId);\n                redisServerNode.setTaskId(childTaskId);\n                logger.info(marker, \"appId {} {}:{}  redis scanKeyTask create successfully\", appId, host, port);\n\n                // 超额就sleep\n                if (keyCounter > sleepCounter) {\n                    factor++;\n                    sleepSeconds(120);\n                }\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{}  redis scanKeyTask create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待scankey子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitAppScanKeyTaskFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_DIAGNOSTIC_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} instanceScanKeyTask execute fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} instanceScanKeyTask execute successfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"应用(%s-%s)的scan key完成\", appDesc.getAppId(), appDesc.getName()));\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/AppSlotAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 17:36\n */\n@Component(\"AppSlotAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class AppSlotAnalysisTask extends BaseTask {\n    private long appId;\n\n    private long auditId;\n\n    private List<RedisServerNode> redisServerNodes;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 1. 检查集群参数\n        taskStepList.add(\"checkAppParam\");\n        // 2.1 创建 appSlotAnalysisTask\n        taskStepList.add(\"createAppSlotAnalysisTask\");\n        // 2.2 等到 ppSlotAnalysisTask完成\n        taskStepList.add(\"waitAppSlotAnalysisTaskFinish\");\n        // 4. 工单审批\n        taskStepList.add(\"updateAudit\");\n        return taskStepList;\n    }\n\n    /**\n     * 0.初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //redis server list\n        String redisServerNodesStr = MapUtils.getString(paramMap, TaskConstants.REDIS_SERVER_NODES_KEY);\n        if (StringUtils.isNotBlank(redisServerNodesStr)) {\n            redisServerNodes = JSONArray.parseArray(redisServerNodesStr, RedisServerNode.class);\n            if (CollectionUtils.isEmpty(redisServerNodes)) {\n                logger.error(marker, \"task {} redisServerNodes is empty\", taskId);\n                return TaskFlowStatusEnum.ABORT;\n            }\n            logger.info(marker, \"user paramMap node: {}\", redisServerNodesStr);\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 1.检查应用参数\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkAppParam() {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(marker, \"appId {} appDesc is null\", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if (!appDesc.isOnline()) {\n            logger.error(marker, \"appId {} is must be online, \", appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n\n    /**\n     * 2.1.创建appSlotAnalysisTask子任务\n     */\n    public TaskFlowStatusEnum createAppSlotAnalysisTask() {\n        redisServerNodes = buildRedisServerNodes(redisServerNodes, appId);\n        paramMap.put(TaskConstants.REDIS_SERVER_NODES_KEY, redisServerNodes);\n\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            //跳过已经执行完毕的节点\n            if (redisServerNode.getTaskId() > 0) {\n                continue;\n            }\n\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n            try {\n                long childTaskId = taskService.addInstanceSlotAnalysisTask(appId, host, port, auditId, taskId);\n\n                redisServerNode.setTaskId(childTaskId);\n                logger.info(marker, \"appId {} {}:{} instanceSlotAnalysisTask create successfully\", appId, host, port);\n            } catch (Exception e) {\n                logger.error(marker, \"appId {} {}:{} instanceSlotAnalysisTask create fail\", appId, host, port);\n                logger.error(marker, e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.2.等待appSlotAnalysisTask子任务完成\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum waitAppSlotAnalysisTaskFinish() {\n        for (RedisServerNode redisServerNode : redisServerNodes) {\n            String host = redisServerNode.getIp();\n            int port = redisServerNode.getPort();\n\n            long childTaskId = redisServerNode.getTaskId();\n            TaskFlowStatusEnum taskFlowStatusEnum = waitTaskFinish(childTaskId, TaskConstants.REDIS_SERVER_DIAGNOSTIC_TIMEOUT);\n            if (taskFlowStatusEnum.equals(TaskFlowStatusEnum.ABORT)) {\n                logger.error(marker, \"appId {} {}:{} instanceSlotAnalysisTask execute fail\", appId, host, port);\n                return TaskFlowStatusEnum.ABORT;\n            } else {\n                logger.info(marker, \"appId {} {}:{} instanceSlotAnalysisTask execute successfully\", appId, host, port);\n            }\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.通过初审：资源分配\n     */\n    public TaskFlowStatusEnum updateAudit() {\n        try {\n            AppDesc appDesc = appService.getByAppId(appId);\n            appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_PASS.value());\n            StringBuffer content = new StringBuffer();\n            content.append(String.format(\"应用(%s-%s)slotAnalysisTask完成\", appDesc.getAppId(), appDesc.getName()));\n\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/InstanceBigKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.redis.util.PipelineUtil;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.Pair;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.nio.charset.Charset;\nimport java.text.MessageFormat;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 15:53\n */\n@Component(\"InstanceBigKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class InstanceBigKeyTask extends BaseTask {\n\n    private String host;\n    private int port;\n    private long appId;\n    private long fromBytes;\n    private long toBytes;\n    private int size;\n    private long auditId;\n    private long parentTaskId;\n\n    private final static int SCAN_COUNT = 100;\n    private final static String CONDITION_TEMPLATE = \"fromBytes:{0}K;size:{1}\";\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // delete key\n        taskStepList.add(\"bigKey\");\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        fromBytes = MapUtils.getLongValue(paramMap, \"fromBytes\");\n        if (fromBytes <= 0) {\n            logger.info(marker, \"task {} fromBytes is empty\", taskId);\n        }\n\n        size = MapUtils.getIntValue(paramMap, \"size\");\n        if (size <= -2) {\n            logger.error(marker, \"task {} size {} is wrong\", taskId, size);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        parentTaskId = MapUtils.getLongValue(paramMap, \"parentTaskId\");\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.检查run以及slave\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.scanKey\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum bigKey() {\n        DiagnosticTaskRecord record = new DiagnosticTaskRecord();\n        record.setAppId(appId);\n        record.setAuditId(auditId);\n        String hostPost = host + \":\" + port;\n        record.setNode(hostPost);\n        String condition = MessageFormat.format(CONDITION_TEMPLATE, String.valueOf(fromBytes), size);\n        record.setDiagnosticCondition(condition);\n        record.setTaskId(taskId);\n        record.setParentTaskId(parentTaskId);\n        record.setType(DiagnosticTypeEnum.BIG_KEY.getType());\n        record.setStatus(0);\n        diagnosticTaskRecordDao.insertDiagnosticTaskRecord(record);\n        long recordId = record.getId();\n\n        /**\n         * 扫描bigkey，计时开始*/\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 1, System.currentTimeMillis() - startTime);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            // scan参数\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n            ScanParams scanParams = new ScanParams().count(SCAN_COUNT);\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n\n            Map<String, String> result = new HashMap<>();\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    Pipeline pipeline = jedis.pipelined();\n                    if (CollectionUtils.isNotEmpty(keyList)) {\n                        List<String> keyStrList = keyList.stream().map(byteKey -> new String(byteKey)).collect(Collectors.toList());\n                        keyStrList.stream().forEach(keyStr -> PipelineUtil.memoryUsage(pipeline, keyStr));\n                        List<Object> memObjectList;\n                        try {\n                            memObjectList = pipeline.syncAndReturnAll();\n                        } catch (JedisRedirectionException e) {\n                            continue; // ignore\n                        }\n                        List<Object> memUsedList = memObjectList;\n                        Map<String, String> keyMemMap = IntStream.range(0, keyList.size())\n                                .filter(i -> !\"none\".equalsIgnoreCase(String.valueOf(memUsedList.get(i)))\n                                        && (memUsedList.get(i) instanceof Long)\n                                        && Long.valueOf(String.valueOf(memUsedList.get(i))) >= fromBytes * 1024)\n                                .mapToObj(i -> new Pair<>(keyStrList.get(i), String.valueOf(memUsedList.get(i))))\n                                .collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n                        result.putAll(keyMemMap);\n                    }\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already scan&check key {}% {} key \", appId, host, port, curSplit * 10, count);\n                        curSplit++;\n                    }\n                    // @TODO暂时写死\n                    TimeUnit.MILLISECONDS.sleep(10);\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n\n                    if ((size > 0 ? result.size() >= size : false) || Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            //结果存redis\n            String redisBigKey = ConstUtils.getInstanceBigKey(taskId, hostPost);\n            assistRedisService.del(redisBigKey);\n            assistRedisService.hmset(redisBigKey, result);\n            long cost = System.currentTimeMillis() - startTime;\n            /**\n             * 计时结束*/\n            //更新记录\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, redisBigKey, 1, cost);\n\n            logger.info(marker, \"{} {}:{} instanceBigKeyTask successfully, cost time is {} ms, total key is {}\", appId, host, port, cost, count);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, \"redis-cli -h {} -p {} admin auth error\", host, port);\n            logger.error(marker, \"instanceBigKeyTask appId {} {}:{}  error:\" + e.getMessage(), appId, host, port, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/InstanceDelKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.constant.SymbolConstant;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum;\nimport com.sohu.cache.util.StringUtil;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.nio.charset.Charset;\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 15:53\n */\n@Component(\"InstanceDelKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class InstanceDelKeyTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private String pattern;\n\n    /**\n     * 支持多个pattern，将pattern按照逗号分隔\n     */\n    private List<String> patterns;\n\n    private long auditId;\n\n    private long parentTaskId;\n\n    private final static int SCAN_COUNT = 100;\n\n    private final static String CONDITION_TEMPLATE = \"pattern:{0}\";\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // delete key\n        taskStepList.add(\"delKey\");\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskStepFlowEnum.TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        pattern = MapUtils.getString(paramMap, \"pattern\");\n        patterns = new ArrayList<>();\n        if (StringUtils.isBlank(pattern)) {\n            logger.info(marker, \"task {} pattern is empty\", taskId);\n        } else {\n            if(pattern.contains(SymbolConstant.COMMA)){\n                patterns = Arrays.asList(pattern.split(\",\"));\n            }else{\n                patterns.add(pattern);\n            }\n        }\n\n        parentTaskId = MapUtils.getLongValue(paramMap, \"parentTaskId\");\n\n        return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.检查run以及slave\n     *\n     * @return\n     */\n    public TaskStepFlowEnum.TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n        return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.scanKey\n     *\n     * @return\n     */\n    public TaskStepFlowEnum.TaskFlowStatusEnum delKey() {\n        DiagnosticTaskRecord record = new DiagnosticTaskRecord();\n        record.setAppId(appId);\n        record.setAuditId(auditId);\n        String hostPost = host + \":\" + port;\n        record.setNode(hostPost);\n        record.setDiagnosticCondition(MessageFormat.format(CONDITION_TEMPLATE, patterns));\n        record.setTaskId(taskId);\n        record.setParentTaskId(parentTaskId);\n        record.setType(DiagnosticTypeEnum.DEL_KEY.getType());\n        record.setStatus(0);\n        diagnosticTaskRecordDao.insertDiagnosticTaskRecord(record);\n        long recordId = record.getId();\n\n        /**\n         * 扫描删除，计时开始*/\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 1, System.currentTimeMillis() - startTime);\n                return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            // scan参数\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n\n            int needScanTimes = 1;\n            if(patterns.size() > 0){\n                needScanTimes = patterns.size();\n            }\n            for(int i = 0; i < needScanTimes; i++){\n                String curPattern = null;\n                if(patterns.size() > 0){\n                    curPattern = patterns.get(i);\n                }\n                ScanParams scanParams = StringUtil.isBlank(curPattern) ?\n                        new ScanParams().count(SCAN_COUNT) :\n                        new ScanParams().match(curPattern).count(SCAN_COUNT);\n                while (true) {\n                    try {\n                        ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                        cursor = scanResult.getCursorAsBytes();\n                        List<byte[]> keyList = scanResult.getResult();\n\n                        //pipeline unlink\n                        Pipeline pipeline = jedis.pipelined();\n                        keyList.stream().forEach(key -> pipeline.unlink(key));\n                        List<Object> unlinkList;\n                        try {\n                            unlinkList = pipeline.syncAndReturnAll();\n                        } catch (JedisRedirectionException e) {\n                            continue;// ignoreu\n                        }\n\n                        count += keyList.size();\n                        if (count > dbSize / totalSplit * curSplit) {\n                            logger.info(marker, \"{} {}:{} has already delete {}% {} key \", appId, host, port, curSplit * 10, count);\n                            curSplit++;\n                        }\n                        // @TODO暂时写死\n                        TimeUnit.MILLISECONDS.sleep(10);\n                    } catch (Exception e) {\n                        logger.error(marker, e.getMessage(), e);\n                    } finally {\n                        //防止无限循环\n                        if (Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                            break;\n                        }\n                    }\n                }\n            }\n\n            long cost = System.currentTimeMillis() - startTime;\n            /**\n             * 计时结束*/\n            //更新记录\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, String.valueOf(count), 1, cost);\n            logger.info(marker, \"{} {}:{} del key successfully, cost time is {} ms, total key is {}\", appId, host, port, cost, count);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, \"redis-cli -h {} -p {} admin auth error\", host, port);\n            logger.error(marker, \"del key appId {} {}:{}  error:\" + e.getMessage(), appId, host, port, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/InstanceHotKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\n\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 15:53\n */\n@Component(\"InstanceHotKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class InstanceHotKeyTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private String command;\n\n    private long auditId;\n\n    private long parentTaskId;\n\n    private final static String CONDITION_TEMPLATE = \"command:{0}\";\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // delete key\n        taskStepList.add(\"hotKey\");\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskStepFlowEnum.TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        command = MapUtils.getString(paramMap, \"command\");\n        if (StringUtils.isBlank(command)) {\n            logger.error(marker, \"task {} command {} is wrong\", taskId, command);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n\n        parentTaskId = MapUtils.getLongValue(paramMap, \"parentTaskId\");\n\n        return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.检查run以及slave\n     *\n     * @return\n     */\n    public TaskStepFlowEnum.TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        }\n        return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.scanKey\n     *\n     * @return\n     */\n    public TaskStepFlowEnum.TaskFlowStatusEnum hotKey() {\n        DiagnosticTaskRecord record = new DiagnosticTaskRecord();\n        record.setAppId(appId);\n        record.setAuditId(auditId);\n        String hostPost = host + \":\" + port;\n        record.setNode(hostPost);\n        record.setDiagnosticCondition(MessageFormat.format(CONDITION_TEMPLATE, command));\n        record.setTaskId(taskId);\n        record.setParentTaskId(parentTaskId);\n        record.setType(DiagnosticTypeEnum.HOT_KEY.getType());\n        record.setStatus(0);\n        diagnosticTaskRecordDao.insertDiagnosticTaskRecord(record);\n        long recordId = record.getId();\n\n        /**\n         * 扫描删除，计时开始*/\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 1, System.currentTimeMillis() - startTime);\n                return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            String result = redisCenter.executeAdminCommand(appId, host, port, command, 60000 * 6);\n\n            //结果存redis\n            String redisHotKey = ConstUtils.getInstanceHotKey(taskId, hostPost);\n            assistRedisService.del(redisHotKey);\n            assistRedisService.set(redisHotKey, result);\n            long cost = System.currentTimeMillis() - startTime;\n            /**\n             * 计时结束*/\n            //更新记录\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, redisHotKey, 1, cost);\n\n            logger.info(marker, \"{} {}:{} hotkey successfully, cost time is {} ms\", appId, host, port, cost);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, \"redis-cli -h {} -p {} admin auth error\", host, port);\n            logger.error(marker, \"del key appId {} {}:{}  error:\" + e.getMessage(), appId, host, port, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            return TaskStepFlowEnum.TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/InstanceIdleKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.redis.util.PipelineUtil;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.*;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.nio.charset.Charset;\nimport java.text.MessageFormat;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 15:53\n */\n@Component(\"InstanceIdleKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class InstanceIdleKeyTask extends BaseTask {\n\n    private String host;\n    private int port;\n    private long appId;\n    private long idleTime;\n    private int size;\n    private long auditId;\n    private long parentTaskId;\n\n    private final static int SCAN_COUNT = 100;\n    private final static String CONDITION_TEMPLATE = \"idleDay:{0};size{1}\";\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // delete key\n        taskStepList.add(\"idleKey\");\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        idleTime = MapUtils.getIntValue(paramMap, \"idleTime\");\n        if (idleTime <= 0) {\n            logger.error(marker, \"task {} idleTime {} is wrong\", taskId, idleTime);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        size = MapUtils.getIntValue(paramMap, \"size\");\n        if (size <= -2) {\n            logger.error(marker, \"task {} size {} is wrong\", taskId, size);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        parentTaskId = MapUtils.getLongValue(paramMap, \"parentTaskId\");\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.检查run以及slave\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.scanKey\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum idleKey() {\n        DiagnosticTaskRecord record = new DiagnosticTaskRecord();\n        record.setAppId(appId);\n        record.setAuditId(auditId);\n        String hostPost = host + \":\" + port;\n        record.setNode(hostPost);\n        record.setDiagnosticCondition(MessageFormat.format(CONDITION_TEMPLATE, idleTime, size));\n        record.setTaskId(taskId);\n        record.setParentTaskId(parentTaskId);\n        record.setType(DiagnosticTypeEnum.IDLE_KEY.getType());\n        record.setStatus(0);\n        diagnosticTaskRecordDao.insertDiagnosticTaskRecord(record);\n        long recordId = record.getId();\n\n        /**\n         * 扫描删除，计时开始*/\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 1, System.currentTimeMillis() - startTime);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            // scan参数\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n            ScanParams scanParams = new ScanParams().count(SCAN_COUNT);\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n\n            Map<String, String> result = new HashMap<>();\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    Pipeline pipeline = jedis.pipelined();\n                    if (CollectionUtils.isNotEmpty(keyList)) {\n                        List<String> keyStrList = keyList.stream().map(byteKey -> new String(byteKey)).collect(Collectors.toList());\n                        keyStrList.stream().forEach(keyStr -> PipelineUtil.objectIdletime(pipeline, keyStr));\n                        List<Object> idletimeList;\n                        try {\n                            idletimeList = pipeline.syncAndReturnAll();\n                        } catch (JedisRedirectionException e) {\n                            continue; // ignore\n                        }\n\n                        List<Long> idletimeResList = new ArrayList<>();\n                        if(CollectionUtils.isNotEmpty(idletimeList)){\n                            idletimeList.stream().filter(idleTime -> (idleTime instanceof Long)).forEach(idleTime -> idletimeResList.add((Long)idleTime));\n                        }\n                        Map<String, String> keyIdleMap = new HashMap<>();\n                        IntStream.range(0, keyStrList.size())\n                                .filter(i -> idletimeResList.get(i) != null)\n                                .forEach(i -> {\n                                    if(idletimeResList.get(i) > idleTime * 3600 * 24){\n                                        keyIdleMap.put(keyStrList.get(i), String.valueOf(idletimeResList.get(i)));\n                                    }\n                                });\n                        result.putAll(keyIdleMap);\n                    }\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already anlysis {}% {} key \", appId, host, port,\n                                curSplit * 10, count);\n                        curSplit++;\n                    }\n                    // @TODO暂时写死\n                    TimeUnit.MILLISECONDS.sleep(10);\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if ((size > 0 ? result.size() >= size : false) || Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            //结果存redis\n            String redisIdleKey = ConstUtils.getInstanceIdleKey(taskId, hostPost);\n            assistRedisService.del(redisIdleKey);\n            assistRedisService.hmset(redisIdleKey, result);\n            long cost = System.currentTimeMillis() - startTime;\n            /**\n             * 计时结束*/\n            //更新记录\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, redisIdleKey, 1, cost);\n\n            logger.info(marker, \"{} {}:{} instanceIdleKeyTask successfully, cost time is {} ms, total key is {}\", appId, host, port, cost, count);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, \"redis-cli -h {} -p {} admin auth error\", host, port);\n            logger.error(marker, \"instanceIdleKeyTask appId {} {}:{}  error:\" + e.getMessage(), appId, host, port, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/InstanceScanCleanKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.ScanCleanConstants;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.assertj.core.util.Lists;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.*;\nimport redis.clients.jedis.util.JedisClusterCRC16;\n\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: zengyizhao\n * @Date: 2022/5/25\n */\n@Component(\"InstanceScanCleanKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class InstanceScanCleanKeyTask extends BaseTask {\n\n    private long appId;\n\n    private long auditId;\n\n    private long parentTaskId;\n\n    private String host;\n\n    private int port;\n\n    private String operateType;\n\n    private String pattern;\n\n    private int perCount;\n\n    private Long ttlLess;\n\n    private Long ttlMore;\n\n    private Integer ttlResetMore;\n\n    private Integer ttlResetLess;\n\n    private Integer index;\n\n    private Integer compareType;\n\n    private Long compareValue;\n\n    private String authPwd;\n\n    private String hostPort;\n\n    private String condition;\n\n    private Random random;\n\n    private Pattern patternExp = Pattern.compile(\"[0-9]+\");\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n\n        //检查匹配分析\n        if(ScanCleanConstants.OPERATE_ANALYSE.equals(paramMap.get(ScanCleanConstants.OPERATE_TYPE))){\n            taskStepList.add(\"analyseKeyMatch\");\n        }\n\n        //分析清理\n        if(ScanCleanConstants.OPERATE_CLEAN.equals(paramMap.get(ScanCleanConstants.OPERATE_TYPE))){\n            taskStepList.add(\"scanClean\");\n        }\n\n        //分析重置ttl\n        if(ScanCleanConstants.OPERATE_TTL_RESET.equals(paramMap.get(ScanCleanConstants.OPERATE_TYPE))){\n            taskStepList.add(\"scanResetTtl\");\n        }\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        operateType = MapUtils.getString(paramMap, ScanCleanConstants.OPERATE_TYPE);\n        if (!(ScanCleanConstants.OPERATE_ANALYSE.equals(operateType)\n                || ScanCleanConstants.OPERATE_CLEAN.equals(operateType)\n                || ScanCleanConstants.OPERATE_TTL_RESET.equals(operateType))) {\n            logger.error(marker, \"task {} isClean {} is wrong\", taskId, operateType);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        pattern = MapUtils.getString(paramMap, ScanCleanConstants.PATTERN);\n        if (StringUtils.isBlank(pattern)) {\n            logger.error(marker, \"task {} pattern is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        this.compilePatternAndSet();\n        AppDesc appDesc = appService.getByAppId(appId);\n        authPwd = appDesc.getAppPassword();\n\n        ttlLess = MapUtils.getLong(paramMap, ScanCleanConstants.TTL_LESS);\n        ttlMore = MapUtils.getLong(paramMap, ScanCleanConstants.TTL_MORE);\n        if(ttlLess != null && ttlMore != null && ttlLess < ttlMore){\n            logger.error(marker, \"task {} ttl filter value more/less condition fault\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        ttlResetLess = MapUtils.getInteger(paramMap, ScanCleanConstants.TTL_RESET_LESS);\n        ttlResetMore = MapUtils.getInteger(paramMap, ScanCleanConstants.TTL_RESET_MORE);\n        if(ttlResetLess != null && ttlResetMore != null && ttlResetLess < ttlResetMore){\n            logger.error(marker, \"task {} ttl reset value more and less condition fault\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        if(ttlResetLess != null && ttlResetMore != null){\n            random = new Random();\n        }\n        perCount = MapUtils.getIntValue(paramMap, ScanCleanConstants.PER_COUNT, 100);\n        parentTaskId = MapUtils.getLongValue(paramMap, \"parentTaskId\");\n        hostPort = host + \":\" + port;\n        getCondition();\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    private void compilePatternAndSet(){\n        Pattern lessMatchPattern = Pattern.compile(\"@Less\\\\{[0-9]+\\\\}Less@\");\n        Matcher lessMatcher = lessMatchPattern.matcher(pattern);\n        boolean foundFlag = lessMatcher.find();\n        if(foundFlag){\n            int start = lessMatcher.start();\n            int end = lessMatcher.end();\n            compareType = ScanCleanConstants.COMPARE_TYPE_LESS_THAN;\n            compareValue = Long.valueOf(pattern.substring(start + 6, end - 6));\n            pattern = pattern.substring(0, start) + \"[0-9]*[0-9]\" + pattern.substring(end);\n            index = start;\n        }else{\n            Pattern moreMatchPattern = Pattern.compile(\"@More\\\\{[0-9]+\\\\}More@\");\n            Matcher moreMatcher = moreMatchPattern.matcher(pattern);\n            foundFlag = moreMatcher.find();\n            if(foundFlag){\n                int start = moreMatcher.start();\n                int end = moreMatcher.end();\n                compareType = ScanCleanConstants.COMPARE_TYPE_MORE_THAN;\n                compareValue = Long.valueOf(pattern.substring(start + 6, end - 6));\n                pattern = pattern.substring(0, start) + \"[0-9]*[0-9]\" + pattern.substring(end);\n                index = start;\n            }\n        }\n    }\n\n    private void getCondition(){\n        StringBuffer stringBuffer = new StringBuffer();\n        if(ScanCleanConstants.OPERATE_ANALYSE.equals(operateType)){\n            stringBuffer.append(\"扫描分析： \");\n        }else if(ScanCleanConstants.OPERATE_CLEAN.equals(operateType)){\n            stringBuffer.append(\"分析清理： \");\n        }else if(ScanCleanConstants.OPERATE_TTL_RESET.equals(operateType)){\n            stringBuffer.append(\"重置ttl： \");\n        }\n        stringBuffer.append(\"匹配键=\").append(pattern);\n        stringBuffer.append(\",每次扫描数量=\").append(perCount);\n        if(compareValue != null){\n            stringBuffer.append(\",精确筛选条件=(\").append(\"key从第\").append(index).append(\"字符起数字匹配值\");\n            if(ScanCleanConstants.COMPARE_TYPE_LESS_THAN.equals(compareType)){\n                stringBuffer.append(\" < \");\n            }else {\n                stringBuffer.append(\" > \");\n            }\n            stringBuffer.append(compareValue).append(\")\");\n        }\n        if(ttlLess != null || ttlMore != null){\n            if(ttlLess != null && ttlMore != null){\n                stringBuffer.append(\",ttl剩余筛选条件=(\").append(\"ttl\").append(\" > \").append(ttlMore).append(\" and ttl < \").append(ttlLess).append(\")\");\n            }else if(ttlLess != null){\n                stringBuffer.append(\",ttl剩余筛选条件=(\").append(\"ttl\").append(\" < \").append(ttlLess).append(\")\");\n            }else if(ttlMore != null){\n                stringBuffer.append(\",ttl剩余筛选条件=(\").append(\"ttl\").append(\" > \").append(ttlMore).append(\")\");\n            }\n        }\n        if(ttlResetLess != null || ttlResetMore != null){\n            stringBuffer.append(\",ttl重置时间配置=(\").append(\"ttl\").append(\" > \").append(ttlResetMore).append(\" and ttl < \").append(ttlResetLess).append(\")\");\n        }\n        condition = stringBuffer.toString();\n    }\n\n    /**\n     * 2.analyseKeyMatch\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum analyseKeyMatch() {\n        long recordId =  saveSubTaskRecord();\n        long startTime = System.currentTimeMillis();\n\n        String instanceScanKey = ConstUtils.getInstanceScanClean(taskId, hostPort);\n        Jedis slaveJedis = null;\n        Jedis masterJedis = null;\n        try {\n            slaveJedis = redisCenter.getJedis(host, port, authPwd);\n            HostAndPort master = redisCenter.getMaster(host, port, authPwd);\n            if(master != null){\n                masterJedis = redisCenter.getJedis(master.getHost(), master.getPort(), authPwd);\n            }else{\n                masterJedis = slaveJedis;\n            }\n            scanAnalyse(slaveJedis, masterJedis, instanceScanKey);\n            //更新记录\n            long cost = System.currentTimeMillis() - startTime;\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, instanceScanKey, 1, cost);\n            logger.info(marker, \"analyse key match successfully, appId:{} hostport:{} cost time is {} ms\", appId, hostPort, cost);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, \"analyse key match error, appId:{} hostport:{} error:\" + e.getMessage(), appId, hostPort, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, instanceScanKey, 2, 0);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (slaveJedis != null) {\n                slaveJedis.close();\n            }\n            if (masterJedis != null) {\n                masterJedis.close();\n            }\n        }\n    }\n\n    /**\n     * 3.scanCleanMatch\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum scanClean() {\n        long recordId =  saveSubTaskRecord();\n        long startTime = System.currentTimeMillis();\n        String instanceScanKey = ConstUtils.getInstanceScanClean(taskId, hostPort);\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getJedis(host, port, authPwd);\n            scanTtlClean(jedis, instanceScanKey);\n            //更新记录\n            long cost = System.currentTimeMillis() - startTime;\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, instanceScanKey, 1, cost);\n            logger.info(marker, \"scan clean successfully, appId:{} hostport:{} cost time is {} ms\", appId, cost);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, \"scan clean key error, appId:{} hostport:{} error:\" + e.getMessage(), appId, hostPort, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, instanceScanKey, 2, 0);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    /**\n     * 4.scanResetTtl\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum scanResetTtl() {\n        long recordId =  saveSubTaskRecord();\n        long startTime = System.currentTimeMillis();\n        String instanceScanKey = ConstUtils.getInstanceScanClean(taskId, hostPort);\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getJedis(host, port, authPwd);\n            scanResetTtl(jedis, instanceScanKey);\n            //更新记录\n            long cost = System.currentTimeMillis() - startTime;\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, instanceScanKey, 1, cost);\n            logger.info(marker, \"scan reset ttl successfully, appId:{} hostport:{} cost time is {} ms\", appId, cost);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(marker, \"scan reset ttl error, appId:{} hostport:{} error:\" + e.getMessage(), appId, hostPort, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, instanceScanKey, 2, 0);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n\n    private long saveSubTaskRecord(){\n        DiagnosticTaskRecord record = new DiagnosticTaskRecord();\n        record.setAppId(appId);\n        record.setAuditId(auditId);\n        record.setNode(hostPort);\n        record.setDiagnosticCondition(condition);\n        record.setTaskId(taskId);\n        record.setParentTaskId(parentTaskId);\n        record.setType(DiagnosticTypeEnum.SCAN_CLEAN.getType());\n        record.setStatus(0);\n        diagnosticTaskRecordDao.insertDiagnosticTaskRecord(record);\n        return record.getId();\n    }\n\n    private void scanAnalyse(Jedis slaveJedis, Jedis masterJedis, String instanceScanKey) throws InterruptedException {\n        //结果存redis\n        assistRedisService.del(instanceScanKey);\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例%s——键分析开始\", instanceScanKey));\n        long startTime = System.currentTimeMillis();\n        ScanParams scanParams = new ScanParams();\n        scanParams.count(perCount);\n        scanParams.match(pattern);\n        ScanResult<String> scanResult = null;\n        String cursor = \"0\";\n        Boolean flag = true;\n        List<String> keyList = new LinkedList<>();\n        List<String> matchKeyList = new LinkedList<>();\n        long totalCount = 0;\n        long count = 0;\n        long notTtlCount = 0;\n        long notValueMatchCount = 0;\n        long times = 0;\n        while (flag) {\n            keyList.clear();\n            scanResult = slaveJedis.scan(cursor, scanParams);\n            cursor = scanResult.getCursor();\n            totalCount += scanResult.getResult().size();\n            for (String key : scanResult.getResult()) {\n                if(compareValue == null){\n                    keyList.add(key);\n                }else {\n                    if(checkMatchByCompareValue(key)){\n                        keyList.add(key);\n                    }else{\n                        notValueMatchCount++;\n                    }\n                }\n            }\n\n            int originalSize = keyList.size();\n            if(ttlLess != null || ttlMore != null){\n                checkTtlByPipeline(masterJedis, keyList);\n            }\n            int filterSize = keyList.size();\n            notTtlCount += originalSize -filterSize;\n\n            count += keyList.size();\n\n            times++;\n            //取样\n            this.sampleMatchKey(matchKeyList, keyList, times);\n            if(times % 10 == 0){\n                logger.info(\"实例{}——键分析进行中,当前精确匹配键数量：{},排除键数量：{},(值去除({})/ttl去除({})),pattern匹配总数量：{},当前cursor:{}, 匹配键示例：{}\",\n                        instanceScanKey, count, (notValueMatchCount + notTtlCount), notValueMatchCount, notTtlCount , totalCount, cursor, (matchKeyList.size() > 10 ? matchKeyList.subList(0, 10) : matchKeyList));\n            }\n            if (cursor.equals(\"0\")) {\n                flag = false;\n                logger.info(\"实例{}——键分析结束-----------scan over!--------\", instanceScanKey);\n            }\n        }\n        //memoryUsageByPipeline\n        long avgMemoryUsage = memoryUsage(masterJedis, matchKeyList);\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例键分析结束, 精确匹配键数量：%s, 取样平均内存占用：%s(B), 总内存占用：%s(B), 排除键数量：%s(值去除(%s)/ttl去除(%s)), pattern匹配总数量：%s\",\n                count, avgMemoryUsage, (count * avgMemoryUsage), (notValueMatchCount + notTtlCount), notValueMatchCount, notTtlCount , totalCount));\n        this.printMatchKey(instanceScanKey, matchKeyList);\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例%s——键分析结束, 总耗时%sms\", instanceScanKey, (System.currentTimeMillis() - startTime)));\n    }\n\n    private void scanTtlClean(Jedis masterJedis, String instanceScanKey) throws InterruptedException {\n        //结果存redis\n        assistRedisService.del(instanceScanKey);\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例%s——键清理开始\", instanceScanKey));\n        ScanParams scanParams = new ScanParams();\n        scanParams.count(perCount);\n        scanParams.match(pattern);\n        ScanResult<String> scanResult = null;\n        String cursor = \"0\";\n        long startTime = System.currentTimeMillis();\n        Boolean flag = true;\n        List<String> keyList = new LinkedList<>();\n        List<String> matchKeyList = new LinkedList<>();\n        long totalCount = 0;\n        long count = 0;\n        long notTtlCount = 0;\n        long notValueMatchCount = 0;\n        long times = 0;\n        while (flag) {\n            keyList.clear();\n            scanResult = masterJedis.scan(cursor, scanParams);\n            cursor = scanResult.getCursor();\n            totalCount += scanResult.getResult().size();\n            for (String key : scanResult.getResult()) {\n                if(compareValue == null){\n                    keyList.add(key);\n                }else {\n                    if(checkMatchByCompareValue(key)){\n                        keyList.add(key);\n                    }else{\n                        notValueMatchCount++;\n                    }\n                }\n            }\n\n            int originalSize = keyList.size();\n            if(ttlLess != null || ttlMore != null){\n                checkTtlByPipeline(masterJedis, keyList);\n            }\n            int filterSize = keyList.size();\n            notTtlCount += originalSize -filterSize;\n            count += keyList.size();\n\n            times++;\n            //取样\n            this.sampleMatchKey(matchKeyList, keyList, times);\n            if(times % 10 == 0){\n                logger.info(\"实例：{},键清理进行中,当前精确匹配键数量：{},排除键数量：{},(值去除({})/ttl去除({})),pattern匹配总数量：{},当前cursor:{}, 匹配键示例：{}\",\n                        instanceScanKey, count, (notValueMatchCount + notTtlCount), notValueMatchCount, notTtlCount , totalCount, cursor, (matchKeyList.size() > 10 ? matchKeyList.subList(0, 10) : matchKeyList));\n            }\n            if(keyList.size() > 0){\n                unlinkByPipeline(masterJedis, keyList);\n                if(times % 10 == 0){\n                    logger.info(\"实例：{},键清理进行中,删除键:{}\", instanceScanKey, (keyList.size() > 10 ? keyList.subList(0, 10) : keyList));\n                }\n            }\n            if (cursor.equals(\"0\")) {\n                flag = false;\n                logger.info(\"实例{}——键清理结束------------scan over!--------\", instanceScanKey);\n            }\n        }\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例——键清理结束, 精确匹配清理键数量：%s, 排除键数量：%s(值去除(%s)/ttl去除(%s)), pattern匹配总数量：%s\",\n                count, (notValueMatchCount + notTtlCount), notValueMatchCount, notTtlCount , totalCount));\n        this.printMatchKey(instanceScanKey, matchKeyList);\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例%s——键清理结束, 总耗时%sms\", instanceScanKey, (System.currentTimeMillis() - startTime)));\n    }\n\n    private void scanResetTtl(Jedis masterJedis, String instanceScanKey) throws InterruptedException {\n        //结果存redis\n        assistRedisService.del(instanceScanKey);\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例%s—键重置ttl开始\", instanceScanKey));\n        ScanParams scanParams = new ScanParams();\n        scanParams.count(perCount);\n        scanParams.match(pattern);\n        ScanResult<String> scanResult = null;\n        String cursor = \"0\";\n        long startTime = System.currentTimeMillis();\n        Boolean flag = true;\n        List<String> keyList = new LinkedList<>();\n        List<String> matchKeyList = new LinkedList<>();\n        long totalCount = 0;\n        long count = 0;\n        long notTtlCount = 0;\n        long notValueMatchCount = 0;\n        long times = 0;\n        while (flag) {\n            keyList.clear();\n            scanResult = masterJedis.scan(cursor, scanParams);\n            cursor = scanResult.getCursor();\n            totalCount += scanResult.getResult().size();\n            for (String key : scanResult.getResult()) {\n                if(compareValue == null){\n                    keyList.add(key);\n                }else {\n                    if(checkMatchByCompareValue(key)){\n                        keyList.add(key);\n                    }else{\n                        notValueMatchCount++;\n                    }\n                }\n            }\n\n            int originalSize = keyList.size();\n            if(ttlLess != null || ttlMore != null){\n                checkTtlByPipeline(masterJedis, keyList);\n            }\n            int filterSize = keyList.size();\n            notTtlCount += originalSize -filterSize;\n\n            count += keyList.size();\n\n            times++;\n            //取样\n            this.sampleMatchKey(matchKeyList, keyList, times);\n            if(times % 10 == 0){\n                logger.info(\"实例：{},键重置ttl进行中,当前精确匹配键数量：{},排除键数量：{},(值去除({})/ttl去除({})),pattern匹配总数量：{},当前cursor:{}, 匹配键示例：{}\",\n                        instanceScanKey, count, (notValueMatchCount + notTtlCount), notValueMatchCount, notTtlCount , totalCount, cursor, (matchKeyList.size() > 10 ? matchKeyList.subList(0, 10) : matchKeyList));\n            }\n            if(keyList.size() > 0){\n                resetTtlByPatch(masterJedis, keyList);\n                if(times % 10 == 0){\n                    logger.info(\"实例：{},键重置ttl进行中,重置ttl键:{}\", instanceScanKey, (keyList.size() > 10 ? keyList.subList(0, 10) : keyList));\n                }\n            }\n            if (cursor.equals(\"0\")) {\n                flag = false;\n                logger.info(\"实例{}——键重置ttl结束------------scan over!--------\", instanceScanKey);\n            }\n        }\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例——键重置ttl结束, 精确匹配重置键数量：%s, 排除键数量：%s(值去除(%s)/ttl去除(%s)), pattern匹配总数量：%s\",\n                count, (notValueMatchCount + notTtlCount), notValueMatchCount, notTtlCount , totalCount));\n        this.printMatchKey(instanceScanKey, matchKeyList);\n        assistRedisService.rpush(instanceScanKey, String.format(\"实例%s——键重置ttl结束, 总耗时%sms\", instanceScanKey, (System.currentTimeMillis() - startTime)));\n    }\n\n    private boolean checkMatchByCompareValue(String key){\n        if(index == null || compareType == null || compareValue == null){\n            return false;\n        }\n        String subKey = key.substring(index);\n        Matcher matcher = patternExp.matcher(subKey);\n        boolean foundFlag = matcher.find();\n        if(foundFlag){\n            int end = matcher.end();\n            try{\n                Long id = Long.valueOf(subKey.substring(0, end));\n                if(ScanCleanConstants.COMPARE_TYPE_MORE_THAN.equals(compareType)){\n                    return id > compareValue;\n                }else if(ScanCleanConstants.COMPARE_TYPE_LESS_THAN.equals(compareType)){\n                    return id < compareValue;\n                }\n            }catch (Exception e){\n                logger.error(\"checkMatchByCompareValue key {}, error,\", key, e.getMessage());\n            }\n        }\n        return false;\n    }\n\n    private void checkTtlByPipeline(Jedis jedis, List<String> keyList){\n        Pipeline pipelined = jedis.pipelined();\n        for (String key : keyList){\n            pipelined.ttl(key);\n        }\n        List<Object> ttlList = pipelined.syncAndReturnAll();\n        int j = 0;\n        for(int i = 0; i < ttlList.size(); i++){\n            Object ttlObj = ttlList.get(i);\n            if(ttlObj instanceof Number){\n                if(((Long) ttlObj > 0)){\n                    if(ttlLess != null && ttlMore != null && ((Long) ttlObj > ttlLess || (Long) ttlObj < ttlMore)) {\n                        keyList.remove(j);\n                        j--;\n                    }else if(ttlLess != null && (Long) ttlObj > ttlLess){\n                        keyList.remove(j);\n                        j--;\n                    }else if(ttlMore != null && (Long) ttlObj < ttlMore){\n                        keyList.remove(j);\n                        j--;\n                    }\n                }\n            }else{\n                keyList.remove(j);\n                j--;\n            }\n            j++;\n        }\n    }\n\n    private long memoryUsage(Jedis jedis, List<String> keyList){\n        if(CollectionUtils.isEmpty(keyList)){\n            return 0;\n        }\n        int totalCount = keyList.size();\n        long sumMemUsage = 0;\n        for (String key : keyList){\n            Long memoryUsage = jedis.memoryUsage(key);\n            if(memoryUsage == null || memoryUsage < 0){\n                totalCount--;\n            }else{\n                sumMemUsage += memoryUsage;\n            }\n        }\n        return sumMemUsage/totalCount;\n    }\n\n    private void sampleMatchKey(List<String> matchKeyList, List<String> keyList, long times){\n        if(CollectionUtils.isNotEmpty(keyList)){\n            if(matchKeyList.size() < 500){\n                int num = perCount/keyList.size() * 2;\n                num  = num > 50 ? 50 : (num > 5 ? num: 5);\n                matchKeyList.addAll(keyList.size() > num ? keyList.subList(0, num) : keyList);\n                return;\n            }\n            if(times%50 == 0){\n                matchKeyList.addAll(keyList.size() > 10 ? keyList.subList(0, 10) : keyList);\n                if(matchKeyList.size() > 500){\n                    for(int i = 0; i < matchKeyList.size() - 500;){\n                        int toRem = new Random().nextInt(500);\n                        matchKeyList.remove(toRem);\n                    }\n                }\n            }\n        }\n    }\n\n    private void printMatchKey(String instanceScanKey, List<String> matchKeyList){\n        if(CollectionUtils.isNotEmpty(matchKeyList)){\n            if(matchKeyList.size() > 100){\n                Collections.shuffle(matchKeyList);\n                matchKeyList = matchKeyList.subList(0, 100);\n            }\n            List<String> matchResultList = new ArrayList<>();\n            boolean first = true;\n            for(int i = 0; i < matchKeyList.size() / 20; ){\n                if(first){\n                    matchResultList.add(\"匹配键示例：[\" + matchKeyList.subList(0, 20).stream().collect(Collectors.joining(\",\")));\n                    first = false;\n                }else{\n                    if(matchKeyList.size() != 20){\n                        matchResultList.add(matchKeyList.subList(0, 20).stream().collect(Collectors.joining(\",\")));\n                    }else{\n                        matchResultList.add(matchKeyList.subList(0, 20).stream().collect(Collectors.joining(\",\")) + \"]\");\n                    }\n                }\n                matchKeyList = matchKeyList.subList(20, matchKeyList.size());\n            }\n            if(matchKeyList.size() > 0){\n                if(first){\n                    matchResultList.add(\"匹配键示例：[\" + matchKeyList.stream().collect(Collectors.joining(\",\")) + \"]\");\n                }else{\n                    matchResultList.add(matchKeyList.stream().collect(Collectors.joining(\",\")) + \"]\");\n                }\n            }\n            assistRedisService.rpushList(instanceScanKey, matchResultList);\n        }else{\n            assistRedisService.rpush(instanceScanKey, \"匹配键示例：[]\");\n        }\n    }\n\n    private void unlinkByPatch(Jedis jedis, List<String> keyList){\n        jedis.unlink(keyList.toArray(new String[keyList.size()]));\n    }\n\n    private void unlinkByPipeline(Jedis jedis, List<String> keyList){\n        Pipeline pipelined = jedis.pipelined();\n        for (String key : keyList){\n            pipelined.unlink(key);\n        }\n        pipelined.sync();\n    }\n\n    private void unlinkBySlotAndPatch(Map<Integer, Jedis> slotJedisMap, List<String> keyList){\n        Map<Jedis, List<String>> keyToJedisMap = new HashMap<>();\n        for (String key : keyList){\n            int slot = JedisClusterCRC16.getSlot(key);\n            Jedis jedis = slotJedisMap.get(slot);\n            if(keyToJedisMap.containsKey(jedis)){\n                keyToJedisMap.get(jedis).add(key);\n            }else{\n                keyToJedisMap.put(jedis, Lists.newArrayList(key));\n            }\n        }\n        Set<Jedis> jedisSet = keyToJedisMap.keySet();\n        for (Jedis jedis : jedisSet){\n            jedis.unlink(keyToJedisMap.get(jedis).toArray(new String[keyToJedisMap.get(jedis).size()]));\n        }\n    }\n\n    private void resetTtlByPatch(Jedis jedis, List<String> keyList){\n        Pipeline pipelined = jedis.pipelined();\n        for (String key : keyList){\n            pipelined.expire(key, ttlResetMore + random.nextInt(ttlResetLess - ttlResetMore));\n        }\n        pipelined.sync();\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/InstanceScanKeyTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.StringUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\n\nimport java.nio.charset.Charset;\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 15:53\n */\n@Component(\"InstanceScanKeyTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class InstanceScanKeyTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private String pattern;\n\n    private long auditId;\n\n    private int size;\n\n    private long parentTaskId;\n\n    private final static int SCAN_COUNT = 100;\n\n    private final static String CONDITION_TEMPLATE = \"pattern:{0};size:{1}\";\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // scan\n        taskStepList.add(\"scanKey\");\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        pattern = MapUtils.getString(paramMap, \"pattern\");\n        if (StringUtils.isBlank(pattern)) {\n            logger.info(marker, \"task {} pattern is empty\", taskId);\n        }\n\n        size = MapUtils.getIntValue(paramMap, \"size\");\n        if (size <= 0) {\n            logger.error(marker, \"task {} size {} is wrong\", taskId, size);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        parentTaskId = MapUtils.getLongValue(paramMap, \"parentTaskId\");\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.检查run以及slave\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.scanKey\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum scanKey() {\n        DiagnosticTaskRecord record = new DiagnosticTaskRecord();\n        record.setAppId(appId);\n        record.setAuditId(auditId);\n        String hostPost = host + \":\" + port;\n        record.setNode(hostPost);\n        String condition = MessageFormat.format(CONDITION_TEMPLATE, pattern, size);\n        record.setDiagnosticCondition(condition);\n        record.setTaskId(taskId);\n        record.setParentTaskId(parentTaskId);\n        record.setType(DiagnosticTypeEnum.SCAN_KEY.getType());\n        record.setStatus(0);\n        diagnosticTaskRecordDao.insertDiagnosticTaskRecord(record);\n        long recordId = record.getId();\n\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 1, System.currentTimeMillis() - startTime);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n            // scan参数\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n            ScanParams scanParams = StringUtil.isBlank(pattern) ?\n                    new ScanParams().count(Math.min(SCAN_COUNT, size)) :\n                    new ScanParams().match(pattern).count(Math.min(SCAN_COUNT, size));\n\n            long count = 0;\n            int totalSplit = 10;\n            int curSplit = 1;\n\n            List<String> result = new ArrayList<>();\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n\n                    if (CollectionUtils.isNotEmpty(keyList)) {\n                        result.addAll(keyList.stream().map(byteKey -> new String(byteKey)).collect(Collectors.toList()));\n                    }\n                    count += keyList.size();\n                    if (count > dbSize / totalSplit * curSplit) {\n                        logger.info(marker, \"{} {}:{} has already scan {}% {} key \", appId, host, port, curSplit * 10, count);\n                        curSplit++;\n                    }\n                    // @TODO暂时写死\n                    TimeUnit.MILLISECONDS.sleep(10);\n                } catch (Exception e) {\n                    logger.error(marker, e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if (result.size() >= size || Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            //结果存redis\n            String redisScanKey = ConstUtils.getInstanceScanKey(taskId, hostPost);\n            assistRedisService.del(redisScanKey);\n            assistRedisService.rpushList(redisScanKey, result);\n            //更新记录\n            long cost = System.currentTimeMillis() - startTime;\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, redisScanKey, 1, cost);\n\n            logger.info(marker, \"{} {}:{} scan key successfully, cost time is {} ms, total key is {}\", appId, host, port, cost, count);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, \"redis-cli -h {} -p {} admin auth error\", host, port);\n            logger.error(marker, \"scan key appId {} {}:{}  error:\" + e.getMessage(), appId, host, port, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/diagnosticTask/InstanceSlotAnalysisTask.java",
    "content": "package com.sohu.cache.task.tasks.diagnosticTask;\n\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.entity.InstanceSlotModel;\nimport com.sohu.cache.redis.util.PipelineUtil;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.Pair;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.exceptions.JedisRedirectionException;\n\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/9 15:53\n */\n@Component(\"InstanceSlotAnalysisTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class InstanceSlotAnalysisTask extends BaseTask {\n\n    private String host;\n\n    private int port;\n\n    private long appId;\n\n    private long auditId;\n\n    private long parentTaskId;\n\n    private static double ERROR_FACTOR = 1;\n    private static String COUNT_ERROR_FORMAT = \"countkeys:{0}; error:{1}; benchmark:{2}\";\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        // 检查实例是否运行\n        taskStepList.add(\"checkIsRun\");\n        // slotAnalysis\n        taskStepList.add(\"slotAnalysis\");\n        return taskStepList;\n    }\n\n    /**\n     * 1.初始化参数\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY);\n        if (auditId <= 0) {\n            logger.error(marker, \"task {} auditId {} is wrong\", taskId, auditId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        parentTaskId = MapUtils.getLongValue(paramMap, \"parentTaskId\");\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 2.检查run以及slave\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsRun() {\n        if (!redisCenter.isRun(appId, host, port)) {\n            logger.error(marker, \"{} {}:{} is not run\", appId, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 3.scanKey\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum slotAnalysis() {\n        DiagnosticTaskRecord record = new DiagnosticTaskRecord();\n        record.setAppId(appId);\n        record.setAuditId(auditId);\n        String hostPost = host + \":\" + port;\n        record.setNode(hostPost);\n        record.setTaskId(taskId);\n        record.setParentTaskId(parentTaskId);\n        record.setType(DiagnosticTypeEnum.SLOT_ANALYSIS.getType());\n        record.setStatus(0);\n        diagnosticTaskRecordDao.insertDiagnosticTaskRecord(record);\n        long recordId = record.getId();\n\n        /**\n         * 扫描删除，计时开始*/\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                logger.info(marker, \"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 1, System.currentTimeMillis() - startTime);\n                return TaskFlowStatusEnum.SUCCESS;\n            }\n            logger.info(marker, \"{} {}:{} total key is {} \", appId, host, port, dbSize);\n\n\n            Map<String, String> result = new HashMap<>();\n\n            Pipeline pipeline = jedis.pipelined();\n            InstanceSlotModel instanceSlotModel = (InstanceSlotModel) MapUtils.getObject(redisCenter.getClusterSlotsMap(appId), hostPost);\n            if (instanceSlotModel != null) {\n                List<Integer> slotList = instanceSlotModel.getSlotList();\n                //set benchmark\n                long benchmark = 0l;\n                for (Integer slot : slotList) {\n                    benchmark = jedis.clusterCountKeysInSlot(slot);\n                    if (benchmark > 0) {\n                        break;\n                    }\n                }\n                slotList.stream().forEach(slot -> PipelineUtil.clusterCountKeysInSlot(pipeline, slot));\n                List<Object> objectList = new ArrayList<>();\n                try {\n                    objectList = pipeline.syncAndReturnAll();\n                } catch (JedisRedirectionException e) {\n                    logger.error(marker, \"redisSlotAnalysis appId {} {}:{}  JedisRedirectionException:\" + e.getMessage(), appId, host, port, e);\n                }\n\n                List<Object> countObjectList = objectList;\n                long finalBenchmark = benchmark;\n                result = IntStream.range(0, slotList.size())\n                        .filter(i -> (countObjectList.get(i) != null) && (countObjectList.get(i) instanceof Long))\n                        .mapToObj(i -> new Pair<>(String.valueOf(slotList.get(i)), getCountAndError((Long) countObjectList.get(i), finalBenchmark)))\n                        .collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n            }\n\n            //结果存redis\n            String redisSlotAnalysis = ConstUtils.getInstanceSlotAnalysis(taskId, hostPost);\n            assistRedisService.del(redisSlotAnalysis);\n            assistRedisService.hmset(redisSlotAnalysis, result);\n            long cost = System.currentTimeMillis() - startTime;\n            /**\n             * 计时结束*/\n            //更新记录\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, redisSlotAnalysis, 1, cost);\n\n            logger.info(marker, \"{} {}:{} redisSlotAnalysis successfully, cost time is {} ms\", appId, host, port, cost);\n            return TaskFlowStatusEnum.SUCCESS;\n        } catch (RuntimeException e) {\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            throw e;\n        } catch (Exception e) {\n            logger.error(marker, \"redis-cli -h {} -p {} admin auth error\", host, port);\n            logger.error(marker, \"redisSlotAnalysis appId {} {}:{}  error:\" + e.getMessage(), appId, host, port, e);\n            diagnosticTaskRecordDao.updateDiagnosticStatus(recordId, \"\", 2, 0);\n            return TaskFlowStatusEnum.ABORT;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n\n    }\n\n    private double getRealError(long count, long benchmark) {\n        if (benchmark <= 0) {\n            return count == benchmark ? 0 : 100;\n        }\n        return Math.abs(count - benchmark) * 1.0 / benchmark;\n    }\n\n    private String getCountAndError(long count, long benchmark) {\n        double err = getRealError(count, benchmark);\n        return err > ERROR_FACTOR ?\n                MessageFormat.format(COUNT_ERROR_FORMAT, String.valueOf(count), String.format(\"%.2f\", err), String.valueOf(benchmark))\n                : \"countkeys:\" + count;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/install/RedisSentinelInstallTask.java",
    "content": "package com.sohu.cache.task.tasks.install;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.protocol.RedisProtocol;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * <p>\n * Description: Redis sentinel安装\n * </p>\n *\n * @author chenshi\n * @version 1.0\n * @date 2019/1/11\n */\n@Component(\"RedisSentinelInstallTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisSentinelInstallTask extends BaseTask {\n\n    private long appId;\n\n    private String host;\n\n    private int port;\n\n    private int quorum;\n\n    SystemResource redisResource;\n\n    private List<RedisServerNode> masterRedisServerNodes;\n\n    /**\n     * 当前实例类型\n     */\n    private final InstanceInfoEnum.InstanceTypeEnum currentInstanceTypeEnum = InstanceInfoEnum.InstanceTypeEnum.REDIS_SENTINEL;\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        taskStepList.add(\"checkIsExist\");\n        taskStepList.add(\"prepareRelateDir\");\n        taskStepList.add(\"prepareRelateBin\");\n        taskStepList.add(\"pushService\");\n        taskStepList.add(\"pushConfig\");\n        taskStepList.add(\"startServer\");\n        taskStepList.add(\"checkIsRun\");\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        quorum = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SENTINEL_QUORUM_KEY);\n        if (quorum <= 0) {\n            logger.error(marker, \"task {} quorum {} is wrong\", taskId, quorum);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        //parse\n        String masterRedisNodeStr = MapUtils.getString(paramMap, TaskConstants.MASTER_REDIS_SERVER_NODES);\n        masterRedisServerNodes = JSONArray.parseArray(masterRedisNodeStr, RedisServerNode.class);\n        if (CollectionUtils.isEmpty(masterRedisServerNodes)) {\n            logger.error(marker, \"task {} masterRedisNodes is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        redisResource = resourceService.getResourceById(appService.getByAppId(appId).getVersionId());\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 检查实例是否已经存在\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsExist() {\n        return checkInstanceIsExist(appId, host, port, currentInstanceTypeEnum);\n    }\n\n    /**\n     * 准备相关目录\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum prepareRelateDir() {\n        return prepareRelateDir(appId, host, port, currentInstanceTypeEnum);\n    }\n\n    /**\n     * 准备二进制执行文件\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum prepareRelateBin() {\n        return prepareRelateBin(appId, host, port, currentInstanceTypeEnum, redisResource.getName());\n    }\n\n    /**\n     * 启动服务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum pushService() {\n        //远程和本地目录\n        /*String instanceRemoteBasePath = machineCenter.getInstanceRemoteBasePath(appId, port, currentInstanceTypeEnum);\n        String instanceLocalTmpPath = machineCenter.getInstanceLocalTempBasePath(appId, host, port, currentInstanceTypeEnum);\n\n        //相关参数\n        int cpuidx = MachineProtocol.getCpuIdx(port);\n        String startCmd = RedisProtocol.getRedisSentinelStartCmd();\n        String runCmd = RedisProtocol.getRedisSentinelRunCmd(port);\n        String serviceShellFileName = RedisProtocol.getServiceShellFileName(currentInstanceTypeEnum);\n\n        logger.info(marker, \"cpuidx is {}\", cpuidx);\n        logger.info(marker, \"startCmd is {}\", startCmd);\n        logger.info(marker, \"runCmd is {}\", runCmd);\n\n        return pushService(appId, currentInstanceTypeEnum, host, port, instanceRemoteBasePath, instanceLocalTmpPath, cpuidx, startCmd, runCmd, serviceShellFileName);\n        */\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 推配置\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum pushConfig() {\n        //资源是否存在\n        redisConfigTemplateService.checkAndInstallRedisResource(host, redisResource);\n        String instanceRemoteBasePath = ConstUtils.CACHECLOUD_BASE_DIR;\n        AppDesc appDesc = appService.getByAppId(appId);\n\n        List<String> masterSentinelConfigs;\n        // 获取masterName\n        if (masterRedisServerNodes.size() == 1) {\n            RedisServerNode redisServerNode = masterRedisServerNodes.get(0);\n            List<Pair<String, String>> customConfigs = new ArrayList<>();\n            customConfigs.add(Pair.of(\"sentinel auth-pass \" + redisServerNode.getMasterName() + ConstUtils.SPACE, appDesc.getAppPassword()));\n            masterSentinelConfigs = this.handleSentinelConfig(redisServerNode.getMasterName(), redisServerNode.getIp(), redisServerNode.getPort(), host, port, appDesc.getVersionId(), customConfigs);\n            logger.info(\"sentinel configs :\" + masterSentinelConfigs);\n        } else {\n            logger.error(marker, \"appId {} masterRedisServerNodes {} is empty\", appId, masterRedisServerNodes);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        if (CollectionUtils.isEmpty(masterSentinelConfigs)) {\n            logger.error(marker, \"appId {} host:{} port:{} versionId:{} instanceRemoteBasePath {} configList is empty\", appId, host, port, appDesc.getVersionId(), instanceRemoteBasePath);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        String masterSentinelFileName = RedisProtocol.getConfig(port, false);\n        String sentinelPathFile = machineCenter\n                .createRemoteFile(host, masterSentinelFileName, masterSentinelConfigs);\n        if (StringUtils.isBlank(sentinelPathFile)) {\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    private String getMasterName(String host, int port) {\n        String masterSentinelName = String.format(\"sentinel-%s-%s\", host, port);\n        return masterSentinelName;\n    }\n\n    private void printConfig(List<String> masterConfigs) {\n        logger.info(\"==================redis-{}-config==================\", masterConfigs);\n        for (String line : masterConfigs) {\n            logger.info(line);\n        }\n    }\n\n    /**\n     * 启动服务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum startServer() {\n        redisConfigTemplateService.checkAndInstallRedisResource(host, redisResource);\n        String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName());\n        String sentinelShell = redisDeployCenter.getSentinelRunShell(host, port, redisDir);\n        logger.info(marker, \"sentinelMasterShell:{}\", sentinelShell);\n        boolean isSentinelMasterShell = machineCenter.startProcessAtPort(host, port, sentinelShell);\n        if (!isSentinelMasterShell) {\n            logger.error(marker, \"sentinelMasterShell={} error\", sentinelShell);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsRun() {\n        //检测是否启动成功前，先休息3秒，否则可能会服务未启动完成，出现不能正确创建socket连接异常\n        try {\n            TimeUnit.SECONDS.sleep(3);\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (!redisCenter.isRun(host, port)) {\n            logger.error(marker, \"sentinel {}:{} is not run\", host, port);\n            return TaskFlowStatusEnum.ABORT;\n        } else {\n            logger.info(marker, \"sentinel {}:{} is run\", host, port);\n            return TaskFlowStatusEnum.SUCCESS;\n        }\n    }\n\n    /**\n     * <p>\n     * Description: 获取sentinel config\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2019/1/11\n     */\n    private List<String> handleSentinelConfig(String masterName, String host, int port, String sentinelHost, int sentinelPort, int versionId, List<Pair<String, String>> customConfigs) {\n        try {\n            // todo  masterHost\n            return redisConfigTemplateService.handleSentinelConfig(masterName, host, port, sentinelHost, sentinelPort, versionId, customConfigs);\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/install/RedisServerInstallTask.java",
    "content": "package com.sohu.cache.task.tasks.install;\n\nimport com.sohu.cache.constant.AppDescEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.protocol.RedisProtocol;\nimport com.sohu.cache.redis.enums.RedisConfigEnum;\nimport com.sohu.cache.redis.util.AuthUtil;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;\n\n/**\n * redis server安装\n */\n@Component(\"RedisServerInstallTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class RedisServerInstallTask extends BaseTask {\n\n    private long appId;\n\n    private String host;\n\n    private int port;\n\n    private int maxMemory;\n\n    private Boolean isCluster;\n\n    SystemResource redisResource;\n\n    /**\n     * 当前实例类型\n     */\n    private final InstanceTypeEnum currentInstanceTypeEnum = InstanceTypeEnum.REDIS_SERVER;\n\n    /**\n     * 密码\n     */\n    private String password;\n\n\n    @Override\n    public List<String> getTaskSteps() {\n        List<String> taskStepList = new ArrayList<String>();\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        /**\n         * 1.检查实例是否存在\n         * 2.检查服务器安装环境 dir / env param\n         * 3.appid | redis version版本验证\n         * 4.push config\n         * 5.start redis instance\n         * 6.validate instance is run\n         */\n        taskStepList.add(\"checkIsExist\");\n        taskStepList.add(\"prepareRelateDir\");\n        taskStepList.add(\"prepareRelateBin\");\n        taskStepList.add(\"pushConfig\");\n        taskStepList.add(\"pushService\");\n        taskStepList.add(\"startServer\");\n        taskStepList.add(\"checkIsRun\");\n        return taskStepList;\n    }\n\n    /**\n     * 初始化参数\n     *\n     * @return\n     */\n    @Override\n    public TaskFlowStatusEnum init() {\n        super.init();\n\n        appId = MapUtils.getLongValue(paramMap, TaskConstants.APPID_KEY);\n        if (appId <= 0) {\n            logger.error(marker, \"task {} appId {} is wrong\", taskId, appId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        host = MapUtils.getString(paramMap, TaskConstants.HOST_KEY);\n        if (StringUtils.isBlank(host)) {\n            logger.error(marker, \"task {} host is empty\", taskId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        port = MapUtils.getIntValue(paramMap, TaskConstants.PORT_KEY);\n        if (port <= 0) {\n            logger.error(marker, \"task {} port {} is wrong\", taskId, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY);\n        if (maxMemory <= 0) {\n            logger.error(marker, \"task {} maxMemory {} is wrong\", taskId, maxMemory);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        redisResource = resourceService.getResourceById(appService.getByAppId(appId).getVersionId());\n        isCluster = MapUtils.getBoolean(paramMap, TaskConstants.IS_CLUSTER_KEY);\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 检查实例是否已经存在\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum checkIsExist() {\n        return checkInstanceIsExist(appId, host, port, currentInstanceTypeEnum);\n    }\n\n    /**\n     * 准备相关目录\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum prepareRelateDir() {\n        return prepareRelateDir(appId, host, port, currentInstanceTypeEnum);\n    }\n\n    /**\n     * 准备二进制执行文件\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum prepareRelateBin() {\n        return prepareRelateBin(appId, host, port, currentInstanceTypeEnum, redisResource.getName());\n    }\n\n    /**\n     * 推配置\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum pushConfig() {\n        redisConfigTemplateService.checkAndInstallRedisResource(host, redisResource);\n        //实例基准目录\n        String instanceRemoteBasePath = ConstUtils.CACHECLOUD_BASE_DIR;\n        AppDesc appDesc = appService.getByAppId(appId);\n        password = getAppPwd(appDesc);\n\n        List<Pair<String, String>> customConfigs = new ArrayList<>();\n        customConfigs.add(Pair.of(RedisConfigEnum.REQUIREPASS.getKey(), password));\n        customConfigs.add(Pair.of(RedisConfigEnum.MASTERAUTH.getKey(), password));\n        List<String> configList = handleCommonConfig(host, port, maxMemory, appDesc.getMaxmemoryPolicy(), appDesc.getVersionId(), isCluster, customConfigs);\n        if (CollectionUtils.isEmpty(configList)) {\n            logger.error(marker, \"appId {} port {} maxmemory {} versionId:{} instanceRemoteBasePath {} configList is empty\", appId, port, maxMemory, appDesc.getVersionId(), instanceRemoteBasePath);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        String fileName;\n        if (isCluster) {\n            fileName = RedisProtocol.getConfig(port, true);\n        } else {\n            fileName = RedisProtocol.getConfig(port, false);\n        }\n        //todo\n        String pathFile = machineCenter.createRemoteFile(host, fileName, configList);\n        if (StringUtils.isBlank(pathFile)) {\n            logger.error(marker, \"appId {} port {} maxmemory {} instanceRemoteBasePath:{} pathFile:{} is empty\", appId, port, maxMemory, instanceRemoteBasePath,pathFile);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    private String getAppPwd(AppDesc appDesc){\n        String newPassword = null;\n        boolean customPwdFlag = appDesc.isSetCustomPassword();\n        if(customPwdFlag){\n            newPassword = appDesc.getCustomPassword();\n        }else{\n            String newPkey = String.valueOf(appId);\n            newPassword = AuthUtil.getAppIdMD5(newPkey);\n        }\n        if(newPassword == null){\n            newPassword = \"\";\n        }\n        return newPassword;\n    }\n\n    /**\n     * 启动服务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum pushService() {\n        //远程和本地目录\n        //String instanceRemoteBasePath = machineCenter.getInstanceRemoteBasePath(appId, port, currentInstanceTypeEnum);\n//\t\tString instanceLocalTmpPath = machineCenter.getInstanceLocalTempBasePath(appId, host, port, currentInstanceTypeEnum);\n\n        //相关参数\n//\t\tint cpuidx = MachineProtocol.getCpuIdx(port);\n        /*String startCmd = RedisProtocol.getRedisServerStartCmd();\n        String runCmd = RedisProtocol.getRedisServerRunCmd(port);\n\t\tString serviceShellFileName = RedisProtocol.getServiceShellFileName(currentInstanceTypeEnum);\n\t\t\n\t\tlogger.info(marker, \"cpuidx is {}\", cpuidx);\n\t\tlogger.info(marker, \"startCmd is {}\", startCmd);\n\t\tlogger.info(marker, \"runCmd is {}\", runCmd);\n\t\t\n\t\treturn pushService(appId, currentInstanceTypeEnum, host, port, instanceRemoteBasePath, instanceLocalTmpPath, cpuidx, startCmd, runCmd, serviceShellFileName);\n\t\t*/\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    /**\n     * 启动服务\n     *\n     * @return\n     */\n    public TaskFlowStatusEnum startServer() {\n        //资源是否存在\n        redisConfigTemplateService.checkAndInstallRedisResource(host, redisResource);\n        String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName());\n        String runShell;\n        if (isCluster) {\n            runShell = redisDeployCenter.getRedisRunShell(true, host, port, redisDir);\n        } else {\n            runShell = redisDeployCenter.getRedisRunShell(false, host, port, redisDir);\n        }\n        //启动实例\n        logger.info(marker, \"masterShell:host={};shell={}\", host, runShell);\n        boolean isMasterShell = machineCenter.startProcessAtPort(host, port, runShell);\n        if (!isMasterShell) {\n            logger.error(marker, \"runShell={} error,{}:{}\", runShell, host, port);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkIsRun() {\n        //检测是否启动成功前，先休息3秒，否则可能会服务未启动完成，出现不能正确创建socket连接异常\n        try {\n            TimeUnit.SECONDS.sleep(3);\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (!redisCenter.isRun(host, port, password)) {\n            logger.error(marker, \"redis server {}:{} is not run\", host, port);\n            return TaskFlowStatusEnum.ABORT;\n        } else {\n            logger.info(marker, \"redis server {}:{} is run\", host, port);\n            return TaskFlowStatusEnum.SUCCESS;\n        }\n    }\n\n    /**\n     * <p>\n     * Description:获取redis config\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2019/1/9\n     */\n    private List<String> handleCommonConfig(String host, int port, int maxMemory, Integer maxMemoryPolicyType, int versionId, boolean isCluster, List<Pair<String, String>> customConfigs) {\n        try {\n            String maxMemoryPolicy = null;\n            if(maxMemoryPolicyType != null){\n                AppDescEnum.MaxmemoryPolicyType policyType = AppDescEnum.MaxmemoryPolicyType.getByType(maxMemoryPolicyType);\n                if(policyType != null){\n                    maxMemoryPolicy = policyType.getName();\n                }\n            }\n            return redisConfigTemplateService.handleRedisConfig(host, port, versionId, maxMemory, maxMemoryPolicy, isCluster, customConfigs, Collections.emptyMap());\n        } catch (Exception e) {\n            logger.error(marker, e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/tasks/resource/PackCompileTask.java",
    "content": "package com.sohu.cache.task.tasks.resource;\n\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.ssh.SSHTemplate;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.PushEnum;\nimport com.sohu.cache.task.constant.TaskConstants;\nimport com.sohu.cache.task.constant.TaskStepFlowEnum.TaskFlowStatusEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.web.enums.SshAuthTypeEnum;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;\n\n/**\n * Created by chenshi on 2020/7/13.\n */\n@Component(\"PackCompileTask\")\n@Scope(SCOPE_PROTOTYPE)\npublic class PackCompileTask extends BaseTask {\n\n    /**\n     * 编译容器ip\n     */\n    private String containerIp;\n    /**\n     * 资源id\n     */\n    private Integer resourceId;\n    /**\n     * 仓库id\n     */\n    private Integer repositoryId;\n    // 操作人\n    private String username;\n    // 资源包\n    private SystemResource resource;\n    // 仓库资源\n    private SystemResource repository;\n\n    // 编译\n    private static String COMPILE_SH = \" mkdir -p %s && cd %s && \" +\n            \" wget -O resource.tar.gz %s && tar zxvf resource.tar.gz --strip-component=1 &&\" +\n            \" rm -f resource.tar.gz && make\";\n    // 备份\n    private static String BACKUP_SH = \"mkdir -p %s && cp -r %s %s \";\n    // 上传\n    private static String PRIVATEKEY_UPLOAD_SH = \"cd %s && tar -cvf %s %s && scp -i %s -oStrictHostKeyChecking=no -r %s %s@%s:%s\";\n    private static String PASSWD_UPLOAD_SH = \"cd %s && tar -cvf %s %s && sshpass -p %s scp -oStrictHostKeyChecking=no -r %s %s@%s:%s\";\n\n    private static String PACKAGE_SUFFIX = \"-make.tar.gz\";\n\n    private static String PRIVATE_KEY = \"/opt/cachecloud/ssh/id_rsa\";\n\n    private static int COMPILE_TIME = 10 * 60 * 1000;\n\n    @Override\n    public List<String> getTaskSteps() {\n\n        List<String> taskStepList = new ArrayList<String>();\n        //1. 参数初始化\n        taskStepList.add(TaskConstants.INIT_METHOD_KEY);\n        //2. 检查容器ssh环境&私钥文件&gcc\n        taskStepList.add(\"checkSshEnv\");\n        //3. 远程仓库下载资源包&解压编译\n        taskStepList.add(\"compile\");\n        //4. 备份资源\n        taskStepList.add(\"bakResource\");\n        //5. 二进制包上传到仓库\n        taskStepList.add(\"uploadResource\");\n        //6. 编译完成\n        taskStepList.add(\"compileOver\");\n        return taskStepList;\n    }\n\n    @Override\n    public TaskFlowStatusEnum init() {\n\n        super.init();\n        // 1.资源id\n        resourceId = MapUtils.getInteger(paramMap, TaskConstants.RESOURCE_ID);\n        if (resourceId == null) {\n            logger.error(marker, \"task {} source machine ip {} is empty\", taskId, resourceId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // 2.目标宿主机\n        repositoryId = MapUtils.getInteger(paramMap, TaskConstants.REPOSITORY_ID);\n        if (repositoryId == null) {\n            logger.error(marker, \"task {} target machine ip {} is empty\", taskId, repositoryId);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        // 3.容器ip\n        containerIp = MapUtils.getString(paramMap, TaskConstants.CONTAINER_IP);\n        if (StringUtils.isEmpty(containerIp)) {\n            logger.error(marker, \"task {} container ip {} is empty\", taskId, containerIp);\n            return TaskFlowStatusEnum.ABORT;\n        }\n\n        // 4.操作人/资源/仓库信息\n        username = MapUtils.getString(paramMap, TaskConstants.USER_INFO_KEY);\n        resource = resourceDao.getResourceById(resourceId);\n        repository = resourceDao.getResourceById(repositoryId);\n\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum checkSshEnv() {\n\n        try {\n            //1. 资源状态更新:编译中\n            if (resource != null) {\n                resource.setIspush(PushEnum.COMPILEING.getValue());\n                resourceDao.update(resource);\n            } else {\n                logger.error(marker, \"resource id:{} is empty,resource {}\", resourceId, resource);\n                return TaskFlowStatusEnum.ABORT;\n            }\n\n            if (ConstUtils.SSH_AUTH_TYPE == SshAuthTypeEnum.PASSWORD.getValue()) {\n                // 2.1 用户名密码\n                logger.info(marker, \"check ssh use username/passwd\");\n                return TaskFlowStatusEnum.SUCCESS;\n            } else if (ConstUtils.SSH_AUTH_TYPE == SshAuthTypeEnum.PUBLIC_KEY.getValue()) {\n                // 2.2 检查私钥文件\n                String check_cmd = String.format(\"ls -l %s\", PRIVATE_KEY);\n                SSHTemplate.Result result = sshService.executeWithResult(containerIp, check_cmd, COMPILE_TIME);\n                if (result.isSuccess() && !StringUtil.isBlank(result.getResult())) {\n                    logger.info(marker, \"check private key exists ,cmd:{} result : {}\", check_cmd, result);\n                    return TaskFlowStatusEnum.SUCCESS;\n                } else {\n                    // 下载私钥文件到机器\n                    logger.error(marker, \"check private key ,cmd:{} result : {}\", check_cmd, result);\n                    return TaskFlowStatusEnum.ABORT;\n                }\n            }\n\n\n        } catch (Exception e) {\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum compile() {\n\n        String respositoryUrl = resourceService.getRespositoryUrl(resourceId, repositoryId);\n\n        //1. 资源编译\n        String compile_dir = ConstUtils.REDIS_COMPILE_BASE_DIR + resource.getName();\n        String compile_cmd = String.format(COMPILE_SH, compile_dir, compile_dir, resource.getUrl());\n\n        logger.info(marker, \"download from url:{}\", respositoryUrl);\n        logger.info(marker, \"compile cmd : {}\", compile_cmd);\n        try {\n            SSHTemplate.Result result = sshService.executeWithResult(containerIp, compile_cmd, COMPILE_TIME);\n            if (result.isSuccess()) {\n                logger.info(marker, \"compile cmd:{} result : {}\", compile_cmd, result);\n            } else {\n                logger.error(marker, \"compile cmd:{} result error: {}\", compile_cmd, result.getExcetion());\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } catch (SSHException e) {\n            logger.error(marker, \"compile error :{}\", e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum bakResource() {\n\n        if (repository != null) {\n            try {\n                String resouceFile = repository.getDir() + resource.getDir() + \"/\" + resource.getName() + PACKAGE_SUFFIX;\n                String bakPath = repository.getDir() + resource.getDir() + \"/bak/\";\n                String backupFile = bakPath + resource.getName() + PACKAGE_SUFFIX + \"-\" + DateUtil.formatYYYYMMddHHMMss(new Date()) + \"-\" + username;\n                String bakcmd = String.format(BACKUP_SH, bakPath, resouceFile, backupFile);\n\n                String repository_ip = repository.getName();//远程仓库ip\n                // 源文件如果存在，需要先备份\n                SSHTemplate.Result existResult = sshService.executeWithResult(repository_ip, String.format(\"ls -l %s\", resouceFile));\n                logger.info(marker, \"resouceFile result:{}\", existResult.isSuccess(), existResult);\n                if (existResult.isSuccess() && !StringUtil.isBlank(existResult.getResult())) {\n                    SSHTemplate.Result result = sshService.executeWithResult(repository_ip, bakcmd);\n                    if (result.isSuccess()) {\n                        logger.info(marker, \"bakResource success cmd:{} ,result :{}\", bakcmd, result.getResult());\n                    } else {\n                        logger.error(marker, \"bakResource error cmd:{} ,result: {}\", bakcmd, result);\n                        return TaskFlowStatusEnum.ABORT;\n                    }\n                }\n\n            } catch (SSHException e) {\n                logger.error(marker, \"bakResource error :{}\", e.getMessage(), e);\n                return TaskFlowStatusEnum.ABORT;\n            }\n        } else {\n            logger.error(marker, \"repository id:{} is empty,repository {}\", repositoryId, repository);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum uploadResource() {\n\n        // 1.源资源文件和打包资源文件\n        String compile_resourceName = ConstUtils.REDIS_COMPILE_BASE_DIR + resource.getName() + PACKAGE_SUFFIX;\n        String upload_path = repository.getDir() + resource.getDir();\n        // 2.上传文件\n        String upload_cmd = \"\";\n        if (ConstUtils.SSH_AUTH_TYPE == SshAuthTypeEnum.PASSWORD.getValue()) {\n            // 2.1 用户名密码\n            upload_cmd = String.format(PASSWD_UPLOAD_SH, ConstUtils.REDIS_COMPILE_BASE_DIR, compile_resourceName, resource.getName(), ConstUtils.PASSWORD, compile_resourceName,  ConstUtils.USERNAME, repository.getName(), upload_path);\n        } else if (ConstUtils.SSH_AUTH_TYPE == SshAuthTypeEnum.PUBLIC_KEY.getValue()) {\n            upload_cmd = String.format(PRIVATEKEY_UPLOAD_SH, ConstUtils.REDIS_COMPILE_BASE_DIR, compile_resourceName, resource.getName(), PRIVATE_KEY, compile_resourceName, ConstUtils.USERNAME, repository.getName(), upload_path);\n        }\n\n        try {\n            SSHTemplate.Result result = sshService.executeWithResult(containerIp, upload_cmd, COMPILE_TIME);\n            if (result.isSuccess()) {\n                logger.info(marker, \"upload cmd {} success : result:{}  \", upload_cmd, result);\n            } else {\n                logger.error(marker, \"upload cmd {} fail : result:{} \", upload_cmd, result);\n            }\n        } catch (SSHException e) {\n            logger.error(marker, \"uploadResource error :{}\", e.getMessage(), e);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n    public TaskFlowStatusEnum compileOver() {\n\n        if (resource != null) {\n            //1.清理远程目录\n            try {\n                String clear_cmd = String.format(\"rm -rf %s\", ConstUtils.REDIS_COMPILE_BASE_DIR + resource.getName() + \"*\");\n                logger.info(marker, \"clear resource :\" + sshService.executeWithResult(containerIp, clear_cmd));\n            } catch (SSHException e) {\n                e.printStackTrace();\n            }\n            //2.更新资源状态\n            resource.setIspush(PushEnum.YES.getValue());\n            resource.setLastmodify(new Date());\n            resource.setUsername(username);\n            resourceDao.update(resource);\n        } else {\n            logger.error(marker, \"resource id:{} is empty,resource {}\", resourceId, resource);\n            return TaskFlowStatusEnum.ABORT;\n        }\n        return TaskFlowStatusEnum.SUCCESS;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/util/AppWechatUtil.java",
    "content": "package com.sohu.cache.task.util;\r\n\r\nimport com.sohu.cache.alert.WeChatComponent;\r\nimport com.sohu.cache.dao.MachineRoomDao;\r\nimport com.sohu.cache.entity.AppAudit;\r\nimport com.sohu.cache.entity.AppDesc;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.stats.app.AppStatsCenter;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.EnvUtil;\r\nimport com.sohu.cache.web.service.AppService;\r\nimport com.sohu.cache.web.service.UserService;\r\nimport org.apache.commons.lang.StringUtils;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.core.env.Environment;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.ArrayList;\r\nimport java.util.Date;\r\nimport java.util.HashSet;\r\nimport java.util.Set;\r\n\r\n/**\r\n * 微信通知\r\n */\r\n@Component\r\npublic class AppWechatUtil {\r\n\r\n    private Logger logger = LoggerFactory.getLogger(AppWechatUtil.class);\r\n\r\n    @Autowired(required = false)\r\n    private WeChatComponent weChatComponent;\r\n\r\n    @Autowired\r\n    private UserService userService;\r\n\r\n    @Autowired\r\n    private MachineRoomDao machineRoomDao;\r\n\r\n    @Autowired\r\n    private AppService appService;\r\n\r\n    @Autowired\r\n    private AppStatsCenter appStatsCenter;\r\n    @Autowired\r\n    private Environment environment;\r\n\r\n    /**\r\n     * 应用状态通知\r\n     *\r\n     * @param appDesc\r\n     * @param appAudit\r\n     */\r\n    public void noticeAppResult(AppDesc appDesc, AppAudit appAudit) {\r\n        if (EnvUtil.isDev(environment)) {\r\n            return;\r\n        }\r\n        try {\r\n            long userId = appDesc.getUserId();\r\n            AppUser appUser = userService.get(userId);\r\n\r\n            long applyUserId = appAudit.getUserId();\r\n            AppUser applyAppUser = userService.get(applyUserId);\r\n\r\n            StringBuffer appCreateWeChatContent = new StringBuffer();\r\n            appCreateWeChatContent\r\n                    .append(String.format(\"申请类型: %s \\n\", appAudit.getTypeDesc()));\r\n            appCreateWeChatContent.append(String.format(\"申请描述: %s \\n\", appAudit.getInfo()));\r\n            appCreateWeChatContent.append(String.format(\"申请时间: %s \\n\", appAudit.getCreateTimeFormat()));\r\n            appCreateWeChatContent.append(String.format(\"申请人员: %s \\n\", applyAppUser.getChName()));\r\n            appCreateWeChatContent\r\n                    .append(String.format(\"申请状态: %s \\n\", appAudit.getStatusDesc()));\r\n            appCreateWeChatContent.append(String.format(\"集群名称: %s\\n\", appDesc.getName()));\r\n            //appCreateWeChatContent.append(String.format(\"集群容量: %s GB<br/>\", appDesc.getForecastMem()));\r\n            //appCreateWeChatContent.append(String.format(\"集群机房: %s(%s) <br/>\", machineRoomName, machineLogicName));\r\n            if (StringUtils.isNotBlank(appAudit.getRefuseReason())) {\r\n                appCreateWeChatContent\r\n                        .append(String.format(\"处理描述: %s \\n\", appAudit.getRefuseReason()));\r\n            }\r\n            Set<String> weChatSet = new HashSet<String>();\r\n            //weChatSet.addAll(ConstUtils.getAdminWeChatList());\r\n            weChatSet.add(appUser.getName());\r\n            weChatSet.add(applyAppUser.getName());\r\n\r\n            weChatComponent.sendWeChat(ConstUtils.NOTICE_TITLE, appCreateWeChatContent.toString(),\r\n                    new ArrayList<String>(weChatSet));\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 集群认领\r\n     *\r\n     * @param appDesc\r\n     * @param operateUser\r\n     */\r\n    public void noticeAppClaim(AppDesc appDesc, AppUser operateUser) {\r\n        if (EnvUtil.isDev(environment)) {\r\n            return;\r\n        }\r\n\r\n        try {\r\n            //            String machineRoomName = appDesc.getMachineRoom();\r\n            //            String machineLogicName;\r\n            //            MachineRoom machineRoom = machineRoomDao.getByName(machineRoomName);\r\n            //            if (appDesc.isMemcached()) {\r\n            //                machineLogicName = machineRoomName;\r\n            //            } else {\r\n            //                machineLogicName = machineRoom.getLogicName();\r\n            //            }\r\n\r\n            StringBuffer appCreateWeChatContent = new StringBuffer();\r\n            appCreateWeChatContent.append(String.format(\"事件类型: %s \\n\", \"集群认领\"));\r\n            appCreateWeChatContent.append(String.format(\"认领人员: %s <br/>\", operateUser.getChName()));\r\n            appCreateWeChatContent.append(String\r\n                    .format(\"认领时间: %s \\n\", new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(new Date())));\r\n            appCreateWeChatContent.append(String.format(\"集群名称: %s \\n\", appDesc.getName()));\r\n            //appCreateWeChatContent.append(String.format(\"集群类型: %s <br/>\", appDesc.getDbTypeDesc()));\r\n            //appCreateWeChatContent.append(String.format(\"集群机房: %s(%s) <br/>\", machineRoomName, machineLogicName));\r\n\r\n            Set<String> weChatSet = new HashSet<String>();\r\n            //weChatSet.addAll(ConstUtils.getAdminWeChatList());\r\n            weChatSet.add(operateUser.getName());\r\n\r\n            weChatComponent.sendWeChat(ConstUtils.NOTICE_TITLE, appCreateWeChatContent.toString(),\r\n                    new ArrayList<String>(weChatSet));\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    public void noticeRmtSyncFinish(long sourceAppId, long targetAppId) {\r\n        if (EnvUtil.isDev(environment)) {\r\n            return;\r\n        }\r\n        try {\r\n            AppDesc sourceAppDesc = appService.getByAppId(sourceAppId);\r\n            AppDesc targetAppDesc = appService.getByAppId(targetAppId);\r\n\r\n            StringBuffer content = new StringBuffer();\r\n            content.append(\"扩容同步完成\\n\");\r\n            content.append(String.format(\"源集群 : %s \\n\", sourceAppDesc.getName()));\r\n            content.append(String.format(\"目集群 : %s \\n\", targetAppDesc.getName()));\r\n            content.append(\"可以执行redis.sh --update\");\r\n            weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString());\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    public void noticeTaskAbort(long taskId, String stepName) {\r\n        if (EnvUtil.isDev(environment)) {\r\n            return;\r\n        }\r\n        try {\r\n            StringBuffer content = new StringBuffer();\r\n            content.append(String.format(\"任务id=%s,stepName=%s中断，请查看\", taskId, stepName));\r\n            weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString());\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    public void noticeAppScaleStop(long sourceAppId, long targetAppId) {\r\n        if (EnvUtil.isDev(environment)) {\r\n            return;\r\n        }\r\n        try {\r\n            AppDesc sourceAppDesc = appService.getByAppId(sourceAppId);\r\n            AppDesc targetAppDesc = appService.getByAppId(targetAppId);\r\n            StringBuffer content = new StringBuffer();\r\n            content.append(String.format(\"集群%s->%s的rmt被强制中断,请查看cc任务日志\",\r\n                    sourceAppDesc.getName(), targetAppDesc.getName()));\r\n            weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString());\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    public void noticeRmtUseMaster(long sourceAppId, long targetAppId) {\r\n        if (EnvUtil.isDev(environment)) {\r\n            return;\r\n        }\r\n        try {\r\n            StringBuffer content = new StringBuffer();\r\n            content.append(String.format(\"集群迁移%s->%s，使用了master做source\", sourceAppId,\r\n                    targetAppId));\r\n            weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString());\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 野生实例\r\n     *\r\n     * @param ip\r\n     * @param port\r\n     */\r\n    public void noticeWildInstance(String ip, int port) {\r\n        if (EnvUtil.isDev(environment)) {\r\n            return;\r\n        }\r\n        try {\r\n            StringBuffer content = new StringBuffer();\r\n            content.append(String.format(\"%s:%s is not in instance_info\", ip, port));\r\n            weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString());\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/task/util/SpringContextUtil.java",
    "content": "package com.sohu.cache.task.util;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SpringContextUtil implements ApplicationContextAware {\n    private ApplicationContext applicationContext;\n\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n\n    public ApplicationContext getApplicationContext() {\n        return applicationContext;\n    }\n\n    public Object getBeanById(String id) {\n        return applicationContext.getBean(id);\n    }\n\n    public Object getBeanByClass(Class c) {\n        return applicationContext.getBean(c);\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/AppKeyUtil.java",
    "content": "package com.sohu.cache.util;\r\n\r\nimport org.springframework.util.DigestUtils;\r\n\r\nimport java.nio.charset.Charset;\r\nimport java.util.Arrays;\r\n\r\n/**\r\n * appkey计算工具\r\n * \r\n * @author leifu\r\n * @Date 2016-7-9\r\n * @Time 下午9:23:59\r\n */\r\npublic class AppKeyUtil {\r\n\r\n    public static String genSecretKey(long appId) {\r\n        StringBuilder key = new StringBuilder();\r\n        // 相关参数\r\n        key.append(appId).append(ConstUtils.APP_SECRET_BASE_KEY);\r\n        // 转成char[]\r\n        char[] strs = key.toString().toCharArray();\r\n        // 排序\r\n        Arrays.sort(strs);\r\n        // md5\r\n        return MD5(new String(strs));\r\n    }\r\n\r\n    private static String MD5(String s) {\r\n            return DigestUtils.md5DigestAsHex(s.getBytes(Charset.forName(\"UTF-8\")));\r\n    }\r\n    \r\n    public static void main(String[] args) {\r\n        System.out.println(genSecretKey(10010));\r\n    }\r\n\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/ConstUtils.java",
    "content": "package com.sohu.cache.util;\n\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.web.enums.SshAuthTypeEnum;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * cachecloud常量\n */\npublic class ConstUtils {\n    public static final String NOTICE_TITLE = \"【CacheCloud】状态通知\";\n\n    /**\n     * 应用类型区分\n     * 2: Redis Cluster\n     * 6: Redis Standalone\n     * 5: Redis+Sentinel\n     * 7: Redis+Twemproxy\n     * 8: Pika+Sentinel\n     * 9: Pika+Twemproxy\n     */\n    public static final int CACHE_TYPE_REDIS_CLUSTER = 2;\n    public static final int CACHE_REDIS_SENTINEL = 5;\n    public static final int CACHE_REDIS_STANDALONE = 6;\n    public static final int CACHE_REDIS_TWEMPROXY = 7;\n    public static final int CACHE_PIKA_SENTINEL = 8;\n    public static final int CACHE_PIKA_TWEMPROXY = 9;\n\n    // 数据源名称\n    public static final String REDIS = \"redis\";\n    public static final String MACHINE = \"machine\";\n\n    //mysql收集数据的时间字段\n    public static final String COLLECT_TIME = \"CollectTime\";\n\n    // 容量转换\n    public static final int _1024 = 1024;\n\n    // 表示空字符串\n    public static final String EMPTY = \"\";\n\n    //SSO clientId\n    public static final String SSO_CLIENT = \"clientId\";\n\n    /**\n     * 服务端版本\n     */\n    public static final String CACHECLOUD_VERSION = \"3.2.0\";\n    /**\n     * 逗号\n     */\n    public static final String COMMA = \",\";\n    /**\n     * 换行\n     */\n    public static final String NEXT_LINE = \"\\n\";\n    /**\n     * 空格\n     */\n    public static final String SPACE = \" \";\n    /**\n     * 冒号\n     */\n    public static final String COLON = \":\";\n    /**\n     * 井号\n     */\n    public static final String POUND = \"#\";\n    /**\n     * 分号\n     */\n    public static final String SEMICOLON = \";\";\n    /**\n     * at @\n     */\n    public static final String AT = \"@\";\n    /**\n     * 内部错误\n     */\n    public static final String INNER_ERROR = \"cachecloud_inner_error\";\n    /**\n     * 登录跳转参数\n     */\n    public final static String RREDIRECT_URL_PARAM = \"redirectUrl\";\n    /**\n     * redis默认启动路径\n     */\n    public final static String REDIS_DEFAULT_DIR = \"/opt/cachecloud/redis\";\n    /**\n     * redis安装基准目录\n     */\n    public static final String REDIS_INSTALL_BASE_DIR = \"/opt/cachecloud\";\n    public static final String REDIS_COMPILE_BASE_DIR = \"/opt/cachecloud/compile/\";\n    /**\n     * pika 安装基准目录,挂载ssd\n     */\n    public static final String PIKA_INSTALL_BASE_DIR = \"/opt/cachecloud/pika\";\n    /**\n     * nutcracker当期版本\n     */\n    public static final String DEFAULT_NUTCRACKER_CUR_VERSION = \"0.4.1\";\n    /**\n     * redis migrate tool当期版本\n     */\n    public static final String DEFAULT_RMT_CUR_VERSION = \"1.0\";\n    /**\n     * codis当期版本\n     */\n    public static final String DEFAULT_CODIS_CUR_VERSION = \"3.2.2\";\n    /**\n     * pika当期版本\n     */\n    public static final String DEFAULT_PIKA_CUR_VERSION = \"3.0.3\";\n    /**\n     * slave相对于master节点端口增值\n     */\n    public static final int SLAVE_PORT_INCREASE = 1000;\n    /**\n     * 机器报警阀值\n     */\n    public static final double DEFAULT_MEMORY_USAGE_RATIO_THRESHOLD = 85.0;\n    /**\n     * 机器磁盘报警阀值\n     */\n    public static final double DEFAULT_DISK_USAGE_RATIO_THRESHOLD = 70.0;\n    /**\n     * 应用客户端连接数报警阀值\n     */\n    public static final int DEFAULT_APP_CLIENT_CONN_THRESHOLD = 2000;\n    /**\n     * 机器统一的用户名、密码、端口\n     */\n    public static final String DEFAULT_USERNAME = \"cachecloud\";\n    public static final String DEFAULT_PASSWORD = \"cachecloud\";\n    public static final String DEFAULT_USER_LOGIN_ENCRY_KEY = \"97c9d9de0a2dbd64\";\n    public static final String DEFAULT_USER_PASSWORD = \"89750bfb62c09ba4f7dfe8b7e45ca31f\";\n    public static final int DEFAULT_SSH_PORT_DEFAULT = 22;\n    /**\n     * ssh授权方式：参考SshAuthTypeEnum\n     */\n    public static final int DEFAULT_SSH_AUTH_TYPE = SshAuthTypeEnum.PUBLIC_KEY.getValue();\n    /**\n     * public key pem\n     */\n    public static final String DEFAULT_PUBLIC_KEY_PEM = \"/opt/ssh/id_rsa\";\n    public static final String DEFAULT_PUBLIC_USERNAME = \"cachecloud\";\n    public static final String MEMCACHE_USER = \"memcached_server\";\n    public static final String MEMCACHE_KEY_PEM = \"/home/redis_server/cachecloud/cachecloud/memcache_server_key/id_rsa\";\n\n    /**\n     * 管理员相关\n     */\n    public static final String DEFAULT_SUPER_ADMIN_NAME = \"admin\";\n    public static final String DEFAULT_SUPER_ADMIN_PASS = \"admin\";\n    public static final String DEFAULT_SUPER_ADMINS = \"admin\";\n\n    /**\n     * redis-migrate-tool端口\n     */\n    public static final int DEFAULT_REDIS_MIGRATE_TOOL_PORT = 8888;\n    /**\n     * redis server基准端口\n     */\n    public static final int DEFAULT_REDIS_SERVER_BASE_PORT = 6400;\n    /**\n     * redis sentinel基准端口\n     */\n    public static final int DEFAULT_REDIS_SENTINEL_BASE_PORT = 26379;\n\n    /**\n     * 1是session,2是cookie(参考UserLoginTypeEnum)\n     */\n    public static final int DEFAULT_USER_LOGIN_TYPE = 2;\n    /**\n     * cachecloud根目录，这个要与cachecloud-init.sh脚本中的目录一致\n     */\n    public static final String DEFAULT_CACHECLOUD_BASE_DIR = \"/opt\";\n    /**\n     * 是否定期清理各种统计数据：(详见CleanUpStatisticsJob)\n     */\n    public static final boolean DEFAULT_WHETHER_SCHEDULE_CLEAN_DATA = true;\n    /**\n     * appkey秘钥\n     */\n    public static final String DEFAULT_APP_SECRET_BASE_KEY = \"cachecloud-2014\";\n    /**\n     * 机器性能统计周期(分钟)\n     */\n    public static final int DEFAULT_MACHINE_STATS_CRON_MINUTE = 1;\n\n    //应用自动扩容与内存审计（判断是否可缩容）相关 start\n    /**\n     * 后台自动处理用户\n     */\n    public static final long DEFAULT_BOT_USER_ID = 0;\n\n    public static long BOT_USER_ID = ConstUtils.DEFAULT_BOT_USER_ID;\n\n    /**\n     * 自动扩容最大允许分片内存大小\n     */\n    public static final long DEFAULT_REDIS_EXPAND_CAPACITY_LIMIT = 8L * 1024 * 1024 * 1024;\n\n    public static long REDIS_EXPAND_CAPACITY_LIMIT = ConstUtils.DEFAULT_REDIS_EXPAND_CAPACITY_LIMIT;\n\n    /**\n     * 应用上线多少天之后，可判断缩容\n     */\n    public static final int DEFAULT_REDIS_REDUCE_CAPACITY_BUFFER_TIME = 15;\n\n    public static int REDIS_REDUCE_CAPACITY_BUFFER_TIME = ConstUtils.DEFAULT_REDIS_REDUCE_CAPACITY_BUFFER_TIME;\n\n    /**\n     * 应用缩容预计处理时间\n     */\n    public static final int DEFAULT_REDIS_REDUCE_CAPACITY_SCHEDULE_TIME = 15;\n\n    public static int REDIS_REDUCE_CAPACITY_SCHEDULE_TIME = ConstUtils.DEFAULT_REDIS_REDUCE_CAPACITY_SCHEDULE_TIME;\n\n    /**\n     * 应用缩容审计告警，是否发送应用负责人\n     */\n    public static final boolean DEFAULT_REDIS_REDUCE_CAPACITY_SENDMAIL = false;\n\n    public static boolean REDIS_REDUCE_CAPACITY_SENDMAIL = ConstUtils.DEFAULT_REDIS_REDUCE_CAPACITY_SENDMAIL;\n\n    /**\n     * 应用扩容告警，是否发送应用负责人\n     */\n    public static final boolean DEFAULT_REDIS_EXPAND_CAPACITY_SENDMAIL = false;\n\n    public static boolean REDIS_EXPAND_CAPACITY_SENDMAIL = ConstUtils.DEFAULT_REDIS_EXPAND_CAPACITY_SENDMAIL;\n\n    /**\n     * 应用内存使用近几天最大值，用于判断是否有峰值\n     */\n    public static final int DEFAULT_REDIS_MEM_USED_MAX_DAYS = 3;\n\n    public static int REDIS_MEM_USED_MAX_DAYS = ConstUtils.DEFAULT_REDIS_MEM_USED_MAX_DAYS;\n    /**\n     * 分片内存缩容最小值（低于不可缩容）\n     */\n    public static final long DEFAULT_REDIS_REDUCE_CAPACITY_SHARDING_MEM = 125L * 1024 * 1024 * 1024 / 1000 ;\n\n    public static long REDIS_REDUCE_CAPACITY_SHARDING_MEM = ConstUtils.DEFAULT_REDIS_REDUCE_CAPACITY_SHARDING_MEM ;\n\n    /**\n     * 分片内存缩容——报警内存最小缩容容量\n     */\n    public static final long DEFAULT_REDIS_REDUCE_CAPACITY_MEM_MIN = 5L * 1024 * 1024 * 1024 / 10;\n\n    public static long REDIS_REDUCE_CAPACITY_MEM_MIN = ConstUtils.DEFAULT_REDIS_REDUCE_CAPACITY_MEM_MIN;\n\n    /**\n     * 分片内存扩容步长\n     */\n    public static final long DEFAULT_REDIS_EXPAND_CAPACITY_STEP = 125L * 1024 * 1024 * 1024 / 1000 ;\n\n    public static long REDIS_EXPAND_CAPACITY_STEP = ConstUtils.DEFAULT_REDIS_EXPAND_CAPACITY_STEP ;\n\n    /**\n     * 应用内存扩容AppAutoCapacityServiceImpl.ExpandConfig\n     */\n    public static final String DEFAULT_REDIS_EXPAND_CAPACITY_CONFIG = \"0,3,0,85,20,1000;3,5,10,85,20,200;5,10,10,85,15,200;10,20,10,85,10,150;20,40,5,90,10,50;40,80,5,95,5,30;80,120,3,95,5,20;120,1000,1,95,0,0\";\n\n    public static String REDIS_EXPAND_CAPACITY_CONFIG = ConstUtils.DEFAULT_REDIS_EXPAND_CAPACITY_CONFIG;\n\n    /**\n     * 应用内存扩容开关\n     */\n    public static final boolean DEFAULT_REDIS_EXPAND_CAPACITY_OPEN_FLAG = false;\n\n    public static boolean REDIS_EXPAND_CAPACITY_OPEN_FLAG = ConstUtils.DEFAULT_REDIS_EXPAND_CAPACITY_OPEN_FLAG;\n    //应用自动扩容与内存审计（判断是否可缩容）相关 end\n\n\n    /**\n     * rdb文件物理机存储路径\n     */\n    public static final String DEFAULT_RDB_MACHINE_DIR = \"/data/redis/data/\";\n\n    public static String RDB_MACHINE_DIR = ConstUtils.DEFAULT_RDB_MACHINE_DIR;\n\n    /**\n     * redis 版本\n     */\n    public static Map<Integer, SystemResource> REDIS_RESOURCE = new HashMap<>();\n\n    /**\n     * 应用持久化主从特殊配置\n     */\n    public static final String DEFAULT_APP_PERSISTENCE_CONFIG_MAP = \"[\" +\n            \"{\" +\n            \"\\\"isMaster\\\":\\\"true\\\",\" +\n            \"\\\"persistenceMap\\\":{\" +\n            \"\\\"0\\\":{\" +\n            \"\\\"appendonly\\\":\\\"yes\\\",\" +\n            \"\\\"appendfsync\\\":\\\"everysec\\\"\" +\n            \"},\" +\n            \"\\\"1\\\":{\" +\n            \"\\\"appendonly\\\":\\\"yes\\\",\" +\n            \"\\\"appendfsync\\\":\\\"no\\\"\" +\n            \"},\" +\n            \"\\\"2\\\":{\" +\n            \"\\\"appendonly\\\":\\\"no\\\"\" +\n            \"},\" +\n            \"\\\"3\\\":{\" +\n            \"\\\"appendonly\\\":\\\"no\\\"\" +\n            \"}\" +\n            \"}\" +\n            \"},\" +\n            \"{\" +\n            \"\\\"isMaster\\\":\\\"false\\\",\" +\n            \"\\\"persistenceMap\\\":{\" +\n            \"\\\"0\\\":{\" +\n            \"\\\"appendonly\\\":\\\"yes\\\",\" +\n            \"\\\"appendfsync\\\":\\\"everysec\\\"\" +\n            \"},\" +\n            \"\\\"1\\\":{\" +\n            \"\\\"appendonly\\\":\\\"yes\\\",\" +\n            \"\\\"appendfsync\\\":\\\"everysec\\\"\" +\n            \"},\" +\n            \"\\\"2\\\":{\" +\n            \"\\\"appendonly\\\":\\\"yes\\\",\" +\n            \"\\\"appendfsync\\\":\\\"everysec\\\"\" +\n            \"},\" +\n            \"\\\"3\\\":{\" +\n            \"\\\"appendonly\\\":\\\"no\\\"\" +\n            \"}\" +\n            \"}\" +\n            \"}\" +\n            \"]\";\n\n    public static String APP_PERSISTENCE_CONFIG_MAP = ConstUtils.DEFAULT_APP_PERSISTENCE_CONFIG_MAP;\n\n    /**\n     * big key阈值\n     */\n    public static final int DEFAULT_STRING_MAX_LENGTH = 100 * 1024;\n    public static final int DEFAULT_HASH_MAX_LENGTH = 50000;\n    public static final int DEFAULT_LIST_MAX_LENGTH = 50000;\n    public static final int DEFAULT_SET_MAX_LENGTH = 50000;\n    public static final int DEFAULT_ZSET_MAX_LENGTH = 50000;\n    public static double MEMORY_USAGE_RATIO_THRESHOLD = DEFAULT_MEMORY_USAGE_RATIO_THRESHOLD;\n    public static double DISK_USAGE_RATIO_THRESHOLD = DEFAULT_DISK_USAGE_RATIO_THRESHOLD;\n    public static int APP_CLIENT_CONN_THRESHOLD = DEFAULT_APP_CLIENT_CONN_THRESHOLD;\n    public static String USERNAME = DEFAULT_USERNAME;\n    public static String PASSWORD = DEFAULT_PASSWORD;\n    public static int SSH_PORT_DEFAULT = DEFAULT_SSH_PORT_DEFAULT;\n    public static int SSH_AUTH_TYPE = DEFAULT_SSH_AUTH_TYPE;\n\n    public static int SSH_CONNECTION_TIMEOUT = 5000;\n    public static String PUBLIC_KEY_PEM = DEFAULT_PUBLIC_KEY_PEM;\n    public static String PUBLIC_USERNAME = DEFAULT_PUBLIC_USERNAME;\n    public static String SUPER_ADMIN_NAME = DEFAULT_SUPER_ADMIN_NAME;\n    public static String SUPER_ADMIN_PASS = DEFAULT_SUPER_ADMIN_PASS;\n    public static String SUPER_ADMINS = DEFAULT_SUPER_ADMINS;\n    public static String USER_LOGIN_ENCRY_KEY = DEFAULT_USER_LOGIN_ENCRY_KEY;\n    public static List<String> SUPER_MANAGER;\n    /**\n     * 联系人\n     */\n    public static String CONTACT;\n\n    /**\n     * redis-migrate-tool相关路径\n     */\n    public static String REDIS_MIGRATE_TOOL_HOME;\n    /**\n     * redis-shake相关路径\n     */\n    public static String REDIS_SHAKE_HOME;\n    /**\n     * redis-full-check相关\n     */\n    public static String REDIS_FULL_CHECK_HOME;\n    public static int REDIS_MIGRATE_TOOL_PORT = DEFAULT_REDIS_MIGRATE_TOOL_PORT;\n    public static int REDIS_SERVER_BASE_PORT = DEFAULT_REDIS_SERVER_BASE_PORT;\n    public static int REDIS_SENTINEL_BASE_PORT = DEFAULT_REDIS_SENTINEL_BASE_PORT;\n    public static int USER_LOGIN_TYPE = DEFAULT_USER_LOGIN_TYPE;\n    /**\n     * cookie登录方式所需要的域\n     */\n    public static String CACHECLOUD_BASE_DIR = DEFAULT_CACHECLOUD_BASE_DIR;\n    public static boolean WHETHER_SCHEDULE_CLEAN_DATA = DEFAULT_WHETHER_SCHEDULE_CLEAN_DATA;\n    public static String APP_SECRET_BASE_KEY = DEFAULT_APP_SECRET_BASE_KEY;\n    public static int MACHINE_STATS_CRON_MINUTE = DEFAULT_MACHINE_STATS_CRON_MINUTE;\n    /**\n     * 领导邮件\n     */\n    public static List<String> LEADER_EMAIL_LIST;\n    public static String NUTCRACKER_CUR_VERSION = DEFAULT_NUTCRACKER_CUR_VERSION;\n    public static String RMT_CUR_VERSION = DEFAULT_RMT_CUR_VERSION;\n    public static String CODIS_CUR_VERSION = DEFAULT_CODIS_CUR_VERSION;\n    public static String PIKA_CUR_VERSION = DEFAULT_PIKA_CUR_VERSION;\n    /**\n     * 内部redis配置\n     */\n    public static volatile String CACHECLOUD_INTERNAL_REDIS_SENTINELS = \"\";\n    public static volatile String CACHECLOUD_INTERNAL_REDIS_MASTERNAME = \"\";\n    public static volatile String CACHECLOUD_INTERNAL_REDIS_PASSWORD = \"\";\n    public static int STRING_MAX_LENGTH = DEFAULT_STRING_MAX_LENGTH;\n    public static int HASH_MAX_LENGTH = DEFAULT_HASH_MAX_LENGTH;\n    public static int LIST_MAX_LENGTH = DEFAULT_LIST_MAX_LENGTH;\n    public static int SET_MAX_LENGTH = DEFAULT_SET_MAX_LENGTH;\n    public static int ZSET_MAX_LENGTH = DEFAULT_ZSET_MAX_LENGTH;\n\n    public static String getRedisMigrateToolCmd(String name) {\n        return ConstUtils.getRedisToolDir(name) + \"src/redis-migrate-tool\";\n    }\n\n    public static String getRedisShakeStartCmd() {\n        return \"sh \" + REDIS_SHAKE_HOME + \"start.sh\";\n    }\n\n    public static String getRedisShakeLinuxCmd(String name) {\n        return ConstUtils.getRedisToolDir(name) + \"redis-shake.linux\";\n    }\n\n    public static String getRedisShakeStopCmd() {\n        return \"sh \" + REDIS_SHAKE_HOME + \"stop.sh\";\n    }\n\n    public static String getRedisFullCheckDir() {\n        return REDIS_FULL_CHECK_HOME;\n    }\n\n    public static String getRedisFullCheckResultDir() {\n        return REDIS_FULL_CHECK_HOME + \"data/\";\n    }\n\n    public static String getRedisFullCheckMetricDir() {\n        return REDIS_FULL_CHECK_HOME + \"metric/\";\n    }\n\n    public static String getRedisFullCheckLogDir() {\n        return REDIS_FULL_CHECK_HOME + \"logs/\";\n    }\n\n    public static String getRedisFullCheckCmd() {\n        return REDIS_FULL_CHECK_HOME + \"redis-full-check\";\n    }\n\n    public static String getRedisMigrateToolDir() {\n        return REDIS_MIGRATE_TOOL_HOME + \"data/\";\n    }\n\n    public static String getRedisShakeDir() {\n        return REDIS_SHAKE_HOME;\n    }\n\n    public static String getRedisShakePidDir(String name) {\n        return ConstUtils.getRedisToolDir(name) + \"pid/\";\n    }\n\n    public static String getRedisShakeLogsDir(String name) {\n        return ConstUtils.getRedisToolDir(name) + \"logs/\";\n    }\n\n    public static String getRedisShakeConfDir(String name) {\n        return ConstUtils.getRedisToolDir(name) + \"conf/\";\n    }\n\n    public static String getRedisShakeHttpPort() {\n        return \"9320\";\n    }\n\n    /**\n     * 空闲key\n     *\n     * @param appId\n     * @param auditId\n     * @return\n     */\n    public static String getRedisServerIdleKey(long appId, long auditId) {\n        return String.format(\"cc:key:idle:%s:%s\", appId, auditId);\n    }\n\n    /**\n     * key类型\n     *\n     * @param appId\n     * @param auditId\n     * @return\n     */\n    public static String getRedisServerTypeKey(long appId, long auditId) {\n        return String.format(\"cc:key:type:%s:%s\", appId, auditId);\n    }\n\n    /**\n     * key ttl\n     *\n     * @param appId\n     * @param auditId\n     * @return\n     */\n    public static String getRedisServerTtlKey(long appId, long auditId) {\n        return String.format(\"cc:key:ttl:%s:%s\", appId, auditId);\n    }\n\n    /**\n     * value size\n     *\n     * @param appId\n     * @param auditId\n     * @return\n     */\n    public static String getRedisServerValueSizeKey(long appId, long auditId) {\n        return String.format(\"cc:key:valueSize:%s:%s\", appId, auditId);\n    }\n\n    /**\n     * 生成taskflowId\n     *\n     * @param taskFlowId\n     * @return\n     */\n    public static String getTaskFlowRedisKey(String taskFlowId) {\n        return \"cc:taskflow:\" + taskFlowId;\n    }\n\n\n    public static String getInstanceScanKey(long taskId, String hostPort) {\n        return String.format(\"cc:key:scan:%s:%s\", taskId, hostPort);\n    }\n\n    public static String getInstanceDelKey(long taskId, String hostPort) {\n        return String.format(\"cc:key:del:%s:%s\", taskId, hostPort);\n    }\n\n    public static String getInstanceBigKey(long taskId, String hostPort) {\n        return String.format(\"cc:key:bigkey:%s:%s\", taskId, hostPort);\n    }\n\n    public static String getInstanceIdleKey(long taskId, String hostPort) {\n        return String.format(\"cc:key:idlekey:%s:%s\", taskId, hostPort);\n    }\n\n    public static String getInstanceHotKey(long taskId, String hostPort) {\n        return String.format(\"cc:key:hotkey:%s:%s\", taskId, hostPort);\n    }\n\n    public static String getInstanceSlotAnalysis(long taskId, String hostPort) {\n        return String.format(\"cc:key:slotAnalysis:%s:%s\", taskId, hostPort);\n    }\n\n    public static String getInstanceScanClean(long taskId, String hostPort) {\n        return String.format(\"cc:key:scanClean:%s:%s\", taskId, hostPort);\n    }\n\n    public static String getScanClean(long taskId) {\n        return String.format(\"cc:key:scanClean:%s\", taskId);\n    }\n\n    public static String getRedisDir(String versionName){\n        return String.format(\"%s/%s\", REDIS_INSTALL_BASE_DIR, versionName);\n    }\n\n    public static String getRedisToolDir(String versionName){\n        return String.format(\"%s/%s/\", REDIS_INSTALL_BASE_DIR, versionName);\n    }\n}\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/DemoCodeUtil.java",
    "content": "package com.sohu.cache.util;\n\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Created by hym\n */\npublic class DemoCodeUtil {\n\n    public static final List<String> redisCluster;\n    public static final List<String> redisSentinel;\n    public static final List<String> redisStandalone;\n\n    public static final List<String> redisSentinelSpring;\n    public static final List<String> redisClusterSpring;\n\n\n    private static final String springAppId = \"${your appId}\";\n\n    static {\n        List<String> tmpRedisCluster = new ArrayList<>();\n        tmpRedisCluster.add(\"PipelineCluster redisCluster = null;                                   \");\n        tmpRedisCluster.add(\"// 使用默认配置                                                             \");\n        tmpRedisCluster.add(\"//redisCluster = ClientBuilder.redisCluster(appId).build();            \");\n        tmpRedisCluster.add(\"/**                                                                       \");\n        tmpRedisCluster.add(\" * 使用自定义配置：                                                         \");\n        tmpRedisCluster.add(\" *  1. setTimeout：redis操作的超时设置，默认2000毫秒。                                    \");\n        tmpRedisCluster.add(\" *  2. setMaxRedirections：节点重定向的最大次数，不建议修改。                              \");\n        tmpRedisCluster.add(\" */                                                                       \");\n        tmpRedisCluster.add(\"JedisPoolConfig poolConfig = new JedisPoolConfig();       \");\n        tmpRedisCluster.add(\"redisCluster = ClientBuilder.redisCluster(appId)                       \");\n        tmpRedisCluster.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.setJedisPoolConfig(poolConfig)                                   \");\n        tmpRedisCluster.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.setTimeout(Protocol.DEFAULT_TIMEOUT)                                                    \");\n        tmpRedisCluster.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.setMaxRedirections(5)                                                    \");\n        tmpRedisCluster.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.build();                                                         \");\n        tmpRedisCluster.add(\"//1.字符串value\");\n        tmpRedisCluster.add(\"redisCluster.set(\\\"key1\\\", \\\"value1\\\");                                \");\n        tmpRedisCluster.add(\"assertEquals(\\\"value1\\\", redisCluster.get(\\\"key1\\\"));                  \");\n        tmpRedisCluster.add(\"//2.实体型value\");\n        tmpRedisCluster.add(\"//高效的序列化工具(spring中要配置成单例)\");\n        tmpRedisCluster.add(\"ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();\");\n        tmpRedisCluster.add(\"long vid = 120;\");\n        tmpRedisCluster.add(\"String videoName = \\\"屌丝男士\\\";\");\n        tmpRedisCluster.add(\"String videoKey = \\\"video:\\\" + vid;\");\n        tmpRedisCluster.add(\"//long vid; String videoName\");\n        tmpRedisCluster.add(\"Video video = new Video(vid, videoName);\");\n        tmpRedisCluster.add(\"redisCluster.set(videoKey, protostuffSerializer.serialize(video));\");\n        tmpRedisCluster.add(\"byte[] resultBytes = redisCluster.getBytes(videoKey);\");\n        tmpRedisCluster.add(\"// 反序列化获取结果\");\n        tmpRedisCluster.add(\"Video resultVideo = protostuffSerializer.deserialize(resultBytes);\");\n        tmpRedisCluster.add(\"assertEquals(videoName, resultVideo.getName());\");\n        redisCluster = Collections.unmodifiableList(tmpRedisCluster);\n\n        List<String> tmpRedisSentinel = new ArrayList<>();\n        tmpRedisSentinel.add(\"JedisSentinelPool sentinelPool = null;                                   \");\n        tmpRedisSentinel.add(\"// 使用默认配置                                                           \");\n        tmpRedisSentinel.add(\"//sentinelPool = ClientBuilder.redisSentinel(appId).build();             \");\n        tmpRedisSentinel.add(\"/**                                                                      \");\n        tmpRedisSentinel.add(\" * 自定义配置：setTimeout 连接和操作超时时间，默认2000毫秒。                  \");\n        tmpRedisSentinel.add(\" */                                                                      \");\n        tmpRedisSentinel.add(\"JedisPoolConfig poolConfig = new JedisPoolConfig();      \");\n        tmpRedisSentinel.add(\"sentinelPool = ClientBuilder.redisSentinel(appId)                        \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.setTimeout(Protocol.DEFAULT_TIMEOUT)                                                \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.setPoolConfig(poolConfig)                                       \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.build();                                                        \");\n\n        tmpRedisSentinel.add(\"Jedis jedis = null;                                                                           \");\n        tmpRedisSentinel.add(\"try {                                                                           \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;jedis = sentinelPool.getResource();                                         \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;//1.字符串value                                                              \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;jedis.set(\\\"key1\\\", \\\"1\\\");                                                  \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;//2.实体型value\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;//高效的序列化工具(spring中要配置成单例)，已经内置在cachecloud的客户端中直接引入即可.\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;long vid = 120;\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;String videoName = \\\"屌丝男士\\\";\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;String videoKey = \\\"video:\\\" + vid;\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;//long vid; String videoName\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;Video video = new Video(vid, videoName);\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;jedis.set(videoKey.getBytes(), protostuffSerializer.serialize(video));\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;byte[] resultBytes = jedis.get(videoKey.getBytes());\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;//反序列化获取结果\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;Video resultVideo = protostuffSerializer.deserialize(resultBytes);\");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(videoName, resultVideo.getName());\");\n\n        tmpRedisSentinel.add(\"} catch (Exception e) {                                                         \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;logger.error(e.getMessage(), e);                                            \");\n        tmpRedisSentinel.add(\"} finally {                                                                     \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;if(jedis!=null)                                                                \");\n        tmpRedisSentinel.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jedis.close();                                                                 \");\n        tmpRedisSentinel.add(\"}                                                                               \");\n        redisSentinel = Collections.unmodifiableList(tmpRedisSentinel);\n\n        List<String> tmpRedisStandalone = new ArrayList<>();\n        tmpRedisStandalone.add(\"JedisPool jedisPool = null;                                         \");\n        tmpRedisStandalone.add(\"// 使用默认配置                                                      \");\n        tmpRedisStandalone.add(\"//jedisPool = ClientBuilder.redisStandalone(appId).build();         \");\n        tmpRedisStandalone.add(\"/**                                                                 \");\n        tmpRedisStandalone.add(\" * 自定义配置：setTimeout 连接和操作超时时间，默认2000毫秒。                  \");\n        tmpRedisStandalone.add(\" */                                                                 \");\n        tmpRedisStandalone.add(\"JedisPoolConfig poolConfig = new JedisPoolConfig(); \");\n        tmpRedisStandalone.add(\"poolConfig.setMaxWaitMillis(Protocol.DEFAULT_TIMEOUT);                                  \");\n        tmpRedisStandalone.add(\"jedisPool = ClientBuilder.redisStandalone(appId)                    \");\n        tmpRedisStandalone.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.setTimeout(Protocol.DEFAULT_TIMEOUT)                                           \");\n        tmpRedisStandalone.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.setPoolConfig(poolConfig)                                  \");\n        tmpRedisStandalone.add(\"&nbsp;&nbsp;&nbsp;&nbsp;.build();                                                   \");\n        tmpRedisStandalone.add(\"Jedis jedis = jedisPool.getResource();                              \");\n        tmpRedisStandalone.add(\"jedis.setnx(\\\"key2\\\", \\\"5\\\");                                       \");\n        tmpRedisStandalone.add(\"assertEquals(\\\"10\\\", jedis.incrBy(\\\"key2\\\", 5));                    \");\n        tmpRedisStandalone.add(\"jedis.close();                                                       \");\n        redisStandalone = Collections.unmodifiableList(tmpRedisStandalone);\n\n        //redis-sentinel的spring配置\n        List<String> tmpRedisSentinelSpring = new ArrayList<>();\n        tmpRedisSentinelSpring.add(\"//spring 配置\");\n        tmpRedisSentinelSpring.add(\"&lt;bean id=\\\"redisSentinelFactory\\\" class=\\\"com.sohu.tv.mobil.common.data.RedisSentinelFactory\\\" init-method=\\\"init\\\"&gt;\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name=\\\"appId\\\" value=\\\"\" + springAppId + \"\\\"/&gt;\");\n        tmpRedisSentinelSpring.add(\"&lt;/bean&gt;\");\n        tmpRedisSentinelSpring.add(\"&lt;bean id=\\\"redisSentinelPool\\\" factory-bean=\\\"redisSentinelFactory\\\" factory-method=\\\"getJedisSentinelPool\\\"/&gt;\");\n        tmpRedisSentinelSpring.add(\"&lt;!--高效的序列化工具--/&gt\");\n        tmpRedisSentinelSpring.add(\"&lt;bean id=\\\"protostuffSerializer\\\" class=\\\"redis.clients.jedis.serializable.ProtostuffSerializer\\\"/&gt;\");\n        tmpRedisSentinelSpring.add(\"\");\n        tmpRedisSentinelSpring.add(\"package xx.xx;\");\n        tmpRedisSentinelSpring.add(\"import com.sohu.tv.builder.ClientBuilder;\");\n        tmpRedisSentinelSpring.add(\"import redis.clients.jedis.JedisPoolConfig;\");\n        tmpRedisSentinelSpring.add(\"import org.slf4j.Logger;\");\n        tmpRedisSentinelSpring.add(\"import org.slf4j.LoggerFactory;\");\n        tmpRedisSentinelSpring.add(\"import redis.clients.jedis.JedisSentinelPool;\");\n        tmpRedisSentinelSpring.add(\"import redis.clients.jedis.Protocol;\");\n        tmpRedisSentinelSpring.add(\"public class RedisSentinelFactory {\");\n        tmpRedisSentinelSpring.add(\"\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;private final Logger logger = LoggerFactory.getLogger(this.getClass());\");\n        tmpRedisSentinelSpring.add(\"\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;private JedisSentinelPool jedisSentinelPool;\");\n        tmpRedisSentinelSpring.add(\"\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;private int appId;\");\n        tmpRedisSentinelSpring.add(\"\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;public void init(){\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//根据自己需要设置poolConfig\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JedisPoolConfig poolConfig = new JedisPoolConfig();\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;poolConfig.setMaxWaitMillis(Protocol.DEFAULT_TIMEOUT);\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//根据自己需要设置超时时间\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jedisSentinelPool = ClientBuilder.redisSentinel(appId)\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setPoolConfig(poolConfig)\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.build();\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception e) {\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(e.getMessage(), e);\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisSentinelSpring.add(\"\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;public JedisSentinelPool getJedisSentinelPool() {\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return jedisSentinelPool;\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisSentinelSpring.add(\"\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;public void setAppId(int appId) {\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.appId = appId;\");\n        tmpRedisSentinelSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisSentinelSpring.add(\"}\");\n        redisSentinelSpring = Collections.unmodifiableList(tmpRedisSentinelSpring);\n\n        // redis-cluster的spring配置\n        List<String> tmpRedisClusterSpring = new ArrayList<>();\n        tmpRedisClusterSpring.add(\"//spring 配置\");\n        tmpRedisClusterSpring.add(\"&lt;bean id=\\\"redisClusterFactory\\\" class=\\\"xx.xx.RedisClusterFactory\\\" init-method=\\\"init\\\"&gt;\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name=\\\"appId\\\" value=\\\"\" + springAppId + \"\\\"/&gt;\");\n        tmpRedisClusterSpring.add(\"&lt;/bean>\");\n        tmpRedisClusterSpring.add(\"&lt;bean id=\\\"redisCluster\\\" factory-bean=\\\"redisClusterFactory\\\" factory-method=\\\"getRedisCluster\\\"/&gt;\");\n        tmpRedisClusterSpring.add(\"&lt;!--高效的序列化工具--/&gt\");\n        tmpRedisClusterSpring.add(\"&lt;bean id=\\\"protostuffSerializer\\\" class=\\\"redis.clients.jedis.serializable.ProtostuffSerializer\\\"/&gt;\");\n        tmpRedisClusterSpring.add(\"\");\n        tmpRedisClusterSpring.add(\"package xx.xx;\");\n        tmpRedisClusterSpring.add(\"import com.sohu.tv.builder.ClientBuilder;\");\n        tmpRedisClusterSpring.add(\"import redis.clients.jedis.JedisPoolConfig;\");\n        tmpRedisClusterSpring.add(\"import org.slf4j.Logger;\");\n        tmpRedisClusterSpring.add(\"import org.slf4j.LoggerFactory;\");\n        tmpRedisClusterSpring.add(\"import redis.clients.jedis.PipelineCluster;\");\n        tmpRedisClusterSpring.add(\"import redis.clients.jedis.Protocol;\");\n        tmpRedisClusterSpring.add(\"public class RedisClusterFactory {\");\n        tmpRedisClusterSpring.add(\"\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;private final Logger logger = LoggerFactory.getLogger(this.getClass());\");\n        tmpRedisClusterSpring.add(\"\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;private PipelineCluster redisCluster;\");\n        tmpRedisClusterSpring.add(\"\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;private int appId;\");\n        tmpRedisClusterSpring.add(\"\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;public void init() {\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//根据自己需要设置poolConfig\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JedisPoolConfig poolConfig = new JedisPoolConfig();\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;poolConfig.setMaxWaitMillis(Protocol.DEFAULT_TIMEOUT);\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//根据自己需要修改参数\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redisCluster = ClientBuilder.redisCluster(appId)\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setJedisPoolConfig(poolConfig)\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.build();\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception e) {\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(e.getMessage(), e);\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisClusterSpring.add(\"\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;public PipelineCluster getRedisCluster() {\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return redisCluster;\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisClusterSpring.add(\"\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;public void setAppId(int appId) {\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.appId = appId;\");\n        tmpRedisClusterSpring.add(\"&nbsp;&nbsp;&nbsp;&nbsp;}\");\n        tmpRedisClusterSpring.add(\"}\");\n        redisClusterSpring = Collections.unmodifiableList(tmpRedisClusterSpring);\n    }\n\n    /**\n     * 获取依赖\n     *\n     * @return\n     */\n    public static List<String> getDependencyRedis() {\n        List<String> dependencyRedis = new ArrayList<String>();\n\n        // 依赖\n        dependencyRedis.add(\"&lt;dependency&gt;                                                     \");\n        dependencyRedis.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;com.sohu.tv&lt;/groupId&gt;                                   \");\n        dependencyRedis.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;cachecloud-client-redis&lt;/artifactId&gt;                       \");\n\n        dependencyRedis.add(\"&lt;/dependency&gt;                                                    \");\n        dependencyRedis.add(\"&lt;repositories&gt;                                                   \");\n        dependencyRedis.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&lt;repository&gt;                                                     \");\n        dependencyRedis.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;id&gt;sohu.nexus&lt;/id&gt;                                              \");\n        dependencyRedis.add(\"&nbsp;&nbsp;&nbsp;&nbsp;&lt;/repository&gt;                                                    \");\n        dependencyRedis.add(\"&lt;/repositories&gt;                                                  \");\n\n        return dependencyRedis;\n    }\n\n    public static List<String> getCode(int appType, long appId) {\n        List<String> list = null;\n        switch (appType) {\n            case ConstUtils.CACHE_REDIS_SENTINEL: {\n                list = new ArrayList<String>(redisSentinel);\n                break;\n            }\n            case ConstUtils.CACHE_REDIS_STANDALONE: {\n                list = new ArrayList<String>(redisStandalone);\n                break;\n            }\n            case ConstUtils.CACHE_TYPE_REDIS_CLUSTER: {\n                list = new ArrayList<String>(redisCluster);\n                break;\n            }\n            default:\n                break;\n        }\n        if (list != null && list.size() > 0) {\n            if (!list.get(0).contains(\"appId =\")) {\n                list.add(0, \"long appId = \" + appId + \";\");\n            }\n        }\n        return list;\n    }\n\n    public static List<String> getSpringConfig(int appType, long appId) {\n        List<String> list = new ArrayList<String>();\n        switch (appType) {\n            case ConstUtils.CACHE_REDIS_SENTINEL: {\n                list.addAll(redisSentinelSpring);\n                break;\n            }\n            case ConstUtils.CACHE_REDIS_STANDALONE: {\n                break;\n            }\n            case ConstUtils.CACHE_TYPE_REDIS_CLUSTER: {\n                list.addAll(redisClusterSpring);\n                break;\n            }\n            default:\n                break;\n        }\n\n        if (list != null && list.size() > 0) {\n            for (int i = 0; i < list.size(); i++) {\n                String line = list.get(i);\n                if (line != null && line.contains(springAppId)) {\n                    line = line.replace(springAppId,\n                            String.valueOf(appId));\n                    list.set(i, line);\n                }\n            }\n        }\n        return list;\n    }\n\n    public static String getRestAPI(int appType, long appId) {\n        String appTypePath = \"\";\n        switch (appType) {\n            case ConstUtils.CACHE_REDIS_SENTINEL: {\n                appTypePath = \"sentinel\";\n                break;\n            }\n            case ConstUtils.CACHE_REDIS_STANDALONE: {\n                appTypePath = \"standalone\";\n                break;\n            }\n            case ConstUtils.CACHE_TYPE_REDIS_CLUSTER: {\n                appTypePath = \"cluster\";\n                break;\n            }\n            default:\n                break;\n        }\n        return \"http://${domain}/cache/client/redis/\" + appTypePath + \"/\" + appId + \".json\";\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/EnvUtil.java",
    "content": "package com.sohu.cache.util;\n\nimport com.google.common.collect.Sets;\nimport org.springframework.core.env.Environment;\n\nimport java.util.Set;\n\n/**\n * Created by yijunzhang\n */\npublic class EnvUtil {\n\n    public static Set<String> getProfiles(Environment environment) {\n        return Sets.newHashSet(environment.getActiveProfiles());\n    }\n\n    public static boolean isOnline(Environment environment) {\n        return getProfiles(environment).contains(\"online\");\n    }\n\n    public static boolean isDev(Environment environment) {\n        Set<String> profiles = getProfiles(environment);\n        return profiles.contains(\"test\") || profiles.contains(\"test-sohu\") || profiles.contains(\"local\") || profiles.contains(\"local-sohu\");\n    }\n\n    public static boolean isLocal(Environment environment) {\n        Set<String> profiles = getProfiles(environment);\n        return profiles.contains(\"local\") || profiles.contains(\"local-sohu\");\n    }\n\n    public static boolean isTest(Environment environment) {\n        Set<String> profiles = getProfiles(environment);\n        return profiles.contains(\"test\") || profiles.contains(\"test-sohu\");\n    }\n\n    public static boolean isOpen(Environment environment) {\n        Set<String> profiles = getProfiles(environment);\n        return profiles.contains(\"open\") || profiles.contains(\"open-sohu\");\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/IdempotentConfirmer.java",
    "content": "package com.sohu.cache.util;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\n/**\r\n * 幂等操作器\r\n * Created by yijunzhang on 14-10-22.\r\n */\r\npublic abstract class IdempotentConfirmer {\r\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\r\n\r\n    private int retry = 3;\r\n\r\n    protected IdempotentConfirmer(int retry) {\r\n        this.retry = retry;\r\n    }\r\n\r\n    public IdempotentConfirmer() {\r\n    }\r\n\r\n    public abstract boolean execute();\r\n\r\n    public boolean run() {\r\n        while (retry-- > 0) {\r\n            try {\r\n                boolean isOk = execute();\r\n                if (isOk){\r\n                    return true;\r\n                }\r\n            } catch (Exception e) {\r\n                logger.error(e.getMessage(), e);\r\n                continue;\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/IntegerUtil.java",
    "content": "package com.sohu.cache.util;\r\n\r\n/**\r\n * Integer 工具类\r\n * \r\n * @author 银时：yinshi.nc / yinshi.nc@taobao.com\r\n * @Date Jan 13, 2012\r\n */\r\npublic class IntegerUtil {\r\n\r\n\t/**\r\n\t * 如果为0,则返回默认值\r\n\t * \r\n\t * @param originalInt\r\n\t * @param defaultInt\r\n\t *            默认Integer\r\n\t * @return\r\n\t */\r\n\tpublic static Integer defaultIfZero( Integer originalInt, Integer defaultInt ) {\r\n\t\tif ( 0 == originalInt ) {\r\n\t\t\treturn defaultInt;\r\n\t\t}\r\n\t\treturn originalInt;\r\n\t}\r\n\r\n\t/**\r\n\t * 如果为0,则返回默认值\r\n\t * \r\n\t * @param originalInt\r\n\t * @param defaultInt\r\n\t *            默认Integer\r\n\t * @return\r\n\t */\r\n\tpublic static Integer defaultIfError( String originalStr, Integer defaultInt ) {\r\n\r\n\t\ttry {\r\n\t\t\treturn Integer.parseInt( StringUtil.trimToEmpty( originalStr ) );\r\n\t\t} catch ( Exception e ) {\r\n\t\t\treturn defaultInt;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 如果是一个不合法的整型，那么返回一个默认值\r\n\t * \r\n\t * @param originalInt\r\n\t * @param defaultInt\r\n\t *            默认Integer\r\n\t * @return\r\n\t */\r\n\tpublic static Integer defaultIfError( Integer originalStr, Integer defaultInt ) {\r\n\r\n\t\ttry {\r\n\t\t\treturn originalStr;\r\n\t\t} catch ( Exception e ) {\r\n\t\t\treturn defaultInt;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 如果非正,则返回默认值<br>\r\n\t * @param originalInt\r\n\t * @param defaultInt\r\n\t *            默认Integer\r\n\t * @return originalInt if originalInt>0, return defaultInt if originalInt<=0\r\n\t */\r\n\tpublic static Integer defaultIfSmallerThan0( Integer originalInt, Integer defaultInt ) {\r\n\t\tif ( 0 >= originalInt ) {\r\n\t\t\treturn defaultInt;\r\n\t\t}\r\n\t\treturn originalInt;\r\n\t}\r\n\t\r\n\t/** 将String 转化成 Integer，如果小于等于0，将抛异常 */\r\n\tpublic static Integer exceptionIfSmallerThan0( String originalStr )throws Exception{\r\n\t\ttry {\r\n\t\t\tint num = Integer.parseInt( StringUtil.trimToEmpty( originalStr ) );\r\n\t\t\tif( num > 0  )\r\n\t\t\t\treturn num;\r\n\t\t\telse\r\n\t\t\t\tthrow new Exception();\r\n\t\t} catch ( Exception e ) {\r\n\t\t\tthrow new Exception( originalStr + \" is smaller than 0, or it is a  invalid parameter \" );\r\n\t\t}\r\n\t}\r\n\t\r\n\t\r\n\t\r\n\t/**\r\n\t * 判断是否大余0\r\n\t * @return false if num <=0  , true if num >0\r\n\t */\r\n\tpublic static boolean isBiggerThan0( int num ){\r\n\t\tif( 0>= num )\r\n\t\t\treturn false;\r\n\t\treturn true;\r\n\t}\r\n\r\n\t/**\r\n\t * Return maxInt if too big, else return original.\r\n\t * \r\n\t * @param originalInt\r\n\t * @param maxInt\r\n\t *            max int\r\n\t * @return\r\n\t */\r\n\tpublic static Integer maxIfTooBig( Integer originalInt, Integer maxInt ) {\r\n\t\tif ( originalInt >= maxInt ) {\r\n\t\t\toriginalInt = maxInt;\r\n\t\t}\r\n\t\treturn originalInt;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/JsonUtil.java",
    "content": "package com.sohu.cache.util;\n\nimport java.io.IOException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.ArrayNode;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\n\n/**\n * jackson转换工具\n * @author leifu\n * @Date 2016年3月23日\n * @Time 上午10:47:57\n */\npublic class JsonUtil {\n    private static final Logger logger = LoggerFactory.getLogger(JsonUtil.class);\n    // 采用jackson\n    private static ObjectMapper mapper = new ObjectMapper();\n\n    /**\n     * 将对象转换为json\n     * \n     * @param entity\n     * @return\n     */\n    public static String toJson(Object entity) {\n        if (entity == null) {\n            return null;\n        }\n        try {\n            return mapper.writeValueAsString(entity);\n        } catch (IOException e) {\n            logger.error(\"parse entity=\" + entity + \" to json error!\", e);\n        }\n        return null;\n    }\n\n    /**\n     * 从json解析出对象\n     * \n     * @param <T>\n     * @param content\n     * @param valueType\n     * @return\n     */\n    public static <T> T fromJson(String content, Class<T> valueType) {\n        if (content == null) {\n            return null;\n        }\n        try {\n            return mapper.readValue(content, valueType);\n        } catch (IOException e) {\n            logger.error(\"parse content=\" + content + \" error!\", e);\n        }\n        return null;\n    }\n\n    private static ObjectMapper getObjectMapper() {\n        return mapper;\n    }\n\n    public static ObjectNode createObjectNode() {\n        return getObjectMapper().createObjectNode();\n    }\n\n    public static ArrayNode createArrayNode() {\n        return getObjectMapper().createArrayNode();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/MD5Util.java",
    "content": "package com.sohu.cache.util;\n\nimport java.security.MessageDigest;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2022/1/26 12:05\n * @Description:\n */\npublic class MD5Util {\n    /**\n    * @Description:    MD5加码 生成32位md5码\n    * @Author:         caoru\n    * @CreateDate:     2018/8/9 14:21\n    */\n    public static String string2MD5(String inStr){\n        MessageDigest md5 = null;\n        try{\n            md5 = MessageDigest.getInstance(\"MD5\");\n        }catch (Exception e){\n            System.out.println(e.toString());\n            e.printStackTrace();\n            return \"\";\n        }\n        char[] charArray = inStr.toCharArray();\n        byte[] byteArray = new byte[charArray.length];\n\n        for (int i = 0; i < charArray.length; i++)\n            byteArray[i] = (byte) charArray[i];\n        byte[] md5Bytes = md5.digest(byteArray);\n        StringBuffer hexValue = new StringBuffer();\n        for (int i = 0; i < md5Bytes.length; i++){\n            int val = ((int) md5Bytes[i]) & 0xff;\n            if (val < 16)\n                hexValue.append(\"0\");\n            hexValue.append(Integer.toHexString(val));\n        }\n        return hexValue.toString();\n\n    }\n\n    /**\n    * @Description:    加密解密算法 执行一次加密，两次解密\n    * @Author:         caoru\n    * @CreateDate:     2018/8/9 14:21\n    */\n    public static String convertMD5(String inStr){\n\n        char[] a = inStr.toCharArray();\n        for (int i = 0; i < a.length; i++){\n            a[i] = (char) (a[i] ^ 't');\n        }\n        String s = new String(a);\n        return s;\n\n    }\n\n\n\n    public static void main(String args[]) {\n        String s = new String(\"10566\");\n        String encryptStr=convertMD5(s);\n        System.out.println(\"原始：\" + s);\n        System.out.println(\"MD5后：\" + string2MD5(s));\n        System.out.println(\"加密的：\" + encryptStr);\n        System.out.println(\"解密的：\" + convertMD5(encryptStr));\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/MapUtil.java",
    "content": "package com.sohu.cache.util;\n\nimport org.apache.commons.beanutils.BeanUtils;\n\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/12/16\n */\npublic class MapUtil {\n\n    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception {\n        if (map == null){\n            return null;\n        }\n        Object obj = beanClass.newInstance();\n        BeanUtils.populate(obj, map);\n        return obj;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/NMONFileFactory.java",
    "content": "package com.sohu.cache.util;\r\n\r\nimport com.sohu.cache.server.data.OS;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport java.io.File;\r\nimport java.security.CodeSource;\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n/**\r\n * nmon文件存储工厂\r\n */\r\npublic class NMONFileFactory {\r\n\tprivate static final Logger logger = LoggerFactory.getLogger(NMONFileFactory.class);\r\n\tpublic static final String NMON_PATH = \"/nmon\";\r\n\tpublic static final String NMON_DIR_PATH = \"nmon.dir\";\r\n\tpublic static final String FILE = \"file\";\r\n\t//nmon文件存储 key为OSType_ProcessorArchitecture_DistributionType\r\n\tprivate static final Map<String, File> nmonFileMap = new HashMap<String, File>();\r\n\t\r\n\tstatic {\r\n\t\tinit();\r\n\t}\r\n\t\r\n\t/**\r\n\t * 初始化nmon文件\r\n\t */\r\n\tprivate static void init() {\r\n\t\ttry {\r\n\t\t\tString path = System.getProperty(NMON_DIR_PATH);\r\n\t\t\tif(path == null) {\r\n\t\t\t\tString classpath = null;\r\n\t\t\t\ttry {\r\n\t\t\t\t\tCodeSource codeSource = NMONFileFactory.class.getProtectionDomain().getCodeSource();\r\n\t\t\t\t\tclasspath = codeSource.getLocation().getPath();\r\n\t\t        \tif(classpath.startsWith(FILE)) {\r\n\t\t        \t\t//like that: file:/opt/app/cachecloud/cachecloud-web-1.0-SNAPSHOT.war!/WEB-INF/classes!/\r\n\t\t        \t\tclasspath = classpath.substring(FILE.length()+1);\r\n\t\t        \t}\r\n\t\t        \tif(new File(classpath).isDirectory()) {\r\n\t\t        \t\tpath = classpath+\"../..\"+NMON_PATH;\r\n\t\t        \t} else {\r\n\t\t        \t\t//like that: /opt/app/cachecloud/cachecloud-web-1.0-SNAPSHOT.war!/WEB-INF/classes!/\r\n\t\t        \t\tString[] tmp = classpath.split(\"!/\", 2);\r\n\t\t        \t\tpath = tmp[0].substring(0, tmp[0].lastIndexOf(\"/\"))+NMON_PATH;\r\n\t\t        \t}\r\n\t\t        } catch (Exception e) {\r\n\t\t            logger.error(classpath, e);\r\n\t\t        }\r\n\t\t\t}\r\n\t\t\tif(path == null){\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t        File nmonDir = new File(path);\r\n\t\t\tif(!nmonDir.exists()) {\r\n\t\t\t\tlogger.error(\"{} path not exist\", nmonDir.getAbsolutePath());\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\t//获取操作系统目录\r\n\t\t\tFile[] osDirs = nmonDir.listFiles();\r\n\t\t\tif(osDirs == null) {\r\n\t\t\t\tlogger.error(\"{} not contains OS folders\", nmonDir.getAbsolutePath());\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tfor(File osDir : osDirs) {\r\n\t\t\t\t//获取处理器架构目录\r\n\t\t\t\tFile[] archFiles = osDir.listFiles();\r\n\t\t\t\tif(archFiles == null) {\r\n\t\t\t\t\tlogger.info(\"{} not contains architecture folders\", osDir.getName());\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tfor(File archDir : archFiles) {\r\n\t\t\t\t\t//获取nmon文件目录\r\n\t\t\t\t\tFile[] nmonFiles = archDir.listFiles();\r\n\t\t\t\t\tif(nmonFiles == null) {\r\n\t\t\t\t\t\tlogger.info(\"{} not contains nomon files\", archDir.getName());\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tfor(File nmonFile : nmonFiles) {\r\n\t\t\t\t\t\tnmonFileMap.put(osDir.getName() + \"_\" + archDir.getName() \r\n\t\t\t\t\t\t\t\t+ \"_\" + nmonFile.getName() , nmonFile);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tlogger.info(\"init {} {} nmon file size=\"+nmonFiles.length, \r\n\t\t\t\t\t\t\tosDir.getName(), archDir.getName());\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tlogger.info(\"init {} finished, os size={}\", nmonDir.getAbsolutePath(), osDirs.length);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"init nmon factory\", e);\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * 根据OS信息获取对应版本的NMON文件\r\n\t * @param os\r\n\t * @return File\r\n\t */\r\n\tpublic static File getNMONFile(OS os) {\r\n\t\tString key = os.getOsType().getValue() \r\n\t\t\t\t+ \"_\" + os.getProcessorArchitecture().getValue()\r\n\t\t\t\t+ \"_\" + os.getDistributionType().getNmonName()\r\n\t\t\t\t+ os.getDistributionVersion().getValue();\r\n\t\treturn nmonFileMap.get(key);\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/NamedArg.java",
    "content": "package com.sohu.cache.util;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.PARAMETER;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\n/**\n * Annotation that provides information about argument's name.\n *\n * @since JavaFX 8.0\n */\n@Retention(RUNTIME)\n@Target(PARAMETER)\npublic @interface NamedArg {\n    /**\n     * The name of the annotated argument.\n     * @return the name of the annotated argument\n     */\n    public String value();\n\n    /**\n     * The default value of the annotated argument.\n     * @return the default value of the annotated argument\n     */\n    public String defaultValue() default \"\";\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/NumberUtil.java",
    "content": "package com.sohu.cache.util;\n\n/**\n * 从commons-lang抄的\n * @author leifu\n */\npublic class NumberUtil {\n    \n    public static int toInt(String str, int defaultValue) {\n        if (str == null) {\n            return defaultValue;\n        }\n        try {\n            return Integer.parseInt(str);\n        } catch (NumberFormatException nfe) {\n            return defaultValue;\n        }\n    }\n\n    public static int toInt(String str) {\n        return toInt(str, 0);\n    }\n    \n    public static long toLong(String str, long defaultValue) {\n        if (str == null) {\n            return defaultValue;\n        }\n        try {\n            return Long.parseLong(str);\n        } catch (NumberFormatException nfe) {\n            return defaultValue;\n        }\n    }\n    \n    public static long toLong(String str) {\n        return toLong(str, 0L);\n    }\n    \n    public static double toDouble(final String str) {\n        return toDouble(str, 0.0d);\n    }\n    \n    public static double toDouble(final String str, final double defaultValue) {\n        if (str == null) {\n            return defaultValue;\n        }\n        try {\n            return Double.parseDouble(str);\n        } catch (final NumberFormatException nfe) {\n            return defaultValue;\n        }\n      }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/OSFactory.java",
    "content": "package com.sohu.cache.util;\r\n\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.sohu.cache.server.data.OS;\r\nimport com.sohu.cache.server.data.OSInfo;\r\nimport com.sohu.cache.server.data.OSInfo.DistributionType;\r\nimport com.sohu.cache.server.data.OSInfo.DistributionVersion;\r\nimport com.sohu.cache.server.data.OSInfo.OSType;\r\nimport com.sohu.cache.server.data.OSInfo.ProcessorArchitecture;\r\n\r\n/**\r\n * 根据操作系统原生信息解析出OS\r\n */\r\npublic class OSFactory {\r\n\tprivate static final Logger logger = LoggerFactory.getLogger(OSFactory.class);\r\n\t//获取发行版本号的主版本和次版本\r\n\tprivate static final Pattern VERSION_PATTERN = Pattern.compile(\"([1-9]+(\\\\.[0-9]+)?)\");\r\n\t\r\n\tpublic static OS getDefaultOS(OSInfo osInfo) {\r\n\t\tString uname = osInfo.getUname();\r\n\t\t//无法获取系统位数\r\n\t\tif(uname == null) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tuname = uname.toLowerCase();\r\n\t\tProcessorArchitecture defaultArch = ProcessorArchitecture.X86_64;\r\n\t\t//其次获取操作系统位数\r\n\t\tif(!uname.contains(defaultArch.getValue())) {\r\n\t\t\tdefaultArch = ProcessorArchitecture.X86;\r\n\t\t}\r\n\t\treturn new OS(OSType.LINUX, DistributionType.LINUX_OLD, \r\n\t\t\t\tDistributionVersion.DEFAULT, defaultArch);\r\n\t}\r\n\t\r\n\t/**\r\n\t * 采用uname -a信息和/etc/issue解析出目前能够支持的操作系统\r\n\t * @param osInfo\r\n\t * @return OS\r\n\t */\r\n\tpublic static OS getOS(OSInfo osInfo) {\r\n\t\tString uname = osInfo.getUname();\r\n\t\tString issue = osInfo.getIssue();\r\n\t\tOSType osType = OSType.LINUX;\r\n\t\tProcessorArchitecture defaultArch = ProcessorArchitecture.X86_64;\r\n\t\tDistributionType defaultDist = DistributionType.LINUX_OLD;\r\n\t\tDistributionVersion version = DistributionVersion.DEFAULT;\r\n\t\t\r\n\t\t//无法获取系统类型，位数 版本，采用默认\r\n\t\tif(uname == null || issue == null) {\r\n\t\t\tOS os = new OS(osType, defaultDist, version, defaultArch);\r\n\t\t\treturn os;\r\n\t\t}\r\n\t\t\r\n\t\tuname = uname.toLowerCase();\r\n\t\t//首先获取操作系统类型\r\n\t\tif(!uname.contains(OSType.LINUX.getValue())) {\r\n\t\t\tlogger.error(\"os={} is temporarily not supported\", uname);\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\t//其次获取操作系统位数\r\n\t\tif(!uname.contains(defaultArch.getValue())) {\r\n\t\t\tdefaultArch = ProcessorArchitecture.X86;\r\n\t\t}\r\n\t\t//再次解析操作系统发行版本\r\n\t\tissue = issue.toLowerCase();\r\n\t\t\r\n\t\tDistributionType findType = DistributionType.findByContains(issue);\r\n\t\t//没有找到匹配的版本，使用默认\r\n\t\tif(findType == null) {\r\n\t\t\tlogger.warn(\"dist cannot matched, {}\", issue);\r\n\t\t\tOS os = new OS(osType, defaultDist, version, defaultArch);\r\n\t\t\treturn os;\r\n\t\t}\r\n\t\t\r\n\t\t//最后解析版本号\r\n\t\tMatcher matcher = VERSION_PATTERN.matcher(issue);\r\n\t\t//没有版本好用默认的\r\n\t\tif(!matcher.find()) {\r\n\t\t\tlogger.warn(\"version not matched, {}\", issue);\r\n\t\t\tOS os = new OS(osType, defaultDist, version, defaultArch);\r\n\t\t\treturn os;\r\n\t\t}\r\n\t\tString ver = matcher.group();\r\n\t\tver = ver.replaceAll(\"\\\\.\", \"\");\r\n\t\tlogger.info(\"version matched, {} - {}\", ver, issue);\r\n\t\tDistributionVersion versionResult = findVersion(findType.getVersions(), ver);\r\n\t\t//没有具体的版本能匹配上\r\n\t\tif(versionResult == null) {\r\n\t\t\tlogger.info(\"version {} not found, {}\", ver, issue);\r\n\t\t\tOS os = new OS(osType, defaultDist, version, defaultArch);\r\n\t\t\treturn os;\r\n\t\t}\r\n\t\t\r\n\t\tOS os = new OS(osType, findType, versionResult, defaultArch);\r\n\t\tlogger.info(\"find OS={}\", os);\r\n\t\treturn os;\r\n\t}\r\n\t\r\n\tprivate static DistributionVersion findVersion(DistributionVersion[] versions, String target) {\r\n\t\tfor(DistributionVersion dv : versions) {\r\n\t\t\tif(dv.getValue().equals(target)){\r\n\t\t\t\treturn dv;\r\n\t\t\t} \r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/ObjectConvert.java",
    "content": "package com.sohu.cache.util;\r\n\r\nimport com.sohu.cache.entity.InstanceInfo;\r\n\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 对象转换工具类\r\n *\r\n * User: lingguo\r\n * Date: 14-5-29\r\n * Time: 下午6:17\r\n */\r\npublic class ObjectConvert {\r\n    private static Logger logger = LoggerFactory.getLogger(ObjectConvert.class);\r\n\r\n    /**\r\n     * 将ip和port连接起来\r\n     *\r\n     * @param ip\r\n     * @param port\r\n     * @return\r\n     */\r\n    public static String linkIpAndPort(String ip, int port) {\r\n        return ip + \":\" + port;\r\n    }\r\n\r\n    /**\r\n     * 将实例列表转化为ip1:port1 ip2:port2\r\n     *\r\n     * @param instanceList\r\n     * @return\r\n     */\r\n    public static String assembleInstance(List<InstanceInfo> instanceList) {\r\n        if (instanceList.isEmpty()) {\r\n            return null;\r\n        }\r\n        StringBuilder instanceBuilder = new StringBuilder();\r\n        for (int i = 0; i < instanceList.size(); i++) {\r\n            InstanceInfo instanceInfo = instanceList.get(i);\r\n            if (instanceInfo.isOffline()) {\r\n                continue;\r\n            }\r\n            if (i > 0) {\r\n                instanceBuilder.append(\" \");\r\n            }\r\n            instanceBuilder.append(instanceInfo.getIp()).append(\":\").append(instanceInfo.getPort());\r\n        }\r\n        return StringUtils.trim(instanceBuilder.toString());\r\n    }\r\n\r\n    /**\r\n     * 将百分比的比值转换为对应浮点数\r\n     *\r\n     * @param value         百分比表示\r\n     * @param defaultVal    默认值\r\n     * @return              转换后的浮点表示\r\n     */\r\n    public static double percentToDouble(String value, double defaultVal) {\r\n        double result = defaultVal;\r\n        if (value == null || value.isEmpty()) {\r\n            return result;\r\n        }\r\n        try {\r\n            result = Double.valueOf(value.substring(0, value.length() - 1));\r\n        } catch (NumberFormatException e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n\r\n        return result;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/Pair.java",
    "content": "/*\n * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.\n * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n */\n\npackage com.sohu.cache.util;\n\nimport java.io.Serializable;\n\n/**\n * <p>A convenience class to represent name-value pairs.</p>\n * @since JavaFX 2.0\n */\npublic class Pair<K,V> implements Serializable{\n\n   /**\n    * Key of this <code>Pair</code>.\n    */\n   private K key;\n\n   /**\n    * Gets the key for this pair.\n    * @return key for this pair\n    */\n   public K getKey() { return key; }\n\n   /**\n    * Value of this this <code>Pair</code>.\n    */\n   private V value;\n\n   /**\n    * Gets the value for this pair.\n    * @return value for this pair\n    */\n   public V getValue() { return value; }\n\n   /**\n    * Creates a new pair\n    * @param key The key for this pair\n    * @param value The value to use for this pair\n    */\n   public Pair(@NamedArg(\"key\") K key, @NamedArg(\"value\") V value) {\n       this.key = key;\n       this.value = value;\n   }\n\n   /**\n    * <p><code>String</code> representation of this\n    * <code>Pair</code>.</p>\n    *\n    * <p>The default name/value delimiter '=' is always used.</p>\n    *\n    *  @return <code>String</code> representation of this <code>Pair</code>\n    */\n   @Override\n   public String toString() {\n       return key + \"=\" + value;\n   }\n\n   /**\n    * <p>Generate a hash code for this <code>Pair</code>.</p>\n    *\n    * <p>The hash code is calculated using both the name and\n    * the value of the <code>Pair</code>.</p>\n    *\n    * @return hash code for this <code>Pair</code>\n    */\n   @Override\n   public int hashCode() {\n       // name's hashCode is multiplied by an arbitrary prime number (13)\n       // in order to make sure there is a difference in the hashCode between\n       // these two parameters:\n       //  name: a  value: aa\n       //  name: aa value: a\n       return key.hashCode() * 13 + (value == null ? 0 : value.hashCode());\n   }\n\n    /**\n     * <p>Test this <code>Pair</code> for equality with another\n     * <code>Object</code>.</p>\n     *\n     * <p>If the <code>Object</code> to be tested is not a\n     * <code>Pair</code> or is <code>null</code>, then this method\n     * returns <code>false</code>.</p>\n     *\n     * <p>Two <code>Pair</code>s are considered equal if and only if\n     * both the names and values are equal.</p>\n     *\n     * @param o the <code>Object</code> to test for\n     * equality with this <code>Pair</code>\n     * @return <code>true</code> if the given <code>Object</code> is\n     * equal to this <code>Pair</code> else <code>false</code>\n     */\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o instanceof Pair) {\n            Pair pair = (Pair) o;\n            if (key != null ? !key.equals(pair.key) : pair.key != null) return false;\n            if (value != null ? !value.equals(pair.value) : pair.value != null) return false;\n            return true;\n        }\n        return false;\n    }\n}\n\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/PandectUtil.java",
    "content": "package com.sohu.cache.util;\n\nimport org.apache.commons.collections.MapUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * <p>\n * Description: 总览统计\n * </p>\n * @author chenshi\n * @version 1.0\n * @date 2017/8/14\n * @param\n * @return\n */\npublic class PandectUtil {\n\n    private static Logger logger = LoggerFactory.getLogger(PandectUtil.class);\n\n    //最后一次从mysql获取map时间\n    public final static String KEY_LASTTIME = \"lastTime\";\n\n    private static Map<String,Object> PANDECT_MAP = new HashMap<String,Object>();\n\n    /**\n     * <p>\n     * Description:1小时自动失效，重新从数据库获取\n     * </p>\n     * @author chenshi\n     * @version 1.0\n     * @date 2017/8/14\n     * @param\n     * @return true:从sql获取 false：从map获取\n     */\n    public static Boolean getFromMysql(){\n        if(!MapUtils.isEmpty(PANDECT_MAP)){\n            Long lastTime = MapUtils.getLong(PANDECT_MAP, KEY_LASTTIME, 0l);\n            long expire = System.currentTimeMillis()-lastTime;\n            logger.info(\"expire = {}ms\",expire);\n            if(expire >= 2*60*1000){\n                return true;\n            }\n            return false;\n        }\n        return true;\n    }\n\n    // 清除map\n    public static void clearMap(){\n        PANDECT_MAP.clear();\n    }\n\n    public static Map<String, Object> getPandectMap() {\n        return PANDECT_MAP;\n    }\n\n    public static void setPandectMap(Map<String, Object> pandectMap) {\n        PANDECT_MAP = pandectMap;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/RedisConstUtils.java",
    "content": "package com.sohu.cache.util;\n\n/**\n * Created by chenshi\n */\npublic class RedisConstUtils {\n\n    /**\n     * Redis版本安装脚本路径\n     */\n    public static final String REDIS_SHELL_DIR = \"/opt/cachecloud/sh/\";\n\n    /**\n     * Redis安装包后缀\n     */\n    public static final String REDIS_INSTALL_PACKAGE_SUFFIX = \".tar.gz\";\n\n    /**\n     * Redis make包后缀\n     */\n    public static final String REDIS_INSTALL_MAKE_PACKAGE_SUFFIX = \"-make.tar.gz\";\n\n    /**\n     * Redis版本名称前缀\n     */\n    public static final String REDIS_VERSION_PREFIX = \"redis-\";\n\n    /**\n     * Redis单版本安装日志\n     */\n    public static final String REDIS_INSTALL_LOG = \"/opt/cachecloud/sh/install.log.%s\";\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/ScheduleUtil.java",
    "content": "package com.sohu.cache.util;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport java.text.ParseException;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Calendar;\r\nimport java.util.Date;\r\nimport java.util.Random;\r\n\r\n/**\r\n * 调度相关的工具类\r\n * <p/>\r\n * Created by yijunzhang on 14-6-10.\r\n */\r\npublic class ScheduleUtil {\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleUtil.class);\r\n\r\n    private static final String COLLECT_TIME_FORMAT = \"yyyyMMddHHmm\";\r\n    private static final String DATE_FORMAT = \"yyyyMMdd\";\r\n\r\n    /**\r\n     * cron表达式：每分钟，根据appId计算分钟的秒数\r\n     *\r\n     * @param appId\r\n     * @return\r\n     */\r\n    public static String getMinuteCronByAppId(long appId) {\r\n        String baseCron = (appId % 50) + \" 0/1 * ? * *\";\r\n        return baseCron;\r\n    }\r\n    \r\n    public static String getMachineStatsCron(long hostId) {\r\n        String baseCron = (hostId % 50) + \" 0/\" + ConstUtils.MACHINE_STATS_CRON_MINUTE + \" * ? * *\";\r\n        return baseCron;\r\n    }\r\n    \r\n    public static String getFiveMinuteCronByHostId(long hostId) {\r\n        String baseCron = (hostId % 50) + \" 0/5 * ? * *\";\r\n        return baseCron;\r\n    }\r\n    \r\n    public static String getRedisSlowLogCron(long appId) {\r\n        Random random = new Random();\r\n        String baseCron = random.nextInt(60) + \" 0/20 * ? * *\";\r\n        return baseCron;\r\n    }\r\n\r\n    /**\r\n     * cron表达式：每小时，根据hostId计算小时的分钟数\r\n     *\r\n     * @param hostId\r\n     * @return\r\n     */\r\n    public static String getHourCronByHostId(long hostId) {\r\n        String hourCron = \"0 %s 0/1 ? * *\";\r\n        Random random = new Random();\r\n        long minute = (hostId + random.nextInt(Integer.MAX_VALUE)) % 60;\r\n        return String.format(hourCron, minute);\r\n    }\r\n\r\n    /**\r\n     * 计算前一分钟的时间，并格式化\r\n     *\r\n     * @param collectTime 基准时间\r\n     * @return\r\n     */\r\n    public static long getLastCollectTime(long collectTime) {\r\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(COLLECT_TIME_FORMAT);\r\n        try {\r\n            Date date = simpleDateFormat.parse(String.valueOf(collectTime));\r\n            Calendar calendar = Calendar.getInstance();\r\n            calendar.setTime(date);\r\n            calendar.add(Calendar.MINUTE, -1);\r\n            Date lastDate = calendar.getTime();\r\n            return Long.parseLong(simpleDateFormat.format(lastDate));\r\n        } catch (ParseException e) {\r\n            LOGGER.error(e.getMessage(), e);\r\n            return 0L;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 格式化时间\r\n     *\r\n     * @param date\r\n     * @return\r\n     */\r\n    public static long getCollectTime(Date date) {\r\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(COLLECT_TIME_FORMAT);\r\n        return Long.parseLong(simpleDateFormat.format(date));\r\n    }\r\n\r\n    /**\r\n     * 返回某一天的起始时间，如：201406300000\r\n     *\r\n     * @param date  当前日期\r\n     * @param offset 针对当前日期的偏移\r\n     * @return 日期的long形式，如201406300000\r\n     */\r\n    public static long getBeginTimeOfDay(Date date, int offset) {\r\n        Calendar calendar = Calendar.getInstance();\r\n        calendar.setTime(date);\r\n        calendar.add(Calendar.DAY_OF_YEAR, offset);\r\n        date = calendar.getTime();\r\n        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);\r\n        return Long.parseLong(dateFormat.format(date) + \"0000\");\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/StringUtil.java",
    "content": "package com.sohu.cache.util;\r\n\r\nimport com.sohu.cache.constant.BaseConstant;\r\nimport com.sohu.cache.constant.EmptyObjectConstant;\r\n\r\nimport java.io.UnsupportedEncodingException;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\n\r\nimport static com.sohu.cache.constant.EmptyObjectConstant.EMPTY_STRING;\r\n\r\n/**\r\n * Description: String Utils\r\n *\r\n * @author nileader / nileader@gmail.com\r\n * @Date Feb 10, 2012\r\n */\r\npublic class StringUtil {\r\n\r\n    /**\r\n     * Check String is equals. targetStr compare with compareStrArray, and\r\n     * return true if equals one or more\r\n     *\r\n     * @param targetStr       寰呮瘮杈冪殑瀛楃涓�\r\n     * @param compareStrArray 瑕佹瘮杈冪殑涓�釜鎴栧涓瓧绗︿覆鏍囧噯\r\n     * @return\r\n     */\r\n    public static boolean containsIgnoreCase(final String originalStr, final CharSequence targetStr) {\r\n\r\n        if (null == originalStr) {\r\n            return false;\r\n        }\r\n\r\n        String originalStrCaps = originalStr.toUpperCase();\r\n        String targetStrCaps = targetStr.toString().toUpperCase();\r\n        return originalStrCaps.contains(targetStrCaps);\r\n    }\r\n\r\n\r\n    /**\r\n     * Description: Remove {_, -, @, $, #, /, &} in string and make letter after\r\n     * this uppercase.<br>\r\n     * e.g. ni_lea-der@gmail./com -> niLeaDerGmail.Com\r\n     *\r\n     * @param @param inputString\r\n     * @param @param firstCharacterUppercase The first letter is need uppercase.\r\n     * @return String\r\n     * @throws\r\n     */\r\n    public static String convertToCamelCaseString(String inputString, boolean firstCharacterUppercase) {\r\n        if (null == inputString) {\r\n            return null;\r\n        }\r\n        StringBuilder sb = new StringBuilder();\r\n\r\n        boolean nextUpperCase = false;\r\n        for (int i = 0; i < inputString.length(); i++) {\r\n            char c = inputString.charAt(i);\r\n\r\n            switch (c) {\r\n                case '_':\r\n                case '-':\r\n                case '@':\r\n                case '$':\r\n                case '#':\r\n                case ' ':\r\n                case '/':\r\n                case '&':\r\n                    if (sb.length() > 0) {\r\n                        nextUpperCase = true;\r\n                    }\r\n                    break;\r\n\r\n                default:\r\n                    if (nextUpperCase) {\r\n                        sb.append(Character.toUpperCase(c));\r\n                        nextUpperCase = false;\r\n                    } else {\r\n                        sb.append(c);\r\n                    }\r\n                    break;\r\n            }\r\n        }\r\n\r\n        if (firstCharacterUppercase) {\r\n            sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));\r\n        } else {\r\n            sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));\r\n        }\r\n\r\n        return sb.toString();\r\n    }\r\n\r\n    /**\r\n     * Return Default if originalStr is empty.\r\n     *\r\n     * @param originalStr 寰呯‘璁ゅ�\r\n     * @param defaultStr  榛樿鍊�\r\n     * @return 濡傛灉originalStr涓虹┖锛岄偅涔堝氨杩斿洖defaultStr\r\n     */\r\n    public static String defaultIfBlank(String originalStr, String defaultStr) {\r\n        if (StringUtil.isBlank(originalStr)) {\r\n            return defaultStr;\r\n        }\r\n        return originalStr;\r\n    }\r\n\r\n    /**\r\n     * Check String is equals Ignore Case. targetStr compare with\r\n     * compareStrArray, and return true if equals all\r\n     *\r\n     * @param targetStr       寰呮瘮杈冪殑瀛楃涓�\r\n     * @param compareStrArray 瑕佹瘮杈冪殑涓�釜鎴栧涓瓧绗︿覆鏍囧噯\r\n     * @return true if targetStr same with every string in compareStrArray\r\n     */\r\n    public static boolean equalsIgnoreCaseAll(String targetStr, String... compareStrArray) {\r\n\r\n        if (StringUtil.isBlank(targetStr) || null == compareStrArray || 0 == compareStrArray.length) {\r\n            return false;\r\n        }\r\n        for (int i = 0; i < compareStrArray.length; i++) {\r\n            if (!targetStr.equalsIgnoreCase(compareStrArray[i])) {\r\n                return false;\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n\r\n    /**\r\n     * Check String is equals. targetStr compare with compareStrArray, and\r\n     * return true if equals one or more\r\n     *\r\n     * @param targetStr       寰呮瘮杈冪殑瀛楃涓�\r\n     * @param compareStrArray 瑕佹瘮杈冪殑涓�釜鎴栧涓瓧绗︿覆鏍囧噯\r\n     * @return true if targetStr same with string in compareStrArray one at\r\n     * least\r\n     */\r\n    public static boolean equalsIgnoreCaseOne(String targetStr, String... compareStrArray) {\r\n\r\n        if (StringUtil.isBlank(targetStr) || null == compareStrArray || 0 == compareStrArray.length) {\r\n            return false;\r\n        }\r\n        for (int i = 0; i < compareStrArray.length; i++) {\r\n            if (targetStr.equalsIgnoreCase(compareStrArray[i])) {\r\n                return true;\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    /**\r\n     * 閫氳繃姝ｅ垯琛ㄨ揪鏂瑰紡锛屾壘鍑轰竴涓瓧绗︿覆涓墍鏈夋寚瀹氱殑瀛愪覆\r\n     *\r\n     * @param @param originalStr 瀛楃涓�\r\n     * @param @param regex 寰呮煡鎵惧瓙涓茬殑姝ｅ垯琛ㄨ揪寮�\r\n     * @return List<String> 瀛愪覆闆嗗悎\r\n     * <p>\r\n     * <pre>\r\n     *  瀵�/1.1.1.1:sid=0x2337c7074dofj02e,37775[1](queued=0,recved=6,sent=7,sid=0x2337c7074f1102e,sdlfjle,dsfe鐨勭粨鏋滄槸锛�\r\n     * [sid=0x2337c7074dofj02e, , sid=0x2337c7074f1102e, ]\r\n     * </pre>\r\n     */\r\n    public static List<String> findAllByRegex(String originalStr, String regex) {\r\n\r\n        if (StringUtil.isBlank(originalStr) || StringUtil.isBlank(regex))\r\n            return null;\r\n\r\n        List<String> targetStrList = new ArrayList<String>();\r\n        final Pattern patternOfTargetStr = Pattern.compile(regex, Pattern.CANON_EQ);\r\n        final Matcher matcherOfTargetStr = patternOfTargetStr.matcher(originalStr);\r\n        /** 寮�瑙ｆ瀽 */\r\n        while (matcherOfTargetStr.find()) {\r\n            targetStrList.add(StringUtil.trimToEmpty(matcherOfTargetStr.group()));\r\n        }\r\n        return targetStrList;\r\n    }\r\n\r\n    /**\r\n     * 閫氳繃姝ｅ垯琛ㄨ揪鏂瑰紡锛屾壘鍑轰竴涓瓧绗︿覆涓涓�釜鎸囧畾鐨勫瓙涓�\r\n     *\r\n     * @param @param originalStr 瀛楃涓�\r\n     * @param @param regex 寰呮煡鎵惧瓙涓茬殑姝ｅ垯琛ㄨ揪寮�\r\n     * @return List<String> 瀛愪覆闆嗗悎\r\n     * <p>\r\n     * <pre>\r\n     *  瀵�/1.1.1.1:sid=0x2337c7074dofj02e,37775[1](queued=0,recved=6,sent=7,sid=0x2337c7074f1102e,sdlfjle,dsfe鐨勭粨鏋滄槸锛�\r\n     * sid=0x2337c7074dofj02e,\r\n     * </pre>\r\n     */\r\n    public static String findFirstByRegex(String originalStr, String regex) {\r\n\r\n        if (StringUtil.isBlank(originalStr) || StringUtil.isBlank(regex))\r\n            return EMPTY_STRING;\r\n\r\n        final Pattern patternOfTargetStr = Pattern.compile(regex, Pattern.CANON_EQ);\r\n        final Matcher matcherOfTargetStr = patternOfTargetStr.matcher(originalStr);\r\n        /** 寮�瑙ｆ瀽 */\r\n        if (matcherOfTargetStr.find()) {\r\n            return StringUtil.trimToEmpty(matcherOfTargetStr.group());\r\n        }\r\n        return EMPTY_STRING;\r\n    }\r\n\r\n    /**\r\n     * 鐢熸垚绌虹櫧琛�\r\n     *\r\n     * @param lines 琛屾暟\r\n     */\r\n    public static String generateLineBlank(int lines) {\r\n        StringBuilder sb = new StringBuilder();\r\n\r\n        for (int i = 0; i < lines; i++) {\r\n            sb.append(\"\\n\");\r\n        }\r\n\r\n        return sb.toString();\r\n    }\r\n\r\n    /**\r\n     * make first letter lower case for str\r\n     *\r\n     * @return Same letter, but the first letter is lower case.\r\n     */\r\n    public static String makeFirstLetterLowerCase(String str) {\r\n        String firstLetter = str.substring(0, 1);\r\n        return firstLetter.toLowerCase() + str.substring(1, str.length());\r\n    }\r\n\r\n    /***\r\n     * check if orginalStr is null or empty. <br>\r\n     * If have more than one originalStr, use isBlank(String...\r\n     * originalStrArray)\r\n     *\r\n     * @param originalStr\r\n     *            寰呯‘璁ゅ�\r\n     * @return true or false;\r\n     */\r\n    public static boolean isBlank(String originalStr) {\r\n        if (null == originalStr) {\r\n            return true;\r\n        }\r\n        if (originalStr.contains(BaseConstant.WORD_SEPARATOR)) {\r\n            return false;\r\n        }\r\n        return trimToEmpty(originalStr).isEmpty();\r\n    }\r\n\r\n    /***\r\n     * check if orginalStr is null or empty\r\n     *\r\n     * @param String\r\n     *            ... originalStrArray\r\n     * @return true if have one blank at least.\r\n     */\r\n    public static boolean isBlank(String... originalStrArray) {\r\n\r\n        if (null == originalStrArray || 0 == originalStrArray.length)\r\n            return true;\r\n        for (int i = 0; i < originalStrArray.length; i++) {\r\n            if (isBlank(originalStrArray[i]))\r\n                return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    /**\r\n     * check the originalStr is contain the whitespace\r\n     *\r\n     * @param originalStr\r\n     * @return true if contain whitespace\r\n     */\r\n    public static boolean isContainWhitespace(String originalStr) {\r\n\r\n        if (StringUtil.isBlank(originalStr)) {\r\n            return true;\r\n        }\r\n        int strLen = originalStr.length();\r\n        for (int i = 0; i < strLen; i++) {\r\n            char ch = originalStr.charAt(i);\r\n            if (Character.isWhitespace(ch)) {\r\n                return true;\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    /**\r\n     * 字符串连接，使用指定分隔符\r\n     *\r\n     * @param subStr\r\n     * @return\r\n     */\r\n    public static String join(String... subStrs) {\r\n\r\n        if (null == subStrs || 0 == subStrs.length) {\r\n            return EMPTY_STRING;\r\n        }\r\n        StringBuilder sb = new StringBuilder();\r\n        for (String subStr : subStrs) {\r\n            sb.append(subStr).append(BaseConstant.WORD_SEPARATOR);\r\n        }\r\n        String sbStr = sb.toString();\r\n        if (sbStr.endsWith(BaseConstant.WORD_SEPARATOR)) {\r\n            sbStr = StringUtil.replaceLast(sbStr, BaseConstant.WORD_SEPARATOR, \"\");\r\n        }\r\n        return sbStr;\r\n    }\r\n\r\n    /**\r\n     * Description: Replaces last substring of this string that matches the\r\n     * given regular expression with the given replacement.<br>\r\n     * Do not worry about null pointer\r\n     *\r\n     * @param @param regex\r\n     * @param @param replacement\r\n     * @return String\r\n     * @throws\r\n     */\r\n    public static String replaceAll(String originalStr, String replacement, String regex) {\r\n        return StringUtil.trimToEmpty(originalStr).replaceAll(regex, replacement);\r\n    }\r\n\r\n    public static String replaceAll(String originalStr, String replacement, String... regexArray) {\r\n\r\n        if (0 == regexArray.length)\r\n            return originalStr;\r\n\r\n        for (String regex : regexArray) {\r\n            originalStr = StringUtil.replaceAll(originalStr, replacement, regex);\r\n        }\r\n\r\n        return originalStr;\r\n    }\r\n\r\n    /**\r\n     * Description: Replaces last substring of this string that matches the\r\n     * given regular expression with the given replacement.\r\n     *\r\n     * @param @param  regex\r\n     * @param @param  replacement\r\n     * @param @return\r\n     * @return String\r\n     * @throws\r\n     */\r\n    public static String replaceLast(String originalStr, String regex, String replacement) {\r\n\r\n        if (StringUtil.isBlank(originalStr))\r\n            return EMPTY_STRING;\r\n\r\n        int index = originalStr.lastIndexOf(regex);\r\n        if (-1 == index)\r\n            return originalStr;\r\n\r\n        // 鍏堝瓨鍌ㄨ繖涓猧ndex涔嬪墠鐨勬墍鏈塻tr\r\n        String temp = originalStr.substring(0, index);\r\n        String temp2 = originalStr.substring(index, originalStr.length());\r\n\r\n        temp2 = temp2.replaceFirst(regex, replacement);\r\n\r\n        originalStr = temp + temp2;\r\n\r\n        return originalStr;\r\n    }\r\n\r\n\r\n    /**\r\n     * Description: Replaces all {n} placeholder use params\r\n     *\r\n     * @param originalStr       a string such as :\r\n     *                          \"select * from table where id={0}, name={1}, gender={3}\"\r\n     * @param replacementParams real params: 1,yinshi.nc,male\r\n     * @note n start with 0\r\n     */\r\n    public static String replaceSequenced(String originalStr, Object... replacementParams) {\r\n\r\n        if (StringUtil.isBlank(originalStr))\r\n            return EMPTY_STRING;\r\n        if (null == replacementParams || 0 == replacementParams.length)\r\n            return originalStr;\r\n\r\n        for (int i = 0; i < replacementParams.length; i++) {\r\n            String elementOfParams = replacementParams[i] + EmptyObjectConstant.EMPTY_STRING;\r\n            if (StringUtil.trimToEmpty(elementOfParams).equalsIgnoreCase(\"null\"))\r\n                elementOfParams = EmptyObjectConstant.EMPTY_STRING;\r\n            originalStr = originalStr.replace(\"{\" + i + \"}\", StringUtil.trimToEmpty(elementOfParams));\r\n        }\r\n\r\n        return originalStr;\r\n    }\r\n\r\n    /**\r\n     * 璁剧疆鍓嶇紑锛屽鏋滆繖涓瓧绗︿覆宸茬粡鏄繖涓墠缂�簡锛岄偅涔堝氨涓嶄綔浠讳綍鎿嶄綔銆� TODO none test\r\n     */\r\n    public static String setPrefix(String originalStr, String prefix) {\r\n        originalStr = StringUtil.trimToEmpty(originalStr);\r\n        prefix = StringUtil.trimToEmpty(prefix);\r\n        if (!originalStr.startsWith(prefix)) {\r\n            originalStr = prefix + originalStr;\r\n        }\r\n        return originalStr;\r\n    }\r\n\r\n    /**\r\n     * /**\r\n     * 鍒ゆ柇瀛楃涓叉槸鍚﹁秴杩囨寚瀹氶暱搴︼紝濡備綍瓒呰繃锛屾坊鍔犳寚瀹氬悗缂�\r\n     *\r\n     * @param originalStr \"閾舵椂鐨�\r\n     * @param maxLength   2\r\n     * @param suffix      ...\r\n     * @return \"閾舵椂...\"\r\n     */\r\n    public static String subStringIfTooLong(String originalStr, int maxLength, String suffix) {\r\n        if (StringUtil.isBlank(originalStr))\r\n            return EmptyObjectConstant.EMPTY_STRING;\r\n        if (maxLength < 0)\r\n            maxLength = 0;\r\n        if (originalStr.length() > maxLength)\r\n            return originalStr.substring(0, maxLength) + StringUtil.trimToEmpty(suffix);\r\n        return originalStr;\r\n    }\r\n\r\n    /**\r\n     * Returns a copy of the string, with leading and trailing whitespace\r\n     * omitted. Don't worry the NullPointerException. Will never return Null.\r\n     *\r\n     * @param originalStr\r\n     * @return \"\" or String without empty str.\r\n     */\r\n    public static String trimToEmpty(String originalStr) {\r\n        if (null == originalStr || originalStr.isEmpty())\r\n            return EMPTY_STRING;\r\n        if (originalStr.equals(BaseConstant.WORD_SEPARATOR))\r\n            return originalStr;\r\n        return originalStr.trim();\r\n    }\r\n\r\n    /**\r\n     * URL编码\r\n     *\r\n     * @param s   String to be translated.\r\n     * @param enc The name of a supported character encoding.\r\n     * @return\r\n     */\r\n    public static String urlEncode(String s, String enc) {\r\n        if (StringUtil.isBlank(s))\r\n            return StringUtil.trimToEmpty(s);\r\n        try {\r\n            return java.net.URLEncoder.encode(trimToEmpty(s), enc);\r\n        } catch (UnsupportedEncodingException e) {\r\n            return s;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * URL编码,使用UTF-8编码\r\n     *\r\n     * @param s   String to be translated.\r\n     * @param enc The name of a supported character encoding.\r\n     * @return\r\n     */\r\n    public static String urlEncode(String s) {\r\n        if (StringUtil.isBlank(s))\r\n            return StringUtil.trimToEmpty(s);\r\n        return urlEncode(trimToEmpty(s), \"UTF-8\");\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/util/TypeUtil.java",
    "content": "package com.sohu.cache.util;\r\n\r\n/**\r\n * Created by yijunzhang on 14-9-26.\r\n */\r\npublic class TypeUtil {\r\n\r\n    public static boolean isRedisType(int type) {\r\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL\r\n                || type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER\r\n                || type == ConstUtils.CACHE_REDIS_STANDALONE\r\n                || type == ConstUtils.CACHE_REDIS_TWEMPROXY\r\n                || type == ConstUtils.CACHE_PIKA_TWEMPROXY\r\n                || type == ConstUtils.CACHE_PIKA_SENTINEL) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean isRedisCluster(int type) {\r\n        if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean isRedisSentinel(int type) {\r\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean isRedisStandalone(int type) {\r\n        if (type == ConstUtils.CACHE_REDIS_STANDALONE) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean isRedisTwemproxy(int type) {\r\n        if (type == ConstUtils.CACHE_REDIS_TWEMPROXY) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean isPikaSentinel(int type) {\r\n        if (type == ConstUtils.CACHE_PIKA_SENTINEL) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean isPikaTwemproxy(int type) {\r\n        if (type == ConstUtils.CACHE_PIKA_TWEMPROXY) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean isRedisDataType(int type) {\r\n        if (type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER\r\n                || type == ConstUtils.CACHE_REDIS_STANDALONE) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/chart/key/ChartKeysUtil.java",
    "content": "package com.sohu.cache.web.chart.key;\n\n/**\n * Created by hym on 14-7-27.\n */\npublic class ChartKeysUtil {\n    public enum ChartKey {\n        RENDER_TO(\"renderTo\"), TYPE(\"type\");\n        private String key;\n\n        ChartKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n    public enum TitleKey {\n        TEXT(\"text\");\n        private String key;\n\n        TitleKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n    public enum SubTitleKey {\n        TEXT(\"text\");\n        private String key;\n\n        SubTitleKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n    public enum XAxisKey {\n        CATEGORIES(\"categories\"),\n        LABELS(\"labels\"),LABELS_STEP(\"step\"),LABELS_ROTATION(\"rotation\"), LABELS_Y(\"y\"),\n        MAX_STAGGER_LINES(\"maxStaggerLines\");\n        private String key;\n\n        XAxisKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n    public enum YAxisKey {\n        TITLE(\"title\"), TITLE_TEXT(\"text\"),\n        PLOTLINES(\"plotLines\"), PLOTLINES_VALUE(\"value\"), PLOTLINES_WIDTH(\"width\"), PLOTLINES_COLOR(\"color\");\n        private String key;\n\n        YAxisKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n    public enum TooltipKey {\n        VALUESUFFIX(\"valueSuffix\"),CROSSHAIRS(\"crosshairs\"),SHARED(\"shared\");\n        private String key;\n\n        TooltipKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n    public enum LegendKey {\n        LAYOUT(\"layout\"), ALIGN(\"align\"), VERTICALALIGN(\"verticalAlign\"), BORDERWIDTH(\"borderWidth\");\n        private String key;\n\n        LegendKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n    public enum SeriesKey {\n        DATA(\"data\"), NAME(\"name\");\n        private String key;\n\n        SeriesKey(String key) {\n            this.key = key;\n        }\n\n        public String getKey() {\n            return key;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/chart/model/AreaChartEntity.java",
    "content": "package com.sohu.cache.web.chart.model;\r\n\r\nimport com.sohu.cache.web.chart.key.ChartKeysUtil;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.LinkedHashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\npublic class AreaChartEntity extends ChartEntity {\r\n    public AreaChartEntity() {\r\n        super();\r\n        putChartType();\r\n        setTooltipShared(true);\r\n    }\r\n\r\n    @Override\r\n    protected void putChartType() {\r\n        this.getChart().put(ChartKeysUtil.ChartKey.TYPE.getKey(), \"area\");\r\n    }\r\n\r\n    public void setXAxisCategories(List<Object> xAxisCategories) {\r\n        setXAxisCategories(xAxisCategories, 14);\r\n    }\r\n\r\n    public void setXAxisCategories(List<Object> xAxisCategories, int totalLabels) {\r\n        setXAxisCategories(xAxisCategories, totalLabels, 0, -5);\r\n    }\r\n\r\n    public void setXAxisCategories(List<Object> xAxisCategories, int totalLabels, int rotation, int y) {\r\n        putXAxis(ChartKeysUtil.XAxisKey.CATEGORIES.getKey(), xAxisCategories);\r\n        if (xAxisCategories.size() >= totalLabels) {\r\n            Map<String, Object> m = null;\r\n            if (this.getxAxis().containsKey(ChartKeysUtil.XAxisKey.LABELS.getKey())) {\r\n                m = (Map<String, Object>) this.getxAxis().get(ChartKeysUtil.XAxisKey.LABELS.getKey());\r\n            }\r\n            if (m == null) {\r\n                m = new LinkedHashMap<String, Object>();\r\n            }\r\n            m.put(ChartKeysUtil.XAxisKey.LABELS_STEP.getKey(), xAxisCategories.size() / totalLabels + 1);\r\n            m.put(ChartKeysUtil.XAxisKey.LABELS_ROTATION.getKey(), rotation);\r\n            m.put(ChartKeysUtil.XAxisKey.LABELS_Y.getKey(), y);\r\n            m.put(ChartKeysUtil.XAxisKey.MAX_STAGGER_LINES.getKey(), 1);\r\n            putXAxis(ChartKeysUtil.XAxisKey.LABELS.getKey(), m);\r\n        }\r\n    }\r\n\r\n    public void setYAxisTitle(String title) {\r\n        if (this.getyAxis().containsKey(ChartKeysUtil.YAxisKey.TITLE.getKey())) {\r\n            ((Map<String, Object>) this.getyAxis().get(ChartKeysUtil.YAxisKey.TITLE.getKey())).put(ChartKeysUtil.YAxisKey.TITLE_TEXT.getKey(), title);\r\n        } else {\r\n            Map<String, Object> map = new HashMap<String, Object>();\r\n            map.put(ChartKeysUtil.YAxisKey.TITLE_TEXT.getKey(), title);\r\n            putYAxis(ChartKeysUtil.YAxisKey.TITLE.getKey(), map);\r\n        }\r\n    }\r\n\r\n    public void setTooltipCrosshairs(boolean crosshairs) {\r\n        putTooltip(ChartKeysUtil.TooltipKey.CROSSHAIRS.getKey(), crosshairs);\r\n    }\r\n\r\n    public void setTooltipShared(boolean shared) {\r\n        putTooltip(ChartKeysUtil.TooltipKey.SHARED.getKey(), shared);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/chart/model/ChartEntity.java",
    "content": "package com.sohu.cache.web.chart.model;\r\n\r\nimport com.sohu.cache.web.chart.key.ChartKeysUtil;\r\n\r\nimport java.util.LinkedHashMap;\r\nimport java.util.LinkedList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\npublic abstract class ChartEntity {\r\n    private Map<String, Object> chart;\r\n    private Map<String, Object> title;\r\n    private Map<String, Object> subtitle;\r\n    private Map<String, Object> xAxis;\r\n    private Map<String, Object> yAxis;\r\n    private Map<String, Object> tooltip;\r\n    private Map<String, Object> legend;\r\n    private List<Map<String, Object>> series;\r\n\r\n    public ChartEntity() {\r\n        // 初始化chart\r\n        Map<String, Object> chart = new LinkedHashMap<String, Object>();\r\n        chart.put(ChartKeysUtil.ChartKey.RENDER_TO.getKey(), \"container\");\r\n        this.setChart(chart);\r\n        // 初始化title\r\n        Map<String, Object> title = new LinkedHashMap<String, Object>();\r\n        title.put(ChartKeysUtil.TitleKey.TEXT.getKey(), \"请设置图表title\");\r\n        this.setTitle(title);\r\n        // 初始化subtitle\r\n        Map<String, Object> subtitle = new LinkedHashMap<String, Object>();\r\n        this.setSubtitle(subtitle);\r\n        // 初始化xAxis\r\n        Map<String, Object> xAxis = new LinkedHashMap<String, Object>();\r\n        this.setxAxis(xAxis);\r\n        // 初始化yAxis\r\n        Map<String, Object> yAxis = new LinkedHashMap<String, Object>();\r\n        this.setyAxis(yAxis);\r\n        // 初始化tooltip\r\n        Map<String, Object> tooltip = new LinkedHashMap<String, Object>();\r\n        this.setTooltip(tooltip);\r\n        // 初始化legend\r\n        Map<String, Object> legend = new LinkedHashMap<String, Object>();\r\n        this.setLegend(legend);\r\n        //初始化series\r\n        List<Map<String, Object>> series = new LinkedList<Map<String, Object>>();\r\n        this.setSeries(series);\r\n\r\n    }\r\n\r\n    /**\r\n     * 所有曲线类型必须设置曲线类型\r\n     */\r\n    protected abstract void putChartType();\r\n\r\n    /**\r\n     * 指定容器，即页面div的id\r\n     * 默认为container，用户可以覆盖\r\n     *\r\n     * @param container 页面div的id\r\n     */\r\n    public void renderTo(String container) {\r\n        this.putChart(ChartKeysUtil.ChartKey.RENDER_TO.getKey(), container);\r\n    }\r\n\r\n    /**\r\n     * 设置chart属性\r\n     *\r\n     * @param key\r\n     * @param value\r\n     */\r\n    public void putChart(String key, Object value) {\r\n        this.getChart().put(key, value);\r\n    }\r\n\r\n    /**\r\n     * 设置title属性\r\n     *\r\n     * @param key\r\n     * @param value\r\n     */\r\n    public void putTitle(String key, Object value) {\r\n        this.getTitle().put(key, value);\r\n    }\r\n\r\n    /**\r\n     * 设置subTitle属性\r\n     *\r\n     * @param key\r\n     * @param value\r\n     */\r\n    public void putSubTitle(String key, Object value) {\r\n        this.getSubtitle().put(key, value);\r\n    }\r\n\r\n    /**\r\n     * @param key\r\n     * @param value\r\n     */\r\n    public void putXAxis(String key, Object value) {\r\n        this.getxAxis().put(key, value);\r\n    }\r\n\r\n    /**\r\n     * @param key\r\n     * @param value\r\n     */\r\n    public void putYAxis(String key, Object value) {\r\n        this.getyAxis().put(key, value);\r\n    }\r\n\r\n    /**\r\n     * @param key\r\n     * @param value\r\n     */\r\n    public void putTooltip(String key, Object value) {\r\n        this.getTooltip().put(key, value);\r\n    }\r\n\r\n    /**\r\n     * @param key\r\n     * @param value\r\n     */\r\n    public void putLegend(String key, Object value) {\r\n        this.getLegend().put(key, value);\r\n    }\r\n\r\n    /**\r\n     * @param series\r\n     */\r\n    public void putSeries(Map<String, Object> series) {\r\n        this.getSeries().add(series);\r\n    }\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n    // 以下是get set 方法\r\n    public Map<String, Object> getChart() {\r\n        return chart;\r\n    }\r\n\r\n    private void setChart(Map<String, Object> chart) {\r\n        this.chart = chart;\r\n    }\r\n\r\n    public Map<String, Object> getTitle() {\r\n        return title;\r\n    }\r\n\r\n    private void setTitle(Map<String, Object> title) {\r\n        this.title = title;\r\n    }\r\n\r\n    public Map<String, Object> getSubtitle() {\r\n        return subtitle;\r\n    }\r\n\r\n    private void setSubtitle(Map<String, Object> subtitle) {\r\n        this.subtitle = subtitle;\r\n    }\r\n\r\n    public Map<String, Object> getxAxis() {\r\n        return xAxis;\r\n    }\r\n\r\n    private void setxAxis(Map<String, Object> xAxis) {\r\n        this.xAxis = xAxis;\r\n    }\r\n\r\n    public Map<String, Object> getyAxis() {\r\n        return yAxis;\r\n    }\r\n\r\n    private void setyAxis(Map<String, Object> yAxis) {\r\n        this.yAxis = yAxis;\r\n    }\r\n\r\n    public Map<String, Object> getTooltip() {\r\n        return tooltip;\r\n    }\r\n\r\n    private void setTooltip(Map<String, Object> tooltip) {\r\n        this.tooltip = tooltip;\r\n    }\r\n\r\n    public Map<String, Object> getLegend() {\r\n        return legend;\r\n    }\r\n\r\n    private void setLegend(Map<String, Object> legend) {\r\n        this.legend = legend;\r\n    }\r\n\r\n    public List<Map<String, Object>> getSeries() {\r\n        return series;\r\n    }\r\n\r\n    private void setSeries(List<Map<String, Object>> series) {\r\n        this.series = series;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/chart/model/HighchartDoublePoint.java",
    "content": "package com.sohu.cache.web.chart.model;\n\nimport com.sohu.cache.entity.AppDailyData;\nimport com.sohu.cache.entity.AppStats;\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang.time.DateUtils;\n\nimport java.text.DecimalFormat;\nimport java.text.ParseException;\nimport java.util.Date;\n\n/**\n * highchart最简单的点 double y\n *\n * @author leifu\n * @Date 2016年8月1日\n * @Time 下午12:59:29\n */\n@Slf4j\npublic class HighchartDoublePoint {\n    /**\n     * 时间戳\n     */\n    private Long x;\n\n    /**\n     * 用于表示y轴数量\n     */\n    private Double y;\n\n    /**\n     * 日期\n     */\n    private String date;\n\n    public HighchartDoublePoint() {\n\n    }\n\n    public HighchartDoublePoint(Long x, Double y, String date) {\n        this.x = x;\n        this.y = y;\n        this.date = date;\n    }\n\n\n    public Long getX() {\n        return x;\n    }\n\n    public void setX(Long x) {\n        this.x = x;\n    }\n\n    public Double getY() {\n        return y;\n    }\n\n    public void setY(Double y) {\n        this.y = y;\n    }\n\n    public String getDate() {\n        return date;\n    }\n\n    public void setDate(String date) {\n        this.date = date;\n    }\n\n    public static HighchartDoublePoint getFromAppStats(AppStats appStat, String statName, Date currentDate, int diffDays) throws ParseException {\n        Date collectDate = getDateTime(appStat.getCollectTime());\n        if (!DateUtils.isSameDay(currentDate, collectDate)) {\n            return null;\n        }\n        //显示用的时间\n        String date = null;\n        try {\n            date = DateUtil.formatDate(collectDate, \"yyyy-MM-dd HH:mm\");\n        } catch (Exception e) {\n            date = DateUtil.formatDate(collectDate, \"yyyy-MM-dd HH\");\n        }\n        DecimalFormat df = new DecimalFormat(\"##.##\");\n        // y坐标\n        double count = 0D;\n        if (\"memFragRatio\".equals(statName)) {\n            long rss = appStat.getUsedMemoryRss();\n            long mem = appStat.getUsedMemory();\n            count = Double.parseDouble(df.format(rss * 1.0D / mem));\n        }\n        //为了显示在一个时间范围内\n        if (diffDays > 0) {\n            collectDate = DateUtils.addDays(collectDate, diffDays);\n        }\n\n        return new HighchartDoublePoint(collectDate.getTime(), count, date);\n    }\n\n\n    public static HighchartDoublePoint getFromAppDailyDatas(AppDailyData appDailyData, String statName) throws ParseException {\n        Date collectDate = appDailyData.getDate();\n        //显示用的时间\n        String date = DateUtil.formatDate(collectDate, \"yyyy-MM-dd\");\n        // y坐标\n        double count = 0D;\n        if (\"avg_hit_ratio\".equals(statName)) {\n            count = appDailyData.getAvgHitRatio();\n        } else if (\"min_minute_hit_ratio\".equals(statName)) {\n            count = appDailyData.getMinMinuteHitRatio();\n        } else if (\"max_minute_hit_ratio\".equals(statName)) {\n            count = appDailyData.getMaxMinuteHitRatio();\n        }\n        return new HighchartDoublePoint(collectDate.getTime(), count, date);\n    }\n\n\n    private static Date getDateTime(long collectTime) throws ParseException {\n        try {\n            return DateUtil.parseYYYYMMddHHMM(String.valueOf(collectTime));\n        } catch (Exception e) {\n            return DateUtil.parseYYYYMMddHH(String.valueOf(collectTime));\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/chart/model/HighchartPoint.java",
    "content": "package com.sohu.cache.web.chart.model;\r\n\r\nimport java.text.ParseException;\r\nimport java.util.Date;\r\n\r\nimport org.apache.commons.lang.time.DateUtils;\r\n\r\nimport com.sohu.cache.entity.AppCommandStats;\r\nimport com.sohu.cache.entity.AppStats;\r\nimport com.sohu.cache.web.util.DateUtil;\r\n\r\n/**\r\n * highchart最简单的点\r\n * \r\n * @author leifu\r\n * @Date 2016年8月1日\r\n * @Time 下午12:59:29\r\n */\r\npublic class HighchartPoint {\r\n    /**\r\n     * 时间戳\r\n     */\r\n    private Long x;\r\n\r\n    /**\r\n     * 用于表示y轴数量\r\n     */\r\n    private Long y;\r\n    \r\n    /**\r\n     * 日期\r\n     */\r\n    private String date;\r\n\r\n    public HighchartPoint() {\r\n\r\n    }\r\n\r\n    public HighchartPoint(Long x, Long y, String date) {\r\n        this.x = x;\r\n        this.y = y;\r\n        this.date = date;\r\n    }\r\n\r\n\r\n    public Long getX() {\r\n        return x;\r\n    }\r\n\r\n    public void setX(Long x) {\r\n        this.x = x;\r\n    }\r\n\r\n    public Long getY() {\r\n        return y;\r\n    }\r\n\r\n    public void setY(Long y) {\r\n        this.y = y;\r\n    }\r\n\r\n    public String getDate() {\r\n        return date;\r\n    }\r\n\r\n    public void setDate(String date) {\r\n        this.date = date;\r\n    }\r\n\r\n    public static HighchartPoint getFromAppCommandStats(AppCommandStats appCommandStats, Date currentDate, int diffDays) throws ParseException {\r\n        Date collectDate = getDateTime(appCommandStats.getCollectTime());\r\n        if (!DateUtils.isSameDay(currentDate, collectDate)) {\r\n            return null;\r\n        }\r\n        \r\n        //显示用的时间\r\n        String date = null;\r\n        try {\r\n            date = DateUtil.formatDate(collectDate, \"yyyy-MM-dd HH:mm\");\r\n        } catch (Exception e) {\r\n            date = DateUtil.formatDate(collectDate, \"yyyy-MM-dd HH\");\r\n        }\r\n        // y坐标\r\n        long commandCount = appCommandStats.getCommandCount();\r\n        // x坐标\r\n        //为了显示在一个时间范围内\r\n        if (diffDays > 0) {\r\n            collectDate = DateUtils.addDays(collectDate, diffDays);\r\n        }\r\n        \r\n        return new HighchartPoint(collectDate.getTime(), commandCount, date);\r\n    }\r\n\r\n    public static HighchartPoint getFromAppStats(AppStats appStat, String statName, Date currentDate, int diffDays) throws ParseException {\r\n        Date collectDate = getDateTime(appStat.getCollectTime());\r\n        if (!DateUtils.isSameDay(currentDate, collectDate)) {\r\n            return null;\r\n        }\r\n        //显示用的时间\r\n        String date = null;\r\n        try {\r\n            date = DateUtil.formatDate(collectDate, \"yyyy-MM-dd HH:mm\");\r\n        } catch (Exception e) {\r\n            date = DateUtil.formatDate(collectDate, \"yyyy-MM-dd HH\");\r\n        }\r\n        // y坐标\r\n        long count = 0;\r\n        if (\"hits\".equals(statName)) {\r\n            count = appStat.getHits();\r\n        } else if (\"misses\".equals(statName)) {\r\n            count = appStat.getMisses();\r\n        } else if (\"usedMemory\".equals(statName)) {\r\n            count = appStat.getUsedMemory() / 1024 / 1024;\r\n        } else if (\"usedMemoryRss\".equals(statName)) {\r\n            count = appStat.getUsedMemoryRss() / 1024 / 1024;\r\n        } else if (\"netInput\".equals(statName)) {\r\n            count = appStat.getNetInputByte();\r\n        } else if (\"netOutput\".equals(statName)) {\r\n            count = appStat.getNetOutputByte();\r\n        } else if (\"connectedClient\".equals(statName)) {\r\n            count = appStat.getConnectedClients();\r\n        } else if (\"objectSize\".equals(statName)) {\r\n            count = appStat.getObjectSize();\r\n        } else if (\"hitPercent\".equals(statName)) {\r\n            count = appStat.getHitPercent();\r\n        } else if (\"cpuSys\".equals(statName)) {\r\n            count = appStat.getCpuSys();\r\n        } else if (\"cpuUser\".equals(statName)) {\r\n            count = appStat.getCpuUser();\r\n        } else if (\"cpuSysChildren\".equals(statName)) {\r\n            count = appStat.getCpuSysChildren();\r\n        } else if (\"cpuUserChildren\".equals(statName)) {\r\n            count = appStat.getCpuUserChildren();\r\n        } else if (\"expiredKeys\".equals(statName)) {\r\n            count = appStat.getExpiredKeys();\r\n        } else if (\"evictedKeys\".equals(statName)) {\r\n            count = appStat.getEvictedKeys();\r\n        } else if (\"usedDisk\".equals(statName)) {\r\n            count = appStat.getUsedDisk() / 1024 / 1024;\r\n        }\r\n        //为了显示在一个时间范围内\r\n        if (diffDays > 0) {\r\n            collectDate = DateUtils.addDays(collectDate, diffDays);\r\n        }\r\n        \r\n        return new HighchartPoint(collectDate.getTime(), count, date);\r\n    }\r\n\r\n    private static Date getDateTime(long collectTime) throws ParseException {\r\n        try {\r\n            return DateUtil.parseYYYYMMddHHMM(String.valueOf(collectTime));\r\n        } catch (Exception e) {\r\n            return DateUtil.parseYYYYMMddHH(String.valueOf(collectTime));\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/chart/model/SimpleChartData.java",
    "content": "package com.sohu.cache.web.chart.model;\r\n\r\nimport java.text.ParseException;\r\nimport java.util.Date;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\nimport com.sohu.cache.entity.AppCommandGroup;\r\nimport com.sohu.cache.entity.AppCommandStats;\r\nimport com.sohu.cache.entity.AppStats;\r\nimport com.sohu.cache.web.util.DateUtil;\r\n\r\n/**\r\n * 用于显示chart的简单对象\r\n * \r\n * @author leifu\r\n * @Time 2014年8月31日\r\n */\r\npublic class SimpleChartData {\r\n\t/**\r\n\t * 时间戳\r\n\t */\r\n\tprivate Long x;\r\n\r\n\t/**\r\n\t * 用于表示y轴数量\r\n\t */\r\n\tprivate Long y;\r\n\r\n\t/**\r\n\t * 命令名\r\n\t */\r\n\tprivate String commandName;\r\n\t\r\n\t/**\r\n\t * 日期\r\n\t */\r\n\tprivate String date;\r\n\r\n\tpublic Long getX() {\r\n\t\treturn x;\r\n\t}\r\n\r\n\tpublic void setX(Long x) {\r\n\t\tthis.x = x;\r\n\t}\r\n\r\n\tpublic Long getY() {\r\n\t\treturn y;\r\n\t}\r\n\r\n\tpublic void setY(Long y) {\r\n\t\tthis.y = y;\r\n\t}\r\n\r\n\tpublic String getCommandName() {\r\n\t\treturn commandName;\r\n\t}\r\n\r\n\tpublic void setCommandName(String commandName) {\r\n\t\tthis.commandName = commandName;\r\n\t}\r\n\r\n\tpublic String getDate() {\r\n        return date;\r\n    }\r\n\r\n    public void setDate(String date) {\r\n        this.date = date;\r\n    }\r\n\r\n    /**\r\n\t * AppCommandStats转换为SimpleChartData\r\n\t * \r\n\t * @param appCommandStats\r\n\t * @return\r\n\t * @throws ParseException\r\n\t */\r\n\tpublic static SimpleChartData getFromAppCommandStats(\r\n\t\t\tAppCommandStats appCommandStats, Integer addDay) throws ParseException {\r\n\t\tSimpleChartData chartData = new SimpleChartData();\r\n\t\tlong collectTime = appCommandStats.getCollectTime();\r\n\t\tString commandName = appCommandStats.getCommandName();\r\n\t\tlong commandCount = appCommandStats.getCommandCount();\r\n\t\tDate dateTime = null;\r\n\t\ttry {\r\n\t\t\tdateTime = DateUtil.parseYYYYMMddHHMM(String.valueOf(collectTime));\r\n\t\t} catch (Exception e) {\r\n\t\t\tdateTime = DateUtil.parseYYYYMMddHH(String.valueOf(collectTime));\r\n\t\t}\r\n\t\tLong x = dateTime.getTime();\r\n\t\tif(addDay != null){\r\n\t\t    x += TimeUnit.DAYS.toMillis(1) * addDay;\r\n\t\t}\r\n\t\tLong y = commandCount;\r\n\t\tString date = null;\r\n        try {\r\n            date = DateUtil.formatDate(dateTime, \"yyyy-MM-dd HH:mm\");\r\n        } catch (Exception e) {\r\n            date = DateUtil.formatDate(dateTime, \"yyyy-MM-dd HH\");\r\n        }\r\n\t\tchartData.setX(x);\r\n\t\tchartData.setY(y);\r\n\t\tchartData.setDate(date);\r\n\t\tchartData.setCommandName(commandName);\r\n\t\treturn chartData;\r\n\t}\r\n\r\n\t/**\r\n\t * AppStats转换为SimpleChartData\r\n\t * \r\n\t * @param appStat\r\n\t * @param statName\r\n\t *            命中数、丢失数的字段\r\n\t * @return\r\n\t * @throws ParseException\r\n\t */\r\n\tpublic static SimpleChartData getFromAppStats(AppStats appStat, String statName) throws ParseException {\r\n\t\tSimpleChartData chartData = new SimpleChartData();\r\n\t\tlong collectTime = appStat.getCollectTime();\r\n\t\tlong count = 0;\r\n\t\tif (\"hits\".equals(statName)) {\r\n\t\t\tcount = appStat.getHits();\r\n\t\t} else if (\"misses\".equals(statName)) {\r\n\t\t\tcount = appStat.getMisses();\r\n\t\t} else if (\"usedMemory\".equals(statName)){\r\n\t\t    count = appStat.getUsedMemory() / 1024 / 1024;\r\n\t\t} else if (\"netInput\".equals(statName)) {\r\n\t\t    count = appStat.getNetInputByte();\r\n\t\t} else if (\"netOutput\".equals(statName)) {\r\n            count = appStat.getNetOutputByte();\r\n        } else if (\"hitPercent\".equals(statName)) {\r\n            count = appStat.getHitPercent();\r\n        }\r\n\t\tDate dateTime = null;\r\n\t\ttry {\r\n\t\t\tdateTime = DateUtil.parseYYYYMMddHHMM(String.valueOf(collectTime));\r\n\t\t} catch (Exception e) {\r\n\t\t\tdateTime = DateUtil.parseYYYYMMddHH(String.valueOf(collectTime));\r\n\t\t}\r\n\t\tLong x = dateTime.getTime();\r\n\t\tLong y = count;\r\n\t\tString date = null;\r\n\t\ttry {\r\n            date = DateUtil.formatDate(dateTime, \"yyyy-MM-dd HH:mm\");\r\n        } catch (Exception e) {\r\n            date = DateUtil.formatDate(dateTime, \"yyyy-MM-dd HH\");\r\n        }\r\n\t\tchartData.setX(x);\r\n\t\tchartData.setY(y);\r\n\t\tchartData.setDate(date);\r\n\r\n\t\treturn chartData;\r\n\t}\r\n\r\n\t/**\r\n\t * AppCommandGroup转换为SimpleChartData用于显示pie图\r\n\t * \r\n\t * @param appCommandGroup\r\n\t * @return\r\n\t */\r\n\tpublic static SimpleChartData getFromAppCommandGroup(\r\n\t\t\tAppCommandGroup appCommandGroup) {\r\n\t\tSimpleChartData chartData = new SimpleChartData();\r\n\t\tchartData.setCommandName(appCommandGroup.getCommandName());\r\n\t\tchartData.setY(appCommandGroup.getCount());\r\n\t\treturn chartData;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"SimpleChartData [x=\" + x + \", y=\" + y + \", commandName=\"\r\n\t\t\t\t+ commandName + \"]\";\r\n\t}\r\n\r\n\t\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/chart/model/SplineChartEntity.java",
    "content": "package com.sohu.cache.web.chart.model;\r\n\r\nimport com.sohu.cache.web.chart.key.ChartKeysUtil;\r\n\r\nimport java.util.*;\r\n\r\npublic class SplineChartEntity extends ChartEntity {\r\n    public SplineChartEntity() {\r\n        super();\r\n        putChartType();\r\n        setTooltipShared(true);\r\n    }\r\n\r\n    @Override\r\n    protected void putChartType() {\r\n        this.getChart().put(ChartKeysUtil.ChartKey.TYPE.getKey(), \"spline\");\r\n    }\r\n\r\n    public void setXAxisCategories(List<Object> xAxisCategories) {\r\n        setXAxisCategories(xAxisCategories, 14);\r\n    }\r\n\r\n    public void setXAxisCategories(List<Object> xAxisCategories, int totalLabels) {\r\n        setXAxisCategories(xAxisCategories, totalLabels, 0, -5);\r\n    }\r\n\r\n    public void setXAxisCategories(List<Object> xAxisCategories, int totalLabels, int rotation, int y) {\r\n        putXAxis(ChartKeysUtil.XAxisKey.CATEGORIES.getKey(), xAxisCategories);\r\n        if (xAxisCategories.size() >= totalLabels) {\r\n            Map<String, Object> m = null;\r\n            if (this.getxAxis().containsKey(ChartKeysUtil.XAxisKey.LABELS.getKey())) {\r\n                m = (Map<String, Object>) this.getxAxis().get(ChartKeysUtil.XAxisKey.LABELS.getKey());\r\n            }\r\n            if (m == null) {\r\n                m = new LinkedHashMap<String, Object>();\r\n            }\r\n            m.put(ChartKeysUtil.XAxisKey.LABELS_STEP.getKey(), xAxisCategories.size() / totalLabels + 1);\r\n            m.put(ChartKeysUtil.XAxisKey.LABELS_ROTATION.getKey(), rotation);\r\n            m.put(ChartKeysUtil.XAxisKey.LABELS_Y.getKey(), y);\r\n            m.put(ChartKeysUtil.XAxisKey.MAX_STAGGER_LINES.getKey(), 1);\r\n            putXAxis(ChartKeysUtil.XAxisKey.LABELS.getKey(), m);\r\n        }\r\n    }\r\n\r\n    public void setYAxisTitle(String title) {\r\n        if (this.getyAxis().containsKey(ChartKeysUtil.YAxisKey.TITLE.getKey())) {\r\n            ((Map<String, Object>) this.getyAxis().get(ChartKeysUtil.YAxisKey.TITLE.getKey())).put(ChartKeysUtil.YAxisKey.TITLE_TEXT.getKey(), title);\r\n        } else {\r\n            Map<String, Object> map = new HashMap<String, Object>();\r\n            map.put(ChartKeysUtil.YAxisKey.TITLE_TEXT.getKey(), title);\r\n            putYAxis(ChartKeysUtil.YAxisKey.TITLE.getKey(), map);\r\n        }\r\n    }\r\n\r\n    public void setTooltipCrosshairs(boolean crosshairs) {\r\n        putTooltip(ChartKeysUtil.TooltipKey.CROSSHAIRS.getKey(), crosshairs);\r\n    }\r\n\r\n    public void setTooltipShared(boolean shared) {\r\n        putTooltip(ChartKeysUtil.TooltipKey.SHARED.getKey(), shared);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AnalysisController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.AppAuditType;\nimport com.sohu.cache.dao.AppAuditDao;\nimport com.sohu.cache.dao.InstanceBigKeyDao;\nimport com.sohu.cache.entity.AppAudit;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.ParamCount;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.task.constant.IdleTimeDistriEnum;\nimport com.sohu.cache.task.constant.TtlTimeDistriEnum;\nimport com.sohu.cache.task.constant.ValueSizeDistriEnum;\nimport com.sohu.cache.task.entity.InstanceBigKey;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.util.AppEmailUtil;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\nimport redis.clients.jedis.Tuple;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Created by yijunzhang\n */\n@Controller\npublic class AnalysisController extends BaseController {\n\n    @Autowired\n    private AssistRedisService assistRedisService;\n\n    @Autowired\n    private InstanceBigKeyDao instanceBigKeyDao;\n\n    @Autowired\n    private TaskService taskService;\n\n    @Autowired\n    private AppAuditDao appAuditDao;\n\n    @Autowired\n    private AppEmailUtil appEmailUtil;\n\n    /**\n     * key分析\n     */\n    @RequestMapping(\"/admin/app/key\")\n    public ModelAndView appKey(Model model, Long appId) {\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        List<AppAudit> appAuditList = appService.getAppAudits(appId, AppAuditType.KEY_ANALYSIS.getValue());\n        model.addAttribute(\"appAuditList\", appAuditList);\n        return new ModelAndView(\"analysis/appKey\");\n    }\n\n    /**\n     * 提交前置键值分析申请\n     *\n     * @param appId 应用id\n     */\n    @RequestMapping(value = \"/admin/app/keyAnalysis\")\n    public ModelAndView submitKeyAnalysis(HttpServletRequest request,\n                                          HttpServletResponse response, Long appId, String nodeInfo, String appAnalysisReason) {\n        try {\n            AppUser appUser = getUserInfo(request);\n            AppDesc appDesc = appService.getByAppId(appId);\n            AppAudit appAudit = appService.saveAppKeyAnalysis(appDesc, appUser, appAnalysisReason, nodeInfo);\n            appEmailUtil.noticeAppResult(appDesc, appAudit);\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\n        }\n        return null;\n    }\n\n    /**\n     * key分析结果\n     */\n    @RequestMapping(\"/admin/app/keyAnalysisResult\")\n    public ModelAndView keyAnalysisResult(Model model, Long appId, long auditId) {\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n\n        //idle\n        String idleKeyResultKey = ConstUtils.getRedisServerIdleKey(appId, auditId);\n        Set<Tuple> idleKeyTuples = assistRedisService.zrangeWithScores(idleKeyResultKey, 0, -1);\n        List<ParamCount> idleKeyParamCountList = new ArrayList<ParamCount>();\n        for (Tuple tuple : idleKeyTuples) {\n            String element = tuple.getElement();\n            IdleTimeDistriEnum idleTimeDistriEnum = IdleTimeDistriEnum.getByValue(element);\n            ParamCount paramCount = new ParamCount(idleTimeDistriEnum.getInfo(), tuple.getScore(), \"\");\n            idleKeyParamCountList.add(paramCount);\n        }\n        model.addAttribute(\"idleKeyParamCountList\", idleKeyParamCountList);\n        model.addAttribute(\"idleKeyDistri\", JSONObject.toJSONString(idleKeyParamCountList));\n\n        //type\n        String keyTypeResultKey = ConstUtils.getRedisServerTypeKey(appId, auditId);\n        Set<Tuple> keyTypeTuples = assistRedisService.zrangeWithScores(keyTypeResultKey, 0, -1);\n        List<ParamCount> keyTypeParamCountList = new ArrayList<ParamCount>();\n        for (Tuple tuple : keyTypeTuples) {\n            ParamCount paramCount = new ParamCount(tuple.getElement(), tuple.getScore(), \"\");\n            keyTypeParamCountList.add(paramCount);\n        }\n        model.addAttribute(\"keyTypeParamCountList\", keyTypeParamCountList);\n        model.addAttribute(\"keyTypeDistri\", JSONObject.toJSONString(keyTypeParamCountList));\n\n        //ttl\n        String keyTtlResultKey = ConstUtils.getRedisServerTtlKey(appId, auditId);\n        Set<Tuple> keyTtlTuples = assistRedisService.zrangeWithScores(keyTtlResultKey, 0, -1);\n        List<ParamCount> keyTtlParamCountList = new ArrayList<ParamCount>();\n        for (Tuple tuple : keyTtlTuples) {\n            String element = tuple.getElement();\n            TtlTimeDistriEnum ttlTimeDistriEnum = TtlTimeDistriEnum.getByValue(element);\n            ParamCount paramCount = new ParamCount(ttlTimeDistriEnum.getInfo(), tuple.getScore(), \"\");\n            keyTtlParamCountList.add(paramCount);\n        }\n        model.addAttribute(\"keyTtlParamCountList\", keyTtlParamCountList);\n        model.addAttribute(\"keyTtlDistri\", JSONObject.toJSONString(keyTtlParamCountList));\n\n        //key value size\n        String keyValueSizeResultKey = ConstUtils.getRedisServerValueSizeKey(appId, auditId);\n        Set<Tuple> keyValueSizeTuples = assistRedisService.zrangeWithScores(keyValueSizeResultKey, 0, -1);\n        List<ParamCount> keyValueSizeParamCountList = new ArrayList<ParamCount>();\n        for (Tuple tuple : keyValueSizeTuples) {\n            String element = tuple.getElement();\n            ValueSizeDistriEnum valueSizeDistriEnum = ValueSizeDistriEnum.getByValue(element);\n            ParamCount paramCount = new ParamCount(valueSizeDistriEnum.getInfo(), tuple.getScore(), \"\");\n            keyValueSizeParamCountList.add(paramCount);\n        }\n        model.addAttribute(\"keyValueSizeParamCountList\", keyValueSizeParamCountList);\n        model.addAttribute(\"keyValueSizeDistri\", JSONObject.toJSONString(keyValueSizeParamCountList));\n\n        //big\n        List<InstanceBigKey> instanceBigKeyList = instanceBigKeyDao.getAppBigKeyList(appId, auditId, null);\n        model.addAttribute(\"instanceBigKeyCount\", instanceBigKeyList.size());\n        instanceBigKeyList = instanceBigKeyList.subList(0, Math.min(instanceBigKeyList.size(), 100));\n        model.addAttribute(\"instanceBigKeyList\", instanceBigKeyList);\n\n        return new ModelAndView(\"analysis/keyAnalysis\");\n    }\n\n    /**\n     * 审批执行键值分析\n     */\n    @RequestMapping(value = \"/manage/app/startKeyAnalysis\")\n    public ModelAndView startKeyAnalysis(HttpServletRequest request) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        long auditId = NumberUtils.toLong(request.getParameter(\"appAuditId\"));\n        appAuditDao.updateAppAuditOperateUser(auditId, getUserInfo(request).getId());\n        long taskId = taskService.addAppKeyAnalysisTask(appId, auditId, 0);\n        return new ModelAndView(\"redirect:/manage/task/flow?taskId=\" + taskId);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppAutomationApiController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.StringUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.sql.Timestamp;\nimport java.util.*;\n\n/**\n * Created by chenshi on 2018/10/29.\n */\n@RestController\n@RequestMapping(\"/api/automation\")\npublic class AppAutomationApiController extends BaseController {\n\n    // httpcode:500异常信息\n    private static String ERROR_EXCEPTION = \"系统异常:%s\";\n    private static String ERROR_APP_NAME_REPEATE = \"应用名称:%s重复\";\n    private static String ERROR_APP_REDISVERSION_NOEXIST = \"Redis版本:%s不存在\";\n    private static String ERROR_APP_USER_NOEXIST = \"用户信息不完整:%s\";\n    private static String ERROR_MACHINE_CLUSTER_NOTENOUGH = \"RedisCluster集群机器:%s数量少于3台\";\n    private static String ERROR_MACHINE_SENTINEL_NOTENOUGH = \"RedisSentinel集群机器:%s数量少于2台\";\n    private static String ERROR_MACHINE_IPINFO = \"机器列表iplist:%s,非法机器ip:%s\";\n    private static String ERROR_RESOURCE_INSUFFICENT = \"机器:%s实例资源分配不足\";\n    private static String ERROR_MEM_ASSIGN = \"申请内存:%s M, 机器剩余总内存:%s M,内存分配不足(至少%s M)\";\n    private static String ERROR_CPU_ASSIGN = \"实例数:%s , 机器总核数:%s, cpu分配不足\";\n    private static String ERROR_REDIS_FORMAT = \"部署格式检查异常:%s \";\n    private static String ERROR_SETNIENL_NOTENOUGH = \"RedisSentinel机器分配不足\";\n    private static String ERROR_APP_TOKEN = \"token校验异常:%s,没有权限调用\";\n    private static String ERROR_INSTANCE_NUM = \"%s应用部署异常，当前实例数:%s\";\n    // info信息\n    private static String ERROR_APP_SAVE_INFO = \"应用信息保存异常\";\n    private static String ERROR_REDIS_DEPLOY_INFO = \"Redis应用部署异常\";\n    private static String ERROR_REDISCLUSTER_INTEGRITY_INFO = \"RedisCluster slots不等于16384, slots num=\";\n    private static String ERROR_APP_SETPASSWD_INFO = \"应用设置密码异常\";\n    private static String SUCCESS_INFO = \"创建成功\";\n    // 内存分配上限\n    private static double MEM_MAX_RATIO = 0.85;// 85%留给应用使用\n    private static int MAX_INSTANCE_MEM = 2;// 最大分片2G\n    // app token\n    private static String APP_TOKEN = \"appAdd\";\n\n    /**\n     * <p>\n     * Description: 应用状态检测接口\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/11/14\n     */\n    @RequestMapping(value = \"/app/inspect\", method = {RequestMethod.POST, RequestMethod.GET})\n    public ResponseEntity<List<Map<String, Object>>> appInspect(\n            @RequestHeader(value = \"token\") String token,\n            @RequestBody List<String> appIds) {\n\n        // 1.token 校验\n        if (token.isEmpty() || !token.equals(APP_TOKEN)) {\n            logger.error(\"error token:{}\", String.format(ERROR_APP_TOKEN, token));\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);\n        }\n        // 2.appids 校验\n        if (CollectionUtils.isEmpty(appIds)) {\n            logger.error(\"error param: appIds:{}\", appIds);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);\n        }\n        // 3.获取appid 状态\n        try {\n            List<AppDesc> appDescs = appService.checkAppStatus(appIds);\n            List<Map<String, Object>> lists = new ArrayList<Map<String, Object>>();\n            for (AppDesc appDesc : appDescs) {\n                Map<String, Object> map = new HashMap<String, Object>();\n                map.put(\"appid\", appDesc.getAppId());\n                map.put(\"status\", appDesc.getStatus());\n                if (appDesc.getStatus() == 4) {\n                    List<AppAudit> appAudits = appService.getAppAudits(appDesc.getAppId(), AppAuditType.APP_AUDIT.getValue());\n                    if (!CollectionUtils.isEmpty(appAudits)) {\n                        map.put(\"message\", appAudits.get(0).getRefuseReason());\n                    }\n                }\n                lists.add(map);\n            }\n            return ResponseEntity.status(HttpStatus.OK).body(lists);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);\n        }\n    }\n\n    /**\n     * <p>\n     * Description: api自动创建集群\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/11/14\n     */\n    @RequestMapping(value = \"/app\", method = {RequestMethod.POST})\n    public ResponseEntity<String> addApp(\n            @RequestHeader(value = \"token\") String token,\n            @RequestBody AppInfoApi appInfoApi) {\n\n        // 接口执行时间\n        long startTime = System.currentTimeMillis();\n        JSONObject response = new JSONObject();\n        /**\n         * 1.token权限校验\n         */\n        if (token.isEmpty() || !token.equals(APP_TOKEN)) {\n            logger.error(\"error:{}\", String.format(ERROR_APP_TOKEN, token));\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_APP_TOKEN, token));\n        }\n        /**\n         * 2.默认属性设置\n         */\n        setDefaultConfig(appInfoApi);\n\n        /**\n         * 3. 基础信息检查\n         */\n        RedisVersion redisVersion = null;\n        AppUser appUser = null;\n        try {\n            //1.1 应用名\n            AppDesc appByName = appService.getAppByName(appInfoApi.getName());\n            if (appByName != null && appByName.getAppId() > 0) {\n                logger.error(\"error:{}\", String.format(ERROR_APP_NAME_REPEATE, appInfoApi.getName()));\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_APP_NAME_REPEATE, appInfoApi.getName()));\n            }\n            // 1.2 redisVersion\n            SystemResource redisResource = redisConfigTemplateService.getRedisVersionByName(appInfoApi.getRedisVersion());\n            if (redisResource != null && redisResource.getId() > 0) {\n                logger.error(\"error:{}\", String.format(ERROR_APP_REDISVERSION_NOEXIST, appInfoApi.getRedisVersion()));\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_APP_REDISVERSION_NOEXIST, appInfoApi.getRedisVersion()));\n            }\n            logger.info(\"redisVersion:{}\", redisVersion);\n            // 1.3 获取用户信息\n            AppUser user = appInfoApi.getUser();\n            if (user == null || StringUtils.isEmpty(user.getName())) {\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_APP_USER_NOEXIST, appInfoApi.getUser()));\n            }\n            if (!StringUtils.isEmpty(user.getName())) {\n                appUser = userService.getByName(appInfoApi.getUser().getName());\n                logger.info(\"get user:{}\", appUser);\n            }\n            // 如果不是cc用户 自动创建\n            if (appUser == null) {\n                appUser = new AppUser(user.getName(), user.getChName(), user.getEmail(), user.getMobile(), user.getWeChat(), AppUserTypeEnum.REGULAR_USER.value(), AppUserAlertEnum.YES.value());\n                userService.save(appUser);\n                logger.info(\"create new appUser:{}\", appUser);\n            }\n            if (appUser.getId() == null) {\n                logger.error(\"error:{}\", String.format(ERROR_APP_USER_NOEXIST, appInfoApi.getUser().getName()));\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_APP_USER_NOEXIST, appInfoApi.getUser().getName()));\n            }\n\n            // 1.4 机器ip合法性验证\n            if (!CollectionUtils.isEmpty(appInfoApi.getIplist())) {\n                // 非法机器ip信息\n                List<String> invalidIps = new ArrayList<String>();\n                for (String ip : appInfoApi.getIplist()) {\n                    if (!StringUtils.isEmpty(ip)) {\n                        MachineInfo machineinfo = machineCenter.getMachineInfoByIp(ip);\n                        if (machineinfo == null) {\n                            invalidIps.add(ip);\n                        }\n                    }\n                }\n                // 存在非法机器信息 返回异常信息\n                if (!CollectionUtils.isEmpty(invalidIps) && invalidIps.size() > 0) {\n                    logger.error(\"iplist:{} exist invalid ipinfo:{}\", appInfoApi.getIplist(), invalidIps);\n                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_MACHINE_IPINFO, appInfoApi.getIplist(), invalidIps));\n                }\n\n            } else {\n                logger.error(\"iplist is empty ,error:{}\", appInfoApi.getIplist());\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_MACHINE_IPINFO, appInfoApi.getIplist(), \"无可用机器ip\"));\n            }\n\n            // 1.5 集群 机器数>=3  sentinel 机器数>=2 sentinel单独分配\n            if (appInfoApi.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER && appInfoApi.getIplist().size() < 3) {\n                logger.error(\"error:{}\", String.format(ERROR_MACHINE_CLUSTER_NOTENOUGH, appInfoApi.getIplist()));\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_MACHINE_CLUSTER_NOTENOUGH, appInfoApi.getIplist()));\n            } else if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_SENTINEL && appInfoApi.getIplist().size() < 2) {\n                logger.error(\"error:{}\", String.format(ERROR_MACHINE_SENTINEL_NOTENOUGH, appInfoApi.getIplist()));\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_MACHINE_SENTINEL_NOTENOUGH, appInfoApi.getIplist()));\n            }\n        } catch (Exception e) {\n            // exception  系统异常 500\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, e.getMessage()));\n        }\n\n        /**\n         * 4.自动分配实例\n         *      4.1 自动计算实例个数 instanceNum\n         *      4.2 实例分配完成合理性验证\n         */\n        List<DeployInfo> deployInfoList = new ArrayList<DeployInfo>();\n        List<MachineMemStatInfo> machineMems = new ArrayList<MachineMemStatInfo>();\n        String sentinelMachines = \"\";//setnienl节点列表\n        String appDeployText = \"\";//部署实例信息\n        StringBuilder appDeployTextBuilder = new StringBuilder();\n        try {\n            String ips = String.join(\",\", appInfoApi.getIplist());\n            logger.info(\"ips :\" + ips);\n            if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {\n                //从机器列表获取sentinel节点\n                sentinelMachines = autoSelectSentinel(appInfoApi);\n                if (sentinelMachines == null) {\n                    logger.error(String.format(ERROR_SETNIENL_NOTENOUGH));\n                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_SETNIENL_NOTENOUGH));\n                }\n            }\n\n            String result = appService.generateDeployInfo(appInfoApi.getType(), appInfoApi.getHasSlave(), appInfoApi.getRoom(), Double.parseDouble(appInfoApi.getMemTotalSize() * 1024 + \"\"),\n                    appInfoApi.getIplist().size(), appInfoApi.getInstanceNum(), 0, ips,\n                    \"\", sentinelMachines, deployInfoList, machineMems);\n            logger.info(\"assign redis instance result :\" + result);\n\n            // 4.2 拆解实例\n            if (!CollectionUtils.isEmpty(deployInfoList)) {\n                for (DeployInfo deployInfo : deployInfoList) {\n                    if (appInfoApi.getHasSlave() == 1) {\n                        if (deployInfo.getMasterIp() != null) {\n                            //appDeployText += deployInfo.getMasterIp() + ConstUtils.COLON + deployInfo.getMemSize() + ConstUtils.COLON + deployInfo.getSlaveIp() + \"\\n\";\n                            appDeployTextBuilder.append(deployInfo.getMasterIp())\n                                    .append(ConstUtils.COLON)\n                                    .append(deployInfo.getMemSize())\n                                    .append(ConstUtils.COLON)\n                                    .append(deployInfo.getSlaveIp())\n                                    .append(\"\\n\");\n                        }\n                        if (deployInfo.getSentinelIp() != null) {\n                            //appDeployText += deployInfo.getSentinelIp() + \"\\n\";\n                            appDeployTextBuilder.append(deployInfo.getSentinelIp()).append(\"\\n\");\n                        }\n                    } else {\n                        if (deployInfo.getMasterIp() != null) {\n                            //appDeployText += deployInfo.getMasterIp() + ConstUtils.COLON + deployInfo.getMemSize() + \"\\n\";\n                            appDeployTextBuilder.append(deployInfo.getMasterIp())\n                                    .append(ConstUtils.COLON)\n                                    .append(deployInfo.getMemSize())\n                                    .append(\"\\n\");\n                        }\n                        if (deployInfo.getSentinelIp() != null) {\n                            //appDeployText += deployInfo.getSentinelIp() + \"\\n\";\n                            appDeployTextBuilder.append(deployInfo.getSentinelIp()).append(\"\\n\");\n                        }\n                    }\n                }\n            } else {\n                logger.error(String.format(ERROR_RESOURCE_INSUFFICENT, ips));\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_RESOURCE_INSUFFICENT, ips));\n            }\n            appDeployText = appDeployTextBuilder.toString();\n            logger.info(\"deployInfo : {}\", appDeployText);\n        } catch (Exception e) {\n            // exception  系统异常 500\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, e.getMessage()));\n        }\n\n        /**\n         * 5. 应用信息部署前 实例格式检查\n         *    5.1 内存/cpu核数检查\n         *    5.2 实例格式检查\n         */\n        try {\n            // cpu和mem检查\n            Map<String, Integer> machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n            if (!CollectionUtils.isEmpty(machineMems)) {\n                long remainMem = 0;\n                int remianCpu = 0;\n                for (MachineMemStatInfo machineMemStatInfo : machineMems) {\n                    remainMem += (machineMemStatInfo.getMem() * 1024 - machineMemStatInfo.getApplyMem() / 1024 / 1024);\n                    int machineCpu = machineInstanceCountMap.get(machineMemStatInfo.getIp()) == null ? 0 : machineInstanceCountMap.get(machineMemStatInfo.getIp());\n                    remianCpu += machineMemStatInfo.getCpu() - machineCpu;\n                }\n                // 1)机器剩余内存量\n                long applyMem = appInfoApi.getHasSlave() == 0 ? appInfoApi.getMemTotalSize() * 1024 : appInfoApi.getMemTotalSize() * 2 * 1024;\n                if (remainMem * MEM_MAX_RATIO < applyMem) {\n                    logger.error(String.format(ERROR_MEM_ASSIGN, applyMem, remainMem, applyMem * 1.25));\n                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_MEM_ASSIGN, applyMem, remainMem, applyMem * 1.25));\n                }\n                // 2).机器剩余cpu核数，应用部署实例数,需要判断是否有主从\n                int instanceNumber = 1; //standalone\n                if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {\n                    instanceNumber = 2;\n                } else if (appInfoApi.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                    instanceNumber = appInfoApi.getHasSlave() == 1 ? appInfoApi.getInstanceNum() * 2 + 1 : appInfoApi.getInstanceNum() + 1;\n                }\n                if (instanceNumber > remianCpu) {\n                    // cpu核数不够\n                    logger.error(String.format(ERROR_CPU_ASSIGN, instanceNumber, remianCpu));\n                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_CPU_ASSIGN, instanceNumber, remianCpu));\n                }\n                logger.info(\"remain cpuNum:{},app instanceNum:{},applyMemTotal:{}M, remian machineMemTotal:{}M\", remianCpu, instanceNumber, applyMem, remainMem);\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, e.getMessage()));\n        }\n\n        // 实例格式检查\n        try {\n            DataFormatCheckResult dataFormatCheckResult = appDeployCenter.checkAppDeployDetail4Api(appInfoApi, appDeployText, redisVersion);\n            if (!dataFormatCheckResult.isSuccess()) {\n                //检查异常\n                logger.error(String.format(ERROR_REDIS_FORMAT, dataFormatCheckResult.getMessage()));\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_REDIS_FORMAT, dataFormatCheckResult.getMessage()));\n            }\n        } catch (Exception e) {\n            // exception  系统异常 500\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, e.getMessage()));\n        }\n\n        /**\n         * 6.appDesc 和 appAudit 应用信息保存\n         */\n        AppAudit appAudit = saveAppInfoAndLog(redisVersion, appUser, appInfoApi);\n        if (appAudit == null) {\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, ERROR_APP_SAVE_INFO));\n        }\n        Long appId = appAudit.getAppId();\n        Long appAuditId = appAudit.getId();\n        if (appId == null || appAuditId == null) {\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, ERROR_APP_SAVE_INFO));\n        }\n        // 获取应用信息\n        AppDesc appDesc = appService.getByAppId(appId);\n\n        /**\n         * 7.异步提交创建应用任务\n         */\n        asyncExecuteAppDeployTask(appId, appAuditId, appDeployText, appUser, appInfoApi);\n\n        /**\n         * 8.发邮件通知管理员\n         */\n        if (appDesc != null) {\n            appEmailUtil.noticeAppResultByApi(appDesc, appAudit);\n        }\n        logger.info(\"api create appdesc cost time :{} ms\", (System.currentTimeMillis() - startTime));\n        response.put(\"cost\", System.currentTimeMillis() - startTime);\n        response.put(\"status\", appDesc.getStatus());\n        response.put(\"appId\", appId);\n        return ResponseEntity.status(HttpStatus.OK).body(response.toString());\n    }\n\n    /**\n     * <p>\n     * Description: 创建集群异步任务\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/11/14\n     */\n    public void asyncExecuteAppDeployTask(final Long appId, final Long appAuditId, final String appDeployText, final AppUser appUser, final AppInfoApi appInfoApi) {\n\n        String key = \"app-automation-\" + appId;\n        asyncService.submitFuture(AsyncThreadPoolFactory.APP_POOL, new KeyCallable<Boolean>(key) {\n            public Boolean execute() {\n                try {\n                    long start = System.currentTimeMillis();\n                    ResponseEntity<String> result = deployApp(appId, appAuditId, appDeployText, appUser, appInfoApi);\n                    logger.info(\"async execute task :{} , cost time:{} ms\", result, System.currentTimeMillis() - start);\n                    if (result.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {\n                        // 记录异常原因\n                        appAuditDao.updateRefuseReason(appAuditId, result.getBody());\n                    }\n                    return true;\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n        });\n    }\n\n    /**\n     * <p>\n     * Description: 部署应用\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/11/14\n     */\n    public ResponseEntity<String> deployApp(Long appId, Long appAuditId, String appDeployText, AppUser appUser, AppInfoApi appInfoApi) {\n\n        /**\n         * 1.开始部署redis\n         */\n        try {\n            boolean isSuccess = false;\n            if (appAuditId != null && StringUtils.isNotBlank(appDeployText)) {\n                String[] appDetails = appDeployText.split(\"\\n\");\n                // 部署service\n                isSuccess = appDeployCenter.allocateResourceApp(appAuditId, Arrays.asList(appDetails), appUser);\n            }\n            // 部署失败 直接返回\n            if (!isSuccess) {\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, ERROR_REDIS_DEPLOY_INFO));\n            }\n            logger.info(\"deploy rediscluster :\" + isSuccess);\n        } catch (Exception e) {\n            // exception  系统异常 500\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, e.getMessage()));\n        }\n\n        /**\n         * 2.集群完整性验证\n         */\n        int slotsNum = 0;\n        try {\n            if (appInfoApi.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                //等待集群握手,有时api获取会不完整\n                Thread.sleep(2000);\n                logger.info(\"wait 2 seocnds for cluster meet success!\");\n\n                Map<String, InstanceSlotModel> clusterSlotsMap = redisCenter.getClusterSlotsMap(appId);\n                logger.info(\"==================clusterSlots validate ==================\");\n                for (Map.Entry<String, InstanceSlotModel> InstanceSlot : clusterSlotsMap.entrySet()) {\n                    slotsNum += InstanceSlot.getValue().getSlotList().size();\n                }\n                logger.info(\"cluster slots assign num =\" + slotsNum);\n                if (slotsNum != 16384) {\n                    logger.info(\"==================cluster is not ok ! ==================\");\n                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, ERROR_REDISCLUSTER_INTEGRITY_INFO + slotsNum));\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, e.getMessage()));\n        }\n\n        /**\n         * 3.部署节点个数验证\n         */\n        AppDesc appDesc = appService.getByAppId(appId);\n        try {\n            // 部署节点数验证\n            List<InstanceInfo> instancelist = instanceDao.getEffectiveInstListByAppId(appId);\n            if (CollectionUtils.isEmpty(instancelist)) {\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_INSTANCE_NUM, appDesc.getTypeDesc(), 0));\n            }\n            logger.info(\"appid :{} ,type:{}, redis instanceNum ={}\", appId, appDesc.getTypeDesc(), instancelist.size());\n            // cluster/standalone/sentinel节点数验证\n            if (appInfoApi.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER && instancelist.size() < 3) {\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_INSTANCE_NUM, appDesc.getTypeDesc(), instancelist.size()));\n            } else if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_SENTINEL && instancelist.size() < 5) {\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_INSTANCE_NUM, appDesc.getTypeDesc(), instancelist.size()));\n            } else if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_STANDALONE && instancelist.size() != 1) {\n                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_INSTANCE_NUM, appDesc.getTypeDesc(), instancelist.size()));\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, e.getMessage()));\n        }\n\n        /**\n         * 4.设置验证密码\n         */\n        try {\n            if (appId > 0) {\n                // 设置密码\n                redisDeployCenter.fixPassword(appId, null, null, true);\n                // 密码校验逻辑\n                boolean checkFlag = redisDeployCenter.checkAuths(appId);\n                logger.info(\"check app passwd:\" + checkFlag);\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(String.format(ERROR_EXCEPTION, ERROR_APP_SETPASSWD_INFO));\n        }\n        return ResponseEntity.status(HttpStatus.OK).body(\"应用创建成功\");\n    }\n\n    /**\n     * 保存应用和审核信息\n     *\n     * @param redisVersion 版本信息\n     * @param appUser      用户信息\n     * @return 审核对象\n     */\n    public AppAudit saveAppInfoAndLog(RedisVersion redisVersion, AppUser appUser, AppInfoApi appInfoApi) {\n\n        try {\n            AppDesc appDesc = new AppDesc();\n            appDesc.setName(appInfoApi.getName());\n            appDesc.setIntro(appInfoApi.getDesc() == null ? \"\" : appInfoApi.getDesc());\n            appDesc.setClientMachineRoom(appInfoApi.getRoom() == null ? \"\" : appInfoApi.getRoom());\n            appDesc.setType(appInfoApi.getType());\n            appDesc.setIsTest(appInfoApi.getIsTest());\n            appDesc.setTypeDesc(appDesc.getTypeDesc());\n            appDesc.setVersionId(redisVersion.getId());\n            appDesc.setUserId(appUser.getId());\n            appDesc.setOfficer(String.valueOf(appUser.getId()));\n            appDesc.setClientConnAlertValue(2000);\n            appDesc.setForecaseQps(1000);\n            appDesc.setForecastObjNum(100000);\n            appDesc.setMemAlertValue(85);\n            appDesc.setVerId(1);//获取版本id\n            appDesc.setStatus((short) AppStatusEnum.STATUS_ALLOCATED.getStatus());\n            // 设置命中率报警0,默认不监控\n            appDesc.setHitPrecentAlertValue(0);\n            // 客户端默认关闭监控\n            appDesc.setIsAccessMonitor(AppUserAlertEnum.NO.value());\n            Timestamp now = new Timestamp(new Date().getTime());\n            appDesc.setCreateTime(now);\n            appDesc.setPassedTime(now);\n\n            appService.save(appDesc);\n            // 保存应用和用户的关系\n            appService.saveAppToUser(appDesc.getAppId(), appDesc.getUserId());\n            // 更新appKey\n            long appId = appDesc.getAppId();\n            appService.updateAppKey(appId);\n\n            // 保存应用审批信息\n            AppAudit appAudit = new AppAudit();\n            appAudit.setAppId(appId);\n            appAudit.setUserId(appUser.getId());\n            appAudit.setUserName(appUser.getName());\n            appAudit.setModifyTime(new Date());\n            appAudit.setParam1(String.valueOf(appInfoApi.getMemTotalSize()));\n            appAudit.setParam2(appDesc.getTypeDesc());\n            appAudit.setInfo(\"类型:\" + appDesc.getTypeDesc() + \";初始申请空间:\" + appInfoApi.getMemTotalSize() + \"G (<a style='color:red'>API创建</a>)\");\n            appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n            appAudit.setType(AppAuditType.APP_AUDIT.getValue());\n            appAuditDao.insertAppAudit(appAudit);\n\n            logger.info(\"appAudit :\" + appAudit);\n            // 保存申请日志\n            AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n                    AppAuditLogTypeEnum.APP_DESC_APPLY);\n            if (appAuditLog != null) {\n                appAuditLogDao.save(appAuditLog);\n            }\n            return appAudit;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    /**\n     * <p>\n     * Description:初始化设置\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/10/31\n     */\n    public void setDefaultConfig(AppInfoApi appInfoApi) {\n        //1.实例数量\n        if (appInfoApi.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n            int memTotalSize = appInfoApi.getMemTotalSize();\n            int maxInstanceMem = MAX_INSTANCE_MEM;//2G\n            int machineNum = appInfoApi.getIplist().size();\n            int instanceNum = 3;\n            // 1.正式应用 /测试计算实例数量\n            if (machineNum < 3) {\n                logger.info(\" machine is not enough !\");\n            }\n            if (maxInstanceMem * 3 <= memTotalSize) {\n                instanceNum = memTotalSize / maxInstanceMem;  //取整\n            }\n            if (maxInstanceMem * 3 > memTotalSize) {\n                instanceNum = machineNum >= 3 ? machineNum : 3;    //取默认值\n            }\n            Double instanceMem = Double.parseDouble(memTotalSize + \"\") / instanceNum * 1024;\n            logger.info(\"集群申请内存量:{}G,机器数:{},分配实例数:{},实例内存:{}\", memTotalSize, machineNum, instanceNum, instanceMem.intValue());\n            appInfoApi.setInstanceNum(instanceNum);\n        } else if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {\n            appInfoApi.setInstanceNum(2);\n        } else if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_STANDALONE) {\n            appInfoApi.setInstanceNum(1);\n        }\n        // 2.默认值 sentinel 有slave | 设置sentinel数量\n        if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {\n            appInfoApi.setHasSlave(1);//sentinel节点需要设置主从\n            if (appInfoApi.getSentinelNum() < 3 || appInfoApi.getSentinelNum() % 2 == 0) {\n                appInfoApi.setSentinelNum(3);//设置默认sentinel节点数量\n            }\n        }\n        if (appInfoApi.getType() == ConstUtils.CACHE_REDIS_STANDALONE) {\n            appInfoApi.setHasSlave(0);//standalone节点不设置从节点\n        }\n        // 3.版本默认值 redis-3.0.7\n        if (StringUtil.isBlank(appInfoApi.getRedisVersion())) {\n            appInfoApi.setRedisVersion(\"redis-3.0.7\");\n        }\n    }\n\n    /**\n     * <p>\n     * Description:自动获取sentinel节点\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/11/1\n     */\n    public String autoSelectSentinel(AppInfoApi appInfoApi) {\n\n        String sentinelMachines = \"\";\n        StringBuilder sentinelMachinesBuilder = new StringBuilder();\n        try {\n            //1. 默认sentinel节点数\n            int sentinelNum = appInfoApi.getSentinelNum();\n            List<MachineMemStatInfo> machineMemStatInfoList = machineCenter.getAllValidMachineMem(new ArrayList<String>(), null, 3);\n            List<MachineMemStatInfo> machineCandi = new ArrayList<MachineMemStatInfo>();\n            for (MachineMemStatInfo memStatInfo : machineMemStatInfoList) {\n                memStatInfo.setInstanceNum(instanceDao.getInstListByIp(memStatInfo.getIp()).size());\n                appService.getMachineCandiList(memStatInfo, 0d, 1, 0, machineCandi);\n            }\n            // 2.优先挑选空闲机器，否则走随机挑选\n            if (machineCandi.size() < sentinelNum) {\n                logger.warn(\"machineCandi size = {},default random sentinel ip!\", machineCandi.size());\n                machineCandi = machineMemStatInfoList;\n            }\n            if (machineCandi.size() < 3) {\n                logger.warn(\"machineCandi size = {} is not enough!\", machineCandi.size());\n                return null;\n            }\n            // 3.按机房分类 (跨机房部署)\n            List<MachineRoom> roomList = machineCenter.getEffectiveRoom();\n            Map<String, List<MachineMemStatInfo>> sentinelMachineMap = new HashMap<String, List<MachineMemStatInfo>>();\n            for (MachineRoom room : roomList) {\n                List<MachineMemStatInfo> list = new ArrayList<MachineMemStatInfo>();\n                sentinelMachineMap.put(room.getName(), list);\n            }\n            for (MachineMemStatInfo memStatInfo : machineCandi) {\n                sentinelMachineMap.get(memStatInfo.getRoom()).add(memStatInfo);\n            }\n            Set<MachineMemStatInfo> sentinelMachineSet = new HashSet<MachineMemStatInfo>();\n            // 4.for select\n            while (sentinelMachineSet.size() < sentinelNum && sentinelMachineMap.size() >= 3) {\n                for (Map.Entry<String, List<MachineMemStatInfo>> entry : sentinelMachineMap.entrySet()) {\n                    getResMachines(entry.getValue(), 1, sentinelMachineSet);\n                    if (sentinelMachineSet.size() == sentinelNum) {\n                        break;\n                    }\n                }\n            }\n            logger.info(\"sentinelMachineSet:{}\", sentinelMachineSet);\n            if (sentinelMachineSet.size() >= 3) {\n                for (MachineMemStatInfo sentinelMachine : sentinelMachineSet) {\n                    //sentinelMachines += sentinelMachine.getIp() + \",\";\n                    sentinelMachinesBuilder.append(sentinelMachine.getIp()).append(\",\");\n                }\n                sentinelMachines = sentinelMachinesBuilder.toString();\n                sentinelMachines = sentinelMachines.substring(0, sentinelMachines.lastIndexOf(\",\"));\n                logger.info(\"sentinelMachines:{}\", sentinelMachines);\n                return sentinelMachines;\n            }\n            return null;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    private void getResMachines(List<MachineMemStatInfo> machineCandi, Integer machineNum, Set<MachineMemStatInfo> resMachines) {\n        if (machineCandi == null) {\n            return;\n        }\n        Map map = new HashMap();\n        if (machineCandi.size() < machineNum) {\n            return;\n        } else {\n            while (map.size() < machineNum) {\n                int random = (int) (Math.random() * machineCandi.size());\n                if (!map.containsKey(random)) {\n                    map.put(random, \"\");\n                    resMachines.add(machineCandi.get(random));\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppClientDataShowController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.client.service.*;\nimport com.sohu.cache.dao.AppClientStatisticGatherDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\nimport com.sohu.cache.util.NumberUtil;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.util.Page;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 应用客户端统计相关\n *\n * @author leifu\n * @Time 2014年8月31日\n */\n@Controller\n@RequestMapping(\"/client/show\")\npublic class AppClientDataShowController extends BaseController {\n\n    /**\n     * 收集数据时间format\n     */\n    private final static String COLLECT_TIME_FORMAT = \"yyyyMMddHHmmss\";\n\n    @Autowired\n    private AppClientReportCommandService appClientReportCommandService;\n\n    @Autowired\n    private AppClientReportExceptionService appClientReportExceptionService;\n    /**\n     * 客户端异常服务\n     */\n    @Resource(name = \"clientReportExceptionService\")\n    private ClientReportExceptionService clientReportExceptionService;\n\n    /**\n     * 应用基本服务\n     */\n    @Resource(name = \"appService\")\n    private AppService appService;\n    /**\n     * 实例信息\n     */\n    @Resource(name = \"instanceStatsCenter\")\n    private InstanceStatsCenter instanceStatsCenter;\n\n    @Autowired\n    private AppClientStatisticGatherDao appClientStatisticGatherDao;\n\n    /**\n     * 应用客户端统计首页\n     */\n    @RequestMapping(\"/index\")\n    public ModelAndView doIndex(HttpServletRequest request, HttpServletResponse response, Model model) {\n        Long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        if (appId == null || appId <= 0) {\n            return new ModelAndView(\"\");\n        }\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        model.addAttribute(\"tabTag\", request.getParameter(\"tabTag\"));\n        model.addAttribute(\"type\", request.getParameter(\"type\"));\n        model.addAttribute(\"searchDate\", request.getParameter(\"searchDate\"));\n        model.addAttribute(\"commandStatisticsStartDate\", request.getParameter(\"commandStatisticsStartDate\"));\n        model.addAttribute(\"commandStatisticsEndDate\", request.getParameter(\"commandStatisticsEndDate\"));\n        model.addAttribute(\"exceptionStartDate\", request.getParameter(\"exceptionStartDate\"));\n        model.addAttribute(\"exceptionEndDate\", request.getParameter(\"exceptionEndDate\"));\n        model.addAttribute(\"valueDistriStartDate\", request.getParameter(\"valueDistriStartDate\"));\n        model.addAttribute(\"valueDistriEndDate\", request.getParameter(\"valueDistriEndDate\"));\n        model.addAttribute(\"costDistriStartDate\", request.getParameter(\"costDistriStartDate\"));\n        model.addAttribute(\"costDistriEndDate\", request.getParameter(\"costDistriEndDate\"));\n        model.addAttribute(\"clientIp\", request.getParameter(\"clientIp\"));\n        model.addAttribute(\"pageNo\", request.getParameter(\"pageNo\"));\n        model.addAttribute(\"firstCommand\", request.getParameter(\"firstCommand\"));\n        model.addAttribute(\"timeDimensionality\", request.getParameter(\"timeDimensionality\"));\n        return new ModelAndView(\"client/appClientIndex\");\n    }\n\n    /**\n     * 客户端异常查询\n     */\n    @RequestMapping(\"/exception\")\n    public ModelAndView doException(HttpServletRequest request, HttpServletResponse response, Model model) {\n        // 1.1 应用信息\n        Long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        if (appId <= 0) {\n            return new ModelAndView(\"\");\n        }\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n\n        // 1.2 异常类型\n        int type = NumberUtil.toInt(request.getParameter(\"type\"));\n        model.addAttribute(\"type\", type);\n\n        // 1.3 客户端ip\n        String clientIp = request.getParameter(\"clientIp\");\n        model.addAttribute(\"clientIp\", clientIp);\n\n        // 1.4 日期格式转换\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = fillWithClientExceptionTime(request, model);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        // 2. 分页查询异常\n        int totalCount = clientReportExceptionService.getAppExceptionCount(appId, timeBetween.getStartTime(), timeBetween.getEndTime(), type, clientIp);\n        int pageNo = NumberUtils.toInt(request.getParameter(\"pageNo\"), 1);\n        int pageSize = NumberUtils.toInt(request.getParameter(\"pageSize\"), 10);\n        Page page = new Page(pageNo, pageSize, totalCount);\n        model.addAttribute(\"page\", page);\n\n        List<AppClientExceptionStat> appClientExceptionList = clientReportExceptionService.getAppExceptionList(appId,\n                timeBetween.getStartTime(), timeBetween.getEndTime(), type, clientIp, page);\n        model.addAttribute(\"appClientExceptionList\", appClientExceptionList);\n\n        return new ModelAndView(\"client/clientException\");\n    }\n\n    /**\n     * 异常查询日期格式\n     */\n    private TimeBetween fillWithClientExceptionTime(HttpServletRequest request, Model model) throws ParseException {\n        final String exceptionDateFormat = \"yyyy-MM-dd\";\n        String exceptionStartDateParam = request.getParameter(\"exceptionStartDate\");\n        String exceptionEndDateParam = request.getParameter(\"exceptionEndDate\");\n        Date startDate;\n        Date endDate;\n        if (StringUtils.isBlank(exceptionStartDateParam) || StringUtils.isBlank(exceptionEndDateParam)) {\n            // 如果为空默认取昨天和今天\n            SimpleDateFormat sdf = new SimpleDateFormat(exceptionDateFormat);\n            startDate = sdf.parse(sdf.format(new Date()));\n            endDate = DateUtils.addDays(startDate, 1);\n            exceptionStartDateParam = DateUtil.formatDate(startDate, exceptionDateFormat);\n            exceptionEndDateParam = DateUtil.formatDate(endDate, exceptionDateFormat);\n        } else {\n            endDate = DateUtil.parse(exceptionEndDateParam, exceptionDateFormat);\n            startDate = DateUtil.parse(exceptionStartDateParam, exceptionDateFormat);\n            //限制不能超过7天\n            if (endDate.getTime() - startDate.getTime() > TimeUnit.DAYS.toMillis(7)) {\n                startDate = DateUtils.addDays(endDate, -7);\n            }\n        }\n        // 前端需要\n        model.addAttribute(\"exceptionStartDate\", exceptionStartDateParam);\n        model.addAttribute(\"exceptionEndDate\", exceptionEndDateParam);\n        // 查询后台需要\n        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n        long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));\n        return new TimeBetween(startTime, endTime, startDate, endDate);\n    }\n\n    @RequestMapping(\"/commandStatistics\")\n    public ModelAndView doCommandStatistics(HttpServletRequest request, HttpServletResponse response, Model model) {\n        // 1.应用信息\n        Long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        if (appId <= 0) {\n            return new ModelAndView(\"\");\n        }\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        model.addAttribute(\"appId\", appId);\n\n        // 2.获取时间区间\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        searchDate = timeBetween.getFormatStartDate();\n        model.addAttribute(\"searchDate\", searchDate);\n\n        try {\n            List<Map<String, Object>> appClientGatherStatList = appClientStatisticGatherDao.getAppClientStatisticByGatherTime(appId, searchDate);\n            model.addAttribute(\"appClientGatherStat\", CollectionUtils.isNotEmpty(appClientGatherStatList) && appClientGatherStatList.size() > 0 ? appClientGatherStatList.get(0) : null);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        // 3.客户端的统计信息\n        Map<String, List<Map<String, Object>>> appClientCommandStatisticsMap = appClientReportCommandService.getAppCommandClientStatistics(appId, null, startTime, endTime, null);\n        model.addAttribute(\"appClientCommandStatisticsJson\", JSONObject.toJSONString(appClientCommandStatisticsMap));\n\n        return new ModelAndView(\"client/clientCommandStatistics\");\n    }\n\n    @RequestMapping(\"/commandStatistics/client\")\n    public ModelAndView getCommandStatisticsByClient(HttpServletRequest request, HttpServletResponse response, Model model) {\n        // 1.应用信息\n        Long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        if (appId <= 0) {\n            return new ModelAndView(\"\");\n        }\n        model.addAttribute(\"appId\", appId);\n        // 2.获取时间区间\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        model.addAttribute(\"searchDate\", timeBetween.getFormatStartDate());\n        List<String> clientList = appClientReportCommandService.getAppDistinctClients(appId, startTime, endTime);\n        model.addAttribute(\"clientList\", clientList);\n        List<String> commandList = appClientReportCommandService.getAppDistinctCommand(appId, startTime, endTime);\n        model.addAttribute(\"commandList\", commandList);\n\n        String firstClient = request.getParameter(\"firstClient\");\n        if (StringUtils.isBlank(firstClient) && CollectionUtils.isNotEmpty(clientList)) {\n            firstClient = clientList.get(0);\n        }\n        model.addAttribute(\"firstClient\", firstClient);\n\n        String firstCommand = request.getParameter(\"firstCommand\");\n        if (StringUtils.isBlank(firstCommand) && CollectionUtils.isNotEmpty(commandList)) {\n            firstCommand = commandList.get(0);\n        }\n        model.addAttribute(\"firstCommand\", firstCommand);\n\n        if (\"all\".equals(firstClient)) {\n            List<Map<String, Object>> sumCommandStatList = appClientReportCommandService.getSumCmdStatByCmd(appId, startTime, endTime, firstCommand);\n            model.addAttribute(\"sumCommandStatJson\", JSONObject.toJSONString(sumCommandStatList));\n        } else if (\"all\".equals(firstCommand)) {\n            List<Map<String, Object>> sumClientStatList = appClientReportCommandService.getSumCmdStatByClient(appId, startTime, endTime, firstClient);\n            model.addAttribute(\"sumClientStatJson\", JSONObject.toJSONString(sumClientStatList));\n        } else {\n            Map<String, List<Map<String, Object>>> appClientCommandStatisticsMap = appClientReportCommandService.getAppCommandClientStatistics(appId, firstCommand, startTime, endTime, firstClient);\n            model.addAttribute(\"appClientCommandStatisticsJson\", JSONObject.toJSONString(appClientCommandStatisticsMap));\n        }\n        return new ModelAndView(\"client/commandStatisticsByClient\");\n    }\n\n    @RequestMapping(\"/exceptionStatistics\")\n    public ModelAndView doExceptionStatistics(HttpServletRequest request, HttpServletResponse response, Model model) {\n        // 1.应用信息\n        Long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        if (appId <= 0) {\n            return new ModelAndView(\"\");\n        }\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        model.addAttribute(\"appId\", appId);\n\n        // 2.获取时间区间\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        searchDate = timeBetween.getFormatStartDate();\n        model.addAttribute(\"searchDate\", searchDate);\n\n        try {\n            List<Map<String, Object>> appClientGatherStatList = appClientStatisticGatherDao.getAppClientStatisticByGatherTime(appId, searchDate);\n            model.addAttribute(\"appClientGatherStat\", CollectionUtils.isNotEmpty(appClientGatherStatList) && appClientGatherStatList.size() > 0 ? appClientGatherStatList.get(0) : null);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        // 3.客户端的统计信息\n        Map<String, List<Map<String, Object>>> appClientExceptionStatisticsMap = appClientReportExceptionService.getAppExceptionStatisticsMap(appId, null, startTime, endTime, null);\n        model.addAttribute(\"appClientExceptionStatisticsJson\", JSONObject.toJSONString(appClientExceptionStatisticsMap));\n\n        return new ModelAndView(\"client/clientExceptionStatistics\");\n    }\n\n    @RequestMapping(\"/exceptionStatistics/client\")\n    public ModelAndView getExceptionStatisticsByClient(HttpServletRequest request, HttpServletResponse response, Model model) {\n        // 1.应用信息\n        Long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        if (appId <= 0) {\n            return new ModelAndView(\"\");\n        }\n        model.addAttribute(\"appId\", appId);\n\n        Integer type = NumberUtils.toInt(request.getParameter(\"exceptionType\"));\n        String viewName = type == 0 ? \"connExceptionStatisticsByClient\" : \"cmdExceptionStatisticsByClient\";\n\n        // 2.获取时间区间\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        model.addAttribute(\"searchDate\", timeBetween.getFormatStartDate());\n        Map<String, List<String>> clientConfigMap = appClientReportExceptionService.getAppClientConfigs(appId, type, startTime, endTime);\n        model.addAttribute(\"clientConfigMap\", clientConfigMap);\n\n        String firstClient = request.getParameter(\"firstClient\");\n        if (StringUtils.isBlank(firstClient) && CollectionUtils.isNotEmpty(clientConfigMap.keySet())) {\n            firstClient = clientConfigMap.keySet().iterator().next();\n        }\n        model.addAttribute(\"firstClient\", firstClient);\n\n        Map<String, List<Map<String, Object>>> appClientExceptionStatisticsMap = appClientReportExceptionService.getAppExceptionStatisticsMap(appId, firstClient, startTime, endTime, type);\n        model.addAttribute(\"appClientExceptionStatisticsJson\", JSONObject.toJSONString(appClientExceptionStatisticsMap));\n        List<Map<String, Object>> appNodeExceptionStatisticsList = appClientReportExceptionService.getDistinctClientNodeStatistics(appId, firstClient, startTime, endTime, type);\n        model.addAttribute(\"appNodeExceptionStatisticsList\", appNodeExceptionStatisticsList);\n\n        return new ModelAndView(\"client/\" + viewName);\n    }\n\n    @RequestMapping(value = \"/sumCommandStat/command\")\n    public void getSumCmdStatByCmd(HttpServletRequest request, HttpServletResponse response) {\n        Long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        String command = request.getParameter(\"command\");\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n\n        JSONObject json = new JSONObject();\n        String result = \"success\";\n        List<Map<String, Object>> sumCommandStatMap = appClientReportCommandService.getSumCmdStatByCmd(appId, startTime, endTime, command);\n        if (CollectionUtils.isEmpty(sumCommandStatMap)) {\n            result = \"sumCommandStatMap is empty\";\n        }\n        json.put(\"result\", result);\n        json.put(\"sumCommandStatMap\", sumCommandStatMap);\n        sendMessage(response, json.toString());\n    }\n\n    @RequestMapping(value = \"/latencyCommandDetails\")\n    public ModelAndView getLatencyCommandDetails(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        model.addAttribute(\"appId\", appId);\n        long timestamp = NumberUtils.toLong(request.getParameter(\"searchTime\")); //毫秒\n        Date date = new Date(timestamp);\n        SimpleDateFormat format = new SimpleDateFormat(\"yyyyMMddHHmm00\");\n        long searchTime = NumberUtils.toLong(format.format(date));\n        model.addAttribute(\"searchTime\", new SimpleDateFormat(\"yyyy-MM-dd HH:mm:00\").format(date));\n\n        Map<String, Map<String, Object>> sumCmdExpStatMap = appClientReportExceptionService.getSumCmdExpStatGroupByNode(appId, searchTime);\n        model.addAttribute(\"sumCmdExpStatMap\", sumCmdExpStatMap);\n\n        Set<String> nodeSet = sumCmdExpStatMap.keySet();\n        Map<String, List<Map<String, Object>>> latencyCommandDetailMap = appClientReportExceptionService.getLatencyCommandDetails(nodeSet, searchTime);\n        model.addAttribute(\"latencyCommandDetailMap\", latencyCommandDetailMap);\n        return new ModelAndView(\"/client/cmdExceptionCommandDetail\");\n    }\n\n    @RequestMapping(value = \"/latencyCommandDetail/node\")\n    public void getLatencyCommandDetailByNode(HttpServletRequest request, HttpServletResponse response) {\n        JSONObject json = new JSONObject();\n        String result = \"success\";\n        String client = request.getParameter(\"client\");\n        String node = request.getParameter(\"node\");\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        List<Map<String, Object>> latencyCommandDetailList = appClientReportExceptionService.getLatencyCommandDetailByNode(client, node, startTime, endTime);\n        if (CollectionUtils.isEmpty(latencyCommandDetailList)) {\n            result = \"latencyCommandDetailList is empty\";\n        }\n        json.put(\"result\", result);\n        json.put(\"latencyCommandDetailList\", latencyCommandDetailList);\n        sendMessage(response, json.toString());\n    }\n\n    private TimeBetween fillWithDateFormat(String searchDate) throws ParseException {\n\n        final String dateFormat = \"yyyy-MM-dd\";\n        Date startDate;\n        Date endDate;\n        if (StringUtils.isBlank(searchDate)) {\n            // 如果为空默认取今天\n            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);\n            startDate = sdf.parse(sdf.format(new Date()));\n        } else {\n            startDate = DateUtil.parse(searchDate, dateFormat);\n        }\n        endDate = DateUtils.addDays(startDate, 1);\n        // 查询后台需要\n        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n        long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));\n        return new TimeBetween(startTime, endTime, startDate, endDate);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Table;\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.AppUserDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.enums.InstanceAlertCheckCycleEnum;\nimport com.sohu.cache.redis.enums.InstanceAlertCompareTypeEnum;\nimport com.sohu.cache.redis.enums.InstanceAlertTypeEnum;\nimport com.sohu.cache.redis.util.Command;\nimport com.sohu.cache.stats.app.AppDailyDataCenter;\nimport com.sohu.cache.stats.app.AppDeployCenter;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.stats.instance.InstanceAlertConfigService;\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.chart.model.HighchartDoublePoint;\nimport com.sohu.cache.web.chart.model.HighchartPoint;\nimport com.sohu.cache.web.chart.model.SimpleChartData;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.ResourceService;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.AppEmailUtil;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.util.Page;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.servlet.ModelAndView;\nimport redis.clients.jedis.commands.ProtocolCommand;\nimport redis.clients.jedis.util.SafeEncoder;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.sql.Timestamp;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 应用统计相关\n *\n * @author leifu\n * @Time 2014年8月31日\n */\n@Controller\n@RequestMapping(\"/admin/app\")\npublic class AppController extends BaseController {\n    private Logger logger = LoggerFactory.getLogger(AppController.class);\n\n    @Resource(name = \"appStatsCenter\")\n    private AppStatsCenter appStatsCenter;\n\n    @Resource(name = \"appEmailUtil\")\n    private AppEmailUtil appEmailUtil;\n\n    @Resource(name = \"appDeployCenter\")\n    private AppDeployCenter appDeployCenter;\n\n    @Resource(name = \"instanceStatsCenter\")\n    private InstanceStatsCenter instanceStatsCenter;\n\n    @Resource(name = \"appDailyDataCenter\")\n    private AppDailyDataCenter appDailyDataCenter;\n\n    @Resource(name = \"instanceAlertConfigService\")\n    private InstanceAlertConfigService instanceAlertConfigService;\n\n    @Autowired\n    private ResourceService resourceService;\n\n    @Resource\n    private MachineCenter machineCenter;\n\n    @Resource\n    private UserService userService;\n    @Autowired\n    private AppUserDao appUserDao;\n\n    @Autowired\n    protected RedisCenter redisCenter;\n\n    /**\n     * 初始化贡献者页面\n     *\n     * @return\n     */\n    @RequestMapping(\"/initBecomeContributor\")\n    public ModelAndView doInitBecomeContributor(HttpServletRequest request,\n                                                HttpServletResponse response, Model model) {\n        model.addAttribute(\"currentUser\", getUserInfo(request));\n        return new ModelAndView(\"app/initBecomeContributor\");\n    }\n\n    /**\n     * 成为cachecloud贡献者\n     *\n     * @param groupName   项目组\n     * @param applyReason 申请理由\n     * @return\n     */\n    @RequestMapping(\"/addBecomeContributor\")\n    public ModelAndView doAddBecomeContributor(HttpServletRequest request,\n                                               HttpServletResponse response, Model model, String groupName, String applyReason) {\n        appEmailUtil.noticeBecomeContributor(groupName, applyReason, getUserInfo(request));\n        model.addAttribute(\"success\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 单个应用首页\n     *\n     * @param appId\n     * @param tabTag       标签名\n     * @param firstCommand 第一条命令\n     * @return\n     * @throws ParseException\n     */\n    @RequestMapping(\"/index\")\n    public ModelAndView index(HttpServletRequest request,\n                              HttpServletResponse response, Model model, Long appId, String tabTag, String firstCommand, String condition)\n            throws ParseException {\n        // 如果应用id为空，取第一个应用id\n        if (appId == null) {\n            return new ModelAndView(\"redirect:/admin/app/list\");\n        }\n\n        // 日期转换\n        String searchDate = request.getParameter(\"searchDate\");\n\n        String startDateParam = request.getParameter(\"startDate\");\n        String endDateParam = request.getParameter(\"endDate\");\n        if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n            Date startDate = new Date();\n            startDateParam = DateUtil.formatDate(startDate, \"yyyy-MM-dd\");\n            endDateParam = DateUtil.formatDate(DateUtils.addDays(startDate, 1), \"yyyy-MM-dd\");\n        }\n\n        //慢查询\n        String slowLogStartDateParam = request.getParameter(\"slowLogStartDate\");\n        String slowLogEndDateParam = request.getParameter(\"slowLogEndDate\");\n        if (StringUtils.isBlank(slowLogStartDateParam) || StringUtils.isBlank(slowLogEndDateParam)) {\n            Date startDate = new Date();\n            slowLogStartDateParam = DateUtil.formatDate(startDate, \"yyyy-MM-dd\");\n            slowLogEndDateParam = DateUtil.formatDate(DateUtils.addDays(startDate, 1), \"yyyy-MM-dd\");\n        }\n\n        //日报\n        String dailyDateParam = request.getParameter(\"dailyDate\");\n        if (StringUtils.isBlank(dailyDateParam)) {\n            dailyDateParam = DateUtil.formatDate(DateUtils.addDays(new Date(), -1), \"yyyy-MM-dd\");\n        }\n\n        int conditionInt = StringUtils.isEmpty(condition) ? 0 : Integer.parseInt(condition);\n\n        model.addAttribute(\"condition\", conditionInt);\n\n        model.addAttribute(\"startDate\", startDateParam);\n        model.addAttribute(\"endDate\", endDateParam);\n        model.addAttribute(\"searchDate\", searchDate);\n        model.addAttribute(\"slowLogStartDate\", slowLogStartDateParam);\n        model.addAttribute(\"slowLogEndDate\", slowLogEndDateParam);\n        model.addAttribute(\"dailyDate\", dailyDateParam);\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"tabTag\", tabTag);\n        model.addAttribute(\"firstCommand\", firstCommand);\n\n        return new ModelAndView(\"app/userAppsIndex\");\n\n    }\n\n    /**\n     * 应用统计相关\n     */\n    @RequestMapping(\"/stat\")\n    public ModelAndView appStat(HttpServletRequest request,\n                                HttpServletResponse response, Model model, Long appId) throws ParseException {\n        AppUser appUser = getUserInfo(request);\n\n        // 1.获取app的VO\n        AppDetailVO appDetail = appStatsCenter.getAppDetail(appId);\n        model.addAttribute(\"appDetail\", appDetail);\n\n        // 2. 时间\n        TimeBetween timeBetween = getTimeBetween(request, model, \"startDate\", \"endDate\");\n        long beginTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n\n        // 3.是否超过1天\n        if (endTime - beginTime > TimeUnit.DAYS.toMillis(1)) {\n            model.addAttribute(\"betweenOneDay\", 0);\n        } else {\n            model.addAttribute(\"betweenOneDay\", 1);\n        }\n\n        // 4. top5命令\n        // 对于top5 只展示搜索当天的\n        TimeBetween oneDaytimeBetween = getTimeBetweenOneDay(request,\"startDate\");\n        long top5BeginTime = oneDaytimeBetween.getStartTime();\n        long top5EndTime = oneDaytimeBetween.getEndTime();\n        List<AppCommandStats> top5Commands = appStatsCenter\n                .getTopLimitAppCommandStatsList(appId, top5BeginTime, top5EndTime, 5);\n        model.addAttribute(\"top5Commands\", top5Commands);\n\n        // 5.峰值\n        List<AppCommandStats> top5ClimaxList = new ArrayList<AppCommandStats>();\n        if (CollectionUtils.isNotEmpty(top5Commands)) {\n            for (AppCommandStats appCommandStats : top5Commands) {\n                AppCommandStats temp = appStatsCenter\n                        .getCommandClimax(appId, top5BeginTime, top5EndTime, appCommandStats.getCommandName());\n                if (temp != null) {\n                    top5ClimaxList.add(temp);\n                }\n            }\n        }\n        if(CollectionUtils.isNotEmpty(top5ClimaxList)){\n            top5ClimaxList.sort(Comparator.comparingLong(AppCommandStats::getCommandCount).reversed());\n        }\n        model.addAttribute(\"top5ClimaxList\", top5ClimaxList);\n        if (appDetail != null && StringUtils.isNotBlank(appDetail.getAppDesc().getAppPassword())) {\n            model.addAttribute(\"md5password\", appDetail.getAppDesc().getAppPassword());\n        }\n        // 7. 当天命令调用总量\n        Long commandCount = appStatsCenter\n                .getAppCommandCount(appId, oneDaytimeBetween.getStartTime(), oneDaytimeBetween.getEndTime());\n        model.addAttribute(\"commandCount\", commandCount);\n        // 8. 是否为管理员\n        boolean isMaster = false;\n        if(appUser != null && AppUserTypeEnum.ADMIN_USER.value().equals(appUser.getType())){\n            isMaster = true;\n        }\n        model.addAttribute(\"isMaster\", isMaster);\n\n        model.addAttribute(\"appId\", appId);\n        return new ModelAndView(\"app/appStat\");\n    }\n\n\n    /**\n     * 命令曲线\n     *\n     * @param firstCommand 第一条命令\n     */\n    @RequestMapping(\"/commandAnalysis\")\n    public ModelAndView appCommandAnalysis(HttpServletRequest request,\n                                           HttpServletResponse response, Model model, Long appId, String firstCommand) throws ParseException {\n        // 1.获取app的VO\n        AppDetailVO appDetail = appStatsCenter.getAppDetail(appId);\n        model.addAttribute(\"appDetail\", appDetail);\n\n        // 2.返回日期\n        TimeBetween timeBetween = getTimeBetween(request, model, \"startDate\", \"endDate\");\n\n        // 3.是否超过1天\n        if(timeBetween.getStartDate() != null && timeBetween.getEndDate() != null\n                && (timeBetween.getEndDate().getTime() - timeBetween.getStartDate().getTime() > TimeUnit.DAYS.toMillis(1))){\n            model.addAttribute(\"betweenOneDay\", 0);\n        } else {\n            model.addAttribute(\"betweenOneDay\", 1);\n        }\n\n        // 4.获取top命令\n        List<AppCommandStats> allCommands = appStatsCenter\n                .getTopLimitAppCommandStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime(), 20);\n        model.addAttribute(\"allCommands\", allCommands);\n        if (StringUtils.isBlank(firstCommand) && CollectionUtils.isNotEmpty(allCommands)) {\n            model.addAttribute(\"firstCommand\", allCommands.get(0).getCommandName());\n        } else {\n            model.addAttribute(\"firstCommand\", firstCommand);\n        }\n        model.addAttribute(\"appId\", appId);\n        // 返回标签名\n        return new ModelAndView(\"app/appCommandAnalysis\");\n    }\n\n    /**\n     * 应用故障\n     */\n    @RequestMapping(\"/fault\")\n    public ModelAndView appFault(HttpServletRequest request,\n                                 HttpServletResponse response, Model model) {\n\n        return new ModelAndView(\"app/appFault\");\n    }\n\n    /**\n     * 应用拓扑图\n     *\n     * @param appId\n     * @return\n     */\n    @RequestMapping(\"/topology\")\n    public ModelAndView statTopology(HttpServletRequest request,\n                                     HttpServletResponse response, Long appId, Model model) {\n        AppUser userInfo = this.getUserInfo(request);\n        int isAdmin = 0;\n        if(userInfo.getType() == AppUserTypeEnum.ADMIN_USER.value()){\n            isAdmin = 1;\n        }\n        model.addAttribute(\"isAdmin\", isAdmin);\n        //应用信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        //实例相关信息(包含统计)\n        fillAppInstanceStats(appId, model);\n        return new ModelAndView(\"app/appTopology\");\n    }\n\n    @RequestMapping(\"/appDesc\")\n    public ModelAndView appDesc(HttpServletRequest request,\n                                HttpServletResponse response, Long appId, Model model) {\n        JSONObject json = new JSONObject();\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc == null) {\n            json.put(\"status\", String.valueOf(SuccessEnum.FAIL.value()));\n        } else {\n            json.put(\"appDesc\", appDesc);\n            json.put(\"status\", String.valueOf(SuccessEnum.SUCCESS.value()));\n        }\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n    /**\n     * 应用机器拓扑图\n     *\n     * @param appId\n     * @return\n     */\n    @RequestMapping(\"/machineInstancesTopology\")\n    public ModelAndView machineInstancesTopology(HttpServletRequest request,\n                                                 HttpServletResponse response, Long appId, Model model) {\n        //应用信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        //拓扑\n        fillAppMachineInstanceTopology(appId, model);\n        return new ModelAndView(\"app/appMachineInstancesTopology\");\n    }\n\n    /**\n     * 应用基本信息\n     *\n     * @param appId 应用id\n     */\n    @RequestMapping(\"/detail\")\n    public ModelAndView appDetail(HttpServletRequest request,\n                                  HttpServletResponse response, Model model, Long appId) {\n        AppUser appUser = getUserInfo(request);\n        // 获取应用vo\n        AppDetailVO appDetail = appStatsCenter.getAppDetail(appId);\n        model.addAttribute(\"appDetail\", appDetail);\n        //增加全局监控\n        model.addAttribute(\"instanceAlertAllList\",\n                instanceAlertConfigService.getByType(InstanceAlertTypeEnum.ALL_ALERT.getValue()));\n        model.addAttribute(\"instanceAlertCheckCycleEnumList\",\n                InstanceAlertCheckCycleEnum.getInstanceAlertCheckCycleEnumList());\n        model.addAttribute(\"instanceAlertCompareTypeEnumList\",\n                InstanceAlertCompareTypeEnum.getInstanceAlertCompareTypeEnumList());\n        if (appDetail != null && !StringUtils.isEmpty(appDetail.getAppDesc().getAppPassword())) {\n            model.addAttribute(\"password\", appDetail.getAppDesc().getAppPassword());\n        } else {\n            model.addAttribute(\"password\", \"\");\n        }\n        // 用户管理权限\n        List<AppUser> userList = userService.getAllUser();\n        Map<Long, AppUser> userMap = userList.stream().collect(Collectors.toMap(AppUser::getId, Function.identity()));\n        model.addAttribute(\"userMap\", userMap);\n        // 业务组\n        List<AppBiz> bizList = userService.getBizList();\n        model.addAttribute(\"bizList\", bizList);\n        Boolean hasAuth = false;\n        String officer = appDetail.getAppDesc().getOfficer();\n        List<String> officers = new ArrayList<String>();\n        if (!StringUtils.isEmpty(officer)) {\n            officers.addAll(Arrays.asList(officer.split(\",\")));\n        }\n        if (appUser != null &&\n                (appUser.getType() == AppUserTypeEnum.ADMIN_USER.value() || officers.contains(String.valueOf(appUser.getId())))) {\n            hasAuth = true;\n        }\n        model.addAttribute(\"hasAuth\", hasAuth);\n\n        return new ModelAndView(\"app/appDetail\");\n    }\n\n    /**\n     * 应用客户端连接\n     *\n     * @param request\n     * @param response\n     * @param model\n     * @param appId\n     * @return\n     */\n    @RequestMapping(\"/clientList\")\n    public ModelAndView clientList(HttpServletRequest request,\n                                   HttpServletResponse response,\n                                   Model model,\n                                   Long appId, int condition) {\n        //应用信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n\n        model.addAttribute(\"condition\", condition);\n\n        List<InstanceInfo> instanceInfoList = appService.getAppOnlineInstanceInfo(appId);\n        Map<Integer, String> instanceMap = instanceInfoList.stream().collect(Collectors.toMap(\n                InstanceInfo::getId,\n                InstanceInfo::getHostPort));\n        model.addAttribute(\"instanceMap\", instanceMap);\n\n        List<Map<String, Object>> addrInstanceList = redisCenter.getAppClientList(appId, condition);\n\n        model.addAttribute(\"addrInstanceList\", addrInstanceList);\n\n        return new ModelAndView(\"app/appClientList\");\n    }\n\n    /**\n     * 获取某个命令时间分布图\n     *\n     * @param appId 应用id\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getCommandStats\")\n    public ModelAndView getCommandStats(HttpServletRequest request,\n                                        HttpServletResponse response, Model model, Long appId) throws ParseException {\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n        long beginTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        // 命令参数\n        String commandName = request.getParameter(\"commandName\");\n        List<AppCommandStats> appCommandStatsList;\n        if (StringUtils.isNotBlank(commandName)) {\n            appCommandStatsList = appStatsCenter.getCommandStatsList(appId, beginTime, endTime, commandName);\n        } else {\n            appCommandStatsList = appStatsCenter.getCommandStatsList(appId, beginTime, endTime);\n        }\n        String result = assembleJson(appCommandStatsList);\n        write(response, result);\n        return null;\n    }\n\n    /**\n     * 获取某个命令时间分布图\n     *\n     * @param appId 应用id\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getMutiDatesCommandStats\")\n    public ModelAndView getMutiDatesCommandStats(HttpServletRequest request,\n                                                 HttpServletResponse response, Model model, Long appId) throws ParseException {\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n        // 命令参数\n        String commandName = request.getParameter(\"commandName\");\n        List<AppCommandStats> appCommandStatsList;\n        if (StringUtils.isNotBlank(commandName)) {\n            appCommandStatsList = appStatsCenter\n                    .getCommandStatsListV2(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                            TimeDimensionalityEnum.MINUTE, commandName);\n        } else {\n            appCommandStatsList = appStatsCenter\n                    .getCommandStatsListV2(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                            TimeDimensionalityEnum.MINUTE);\n        }\n        String result = assembleMutilDateAppCommandJsonMinute(appCommandStatsList, timeBetween.getStartDate(),\n                timeBetween.getEndDate());\n        model.addAttribute(\"data\", result);\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 获取命中率、丢失率等分布\n     *\n     * @param appId    应用id\n     * @param statName 统计项(hit,miss等)\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getAppStats\")\n    public ModelAndView getAppStats(HttpServletRequest request,\n                                    HttpServletResponse response, Model model, Long appId,\n                                    String statName) throws ParseException {\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n        List<AppStats> appStats = appStatsCenter\n                .getAppStatsListByMinuteTime(appId, timeBetween.getStartTime(), timeBetween.getEndTime());\n        String result = assembleAppStatsJson(appStats, statName);\n        write(response, result);\n        return null;\n    }\n\n    /**\n     * 多命令\n     *\n     * @param appId\n     * @return\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getMutiStatAppStats\")\n    public ModelAndView getMutiStatAppStats(HttpServletRequest request,\n                                            HttpServletResponse response, Model model, Long appId) throws ParseException {\n        String statNames = request.getParameter(\"statName\");\n        List<String> statNameList = Arrays.asList(statNames.split(ConstUtils.COMMA));\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n        List<AppStats> appStats = appStatsCenter\n                .getAppStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                        TimeDimensionalityEnum.MINUTE);\n        String result = assembleMutiStatAppStatsJsonMinute(appStats, statNameList, timeBetween.getStartDate());\n        model.addAttribute(\"data\", result);\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 获取命中率、丢失率等分布\n     *\n     * @param appId    应用id\n     * @param statName 统计项(hit,miss等)\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getMutiDatesAppStats\")\n    public ModelAndView getMutiDatesAppStats(HttpServletRequest request,\n                                             HttpServletResponse response, Model model, Long appId,\n                                             String statName, Integer addDay) throws ParseException {\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n        List<AppStats> appStats = appStatsCenter\n                .getAppStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                        TimeDimensionalityEnum.MINUTE);\n        String result = assembleMutilDateAppStatsJsonMinute(appStats, statName, timeBetween.getStartDate(),\n                timeBetween.getEndDate());\n        model.addAttribute(\"data\", result);\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 跳转到获取命中率分布页面\n     *\n     * @param appId    应用id\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getAppHitRatioInfo\")\n    public ModelAndView getMutiDatesAppHitRatioStats(HttpServletRequest request,\n                                                     HttpServletResponse response, Model model, Long appId) throws ParseException {\n\n        Date yesterDay = DateUtils.addDays(new Date(), -1);\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"searchDate\", DateUtil.formatDate(yesterDay, \"yyyy-MM-dd\"));\n        return new ModelAndView(\"app/appHitRatio\");\n    }\n\n    /**\n     * 获取命中率分布\n     *\n     * @param appId    应用id\n     * @param statName 统计项(hit,miss等)\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getMutiDatesAppHitRatioStats\")\n    public ModelAndView getMutiDatesAppHitRatioStats(HttpServletRequest request,\n                                             HttpServletResponse response, Model model, Long appId,\n                                             String statName) throws ParseException {\n        String endDateParam = request.getParameter(\"endDate\");\n        String startDateParam = request.getParameter(\"startDate\");\n        Date endDate = DateUtil.parseYYYY_MM_dd(endDateParam);\n        Date startDate = null;\n        if (StringUtils.isBlank(startDateParam)) {\n            startDate = DateUtils.addDays(endDate, -30);\n        } else {\n            startDate = DateUtil.parseYYYY_MM_dd(startDateParam);\n        }\n        long beginTime = NumberUtils.toLong(DateUtil.formatYYYYMMdd(startDate));\n        long endTime = NumberUtils.toLong(DateUtil.formatYYYYMMdd(endDate));\n        List<AppDailyData> appDailyDatas = appStatsCenter\n                .getAppHitRatioList(appId, beginTime, endTime);\n        String result = assembleMutilDateAppHitRatio(appDailyDatas, statName);\n        model.addAttribute(\"data\", result);\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的统计信息\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/appInstanceNetStat\")\n    public ModelAndView appInstanceNetStat(HttpServletRequest request, HttpServletResponse response, Model model,\n                                           Long appId) throws ParseException {\n        // 应用基本信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        model.addAttribute(\"appId\", appId);\n\n        // 日期格式转换\n        getTimeBetween(request, model, \"startDate\", \"endDate\");\n\n        return new ModelAndView(\"app/appInstanceNetStat\");\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的CPU统计信息\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/appInstanceCpuStat\")\n    public ModelAndView appInstanceCpuStat(HttpServletRequest request, Model model,\n                                           Long appId) throws ParseException {\n        // 应用基本信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        model.addAttribute(\"appId\", appId);\n\n        // 日期格式转换\n        getTimeBetween(request, model, \"startDate\", \"endDate\");\n\n        return new ModelAndView(\"app/appInstanceCpuStat\");\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的统计信息\n     *\n     * @param appId 应用流量\n     */\n    @RequestMapping(\"/getAppInstancesCpuStat\")\n    public void getAppInstancesCpuStat(HttpServletRequest request, HttpServletResponse response, Model model,\n                                       Long appId) throws ParseException {\n        //时间转换\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n\n        //缩减字段\n        String cpuSysCommand = \"used_cpu_sys\";\n        String cpuUserCommand = \"used_cpu_user\";\n        String cpuSysChildCommand = \"used_cpu_sys_children\";\n        String cpuUserChildCommand = \"used_cpu_user_children\";\n        Map<String, String> commandMap = Maps.newHashMap();\n        commandMap.put(cpuSysCommand, \"cs\");\n        commandMap.put(cpuUserCommand, \"cu\");\n        commandMap.put(cpuSysChildCommand, \"cs_child\");\n        commandMap.put(cpuUserChildCommand, \"cu_child\");\n\n        //获取应用下所有实例网络流量统计\n        Map<Integer, Map<String, List<InstanceCommandStats>>> appInstancesNetStat = instanceStatsCenter\n                .getStandardStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                        Arrays.asList(cpuSysCommand, cpuUserCommand, cpuSysChildCommand, cpuUserChildCommand));\n\n        //解析成json数组\n        List<Map<String, Object>> appInstancesNetStatList = new ArrayList<Map<String, Object>>();\n        for (Entry<Integer, Map<String, List<InstanceCommandStats>>> entry : appInstancesNetStat.entrySet()) {\n            Integer instanceId = entry.getKey();\n\n            //实例基本信息\n            Map<String, Object> instanceStatMap = new HashMap<String, Object>();\n            instanceStatMap.put(\"instanceId\", instanceId);\n            InstanceInfo instanceInfo = instanceStatsCenter.getInstanceInfo(instanceId);\n            instanceStatMap.put(\"instanceInfo\", instanceInfo.getIp() + \":\" + instanceInfo.getPort());\n\n            //每个实例的统计信息\n            List<Map<String, Object>> instanceCpuStatList = new ArrayList<Map<String, Object>>();\n            instanceStatMap.put(\"instanceCpuStatMapList\", instanceCpuStatList);\n            appInstancesNetStatList.add(instanceStatMap);\n\n            //记录输入和输出流量\n            Map<String, List<InstanceCommandStats>> map = entry.getValue();\n            List<InstanceCommandStats> instanceCommandStatsList = new ArrayList<InstanceCommandStats>();\n            instanceCommandStatsList.addAll(map.get(cpuSysCommand));\n            instanceCommandStatsList.addAll(map.get(cpuUserCommand));\n            instanceCommandStatsList.addAll(map.get(cpuSysChildCommand));\n            instanceCommandStatsList.addAll(map.get(cpuUserChildCommand));\n\n            Map<Long, Map<String, Object>> total = new HashMap<Long, Map<String, Object>>();\n            for (InstanceCommandStats instanceCommandStat : instanceCommandStatsList) {\n                //用timestamp作为key,保证输入和输出流量在一个Map统计里\n                long timestamp = instanceCommandStat.getTimeStamp();\n                long commandCount = instanceCommandStat.getCommandCount();\n                String command = instanceCommandStat.getCommandName();\n                //精简字段\n                command = commandMap.get(command);\n                if (total.containsKey(timestamp)) {\n                    Map<String, Object> tmpMap = total.get(timestamp);\n                    tmpMap.put(command, commandCount);\n                } else {\n                    Map<String, Object> tmpMap = new HashMap<String, Object>();\n                    tmpMap.put(\"t\", timestamp);\n                    tmpMap.put(command, commandCount);\n                    total.put(timestamp, tmpMap);\n                    instanceCpuStatList.add(tmpMap);\n                }\n            }\n        }\n\n        String result = JSONObject.toJSONString(appInstancesNetStatList);\n        write(response, result);\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的CPU统计信息\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/appInstanceMemFragRatioStat\")\n    public ModelAndView appInstanceMemFragRatioStat(HttpServletRequest request, Model model,\n                                                    Long appId) throws ParseException {\n        // 应用基本信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        model.addAttribute(\"appId\", appId);\n\n        // 日期格式转换\n        getTimeBetween(request, model, \"startDate\", \"endDate\");\n\n        return new ModelAndView(\"app/appInstanceMemFragRatioStat\");\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的统计信息\n     *\n     * @param appId 应用流量\n     */\n    @RequestMapping(\"/getAppInstancesMemFragRatioStat\")\n    public void getAppInstancesMemFragRatioStat(HttpServletRequest request, HttpServletResponse response, Model model,\n                                                Long appId) throws ParseException {\n        //时间转换\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n\n        //缩减字段\n        String ratioCommand = \"mem_fragmentation_ratio\";\n        Map<String, String> commandMap = Maps.newHashMap();\n        commandMap.put(ratioCommand, \"ratio\");\n\n        long start = System.currentTimeMillis();\n        //获取应用下所有实例碎片率统计\n        Table<Integer, String, Map<String, List<InstanceMinuteStats>>> table = instanceStatsCenter\n                .getInstanceMinuteStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                        Arrays.asList(ratioCommand));\n        logger.warn(\"getInstanceMinuteStatsList cost:{} ms\", (System.currentTimeMillis() - start));\n\n        start = System.currentTimeMillis();\n\n        //解析成json数组\n        List<Map<String, Object>> appInstancesMemFragRatioStatList = new ArrayList<Map<String, Object>>();\n        for (Table.Cell<Integer, String, Map<String, List<InstanceMinuteStats>>> cell : table.cellSet()) {\n            Integer instanceId = cell.getRowKey();\n\n            //实例基本信息\n            Map<String, Object> instanceStatMap = new HashMap<String, Object>();\n            instanceStatMap.put(\"instanceId\", instanceId);\n            instanceStatMap.put(\"instanceInfo\", cell.getColumnKey());\n\n            //每个实例的统计信息\n            List<Map<String, Object>> instanceMemFragRatioStatList = new ArrayList<Map<String, Object>>();\n            instanceStatMap.put(\"instanceMemFragRatioStatMapList\", instanceMemFragRatioStatList);\n            appInstancesMemFragRatioStatList.add(instanceStatMap);\n\n            //记录碎片率\n            Map<String, List<InstanceMinuteStats>> map = cell.getValue();\n            List<InstanceMinuteStats> instanceCommandStatsList = new ArrayList<>();\n            instanceCommandStatsList.addAll(map.get(ratioCommand));\n\n\n            Map<Long, Map<String, Object>> total = new HashMap<Long, Map<String, Object>>();\n            for (InstanceMinuteStats instanceMinuteStats : instanceCommandStatsList) {\n                //用timestamp作为key,保证在一个Map统计里\n                long timestamp = instanceMinuteStats.getTimeStamp();\n                double memFragRatio = instanceMinuteStats.getMemFragmentationRatio();\n                String command = instanceMinuteStats.getCommandName();\n                //精简字段\n                command = commandMap.get(command);\n                if (total.containsKey(timestamp)) {\n                    Map<String, Object> tmpMap = total.get(timestamp);\n                    tmpMap.put(command, memFragRatio);\n                } else {\n                    Map<String, Object> tmpMap = new HashMap<String, Object>();\n                    tmpMap.put(\"t\", timestamp);\n                    tmpMap.put(command, memFragRatio);\n                    total.put(timestamp, tmpMap);\n                    instanceMemFragRatioStatList.add(tmpMap);\n                }\n            }\n        }\n\n        String result = JSONObject.toJSONString(appInstancesMemFragRatioStatList);\n        logger.warn(\"parse Json cost:{} ms\", (System.currentTimeMillis() - start));\n        write(response, result);\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的过期/淘汰键统计信息\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/appInstanceExpiredEvictedKeysStat\")\n    public ModelAndView appInstanceExpiredEvictedKeysStat(HttpServletRequest request, Model model,\n                                                          Long appId) throws ParseException {\n        // 应用基本信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n        model.addAttribute(\"appId\", appId);\n        // 日期格式转换\n        getTimeBetween(request, model, \"startDate\", \"endDate\");\n        return new ModelAndView(\"app/appInstanceExpiredEvictedKeysStat\");\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的过期/淘汰键统计信息\n     * @param appId 应用流量\n     */\n    @RequestMapping(\"/getAppInstancesExpiredEvictedKeysStat\")\n    public void getAppInstancesExpiredEvictedKeysStat(HttpServletRequest request, HttpServletResponse response, Model model,\n                                                      Long appId) throws ParseException {\n        //时间转换\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n\n        //缩减字段\n        String expiredKeysCommand = \"expired_keys\";\n        String evictedKeysCommand = \"evicted_keys\";\n        Map<String, String> commandMap = Maps.newHashMap();\n        commandMap.put(expiredKeysCommand, \"exkey\");\n        commandMap.put(evictedKeysCommand, \"evkey\");\n\n        long start = System.currentTimeMillis();\n        //获取应用下所有实例过期/淘汰键\n        Table<Integer, String, Map<String, List<InstanceMinuteStats>>> table = instanceStatsCenter\n                .getInstanceMinuteStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                        Arrays.asList(expiredKeysCommand, evictedKeysCommand));\n        logger.warn(\"getInstanceMinuteStatsList cost:{} ms\", (System.currentTimeMillis() - start));\n\n        start = System.currentTimeMillis();\n\n        //解析成json数组\n        List<Map<String, Object>> appInstancesExpiredEvictedKeysStatList = new ArrayList<Map<String, Object>>();\n        for (Table.Cell<Integer, String, Map<String, List<InstanceMinuteStats>>> cell : table.cellSet()) {\n            Integer instanceId = cell.getRowKey();\n\n            //实例基本信息\n            Map<String, Object> instanceStatMap = new HashMap<String, Object>();\n            instanceStatMap.put(\"instanceId\", instanceId);\n            instanceStatMap.put(\"instanceInfo\", cell.getColumnKey());\n\n            //每个实例的统计信息\n            List<Map<String, Object>> instanceExpiredEvictedKeysStatList = new ArrayList<Map<String, Object>>();\n            instanceStatMap.put(\"instanceExpiredEvictedKeysStatMapList\", instanceExpiredEvictedKeysStatList);\n            appInstancesExpiredEvictedKeysStatList.add(instanceStatMap);\n\n            //记录过期、淘汰键\n            Map<String, List<InstanceMinuteStats>> map = cell.getValue();\n            List<InstanceMinuteStats> instanceCommandStatsList = new ArrayList<>();\n            instanceCommandStatsList.addAll(map.get(expiredKeysCommand));\n            instanceCommandStatsList.addAll(map.get(evictedKeysCommand));\n\n            Map<Long, Map<String, Object>> total = new HashMap<Long, Map<String, Object>>();\n            for (InstanceMinuteStats instanceMinuteStats : instanceCommandStatsList) {\n                //用timestamp作为key,保证在一个Map统计里\n                long timestamp = instanceMinuteStats.getTimeStamp();\n                double memFragRatio = instanceMinuteStats.getMemFragmentationRatio();\n                String command = instanceMinuteStats.getCommandName();\n                //精简字段\n                command = commandMap.get(command);\n                if (total.containsKey(timestamp)) {\n                    Map<String, Object> tmpMap = total.get(timestamp);\n                    tmpMap.put(command, memFragRatio);\n                } else {\n                    Map<String, Object> tmpMap = new HashMap<String, Object>();\n                    tmpMap.put(\"t\", timestamp);\n                    tmpMap.put(command, memFragRatio);\n                    total.put(timestamp, tmpMap);\n                    instanceExpiredEvictedKeysStatList.add(tmpMap);\n                }\n            }\n        }\n\n        String result = JSONObject.toJSONString(appInstancesExpiredEvictedKeysStatList);\n        logger.warn(\"parse Json cost:{} ms\", (System.currentTimeMillis() - start));\n        write(response, result);\n    }\n\n    /**\n     * 获取指定时间内某个应用全部实例的统计信息\n     *\n     * @param appId 应用流量\n     */\n    @RequestMapping(\"/getAppInstancesNetStat\")\n    public ModelAndView getAppInstancesNetStat(HttpServletRequest request, HttpServletResponse response, Model model,\n                                               Long appId) throws ParseException {\n        //时间转换\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n\n        //缩减字段\n        String netInCommand = \"total_net_input_bytes\";\n        String netOutCommand = \"total_net_output_bytes\";\n        Map<String, String> commandMap = new HashMap<String, String>();\n        commandMap.put(netInCommand, \"i\");\n        commandMap.put(netOutCommand, \"o\");\n\n        //获取应用下所有实例网络流量统计\n        Map<Integer, Map<String, List<InstanceCommandStats>>> appInstancesNetStat = instanceStatsCenter\n                .getStandardStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime(),\n                        Arrays.asList(netInCommand, netOutCommand));\n\n        //解析成json数组\n        List<Map<String, Object>> appInstancesNetStatList = new ArrayList<Map<String, Object>>();\n        for (Entry<Integer, Map<String, List<InstanceCommandStats>>> entry : appInstancesNetStat.entrySet()) {\n            Integer instanceId = entry.getKey();\n\n            //实例基本信息\n            Map<String, Object> instanceStatMap = new HashMap<String, Object>();\n            instanceStatMap.put(\"instanceId\", instanceId);\n            InstanceInfo instanceInfo = instanceStatsCenter.getInstanceInfo(instanceId);\n            instanceStatMap.put(\"instanceInfo\", instanceInfo.getIp() + \":\" + instanceInfo.getPort());\n\n            //每个实例的统计信息\n            List<Map<String, Object>> instanceNetStatMapList = new ArrayList<Map<String, Object>>();\n            instanceStatMap.put(\"instanceNetStatMapList\", instanceNetStatMapList);\n            appInstancesNetStatList.add(instanceStatMap);\n\n            //记录输入和输出流量\n            Map<String, List<InstanceCommandStats>> map = entry.getValue();\n            List<InstanceCommandStats> instanceCommandStatsList = new ArrayList<InstanceCommandStats>();\n            instanceCommandStatsList.addAll(map.get(netInCommand));\n            instanceCommandStatsList.addAll(map.get(netOutCommand));\n\n            Map<Long, Map<String, Object>> total = new HashMap<Long, Map<String, Object>>();\n            for (InstanceCommandStats instanceCommandStat : instanceCommandStatsList) {\n                //用timestamp作为key,保证输入和输出流量在一个Map统计里\n                long timestamp = instanceCommandStat.getTimeStamp();\n                long commandCount = instanceCommandStat.getCommandCount();\n                String command = instanceCommandStat.getCommandName();\n                //精简字段\n                command = commandMap.get(command);\n                if (total.containsKey(timestamp)) {\n                    Map<String, Object> tmpMap = total.get(timestamp);\n                    tmpMap.put(command, commandCount);\n                } else {\n                    Map<String, Object> tmpMap = new HashMap<String, Object>();\n                    tmpMap.put(\"t\", timestamp);\n                    tmpMap.put(command, commandCount);\n                    total.put(timestamp, tmpMap);\n                    instanceNetStatMapList.add(tmpMap);\n                }\n            }\n        }\n\n        String result = JSONObject.toJSONString(appInstancesNetStatList);\n        write(response, result);\n        return null;\n    }\n\n    /**\n     * @param appId\n     * @throws ParseException\n     */\n    @RequestMapping(\"/getTop5Commands\")\n    public ModelAndView getAppTop5Commands(HttpServletRequest request,\n                                           HttpServletResponse response, Model model, Long appId) throws ParseException {\n        TimeBetween timeBetween = getTimeBetweenOneDay(request,\"startDate\");\n        List<AppCommandStats> appCommandStats = appStatsCenter\n                .getTop5AppCommandStatsList(appId, timeBetween.getStartTime(), timeBetween.getEndTime());\n        String result = assembleJson(appCommandStats);\n        write(response, result);\n        return null;\n    }\n\n    /**\n     * 应用各个命令分布情况\n     *\n     * @param appId 应用id\n     * @throws ParseException\n     */\n    @RequestMapping(\"/appCommandDistribute\")\n    public ModelAndView appCommandDistribute(HttpServletRequest request,\n                                             HttpServletResponse response, Model model, Long appId) throws ParseException {\n        TimeBetween timeBetween = getJsonTimeBetween(request);\n        List<AppCommandGroup> appCommandGroupList = appStatsCenter\n                .getAppCommandGroup(appId, timeBetween.getStartTime(), timeBetween.getEndTime());\n        String result = assembleGroupJson(appCommandGroupList);\n        write(response, result);\n        return null;\n    }\n\n    /**\n     * 应用列表\n     */\n    @RequestMapping(value = \"/list\")\n    public ModelAndView doAppList(HttpServletRequest request,\n                                  HttpServletResponse response, Model model, String appParam, AppSearch appSearch, String userId) {\n        // 1.获取该用户能够读取的应用列表,没有返回申请页面\n        AppUser currentUser = getUserInfo(request);\n        model.addAttribute(\"currentUser\", currentUser);\n        int userAppCount = appService.getUserAppCount(currentUser.getId());\n        if (userAppCount == 0 && !AppUserTypeEnum.ADMIN_USER.value().equals(currentUser.getType())) {\n            return new ModelAndView(\"redirect:/admin/app/init\");\n        }\n        // 2.0 默认只出运行中的\n        if (appSearch.getAppStatus() == null) {\n            appSearch.setAppStatus(AppStatusEnum.STATUS_PUBLISHED.getStatus());\n        }\n        // 2.1 查询指定时间客户端异常\n        if (!StringUtils.isEmpty(appParam)) {\n            if (StringUtils.isNumeric(appParam)) {\n                appSearch.setAppId(Long.parseLong(appParam));\n            } else {\n                appSearch.setAppName(appParam);\n            }\n        }\n        if (StringUtils.isNotEmpty(userId)) {\n            appSearch.setUserId(NumberUtils.toLong(userId));\n        }\n        // 2.2 分页相关\n        int totalCount = appService.getAppDescCount(currentUser, appSearch);\n        int pageNo = NumberUtils.toInt(request.getParameter(\"pageNo\"), 1);\n        int pageSize = NumberUtils.toInt(request.getParameter(\"pageSize\"), 10);\n        Page page = new Page(pageNo, pageSize, totalCount);\n        model.addAttribute(\"page\", page);\n        appSearch.setPage(page);\n\n        List<AppDesc> apps = appService.getAppDescList(currentUser, appSearch);\n        // 2.3 应用列表\n        List<AppDetailVO> appDetailList = new ArrayList<AppDetailVO>();\n        model.addAttribute(\"appDetailList\", appDetailList);\n\n        // 3.Redis版本信息\n        List<SystemResource> resourcelist = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\n\n        // 4. 全局统计\n        long totalApplyMem = 0;\n        long totalUsedMem = 0;\n        long totalApps = 0;\n        if (apps != null && apps.size() > 0) {\n            for (AppDesc appDesc : apps) {\n                AppDetailVO appDetail = appStatsCenter.getAppDetail(appDesc.getAppId());\n                appDetailList.add(appDetail);\n                totalApplyMem += appDetail.getMem();\n                totalUsedMem += appDetail.getMemUsePercent() * appDetail.getMem() / 100.0;\n                totalApps++;\n                appDetail.getAppDesc().setBizGroup(appDesc.getBizGroup());\n            }\n        }\n\n        List<AppUser> userList = userService.getAllUser();\n        List<AppBiz> bizList = userService.getBizList();\n\n        model.addAttribute(\"userList\", userList);\n        model.addAttribute(\"bizList\", bizList);\n        model.addAttribute(\"appParam\", appParam);\n        model.addAttribute(\"resourcelist\", resourcelist);\n        model.addAttribute(\"totalApps\", totalApps);\n        model.addAttribute(\"totalApplyMem\", totalApplyMem);\n        model.addAttribute(\"totalUsedMem\", totalUsedMem);\n        return new ModelAndView(\"app/appList\");\n    }\n\n    /**\n     * 应用列表\n     */\n    @RequestMapping(value = \"/listStats\")\n    @ResponseBody\n    public Map<String, Object> doAppListStats(HttpServletRequest request) {\n        Map<String, Object> resultMap = new HashMap<>();\n        // 1.获取该用户能够读取的应用列表,没有返回申请页面\n        AppUser currentUser = getUserInfo(request);\n        // 4. 全局统计\n        Calendar instance = Calendar.getInstance();\n        Date now = instance.getTime();\n        instance.add(Calendar.DATE, -5);\n        Date startTime = instance.getTime();\n        List<AppMonitorStatisticsResult> monitorStatisticsResultList =\n                appService.getAppMonitorStatistics(currentUser.getId(),\n                        AppUserTypeEnum.ADMIN_USER.value().equals(currentUser.getType()),\n                        DateUtil.formatYYYY_MM_dd(startTime), DateUtil.formatYYYY_MM_dd(now));\n\n        Map<String, Object> capacityMap = appService.getAppCapacityStats(currentUser.getId(),\n                AppUserTypeEnum.ADMIN_USER.value().equals(currentUser.getType()));\n        resultMap.put(\"monitorList\", monitorStatisticsResultList);\n        resultMap.put(\"capacityMap\", capacityMap);\n        return resultMap;\n    }\n\n    /**\n     * 初始化应用申请\n     */\n    @RequestMapping(value = \"/init\")\n    public ModelAndView doAppInit(HttpServletRequest request,\n                                  HttpServletResponse response, Model model) {\n        List<AppUser> userList = userService.getAllUser();\n        List<MachineRoom> roomList = machineCenter.getEffectiveRoom();\n        List<SystemResource> versionList = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\n        versionList = versionList.stream().filter(systemResource -> (systemResource.getOrderNum() >= 100)).collect(Collectors.toList());\n        //获取插件信息\n        model.addAttribute(\"userList\", userList);\n        model.addAttribute(\"roomList\", roomList);\n        model.addAttribute(\"versionList\", versionList);\n        model.addAttribute(\"policyList\", AppDescEnum.MaxmemoryPolicyType.getAll());\n\n        return new ModelAndView(\"app/jobIndex/appInitIndex\");\n    }\n\n    @RequestMapping(value = \"/import\")\n    public ModelAndView doAppImport(HttpServletRequest request,\n                                    HttpServletResponse response, Model model) {\n        List<AppUser> userList = userService.getAllUser();\n        List<MachineRoom> roomList = machineCenter.getEffectiveRoom();\n        List<SystemResource> versionList = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\n        model.addAttribute(\"userList\", userList);\n        model.addAttribute(\"roomList\", roomList);\n        model.addAttribute(\"versionList\", versionList);\n\n        return new ModelAndView(\"app/jobIndex/appImportIndex\");\n    }\n\n    @RequestMapping(value = \"/jobs\")\n    public ModelAndView doAppJobs(HttpServletRequest request,\n                                  HttpServletResponse response, Model model, Long appId,\n                                  Integer status, Integer type) {\n        model.addAttribute(\"appId\", appId);\n        AppUser currentUser = getUserInfo(request);\n        List<AppAudit> jobList = appService.getAppAudits(4, type, null, currentUser.getId(), null);\n        Map<String, Object> statusStatisMap = appService.getStatisticGroupByStatus(currentUser.getId(), null, null, null);\n        Map<String, Object> typeStatisMap = appService.getStatisticGroupByType(currentUser.getId(), null, null, null);\n        List<AppUser> adminList = appUserDao.getAdminList();\n        model.addAttribute(\"adminMap\", adminList.stream().collect(Collectors.toMap(AppUser::getId, Function.identity())));\n\n        AppAuditType[] appAuditTypes = AppAuditType.values();\n        model.addAttribute(\"appAuditTypeMap\", Arrays.stream(appAuditTypes).collect(Collectors.toMap(AppAuditType::getValue, Function.identity())));\n\n        model.addAttribute(\"statusStatisMap\", statusStatisMap);\n        model.addAttribute(\"typeStatisMap\", typeStatisMap);\n        model.addAttribute(\"jobList\", jobList);\n        model.addAttribute(\"status\", status);\n        model.addAttribute(\"type\", type);\n        return new ModelAndView(\"app/jobIndex/index\");\n    }\n\n\n    @RequestMapping(value = \"/appKeyAnalysis\")\n    public ModelAndView doKeyAnalysis(HttpServletRequest request,\n                                      HttpServletResponse response, Model model, Long appId,\n                                      Integer status, Integer type) {\n        model.addAttribute(\"appId\", appId);\n        if (appId != null) {\n            List<InstanceInfo> instanceInfos = appService.getAppOnlineInstanceInfo(appId);\n            model.addAttribute(\"appInstanceList\", instanceInfos);\n        }\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appKeyAnalysisIndex\");\n    }\n\n\n    @RequestMapping(value = \"/appScale\")\n    public ModelAndView doAppScale(HttpServletRequest request,\n                                   HttpServletResponse response, Model model, Long appId) {\n        model.addAttribute(\"appId\", appId);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appScaleIndex\");\n    }\n\n\n    @RequestMapping(value = \"/appDiagnostic\")\n    public ModelAndView doAppDiagnostic(HttpServletRequest request,\n                                        HttpServletResponse response, Model model, Long appId) {\n        model.addAttribute(\"appId\", appId);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        Map<Integer, String> diagnosticTypeMap = new TreeMap<>();\n        for (DiagnosticTypeEnum diagnosticType : DiagnosticTypeEnum.values()) {\n            diagnosticTypeMap.put(diagnosticType.getType(), diagnosticType.getDesc() + \": \" + diagnosticType.getMore());\n        }\n        model.addAttribute(\"diagnosticTypeMap\", diagnosticTypeMap);\n\n        return new ModelAndView(\"app/jobIndex/appDiagnosticIndex\");\n    }\n\n    @RequestMapping(value = \"/appDel\")\n    public ModelAndView doAppDel(HttpServletRequest request,\n                                 HttpServletResponse response, Model model, Long appId) {\n        model.addAttribute(\"appId\", appId);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appCleanIndex\");\n    }\n\n    @RequestMapping(value = \"/appOffline\")\n    public ModelAndView doAppOffline(HttpServletRequest request,\n                                     HttpServletResponse response, Model model, Long appId) {\n        model.addAttribute(\"appId\", appId);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appOfflineIndex\");\n    }\n\n\n    @RequestMapping(value = \"/appDataMigrate\")\n    public ModelAndView doAppDataMigrate(HttpServletRequest request,\n                                         HttpServletResponse response, Model model, Long appId) {\n        model.addAttribute(\"appId\", appId);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appDataMigrateIndex\");\n    }\n\n    @RequestMapping(value = \"/appConfig\")\n    public ModelAndView doAppConfig(HttpServletRequest request,\n                                    HttpServletResponse response, Model model, Long appId, Long instanceId) {\n        model.addAttribute(\"appId\", appId);\n        int first_instanceId = -1;\n        if (appId != null) {\n            List<InstanceInfo> instanceInfos = appService.getAppOnlineInstanceInfo(appId);\n            model.addAttribute(\"appInstanceList\", instanceInfos);\n            first_instanceId = CollectionUtils.isNotEmpty(instanceInfos) && instanceInfos.size() > 0 ? instanceInfos.get(0).getId() : -1;\n        }\n        if (instanceId != null) {\n            model.addAttribute(\"instanceId\", instanceId);\n            first_instanceId = instanceId.intValue();\n        }\n        Map<String, String> redisConfigMap = redisCenter.getRedisConfigList(first_instanceId);\n        model.addAttribute(\"redisConfigMap\", redisConfigMap);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appConfigIndex\");\n    }\n\n    @RequestMapping(\"/redisConfig\")\n    public ModelAndView redisConfig(HttpServletRequest request,\n                                    HttpServletResponse response, Long appId, Integer instanceId, Model model) {\n        JSONObject json = new JSONObject();\n        if (instanceId == null) {\n            List<InstanceInfo> instanceInfos = appService.getAppOnlineInstanceInfo(appId);\n            instanceId = CollectionUtils.isNotEmpty(instanceInfos) && instanceInfos.size() > 0 ? instanceInfos.get(0).getId() : -1;\n        }\n        Map<String, String> redisConfigMap = redisCenter.getRedisConfigList(instanceId.intValue());\n\n\n        if (MapUtils.isEmpty(redisConfigMap)) {\n            json.put(\"status\", String.valueOf(SuccessEnum.FAIL.value()));\n        } else {\n            json.put(\"redisConfigMap\", redisConfigMap);\n            json.put(\"status\", String.valueOf(SuccessEnum.SUCCESS.value()));\n        }\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n    @RequestMapping(\"/redisCommand\")\n    public ModelAndView redisCommand(HttpServletRequest request,\n                                    HttpServletResponse response, Long appId, Integer instanceId, Model model) {\n        JSONObject json = new JSONObject();\n        if (instanceId == null) {\n            List<InstanceInfo> instanceInfos = appService.getAppOnlineInstanceInfo(appId);\n            instanceId = CollectionUtils.isNotEmpty(instanceInfos) && instanceInfos.size() > 0 ? instanceInfos.get(0).getId() : -1;\n        }\n        List<String> commandList = redisCenter.getRedisCommand(instanceId.intValue());\n        if (CollectionUtils.isEmpty(commandList)) {\n            json.put(\"status\", String.valueOf(SuccessEnum.FAIL.value()));\n        } else {\n            json.put(\"commandList\", commandList);\n            json.put(\"status\", String.valueOf(SuccessEnum.SUCCESS.value()));\n        }\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n    @RequestMapping(\"/redisRenameCommand\")\n    public ModelAndView redisRenameCommand(HttpServletRequest request,\n                                     HttpServletResponse response, Long appId, Integer instanceId, Model model) {\n        JSONObject json = new JSONObject();\n        if (instanceId == null) {\n            List<InstanceInfo> instanceInfos = appService.getAppOnlineInstanceInfo(appId);\n            instanceId = CollectionUtils.isNotEmpty(instanceInfos) && instanceInfos.size() > 0 ? instanceInfos.get(0).getId() : -1;\n        }\n        List<Pair<String, String>> configs = redisCenter.getConfigsInConfigFile(instanceId.intValue(), \"rename-command\");\n        if (CollectionUtils.isEmpty(configs)) {\n            json.put(\"status\", String.valueOf(SuccessEnum.FAIL.value()));\n        } else {\n            json.put(\"renameCommands\", configs);\n            json.put(\"status\", String.valueOf(SuccessEnum.SUCCESS.value()));\n        }\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n    @RequestMapping(value = \"/appAlterConfig\")\n    public ModelAndView doAppAlterConfig(HttpServletRequest request,\n                                         HttpServletResponse response, Model model, Long appId) {\n        model.addAttribute(\"appId\", appId);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appAlterConfigIndex\");\n    }\n\n\n    @RequestMapping(value = \"/appScanClean\")\n    public ModelAndView doAppScanClean(HttpServletRequest request,\n                                         HttpServletResponse response, Model model, Long appId) {\n        model.addAttribute(\"appId\", appId);\n\n        AppUser currentUser = getUserInfo(request);\n        List<AppDesc> appDescList = appService.getAppDescList(currentUser, new AppSearch());\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n        return new ModelAndView(\"app/jobIndex/appScanCleanIndex\");\n    }\n\n\n\n    @RequestMapping(value = \"/job/submit\")\n    public ModelAndView submitJobApplication(HttpServletRequest request,\n                                             HttpServletResponse response, Model model,\n                                             Long appId, String nodeInfos, int jobType, String reason, String param) {\n        try {\n            AppUser appUser = getUserInfo(request);\n\n            AppAudit appAudit = new AppAudit();\n            appAudit.setAppId(appId == null ? -1 : appId);\n            appAudit.setUserId(appUser.getId());\n            appAudit.setUserName(appUser.getName());\n            appAudit.setModifyTime(new Date());\n            String info = \"申请原因: \" + reason;\n            if (StringUtils.isNotBlank(nodeInfos)) {\n                info += \"，节点: \" + nodeInfos;\n                appAudit.setParam1(nodeInfos);\n            }\n            if (StringUtils.isNotBlank(param)) {\n                info += \"，其他: \" + param;\n                appAudit.setParam2(param);\n            }\n            appAudit.setInfo(info);\n            appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n            appAudit.setType(jobType);\n            Date now = new Date();\n            appAudit.setCreateTime(now);\n            appAudit.setModifyTime(now);\n            appAuditDao.insertAppAudit(appAudit);\n\n            // 保存日志\n//            AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n//                    AppAuditLogTypeEnum.KEY_VALUE_ANALYSIS);\n//            if (appAuditLog != null) {\n//                appAuditLogDao.save(appAuditLog);\n//            }\n\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\n        }\n        return null;\n    }\n\n\n    @RequestMapping(value = \"import/submit\", method = RequestMethod.POST)\n    public ModelAndView doAppImportSubmit(HttpServletRequest request,\n                                          HttpServletResponse response, Model model, AppDesc appDesc, String memSize) {\n        AppUser appUser = getUserInfo(request);\n\n        //创建appImport\n        AppImport appImport = new AppImport();\n        //appDesc入库\n        if (appDesc != null) {\n            Timestamp now = new Timestamp(new Date().getTime());\n            appDesc.setCreateTime(now);\n            appDesc.setPassedTime(now);\n            appDesc.setVerId(1);\n            appDesc.setStatus((short) AppStatusEnum.STATUS_INITIALIZE.getStatus());\n            appDesc.setHitPrecentAlertValue(0);\n            appDesc.setIsAccessMonitor(AppUserAlertEnum.NO.value());\n            appService.save(appDesc);\n            // 保存应用和用户的关系\n            String officers = appDesc.getOfficer();\n            if (!StringUtils.isEmpty(officers)) {\n                for (String officerId : officers.split(\",\")) {\n                    if (!StringUtils.isEmpty(officerId)) {\n                        appService.saveAppToUser(appDesc.getAppId(), Long.parseLong(officerId));\n                    }\n                }\n            }\n            // 更新appKey\n            long appId = appDesc.getAppId();\n            appService.updateAppKey(appId);\n        }\n        appImport.setAppId(appDesc.getAppId());\n        appImport.setMemSize(NumberUtils.toInt(memSize));\n        appImport.setSourceType(NumberUtils.toInt(request.getParameter(\"sourceType\")));\n        appImport.setInstanceInfo(request.getParameter(\"appInstanceInfo\"));\n        appImport.setRedisPassword(request.getParameter(\"password\"));\n        appImport.setStatus(0);\n        appImportDao.save(appImport);\n\n        //appAudit入库\n        AppAudit appAudit = new AppAudit();\n        appAudit.setAppId(appDesc.getAppId());\n        appAudit.setUserId(appUser.getId());\n        appAudit.setUserName(appUser.getName());\n        appAudit.setInfo(\"迁移到应用：\" + appDesc.getAppId() + \" \" + appDesc.getName());\n        appAudit.setModifyTime(new Date());\n        appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n        appAudit.setType(AppAuditType.APP_IMPORT.getValue());\n        appAudit.setParam1(String.valueOf(appImport.getId()));\n        Date now = new Date();\n        appAudit.setCreateTime(now);\n        appAudit.setModifyTime(now);\n        appAuditDao.insertAppAudit(appAudit);\n\n        return new ModelAndView(\"redirect:/admin/app/jobs\");\n    }\n\n    /**\n     * 添加应用\n     *\n     * @param appDesc 应用实体\n     * @param memSize 申请容量(G)\n     * @return\n     */\n    @RequestMapping(value = \"/add\", method = RequestMethod.POST)\n    public ModelAndView doAppAdd(HttpServletRequest request,\n                                 HttpServletResponse response, Model model, AppDesc appDesc, String memSize) {\n        AppUser appUser = getUserInfo(request);\n        if (appDesc != null) {\n            Timestamp now = new Timestamp(new Date().getTime());\n            appDesc.setCreateTime(now);\n            appDesc.setPassedTime(now);\n            appDesc.setVerId(1);\n            appDesc.setStatus((short) AppStatusEnum.STATUS_ALLOCATED.getStatus());\n            // 设置命中率报警0,默认不监控\n            appDesc.setHitPrecentAlertValue(0);\n            // 客户端默认关闭监控\n            appDesc.setIsAccessMonitor(AppUserAlertEnum.NO.value());\n            appDeployCenter.createApp(appDesc, appUser, memSize);\n        }\n        return new ModelAndView(\"redirect:/admin/app/jobs\");\n    }\n\n    /**\n     * 查看应用名是否存在\n     *\n     * @param appName\n     * @return\n     */\n    @RequestMapping(value = \"/checkAppNameExist\")\n    public ModelAndView doCheckAppNameExist(HttpServletRequest request,\n                                            HttpServletResponse response, Model model, String appName) {\n        AppDesc appDesc = appService.getAppByName(appName);\n        if (appDesc != null) {\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        } else {\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\n        }\n        return null;\n    }\n\n    /**\n     * 应用命令查询\n     *\n     * @param appId\n     * @return\n     */\n    @RequestMapping(\"/command\")\n    public ModelAndView command(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {\n        if (appId != null && appId > 0) {\n            model.addAttribute(\"appId\", appId);\n        }\n        return new ModelAndView(\"app/appCommand\");\n    }\n\n    /**\n     * 执行应用命令\n     *\n     * @param appId\n     * @return\n     */\n    @RequestMapping(\"/commandExecute\")\n    public ModelAndView commandExecute(HttpServletRequest request, HttpServletResponse response, Model model,\n                                       Long appId) {\n        AppUser currentUser = getUserInfo(request);\n        if (appId != null && appId > 0) {\n            model.addAttribute(\"appId\", appId);\n            String command = request.getParameter(\"command\");\n            String result = appStatsCenter.executeCommand(appId, command, currentUser != null ? currentUser.getName() : null);\n            model.addAttribute(\"result\", result);\n        } else {\n            model.addAttribute(\"result\", \"error\");\n        }\n        return new ModelAndView(\"app/commandExecute\");\n    }\n\n    /**\n     * 删除应用下的指定用户\n     *\n     * @param userId\n     * @param appId\n     * @return\n     */\n    @RequestMapping(value = \"/deleteAppToUser\")\n    public ModelAndView doDeleteAppToUser(HttpServletRequest request,\n                                          HttpServletResponse response, Model model, Long userId, Long appId) {\n        if (userId != null && appId != null) {\n            // 验证删除权限\n            AppUser currentUser = getUserInfo(request);\n            List<AppToUser> appToUsers = appService.getAppToUserList(appId);\n            if (CollectionUtils.isNotEmpty(appToUsers)) {\n                for (AppToUser appToUser : appToUsers) {\n                    if (appToUser.getUserId().equals(currentUser.getId())) {\n                        write(response, String.valueOf(SuccessEnum.FAIL.value()));\n                    }\n                }\n            }\n            appService.deleteAppToUser(appId, userId);\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        } else {\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\n        }\n        return null;\n    }\n\n    /**\n     * 更新用户\n     *\n     * @param name\n     * @param chName\n     * @param email\n     * @param mobile\n     * @param weChat\n     * @param type\n     * @param userId\n     * @return\n     */\n    @RequestMapping(value = \"/changeAppUserInfo\")\n    public ModelAndView doAddUser(HttpServletRequest request,\n                                  HttpServletResponse response, Model model, String name, String chName, String email, String mobile,\n                                  String weChat,\n                                  Integer type, Integer isAlert, Long userId, String company, String purpose, Long bizId) {\n        // 后台暂时不对参数进行验证\n        AppUser appUser = AppUser.buildFrom(userId, name, chName, email, mobile, weChat, type, isAlert, company, purpose, bizId);\n        try {\n            if (userId == null) {\n                appUser.setPassword(ConstUtils.DEFAULT_USER_PASSWORD);\n                userService.save(appUser);\n            } else {\n                userService.update(appUser);\n            }\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        } catch (Exception e) {\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    /**\n     * 扩容申请\n     *\n     * @param appId          应用id\n     * @param applyMemSize   申请容量\n     * @param appScaleReason 申请原因\n     * @return\n     */\n    @RequestMapping(value = \"/scale\")\n    public ModelAndView doScaleApp(HttpServletRequest request,\n                                   HttpServletResponse response, Model model, Long appId, String applyMemSize, String appScaleReason) {\n        AppUser appUser = getUserInfo(request);\n        AppDesc appDesc = appService.getByAppId(appId);\n        AppAudit appAudit = appService\n                .saveAppScaleApply(appDesc, appUser, applyMemSize, appScaleReason, AppAuditType.APP_SCALE);\n        appEmailUtil.noticeAppResult(appDesc, appAudit);\n        write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        return null;\n    }\n\n    /**\n     * 应用修改配置申请\n     *\n     * @param appId          应用id\n     * @param appConfigKey   配置项\n     * @param appConfigValue 配置值\n     * @return\n     */\n    @RequestMapping(value = \"/changeAppConfig\")\n    public ModelAndView doChangeAppConfig(HttpServletRequest request,\n                                          HttpServletResponse response, Model model, Long appId, Long instanceId, String appConfigKey,\n                                          String appConfigValue, String appConfigReason) {\n        AppUser appUser = getUserInfo(request);\n        AppDesc appDesc = appService.getByAppId(appId);\n        AppAudit appAudit = appService\n                .saveAppChangeConfig(appDesc, appUser, instanceId, appConfigKey, appConfigValue, appConfigReason,\n                        AppAuditType.APP_MODIFY_CONFIG);\n        appEmailUtil.noticeAppResult(appDesc, appAudit);\n        write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        return null;\n    }\n\n    /**\n     * <p>\n     * Description: 全局报警项修改\n     * </p>\n     *\n     * @param\n     * @return\n     * @author chenshi\n     * @version 1.0\n     * @date 2017/9/25\n     */\n    @RequestMapping(value = \"/changeAppMonitorConfig\")\n    public ModelAndView doChangeMonitorAppConfig(HttpServletRequest request,\n                                                 HttpServletResponse response, Model model, Long appId, Long instanceId, String appConfigKey,\n                                                 String appConfigValue, String appConfigReason) {\n        AppUser appUser = getUserInfo(request);\n        AppDesc appDesc = appService.getByAppId(appId);\n        AppAudit appAudit = appService\n                .saveAppChangeConfig(appDesc, appUser, instanceId, appConfigKey, appConfigValue, appConfigReason,\n                        AppAuditType.APP_MODIFY_CONFIG);\n        appEmailUtil.noticeAppResult(appDesc, appAudit);\n        write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        return null;\n    }\n\n    /**\n     * 实例修改配置申请\n     *\n     * @param appId               应用id\n     * @param instanceConfigKey   配置项\n     * @param instanceConfigValue 配置值\n     * @return\n     */\n    @RequestMapping(value = \"/changeInstanceConfig\")\n    public ModelAndView doChangeInstanceConfig(HttpServletRequest request,\n                                               HttpServletResponse response, Model model, Long appId, Long instanceId, String instanceConfigKey,\n                                               String instanceConfigValue, String instanceConfigReason) {\n        AppUser appUser = getUserInfo(request);\n        AppDesc appDesc = appService.getByAppId(appId);\n        AppAudit appAudit = appService\n                .saveInstanceChangeConfig(appDesc, appUser, instanceId, instanceConfigKey, instanceConfigValue,\n                        instanceConfigReason, AppAuditType.INSTANCE_MODIFY_CONFIG);\n        appEmailUtil.noticeAppResult(appDesc, appAudit);\n        write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        return null;\n    }\n\n    /**\n     * 添加应用和用户对应关系\n     *\n     * @param appId 应用id\n     * @param users 用户id(邮箱前缀)\n     * @returns\n     */\n    @RequestMapping(value = \"/addAppToUser\")\n    public ModelAndView doAddAppToUser(HttpServletRequest request,\n                                       HttpServletResponse response, Model model, Long appId, String users) {\n        if (StringUtils.isNotBlank(users)) {\n            List<String> userIdList = Arrays.asList(users.split(\",\"));\n            for (String userIdStr : userIdList) {\n                if (!appService.saveAppToUser(appId, NumberUtils.toLong(userIdStr, -1l))) {\n                    write(response, String.valueOf(SuccessEnum.FAIL.value()));\n                }\n            }\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        }\n        return null;\n    }\n\n    /**\n     * 修改应用报警配置\n     */\n    @RequestMapping(value = \"/changeAppAlertConfig\")\n    public ModelAndView doChangeAppAlertConfig(HttpServletRequest request,\n                                               HttpServletResponse response, Model model) {\n\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"), -1);\n        int memAlertValue = NumberUtils.toInt(request.getParameter(\"memAlertValue\"), -1);\n        int clientConnAlertValue = NumberUtils.toInt(request.getParameter(\"clientConnAlertValue\"), -1);\n        int hitPrecentAlertValue = NumberUtils.toInt(request.getParameter(\"hitPrecentAlertValue\"), 0);\n        int isAccessMonitor = NumberUtils.toInt(request.getParameter(\"isAccessMonitor\"), 0);\n        SuccessEnum result = appService\n                .changeAppAlertConfig(appId, memAlertValue, clientConnAlertValue, hitPrecentAlertValue, isAccessMonitor,\n                        getUserInfo(request));\n        write(response, String.valueOf(result.value()));\n        return null;\n    }\n\n    /**\n     * 修改应用信息\n     */\n    @RequestMapping(value = \"/updateAppDetail\")\n    public ModelAndView doUpdateAppDetail(HttpServletRequest request,\n                                          HttpServletResponse response, Model model) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"), 0);\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"{} want to update appId={} info!\", appUser.getName(), appId);\n        String appDescName = request.getParameter(\"appDescName\");\n        String appDescIntro = request.getParameter(\"appDescIntro\");\n        String officer = request.getParameter(\"officer\");\n        SuccessEnum successEnum = SuccessEnum.SUCCESS;\n        if (appId <= 0 || StringUtils.isBlank(appDescName) || StringUtils.isBlank(appDescIntro) || StringUtils\n                .isBlank(officer)) {\n            successEnum = SuccessEnum.FAIL;\n        } else {\n            try {\n                AppDesc appDesc = appService.getByAppId(appId);\n                appDesc.setName(appDescName);\n                appDesc.setIntro(appDescIntro);\n                appDesc.setOfficer(officer);\n                appService.update(appDesc);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n                successEnum = SuccessEnum.FAIL;\n            }\n        }\n        write(response, String.valueOf(successEnum.value()));\n        return null;\n    }\n\n    /**\n     * 应用日报查询\n     */\n    @RequestMapping(\"/daily\")\n    public ModelAndView appDaily(HttpServletRequest request,\n                                 HttpServletResponse response, Model model, Long appId) throws ParseException {\n        // 1. 应用信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n\n        // 2. 日期\n        String dailyDateParam = request.getParameter(\"dailyDate\");\n        Date date;\n        if (StringUtils.isBlank(dailyDateParam)) {\n            date = DateUtils.addDays(new Date(), -1);\n        } else {\n            date = DateUtil.parseYYYY_MM_dd(dailyDateParam);\n        }\n        model.addAttribute(\"dailyDate\", dailyDateParam);\n\n        // 3. 日报\n        AppDailyData appDailyData = appDailyDataCenter.getAppDailyData(appId, date);\n        model.addAttribute(\"appDailyData\", appDailyData);\n\n        return new ModelAndView(\"app/appDaily\");\n    }\n\n    /**\n     * 应用历史慢查询\n     *\n     * @param appId\n     * @return\n     * @throws ParseException\n     */\n    @RequestMapping(\"/slowLog\")\n    public ModelAndView appSlowLog(HttpServletRequest request,\n                                   HttpServletResponse response, Model model, Long appId) throws ParseException {\n        // 应用基本信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n\n        // 开始和结束日期\n        TimeBetween timeBetween = getTimeBetween(request, model, \"slowLogStartDate\", \"slowLogEndDate\");\n        Date startDate = timeBetween.getStartDate();\n        Date endDate = timeBetween.getEndDate();\n\n        // 应用慢查询日志\n        Map<String, Long> appInstanceSlowLogCountMap = appStatsCenter\n                .getInstanceSlowLogCountMapByAppId(appId, startDate, endDate);\n        model.addAttribute(\"appInstanceSlowLogCountMap\", appInstanceSlowLogCountMap);\n        List<InstanceSlowLog> appInstanceSlowLogList = appStatsCenter\n                .getInstanceSlowLogByAppId(appId, startDate, endDate);\n        model.addAttribute(\"appInstanceSlowLogList\", appInstanceSlowLogList);\n\n        // 各个实例对应的慢查询日志\n        Map<String, List<InstanceSlowLog>> instaceSlowLogMap = new HashMap<String, List<InstanceSlowLog>>();\n        Map<String, Long> instanceHostPortIdMap = new HashMap<String, Long>();\n        for (InstanceSlowLog instanceSlowLog : appInstanceSlowLogList) {\n            String hostPort = instanceSlowLog.getIp() + \":\" + instanceSlowLog.getPort();\n            instanceHostPortIdMap.put(hostPort, instanceSlowLog.getInstanceId());\n            if (instaceSlowLogMap.containsKey(hostPort)) {\n                instaceSlowLogMap.get(hostPort).add(instanceSlowLog);\n            } else {\n                List<InstanceSlowLog> list = new ArrayList<InstanceSlowLog>();\n                list.add(instanceSlowLog);\n                instaceSlowLogMap.put(hostPort, list);\n            }\n        }\n        model.addAttribute(\"instaceSlowLogMap\", instaceSlowLogMap);\n        model.addAttribute(\"instanceHostPortIdMap\", instanceHostPortIdMap);\n\n        return new ModelAndView(\"app/slowLog\");\n    }\n\n    @RequestMapping(\"/latencyMonitor\")\n    public ModelAndView appLatencyMonitor(HttpServletRequest request,\n                                          HttpServletResponse response, Model model, Long appId) {\n        // 应用基本信息\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appDesc\", appDesc);\n\n        // 获取时间区间\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = DateUtil.fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        searchDate = timeBetween.getFormatStartDate();\n        model.addAttribute(\"searchDate\", searchDate);\n\n        // 应用延迟监控数据\n        Map<String, List<Map<String, Object>>> appLatencyStats = appStatsCenter.getAppLatencyStats(appId, startTime, endTime);\n        model.addAttribute(\"appLatencyStatsJson\", JSONObject.toJSONString(appLatencyStats));\n\n        Map<String, Long> appLatencyStatsGroupByInstance = appStatsCenter.getAppLatencyStatsGroupByInstance(appId, startTime, endTime);\n        model.addAttribute(\"appLatencyStatsGroupByInstance\", appLatencyStatsGroupByInstance);\n\n        Set<String> instanceSet = new HashSet<>();\n        instanceSet.addAll(appLatencyStatsGroupByInstance.keySet());\n\n        // 应用慢查询日志\n        Map<String, Long> appInstanceSlowLogCountMap = appStatsCenter\n                .getInstanceSlowLogCountMapByAppId(appId, timeBetween.getStartDate(), timeBetween.getEndDate());\n        model.addAttribute(\"appInstanceSlowLogCountMap\", appInstanceSlowLogCountMap);\n        instanceSet.addAll(appInstanceSlowLogCountMap.keySet());\n        model.addAttribute(\"instanceSet\", instanceSet);\n\n        List<InstanceSlowLog> appInstanceSlowLogList = appStatsCenter\n                .getInstanceSlowLogByAppId(appId, timeBetween.getStartDate(), timeBetween.getEndDate());\n        model.addAttribute(\"appInstanceSlowLogList\", appInstanceSlowLogList);\n\n        // 各个实例对应的慢查询日志\n        Map<String, List<InstanceSlowLog>> instaceSlowLogMap = new HashMap<String, List<InstanceSlowLog>>();\n        Map<String, Long> instanceHostPortIdMap = new HashMap<String, Long>();\n        for (InstanceSlowLog instanceSlowLog : appInstanceSlowLogList) {\n            String hostPort = instanceSlowLog.getIp() + \":\" + instanceSlowLog.getPort();\n            instanceHostPortIdMap.put(hostPort, instanceSlowLog.getInstanceId());\n            if (instaceSlowLogMap.containsKey(hostPort)) {\n                instaceSlowLogMap.get(hostPort).add(instanceSlowLog);\n            } else {\n                List<InstanceSlowLog> list = new ArrayList<InstanceSlowLog>();\n                list.add(instanceSlowLog);\n                instaceSlowLogMap.put(hostPort, list);\n            }\n        }\n        model.addAttribute(\"instaceSlowLogMap\", instaceSlowLogMap);\n        model.addAttribute(\"instanceHostPortIdMap\", instanceHostPortIdMap);\n        return new ModelAndView(\"app/slowLog\");\n    }\n\n    @RequestMapping(value = \"/latencyInfoDetails\")\n    public ModelAndView getLatencyInfoDetails(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        model.addAttribute(\"appId\", appId);\n\n        long timestamp = NumberUtils.toLong(request.getParameter(\"searchTime\")); //毫秒\n        Date startDate = new Date(timestamp);\n        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, \"yyyyMMddHHmm00\"));\n        Date endDate = DateUtils.addMinutes(startDate, 1);\n        long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, \"yyyyMMddHHmm00\"));\n        model.addAttribute(\"searchTime\", new SimpleDateFormat(\"yyyy-MM-dd HH:mm:00\").format(startDate));\n\n        Map<String, Long> sumInstanceLatencyStatMap = appStatsCenter.getAppLatencyStatsGroupByInstance(appId, startTime, endTime);\n        model.addAttribute(\"sumInstanceLatencyStatMap\", sumInstanceLatencyStatMap);\n\n        Map<String, List<Map<String, Object>>> latencyInfoDetailMap = appStatsCenter.getAppLatencyInfo(appId, startTime, endTime);\n        model.addAttribute(\"latencyInfoDetailMap\", latencyInfoDetailMap);\n\n        return new ModelAndView(\"/app/appLatencyInfoDetail\");\n    }\n\n    @RequestMapping(value = \"/latencyRelatedSlowLog\")\n    public ModelAndView getLatencyRelatedSlowLog(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long instanceId = NumberUtils.toLong(request.getParameter(\"instanceId\"));\n        String executeDate = request.getParameter(\"executeDate\"); //秒\n        List<InstanceSlowLog> instanceSlowLogList = appStatsCenter.getByInstanceExecuteTime(instanceId, executeDate);\n        model.addAttribute(\"instanceSlowLogList\", instanceSlowLogList);\n\n        return new ModelAndView(\"\");\n    }\n\n\n    /**\n     * 清理应用数据\n     */\n    @RequestMapping(value = \"/cleanAppData\")\n    public ModelAndView doCleanAppData(HttpServletRequest request, HttpServletResponse response, Model model,\n                                       long appId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"{} start to clean appId={} data!\", appUser.getName(), appId);\n        SuccessEnum successEnum = SuccessEnum.FAIL;\n        if (appId > 0) {\n            //验证用户对应用的权限 以及数据清理的结果\n            if (checkAppUserProvilege(request, appId) && appDeployCenter.cleanAppData(appId, getUserInfo(request))) {\n                successEnum = SuccessEnum.SUCCESS;\n            }\n        }\n        logger.warn(\"{} end to clean appId={} data, result is {}\", appUser.getName(), appId, successEnum.info());\n        write(response, String.valueOf(successEnum.value()));\n        return null;\n    }\n\n    /**\n     * AppCommandGroup列表组装成json串\n     */\n    private String assembleGroupJson(List<AppCommandGroup> appCommandGroupList) {\n        if (appCommandGroupList == null || appCommandGroupList.isEmpty()) {\n            return \"[]\";\n        }\n        List<SimpleChartData> list = new ArrayList<SimpleChartData>();\n        for (AppCommandGroup appCommandGroup : appCommandGroupList) {\n            SimpleChartData chartData = SimpleChartData\n                    .getFromAppCommandGroup(appCommandGroup);\n            list.add(chartData);\n        }\n        return JSONObject.toJSONString(list);\n    }\n\n    /**\n     * AppStats列表组装成json串\n     */\n    private String assembleAppStatsJson(List<AppStats> appStats, String statName) {\n        if (appStats == null || appStats.isEmpty()) {\n            return \"[]\";\n        }\n        List<SimpleChartData> list = new ArrayList<SimpleChartData>();\n        for (AppStats stat : appStats) {\n            try {\n                SimpleChartData chartData = SimpleChartData.getFromAppStats(stat, statName);\n                list.add(chartData);\n            } catch (ParseException e) {\n                logger.info(e.getMessage(), e);\n            }\n        }\n        return JSONObject.toJSONString(list);\n    }\n\n    private String assembleMutilDateAppCommandJsonMinute(List<AppCommandStats> appCommandStats, Date startDate,\n                                                         Date endDate) {\n        if (appCommandStats == null || appCommandStats.isEmpty()) {\n            return \"[]\";\n        }\n        Map<String, List<HighchartPoint>> map = new HashMap<String, List<HighchartPoint>>();\n        Date currentDate = DateUtils.addDays(endDate, -1);\n        int diffDays = 0;\n        while (currentDate.getTime() >= startDate.getTime()) {\n            List<HighchartPoint> list = new ArrayList<HighchartPoint>();\n            for (AppCommandStats stat : appCommandStats) {\n                try {\n                    HighchartPoint highchartPoint = HighchartPoint.getFromAppCommandStats(stat, currentDate, diffDays);\n                    if (highchartPoint == null) {\n                        continue;\n                    }\n                    list.add(highchartPoint);\n                } catch (ParseException e) {\n                    logger.info(e.getMessage(), e);\n                }\n            }\n            String formatDate = DateUtil.formatDate(currentDate, \"yyyy-MM-dd\");\n            map.put(formatDate, list);\n            currentDate = DateUtils.addDays(currentDate, -1);\n            diffDays++;\n        }\n        return JSONObject.toJSONString(map);\n    }\n\n    /**\n     * 多命令组装\n     *\n     * @param appStats\n     * @param statNameList\n     * @param startDate\n     * @return\n     */\n    private String assembleMutiStatAppStatsJsonMinute(List<AppStats> appStats, List<String> statNameList,\n                                                      Date startDate) {\n        if (appStats == null || appStats.isEmpty()) {\n            return \"[]\";\n        }\n        Map<String, List<HighchartPoint>> map = new HashMap<String, List<HighchartPoint>>();\n        for (String statName : statNameList) {\n            List<HighchartPoint> list = new ArrayList<HighchartPoint>();\n            for (AppStats stat : appStats) {\n                try {\n                    HighchartPoint highchartPoint = HighchartPoint.getFromAppStats(stat, statName, startDate, 0);\n                    if (highchartPoint == null) {\n                        continue;\n                    }\n                    list.add(highchartPoint);\n                } catch (ParseException e) {\n                    logger.info(e.getMessage(), e);\n                }\n            }\n            map.put(statName, list);\n        }\n        return JSONObject.toJSONString(map);\n    }\n\n    /**\n     * 多时间组装\n     *\n     * @param appStats\n     * @param statName\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    private String assembleMutilDateAppStatsJsonMinute(List<AppStats> appStats, String statName, Date startDate,\n                                                       Date endDate) {\n        if (appStats == null || appStats.isEmpty()) {\n            return \"[]\";\n        }\n        Map<String, List<HighchartPoint>> map = new HashMap<String, List<HighchartPoint>>();\n        Date currentDate = DateUtils.addDays(endDate, -1);\n        int diffDays = 0;\n        while (currentDate.getTime() >= startDate.getTime()) {\n            List<HighchartPoint> list = new ArrayList<HighchartPoint>();\n            for (AppStats stat : appStats) {\n                try {\n                    HighchartPoint highchartPoint = HighchartPoint\n                            .getFromAppStats(stat, statName, currentDate, diffDays);\n                    if (highchartPoint == null) {\n                        continue;\n                    }\n                    list.add(highchartPoint);\n                } catch (ParseException e) {\n                    logger.info(e.getMessage(), e);\n                }\n            }\n            String formatDate = DateUtil.formatDate(currentDate, \"yyyy-MM-dd\");\n            map.put(formatDate, list);\n            currentDate = DateUtils.addDays(currentDate, -1);\n            diffDays++;\n        }\n        return JSONObject.toJSONString(map);\n    }\n\n    /**\n     * 多时间组装\n     *\n     * @param appDailyDatas\n     * @param statName\n     * @return\n     */\n    private String assembleMutilDateAppHitRatio(List<AppDailyData> appDailyDatas, String statName) {\n        if (appDailyDatas == null || appDailyDatas.isEmpty()) {\n            return \"[]\";\n        }\n        List<HighchartDoublePoint> list = new ArrayList<>();\n        for (AppDailyData appDailyData : appDailyDatas) {\n            try {\n                HighchartDoublePoint highchartPoint = HighchartDoublePoint\n                        .getFromAppDailyDatas(appDailyData, statName);\n                if (highchartPoint == null) {\n                    continue;\n                }\n                list.add(highchartPoint);\n            } catch (ParseException e) {\n                logger.info(e.getMessage(), e);\n            }\n        }\n        return JSONObject.toJSONString(list);\n    }\n\n    /**\n     * 多时间组装\n     *\n     * @param appStats\n     * @param statName\n     * @param startDate\n     * @param endDate\n     * @return\n     */\n    private String assembleMutilDateAppStatsJsonMinuteDoublePoint(List<AppStats> appStats, String statName, Date startDate,\n                                                                  Date endDate) {\n        if (appStats == null || appStats.isEmpty()) {\n            return \"[]\";\n        }\n        Map<String, List<HighchartDoublePoint>> map = new HashMap<>();\n        Date currentDate = DateUtils.addDays(endDate, -1);\n        int diffDays = 0;\n        while (currentDate.getTime() >= startDate.getTime()) {\n            List<HighchartDoublePoint> list = new ArrayList<>();\n            for (AppStats stat : appStats) {\n                try {\n                    HighchartDoublePoint highchartPoint = HighchartDoublePoint\n                            .getFromAppStats(stat, statName, currentDate, diffDays);\n                    if (highchartPoint == null) {\n                        continue;\n                    }\n                    list.add(highchartPoint);\n                } catch (ParseException e) {\n                    logger.info(e.getMessage(), e);\n                }\n            }\n            String formatDate = DateUtil.formatDate(currentDate, \"yyyy-MM-dd\");\n            map.put(formatDate, list);\n            currentDate = DateUtils.addDays(currentDate, -1);\n            diffDays++;\n        }\n        return JSONObject.toJSONString(map);\n    }\n\n    /**\n     * AppCommandStats列表组装成json串\n     */\n    private String assembleJson(List<AppCommandStats> appCommandStatsList) {\n        return assembleJson(appCommandStatsList, null);\n    }\n\n    private String assembleJson(List<AppCommandStats> appCommandStatsList, Integer addDay) {\n        if (appCommandStatsList == null || appCommandStatsList.isEmpty()) {\n            return \"[]\";\n        }\n        List<SimpleChartData> list = new ArrayList<SimpleChartData>();\n        for (AppCommandStats stat : appCommandStatsList) {\n            try {\n                SimpleChartData chartData = SimpleChartData\n                        .getFromAppCommandStats(stat, addDay);\n                list.add(chartData);\n            } catch (ParseException e) {\n                logger.info(e.getMessage(), e);\n            }\n        }\n        return JSONObject.toJSONString(list);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppDataMigrateController.java",
    "content": "package com.sohu.cache.web.controller;\n\n\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.AppUserDao;\nimport com.sohu.cache.dao.ResourceDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.stats.app.AppDataMigrateCenter;\nimport com.sohu.cache.stats.app.RedisMigrateToolCenter;\nimport com.sohu.cache.stats.app.RedisShakeCenter;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppImportService;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.ResourceService;\nimport com.sohu.cache.web.util.Page;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 应用数据迁移入口\n *\n * @author leifu\n * @Date 2016-6-8\n * @Time 下午11:10:34\n */\n@Controller\n@RequestMapping(\"/data/migrate\")\npublic class AppDataMigrateController extends BaseController {\n\n    private static Set<String> MIGRATE_SAMPLE_USEFUL_LINES = new HashSet<String>();\n\n    static {\n        MIGRATE_SAMPLE_USEFUL_LINES.add(\"Checked keys\");\n        MIGRATE_SAMPLE_USEFUL_LINES.add(\"Inconsistent value keys\");\n        MIGRATE_SAMPLE_USEFUL_LINES.add(\"Inconsistent expire keys\");\n        MIGRATE_SAMPLE_USEFUL_LINES.add(\"Other check error keys\");\n        MIGRATE_SAMPLE_USEFUL_LINES.add(\"Checked OK keys\");\n    }\n\n    @Autowired\n    private AppDataMigrateCenter appDataMigrateCenter;\n    @Resource(name = \"redisMigrateToolCenter\")\n    private RedisMigrateToolCenter redisMigrateToolCenter;\n    @Autowired\n    private RedisShakeCenter redisShakeCenter;\n    @Resource(name = \"appService\")\n    private AppService appService;\n    @Resource(name = \"machineCenter\")\n    private MachineCenter machineCenter;\n    @Autowired\n    private ResourceDao resourceDao;\n    @Autowired\n    private AppUserDao appUserDao;\n    @Autowired\n    private ResourceService resourceService;\n    @Autowired\n    private AppImportService appImportService;\n\n\n    @RequestMapping(\"/index\")\n    public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model,\n                              String tabTag,\n                              AppDataMigrateSearch appDataMigrateSearch) {\n        List<AppUser> adminList = appUserDao.getAdminList();\n        model.addAttribute(\"adminList\", adminList);\n\n        // 分页相关\n        int totalCount = appDataMigrateCenter.getMigrateTaskCount(appDataMigrateSearch);\n        int pageNo = NumberUtils.toInt(request.getParameter(\"pageNo\"), 1);\n        Page page = new Page(pageNo, 15, totalCount);\n        appDataMigrateSearch.setPage(page);\n\n        List<AppDataMigrateStatus> appDataMigrateStatusList = appDataMigrateCenter.search(appDataMigrateSearch);\n        model.addAttribute(\"page\", page);\n        model.addAttribute(\"appDataMigrateStatusList\", appDataMigrateStatusList);\n        model.addAttribute(\"appDataMigrateSearch\", appDataMigrateSearch);\n        model.addAttribute(\"tabTag\", tabTag);\n        model.addAttribute(\"appMigrateActive\", SuccessEnum.SUCCESS.value());\n\n        return new ModelAndView(\"manage/migrate/list\");\n    }\n\n    /**\n     * 初始化界面\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/init\")\n    public ModelAndView init(HttpServletRequest request, Model model) {\n        List<MachineInfo> machineInfoList = machineCenter.getMachineInfoByType(MachineInfoEnum.TypeEnum.REDIS_MIGRATE_TOOL);\n        Map<String, Integer> machineInfoMap = machineInfoList.stream().collect(Collectors.toMap(\n                machineInfo -> machineInfo.getIp(),\n                machineInfo -> getMigrateMachineUsed(machineInfo.getIp())));\n        List<SystemResource> resourcelist = resourceService.getResourceList(ResourceEnum.TOOL.getValue());\n        model.addAttribute(\"resourcelist\", resourcelist);\n        model.addAttribute(\"machineInfoMap\", machineInfoMap);\n\n        long importId = NumberUtils.toLong(request.getParameter(\"importId\"));\n        if (importId > 0) {\n            AppImport appImport = appImportService.get(importId);\n            model.addAttribute(\"importId\", importId);\n            model.addAttribute(\"targetAppId\", appImport.getAppId());\n            model.addAttribute(\"sourceServers\", appImport.getInstanceInfo());\n            model.addAttribute(\"redisSourcePass\", appImport.getRedisPassword());\n            model.addAttribute(\"sourceType\", appImport.getSourceType());\n            model.addAttribute(\"sourceDataType\", 0);\n            model.addAttribute(\"redisSourceVersion\", appImport.getRedisVersionName());\n        }\n\n        return new ModelAndView(\"migrate/init\");\n    }\n\n    private int getMigrateMachineUsed(String migrateMachineIp) {\n        try {\n            String cmd = \"ps -ef | grep redis-shake | grep -v grep | grep -v tail\";\n            String response = SSHUtil.execute(migrateMachineIp, cmd);\n            if (StringUtils.isNotEmpty(response)) {\n                String[] redis_shake_count = response.split(ConstUtils.NEXT_LINE);\n                return redis_shake_count.length;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return 0;\n    }\n\n    /**\n     * 检查配置\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/check\")\n    public ModelAndView check(HttpServletRequest request, Model model) {\n        //相关参数\n        String migrateMachineIp = request.getParameter(\"migrateMachineIp\");\n        String sourceRedisMigrateIndex = request.getParameter(\"sourceRedisMigrateIndex\");\n        AppDataMigrateEnum sourceRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(sourceRedisMigrateIndex, -1));\n        String sourceServers = request.getParameter(\"sourceServers\");\n        String targetRedisMigrateIndex = request.getParameter(\"targetRedisMigrateIndex\");\n        AppDataMigrateEnum targetRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(targetRedisMigrateIndex, -1));\n        String targetServers = request.getParameter(\"targetServers\");\n        String redisSourcePass = request.getParameter(\"redisSourcePass\");\n        String redisTargetPass = request.getParameter(\"redisTargetPass\");\n\n        //检查返回结果\n//        int migrateTool = NumberUtils.toInt(request.getParameter(\"migrateTool\"), 0);\n        int versionid = NumberUtils.toInt(request.getParameter(\"versionid\"));\n        SystemResource resource = resourceService.getResourceById(versionid);\n\n        if (resource == null) {\n            return null;\n        }\n        // 检验机器安装环境\n        redisConfigTemplateService.checkAndInstallRedisTool(migrateMachineIp, resource);\n\n        AppDataMigrateResult redisMigrateResult = null;\n        if (resource.getName().indexOf(\"redis-shake\") > -1) {\n            redisMigrateResult = redisShakeCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass, resource);\n        } else if (resource.getName().indexOf(\"redis-migrate-tool\") > -1) {\n            redisMigrateResult = redisMigrateToolCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass, resource);\n        }\n        model.addAttribute(\"status\", redisMigrateResult.getStatus());\n        model.addAttribute(\"message\", redisMigrateResult.getMessage());\n        /*AppDataMigrateResult redisMigrateResult = migrateTool == 1 ?\n                redisMigrateToolCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass)\n                : redisShakeCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass);\n        model.addAttribute(\"status\", redisMigrateResult.getStatus());\n        model.addAttribute(\"message\", redisMigrateResult.getMessage());*/\n\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 开始迁移\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/start\")\n    public ModelAndView start(HttpServletRequest request, Model model) {\n        //相关参数\n        String migrateMachineIp = request.getParameter(\"migrateMachineIp\");\n        String sourceRedisMigrateIndex = request.getParameter(\"sourceRedisMigrateIndex\");\n        AppDataMigrateEnum sourceRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(sourceRedisMigrateIndex, -1));\n        String sourceServers = request.getParameter(\"sourceServers\");\n        String targetRedisMigrateIndex = request.getParameter(\"targetRedisMigrateIndex\");\n        AppDataMigrateEnum targetRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(targetRedisMigrateIndex, -1));\n        String targetServers = request.getParameter(\"targetServers\");\n        long sourceAppId = NumberUtils.toLong(request.getParameter(\"sourceAppId\"));\n        long targetAppId = NumberUtils.toLong(request.getParameter(\"targetAppId\"));\n        String redisSourcePass = request.getParameter(\"redisSourcePass\");\n        String redisTargetPass = request.getParameter(\"redisTargetPass\");\n        String redisSourceVersion = request.getParameter(\"redisSourceVersion\");\n        String redisTargetVersion = request.getParameter(\"redisTargetVersion\");\n        int source_rdb_parallel = NumberUtils.toInt(request.getParameter(\"source_rdb_parallel\"), 8);\n        int parallel = NumberUtils.toInt(request.getParameter(\"parallel\"), 16);\n\n        AppUser appUser = getUserInfo(request);\n        long userId = appUser == null ? 0 : appUser.getId();\n\n        // 不需要对格式进行检验,check已经做过了，开始迁移\n//        int migrateTool = NumberUtils.toInt(request.getParameter(\"migrateTool\"), 0);\n        int versionid = NumberUtils.toInt(request.getParameter(\"versionid\"));\n        SystemResource resource = resourceService.getResourceById(versionid);\n\n        if (resource == null) {\n            return null;\n        }\n        AppDataMigrateStatus appDataMigrateStatus = new AppDataMigrateStatus();\n        if (resource.getName().indexOf(\"redis-shake\") > -1) {\n            appDataMigrateStatus = redisShakeCenter.migrate(migrateMachineIp, source_rdb_parallel, parallel,\n                    sourceRedisMigrateEnum, sourceServers,\n                    targetRedisMigrateEnum, targetServers,\n                    sourceAppId, targetAppId,\n                    redisSourcePass, redisTargetPass,\n                    redisSourceVersion, redisTargetVersion,\n                    userId, resource);\n        } else if (resource.getName().indexOf(\"redis-migrate-tool\") > -1) {\n            appDataMigrateStatus = redisMigrateToolCenter.migrate(migrateMachineIp, sourceRedisMigrateEnum, sourceServers,\n                    targetRedisMigrateEnum, targetServers, sourceAppId, targetAppId, redisSourcePass, redisTargetPass, userId, resource);\n        }\n        model.addAttribute(\"status\", 1);\n        model.addAttribute(\"migrateId\", appDataMigrateStatus.getMigrateId());\n\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 停掉迁移任务\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/stop\")\n    public ModelAndView stop(HttpServletRequest request, Model model) {\n        //任务id：查到任务相关信息\n        long id = NumberUtils.toLong(request.getParameter(\"id\"));\n        int migrateTool = NumberUtils.toInt(request.getParameter(\"migrateTool\"), 0);\n\n\n        AppDataMigrateResult stopMigrateResult = migrateTool == 1 ?\n                redisMigrateToolCenter.stopMigrate(id)\n                : redisShakeCenter.stopMigrate(id);\n        model.addAttribute(\"status\", stopMigrateResult.getStatus());\n        model.addAttribute(\"message\", stopMigrateResult.getMessage());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 查看迁移日志\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/log\")\n    public ModelAndView log(HttpServletRequest request, Model model) {\n        //任务id：查到任务相关信息\n        long id = NumberUtils.toLong(request.getParameter(\"id\"));\n        int pageSize = NumberUtils.toInt(request.getParameter(\"pageSize\"), 0);\n        if (pageSize == 0) {\n            pageSize = 100;\n        }\n\n        String log = appDataMigrateCenter.showDataMigrateLog(id, pageSize);\n        model.addAttribute(\"logList\", Arrays.asList(log.split(ConstUtils.NEXT_LINE)));\n        return new ModelAndView(\"migrate/log\");\n    }\n\n    /**\n     * 查看迁移配置\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/config\")\n    public ModelAndView config(HttpServletRequest request, Model model) {\n        //任务id：查到任务相关信息\n        long id = NumberUtils.toLong(request.getParameter(\"id\"));\n        String config = appDataMigrateCenter.showDataMigrateConf(id);\n        model.addAttribute(\"configList\", Arrays.asList(config.split(ConstUtils.NEXT_LINE)));\n        return new ModelAndView(\"migrate/config\");\n    }\n\n    /**\n     * 查看迁移进度\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/process\")\n    public ModelAndView showProcess(HttpServletRequest request, Model model) {\n        long id = NumberUtils.toLong(request.getParameter(\"id\"));\n        int migrateTool = NumberUtils.toInt(request.getParameter(\"migrateTool\"), 0);\n        if (migrateTool == 0) {\n            String process = redisShakeCenter.showProcess(id);\n            model.addAttribute(\"process\", process);\n            return new ModelAndView(\"\");\n        } else {\n            Map<RedisMigrateToolConstant, Map<String, Object>> migrateToolStatMap = appDataMigrateCenter.showMiragteToolProcess(id);\n            model.addAttribute(\"migrateToolStatMap\", migrateToolStatMap);\n            return new ModelAndView(\"migrate/process\");\n        }\n    }\n\n    /**\n     * 数据校验\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/checkData\")\n    public ModelAndView checkData(HttpServletRequest request, Model model) {\n        long id = NumberUtils.toLong(request.getParameter(\"id\"));\n        int migrateTool = NumberUtils.toInt(request.getParameter(\"migrateTool\"), 0);\n        int comparemode = NumberUtils.toInt(request.getParameter(\"comparemode\"), 3);\n        int nums = 1000 + new Random().nextInt(2000);\n        CommandResult commandResult;\n        List<String> checkDataResultList = new ArrayList<String>();\n        if (migrateTool == 1) {\n            // redis-migrate-tool 采样校验\n            commandResult = redisMigrateToolCenter.sampleCheckData(id, nums);\n            String message = commandResult.getResult();\n            checkDataResultList.add(\"一共随机检验了\" + nums + \"个key\" + \",检查结果如下:\");\n            String[] lineArr = message.split(ConstUtils.NEXT_LINE);\n            for (String line : lineArr) {\n                if (StringUtils.isBlank(line)) {\n                    continue;\n                }\n                // 行数太多显示会有问题\n                if (lineArr.length > 100 && !isUsefulLine(line)) {\n                    continue;\n                }\n                //message格式显示有点问题\n                line = line.replace(\"[0m\", \"\");\n                line = line.replace(\"[31m\", \"\");\n                line = line.replace(\"[33m\", \"\");\n                checkDataResultList.add(line.trim());\n            }\n            model.addAttribute(\"checkDataResultList\", checkDataResultList);\n            model.addAttribute(\"checkDataCommand\", commandResult.getCommand());\n        } else {\n            // redis-full-check\n            commandResult = redisShakeCenter.checkData(id, 1000, comparemode);\n            model.addAttribute(\"checkDataCommand\", commandResult.getCommand());\n        }\n        return new ModelAndView(\"migrate/checkData\");\n    }\n\n    /**\n     * 查看迁移日志\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/checkData/log\")\n    public ModelAndView checkDatalog(HttpServletRequest request, Model model) {\n        //任务id：查到任务相关信息\n        long id = NumberUtils.toLong(request.getParameter(\"id\"));\n        int pageSize = NumberUtils.toInt(request.getParameter(\"pageSize\"), 0);\n        if (pageSize == 0) {\n            pageSize = 100;\n        }\n\n        String log = appDataMigrateCenter.showCheckDataLog(id, pageSize);\n        model.addAttribute(\"checkDatalogList\", Arrays.asList(log.split(ConstUtils.NEXT_LINE)));\n        return new ModelAndView(\"migrate/checkDataLog\");\n    }\n\n    private boolean isUsefulLine(String line) {\n        for (String usefulLine : MIGRATE_SAMPLE_USEFUL_LINES) {\n            if (line.contains(usefulLine)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 通过应用id获取可用的Redis实例信息\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/appInstanceList\")\n    public ModelAndView appInstanceList(HttpServletRequest request, Model model) {\n        String appIdStr = request.getParameter(\"appId\");\n        String migrateToolStr = request.getParameter(\"migrateTool\");\n        long appId = NumberUtils.toLong(appIdStr);\n        int migrateTool = NumberUtils.toInt(migrateToolStr);\n        AppDesc appDesc = appService.getByAppId(appId);\n        String instances = migrateTool == 1 ?\n                redisMigrateToolCenter.getAppInstanceListForRedisMigrateTool(appId)\n                : redisShakeCenter.getAppInstanceListForRedisShake(appId);\n        model.addAttribute(\"instances\", instances);\n        model.addAttribute(\"password\", appDesc == null ? \"\" : appDesc.getAppPassword());\n        model.addAttribute(\"appType\", appDesc == null ? -1 : appDesc.getType());\n        model.addAttribute(\"appName\", appDesc == null ? \"\" : appDesc.getName());\n        model.addAttribute(\"redisVersion\", appDesc == null ? \"\" : resourceDao.getResourceById(appDesc.getVersionId()).getName());\n        return new ModelAndView(\"\");\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppManageController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.InstanceReshardProcessDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.RedisDeployCenter;\nimport com.sohu.cache.redis.util.AuthUtil;\nimport com.sohu.cache.stats.app.AppDailyDataCenter;\nimport com.sohu.cache.stats.app.AppDeployCenter;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.DeployInfoEnum;\nimport com.sohu.cache.web.enums.NodeEnum;\nimport com.sohu.cache.web.enums.RedisOperateEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppScrollRestartService;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.util.AppEmailUtil;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.text.ParseException;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 应用后台管理\n *\n * @author leifu\n * @Time 2014年7月3日\n */\n@Controller\n@RequestMapping(\"manage/app\")\npublic class AppManageController extends BaseController {\n\n    private Logger logger = LoggerFactory.getLogger(AppManageController.class);\n\n    @Resource(name = \"machineCenter\")\n    private MachineCenter machineCenter;\n\n    @Resource(name = \"appEmailUtil\")\n    private AppEmailUtil appEmailUtil;\n\n    @Resource(name = \"appDeployCenter\")\n    private AppDeployCenter appDeployCenter;\n\n    @Resource(name = \"redisCenter\")\n    private RedisCenter redisCenter;\n\n    @Resource(name = \"redisDeployCenter\")\n    private RedisDeployCenter redisDeployCenter;\n\n    @Resource(name = \"appDailyDataCenter\")\n    private AppDailyDataCenter appDailyDataCenter;\n\n    @Resource(name = \"instanceReshardProcessDao\")\n    private InstanceReshardProcessDao instanceReshardProcessDao;\n\n    @Resource(name = \"appService\")\n    private AppService appService;\n\n    @Autowired\n    private AppScrollRestartService appScrollRestartService;\n\n    @Resource\n    private TaskService taskService;\n\n    @RequestMapping(\"/appDaily\")\n    public ModelAndView appDaily(HttpServletRequest request, HttpServletResponse response, Model model) throws ParseException {\n        AppUser userInfo = getUserInfo(request);\n        logger.warn(\"user {} want to send appdaily\", userInfo.getName());\n        if (ConstUtils.SUPER_MANAGER.contains(userInfo.getName())) {\n            Date startDate;\n            Date endDate;\n            String startDateParam = request.getParameter(\"startDate\");\n            String endDateParam = request.getParameter(\"endDate\");\n            if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n                endDate = new Date();\n                startDate = DateUtils.addDays(endDate, -1);\n            } else {\n                startDate = DateUtil.parseYYYY_MM_dd(startDateParam);\n                endDate = DateUtil.parseYYYY_MM_dd(endDateParam);\n            }\n            long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n            if (appId > 0) {\n                appDailyDataCenter.sendAppDailyEmail(appId, startDate, endDate);\n            } else {\n                appDailyDataCenter.sendAppDailyEmail();\n            }\n            model.addAttribute(\"msg\", \"success!\");\n        } else {\n            model.addAttribute(\"msg\", \"no power!\");\n        }\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 审核列表\n     *\n     * @param status 审核状态\n     * @param type   申请类型\n     */\n    @RequestMapping(value = \"/auditList\")\n    public ModelAndView doAppAuditList(HttpServletRequest request, HttpServletResponse response, Model model,\n                                       Integer status, Integer type, Long auditId, Long operateId, Long userId,\n                                       String startDate, String endDate, Long adminId) {\n        AppAuditType[] appAuditTypes = AppAuditType.values();\n        model.addAttribute(\"appAuditTypeMap\", Arrays.stream(appAuditTypes).collect(Collectors.toMap(AppAuditType::getValue, Function.identity())));\n        List<AppUser> userList = userService.getAllUser();\n        model.addAttribute(\"userMap\", userList.stream().collect(Collectors.toMap(AppUser::getId, Function.identity())));\n        //获取审核列表\n        List<AppAudit> list = appService.getAppAudits(status, type, auditId, userId, operateId);\n\n        //任务汇总\n        AppUser currentUser = getUserInfo(request);\n        Date startTime = null;\n        Date endTime = null;\n        if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {\n            startTime = DateUtil.getDateByFormat(startDate, \"yyyy-MM-dd\");\n            endTime = DateUtil.getDateByFormat(endDate, \"yyyy-MM-dd\");\n        }\n        Map<String, Object> statusStatisMap = appService.getStatisticGroupByStatus(null, adminId, startTime, endTime);\n        Map<String, Object> typeStatisMap = appService.getStatisticGroupByType(null, adminId, startTime, endTime);\n\n        model.addAttribute(\"statusStatisMap\", statusStatisMap);\n        model.addAttribute(\"typeStatisMap\", typeStatisMap);\n        model.addAttribute(\"list\", list);\n\n        model.addAttribute(\"userId\", userId);\n        model.addAttribute(\"operateId\", operateId);\n        model.addAttribute(\"status\", status);\n        model.addAttribute(\"type\", type);\n        model.addAttribute(\"auditId\", auditId);\n        model.addAttribute(\"checkActive\", SuccessEnum.SUCCESS.value());\n        model.addAttribute(\"startDate\", startDate);\n        model.addAttribute(\"endDate\", endDate);\n        model.addAttribute(\"adminId\", adminId);\n\n        return new ModelAndView(\"manage/appAudit/list\");\n    }\n\n\n    @RequestMapping(value = \"/audit/update\")\n    public ModelAndView updateAppAudit(HttpServletRequest request, HttpServletResponse response, Model model, Long auditId, int status, int type) {\n        AppUser appUser = getUserInfo(request);\n\n\n        return new ModelAndView(\"manage/appAudit/list\");\n    }\n\n    /**\n     * 处理应用配置修改\n     *\n     * @param appAuditId 审批id\n     */\n    @RequestMapping(value = \"/initAppConfigChange\")\n    public ModelAndView doInitAppConfigChange(HttpServletRequest request,\n                                              HttpServletResponse response, Model model, Long appAuditId) {\n        // 申请原因\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\n        model.addAttribute(\"appAudit\", appAudit);\n\n        // 用第一个参数存实例id\n        Long instanceId = NumberUtils.toLong(appAudit.getParam1());\n        Map<String, String> redisConfigList = redisCenter.getRedisConfigList(instanceId.intValue());\n        model.addAttribute(\"redisConfigList\", redisConfigList);\n        model.addAttribute(\"instanceId\", instanceId);\n\n        // 实例列表\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appAudit.getAppId());\n        model.addAttribute(\"instanceList\", instanceList);\n        model.addAttribute(\"appId\", appAudit.getAppId());\n        model.addAttribute(\"appAuditId\", appAuditId);\n\n        // 修改配置的键值对\n        model.addAttribute(\"appConfigKey\", appAudit.getParam2());\n        model.addAttribute(\"appConfigValue\", appAudit.getParam3());\n\n        return new ModelAndView(\"manage/appAudit/initAppConfigChange\");\n    }\n\n    /**\n     * 添加应用配置修改\n     *\n     * @param appId          应用id\n     * @param appConfigKey   配置项\n     * @param appConfigValue 配置值\n     * @param appAuditId     审批id\n     */\n    @RequestMapping(value = \"/addAppConfigChange\")\n    public ModelAndView doAddAppConfigChange(HttpServletRequest request,\n                                             HttpServletResponse response, Model model, Long appId,\n                                             String appConfigKey, String appConfigValue, Long appAuditId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} change appConfig:appId={};key={};value={},appAuditId:{}\", appUser.getName(), appId, appConfigKey, appConfigValue, appAuditId);\n        boolean isModify = false;\n        if (appId != null && StringUtils.isNotBlank(appConfigKey)) {\n            try {\n                if(appAuditId != null ){\n                    appAuditDao.updateAppAuditOperateUser(appAuditId, appUser.getId());\n                }\n                isModify = appDeployCenter.modifyAppConfig(appId, appAuditId, appConfigKey, appConfigValue);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        logger.warn(\"user {} change appConfig:appId={};key={};value={},appAuditId:{},result is:{}\", appUser.getName(), appId, appConfigKey, appConfigValue, appAuditId, isModify);\n        return new ModelAndView(\"redirect:/manage/app/auditList\");\n    }\n\n    /**\n     * 初始化水平扩容申请\n     */\n    @RequestMapping(value = \"/initHorizontalScaleApply\")\n    public ModelAndView doInitHorizontalScaleApply(HttpServletRequest request, HttpServletResponse response, Model model, Long appAuditId) {\n        appAuditDao.updateAppAuditOperateUser(appAuditId, getUserInfo(request).getId());\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\n        model.addAttribute(\"appAudit\", appAudit);\n        model.addAttribute(\"appId\", appAudit.getAppId());\n        return new ModelAndView(\"manage/appAudit/initHorizontalScaleApply\");\n    }\n\n\n    /**\n     * 添加水平扩容节点\n     *\n     * @return\n     */\n    @RequestMapping(value = \"/addHorizontalNodes\")\n    public ModelAndView doAddHorizontalNodes(HttpServletRequest request,\n                                             HttpServletResponse response, Model model, String masterSizeSlave,\n                                             Long appAuditId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} addHorizontalNodes:{}\", appUser.getName(), masterSizeSlave);\n        boolean isAdd = false;\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\n\n        String[] nodes = masterSizeSlave.split(ConstUtils.NEXT_LINE);\n        for (String node : nodes) {\n            if (!StringUtils.isEmpty(node.trim())) {\n                // 解析配置\n                String[] configArr = node.trim().split(ConstUtils.COLON);\n                String masterHost = configArr[0];\n                String memSize = configArr[1];\n                int memSizeInt = NumberUtils.toInt(memSize);\n                String slaveHost = null;\n                if (configArr.length >= 3) {\n                    slaveHost = configArr[2];\n                }\n                try {\n                    isAdd = appDeployCenter.addHorizontalNodes(appAudit.getAppId(), masterHost, slaveHost, memSizeInt);\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                }\n            }\n        }\n        logger.warn(\"addAppClusterSharding:{}, result is {}\", masterSizeSlave, isAdd);\n        model.addAttribute(\"status\", isAdd ? 1 : 0);\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 检测水平扩容节点\n     *\n     * @param masterSizeSlave\n     * @param appAuditId\n     * @return\n     */\n    @RequestMapping(value = \"/checkHorizontalNodes\")\n    public ModelAndView doCheckHorizontalNodes(HttpServletRequest request,\n                                               HttpServletResponse response, Model model, String masterSizeSlave,\n                                               Long appAuditId) {\n        DataFormatCheckResult dataFormatCheckResult = null;\n        try {\n            dataFormatCheckResult = appDeployCenter.checkHorizontalNodes(appAuditId, masterSizeSlave);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            dataFormatCheckResult = DataFormatCheckResult.fail(ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n        }\n        model.addAttribute(\"status\", dataFormatCheckResult.getStatus());\n        model.addAttribute(\"message\", dataFormatCheckResult.getMessage());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 水平扩容初始化\n     *\n     * @param appAuditId\n     */\n    @RequestMapping(value = \"/handleHorizontalScale\")\n    public ModelAndView doHandleHorizontalScale(HttpServletRequest request,\n                                                HttpServletResponse response, Model model, Long appAuditId) {\n        // 1. 审批\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\n        model.addAttribute(\"appAudit\", appAudit);\n        model.addAttribute(\"appId\", appAudit.getAppId());\n\n        // 2. 进度\n        List<InstanceReshardProcess> instanceReshardProcessList = instanceReshardProcessDao.getByAuditId(appAudit.getId());\n        model.addAttribute(\"instanceReshardProcessList\", instanceReshardProcessList);\n\n        // 3. 实例列表和统计\n        fillAppInstanceStats(appAudit.getAppId(), model);\n        // 4. 实例所在机器信息\n        fillAppMachineStat(appAudit.getAppId(), model);\n\n        return new ModelAndView(\"manage/appAudit/handleHorizontalScale\");\n    }\n\n    /**\n     * 显示reshard进度\n     */\n    @RequestMapping(value = \"/showReshardProcess\")\n    public ModelAndView doShowReshardProcess(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long auditId = NumberUtils.toLong(request.getParameter(\"auditId\"));\n        List<InstanceReshardProcess> instanceReshardProcessList = instanceReshardProcessDao.getByAuditId(auditId);\n        write(response, JSONObject.toJSONString(instanceReshardProcessList));\n        return null;\n    }\n\n    /**\n     * 水平扩容配置检查\n     *\n     * @param sourceId   源实例ID\n     * @param targetId   目标实例ID\n     * @param startSlot  开始slot\n     * @param endSlot    结束slot\n     * @param appId      应用id\n     * @param appAuditId 审批id\n     * @return\n     */\n    @RequestMapping(value = \"/checkHorizontalScale\")\n    public ModelAndView doCheckHorizontalScale(HttpServletRequest request, HttpServletResponse response, Model model,\n                                               long sourceId, long targetId, int startSlot, int endSlot, long appId, long appAuditId, int migrateType) {\n        HorizontalResult horizontalResult = appDeployCenter.checkHorizontal(appId, appAuditId, sourceId, targetId,\n                startSlot, endSlot, migrateType);\n        model.addAttribute(\"status\", horizontalResult.getStatus());\n        model.addAttribute(\"message\", horizontalResult.getMessage());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 开始水平扩容\n     *\n     * @param sourceId   源实例ID\n     * @param targetId   目标实例ID\n     * @param startSlot  开始slot\n     * @param endSlot    结束slot\n     * @param appId      应用id\n     * @param appAuditId 审批id\n     * @return\n     */\n    @RequestMapping(value = \"/startHorizontalScale\")\n    public ModelAndView doStartHorizontalScale(HttpServletRequest request, HttpServletResponse response, Model model,\n                                               long sourceId, long targetId, int startSlot, int endSlot, long appId, long appAuditId, int migrateType) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} horizontalScaleApply appId {} appAuditId {} sourceId {} targetId {} startSlot {} endSlot {}\",\n                appUser.getName(), appId, appAuditId, sourceId, targetId, startSlot, endSlot);\n        HorizontalResult horizontalResult = appDeployCenter.startHorizontal(appId, appAuditId, sourceId, targetId,\n                startSlot, endSlot, migrateType);\n        model.addAttribute(\"status\", horizontalResult.getStatus());\n        model.addAttribute(\"message\", horizontalResult.getMessage());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 重试水平扩容\n     *\n     * @param instanceReshardProcessId\n     * @return\n     */\n    @RequestMapping(value = \"/retryHorizontalScale\")\n    public ModelAndView retryHorizontalScale(HttpServletRequest request, HttpServletResponse response, Model model, int instanceReshardProcessId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} retryHorizontalScale id {}\", appUser.getName(), instanceReshardProcessId);\n        HorizontalResult horizontalResult = appDeployCenter.retryHorizontal(instanceReshardProcessId);\n        model.addAttribute(\"status\", horizontalResult.getStatus());\n        model.addAttribute(\"message\", horizontalResult.getMessage());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 处理应用扩容\n     *\n     * @param appAuditId 审批id\n     */\n    @RequestMapping(value = \"/initAppScaleApply\")\n    public ModelAndView doInitAppScaleApply(HttpServletRequest request, HttpServletResponse response, Model model, Long appAuditId) {\n        // 申请原因\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\n        model.addAttribute(\"appAudit\", appAudit);\n\n        // 实例列表和统计\n        fillAppInstanceStats(appAudit.getAppId(), model);\n        // 实例所在机器信息\n        fillAppMachineStat(appAudit.getAppId(), model);\n\n        long appId = appAudit.getAppId();\n        AppDesc appDesc = appService.getByAppId(appId);\n        model.addAttribute(\"appAuditId\", appAuditId);\n        model.addAttribute(\"appId\", appAudit.getAppId());\n        model.addAttribute(\"appDesc\", appDesc);\n\n        return new ModelAndView(\"manage/appAudit/initAppScaleApply\");\n    }\n\n    /**\n     * 添加扩容配置\n     *\n     * @param appScaleText 扩容配置\n     * @param appAuditId   审批id\n     */\n    @RequestMapping(value = \"/addAppScaleApply\")\n    public ModelAndView doAddAppScaleApply(HttpServletRequest request,\n                                           HttpServletResponse response, Model model, String appScaleText,\n                                           Long appAuditId, Long appId) {\n        AppUser appUser = getUserInfo(request);\n        logger.error(\"user {} appScaleApplay : appScaleText={},appAuditId:{}\", appUser.getName(), appScaleText, appAuditId);\n        boolean isSuccess = false;\n        int mem = NumberUtils.toInt(appScaleText, 0);\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appAuditId != null && StringUtils.isNotBlank(appScaleText) && appDesc != null) {\n            try {\n                isSuccess = appDeployCenter.verticalExpansion(appId, appAuditId, appUser.getId(), mem);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        } else {\n            logger.error(\"appScaleApplay error param: appScaleText={},appAuditId:{},appId:{}\", appScaleText, appAuditId, appId);\n        }\n        logger.error(\"user {} appScaleApplay: appScaleText={},appAuditId:{}, result is {}\", appUser.getName(), appScaleText, appAuditId, isSuccess);\n        return new ModelAndView(\"redirect:/manage/app/auditList\");\n    }\n\n    /**\n     * 初始化部署应用\n     *\n     * @param appAuditId 审批id\n     * @return\n     */\n    @RequestMapping(value = \"/initAppDeploy\")\n    public ModelAndView doInitAppDeploy(HttpServletRequest request, HttpServletResponse response, Model model, Long appAuditId) {\n        long appId;\n        AppDesc appDesc;\n        if (appAuditId == null) {\n            appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n            appDesc = appService.getByAppId(appId);\n        } else {\n            // 申请原因\n            AppAudit appAudit = appService.getAppAuditById(appAuditId);\n            appId = appAudit.getAppId();\n            model.addAttribute(\"appAudit\", appAudit);\n            appDesc = appService.getByAppId(appId);\n        }\n\n        // 获取所有Redis版本\n        List<SystemResource> allRedisVersion = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\n        // 机器列表\n        List<MachineStats> machineList = machineCenter.getMachineStats(null, null, null, null, null, null, null);\n        // 获取机器信息\n        Map<String, Integer> machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n\n        if (appDesc.getVersionId() > 0) {\n            model.addAttribute(\"version\", resourceService.getResourceById(appDesc.getVersionId()));\n        }\n        List<MachineRoom> roomList = machineCenter.getEffectiveRoom();\n        model.addAttribute(\"roomList\", roomList);\n\n        model.addAttribute(\"machineList\", machineList);\n        model.addAttribute(\"machineInstanceCountMap\", machineInstanceCountMap);\n        model.addAttribute(\"appAuditId\", appAuditId);\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"md5password\", AuthUtil.getAppIdMD5(String.valueOf(appId)));\n        model.addAttribute(\"appDesc\", appService.getByAppId(appId));\n        model.addAttribute(\"versionList\", allRedisVersion);\n        model.addAttribute(\"importId\", request.getParameter(\"importId\"));\n\n        return new ModelAndView(\"manage/appAudit/deploy/initAppDeploy\");\n    }\n\n    @RequestMapping(value = \"/generateDeployInfo\", method = {RequestMethod.POST, RequestMethod.GET})\n    public ModelAndView generateDeployInfo(HttpServletResponse response,\n                                           int type,\n                                           int hasSalve,\n                                           int maxMemory,\n                                           int redisNum,\n                                           int sentinelNum,\n                                           int pikaNum,\n                                           int twemproxyNum,\n                                           String appDeployInfo,\n                                           String redisMachines,\n                                           String sentinelMachines,\n                                           String twemproxyMachines,\n                                           String pikaMachines) {\n        // 1.根据应用类型获取部署拓扑信息\n        logger.info(\"type:{} ,hasSalve:{} ,maxMemory:{}MB \", type, hasSalve, maxMemory);\n        logger.info(\"redisMachines:{} ,num:{} \", redisMachines, redisNum);\n        logger.info(\"sentinelMachines:{} ,num:{} \", sentinelMachines, sentinelNum);\n        logger.info(\"twemproxyMachines:{} ,num:{} \", twemproxyMachines, twemproxyNum);\n        logger.info(\"pikaMachines:{} ,num:{} \", pikaMachines, pikaNum);\n\n        // 2.根据应用类型获取部署拓扑信息\n        List<DeployInfo> deployInfoList = new ArrayList<DeployInfo>();\n\n        Map<String, DeployInfoStat> machineDeployStatMap = new HashMap<>();\n        Map<String, Integer> machineInstanceCountMap = new HashMap<>();\n        List<MachineMemStatInfo> machineMemStatInfo = new ArrayList<>();\n        Set<String> ipList = new HashSet<String>();\n        String deployStatus = DeployInfoEnum.SUCCESS.getValue();\n        try {\n            List<String> redisMachinelist = Arrays.asList(redisMachines.split(\";\"));\n            List<String> sentinelMachinelist = Arrays.asList(sentinelMachines.split(\";\"));\n            List<String> twemproxyMachinelist = Arrays.asList(twemproxyMachines.split(\";\"));\n            List<String> pikaMachinelist = Arrays.asList(pikaMachines.split(\";\"));\n\n            switch (type) {\n                case 2: // redis cluster\n                    appService.generateInstanceInfo(redisMachinelist, NodeEnum.REDIS_NODE.getValue(), type, redisMachinelist.size() * redisNum, maxMemory, hasSalve, deployInfoList);\n                    ipList.addAll(redisMachinelist);\n                    break;\n                case 5: // sentinel + redis\n                    appService.generateInstanceInfo(redisMachinelist, NodeEnum.REDIS_NODE.getValue(), type, 1, maxMemory, hasSalve, deployInfoList);\n                    appService.generateProxyinfo(sentinelMachinelist, NodeEnum.SENTINEL_NODE.getValue(), type, sentinelMachinelist.size() * sentinelNum, deployInfoList);\n                    ipList.addAll(redisMachinelist);\n                    ipList.addAll(sentinelMachinelist);\n                    break;\n                case 6:// standalone + redis\n                    deployInfoList.add(new DeployInfo(type, redisMachinelist.get(0), maxMemory));\n                    ipList.addAll(redisMachinelist);\n                    break;\n                case 7: //twemproxy + redis\n                    appService.generateInstanceInfo(redisMachinelist, NodeEnum.REDIS_NODE.getValue(), type, redisMachinelist.size() * redisNum, maxMemory, hasSalve, deployInfoList);\n                    appService.generateProxyinfo(sentinelMachinelist, NodeEnum.SENTINEL_NODE.getValue(), type, sentinelMachinelist.size() * sentinelNum, deployInfoList);\n                    appService.generateProxyinfo(twemproxyMachinelist, NodeEnum.TWEMPROXY_NODE.getValue(), type, twemproxyMachinelist.size() * twemproxyNum, deployInfoList);\n                    ipList.addAll(redisMachinelist);\n                    ipList.addAll(sentinelMachinelist);\n                    ipList.addAll(twemproxyMachinelist);\n                    break;\n                case 8: //sentinel + pika\n                    appService.generateInstanceInfo(pikaMachinelist, NodeEnum.PIKA_NODE.getValue(), type, 1, maxMemory, hasSalve, deployInfoList);\n                    appService.generateProxyinfo(sentinelMachinelist, NodeEnum.SENTINEL_NODE.getValue(), type, sentinelMachinelist.size() * sentinelNum, deployInfoList);\n                    ipList.addAll(pikaMachinelist);\n                    ipList.addAll(sentinelMachinelist);\n                    break;\n                case 9: //twemproxy + pika\n                    appService.generateInstanceInfo(pikaMachinelist, NodeEnum.PIKA_NODE.getValue(), type, pikaMachinelist.size() * pikaNum, maxMemory, hasSalve, deployInfoList);\n                    appService.generateProxyinfo(sentinelMachinelist, NodeEnum.SENTINEL_NODE.getValue(), type, sentinelMachinelist.size() * sentinelNum, deployInfoList);\n                    appService.generateProxyinfo(twemproxyMachinelist, NodeEnum.TWEMPROXY_NODE.getValue(), type, twemproxyMachinelist.size() * twemproxyNum, deployInfoList);\n                    ipList.addAll(pikaMachinelist);\n                    ipList.addAll(sentinelMachinelist);\n                    ipList.addAll(twemproxyMachinelist);\n                    break;\n                default:\n                    break;\n            }\n            if (CollectionUtils.isEmpty(deployInfoList)) {\n                deployStatus = DeployInfoEnum.EMPTY.getValue();\n            }\n\n            // 收集机器资源信息\n            if (!CollectionUtils.isEmpty(ipList)) {\n                machineDeployStatMap = appService.getMachineDeployStat(ipList, deployInfoList);\n                machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n                machineMemStatInfo = machineCenter.getValidMachineMemByIpList(new ArrayList(ipList));\n            }\n        } catch (Exception e) {\n            deployStatus = DeployInfoEnum.EXCEPTION.getValue();\n            logger.error(e.getMessage(), e);\n        }\n\n        // 3. response\n        JSONObject json = new JSONObject();\n        json.put(\"result\", deployStatus);\n        json.put(\"deployInfoList\", deployInfoList);\n        json.put(\"resMachines\", machineMemStatInfo);\n        json.put(\"machineDeployStatMap\", machineDeployStatMap);\n        json.put(\"machineInstanceCountMap\", machineInstanceCountMap);\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n    /**\n     * 应用部署task\n     *\n     * @param request\n     * @param appAuditId\n     * @param type\n     * @param maxMemory\n     * @param redisNum\n     * @param sentinelNum\n     * @param pikaNum\n     * @param twemproxyNum\n     * @param redisMachines\n     * @param sentinelMachines\n     * @param twemproxyMachines\n     * @param pikaMachines\n     * @return\n     */\n    @RequestMapping(value = \"/addAppDeployTask\", method = {RequestMethod.POST})\n    public ModelAndView doAddAppDeployTask(HttpServletRequest request,\n                                           HttpServletResponse response,\n                                           Long appAuditId,\n                                           int isSetPasswd,\n                                           int versionId,\n                                           int importantLevel,\n                                           long appid,\n                                           int type,\n                                           int maxMemory,\n                                           int redisNum,\n                                           int sentinelNum,\n                                           int pikaNum,\n                                           int twemproxyNum,\n                                           @RequestParam(required = false)  String appDeployInfo,\n                                           String redisMachines,\n                                           String sentinelMachines,\n                                           String twemproxyMachines,\n                                           String pikaMachines,\n                                           String customPassword\n                                        ) {\n        JSONObject json = new JSONObject();\n        long taskid = -1;//任务流跳转\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} appid:{} ,appAuditId:{}, importantLevel:{} ,isSetPasswd:{},versionId:{},customPassword:{}\", appUser.getName(), appid, appAuditId, importantLevel, isSetPasswd, versionId, customPassword);\n        logger.info(\"type:{} ,maxMemory:{} MB \", type, maxMemory);\n        logger.info(\"appDeployInfo :{} \", appDeployInfo);\n        logger.info(\"redisMachines:{} ,num:{} \", redisMachines, redisNum);\n        logger.info(\"sentinelMachines:{} ,num:{} \", sentinelMachines, sentinelNum);\n        logger.info(\"twemproxyMachines:{} ,num:{} \", twemproxyMachines, twemproxyNum);\n        logger.info(\"pikaMachines:{} ,num:{} \", pikaMachines, pikaNum);\n        try {\n            List<String> appDeployInfolist = new ArrayList<>();\n            if(StringUtils.isNotEmpty(appDeployInfo)){\n                appDeployInfolist = Arrays.asList(appDeployInfo.split(\"\\n\"));\n            }\n            List<String> redisMachinelist = Arrays.asList(redisMachines.split(\";\"));\n            List<String> sentinelMachinelist = Arrays.asList(sentinelMachines.split(\";\"));\n            List<String> twemproxyMachinelist = Arrays.asList(twemproxyMachines.split(\";\"));\n            List<String> pikaMachinelist = Arrays.asList(pikaMachines.split(\";\"));\n\n            // 1.保存应用信息\n            AppDesc appDesc = appService.getByAppId(appid);\n            if (appDesc != null) {\n                appDesc.setImportantLevel(importantLevel);\n                appDesc.setVersionId(versionId);\n                appDesc.setType(type);\n                if(StringUtils.isNotBlank(customPassword)){\n                    appDesc.setCustomPassword(customPassword);\n                }\n                appService.updateWithCustomPwd(appDesc);\n            } else {\n                json.put(\"status\", \"fail\");\n                json.put(\"message\", \"部署失败:获取应用信息为空,请检查服务日志!\");\n                sendMessage(response, json.toString());\n                return null;\n            }\n            // 2.获取Redis版本信息\n            SystemResource redisResource = resourceService.getResourceById(versionId);\n\n            if (redisResource == null) {\n                json.put(\"status\", \"fail\");\n                json.put(\"message\", \"部署失败:redis版本不存在,请检查服务日志!\");\n                sendMessage(response, json.toString());\n                return null;\n            }\n            if (appAuditId != null) {\n                appAuditDao.updateAppAuditOperateUser(appAuditId, appUser.getId());\n            } else {\n                appAuditId = -1l;\n            }\n\n            //2.根据应用类型获取部署拓扑信息\n            switch (type) {\n                case 2: //  部署task :redis cluster\n                    taskid = taskService.addRedisClusterAppTask(appid, appAuditId, maxMemory, appDeployInfolist, redisMachinelist, redisNum, redisResource.getName(), -1);\n                    break;\n                case 5: //  部署task :sentinel + redis\n                    taskid = taskService.addRedisSentinelAppTask(appid, appAuditId, maxMemory, redisMachinelist, sentinelMachinelist, redisNum, sentinelNum, redisResource.getName(), -1);\n                    break;\n                case 6://   部署task :standalone\n                    taskid = taskService.addRedisStandaloneAppTask(appid, appAuditId, maxMemory, redisMachinelist, 1, redisResource.getName(), -1);\n                    break;\n                case 7: //  部署task :twemproxy + redis\n                    taskid = taskService.addTwemproxyAppTask(appid, appAuditId, maxMemory, redisMachinelist, sentinelMachinelist,\n                            twemproxyMachinelist, redisNum, sentinelNum, twemproxyNum, false, redisResource.getName(), -1);\n                    break;\n                case 8: //  部署task :sentinel + pika\n                    taskid = taskService.addPikaSentinelAppTask(appid, appAuditId, maxMemory, pikaMachinelist, sentinelMachinelist,\n                            pikaNum, sentinelNum, -1);\n                    break;\n                case 9: //  部署task :twemproxy + pika\n                    taskid = taskService.addTwemproxyPikaTask(appid, appAuditId, maxMemory, pikaMachinelist, sentinelMachinelist, twemproxyMachinelist,\n                            pikaNum, sentinelNum, twemproxyNum, false, -1);\n                    break;\n                default:\n                    break;\n            }\n\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            json.put(\"status\", \"fail\");\n            json.put(\"message\", \"部署失败:系统存在异常,请检查服务日志!\");\n            sendMessage(response, json.toString());\n            return null;\n        }\n        json.put(\"status\", \"success\");\n        json.put(\"taskid\", taskid); // 用于跳转到任务流页面\n        json.put(\"message\", \"应用任务流开始构建，即将跳转任务流部署页面!\");\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n    /**\n     * @Description: 生成部署信息\n     * @Author: caoru\n     * @CreateDate: 2018/9/25 20:41\n     */\n\n    @RequestMapping(value = \"/getDeployInfo\", method = {RequestMethod.POST, RequestMethod.GET})\n    public ModelAndView getDeployInfo(HttpServletResponse response, Model model,\n                                      Integer type,\n                                      Integer isSalve,\n                                      String room,\n                                      Double size,\n                                      Integer machineNum,\n                                      Integer instanceNum,\n                                      Integer useType,\n                                      String machines,\n                                      String excludeMachines,\n                                      String sentinelMachines) {\n\n        try {\n            JSONObject json = new JSONObject();\n            String result;\n            //参数校验是否为空\n            if (type == null || isSalve == null || room == null || size == null || machineNum == null || instanceNum == null || useType == null) {\n                logger.info(\"参数为空\");\n                result = \"param is error or null\";\n                json.put(\"result\", result);\n                sendMessage(response, json.toString());\n                return new ModelAndView(\"\");\n            }\n            size *= 1024;\n            if (TypeUtil.isRedisSentinel(type)) {\n                if (sentinelMachines.isEmpty()) {\n                    logger.info(\"sentinel类型，请指定sentinel机器\");\n                    result = \"sentinel类型，请指定sentinel机器\";\n                    json.put(\"result\", result);\n                    sendMessage(response, json.toString());\n                    return new ModelAndView(\"\");\n                } else {\n                    StringBuilder resultBuilder = new StringBuilder();\n                    List<MachineMemStatInfo> sentinelMachineList = machineCenter.getValidMachineMemByIpList(Arrays.asList(sentinelMachines.split(\",\")));\n                    for (MachineMemStatInfo sentinelMachine : sentinelMachineList) {\n                        if (sentinelMachine.getCpu() - sentinelMachine.getInstanceNum() < 1) {\n                            //result += sentinelMachine.getIp() + \",\";\n                            resultBuilder.append(sentinelMachine.getIp()).append(\",\");\n                        }\n                    }\n                    result = resultBuilder.toString();\n                    if (result != null && !result.isEmpty()) {\n                        result = \"sentinel机器：\" + result;\n                        result += \"cpu核数不足\";\n                        json.put(\"result\", result);\n                        sendMessage(response, json.toString());\n                        return new ModelAndView(\"\");\n                    }\n                }\n            }\n            //存储部署信息\n            List<DeployInfo> deployInfoList = new ArrayList<DeployInfo>();\n            //存储满足条件的机器ip\n            List<MachineMemStatInfo> resMachines = new ArrayList<MachineMemStatInfo>();\n\n            result = appService.generateDeployInfo(type, isSalve, room, size, machineNum, instanceNum, useType, machines, excludeMachines, sentinelMachines, deployInfoList, resMachines);\n            if (!DeployInfoEnum.SUCCESS.getValue().equals(result)) {\n                logger.info(\"result: {}\", result);\n                json.put(\"result\", result);\n                sendMessage(response, json.toString());\n                return new ModelAndView(\"\");\n            }\n            Set<String> ipList = new HashSet<String>();\n            Iterator<MachineMemStatInfo> iterator = resMachines.iterator();\n            while (iterator.hasNext()) {\n                String ip = iterator.next().getIp();\n                if (ipList.contains(ip)) {\n                    iterator.remove();\n                } else {\n                    ipList.add(ip);\n                }\n            }\n            Map<String, DeployInfoStat> machineDeployStatMap = appService.getMachineDeployStat(ipList, deployInfoList);\n            Map<String, Integer> machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n\n            json.put(\"result\", result);\n            json.put(\"machineInstanceCountMap\", machineInstanceCountMap);\n            json.put(\"resMachines\", resMachines);\n            json.put(\"deployInfoList\", deployInfoList);\n            json.put(\"machineDeployStatMap\", machineDeployStatMap);\n            sendMessage(response, json.toString());\n            return new ModelAndView(\"\");\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 通过,获取驳回申请\n     *\n     * @param status       审批状态\n     * @param appAuditId   审批id\n     * @param refuseReason 应用id\n     * @return\n     */\n    @RequestMapping(value = \"/addAuditStatus\")\n    public ModelAndView doAddAuditStatus(HttpServletRequest request, HttpServletResponse response, Model model, Integer status, Long appAuditId, String refuseReason, Integer type) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} addAuditStatus: status={},appAuditId:{},refuseReason:{}\", appUser.getName(), status, appAuditId, refuseReason);\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\n        Long appId = appAudit.getAppId();\n\n        // 通过或者驳回并记录日志\n        appService.updateAppAuditStatus(appAuditId, appId, status, appUser);\n\n        // 记录驳回原因\n        if (AppCheckEnum.APP_REJECT.value().equals(status)) {\n            appAudit.setRefuseReason(refuseReason);\n            appService.updateRefuseReason(appAudit, getUserInfo(request));\n        }\n\n        // 发邮件统计\n        if (AppCheckEnum.APP_PASS.value().equals(status) || AppCheckEnum.APP_REJECT.value().equals(status)) {\n            AppDesc appDesc = appService.getByAppId(appId);\n            AppUser applyUser = userService.get(appAudit.getUserId());\n            if(appDesc != null){\n                appEmailUtil.noticeAppResultWithApplyUser(applyUser, appDesc, appService.getAppAuditById(appAuditId));\n            }else{\n                appEmailUtil.noticeAuditResult(applyUser, appService.getAppAuditById(appAuditId));\n            }\n        }\n        //没有传入type参数，只有在申请应用时会传入\n        if (type == null) {\n            // 批准成功直接跳转\n            if (AppCheckEnum.APP_PASS.value().equals(status)) {\n                return new ModelAndView(\"redirect:/manage/app/auditList\");\n            }\n        }\n\n        if (AppCheckEnum.APP_ALLOCATE_RESOURCE.value().equals(status) && AppAuditType.APP_DIAGNOSTIC.getValue() == type) {\n            return new ModelAndView(\"redirect:/manage/app/tool/index\");\n        }\n\n        if (AppCheckEnum.APP_ALLOCATE_RESOURCE.value().equals(status) && AppAuditType.APP_OFFLINE.getValue() == type) {\n            return new ModelAndView(\"redirect:/manage/total/list?appParam=\" + appId);\n        }\n\n        if (AppCheckEnum.APP_ALLOCATE_RESOURCE.value().equals(status) && AppAuditType.FLUSHALL_DATA.getValue() == type) {\n            return new ModelAndView(\"redirect:/manage/app/tool/index?tabTag=deleteKey\");\n        }\n\n        if (AppCheckEnum.APP_ALLOCATE_RESOURCE.value().equals(status) && AppAuditType.APP_MIGRATE.getValue() == type) {\n            return new ModelAndView(\"redirect:/data/migrate/init\");\n        }\n\n        if (AppCheckEnum.APP_ALLOCATE_RESOURCE.value().equals(status) && AppAuditType.APP_IMPORT.getValue() == type) {\n            return new ModelAndView(\"redirect:/import/app/init?importId=\" + appAudit.getParam1());\n        }\n\n        if (AppCheckEnum.APP_ALLOCATE_RESOURCE.value().equals(status) && AppAuditType.SCAN_CLEAN.getValue() == type) {\n            return new ModelAndView(\"redirect:/manage/app/tool/index?tabTag=scanClean\");\n        }\n\n        write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\n        return null;\n    }\n\n    /**\n     * 下线应用\n     *\n     * @param appId\n     * @return\n     */\n    @RequestMapping(value = \"/offLine\")\n    public ModelAndView offLineApp(HttpServletRequest request,\n                                   HttpServletResponse response, Model model, Long appId, Long appAuditId) {\n        AppUser userInfo = getUserInfo(request);\n        logger.warn(\"user {} hope to offline appId: {}\", userInfo.getName(), appId);\n        long taskId = appDeployCenter.offLineApp(appId, userInfo, appAuditId);\n        if (appAuditId != null) {\n            appAuditDao.updateTaskId(appAuditId, taskId);\n        }\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"taskId\", taskId);\n        model.addAttribute(\"message\", \"下线任务已提交，即将跳转任务流页面， taskId:\" + taskId);\n        logger.warn(\"user {} offline appId: {}, taskId is {}\", userInfo.getName(), appId, taskId);\n        return new ModelAndView();\n    }\n\n    /**\n     * 实例机器信息\n     *\n     * @param appId\n     * @param model\n     */\n    private void fillAppMachineStat(Long appId, Model model) {\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n\n        Map<String, MachineStats> machineStatsMap = new HashMap<String, MachineStats>();\n        Map<String, Long> machineCanUseMem = new HashMap<String, Long>();\n\n        for (InstanceInfo instanceInfo : instanceList) {\n            if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\n                continue;\n            }\n            String ip = instanceInfo.getIp();\n            if (machineStatsMap.containsKey(ip)) {\n                continue;\n            }\n            MachineStats machineStats = machineCenter.getMachineMemoryDetail(ip);\n            // 机器ip可能下线，查不到数据\n            if (machineStats != null) {\n                machineStatsMap.put(ip, machineStats);\n                machineCanUseMem.put(ip, machineStats.getMachineMemInfo().getLockedMem());\n            }\n        }\n        model.addAttribute(\"machineCanUseMem\", machineCanUseMem);\n        model.addAttribute(\"machineStatsMap\", machineStatsMap);\n    }\n\n\n    /**\n     * 应用运维\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/index\")\n    public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model, Long appId, String tabTag) {\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"tabTag\", tabTag);\n        return new ModelAndView(\"manage/appOps/appOpsIndex\");\n    }\n\n    /**\n     * 应用机器运维\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/machine\")\n    public ModelAndView appMachine(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {\n        if (appId != null && appId > 0) {\n            List<MachineStats> appMachineList = appService.getAppMachineDetail(appId);\n            model.addAttribute(\"appMachineList\", appMachineList);\n            AppDesc appDesc = appService.getByAppId(appId);\n            model.addAttribute(\"appDesc\", appDesc);\n        }\n        return new ModelAndView(\"manage/appOps/appMachine\");\n    }\n\n    /**\n     * 应用实例运维\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/instance\")\n    public ModelAndView appInstance(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {\n        if (appId != null && appId > 0) {\n            AppDesc appDesc = appService.getByAppId(appId);\n            model.addAttribute(\"appDesc\", appDesc);\n            //实例信息和统计\n            fillAppInstanceStats(appId, model);\n\n            model.addAttribute(\"k8sMachineMaps\", machineCenter.getK8sMachineMap());\n            //只有cluster类型才需要计算slot相关\n            if (TypeUtil.isRedisCluster(appDesc.getType())) {\n                // 计算丢失的slot区间\n                Map<String, String> lossSlotsSegmentMap = redisCenter.getClusterLossSlots(appId);\n                model.addAttribute(\"lossSlotsSegmentMap\", lossSlotsSegmentMap);\n            }\n        }\n        return new ModelAndView(\"manage/appOps/appInstance\");\n    }\n\n    /**\n     * 应用详细信息和各种申请记录\n     *\n     * @param appId\n     */\n    @RequestMapping(\"/detail\")\n    public ModelAndView appInfoAndAudit(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {\n        if (appId != null && appId > 0) {\n            List<AppAudit> appAuditList = appService.getAppAuditListByAppId(appId);\n            AppDesc appDesc = appService.getByAppId(appId);\n            appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));\n            model.addAttribute(\"appAuditList\", appAuditList);\n            model.addAttribute(\"appDesc\", appDesc);\n        }\n        return new ModelAndView(\"manage/appOps/appInfoAndAudit\");\n    }\n\n    /**\n     * redisCluster节点删除: forget + shutdown\n     *\n     * @param appId             应用id\n     * @param delNodeInstanceId 需要被forget的节点\n     * @return\n     */\n    @RequestMapping(\"/clusterDelNode\")\n    public ModelAndView clusterDelNode(HttpServletRequest request, HttpServletResponse response, Model model, Long appId,\n                                       int delNodeInstanceId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {}, clusterForget: appId:{}, instanceId:{}\", appUser.getName(), appId, delNodeInstanceId);\n        // 检测forget条件\n        ClusterOperateResult checkClusterForgetResult = ClusterOperateResult.fail(\"checkClusterForget Exception\");\n        try {\n            checkClusterForgetResult = redisDeployCenter.checkClusterForget(appId, delNodeInstanceId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (!checkClusterForgetResult.isSuccess()) {\n            model.addAttribute(\"success\", checkClusterForgetResult.getStatus());\n            model.addAttribute(\"message\", checkClusterForgetResult.getMessage());\n            return new ModelAndView(\"\");\n        }\n\n        // 执行delnode:forget + shutdown\n        ClusterOperateResult delNodeResult = ClusterOperateResult.fail(\"delNode Exception\");\n        try {\n            delNodeResult = redisDeployCenter.delNode(appId, delNodeInstanceId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        model.addAttribute(\"success\", delNodeResult.getStatus());\n        model.addAttribute(\"message\", delNodeResult.getMessage());\n        logger.warn(\"user {}, clusterForget: appId:{}, instanceId:{}, result is {}\", appUser.getName(), appId, delNodeInstanceId, delNodeResult.getStatus());\n\n        return new ModelAndView(\"\");\n\n    }\n\n    /**\n     * redisCluster从节点failover\n     *\n     * @param appId           应用id\n     * @param slaveInstanceId 从节点instanceId\n     * @return\n     */\n    @RequestMapping(\"/clusterSlaveFailOver\")\n    public void clusterSlaveFailOver(HttpServletRequest request, HttpServletResponse response, Model model, Long appId,\n                                     int slaveInstanceId) {\n        boolean success = false;\n        String failoverParam = request.getParameter(\"failoverParam\");\n        logger.warn(\"clusterSlaveFailOver: appId:{}, slaveInstanceId:{}, failoverParam:{}\", appId, slaveInstanceId, failoverParam);\n        if (appId != null && appId > 0 && slaveInstanceId > 0) {\n            try {\n                success = redisDeployCenter.clusterFailover(appId, slaveInstanceId, failoverParam);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        } else {\n            logger.error(\"error param clusterSlaveFailOver: appId:{}, slaveInstanceId:{}, failoverParam:{}\", appId, slaveInstanceId, failoverParam);\n        }\n        logger.warn(\"clusterSlaveFailOver: appId:{}, slaveInstanceId:{}, failoverParam:{}, result is {}\", appId, slaveInstanceId, failoverParam, success);\n        write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));\n    }\n\n\n    /**\n     * @Description: 自动生成slave ip\n     * @Author: caoru\n     * @CreateDate: 2018/10/11 11:50\n     */\n    @RequestMapping(value = \"/genSlaveIp\")\n    public void genSlaveIp(HttpServletRequest request, HttpServletResponse response, Model model, long appId,\n                           int masterInstanceId) {\n        String machineRes;\n        String result = \"success\";\n        JSONObject json = new JSONObject();\n        if (appId > 0 && masterInstanceId > 0) {\n            try {\n                machineRes = redisDeployCenter.genSlaveIp(appId, masterInstanceId);\n                if (StringUtils.isEmpty(machineRes)) {\n                    result = \"没有满足条件的机器可用\";\n                }\n                json.put(\"result\", result);\n                json.put(\"machineRes\", machineRes);\n                sendMessage(response, json.toString());\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n\n    }\n\n    /**\n     * 添加slave节点\n     *\n     * @param appId\n     * @param masterInstanceId\n     * @param slaveHost\n     * @return\n     */\n    @RequestMapping(value = \"/addSlave\")\n    public void addSlave(HttpServletRequest request, HttpServletResponse response, Model model, long appId,\n                         int masterInstanceId, String slaveHost) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} addSlave: appId:{},masterInstanceId:{},slaveHost:{}\", appUser.getName(), appId, masterInstanceId, slaveHost);\n        boolean success = false;\n        if (appId > 0 && StringUtils.isNotBlank(slaveHost) && masterInstanceId > 0) {\n            try {\n                success = redisDeployCenter.addSlave(appId, masterInstanceId, slaveHost);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        logger.warn(\"user {} addSlave: appId:{},masterInstanceId:{},slaveHost:{} result is {}\", appUser.getName(), appId, masterInstanceId, slaveHost, success);\n        write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));\n    }\n\n    /**\n     * 添加sentinel节点\n     *\n     * @param appId\n     * @param sentinelHost\n     * @return\n     */\n    @RequestMapping(value = \"/addSentinel\")\n    public void addSentinel(HttpServletRequest request, HttpServletResponse response, Model model, long appId, String sentinelHost) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} addSentinel: appId:{}, sentinelHost:{}\", appUser.getName(), appId, sentinelHost);\n        boolean success = false;\n        if (appId > 0 && StringUtils.isNotBlank(sentinelHost)) {\n            try {\n                success = redisDeployCenter.addSentinel(appId, sentinelHost);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        logger.warn(\"user {} addSentinel: appId:{}, sentinelHost:{} result is {}\", appUser.getName(), appId, sentinelHost, success);\n        write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));\n    }\n\n    /**\n     * 为失联的slot添加master节点\n     *\n     * @param appId\n     */\n    @RequestMapping(value = \"/addFailSlotsMaster\")\n    public void addFailSlotsMaster(HttpServletRequest request, HttpServletResponse response, Model model, long appId, String failSlotsMasterHost, int instanceId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} addFailSlotsMaster: appId:{}, instanceId {}, newMasterHost:{}\", appUser.getName(), appId, instanceId, failSlotsMasterHost);\n        RedisOperateEnum redisOperateEnum = RedisOperateEnum.FAIL;\n        if (appId > 0 && StringUtils.isNotBlank(failSlotsMasterHost)) {\n            try {\n                redisOperateEnum = redisDeployCenter.addSlotsFailMaster(appId, instanceId, failSlotsMasterHost);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        logger.warn(\"user {} addFailSlotsMaster: appId:{}, instanceId {}, newMasterHost:{} result is {}\", appUser.getName(), appId, instanceId, failSlotsMasterHost, redisOperateEnum.getValue());\n        write(response, String.valueOf(redisOperateEnum.getValue()));\n    }\n\n\n    /**\n     * sentinelFailOver操作\n     *\n     * @param appId\n     * @return\n     */\n    @RequestMapping(\"/sentinelFailOver\")\n    public void sentinelFailOver(HttpServletRequest request, HttpServletResponse response, Model model, long appId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} sentinelFailOver, appId:{}\", appUser.getName(), appId);\n        boolean success = false;\n        if (appId > 0) {\n            try {\n                success = redisDeployCenter.sentinelFailover(appId);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        } else {\n            logger.error(\"error param, sentinelFailOver: appId:{}\", appId);\n        }\n        logger.warn(\"user {} sentinelFailOver, appId:{}, result is {}\", appUser.getName(), appId, success);\n        write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));\n    }\n\n    @RequestMapping(\"/sentinelReset\")\n    public void sentinelReset(HttpServletRequest request, HttpServletResponse response, Model model, long appId) {\n        AppUser appUser = getUserInfo(request);\n        logger.warn(\"user {} sentinelReset, appId:{}\", appUser.getName(), appId);\n        boolean success = false;\n        if (appId > 0) {\n            try {\n                success = redisDeployCenter.sentinelReset(appId);\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        } else {\n            logger.error(\"error param, sentinelReset: appId:{}\", appId);\n        }\n        logger.warn(\"user {} sentinelReset, appId:{}, result is {}\", appUser.getName(), appId, success);\n        write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));\n    }\n\n    /**\n     * 应用重要性级别\n     */\n    @RequestMapping(value = \"/updateAppImportantLevel\")\n    public ModelAndView doUpdateAppImportantLevel(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        int importantLevel = NumberUtils.toInt(request.getParameter(\"importantLevel\"));\n        SuccessEnum successEnum = SuccessEnum.FAIL;\n        if (appId > 0 && importantLevel >= 0) {\n            try {\n                AppDesc appDesc = appService.getByAppId(appId);\n                appDesc.setImportantLevel(importantLevel);\n                appService.update(appDesc);\n                successEnum = SuccessEnum.SUCCESS;\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        model.addAttribute(\"status\", successEnum.value());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 更新应用密码\n     */\n    @RequestMapping(value = \"/updateAppPassword\")\n    public ModelAndView doUpdateAppPassword(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        String password = request.getParameter(\"password\");\n        Boolean isSetPasswd = Boolean.valueOf(request.getParameter(\"isSetPasswd\"));\n        logger.info(\"modify appId:{},password:{}\", appId, password);\n        SuccessEnum successEnum = SuccessEnum.FAIL;\n        if (appId > 0) {\n            try {\n                //增加版本校验，6.0.0-6.0.8不支持清除密码，即password为空的情况\n                AppDesc appDesc = appService.getByAppId(appId);\n                if(appDesc != null){\n                    if(StringUtils.isBlank(password)){\n                        // Redis版本信息\n                        SystemResource resource = resourceService.getResourceById(appDesc.getVersionId());\n                        String name = resource.getName();\n                        if(name != null){\n                            String[] split = name.split(\"-\");\n                            if(split != null && split.length == 2){\n                                String version = split[1].replace(\".\", \"\");\n                                int versionNum = Integer.parseInt(version);\n                                if(versionNum <= 608 && versionNum >= 600){\n                                    model.addAttribute(\"status\", successEnum.value());\n                                    return new ModelAndView(\"\");\n                                }\n                            }\n                        }\n                    }\n                    // 修改密码逻辑\n                    redisDeployCenter.fixPassword(appId, password, isSetPasswd, false);\n                }\n                successEnum = SuccessEnum.SUCCESS;\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        model.addAttribute(\"status\", successEnum.value());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 更新应用 Redis版本\n     */\n    @RequestMapping(value = \"/updateRedisVersion\")\n    public ModelAndView doUpdateAppRedisVersion(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        int versionId = NumberUtils.toInt(request.getParameter(\"versionId\"));\n        logger.info(\"modify appId:{},versionId:{}\", appId, versionId);\n        SuccessEnum successEnum = SuccessEnum.FAIL;\n        if (appId > 0 && versionId > 0) {\n            try {\n                AppDesc appDesc = appService.getByAppId(appId);\n                appDesc.setVersionId(versionId);\n                appService.update(appDesc);\n                successEnum = SuccessEnum.SUCCESS;\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        model.addAttribute(\"status\", successEnum.value());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * <p>\n     * Description: 应用密码初始化页面\n     * </p>\n     *\n     * @param\n     * @return\n     * @author chenshi\n     * @version 1.0\n     * @date 2017/8/2\n     */\n    @RequestMapping(value = \"/initAppPassword\")\n    public ModelAndView doInitAppPassword(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {\n        if (appId != null && appId > 0) {\n            AppDesc appDesc = appService.getByAppId(appId);\n            model.addAttribute(\"appId\", appDesc.getAppId());\n            model.addAttribute(\"pkey\", appDesc.getPkey());\n            model.addAttribute(\"customPassword\", appDesc.getCustomPassword());\n        }\n        return new ModelAndView(\"manage/appOps/appCodeInit\");\n    }\n\n    @RequestMapping(value = \"/checkAppPassword\")\n    public ModelAndView doCheckAppPassword(HttpServletRequest request, HttpServletResponse response, Model model) {\n\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        logger.info(\"check appid:{}\", appId);\n        SuccessEnum successEnum = SuccessEnum.FAIL;\n        if (appId > 0) {\n            try {\n                // 密码校验逻辑\n                boolean check = redisDeployCenter.checkAuths(appId);\n                // 返回true 则一致\n                if (check) {\n                    successEnum = SuccessEnum.SUCCESS;\n                }\n            } catch (Exception e) {\n                logger.error(e.getMessage(), e);\n            }\n        }\n        model.addAttribute(\"status\", successEnum.value());\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"/updateAppPersistenceType\")\n    public void updateAppPersistenceType(HttpServletRequest request, HttpServletResponse response) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"), -1);\n        Integer persistenceType = Integer.valueOf(request.getParameter(\"persistenceType\"));\n        if(AppDescEnum.AppPersistenceType.getByType(persistenceType) == null){\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\n            return;\n        }\n        boolean executeFlag = appService.updateAppPersistenceType(appId, persistenceType);\n        write(response, String.valueOf(executeFlag == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppMigrateController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.enums.UseTypeEnum;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport com.sohu.cache.web.vo.RedisInfo;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * <p>\n * Description: 应用在线迁移\n * </p>\n *\n * @author chenshi\n * @version 1.0\n * @date 2018/9/21\n */\n@Controller\n@RequestMapping(\"/manage/app/migrate\")\npublic class AppMigrateController extends BaseController {\n\n    // 实例和日志信息\n    private static String INSTANCE_INFO_KEY = \"instanceInfo\";\n    private static String INSTANCE_LOG_KEY = \"instanceLog\";\n    // 下线实例信息\n    private static String DOWN_INSTANCE_IDS_KEY = \"downInstanceIds\";\n    private static String DOWN_INSTANCE_INFO_KEY = \"downInstanceInfo\";\n\n    @RequestMapping(value = \"init\", method = {RequestMethod.POST, RequestMethod.GET})\n    public ModelAndView init(Model model, long appId) {\n        // 1.获取应用信息\n        AppDetailVO appDetail = appStatsCenter.getAppDetail(appId);\n        List<MachineStats> machinelist = machineCenter.getMachineStats(null, null, null, null, null, null, null);\n        // 获取机器信息\n        Map<String, Integer> machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n        // 2.获取当前redis实例信息\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        String instanceRedisinfo = getInstanceInfo(instanceList, appId).get(INSTANCE_INFO_KEY);\n        // 3.如果是sentinel，获取sentinel实例信息\n        String instanceSentinelinfo = getSentinelInstanceInfo(instanceList);\n        List<MachineRoom> roomList = machineCenter.getEffectiveRoom();\n\n        model.addAttribute(\"roomList\", roomList);\n        model.addAttribute(\"appDetail\", appDetail);\n        model.addAttribute(\"instanceSentinelInfo\", instanceSentinelinfo);\n        model.addAttribute(\"instanceSourceInfo\", instanceSentinelinfo + instanceRedisinfo);\n        model.addAttribute(\"machinelist\", machinelist);\n        model.addAttribute(\"machineInstanceCountMap\", machineInstanceCountMap);\n        return new ModelAndView(\"/manage/appOps/appMigrate\");\n    }\n\n    @RequestMapping(value = \"selectMachine\", method = {RequestMethod.POST})\n    public ModelAndView autoSelectMachine(HttpServletResponse response,\n                                          int type,\n                                          int useType,\n                                          String room,\n                                          int machineNum,\n                                          long mem,\n                                          int masterNum,\n                                          int slaveNum) {\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        if (room == null || machineNum == 0 || masterNum == 0) {\n            logger.info(\"可用机器数不足\");\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"参数输入错误\");\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;\n        }\n\n        int masterMachineNum;\n        Integer isSalve = slaveNum == 0 ? 0 : 1;\n        if (TypeUtil.isRedisSentinel(type)) {\n            masterMachineNum = 1 + slaveNum;\n        } else if (TypeUtil.isRedisStandalone(type)) {\n            masterMachineNum = 1;\n        } else if (TypeUtil.isRedisCluster(type)) {\n            masterMachineNum = machineNum;\n        } else {\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"type参数错误\");\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;\n        }\n\n        Double reqSize = Math.ceil(mem * 1.0D / masterNum) * Math.ceil(masterNum * 1.0D / masterMachineNum);\n        Integer reqCpu = masterNum % masterMachineNum == 0 ? masterNum / masterMachineNum : masterNum / masterMachineNum + 1;\n        List<MachineMemStatInfo> machineMemStatInfoList = machineCenter.getAllValidMachineMem(new ArrayList<String>(), room, useType);\n\n        List<MachineMemStatInfo> machineCandi = new ArrayList<MachineMemStatInfo>();\n        for (MachineMemStatInfo memStatInfo : machineMemStatInfoList) {\n            appService.getMachineCandiList(memStatInfo, reqSize, reqCpu, isSalve, machineCandi);\n        }\n        List<MachineMemStatInfo> resMachines = new ArrayList<MachineMemStatInfo>();\n        appService.getResMachines(machineCandi, masterMachineNum, resMachines);\n\n        if (resMachines.size() == 0) {\n            if (useType == UseTypeEnum.Machine_test.getValue()) {\n                appService.getResMachines(machineMemStatInfoList, masterMachineNum, resMachines);\n            }\n            if (resMachines.size() == 0) {\n                logger.info(\"可用机器数不足\");\n                resultMap.put(\"status\", SuccessEnum.ERROR.value());\n                resultMap.put(\"message\", \"可用机器数不足\");\n                sendMessage(response, JSONObject.toJSONString(resultMap));\n                return null;\n            }\n        }\n\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        resultMap.put(\"resMachineList\", resMachines);\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n\n    @RequestMapping(value = \"selectSentinelMachine\", method = {RequestMethod.POST})\n    public ModelAndView selectSentinelMachine(HttpServletResponse response, long appId) {\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        String instanceSentinelInfo = getSentinelInstanceInfo(instanceList);\n        if (instanceSentinelInfo.isEmpty()) {\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"sentinel信息为空\");\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;\n        }\n        List<String> sentinelInfoList = Arrays.asList(instanceSentinelInfo.split(\"\\n\"));\n        int sentinelNum = sentinelInfoList.size();\n        if (sentinelNum < 3 || sentinelNum % 2 == 0) {\n            resultMap.put(\"message\", \"sentinel机器数为\" + sentinelNum + \"不满足条件，系统自动进行调整\");\n            sentinelNum = sentinelNum < 3 ? 3 : sentinelNum + 1;\n        }\n\n        Integer reqCpu = 1;\n        List<MachineMemStatInfo> machineMemStatInfoList = machineCenter.getAllValidMachineMem(new ArrayList<String>(), null, 3);\n        List<MachineMemStatInfo> machineCandi = new ArrayList<MachineMemStatInfo>();\n        for (MachineMemStatInfo memStatInfo : machineMemStatInfoList) {\n            memStatInfo.setInstanceNum(instanceDao.getInstListByIp(memStatInfo.getIp()).size());\n            appService.getMachineCandiList(memStatInfo, 0d, reqCpu, 0, machineCandi);\n        }\n        if (machineCandi.size() < sentinelNum) {\n            /*resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"可用sentinel机器数不足\");\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;*/\n            // 可用sentinel机器不足 则默认挑选\n            logger.warn(\"machineCandi size = {},default random sentinel ip!\", machineCandi.size());\n            machineCandi = machineMemStatInfoList;\n        }\n        //按机房分类\n        List<MachineRoom> roomList = machineCenter.getEffectiveRoom();\n        Map<String, List<MachineMemStatInfo>> sentinelMachineMap = new HashMap<String, List<MachineMemStatInfo>>();\n        for (MachineRoom room : roomList) {\n            List<MachineMemStatInfo> list = new ArrayList<MachineMemStatInfo>();\n            sentinelMachineMap.put(room.getName(), list);\n        }\n        for (MachineMemStatInfo memStatInfo : machineCandi) {\n            sentinelMachineMap.get(memStatInfo.getRoom()).add(memStatInfo);\n        }\n        Set<MachineMemStatInfo> sentinelMachineSet = new HashSet<MachineMemStatInfo>();\n        //select\n        while (sentinelMachineSet.size() < sentinelNum) {\n            for (Map.Entry<String, List<MachineMemStatInfo>> entry: sentinelMachineMap.entrySet()) {\n                getResMachines(entry.getValue(), 1, sentinelMachineSet);\n                if (sentinelMachineSet.size() == sentinelNum) {\n                    break;\n                }\n            }\n        }\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        resultMap.put(\"sentinelMachineList\", new ArrayList<MachineMemStatInfo>(sentinelMachineSet));\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    private void getResMachines(List<MachineMemStatInfo> machineCandi, Integer machineNum, Set<MachineMemStatInfo> resMachines) {\n        if (machineCandi == null) {\n            return;\n        }\n        Map map = new HashMap();\n        if (machineCandi.size() < machineNum) {\n            return;\n        } else {\n            while (map.size() < machineNum) {\n                int random = (int) (Math.random() * machineCandi.size());\n                if (!map.containsKey(random)) {\n                    map.put(random, \"\");\n                    resMachines.add(machineCandi.get(random));\n                }\n            }\n        }\n    }\n\n    @RequestMapping(value = \"generateMachineInfo\", method = {RequestMethod.POST})\n    public ModelAndView autoSelectMachine(HttpServletResponse response,\n                                          String machineIps) {\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        if (machineIps == null || machineIps.isEmpty()) {\n            logger.info(\"所选sentinel机器为空\");\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"请选择sentinel机器\");\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;\n        }\n        List<MachineMemStatInfo> resMachines = machineCenter.getValidMachineMemByIpList(Arrays.asList(machineIps.split(\",\")));\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        resultMap.put(\"resMachineList\", resMachines);\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    @RequestMapping(value = \"checkPlan\", method = {RequestMethod.POST})\n    public ModelAndView checkPlan(HttpServletResponse response, long appId, String machineInfo, String machineSentinelInfo, int type) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        SuccessEnum successEnum = SuccessEnum.SUCCESS;\n\n        logger.info(\" appId:{},type:{},machineInfo:{},machineSentinelInfo:{}\", appId, type, machineInfo, machineSentinelInfo);\n        // param check\n        if (StringUtils.isEmpty(machineInfo)) {\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"迁移机器参数异常,machineInfo:\" + machineInfo);\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;\n        }\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        // 1.迁移机器ip连通性,系统环境安装env检查\n        // 2.迁移机器安装redis版本检查\n//        RedisVersion redisVersion = redisConfigTemplateService.getRedisVersionById(appDesc.getVersionId());\n        SystemResource redisResource = resourceService.getResourceById(appDesc.getVersionId());\n        try {\n            Boolean flag = true;\n            for (String machineIp : machineInfo.split(ConstUtils.SEMICOLON)) {\n                if (!StringUtils.isEmpty(machineIp)) {\n                    // todo\n//                    flag = redisConfigTemplateService.checkMachineRedisVersion(machineIp, redisVersion);\n//                    if (!flag) {\n//                        resultMap.put(\"status\", SuccessEnum.ERROR.value());\n//                        resultMap.put(\"message\", \"\" +\n//                                \"迁移机器:\" + machineIp + \"未安装\" + redisVersion.getName() + \"版本,请先安装!\");\n//                        sendMessage(response, JSONObject.toJSONString(resultMap));\n//                        return null;\n//                    }\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"迁移机器Redis版本检查异常，请查看日志!\");\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;\n        }\n        // 3.获取下线节点\n        Map<String, String> mapInfo = getDownInstanceInfo(instanceList, appId, \"slave\");\n        // 4.获取新实例节点\n        List<InstanceInfo> masterNodes = new ArrayList<InstanceInfo>();\n        List<InstanceInfo> sentinelNodes = new ArrayList<InstanceInfo>();\n        if (instanceList != null && instanceList.size() > 0) {\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"master\")) {\n                    masterNodes.add(instance);\n                }\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"sentinel\")) {\n                    sentinelNodes.add(instance);\n                }\n            }\n        }\n        Map<String, RedisInfo> redisInfoMap = getRedisInfo(masterNodes, machineInfo);\n        String newInstanceInfo;\n        StringBuilder newInstanceInfoBuilder = new StringBuilder(\"新增实例信息:\\n\");\n        for (Map.Entry<String, RedisInfo> redisInfo : redisInfoMap.entrySet()) {\n            RedisInfo redisNode = redisInfo.getValue();\n            //newInstanceInfo += redisNode.getRedisInfo(redisNode);\n            newInstanceInfoBuilder.append(redisNode.getRedisInfo(redisNode));\n        }\n        // 5.如果是sentinel集群\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            Map<String, RedisInfo> sentinelInfoMap = getSentinelInfo(sentinelNodes, machineSentinelInfo);\n            for (Map.Entry<String, RedisInfo> sentinelInfo : sentinelInfoMap.entrySet()) {\n                RedisInfo redisNode = sentinelInfo.getValue();\n                //newInstanceInfo += redisNode.getRedisInfo(redisNode);\n                newInstanceInfoBuilder.append(redisNode.getRedisInfo(redisNode));\n            }\n            // 获取需要下线的sentinel\n            Map<String, String> sentinelInfo = getDownInstanceInfo(instanceList, appId, \"sentinel\");\n            resultMap.put(\"downSentinelIds\", sentinelInfo.get(DOWN_INSTANCE_IDS_KEY));\n        }\n        newInstanceInfo = newInstanceInfoBuilder.toString();\n        resultMap.put(\"status\", successEnum.value());\n        resultMap.put(\"downInstanceInfo\", \"下线实例信息:\\n\" + mapInfo.get(DOWN_INSTANCE_INFO_KEY));\n        resultMap.put(\"downInstanceIds\", mapInfo.get(DOWN_INSTANCE_IDS_KEY));\n        resultMap.put(\"newInstanceInfo\", newInstanceInfo);\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    @RequestMapping(value = \"nodeReplace\", method = {RequestMethod.POST})\n    public ModelAndView nodeReplace(HttpServletResponse response, long appId, String machineInfo, String machineSentinelInfo, String downInstanceIds, int type) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        SuccessEnum successEnum = SuccessEnum.SUCCESS;\n        logger.info(\"appid:{}, downInstanceIds:{}\", appId, downInstanceIds);\n\n        // 1.获取应用信息\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        // 2.关闭指定下线slave节点\n        Boolean shutdownFlag = shutdownInstance(downInstanceIds, appId);\n        if (!shutdownFlag) {\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"关闭slave节点异常，请查看日志!\");\n            sendMessage(response, JSONObject.toJSONString(resultMap));\n            return null;\n        }\n        // 3.启动新Redis实例\n        startInstance(instanceList, machineInfo, appId);\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            // 启动sentinel实例\n            startSentinelInstance(instanceList, machineSentinelInfo, appId);\n        }\n        // 4.获取迁移后最新实例信息\n        instanceList = appService.getAppInstanceInfo(appId);\n        Map<String, String> instanceInfo = getInstanceInfo(instanceList, appId);\n        // 5.如果是sentinel，获取sentinel实例信息\n        String instanceSentinelinfo = \"\";\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            instanceSentinelinfo = getSentinelInstanceInfo(instanceList);\n        }\n        resultMap.put(\"status\", successEnum.value());\n        resultMap.put(\"instanceTargetInfo\", instanceInfo.get(INSTANCE_INFO_KEY) + instanceSentinelinfo);\n        resultMap.put(\"instanceTargetLog\", instanceInfo.get(INSTANCE_LOG_KEY));\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n\n        return null;\n    }\n\n    @RequestMapping(value = \"msFailover\", method = {RequestMethod.POST})\n    public ModelAndView msFailover(HttpServletResponse response, Model model, long appId, int type) {\n\n        // 1.从节点failover\n        Map<String, Object> resultMap = redisConfigTemplateService.slaveFailover(appId);\n        // 2.获取最新实例信息\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        Map<String, String> instanceInfo = getInstanceInfo(instanceList, appId);\n        // 3.需要下线的slave实例信息\n        Map<String, String> mapInfo = getDownInstanceInfo(instanceList, appId, \"slave\");\n        // 4.如果是sentinel，获取sentinel实例信息\n        String instanceSentinelinfo = \"\";\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            instanceSentinelinfo = getSentinelInstanceInfo(instanceList);\n        }\n        resultMap.put(\"instanceTargetInfo\", instanceInfo.get(INSTANCE_INFO_KEY) + instanceSentinelinfo);\n        resultMap.put(\"instanceTargetLog\", instanceInfo.get(INSTANCE_LOG_KEY));\n        resultMap.put(\"downInstanceInfo\", \"下线实例信息:\\n\" + mapInfo.get(DOWN_INSTANCE_INFO_KEY));\n        resultMap.put(\"downInstanceIds\", mapInfo.get(DOWN_INSTANCE_IDS_KEY));\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    @RequestMapping(value = \"addSlave\", method = {RequestMethod.POST})\n    public ModelAndView addNewSlave(HttpServletResponse response, long appId, String machineInfo, int type) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        // 1.参数验证\n        if (StringUtils.isEmpty(machineInfo) && appId < 0) {\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"参数异常:machineInfo:{\" + machineInfo + \"},appId:{\" + appId + \"}\");\n        }\n        // 2.获取实例信息\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        // 3.添加新从节点\n        startInstance(instanceList, machineInfo, appId);\n        // 4.日志\n        instanceList = appService.getAppInstanceInfo(appId);\n        // 5.如果是sentinel，获取sentinel实例信息\n        String instanceSentinelinfo = \"\";\n        if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n            instanceSentinelinfo = getSentinelInstanceInfo(instanceList);\n        }\n        Map<String, String> instanceInfo = getInstanceInfo(instanceList, appId);\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        resultMap.put(\"instanceTargetInfo\", instanceInfo.get(INSTANCE_INFO_KEY) + instanceSentinelinfo);\n        resultMap.put(\"instanceTargetLog\", instanceInfo.get(INSTANCE_LOG_KEY));\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    @RequestMapping(value = \"appCheck\", method = {RequestMethod.POST})\n    public ModelAndView appStatusCheck(HttpServletResponse response, long appId) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n\n        // 1.连接数检测\n\n        // 2.实例运行状态\n\n\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    @RequestMapping(value = \"downSlave\", method = {RequestMethod.POST})\n    public ModelAndView downSlave(HttpServletResponse response, long appId, String downInstanceIds, int type) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        logger.info(\"appid:{}, downInstanceIds:{},type:{} \", appId, downInstanceIds, type);\n        // 1.关闭指定下线slave节点\n        Boolean shutdownFlag = shutdownInstance(downInstanceIds, appId);\n\n        if (!shutdownFlag) {\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", \"关闭slave节点异常，请查看日志!\");\n        } else {\n            List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n            Map<String, String> instanceInfo = getInstanceInfo(instanceList, appId);\n\n            // 2.如果是sentinel，获取sentinel实例信息\n            String instanceSentinelinfo = \"\";\n            if (type == ConstUtils.CACHE_REDIS_SENTINEL) {\n                instanceSentinelinfo = getSentinelInstanceInfo(instanceList);\n            }\n            resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n            resultMap.put(\"instanceTargetInfo\", instanceInfo.get(INSTANCE_INFO_KEY) + instanceSentinelinfo);\n            resultMap.put(\"instanceTargetLog\", instanceInfo.get(INSTANCE_LOG_KEY));\n        }\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    @RequestMapping(value = \"complete\", method = {RequestMethod.POST})\n    public ModelAndView migrateComplete(HttpServletResponse response, long appId) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    /**\n     * <p>\n     * Description: 获取实例信息\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/10/8\n     */\n    public Map<String, String> getDownInstanceInfo(List<InstanceInfo> instanceList, long appId, String role) {\n        Map<String, String> instanceMap = new HashMap<String, String>();\n        String downInstanceInfo = \"\";\n        List<String> downInstanceIds = new ArrayList<String>();\n        if (instanceList != null && instanceList.size() > 0) {\n            // 遍历获取slave节点\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(role)) {\n                    String redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\n                    RedisInfo slaveInfo = new RedisInfo(instance.getIp(), instance.getPort(), redisVersion, instance.getRoleDesc());\n                    downInstanceInfo += slaveInfo.getInfo(slaveInfo);\n                    downInstanceIds.add(instance.getId() + \"#\" + instance.getIp() + \":\" + instance.getPort());\n                }\n            }\n        }\n        instanceMap.put(DOWN_INSTANCE_INFO_KEY, downInstanceInfo);\n        instanceMap.put(DOWN_INSTANCE_IDS_KEY, StringUtils.join(downInstanceIds, \",\"));\n        return instanceMap;\n    }\n\n    /**\n     * <p>\n     * Description: 下线实例\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/10/8\n     */\n    public Boolean shutdownInstance(String downInstanceIds, long appId) {\n        if (!StringUtils.isEmpty(downInstanceIds)) {\n            try {\n                // 遍历获取slave节点,关闭\n                for (String downInstance : downInstanceIds.split(\",\")) {\n                    int instanceId = Integer.parseInt(downInstance.split(\"#\")[0]);\n                    String hostInfo = String.valueOf(downInstance.split(\"#\")[1]);\n                    boolean closeOp = instanceDeployCenter.shutdownExistInstance(appId, instanceId);\n                    logger.info(\"appid:{} shutdown slave instance:[] :{}\", appId, instanceId, hostInfo);\n                }\n            } catch (Exception e) {\n                logger.info(e.getMessage(), e);\n                return false;\n            }\n        } else {\n            // 没有slave 可下线的slave节点\n            logger.info(\" appid:{} ,has no slave node need to shutdown ,downInstanceIds:{}\", appId, downInstanceIds);\n        }\n        return true;\n    }\n\n    /**\n     * <p>\n     * Description:添加实例\n     * </p>\n     *\n     * @param instanceList 实例列表\n     * @param machineInfo  机器信息\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/10/8\n     */\n    public void startInstance(List<InstanceInfo> instanceList, String machineInfo, long appId) {\n\n        // 1 当前master节点信息\n        List<InstanceInfo> masterNodes = new ArrayList<InstanceInfo>();\n        if (instanceList != null && instanceList.size() > 0) {\n            // 遍历获取slave节点,关闭slave节点\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"master\")) {\n                    masterNodes.add(instance);\n                }\n            }\n        }\n        // 2 启动新的slave实例\n        Map<String, RedisInfo> redisInfoMap = getRedisInfo(masterNodes, machineInfo);\n        if (!CollectionUtils.isEmpty(masterNodes)) {\n            for (InstanceInfo masterInstance : masterNodes) {\n                if (masterInstance.getRoleDesc().equals(\"master\")) {\n                    // 获取新slave节点\n                    RedisInfo slaveNode = redisInfoMap.get(masterInstance.getHostPort());\n                    boolean success = false;\n                    if (appId > 0 && StringUtils.isNotBlank(slaveNode.getIp())) {\n                        try {\n                            success = redisDeployCenter.addSlave(appId, masterInstance.getId(), slaveNode.getIp());\n                            // sleep 15s for master psync\n                            TimeUnit.SECONDS.sleep(15);\n                            // todo 检测slave节点同步数据状态成功才发起下一个节点优化\n                            long start = System.currentTimeMillis();\n                            Boolean psyncFlag = redisConfigTemplateService.slaveIsPsync(appId, masterInstance.getIp(), masterInstance.getPort());\n                            logger.info(\"appid:{} add slave wait psync cost :{}ms ,psyncFlag:{}\", appId, (System.currentTimeMillis() - start), psyncFlag);\n                        } catch (Exception e) {\n                            logger.error(e.getMessage(), e);\n                            success = false;\n                        }\n                    }\n                    logger.warn(\"migrate addSlave: appId:{},masterInstanceId:{},slaveHost:{} result is {}\", appId, masterInstance.getId(), slaveNode.getIp(), success);\n                }\n            }\n        }\n    }\n\n    /**\n     * <p>\n     * Description: sentinel实例启动\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/10/11\n     */\n    public void startSentinelInstance(List<InstanceInfo> instanceList, String machineSentinelInfo, long appId) {\n\n        // 1 当前sentinel节点信息\n        List<InstanceInfo> sentinelNodes = new ArrayList<InstanceInfo>();\n        if (instanceList != null && instanceList.size() > 0) {\n            // 遍历获取slave节点,关闭slave节点\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"sentinel\")) {\n                    sentinelNodes.add(instance);\n                }\n            }\n        }\n        // 2 启动新的slave实例\n        Map<String, RedisInfo> redisInfoMap = getSentinelInfo(sentinelNodes, machineSentinelInfo);\n        if (!CollectionUtils.isEmpty(sentinelNodes) && !MapUtils.isEmpty(redisInfoMap)) {\n            for (InstanceInfo sentnelNode : sentinelNodes) {\n                // 获取新slave节点\n                RedisInfo sentinelInfo = redisInfoMap.get(sentnelNode.getHostPort());\n                boolean success = false;\n                if (appId > 0 && StringUtils.isNotBlank(sentinelInfo.getIp())) {\n                    try {\n                        success = redisDeployCenter.addSentinel(appId, sentinelInfo.getIp());\n                    } catch (Exception e) {\n                        logger.error(e.getMessage(), e);\n                        success = false;\n                    }\n                }\n                logger.warn(\"migrate addSentinel: appId:{},sentinelHost:{} result is {}\", appId, sentnelNode.getHostPort(), success);\n            }\n        }\n    }\n\n    /**\n     * <p>\n     * Description: 获取迁移实例对应关系\n     * </p>\n     *\n     * @version 1.0\n     * @date 2018/10/9\n     */\n    public Map<String, RedisInfo> getRedisInfo(List<InstanceInfo> masterNodes, String machineInfo) {\n\n        Map<String, RedisInfo> redisInfoMap = new HashMap<String, RedisInfo>();\n        try {\n            List<String> machineIps = new ArrayList<String>();\n            if (!StringUtils.isEmpty(machineInfo)) {\n                machineIps = Arrays.asList(machineInfo.split(ConstUtils.SEMICOLON));\n            }\n            logger.info(\"masterNodes num=\" + masterNodes.size() + \" ,machineNums =\" + machineIps.size() + \",machine ips:\" + machineIps);\n\n            String role = \"slave\";\n            int pos = 0;\n            for (InstanceInfo masterNode : masterNodes) {\n                int tag = pos % machineIps.size();\n                int retryTimes = 0;\n                // 挑选主从节点，如果只有重复则可以重复\n                while (masterNode.getIp().equals(machineIps.get(tag)) && retryTimes++ <= 3) {\n                    if (masterNodes.size() % machineIps.size() != 0) {\n                        tag = (tag + masterNodes.size() % machineIps.size()) % machineIps.size();\n                    } else {\n                        tag = (tag + masterNodes.size() % machineIps.size() + 1) % machineIps.size();\n                    }\n                }\n                redisInfoMap.put(masterNode.getHostPort(), new RedisInfo(machineIps.get(tag), role));\n                logger.info(\"masterNode:{} => {}\", masterNode.getHostPort(), machineIps.get(tag));\n                pos++;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        logger.info(\"redisInfoMap:{}\", redisInfoMap);\n        return redisInfoMap;\n    }\n\n    public Map<String, RedisInfo> getSentinelInfo(List<InstanceInfo> sentinelNodes, String machineSentinelInfo) {\n\n        Map<String, RedisInfo> sentinelInfoMap = new HashMap<String, RedisInfo>();\n        try {\n            /**\n             * 规则:\n             * 1. 主从尽量在不同节点 (物理机节点)\n             * 2. 如果节点不够可部署在同一节点\n             */\n            List<String> machineIps = new ArrayList<String>();\n            if (!StringUtils.isEmpty(machineSentinelInfo)) {\n                machineIps = Arrays.asList(machineSentinelInfo.split(ConstUtils.SEMICOLON));\n            }\n            String role = \"sentinel\";\n            for (InstanceInfo sentinelNode : sentinelNodes) {\n                String host = sentinelNode.getIp();\n                // 获取随机节点\n                List<String> ips = new ArrayList<String>();\n                // 1.去重sentinelIp\n                for (String ip : machineIps) {\n                    if (!ip.equals(host)) {\n                        ips.add(ip);\n                    }\n                }\n                // 2.获取随机节点\n                if (ips.size() > 0) {\n                    int random = Math.abs((sentinelNode.getHostPort()).hashCode()) % ips.size();\n                    sentinelInfoMap.put(sentinelNode.getHostPort(), new RedisInfo(ips.get(random), role));\n                } else {\n                    sentinelInfoMap.put(sentinelNode.getHostPort(), new RedisInfo(host, role));\n                }\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        logger.info(\"sentinelInfoMap:{}\", sentinelInfoMap);\n        return sentinelInfoMap;\n    }\n\n    private Map<String, String> getInstanceInfo(List<InstanceInfo> instanceList, long appId) {\n\n        Map<String, String> resultMap = new HashMap<String, String>();\n        //实例信息和日志信息\n        String instanceInfo = \"\";\n        String instanceLog = \"\";\n        StringBuilder instanceInfoBuilder = new StringBuilder();\n        StringBuilder instanceLogBuilder = new StringBuilder();\n        //建立slave-master实例关系\n        Map<String, RedisInfo> instanceMap = new HashMap<String, RedisInfo>();\n        if (instanceList != null && instanceList.size() > 0) {\n            // 遍历获取slave节点\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"slave\")) {\n                    String redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\n                    RedisInfo slaveInfo = new RedisInfo(instance.getId(), instance.getIp(), instance.getPort(), redisVersion, instance.getRoleDesc());\n                    RedisInfo masterInfo = new RedisInfo(instance.getMasterHost(), instance.getMasterPort());\n                    instanceMap.put(masterInfo.getIpAndPortInfo(masterInfo) + \"#\" + slaveInfo.getIpAndPortInfo(slaveInfo), slaveInfo);\n                }\n            }\n            // 遍历获取master节点\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"master\")) {\n                    String redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\n                    RedisInfo masterInfo = new RedisInfo(instance.getIp(), instance.getPort(), redisVersion, instance.getRoleDesc());\n                    //instanceInfo += masterInfo.getInfo(masterInfo);\n                    //instanceLog += \"<br/>\";\n                    instanceInfoBuilder.append(masterInfo.getInfo(masterInfo));\n                    instanceLogBuilder.append(\"<br/>\");\n                    // 遍历map\n                    for (Map.Entry<String, RedisInfo> redisInfo : instanceMap.entrySet()) {\n                        if (redisInfo.getKey().indexOf(masterInfo.getIpAndPortInfo(masterInfo)) > -1) {\n                            //instanceInfo += \"------\" + redisInfo.getValue().getInfo(redisInfo.getValue());\n                            //instanceLog += \"<a target='_blank' href=/manage/instance/log?instanceId=\" + redisInfo.getValue().getSid() + \">日志</a><br/>\";\n                            instanceInfoBuilder.append(\"------\")\n                                    .append(redisInfo.getValue().getInfo(redisInfo.getValue()));\n                            instanceLogBuilder.append(\"<a target='_blank' href=/manage/instance/log?instanceId=\")\n                                    .append(redisInfo.getValue().getSid())\n                                    .append(\">日志</a><br/>\");\n                        }\n                    }\n                }\n            }\n        }\n        instanceLog = instanceLogBuilder.toString();\n        instanceInfo = instanceInfoBuilder.toString();\n        // 返回实例和日志信息\n        resultMap.put(INSTANCE_INFO_KEY, instanceInfo);\n        resultMap.put(INSTANCE_LOG_KEY, instanceLog);\n        return resultMap;\n    }\n\n    /**\n     * <p>\n     * Description:获取sentinel实例信息\n     * </p>\n     *\n     * @version 1.0\n     * @date 2018/10/11\n     */\n    private String getSentinelInstanceInfo(List<InstanceInfo> instanceList) {\n        //String instanceSentinelinfo = \"\";\n        StringBuilder instanceSentinelInfoBuilder = new StringBuilder();\n        List<InstanceInfo> sentinelNodes = new ArrayList<InstanceInfo>();\n        if (instanceList != null && instanceList.size() > 0) {\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus() && instance.getRoleDesc().equals(\"sentinel\")) {\n                    sentinelNodes.add(instance);\n                }\n            }\n            if (sentinelNodes != null && sentinelNodes.size() > 0) {\n                for (InstanceInfo sentinelNode : sentinelNodes) {\n                    //instanceSentinelinfo += sentinelNode.getHostPort() + \" \" + sentinelNode.getTypeDesc() + \" \\n\";\n                    instanceSentinelInfoBuilder.append(sentinelNode.getHostPort())\n                            .append(\" \")\n                            .append(sentinelNode.getTypeDesc())\n                            .append(\" \\n\");\n                }\n            }\n        }\n        return instanceSentinelInfoBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppScrollRestartController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.ConfigRestartRecord;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.web.enums.AppTypeEnum;\nimport com.sohu.cache.web.enums.ConfigRestartOperateEnum;\nimport com.sohu.cache.web.enums.RestartStatusEnum;\nimport com.sohu.cache.web.service.AppScrollRestartService;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.vo.AppRedisConfigVo;\nimport com.sohu.cache.web.vo.ExecuteResult;\nimport com.sohu.cache.web.vo.GeneralResponse;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * <p>\n * Description: 应用滚动重启\n * </p>\n *\n * @author zengyizhao\n * @version 1.0\n * @date 2021/9/13\n */\n@Controller\n@RequestMapping(\"/manage/app/restart\")\npublic class AppScrollRestartController extends BaseController {\n\n    @Autowired\n    private AppService appService;\n\n    @Autowired\n    private AppScrollRestartService appScrollRestartService;\n\n    @Autowired\n    private AssistRedisService assistRedisService;\n\n    private final static String RESTART_CONFIG_KEY = \"restart:config:\";\n\n    /**\n     * 查询滚动重启记录\n     * @param model\n     * @param appId\n     * @return\n     */\n    @RequestMapping(value = \"getRestartRecord\", method = RequestMethod.GET)\n    public ModelAndView getRestartRecord(Model model, Long appId, Integer pageNo) {\n        ConfigRestartRecord configRestartRecord = new ConfigRestartRecord();\n        configRestartRecord.setAppId(appId);\n        if(pageNo == null){\n            pageNo = 1;\n        }\n        int pageSize = 10;\n        List<ConfigRestartRecord> configRestartRecordByCondition = appScrollRestartService.getConfigRestartRecordByCondition(model, configRestartRecord, pageNo, pageSize);\n        List<InstanceInfo> instanceInfoList = new ArrayList<>();\n        if(appId != null){\n            instanceInfoList = appService.getAppBasicInstanceInfo(appId);\n        }else{\n            Set<Long> appIdSet = new HashSet<>();\n            for (ConfigRestartRecord record : configRestartRecordByCondition) {\n                if(appIdSet.contains(record.getAppId())){\n                    continue;\n                }\n                appIdSet.add(record.getAppId());\n                instanceInfoList.addAll(appService.getAppBasicInstanceInfo(record.getAppId()));\n            }\n        }\n        Map<Integer, InstanceInfo> instanceInfoMap = instanceInfoList.stream().collect(Collectors.toMap(InstanceInfo::getId, Function.identity(), (v1, v2) -> v1));\n        model.addAttribute(\"restartRecordList\", configRestartRecordByCondition);\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"instanceInfoMap\", instanceInfoMap);\n        return new ModelAndView(\"manage/appOps/appRestartList\");\n    }\n\n    /**\n     * 滚动重启\n     * @param appRedisConfigVo\n     * @return\n     */\n    @RequestMapping(value = \"scrollRestart\", method = RequestMethod.POST)\n    public void scrollRestart(HttpServletRequest request, HttpServletResponse response, @RequestBody AppRedisConfigVo appRedisConfigVo) {\n        AppUser appUser = getUserInfo(request);\n        if(assistRedisService.get(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId()) != null){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"滚动重启/修改配置正在执行中，不允许重复操作。\"));\n            sendMessage(response, json);\n            return;\n        }\n        // 1.获取应用信息\n        AppDesc appDesc = appService.getByAppId(appRedisConfigVo.getAppId());\n        //无有效应用时,或应用redis类型不符时，返回\n        if(appDesc == null || appDesc.getType() != AppTypeEnum.REDIS_SENTINEL.getType() && appDesc.getType() != AppTypeEnum.REDIS_CLUSTER.getType()){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"参数有误，请确认。\"));\n            sendMessage(response, json);\n            return;\n        }\n        // 2.获取当前应用下redis实例信息\n        List<InstanceInfo> instanceList = appService.getAppBasicInstanceInfo(appRedisConfigVo.getAppId());\n        //过滤出运行中的实例\n        instanceList = instanceList.stream().filter(instanceInfo -> instanceInfo.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()).collect(Collectors.toList());\n        if(assistRedisService.get(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId()) != null){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"滚动重启/修改配置正在执行中，不允许重复操作。\"));\n            sendMessage(response, json);\n            return;\n        }\n        //处理实例信息，封装主从信息\n        boolean executeFlag = appScrollRestartService.handleAppInstanceInfo(instanceList, appDesc);\n        if(!executeFlag){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"未正确获取到实例主从信息，请重试。\"));\n            sendMessage(response, json);\n            return;\n        }\n        if(assistRedisService.get(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId()) != null){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"滚动重启/修改配置正在执行中，不允许重复操作。\"));\n            sendMessage(response, json);\n            return;\n        }\n        //校验有效实例，并校验传入的instancId全部有效，无效直接返回\n        boolean check = this.checkPointedInstance(instanceList, appRedisConfigVo.getInstanceList());\n        if(!check){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"实例不满足此操作。\"));\n            sendMessage(response, json);\n            return;\n        }\n        if(assistRedisService.get(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId()) != null){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"滚动重启/修改配置正在执行中，不允许重复操作。\"));\n            sendMessage(response, json);\n            return;\n        }\n        ExecuteResult executeResult = appScrollRestartService.handleRestart(appUser, appDesc, instanceList, appRedisConfigVo);\n        String responseJson = null;\n        if(executeResult.getMessage() != null){\n            responseJson = JSONObject.toJSONString(GeneralResponse.ok(executeResult.getMessage()));\n        }\n        sendMessage(response, responseJson);\n        return;\n    }\n\n    /**\n     * 停止滚动重启\n     * @param response\n     * @param appId\n     * @return\n     */\n    @RequestMapping(value = \"stopRestart\", method = RequestMethod.GET)\n    public void getRestartRecord(Model model, HttpServletResponse response, Long appId) {\n        boolean existsStopRestartFlag = appScrollRestartService.existsStopRestartFlag(appId);\n        if(existsStopRestartFlag){\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"停止请求不允许重复发送，请通过日志查看重启进度。\"));\n            sendMessage(response, json);\n            return;\n        }\n\n        ConfigRestartRecord configRestartRecord = new ConfigRestartRecord();\n        configRestartRecord.setAppId(appId);\n        int pageNo = 1;\n        int pageSize = 10;\n        List<ConfigRestartRecord> configRestartRecordByCondition = appScrollRestartService.getConfigRestartRecordByCondition(model, configRestartRecord, pageNo, pageSize);\n        boolean existRestart = false;\n        for (ConfigRestartRecord record : configRestartRecordByCondition) {\n            if((record.getStatus() == RestartStatusEnum.RUNNING.getValue() && record.getOperateType() == ConfigRestartOperateEnum.RESTART.getValue())\n                || (record.getStatus() == RestartStatusEnum.RESTART_AFTER_CONFIG.getValue() && record.getOperateType() == ConfigRestartOperateEnum.CONFIG_RESTART.getValue())){\n                existRestart = true;\n                break;\n            }\n        }\n        if(existRestart){\n            boolean result = appScrollRestartService.addStopRestartFlag(appId);\n            if(result){\n                String json = JSONObject.toJSONString(GeneralResponse.ok(\"停止请求已发送，但不确保停止，请通过日志查看重启进度。\"));\n                sendMessage(response, json);\n                return;\n            }\n        }else{\n            String json = JSONObject.toJSONString(GeneralResponse.ok(\"重启任务不存在或已结束，请确认。\"));\n            sendMessage(response, json);\n            return;\n        }\n    }\n\n\n    /**\n     * 修改配置\n     * @param appRedisConfigVo\n     * @return\n     */\n    @RequestMapping(value = \"updateConfig\", method = RequestMethod.POST)\n    public void updateConfig(HttpServletRequest request, HttpServletResponse response, @RequestBody AppRedisConfigVo appRedisConfigVo) {\n        AppUser appUser = getUserInfo(request);\n        if (!assistRedisService.setNx(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId(), \"1\")) {\n            String json = JSONObject.toJSONString(GeneralResponse.error(500, \"滚动重启/修改配置正在执行中，不允许重复操作。\"));\n            sendMessage(response, json);\n            return;\n        }\n        try {\n            // 1.获取应用信息\n            AppDesc appDesc = appService.getByAppId(appRedisConfigVo.getAppId());\n            //无有效应用时,或应用redis类型不符时，返回\n            if (appDesc == null || appDesc.getType() != AppTypeEnum.REDIS_SENTINEL.getType() && appDesc.getType() != AppTypeEnum.REDIS_CLUSTER.getType()\n            ) {\n                String json = JSONObject.toJSONString(GeneralResponse.error(500, \"参数有误，请确认。\"));\n                sendMessage(response, json);\n                return;\n            }\n            //判断是否有配置信息\n            if (CollectionUtils.isEmpty(appRedisConfigVo.getConfigList())) {\n                String json = JSONObject.toJSONString(GeneralResponse.error(500, \"参数有误，请确认。\"));\n                sendMessage(response, json);\n                return;\n            }\n            // 2.获取当前应用下redis实例信息\n            List<InstanceInfo> instanceList = appService.getAppBasicInstanceInfo(appRedisConfigVo.getAppId());\n            //过滤出运行中的实例\n            instanceList = instanceList.stream().filter(instanceInfo -> instanceInfo.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()).collect(Collectors.toList());\n            //处理实例信息，封装主从信息\n            boolean executeFlag = appScrollRestartService.handleAppInstanceInfo(instanceList, appDesc);\n            if(!executeFlag){\n                String json = JSONObject.toJSONString(GeneralResponse.error(500, \"未正确获取到实例主从信息，请重试。\"));\n                sendMessage(response, json);\n                return;\n            }\n            //校验有效实例，并校验传入的instancId全部有效，无效直接返回\n            boolean check = this.checkPointedInstance(instanceList, appRedisConfigVo.getInstanceList());\n            if (!check) {\n                String json = JSONObject.toJSONString(GeneralResponse.error(500, \"实例不满足此操作。\"));\n                sendMessage(response, json);\n                return;\n            }\n\n            Map<String, Object> map = appScrollRestartService.handleConfig(appUser, appDesc, instanceList, appRedisConfigVo);\n            String handleResult = (String) map.get(\"errorInfo\");\n            String responseJson = null;\n            if (StringUtils.isNotEmpty(handleResult)) {\n                responseJson = JSONObject.toJSONString(GeneralResponse.error(500, handleResult));\n            } else {\n                responseJson = JSONObject.toJSONString(GeneralResponse.ok(map));\n            }\n            sendMessage(response, responseJson);\n            return;\n        }catch (Exception e){\n            logger.error(\"updateConfig error: \", e);\n        }finally {\n            assistRedisService.remove(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId());\n        }\n    }\n\n    /**\n     * 校验有效实例是否为空，并校验传入的instancId全部有效，无效直接返回\n     * @param instanceInfoList\n     * @param instanceIdList\n     * @return\n     */\n    private boolean checkPointedInstance(List<InstanceInfo> instanceInfoList, List<Integer> instanceIdList){\n        if(CollectionUtils.isEmpty(instanceInfoList)){\n            return false;\n        }\n        if(CollectionUtils.isEmpty(instanceIdList)){\n            return true;\n        }\n        boolean existFlag = false;\n        for(Integer instanceId : instanceIdList){\n            existFlag = false;\n            for(InstanceInfo instanceInfo : instanceInfoList){\n                if(instanceId == instanceInfo.getId()){\n                    existFlag = true;\n                    break;\n                }\n            }\n            if(!existFlag){\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 发送json消息\n     *\n     * @param response\n     * @param responseMessage\n     */\n    public void sendMessage(HttpServletResponse response, GeneralResponse responseMessage) {\n        response.reset();\n        response.setContentType(\"application/json;charset=UTF-8\");\n        PrintWriter printWriter = null;\n        try {\n            printWriter = response.getWriter();\n            printWriter.write(JSONObject.toJSONString(responseMessage));\n        } catch (IOException e) {\n            logger.error(ExceptionUtils.getFullStackTrace(e));\n        } finally {\n            if (printWriter != null) {\n                printWriter.flush();\n                printWriter.close();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppStatController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.sohu.cache.constant.MachineInfoEnum;\nimport com.sohu.cache.dao.AppClientStatisticGatherDao;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.entity.AppClientStatisticGather;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.TimeBetween;\nimport com.sohu.cache.task.tasks.daily.TopologyExamTask;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 应用常用统计\n *\n * @author leifu\n * @Time 2014年10月14日\n */\n@Controller\n@RequestMapping(\"manage/app/stat\")\npublic class AppStatController extends BaseController {\n\n    @Resource(name = \"appService\")\n    private AppService appService;\n    @Resource\n    private AppDao appDao;\n    @Autowired\n    private AppClientStatisticGatherDao appClientStatisticGatherDao;\n    @Autowired\n    TopologyExamTask topologyExamTask;\n\n    @RequestMapping(value = \"/list\")\n    public ModelAndView doAppStatsList(HttpServletRequest request,\n                                       HttpServletResponse response, Model model) {\n        //获取tab\n        int tabId = NumberUtils.toInt(request.getParameter(\"tabId\"), 0);\n        model.addAttribute(\"tabId\", tabId);\n\n        //获取appId，判断有无\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"), -1l);\n        model.addAttribute(\"appId\", appId == -1l ? \"\" : appId);\n        if (appId != -1l) {\n            AppDesc appDesc = appDao.getAppDescById(appId);\n            if (appDesc == null) {\n                return new ModelAndView(\"manage/appStat/list\");\n            }\n        }\n        //获取searchDate，判断有无\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = DateUtil.fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        searchDate = timeBetween.getFormatStartDate();\n        model.addAttribute(\"searchDate\", searchDate);\n\n        //appDescList\n        List<AppDesc> appDescList = appDao.getOnlineApps();\n        model.addAttribute(\"list\", appDescList);\n\n\n        switch (tabId) {\n            case 0: {\n                //appClientGatherStatMap\n                Map<Long, Map<String, Object>> appClientGatherStatMap = appService.getAppClientStatGather(appId, searchDate);\n                model.addAttribute(\"appClientGatherStatMap\", appClientGatherStatMap);\n                break;\n            }\n        }\n\n        model.addAttribute(\"appStatActive\", SuccessEnum.SUCCESS.value());\n        model.addAttribute(\"collectAlert\", \"(请等待\" + ConstUtils.MACHINE_STATS_CRON_MINUTE + \"分钟)\");\n        return new ModelAndView(\"manage/appStat/list\");\n    }\n\n\n    @RequestMapping(value = \"/list/server\")\n    public ModelAndView doAppStatsListForServer(HttpServletRequest request,\n                                                HttpServletResponse response, Model model) {\n        //获取tab\n        int tabId = NumberUtils.toInt(request.getParameter(\"tabId\"), 1);\n        model.addAttribute(\"tabId\", tabId);\n\n        //获取appId，判断有无\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"), -1l);\n        model.addAttribute(\"appId\", appId == -1l ? \"\" : appId);\n        if (appId != -1l) {\n            AppDesc appDesc = appDao.getAppDescById(appId);\n            if (appDesc == null) {\n                return new ModelAndView(\"manage/appStat/listServer\");\n            }\n        }\n        //获取searchDate，判断有无\n        String searchDate = request.getParameter(\"searchDate\");\n        TimeBetween timeBetween = new TimeBetween();\n        try {\n            timeBetween = DateUtil.fillWithDateFormat(searchDate);\n        } catch (ParseException e) {\n            logger.error(e.getMessage(), e);\n        }\n        searchDate = timeBetween.getFormatStartDate();\n        model.addAttribute(\"searchDate\", searchDate);\n\n        if(tabId == 1 || tabId == 2 || tabId == 3){\n            //appDescList\n            List<AppDesc> appDescList = appDao.getOnlineApps();\n            appDescList.forEach(appDesc -> {\n                String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n                appDesc.setVersionName(versionName);\n            });\n            model.addAttribute(\"appDescList\", appDescList);\n\n            //appDetailVOMap\n            Map<Long, AppDetailVO> appDetailVOMap = appStatsCenter.getOnlineAppDetails();\n            model.addAttribute(\"appDetailVOMap\", appDetailVOMap);\n\n            //appClientGatherStatMap\n            Map<Long, Map<String, Object>> appClientGatherStatMap = appService.getAppClientStatGather(appId, searchDate);\n            model.addAttribute(\"appClientGatherStatMap\", appClientGatherStatMap);\n        }\n        //机器环境检查\n        if(tabId == 4 ) {\n            SimpleDateFormat searchFormat = new SimpleDateFormat(\"yyyy-MM-dd\");\n            Map<String, Object> machineEnvMap = null;\n            try {\n                machineEnvMap = machineCenter.getAllMachineEnv(searchFormat.parse(searchDate), MachineInfoEnum.MachineTypeEnum.CONTAINER.getValue());\n            } catch (ParseException e) {\n                logger.error(\"machineCenter get container date:{} error :{}\",searchDate,e.getMessage());\n            }\n            model.addAttribute(\"machineEnvMap\", machineEnvMap);\n        }\n        // 宿主环境检查\n        if(tabId == 5) {\n            SimpleDateFormat searchFormat = new SimpleDateFormat(\"yyyy-MM-dd\");\n            Map<String, Object> machineEnvMap = null;\n            try {\n                machineEnvMap = machineCenter.getAllMachineEnv(searchFormat.parse(searchDate), MachineInfoEnum.MachineTypeEnum.HOST.getValue());\n            } catch (ParseException e) {\n                logger.error(\"machineCenter get host date:{} error :{}\",searchDate,e.getMessage());\n            }\n            model.addAttribute(\"machineEnvMap\", machineEnvMap);\n        }\n\n        model.addAttribute(\"appStatServerActive\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"manage/appStat/listServer\");\n    }\n\n    @RequestMapping(\"/clientList\")\n    public ModelAndView clientList(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {\n\n        List<String> clientList = new ArrayList<>();\n\n        List<InstanceInfo> instanceList = appService.getAppOnlineInstanceInfo(appId);\n        for (InstanceInfo instance : instanceList) {\n            if (\"master\".equals(instance.getRoleDesc())) {\n                clientList = redisCenter.getClientList(instance.getId());\n                break;\n            }\n        }\n        Set<String> clientSet = clientList.stream()\n                .filter(clientInfo -> StringUtils.isNotBlank(clientInfo))\n                .filter(clientInfo -> clientInfo.contains(\"flags=N\"))\n//                .filter(clientInfo -> !clientInfo.contains(\"cmd=client\"))\n                .map(clientInfo -> clientInfo.split(\" |:\")[1])\n                .collect(Collectors.toSet());\n\n        model.addAttribute(\"clientSet\", clientSet.stream().collect(Collectors.joining(\"\\n\")));\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(\"/topologyUpdate\")\n    public ModelAndView topologyUpdate(HttpServletRequest request, HttpServletResponse response, Model model) {\n        try {\n            List<AppClientStatisticGather> topologyExamList = topologyExamTask.checkAppsTopology(new Date());\n            if (CollectionUtils.isNotEmpty(topologyExamList)) {\n                appClientStatisticGatherDao.batchSaveTopologyExam(topologyExamList);\n            }\n            model.addAttribute(\"status\", 1);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            model.addAttribute(\"status\", 0);\n        }\n        return new ModelAndView(\"\");\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppToolController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.ResourceDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.stats.admin.CoreAppsStatCenter;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.DiagnosticToolService;\nimport com.sohu.cache.web.service.ToolService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 应用诊断工具\n * Created by rucao on 2018/11/9\n */\n@Controller\n@RequestMapping(\"/manage/app/tool\")\npublic class AppToolController extends BaseController {\n    private Logger logger = LoggerFactory.getLogger(AppToolController.class);\n\n    @Resource\n    private RedisCenter redisCenter;\n    @Resource\n    private ToolService toolService;\n    @Resource\n    private AppDao appDao;\n    @Resource\n    private CoreAppsStatCenter coreAppsStatCenter;\n    @Autowired\n    private DiagnosticToolService diagnosticToolService;\n    @Autowired\n    private ResourceDao resourceDao;\n    @Autowired\n    private TaskService taskService;\n\n\n    /**\n     * 跳转到主页\n     *\n     * @param request\n     * @param response\n     * @param model\n     * @return\n     */\n    @RequestMapping(\"/index\")\n    public ModelAndView index(HttpServletRequest request, HttpServletResponse response,\n                              Model model, String tabTag, Long appId, Long parentTaskId, Long auditId, Integer diagnosticStatus) {\n        model.addAttribute(\"tabTag\", tabTag);\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"parentTaskId\", parentTaskId);\n        model.addAttribute(\"auditId\", auditId);\n        model.addAttribute(\"diagnosticStatus\", diagnosticStatus);\n\n        model.addAttribute(\"diagnosticActive\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"manage/diagnosticTool/list\");\n    }\n\n    @RequestMapping(value = \"/diagnostic/tool\")\n    public ModelAndView diagnosticToolIndex(HttpServletRequest request,\n                                            HttpServletResponse response, Model model, String tabTag,\n                                            Long appId, Long parentTaskId, Long auditId, Integer diagnosticStatus) {\n\n        List<AppDesc> appDescList = appDao.getOnlineApps();\n        appDescList.forEach(appDesc -> {\n            String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(\"\");\n            appDesc.setVersionName(versionName);\n        });\n        Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n        model.addAttribute(\"appDescMap\", appDescMap);\n\n\n        if (tabTag == null || \"redis-cli\".equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticTool\");\n        }\n\n\n        int type = DiagnosticTypeEnum.getDescKey(tabTag);\n        List<DiagnosticTaskRecord> diagnosticTaskRecordList = diagnosticToolService.getDiagnosticTaskRecords(appId, parentTaskId, auditId, type, diagnosticStatus);\n        model.addAttribute(\"diagnosticTaskRecordList\", diagnosticTaskRecordList);\n        model.addAttribute(\"appId\", appId);\n        model.addAttribute(\"parentTaskId\", parentTaskId);\n        model.addAttribute(\"auditId\", auditId);\n        model.addAttribute(\"type\", type);\n        model.addAttribute(\"diagnosticStatus\", diagnosticStatus);\n\n        if (DiagnosticTypeEnum.SCAN_KEY.getDesc().equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticScan\");\n        } else if (DiagnosticTypeEnum.BIG_KEY.getDesc().equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticMemUsed\");\n        } else if (DiagnosticTypeEnum.IDLE_KEY.getDesc().equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticIdleKey\");\n        } else if (DiagnosticTypeEnum.HOT_KEY.getDesc().equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticHotKey\");\n        } else if (DiagnosticTypeEnum.DEL_KEY.getDesc().equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticDelKey\");\n        } else if (DiagnosticTypeEnum.SLOT_ANALYSIS.getDesc().equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticSlot\");\n        } else if (DiagnosticTypeEnum.SCAN_CLEAN.getDesc().equals(tabTag)) {\n            return new ModelAndView(\"manage/diagnosticTool/diagnosticScanClean\");\n        }\n\n        return new ModelAndView(\"\");\n    }\n\n\n    @RequestMapping(value = \"/diagnostic/appInstances\")\n    public ModelAndView getAppInstances(HttpServletRequest request,\n                                        HttpServletResponse response, Model model, Long appId) {\n        JSONObject json = new JSONObject();\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc == null) {\n            json.put(\"status\", String.valueOf(SuccessEnum.FAIL.value()));\n        } else {\n            List<InstanceInfo> instanceInfos = appService.getAppOnlineInstanceInfo(appId);\n            json.put(\"appInstanceList\", instanceInfos);\n            json.put(\"status\", String.valueOf(SuccessEnum.SUCCESS.value()));\n        }\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n\n    @RequestMapping(value = \"/diagnostic/submit\")\n    public ModelAndView submitDiagnostic(HttpServletRequest request,\n                                         HttpServletResponse response, Model model, int type,\n                                         Long auditId, Long appId, String nodes, String params) {\n\n\n        if (auditId == null && appId != null && appId != -1L) {\n            AppUser appUser = getUserInfo(request);\n            AppDesc appDesc = appService.getByAppId(appId);\n            AppAudit appAudit = appService.saveAppDiagnostic(appDesc, appUser, \"应用诊断任务:\" + DiagnosticTypeEnum.getKeyDesc(type));\n            auditId = appAudit.getId();\n        }\n        AppUser appUser = getUserInfo(request);\n        appAuditDao.updateAppAuditUser(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value(), appUser.getId());\n\n        long taskId = -1l;\n        if (type == DiagnosticTypeEnum.SCAN_KEY.getType()) {\n            String[] paramArray = params.split(\",\");\n            String pattern = paramArray.length > 0 ? paramArray[0] : \"\";\n            int size = paramArray.length > 1 ? Integer.parseInt(paramArray[1]) : 20;\n\n            taskId = taskService.addAppScanKeyTask(appId, auditId, nodes, pattern, size, 0);\n        } else if (type == DiagnosticTypeEnum.BIG_KEY.getType()) {\n            String[] paramArray = params.split(\",\");\n            long fromBytes = paramArray.length > 0 ? Long.parseLong(paramArray[0]) : 10;\n            int size = paramArray.length > 1 ? Integer.parseInt(paramArray[1]) : 20;\n\n            taskId = taskService.addAppBigKeyTask(appId, nodes, fromBytes, -1, size, auditId, 0);\n        } else if (type == DiagnosticTypeEnum.IDLE_KEY.getType()) {\n            String[] paramArray = params.split(\",\");\n            long idleTime = paramArray.length > 0 ? Integer.parseInt(paramArray[0]) : 7;\n            int size = paramArray.length > 1 ? Integer.parseInt(paramArray[1]) : 20;\n\n            taskId = taskService.addAppIdleKeyTask(appId, nodes, idleTime, size, auditId, 0);\n        } else if (type == DiagnosticTypeEnum.HOT_KEY.getType()) {\n            String[] paramArray = params.split(\",\");\n            String command = paramArray.length > 0 ? paramArray[0] : \"hotkey\";\n\n            taskId = taskService.addAppHotKeyTask(appId, nodes, command, auditId, 0);\n        } else if (type == DiagnosticTypeEnum.DEL_KEY.getType()) {\n            String[] paramArray = params.split(\",\");\n            String pattern = paramArray.length > 0 ? paramArray[0] : \"\";\n\n            taskId = taskService.addAppDelKeyTask(appId, nodes, pattern, auditId, 0);\n        } else if (type == DiagnosticTypeEnum.SLOT_ANALYSIS.getType()) {\n            taskId = taskService.addAppSlotAnalysisTask(appId, nodes, auditId, 0);\n        } else if (type == DiagnosticTypeEnum.SCAN_CLEAN.getType()) {\n            Map map = JSONObject.parseObject(params, Map.class);\n            taskId = taskService.addAppScanCleanTask(appId, map, auditId, 0);\n        }\n        JSONObject json = new JSONObject();\n        json.put(\"status\", \"success\");\n        json.put(\"taskId\", taskId);\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n\n    @RequestMapping(value = \"/diagnostic/result\")\n    public ModelAndView diagnosticResultList(HttpServletRequest request,\n                                             HttpServletResponse response, Model model,\n                                             Long appId, Long parentTaskId, Long auditId, Integer type, Integer diagnosticStatus) {\n\n        try {\n            Map<Integer, String> diagnosticTypeMap = new HashMap<>();\n            for (DiagnosticTypeEnum diagnosticType : DiagnosticTypeEnum.values()) {\n                diagnosticTypeMap.put(diagnosticType.getType(), diagnosticType.getDesc());\n            }\n            model.addAttribute(\"diagnosticTypeMap\", diagnosticTypeMap);\n\n            List<AppDesc> appDescList = appDao.getOnlineApps();\n            Map<Long, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(AppDesc::getAppId, Function.identity()));\n            model.addAttribute(\"appDescMap\", appDescMap);\n\n            List<DiagnosticTaskRecord> diagnosticTaskRecordList = diagnosticToolService.getDiagnosticTaskRecords(appId, parentTaskId, auditId, type, diagnosticStatus);\n            model.addAttribute(\"diagnosticTaskRecordList\", diagnosticTaskRecordList);\n\n            model.addAttribute(\"appId\", appId);\n            model.addAttribute(\"parentTaskId\", parentTaskId);\n            model.addAttribute(\"auditId\", auditId);\n            model.addAttribute(\"type\", type);\n            model.addAttribute(\"diagnosticStatus\", diagnosticStatus);\n            model.addAttribute(\"diagnosticActive\", SuccessEnum.SUCCESS.value());\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        return new ModelAndView(\"manage/diagnosticTool/resultList\");\n    }\n\n    @RequestMapping(value = \"/diagnostic/data\")\n    public ModelAndView diagnosticResult(HttpServletRequest request,\n                                         HttpServletResponse response, Model model, String redisKey, int type, boolean err) {\n        JSONObject json = new JSONObject();\n        if (!StringUtil.isBlank(redisKey)) {\n\n            if (type == DiagnosticTypeEnum.SCAN_KEY.getType()) {\n                List<String> result = diagnosticToolService.getScanDiagnosticData(redisKey);\n                json.put(\"count\", result.size());\n                json.put(\"result\", result);\n            } else if (type == DiagnosticTypeEnum.BIG_KEY.getType()\n                    || type == DiagnosticTypeEnum.IDLE_KEY.getType()\n                    || type == DiagnosticTypeEnum.SLOT_ANALYSIS.getType()) {\n                Map<String, String> result = diagnosticToolService.getDiagnosticDataMap(redisKey, type, err);\n                json.put(\"count\", result.size());\n                json.put(\"result\", result);\n            } else if (type == DiagnosticTypeEnum.HOT_KEY.getType()) {\n                String result = diagnosticToolService.getHotkeyDiagnosticData(redisKey);\n                json.put(\"result\", result == null ? \"\" : result.replaceAll(\"(\\\\r\\\\n|\\\\n|\\\\n\\\\r)\", \"<br/>\"));\n            } else if (type == DiagnosticTypeEnum.SCAN_CLEAN.getType()) {\n                List<String> result = diagnosticToolService.getScanCleanDiagnosticData(redisKey);\n                json.put(\"result\", result);\n            }\n        }\n        json.put(\"status\", String.valueOf(SuccessEnum.SUCCESS.value()));\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n    @RequestMapping(value = \"/diagnostic/sampleCompareData\")\n    public ModelAndView diagnosticSampleCompareResult(HttpServletRequest request,\n                                         HttpServletResponse response, Model model, String redisKey) {\n        List<String> result = new ArrayList<>();\n        try {\n            if (!StringUtil.isBlank(redisKey)) {\n                result = diagnosticToolService.getScanCleanDiagnosticData(redisKey);\n            }\n            model.addAttribute(\"diagnosticResultList\", result);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return new ModelAndView(\"manage/diagnosticTool/diagnosticSampleCompareResult\");\n    }\n\n    @RequestMapping(\"/commandExecute\")\n    public ModelAndView getCommandExecute(HttpServletRequest request,\n                                          HttpServletResponse response,\n                                          Model model, Long appId, String node, String command, Integer timeout) {\n        String result = \"\";\n        if (appId != null && appId > 0 && !StringUtil.isBlank(node)) {\n            model.addAttribute(\"appId\", appId);\n            model.addAttribute(\"node\", node);\n\n            String host = node.split(\":\")[0];\n            int port = Integer.parseInt(node.split(\":\")[1]);\n            result = redisCenter.executeAdminCommand(appId, host, port, command, timeout);\n        } else {\n            result = \"error\";\n        }\n        model.addAttribute(\"result\", result);\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        resultMap.put(\"result\", result);\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    @PostMapping(value = \"/diagnostic/sampleScan\")\n    public ModelAndView getSampleScan(HttpServletRequest request,\n                                      HttpServletResponse response,\n                                      Model model,\n                                      Long appId, String nodes, String pattern) {\n\n        List<String> sampleScanData = diagnosticToolService.getSampleScanData(appId, nodes, pattern);\n        JSONObject json = new JSONObject();\n        json.put(\"count\", sampleScanData.size());\n        json.put(\"result\", sampleScanData);\n        json.put(\"status\", \"success\");\n        sendMessage(response, json.toString());\n        return null;\n    }\n\n\n    @PostMapping(value = \"findInstancePatternKeys\")\n    public ModelAndView findInstancePatternKeys(Model model,\n                                                long appId,\n                                                String ip,\n                                                int port,\n                                                String pattern) {\n\n        model.addAttribute(\"result\", 1);\n        return new ModelAndView(\"\");\n    }\n\n    @PostMapping(value = \"findInstanceBigKey\")\n    public ModelAndView findInstanceBigKey(Model model,\n                                           long appId,\n                                           String ip,\n                                           int port,\n                                           long startBytes,\n                                           long endBytes) {\n        List<String> instanceBigKeyList = redisCenter.findInstanceBigKey(appId, ip, port, startBytes, endBytes);\n        model.addAttribute(\"instanceBigKeyList\", instanceBigKeyList);\n        return new ModelAndView(\"\");\n    }\n\n    @PostMapping(value = \"findClusterBigKey\")\n    public ModelAndView findClusterBigKey(Model model,\n                                          long appId,\n                                          long startBytes,\n                                          long endBytes) {\n        List<String> clusterBigKeyList = redisCenter.findClusterBigKey(appId, startBytes, endBytes);\n        model.addAttribute(\"clusterBigKeyList\", clusterBigKeyList);\n        return new ModelAndView(\"\");\n    }\n\n    @PostMapping(value = \"findInstanceIdleKeys\")\n    public ModelAndView findInstanceIdleKeys(Model model,\n                                             long appId,\n                                             String ip,\n                                             int port,\n                                             long idleDays) {\n        List<String> instanceIdleKeyList = redisCenter.findInstanceIdleKeys(appId, ip, port, idleDays);\n        model.addAttribute(\"instanceIdleKeyList\", instanceIdleKeyList);\n        return new ModelAndView(\"\");\n    }\n\n    @PostMapping(value = \"findClusterIdleKeys\")\n    public ModelAndView findClusterIdleKeys(Model model,\n                                            long appId,\n                                            long idleDays) {\n        List<String> clusterIdleKeyList = redisCenter.findClusterIdleKeys(appId, idleDays);\n        model.addAttribute(\"clusterIdleKeyList\", clusterIdleKeyList);\n        return new ModelAndView(\"\");\n    }\n\n    @PostMapping(value = \"delInstancePatternKeys\")\n    public ModelAndView delInstancePatternKeys(Model model,\n                                               long appId,\n                                               String ip,\n                                               int port,\n                                               String pattern) {\n        redisCenter.delInstancePatternKeys(appId, ip, port, pattern);\n        model.addAttribute(\"result\", 1);\n        return new ModelAndView(\"\");\n    }\n\n    @PostMapping(value = \"delClusterPatternKey\")\n    public ModelAndView delClusterPatternKey(Model model,\n                                             long appId,\n                                             String pattern) {\n        redisCenter.delClusterPatternKey(appId, pattern);\n        model.addAttribute(\"result\", 1);\n        return new ModelAndView(\"\");\n    }\n\n\n    @GetMapping(value = \"topologyExam/{appid}\")\n    public ModelAndView topologyExamByAppid(HttpServletRequest request,\n                                            HttpServletResponse response,\n                                            Model model,\n                                            @PathVariable(\"appid\") long appid) {\n        List<Map> res = toolService.topologyExamByAppid(appid);\n        model.addAttribute(\"result\", res);\n        write(response, res.toString());\n        return null;\n    }\n\n    @PostMapping(value = \"topologyExam\")\n    public ModelAndView topologyExam(Model model,\n                                     @RequestBody List<Long> appidList) {\n        toolService.topologyExam(appidList);\n        return null;\n    }\n\n    @PostMapping(value = \"topologyExam/all\")\n    public ModelAndView topologyExamAll(@RequestParam Boolean examTest) {\n        List<AppDesc> appDescList = appDao.getOnlineApps();\n        List<Long> appidList = new ArrayList<Long>();\n        for (AppDesc appDesc : appDescList) {\n            if (examTest == false) {\n                if (appDesc.getIsTest() == 0) {\n                    appidList.add(appDesc.getAppId());\n                }\n            } else {\n                appidList.add(appDesc.getAppId());\n            }\n        }\n        toolService.topologyExam(appidList);\n        return null;\n    }\n\n    @GetMapping(\"/restAppDescOfficer\")\n    public void restAppDescOfficer() {\n        toolService.restAppDescOfficer();\n    }\n\n\n    @GetMapping(\"/sendExpAppsStatDataEmail\")\n    public ModelAndView sendExpAppsStatDataEmail(@RequestParam(\"searchDate\") String searchDate) {\n        logger.info(\"begin-sendExpAppsStatDataEmail\");\n        coreAppsStatCenter.sendExpAppsStatDataEmail(searchDate);\n        logger.info(\"end-sendExpAppsStatDataEmail\");\n        return null;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/BaseController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.constant.AppUserTypeEnum;\nimport com.sohu.cache.dao.AppAuditDao;\nimport com.sohu.cache.dao.AppAuditLogDao;\nimport com.sohu.cache.dao.AppImportDao;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.RedisConfigTemplateService;\nimport com.sohu.cache.redis.RedisDeployCenter;\nimport com.sohu.cache.stats.app.AppDeployCenter;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.ResourceService;\nimport com.sohu.cache.web.service.UserLoginStatusService;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.AppEmailUtil;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.vladsch.flexmark.ext.tables.TablesExtension;\nimport com.vladsch.flexmark.html.HtmlRenderer;\nimport com.vladsch.flexmark.parser.Parser;\nimport com.vladsch.flexmark.parser.ParserEmulationProfile;\nimport com.vladsch.flexmark.util.ast.Document;\nimport com.vladsch.flexmark.util.builder.Extension;\nimport com.vladsch.flexmark.util.options.MutableDataSet;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ui.Model;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.PrintWriter;\nimport java.nio.charset.Charset;\nimport java.text.ParseException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 基类controller\n *\n * @author leifu\n * @Time 2014年10月16日\n */\npublic class BaseController {\n    protected final Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    protected UserService userService;\n\n    @Autowired\n    protected AppService appService;\n\n    @Autowired\n    protected MachineCenter machineCenter;\n\n    @Autowired\n    protected UserLoginStatusService userLoginStatusService;\n\n    @Autowired\n    protected RedisCenter redisCenter;\n\n    @Resource\n    protected AppDeployCenter appDeployCenter;\n\n    @Resource\n    protected AppAuditDao appAuditDao;\n\n    @Resource\n    protected AppImportDao appImportDao;\n\n    @Resource\n    protected AppAuditLogDao appAuditLogDao;\n\n    @Resource\n    protected InstanceDao instanceDao;\n\n    @Resource\n    protected RedisDeployCenter redisDeployCenter;\n\n    @Resource\n    protected AppEmailUtil appEmailUtil;\n\n    @Resource\n    protected AsyncService asyncService;\n\n    @Autowired\n    protected AppStatsCenter appStatsCenter;\n\n    @Autowired\n    protected InstanceDeployCenter instanceDeployCenter;\n\n    @Autowired\n    AssistRedisService assistRedisService;\n\n    @Resource\n    protected RedisConfigTemplateService redisConfigTemplateService;\n\n    @Resource\n    protected ResourceService resourceService;\n\n    public Boolean saveTempResource(String resourceId, String content) {\n        try {\n            assistRedisService.set(getResourceKey(resourceId), content);\n        } catch (Exception e) {\n            return false;\n        }\n        return true;\n    }\n\n    private String getResourceKey(String resourceId) {\n        return String.format(\"resource_%s\", resourceId);\n    }\n\n    public boolean clearTempResource(String resourceId) {\n        try {\n            assistRedisService.del(getResourceKey(resourceId));\n        } catch (Exception e) {\n            return false;\n        }\n        return true;\n    }\n\n    public String getTempResource(String resourceId) {\n        try {\n            return assistRedisService.get(getResourceKey(resourceId));\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    protected TimeBetween getJsonTimeBetween(HttpServletRequest request) throws ParseException {\n        String startDateParam = request.getParameter(\"startDate\");\n        String endDateParam = request.getParameter(\"endDate\");\n        Date startDate = DateUtil.parseYYYY_MM_dd(startDateParam);\n        Date endDate;\n        if (StringUtils.isBlank(endDateParam)) {\n            endDate = DateUtils.addDays(startDate, 1);\n        } else {\n            endDate = DateUtil.parseYYYY_MM_dd(endDateParam);\n        }\n        long beginTime = NumberUtils.toLong(DateUtil.formatYYYYMMddHHMM(startDate));\n        long endTime = NumberUtils.toLong(DateUtil.formatYYYYMMddHHMM(endDate));\n        return new TimeBetween(beginTime, endTime, startDate, endDate);\n    }\n\n    protected TimeBetween getTimeBetween(HttpServletRequest request, Model model, String startDateAtr,\n                                         String endDateAtr) throws ParseException {\n        String startDateParam = request.getParameter(startDateAtr);\n        String endDateParam = request.getParameter(endDateAtr);\n        Date startDate;\n        Date endDate;\n        if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n            startDate = new Date();\n            endDate = DateUtils.addDays(startDate, 1);\n        } else {\n            endDate = DateUtil.parseYYYY_MM_dd(endDateParam);\n            startDate = DateUtil.parseYYYY_MM_dd(startDateParam);\n        }\n        Date yesterDay = DateUtils.addDays(startDate, -1);\n\n        long beginTime = NumberUtils.toLong(DateUtil.formatYYYYMMddHHMM(startDate));\n        long endTime = NumberUtils.toLong(DateUtil.formatYYYYMMddHHMM(endDate));\n        model.addAttribute(startDateAtr, startDateParam);\n        model.addAttribute(endDateAtr, endDateParam);\n        model.addAttribute(\"yesterDay\", DateUtil.formatDate(yesterDay, \"yyyy-MM-dd\"));\n        return new TimeBetween(beginTime, endTime, startDate, endDate);\n    }\n\n    protected TimeBetween getTimeBetweenOneDay(HttpServletRequest request, String startDateAtr) throws ParseException {\n        String startDateParam = request.getParameter(startDateAtr);\n        Date startDate;\n        Date endDate;\n        if (StringUtils.isBlank(startDateParam)) {\n            startDate = new Date();\n        } else {\n            startDate = DateUtil.parseYYYY_MM_dd(startDateParam);\n        }\n        endDate = DateUtils.addDays(startDate, 1);\n        long beginTime = NumberUtils.toLong(DateUtil.formatYYYYMMddHHMM(startDate));\n        long endTime = NumberUtils.toLong(DateUtil.formatYYYYMMddHHMM(endDate));\n        return new TimeBetween(beginTime, endTime, startDate, endDate);\n    }\n\n    /**\n     * 返回用户基本信息\n     *\n     * @param request\n     * @return\n     */\n    public AppUser getUserInfo(HttpServletRequest request) {\n        String userName = userLoginStatusService.getUserNameFromLoginStatus(request);\n        return userService.getByName(userName);\n    }\n\n\n    /**\n     * 发送json消息\n     *\n     * @param response\n     * @param message\n     */\n    public void sendMessage(HttpServletResponse response, String message) {\n        response.reset();\n        response.setContentType(\"application/json;charset=UTF-8\");\n        PrintWriter printWriter = null;\n        try {\n            printWriter = response.getWriter();\n            printWriter.write(message);\n        } catch (IOException e) {\n            logger.error(ExceptionUtils.getFullStackTrace(e));\n        } finally {\n            if (printWriter != null) {\n                printWriter.flush();\n                printWriter.close();\n            }\n        }\n\n    }\n\n    /**\n     * @param response\n     * @param result\n     */\n    protected void write(HttpServletResponse response, String result) {\n        try {\n            response.setContentType(\"text/javascript\");\n            response.setCharacterEncoding(\"UTF-8\");\n            response.getWriter().print(result);\n            response.getWriter().flush();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    /**\n     * 查看用户对于app操作的权限\n     *\n     * @param request\n     * @param appId\n     * @return\n     */\n    protected boolean checkAppUserProvilege(HttpServletRequest request, long appId) {\n        // 当前用户\n        AppUser currentUser = getUserInfo(request);\n        if (currentUser == null) {\n            logger.error(\"currentUser is empty\");\n            return false;\n        }\n\n        if (AppUserTypeEnum.ADMIN_USER.value().equals(currentUser.getType())) {\n            return true;\n        }\n\n        // 应用用户列表\n        List<AppToUser> appToUsers = appService.getAppToUserList(appId);\n        if (CollectionUtils.isEmpty(appToUsers)) {\n            logger.error(\"appId {} userList is empty\", appId);\n            return false;\n        }\n\n        // 应用下用户id集合\n        Set<Long> appUserIdSet = new HashSet<Long>();\n        for (AppToUser appToUser : appToUsers) {\n            appUserIdSet.add(appToUser.getUserId());\n        }\n\n        //最终判断\n        if (!appUserIdSet.contains(currentUser.getId())) {\n            logger.error(\"currentUser {} hasn't previlege in appId {}\", currentUser.getId(), appId);\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 实例统计信息\n     *\n     * @param appId\n     * @param model\n     */\n    protected void fillAppInstanceStats(Long appId, Model model) {\n        // 实例列表\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        model.addAttribute(\"instanceList\", instanceList);\n        Map<Integer, List<InstanceInfo>> instanceListMap = instanceGroupByMaster(instanceList);\n        model.addAttribute(\"instanceListMap\", instanceListMap);\n\n\n        // 实例Map\n        Map<Integer, InstanceInfo> instanceInfoMap = new HashMap<Integer, InstanceInfo>();\n        for (InstanceInfo instanceInfo : instanceList) {\n            instanceInfoMap.put(instanceInfo.getId(), instanceInfo);\n        }\n        model.addAttribute(\"instanceInfoMap\", instanceInfoMap);\n\n        // 实例统计\n        List<InstanceStats> appInstanceStats = appService.getAppInstanceStats(appId);\n        Map<String, InstanceStats> instanceStatsMap = new HashMap<String, InstanceStats>();\n        for (InstanceStats instanceStats : appInstanceStats) {\n            instanceStatsMap.put(instanceStats.getIp() + \":\" + instanceStats.getPort(), instanceStats);\n        }\n        model.addAttribute(\"instanceStatsMap\", instanceStatsMap);\n\n        //slot分布\n        Map<String, InstanceSlotModel> clusterSlotsMap = redisCenter.getClusterSlotsMap(appId);\n        model.addAttribute(\"clusterSlotsMap\", clusterSlotsMap);\n\n        //机器列表\n        long startTime = System.currentTimeMillis();\n        List<MachineStats> machineList = machineCenter.getMachineStats(null, null, null, null, null, null, null);\n        Map<String, MachineStats> machineMap = machineList.stream().collect(Collectors.toMap(MachineStats::getIp, machineStats -> machineStats));\n        model.addAttribute(\"machineMap\", machineMap);\n        logger.info(\"getMachineStats cost: {}, appId: {}\", System.currentTimeMillis() - startTime, appId);\n\n        Map<String, Integer> machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n        model.addAttribute(\"machineInstanceCountMap\", machineInstanceCountMap);\n    }\n\n    private Map<Integer, List<InstanceInfo>> instanceGroupByMaster(List<InstanceInfo> instanceList) {\n        Map<Integer, List<InstanceInfo>> resultMap = new HashMap<Integer, List<InstanceInfo>>();\n        for (InstanceInfo info : instanceList) {\n            String roleDesc = info.getRoleDesc();\n            if (roleDesc != null && roleDesc.equals(\"master\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, info.getId(), new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(info.getId(), list);\n            } else if (roleDesc != null && roleDesc.equals(\"slave\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, info.getMasterInstanceId(), new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(info.getMasterInstanceId(), list);\n            } else if (roleDesc != null && roleDesc.equals(\"sentinel\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, -2, new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(-2, list);\n            } else {//offline\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, -1, new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(-1, list);\n            }\n        }\n        return resultMap;\n    }\n\n    /**\n     * 应用机器实例分布图\n     *\n     * @param appId\n     * @param model\n     */\n    protected void fillAppMachineInstanceTopology(Long appId, Model model) {\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        int groupId = 1;\n        // 1.分组，同一个主从在一组\n        for (int i = 0; i < instanceList.size(); i++) {\n            InstanceInfo instance = instanceList.get(i);\n            // 有了groupId，不再设置\n            if (instance.getGroupId() > 0) {\n                continue;\n            }\n            if (instance.isOffline()) {\n                continue;\n            }\n            for (int j = i + 1; j < instanceList.size(); j++) {\n                InstanceInfo instanceCompare = instanceList.get(j);\n                if (instanceCompare.isOffline()) {\n                    continue;\n                }\n                // 寻找主从对应关系\n                if (instanceCompare.getMasterInstanceId() == instance.getId()\n                        || instance.getMasterInstanceId() == instanceCompare.getId()) {\n                    instanceCompare.setGroupId(groupId);\n                }\n            }\n            instance.setGroupId(groupId++);\n        }\n\n        // 2.机器下的实例列表\n        Map<String, List<InstanceInfo>> machineInstanceMap = new HashMap<String, List<InstanceInfo>>();\n        for (InstanceInfo instance : instanceList) {\n            String ip = instance.getIp();\n            if (machineInstanceMap.containsKey(ip)) {\n                machineInstanceMap.get(ip).add(instance);\n            } else {\n                List<InstanceInfo> tempInstanceList = new ArrayList<InstanceInfo>();\n                tempInstanceList.add(instance);\n                machineInstanceMap.put(ip, tempInstanceList);\n            }\n        }\n\n        model.addAttribute(\"machineInstanceMap\", machineInstanceMap);\n        model.addAttribute(\"instancePairCount\", groupId - 1);\n    }\n\n    /**\n     * markdown to html\n     */\n    public String markdown2html(String filename, String suffix) throws Exception {\n        String templatePath = \"static/\" + filename + suffix;\n        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(templatePath);\n        if (inputStream == null) {\n            return null;\n        }\n        String markdown = new String(read(inputStream), Charset.forName(\"UTF-8\"));\n        MutableDataSet options = new MutableDataSet();\n        options.setFrom(ParserEmulationProfile.MARKDOWN);\n        options.set(Parser.EXTENSIONS, Arrays.asList(new Extension[]{TablesExtension.create()}));\n        Document document = Parser.builder(options).build().parse(markdown);\n        String html = HtmlRenderer.builder(options).build().render(document);\n        return html;\n    }\n\n    public byte[] read(InputStream inputStream) throws IOException {\n        byte[] buffer = new byte[1024];\n        int len = 0;\n        ByteArrayOutputStream bos = null;\n        try {\n            bos = new ByteArrayOutputStream();\n            while ((len = inputStream.read(buffer)) != -1) {\n                bos.write(buffer, 0, len);\n            }\n        } finally {\n            if (bos != null) {\n                try {\n                    bos.close();\n                } catch (IOException e) {\n                }\n            }\n            if (inputStream != null) {\n                try {\n                    inputStream.close();\n                } catch (IOException e) {\n                }\n            }\n        }\n        return bos.toByteArray();\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/ClientManageController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport com.sohu.cache.web.service.UserService;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport com.sohu.cache.client.service.ClientReportExceptionService;\nimport com.sohu.cache.client.service.ClientVersionService;\nimport com.sohu.cache.entity.AppClientVersion;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.ClientInstanceException;\nimport com.sohu.cache.web.enums.SuccessEnum;\n\n/**\n * 客户端管理\n * \n * @author leifu\n * @Date 2016年2月18日\n * @Time 下午4:55:32\n */\n@Controller\n@RequestMapping(\"manage/client\")\npublic class ClientManageController extends BaseController {\n\n    /**\n     * 客户端异常服务\n     */\n    @Resource(name = \"clientReportExceptionService\")\n    private ClientReportExceptionService clientReportExceptionService;\n    \n    /**\n     * 客户端版本服务\n     */\n    @Resource(name = \"clientVersionService\")\n    private ClientVersionService clientVersionService;\n\n    @Resource\n    private UserService userService;\n\n    /**\n     * /manage/client/exception\n     * @param request\n     * @param response\n     * @param model\n     * @return\n     */\n    @RequestMapping(value = \"/exception\")\n    public ModelAndView doClientExceptionStat(HttpServletRequest request, HttpServletResponse response, Model model) {\n        String ip = request.getParameter(\"ip\");\n        model.addAttribute(\"ip\", ip);\n        //近一个月\n        long collectTime = NumberUtils.toLong(new SimpleDateFormat(\"yyyyMMdd000000\").format(DateUtils.addMonths(new Date(), -1)));\n        \n        // 一段时间内客户端异常\n        List<ClientInstanceException> clientInstanceExceptionList = clientReportExceptionService.getInstanceExceptionStat(ip, collectTime);\n        model.addAttribute(\"clientInstanceExceptionList\", clientInstanceExceptionList);\n        \n        // 应用相关map\n        fillAppInfoMap(model);\n        \n        model.addAttribute(\"clientExceptionActive\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"manage/client/exception/list\");\n    }\n\n    private void fillAppInfoMap(Model model) {\n        List<AppDesc> appDescList = appService.getAllAppDesc();\n        \n        // 所有应用id和负责人对应关系\n        Map<Long, String> appIdOwnerMap = new HashMap<Long, String>();\n        for (AppDesc appDesc : appDescList) {\n            appIdOwnerMap.put(appDesc.getAppId(), userService.getOfficerName(appDesc.getOfficer()));\n        }\n        model.addAttribute(\"appIdOwnerMap\", appIdOwnerMap);\n        \n        // 所有应用id和应用名对应关系\n        Map<Long, String> appIdNameMap = new HashMap<Long, String>();\n        for (AppDesc appDesc : appDescList) {\n            appIdNameMap.put(appDesc.getAppId(), appDesc.getName());\n        }\n        model.addAttribute(\"appIdNameMap\", appIdNameMap);\n    }\n\n    /**\n     * /manage/client/version\n     * @param request\n     * @param response\n     * @param model\n     * @return\n     */\n    /*@RequestMapping(value = \"/version\")\n    public ModelAndView doVersionStat(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"),-1);\n        List<AppClientVersion> appClientVersionList =  clientVersionService.getAll(appId);\n        \n        // 应用相关map\n        fillAppInfoMap(model);\n        \n        model.addAttribute(\"appClientVersionList\", appClientVersionList);\n        model.addAttribute(\"clientVersionActive\", SuccessEnum.SUCCESS.value());\n        model.addAttribute(\"appId\", request.getParameter(\"appId\"));\n        \n        return new ModelAndView(\"manage/client/version/list\");\n    }*/\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/ConfigManageController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.Map.Entry;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\nimport org.apache.commons.collections.MapUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.entity.SystemConfig;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.service.ConfigService;\r\nimport com.sohu.cache.web.util.AppEmailUtil;\r\n\r\n/**\r\n * cachecloud配置管理\r\n * \r\n * @author leifu\r\n * @Date 2016年5月23日\r\n * @Time 上午10:31:16\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/config\")\r\npublic class ConfigManageController extends BaseController {\r\n\r\n    @Resource(name = \"configService\")\r\n    private ConfigService configService;\r\n    \r\n    @Resource(name = \"appEmailUtil\")\r\n    private AppEmailUtil appEmailUtil;\r\n\r\n    /**\r\n     * 初始化配置\r\n     * \r\n     * @param request\r\n     * @param response\r\n     * @param model\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/init\")\r\n    public ModelAndView init(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        List<SystemConfig> configList = configService.getConfigList(1);\r\n        model.addAttribute(\"configList\", configList);\r\n        model.addAttribute(\"success\", request.getParameter(\"success\"));\r\n        model.addAttribute(\"configActive\", SuccessEnum.SUCCESS.value());\r\n        return new ModelAndView(\"manage/config/init\");\r\n    }\r\n\r\n    /**\r\n     * 修改配置\r\n     * \r\n     * @param request\r\n     * @param response\r\n     * @param model\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/update\")\r\n    public ModelAndView update(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        logger.warn(\"user {} want to change config!\", appUser.getName());\r\n        List<SystemConfig> oldConfigList = configService.getConfigList(1);\r\n        SuccessEnum successEnum;\r\n        Map<String, String> configMap = new HashMap<String, String>();\r\n        try {\r\n            Map<String, String[]> paramMap = request.getParameterMap();\r\n            for (Entry<String, String[]> entry : paramMap.entrySet()) {\r\n                String key = entry.getKey();\r\n                String value = entry.getValue()[0];\r\n                if (StringUtils.isNotBlank(key)) {\r\n                    configMap.put(key, value);\r\n                }\r\n            }\r\n            if (MapUtils.isEmpty(configMap)) {\r\n                logger.error(\"params {} may be empty!!\", paramMap);\r\n            }\r\n            successEnum = configService.updateConfig(configMap);\r\n            if (successEnum.equals(SuccessEnum.SUCCESS)) {\r\n                configService.reloadSystemConfig();\r\n            }\r\n        } catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        Map<String, String> systemDifConfigMap = getDifConfigMap(oldConfigList, configMap);\r\n        appEmailUtil.sendSystemConfigDifEmail(appUser, systemDifConfigMap, successEnum);\r\n        logger.warn(\"user {} change config result is {}!\", appUser.getName(), successEnum.value());\r\n        return new ModelAndView(\"redirect:/manage/config/init?success=\" + successEnum.value());\r\n    }\r\n\r\n    private Map<String, String> getDifConfigMap(List<SystemConfig> oldConfigList, Map<String, String> configMap) {\r\n        Map<String, String> systemDifConfigMap = new HashMap<String, String>();\r\n        for (SystemConfig systemConfig : oldConfigList) {\r\n            String key = systemConfig.getConfigKey();\r\n            String oldValue = systemConfig.getConfigValue();\r\n            String newValue = configMap.get(key);\r\n            if (newValue != null && !oldValue.equals(newValue)) {\r\n                systemDifConfigMap.put(systemConfig.getInfo(), String.format(\"old value: %s, new value: %s\", oldValue, newValue));\r\n            }\r\n        }\r\n        return systemDifConfigMap;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/ExamToolController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.google.common.collect.Lists;\nimport com.sohu.cache.dao.MachineDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.task.tasks.daily.TopologyExamTask;\nimport com.sohu.cache.web.enums.ExamToolEnum;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2019/1/25\n */\n@Controller\n@RequestMapping(\"/manage/tool\")\npublic class ExamToolController extends BaseController{\n    @Autowired\n    private TaskService taskService;\n    @Autowired\n    private MachineDao machineDao;\n    @Autowired\n    TopologyExamTask topologyExamTask;\n\n    @RequestMapping(value = \"/topologyExam\")\n    public ModelAndView startTopologyExam(Model model,\n                                          long appId){\n\n        ArrayList<AppDesc> applist = new ArrayList<AppDesc>();\n        AppDesc appDesc = appService.getByAppId(appId);\n        if(appDesc.getAppId() > 0){\n            applist.add(appDesc);\n            Map<String, Object> info = topologyExamTask.check(applist);\n            model.addAttribute(\"checkInfo\",info);\n        }\n        return new ModelAndView(\"manage/appTool/appKeysDealTool\");\n    }\n\n    @RequestMapping(value = \"/topologyExam/online\",method = RequestMethod.POST)\n    public long startAllTopologyExam(){\n        long taskId=taskService.addAppTopologyExamTask(true, ExamToolEnum.EXAM_NON_TEST.getValue(),0,0);\n        return taskId;\n    }\n\n    @RequestMapping(value = \"/machineExam\",method = RequestMethod.GET)\n    public ModelAndView startMachineExam(Model model,\n                                         @RequestParam Integer useType,\n                                         @RequestParam String ipLike){\n        List<String> machineIpList=Lists.newArrayList();\n        List<MachineInfo> machineInfoByCondition = machineDao.getMachineInfoByCondition(ipLike, useType, -1, null, -1, null);\n        for (MachineInfo machineInfo:machineInfoByCondition){\n            machineIpList.add(machineInfo.getIp());\n        }\n        taskService.addMachineExamTask(machineIpList,useType,0);\n        return new ModelAndView(\"\");\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/ImportAppController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.constant.AppStatusEnum;\nimport com.sohu.cache.constant.ImportAppResult;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.dao.AppDataMigrateStatusDao;\nimport com.sohu.cache.dao.TaskQueueDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.stats.app.ImportAppCenter;\nimport com.sohu.cache.task.entity.TaskQueue;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.AppImportStatusEnum;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppImportService;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\nimport redis.clients.jedis.Jedis;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 已经存在Redis导入\n */\n@Controller\n@RequestMapping(\"/import/app\")\npublic class ImportAppController extends BaseController {\n\n    @Resource(name = \"importAppCenter\")\n    private ImportAppCenter importAppCenter;\n    @Autowired\n    private AppImportService appImportService;\n    @Autowired\n    private AppDataMigrateStatusDao appDataMigrateStatusDao;\n    @Autowired\n    private TaskQueueDao taskQueueDao;\n\n    @RequestMapping(\"/index\")\n    public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model, String tabTag) {\n        model.addAttribute(\"tabTag\", tabTag);\n        model.addAttribute(\"appImportActive\", SuccessEnum.SUCCESS.value());\n        List<AppImport> appImportList = appImportService.getImportAppList(-1);\n        model.addAttribute(\"appImportList\", appImportList);\n        model.addAttribute(\"appImportStatusMap\", Arrays.stream(AppImportStatusEnum.values()).collect(Collectors.toMap(AppImportStatusEnum::getStatus, Function.identity())));\n\n\n        return new ModelAndView(\"manage/appImport/list\");\n    }\n\n    @RequestMapping(value = \"init\")\n    public ModelAndView init(Model model, long importId) {\n        //1.获取导入信息\n        AppImport appImport = appImportService.get(importId);\n        if (appImport == null) {\n            return new ModelAndView(\"\");\n        }\n        Long appId = appImport.getAppId();\n        AppDesc appDesc = appService.getByAppId(appId);\n        int oldStatus = appImport.getStatus();\n        int status = oldStatus;\n\n        if (status == AppImportStatusEnum.PREPARE.getStatus()) {\n            //导入申请未处理阶段\n            if (appDesc.getVersionId() != -1) {\n                appImport.setRedisVersionName(resourceService.getResourceById(appDesc.getVersionId()).getName());\n            }\n            model.addAttribute(\"appDesc\", appDesc);\n        }\n        if (status > 10 && status < 20) {\n            //创建版本阶段\n            if (appDesc.getVersionId() == -1) {\n                SystemResource resource = resourceService.getResourceByName(appImport.getRedisVersionName());\n                if (resource != null) {\n                    appDesc.setVersionId(resource.getId());\n                    appService.update(appDesc);\n                    status = AppImportStatusEnum.VERSION_BUILD_END.getStatus();\n                    model.addAttribute(\"hasRedisVersion\", 1);\n                } else {\n                    model.addAttribute(\"hasRedisVersion\", 0);\n                }\n            } else {\n                status = AppImportStatusEnum.VERSION_BUILD_END.getStatus();\n                model.addAttribute(\"hasRedisVersion\", 1);\n            }\n        }\n        if (status >= 20 && status < 30) {\n            //创建应用阶段\n            long appBuildTaskId = appImport.getAppBuildTaskId();\n            if (appBuildTaskId > 0) {\n                TaskQueue appBuildTask = taskQueueDao.getById(appBuildTaskId);\n                if (appBuildTask != null && (appBuildTask.getStatus() == 2 || appBuildTask.getStatus() == 3)) {\n                    status = AppImportStatusEnum.APP_BUILD_ERROR.getStatus();\n                } else if (appBuildTask != null && appBuildTask.getStatus() == 4) {\n                    appDesc.setStatus(AppStatusEnum.STATUS_PUBLISHED.getStatus());\n                    appService.update(appDesc);\n                    status = AppImportStatusEnum.APP_BUILD_END.getStatus();\n                } else {\n                    status = AppImportStatusEnum.APP_BUILD_START.getStatus();\n                }\n            } else {\n                status = AppImportStatusEnum.APP_BUILD_INIT.getStatus();\n            }\n        }\n        if (status >= 30 && status < 40) {\n            //数据迁移阶段\n            long migrateId = appImport.getMigrateId();\n            if (migrateId > 0) {\n                AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.getByMigrateId(migrateId);\n                int migrateStatus = appDataMigrateStatus.getStatus();\n                if (migrateStatus == 1) {\n                    status = AppImportStatusEnum.MIGRATE_END.getStatus();\n                } else if (migrateStatus == 2) {\n                    status = AppImportStatusEnum.MIGRATE_ERROR.getStatus();\n                } else {\n                    status = AppImportStatusEnum.MIGRATE_START.getStatus();\n                }\n            }\n        }\n        if (status == AppImportStatusEnum.MIGRATE_END.getStatus()) {\n            model.addAttribute(\"appId\", appId);\n        }\n\n        if (oldStatus != status) {\n            appImport.setStatus(status);\n            appImportService.update(appImport);\n        }\n        model.addAttribute(\"appImport\", appImport);\n        model.addAttribute(\"appImportStatusMap\", Arrays.stream(AppImportStatusEnum.values()).collect(Collectors.toMap(AppImportStatusEnum::getStatus, Function.identity())));\n        return new ModelAndView(\"/manage/appImport/appImport\");\n    }\n\n    @RequestMapping(value = \"/preRebuildApp\")\n    public ModelAndView preRebuildApp(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long importId = NumberUtils.toLong(request.getParameter(\"importId\"));\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        AppImport appImport = appImportService.get(importId);\n        appImport.setStatus(AppImportStatusEnum.APP_BUILD_INIT.getStatus());\n        appImport.setAppBuildTaskId(0);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc != null) {\n            //offline instances\n            List<InstanceInfo> instanceInfos = instanceDao.getInstListByAppId(appId);\n            int type = appDesc.getType();\n            if (instanceInfos != null) {\n                instanceInfos.parallelStream().map(instanceInfo -> instanceOffline(appId, instanceInfo, type)).collect(Collectors.toList());\n            }\n            appDesc.setStatus(AppStatusEnum.STATUS_INITIALIZE.getStatus());\n            if (appService.update(appDesc) > 0 && appImportService.update(appImport) > 0) {\n                model.addAttribute(\"success\", 1);\n            }\n        }\n\n        appImportService.update(appImport);\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"/preReMigrate\")\n    public ModelAndView preReMigrate(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long importId = NumberUtils.toLong(request.getParameter(\"importId\"));\n        long appId = NumberUtils.toLong(request.getParameter(\"appId\"));\n        AppImport appImport = appImportService.get(importId);\n        appImport.setStatus(AppImportStatusEnum.APP_BUILD_END.getStatus());\n        appImport.setMigrateId(0);\n\n        AppDesc appDesc = appService.getByAppId(appId);\n        if (appDesc != null) {\n            //todo 清空数据可能耗时太长\n            if (cleanAppData(appId) && appImportService.update(appImport) > 0) {\n                model.addAttribute(\"success\", 1);\n            }\n        }\n\n        appImportService.update(appImport);\n        return new ModelAndView(\"\");\n    }\n\n    private boolean cleanAppData(long appId) {\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);\n        // 开始清除\n        for (InstanceInfo instance : instanceList) {\n            if (instance.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                continue;\n            }\n            String host = instance.getIp();\n            int port = instance.getPort();\n            // master + 非sentinel节点\n            BooleanEnum isMater = redisCenter.isMaster(appId, host, port);\n            if (isMater == BooleanEnum.TRUE && !TypeUtil.isRedisSentinel(instance.getType())) {\n                //异步线程处理\n                AsyncThreadPoolFactory.DEFAULT_ASYNC_THREAD_POOL.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        Jedis jedis = redisCenter.getAdminJedis(appId, host, port);\n                        jedis.getClient().setConnectionTimeout(1000);\n                        jedis.getClient().setSoTimeout(60000);\n                        try {\n                            logger.warn(\"{}:{} start clear data\", host, port);\n                            long start = System.currentTimeMillis();\n                            String result = jedis.flushAll();\n                            logger.warn(\"{}:{} finish clear data :{}, cost time:{} ms\", host, port, result,\n                                    (System.currentTimeMillis() - start));\n                        } catch (Exception e) {\n                            logger.error(\"clear redis: \" + e.getMessage(), e);\n                        } finally {\n                            jedis.close();\n                        }\n                    }\n                });\n            }\n        }\n        return true;\n    }\n\n    private boolean instanceOffline(long appId, InstanceInfo instanceInfo, int type) {\n        final String ip = instanceInfo.getIp();\n        final int port = instanceInfo.getPort();\n        boolean isShutdown = TypeUtil.isRedisType(type) ? redisCenter.shutdown(appId, ip, port) : true;\n        if(isShutdown){\n            isShutdown = redisCenter.checkShutdownSuccess(instanceInfo);\n        }\n        if (isShutdown) {\n            instanceInfo.setStatus(InstanceStatusEnum.OFFLINE_STATUS.getStatus());\n            instanceDao.update(instanceInfo);\n        } else {\n            return false;\n        }\n        return true;\n    }\n\n    @RequestMapping(value = \"/goOn\")\n    public ModelAndView goOn(HttpServletRequest request, HttpServletResponse response, Model model) {\n        long importId = NumberUtils.toLong(request.getParameter(\"importId\"));\n        int status = NumberUtils.toInt(request.getParameter(\"status\"));\n        long migrateId = NumberUtils.toLong(request.getParameter(\"migrateId\"));\n        long appBuildTaskId = NumberUtils.toLong(request.getParameter(\"appBuildTaskId\"));\n        AppImport appImport = appImportService.get(importId);\n        if (status > 0) {\n            appImport.setStatus(status);\n        }\n        if (migrateId > 0) {\n            appImport.setMigrateId(migrateId);\n        }\n        if (appBuildTaskId > 0) {\n            appImport.setAppBuildTaskId(appBuildTaskId);\n        }\n        appImportService.update(appImport);\n        model.addAttribute(\"success\", 1);\n        model.addAttribute(\"message\", \"成功\");\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"/check\")\n    public ModelAndView check(HttpServletRequest request, HttpServletResponse response, Model model) {\n\n        int type = NumberUtils.toInt(request.getParameter(\"type\"));\n        String appInstanceInfo = request.getParameter(\"appInstanceInfo\");\n        String password = request.getParameter(\"password\");\n        ImportAppResult importAppResult = importAppCenter.check(type, appInstanceInfo, password);\n        model.addAttribute(\"status\", importAppResult.getStatus());\n        model.addAttribute(\"message\", importAppResult.getMessage());\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"/add\")\n    public ModelAndView add(HttpServletRequest request,\n                            HttpServletResponse response, Model model) {\n        AppDesc appDesc = genAppDesc(request);\n        String appInstanceInfo = request.getParameter(\"appInstanceInfo\");\n        logger.warn(\"appDesc:\" + appDesc);\n        logger.warn(\"appInstanceInfo: \" + appInstanceInfo);\n\n        // 不需要对格式进行检验,check已经做过了。\n        boolean isSuccess = importAppCenter.importAppAndInstance(appDesc, appInstanceInfo);\n        logger.warn(\"import app result is {}\", isSuccess);\n\n        model.addAttribute(\"status\", isSuccess ? 1 : 0);\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 生成AppDesc\n     *\n     * @param request\n     * @return\n     */\n    private AppDesc genAppDesc(HttpServletRequest request) {\n        // 当前用户\n        AppUser currentUser = getUserInfo(request);\n        // 当前时间\n        Date date = new Date();\n        // 组装Appdesc\n        AppDesc appDesc = new AppDesc();\n        appDesc.setName(request.getParameter(\"name\"));\n        appDesc.setIntro(request.getParameter(\"intro\"));\n        appDesc.setOfficer(request.getParameter(\"officer\"));\n        appDesc.setType(NumberUtils.toInt(request.getParameter(\"type\")));\n        appDesc.setIsTest(NumberUtils.toInt(request.getParameter(\"isTest\")));\n        appDesc.setMemAlertValue(NumberUtils.toInt(request.getParameter(\"memAlertValue\")));\n        appDesc.setAppKey(request.getParameter(\"password\"));\n        appDesc.setUserId(currentUser.getId());\n        appDesc.setStatus(2);\n        appDesc.setCreateTime(date);\n        appDesc.setPassedTime(date);\n        appDesc.setVerId(1);\n\n        return appDesc;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/IndexController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * 首页\n *\n * @author leifu\n * @Date 2014年10月28日\n * @Time 上午10:49:32\n */\n@Controller\n@RequestMapping(\"/\")\npublic class IndexController extends BaseController {\n\n    @RequestMapping(value = \"\")\n    public ModelAndView index(HttpServletRequest request, HttpServletResponse response) {\n        String userName = userLoginStatusService.getUserNameFromTicket(request);\n        String redirectUrl = request.getParameter(\"redirectUrl\");\n        if(StringUtils.isBlank(redirectUrl)){\n            redirectUrl = \"/admin/app/list\";\n        }\n        if (StringUtils.isNotBlank(userName)) {\n            userLoginStatusService.addLoginStatus(request, response, userName);\n        }\n        return new ModelAndView(\"redirect:\" + redirectUrl);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/InstanceAlertValueController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.sohu.cache.constant.ErrorMessageEnum;\r\nimport com.sohu.cache.dao.InstanceDao;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.entity.InstanceAlertConfig;\r\nimport com.sohu.cache.entity.InstanceInfo;\r\nimport com.sohu.cache.redis.enums.*;\r\nimport com.sohu.cache.stats.instance.InstanceAlertConfigService;\r\nimport com.sohu.cache.util.TypeUtil;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.vo.AlertConfig;\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.springframework.dao.DuplicateKeyException;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.util.*;\r\nimport java.util.stream.Collectors;\r\n\r\n/**\r\n * 实例报警阀值\r\n *\r\n * @author leifu\r\n * @Date 2016年8月24日\r\n * @Time 下午1:24:25\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/instanceAlert\")\r\npublic class InstanceAlertValueController extends BaseController {\r\n\r\n    @Resource(name = \"instanceAlertConfigService\")\r\n    private InstanceAlertConfigService instanceAlertConfigService;\r\n\r\n    @Resource(name = \"instanceDao\")\r\n    private InstanceDao instanceDao;\r\n\r\n    /**\r\n     * 初始化配置\r\n     */\r\n    @RequestMapping(value = \"/init\")\r\n    public ModelAndView init(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        int appType = 0;\r\n//        String appTypeStr = request.getParameter(\"appType\");\r\n//        if (StringUtils.isNotEmpty(appTypeStr)) {\r\n//            appType = Integer.parseInt(appTypeStr);\r\n//        }\r\n        model.addAttribute(\"appType\", appType);\r\n        model.addAttribute(\"instanceAlertCheckCycleEnumList\", InstanceAlertCheckCycleEnum.getInstanceAlertCheckCycleEnumList());\r\n        model.addAttribute(\"instanceAlertCompareTypeEnumList\", InstanceAlertCompareTypeEnum.getInstanceAlertCompareTypeEnumList());\r\n        model.addAttribute(\"redisAlertConfigEnumList\", RedisAlertConfigEnum.getRedisAlertConfigEnumList());\r\n        List<InstanceAlertConfig> globalAlertConfigList = instanceAlertConfigService.getByTypeAndAppType(InstanceAlertTypeEnum.ALL_ALERT.getValue(), appType);\r\n        model.addAttribute(\"instanceAlertAllList\", globalAlertConfigList);\r\n        model.addAttribute(\"redisUsedGlobalAlertConfigList\", distinctUsedGlobalAlert(globalAlertConfigList));\r\n        model.addAttribute(\"instanceAlertList\", instanceAlertConfigService.getAll());\r\n        model.addAttribute(\"success\", request.getParameter(\"success\"));\r\n        model.addAttribute(\"redisAlertValueActive\", SuccessEnum.SUCCESS.value());\r\n        List<InstanceAlertConfig> instanceAlertSpecialList = instanceAlertConfigService.getByTypeAndAppType(InstanceAlertTypeEnum.INSTANCE_ALERT.getValue(), appType);\r\n        List<InstanceAlertConfig> appAlertSpecialList = instanceAlertConfigService.getByTypeAndAppType(InstanceAlertTypeEnum.APP_ALERT.getValue(), appType);\r\n        fillinstanceHostPort(instanceAlertSpecialList);\r\n        List<InstanceAlertConfig> appAndInstanceAlertConfigList = addAppAlertConfigToInstanceSpecialList(appAlertSpecialList, instanceAlertSpecialList);\r\n        model.addAttribute(\"instanceAlertSpecialList\", appAndInstanceAlertConfigList);\r\n        return new ModelAndView(\"manage/instanceAlert/init\");\r\n    }\r\n\r\n    /**\r\n     * 筛选出使用的全局配置，唯一的\r\n     * @param globalAlertConfigList\r\n     * @return\r\n     */\r\n    private List<AlertConfig> distinctUsedGlobalAlert(List<InstanceAlertConfig> globalAlertConfigList) {\r\n        if(CollectionUtils.isEmpty(globalAlertConfigList)){\r\n            return Collections.emptyList();\r\n        }\r\n        List<AlertConfig> alertConfigList = new ArrayList<>();\r\n        globalAlertConfigList.stream().forEach(instanceAlertConfig -> {\r\n            AlertConfig alertConfig = new AlertConfig();\r\n            alertConfig.setValue(instanceAlertConfig.getAlertConfig());\r\n            alertConfig.setInfo(instanceAlertConfig.getConfigInfo());\r\n            alertConfigList.add(alertConfig);\r\n        });\r\n        return alertConfigList.stream().distinct().collect(Collectors.toList());\r\n    }\r\n\r\n    /**\r\n     * 填充hostport\r\n     *\r\n     * @param instanceAlertSpecialList\r\n     */\r\n    private void fillinstanceHostPort(List<InstanceAlertConfig> instanceAlertSpecialList) {\r\n        if (CollectionUtils.isEmpty(instanceAlertSpecialList)) {\r\n            return;\r\n        }\r\n        for (InstanceAlertConfig instanceAlertConfig : instanceAlertSpecialList) {\r\n            long instanceId = instanceAlertConfig.getInstanceId();\r\n            InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\r\n            if (instanceInfo == null) {\r\n                continue;\r\n            }\r\n            instanceAlertConfig.setInstanceInfo(instanceInfo);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 将应用报警封装为特殊实例报警进行返回\r\n     * @param appAlertSpecialList\r\n     * @param instanceAlertSpecialList\r\n     */\r\n    private List<InstanceAlertConfig> addAppAlertConfigToInstanceSpecialList(List<InstanceAlertConfig> appAlertSpecialList, List<InstanceAlertConfig> instanceAlertSpecialList) {\r\n        if(CollectionUtils.isNotEmpty(appAlertSpecialList)){\r\n            if(CollectionUtils.isEmpty(instanceAlertSpecialList)){\r\n                return appAlertSpecialList;\r\n            }else{\r\n                instanceAlertSpecialList.addAll(appAlertSpecialList);\r\n                return instanceAlertSpecialList.stream().sorted(Comparator.comparing(instanceAlertConfig -> instanceAlertConfig.getId())).collect(Collectors.toList());\r\n            }\r\n        }\r\n        return instanceAlertSpecialList;\r\n    }\r\n\r\n    /**\r\n     * 添加配置\r\n     */\r\n    @RequestMapping(value = \"/add\")\r\n    public ModelAndView add(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        InstanceAlertConfig instanceAlertConfig = getInstanceAlertConfig(request);\r\n        SuccessEnum successEnum;\r\n        try {\r\n            //如果未传重要程度，则查询已存在的全局配置获取\r\n            if(instanceAlertConfig.getImportantLevel() == null){\r\n                InstanceAlertConfig globalAlertConfig = instanceAlertConfigService.getGlobalAlertConfigByCondition(instanceAlertConfig.getAlertConfig(), instanceAlertConfig.getCompareType(), instanceAlertConfig.getAppType());\r\n                if(globalAlertConfig != null){\r\n                    instanceAlertConfig.setImportantLevel(globalAlertConfig.getImportantLevel() == null ? 0 : globalAlertConfig.getImportantLevel());\r\n                }else{\r\n                    instanceAlertConfig.setImportantLevel(0);\r\n                }\r\n            }\r\n            logger.warn(\"user {} want to add instanceAlertConfig {}\", appUser.getName(), instanceAlertConfig);\r\n            instanceAlertConfigService.save(instanceAlertConfig);\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.warn(\"user {} add instanceAlertConfig {}, result is {}\", appUser.getName(), instanceAlertConfig, successEnum.value());\r\n        model.addAttribute(\"status\", successEnum.value());\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    /**\r\n     * 添加应用配置,仅添加一条信息\r\n     */\r\n    @RequestMapping(value = \"/addApp\")\r\n    public ModelAndView addApp(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        int appid = NumberUtils.toInt(request.getParameter(\"appid\"));\r\n        //判断此应用下是否有实例\r\n        List<InstanceInfo> instancelist = instanceDao.getEffectiveInstListByAppId(appid);\r\n        if(instancelist != null && instancelist.size()>0){\r\n            InstanceAlertConfig appAlertConfig = getAppAlertConfig(request);\r\n            SuccessEnum successEnum;\r\n            try {\r\n                logger.warn(\"user {} want to add app instanceAlertConfig, size is {}\", appUser.getName(), instancelist.size());\r\n                //如果未传重要程度，则查询已存在的全局配置获取\r\n                if(appAlertConfig.getImportantLevel() == null){\r\n                    InstanceAlertConfig globalAlertConfig = instanceAlertConfigService.getGlobalAlertConfigByCondition(appAlertConfig.getAlertConfig(), appAlertConfig.getCompareType(), appAlertConfig.getAppType());\r\n                    if(globalAlertConfig != null){\r\n                        appAlertConfig.setImportantLevel(globalAlertConfig.getImportantLevel() == null ? 0 : globalAlertConfig.getImportantLevel());\r\n                    }else{\r\n                        appAlertConfig.setImportantLevel(0);\r\n                    }\r\n                }\r\n                instanceAlertConfigService.save(appAlertConfig);\r\n                successEnum = SuccessEnum.SUCCESS;\r\n            } catch (Exception e) {\r\n                successEnum = SuccessEnum.FAIL;\r\n                model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\r\n                logger.error(e.getMessage(), e);\r\n            }\r\n            logger.warn(\"user {} add app , result is {}\", appUser.getName(), successEnum);\r\n            model.addAttribute(\"status\", successEnum.value());\r\n        }\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    /**\r\n     * 检查hostPort是否存在\r\n     */\r\n    @RequestMapping(value = \"/checkInstanceHostPort\")\r\n    public ModelAndView checkInstanceHostPort(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        String hostPort = request.getParameter(\"instanceHostPort\");\r\n        if (StringUtils.isBlank(hostPort)) {\r\n            model.addAttribute(\"status\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", \"参数为空\");\r\n            return new ModelAndView(\"\");\r\n        }\r\n        String[] hostPortArr = hostPort.split(\":\");\r\n        if (hostPortArr.length != 2) {\r\n            model.addAttribute(\"status\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", \"hostPort:\" + hostPort + \"格式错误\");\r\n            return new ModelAndView(\"\");\r\n        }\r\n        String host = hostPortArr[0];\r\n        int port = NumberUtils.toInt(hostPortArr[1]);\r\n        InstanceInfo instanceInfo = instanceDao.getAllInstByIpAndPort(host, port);\r\n        if (instanceInfo == null) {\r\n            model.addAttribute(\"status\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", \"hostPort:\" + hostPort + \"不存在\");\r\n        } else {\r\n            model.addAttribute(\"status\", SuccessEnum.SUCCESS.value());\r\n        }\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    /**\r\n     * 初始化配置\r\n     */\r\n    @RequestMapping(value = \"/monitor\")\r\n    public ModelAndView monitor(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        instanceAlertConfigService.monitorLastMinuteAllInstanceInfo();\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * 修改配置\r\n     */\r\n    @RequestMapping(value = \"/update\")\r\n    public ModelAndView update(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        int id = NumberUtils.toInt(request.getParameter(\"id\"));\r\n        String alertValue = request.getParameter(\"alertValue\");\r\n        int checkCycle = NumberUtils.toInt(request.getParameter(\"checkCycle\"));\r\n        int compareType = NumberUtils.toInt(request.getParameter(\"compareType\"));\r\n        int importantLevel = NumberUtils.toInt(request.getParameter(\"importantLevel\"));\r\n        logger.warn(\"user {} want to change instance alert id={}, alertValue={}, checkCycle={}, compareType={}\", appUser.getName(), id, alertValue, checkCycle, compareType);\r\n        SuccessEnum successEnum;\r\n        try {\r\n            InstanceAlertConfig orgInstAlertConfig = instanceAlertConfigService.get(id);\r\n            instanceAlertConfigService.update(id, alertValue, checkCycle, compareType, importantLevel);\r\n            //判断是否为更新紧急程度，如是，判断是否为全局报警，如是，则更新所有报警级别为此级别\r\n            if(orgInstAlertConfig != null && importantLevel != orgInstAlertConfig.getImportantLevel()){\r\n                if(InstanceAlertTypeEnum.ALL_ALERT.getValue() == orgInstAlertConfig.getType()){\r\n                    instanceAlertConfigService.updateImportantLevel(orgInstAlertConfig.getAlertConfig(), compareType, importantLevel, orgInstAlertConfig.getAppType());\r\n                }\r\n            }\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } catch (DuplicateKeyException e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            model.addAttribute(\"message\", ErrorMessageEnum.ALERT_CONFIG_CONSTRAINT_ERROR_MSG.getMessage());\r\n            logger.error(e.getMessage(), e);\r\n        }catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.warn(\"user {} change instance alert id={}, alertValue={}, checkCycle={}, compareType={}, result is {}\", appUser.getName(), id, alertValue, checkCycle, compareType, successEnum.info());\r\n        model.addAttribute(\"status\", successEnum.value());\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    /**\r\n     * 删除配置\r\n     */\r\n    @RequestMapping(value = \"/remove\")\r\n    public ModelAndView remove(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        int id = NumberUtils.toInt(request.getParameter(\"id\"));\r\n        InstanceAlertConfig instanceAlertConfig = instanceAlertConfigService.get(id);\r\n        logger.warn(\"user {} want to delete config id {}, instanceAlertConfig {}\", appUser.getName(), id, instanceAlertConfig);\r\n        SuccessEnum successEnum;\r\n        try {\r\n            instanceAlertConfigService.remove(id);\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.warn(\"user {} want to delete config id {}, instanceAlertConfig {}, result is {}\", appUser.getName(), id, instanceAlertConfig, successEnum.info());\r\n        model.addAttribute(\"status\", successEnum.value());\r\n        return new ModelAndView(\"\");\r\n\r\n    }\r\n\r\n\r\n    private InstanceInfo getInstanceInfo(String hostPort) {\r\n        String[] hostPortArr = hostPort.split(\":\");\r\n        String host = hostPortArr[0];\r\n        int port = NumberUtils.toInt(hostPortArr[1]);\r\n        return instanceDao.getAllInstByIpAndPort(host, port);\r\n    }\r\n\r\n\r\n    private InstanceAlertConfig getInstanceAlertConfig(HttpServletRequest request) {\r\n        // 相关参数\r\n        Date now = new Date();\r\n        String alertConfig = request.getParameter(\"alertConfig\");\r\n        String alertValue = request.getParameter(\"alertValue\");\r\n        String configInfo = request.getParameter(\"configInfo\");\r\n        int compareType = NumberUtils.toInt(request.getParameter(\"compareType\"));\r\n        int checkCycle = NumberUtils.toInt(request.getParameter(\"checkCycle\"));\r\n        String importantLevelStr = request.getParameter(\"importantLevel\");\r\n        Integer importantLevel = null;\r\n        if(StringUtils.isNotEmpty(importantLevelStr)){\r\n            importantLevel = NumberUtils.toInt(importantLevelStr);\r\n        }\r\n        int instanceId = 0;\r\n        int type = NumberUtils.toInt(request.getParameter(\"type\"));\r\n        int appType = NumberUtils.toInt(request.getParameter(\"appType\"));\r\n        if (InstanceAlertTypeEnum.INSTANCE_ALERT.getValue() == type) {\r\n            String hostPort = request.getParameter(\"instanceHostPort\");\r\n            InstanceInfo instanceInfo = getInstanceInfo(hostPort);\r\n            instanceId = instanceInfo.getId();\r\n        }\r\n        // 生成对象\r\n        InstanceAlertConfig instanceAlertConfig = new InstanceAlertConfig();\r\n        instanceAlertConfig.setAlertConfig(alertConfig);\r\n        instanceAlertConfig.setAlertValue(alertValue);\r\n        instanceAlertConfig.setConfigInfo(configInfo);\r\n        instanceAlertConfig.setCompareType(compareType);\r\n        instanceAlertConfig.setInstanceId(instanceId);\r\n        instanceAlertConfig.setCheckCycle(checkCycle);\r\n        instanceAlertConfig.setLastCheckTime(now);\r\n        instanceAlertConfig.setType(type);\r\n        instanceAlertConfig.setAppType(appType);\r\n        instanceAlertConfig.setUpdateTime(now);\r\n        instanceAlertConfig.setImportantLevel(importantLevel);\r\n        instanceAlertConfig.setStatus(InstanceAlertStatusEnum.YES.getValue());\r\n        return instanceAlertConfig;\r\n    }\r\n\r\n    private List<InstanceAlertConfig> getInstanceAlertConfig(HttpServletRequest request, List<InstanceInfo> instanceList) {\r\n\r\n        List<InstanceAlertConfig> instanceAlertConfigList = new ArrayList<InstanceAlertConfig>();\r\n        if (instanceList != null && instanceList.size() > 0) {\r\n            for (InstanceInfo instance : instanceList) {\r\n\r\n                // sentinel节点忽略\r\n                if(TypeUtil.isRedisSentinel(instance.getType())){\r\n                    logger.info(\"sentinel node ignore\");\r\n                    continue;\r\n                }\r\n                logger.info(\"ip:{},port:{} alert born \",instance.getHostId(),instance.getHostPort());\r\n                // 相关参数\r\n                Date now = new Date();\r\n                String alertConfig = request.getParameter(\"alertConfig\");\r\n                String alertValue = request.getParameter(\"alertValue\");\r\n                RedisAlertConfigEnum redisAlertConfigEnum = RedisAlertConfigEnum.getRedisAlertConfig(alertConfig);\r\n                String configInfo = redisAlertConfigEnum == null ? \"\" : redisAlertConfigEnum.getInfo();\r\n                int compareType = NumberUtils.toInt(request.getParameter(\"compareType\"));\r\n                int checkCycle = NumberUtils.toInt(request.getParameter(\"checkCycle\"));\r\n                int instanceId =  instance.getId();\r\n                // 生成对象\r\n                InstanceAlertConfig instanceAlertConfig = new InstanceAlertConfig();\r\n                instanceAlertConfig.setAlertConfig(alertConfig);\r\n                instanceAlertConfig.setAlertValue(alertValue);\r\n                instanceAlertConfig.setConfigInfo(configInfo);\r\n                instanceAlertConfig.setCompareType(compareType);\r\n                instanceAlertConfig.setInstanceId(instanceId);\r\n                instanceAlertConfig.setCheckCycle(checkCycle);\r\n                instanceAlertConfig.setLastCheckTime(now);\r\n                instanceAlertConfig.setType(2);\r\n                instanceAlertConfig.setUpdateTime(now);\r\n                instanceAlertConfig.setStatus(InstanceAlertStatusEnum.YES.getValue());\r\n                //添加至监控列表\r\n                instanceAlertConfigList.add(instanceAlertConfig);\r\n            }\r\n        }\r\n\r\n        return instanceAlertConfigList;\r\n    }\r\n\r\n    private InstanceAlertConfig getAppAlertConfig(HttpServletRequest request) {\r\n        // 相关参数\r\n        Date now = new Date();\r\n        long appid = NumberUtils.toLong(request.getParameter(\"appid\"));\r\n        String alertConfig = request.getParameter(\"alertConfig\");\r\n        String alertValue = request.getParameter(\"alertValue\");\r\n        String configInfo = request.getParameter(\"configInfo\");\r\n        int compareType = NumberUtils.toInt(request.getParameter(\"compareType\"));\r\n        int checkCycle = NumberUtils.toInt(request.getParameter(\"checkCycle\"));\r\n        Integer appType = NumberUtils.toInt(request.getParameter(\"appType\"));\r\n        String importantLevelStr = request.getParameter(\"importantLevel\");\r\n        Integer importantLevel = null;\r\n        if(StringUtils.isNotEmpty(importantLevelStr)){\r\n            importantLevel = NumberUtils.toInt(importantLevelStr);\r\n        }\r\n        // 生成对象\r\n        InstanceAlertConfig instanceAlertConfig = new InstanceAlertConfig();\r\n        instanceAlertConfig.setAlertConfig(alertConfig);\r\n        instanceAlertConfig.setAlertValue(alertValue);\r\n        instanceAlertConfig.setConfigInfo(configInfo);\r\n        instanceAlertConfig.setCompareType(compareType);\r\n        instanceAlertConfig.setInstanceId(appid);\r\n        instanceAlertConfig.setCheckCycle(checkCycle);\r\n        instanceAlertConfig.setLastCheckTime(now);\r\n        instanceAlertConfig.setType(3);\r\n        instanceAlertConfig.setAppType(appType);\r\n        instanceAlertConfig.setUpdateTime(now);\r\n        instanceAlertConfig.setStatus(InstanceAlertStatusEnum.YES.getValue());\r\n        instanceAlertConfig.setImportantLevel(importantLevel);\r\n        return instanceAlertConfig;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/InstanceController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.sohu.cache.alert.InstanceAlertService;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\nimport com.sohu.cache.web.chart.key.ChartKeysUtil;\nimport com.sohu.cache.web.chart.model.SplineChartEntity;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.WebClientComponent;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.vo.RedisSlowLog;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.text.ParseException;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Created by hym on 14-7-27.\n */\n@Controller\n@RequestMapping(\"/admin/instance\")\npublic class InstanceController {\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Resource(name = \"instanceStatsCenter\")\n    private InstanceStatsCenter instanceStatsCenter;\n\n    @Resource(name = \"appStatsCenter\")\n    private AppStatsCenter appStatsCenter;\n\n    @Resource(name = \"redisCenter\")\n    private RedisCenter redisCenter;\n\n    @Resource\n    private InstanceAlertService instanceAlertService;\n\n    @RequestMapping(\"/index\")\n    public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId, Long appId, String tabTag) {\n\n        String startDateParam = request.getParameter(\"startDate\");\n        String endDateParam = request.getParameter(\"endDate\");\n\n        if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n            Date endDate = new Date();\n            Date startDate = DateUtils.addDays(endDate, -1);\n            startDateParam = DateUtil.formatDate(startDate, \"yyyyMMdd\");\n            endDateParam = DateUtil.formatDate(endDate, \"yyyyMMdd\");\n        }\n        model.addAttribute(\"startDate\", startDateParam);\n        model.addAttribute(\"endDate\", endDateParam);\n\n        String condition = request.getParameter(\"condition\");\n        int conditionInt = StringUtils.isEmpty(condition) ? 0 : Integer.parseInt(condition);\n        model.addAttribute(\"condition\", conditionInt);\n\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n            InstanceInfo instanceInfo = instanceStatsCenter.getInstanceInfo(instanceId);\n\n            if (instanceInfo == null) {\n                model.addAttribute(\"type\", -1);\n            } else {\n                if (appId != null && appId > 0) {\n                    model.addAttribute(\"appId\", appId);\n                } else {\n                    model.addAttribute(\"appId\", instanceInfo.getAppId());\n                }\n                model.addAttribute(\"type\", instanceInfo.getType());\n            }\n        } else {\n\n        }\n        if (tabTag != null) {\n            model.addAttribute(\"tabTag\", tabTag);\n        }\n        return new ModelAndView(\"instance/instanceIndex\");\n    }\n\n    @RequestMapping(\"/stat\")\n    public ModelAndView stat(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId) {\n\n        String startDateParam = request.getParameter(\"startDate\");\n        String endDateParam = request.getParameter(\"endDate\");\n\n        if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n            Date endDate = new Date();\n            Date startDate = DateUtils.addDays(endDate, -1);\n            startDateParam = DateUtil.formatDate(startDate, \"yyyyMMdd\");\n            endDateParam = DateUtil.formatDate(endDate, \"yyyyMMdd\");\n        }\n        model.addAttribute(\"startDate\", startDateParam);\n        model.addAttribute(\"endDate\", endDateParam);\n\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n            InstanceInfo instanceInfo = instanceStatsCenter.getInstanceInfo(instanceId);\n            model.addAttribute(\"instanceInfo\", instanceInfo);\n            model.addAttribute(\"appId\", instanceInfo.getAppId());\n            model.addAttribute(\"appDetail\", appStatsCenter.getAppDetail(instanceInfo.getAppId()));\n            InstanceStats instanceStats = instanceStatsCenter.getInstanceStats(instanceId);\n            model.addAttribute(\"instanceStats\", instanceStats);\n            List<AppCommandStats> topLimitAppCommandStatsList = appStatsCenter.getTopLimitAppCommandStatsList(instanceInfo.getAppId(), Long.parseLong(startDateParam) * 10000, Long.parseLong(endDateParam) * 10000, 5);\n            model.addAttribute(\"appCommandStats\", topLimitAppCommandStatsList);\n        }\n        return new ModelAndView(\"instance/instanceStat\");\n    }\n\n    @RequestMapping(\"/advancedAnalysis\")\n    public ModelAndView advancedAnalysis(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId) {\n\n        String startDateParam = request.getParameter(\"startDate\");\n        String endDateParam = request.getParameter(\"endDate\");\n        String formatStartDate = null;\n        String formatEndDate = null;\n\n        if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n            Date endDate = new Date();\n            Date startDate = DateUtils.addDays(endDate, -1);\n            startDateParam = DateUtil.formatDate(startDate, \"yyyyMMdd\");\n            endDateParam = DateUtil.formatDate(endDate, \"yyyyMMdd\");\n            formatStartDate = startDateParam;\n            formatEndDate = endDateParam;\n        }else{\n            if(startDateParam.contains(\"-\") && endDateParam.contains(\"-\")){\n                try{\n                    formatStartDate = DateUtil.formatDate(DateUtil.parseYYYY_MM_dd(startDateParam), \"yyyyMMdd\");\n                    formatEndDate = DateUtil.formatDate(DateUtil.parseYYYY_MM_dd(endDateParam), \"yyyyMMdd\");\n                }catch (Exception e){\n                    logger.error(\"date format error\", e);\n                }\n            }else{\n                formatStartDate = startDateParam;\n                formatEndDate = endDateParam;\n            }\n        }\n        model.addAttribute(\"startDate\", startDateParam);\n        model.addAttribute(\"endDate\", endDateParam);\n\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n            InstanceInfo instanceInfo = instanceStatsCenter.getInstanceInfo(instanceId);\n            model.addAttribute(\"instanceInfo\", instanceInfo);\n            model.addAttribute(\"appId\", instanceInfo.getAppId());\n            List<AppCommandStats> topLimitAppCommandStatsList = appStatsCenter.getTopLimitAppCommandStatsList(instanceInfo.getAppId(), Long.parseLong(formatStartDate) * 10000, Long.parseLong(formatEndDate) * 10000, 5);\n            model.addAttribute(\"appCommandStats\", topLimitAppCommandStatsList);\n        } else {\n\n        }\n        return new ModelAndView(\"instance/instanceAdvancedAnalysis\");\n    }\n\n    /**\n     * 获取某个命令时间分布图\n     *\n     * @param instanceId  实例id\n     * @param commandName 命令名称\n     * @throws java.text.ParseException\n     */\n    @RequestMapping(\"/getCommandStats\")\n    public ModelAndView getCommandStats(HttpServletRequest request,\n                                        HttpServletResponse response, Model model, Long instanceId,\n                                        String commandName) throws ParseException {\n        String startDateParam = request.getParameter(\"startDate\");\n        String endDateParam = request.getParameter(\"endDate\");\n\n        if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n            Date endDate = new Date();\n            Date startDate = DateUtils.addDays(endDate, -1);\n            startDateParam = DateUtil.formatDate(startDate, \"yyyyMMdd\");\n            endDateParam = DateUtil.formatDate(endDate, \"yyyyMMdd\");\n        }\n        model.addAttribute(\"startDate\", startDateParam);\n        model.addAttribute(\"endDate\", endDateParam);\n\n        Date startDate = DateUtil.parseYYYYMMdd(startDateParam);\n        Date endDate = DateUtil.parseYYYYMMdd(endDateParam);\n        if (instanceId != null) {\n            long firstDayBegin = NumberUtils.toLong(DateUtil.formatYYYYMMdd(startDate) + \"0000\");\n            long firstDayEnd = NumberUtils.toLong(DateUtil.formatYYYYMMdd(startDate) + \"2359\");\n            long secondDayBegin = NumberUtils.toLong(DateUtil.formatYYYYMMdd(endDate) + \"0000\");\n            long secondDayEnd = NumberUtils.toLong(DateUtil.formatYYYYMMdd(endDate) + \"2359\");\n            List<InstanceCommandStats> instanceCommandStatsListFirst = instanceStatsCenter\n                    .getCommandStatsList(instanceId, firstDayBegin, firstDayEnd, commandName);\n            List<InstanceCommandStats> instanceCommandStatsListSecond = instanceStatsCenter\n                    .getCommandStatsList(instanceId, secondDayBegin, secondDayEnd, commandName);\n            Map<String, InstanceCommandStats> cmdStatsFirst = new HashMap<String, InstanceCommandStats>();\n            Map<String, InstanceCommandStats> cmdStatsSecond = new HashMap<String, InstanceCommandStats>();\n\n            for (InstanceCommandStats first : instanceCommandStatsListFirst) {\n                cmdStatsFirst.put(first.getCollectTime() + \"\", first);\n            }\n            for (InstanceCommandStats second : instanceCommandStatsListSecond) {\n                cmdStatsSecond.put(second.getCollectTime() + \"\", second);\n            }\n\n            SplineChartEntity splineChartEntity = new SplineChartEntity();\n            String container = request.getParameter(\"container\");\n            if (container != null) {\n                splineChartEntity.renderTo(container);\n            }\n            model.addAttribute(\"chart\", splineChartEntity);\n            splineChartEntity.putTitle(ChartKeysUtil.TitleKey.TEXT.getKey(), \"命令:\" + commandName + \" 的比较曲线【\" + startDateParam + \"】-【\" + endDateParam + \"】\");\n            splineChartEntity.setYAxisTitle(\"y\");\n            List<Long> data1 = new ArrayList<Long>();\n            List<Long> data2 = new ArrayList<Long>();\n            Map<String, Object> serie1 = new HashMap<String, Object>();\n            serie1.put(\"name\", startDateParam);\n            serie1.put(\"data\", data1);\n//            serie1.put(\"type\", \"area\");\n            Map<String, Object> serie2 = new HashMap<String, Object>();\n            serie2.put(\"name\", endDateParam);\n            serie2.put(\"data\", data2);\n//            serie2.put(\"type\", \"area\");\n            splineChartEntity.putSeries(serie1);\n            splineChartEntity.putSeries(serie2);\n            List<Object> x = new LinkedList<Object>();\n            for (int i = 0; i < 1440; i += 1) {\n                Date date = DateUtils.addMinutes(startDate, i);\n                String s = DateUtil.formatHHMM(date);\n                if (cmdStatsFirst.containsKey(startDateParam + s)) {\n                    data1.add(cmdStatsFirst.get(startDateParam + s).getCommandCount());\n                } else {\n                    data1.add(0l);\n                }\n                if (cmdStatsSecond.containsKey(endDateParam + s)) {\n                    data2.add(cmdStatsSecond.get(endDateParam + s).getCommandCount());\n                } else {\n                    data2.add(0l);\n                }\n\n                x.add(s);\n            }\n            splineChartEntity.setXAxisCategories(x);\n        }\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 获取某个命令时间分布图\n     *\n     * @param instanceId  实例id\n     * @param commandName 命令名称\n     * @throws java.text.ParseException\n     */\n    @RequestMapping(\"/getCommandStatsV2\")\n    public ModelAndView getCommandStatsV2(HttpServletRequest request,\n                                          HttpServletResponse response, Model model, Long instanceId,\n                                          String commandName) throws ParseException {\n        String startDateParam = request.getParameter(\"startDate\");\n        String endDateParam = request.getParameter(\"endDate\");\n\n        if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {\n            Date endDate = new Date();\n            Date startDate = DateUtils.addDays(endDate, -1);\n            startDateParam = DateUtil.formatDate(startDate, \"yyyyMMdd\");\n            endDateParam = DateUtil.formatDate(endDate, \"yyyyMMdd\");\n        }\n        model.addAttribute(\"startDate\", startDateParam);\n        model.addAttribute(\"endDate\", endDateParam);\n\n        Date startDate = DateUtil.parseYYYYMMdd(startDateParam);\n        Date endDate = DateUtil.parseYYYYMMdd(endDateParam);\n        if (instanceId != null) {\n            long firstDayBegin = NumberUtils.toLong(DateUtil.formatYYYYMMdd(startDate) + \"0000\");\n            long firstDayEnd = NumberUtils.toLong(DateUtil.formatYYYYMMdd(startDate) + \"2359\");\n            long secondDayBegin = NumberUtils.toLong(DateUtil.formatYYYYMMdd(endDate) + \"0000\");\n            long secondDayEnd = NumberUtils.toLong(DateUtil.formatYYYYMMdd(endDate) + \"2359\");\n            List<InstanceCommandStats> instanceCommandStatsListFirst = instanceStatsCenter\n                    .getCommandStatsList(instanceId, firstDayBegin, firstDayEnd, commandName);\n            List<InstanceCommandStats> instanceCommandStatsListSecond = instanceStatsCenter\n                    .getCommandStatsList(instanceId, secondDayBegin, secondDayEnd, commandName);\n            Map<String, InstanceCommandStats> cmdStatsFirst = new HashMap<String, InstanceCommandStats>();\n            Map<String, InstanceCommandStats> cmdStatsSecond = new HashMap<String, InstanceCommandStats>();\n\n            for (InstanceCommandStats first : instanceCommandStatsListFirst) {\n                cmdStatsFirst.put(first.getCollectTime() + \"\", first);\n            }\n            for (InstanceCommandStats second : instanceCommandStatsListSecond) {\n                cmdStatsSecond.put(second.getCollectTime() + \"\", second);\n            }\n\n            SplineChartEntity splineChartEntity = new SplineChartEntity();\n            String container = request.getParameter(\"container\");\n            if (container != null) {\n                splineChartEntity.renderTo(container);\n            }\n            model.addAttribute(\"chart\", splineChartEntity);\n            splineChartEntity.putTitle(ChartKeysUtil.TitleKey.TEXT.getKey(), \"命令:\" + commandName + \" 的比较曲线【\" + startDateParam + \"】-【\" + endDateParam + \"】\");\n            splineChartEntity.setYAxisTitle(\"y\");\n            List<Long> data1 = new ArrayList<Long>();\n            List<Long> data2 = new ArrayList<Long>();\n            Map<String, Object> marker = new HashMap<String, Object>();\n            marker.put(\"radius\", 1);\n            Map<String, Object> serie1 = new HashMap<String, Object>();\n            serie1.put(\"name\", startDateParam);\n            serie1.put(\"data\", data1);\n            serie1.put(\"marker\", marker);\n            Map<String, Object> serie2 = new HashMap<String, Object>();\n            serie2.put(\"name\", endDateParam);\n            serie2.put(\"data\", data2);\n            serie2.put(\"marker\", marker);\n            splineChartEntity.putSeries(serie1);\n            splineChartEntity.putSeries(serie2);\n            List<Object> x = new LinkedList<Object>();\n            for (int i = 0; i < 1440; i += 1) {\n                Date date = DateUtils.addMinutes(startDate, i);\n                String s = DateUtil.formatHHMM(date);\n                if (cmdStatsFirst.containsKey(startDateParam + s)) {\n                    data1.add(cmdStatsFirst.get(startDateParam + s).getCommandCount());\n                } else {\n                    data1.add(0l);\n                }\n                if (cmdStatsSecond.containsKey(endDateParam + s)) {\n                    data2.add(cmdStatsSecond.get(endDateParam + s).getCommandCount());\n                } else {\n                    data2.add(0l);\n                }\n\n                x.add(s);\n            }\n            splineChartEntity.setXAxisCategories(x);\n        }\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(\"/fault\")\n    public ModelAndView fault( Model model, Integer instanceId) {\n        List<InstanceFault> list = null;\n        try {\n            list = instanceAlertService.getListByInstId(instanceId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        if (list == null) {\n            list = new ArrayList<InstanceFault>();\n        }\n        model.addAttribute(\"list\", list);\n        return new ModelAndView(\"instance/instanceFault\");\n    }\n\n    @RequestMapping(\"/configSelect\")\n    public ModelAndView configSelect(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId, Long appId) {\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n            Map<String, String> redisConfigList = redisCenter.getRedisConfigList(instanceId.intValue());\n            model.addAttribute(\"redisConfigList\", redisConfigList);\n        }\n        if (appId != null && appId > 0) {\n            model.addAttribute(\"appId\", appId);\n        }\n        return new ModelAndView(\"instance/instanceConfigSelect\");\n    }\n\n    @RequestMapping(\"/slowSelect\")\n    public ModelAndView slowSelect(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId) {\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n            List<RedisSlowLog> redisSlowLogs = redisCenter.getRedisSlowLogs(instanceId.intValue(), -1);\n            model.addAttribute(\"redisSlowLogs\", redisSlowLogs);\n        }\n        return new ModelAndView(\"instance/instanceSlowSelect\");\n    }\n\n    @Autowired\n    private WebClientComponent webClientComponent;\n    @Autowired\n    private InstanceDao instanceDao;\n    @Autowired\n    private AppService appService;\n\n    @RequestMapping(\"/clientList\")\n    public ModelAndView clientList(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId, int condition) {\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n            model.addAttribute(\"condition\", condition);\n            long appId = instanceDao.getInstanceInfoById(instanceId).getAppId();\n            List<String> redisClientList = appService.getAppOnlineInstanceInfo(appId).stream().map(instanceInfo -> instanceInfo.getIp()).collect(Collectors.toList());\n            List<String> ccWebClientList = webClientComponent.getWebClientIps();\n\n            List<String> clientList = redisCenter.getClientList(instanceId.intValue());\n            model.addAttribute(\"clientList\", clientList);\n\n            List<Map<String, Object>> clientMapList = redisCenter.formatClientList(clientList);\n            List<Map<String, Object>> clientMapList_final = new ArrayList<>();\n            switch (condition) {\n                case 0:\n                    clientMapList_final = clientMapList.stream().filter(clientMap -> (!ccWebClientList.contains(clientMap.get(\"addr\")) && !redisClientList.contains(clientMap.get(\"addr\")))).collect(Collectors.toList());\n                    break;\n                case 1:\n                    clientMapList_final = clientMapList.stream().filter(clientMap -> ccWebClientList.contains(clientMap.get(\"addr\"))).collect(Collectors.toList());\n                    break;\n                case 2:\n                    clientMapList_final = clientMapList.stream().filter(clientMap -> redisClientList.contains(clientMap.get(\"addr\"))).collect(Collectors.toList());\n                    break;\n                case 3:\n                    clientMapList_final = clientMapList;\n                    break;\n            }\n            model.addAttribute(\"clientMapList\", clientMapList_final);\n        }\n        return new ModelAndView(\"instance/instanceClientList\");\n    }\n\n    @RequestMapping(\"/command\")\n    public ModelAndView command(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId, Long appId) {\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n        }\n        return new ModelAndView(\"instance/instanceCommand\");\n    }\n\n    @RequestMapping(\"/commandExecute\")\n    public ModelAndView commandExecute(HttpServletRequest request, HttpServletResponse response, Model model, Integer admin, Long instanceId, Long appId) {\n        if (instanceId != null && instanceId > 0) {\n            model.addAttribute(\"instanceId\", instanceId);\n            String command = request.getParameter(\"command\");\n            String result = instanceStatsCenter.executeCommand(instanceId, command);\n            model.addAttribute(\"result\", result);\n        } else {\n            model.addAttribute(\"result\", \"error\");\n        }\n        return new ModelAndView(\"instance/commandExecute\");\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/InstanceManageController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport com.sohu.cache.async.AsyncService;\r\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\r\nimport com.sohu.cache.async.KeyCallable;\r\nimport com.sohu.cache.entity.*;\r\nimport com.sohu.cache.exception.SSHException;\r\nimport com.sohu.cache.redis.RedisCenter;\r\nimport com.sohu.cache.ssh.SSHService;\r\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\r\nimport com.sohu.cache.stats.instance.InstanceStatsCenter;\r\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.StringUtil;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.service.MigrateService;\r\nimport org.apache.commons.collections.CollectionUtils;\r\nimport org.apache.commons.collections.map.HashedMap;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.bind.annotation.RequestParam;\r\nimport org.springframework.web.servlet.ModelAndView;\r\nimport redis.clients.jedis.HostAndPort;\r\n\r\nimport javax.annotation.PostConstruct;\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.util.*;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\n/**\r\n * 应用后台管理\r\n *\r\n * @author leifu\r\n * @Time 2014年7月3日\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/instance\")\r\npublic class InstanceManageController extends BaseController {\r\n\r\n    private Logger logger = LoggerFactory.getLogger(InstanceManageController.class);\r\n\r\n    @Resource(name = \"instanceDeployCenter\")\r\n    private InstanceDeployCenter instanceDeployCenter;\r\n\r\n    @Resource(name = \"redisCenter\")\r\n    private RedisCenter redisCenter;\r\n\r\n    @Resource(name = \"instanceStatsCenter\")\r\n    private InstanceStatsCenter instanceStatsCenter;\r\n\r\n    @Resource\r\n    SSHService sshService;\r\n\r\n    @Autowired\r\n    private MigrateService migrateService;\r\n\r\n    /**\r\n     * 上线(和下线分开)\r\n     *\r\n     * @param instanceId\r\n     */\r\n    @RequestMapping(value = \"/startInstance\")\r\n    public ModelAndView doStartInstance(HttpServletRequest request, HttpServletResponse response, Model model, long appId, int instanceId) {\r\n        AppUser appUser = getUserInfo(request);\r\n        logger.warn(\"user {} startInstance {} \", appUser.getName(), instanceId);\r\n        boolean result = false;\r\n        if (instanceId > 0) {\r\n            try {\r\n                result = instanceDeployCenter.startExistInstance(appId, instanceId);\r\n            } catch (Exception e) {\r\n                logger.error(e.getMessage(), e);\r\n                model.addAttribute(\"message\", e.getMessage());\r\n            }\r\n        } else {\r\n            logger.error(\"doStartInstance instanceId:{}\", instanceId);\r\n            model.addAttribute(\"message\", \"wrong param\");\r\n        }\r\n        logger.warn(\"user {} startInstance {} result is {}\", appUser.getName(), instanceId, result);\r\n        if (result) {\r\n            model.addAttribute(\"success\", SuccessEnum.SUCCESS.value());\r\n        } else {\r\n            model.addAttribute(\"success\", SuccessEnum.FAIL.value());\r\n        }\r\n        return new ModelAndView();\r\n    }\r\n\r\n    @RequestMapping(value = \"/scrollStartInstance\")\r\n    public ModelAndView scrollStartInstance(HttpServletRequest request, Model model, String machineIp) {\r\n\r\n        AppUser appUser = getUserInfo(request);\r\n        logger.warn(\"user {} scroll startInstance ip :{} \", appUser.getName(), machineIp);\r\n        try {\r\n            List<InstanceAlertValueResult> instanceAlertValueResults = instanceDeployCenter.checkAndStartExceptionInstance(machineIp, false);\r\n            if (!CollectionUtils.isEmpty(instanceAlertValueResults)) {\r\n                model.addAttribute(\"message\", \"滚动重启：恢复实例数量:\" + instanceAlertValueResults.size());\r\n            } else {\r\n                model.addAttribute(\"message\", \"滚动重启：无实例需要启动!\");\r\n            }\r\n            model.addAttribute(\"success\", SuccessEnum.SUCCESS.value());\r\n        } catch (Exception e) {\r\n            logger.error(\"scrollStartInstance error message :{}\", e.getMessage(), e);\r\n            model.addAttribute(\"success\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", \"滚动重启异常：\" + e.getMessage());\r\n        }\r\n        return new ModelAndView();\r\n    }\r\n\r\n    /**\r\n     * 下线实例\r\n     *\r\n     * @param instanceId\r\n     */\r\n    @RequestMapping(value = \"/shutdownInstance\")\r\n    public ModelAndView doShutdownInstance(HttpServletRequest request, HttpServletResponse response, Model model, long appId, int instanceId) {\r\n        AppUser appUser = getUserInfo(request);\r\n        logger.warn(\"user {} shutdownInstance {} \", appUser.getName(), instanceId);\r\n        boolean result = false;\r\n        if (instanceId > 0) {\r\n            try {\r\n                result = instanceDeployCenter.shutdownExistInstance(appId, instanceId);\r\n            } catch (Exception e) {\r\n                logger.error(e.getMessage(), e);\r\n                model.addAttribute(\"message\", e.getMessage());\r\n            }\r\n        } else {\r\n            logger.error(\"doShutdownInstance instanceId:{}\", instanceId);\r\n            model.addAttribute(\"message\", \"wrong param\");\r\n        }\r\n        logger.warn(\"user {} shutdownInstance {}, result is {}\", appUser.getName(), instanceId, result);\r\n        if (result) {\r\n            model.addAttribute(\"success\", SuccessEnum.SUCCESS.value());\r\n        } else {\r\n            model.addAttribute(\"success\", SuccessEnum.FAIL.value());\r\n        }\r\n        return new ModelAndView();\r\n    }\r\n\r\n    /**\r\n     * cluster forget instance\r\n     *\r\n     * @param instanceId\r\n     */\r\n    @RequestMapping(value = \"/forgetInstance\")\r\n    public ModelAndView forgetInstance(HttpServletRequest request, HttpServletResponse response, Model model, long appId, int instanceId) {\r\n        AppUser appUser = getUserInfo(request);\r\n        logger.warn(\"user {} forgetInstance {} \", appUser.getName(), instanceId);\r\n        boolean result = false;\r\n        if (instanceId > 0) {\r\n            try {\r\n                result = instanceDeployCenter.forgetInstance(appId, instanceId);\r\n            } catch (Exception e) {\r\n                logger.error(e.getMessage(), e);\r\n                model.addAttribute(\"message\", e.getMessage());\r\n            }\r\n        } else {\r\n            logger.error(\"doForgetInstance instanceId:{}\", instanceId);\r\n            model.addAttribute(\"message\", \"wrong param\");\r\n        }\r\n        logger.warn(\"user {} forgetInstance {}, result is {}\", appUser.getName(), instanceId, result);\r\n        if (result) {\r\n            model.addAttribute(\"success\", SuccessEnum.SUCCESS.value());\r\n        } else {\r\n            model.addAttribute(\"success\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", \"请查看日志\");\r\n        }\r\n        return new ModelAndView();\r\n    }\r\n\r\n    @Resource\r\n    AsyncService asyncService;\r\n\r\n    @PostConstruct\r\n    public void init() {\r\n        asyncService.assemblePool(AsyncThreadPoolFactory.TASK_EXECUTE_POOL,\r\n                AsyncThreadPoolFactory.TASK_EXECUTE_THREAD_POOL);\r\n    }\r\n\r\n    @RequestMapping(\"/migrate\")\r\n    public ModelAndView doMigrateInstance(HttpServletResponse response,\r\n                                          String sourceIp, String targetIp, String instanceIds,\r\n                                          @RequestParam(value = \"forceFlag\", required = false, defaultValue = \"false\") boolean forceFlag) {\r\n\r\n        Map<String, Object> resultMap = new HashedMap();\r\n        String key = \"migrate-instance-\" + sourceIp + \"-\" + targetIp;\r\n        asyncService.submitFuture(AsyncThreadPoolFactory.MACHINE_POOL, new KeyCallable<Boolean>(key) {\r\n            public Boolean execute() {\r\n                try {\r\n                    if (!forceFlag) {\r\n                        //一键迁移\r\n                        migrate(sourceIp, targetIp, instanceIds);\r\n                    } else {\r\n                        //一键强制迁移\r\n                            migrateService.forceMigrate(sourceIp, targetIp);\r\n                    }\r\n                    return true;\r\n                } catch (Exception e) {\r\n                    logger.error(\"doMigrateInstance \", e.getMessage(), e);\r\n                    return false;\r\n                }\r\n            }\r\n        });\r\n\r\n        resultMap.put(\"status\", 1);\r\n        sendMessage(response, JSONObject.toJSONString(resultMap));\r\n        return null;\r\n    }\r\n\r\n    public boolean migrate(String sourceIp, String targetIp, String instanceIds) throws SSHException {\r\n\r\n        /**\r\n         *  1. 检查目标容器的连通性\r\n         *  2. 获取需要迁移的实例信息\r\n         *  3. 遍历实例:(只对cluster实例迁移)\r\n         *      3.1 standalone/sentinel节点跳过\r\n         *      3.2 如果实例是master：添加新的从节点；获取master节点slave0；执行failover ；下线老节点\r\n         *      3.3 如果是slave节点：添加新从节点；下线老的从节点\r\n         *  4. 输出报告：下线节点数 ，迁移节点数\r\n         */\r\n        //1.检查目标容器的连通性\r\n        String execute = sshService.execute(targetIp, \"echo ok\");\r\n\r\n        //2.获取机器所有实例\r\n        List<InstanceInfo> instanceList = machineCenter.getMachineInstanceInfo(sourceIp);\r\n\r\n        //3.如果是部分实例迁移，剔除不需要迁移的实例\r\n        if (!StringUtil.isBlank(instanceIds) && !instanceIds.equals(\"-1\")) {\r\n            List<String> partInstanceIds = Arrays.asList(instanceIds.split(\",\"));\r\n            List<InstanceInfo> partInstanceList = new ArrayList<>();\r\n            // 找出需要迁移的实例\r\n            for (InstanceInfo instanceInfo : instanceList) {\r\n                if (instanceInfo != null && partInstanceIds.contains(String.valueOf(instanceInfo.getId()))) {\r\n                    partInstanceList.add(instanceInfo);\r\n                }\r\n            }\r\n            instanceList = partInstanceList;\r\n        }\r\n        logger.info(\"container instance migrate instanceIds:{} list size:{}\", instanceIds, instanceList.size());\r\n\r\n        // 4.开始迁移\r\n        if (!CollectionUtils.isEmpty(instanceList)) {\r\n            for (InstanceInfo instanceInfo : instanceList) {\r\n                if (instanceInfo.isOnline() && (instanceInfo.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER)) {\r\n                    //每次重新获取实例 slave/master角色，可能会发生变化\r\n                    String role = redisCenter.getInstanceRole(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\r\n                    logger.info(\"instanceInfo:{} {} role:{} start migrate\", instanceInfo.getIp(), instanceInfo.getPort(),role);\r\n                    // a)当前为master节点\r\n                    if (InstanceRoleEnum.MASTER.getInfo().equals(role)) {\r\n                        try {\r\n                            //a.1)获取master节点slave0\r\n                            AppDesc appdesc = appService.getByAppId(instanceInfo.getAppId());\r\n                            HostAndPort slave0 = redisCenter.getSlave0(instanceInfo.getIp(), instanceInfo.getPort(), appdesc.getAppPassword());\r\n                            // a.2)执行failover\r\n                            if (slave0 == null) {\r\n                                continue;\r\n                            }\r\n                            boolean isFailover = redisDeployCenter.clusterFailover(instanceInfo.getAppId(), slave0, \"force\");\r\n                            if (!isFailover) {\r\n                                continue;\r\n                            }\r\n                            int times = 0;\r\n                            boolean checkFailover = false;\r\n                            while (!checkFailover && times++ <= 10) {\r\n                                Boolean status = redisCenter.getRedisReplicationStatus(instanceInfo.getAppId(), slave0.getHost(), slave0.getPort());\r\n                                if (status) {\r\n                                    checkFailover = status;\r\n                                } else {\r\n                                    TimeUnit.MILLISECONDS.sleep(6000);\r\n                                    logger.info(\" check slave replication status ,waiting 5s ....\");\r\n                                }\r\n                            }\r\n                            if (!checkFailover) {\r\n                                // 如果failover失败 ，则不下线源节点，继续轮训下个节点\r\n                                continue;\r\n                            }\r\n\r\n                            TimeUnit.SECONDS.sleep(5);\r\n\r\n                            //a.3) 添加新的从节点\r\n                            Boolean isSuccess = null;\r\n                            HostAndPort masterInfo = redisCenter.getMaster(instanceInfo.getIp(), instanceInfo.getPort(), appdesc.getAppPassword());\r\n                            if (!StringUtils.isEmpty(masterInfo.getHost()) && masterInfo.getPort() > 0) {\r\n                                InstanceInfo masterInst = instanceDao.getInstByIpAndPort(masterInfo.getHost(), masterInfo.getPort());\r\n                                if (masterInst == null) {\r\n                                    continue;\r\n                                }\r\n                                isSuccess = redisDeployCenter.addSlave(instanceInfo.getAppId(), masterInst.getId(), targetIp);\r\n                                if (!isSuccess) {\r\n                                    // 添加从节点 失败，则退出 检查原因\r\n                                    logger.error(\"migrate add slave {}:{} fail\", instanceInfo.getAppId(), masterInst.getId());\r\n                                    break;\r\n                                }\r\n                            }\r\n                            //a.4) 下线节点\r\n                            boolean isOffline = instanceDeployCenter.shutdownExistInstance(instanceInfo.getAppId(), instanceInfo.getId());\r\n                            logger.info(\"MigrateInstance appid:{} offline node master:{} {}， add new slave :{} {}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), isOffline, targetIp, isSuccess);\r\n                        } catch (Exception e) {\r\n                            e.printStackTrace();\r\n                        }\r\n                    }\r\n                    // b).当前为slave节点\r\n                    if (InstanceRoleEnum.SLAVE.getInfo().equals(role)) {\r\n                        AppDesc appdesc = appService.getByAppId(instanceInfo.getAppId());\r\n                        HostAndPort masterInfo = redisCenter.getMaster(instanceInfo.getIp(), instanceInfo.getPort(), appdesc.getAppPassword());\r\n                        if (masterInfo == null) {\r\n                            logger.error(\"migrate get master info fail,slave instanceInfo:{} {}\", instanceInfo.getIp(), instanceInfo.getPort());\r\n                            continue;\r\n                        }\r\n                        if (!StringUtils.isEmpty(masterInfo.getHost()) && masterInfo.getPort() > 0) {\r\n                            try {\r\n                                InstanceInfo masterInst = instanceDao.getInstByIpAndPort(masterInfo.getHost(), masterInfo.getPort());\r\n                                if (masterInst == null) {\r\n                                    continue;\r\n                                }\r\n                                //添加新的slave节点\r\n                                boolean isSuccess = redisDeployCenter.addSlave(instanceInfo.getAppId(), masterInst.getId(), targetIp);\r\n                                //下线当前slave节点\r\n                                boolean isOffline = instanceDeployCenter.shutdownExistInstance(instanceInfo.getAppId(), instanceInfo.getId());\r\n                                // sleep 5s\r\n                                TimeUnit.SECONDS.sleep(5);\r\n                                logger.info(\"MigrateInstance appid:{} offline slave:{} {},add slave :{} {}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), isOffline, targetIp, isSuccess);\r\n                            } catch (Exception e) {\r\n                                e.printStackTrace();\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n\r\n    /**\r\n     * 查看redis节点日志\r\n     */\r\n    @RequestMapping(\"/log\")\r\n    public ModelAndView doShowLog(HttpServletRequest request, HttpServletResponse response, Model model, int instanceId) {\r\n        int pageSize = NumberUtils.toInt(request.getParameter(\"pageSize\"), 0);\r\n        if (pageSize == 0) {\r\n            pageSize = 100;\r\n        }\r\n        String instanceLogStr = instanceDeployCenter.showInstanceRecentLog(instanceId, pageSize);\r\n        model.addAttribute(\"instanceLogList\", StringUtils.isBlank(instanceLogStr) ? Collections.emptyList() : Arrays.asList(instanceLogStr.split(\"\\n\")));\r\n        return new ModelAndView(\"manage/instance/log\");\r\n    }\r\n\r\n    /**\r\n     * 处理实例配置修改\r\n     *\r\n     * @param appAuditId 审批id\r\n     */\r\n    @RequestMapping(value = \"/initInstanceConfigChange\")\r\n    public ModelAndView doInitInstanceConfigChange(HttpServletRequest request,\r\n                                                   HttpServletResponse response, Model model, Long appAuditId) {\r\n        // 申请原因\r\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\r\n        model.addAttribute(\"appAudit\", appAudit);\r\n\r\n        // 用第一个参数存实例id\r\n        Long instanceId = NumberUtils.toLong(appAudit.getParam1());\r\n        Map<String, String> redisConfigList = redisCenter.getRedisConfigList(instanceId.intValue());\r\n        model.addAttribute(\"redisConfigList\", redisConfigList);\r\n\r\n        // 实例\r\n        InstanceInfo instanceInfo = instanceStatsCenter.getInstanceInfo(instanceId);\r\n        model.addAttribute(\"instanceInfo\", instanceInfo);\r\n        model.addAttribute(\"appId\", appAudit.getAppId());\r\n        model.addAttribute(\"appAuditId\", appAuditId);\r\n\r\n        // 修改配置的键值对\r\n        model.addAttribute(\"instanceConfigKey\", appAudit.getParam2());\r\n        model.addAttribute(\"instanceConfigValue\", appAudit.getParam3());\r\n\r\n        return new ModelAndView(\"manage/appAudit/initInstanceConfigChange\");\r\n    }\r\n\r\n    /**\r\n     * @param appId               应用id\r\n     * @param host                实例ip\r\n     * @param port                实例端口\r\n     * @param instanceConfigKey   实例配置key\r\n     * @param instanceConfigValue 实例配置value\r\n     * @param appAuditId          审批id\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/addInstanceConfigChange\")\r\n    public ModelAndView doAddAppConfigChange(HttpServletRequest request,\r\n                                             HttpServletResponse response, Model model, Long appId, String host, int port,\r\n                                             String instanceConfigKey, String instanceConfigValue, Long appAuditId) {\r\n        AppUser appUser = getUserInfo(request);\r\n        logger.warn(\"user {} change instanceConfig:appId={},{}:{};key={};value={},appAuditId:{}\", appUser.getName(), appId, host, port, instanceConfigKey, instanceConfigValue, appAuditId);\r\n        boolean isModify = false;\r\n        if (StringUtils.isNotBlank(host) && port > 0 && StringUtils.isNotBlank(instanceConfigKey)) {\r\n            try {\r\n                if (appAuditId != null) {\r\n                    appAuditDao.updateAppAuditOperateUser(appAuditId, appUser.getId());\r\n                }\r\n                isModify = instanceDeployCenter.modifyInstanceConfig(appId, appAuditId, host, port, instanceConfigKey, instanceConfigValue);\r\n            } catch (Exception e) {\r\n                logger.error(e.getMessage(), e);\r\n            }\r\n        }\r\n        logger.warn(\"user {} change instanceConfig:appId={},{}:{};key={};value={},appAuditId:{},result is:{}\", appUser.getName(), appId, host, port, instanceConfigKey, instanceConfigValue, appAuditId, isModify);\r\n        if (appAuditId != null) {\r\n            return new ModelAndView(\"redirect:/manage/app/auditList\");\r\n        } else {\r\n            JSONObject json = new JSONObject();\r\n            json.put(\"result\", isModify ? 1 : 0);\r\n            sendMessage(response, json.toString());\r\n            return null;\r\n        }\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/InstanceOperationController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.sohu.cache.constant.AppStatusEnum;\nimport com.sohu.cache.constant.ErrorMessageEnum;\nimport com.sohu.cache.dao.MachineDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.web.enums.CompareTypeEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppRedisCommandCheckService;\nimport com.sohu.cache.web.service.AppRedisConfigCheckService;\nimport com.sohu.cache.web.service.AppScrollRestartService;\nimport com.sohu.cache.web.vo.*;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/27 17:34\n * @Description: 实例运维检测\n */\n@Controller\n@RequestMapping(\"manage/instance\")\npublic class InstanceOperationController extends BaseController {\n\n    @Autowired\n    private AppRedisConfigCheckService appRedisConfigCheckService;\n\n    @Autowired\n    private AppRedisCommandCheckService appRedisCommandCheckService;\n\n    @Autowired\n    private AppScrollRestartService appScrollRestartService;\n\n    @Autowired\n    private MachineDao machineDao;\n\n    /**\n     * 实例运维页面\n     * @param request\n     * @param model\n     * @return\n     */\n    @RequestMapping(value = \"/opsList\")\n    public ModelAndView opsList(HttpServletRequest request, Model model) {\n        AppUser currentUser = getUserInfo(request);\n        //获取tab\n        int tabId = NumberUtils.toInt(request.getParameter(\"tabId\"), 1);\n        model.addAttribute(\"tabId\", tabId);\n        model.addAttribute(\"insOperateActive\", SuccessEnum.SUCCESS.value());\n        if(tabId == 1){\n            List<SystemResource> versionList = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\n            List<RedisConfigCheckResult> redisConfigCheckResult = appRedisConfigCheckService.getRedisConfigCheckResult();\n\n            CompareTypeEnum[] values = CompareTypeEnum.values();\n            List<CompareTypeEnum> compareTypeEnums = Arrays.asList(values);\n            compareTypeEnums.sort((o1, o2) -> o1.getType());\n            List<AppDesc> allAppDesc = appService.getAllAppDesc();\n            allAppDesc = allAppDesc.stream().filter(appDesc -> appDesc.getStatus() == AppStatusEnum.STATUS_PUBLISHED.getStatus()).collect(Collectors.toList());\n            model.addAttribute(\"appList\", allAppDesc);\n            model.addAttribute(\"redisVersionList\", versionList);\n            model.addAttribute(\"compareTypeList\", compareTypeEnums);\n            model.addAttribute(\"checkResultList\", redisConfigCheckResult);\n        }\n        if(tabId == 2){\n            List<RedisCommandCheckResult> redisCommandCheckResult = appRedisCommandCheckService.getRedisCommandCheckResult();\n            model.addAttribute(\"commandCheckResult\", redisCommandCheckResult);\n        }\n        if(tabId == 3){\n            String appIdStr = request.getParameter(\"appId\");\n            String pageNoStr = request.getParameter(\"pageNo\");\n            Integer pageNo = 1;\n            Long appId = null;\n            if(!StringUtil.isBlank(appIdStr)) {\n                appId = Long.valueOf(appIdStr);\n            }\n            ConfigRestartRecord configRestartRecord = new ConfigRestartRecord();\n            configRestartRecord.setAppId(appId);\n            if(!StringUtil.isBlank(pageNoStr)){\n                pageNo = Integer.valueOf(pageNoStr);\n            }\n            int pageSize = 10;\n            List<ConfigRestartRecord> configRestartRecordByCondition = appScrollRestartService.getConfigRestartRecordByCondition(model, configRestartRecord, pageNo, pageSize);\n            List<InstanceInfo> instanceInfoList = new ArrayList<>();\n            if(appId != null){\n                instanceInfoList = appService.getAppBasicInstanceInfo(appId);\n            }else{\n                Set<Long> appIdSet = new HashSet<>();\n                for (ConfigRestartRecord record : configRestartRecordByCondition) {\n                    if(appIdSet.contains(record.getAppId())){\n                        continue;\n                    }\n                    appIdSet.add(record.getAppId());\n                    instanceInfoList.addAll(appService.getAppBasicInstanceInfo(record.getAppId()));\n                }\n            }\n            Map<Integer, InstanceInfo> instanceInfoMap = instanceInfoList.stream().collect(Collectors.toMap(InstanceInfo::getId, Function.identity(), (v1, v2) -> v1));\n            model.addAttribute(\"restartRecordList\", configRestartRecordByCondition);\n            model.addAttribute(\"appId\", appId);\n            model.addAttribute(\"instanceInfoMap\", instanceInfoMap);\n        }\n        return new ModelAndView(\"manage/instanceOps/instanceOpsIndex\");\n    }\n\n    /**\n     * 配置检测\n     * @param request\n     * @param model\n     * @return\n     */\n    @RequestMapping(value = \"/configCheck\")\n    @ResponseBody\n    public ModelAndView configCheck(HttpServletRequest request, Model model) {\n        AppUser appUser = getUserInfo(request);\n        AppRedisConfigCheckVo checkVo = getRedisConfigCheck(request);\n        RedisConfigCheckResult configCheckResults = appRedisConfigCheckService.checkRedisConfig(appUser, checkVo);\n        if(configCheckResults != null){\n            model.addAttribute(\"status\", SuccessEnum.SUCCESS.value());\n        }else{\n            model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\n        }\n        return new ModelAndView(\"\");\n    }\n\n\n    /**\n     * 配置检测\n     * @param request\n     * @param model\n     * @return\n     */\n    @RequestMapping(value = \"/getMachineList\")\n    @ResponseBody\n    public ModelAndView getMachineList(HttpServletRequest request, Model model,\n                                       String ip, String realIp, Integer searchType) {\n        List<MachineInfo> machineList = machineDao.getMachineListByCondition(ip, realIp);\n        Set<String> ipSet = new HashSet<>();\n        if(searchType == 1){\n            ipSet = machineList.stream().map(machineInfo -> machineInfo.getRealIp()).collect(Collectors.toSet());\n        }else if(searchType == 2){\n            ipSet = machineList.stream().map(machineInfo -> machineInfo.getIp()).collect(Collectors.toSet());\n        }\n        model.addAttribute(\"ipSet\", ipSet);\n        model.addAttribute(\"status\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 获取某次配置检测结果\n     * @param request\n     * @param model\n     * @param uuid\n     * @return\n     */\n    @RequestMapping(value = \"/getConfigCheck\")\n    public ModelAndView getConfigCheck(HttpServletRequest request, Model model, String uuid) {\n        AppUser appUser = getUserInfo(request);\n        List<AppRedisConfigCheckResult> redisConfigCheckResult = appRedisConfigCheckService.getRedisConfigCheckDetailResult(uuid);\n        List<SystemResource> versionList = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\n        model.addAttribute(\"redisVersionList\", versionList);\n        CompareTypeEnum[] values = CompareTypeEnum.values();\n        List<CompareTypeEnum> compareTypeEnums = Arrays.asList(values);\n        compareTypeEnums.sort((o1, o2) -> o1.getType());\n        model.addAttribute(\"compareTypeList\", compareTypeEnums);\n        model.addAttribute(\"checkResultList\", redisConfigCheckResult);\n        return new ModelAndView(\"manage/instanceOps/instanceConfigCheckList\");\n    }\n\n    private AppRedisConfigCheckVo getRedisConfigCheck(HttpServletRequest request) {\n        String appIdStr = request.getParameter(\"appId\");\n        Long appId = null;\n        if(StringUtils.isNotEmpty(appIdStr)){\n            appId = Long.parseLong(appIdStr);\n        }\n        String configName = request.getParameter(\"configName\");\n        String versionIdStr = request.getParameter(\"versionId\");\n        Integer versionId = null;\n        if(StringUtils.isNotEmpty(versionIdStr)){\n            versionId = Integer.parseInt(versionIdStr);\n        }\n        String expectValue = request.getParameter(\"expectValue\");\n        int compareType = NumberUtils.toInt(request.getParameter(\"compareType\"));\n        // 生成对象\n        AppRedisConfigCheckVo appRedisConfigCheckVo = new AppRedisConfigCheckVo();\n        appRedisConfigCheckVo.setAppId(appId);\n        appRedisConfigCheckVo.setConfigName(configName);\n        appRedisConfigCheckVo.setVersionId(versionId);\n        appRedisConfigCheckVo.setCompareType(compareType);\n        appRedisConfigCheckVo.setExpectValue(expectValue);\n        return appRedisConfigCheckVo;\n    }\n\n    /**\n     * 命令检测\n     * @param request\n     * @param model\n     * @return\n     */\n    @RequestMapping(value = \"/commandCheck\")\n    @ResponseBody\n    public ModelAndView doStatList(HttpServletRequest request, Model model) {\n        AppUser appUser = getUserInfo(request);\n        AppRedisCommandCheckVo checkVo = getRedisCommandCheck(request);\n        appRedisCommandCheckService.checkRedisCommand(appUser, checkVo);\n        model.addAttribute(\"status\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"\");\n    }\n\n    private AppRedisCommandCheckVo getRedisCommandCheck(HttpServletRequest request) {\n        String machineIps = request.getParameter(\"machineIps\");\n        String podIp = request.getParameter(\"podIp\");\n        String command = request.getParameter(\"command\");\n        String checkTypeStr = request.getParameter(\"checkType\");\n        Integer checkType = null;\n        if(StringUtils.isNotEmpty(checkTypeStr)){\n            checkType = Integer.valueOf(checkTypeStr);\n        }\n        String infoIndicate = request.getParameter(\"infoIndicate\");\n        String indicateName = request.getParameter(\"indicateName\");\n        String maxTryStr = request.getParameter(\"maxTry\");\n        Integer maxTry = null;\n        if(StringUtils.isNotEmpty(maxTryStr)){\n            maxTry = Integer.valueOf(maxTryStr);\n        }\n        String expectValue = request.getParameter(\"expectValue\");\n        String minuteInternalStr = request.getParameter(\"minuteInternal\");\n        Integer minuteInternal = null;\n        if(StringUtils.isNotEmpty(minuteInternalStr)){\n            minuteInternal = Integer.valueOf(minuteInternalStr);\n        }\n        AppRedisCommandCheckVo checkVo = new AppRedisCommandCheckVo();\n        checkVo.setMachineIps(machineIps);\n        checkVo.setPodIp(podIp);\n        checkVo.setCommand(command);\n        checkVo.setCheckType(checkType);\n        checkVo.setInfoIndicate(infoIndicate);\n        checkVo.setIndicateName(indicateName);\n        checkVo.setExpectValue(expectValue);\n        checkVo.setMaxTry(maxTry);\n        checkVo.setMinuteInternal(minuteInternal);\n        return checkVo;\n    }\n\n    /**\n     * 查询命令检测\n     * @param request\n     * @param model\n     * @param uuid\n     * @return\n     */\n    @RequestMapping(value = \"/getCommandCheck\")\n    public ModelAndView getCommandCheck(HttpServletRequest request, Model model, String uuid) {\n        AppUser appUser = getUserInfo(request);\n        AppRedisCommandCheckResult appRedisCommandCheckResult = appRedisCommandCheckService.getRedisCommandCheckDetailResult(uuid);\n        model.addAttribute(\"checkResult\", appRedisCommandCheckResult);\n        return new ModelAndView(\"manage/instanceOps/instanceCommandCheckList\");\n    }\n\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/LoginController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.sohu.cache.constant.AppUserTypeEnum;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.entity.LoginResult;\r\nimport com.sohu.cache.login.LoginComponent;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.util.MD5Util;\r\nimport com.sohu.cache.utils.EnvCustomUtil;\r\nimport com.sohu.cache.web.enums.AdminEnum;\r\nimport com.sohu.cache.web.enums.LoginEnum;\r\nimport com.sohu.cache.web.service.UserLoginStatusService;\r\nimport com.sohu.cache.web.vo.GeneralResponse;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestBody;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.bind.annotation.RequestMethod;\r\nimport org.springframework.web.bind.annotation.ResponseBody;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\n/**\r\n * 登录逻辑\r\n *\r\n * @author leifu\r\n * @Time 2014年6月12日\r\n */\r\n@Controller\r\n@RequestMapping(\"manage\")\r\npublic class LoginController extends BaseController {\r\n\r\n    @Resource(name = \"userLoginStatusService\")\r\n    private UserLoginStatusService userLoginStatusService;\r\n\r\n    @Autowired(required = false)\r\n    private LoginComponent loginComponent;\r\n\r\n    /**\r\n     * 用户登录界面\r\n     *\r\n     * @param request\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/login\", method = RequestMethod.GET)\r\n    public ModelAndView init(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        model.addAttribute(ConstUtils.RREDIRECT_URL_PARAM, request.getParameter(ConstUtils.RREDIRECT_URL_PARAM));\r\n        model.addAttribute(\"pwdswitch\", EnvCustomUtil.pwdswitch);\r\n        return new ModelAndView(\"manage/login\");\r\n    }\r\n\r\n    /**\r\n     * 用户登录\r\n     *\r\n     * @param userName 用户名\r\n     * @param password 密码\r\n     * @param isAdmin  是否勾选超级管理员选项,1是0否\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/loginIn\", method = RequestMethod.POST)\r\n    public ModelAndView loginIn(HttpServletRequest request,\r\n                                HttpServletResponse response, Model model, String userName, String password, boolean isAdmin) {\r\n        // 登录结果\r\n        LoginResult loginResult = new LoginResult();\r\n        loginResult.setAdminEnum((isAdmin == true ? AdminEnum.IS_ADMIN : AdminEnum.NOT_ADMIN));\r\n        loginResult.setLoginEnum(LoginEnum.LOGIN_WRONG_USER_OR_PASSWORD);\r\n\r\n        AppUser userModel = null;\r\n        if (ConstUtils.SUPER_ADMIN_NAME.equals(userName)) {\r\n            userModel = userService.getByName(userName);\r\n            String checkPwd = ConstUtils.SUPER_ADMIN_PASS;\r\n            if(EnvCustomUtil.pwdswitch){\r\n                checkPwd = MD5Util.string2MD5(ConstUtils.SUPER_ADMIN_PASS);\r\n            }\r\n            if (userModel != null && checkPwd.equals(password)) {\r\n                loginResult.setLoginEnum(LoginEnum.LOGIN_SUCCESS);\r\n            } else {\r\n                loginResult.setLoginEnum(LoginEnum.LOGIN_WRONG_USER_OR_PASSWORD);\r\n            }\r\n        } else {\r\n            if (loginComponent != null && loginComponent.passportCheck(userName, password)) {\r\n                // 同时要验证是否有cachecloud权限\r\n                userModel = userService.getByName(userName);\r\n                if (userModel != null && userModel.getType() != AppUserTypeEnum.NO_USER.value()) {\r\n                    if (isAdmin) {\r\n                        if (AppUserTypeEnum.ADMIN_USER.value().equals(userModel.getType())) {\r\n                            loginResult.setLoginEnum(LoginEnum.LOGIN_SUCCESS);\r\n                        } else {\r\n                            loginResult.setLoginEnum(LoginEnum.LOGIN_NOT_ADMIN);\r\n                        }\r\n                    } else {\r\n                        loginResult.setLoginEnum(LoginEnum.LOGIN_SUCCESS);\r\n                    }\r\n                } else {\r\n                    // 用户不存在\r\n                    loginResult.setLoginEnum(LoginEnum.LOGIN_USER_NOT_EXIST);\r\n                }\r\n            }\r\n        }\r\n        // 登录成功写入登录状态\r\n        if (loginResult.getLoginEnum().equals(LoginEnum.LOGIN_SUCCESS)) {\r\n            userLoginStatusService.addLoginStatus(request, response, userModel.getName());\r\n        }\r\n        model.addAttribute(\"success\", loginResult.getLoginEnum().value());\r\n        model.addAttribute(\"admin\", loginResult.getAdminEnum().value());\r\n        return new ModelAndView();\r\n    }\r\n\r\n    /**\r\n     * 用户注销\r\n     *\r\n     * @return\r\n     */\r\n    @RequestMapping(\"/logout\")\r\n    public ModelAndView logout(HttpServletRequest request, HttpServletResponse response) {\r\n        userLoginStatusService.removeLoginStatus(request, response);\r\n        //String redirectUrl = userLoginStatusService.getLogoutUrl();\r\n        return new ModelAndView(\"redirect:\" + \"/manage/login\");\r\n    }\r\n\r\n    /**\r\n     * 用户登录\r\n     *\r\n     * @param appUser 用户\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/loginCheck\", method = RequestMethod.POST)\r\n    @ResponseBody\r\n    public GeneralResponse<String> loginCheck(HttpServletRequest request,\r\n                                              @RequestBody AppUser appUser) {\r\n        AppUser user = userService.getByName(appUser.getName());\r\n        if(user != null && user.getPassword() != null && user.getPassword().equals(appUser.getPassword())){\r\n            return GeneralResponse.ok();\r\n        }\r\n        return GeneralResponse.error(1001, \"用户名或密码错误\");\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/MachineManageController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.MachineInfoEnum;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineDeployCenter;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.MachineTaskEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.*;\n\n/**\n * 机器管理\n *\n * @author leifu\n * @Time 2014年10月14日\n */\n@Controller\n@RequestMapping(\"manage/machine\")\npublic class MachineManageController extends BaseController {\n\n    private final static String COMMA = \",\";\n    @Resource\n    private TaskService taskService;\n    @Resource\n    private MachineDeployCenter machineDeployCenter;\n\n    @RequestMapping(\"/index\")\n    public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model,\n                              String tabTag,\n                              String ipLike, Integer versionId, Integer isInstall, Integer useType, Integer type, Integer k8sType, String realip) {\n        model.addAttribute(\"tabTag\", tabTag);\n        model.addAttribute(\"isInstall\", isInstall);\n        model.addAttribute(\"versionId\",versionId);\n        model.addAttribute(\"useType\", useType);\n        model.addAttribute(\"ipLike\", ipLike);\n        model.addAttribute(\"k8sType\", k8sType);\n        model.addAttribute(\"type\", type);\n        model.addAttribute(\"realip\", realip);\n        model.addAttribute(\"machineActive\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"manage/machine/list\");\n    }\n\n    @RequestMapping(value = \"/pod/changelist\")\n    public ModelAndView doPodList(Model model, String ip) {\n\n        List<MachineRelation> machineRelationList = machineDeployCenter.getMachineRelationList(ip);\n\n        MachineInfo machineinfo = machineCenter.getMachineInfoByIp(ip);\n        String realIp = machineinfo != null ? machineinfo.getRealIp() : \"\";\n        logger.info(\"ip:{} ,realIp:{} ,pod change size:{}\", ip, realIp, machineRelationList.size());\n        model.addAttribute(\"ip\", ip);\n        model.addAttribute(\"realIp\", realIp);\n        model.addAttribute(\"relationList\", machineRelationList);\n\n        return new ModelAndView(\"manage/pod/list\");\n    }\n\n    @RequestMapping(value = \"/pod/add/syncTask\", method = RequestMethod.POST)\n    public ModelAndView doAddMachineSyncTask(Model model, HttpServletResponse response, String containerIp, String sourceIp, String targetIp, Integer relationId) {\n\n        Map<String, Object> taskMap = new HashMap<String, Object>();\n        try {\n            if (!StringUtils.isEmpty(containerIp) && !StringUtils.isEmpty(sourceIp) && !StringUtils.isEmpty(targetIp)) {\n                // 检查是否已经有同步任务\n                SuccessEnum successEnum = machineDeployCenter.checkMachineSyncStatus(containerIp, sourceIp, MachineTaskEnum.SYNCING.getValue());\n                if (successEnum.value() == SuccessEnum.NO_REPEAT.value()) {\n                    long taskId = taskService.addMachineSyncTask(sourceIp, targetIp, containerIp, String.format(\"sourceMachine:%s tagetMachine:%s \", sourceIp, targetIp), 0);\n                    if (taskId > 0) {\n                        logger.info(\"add machine sync task:{} \", taskId);\n                        taskMap.put(\"status\", SuccessEnum.SUCCESS.value());\n                        taskMap.put(\"taskId\", taskId);\n                        machineDeployCenter.updateMachineRelation(relationId, taskId, MachineTaskEnum.SYNCING.getValue());\n                    }\n                } else {\n                    taskMap.put(\"status\", SuccessEnum.FAIL.value());\n                    taskMap.put(\"message\", \"containerIp:\" + containerIp + \",sourceIp:\" + sourceIp + \" ,任务:\" + successEnum.info());\n                    machineDeployCenter.updateMachineRelation(relationId, null, MachineTaskEnum.SYNC_FAILED.getValue());\n                }\n            } else {\n                taskMap.put(\"status\", SuccessEnum.FAIL.value());\n                taskMap.put(\"message\", \"参数验证失败 containerIp:\" + containerIp + \",sourceIp:\" + sourceIp + \",targetIp:\" + targetIp);\n            }\n        } catch (Exception e) {\n            taskMap.put(\"status\", SuccessEnum.FAIL.value());\n            taskMap.put(\"message\", \"add task exception:\" + e.getMessage());\n            logger.error(e.getMessage(), e);\n        }\n        sendMessage(response, JSONObject.toJSONString(taskMap));\n        return null;\n    }\n\n    @RequestMapping(value = \"/list\")\n    public ModelAndView doMachineList(HttpServletRequest request,\n                                      HttpServletResponse response, Model model,\n                                      String tabTag,\n                                      String ipLike, Integer versionId, Integer isInstall, Integer useType, Integer type, Integer k8sType, String realip) {\n\n        if (tabTag.equals(\"machine\")) {\n            List<MachineStats> machineList = machineCenter.getMachineStats(ipLike, useType, type, versionId, isInstall, k8sType, realip);\n            Map<String, Integer> machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n            List<MachineRoom> roomList = machineCenter.getEffectiveRoom();\n            model.addAttribute(\"roomList\", roomList);\n            model.addAttribute(\"list\", machineList);\n            model.addAttribute(\"isInstall\", isInstall);\n            model.addAttribute(\"versionId\",versionId);\n            model.addAttribute(\"useType\", useType);\n            model.addAttribute(\"ipLike\", ipLike);\n            model.addAttribute(\"k8sType\", k8sType);\n            model.addAttribute(\"type\", type);\n            model.addAttribute(\"realip\", realip);\n            model.addAttribute(\"machineActive\", SuccessEnum.SUCCESS.value());\n            model.addAttribute(\"collectAlert\", \"(请等待\" + ConstUtils.MACHINE_STATS_CRON_MINUTE + \"分钟)\");\n            model.addAttribute(\"machineInstanceCountMap\", machineInstanceCountMap);\n\n            return new ModelAndView(\"manage/machine/machineList\");\n        } else if (tabTag.equals(\"room\")) {\n            List<MachineRoom> roomList = machineCenter.getAllRoom();\n            model.addAttribute(\"roomList\", roomList);\n            return new ModelAndView(\"manage/machine/roomList\");\n        }\n        return new ModelAndView(\"\");\n    }\n\n    /**\n     * 机器实例展示\n     *\n     * @param ip\n     * @return\n     */\n    @RequestMapping(value = \"/machineInstances\")\n    public ModelAndView doMachineInstances(HttpServletRequest request,\n                                           HttpServletResponse response, Model model, String ip) {\n        //机器以及机器下面的实例信息\n        MachineInfo machineInfo = machineCenter.getMachineInfoByIp(ip);\n        List<InstanceInfo> instanceList = machineCenter.getMachineInstanceInfo(ip);\n        List<InstanceStats> instanceStatList = machineCenter.getMachineInstanceStatsByIp(ip);\n        //统计信息\n        fillInstanceModel(instanceList, instanceStatList, model);\n\n        // 机器列表\n        List<MachineStats> machineList = machineCenter.getMachineStats(null, null, null, null, null, null, null);\n        // 获取机器信息\n        Map<String, Integer> machineInstanceCountMap = machineCenter.getMachineInstanceCountMap();\n        \n        model.addAttribute(\"machineInstanceCountMap\", machineInstanceCountMap);\n        model.addAttribute(\"machineList\", machineList);\n        model.addAttribute(\"machineInfo\", machineInfo);\n        model.addAttribute(\"machineActive\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"manage/machine/machineInstances\");\n    }\n\n    /**\n     * 检查机器下是否有存活的实例\n     *\n     * @param ip\n     * @return\n     */\n    @RequestMapping(value = \"/checkMachineInstances\")\n    public ModelAndView doCheckMachineInstances(HttpServletRequest request,\n                                                HttpServletResponse response, Model model, String ip) {\n        List<InstanceInfo> instanceList = machineCenter.getMachineInstanceInfo(ip);\n        model.addAttribute(\"machineHasInstance\", CollectionUtils.isNotEmpty(instanceList));\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"/add\", method = {RequestMethod.POST})\n    public ModelAndView doAdd(HttpServletRequest request,\n                              HttpServletResponse response, Model model) {\n        MachineInfo machineInfo = new MachineInfo();\n        machineInfo.setIp(request.getParameter(\"ip\"));\n        machineInfo.setRoom(request.getParameter(\"room\"));\n        machineInfo.setMem(NumberUtils.toInt(request.getParameter(\"mem\"), 0));\n        machineInfo.setCpu(NumberUtils.toInt(request.getParameter(\"cpu\"), 0));\n        machineInfo.setDisk(NumberUtils.toInt(request.getParameter(\"disk\"), 0));\n        machineInfo.setVirtual(NumberUtils.toInt(request.getParameter(\"virtual\"), 0));\n        machineInfo.setRealIp(request.getParameter(\"realIp\"));\n        machineInfo.setType(NumberUtils.toInt(request.getParameter(\"machineType\"), 0));\n        machineInfo.setExtraDesc(request.getParameter(\"extraDesc\"));\n        machineInfo.setCollect(NumberUtils.toInt(request.getParameter(\"collect\"), 1));\n        machineInfo.setVersionInstall(request.getParameter(\"versionInfo\"));\n\n        Date date = new Date();\n        machineInfo.setSshUser(ConstUtils.USERNAME);\n        machineInfo.setSshPasswd(ConstUtils.PASSWORD);\n        machineInfo.setServiceTime(date);\n        machineInfo.setModifyTime(date);\n        machineInfo.setAvailable(MachineInfoEnum.AvailableEnum.YES.getValue());\n        boolean isSuccess = machineDeployCenter.addMachine(machineInfo);\n        model.addAttribute(\"result\", isSuccess);\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"/addMultiple\", method = {RequestMethod.POST})\n    public ModelAndView doAddMultiple(HttpServletRequest request,\n                                      HttpServletResponse response, Model model) {\n        boolean isSuccess = true;\n        List<String> ipList = Arrays.asList(request.getParameter(\"ip\").split(COMMA));\n        List<String> realIpList = Arrays.asList(request.getParameter(\"realIp\").split(COMMA));//ip与realIp一一对应\n        for (int i = 0; i < ipList.size(); i++) {\n            MachineInfo machineInfo = new MachineInfo();\n            machineInfo.setIp(ipList.get(i));\n            machineInfo.setRoom(request.getParameter(\"room\"));\n            machineInfo.setMem(NumberUtils.toInt(request.getParameter(\"mem\"), 0));\n            machineInfo.setCpu(NumberUtils.toInt(request.getParameter(\"cpu\"), 0));\n            machineInfo.setDisk(NumberUtils.toInt(request.getParameter(\"disk\"), 0));\n            machineInfo.setVirtual(NumberUtils.toInt(request.getParameter(\"virtual\"), 0));\n            machineInfo.setDisType(NumberUtils.toInt(request.getParameter(\"disType\"), 0));\n            machineInfo.setRealIp(i < realIpList.size() ? realIpList.get(i) : \"\");\n            machineInfo.setType(NumberUtils.toInt(request.getParameter(\"machineType\"), 0));\n            machineInfo.setUseType(NumberUtils.toInt(request.getParameter(\"useType\"), 0));\n            machineInfo.setK8sType(NumberUtils.toInt(request.getParameter(\"k8sType\"), 0));\n            machineInfo.setExtraDesc(request.getParameter(\"extraDesc\"));\n            machineInfo.setCollect(NumberUtils.toInt(request.getParameter(\"collect\"), 1));\n            machineInfo.setVersionInstall(request.getParameter(\"versionInfo\"));\n            machineInfo.setRack(request.getParameter(\"rack\"));\n\n            Date date = new Date();\n            machineInfo.setSshUser(ConstUtils.USERNAME);\n            machineInfo.setSshPasswd(ConstUtils.PASSWORD);\n            machineInfo.setServiceTime(date);\n            machineInfo.setModifyTime(date);\n            machineInfo.setAvailable(MachineInfoEnum.AvailableEnum.YES.getValue());\n            if (!machineDeployCenter.addMachine(machineInfo)) {\n                isSuccess = false;\n                break;\n            }\n        }\n        model.addAttribute(\"result\", isSuccess);\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"/delete\")\n    public ModelAndView doDelete(HttpServletRequest request, HttpServletResponse response, Model model) {\n        String machineIp = request.getParameter(\"machineIp\");\n        if (StringUtils.isNotBlank(machineIp)) {\n            MachineInfo machineInfo = machineCenter.getMachineInfoByIp(machineIp);\n            boolean success = machineDeployCenter.removeMachine(machineInfo);\n            logger.warn(\"delete machine {}, result is {}\", machineIp, success);\n        } else {\n            logger.warn(\"machineIp is empty!\");\n        }\n        return new ModelAndView(\"redirect:/manage/machine/index?tabTag=machine\");\n    }\n\n\n    /**\n     * 实例统计信息\n     *\n     * @param\n     * @param model\n     */\n    protected void fillInstanceModel(List<InstanceInfo> instanceList, List<InstanceStats> appInstanceStats, Model model) {\n        Map<String, MachineStats> machineStatsMap = new HashMap<String, MachineStats>();\n        Map<String, Long> machineCanUseMem = new HashMap<String, Long>();\n        Map<String, InstanceStats> instanceStatsMap = new HashMap<String, InstanceStats>();\n        Map<Long, AppDesc> appInfoMap = new HashMap<Long, AppDesc>();\n\n        for (InstanceStats instanceStats : appInstanceStats) {\n            instanceStatsMap.put(instanceStats.getIp() + \":\" + instanceStats.getPort(), instanceStats);\n            AppDesc appDesc = appService.getByAppId(instanceStats.getAppId());\n            appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));\n            appInfoMap.put(instanceStats.getAppId(), appDesc);\n        }\n\n        for (InstanceInfo instanceInfo : instanceList) {\n            if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {\n                continue;\n            }\n            String ip = instanceInfo.getIp();\n            if (machineStatsMap.containsKey(ip)) {\n                continue;\n            }\n            List<MachineStats> machineStatsList = machineCenter.getMachineStats(ip);\n            MachineStats machineStats = null;\n            for (MachineStats stats : machineStatsList) {\n                if (stats.getIp().equals(ip)) {\n                    machineStats = stats;\n                    machineStatsMap.put(ip, machineStats);\n                    break;\n                }\n            }\n            MachineStats ms = machineCenter.getMachineMemoryDetail(ip);\n            machineCanUseMem.put(ip, ms.getMachineMemInfo().getLockedMem());\n            machineStatsMap.get(ip).setMachineMemInfo(ms.getMachineMemInfo());\n        }\n        model.addAttribute(\"appInfoMap\", appInfoMap);\n\n        model.addAttribute(\"machineCanUseMem\", machineCanUseMem);\n        model.addAttribute(\"machineStatsMap\", machineStatsMap);\n\n        model.addAttribute(\"instanceList\", instanceList);\n        model.addAttribute(\"instanceStatsMap\", instanceStatsMap);\n    }\n\n\n    @RequestMapping(value = \"room/add\", method = {RequestMethod.POST})\n    public ModelAndView doRoomAdd(HttpServletRequest request,\n                                  HttpServletResponse response, Model model) {\n        MachineRoom room = new MachineRoom();\n        room.setId(NumberUtils.toInt(request.getParameter(\"id\")));\n        room.setName(request.getParameter(\"name\"));\n        room.setStatus(NumberUtils.toInt(request.getParameter(\"status\"), 1));\n        room.setDesc(request.getParameter(\"desc\"));\n        room.setIpNetwork(request.getParameter(\"ipNetwork\"));\n        room.setOperator(request.getParameter(\"operator\"));\n\n        boolean isSuccess = machineDeployCenter.addMachineRoom(room);\n        model.addAttribute(\"result\", isSuccess);\n        return new ModelAndView(\"\");\n    }\n\n    @RequestMapping(value = \"room/delete\")\n    public ModelAndView doRoomDelete(HttpServletRequest request, HttpServletResponse response, Model model) {\n        int roomId = NumberUtils.toInt(request.getParameter(\"id\"), -1);\n        if (roomId > -1) {\n            boolean success = machineDeployCenter.removeMachineRoom(roomId);\n            logger.warn(\"delete machine {}, result is {}\", roomId, success);\n        } else {\n            logger.warn(\"machineIp is empty!\");\n        }\n        return new ModelAndView(\"redirect:/manage/machine/index?tabTag=room\");\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/NoticeManageController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Arrays;\r\nimport java.util.List;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.util.AppEmailUtil;\r\n\r\n@Controller\r\n@RequestMapping(\"manage/notice\")\r\npublic class NoticeManageController extends BaseController {\r\n    \r\n    @Resource(name = \"appEmailUtil\")\r\n    private AppEmailUtil appEmailUtil;\r\n\r\n    /**\r\n     * 初始化系统通知\r\n     * \r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/initNotice\")\r\n    public ModelAndView init(HttpServletRequest request,\r\n            HttpServletResponse response, Model model) {\r\n\r\n        String notice = \"\";\r\n        model.addAttribute(\"notice\", notice);\r\n        model.addAttribute(\"success\", request.getParameter(\"success\"));\r\n        model.addAttribute(\"noticeActive\", SuccessEnum.SUCCESS.value());\r\n        return new ModelAndView(\"manage/notice/initNotice\");\r\n    }\r\n\r\n    /**\r\n     * 发送邮件通知\r\n     */\r\n    @RequestMapping(value = \"/add\")\r\n    public ModelAndView addNotice(HttpServletRequest request,\r\n            HttpServletResponse response, Model model) {\r\n        String notice = request.getParameter(\"notice\");\r\n        boolean result = appEmailUtil.noticeAllUser(notice);\r\n        model.addAttribute(\"success\", result ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value());\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    /**\r\n     * 获取系统通知\r\n     * \r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/get\")\r\n    public ModelAndView getNotice(HttpServletRequest request,\r\n            HttpServletResponse response, Model model) {\r\n        String notice = \"\";\r\n        List<String> list = null;\r\n        if (StringUtils.isNotBlank(notice)) {\r\n            list = Arrays.asList(notice.split(ConstUtils.NEXT_LINE));\r\n            model.addAttribute(\"status\", SuccessEnum.SUCCESS.value());\r\n        } else {\r\n            list = new ArrayList<String>();\r\n            model.addAttribute(\"status\", SuccessEnum.FAIL.value());\r\n        }\r\n        model.addAttribute(\"data\", list);\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/OperationController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.constant.AppUserAlertEnum;\nimport com.sohu.cache.constant.AppUserTypeEnum;\nimport com.sohu.cache.constant.MachineInfoEnum;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineDeployCenter;\nimport com.sohu.cache.redis.RedisConfigTemplateService;\nimport com.sohu.cache.task.constant.MachineSyncEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\nimport com.sohu.cache.web.enums.PodStatusEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppAlertRecordService;\nimport com.sohu.cache.web.util.FreemakerUtils;\nimport freemarker.template.Configuration;\nimport io.swagger.annotations.ApiResponse;\nimport io.swagger.annotations.ApiResponses;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.annotation.Resource;\nimport java.text.MessageFormat;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * 运维工具\n * Created by rucao on 2018/7/30\n */\n@RestController\n@RequestMapping(\"operation\")\npublic class OperationController extends BaseController {\n    private final static String DOT = \".\";\n    private final static String IPREG = \"([1-9]|[1-9]\\\\d|1\\\\d{2}|2[0-4]\\\\d|25[0-5])(\\\\.(\\\\d|[1-9]\\\\d|1\\\\d{2}|2[0-4]\\\\d|25[0-5])){3}\";\n    private final static String format = \"{0}:{1}\";\n\n    @Resource\n    private MachineDeployCenter machineDeployCenter;\n    @Resource\n    private MachineDao machineDao;\n    @Resource\n    private InstanceDao instanceDao;\n    @Resource\n    private AppDao appDao;\n    @Resource\n    private MachineRoomDao machineRoomDao;\n    @Resource\n    private EmailComponent emailComponent;\n    @Resource\n    private RedisConfigTemplateService redisConfigTemplateService;\n    @Resource\n    private MachineRelationDao machineRelationDao;\n    @Autowired\n    private Configuration configuration;\n    @Autowired\n    private AppAlertRecordService appAlertRecordService;\n\n    @RequestMapping(value = \"/machines\", method = {RequestMethod.POST})\n    public ResponseEntity<String> addMachines(@RequestHeader(value = \"token\", defaultValue = \"addMachine\") String token,\n                                              @RequestBody List<MachineInfo> machineInfoList) {\n        long startTime = System.currentTimeMillis();\n\n        HttpStatus status = HttpStatus.CREATED;\n        String message = \"\";\n        List<OperationAlertValueResult> resultList = new ArrayList<OperationAlertValueResult>();\n        if (token.isEmpty() || !token.equals(\"addMachine\")) {\n            status = HttpStatus.UNAUTHORIZED;\n            message = \"TOKEN 验证失败\";\n            OperationAlertValueResult operationResult = new OperationAlertValueResult(\"\", null, \"CREATE\", status.toString() + status.getReasonPhrase(), message);\n            resultList.add(operationResult);\n            operationNotice(resultList, \"CREATE\");\n            return ResponseEntity.status(status).body(message);\n        }\n\n        for (int i = 0; i < machineInfoList.size(); i++) {\n            MachineInfo machineInfo = machineInfoList.get(i);\n            logger.info(\"machineInfo :{}\", machineInfo);\n            String ip = machineInfo.getIp();\n            if (!isIp(ip)) {\n                status = HttpStatus.BAD_REQUEST;\n                message += \"ip为空或格式错误，无法插入\\n\";\n                OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, \"CREATE\",\n                        status.toString() + status.getReasonPhrase(),\n                        \"ip为空或格式错误，无法插入\\n\");\n                resultList.add(operationResult);\n                continue;\n            }\n            if (isIpRepeat(ip)) {\n                message += ip + \"已经存在且有效，无法重新插入\\n\";\n                OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, \"CREATE\",\n                        status.toString() + status.getReasonPhrase(),\n                        ip + \"已经存在且有效，无法重新插入\\n\");\n                resultList.add(operationResult);\n                continue;\n            }\n            message += ip;\n\n            if (machineInfo.getMem() == 0) {\n                message += \"内存为空,\";\n            }\n            if (machineInfo.getCpu() == 0) {\n                message += \"cpu为空,\";\n            }\n            if (machineInfo.getVirtual() == 1 && !isIp(machineInfo.getRealIp())) {\n                message += \"宿主机ip为空或格式错误,\";\n            }\n\n            if (machineInfo.getUseType() == 0) {\n                if (StringUtils.isEmpty(machineInfo.getProjectName())) {\n                    message += \"Redis专用机器未填写项目名称,\";\n                } else {\n                    machineInfo.setExtraDesc(MessageFormat.format(format, machineInfo.getExtraDesc(), machineInfo.getProjectName()));\n                }\n\n            }\n            autoInsertRoom(machineInfo);\n            Date date = new Date();\n            machineInfo.setType(MachineInfoEnum.TypeEnum.REDIS_NODE.getType());\n            machineInfo.setCollect(1);\n\n            machineInfo.setSshUser(ConstUtils.USERNAME);\n            machineInfo.setSshPasswd(ConstUtils.PASSWORD);\n            machineInfo.setServiceTime(date);\n            machineInfo.setModifyTime(date);\n            machineInfo.setAvailable(MachineInfoEnum.AvailableEnum.YES.getValue());\n            try {\n                if (!machineDeployCenter.addMachine(machineInfo)) {\n                    message += \"插入失败\\n\";\n                    OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, \"CREATE\",\n                            \"save or deploy machineInfo error\",\n                            \"插入失败\\n\");\n                    resultList.add(operationResult);\n                } else {\n                    message += \"插入成功\\n\";\n                    OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, \"CREATE\",\n                            HttpStatus.CREATED.toString() + HttpStatus.CREATED.getReasonPhrase(),\n                            \"插入成功\\n\");\n                    resultList.add(operationResult);\n                }\n                logger.info(message);\n                // 更新机器安装redis版本\n                if (machineInfo.getAvailable() == MachineInfoEnum.AvailableEnum.YES.getValue()) {\n                    // 3.更新线上机器version_install版本状态\n                    redisConfigTemplateService.updateMachineInstallRedis(machineInfo.getIp());\n                }\n            } catch (Exception ex) {\n                logger.info(\"addMultiMachines: {}\", ex.getMessage());\n            }\n        }\n        operationNotice(resultList, \"CREATE\");\n\n        logger.info(\"add {} machine costtime ={} ms\", machineInfoList.size(), (System.currentTimeMillis() - startTime));\n        return ResponseEntity.status(status).body(message);\n    }\n\n    /**\n     * k8s pod容器变更:上线 (k8s自身调度/升级)\n     *\n     * @param token\n     * @param machineInfoList\n     * @return\n     */\n    @RequestMapping(value = \"/pod/online\", method = {RequestMethod.POST})\n    public ResponseEntity<String> onlinePod(@RequestHeader(value = \"token\", defaultValue = \"onlinePod\") String token,\n                                            @RequestBody List<MachineInfo> machineInfoList) {\n        long startTime = System.currentTimeMillis();\n        HttpStatus status = HttpStatus.CREATED;\n        String message = \"\";\n        String type = \"POD-ONLINE\";\n        List<OperationAlertValueResult> resultList = new ArrayList<OperationAlertValueResult>();\n        if (token.isEmpty() || !token.equals(\"onlinePod\")) {\n            status = HttpStatus.UNAUTHORIZED;\n            message = \"TOKEN 验证失败\";\n            OperationAlertValueResult operationResult = new OperationAlertValueResult(\"\", null, type, status.toString() + status.getReasonPhrase(), message);\n            resultList.add(operationResult);\n            operationNotice(resultList, type);\n            return ResponseEntity.status(status).body(message);\n        }\n\n        if (machineInfoList != null && machineInfoList.size() > 0) {\n            for (MachineInfo machineInfo : machineInfoList) {\n                logger.info(\"machineInfo :{}\", machineInfo);\n                String ip = machineInfo.getIp();\n                String realIp = machineInfo.getRealIp();\n\n                // save machine relation\n                try {\n                    long podUpdateTime = machineInfo.getPodUpdateTime() == 0 ? System.currentTimeMillis() : machineInfo.getPodUpdateTime();\n                    MachineRelation machineRelation = new MachineRelation(ip, realIp, new Date(podUpdateTime), machineInfo.getProjectName(), PodStatusEnum.ONLINE.getValue());\n                    machineRelationDao.saveOrUpdateMachineRelation(machineRelation);\n                } catch (Exception e) {\n                    logger.info(\"save pod online relation: {}\", e.getMessage());\n                }\n\n                if (!isIp(ip)) {\n                    status = HttpStatus.BAD_REQUEST;\n                    message += \"ip为空或格式错误，无法插入\\n\";\n                    OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, type,\n                            status.toString() + status.getReasonPhrase(),\n                            \"ip为空或格式错误，无法插入\\n\");\n                    resultList.add(operationResult);\n                    continue;\n                }\n                message += ip;\n                if (machineInfo.getMem() == 0) {\n                    message += \"内存为空,\";\n                }\n                if (machineInfo.getCpu() == 0) {\n                    message += \"cpu为空,\";\n                }\n                if (machineInfo.getVirtual() == 1 && !isIp(machineInfo.getRealIp())) {\n                    message += \"宿主机ip为空或格式错误,\";\n                }\n                // 专用机器\n                if (machineInfo.getUseType() == 0) {\n                    if (StringUtils.isEmpty(machineInfo.getProjectName())) {\n                        message += \"Redis专用机器未填写项目名称,\";\n                    } else {\n                        machineInfo.setExtraDesc(MessageFormat.format(format, machineInfo.getExtraDesc(), machineInfo.getProjectName()));\n                    }\n\n                }\n                // 插入机房信息\n                autoInsertRoom(machineInfo);\n                Date date = new Date();\n                // 机器类型: redis/迁移工具   机器部署类型: 专用/混合/测试/sentinel\n                MachineInfo machineOldInfo = getMachineInfo(ip);\n                if (machineOldInfo != null) {\n                    machineInfo.setType(machineOldInfo.getType());\n                    machineInfo.setUseType(machineOldInfo.getUseType());\n                } else {\n                    machineInfo.setType(MachineInfoEnum.TypeEnum.REDIS_NODE.getType());\n                }\n                machineInfo.setCollect(1);\n                machineInfo.setSshUser(ConstUtils.USERNAME);\n                machineInfo.setSshPasswd(ConstUtils.PASSWORD);\n                machineInfo.setServiceTime(date);\n                machineInfo.setModifyTime(date);\n                machineInfo.setAvailable(MachineInfoEnum.AvailableEnum.YES.getValue());\n                try {\n                    boolean saveOrUpdate = isK8sIpRepeat(ip);\n                    if (!machineDeployCenter.addMachine(machineInfo)) {\n                        message += \"插入失败\\n\";\n                        OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, type,\n                                \"save or deploy machineInfo error\",\n                                \"插入失败\\n\");\n                        resultList.add(operationResult);\n                    } else {\n                        // 判断机器是新增还是修改\n                        if (saveOrUpdate) {\n                            message += ip + \"修改机器成功\\n\";\n                            OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, type,\n                                    status.toString() + status.getReasonPhrase(),\n                                    ip + \"机器信息修改成功\\n\");\n                            resultList.add(operationResult);\n                            // 提交异步任务检测实例\n                            asyncExecuteDetectTask(ip);\n                        } else {\n                            message += \"新增机器成功\\n\";\n                            OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, type,\n                                    HttpStatus.CREATED.toString() + HttpStatus.CREATED.getReasonPhrase(),\n                                    \"新增机器成功\\n\");\n                            resultList.add(operationResult);\n                        }\n\n                    }\n                    logger.info(message);\n                } catch (Exception ex) {\n                    logger.info(\"add Machine:{} error: {}\", machineInfo, ex.getMessage());\n                }\n            }\n            logger.info(\"add pod {} machine costtime ={} ms\", machineInfoList.size(), (System.currentTimeMillis() - startTime));\n        }\n        operationNotice(resultList, type);\n        return ResponseEntity.status(status).body(message);\n\n    }\n\n    /**\n     * <p>\n     * Description: 异步任务: 检测实例状态 & 滚动重启\n     * </p>\n     */\n    public void asyncExecuteDetectTask(final String ip) {\n\n        String key = \"detect-instance-\" + ip;\n        asyncService.submitFuture(AsyncThreadPoolFactory.DEFAULT_ASYNC_POOL, new KeyCallable<Boolean>(key) {\n            public Boolean execute() {\n                try {\n                    // 1.检测pod是否被调度到其他宿主机\n                    MachineSyncEnum syncStatus = instanceDeployCenter.podChangeStatus(ip);\n                    // 2. pod状态为MachineSyncEnum.NO_CHANGE 或 MachineSyncEnum.SYNC_SUCCESS 自动检测实例\n                    List<InstanceAlertValueResult> instanceAlertValueResults = new ArrayList<>();\n                    if (syncStatus.getValue() == MachineSyncEnum.NO_CHANGE.getValue() ||\n                            syncStatus.getValue() == MachineSyncEnum.SYNC_SUCCESS.getValue()) {\n                        instanceAlertValueResults = instanceDeployCenter.checkAndStartExceptionInstance(ip, true);\n                    }\n                    logger.info(\"pod ip:{} sync status:{}, scroll redis number:{} \", ip, syncStatus.getDesc(), instanceAlertValueResults.size());\n                    return true;\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                    return false;\n                }\n            }\n        });\n    }\n\n    /**\n     * k8s pod容器变更:下线 (k8s自身调度/停止/滚动升级)\n     *\n     * @param token\n     * @param machineInfoList\n     * @return\n     */\n    @RequestMapping(value = \"/pod/offline\", method = {RequestMethod.POST})\n    public ResponseEntity<String> offlinePod(@RequestHeader(value = \"token\", defaultValue = \"offlinePod\") String token,\n                                             @RequestBody List<MachineInfo> machineInfoList) {\n        HttpStatus status = HttpStatus.NO_CONTENT;\n        String message = \"\";\n        String type = \"POD-OFFLINE\";\n\n        logger.info(\"machineinfo list: {}\", machineInfoList);\n        List<OperationAlertValueResult> resultList = new ArrayList<OperationAlertValueResult>();\n        if (token.isEmpty() || !token.equals(\"offlinePod\")) {\n            status = HttpStatus.UNAUTHORIZED;\n            message = \"TOKEN 验证失败\";\n        }\n        // 遍历变更下线的pod\n        if (machineInfoList != null && machineInfoList.size() > 0) {\n            for (MachineInfo machineInfo : machineInfoList) {\n                String ip = machineInfo.getIp();\n                String realIp = machineInfo.getRealIp();\n                // save machine relation\n                try {\n                    long podUpdateTime = machineInfo.getPodUpdateTime() == 0 ? System.currentTimeMillis() : machineInfo.getPodUpdateTime();\n                    MachineRelation machineRelation = new MachineRelation(ip, realIp, new Date(podUpdateTime), machineInfo.getProjectName(), PodStatusEnum.OFFLINE.getValue());\n                    machineRelationDao.saveOrUpdateMachineRelation(machineRelation);\n                    //修改机器状态 为下线\n                    machineDao.removeMachineInfoByIp(ip);\n                } catch (Exception e) {\n                    logger.info(\"save pod offline relation: {}\", e.getMessage());\n                }\n\n                if (!isIp(ip)) {\n                    status = HttpStatus.BAD_REQUEST;\n                    message += \"ip为空或格式错误\\n\";\n                }\n                OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, type, status.toString() + status.getReasonPhrase(), message);\n                resultList.add(operationResult);\n            }\n        }\n        operationNotice(resultList, type);\n        return ResponseEntity.status(status).body(message);\n    }\n\n    @RequestMapping(value = \"/machine\", method = {RequestMethod.DELETE})\n    public ResponseEntity<String> deleteMachine(@RequestHeader(value = \"token\", defaultValue = \"deleteMachine\") String token,\n                                                @RequestParam(value = \"ip\") String ip) {\n\n        HttpStatus status = HttpStatus.NO_CONTENT;\n        String message = \"\";\n        MachineInfo machineInfo = null;\n        List<OperationAlertValueResult> resultList = new ArrayList<OperationAlertValueResult>();\n        if (token.isEmpty() || !token.equals(\"deleteMachine\")) {\n            status = HttpStatus.UNAUTHORIZED;\n            message = \"TOKEN 验证失败\";\n            OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, \"DELETE\", status.toString() + status.getReasonPhrase(), message);\n            resultList.add(operationResult);\n            operationNotice(resultList, \"DELETE\");\n            return ResponseEntity.status(status).body(message);\n        }\n        if (!isIp(ip)) {\n            status = HttpStatus.BAD_REQUEST;\n            message += \"ip为空或格式错误\\n\";\n        } else if (checkMachineActive(ip)) {\n            machineInfo = machineDao.getMachineInfoByIp(ip);\n            status = HttpStatus.INTERNAL_SERVER_ERROR;\n            message += ip + \"有redis节点存活，删除失败\\n\";\n        } else {\n            machineInfo = machineDao.getMachineInfoByIp(ip);\n            if (machineInfo == null) {\n                status = HttpStatus.NOT_FOUND;\n                message += ip + \"机器资源不存在\\n\";\n            } else if (!machineDeployCenter.removeMachine(machineInfo)) {\n                message += ip + \"删除失败\\n\";\n            }\n        }\n        OperationAlertValueResult operationResult = new OperationAlertValueResult(ip, machineInfo, \"DELETE\", status.toString() + status.getReasonPhrase(), message);\n        resultList.add(operationResult);\n        operationNotice(resultList, \"DELETE\");\n        return ResponseEntity.status(status).body(message);\n    }\n\n    @RequestMapping(value = \"/users\", method = RequestMethod.POST)\n    @ApiResponses({\n            @ApiResponse(code = 201, message = \"Created\"),\n            @ApiResponse(code = 400, message = \"Bad Request\"),\n            @ApiResponse(code = 401, message = \"Unauthorized\"),\n            @ApiResponse(code = 500, message = \"Internal Server Error\")\n    })\n    public ResponseEntity<String> addUsers(@RequestHeader(value = \"token\", defaultValue = \"addUsers\") String token,\n                                           @RequestParam(value = \"appId\") Long appId,\n                                           @RequestBody List<AppUser> userList) {\n        HttpStatus status = HttpStatus.CREATED;\n        String message = \"\";\n        if (token.isEmpty() || !token.equals(\"addUsers\")) {\n            status = HttpStatus.UNAUTHORIZED;\n            message = \"TOKEN 验证失败\";\n            return ResponseEntity.status(status).body(message);\n        }\n        try {\n            if (appDao.getOnlineAppDescById(appId) == null) {\n                status = HttpStatus.BAD_REQUEST;\n                message = \"appId应用状态无效\";\n                return ResponseEntity.status(status).body(message);\n            }\n            if (userList.size() == 0) {\n                status = HttpStatus.BAD_REQUEST;\n                message = \"userList为空\";\n                return ResponseEntity.status(status).body(message);\n            }\n\n            for (AppUser appUser : userList) {\n                String userName = appUser.getName();\n                if (StringUtils.isNotBlank(userName)) {\n                    try {\n                        AppUser needAddAppUser = userService.getByName(userName);\n                        if (needAddAppUser == null) {\n                            if (StringUtils.isBlank(appUser.getChName())) {\n                                appUser.setChName(\"\");\n                            }\n                            if (StringUtils.isBlank(appUser.getEmail())) {\n                                appUser.setEmail(\"\");\n                            }\n                            if (StringUtils.isBlank(appUser.getMobile())) {\n                                appUser.setMobile(\"\");\n                            }\n                            if (StringUtils.isBlank(appUser.getWeChat())) {\n                                appUser.setWeChat(\"\");\n                            }\n                            appUser.setType(AppUserTypeEnum.REGULAR_USER.value());\n                            appUser.setIsAlert(AppUserAlertEnum.YES.value());\n                            if (userService.save(appUser) == SuccessEnum.SUCCESS) {\n                                needAddAppUser = userService.getByName(userName);\n                            } else {\n                                status = HttpStatus.INTERNAL_SERVER_ERROR;\n                                message += \"用户:\" + userName + \"不存在，且用户创建失败\\n\";\n                            }\n                        }\n                        if (needAddAppUser != null) {\n                            if (appService.saveAppToUser(appId, needAddAppUser.getId())) {\n                                message += \"用户:\" + userName + \"添加成功\\n\";\n                            } else {\n                                status = HttpStatus.INTERNAL_SERVER_ERROR;\n                                message += \"用户:\" + userName + \"添加失败\\n\";\n                            }\n\n                        }\n                    } catch (Exception ex) {\n                        logger.info(\"addUsers: {}\", ex.getMessage());\n                    }\n                } else {\n                    status = HttpStatus.BAD_REQUEST;\n                    message += \"用户名(英文，域账户)为空\\n\";\n                }\n            }\n\n        } catch (Exception ex) {\n            logger.info(\"addUsers: {}\", ex.getMessage());\n        }\n        return ResponseEntity.status(status).body(message);\n    }\n\n    /**\n     * 根据网段自动识别机房并填入\n     *\n     * @param machineInfo\n     */\n    private void autoInsertRoom(MachineInfo machineInfo) {\n        String ip = machineInfo.getIp();\n        List<MachineRoom> roomList = machineRoomDao.getEffectiveRoom();\n        for (MachineRoom room : roomList) {\n            String ipNetwork = room.getIpNetwork();\n            String ipSub1 = ipNetwork.substring(0, ipNetwork.indexOf(DOT, 3));\n            String ipSub2 = ip.substring(0, ip.indexOf(DOT, 3));\n            if (ipSub1.equals(ipSub2)) {\n                machineInfo.setRoom(room.getName());\n            }\n        }\n    }\n\n    /**\n     * 正则验证ip格式\n     *\n     * @param ipAddress\n     * @return\n     */\n    private boolean isIp(String ipAddress) {\n        if (ipAddress == null || ipAddress.isEmpty())\n            return false;\n        Pattern pattern = Pattern.compile(IPREG);\n        Matcher matcher = pattern.matcher(ipAddress);\n        return matcher.matches();\n    }\n\n    /**\n     * ip是否重复 available==1\n     *\n     * @param ip\n     * @return\n     */\n    private boolean isIpRepeat(String ip) {\n        List<MachineInfo> machineInfos = machineDao.getMachineInfoByLikeIp(ip);\n        if (machineInfos == null || machineInfos.size() == 0) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * ip,realIp 是否存在\n     *\n     * @param ip\n     * @return\n     */\n    private boolean isK8sIpRepeat(String ip) {\n        MachineInfo machineInfo = machineDao.existk8sMachine(ip);\n        if (machineInfo == null) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * 机器类型(type/useType) 不覆盖\n     *\n     * @param ip\n     * @return\n     */\n    private MachineInfo getMachineInfo(String ip) {\n        return machineDao.getMachineInfoByIp(ip);\n    }\n\n    /**\n     * machine的可删除状态，false表示不存活，可删；true表示有存活节点，不可删\n     *\n     * @param ip\n     * @return\n     */\n    private boolean checkMachineActive(String ip) {\n        List<InstanceInfo> instancelist = instanceDao.getInstListByIp(ip);\n        if (instancelist == null || instancelist.size() == 0)\n            return false;\n        return true;\n    }\n\n    /**\n     * 操作给管理员发邮件通知\n     *\n     * @param operationAlertValueResultList\n     * @param type\n     */\n    private void operationNotice(List<OperationAlertValueResult> operationAlertValueResultList, String type) {\n        String title = \"【CacheCloud】\";\n        if (type.equalsIgnoreCase(\"DELETE\")) {\n            title += \"销毁机器\";\n        } else if (type.equalsIgnoreCase(\"CREATE\")) {\n            title += \"添加机器\";\n        } else if (type.equalsIgnoreCase(\"POD-ONLINE\")) {\n            title += \"POD状态变更:上线\";\n        } else if (type.equalsIgnoreCase(\"POD-OFFLINE\")) {\n            title += \"POD状态变更:下线\";\n        }\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"operationAlertValueResultList\", operationAlertValueResultList);\n        String mailContent = FreemakerUtils.createText(\"OperationAlert.ftl\", configuration, context);\n        logger.info(\"send mail content: {}\" + operationAlertValueResultList);\n        appAlertRecordService.saveAlertInfoByType(AlertTypeEnum.MACHINE_MANAGE, title, null, operationAlertValueResultList);\n        emailComponent.sendMailToAdmin(title, mailContent);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/QuartzManageController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport java.util.List;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\nimport com.sohu.cache.entity.TriggerInfo;\r\nimport com.sohu.cache.schedule.SchedulerCenter;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.quartz.TriggerKey;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\n\r\n/**\r\n * quartz管理test\r\n *\r\n * @author leifu\r\n * @Time 2014年7月4日\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/quartz\")\r\npublic class QuartzManageController extends BaseController {\r\n\r\n    @Resource\r\n    private SchedulerCenter schedulerCenter;\r\n\r\n    @RequestMapping(value = \"/list\")\r\n    public ModelAndView doQuartzList(HttpServletRequest request,\r\n                                     HttpServletResponse response, Model model) {\r\n        String query = request.getParameter(\"query\");\r\n        List<TriggerInfo> triggerList;\r\n        if (StringUtils.isBlank(query)) {\r\n            triggerList = schedulerCenter.getAllTriggers();\r\n            query = \"\";\r\n        } else {\r\n            triggerList = schedulerCenter.getTriggersByNameOrGroup(query);\r\n        }\r\n        model.addAttribute(\"triggerList\", triggerList);\r\n        model.addAttribute(\"quartzActive\", SuccessEnum.SUCCESS.value());\r\n        model.addAttribute(\"query\", query);\r\n        return new ModelAndView(\"manage/quartz/list\");\r\n    }\r\n\r\n    @RequestMapping(value = \"/pause\")\r\n    public String pause(HttpServletRequest request,\r\n                                     HttpServletResponse response, Model model) {\r\n        String name = request.getParameter(\"name\");\r\n        String group = request.getParameter(\"group\");\r\n        if (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(group)) {\r\n            schedulerCenter.pauseTrigger(new TriggerKey(name, group));\r\n        }\r\n        return \"redirect:/manage/quartz/list\";\r\n    }\r\n\r\n    @RequestMapping(value = \"/resume\")\r\n    public String resume(HttpServletRequest request,\r\n                        HttpServletResponse response, Model model) {\r\n        String name = request.getParameter(\"name\");\r\n        String group = request.getParameter(\"group\");\r\n        if (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(group)) {\r\n            schedulerCenter.resumeTrigger(new TriggerKey(name, group));\r\n        }\r\n        return \"redirect:/manage/quartz/list\";\r\n    }\r\n\r\n    @RequestMapping(value = \"/remove\")\r\n    public String remove(HttpServletRequest request,\r\n                         HttpServletResponse response, Model model) {\r\n        String name = request.getParameter(\"name\");\r\n        String group = request.getParameter(\"group\");\r\n        if (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(group)) {\r\n            schedulerCenter.unscheduleJob(new TriggerKey(name, group));\r\n        }\r\n        return \"redirect:/manage/quartz/list\";\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/RedisConfigTemplateController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.sohu.cache.constant.ErrorMessageEnum;\r\nimport com.sohu.cache.constant.RedisConfigTemplateChangeEnum;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.entity.InstanceConfig;\r\nimport com.sohu.cache.entity.SystemResource;\r\nimport com.sohu.cache.redis.RedisConfigTemplateService;\r\nimport com.sohu.cache.task.constant.ResourceEnum;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.service.ResourceService;\r\nimport com.sohu.cache.web.util.AppEmailUtil;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.util.ArrayList;\r\nimport java.util.Date;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * Redis配置模板管理\r\n *\r\n * @author leifu\r\n * @Date 2016-6-25\r\n * @Time 下午2:48:25\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/redisConfig\")\r\npublic class RedisConfigTemplateController extends BaseController {\r\n\r\n    @Resource(name = \"redisConfigTemplateService\")\r\n    private RedisConfigTemplateService redisConfigTemplateService;\r\n\r\n    @Resource(name = \"appEmailUtil\")\r\n    private AppEmailUtil appEmailUtil;\r\n\r\n    @Autowired\r\n    private ResourceService resourceService;\r\n\r\n    /**\r\n     * 初始化配置\r\n     */\r\n    @RequestMapping(value = \"/init\")\r\n    public ModelAndView init(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        // 默认是Redis普通节点配置\r\n        int type = NumberUtils.toInt(request.getParameter(\"type\"), ConstUtils.CACHE_REDIS_STANDALONE);\r\n        int resourceId = NumberUtils.toInt(request.getParameter(\"versionid\"),0);\r\n\r\n        // 获取redis资源包\r\n        List<SystemResource> resourceList = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\r\n        model.addAttribute(\"resourceList\", resourceList);\r\n        if(resourceId == 0){\r\n            model.addAttribute(\"currentVersion\", resourceList.get(0));\r\n        }else{\r\n            model.addAttribute(\"currentVersion\", resourceService.getResourceById(resourceId));\r\n        }\r\n        model.addAttribute(\"redisConfigList\", redisConfigTemplateService.getByVesionAndType(type, resourceId));\r\n        model.addAttribute(\"success\", request.getParameter(\"success\"));\r\n        model.addAttribute(\"redisConfigActive\", SuccessEnum.SUCCESS.value());\r\n        model.addAttribute(\"versionid\", resourceId);\r\n        model.addAttribute(\"type\", type);\r\n        return new ModelAndView(\"manage/redisConfig/init\");\r\n    }\r\n\r\n    /**\r\n     * 修改配置\r\n     */\r\n    @RequestMapping(value = \"/update\")\r\n    public ModelAndView update(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        String versionName = StringUtils.isEmpty(request.getParameter(\"versionName\")) ? \"\" : String.valueOf(request.getParameter(\"versionName\"));\r\n        String id = request.getParameter(\"id\");\r\n        String configKey = request.getParameter(\"configKey\");\r\n        String configValue = request.getParameter(\"configValue\");\r\n        String info = request.getParameter(\"info\");\r\n        int status = NumberUtils.toInt(request.getParameter(\"status\"), -1);\r\n        int valueType = NumberUtils.toInt(request.getParameter(\"valueType\"), 0);\r\n        if (StringUtils.isBlank(id) || !NumberUtils.isDigits(id) || StringUtils.isBlank(configKey) || status > 1\r\n                || status < 0) {\r\n            model.addAttribute(\"status\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", ErrorMessageEnum.PARAM_ERROR_MSG.getMessage() + \"id=\" + id + \",configKey=\"\r\n                    + configKey + \",configValue=\" + configValue + \",status=\" + status);\r\n            return new ModelAndView(\"\");\r\n        }\r\n        //开始修改\r\n        logger.warn(\"user {} want to change id={}'s configKey={}, configValue={}, info={}, status={}\", appUser.getName(),\r\n                id, configKey, configValue, info, status);\r\n        SuccessEnum successEnum;\r\n        InstanceConfig instanceConfig = redisConfigTemplateService.getById(NumberUtils.toLong(id));\r\n        try {\r\n            instanceConfig.setConfigValue(configValue);\r\n            instanceConfig.setInfo(info);\r\n            instanceConfig.setStatus(status);\r\n            instanceConfig.setValueType(valueType);\r\n            redisConfigTemplateService.saveOrUpdate(instanceConfig);\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.warn(\"user {} want to change {} id={}'s configKey={}, configValue={}, info={}, status={}, result is {}\", appUser.getName(), versionName,\r\n                id, configKey, configValue, info, status, successEnum.value());\r\n        //发送邮件通知\r\n        appEmailUtil.sendRedisConfigTemplateChangeEmail(appUser, versionName, instanceConfig, successEnum, RedisConfigTemplateChangeEnum.UPDATE);\r\n        model.addAttribute(\"status\", successEnum.value());\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    /**\r\n     * 删除配置\r\n     */\r\n    @RequestMapping(value = \"/remove\")\r\n    public ModelAndView remove(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        String idParam = request.getParameter(\"id\");\r\n        String versionName = StringUtils.isEmpty(request.getParameter(\"versionName\")) ? \"\" : String.valueOf(request.getParameter(\"versionName\"));\r\n        long id = NumberUtils.toLong(idParam);\r\n        if (id <= 0) {\r\n            model.addAttribute(\"status\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", ErrorMessageEnum.PARAM_ERROR_MSG.getMessage() + \"id=\" + idParam);\r\n            return new ModelAndView(\"\");\r\n        }\r\n        logger.warn(\"user {} want to delete id={}'s config\", appUser.getName(), id);\r\n        SuccessEnum successEnum;\r\n        InstanceConfig instanceConfig = redisConfigTemplateService.getById(id);\r\n        try {\r\n            redisConfigTemplateService.remove(id);\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.warn(\"user {} want to delete {} id={}'s config, result is {}\", appUser.getName(), versionName, id, successEnum.value());\r\n        //发送邮件通知\r\n        appEmailUtil.sendRedisConfigTemplateChangeEmail(appUser, versionName, instanceConfig, successEnum, RedisConfigTemplateChangeEnum.DELETE);\r\n        model.addAttribute(\"status\", successEnum.value());\r\n        return new ModelAndView(\"\");\r\n\r\n    }\r\n\r\n    /**\r\n     * 添加配置\r\n     */\r\n    @RequestMapping(value = \"/add\")\r\n    public ModelAndView add(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        AppUser appUser = getUserInfo(request);\r\n        String versionName = StringUtils.isEmpty(request.getParameter(\"versionName\")) ? \"\" : String.valueOf(request.getParameter(\"versionName\"));\r\n        InstanceConfig instanceConfig = getInstanceConfig(request);\r\n        if (StringUtils.isBlank(instanceConfig.getConfigKey())) {\r\n            model.addAttribute(\"status\", SuccessEnum.FAIL.value());\r\n            model.addAttribute(\"message\", ErrorMessageEnum.PARAM_ERROR_MSG.getMessage() + \"configKey=\" + instanceConfig.getConfigKey());\r\n            return new ModelAndView(\"\");\r\n        }\r\n        logger.warn(\"user {} want to add config, configKey is {}, configValue is {}, type is {}\", appUser.getName(),\r\n                instanceConfig.getConfigKey(), instanceConfig.getConfigValue(), instanceConfig.getType());\r\n        SuccessEnum successEnum;\r\n        try {\r\n            redisConfigTemplateService.saveOrUpdate(instanceConfig);\r\n            successEnum = SuccessEnum.SUCCESS;\r\n        } catch (Exception e) {\r\n            successEnum = SuccessEnum.FAIL;\r\n            model.addAttribute(\"message\", ErrorMessageEnum.INNER_ERROR_MSG.getMessage());\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.warn(\"user {} want to add {} config, configKey is {}, configValue is {}, type is {}, result is {}\",\r\n                appUser.getName(), versionName,\r\n                instanceConfig.getConfigKey(), instanceConfig.getConfigValue(), instanceConfig.getType(), successEnum.value());\r\n        model.addAttribute(\"status\", successEnum.value());\r\n        //发送邮件通知\r\n        appEmailUtil.sendRedisConfigTemplateChangeEmail(appUser, versionName, instanceConfig, successEnum, RedisConfigTemplateChangeEnum.ADD);\r\n        return new ModelAndView(\"\");\r\n\r\n    }\r\n\r\n    /**\r\n     * Redis配置对比\r\n     */\r\n    @RequestMapping(value = \"/contrast\")\r\n    public ModelAndView contrast(HttpServletRequest request, Model model, Integer upgradeVersionId, Integer currentVersionId) {\r\n\r\n        //对比配置\r\n        List<InstanceConfig> configList_current = redisConfigTemplateService.getByVesion(currentVersionId);\r\n        List<InstanceConfig> configList_upgrade = redisConfigTemplateService.getByVesion(upgradeVersionId);\r\n        // current Map\r\n        Map<String, Object> currentConfigMap = new HashMap<String, Object>();\r\n        for (InstanceConfig instanceConfig : configList_current) {\r\n            if (instanceConfig.getStatus() == 1) {\r\n                currentConfigMap.put(instanceConfig.getConfigKey(), instanceConfig.getConfigValue());\r\n            }\r\n        }\r\n        // upgrade Map\r\n        Map<String, Object> upgradeConfigMap = new HashMap<String, Object>();\r\n        for (InstanceConfig instanceConfig : configList_upgrade) {\r\n            if (instanceConfig.getStatus() == 1) {\r\n                upgradeConfigMap.put(instanceConfig.getConfigKey(), instanceConfig.getConfigValue());\r\n            }\r\n        }\r\n        // same Map\r\n        Map<String, Object> sameConfigMap = new HashMap<String, Object>();\r\n\r\n        logger.info(\"current config item size ={}\", configList_current.size());\r\n        logger.info(\"upgarde config item size ={}\", configList_upgrade.size());\r\n        // 临时变量\r\n        List<InstanceConfig> localConfig = configList_upgrade;\r\n        // 配置变更\r\n        for (InstanceConfig instanceConfig : localConfig) {\r\n            String key = instanceConfig.getConfigKey();\r\n            String value = instanceConfig.getConfigValue();\r\n            if (currentConfigMap.containsKey(key) && currentConfigMap.get(key).equals(value)) {\r\n                currentConfigMap.remove(key);\r\n                upgradeConfigMap.remove(key);\r\n                sameConfigMap.put(key, value);\r\n            }\r\n        }\r\n        logger.info(\"diff current : {},size ={}\", currentConfigMap, currentConfigMap.size());\r\n        logger.info(\"diff upgrade : {},size ={}\", upgradeConfigMap, upgradeConfigMap.size());\r\n        logger.info(\"same configuration: {},size ={}\", sameConfigMap, sameConfigMap.size());\r\n\r\n        // 获取当前redis的所有版本\r\n        model.addAttribute(\"sameConfigMap\", sameConfigMap);\r\n        model.addAttribute(\"currentConfigMap\", currentConfigMap);\r\n        model.addAttribute(\"currentVersion\", resourceService.getResourceById(currentVersionId));\r\n        model.addAttribute(\"upgradeConfigMap\", upgradeConfigMap);\r\n        model.addAttribute(\"upgradeVersion\", resourceService.getResourceById(upgradeVersionId));\r\n        model.addAttribute(\"success\", request.getParameter(\"success\"));\r\n        model.addAttribute(\"redisConfigActive\", SuccessEnum.SUCCESS.value());\r\n\r\n        return new ModelAndView(\"manage/redisConfig/contrast\");\r\n    }\r\n\r\n    /**\r\n     * 预览配置\r\n     */\r\n    @RequestMapping(value = \"/preview\")\r\n    public ModelAndView preview(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        //默认配置\r\n        int type = NumberUtils.toInt(request.getParameter(\"type\"), -1);\r\n        String host = StringUtils.isBlank(request.getParameter(\"host\")) ? \"127.0.0.1\" : request.getParameter(\"host\");\r\n        int port = NumberUtils.toInt(request.getParameter(\"port\"), 6379);\r\n        int maxMemory = NumberUtils.toInt(request.getParameter(\"maxMemory\"), 2048);\r\n        int sentinelPort = NumberUtils.toInt(request.getParameter(\"sentinelPort\"), 26379);\r\n        String masterName = StringUtils.isBlank(request.getParameter(\"masterName\")) ? \"myMaster\" : request\r\n                .getParameter(\"masterName\");\r\n        int versionId = NumberUtils.toInt(request.getParameter(\"versionId\"), 1);\r\n\r\n        // 根据类型生成配置模板\r\n        List<String> configList = new ArrayList<String>();\r\n        if (ConstUtils.CACHE_REDIS_STANDALONE == type) {\r\n            configList = redisConfigTemplateService.handleCommonConfig(host, port, maxMemory, null, versionId);\r\n        } else if (ConstUtils.CACHE_REDIS_SENTINEL == type) {\r\n            configList = redisConfigTemplateService.handleSentinelConfig(masterName, host, port, host, sentinelPort, versionId, null);\r\n        } else if (ConstUtils.CACHE_TYPE_REDIS_CLUSTER == type) {\r\n            configList = redisConfigTemplateService.handleClusterConfig(port, versionId);\r\n        }\r\n        model.addAttribute(\"type\", type);\r\n        model.addAttribute(\"host\", host);\r\n        model.addAttribute(\"port\", port);\r\n        model.addAttribute(\"maxMemory\", maxMemory);\r\n        model.addAttribute(\"sentinelPort\", sentinelPort);\r\n        model.addAttribute(\"masterName\", masterName);\r\n        model.addAttribute(\"configList\", configList);\r\n        return new ModelAndView(\"manage/redisConfig/preview\");\r\n    }\r\n\r\n    /**\r\n     * 使用最简单的request生成InstanceConfig对象\r\n     *\r\n     * @return\r\n     */\r\n    private InstanceConfig getInstanceConfig(HttpServletRequest request) {\r\n        String configKey = request.getParameter(\"configKey\");\r\n        String configValue = request.getParameter(\"configValue\");\r\n        String info = request.getParameter(\"info\");\r\n        String type = request.getParameter(\"type\");\r\n        String versionid = request.getParameter(\"versionid\");\r\n        int valueType = NumberUtils.toInt(request.getParameter(\"valueType\"), 0);\r\n        InstanceConfig instanceConfig = new InstanceConfig();\r\n        instanceConfig.setConfigKey(configKey);\r\n        instanceConfig.setConfigValue(configValue);\r\n        instanceConfig.setInfo(info);\r\n        instanceConfig.setType(NumberUtils.toInt(type));\r\n        instanceConfig.setUpdateTime(new Date());\r\n        instanceConfig.setStatus(1);\r\n        instanceConfig.setVersionId(NumberUtils.toInt(versionid));\r\n        instanceConfig.setValueType(valueType);\r\n\r\n        return instanceConfig;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/RedisVersionUpgradeController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.RedisVersionStat;\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.RedisConfigTemplateService;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.ResourceService;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.*;\n\n/**\n * Created by chenshi on 2018/9/4.\n */\n@Controller\n@RequestMapping(\"manage/redis/upgrade\")\npublic class RedisVersionUpgradeController extends BaseController {\n\n    private Logger logger = LoggerFactory.getLogger(RedisVersionUpgradeController.class);\n\n    @Autowired\n    private RedisConfigTemplateService redisConfigTemplateService;\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private ResourceService resourceService;\n\n    /**\n     * <p>\n     * Description: 实例和配置检查\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/9/11\n     */\n    @RequestMapping(value = \"check/instance\", method = {RequestMethod.GET, RequestMethod.POST})\n    public ModelAndView checkInstance(HttpServletResponse response, Long appId, Integer upgradeVersionId, String upgradeVersionName) {\n\n        logger.info(\"--------check instance , appId:{} , upgradeVersionId:{}\", appId, upgradeVersionId);\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        SuccessEnum successEnum;\n\n        // 1.param check\n        if (appId == null || upgradeVersionId == null) {\n            // return error\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", String.format(\"appId:%s或版本号:%s参数不正确\", appId, upgradeVersionName));\n            return null;\n        }\n        // 2.版本有效检查\n        SystemResource redisResource = resourceService.getResourceById(upgradeVersionId);\n        if (redisResource == null || redisResource.getStatus() == 0) {\n            // 无该版本配置 || 版本配置无效\n            resultMap.put(\"status\", SuccessEnum.ERROR.value());\n            resultMap.put(\"message\", String.format(\"升级版本号%s 无效或删除\", upgradeVersionName));\n            return null;\n        }\n        // 4. 版本配置项变更预览\n        // 5. master是否都有slave节点,所有实例信息\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        Set<String> machineSet = new HashSet<String>();\n        String instanceInfo;\n        StringBuilder instanceInfoBuilder = new StringBuilder();\n        int masterNum = 0;\n        int slaveNum = 0;\n        if (instanceList != null && instanceList.size() > 0) {\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    String redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\n                    instanceInfoBuilder.append(instance.getIp())\n                            .append(\":\")\n                            .append(instance.getPort())\n                            .append(instance.getRoleDesc())\n                            .append(\" version:\")\n                            .append(redisVersion)\n                            .append(\"\\n\");\n                    // 主从节点判断\n                    if (instance.getRoleDesc().equals(\"slave\")) {\n                        slaveNum++;\n                    } else if (instance.getRoleDesc().equals(\"master\")) {\n                        masterNum++;\n                    }\n                    machineSet.add(instance.getIp());\n                }\n            }\n        }\n        instanceInfo = instanceInfoBuilder.toString();\n        // master节点数 <= slave节点数，才能迁移\n        if (masterNum > slaveNum || instanceList == null) {\n            successEnum = SuccessEnum.ERROR;\n            resultMap.put(\"message\", String.format(\"主(%d)从(%d)实例节点不一致\", masterNum, slaveNum));\n        } else {\n            successEnum = SuccessEnum.SUCCESS;\n        }\n        // 5.1 实例机器redis资源包安装检查，如果没有redis资源包，自动拉取安装资源包\n        String machineInstallInfo = \"\";\n        if (!CollectionUtils.isEmpty(machineSet) && successEnum == SuccessEnum.SUCCESS) {\n            for (String machineIp : machineSet) {\n                // Redis资源校验&推包\n                Boolean installStatus = redisConfigTemplateService.checkAndInstallRedisResource(machineIp, redisResource);\n                if (!installStatus) {\n                    successEnum = SuccessEnum.ERROR;\n                    resultMap.put(\"message\", String.format(\"%s安装 %s版本失败,请检查日志！\", machineIp, redisResource.getName()));\n                    break;\n                }\n            }\n        }\n\n        resultMap.put(\"status\", successEnum.value());\n        resultMap.put(\"upgradeVersion\", redisResource);\n        resultMap.put(\"instanceInfo\", instanceInfo);\n        resultMap.put(\"machineInstallInfo\", machineInstallInfo);\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    /**\n     * <p>\n     * Description: slave更新配置并重启\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/9/13\n     */\n    @RequestMapping(value = \"slave/update/config\", method = RequestMethod.POST)\n    public ModelAndView slaveUpdateConfig(HttpServletResponse response, Long appId, Integer upgradeVersionId, String upgradeVersionName) {\n\n        Map<String, Object> resultMap = redisConfigTemplateService.slaveUpdateConfig(appId, upgradeVersionId, upgradeVersionName);\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    /**\n     * <p>\n     * Description: slave failover\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/9/13\n     */\n    @RequestMapping(value = \"slave/failover\", method = RequestMethod.POST)\n    public ModelAndView masterSlavefailover(HttpServletResponse response, Long appId) {\n\n        // 1.failover 状态\n        Map<String, Object> resultMap = redisConfigTemplateService.slaveFailover(appId);\n        // 2. 输出节点信息及日志\n        List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);\n        StringBuilder instanceInfoBuilder = new StringBuilder();\n        StringBuilder instanceLogBuilder = new StringBuilder();\n        if (instanceList != null && instanceList.size() > 0) {\n            for (InstanceInfo instance : instanceList) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    String redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\n                    //instanceInfo += instance.getIp() + \":\" + instance.getPort() + \" \" + instance.getRoleDesc() + \" version:\" + redisVersion + \" \\n\";\n                    //instanceLog += \"<a target='_blank' href=/manage/instance/log?instanceId=\" + instance.getId() + \">日志</a><br/>\";\n                    instanceInfoBuilder.append(instance.getIp())\n                            .append(\":\")\n                            .append(instance.getPort())\n                            .append(\" \")\n                            .append(instance.getRoleDesc())\n                            .append(\" version:\")\n                            .append(redisVersion)\n                            .append(\" \\n\");\n                    instanceLogBuilder.append(\"<a target='_blank' href=/manage/instance/log?instanceId=\" + instance.getId() + \">日志</a><br/>\");\n                }\n            }\n        }\n        resultMap.put(\"instanceInfo\", instanceInfoBuilder.toString());\n        resultMap.put(\"instanceLog\", instanceLogBuilder.toString());\n\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    /**\n     * <p>\n     * Description: 更新应用信息，返回最终实例信息\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/9/13\n     */\n    @RequestMapping(value = \"complete/check\", method = {RequestMethod.POST, RequestMethod.POST})\n    public ModelAndView completeCheck(HttpServletResponse response, Long appId, Integer upgradeVersionId) {\n\n        Map<String, Object> resultMap = new HashMap<String, Object>();\n        // 1.更新应用为升级的版本\n        AppDesc appDesc = appService.getByAppId(appId);\n        appDesc.setVersionId(upgradeVersionId);\n        appService.update(appDesc);\n        // 2.遍历当前节点状态\n        List<InstanceInfo> instancelist = appService.getAppInstanceInfo(appId);\n        StringBuilder instanceInfoBuilder = new StringBuilder();\n        if (instancelist != null && instancelist.size() > 0) {\n            for (InstanceInfo instance : instancelist) {\n                if (instance.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    String redisVersion = redisCenter.getRedisVersion(appId, instance.getIp(), instance.getPort());\n                    //instanceInfo += instance.getIp() + \":\" + instance.getPort() + \" \" + instance.getRoleDesc() + \" version:\" + redisVersion + \" \\n\";\n                    instanceInfoBuilder.append(instance.getIp())\n                            .append(\":\")\n                            .append(instance.getPort())\n                            .append(\" \")\n                            .append(instance.getRoleDesc())\n                            .append(\" version:\")\n                            .append(redisVersion)\n                            .append(\" \\n\");\n                }\n            }\n        }\n\n        resultMap.put(\"status\", SuccessEnum.SUCCESS.value());\n        resultMap.put(\"instanceInfo\", instanceInfoBuilder.toString());\n        resultMap.put(\"instanceLog\", \"\");\n        sendMessage(response, JSONObject.toJSONString(resultMap));\n        return null;\n    }\n\n    public void setRedisConfigTemplateService(RedisConfigTemplateService redisConfigTemplateService) {\n        this.redisConfigTemplateService = redisConfigTemplateService;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/ResourceController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.task.TaskService;\nimport com.sohu.cache.task.constant.PushEnum;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.ResourceService;\nimport com.sohu.cache.web.service.ServerDataService;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by chenshi on 2020/7/3.\n */\n@Controller\n@RequestMapping(\"/manage/app/resource\")\npublic class ResourceController extends BaseController {\n\n    @Autowired\n    ResourceService resourceService;\n    @Autowired\n    SSHService sshService;\n    @Autowired\n    TaskService taskService;\n    @Autowired\n    ServerDataService serverDataService;\n\n    @RequestMapping(\"/index\")\n    public ModelAndView index(Model model, String tabTag, String searchName) {\n        model.addAttribute(\"tabTag\", tabTag);\n        model.addAttribute(\"searchName\", searchName);\n\n        model.addAttribute(\"reourcesActive\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"manage/resource/list\");\n    }\n\n    @RequestMapping(\"/redis/{tab}\")\n    public ModelAndView tab(@PathVariable(\"tab\") String tab, String searchName, Model model) {\n        List<SystemResource> resourceList = new ArrayList<SystemResource>();\n        int resource_type = 0;\n        List<ServerInfo> allServerInfo = new ArrayList<>();\n        switch (tab) {\n            case \"respo\":\n                resource_type = ResourceEnum.ALL.getValue();\n                break;\n            case \"script\":\n                resource_type = ResourceEnum.SCRIPT.getValue();\n                break;\n            case \"redis\":\n                resource_type = ResourceEnum.REDIS.getValue();\n                allServerInfo.addAll(serverDataService.getAllServerInfo());\n                break;\n            case \"tool\":\n                resource_type = ResourceEnum.TOOL.getValue();\n                break;\n            case \"sshkey\":\n                resource_type = ResourceEnum.SSHKEY.getValue();\n                break;\n            case \"dir\":\n                resource_type = ResourceEnum.DIR.getValue();\n                break;\n            default:\n                break;\n        }\n\n        if (StringUtils.isEmpty(searchName)) {\n            resourceList = resourceService.getResourceList(resource_type);\n        } else {\n            resourceList = resourceService.getResourceList(resource_type, searchName);\n            model.addAttribute(\"searchName\", searchName);\n        }\n\n        //仓库资源\n        List<SystemResource> reposlist = resourceService.getResourceList(ResourceEnum.Repository.getValue());\n        if (!CollectionUtils.isEmpty(reposlist)) {\n            model.addAttribute(\"repository\", reposlist.get(0));\n        }\n        // 目录资源\n        List<SystemResource> dirlist = resourceService.getResourceList(ResourceEnum.DIR.getValue());\n\n        model.addAttribute(\"tabTag\", tab);\n        model.addAttribute(\"resourceList\", resourceList);\n        model.addAttribute(\"allServerInfo\", allServerInfo);\n        model.addAttribute(\"dirList\", dirlist);\n        model.addAttribute(\"appUseMap\", resourceService.getAppUseRedis());\n        model.addAttribute(\"reourcesActive\", SuccessEnum.SUCCESS.value());\n        return new ModelAndView(\"manage/resource/\" + tab);\n    }\n\n    @RequestMapping(\"/add\")\n    public ModelAndView resourceAdd(HttpServletRequest request, HttpServletResponse response, Integer resourceId, Model model) {\n\n        JSONObject result = new JSONObject();\n        SystemResource resource = new SystemResource();\n        //修改\n        if (resourceId != null && resourceId > 0) {\n            resource = resourceService.getResourceById(resourceId);\n        }\n        int versionCopyId = NumberUtils.toInt(request.getParameter(\"copyVersion\"), -1);\n        int resourceType = NumberUtils.toInt(request.getParameter(\"resourceType\"));\n        resource.setName(request.getParameter(\"resourceName\"));\n        resource.setIntro(request.getParameter(\"resourceDesc\"));\n        resource.setDir(request.getParameter(\"resourceDir\"));\n        resource.setStatus(NumberUtils.toInt(request.getParameter(\"resourceStatus\")));\n        resource.setType(resourceType);\n        resource.setUrl(request.getParameter(\"resourceUrl\"));\n        resource.setOrderNum(NumberUtils.toInt(request.getParameter(\"orderNum\")));\n        resource.setLastmodify(new Date());\n        resource.setUsername(getUserInfo(request).getName());\n\n\n        SuccessEnum successEnum = null;\n        if (resourceId != null && resourceId > 0) {\n            resource.setId(resourceId);\n            successEnum = resourceService.updateResource(resource);\n        } else {\n            // 验重\n            SystemResource existResource = resourceService.getResourceByName(resource.getName());\n            if (existResource != null && existResource.getId() > 0) {\n                result.put(\"status\", SuccessEnum.FAIL.value());\n                result.put(\"message\", \"资源名已存在\");\n                sendMessage(response, result.toString());\n                return null;\n            }\n\n            resource.setIspush(PushEnum.NO.getValue());\n            successEnum = resourceService.saveResource(resource);\n            if (versionCopyId > -1 && resourceType == ResourceEnum.REDIS.getValue()) {\n                redisConfigTemplateService.copyRedisConfig(versionCopyId, resource);\n            }\n        }\n\n        result.put(\"status\", successEnum.value());\n        sendMessage(response, result.toString());\n\n        return null;\n    }\n\n    @RequestMapping(\"/get\")\n    public ModelAndView resourceGet(HttpServletResponse response, Integer resourceId) {\n\n        JSONObject result = new JSONObject();\n        SuccessEnum successEnum = SuccessEnum.FAIL;\n\n        if (resourceId != null && resourceId > 0) {\n            SystemResource resource = resourceService.getResourceById(resourceId);\n            if (resource != null) {\n                successEnum = SuccessEnum.SUCCESS;\n                result.put(\"resource\", resource);\n            }\n        }\n        result.put(\"status\", successEnum.value());\n        sendMessage(response, result.toString());\n        return null;\n    }\n\n    @RequestMapping(\"/ssh\")\n    public ModelAndView generateSshkey(HttpServletResponse response, String command, String containerIp, Integer resourceId) {\n\n        JSONObject result = new JSONObject();\n        if (StringUtils.isEmpty(containerIp)) {\n            containerIp = machineCenter.getFirstMachineIp();\n        }\n        SuccessEnum successEnum = SuccessEnum.FAIL;\n\n        /*if (resourceId != null && resourceId > 0) {\n            Boolean flag = resourceService.generateSshkey(containerIp, command);\n        }*/\n        result.put(\"status\", successEnum.value());\n        sendMessage(response, result.toString());\n        return null;\n    }\n\n    @RequestMapping(\"/push\")\n    public ModelAndView resourcePush(HttpServletRequest request, HttpServletResponse response, Integer repositoryId, Integer resourceId, String content) {\n\n        // 1. push content\n        SuccessEnum successEnum = null;\n        JSONObject result = new JSONObject();\n        AppUser userInfo = getUserInfo(request);\n        if (repositoryId != null && resourceId != null) {\n            SystemResource resource = resourceService.getResourceById(resourceId);\n            if (resource.getType() == ResourceEnum.SCRIPT.getValue()) {\n                // 1.1推送脚本资源\n                successEnum = resourceService.pushScript(repositoryId, resourceId, content, userInfo);\n                // 1.2.清理临时资源\n                clearTempResource(String.valueOf(resourceId));\n            } else if (resource.getType() == ResourceEnum.DIR.getValue()) {\n                // 2.1.推送目录资源\n                successEnum = resourceService.pushDir(repositoryId, resourceId, userInfo);\n            }\n            result.put(\"status\", successEnum == null ? successEnum : successEnum.value());\n        } else {\n            result.put(\"status\", SuccessEnum.ERROR.value());\n            result.put(\"message\", \"资源id异常\");\n        }\n\n        sendMessage(response, result.toString());\n\n        return null;\n    }\n\n    @RequestMapping(\"/compile\")\n    public ModelAndView compile(HttpServletRequest request, HttpServletResponse response, String containerIp, String compileInfo, Integer repositoryId, Integer resourceId, String content) {\n\n        // 1. push content\n        JSONObject result = new JSONObject();\n\n        if (StringUtils.isEmpty(containerIp)) {\n            // 从机器列表获取一台可用机器\n            containerIp = machineCenter.getFirstMachineIp();\n        }\n        AppUser userInfo = getUserInfo(request);\n        if (repositoryId != null && resourceId != null) {\n            // 推送资源\n            long taskid = taskService.addResourceCompileTask(resourceId, repositoryId, containerIp, userInfo);\n            SystemResource resource = resourceService.getResourceById(resourceId);\n            resource.setTaskId(taskid);\n            resource.setCompileInfo(compileInfo);\n            resourceService.updateResource(resource);\n            result.put(\"status\", SuccessEnum.SUCCESS.value());\n        } else {\n            result.put(\"status\", SuccessEnum.ERROR.value());\n            result.put(\"message\", \"资源id异常\");\n        }\n\n        sendMessage(response, result.toString());\n        return null;\n    }\n\n    @RequestMapping(\"/temporarySave\")\n    public ModelAndView temporarySave(HttpServletResponse response, Integer resourceId, String content) {\n\n        // 1. push content\n        JSONObject result = new JSONObject();\n        if (saveTempResource(String.valueOf(resourceId), content)) {\n            result.put(\"status\", SuccessEnum.SUCCESS.value());\n            SystemResource resource = resourceService.getResourceById(resourceId);\n            if (resource.getIspush() == PushEnum.NO.getValue()) {\n                resource.setIspush(PushEnum.NO_WITH_MODIFY.getValue());\n            } else if (resource.getIspush() == PushEnum.YES.getValue()) {\n                resource.setIspush(PushEnum.YES_WITH_MODIFY.getValue());\n            }\n            resourceService.updateResource(resource);\n        }\n        sendMessage(response, result.toString());\n        return null;\n    }\n\n    @RequestMapping(\"/script/load\")\n    public ModelAndView load(HttpServletResponse response, Integer resourceId, Integer respositoryId) {\n\n        // 1. push content\n        JSONObject result = new JSONObject();\n        if (resourceId != null && respositoryId != null) {\n            String localContent = getTempResource(String.valueOf(resourceId));\n\n            if (StringUtils.isEmpty(localContent)) {\n                String remoteContent = resourceService.getRemoteFileContent(resourceId, respositoryId);\n                if (!StringUtils.isEmpty(remoteContent)) {\n                    result.put(\"content\", remoteContent);\n                    // 来源:远程文件\n                    result.put(\"source\", \"2\");\n                }\n            } else {\n                // 来源:临时文件\n                result.put(\"content\", localContent);\n                result.put(\"source\", \"1\");\n            }\n            result.put(\"script\", localContent);\n            result.put(\"status\", SuccessEnum.SUCCESS.value());\n        }\n        sendMessage(response, result.toString());\n        return null;\n    }\n\n    @RequestMapping(\"/config\")\n    public ModelAndView config(HttpServletRequest request, HttpServletResponse response, Integer repositoryId, Integer resourceId) {\n\n        // 1. push content\n        JSONObject result = new JSONObject();\n\n        AppUser userInfo = getUserInfo(request);\n        if (repositoryId != null && resourceId != null) {\n\n        }\n\n        return new ModelAndView(\"manage/resource/config\");\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/ServerController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.alibaba.fastjson.JSON;\r\nimport com.sohu.cache.entity.ServerInfo;\r\nimport com.sohu.cache.entity.ServerStatus;\r\nimport com.sohu.cache.web.service.ServerDataService;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.math.BigDecimal;\r\nimport java.text.DecimalFormat;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.*;\r\n\r\n/**\r\n * 获取服务器状态\r\n */\r\n@Controller\r\n@RequestMapping(\"/server\")\r\npublic class ServerController extends BaseController{\r\n\r\n\t@Resource\r\n\tprivate ServerDataService serverDataService;\r\n\t\r\n\tprivate DecimalFormat df = new DecimalFormat(\"0.0\");\r\n\t/**\r\n\t * 跳转到主页\r\n\t * @param request\r\n\t * @param response\r\n\t * @param model\r\n\t * @return\r\n\t */\r\n\t@RequestMapping(\"/index\")\r\n\tpublic ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n\t\tString ip = request.getParameter(\"ip\");\r\n\t\tmodel.addAttribute(\"ip\", ip);\r\n\t\tString date = request.getParameter(\"date\");\r\n\t\tif(date == null) {\r\n\t\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\r\n\t\t\tdate = sdf.format(new Date());\r\n\t\t}\r\n\t\tmodel.addAttribute(\"date\", date);\r\n        return new ModelAndView(\"server/index\");\r\n\t}\r\n\t/**\r\n\t * 服务器信息概览\r\n\t * @param request\r\n\t * @param response\r\n\t * @param model\r\n\t * @return\r\n\t */\r\n\t@RequestMapping(\"/overview\")\r\n\tpublic ModelAndView overview(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n        String ip = request.getParameter(\"ip\");\r\n        String date = request.getParameter(\"date\");\r\n        //获取服务器静态信息\r\n        ServerInfo info = serverDataService.queryServerInfo(ip);\r\n        if(info != null) {\r\n\t        model.addAttribute(\"info\", info);\r\n\t        //解析ulimit\r\n\t        String ulimit = info.getUlimit();\r\n\t\t\tif(!StringUtils.isEmpty(ulimit)) {\r\n\t\t\t\tString[] tmp = ulimit.split(\";\");\r\n\t\t\t\tif(tmp.length ==2) {\r\n\t\t\t\t\tString[] a = tmp[0].split(\",\");\r\n\t\t\t\t\tif(a != null && a.length == 2) {\r\n\t\t\t\t\t\tif(\"f\".equals(a[0])) {\r\n\t\t\t\t\t\t\tmodel.addAttribute(\"file\", a[1]);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\ta = tmp[1].split(\",\");\r\n\t\t\t\t\tif(a != null && a.length == 2) {\r\n\t\t\t\t\t\tif(\"p\".equals(a[0])) {\r\n\t\t\t\t\t\t\tmodel.addAttribute(\"process\", a[1]);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n        }\r\n\t\t//获取服务器状态\r\n        List<ServerStatus> list = serverDataService.queryServerOverview(ip, date);\r\n    \t//x轴坐标\r\n\t\tList<String> xAxis = new ArrayList<String>();\r\n\t\t\r\n\t\t//1分钟最大load\r\n\t\tfloat maxLoad1 = 0;\r\n\t\t//load1总量\r\n\t\tdouble totalLoad1 = 0;\r\n\t\t\r\n\t\t//最大user\r\n\t\tfloat maxUser = 0;\r\n\t\t//最大sys\r\n\t\tfloat maxSys = 0;\r\n\t\t//最大wio\r\n\t\tfloat maxWa = 0;\r\n\t\t\r\n\t\t//当前可用内存\r\n\t\tfloat curFree = 0;\r\n\t\t//最大内存使用量\r\n\t\tfloat maxUse = 0;\r\n\t\t//最大内存cache量\r\n\t\tfloat maxCache = 0;\r\n\t\t//最大内存buffer量\r\n\t\tfloat maxBuffer = 0;\r\n\t\t//最大swap使用量\r\n\t\tfloat maxSwapUse = 0;\r\n\t\t\r\n\t\t//最大网络流入速度\r\n\t\tfloat maxNetIn = 0;\r\n\t\t//最大网络流出速度\r\n\t\tfloat maxNetOut = 0;\r\n\t\t//最大连接ESTABLISHED数\r\n\t\tint maxConn = 0;\r\n\t\t//最大连接TIME_WAIT数\r\n\t\tint maxWait = 0;\r\n\t\t//最大连接ORPHAN数\r\n\t\tint maxOrphan = 0;\r\n\t\t\r\n\t\t//最大读取速率\r\n\t\tfloat maxRead = 0;\r\n\t\t//最大写入速率\r\n\t\tfloat maxWrite = 0;\r\n\t\t//最繁忙程度\r\n\t\tfloat maxBusy = 0;\r\n\t\t//最大iops量\r\n\t\tfloat maxIops = 0;\r\n\t\t\r\n\t\t//load serie\r\n\t\tSeries<Float> load1Serie = new Series<Float>(\"1-min\");\r\n\t\tSeries<Float> load5Serie = new Series<Float>(\"5-min\");\r\n\t\tSeries<Float> load15Serie = new Series<Float>(\"15-min\");\r\n\t\t\r\n\t\t//cpu serie\r\n\t\tSeries<Float> userSerie = new Series<Float>(\"user\");\r\n\t\tSeries<Float> sysSerie = new Series<Float>(\"sys\");\r\n\t\tSeries<Float> waSerie = new Series<Float>(\"wa\");\r\n\t\t\r\n\t\t//memory serie\r\n\t\tSeries<Float> totalSerie = new Series<Float>(\"total\");\r\n\t\tSeries<Float> useSerie = new Series<Float>(\"use\");\r\n\t\tuseSerie.setType(\"area\");\r\n\t\tSeries<Float> cacheSerie = new Series<Float>(\"cache\");\r\n\t\tcacheSerie.setType(\"area\");\r\n\t\tSeries<Float> bufferSerie = new Series<Float>(\"buffer\");\r\n\t\tbufferSerie.setType(\"area\");\r\n\t\tSeries<Float> swapSerie = new Series<Float>(\"total\");\r\n\t\tSeries<Float> swapUseSerie = new Series<Float>(\"use\");\r\n\t\t\r\n\t\t//net serie\r\n\t\tSeries<Float> netInSerie = new Series<Float>(\"in\");\r\n\t\tSeries<Float> netOutSerie = new Series<Float>(\"out\");\r\n\t\t\r\n\t\t//tcp serie\r\n\t\tSeries<Integer> establishedSerie = new Series<Integer>(\"established\");\r\n\t\tSeries<Integer> twSerie = new Series<Integer>(\"time wait\");\r\n\t\tSeries<Integer> orphanSerie = new Series<Integer>(\"orphan\");\r\n\t\t\r\n\t\t//disk serie\r\n\t\tSeries<Float> readSerie = new Series<Float>(\"read\");\r\n\t\treadSerie.setType(\"column\");\r\n\t\tSeries<Float> writeSerie = new Series<Float>(\"write\");\r\n\t\twriteSerie.setType(\"column\");\r\n\t\tSeries<Float> busySerie = new Series<Float>(\"busy\");\r\n\t\tbusySerie.setYAxis(1);\r\n\t\tSeries<Float> iopsSerie = new Series<Float>(\"iops\");\r\n\t\tiopsSerie.setYAxis(2);\r\n\t\t\r\n\t\tfor(int i = 0; i < list.size(); ++i) {\r\n\t\t\tServerStatus ss = list.get(i);\r\n\t\t\t//x axis\r\n\t\t\txAxis.add(ss.getCtime().substring(0, 2) + \":\" + ss.getCtime().substring(2));\r\n\t\t\t//load相关\r\n\t\t\tload1Serie.addData(ss.getCload1());\r\n\t\t\tload5Serie.addData(ss.getCload5());\r\n\t\t\tload15Serie.addData(ss.getCload15());\r\n\t\t\tmaxLoad1 = getBigger(maxLoad1, ss.getCload1());\r\n\t\t\ttotalLoad1 += ss.getCload1();\r\n\t\t\t//cpu相关\r\n\t\t\tuserSerie.addData(ss.getCuser());\r\n\t\t\tsysSerie.addData(ss.getCsys());\r\n\t\t\twaSerie.addData(ss.getCwio());\r\n\t\t\tmaxUser = getBigger(maxUser, ss.getCuser());\r\n\t\t\tmaxSys = getBigger(maxSys, ss.getCsys());\r\n\t\t\tmaxWa = getBigger(maxWa, ss.getCwio());\r\n\t\t\t//memory相关\r\n\t\t\ttotalSerie.addData(ss.getMtotal());\r\n\t\t\tfloat use = ss.getMtotal()-ss.getMfree()-ss.getMcache()-ss.getMbuffer();\r\n\t\t\tuseSerie.addData(use);\r\n\t\t\tcacheSerie.addData(ss.getMcache());\r\n\t\t\tbufferSerie.addData(ss.getMbuffer());\r\n\t\t\tmaxUse = getBigger(maxUse, use);\r\n\t\t\tmaxCache = getBigger(maxCache, ss.getMcache());\r\n\t\t\tmaxBuffer = getBigger(maxBuffer, ss.getMbuffer());\r\n\t\t\tif(i == list.size() - 1) {\r\n\t\t\t\tcurFree = ss.getMtotal() - use;\r\n\t\t\t}\r\n\t\t\t//swap相关\r\n\t\t\tswapSerie.addData(ss.getMswap());\r\n\t\t\tfloat swapUse = ss.getMswap() - ss.getMswapFree();\r\n\t\t\tswapUse = floor(swapUse);\r\n\t\t\tswapUseSerie.addData(swapUse);\r\n\t\t\tmaxSwapUse = getBigger(maxSwapUse, swapUse);\r\n\t\t\t//net相关\r\n\t\t\tnetInSerie.addData(ss.getNin());\r\n\t\t\tnetOutSerie.addData(ss.getNout());\r\n\t\t\tmaxNetIn = getBigger(maxNetIn, ss.getNin());\r\n\t\t\tmaxNetOut = getBigger(maxNetOut, ss.getNout());\r\n\t\t\t//tcp相关\r\n\t\t\testablishedSerie.addData(ss.getTuse());\r\n\t\t\ttwSerie.addData(ss.getTwait());\r\n\t\t\torphanSerie.addData(ss.getTorphan());\r\n\t\t\tmaxConn = getBigger(maxConn, ss.getTuse());\r\n\t\t\tmaxWait = getBigger(maxWait, ss.getTwait());\r\n\t\t\tmaxOrphan = getBigger(maxOrphan, ss.getTorphan());\r\n\t\t\t//disk相关\r\n\t\t\treadSerie.addData(ss.getDread());\r\n\t\t\twriteSerie.addData(ss.getDwrite());\r\n\t\t\tbusySerie.addData(ss.getDbusy());\r\n\t\t\tiopsSerie.addData(ss.getDiops());\r\n\t\t\tmaxRead = getBigger(maxRead, ss.getDread());\r\n\t\t\tmaxWrite = getBigger(maxWrite, ss.getDwrite());\r\n\t\t\tmaxBusy = getBigger(maxBusy, ss.getDbusy());\r\n\t\t\tmaxIops = getBigger(maxIops, ss.getDiops());\r\n\t\t}\r\n\t\t//x axis\r\n\t\tmodel.addAttribute(\"xAxis\", JSON.toJSONString(xAxis));\r\n\t\t//load\r\n\t\tmodel.addAttribute(\"load1\", JSON.toJSONString(load1Serie));\r\n\t\tmodel.addAttribute(\"load5\", JSON.toJSONString(load5Serie));\r\n\t\tmodel.addAttribute(\"load15\", JSON.toJSONString(load15Serie));\r\n\t\tmodel.addAttribute(\"maxLoad1\", maxLoad1);\r\n\t\tmodel.addAttribute(\"avgLoad1\", format(totalLoad1, list.size()));\r\n\t\t//cpu\r\n\t\tmodel.addAttribute(\"user\", JSON.toJSONString(userSerie));\r\n\t\tmodel.addAttribute(\"sys\", JSON.toJSONString(sysSerie));\r\n\t\tmodel.addAttribute(\"wa\", JSON.toJSONString(waSerie));\r\n\t\tmodel.addAttribute(\"maxUser\", maxUser);\r\n\t\tmodel.addAttribute(\"maxSys\", maxSys);\r\n\t\tmodel.addAttribute(\"maxWa\", maxWa);\r\n\t\t//memory\r\n\t\tmodel.addAttribute(\"mtotal\", JSON.toJSONString(totalSerie));\r\n\t\tmodel.addAttribute(\"muse\", JSON.toJSONString(useSerie));\r\n\t\tmodel.addAttribute(\"mcache\", JSON.toJSONString(cacheSerie));\r\n\t\tmodel.addAttribute(\"mbuffer\", JSON.toJSONString(bufferSerie));\r\n\t\tmodel.addAttribute(\"curFree\", format(curFree, 1024));\r\n\t\tmodel.addAttribute(\"maxUse\", format(maxUse, 1024));\r\n\t\tmodel.addAttribute(\"maxCache\", format(maxCache, 1024));\r\n\t\tmodel.addAttribute(\"maxBuffer\", format(maxBuffer, 1024));\r\n\t\t//swap\r\n\t\tmodel.addAttribute(\"mswap\", JSON.toJSONString(swapSerie));\r\n\t\tmodel.addAttribute(\"mswapUse\", JSON.toJSONString(swapUseSerie));\r\n\t\tmodel.addAttribute(\"maxSwap\", maxSwapUse);\r\n\t\t//net\r\n\t\tmodel.addAttribute(\"nin\", JSON.toJSONString(netInSerie));\r\n\t\tmodel.addAttribute(\"nout\", JSON.toJSONString(netOutSerie));\r\n\t\tmodel.addAttribute(\"maxNetIn\", format(maxNetIn, 1024));\r\n\t\tmodel.addAttribute(\"maxNetOut\", format(maxNetOut, 1024));\r\n\t\t//tcp\r\n\t\tmodel.addAttribute(\"testab\", JSON.toJSONString(establishedSerie));\r\n\t\tmodel.addAttribute(\"twait\", JSON.toJSONString(twSerie));\r\n\t\tmodel.addAttribute(\"torph\", JSON.toJSONString(orphanSerie));\r\n\t\tmodel.addAttribute(\"maxConn\", maxConn);\r\n\t\tmodel.addAttribute(\"maxWait\", maxWait);\r\n\t\tmodel.addAttribute(\"maxOrphan\", maxOrphan);\r\n\t\t//disk\r\n\t\tmodel.addAttribute(\"dread\", JSON.toJSONString(readSerie));\r\n\t\tmodel.addAttribute(\"dwrite\", JSON.toJSONString(writeSerie));\r\n\t\tmodel.addAttribute(\"dbusy\", JSON.toJSONString(busySerie));\r\n\t\tmodel.addAttribute(\"diops\", JSON.toJSONString(iopsSerie));\r\n\t\tmodel.addAttribute(\"maxRead\", format(maxRead, 1024));\r\n\t\tmodel.addAttribute(\"maxWrite\", format(maxWrite, 1024));\r\n\t\tmodel.addAttribute(\"maxBusy\", maxBusy);\r\n\t\tmodel.addAttribute(\"maxIops\", maxIops);\r\n        model.addAttribute(\"date\", date);\r\n        return new ModelAndView(\"server/overview\");\r\n\t}\r\n\t\r\n\tprivate String format(double a, int b) {\r\n\t\tif(b <= 0) {\r\n\t\t\treturn \"0\";\r\n\t\t}\r\n\t\treturn df.format(a/b);\r\n\t}\r\n\t\r\n\tprivate float getBigger(float a, float b) {\r\n\t\tif(a > b) {\r\n\t\t\treturn a;\r\n\t\t}\r\n\t\treturn b;\r\n\t}\r\n\t\r\n\tprivate int getBigger(int a, int b) {\r\n\t\tif(a > b) {\r\n\t\t\treturn a;\r\n\t\t}\r\n\t\treturn b;\r\n\t}\r\n\t\r\n\t/**\r\n\t * 保留一位小数，四舍五入\r\n\t * @param v\r\n\t * @return\r\n\t */\r\n\tprivate float floor(float v) {\r\n\t\treturn new BigDecimal(v).setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();\r\n\t}\r\n\t\r\n\t/**\r\n\t * 获取服务器cpu各个核状态\r\n\t * @param request\r\n\t * @param response\r\n\t * @param model\r\n\t * @return\r\n\t */\r\n\t@RequestMapping(\"/cpu\")\r\n\tpublic ModelAndView cpu(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n\t\tString ip = request.getParameter(\"ip\");\r\n        String date = request.getParameter(\"date\");\r\n        List<ServerStatus> list = serverDataService.queryServerCpu(ip, date);\r\n        Map<String, CpuChart> subcpuMap = new TreeMap<String, CpuChart>();\r\n        //x轴坐标\r\n\t\tList<String> xAxis = new ArrayList<String>();\r\n        for(ServerStatus ss : list) {\r\n        \tString subcpuString = ss.getCExt();\r\n\t\t\tString[] subCpuArray = subcpuString.split(\";\");\r\n\t\t\txAxis.add(ss.getCtime());\r\n\t\t\tfor(String subcpu : subCpuArray) {\r\n\t\t\t\tif(StringUtils.isEmpty(subcpu)) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tString[] cpu = subcpu.split(\",\");\r\n\t\t\t\tCpuChart cpuChart = subcpuMap.get(cpu[0]);\r\n\t\t\t\tif(cpuChart == null) {\r\n\t\t\t\t\tcpuChart = new CpuChart(cpu[0]);\r\n\t\t\t\t\tsubcpuMap.put(cpu[0], cpuChart);\r\n\t\t\t\t}\r\n\t\t\t\tfloat user = NumberUtils.toFloat(cpu[1]);\r\n\t\t\t\tfloat sys = NumberUtils.toFloat(cpu[2]);\r\n\t\t\t\tfloat wa = NumberUtils.toFloat(cpu[3]);\r\n\t\t\t\tcpuChart.addUserSeries(user);\r\n\t\t\t\tcpuChart.addSysSeries(sys);\r\n\t\t\t\tcpuChart.addWaSeries(wa);\r\n\t\t\t\tcpuChart.setMaxUser(user);\r\n\t\t\t\tcpuChart.setMaxSys(sys);\r\n\t\t\t\tcpuChart.setMaxWa(wa);\r\n\t\t\t\tcpuChart.addUser(user);\r\n\t\t\t\tcpuChart.addSys(sys);\r\n\t\t\t\tcpuChart.addWa(wa);\r\n\t\t\t}\r\n        }\r\n        //x axis\r\n\t\tmodel.addAttribute(\"xAxis\", JSON.toJSONString(xAxis));\r\n        model.addAttribute(\"cpu\", subcpuMap.values());\r\n        return new ModelAndView(\"server/cpu\");\r\n\t}\r\n\t\r\n\t/**\r\n\t * 获取服务器各网卡状态\r\n\t * @param request\r\n\t * @param response\r\n\t * @param model\r\n\t * @return\r\n\t */\r\n\t@RequestMapping(\"/net\")\r\n\tpublic ModelAndView net(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n\t\tString ip = request.getParameter(\"ip\");\r\n        String date = request.getParameter(\"date\");\r\n        List<ServerStatus> list = serverDataService.queryServerNet(ip, date);\r\n        Map<String, NetChart> subnetMap = new TreeMap<String, NetChart>();\r\n        //x轴坐标\r\n\t\tList<String> xAxis = new ArrayList<String>();\r\n        for(ServerStatus ss : list) {\r\n        \txAxis.add(ss.getCtime());\r\n        \taddNetMap(ss.getNinExt(), subnetMap, true);\r\n        \taddNetMap(ss.getNoutExt(), subnetMap, false);\r\n        }\r\n        //x axis\r\n\t\tmodel.addAttribute(\"xAxis\", JSON.toJSONString(xAxis));\r\n        model.addAttribute(\"net\", subnetMap.values());\r\n        return new ModelAndView(\"server/net\");\r\n\t}\r\n\t\r\n\t/**\r\n\t * parse net to map\r\n\t * @param netString\r\n\t * @param subnetMap\r\n\t * @param isIn\r\n\t */\r\n\tprivate void addNetMap(String netString, Map<String, NetChart> subnetMap, boolean isIn) {\r\n\t\tString[] subnetArray = netString.split(\";\");\r\n\t\tfor(String subnet : subnetArray) {\r\n\t\t\tif(StringUtils.isEmpty(subnet)) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tString[] net = subnet.split(\",\");\r\n\t\t\tNetChart netChart = subnetMap.get(net[0]);\r\n\t\t\tif(netChart == null) {\r\n\t\t\t\tnetChart = new NetChart(net[0]);\r\n\t\t\t\tsubnetMap.put(net[0], netChart);\r\n\t\t\t}\r\n\t\t\tfloat v = NumberUtils.toFloat(net[1]);\r\n\t\t\tif(isIn) {\r\n\t\t\t\tnetChart.addInSeries(v);\r\n\t\t\t\tnetChart.addTotalIn(v);\r\n\t\t\t\tnetChart.setMaxIn(v);\r\n\t\t\t}else {\r\n\t\t\t\tnetChart.addOutSeries(v);\r\n\t\t\t\tnetChart.addTotalOut(v);\r\n\t\t\t\tnetChart.setMaxOut(v);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * 获取硬盘各分区状态\r\n\t * @param request\r\n\t * @param response\r\n\t * @param model\r\n\t * @return\r\n\t */\r\n\t@RequestMapping(\"/disk\")\r\n\tpublic ModelAndView disk(HttpServletRequest request, HttpServletResponse response, Model model) {\r\n\t\tString ip = request.getParameter(\"ip\");\r\n        String date = request.getParameter(\"date\");\r\n        List<ServerStatus> list = serverDataService.queryServerDisk(ip, date);\r\n        DiskChart readChart = new DiskChart();\r\n        DiskChart writeChart = new DiskChart();\r\n        DiskChart busyChart = new DiskChart();\r\n        DiskChart iopsChart = new DiskChart();\r\n        DiskChart spaceChart = new DiskChart();\r\n        //x轴坐标\r\n\t\tList<String> xAxis = new ArrayList<String>();\r\n        for(ServerStatus ss : list) {\r\n        \txAxis.add(ss.getCtime());\r\n        \t//解析use\r\n        \tString dext = ss.getDExt();\r\n\t\t\tif(!StringUtils.isEmpty(dext)) {\r\n\t\t\t\tString[] items = dext.split(\";\");\r\n\t\t\t\tif(items != null) {\r\n\t\t\t\t\tfor(String item : items) {\r\n\t\t\t\t\t\tString[] sds = item.split(\"=\");\r\n\t\t\t\t\t\tif(sds.length == 2) {\r\n\t\t\t\t\t\t\tif(\"DISKXFER\".equals(sds[0])) {\r\n\t\t\t\t\t\t\t\taddToChart(sds[1], iopsChart);\r\n\t\t\t\t\t\t\t} else if(\"DISKREAD\".equals(sds[0])) {\r\n\t\t\t\t\t\t\t\taddToChart(sds[1], readChart);\r\n\t\t\t\t\t\t\t} else if(\"DISKWRITE\".equals(sds[0])) {\r\n\t\t\t\t\t\t\t\taddToChart(sds[1], writeChart);\r\n\t\t\t\t\t\t\t} else if(\"DISKBUSY\".equals(sds[0])) {\r\n\t\t\t\t\t\t\t\taddToChart(sds[1], busyChart);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//解析space\r\n\t\t\tString space = ss.getDspace();\r\n\t\t\taddToChart(space, spaceChart);\r\n        }\r\n        //x axis\r\n\t\tmodel.addAttribute(\"xAxis\", JSON.toJSONString(xAxis));\r\n        model.addAttribute(\"read\", readChart);\r\n        model.addAttribute(\"write\", writeChart);\r\n        model.addAttribute(\"busy\", busyChart);\r\n        model.addAttribute(\"iops\", iopsChart);\r\n        model.addAttribute(\"space\", spaceChart);\r\n        return new ModelAndView(\"server/disk\");\r\n\t}\r\n\t\r\n\tprivate void addToChart(String line, DiskChart chart) {\r\n\t\tString[] parts = line.split(\",\");\r\n\t\tfor(String part : parts) {\r\n\t\t\tif(StringUtils.isEmpty(part)) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tString[] values = part.split(\":\");\r\n\t\t\tfloat d = NumberUtils.toFloat(values[1]);\r\n\t\t\tchart.addSeries(values[0], d);\r\n\t\t\tchart.setMax(d);\r\n\t\t\tchart.addTotal(d);\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * net chart\r\n\t */\r\n\tpublic class NetChart{\r\n\t\tprivate String name;\r\n\t\tprivate Series<Float> inSeries = new Series<Float>(\"in\");\r\n\t\tprivate Series<Float> outSeries = new Series<Float>(\"out\");\r\n\t\tprivate float maxIn;\r\n\t\tprivate float maxOut;\r\n\t\tprivate float totalIn;\r\n\t\tprivate float totalOut;\r\n\t\tpublic NetChart(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\tpublic String getName() {\r\n\t\t\treturn name;\r\n\t\t}\r\n\t\tpublic void setName(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\tpublic Series<Float> getInSeries() {\r\n\t\t\treturn inSeries;\r\n\t\t}\r\n\t\tpublic void addInSeries(float d) {\r\n\t\t\tthis.inSeries.addData(d);\r\n\t\t}\r\n\t\tpublic Series<Float> getOutSeries() {\r\n\t\t\treturn outSeries;\r\n\t\t}\r\n\t\tpublic void addOutSeries(float d) {\r\n\t\t\tthis.outSeries.addData(d);\r\n\t\t}\r\n\t\tpublic float getMaxIn() {\r\n\t\t\treturn maxIn;\r\n\t\t}\r\n\t\tpublic void setMaxIn(float in) {\r\n\t\t\tif(this.maxIn < in) {\r\n\t\t\t\tthis.maxIn = in;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic float getMaxOut() {\r\n\t\t\treturn maxOut;\r\n\t\t}\r\n\t\tpublic void setMaxOut(float out) {\r\n\t\t\tif(this.maxOut < out) {\r\n\t\t\t\tthis.maxOut = out;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic void addTotalIn(float in) {\r\n\t\t\tthis.totalIn += in;\r\n\t\t}\r\n\t\tpublic void addTotalOut(float out) {\r\n\t\t\tthis.totalOut += out;\r\n\t\t}\r\n\t\tpublic String getAvgIn() {\r\n\t\t\treturn format(totalIn, inSeries.getData().size());\r\n\t\t}\r\n\t\tpublic String getAvgOut() {\r\n\t\t\treturn format(totalOut, outSeries.getData().size());\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * disk chart\r\n\t */\r\n\tpublic class DiskChart{\r\n\t\tprivate float max;\r\n\t\tprivate float total;\r\n\t\tprivate Map<String, Series<Float>> seriesMap = new TreeMap<String, Series<Float>>();\r\n\t\tpublic void addSeries(String partition, float d) {\r\n\t\t\tSeries<Float> series = seriesMap.get(partition);\r\n\t\t\tif(series == null) {\r\n\t\t\t\tseries = new Series<Float>(partition);\r\n\t\t\t\tseriesMap.put(partition, series);\r\n\t\t\t}\r\n\t\t\tseries.addData(d);\r\n\t\t}\r\n\t\tpublic Collection<Series<Float>> getSeries() {\r\n\t\t\treturn seriesMap.values();\r\n\t\t}\r\n\t\tpublic float getMax() {\r\n\t\t\treturn max;\r\n\t\t}\r\n\t\tpublic void setMax(float max) {\r\n\t\t\tif(this.max < max) {\r\n\t\t\t\tthis.max = max;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic String getAvg() {\r\n\t\t\tCollection<Series<Float>> coll = seriesMap.values();\r\n\t\t\tint size = 0;\r\n\t\t\tif(coll != null) {\r\n\t\t\t\tfor(Series<Float> series : coll) {\r\n\t\t\t\t\tsize += series.getData().size();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn format(total, size);\r\n\t\t}\r\n\t\tpublic void addTotal(float total) {\r\n\t\t\tthis.total += total;\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * cpu chart\r\n\t */\r\n\tpublic class CpuChart{\r\n\t\tprivate String name;\r\n\t\tprivate Series<Float> userSeries = new Series<Float>(\"user\");\r\n\t\tprivate Series<Float> sysSeries = new Series<Float>(\"sys\");\r\n\t\tprivate Series<Float> waSeries = new Series<Float>(\"wa\");\r\n\t\tprivate float maxUser;\r\n\t\tprivate float maxSys;\r\n\t\tprivate float maxWa;\r\n\t\tprivate float totalUser;\r\n\t\tprivate float totalSys;\r\n\t\tprivate float totalWa;\r\n\t\tpublic CpuChart(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\tpublic String getName() {\r\n\t\t\treturn name;\r\n\t\t}\r\n\t\tpublic float getMaxUser() {\r\n\t\t\treturn maxUser;\r\n\t\t}\r\n\t\tpublic void setMaxUser(float user) {\r\n\t\t\tif(this.maxUser < user) {\r\n\t\t\t\tthis.maxUser = user;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic float getMaxSys() {\r\n\t\t\treturn maxSys;\r\n\t\t}\r\n\t\tpublic void setMaxSys(float sys) {\r\n\t\t\tif(this.maxSys < sys) {\r\n\t\t\t\tthis.maxSys = sys;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic float getMaxWa() {\r\n\t\t\treturn maxWa;\r\n\t\t}\r\n\t\tpublic void setMaxWa(float wa) {\r\n\t\t\tif(this.maxWa < wa) {\r\n\t\t\t\tthis.maxWa = wa;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic String getAvgUser() {\r\n\t\t\treturn format(totalUser, userSeries.getData().size());\r\n\t\t}\r\n\t\tpublic String getAvgSys() {\r\n\t\t\treturn format(totalSys, sysSeries.getData().size());\r\n\t\t}\r\n\t\tpublic String getAvgWa() {\r\n\t\t\treturn format(totalWa, waSeries.getData().size());\r\n\t\t}\r\n\t\tpublic void addUser(float user) {\r\n\t\t\tthis.totalUser += user;\r\n\t\t}\r\n\t\tpublic void addSys(float sys) {\r\n\t\t\tthis.totalSys += sys;\r\n\t\t}\r\n\t\tpublic void addWa(float wa) {\r\n\t\t\tthis.totalWa += wa;\r\n\t\t}\r\n\t\tpublic Series<Float> getUserSeries() {\r\n\t\t\treturn userSeries;\r\n\t\t}\r\n\t\tpublic void addUserSeries(Float v) {\r\n\t\t\tthis.userSeries.addData(v);\r\n\t\t}\r\n\t\tpublic Series<Float> getSysSeries() {\r\n\t\t\treturn sysSeries;\r\n\t\t}\r\n\t\tpublic void addSysSeries(Float v) {\r\n\t\t\tthis.sysSeries.addData(v);\r\n\t\t}\r\n\t\tpublic Series<Float> getWaSeries() {\r\n\t\t\treturn waSeries;\r\n\t\t}\r\n\t\tpublic void addWaSeries(Float v) {\r\n\t\t\tthis.waSeries.addData(v);\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * Highchars Series\r\n\t * @param <T> \r\n\t */\r\n\tpublic static class Series<T>{\r\n\t\tprivate String name;\r\n\t\tprivate List<T> data = new ArrayList<T>();\r\n\t\tprivate String type = \"spline\";\r\n\t\tprivate int yAxis;\r\n\t\tpublic String toJson() {\r\n\t\t\treturn JSON.toJSONString(this);\r\n\t\t}\r\n\t\tpublic Series(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\tpublic int getYAxis() {\r\n\t\t\treturn yAxis;\r\n\t\t}\r\n\t\tpublic void setYAxis(int yAxis) {\r\n\t\t\tthis.yAxis = yAxis;\r\n\t\t}\r\n\t\tpublic void setType(String type) {\r\n\t\t\tthis.type = type;\r\n\t\t}\r\n\t\tpublic String getType() {\r\n\t\t\treturn type;\r\n\t\t}\r\n\t\tpublic String getName() {\r\n\t\t\treturn name;\r\n\t\t}\r\n\t\tpublic void setName(String name) {\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t\tpublic void addData(T d) {\r\n\t\t\tdata.add(d);\r\n\t\t}\r\n\t\tpublic List<T> getData() {\r\n\t\t\treturn data;\r\n\t\t}\r\n\t\t@Override\r\n\t\tpublic String toString() {\r\n\t\t\treturn \"Serie [name=\" + name + \", data=\" + data + \", type=\" + type\r\n\t\t\t\t\t+ \", yAxis=\" + yAxis + \"]\";\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/TaskController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport com.sohu.cache.constant.OperateResult;\r\nimport com.sohu.cache.entity.AppDesc;\r\nimport com.sohu.cache.redis.AssistRedisService;\r\nimport com.sohu.cache.task.TaskService;\r\nimport com.sohu.cache.task.entity.TaskQueue;\r\nimport com.sohu.cache.task.entity.TaskSearch;\r\nimport com.sohu.cache.task.entity.TaskStepFlow;\r\nimport com.sohu.cache.task.entity.TaskStepMeta;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.util.Page;\r\nimport org.apache.commons.collections.MapUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.util.ArrayList;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 任务管理\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/task\")\r\npublic class TaskController extends BaseController {\r\n\r\n    @Autowired\r\n    private TaskService taskService;\r\n\r\n    @Autowired\r\n    private AssistRedisService assistRedisService;\r\n\r\n    @RequestMapping(value = \"/list\")\r\n    public ModelAndView taskQueueList(HttpServletRequest request, HttpServletResponse response, Model model,\r\n            TaskSearch taskSearch) {\r\n        List<TaskQueue> taskQueueList = null;\r\n\r\n        Long searchTaskId = NumberUtils.toLong(request.getParameter(\"searchTaskId\"));\r\n        int pageNo = NumberUtils.toInt(request.getParameter(\"pageNo\"), 1);\r\n        int pageSize = NumberUtils.toInt(request.getParameter(\"pageSize\"), 30);\r\n        if (searchTaskId != null && searchTaskId > 0) {\r\n            taskQueueList = taskService.getTaskQueueTreeByTaskId(searchTaskId);\r\n            Page page = new Page(pageNo, pageSize, taskQueueList.size());\r\n            model.addAttribute(\"page\", page);\r\n            taskSearch.setPage(page);\r\n        } else {\r\n            // 分页相关:count\r\n            int totalCount = taskService.getTaskQueueCount(taskSearch);\r\n            Page page = new Page(pageNo, pageSize, totalCount);\r\n            model.addAttribute(\"page\", page);\r\n            // 分页相关:list\r\n            taskSearch.setPage(page);\r\n            taskQueueList = taskService.getTaskQueueList(taskSearch);\r\n        }\r\n        //填充任务流\r\n        for (TaskQueue taskQueue : taskQueueList) {\r\n            List<TaskStepFlow> taskStepFlowList = taskService.getTaskStepFlowList(taskQueue.getId());\r\n            taskQueue.setTaskStepFlowList(taskStepFlowList);\r\n        }\r\n        model.addAttribute(\"searchTaskId\", searchTaskId);\r\n        model.addAttribute(\"taskQueueList\", taskQueueList);\r\n        model.addAttribute(\"taskActive\", SuccessEnum.SUCCESS.value());\r\n        return new ModelAndView(\"manage/task/queueList\");\r\n    }\r\n\r\n    @RequestMapping(value = \"/execute\")\r\n    public ModelAndView execute(HttpServletRequest request,\r\n            HttpServletResponse response, Model model) {\r\n        final long taskId = NumberUtils.toLong(request.getParameter(\"taskId\"));\r\n        TaskQueue taskQueue = taskService.getTaskQueueById(taskId);\r\n        //TODO\r\n        if (taskQueue != null) {\r\n            new Thread(new Runnable() {\r\n                @Override\r\n                public void run() {\r\n                    taskService.executeTask(taskId);\r\n                }\r\n            }).start();\r\n        }\r\n        return new ModelAndView(\"redirect:/manage/task/flow?taskId=\" + taskId);\r\n    }\r\n\r\n    @RequestMapping(value = \"/changeParam\")\r\n    public ModelAndView changeParam(HttpServletRequest request,\r\n            HttpServletResponse response, Model model) {\r\n        long taskId = NumberUtils.toLong(request.getParameter(\"taskId\"));\r\n        String prettyParam = request.getParameter(\"prettyParamText\");\r\n        JSONObject jsonObject = JSONObject.parseObject(prettyParam);\r\n        String param = jsonObject.toJSONString();\r\n        OperateResult operateResult = taskService.updateParam(taskId, param);\r\n\r\n        model.addAttribute(\"result\", operateResult.isSuccess() ? 1 : 0);\r\n        model.addAttribute(\"message\", operateResult.getMessage());\r\n\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    @RequestMapping(value = \"/changeTaskFlowStatus\")\r\n    public ModelAndView changeTaskFlowStatus(HttpServletRequest request,\r\n            HttpServletResponse response, Model model) {\r\n        long taskFlowId = NumberUtils.toLong(request.getParameter(\"taskFlowId\"));\r\n        int status = NumberUtils.toInt(request.getParameter(\"status\"));\r\n        OperateResult operateResult = taskService.updateTaskFlowStatus(taskFlowId, status);\r\n\r\n        model.addAttribute(\"result\", operateResult.isSuccess() ? 1 : 0);\r\n        model.addAttribute(\"message\", operateResult.getMessage());\r\n\r\n        return new ModelAndView(\"\");\r\n    }\r\n\r\n    @RequestMapping(value = \"/flow\")\r\n    public ModelAndView taskFlowList(HttpServletRequest request,\r\n            HttpServletResponse response, Model model) {\r\n        long taskId = NumberUtils.toLong(request.getParameter(\"taskId\"));\r\n\r\n        //任务\r\n        TaskQueue taskQueue = taskService.getTaskQueueById(taskId);\r\n        model.addAttribute(\"taskQueue\", taskQueue);\r\n\r\n        long appId = taskQueue.getAppId();\r\n        AppDesc appDesc = appService.getByAppId(appId);\r\n        model.addAttribute(\"appDesc\", appDesc);\r\n\r\n        //任务流描述\r\n        String className = taskQueue.getClassName();\r\n        List<TaskStepMeta> taskStepMetaList = taskService.getTaskStepMetaList(className);\r\n\r\n        //任务流列表\r\n        List<TaskStepFlow> taskStepFlowList = taskService.getTaskStepFlowList(taskId);\r\n        model.addAttribute(\"taskStepFlowList\", taskStepFlowList);\r\n\r\n        List<String> logList = new ArrayList<String>();\r\n        for (TaskStepFlow taskStepFlow : taskStepFlowList) {\r\n            String line = String\r\n                    .format(\"==========================%s %d======================\", taskStepFlow.getStepName(),\r\n                            taskStepFlow.getOrderNo());\r\n            logList.add(line);\r\n            String taskFlowIdKey = ConstUtils.getTaskFlowRedisKey(String.valueOf(taskStepFlow.getId()));\r\n            List<String> tempList = assistRedisService.lrange(taskFlowIdKey, 0, -1);\r\n            logList.addAll(tempList);\r\n        }\r\n        model.addAttribute(\"logList\", logList);\r\n\r\n        Map<String, List<String>> stepLogListMap = new HashMap<String, List<String>>();\r\n        for (TaskStepFlow taskStepFlow : taskStepFlowList) {\r\n            String stepName = taskStepFlow.getStepName();\r\n            String taskFlowIdKey = ConstUtils.getTaskFlowRedisKey(String.valueOf(taskStepFlow.getId()));\r\n             List<String> tempList = assistRedisService.lrange(taskFlowIdKey, 0, -1);\r\n            stepLogListMap.put(stepName, tempList);\r\n        }\r\n        model.addAttribute(\"stepLogListMap\", stepLogListMap);\r\n\r\n        //task Progress\r\n        taskQueue.setTaskStepFlowList(taskStepFlowList);\r\n\r\n        //获取当前执行步骤\r\n        TaskStepFlow currentTaskStepFlow = taskService.getCurrentTaskStepFlow(taskId);\r\n        model.addAttribute(\"currentTaskStepFlow\", currentTaskStepFlow);\r\n\r\n        //填充taskStepMeta\r\n        fillTaskStepMeta(taskStepFlowList, taskStepMetaList, currentTaskStepFlow);\r\n\r\n        model.addAttribute(\"taskActive\", SuccessEnum.SUCCESS.value());\r\n        return new ModelAndView(\"manage/task/flowList\");\r\n    }\r\n\r\n    /**\r\n     * 填充任务流描述\r\n     *\r\n     * @param taskStepFlowList\r\n     * @param taskStepMetaList\r\n     */\r\n    private void fillTaskStepMeta(List<TaskStepFlow> taskStepFlowList, List<TaskStepMeta> taskStepMetaList,\r\n            TaskStepFlow currentTaskStepFlow) {\r\n        //生成Map<class-step,TaskStepMeta>\r\n        Map<String, TaskStepMeta> classStepTaskStepMetaMap = new HashMap<String, TaskStepMeta>();\r\n        for (TaskStepMeta taskStepMeta : taskStepMetaList) {\r\n            String key = generateClassStepKey(taskStepMeta.getClassName(), taskStepMeta.getStepName());\r\n            classStepTaskStepMetaMap.put(key, taskStepMeta);\r\n        }\r\n\r\n        //遍历TaskStepFlow\r\n        for (TaskStepFlow taskStepFlow : taskStepFlowList) {\r\n            String key = generateClassStepKey(taskStepFlow.getClassName(), taskStepFlow.getStepName());\r\n            TaskStepMeta taskStepMeta = (TaskStepMeta) MapUtils\r\n                    .getObject(classStepTaskStepMetaMap, key, new TaskStepMeta());\r\n            taskStepFlow.setTaskStepMeta(taskStepMeta);\r\n        }\r\n\r\n        //当前TaskStepFlow\r\n        if (currentTaskStepFlow != null) {\r\n            String currentKey = generateClassStepKey(currentTaskStepFlow.getClassName(),\r\n                    currentTaskStepFlow.getStepName());\r\n            TaskStepMeta currentTaskStepMeta = (TaskStepMeta) MapUtils\r\n                    .getObject(classStepTaskStepMetaMap, currentKey, new TaskStepMeta());\r\n            currentTaskStepFlow.setTaskStepMeta(currentTaskStepMeta);\r\n        }\r\n    }\r\n\r\n    private String generateClassStepKey(String className, String stepName) {\r\n        return className + \"-\" + stepName;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/TotalManageController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.sohu.cache.constant.MachineInfoEnum;\r\nimport com.sohu.cache.dao.QuartzDao;\r\nimport com.sohu.cache.dao.TaskQueueDao;\r\nimport com.sohu.cache.entity.*;\r\nimport com.sohu.cache.machine.MachineCenter;\r\nimport com.sohu.cache.stats.app.AppStatsCenter;\r\nimport com.sohu.cache.task.constant.ResourceEnum;\r\nimport com.sohu.cache.task.constant.TaskQueueEnum.TaskStatusEnum;\r\nimport com.sohu.cache.web.enums.StatEnum;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.enums.TriggerStateEnum;\r\nimport com.sohu.cache.web.service.ResourceService;\r\nimport com.sohu.cache.web.util.Page;\r\nimport com.sohu.cache.web.vo.AppDetailVO;\r\nimport com.sohu.cache.web.vo.MachineStatsVo;\r\nimport org.apache.commons.collections.MapUtils;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 全局统计\r\n *\r\n * @author leifu\r\n * @Time 2014年10月14日\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/total\")\r\npublic class TotalManageController extends BaseController {\r\n\r\n    @Resource(name = \"appStatsCenter\")\r\n    private AppStatsCenter appStatsCenter;\r\n\r\n    @Resource\r\n    private MachineCenter machineCenter;\r\n\r\n    @Autowired\r\n    private ResourceService resourceService;\r\n\r\n    @Autowired\r\n    private TaskQueueDao taskQueueDao;\r\n\r\n    @Autowired\r\n    private QuartzDao quartzDao;\r\n\r\n    /**\r\n     * 应用运维\r\n     */\r\n    @RequestMapping(value = \"/list\")\r\n    public ModelAndView doTotalList(HttpServletRequest request,\r\n                                    HttpServletResponse response, String appParam, Model model, AppSearch appSearch) {\r\n\r\n        AppUser currentUser = getUserInfo(request);\r\n        // 获取所有有效版本信息\r\n        List<SystemResource> redisVersionList = resourceService.getResourceList(ResourceEnum.REDIS.getValue());\r\n        //appParam 判断是查询appid还是应用名称\r\n        if (!StringUtils.isEmpty(appParam)) {\r\n            if (StringUtils.isNumeric(appParam)) {\r\n                appSearch.setAppId(Long.parseLong(appParam));\r\n            } else {\r\n                appSearch.setAppName(appParam);\r\n            }\r\n        }\r\n        //分页\r\n        int totalCount = appService.getAppDescCount(currentUser, appSearch);\r\n        int pageNo = NumberUtils.toInt(request.getParameter(\"pageNo\"), 1);\r\n        int pageSize = NumberUtils.toInt(request.getParameter(\"pageSize\"), 20);\r\n        Page page = new Page(pageNo, pageSize, totalCount);\r\n        appSearch.setPage(page);\r\n\r\n        List<AppDesc> apps = appService.getAppDescList(currentUser, appSearch);\r\n        List<AppDetailVO> appDetailList = new ArrayList<AppDetailVO>();\r\n\r\n        if (apps != null && apps.size() > 0) {\r\n            for (AppDesc appDesc : apps) {\r\n                AppDetailVO appDetail = appStatsCenter.getAppDetail(appDesc.getAppId());\r\n                appDetail.getAppDesc().setBackupType(appDesc.getBackupType());\r\n                appDetailList.add(appDetail);\r\n            }\r\n        } else {\r\n            //如果没有查询结果\r\n            page.setTotalCount(0);\r\n        }\r\n        model.addAttribute(\"apps\", apps);\r\n        model.addAttribute(\"appDetailList\", appDetailList);\r\n        model.addAttribute(\"list\", apps);\r\n        model.addAttribute(\"appOperateActive\", SuccessEnum.SUCCESS.value());\r\n        model.addAttribute(\"appParam\", appParam);\r\n        model.addAttribute(\"page\", page);\r\n        model.addAttribute(\"persistenceType\", appSearch.getPersistenceType());\r\n        model.addAttribute(\"backupType\", appSearch.getBackupType());\r\n        model.addAttribute(\"redisVersionList\", redisVersionList);\r\n\r\n        return new ModelAndView(\"manage/total/list\");\r\n    }\r\n\r\n    /**\r\n     * 全局统计\r\n     */\r\n    @RequestMapping(value = \"/statlist\")\r\n    public ModelAndView doStatList(HttpServletRequest request,\r\n                                   HttpServletResponse response, Model model) {\r\n\r\n        // 一.应用及内存统计\r\n        Map<String, Object> appTotalStat = appStatsCenter.getAppTotalStat();\r\n        model.addAttribute(\"totalRunningApps\", MapUtils.getString(appTotalStat, StatEnum.TOTAL_EFFETIVE_APP.value(), \"0\"));\r\n        model.addAttribute(\"totalMachineCount\", MapUtils.getString(appTotalStat, StatEnum.TOTAL_MACHINE_NUM.value(), \"0\"));\r\n        model.addAttribute(\"totalRunningInstance\", MapUtils.getString(appTotalStat, StatEnum.TOTAL_INSTANCE_NUM.value(), \"0\"));\r\n        model.addAttribute(\"redisTypeCount\", MapUtils.getString(appTotalStat, StatEnum.REDIS_VERSION_COUNT.value()));\r\n        model.addAttribute(\"redisDistributeList\", MapUtils.getString(appTotalStat, StatEnum.REDIS_VERSION_DISTRIBUTE.value()));\r\n        model.addAttribute(\"machineMemoryDistributeList\", MapUtils.getString(appTotalStat, StatEnum.MACHINE_USEDMEMORY_DISTRIBUTE.value()));\r\n        model.addAttribute(\"maxMemoryDistributeList\", MapUtils.getString(appTotalStat, StatEnum.MACHINE_MAXMEMORY_DISTRIBUTE.value()));\r\n        model.addAttribute(\"roomDistributeList\", MapUtils.getString(appTotalStat, StatEnum.MACHIEN_ROOM_DISTRIBUTE.value()));\r\n        List<MachineStatsVo> machineStatsVoList = machineCenter.getmachineStatsVoList();\r\n        int redisToolNum = machineCenter.getMachineNum(MachineInfoEnum.TypeEnum.REDIS_MIGRATE_TOOL.getType());\r\n        model.addAttribute(\"machineStatsVoList\", machineStatsVoList);\r\n        model.addAttribute(\"machineRedisCount\", MapUtils.getInteger(appTotalStat, StatEnum.TOTAL_MACHINE_NUM.value(), 0)-redisToolNum);\r\n        model.addAttribute(\"machineRedisToolCount\", redisToolNum);\r\n\r\n        //二、quartz相关\r\n        int triggerWaitingCount = quartzDao.getTriggerStateCount(TriggerStateEnum.WAITING.getState());\r\n        int triggerErrorCount = quartzDao.getTriggerStateCount(TriggerStateEnum.ERROR.getState());\r\n        int triggerPausedCount = quartzDao.getTriggerStateCount(TriggerStateEnum.PAUSED.getState());\r\n        int triggerAcquiredCount = quartzDao.getTriggerStateCount(TriggerStateEnum.ACQUIRED.getState());\r\n        int triggerBlockedCount = quartzDao.getTriggerStateCount(TriggerStateEnum.BLOCKED.getState());\r\n        int misfireCount = quartzDao.getMisFireTriggerCount();\r\n        int triggerTotalCount = triggerWaitingCount + triggerErrorCount + triggerPausedCount + triggerAcquiredCount + triggerBlockedCount;\r\n\r\n        model.addAttribute(\"triggerWaitingCount\", triggerWaitingCount);\r\n        model.addAttribute(\"triggerErrorCount\", triggerErrorCount);\r\n        model.addAttribute(\"triggerPausedCount\", triggerPausedCount);\r\n        model.addAttribute(\"triggerAcquiredCount\", triggerAcquiredCount);\r\n        model.addAttribute(\"triggerBlockedCount\", triggerBlockedCount);\r\n        model.addAttribute(\"misfireCount\", misfireCount);\r\n        model.addAttribute(\"triggerTotalCount\", triggerTotalCount);\r\n\r\n        //三、任务相关\r\n        int newTaskCount = taskQueueDao.getStatusCount(TaskStatusEnum.NEW.getStatus());\r\n        int runningTaskCount = taskQueueDao.getStatusCount(TaskStatusEnum.RUNNING.getStatus());\r\n        int abortTaskCount = taskQueueDao.getStatusCount(TaskStatusEnum.ABORT.getStatus());\r\n        int successTaskCount = taskQueueDao.getStatusCount(TaskStatusEnum.SUCCESS.getStatus());\r\n        int totalTaskCount = newTaskCount + runningTaskCount + abortTaskCount + successTaskCount;\r\n        model.addAttribute(\"newTaskCount\", newTaskCount);\r\n        model.addAttribute(\"runningTaskCount\", runningTaskCount);\r\n        model.addAttribute(\"abortTaskCount\", abortTaskCount);\r\n        model.addAttribute(\"successTaskCount\", successTaskCount);\r\n        model.addAttribute(\"totalTaskCount\", totalTaskCount);\r\n\r\n        model.addAttribute(\"totalActive\", SuccessEnum.SUCCESS.value());\r\n        return new ModelAndView(\"manage/totalstat/list\");\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/TriggerController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.sohu.cache.util.ObjectConvert;\r\nimport org.quartz.Scheduler;\r\nimport org.quartz.SchedulerException;\r\nimport org.quartz.TriggerKey;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.beans.factory.annotation.Qualifier;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.util.Assert;\r\nimport org.springframework.web.bind.annotation.PathVariable;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\n\r\n/**\r\n * trigger操作\r\n * @author leifu\r\n * @Date 2014年05月19日\r\n * @Time 下午5:15:36\r\n */\r\n@Controller\r\n@RequestMapping(value = \"/cache/triggers\")\r\npublic class TriggerController {\r\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\r\n\r\n    @Autowired(required = false)\r\n    @Qualifier(\"clusterScheduler\")\r\n    private Scheduler scheduler;\r\n\r\n    @RequestMapping(value = \"/pause/{appId}/{type}/{host}/{port}\")\r\n    public void pauseTrigger(@PathVariable long appId, @PathVariable int type, @PathVariable String host, @PathVariable int port) {\r\n        Assert.isTrue(appId > 0);\r\n        Assert.isTrue(type > 0);\r\n        Assert.hasText(host);\r\n        Assert.isTrue(port > 0);\r\n\r\n        String triggerName = ObjectConvert.linkIpAndPort(host, port);\r\n        String triggerGroup = \"\";\r\n\r\n        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);\r\n        try {\r\n            scheduler.pauseTrigger(triggerKey);\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.info(\"trigger with name: {}, group: {} is paused\", port, host);\r\n    }\r\n\r\n    @RequestMapping(value = \"/resume/{appId}/{type}/{host}/{port}\")\r\n    public void resumeTrigger(@PathVariable long appId, @PathVariable int type, @PathVariable String host, @PathVariable int port) {\r\n        Assert.isTrue(appId > 0);\r\n        Assert.isTrue(type > 0);\r\n        Assert.hasText(host);\r\n        Assert.isTrue(port > 0);\r\n\r\n        String triggerName = ObjectConvert.linkIpAndPort(host, port);\r\n        String triggerGroup = \"\";\r\n\r\n        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);\r\n        try {\r\n            scheduler.resumeTrigger(triggerKey);\r\n        } catch (SchedulerException e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        logger.info(\"trigger with name: {}, group: {} is resumed\", port, host);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/UserController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\nimport com.sohu.cache.entity.AppBiz;\r\nimport com.sohu.cache.utils.EnvCustomUtil;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\nimport com.sohu.cache.constant.AppAuditType;\r\nimport com.sohu.cache.entity.AppAudit;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.util.AppEmailUtil;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 注册用户管理(页面没有权限限制)\r\n * \r\n * @author leifu\r\n * @Date 2014年10月28日\r\n * @Time 上午10:49:32\r\n */\r\n@Controller\r\n@RequestMapping(\"/user\")\r\npublic class UserController extends BaseController{\r\n    \r\n    @Resource(name = \"appEmailUtil\")\r\n    private AppEmailUtil appEmailUtil;\r\n\r\n    /**\r\n     * 注册用户页面\r\n     */\r\n    @RequestMapping(value = \"/register\")\r\n    public ModelAndView userRegister(HttpServletRequest request,\r\n            HttpServletResponse response, Model model, Integer success) {\r\n        List<AppBiz> bizList = userService.getBizList();\r\n        model.addAttribute(\"success\", success);\r\n        model.addAttribute(\"bizList\", bizList);\r\n        model.addAttribute(\"pwdswitch\", EnvCustomUtil.pwdswitch);\r\n        return new ModelAndView(\"user/userRegister\");\r\n    }\r\n    \r\n    /**\r\n     * 注册用户申请\r\n     */\r\n    @RequestMapping(value = \"/apply\")\r\n    public ModelAndView doAddUser(HttpServletRequest request,\r\n            HttpServletResponse response, Model model, String name, String chName, String email, String mobile, String weChat,\r\n            Integer type, Long userId, Integer isAlert, String password, String company, String purpose, Long bizId) {\r\n        SuccessEnum success = SuccessEnum.SUCCESS;\r\n        try {\r\n            //保存用户(type=-1为无效用户,需要审批)\r\n            AppUser appUser = AppUser.buildFrom(userId, name, chName, email, mobile, weChat, type, isAlert, password, company, purpose, bizId);\r\n            userService.save(appUser);\r\n            //提交审批\r\n            AppAudit appAudit = appService.saveRegisterUserApply(appUser,AppAuditType.REGISTER_USER_APPLY);\r\n            appEmailUtil.noticeUserResult(appUser, appAudit);\r\n        } catch (Exception e) {\r\n            success = SuccessEnum.FAIL;\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        return new ModelAndView(\"redirect:/user/register?success=\" + success.value());\r\n    }\r\n    \r\n    \r\n    @RequestMapping(value = \"/checkUserNameExist\")\r\n    public ModelAndView doCheckUserNameExist(HttpServletRequest request,\r\n            HttpServletResponse response, Model model, String userName) {\r\n        AppUser appUser = userService.getByName(userName);\r\n        if (appUser != null) {\r\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\r\n        } else {\r\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\r\n        }\r\n        return null;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/UserManageController.java",
    "content": "package com.sohu.cache.web.controller;\r\n\r\nimport com.sohu.cache.constant.AppCheckEnum;\r\nimport com.sohu.cache.constant.AppUserTypeEnum;\r\nimport com.sohu.cache.entity.AppAudit;\r\nimport com.sohu.cache.entity.AppBiz;\r\nimport com.sohu.cache.entity.AppUser;\r\nimport com.sohu.cache.util.ConstUtils;\r\nimport com.sohu.cache.utils.EnvCustomUtil;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\nimport com.sohu.cache.web.util.AppEmailUtil;\r\nimport com.sohu.cache.web.vo.AppUserVo;\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.apache.commons.lang.math.NumberUtils;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.ui.Model;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.servlet.ModelAndView;\r\n\r\nimport javax.annotation.Resource;\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport java.util.List;\r\n\r\n/**\r\n * 用户信息管理\r\n *\r\n * @author leifu\r\n * @Time 2014年6月6日\r\n */\r\n@Controller\r\n@RequestMapping(\"manage/user\")\r\npublic class UserManageController extends BaseController {\r\n\r\n    @Resource(name = \"appEmailUtil\")\r\n    private AppEmailUtil appEmailUtil;\r\n\r\n    /**\r\n     * 用户初始化\r\n     *\r\n     * @param id 用户id\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/init\")\r\n    public ModelAndView doUserInit(HttpServletRequest request,\r\n                                   HttpServletResponse response, Model model, Long id) {\r\n        if (id != null) {\r\n            AppUser user = userService.get(id);\r\n            model.addAttribute(\"user\", user);\r\n            model.addAttribute(\"modify\", true);\r\n        }\r\n        return new ModelAndView(\"manage/user/initUser\");\r\n    }\r\n\r\n    /**\r\n     * 更新用户\r\n     *\r\n     * @param name\r\n     * @param chName\r\n     * @param email\r\n     * @param mobile\r\n     * @param weChat\r\n     * @param type\r\n     * @param userId\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/add\")\r\n    public ModelAndView doAddUser(HttpServletRequest request,\r\n                                  HttpServletResponse response, Model model, String name, String chName, String email, String mobile, String weChat,\r\n                                  Integer type, Long userId, Integer isAlert, String company, String purpose, Long bizId) {\r\n        // 后台暂时不对参数进行验证\r\n        AppUser appUser = AppUser.buildFrom(userId, name, chName, email, mobile, weChat, type, isAlert, company, purpose, bizId);\r\n        try {\r\n            if (userId == null) {\r\n                appUser.setPassword(ConstUtils.DEFAULT_USER_PASSWORD);\r\n                userService.save(appUser);\r\n            } else {\r\n                userService.update(appUser);\r\n            }\r\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\r\n        } catch (Exception e) {\r\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * 删除用户\r\n     *\r\n     * @param userId\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/delete\")\r\n    public ModelAndView doDeleteUser(HttpServletRequest request,\r\n                                     HttpServletResponse response, Model model, Long userId) {\r\n        userService.delete(userId);\r\n        return new ModelAndView(\"redirect:/manage/user/list\");\r\n    }\r\n\r\n    /**\r\n     * 重置密码\r\n     *\r\n     * @param userId\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/resetPwd\")\r\n    public ModelAndView doResetUserPwd(HttpServletRequest request,\r\n                                     HttpServletResponse response, Model model, Long userId) {\r\n        userService.resetPwd(userId);\r\n        return new ModelAndView(\"redirect:/manage/user/list\");\r\n    }\r\n\r\n    /**\r\n     * 修改密码\r\n     *\r\n     * @param userId\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/updatePwd\")\r\n    public ModelAndView doResetUserPwd(HttpServletRequest request,\r\n                                       HttpServletResponse response, Model model, Long userId, String password) {\r\n        SuccessEnum successEnum = userService.updatePwd(userId, password);\r\n        if(successEnum.equals(SuccessEnum.SUCCESS)){\r\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\r\n        }else{\r\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\r\n        }\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * 用户列表\r\n     *\r\n     * @param  searchChName 中文名\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/list\")\r\n    public ModelAndView doUserList(HttpServletRequest request,\r\n                                   HttpServletResponse response, Model model, String searchChName, String searchBizName) {\r\n        //获取tab\r\n        int tabId = NumberUtils.toInt(request.getParameter(\"tabId\"), 1);\r\n        model.addAttribute(\"tabId\", tabId);\r\n        if(tabId == 1){\r\n            List<AppUserVo> users = userService.getUserWithBizList(searchChName, searchBizName);\r\n            model.addAttribute(\"users\", users);\r\n            model.addAttribute(\"searchChName\", searchChName);\r\n            model.addAttribute(\"searchBizName\", searchBizName);\r\n        }\r\n\r\n        List<AppBiz> bizList = userService.getBizList();\r\n        model.addAttribute(\"bizList\", bizList);\r\n        if(tabId == 2){\r\n        }\r\n        model.addAttribute(\"userActive\", SuccessEnum.SUCCESS.value());\r\n        model.addAttribute(\"pwdswitch\", EnvCustomUtil.pwdswitch);\r\n        return new ModelAndView(\"manage/user/list\");\r\n    }\r\n\r\n    @RequestMapping(value = \"/addAuditStatus\")\r\n    public ModelAndView doAddAuditStatus(HttpServletRequest request,\r\n                                         HttpServletResponse response, Model model, Integer status, Integer type,\r\n                                         Long appAuditId, String refuseReason, Long appId) {\r\n        AppAudit appAudit = appService.getAppAuditById(appAuditId);\r\n        AppUser appUser = userService.get(appAudit.getUserId());\r\n        // 通过或者驳回并记录日志\r\n        appService.updateUserAuditStatus(appAuditId, status, appUser.getId());\r\n\r\n        // 记录驳回原因\r\n        if (AppCheckEnum.APP_REJECT.value().equals(status)) {\r\n            appAudit.setRefuseReason(refuseReason);\r\n            appService.updateRefuseReason(appAudit, getUserInfo(request));\r\n            userService.delete(appUser.getId());\r\n        }\r\n\r\n        // 发邮件统计\r\n        if (AppCheckEnum.APP_PASS.value().equals(status)\r\n                || AppCheckEnum.APP_REJECT.value().equals(status)) {\r\n            appUser.setType(AppUserTypeEnum.REGULAR_USER.value());\r\n            appAudit.setStatus(status);\r\n            userService.update(appUser);\r\n            appEmailUtil.noticeUserResult(appUser, appAudit);\r\n        }\r\n\r\n        // 批准成功直接跳转\r\n        if (AppCheckEnum.APP_PASS.value().equals(status)) {\r\n            return new ModelAndView(\"redirect:/manage/app/auditList\");\r\n        }\r\n\r\n        write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * 更新业务组\r\n     *\r\n     * @param toRemoverUserName\r\n     * @param toChargeUserName\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/takeover\")\r\n    public void doTakeover(HttpServletRequest request,\r\n                                 HttpServletResponse response, String toRemoverUserName, String toChargeUserName) {\r\n        if(StringUtils.isNotBlank(toRemoverUserName) && StringUtils.isNotBlank(toChargeUserName)\r\n            && !toRemoverUserName.equals(toChargeUserName)){\r\n            try {\r\n                AppUser toRemoverUser = userService.getByName(toRemoverUserName);\r\n                AppUser toChargeUser = userService.getByName(toChargeUserName);\r\n                if(toRemoverUser != null && toChargeUser != null && toRemoverUser != toChargeUser){\r\n                    SuccessEnum successEnum = userService.takeoverUser(toRemoverUser, toChargeUser);\r\n                    if(successEnum.equals(SuccessEnum.SUCCESS)){\r\n                        write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\r\n                        return;\r\n                    }\r\n                }\r\n            } catch (Exception e) {\r\n                logger.error(e.getMessage(), e);\r\n            }\r\n        }\r\n        write(response, String.valueOf(SuccessEnum.FAIL.value()));\r\n    }\r\n\r\n    /**\r\n     * 根据id删除业务组\r\n     *\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/biz/delete\")\r\n    public ModelAndView doUserBizList(HttpServletRequest request,\r\n                                   HttpServletResponse response, Model model, Long bizId, Integer tabId) {\r\n        userService.deleteBiz(bizId);\r\n        if(tabId == null){\r\n            tabId = 1;\r\n        }\r\n        return new ModelAndView(\"redirect:/manage/user/list?tabId=\" + tabId);\r\n    }\r\n\r\n    /**\r\n     * 更新业务组\r\n     *\r\n     * @param name\r\n     * @param bizDesc\r\n     * @return\r\n     */\r\n    @RequestMapping(value = \"/biz/add\")\r\n    public ModelAndView doAddBiz(HttpServletRequest request,\r\n                                  HttpServletResponse response, Model model, String name, String bizDesc, Long bizId) {\r\n        // 后台暂时不对参数进行验证\r\n        AppBiz appBiz = AppBiz.of(name, bizDesc);\r\n        try {\r\n            if (bizId == null) {\r\n                userService.saveBiz(appBiz);\r\n            } else {\r\n                appBiz.setId(bizId);\r\n                userService.updateBiz(appBiz);\r\n            }\r\n            write(response, String.valueOf(SuccessEnum.SUCCESS.value()));\r\n        } catch (Exception e) {\r\n            write(response, String.valueOf(SuccessEnum.FAIL.value()));\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        return null;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/WebResourceController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.sohu.cache.util.ConstUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n * 前端页面相关基础资源\n *\n * @author zengyizhao\n * @Time 2023年7月3日\n */\n@Controller\n@RequestMapping(\"/web/resource\")\npublic class WebResourceController extends BaseController {\n    private Logger logger = LoggerFactory.getLogger(WebResourceController.class);\n\n\n    /**\n     * 初始化贡献者页面\n     *\n     * @return\n     */\n    @RequestMapping(\"/{path}/{filename}\")\n    public ModelAndView doInitBecomeContributor(@PathVariable String path, @PathVariable String filename,\n                                                HttpServletRequest request,\n                                                HttpServletResponse response, Model model) {\n        if((\"inc\".equals(path) && \"daily\".equals(filename)) || (\"inc\".equals(path) && \"contact\".equals(filename))){\n            model.addAttribute(\"contact\", ConstUtils.CONTACT);\n        }\n        return new ModelAndView(path + \"/\" + filename);\n    }\n\n    /**\n     * 初始化贡献者页面\n     *\n     * @return\n     */\n    @RequestMapping(\"/noPower\")\n    public ModelAndView redirectNoPower(@RequestParam(\"appId\") String appId,\n                                                Model model,\n                                                HttpServletRequest request,\n                                                HttpServletResponse response) {\n        model.addAttribute(\"appId\", appId);\n        return new ModelAndView(\"noPower\");\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/controller/WikiController.java",
    "content": "package com.sohu.cache.web.controller;\n\nimport com.vladsch.flexmark.ext.tables.TablesExtension;\nimport com.vladsch.flexmark.html.HtmlRenderer;\nimport com.vladsch.flexmark.parser.Parser;\nimport com.vladsch.flexmark.parser.ParserEmulationProfile;\nimport com.vladsch.flexmark.util.ast.Document;\nimport com.vladsch.flexmark.util.builder.Extension;\nimport com.vladsch.flexmark.util.options.MutableDataSet;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.util.Arrays;\nimport java.util.Map;\n\n@Controller\n@RequestMapping(\"/wiki\")\npublic class WikiController extends BaseController {\n\n    @RequestMapping(\"/{path}/{filename}\")\n    public String subPages(@PathVariable String path, @PathVariable String filename,\n                           @RequestParam(required = false) String entry,\n                           Map<String, Object> map) throws Exception {\n        String html = markdown2html(path + \"/\" + filename, \".md\");\n        //html = html.replace(\"${version}\", \"1.0.0\");\n        // toc\n        String toc = markdown2html(path + \"/\" + filename, \".toc.md\");\n        if (toc != null) {\n            map.put(\"toc\", toc);\n        }\n        map.put(\"response\", html);\n        if (entry != null && entry.equals(\"client\") && \"client\".equals(filename)) {\n            return \"wikiAccessClientTemplate\";\n        }\n\n        return \"wikiTemplate\";\n    }\n\n    public String markdown2html(String filename, String suffix) throws Exception {\n        String templatePath = \"static/wiki/\" + filename + suffix;\n        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(templatePath);\n        if (inputStream == null) {\n            return null;\n        }\n        String markdown = new String(read(inputStream), Charset.forName(\"utf-8\"));\n        MutableDataSet options = new MutableDataSet();\n        options.setFrom(ParserEmulationProfile.MARKDOWN);\n        options.set(Parser.EXTENSIONS, Arrays.asList(new Extension[]{TablesExtension.create()}));\n        Document document = Parser.builder(options).build().parse(markdown);\n        String html = HtmlRenderer.builder(options).build().render(document);\n        return html;\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/AdminEnum.java",
    "content": "package com.sohu.cache.web.enums;\r\n\r\n/**\r\n * 管理员\r\n *\r\n * @author leifu\r\n * @Time 2014年10月16日\r\n */\r\npublic enum AdminEnum {\r\n    IS_ADMIN(1), // 是管理员\r\n    NOT_ADMIN(0); // 不是管理员\r\n\r\n    int value;\r\n\r\n    private AdminEnum(int value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public int value() {\r\n        return value;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/AlertTypeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/3 14:40\n * @Description: 邮件报警类型枚举\n */\npublic enum AlertTypeEnum {\n\n    INSTANCE_RUNNING_STATE_CHANGE(1, \"实例运行状态变更\", 1, 1),\n    INATANCE_EXCEPTION_STATE_MONITOR(2, \"实例状态异常监控\", 1, 1),\n    MACHINE_MEMORY_OVER_PRESET(3, \"机器内存报警\", 1, 2),\n    INSTANCE_MINUTE_MONITOR(4, \"实例分钟报警\", 1, 0),\n    POD_RESTART_SYNC_TASK(5, \"Pod重启机器同步任务报警\", 1, 1),\n    POD_RESTART_INSTANCE_RECOVER(6, \"Pod重启探测Redis实例报警\", 1, 1),\n    MACHINE_MANAGE(7, \"机器管理\", 1, 1),\n    APP_MINUTE_MONITOR(8, \"实例分钟报警\", 1, 0),\n    APP_CLIENT_CONNECTION(9, \"应用客户端连接数报警\", 0, 2),\n    APP_SHARD_CLENT_CONNECTION(10, \"应用实例-分片客户端连接数报警\", 0, 2),\n    APP_HIT_RATIO(11, \"应用平均命中率报警\", 0, 2),\n    APP_MEM_USED_RATIO(12, \"应用内存使用率报警\", 0, 2),\n    APP_SHARD_MEM_USED_RATIO(13, \"分片内存使用率报警\", 0, 2),\n    MACHINE_DISK_OVER_PRESET(14, \"机器磁盘报警\", 1, 2),\n    APP_DISK_USED_RATIO(15, \"应用磁盘使用率报警\", 0, 2),\n    APP_SHARD_DISK_USED_RATIO(16, \"分片磁盘使用率报警\", 0, 2);\n\n    private int type;//邮件类型\n\n    private String info;//邮件类型信息\n\n    private int visibleType;//可见类型（0：均可见；1：仅管理员可见；）\n\n    private int importantLevel;//重要类型（0：一般；1：重要；2：紧急）\n\n    private static Map<Integer, AppTypeEnum> MAP = new HashMap<Integer, AppTypeEnum>();\n\n    static {\n        for (AppTypeEnum appTypeEnum : AppTypeEnum.values()) {\n            MAP.put(appTypeEnum.getType(), appTypeEnum);\n        }\n    }\n\n    AlertTypeEnum(int type, String info, int visibleType, int importantLevel) {\n        this.type = type;\n        this.info = info;\n        this.visibleType = visibleType;\n        this.importantLevel = importantLevel;\n    }\n\n    public static AppTypeEnum getByType(int type) {\n        return MAP.get(type);\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public int getVisibleType() {\n        return visibleType;\n    }\n\n    public int getImportantLevel() {\n        return importantLevel;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/AppImportStatusEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * @Author: rucao\n * @Date: 2021/1/11 下午5:45\n */\npublic enum AppImportStatusEnum {\n    PREPARE(0, \"准备\", \"应用导入-未开始\"),\n    START(1, \"进行中...\", \"应用导入-开始\"),\n    ERROR(2, \"error\", \"应用导入-出错\"),\n\n    VERSION_BUILD_START(11, \"进行中...\", \"新建redis版本-进行中\"),\n    VERSION_BUILD_ERROR(12, \"error\", \"新建redis版本-出错\"),\n    VERSION_BUILD_END(20, \"成功\", \"新建redis版本-完成\"),\n\n    APP_BUILD_INIT(21, \"准备就绪\", \"新建redis应用-准备就绪\"),\n    APP_BUILD_START(22, \"进行中...\", \"新建redis应用-进行中\"),\n    APP_BUILD_ERROR(23, \"error\", \"新建redis应用-出错\"),\n    APP_BUILD_END(30, \"成功\", \"新建redis应用-完成\"),\n\n    MIGRATE_INIT(31, \"准备就绪\", \"数据迁移-准备就绪\"),\n    MIGRATE_START(32, \"进行中...\", \"数据迁移-进行中\"),\n    MIGRATE_ERROR(33, \"error\", \"数据迁移-出错\"),\n    MIGRATE_END(3, \"成功\", \"应用导入-成功\"),\n    ;\n    private int status;\n    private String desc;\n    private String info;\n\n    AppImportStatusEnum(int status, String desc, String info) {\n        this.status = status;\n        this.desc = desc;\n        this.info = info;\n    }\n\n    public int getStatus() {\n        return status;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/AppOrderByEnum.java",
    "content": "package com.sohu.cache.web.enums;\r\n\r\n/**\r\n * app列表排序\r\n * @Date 2014年11月14日\r\n * @Time 上午10:55:47\r\n */\r\npublic enum AppOrderByEnum {\r\n    HIT_PERCENTAGE_HIGH_TO_LOW(\"hit_percentage_high_to_low\"),\r\n    HIT_PERCENTAGE_LOW_TO_HIGH(\"hit_percentage_low_to_high\");\r\n\r\n    private String value;\r\n\r\n    private AppOrderByEnum(String value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public String getValue() {\r\n        return value;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/AppTypeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 应用类型\n */\npublic enum AppTypeEnum {\n    TWEMPROXY(1, \"twemproxy\"),\n    REDIS_CLUSTER(2, \"Redis-Cluster\"),\n    CODIS(3, \"codis\"),\n    PIKA(4, \"pika\"),\n    REDIS_SENTINEL(5, \"Redis-Sentinel\"),\n    REDIS_STANDALONE(6, \"Redis-Standalone\"),\n    MEMCACHED(7, \"memcached\"),\n    PIKA_SENTINEL(8, \"pika-sentinel\");\n\n    private int type;\n\n    private String info;\n\n    private static Map<Integer, AppTypeEnum> MAP = new HashMap<Integer, AppTypeEnum>();\n\n    static {\n        for (AppTypeEnum appTypeEnum : AppTypeEnum.values()) {\n            MAP.put(appTypeEnum.getType(), appTypeEnum);\n        }\n    }\n\n    AppTypeEnum(int type, String info) {\n        this.type = type;\n        this.info = info;\n    }\n\n    public static AppTypeEnum getByType(int type) {\n        return MAP.get(type);\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/BooleanEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by rucao on 2020/1/17\n */\npublic enum BooleanEnum {\n    OTHER(-1),\n    FALSE(0),\n    TRUE(1);\n\n    int value;\n\n    BooleanEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/CheckEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by chenshi on 2021/1/12.\n */\npublic enum CheckEnum {\n\n    //正常\n    CONSISTENCE(1),\n    //异常\n    INCONSISTENCE(2),\n    //\n    EXCEPTION(3);\n\n    private int value;\n\n    CheckEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/ClientTypeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * @Author: rucao\n * @Date: 2020/5/13 5:35 下午\n */\npublic enum ClientTypeEnum {\n    A(\"A\", \" 尽可能快地关闭连接\"),\n    b(\"b\", \" 客户端正在等待阻塞事件\"),\n    c(\"c\", \" 在将回复完整地写出之后，关闭链接\"),\n    d(\"d\", \" 一个受监视(watched)的键已被修改，EXEC命令将失败\"),\n    i(\"i\", \" 客户端正在等待VM I/O操作(已废弃)\"),\n    M(\"M\", \" master客户端\"),\n    N(\"N\", \" 普通客户端\"),\n    O(\"O\", \" 客户端是MONITOR模式下的附属节点(slave)\"),\n    P(\"P\", \" Pub/Sub客户端\"),\n    r(\"r\", \" 客户端是只读模式的集群节点\"),\n    S(\"S\", \" slave客户端\"),\n    u(\"u\", \" 客户端未被阻塞(unblocked)\"),\n    U(\"U\", \" 通过Unix套接字连接的客户端\"),\n    x(\"x\", \" 客户端正在执行事务\"),\n    Method(\"method\", \"method\");\n\n    private final String flags;\n    private final String desc;\n\n    ClientTypeEnum(String flags, String desc) {\n        this.flags = flags;\n        this.desc = desc;\n    }\n\n    public String getFlags() {\n        return flags;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public String getDesc(String flags) {\n        for (ClientTypeEnum e : ClientTypeEnum.values()) {\n            if (e.flags.equals(flags)) {\n                return e.flags + \":\" + e.desc;\n            }\n        }\n        return flags;\n\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/CompareTypeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/27 14:40\n * @Description: 比较类型-redis配置项检测\n */\npublic enum CompareTypeEnum {\n\n    EQUAL(1, \"等于\"),\n    NOT_EQUAL(2, \"不等于\"),\n    LESS_THAN(3, \"小于\"),\n    MORE_THAN(4, \"大于\");\n\n    private int type;//比较类型\n\n    private String info;//比较类型说明\n\n    private static Map<Integer, CompareTypeEnum> MAP = new HashMap<Integer, CompareTypeEnum>();\n\n    static {\n        for (CompareTypeEnum compareTypeEnum : CompareTypeEnum.values()) {\n            MAP.put(compareTypeEnum.getType(), compareTypeEnum);\n        }\n    }\n\n    CompareTypeEnum(int type, String info) {\n        this.type = type;\n        this.info = info;\n    }\n\n    public static CompareTypeEnum getByType(int type) {\n        return MAP.get(type);\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/ConfigRestartOperateEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/11/16 16:10\n * @Description: 配置、重启操作类型\n */\npublic enum ConfigRestartOperateEnum {\n    /**\n     * 操作类型（0:滚动重启，1:修改配置强制重启；2：修改配置）\n     */\n    RESTART(0),\n    CONFIG_RESTART(1),\n    CONFIG(2);\n\n    private int value;\n\n    ConfigRestartOperateEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/DeployInfoEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by rucao on 2018/10/12\n */\npublic enum DeployInfoEnum {\n    SUCCESS(\"success\"),\n    EMPTY(\"empty:error\"),\n    EXCEPTION(\"exception:error\"),\n    PARAM_ERROR(\"param:error\"),\n    MACHINE_NUM_ERROR(\"machine_num:error\");\n\n    private String value;\n\n    DeployInfoEnum(String value) {\n        this.value = value;\n    }\n\n    public String getValue() {\n        return value;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/ExamToolEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by rucao on 2019/1/29\n */\npublic enum ExamToolEnum {\n    EXAM_ALL(0,\"exam_all\"),\n    EXAM_NON_TEST(1,\"exam_non_test\"),\n    EXAM_LIST(2,\"exam_appid_list\"),\n    EXAM_APPID(3,\"exam_appid\");\n\n    private final int value;\n    private final String examTypeStr;\n\n    ExamToolEnum (int value,String examTypeStr){\n        this.value=value;\n        this.examTypeStr=examTypeStr;\n    }\n\n    public int getValue(){\n        return this.value;\n    }\n    public String getExamTypeStr(){\n        return  this.examTypeStr;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/ImportantLevelTypeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/22 13:34\n * @Description: 报警重要程度配置\n */\npublic enum ImportantLevelTypeEnum {\n    NORMAL(0, \"一般\"),\n    IMPORTANT(1, \"重要\"),\n    URGENT(2, \"紧急\");\n\n\n    private final static List<ImportantLevelTypeEnum> instanceAlertTypeEnumList = new ArrayList<ImportantLevelTypeEnum>();\n    static {\n        for (ImportantLevelTypeEnum instanceAlertTypeEnum : ImportantLevelTypeEnum.values()) {\n            instanceAlertTypeEnumList.add(instanceAlertTypeEnum);\n        }\n    }\n\n    private int type;\n\n    private String info;\n\n    private ImportantLevelTypeEnum(int type, String info) {\n        this.type = type;\n        this.info = info;\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public static String getInfoByType(int type){\n        for(ImportantLevelTypeEnum importantLevelTypeEnum: instanceAlertTypeEnumList){\n            if(importantLevelTypeEnum.getType() == type){\n                return importantLevelTypeEnum.getInfo();\n            }\n        }\n        return ImportantLevelTypeEnum.NORMAL.getInfo();\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/LoginEnum.java",
    "content": "package com.sohu.cache.web.enums;\r\n\r\n/**\r\n * 登录状态\r\n * \r\n * @author leifu\r\n * @Time 2014年10月16日\r\n */\r\npublic enum LoginEnum {\r\n    LOGIN_SUCCESS(1), // 成功\r\n    LOGIN_WRONG_USER_OR_PASSWORD(0), // 用户名或者密码错误\r\n    LOGIN_USER_NOT_EXIST(-1), // 不是cachecloud用户\r\n    LOGIN_NOT_ADMIN(-2);// 不是超级管理员\r\n\r\n    int value;\r\n\r\n    private LoginEnum(int value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public int value() {\r\n        return value;\r\n    }\r\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/MachineMemoryDistriEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n\nimport com.sohu.cache.util.NumberUtil;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 机器内存使用或分配区间\n */\npublic enum MachineMemoryDistriEnum {\n    BETWEEN_0_TO_1_PERCENT(\"0_1\", \"0-1%\", 2),\n    BETWEEN_1_TO_10_PERCENT(\"1_10\", \"1-10%\", 3),\n    BETWEEN_10_TO_25_PERCENT(\"10_25\", \"10-25%\", 4),\n    BETWEEN_25_TO_50_PERCENT(\"25_50\", \"25-50%\", 5),\n    BETWEEN_50_TO_75_PERCENT(\"50_75\", \"50-75%\", 6),\n    BETWEEN_75_TO_100_PERCENT(\"75_90\", \"75-90%\", 7),\n    BETWEEN_90_TO_100_PERCENT(\"90_100\", \"90-100%\", 8),\n    BETWEEN_100_TO_1000_PERCENT(\"100_1000\", \"100%以上\", 9);\n\n    public final static Map<String, MachineMemoryDistriEnum> MAP;\n\n    static {\n        Map<String, MachineMemoryDistriEnum> tmpMap = new HashMap<>();\n        for (MachineMemoryDistriEnum enumObject : MachineMemoryDistriEnum.values()) {\n            tmpMap.put(enumObject.getValue(), enumObject);\n        }\n        MAP = Collections.unmodifiableMap(tmpMap);\n    }\n\n    private String value;\n    private String info;\n    private int type;\n\n    private MachineMemoryDistriEnum(String value, String info, int type) {\n        this.value = value;\n        this.info = info;\n        this.type = type;\n    }\n\n    public static MachineMemoryDistriEnum getByValue(String targetValue) {\n        return MAP.get(targetValue);\n    }\n\n    /**\n     * @param percent\n     * @return\n     */\n    public static MachineMemoryDistriEnum getRightPercentDistri(int percent) {\n        MachineMemoryDistriEnum[] enumArr = MachineMemoryDistriEnum.values();\n        for (MachineMemoryDistriEnum enumObject : enumArr) {\n            if (isInSize(enumObject, percent)) {\n                return enumObject;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * @param enumObject\n     * @return\n     */\n    private static boolean isInSize(MachineMemoryDistriEnum enumObject, long costTime) {\n        String value = enumObject.getValue();\n        int index = value.indexOf(\"_\");\n        int start = NumberUtil.toInt(value.substring(0, index));\n        int end = NumberUtil.toInt(value.substring(index + 1));\n        if (costTime >= start && costTime < end) {\n            return true;\n        }\n        return false;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public int getType() {\n        return type;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/MachineTaskEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by chenshi on 2019/5/28.\n */\npublic enum MachineTaskEnum {\n\n    //未同步\n    UNSYNC(0),\n    //已同步\n    SYNCED(1),\n    //同步中\n    SYNCING(-1),\n    //同步失败\n    SYNC_FAILED(-2);\n\n    private int value;\n\n    MachineTaskEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/MasterSlaveExistEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 应用类型\n */\npublic enum MasterSlaveExistEnum {\n    NONE(0, \"no master or slave\"),\n    MASTER(1, \"only master\"),\n    SLAVE(2, \"only slave\"),\n    MASTRE_SLAVE(3, \"both master and slave\");\n\n    private int type;\n\n    private String info;\n\n    private static Map<Integer, MasterSlaveExistEnum> MAP = new HashMap<Integer, MasterSlaveExistEnum>();\n\n    static {\n        for (MasterSlaveExistEnum appTypeEnum : MasterSlaveExistEnum.values()) {\n            MAP.put(appTypeEnum.getType(), appTypeEnum);\n        }\n    }\n\n    MasterSlaveExistEnum(int type, String info) {\n        this.type = type;\n        this.info = info;\n    }\n\n    public static MasterSlaveExistEnum getByType(int type) {\n        return MAP.get(type);\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/NodeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by chenshi on 2019/4/19.\n */\npublic enum NodeEnum {\n\n    REDIS_NODE(1),\n    SENTINEL_NODE(2),\n    TWEMPROXY_NODE(3),\n    PIKA_NODE(4);\n\n    private int value;\n\n    private NodeEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/PodStatusEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by chenshi on 2019/5/21.\n */\npublic enum PodStatusEnum {\n\n    OFFLINE(0),ONLINE(1);\n\n    private int value;\n\n    PodStatusEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/RedisOperateEnum.java",
    "content": "package com.sohu.cache.web.enums;\r\n\r\n/**\r\n * \r\n * @author leifu\r\n * @Date 2016年1月12日\r\n * @Time 下午2:28:25\r\n */\r\npublic enum RedisOperateEnum {\r\n   \r\n    OP_SUCCESS(1),\r\n    ALREADY_SUCCESS(2),\r\n    FAIL(0);\r\n    \r\n    private int value;\r\n\r\n    private RedisOperateEnum(int value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public int getValue() {\r\n        return value;\r\n    }\r\n    \r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/RedisVersionEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * Created by chenshi on 2018/8/23.\n */\npublic enum RedisVersionEnum {\n\n    Redis3_0_7(1),\n//    Redis3_2(2),\n//    Redis4_0(3);\n    Not_bind(0),\n    Is_bind(1),\n    Redis_installed(1),\n    Redis_uninstalled(0),\n    Redis_installException(-1);\n\n\n    private int value;\n\n    private RedisVersionEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/RestartStatusEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/13 16:10\n * @Description:\n */\npublic enum RestartStatusEnum {\n    /**\n     * 状态：0等待，1运行，2成功，3失败，4配置修改待重启\n     */\n    WAITING(0),\n    RUNNING(1),\n    SUCCESS(2),\n    FAIL(3),\n    NEED_RESTART(4),\n    RESTART_AFTER_CONFIG(5),\n    INTERUPT(6);\n\n    private int value;\n\n    RestartStatusEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/SshAuthTypeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * ssh授权方式\n * @author leifu\n * @date 2018年6月15日\n * @time 下午5:56:59\n */\npublic enum SshAuthTypeEnum {\n\t\n\tPASSWORD(1, \"用户密码\"),\n\tPUBLIC_KEY(2, \"公钥\");\n\t\n\tprivate int value;\n\t\n\tprivate String info;\n\n\tprivate SshAuthTypeEnum(int value, String info) {\n\t\tthis.value = value;\n\t\tthis.info = info;\n\t}\n\n\tpublic int getValue() {\n\t\treturn value;\n\t}\n\n\tpublic String getInfo() {\n\t\treturn info;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/StatEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * <p>\n * Description:后台管理总览相关统计参数\n * </p>\n * @author chenshi\n * @version 1.0\n * @date 2017/8/14\n * @param\n * @return\n */\npublic enum StatEnum {\n\n    /**\n     * 基础统计:\n     * 1.在线应用数\n     * 2.在线实例数\n     * 3.在线机器数\n     * 4.Redis版本数量\n     */\n    TOTAL_EFFETIVE_APP(\"effetiveAppCount\"),\n    TOTAL_INSTANCE_NUM(\"totalInstanceCount\"),\n    TOTAL_MACHINE_NUM(\"totalMachineCount\"),\n    TOTAL_PHYSICAL_MACHINE_NUM(\"totalPhysicalMachineCount\"),\n    REDIS_VERSION_COUNT(\"redisTypeCount\"),\n    /**\n     * 分布统计：\n     * 1.Redis版本分布与统计\n     * 2.机器内存统计\n     * 3.实例内存统计\n     * 4.按机房内存统计\n     */\n    REDIS_VERSION_DISTRIBUTE(\"redisDistribute\"),\n    MACHINE_MAXMEMORY_DISTRIBUTE(\"maxMemoryDistrubute\"),\n    MACHINE_USEDMEMORY_DISTRIBUTE(\"usedMemoryDistribute\"),\n    MACHIEN_ROOMMEMORY_DISTRIBUTE(\"roomMemoryDistribute\"),\n    MACHIEN_ROOM_DISTRIBUTE(\"roomDistribute\");\n\n    String value;\n\n    private StatEnum(String value) {\n        this.value = value;\n    }\n\n    public String value() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/SuccessEnum.java",
    "content": "package com.sohu.cache.web.enums;\r\n\r\n/**\r\n * 成功失败状态\r\n *\r\n * @author leifu\r\n * @Time 2014年10月16日\r\n */\r\npublic enum SuccessEnum {\r\n    ERROR(-1),\r\n    SUCCESS(1),\r\n    FAIL(0),\r\n    REPEAT(2),\r\n    NO_REPEAT(4),\r\n    INSTALLED(3);\r\n\r\n    int value;\r\n\r\n    private SuccessEnum(int value) {\r\n        this.value = value;\r\n    }\r\n\r\n    public int value() {\r\n        return value;\r\n    }\r\n\r\n    public String info() {\r\n        if (value == 3) {\r\n            return \"已安装\";\r\n        }else if (value == 2) {\r\n            return \"重复插入\";\r\n        }else if (value == 4) {\r\n            return \"不重复\";\r\n        } else if (value == 1) {\r\n            return \"成功\";\r\n        } else if (value == -1) {\r\n            return \"错误\";\r\n        } else {\r\n            return \"失败\";\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/TriggerStateEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n * @author fulei\n * @date 2018年8月8日\n * @time 下午3:29:48\n */\npublic enum TriggerStateEnum {\n\n\tWAITING(\"WAITING\", \"等待\"),\n\tACQUIRED(\"ACQUIRED\", \"正常运行\"),\n\tPAUSED(\"PAUSED\", \"暂停\"),\n\tBLOCKED(\"BLOCKED\", \"阻塞\"),\n\tERROR(\"ERROR\", \"错误\");\n\t\n\tprivate String state;\n\t\n\tprivate String info;\n\t\n\tprivate TriggerStateEnum(String state, String info) {\n\t\tthis.state = state;\n\t\tthis.info = info;\n\t}\n\n\tpublic String getState() {\n\t\treturn state;\n\t}\n\n\tpublic String getInfo() {\n\t\treturn info;\n\t}\n\t\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/UseTypeEnum.java",
    "content": "package com.sohu.cache.web.enums;\n\n/**\n* @Description:    机器部署类型\n* @Author:         caoru\n* @CreateDate:     2018/10/8 18:32\n*/\npublic enum UseTypeEnum {\n\n    Machine_special(0),\n    Machine_test(1),\n    Machine_mix(2);\n\n    private int value;\n\n    private UseTypeEnum(int value) {\n        this.value = value;\n    }\n\n    public int getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/enums/WebClients.java",
    "content": "package com.sohu.cache.web.enums;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2020/5/19 3:59 下午\n */\npublic class WebClients {\n    public static List<String> webClientIpList = new ArrayList<>();\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/AppAlertRecordService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppAlertRecord;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\n\nimport java.util.List;\n\n/**\n * 保存报警信息\n * @author zengyizhao\n * @Date 2021年9月3日\n */\npublic interface AppAlertRecordService {\n    \n    /**\n     * 保存报警信息\n     *\n     * @param appAlertRecord\n     * @return\n     */\n    int saveAlertInfo(AppAlertRecord appAlertRecord);\n\n    /**\n     * 批量保存报警信息\n     * @param appAlertRecordList\n     * @return\n     */\n    int saveBatchAlertInfo(List<AppAlertRecord> appAlertRecordList);\n\n    /**\n     * 异步保存报警信息\n     *\n     * @param appAlertRecord\n     * @return\n     */\n    void asyncSaveAlertInfo(AppAlertRecord appAlertRecord);\n\n    /**\n     * 异步批量保存报警信息\n     * @param appAlertRecordList\n     * @return\n     */\n    void asyncSaveBatchAlertInfo(List<AppAlertRecord> appAlertRecordList);\n\n    /**\n     * 根据报警类型，保存报警信息\n     * @param type 类型\n     * @param title 邮件标题\n     * @param message 邮件内容\n     * @param object\n     * @return\n     */\n    int saveAlertInfoByType(AlertTypeEnum type, String title, String message, Object... object);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/AppAutoCapacityService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppCapacityMonitor;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.InstanceStats;\n\nimport java.util.Map;\n\n/**\n * 容量监控\n * @author zengyizhao\n * @Date 2022年10月9日\n */\npublic interface AppAutoCapacityService {\n    \n    /**\n     * 检查是否需自动扩容，并扩容处理\n     *\n     * @param appDesc\n     * @param appMemUsePercent\n     * @param instanceStatsMap\n     * @return\n     */\n    void checkAndExpandCapacity(AppDesc appDesc, int appMemUsePercent, Map<InstanceInfo, InstanceStats> instanceStatsMap);\n\n    void updateAppCapacityMonitor(AppCapacityMonitor appCapacityMonitor);\n\n    void updateAppMemUsedHistory();\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/AppImportService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppImport;\n\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2021/1/8 上午10:48\n */\npublic interface AppImportService {\n    List<AppImport> getImportAppList(Integer status);\n\n    int save(AppImport appImport);\n\n    int update(AppImport appImport);\n\n    AppImport appImport(Long id);\n\n    AppImport get(Long id);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/AppRedisCommandCheckService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.web.vo.*;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/26 17:24\n * @Description: redis命令检测\n */\npublic interface AppRedisCommandCheckService {\n\n    RedisCommandCheckResult checkRedisCommand(AppUser appUser, AppRedisCommandCheckVo appRedisCommandCheckVo);\n\n    void saveRedisCommandCheckResult(RedisCommandCheckResult redisCommandCheckResult, AppRedisCommandCheckResult resultList);\n\n    List<RedisCommandCheckResult> getRedisCommandCheckResult();\n\n    AppRedisCommandCheckResult getRedisCommandCheckDetailResult(String uuid);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/AppRedisConfigCheckService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.web.vo.AppRedisConfigCheckResult;\nimport com.sohu.cache.web.vo.AppRedisConfigCheckVo;\nimport com.sohu.cache.web.vo.RedisConfigCheckResult;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/26 17:24\n * @Description: redis配置检测\n */\npublic interface AppRedisConfigCheckService {\n\n    RedisConfigCheckResult checkRedisConfig(AppUser appUser, AppRedisConfigCheckVo configCheckVo);\n\n    void saveRedisConfigCheckResult(RedisConfigCheckResult redisConfigCheckResult, List<AppRedisConfigCheckResult> resultList);\n\n    List<RedisConfigCheckResult> getRedisConfigCheckResult();\n\n    List<AppRedisConfigCheckResult> getRedisConfigCheckDetailResult(String uuid);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/AppScrollRestartService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.ConfigRestartRecord;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.web.enums.RestartStatusEnum;\nimport com.sohu.cache.web.vo.AppRedisConfigVo;\nimport com.sohu.cache.web.vo.ExecuteResult;\nimport org.springframework.ui.Model;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/13 16:09\n * @Description: 应用滚动重启修改配置\n */\npublic interface AppScrollRestartService {\n\n    /**\n     * 处理应用实例信息（封装主从信息）\n     * @param instanceInfoList\n     * @param appDesc\n     */\n    boolean handleAppInstanceInfo(List<InstanceInfo> instanceInfoList, AppDesc appDesc);\n\n    /**\n     * 根据主节点分组\n     * @param instanceList\n     * @return\n     */\n    Map<Integer, List<InstanceInfo>> instanceGroupByMaster(List<InstanceInfo> instanceList);\n\n    /**\n     * 根据用户信息，应用信息，重启请求信息，操作类型生成重启记录并保存\n     * @param appUser\n     * @param appDesc\n     * @param paramObj\n     * @param opertateType\n     * @return\n     */\n    long generateAndSaveConfigRestartRecord(AppUser appUser, AppDesc appDesc, Object paramObj, Integer opertateType, List<InstanceInfo> pointedInstanceList);\n\n    /**\n     * 保存重启记录\n     * @param configRestartRecord\n     */\n    void saveConfigRestartRecord(ConfigRestartRecord configRestartRecord);\n\n    /**\n     * 更新重启记录\n     * @param configRestartRecord\n     */\n    void updateConfigRestartRecord(ConfigRestartRecord configRestartRecord);\n\n    /**\n     * 更新修改配置、重启记录\n     * @param recordId\n     * @param restartStatusEnum\n     * @param lastLog\n     */\n    void updateConfigRestartRecord(long recordId, RestartStatusEnum restartStatusEnum, String... lastLog);\n\n    /**\n     * 查询重启记录\n     * @param id\n     * @return\n     */\n    ConfigRestartRecord getConfigRestartRecord(long id);\n\n    /**\n     * 查询重启记录及信息\n     * @param configRestartRecord\n     * @return\n     */\n    List<ConfigRestartRecord> getConfigRestartRecordByCondition(Model model, ConfigRestartRecord configRestartRecord, int pageNo, int pageSize);\n\n    /**\n     * 保存过程日志到redis\n     * @param id\n     * @param log\n     */\n    void saveConfigRestartLog(long id, String log);\n\n    /**\n     * 获取过程日志，并删除\n     * @param id\n     * @return\n     */\n    List<String> getAndDeleteConfigRestartLog(long id);\n\n    /**\n     * 删除redis过程日志\n     * @param id\n     */\n    void deleteConfigRestartLog(long id);\n\n    /**\n     * 添加停止滚动重启标志\n     * @param appId\n     * @return\n     */\n    boolean addStopRestartFlag(Long appId);\n\n    /**\n     * 删除停止滚动重启标志\n     * @param appId\n     * @return\n     */\n    boolean deleteStopRestartFlag(Long appId);\n\n    /**\n     * 判断是否有停止滚动重启标志\n     * @param appId\n     * @return\n     */\n    boolean existsStopRestartFlag(Long appId);\n\n    /**\n     * 处理滚动重启\n     * @param appDesc\n     * @param instanceInfoList\n     * @param appRedisConfigVo\n     * @return\n     */\n    ExecuteResult handleRestart(AppUser appUser, AppDesc appDesc, List<InstanceInfo> instanceInfoList, AppRedisConfigVo appRedisConfigVo);\n\n    /**\n     * 按分组处理滚动重启\n     * @param appUser\n     * @param appDesc\n     * @param appRedisConfigVo\n     * @param instanceInfoList\n     * @param pointedInstanceList\n     * @param groupMap\n     */\n    void handleRestartByGroup(AppUser appUser, AppDesc appDesc, AppRedisConfigVo appRedisConfigVo, List<InstanceInfo> instanceInfoList, List<InstanceInfo> pointedInstanceList, Map<Integer, List<InstanceInfo>> groupMap);\n\n    /**\n     * 处理修改配置\n     * @param appUser\n     * @param appDesc\n     * @param instanceInfoList\n     * @param appRedisConfigVo\n     * @return\n     */\n    Map<String,Object> handleConfig(AppUser appUser, AppDesc appDesc, List<InstanceInfo> instanceInfoList, AppRedisConfigVo appRedisConfigVo);\n\n    /**\n     * 判断应用是否正在滚动重启\n     * @param appId\n     * @return\n     */\n    boolean isAppOnScrollRestart(long appId);\n\n    /**\n     * 执行failover 并check重试\n     * @param retryTime\n     * @param slaveInstance\n     * @param appDesc\n     * @return\n     */\n    boolean failoverAndCheckIdempotent(int retryTime, InstanceInfo slaveInstance, AppDesc appDesc);\n\n    /**\n     * 执行failover并检查\n     * @param slaveInstance 必须包括masterHost & masterPort\n     * @param appDesc\n     * @return\n     */\n    Optional<String> clusterFailoverAndCheck(InstanceInfo slaveInstance, AppDesc appDesc);\n\n    /**\n     * 判断实例是否完成加载dump文件\n     * @param instanceInfo\n     */\n    boolean checkLoadFinish(InstanceInfo instanceInfo);\n\n    /**\n     * 判断实例是否准备完成\n     * @param instanceInfo\n     */\n    boolean checkSlaveReadyFinish(InstanceInfo instanceInfo);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/AppService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.constant.AppAuditType;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 应用相关操作\n *\n * @author leifu\n * @Time 2014年10月21日\n */\npublic interface AppService {\n\n    /**\n     * 查询指定用户下的应用个数\n     *\n     * @param appUser\n     * @return\n     */\n    int getAppDescCount(AppUser appUser, AppSearch appSearch);\n\n    /**\n     * 获取应用master节点\n     *\n     * @param appId\n     * @return\n     */\n    List<InstanceInfo> getAppMasterInstanceInfoList(long appId);\n\n    /**\n     * 查询指定用户下的所有的应用\n     *\n     * @param appUser\n     * @return\n     */\n    List<AppDesc> getAppDescList(AppUser appUser, AppSearch appSearch);\n\n    /**\n     * 按照Appid取应用信息\n     *\n     * @param appId\n     * @return\n     */\n    AppDesc getByAppId(Long appId);\n\n    /**\n     * 保存应用\n     *\n     * @param appDesc\n     * @return\n     */\n    int save(AppDesc appDesc);\n\n    /**\n     * 更新应用\n     *\n     * @param appDesc\n     * @return\n     */\n    int update(AppDesc appDesc);\n\n    /**\n     * 更新应用 pwd\n     * @param appId\n     * @param appPwd\n     * @return\n     */\n    int updateAppPwd(long appId, String appPwd);\n\n    /**\n     * 获取应用下实例的基本信息\n     * @param appId\n     * @return\n     */\n    List<InstanceInfo> getAppBasicInstanceInfo(Long appId);\n\n    /**\n     * 获取应用下实例基本信息，并按照主从分组\n     * @param appId\n     * @return\n     */\n    Map<InstanceInfo, List<InstanceInfo>> getAppInstanceInfoGroup(Long appId);\n\n        /**\n         * 获取应用的实例\n         *\n         * @param appId\n         * @return\n         */\n    List<InstanceInfo> getAppInstanceInfo(Long appId);\n\n    List<InstanceInfo> getAppOnlineInstanceInfo(Long appId);\n\n    List<InstanceStats> getAppInstanceStats(Long appId);\n\n    int updateWithCustomPwd(AppDesc appDesc);\n\n    /**\n     * 保存用户与应用的关系\n     *\n     * @param appId\n     * @param userId\n     * @return\n     */\n    boolean saveAppToUser(Long appId, Long userId);\n\n    /**\n     * 更新审核状态\n     *\n     * @param id      审批id\n     * @param appId\n     * @param status  审批状态\n     * @param appUser 更新人\n     */\n    void updateAppAuditStatus(Long id, Long appId, Integer status, AppUser appUser);\n\n    /**\n     * 更新用户审核状态\n     *\n     * @param id     审批id\n     * @param status 审批状态\n     */\n    void updateUserAuditStatus(Long id, Integer status, Long operateId);\n\n    /**\n     * 通过应用名获取应用\n     *\n     * @param appName\n     * @return\n     */\n    AppDesc getAppByName(String appName);\n\n    /**\n     * 获取应用下的所有用户应用关系列表\n     *\n     * @param appId\n     * @return\n     */\n    List<AppToUser> getAppToUserList(Long appId);\n\n    /**\n     * 删除用户应用关系\n     *\n     * @param appId\n     * @param userId\n     */\n    SuccessEnum deleteAppToUser(Long appId, Long userId);\n\n    /**\n     * 获取审批列表\n     *\n     * @param status(参考AppAppCheckEnum)\n     * @param type                      (参考AppAuditType)\n     * @return\n     */\n    List<AppAudit> getAppAudits(Integer status, Integer type, Long auditId, Long userId, Long operateId);\n\n\n    Map<String, Object> getStatisticGroupByStatus(Long userId, Long operateId, Date startTime, Date endTime);\n\n    Map<String, Object> getStatisticGroupByType(Long userId, Long operateId, Date startTime, Date endTime);\n\n    /**\n     * 获取应用申请信息\n     *\n     * @param appid 应用id\n     * @param type  (参考AppAuditType)\n     */\n    List<AppAudit> getAppAudits(Long appid, Integer type);\n\n    /**\n     * 保存扩容申请\n     *\n     * @param appDesc\n     * @param appUser\n     * @param applyMemSize   扩容容量\n     * @param appScaleReason 扩容原因\n     * @param appScale       申请类型\n     */\n    AppAudit saveAppScaleApply(AppDesc appDesc, AppUser appUser, String applyMemSize, String appScaleReason,\n                               AppAuditType appScale);\n\n    /**\n     * 保存应用配置申请\n     *\n     * @param appDesc\n     * @param appUser\n     * @param instanceId     实例id\n     * @param appConfigKey   配置项\n     * @param appConfigValue 配置值\n     * @param modifyConfig   申请类型\n     */\n    AppAudit saveAppChangeConfig(AppDesc appDesc, AppUser appUser, Long instanceId, String appConfigKey,\n                                 String appConfigValue, String appConfigReason, AppAuditType modifyConfig);\n\n    /**\n     * 保存实例配置申请\n     *\n     * @param appDesc\n     * @param appUser\n     * @param instanceId\n     * @param instanceConfigKey\n     * @param instanceConfigValue\n     * @param instanceConfigReason\n     * @param instanceModifyConfig\n     * @return\n     */\n    AppAudit saveInstanceChangeConfig(AppDesc appDesc, AppUser appUser, Long instanceId, String instanceConfigKey,\n                                      String instanceConfigValue, String instanceConfigReason, AppAuditType instanceModifyConfig);\n\n    /**\n     * 获取审批信息\n     *\n     * @param appAuditId\n     * @return\n     */\n    AppAudit getAppAuditById(Long appAuditId);\n\n    /**\n     * 驳回理由\n     *\n     * @param appAudit\n     * @param userInfo\n     */\n    SuccessEnum updateRefuseReason(AppAudit appAudit, AppUser userInfo);\n\n    /**\n     * 获取用户的应用数量\n     *\n     * @param userId\n     * @return\n     */\n    int getUserAppCount(Long userId);\n\n    /**\n     * 获取应用的机器信息\n     *\n     * @param appId\n     * @return\n     */\n    List<MachineStats> getAppMachineDetail(Long appId);\n\n    /**\n     * 获取应用拥有实例的机器\n     * @param appId\n     * @return\n     */\n    public List<MachineStats> getAppMachine(Long appId);\n\n    /**\n     * 根据应用id获取审批记录\n     *\n     * @param appId\n     * @return\n     */\n    List<AppAudit> getAppAuditListByAppId(Long appId);\n\n    /**\n     * 注册用户申请\n     *\n     * @param appUser\n     * @param registerUserApply\n     * @return\n     */\n    AppAudit saveRegisterUserApply(AppUser appUser, AppAuditType registerUserApply);\n\n    /**\n     * 获取所有应用\n     */\n    List<AppDesc> getAllAppDesc();\n\n    /**\n     * 修改报警配置\n     *\n     * @param appId\n     * @param memAlertValue\n     * @param clientConnAlertValue\n     * @param appUser\n     * @return\n     */\n    SuccessEnum changeAppAlertConfig(long appId, int memAlertValue, int clientConnAlertValue, int hitPrecentAlertValue,\n                                     int isAccessMonitor, AppUser appUser);\n\n    /**\n     * 更新appKey\n     *\n     * @param appId\n     */\n    void updateAppKey(long appId);\n\n    /**\n     * @param machinelist 机器信息\n     * @param type        参考NodeEum\n     * @param appType     应用类型\n     * @param instanceNum 实例数量\n     * @param maxMemory   内存大小\n     * @param hasSalve    是否有从节点\n     * @return 添加部署Redis/Pika节点信息\n     */\n    public List<DeployInfo> generateInstanceInfo(List<String> machinelist, int type, int appType, int instanceNum, int maxMemory, int hasSalve, List<DeployInfo> deployInfoList);\n\n    /**\n     * @param proxylist 机器信息\n     * @param type      参考NodeEum\n     * @param appType   应用类型\n     * @param proxyNum  实例数量\n     * @return 添加sentinel/proxy节点\n     */\n    public List<DeployInfo> generateProxyinfo(List<String> proxylist, int type, int appType, int proxyNum, List<DeployInfo> deployInfoList);\n\n    String generateDeployInfo(Integer type,\n                              Integer isSalve,\n                              String room,\n                              Double size,\n                              Integer machineNum,\n                              Integer instanceNum,\n                              Integer useType,\n                              String machines,\n                              String excludeMachines,\n                              String sentinelMachines,\n                              List<DeployInfo> getDeployInfo,\n                              List<MachineMemStatInfo> resMachines);\n\n    /**\n     * @Description: 获取部署信息统计\n     * @Author: caoru\n     * @CreateDate: 2018/10/10 16:34\n     */\n    Map getMachineDeployStat(Set<String> ipList, List<DeployInfo> deployInfoList);\n\n    /**\n     * @Description: 计算内存和核数\n     * @Author: caoru\n     * @CreateDate: 2018/10/14 21:06\n     */\n    void getMachineCandiList(MachineMemStatInfo memStatInfo, Double reqSize, Integer reqCpu, Integer isSalve,\n                             List<MachineMemStatInfo> machineCandi);\n\n    /**\n     * @Description: 从备选集中获取机器列表\n     * @Author: caoru\n     * @CreateDate: 2018/10/9 10:34\n     */\n    void getResMachines(List<MachineMemStatInfo> machineCandi, Integer machineNum,\n                        List<MachineMemStatInfo> resMachines);\n\n    /**\n     * <p>\n     * Description:\n     * </p>\n     *\n     * @author chenshi\n     * @version 1.0\n     * @date 2018/11/14\n     */\n    List<AppDesc> checkAppStatus(List<String> appids);\n\n    /**\n     * 键值分析\n     *\n     * @param appDesc\n     * @param appUser\n     * @param appAnalysisReason\n     * @return\n     */\n    AppAudit saveAppKeyAnalysis(AppDesc appDesc, AppUser appUser, String appAnalysisReason, String nodeInfo);\n\n    /**\n     * 应用诊断\n     *\n     * @param appDesc\n     * @param appUser\n     * @param reason\n     * @return\n     */\n    AppAudit saveAppDiagnostic(AppDesc appDesc, AppUser appUser, String reason);\n\n    /**\n     * 获取碎片率高的应用\n     */\n    List<AppTopMemFragRatio> getTopMemFragRatioApps(TimeBetween timeBetween);\n\n    public List<InstanceInfo> getAppInstanceByType(long appId, InstanceTypeEnum instanceTypeEnum);\n\n    /**\n     * @param gatherTime\n     * @return\n     */\n    Map<Long, Map<String, Object>> getAppClientStatGather(long appId, String gatherTime);\n\n    Map<String, List<Map<String, Object>>> getFilterAppClientStatGather(long appId, String gatherTime);\n\n    /**\n     * 检查应用持久化配置并修复\n     * @param appId\n     * @param masterConfigMap\n     * @param slaveConfigMap\n     * @return 执行失败信息\n     */\n    String checkAppPersistenceConfigAndFix(long appId, Map<String, String> masterConfigMap, Map<String, String> slaveConfigMap);\n\n\n    /**\n     * 更新持久化类型\n     * @param appId\n     * @param persistenceType\n     */\n    boolean updateAppPersistenceType(long appId, int persistenceType);\n\n    /**\n     * 更新内存策略\n     * @param appId\n     * @param maxmemoryPolicy\n     * @return\n     */\n    boolean updateAppMaxmemoryPolicy(long appId, int maxmemoryPolicy);\n\n    /**\n     * 获取用户所属应用的内存统计信息（包含版本）\n     * @param userId\n     * @param isAdmin\n     * @return\n     */\n    List<AppCapacityStatisticsResult> getAppCapacityStatistics(Long userId, Boolean isAdmin);\n\n    Map<String, Object> getAppCapacityStats(Long userId, Boolean isAdmin);\n\n    /**\n     * 获取用户所属应用的监控统计信息\n     * @param userId\n     * @param isAdmin\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    List<AppMonitorStatisticsResult> getAppMonitorStatistics(Long userId, Boolean isAdmin, String startTime, String endTime);\n\n    /**\n     * 更新应用拓扑检测结果\n     * @param topologyExam\n     * @return\n     */\n    int saveAppTopologyExam(AppClientStatisticGather topologyExam);\n\n    /**\n     * 获取拓扑检测失败的应用\n     * @param gatherTime\n     * @return\n     */\n    List<AppClientStatisticGather> getTopologyExamFailedByGatherTime(String gatherTime);\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/ConfigService.java",
    "content": "package com.sohu.cache.web.service;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\nimport com.sohu.cache.entity.SystemConfig;\r\nimport com.sohu.cache.web.enums.SuccessEnum;\r\n\r\n/**\r\n * cachecloud配置服务\r\n * @author leifu\r\n * @Date 2016年5月23日\r\n * @Time 上午10:35:04\r\n */\r\npublic interface ConfigService {\r\n    \r\n    /**\r\n     * 加载配置\r\n     */\r\n    public void reloadSystemConfig();\r\n\r\n    /**\r\n     * 更新配置\r\n     * @param configMap\r\n     * @return\r\n     */\r\n    SuccessEnum updateConfig(Map<String, String> configMap);\r\n\r\n    /**\r\n     * 获取配置列表\r\n     * @param status\r\n     * @return\r\n     */\r\n    List<SystemConfig> getConfigList(int status);\r\n    \r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/DiagnosticToolService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.entity.InstanceInfo;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/5 5:33 下午\n */\npublic interface DiagnosticToolService {\n    Map<Long, List<InstanceInfo>> getAppInstancesMap(List<AppDesc> appDescList);\n\n    List<DiagnosticTaskRecord> getDiagnosticTaskRecords(Long appId, Long parentTaskId, Long auditId, Integer type, Integer status);\n\n    List<String> getScanDiagnosticData(String redisKey);\n\n    List<String> getScanCleanDiagnosticData(String redisKey);\n\n    Map<String, String> getDiagnosticDataMap(String redisKey,int type,boolean err);\n\n    String getHotkeyDiagnosticData(String redisKey);\n\n    List<String> getSampleScanData(Long appId, String nodes, String pattern);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/InstancePortService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.task.entity.NutCrackerNode;\nimport com.sohu.cache.task.entity.RedisSentinelNode;\nimport com.sohu.cache.task.entity.RedisServerNode;\n\nimport java.util.List;\n\n/**\n * @author fulei\n * @date 2018年7月4日\n * @time 下午3:20:57\n */\npublic interface InstancePortService {\n\n\t/**\n     * 检查机器端口是否被占用\n\t * @param ip\n\t * @param port\n\t * @return\n\t */\n\tpublic boolean checkHostPortExist(String ip, int port);\n\t\n\t/**\n\t * 从当前机器生成一个可用实例\n\t * @param appId\n\t * @param host\n\t * @param maxMemory\n\t * @return\n\t */\n\tpublic RedisServerNode generateRedisServerNode(long appId, String host, int maxMemory, InstanceRoleEnum instanceRoleEnum);\n\t\n\t/**\n\t * 从当前机器生成一个可用实例\n\t * @param appId\n\t * @param host\n\t * @param maxMemory\n\t * @return\n\t */\n//\tpublic PikaNode generatePikaNode(long appId, String host, int maxMemory, InstanceRoleEnum instanceRoleEnum);\n\t\n\t/**\n\t * 生成redis server 实例列表\n\t * @param appId\n\t * @param redisServerMachineList\n\t * @param masterPerMachine\n\t * @param maxMemory\n\t * @return\n\t */\n\tpublic List<RedisServerNode> generateRedisServerNodeList(long appId, List<String> redisServerMachineList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int masterPerMachine, int maxMemory);\n\n\t/**\n\t * 生成redis server 实例列表\n\t * @param appId\n\t * @param appDeployInfoList (masterIp:maxmemory:slaveIp)\n\t * @param masterPerMachine\n\t * @param maxMemory\n\t * @return\n\t */\n\tpublic List<RedisServerNode> generateRedisServerNodeListWithDeployInfo(long appId, List<String> appDeployInfoList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int masterPerMachine, int maxMemory);\n\n\t/**\n\t * 生成redis sentinel 实例列表\n\t * @param appId\n\t * @param redisSentinelMachineList\n\t * @param sentinelPerMachine\n\t * @return\n\t */\n\tpublic List<RedisSentinelNode> generateRedisSentinelNodeList(long appId, List<String> redisSentinelMachineList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int sentinelPerMachine);\n\t\n\t/**\n\t * 生成nut cracker 实例列表\n\t * @param appId\n\t * @param nutCrackerMachineList\n\t * @param nutCrackerPerMachine\n\t * @return\n\t */\n\tpublic List<NutCrackerNode> generateNutCrackerNodeList(long appId, List<String> nutCrackerMachineList,int nutCrackerPerMachine);\n\t\n\t/**\n\t * 获取redis-port 实例列表\n\t * @param sourceAppId\n\t * @param targetAppId\n\t * @param slaveInstanceInfoList\n\t * @param proxyInstanceInfoList\n\t * @return\n\t */\n//\tpublic List<RedisPortNode> generateRedisPortNodeList(long sourceAppId, long targetAppId,List<InstanceInfo> slaveInstanceInfoList, List<InstanceInfo> proxyInstanceInfoList);\n\n\t/**\n\t * 获取redis-migrate-tool实例列表\n\t * @return\n\t */\n//\tpublic List<RedisMigrateToolNode> generateRedisMigrateToolNodeList(int rmtCount, String logicName);\n\n\t/**\n\t * 获取proxy实例列表\n\t * @param appId\n\t * @param codisProxyMachineList\n\t * @param codisProxyPerMachine\n\t * @return\n\t */\n//\tpublic List<CodisProxyNode> generateCodisProxyList(long appId, List<String> codisProxyMachineList,int codisProxyPerMachine);\n\n\t/**\n\t * 获取dashboard实例列表\n\t * @param appId\n\t * @param codisDashboardMachineList\n\t * @return\n\t */\n//\tpublic List<CodisDashboardNode> generateCodisDashboardList(long appId, List<String> codisDashboardMachineList);\n\n\t/**\n\t * @param appId\n\t * @param pikaMachineList\n\t * @param masterPerMachine\n\t * @return\n\t */\n//\tpublic List<PikaNode> generatePikaNodeList(long appId, List<String> pikaMachineList, int masterPerMachine);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/MigrateService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.exception.SSHException;\n\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2023/2/28 15:33\n * @Description: 迁移服务\n * @Version: 1.0\n */\npublic interface MigrateService {\n\n    /**\n     * 根据机器pod，强制迁移\n     * @param sourceIp\n     * @param targetIp\n     * @throws SSHException\n     */\n    public void forceMigrate(String sourceIp, String targetIp) throws SSHException;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/ResourceService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.web.enums.SuccessEnum;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by chenshi on 2020/7/6.\n */\npublic interface ResourceService {\n\n    SuccessEnum saveResource(SystemResource systemResouce);\n\n    SuccessEnum updateResource(SystemResource systemResouce);\n\n    List<SystemResource> getResourceList(int resourceType);\n\n    List<SystemResource> getResourceList(int resourceType, String searchName);\n\n    SuccessEnum pushScript(Integer repositoryId, Integer resourceId, String content, AppUser userInfo);\n\n    SuccessEnum pushDir(Integer repositoryId, Integer resourceId, AppUser userInfo);\n\n    SystemResource getResourceById(int resourceId);\n\n    SystemResource getResourceByName(String resourceName);\n\n    String getRespositoryUrl(int resourceId, int respoitoryId);\n\n    String getRemoteFileContent(int resourceId, int respoitoryId);\n\n    //获取远程仓库信息\n    SystemResource getRepository();\n\n    Map<Integer, Integer> getAppUseRedis();\n\n    SystemResource getRedisResourceByCache(Integer repositoryId);\n\n    String getRedisVersion(Integer repositoryId);\n\n    boolean checkRedisVersionGreater(Integer repositoryId, int[] versions);\n}\n\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/ServerDataService.java",
    "content": "package com.sohu.cache.web.service;\r\n\r\nimport java.util.List;\r\n\r\nimport com.sohu.cache.entity.ServerInfo;\r\nimport com.sohu.cache.entity.ServerStatus;\r\nimport com.sohu.cache.server.data.Server;\r\n\r\npublic interface ServerDataService {\r\n\t/**\r\n\t * 查询服务器基本信息\r\n\t * @param ip\r\n\t * @return @ServerInfo\r\n\t */\r\n\tpublic ServerInfo queryServerInfo(String ip);\r\n\r\n\t/**\r\n\t * 获取所有机器的系统信息\r\n\t * @return\r\n\t */\r\n\tpublic List<ServerInfo> getAllServerInfo();\r\n\t\r\n\t/**\r\n\t * 保存服务器发行版信息\r\n\t * @param ip\r\n\t * @param dist from /etc/issue\r\n\t */\r\n\tpublic void saveServerInfo(String ip, String dist);\r\n\t\r\n\t/**\r\n\t * 保存/更新服务器信息\r\n\t * @param server\r\n\t * @return 影响的行数\r\n\t */\r\n\tpublic Integer saveAndUpdateServerInfo(Server server);\r\n\t\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerStatus(String ip, String date);\r\n\t\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerOverview(String ip, String date);\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerCpu(String ip, String date);\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerNet(String ip, String date);\r\n\t/**\r\n\t * 查询服务器状态\r\n\t * @param ip\r\n\t * @param date\r\n\t * @return List<ServerStatus>\r\n\t */\r\n\tpublic List<ServerStatus> queryServerDisk(String ip, String date);\r\n\t\r\n\t/**\r\n\t * 保存服务器状态\r\n\t */\r\n\tpublic void saveServerStat(Server server);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/ToolService.java",
    "content": "package com.sohu.cache.web.service;\n\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by rucao on 2018/12/11\n */\npublic interface ToolService {\n    /**\n     * <p>\n     * Description: 通过appid进行任务检查\n     * </p>\n     * @param appId 应用id\n     */\n    List<Map> topologyExamByAppid(long appId);\n    /**\n     * <p>\n     * Description: 所有线上应用进行任务检查\n     * </p>\n     * @param appidList 线上appid集合\n     */\n    void topologyExam(List<Long> appidList);\n\n    void restAppDescOfficer();\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/UserLoginStatusService.java",
    "content": "package com.sohu.cache.web.service;\r\n\r\nimport com.sohu.cache.entity.AppUser;\r\n\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport javax.servlet.http.HttpServletResponse;\r\n\r\n/**\r\n * 用户登录状态信息服务\r\n * \r\n * @author leifu\r\n * @Date 2016年6月15日\r\n * @Time 下午1:24:09\r\n */\r\npublic interface UserLoginStatusService {\r\n    public final static String LOGIN_USER_STATUS_NAME = \"CACHE_CLOUD_USER_STATUS\";\r\n\r\n    /**\r\n     * 获取用户登录状态信息\r\n     * \r\n     * @param request\r\n     * @return\r\n     */\r\n    String getUserNameFromLoginStatus(HttpServletRequest request);\r\n\r\n    /**\r\n     * 解析ticket\r\n     *\r\n     * @param request\r\n     * @return\r\n     */\r\n    String getUserNameFromTicket(HttpServletRequest request);\r\n\r\n    /**\r\n     * 获取登录跳转地址\r\n     *\r\n     * @param request\r\n     * @return\r\n     * @throws Exception\r\n     */\r\n    String getRedirectUrl(HttpServletRequest request);\r\n\r\n    /**\r\n     * 获取注销登录地址\r\n     *\r\n     * @return\r\n     * @throws Exception\r\n     */\r\n    String getLogoutUrl();\r\n\r\n    /**\r\n     * 获取新用户注册地址\r\n     *\r\n     * @return\r\n     * @throws Exception\r\n     */\r\n    String getRegisterUrl(AppUser user);\r\n\r\n    /**\r\n     * 添加用户登录状态信息\r\n     * \r\n     * @param request\r\n     * @param response\r\n     * @param userName\r\n     */\r\n    void addLoginStatus(HttpServletRequest request, HttpServletResponse response, String userName);\r\n\r\n    /**\r\n     * 移除用户登录状态信息\r\n     * \r\n     * @param request\r\n     * @param response\r\n     */\r\n    void removeLoginStatus(HttpServletRequest request, HttpServletResponse response);\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/UserService.java",
    "content": "package com.sohu.cache.web.service;\n\nimport java.util.List;\n\nimport com.sohu.cache.entity.AppBiz;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.vo.AppUserVo;\n\n/**\n * 用户管理service\n * @author leifu\n * @Date 2014年10月27日\n * @Time 上午9:57:47\n */\npublic interface UserService {\n\n    /**\n     * 通过id获取用户\n     * @param userId\n     * @return\n     */\n    AppUser get(Long userId);\n\n    /**\n     * 通过中文名获取用户\n     * @param chName\n     * @return\n     */\n    List<AppUser> getUserList(String chName);\n\n    /**\n     * 通过中文名和业务组名获取用户\n     * @param chName\n     * @param bizName\n     * @return\n     */\n    List<AppUserVo> getUserWithBizList(String chName, String bizName);\n\n    /**\n     * 获取所有用户\n     */\n    List<AppUser> getAllUser();\n\n    /**\n     * 获取某个应用下的所有用户\n     * @param appId\n     * @return\n     */\n    List<AppUser> getByAppId(Long appId);\n\n    /**\n     * 获取需要报警的用户列表\n     * @param appId\n     * @return\n     */\n    public List<AppUser> getAlertByAppId(Long appId);\n\n    /**\n     * 通过域账户前缀获取用户\n     * @param name\n     * @return\n     */\n    AppUser getByName(String name);\n\n    /**\n     * 保存用户\n     * @param appUser\n     * @return\n     */\n    SuccessEnum save(AppUser appUser);\n\n    /**\n     * 更新用户\n     * @param appUser\n     * @return\n     */\n    SuccessEnum update(AppUser appUser);\n\n    /**\n     * 删除用户\n     * @param userId\n     * @return\n     */\n    SuccessEnum delete(Long userId);\n\n    /**\n     * 重置密码\n     * @param userId\n     * @return\n     */\n    SuccessEnum resetPwd(Long userId);\n\n    /**\n     * 修改密码\n     * @param userId\n     * @param password\n     * @return\n     */\n    SuccessEnum updatePwd(Long userId, String password);\n\n    String getOfficerName(Long appId);\n\n    String getOfficerName(String officer);\n\n    /**\n     * 获取某个应用下的所有负责人\n     * @param officer\n     * @return\n     */\n    List<AppUser> getOfficerUserByUserIds(String officer);\n\n    /**\n     * 接手用户\n     * @param toRemoveUser\n     * @param toChargeUser\n     * @return\n     */\n    SuccessEnum takeoverUser(AppUser toRemoveUser, AppUser toChargeUser);\n\n    /**\n     * 通过id获取业务组\n     * @param bizId\n     * @return\n     */\n    AppBiz getBiz(Long bizId);\n\n    /**\n     * 获取所有业务组\n     * @return\n     */\n    List<AppBiz> getBizList();\n\n    /**\n     * 保存业务组\n     * @param appBiz\n     * @return\n     */\n    SuccessEnum saveBiz(AppBiz appBiz);\n\n    /**\n     * 更新业务组\n     * @param appBiz\n     * @return\n     */\n    SuccessEnum updateBiz(AppBiz appBiz);\n\n    /**\n     * 删除业务组\n     * @param bizId\n     * @return\n     */\n    SuccessEnum deleteBiz(Long bizId);\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/WebClientComponent.java",
    "content": "package com.sohu.cache.web.service;\n\nimport com.sohu.cache.web.enums.WebClients;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2020/5/19 3:48 下午\n */\n@Component\npublic class WebClientComponent {\n    @Value(\"${cachecloud.web.clients:127.0.0.1}\")\n    private String[] clients;\n\n    public List<String> getWebClientIps() {\n        if (CollectionUtils.isEmpty(WebClients.webClientIpList)) {\n            WebClients.webClientIpList = Arrays.asList(clients);\n        }\n        return WebClients.webClientIpList;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppAlertRecordServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.web.enums.AlertTypeEnum;\nimport com.sohu.cache.web.service.AppAlertRecordService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.dao.AppAlertRecordDao;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/3 13:34\n * @Description: 报警记录服务实现类\n */\n@Slf4j\n@Service\npublic class AppAlertRecordServiceImpl implements AppAlertRecordService {\n\n    @Autowired\n    private AppAlertRecordDao appAlertRecordDao;\n\n    /**\n     * 保存报警信息\n     *\n     * @param appAlertRecord\n     * @return\n     */\n    @Override\n    public int saveAlertInfo(AppAlertRecord appAlertRecord) {\n        return appAlertRecordDao.save(appAlertRecord);\n    }\n\n    /**\n     * 批量保存报警信息\n     * @param appAlertRecordList\n     * @return\n     */\n    @Override\n    public int saveBatchAlertInfo(List<AppAlertRecord> appAlertRecordList) {\n        return appAlertRecordDao.batchSave(appAlertRecordList);\n    }\n\n    /**\n     * 异步保存报警信息\n     * @param appAlertRecord\n     * @return\n     */\n    @Override\n    public void asyncSaveAlertInfo(AppAlertRecord appAlertRecord) {\n        AsyncThreadPoolFactory.ALERT_RECORD_THREAD_POOL.execute(new Runnable() {\n            @Override\n            public void run() {\n                appAlertRecordDao.save(appAlertRecord);\n            }\n        });\n    }\n\n    /**\n     * 异步批量保存报警信息\n     * @param appAlertRecordList\n     * @return\n     */\n    @Override\n    public void asyncSaveBatchAlertInfo(List<AppAlertRecord> appAlertRecordList) {\n        AsyncThreadPoolFactory.ALERT_RECORD_THREAD_POOL.execute(new Runnable() {\n            @Override\n            public void run() {\n                appAlertRecordDao.batchSave(appAlertRecordList);\n            }\n        });\n    }\n\n    /**\n     * 根据报警类型，保存报警信息\n     * @param type 类型\n     * @param object\n     * @return\n     */\n    @Override\n    public int saveAlertInfoByType(AlertTypeEnum type, String title, String content, Object... object) {\n        try{\n            Date date = new Date();\n            List<AppAlertRecord> appAlertRecordList = this.getAlertRecordByType(type, date, title, content, object);\n            if(CollectionUtils.isNotEmpty(appAlertRecordList)){\n                this.asyncSaveBatchAlertInfo(appAlertRecordList);\n            }\n        }catch (Exception e){\n            log.error(\"saveAlertInfoByType error : \", e);\n        }\n        return 1;\n    }\n\n    /**\n     * 根据邮件类型及信息生成对应的报警信息记录\n     * @param type\n     * @param title\n     * @param content\n     * @param objects\n     * @return\n     */\n    private List<AppAlertRecord> getAlertRecordByType(AlertTypeEnum type, Date date, String title, String content, Object... objects){\n        if(type == null){\n            return null;\n        }\n        if(objects == null || objects.length < 1){\n            return null;\n        }\n        List<AppAlertRecord> appAlertRecordList = new ArrayList<>();\n        if(type == AlertTypeEnum.INSTANCE_RUNNING_STATE_CHANGE){\n            InstanceInfo info = (InstanceInfo) (objects[0]);\n            generateAlertRecordByInstanceInfo(type, date, appAlertRecordList, title, content, info);\n        } else if(type == AlertTypeEnum.INATANCE_EXCEPTION_STATE_MONITOR\n                || type == AlertTypeEnum.INSTANCE_MINUTE_MONITOR\n                || type == AlertTypeEnum.POD_RESTART_INSTANCE_RECOVER){\n            //此类型，传入的content为空，content需自己组装\n            List<InstanceAlertValueResult> alertInstInfoList = (List<InstanceAlertValueResult>) (objects[0]);\n            generateAlertRecordByInstanceAlertList(type, date, appAlertRecordList, title, alertInstInfoList);\n        }else if(type == AlertTypeEnum.MACHINE_MEMORY_OVER_PRESET\n                || type == AlertTypeEnum.POD_RESTART_SYNC_TASK){\n            String ip = (String) (objects[0]);\n            generateAlertRecordByIp(type, date, appAlertRecordList, title, content, ip);\n        }else if(type == AlertTypeEnum.MACHINE_MANAGE){\n            //此类型，传入的content为空，content需自己组装\n            List<OperationAlertValueResult> recoverInstInfo = (List<OperationAlertValueResult>) (objects[0]);\n            generateAlertRecordByOPerationList(type, date, appAlertRecordList, title, recoverInstInfo);\n        }else if(type == AlertTypeEnum.APP_SHARD_CLENT_CONNECTION\n                || type == AlertTypeEnum.APP_SHARD_MEM_USED_RATIO){\n            AppDetailVO appDetailVO = (AppDetailVO) (objects[0]);\n            InstanceStats instanceStats = (InstanceStats) (objects[1]);\n            generateAlertRecordByAppAndInstance(type, date, appAlertRecordList, title, content, appDetailVO, instanceStats);\n        }else if(type == AlertTypeEnum.APP_CLIENT_CONNECTION\n                || type == AlertTypeEnum.APP_HIT_RATIO\n                || type == AlertTypeEnum.APP_MEM_USED_RATIO){\n            AppDetailVO appDetailVO = (AppDetailVO) (objects[0]);\n            generateAlertRecordByAppDetailVO(type, date, appAlertRecordList, title, content, appDetailVO);\n        }\n        //此种类型type == AlertTypeEnum.APP_MINUTE_MONITOR不做处理，与 AlertTypeEnum.INSTANCE_MINUTE_MONITOR重复\n        return appAlertRecordList;\n    }\n\n    /**\n     * 根据实例信息生成报警信息\n     * @param type\n     * @param appAlertRecordList\n     * @param title\n     * @param content\n     * @param instanceInfo\n     */\n    private void generateAlertRecordByInstanceInfo(AlertTypeEnum type, Date date, List<AppAlertRecord> appAlertRecordList, String title, String content, InstanceInfo instanceInfo){\n        AppAlertRecord appAlertRecord = new AppAlertRecord();\n        appAlertRecord.setVisibleType(type.getVisibleType());\n        appAlertRecord.setImportantLevel(type.getImportantLevel());\n        appAlertRecord.setTitle(title);\n        appAlertRecord.setContent(content);\n        appAlertRecord.setAppId(instanceInfo.getAppId());\n        appAlertRecord.setInstanceId(Long.valueOf(instanceInfo.getId()));\n        appAlertRecord.setIp(instanceInfo.getIp());\n        appAlertRecord.setPort(instanceInfo.getPort());\n        appAlertRecord.setCreateTime(date);\n        appAlertRecordList.add(appAlertRecord);\n    }\n\n    /**\n     * 根据机器ip生成报警信息\n     * @param type\n     * @param appAlertRecordList\n     * @param title\n     * @param content\n     * @param ip\n     */\n    private void generateAlertRecordByIp(AlertTypeEnum type, Date date, List<AppAlertRecord> appAlertRecordList, String title, String content, String ip){\n        AppAlertRecord appAlertRecord = new AppAlertRecord();\n        appAlertRecord.setVisibleType(type.getVisibleType());\n        appAlertRecord.setImportantLevel(type.getImportantLevel());\n        appAlertRecord.setTitle(title);\n        appAlertRecord.setContent(content);\n        appAlertRecord.setIp(ip);\n        appAlertRecord.setCreateTime(date);\n        appAlertRecordList.add(appAlertRecord);\n    }\n\n    /**\n     * 根据应用详细信息生成报警信息\n     * @param type\n     * @param appAlertRecordList\n     * @param title\n     * @param content\n     * @param appDetailVO\n     */\n    private void generateAlertRecordByAppDetailVO(AlertTypeEnum type, Date date, List<AppAlertRecord> appAlertRecordList, String title, String content, AppDetailVO appDetailVO){\n        AppAlertRecord appAlertRecord = new AppAlertRecord();\n        appAlertRecord.setVisibleType(type.getVisibleType());\n        appAlertRecord.setImportantLevel(type.getImportantLevel());\n        appAlertRecord.setTitle(title);\n        appAlertRecord.setContent(content);\n        long appId = appDetailVO.getAppDesc().getAppId();\n        appAlertRecord.setAppId(appId);\n        appAlertRecord.setCreateTime(date);\n        appAlertRecordList.add(appAlertRecord);\n    }\n\n    /**\n     * 根据应用和实例信息生成报警信息\n     * @param type\n     * @param appAlertRecordList\n     * @param title\n     * @param content\n     * @param appDetailVO\n     * @param instanceStats\n     */\n    private void generateAlertRecordByAppAndInstance(AlertTypeEnum type, Date date, List<AppAlertRecord> appAlertRecordList, String title, String content, AppDetailVO appDetailVO, InstanceStats instanceStats){\n        AppAlertRecord appAlertRecord = new AppAlertRecord();\n        appAlertRecord.setVisibleType(type.getVisibleType());\n        appAlertRecord.setImportantLevel(type.getImportantLevel());\n        appAlertRecord.setTitle(title);\n        appAlertRecord.setContent(content);\n        long appId = appDetailVO.getAppDesc().getAppId();\n        appAlertRecord.setAppId(appId);\n        appAlertRecord.setInstanceId(instanceStats.getInstId());\n        appAlertRecord.setIp(instanceStats.getIp());\n        appAlertRecord.setPort(instanceStats.getPort());\n        appAlertRecord.setCreateTime(date);\n        appAlertRecordList.add(appAlertRecord);\n    }\n\n    /**\n     * 根据实例报警结果列表生成报警信息\n     * @param type\n     * @param appAlertRecordList\n     * @param title\n     * @param alertInstInfoList\n     */\n    private void generateAlertRecordByInstanceAlertList(AlertTypeEnum type, Date date, List<AppAlertRecord> appAlertRecordList, String title, List<InstanceAlertValueResult> alertInstInfoList){\n        for(InstanceAlertValueResult instanceAlert : alertInstInfoList){\n            AppAlertRecord appAlertRecord = new AppAlertRecord();\n            appAlertRecord.setVisibleType(type.getVisibleType());\n            appAlertRecord.setImportantLevel(type.getImportantLevel());\n            appAlertRecord.setTitle(title);\n            appAlertRecord.setAppId(instanceAlert.getAppId());\n            appAlertRecord.setIp(instanceAlert.getInstanceInfo().getIp());\n            appAlertRecord.setPort(instanceAlert.getInstanceInfo().getPort());\n            StringBuilder sb = new StringBuilder();\n            if(type == AlertTypeEnum.INATANCE_EXCEPTION_STATE_MONITOR) {\n                sb.append(\"状态:\");\n                sb.append(instanceAlert.getInstanceInfo().getStatusDesc());\n                sb.append(\", 说明:\");\n                sb.append(instanceAlert.getOtherInfo());\n            }\n            if(type == AlertTypeEnum.INSTANCE_MINUTE_MONITOR) {\n                if(instanceAlert.getInstanceAlertConfig() != null && instanceAlert.getInstanceAlertConfig().getImportantLevel() != null){\n                    appAlertRecord.setImportantLevel(instanceAlert.getInstanceAlertConfig().getImportantLevel());\n                }\n                sb.append(\"属性值:\");\n                sb.append(instanceAlert.getInstanceAlertConfig().getAlertConfig());\n                sb.append(\", 说明:\");\n                sb.append(instanceAlert.getAlertMessage());\n                sb.append(\", 其他信息:\");\n                sb.append(instanceAlert.getOtherInfo());\n            }\n            if(type == AlertTypeEnum.POD_RESTART_INSTANCE_RECOVER) {\n                sb.append(\"实例恢复时间:\");\n                sb.append(instanceAlert.getOtherInfo());\n            }\n            appAlertRecord.setContent(sb.toString());\n            appAlertRecord.setCreateTime(date);\n            appAlertRecordList.add(appAlertRecord);\n        }\n    }\n\n    /**\n     * 根据机器操作结果列表生成报警信息\n     * @param type\n     * @param appAlertRecordList\n     * @param title\n     * @param recoverInstInfo\n     */\n    private void generateAlertRecordByOPerationList(AlertTypeEnum type, Date date, List<AppAlertRecord> appAlertRecordList, String title, List<OperationAlertValueResult> recoverInstInfo){\n        for(OperationAlertValueResult operationAlertValueResult : recoverInstInfo){\n            AppAlertRecord appAlertRecord = new AppAlertRecord();\n            appAlertRecord.setVisibleType(type.getVisibleType());\n            appAlertRecord.setImportantLevel(type.getImportantLevel());\n            appAlertRecord.setTitle(title);\n            MachineInfo machineInfo = operationAlertValueResult.getMachineInfo();\n            StringBuilder sb = new StringBuilder();\n            if(machineInfo != null){\n                appAlertRecord.setIp(machineInfo.getIp());\n                sb.append(\"ip:\");\n                sb.append(machineInfo.getIp());\n                sb.append(\", real_ip:\");\n                sb.append(machineInfo.getRealIp());\n                sb.append(\", machineInfo(内存:\");\n                sb.append(machineInfo.getMem());\n                sb.append(\", cpu:\");\n                sb.append(machineInfo.getCpu());\n                sb.append(\", 备注说明:\");\n                sb.append(machineInfo.getExtraDesc());\n                sb.append(\"), \");\n            }\n            sb.append(\"操作:\");\n            sb.append(operationAlertValueResult.getType());\n            sb.append(\", staus:\");\n            sb.append(operationAlertValueResult.getStatus());\n            sb.append(\", message:\");\n            sb.append(operationAlertValueResult.getMessage());\n            appAlertRecord.setCreateTime(date);\n            appAlertRecord.setContent(sb.toString());\n            appAlertRecordList.add(appAlertRecord);\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppAutoCapacityServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.dao.AppCapacityMonitorDao;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.entity.AppCapacityMonitor;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.InstanceStats;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.service.AppAutoCapacityService;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.UserService;\nimport freemarker.template.Configuration;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.time.DateUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.OptionalLong;\nimport java.util.stream.Collectors;\n\n/**\n * 容量监控\n * @author zengyizhao\n * @Date 2022年10月9日\n */\n@Slf4j\n@Service(\"appAutoCapacityService\")\npublic class AppAutoCapacityServiceImpl implements AppAutoCapacityService {\n\n    @Autowired\n    private AppCapacityMonitorDao appCapacityMonitorDao;\n\n    /**\n     * 邮箱报警\n     */\n    @Autowired\n    private EmailComponent emailComponent;\n\n    @Autowired\n    private AppDao appDao;\n\n    @Autowired\n    private AppService appService;\n\n    @Autowired\n    private UserService userService;\n\n    @Autowired\n    private AppStatsCenter appStatsCenter;\n\n    @Autowired\n    private Configuration configuration;\n\n    @Async(\"asyncExecutor\")\n    @Override\n    public void checkAndExpandCapacity(AppDesc appDesc, int appMemUsePercent, Map<InstanceInfo, InstanceStats> instanceStatsMap) {\n        try{\n            if(appDesc != null){\n                return;\n            }\n            Collection<InstanceStats> stats = instanceStatsMap.values();\n            stats = stats.stream().filter(instanceStats -> instanceStats != null).collect(Collectors.toList());\n            Long shardingMasterCount = stats.stream().filter(instanceStats -> instanceStats.getRole() == 1).count();\n            int shardingMasterNum = Long.valueOf(shardingMasterCount == null ? 0L : shardingMasterCount).intValue();\n            //保存当前内存使用\n            List<Map.Entry<InstanceInfo, InstanceStats>> collect = instanceStatsMap.entrySet().stream().filter(entry -> entry.getValue() != null && entry.getValue().getRole() == 1).collect(Collectors.toList());\n            long totalMem = collect.stream().mapToLong(entry -> entry.getValue().getMaxMemory()).sum();\n            long appUsedMem = collect.stream().mapToLong(entry -> entry.getValue().getUsedMemory()).sum();\n            long shardingUsedMem = 0;\n            OptionalLong shardingUsedMaxMem = collect.stream().mapToLong(entry -> entry.getValue().getUsedMemory()).max();\n            if(shardingUsedMaxMem.isPresent()){\n                shardingUsedMem = shardingUsedMaxMem.getAsLong();\n            }\n            OptionalLong shardingMaxMem = stats.stream().filter(instanceStats -> instanceStats.getRole() == 1).mapToLong(stat -> stat.getMaxMemory()).max();\n            long shardingMem = 0;\n            if(shardingMaxMem.isPresent()){\n                shardingMem = shardingMaxMem.getAsLong();\n            }\n            AppCapacityMonitor appCapacityMonitor = appCapacityMonitorDao.getAppCapacityMonitorByAppId(appDesc.getAppId());\n            //无历史信息\n            if(appCapacityMonitor == null){\n                //添加记录并不进行下方check\n                this.saveAppCapacityMonitor(appDesc, shardingMasterNum, totalMem, appUsedMem, shardingMem, shardingUsedMem);\n                return;\n            }\n            //更新记录\n            this.updateAppCapacityMonitor(appCapacityMonitor.getId(), appUsedMem, totalMem, shardingUsedMem, shardingMem);\n            //判断是否减小过容量，更新缩容记录及时间\n            if(totalMem < appCapacityMonitor.getCurMem()){\n                this.cleanAppCapacityReduceSchedule(appCapacityMonitor.getId());\n            }\n        } catch(Exception exp){\n            log.error(\"checkAndExpandCapacity app:{} error:{} \", appDesc, exp.getMessage());\n        }\n    }\n\n    private void saveAppCapacityMonitor(AppDesc appDesc, int shardingMasterNum, long mem, long appMemUsed, long shardingMem, long shardingUsedMem) {\n        ExpandConfig expandConfig = this.getExpandConfig(mem);\n        AppCapacityMonitor appCapacityMonitor = new AppCapacityMonitor();\n        appCapacityMonitor.setAppId(appDesc.getAppId());\n        appCapacityMonitor.setShardingMasterNum(shardingMasterNum);\n        appCapacityMonitor.setMem(mem);\n        appCapacityMonitor.setCurMem(mem);\n        appCapacityMonitor.setMemUsed(appMemUsed);\n        appCapacityMonitor.setShardingMem(shardingMem);\n        appCapacityMonitor.setCurShardingMem(shardingMem);\n        appCapacityMonitor.setShardingMemUsed(shardingUsedMem);\n        appCapacityMonitor.setExpandMemPercent(expandConfig.getExpandMemPercent());\n        appCapacityMonitor.setExpandRatio(expandConfig.getExpandRatio());\n        appCapacityMonitor.setExpandRatioTotal(expandConfig.getExpandRatioTotal());\n        appCapacityMonitor.setExpandCount(expandConfig.getExpandCount());\n        appCapacityMonitor.setIsExpand(1);\n        appCapacityMonitor.setIsReduce(1);\n        appCapacityMonitor.setUpdateTime(new Date());\n        appCapacityMonitorDao.save(appCapacityMonitor);\n    }\n\n    private void updateAppCapacityMonitor(long id, long appMemUsed, long curMem, long shardingUsedMem, long curShardingMem) {\n        ExpandConfig expandConfig = this.getExpandConfig(curMem);\n        AppCapacityMonitor appCapacityMonitor = new AppCapacityMonitor();\n        appCapacityMonitor.setId(id);\n        appCapacityMonitor.setMemUsed(appMemUsed);\n        appCapacityMonitor.setCurMem(curMem);\n        appCapacityMonitor.setShardingMemUsed(shardingUsedMem);\n        appCapacityMonitor.setCurShardingMem(curShardingMem);\n        appCapacityMonitor.setExpandMemPercent(expandConfig.getExpandMemPercent());\n        appCapacityMonitor.setExpandRatio(expandConfig.getExpandRatio());\n        appCapacityMonitor.setExpandRatioTotal(expandConfig.getExpandRatioTotal());\n        appCapacityMonitor.setExpandCount(expandConfig.getExpandCount());\n        appCapacityMonitorDao.update(appCapacityMonitor);\n    }\n\n    private void cleanAppCapacityReduceSchedule(long id) {\n        AppCapacityMonitor appCapacityMonitor = new AppCapacityMonitor();\n        appCapacityMonitor.setId(id);\n        appCapacityMonitor.setScheduleStatus(0);\n        appCapacityMonitorDao.updateAppCapacityReduceSchedule(appCapacityMonitor);\n    }\n\n    @Override\n    public void updateAppCapacityMonitor(AppCapacityMonitor appCapacityMonitor) {\n        appCapacityMonitorDao.update(appCapacityMonitor);\n    }\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    private static class ExpandConfig{\n\n        //初始内存下限G\n        private long memSmall;\n\n        //初始内存上限G\n        private long memGreat;\n\n        //当日扩容次数限制\n        private int expandCount;\n\n        //扩容内存使用率百分比\n        private int expandMemPercent;\n\n        //扩容比率\n        private int expandRatio;\n\n        //总扩容比率\n        private int expandRatioTotal;\n\n        public ExpandConfig(String[] params){\n            if(params != null && params.length == 6){\n                this.memSmall = Long.valueOf(params[0]);\n                this.memGreat = Long.valueOf(params[1]);\n                this.expandCount = Integer.valueOf(params[2]);\n                this.expandMemPercent = Integer.valueOf(params[3]);\n                this.expandRatio = Integer.valueOf(params[4]);\n                this.expandRatioTotal = Integer.valueOf(params[5]);\n            }\n        }\n    }\n\n    private ExpandConfig getExpandConfig(long mem){\n        String redisExpandConfig = ConstUtils.REDIS_EXPAND_CAPACITY_CONFIG;\n        List<ExpandConfig> defaultExpandConfigList = new ArrayList<>();\n        if(StringUtils.isNotBlank(redisExpandConfig)){\n            String[] expandConfigs = redisExpandConfig.split(\";\");\n            List<String> list = Arrays.asList(expandConfigs);\n            list.stream().forEach(expandConfig -> defaultExpandConfigList.add(new ExpandConfig(expandConfig.split(\",\"))));\n        }\n        ExpandConfig meetExpandConfig = null;\n        Optional<ExpandConfig> expandConfigOptional = defaultExpandConfigList.stream().filter(expandConfig -> (expandConfig.getMemSmall() * 1024 * 1024 * 1024 < mem && expandConfig.getMemGreat() * 1024 * 1024 * 1024 >= mem)).findFirst();\n        if(expandConfigOptional.isPresent()){\n            meetExpandConfig = expandConfigOptional.get();\n        }\n        return meetExpandConfig;\n    }\n\n    @Override\n    public void updateAppMemUsedHistory() {\n        List<AppDesc> appDescList = appDao.getOnlineApps();\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMdd0000\");\n        Date date = new Date();\n        Date beginDate = DateUtils.addDays(date, 0 - ConstUtils.REDIS_MEM_USED_MAX_DAYS);\n        String beginTime = sdf.format(beginDate);\n        String endTime = sdf.format(date);\n        appDescList.stream().forEach(appDesc -> {\n            Long usedMemoryMax = appStatsCenter.getUsedMemoryMaxByTimeBetween(appDesc.getAppId(), Long.valueOf(beginTime), Long.valueOf(endTime));\n            if(usedMemoryMax != null && usedMemoryMax > 0){\n                appCapacityMonitorDao.updateAppUsedMemHistory(appDesc.getAppId(), usedMemoryMax);\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppImportServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.dao.AppImportDao;\nimport com.sohu.cache.entity.AppImport;\nimport com.sohu.cache.web.service.AppImportService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @Author: rucao\n * @Date: 2021/1/8 上午11:43\n */\n@Slf4j\n@Service\npublic class AppImportServiceImpl implements AppImportService {\n    @Autowired\n    private AppImportDao appImportDao;\n\n    @Override\n    public List<AppImport> getImportAppList(Integer status) {\n        List<AppImport> appImportList = new ArrayList<>();\n        try {\n            appImportList = appImportDao.getAppImports(status);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return appImportList;\n    }\n\n    @Override\n    public int save(AppImport appImport) {\n        try {\n            return appImportDao.save(appImport);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return -1;\n    }\n\n    @Override\n    public int update(AppImport appImport) {\n        try {\n            return appImportDao.update(appImport);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return -1;\n    }\n\n    @Override\n    public AppImport appImport(Long id) {\n        AppImport appImport = get(id);\n        if (appImport == null) {\n\n        }\n\n        return null;\n    }\n\n    @Override\n    public AppImport get(Long id) {\n        try {\n            return appImportDao.get(id);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppRedisCommandCheckServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.constant.RedisConstant;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.dao.MachineDao;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.entity.MachineInfo;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.enums.DirEnum;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.service.AppRedisCommandCheckService;\nimport com.sohu.cache.web.vo.*;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport redis.clients.jedis.Jedis;\n\nimport javax.annotation.Resource;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/29 11:34\n * @Description: redis命令检测\n */\n@Slf4j\n@Service\npublic class AppRedisCommandCheckServiceImpl implements AppRedisCommandCheckService {\n\n    @Autowired\n    private InstanceDao instanceDao;\n\n    @Autowired\n    private RedisCenter redisCenter;\n\n    @Autowired\n    private AssistRedisService assistRedisService;\n\n    private static final String REDIS_COMMAND_CHECK_RESULT_SAVE_KEY = \"redis:command:check:result\";\n\n    private static final String REDIS_COMMAND_CHECK_RESULT_KEY = \"redis:command:check:result:\";\n\n    @Resource\n    private MachineDao machineDao;\n\n    @Resource\n    private EmailComponent emailComponent;\n\n    /**\n     *\n     * @param checkVo\n     */\n    @Override\n    public RedisCommandCheckResult checkRedisCommand(AppUser appUser, AppRedisCommandCheckVo checkVo){\n        RedisCommandCheckResult redisCommandCheckResult = new RedisCommandCheckResult();\n        BeanUtils.copyProperties(checkVo, redisCommandCheckResult);\n        redisCommandCheckResult.setCreateTime(new Date());\n        redisCommandCheckResult.setSuccess(true);\n        redisCommandCheckResult.setUserName(appUser.getChName() == null ? appUser.getName() : appUser.getChName());\n        long beginTime = System.currentTimeMillis();\n        StringBuilder stringBuilder = new StringBuilder();\n        List<InstanceInfo> allInstanceList = getInstanceListToCheck(checkVo.getMachineIps(), checkVo.getPodIp());\n        AppRedisCommandCheckResult checkResult = new AppRedisCommandCheckResult();\n        List<InstanceRedisCommandCheckResult> instanceCheckResultList = new ArrayList<>();\n        checkResult.setMachineIps(checkVo.getMachineIps());\n        checkResult.setCreateTime(new Date());\n        checkResult.setPodIp(checkVo.getPodIp());\n        checkResult.setCommand(checkVo.getCommand());\n        checkResult.setSuccess(false);\n\n        //遍历实例并分别处理\n        for (InstanceInfo instanceInfo : allInstanceList){\n            BooleanEnum master = redisCenter.isMaster(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n            if(master.equals(BooleanEnum.OTHER)){\n                continue;\n            }\n            InstanceRedisCommandCheckResult instanceCheckResult = new InstanceRedisCommandCheckResult();\n            instanceCheckResultList.add(instanceCheckResult);\n            instanceCheckResult.setInstanceInfo(instanceInfo);\n            String s = handleByInstance(instanceInfo, checkVo);\n            if(StringUtil.isBlank(s)){\n                instanceCheckResult.setSuccess(true);\n                continue;\n            }\n            redisCommandCheckResult.setSuccess(false);\n            instanceCheckResult.setSuccess(false);\n            instanceCheckResult.setMessage(s);\n            stringBuilder.append(s);\n            stringBuilder.append(\"\\r\\n\");\n        }\n        checkResult.setInstanceCheckList(instanceCheckResultList);\n        log.info(\"耗时：{}ms，结果信息：{}\", System.currentTimeMillis() - beginTime, checkResult);\n        UUID uuid = UUID.randomUUID();\n        redisCommandCheckResult.setKey(uuid.toString());\n        this.saveRedisCommandCheckResult(redisCommandCheckResult, checkResult);\n        return redisCommandCheckResult;\n    }\n\n    //machineIps\n    //podIp\n    //命令\n    private List<InstanceInfo> getInstanceListToCheck(String machineIps, String podIp){\n        List<InstanceInfo> allInstanceList = new ArrayList<>();\n        if(StringUtils.isNotEmpty(podIp)){\n            allInstanceList = getInstanceInfoList(podIp);\n        }else if(machineIps != null){\n            List<String> machineList = getMachineIpist(machineIps);\n            //遍历machine list，分别获取machine下 pod list\n            List<MachineInfo> allMachineList =  new ArrayList<>();\n            for (String machineIp: machineList) {\n                List<MachineInfo> vmPodList = getVmPodList(machineIp);\n                if(!org.springframework.util.CollectionUtils.isEmpty(vmPodList)){\n                    allMachineList.addAll(vmPodList);\n                }\n            }\n            //分别获取 pod下 redis intance list\n            for (MachineInfo machineInfo : allMachineList) {\n                List<InstanceInfo> instanceInfoList = getInstanceInfoList(machineInfo.getIp());\n                if(!CollectionUtils.isEmpty(instanceInfoList)){\n                    allInstanceList.addAll(instanceInfoList);\n                }\n            }\n        }\n        return allInstanceList;\n    }\n\n    /**\n     * 获取机器ip列表\n     * @return\n     */\n    private List<String> getMachineIpist(String machineIps){\n        String[] split = machineIps.split(\";\");\n        List<String> ipList = Arrays.asList(split);\n        return ipList;\n    }\n\n    /**\n     * 获取机器pod列表\n     * @return\n     */\n    private List<MachineInfo> getVmPodList(String realIp){\n        List<MachineInfo> machineInfoList = machineDao.getMachineInfoByCondition(null, -1, -1, null , -1, realIp);\n        return machineInfoList;\n    }\n\n    /**\n     * 根据机器ip获取instance列表\n     * @param ip\n     * @return\n     */\n    private List<InstanceInfo> getInstanceInfoList(String ip){\n        //获取一台机器的所有实例\n        List<InstanceInfo> instListByIp = instanceDao.getInstListByIp(ip);\n        return instListByIp;\n    }\n\n    /**\n     * 根据实例进行处理\n     * @param instanceInfo\n     * @return\n     */\n    private String handleByInstance(InstanceInfo instanceInfo, AppRedisCommandCheckVo checkVo){\n        long startTime = 0L;\n        //执行命令\n        if(\"bgsave\".equals(checkVo.getCommand())){\n            startTime = sendBgsaveCommand(instanceInfo);\n        }else if(\"bgrewriteaof\".equals(checkVo.getCommand())) {\n            startTime = sendBgrewriteaofCommand(instanceInfo);\n        }\n        if(startTime == 0L){\n            return String.format(\"实例appId：%s, ip:%s, port:%s，处理失败：发送命令失败\", instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n        }\n        //等待命令执行结束或定时等待\n        String checkInfo = null;\n        if(\"bgsave\".equals(checkVo.getCommand())){\n            checkInfo = checkByInfo(instanceInfo, startTime, checkVo.getMaxTry(), RedisConstant.Persistence, \"rdb_bgsave_in_progress\", \"0\");\n        }else if(\"bgrewriteaof\".equals(checkVo.getCommand())) {\n            checkInfo = checkByInfo(instanceInfo, startTime, checkVo.getMaxTry(), RedisConstant.Persistence, \"aof_rewrite_in_progress\", \"0\");\n        }\n        log.info(\"实例appId：{}, ip:{}, port:{}，执行checkByInfo用时：{}ms，结果：{}\", instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), System.currentTimeMillis() - startTime, checkInfo);\n        if(checkInfo != null){\n            return checkInfo;\n        }\n        //检测校验\n        checkInfo = checkByLog(instanceInfo, \"crashed by signal\", 10);\n        if(checkInfo == null){\n            log.info(\"实例appId：{}, ip:{}, port:{}，未出现指定信息\", instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n        }else{\n            log.info(\"实例appId：{}, ip:{}, port:{}，出现错误信息：{}\", instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), checkInfo);\n        }\n        return checkInfo;\n    }\n\n    /**\n     * 发送bgrewriteaof命令并获取返回信息\n     * @param instanceInfo\n     * @return\n     */\n    private long sendBgrewriteaofCommand(InstanceInfo instanceInfo){\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        long appId = instanceInfo.getAppId();\n        StringBuilder infoBuilder = new StringBuilder();\n        Jedis jedis = null;\n        long start = 0L;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            start = System.currentTimeMillis();\n            String info = jedis.bgrewriteaof();\n            log.info(\"实例appId：{}, ip:{}, port:{}，bgrewriteaof结果：{}\", appId, host, port, info);\n        } catch (Exception e) {\n            start = 0L;\n            log.error(\"实例appId：{}, host:{}, port:{}, redis-bgrewriteaof errorMsg:{}\", appId, host, port, e.getMessage());\n        } finally {\n            if (jedis != null){\n                jedis.close();\n            }\n        }\n        return start;\n    }\n\n    /**\n     * 发送命令并获取返回信息\n     * @param instanceInfo\n     * @return\n     */\n    private long sendBgsaveCommand(InstanceInfo instanceInfo){\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        long appId = instanceInfo.getAppId();\n        StringBuilder infoBuilder = new StringBuilder();\n        Jedis jedis = null;\n        long start = 0L;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            start = System.currentTimeMillis();\n            String info = jedis.bgsave();\n            log.info(\"实例appId：{}, ip:{}, port:{}，bgrewriteaof结果：{}\", appId, host, port, info);\n        } catch (Exception e) {\n            start = 0L;\n            log.error(\"实例appId：{}, host:{}, port:{}, redis-bgrewriteaof errorMsg:{}\", appId, host, port, e.getMessage());\n        } finally {\n            if (jedis != null){\n                jedis.close();\n            }\n        }\n        return start;\n    }\n\n    private String getMethod(String command) {\n        return null;\n    }\n\n    /**\n     * 发送info命令查看相关指标信息,进行监测\n     * @param instanceInfo 实例信息\n     * @param startTime 开始时间戳\n     * @param maxTry 最大重试次数\n     * @param redisConstant redis info中一级指标常量\n     * @param indicateName 配置项\n     * @param expectValue 期望值\n     * @return\n     */\n    private String checkByInfo(InstanceInfo instanceInfo, long startTime, Integer maxTry, RedisConstant redisConstant, String indicateName, String expectValue){\n        String result = null;\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        long appId = instanceInfo.getAppId();\n        Jedis jedis = null;\n        boolean reConfirmFlag = true;\n        long costTime = 0L;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            int retryTimes = 1;\n            long sleepTime = 500L;\n            boolean printMemory = true;\n            String configValue = null;\n            while(reConfirmFlag && retryTimes < maxTry){\n                try {\n                    Thread.sleep(retryTimes * sleepTime > 3000L ? 3000L : retryTimes * sleepTime );\n                } catch (InterruptedException e) {\n\n                }\n                String info = jedis.info(\"all\");\n                Map<RedisConstant, Map<String, Object>> infoStats = processRedisStats(info);\n                Map<String, Object> persistenceMap = infoStats.get(redisConstant);\n                if(printMemory){\n                    log.info(\"实例appId：{}, ip:{}, port:{}，retryTimes:{}, info : {}\", appId, host, port, retryTimes, info);\n                    printMemory = false;\n                }\n                configValue = MapUtils.getString(persistenceMap, indicateName);\n                if(expectValue.equals(configValue)){\n                    reConfirmFlag = false;\n                    costTime = System.currentTimeMillis() - startTime;\n                }\n                retryTimes++;\n            }\n            if(reConfirmFlag){\n                result = String.format(\"实例appId：%s, ip:%s, port:%s, redis-checkByInfo, configName:%s, expectValue:%s, configValue:%s\", appId, host, port, indicateName, expectValue, configValue);\n            }\n            log.info(\"实例appId：{}, ip:{}, port:{}，重试次数：{}，配置项：{}，期望值：{}，实际值：{} 最终结果(结束false/未结束true)：{}，耗时：{}ms\",\n                    instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), retryTimes - 1, indicateName, expectValue, configValue, reConfirmFlag, costTime);\n        } catch (Exception e) {\n            e.printStackTrace();\n            log.error(String.format(\"实例appId：%s, ip:%s, port:%s, redis-checkByInfo errorMsg:\", appId, host, port), e);\n            result = String.format(\"实例appId：%s, ip:%s, port:%s, redis-checkByInfo errorMsg:%s\", appId, host, port, e.getMessage());\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return result;\n    }\n\n    /**\n     * 查看实例日志中，aof rewrite是否被terminated 或 redis是否crashed by signal: 11\n     * @param instanceInfo\n     * @return\n     */\n    private String checkByLog(InstanceInfo instanceInfo, String expectValue, int minuteInternalAllow){\n        String recentLog = getRecentLog(instanceInfo);\n        if(StringUtil.isBlank(recentLog)){\n            return \"实例appId:\" + instanceInfo.getAppId() + \"-ip:\" + instanceInfo.getIp() + \"-port:\" + instanceInfo.getPort() + \"-结果：无日志，请确认实例是否正常并人工判定\";\n        }\n        String[] logArray = recentLog.split(System.getProperty(\"line.separator\"));\n        if(logArray != null && logArray.length > 1){\n            for(int i = logArray.length - 1; i >= 0; i--){\n                String log = logArray[i];\n                if(log != null && log.contains(expectValue)){\n                    if(!checkLogTimeMeet(log, minuteInternalAllow)){\n                        continue;\n                    }\n                    return  \"实例appId:\" + instanceInfo.getAppId() + \"-ip:\" + instanceInfo.getIp() + \"-port:\" + instanceInfo.getPort() + \"-结果log: \" + log;\n                }\n            }\n        }\n        return null;\n    }\n\n    private boolean checkLogTimeMeet(String log, int minuteInternalAllow) {\n        boolean flag = false;\n        Calendar calendar = Calendar.getInstance(Locale.US);\n        int year = 0;\n        try{\n            calendar.add(Calendar.MINUTE, -minuteInternalAllow);\n            year = calendar.get(Calendar.YEAR);\n        }catch (Exception e){\n            return flag;\n        }\n        SimpleDateFormat sdf = new SimpleDateFormat(\"dd MMM yyyy hh:mm:ss.SSS\", Locale.US);\n        String[] strArray = log.split(\" \");\n        if (strArray != null && strArray.length > 5) {\n            StringBuilder sb = new StringBuilder();\n            int strSize = 5;\n            for (int i = 1; i < strSize; i++) {\n                String str = strArray[i];\n                if(i == 3 && !str.equals(String.valueOf(year))){\n                    sb.append(year);\n                    sb.append(\" \");\n                    strSize = 4;\n                }\n                sb.append(str);\n                sb.append(\" \");\n            }\n            sb.deleteCharAt(sb.length() - 1);\n            try {\n                Date parse = sdf.parse(sb.toString());\n                flag = !parse.before(calendar.getTime());\n            } catch (ParseException e) {\n\n            }\n        }\n        return flag;\n    }\n\n    /**\n     * 发送info命令查看aof rewrite是否结束\n     * @param instanceInfo\n     * @return\n     */\n    private boolean checkInProgress(InstanceInfo instanceInfo, long startTime){\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        long appId = instanceInfo.getAppId();\n        Jedis jedis = null;\n        boolean reConfirmFlag = true;\n        long costTime = 0L;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n            int retryTimes = 1;\n            long sleepTime = 500L;\n            boolean printMemory = true;\n            while(reConfirmFlag && retryTimes < 40){\n                try {\n                    Thread.sleep(retryTimes * sleepTime > 3000L ? 3000L : retryTimes * sleepTime );\n                } catch (InterruptedException e) {\n\n                }\n                String info = jedis.info(\"all\");\n                Map<RedisConstant, Map<String, Object>> infoStats = processRedisStats(info);\n                Map<String, Object> persistenceMap = infoStats.get(RedisConstant.Persistence);\n                if(printMemory){\n                    Map<String, Object> memoryMap = infoStats.get(RedisConstant.Memory);\n                    String used_memory_human = MapUtils.getString(memoryMap, \"used_memory_human\");\n                    log.info(\"实例appId：{}, ip:{}, port:{}，retryTimes:{}, info used_memory_human: {}\", appId, host, port, retryTimes, used_memory_human);\n                    printMemory = false;\n                }\n                String aof_rewrite_in_progress = MapUtils.getString(persistenceMap, \"aof_rewrite_in_progress\");\n                if(\"0\".equals(aof_rewrite_in_progress)){\n                    reConfirmFlag = false;\n                    long lastCostTime = MapUtils.getLong(persistenceMap, \"aof_last_rewrite_time_sec\");\n                    if(lastCostTime != 0L && lastCostTime != -1L){\n                        costTime = lastCostTime * 1000;\n                    }else{\n                        costTime = System.currentTimeMillis() - startTime;\n                    }\n                }\n                retryTimes++;\n            }\n            log.info(\"实例appId：{}, ip:{}, port:{}，重试次数：{}， 最终结果(结束false/未结束true)：{}，耗时：{}ms\", instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), retryTimes - 1, reConfirmFlag, costTime);\n        } catch (Exception e) {\n            log.error(\"实例appId：{}, host:{}, port:{}, redis-checkInProgress errorMsg:{}\", appId, host, port, e.getMessage());\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return reConfirmFlag;\n    }\n\n    /**\n     * 处理redis统计信息\n     *\n     * @param statResult 统计结果串\n     */\n    private Map<RedisConstant, Map<String, Object>> processRedisStats(String statResult) {\n        Map<RedisConstant, Map<String, Object>> redisStatMap = new HashMap<RedisConstant, Map<String, Object>>();\n        String[] data = statResult.split(\"\\r\\n\");\n        String key;\n        int i = 0;\n        int length = data.length;\n        while (i < length) {\n            if (data[i].contains(\"#\")) {\n                int index = data[i].indexOf('#');\n                key = data[i].substring(index + 1);\n                ++i;\n                RedisConstant redisConstant = RedisConstant.value(key.trim());\n                if (redisConstant == null) {\n                    continue;\n                }\n                Map<String, Object> sectionMap = new LinkedHashMap<String, Object>();\n                while (i < length && data[i].contains(\":\")) {\n                    String[] pair = StringUtils.splitByWholeSeparator(data[i], \":\");\n                    sectionMap.put(pair[0], pair[1]);\n                    i++;\n                }\n                redisStatMap.put(redisConstant, sectionMap);\n            } else {\n                i++;\n            }\n        }\n        return redisStatMap;\n    }\n\n    /**\n     * 查看实例日志中，aof rewrite是否被terminated 或 redis是否crashed by signal: 11\n     * @param instanceInfo\n     * @return\n     */\n    private String getRecentLog(InstanceInfo instanceInfo){\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        int type = instanceInfo.getType();\n        String logType = \"\";\n        if (TypeUtil.isRedisDataType(type)) {\n            logType = \"redis-\";\n        } else if (TypeUtil.isRedisSentinel(type)) {\n            logType = \"redis-sentinel-\";\n        }\n        String remoteFilePath = getMachineRelativeDir(host, DirEnum.LOG_DIR.getValue()) + logType + port + \"-*.log\";\n        StringBuilder command = new StringBuilder();\n        command.append(\"/usr/bin/tail -n\").append(1000).append(\" \").append(remoteFilePath);\n        try {\n            return SSHUtil.execute(host, command.toString());\n        } catch (SSHException e) {\n            log.error(\"实例appId：{}, host:{}, port:{}, redis-checkInProgress errorMsg:{}\", instanceInfo.getAppId(), host, port, e.getMessage());\n            return null;\n        }\n    }\n\n    /**\n     * 获取日志相对路径\n     * @param host\n     * @param dirType\n     * @return\n     */\n    public String getMachineRelativeDir(String host, int dirType) {\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n        if (machineInfo != null && machineInfo.isK8sMachine(machineInfo.getK8sType())) {\n            return MachineProtocol.getK8sDir(host, dirType);\n        }\n        return MachineProtocol.getDir(dirType);\n    }\n\n    @Override\n    public void saveRedisCommandCheckResult(RedisCommandCheckResult redisCommandCheckResult, AppRedisCommandCheckResult checkResult) {\n        if(redisCommandCheckResult != null){\n            Long llen = assistRedisService.llen(REDIS_COMMAND_CHECK_RESULT_SAVE_KEY);\n            if(llen >= 20){\n                String configResult = assistRedisService.lpop(REDIS_COMMAND_CHECK_RESULT_SAVE_KEY);\n                RedisCommandCheckResult toRemoveResult = JSONObject.parseObject(configResult, RedisCommandCheckResult.class);\n                assistRedisService.remove(REDIS_COMMAND_CHECK_RESULT_KEY + toRemoveResult.getKey());\n            }\n            assistRedisService.rpush(REDIS_COMMAND_CHECK_RESULT_SAVE_KEY, JSONObject.toJSONString(redisCommandCheckResult));\n            if(checkResult != null && CollectionUtils.isNotEmpty(checkResult.getInstanceCheckList())){\n                assistRedisService.set(REDIS_COMMAND_CHECK_RESULT_KEY + redisCommandCheckResult.getKey(), JSONObject.toJSONString(checkResult));\n            }\n        }\n    }\n\n    @Override\n    public List<RedisCommandCheckResult> getRedisCommandCheckResult() {\n        List<RedisCommandCheckResult> lists = new ArrayList<>();\n        List<String> lrange = assistRedisService.lrange(REDIS_COMMAND_CHECK_RESULT_SAVE_KEY, 0, 20);\n        if(CollectionUtils.isNotEmpty(lrange)){\n            for (String str : lrange) {\n                lists.add(JSONObject.parseObject(str, RedisCommandCheckResult.class));\n            }\n        }\n        lists = lists.stream().sorted(Comparator.comparing(RedisCommandCheckResult::getCreateTime).reversed()).collect(Collectors.toList());\n        return lists;\n    }\n\n    @Override\n    public AppRedisCommandCheckResult getRedisCommandCheckDetailResult(String uuid) {\n        AppRedisCommandCheckResult checkResult = null;\n        String resultStr = assistRedisService.get(REDIS_COMMAND_CHECK_RESULT_KEY + uuid);\n        if(StringUtils.isNotEmpty(resultStr)){\n            checkResult = JSONObject.parseObject(resultStr, AppRedisCommandCheckResult.class);\n        }\n        return checkResult;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppRedisConfigCheckServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.AppStatusEnum;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppSearch;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.CompareTypeEnum;\nimport com.sohu.cache.web.service.AppRedisConfigCheckService;\nimport com.sohu.cache.web.vo.AppRedisConfigCheckResult;\nimport com.sohu.cache.web.vo.AppRedisConfigCheckVo;\nimport com.sohu.cache.web.vo.InstanceRedisConfigCheckResult;\nimport com.sohu.cache.web.vo.RedisConfigCheckResult;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.exceptions.JedisConnectionException;\nimport redis.clients.jedis.exceptions.JedisDataException;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/26 17:34\n * @Description: redis配置检测\n */\n@Slf4j\n@Service\npublic class AppRedisConfigCheckServiceImpl implements AppRedisConfigCheckService {\n\n    @Autowired\n    private AppDao appDao;\n\n    @Autowired\n    private InstanceDao instanceDao;\n\n    @Autowired\n    private RedisCenter redisCenter;\n\n    @Autowired\n    private AssistRedisService assistRedisService;\n\n    private static final String REDIS_CHECK_RESULT_SAVE_KEY = \"redis:check:result\";\n\n    private static final String REDIS_CHECK_RESULT_KEY = \"redis:check:result:\";\n\n    @Override\n    public RedisConfigCheckResult checkRedisConfig(AppUser appUser, AppRedisConfigCheckVo configCheckVo) {\n        RedisConfigCheckResult redisConfigCheckResult = new RedisConfigCheckResult();\n        redisConfigCheckResult.setUserName(appUser.getChName() == null ? appUser.getName() : appUser.getChName());\n        redisConfigCheckResult.setCreateTime(new Date());\n        redisConfigCheckResult.setSuccess(true);\n        BeanUtils.copyProperties(configCheckVo, redisConfigCheckResult);\n        List<AppRedisConfigCheckResult> configCheckResults = new ArrayList<>();\n        //获取待检测配置项\n        String configName = configCheckVo.getConfigName();\n        //trim一下首尾\n        String expectValue = configCheckVo.getExpectValue();\n        //获取应用（根据redis版本，用户指定应用id，全部）\n        List<AppDesc> appDescList = getAppByCondition(configCheckVo.getAppId(), configCheckVo.getVersionId());\n        //获取检测规则, 并校验\n        int compareType = configCheckVo.getCompareType();\n        CompareTypeEnum compareTypeEnum = getCompareType(compareType);\n        if(compareTypeEnum == null){\n            return null;\n        }\n        //递归对每个应用进行处理(1.获取应用下实例; 2.根据检测项获取配置值，与检测规则对比，得出结果; 3.每个应用的配置值进行对比，并返回校验失败的信息)\n        for(AppDesc appDesc : appDescList){\n            AppRedisConfigCheckResult appCheckResult = new AppRedisConfigCheckResult();\n            appCheckResult.setAppDesc(appDesc);\n            appCheckResult.setCompareType(compareType);\n            appCheckResult.setConfigName(configName);\n            appCheckResult.setExpectValue(expectValue);\n            appCheckResult.setVersionId(configCheckVo.getVersionId());\n            appCheckResult.setSuccess(true);\n            appCheckResult.setCreateTime(new Date());\n            List<InstanceRedisConfigCheckResult> instanceCheckResultList = new ArrayList<>();\n            List<InstanceInfo> instanceInfoList = getInstanceInfoListByApp(appDesc.getAppId());\n            for (InstanceInfo instanceInfo : instanceInfoList) {\n                BooleanEnum master = redisCenter.isMaster(appDesc.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n                if(master.equals(BooleanEnum.OTHER)){\n                    continue;\n                }\n                InstanceRedisConfigCheckResult instanceCheckResult = new InstanceRedisConfigCheckResult();\n                instanceCheckResult.setInstanceInfo(instanceInfo);\n                String realValue = getConfigByCommand(appDesc, instanceInfo, configName);\n                boolean checkResult = checkConfigSatisfy(expectValue, realValue, compareTypeEnum);\n                if(!checkResult){\n                    redisConfigCheckResult.setSuccess(false);\n                    appCheckResult.setSuccess(false);\n                    instanceCheckResult.setSuccess(false);\n                    instanceCheckResult.setConfigName(configName);\n                    instanceCheckResult.setExpectValue(expectValue);\n                    instanceCheckResult.setRealValue(realValue);\n                }else{\n                    instanceCheckResult.setSuccess(true);\n                }\n                instanceCheckResultList.add(instanceCheckResult);\n            }\n            appCheckResult.setInstanceCheckList(instanceCheckResultList);\n            configCheckResults.add(appCheckResult);\n        }\n        UUID uuid = UUID.randomUUID();\n        redisConfigCheckResult.setKey(uuid.toString());\n        this.saveRedisConfigCheckResult(redisConfigCheckResult, configCheckResults);\n        return redisConfigCheckResult;\n    }\n\n    /**\n     * 获取比较类型\n     * @param compareType\n     * @return\n     */\n    private CompareTypeEnum getCompareType(int compareType) {\n        return CompareTypeEnum.getByType(compareType);\n    }\n\n    /**\n     * 根据（appId, versionId）查询应用\n     * @param appId\n     * @param versionId\n     * @return\n     */\n    private List<AppDesc> getAppByCondition(Long appId, Integer versionId){\n        AppSearch appSearch =  new AppSearch();\n        appSearch.setAppId(appId);\n        appSearch.setVersionId(versionId);\n        appSearch.setAppStatus(AppStatusEnum.STATUS_PUBLISHED.getStatus());\n        List<AppDesc> allAppDescList = appDao.getAllAppDescList(appSearch);\n        return allAppDescList;\n    }\n\n    /**\n     * 查询应用下有效的实例信息\n     * @param appId\n     * @return\n     */\n    private List<InstanceInfo> getInstanceInfoListByApp(Long appId){\n        // 2.获取当前应用下redis实例信息\n        List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);;\n        //过滤出运行中的实例\n        instanceList = instanceList.stream().filter(instanceInfo -> instanceInfo.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()).collect(Collectors.toList());\n        return instanceList;\n    }\n\n    /**\n     * 通过config get获取配置项的值\n     * @param appDesc\n     * @param instanceInfo\n     * @param configName\n     * @return\n     */\n    private String getConfigByCommand(AppDesc appDesc, InstanceInfo instanceInfo, String configName){\n        String configValue = null;\n        // 1.获取连接\n        final Jedis jedis = redisCenter.getAdminJedis(appDesc.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), 5000, 5000);\n        try {\n            List<String> strings = jedis.configGet(configName);\n            if(strings != null && strings.size() > 1){\n                configValue = strings.get(1);\n            }\n        } catch (Exception e) {\n            if(e instanceof JedisConnectionException){\n                configValue = \"连接失败，未取到值\";\n            }else if(e instanceof JedisDataException){\n                if(e.getMessage().contains(\"ERR unknown command `CONFIG`, with args beginning with: `get`, \")){\n                    configValue = \"无此配置，未取到值\";\n                }\n            }\n            if(configValue == null){\n                configValue = \"异常，未取到值\";\n            }\n            log.error(\"getConfigByCommand\", e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return configValue;\n    }\n\n    private boolean checkConfigSatisfy(String expectValue, String realValue, CompareTypeEnum compareTypeEnum){\n        boolean resultFlag = false;\n        if(StringUtil.isBlank(expectValue)){\n            expectValue = \"\";\n        }\n        if(CompareTypeEnum.EQUAL.equals(compareTypeEnum)){\n            if(StringUtil.isBlank(realValue) && StringUtil.isBlank(expectValue)){\n                resultFlag = true;\n            }else {\n                resultFlag = expectValue.equals(realValue);\n            }\n            return resultFlag;\n        }else if(CompareTypeEnum.NOT_EQUAL.equals(compareTypeEnum)){\n            if(StringUtil.isBlank(realValue) && StringUtil.isBlank(expectValue)){\n                resultFlag = false;\n            }else {\n                resultFlag = !expectValue.equals(realValue);\n            }\n            return resultFlag;\n        }else {\n            if(StringUtil.isBlank(realValue) || StringUtil.isBlank(expectValue)){\n                return resultFlag;\n            }\n            Integer compare = null;\n            try{\n                long l = Long.parseLong(realValue);\n                long l1 = Long.parseLong(expectValue);\n                compare = Long.compare(l, l1);\n            }catch (Exception e){\n\n            }\n            if(compare == null){\n                try{\n                    double v = Double.parseDouble(realValue);\n                    double v1 = Double.parseDouble(expectValue);\n                    compare = Double.compare(v, v1);\n                }catch (Exception e){\n\n                }\n            }\n            if(compare == null){\n                return resultFlag;\n            }\n            if(CompareTypeEnum.LESS_THAN.equals(compareTypeEnum)){\n                return compare < 0;\n            }\n            if(CompareTypeEnum.MORE_THAN.equals(compareTypeEnum)){\n                return compare > 0;\n            }\n            return resultFlag;\n        }\n    }\n\n    @Override\n    public void saveRedisConfigCheckResult(RedisConfigCheckResult redisConfigCheckResult, List<AppRedisConfigCheckResult> resultList) {\n        if(redisConfigCheckResult != null){\n            Long llen = assistRedisService.llen(REDIS_CHECK_RESULT_SAVE_KEY);\n            if(llen >= 20){\n                String configResult = assistRedisService.lpop(REDIS_CHECK_RESULT_SAVE_KEY);\n                RedisConfigCheckResult toRemoveResult = JSONObject.parseObject(configResult, RedisConfigCheckResult.class);\n                assistRedisService.remove(REDIS_CHECK_RESULT_KEY + toRemoveResult.getKey());\n            }\n            assistRedisService.rpush(REDIS_CHECK_RESULT_SAVE_KEY, JSONObject.toJSONString(redisConfigCheckResult));\n            if(CollectionUtils.isNotEmpty(resultList)){\n                assistRedisService.set(REDIS_CHECK_RESULT_KEY + redisConfigCheckResult.getKey(), JSONObject.toJSONString(resultList));\n            }\n        }\n    }\n\n    @Override\n    public List<RedisConfigCheckResult> getRedisConfigCheckResult() {\n        List<RedisConfigCheckResult> lists = new ArrayList<>();\n        List<String> lrange = assistRedisService.lrange(REDIS_CHECK_RESULT_SAVE_KEY, 0, 20);\n        if(CollectionUtils.isNotEmpty(lrange)){\n            for (String str : lrange) {\n                lists.add(JSONObject.parseObject(str, RedisConfigCheckResult.class));\n            }\n        }\n        lists = lists.stream().sorted(Comparator.comparing(RedisConfigCheckResult::getCreateTime).reversed()).collect(Collectors.toList());\n        return lists;\n    }\n\n    @Override\n    public List<AppRedisConfigCheckResult> getRedisConfigCheckDetailResult(String uuid) {\n        List<AppRedisConfigCheckResult> configCheckResults = new ArrayList<>();\n        String resultStr = assistRedisService.get(REDIS_CHECK_RESULT_KEY + uuid);\n        if(!StringUtil.isBlank(resultStr)){\n            configCheckResults = JSONObject.parseArray(resultStr, AppRedisConfigCheckResult.class);\n        }\n        return configCheckResults;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppScrollRestartServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.sohu.cache.constant.AppDescEnum;\nimport com.sohu.cache.constant.InstanceStatusEnum;\nimport com.sohu.cache.dao.ConfigRestartRecordDao;\nimport com.sohu.cache.dao.InstanceConfigDao;\nimport com.sohu.cache.dao.MachineDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.RedisDeployCenter;\nimport com.sohu.cache.redis.enums.DirEnum;\nimport com.sohu.cache.redis.enums.RedisConfigEnum;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.IdempotentConfirmer;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.AppTypeEnum;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.MasterSlaveExistEnum;\nimport com.sohu.cache.web.enums.RestartStatusEnum;\nimport com.sohu.cache.web.service.AppScrollRestartService;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.util.DateUtil;\nimport com.sohu.cache.web.util.Page;\nimport com.sohu.cache.web.vo.AppRedisConfigVo;\nimport com.sohu.cache.web.vo.ExecuteResult;\nimport com.sohu.cache.web.vo.MasterSlaveGroupBo;\nimport com.sohu.cache.web.vo.RedisConfigVo;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\nimport org.springframework.ui.Model;\nimport org.springframework.util.CollectionUtils;\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.exceptions.JedisConnectionException;\nimport redis.clients.jedis.exceptions.JedisDataException;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/13 16:10\n * @Description:\n */\n@Slf4j\n@Service\npublic class AppScrollRestartServiceImpl implements AppScrollRestartService {\n\n    @Autowired\n    private MachineDao machineDao;\n\n    @Autowired\n    private InstanceConfigDao instanceConfigDao;\n\n    @Autowired\n    private RedisCenter redisCenter;\n\n    @Autowired\n    private RedisDeployCenter redisDeployCenter;\n\n    @Autowired\n    protected InstanceDeployCenter instanceDeployCenter;\n\n    @Autowired\n    private ConfigRestartRecordDao configRestartRecordDao;\n\n    @Autowired\n    protected AppService appService;\n\n    @Autowired\n    private AssistRedisService assistRedisService;\n\n    @Value(\"${cachecloud.redis.config-test.appid:#{null}}\")\n    private Long TEST_APP_ID;\n\n    private static final String RESTART_LOG_KEY = \"restart:log:\";\n\n    private final static String RESTART_CONFIG_KEY = \"restart:config:\";\n\n    private final static String RESTART_STOP_KEY = \"restart:stop:\";\n\n    private final static String INTERRUPT_STOP = \">>>>> 被中断 >>>>>\";\n\n    /**\n     *\n     * @param instanceInfoList\n     * @param appDesc\n     * @return\n     */\n    @Override\n    public boolean handleAppInstanceInfo(List<InstanceInfo> instanceInfoList, AppDesc appDesc) {\n        boolean resultFlag =  true;\n        if (instanceInfoList != null && instanceInfoList.size() > 0) {\n            for (InstanceInfo instanceInfo : instanceInfoList) {\n                int type = instanceInfo.getType();\n                if (instanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    continue;\n                }\n                if (TypeUtil.isRedisType(type)) {\n                    if (TypeUtil.isRedisSentinel(type)) {\n                        continue;\n                    }\n                    String host = instanceInfo.getIp();\n                    int port = instanceInfo.getPort();\n                    // 幂等操作\n                    BooleanEnum isMaster = BooleanEnum.OTHER;\n                    int retryTime = 3;\n                    while (BooleanEnum.OTHER.equals(isMaster) && retryTime-- > 0){\n                        isMaster = redisCenter.isMaster(instanceInfo.getAppId(), host, port);\n                    }\n                    if(BooleanEnum.OTHER.equals(isMaster)){\n                        return false;\n                    }\n                    instanceInfo.setRoleDesc(isMaster);\n                    if (BooleanEnum.FALSE == isMaster) {\n                        retryTime = 3;\n                        HostAndPort hap = null;\n                        while (hap == null && retryTime-- > 0){\n                            hap = redisCenter.getMaster(host, port, appDesc.getAppPassword());\n                        }\n                        if(hap == null){\n                            return false;\n                        }\n                        if (hap != null) {\n                            instanceInfo.setMasterHost(hap.getHost());\n                            instanceInfo.setMasterPort(hap.getPort());\n                            for (InstanceInfo innerInfo : instanceInfoList) {\n                                if (innerInfo.getIp().equals(hap.getHost())\n                                        && innerInfo.getPort() == hap.getPort()) {\n                                    instanceInfo.setMasterInstanceId(innerInfo.getId());\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return resultFlag;\n    }\n\n    @Override\n    public Map<Integer, List<InstanceInfo>> instanceGroupByMaster(List<InstanceInfo> instanceList) {\n        Map<Integer, List<InstanceInfo>> resultMap = new HashMap<Integer, List<InstanceInfo>>();\n        for (InstanceInfo info : instanceList) {\n            String roleDesc = info.getRoleDesc();\n            if (roleDesc != null && roleDesc.equals(\"master\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, info.getId(), new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(info.getId(), list);\n            } else if (roleDesc != null && roleDesc.equals(\"slave\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, info.getMasterInstanceId(), new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(info.getMasterInstanceId(), list);\n            }\n        }\n        return resultMap;\n    }\n\n    @Override\n    public long generateAndSaveConfigRestartRecord(AppUser appUser, AppDesc appDesc, Object paramObject, Integer opertateType, List<InstanceInfo> instanceInfoList){\n        ConfigRestartRecord configRestartRecord = new ConfigRestartRecord();\n        configRestartRecord.setAppId(appDesc.getAppId());\n        configRestartRecord.setAppName(appDesc.getName());\n        configRestartRecord.setParam(JSONObject.toJSONString(paramObject));\n        if(opertateType == null){\n            if(paramObject instanceof AppRedisConfigVo){\n                configRestartRecord.setOperateType(((AppRedisConfigVo)paramObject).isConfigFlag() == true ? 1 : 0);\n            }\n        }else{\n            configRestartRecord.setOperateType(opertateType);\n        }\n        List<Integer> instanceIdList = new ArrayList<>();\n        if(instanceInfoList != null && instanceInfoList.size() > 0){\n            instanceInfoList.forEach(instanceInfo -> instanceIdList.add(instanceInfo.getId()));\n        }\n        configRestartRecord.setInstances(JSONObject.toJSONString(instanceIdList));\n        Date date = new Date();\n        configRestartRecord.setCreateTime(date);\n        configRestartRecord.setEndTime(date);\n        configRestartRecord.setStartTime(date);\n        configRestartRecord.setUpdateTime(date);\n        configRestartRecord.setStatus(RestartStatusEnum.RUNNING.getValue());\n        configRestartRecord.setUserId(appUser.getId());\n        configRestartRecord.setUserName(appUser.getChName());\n        this.saveConfigRestartRecord(configRestartRecord);\n        return configRestartRecord.getId();\n    }\n\n    @Override\n    public void saveConfigRestartRecord(ConfigRestartRecord configRestartRecord) {\n        configRestartRecordDao.save(configRestartRecord);\n    }\n\n    @Override\n    public void updateConfigRestartRecord(ConfigRestartRecord configRestartRecord) {\n        configRestartRecordDao.updateByCondition(configRestartRecord);\n    }\n\n    @Override\n    public ConfigRestartRecord getConfigRestartRecord(long id) {\n        ConfigRestartRecord configRestartRecord = configRestartRecordDao.getById(id);\n        if(RestartStatusEnum.WAITING.getValue() == configRestartRecord.getStatus()\n                || RestartStatusEnum.RUNNING.getValue() == configRestartRecord.getStatus()){\n            List<String> logList = assistRedisService.lrange(RESTART_LOG_KEY + id, 0, -1);\n            configRestartRecord.setLog(JSONObject.toJSONString(logList));\n        }\n        return configRestartRecord;\n    }\n\n    @Override\n    public List<ConfigRestartRecord> getConfigRestartRecordByCondition(Model model, ConfigRestartRecord configRestartRecordParam, int pageNo, int pageSize) {\n        int totalCount = configRestartRecordDao.getCountByCondition(configRestartRecordParam);\n\n        Page page = new Page(pageNo, pageSize, totalCount);\n        model.addAttribute(\"page\", page);\n        // 分页相关:list\n        configRestartRecordParam.setPage(page);\n        List<ConfigRestartRecord> listByCondition = configRestartRecordDao.getListByCondition(configRestartRecordParam);\n        listByCondition.stream().forEach(configRestartRecord -> {\n            if(RestartStatusEnum.WAITING.getValue() == configRestartRecord.getStatus()\n                    || RestartStatusEnum.RUNNING.getValue() == configRestartRecord.getStatus()\n                    || RestartStatusEnum.NEED_RESTART.getValue() == configRestartRecord.getStatus()\n                    || RestartStatusEnum.RESTART_AFTER_CONFIG.getValue() == configRestartRecord.getStatus()\n            ){\n                List<String> logList = assistRedisService.lrange(RESTART_LOG_KEY + configRestartRecord.getId(), 0, -1);\n                configRestartRecord.setLog(JSONObject.toJSONString(logList));\n            }\n        });\n        return listByCondition;\n    }\n\n    @Override\n    public void saveConfigRestartLog(long id, String log) {\n        assistRedisService.rpush(RESTART_LOG_KEY + id, log);\n    }\n\n    @Override\n    public List<String> getAndDeleteConfigRestartLog(long id){\n        List<String> logList = assistRedisService.lrange(RESTART_LOG_KEY + id, 0, -1);\n        assistRedisService.remove(RESTART_LOG_KEY + id);\n        return logList;\n    }\n\n    @Override\n    public void deleteConfigRestartLog(long id){\n        assistRedisService.remove(RESTART_LOG_KEY + id);\n    }\n\n    /**\n     * 更新修改配置、重启记录\n     * @param recordId\n     * @param restartStatusEnum\n     * @param lastLog\n     */\n    @Override\n    public void updateConfigRestartRecord(long recordId, RestartStatusEnum restartStatusEnum, String... lastLog) {\n        ConfigRestartRecord configRestartRecord = new ConfigRestartRecord();\n        configRestartRecord.setId(recordId);\n        configRestartRecord.setStatus(restartStatusEnum.getValue());\n        List<String> logList = new ArrayList<>();\n        if(!RestartStatusEnum.NEED_RESTART.equals(restartStatusEnum)\n            && !RestartStatusEnum.RESTART_AFTER_CONFIG.equals(restartStatusEnum)){\n            logList = this.getAndDeleteConfigRestartLog(recordId);\n        }\n        if(lastLog != null && lastLog.length > 0){\n            logList.addAll(Arrays.asList(lastLog));\n        }\n        if(!CollectionUtils.isEmpty(logList)){\n            configRestartRecord.setLog(JSONObject.toJSONString(logList));\n        }\n        configRestartRecord.setEndTime(new Date());\n        this.updateConfigRestartRecord(configRestartRecord);\n    }\n\n    /**\n     * 添加停止滚动重启标志\n     * @param appId\n     * @return\n     */\n    @Override\n    public boolean addStopRestartFlag(Long appId) {\n        boolean result = false;\n        result = assistRedisService.set(RESTART_STOP_KEY + appId, 1);\n        return result;\n    }\n\n    /**\n     * 删除停止滚动重启标志\n     * @param appId\n     * @return\n     */\n    @Override\n    public boolean deleteStopRestartFlag(Long appId) {\n        boolean result = false;\n        result = assistRedisService.del(RESTART_STOP_KEY + appId);\n        return result;\n    }\n\n    /**\n     * 判断是否有停止滚动重启标志\n     * @param appId\n     * @return\n     */\n    @Override\n    public boolean existsStopRestartFlag(Long appId){\n        boolean result = false;\n        result = assistRedisService.exists(RESTART_STOP_KEY + appId);\n        return result;\n    }\n\n    /**\n     * 滚动重启处理\n     * @param appDesc\n     * @param instanceInfoList\n     * @param appRedisConfigVo\n     * @return\n     */\n    @Override\n    public ExecuteResult handleRestart(AppUser appUser, AppDesc appDesc, List<InstanceInfo> instanceInfoList, AppRedisConfigVo appRedisConfigVo){\n        if(assistRedisService.get(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId()) != null){\n            return ExecuteResult.error(\"滚动重启/修改配置正在执行中，不允许重复操作。\");\n        }\n        log.info(String.format(\"restart info, appId: %s, instanceInfoList: %s, paramInfo: %s\", appDesc.getAppId(), instanceInfoList, appRedisConfigVo));\n        //拆分实例为，主从分组\n        Map<Integer, List<InstanceInfo>> groupMap = this.instanceGroupByMaster(instanceInfoList);\n        //筛选出需要处理的实例\n        List<Integer> instanceIdList = appRedisConfigVo.getInstanceList();\n        List<InstanceInfo> pointedInstanceList = this.filterPointedInstance(instanceInfoList, instanceIdList);\n\n        //走重启逻辑, 校验相关的实例是否为主从备份\n        boolean check = this.checkIsMasterSlavePair(pointedInstanceList, groupMap);\n        if(assistRedisService.get(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId()) != null){\n            return ExecuteResult.error(\"滚动重启/修改配置正在执行中，不允许重复操作。\");\n        }\n        if(!check){\n            String tipInfo = \"集群主从实例不满足操作\";\n            return ExecuteResult.error(tipInfo);\n        }\n\n        if(assistRedisService.get(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId()) != null){\n            return ExecuteResult.error(\"滚动重启/修改配置正在执行中，不允许重复操作。\");\n        }\n        //按实例主从分组处理——重启实例, 异步\n        AsyncRestartService asyncRestartService = new AsyncRestartService(appUser, appDesc, appRedisConfigVo, instanceInfoList, pointedInstanceList, groupMap);\n        asyncRestartService.start();\n        return ExecuteResult.ok(\"重启进行中，请前往查看进程记录。\");\n    }\n\n    /**\n     * 处理修改配置\n     * @param appUser\n     * @param appDesc\n     * @param instanceInfoList\n     * @param appRedisConfigVo\n     * @return\n     */\n    @Override\n    public Map<String,Object> handleConfig(AppUser appUser, AppDesc appDesc, List<InstanceInfo> instanceInfoList, AppRedisConfigVo appRedisConfigVo){\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"commandSet\", false);\n        //拆分实例为，主从分组\n        Map<Integer, List<InstanceInfo>> groupMap = this.instanceGroupByMaster(instanceInfoList);\n        //处理配置项，将相同配置名的归为一组进行处理\n        Map<String, Set<String>> redisConfigMap = this.groupRedisConfigByName(appRedisConfigVo.getConfigList());\n\n        //校验配置项合法性\n        boolean isSupport = this.checkConfigIsSupport(appDesc, redisConfigMap);\n        if(!isSupport){\n//            map.put(\"errorInfo\", String.format(\"配置模板中无此配置项：%s\", redisConfigMap));\n//            return map;\n            log.error(String.format(\"配置模板中无此配置项：%s\", redisConfigMap));\n        }\n        //筛选出需要处理的实例\n        List<Integer> instanceIdList = appRedisConfigVo.getInstanceList();\n        List<InstanceInfo> pointedInstanceList = this.filterPointedInstance(instanceInfoList, instanceIdList);\n        //获取一个实例用于check config set是否可用\n        InstanceInfo toCheckInstance = this.getInstanceToCheckConfigSet(instanceInfoList, instanceIdList);\n        //通过一个实例运行config set 判断是否成功，成功则全部通过进行处理。\n        Map<String, String> toCheckInstanceConfig = this.getConfigByCommand(appDesc, toCheckInstance, redisConfigMap);\n        int checkResult = this.checkConfigSetAvailable(appDesc, toCheckInstance, redisConfigMap);\n\n        //保存重启记录\n        Integer operateType = 1;\n        if(checkResult == 1 || checkResult == 2){\n            operateType = 2;\n        }\n        long recordId = this.generateAndSaveConfigRestartRecord(appUser, appDesc, appRedisConfigVo, operateType, instanceInfoList);\n        appRedisConfigVo.setRecordId(recordId);\n        map.put(\"recordId\", recordId);\n        // 仅修改相关实例的配置，无需重启\n        String errorInfo = null;\n        if(checkResult == 1){\n            this.saveConfigRestartLog(recordId, this.generateLog(\"修改配置 >>>> 开始，共%s个实例， 配置信息为：%s\", pointedInstanceList.size(), redisConfigMap));\n            //获取原有配置\n            List<Map<String, String>> pointedInstanceConfigList = new ArrayList<>();\n            for(int i = 0; i < pointedInstanceList.size(); i++){\n                InstanceInfo instanceInfo = pointedInstanceList.get(i);\n                if(!InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())\n                        && !InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc())){\n                    continue;\n                }\n            }\n            for(int i = 0; i < pointedInstanceList.size(); i++){\n                InstanceInfo instanceInfo = pointedInstanceList.get(i);\n                if(!InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())\n                        && !InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc())){\n                    continue;\n                }\n                //获取原配置信息\n                Map<String, String> configByCommand = null;\n                if(instanceInfo.equals(toCheckInstance)){\n                    configByCommand = toCheckInstanceConfig;\n                }else{\n                    configByCommand = this.getConfigByCommand(appDesc, instanceInfo, redisConfigMap);\n                }\n                long startTime = System.currentTimeMillis();\n                boolean configResult = this.configSetAndRewrite(appDesc, instanceInfo, redisConfigMap);\n                long endTime = System.currentTimeMillis();\n                this.saveConfigRestartLog(recordId, this.generateLog(\"第%s个实例， 实例信息：%s， 原配置信息:%s，结果：%s，耗时：%s\", (i + 1), instanceInfo.getHostPort(), configByCommand, (configResult ? \"成功\" : \"失败\"), this.getTimeBetween(endTime, startTime)));\n                if(!configResult){\n                    errorInfo = String.format(\"修改配置config set/rewrite失败， 请人工确认。节点信息如下：appId:%s，instanceId:%s，hostPort:%s\", instanceInfo.getAppId(), instanceInfo.getId(), instanceInfo.getHostPort());\n                    break;\n                }\n            }\n            map.put(\"commandSet\", true);\n            map.put(\"errorInfo\", errorInfo);\n            if(StringUtil.isBlank(errorInfo)){\n                this.updateConfigRestartRecord(recordId, RestartStatusEnum.SUCCESS, this.generateLog(\"修改配置 >>>> 结束\"));\n                // 增加特殊逻辑，判断是否为修改maxMemory-policy，对应修改appDesc表设置\n                if (redisConfigMap.containsKey(RedisConfigEnum.MAXMEMORY_POLICY.getKey())) {\n                    Set<String> maxMemoryPolicys = redisConfigMap.get(RedisConfigEnum.MAXMEMORY_POLICY.getKey());\n                    if (org.apache.commons.collections.CollectionUtils.isNotEmpty(maxMemoryPolicys)) {\n                        String maxMemoryPolicy = maxMemoryPolicys.iterator().next();\n                        AppDescEnum.MaxmemoryPolicyType maxmemoryPolicyType = AppDescEnum.MaxmemoryPolicyType.getByName(maxMemoryPolicy);\n                        if(maxmemoryPolicyType != null){\n                            appService.updateAppMaxmemoryPolicy(appDesc.getAppId(), maxmemoryPolicyType.getType());\n                        }\n                    }\n                }\n            }else{\n                this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL, this.generateLog(errorInfo));\n            }\n            return map;\n        }\n        if(checkResult == 2){\n            errorInfo = String.format(\"配置值不合法，配置：%s\", redisConfigMap);\n            map.put(\"commandSet\", false);\n            map.put(\"errorInfo\", errorInfo);\n            this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL, this.generateLog(errorInfo));\n            return map;\n        }\n        //走重启逻辑, 校验相关的实例是否为主从备份\n        boolean check = this.checkIsMasterSlavePair(pointedInstanceList, groupMap);\n        if(!check){\n            errorInfo = \"集群主从实例不满足操作\";\n            map.put(\"errorInfo\", errorInfo);\n            this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL, this.generateLog(errorInfo));\n            return map;\n        }\n\n        //在测试机上进行测试\n        boolean testFlag = this.testUpdateConfigFileAndRestart(appDesc.getAppId(), redisConfigMap);\n        if(!testFlag){\n            errorInfo = String.format(\"配置有严重错误或不支持，配置： %s\", redisConfigMap);\n            map.put(\"commandSet\", false);\n            map.put(\"errorInfo\", errorInfo);\n            this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL, this.generateLog(errorInfo));\n            return map;\n        }\n\n        //按实例主从分组处理——修改配置实例\n        errorInfo = this.handleConfigByGroup(appDesc, appRedisConfigVo, pointedInstanceList, groupMap, redisConfigMap);\n        map.put(\"errorInfo\", errorInfo);\n        return map;\n    }\n\n    @Override\n    public boolean isAppOnScrollRestart(long appId) {\n        ConfigRestartRecord configRestartRecord = new ConfigRestartRecord();\n        configRestartRecord.setAppId(appId);\n        List<ConfigRestartRecord> restartList = configRestartRecordDao.getListByCondition(configRestartRecord);\n        Optional<ConfigRestartRecord> restartRecordOptional = restartList.stream().filter(restartRecord ->\n                restartRecord.getStatus() == RestartStatusEnum.RUNNING.getValue()\n                || restartRecord.getStatus() == RestartStatusEnum.RESTART_AFTER_CONFIG.getValue()).findFirst();\n        return restartRecordOptional.isPresent();\n    }\n\n    /**\n     * 校验相关的实例是否为主从备份\n     * @param pointedInstanceList 指定的某些节点, 可为空\n     * @param groupMap 已根据主从进行分组，参见#AppScrollRestartService.instanceGroupByMaster\n     * @return false:不满足主从，true满足\n     */\n    private boolean checkIsMasterSlavePair(List<InstanceInfo> pointedInstanceList, Map<Integer, List<InstanceInfo>> groupMap){\n        if(CollectionUtils.isEmpty(pointedInstanceList)){\n            //校验是否均为主从备份\n            Set<Map.Entry<Integer, List<InstanceInfo>>> entries = groupMap.entrySet();\n            for (Map.Entry<Integer, List<InstanceInfo>> entry : entries){\n                List<InstanceInfo> instanceInfos = entry.getValue();\n                if(instanceInfos.size() < 2){\n                    return false;\n                }\n            }\n        }else{\n            for(InstanceInfo instanceInfo : pointedInstanceList){\n                if(InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc())){\n                    List<InstanceInfo> instanceInfos = groupMap.get(instanceInfo.getId());\n                    if(instanceInfos.size() < 2){\n                        return false;\n                    }\n                }\n                if(InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())){\n                    List<InstanceInfo> instanceInfos = groupMap.get(instanceInfo.getMasterInstanceId());\n                    if(instanceInfos.size() < 2){\n                        return false;\n                    }\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 从实例中获取一个实例去进行config set 校验\n     * @param instanceInfoList\n     * @param instanceIdList\n     * @return\n     */\n    private InstanceInfo getInstanceToCheckConfigSet(List<InstanceInfo> instanceInfoList, List<Integer> instanceIdList){\n        InstanceInfo toCheckInstance = null;\n        if(CollectionUtils.isEmpty(instanceIdList)){\n            for(InstanceInfo instanceInfo : instanceInfoList){\n                if(InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())){\n                    toCheckInstance = instanceInfo;\n                    break;\n                }\n            }\n            if(toCheckInstance == null){\n                toCheckInstance = instanceInfoList.get(0);\n            }\n        }else{\n            for(InstanceInfo instanceInfo : instanceInfoList){\n                if(instanceInfo.getId() == instanceIdList.get(0)){\n                    toCheckInstance = instanceInfo;\n                    break;\n                }\n            }\n        }\n        return toCheckInstance;\n    }\n\n    /**\n     * 筛选出指定实例\n     * @param instanceInfoList\n     * @param instanceIdList\n     * @return\n     */\n    private List<InstanceInfo> filterPointedInstance(List<InstanceInfo> instanceInfoList, List<Integer> instanceIdList){\n        if(CollectionUtils.isEmpty(instanceIdList)){\n            return instanceInfoList;\n        }\n        List<InstanceInfo> filterInstanceList = new ArrayList<>();\n        for(Integer instanceId : instanceIdList){\n            for(InstanceInfo instanceInfo : instanceInfoList){\n                if(instanceId == instanceInfo.getId()){\n                    filterInstanceList.add(instanceInfo);\n                    break;\n                }\n            }\n        }\n        return filterInstanceList;\n    }\n\n    class AsyncRestartService extends Thread{\n\n        private AppUser appUser;\n\n        private AppDesc appDesc;\n\n        private AppRedisConfigVo appRedisConfigVo;\n\n        private List<InstanceInfo> instanceInfoList;\n\n        private List<InstanceInfo> pointedInstanceList;\n\n        private Map<Integer, List<InstanceInfo>> groupMap;\n\n        public AsyncRestartService(){\n\n        }\n\n        public AsyncRestartService(AppUser appUser, AppDesc appDesc, AppRedisConfigVo appRedisConfigVo, List<InstanceInfo> instanceInfoList, List<InstanceInfo> pointedInstanceList, Map<Integer, List<InstanceInfo>> groupMap){\n            this.appUser = appUser;\n            this.appDesc = appDesc;\n            this.appRedisConfigVo = appRedisConfigVo;\n            this.instanceInfoList = instanceInfoList;\n            this.pointedInstanceList = pointedInstanceList;\n            this.groupMap = groupMap;\n        }\n\n        @Override\n        public void run() {\n            handleRestartByGroup(appUser, appDesc, appRedisConfigVo, instanceInfoList, pointedInstanceList, groupMap);\n        }\n    }\n\n    /**\n     * 按实例主从分组重启, 异步\n     * @param appDesc\n     * @param groupMap\n     * @return\n     */\n    @Override\n    public void handleRestartByGroup(AppUser appUser, AppDesc appDesc, AppRedisConfigVo appRedisConfigVo, List<InstanceInfo> instanceInfoList, List<InstanceInfo> pointedInstanceList, Map<Integer, List<InstanceInfo>> groupMap){\n        Long recordId = null;\n        boolean restartAfterConfig = false;\n        //保存重启记录\n        if(appRedisConfigVo.getRecordId() == null){\n            recordId = this.generateAndSaveConfigRestartRecord(appUser, appDesc, appRedisConfigVo, null, instanceInfoList);\n            appRedisConfigVo.setRecordId(recordId);\n        }else{\n            recordId = appRedisConfigVo.getRecordId();\n            restartAfterConfig = true;\n        }\n\n        if (!assistRedisService.setNx(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId(), \"1\")) {\n            this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL, this.generateLog(\"滚动重启/修改配置正在执行中，不允许重复操作。\"));\n            return;\n        }\n        if(assistRedisService.exists(RESTART_STOP_KEY + appRedisConfigVo.getAppId())){\n            assistRedisService.del(RESTART_STOP_KEY + appRedisConfigVo.getAppId());\n        }\n        try{\n            if(restartAfterConfig){\n                this.updateConfigRestartRecord(recordId, RestartStatusEnum.RESTART_AFTER_CONFIG);\n            }\n            log.info(String.format(\"restart by group, start, appId: %s, paramInfo: %s\", appDesc.getAppId(), appRedisConfigVo));\n            ExecuteResult executeResult =  new ExecuteResult();\n            executeResult.setSuccess(false);\n            boolean transferFlag = appRedisConfigVo.isTransferFlag();\n            boolean quickFinishFlag = appRedisConfigVo.isQuickFinishFlag();\n            Set<Map.Entry<Integer, List<InstanceInfo>>> entries = groupMap.entrySet();\n            //------------------------\n            int groupCount = 0;\n            for(Map.Entry<Integer, List<InstanceInfo>> map : entries) {\n                MasterSlaveGroupBo masterSlaveGroupBo = this.getOneMasterSlaveGroup(pointedInstanceList, map);\n                MasterSlaveExistEnum masterSlaveExistEnum = masterSlaveGroupBo.getMasterSlaveExistEnum();//1:only master;2:only slave;3:master and slave\n                if (MasterSlaveExistEnum.NONE.equals(masterSlaveExistEnum)) {\n                    continue;\n                }\n                groupCount++;\n            }\n            this.saveConfigRestartLog(recordId, this.generateLog(\"滚动重启 >>>>>> 开始，主从分组共%s个\", groupCount));\n            long initialStartTime = System.currentTimeMillis();\n            //-------------------\n            int runGroup = 0;\n            for(Map.Entry<Integer, List<InstanceInfo>> map : entries){\n                MasterSlaveGroupBo masterSlaveGroupBo = this.getOneMasterSlaveGroup(pointedInstanceList, map);\n                MasterSlaveExistEnum masterSlaveExistEnum = masterSlaveGroupBo.getMasterSlaveExistEnum();//1:only master;2:only slave;3:master and slave\n                InstanceInfo oneGroupMaster = masterSlaveGroupBo.getMaster();\n                List<InstanceInfo> oneGroupSlave = masterSlaveGroupBo.getSlaveList();\n                if(MasterSlaveExistEnum.NONE.equals(masterSlaveExistEnum)){\n                    continue;\n                }\n                runGroup++;\n                //处理当前某一分组\n                List<String> slaveStrList = new ArrayList<>();\n                oneGroupSlave.forEach(slaveInfo -> slaveStrList.add(slaveInfo.getHostPort()));\n                if(this.existsStopRestartFlag(appRedisConfigVo.getAppId())){\n                    if(restartAfterConfig){\n                        this.saveConfigRestartLog(recordId, this.generateLog(\"滚动重启 >>>>>> 被中断，请注意配置文件已修改，需人工判定生效范围及影响\"));\n                    }else{\n                        this.saveConfigRestartLog(recordId, this.generateLog(\"滚动重启 >>>>>> 被中断，请确认影响\"));\n                    }\n                    this.updateConfigRestartRecord(recordId, RestartStatusEnum.INTERUPT);\n                    return;\n                }\n                this.saveConfigRestartLog(recordId, this.generateLog(\"处理第%s个主从分组开始，主节点信息：%s，从节点信息：%s，主从存在标志：%s\", runGroup, oneGroupMaster.getHostPort(), slaveStrList, masterSlaveExistEnum.getInfo()));\n                long startTime = System.currentTimeMillis();\n                executeResult = this.restartOneGroup(recordId, runGroup, appDesc, oneGroupMaster, oneGroupSlave, masterSlaveExistEnum, transferFlag, quickFinishFlag);\n                long endTime = System.currentTimeMillis();\n                this.saveConfigRestartLog(recordId,\n                        this.generateLog(\"处理第%s个主从分组结束。结果为：%s，耗时：%s\", runGroup,\n                                (executeResult.isSuccess() ? \"成功\" : executeResult.getMessage()), this.getTimeBetween(endTime, startTime)));\n                if(!executeResult.isSuccess()){\n                    //中断\n                    if(executeResult.getMessage() != null && executeResult.getMessage().contains(INTERRUPT_STOP)){\n                        long finalEndTime = System.currentTimeMillis();\n                        if(restartAfterConfig){\n                            this.saveConfigRestartLog(recordId, this.generateLog(\"滚动重启 >>>>>> 结束，被中断，请注意配置文件已修改，需人工判定生效范围及影响，主从分组共%s个，耗时：%s\", groupCount, this.getTimeBetween(finalEndTime, initialStartTime)));\n                        }else{\n                            this.saveConfigRestartLog(recordId, this.generateLog(\"滚动重启 >>>>>> 结束，被中断，主从分组共%s个，耗时：%s\", groupCount, this.getTimeBetween(finalEndTime, initialStartTime)));\n                        }\n                        this.updateConfigRestartRecord(recordId, RestartStatusEnum.INTERUPT);\n                        return;\n                    }else{\n                        //异常结束\n                        long finalEndTime = System.currentTimeMillis();\n                        this.saveConfigRestartLog(recordId, this.generateLog(\"滚动重启 >>>>>> 结束，失败，耗时：%s\", groupCount, this.getTimeBetween(finalEndTime, initialStartTime)));\n                        this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL);\n                        return;\n                    }\n                }\n            }\n            long finalEndTime = System.currentTimeMillis();\n            this.saveConfigRestartLog(recordId, this.generateLog(\"滚动重启 >>>>>> 结束，主从分组共%s个，耗时：%s\", groupCount, this.getTimeBetween(finalEndTime, initialStartTime)));\n            //正常结束\n            this.updateConfigRestartRecord(recordId, RestartStatusEnum.SUCCESS);\n            return;\n        }catch (Exception e){\n            log.error(\"handleRestartByGroup error: \", e);\n        }finally {\n            assistRedisService.remove(RESTART_CONFIG_KEY + appRedisConfigVo.getAppId());\n            this.deleteStopRestartFlag(appRedisConfigVo.getAppId());\n        }\n    }\n\n    /**\n     * 按实例主从分组配置\n     * @param appDesc\n     * @param groupMap\n     * @return\n     */\n    private String handleConfigByGroup(AppDesc appDesc, AppRedisConfigVo appRedisConfigVo, List<InstanceInfo> pointedInstanceList, Map<Integer, List<InstanceInfo>> groupMap, Map<String, Set<String>> redisConfigMap){\n        Long recordId = appRedisConfigVo.getRecordId();\n        boolean transferFlag = appRedisConfigVo.isTransferFlag();\n        StringBuilder  sb = new StringBuilder();\n        if(CollectionUtils.isEmpty(groupMap)){\n            return null;\n        }\n        Set<Map.Entry<Integer, List<InstanceInfo>>> entries = groupMap.entrySet();\n        //------------------------\n        int groupCount = 0;\n        for(Map.Entry<Integer, List<InstanceInfo>> map : entries) {\n            MasterSlaveGroupBo masterSlaveGroupBo = this.getOneMasterSlaveGroup(pointedInstanceList, map);\n            MasterSlaveExistEnum masterSlaveExistEnum = masterSlaveGroupBo.getMasterSlaveExistEnum();//1:only master;2:only slave;3:master and slave\n            if (MasterSlaveExistEnum.NONE.equals(masterSlaveExistEnum)) {\n                continue;\n            }\n            groupCount++;\n        }\n        this.saveConfigRestartLog(recordId, this.generateLog(\"强制修改配置 >>>> 开始，待处理主从分组共%s个，配置信息为：%s\", groupCount, redisConfigMap));\n        long initialStartTime = System.currentTimeMillis();\n        //-------------------\n        int runGroup =  0;\n        for(Map.Entry<Integer, List<InstanceInfo>> map : entries){\n            MasterSlaveGroupBo masterSlaveGroupBo = this.getOneMasterSlaveGroup(pointedInstanceList, map);\n            MasterSlaveExistEnum masterSlaveExistEnum = masterSlaveGroupBo.getMasterSlaveExistEnum();//1:only master;2:only slave;3:master and slave\n            InstanceInfo oneGroupMaster = masterSlaveGroupBo.getMaster();\n            List<InstanceInfo> oneGroupSlave = masterSlaveGroupBo.getSlaveList();\n            if(MasterSlaveExistEnum.NONE.equals(masterSlaveExistEnum)){\n                continue;\n            }\n            runGroup++;\n            //处理当前某一分组\n            long startTime = System.currentTimeMillis();\n//            this.saveConfigRestartLog(recordId, this.generateLog(\"强制修改配置，处理第%s个主从分组开始\", runGroup));\n            String oneGroupError = this.configOneGroup(recordId, runGroup, appDesc, oneGroupMaster, oneGroupSlave, redisConfigMap, masterSlaveExistEnum, transferFlag);\n            long endTime = System.currentTimeMillis();\n            this.saveConfigRestartLog(recordId, this.generateLog(\"第%s个主从分组结束。结果为：%s，耗时：%s\" , runGroup, (StringUtil.isBlank(oneGroupError) ? \"成功\" : oneGroupError), this.getTimeBetween(endTime, startTime)));\n            if(StringUtils.isNotEmpty(oneGroupError)){\n                sb.append(oneGroupError);\n                sb.append(\";\\n\");\n                this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL);\n                return sb.toString();\n            }\n        }\n        long finalEndTime = System.currentTimeMillis();\n        if(StringUtil.isBlank(sb.toString())){\n            this.saveConfigRestartLog(recordId, this.generateLog(\"强制修改配置 >>>> 结束，结果为：%s，耗时：%s\", (StringUtil.isBlank(sb.toString()) ? \"成功\" : sb.toString()), this.getTimeBetween(finalEndTime, initialStartTime)));\n            this.saveConfigRestartLog(recordId, this.generateLog(\">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\"));\n            this.updateConfigRestartRecord(recordId, RestartStatusEnum.NEED_RESTART);\n        }else{\n            this.updateConfigRestartRecord(recordId, RestartStatusEnum.FAIL, this.generateLog(\"强制修改配置 >>>> 结束，结果为：失败，失败信息：%s，耗时：%s\", (StringUtil.isBlank(sb.toString()) ? \"成功\" : sb.toString()), this.getTimeBetween(finalEndTime, initialStartTime)));\n            this.saveConfigRestartLog(recordId, this.generateLog(\">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\"));\n        }\n        return sb.toString();\n    }\n\n    /**\n     * （根据指定实例），拆分出主节点，从节点（多个），及主从节点是否均包含\n     * @param pointedInstanceList\n     * @param map\n     * @return\n     */\n    private MasterSlaveGroupBo getOneMasterSlaveGroup(List<InstanceInfo> pointedInstanceList, Map.Entry<Integer, List<InstanceInfo>> map){\n        MasterSlaveGroupBo masterSlaveGroupBo = new MasterSlaveGroupBo();\n        int masterSlaveFlag = 0;//1:only master;2:only slave;3:master and slave\n        InstanceInfo oneGroupMaster = null;\n        List<InstanceInfo> oneGroupSlave = new ArrayList<>();\n        if(CollectionUtils.isEmpty(pointedInstanceList)){\n            List<InstanceInfo> oneGroupList = map.getValue();\n            //获取当前分组主节点、从节点列表\n            for (InstanceInfo instanceInfo : oneGroupList) {\n                if(InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc())){\n                    oneGroupMaster = instanceInfo;\n                }\n                if(InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc())){\n                    oneGroupSlave.add(instanceInfo);\n                }\n            }\n            masterSlaveFlag = 3;\n        }else{\n            List<InstanceInfo> oneGroupList = map.getValue();\n            //从指定的实例中，获取当前分组待处理的主节点，从节点列表\n            for(int i = 0; i < pointedInstanceList.size(); i++){\n                InstanceInfo instanceInfo = pointedInstanceList.get(i);\n                if(InstanceRoleEnum.MASTER.getInfo().equals(instanceInfo.getRoleDesc()) && instanceInfo.getId() == map.getKey()){\n                    oneGroupMaster = instanceInfo;\n                    masterSlaveFlag = masterSlaveFlag > 0 ? 3 : 1;\n                }else{\n                    if(InstanceRoleEnum.SLAVE.getInfo().equals(instanceInfo.getRoleDesc()) && instanceInfo.getMasterInstanceId() == map.getKey()){\n                        oneGroupSlave.add(instanceInfo);\n                        masterSlaveFlag = masterSlaveFlag > 0 ? (masterSlaveFlag == 2 ? 2: 3) : 2;\n\n                    }\n                }\n                if(i == pointedInstanceList.size() - 1 && masterSlaveFlag == 1){\n                    for (InstanceInfo oneGroupInstance : oneGroupList) {\n                        if(InstanceRoleEnum.SLAVE.getInfo().equals(oneGroupInstance.getRoleDesc())){\n                            oneGroupSlave.add(oneGroupInstance);\n                            break;\n                        }\n                    }\n                }\n                if(i == pointedInstanceList.size() - 1 && masterSlaveFlag == 2){\n                    for (InstanceInfo oneGroupInstance : oneGroupList) {\n                        if(InstanceRoleEnum.MASTER.getInfo().equals(oneGroupInstance.getRoleDesc())){\n                            oneGroupMaster = oneGroupInstance;\n                            break;\n                        }\n                    }\n                }\n            }\n        }\n        masterSlaveGroupBo.setMaster(oneGroupMaster);\n        masterSlaveGroupBo.setMasterSlaveExistEnum(MasterSlaveExistEnum.getByType(masterSlaveFlag));\n        masterSlaveGroupBo.setSlaveList(oneGroupSlave);\n        return masterSlaveGroupBo;\n    }\n\n\n\n    /**\n     * 滚动重启：重启从节点，选某个从节点failover，重启原主节点，根据条件（在原主节点failover）\n     * @param appDesc\n     * @param master\n     * @param slaveList\n     * @param masterSlaveFlag\n     * @param transferFlag\n     * @return\n     */\n    private ExecuteResult restartOneGroup(long recordId, int runGroup, AppDesc appDesc, InstanceInfo master, List<InstanceInfo> slaveList, MasterSlaveExistEnum masterSlaveFlag , boolean transferFlag, boolean quickFinishFlag){\n        ExecuteResult executeResult = ExecuteResult.error();\n        log.info(String.format(\"restart by group one group, recordId: %s, master: %s, slaveList: %s, masterSlaveFlag: %s\", recordId, master.getHostPort(), slaveList.stream().map(slaveInfo -> slaveInfo.getHostPort()).collect(Collectors.joining(\",\")), masterSlaveFlag));\n        if(this.existsStopRestartFlag(appDesc.getAppId())){\n            return ExecuteResult.error(String.format(\"本分组未开始时%s\", INTERRUPT_STOP));\n        }\n        if(MasterSlaveExistEnum.MASTRE_SLAVE.equals(masterSlaveFlag) || MasterSlaveExistEnum.SLAVE.equals(masterSlaveFlag)){\n            //重启并修改从节点\n            executeResult = this.restartSlaveNodes(recordId, appDesc, slaveList, master);\n            if (!executeResult.isSuccess()) {\n                return executeResult;\n            }\n            //判断是否与主节点完成同步\n            boolean slaveReady = this.checkSlaveReadyFinish(slaveList.get(0));\n            if(!slaveReady){\n                return ExecuteResult.error(String.format(\"判定从节点是否完成与主节点同步失败，slave ：%s\", slaveList.get(0)));\n            }\n            if(quickFinishFlag){\n                //重启节点后，休息3s\n                try {\n                    TimeUnit.SECONDS.sleep(3);\n                } catch (InterruptedException e) {\n                }\n            }else {\n                //重启节点后，休息30s\n                try {\n                    TimeUnit.SECONDS.sleep(30);\n                } catch (InterruptedException e) {\n                }\n            }\n        }\n\n        if(this.existsStopRestartFlag(appDesc.getAppId())){\n            return ExecuteResult.error(String.format(\"从节点重启后，从节点failover未开始，主节点重启未开始时%s\", INTERRUPT_STOP));\n        }\n        if(!MasterSlaveExistEnum.SLAVE.equals(masterSlaveFlag)){\n            //failover到从节点\n            Optional<String> failOverResult = this.clusterFailoverSlaveInstanceAndCheck(recordId, slaveList.get(0), master, appDesc);\n            if (failOverResult.isPresent()) {\n                return ExecuteResult.error(String.format(\"对从节点执行failover失败，信息如下：%s\", failOverResult.get()));\n            }\n            //failover后，休息120s\n            if(quickFinishFlag){\n                try {\n                    TimeUnit.SECONDS.sleep(3);\n                } catch (InterruptedException e) {\n                }\n            }else{\n                try {\n                    TimeUnit.SECONDS.sleep(120);\n                } catch (InterruptedException e) {\n                }\n            }\n\n            if(this.existsStopRestartFlag(appDesc.getAppId())){\n                return ExecuteResult.error(String.format(\"从节点重启后，从节点failover后，主节点重启未开始时%s，请注意是否允许主从切换，并人工处理。\", INTERRUPT_STOP));\n            }\n            //重启原主节点\n            Optional<String> restartResult = this.restartSlaveNode(recordId, appDesc, master, slaveList.get(0));\n            if (restartResult.isPresent()) {\n                return ExecuteResult.error(restartResult.get());\n            }\n            if (!transferFlag) {\n                if(this.existsStopRestartFlag(appDesc.getAppId())){\n                    return ExecuteResult.error(String.format(\"从节点重启后，从节点failover后，主节点重启后，主节点failover未开始时%s，请注意是否允许主从切换，并人工处理。\", INTERRUPT_STOP));\n                }\n                //判断是否与主节点完成同步\n                boolean slaveReady = this.checkSlaveReadyFinish(master);\n                if(!slaveReady){\n                    return ExecuteResult.error(String.format(\"判定从节点是否完成与主节点同步失败，slave ：%s\", slaveList.get(0)));\n                }\n                //对原主节点进行failover\n                failOverResult = this.clusterFailoverSlaveInstanceAndCheck(recordId, master, slaveList.get(0), appDesc);\n                if (failOverResult.isPresent()) {\n                    return ExecuteResult.error(String.format(\"对原主节点执行failover失败，信息如下：%s\", failOverResult.get()));\n                }\n                //failover后，休息120s\n                if(!quickFinishFlag){\n                    try {\n                        TimeUnit.SECONDS.sleep(120);\n                    } catch (InterruptedException e) {\n                    }\n                }\n            }\n        }\n        return ExecuteResult.ok();\n    }\n\n    /**\n     * 修改配置\n     * 修改配置：//只修改从节点（关闭从节点，修改配置文件，重启从节点）\n     * 修改配置：//只修改主节点（选某个从节点failover, 关闭原主节点，修改配置文件，重启原主节点，在原主节点failover）\n     * 修改配置：//修改主和从节点（关闭从节点，修改从节点配置，重启从节点，选某个从节点failover, 关闭原主节点，修改原主节点配置，重启原主节点，根据条件（在原主节点failover））\n     * @param appDesc\n     * @param master\n     * @param slaveList\n     * @param masterSlaveExistEnum\n     * @param transferFlag\n     * @return\n     */\n    private String configOneGroup(long recordId, int runGroup, AppDesc appDesc, InstanceInfo master, List<InstanceInfo> slaveList, Map<String, Set<String>> redisConfigMap, MasterSlaveExistEnum masterSlaveExistEnum , boolean transferFlag){\n        if(MasterSlaveExistEnum.MASTRE_SLAVE.equals(masterSlaveExistEnum) || CollectionUtils.isEmpty(redisConfigMap) || MasterSlaveExistEnum.SLAVE.equals(masterSlaveExistEnum)){\n            //重启并修改从节点\n            String res = this.configNodes(appDesc, recordId, runGroup, slaveList, redisConfigMap);\n            if (StringUtils.isNotEmpty(res)) {\n                return res;\n            }\n        }\n        if(!MasterSlaveExistEnum.SLAVE.equals(masterSlaveExistEnum)){\n            //重启并修改原主节点\n            long statTime = System.currentTimeMillis();\n            Map<String, String> configByCommand = this.getConfigByCommand(appDesc, master, redisConfigMap);\n            String res = this.configNode(appDesc, master, redisConfigMap);\n            long endTime = System.currentTimeMillis();\n            this.saveConfigRestartLog(recordId, this.generateLog(\"第%s分组，主节点信息：%s，原配置信息:%s，结果：%s，耗时：%s\", runGroup, master.getHostPort(), configByCommand, (StringUtil.isBlank(res) ? \"成功\" : \"失败\"), this.getTimeBetween(endTime, statTime)));\n            if (StringUtils.isNotEmpty(res)) {\n                return res;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 修改配置从节点\n     * @param instanceInfoList\n     * @param redisConfigMap\n     * @return\n     */\n    private String configNodes(AppDesc appDesc, long recordId, int runGroup, List<InstanceInfo> instanceInfoList, Map<String, Set<String>> redisConfigMap) {\n        StringBuilder sb = new StringBuilder();\n        for(InstanceInfo instanceInfo : instanceInfoList){\n            long statTime = System.currentTimeMillis();\n            Map<String, String> configByCommand = this.getConfigByCommand(appDesc, instanceInfo, redisConfigMap);\n            String res = this.configNode(appDesc, instanceInfo, redisConfigMap);\n            long endTime = System.currentTimeMillis();\n            //获取原配置信息\n            this.saveConfigRestartLog(recordId, this.generateLog(\"第%s分组，从节点信息:%s，原配置信息:%s，结果：%s，耗时：%s\", runGroup, instanceInfo.getHostPort(), configByCommand, (StringUtil.isBlank(res) ? \"成功\" : \"失败\"), this.getTimeBetween(endTime, statTime)));\n            if(StringUtils.isNotEmpty(res)){\n                sb.append(res);\n                break;\n            }\n        }\n        return sb.toString();\n    }\n\n    /**\n     * 修改配置从节点\n     * @param instanceInfo\n     * @param redisConfigMap\n     * @return\n     */\n    private String configNode(AppDesc appDesc, InstanceInfo instanceInfo, Map<String, Set<String>> redisConfigMap) {\n        boolean executeFlag = false;\n        if(CollectionUtils.isEmpty(redisConfigMap)) {\n            return null;\n        }\n        //修改配置\n        executeFlag = this.updateConfigFile(instanceInfo, redisConfigMap);\n        if(!executeFlag){\n            return String.format(\"关闭节点后，修改配置文件失败，需进行手动重启，节点信息如下：appId:%s，instanceId:%s，hostPort:%s\", instanceInfo.getAppId(), instanceInfo.getId(), instanceInfo.getHostPort());\n        }\n        return null;\n    }\n\n    /**\n     * 依次重启从节点，一旦有一个失败，则返回\n     * @param appDesc\n     * @param slaveList\n     * @param currentMaster\n     * @return\n     */\n    private ExecuteResult restartSlaveNodes(long recordId, AppDesc appDesc, List<InstanceInfo> slaveList, InstanceInfo currentMaster) {\n        ExecuteResult executeResult = ExecuteResult.error();\n        StringBuilder sb = new StringBuilder();\n        for(int i = 0; i < slaveList.size(); i++){\n            InstanceInfo instanceInfo = slaveList.get(i);\n            Optional<String> restartResult = this.restartSlaveNode(recordId, appDesc, instanceInfo, currentMaster);\n            log.info(String.format(\"重启第%s个从节点，从节点信息：%s，结果为：%s\", (i + 1), instanceInfo.getHostPort(), (restartResult.isPresent() ? restartResult.get() : \"成功\")));\n//            appScrollRestartService.saveConfigRestartLog(recordId, this.generateLog(\"重启第%s个从节点，从节点信息：%s，结果为：%s\", (i + 1), instanceInfo.getHostPort(), (restartResult.isPresent() ? restartResult.get() : \"成功\")));\n            if(restartResult.isPresent()){\n                sb.append(String.format(\"重启从节点失败，instance:%s，失败信息：%s\", instanceInfo.getHostPort(), restartResult.get()));\n                sb.append(\"\\n\");\n                return executeResult.error(sb.toString());\n            }else{\n                sb.append(String.format(\"重启从节点成功，instance:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort()));\n                sb.append(\"\\n\");\n            }\n        }\n        return ExecuteResult.ok(sb.toString());\n    }\n\n    /**\n     * 重启节点\n     * @param appDesc\n     * @param instanceInfo\n     * @return\n     */\n    private Optional<String> restartNode(Long recordId, AppDesc appDesc, InstanceInfo instanceInfo) {\n        log.info(String.format(\"restart slave node, shutdown instance, start, recordId: %s, slaveInstance: %s\", recordId, instanceInfo.getHostPort()));\n        boolean executeFlag = this.shutdownInstanceIdempotent(3, recordId, appDesc, instanceInfo);\n        log.info(String.format(\"restart slave node, shutdown instance, end, recordId: %s, slaveInstance: %s, result: %s\", recordId, instanceInfo.getHostPort(), executeFlag));\n        if(!executeFlag){\n            return Optional.of(String.format(\"重启从节点，关闭从节点失败，节点信息：%s\", instanceInfo.getHostPort()));\n        }\n        log.info(String.format(\"restart slave node, start instance, start, recordId: %s, slaveInstance: %s\", recordId, instanceInfo.getHostPort()));\n        executeFlag = startInstance(instanceInfo);\n        log.info(String.format(\"restart slave node, start instance, end, recordId: %s, slaveInstance: %s, result: %s\", recordId, instanceInfo.getHostPort(), executeFlag));\n        if(!executeFlag){\n            return Optional.of(String.format(\"重启从节点，关闭从节点后，重启从节点失败，节点信息：%s\", instanceInfo.getHostPort()));\n        }\n        executeFlag = this.checkLoadFinish(instanceInfo);\n        if(!executeFlag){\n            log.error(String.format(\"重启从节点，关闭从节点后，重启从节点，加载dump文件失败，任务继续。如有需要，请管理员确认，节点信息：%s\", instanceInfo.getHostPort()));\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * 关闭实例，幂等操作，目前最大支持3次\n     * @return\n     */\n    private boolean shutdownInstanceIdempotent(int retryTime, Long recordId, AppDesc appDesc, InstanceInfo instanceInfo){\n        boolean executeFlag = false;\n        long beginShutdown = System.currentTimeMillis();\n        executeFlag = shutdownInstance(instanceInfo);\n        log.info(String.format(\"restart slave node, shutdown instance, recordId: %s, slaveInstance: %s, result: %s\", recordId, instanceInfo.getHostPort(), executeFlag));\n        if(!executeFlag){\n            return false;\n        }\n        //2. check the slave node original process has release config file\n        int tryTimes = 200;\n        long sleepTime = 2L;\n        while(tryTimes-- > 0){\n            try{\n                executeFlag = this.checkFileReleaseAfterShutdown(appDesc, instanceInfo);\n                if(executeFlag){\n                    break;\n                }\n                TimeUnit.SECONDS.sleep(sleepTime);\n                if(tryTimes == 195){\n//                    this.saveConfigRestartLog(recordId, this.generateLog(\"重启从节点, shutdown and check，持续10s仍未完成，请等待留意，节点信息：%s。\", instanceInfo.getHostPort()));\n                }\n                if(tryTimes % 5 == 0){\n                    boolean shutdownFailFlag = this.checkByLog(instanceInfo, \"can't exit\", beginShutdown);\n                    log.info(String.format(\"restart slave node, shutdown instance fail and retry check, recordId: %s, slaveInstance: %s, result: %s\", recordId, instanceInfo.getHostPort(), shutdownFailFlag));\n                    if(shutdownFailFlag && retryTime-- > 0){\n                        log.info(String.format(\"restart slave node, shutdown instance fail and retry, recordId: %s, slaveInstance: %s, retryTime left : %s\", recordId, instanceInfo.getHostPort(), retryTime));\n                        TimeUnit.SECONDS.sleep(20 * (3 - retryTime) > 40 ? 40 : 20 * (3 - retryTime));\n                        return this.shutdownInstanceIdempotent(retryTime, recordId, appDesc, instanceInfo);\n                    }\n                }\n            }catch (Exception e){\n                log.error(\"shutdown Instance error: \", e);\n                continue;\n            }\n        }\n        return executeFlag;\n    }\n\n    /**\n     * 重启从节点\n     * @param appDesc\n     * @param slaveInstance\n     * @param currentMaster\n     * @return\n     */\n    private Optional<String> restartSlaveNode(long recordId, AppDesc appDesc, InstanceInfo slaveInstance, InstanceInfo currentMaster) {\n        //判断当前实例是否为从节点，且存在主节点，且master_link_status  up\n        BooleanEnum slaveAndMasterUp = BooleanEnum.FALSE;\n        int tryTimes = 3;\n        while (tryTimes-- > 0) {\n            try {\n                slaveAndMasterUp = redisCenter.isSlaveAndPointedMasterUp(appDesc, slaveInstance, currentMaster);\n                if (BooleanEnum.TRUE.equals(slaveAndMasterUp)){\n                    break;\n                }\n                TimeUnit.SECONDS.sleep(2L);\n            } catch (Exception e) {\n                log.error(\"restartSlaveNode check is Slave and master is up, error: \", e);\n                continue;\n            }\n        }\n        log.info(String.format(\"restart slave node, isSlaveAndPointedMasterUp check, recordId: %s, slaveInstance: %s, currentMaster: %s, slaveAndMasterUp: %s\", recordId, slaveInstance.getHostPort(), currentMaster.getHostPort(), slaveAndMasterUp));\n        if(BooleanEnum.FALSE.equals(slaveAndMasterUp)){\n            return Optional.of(\"重启从节点，主从状态校验失败\");\n        }\n        return this.restartNode(recordId, appDesc, slaveInstance);\n    }\n\n    /**\n     * 执行failover并检查是否成功\n     * @param slaveInstance\n     * @param currentMaster\n     * @param appDesc\n     * @return\n     */\n    private Optional<String> clusterFailoverSlaveInstanceAndCheck(long recordId, InstanceInfo slaveInstance, InstanceInfo currentMaster, AppDesc appDesc){\n        try{\n            //判断当前实例是否为从节点，且存在主节点，且master_link_status  up\n            BooleanEnum slaveAndMasterUp = BooleanEnum.FALSE;\n            int tryTimes = 300;\n            while (tryTimes-- > 0) {\n                try {\n                    BooleanEnum result = redisCenter.isSlaveAndPointedMasterUp(appDesc, slaveInstance, currentMaster);\n                    if (BooleanEnum.TRUE.equals(result)){\n                        slaveAndMasterUp = result;\n                        break;\n                    }\n                    TimeUnit.SECONDS.sleep(2L);\n                } catch (Exception e) {\n                    log.error(\"clusterFailoverSlaveInstanceAndCheck is Slave and master is pointed master error: \", e);\n                    continue;\n                }\n            }\n            log.info(String.format(\"restart slave node, failover pre check, isSlaveAndPointedMasterUp, recordId: %s, slaveInstance: %s, currentMaster: %s, result: %s\", recordId, slaveInstance.getHostPort(), currentMaster.getHostPort(), slaveAndMasterUp));\n\n            if(BooleanEnum.FALSE.equals(slaveAndMasterUp)){\n                return Optional.of(String.format(\"执行failover and check， 检验为从节点及与主节点连接正常，失败，从节点信息：%s，主节点信息：%s\", slaveInstance.getHostPort(), currentMaster.getHostPort()));\n            }\n            log.info(String.format(\"restart slave node, failoverAndCheckIdempotent, start, recordId: %s, slaveInstance: %s, currentMaster: %s\", recordId, slaveInstance.getHostPort(), currentMaster.getHostPort()));\n            boolean isFailover = this.failoverAndCheckIdempotent(3, recordId, slaveInstance, currentMaster, appDesc);\n            //failover后，检测是否完成主从同步\n            if(isFailover){\n                this.checkSlaveReadyFinish(currentMaster);\n                log.info(String.format(\"restart slave node, failoverAndCheckIdempotent success and check new slave ready, recordId: %s, new slaveInstance: %s, currentMaster: %s, result: %s\", recordId, currentMaster.getHostPort(), slaveInstance.getHostPort(), isFailover));\n            }\n            log.info(String.format(\"restart slave node, failoverAndCheckIdempotent, end, recordId: %s, slaveInstance: %s, currentMaster: %s, result: %s\", recordId, slaveInstance.getHostPort(), currentMaster.getHostPort(), isFailover));\n            if(!isFailover){\n                return Optional.of(String.format(\"执行failover and check失败，从节点信息：%s，主节点信息：%s\", slaveInstance.getHostPort(), currentMaster.getHostPort()));\n            }\n            return Optional.empty();\n        }catch (Exception e){\n            log.error(\"clusterFailoverSlaveInstanceAndCheck is Slave and master is pointed master error: \", e);\n            return Optional.of(String.format(\"执行failover and check，出现异常，异常信息：%s，从节点信息：%s，主节点信息：%s\", e.getMessage(), slaveInstance.getHostPort(), currentMaster.getHostPort()));\n        }\n    }\n\n\n    /**\n     * 关闭实例，幂等操作，目前最大支持3次\n     * @return\n     */\n    private boolean failoverAndCheckIdempotent(int retryTime, long recordId, InstanceInfo slaveInstance, InstanceInfo currentMaster, AppDesc appDesc){\n        long beginShutdown = System.currentTimeMillis();\n        boolean executeFlag = false;\n        try{\n            if(AppTypeEnum.REDIS_CLUSTER.getType() == appDesc.getType()){\n                executeFlag = redisDeployCenter.clusterFailover(slaveInstance.getAppId(), slaveInstance.getId(), null);\n            }\n        }catch (Exception e){\n            log.error(\"cluster failover异常，异常信息：\", e);\n        }\n        log.info(String.format(\"restart slave node, clusterFailover, recordId: %s, slaveInstance: %s, currentMaster: %s, result: %s\", recordId, slaveInstance.getHostPort(), currentMaster.getHostPort(), executeFlag));\n        if (!executeFlag) {\n            return executeFlag;\n        }\n        int tryTimes = 200;\n        long sleepTime = 2L;\n        while(tryTimes-- > 0){\n            try{\n                executeFlag = redisCenter.getRedisReplicationStatus(slaveInstance.getAppId(), slaveInstance.getIp(), slaveInstance.getPort());\n                if (executeFlag) {\n                    break;\n                }\n                TimeUnit.SECONDS.sleep(sleepTime);\n                if(tryTimes == 195){\n//                    this.saveConfigRestartLog(recordId, this.generateLog(\"failover and check, 持续10s仍未完成，请等待留意，节点信息：%s。\", slaveInstance.getHostPort()));\n                }\n                if(tryTimes % 5 == 0){\n                    boolean shutdownFailFlag = this.checkByLog(slaveInstance, \"Manual failover timed out\", beginShutdown);\n                    log.info(String.format(\"restart slave node, clusterFailover fail and retry check, recordId: %s, slaveInstance: %s, currentMaster: %s, result: %s\", recordId, slaveInstance.getHostPort(), currentMaster.getHostPort(), shutdownFailFlag));\n                    if(shutdownFailFlag && retryTime-- > 0){\n                        log.info(String.format(\"restart slave node, clusterFailover fail and retry, recordId: %s, slaveInstance: %s, currentMaster: %s\", recordId, slaveInstance.getHostPort(), currentMaster.getHostPort()));\n                        TimeUnit.SECONDS.sleep(20 * (3 - retryTime) > 40 ? 40 : 20 * (3 - retryTime));\n                        return this.failoverAndCheckIdempotent(retryTime, recordId, slaveInstance, currentMaster, appDesc);\n                    }\n                }\n            }catch (Exception e){\n                log.error(\"failover and check error: \", e);\n                continue;\n            }\n        }\n        return executeFlag;\n    }\n\n    /**\n     * 执行failover并检查\n     * @param slaveInstance 必须包括masterHost & masterPort\n     * @param appDesc\n     * @return\n     */\n    public Optional<String> clusterFailoverAndCheck(InstanceInfo slaveInstance, AppDesc appDesc){\n        InstanceInfo currentMaster = new InstanceInfo();\n        currentMaster.setAppId(slaveInstance.getAppId());\n        currentMaster.setType(slaveInstance.getType());\n        currentMaster.setIp(slaveInstance.getMasterHost());\n        currentMaster.setPort(slaveInstance.getMasterPort());\n        try{\n            // 先判断slave是否已load完成并ready,\n            this.checkLoadFinish(slaveInstance);\n            this.checkSlaveReadyFinish(slaveInstance);\n            //判断当前实例是否为从节点，且存在主节点，且master_link_status  up\n            BooleanEnum slaveAndMasterUp = BooleanEnum.FALSE;\n            int tryTimes = 300;\n            while (tryTimes-- > 0) {\n                try {\n                    BooleanEnum result = redisCenter.isSlaveAndPointedMasterUp(appDesc, slaveInstance, currentMaster);\n                    if (BooleanEnum.TRUE.equals(result)){\n                        slaveAndMasterUp = result;\n                        break;\n                    }\n                    TimeUnit.SECONDS.sleep(2L);\n                } catch (Exception e) {\n                    log.error(\"clusterFailoverAndCheck is Slave and master is pointed master error: \", e);\n                    continue;\n                }\n            }\n            log.info(String.format(\"failover pre check, isSlaveAndPointedMasterUp, slaveInstance: %s, currentMaster: %s, result: %s\", slaveInstance.getHostPort(), currentMaster.getHostPort(), slaveAndMasterUp));\n\n            if(BooleanEnum.FALSE.equals(slaveAndMasterUp)){\n                return Optional.of(String.format(\"执行failover and check， 检验为从节点及与主节点连接正常，失败，从节点信息：%s，主节点信息：%s\", slaveInstance.getHostPort(), currentMaster.getHostPort()));\n            }\n            log.info(String.format(\"failoverAndCheckIdempotent, start, slaveInstance: %s, currentMaster: %s\", slaveInstance.getHostPort(), currentMaster.getHostPort()));\n            boolean isFailover = this.failoverAndCheckIdempotent(3, slaveInstance, appDesc);\n            //failover后，检测是否完成主从同步\n            if(isFailover){\n                this.checkSlaveReadyFinish(currentMaster);\n                log.info(String.format(\"failoverAndCheckIdempotent success and check slave ready, new slaveInstance: %s, currentMaster: %s, result: %s\", currentMaster.getHostPort(), slaveInstance.getHostPort(), isFailover));\n            }\n            log.info(String.format(\"failoverAndCheckIdempotent, end, slaveInstance: %s, currentMaster: %s, result: %s\", slaveInstance.getHostPort(), currentMaster.getHostPort(), isFailover));\n            if(!isFailover){\n                return Optional.of(String.format(\"执行failover and check失败，从节点信息：%s，主节点信息：%s\", slaveInstance.getHostPort(), currentMaster.getHostPort()));\n            }\n            return Optional.empty();\n        }catch (Exception e){\n            log.error(\"clusterFailoverSlaveInstanceAndCheck is Slave and master is pointed master error: \", e);\n            return Optional.of(String.format(\"执行failover and check，出现异常，异常信息：%s，从节点信息：%s，主节点信息：%s\", e.getMessage(), slaveInstance.getHostPort(), currentMaster.getHostPort()));\n        }\n    }\n\n    /**\n     * 关闭实例，幂等操作，目前最大支持3次\n     * @return\n     */\n    @Override\n    public boolean failoverAndCheckIdempotent(int retryTime, InstanceInfo slaveInstance, AppDesc appDesc){\n        long beginShutdown = System.currentTimeMillis();\n        boolean executeFlag = false;\n        try{\n            if(AppTypeEnum.REDIS_CLUSTER.getType() == appDesc.getType()){\n                executeFlag = redisDeployCenter.clusterFailover(slaveInstance.getAppId(), slaveInstance.getId(), null);\n            }\n        }catch (Exception e){\n            log.error(\"cluster failover异常，异常信息：\", e);\n        }\n        log.info(String.format(\"clusterFailover, slaveInstance: %s, currentMaster: %s, result: %s\", slaveInstance.getHostPort(), (slaveInstance.getMasterHost() + \":\" + slaveInstance.getMasterPort()), executeFlag));\n        if (!executeFlag) {\n            return executeFlag;\n        }\n        int tryTimes = 200;\n        long sleepTime = 2L;\n        while(tryTimes-- > 0){\n            try{\n                if(TypeUtil.isRedisType(appDesc.getType())){\n                    executeFlag = redisCenter.getRedisReplicationStatus(slaveInstance.getAppId(), slaveInstance.getIp(), slaveInstance.getPort());\n                }\n                if (executeFlag) {\n                    break;\n                }\n                TimeUnit.SECONDS.sleep(sleepTime);\n                if(TypeUtil.isRedisType(appDesc.getType())){\n                    if(tryTimes % 5 == 0){\n                        boolean shutdownFailFlag = this.checkByLog(slaveInstance, \"Manual failover timed out\", beginShutdown);\n                        log.info(String.format(\"clusterFailover fail and retry check, slaveInstance: %s, currentMaster: %s, result: %s\", slaveInstance.getHostPort(), (slaveInstance.getMasterHost() + \":\" + slaveInstance.getMasterPort()), shutdownFailFlag));\n                        if(shutdownFailFlag && retryTime-- > 0){\n                            log.info(String.format(\"clusterFailover fail and retry, slaveInstance: %s, currentMaster: %s\", slaveInstance.getHostPort(), (slaveInstance.getMasterHost() + \":\" + slaveInstance.getMasterPort())));\n                            TimeUnit.SECONDS.sleep(20 * (3 - retryTime) > 40 ? 40 : 20 * (3 - retryTime));\n                            return this.failoverAndCheckIdempotent(retryTime, slaveInstance, appDesc);\n                        }\n                    }\n                }\n            }catch (Exception e){\n                log.error(\"failover and check error: \", e);\n                continue;\n            }\n        }\n        return executeFlag;\n    }\n\n    /**\n     * 下线实例\n     * @param instanceInfo\n     * @return\n     */\n    public boolean shutdownInstance(InstanceInfo instanceInfo) {\n        if (instanceInfo != null) {\n            try {\n                boolean operationFlag = instanceDeployCenter.shutdownExistInstance(instanceInfo.getAppId(), instanceInfo.getId());\n                log.info(\"shutdown slave, appId:${}, instance:${}, result:${}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), operationFlag);\n                return operationFlag;\n            } catch (Exception e) {\n                log.error(\"shutdown slave, appId: \" + instanceInfo.getAppId() +\", instance:\" + instanceInfo.getHostPort() + \",异常：\", e);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 启动实例\n     * @param instanceInfo\n     */\n    public boolean startInstance(InstanceInfo instanceInfo) {\n        if (instanceInfo != null) {\n            try {\n                boolean operationFlag = instanceDeployCenter.startExistInstanceWithoutResourceCheck(instanceInfo.getAppId(), instanceInfo.getId());\n                log.info(\"restart slave node, start instance, appId:${}, instance:${}, result:${}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), operationFlag);\n                return operationFlag;\n            } catch (Exception e) {\n                log.error(\"restart slave node, start instance, error, appId: \" + instanceInfo.getAppId() +\", instance:\" + instanceInfo.getHostPort() + \",异常：\", e);\n                return false;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断实例是否完成加载dump文件\n     * @param instanceInfo\n     */\n    public boolean checkLoadFinish(InstanceInfo instanceInfo) {\n        boolean loadFinish = false;\n        if (instanceInfo != null) {\n            int retry = 3;\n            while (retry-- > 0) {\n                // 1.获取连接\n                try (Jedis jedis = redisCenter.getAdminJedis(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), 5000, 5000);) {\n                    if(TypeUtil.isRedisType(instanceInfo.getType())){\n                        loadFinish = redisCenter.checkLoadFinish(jedis, 5);\n                    }\n                    log.info(\"checkLoadFinish instance, appId:${}, instance:${}, result:${}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), loadFinish);\n                    if(loadFinish){\n                        break;\n                    }\n                } catch (Exception e) {\n                    log.error(\"restart slave node, start instance, error, appId: \" + instanceInfo.getAppId() + \", instance:\" + instanceInfo.getHostPort() + \",异常：\", e);\n                }\n            }\n        }\n        return loadFinish;\n    }\n\n    /**\n     * 判断实例是否准备完成\n     * @param instanceInfo\n     */\n    public boolean checkSlaveReadyFinish(InstanceInfo instanceInfo) {\n        boolean slaveReady = false;\n        if (instanceInfo != null) {\n            try {\n                int checkTimes = 100;\n                while (checkTimes-- > 0){\n                    // 1.获取连接\n                    try (Jedis jedis = redisCenter.getAdminJedis(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), 5000, 5000);){\n                        if(TypeUtil.isRedisType(instanceInfo.getType())){\n                            slaveReady = redisCenter.checkSlaveReady(jedis, null);\n                        }\n                        log.info(\"checkSlaveReadyFinish instance, appId:${}, instance:${}, result:${}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), slaveReady);\n                        if(slaveReady){\n                            break;\n                        }\n                        TimeUnit.SECONDS.sleep(5);\n                    } catch (Exception e) {\n                        log.error(\"checkSlaveReadyFinish, instance, error, appId: \" + instanceInfo.getAppId() +\", instance:\" + instanceInfo.getHostPort() + \",异常：\", e);\n                    }\n                }\n            } catch (Exception e) {\n                log.error(\"{}:{} checkSlaveReadyFinish failed {}\", instanceInfo.getIp(), instanceInfo.getPort(), e.getMessage(), e);\n            }\n        }\n        return slaveReady;\n    }\n\n    /**\n     * check whether config set is ok with this to updated config name\n     * just support check one config name, if more than one, update this logic code\n     * @param instanceInfo\n     * @return 0:失败；1：成功；2：配置值错误\n     */\n    private int checkConfigSetAvailable(AppDesc appDesc, InstanceInfo instanceInfo, Map<String, Set<String>> redisConfigMap) {\n        int checkResult = 0;\n        // 1.获取连接\n        final Jedis jedis = redisCenter.getAdminJedis(appDesc.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), 5000, 5000);\n        try {\n            int retry = 3;\n            while (retry-- > 0) {\n                Set<Map.Entry<String, Set<String>>> entrySet = redisConfigMap.entrySet();\n                for (Map.Entry<String, Set<String>> entry : entrySet) {\n                    String configName = entry.getKey();\n                    Set<String> configValueSet = entry.getValue();\n                    StringBuilder sb = new StringBuilder();\n                    for (String configValue : configValueSet) {\n                        sb.append(configValue);\n                        sb.append(\" \");\n                    }\n                    sb.deleteCharAt(sb.length() - 1);\n                    try {\n                        String result = jedis.configSet(configName, sb.toString());\n                        if (result != null) {\n                            log.info(String.format(\"check config set available, config set, appId:%s, instance:%s, configName:%s, configValue:%s, result=%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), configName, sb, result));\n                            if (result.equalsIgnoreCase(\"OK\")) {\n                                checkResult = 1;\n                                continue;\n                            } else if (result.equalsIgnoreCase(\"Invalid argument\")) {\n                                checkResult = 2;\n                                continue;\n                            }\n                        }\n                    } catch (Exception e) {\n                        log.error(e.getMessage(), e);\n                        if (e instanceof JedisDataException && e.getMessage() != null && e.getMessage().contains(\"Invalid argument\")) {\n                            checkResult = 2;\n                            continue;\n                        }\n                    }\n                }\n                if(checkResult != 0){\n                    break;\n                }\n            }\n            return checkResult;\n        } catch (Exception e) {\n            log.error(String.format(\"check config set available, appId:%s, instance:%s, 异常：\", instanceInfo.getAppId(), instanceInfo.getHostPort()), e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return checkResult;\n    }\n\n    /**\n     * config set and rewrite\n     * @param instanceInfo\n     * @return\n     */\n    private boolean configSetAndRewrite(AppDesc appDesc, InstanceInfo instanceInfo, Map<String, Set<String>> redisConfigMap) {\n        // 1.获取连接\n        final Jedis jedis = redisCenter.getAdminJedis(appDesc.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), 5000, 5000);\n        try {\n            boolean isConfig = new IdempotentConfirmer() {\n                @Override\n                public boolean execute() {\n                    boolean isRun = redisCenter.isRun(appDesc.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n                    if (!isRun) {\n                        log.warn(\"config set and rewrite, check is run fail, instance is not run, appId:${}, instance:${}\", instanceInfo.getAppId(), instanceInfo.getHostPort());\n                        return false;\n                    }\n                    Set<Map.Entry<String, Set<String>>> entrySet = redisConfigMap.entrySet();\n                    boolean isConfig =false;\n                    for (Map.Entry<String, Set<String>> entry : entrySet){\n                        String configName = entry.getKey();\n                        Set<String> configValueSet = entry.getValue();\n                        StringBuilder sb = new StringBuilder();\n                        for (String configValue : configValueSet) {\n                            sb.append(configValue);\n                            sb.append(\" \");\n                        }\n                        sb.deleteCharAt(sb.length() - 1);\n                        String result = jedis.configSet(configName, sb.toString());\n                        isConfig = result != null && result.equalsIgnoreCase(\"OK\");\n                        if (!isConfig) {\n                            log.error(String.format(\"config set and rewrite, config set fail, appId:%s, instance:%s, configName:%s, configValue:%s, result=%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), configName, sb.toString(), result));\n                            return false;\n                        }\n                    }\n                    return isConfig;\n                }\n            }.run();\n            String response = jedis.configRewrite();\n            boolean isRewrite = response != null && response.equalsIgnoreCase(\"OK\");\n            if (!isRewrite) {\n                log.error(String.format(\"config set and rewrite, config rewrite fail, appId:%s, instance:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort()));\n            }\n            return isConfig;\n        } catch (Exception e) {\n            log.error(String.format(\"config set and rewrite, occur error, appId:%s, instance:%s, error:\", instanceInfo.getAppId(), instanceInfo.getHostPort()), e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n        return false;\n    }\n\n    /**\n     * 通过config get获取配置项的值\n     * @param appDesc\n     * @param instanceInfo\n     * @param redisConfigMap\n     * @return\n     */\n    private Map<String, String> getConfigByCommand(AppDesc appDesc, InstanceInfo instanceInfo, Map<String, Set<String>> redisConfigMap){\n        Map<String, String> configNameValueMap = new HashMap<>();\n        Set<String> configNameSet = redisConfigMap.keySet();\n        if(configNameSet.size() > 0) {\n            String configValue = null;\n            // 1.获取连接\n            final Jedis jedis = redisCenter.getAdminJedis(appDesc.getAppId(), instanceInfo.getIp(), instanceInfo.getPort(), 5000, 5000);\n            try {\n                for (String configName : configNameSet) {\n                    try {\n                        List<String> strings = jedis.configGet(configName);\n                        if (strings != null && strings.size() > 1) {\n                            configValue = strings.get(1);\n                        }\n                    } catch (Exception e) {\n                        if (e instanceof JedisConnectionException) {\n                            configValue = \"连接失败，未取到值\";\n                        } else if (e instanceof JedisDataException) {\n                            if (e.getMessage().contains(\"ERR unknown command `CONFIG`, with args beginning with: `get`, \")) {\n                                configValue = \"无此配置，未取到值\";\n                            }\n                        }\n                        if (configValue == null) {\n                            configValue = \"异常，未取到值\";\n                        }\n                        log.error(\"getConfigByCommand\", e);\n                    }\n                    configNameValueMap.put(configName, configValue);\n                }\n            } finally {\n                if (jedis != null)\n                    jedis.close();\n            }\n        }\n        return configNameValueMap;\n    }\n\n    /**\n     * 將redis config 进行合并\n     * @param redisConfigList\n     * @return\n     */\n    private Map<String, Set<String>> groupRedisConfigByName(List<RedisConfigVo> redisConfigList){\n        Map<String, Set<String>> map = new HashMap<>();\n        for (RedisConfigVo redisConfigVo : redisConfigList) {\n            if(map.containsKey(redisConfigVo.getConfigName())){\n                map.get(redisConfigVo.getConfigName()).add(redisConfigVo.getConfigValue());\n            }else{\n                HashSet<String> valueSet = new HashSet<>();\n                valueSet.add(redisConfigVo.getConfigValue());\n                map.put(redisConfigVo.getConfigName(), valueSet);\n            }\n        }\n        return map;\n    }\n\n    /**\n     * config redis.conf file directly\n     * @param instanceInfo\n     * @return\n     */\n    private boolean updateConfigFile(InstanceInfo instanceInfo, Map<String, Set<String>> redisConfigMap){\n        if(CollectionUtils.isEmpty(redisConfigMap)){\n            return true;\n        }\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        int type = instanceInfo.getType();\n        String confType = \"\";\n        if (TypeUtil.isRedisCluster(type)) {\n            confType = \"redis-cluster-\";\n        } else if (TypeUtil.isRedisSentinel(type)) {\n            confType = \"redis-sentinel-\";\n        } else if (TypeUtil.isRedisStandalone(type)){\n            confType = \"redis-sentinel-\";\n        }\n        String remoteFilePath = getMachineRelativeDir(host, DirEnum.CONF_DIR.getValue()) + confType + port + \".conf\";\n        String backFilePath = remoteFilePath + \".program.bak\";\n        //拷贝副本，命名统一 program.bak\n        StringBuilder command = new StringBuilder();\n        command.append(\"cp \").append(remoteFilePath).append(\" \").append(backFilePath);\n        log.info(String.format(\"update config file, copy file, appId:%s, instance:%s, command:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), command));\n        boolean executeFlag = true;\n        try {\n            SSHUtil.execute(host, command.toString());\n        } catch (SSHException e) {\n            executeFlag = false;\n            log.error(String.format(\"update config file, copy file, appId:%s, instance:%s, command:%s, error: \", instanceInfo.getAppId(), instanceInfo.getHostPort(), command), e);\n        }\n\n        Set<Map.Entry<String, Set<String>>> configSet = redisConfigMap.entrySet();\n        for (Map.Entry<String, Set<String>> entry : configSet) {\n            String configName = entry.getKey();\n            Set<String> configValues = entry.getValue();\n            if(executeFlag){\n                //删除原有配置项\n                StringBuilder removeConfigCommand = new StringBuilder();\n                removeConfigCommand.append(\"sed -i '/^\").append(configName).append(\" /d' \").append(remoteFilePath);\n                log.info(String.format(\"update config file, delete config name, appId:%s, instance:%s, command:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), removeConfigCommand));\n                //sed -i '/save /d' redis-test-111.conf\n                try {\n                    SSHUtil.execute(host, removeConfigCommand.toString());\n                } catch (SSHException e) {\n                    executeFlag = false;\n                    log.error(String.format(\"update config file, delete config name, appId:%s, instance:%s, command:%s, error: \", instanceInfo.getAppId(), instanceInfo.getHostPort(), removeConfigCommand), e);\n                }\n            }\n            if(executeFlag){\n                //新增新的配置项\n                StringBuilder addConfigCommand = new StringBuilder();\n                addConfigCommand.append(\"echo -e '\").append(\"\\n\");\n                for(String configValue : configValues){\n                    addConfigCommand.append(configName).append(\" \").append(configValue).append(\"\\n\");\n                }\n                addConfigCommand.append(\"' >> \").append(remoteFilePath);\n                log.info(String.format(\"update config file, add config name value, appId:%s, instance:%s, command:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), addConfigCommand));\n                try {\n                    SSHUtil.execute(host, addConfigCommand.toString());\n                } catch (SSHException e) {\n                    executeFlag = false;\n                    log.error(String.format(\"update config file, add config name value, appId:%s, instance:%s, command:%s, error: \", instanceInfo.getAppId(), instanceInfo.getHostPort(), addConfigCommand), e);\n                }\n            }\n        }\n        return executeFlag;\n    }\n\n    /**\n     * rollback redis.conf file to last version\n     * @param instanceInfo\n     * @return\n     */\n    private boolean rollbackUpdateConfigFile(InstanceInfo instanceInfo){\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        int type = instanceInfo.getType();\n        String confType = \"\";\n        if (TypeUtil.isRedisCluster(type)) {\n            confType = \"redis-cluster-\";\n        } else if (TypeUtil.isRedisSentinel(type)) {\n            confType = \"redis-sentinel-\";\n        } else if (TypeUtil.isRedisStandalone(type)) {\n            confType = \"redis-sentinel-\";\n        }\n        String remoteFilePath = getMachineRelativeDir(host, DirEnum.CONF_DIR.getValue()) + confType + port + \".conf\";\n        String backFilePath = remoteFilePath + \".program.bak\";\n        //拷贝副本，命名统一 program.bak\n        StringBuilder command = new StringBuilder();\n        command.append(\"mv \").append(backFilePath).append(\" \").append(remoteFilePath);\n        log.info(String.format(\"rollback config file to last version, appId:%s, instance:%s, command:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), command));\n        boolean executeFlag = true;\n        try {\n            String execute = SSHUtil.execute(host, command.toString());\n\n            if(StringUtil.isBlank(execute) || (execute != null && execute.contains(\"No such file\"))){\n                executeFlag = true;\n            }else{\n                executeFlag = false;\n            }\n            log.info(String.format(\"rollback config file to last version, appId:%s, instance:%s, command:%s, resultStr:%s, result:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), command, execute, executeFlag));\n        } catch (SSHException e) {\n            executeFlag = false;\n            log.error(String.format(\"rollback config file to last version, appId:%s, instance:%s, command:%s, error: \", instanceInfo.getAppId(), instanceInfo.getHostPort(), command), e);\n        }\n        return executeFlag;\n    }\n\n    /**\n     * 关闭节点后，判断配置文件句柄是否释放\n     * @param appDesc\n     * @param instanceInfo\n     * @return\n     */\n    private boolean checkFileReleaseAfterShutdown(AppDesc appDesc, InstanceInfo instanceInfo) {\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        StringBuilder command = new StringBuilder();\n        command.append(\"ps -ef | grep redis | grep redis-server | grep :\").append(port).append(\"  | grep -v \\\"grep\\\"\");\n        log.info(String.format(\"check config file release, appId:%s, instance:%s, command:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), command));\n        boolean executeFlag = false;\n        try {\n            String execute = SSHUtil.execute(host, command.toString());\n            log.info(String.format(\"check config file release, appId:%s, instance:%s, command:%s, result:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), command, execute));\n            if(StringUtils.isEmpty(execute)){\n                executeFlag = true;\n            }\n        } catch (SSHException e) {\n            executeFlag = false;\n            log.error(String.format(\"check config file release, appId:%s, instance:%s, command:%s, error: \", instanceInfo.getAppId(), instanceInfo.getHostPort(), command), e);\n        }\n        return executeFlag;\n    }\n\n\n    /**\n     * 获取相对路径\n     * @param host\n     * @param dirType\n     * @return\n     */\n    public String getMachineRelativeDir(String host, int dirType) {\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(host);\n        if (machineInfo != null && machineInfo.isK8sMachine(machineInfo.getK8sType())) {\n            return MachineProtocol.getK8sDir(host, dirType);\n        }\n        return MachineProtocol.getDir(dirType);\n    }\n\n    /**\n     * 校验当前版本的配置模板中的配置项，是否支持这些配置\n     * @param appDesc 应用信息\n     * @param redisConfigMap 根据配置名分组的配置信息，value为配置值的set\n     * @return\n     */\n    public boolean checkConfigIsSupport(AppDesc appDesc, Map<String, Set<String>> redisConfigMap){\n        int type = appDesc.getType();\n        int versionId = appDesc.getVersionId();\n        //根据redis version获取配置模板列表\n        List<InstanceConfig> instanceConfigList = instanceConfigDao.getByVersion(versionId);\n        //过滤配置，有效配置，仅根据应用类型保留：2.cluster节点特殊配置（cluster类型是保留）, 5:sentinel节点配置（删除）, 6:redis普通节点（保留）\n        instanceConfigList = instanceConfigList.stream().filter(instanceConfig -> instanceConfig.getType() != 5 && instanceConfig.getStatus() == 1).collect(Collectors.toList());\n        if(!AppTypeEnum.REDIS_CLUSTER.equals(AppTypeEnum.getByType(type))){\n            instanceConfigList = instanceConfigList.stream().filter(instanceConfig -> instanceConfig.getType() != 2).collect(Collectors.toList());\n        }\n        Set<String> configNames = redisConfigMap.keySet();\n        for(String configName : configNames){\n            for (int i = 0; i < instanceConfigList.size(); i++){\n                InstanceConfig instanceConfig = instanceConfigList.get(i);\n                String configKey = instanceConfig.getConfigKey();\n                if(configName.equals(configKey)){\n                    break;\n                }else{\n                    if(configKey.contains(\" \")){\n                        String[] s = configKey.split(\" \");\n                        if(s != null && s.length > 1 && configName.equals(s[0])){\n                            break;\n                        }\n                    }\n                    if(i == instanceConfigList.size() - 1){\n                        return false;\n                    }\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 校验当前版本的配置模板中的配置项，是否支持这些配置\n     * @param redisConfigMap 根据配置名分组的配置信息，value为配置值的set\n     * @return\n     */\n    public boolean testUpdateConfigFileAndRestart(Long appId, Map<String, Set<String>> redisConfigMap){\n        //专用于测试的应用信息\n        AppDesc appDesc = null;\n        List<InstanceInfo> instanceInfoList = null;\n        if(TEST_APP_ID != null){\n            appDesc = appService.getByAppId(TEST_APP_ID);\n            instanceInfoList = appService.getAppInstanceInfo(TEST_APP_ID);\n        }else{\n            appDesc = appService.getByAppId(appId);\n            instanceInfoList = appService.getAppInstanceInfo(appId);\n        }\n\n        if(CollectionUtils.isEmpty(instanceInfoList)){\n            return false;\n        }\n        //获取一个实例用于check config set是否可用\n        InstanceInfo instanceInfo = this.getInstanceToCheckConfigSet(instanceInfoList, null);\n\n        //更新配置文件\n        this.updateConfigFile(instanceInfo, redisConfigMap);\n        //重启节点\n        Optional<String> s1 = this.restartNode(null, appDesc, instanceInfo);\n        log.info(String.format(\"test update config file and restart, appId:%s, instance:%s, resultMsg:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), s1));\n        if(s1.isPresent()){\n            log.error(String.format(\"test update config file and restart, error, rollback config and restart begin, appId:%s, instance:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort()));\n            //失败时，还原配置并重启\n            boolean executeFlag = this.rollbackUpdateConfigFile(instanceInfo);\n            log.error(String.format(\"test update config file and restart error, rollback config and restart, rollback config fail, appId:%s, instance:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort()));\n            if(executeFlag == true){\n                //重启节点\n                Optional<String> s2 = this.restartNode(null, appDesc, instanceInfo);\n                if(s2.isPresent()){\n                    log.error(String.format(\"test update config file and restart, error, rollback config and restart, rollback config success, restart fail, appId:%s, instance:%s, restartResult:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), s2));\n                }\n            }\n            return false;\n        }else{\n            boolean executeFlag = this.rollbackUpdateConfigFile(instanceInfo);\n            log.info(String.format(\"test update config file and restart success, this time just rollback config file, appId:%s, instance:%s, rollback result:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), executeFlag));\n            if(executeFlag == true) {\n                //重启节点\n                Optional<String> s2 = this.restartNode(null, appDesc, instanceInfo);\n                log.info(String.format(\"test update config file and restart success, this time just restart node, appId:%s, instance:%s, restartResult:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), s2));\n                if(s2.isPresent()){\n                    log.error(String.format(\"test update config file and restart success, just rollback this config test, appId:%s, instance:%s, restartResult:%s\", instanceInfo.getAppId(), instanceInfo.getHostPort(), s2));\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    private String generateLog(String format, Object... objects){\n        List<Object> objectsList = new ArrayList<>();\n        if(objects != null){\n            for(int i = 0; i < objects.length; i++){\n                objectsList.add(objects[i]);\n            }\n        }\n        objectsList.add(0, DateUtil.formatYYYYMMddHHMMSS(new Date()));\n        return String.format(\"%s \" + format, objectsList.toArray());\n    }\n\n    private String getTimeBetween(long endTime, long startTime){\n        long l = (endTime - startTime);\n        return l + \"ms\";\n    }\n\n\n    /**\n     * 查看实例日志中，aof rewrite是否被terminated 或 redis是否crashed by signal: 11\n     * @param instanceInfo\n     * @return\n     */\n    private boolean checkByLog(InstanceInfo instanceInfo, String expectValue, long startTime){\n        String recentLog = getRecentLog(instanceInfo);\n        if(StringUtil.isBlank(recentLog)){\n            return false;\n        }\n        String[] logArray = recentLog.split(System.getProperty(\"line.separator\"));\n        if(logArray != null && logArray.length > 1){\n            for(int i = logArray.length - 1; i >= 0; i--){\n                String logInfo = logArray[i];\n                if(logInfo != null && logInfo.contains(expectValue)){\n                    log.info(String.format(\"check by log, log info : %s, time: %s\", logInfo, startTime));\n                    if(!checkLogTimeMeet(logInfo, startTime)){\n                        continue;\n                    }else{\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 查看实例日志中，aof rewrite是否被terminated 或 redis是否crashed by signal: 11\n     * @param instanceInfo\n     * @return\n     */\n    private String getRecentLog(InstanceInfo instanceInfo){\n        String host = instanceInfo.getIp();\n        int port = instanceInfo.getPort();\n        int type = instanceInfo.getType();\n        String logType = \"\";\n        if (TypeUtil.isRedisDataType(type)) {\n            logType = \"redis-\";\n        } else if (TypeUtil.isRedisSentinel(type)) {\n            logType = \"redis-sentinel-\";\n        }\n        String remoteFilePath = getMachineRelativeDir(host, DirEnum.LOG_DIR.getValue()) + logType + port + \"-*.log\";\n        StringBuilder command = new StringBuilder();\n        command.append(\"/usr/bin/tail -n\").append(500).append(\" \").append(remoteFilePath);\n        try {\n            return SSHUtil.execute(host, command.toString());\n        } catch (SSHException e) {\n            log.error(\"实例appId：{}, host:{}, port:{}, getRecentLog errorMsg:{}\", instanceInfo.getAppId(), host, port, e.getMessage());\n            return null;\n        }\n    }\n\n    private boolean checkLogTimeMeet(String logInfo, long beginTime) {\n        //allow machine clock 1s difference\n        beginTime = beginTime - 1 * 1000;\n        boolean flag = false;\n        Calendar calendar = Calendar.getInstance(Locale.US);\n        int year = 0;\n        try{\n            calendar.setTimeInMillis(beginTime);\n            year = calendar.get(Calendar.YEAR);\n        }catch (Exception e){\n            return flag;\n        }\n        SimpleDateFormat sdf = new SimpleDateFormat(\"dd MMM yyyy hh:mm:ss.SSS\", Locale.US);\n        String[] strArray = logInfo.split(\" \");\n\n        if (strArray != null && strArray.length > 5) {\n            StringBuilder sb = new StringBuilder();\n            int strSize = 5;\n            for (int i = 1; i < strSize; i++) {\n                String str = strArray[i];\n                if(i == 3 && !str.equals(String.valueOf(year))){\n                    sb.append(year);\n                    sb.append(\" \");\n                    strSize = 4;\n                }\n                sb.append(str);\n                sb.append(\" \");\n            }\n            sb.deleteCharAt(sb.length() - 1);\n            try {\n                Date parse = sdf.parse(sb.toString());\n                flag = !parse.before(calendar.getTime());\n            } catch (ParseException e) {\n                log.error(\"log time parse error : \", e);\n            }\n        }\n        return flag;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.AsyncThreadPoolFactory;\nimport com.sohu.cache.constant.*;\nimport com.sohu.cache.dao.*;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum;\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.util.AppKeyUtil;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.Pair;\nimport com.sohu.cache.util.TypeUtil;\nimport com.sohu.cache.web.enums.BooleanEnum;\nimport com.sohu.cache.web.enums.DeployInfoEnum;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.enums.UseTypeEnum;\nimport com.sohu.cache.web.service.AppScrollRestartService;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.assertj.core.util.Lists;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport redis.clients.jedis.HostAndPort;\n\nimport javax.annotation.PostConstruct;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 应用操作实现类\n *\n * @author leifu\n * @Time 2014年10月21日\n */\n@Service(\"appService\")\npublic class AppServiceImpl implements AppService {\n\n    private Logger logger = LoggerFactory.getLogger(AppServiceImpl.class);\n    /**\n     * 应用相关dao\n     */\n    @Autowired\n    private AppDao appDao;\n    /**\n     * 应用日志相关dao\n     */\n    @Autowired\n    private AppAuditLogDao appAuditLogDao;\n    /**\n     * 实例相关dao\n     */\n    @Autowired\n    private InstanceDao instanceDao;\n\n    /**\n     * 应用用户关系相关dao\n     */\n    @Autowired\n    private AppToUserDao appToUserDao;\n\n    /**\n     * 应用申请相关dao\n     */\n    @Autowired\n    private AppAuditDao appAuditDao;\n    /**\n     * 用户信息dao\n     */\n    @Autowired\n    private AppUserDao appUserDao;\n    /**\n     * 业务组信息dao\n     */\n    @Autowired\n    private AppBizDao appBizDao;\n    /**\n     * 应用统计dao\n     */\n    @Autowired\n    private AppStatsDao appStatsDao;\n    @Autowired\n    private InstanceStatsDao instanceStatsDao;\n    @Autowired\n    @Lazy\n    private RedisCenter redisCenter;\n    @Autowired\n    @Lazy\n    private MachineCenter machineCenter;\n    @Autowired\n    private MachineDao machineDao;\n    @Autowired\n    private MachineStatsDao machineStatsDao;\n    @Autowired\n    private AsyncService asyncService;\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private AppClientStatisticGatherDao appClientStatisticGatherDao;\n    @Autowired\n    private AppStatsCenter appStatsCenter;\n    @Autowired\n    private AppScrollRestartService appScrollRestartService;\n\n    @PostConstruct\n    public void init() {\n        asyncService.assemblePool(AsyncThreadPoolFactory.APP_POOL, AsyncThreadPoolFactory.APP_THREAD_POOL);\n    }\n\n    @Override\n    public int getAppDescCount(AppUser appUser, AppSearch appSearch) {\n        int count = 0;\n        // 管理员获取全部应用\n        if (AppUserTypeEnum.ADMIN_USER.value().equals(appUser.getType())) {\n            count = appDao.getAllAppCount(appSearch);\n        } else {\n            count = appDao.getUserAppCount(appUser.getId());\n        }\n        return count;\n    }\n\n    @Override\n    public List<InstanceInfo> getAppMasterInstanceInfoList(long appId) {\n        List<InstanceInfo> resultList = new ArrayList<InstanceInfo>();\n        List<InstanceInfo> appInstanceInfoList = getAppInstanceInfo(appId);\n        for (InstanceInfo instanceInfo : appInstanceInfoList) {\n            if ((instanceInfo.isRedisData() || instanceInfo.isPika()) && \"master\".equals(instanceInfo.getRoleDesc())\n                    && instanceInfo.getMasterInstanceId() == 0) {\n                resultList.add(instanceInfo);\n            }\n        }\n        return resultList;\n    }\n\n    @Override\n    public List<AppDesc> getAppDescList(AppUser appUser, AppSearch appSearch) {\n        List<AppDesc> list;\n        // 管理员获取全部应用\n        if (AppUserTypeEnum.ADMIN_USER.value().equals(appUser.getType())) {\n            list = appDao.getAllAppDescList(appSearch);\n        } else {\n            list = appDao.getAppDescList(appUser.getId());\n        }\n        return list;\n    }\n\n    @Override\n    public AppDesc getByAppId(Long appId) {\n        AppDesc appDesc = null;\n        try {\n            appDesc = appDao.getAppDescById(appId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return appDesc;\n    }\n\n    @Override\n    public int save(AppDesc appDesc) {\n        return appDao.save(appDesc);\n    }\n\n    @Override\n    public int update(AppDesc appDesc) {\n        return appDao.update(appDesc);\n    }\n\n    /**\n     * 更新应用 pwd\n     * @param appId\n     * @param appPwd\n     * @return\n     */\n    @Override\n    public int updateAppPwd(long appId, String appPwd){\n        return appDao.updateAppPwd(appId, appPwd);\n    }\n\n    @Override\n    public int updateWithCustomPwd(AppDesc appDesc) {\n        return appDao.updateWithCustomPwd(appDesc);\n    }\n\n    @Override\n    public boolean saveAppToUser(Long appId, Long userId) {\n        try {\n            // 用户id下应用\n            List<AppToUser> list = appToUserDao.getByUserId(userId);\n            if (CollectionUtils.isNotEmpty(list)) {\n                for (AppToUser appToUser : list) {\n                    if (appToUser.getAppId().equals(appId)) {\n                        return true;\n                    }\n                }\n            }\n            appToUserDao.save(new AppToUser(userId, appId));\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n    }\n\n    @Override\n    public void updateAppAuditStatus(Long id, Long appId, Integer status, AppUser appUser) {\n        appAuditDao.updateAppAuditUser(id, status, appUser.getId());\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        AppAudit appAudit = appAuditDao.getAppAudit(id);\n\n        // 只有应用创建才会设置状态\n        if (AppAuditType.APP_AUDIT.getValue() == appAudit.getType()) {\n            if (AppCheckEnum.APP_PASS.value().equals(status)) {\n                appDesc.setStatus(AppStatusEnum.STATUS_PUBLISHED.getStatus());\n                appDesc.setPassedTime(new Date());\n                appDao.update(appDesc);\n            } else if (AppCheckEnum.APP_REJECT.value().equals(status)) {\n                appDesc.setStatus(AppStatusEnum.STATUS_DENY.getStatus());\n                appDao.update(appDesc);\n            }\n        }\n\n        // 保存审批日志\n        AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n                AppAuditLogTypeEnum.APP_CHECK);\n        if (appAuditLog != null) {\n            appAuditLogDao.save(appAuditLog);\n        }\n    }\n\n    @Override\n    public void updateUserAuditStatus(Long id, Integer status, Long operateId) {\n        appAuditDao.updateAppAuditUser(id, status, operateId);\n    }\n\n    @Override\n    public List<AppToUser> getAppToUserList(Long appId) {\n        return appToUserDao.getByAppId(appId);\n    }\n\n    @Override\n    public AppDesc getAppByName(String appName) {\n        return appDao.getByAppName(appName);\n    }\n\n    /**\n     * 获取app 基本实例信息\n     * @param appId\n     * @return\n     */\n    @Override\n    public List<InstanceInfo> getAppBasicInstanceInfo(Long appId) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if(appDesc == null){\n            return new ArrayList<>();\n        }\n        return instanceDao.getInstListByAppId(appId);\n    }\n\n    /**\n     * 获取应用下实例基本信息，并按照主从分组\n     * @param appId\n     * @return\n     */\n    @Override\n    public Map<InstanceInfo, List<InstanceInfo>> getAppInstanceInfoGroup(Long appId){\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if(appDesc == null){\n            return Collections.EMPTY_MAP;\n        }\n        List<InstanceInfo> instanceInfoList = instanceDao.getInstListByAppId(appId);\n        instanceInfoList = instanceInfoList.stream()\n                .filter(instanceInfo -> instanceInfo.getStatus() == InstanceStatusEnum.GOOD_STATUS.getStatus()\n                        && TypeUtil.isRedisDataType(instanceInfo.getType()))\n                .collect(Collectors.toList());\n        if (CollectionUtils.isNotEmpty(instanceInfoList)) {\n            for (InstanceInfo instanceInfo : instanceInfoList) {\n                int type = instanceInfo.getType();\n                if (instanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    continue;\n                }\n                if (TypeUtil.isRedisType(type)) {\n                    if (TypeUtil.isRedisSentinel(type)) {\n                        continue;\n                    }\n                    String host = instanceInfo.getIp();\n                    int port = instanceInfo.getPort();\n                    // 幂等操作\n                    BooleanEnum isMaster = redisCenter.isMaster(appId, host, port);\n                    instanceInfo.setRoleDesc(isMaster);\n                    if (BooleanEnum.FALSE == isMaster) {\n                        HostAndPort hap = redisCenter.getMaster(host, port, appDesc.getAppPassword());\n                        if (hap != null) {\n                            instanceInfo.setMasterHost(hap.getHost());\n                            instanceInfo.setMasterPort(hap.getPort());\n                            for (InstanceInfo innerInfo : instanceInfoList) {\n                                if (innerInfo.getIp().equals(hap.getHost())\n                                        && innerInfo.getPort() == hap.getPort()) {\n                                    instanceInfo.setMasterInstanceId(innerInfo.getId());\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        Map<InstanceInfo, List<InstanceInfo>> resultMap = new HashMap<InstanceInfo, List<InstanceInfo>>();\n        for (InstanceInfo info : instanceInfoList) {\n            String roleDesc = info.getRoleDesc();\n            if (roleDesc != null && roleDesc.equals(\"master\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, info, new ArrayList<InstanceInfo>());\n                resultMap.put(info, list);\n            } else if (roleDesc != null && roleDesc.equals(\"slave\")) {\n                int masterInstanceId = info.getMasterInstanceId();\n                Optional<InstanceInfo> masterInstanceOptional = instanceInfoList.stream().filter(instance -> instance.getId() == masterInstanceId).findFirst();\n                if(masterInstanceOptional.isPresent()){\n                    InstanceInfo masterInstance = masterInstanceOptional.get();\n                    List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, masterInstance, new ArrayList<InstanceInfo>());\n                    list.add(info);\n                    resultMap.put(masterInstance, list);\n                }\n            }\n        }\n        return resultMap;\n    }\n\n    @Override\n    public List<InstanceInfo> getAppInstanceInfo(Long appId) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        String password = appDesc.getAppPassword();\n        List<InstanceInfo> resultList = instanceDao.getInstListByAppId(appId);\n        return getInstancelistInfo(appId, password, resultList);\n    }\n\n    @Override\n    public List<InstanceInfo> getAppOnlineInstanceInfo(Long appId) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if(appDesc == null){\n            return Collections.EMPTY_LIST;\n        }\n        String password = appDesc.getAppPassword();\n        List<InstanceInfo> resultList = instanceDao.getEffectiveInstListByAppId(appId);\n        return getInstancelistInfo(appId, password, resultList);\n    }\n\n    public List<InstanceInfo> getInstancelistInfo(Long appId, String password, List<InstanceInfo> resultList) {\n        if (resultList != null && resultList.size() > 0) {\n            for (InstanceInfo instanceInfo : resultList) {\n                int type = instanceInfo.getType();\n                if (instanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                    continue;\n                }\n                if (TypeUtil.isRedisType(type)) {\n                    if (TypeUtil.isRedisSentinel(type)) {\n                        continue;\n                    }\n                    String host = instanceInfo.getIp();\n                    int port = instanceInfo.getPort();\n                    // 幂等操作\n                    BooleanEnum isMaster = redisCenter.isMaster(appId, host, port);\n                    instanceInfo.setRoleDesc(isMaster);\n                    if (BooleanEnum.FALSE == isMaster) {\n                        HostAndPort hap = redisCenter.getMaster(host, port, password);\n                        if (hap != null) {\n                            instanceInfo.setMasterHost(hap.getHost());\n                            instanceInfo.setMasterPort(hap.getPort());\n                            for (InstanceInfo innerInfo : resultList) {\n                                if (innerInfo.getIp().equals(hap.getHost())\n                                        && innerInfo.getPort() == hap.getPort()) {\n                                    instanceInfo.setMasterInstanceId(innerInfo.getId());\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return resultList;\n    }\n\n    @Override\n    public List<InstanceStats> getAppInstanceStats(Long appId) {\n        List<InstanceStats> instanceStats = instanceStatsDao.getInstanceStatsByAppId(appId);\n        return instanceStats;\n    }\n\n    @Override\n    public SuccessEnum deleteAppToUser(Long appId, Long userId) {\n        try {\n            appToUserDao.deleteAppToUser(appId, userId);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public List<AppAudit> getAppAudits(Integer status, Integer type, Long auditId, Long userId, Long operateId) {\n        List<AppAudit> list = appAuditDao.selectWaitAppAudits(status, type, auditId, userId, operateId);\n        for (Iterator<AppAudit> i = list.iterator(); i.hasNext(); ) {\n            AppAudit appAudit = i.next();\n            AppDesc appDesc = appDao.getAppDescById(appAudit.getAppId());\n            appAudit.setAppDesc(appDesc);\n        }\n        return list;\n    }\n\n    @Override\n    public Map<String, Object> getStatisticGroupByStatus(Long userId, Long operateId, Date startTime, Date endTime) {\n        List<Map<String, Object>> statis = appAuditDao.getStatisticGroupByStatus(userId, operateId, startTime, endTime);\n        return statis.stream().collect(Collectors.toMap(stat -> MapUtils.getString(stat, \"status\"), stat -> MapUtils.getInteger(stat, \"count\")));\n    }\n\n    @Override\n    public Map<String, Object> getStatisticGroupByType(Long userId, Long operateId, Date startTime, Date endTime) {\n        List<Map<String, Object>> statis = appAuditDao.getStatisticGroupByType(userId, operateId, startTime, endTime);\n        return statis.stream().collect(Collectors.toMap(stat -> MapUtils.getString(stat, \"type\"), stat -> MapUtils.getInteger(stat, \"count\")));\n    }\n\n    public List<AppAudit> getAppAudits(Long appid, Integer type) {\n        return appAuditDao.getAppAuditByCondition(appid, type);\n    }\n\n    @Override\n    public AppAudit saveAppScaleApply(AppDesc appDesc, AppUser appUser, String applyMemSize, String appScaleReason,\n                                      AppAuditType appScale) {\n        AppAudit appAudit = new AppAudit();\n        appAudit.setAppId(appDesc.getAppId());\n        appAudit.setUserId(appUser.getId());\n        appAudit.setUserName(appUser.getName());\n        appAudit.setModifyTime(new Date());\n        appAudit.setParam1(applyMemSize);\n        appAudit.setParam2(appScaleReason);\n        appAudit.setInfo(\"扩容申请---申请容量:\" + applyMemSize + \", 申请原因: \" + appScaleReason);\n        appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n        appAudit.setType(appScale.getValue());\n        appAuditDao.insertAppAudit(appAudit);\n\n        //保存扩容申请\n        AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n                AppAuditLogTypeEnum.APP_SCALE_APPLY);\n        if (appAuditLog != null) {\n            appAuditLogDao.save(appAuditLog);\n        }\n\n        return appAudit;\n    }\n\n    @Override\n    public AppAudit saveAppChangeConfig(AppDesc appDesc, AppUser appUser, Long instanceId, String appConfigKey,\n                                        String appConfigValue, String appConfigReason, AppAuditType modifyConfig) {\n        AppAudit appAudit = new AppAudit();\n        appAudit.setAppId(appDesc.getAppId());\n        appAudit.setUserId(appUser.getId());\n        appAudit.setUserName(appUser.getName());\n        appAudit.setModifyTime(new Date());\n        appAudit.setParam1(String.valueOf(instanceId));\n        appAudit.setParam2(appConfigKey);\n        appAudit.setParam3(appConfigValue);\n        appAudit.setInfo(\"修改配置项:\" + appConfigKey + \", 配置值: \" + appConfigValue + \", 修改原因: \" + appConfigReason);\n        appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n        appAudit.setType(modifyConfig.getValue());\n        appAuditDao.insertAppAudit(appAudit);\n\n        //保存日志\n        AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n                AppAuditLogTypeEnum.APP_CONFIG_APPLY);\n        if (appAuditLog != null) {\n            appAuditLogDao.save(appAuditLog);\n        }\n\n        return appAudit;\n\n    }\n\n    @Override\n    public AppAudit saveInstanceChangeConfig(AppDesc appDesc, AppUser appUser, Long instanceId,\n                                             String instanceConfigKey, String instanceConfigValue, String instanceConfigReason,\n                                             AppAuditType instanceModifyConfig) {\n        AppAudit appAudit = new AppAudit();\n        long appId = appDesc.getAppId();\n        appAudit.setAppId(appId);\n        appAudit.setUserId(appUser.getId());\n        appAudit.setUserName(appUser.getName());\n        appAudit.setModifyTime(new Date());\n        appAudit.setParam1(String.valueOf(instanceId));\n        appAudit.setParam2(instanceConfigKey);\n        appAudit.setParam3(instanceConfigValue);\n        InstanceInfo instanceInfo = instanceDao.getInstanceInfoById(instanceId);\n        String hostPort = instanceInfo == null ? \"\" : (instanceInfo.getIp() + \":\" + instanceInfo.getPort());\n        appAudit.setInfo(\n                \"appId=\" + appId + \"下的\" + hostPort + \"实例申请修改配置项:\" + instanceConfigKey + \", 配置值: \" + instanceConfigValue\n                        + \", 修改原因: \" + instanceConfigReason);\n        appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n        appAudit.setType(instanceModifyConfig.getValue());\n        appAuditDao.insertAppAudit(appAudit);\n\n        //保存日志\n        AppAuditLog appAuditLog = AppAuditLog\n                .generate(appDesc, appUser, appAudit.getId(), AppAuditLogTypeEnum.INSTANCE_CONFIG_APPLY);\n        if (appAuditLog != null) {\n            appAuditLogDao.save(appAuditLog);\n        }\n\n        return appAudit;\n    }\n\n    @Override\n    public SuccessEnum updateRefuseReason(AppAudit appAudit, AppUser userInfo) {\n        try {\n            appAuditDao.updateRefuseReason(appAudit.getId(), appAudit.getRefuseReason());\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public int getUserAppCount(Long userId) {\n        int count = 0;\n        try {\n            // 表比较小\n            List<AppToUser> list = appToUserDao.getByUserId(userId);\n            if (CollectionUtils.isNotEmpty(list)) {\n                count = list.size();\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return count;\n    }\n\n    @Override\n    public List<MachineStats> getAppMachineDetail(Long appId) {\n        //应用信息\n        Assert.isTrue(appId != null && appId > 0L);\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(\"appDesc:id={} is not exist\", appId);\n            return Collections.emptyList();\n        }\n\n        //应用实例列表\n        List<InstanceInfo> appInstanceList = getAppInstanceInfo(appId);\n        if (CollectionUtils.isEmpty(appInstanceList)) {\n            return Collections.emptyList();\n        }\n\n        //防止重复\n        Set<String> instanceMachineHosts = new HashSet<String>();\n        //结果列表\n        List<MachineStats> machineDetailVOList = new ArrayList<MachineStats>();\n        //应用的机器信息\n        for (InstanceInfo instanceInfo : appInstanceList) {\n            String ip = instanceInfo.getIp();\n            if (instanceMachineHosts.contains(ip)) {\n                continue;\n            } else {\n                instanceMachineHosts.add(ip);\n            }\n            MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip);\n            if (machineStats == null) {\n                continue;\n            }\n            //已经分配的内存\n            int memoryHost = instanceDao.getMemoryByHost(ip);\n            machineStats.setMemoryAllocated(memoryHost);\n            //机器信息\n            MachineInfo machineInfo = machineCenter.getMachineInfoByIp(ip);\n            if (machineInfo == null) {\n                continue;\n            }\n            //下线机器不展示\n            if (machineInfo.isOffline()) {\n                continue;\n            }\n            machineStats.setInfo(machineInfo);\n            machineDetailVOList.add(machineStats);\n        }\n        return machineDetailVOList;\n    }\n\n    @Override\n    public List<MachineStats> getAppMachine(Long appId) {\n        //应用信息\n        Assert.isTrue(appId != null && appId > 0L);\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            logger.error(\"appDesc:id={} is not exist\", appId);\n            return Collections.emptyList();\n        }\n\n        //应用实例列表\n        List<InstanceInfo> appInstanceList = getAppInstanceInfo(appId);\n        if (CollectionUtils.isEmpty(appInstanceList)) {\n            return Collections.emptyList();\n        }\n\n        //防止重复\n        Set<String> instanceMachineHosts = new HashSet<String>();\n        //结果列表\n        List<MachineStats> machineDetailVOList = new ArrayList<MachineStats>();\n        //应用的机器信息\n        for (InstanceInfo instanceInfo : appInstanceList) {\n            if(!instanceInfo.isOffline()){\n                String ip = instanceInfo.getIp();\n                if (instanceMachineHosts.contains(ip)) {\n                    continue;\n                } else {\n                    instanceMachineHosts.add(ip);\n                }\n                MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip);\n                if (machineStats == null) {\n                    continue;\n                }\n                //已经分配的内存\n                int memoryHost = instanceDao.getMemoryByHost(ip);\n                machineStats.setMemoryAllocated(memoryHost);\n                //机器信息\n                MachineInfo machineInfo = machineCenter.getMachineInfoByIp(ip);\n                if (machineInfo == null) {\n                    continue;\n                }\n                //下线机器不展示\n                if (machineInfo.isOffline()) {\n                    continue;\n                }\n                machineStats.setInfo(machineInfo);\n                machineDetailVOList.add(machineStats);\n            }\n        }\n        return machineDetailVOList;\n    }\n\n\n    @Override\n    public AppAudit getAppAuditById(Long appAuditId) {\n        return appAuditDao.getAppAudit(appAuditId);\n    }\n\n    @Override\n    public List<AppAudit> getAppAuditListByAppId(Long appId) {\n        Assert.isTrue(appId != null && appId > 0L);\n        List<AppAudit> appAudits = appAuditDao.getAppAuditByAppId(appId);\n        if (CollectionUtils.isNotEmpty(appAudits)) {\n            for (AppAudit appAudit : appAudits) {\n                Long appAuditId = appAudit.getId();\n                List<AppAuditLog> logList = appAuditLogDao.getAuditByType(appAuditId, AppAuditLogTypeEnum.APP_CHECK.value());\n                AppAuditLog log = null;\n                if (CollectionUtils.isNotEmpty(logList)) {\n                    log = logList.get(0);\n                    log.setAppUser(appUserDao.get(log.getUserId()));\n                }\n                appAudit.setAppAuditLog(log);\n            }\n        }\n        return appAudits;\n    }\n\n    @Override\n    public AppAudit saveRegisterUserApply(AppUser appUser, AppAuditType registerUserApply) {\n        AppAudit appAudit = new AppAudit();\n        appAudit.setAppId(0);\n        appAudit.setUserId(appUser.getId());\n        appAudit.setUserName(appUser.getName());\n        appAudit.setModifyTime(new Date());\n        StringBuilder info = new StringBuilder();\n        info.append(appUser.getChName() + \"申请成为Cachecloud用户, 手机:\" + appUser.getMobile() + \",邮箱:\" + appUser.getEmail()\n                + \",微信:\" + appUser.getWeChat());\n        if(!StringUtils.isEmpty(appUser.getCompany())){\n            info.append(\",公司:\" + appUser.getCompany());\n        }\n        if(!StringUtils.isEmpty(appUser.getPurpose())){\n            info.append(\",使用目的:\" + appUser.getPurpose());\n        }\n        if(!StringUtils.isEmpty(appUser.getBizId())){\n            AppBiz appBiz = appBizDao.get(appUser.getBizId());\n            if(appBiz != null && !StringUtils.isEmpty(appBiz.getName())){\n                info.append(\",归属业务组:\" + appBiz.getName());\n            }\n        }\n        appAudit.setInfo(info.toString());\n        appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n        appAudit.setType(registerUserApply.getValue());\n        appAuditDao.insertAppAudit(appAudit);\n        return appAudit;\n    }\n\n    @Override\n    public List<AppDesc> getAllAppDesc() {\n        return appDao.getAllAppDescList(null);\n    }\n\n    @Override\n    public SuccessEnum changeAppAlertConfig(long appId, int memAlertValue, int clientConnAlertValue,\n                                            int hitPrecentAlertConfig, int isAccessMonitor, AppUser appUser) {\n        if (appId <= 0 || memAlertValue <= 0 || clientConnAlertValue <= 0) {\n            return SuccessEnum.FAIL;\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return SuccessEnum.FAIL;\n        }\n        try {\n            // 修改报警阀值\n            appDesc.setMemAlertValue(memAlertValue);\n            appDesc.setClientConnAlertValue(clientConnAlertValue);\n            appDesc.setHitPrecentAlertValue(hitPrecentAlertConfig);\n            appDesc.setIsAccessMonitor(isAccessMonitor);\n            appDao.update(appDesc);\n            // 添加日志\n            AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, 0L, AppAuditLogTypeEnum.APP_CHANGE_ALERT);\n            if (appAuditLog != null) {\n                appAuditLogDao.save(appAuditLog);\n            }\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public void updateAppKey(long appId) {\n        appDao.updateAppKey(appId, AppKeyUtil.genSecretKey(appId));\n    }\n\n    @Override\n    public String generateDeployInfo(Integer type,\n                                     Integer isSalve,\n                                     String room,\n                                     Double size,\n                                     Integer machineNum,\n                                     Integer instanceNum,\n                                     Integer useType,\n                                     String machines,\n                                     String excludeMachines,\n                                     String sentinelMachines,\n                                     List<DeployInfo> deployInfoList,\n                                     List<MachineMemStatInfo> resMachines) {\n        String result = DeployInfoEnum.SUCCESS.getValue();\n        // 获取有效可用机器列表\n        List<String> excludeMachineList = Arrays.asList(excludeMachines.split(\",\"));\n        List<String> sentinelMachineLIst = Arrays.asList(sentinelMachines.split(\",\"));\n        //存储满足条件的机器ip\n        List<MachineMemStatInfo> machineCandi = new ArrayList<MachineMemStatInfo>();\n        //存储real machines ip\n        //Set<String> realMachines = new HashSet<String>();\n        //每个机器的需求size，向上取整\n        Double reqSize = Math.ceil(size / instanceNum) * Math.ceil(instanceNum * 1.0D / machineNum);\n        Integer reqCpu = instanceNum % machineNum == 0 ? instanceNum / machineNum : instanceNum / machineNum + 1;\n\n        if (useType == UseTypeEnum.Machine_special.getValue()) {\n            if (machines != null && !machines.isEmpty()) {\n                List<String> ipList = Arrays.asList(machines.split(\",\"));\n                if (ipList.size() < machineNum) {\n                    logger.info(\"指定机器数小于分配机器数\");\n                    result = \"指定机器数小于分配机器数\";\n                    return result;\n                }\n                List<MachineMemStatInfo> tmp = machineCenter.getValidMachineMemByIpList(ipList);\n                for (MachineMemStatInfo memStatInfo : tmp) {\n                    resMachines.add(memStatInfo);\n                }\n            } else {\n                logger.info(\"部署类型为专用，指定机器为空\");\n                result = \"machine list is null\";\n                return result;\n            }\n        } else {\n            List<MachineMemStatInfo> machineMemStatInfoList = machineCenter\n                    .getAllValidMachineMem(excludeMachineList, room, useType);\n            for (MachineMemStatInfo memStatInfo : machineMemStatInfoList) {\n                getMachineCandiList(memStatInfo, reqSize, reqCpu, isSalve, machineCandi);\n            }\n            getResMachines(machineCandi, machineNum, resMachines);\n            if ((resMachines == null || resMachines.size() == 0) && useType == UseTypeEnum.Machine_test.getValue()) {\n                getResMachines(machineMemStatInfoList, machineNum, resMachines);\n            }\n        }\n\n        if (resMachines == null || resMachines.size() == 0) {\n            logger.info(\"无可用机器\");\n            result = \"可用机器数小于指定机器数\";\n            return result;\n        }\n\n        getDeployInfo(type, isSalve, resMachines, size, instanceNum, sentinelMachineLIst, deployInfoList);\n        logger.info(\"deployInfoList: {}\", deployInfoList);\n\n        return result;\n    }\n\n    /**\n     * @Description: 通过机器列表生成部署详情\n     * @Author: caoru\n     * @CreateDate: 2018/9/25 22:03\n     */\n    private void getDeployInfo(Integer type, Integer isSalve, List<MachineMemStatInfo> machineList, Double size,\n                               Integer instanceNum, List<String> sentinelMachineLIst, List<DeployInfo> deployInfoList) {\n        if (machineList == null)\n            return;\n\n        try {\n            /**\n             * redis cluster\n             */\n            if (TypeUtil.isRedisCluster(type)) {\n                Double instanceSize = Math.ceil(size / instanceNum);\n                //masterIp-memSize\n                for (int i = 0; i < instanceNum; i++) {\n                    DeployInfo deployInfo = new DeployInfo(type, machineList.get(i % machineList.size()).getIp(),\n                            instanceSize.intValue());\n                    deployInfoList.add(deployInfo);\n                }\n                //+slaveIp\n                if (isSalve == 1) {\n                    int i = 0;\n                    for (int j = 0; j < deployInfoList.size(); j++) {\n                        DeployInfo deployInfo = deployInfoList.get(j);\n                        while (isSameRealMachine(deployInfo.getMasterIp(),\n                                machineList.get(i % machineList.size()).getIp())) {\n                            i++;\n                        }\n                        deployInfo.setSlaveIp(machineList.get(i % machineList.size()).getIp());\n                        i++;\n                    }\n                }\n            }\n            /**\n             * redis Sentinel\n             */\n            else if (TypeUtil.isRedisSentinel(type)) {\n                if (isSalve == 1) {//master:size:slave\n                    DeployInfo deployInfo = new DeployInfo(type, machineList.get(0).getIp(), size.intValue());\n                    int i = 0;\n                    while (isSameRealMachine(deployInfo.getMasterIp(),\n                            machineList.get(i % machineList.size()).getIp())) {\n                        i++;\n                    }\n                    deployInfo.setSlaveIp(machineList.get(i % machineList.size()).getIp());\n                    deployInfoList.add(deployInfo);\n                } else {//master:size\n                    DeployInfo deployInfo = new DeployInfo(type, machineList.get(0).getIp(), size.intValue());\n                    deployInfoList.add(deployInfo);\n                }\n                //sentinel list\n                for (int i = 0; i < sentinelMachineLIst.size(); i++) {\n                    DeployInfo sentinel = new DeployInfo(type, sentinelMachineLIst.get(i));\n                    deployInfoList.add(sentinel);\n                }\n                machineList.addAll(machineCenter.getValidMachineMemByIpList(sentinelMachineLIst));\n\n            }\n            /**\n             * redis standalone\n             */\n            else if (TypeUtil.isRedisStandalone(type)) {\n                //master:size\n                DeployInfo deployInfo = new DeployInfo(type, machineList.get(0).getIp(), size.intValue());\n                deployInfoList.add(deployInfo);\n            }\n        } catch (Exception ex) {\n            logger.warn(ex.getMessage());\n            logger.warn(\"getDeployInfo error\");\n        }\n    }\n\n    /**\n     * @Description: 获取候选机器列表\n     * @Author: caoru\n     * @CreateDate: 2018/10/8 20:43\n     */\n    public void getMachineCandiList(MachineMemStatInfo memStatInfo, Double reqSize, Integer reqCpu, Integer isSalve,\n                                    List<MachineMemStatInfo> machineCandi) {\n        Integer mem = memStatInfo.getMem();//单位G\n        Long applyMen = memStatInfo.getApplyMem();//单位bit\n        Long availMen = mem * 1024 - applyMen / 1024 / 1024;////单位M\n        Integer availCpu = memStatInfo.getCpu() - memStatInfo.getInstanceNum();\n        if (availMen >= Math.ceil(reqSize) * (isSalve + 1) && availCpu >= reqCpu * (isSalve + 1)) {\n            machineCandi.add(memStatInfo);\n        }\n    }\n\n    @Override\n    public Map getMachineDeployStat(Set<String> ipList, List<DeployInfo> deployInfoList) {\n        Map<String, DeployInfoStat> machineDeployStatMap = new HashMap<String, DeployInfoStat>();\n        if (!CollectionUtils.isEmpty(deployInfoList)) {\n            for (String ip : ipList) {\n                Integer masterNum = 0;\n                Integer slaveNum = 0;\n                Integer sentinelNum = 0;\n                Integer twemproxylNum = 0;\n                for (DeployInfo deployInfo : deployInfoList) {\n                    masterNum += (deployInfo.getMasterIp() != null && deployInfo.getMasterIp().equals(ip)) || (deployInfo.getMasterPikaIp() != null && deployInfo.getMasterPikaIp().equals(ip)) ? 1 : 0;\n                    slaveNum += (deployInfo.getSlaveIp() != null && deployInfo.getSlaveIp().equals(ip)) || (deployInfo.getSlavePikaIp() != null && deployInfo.getSlavePikaIp().equals(ip)) ? 1 : 0;\n                    sentinelNum += deployInfo.getSentinelIp() != null && deployInfo.getSentinelIp().equals(ip) ? 1 : 0;\n                    twemproxylNum += deployInfo.getTwemproxyIp() != null && deployInfo.getTwemproxyIp().equals(ip) ? 1 : 0;\n                }\n                DeployInfoStat deployInfoStat = new DeployInfoStat(masterNum, slaveNum, sentinelNum, twemproxylNum);\n                machineDeployStatMap.put(ip, deployInfoStat);\n            }\n        }\n        return machineDeployStatMap;\n    }\n\n    @Override\n    public void getResMachines(List<MachineMemStatInfo> machineCandi, Integer machineNum,\n                               List<MachineMemStatInfo> resMachines) {\n        if (machineCandi == null) {\n            return;\n        }\n        Map map = new HashMap();\n\n        if (machineCandi.size() < machineNum) {\n            return;\n        } else {\n            while (map.size() < machineNum) {\n                int random = (int) (Math.random() * machineCandi.size());\n                if (!map.containsKey(random)) {\n                    map.put(random, \"\");\n                    resMachines.add(machineCandi.get(random));\n                }\n            }\n        }\n    }\n\n    /**\n     * @param machinelist 机器信息\n     * @param type        应用类型\n     * @param instanceNum 实例数量 实例数据为0时，则每个机器部署一个实例\n     * @param maxMemory   内存大小\n     * @param hasSalve    是否有从节点\n     * @return 添加部署Redis/Pika节点信息\n     */\n    public List<DeployInfo> generateInstanceInfo(List<String> machinelist, int type, int appType, int instanceNum, int maxMemory, int hasSalve, List<DeployInfo> deployInfoList) {\n        if (!CollectionUtils.isEmpty(machinelist)) {\n            for (int index = 0; index < instanceNum; index++) {\n                DeployInfo deployInfo = null;\n                if (DeployInfo.isRedisNode(type)) {\n                    deployInfo = hasSalve == 1\n                            ? DeployInfo.getRedisInfo(appType, machinelist.get(index % machinelist.size()), maxMemory, machinelist.get((index + 1) % machinelist.size()))\n                            : DeployInfo.getRedisInfo(appType, machinelist.get(index % machinelist.size()), maxMemory, null);\n                } else {\n                    deployInfo = hasSalve == 1\n                            ? DeployInfo.getPikaInfo(appType, machinelist.get(index % machinelist.size()), maxMemory, machinelist.get((index + 1) % machinelist.size()))\n                            : DeployInfo.getPikaInfo(appType, machinelist.get(index % machinelist.size()), maxMemory, null);\n                }\n                if (deployInfo != null) {\n                    deployInfoList.add(deployInfo);\n                }\n            }\n            //每台机器仅部署一个实例处理逻辑\n            if (instanceNum == 0) {\n                for (int index = 0; index < machinelist.size() - 1; ) {\n                    DeployInfo deployInfo = null;\n                    if (DeployInfo.isRedisNode(type)) {\n                        deployInfo = hasSalve == 1\n                                ? DeployInfo.getRedisInfo(appType, machinelist.get(index), maxMemory, machinelist.get(index + 1))\n                                : DeployInfo.getRedisInfo(appType, machinelist.get(index), maxMemory, null);\n                    } else {\n                        deployInfo  = hasSalve == 1\n                                ? DeployInfo.getPikaInfo(appType, machinelist.get(index), maxMemory, machinelist.get(index + 1))\n                                : DeployInfo.getPikaInfo(appType, machinelist.get(index), maxMemory, null);\n                    }\n                    if (deployInfo != null) {\n                        deployInfoList.add(deployInfo);\n                    }\n                    if(hasSalve == 1) {\n                        index += 2;\n                    } else {\n                        index += 1;\n                    }\n                }\n            }\n        }\n        return deployInfoList;\n    }\n\n    public List<DeployInfo> generateProxyinfo(List<String> proxylist, int type, int appType, int proxyNum, List<DeployInfo> deployInfoList) {\n        if (!CollectionUtils.isEmpty(proxylist)) {\n            for (int index = 0; index < proxyNum; index++) {\n                DeployInfo deployInfo = null;\n                if (DeployInfo.isSentinelNode(type)) {\n                    deployInfo = DeployInfo.getSentinelInfo(appType, proxylist.get(index % proxylist.size()));\n                } else {\n                    deployInfo = DeployInfo.getTwemproxyInfo(appType, proxylist.get(index % proxylist.size()));\n                }\n                if (deployInfo != null) {\n                    deployInfoList.add(deployInfo);\n                }\n            }\n        }\n        return deployInfoList;\n    }\n\n    @Override\n    public List<AppDesc> checkAppStatus(List<String> appIds) {\n        return appDao.getAppDescByIds(appIds);\n    }\n\n    @Override\n    public List<AppTopMemFragRatio> getTopMemFragRatioApps(TimeBetween timeBetween) {\n        long startTime = timeBetween.getStartTime();\n        long endTime = timeBetween.getEndTime();\n        return appStatsDao.getTopMemFragRatioApps(startTime, endTime);\n    }\n\n    @Override\n    public Map<Long, Map<String, Object>> getAppClientStatGather(long appId, String gatherTime) {\n        List<Map<String, Object>> appClientGatherStatList = appClientStatisticGatherDao.getAppClientStatisticByGatherTime(appId, gatherTime);\n        return appClientGatherStatList.stream().collect(Collectors.toMap(appClientGatherStat -> MapUtils.getLong(appClientGatherStat, \"app_id\"), appClientGatherStat -> appClientGatherStat));\n    }\n\n    @Override\n    public Map<String, List<Map<String, Object>>> getFilterAppClientStatGather(long appId, String gatherTime) {\n        List<Map<String, Object>> appClientGatherStatList = appClientStatisticGatherDao.getExpAppStatisticByGatherTime(gatherTime);\n        return groupAppClientStatGather(appClientGatherStatList);\n    }\n\n    private boolean appClientStatGatherFilter(Map<String, Object> appClientGatherStat) {\n        if (MapUtils.isEmpty(appClientGatherStat)) {\n            return false;\n        }\n        try {\n            long appId = MapUtils.getLongValue(appClientGatherStat, \"app_id\", 0l);\n            if (appService.getByAppId(appId).isTestOk()) {\n                return false;\n            }\n            return true;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return false;\n        }\n\n    }\n\n    private Map<String, List<Map<String, Object>>> groupAppClientStatGather(List<Map<String, Object>> appClientGatherStatList) {\n        Map<String, List<Map<String, Object>>> result = new HashMap<>();\n        if (CollectionUtils.isEmpty(appClientGatherStatList)) {\n            return result;\n        }\n\n        List<Map<String, Object>> expAppStats = new ArrayList<>();\n        List<Map<String, Object>> latencyAppStats = new ArrayList<>();\n        List<Map<String, Object>> memAlterAppStats = new ArrayList<>();\n        List<Map<String, Object>> fragRatioAppStats = new ArrayList<>();\n        List<Map<String, Object>> topologyAppStats = new ArrayList<>();\n\n\n        try {\n            appClientGatherStatList.forEach(appClientGatherMap -> {\n                long appId = MapUtils.getLongValue(appClientGatherMap, \"app_id\", 0l);\n                AppDesc appDesc = appService.getByAppId(appId);\n                if (appDesc != null && !appDesc.isTestOk()) {\n                    //增加应用是否下线判断，如应用下线，此条客户端统计信息不做处理\n                    if(AppStatusEnum.STATUS_PUBLISHED.getStatus() != appDesc.getStatus()){\n                        return;\n                    }\n                    AppDetailVO appDetail = appStatsCenter.getAppDetail(appId);\n                    long mem = appDetail.getMem();\n\n                    long exp_count = MapUtils.getLongValue(appClientGatherMap, \"exp_count\", 0l);\n                    long slow_log_count = MapUtils.getLongValue(appClientGatherMap, \"slow_log_count\", 0l);\n                    //double mem_used_ratio = MapUtils.getDoubleValue(appClientGatherMap, \"mem_used_ratio\", 0.00);\n                    double avg_mem_frag_ratio = MapUtils.getDoubleValue(appClientGatherMap, \"avg_mem_frag_ratio\", 0.00);\n\n                    double format_mem = mem / 1024.0;\n                    double format_used_memory = MapUtils.getLongValue(appClientGatherMap, \"used_memory\", 0l) / 1024 / 1024 / 1024.0;\n                    double format_used_memory_rss = MapUtils.getLongValue(appClientGatherMap, \"used_memory_rss\", 0l) / 1024 / 1024 / 1024.0;\n                    double mem_used_ratio = format_mem == 0 ? 0.0 : (format_used_memory / format_mem) * 100;\n\n                    int topology_exam_result = MapUtils.getIntValue(appClientGatherMap, \"topology_exam_result\", -1);\n                    if (exp_count > 0) {\n                        expAppStats.add(appClientGatherMap);\n                    }\n                    if (slow_log_count > 100) {\n                        latencyAppStats.add(appClientGatherMap);\n                    }\n                    if (mem > 1024 * 10 && mem_used_ratio <= 40.0) {\n                        appClientGatherMap.put(\"format_mem\", String.format(\"%.2f\", format_mem));\n                        appClientGatherMap.put(\"format_used_memory\", String.format(\"%.2f\", format_used_memory));\n                        appClientGatherMap.put(\"mem_used_ratio\", Double.valueOf(String.format(\"%.2f\", mem_used_ratio)));\n                        memAlterAppStats.add(appClientGatherMap);\n                    }\n                    if (format_used_memory * 1024 > 500 && avg_mem_frag_ratio > 1.2) {\n                        appClientGatherMap.put(\"format_used_memory\", String.format(\"%.2f\", format_used_memory));\n                        appClientGatherMap.put(\"format_used_memory_rss\", String.format(\"%.2f\", format_used_memory_rss));\n                        fragRatioAppStats.add(appClientGatherMap);\n                    }\n                    if (topology_exam_result == 1) {\n                        topologyAppStats.add(appClientGatherMap);\n                    }\n                }\n            });\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        try {\n            Collections.sort(latencyAppStats, (o1, o2) -> {\n                long val1 = (long) o1.get(\"slow_log_count\");\n                long val2 = (long) o2.get(\"slow_log_count\");\n                return val1 > val2 ? -1 : 1; // 从大到小\n            });\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        try {\n            Collections.sort(memAlterAppStats, (o1, o2) -> {\n                double val1 = (double) o1.get(\"mem_used_ratio\");\n                double val2 = (double) o2.get(\"mem_used_ratio\");\n                return val1 > val2 ? 1 : -1; // 从小到大\n            });\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        try {\n            Collections.sort(fragRatioAppStats, (o1, o2) -> {\n                double val1 = (double) o1.get(\"avg_mem_frag_ratio\");\n                double val2 = (double) o2.get(\"avg_mem_frag_ratio\");\n                return val1 > val2 ? -1 : 1; // 从大到小\n            });\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n\n        result.put(\"expAppStats\", expAppStats);\n        result.put(\"latencyAppStats\", latencyAppStats.subList(0, latencyAppStats.size() < 10 ? latencyAppStats.size() : 10));\n        result.put(\"memAlterAppStats\", memAlterAppStats.subList(0, memAlterAppStats.size() < 10 ? memAlterAppStats.size() : 10));\n        result.put(\"fragRatioAppStats\", fragRatioAppStats.subList(0, fragRatioAppStats.size() < 10 ? fragRatioAppStats.size() : 10));\n        result.put(\"topologyAppStats\", topologyAppStats);\n\n        return result;\n    }\n\n    @Override\n    public AppAudit saveAppKeyAnalysis(AppDesc appDesc, AppUser appUser, String appAnalysisReason, String nodeInfo) {\n        AppAudit appAudit = new AppAudit();\n        appAudit.setAppId(appDesc.getAppId());\n        appAudit.setUserId(appUser.getId());\n        appAudit.setUserName(appUser.getName());\n        // nodeInfo 待分析节点地址\n        if (StringUtils.hasText(nodeInfo)) {\n            appAudit.setParam1(nodeInfo);\n        }\n        appAudit.setModifyTime(new Date());\n        appAudit.setInfo(\"申请原因: \" + appAnalysisReason);\n        appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n        appAudit.setType(AppAuditType.KEY_ANALYSIS.getValue());\n        Date now = new Date();\n        appAudit.setCreateTime(now);\n        appAudit.setModifyTime(now);\n        appAuditDao.insertAppAudit(appAudit);\n\n        // 保存日志\n        AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n                AppAuditLogTypeEnum.KEY_VALUE_ANALYSIS);\n        if (appAuditLog != null) {\n            appAuditLogDao.save(appAuditLog);\n        }\n\n        return appAudit;\n    }\n\n    @Override\n    public AppAudit saveAppDiagnostic(AppDesc appDesc, AppUser appUser, String reason) {\n        AppAudit appAudit = new AppAudit();\n        appAudit.setAppId(appDesc.getAppId());\n        appAudit.setUserId(appUser.getId());\n        appAudit.setUserName(appUser.getName());\n        appAudit.setModifyTime(new Date());\n        appAudit.setInfo(\"申请原因: \" + reason);\n        appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value());\n        appAudit.setType(AppAuditType.APP_DIAGNOSTIC.getValue());\n        Date now = new Date();\n        appAudit.setCreateTime(now);\n        appAudit.setModifyTime(now);\n        appAuditDao.insertAppAudit(appAudit);\n\n        // 保存日志\n        AppAuditLog appAuditLog = AppAuditLog.generate(appDesc, appUser, appAudit.getId(),\n                AppAuditLogTypeEnum.APP_DIAGNOSTIC_APPLY);\n        if (appAuditLog != null) {\n            appAuditLogDao.save(appAuditLog);\n        }\n\n        return appAudit;\n    }\n\n    private void addRealMeachine(MachineInfo machineInfo, Set<String> realMachines) {\n        if (machineInfo.getVirtual() == 0) {\n            realMachines.add(machineInfo.getIp());\n        } else {\n            realMachines.add(machineInfo.getRealIp());\n        }\n    }\n\n    private boolean isSameRealMachine(String ip1, String ip2) {\n        if (ip1.equals(ip2))\n            return true;\n        MachineInfo machine1 = machineDao.getMachineInfoByIp(ip1);\n        MachineInfo machine2 = machineDao.getMachineInfoByIp(ip2);\n        if (machine1.getVirtual() == 1 && machine2.getVirtual() == 1) {\n            return machine1.getExtraDesc().equals(machine2.getRealIp());\n        } else {\n            return machine1.getIp().equals(machine2.getIp());\n        }\n    }\n\n    @Override\n    public List<InstanceInfo> getAppInstanceByType(long appId, InstanceTypeEnum instanceTypeEnum) {\n        List<InstanceInfo> resultList = new ArrayList<InstanceInfo>();\n\n        List<InstanceInfo> appInstanceInfoList = getAppInstanceInfo(appId);\n        for (InstanceInfo instanceInfo : appInstanceInfoList) {\n            if (instanceTypeEnum.getType() == instanceInfo.getType()) {\n                resultList.add(instanceInfo);\n            }\n        }\n\n        return resultList;\n    }\n\n    @Override\n    public String checkAppPersistenceConfigAndFix(long appId, Map<String, String> masterConfigMap, Map<String, String> slaveConfigMap) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        List<InstanceInfo> instanceInfoList = this.getInstanceInfoListWithRole(appId);\n        Map<Integer, List<InstanceInfo>> groupMap = this.groupInstanceByMaster(instanceInfoList);\n        boolean masterSlavePair = this.checkIsMasterSlavePair(groupMap);\n        if(!masterSlavePair){\n            if(appScrollRestartService.isAppOnScrollRestart(appId)){\n                logger.error(\"checkAppPersistenceConfigAndFix, the app is on scroll restart, checkIsMasterSlavePair failed and will ignore this time, appId:{}\", appId);\n                return null;\n            }\n            logger.error(\"checkAppPersistenceConfigAndFix checkIsMasterSlavePair failed, appId:{}\", appId);\n            return \"checkIsMasterSlavePair failed\";\n        }\n        StringBuilder sb = new StringBuilder();\n        groupMap.forEach((masterId, instanceInfoS) -> {\n            List<InstanceInfo> oneGroupList = new ArrayList<>();\n            //check\n            instanceInfoS.forEach(instanceInfo -> {\n                if(instanceInfo.getRoleDesc().equals(InstanceRoleEnum.MASTER.getInfo())){\n                    masterConfigMap.forEach((configKey, expectConfigValue) -> {\n                        String configValue = redisCenter.configGet(appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey);\n                        if(StringUtils.isEmpty(expectConfigValue) && StringUtils.isEmpty(configValue) || expectConfigValue.equals(configValue)){\n                            return;\n                        }else{\n                            oneGroupList.add(instanceInfo);\n                        }\n                    });\n                }else if(instanceInfo.getRoleDesc().equals(InstanceRoleEnum.SLAVE.getInfo())){\n                    slaveConfigMap.forEach((configKey, expectConfigValue) -> {\n                        String configValue = redisCenter.configGet(appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey);\n                        if (StringUtils.isEmpty(expectConfigValue) && StringUtils.isEmpty(configValue) || expectConfigValue.equals(configValue)) {\n                            return;\n                        } else {\n                            oneGroupList.add(0, instanceInfo);\n                        }\n                    });\n                }\n            });\n\n            oneGroupList.forEach(instanceInfo -> {\n                if(instanceInfo.getRoleDesc().equals(InstanceRoleEnum.MASTER.getInfo())){\n                    masterConfigMap.forEach((configKey, expectConfigValue) -> {\n                        String configValue = redisCenter.configGet(appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey);\n                        if(StringUtils.isEmpty(expectConfigValue) && StringUtils.isEmpty(configValue) || expectConfigValue.equals(configValue)){\n                            return;\n                        }else{\n                            logger.info(\"checkAppPersistenceConfigAndFix configSetAndRewrite success, appId:{} host:{} port:{} key:{} value:{}\", appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue);\n                            boolean configSetAndRewrite = redisCenter.configSetAndRewrite(appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue);\n                            if(!configSetAndRewrite){\n                                logger.error(\"checkAppPersistenceConfigAndFix configSetAndRewrite failed, appId:{} host:{} port:{} key:{} value:{}\", appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue);\n                                sb.append(String.format(\"configSetAndRewrite master failed, appId:%s host:%s port:%s key:%s value:%s\", appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue));\n                            }\n                        }\n                    });\n                }else if(instanceInfo.getRoleDesc().equals(InstanceRoleEnum.SLAVE.getInfo())){\n                    slaveConfigMap.forEach((configKey, expectConfigValue) -> {\n                        String configValue = redisCenter.configGet(appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey);\n                        if (StringUtils.isEmpty(expectConfigValue) && StringUtils.isEmpty(configValue) || expectConfigValue.equals(configValue)) {\n                            return;\n                        } else {\n                            logger.info(\"checkAppPersistenceConfigAndFix configSetAndRewrite success, appId:{} host:{} port:{} key:{} value:{}\", appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue);\n                            boolean configSetAndRewrite = redisCenter.configSetAndRewrite(appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue);\n                            if(!configSetAndRewrite){\n                                logger.error(\"checkAppPersistenceConfigAndFix configSetAndRewrite failed, appId:{} host:{} port:{} key:{} value:{}\", appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue);\n                                sb.append(String.format(\"configSetAndRewrite slave failed, appId:%s host:%s port:%s key:%s value:%s\", appId, instanceInfo.getIp(), instanceInfo.getPort(), configKey, expectConfigValue));\n                            }\n                        }\n                    });\n                }\n            });\n        });\n        if(sb.length() > 0){\n            return sb.toString();\n        }\n        return null;\n    }\n\n    public List<InstanceInfo> getInstanceInfoListWithRole(Long appId) {\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        return this.getInstanceInfoListWithRole(appId, appDesc);\n    }\n\n    public List<InstanceInfo> getInstanceInfoListWithRole(Long appId, AppDesc appDesc) {\n        List<InstanceInfo> resultList = instanceDao.getInstListByAppId(appId);\n        String password = appDesc.getAppPassword();\n        resultList.forEach(instanceInfo -> {\n            int type = instanceInfo.getType();\n            if (instanceInfo.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) {\n                return;\n            }\n            if (TypeUtil.isRedisType(type)) {\n                if (TypeUtil.isRedisSentinel(type)) {\n                    return;\n                }\n                String host = instanceInfo.getIp();\n                int port = instanceInfo.getPort();\n                // 幂等操作\n                BooleanEnum isMaster = redisCenter.isMaster(appId, host, port);\n                instanceInfo.setRoleDesc(isMaster);\n                if (BooleanEnum.FALSE == isMaster) {\n                    HostAndPort hap = redisCenter.getMaster(host, port, password);\n                    if (hap != null) {\n                        instanceInfo.setMasterHost(hap.getHost());\n                        instanceInfo.setMasterPort(hap.getPort());\n                        for (InstanceInfo innerInfo : resultList) {\n                            if (innerInfo.getIp().equals(hap.getHost())\n                                    && innerInfo.getPort() == hap.getPort()) {\n                                instanceInfo.setMasterInstanceId(innerInfo.getId());\n                                break;\n                            }\n                        }\n                    }\n                }\n            }\n        });\n        return resultList;\n    }\n\n    public Map<Integer, List<InstanceInfo>> groupInstanceByMaster(List<InstanceInfo> instanceInfoWithRoleList) {\n        Map<Integer, List<InstanceInfo>> resultMap = new HashMap<Integer, List<InstanceInfo>>();\n        instanceInfoWithRoleList.forEach(info -> {\n            String roleDesc = info.getRoleDesc();\n            if (roleDesc != null && roleDesc.equals(\"master\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, info.getId(), new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(info.getId(), list);\n            } else if (roleDesc != null && roleDesc.equals(\"slave\")) {\n                List<InstanceInfo> list = (ArrayList<InstanceInfo>) MapUtils.getObject(resultMap, info.getMasterInstanceId(), new ArrayList<InstanceInfo>());\n                list.add(info);\n                resultMap.put(info.getMasterInstanceId(), list);\n            }\n        });\n        return resultMap;\n    }\n\n    /**\n     * 校验相关的实例是否为主从备份\n     * @param groupMap 已根据主从进行分组\n     * @return false:不满足主从，true满足\n     */\n    private boolean checkIsMasterSlavePair(Map<Integer, List<InstanceInfo>> groupMap){\n        //校验是否均为主从备份\n        Set<Map.Entry<Integer, List<InstanceInfo>>> entries = groupMap.entrySet();\n        for (Map.Entry<Integer, List<InstanceInfo>> entry : entries){\n            List<InstanceInfo> instanceInfos = entry.getValue();\n            if(instanceInfos.size() < 2){\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public boolean updateAppPersistenceType(long appId, int persistenceType) {\n        int execNum = appDao.updateAppPersistenceType(appId, persistenceType);\n        if(execNum > 0){\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean updateAppMaxmemoryPolicy(long appId, int maxmemoryPolicy) {\n        int execNum = appDao.updateAppMaxmemoryPolicy(appId, maxmemoryPolicy);\n        if(execNum > 0){\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 获取用户所属应用的内存统计信息（包含版本）\n     * @param userId\n     * @param isAdmin\n     * @return\n     */\n    @Override\n    public List<AppCapacityStatisticsResult> getAppCapacityStatistics(Long userId, Boolean isAdmin){\n        AppStatisticsSearch search = AppStatisticsSearch.builder().userId(userId).isAdmin(isAdmin).build();\n        return appDao.getCapacityStatistics(search);\n    }\n\n    @Override\n    public Map<String, Object> getAppCapacityStats(Long userId, Boolean isAdmin){\n        Map<String, Object> resultMap = new HashMap<>();\n        List<AppCapacityStatisticsResult> capacityStatistics = this.getAppCapacityStatistics(userId, isAdmin);\n        Map<Integer, Integer> versionMap = new HashMap<>();\n        long totalApplyMem = 0L;\n        long totalUsedMem = 0L;\n        long totalMasterCount = 0L;\n        for (AppCapacityStatisticsResult result : capacityStatistics) {\n            totalUsedMem += result.getMemUsed() == null ? 0 :result.getMemUsed();\n            totalApplyMem += result.getCurMem() == null ? 0 :result.getCurMem();\n            totalMasterCount += result.getShardingMasterNum() == null ? 0 :result.getShardingMasterNum();\n            Integer versionId = result.getVersionId();\n            if(versionMap.containsKey(versionId)){\n                versionMap.put(versionId, (versionMap.get(versionId) + 1));\n            }else{\n                versionMap.put(versionId, 1);\n            }\n        }\n        Map<Integer, SystemResource> resourceMap = ConstUtils.REDIS_RESOURCE;\n        List<Pair<String, Integer>> versionList = new ArrayList<>();\n        versionMap.keySet().forEach(versionId -> {\n            SystemResource systemResource = resourceMap.get(versionId);\n            if(systemResource != null){\n                versionList.add(new Pair<>(systemResource.getName(), versionMap.get(versionId)));\n            }\n        });\n\n        resultMap.put(\"appCount\", capacityStatistics.size());\n        resultMap.put(\"applyMem\", totalApplyMem);\n        resultMap.put(\"usedMem\", totalUsedMem);\n        resultMap.put(\"notUsedMem\", totalApplyMem - totalUsedMem);\n        resultMap.put(\"masterCount\", totalMasterCount);\n        resultMap.put(\"versionList\", versionList);\n\n        List<Pair<Long, Double>> capacityAuditList = new ArrayList<>();\n        capacityStatistics.sort(Comparator.comparingDouble(AppCapacityStatisticsResult :: getMemUsedRatio));\n        if(capacityStatistics != null){\n            int limit = capacityStatistics.size() > 5 ? 5 : capacityStatistics.size();\n            for(int i = 0; i < limit; i++){\n                AppCapacityStatisticsResult statisticsResult = capacityStatistics.get(i);\n                capacityAuditList.add(new Pair<>(statisticsResult.getAppId(), statisticsResult.getMemUsedRatio()));\n            }\n        }\n        resultMap.put(\"auditList\", capacityAuditList);\n        return resultMap;\n    }\n\n    /**\n     * 获取用户所属应用的监控统计信息\n     * @param userId\n     * @param isAdmin\n     * @param startTime\n     * @param endTime\n     * @return\n     */\n    @Override\n    public List<AppMonitorStatisticsResult> getAppMonitorStatistics(Long userId, Boolean isAdmin, String startTime, String endTime){\n        AppStatisticsSearch search = AppStatisticsSearch.builder().userId(userId).isAdmin(isAdmin).startTime(startTime).endTime(endTime).build();\n        return appDao.getMonitorStatistics(search);\n    }\n\n    /**\n     * 更新应用拓扑检测结果\n     * @param topologyExam\n     * @return\n     */\n    @Override\n    public int saveAppTopologyExam(AppClientStatisticGather topologyExam){\n        return appClientStatisticGatherDao.batchSaveTopologyExam(Lists.newArrayList(topologyExam));\n    }\n\n    /**\n     * 获取拓扑检测失败的应用\n     * @param gatherTime\n     * @return\n     */\n    @Override\n    public List<AppClientStatisticGather> getTopologyExamFailedByGatherTime(String gatherTime){\n        return appClientStatisticGatherDao.getTopologyExamFailedByGatherTime(gatherTime);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/ConfigServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport com.sohu.cache.alert.utils.AlertUtils;\nimport com.sohu.cache.util.StringUtil;\nimport org.apache.commons.collections.MapUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.sohu.cache.dao.ConfigDao;\nimport com.sohu.cache.entity.SystemConfig;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.ConfigService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.PostConstruct;\n\n/**\n * @author leifu\n * @Date 2016年5月23日\n * @Time 上午10:35:26\n */\n@Service(\"configService\")\npublic class ConfigServiceImpl implements ConfigService {\n\n    private Logger logger = LoggerFactory.getLogger(ConfigServiceImpl.class);\n    @Autowired\n    private ConfigDao configDao;\n\n\n    @PostConstruct\n    public void init() {\n        reloadSystemConfig();\n    }\n\n    /**\n     * 加载配置\n     */\n    public void reloadSystemConfig() {\n        logger.info(\"ConfigServiceImpl reload config start\");\n        // 加载配置\n        Map<String, String> configMap = getConfigMap();\n\n        // 文案相关\n        ConstUtils.CONTACT = MapUtils.getString(configMap, \"cachecloud.contact\");\n        logger.debug(\"{}: {}\", \"ConstUtils.CONTACT\", ConstUtils.CONTACT);\n\n        // 报警相关配置\n        AlertUtils.EMAILS = MapUtils.getString(configMap, \"cachecloud.owner.email\");\n        logger.debug(\"{}: {}\", \"ConstUtils.EMAILS\", AlertUtils.EMAILS);\n\n        AlertUtils.PHONES = MapUtils.getString(configMap, \"cachecloud.owner.phone\");\n        logger.debug(\"{}: {}\", \"ConstUtils.PHONES\", AlertUtils.PHONES);\n\n        AlertUtils.WECHAT = MapUtils.getString(configMap, \"cachecloud.owner.weChat\");\n        logger.debug(\"{}: {}\", \"ConstUtils.WECHAT\", AlertUtils.WECHAT);\n\n        // ssh相关配置\n        ConstUtils.USERNAME = MapUtils.getString(configMap, \"cachecloud.machine.ssh.name\", ConstUtils.DEFAULT_USERNAME);\n        logger.debug(\"{}: {}\", \"ConstUtils.USERNAME\", ConstUtils.USERNAME);\n\n        ConstUtils.PASSWORD = MapUtils.getString(configMap, \"cachecloud.machine.ssh.password\",\n                ConstUtils.DEFAULT_PASSWORD);\n        logger.debug(\"{}: {}\", \"ConstUtils.PASSWORD\", ConstUtils.PASSWORD);\n\n        ConstUtils.SSH_PORT_DEFAULT = Integer.parseInt(MapUtils.getString(configMap, \"cachecloud.machine.ssh.port\",\n                String.valueOf(ConstUtils.DEFAULT_SSH_PORT_DEFAULT)));\n        logger.debug(\"{}: {}\", \"ConstUtils.SSH_PORT_DEFAULT\", ConstUtils.SSH_PORT_DEFAULT);\n\n        //ssh授权方式\n        ConstUtils.SSH_AUTH_TYPE = MapUtils.getIntValue(configMap, \"cachecloud.ssh.auth.type\", ConstUtils.DEFAULT_SSH_AUTH_TYPE);\n        logger.debug(\"{}: {}\", \"ConstUtils.SSH_AUTH\", ConstUtils.SSH_AUTH_TYPE);\n\n        //public key pem\n        ConstUtils.PUBLIC_KEY_PEM = MapUtils.getString(configMap, \"cachecloud.public.key.pem\", ConstUtils.DEFAULT_PUBLIC_KEY_PEM);\n        logger.debug(\"{}: {}\", \"ConstUtils.PUBLIC_KEY_PEM\", ConstUtils.PUBLIC_KEY_PEM);\n\n        ConstUtils.PUBLIC_KEY_PEM = MapUtils.getString(configMap, \"cachecloud.public.key.pem\", ConstUtils.DEFAULT_PUBLIC_KEY_PEM);\n        logger.debug(\"{}: {}\", \"ConstUtils.PUBLIC_KEY_PEM\", ConstUtils.PUBLIC_KEY_PEM);\n\n        ConstUtils.PUBLIC_USERNAME = MapUtils.getString(configMap, \"cachecloud.public.user.name\", ConstUtils.DEFAULT_PUBLIC_USERNAME);\n        logger.debug(\"{}: {}\", \"ConstUtils.PUBLIC_USERNAME\", ConstUtils.PUBLIC_USERNAME);\n\n        // 管理员相关配置\n        ConstUtils.SUPER_ADMIN_NAME = MapUtils.getString(configMap, \"cachecloud.admin.user.name\",\n                ConstUtils.DEFAULT_SUPER_ADMIN_NAME);\n        logger.debug(\"{}: {}\", \"ConstUtils.SUPER_ADMIN_NAME\", ConstUtils.SUPER_ADMIN_NAME);\n\n        ConstUtils.SUPER_ADMIN_PASS = MapUtils.getString(configMap, \"cachecloud.admin.user.password\",\n                ConstUtils.DEFAULT_SUPER_ADMIN_PASS);\n        logger.debug(\"{}: {}\", \"ConstUtils.SUPER_ADMIN_PASS\", ConstUtils.SUPER_ADMIN_PASS);\n\n        ConstUtils.SUPER_ADMINS = MapUtils.getString(configMap, \"cachecloud.superAdmin\",\n                ConstUtils.DEFAULT_SUPER_ADMINS);\n        logger.debug(\"{}: {}\", \"ConstUtils.SUPER_ADMINS\", ConstUtils.SUPER_ADMINS);\n\n        ConstUtils.SUPER_MANAGER = Arrays.asList(ConstUtils.SUPER_ADMINS.split(\",\"));\n        logger.debug(\"{}: {}\", \"ConstUtils.SUPER_MANAGER\", ConstUtils.SUPER_MANAGER);\n\n        String leaderEmailListStr = MapUtils.getString(configMap, \"cachecloud.leader.email\");\n        if (!StringUtil.isBlank(leaderEmailListStr)) {\n            ConstUtils.LEADER_EMAIL_LIST = Arrays.asList(leaderEmailListStr.split(\",\"));\n        }\n\n        // 机器报警阀值\n        ConstUtils.MEMORY_USAGE_RATIO_THRESHOLD = MapUtils.getDoubleValue(configMap, \"machine.mem.alert.ratio\",\n                ConstUtils.DEFAULT_MEMORY_USAGE_RATIO_THRESHOLD);\n        logger.debug(\"{}: {}\", \"ConstUtils.MEMORY_USAGE_RATIO_THRESHOLD\", ConstUtils.MEMORY_USAGE_RATIO_THRESHOLD);\n\n        //cachecloud根目录\n        ConstUtils.CACHECLOUD_BASE_DIR = MapUtils.getString(configMap, \"cachecloud.base.dir\", ConstUtils.DEFAULT_CACHECLOUD_BASE_DIR);\n        logger.debug(\"{}: {}\", \"ConstUtils.CACHECLOUD_BASE_DIR\", ConstUtils.CACHECLOUD_BASE_DIR);\n\n        //应用客户端连接报警阀值\n        ConstUtils.APP_CLIENT_CONN_THRESHOLD = MapUtils.getIntValue(configMap, \"cachecloud.app.client.conn.threshold\", ConstUtils.DEFAULT_APP_CLIENT_CONN_THRESHOLD);\n        logger.debug(\"{}: {}\", \"ConstUtils.APP_CLIENT_CONN_THRESHOLD\", ConstUtils.APP_CLIENT_CONN_THRESHOLD);\n\n        //邮件报警接口\n        AlertUtils.EMAIL_ALERT_INTERFACE = MapUtils.getString(configMap, \"cachecloud.email.alert.interface\");\n        logger.debug(\"{}: {}\", \"ConstUtils.EMAIL_ALERT_INTERFACE\", AlertUtils.EMAIL_ALERT_INTERFACE);\n\n        //短信报警接口\n        AlertUtils.MOBILE_ALERT_INTERFACE = MapUtils.getString(configMap, \"cachecloud.mobile.alert.interface\");\n        logger.debug(\"{}: {}\", \"ConstUtils.MOBILE_ALERT_INTERFACE\", AlertUtils.MOBILE_ALERT_INTERFACE);\n\n        //微信报警接口\n        AlertUtils.WECHAT_ALERT_INTERFACE = MapUtils.getString(configMap, \"cachecloud.weChat.alert.interface\");\n        logger.debug(\"{}: {}\", \"ConstUtils.MOBILE_ALERT_INTERFACE\", AlertUtils.MOBILE_ALERT_INTERFACE);\n\n        //是否定期清理各种统计数据(详见CleanUpStatisticsJob)\n        ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA = MapUtils.getBooleanValue(configMap, \"cachecloud.whether.schedule.clean.data\", ConstUtils.DEFAULT_WHETHER_SCHEDULE_CLEAN_DATA);\n        logger.debug(\"{}: {}\", \"ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA\", ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA);\n\n        // 机器性能统计周期(分钟)\n        ConstUtils.MACHINE_STATS_CRON_MINUTE = MapUtils.getIntValue(configMap, \"cachecloud.machine.stats.cron.minute\", ConstUtils.DEFAULT_MACHINE_STATS_CRON_MINUTE);\n        logger.debug(\"{}: {}\", \"ConstUtils.MACHINE_STATS_CRON_MINUTE\", ConstUtils.MACHINE_STATS_CRON_MINUTE);\n\n        logger.info(\"ConfigServiceImpl reload config end\");\n    }\n\n    @Override\n    public SuccessEnum updateConfig(Map<String, String> configMap) {\n        for (Entry<String, String> entry : configMap.entrySet()) {\n            String configKey = entry.getKey();\n            String configValue = entry.getValue();\n            try {\n                configDao.update(configKey, configValue);\n            } catch (Exception e) {\n                logger.error(\"key {} value {} update faily\" + e.getMessage(), configKey, configValue, e);\n                return SuccessEnum.FAIL;\n            }\n        }\n        return SuccessEnum.SUCCESS;\n    }\n\n    @Override\n    public List<SystemConfig> getConfigList(int status) {\n        try {\n            return configDao.getConfigList(status);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    /**\n     * 获取所有配置的key-value\n     *\n     * @return\n     */\n    private Map<String, String> getConfigMap() {\n        Map<String, String> configMap = new LinkedHashMap<String, String>();\n        List<SystemConfig> systemConfigList = getConfigList(1);\n        for (SystemConfig systemConfig : systemConfigList) {\n            configMap.put(systemConfig.getConfigKey(), systemConfig.getConfigValue());\n        }\n        return configMap;\n    }\n\n    public void setConfigDao(ConfigDao configDao) {\n        this.configDao = configDao;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/DiagnosticToolServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.constant.DiagnosticTypeEnum;\nimport com.sohu.cache.dao.DiagnosticTaskRecordDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.DiagnosticTaskRecord;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.AssistRedisService;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.DiagnosticToolService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.ScanParams;\nimport redis.clients.jedis.ScanResult;\n\nimport java.nio.charset.Charset;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * @Author: rucao\n * @Date: 2020/6/5 5:33 下午\n */\n@Service\n@Slf4j\npublic class DiagnosticToolServiceImpl implements DiagnosticToolService {\n    @Autowired\n    private AssistRedisService assistRedisService;\n    @Autowired\n    private DiagnosticTaskRecordDao diagnosticTaskRecordDao;\n    @Autowired\n    private AppService appService;\n    @Autowired\n    private RedisCenter redisCenter;\n\n    @Override\n    public Map<Long, List<InstanceInfo>> getAppInstancesMap(List<AppDesc> appDescList) {\n\n        Map<Long, List<InstanceInfo>> appInstancesMap = appDescList.parallelStream().collect(Collectors.toMap(\n                appDesc -> appDesc.getAppId(),\n                appDesc -> appService.getAppOnlineInstanceInfo(appDesc.getAppId())));\n        return appInstancesMap;\n    }\n\n    @Override\n    public List<DiagnosticTaskRecord> getDiagnosticTaskRecords(Long appId, Long parentTaskId, Long auditId, Integer type, Integer status) {\n        List<DiagnosticTaskRecord> diagnosticTaskRecords = new ArrayList<>();\n        try {\n            diagnosticTaskRecords = diagnosticTaskRecordDao.getDiagnosticTaskRecords(appId, parentTaskId, auditId, type, status);\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        }\n        return diagnosticTaskRecords;\n    }\n\n    @Override\n    public Map<String, String> getDiagnosticDataMap(String redisKey, int type, boolean err) {\n        if (type == DiagnosticTypeEnum.SLOT_ANALYSIS.getType() && err == true) {\n            Map<String, String> tmp = assistRedisService.hgetAll(redisKey);\n            return tmp.keySet().stream()\n                    .filter(key -> tmp.get(key).contains(\"error\"))\n                    .collect(Collectors.toMap(key -> key, key -> tmp.get(key)));\n        }\n        return assistRedisService.hgetAll(redisKey);\n    }\n\n    @Override\n    public String getHotkeyDiagnosticData(String redisKey) {\n        return assistRedisService.get(redisKey);\n    }\n\n    @Override\n    public List<String> getScanDiagnosticData(String redisKey) {\n        return assistRedisService.lrange(redisKey, 0, -1);\n    }\n\n    @Override\n    public List<String> getScanCleanDiagnosticData(String redisKey) {\n        return assistRedisService.lrange(redisKey, 0, -1);\n    }\n\n    @Override\n    public List<String> getSampleScanData(Long appId, String nodes, String pattern) {\n        List<String> sampleScanData = new ArrayList<>();\n        String host = \"\";\n        int port = 0;\n        if (StringUtil.isBlank(nodes)) {\n            List<InstanceInfo> instanceInfoList = appService.getAppOnlineInstanceInfo(appId);\n            for (InstanceInfo instanceInfo : instanceInfoList) {\n                if (\"master\".equals(instanceInfo.getRoleDesc())) {\n                    host = instanceInfo.getIp();\n                    port = instanceInfo.getPort();\n                    break;\n                }\n            }\n        } else {\n            String[] nodeArray = nodes.split(\",\");\n            String hostPort = nodeArray.length > 0 ? nodeArray[0] : \"\";\n            if (!StringUtil.isBlank(hostPort)) {\n                host = hostPort.split(\":\").length > 0 ? hostPort.split(\":\")[0] : \"\";\n                port = hostPort.split(\":\").length > 1 ? Integer.parseInt(hostPort.split(\":\")[1]) : 0;\n            }\n        }\n\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = null;\n        try {\n            jedis = redisCenter.getAdminJedis(appId, host, port);\n\n            long dbSize = jedis.dbSize();\n            if (dbSize == 0) {\n                log.info(\"{} {}:{} dbsize is {}\", appId, host, port, dbSize);\n                return sampleScanData;\n            }\n            log.info(\"{} {}:{} total key is {} \", appId, host, port, dbSize);\n            // scan参数\n            byte[] cursor = \"0\".getBytes(Charset.forName(\"UTF-8\"));\n            ScanParams scanParams = StringUtil.isBlank(pattern) ?\n                    new ScanParams().count(50) :\n                    new ScanParams().match(pattern).count(50);\n\n            while (true) {\n                try {\n                    ScanResult<byte[]> scanResult = jedis.scan(cursor, scanParams);\n                    cursor = scanResult.getCursorAsBytes();\n                    List<byte[]> keyList = scanResult.getResult();\n                    if (CollectionUtils.isNotEmpty(keyList)) {\n                        sampleScanData.addAll(keyList.stream().map(byteKey -> new String(byteKey)).collect(Collectors.toList()));\n                    }\n                } catch (Exception e) {\n                    log.error(e.getMessage(), e);\n                } finally {\n                    //防止无限循环\n                    if (sampleScanData.size() >= 50 || Arrays.equals(\"0\".getBytes(Charset.forName(\"UTF-8\")), cursor)) {\n                        break;\n                    }\n                }\n            }\n            //结果存redis\n            long cost = System.currentTimeMillis() - startTime;\n            log.info(\"{} {}:{} scan key successfully, cost time is {} ms, total key is {}\", appId, host, port, cost, sampleScanData.size());\n            return sampleScanData;\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            log.error(\"redis-cli -h {} -p {} admin auth error\", host, port);\n            log.error(\"scan key appId {} {}:{}  error:\" + e.getMessage(), appId, host, port, e);\n            return sampleScanData;\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/InstancePortServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.dao.MachineRoomDao;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.task.BaseTask;\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.task.entity.NutCrackerNode;\nimport com.sohu.cache.task.entity.RedisSentinelNode;\nimport com.sohu.cache.task.entity.RedisServerNode;\nimport com.sohu.cache.task.util.AppWechatUtil;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.service.InstancePortService;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Service;\n\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * @author fulei\n * @date 2018年7月4日\n * @time 下午3:29:10\n */\n@Service\npublic class InstancePortServiceImpl implements InstancePortService {\n\n    private Logger logger = LoggerFactory.getLogger(InstancePortServiceImpl.class);\n\n\t/*@Value(\"${appEnvName}\")\n\tprivate String appEnvName;*/\n\n    @Autowired\n    private AppWechatUtil appWechatUtil;\n\n    /**\n     * 机房dao\n     */\n    @Autowired\n    private MachineRoomDao machineRoomDao;\n\n    /**\n     * 实例dao\n     */\n    @Autowired\n    private InstanceDao instanceDao;\n\n    /**\n     * redis-port实例dao\n     */\n//\tprivate RedisPortInstanceDao redisPortInstanceDao;\n\n    /**\n     * redis-migrate-tool实例dao\n     */\n//\tprivate RedisMigrateToolInstanceDao redisMigrateToolInstanceDao;\n\n\t/**\n\t * redis相关\n\t */\n\t@Autowired\n    @Lazy\n\tprivate RedisCenter redisCenter;\n\n\n    /**\n     * 1. 主从不同机器\n     * 2. 端口从起始端口开始自增1\n     */\n    @Override\n    public List<RedisServerNode> generateRedisServerNodeList(long appId, List<String> redisServerMachineList,\n                                                             int masterPerMachine, int maxMemory) {\n\n        // 最终结果\n        List<RedisServerNode> redisServerNodeList = new ArrayList<RedisServerNode>();\n        // 起始节点\n        int masterPort = (int) (ConstUtils.REDIS_SERVER_BASE_PORT + (appId % 10) * 10);\n\n        // 记录本次每个机器分配的port\n        Map<String, Set<Integer>> ipPortSetMap = new HashMap<String, Set<Integer>>();\n\n        for (int i = 0; i < redisServerMachineList.size(); i++) {\n\n            String masterHost = redisServerMachineList.get(i);\n            String slaveHost = redisServerMachineList.get((i + 1) % redisServerMachineList.size());\n\n            for (int j = 0; j < masterPerMachine; j++) {\n                // master node\n                //如果端口存在就自增\n                while (checkHostPortExist(ipPortSetMap, masterHost, masterPort)) {\n                    masterPort++;\n                }\n                redisServerNodeList.add(new RedisServerNode(masterHost, masterPort, InstanceRoleEnum.MASTER.getRole(),\n                        maxMemory, \"\", 0));\n\n                // slave node\n                int slavePort = masterPort + ConstUtils.SLAVE_PORT_INCREASE;\n                //如果端口存在就自增\n                while (checkHostPortExist(ipPortSetMap, slaveHost, slavePort)) {\n                    slavePort++;\n                }\n                redisServerNodeList.add(new RedisServerNode(slaveHost, slavePort, InstanceRoleEnum.SLAVE.getRole(),\n                        maxMemory, masterHost, masterPort));\n\n                masterPort += 1;\n            }\n        }\n\n        return redisServerNodeList;\n    }\n\n    /**\n     * 1. 主从不同机器\n     * 2. 端口从起始端口开始自增1\n     */\n    @Override\n    public List<RedisServerNode> generateRedisServerNodeListWithDeployInfo(long appId, List<String> appDeployInfoList,\n                                                                           int masterPerMachine, int maxMemory) {\n\n        // 最终结果\n        List<RedisServerNode> redisServerNodeList = new ArrayList<RedisServerNode>();\n        // 起始节点\n        int masterPort = (int) (ConstUtils.REDIS_SERVER_BASE_PORT + (appId % 10) * 10);\n\n        // 记录本次每个机器分配的port\n        Map<String, Set<Integer>> ipPortSetMap = new HashMap<String, Set<Integer>>();\n        for (int i=0; i < appDeployInfoList.size(); i++) {\n            String masterAndSlaveHost = appDeployInfoList.get(i);\n            String[] hostInfoArray = masterAndSlaveHost.split(\":\");\n            String masterHost = hostInfoArray[0];\n            maxMemory = Integer.parseInt(hostInfoArray[1]);\n            while (checkHostPortExist(ipPortSetMap, masterHost, masterPort)) {\n                masterPort++;\n            }\n            redisServerNodeList.add(new RedisServerNode(masterHost, masterPort, InstanceRoleEnum.MASTER.getRole(),\n                    maxMemory, \"\", 0));\n\n            if (hostInfoArray.length >= 3) {\n                String slaveHost = hostInfoArray[2];\n                int slavePort = masterPort + ConstUtils.SLAVE_PORT_INCREASE;\n                while (checkHostPortExist(ipPortSetMap, slaveHost, slavePort)) {\n                    slavePort++;\n                }\n                redisServerNodeList.add(new RedisServerNode(slaveHost, slavePort, InstanceRoleEnum.SLAVE.getRole(),\n                        maxMemory, masterHost, masterPort));\n            }\n            masterPort += 1;\n        }\n        return redisServerNodeList;\n    }\n\n    @Override\n    public List<RedisSentinelNode> generateRedisSentinelNodeList(long appId, List<String> redisSentinelMachineList,\n                                                                 int sentinelPerMachine) {\n        // 最终结果\n        List<RedisSentinelNode> redisSentinelNodeList = new ArrayList<RedisSentinelNode>();\n        // 起始节点,appid 10000起步 @TODO\n        int port = (int) (ConstUtils.REDIS_SENTINEL_BASE_PORT + (appId % 100) * 10);\n        for (String ip : redisSentinelMachineList) {\n            for (int j = 0; j < sentinelPerMachine; j++) {\n                //如果端口存在就自增\n                while (checkHostPortExist(ip, port)) {\n                    port++;\n                }\n                redisSentinelNodeList.add(new RedisSentinelNode(ip, port));\n                port++;\n            }\n        }\n        return redisSentinelNodeList;\n    }\n\n\t/*@Override\n    public List<PikaNode> generatePikaNodeList(long appId, List<String> pikaMachineList, int masterPerMachine) {\n\t\t// 最终结果\n\t\tList<PikaNode> pikaNodeList = new ArrayList<PikaNode>();\n\t\t// 起始节点\n\t\tint masterPort = (int) (ConstUtils.PIKA_BASE_PORT + (appId % 10) * 10);\n\t\t// 记录本次每个机器分配的port\n\t\tMap<String, Set<Integer>> ipPortSetMap = new HashMap<String, Set<Integer>>();\n\t\t// 如果是一对，那只只要主从\n\t\tint machineLength = pikaMachineList.size() == 2 ? 1 : pikaMachineList.size();\n\n\t\tfor (int i = 0; i < machineLength; i++) {\n\n\t\t\tString masterHost = pikaMachineList.get(i);\n\t\t\tString slaveHost = pikaMachineList.get((i + 1) % pikaMachineList.size());\n\n\t\t\tfor (int j = 0; j < masterPerMachine; j++) {\n\t\t\t\t// master node\n\t\t\t\twhile (checkHostPortExist(ipPortSetMap, masterHost, masterPort)) {\n\t\t\t\t\tmasterPort++;\n\t\t\t\t}\n\t\t\t\tpikaNodeList.add(new PikaNode(masterHost, masterPort, InstanceRoleEnum.MASTER.getRole(), \"\", 0));\n\n\t\t\t\t// slave node\n\t\t\t\tint slavePort = masterPort + ConstUtils.PIKA_SLAVE_PORT_INCREASE;\n\t\t\t\twhile (checkHostPortExist(ipPortSetMap, slaveHost, slavePort)) {\n\t\t\t\t\tslavePort++;\n\t\t\t\t}\n\t\t\t\tpikaNodeList.add(new PikaNode(slaveHost, slavePort, InstanceRoleEnum.SLAVE.getRole(), masterHost, masterPort));\n\n\t\t\t\tmasterPort += 1;\n\t\t\t}\n\t\t}\n\n\t\treturn pikaNodeList;\n\t}*/\n\n    /**\n     * 每台端口从baseport开始自增1\n     */\n    @Override\n    public List<NutCrackerNode> generateNutCrackerNodeList(long appId, List<String> nutCrackerMachineList,\n                                                           int nutCrackerPerMachine) {\n//\t\t// 最终结果\n//\t\tList<NutCrackerNode> nutCrackerNodeList = new ArrayList<NutCrackerNode>();\n//\t\t// 起始节点\n//\t\tint port =(int) (ConstUtils.NUT_CRACKER_BASE_PORT + (appId % 10) * 10);\n//\t\tfor (String ip : nutCrackerMachineList) {\n//\t\t\tint initPort = port;\n//\t\t\tfor (int j = 0; j < nutCrackerPerMachine; j++) {\n//\t\t\t\t//如果端口存在就自增\n//\t\t\t\twhile (checkNutCrackerHostPortExist(ip, initPort)) {\n//\t\t\t\t\tinitPort++;\n//\t\t\t\t}\n//\t\t\t\tnutCrackerNodeList.add(new NutCrackerNode(ip, initPort));\n//\t\t\t\tinitPort++;\n//\t\t\t}\n//\t\t}\n//\t\treturn nutCrackerNodeList;\n        // @todo fulei\n        return null;\n    }\n\t\n\t/*@Override\n\tpublic List<CodisProxyNode> generateCodisProxyList(long appId, List<String> codisProxyMachineList,\n\t\t\tint codisProxyPerMachine) {\n\t\t// 最终结果\n\t\tList<CodisProxyNode> codisProxyNodeList = new ArrayList<CodisProxyNode>();\n\t\t// 起始节点\n\t\tint port;\n\t\tif (appEnvName.equals(AppEnvNameEnum.afun.getName())) {\n\t\t\tport = ConstUtils.CODIS_PROXY_BASE_PORT;\n\t\t} else {\n\t\t\tport = (int) (ConstUtils.CODIS_PROXY_BASE_PORT + (appId % 10) * 10);\n\t\t}\n\t\tfor (String ip : codisProxyMachineList) {\n\t\t\tint initPort = port;\n\t\t\tfor (int j = 0; j < codisProxyPerMachine; j++) {\n\t\t\t\t//如果端口存在就自增\n\t\t\t\twhile (checkCodisProxyHostPortExist(ip, initPort)) {\n\t\t\t\t\tinitPort++;\n\t\t\t\t}\n\t\t\t\tcodisProxyNodeList.add(new CodisProxyNode(ip, initPort));\n\t\t\t\tinitPort++;\n\t\t\t}\n\t\t}\n\t\treturn codisProxyNodeList;\n\t}*/\n\n\t/*@Override\n\tpublic List<CodisDashboardNode> generateCodisDashboardList(long appId, List<String> codisDashboardMachineList) {\n\t\t// 最终结果\n\t\tList<CodisDashboardNode> codisDashboardNodeList = new ArrayList<CodisDashboardNode>();\n\t\tint port = ConstUtils.CODIS_DASHBOARD_BASE_PORT;\n\t\tfor (String ip : codisDashboardMachineList) {\n\t\t\t// 如果端口存在就自增\n\t\t\twhile (checkDashboardHostPortExist(ip, port)) {\n\t\t\t\tport++;\n\t\t\t}\n\t\t\tcodisDashboardNodeList.add(new CodisDashboardNode(ip, port));\n\t\t\tport++;\n\t\t}\n\t\treturn codisDashboardNodeList;\n\t}*/\n\n    @Override\n    public boolean checkHostPortExist(String ip, int port) {\n        InstanceInfo instanceInfo = instanceDao.getAllInstByIpAndPort(ip, port);\n        if (instanceInfo != null) {\n            return true;\n        }\n        //只检测一次\n        boolean isRedisRun = redisCenter.isRun(ip, port, 1);\n        if (isRedisRun) {\n            appWechatUtil.noticeWildInstance(ip, port);\n            logger.warn(BaseTask.marker, \"{}:{} process is not in instance_info table\", ip, port);\n            return true;\n        }\n        return false;\n    }\n\n    private boolean checkHostPortExist(Map<String, Set<Integer>> ipPortSetMap, String host, int port) {\n        Set<Integer> portSet = ipPortSetMap.get(host);\n        if (CollectionUtils.isNotEmpty(portSet) && portSet.contains(port)) {\n            return true;\n        } else {\n            if (portSet == null) {\n                Set<Integer> set = new HashSet<Integer>();\n                set.add(port);\n                ipPortSetMap.put(host, set);\n            } else {\n                ipPortSetMap.get(host).add(port);\n            }\n            return checkHostPortExist(host, port);\n        }\n    }\n\n    /**\n     * 暂时和nut cracker一样\n     *\n     * @return\n     */\n\t/*private boolean checkCodisProxyHostPortExist(String ip, int port) {\n\t\tList<InstanceInfo> instanceInfoList = instanceDao.getInstanceByIpAndStatPort(ip, port);\n\t\tif (CollectionUtils.isNotEmpty(instanceInfoList)) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn checkHostPortExist(ip, port);\n\t\t}\n\t}*/\n    private boolean checkNutCrackerHostPortExist(String ip, int port) {\n//\t\tList<InstanceInfo> instanceInfoList = instanceDao.getInstanceByIpAndStatPort(ip, port);\n//\t\tif (CollectionUtils.isNotEmpty(instanceInfoList)) {\n//\t\t\treturn true;\n//\t\t} else {\n//\t\t\treturn checkHostPortExist(ip, port);\n//\t\t}\n        // @todo fulei\n        return true;\n    }\n\n    public boolean checkDashboardHostPortExist(String ip, int port) {\n        InstanceInfo instanceInfo = instanceDao.getAllInstByIpAndPort(ip, port);\n        if (instanceInfo != null) {\n            return true;\n        }\n        return false;\n    }\n\n    private boolean checkRedisMigrateToolPortExist(String ip, int port) {\n        // @todo fulei\n        return true;\n//\t\tRedisMigrateToolInstance redisMigrateToolInstance = redisMigrateToolInstanceDao.getByHostAndPort(ip, port);\n//\t\tif (redisMigrateToolInstance != null) {\n//\t\t\treturn true;\n//\t\t}\n//\t\t//只检测一次\n//\t\tboolean isRedisRun = redisCenter.isRun(ip, port, 1);\n//\t\tif (isRedisRun) {\n//\t\t\tappWechatUtil.noticeWildInstance(ip, port);\n//\t\t\tlogger.warn(BaseTask.marker, \"{}:{} process is not in instance_info table\", ip, port);\n//\t\t\treturn true;\n//\t\t}\n//\t\treturn false;\n    }\n\t\n\t\n\t/*@Override\n\tpublic List<RedisPortNode> generateRedisPortNodeList(long sourceAppId, long targetAppId,\n\t\t\tList<InstanceInfo> slaveInstanceInfoList, List<InstanceInfo> proxyInstanceInfoList) {\n\t\t\n\t\tList<RedisPortNode> redisPortNodeList = new ArrayList<RedisPortNode>();\n\t\t\n\t\tfor (int i = 0; i < slaveInstanceInfoList.size(); i++) {\n\t\t\t//proxy索引\n\t\t\tint index = i % proxyInstanceInfoList.size();\n\t\t\t\n\t\t\tInstanceInfo proxyInstanceInfo = proxyInstanceInfoList.get(index);\n\t\t\tInstanceInfo slaveInstanceInfo  = slaveInstanceInfoList.get(i);\n\t\t\t\n\t\t\tString redisPortHost = slaveInstanceInfo.getIp();\n\t\t\tint redisPortPort = slaveInstanceInfoList.get(i).getPort() + ConstUtils.REDIS_PORT_PORT_INCREASE;\n\t\t\tString sourceHost = slaveInstanceInfo.getIp();\n\t\t\tint sourcePort = slaveInstanceInfo.getPort();\n\t\t\tString targetHost = proxyInstanceInfo.getIp();\n\t\t\tint targetPort = proxyInstanceInfo.getPort();\n\t\t\n\t\t\twhile (checkRedisPortExist(redisPortHost, redisPortPort, sourceHost, sourcePort, targetHost, targetPort)) {\n\t\t\t\tredisPortPort++;\n\t\t\t}\n\t\t\t\n\t\t\tRedisPortNode redisPortNode = new RedisPortNode();\n\t\t\tredisPortNode.setIp(redisPortHost);\n\t\t\tredisPortNode.setPort(redisPortPort);\n\t\t\tredisPortNode.setSourceInstanceId(slaveInstanceInfo.getId());\n\t\t\tredisPortNode.setSourceIp(sourceHost);\n\t\t\tredisPortNode.setSourcePort(sourcePort);\n\t\t\tredisPortNode.setTargetInstanceId(proxyInstanceInfo.getId());\n\t\t\tredisPortNode.setTargetIp(targetHost);\n\t\t\tredisPortNode.setTargetPort(targetPort);\n\t\t\t\n\t\t\tredisPortNodeList.add(redisPortNode);\n\t\t}\n\t\t\n\t\treturn redisPortNodeList;\n\t}*/\n\n    @Override\n    public RedisServerNode generateRedisServerNode(long appId, String host, int maxMemory, InstanceRoleEnum instanceRoleEnum) {\n        synchronized (host.intern()) {\n            try {\n                //防止端口重复 @TODO也可以用本地缓存做端口限制\n                TimeUnit.SECONDS.sleep(3);\n                int port = (int) (ConstUtils.REDIS_SERVER_BASE_PORT + (appId % 10) * 10);\n                if (InstanceRoleEnum.SLAVE.equals(instanceRoleEnum)) {\n                    port += ConstUtils.SLAVE_PORT_INCREASE;\n                }\n                while (checkHostPortExist(host, port)) {\n                    logger.info(BaseTask.marker, \"appId {} host {} port is {}\", appId, host, port);\n                    port++;\n                }\n                logger.info(BaseTask.marker, \"final appId {} host {} port is {}\", appId, host, port);\n                return new RedisServerNode(host, port, maxMemory);\n            } catch (Exception e) {\n                logger.error(BaseTask.marker, e.getMessage(), e);\n                return null;\n            }\n        }\n    }\n\t\n\t/*@Override\n\tpublic PikaNode generatePikaNode(long appId, String host, int maxMemory, InstanceRoleEnum instanceRoleEnum) {\n\t\tsynchronized (host.intern()) {\n\t\t\ttry {\n\t\t\t\t//防止端口重复 @TODO也可以用本地缓存做端口限制\n\t\t\t\tTimeUnit.SECONDS.sleep(3);\n\t\t\t\tint port = (int) (ConstUtils.PIKA_BASE_PORT + (appId % 10) * 10);\n\t\t\t\tif (InstanceRoleEnum.SLAVE.equals(instanceRoleEnum)) {\n\t\t\t\t\tport += ConstUtils.PIKA_SLAVE_PORT_INCREASE;\n\t\t\t\t}\n\t\t\t\twhile (checkHostPortExist(host, port)) {\n\t\t\t\t\tlogger.info(BaseTask.marker, \"appId {} host {} port is {}\", appId, host, port);\n\t\t\t\t\tport++;\n\t\t\t\t}\n\t\t\t\tlogger.info(BaseTask.marker, \"final appId {} host {} port is {}\", appId, host, port);\n\t\t\t\treturn new PikaNode(host, port);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlogger.error(BaseTask.marker, e.getMessage(), e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}*/\n\t\n\t\n\t/*@Override\n\tpublic List<RedisMigrateToolNode> generateRedisMigrateToolNodeList(int rmtCount, String machineLogicName) {\n\t\t//最终结果\n\t\tList<RedisMigrateToolNode> redisMigrateToolNodeList = new ArrayList<RedisMigrateToolNode>();\n\t\t\n\t\t//所有rmt机器\n\t\tList<MachineInfo> redisMigrateToolMachineList = machineInfoDao.getMachineInfoByKsp(KspNodeEnum.MIGRATE_TOOL.getType());\n\t\tif (appEnvName.equals(AppEnvNameEnum.afun.getName())) {\n\t\t\tredisMigrateToolMachineList = machineInfoDao.getMachineInfoByKsp(\"afun-redis-migrate-tool\");\n\t\t\tif (CollectionUtils.isNotEmpty(redisMigrateToolMachineList)) {\n\t\t\t\tString host = redisMigrateToolMachineList.get(0).getIp();\n\t\t\t\tint port = ConstUtils.REDIS_MIGRATE_TOOL_PORT;\n\t\t\t\t\n\t\t\t\t//检查表和心跳\n\t\t\t\tif (!checkRedisMigrateToolPortExist(host, port)) {\n\t\t\t\t\tredisMigrateToolNodeList.add(new RedisMigrateToolNode(host, port));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tredisMigrateToolMachineList = machineInfoDao.getMachineInfoByKsp(KspNodeEnum.MIGRATE_TOOL.getType());\n\t\t\tfor (MachineInfo redisMigrateToolMachine : redisMigrateToolMachineList) {\n\t\t\t\tMachineRoom machineRoom = machineRoomDao.getByName(redisMigrateToolMachine.getMachineRoomName());\n\t\t\t\tif (!machineLogicName.equals(machineRoom.getLogicName())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (redisMigrateToolNodeList.size() >= rmtCount) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tString host = redisMigrateToolMachine.getIp();\n\t\t\t\tint port = ConstUtils.REDIS_MIGRATE_TOOL_PORT;\n\t\t\t\t\n\t\t\t\t//检查表和心跳\n\t\t\t\tif (!checkRedisMigrateToolPortExist(host, port)) {\n\t\t\t\t\tredisMigrateToolNodeList.add(new RedisMigrateToolNode(host, port));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn redisMigrateToolNodeList;\n\t}*/\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/MigrateServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.async.AsyncService;\nimport com.sohu.cache.async.KeyCallable;\nimport com.sohu.cache.dao.InstanceDao;\nimport com.sohu.cache.dao.InstanceStatsDao;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.machine.MachineCenter;\nimport com.sohu.cache.redis.RedisCenter;\nimport com.sohu.cache.redis.RedisDeployCenter;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.stats.instance.InstanceDeployCenter;\nimport com.sohu.cache.task.constant.InstanceRoleEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.MigrateService;\nimport com.sohu.cache.web.vo.RedisClusterNode;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport redis.clients.jedis.HostAndPort;\n\nimport java.util.*;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2023/2/28 15:33\n * @Description: 迁移服务\n * @Version: 1.0\n */\n@Slf4j\n@Service(\"migrateService\")\npublic class MigrateServiceImpl implements MigrateService {\n\n    @Autowired\n    private RedisCenter redisCenter;\n\n    @Autowired\n    protected AppService appService;\n\n    @Autowired\n    private SSHService sshService;\n\n    @Autowired\n    private MachineCenter machineCenter;\n\n    @Autowired\n    private AsyncService asyncService;\n\n    @Autowired\n    private RedisDeployCenter redisDeployCenter;\n\n    @Autowired\n    private InstanceDao instanceDao;\n\n    @Autowired\n    private InstanceStatsDao instanceStatsDao;\n\n    @Autowired\n    private InstanceDeployCenter instanceDeployCenter;\n\n    @Override\n    public void forceMigrate(String sourceIp, String targetIp) throws SSHException {\n\n        /**\n         *  1. 检查目标容器的连通性\n         *  2. 获取需要迁移的实例信息\n         *  3. 遍历实例:(只对cluster实例迁移)\n         *      3.1 standalone/sentinel节点跳过\n         *      3.2 如果实例是master：添加新的从节点；获取master节点slave0；执行failover ；下线老节点\n         *      3.3 如果是slave节点：添加新从节点；下线老的从节点\n         *  4. 输出报告：下线节点数 ，迁移节点数\n         */\n        log.info(\"container forceMigrate start sourceIp:{} targetIp:{}\", sourceIp, targetIp);\n        //1.检查目标容器的连通性\n        String execute = sshService.execute(targetIp, \"echo ok\");\n\n        //2.获取机器所有实例\n        List<InstanceInfo> instanceList = machineCenter.getMachineInstanceInfo(sourceIp);\n        log.info(\"container forceMigrate instances:{} list size:{}\", instanceList, instanceList.size());\n        List<Object> resultList = null;\n        // 4.遍历实例，按照appId分组\n        if (!CollectionUtils.isEmpty(instanceList)) {\n            //按照appId进行分组\n            Map<Long, List<InstanceInfo>> appInstanceMap = new HashMap<>();\n            instanceList.forEach(instanceInfo -> {\n                if (instanceInfo.getType() != ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                    return;\n                }\n                if (appInstanceMap.containsKey(instanceInfo.getAppId())) {\n                    appInstanceMap.get(instanceInfo.getAppId()).add(instanceInfo);\n                } else {\n                    List<InstanceInfo> instanceInfoList = new ArrayList<>();\n                    instanceInfoList.add(instanceInfo);\n                    appInstanceMap.put(instanceInfo.getAppId(), instanceInfoList);\n                }\n            });\n\n            Set<Long> appSet = appInstanceMap.keySet();\n            String key = \"force-migrate-instance-\" + sourceIp + \"-\" + targetIp;\n            List<Future> doForceMigrateResult = appSet.stream().map(appId -> {\n                return asyncService.submitFutureWithRst(new KeyCallable<Boolean>(key + appId) {\n                    public Boolean execute() {\n                        try {\n                            //一键强制迁移\n                            return forceMigrateAppInstances(targetIp, appId, appInstanceMap.get(appId));\n                        } catch (Exception e) {\n                            log.error(\"doForceMigrateInstance \", e.getMessage(), e);\n                            return false;\n                        }\n                    }\n                });\n            }).collect(Collectors.toList());\n            if (CollectionUtils.isNotEmpty(doForceMigrateResult)) {\n                resultList = doForceMigrateResult.stream().map(future -> {\n                    try {\n                        return future.get();\n                    } catch (Exception e) {\n                        log.error(e.getMessage());\n                    }\n                    return false;\n                }).collect(Collectors.toList());\n            }\n        }\n        log.info(\"container forceMigrate end sourceIp:{} targetIp:{}, result:{}\", sourceIp, targetIp, resultList);\n    }\n\n    public boolean forceMigrateAppInstances(String targetIp, Long appId, List<InstanceInfo> instanceList) throws SSHException {\n        log.info(\"container forceMigrate AppInstances start appId:{} targetIp:{}, instance:{}\", appId, targetIp, instanceList);\n        List<InstanceInfo> failInstanceList = new ArrayList<>();\n        instanceList = instanceList.stream()\n                .filter(instanceInfo -> instanceInfo.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER)\n                .collect(Collectors.toList());\n        //先执行能正确获取角色的节点，如为执行成功，则添加到非正常节点中\n        for (InstanceInfo instanceInfo : instanceList) {\n            String role = redisCenter.getInstanceRole(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n            log.info(\"instanceInfo:{} {} role:{} start migrate\", instanceInfo.getIp(), instanceInfo.getPort(),role);\n            if (instanceInfo.isOnline()) {\n                // a)当前为master节点\n                if (InstanceRoleEnum.MASTER.getInfo().equals(role)) {\n                    if (!handleMasterMigrate(instanceInfo, targetIp)) {\n                        failInstanceList.add(instanceInfo);\n                    }\n                }\n                // b).当前为slave节点\n                if (InstanceRoleEnum.SLAVE.getInfo().equals(role)) {\n                    if (!handleSlaveMigrate(instanceInfo, targetIp)) {\n                        failInstanceList.add(instanceInfo);\n                    }\n                }\n            } else {\n                failInstanceList.add(instanceInfo);\n            }\n        }\n        log.info(\"container forceMigrate AppInstances appId:{} targetIp:{}, need to retry instances:{}\", appId, targetIp, failInstanceList);\n        //强制迁移非正常节点\n        int retryTimes = 3;\n        while (failInstanceList.size() > 0 && retryTimes > 0) {\n            //获取拓扑信息\n            Map<String, RedisClusterNode> appClusterNode = this.getAppClusterNode(appId);\n            //每个实例传入拓扑信息进行处理\n            for (int i = 0; i < failInstanceList.size(); ) {\n                InstanceInfo instanceInfo = failInstanceList.get(i);\n                RedisClusterNode clusterNode = appClusterNode.get(instanceInfo.getHostPort());\n                if (clusterNode != null) {\n                    boolean handlerResult = false;\n                    if (\"master\".equals(clusterNode.getRole())) {\n                        try {\n                            Optional<RedisClusterNode> slaveNodeOptional = appClusterNode.values().stream()\n                                    .filter(node -> clusterNode.getHostPort().equals(node.getMasterHostPort()) && node.isConnected() && !node.isFail())\n                                    .findFirst();\n                            if (slaveNodeOptional.isPresent()) {\n                                String hostPort = slaveNodeOptional.get().getHostPort();\n                                String[] hostPortArray = hostPort.split(\":\");\n                                InstanceInfo slaveInstance = instanceDao.getInstByIpAndPort(hostPortArray[0], Integer.valueOf(hostPortArray[1]));\n                                handlerResult = handleUnknownInstanceMigrate(instanceInfo, slaveInstance, true, targetIp, true);\n                            }\n                        } catch (Exception e) {\n                            log.error(\"container forceMigrate AppInstances appId:{} targetIp:{}, forceMigrate master fail instance:{}, error: {}\", appId, targetIp, instanceInfo, e.getMessage());\n                            e.printStackTrace();\n                        }\n                    } else {\n                        try {\n                            Optional<RedisClusterNode> masterNodeOptional = appClusterNode.values().stream()\n                                    .filter(node -> clusterNode.getMasterHostPort().equals(node.getHostPort()) && node.isConnected() && !node.isFail())\n                                    .findFirst();\n                            if (masterNodeOptional.isPresent()) {\n                                String hostPort = masterNodeOptional.get().getHostPort();\n                                Set<RedisClusterNode> slaves = appClusterNode.values().stream()\n                                        .filter(node -> hostPort.equals(node.getMasterHostPort())\n                                                && !node.getHostPort().equals(clusterNode.getHostPort()))\n                                        .collect(Collectors.toSet());\n                                boolean addSlave = true;\n                                if (CollectionUtils.isNotEmpty(slaves)) {\n                                    addSlave = false;\n                                }\n                                String[] hostPortArray = hostPort.split(\":\");\n                                InstanceInfo masterInstance = instanceDao.getInstByIpAndPort(hostPortArray[0], Integer.valueOf(hostPortArray[1]));\n                                handlerResult = handleUnknownInstanceMigrate(masterInstance, instanceInfo, false, targetIp, addSlave);\n                            }\n                        } catch (Exception e) {\n                            log.error(\"container forceMigrate AppInstances appId:{} targetIp:{}, forceMigrate slave fail instance:{}, error: {}\", appId, targetIp, instanceInfo, e.getMessage());\n                            e.printStackTrace();\n                        }\n                    }\n                    if (handlerResult) {\n                        failInstanceList.remove(i);\n                    } else {\n                        i++;\n                    }\n                }\n            }\n            log.warn(\"container forceMigrate AppInstances appId:{} targetIp:{}, after one more retry, fail instance left:{}\", appId, targetIp, failInstanceList);\n            retryTimes--;\n        }\n        if (CollectionUtils.isNotEmpty(failInstanceList)) {\n            log.warn(\"container forceMigrate AppInstances appId:{} targetIp:{}, forceMigrate fail instance:{}\", appId, targetIp, failInstanceList);\n            return false;\n        }\n        return true;\n    }\n\n    private boolean handleMasterMigrate(InstanceInfo instanceInfo, String targetIp) {\n        try {\n            //1)获取master节点slave0\n            AppDesc appdesc = appService.getByAppId(instanceInfo.getAppId());\n//            HostAndPort slave0 = redisCenter.getSlave0(instanceInfo.getIp(), instanceInfo.getPort(), appdesc.getAppPassword());\n            HostAndPort slave0 = null;\n            if (instanceInfo.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                slave0 = redisCenter.getSlave0(instanceInfo.getIp(), instanceInfo.getPort(), appdesc.getAppPassword());\n            }\n            //2)执行failover force\n            if (slave0 == null) {\n                return false;\n            }\n            InstanceInfo masterInst = instanceDao.getInstByIpAndPort(slave0.getHost(), slave0.getPort());\n            boolean checkFailover = this.failoverAndCheck(masterInst);\n            if (!checkFailover) {\n                // 如果failover失败 ，则不下线源节点，继续轮训下个节点\n                return false;\n            }\n            TimeUnit.SECONDS.sleep(5);\n            //3) 下线节点\n            boolean isOffline = instanceDeployCenter.shutdownExistInstance(instanceInfo.getAppId(), instanceInfo.getId());\n            log.info(\"forceMigrate handleMasterMigrate appid:{} offline node master:{} to {}, result :{}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), targetIp, isOffline);\n            //4) 添加新的从节点\n            Boolean isSuccess = false;\n            if (instanceInfo.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                isSuccess = redisDeployCenter.addSlave(instanceInfo.getAppId(), masterInst.getId(), targetIp);\n            }\n//            Boolean isSuccess = redisDeployCenter.addSlave(instanceInfo.getAppId(), masterInst.getId(), targetIp);\n            log.info(\"forceMigrate handleMasterMigrate appid:{} offline node master:{} result:{}， add new slave :{} result:{}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), isOffline, targetIp, isSuccess);\n            if (isSuccess) {\n                return true;\n            } else {\n                // 添加从节点 失败，则退出 检查原因\n                log.error(\"forceMigrate handleMasterMigrate add slave {}:{} fail\", instanceInfo.getAppId(), masterInst.getId());\n            }\n        } catch (Exception e) {\n            log.error(\"container forceMigrate handleMasterMigrate targetIp:{}, forceMigrate fail instance:{}, error: {}\", targetIp, instanceInfo, e.getMessage());\n        }\n        return false;\n    }\n\n    private boolean handleSlaveMigrate(InstanceInfo instanceInfo, String targetIp) {\n        AppDesc appdesc = appService.getByAppId(instanceInfo.getAppId());\n        try {\n            HostAndPort masterInfo = redisCenter.getMaster(instanceInfo.getIp(), instanceInfo.getPort(), appdesc.getAppPassword());\n            if (masterInfo == null) {\n                return false;\n            }\n            if (StringUtils.isNotEmpty(masterInfo.getHost()) && masterInfo.getPort() > 0) {\n                InstanceInfo masterInst = instanceDao.getInstByIpAndPort(masterInfo.getHost(), masterInfo.getPort());\n                if (masterInst == null) {\n                    return false;\n                }\n                //下线当前slave节点\n                boolean isOffline = instanceDeployCenter.shutdownExistInstance(instanceInfo.getAppId(), instanceInfo.getId());\n                log.info(\"forceMigrate handleSlaveMigrate appid:{} offline slave:{}, result :{}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), isOffline);\n                //添加新的slave节点\n                Boolean isSuccess = false;\n                if (instanceInfo.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                    isSuccess = redisDeployCenter.addSlave(instanceInfo.getAppId(), masterInst.getId(), targetIp);\n                }\n                //boolean isSuccess = redisDeployCenter.addSlave(instanceInfo.getAppId(), masterInst.getId(), targetIp);\n                // sleep 5s\n                TimeUnit.SECONDS.sleep(5);\n                log.info(\"forceMigrate handleSlaveMigrate appid:{} offline slave:{} result:{},add slave:{} result:{}\", instanceInfo.getAppId(), instanceInfo.getHostPort(), isOffline, targetIp, isSuccess);\n                if (isSuccess) {\n                    return true;\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"container forceMigrate handleSlaveMigrate targetIp:{}, forceMigrate fail instance:{}, error: {}\", targetIp, instanceInfo, e.getMessage());\n        }\n        return false;\n    }\n\n    private Map<String, RedisClusterNode> getAppClusterNode(long appId) {\n        List<InstanceInfo> appInstances = appService.getAppBasicInstanceInfo(appId);\n        Set<String> hostPortSet = appInstances.stream().filter(instanceInfo -> !instanceInfo.isOffline())\n                .map(instanceInfo -> instanceInfo.getHostPort()).collect(Collectors.toSet());\n        appInstances = appInstances.stream()\n                .filter(instanceInfo -> instanceInfo.isOnline()).collect(Collectors.toList());\n        Map<String, RedisClusterNode> clusterNode = new HashMap<>();\n        for (int i = 0; i < appInstances.size(); i++) {\n            clusterNode = this.getClusterNodeByInstance(hostPortSet, appInstances.get(i));\n            if (MapUtils.isNotEmpty(clusterNode)) {\n                break;\n            }\n        }\n        return clusterNode;\n    }\n\n    private Map<String, RedisClusterNode> getClusterNodeByInstance(Set<String> hostPortSet, InstanceInfo instanceInfo) {\n        try {\n            String clusterNodes = redisCenter.getClusterNodes(instanceInfo.getAppId(), instanceInfo.getIp(), instanceInfo.getPort());\n            if (StringUtils.isBlank(clusterNodes)) {\n                return null;\n            }\n            final Map<String, RedisClusterNode> nodeMap = new HashMap<>();\n            hostPortSet.forEach(hostPort -> nodeMap.put(hostPort, null));\n            String[] clusterNodeArray = clusterNodes.split(\"\\n\");\n            for (int i = 0; i < clusterNodeArray.length; i++) {\n                final String nodeInfo = clusterNodeArray[i];\n                Optional<String> optional = hostPortSet.stream().filter(hostPort -> nodeInfo.contains(hostPort)).findFirst();\n                if (!optional.isPresent()) {\n                    continue;\n                }\n                if (StringUtils.isNotBlank(nodeInfo)) {\n                    String[] nodeInfoPartArray = nodeInfo.split(\" \");\n                    RedisClusterNode clusterNode = new RedisClusterNode();\n                    clusterNode.setHostPort(optional.get());\n                    if (nodeInfo.contains(\"connected\")) {\n                        clusterNode.setConnected(true);\n                    }\n                    if (nodeInfo.contains(\"fail\")) {\n                        clusterNode.setFail(true);\n                    }\n                    if (nodeInfo.contains(\"master\")) {\n                        clusterNode.setRole(\"master\");\n                    }\n                    if (nodeInfo.contains(\"slave\")) {\n                        clusterNode.setRole(\"slave\");\n                        String masterId = nodeInfoPartArray[3];\n                        for (String clusterNodeStr : clusterNodeArray) {\n                            if (clusterNodeStr.contains(\"master\") && clusterNodeStr.contains(masterId)) {\n                                String[] masterNodeInfoPartArray = clusterNodeStr.split(\" \");\n                                String masterHostPort = masterNodeInfoPartArray[1];\n                                if (masterHostPort.contains(\"@\")) {\n                                    masterHostPort = masterHostPort.substring(0, masterHostPort.indexOf(\"@\"));\n                                }\n                                clusterNode.setMasterHostPort(masterHostPort);\n                            }\n                        }\n                    }\n                    nodeMap.put(optional.get(), clusterNode);\n                }\n            }\n            return nodeMap;\n        } catch (Exception e) {\n            log.error(\"forceMigrate getClusterNodeByInstance instance:{}, error:{}\", instanceInfo, e.getMessage());\n        }\n        return null;\n    }\n\n    private boolean handleUnknownInstanceMigrate(InstanceInfo masterInstance, InstanceInfo slaveInstance, boolean currentIsMaster, String targetIp, boolean addSlave) {\n        try {\n            if (currentIsMaster) {\n                //1) failover\n                boolean failoverRst = this.failoverAndCheck(slaveInstance);\n                if (!failoverRst) {\n                    return false;\n                }\n                TimeUnit.SECONDS.sleep(5);\n                //2) 下线节点\n                boolean isOffline = instanceDeployCenter.shutdownExistInstance(masterInstance.getAppId(), masterInstance.getId());\n                log.info(\"forceMigrate handleUnknownInstanceMigrate appid:{} offline node master:{} to {}, result :{}\", masterInstance.getAppId(), masterInstance.getHostPort(), targetIp, isOffline);\n                //3) 添加新的从节点\n                Boolean isSuccess = redisDeployCenter.addSlave(slaveInstance.getAppId(), slaveInstance.getId(), targetIp);\n                log.info(\"forceMigrate handleUnknownInstanceMigrate appid:{} offline node master:{} result:{}, add new slave:{} result:{}\", masterInstance.getAppId(), masterInstance.getHostPort(), isOffline, targetIp, isSuccess);\n                if (isSuccess) {\n                    return true;\n                } else {\n                    // 添加从节点 失败，则退出 检查原因\n                    log.error(\"forceMigrate handleUnknownInstanceMigrate add slave {}:{} fail\", slaveInstance.getAppId(), slaveInstance.getId());\n                }\n            } else {\n                //1) 下线节点\n                boolean isOffline = instanceDeployCenter.shutdownExistInstance(slaveInstance.getAppId(), slaveInstance.getId());\n                log.info(\"forceMigrate handleUnknownInstanceMigrate appid:{} offline node slave:{}, result :{}\", slaveInstance.getAppId(), slaveInstance.getHostPort(), isOffline);\n                //2) 添加新的从节点\n                Boolean isSuccess = false;\n                if (addSlave) {\n                    isSuccess = redisDeployCenter.addSlave(masterInstance.getAppId(), masterInstance.getId(), targetIp);\n                }\n                log.info(\"forceMigrate handleUnknownInstanceMigrate appid:{} offline node slave:{} result:{}， addSlave Flag:{}, add new slave:{} result:{}\",\n                        masterInstance.getAppId(), masterInstance.getHostPort(), isOffline, addSlave, targetIp, isSuccess);\n                if ((addSlave && isSuccess) || !addSlave) {\n                    return true;\n                } else {\n                    // 添加从节点失败，则退出 检查原因\n                    log.error(\"forceMigrate handleUnknownInstanceMigrate add slave {}:{} fail\", masterInstance.getAppId(), masterInstance.getId());\n                }\n            }\n        } catch (Exception e) {\n            log.error(\"forceMigrate handleUnknownInstanceMigrate appid:{} master node:{}， slave node:{}, currentIsMaster:{}, targetIp:{}, error:{}\",\n                    masterInstance.getAppId(), masterInstance.getHostPort(), slaveInstance.getHostPort(), currentIsMaster, targetIp, e.getMessage());\n        }\n        return false;\n    }\n\n    private boolean failoverAndCheck(InstanceInfo slaveInstance) {\n        try {\n\n            boolean isFailover = redisDeployCenter.clusterFailover(slaveInstance.getAppId(), slaveInstance.getId(), \"force\");\n            int times = 0;\n            boolean checkFailover = false;\n            while (!checkFailover && times++ <= 5) {\n                Boolean status = false;\n//                Boolean status = redisCenter.getRedisFailoverForceStatus(slaveInstance.getAppId(), slaveInstance.getIp(), slaveInstance.getPort());\n                if (slaveInstance.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {\n                    status = redisCenter.getRedisReplicationStatus(slaveInstance.getAppId(), slaveInstance.getIp(), slaveInstance.getPort());\n                }\n                if (status) {\n                    checkFailover = status;\n                } else {\n                    TimeUnit.SECONDS.sleep(6);\n                    log.info(\" check slave replication status ,waiting 6s ....\");\n                }\n            }\n            if (!checkFailover) {\n                // 如果failover失败 ，则不下线源节点，继续轮训下个节点\n                return false;\n            }\n            return true;\n        } catch (Exception e) {\n            log.error(\"forceMigrate failoverAndCheck appId:{}, instance:{}, error: {}\", slaveInstance.getAppId(), slaveInstance.getHostPort(), e.getMessage());\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/ResourceServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.dao.ResourceDao;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.entity.SystemResource;\nimport com.sohu.cache.exception.SSHException;\nimport com.sohu.cache.protocol.MachineProtocol;\nimport com.sohu.cache.ssh.SSHService;\nimport com.sohu.cache.ssh.SSHTemplate;\nimport com.sohu.cache.ssh.SSHUtil;\nimport com.sohu.cache.task.constant.PushEnum;\nimport com.sohu.cache.task.constant.ResourceEnum;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.ResourceService;\nimport com.sohu.cache.web.util.DateUtil;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.PostConstruct;\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n\n/**\n * Created by chenshi on 2020/7/6.\n */\n@Service(\"resourceService\")\npublic class ResourceServiceImpl implements ResourceService {\n\n    private Logger logger = LoggerFactory.getLogger(ResourceService.class);\n\n    @Autowired\n    ResourceDao resourceDao;\n    @Autowired\n    SSHService sshService;\n\n    @PostConstruct\n    public void init() {\n        List<SystemResource> resourceList = this.getResourceList(ResourceEnum.REDIS.getValue());\n        ConstUtils.REDIS_RESOURCE = resourceList.stream().collect(Collectors.toMap(res -> Integer.valueOf(res.getId()), Function.identity()));\n    }\n\n    public SuccessEnum saveResource(SystemResource systemResouce) {\n        try {\n            resourceDao.save(systemResouce);\n        } catch (Exception e) {\n            logger.error(\"key {} value {} update faily\" + e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n        return SuccessEnum.SUCCESS;\n    }\n\n    public SuccessEnum updateResource(SystemResource systemResouce) {\n        try {\n            resourceDao.update(systemResouce);\n        } catch (Exception e) {\n            logger.error(\"key {} value {} update faily\" + e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n        return SuccessEnum.REPEAT;\n    }\n\n    public List<SystemResource> getResourceList(int resourceType) {\n        try {\n            return resourceDao.getResourceList(resourceType);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    public List<SystemResource> getResourceList(int resourceType, String searchName) {\n        try {\n            return resourceDao.getResourceListByName(resourceType, searchName);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public SuccessEnum pushScript(Integer repositoryId, Integer resourceId, String content, AppUser userInfo) {\n\n        try {\n            SystemResource repository = resourceDao.getResourceById(repositoryId);\n            SystemResource resource = resourceDao.getResourceById(resourceId);\n            // a).推送脚本\n            if (repository != null && resource != null && resource.getType() == ResourceEnum.SCRIPT.getValue()) {\n                // 远程仓库地址ip/path\n                String repos_ip = repository.getName();\n                String fileDir = String.format(\"%s%s\", repository.getDir(), resource.getDir());\n\n                // 1. 先内容保存到本地\n                String localAbsolutePath = MachineProtocol.TMP_DIR + resource.getName();\n                File tmpDir = new File(MachineProtocol.TMP_DIR);\n                if (!tmpDir.exists()) {\n                    if (!tmpDir.mkdirs()) {\n                        logger.error(\"cannot create /tmp/cachecloud directory.\");\n                    }\n                }\n                Path path = Paths.get(MachineProtocol.TMP_DIR + resource.getName());\n                BufferedWriter bufferedWriter = null;\n                try {\n                    bufferedWriter = Files.newBufferedWriter(path, Charset.forName(MachineProtocol.ENCODING_UTF8));\n                    bufferedWriter.write(content);\n                } catch (IOException e) {\n                    logger.error(\"write rmt file error, ip: {}, path: {}, content: {}\", repos_ip, localAbsolutePath, content);\n                    return SuccessEnum.FAIL;\n                } finally {\n                    if (bufferedWriter != null) {\n                        try {\n                            bufferedWriter.close();\n                        } catch (Exception e) {\n                            logger.error(e.getMessage(), e);\n                        }\n                    }\n                }\n                // 2. scp file to remote\n\n                try {\n                    //2.1 备份老文件\n                    String bakDir = fileDir + \"/bak\";\n                    String filename = fileDir + \"/\" + resource.getName();\n                    String bakfilename = bakDir + \"/\" + resource.getName() + \"-\" + DateUtil.formatYYYYMMddHHMMss(new Date()) + \"-\" + userInfo.getName();\n                    String bakcmd = String.format(\"mkdir -p %s && cp -r %s %s\", bakDir, filename, bakfilename);\n                    String bak_result = SSHUtil.execute(repos_ip, bakcmd);\n                    logger.info(\"bak_result: {}\", bak_result);\n\n                    //2.2 上传新文件\n                    SSHUtil.scpFileToRemote(repos_ip, localAbsolutePath, fileDir);\n                } catch (SSHException e) {\n                    logger.error(\"message {}\", e.getMessage(), e);\n                    return SuccessEnum.FAIL;\n                }\n                // 3. delete temp file\n                File file = new File(localAbsolutePath);\n                if (file.exists()) {\n                    boolean del = file.delete();\n                    if (!del) {\n                        logger.warn(\"file.delete:{}\", del);\n                    }\n                }\n                // 4.update push status\n                resource.setIspush(PushEnum.YES.getValue());\n                resource.setUsername(userInfo.getName());\n                resource.setLastmodify(new Date());\n                resourceDao.update(resource);\n            }\n        } catch (Exception e) {\n            logger.error(\"pushScript resource repositoryId:{} resourceId:{} error:{}\", repositoryId, resourceId, e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n        return SuccessEnum.SUCCESS;\n    }\n\n    @Override\n    public SuccessEnum pushDir(Integer repositoryId, Integer resourceId, AppUser userInfo) {\n\n        try {\n            SystemResource repository = resourceDao.getResourceById(repositoryId);\n            SystemResource resource = resourceDao.getResourceById(resourceId);\n            if (repository != null && resource != null) {\n                // 1.推送目录\n                SSHTemplate.Result result = sshService.executeWithResult(repository.getName(), String.format(\"mkdir -p %s\", repository.getDir() + resource.getName()));\n                if (!result.isSuccess()) {\n                    logger.error(\"pushDir resource repositoryId:{} resourceId:{} result:{}\", repositoryId, resourceId, result);\n                    return SuccessEnum.FAIL;\n                }\n                // 2.update push status\n                resource.setIspush(PushEnum.YES.getValue());\n                resource.setUsername(userInfo.getName());\n                resource.setLastmodify(new Date());\n                resourceDao.update(resource);\n            }\n\n        } catch (Exception e) {\n            logger.error(\"pushDir resource repositoryId:{} resourceId:{} error:{}\", repositoryId, resourceId, e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n        return SuccessEnum.SUCCESS;\n    }\n\n    public SystemResource getResourceById(int repositoryId) {\n        try {\n            return resourceDao.getResourceById(repositoryId);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    public SystemResource getResourceByName(String resourceName) {\n        try {\n            return resourceDao.getResourceByName(resourceName);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    public String getRespositoryUrl(int resourceId, int respoitoryId) {\n\n        try {\n            SystemResource resource = resourceDao.getResourceById(resourceId);\n            SystemResource repository = resourceDao.getResourceById(respoitoryId);\n\n            String ip = repository.getName();\n            // validate ip connect\n            if (!StringUtils.isEmpty(ip)) {\n                return String.format(\"%s%s/%s\", repository.getUrl(), resource.getDir(), resource.getName());\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n\n    }\n\n    public String getRemoteFileContent(int resourceId, int respoitoryId) {\n\n        try {\n            SystemResource resource = resourceDao.getResourceById(resourceId);\n            SystemResource repository = resourceDao.getResourceById(respoitoryId);\n\n            String ip = repository.getName();\n            // validate ip connect\n            if (!StringUtils.isEmpty(ip)) {\n                String command = String.format(\"cat %s%s/%s\", repository.getDir(), resource.getDir(), resource.getName());\n                SSHTemplate.Result result = sshService.executeWithResult(ip, command);\n                logger.info(result.getResult());\n                return result.getResult();\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    public SystemResource getRepository() {\n        try {\n            List<SystemResource> repositorylist = resourceDao.getResourceList(ResourceEnum.Repository.getValue());\n            if (!CollectionUtils.isEmpty(repositorylist)) {\n                return repositorylist.get(0);\n            } else {\n                return null;\n            }\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return null;\n    }\n\n    public Map<Integer, Integer> getAppUseRedis() {\n        Map<Integer, Integer> resultMap = new HashMap<>();\n        try {\n            List<Map<Integer, Integer>> mapList = resourceDao.getAppUseRedis();\n            if (!CollectionUtils.isEmpty(mapList)) {\n                for (Map<Integer, Integer> stat : mapList) {\n                    resultMap.put(MapUtils.getInteger(stat, \"version_id\"), MapUtils.getInteger(stat, \"num\"));\n                }\n            }\n        } catch (Exception e) {\n            logger.error(\"getAppUseRedis error :{}\", e.getMessage(), e);\n        }\n        return resultMap;\n    }\n\n    @Override\n    public SystemResource getRedisResourceByCache(Integer repositoryId) {\n        SystemResource resource = null;\n        try {\n            Map<Integer, SystemResource> resourceMap = ConstUtils.REDIS_RESOURCE;\n            if (MapUtils.isNotEmpty(resourceMap)) {\n                resource = resourceMap.get(repositoryId);\n            }\n\n            if (resource == null) {\n                resource = this.getResourceById(repositoryId);\n                if (resource != null) {\n                    ConstUtils.REDIS_RESOURCE.put(Integer.valueOf(resource.getId()), resource);\n                }\n            }\n            return resource;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public String getRedisVersion(Integer repositoryId) {\n        String version = null;\n        SystemResource redisResource = this.getRedisResourceByCache(repositoryId);\n        if (redisResource != null) {\n            String name = redisResource.getName();\n            if (name.contains(\"-\")) {\n                String[] nameArr = name.split(\"-\");\n                if (nameArr != null && nameArr.length == 2) {\n                    String[] versionArr = nameArr[1].split(\"\\\\.\");\n                    if (versionArr != null && versionArr.length == 3) {\n                        version = nameArr[1];\n                    }\n                }\n            }\n        }\n        return version;\n    }\n\n    @Override\n    public boolean checkRedisVersionGreater(Integer repositoryId, int[] versions) {\n        SystemResource redisResource = this.getRedisResourceByCache(repositoryId);\n        if (redisResource != null) {\n            String name = redisResource.getName();\n            if (name.contains(\"-\")) {\n                String[] nameArr = name.split(\"-\");\n                if (nameArr != null && nameArr.length == 2) {\n                    String[] versionArr = nameArr[1].split(\"\\\\.\");\n                    if (versionArr != null && versionArr.length == 3) {\n                        for (int i = 0; i < versions.length; i++) {\n                            if (Integer.valueOf(versionArr[i]) < versions[i]) {\n                                return false;\n                            }\n                        }\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/ServerDataServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.sohu.cache.dao.ServerStatusDao;\r\nimport com.sohu.cache.entity.ServerInfo;\r\nimport com.sohu.cache.entity.ServerStatus;\r\nimport com.sohu.cache.server.data.Server;\r\nimport com.sohu.cache.web.service.ServerDataService;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Service;\r\n\r\n@Service\r\npublic class ServerDataServiceImpl implements ServerDataService {\r\n\tprivate static final Logger logger = LoggerFactory.getLogger(ServerDataServiceImpl.class);\r\n\t@Autowired\r\n\tprivate ServerStatusDao serverStatusDao;\r\n\t\r\n\t@Override\r\n\tpublic ServerInfo queryServerInfo(String ip) {\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.queryServerInfo(ip);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"query err:\"+ip, e);\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tpublic List<ServerInfo> getAllServerInfo(){\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.getAllServerInfo();\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(e.getMessage(),e);\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void saveServerInfo(String ip, String dist) {\r\n\t\tif(dist == null) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tdist = dist.trim();\r\n\t\tif(dist.length() == 0) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\tserverStatusDao.saveServerInfo(ip, dist);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"saveServerInfo err:\"+ip+\" dist=\"+dist, e);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic Integer saveAndUpdateServerInfo(Server server) {\r\n\t\tif(server.getHost() == null || server.getNmon() == null || server.getCpus() == 0 || \r\n\t\t   server.getCpuModel() == null || server.getKernel() == null || server.getUlimit() == null) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.saveAndUpdateServerInfo(server);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"saveAndUpdateServerInfo err server=\"+server, e);\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic List<ServerStatus> queryServerStatus(String ip, String date) {\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.queryServerStatus(ip, date);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"queryServerStatus err ip=\"+ip+\" date=\"+date, e);\r\n\t\t}\r\n\t\treturn new ArrayList<ServerStatus>(0);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic List<ServerStatus> queryServerOverview(String ip, String date) {\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.queryServerOverview(ip, date);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"queryServerOverview err ip=\"+ip+\" date=\"+date, e);\r\n\t\t}\r\n\t\treturn new ArrayList<ServerStatus>(0);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic List<ServerStatus> queryServerCpu(String ip, String date) {\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.queryServerCpu(ip, date);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"queryServerCpu err ip=\"+ip+\" date=\"+date, e);\r\n\t\t}\r\n\t\treturn new ArrayList<ServerStatus>(0);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic List<ServerStatus> queryServerNet(String ip, String date) {\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.queryServerNet(ip, date);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"queryServerNet err ip=\"+ip+\" date=\"+date, e);\r\n\t\t}\r\n\t\treturn new ArrayList<ServerStatus>(0);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic List<ServerStatus> queryServerDisk(String ip, String date) {\r\n\t\ttry {\r\n\t\t\treturn serverStatusDao.queryServerDisk(ip, date);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"queryServerDisk err ip=\"+ip+\" date=\"+date, e);\r\n\t\t}\r\n\t\treturn new ArrayList<ServerStatus>(0);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void saveServerStat(Server server) {\r\n\t\tif(server == null || server.getDateTime() == null) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\tserverStatusDao.saveServerStat(server);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlogger.error(\"saveServerStat err server=\"+server, e);\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/ToolServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.AppUserDao;\nimport com.sohu.cache.dao.MachineDao;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.task.constant.TopoloyExamContants;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.service.AppService;\nimport com.sohu.cache.web.service.ToolService;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.text.MessageFormat;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Created by rucao on 2018/12/11\n */\n@Service\npublic class ToolServiceImpl implements ToolService {\n    private final static String APPID = \"appId\";\n    private final static String TYPE = \"type\";\n    private final static String STATUS = \"status\";\n    private final static String DESC = \"desc\";\n    private final static String SENTINEL_DESC = \"instanceId:{0},ip:{1}\";\n    private final static String MASTER_SALVE = \"masterIp:{0},salveIp:{1}\";\n    private Logger logger = LoggerFactory.getLogger(ToolServiceImpl.class);\n    @Resource\n    private AppDao appDao;\n    @Resource\n    private MachineDao machineDao;\n    @Resource\n    private AppService appService;\n    @Resource\n    private AppUserDao appUserDao;\n\n    @Override\n    public List<Map> topologyExamByAppid(long appId) {\n        List<Map> examResult = new ArrayList<Map>();\n        checkTopologyByAppid(appId, examResult);\n        return examResult;\n    }\n\n    @Override\n    public void topologyExam(List<Long> appidList) {\n        List<Map> examResult = new ArrayList<>();\n        for (long appId : appidList) {\n            checkTopologyByAppid(appId, examResult);\n        }\n    }\n\n    @Override\n    public void restAppDescOfficer() {\n        List<AppDesc> appDescList = appDao.getAllApps();\n        appDescList.stream().forEach(appDesc -> {\n            try {\n                String officer = appDesc.getOfficer();\n                if (StringUtils.isNotEmpty(officer)) {\n                    List<String> nameList = Arrays.asList(officer.split(\";|,| |；|，|、\"));\n                    String officer_new = String.join(\",\", getUserIdListByNames(nameList));\n                    appDesc.setOfficer(officer_new);\n                    if (appDao.update(appDesc) > 0) {\n                        logger.info(\"update appDesc success, appId:{}, officer:{}\", appDesc.getAppId(), officer_new);\n                    } else {\n                        logger.info(\"update appDesc fail, appId:{}, officer:{}\", appDesc.getAppId(), officer_new);\n                    }\n                }\n            }catch (Exception e){\n                logger.error(\"update appDesc error, appId:{}\", appDesc.getAppId());\n            }\n        });\n    }\n\n    private List<String> getUserIdListByNames(List<String> nameList) {\n        return nameList.stream()\n                .map(name -> NumberUtils.isNumber(name) ? name : getUserIdByName(name))\n                .collect(Collectors.toList());\n    }\n\n    private String getUserIdByName(String name) {\n        List<AppUser> appUsers = appUserDao.getUserList(name);\n        if (CollectionUtils.isNotEmpty(appUsers) && appUsers.size() > 0) {\n            return String.valueOf(appUsers.get(0).getId());\n        } else {\n            AppUser user = appUserDao.getByName(name);\n            if(user!=null){\n                return String.valueOf(user.getId());\n            }else {\n                AppUser appUser = appUserDao.getByEmail(name);\n                return appUser == null ? \"\" : String.valueOf(appUser.getId());\n            }\n        }\n    }\n\n\n    public void checkTopologyByAppid(long appId, List<Map> examResult) {\n        try {\n            AppDesc appDesc = appDao.getAppDescById(appId);\n            if (appDesc.getType() == ConstUtils.CACHE_REDIS_SENTINEL) {//redis-sentinel\n                logger.info(\"redis-sentinel应用\");\n                sentinelExam(appId, examResult);\n            } else if (appDesc.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {//redis-cluster\n                logger.info(\"redis-cluster应用\");\n                clusterExam(appId, examResult);\n            }\n        } catch (Exception e) {\n            logger.error(\"appid:{}, exception:{}\", appId, e.getMessage());\n        }\n    }\n\n    private String getRealIp(String ip) {\n        MachineInfo machineInfo = machineDao.getMachineInfoByIp(ip);\n        if (machineInfo.getVirtual() == 1 && !StringUtils.isEmpty(machineInfo.getRealIp())) {\n            return machineInfo.getRealIp();\n        } else {\n            return ip;\n        }\n    }\n\n    private void sentinelExam(final long appId, List<Map> examResult) {\n        List<InstanceInfo> instances = appService.getAppInstanceInfo(appId);\n        //sentinel set\n        Map<String, String> sent_realIps = new HashMap<String, String>();\n        //master_slave\n        Map<String, List<String>> master_slave = new HashMap<String, List<String>>();\n        for (InstanceInfo inst : instances) {\n            if (inst.getStatus() == 1 && inst.getRoleDesc().equals(\"sentinel\")) {\n                String desc = MapUtils.getString(sent_realIps, getRealIp(inst.getIp()), \"\");\n                sent_realIps.put(getRealIp(inst.getIp()), desc + \"\\n\" + MessageFormat.format(SENTINEL_DESC, inst.getId(), inst.getIp()));\n            } else if (inst.getStatus() == 1 && inst.getRoleDesc().equals(\"master\")) {\n\n            } else if (inst.getStatus() == 1 && inst.getRoleDesc().equals(\"slave\")) {\n                String masterHost = inst.getMasterHost();\n                List<String> slaves = (ArrayList<String>) MapUtils.getObject(master_slave, masterHost, new ArrayList<String>());\n                slaves.add(inst.getIp());\n                master_slave.put(masterHost, slaves);\n            }\n        }\n        //sentinel result\n        if (sent_realIps.size() < 3) {\n            //String tmp = \"\";\n            StringBuilder tmpBuilder = new StringBuilder();\n            for (Map.Entry<String, String> realIps : sent_realIps.entrySet()) {\n                tmpBuilder.append(\"realIp::\")\n                        .append(realIps.getValue())\n                        .append(\"<br/>\")\n                        .append(realIps.getValue())\n                        .append(\"<br/>\");\n            }\n            final String descStr = tmpBuilder.toString();\n            examResult.add(\n                    new HashMap<String, String>() {{\n                        put(APPID, String.valueOf(appId));\n                        put(TYPE, \"redis_sentinel\");\n                        put(STATUS, TopoloyExamContants.NODESNUM_DESC);\n                        put(DESC, descStr);\n                    }}\n            );\n            logger.info(\"sentinel节点分布在少于3个物理机，应用：{0}\", appId);\n        }\n        //master-slave result\n        StringBuilder tmpBuilder1 = new StringBuilder();\n        for (Map.Entry<String, List<String>> res : master_slave.entrySet()) {\n            for (String salveIp : res.getValue()) {\n                if (getRealIp(salveIp).equals(getRealIp(res.getKey()))) {\n                    tmpBuilder1.append(MessageFormat.format(MASTER_SALVE, res.getKey(), salveIp))\n                            .append(\"<br/>\");\n                }\n            }\n        }\n        if (tmpBuilder1.length() > 0) {\n            final String descStr = tmpBuilder1.toString();\n            examResult.add(\n                    new HashMap<String, String>() {{\n                        put(APPID, String.valueOf(appId));\n                        put(TYPE, \"redis_sentinel\");\n                        put(STATUS, \"master-slave节点不能在一台物理机上\");\n                        put(DESC, descStr);\n                    }}\n            );\n            logger.info(\"master-slave节点不能在一台物理机上，应用: {0}\", appId);\n        }\n    }\n\n    private void clusterExam(final long appId, List<Map> examResult) {\n        List<InstanceInfo> instances = appService.getAppInstanceInfo(appId);\n        //mater节点数\n        List<String> masters = new ArrayList<>();\n        //master物理机数量\n        Map<String, String> master_realIps = new HashMap<String, String>();\n        Map<String, String> realIpMap = new HashMap<String, String>();\n\n        int slaveNum = 0;\n        //master_slave\n        Map<String, List<String>> master_slave = new HashMap<String, List<String>>();\n        for (InstanceInfo inst : instances) {\n            if (inst.getStatus() == 1) {//实例正常启用\n                if (inst.getRoleDesc().equals(\"master\")) {\n                    //master_realIps.add(getRealIp(inst.getIp()));\n                    masters.add(inst.getIp());\n                    String desc = MapUtils.getString(master_realIps, getRealIp(inst.getIp()), \"\");\n                    master_realIps.put(getRealIp(inst.getIp()), desc + \"\\n\" + MessageFormat.format(SENTINEL_DESC, inst.getId(), inst.getIp()));\n\n                    String desc1 = MapUtils.getString(realIpMap, getRealIp(inst.getIp()), \"\");\n                    realIpMap.put(getRealIp(inst.getIp()), desc1 + \",\" + inst.getId());\n\n                    String masterId = String.valueOf(inst.getId());\n                    String masterHost = inst.getIp();\n                    String key = masterId + \"_\" + masterHost;\n                    List<String> slaves = (ArrayList<String>) MapUtils.getObject(master_slave, key, new ArrayList<String>());\n                    master_slave.put(key, slaves);\n                } else if (inst.getRoleDesc().equals(\"slave\")) {\n                    slaveNum++;\n                    String masterHost = inst.getMasterHost();\n                    String masterId = String.valueOf(inst.getMasterInstanceId());\n                    String slaveIp = inst.getIp();\n                    String key = masterId + \"_\" + masterHost;\n                    List<String> slaves = (ArrayList<String>) MapUtils.getObject(master_slave, key, new ArrayList<String>());\n                    if (!slaves.contains(slaveIp)) {\n                        slaves.add(slaveIp);\n                    }\n                    master_slave.put(key, slaves);\n                }\n            }\n        }\n\n        if (master_realIps.size() < 3) {\n            StringBuilder tmpBuilder = new StringBuilder();\n            for (Map.Entry<String, String> realIps : master_realIps.entrySet()) {\n                tmpBuilder.append(\"realIp::\")\n                        .append(realIps.getKey())\n                        .append(\"\\n\")\n                        .append(realIps.getValue())\n                        .append(\"\\n\");\n            }\n            final String descStr = tmpBuilder.toString();\n            examResult.add(\n                    new HashMap<String, String>() {{\n                        put(APPID, String.valueOf(appId));\n                        put(TYPE, TopoloyExamContants.REDIS_CLUSTER);\n                        put(STATUS, TopoloyExamContants.NODESNUM_DESC);\n                        put(DESC, descStr);\n                    }}\n            );\n        }\n        //2. master-slave result\n        StringBuilder tmpBuilder1 = new StringBuilder();\n        for (Map.Entry<String, List<String>> res : master_slave.entrySet()) {\n            for (String salveIp : res.getValue()) {\n                String masterHost = res.getKey().split(\"_\")[1];\n                if (getRealIp(salveIp).equals(masterHost)) {\n                    tmpBuilder1.append(MessageFormat.format(MASTER_SALVE, res.getKey(), salveIp))\n                            .append(\"\\n\");\n                }\n            }\n            //检查master是否有slave\n            if (slaveNum != 0) {\n                if (res.getValue().size() == 0) {\n                    examResult.add(\n                            new HashMap<String, String>() {{\n                                put(APPID, String.valueOf(appId));\n                                put(TYPE, TopoloyExamContants.REDIS_CLUSTER);\n                                put(STATUS, TopoloyExamContants.SLAVE_NOT_EXIST);\n                                put(DESC, MessageFormat.format(MASTER_SALVE, res.getKey(), \"null\"));\n                            }}\n                    );\n                }\n            }\n        }\n        if (tmpBuilder1.length() > 0) {\n            final String descStr = tmpBuilder1.toString();\n            examResult.add(\n                    new HashMap<String, String>() {{\n                        put(APPID, String.valueOf(appId));\n                        put(TYPE, TopoloyExamContants.REDIS_CLUSTER);\n                        put(STATUS, TopoloyExamContants.MASTER_SLAVE_DESC);\n                        put(DESC, descStr);\n                    }}\n            );\n        }\n        //3. 故障转移\n        for (Map.Entry<String, String> entry : realIpMap.entrySet()) {\n            String[] instList = entry.getValue().split(\",\");\n            int num = instList.length;\n            //String desc = \"\";\n            StringBuilder descBuilder = new StringBuilder();\n            int count = 0;\n            for (int i = 0; i < num; i++) {\n                //desc += \"instanceId::\" + instList[i] + \"   \";\n                descBuilder.append(\"instanceId::\")\n                        .append(instList[i])\n                        .append(\"   \");\n                if (!instList[i].isEmpty()) {\n                    count++;\n                }\n            }\n            descBuilder.append(\"   物理机ip::\").append(entry.getKey());\n            final String descStr = descBuilder.toString();\n            if (masters.size() - count < masters.size() / 2 + 1) {\n                examResult.add(\n                        new HashMap<String, String>() {{\n                            put(APPID, String.valueOf(appId));\n                            put(TYPE, \"redis_cluster\");\n                            put(STATUS, \"集群中一台物理机下线不满足自动故障转移条件\");\n                            put(DESC, descStr);\n                        }}\n                );\n                logger.info(\"集群中某一台物理机下线不满足自动故障转移条件，应用: \" + appId);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/UserLoginStatusCookieServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.login.LoginComponent;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.EnvUtil;\nimport com.sohu.cache.util.StringUtil;\nimport com.sohu.cache.web.service.UserLoginStatusService;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.util.AESCoder;\nimport com.sohu.cache.web.util.WebUtil;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.env.Environment;\nimport org.springframework.stereotype.Service;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Random;\n\n/**\n * cookie保护登录状态\n *\n * @author leifu\n */\n@Service(\"userLoginStatusService\")\npublic class UserLoginStatusCookieServiceImpl implements UserLoginStatusService {\n\n    private Logger logger = LoggerFactory.getLogger(UserLoginStatusCookieServiceImpl.class);\n\n    @Autowired\n    private LoginComponent loginComponent;\n\n    @Autowired\n    protected UserService userService;\n\n    @Autowired\n    private Environment environment;\n\n    private static final String CONCATE_STR = \"&&\";\n\n    @Override\n    public String getUserNameFromLoginStatus(HttpServletRequest request) {\n        if (EnvUtil.isLocal(environment)) {\n            //todo for local\n            return \"admin\";\n        }\n        String userName = null;\n        String userCookie = null;\n        String cookie = WebUtil.getLoginCookieValue(request);\n        try {\n            userCookie = AESCoder.decrypt(cookie, ConstUtils.USER_LOGIN_ENCRY_KEY);\n        } catch (Exception e) {\n            logger.error(\"getUserNameFromLoginStatus decrypt error: \", e);\n        }\n        if(StringUtil.isBlank(userCookie)){\n            return null;\n        }\n        String[] userInfos = userCookie.split(CONCATE_STR);\n        if(userInfos != null && userInfos.length > 0){\n            userName = userInfos[0];\n        }\n        if (StringUtils.isNotBlank(userName)) {\n            return userName;\n        }\n        return null;\n    }\n\n    @Override\n    public String getUserNameFromTicket(HttpServletRequest request) {\n        String ticket = request.getParameter(\"ticket\");\n        if (StringUtils.isNotBlank(ticket)) {\n            String email = loginComponent.getEmail(ticket);\n            if (StringUtils.isNotBlank(email) && email.contains(\"@\")) {\n                String userName = email.substring(0, email.indexOf(\"@\"));\n                return userName;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public String getRedirectUrl(HttpServletRequest request) {\n        return loginComponent.getRedirectUrl(request);\n    }\n\n    @Override\n    public String getLogoutUrl() {\n        return loginComponent.getLogoutUrl();\n    }\n\n    @Override\n    public String getRegisterUrl(AppUser user) {\n        if (user != null && user.getType() == -1) {\n            return \"/user/register?success=1\";\n        }\n        return \"/user/register\";\n    }\n\n    @Override\n    public void addLoginStatus(HttpServletRequest request, HttpServletResponse response, String userName) {\n        if(userName == null){\n            userName = \"\";\n        }else{\n            userName = userName + CONCATE_STR + getRandomStr4Encrypt();\n            try {\n                userName = AESCoder.encrypt(userName,  ConstUtils.USER_LOGIN_ENCRY_KEY);\n            } catch (Exception e) {\n                logger.error(\"addLoginStatus encrypt error: \",e);\n            }\n        }\n        Cookie cookie = new Cookie(LOGIN_USER_STATUS_NAME, userName);\n        cookie.setDomain(request.getServerName());\n        cookie.setPath(\"/\");\n        cookie.setMaxAge(-1);\n        response.addCookie(cookie);\n    }\n\n    @Override\n    public void removeLoginStatus(HttpServletRequest request, HttpServletResponse response) {\n        addLoginStatus(request, response, \"\");\n    }\n\n    private String getRandomStr4Encrypt(){\n        return String.valueOf(new Random().nextInt(Integer.MAX_VALUE));\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/UserServiceImpl.java",
    "content": "package com.sohu.cache.web.service.impl;\n\nimport com.sohu.cache.constant.AppUserAlertEnum;\nimport com.sohu.cache.dao.AppBizDao;\nimport com.sohu.cache.dao.AppDao;\nimport com.sohu.cache.dao.AppToUserDao;\nimport com.sohu.cache.dao.AppUserDao;\nimport com.sohu.cache.entity.AppBiz;\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.entity.AppToUser;\nimport com.sohu.cache.entity.AppUser;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.vo.AppUserVo;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 用户管理实现\n *\n * @author leifu\n * @Date 2014年10月27日\n * @Time 上午9:57:43\n */\n@Service\npublic class UserServiceImpl implements UserService {\n    private Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);\n\n    /**\n     * 用户dao\n     */\n    @Autowired\n    private AppUserDao appUserDao;\n\n    /**\n     * 业务组dao\n     */\n    @Autowired\n    private AppBizDao appBizDao;\n\n    /**\n     * 用户应用关系dao\n     */\n    @Autowired\n    private AppToUserDao appToUserDao;\n    @Resource\n    private AppDao appDao;\n\n    @Override\n    public AppUser get(Long userId) {\n        return appUserDao.get(userId);\n    }\n\n    @Override\n    public List<AppUser> getUserList(String chName) {\n        return appUserDao.getUserList(chName);\n    }\n\n    /**\n     * 通过中文名和业务组名获取用户\n     * @param chName\n     * @param bizName\n     * @return\n     */\n    @Override\n    public List<AppUserVo> getUserWithBizList(String chName, String bizName) {\n        return appUserDao.getUserWithBizList(chName, bizName);\n    }\n\n    @Override\n    public List<AppUser> getAllUser() {\n        return appUserDao.getAllUser();\n    }\n\n    @Override\n    public List<AppUser> getByAppId(Long appId) {\n        if (appId == null || appId < 0) {\n            return Collections.emptyList();\n        }\n        List<AppUser> resultList = new ArrayList<AppUser>();\n        List<AppToUser> appToUsers = appToUserDao.getByAppId(appId);\n        if (appToUsers != null && appToUsers.size() > 0) {\n            for (AppToUser appToUser : appToUsers) {\n                Long userId = appToUser.getUserId();\n                if (userId == null) {\n                    continue;\n                }\n                AppUser user = appUserDao.get(userId);\n                if (user == null) {\n                    continue;\n                }\n                resultList.add(user);\n            }\n        }\n        return resultList;\n    }\n\n    @Override\n    public List<AppUser> getAlertByAppId(Long appId) {\n        if (appId == null || appId < 0) {\n            return Collections.emptyList();\n        }\n        List<AppUser> resultList = new ArrayList<AppUser>();\n        List<AppToUser> appToUsers = appToUserDao.getByAppId(appId);\n        if (appToUsers != null && appToUsers.size() > 0) {\n            for (AppToUser appToUser : appToUsers) {\n                Long userId = appToUser.getUserId();\n                if (userId == null) {\n                    continue;\n                }\n                AppUser user = appUserDao.get(userId);\n                if (user == null || user.getIsAlert() == AppUserAlertEnum.NO.value()) {\n                    continue;\n                }\n                resultList.add(user);\n            }\n        }\n        return resultList;\n    }\n\n    @Override\n    public AppUser getByName(String name) {\n        try {\n            return appUserDao.getByName(name);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return null;\n        }\n    }\n\n    @Override\n    public SuccessEnum save(AppUser appUser) {\n        try {\n            appUserDao.save(appUser);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public SuccessEnum update(AppUser appUser) {\n        try {\n            appUserDao.update(appUser);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public SuccessEnum delete(Long userId) {\n        try {\n            appUserDao.delete(userId);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public SuccessEnum resetPwd(Long userId) {\n        try {\n            appUserDao.updatePwd(userId, ConstUtils.DEFAULT_USER_PASSWORD);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public SuccessEnum updatePwd(Long userId, String password) {\n        try {\n            appUserDao.updatePwd(userId, password);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public String getOfficerName(Long appId) {\n        if (appId == null || appId < 0) {\n            return \"\";\n        }\n        AppDesc appDesc = appDao.getAppDescById(appId);\n        if (appDesc == null) {\n            return \"\";\n        } else {\n            return getOfficerName(appDesc.getOfficer());\n        }\n\n    }\n\n    public String getOfficerName(String officer) {\n        String officerName = \"\";\n        if (StringUtils.isNotEmpty(officer)) {\n            List<AppUser> officerList = Arrays.stream(officer.split(\",\"))\n                    .filter(userId -> get(NumberUtils.toLong(userId)) != null)\n                    .map(userId -> get(NumberUtils.toLong(userId))).collect(Collectors.toList());\n            List<String> userStrList = officerList.stream().map(user -> user.getChName() + \"(\" + user.getName() + \")\").collect(Collectors.toList());\n            if (CollectionUtils.isNotEmpty(userStrList)) {\n                officerName = StringUtils.join(userStrList, \",\");\n            }\n        }\n        return officerName;\n    }\n\n    /**\n     * 获取某个应用下的所有负责人\n     * @param officer\n     * @return\n     */\n    public List<AppUser> getOfficerUserByUserIds(String officer){\n        List<AppUser> officerList = new ArrayList<>();\n        if (StringUtils.isNotEmpty(officer)) {\n            officerList = Arrays.stream(officer.split(\",\"))\n                    .map(userId -> get(NumberUtils.toLong(userId))).filter(appUser -> appUser != null).collect(Collectors.toList());\n        }\n        return officerList;\n    }\n\n    /**\n     * 接手用户\n     * @param toRemoveUser\n     * @param toChargeUser\n     * @return\n     */\n    public SuccessEnum takeoverUser(AppUser toRemoveUser, AppUser toChargeUser){\n        try{\n            if(toRemoveUser != null && toChargeUser != null && toRemoveUser != toChargeUser){\n                List<AppToUser> removeList = appToUserDao.getByUserId(toRemoveUser.getId());\n                Set<Long> handleAppList = removeList.stream().map(appToUser -> appToUser.getAppId()).collect(Collectors.toSet());\n                List<AppToUser> chargeList = appToUserDao.getByUserId(toChargeUser.getId());\n                Set<Long> existAppList = chargeList.stream().map(appToUser -> appToUser.getAppId()).collect(Collectors.toSet());\n                handleAppList.forEach(appId ->{\n                    if(existAppList.contains(appId)){\n                        appToUserDao.deleteAppToUser(appId, toRemoveUser.getId());\n                    }else{\n                        appToUserDao.takeOverAppToUser(appId, toRemoveUser.getId(), toChargeUser.getId());\n                    }\n                });\n                return SuccessEnum.SUCCESS;\n            }\n            return SuccessEnum.FAIL;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public AppBiz getBiz(Long bizId) {\n        return appBizDao.get(bizId);\n    }\n\n    @Override\n    public List<AppBiz> getBizList() {\n        return appBizDao.getBizList();\n    }\n\n    @Override\n    public SuccessEnum saveBiz(AppBiz appBiz) {\n        try {\n            appBizDao.save(appBiz);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public SuccessEnum updateBiz(AppBiz appBiz) {\n        try {\n            appBizDao.update(appBiz);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n    @Override\n    public SuccessEnum deleteBiz(Long bizId) {\n        try {\n            appBizDao.delete(bizId);\n            return SuccessEnum.SUCCESS;\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return SuccessEnum.FAIL;\n        }\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/AESCoder.java",
    "content": "package com.sohu.cache.web.util;\n\nimport com.sohu.cache.util.StringUtil;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.security.Key;\n\npublic class AESCoder {\n\n    /**\n     * 密钥算法\n     * java6支持56位密钥，bouncycastle支持64位\n     * */\n    public static final String KEY_ALGORITHM=\"AES\";\n\n    /**\n     * 加密/解密算法/工作模式/填充方式\n     *\n     * JAVA6 支持PKCS5PADDING填充方式\n     * Bouncy castle支持PKCS7Padding填充方式\n     * */\n    public static final String CIPHER_ALGORITHM=\"AES/ECB/PKCS5Padding\";\n\n    /**\n     *\n     * 生成密钥，java6只支持56位密钥，bouncycastle支持64位密钥\n     * @return byte[] 二进制密钥\n     * */\n    public static byte[] initkey() throws Exception{\n\n        //实例化密钥生成器\n        KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM);\n        //初始化密钥生成器，AES要求密钥长度为128位、192位、256位\n        kg.init(256);\n        //生成密钥\n        SecretKey secretKey=kg.generateKey();\n        //获取二进制密钥编码形式\n        return secretKey.getEncoded();\n    }\n    /**\n     * 转换密钥\n     * @param key 二进制密钥\n     * @return Key 密钥\n     * */\n    public static Key toKey(byte[] key) throws Exception{\n        //实例化DES密钥\n        //生成密钥\n        SecretKey secretKey=new SecretKeySpec(key,KEY_ALGORITHM);\n        return secretKey;\n    }\n\n    /**\n     * 加密数据\n     * @param data 待加密数据\n     * @return byte[] 加密后的数据\n     * */\n    public static String encrypt(String data, String key) throws Exception{\n        if(StringUtil.isBlank(data)){\n            return \"\";\n        }\n        byte[] encrypt = encrypt(data.getBytes(), key.getBytes());\n        return parseByte2HexStr(encrypt);\n    }\n\n    /**\n     * 加密数据\n     * @param data 待加密数据\n     * @param key 密钥\n     * @return byte[] 加密后的数据\n     * */\n    public static byte[] encrypt(byte[] data,byte[] key) throws Exception{\n        //还原密钥\n        Key k=toKey(key);\n        /**\n         * 实例化\n         * 使用 PKCS7PADDING 填充方式，按如下方式实现,就是调用bouncycastle组件实现\n         * Cipher.getInstance(CIPHER_ALGORITHM,\"BC\")\n         */\n        Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);\n        //初始化，设置为加密模式\n        cipher.init(Cipher.ENCRYPT_MODE, k);\n        //执行操作\n        return cipher.doFinal(data);\n    }\n\n    /**\n     * 解密数据\n     * @param data 待解密数据\n     * @return byte[] 解密后的数据\n     * */\n    public static String decrypt(String data, String key) throws Exception{\n        if(StringUtil.isBlank(data)){\n            return null;\n        }\n        byte[] dataByte = parseHexStr2Byte(data);\n        if(dataByte == null || dataByte.length < 1){\n            return null;\n        }\n        byte[] decrypt = decrypt(dataByte, key.getBytes());\n        return new String(decrypt, \"UTF-8\");\n    }\n\n    /**\n     * 解密数据\n     * @param data 待解密数据\n     * @param key 密钥\n     * @return byte[] 解密后的数据\n     * */\n    public static byte[] decrypt(byte[] data,byte[] key) throws Exception{\n        //欢迎密钥\n        Key k =toKey(key);\n        /**\n         * 实例化\n         * 使用 PKCS7PADDING 填充方式，按如下方式实现,就是调用bouncycastle组件实现\n         * Cipher.getInstance(CIPHER_ALGORITHM,\"BC\")\n         */\n        Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);\n        //初始化，设置为解密模式\n        cipher.init(Cipher.DECRYPT_MODE, k);\n        //执行操作\n        return cipher.doFinal(data);\n    }\n\n    /**\n     * 将16进制转换为二进制\n     *\n     * @param hexStr    字符串\n     * @return          字节数组\n     */\n    public static byte[] parseHexStr2Byte(String hexStr) {\n        if (hexStr.length() < 1) {\n            return null;\n        }\n        byte[] result = new byte[hexStr.length() / 2];\n        for (int i = 0; i < hexStr.length() / 2; i++) {\n            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);\n            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);\n            result[i] = (byte) (high * 16 + low);\n        }\n        return result;\n    }\n\n    /**\n     * 将二进制转换成16进制\n     *\n     * @param buf       字节数组\n     * @return          字符串\n     */\n    public static String parseByte2HexStr(byte buf[]) {\n        StringBuffer sb = new StringBuffer();\n        for (int i = 0; i < buf.length; i++) {\n            String hex = Integer.toHexString(buf[i] & 0xFF);\n            if (hex.length() == 1) {\n                hex = '0' + hex;\n            }\n            sb.append(hex.toUpperCase());\n        }\n        return sb.toString();\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/AppEmailUtil.java",
    "content": "package com.sohu.cache.web.util;\n\nimport com.sohu.cache.alert.EmailComponent;\nimport com.sohu.cache.constant.AppAuditType;\nimport com.sohu.cache.constant.AppCheckEnum;\nimport com.sohu.cache.constant.RedisConfigTemplateChangeEnum;\nimport com.sohu.cache.entity.*;\nimport com.sohu.cache.stats.app.AppStatsCenter;\nimport com.sohu.cache.util.ConstUtils;\nimport com.sohu.cache.util.EnvUtil;\nimport com.sohu.cache.web.enums.SuccessEnum;\nimport com.sohu.cache.web.service.UserService;\nimport com.sohu.cache.web.vo.AppDetailVO;\nimport freemarker.template.Configuration;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.env.Environment;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.stream.Collectors;\n\n/**\n * 邮件通知应用的申请流程(方法内是具体的文案)\n *\n * @author leifu\n */\n@Component\n@Slf4j\npublic class AppEmailUtil {\n    private Logger logger = LoggerFactory.getLogger(AppEmailUtil.class);\n\n    @Autowired(required = false)\n    private EmailComponent emailComponent;\n\n    @Autowired\n    private UserService userService;\n\n    @Autowired\n    private Configuration configuration;\n\n    @Autowired\n    private AppStatsCenter appStatsCenter;\n\n    @Autowired\n    private Environment environment;\n\n    /**\n     * 应用状态通知\n     *\n     * @param appDesc\n     * @param appAudit\n     */\n    public void noticeAppResult(AppDesc appDesc, AppAudit appAudit) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        List<String> ccEmailList = getCCEmailList(appDesc, appAudit);\n        String officerIds = appDesc.getOfficer();\n        appDesc.setOfficer(userService.getOfficerName(officerIds));\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"appDesc\", appDesc);\n        context.put(\"appAudit\", appAudit);\n        context.put(\"appDailyData\", new AppDailyData());\n        context.put(\"instanceAlertValueResultList\", new ArrayList<InstanceAlertValueResult>());\n        String mailContent = FreemakerUtils.createText(\"appAudit.ftl\", configuration, context);\n        AppUser appUser = userService.get(appDesc.getUserId());\n        List<String> receiveEmailList = new ArrayList<>();\n        userService.getOfficerUserByUserIds(officerIds).forEach(user -> receiveEmailList.add(user.getEmail()));\n        if(CollectionUtils.isEmpty(receiveEmailList) && appUser != null) {\n            receiveEmailList.add(appUser.getEmail());\n        }\n        emailComponent.sendMail(\"【CacheCloud】状态通知\", mailContent, receiveEmailList, ccEmailList);\n    }\n\n    /**\n     * 应用状态通知\n     *\n     * @param applyUser\n     * @param appDesc\n     * @param appAudit\n     */\n    public void noticeAppResultWithApplyUser(AppUser applyUser, AppDesc appDesc, AppAudit appAudit) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        List<String> ccEmailList = getCCEmailList(appDesc, appAudit);\n        String officerIds = appDesc.getOfficer();\n        appDesc.setOfficer(userService.getOfficerName(officerIds));\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"appDesc\", appDesc);\n        context.put(\"appAudit\", appAudit);\n        context.put(\"appDailyData\", new AppDailyData());\n        context.put(\"instanceAlertValueResultList\", new ArrayList<InstanceAlertValueResult>());\n        String mailContent = FreemakerUtils.createText(\"appAudit.ftl\", configuration, context);\n        AppUser appUser = userService.get(appDesc.getUserId());\n        List<String> receiveEmailList = new ArrayList<>();\n        userService.getOfficerUserByUserIds(officerIds).forEach(user -> receiveEmailList.add(user.getEmail()));\n        if(CollectionUtils.isEmpty(receiveEmailList) && appUser != null) {\n            receiveEmailList.add(appUser.getEmail());\n        }\n        receiveEmailList.add(applyUser.getEmail());\n        List<String> appOfficeList = receiveEmailList.stream().distinct().collect(Collectors.toList());\n        emailComponent.sendMail(\"【CacheCloud】状态通知\", mailContent, appOfficeList, ccEmailList);\n    }\n\n    /**\n     * 应用状态通知\n     *\n     * @param appUser\n     * @param appAudit\n     */\n    public void noticeAuditResult(AppUser appUser, AppAudit appAudit) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        List<String> ccEmailList = Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA));\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"appAudit\", appAudit);\n        context.put(\"appDailyData\", new AppDailyData());\n        context.put(\"instanceAlertValueResultList\", new ArrayList<InstanceAlertValueResult>());\n        String mailContent = FreemakerUtils.createText(\"appAudit.ftl\", configuration, context);\n        List<String> receiveEmailList = new ArrayList<>();\n        if(appUser != null){\n            receiveEmailList.add(appUser.getEmail());\n        }\n        emailComponent.sendMail(\"【CacheCloud】状态通知\", mailContent, receiveEmailList, ccEmailList);\n    }\n\n    /**\n     * API应用状态通知\n     *\n     * @param appDesc\n     * @param appAudit\n     */\n    public void noticeAppResultByApi(AppDesc appDesc, AppAudit appAudit) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        List<String> ccEmailList = getCCEmailList(appDesc, appAudit);\n        appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));\n        Map<String, Object> context = new HashMap<>();\n        context.put(\"appDesc\", appDesc);\n        context.put(\"appAudit\", appAudit);\n        context.put(\"appDailyData\", new AppDailyData());\n        context.put(\"instanceAlertValueResultList\", new ArrayList<InstanceAlertValueResult>());\n        String mailContent = FreemakerUtils.createText(\"appAudit.ftl\", configuration, context);\n        AppUser appUser = userService.get(appDesc.getUserId());\n        emailComponent.sendMail(\"【CacheCloud】API应用申请通知\", mailContent, Arrays.asList(appUser.getEmail()), ccEmailList);\n    }\n\n    /**\n     * 重要应用抄送\n     *\n     * @param appDesc\n     * @param appAudit\n     * @return\n     */\n    private List<String> getCCEmailList(AppDesc appDesc, AppAudit appAudit) {\n        Set<String> ccEmailSet = new LinkedHashSet<String>();\n        for (String email : emailComponent.getAdminEmail().split(ConstUtils.COMMA)) {\n            ccEmailSet.add(email);\n        }\n        //S级别，且是开通邮件\n        if (appDesc.isSuperImportant() && AppAuditType.APP_AUDIT.getValue() == appAudit.getType()) {\n            ccEmailSet.addAll(ConstUtils.LEADER_EMAIL_LIST);\n        }\n        return new ArrayList<String>(ccEmailSet);\n    }\n\n    /**\n     * 贡献者通知\n     *\n     * @param groupName\n     * @param applyReason\n     * @param appUser\n     */\n    public void noticeBecomeContributor(String groupName, String applyReason, AppUser appUser) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        StringBuffer mailContent = new StringBuffer();\n        mailContent.append(appUser.getChName() + \"(项目组:\" + groupName + \")申请成为CacheCloud贡献者<br/>\");\n        mailContent.append(\"申请理由:<br/>\" + applyReason);\n        emailComponent.sendMail(\"【CacheCloud】状态通知\", mailContent.toString(), Arrays.asList(appUser.getEmail()), Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n    }\n\n    /**\n     * 注册用户通知\n     *\n     * @param appUser\n     * @param appAudit\n     */\n    public void noticeUserResult(AppUser appUser, AppAudit appAudit) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        if (appAudit == null) {\n            return;\n        }\n        StringBuffer mailContent = new StringBuffer();\n        if (AppCheckEnum.APP_WATING_CHECK.value().equals(appAudit.getStatus())) {\n            mailContent.append(appUser.getChName() + \"申请想成为CacheCloud用户，请管理员帮忙处理！<br/>\");\n        } else if (AppCheckEnum.APP_PASS.value().equals(appAudit.getStatus())) {\n            mailContent.append(\"您的用户申请已经审批通过，您可以登录正常Cachecloud了！<br/>\");\n        } else if (AppCheckEnum.APP_REJECT.value().equals(appAudit.getStatus())) {\n            mailContent.append(\"您的用户申请被驳回，原因是: \" + appAudit.getRefuseReason());\n        }\n        emailComponent.sendMail(\"【CacheCloud】状态通知\", mailContent.toString(), Arrays.asList(appUser.getEmail()), Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n    }\n\n    /**\n     * 下线应用通知\n     *\n     * @param appUser\n     * @param appId\n     * @param isSuccess\n     */\n    public void noticeOfflineApp(AppUser appUser, Long appId, boolean isSuccess) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        AppDetailVO appDetailVO = appStatsCenter.getAppDetail(appId);\n        StringBuilder mailContent = new StringBuilder();\n        mailContent.append(appUser.getChName()).append(\",对应用appid=\").append(appId);\n        mailContent.append(\"进行下线,操作结果是\").append(isSuccess ? \"下线任务执行成功\" : \"下线任务失败,请关注任务流日志\");\n        mailContent.append(\",请知晓!\");\n        emailComponent.sendMail(\"【CacheCloud】状态通知\", mailContent.toString(), appDetailVO.getEmailList(), Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n    }\n\n    public void sendRedisConfigTemplateChangeEmail(AppUser appUser, String versionName, InstanceConfig instanceConfig,\n                                                   SuccessEnum successEnum, RedisConfigTemplateChangeEnum redisConfigTemplateChangeEnum) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        String mailTitle = \"【CacheCloud】-Redis配置模板修改通知\";\n        String mailContent = String.format(\"%s 对 %s 配置模板 进行了%s,操作结果是%s,具体为(key=%s,value=%s,状态为%s)\",\n                appUser.getChName(), versionName,\n                redisConfigTemplateChangeEnum.getInfo(), successEnum.info(), instanceConfig.getConfigKey(),\n                instanceConfig.getConfigValue(), instanceConfig.getStatusDesc());\n        emailComponent.sendMail(mailTitle, mailContent.toString(), Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n\n    }\n\n    public void sendAddRedisVersionEmail(AppUser appUser, String versionName, SuccessEnum successEnum) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        String mailTitle = \"【CacheCloud】- 新增Redis版本通知\";\n        String mailContent = String.format(\"%s 新增Redis版本:%s ,状态为:%s)\", appUser.getChName(), versionName, successEnum.info());\n        emailComponent.sendMail(mailTitle, mailContent.toString(), Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n\n    }\n\n    public void sendSystemConfigDifEmail(AppUser appUser, Map<String, String> systemDifConfigMap,\n                                         SuccessEnum successEnum) {\n        if (EnvUtil.isDev(environment)) {\n            return;\n        }\n        if (MapUtils.isEmpty(systemDifConfigMap)) {\n            return;\n        }\n        String mailTitle = \"【CacheCloud】-系统配置修改通知\";\n        StringBuffer mailContent = new StringBuffer();\n        mailContent.append(appUser.getChName() + \"修改了系统配置，修改结果:\" + successEnum.info() + \"<br/>\");\n        mailContent.append(\"具体配置如下:<br/>\");\n        for (Entry<String, String> entry : systemDifConfigMap.entrySet()) {\n            mailContent.append(entry.getKey() + \"-->\" + entry.getValue() + \"<br/>\");\n        }\n        emailComponent.sendMail(mailTitle, mailContent.toString(), Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n    }\n\n    /**\n     * 系统通知\n     *\n     * @param noticeContent\n     * @return\n     */\n    public boolean noticeAllUser(String noticeContent) {\n        if (EnvUtil.isDev(environment)) {\n            return false;\n        }\n        if (StringUtils.isBlank(noticeContent)) {\n            return false;\n        }\n        try {\n            String mailTitle = \"【CacheCloud】-系统通知\";\n            StringBuffer mailContent = new StringBuffer();\n            String[] noticeArray = noticeContent.split(ConstUtils.NEXT_LINE);\n            for (String noticeLine : noticeArray) {\n                mailContent.append(noticeLine).append(\"<br/>\");\n            }\n            List<String> emailList = new ArrayList<String>();\n            List<AppUser> appUserList = userService.getUserList(null);\n            if (CollectionUtils.isEmpty(appUserList)) {\n                return false;\n            }\n            for (AppUser appUser : appUserList) {\n                String email = appUser.getEmail();\n                if (StringUtils.isBlank(email)) {\n                    continue;\n                }\n                emailList.add(email);\n            }\n            return emailComponent.sendMail(mailTitle, mailContent.toString(), emailList, Arrays.asList(emailComponent.getAdminEmail().split(ConstUtils.COMMA)));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/DateUtil.java",
    "content": "package com.sohu.cache.web.util;\n\nimport com.sohu.cache.entity.TimeBetween;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\n\n/**\n * @author leifu\n * @Time 2014年8月31日\n */\npublic class DateUtil {\n    private final static Logger logger = LoggerFactory.getLogger(DateUtil.class);\n    private final static String COLLECT_TIME_FORMAT = \"yyyyMMddHHmmss\";\n\n    /*\n     * yyyyMMddHHmm格式format\n     */\n    public static String formatYYYYMMddHHMM(Date date) {\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmm\");\n        return sdf.format(date);\n    }\n\n    /*\n     * yyyyMMddHHmmss格式format\n     */\n    public static String formatYYYYMMddHHMMss(Date date) {\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\n        return sdf.format(date);\n    }\n\n    /*\n     * yyyy-MM-dd HH:mm:ss格式format\n     */\n    public static String formatYYYYMMddHHMMSS(Date date) {\n        if (date == null) {\n            return null;\n        }\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        return sdf.format(date);\n    }\n\n    /*\n     * yyyyMMddHHmm格式parse\n     */\n    public static Date parse(String dateStr, String format) throws ParseException {\n        SimpleDateFormat sdf = new SimpleDateFormat(format);\n        return sdf.parse(dateStr);\n    }\n\n    /*\n     * yyyyMMddHHmm格式parse\n     */\n    public static Date parseYYYYMMddHHMM(String dateStr) throws ParseException {\n        return parse(dateStr, \"yyyyMMddHHmm\");\n    }\n\n    /**\n     * yyyyMMddHH格式parse\n     *\n     * @throws ParseException\n     */\n    public static Date parseYYYYMMddHH(String dateStr) throws ParseException {\n        return parse(dateStr, \"yyyyMMddHH\");\n    }\n\n\n    /*\n     * yyyy-MM-dd格式parse\n     */\n    public static Date parseYYYY_MM_dd(String dateStr) throws ParseException {\n        return parse(dateStr, \"yyyy-MM-dd\");\n    }\n\n    /**\n     * yyyyMMdd格式parse\n     */\n    public static Date parseYYYYMMdd(String dateStr) throws ParseException {\n        return parse(dateStr, \"yyyyMMdd\");\n    }\n\n\n    public static Date getDateByFormat(String date, String format) {\n        SimpleDateFormat sf = new SimpleDateFormat(format);\n        Date result = null;\n        try {\n            result = sf.parse(date);\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n        return result;\n    }\n\n    public static String formatDate(Date date, String format) {\n        SimpleDateFormat sf = new SimpleDateFormat(format);\n        return sf.format(date);\n    }\n\n\n    public static String formatYYYYMMdd(Date date) {\n        SimpleDateFormat sf = new SimpleDateFormat(\"yyyyMMdd\");\n        return sf.format(date);\n    }\n\n    public static String formatYYYY_MM_dd(Date date) {\n        SimpleDateFormat sf = new SimpleDateFormat(\"yyyy-MM-dd\");\n        return sf.format(date);\n    }\n\n    public static String formatHHMM(Date date) {\n        SimpleDateFormat sf = new SimpleDateFormat(\"HHmm\");\n        return sf.format(date);\n    }\n\n    public static TimeBetween fillWithDateFormat(String searchDate) throws ParseException {\n        final String dateFormat = \"yyyy-MM-dd\";\n        Date startDate;\n        Date endDate;\n        if (StringUtils.isBlank(searchDate)) {\n            // 如果为空默认取今天\n            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);\n            startDate = sdf.parse(sdf.format(new Date()));\n        } else {\n            startDate = com.sohu.cache.web.util.DateUtil.parse(searchDate, dateFormat);\n        }\n        endDate = DateUtils.addDays(startDate, 1);\n        // 查询后台需要\n        long startTime = NumberUtils.toLong(com.sohu.cache.web.util.DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n        long endTime = NumberUtils.toLong(com.sohu.cache.web.util.DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));\n        return new TimeBetween(startTime, endTime, startDate, endDate);\n    }\n\n    public static TimeBetween fillWithMinDateFormat(String searchDate, String dateFormat) throws ParseException {\n        Date startDate;\n        Date endDate;\n        if (StringUtils.isBlank(searchDate)) {\n            // 如果为空默认取今天\n            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);\n            startDate = sdf.parse(sdf.format(new Date()));\n        } else {\n            startDate = com.sohu.cache.web.util.DateUtil.parse(searchDate, dateFormat);\n        }\n        endDate = DateUtils.addMinutes(startDate, 1);\n        // 查询后台需要\n        long startTime = NumberUtils.toLong(com.sohu.cache.web.util.DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));\n        long endTime = NumberUtils.toLong(com.sohu.cache.web.util.DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));\n        return new TimeBetween(startTime, endTime, startDate, endDate);\n    }\n\n    public static Date customDateByHHMM(int hh, int mm){\n        Calendar calendar = Calendar.getInstance();\n        calendar.set(Calendar.HOUR_OF_DAY, hh);\n        calendar.set(Calendar.MINUTE, mm);\n        calendar.set(Calendar.SECOND, 0);\n        return calendar.getTime();\n    }\n\n    public static boolean timeLessDiffAfter(Date date1, Date date2, int diffSecond){\n        return (date2.getTime()/1000 > date1.getTime()/1000) && (date2.getTime() - date1.getTime()) / 1000 <= diffSecond;\n    }\n\n    public static boolean timeMoreDiffAfter(Date date1, Date date2, int diffSecond){\n        return (date2.getTime() - date1.getTime()) / 1000 >= diffSecond;\n    }\n\n    public static Date getBeginOfDay(Date date){\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTime(date);\n        calendar.set(Calendar.HOUR_OF_DAY, 0);\n        calendar.set(Calendar.MINUTE, 0);\n        calendar.set(Calendar.SECOND, 0);\n        calendar.set(Calendar.MILLISECOND, 0);\n        return calendar.getTime();\n    }\n\n    public static Date getBeginOfTomorrow(Date date){\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTime(date);\n        calendar.add(Calendar.DATE, 1);\n        calendar.set(Calendar.HOUR_OF_DAY, 0);\n        calendar.set(Calendar.MINUTE, 0);\n        calendar.set(Calendar.SECOND, 0);\n        calendar.set(Calendar.MILLISECOND, 0);\n        return calendar.getTime();\n    }\n\n    public static String getTimeBeforePointedMinute(Date date, int mm){\n        Date newDate = DateUtils.addMinutes(date, 0 - mm);\n        SimpleDateFormat sf = new SimpleDateFormat(\"yyyyMMddHHmm00\");\n        return sf.format(newDate);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/FreemakerUtils.java",
    "content": "package com.sohu.cache.web.util;\n\nimport freemarker.template.Configuration;\nimport freemarker.template.Template;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.ui.freemarker.FreeMarkerTemplateUtils;\n\nimport java.text.DecimalFormat;\nimport java.util.Map;\n\n/**\n * @Author: rucao\n * @Date: 2020/10/14 10:11\n */\npublic class FreemakerUtils {\n    protected static final Logger logger = LoggerFactory.getLogger(FreemakerUtils.class);\n\n    public synchronized static String createText(String templateName, Configuration configuration, Map<String, Object> model) {\n        if (configuration != null) {\n            try {\n                Template template = configuration.getTemplate(templateName);\n                model.put(\"ccDomain\", IpUtil.domain);\n                model.put(\"decimalFormat\", new DecimalFormat(\"###,###\"));\n                String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);\n                return html;\n            } catch (Exception e) {\n                logger.error(e.getLocalizedMessage(), e);\n            }\n        }\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/IpUtil.java",
    "content": "package com.sohu.cache.web.util;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.beans.factory.annotation.Value;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport javax.servlet.http.HttpServletRequest;\r\nimport java.net.InetAddress;\r\nimport java.net.NetworkInterface;\r\nimport java.util.Enumeration;\r\n\r\n/**\r\n * ip工具\r\n *\r\n * @author leifu\r\n */\r\n@Component\r\npublic class IpUtil {\r\n\r\n    public static String domain;\r\n\r\n    @Value(\"${server.port}\")\r\n    private int port;\r\n\r\n    public static Logger logger = LoggerFactory.getLogger(IpUtil.class);\r\n\r\n    public static String getIpAddr(HttpServletRequest request) {\r\n        String ip = request.getHeader(\"x-forwarded-for\");\r\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\r\n            ip = request.getHeader(\"Proxy-Client-IP\");\r\n        }\r\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\r\n            ip = request.getHeader(\"WL-Proxy-Client-IP\");\r\n        }\r\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\r\n            ip = request.getRemoteAddr();\r\n        }\r\n        return ip;\r\n    }\r\n\r\n    public int getLocalPort() {\r\n        return port;\r\n    }\r\n\r\n    public String getLocalIP() {\r\n        InetAddress inetAddress = getLocalHostLANAddress();\r\n        return inetAddress.getHostAddress();\r\n    }\r\n\r\n    public String getCurrentIpPort() {\r\n        return getLocalIP() + \":\" + port;\r\n    }\r\n\r\n    public InetAddress getLocalHostLANAddress() {\r\n        try {\r\n            InetAddress candidateAddress = null;\r\n            // 遍历所有的网络接口\r\n            for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {\r\n                NetworkInterface iface = (NetworkInterface) ifaces.nextElement();\r\n                // 所有的接口下遍历IP\r\n                for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {\r\n                    InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();\r\n                    if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址\r\n                        if (inetAddr.isSiteLocalAddress()) {\r\n                            // 优先site-local地址\r\n                            return inetAddr;\r\n                        } else if (candidateAddress == null) {\r\n                            candidateAddress = inetAddr;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            if (candidateAddress != null) {\r\n                return candidateAddress;\r\n            }\r\n            // 如果没有发现 non-loopback地址.用次选方案\r\n            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();\r\n            return jdkSuppliedAddress;\r\n        } catch (Exception e) {\r\n            logger.error(e.getMessage(), e);\r\n        }\r\n        return null;\r\n    }\r\n\r\n    @Value(\"${server.domain}\")\r\n    public void setDomain(String ccdomain){\r\n        domain = ccdomain;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/Page.java",
    "content": "package com.sohu.cache.web.util;\r\n\r\n/**\r\n * 分页对象\r\n * @author leifu\r\n * @Date 2015年2月10日\r\n * @Time 下午6:38:18\r\n */\r\npublic class Page implements java.io.Serializable {\r\n    private static final long serialVersionUID = 7887139614696114877L;\r\n    \r\n\r\n    /**\r\n     * 当前页数\r\n     */\r\n    private int pageNo;\r\n    \r\n    /**\r\n     * 每页的记录数\r\n     */\r\n    private int pageSize;\r\n    \r\n    /**\r\n     * 总记录数\r\n     */\r\n    private int totalCount;\r\n    \r\n    public Page(int pageNo, int pageSize, int totalCount) {\r\n        this.pageNo = pageNo;\r\n        this.pageSize = pageSize;\r\n        this.totalCount = totalCount;\r\n    }\r\n\r\n    /**\r\n     * 取总页数\r\n     */\r\n    public int getTotalPages() {\r\n        if (totalCount % pageSize == 0)\r\n            return totalCount / pageSize;\r\n        else\r\n            return totalCount / pageSize + 1;\r\n    }\r\n\r\n    /**\r\n     * 获取任一页第一条数据的位置,startIndex从0开始\r\n     */\r\n    public int getStart() {\r\n        return (pageNo - 1) * pageSize;\r\n    }\r\n    \r\n    public int getNumberOfPages() {\r\n        int totalPageCount = getTotalPages();\r\n        return totalPageCount >= 10 ? 10 : totalPageCount;\r\n    }\r\n\r\n    public int getPageNo() {\r\n        return pageNo;\r\n    }\r\n\r\n    public void setPageNo(int pageNo) {\r\n        this.pageNo = pageNo;\r\n    }\r\n\r\n    public int getPageSize() {\r\n        return pageSize;\r\n    }\r\n\r\n    public void setPageSize(int pageSize) {\r\n        this.pageSize = pageSize;\r\n    }\r\n\r\n    public int getTotalCount() {\r\n        return totalCount;\r\n    }\r\n\r\n    public void setTotalCount(int totalCount) {\r\n        this.totalCount = totalCount;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/SimpleFileUtil.java",
    "content": "package com.sohu.cache.web.util;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.sohu.cache.web.controller.AppController;\r\n\r\n/**\r\n * 简单文件读取\r\n * @author leifu\r\n * @Date 2015年3月2日\r\n * @Time 下午2:12:15\r\n */\r\npublic class SimpleFileUtil {\r\n    private static final Logger logger = LoggerFactory.getLogger(SimpleFileUtil.class);\r\n\r\n    /**\r\n     * 从class环境读取文件成List<String>\r\n     * @param fileName\r\n     * @return\r\n     */\r\n    public static List<String> getListFromFile(String fileName, String encoding) {\r\n        List<String> list = new ArrayList<String>();\r\n\r\n        InputStream is = null;\r\n        BufferedReader br = null;\r\n        try {\r\n            is = AppController.class.getClassLoader().getResourceAsStream(fileName);\r\n            br = new BufferedReader(new InputStreamReader(is, encoding));\r\n            String line = null;\r\n            while ((line = br.readLine()) != null) {\r\n                list.add(line);\r\n            }\r\n        } catch (IOException e) {\r\n            logger.error(e.getMessage(), e);\r\n        } finally {\r\n            if (is != null) {\r\n                try {\r\n                    is.close();\r\n                } catch (IOException e) {\r\n                    logger.error(e.getMessage(), e);\r\n                }\r\n            }\r\n            if (br != null) {\r\n                try {\r\n                    br.close();\r\n                } catch (IOException e) {\r\n                    logger.error(e.getMessage(), e);\r\n                }\r\n            }\r\n        }\r\n        return list;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/util/WebUtil.java",
    "content": "package com.sohu.cache.web.util;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.web.util.WebUtils;\n\nimport javax.servlet.ServletRequest;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\n/**\n * web相关工具\n * @Description: \n * @author yongfeigao\n * @date 2018年6月12日\n */\npublic class WebUtil {\n\n    public static final String LOGIN_USER_STATUS_NAME = \"CACHE_CLOUD_USER_STATUS\";\n\n    /**\n     * 从request中获取客户端ip\n     * \n     * @param request\n     * @return\n     */\n    public static String getIp(ServletRequest request) {\n        HttpServletRequest req = (HttpServletRequest) request;\n        String addr = getHeaderValue(req, \"X-Forwarded-For\");\n        if (StringUtils.isNotEmpty(addr) && addr.contains(\",\")) {\n            addr = addr.split(\",\")[0];\n        }\n        if (StringUtils.isEmpty(addr)) {\n            addr = getHeaderValue(req, \"X-Real-IP\");\n        }\n        if (StringUtils.isEmpty(addr)) {\n            addr = req.getRemoteAddr();\n        }\n        return addr;\n    }\n    \n    /**\n     * 获取请求的完整url\n     * @param request\n     * @return\n     */\n    public static String getUrl(HttpServletRequest request) {\n        String url = request.getRequestURL().toString();\n        String queryString = request.getQueryString();\n        if(queryString != null) {\n            url += \"?\" + request.getQueryString();\n        }\n        return url;\n    }\n    \n    /**\n     * 获取ServletRequest header value\n     * @param request\n     * @param name\n     * @return\n     */\n    public static String getHeaderValue(HttpServletRequest request, String name) {\n        String v = request.getHeader(name);\n        if(v == null) {\n            return null;\n        }\n        return v.trim();\n    }\n    \n    /**\n     * 从request属性中获取对象\n     * @param request\n     * @return\n     */\n    public static void setEmailAttribute(ServletRequest request, String email) {\n        request.setAttribute(\"email\", email);\n    }\n    \n    /**\n     * 从request属性中获取对象\n     * @param request\n     * @return\n     */\n    public static String getEmailAttribute(ServletRequest request) {\n        Object email = request.getAttribute(\"email\");\n        if(email == null) {\n            return null;\n        }\n        return email.toString();\n    }\n    \n    /**\n     * 从request属性中获取对象\n     * @param request\n     * @return\n     */\n    public static void setAttribute(ServletRequest request, String name, Object obj) {\n        request.setAttribute(name, obj);\n    }\n    \n    /**\n     * 设置对象到request属性中\n     * @param request\n     * @return\n     */\n    public static Object getAttribute(ServletRequest request, String name) {\n        return request.getAttribute(name);\n    }\n    \n    /**\n     * 输出内容到页面\n     * @param response\n     * @param result\n     * @throws IOException\n     */\n    public static void print(HttpServletResponse response, String result) throws IOException {\n        response.setContentType(\"text/html;charset=UTF-8\");\n        PrintWriter out = response.getWriter();\n        out.print(result);\n        out.flush();\n        out.close();\n        out = null;\n    }\n    \n    /**\n     * 获取登录的cookie的值\n     * \n     * @param request\n     * @return\n     */\n    public static String getLoginCookieValue(HttpServletRequest request) {\n        Cookie cookie = WebUtils.getCookie(request, LOGIN_USER_STATUS_NAME);\n        if(cookie != null) {\n            return cookie.getValue();\n        }\n        return null;\n    }\n    \n    /**\n     * 获取登录的cookie\n     * \n     * @param request\n     * @return\n     */\n    public static Cookie getLoginCookie(HttpServletRequest request) {\n        return WebUtils.getCookie(request, LOGIN_USER_STATUS_NAME);\n    }\n\n    /**\n     * 设置登录的cookie\n     */\n    public static void setLoginCookie(HttpServletResponse response, String value) {\n        Cookie cookie = new Cookie(LOGIN_USER_STATUS_NAME, value);\n        cookie.setPath(\"/\");\n        response.addCookie(cookie);\n    }\n\n    /**\n     * 移除登录的cookie\n     */\n    public static void deleteLoginCookie(HttpServletResponse response) {\n        Cookie cookie = new Cookie(LOGIN_USER_STATUS_NAME, \"\");\n        cookie.setPath(\"/\");\n        cookie.setMaxAge(0);\n        response.addCookie(cookie);\n    }\n    \n    /**\n     * 跳转\n     * @param response\n     * @param request\n     * @param path\n     * @throws IOException \n     */\n    public static void redirect(HttpServletResponse response, HttpServletRequest request, String path) throws IOException {\n        response.sendRedirect(request.getContextPath() + path);\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AlertConfig.java",
    "content": "package com.sohu.cache.web.vo;\n\npublic class AlertConfig {\n\n    private String value;\n\n    private String info;\n\n    public String getValue() {\n        return value;\n    }\n\n    public void setValue(String value) {\n        this.value = value;\n    }\n\n    public String getInfo() {\n        return info;\n    }\n\n    public void setInfo(String info) {\n            this.info = info;\n        }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((value == null) ? 0 : value.hashCode());\n        result = prime * result + ((info == null) ? 0 : info.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        AlertConfig other = (AlertConfig) obj;\n        if (value == null) {\n            if (other.value != null){\n                return false;\n            }\n        } else if (!value.equals(other.value)){\n            return false;\n        }\n        if (info == null) {\n            if (other.info != null){\n                return false;\n            }\n        } else if (!info.equals(other.info)){\n            return false;\n        }\n        return true;\n    }\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AppDetailVO.java",
    "content": "package com.sohu.cache.web.vo;\r\rimport java.util.ArrayList;\rimport java.util.List;\r\rimport lombok.Data;\rimport org.apache.commons.collections.CollectionUtils;\rimport org.apache.commons.lang.StringUtils;\r\rimport com.sohu.cache.entity.AppDesc;\rimport com.sohu.cache.entity.AppUser;\r\r/**\r * 应用详情\r * @author leifu\r * @Time 2014年8月29日\r */\r@Data\rpublic class AppDetailVO {\r\r    private AppDesc appDesc;\r\r    /**\r     * 内存空间\r     */\r    private long mem;\r\r    /**\r     * 当前内存\r     */\r    private long currentMem;\r\r    /**\r     * 机器数\r     */\r    private int machineNum;\r\r    /**\r     * 主节点数\r     */\r    private int masterNum;\r\r    /**\r     * 从节点数\r     */\r    private int slaveNum;\r\r    /**\r     * 哨兵节点数\r     */\r    private int sentinelNum;\r\r    /**\r     * 当前对象数\r     */\r    private long currentObjNum;\r    \r    /**\r     * 当前连接数\r     */\r    private int conn;\r\r    /**\r     * 内存使用报警\r     */\r    private double memUseThreshold;\r\r    /**\r     * 命中率使用报警\r     */\r    private double hitPercentThreshold;\r\r    /**\r     * 内存使用率\r     */\r    private double memUsePercent;\r\r    /**\r     * 最大碎片率\r     */\r    private double highestMemFragRatio;\r\r    /**\r     * 最大碎片率的实例id\r     */\r    private long instIdWithHighestMemFragRatio;\r\r    /**\r     * 命中率\r     */\r    private double hitPercent;\r    \r    /**\r     * 应用授权的用户\r     */\r    private List<AppUser> appUsers;\r\r    /**\r     * 应用报警的用户\r     */\r    private List<AppUser> alertUsers;\r\r    /**\r     * 当前磁盘占用\r     */\r    private long currentDisk;\r\r    /**\r     * 磁盘使用率\r     */\r    private double diskUsePercent;\r\r    public List<String> getPhoneList(){\r\t    List<String> phoneList = new ArrayList<String>();\r\t    if(CollectionUtils.isNotEmpty(appUsers)){\r\t        for(AppUser appUser : appUsers){\r\t            String mobile = appUser.getMobile();\r\t            if(StringUtils.isNotBlank(mobile)){\r\t                phoneList.add(appUser.getMobile());\r\t            }\r\t        }\r\t    }\r\t    return phoneList;\r\t}\r\r    public List<String> getWeChatList(){\r        List<String> weChatList = new ArrayList<String>();\r        if(CollectionUtils.isNotEmpty(alertUsers)){\r            for(AppUser appUser : alertUsers){\r                String weChat = appUser.getWeChat();\r                if(StringUtils.isNotBlank(weChat)){\r                    weChatList.add(appUser.getWeChat());\r                }\r            }\r        }\r        return weChatList;\r    }\r\r\tpublic List<String> getEmailList(){\r        List<String> emailList = new ArrayList<String>();\r        if(CollectionUtils.isNotEmpty(alertUsers)){\r            for(AppUser appUser : alertUsers){\r                String email = appUser.getEmail();\r                if(StringUtils.isNotBlank(email)){\r                    emailList.add(appUser.getEmail());\r                }\r            }\r        }\r        return emailList;\r    }\r}\r"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AppRedisCommandCheckResult.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: 应用redis 命令校验结果\n */\n@Data\npublic class AppRedisCommandCheckResult implements Serializable {\n\n    private Date createTime;\n\n    private String createTimeStr;\n\n    /**\n     * 宿主机ip\n     */\n    private String machineIps;\n\n    /**\n     * pod ip\n     */\n    private String podIp;\n\n    /**\n     * 命令\n     */\n    private String command;\n\n    /**\n     * 配置项\n     */\n    private List<InstanceRedisCommandCheckResult> instanceCheckList;\n\n    /**\n     * redis版本\n     */\n    private boolean success;\n\n    public String getCreateTimeStr(){\n        return DateUtil.formatYYYYMMddHHMMSS(createTime);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AppRedisCommandCheckVo.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/29 11:11\n * @Description: 应用redis 配置校验\n */\n@Data\npublic class AppRedisCommandCheckVo {\n\n    /**\n     * 宿主机ip\n     */\n    private String machineIps;\n\n    /**\n     * pod ip\n     */\n    private String podIp;\n\n    /**\n     * 命令\n     */\n    private String command;\n\n    /**\n     * 检测方式\n     */\n    private Integer checkType;\n\n    /**\n     * info检测\n     * info一级指标项\n     */\n    private String infoIndicate;\n\n    /**\n     * info检测\n     * 配置项\n     */\n    private String indicateName;\n\n\n    /**\n     * info检测\n     * 最大重试次数\n     */\n    private Integer maxTry = 40;\n\n    /**\n     * 比较值\n     */\n    private String expectValue;\n\n    /**\n     * log检测\n     * 允许的log距离当前时间的范围，以分钟计（如10分钟以内）\n     */\n    private Integer minuteInternal = 10;\n\n    public Integer getMaxTry(){\n        if(maxTry == null){\n            return 40;\n        }\n        return maxTry;\n    }\n\n    public Integer getMinuteInternal(){\n        if(minuteInternal == null){\n            return 10;\n        }\n        return minuteInternal;\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AppRedisConfigCheckResult.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.entity.AppDesc;\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.Data;\nimport org.apache.commons.collections.CollectionUtils;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: 应用redis 配置校验\n */\n@Data\npublic class AppRedisConfigCheckResult implements Serializable {\n\n    /**\n     * 检测时间\n     */\n    private Date createTime;\n\n    /**\n     * redis版本\n     */\n    private Integer versionId;\n\n    /**\n     * 应用id\n     */\n    private AppDesc appDesc;\n\n    /**\n     * 配置项\n     */\n    private String configName;\n\n    /**\n     * 比较类型\n     */\n    private int compareType;\n\n    /**\n     * 比较值\n     */\n    private String expectValue;\n\n    /**\n     * 配置项\n     */\n    private List<InstanceRedisConfigCheckResult> instanceCheckList;\n\n    /**\n     * redis版本\n     */\n    private boolean success;\n\n    private Long appId;\n\n    private String createTimeStr;\n\n    public Long getAppId(){\n        if(appDesc != null){\n            return appDesc.getAppId();\n        }\n        return null;\n    }\n\n    private String instanceIds;\n\n    public String getInstanceIds(){\n        final StringBuilder sb = new StringBuilder();\n        if(CollectionUtils.isNotEmpty(instanceCheckList)){\n            instanceCheckList.forEach(instanceCheckResult -> {\n                if(!instanceCheckResult.isSuccess()){\n                    sb.append(instanceCheckResult.getInstanceInfo().getId());\n                    sb.append(\",\");\n                }\n            });\n            if(sb.length() > 1){\n                sb.deleteCharAt(sb.length() - 1);\n            }\n        }\n        return sb.toString();\n    }\n\n    public String getCreateTimeStr(){\n        return DateUtil.formatYYYYMMddHHMMSS(createTime);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AppRedisConfigCheckVo.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: 应用redis 配置校验\n */\n@Data\npublic class AppRedisConfigCheckVo {\n\n    /**\n     * 应用id\n     */\n    private Long appId;\n\n    /**\n     * redis版本\n     */\n    private Integer versionId;\n\n    /**\n     * 配置项\n     */\n    private String configName;\n\n    /**\n     * 比较类型\n     */\n    private int compareType;\n\n    /**\n     * 比较值\n     */\n    private String expectValue;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AppRedisConfigVo.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: 应用redis 配置参数名和值\n */\n@Data\npublic class AppRedisConfigVo {\n\n    /**\n     * 应用id\n     */\n    private Long appId;\n\n    /**\n     * 修改配置、重启日志记录id\n     */\n    private Long recordId;\n\n    /**\n     * 是否更新配置\n     */\n    private boolean configFlag;\n\n    /**\n     * 是否允许主从切换\n     */\n    private boolean transferFlag;\n\n    /**\n     * 操作的实例\n     */\n    private List<Integer> instanceList;\n\n    /**\n     * 配置名称\n     */\n    private List<RedisConfigVo> configList;\n\n    /**\n     * 是否快速完成滚动重启（修改配置，滚动重启）\n     * 如果为是，则在操作每个主从分组中，不再sleep等待（等待的目的是：客户端刷新集群拓扑）\n     */\n    private boolean quickFinishFlag;\n\n    public List<Integer> getInstanceList() {\n        if(CollectionUtils.isEmpty(instanceList)){\n            return null;\n        }\n        for (Integer instanceId : instanceList) {\n            if(instanceId == 0){\n                return null;\n            }\n        }\n        return instanceList;\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/AppUserVo.java",
    "content": "package com.sohu.cache.web.vo;\r\rimport com.sohu.cache.entity.AppUser;\rimport lombok.Data;\r\r/**\r * 用户及所属业务组\r * @author zengyizhao\r * @Time 2023年1月17日\r */\r@Data\rpublic class AppUserVo extends AppUser{\r\r    /**\r     * 业务组名称\r     */\r    private String bizName;\r\r    /**\r     * 业务组描述\r     */\r    private String bizDesc;\r\r}\r"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/ExecuteResult.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/17 16:13\n * @Description: 执行结果及信息提示\n */\n@Data\npublic class ExecuteResult {\n\n    /**\n     * 是否成功\n     */\n    private boolean success;\n\n    /**\n     * 操作结果提示\n     */\n    private String message;\n\n    public ExecuteResult(){\n\n    }\n\n    public ExecuteResult(boolean success, String message) {\n        this.success = success;\n        this.message = message;\n    }\n\n    public static ExecuteResult ok(){\n        return new ExecuteResult(true, null);\n    }\n\n    public static <T> ExecuteResult ok(String message){\n        return new ExecuteResult(true, message);\n    }\n\n    public static ExecuteResult error(){\n        return new ExecuteResult(false, null);\n    }\n\n    public static ExecuteResult error(String message){\n        return new ExecuteResult(false, message);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/GeneralResponse.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/17 16:13\n * @Description: 返回结果类\n */\n@Data\npublic class GeneralResponse<T> {\n\n    /**\n     * 是否成功\n     */\n    private boolean success;\n\n    /**\n     * 操作状态码\n     */\n    private int status;\n\n    /**\n     * 操作结果提示\n     */\n    private String error;\n\n    /**\n     * 对象结果\n     */\n    private T data;\n\n    public GeneralResponse(){\n\n    }\n\n    public GeneralResponse(boolean success, int status, String error, T data) {\n        this.success = success;\n        this.status = status;\n        this.error = error;\n        this.data = data;\n    }\n\n    public static GeneralResponse ok(){\n        return new GeneralResponse(true, 200, null, null);\n    }\n\n    public static <T> GeneralResponse ok(T data){\n        return new GeneralResponse(true, 200, null, data);\n    }\n\n    public static GeneralResponse error(int status, String error){\n        return new GeneralResponse(false, status, error, null);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/InstanceRedisCommandCheckResult.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.entity.InstanceInfo;\nimport lombok.Data;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: 应用redis 命令校验结果\n */\n@Data\npublic class InstanceRedisCommandCheckResult {\n\n    /**\n     * 校验结果\n     */\n    private boolean success;\n\n    /**\n     * 错误信息\n     */\n    private String message;\n\n    /**\n     * 实例信息\n     */\n    private InstanceInfo instanceInfo;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/InstanceRedisConfigCheckResult.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.entity.InstanceInfo;\nimport lombok.Data;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: 应用redis 配置校验\n */\n@Data\npublic class InstanceRedisConfigCheckResult {\n\n    /**\n     * 校验结果\n     */\n    private boolean success;\n\n    /**\n     * 配置项\n     */\n    private String configName;\n\n    /**\n     * 配置预期值\n     */\n    private String expectValue;\n\n    /**\n     * 实际值\n     */\n    private String realValue;\n\n    /**\n     * 实例信息\n     */\n    private InstanceInfo instanceInfo;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/MachineEnv.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.web.enums.CheckEnum;\nimport lombok.Data;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n\n/**\n * Created by chenshi on 2021/1/12.\n */\n@Data\npublic class MachineEnv {\n\n    private static Logger logger = LoggerFactory.getLogger(MachineEnv.class);\n    /**\n     * 容器环境参数\n     */\n    //内存分配策略设置为1:表示内核允许分配所有的物理内存\n    private String overcommit_memory;\n    private final static String overcommit_memory_warnning = \"1\";\n    //swap策略设置为0：关闭swap，避免内存io转换磁盘io导致阻塞\n    private String swappines = \"0\";\n    private final static String swappines_warnning = \"0\";\n    //thp设置：never,防止fork过程中消耗大内存拷贝导致阻塞\n    private String transparent_hugepage_enable;\n    private final static String transparent_hugepage_enable_warnning = \"always madvise [never]\";\n    private String transparent_hugepage_defrag;\n    private final static String transparent_hugepage_defrag_warnning = \"always defer madvise [never]\";\n    private final static String thp_judge = \"[never]\";\n    //nproc用户线程数: * soft nproc 4096\n    private String nproc;\n    private final static String nproc_warnning = \"*          soft    nproc     4096\";\n\n    /**\n     * 宿主环境参数\n     */\n    //tcp连接队列数:512\n    private String somaxconn;\n    private final static String somaxconn_warnning = \"511\";\n    private final static int somaxconn_less_warnning = 511;\n    //redis fsync slow log\n    private int fsync_delay_times;\n    private final static int fsync_delay_times_warnning = 10;\n    //cachecloud用户最大线程数量,当前宿主环境为4096\n    private int nproc_threads = 1024;\n    private final static int nproc_threads_warnning = 1024;\n    // ssh pass版本\n    private String sshPass;\n    private final static String sshPass_warnning = \"\";\n    // 文件句柄数量\n    private int unlimit_used;\n    private int unlimit;\n    private final static int unlimit_warnning = 40000;\n    // 磁盘信息\n    private String diskUsed;\n    private final static int diskUsed_warnning = 80;\n    // 实例数量\n    private int instanceNum;\n\n    /**\n     * 容器资源\n     */\n    public MachineEnv(String overcommit_memory, String swappines, String transparent_hugepage_enable, String transparent_hugepage_defrag, String nproc) {\n        this.overcommit_memory = overcommit_memory;\n        this.swappines = swappines;\n        this.transparent_hugepage_enable = transparent_hugepage_enable;\n        this.transparent_hugepage_defrag = transparent_hugepage_defrag;\n        this.nproc = nproc;\n    }\n\n    /**\n     * 宿主资源\n     */\n    public MachineEnv(String somaxconn, int fsync_delay_times, int nproc_threads, String sshPass, int unlimit_used, int unlimit, String diskUsed, int instanceNum) {\n        this.somaxconn = somaxconn;\n        this.fsync_delay_times = fsync_delay_times;\n        this.nproc_threads = nproc_threads;\n        this.sshPass = sshPass;\n        this.unlimit_used = unlimit_used;\n        this.unlimit = unlimit;\n        this.diskUsed = diskUsed;\n        this.instanceNum = instanceNum;\n    }\n\n    public static MachineEnv getDefaultEnv() {\n       return new MachineEnv(\"-1\",\"-1\",\"-1\",\"-1\",\"-1\",\"-1\",-1,-1,\"-1\",-1,-1,\"-1\",-1);\n    }\n\n    public MachineEnv(String overcommit_memory, String swappines, String transparent_hugepage_enable, String transparent_hugepage_defrag, String nproc, String somaxconn, int fsync_delay_times, int nproc_threads, String sshPass, int unlimit_used, int unlimit, String diskUsed, int instanceNum) {\n        this.overcommit_memory = overcommit_memory;\n        this.swappines = swappines;\n        this.transparent_hugepage_enable = transparent_hugepage_enable;\n        this.transparent_hugepage_defrag = transparent_hugepage_defrag;\n        this.nproc = nproc;\n        this.somaxconn = somaxconn;\n        this.fsync_delay_times = fsync_delay_times;\n        this.nproc_threads = nproc_threads;\n        this.sshPass = sshPass;\n        this.unlimit_used = unlimit_used;\n        this.unlimit = unlimit;\n        this.diskUsed = diskUsed;\n        this.instanceNum = instanceNum;\n    }\n\n    public static int checkContainer(MachineEnv env) {\n\n        try {\n            if (env.overcommit_memory.equals(overcommit_memory_warnning) && env.transparent_hugepage_enable.contains(thp_judge) &&\n                    env.transparent_hugepage_defrag.contains(thp_judge) && env.swappines.equals(swappines_warnning) &&\n                    env.nproc.equals(nproc_warnning)) {\n                return CheckEnum.CONSISTENCE.getValue();\n            }\n            return CheckEnum.INCONSISTENCE.getValue();\n        } catch (Exception e) {\n            logger.error(\"MachineEnvUtil checkContainer error  env:{} {}\", env, e.getMessage());\n            return CheckEnum.EXCEPTION.getValue();\n        }\n    }\n\n    public static int checkHost(MachineEnv env) {\n        try {\n            int diskuse_precent = 0;\n            try {\n                diskuse_precent = Integer.parseInt(env.diskUsed.split(\"%\")[0]);\n            } catch (Exception e) {\n                logger.error(\"disk used {} parse error:{}\", env.diskUsed,e.getMessage());\n            }\n\n            if (env.nproc_threads <= nproc_threads_warnning && env.fsync_delay_times <= fsync_delay_times_warnning &&\n                    env.somaxconn.equals(somaxconn_warnning) && env.unlimit_used < unlimit_warnning && diskuse_precent < diskUsed_warnning) {\n                return CheckEnum.CONSISTENCE.getValue();\n            }\n            return CheckEnum.INCONSISTENCE.getValue();\n        } catch (Exception e) {\n            logger.error(\"MachineEnvUtil checkMachine error  env:{} {}\", env, e.getMessage(), e);\n            return CheckEnum.EXCEPTION.getValue();\n        }\n    }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/MachineStatsVo.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\n@Data\npublic class MachineStatsVo {\n\n    private String machineRoom;\n\n    private long totalMachineMem;\n\n    private long totalMachineFreeMem;\n\n    private long totalInstanceMaxMem;\n\n    private long totalInstanceUsedMem;\n\n    private long totalMachineDisk;\n\n    private long totalMachineFreeDisk;\n\n    private long totalInstanceApplyDisk;\n\n    private long totalInstanceUsedDisk;\n\n    private double machineDiskUsedRatio;\n\n    private double instanceMemUsedRatio;\n\n    private double instanceDiskUsedRatio;\n\n    public double getMachineMemUsedRatio() {\n        if (totalMachineMem == 0) {\n            return 0;\n        }\n        return (totalMachineMem - totalMachineFreeMem) * 100.0 / totalMachineMem * 1.0;\n    }\n\n    public double getMachineDiskUsedRatio() {\n        return (totalMachineDisk - totalMachineFreeDisk) * 100.0 / totalMachineDisk * 1.0;\n    }\n\n    public double getInstanceMemUsedRatio() {\n        if (totalInstanceMaxMem == 0) {\n            return 0;\n        }\n        return totalInstanceUsedMem * 100.0 / totalInstanceMaxMem * 1.0;\n    }\n\n    public double getInstanceDiskUsedRatio() {\n        if (totalInstanceApplyDisk == 0) {\n            return 0;\n        }\n        return (totalInstanceUsedDisk) * 100.0 / (totalInstanceApplyDisk * 1024 * 1024 * 1.0);\n    }\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/MasterSlaveGroupBo.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.entity.InstanceInfo;\nimport com.sohu.cache.web.enums.MasterSlaveExistEnum;\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/10/20 18:55\n * @Description: 主从分组辅助类\n */\n@Data\npublic class MasterSlaveGroupBo {\n\n    private InstanceInfo master;\n\n    private List<InstanceInfo> slaveList;\n\n    private MasterSlaveExistEnum masterSlaveExistEnum;\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/RedisClusterNode.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @CreateTime: 2023/2/27 18:43\n * @Description: redis node信息（cluster node）\n * @Version: 1.0\n */\n@Data\npublic class RedisClusterNode {\n\n    private String hostPort;\n\n    private String role;\n\n    private boolean fail;\n\n    private boolean connected;\n\n    private List<Integer> slots;\n\n    private String masterHostPort;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/RedisCommandCheckResult.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: 应用redis 命令校验结果\n */\n@Data\npublic class RedisCommandCheckResult implements Serializable {\n\n    private Date createTime;\n\n    private String createTimeStr;\n\n    /**\n     * 宿主机ip\n     */\n    private String machineIps;\n\n    /**\n     * pod ip\n     */\n    private String podIp;\n\n    /**\n     * 命令\n     */\n    private String command;\n\n    /**\n     * 配置项\n     */\n    private String key;\n\n    /**\n     * 操作人\n     */\n    private String userName;\n\n    /**\n     * redis版本\n     */\n    private boolean success;\n\n    public String getCreateTimeStr(){\n        return DateUtil.formatYYYYMMddHHMMSS(createTime);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/RedisConfigCheckResult.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport com.sohu.cache.web.util.DateUtil;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: redis 配置校验结果\n */\n@Data\npublic class RedisConfigCheckResult implements Serializable {\n\n    /**\n     * 检测时间\n     */\n    private Date createTime;\n\n    /**\n     * redis版本\n     */\n    private Integer versionId;\n\n    /**\n     * 配置项\n     */\n    private String configName;\n\n    /**\n     * 比较类型\n     */\n    private int compareType;\n\n    /**\n     * 比较值\n     */\n    private String expectValue;\n\n    /**\n     * 比较值\n     */\n    private String userName;\n\n    /**\n     * 配置项\n     */\n    private String key;\n\n    /**\n     * redis版本\n     */\n    private boolean success;\n\n    private String createTimeStr;\n\n    public String getCreateTimeStr(){\n        return DateUtil.formatYYYYMMddHHMMSS(createTime);\n    }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/RedisConfigVo.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\n/**\n * @Author: zengyizhao\n * @DateTime: 2021/9/14 11:11\n * @Description: redis 配置参数名和值\n */\n@Data\npublic class RedisConfigVo {\n\n    /**\n     * 配置名称\n     */\n    private String configName;\n\n    /**\n     * 配置值\n     */\n    private String configValue;\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/RedisInfo.java",
    "content": "package com.sohu.cache.web.vo;\n\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * <p>\n * Description: Redis 主-从关系 vo\n * </p>\n *\n * @author chenshi\n * @version 1.0\n * @date 2018/9/21\n */\n@Data\npublic class RedisInfo {\n\n    private int sid;\n\n    private String ip;\n\n    private int port;\n\n    private String version;\n\n    private String role;\n\n    List<RedisInfo> redisInfoList;\n\n    public RedisInfo(String ip, int port) {\n        this.ip = ip;\n        this.port = port;\n    }\n\n    public RedisInfo(String ip, int port, String version, String role) {\n        this.ip = ip;\n        this.port = port;\n        this.version = version;\n        this.role = role;\n    }\n\n    public RedisInfo(int sid, String ip, int port, String version, String role) {\n        this.sid = sid;\n        this.ip = ip;\n        this.port = port;\n        this.version = version;\n        this.role = role;\n    }\n\n    public RedisInfo(String ip, String role) {\n        this.ip = ip;\n        this.role = role;\n    }\n\n    public RedisInfo(String ip, int port, String version, String role, List<RedisInfo> redisInfoList) {\n        this.ip = ip;\n        this.port = port;\n        this.version = version;\n        this.role = role;\n        this.redisInfoList = redisInfoList;\n    }\n\n    public String getIpAndPortInfo(RedisInfo redisInfo) {\n        return redisInfo.getIp() + \":\" + redisInfo.getPort();\n    }\n\n    public String getRedisInfo(RedisInfo redisInfo) {\n        if (redisInfo.getPort() == 0) {\n            return redisInfo.getIp() + \":xxxx \" + redisInfo.getRole() + \" \\n\";\n        }\n        return redisInfo.getIp() + \":\" + redisInfo.getPort() + \" \" + redisInfo.getRole() + \" \\n\";\n    }\n\n    public String getInfo(RedisInfo redisInfo) {\n        return redisInfo.getIp() + \":\" + redisInfo.getPort() + \" \" + redisInfo.getRole() + \" \" + redisInfo.getVersion() + \" \\n\";\n    }\n\n}\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/java/com/sohu/cache/web/vo/RedisSlowLog.java",
    "content": "package com.sohu.cache.web.vo;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n\r\n/**\r\n * Created by yijunzhang on 14-10-14.\r\n */\r\n@Data\r\npublic class RedisSlowLog {\r\n\r\n    /**\r\n     * 慢查询id\r\n     */\r\n    private long id;\r\n\r\n    /**\r\n     * 执行时间点\r\n     */\r\n    private String timeStamp;\r\n\r\n    /**\r\n     * 慢查询执行时间(微秒)\r\n     */\r\n    private long executionTime;\r\n\r\n    private String command;\r\n\r\n    /**\r\n     * 执行日期时间\r\n     */\r\n    private Date date;\r\n\r\n    public Date getDate() {\r\n        return (Date) date.clone();\r\n    }\r\n\r\n    public void setDate(Date date) {\r\n        this.date = (Date) date.clone();\r\n    }\r\n}\r\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/application-local.yml",
    "content": "spring:\n  application:\n    name: cloud.cachecloud-web.local\n    import: classpath:spring/spring.xml\n  freemarker:\n    cache: false\n    settings:\n      template_update_delay: 0\nserver:\n  port: 8080\n  #ip/域名地址\n  domain: http://127.0.0.1:8080\n  servlet:\n    jsp:\n      init-parameters:\n        development: true #jsp 热部署\n\ncachecloud:\n  primary:\n    jdbcUrl: jdbc:mysql://127.0.0.1:3306/cachecloud_open?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&connectTimeout=3000&socketTimeout=10000&serverTimezone=Asia/Shanghai\n    username: cachecloud-open\n    password: cachecloud-open\n    minimumIdle: 1\n    maximumPoolSize: 3\n  redis: #配置cachecloud-web需要的redis，用户存储任务流log\n    enable: true #if config redis, suggest config true\n    main:\n      host: 127.0.0.1\n      port: 6379\n      password:\n  #区分资源\n  web:\n    clients: 127.0.0.1\naof:\n  rewrite:\n    ignore-appIds:"
  },
  {
    "path": "cachecloud-web/src/main/resources/application-online.yml",
    "content": "spring:\n  application:\n    name: cloud.cachecloud-web.open.online\n    import: classpath:spring/spring.xml\n  freemarker:\n    cache: false\n    settings:\n      template_update_delay: 0\nserver:\n  port: 8080\n  domain: 127.0.0.1:8080\n\ncachecloud:\n  primary:\n    jdbcUrl: jdbc:mysql://127.0.0.1:3306/cachecloud-open?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&connectTimeout=3000&socketTimeout=10000&serverTimezone=Asia/Shanghai\n    username: cachecloud-open\n    password: cachecloud-open\n    minimumIdle: 10\n    maximumPoolSize: 50\n  redis: #配置cachecloud-web需要的redis，用户存储任务流log\n    enable: true #if config redis, suggest config true\n    main:\n      host: 127.0.0.1\n      port: 6379\n      password:\n  web:\n    clients: 127.0.0.1\naof:\n  rewrite:\n    ignore-appIds:"
  },
  {
    "path": "cachecloud-web/src/main/resources/application-open.yml",
    "content": "spring:\n  application:\n    name: cloud.cachecloud-web.open\n    import: classpath:spring/spring.xml\n  freemarker:\n    cache: false\n    settings:\n      template_update_delay: 0\nserver:\n  port: 8080\n  domain: 127.0.0.1:8080\n\ncachecloud:\n  primary:\n    jdbcUrl: jdbc:mysql://127.0.0.1:3306/cachecloud-open?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&connectTimeout=3000&socketTimeout=10000&serverTimezone=Asia/Shanghai\n    username: cachecloud-open\n    password: cachecloud-open\n    minimumIdle: 10\n    maximumPoolSize: 50\n  redis: #配置cachecloud-web需要的redis，用户存储任务流log\n    enable: true #if config redis, suggest config true\n    main:\n      host: 127.0.0.1\n      port: 6379\n      password:\n  web:\n    clients: 127.0.0.1\naof:\n  rewrite:\n    ignore-appIds:"
  },
  {
    "path": "cachecloud-web/src/main/resources/application-test.yml",
    "content": "spring:\n  application:\n    name: cloud.cachecloud-web.open.test\n    import: classpath:spring/spring.xml\n  freemarker:\n    cache: false\n    settings:\n      template_update_delay: 0\nserver:\n  port: 8080\n  domain: 127.0.0.1:8080\n\ncachecloud:\n  primary:\n    jdbcUrl: jdbc:mysql://127.0.0.1:3306/cachecloud-open?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&connectTimeout=3000&socketTimeout=10000&serverTimezone=Asia/Shanghai\n    username: cachecloud-open\n    password: cachecloud-open\n    minimumIdle: 3\n    maximumPoolSize: 20\n  redis: #配置cachecloud-web需要的redis，用户存储任务流log\n    enable: true #if config redis, suggest config true\n    main:\n      host: 127.0.0.1\n      port: 6379\n      password:\n  web:\n    clients: 127.0.0.1\naof:\n  rewrite:\n    ignore-appIds:"
  },
  {
    "path": "cachecloud-web/src/main/resources/application.yml",
    "content": "spring:\n  profiles:\n    active: local\n  main:\n    allow-bean-definition-overriding: true\n  mvc:\n    servlet:\n      load-on-startup: 1\n#      path: /\n    static-path-pattern: /assets/**, /img/**\n    log-resolved-exception: true\n    contentnegotiation:\n      favor-path-extension: true\n    pathmatch:\n      matching-strategy: ant-path-matcher\n      use-suffix-pattern: true\n      use-registered-suffix-pattern: true\n  web:\n    resources:\n      static-locations: classpath:assets/,classpath:static/img/\n  freemarker:\n    enabled: true\n    cache: false\n    content-type: text/html\n    charset: UTF-8\n    suffix: .html\n    order: 1\n    expose-request-attributes: false\n    prefer-file-system-access: true\n    # 千位数 逗号分隔问题修复\n    settings:\n      number_format: 0.##\n      apiBuiltinEnabled: true\n      template_update_delay: 0\n    request-context-attribute: request\n  http:\n    encoding:\n      charset: UTF-8\n      enabled: true\n      force: true\ncachecloud:\n  primary:\n    driverClassName: com.mysql.cj.jdbc.Driver\n    validationQuery: select 1 from dual\n    connectionTimeout: 60000\n    idleTimeout: 600000\nmanagement:\n  server:\n    port: 8888\n  endpoint:\n    prometheus:\n      enabled: true\n    health:\n      show-details: always\n    shutdown: #关闭不安全控制endpoints\n      enabled: false\n    restart:\n      enabled: false\n    pause:\n      enabled: false\n    heapdump:\n      enabled: false\n    refresh:\n      enabled: false\n    auditevents:\n      enabled: false\n    jolokia:\n      enabled: false\n    env:\n      post:\n        enabled: false"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/css/buttons.css",
    "content": "/*! @license\n*\n* Buttons\n* Copyright 2012-2014 Alex Wolfe and Rob Levin\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*        http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n/*\n* Compass (optional)\n*\n* We recommend the use of autoprefixer instead of Compass\n* when using buttons. However, buttons does support Compass.\n* simply change $ubtn-use-compass to true and uncomment the\n* @import 'compass' code below to use Compass.\n*/\n/*\n* Required Files\n*\n* These files include the variables and options\n* and base css styles that are required to generate buttons.\n*/\n/*\n* $ubtn prefix (reserved)\n*\n* This prefix stands for Unicorn Button - ubtn\n* We provide a prefix to the Sass Variables to\n* prevent namespace collisions that could occur if\n* you import buttons as part of your Sass build process.\n* We kindly ask you not to use the prefix $ubtn in your project\n* in order to avoid possilbe name conflicts. Thanks!\n*/\n/*\n* Button Namespace (ex .button or .btn)\n*\n*/\n/*\n* Button Defaults\n*\n* Some default settings that are used throughout the button library.\n* Changes to these settings will be picked up by all of the other modules.\n* The colors used here are the default colors for the base button (gray).\n* The font size and height are used to set the base size for the buttons.\n* The size values will be used to calculate the larger and smaller button sizes.\n*/\n/*\n* Button Colors\n*\n* $ubtn-colors is used to generate the different button colors.\n* Edit or add colors to the list below and recompile.\n* Each block contains the (name, background, color)\n* The class is generated using the name: (ex .button-primary)\n*/\n/*\n* Button Shapes\n*\n* $ubtn-shapes is used to generate the different button shapes.\n* Edit or add shapes to the list below and recompile.\n* Each block contains the (name, border-radius).\n* The class is generated using the name: (ex .button-square).\n*/\n/*\n* Button Sizes\n*\n* $ubtn-sizes is used to generate the different button sizes.\n* Edit or add colors to the list below and recompile.\n* Each block contains the (name, size multiplier).\n* The class is generated using the name: (ex .button-giant).\n*/\n/*\n* Color Mixin\n*\n* Iterates through the list of colors and creates\n*\n*/\n/*\n* No Animation\n*\n* Sets animation property to none\n*/\n/*\n* Clearfix\n*\n* Clears floats inside the container\n*/\n/*\n* Base Button Style\n*\n* The default values for the .button class\n*/\n.button {\n    color: #666;\n    background-color: #EEE;\n    border-color: #EEE;\n    font-weight: 300;\n    font-size: 16px;\n    font-family: \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;\n    text-decoration: none;\n    text-align: center;\n    line-height: 40px;\n    height: 40px;\n    padding: 0 40px;\n    margin: 0;\n    display: inline-block;\n    appearance: none;\n    cursor: pointer;\n    border: none;\n    -webkit-box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    box-sizing: border-box;\n    -webkit-transition-property: all;\n    transition-property: all;\n    -webkit-transition-duration: .3s;\n    transition-duration: .3s;\n    /*\n    * Disabled State\n    *\n    * The disabled state uses the class .disabled, is-disabled,\n    * and the form attribute disabled=\"disabled\".\n    * The use of !important is only added because this is a state\n    * that must be applied to all buttons when in a disabled state.\n    */ }\n.button:visited {\n    color: #666; }\n.button:hover, .button:focus {\n    background-color: #f6f6f6;\n    text-decoration: none;\n    outline: none; }\n.button:active, .button.active, .button.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);\n    text-decoration: none;\n    background-color: #eeeeee;\n    border-color: #cfcfcf;\n    color: #d4d4d4;\n    -webkit-transition-duration: 0s;\n    transition-duration: 0s;\n    -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2); }\n.button.disabled, .button.is-disabled, .button:disabled {\n    top: 0 !important;\n    background: #EEE !important;\n    border: 1px solid #DDD !important;\n    text-shadow: 0 1px 1px white !important;\n    color: #CCC !important;\n    cursor: default !important;\n    appearance: none !important;\n    -webkit-box-shadow: none !important;\n    box-shadow: none !important;\n    opacity: .8 !important; }\n\n/*\n* Base Button Tyography\n*\n*/\n.button-uppercase {\n    text-transform: uppercase; }\n\n.button-lowercase {\n    text-transform: lowercase; }\n\n.button-capitalize {\n    text-transform: capitalize; }\n\n.button-small-caps {\n    font-variant: small-caps; }\n\n.button-icon-txt-large {\n    font-size: 36px !important; }\n\n/*\n* Base padding\n*\n*/\n.button-width-small {\n    padding: 0 10px !important; }\n\n/*\n* Base Colors\n*\n* Create colors for buttons\n* (.button-primary, .button-secondary, etc.)\n*/\n.button-primary,\n.button-primary-flat {\n    background-color: #1B9AF7;\n    border-color: #1B9AF7;\n    color: #FFF; }\n.button-primary:visited,\n.button-primary-flat:visited {\n    color: #FFF; }\n.button-primary:hover, .button-primary:focus,\n.button-primary-flat:hover,\n.button-primary-flat:focus {\n    background-color: #4cb0f9;\n    border-color: #4cb0f9;\n    color: #FFF; }\n.button-primary:active, .button-primary.active, .button-primary.is-active,\n.button-primary-flat:active,\n.button-primary-flat.active,\n.button-primary-flat.is-active {\n    background-color: #2798eb;\n    border-color: #2798eb;\n    color: #0880d7; }\n\n.button-plain,\n.button-plain-flat {\n    background-color: #FFF;\n    border-color: #FFF;\n    color: #1B9AF7; }\n.button-plain:visited,\n.button-plain-flat:visited {\n    color: #1B9AF7; }\n.button-plain:hover, .button-plain:focus,\n.button-plain-flat:hover,\n.button-plain-flat:focus {\n    background-color: white;\n    border-color: white;\n    color: #1B9AF7; }\n.button-plain:active, .button-plain.active, .button-plain.is-active,\n.button-plain-flat:active,\n.button-plain-flat.active,\n.button-plain-flat.is-active {\n    background-color: white;\n    border-color: white;\n    color: #e6e6e6; }\n\n.button-inverse,\n.button-inverse-flat {\n    background-color: #222;\n    border-color: #222;\n    color: #EEE; }\n.button-inverse:visited,\n.button-inverse-flat:visited {\n    color: #EEE; }\n.button-inverse:hover, .button-inverse:focus,\n.button-inverse-flat:hover,\n.button-inverse-flat:focus {\n    background-color: #3c3c3c;\n    border-color: #3c3c3c;\n    color: #EEE; }\n.button-inverse:active, .button-inverse.active, .button-inverse.is-active,\n.button-inverse-flat:active,\n.button-inverse-flat.active,\n.button-inverse-flat.is-active {\n    background-color: #222222;\n    border-color: #222222;\n    color: #090909; }\n\n.button-action,\n.button-action-flat {\n    background-color: #A5DE37;\n    border-color: #A5DE37;\n    color: #FFF; }\n.button-action:visited,\n.button-action-flat:visited {\n    color: #FFF; }\n.button-action:hover, .button-action:focus,\n.button-action-flat:hover,\n.button-action-flat:focus {\n    background-color: #b9e563;\n    border-color: #b9e563;\n    color: #FFF; }\n.button-action:active, .button-action.active, .button-action.is-active,\n.button-action-flat:active,\n.button-action-flat.active,\n.button-action-flat.is-active {\n    background-color: #a1d243;\n    border-color: #a1d243;\n    color: #8bc220; }\n\n.button-highlight,\n.button-highlight-flat {\n    background-color: #FEAE1B;\n    border-color: #FEAE1B;\n    color: #FFF; }\n.button-highlight:visited,\n.button-highlight-flat:visited {\n    color: #FFF; }\n.button-highlight:hover, .button-highlight:focus,\n.button-highlight-flat:hover,\n.button-highlight-flat:focus {\n    background-color: #fec04e;\n    border-color: #fec04e;\n    color: #FFF; }\n.button-highlight:active, .button-highlight.active, .button-highlight.is-active,\n.button-highlight-flat:active,\n.button-highlight-flat.active,\n.button-highlight-flat.is-active {\n    background-color: #f3ab26;\n    border-color: #f3ab26;\n    color: #e59501; }\n\n.button-caution,\n.button-caution-flat {\n    background-color: #FF4351;\n    border-color: #FF4351;\n    color: #FFF; }\n.button-caution:visited,\n.button-caution-flat:visited {\n    color: #FFF; }\n.button-caution:hover, .button-caution:focus,\n.button-caution-flat:hover,\n.button-caution-flat:focus {\n    background-color: #ff7680;\n    border-color: #ff7680;\n    color: #FFF; }\n.button-caution:active, .button-caution.active, .button-caution.is-active,\n.button-caution-flat:active,\n.button-caution-flat.active,\n.button-caution-flat.is-active {\n    background-color: #f64c59;\n    border-color: #f64c59;\n    color: #ff1022; }\n\n.button-royal,\n.button-royal-flat {\n    background-color: #7B72E9;\n    border-color: #7B72E9;\n    color: #FFF; }\n.button-royal:visited,\n.button-royal-flat:visited {\n    color: #FFF; }\n.button-royal:hover, .button-royal:focus,\n.button-royal-flat:hover,\n.button-royal-flat:focus {\n    background-color: #a49ef0;\n    border-color: #a49ef0;\n    color: #FFF; }\n.button-royal:active, .button-royal.active, .button-royal.is-active,\n.button-royal-flat:active,\n.button-royal-flat.active,\n.button-royal-flat.is-active {\n    background-color: #827ae1;\n    border-color: #827ae1;\n    color: #5246e2; }\n\n/*\n* Base Layout Styles\n*\n* Very Miminal Layout Styles\n*/\n.button-block,\n.button-stacked {\n    display: block; }\n\n/*\n* Button Types (optional)\n*\n* All of the files below represent the various button\n* types (including shapes & sizes). None of these files\n* are required. Simple remove the uneeded type below and\n* the button type will be excluded from the final build\n*/\n/*\n* Button Shapes\n*\n* This file creates the various button shapes\n* (ex. Circle, Rounded, Pill)\n*/\n.button-square {\n    border-radius: 0; }\n\n.button-box {\n    border-radius: 10px; }\n\n.button-rounded {\n    border-radius: 4px; }\n\n.button-pill {\n    border-radius: 200px; }\n\n.button-circle {\n    border-radius: 100%; }\n\n/*\n* Size Adjustment for equal height & widht buttons\n*\n* Remove padding and set a fixed width.\n*/\n.button-circle,\n.button-box,\n.button-square {\n    padding: 0 !important;\n    width: 40px; }\n.button-circle.button-giant,\n.button-box.button-giant,\n.button-square.button-giant {\n    width: 70px; }\n.button-circle.button-jumbo,\n.button-box.button-jumbo,\n.button-square.button-jumbo {\n    width: 60px; }\n.button-circle.button-large,\n.button-box.button-large,\n.button-square.button-large {\n    width: 50px; }\n.button-circle.button-normal,\n.button-box.button-normal,\n.button-square.button-normal {\n    width: 40px; }\n.button-circle.button-small,\n.button-box.button-small,\n.button-square.button-small {\n    width: 30px; }\n.button-circle.button-tiny,\n.button-box.button-tiny,\n.button-square.button-tiny {\n    width: 24px; }\n\n/*\n* Border Buttons\n*\n* These buttons have no fill they only have a\n* border to define their hit target.\n*/\n.button-border, .button-border-thin, .button-border-thick {\n    background: none;\n    border-width: 2px;\n    border-style: solid;\n    line-height: 36px; }\n.button-border:hover, .button-border-thin:hover, .button-border-thick:hover {\n    background-color: rgba(255, 255, 255, 0.9); }\n.button-border:active, .button-border-thin:active, .button-border-thick:active, .button-border.active, .active.button-border-thin, .active.button-border-thick, .button-border.is-active, .is-active.button-border-thin, .is-active.button-border-thick {\n    -webkit-box-shadow: none;\n    box-shadow: none;\n    text-shadow: none;\n    -webkit-transition-property: all;\n    transition-property: all;\n    -webkit-transition-duration: .3s;\n    transition-duration: .3s; }\n\n/*\n* Border Optional Sizes\n*\n* A slight variation in border thickness\n*/\n.button-border-thin {\n    border-width: 1px; }\n\n.button-border-thick {\n    border-width: 3px; }\n\n/*\n* Border Button Colors\n*\n* Create colors for buttons\n* (.button-primary, .button-secondary, etc.)\n*/\n.button-border, .button-border-thin, .button-border-thick,\n.button-border-thin,\n.button-border-thick {\n    /*\n    * Border Button Size Adjustment\n    *\n    * The line-height must be adjusted to compinsate for\n    * the width of the border.\n    */ }\n.button-border.button-primary, .button-primary.button-border-thin, .button-primary.button-border-thick,\n.button-border-thin.button-primary,\n.button-border-thick.button-primary {\n    color: #1B9AF7; }\n.button-border.button-primary:hover, .button-primary.button-border-thin:hover, .button-primary.button-border-thick:hover, .button-border.button-primary:focus, .button-primary.button-border-thin:focus, .button-primary.button-border-thick:focus,\n.button-border-thin.button-primary:hover,\n.button-border-thin.button-primary:focus,\n.button-border-thick.button-primary:hover,\n.button-border-thick.button-primary:focus {\n    background-color: rgba(76, 176, 249, 0.9);\n    color: rgba(255, 255, 255, 0.9); }\n.button-border.button-primary:active, .button-primary.button-border-thin:active, .button-primary.button-border-thick:active, .button-border.button-primary.active, .button-primary.active.button-border-thin, .button-primary.active.button-border-thick, .button-border.button-primary.is-active, .button-primary.is-active.button-border-thin, .button-primary.is-active.button-border-thick,\n.button-border-thin.button-primary:active,\n.button-border-thin.button-primary.active,\n.button-border-thin.button-primary.is-active,\n.button-border-thick.button-primary:active,\n.button-border-thick.button-primary.active,\n.button-border-thick.button-primary.is-active {\n    background-color: rgba(39, 152, 235, 0.7);\n    color: rgba(255, 255, 255, 0.5);\n    opacity: .3; }\n.button-border.button-plain, .button-plain.button-border-thin, .button-plain.button-border-thick,\n.button-border-thin.button-plain,\n.button-border-thick.button-plain {\n    color: #FFF; }\n.button-border.button-plain:hover, .button-plain.button-border-thin:hover, .button-plain.button-border-thick:hover, .button-border.button-plain:focus, .button-plain.button-border-thin:focus, .button-plain.button-border-thick:focus,\n.button-border-thin.button-plain:hover,\n.button-border-thin.button-plain:focus,\n.button-border-thick.button-plain:hover,\n.button-border-thick.button-plain:focus {\n    background-color: rgba(255, 255, 255, 0.9);\n    color: rgba(27, 154, 247, 0.9); }\n.button-border.button-plain:active, .button-plain.button-border-thin:active, .button-plain.button-border-thick:active, .button-border.button-plain.active, .button-plain.active.button-border-thin, .button-plain.active.button-border-thick, .button-border.button-plain.is-active, .button-plain.is-active.button-border-thin, .button-plain.is-active.button-border-thick,\n.button-border-thin.button-plain:active,\n.button-border-thin.button-plain.active,\n.button-border-thin.button-plain.is-active,\n.button-border-thick.button-plain:active,\n.button-border-thick.button-plain.active,\n.button-border-thick.button-plain.is-active {\n    background-color: rgba(255, 255, 255, 0.7);\n    color: rgba(27, 154, 247, 0.5);\n    opacity: .3; }\n.button-border.button-inverse, .button-inverse.button-border-thin, .button-inverse.button-border-thick,\n.button-border-thin.button-inverse,\n.button-border-thick.button-inverse {\n    color: #222; }\n.button-border.button-inverse:hover, .button-inverse.button-border-thin:hover, .button-inverse.button-border-thick:hover, .button-border.button-inverse:focus, .button-inverse.button-border-thin:focus, .button-inverse.button-border-thick:focus,\n.button-border-thin.button-inverse:hover,\n.button-border-thin.button-inverse:focus,\n.button-border-thick.button-inverse:hover,\n.button-border-thick.button-inverse:focus {\n    background-color: rgba(60, 60, 60, 0.9);\n    color: rgba(238, 238, 238, 0.9); }\n.button-border.button-inverse:active, .button-inverse.button-border-thin:active, .button-inverse.button-border-thick:active, .button-border.button-inverse.active, .button-inverse.active.button-border-thin, .button-inverse.active.button-border-thick, .button-border.button-inverse.is-active, .button-inverse.is-active.button-border-thin, .button-inverse.is-active.button-border-thick,\n.button-border-thin.button-inverse:active,\n.button-border-thin.button-inverse.active,\n.button-border-thin.button-inverse.is-active,\n.button-border-thick.button-inverse:active,\n.button-border-thick.button-inverse.active,\n.button-border-thick.button-inverse.is-active {\n    background-color: rgba(34, 34, 34, 0.7);\n    color: rgba(238, 238, 238, 0.5);\n    opacity: .3; }\n.button-border.button-action, .button-action.button-border-thin, .button-action.button-border-thick,\n.button-border-thin.button-action,\n.button-border-thick.button-action {\n    color: #A5DE37; }\n.button-border.button-action:hover, .button-action.button-border-thin:hover, .button-action.button-border-thick:hover, .button-border.button-action:focus, .button-action.button-border-thin:focus, .button-action.button-border-thick:focus,\n.button-border-thin.button-action:hover,\n.button-border-thin.button-action:focus,\n.button-border-thick.button-action:hover,\n.button-border-thick.button-action:focus {\n    background-color: rgba(185, 229, 99, 0.9);\n    color: rgba(255, 255, 255, 0.9); }\n.button-border.button-action:active, .button-action.button-border-thin:active, .button-action.button-border-thick:active, .button-border.button-action.active, .button-action.active.button-border-thin, .button-action.active.button-border-thick, .button-border.button-action.is-active, .button-action.is-active.button-border-thin, .button-action.is-active.button-border-thick,\n.button-border-thin.button-action:active,\n.button-border-thin.button-action.active,\n.button-border-thin.button-action.is-active,\n.button-border-thick.button-action:active,\n.button-border-thick.button-action.active,\n.button-border-thick.button-action.is-active {\n    background-color: rgba(161, 210, 67, 0.7);\n    color: rgba(255, 255, 255, 0.5);\n    opacity: .3; }\n.button-border.button-highlight, .button-highlight.button-border-thin, .button-highlight.button-border-thick,\n.button-border-thin.button-highlight,\n.button-border-thick.button-highlight {\n    color: #FEAE1B; }\n.button-border.button-highlight:hover, .button-highlight.button-border-thin:hover, .button-highlight.button-border-thick:hover, .button-border.button-highlight:focus, .button-highlight.button-border-thin:focus, .button-highlight.button-border-thick:focus,\n.button-border-thin.button-highlight:hover,\n.button-border-thin.button-highlight:focus,\n.button-border-thick.button-highlight:hover,\n.button-border-thick.button-highlight:focus {\n    background-color: rgba(254, 192, 78, 0.9);\n    color: rgba(255, 255, 255, 0.9); }\n.button-border.button-highlight:active, .button-highlight.button-border-thin:active, .button-highlight.button-border-thick:active, .button-border.button-highlight.active, .button-highlight.active.button-border-thin, .button-highlight.active.button-border-thick, .button-border.button-highlight.is-active, .button-highlight.is-active.button-border-thin, .button-highlight.is-active.button-border-thick,\n.button-border-thin.button-highlight:active,\n.button-border-thin.button-highlight.active,\n.button-border-thin.button-highlight.is-active,\n.button-border-thick.button-highlight:active,\n.button-border-thick.button-highlight.active,\n.button-border-thick.button-highlight.is-active {\n    background-color: rgba(243, 171, 38, 0.7);\n    color: rgba(255, 255, 255, 0.5);\n    opacity: .3; }\n.button-border.button-caution, .button-caution.button-border-thin, .button-caution.button-border-thick,\n.button-border-thin.button-caution,\n.button-border-thick.button-caution {\n    color: #FF4351; }\n.button-border.button-caution:hover, .button-caution.button-border-thin:hover, .button-caution.button-border-thick:hover, .button-border.button-caution:focus, .button-caution.button-border-thin:focus, .button-caution.button-border-thick:focus,\n.button-border-thin.button-caution:hover,\n.button-border-thin.button-caution:focus,\n.button-border-thick.button-caution:hover,\n.button-border-thick.button-caution:focus {\n    background-color: rgba(255, 118, 128, 0.9);\n    color: rgba(255, 255, 255, 0.9); }\n.button-border.button-caution:active, .button-caution.button-border-thin:active, .button-caution.button-border-thick:active, .button-border.button-caution.active, .button-caution.active.button-border-thin, .button-caution.active.button-border-thick, .button-border.button-caution.is-active, .button-caution.is-active.button-border-thin, .button-caution.is-active.button-border-thick,\n.button-border-thin.button-caution:active,\n.button-border-thin.button-caution.active,\n.button-border-thin.button-caution.is-active,\n.button-border-thick.button-caution:active,\n.button-border-thick.button-caution.active,\n.button-border-thick.button-caution.is-active {\n    background-color: rgba(246, 76, 89, 0.7);\n    color: rgba(255, 255, 255, 0.5);\n    opacity: .3; }\n.button-border.button-royal, .button-royal.button-border-thin, .button-royal.button-border-thick,\n.button-border-thin.button-royal,\n.button-border-thick.button-royal {\n    color: #7B72E9; }\n.button-border.button-royal:hover, .button-royal.button-border-thin:hover, .button-royal.button-border-thick:hover, .button-border.button-royal:focus, .button-royal.button-border-thin:focus, .button-royal.button-border-thick:focus,\n.button-border-thin.button-royal:hover,\n.button-border-thin.button-royal:focus,\n.button-border-thick.button-royal:hover,\n.button-border-thick.button-royal:focus {\n    background-color: rgba(164, 158, 240, 0.9);\n    color: rgba(255, 255, 255, 0.9); }\n.button-border.button-royal:active, .button-royal.button-border-thin:active, .button-royal.button-border-thick:active, .button-border.button-royal.active, .button-royal.active.button-border-thin, .button-royal.active.button-border-thick, .button-border.button-royal.is-active, .button-royal.is-active.button-border-thin, .button-royal.is-active.button-border-thick,\n.button-border-thin.button-royal:active,\n.button-border-thin.button-royal.active,\n.button-border-thin.button-royal.is-active,\n.button-border-thick.button-royal:active,\n.button-border-thick.button-royal.active,\n.button-border-thick.button-royal.is-active {\n    background-color: rgba(130, 122, 225, 0.7);\n    color: rgba(255, 255, 255, 0.5);\n    opacity: .3; }\n.button-border.button-giant, .button-giant.button-border-thin, .button-giant.button-border-thick,\n.button-border-thin.button-giant,\n.button-border-thick.button-giant {\n    line-height: 66px; }\n.button-border.button-jumbo, .button-jumbo.button-border-thin, .button-jumbo.button-border-thick,\n.button-border-thin.button-jumbo,\n.button-border-thick.button-jumbo {\n    line-height: 56px; }\n.button-border.button-large, .button-large.button-border-thin, .button-large.button-border-thick,\n.button-border-thin.button-large,\n.button-border-thick.button-large {\n    line-height: 46px; }\n.button-border.button-normal, .button-normal.button-border-thin, .button-normal.button-border-thick,\n.button-border-thin.button-normal,\n.button-border-thick.button-normal {\n    line-height: 36px; }\n.button-border.button-small, .button-small.button-border-thin, .button-small.button-border-thick,\n.button-border-thin.button-small,\n.button-border-thick.button-small {\n    line-height: 26px; }\n.button-border.button-tiny, .button-tiny.button-border-thin, .button-tiny.button-border-thick,\n.button-border-thin.button-tiny,\n.button-border-thick.button-tiny {\n    line-height: 20px; }\n\n/*\n* Border Buttons\n*\n* These buttons have no fill they only have a\n* border to define their hit target.\n*/\n.button-borderless {\n    background: none;\n    border: none;\n    padding: 0 8px !important;\n    color: #EEE;\n    font-size: 20.8px;\n    font-weight: 200;\n    /*\n    * Borderless Button Colors\n    *\n    * Create colors for buttons\n    * (.button-primary, .button-secondary, etc.)\n    */\n    /*\n    * Borderles Size Adjustment\n    *\n    * The font-size must be large to compinsate for\n    * the lack of a hit target.\n    */ }\n.button-borderless:hover, .button-borderless:focus {\n    background: none; }\n.button-borderless:active, .button-borderless.active, .button-borderless.is-active {\n    -webkit-box-shadow: none;\n    box-shadow: none;\n    text-shadow: none;\n    -webkit-transition-property: all;\n    transition-property: all;\n    -webkit-transition-duration: .3s;\n    transition-duration: .3s;\n    opacity: .3; }\n.button-borderless.button-primary {\n    color: #1B9AF7; }\n.button-borderless.button-plain {\n    color: #FFF; }\n.button-borderless.button-inverse {\n    color: #222; }\n.button-borderless.button-action {\n    color: #A5DE37; }\n.button-borderless.button-highlight {\n    color: #FEAE1B; }\n.button-borderless.button-caution {\n    color: #FF4351; }\n.button-borderless.button-royal {\n    color: #7B72E9; }\n.button-borderless.button-giant {\n    font-size: 36.4px;\n    height: 52.4px;\n    line-height: 52.4px; }\n.button-borderless.button-jumbo {\n    font-size: 31.2px;\n    height: 47.2px;\n    line-height: 47.2px; }\n.button-borderless.button-large {\n    font-size: 26px;\n    height: 42px;\n    line-height: 42px; }\n.button-borderless.button-normal {\n    font-size: 20.8px;\n    height: 36.8px;\n    line-height: 36.8px; }\n.button-borderless.button-small {\n    font-size: 15.6px;\n    height: 31.6px;\n    line-height: 31.6px; }\n.button-borderless.button-tiny {\n    font-size: 12.48px;\n    height: 28.48px;\n    line-height: 28.48px; }\n\n/*\n* Raised Buttons\n*\n* A classic looking button that offers\n* great depth and affordance.\n*/\n.button-raised {\n    border-color: #e1e1e1;\n    border-style: solid;\n    border-width: 1px;\n    line-height: 38px;\n    background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e1e1e1));\n    background: linear-gradient(#f6f6f6, #e1e1e1);\n    -webkit-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.15);\n    box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.15); }\n.button-raised:hover, .button-raised:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(white), to(gainsboro));\n    background: linear-gradient(top, white, gainsboro); }\n.button-raised:active, .button-raised.active, .button-raised.is-active {\n    background: #eeeeee;\n    -webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.2), 0px 1px 0px white;\n    box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.2), 0px 1px 0px white; }\n\n/*\n* Raised Button Colors\n*\n* Create colors for raised buttons\n*/\n.button-raised.button-primary {\n    border-color: #088ef0;\n    background: -webkit-gradient(linear, left top, left bottom, from(#34a5f8), to(#088ef0));\n    background: linear-gradient(#34a5f8, #088ef0); }\n.button-raised.button-primary:hover, .button-raised.button-primary:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(#42abf8), to(#0888e6));\n    background: linear-gradient(top, #42abf8, #0888e6); }\n.button-raised.button-primary:active, .button-raised.button-primary.active, .button-raised.button-primary.is-active {\n    border-color: #0880d7;\n    background: #2798eb; }\n.button-raised.button-plain {\n    border-color: #f2f2f2;\n    background: -webkit-gradient(linear, left top, left bottom, from(white), to(#f2f2f2));\n    background: linear-gradient(white, #f2f2f2); }\n.button-raised.button-plain:hover, .button-raised.button-plain:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(white), to(#ededed));\n    background: linear-gradient(top, white, #ededed); }\n.button-raised.button-plain:active, .button-raised.button-plain.active, .button-raised.button-plain.is-active {\n    border-color: #e6e6e6;\n    background: white; }\n.button-raised.button-inverse {\n    border-color: #151515;\n    background: -webkit-gradient(linear, left top, left bottom, from(#2f2f2f), to(#151515));\n    background: linear-gradient(#2f2f2f, #151515); }\n.button-raised.button-inverse:hover, .button-raised.button-inverse:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(#363636), to(#101010));\n    background: linear-gradient(top, #363636, #101010); }\n.button-raised.button-inverse:active, .button-raised.button-inverse.active, .button-raised.button-inverse.is-active {\n    border-color: #090909;\n    background: #222222; }\n.button-raised.button-action {\n    border-color: #9ad824;\n    background: -webkit-gradient(linear, left top, left bottom, from(#afe24d), to(#9ad824));\n    background: linear-gradient(#afe24d, #9ad824); }\n.button-raised.button-action:hover, .button-raised.button-action:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(#b5e45a), to(#94cf22));\n    background: linear-gradient(top, #b5e45a, #94cf22); }\n.button-raised.button-action:active, .button-raised.button-action.active, .button-raised.button-action.is-active {\n    border-color: #8bc220;\n    background: #a1d243; }\n.button-raised.button-highlight {\n    border-color: #fea502;\n    background: -webkit-gradient(linear, left top, left bottom, from(#feb734), to(#fea502));\n    background: linear-gradient(#feb734, #fea502); }\n.button-raised.button-highlight:hover, .button-raised.button-highlight:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(#febc44), to(#f49f01));\n    background: linear-gradient(top, #febc44, #f49f01); }\n.button-raised.button-highlight:active, .button-raised.button-highlight.active, .button-raised.button-highlight.is-active {\n    border-color: #e59501;\n    background: #f3ab26; }\n.button-raised.button-caution {\n    border-color: #ff2939;\n    background: -webkit-gradient(linear, left top, left bottom, from(#ff5c69), to(#ff2939));\n    background: linear-gradient(#ff5c69, #ff2939); }\n.button-raised.button-caution:hover, .button-raised.button-caution:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(#ff6c77), to(#ff1f30));\n    background: linear-gradient(top, #ff6c77, #ff1f30); }\n.button-raised.button-caution:active, .button-raised.button-caution.active, .button-raised.button-caution.is-active {\n    border-color: #ff1022;\n    background: #f64c59; }\n.button-raised.button-royal {\n    border-color: #665ce6;\n    background: -webkit-gradient(linear, left top, left bottom, from(#9088ec), to(#665ce6));\n    background: linear-gradient(#9088ec, #665ce6); }\n.button-raised.button-royal:hover, .button-raised.button-royal:focus {\n    background: -webkit-gradient(linear, left top, left bottom, from(#9c95ef), to(#5e53e4));\n    background: linear-gradient(top, #9c95ef, #5e53e4); }\n.button-raised.button-royal:active, .button-raised.button-royal.active, .button-raised.button-royal.is-active {\n    border-color: #5246e2;\n    background: #827ae1; }\n\n/*\n* 3D Buttons\n*\n* These buttons have a heavy three dimensional\n* style that mimics the visual appearance of a\n* real life button.\n*/\n.button-3d {\n    position: relative;\n    top: 0;\n    -webkit-box-shadow: 0 7px 0 #bbbbbb, 0 8px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 7px 0 #bbbbbb, 0 8px 3px rgba(0, 0, 0, 0.2); }\n.button-3d:hover, .button-3d:focus {\n    -webkit-box-shadow: 0 7px 0 #bbbbbb, 0 8px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 7px 0 #bbbbbb, 0 8px 3px rgba(0, 0, 0, 0.2); }\n.button-3d:active, .button-3d.active, .button-3d.is-active {\n    top: 5px;\n    -webkit-transition-property: all;\n    transition-property: all;\n    -webkit-transition-duration: .15s;\n    transition-duration: .15s;\n    -webkit-box-shadow: 0 2px 0 #bbbbbb, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 #bbbbbb, 0 3px 3px rgba(0, 0, 0, 0.2); }\n\n/*\n* 3D Button Colors\n*\n* Create colors for buttons\n* (.button-primary, .button-secondary, etc.)\n*/\n.button-3d.button-primary {\n    -webkit-box-shadow: 0 7px 0 #0880d7, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #0880d7, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-primary:hover, .button-3d.button-primary:focus {\n    -webkit-box-shadow: 0 7px 0 #077ace, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #077ace, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-primary:active, .button-3d.button-primary.active, .button-3d.button-primary.is-active {\n    -webkit-box-shadow: 0 2px 0 #0662a6, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 #0662a6, 0 3px 3px rgba(0, 0, 0, 0.2); }\n.button-3d.button-plain {\n    -webkit-box-shadow: 0 7px 0 #e6e6e6, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #e6e6e6, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-plain:hover, .button-3d.button-plain:focus {\n    -webkit-box-shadow: 0 7px 0 #e0e0e0, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #e0e0e0, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-plain:active, .button-3d.button-plain.active, .button-3d.button-plain.is-active {\n    -webkit-box-shadow: 0 2px 0 #cccccc, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 #cccccc, 0 3px 3px rgba(0, 0, 0, 0.2); }\n.button-3d.button-inverse {\n    -webkit-box-shadow: 0 7px 0 #090909, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #090909, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-inverse:hover, .button-3d.button-inverse:focus {\n    -webkit-box-shadow: 0 7px 0 #030303, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #030303, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-inverse:active, .button-3d.button-inverse.active, .button-3d.button-inverse.is-active {\n    -webkit-box-shadow: 0 2px 0 black, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 black, 0 3px 3px rgba(0, 0, 0, 0.2); }\n.button-3d.button-action {\n    -webkit-box-shadow: 0 7px 0 #8bc220, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #8bc220, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-action:hover, .button-3d.button-action:focus {\n    -webkit-box-shadow: 0 7px 0 #84b91f, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #84b91f, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-action:active, .button-3d.button-action.active, .button-3d.button-action.is-active {\n    -webkit-box-shadow: 0 2px 0 #6b9619, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 #6b9619, 0 3px 3px rgba(0, 0, 0, 0.2); }\n.button-3d.button-highlight {\n    -webkit-box-shadow: 0 7px 0 #e59501, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #e59501, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-highlight:hover, .button-3d.button-highlight:focus {\n    -webkit-box-shadow: 0 7px 0 #db8e01, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #db8e01, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-highlight:active, .button-3d.button-highlight.active, .button-3d.button-highlight.is-active {\n    -webkit-box-shadow: 0 2px 0 #b27401, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 #b27401, 0 3px 3px rgba(0, 0, 0, 0.2); }\n.button-3d.button-caution {\n    -webkit-box-shadow: 0 7px 0 #ff1022, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #ff1022, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-caution:hover, .button-3d.button-caution:focus {\n    -webkit-box-shadow: 0 7px 0 #ff0618, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #ff0618, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-caution:active, .button-3d.button-caution.active, .button-3d.button-caution.is-active {\n    -webkit-box-shadow: 0 2px 0 #dc0010, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 #dc0010, 0 3px 3px rgba(0, 0, 0, 0.2); }\n.button-3d.button-royal {\n    -webkit-box-shadow: 0 7px 0 #5246e2, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #5246e2, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-royal:hover, .button-3d.button-royal:focus {\n    -webkit-box-shadow: 0 7px 0 #493de1, 0 8px 3px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 7px 0 #493de1, 0 8px 3px rgba(0, 0, 0, 0.3); }\n.button-3d.button-royal:active, .button-3d.button-royal.active, .button-3d.button-royal.is-active {\n    -webkit-box-shadow: 0 2px 0 #2f21d4, 0 3px 3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 0 #2f21d4, 0 3px 3px rgba(0, 0, 0, 0.2); }\n\n/*\n* Glowing Buttons\n*\n* A pulse like glow that appears\n* rythmically around the edges of\n* a button.\n*/\n/*\n* Glow animation mixin for Compass users\n*\n*/\n/*\n* Glowing Keyframes\n*\n*/\n@-webkit-keyframes glowing {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(44, 154, 219, 0.3);\n        box-shadow: 0 0 0 rgba(44, 154, 219, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(44, 154, 219, 0.8);\n        box-shadow: 0 0 20px rgba(44, 154, 219, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(44, 154, 219, 0.3);\n        box-shadow: 0 0 0 rgba(44, 154, 219, 0.3); } }\n@keyframes glowing {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(44, 154, 219, 0.3);\n        box-shadow: 0 0 0 rgba(44, 154, 219, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(44, 154, 219, 0.8);\n        box-shadow: 0 0 20px rgba(44, 154, 219, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(44, 154, 219, 0.3);\n        box-shadow: 0 0 0 rgba(44, 154, 219, 0.3); } }\n/*\n* Glowing Keyframes for various colors\n*\n*/\n@-webkit-keyframes glowing-primary {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(27, 154, 247, 0.3);\n        box-shadow: 0 0 0 rgba(27, 154, 247, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(27, 154, 247, 0.8);\n        box-shadow: 0 0 20px rgba(27, 154, 247, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(27, 154, 247, 0.3);\n        box-shadow: 0 0 0 rgba(27, 154, 247, 0.3); } }\n@keyframes glowing-primary {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(27, 154, 247, 0.3);\n        box-shadow: 0 0 0 rgba(27, 154, 247, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(27, 154, 247, 0.8);\n        box-shadow: 0 0 20px rgba(27, 154, 247, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(27, 154, 247, 0.3);\n        box-shadow: 0 0 0 rgba(27, 154, 247, 0.3); } }\n@-webkit-keyframes glowing-plain {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(255, 255, 255, 0.3);\n        box-shadow: 0 0 0 rgba(255, 255, 255, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(255, 255, 255, 0.8);\n        box-shadow: 0 0 20px rgba(255, 255, 255, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(255, 255, 255, 0.3);\n        box-shadow: 0 0 0 rgba(255, 255, 255, 0.3); } }\n@keyframes glowing-plain {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(255, 255, 255, 0.3);\n        box-shadow: 0 0 0 rgba(255, 255, 255, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(255, 255, 255, 0.8);\n        box-shadow: 0 0 20px rgba(255, 255, 255, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(255, 255, 255, 0.3);\n        box-shadow: 0 0 0 rgba(255, 255, 255, 0.3); } }\n@-webkit-keyframes glowing-inverse {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(34, 34, 34, 0.3);\n        box-shadow: 0 0 0 rgba(34, 34, 34, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(34, 34, 34, 0.8);\n        box-shadow: 0 0 20px rgba(34, 34, 34, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(34, 34, 34, 0.3);\n        box-shadow: 0 0 0 rgba(34, 34, 34, 0.3); } }\n@keyframes glowing-inverse {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(34, 34, 34, 0.3);\n        box-shadow: 0 0 0 rgba(34, 34, 34, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(34, 34, 34, 0.8);\n        box-shadow: 0 0 20px rgba(34, 34, 34, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(34, 34, 34, 0.3);\n        box-shadow: 0 0 0 rgba(34, 34, 34, 0.3); } }\n@-webkit-keyframes glowing-action {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(165, 222, 55, 0.3);\n        box-shadow: 0 0 0 rgba(165, 222, 55, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(165, 222, 55, 0.8);\n        box-shadow: 0 0 20px rgba(165, 222, 55, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(165, 222, 55, 0.3);\n        box-shadow: 0 0 0 rgba(165, 222, 55, 0.3); } }\n@keyframes glowing-action {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(165, 222, 55, 0.3);\n        box-shadow: 0 0 0 rgba(165, 222, 55, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(165, 222, 55, 0.8);\n        box-shadow: 0 0 20px rgba(165, 222, 55, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(165, 222, 55, 0.3);\n        box-shadow: 0 0 0 rgba(165, 222, 55, 0.3); } }\n@-webkit-keyframes glowing-highlight {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(254, 174, 27, 0.3);\n        box-shadow: 0 0 0 rgba(254, 174, 27, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(254, 174, 27, 0.8);\n        box-shadow: 0 0 20px rgba(254, 174, 27, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(254, 174, 27, 0.3);\n        box-shadow: 0 0 0 rgba(254, 174, 27, 0.3); } }\n@keyframes glowing-highlight {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(254, 174, 27, 0.3);\n        box-shadow: 0 0 0 rgba(254, 174, 27, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(254, 174, 27, 0.8);\n        box-shadow: 0 0 20px rgba(254, 174, 27, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(254, 174, 27, 0.3);\n        box-shadow: 0 0 0 rgba(254, 174, 27, 0.3); } }\n@-webkit-keyframes glowing-caution {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(255, 67, 81, 0.3);\n        box-shadow: 0 0 0 rgba(255, 67, 81, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(255, 67, 81, 0.8);\n        box-shadow: 0 0 20px rgba(255, 67, 81, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(255, 67, 81, 0.3);\n        box-shadow: 0 0 0 rgba(255, 67, 81, 0.3); } }\n@keyframes glowing-caution {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(255, 67, 81, 0.3);\n        box-shadow: 0 0 0 rgba(255, 67, 81, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(255, 67, 81, 0.8);\n        box-shadow: 0 0 20px rgba(255, 67, 81, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(255, 67, 81, 0.3);\n        box-shadow: 0 0 0 rgba(255, 67, 81, 0.3); } }\n@-webkit-keyframes glowing-royal {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(123, 114, 233, 0.3);\n        box-shadow: 0 0 0 rgba(123, 114, 233, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(123, 114, 233, 0.8);\n        box-shadow: 0 0 20px rgba(123, 114, 233, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(123, 114, 233, 0.3);\n        box-shadow: 0 0 0 rgba(123, 114, 233, 0.3); } }\n@keyframes glowing-royal {\n    from {\n        -webkit-box-shadow: 0 0 0 rgba(123, 114, 233, 0.3);\n        box-shadow: 0 0 0 rgba(123, 114, 233, 0.3); }\n    50% {\n        -webkit-box-shadow: 0 0 20px rgba(123, 114, 233, 0.8);\n        box-shadow: 0 0 20px rgba(123, 114, 233, 0.8); }\n    to {\n        -webkit-box-shadow: 0 0 0 rgba(123, 114, 233, 0.3);\n        box-shadow: 0 0 0 rgba(123, 114, 233, 0.3); } }\n/*\n* Glowing Buttons Base Styes\n*\n* A pulse like glow that appears\n* rythmically around the edges of\n* a button.\n*/\n.button-glow {\n    -webkit-animation-duration: 3s;\n    animation-duration: 3s;\n    -webkit-animation-iteration-count: infinite;\n    animation-iteration-count: infinite;\n    -webkit-animation-name: glowing;\n    animation-name: glowing; }\n.button-glow:active, .button-glow.active, .button-glow.is-active {\n    -webkit-animation-name: none;\n    animation-name: none; }\n\n/*\n* Glowing Button Colors\n*\n* Create colors for glowing buttons\n*/\n.button-glow.button-primary {\n    -webkit-animation-name: glowing-primary;\n    animation-name: glowing-primary; }\n.button-glow.button-plain {\n    -webkit-animation-name: glowing-plain;\n    animation-name: glowing-plain; }\n.button-glow.button-inverse {\n    -webkit-animation-name: glowing-inverse;\n    animation-name: glowing-inverse; }\n.button-glow.button-action {\n    -webkit-animation-name: glowing-action;\n    animation-name: glowing-action; }\n.button-glow.button-highlight {\n    -webkit-animation-name: glowing-highlight;\n    animation-name: glowing-highlight; }\n.button-glow.button-caution {\n    -webkit-animation-name: glowing-caution;\n    animation-name: glowing-caution; }\n.button-glow.button-royal {\n    -webkit-animation-name: glowing-royal;\n    animation-name: glowing-royal; }\n\n/*\n* Dropdown menu buttons\n*\n* A dropdown menu appears\n* when a button is pressed\n*/\n/*\n* Dropdown Container\n*\n*/\n.button-dropdown {\n    position: relative;\n    overflow: visible;\n    display: inline-block; }\n\n/*\n* Dropdown List Style\n*\n*/\n.button-dropdown-list {\n    display: none;\n    position: absolute;\n    padding: 0;\n    margin: 0;\n    top: 0;\n    left: 0;\n    z-index: 1000;\n    min-width: 100%;\n    list-style-type: none;\n    background: rgba(255, 255, 255, 0.95);\n    border-style: solid;\n    border-width: 1px;\n    border-color: #d4d4d4;\n    font-family: \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;\n    -webkit-box-shadow: 0 2px 7px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 7px rgba(0, 0, 0, 0.2);\n    border-radius: 3px;\n    -webkit-box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    box-sizing: border-box;\n    /*\n    * Dropdown Below\n    *\n    */\n    /*\n    * Dropdown Above\n    *\n    */ }\n.button-dropdown-list.is-below {\n    top: 100%;\n    border-top: none;\n    border-radius: 0 0 3px 3px; }\n.button-dropdown-list.is-above {\n    bottom: 100%;\n    top: auto;\n    border-bottom: none;\n    border-radius: 3px 3px 0 0;\n    -webkit-box-shadow: 0 -2px 7px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 -2px 7px rgba(0, 0, 0, 0.2); }\n\n/*\n* Dropdown Buttons\n*\n*/\n.button-dropdown-list > li {\n    padding: 0;\n    margin: 0;\n    display: block; }\n.button-dropdown-list > li > a {\n    display: block;\n    line-height: 40px;\n    font-size: 12.8px;\n    padding: 5px 10px;\n    float: none;\n    color: #666;\n    text-decoration: none; }\n.button-dropdown-list > li > a:hover {\n    color: #5e5e5e;\n    background: #f6f6f6;\n    text-decoration: none; }\n\n.button-dropdown-divider {\n    border-top: 1px solid #e6e6e6; }\n\n/*\n* Dropdown Colors\n*\n* Create colors for buttons\n* (.button-primary, .button-secondary, etc.)\n*/\n.button-dropdown.button-dropdown-primary .button-dropdown-list {\n    background: rgba(27, 154, 247, 0.95);\n    border-color: #0880d7; }\n.button-dropdown.button-dropdown-primary .button-dropdown-list .button-dropdown-divider {\n    border-color: #0888e6; }\n.button-dropdown.button-dropdown-primary .button-dropdown-list > li > a {\n    color: #FFF; }\n.button-dropdown.button-dropdown-primary .button-dropdown-list > li > a:hover {\n    color: #f2f2f2;\n    background: #088ef0; }\n.button-dropdown.button-dropdown-plain .button-dropdown-list {\n    background: rgba(255, 255, 255, 0.95);\n    border-color: #e6e6e6; }\n.button-dropdown.button-dropdown-plain .button-dropdown-list .button-dropdown-divider {\n    border-color: #ededed; }\n.button-dropdown.button-dropdown-plain .button-dropdown-list > li > a {\n    color: #1B9AF7; }\n.button-dropdown.button-dropdown-plain .button-dropdown-list > li > a:hover {\n    color: #088ef0;\n    background: #f2f2f2; }\n.button-dropdown.button-dropdown-inverse .button-dropdown-list {\n    background: rgba(34, 34, 34, 0.95);\n    border-color: #090909; }\n.button-dropdown.button-dropdown-inverse .button-dropdown-list .button-dropdown-divider {\n    border-color: #101010; }\n.button-dropdown.button-dropdown-inverse .button-dropdown-list > li > a {\n    color: #EEE; }\n.button-dropdown.button-dropdown-inverse .button-dropdown-list > li > a:hover {\n    color: #e1e1e1;\n    background: #151515; }\n.button-dropdown.button-dropdown-action .button-dropdown-list {\n    background: rgba(165, 222, 55, 0.95);\n    border-color: #8bc220; }\n.button-dropdown.button-dropdown-action .button-dropdown-list .button-dropdown-divider {\n    border-color: #94cf22; }\n.button-dropdown.button-dropdown-action .button-dropdown-list > li > a {\n    color: #FFF; }\n.button-dropdown.button-dropdown-action .button-dropdown-list > li > a:hover {\n    color: #f2f2f2;\n    background: #9ad824; }\n.button-dropdown.button-dropdown-highlight .button-dropdown-list {\n    background: rgba(254, 174, 27, 0.95);\n    border-color: #e59501; }\n.button-dropdown.button-dropdown-highlight .button-dropdown-list .button-dropdown-divider {\n    border-color: #f49f01; }\n.button-dropdown.button-dropdown-highlight .button-dropdown-list > li > a {\n    color: #FFF; }\n.button-dropdown.button-dropdown-highlight .button-dropdown-list > li > a:hover {\n    color: #f2f2f2;\n    background: #fea502; }\n.button-dropdown.button-dropdown-caution .button-dropdown-list {\n    background: rgba(255, 67, 81, 0.95);\n    border-color: #ff1022; }\n.button-dropdown.button-dropdown-caution .button-dropdown-list .button-dropdown-divider {\n    border-color: #ff1f30; }\n.button-dropdown.button-dropdown-caution .button-dropdown-list > li > a {\n    color: #FFF; }\n.button-dropdown.button-dropdown-caution .button-dropdown-list > li > a:hover {\n    color: #f2f2f2;\n    background: #ff2939; }\n.button-dropdown.button-dropdown-royal .button-dropdown-list {\n    background: rgba(123, 114, 233, 0.95);\n    border-color: #5246e2; }\n.button-dropdown.button-dropdown-royal .button-dropdown-list .button-dropdown-divider {\n    border-color: #5e53e4; }\n.button-dropdown.button-dropdown-royal .button-dropdown-list > li > a {\n    color: #FFF; }\n.button-dropdown.button-dropdown-royal .button-dropdown-list > li > a:hover {\n    color: #f2f2f2;\n    background: #665ce6; }\n\n/*\n* Buton Groups\n*\n* A group of related buttons\n* displayed edge to edge\n*/\n.button-group {\n    position: relative;\n    display: inline-block; }\n.button-group:after {\n    content: \" \";\n    display: block;\n    clear: both; }\n.button-group .button,\n.button-group .button-dropdown {\n    float: left; }\n.button-group .button:not(:first-child):not(:last-child),\n.button-group .button-dropdown:not(:first-child):not(:last-child) {\n    border-radius: 0;\n    border-right: none; }\n.button-group .button:first-child,\n.button-group .button-dropdown:first-child {\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n    border-right: none; }\n.button-group .button:last-child,\n.button-group .button-dropdown:last-child {\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0; }\n\n/*\n* Button Wrapper\n*\n* A wrap around effect to highlight\n* the shape of the button and offer\n* a subtle visual effect.\n*/\n.button-wrap {\n    border: 1px solid #e3e3e3;\n    display: inline-block;\n    padding: 9px;\n    background: -webkit-gradient(linear, left top, left bottom, from(#f2f2f2), to(#FFF));\n    background: linear-gradient(#f2f2f2, #FFF);\n    border-radius: 200px;\n    -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.04);\n    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.04); }\n\n/*\n* Long Shadow Buttons\n*\n* A visual effect adding a flat shadow to the text of a button\n*/\n/*\n* Long Shadow Function\n*\n* Loops $length times building a long shadow. Defaults downward right\n*/\n/*\n* LONG SHADOW MIXIN\n*\n*/\n/*\n* Shadow Right\n*\n*/\n.button-longshadow,\n.button-longshadow-right {\n    overflow: hidden; }\n.button-longshadow.button-primary,\n.button-longshadow-right.button-primary {\n    text-shadow: 0px 0px #0880d7, 1px 1px #0880d7, 2px 2px #0880d7, 3px 3px #0880d7, 4px 4px #0880d7, 5px 5px #0880d7, 6px 6px #0880d7, 7px 7px #0880d7, 8px 8px #0880d7, 9px 9px #0880d7, 10px 10px #0880d7, 11px 11px #0880d7, 12px 12px #0880d7, 13px 13px #0880d7, 14px 14px #0880d7, 15px 15px #0880d7, 16px 16px #0880d7, 17px 17px #0880d7, 18px 18px #0880d7, 19px 19px #0880d7, 20px 20px #0880d7, 21px 21px #0880d7, 22px 22px #0880d7, 23px 23px #0880d7, 24px 24px #0880d7, 25px 25px #0880d7, 26px 26px #0880d7, 27px 27px #0880d7, 28px 28px #0880d7, 29px 29px #0880d7, 30px 30px #0880d7, 31px 31px #0880d7, 32px 32px #0880d7, 33px 33px #0880d7, 34px 34px #0880d7, 35px 35px #0880d7, 36px 36px #0880d7, 37px 37px #0880d7, 38px 38px #0880d7, 39px 39px #0880d7, 40px 40px #0880d7, 41px 41px #0880d7, 42px 42px #0880d7, 43px 43px #0880d7, 44px 44px #0880d7, 45px 45px #0880d7, 46px 46px #0880d7, 47px 47px #0880d7, 48px 48px #0880d7, 49px 49px #0880d7, 50px 50px #0880d7, 51px 51px #0880d7, 52px 52px #0880d7, 53px 53px #0880d7, 54px 54px #0880d7, 55px 55px #0880d7, 56px 56px #0880d7, 57px 57px #0880d7, 58px 58px #0880d7, 59px 59px #0880d7, 60px 60px #0880d7, 61px 61px #0880d7, 62px 62px #0880d7, 63px 63px #0880d7, 64px 64px #0880d7, 65px 65px #0880d7, 66px 66px #0880d7, 67px 67px #0880d7, 68px 68px #0880d7, 69px 69px #0880d7, 70px 70px #0880d7, 71px 71px #0880d7, 72px 72px #0880d7, 73px 73px #0880d7, 74px 74px #0880d7, 75px 75px #0880d7, 76px 76px #0880d7, 77px 77px #0880d7, 78px 78px #0880d7, 79px 79px #0880d7, 80px 80px #0880d7, 81px 81px #0880d7, 82px 82px #0880d7, 83px 83px #0880d7, 84px 84px #0880d7, 85px 85px #0880d7; }\n.button-longshadow.button-primary:active, .button-longshadow.button-primary.active, .button-longshadow.button-primary.is-active,\n.button-longshadow-right.button-primary:active,\n.button-longshadow-right.button-primary.active,\n.button-longshadow-right.button-primary.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow.button-plain,\n.button-longshadow-right.button-plain {\n    text-shadow: 0px 0px #e6e6e6, 1px 1px #e6e6e6, 2px 2px #e6e6e6, 3px 3px #e6e6e6, 4px 4px #e6e6e6, 5px 5px #e6e6e6, 6px 6px #e6e6e6, 7px 7px #e6e6e6, 8px 8px #e6e6e6, 9px 9px #e6e6e6, 10px 10px #e6e6e6, 11px 11px #e6e6e6, 12px 12px #e6e6e6, 13px 13px #e6e6e6, 14px 14px #e6e6e6, 15px 15px #e6e6e6, 16px 16px #e6e6e6, 17px 17px #e6e6e6, 18px 18px #e6e6e6, 19px 19px #e6e6e6, 20px 20px #e6e6e6, 21px 21px #e6e6e6, 22px 22px #e6e6e6, 23px 23px #e6e6e6, 24px 24px #e6e6e6, 25px 25px #e6e6e6, 26px 26px #e6e6e6, 27px 27px #e6e6e6, 28px 28px #e6e6e6, 29px 29px #e6e6e6, 30px 30px #e6e6e6, 31px 31px #e6e6e6, 32px 32px #e6e6e6, 33px 33px #e6e6e6, 34px 34px #e6e6e6, 35px 35px #e6e6e6, 36px 36px #e6e6e6, 37px 37px #e6e6e6, 38px 38px #e6e6e6, 39px 39px #e6e6e6, 40px 40px #e6e6e6, 41px 41px #e6e6e6, 42px 42px #e6e6e6, 43px 43px #e6e6e6, 44px 44px #e6e6e6, 45px 45px #e6e6e6, 46px 46px #e6e6e6, 47px 47px #e6e6e6, 48px 48px #e6e6e6, 49px 49px #e6e6e6, 50px 50px #e6e6e6, 51px 51px #e6e6e6, 52px 52px #e6e6e6, 53px 53px #e6e6e6, 54px 54px #e6e6e6, 55px 55px #e6e6e6, 56px 56px #e6e6e6, 57px 57px #e6e6e6, 58px 58px #e6e6e6, 59px 59px #e6e6e6, 60px 60px #e6e6e6, 61px 61px #e6e6e6, 62px 62px #e6e6e6, 63px 63px #e6e6e6, 64px 64px #e6e6e6, 65px 65px #e6e6e6, 66px 66px #e6e6e6, 67px 67px #e6e6e6, 68px 68px #e6e6e6, 69px 69px #e6e6e6, 70px 70px #e6e6e6, 71px 71px #e6e6e6, 72px 72px #e6e6e6, 73px 73px #e6e6e6, 74px 74px #e6e6e6, 75px 75px #e6e6e6, 76px 76px #e6e6e6, 77px 77px #e6e6e6, 78px 78px #e6e6e6, 79px 79px #e6e6e6, 80px 80px #e6e6e6, 81px 81px #e6e6e6, 82px 82px #e6e6e6, 83px 83px #e6e6e6, 84px 84px #e6e6e6, 85px 85px #e6e6e6; }\n.button-longshadow.button-plain:active, .button-longshadow.button-plain.active, .button-longshadow.button-plain.is-active,\n.button-longshadow-right.button-plain:active,\n.button-longshadow-right.button-plain.active,\n.button-longshadow-right.button-plain.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow.button-inverse,\n.button-longshadow-right.button-inverse {\n    text-shadow: 0px 0px #090909, 1px 1px #090909, 2px 2px #090909, 3px 3px #090909, 4px 4px #090909, 5px 5px #090909, 6px 6px #090909, 7px 7px #090909, 8px 8px #090909, 9px 9px #090909, 10px 10px #090909, 11px 11px #090909, 12px 12px #090909, 13px 13px #090909, 14px 14px #090909, 15px 15px #090909, 16px 16px #090909, 17px 17px #090909, 18px 18px #090909, 19px 19px #090909, 20px 20px #090909, 21px 21px #090909, 22px 22px #090909, 23px 23px #090909, 24px 24px #090909, 25px 25px #090909, 26px 26px #090909, 27px 27px #090909, 28px 28px #090909, 29px 29px #090909, 30px 30px #090909, 31px 31px #090909, 32px 32px #090909, 33px 33px #090909, 34px 34px #090909, 35px 35px #090909, 36px 36px #090909, 37px 37px #090909, 38px 38px #090909, 39px 39px #090909, 40px 40px #090909, 41px 41px #090909, 42px 42px #090909, 43px 43px #090909, 44px 44px #090909, 45px 45px #090909, 46px 46px #090909, 47px 47px #090909, 48px 48px #090909, 49px 49px #090909, 50px 50px #090909, 51px 51px #090909, 52px 52px #090909, 53px 53px #090909, 54px 54px #090909, 55px 55px #090909, 56px 56px #090909, 57px 57px #090909, 58px 58px #090909, 59px 59px #090909, 60px 60px #090909, 61px 61px #090909, 62px 62px #090909, 63px 63px #090909, 64px 64px #090909, 65px 65px #090909, 66px 66px #090909, 67px 67px #090909, 68px 68px #090909, 69px 69px #090909, 70px 70px #090909, 71px 71px #090909, 72px 72px #090909, 73px 73px #090909, 74px 74px #090909, 75px 75px #090909, 76px 76px #090909, 77px 77px #090909, 78px 78px #090909, 79px 79px #090909, 80px 80px #090909, 81px 81px #090909, 82px 82px #090909, 83px 83px #090909, 84px 84px #090909, 85px 85px #090909; }\n.button-longshadow.button-inverse:active, .button-longshadow.button-inverse.active, .button-longshadow.button-inverse.is-active,\n.button-longshadow-right.button-inverse:active,\n.button-longshadow-right.button-inverse.active,\n.button-longshadow-right.button-inverse.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow.button-action,\n.button-longshadow-right.button-action {\n    text-shadow: 0px 0px #8bc220, 1px 1px #8bc220, 2px 2px #8bc220, 3px 3px #8bc220, 4px 4px #8bc220, 5px 5px #8bc220, 6px 6px #8bc220, 7px 7px #8bc220, 8px 8px #8bc220, 9px 9px #8bc220, 10px 10px #8bc220, 11px 11px #8bc220, 12px 12px #8bc220, 13px 13px #8bc220, 14px 14px #8bc220, 15px 15px #8bc220, 16px 16px #8bc220, 17px 17px #8bc220, 18px 18px #8bc220, 19px 19px #8bc220, 20px 20px #8bc220, 21px 21px #8bc220, 22px 22px #8bc220, 23px 23px #8bc220, 24px 24px #8bc220, 25px 25px #8bc220, 26px 26px #8bc220, 27px 27px #8bc220, 28px 28px #8bc220, 29px 29px #8bc220, 30px 30px #8bc220, 31px 31px #8bc220, 32px 32px #8bc220, 33px 33px #8bc220, 34px 34px #8bc220, 35px 35px #8bc220, 36px 36px #8bc220, 37px 37px #8bc220, 38px 38px #8bc220, 39px 39px #8bc220, 40px 40px #8bc220, 41px 41px #8bc220, 42px 42px #8bc220, 43px 43px #8bc220, 44px 44px #8bc220, 45px 45px #8bc220, 46px 46px #8bc220, 47px 47px #8bc220, 48px 48px #8bc220, 49px 49px #8bc220, 50px 50px #8bc220, 51px 51px #8bc220, 52px 52px #8bc220, 53px 53px #8bc220, 54px 54px #8bc220, 55px 55px #8bc220, 56px 56px #8bc220, 57px 57px #8bc220, 58px 58px #8bc220, 59px 59px #8bc220, 60px 60px #8bc220, 61px 61px #8bc220, 62px 62px #8bc220, 63px 63px #8bc220, 64px 64px #8bc220, 65px 65px #8bc220, 66px 66px #8bc220, 67px 67px #8bc220, 68px 68px #8bc220, 69px 69px #8bc220, 70px 70px #8bc220, 71px 71px #8bc220, 72px 72px #8bc220, 73px 73px #8bc220, 74px 74px #8bc220, 75px 75px #8bc220, 76px 76px #8bc220, 77px 77px #8bc220, 78px 78px #8bc220, 79px 79px #8bc220, 80px 80px #8bc220, 81px 81px #8bc220, 82px 82px #8bc220, 83px 83px #8bc220, 84px 84px #8bc220, 85px 85px #8bc220; }\n.button-longshadow.button-action:active, .button-longshadow.button-action.active, .button-longshadow.button-action.is-active,\n.button-longshadow-right.button-action:active,\n.button-longshadow-right.button-action.active,\n.button-longshadow-right.button-action.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow.button-highlight,\n.button-longshadow-right.button-highlight {\n    text-shadow: 0px 0px #e59501, 1px 1px #e59501, 2px 2px #e59501, 3px 3px #e59501, 4px 4px #e59501, 5px 5px #e59501, 6px 6px #e59501, 7px 7px #e59501, 8px 8px #e59501, 9px 9px #e59501, 10px 10px #e59501, 11px 11px #e59501, 12px 12px #e59501, 13px 13px #e59501, 14px 14px #e59501, 15px 15px #e59501, 16px 16px #e59501, 17px 17px #e59501, 18px 18px #e59501, 19px 19px #e59501, 20px 20px #e59501, 21px 21px #e59501, 22px 22px #e59501, 23px 23px #e59501, 24px 24px #e59501, 25px 25px #e59501, 26px 26px #e59501, 27px 27px #e59501, 28px 28px #e59501, 29px 29px #e59501, 30px 30px #e59501, 31px 31px #e59501, 32px 32px #e59501, 33px 33px #e59501, 34px 34px #e59501, 35px 35px #e59501, 36px 36px #e59501, 37px 37px #e59501, 38px 38px #e59501, 39px 39px #e59501, 40px 40px #e59501, 41px 41px #e59501, 42px 42px #e59501, 43px 43px #e59501, 44px 44px #e59501, 45px 45px #e59501, 46px 46px #e59501, 47px 47px #e59501, 48px 48px #e59501, 49px 49px #e59501, 50px 50px #e59501, 51px 51px #e59501, 52px 52px #e59501, 53px 53px #e59501, 54px 54px #e59501, 55px 55px #e59501, 56px 56px #e59501, 57px 57px #e59501, 58px 58px #e59501, 59px 59px #e59501, 60px 60px #e59501, 61px 61px #e59501, 62px 62px #e59501, 63px 63px #e59501, 64px 64px #e59501, 65px 65px #e59501, 66px 66px #e59501, 67px 67px #e59501, 68px 68px #e59501, 69px 69px #e59501, 70px 70px #e59501, 71px 71px #e59501, 72px 72px #e59501, 73px 73px #e59501, 74px 74px #e59501, 75px 75px #e59501, 76px 76px #e59501, 77px 77px #e59501, 78px 78px #e59501, 79px 79px #e59501, 80px 80px #e59501, 81px 81px #e59501, 82px 82px #e59501, 83px 83px #e59501, 84px 84px #e59501, 85px 85px #e59501; }\n.button-longshadow.button-highlight:active, .button-longshadow.button-highlight.active, .button-longshadow.button-highlight.is-active,\n.button-longshadow-right.button-highlight:active,\n.button-longshadow-right.button-highlight.active,\n.button-longshadow-right.button-highlight.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow.button-caution,\n.button-longshadow-right.button-caution {\n    text-shadow: 0px 0px #ff1022, 1px 1px #ff1022, 2px 2px #ff1022, 3px 3px #ff1022, 4px 4px #ff1022, 5px 5px #ff1022, 6px 6px #ff1022, 7px 7px #ff1022, 8px 8px #ff1022, 9px 9px #ff1022, 10px 10px #ff1022, 11px 11px #ff1022, 12px 12px #ff1022, 13px 13px #ff1022, 14px 14px #ff1022, 15px 15px #ff1022, 16px 16px #ff1022, 17px 17px #ff1022, 18px 18px #ff1022, 19px 19px #ff1022, 20px 20px #ff1022, 21px 21px #ff1022, 22px 22px #ff1022, 23px 23px #ff1022, 24px 24px #ff1022, 25px 25px #ff1022, 26px 26px #ff1022, 27px 27px #ff1022, 28px 28px #ff1022, 29px 29px #ff1022, 30px 30px #ff1022, 31px 31px #ff1022, 32px 32px #ff1022, 33px 33px #ff1022, 34px 34px #ff1022, 35px 35px #ff1022, 36px 36px #ff1022, 37px 37px #ff1022, 38px 38px #ff1022, 39px 39px #ff1022, 40px 40px #ff1022, 41px 41px #ff1022, 42px 42px #ff1022, 43px 43px #ff1022, 44px 44px #ff1022, 45px 45px #ff1022, 46px 46px #ff1022, 47px 47px #ff1022, 48px 48px #ff1022, 49px 49px #ff1022, 50px 50px #ff1022, 51px 51px #ff1022, 52px 52px #ff1022, 53px 53px #ff1022, 54px 54px #ff1022, 55px 55px #ff1022, 56px 56px #ff1022, 57px 57px #ff1022, 58px 58px #ff1022, 59px 59px #ff1022, 60px 60px #ff1022, 61px 61px #ff1022, 62px 62px #ff1022, 63px 63px #ff1022, 64px 64px #ff1022, 65px 65px #ff1022, 66px 66px #ff1022, 67px 67px #ff1022, 68px 68px #ff1022, 69px 69px #ff1022, 70px 70px #ff1022, 71px 71px #ff1022, 72px 72px #ff1022, 73px 73px #ff1022, 74px 74px #ff1022, 75px 75px #ff1022, 76px 76px #ff1022, 77px 77px #ff1022, 78px 78px #ff1022, 79px 79px #ff1022, 80px 80px #ff1022, 81px 81px #ff1022, 82px 82px #ff1022, 83px 83px #ff1022, 84px 84px #ff1022, 85px 85px #ff1022; }\n.button-longshadow.button-caution:active, .button-longshadow.button-caution.active, .button-longshadow.button-caution.is-active,\n.button-longshadow-right.button-caution:active,\n.button-longshadow-right.button-caution.active,\n.button-longshadow-right.button-caution.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow.button-royal,\n.button-longshadow-right.button-royal {\n    text-shadow: 0px 0px #5246e2, 1px 1px #5246e2, 2px 2px #5246e2, 3px 3px #5246e2, 4px 4px #5246e2, 5px 5px #5246e2, 6px 6px #5246e2, 7px 7px #5246e2, 8px 8px #5246e2, 9px 9px #5246e2, 10px 10px #5246e2, 11px 11px #5246e2, 12px 12px #5246e2, 13px 13px #5246e2, 14px 14px #5246e2, 15px 15px #5246e2, 16px 16px #5246e2, 17px 17px #5246e2, 18px 18px #5246e2, 19px 19px #5246e2, 20px 20px #5246e2, 21px 21px #5246e2, 22px 22px #5246e2, 23px 23px #5246e2, 24px 24px #5246e2, 25px 25px #5246e2, 26px 26px #5246e2, 27px 27px #5246e2, 28px 28px #5246e2, 29px 29px #5246e2, 30px 30px #5246e2, 31px 31px #5246e2, 32px 32px #5246e2, 33px 33px #5246e2, 34px 34px #5246e2, 35px 35px #5246e2, 36px 36px #5246e2, 37px 37px #5246e2, 38px 38px #5246e2, 39px 39px #5246e2, 40px 40px #5246e2, 41px 41px #5246e2, 42px 42px #5246e2, 43px 43px #5246e2, 44px 44px #5246e2, 45px 45px #5246e2, 46px 46px #5246e2, 47px 47px #5246e2, 48px 48px #5246e2, 49px 49px #5246e2, 50px 50px #5246e2, 51px 51px #5246e2, 52px 52px #5246e2, 53px 53px #5246e2, 54px 54px #5246e2, 55px 55px #5246e2, 56px 56px #5246e2, 57px 57px #5246e2, 58px 58px #5246e2, 59px 59px #5246e2, 60px 60px #5246e2, 61px 61px #5246e2, 62px 62px #5246e2, 63px 63px #5246e2, 64px 64px #5246e2, 65px 65px #5246e2, 66px 66px #5246e2, 67px 67px #5246e2, 68px 68px #5246e2, 69px 69px #5246e2, 70px 70px #5246e2, 71px 71px #5246e2, 72px 72px #5246e2, 73px 73px #5246e2, 74px 74px #5246e2, 75px 75px #5246e2, 76px 76px #5246e2, 77px 77px #5246e2, 78px 78px #5246e2, 79px 79px #5246e2, 80px 80px #5246e2, 81px 81px #5246e2, 82px 82px #5246e2, 83px 83px #5246e2, 84px 84px #5246e2, 85px 85px #5246e2; }\n.button-longshadow.button-royal:active, .button-longshadow.button-royal.active, .button-longshadow.button-royal.is-active,\n.button-longshadow-right.button-royal:active,\n.button-longshadow-right.button-royal.active,\n.button-longshadow-right.button-royal.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n\n/*\n* Shadow Left\n*\n*/\n.button-longshadow-left {\n    overflow: hidden; }\n.button-longshadow-left.button-primary {\n    text-shadow: 0px 0px #0880d7, -1px 1px #0880d7, -2px 2px #0880d7, -3px 3px #0880d7, -4px 4px #0880d7, -5px 5px #0880d7, -6px 6px #0880d7, -7px 7px #0880d7, -8px 8px #0880d7, -9px 9px #0880d7, -10px 10px #0880d7, -11px 11px #0880d7, -12px 12px #0880d7, -13px 13px #0880d7, -14px 14px #0880d7, -15px 15px #0880d7, -16px 16px #0880d7, -17px 17px #0880d7, -18px 18px #0880d7, -19px 19px #0880d7, -20px 20px #0880d7, -21px 21px #0880d7, -22px 22px #0880d7, -23px 23px #0880d7, -24px 24px #0880d7, -25px 25px #0880d7, -26px 26px #0880d7, -27px 27px #0880d7, -28px 28px #0880d7, -29px 29px #0880d7, -30px 30px #0880d7, -31px 31px #0880d7, -32px 32px #0880d7, -33px 33px #0880d7, -34px 34px #0880d7, -35px 35px #0880d7, -36px 36px #0880d7, -37px 37px #0880d7, -38px 38px #0880d7, -39px 39px #0880d7, -40px 40px #0880d7, -41px 41px #0880d7, -42px 42px #0880d7, -43px 43px #0880d7, -44px 44px #0880d7, -45px 45px #0880d7, -46px 46px #0880d7, -47px 47px #0880d7, -48px 48px #0880d7, -49px 49px #0880d7, -50px 50px #0880d7, -51px 51px #0880d7, -52px 52px #0880d7, -53px 53px #0880d7, -54px 54px #0880d7, -55px 55px #0880d7, -56px 56px #0880d7, -57px 57px #0880d7, -58px 58px #0880d7, -59px 59px #0880d7, -60px 60px #0880d7, -61px 61px #0880d7, -62px 62px #0880d7, -63px 63px #0880d7, -64px 64px #0880d7, -65px 65px #0880d7, -66px 66px #0880d7, -67px 67px #0880d7, -68px 68px #0880d7, -69px 69px #0880d7, -70px 70px #0880d7, -71px 71px #0880d7, -72px 72px #0880d7, -73px 73px #0880d7, -74px 74px #0880d7, -75px 75px #0880d7, -76px 76px #0880d7, -77px 77px #0880d7, -78px 78px #0880d7, -79px 79px #0880d7, -80px 80px #0880d7, -81px 81px #0880d7, -82px 82px #0880d7, -83px 83px #0880d7, -84px 84px #0880d7, -85px 85px #0880d7; }\n.button-longshadow-left.button-primary:active, .button-longshadow-left.button-primary.active, .button-longshadow-left.button-primary.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow-left.button-plain {\n    text-shadow: 0px 0px #e6e6e6, -1px 1px #e6e6e6, -2px 2px #e6e6e6, -3px 3px #e6e6e6, -4px 4px #e6e6e6, -5px 5px #e6e6e6, -6px 6px #e6e6e6, -7px 7px #e6e6e6, -8px 8px #e6e6e6, -9px 9px #e6e6e6, -10px 10px #e6e6e6, -11px 11px #e6e6e6, -12px 12px #e6e6e6, -13px 13px #e6e6e6, -14px 14px #e6e6e6, -15px 15px #e6e6e6, -16px 16px #e6e6e6, -17px 17px #e6e6e6, -18px 18px #e6e6e6, -19px 19px #e6e6e6, -20px 20px #e6e6e6, -21px 21px #e6e6e6, -22px 22px #e6e6e6, -23px 23px #e6e6e6, -24px 24px #e6e6e6, -25px 25px #e6e6e6, -26px 26px #e6e6e6, -27px 27px #e6e6e6, -28px 28px #e6e6e6, -29px 29px #e6e6e6, -30px 30px #e6e6e6, -31px 31px #e6e6e6, -32px 32px #e6e6e6, -33px 33px #e6e6e6, -34px 34px #e6e6e6, -35px 35px #e6e6e6, -36px 36px #e6e6e6, -37px 37px #e6e6e6, -38px 38px #e6e6e6, -39px 39px #e6e6e6, -40px 40px #e6e6e6, -41px 41px #e6e6e6, -42px 42px #e6e6e6, -43px 43px #e6e6e6, -44px 44px #e6e6e6, -45px 45px #e6e6e6, -46px 46px #e6e6e6, -47px 47px #e6e6e6, -48px 48px #e6e6e6, -49px 49px #e6e6e6, -50px 50px #e6e6e6, -51px 51px #e6e6e6, -52px 52px #e6e6e6, -53px 53px #e6e6e6, -54px 54px #e6e6e6, -55px 55px #e6e6e6, -56px 56px #e6e6e6, -57px 57px #e6e6e6, -58px 58px #e6e6e6, -59px 59px #e6e6e6, -60px 60px #e6e6e6, -61px 61px #e6e6e6, -62px 62px #e6e6e6, -63px 63px #e6e6e6, -64px 64px #e6e6e6, -65px 65px #e6e6e6, -66px 66px #e6e6e6, -67px 67px #e6e6e6, -68px 68px #e6e6e6, -69px 69px #e6e6e6, -70px 70px #e6e6e6, -71px 71px #e6e6e6, -72px 72px #e6e6e6, -73px 73px #e6e6e6, -74px 74px #e6e6e6, -75px 75px #e6e6e6, -76px 76px #e6e6e6, -77px 77px #e6e6e6, -78px 78px #e6e6e6, -79px 79px #e6e6e6, -80px 80px #e6e6e6, -81px 81px #e6e6e6, -82px 82px #e6e6e6, -83px 83px #e6e6e6, -84px 84px #e6e6e6, -85px 85px #e6e6e6; }\n.button-longshadow-left.button-plain:active, .button-longshadow-left.button-plain.active, .button-longshadow-left.button-plain.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow-left.button-inverse {\n    text-shadow: 0px 0px #090909, -1px 1px #090909, -2px 2px #090909, -3px 3px #090909, -4px 4px #090909, -5px 5px #090909, -6px 6px #090909, -7px 7px #090909, -8px 8px #090909, -9px 9px #090909, -10px 10px #090909, -11px 11px #090909, -12px 12px #090909, -13px 13px #090909, -14px 14px #090909, -15px 15px #090909, -16px 16px #090909, -17px 17px #090909, -18px 18px #090909, -19px 19px #090909, -20px 20px #090909, -21px 21px #090909, -22px 22px #090909, -23px 23px #090909, -24px 24px #090909, -25px 25px #090909, -26px 26px #090909, -27px 27px #090909, -28px 28px #090909, -29px 29px #090909, -30px 30px #090909, -31px 31px #090909, -32px 32px #090909, -33px 33px #090909, -34px 34px #090909, -35px 35px #090909, -36px 36px #090909, -37px 37px #090909, -38px 38px #090909, -39px 39px #090909, -40px 40px #090909, -41px 41px #090909, -42px 42px #090909, -43px 43px #090909, -44px 44px #090909, -45px 45px #090909, -46px 46px #090909, -47px 47px #090909, -48px 48px #090909, -49px 49px #090909, -50px 50px #090909, -51px 51px #090909, -52px 52px #090909, -53px 53px #090909, -54px 54px #090909, -55px 55px #090909, -56px 56px #090909, -57px 57px #090909, -58px 58px #090909, -59px 59px #090909, -60px 60px #090909, -61px 61px #090909, -62px 62px #090909, -63px 63px #090909, -64px 64px #090909, -65px 65px #090909, -66px 66px #090909, -67px 67px #090909, -68px 68px #090909, -69px 69px #090909, -70px 70px #090909, -71px 71px #090909, -72px 72px #090909, -73px 73px #090909, -74px 74px #090909, -75px 75px #090909, -76px 76px #090909, -77px 77px #090909, -78px 78px #090909, -79px 79px #090909, -80px 80px #090909, -81px 81px #090909, -82px 82px #090909, -83px 83px #090909, -84px 84px #090909, -85px 85px #090909; }\n.button-longshadow-left.button-inverse:active, .button-longshadow-left.button-inverse.active, .button-longshadow-left.button-inverse.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow-left.button-action {\n    text-shadow: 0px 0px #8bc220, -1px 1px #8bc220, -2px 2px #8bc220, -3px 3px #8bc220, -4px 4px #8bc220, -5px 5px #8bc220, -6px 6px #8bc220, -7px 7px #8bc220, -8px 8px #8bc220, -9px 9px #8bc220, -10px 10px #8bc220, -11px 11px #8bc220, -12px 12px #8bc220, -13px 13px #8bc220, -14px 14px #8bc220, -15px 15px #8bc220, -16px 16px #8bc220, -17px 17px #8bc220, -18px 18px #8bc220, -19px 19px #8bc220, -20px 20px #8bc220, -21px 21px #8bc220, -22px 22px #8bc220, -23px 23px #8bc220, -24px 24px #8bc220, -25px 25px #8bc220, -26px 26px #8bc220, -27px 27px #8bc220, -28px 28px #8bc220, -29px 29px #8bc220, -30px 30px #8bc220, -31px 31px #8bc220, -32px 32px #8bc220, -33px 33px #8bc220, -34px 34px #8bc220, -35px 35px #8bc220, -36px 36px #8bc220, -37px 37px #8bc220, -38px 38px #8bc220, -39px 39px #8bc220, -40px 40px #8bc220, -41px 41px #8bc220, -42px 42px #8bc220, -43px 43px #8bc220, -44px 44px #8bc220, -45px 45px #8bc220, -46px 46px #8bc220, -47px 47px #8bc220, -48px 48px #8bc220, -49px 49px #8bc220, -50px 50px #8bc220, -51px 51px #8bc220, -52px 52px #8bc220, -53px 53px #8bc220, -54px 54px #8bc220, -55px 55px #8bc220, -56px 56px #8bc220, -57px 57px #8bc220, -58px 58px #8bc220, -59px 59px #8bc220, -60px 60px #8bc220, -61px 61px #8bc220, -62px 62px #8bc220, -63px 63px #8bc220, -64px 64px #8bc220, -65px 65px #8bc220, -66px 66px #8bc220, -67px 67px #8bc220, -68px 68px #8bc220, -69px 69px #8bc220, -70px 70px #8bc220, -71px 71px #8bc220, -72px 72px #8bc220, -73px 73px #8bc220, -74px 74px #8bc220, -75px 75px #8bc220, -76px 76px #8bc220, -77px 77px #8bc220, -78px 78px #8bc220, -79px 79px #8bc220, -80px 80px #8bc220, -81px 81px #8bc220, -82px 82px #8bc220, -83px 83px #8bc220, -84px 84px #8bc220, -85px 85px #8bc220; }\n.button-longshadow-left.button-action:active, .button-longshadow-left.button-action.active, .button-longshadow-left.button-action.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow-left.button-highlight {\n    text-shadow: 0px 0px #e59501, -1px 1px #e59501, -2px 2px #e59501, -3px 3px #e59501, -4px 4px #e59501, -5px 5px #e59501, -6px 6px #e59501, -7px 7px #e59501, -8px 8px #e59501, -9px 9px #e59501, -10px 10px #e59501, -11px 11px #e59501, -12px 12px #e59501, -13px 13px #e59501, -14px 14px #e59501, -15px 15px #e59501, -16px 16px #e59501, -17px 17px #e59501, -18px 18px #e59501, -19px 19px #e59501, -20px 20px #e59501, -21px 21px #e59501, -22px 22px #e59501, -23px 23px #e59501, -24px 24px #e59501, -25px 25px #e59501, -26px 26px #e59501, -27px 27px #e59501, -28px 28px #e59501, -29px 29px #e59501, -30px 30px #e59501, -31px 31px #e59501, -32px 32px #e59501, -33px 33px #e59501, -34px 34px #e59501, -35px 35px #e59501, -36px 36px #e59501, -37px 37px #e59501, -38px 38px #e59501, -39px 39px #e59501, -40px 40px #e59501, -41px 41px #e59501, -42px 42px #e59501, -43px 43px #e59501, -44px 44px #e59501, -45px 45px #e59501, -46px 46px #e59501, -47px 47px #e59501, -48px 48px #e59501, -49px 49px #e59501, -50px 50px #e59501, -51px 51px #e59501, -52px 52px #e59501, -53px 53px #e59501, -54px 54px #e59501, -55px 55px #e59501, -56px 56px #e59501, -57px 57px #e59501, -58px 58px #e59501, -59px 59px #e59501, -60px 60px #e59501, -61px 61px #e59501, -62px 62px #e59501, -63px 63px #e59501, -64px 64px #e59501, -65px 65px #e59501, -66px 66px #e59501, -67px 67px #e59501, -68px 68px #e59501, -69px 69px #e59501, -70px 70px #e59501, -71px 71px #e59501, -72px 72px #e59501, -73px 73px #e59501, -74px 74px #e59501, -75px 75px #e59501, -76px 76px #e59501, -77px 77px #e59501, -78px 78px #e59501, -79px 79px #e59501, -80px 80px #e59501, -81px 81px #e59501, -82px 82px #e59501, -83px 83px #e59501, -84px 84px #e59501, -85px 85px #e59501; }\n.button-longshadow-left.button-highlight:active, .button-longshadow-left.button-highlight.active, .button-longshadow-left.button-highlight.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow-left.button-caution {\n    text-shadow: 0px 0px #ff1022, -1px 1px #ff1022, -2px 2px #ff1022, -3px 3px #ff1022, -4px 4px #ff1022, -5px 5px #ff1022, -6px 6px #ff1022, -7px 7px #ff1022, -8px 8px #ff1022, -9px 9px #ff1022, -10px 10px #ff1022, -11px 11px #ff1022, -12px 12px #ff1022, -13px 13px #ff1022, -14px 14px #ff1022, -15px 15px #ff1022, -16px 16px #ff1022, -17px 17px #ff1022, -18px 18px #ff1022, -19px 19px #ff1022, -20px 20px #ff1022, -21px 21px #ff1022, -22px 22px #ff1022, -23px 23px #ff1022, -24px 24px #ff1022, -25px 25px #ff1022, -26px 26px #ff1022, -27px 27px #ff1022, -28px 28px #ff1022, -29px 29px #ff1022, -30px 30px #ff1022, -31px 31px #ff1022, -32px 32px #ff1022, -33px 33px #ff1022, -34px 34px #ff1022, -35px 35px #ff1022, -36px 36px #ff1022, -37px 37px #ff1022, -38px 38px #ff1022, -39px 39px #ff1022, -40px 40px #ff1022, -41px 41px #ff1022, -42px 42px #ff1022, -43px 43px #ff1022, -44px 44px #ff1022, -45px 45px #ff1022, -46px 46px #ff1022, -47px 47px #ff1022, -48px 48px #ff1022, -49px 49px #ff1022, -50px 50px #ff1022, -51px 51px #ff1022, -52px 52px #ff1022, -53px 53px #ff1022, -54px 54px #ff1022, -55px 55px #ff1022, -56px 56px #ff1022, -57px 57px #ff1022, -58px 58px #ff1022, -59px 59px #ff1022, -60px 60px #ff1022, -61px 61px #ff1022, -62px 62px #ff1022, -63px 63px #ff1022, -64px 64px #ff1022, -65px 65px #ff1022, -66px 66px #ff1022, -67px 67px #ff1022, -68px 68px #ff1022, -69px 69px #ff1022, -70px 70px #ff1022, -71px 71px #ff1022, -72px 72px #ff1022, -73px 73px #ff1022, -74px 74px #ff1022, -75px 75px #ff1022, -76px 76px #ff1022, -77px 77px #ff1022, -78px 78px #ff1022, -79px 79px #ff1022, -80px 80px #ff1022, -81px 81px #ff1022, -82px 82px #ff1022, -83px 83px #ff1022, -84px 84px #ff1022, -85px 85px #ff1022; }\n.button-longshadow-left.button-caution:active, .button-longshadow-left.button-caution.active, .button-longshadow-left.button-caution.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n.button-longshadow-left.button-royal {\n    text-shadow: 0px 0px #5246e2, -1px 1px #5246e2, -2px 2px #5246e2, -3px 3px #5246e2, -4px 4px #5246e2, -5px 5px #5246e2, -6px 6px #5246e2, -7px 7px #5246e2, -8px 8px #5246e2, -9px 9px #5246e2, -10px 10px #5246e2, -11px 11px #5246e2, -12px 12px #5246e2, -13px 13px #5246e2, -14px 14px #5246e2, -15px 15px #5246e2, -16px 16px #5246e2, -17px 17px #5246e2, -18px 18px #5246e2, -19px 19px #5246e2, -20px 20px #5246e2, -21px 21px #5246e2, -22px 22px #5246e2, -23px 23px #5246e2, -24px 24px #5246e2, -25px 25px #5246e2, -26px 26px #5246e2, -27px 27px #5246e2, -28px 28px #5246e2, -29px 29px #5246e2, -30px 30px #5246e2, -31px 31px #5246e2, -32px 32px #5246e2, -33px 33px #5246e2, -34px 34px #5246e2, -35px 35px #5246e2, -36px 36px #5246e2, -37px 37px #5246e2, -38px 38px #5246e2, -39px 39px #5246e2, -40px 40px #5246e2, -41px 41px #5246e2, -42px 42px #5246e2, -43px 43px #5246e2, -44px 44px #5246e2, -45px 45px #5246e2, -46px 46px #5246e2, -47px 47px #5246e2, -48px 48px #5246e2, -49px 49px #5246e2, -50px 50px #5246e2, -51px 51px #5246e2, -52px 52px #5246e2, -53px 53px #5246e2, -54px 54px #5246e2, -55px 55px #5246e2, -56px 56px #5246e2, -57px 57px #5246e2, -58px 58px #5246e2, -59px 59px #5246e2, -60px 60px #5246e2, -61px 61px #5246e2, -62px 62px #5246e2, -63px 63px #5246e2, -64px 64px #5246e2, -65px 65px #5246e2, -66px 66px #5246e2, -67px 67px #5246e2, -68px 68px #5246e2, -69px 69px #5246e2, -70px 70px #5246e2, -71px 71px #5246e2, -72px 72px #5246e2, -73px 73px #5246e2, -74px 74px #5246e2, -75px 75px #5246e2, -76px 76px #5246e2, -77px 77px #5246e2, -78px 78px #5246e2, -79px 79px #5246e2, -80px 80px #5246e2, -81px 81px #5246e2, -82px 82px #5246e2, -83px 83px #5246e2, -84px 84px #5246e2, -85px 85px #5246e2; }\n.button-longshadow-left.button-royal:active, .button-longshadow-left.button-royal.active, .button-longshadow-left.button-royal.is-active {\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); }\n\n/*\n* Button Sizes\n*\n* This file creates the various button sizes\n* (ex. .button-large, .button-small, etc.)\n*/\n.button-giant {\n    font-size: 28px;\n    height: 70px;\n    line-height: 70px;\n    padding: 0 70px; }\n\n.button-jumbo {\n    font-size: 24px;\n    height: 60px;\n    line-height: 60px;\n    padding: 0 60px; }\n\n.button-large {\n    font-size: 20px;\n    height: 50px;\n    line-height: 50px;\n    padding: 0 50px; }\n\n.button-normal {\n    font-size: 16px;\n    height: 40px;\n    line-height: 40px;\n    padding: 0 40px; }\n\n.button-small {\n    font-size: 12px;\n    height: 30px;\n    line-height: 30px;\n    padding: 0 30px; }\n\n.button-tiny {\n    font-size: 9.6px;\n    height: 24px;\n    line-height: 24px;\n    padding: 0 24px; }"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/css/common.css",
    "content": ".bs-wizard {\n    margin-top: 40px;\n}\n\n/*Form Wizard*/\n.bs-wizard {\n    border-bottom: solid 1px #e0e0e0;\n    padding: 0 0 10px 0;\n}\n\n.bs-wizard > .bs-wizard-step {\n    padding: 0;\n    position: relative;\n}\n\n.bs-wizard > .bs-wizard-step + .bs-wizard-step {\n}\n\n.bs-wizard > .bs-wizard-step .bs-wizard-stepnum {\n    color: #595959;\n    font-size: 16px;\n    margin-bottom: 5px;\n}\n\n.bs-wizard > .bs-wizard-step .bs-wizard-info {\n    color: #999;\n    font-size: 14px;\n}\n\n.bs-wizard > .bs-wizard-step > .bs-wizard-dot {\n    position: absolute;\n    width: 30px;\n    height: 30px;\n    display: block;\n    background: #fbe8aa;\n    top: 45px;\n    left: 50%;\n    margin-top: -15px;\n    margin-left: -15px;\n    border-radius: 50%;\n}\n\n.bs-wizard > .bs-wizard-step > .bs-wizard-dot:after {\n    content: ' ';\n    width: 14px;\n    height: 14px;\n    background: #fbbd19;\n    border-radius: 50px;\n    position: absolute;\n    top: 8px;\n    left: 8px;\n}\n\n.bs-wizard > .bs-wizard-step > .progress {\n    position: relative;\n    border-radius: 0px;\n    height: 8px;\n    box-shadow: none;\n    margin: 20px 0;\n}\n\n.bs-wizard > .bs-wizard-step > .progress > .progress-bar {\n    width: 0px;\n    box-shadow: none;\n    background: #fbe8aa;\n}\n\n.bs-wizard > .bs-wizard-step.complete > .progress > .progress-bar {\n    width: 100%;\n}\n\n.bs-wizard > .bs-wizard-step.active > .progress > .progress-bar {\n    width: 50%;\n}\n\n.bs-wizard > .bs-wizard-step:first-child.active > .progress > .progress-bar {\n    width: 0%;\n}\n\n.bs-wizard > .bs-wizard-step:last-child.active > .progress > .progress-bar {\n    width: 100%;\n}\n\n.bs-wizard > .bs-wizard-step.disabled > .bs-wizard-dot {\n    background-color: #f5f5f5;\n}\n\n.bs-wizard > .bs-wizard-step.disabled > .bs-wizard-dot:after {\n    opacity: 0;\n}\n\n.bs-wizard > .bs-wizard-step:first-child > .progress {\n    left: 50%;\n    width: 50%;\n}\n\n.bs-wizard > .bs-wizard-step:last-child > .progress {\n    width: 50%;\n}\n\n.bs-wizard > .bs-wizard-step.disabled a.bs-wizard-dot {\n    pointer-events: none;\n}\n\n/*END Form Wizard*/"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/css/custom.css",
    "content": ".progress-fs-1 {\n    font-size: 1rem;\n    font-weight: 400;\n    line-height: 1.5;\n}\n\n.table-sm > .progress-fs-1 {\n    font-size: .75rem;\n    font-weight: 400;\n    line-height: 1.5;\n}\n\n.progress-bar > span {\n    font-weight: bolder !important;\n    text-align: start;\n}\n\n.progress {\n    height: auto;\n    border-radius: var(--bs-progress-border-radius);\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/css/githubmd.css",
    "content": ".markdown-body {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n    font-size: 16px;\n    line-height: 1.5;\n    word-wrap: break-word\n}\n\n.markdown-body::before {\n    display: table;\n    content: \"\"\n}\n\n.markdown-body::after {\n    display: table;\n    clear: both;\n    content: \"\"\n}\n\n.markdown-body>*:first-child {\n    margin-top: 0 !important\n}\n\n.markdown-body>*:last-child {\n    margin-bottom: 0 !important\n}\n\n.markdown-body a:not([href]) {\n    color: inherit;\n    text-decoration: none\n}\n\n.markdown-body .absent {\n    color: #cb2431\n}\n\n.markdown-body .anchor {\n    float: left;\n    padding-right: 4px;\n    margin-left: -20px;\n    line-height: 1\n}\n\n.markdown-body .anchor:focus {\n    outline: none\n}\n\n.markdown-body p,\n.markdown-body blockquote,\n.markdown-body ul,\n.markdown-body ol,\n.markdown-body dl,\n.markdown-body table,\n.markdown-body pre {\n    margin-top: 0;\n    margin-bottom: 16px\n}\n\n.markdown-body hr {\n    height: 0.25em;\n    padding: 0;\n    margin: 24px 0;\n    background-color: #e1e4e8;\n    border: 0\n}\n\n.markdown-body blockquote {\n    padding: 0 1em;\n    color: #6a737d;\n    border-left: 0.25em solid #dfe2e5\n}\n\n.markdown-body blockquote>:first-child {\n    margin-top: 0\n}\n\n.markdown-body blockquote>:last-child {\n    margin-bottom: 0\n}\n\n.markdown-body kbd {\n    display: inline-block;\n    padding: 3px 5px;\n    font-size: 11px;\n    line-height: 10px;\n    color: #444d56;\n    vertical-align: middle;\n    background-color: #fafbfc;\n    border: solid 1px #c6cbd1;\n    border-bottom-color: #959da5;\n    border-radius: 3px;\n    box-shadow: inset 0 -1px 0 #959da5\n}\n\n.markdown-body h1,\n.markdown-body h2,\n.markdown-body h3,\n.markdown-body h4,\n.markdown-body h5,\n.markdown-body h6 {\n    margin-top: 24px;\n    margin-bottom: 16px;\n    font-weight: 600;\n    line-height: 1.25\n}\n\n.markdown-body h1 .octicon-link,\n.markdown-body h2 .octicon-link,\n.markdown-body h3 .octicon-link,\n.markdown-body h4 .octicon-link,\n.markdown-body h5 .octicon-link,\n.markdown-body h6 .octicon-link {\n    color: #1b1f23;\n    vertical-align: middle;\n    visibility: hidden\n}\n\n.markdown-body h1:hover .anchor,\n.markdown-body h2:hover .anchor,\n.markdown-body h3:hover .anchor,\n.markdown-body h4:hover .anchor,\n.markdown-body h5:hover .anchor,\n.markdown-body h6:hover .anchor {\n    text-decoration: none\n}\n\n.markdown-body h1:hover .anchor .octicon-link,\n.markdown-body h2:hover .anchor .octicon-link,\n.markdown-body h3:hover .anchor .octicon-link,\n.markdown-body h4:hover .anchor .octicon-link,\n.markdown-body h5:hover .anchor .octicon-link,\n.markdown-body h6:hover .anchor .octicon-link {\n    visibility: visible\n}\n\n.markdown-body h1 tt,\n.markdown-body h1 code,\n.markdown-body h2 tt,\n.markdown-body h2 code,\n.markdown-body h3 tt,\n.markdown-body h3 code,\n.markdown-body h4 tt,\n.markdown-body h4 code,\n.markdown-body h5 tt,\n.markdown-body h5 code,\n.markdown-body h6 tt,\n.markdown-body h6 code {\n    font-size: inherit\n}\n\n.markdown-body h1 {\n    padding-bottom: 0.3em;\n    font-size: 2em;\n    border-bottom: 1px solid #eaecef\n}\n\n.markdown-body h2 {\n    padding-bottom: 0.3em;\n    font-size: 1.5em;\n    border-bottom: 1px solid #eaecef\n}\n\n.markdown-body h3 {\n    font-size: 1.25em\n}\n\n.markdown-body h4 {\n    font-size: 1em\n}\n\n.markdown-body h5 {\n    font-size: 0.875em\n}\n\n.markdown-body h6 {\n    font-size: 0.85em;\n    color: #6a737d\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/css/jobindex.css",
    "content": "/**\n* Template Name: NiceAdmin\n* Updated: Mar 09 2023 with Bootstrap v5.2.3\n* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/\n* Author: BootstrapMade.com\n* License: https://bootstrapmade.com/license/\n*/\n\n/*--------------------------------------------------------------\n# General\n--------------------------------------------------------------*/\n\nlabel {\n  margin-right: 3rem !important;\n  display: flex !important;\n  justify-content: flex-end !important;\n}\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/css/mem-cloud.css",
    "content": "table, td{\n    font:100% Arial, Helvetica, sans-serif;\n}\ntable{width:100%;border-collapse:collapse;margin:1em 0;}\nth, td{text-align:left;padding:.5em;border:1px solid #fff;}\ntd{background:#e5f1f4;}\n/* tablecloth styles */\ntr.even td{background:#e5f1f4;}\ntr.odd td{background:#f8fbfc;}\n\n\nth.over, tr.even th.over, tr.odd th.over{background:#4a98af;}\nth.down, tr.even th.down, tr.odd th.down{background:#bce774;}\nth.selected, tr.even th.selected, tr.odd th.selected{}\n\n\ntd.over, tr.even td.over, tr.odd td.over{background:#ecfbd4;}\ntd.down, tr.even td.down, tr.odd td.down{background:#bce774;color:#fff;}\ntd.selected, tr.even td.selected, tr.odd td.selected{background:#bce774;color:#555;}\n\n\n/* use this if you want to apply different styling to empty table cells*/\ntd.empty, tr.odd td.empty, tr.even td.empty{background:#fff;}\n\n\n\n\n.margin-custom-bottom0{\n    margin-bottom:0px;\n    padding: 0px 0px;\n}\n.margin-custom-top10{\n    margin-top: 10px;\n}\n\n\ndiv.console { font-size: 14px; word-wrap: break-word;}\ndiv.console div.jquery-console-inner\n{ width:100%; height:400px; background:#333; padding:0.5em;\n    overflow:auto }\ndiv.console div.jquery-console-prompt-box\n{ color:#fff; font-family:monospace; }\ndiv.console div.jquery-console-focus span.jquery-console-cursor\n{ background:#fefefe; color:#333; font-weight:bold }\ndiv.console div.jquery-console-message-error\n{ color:#ef0505; font-family:sans-serif; font-weight:bold;\n    padding:0.1em; }\ndiv.console div.jquery-console-message-value\n{ color:#1ad027; font-family:monospace;\n    padding:0.1em; }\ndiv.console div.jquery-console-message-type\n{ color:#52666f; font-family:monospace;\n    padding:0.1em; }\ndiv.console span.jquery-console-prompt-label { font-weight:bold }"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/css/adminlte.css",
    "content": "/*!\n *   AdminLTE v3.2.0\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n/*!\n * Bootstrap v4.6.1 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --blue: #007bff;\n  --indigo: #6610f2;\n  --purple: #6f42c1;\n  --pink: #e83e8c;\n  --red: #dc3545;\n  --orange: #fd7e14;\n  --yellow: #ffc107;\n  --green: #28a745;\n  --teal: #20c997;\n  --cyan: #17a2b8;\n  --white: #fff;\n  --gray: #6c757d;\n  --gray-dark: #343a40;\n  --primary: #007bff;\n  --secondary: #6c757d;\n  --success: #28a745;\n  --info: #17a2b8;\n  --warning: #ffc107;\n  --danger: #dc3545;\n  --light: #f8f9fa;\n  --dark: #343a40;\n  --breakpoint-xs: 0;\n  --breakpoint-sm: 576px;\n  --breakpoint-md: 768px;\n  --breakpoint-lg: 992px;\n  --breakpoint-xl: 1200px;\n  --font-family-sans-serif: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  line-height: 1.15;\n  -webkit-text-size-adjust: 100%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n  display: block;\n}\n\nbody {\n  margin: 0;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  text-align: left;\n  background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n  outline: 0 !important;\n}\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n  overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n  text-decoration: underline;\n  -webkit-text-decoration: underline dotted;\n  text-decoration: underline dotted;\n  cursor: help;\n  border-bottom: 0;\n  -webkit-text-decoration-skip-ink: none;\n  text-decoration-skip-ink: none;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: 700;\n}\n\ndd {\n  margin-bottom: .5rem;\n  margin-left: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall {\n  font-size: 80%;\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -.25em;\n}\n\nsup {\n  top: -.5em;\n}\n\na {\n  color: #007bff;\n  text-decoration: none;\n  background-color: transparent;\n}\n\na:hover {\n  color: #0056b3;\n  text-decoration: none;\n}\n\na:not([href]):not([class]) {\n  color: inherit;\n  text-decoration: none;\n}\n\na:not([href]):not([class]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  font-size: 1em;\n}\n\npre {\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n  -ms-overflow-style: scrollbar;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg {\n  vertical-align: middle;\n  border-style: none;\n}\n\nsvg {\n  overflow: hidden;\n  vertical-align: middle;\n}\n\ntable {\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.75rem;\n  padding-bottom: 0.75rem;\n  color: #6c757d;\n  text-align: left;\n  caption-side: bottom;\n}\n\nth {\n  text-align: inherit;\n  text-align: -webkit-match-parent;\n}\n\nlabel {\n  display: inline-block;\n  margin-bottom: 0.5rem;\n}\n\nbutton {\n  border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n  outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\ninput {\n  overflow: visible;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n[role=\"button\"] {\n  cursor: pointer;\n}\n\nselect {\n  word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n  -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n  cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  box-sizing: border-box;\n  padding: 0;\n}\n\ntextarea {\n  overflow: auto;\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  max-width: 100%;\n  padding: 0;\n  margin-bottom: .5rem;\n  font-size: 1.5rem;\n  line-height: inherit;\n  color: inherit;\n  white-space: normal;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n[type=\"search\"] {\n  outline-offset: -2px;\n  -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\nsummary {\n  display: list-item;\n  cursor: pointer;\n}\n\ntemplate {\n  display: none;\n}\n\n[hidden] {\n  display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  margin-bottom: 0.5rem;\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.2;\n  color: inherit;\n}\n\nh1, .h1 {\n  font-size: 2.5rem;\n}\n\nh2, .h2 {\n  font-size: 2rem;\n}\n\nh3, .h3 {\n  font-size: 1.75rem;\n}\n\nh4, .h4 {\n  font-size: 1.5rem;\n}\n\nh5, .h5 {\n  font-size: 1.25rem;\n}\n\nh6, .h6 {\n  font-size: 1rem;\n}\n\n.lead {\n  font-size: 1.25rem;\n  font-weight: 300;\n}\n\n.display-1 {\n  font-size: 6rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-2 {\n  font-size: 5.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-3 {\n  font-size: 4.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-4 {\n  font-size: 3.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\nhr {\n  margin-top: 1rem;\n  margin-bottom: 1rem;\n  border: 0;\n  border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n  font-size: 80%;\n  font-weight: 400;\n}\n\nmark,\n.mark {\n  padding: 0.2em;\n  background-color: #fcf8e3;\n}\n\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline-item {\n  display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n  margin-right: 0.5rem;\n}\n\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\n.blockquote {\n  margin-bottom: 1rem;\n  font-size: 1.25rem;\n}\n\n.blockquote-footer {\n  display: block;\n  font-size: 80%;\n  color: #6c757d;\n}\n\n.blockquote-footer::before {\n  content: \"\\2014\\00A0\";\n}\n\n.img-fluid {\n  max-width: 100%;\n  height: auto;\n}\n\n.img-thumbnail {\n  padding: 0.25rem;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n  border-radius: 0.25rem;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  max-width: 100%;\n  height: auto;\n}\n\n.figure {\n  display: inline-block;\n}\n\n.figure-img {\n  margin-bottom: 0.5rem;\n  line-height: 1;\n}\n\n.figure-caption {\n  font-size: 90%;\n  color: #6c757d;\n}\n\ncode {\n  font-size: 87.5%;\n  color: #e83e8c;\n  word-wrap: break-word;\n}\n\na > code {\n  color: inherit;\n}\n\nkbd {\n  padding: 0.2rem 0.4rem;\n  font-size: 87.5%;\n  color: #fff;\n  background-color: #212529;\n  border-radius: 0.2rem;\n  box-shadow: inset 0 -0.1rem 0 rgba(0, 0, 0, 0.25);\n}\n\nkbd kbd {\n  padding: 0;\n  font-size: 100%;\n  font-weight: 700;\n  box-shadow: none;\n}\n\npre {\n  display: block;\n  font-size: 87.5%;\n  color: #212529;\n}\n\npre code {\n  font-size: inherit;\n  color: inherit;\n  word-break: normal;\n}\n\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n\n.container,\n.container-fluid,\n.container-sm,\n.container-md,\n.container-lg,\n.container-xl {\n  width: 100%;\n  padding-right: 7.5px;\n  padding-left: 7.5px;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n@media (min-width: 576px) {\n  .container, .container-sm {\n    max-width: 540px;\n  }\n}\n\n@media (min-width: 768px) {\n  .container, .container-sm, .container-md {\n    max-width: 720px;\n  }\n}\n\n@media (min-width: 992px) {\n  .container, .container-sm, .container-md, .container-lg {\n    max-width: 960px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .container, .container-sm, .container-md, .container-lg, .container-xl {\n    max-width: 1140px;\n  }\n}\n\n.row {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  margin-right: -7.5px;\n  margin-left: -7.5px;\n}\n\n.no-gutters {\n  margin-right: 0;\n  margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .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-sm,\n.col-sm-auto, .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-md,\n.col-md-auto, .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-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n  position: relative;\n  width: 100%;\n  padding-right: 7.5px;\n  padding-left: 7.5px;\n}\n\n.col {\n  -ms-flex-preferred-size: 0;\n  flex-basis: 0;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  max-width: 100%;\n}\n\n.row-cols-1 > * {\n  -ms-flex: 0 0 100%;\n  flex: 0 0 100%;\n  max-width: 100%;\n}\n\n.row-cols-2 > * {\n  -ms-flex: 0 0 50%;\n  flex: 0 0 50%;\n  max-width: 50%;\n}\n\n.row-cols-3 > * {\n  -ms-flex: 0 0 33.333333%;\n  flex: 0 0 33.333333%;\n  max-width: 33.333333%;\n}\n\n.row-cols-4 > * {\n  -ms-flex: 0 0 25%;\n  flex: 0 0 25%;\n  max-width: 25%;\n}\n\n.row-cols-5 > * {\n  -ms-flex: 0 0 20%;\n  flex: 0 0 20%;\n  max-width: 20%;\n}\n\n.row-cols-6 > * {\n  -ms-flex: 0 0 16.666667%;\n  flex: 0 0 16.666667%;\n  max-width: 16.666667%;\n}\n\n.col-auto {\n  -ms-flex: 0 0 auto;\n  flex: 0 0 auto;\n  width: auto;\n  max-width: 100%;\n}\n\n.col-1 {\n  -ms-flex: 0 0 8.333333%;\n  flex: 0 0 8.333333%;\n  max-width: 8.333333%;\n}\n\n.col-2 {\n  -ms-flex: 0 0 16.666667%;\n  flex: 0 0 16.666667%;\n  max-width: 16.666667%;\n}\n\n.col-3 {\n  -ms-flex: 0 0 25%;\n  flex: 0 0 25%;\n  max-width: 25%;\n}\n\n.col-4 {\n  -ms-flex: 0 0 33.333333%;\n  flex: 0 0 33.333333%;\n  max-width: 33.333333%;\n}\n\n.col-5 {\n  -ms-flex: 0 0 41.666667%;\n  flex: 0 0 41.666667%;\n  max-width: 41.666667%;\n}\n\n.col-6 {\n  -ms-flex: 0 0 50%;\n  flex: 0 0 50%;\n  max-width: 50%;\n}\n\n.col-7 {\n  -ms-flex: 0 0 58.333333%;\n  flex: 0 0 58.333333%;\n  max-width: 58.333333%;\n}\n\n.col-8 {\n  -ms-flex: 0 0 66.666667%;\n  flex: 0 0 66.666667%;\n  max-width: 66.666667%;\n}\n\n.col-9 {\n  -ms-flex: 0 0 75%;\n  flex: 0 0 75%;\n  max-width: 75%;\n}\n\n.col-10 {\n  -ms-flex: 0 0 83.333333%;\n  flex: 0 0 83.333333%;\n  max-width: 83.333333%;\n}\n\n.col-11 {\n  -ms-flex: 0 0 91.666667%;\n  flex: 0 0 91.666667%;\n  max-width: 91.666667%;\n}\n\n.col-12 {\n  -ms-flex: 0 0 100%;\n  flex: 0 0 100%;\n  max-width: 100%;\n}\n\n.order-first {\n  -ms-flex-order: -1;\n  order: -1;\n}\n\n.order-last {\n  -ms-flex-order: 13;\n  order: 13;\n}\n\n.order-0 {\n  -ms-flex-order: 0;\n  order: 0;\n}\n\n.order-1 {\n  -ms-flex-order: 1;\n  order: 1;\n}\n\n.order-2 {\n  -ms-flex-order: 2;\n  order: 2;\n}\n\n.order-3 {\n  -ms-flex-order: 3;\n  order: 3;\n}\n\n.order-4 {\n  -ms-flex-order: 4;\n  order: 4;\n}\n\n.order-5 {\n  -ms-flex-order: 5;\n  order: 5;\n}\n\n.order-6 {\n  -ms-flex-order: 6;\n  order: 6;\n}\n\n.order-7 {\n  -ms-flex-order: 7;\n  order: 7;\n}\n\n.order-8 {\n  -ms-flex-order: 8;\n  order: 8;\n}\n\n.order-9 {\n  -ms-flex-order: 9;\n  order: 9;\n}\n\n.order-10 {\n  -ms-flex-order: 10;\n  order: 10;\n}\n\n.order-11 {\n  -ms-flex-order: 11;\n  order: 11;\n}\n\n.order-12 {\n  -ms-flex-order: 12;\n  order: 12;\n}\n\n.offset-1 {\n  margin-left: 8.333333%;\n}\n\n.offset-2 {\n  margin-left: 16.666667%;\n}\n\n.offset-3 {\n  margin-left: 25%;\n}\n\n.offset-4 {\n  margin-left: 33.333333%;\n}\n\n.offset-5 {\n  margin-left: 41.666667%;\n}\n\n.offset-6 {\n  margin-left: 50%;\n}\n\n.offset-7 {\n  margin-left: 58.333333%;\n}\n\n.offset-8 {\n  margin-left: 66.666667%;\n}\n\n.offset-9 {\n  margin-left: 75%;\n}\n\n.offset-10 {\n  margin-left: 83.333333%;\n}\n\n.offset-11 {\n  margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n  .col-sm {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-sm-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-sm-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-sm-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-sm-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-sm-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-sm-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-sm-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-sm-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-sm-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-sm-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-sm-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-sm-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-sm-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-sm-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-sm-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-sm-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-sm-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-sm-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-sm-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-sm-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-sm-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-sm-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-sm-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-sm-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-sm-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-sm-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-sm-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-sm-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-sm-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-sm-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-sm-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-sm-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-sm-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-sm-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-sm-0 {\n    margin-left: 0;\n  }\n  .offset-sm-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-sm-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-sm-3 {\n    margin-left: 25%;\n  }\n  .offset-sm-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-sm-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-sm-6 {\n    margin-left: 50%;\n  }\n  .offset-sm-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-sm-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-sm-9 {\n    margin-left: 75%;\n  }\n  .offset-sm-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-sm-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 768px) {\n  .col-md {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-md-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-md-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-md-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-md-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-md-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-md-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-md-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-md-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-md-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-md-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-md-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-md-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-md-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-md-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-md-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-md-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-md-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-md-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-md-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-md-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-md-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-md-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-md-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-md-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-md-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-md-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-md-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-md-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-md-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-md-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-md-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-md-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-md-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-md-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-md-0 {\n    margin-left: 0;\n  }\n  .offset-md-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-md-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-md-3 {\n    margin-left: 25%;\n  }\n  .offset-md-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-md-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-md-6 {\n    margin-left: 50%;\n  }\n  .offset-md-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-md-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-md-9 {\n    margin-left: 75%;\n  }\n  .offset-md-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-md-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 992px) {\n  .col-lg {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-lg-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-lg-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-lg-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-lg-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-lg-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-lg-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-lg-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-lg-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-lg-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-lg-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-lg-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-lg-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-lg-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-lg-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-lg-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-lg-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-lg-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-lg-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-lg-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-lg-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-lg-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-lg-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-lg-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-lg-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-lg-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-lg-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-lg-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-lg-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-lg-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-lg-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-lg-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-lg-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-lg-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-lg-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-lg-0 {\n    margin-left: 0;\n  }\n  .offset-lg-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-lg-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-lg-3 {\n    margin-left: 25%;\n  }\n  .offset-lg-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-lg-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-lg-6 {\n    margin-left: 50%;\n  }\n  .offset-lg-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-lg-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-lg-9 {\n    margin-left: 75%;\n  }\n  .offset-lg-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-lg-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-xl-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-xl-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-xl-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-xl-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-xl-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-xl-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-xl-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-xl-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-xl-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-xl-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-xl-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-xl-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-xl-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-xl-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-xl-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-xl-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-xl-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-xl-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-xl-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-xl-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-xl-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-xl-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-xl-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-xl-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-xl-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-xl-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-xl-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-xl-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-xl-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-xl-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-xl-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-xl-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-xl-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-xl-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-xl-0 {\n    margin-left: 0;\n  }\n  .offset-xl-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-xl-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-xl-3 {\n    margin-left: 25%;\n  }\n  .offset-xl-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-xl-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-xl-6 {\n    margin-left: 50%;\n  }\n  .offset-xl-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-xl-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-xl-9 {\n    margin-left: 75%;\n  }\n  .offset-xl-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-xl-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n.table {\n  width: 100%;\n  margin-bottom: 1rem;\n  color: #212529;\n  background-color: transparent;\n}\n\n.table th,\n.table td {\n  padding: 0.75rem;\n  vertical-align: top;\n  border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n  border-top: 2px solid #dee2e6;\n}\n\n.table-sm th,\n.table-sm td {\n  padding: 0.3rem;\n}\n\n.table-bordered {\n  border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n  border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n  border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n  border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n  color: #212529;\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n  background-color: #b8daff;\n}\n\n.table-primary th,\n.table-primary td,\n.table-primary thead th,\n.table-primary tbody + tbody {\n  border-color: #7abaff;\n}\n\n.table-hover .table-primary:hover {\n  background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n  background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n  background-color: #d6d8db;\n}\n\n.table-secondary th,\n.table-secondary td,\n.table-secondary thead th,\n.table-secondary tbody + tbody {\n  border-color: #b3b7bb;\n}\n\n.table-hover .table-secondary:hover {\n  background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n  background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n  background-color: #c3e6cb;\n}\n\n.table-success th,\n.table-success td,\n.table-success thead th,\n.table-success tbody + tbody {\n  border-color: #8fd19e;\n}\n\n.table-hover .table-success:hover {\n  background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n  background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n  background-color: #bee5eb;\n}\n\n.table-info th,\n.table-info td,\n.table-info thead th,\n.table-info tbody + tbody {\n  border-color: #86cfda;\n}\n\n.table-hover .table-info:hover {\n  background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n  background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n  background-color: #ffeeba;\n}\n\n.table-warning th,\n.table-warning td,\n.table-warning thead th,\n.table-warning tbody + tbody {\n  border-color: #ffdf7e;\n}\n\n.table-hover .table-warning:hover {\n  background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n  background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n  background-color: #f5c6cb;\n}\n\n.table-danger th,\n.table-danger td,\n.table-danger thead th,\n.table-danger tbody + tbody {\n  border-color: #ed969e;\n}\n\n.table-hover .table-danger:hover {\n  background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n  background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n  background-color: #fdfdfe;\n}\n\n.table-light th,\n.table-light td,\n.table-light thead th,\n.table-light tbody + tbody {\n  border-color: #fbfcfc;\n}\n\n.table-hover .table-light:hover {\n  background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n  background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n  background-color: #c6c8ca;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th,\n.table-dark tbody + tbody {\n  border-color: #95999c;\n}\n\n.table-hover .table-dark:hover {\n  background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n  background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n  color: #fff;\n  background-color: #212529;\n  border-color: #383f45;\n}\n\n.table .thead-light th {\n  color: #495057;\n  background-color: #e9ecef;\n  border-color: #dee2e6;\n}\n\n.table-dark {\n  color: #fff;\n  background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n  border-color: #383f45;\n}\n\n.table-dark.table-bordered {\n  border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n  color: #fff;\n  background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n  .table-responsive-sm {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-sm > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .table-responsive-md {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-md > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .table-responsive-lg {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-lg > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .table-responsive-xl {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-xl > .table-bordered {\n    border: 0;\n  }\n}\n\n.table-responsive {\n  display: block;\n  width: 100%;\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n}\n\n.table-responsive > .table-bordered {\n  border: 0;\n}\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0);\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .form-control {\n    transition: none;\n  }\n}\n\n.form-control::-ms-expand {\n  background-color: transparent;\n  border: 0;\n}\n\n.form-control:focus {\n  color: #495057;\n  background-color: #fff;\n  border-color: #80bdff;\n  outline: 0;\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0);\n}\n\n.form-control::-webkit-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::-moz-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::-ms-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n  background-color: #e9ecef;\n  opacity: 1;\n}\n\ninput[type=\"date\"].form-control,\ninput[type=\"time\"].form-control,\ninput[type=\"datetime-local\"].form-control,\ninput[type=\"month\"].form-control {\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\nselect.form-control:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #495057;\n}\n\nselect.form-control:focus::-ms-value {\n  color: #495057;\n  background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n  display: block;\n  width: 100%;\n}\n\n.col-form-label {\n  padding-top: calc(0.375rem + 1px);\n  padding-bottom: calc(0.375rem + 1px);\n  margin-bottom: 0;\n  font-size: inherit;\n  line-height: 1.5;\n}\n\n.col-form-label-lg {\n  padding-top: calc(0.5rem + 1px);\n  padding-bottom: calc(0.5rem + 1px);\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.col-form-label-sm {\n  padding-top: calc(0.25rem + 1px);\n  padding-bottom: calc(0.25rem + 1px);\n  font-size: 0.875rem;\n  line-height: 1.5;\n}\n\n.form-control-plaintext {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0;\n  margin-bottom: 0;\n  font-size: 1rem;\n  line-height: 1.5;\n  color: #212529;\n  background-color: transparent;\n  border: solid transparent;\n  border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.form-control-sm {\n  height: calc(1.8125rem + 2px);\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.form-control-lg {\n  height: calc(2.875rem + 2px);\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\nselect.form-control[size], select.form-control[multiple] {\n  height: auto;\n}\n\ntextarea.form-control {\n  height: auto;\n}\n\n.form-group {\n  margin-bottom: 1rem;\n}\n\n.form-text {\n  display: block;\n  margin-top: 0.25rem;\n}\n\n.form-row {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  margin-right: -5px;\n  margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n  padding-right: 5px;\n  padding-left: 5px;\n}\n\n.form-check {\n  position: relative;\n  display: block;\n  padding-left: 1.25rem;\n}\n\n.form-check-input {\n  position: absolute;\n  margin-top: 0.3rem;\n  margin-left: -1.25rem;\n}\n\n.form-check-input[disabled] ~ .form-check-label,\n.form-check-input:disabled ~ .form-check-label {\n  color: #6c757d;\n}\n\n.form-check-label {\n  margin-bottom: 0;\n}\n\n.form-check-inline {\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding-left: 0;\n  margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n  position: static;\n  margin-top: 0;\n  margin-right: 0.3125rem;\n  margin-left: 0;\n}\n\n.valid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 80%;\n  color: #28a745;\n}\n\n.valid-tooltip {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: .1rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  color: #fff;\n  background-color: rgba(40, 167, 69, 0.9);\n  border-radius: 0.25rem;\n}\n\n.form-row > .col > .valid-tooltip,\n.form-row > [class*=\"col-\"] > .valid-tooltip {\n  left: 5px;\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n  border-color: #28a745;\n  padding-right: 2.25rem !important;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated select.form-control:valid, select.form-control.is-valid {\n  padding-right: 3rem !important;\n  background-position: right 1.5rem center;\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:valid, .custom-select.is-valid {\n  border-color: #28a745;\n  padding-right: calc(0.75em + 2.3125rem) !important;\n  background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat, #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat;\n}\n\n.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n  color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n  color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n  border-color: #28a745;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n  border-color: #34ce57;\n  background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n  border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 80%;\n  color: #dc3545;\n}\n\n.invalid-tooltip {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: .1rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  color: #fff;\n  background-color: rgba(220, 53, 69, 0.9);\n  border-radius: 0.25rem;\n}\n\n.form-row > .col > .invalid-tooltip,\n.form-row > [class*=\"col-\"] > .invalid-tooltip {\n  left: 5px;\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n  border-color: #dc3545;\n  padding-right: 2.25rem !important;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated select.form-control:invalid, select.form-control.is-invalid {\n  padding-right: 3rem !important;\n  background-position: right 1.5rem center;\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:invalid, .custom-select.is-invalid {\n  border-color: #dc3545;\n  padding-right: calc(0.75em + 2.3125rem) !important;\n  background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat, #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat;\n}\n\n.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n  color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n  color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n  border-color: #e4606d;\n  background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-flow: row wrap;\n  flex-flow: row wrap;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.form-inline .form-check {\n  width: 100%;\n}\n\n@media (min-width: 576px) {\n  .form-inline label {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n    margin-bottom: 0;\n  }\n  .form-inline .form-group {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n    -ms-flex-align: center;\n    align-items: center;\n    margin-bottom: 0;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .form-inline .form-control-plaintext {\n    display: inline-block;\n  }\n  .form-inline .input-group,\n  .form-inline .custom-select {\n    width: auto;\n  }\n  .form-inline .form-check {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n    width: auto;\n    padding-left: 0;\n  }\n  .form-inline .form-check-input {\n    position: relative;\n    -ms-flex-negative: 0;\n    flex-shrink: 0;\n    margin-top: 0;\n    margin-right: 0.25rem;\n    margin-left: 0;\n  }\n  .form-inline .custom-control {\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n  }\n  .form-inline .custom-control-label {\n    margin-bottom: 0;\n  }\n}\n\n.btn {\n  display: inline-block;\n  font-weight: 400;\n  color: #212529;\n  text-align: center;\n  vertical-align: middle;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  background-color: transparent;\n  border: 1px solid transparent;\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  line-height: 1.5;\n  border-radius: 0.25rem;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .btn {\n    transition: none;\n  }\n}\n\n.btn:hover {\n  color: #212529;\n  text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n  outline: 0;\n  box-shadow: none;\n}\n\n.btn.disabled, .btn:disabled {\n  opacity: 0.65;\n  box-shadow: none;\n}\n\n.btn:not(:disabled):not(.disabled) {\n  cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n  box-shadow: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n  pointer-events: none;\n}\n\n.btn-primary {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n  box-shadow: none;\n}\n\n.btn-primary:hover {\n  color: #fff;\n  background-color: #0069d9;\n  border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n  color: #fff;\n  background-color: #0069d9;\n  border-color: #0062cc;\n  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #0062cc;\n  border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);\n}\n\n.btn-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n  box-shadow: none;\n}\n\n.btn-secondary:hover {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #545b62;\n  border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.btn-success {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n  box-shadow: none;\n}\n\n.btn-success:hover {\n  color: #fff;\n  background-color: #218838;\n  border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n  color: #fff;\n  background-color: #218838;\n  border-color: #1e7e34;\n  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n  color: #fff;\n  background-color: #1e7e34;\n  border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);\n}\n\n.btn-info {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  box-shadow: none;\n}\n\n.btn-info:hover {\n  color: #fff;\n  background-color: #138496;\n  border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n  color: #fff;\n  background-color: #138496;\n  border-color: #117a8b;\n  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n  color: #fff;\n  background-color: #117a8b;\n  border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);\n}\n\n.btn-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n  box-shadow: none;\n}\n\n.btn-warning:hover {\n  color: #1f2d3d;\n  background-color: #e0a800;\n  border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n  color: #1f2d3d;\n  background-color: #e0a800;\n  border-color: #d39e00;\n  box-shadow: 0 0 0 0 rgba(221, 171, 15, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #d39e00;\n  border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(221, 171, 15, 0.5);\n}\n\n.btn-danger {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n  box-shadow: none;\n}\n\n.btn-danger:hover {\n  color: #fff;\n  background-color: #c82333;\n  border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n  color: #fff;\n  background-color: #c82333;\n  border-color: #bd2130;\n  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #bd2130;\n  border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);\n}\n\n.btn-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  box-shadow: none;\n}\n\n.btn-light:hover {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n  border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.btn-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n  box-shadow: none;\n}\n\n.btn-dark:hover {\n  color: #fff;\n  background-color: #23272b;\n  border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n  color: #fff;\n  background-color: #23272b;\n  border-color: #1d2124;\n  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #1d2124;\n  border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);\n}\n\n.btn-outline-primary {\n  color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n  color: #007bff;\n  background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n  color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n  color: #6c757d;\n  background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n  color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n  color: #28a745;\n  background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n  color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n  color: #17a2b8;\n  background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n  color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n  color: #ffc107;\n  background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n  color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n  color: #dc3545;\n  background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n  color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n  color: #f8f9fa;\n  background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n  color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n  color: #343a40;\n  background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n  font-weight: 400;\n  color: #007bff;\n  text-decoration: none;\n}\n\n.btn-link:hover {\n  color: #0056b3;\n  text-decoration: none;\n}\n\n.btn-link:focus, .btn-link.focus {\n  text-decoration: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n  color: #6c757d;\n  pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.btn-block {\n  display: block;\n  width: 100%;\n}\n\n.btn-block + .btn-block {\n  margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n\n.fade {\n  transition: opacity 0.15s linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .fade {\n    transition: none;\n  }\n}\n\n.fade:not(.show) {\n  opacity: 0;\n}\n\n.collapse:not(.show) {\n  display: none;\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  transition: height 0.35s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .collapsing {\n    transition: none;\n  }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n  position: relative;\n}\n\n.dropdown-toggle {\n  white-space: nowrap;\n}\n\n.dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0;\n  border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 10rem;\n  padding: 0.5rem 0;\n  margin: 0.125rem 0 0;\n  font-size: 1rem;\n  color: #212529;\n  text-align: left;\n  list-style: none;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 0.25rem;\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.175);\n}\n\n.dropdown-menu-left {\n  right: auto;\n  left: 0;\n}\n\n.dropdown-menu-right {\n  right: 0;\n  left: auto;\n}\n\n@media (min-width: 576px) {\n  .dropdown-menu-sm-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-sm-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 768px) {\n  .dropdown-menu-md-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-md-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 992px) {\n  .dropdown-menu-lg-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-lg-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 1200px) {\n  .dropdown-menu-xl-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-xl-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n.dropup .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-top: 0;\n  margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0.3em solid;\n  border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n  top: 0;\n  right: auto;\n  left: 100%;\n  margin-top: 0;\n  margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0;\n  border-bottom: 0.3em solid transparent;\n  border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n  vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n  top: 0;\n  right: 100%;\n  left: auto;\n  margin-top: 0;\n  margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n  display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0.3em solid;\n  border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n  vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n  right: auto;\n  bottom: auto;\n}\n\n.dropdown-divider {\n  height: 0;\n  margin: 0.5rem 0;\n  overflow: hidden;\n  border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n  display: block;\n  width: 100%;\n  padding: 0.25rem 1rem;\n  clear: both;\n  font-weight: 400;\n  color: #212529;\n  text-align: inherit;\n  white-space: nowrap;\n  background-color: transparent;\n  border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n  color: #16181b;\n  text-decoration: none;\n  background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n  color: #fff;\n  text-decoration: none;\n  background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n  color: #6c757d;\n  pointer-events: none;\n  background-color: transparent;\n}\n\n.dropdown-menu.show {\n  display: block;\n}\n\n.dropdown-header {\n  display: block;\n  padding: 0.5rem 1rem;\n  margin-bottom: 0;\n  font-size: 0.875rem;\n  color: #6c757d;\n  white-space: nowrap;\n}\n\n.dropdown-item-text {\n  display: block;\n  padding: 0.25rem 1rem;\n  color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n  z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n  z-index: 1;\n}\n\n.btn-toolbar {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-pack: start;\n  justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n  width: auto;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) {\n  margin-left: -1px;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n  padding-right: 0.5625rem;\n  padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n  margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n  margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n  padding-right: 0.375rem;\n  padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n  padding-right: 0.75rem;\n  padding-left: 0.75rem;\n}\n\n.btn-group.show .dropdown-toggle {\n  box-shadow: none;\n}\n\n.btn-group.show .dropdown-toggle.btn-link {\n  box-shadow: none;\n}\n\n.btn-group-vertical {\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-align: start;\n  align-items: flex-start;\n  -ms-flex-pack: center;\n  justify-content: center;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n  width: 100%;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n  margin-top: -1px;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n  margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n\n.input-group {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: stretch;\n  align-items: stretch;\n  width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .form-control-plaintext,\n.input-group > .custom-select,\n.input-group > .custom-file {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  width: 1%;\n  min-width: 0;\n  margin-bottom: 0;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .form-control-plaintext + .form-control,\n.input-group > .form-control-plaintext + .custom-select,\n.input-group > .form-control-plaintext + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n  margin-left: -1px;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {\n  z-index: 3;\n}\n\n.input-group > .custom-file .custom-file-input:focus {\n  z-index: 4;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.input-group:not(.has-validation) > .form-control:not(:last-child),\n.input-group:not(.has-validation) > .custom-select:not(:last-child),\n.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label,\n.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group.has-validation > .form-control:nth-last-child(n + 3),\n.input-group.has-validation > .custom-select:nth-last-child(n + 3),\n.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label,\n.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n  position: relative;\n  z-index: 2;\n}\n\n.input-group-prepend .btn:focus,\n.input-group-append .btn:focus {\n  z-index: 3;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n  margin-left: -1px;\n}\n\n.input-group-prepend {\n  margin-right: -1px;\n}\n\n.input-group-append {\n  margin-left: -1px;\n}\n\n.input-group-text {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding: 0.375rem 0.75rem;\n  margin-bottom: 0;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #e9ecef;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n\n.input-group-lg > .form-control:not(textarea),\n.input-group-lg > .custom-select {\n  height: calc(2.875rem + 2px);\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .custom-select,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\n.input-group-sm > .form-control:not(textarea),\n.input-group-sm > .custom-select {\n  height: calc(1.8125rem + 2px);\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .custom-select,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.input-group-lg > .custom-select,\n.input-group-sm > .custom-select {\n  padding-right: 1.75rem;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .btn,\n.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .input-group-text,\n.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .btn,\n.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.custom-control {\n  position: relative;\n  z-index: 1;\n  display: block;\n  min-height: 1.5rem;\n  padding-left: 1.5rem;\n  -webkit-print-color-adjust: exact;\n  color-adjust: exact;\n}\n\n.custom-control-inline {\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  margin-right: 1rem;\n}\n\n.custom-control-input {\n  position: absolute;\n  left: 0;\n  z-index: -1;\n  width: 1rem;\n  height: 1.25rem;\n  opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n  color: #fff;\n  border-color: #007bff;\n  background-color: #007bff;\n  box-shadow: none;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input:not(:disabled):active ~ .custom-control-label::before {\n  color: #fff;\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n  box-shadow: none;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label {\n  color: #6c757d;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before {\n  background-color: #e9ecef;\n}\n\n.custom-control-label {\n  position: relative;\n  margin-bottom: 0;\n  vertical-align: top;\n}\n\n.custom-control-label::before {\n  position: absolute;\n  top: 0.25rem;\n  left: -1.5rem;\n  display: block;\n  width: 1rem;\n  height: 1rem;\n  pointer-events: none;\n  content: \"\";\n  background-color: #dee2e6;\n  border: #adb5bd solid 1px;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-control-label::after {\n  position: absolute;\n  top: 0.25rem;\n  left: -1.5rem;\n  display: block;\n  width: 1rem;\n  height: 1rem;\n  content: \"\";\n  background: 50% / 50% 50% no-repeat;\n}\n\n.custom-checkbox .custom-control-label::before {\n  border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n  box-shadow: none;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n  border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-switch {\n  padding-left: 2.25rem;\n}\n\n.custom-switch .custom-control-label::before {\n  left: -2.25rem;\n  width: 1.75rem;\n  pointer-events: all;\n  border-radius: 0.5rem;\n}\n\n.custom-switch .custom-control-label::after {\n  top: calc(0.25rem + 2px);\n  left: calc(-2.25rem + 2px);\n  width: calc(1rem - 4px);\n  height: calc(1rem - 4px);\n  background-color: #adb5bd;\n  border-radius: 0.5rem;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n  transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-switch .custom-control-label::after {\n    transition: none;\n  }\n}\n\n.custom-switch .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #dee2e6;\n  -webkit-transform: translateX(0.75rem);\n  transform: translateX(0.75rem);\n}\n\n.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n  display: inline-block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  vertical-align: middle;\n  background: #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n.custom-select:focus {\n  border-color: #80bdff;\n  outline: 0;\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n\n.custom-select:focus::-ms-value {\n  color: #495057;\n  background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n  height: auto;\n  padding-right: 0.75rem;\n  background-image: none;\n}\n\n.custom-select:disabled {\n  color: #6c757d;\n  background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n  display: none;\n}\n\n.custom-select:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #495057;\n}\n\n.custom-select-sm {\n  height: calc(1.8125rem + 2px);\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n  padding-left: 0.5rem;\n  font-size: 75%;\n}\n\n.custom-select-lg {\n  height: calc(2.875rem + 2px);\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  padding-left: 1rem;\n  font-size: 125%;\n}\n\n.custom-file {\n  position: relative;\n  display: inline-block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  margin-bottom: 0;\n}\n\n.custom-file-input {\n  position: relative;\n  z-index: 2;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  margin: 0;\n  overflow: hidden;\n  opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n  box-shadow: none;\n}\n\n.custom-file-input[disabled] ~ .custom-file-label,\n.custom-file-input:disabled ~ .custom-file-label {\n  background-color: #e9ecef;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n  content: \"Browse\";\n}\n\n.custom-file-input ~ .custom-file-label[data-browse]::after {\n  content: attr(data-browse);\n}\n\n.custom-file-label {\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 0.75rem;\n  overflow: hidden;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  background-color: #fff;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: none;\n}\n\n.custom-file-label::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 3;\n  display: block;\n  height: 2.25rem;\n  padding: 0.375rem 0.75rem;\n  line-height: 1.5;\n  color: #495057;\n  content: \"Browse\";\n  background-color: #e9ecef;\n  border-left: inherit;\n  border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n  width: 100%;\n  height: 1rem;\n  padding: 0;\n  background-color: transparent;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n.custom-range:focus {\n  outline: 0;\n}\n\n.custom-range:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-focus-outer {\n  border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: -0.25rem;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -webkit-appearance: none;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-webkit-slider-thumb {\n    -webkit-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-moz-range-thumb {\n  width: 1rem;\n  height: 1rem;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-moz-range-thumb {\n    -moz-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-ms-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: 0;\n  margin-right: 0;\n  margin-left: 0;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-ms-thumb {\n    -ms-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: transparent;\n  border-color: transparent;\n  border-width: 0.5rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-ms-fill-lower {\n  background-color: #dee2e6;\n  border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n  margin-right: 15px;\n  background-color: #dee2e6;\n  border-radius: 1rem;\n}\n\n.custom-range:disabled::-webkit-slider-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-range:disabled::-webkit-slider-runnable-track {\n  cursor: default;\n}\n\n.custom-range:disabled::-moz-range-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-range:disabled::-moz-range-track {\n  cursor: default;\n}\n\n.custom-range:disabled::-ms-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-control-label::before,\n.custom-file-label,\n.custom-select {\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-control-label::before,\n  .custom-file-label,\n  .custom-select {\n    transition: none;\n  }\n}\n\n.nav {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.nav-link {\n  display: block;\n  padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n  text-decoration: none;\n}\n\n.nav-link.disabled {\n  color: #6c757d;\n  pointer-events: none;\n  cursor: default;\n}\n\n.nav-tabs {\n  border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-link {\n  margin-bottom: -1px;\n  border: 1px solid transparent;\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n  border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n  color: #6c757d;\n  background-color: transparent;\n  border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n  color: #495057;\n  background-color: #fff;\n  border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n  border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n  color: #fff;\n  background-color: #007bff;\n}\n\n.nav-fill > .nav-link,\n.nav-fill .nav-item {\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  text-align: center;\n}\n\n.nav-justified > .nav-link,\n.nav-justified .nav-item {\n  -ms-flex-preferred-size: 0;\n  flex-basis: 0;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  text-align: center;\n}\n\n.tab-content > .tab-pane {\n  display: none;\n}\n\n.tab-content > .active {\n  display: block;\n}\n\n.navbar {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n  padding: 0.5rem 0.5rem;\n}\n\n.navbar .container,\n.navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n}\n\n.navbar-brand {\n  display: inline-block;\n  padding-top: 0.3125rem;\n  padding-bottom: 0.3125rem;\n  margin-right: 0.5rem;\n  font-size: 1.25rem;\n  line-height: inherit;\n  white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n  text-decoration: none;\n}\n\n.navbar-nav {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.navbar-nav .nav-link {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n  position: static;\n  float: none;\n}\n\n.navbar-text {\n  display: inline-block;\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n  -ms-flex-preferred-size: 100%;\n  flex-basis: 100%;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.navbar-toggler {\n  padding: 0.25rem 0.75rem;\n  font-size: 1.25rem;\n  line-height: 1;\n  background-color: transparent;\n  border: 1px solid transparent;\n  border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n  text-decoration: none;\n}\n\n.navbar-toggler-icon {\n  display: inline-block;\n  width: 1.5em;\n  height: 1.5em;\n  vertical-align: middle;\n  content: \"\";\n  background: 50% / 100% 100% no-repeat;\n}\n\n.navbar-nav-scroll {\n  max-height: 75vh;\n  overflow-y: auto;\n}\n\n@media (max-width: 575.98px) {\n  .navbar-expand-sm > .container,\n  .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 576px) {\n  .navbar-expand-sm {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-sm .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-sm .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-sm .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-sm > .container,\n  .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-sm .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-sm .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-sm .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .navbar-expand-md > .container,\n  .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-expand-md {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-md .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-md .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-md .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-md > .container,\n  .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-md .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-md .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-md .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .navbar-expand-lg > .container,\n  .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .navbar-expand-lg {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-lg .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-lg .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-lg .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-lg > .container,\n  .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-lg .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-lg .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-lg .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .navbar-expand-xl > .container,\n  .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 1200px) {\n  .navbar-expand-xl {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-xl .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-xl .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-xl .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-xl > .container,\n  .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-xl .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-xl .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-xl .navbar-toggler {\n    display: none;\n  }\n}\n\n.navbar-expand {\n  -ms-flex-flow: row nowrap;\n  flex-flow: row nowrap;\n  -ms-flex-pack: start;\n  justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n  padding-right: 1rem;\n  padding-left: 1rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n  -ms-flex-wrap: nowrap;\n  flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-nav-scroll {\n  overflow: visible;\n}\n\n.navbar-expand .navbar-collapse {\n  display: -ms-flexbox !important;\n  display: flex !important;\n  -ms-flex-preferred-size: auto;\n  flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n  display: none;\n}\n\n.navbar-light .navbar-brand {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n  color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n  color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n  color: rgba(0, 0, 0, 0.5);\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n  color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n  color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n  color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n  color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n  color: white;\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n  color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n  color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n  color: rgba(255, 255, 255, 0.75);\n  border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n  color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-text a {\n  color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n  color: #fff;\n}\n\n.card {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  min-width: 0;\n  word-wrap: break-word;\n  background-color: #fff;\n  background-clip: border-box;\n  border: 0 solid rgba(0, 0, 0, 0.125);\n  border-radius: 0.25rem;\n}\n\n.card > hr {\n  margin-right: 0;\n  margin-left: 0;\n}\n\n.card > .list-group {\n  border-top: inherit;\n  border-bottom: inherit;\n}\n\n.card > .list-group:first-child {\n  border-top-width: 0;\n  border-top-left-radius: calc(0.25rem - 0);\n  border-top-right-radius: calc(0.25rem - 0);\n}\n\n.card > .list-group:last-child {\n  border-bottom-width: 0;\n  border-bottom-right-radius: calc(0.25rem - 0);\n  border-bottom-left-radius: calc(0.25rem - 0);\n}\n\n.card > .card-header + .list-group,\n.card > .list-group + .card-footer {\n  border-top: 0;\n}\n\n.card-body {\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  min-height: 1px;\n  padding: 1.25rem;\n}\n\n.card-title {\n  margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n  margin-top: -0.375rem;\n  margin-bottom: 0;\n}\n\n.card-text:last-child {\n  margin-bottom: 0;\n}\n\n.card-link:hover {\n  text-decoration: none;\n}\n\n.card-link + .card-link {\n  margin-left: 1.25rem;\n}\n\n.card-header {\n  padding: 0.75rem 1.25rem;\n  margin-bottom: 0;\n  background-color: rgba(0, 0, 0, 0.03);\n  border-bottom: 0 solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n  border-radius: calc(0.25rem - 0) calc(0.25rem - 0) 0 0;\n}\n\n.card-footer {\n  padding: 0.75rem 1.25rem;\n  background-color: rgba(0, 0, 0, 0.03);\n  border-top: 0 solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n  border-radius: 0 0 calc(0.25rem - 0) calc(0.25rem - 0);\n}\n\n.card-header-tabs {\n  margin-right: -0.625rem;\n  margin-bottom: -0.75rem;\n  margin-left: -0.625rem;\n  border-bottom: 0;\n}\n\n.card-header-pills {\n  margin-right: -0.625rem;\n  margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  padding: 1.25rem;\n  border-radius: calc(0.25rem - 0);\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n  -ms-flex-negative: 0;\n  flex-shrink: 0;\n  width: 100%;\n}\n\n.card-img,\n.card-img-top {\n  border-top-left-radius: calc(0.25rem - 0);\n  border-top-right-radius: calc(0.25rem - 0);\n}\n\n.card-img,\n.card-img-bottom {\n  border-bottom-right-radius: calc(0.25rem - 0);\n  border-bottom-left-radius: calc(0.25rem - 0);\n}\n\n.card-deck .card {\n  margin-bottom: 7.5px;\n}\n\n@media (min-width: 576px) {\n  .card-deck {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n    margin-right: -7.5px;\n    margin-left: -7.5px;\n  }\n  .card-deck .card {\n    -ms-flex: 1 0 0%;\n    flex: 1 0 0%;\n    margin-right: 7.5px;\n    margin-bottom: 0;\n    margin-left: 7.5px;\n  }\n}\n\n.card-group > .card {\n  margin-bottom: 7.5px;\n}\n\n@media (min-width: 576px) {\n  .card-group {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n  }\n  .card-group > .card {\n    -ms-flex: 1 0 0%;\n    flex: 1 0 0%;\n    margin-bottom: 0;\n  }\n  .card-group > .card + .card {\n    margin-left: 0;\n    border-left: 0;\n  }\n  .card-group > .card:not(:last-child) {\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-top,\n  .card-group > .card:not(:last-child) .card-header {\n    border-top-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-bottom,\n  .card-group > .card:not(:last-child) .card-footer {\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:first-child) {\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-top,\n  .card-group > .card:not(:first-child) .card-header {\n    border-top-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-bottom,\n  .card-group > .card:not(:first-child) .card-footer {\n    border-bottom-left-radius: 0;\n  }\n}\n\n.card-columns .card {\n  margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n  .card-columns {\n    -webkit-column-count: 3;\n    -moz-column-count: 3;\n    column-count: 3;\n    -webkit-column-gap: 1.25rem;\n    -moz-column-gap: 1.25rem;\n    column-gap: 1.25rem;\n    orphans: 1;\n    widows: 1;\n  }\n  .card-columns .card {\n    display: inline-block;\n    width: 100%;\n  }\n}\n\n.accordion {\n  overflow-anchor: none;\n}\n\n.accordion > .card {\n  overflow: hidden;\n}\n\n.accordion > .card:not(:last-of-type) {\n  border-bottom: 0;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.accordion > .card:not(:first-of-type) {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.accordion > .card > .card-header {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.breadcrumb {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  padding: 0.75rem 1rem;\n  margin-bottom: 1rem;\n  list-style: none;\n  background-color: #e9ecef;\n  border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n  padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n  float: left;\n  padding-right: 0.5rem;\n  color: #6c757d;\n  content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n  text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n  text-decoration: none;\n}\n\n.breadcrumb-item.active {\n  color: #6c757d;\n}\n\n.pagination {\n  display: -ms-flexbox;\n  display: flex;\n  padding-left: 0;\n  list-style: none;\n  border-radius: 0.25rem;\n}\n\n.page-link {\n  position: relative;\n  display: block;\n  padding: 0.5rem 0.75rem;\n  margin-left: -1px;\n  line-height: 1.25;\n  color: #007bff;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n  z-index: 2;\n  color: #0056b3;\n  text-decoration: none;\n  background-color: #e9ecef;\n  border-color: #dee2e6;\n}\n\n.page-link:focus {\n  z-index: 3;\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-item:first-child .page-link {\n  margin-left: 0;\n  border-top-left-radius: 0.25rem;\n  border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n  border-top-right-radius: 0.25rem;\n  border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n  z-index: 3;\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n  color: #6c757d;\n  pointer-events: none;\n  cursor: auto;\n  background-color: #fff;\n  border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n  padding: 0.75rem 1.5rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n  border-top-left-radius: 0.3rem;\n  border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n  border-top-right-radius: 0.3rem;\n  border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n  border-top-left-radius: 0.2rem;\n  border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n  border-top-right-radius: 0.2rem;\n  border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n  display: inline-block;\n  padding: 0.25em 0.4em;\n  font-size: 75%;\n  font-weight: 700;\n  line-height: 1;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: 0.25rem;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .badge {\n    transition: none;\n  }\n}\n\na.badge:hover, a.badge:focus {\n  text-decoration: none;\n}\n\n.badge:empty {\n  display: none;\n}\n\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n\n.badge-pill {\n  padding-right: 0.6em;\n  padding-left: 0.6em;\n  border-radius: 10rem;\n}\n\n.badge-primary {\n  color: #fff;\n  background-color: #007bff;\n}\n\na.badge-primary:hover, a.badge-primary:focus {\n  color: #fff;\n  background-color: #0062cc;\n}\n\na.badge-primary:focus, a.badge-primary.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.badge-secondary {\n  color: #fff;\n  background-color: #6c757d;\n}\n\na.badge-secondary:hover, a.badge-secondary:focus {\n  color: #fff;\n  background-color: #545b62;\n}\n\na.badge-secondary:focus, a.badge-secondary.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.badge-success {\n  color: #fff;\n  background-color: #28a745;\n}\n\na.badge-success:hover, a.badge-success:focus {\n  color: #fff;\n  background-color: #1e7e34;\n}\n\na.badge-success:focus, a.badge-success.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.badge-info {\n  color: #fff;\n  background-color: #17a2b8;\n}\n\na.badge-info:hover, a.badge-info:focus {\n  color: #fff;\n  background-color: #117a8b;\n}\n\na.badge-info:focus, a.badge-info.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.badge-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n}\n\na.badge-warning:hover, a.badge-warning:focus {\n  color: #1f2d3d;\n  background-color: #d39e00;\n}\n\na.badge-warning:focus, a.badge-warning.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.badge-danger {\n  color: #fff;\n  background-color: #dc3545;\n}\n\na.badge-danger:hover, a.badge-danger:focus {\n  color: #fff;\n  background-color: #bd2130;\n}\n\na.badge-danger:focus, a.badge-danger.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.badge-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n}\n\na.badge-light:hover, a.badge-light:focus {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n}\n\na.badge-light:focus, a.badge-light.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.badge-dark {\n  color: #fff;\n  background-color: #343a40;\n}\n\na.badge-dark:hover, a.badge-dark:focus {\n  color: #fff;\n  background-color: #1d2124;\n}\n\na.badge-dark:focus, a.badge-dark.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.jumbotron {\n  padding: 2rem 1rem;\n  margin-bottom: 2rem;\n  background-color: #e9ecef;\n  border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n  .jumbotron {\n    padding: 4rem 2rem;\n  }\n}\n\n.jumbotron-fluid {\n  padding-right: 0;\n  padding-left: 0;\n  border-radius: 0;\n}\n\n.alert {\n  position: relative;\n  padding: 0.75rem 1.25rem;\n  margin-bottom: 1rem;\n  border: 1px solid transparent;\n  border-radius: 0.25rem;\n}\n\n.alert-heading {\n  color: inherit;\n}\n\n.alert-link {\n  font-weight: 700;\n}\n\n.alert-dismissible {\n  padding-right: 4rem;\n}\n\n.alert-dismissible .close, .alert-dismissible .mailbox-attachment-close {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  padding: 0.75rem 1.25rem;\n  color: inherit;\n}\n\n.alert-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.alert-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n  color: #002752;\n}\n\n.alert-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n  color: #202326;\n}\n\n.alert-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.alert-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n  color: #0b2e13;\n}\n\n.alert-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.alert-info hr {\n  border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n  color: #062c33;\n}\n\n.alert-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.alert-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n  color: #533f03;\n}\n\n.alert-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n  color: #491217;\n}\n\n.alert-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.alert-light hr {\n  border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n  color: #686868;\n}\n\n.alert-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n  color: #040505;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 1rem 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 1rem 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n.progress {\n  display: -ms-flexbox;\n  display: flex;\n  height: 1rem;\n  overflow: hidden;\n  line-height: 0;\n  font-size: 0.75rem;\n  background-color: #e9ecef;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1);\n}\n\n.progress-bar {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  overflow: hidden;\n  color: #fff;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #007bff;\n  transition: width 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar {\n    transition: none;\n  }\n}\n\n.progress-bar-striped {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n  -webkit-animation: 1s linear infinite progress-bar-stripes;\n  animation: 1s linear infinite progress-bar-stripes;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar-animated {\n    -webkit-animation: none;\n    animation: none;\n  }\n}\n\n.media {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: start;\n  align-items: flex-start;\n}\n\n.media-body {\n  -ms-flex: 1;\n  flex: 1;\n}\n\n.list-group {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  border-radius: 0.25rem;\n}\n\n.list-group-item-action {\n  width: 100%;\n  color: #495057;\n  text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n  z-index: 1;\n  color: #495057;\n  text-decoration: none;\n  background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n  color: #212529;\n  background-color: #e9ecef;\n}\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 0.75rem 1.25rem;\n  background-color: #fff;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n  border-top-left-radius: inherit;\n  border-top-right-radius: inherit;\n}\n\n.list-group-item:last-child {\n  border-bottom-right-radius: inherit;\n  border-bottom-left-radius: inherit;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n  color: #6c757d;\n  pointer-events: none;\n  background-color: #fff;\n}\n\n.list-group-item.active {\n  z-index: 2;\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.list-group-item + .list-group-item {\n  border-top-width: 0;\n}\n\n.list-group-item + .list-group-item.active {\n  margin-top: -1px;\n  border-top-width: 1px;\n}\n\n.list-group-horizontal {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.list-group-horizontal > .list-group-item:first-child {\n  border-bottom-left-radius: 0.25rem;\n  border-top-right-radius: 0;\n}\n\n.list-group-horizontal > .list-group-item:last-child {\n  border-top-right-radius: 0.25rem;\n  border-bottom-left-radius: 0;\n}\n\n.list-group-horizontal > .list-group-item.active {\n  margin-top: 0;\n}\n\n.list-group-horizontal > .list-group-item + .list-group-item {\n  border-top-width: 1px;\n  border-left-width: 0;\n}\n\n.list-group-horizontal > .list-group-item + .list-group-item.active {\n  margin-left: -1px;\n  border-left-width: 1px;\n}\n\n@media (min-width: 576px) {\n  .list-group-horizontal-sm {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-sm > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 768px) {\n  .list-group-horizontal-md {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-md > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 992px) {\n  .list-group-horizontal-lg {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-lg > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .list-group-horizontal-xl {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-xl > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n.list-group-flush {\n  border-radius: 0;\n}\n\n.list-group-flush > .list-group-item {\n  border-width: 0 0 1px;\n}\n\n.list-group-flush > .list-group-item:last-child {\n  border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n  color: #004085;\n  background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n  color: #004085;\n  background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n  color: #fff;\n  background-color: #004085;\n  border-color: #004085;\n}\n\n.list-group-item-secondary {\n  color: #383d41;\n  background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n  color: #383d41;\n  background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n  color: #fff;\n  background-color: #383d41;\n  border-color: #383d41;\n}\n\n.list-group-item-success {\n  color: #155724;\n  background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n  color: #155724;\n  background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n  color: #fff;\n  background-color: #155724;\n  border-color: #155724;\n}\n\n.list-group-item-info {\n  color: #0c5460;\n  background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n  color: #0c5460;\n  background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n  color: #fff;\n  background-color: #0c5460;\n  border-color: #0c5460;\n}\n\n.list-group-item-warning {\n  color: #856404;\n  background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n  color: #856404;\n  background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n  color: #fff;\n  background-color: #856404;\n  border-color: #856404;\n}\n\n.list-group-item-danger {\n  color: #721c24;\n  background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n  color: #721c24;\n  background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n  color: #fff;\n  background-color: #721c24;\n  border-color: #721c24;\n}\n\n.list-group-item-light {\n  color: #818182;\n  background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n  color: #818182;\n  background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n  color: #fff;\n  background-color: #818182;\n  border-color: #818182;\n}\n\n.list-group-item-dark {\n  color: #1b1e21;\n  background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n  color: #1b1e21;\n  background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n  color: #fff;\n  background-color: #1b1e21;\n  border-color: #1b1e21;\n}\n\n.close, .mailbox-attachment-close {\n  float: right;\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: .5;\n}\n\n.close:hover, .mailbox-attachment-close:hover {\n  color: #000;\n  text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .mailbox-attachment-close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus, .mailbox-attachment-close:not(:disabled):not(.disabled):focus {\n  opacity: .75;\n}\n\nbutton.close, button.mailbox-attachment-close {\n  padding: 0;\n  background-color: transparent;\n  border: 0;\n}\n\na.close.disabled, a.disabled.mailbox-attachment-close {\n  pointer-events: none;\n}\n\n.toast {\n  -ms-flex-preferred-size: 350px;\n  flex-basis: 350px;\n  max-width: 350px;\n  font-size: 0.875rem;\n  background-color: rgba(255, 255, 255, 0.85);\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.1);\n  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);\n  opacity: 0;\n  border-radius: 0.25rem;\n}\n\n.toast:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.toast.showing {\n  opacity: 1;\n}\n\n.toast.show {\n  display: block;\n  opacity: 1;\n}\n\n.toast.hide {\n  display: none;\n}\n\n.toast-header {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding: 0.25rem 0.75rem;\n  color: #6c757d;\n  background-color: rgba(255, 255, 255, 0.85);\n  background-clip: padding-box;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n  border-top-left-radius: calc(0.25rem - 1px);\n  border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.toast-body {\n  padding: 0.75rem;\n}\n\n.modal-open {\n  overflow: hidden;\n}\n\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n.modal {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 1050;\n  display: none;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  outline: 0;\n}\n\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 0.5rem;\n  pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n  transition: -webkit-transform 0.3s ease-out;\n  transition: transform 0.3s ease-out;\n  transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;\n  -webkit-transform: translate(0, -50px);\n  transform: translate(0, -50px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .modal.fade .modal-dialog {\n    transition: none;\n  }\n}\n\n.modal.show .modal-dialog {\n  -webkit-transform: none;\n  transform: none;\n}\n\n.modal.modal-static .modal-dialog {\n  -webkit-transform: scale(1.02);\n  transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n  display: -ms-flexbox;\n  display: flex;\n  max-height: calc(100% - 1rem);\n}\n\n.modal-dialog-scrollable .modal-content {\n  max-height: calc(100vh - 1rem);\n  overflow: hidden;\n}\n\n.modal-dialog-scrollable .modal-header,\n.modal-dialog-scrollable .modal-footer {\n  -ms-flex-negative: 0;\n  flex-shrink: 0;\n}\n\n.modal-dialog-scrollable .modal-body {\n  overflow-y: auto;\n}\n\n.modal-dialog-centered {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  min-height: calc(100% - 1rem);\n}\n\n.modal-dialog-centered::before {\n  display: block;\n  height: calc(100vh - 1rem);\n  height: -webkit-min-content;\n  height: -moz-min-content;\n  height: min-content;\n  content: \"\";\n}\n\n.modal-dialog-centered.modal-dialog-scrollable {\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  height: 100%;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable .modal-content {\n  max-height: none;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable::before {\n  content: none;\n}\n\n.modal-content {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  width: 100%;\n  pointer-events: auto;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 0.3rem;\n  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5);\n  outline: 0;\n}\n\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 1040;\n  width: 100vw;\n  height: 100vh;\n  background-color: #000;\n}\n\n.modal-backdrop.fade {\n  opacity: 0;\n}\n\n.modal-backdrop.show {\n  opacity: 0.5;\n}\n\n.modal-header {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: start;\n  align-items: flex-start;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n  padding: 1rem;\n  border-bottom: 1px solid #e9ecef;\n  border-top-left-radius: calc(0.3rem - 1px);\n  border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.modal-header .close, .modal-header .mailbox-attachment-close {\n  padding: 1rem;\n  margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n  margin-bottom: 0;\n  line-height: 1.5;\n}\n\n.modal-body {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  padding: 1rem;\n}\n\n.modal-footer {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: end;\n  justify-content: flex-end;\n  padding: 0.75rem;\n  border-top: 1px solid #e9ecef;\n  border-bottom-right-radius: calc(0.3rem - 1px);\n  border-bottom-left-radius: calc(0.3rem - 1px);\n}\n\n.modal-footer > * {\n  margin: 0.25rem;\n}\n\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n\n@media (min-width: 576px) {\n  .modal-dialog {\n    max-width: 500px;\n    margin: 1.75rem auto;\n  }\n  .modal-dialog-scrollable {\n    max-height: calc(100% - 3.5rem);\n  }\n  .modal-dialog-scrollable .modal-content {\n    max-height: calc(100vh - 3.5rem);\n  }\n  .modal-dialog-centered {\n    min-height: calc(100% - 3.5rem);\n  }\n  .modal-dialog-centered::before {\n    height: calc(100vh - 3.5rem);\n    height: -webkit-min-content;\n    height: -moz-min-content;\n    height: min-content;\n  }\n  .modal-content {\n    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);\n  }\n  .modal-sm {\n    max-width: 300px;\n  }\n}\n\n@media (min-width: 992px) {\n  .modal-lg,\n  .modal-xl {\n    max-width: 800px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .modal-xl {\n    max-width: 1140px;\n  }\n}\n\n.tooltip {\n  position: absolute;\n  z-index: 1070;\n  display: block;\n  margin: 0;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  font-size: 0.875rem;\n  word-wrap: break-word;\n  opacity: 0;\n}\n\n.tooltip.show {\n  opacity: 0.9;\n}\n\n.tooltip .arrow {\n  position: absolute;\n  display: block;\n  width: 0.8rem;\n  height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n  position: absolute;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n  padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n  bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n  top: 0;\n  border-width: 0.4rem 0.4rem 0;\n  border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n  padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n  left: 0;\n  width: 0.4rem;\n  height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n  right: 0;\n  border-width: 0.4rem 0.4rem 0.4rem 0;\n  border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n  padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n  top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n  bottom: 0;\n  border-width: 0 0.4rem 0.4rem;\n  border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n  padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n  right: 0;\n  width: 0.4rem;\n  height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n  left: 0;\n  border-width: 0.4rem 0 0.4rem 0.4rem;\n  border-left-color: #000;\n}\n\n.tooltip-inner {\n  max-width: 200px;\n  padding: 0.25rem 0.5rem;\n  color: #fff;\n  text-align: center;\n  background-color: #000;\n  border-radius: 0.25rem;\n}\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1060;\n  display: block;\n  max-width: 276px;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  font-size: 0.875rem;\n  word-wrap: break-word;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 0.3rem;\n  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.2);\n}\n\n.popover .arrow {\n  position: absolute;\n  display: block;\n  width: 1rem;\n  height: 0.5rem;\n  margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n  position: absolute;\n  display: block;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n  margin-bottom: 0.5rem;\n}\n\n.bs-popover-top > .arrow, .bs-popover-auto[x-placement^=\"top\"] > .arrow {\n  bottom: calc(-0.5rem - 1px);\n}\n\n.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^=\"top\"] > .arrow::before {\n  bottom: 0;\n  border-width: 0.5rem 0.5rem 0;\n  border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^=\"top\"] > .arrow::after {\n  bottom: 1px;\n  border-width: 0.5rem 0.5rem 0;\n  border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n  margin-left: 0.5rem;\n}\n\n.bs-popover-right > .arrow, .bs-popover-auto[x-placement^=\"right\"] > .arrow {\n  left: calc(-0.5rem - 1px);\n  width: 0.5rem;\n  height: 1rem;\n  margin: 0.3rem 0;\n}\n\n.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^=\"right\"] > .arrow::before {\n  left: 0;\n  border-width: 0.5rem 0.5rem 0.5rem 0;\n  border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^=\"right\"] > .arrow::after {\n  left: 1px;\n  border-width: 0.5rem 0.5rem 0.5rem 0;\n  border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n  margin-top: 0.5rem;\n}\n\n.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow {\n  top: calc(-0.5rem - 1px);\n}\n\n.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::before {\n  top: 0;\n  border-width: 0 0.5rem 0.5rem 0.5rem;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::after {\n  top: 1px;\n  border-width: 0 0.5rem 0.5rem 0.5rem;\n  border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n  position: absolute;\n  top: 0;\n  left: 50%;\n  display: block;\n  width: 1rem;\n  margin-left: -0.5rem;\n  content: \"\";\n  border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n  margin-right: 0.5rem;\n}\n\n.bs-popover-left > .arrow, .bs-popover-auto[x-placement^=\"left\"] > .arrow {\n  right: calc(-0.5rem - 1px);\n  width: 0.5rem;\n  height: 1rem;\n  margin: 0.3rem 0;\n}\n\n.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^=\"left\"] > .arrow::before {\n  right: 0;\n  border-width: 0.5rem 0 0.5rem 0.5rem;\n  border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^=\"left\"] > .arrow::after {\n  right: 1px;\n  border-width: 0.5rem 0 0.5rem 0.5rem;\n  border-left-color: #fff;\n}\n\n.popover-header {\n  padding: 0.5rem 0.75rem;\n  margin-bottom: 0;\n  font-size: 1rem;\n  color: inherit;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-top-left-radius: calc(0.3rem - 1px);\n  border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n  display: none;\n}\n\n.popover-body {\n  padding: 0.5rem 0.75rem;\n  color: #212529;\n}\n\n.carousel {\n  position: relative;\n}\n\n.carousel.pointer-event {\n  -ms-touch-action: pan-y;\n  touch-action: pan-y;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n\n.carousel-inner::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.carousel-item {\n  position: relative;\n  display: none;\n  float: left;\n  width: 100%;\n  margin-right: -100%;\n  -webkit-backface-visibility: hidden;\n  backface-visibility: hidden;\n  transition: -webkit-transform 0.6s ease;\n  transition: transform 0.6s ease;\n  transition: transform 0.6s ease, -webkit-transform 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-item {\n    transition: none;\n  }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n  display: block;\n}\n\n.carousel-item-next:not(.carousel-item-left),\n.active.carousel-item-right {\n  -webkit-transform: translateX(100%);\n  transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-right),\n.active.carousel-item-left {\n  -webkit-transform: translateX(-100%);\n  transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n  opacity: 0;\n  transition-property: opacity;\n  -webkit-transform: none;\n  transform: none;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n  z-index: 1;\n  opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n  z-index: 0;\n  opacity: 0;\n  transition: opacity 0s 0.6s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-fade .active.carousel-item-left,\n  .carousel-fade .active.carousel-item-right {\n    transition: none;\n  }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  z-index: 1;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: center;\n  justify-content: center;\n  width: 15%;\n  padding: 0;\n  color: #fff;\n  text-align: center;\n  background: none;\n  border: 0;\n  opacity: 0.5;\n  transition: opacity 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-control-prev,\n  .carousel-control-next {\n    transition: none;\n  }\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n  color: #fff;\n  text-decoration: none;\n  outline: 0;\n  opacity: 0.9;\n}\n\n.carousel-control-prev {\n  left: 0;\n}\n\n.carousel-control-next {\n  right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n  display: inline-block;\n  width: 20px;\n  height: 20px;\n  background: 50% / 100% 100% no-repeat;\n}\n\n.carousel-control-prev-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 15;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  padding-left: 0;\n  margin-right: 15%;\n  margin-left: 15%;\n  list-style: none;\n}\n\n.carousel-indicators li {\n  box-sizing: content-box;\n  -ms-flex: 0 1 auto;\n  flex: 0 1 auto;\n  width: 30px;\n  height: 3px;\n  margin-right: 3px;\n  margin-left: 3px;\n  text-indent: -999px;\n  cursor: pointer;\n  background-color: #fff;\n  background-clip: padding-box;\n  border-top: 10px solid transparent;\n  border-bottom: 10px solid transparent;\n  opacity: .5;\n  transition: opacity 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-indicators li {\n    transition: none;\n  }\n}\n\n.carousel-indicators .active {\n  opacity: 1;\n}\n\n.carousel-caption {\n  position: absolute;\n  right: 15%;\n  bottom: 20px;\n  left: 15%;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #fff;\n  text-align: center;\n}\n\n@-webkit-keyframes spinner-border {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg);\n  }\n}\n\n@keyframes spinner-border {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg);\n  }\n}\n\n.spinner-border {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  vertical-align: -0.125em;\n  border: 0.25em solid currentColor;\n  border-right-color: transparent;\n  border-radius: 50%;\n  -webkit-animation: .75s linear infinite spinner-border;\n  animation: .75s linear infinite spinner-border;\n}\n\n.spinner-border-sm {\n  width: 1rem;\n  height: 1rem;\n  border-width: 0.2em;\n}\n\n@-webkit-keyframes spinner-grow {\n  0% {\n    -webkit-transform: scale(0);\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes spinner-grow {\n  0% {\n    -webkit-transform: scale(0);\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.spinner-grow {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  vertical-align: -0.125em;\n  background-color: currentColor;\n  border-radius: 50%;\n  opacity: 0;\n  -webkit-animation: .75s linear infinite spinner-grow;\n  animation: .75s linear infinite spinner-grow;\n}\n\n.spinner-grow-sm {\n  width: 1rem;\n  height: 1rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .spinner-border,\n  .spinner-grow {\n    -webkit-animation-duration: 1.5s;\n    animation-duration: 1.5s;\n  }\n}\n\n.align-baseline {\n  vertical-align: baseline !important;\n}\n\n.align-top {\n  vertical-align: top !important;\n}\n\n.align-middle {\n  vertical-align: middle !important;\n}\n\n.align-bottom {\n  vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n  vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n  vertical-align: text-top !important;\n}\n\n.bg-primary {\n  background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n  background-color: #0062cc !important;\n}\n\n.bg-secondary {\n  background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n  background-color: #545b62 !important;\n}\n\n.bg-success {\n  background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n  background-color: #1e7e34 !important;\n}\n\n.bg-info {\n  background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n  background-color: #117a8b !important;\n}\n\n.bg-warning {\n  background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n  background-color: #d39e00 !important;\n}\n\n.bg-danger {\n  background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n  background-color: #bd2130 !important;\n}\n\n.bg-light {\n  background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n  background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n  background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n  background-color: #1d2124 !important;\n}\n\n.bg-white {\n  background-color: #fff !important;\n}\n\n.bg-transparent {\n  background-color: transparent !important;\n}\n\n.border {\n  border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n  border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n  border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n  border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n  border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n  border: 0 !important;\n}\n\n.border-top-0 {\n  border-top: 0 !important;\n}\n\n.border-right-0 {\n  border-right: 0 !important;\n}\n\n.border-bottom-0 {\n  border-bottom: 0 !important;\n}\n\n.border-left-0 {\n  border-left: 0 !important;\n}\n\n.border-primary {\n  border-color: #007bff !important;\n}\n\n.border-secondary {\n  border-color: #6c757d !important;\n}\n\n.border-success {\n  border-color: #28a745 !important;\n}\n\n.border-info {\n  border-color: #17a2b8 !important;\n}\n\n.border-warning {\n  border-color: #ffc107 !important;\n}\n\n.border-danger {\n  border-color: #dc3545 !important;\n}\n\n.border-light {\n  border-color: #f8f9fa !important;\n}\n\n.border-dark {\n  border-color: #343a40 !important;\n}\n\n.border-white {\n  border-color: #fff !important;\n}\n\n.rounded-sm {\n  border-radius: 0.2rem !important;\n}\n\n.rounded {\n  border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n  border-top-left-radius: 0.25rem !important;\n  border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n  border-top-right-radius: 0.25rem !important;\n  border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n  border-bottom-right-radius: 0.25rem !important;\n  border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n  border-top-left-radius: 0.25rem !important;\n  border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-lg {\n  border-radius: 0.3rem !important;\n}\n\n.rounded-circle {\n  border-radius: 50% !important;\n}\n\n.rounded-pill {\n  border-radius: 50rem !important;\n}\n\n.rounded-0 {\n  border-radius: 0 !important;\n}\n\n.clearfix::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.d-none {\n  display: none !important;\n}\n\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: -ms-flexbox !important;\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: -ms-inline-flexbox !important;\n  display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n  .d-sm-none {\n    display: none !important;\n  }\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .d-md-none {\n    display: none !important;\n  }\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .d-lg-none {\n    display: none !important;\n  }\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .d-xl-none {\n    display: none !important;\n  }\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media print {\n  .d-print-none {\n    display: none !important;\n  }\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n.embed-responsive {\n  position: relative;\n  display: block;\n  width: 100%;\n  padding: 0;\n  overflow: hidden;\n}\n\n.embed-responsive::before {\n  display: block;\n  content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  border: 0;\n}\n\n.embed-responsive-21by9::before {\n  padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n  padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n  padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n  padding-top: 100%;\n}\n\n.flex-row {\n  -ms-flex-direction: row !important;\n  flex-direction: row !important;\n}\n\n.flex-column {\n  -ms-flex-direction: column !important;\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  -ms-flex-direction: row-reverse !important;\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  -ms-flex-direction: column-reverse !important;\n  flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n  -ms-flex-wrap: wrap !important;\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  -ms-flex-wrap: nowrap !important;\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  -ms-flex-wrap: wrap-reverse !important;\n  flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n  -ms-flex: 1 1 auto !important;\n  flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n  -ms-flex-positive: 0 !important;\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  -ms-flex-positive: 1 !important;\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  -ms-flex-negative: 0 !important;\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  -ms-flex-negative: 1 !important;\n  flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n  -ms-flex-pack: start !important;\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  -ms-flex-pack: end !important;\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  -ms-flex-pack: center !important;\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  -ms-flex-pack: justify !important;\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  -ms-flex-pack: distribute !important;\n  justify-content: space-around !important;\n}\n\n.align-items-start {\n  -ms-flex-align: start !important;\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  -ms-flex-align: end !important;\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  -ms-flex-align: center !important;\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  -ms-flex-align: baseline !important;\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  -ms-flex-align: stretch !important;\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  -ms-flex-line-pack: start !important;\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  -ms-flex-line-pack: end !important;\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  -ms-flex-line-pack: center !important;\n  align-content: center !important;\n}\n\n.align-content-between {\n  -ms-flex-line-pack: justify !important;\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  -ms-flex-line-pack: distribute !important;\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  -ms-flex-line-pack: stretch !important;\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  -ms-flex-item-align: auto !important;\n  align-self: auto !important;\n}\n\n.align-self-start {\n  -ms-flex-item-align: start !important;\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  -ms-flex-item-align: end !important;\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  -ms-flex-item-align: center !important;\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  -ms-flex-item-align: baseline !important;\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  -ms-flex-item-align: stretch !important;\n  align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n  .flex-sm-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-sm-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-sm-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-sm-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .flex-md-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-md-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-md-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-md-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-md-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .flex-lg-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-lg-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-lg-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-lg-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .flex-xl-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-xl-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-xl-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-xl-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n.float-left {\n  float: left !important;\n}\n\n.float-right {\n  float: right !important;\n}\n\n.float-none {\n  float: none !important;\n}\n\n@media (min-width: 576px) {\n  .float-sm-left {\n    float: left !important;\n  }\n  .float-sm-right {\n    float: right !important;\n  }\n  .float-sm-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .float-md-left {\n    float: left !important;\n  }\n  .float-md-right {\n    float: right !important;\n  }\n  .float-md-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .float-lg-left {\n    float: left !important;\n  }\n  .float-lg-right {\n    float: right !important;\n  }\n  .float-lg-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .float-xl-left {\n    float: left !important;\n  }\n  .float-xl-right {\n    float: right !important;\n  }\n  .float-xl-none {\n    float: none !important;\n  }\n}\n\n.user-select-all {\n  -webkit-user-select: all !important;\n  -moz-user-select: all !important;\n  user-select: all !important;\n}\n\n.user-select-auto {\n  -webkit-user-select: auto !important;\n  -moz-user-select: auto !important;\n  -ms-user-select: auto !important;\n  user-select: auto !important;\n}\n\n.user-select-none {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  -ms-user-select: none !important;\n  user-select: none !important;\n}\n\n.overflow-auto {\n  overflow: auto !important;\n}\n\n.overflow-hidden {\n  overflow: hidden !important;\n}\n\n.position-static {\n  position: static !important;\n}\n\n.position-relative {\n  position: relative !important;\n}\n\n.position-absolute {\n  position: absolute !important;\n}\n\n.position-fixed {\n  position: fixed !important;\n}\n\n.position-sticky {\n  position: -webkit-sticky !important;\n  position: sticky !important;\n}\n\n.fixed-top {\n  position: fixed;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n.fixed-bottom {\n  position: fixed;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n@supports ((position: -webkit-sticky) or (position: sticky)) {\n  .sticky-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  overflow: visible;\n  clip: auto;\n  white-space: normal;\n}\n\n.shadow-sm {\n  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n  box-shadow: none !important;\n}\n\n.w-25 {\n  width: 25% !important;\n}\n\n.w-50 {\n  width: 50% !important;\n}\n\n.w-75 {\n  width: 75% !important;\n}\n\n.w-100 {\n  width: 100% !important;\n}\n\n.w-auto {\n  width: auto !important;\n}\n\n.h-25 {\n  height: 25% !important;\n}\n\n.h-50 {\n  height: 50% !important;\n}\n\n.h-75 {\n  height: 75% !important;\n}\n\n.h-100 {\n  height: 100% !important;\n}\n\n.h-auto {\n  height: auto !important;\n}\n\n.mw-100 {\n  max-width: 100% !important;\n}\n\n.mh-100 {\n  max-height: 100% !important;\n}\n\n.min-vw-100 {\n  min-width: 100vw !important;\n}\n\n.min-vh-100 {\n  min-height: 100vh !important;\n}\n\n.vw-100 {\n  width: 100vw !important;\n}\n\n.vh-100 {\n  height: 100vh !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n  margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n  margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n  margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n  margin-left: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n  margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n  margin-left: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n  margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n  margin-left: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n  margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n  margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n  margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n  margin-left: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n  margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n  margin-left: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n  margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n  margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n  margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n  margin-left: 3rem !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n  padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n  padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n  padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n  padding-left: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n  padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n  padding-left: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n  padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n  padding-left: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n  padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n  padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n  padding-left: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n  padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n  padding-left: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n  padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n  padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n  padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n  padding-left: 3rem !important;\n}\n\n.m-n1 {\n  margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n  margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n  margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n  margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n  margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n  margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n  margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n  margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n  margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n  margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n  margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n  margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n  margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n  margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n  margin-left: -1rem !important;\n}\n\n.m-n4 {\n  margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n  margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n  margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n  margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n  margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n  margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n  margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n  margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n  margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n  margin-left: -3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n  margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n  margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n  margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n  margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .mt-sm-0,\n  .my-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mr-sm-0,\n  .mx-sm-0 {\n    margin-right: 0 !important;\n  }\n  .mb-sm-0,\n  .my-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-sm-0,\n  .mx-sm-0 {\n    margin-left: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-sm-1,\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-sm-1,\n  .mx-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-sm-1,\n  .my-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-sm-1,\n  .mx-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-sm-2,\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-sm-2,\n  .mx-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-sm-2,\n  .my-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-sm-2,\n  .mx-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .mt-sm-3,\n  .my-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-sm-3,\n  .mx-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-sm-3,\n  .my-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-sm-3,\n  .mx-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-sm-4,\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-sm-4,\n  .mx-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-sm-4,\n  .my-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-sm-4,\n  .mx-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .mt-sm-5,\n  .my-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-sm-5,\n  .mx-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-sm-5,\n  .my-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-sm-5,\n  .mx-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .pt-sm-0,\n  .py-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pr-sm-0,\n  .px-sm-0 {\n    padding-right: 0 !important;\n  }\n  .pb-sm-0,\n  .py-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-sm-0,\n  .px-sm-0 {\n    padding-left: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-sm-1,\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-sm-1,\n  .px-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-sm-1,\n  .py-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-sm-1,\n  .px-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-sm-2,\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-sm-2,\n  .px-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-sm-2,\n  .py-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-sm-2,\n  .px-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .pt-sm-3,\n  .py-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-sm-3,\n  .px-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-sm-3,\n  .py-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-sm-3,\n  .px-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-sm-4,\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-sm-4,\n  .px-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-sm-4,\n  .py-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-sm-4,\n  .px-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .pt-sm-5,\n  .py-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-sm-5,\n  .px-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-sm-5,\n  .py-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-sm-5,\n  .px-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .m-sm-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-sm-n1,\n  .my-sm-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-sm-n1,\n  .mx-sm-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-sm-n1,\n  .my-sm-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-sm-n1,\n  .mx-sm-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-sm-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-sm-n2,\n  .my-sm-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-sm-n2,\n  .mx-sm-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-sm-n2,\n  .my-sm-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-sm-n2,\n  .mx-sm-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-sm-n3 {\n    margin: -1rem !important;\n  }\n  .mt-sm-n3,\n  .my-sm-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-sm-n3,\n  .mx-sm-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-sm-n3,\n  .my-sm-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-sm-n3,\n  .mx-sm-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-sm-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-sm-n4,\n  .my-sm-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-sm-n4,\n  .mx-sm-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-sm-n4,\n  .my-sm-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-sm-n4,\n  .mx-sm-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-sm-n5 {\n    margin: -3rem !important;\n  }\n  .mt-sm-n5,\n  .my-sm-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-sm-n5,\n  .mx-sm-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-sm-n5,\n  .my-sm-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-sm-n5,\n  .mx-sm-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mt-sm-auto,\n  .my-sm-auto {\n    margin-top: auto !important;\n  }\n  .mr-sm-auto,\n  .mx-sm-auto {\n    margin-right: auto !important;\n  }\n  .mb-sm-auto,\n  .my-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-sm-auto,\n  .mx-sm-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .mt-md-0,\n  .my-md-0 {\n    margin-top: 0 !important;\n  }\n  .mr-md-0,\n  .mx-md-0 {\n    margin-right: 0 !important;\n  }\n  .mb-md-0,\n  .my-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-md-0,\n  .mx-md-0 {\n    margin-left: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-md-1,\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-md-1,\n  .mx-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-md-1,\n  .my-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-md-1,\n  .mx-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-md-2,\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-md-2,\n  .mx-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-md-2,\n  .my-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-md-2,\n  .mx-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .mt-md-3,\n  .my-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-md-3,\n  .mx-md-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-md-3,\n  .my-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-md-3,\n  .mx-md-3 {\n    margin-left: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-md-4,\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-md-4,\n  .mx-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-md-4,\n  .my-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-md-4,\n  .mx-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .mt-md-5,\n  .my-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-md-5,\n  .mx-md-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-md-5,\n  .my-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-md-5,\n  .mx-md-5 {\n    margin-left: 3rem !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .pt-md-0,\n  .py-md-0 {\n    padding-top: 0 !important;\n  }\n  .pr-md-0,\n  .px-md-0 {\n    padding-right: 0 !important;\n  }\n  .pb-md-0,\n  .py-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-md-0,\n  .px-md-0 {\n    padding-left: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-md-1,\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-md-1,\n  .px-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-md-1,\n  .py-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-md-1,\n  .px-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-md-2,\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-md-2,\n  .px-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-md-2,\n  .py-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-md-2,\n  .px-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .pt-md-3,\n  .py-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-md-3,\n  .px-md-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-md-3,\n  .py-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-md-3,\n  .px-md-3 {\n    padding-left: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-md-4,\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-md-4,\n  .px-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-md-4,\n  .py-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-md-4,\n  .px-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .pt-md-5,\n  .py-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-md-5,\n  .px-md-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-md-5,\n  .py-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-md-5,\n  .px-md-5 {\n    padding-left: 3rem !important;\n  }\n  .m-md-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-md-n1,\n  .my-md-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-md-n1,\n  .mx-md-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-md-n1,\n  .my-md-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-md-n1,\n  .mx-md-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-md-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-md-n2,\n  .my-md-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-md-n2,\n  .mx-md-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-md-n2,\n  .my-md-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-md-n2,\n  .mx-md-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-md-n3 {\n    margin: -1rem !important;\n  }\n  .mt-md-n3,\n  .my-md-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-md-n3,\n  .mx-md-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-md-n3,\n  .my-md-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-md-n3,\n  .mx-md-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-md-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-md-n4,\n  .my-md-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-md-n4,\n  .mx-md-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-md-n4,\n  .my-md-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-md-n4,\n  .mx-md-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-md-n5 {\n    margin: -3rem !important;\n  }\n  .mt-md-n5,\n  .my-md-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-md-n5,\n  .mx-md-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-md-n5,\n  .my-md-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-md-n5,\n  .mx-md-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mt-md-auto,\n  .my-md-auto {\n    margin-top: auto !important;\n  }\n  .mr-md-auto,\n  .mx-md-auto {\n    margin-right: auto !important;\n  }\n  .mb-md-auto,\n  .my-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-md-auto,\n  .mx-md-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .mt-lg-0,\n  .my-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mr-lg-0,\n  .mx-lg-0 {\n    margin-right: 0 !important;\n  }\n  .mb-lg-0,\n  .my-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-lg-0,\n  .mx-lg-0 {\n    margin-left: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-lg-1,\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-lg-1,\n  .mx-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-lg-1,\n  .my-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-lg-1,\n  .mx-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-lg-2,\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-lg-2,\n  .mx-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-lg-2,\n  .my-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-lg-2,\n  .mx-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .mt-lg-3,\n  .my-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-lg-3,\n  .mx-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-lg-3,\n  .my-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-lg-3,\n  .mx-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-lg-4,\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-lg-4,\n  .mx-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-lg-4,\n  .my-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-lg-4,\n  .mx-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .mt-lg-5,\n  .my-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-lg-5,\n  .mx-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-lg-5,\n  .my-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-lg-5,\n  .mx-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .pt-lg-0,\n  .py-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pr-lg-0,\n  .px-lg-0 {\n    padding-right: 0 !important;\n  }\n  .pb-lg-0,\n  .py-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-lg-0,\n  .px-lg-0 {\n    padding-left: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-lg-1,\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-lg-1,\n  .px-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-lg-1,\n  .py-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-lg-1,\n  .px-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-lg-2,\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-lg-2,\n  .px-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-lg-2,\n  .py-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-lg-2,\n  .px-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .pt-lg-3,\n  .py-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-lg-3,\n  .px-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-lg-3,\n  .py-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-lg-3,\n  .px-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-lg-4,\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-lg-4,\n  .px-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-lg-4,\n  .py-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-lg-4,\n  .px-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .pt-lg-5,\n  .py-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-lg-5,\n  .px-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-lg-5,\n  .py-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-lg-5,\n  .px-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .m-lg-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-lg-n1,\n  .my-lg-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-lg-n1,\n  .mx-lg-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-lg-n1,\n  .my-lg-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-lg-n1,\n  .mx-lg-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-lg-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-lg-n2,\n  .my-lg-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-lg-n2,\n  .mx-lg-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-lg-n2,\n  .my-lg-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-lg-n2,\n  .mx-lg-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-lg-n3 {\n    margin: -1rem !important;\n  }\n  .mt-lg-n3,\n  .my-lg-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-lg-n3,\n  .mx-lg-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-lg-n3,\n  .my-lg-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-lg-n3,\n  .mx-lg-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-lg-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-lg-n4,\n  .my-lg-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-lg-n4,\n  .mx-lg-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-lg-n4,\n  .my-lg-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-lg-n4,\n  .mx-lg-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-lg-n5 {\n    margin: -3rem !important;\n  }\n  .mt-lg-n5,\n  .my-lg-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-lg-n5,\n  .mx-lg-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-lg-n5,\n  .my-lg-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-lg-n5,\n  .mx-lg-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mt-lg-auto,\n  .my-lg-auto {\n    margin-top: auto !important;\n  }\n  .mr-lg-auto,\n  .mx-lg-auto {\n    margin-right: auto !important;\n  }\n  .mb-lg-auto,\n  .my-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-lg-auto,\n  .mx-lg-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .mt-xl-0,\n  .my-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mr-xl-0,\n  .mx-xl-0 {\n    margin-right: 0 !important;\n  }\n  .mb-xl-0,\n  .my-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-xl-0,\n  .mx-xl-0 {\n    margin-left: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-xl-1,\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-xl-1,\n  .mx-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-xl-1,\n  .my-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-xl-1,\n  .mx-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-xl-2,\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-xl-2,\n  .mx-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-xl-2,\n  .my-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-xl-2,\n  .mx-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .mt-xl-3,\n  .my-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-xl-3,\n  .mx-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-xl-3,\n  .my-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-xl-3,\n  .mx-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-xl-4,\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-xl-4,\n  .mx-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-xl-4,\n  .my-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-xl-4,\n  .mx-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .mt-xl-5,\n  .my-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-xl-5,\n  .mx-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-xl-5,\n  .my-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-xl-5,\n  .mx-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .pt-xl-0,\n  .py-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pr-xl-0,\n  .px-xl-0 {\n    padding-right: 0 !important;\n  }\n  .pb-xl-0,\n  .py-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-xl-0,\n  .px-xl-0 {\n    padding-left: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-xl-1,\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-xl-1,\n  .px-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-xl-1,\n  .py-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-xl-1,\n  .px-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-xl-2,\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-xl-2,\n  .px-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-xl-2,\n  .py-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-xl-2,\n  .px-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .pt-xl-3,\n  .py-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-xl-3,\n  .px-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-xl-3,\n  .py-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-xl-3,\n  .px-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-xl-4,\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-xl-4,\n  .px-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-xl-4,\n  .py-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-xl-4,\n  .px-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .pt-xl-5,\n  .py-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-xl-5,\n  .px-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xl-5,\n  .py-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-xl-5,\n  .px-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .m-xl-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-xl-n1,\n  .my-xl-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-xl-n1,\n  .mx-xl-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-xl-n1,\n  .my-xl-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-xl-n1,\n  .mx-xl-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-xl-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-xl-n2,\n  .my-xl-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-xl-n2,\n  .mx-xl-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-xl-n2,\n  .my-xl-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-xl-n2,\n  .mx-xl-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-xl-n3 {\n    margin: -1rem !important;\n  }\n  .mt-xl-n3,\n  .my-xl-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-xl-n3,\n  .mx-xl-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-xl-n3,\n  .my-xl-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-xl-n3,\n  .mx-xl-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-xl-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-xl-n4,\n  .my-xl-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-xl-n4,\n  .mx-xl-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-xl-n4,\n  .my-xl-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-xl-n4,\n  .mx-xl-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-xl-n5 {\n    margin: -3rem !important;\n  }\n  .mt-xl-n5,\n  .my-xl-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-xl-n5,\n  .mx-xl-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-xl-n5,\n  .my-xl-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-xl-n5,\n  .mx-xl-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mt-xl-auto,\n  .my-xl-auto {\n    margin-top: auto !important;\n  }\n  .mr-xl-auto,\n  .mx-xl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xl-auto,\n  .my-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-xl-auto,\n  .mx-xl-auto {\n    margin-left: auto !important;\n  }\n}\n\n.stretched-link::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1;\n  pointer-events: auto;\n  content: \"\";\n  background-color: rgba(0, 0, 0, 0);\n}\n\n.text-monospace {\n  font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !important;\n}\n\n.text-justify {\n  text-align: justify !important;\n}\n\n.text-wrap {\n  white-space: normal !important;\n}\n\n.text-nowrap {\n  white-space: nowrap !important;\n}\n\n.text-truncate {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.text-left {\n  text-align: left !important;\n}\n\n.text-right {\n  text-align: right !important;\n}\n\n.text-center {\n  text-align: center !important;\n}\n\n@media (min-width: 576px) {\n  .text-sm-left {\n    text-align: left !important;\n  }\n  .text-sm-right {\n    text-align: right !important;\n  }\n  .text-sm-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .text-md-left {\n    text-align: left !important;\n  }\n  .text-md-right {\n    text-align: right !important;\n  }\n  .text-md-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .text-lg-left {\n    text-align: left !important;\n  }\n  .text-lg-right {\n    text-align: right !important;\n  }\n  .text-lg-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .text-xl-left {\n    text-align: left !important;\n  }\n  .text-xl-right {\n    text-align: right !important;\n  }\n  .text-xl-center {\n    text-align: center !important;\n  }\n}\n\n.text-lowercase {\n  text-transform: lowercase !important;\n}\n\n.text-uppercase {\n  text-transform: uppercase !important;\n}\n\n.text-capitalize {\n  text-transform: capitalize !important;\n}\n\n.font-weight-light {\n  font-weight: 300 !important;\n}\n\n.font-weight-lighter {\n  font-weight: lighter !important;\n}\n\n.font-weight-normal {\n  font-weight: 400 !important;\n}\n\n.font-weight-bold {\n  font-weight: 700 !important;\n}\n\n.font-weight-bolder {\n  font-weight: bolder !important;\n}\n\n.font-italic {\n  font-style: italic !important;\n}\n\n.text-white {\n  color: #fff !important;\n}\n\n.text-primary {\n  color: #000bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n  color: #0056b3 !important;\n}\n\n.text-secondary {\n  color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n  color: #494f54 !important;\n}\n\n.text-success {\n  color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n  color: #19692c !important;\n}\n\n.text-info {\n  color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n  color: #0f6674 !important;\n}\n\n.text-warning {\n  color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n  color: #ba8b00 !important;\n}\n\n.text-danger {\n  color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n  color: #a71d2a !important;\n}\n\n.text-light {\n  color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n  color: #cbd3da !important;\n}\n\n.text-dark {\n  color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n  color: #121416 !important;\n}\n\n.text-body {\n  color: #212529 !important;\n}\n\n.text-muted {\n  color: #6c757d !important;\n}\n\n.text-black-50 {\n  color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n.text-decoration-none {\n  text-decoration: none !important;\n}\n\n.text-break {\n  word-break: break-word !important;\n  word-wrap: break-word !important;\n}\n\n.text-reset {\n  color: inherit !important;\n}\n\n.visible {\n  visibility: visible !important;\n}\n\n.invisible {\n  visibility: hidden !important;\n}\n\n@media print {\n  *,\n  *::before,\n  *::after {\n    text-shadow: none !important;\n    box-shadow: none !important;\n  }\n  a:not(.btn) {\n    text-decoration: underline;\n  }\n  abbr[title]::after {\n    content: \" (\" attr(title) \")\";\n  }\n  pre {\n    white-space: pre-wrap !important;\n  }\n  pre,\n  blockquote {\n    border: 1px solid #adb5bd;\n    page-break-inside: avoid;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  @page {\n    size: a3;\n  }\n  body {\n    min-width: 992px !important;\n  }\n  .container {\n    min-width: 992px !important;\n  }\n  .navbar {\n    display: none;\n  }\n  .badge {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #dee2e6 !important;\n  }\n  .table-dark {\n    color: inherit;\n  }\n  .table-dark th,\n  .table-dark td,\n  .table-dark thead th,\n  .table-dark tbody + tbody {\n    border-color: #dee2e6;\n  }\n  .table .thead-dark th {\n    color: inherit;\n    border-color: #dee2e6;\n  }\n}\n\n@-webkit-keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@-webkit-keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@-webkit-keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@-webkit-keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@-webkit-keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.dark-mode :root {\n  --lightblue: #86bad8;\n  --navy: #002c59;\n  --olive: #74c8a3;\n  --lime: #67ffa9;\n  --fuchsia: #f672d8;\n  --maroon: #ed6c9b;\n  --blue: #3f6791;\n  --indigo: #6610f2;\n  --purple: #6f42c1;\n  --pink: #e83e8c;\n  --red: #e74c3c;\n  --orange: #fd7e14;\n  --yellow: #f39c12;\n  --green: #00bc8c;\n  --teal: #20c997;\n  --cyan: #3498db;\n  --white: #fff;\n  --gray: #6c757d;\n  --gray-dark: #343a40;\n  --primary: #3f6791;\n  --secondary: #6c757d;\n  --success: #00bc8c;\n  --info: #3498db;\n  --warning: #f39c12;\n  --danger: #e74c3c;\n  --light: #f8f9fa;\n  --dark: #343a40;\n}\n\n.animation__shake {\n  -webkit-animation: shake 1500ms;\n  animation: shake 1500ms;\n}\n\n.animation__wobble {\n  -webkit-animation: wobble 1500ms;\n  animation: wobble 1500ms;\n}\n\n.preloader {\n  display: -ms-flexbox;\n  display: flex;\n  background-color: #f4f6f9;\n  height: 100vh;\n  width: 100%;\n  transition: height 200ms linear;\n  position: fixed;\n  left: 0;\n  top: 0;\n  z-index: 9999;\n}\n\n.dark-mode .preloader {\n  background-color: #454d55 !important;\n  color: #fff;\n}\n\nhtml.scroll-smooth {\n  scroll-behavior: smooth;\n}\n\nhtml,\nbody,\n.wrapper {\n  min-height: 100%;\n}\n\n.wrapper {\n  position: relative;\n}\n\n.wrapper .content-wrapper {\n  min-height: calc(100vh - calc(3.5rem + 1px) - calc(3.5rem + 1px));\n}\n\n.layout-boxed .wrapper {\n  box-shadow: 0 0 10 rgba(0, 0, 0, 0.3);\n}\n\n.layout-boxed .wrapper, .layout-boxed .wrapper::before {\n  margin: 0 auto;\n  max-width: 1250px;\n  overflow: hidden;\n}\n\n.layout-boxed .wrapper .main-sidebar {\n  left: inherit;\n}\n\n@supports not (-webkit-touch-callout: none) {\n  .layout-fixed .wrapper .sidebar {\n    height: calc(100vh - (3.5rem + 1px));\n  }\n  .layout-fixed.text-sm .wrapper .sidebar {\n    height: calc(100vh - (2.93725rem + 1px));\n  }\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n  top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse .wrapper .brand-link {\n  height: calc(3.5rem + 1px);\n  width: 4.6rem;\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse .wrapper .brand-link.text-sm,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse .wrapper .brand-link.text-sm,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse .wrapper .brand-link.text-sm {\n  height: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse.text-sm .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse.text-sm .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse.text-sm .wrapper .brand-link {\n  height: calc(2.93725rem + 1px);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar {\n  margin-top: calc(calc(2.93725rem + 1px) / -1);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .control-sidebar {\n  top: 0;\n}\n\n.layout-navbar-fixed .wrapper a.anchor {\n  display: block;\n  position: relative;\n  top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n}\n\n.layout-navbar-fixed .wrapper .main-sidebar:hover .brand-link {\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n}\n\n.layout-navbar-fixed .wrapper .brand-link {\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n  z-index: 1035;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-lightblue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-lightblue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-navy .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-navy .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-olive .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-olive .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-lime .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-lime .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-fuchsia .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-fuchsia .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-maroon .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-maroon .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-blue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-blue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-indigo .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-indigo .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-purple .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-purple .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-pink .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-pink .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-red .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-red .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-orange .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-orange .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-yellow .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-yellow .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-green .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-green .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-teal .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-teal .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-cyan .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-cyan .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-white .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-white .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-gray .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-gray .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-gray-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-gray-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .main-header.border-bottom-0 ~ .content-wrapper {\n  margin-top: 3.5rem;\n}\n\n.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header {\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1033;\n}\n\n.layout-navbar-fixed.text-sm .wrapper .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-not-fixed .wrapper .brand-link {\n  position: static;\n}\n\n.layout-navbar-not-fixed .wrapper .sidebar,\n.layout-navbar-not-fixed .wrapper .content-wrapper {\n  margin-top: 0;\n}\n\n.layout-navbar-not-fixed .wrapper .main-header {\n  position: static;\n}\n\n.layout-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: 0;\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n  top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n.layout-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n.layout-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .control-sidebar {\n  top: 0;\n}\n\n.layout-navbar-fixed .wrapper a.anchor {\n  display: block;\n  position: relative;\n  top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n}\n\n.layout-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n  height: calc(3.5rem + 1px);\n  transition: width 0.3s ease-in-out;\n  width: 4.6rem;\n}\n\n.text-sm .layout-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n  height: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n}\n\n.layout-navbar-fixed .wrapper .brand-link {\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n  z-index: 1035;\n}\n\n.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n.layout-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header {\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1037;\n}\n\n.layout-navbar-fixed.text-sm .wrapper .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar {\n  margin-top: calc(calc(2.93725rem + 1px) / -1);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-not-fixed .wrapper .brand-link {\n  position: static;\n}\n\n.layout-navbar-not-fixed .wrapper .sidebar,\n.layout-navbar-not-fixed .wrapper .content-wrapper {\n  margin-top: 0;\n}\n\n.layout-navbar-not-fixed .wrapper .main-header {\n  position: static;\n}\n\n.layout-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: 0;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-sm-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-sm-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-sm-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-sm-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-sm-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-sm-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-sm-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-sm-navbar-not-fixed .wrapper .sidebar,\n  .layout-sm-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-sm-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-sm-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-md-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-md-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-md-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-md-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-md-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-md-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-md-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-md-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-md-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-md-navbar-not-fixed .wrapper .sidebar,\n  .layout-md-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-md-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-md-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-lg-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-lg-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-lg-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-lg-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-lg-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-lg-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-lg-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-lg-navbar-not-fixed .wrapper .sidebar,\n  .layout-lg-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-lg-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-lg-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-xl-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-xl-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-xl-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-xl-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-xl-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-xl-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-xl-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-xl-navbar-not-fixed .wrapper .sidebar,\n  .layout-xl-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-xl-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-xl-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n.layout-footer-fixed .wrapper .control-sidebar {\n  bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .main-footer {\n  bottom: 0;\n  left: 0;\n  position: fixed;\n  right: 0;\n  z-index: 1032;\n}\n\n.layout-footer-not-fixed .wrapper .main-footer {\n  position: static;\n}\n\n.layout-footer-not-fixed .wrapper .content-wrapper {\n  margin-bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .control-sidebar {\n  bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .main-footer {\n  bottom: 0;\n  left: 0;\n  position: fixed;\n  right: 0;\n  z-index: 1032;\n}\n\n.layout-footer-fixed .wrapper .content-wrapper {\n  padding-bottom: calc(3.5rem + 1px);\n}\n\n.layout-footer-not-fixed .wrapper .main-footer {\n  position: static;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-sm-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-sm-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-sm-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-md-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-md-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-md-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-lg-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-lg-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-lg-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-xl-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-xl-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-xl-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n.layout-top-nav .wrapper {\n  margin-left: 0;\n}\n\n.layout-top-nav .wrapper .main-header .brand-image {\n  margin-top: -.5rem;\n  margin-right: .2rem;\n  height: 33px;\n}\n\n.layout-top-nav .wrapper .main-sidebar {\n  bottom: inherit;\n  height: inherit;\n}\n\n.layout-top-nav .wrapper .content-wrapper,\n.layout-top-nav .wrapper .main-header,\n.layout-top-nav .wrapper .main-footer {\n  margin-left: 0;\n}\n\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .content-wrapper, body.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .content-wrapper::before,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-footer,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-footer::before,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-header,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-header::before {\n  margin-left: 0;\n}\n\n@media (min-width: 768px) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper, .sidebar-collapse\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer, .sidebar-collapse\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (max-width: 991.98px) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse .sidebar-mini-md .content-wrapper, .sidebar-collapse\n  .sidebar-mini-md .main-footer, .sidebar-collapse\n  .sidebar-mini-md .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse .sidebar-mini-xs .content-wrapper, .sidebar-collapse\n  .sidebar-mini-xs .main-footer, .sidebar-collapse\n  .sidebar-mini-xs .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n.content-wrapper {\n  background-color: #f4f6f9;\n}\n\n.content-wrapper > .content {\n  padding: 0 0.5rem;\n}\n\n.main-sidebar, .main-sidebar::before {\n  transition: margin-left 0.3s ease-in-out, width 0.3s ease-in-out;\n  width: 250px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .main-sidebar, .main-sidebar::before {\n    transition: none;\n  }\n}\n\n.sidebar-collapse:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-sidebar, .sidebar-collapse:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-sidebar::before {\n  box-shadow: none !important;\n}\n\n.sidebar-collapse .main-sidebar, .sidebar-collapse .main-sidebar::before {\n  margin-left: -250px;\n}\n\n.sidebar-collapse .main-sidebar .nav-sidebar.nav-child-indent .nav-treeview {\n  padding: 0;\n}\n\n@media (max-width: 767.98px) {\n  .main-sidebar, .main-sidebar::before {\n    box-shadow: none !important;\n    margin-left: -250px;\n  }\n  .sidebar-open .main-sidebar, .sidebar-open .main-sidebar::before {\n    margin-left: 0;\n  }\n}\n\nbody:not(.layout-fixed) .main-sidebar {\n  height: inherit;\n  min-height: 100%;\n  position: absolute;\n  top: 0;\n}\n\nbody:not(.layout-fixed) .main-sidebar .sidebar {\n  overflow-y: auto;\n}\n\n.layout-fixed .brand-link {\n  width: 250px;\n}\n\n.layout-fixed .main-sidebar {\n  bottom: 0;\n  float: none;\n  left: 0;\n  position: fixed;\n  top: 0;\n}\n\n.layout-fixed .control-sidebar {\n  bottom: 0;\n  float: none;\n  position: fixed;\n  top: 0;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content {\n  height: calc(100vh - calc(3.5rem + 1px));\n  overflow-y: auto;\n  scrollbar-width: thin;\n  scrollbar-color: #a9a9a9 transparent;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar {\n  width: .5rem;\n  height: .5rem;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-thumb {\n  background-color: #a9a9a9;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-track {\n  background-color: transparent;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n@supports (-webkit-touch-callout: none) {\n  .layout-fixed .main-sidebar {\n    height: inherit;\n  }\n}\n\n.main-footer {\n  background-color: #fff;\n  border-top: 1px solid #dee2e6;\n  color: #869099;\n  padding: 1rem;\n}\n\n.text-sm .main-footer, .main-footer.text-sm {\n  padding: 0.812rem;\n}\n\n.content-header {\n  padding: 15px 0.5rem;\n}\n\n.text-sm .content-header {\n  padding: 10px 0.5rem;\n}\n\n.content-header h1 {\n  font-size: 1.8rem;\n  margin: 0;\n}\n\n.text-sm .content-header h1 {\n  font-size: 1.5rem;\n}\n\n.content-header .breadcrumb {\n  background-color: transparent;\n  line-height: 1.8rem;\n  margin-bottom: 0;\n  padding: 0;\n}\n\n.text-sm .content-header .breadcrumb {\n  line-height: 1.5rem;\n}\n\n.hold-transition .content-wrapper,\n.hold-transition .main-header,\n.hold-transition .main-sidebar,\n.hold-transition .main-sidebar *,\n.hold-transition .control-sidebar,\n.hold-transition .control-sidebar *,\n.hold-transition .main-footer {\n  transition: none !important;\n  -webkit-animation-duration: 0s !important;\n  animation-duration: 0s !important;\n}\n\n.dark-mode {\n  background-color: #454d55 !important;\n  color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n.dark-mode .breadcrumb-item.active,\n.dark-mode .breadcrumb-item + .breadcrumb-item::before {\n  color: #adb5bd;\n}\n\n.dark-mode .main-footer {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.dark-mode .content-wrapper {\n  background-color: #454d55;\n  color: #fff;\n}\n\n.dark-mode .content-wrapper .content-header {\n  color: #fff;\n}\n\n.main-header {\n  border-bottom: 1px solid #dee2e6;\n  z-index: 1034;\n}\n\n.main-header .nav-link {\n  height: 2.5rem;\n  position: relative;\n}\n\n.text-sm .main-header .nav-link, .main-header.text-sm .nav-link {\n  height: 1.93725rem;\n  padding: 0.35rem 1rem;\n}\n\n.text-sm .main-header .nav-link > .fa,\n.text-sm .main-header .nav-link > .fas,\n.text-sm .main-header .nav-link > .far,\n.text-sm .main-header .nav-link > .fab,\n.text-sm .main-header .nav-link > .fal,\n.text-sm .main-header .nav-link > .fad,\n.text-sm .main-header .nav-link > .svg-inline--fa,\n.text-sm .main-header .nav-link > .ion, .main-header.text-sm .nav-link > .fa,\n.main-header.text-sm .nav-link > .fas,\n.main-header.text-sm .nav-link > .far,\n.main-header.text-sm .nav-link > .fab,\n.main-header.text-sm .nav-link > .fal,\n.main-header.text-sm .nav-link > .fad,\n.main-header.text-sm .nav-link > .svg-inline--fa,\n.main-header.text-sm .nav-link > .ion {\n  font-size: 0.875rem;\n}\n\n.main-header .navbar-nav .nav-item {\n  margin: 0;\n}\n\n.main-header .navbar-nav[class*=\"-right\"] .dropdown-menu {\n  left: auto;\n  margin-top: -3px;\n  right: 0;\n}\n\n@media (max-width: 575.98px) {\n  .main-header .navbar-nav[class*=\"-right\"] .dropdown-menu {\n    left: 0;\n    right: auto;\n  }\n}\n\n.main-header.dropdown-legacy .dropdown-menu {\n  top: 3rem;\n  margin-top: 0;\n}\n\n.navbar-img {\n  height: calc(calc(3.5rem + 1px) * .5);\n  width: auto;\n}\n\n.navbar-badge {\n  font-size: .6rem;\n  font-weight: 300;\n  padding: 2px 4px;\n  position: absolute;\n  right: 5px;\n  top: 9px;\n}\n\n.btn-navbar {\n  background-color: transparent;\n  border-left-width: 0;\n}\n\n.form-control-navbar {\n  border-right-width: 0;\n}\n\n.form-control-navbar + .input-group-append {\n  margin-left: 0;\n}\n\n.form-control-navbar,\n.btn-navbar {\n  transition: none;\n}\n\n.navbar-dark .form-control-navbar,\n.navbar-dark .btn-navbar {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar:focus,\n.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #495057;\n  border-color: #6c757d !important;\n  color: #ced4da;\n}\n\n.navbar-light .form-control-navbar,\n.navbar-light .btn-navbar {\n  background-color: #dadfe4;\n  border-color: #ced4da;\n}\n\n.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar:focus,\n.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d3d9df;\n  border-color: #c7ced5 !important;\n  color: #ced4da;\n}\n\n.navbar-light .navbar-search-block .form-control-navbar:focus,\n.navbar-light .navbar-search-block .form-control-navbar:focus + .input-group-append .btn-navbar {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-search-block {\n  position: absolute;\n  padding: 0 1rem;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 10;\n  display: none;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  background-color: initial;\n}\n\n.navbar-search-block.navbar-search-open {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.navbar-search-block .input-group {\n  width: 100%;\n}\n\n.brand-link {\n  display: block;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  padding: 0.8125rem 0.5rem;\n  transition: width 0.3s ease-in-out;\n  white-space: nowrap;\n}\n\n.brand-link:hover {\n  color: #fff;\n  text-decoration: none;\n}\n\n.text-sm .brand-link {\n  font-size: inherit;\n}\n\n[class*=\"sidebar-dark\"] .brand-link {\n  border-bottom: 1px solid #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .brand-link,\n[class*=\"sidebar-dark\"] .brand-link .pushmenu {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n[class*=\"sidebar-dark\"] .brand-link:hover,\n[class*=\"sidebar-dark\"] .brand-link .pushmenu:hover {\n  color: #fff;\n}\n\n[class*=\"sidebar-light\"] .brand-link {\n  border-bottom: 1px solid #dee2e6;\n}\n\n[class*=\"sidebar-light\"] .brand-link,\n[class*=\"sidebar-light\"] .brand-link .pushmenu {\n  color: rgba(0, 0, 0, 0.8);\n}\n\n[class*=\"sidebar-light\"] .brand-link:hover,\n[class*=\"sidebar-light\"] .brand-link .pushmenu:hover {\n  color: #000;\n}\n\n.brand-link .pushmenu {\n  margin-right: 0.5rem;\n  font-size: 1rem;\n}\n\n.brand-link .brand-link {\n  padding: 0;\n  border-bottom: none;\n}\n\n.brand-link .brand-image {\n  float: left;\n  line-height: .8;\n  margin-left: .8rem;\n  margin-right: .5rem;\n  margin-top: -3px;\n  max-height: 33px;\n  width: auto;\n}\n\n.brand-link .brand-image-xs {\n  float: left;\n  line-height: .8;\n  margin-top: -.1rem;\n  max-height: 33px;\n  width: auto;\n}\n\n.brand-link .brand-image-xl {\n  line-height: .8;\n  max-height: 40px;\n  width: auto;\n}\n\n.brand-link .brand-image-xl.single {\n  margin-top: -.3rem;\n}\n\n.brand-link.text-sm .brand-image,\n.text-sm .brand-link .brand-image {\n  height: 29px;\n  margin-bottom: -.25rem;\n  margin-left: .95rem;\n  margin-top: -.25rem;\n}\n\n.brand-link.text-sm .brand-image-xs,\n.text-sm .brand-link .brand-image-xs {\n  margin-top: -.2rem;\n  max-height: 29px;\n}\n\n.brand-link.text-sm .brand-image-xl,\n.text-sm .brand-link .brand-image-xl {\n  margin-top: -.225rem;\n  max-height: 38px;\n}\n\n.main-sidebar {\n  height: 100vh;\n  overflow-y: hidden;\n  z-index: 1038;\n}\n\n.main-sidebar a:-moz-focusring {\n  border: 0;\n  outline: none;\n}\n\n.sidebar {\n  height: calc(100% - (3.5rem + 1px));\n  overflow-x: hidden;\n  overflow-y: initial;\n  padding-bottom: 0;\n  padding-left: 0.5rem;\n  padding-right: 0.5rem;\n  padding-top: 0;\n  scrollbar-color: #a9a9a9 transparent;\n  scrollbar-width: none;\n}\n\n.sidebar::-webkit-scrollbar-thumb {\n  background-color: #a9a9a9;\n}\n\n.sidebar::-webkit-scrollbar-track {\n  background-color: transparent;\n}\n\n.sidebar::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n.sidebar::-webkit-scrollbar {\n  width: 0;\n}\n\n.sidebar:hover {\n  scrollbar-width: thin;\n}\n\n.sidebar:hover::-webkit-scrollbar {\n  width: .5rem;\n  height: .5rem;\n}\n\n.brand-link.border-bottom-0 ~ .sidebar {\n  height: calc(100% - 3.5rem);\n}\n\n.user-panel {\n  position: relative;\n}\n\n[class*=\"sidebar-dark\"] .user-panel {\n  border-bottom: 1px solid #4f5962;\n}\n\n[class*=\"sidebar-light\"] .user-panel {\n  border-bottom: 1px solid #dee2e6;\n}\n\n.user-panel,\n.user-panel .info {\n  overflow: hidden;\n  white-space: nowrap;\n}\n\n.user-panel .image {\n  display: inline-block;\n  padding-left: 0.8rem;\n}\n\n.user-panel img {\n  height: auto;\n  width: 2.1rem;\n}\n\n.user-panel .info {\n  display: inline-block;\n  padding: 5px 5px 5px 10px;\n}\n\n.user-panel .status,\n.user-panel .dropdown-menu {\n  font-size: 0.875rem;\n}\n\n.nav-sidebar .nav-item > .nav-link {\n  margin-bottom: .2rem;\n}\n\n.nav-sidebar .nav-item > .nav-link .right {\n  transition: -webkit-transform ease-in-out 0.3s;\n  transition: transform ease-in-out 0.3s;\n  transition: transform ease-in-out 0.3s, -webkit-transform ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .nav-sidebar .nav-item > .nav-link .right {\n    transition: none;\n  }\n}\n\n.nav-sidebar .nav-link > .right,\n.nav-sidebar .nav-link > p > .right {\n  position: absolute;\n  right: 1rem;\n  top: .7rem;\n}\n\n.nav-sidebar .nav-link > .right i,\n.nav-sidebar .nav-link > .right span,\n.nav-sidebar .nav-link > p > .right i,\n.nav-sidebar .nav-link > p > .right span {\n  margin-left: .5rem;\n}\n\n.nav-sidebar .nav-link > .right:nth-child(2),\n.nav-sidebar .nav-link > p > .right:nth-child(2) {\n  right: 2.2rem;\n}\n\n.nav-sidebar .menu-open > .nav-treeview {\n  display: block;\n}\n\n.nav-sidebar .menu-open > .nav-link svg.right,\n.nav-sidebar .menu-open > .nav-link i.right,\n.nav-sidebar .menu-is-opening > .nav-link svg.right,\n.nav-sidebar .menu-is-opening > .nav-link i.right {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n.nav-sidebar > .nav-item {\n  margin-bottom: 0;\n}\n\n.nav-sidebar > .nav-item .nav-icon {\n  margin-left: .05rem;\n  font-size: 1.2rem;\n  margin-right: .2rem;\n  text-align: center;\n  width: 1.6rem;\n}\n\n.nav-sidebar > .nav-item .nav-icon.fa, .nav-sidebar > .nav-item .nav-icon.fas, .nav-sidebar > .nav-item .nav-icon.far, .nav-sidebar > .nav-item .nav-icon.fab, .nav-sidebar > .nav-item .nav-icon.fal, .nav-sidebar > .nav-item .nav-icon.fad, .nav-sidebar > .nav-item .nav-icon.svg-inline--fa, .nav-sidebar > .nav-item .nav-icon.ion {\n  font-size: 1.1rem;\n}\n\n.nav-sidebar > .nav-item .float-right {\n  margin-top: 3px;\n}\n\n.nav-sidebar .nav-treeview {\n  display: none;\n  list-style: none;\n  padding: 0;\n}\n\n.nav-sidebar .nav-treeview > .nav-item > .nav-link > .nav-icon {\n  width: 1.6rem;\n}\n\n.nav-sidebar.nav-child-indent .nav-treeview {\n  transition: padding 0.3s ease-in-out;\n  padding-left: 1rem;\n}\n\n.text-sm .nav-sidebar.nav-child-indent .nav-treeview {\n  padding-left: .5rem;\n}\n\n.nav-sidebar.nav-child-indent.nav-legacy .nav-treeview .nav-treeview {\n  padding-left: 2rem;\n  margin-left: -1rem;\n}\n\n.text-sm .nav-sidebar.nav-child-indent.nav-legacy .nav-treeview .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.nav-sidebar .nav-header {\n  font-size: .9rem;\n  padding: 0.5rem 0.75rem;\n}\n\n.nav-sidebar .nav-link p {\n  display: inline;\n  margin: 0;\n  white-space: normal;\n}\n\n.sidebar-is-opening .sidebar .nav-sidebar .nav-link p {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n#sidebar-overlay {\n  background-color: rgba(0, 0, 0, 0.1);\n  bottom: 0;\n  display: none;\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1037;\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-open #sidebar-overlay {\n    display: block;\n  }\n}\n\n[class*=\"sidebar-light-\"] {\n  background-color: #fff;\n}\n\n[class*=\"sidebar-light-\"] .user-panel a:hover {\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .status {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .status:hover, [class*=\"sidebar-light-\"] .user-panel .status:focus, [class*=\"sidebar-light-\"] .user-panel .status:active {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .dropdown-menu {\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .user-panel .dropdown-item {\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link:active, [class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link:focus {\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item.menu-open > .nav-link,\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item:hover > .nav-link {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link.active {\n  color: #000;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-treeview {\n  background-color: transparent;\n}\n\n[class*=\"sidebar-light-\"] .nav-header {\n  background-color: inherit;\n  color: #292d32;\n}\n\n[class*=\"sidebar-light-\"] .sidebar a {\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .sidebar a:hover {\n  text-decoration: none;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link {\n  color: #777;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:hover, [class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:focus {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #000;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link.active:hover {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview .nav-treeview {\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link, [class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link.active {\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-dark-\"] {\n  background-color: #343a40;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel a:hover {\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .status {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .status:hover, [class*=\"sidebar-dark-\"] .user-panel .status:focus, [class*=\"sidebar-dark-\"] .user-panel .status:active {\n  background-color: rgba(247, 247, 247, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .dropdown-menu {\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);\n  border-color: rgba(242, 242, 242, 0.1);\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .dropdown-item {\n  color: #212529;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link:active {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item.menu-open > .nav-link,\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item:hover > .nav-link,\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link:focus {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link.active {\n  color: #fff;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-treeview {\n  background-color: transparent;\n}\n\n[class*=\"sidebar-dark-\"] .nav-header {\n  background-color: inherit;\n  color: #d0d4db;\n}\n\n[class*=\"sidebar-dark-\"] .sidebar a {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .sidebar a:hover, [class*=\"sidebar-dark-\"] .sidebar a:focus {\n  text-decoration: none;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link:hover, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link:focus {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active:hover, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active:focus {\n  background-color: rgba(255, 255, 255, 0.9);\n  color: #343a40;\n}\n\n[class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview .nav-treeview {\n  border-color: rgba(255, 255, 255, 0.9);\n}\n\n[class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link, [class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link.active {\n  border-color: rgba(255, 255, 255, 0.9);\n}\n\n.sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.sidebar-dark-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #007bff;\n}\n\n.sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.sidebar-dark-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.sidebar-dark-success .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-success .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.sidebar-dark-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #28a745;\n}\n\n.sidebar-dark-info .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-info .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.sidebar-dark-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #17a2b8;\n}\n\n.sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-warning .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ffc107;\n}\n\n.sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-danger .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.sidebar-dark-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #dc3545;\n}\n\n.sidebar-dark-light .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-light .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f8f9fa;\n}\n\n.sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.sidebar-dark-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.sidebar-dark-lightblue .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-lightblue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.sidebar-dark-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3c8dbc;\n}\n\n.sidebar-dark-navy .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-navy .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.sidebar-dark-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #001f3f;\n}\n\n.sidebar-dark-olive .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-olive .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.sidebar-dark-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3d9970;\n}\n\n.sidebar-dark-lime .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-lime .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #01ff70;\n}\n\n.sidebar-dark-fuchsia .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-fuchsia .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.sidebar-dark-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f012be;\n}\n\n.sidebar-dark-maroon .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-maroon .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.sidebar-dark-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #d81b60;\n}\n\n.sidebar-dark-blue .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-blue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.sidebar-dark-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #007bff;\n}\n\n.sidebar-dark-indigo .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-indigo .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.sidebar-dark-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6610f2;\n}\n\n.sidebar-dark-purple .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-purple .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.sidebar-dark-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6f42c1;\n}\n\n.sidebar-dark-pink .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-pink .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.sidebar-dark-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e83e8c;\n}\n\n.sidebar-dark-red .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-red .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.sidebar-dark-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #dc3545;\n}\n\n.sidebar-dark-orange .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-orange .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fd7e14;\n}\n\n.sidebar-dark-yellow .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-yellow .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ffc107;\n}\n\n.sidebar-dark-green .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-green .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.sidebar-dark-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #28a745;\n}\n\n.sidebar-dark-teal .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-teal .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.sidebar-dark-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #20c997;\n}\n\n.sidebar-dark-cyan .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-cyan .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.sidebar-dark-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #17a2b8;\n}\n\n.sidebar-dark-white .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-white .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fff;\n}\n\n.sidebar-dark-gray .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-gray .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.sidebar-dark-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.sidebar-dark-gray-dark .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-gray-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.sidebar-dark-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.nav-flat {\n  margin: -0.25rem -0.5rem 0;\n}\n\n.nav-flat .nav-item > .nav-link {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.nav-flat .nav-item > .nav-link > .nav-icon {\n  margin-left: .55rem;\n}\n\n.nav-flat:not(.nav-child-indent) .nav-treeview .nav-item > .nav-link > .nav-icon {\n  margin-left: .4rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview {\n  padding-left: 0;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-icon {\n  margin-left: .85rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview {\n  border-left: .2rem solid;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.15rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.45rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.75rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 2.05rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-icon {\n  margin-left: .55rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-link {\n  padding-left: calc(1rem - .2rem);\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-icon {\n  margin-left: .35rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: .15rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: -.15rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: -.35rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon {\n  margin-left: .4rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon {\n  margin-left: .85rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.15rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.45rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.75rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 2.05rem;\n}\n\n.nav-flat .nav-icon {\n  transition: margin-left ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .nav-flat .nav-icon {\n    transition: none;\n  }\n}\n\n.nav-flat .nav-treeview .nav-icon {\n  margin-left: -.2rem;\n}\n\n.nav-flat.nav-sidebar > .nav-item .nav-treeview,\n.nav-flat.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n.nav-flat.nav-sidebar > .nav-item .nav-treeview .nav-item > .nav-link,\n.nav-flat.nav-sidebar > .nav-item > .nav-treeview .nav-item > .nav-link {\n  border-left: .2rem solid;\n}\n\n.nav-legacy {\n  margin: -0.25rem -0.5rem 0;\n}\n\n.nav-legacy.nav-sidebar .nav-item > .nav-link {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.nav-legacy.nav-sidebar .nav-item > .nav-link > .nav-icon {\n  margin-left: .55rem;\n}\n\n.text-sm .nav-legacy.nav-sidebar .nav-item > .nav-link > .nav-icon {\n  margin-left: .75rem;\n}\n\n.nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  background-color: inherit;\n  border-left: 3px solid transparent;\n  box-shadow: none;\n}\n\n.nav-legacy.nav-sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.55rem - 3px);\n}\n\n.text-sm .nav-legacy.nav-sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n.text-sm .nav-legacy.nav-sidebar.nav-flat .nav-treeview .nav-item > .nav-link > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n.sidebar-mini .nav-legacy > .nav-item .nav-link .nav-icon,\n.sidebar-mini-md .nav-legacy > .nav-item .nav-link .nav-icon,\n.sidebar-mini-xs .nav-legacy > .nav-item .nav-link .nav-icon {\n  transition: margin-left ease-in-out 0.3s;\n  margin-left: .6rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar-mini .nav-legacy > .nav-item .nav-link .nav-icon,\n  .sidebar-mini-md .nav-legacy > .nav-item .nav-link .nav-icon,\n  .sidebar-mini-xs .nav-legacy > .nav-item .nav-link .nav-icon {\n    transition: none;\n  }\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview {\n  padding-left: 1rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 2rem;\n  margin-left: -1rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview {\n  padding-left: .5rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon {\n  margin-left: .55rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: .36rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 0;\n  margin-left: 0;\n}\n\n.sidebar-mini.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-md.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-xs.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon {\n  margin-left: .75rem;\n}\n\n.sidebar-mini.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-md.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-xs.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item .nav-treeview,\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  color: #fff;\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:focus, [class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: transparent;\n  color: #fff;\n}\n\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item .nav-treeview,\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  color: #000;\n}\n\n[class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:focus, [class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: transparent;\n  color: #000;\n}\n\n.nav-collapse-hide-child .menu-open > .nav-treeview {\n  max-height: -webkit-min-content;\n  max-height: -moz-min-content;\n  max-height: min-content;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.sidebar-collapse .sidebar:not(:hover) .nav-collapse-hide-child .menu-open > .nav-treeview {\n  max-height: 0;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.main-sidebar.sidebar-focused .nav-collapse-hide-child .sidebar-mini.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover .nav-collapse-hide-child .sidebar-mini.sidebar-collapse .menu-open > .nav-treeview, .main-sidebar.sidebar-focused\n.nav-collapse-hide-child .sidebar-mini-md.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover\n.nav-collapse-hide-child .sidebar-mini-md.sidebar-collapse .menu-open > .nav-treeview, .main-sidebar.sidebar-focused\n.nav-collapse-hide-child .sidebar-mini-xs.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover\n.nav-collapse-hide-child .sidebar-mini-xs.sidebar-collapse .menu-open > .nav-treeview {\n  max-height: -webkit-min-content;\n  max-height: -moz-min-content;\n  max-height: min-content;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.nav-compact .nav-link,\n.nav-compact .nav-header {\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n}\n\n.nav-compact .nav-header:not(:first-of-type) {\n  padding-top: 0.75rem;\n  padding-bottom: 0.25rem;\n}\n\n.nav-compact .nav-link > .right,\n.nav-compact .nav-link > p > .right {\n  top: .465rem;\n}\n\n.text-sm .nav-compact .nav-link > .right,\n.text-sm .nav-compact .nav-link > p > .right {\n  top: .7rem;\n}\n\n[class*=\"sidebar-dark\"] .form-control-sidebar,\n[class*=\"sidebar-dark\"] .btn-sidebar {\n  background-color: #3f474e;\n  border: 1px solid #56606a;\n  color: white;\n}\n\n[class*=\"sidebar-dark\"] .form-control-sidebar:focus,\n[class*=\"sidebar-dark\"] .btn-sidebar:focus {\n  border: 1px solid #7a8793;\n}\n\n[class*=\"sidebar-dark\"] .btn-sidebar:hover {\n  background-color: #454d55;\n}\n\n[class*=\"sidebar-dark\"] .btn-sidebar:focus {\n  background-color: #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item {\n  background-color: #454d55;\n  border-color: #56606a;\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item:hover {\n  background-color: #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item:focus {\n  background-color: #515a63;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item .search-path {\n  color: #adb5bd;\n}\n\n[class*=\"sidebar-light\"] .form-control-sidebar,\n[class*=\"sidebar-light\"] .btn-sidebar {\n  background-color: #f2f2f2;\n  border: 1px solid #d9d9d9;\n  color: #1f2d3d;\n}\n\n[class*=\"sidebar-light\"] .form-control-sidebar:focus,\n[class*=\"sidebar-light\"] .btn-sidebar:focus {\n  border: 1px solid #b3b3b3;\n}\n\n[class*=\"sidebar-light\"] .btn-sidebar:hover {\n  background-color: #ececec;\n}\n\n[class*=\"sidebar-light\"] .btn-sidebar:focus {\n  background-color: #e6e6e6;\n}\n\n[class*=\"sidebar-light\"] .list-group-item {\n  border-color: #d9d9d9;\n}\n\n[class*=\"sidebar-light\"] .list-group-item:hover {\n  background-color: #ececec;\n}\n\n[class*=\"sidebar-light\"] .list-group-item:focus {\n  background-color: #e6e6e6;\n}\n\n[class*=\"sidebar-light\"] .list-group-item .search-path {\n  color: #6c757d;\n}\n\n.sidebar .form-inline .input-group {\n  width: 100%;\n  -ms-flex-wrap: nowrap;\n  flex-wrap: nowrap;\n}\n\n.sidebar nav .form-inline {\n  margin-bottom: .2rem;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs).sidebar-collapse .main-sidebar {\n  margin-left: 0;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .content-wrapper,\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-header,\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-footer {\n  z-index: 9999;\n  position: relative;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .control-sidebar {\n  z-index: 9999;\n}\n\n.sidebar-collapse .form-control-sidebar,\n.sidebar-collapse .form-control-sidebar ~ .input-group-append,\n.sidebar-collapse .sidebar-search-results {\n  display: none;\n}\n\n[data-widget=\"sidebar-search\"] input[type=\"search\"]::-ms-clear, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-ms-reveal {\n  display: none;\n  width: 0;\n  height: 0;\n}\n\n[data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-cancel-button, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-decoration, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-results-button, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-results-decoration {\n  display: none;\n}\n\n.sidebar-search-results {\n  position: relative;\n  display: none;\n  width: 100%;\n}\n\n.sidebar-search-open .sidebar-search-results {\n  display: inline-block;\n}\n\n.sidebar-search-results .search-title {\n  margin-bottom: -.1rem;\n}\n\n.sidebar-search-results .list-group {\n  position: absolute;\n  width: 100%;\n  z-index: 1039;\n}\n\n.sidebar-search-results .list-group > .list-group-item {\n  padding: 0.375rem 0.75rem;\n}\n\n.sidebar-search-results .list-group > .list-group-item:-moz-focusring {\n  margin-top: 0;\n  border-left: 1px solid transparent;\n  border-top: 0;\n  border-bottom: 1px solid transparent;\n}\n\n.sidebar-search-results .list-group > .list-group-item:first-child {\n  margin-top: 0;\n  border-top: 0;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.sidebar-search-results .search-path {\n  font-size: 80%;\n}\n\n.sidebar-search-open .btn,\n.sidebar-search-open .form-control {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n[class*=\"sidebar-dark\"] .sidebar-custom {\n  border-top: 1px solid #4f5962;\n}\n\n[class*=\"sidebar-light\"] .sidebar-custom {\n  border-top: 1px solid #dee2e6;\n}\n\n.layout-fixed.sidebar-collapse .hide-on-collapse {\n  display: none;\n}\n\n.layout-fixed.sidebar-collapse:hover .hide-on-collapse {\n  display: block;\n}\n\n.layout-fixed.text-sm .main-sidebar-custom .sidebar {\n  height: calc(100% - ((2.93725rem + 3.8rem) + 1px));\n}\n\n.layout-fixed.text-sm .main-sidebar-custom .sidebar-custom {\n  height: 3.8rem;\n  padding: 0.85rem 0.5rem;\n}\n\n.layout-fixed .main-sidebar-custom {\n  height: -webkit-fill-available;\n  height: -moz-available;\n  height: -ms-stretch;\n  height: stretch;\n}\n\n.layout-fixed .main-sidebar-custom .sidebar {\n  height: calc(100% - ((3.5rem + 4rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom .sidebar-custom {\n  height: 4rem;\n  padding: 0.85rem 0.5rem;\n}\n\n.layout-fixed .main-sidebar-custom-lg .sidebar {\n  height: calc(100% - ((3.5rem + 6rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom-lg .sidebar-custom {\n  height: 6rem;\n}\n\n.layout-fixed .main-sidebar-custom-xl .sidebar {\n  height: calc(100% - ((3.5rem + 8rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom-xl .sidebar-custom {\n  height: 8rem;\n}\n\n.layout-fixed .main-sidebar-custom .pos-right,\n.layout-fixed .main-sidebar-custom-lg .pos-right,\n.layout-fixed .main-sidebar-custom-xl .pos-right {\n  position: absolute;\n  right: .5rem;\n}\n\n.sidebar-hidden .main-sidebar,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .main-sidebar {\n  display: none !important;\n}\n\n.sidebar-hidden .content-wrapper,\n.sidebar-hidden .main-header,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .content-wrapper,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .main-header {\n  margin-left: 0 !important;\n}\n\n.dark-mode .sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3f6791;\n}\n\n.dark-mode .sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.dark-mode .sidebar-dark-success .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-success .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #00bc8c;\n}\n\n.dark-mode .sidebar-dark-info .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-info .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3498db;\n}\n\n.dark-mode .sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-warning .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f39c12;\n}\n\n.dark-mode .sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-danger .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e74c3c;\n}\n\n.dark-mode .sidebar-dark-light .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-light .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f8f9fa;\n}\n\n.dark-mode .sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.dark-mode .sidebar-dark-lightblue .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lightblue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #86bad8;\n}\n\n.dark-mode .sidebar-dark-navy .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-navy .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #002c59;\n}\n\n.dark-mode .sidebar-dark-olive .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-olive .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #74c8a3;\n}\n\n.dark-mode .sidebar-dark-lime .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lime .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #67ffa9;\n}\n\n.dark-mode .sidebar-dark-fuchsia .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-fuchsia .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f672d8;\n}\n\n.dark-mode .sidebar-dark-maroon .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-maroon .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ed6c9b;\n}\n\n.dark-mode .sidebar-dark-blue .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-blue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3f6791;\n}\n\n.dark-mode .sidebar-dark-indigo .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-indigo .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6610f2;\n}\n\n.dark-mode .sidebar-dark-purple .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-purple .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6f42c1;\n}\n\n.dark-mode .sidebar-dark-pink .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-pink .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e83e8c;\n}\n\n.dark-mode .sidebar-dark-red .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-red .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e74c3c;\n}\n\n.dark-mode .sidebar-dark-orange .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-orange .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fd7e14;\n}\n\n.dark-mode .sidebar-dark-yellow .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-yellow .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f39c12;\n}\n\n.dark-mode .sidebar-dark-green .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-green .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #00bc8c;\n}\n\n.dark-mode .sidebar-dark-teal .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-teal .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #20c997;\n}\n\n.dark-mode .sidebar-dark-cyan .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-cyan .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3498db;\n}\n\n.dark-mode .sidebar-dark-white .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-white .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fff;\n}\n\n.dark-mode .sidebar-dark-gray .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.dark-mode .sidebar-dark-gray-dark .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.dark-mode [class*=\"sidebar-light-\"] .sidebar a {\n  color: #343a40;\n}\n\n.dark-mode [class*=\"sidebar-light-\"] .sidebar a:hover {\n  text-decoration: none;\n}\n\n.logo-xs,\n.logo-xl {\n  opacity: 1;\n  position: absolute;\n  visibility: visible;\n}\n\n.logo-xs.brand-image-xs,\n.logo-xl.brand-image-xs {\n  left: 18px;\n  top: 12px;\n}\n\n.logo-xs.brand-image-xl,\n.logo-xl.brand-image-xl {\n  left: 12px;\n  top: 6px;\n}\n\n.logo-xs {\n  opacity: 0;\n  visibility: hidden;\n}\n\n.logo-xs.brand-image-xl {\n  left: 16px;\n  top: 8px;\n}\n\n.brand-link.logo-switch::before {\n  content: \"\\00a0\";\n}\n\n@media (min-width: 992px) {\n  .sidebar-mini .nav-sidebar,\n  .sidebar-mini .nav-sidebar > .nav-header,\n  .sidebar-mini .nav-sidebar .nav-link {\n    white-space: nowrap;\n  }\n  .sidebar-mini.sidebar-collapse .d-hidden-mini {\n    display: none;\n  }\n  .sidebar-mini.sidebar-collapse .content-wrapper,\n  .sidebar-mini.sidebar-collapse .main-footer,\n  .sidebar-mini.sidebar-collapse .main-header {\n    margin-left: 4.6rem !important;\n  }\n  .sidebar-mini.sidebar-collapse .nav-sidebar .nav-header {\n    display: none;\n  }\n  .sidebar-mini.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n    width: 0;\n    white-space: nowrap;\n  }\n  .sidebar-mini.sidebar-collapse .sidebar .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .brand-text {\n    margin-left: -10px;\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .logo-xl {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .logo-xs {\n    display: inline-block;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar {\n    overflow-x: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar, .sidebar-mini.sidebar-collapse .main-sidebar::before {\n    margin-left: 0;\n    width: 4.6rem;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar .user-panel .image {\n    float: none;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n    text-align: left;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n    float: left;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n    display: inline-block;\n    margin-left: 0;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n    margin-right: .5rem;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n    display: block !important;\n    -webkit-transform: translateZ(0);\n    transform: translateZ(0);\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n    display: inline-block !important;\n  }\n  .sidebar-mini.sidebar-collapse .visible-sidebar-mini {\n    display: block !important;\n  }\n  .sidebar-mini.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse.layout-fixed .brand-link {\n    width: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini.sidebar-collapse .main-sidebar {\n    box-shadow: none !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-md .nav-sidebar,\n  .sidebar-mini-md .nav-sidebar > .nav-header,\n  .sidebar-mini-md .nav-sidebar .nav-link {\n    white-space: nowrap;\n  }\n  .sidebar-mini-md.sidebar-collapse .d-hidden-mini {\n    display: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .content-wrapper,\n  .sidebar-mini-md.sidebar-collapse .main-footer,\n  .sidebar-mini-md.sidebar-collapse .main-header {\n    margin-left: 4.6rem !important;\n  }\n  .sidebar-mini-md.sidebar-collapse .nav-sidebar .nav-header {\n    display: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n    width: 0;\n    white-space: nowrap;\n  }\n  .sidebar-mini-md.sidebar-collapse .sidebar .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .brand-text {\n    margin-left: -10px;\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .logo-xl {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .logo-xs {\n    display: inline-block;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar {\n    overflow-x: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar, .sidebar-mini-md.sidebar-collapse .main-sidebar::before {\n    margin-left: 0;\n    width: 4.6rem;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar .user-panel .image {\n    float: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n    text-align: left;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n    float: left;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n    display: inline-block;\n    margin-left: 0;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n    margin-right: .5rem;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n    display: block !important;\n    -webkit-transform: translateZ(0);\n    transform: translateZ(0);\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n    display: inline-block !important;\n  }\n  .sidebar-mini-md.sidebar-collapse .visible-sidebar-mini {\n    display: block !important;\n  }\n  .sidebar-mini-md.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse.layout-fixed .brand-link {\n    width: 4.6rem;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .sidebar-mini-md.sidebar-collapse .main-sidebar {\n    box-shadow: none !important;\n  }\n}\n\n.sidebar-mini-xs .nav-sidebar,\n.sidebar-mini-xs .nav-sidebar > .nav-header,\n.sidebar-mini-xs .nav-sidebar .nav-link {\n  white-space: nowrap;\n}\n\n.sidebar-mini-xs.sidebar-collapse .d-hidden-mini {\n  display: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .content-wrapper,\n.sidebar-mini-xs.sidebar-collapse .main-footer,\n.sidebar-mini-xs.sidebar-collapse .main-header {\n  margin-left: 4.6rem !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse .nav-sidebar .nav-header {\n  display: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n  width: 0;\n  white-space: nowrap;\n}\n\n.sidebar-mini-xs.sidebar-collapse .sidebar .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .brand-text {\n  margin-left: -10px;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .logo-xl {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .logo-xs {\n  display: inline-block;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar {\n  overflow-x: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar, .sidebar-mini-xs.sidebar-collapse .main-sidebar::before {\n  margin-left: 0;\n  width: 4.6rem;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar .user-panel .image {\n  float: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n  text-align: left;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n  float: left;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n  display: inline-block;\n  margin-left: 0;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n  margin-right: .5rem;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n  display: block !important;\n  -webkit-transform: translateZ(0);\n  transform: translateZ(0);\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n  display: inline-block !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse .visible-sidebar-mini {\n  display: block !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse.layout-fixed .brand-link {\n  width: 4.6rem;\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 3rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 4rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 3rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 4rem);\n}\n\n.sidebar-mini .main-sidebar .nav-flat .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 2);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 3);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 4);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - .5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1.5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2.5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - .5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 2);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 3);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 4);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 5);\n}\n\n.sidebar-mini .main-sidebar .nav-link,\n.sidebar-mini-md .main-sidebar .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-link {\n  width: calc(250px - 0.5rem * 2);\n  transition: width ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar-mini .main-sidebar .nav-link,\n  .sidebar-mini-md .main-sidebar .nav-link,\n  .sidebar-mini-xs .main-sidebar .nav-link {\n    transition: none;\n  }\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar .sidebar-search-results {\n  display: none;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar .nav-link {\n  width: 3.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-legacy .nav-link {\n  width: 4.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview {\n  padding-left: 0 !important;\n  margin-left: 0 !important;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link {\n  width: calc(4.6rem - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 1rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 1rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 2rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 3rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 4rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 3);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 4);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-compact .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - .5rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 3);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 4);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 5);\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover {\n  width: 4.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .nav-header, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .nav-header, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .nav-header {\n  display: none;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .brand-link, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .brand-link, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .brand-link {\n  width: 4.6rem !important;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .user-panel .image, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .user-panel .image, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .user-panel .image {\n  float: none !important;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .logo-xs, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .logo-xs, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .logo-xs {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .logo-xl, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .logo-xl, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .logo-xl {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview {\n  padding-left: 0;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p {\n  margin-left: -10px;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n  display: inline-block;\n  width: 0;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon {\n  margin-right: 0;\n}\n\n.nav-sidebar {\n  position: relative;\n}\n\n.nav-sidebar:hover {\n  overflow: visible;\n}\n\n.sidebar-form,\n.nav-sidebar > .nav-header {\n  overflow: hidden;\n  text-overflow: clip;\n}\n\n.nav-sidebar .nav-item > .nav-link {\n  position: relative;\n}\n\n.nav-sidebar .nav-item > .nav-link > .float-right {\n  margin-top: -7px;\n  position: absolute;\n  right: 10px;\n  top: 50%;\n}\n\n.sidebar .nav-link p,\n.main-sidebar .brand-text,\n.main-sidebar .logo-xs,\n.main-sidebar .logo-xl,\n.sidebar .user-panel .info {\n  transition: margin-left 0.3s linear, opacity 0.3s ease, visibility 0.3s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar .nav-link p,\n  .main-sidebar .brand-text,\n  .main-sidebar .logo-xs,\n  .main-sidebar .logo-xl,\n  .sidebar .user-panel .info {\n    transition: none;\n  }\n}\n\nhtml.control-sidebar-animate {\n  overflow-x: hidden;\n}\n\n.control-sidebar {\n  bottom: calc(3.5rem + 1px);\n  position: absolute;\n  top: calc(3.5rem + 1px);\n  z-index: 1031;\n}\n\n.control-sidebar, .control-sidebar::before {\n  bottom: calc(3.5rem + 1px);\n  display: none;\n  right: -250px;\n  width: 250px;\n  transition: right 0.3s ease-in-out, display 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar, .control-sidebar::before {\n    transition: none;\n  }\n}\n\n.control-sidebar::before {\n  content: \"\";\n  display: block;\n  position: fixed;\n  top: 0;\n  z-index: -1;\n}\n\nbody.text-sm .control-sidebar {\n  bottom: calc(2.9365rem + 1px);\n  top: calc(2.93725rem + 1px);\n}\n\n.main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.main-footer.text-sm ~ .control-sidebar {\n  bottom: calc(2.9365rem + 1px);\n}\n\n.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-push-slide .main-footer {\n  transition: margin-right 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar-push-slide .content-wrapper,\n  .control-sidebar-push-slide .main-footer {\n    transition: none;\n  }\n}\n\n.control-sidebar-open .control-sidebar {\n  display: block !important;\n}\n\n.control-sidebar-open .control-sidebar, .control-sidebar-open .control-sidebar::before {\n  right: 0;\n}\n\n.control-sidebar-open.control-sidebar-push .content-wrapper,\n.control-sidebar-open.control-sidebar-push .main-footer, .control-sidebar-open.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-open.control-sidebar-push-slide .main-footer {\n  margin-right: 250px;\n}\n\n.control-sidebar-slide-open .control-sidebar {\n  display: block;\n}\n\n.control-sidebar-slide-open .control-sidebar, .control-sidebar-slide-open .control-sidebar::before {\n  right: 0;\n  transition: right 0.3s ease-in-out, display 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar-slide-open .control-sidebar, .control-sidebar-slide-open .control-sidebar::before {\n    transition: none;\n  }\n}\n\n.control-sidebar-slide-open.control-sidebar-push .content-wrapper,\n.control-sidebar-slide-open.control-sidebar-push .main-footer, .control-sidebar-slide-open.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-slide-open.control-sidebar-push-slide .main-footer {\n  margin-right: 250px;\n}\n\n.control-sidebar-dark {\n  background-color: #343a40;\n}\n\n.control-sidebar-dark,\n.control-sidebar-dark a,\n.control-sidebar-dark .nav-link {\n  color: #c2c7d0;\n}\n\n.control-sidebar-dark a:hover {\n  color: #fff;\n}\n\n.control-sidebar-dark h1,\n.control-sidebar-dark h2,\n.control-sidebar-dark h3,\n.control-sidebar-dark h4,\n.control-sidebar-dark h5,\n.control-sidebar-dark h6,\n.control-sidebar-dark label {\n  color: #fff;\n}\n\n.control-sidebar-dark .nav-tabs {\n  background-color: rgba(255, 255, 255, 0.1);\n  border-bottom: 0;\n  margin-bottom: 5px;\n}\n\n.control-sidebar-dark .nav-tabs .nav-item {\n  margin: 0;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link {\n  border-radius: 0;\n  padding: 10px 20px;\n  position: relative;\n  text-align: center;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link, .control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active {\n  border: 0;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active {\n  border-bottom-color: transparent;\n  border-left-color: transparent;\n  border-top-color: transparent;\n  color: #fff;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link.active {\n  background-color: #343a40;\n}\n\n.control-sidebar-dark .tab-pane {\n  padding: 10px 15px;\n}\n\n.control-sidebar-light {\n  color: #4b545c;\n  background-color: #fff;\n  border-left: 1px solid #dee2e6;\n}\n\n.text-sm .dropdown-menu {\n  font-size: 0.875rem !important;\n}\n\n.text-sm .dropdown-toggle::after {\n  vertical-align: .2rem;\n}\n\n.dropdown-item-title {\n  font-size: 1rem;\n  margin: 0;\n}\n\n.dropdown-icon::after {\n  margin-left: 0;\n}\n\n.dropdown-menu-lg {\n  max-width: 300px;\n  min-width: 280px;\n  padding: 0;\n}\n\n.dropdown-menu-lg .dropdown-divider {\n  margin: 0;\n}\n\n.dropdown-menu-lg .dropdown-item {\n  padding: 0.5rem 1rem;\n}\n\n.dropdown-menu-lg p {\n  margin: 0;\n  white-space: normal;\n}\n\n.dropdown-submenu {\n  position: relative;\n}\n\n.dropdown-submenu > a::after {\n  border-top: 0.3em solid transparent;\n  border-right: 0;\n  border-bottom: 0.3em solid transparent;\n  border-left: 0.3em solid;\n  float: right;\n  margin-left: .5rem;\n  margin-top: .5rem;\n}\n\n.dropdown-submenu > .dropdown-menu {\n  left: 100%;\n  margin-left: 0;\n  margin-top: 0;\n  top: 0;\n}\n\n.dropdown-hover:hover > .dropdown-menu, .dropdown-hover.nav-item.dropdown:hover > .dropdown-menu,\n.dropdown-hover .dropdown-submenu:hover > .dropdown-menu, .dropdown-hover.dropdown-submenu:hover > .dropdown-menu {\n  display: block;\n}\n\n.dropdown-menu-xl {\n  max-width: 420px;\n  min-width: 360px;\n  padding: 0;\n}\n\n.dropdown-menu-xl .dropdown-divider {\n  margin: 0;\n}\n\n.dropdown-menu-xl .dropdown-item {\n  padding: 0.5rem 1rem;\n}\n\n.dropdown-menu-xl p {\n  margin: 0;\n  white-space: normal;\n}\n\n.dropdown-footer,\n.dropdown-header {\n  display: block;\n  font-size: 0.875rem;\n  padding: 0.5rem 1rem;\n  text-align: center;\n}\n\n.open:not(.dropup) > .animated-dropdown-menu {\n  -webkit-animation: flipInX 0.7s both;\n  animation: flipInX 0.7s both;\n  -webkit-backface-visibility: visible !important;\n  backface-visibility: visible !important;\n}\n\n.navbar-custom-menu > .navbar-nav > li {\n  position: relative;\n}\n\n.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {\n  position: absolute;\n  right: 0;\n  left: auto;\n}\n\n@media (max-width: 767.98px) {\n  .navbar-custom-menu > .navbar-nav {\n    float: right;\n  }\n  .navbar-custom-menu > .navbar-nav > li {\n    position: static;\n  }\n  .navbar-custom-menu > .navbar-nav > li > .dropdown-menu {\n    position: absolute;\n    right: 5%;\n    left: auto;\n    border: 1px solid #ddd;\n    background-color: #fff;\n  }\n}\n\n.navbar-nav > .user-menu > .nav-link::after {\n  content: none;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  padding: 0;\n  width: 280px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu,\n.navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header {\n  height: 175px;\n  padding: 10px;\n  text-align: center;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > img {\n  z-index: 5;\n  height: 90px;\n  width: 90px;\n  border: 3px solid;\n  border-color: transparent;\n  border-color: rgba(255, 255, 255, 0.2);\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p {\n  z-index: 5;\n  font-size: 17px;\n  margin-top: 10px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p > small {\n  display: block;\n  font-size: 12px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-bottom: 1px solid #495057;\n  border-top: 1px solid #dee2e6;\n  padding: 15px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-body::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu > .dropdown-menu > .user-body a {\n    background-color: #fff !important;\n    color: #495057 !important;\n  }\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer {\n  background-color: #f8f9fa;\n  padding: 10px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default {\n  color: #6c757d;\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:hover {\n    background-color: #f8f9fa;\n  }\n}\n\n.navbar-nav > .user-menu .user-image {\n  border-radius: 50%;\n  float: left;\n  height: 2.1rem;\n  margin-right: 10px;\n  margin-top: -2px;\n  width: 2.1rem;\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu .user-image {\n    float: none;\n    line-height: 10px;\n    margin-right: .4rem;\n    margin-top: -8px;\n  }\n}\n\n.dark-mode .dropdown-menu {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .dropdown-item {\n  color: #fff;\n}\n\n.dark-mode .dropdown-item:focus, .dark-mode .dropdown-item:hover {\n  background-color: #3f474e;\n}\n\n.dark-mode .dropdown-divider {\n  border-color: #6c757d;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer {\n  background-color: #3a4047;\n  color: #fff;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default {\n  color: #fff;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:hover, .dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:focus {\n  background-color: #3f474e;\n  color: #dee2e6;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:focus {\n  background-color: #454d55;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-color: #6c757d;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body a {\n  background-color: transparent !important;\n  color: #fff !important;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body a:hover, .dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body a:focus {\n  color: #ced4da !important;\n}\n\n.nav-pills .nav-link {\n  color: #6c757d;\n}\n\n.nav-pills .nav-link:not(.active):hover {\n  color: #007bff;\n}\n\n.nav-pills .nav-item.dropdown.show .nav-link:hover {\n  color: #fff;\n}\n\n.nav-tabs.flex-column {\n  border-bottom: 0;\n  border-right: 1px solid #dee2e6;\n}\n\n.nav-tabs.flex-column .nav-link {\n  border-bottom-left-radius: 0.25rem;\n  border-top-right-radius: 0;\n  margin-right: -1px;\n}\n\n.nav-tabs.flex-column .nav-link:hover, .nav-tabs.flex-column .nav-link:focus {\n  border-color: #e9ecef transparent #e9ecef #e9ecef;\n}\n\n.nav-tabs.flex-column .nav-link.active,\n.nav-tabs.flex-column .nav-item.show .nav-link {\n  border-color: #dee2e6 transparent #dee2e6 #dee2e6;\n}\n\n.nav-tabs.flex-column.nav-tabs-right {\n  border-left: 1px solid #dee2e6;\n  border-right: 0;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link {\n  border-bottom-left-radius: 0;\n  border-bottom-right-radius: 0.25rem;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0.25rem;\n  margin-left: -1px;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link:hover, .nav-tabs.flex-column.nav-tabs-right .nav-link:focus {\n  border-color: #e9ecef #e9ecef #e9ecef transparent;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link.active,\n.nav-tabs.flex-column.nav-tabs-right .nav-item.show .nav-link {\n  border-color: #dee2e6 #dee2e6 #dee2e6 transparent;\n}\n\n.navbar-no-expand {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-no-expand .nav-link {\n  padding-left: 1rem;\n  padding-right: 1rem;\n}\n\n.navbar-no-expand .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-light {\n  background-color: #f8f9fa;\n}\n\n.navbar-dark {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.navbar-primary {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar,\n.navbar-primary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #0071eb;\n  border-color: #0065d1;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus,\n.navbar-primary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #006fe6;\n  border-color: #0065d1 !important;\n  color: #343a40;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar,\n.navbar-primary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1486ff;\n  border-color: #2e93ff;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus,\n.navbar-primary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1a88ff;\n  border-color: #2e93ff !important;\n  color: #fff;\n}\n\n.navbar-secondary {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar,\n.navbar-secondary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus,\n.navbar-secondary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar,\n.navbar-secondary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus,\n.navbar-secondary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.navbar-success {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.navbar-success.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar,\n.navbar-success.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #24973e;\n  border-color: #1f8236;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus,\n.navbar-success.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #23923d;\n  border-color: #1f8236 !important;\n  color: #343a40;\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar,\n.navbar-success.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2cb74c;\n  border-color: #31cc54;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus,\n.navbar-success.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #2dbc4e;\n  border-color: #31cc54 !important;\n  color: #fff;\n}\n\n.navbar-info {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.navbar-info.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar,\n.navbar-info.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1592a6;\n  border-color: #127e8f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus,\n.navbar-info.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #148ea1;\n  border-color: #127e8f !important;\n  color: #343a40;\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar,\n.navbar-info.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #19b2ca;\n  border-color: #1cc6e1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus,\n.navbar-info.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1ab6cf;\n  border-color: #1cc6e1 !important;\n  color: #fff;\n}\n\n.navbar-warning {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar,\n.navbar-warning.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f2b500;\n  border-color: #d8a200;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus,\n.navbar-warning.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #edb100;\n  border-color: #d8a200 !important;\n  color: #343a40;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar,\n.navbar-warning.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ffc61b;\n  border-color: #ffcc35;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus,\n.navbar-warning.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ffc721;\n  border-color: #ffcc35 !important;\n  color: #fff;\n}\n\n.navbar-danger {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar,\n.navbar-danger.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #d72536;\n  border-color: #c22231;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus,\n.navbar-danger.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d32535;\n  border-color: #c22231 !important;\n  color: #343a40;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar,\n.navbar-danger.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df4655;\n  border-color: #e35c69;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus,\n.navbar-danger.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e04b59;\n  border-color: #e35c69 !important;\n  color: #fff;\n}\n\n.navbar-lightblue {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar,\n.navbar-lightblue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3781ad;\n  border-color: #317399;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus,\n.navbar-lightblue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #367fa9;\n  border-color: #317399 !important;\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar,\n.navbar-lightblue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #4897c5;\n  border-color: #5ba2cb;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus,\n.navbar-lightblue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4c99c6;\n  border-color: #5ba2cb !important;\n  color: #fff;\n}\n\n.navbar-navy {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar,\n.navbar-navy.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00152b;\n  border-color: #000811;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus,\n.navbar-navy.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #001226;\n  border-color: #000811 !important;\n  color: #343a40;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar,\n.navbar-navy.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #002953;\n  border-color: #00366d;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus,\n.navbar-navy.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #002c59;\n  border-color: #00366d !important;\n  color: #fff;\n}\n\n.navbar-olive {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar,\n.navbar-olive.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #378a65;\n  border-color: #307858;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus,\n.navbar-olive.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #368763;\n  border-color: #307858 !important;\n  color: #343a40;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar,\n.navbar-olive.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #43a87b;\n  border-color: #4cb888;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus,\n.navbar-olive.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #44ab7d;\n  border-color: #4cb888 !important;\n  color: #fff;\n}\n\n.navbar-lime {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar,\n.navbar-lime.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00ec67;\n  border-color: #00d25c;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus,\n.navbar-lime.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00e765;\n  border-color: #00d25c !important;\n  color: #343a40;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar,\n.navbar-lime.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #15ff7b;\n  border-color: #2fff8a;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus,\n.navbar-lime.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1bff7e;\n  border-color: #2fff8a !important;\n  color: #fff;\n}\n\n.navbar-fuchsia {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar,\n.navbar-fuchsia.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df0eb0;\n  border-color: #c70d9d;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus,\n.navbar-fuchsia.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #db0ead;\n  border-color: #c70d9d !important;\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar,\n.navbar-fuchsia.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f125c3;\n  border-color: #f33dca;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus,\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f22ac5;\n  border-color: #f33dca !important;\n  color: #fff;\n}\n\n.navbar-maroon {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar,\n.navbar-maroon.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #c61958;\n  border-color: #af164e;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus,\n.navbar-maroon.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #c11856;\n  border-color: #af164e !important;\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar,\n.navbar-maroon.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e4246a;\n  border-color: #e63a79;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus,\n.navbar-maroon.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e4286d;\n  border-color: #e63a79 !important;\n  color: #fff;\n}\n\n.navbar-blue {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar,\n.navbar-blue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #0071eb;\n  border-color: #0065d1;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus,\n.navbar-blue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #006fe6;\n  border-color: #0065d1 !important;\n  color: #343a40;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar,\n.navbar-blue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1486ff;\n  border-color: #2e93ff;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus,\n.navbar-blue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1a88ff;\n  border-color: #2e93ff !important;\n  color: #fff;\n}\n\n.navbar-indigo {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar,\n.navbar-indigo.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #5d0ce1;\n  border-color: #530bc9;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus,\n.navbar-indigo.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #5b0cdd;\n  border-color: #530bc9 !important;\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar,\n.navbar-indigo.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7223f3;\n  border-color: #823cf4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus,\n.navbar-indigo.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7528f3;\n  border-color: #823cf4 !important;\n  color: #fff;\n}\n\n.navbar-purple {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar,\n.navbar-purple.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #663bb4;\n  border-color: #5b35a0;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus,\n.navbar-purple.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #643ab0;\n  border-color: #5b35a0 !important;\n  color: #343a40;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar,\n.navbar-purple.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7b51c6;\n  border-color: #8965cc;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus,\n.navbar-purple.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7e55c7;\n  border-color: #8965cc !important;\n  color: #fff;\n}\n\n.navbar-pink {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar,\n.navbar-pink.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e62c81;\n  border-color: #de1a74;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus,\n.navbar-pink.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e5277e;\n  border-color: #de1a74 !important;\n  color: #343a40;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar,\n.navbar-pink.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ea5097;\n  border-color: #ed67a4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus,\n.navbar-pink.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #eb559a;\n  border-color: #ed67a4 !important;\n  color: #fff;\n}\n\n.navbar-red {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.navbar-red.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar,\n.navbar-red.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #d72536;\n  border-color: #c22231;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus,\n.navbar-red.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d32535;\n  border-color: #c22231 !important;\n  color: #343a40;\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar,\n.navbar-red.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df4655;\n  border-color: #e35c69;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus,\n.navbar-red.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e04b59;\n  border-color: #e35c69 !important;\n  color: #fff;\n}\n\n.navbar-orange {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar,\n.navbar-orange.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fa7302;\n  border-color: #e16702;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus,\n.navbar-orange.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f57102;\n  border-color: #e16702 !important;\n  color: #343a40;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar,\n.navbar-orange.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fd8928;\n  border-color: #fd9742;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus,\n.navbar-orange.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #fd8c2d;\n  border-color: #fd9742 !important;\n  color: #fff;\n}\n\n.navbar-yellow {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar,\n.navbar-yellow.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f2b500;\n  border-color: #d8a200;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus,\n.navbar-yellow.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #edb100;\n  border-color: #d8a200 !important;\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar,\n.navbar-yellow.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ffc61b;\n  border-color: #ffcc35;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus,\n.navbar-yellow.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ffc721;\n  border-color: #ffcc35 !important;\n  color: #fff;\n}\n\n.navbar-green {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.navbar-green.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar,\n.navbar-green.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #24973e;\n  border-color: #1f8236;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus,\n.navbar-green.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #23923d;\n  border-color: #1f8236 !important;\n  color: #343a40;\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar,\n.navbar-green.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2cb74c;\n  border-color: #31cc54;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus,\n.navbar-green.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #2dbc4e;\n  border-color: #31cc54 !important;\n  color: #fff;\n}\n\n.navbar-teal {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar,\n.navbar-teal.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1db78a;\n  border-color: #1aa179;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus,\n.navbar-teal.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1cb386;\n  border-color: #1aa179 !important;\n  color: #343a40;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar,\n.navbar-teal.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #23dba4;\n  border-color: #38dfae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus,\n.navbar-teal.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #26dca6;\n  border-color: #38dfae !important;\n  color: #fff;\n}\n\n.navbar-cyan {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar,\n.navbar-cyan.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1592a6;\n  border-color: #127e8f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus,\n.navbar-cyan.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #148ea1;\n  border-color: #127e8f !important;\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar,\n.navbar-cyan.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #19b2ca;\n  border-color: #1cc6e1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus,\n.navbar-cyan.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1ab6cf;\n  border-color: #1cc6e1 !important;\n  color: #fff;\n}\n\n.navbar-white {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.navbar-white.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar,\n.navbar-white.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: whitesmoke;\n  border-color: #e8e8e8;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus,\n.navbar-white.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f2f2f2;\n  border-color: #e8e8e8 !important;\n  color: #343a40;\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar,\n.navbar-white.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: white;\n  border-color: white;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus,\n.navbar-white.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: white;\n  border-color: white !important;\n  color: #fff;\n}\n\n.navbar-gray {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar,\n.navbar-gray.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus,\n.navbar-gray.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar,\n.navbar-gray.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus,\n.navbar-gray.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.navbar-gray-dark {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar,\n.navbar-gray-dark.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2b3035;\n  border-color: #1f2327;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus,\n.navbar-gray-dark.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #292d32;\n  border-color: #1f2327 !important;\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar,\n.navbar-gray-dark.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3d444b;\n  border-color: #495159;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus,\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #3f474e;\n  border-color: #495159 !important;\n  color: #fff;\n}\n\n.navbar-nav-not-expanded {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-nav-not-expanded .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-nav-not-expanded .nav-link {\n  padding-right: 1rem;\n  padding-left: 1rem;\n}\n\n.dark-mode .nav-pills .nav-link {\n  color: #ced4da;\n}\n\n.dark-mode .nav-tabs {\n  border-color: #56606a;\n}\n\n.dark-mode .nav-tabs .nav-link:focus,\n.dark-mode .nav-tabs .nav-link:hover {\n  border-color: #56606a;\n}\n\n.dark-mode .nav-tabs .nav-item.show .nav-link,\n.dark-mode .nav-tabs .nav-link.active {\n  background-color: #343a40;\n  border-color: #56606a #56606a transparent #56606a;\n  color: #fff;\n}\n\n.dark-mode .nav-tabs.flex-column .nav-item.show .nav-link.active, .dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:focus, .dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:hover,\n.dark-mode .nav-tabs.flex-column .nav-link.active,\n.dark-mode .nav-tabs.flex-column .nav-link:focus,\n.dark-mode .nav-tabs.flex-column .nav-link:hover {\n  border-color: #56606a transparent #56606a #56606a;\n}\n\n.dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:focus, .dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:hover,\n.dark-mode .nav-tabs.flex-column .nav-link:focus,\n.dark-mode .nav-tabs.flex-column .nav-link:hover {\n  background-color: #3f474e;\n}\n\n.dark-mode .nav-tabs.flex-column.nav-tabs-right {\n  border-color: #56606a;\n}\n\n.dark-mode .nav-tabs.flex-column.nav-tabs-right .nav-link.active, .dark-mode .nav-tabs.flex-column.nav-tabs-right .nav-link:focus, .dark-mode .nav-tabs.flex-column.nav-tabs-right .nav-link:hover {\n  border-color: #56606a #56606a #56606a transparent;\n}\n\n.dark-mode .navbar-light {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .navbar-dark {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.dark-mode .navbar-primary {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar,\n.dark-mode .navbar-primary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #395d83;\n  border-color: #315071;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #375a7f;\n  border-color: #315071 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar,\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45719f;\n  border-color: #4d7eb1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4774a3;\n  border-color: #4d7eb1 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar,\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar,\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-success {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar,\n.dark-mode .navbar-success.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00a87d;\n  border-color: #008e6a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00a379;\n  border-color: #008e6a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar,\n.dark-mode .navbar-success.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00d09b;\n  border-color: #00eaae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00d69f;\n  border-color: #00eaae !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-info {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar,\n.dark-mode .navbar-info.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #268fd5;\n  border-color: #2280bf;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #258cd1;\n  border-color: #2280bf !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar,\n.dark-mode .navbar-info.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45a1de;\n  border-color: #5bace2;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4aa3df;\n  border-color: #5bace2 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-warning {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar,\n.dark-mode .navbar-warning.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e5910c;\n  border-color: #cd820a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e08e0b;\n  border-color: #cd820a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar,\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f4a425;\n  border-color: #f5ae3e;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f4a62a;\n  border-color: #f5ae3e !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-danger {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar,\n.dark-mode .navbar-danger.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e53b2a;\n  border-color: #da2d1b;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e43725;\n  border-color: #da2d1b !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar,\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e95d4e;\n  border-color: #ec7265;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ea6153;\n  border-color: #ec7265 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar,\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #76b1d3;\n  border-color: #63a6cd;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #72afd2;\n  border-color: #63a6cd !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar,\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #95c3dd;\n  border-color: #a9cee3;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #99c5de;\n  border-color: #a9cee3 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-navy {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar,\n.dark-mode .navbar-navy.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #002244;\n  border-color: #00152b;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #001f3f;\n  border-color: #00152b !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar,\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00366d;\n  border-color: #004286;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #003872;\n  border-color: #004286 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-olive {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar,\n.dark-mode .navbar-olive.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #66c299;\n  border-color: #53bb8d;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #62c096;\n  border-color: #53bb8d !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar,\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #83ceac;\n  border-color: #95d5b8;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #87cfaf;\n  border-color: #95d5b8 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-lime {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar,\n.dark-mode .navbar-lime.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #53ff9e;\n  border-color: #39ff90;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4eff9b;\n  border-color: #39ff90 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar,\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7bffb5;\n  border-color: #95ffc3;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #81ffb8;\n  border-color: #95ffc3 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar,\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f55fd3;\n  border-color: #f347cc;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f55ad2;\n  border-color: #f347cc !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar,\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f785de;\n  border-color: #f99de4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f88adf;\n  border-color: #f99de4 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar,\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ea5a8f;\n  border-color: #e8447f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ea568c;\n  border-color: #e8447f !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar,\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ef7ea8;\n  border-color: #f295b7;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f083ab;\n  border-color: #f295b7 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-blue {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar,\n.dark-mode .navbar-blue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #395d83;\n  border-color: #315071;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #375a7f;\n  border-color: #315071 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar,\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45719f;\n  border-color: #4d7eb1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4774a3;\n  border-color: #4d7eb1 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar,\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #5d0ce1;\n  border-color: #530bc9;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #5b0cdd;\n  border-color: #530bc9 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar,\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7223f3;\n  border-color: #823cf4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7528f3;\n  border-color: #823cf4 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-purple {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar,\n.dark-mode .navbar-purple.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #663bb4;\n  border-color: #5b35a0;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #643ab0;\n  border-color: #5b35a0 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar,\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7b51c6;\n  border-color: #8965cc;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7e55c7;\n  border-color: #8965cc !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-pink {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar,\n.dark-mode .navbar-pink.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e62c81;\n  border-color: #de1a74;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e5277e;\n  border-color: #de1a74 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar,\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ea5097;\n  border-color: #ed67a4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #eb559a;\n  border-color: #ed67a4 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-red {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar,\n.dark-mode .navbar-red.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e53b2a;\n  border-color: #da2d1b;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e43725;\n  border-color: #da2d1b !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar,\n.dark-mode .navbar-red.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e95d4e;\n  border-color: #ec7265;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ea6153;\n  border-color: #ec7265 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-orange {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar,\n.dark-mode .navbar-orange.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fa7302;\n  border-color: #e16702;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f57102;\n  border-color: #e16702 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar,\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fd8928;\n  border-color: #fd9742;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #fd8c2d;\n  border-color: #fd9742 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar,\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e5910c;\n  border-color: #cd820a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e08e0b;\n  border-color: #cd820a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar,\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f4a425;\n  border-color: #f5ae3e;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f4a62a;\n  border-color: #f5ae3e !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-green {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar,\n.dark-mode .navbar-green.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00a87d;\n  border-color: #008e6a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00a379;\n  border-color: #008e6a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar,\n.dark-mode .navbar-green.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00d09b;\n  border-color: #00eaae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00d69f;\n  border-color: #00eaae !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-teal {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar,\n.dark-mode .navbar-teal.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1db78a;\n  border-color: #1aa179;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1cb386;\n  border-color: #1aa179 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar,\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #23dba4;\n  border-color: #38dfae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #26dca6;\n  border-color: #38dfae !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar,\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #268fd5;\n  border-color: #2280bf;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #258cd1;\n  border-color: #2280bf !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar,\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45a1de;\n  border-color: #5bace2;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4aa3df;\n  border-color: #5bace2 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-white {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar,\n.dark-mode .navbar-white.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: whitesmoke;\n  border-color: #e8e8e8;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f2f2f2;\n  border-color: #e8e8e8 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar,\n.dark-mode .navbar-white.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: white;\n  border-color: white;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: white;\n  border-color: white !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar,\n.dark-mode .navbar-gray.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar,\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar,\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2b3035;\n  border-color: #1f2327;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #292d32;\n  border-color: #1f2327 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar,\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3d444b;\n  border-color: #495159;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #3f474e;\n  border-color: #495159 !important;\n  color: #fff;\n}\n\n.pagination-month .page-item {\n  justify-self: stretch;\n}\n\n.pagination-month .page-item .page-link {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  box-shadow: none;\n}\n\n.pagination-month .page-item:first-child .page-link, .pagination-month .page-item:last-child .page-link {\n  height: 100%;\n  font-size: 1.25rem;\n}\n\n.pagination-month .page-item .page-month {\n  margin-bottom: 0;\n  font-size: 1.25rem;\n  font-weight: 700;\n}\n\n.pagination-month .page-item .page-year {\n  margin-bottom: 0;\n}\n\n.pagination-month.pagination-lg .page-month {\n  font-size: 1.5625rem;\n}\n\n.pagination-month.pagination-sm .page-month {\n  font-size: 1rem;\n}\n\n.dark-mode .page-item.disabled a,\n.dark-mode .page-item.disabled .page-link {\n  background-color: #3a4047 !important;\n  border-color: #6c757d !important;\n  color: #6c757d;\n}\n\n.dark-mode .page-item .page-link {\n  color: #3f6791;\n}\n\n.dark-mode .page-item.active .page-link {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .page-item.active .page-link:hover, .dark-mode .page-item.active .page-link:focus {\n  color: #ced4da !important;\n}\n\n.dark-mode .page-item:not(.active) .page-link {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.dark-mode .page-item:not(.active) .page-link:hover, .dark-mode .page-item:not(.active) .page-link:focus {\n  color: #4774a3;\n  background-color: #3f474e;\n}\n\n.form-group.has-icon {\n  position: relative;\n}\n\n.form-group.has-icon .form-control {\n  padding-right: 35px;\n}\n\n.form-group.has-icon .form-icon {\n  background-color: transparent;\n  border: 0;\n  cursor: pointer;\n  font-size: 1rem;\n  padding: 0.375rem 0.75rem;\n  position: absolute;\n  right: 3px;\n  top: 0;\n}\n\n.btn-group-vertical .btn.btn-flat:first-of-type, .btn-group-vertical .btn.btn-flat:last-of-type {\n  border-radius: 0;\n}\n\n.form-control-feedback.fa, .form-control-feedback.fas, .form-control-feedback.far, .form-control-feedback.fab, .form-control-feedback.fal, .form-control-feedback.fad, .form-control-feedback.svg-inline--fa, .form-control-feedback.ion {\n  line-height: calc(2.25rem + 2px);\n}\n\n.input-lg + .form-control-feedback.fa, .input-lg + .form-control-feedback.fas, .input-lg + .form-control-feedback.far, .input-lg + .form-control-feedback.fab, .input-lg + .form-control-feedback.fal, .input-lg + .form-control-feedback.fad, .input-lg + .form-control-feedback.svg-inline--fa, .input-lg + .form-control-feedback.ion,\n.input-group-lg + .form-control-feedback.fa,\n.input-group-lg + .form-control-feedback.fas,\n.input-group-lg + .form-control-feedback.far,\n.input-group-lg + .form-control-feedback.fab,\n.input-group-lg + .form-control-feedback.fal,\n.input-group-lg + .form-control-feedback.fad,\n.input-group-lg + .form-control-feedback.svg-inline--fa,\n.input-group-lg + .form-control-feedback.ion {\n  line-height: calc(2.875rem + 2px);\n}\n\n.form-group-lg .form-control + .form-control-feedback.fa, .form-group-lg .form-control + .form-control-feedback.fas, .form-group-lg .form-control + .form-control-feedback.far, .form-group-lg .form-control + .form-control-feedback.fab, .form-group-lg .form-control + .form-control-feedback.fal, .form-group-lg .form-control + .form-control-feedback.fad, .form-group-lg .form-control + .form-control-feedback.svg-inline--fa, .form-group-lg .form-control + .form-control-feedback.ion {\n  line-height: calc(2.875rem + 2px);\n}\n\n.input-sm + .form-control-feedback.fa, .input-sm + .form-control-feedback.fas, .input-sm + .form-control-feedback.far, .input-sm + .form-control-feedback.fab, .input-sm + .form-control-feedback.fal, .input-sm + .form-control-feedback.fad, .input-sm + .form-control-feedback.svg-inline--fa, .input-sm + .form-control-feedback.ion,\n.input-group-sm + .form-control-feedback.fa,\n.input-group-sm + .form-control-feedback.fas,\n.input-group-sm + .form-control-feedback.far,\n.input-group-sm + .form-control-feedback.fab,\n.input-group-sm + .form-control-feedback.fal,\n.input-group-sm + .form-control-feedback.fad,\n.input-group-sm + .form-control-feedback.svg-inline--fa,\n.input-group-sm + .form-control-feedback.ion {\n  line-height: calc(1.8125rem + 2px);\n}\n\n.form-group-sm .form-control + .form-control-feedback.fa, .form-group-sm .form-control + .form-control-feedback.fas, .form-group-sm .form-control + .form-control-feedback.far, .form-group-sm .form-control + .form-control-feedback.fab, .form-group-sm .form-control + .form-control-feedback.fal, .form-group-sm .form-control + .form-control-feedback.fad, .form-group-sm .form-control + .form-control-feedback.svg-inline--fa, .form-group-sm .form-control + .form-control-feedback.ion {\n  line-height: calc(1.8125rem + 2px);\n}\n\nlabel:not(.form-check-label):not(.custom-file-label) {\n  font-weight: 700;\n}\n\n.warning-feedback {\n  font-size: 80%;\n  color: #ffc107;\n  display: none;\n  margin-top: 0.25rem;\n  width: 100%;\n}\n\n.warning-tooltip {\n  border-radius: 0.25rem;\n  font-size: 0.875rem;\n  background-color: rgba(255, 193, 7, 0.9);\n  color: #1f2d3d;\n  display: none;\n  line-height: 1.5;\n  margin-top: .1rem;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  position: absolute;\n  top: 100%;\n  z-index: 5;\n}\n\n.form-control.is-warning {\n  border-color: #ffc107;\n}\n\n.form-control.is-warning:focus {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.form-control.is-warning ~ .warning-feedback,\n.form-control.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\ntextarea.form-control.is-warning {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.custom-select.is-warning {\n  border-color: #ffc107;\n}\n\n.custom-select.is-warning:focus {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.custom-select.is-warning ~ .warning-feedback,\n.custom-select.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.form-control-file.is-warning ~ .warning-feedback,\n.form-control-file.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.form-check-input.is-warning ~ .form-check-label {\n  color: #ffc107;\n}\n\n.form-check-input.is-warning ~ .warning-feedback,\n.form-check-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-control-input.is-warning ~ .custom-control-label {\n  color: #ffc107;\n}\n\n.custom-control-input.is-warning ~ .custom-control-label::before {\n  border-color: #ffc107;\n}\n\n.custom-control-input.is-warning ~ .warning-feedback,\n.custom-control-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-control-input.is-warning:checked ~ .custom-control-label::before {\n  background-color: #ffce3a;\n  border-color: #ffce3a;\n}\n\n.custom-control-input.is-warning:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input.is-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffc107;\n}\n\n.custom-file-input.is-warning ~ .custom-file-label {\n  border-color: #ffc107;\n}\n\n.custom-file-input.is-warning ~ .warning-feedback,\n.custom-file-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-file-input.is-warning:focus ~ .custom-file-label {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\nbody.text-sm .input-group-text {\n  font-size: 0.875rem;\n}\n\n.form-control.form-control-border,\n.custom-select.form-control-border {\n  border-top: 0;\n  border-left: 0;\n  border-right: 0;\n  border-radius: 0;\n  box-shadow: inherit;\n}\n\n.form-control.form-control-border.border-width-2,\n.custom-select.form-control-border.border-width-2 {\n  border-bottom-width: 2px;\n}\n\n.form-control.form-control-border.border-width-3,\n.custom-select.form-control-border.border-width-3 {\n  border-bottom-width: 3px;\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::after {\n  background-color: #003e80;\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99caff;\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::after {\n  background-color: #0f401b;\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #86e29b;\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::after {\n  background-color: #093e47;\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7adeee;\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::after {\n  background-color: #876500;\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #ffe7a0;\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::after {\n  background-color: #7c151f;\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f3b7bd;\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::after {\n  background-color: #aeb9c5;\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::after {\n  background-color: #1d455b;\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #acd0e5;\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #006ad8;\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::after {\n  background-color: #193e2d;\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99d6bb;\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::after {\n  background-color: #008138;\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #9affc6;\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::after {\n  background-color: #7b0861;\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f9a2e5;\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::after {\n  background-color: #670d2e;\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f29aba;\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::after {\n  background-color: #003e80;\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99caff;\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::after {\n  background-color: #33077c;\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c3a1fa;\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::after {\n  background-color: #382063;\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c7b5e7;\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::after {\n  background-color: #95124e;\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c7dd;\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::after {\n  background-color: #7c151f;\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f3b7bd;\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::after {\n  background-color: #904201;\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fed1ac;\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::after {\n  background-color: #876500;\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #ffe7a0;\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::after {\n  background-color: #0f401b;\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #86e29b;\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::after {\n  background-color: #0e5b44;\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #94eed3;\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::after {\n  background-color: #093e47;\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7adeee;\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::after {\n  background-color: #bfbfbf;\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.custom-range.custom-range-primary:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-primary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary::-webkit-slider-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-primary::-moz-range-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-primary::-ms-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-secondary:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-secondary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-secondary::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-secondary::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-success:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-success:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success::-webkit-slider-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-webkit-slider-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-success::-moz-range-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-moz-range-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-success::-ms-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-ms-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-info:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-info:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info::-webkit-slider-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-webkit-slider-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-info::-moz-range-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-moz-range-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-info::-ms-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-ms-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-warning:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-warning:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning::-webkit-slider-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-webkit-slider-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-warning::-moz-range-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-moz-range-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-warning::-ms-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-ms-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-danger:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-danger:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger::-webkit-slider-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-webkit-slider-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-danger::-moz-range-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-moz-range-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-danger::-ms-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-ms-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-light:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-light:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light::-webkit-slider-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-light::-moz-range-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-light::-ms-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-ms-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-dark:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-lightblue:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-lightblue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue::-webkit-slider-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-webkit-slider-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-lightblue::-moz-range-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-moz-range-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-lightblue::-ms-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-ms-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-navy:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-navy:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy::-webkit-slider-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-webkit-slider-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-navy::-moz-range-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-moz-range-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-navy::-ms-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-ms-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-olive:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-olive:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive::-webkit-slider-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-webkit-slider-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-olive::-moz-range-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-moz-range-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-olive::-ms-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-ms-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-lime:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-lime:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime::-webkit-slider-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-webkit-slider-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-lime::-moz-range-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-moz-range-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-lime::-ms-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-ms-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-fuchsia:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-fuchsia:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia::-webkit-slider-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-webkit-slider-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-fuchsia::-moz-range-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-moz-range-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-fuchsia::-ms-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-ms-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-maroon:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-maroon:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon::-webkit-slider-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-webkit-slider-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-maroon::-moz-range-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-moz-range-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-maroon::-ms-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-ms-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-blue:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-blue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue::-webkit-slider-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-blue::-moz-range-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-blue::-ms-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-indigo:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-indigo:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo::-webkit-slider-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-webkit-slider-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-indigo::-moz-range-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-moz-range-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-indigo::-ms-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-ms-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-purple:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-purple:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple::-webkit-slider-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-webkit-slider-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-purple::-moz-range-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-moz-range-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-purple::-ms-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-ms-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-pink:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-pink:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink::-webkit-slider-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-webkit-slider-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-pink::-moz-range-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-moz-range-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-pink::-ms-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-ms-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-red:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-red:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red::-webkit-slider-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-webkit-slider-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-red::-moz-range-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-moz-range-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-red::-ms-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-ms-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-orange:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-orange:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange::-webkit-slider-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-webkit-slider-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-orange::-moz-range-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-moz-range-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-orange::-ms-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-ms-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-yellow:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-yellow:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow::-webkit-slider-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-webkit-slider-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-yellow::-moz-range-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-moz-range-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-yellow::-ms-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-ms-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-green:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-green:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green::-webkit-slider-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-webkit-slider-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-green::-moz-range-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-moz-range-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-green::-ms-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-ms-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-teal:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-teal:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal::-webkit-slider-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-webkit-slider-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-teal::-moz-range-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-moz-range-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-teal::-ms-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-ms-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-cyan:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-cyan:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan::-webkit-slider-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-webkit-slider-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-cyan::-moz-range-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-moz-range-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-cyan::-ms-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-ms-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-white:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-white:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white::-webkit-slider-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-white::-moz-range-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-white::-ms-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-ms-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-gray:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-gray:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray-dark:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-gray-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-gray-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-gray-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-control-input-primary:checked ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n}\n\n.custom-control-input-primary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23007bff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-primary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23007bff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-primary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input-primary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input-primary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n}\n\n.custom-control-input-secondary:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.custom-control-input-secondary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-secondary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-secondary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.custom-control-input-secondary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.custom-control-input-secondary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.custom-control-input-success:checked ~ .custom-control-label::before {\n  border-color: #28a745;\n  background-color: #28a745;\n}\n\n.custom-control-input-success.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-success.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2328a745'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-success:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.custom-control-input-success:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #71dd8a;\n}\n\n.custom-control-input-success:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #9be7ac;\n  border-color: #9be7ac;\n}\n\n.custom-control-input-info:checked ~ .custom-control-label::before {\n  border-color: #17a2b8;\n  background-color: #17a2b8;\n}\n\n.custom-control-input-info.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2317a2b8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-info.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2317a2b8'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-info:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(23, 162, 184, 0.25);\n}\n\n.custom-control-input-info:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #63d9ec;\n}\n\n.custom-control-input-info:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #90e4f1;\n  border-color: #90e4f1;\n}\n\n.custom-control-input-warning:checked ~ .custom-control-label::before {\n  border-color: #ffc107;\n  background-color: #ffc107;\n}\n\n.custom-control-input-warning.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffc107' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-warning.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffc107'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-warning:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffe187;\n}\n\n.custom-control-input-warning:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffeeba;\n  border-color: #ffeeba;\n}\n\n.custom-control-input-danger:checked ~ .custom-control-label::before {\n  border-color: #dc3545;\n  background-color: #dc3545;\n}\n\n.custom-control-input-danger.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23dc3545' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-danger.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23dc3545'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-danger:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.custom-control-input-danger:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #efa2a9;\n}\n\n.custom-control-input-danger:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f6cdd1;\n  border-color: #f6cdd1;\n}\n\n.custom-control-input-light:checked ~ .custom-control-label::before {\n  border-color: #f8f9fa;\n  background-color: #f8f9fa;\n}\n\n.custom-control-input-light.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f8f9fa' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-light.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f8f9fa'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-light:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(248, 249, 250, 0.25);\n}\n\n.custom-control-input-light:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.custom-control-input-light:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.custom-control-input-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.custom-control-input-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.custom-control-input-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.custom-control-input-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.custom-control-input-lightblue:checked ~ .custom-control-label::before {\n  border-color: #3c8dbc;\n  background-color: #3c8dbc;\n}\n\n.custom-control-input-lightblue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233c8dbc' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lightblue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233c8dbc'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lightblue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(60, 141, 188, 0.25);\n}\n\n.custom-control-input-lightblue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #99c5de;\n}\n\n.custom-control-input-lightblue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #c0dbeb;\n  border-color: #c0dbeb;\n}\n\n.custom-control-input-navy:checked ~ .custom-control-label::before {\n  border-color: #001f3f;\n  background-color: #001f3f;\n}\n\n.custom-control-input-navy.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23001f3f' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-navy.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23001f3f'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-navy:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 31, 63, 0.25);\n}\n\n.custom-control-input-navy:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #005ebf;\n}\n\n.custom-control-input-navy:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #0077f2;\n  border-color: #0077f2;\n}\n\n.custom-control-input-olive:checked ~ .custom-control-label::before {\n  border-color: #3d9970;\n  background-color: #3d9970;\n}\n\n.custom-control-input-olive.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233d9970' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-olive.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233d9970'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-olive:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(61, 153, 112, 0.25);\n}\n\n.custom-control-input-olive:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #87cfaf;\n}\n\n.custom-control-input-olive:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #abdec7;\n  border-color: #abdec7;\n}\n\n.custom-control-input-lime:checked ~ .custom-control-label::before {\n  border-color: #01ff70;\n  background-color: #01ff70;\n}\n\n.custom-control-input-lime.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2301ff70' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lime.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2301ff70'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lime:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(1, 255, 112, 0.25);\n}\n\n.custom-control-input-lime:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #81ffb8;\n}\n\n.custom-control-input-lime:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b4ffd4;\n  border-color: #b4ffd4;\n}\n\n.custom-control-input-fuchsia:checked ~ .custom-control-label::before {\n  border-color: #f012be;\n  background-color: #f012be;\n}\n\n.custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f012be' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f012be'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-fuchsia:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(240, 18, 190, 0.25);\n}\n\n.custom-control-input-fuchsia:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f88adf;\n}\n\n.custom-control-input-fuchsia:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbbaec;\n  border-color: #fbbaec;\n}\n\n.custom-control-input-maroon:checked ~ .custom-control-label::before {\n  border-color: #d81b60;\n  background-color: #d81b60;\n}\n\n.custom-control-input-maroon.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23d81b60' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-maroon.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23d81b60'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-maroon:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(216, 27, 96, 0.25);\n}\n\n.custom-control-input-maroon:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f083ab;\n}\n\n.custom-control-input-maroon:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f5b0c9;\n  border-color: #f5b0c9;\n}\n\n.custom-control-input-blue:checked ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n}\n\n.custom-control-input-blue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23007bff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-blue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23007bff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-blue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input-blue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input-blue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n}\n\n.custom-control-input-indigo:checked ~ .custom-control-label::before {\n  border-color: #6610f2;\n  background-color: #6610f2;\n}\n\n.custom-control-input-indigo.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236610f2' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-indigo.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236610f2'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-indigo:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(102, 16, 242, 0.25);\n}\n\n.custom-control-input-indigo:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b389f9;\n}\n\n.custom-control-input-indigo:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d2b9fb;\n  border-color: #d2b9fb;\n}\n\n.custom-control-input-purple:checked ~ .custom-control-label::before {\n  border-color: #6f42c1;\n  background-color: #6f42c1;\n}\n\n.custom-control-input-purple.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236f42c1' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-purple.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236f42c1'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-purple:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(111, 66, 193, 0.25);\n}\n\n.custom-control-input-purple:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b8a2e0;\n}\n\n.custom-control-input-purple:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d5c8ed;\n  border-color: #d5c8ed;\n}\n\n.custom-control-input-pink:checked ~ .custom-control-label::before {\n  border-color: #e83e8c;\n  background-color: #e83e8c;\n}\n\n.custom-control-input-pink.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e83e8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-pink.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e83e8c'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-pink:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(232, 62, 140, 0.25);\n}\n\n.custom-control-input-pink:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f6b0d0;\n}\n\n.custom-control-input-pink:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbddeb;\n  border-color: #fbddeb;\n}\n\n.custom-control-input-red:checked ~ .custom-control-label::before {\n  border-color: #dc3545;\n  background-color: #dc3545;\n}\n\n.custom-control-input-red.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23dc3545' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-red.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23dc3545'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-red:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.custom-control-input-red:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #efa2a9;\n}\n\n.custom-control-input-red:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f6cdd1;\n  border-color: #f6cdd1;\n}\n\n.custom-control-input-orange:checked ~ .custom-control-label::before {\n  border-color: #fd7e14;\n  background-color: #fd7e14;\n}\n\n.custom-control-input-orange.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fd7e14' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-orange.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fd7e14'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-orange:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(253, 126, 20, 0.25);\n}\n\n.custom-control-input-orange:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #fec392;\n}\n\n.custom-control-input-orange:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffdfc5;\n  border-color: #ffdfc5;\n}\n\n.custom-control-input-yellow:checked ~ .custom-control-label::before {\n  border-color: #ffc107;\n  background-color: #ffc107;\n}\n\n.custom-control-input-yellow.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffc107' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-yellow.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffc107'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-yellow:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input-yellow:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffe187;\n}\n\n.custom-control-input-yellow:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffeeba;\n  border-color: #ffeeba;\n}\n\n.custom-control-input-green:checked ~ .custom-control-label::before {\n  border-color: #28a745;\n  background-color: #28a745;\n}\n\n.custom-control-input-green.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-green.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2328a745'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-green:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.custom-control-input-green:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #71dd8a;\n}\n\n.custom-control-input-green:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #9be7ac;\n  border-color: #9be7ac;\n}\n\n.custom-control-input-teal:checked ~ .custom-control-label::before {\n  border-color: #20c997;\n  background-color: #20c997;\n}\n\n.custom-control-input-teal.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2320c997' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-teal.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2320c997'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-teal:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(32, 201, 151, 0.25);\n}\n\n.custom-control-input-teal:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #7eeaca;\n}\n\n.custom-control-input-teal:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #aaf1dc;\n  border-color: #aaf1dc;\n}\n\n.custom-control-input-cyan:checked ~ .custom-control-label::before {\n  border-color: #17a2b8;\n  background-color: #17a2b8;\n}\n\n.custom-control-input-cyan.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2317a2b8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-cyan.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2317a2b8'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-cyan:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(23, 162, 184, 0.25);\n}\n\n.custom-control-input-cyan:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #63d9ec;\n}\n\n.custom-control-input-cyan:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #90e4f1;\n  border-color: #90e4f1;\n}\n\n.custom-control-input-white:checked ~ .custom-control-label::before {\n  border-color: #fff;\n  background-color: #fff;\n}\n\n.custom-control-input-white.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-white.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-white:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 255, 255, 0.25);\n}\n\n.custom-control-input-white:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.custom-control-input-white:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.custom-control-input-gray:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.custom-control-input-gray.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.custom-control-input-gray:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.custom-control-input-gray:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.custom-control-input-gray-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.custom-control-input-gray-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.custom-control-input-gray-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.custom-control-input-outline ~ .custom-control-label::before {\n  background-color: transparent !important;\n  box-shadow: none;\n}\n\n.custom-control-input-outline:checked ~ .custom-control-label::before {\n  background-color: transparent;\n}\n\n.navbar-dark .btn-navbar,\n.navbar-dark .form-control-navbar {\n  background-color: #3f474e;\n  border: 1px solid #56606a;\n  color: white;\n}\n\n.navbar-dark .btn-navbar:hover {\n  background-color: #454d55;\n}\n\n.navbar-dark .btn-navbar:focus {\n  background-color: #4b545c;\n}\n\n.navbar-dark .form-control-navbar + .input-group-prepend > .btn-navbar,\n.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3f474e;\n  color: #fff;\n  border: 1px solid #56606a;\n  border-left: none;\n}\n\n.dark-mode .form-control:not(.form-control-navbar):not(.form-control-sidebar),\n.dark-mode .custom-select,\n.dark-mode .custom-file-label,\n.dark-mode .custom-file-label::after,\n.dark-mode .custom-control-label::before,\n.dark-mode .input-group-text {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .form-control:not(.form-control-navbar):not(.form-control-sidebar):not(.is-invalid):not(:focus),\n.dark-mode .custom-file-label,\n.dark-mode .custom-file-label::after {\n  border-color: #6c757d;\n}\n\n.dark-mode select {\n  background-color: #343a40;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .custom-select {\n  background: #343a40 url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23fff' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat;\n}\n\n.dark-mode .custom-select[multiple] {\n  background: #343a40;\n}\n\n.dark-mode .input-group-text {\n  border-color: #6c757d;\n}\n\n.dark-mode .custom-control-input:disabled ~ .custom-control-label::before,\n.dark-mode .custom-control-input[disabled] ~ .custom-control-label::before {\n  background-color: #3f474e;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode input:-webkit-autofill,\n.dark-mode input:-webkit-autofill:hover,\n.dark-mode input:-webkit-autofill:focus,\n.dark-mode textarea:-webkit-autofill,\n.dark-mode textarea:-webkit-autofill:hover,\n.dark-mode textarea:-webkit-autofill:focus,\n.dark-mode select:-webkit-autofill,\n.dark-mode select:-webkit-autofill:hover,\n.dark-mode select:-webkit-autofill:focus {\n  -webkit-text-fill-color: #fff;\n}\n\n.dark-mode .custom-range::-webkit-slider-runnable-track {\n  background-color: #454d55;\n}\n\n.dark-mode .custom-range::-moz-range-track {\n  background-color: #454d55;\n}\n\n.dark-mode .custom-range::-ms-track {\n  background-color: #454d55;\n}\n\n.dark-mode .custom-range.custom-range-primary:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-primary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-primary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-primary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-primary::-webkit-slider-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-primary::-webkit-slider-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-primary::-moz-range-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-primary::-moz-range-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-primary::-ms-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-primary::-ms-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-secondary::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-success:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-success:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-success:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-success:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-success::-webkit-slider-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-success::-webkit-slider-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-success::-moz-range-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-success::-moz-range-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-success::-ms-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-success::-ms-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-info:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-info:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-info:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-info:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-info::-webkit-slider-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-info::-webkit-slider-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-info::-moz-range-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-info::-moz-range-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-info::-ms-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-info::-ms-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-warning:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-warning:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-warning:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-warning:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-warning::-webkit-slider-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-warning::-webkit-slider-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-warning::-moz-range-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-warning::-moz-range-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-warning::-ms-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-warning::-ms-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-danger:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-danger:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-danger:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-danger:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-danger::-webkit-slider-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-danger::-webkit-slider-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-danger::-moz-range-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-danger::-moz-range-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-danger::-ms-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-danger::-ms-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-light:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-light:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-light:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-light:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-light::-webkit-slider-thumb {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-range.custom-range-light::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-light::-moz-range-thumb {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-range.custom-range-light::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-light::-ms-thumb {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-range.custom-range-light::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-dark:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-webkit-slider-thumb {\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-moz-range-thumb {\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-ms-thumb {\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-navy:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-navy:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-navy:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-navy:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-navy::-webkit-slider-thumb {\n  background-color: #002c59;\n}\n\n.dark-mode .custom-range.custom-range-navy::-webkit-slider-thumb:active {\n  background-color: #0c84ff;\n}\n\n.dark-mode .custom-range.custom-range-navy::-moz-range-thumb {\n  background-color: #002c59;\n}\n\n.dark-mode .custom-range.custom-range-navy::-moz-range-thumb:active {\n  background-color: #0c84ff;\n}\n\n.dark-mode .custom-range.custom-range-navy::-ms-thumb {\n  background-color: #002c59;\n}\n\n.dark-mode .custom-range.custom-range-navy::-ms-thumb:active {\n  background-color: #0c84ff;\n}\n\n.dark-mode .custom-range.custom-range-olive:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-olive:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-olive:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-olive:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-olive::-webkit-slider-thumb {\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-range.custom-range-olive::-webkit-slider-thumb:active {\n  background-color: #f4fbf8;\n}\n\n.dark-mode .custom-range.custom-range-olive::-moz-range-thumb {\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-range.custom-range-olive::-moz-range-thumb:active {\n  background-color: #f4fbf8;\n}\n\n.dark-mode .custom-range.custom-range-olive::-ms-thumb {\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-range.custom-range-olive::-ms-thumb:active {\n  background-color: #f4fbf8;\n}\n\n.dark-mode .custom-range.custom-range-lime:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-lime:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lime:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lime:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lime::-webkit-slider-thumb {\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-range.custom-range-lime::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lime::-moz-range-thumb {\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-range.custom-range-lime::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lime::-ms-thumb {\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-range.custom-range-lime::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-webkit-slider-thumb {\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-moz-range-thumb {\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-ms-thumb {\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-maroon::-webkit-slider-thumb {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-moz-range-thumb {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-ms-thumb {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-blue:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-blue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-blue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-blue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-blue::-webkit-slider-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-blue::-webkit-slider-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-blue::-moz-range-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-blue::-moz-range-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-blue::-ms-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-blue::-ms-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-indigo::-webkit-slider-thumb {\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-webkit-slider-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-moz-range-thumb {\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-moz-range-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-ms-thumb {\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-ms-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.dark-mode .custom-range.custom-range-purple:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-purple:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-purple:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-purple:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-purple::-webkit-slider-thumb {\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-range.custom-range-purple::-webkit-slider-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.dark-mode .custom-range.custom-range-purple::-moz-range-thumb {\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-range.custom-range-purple::-moz-range-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.dark-mode .custom-range.custom-range-purple::-ms-thumb {\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-range.custom-range-purple::-ms-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.dark-mode .custom-range.custom-range-pink:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-pink:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-pink:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-pink:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-pink::-webkit-slider-thumb {\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-range.custom-range-pink::-webkit-slider-thumb:active {\n  background-color: #fbddeb;\n}\n\n.dark-mode .custom-range.custom-range-pink::-moz-range-thumb {\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-range.custom-range-pink::-moz-range-thumb:active {\n  background-color: #fbddeb;\n}\n\n.dark-mode .custom-range.custom-range-pink::-ms-thumb {\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-range.custom-range-pink::-ms-thumb:active {\n  background-color: #fbddeb;\n}\n\n.dark-mode .custom-range.custom-range-red:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-red:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-red:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-red:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-red::-webkit-slider-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-red::-webkit-slider-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-red::-moz-range-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-red::-moz-range-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-red::-ms-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-red::-ms-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-orange:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-orange:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-orange:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-orange:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-orange::-webkit-slider-thumb {\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-range.custom-range-orange::-webkit-slider-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.dark-mode .custom-range.custom-range-orange::-moz-range-thumb {\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-range.custom-range-orange::-moz-range-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.dark-mode .custom-range.custom-range-orange::-ms-thumb {\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-range.custom-range-orange::-ms-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-yellow::-webkit-slider-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-webkit-slider-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-moz-range-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-moz-range-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-ms-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-ms-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-green:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-green:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-green:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-green:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-green::-webkit-slider-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-green::-webkit-slider-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-green::-moz-range-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-green::-moz-range-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-green::-ms-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-green::-ms-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-teal:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-teal:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-teal:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-teal:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-teal::-webkit-slider-thumb {\n  background-color: #20c997;\n}\n\n.dark-mode .custom-range.custom-range-teal::-webkit-slider-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.dark-mode .custom-range.custom-range-teal::-moz-range-thumb {\n  background-color: #20c997;\n}\n\n.dark-mode .custom-range.custom-range-teal::-moz-range-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.dark-mode .custom-range.custom-range-teal::-ms-thumb {\n  background-color: #20c997;\n}\n\n.dark-mode .custom-range.custom-range-teal::-ms-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-cyan::-webkit-slider-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-webkit-slider-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-moz-range-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-moz-range-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-ms-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-ms-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-white:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-white:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-white:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-white:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-white::-webkit-slider-thumb {\n  background-color: #fff;\n}\n\n.dark-mode .custom-range.custom-range-white::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-white::-moz-range-thumb {\n  background-color: #fff;\n}\n\n.dark-mode .custom-range.custom-range-white::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-white::-ms-thumb {\n  background-color: #fff;\n}\n\n.dark-mode .custom-range.custom-range-white::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-gray:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-gray:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-gray::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-gray::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-gray::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-gray::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-gray::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-off-primary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::after {\n  background-color: #182838;\n}\n\n.dark-mode .custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-primary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #97b4d2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-off-secondary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.dark-mode .custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-on-secondary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.dark-mode .custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-off-success .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::after {\n  background-color: #003d2d;\n}\n\n.dark-mode .custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-on-success .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #56ffd4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-off-info .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::after {\n  background-color: #16527a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-on-info .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #b6daf2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-off-warning .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::after {\n  background-color: #7f5006;\n}\n\n.dark-mode .custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-on-warning .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fad9a4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-off-danger .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::after {\n  background-color: #921e12;\n}\n\n.dark-mode .custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-on-danger .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c9c4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.dark-mode .custom-switch.custom-switch-off-light .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::after {\n  background-color: #aeb9c5;\n}\n\n.dark-mode .custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.dark-mode .custom-switch.custom-switch-on-light .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-off-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-on-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::before {\n  background-color: #86bad8;\n  border-color: #3c8dbc;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lightblue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::after {\n  background-color: #367fa9;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #86bad8;\n  border-color: #3c8dbc;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lightblue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fafcfd;\n}\n\n.dark-mode .custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::before {\n  background-color: #002c59;\n  border-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-off-navy .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #002c59;\n  border-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-navy .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #0077f2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::before {\n  background-color: #74c8a3;\n  border-color: #3d9970;\n}\n\n.dark-mode .custom-switch.custom-switch-off-olive .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::after {\n  background-color: #368763;\n}\n\n.dark-mode .custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #74c8a3;\n  border-color: #3d9970;\n}\n\n.dark-mode .custom-switch.custom-switch-on-olive .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #e2f3eb;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::before {\n  background-color: #67ffa9;\n  border-color: #01ff70;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lime .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::after {\n  background-color: #00e765;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #67ffa9;\n  border-color: #01ff70;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lime .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::before {\n  background-color: #f672d8;\n  border-color: #f012be;\n}\n\n.dark-mode .custom-switch.custom-switch-off-fuchsia .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::after {\n  background-color: #db0ead;\n}\n\n.dark-mode .custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f672d8;\n  border-color: #f012be;\n}\n\n.dark-mode .custom-switch.custom-switch-on-fuchsia .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::before {\n  background-color: #ed6c9b;\n  border-color: #d81b60;\n}\n\n.dark-mode .custom-switch.custom-switch-off-maroon .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::after {\n  background-color: #c11856;\n}\n\n.dark-mode .custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ed6c9b;\n  border-color: #d81b60;\n}\n\n.dark-mode .custom-switch.custom-switch-on-maroon .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fef4f8;\n}\n\n.dark-mode .custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-off-blue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::after {\n  background-color: #182838;\n}\n\n.dark-mode .custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-blue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #97b4d2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.dark-mode .custom-switch.custom-switch-off-indigo .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::after {\n  background-color: #33077c;\n}\n\n.dark-mode .custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.dark-mode .custom-switch.custom-switch-on-indigo .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c3a1fa;\n}\n\n.dark-mode .custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.dark-mode .custom-switch.custom-switch-off-purple .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::after {\n  background-color: #382063;\n}\n\n.dark-mode .custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.dark-mode .custom-switch.custom-switch-on-purple .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c7b5e7;\n}\n\n.dark-mode .custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.dark-mode .custom-switch.custom-switch-off-pink .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::after {\n  background-color: #95124e;\n}\n\n.dark-mode .custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-pink .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c7dd;\n}\n\n.dark-mode .custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-off-red .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::after {\n  background-color: #921e12;\n}\n\n.dark-mode .custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-on-red .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c9c4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.dark-mode .custom-switch.custom-switch-off-orange .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::after {\n  background-color: #904201;\n}\n\n.dark-mode .custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.dark-mode .custom-switch.custom-switch-on-orange .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fed1ac;\n}\n\n.dark-mode .custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-off-yellow .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::after {\n  background-color: #7f5006;\n}\n\n.dark-mode .custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-on-yellow .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fad9a4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-off-green .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::after {\n  background-color: #003d2d;\n}\n\n.dark-mode .custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-on-green .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #56ffd4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.dark-mode .custom-switch.custom-switch-off-teal .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::after {\n  background-color: #0e5b44;\n}\n\n.dark-mode .custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.dark-mode .custom-switch.custom-switch-on-teal .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #94eed3;\n}\n\n.dark-mode .custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-off-cyan .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::after {\n  background-color: #16527a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-on-cyan .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #b6daf2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.dark-mode .custom-switch.custom-switch-off-white .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::after {\n  background-color: #bfbfbf;\n}\n\n.dark-mode .custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.dark-mode .custom-switch.custom-switch-on-white .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.dark-mode .custom-control-input-primary:checked ~ .custom-control-label::before {\n  border-color: #3f6791;\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-control-input-primary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233f6791' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-primary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233f6791'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-primary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-control-input-primary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #85a7ca;\n}\n\n.dark-mode .custom-control-input-primary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #a9c1da;\n  border-color: #a9c1da;\n}\n\n.dark-mode .custom-control-input-secondary:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-control-input-secondary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-secondary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-secondary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-control-input-secondary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.dark-mode .custom-control-input-secondary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.dark-mode .custom-control-input-success:checked ~ .custom-control-label::before {\n  border-color: #00bc8c;\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-control-input-success.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2300bc8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-success.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2300bc8c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-success:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-control-input-success:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #3dffcd;\n}\n\n.dark-mode .custom-control-input-success:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #70ffda;\n  border-color: #70ffda;\n}\n\n.dark-mode .custom-control-input-info:checked ~ .custom-control-label::before {\n  border-color: #3498db;\n  background-color: #3498db;\n}\n\n.dark-mode .custom-control-input-info.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233498db' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-info.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233498db'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-info:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-control-input-info:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #a0cfee;\n}\n\n.dark-mode .custom-control-input-info:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #cce5f6;\n  border-color: #cce5f6;\n}\n\n.dark-mode .custom-control-input-warning:checked ~ .custom-control-label::before {\n  border-color: #f39c12;\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-control-input-warning.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f39c12' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-warning.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f39c12'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-warning:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-control-input-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .custom-control-input-warning:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fce3bc;\n  border-color: #fce3bc;\n}\n\n.dark-mode .custom-control-input-danger:checked ~ .custom-control-label::before {\n  border-color: #e74c3c;\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-control-input-danger.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e74c3c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-danger.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e74c3c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-danger:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-control-input-danger:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .custom-control-input-danger:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbdedb;\n  border-color: #fbdedb;\n}\n\n.dark-mode .custom-control-input-light:checked ~ .custom-control-label::before {\n  border-color: #f8f9fa;\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-control-input-light.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f8f9fa' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-light.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f8f9fa'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-light:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-control-input-light:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-light:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.dark-mode .custom-control-input-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-control-input-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.dark-mode .custom-control-input-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.dark-mode .custom-control-input-lightblue:checked ~ .custom-control-label::before {\n  border-color: #86bad8;\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-control-input-lightblue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2386bad8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lightblue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2386bad8'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lightblue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-control-input-lightblue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .custom-control-input-lightblue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-navy:checked ~ .custom-control-label::before {\n  border-color: #002c59;\n  background-color: #002c59;\n}\n\n.dark-mode .custom-control-input-navy.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23002c59' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-navy.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23002c59'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-navy:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-control-input-navy:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #006ad8;\n}\n\n.dark-mode .custom-control-input-navy:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #0c84ff;\n  border-color: #0c84ff;\n}\n\n.dark-mode .custom-control-input-olive:checked ~ .custom-control-label::before {\n  border-color: #74c8a3;\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-control-input-olive.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2374c8a3' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-olive.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2374c8a3'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-olive:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-control-input-olive:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #cfecdf;\n}\n\n.dark-mode .custom-control-input-olive:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f4fbf8;\n  border-color: #f4fbf8;\n}\n\n.dark-mode .custom-control-input-lime:checked ~ .custom-control-label::before {\n  border-color: #67ffa9;\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-control-input-lime.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2367ffa9' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lime.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2367ffa9'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lime:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-control-input-lime:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #e7fff1;\n}\n\n.dark-mode .custom-control-input-lime:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-fuchsia:checked ~ .custom-control-label::before {\n  border-color: #f672d8;\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f672d8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f672d8'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-fuchsia:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-control-input-fuchsia:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #feeaf9;\n}\n\n.dark-mode .custom-control-input-fuchsia:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-maroon:checked ~ .custom-control-label::before {\n  border-color: #ed6c9b;\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-control-input-maroon.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ed6c9b' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-maroon.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ed6c9b'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-maroon:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-control-input-maroon:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #fbdee8;\n}\n\n.dark-mode .custom-control-input-maroon:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-blue:checked ~ .custom-control-label::before {\n  border-color: #3f6791;\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-control-input-blue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233f6791' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-blue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233f6791'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-blue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-control-input-blue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #85a7ca;\n}\n\n.dark-mode .custom-control-input-blue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #a9c1da;\n  border-color: #a9c1da;\n}\n\n.dark-mode .custom-control-input-indigo:checked ~ .custom-control-label::before {\n  border-color: #6610f2;\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-control-input-indigo.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236610f2' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-indigo.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236610f2'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-indigo:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-control-input-indigo:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b389f9;\n}\n\n.dark-mode .custom-control-input-indigo:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d2b9fb;\n  border-color: #d2b9fb;\n}\n\n.dark-mode .custom-control-input-purple:checked ~ .custom-control-label::before {\n  border-color: #6f42c1;\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-control-input-purple.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236f42c1' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-purple.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236f42c1'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-purple:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-control-input-purple:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .custom-control-input-purple:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d5c8ed;\n  border-color: #d5c8ed;\n}\n\n.dark-mode .custom-control-input-pink:checked ~ .custom-control-label::before {\n  border-color: #e83e8c;\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-control-input-pink.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e83e8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-pink.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e83e8c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-pink:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-control-input-pink:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .custom-control-input-pink:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbddeb;\n  border-color: #fbddeb;\n}\n\n.dark-mode .custom-control-input-red:checked ~ .custom-control-label::before {\n  border-color: #e74c3c;\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-control-input-red.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e74c3c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-red.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e74c3c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-red:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-control-input-red:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .custom-control-input-red:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbdedb;\n  border-color: #fbdedb;\n}\n\n.dark-mode .custom-control-input-orange:checked ~ .custom-control-label::before {\n  border-color: #fd7e14;\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-control-input-orange.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fd7e14' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-orange.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fd7e14'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-orange:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-control-input-orange:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #fec392;\n}\n\n.dark-mode .custom-control-input-orange:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffdfc5;\n  border-color: #ffdfc5;\n}\n\n.dark-mode .custom-control-input-yellow:checked ~ .custom-control-label::before {\n  border-color: #f39c12;\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-control-input-yellow.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f39c12' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-yellow.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f39c12'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-yellow:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-control-input-yellow:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .custom-control-input-yellow:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fce3bc;\n  border-color: #fce3bc;\n}\n\n.dark-mode .custom-control-input-green:checked ~ .custom-control-label::before {\n  border-color: #00bc8c;\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-control-input-green.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2300bc8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-green.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2300bc8c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-green:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-control-input-green:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #3dffcd;\n}\n\n.dark-mode .custom-control-input-green:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #70ffda;\n  border-color: #70ffda;\n}\n\n.dark-mode .custom-control-input-teal:checked ~ .custom-control-label::before {\n  border-color: #20c997;\n  background-color: #20c997;\n}\n\n.dark-mode .custom-control-input-teal.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2320c997' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-teal.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2320c997'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-teal:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-control-input-teal:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #7eeaca;\n}\n\n.dark-mode .custom-control-input-teal:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #aaf1dc;\n  border-color: #aaf1dc;\n}\n\n.dark-mode .custom-control-input-cyan:checked ~ .custom-control-label::before {\n  border-color: #3498db;\n  background-color: #3498db;\n}\n\n.dark-mode .custom-control-input-cyan.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233498db' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-cyan.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233498db'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-cyan:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-control-input-cyan:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #a0cfee;\n}\n\n.dark-mode .custom-control-input-cyan:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #cce5f6;\n  border-color: #cce5f6;\n}\n\n.dark-mode .custom-control-input-white:checked ~ .custom-control-label::before {\n  border-color: #fff;\n  background-color: #fff;\n}\n\n.dark-mode .custom-control-input-white.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-white.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-white:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-control-input-white:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-white:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-gray:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-control-input-gray.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-control-input-gray:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.dark-mode .custom-control-input-gray:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.dark-mode .custom-control-input-gray-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.dark-mode .custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-control-input-gray-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.dark-mode .custom-control-input-gray-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.progress {\n  box-shadow: none;\n  border-radius: 1px;\n}\n\n.progress.vertical {\n  display: inline-block;\n  height: 200px;\n  margin-right: 10px;\n  position: relative;\n  width: 30px;\n}\n\n.progress.vertical > .progress-bar {\n  bottom: 0;\n  position: absolute;\n  width: 100%;\n}\n\n.progress.vertical.sm, .progress.vertical.progress-sm {\n  width: 20px;\n}\n\n.progress.vertical.xs, .progress.vertical.progress-xs {\n  width: 10px;\n}\n\n.progress.vertical.xxs, .progress.vertical.progress-xxs {\n  width: 3px;\n}\n\n.progress-group {\n  margin-bottom: 0.5rem;\n}\n\n.progress-sm {\n  height: 10px;\n}\n\n.progress-xs {\n  height: 7px;\n}\n\n.progress-xxs {\n  height: 3px;\n}\n\n.table tr > td .progress {\n  margin: 0;\n}\n\n.dark-mode .progress {\n  background: #454d55;\n}\n\n.card-primary:not(.card-outline) > .card-header {\n  background-color: #007bff;\n}\n\n.card-primary:not(.card-outline) > .card-header,\n.card-primary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-primary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-primary.card-outline {\n  border-top: 3px solid #007bff;\n}\n\n.card-primary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-primary.card-outline-tabs > .card-header a.active,\n.card-primary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #007bff;\n}\n\n.bg-primary > .card-header .btn-tool,\n.bg-gradient-primary > .card-header .btn-tool,\n.card-primary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-primary > .card-header .btn-tool:hover,\n.bg-gradient-primary > .card-header .btn-tool:hover,\n.card-primary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget .table td,\n.card.bg-primary .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #0067d6;\n  color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3395ff;\n  color: #fff;\n}\n\n.card-secondary:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.card-secondary:not(.card-outline) > .card-header,\n.card-secondary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-secondary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-secondary.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.card-secondary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-secondary.card-outline-tabs > .card-header a.active,\n.card-secondary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.bg-secondary > .card-header .btn-tool,\n.bg-gradient-secondary > .card-header .btn-tool,\n.card-secondary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-secondary > .card-header .btn-tool:hover,\n.bg-gradient-secondary > .card-header .btn-tool:hover,\n.card-secondary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget .table td,\n.card.bg-secondary .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.card-success:not(.card-outline) > .card-header {\n  background-color: #28a745;\n}\n\n.card-success:not(.card-outline) > .card-header,\n.card-success:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-success:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-success.card-outline {\n  border-top: 3px solid #28a745;\n}\n\n.card-success.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-success.card-outline-tabs > .card-header a.active,\n.card-success.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #28a745;\n}\n\n.bg-success > .card-header .btn-tool,\n.bg-gradient-success > .card-header .btn-tool,\n.card-success:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-success > .card-header .btn-tool:hover,\n.bg-gradient-success > .card-header .btn-tool:hover,\n.card-success:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget .table td,\n.card.bg-success .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #208637;\n  color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table td.active,\n.card.bg-success .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ce57;\n  color: #fff;\n}\n\n.card-info:not(.card-outline) > .card-header {\n  background-color: #17a2b8;\n}\n\n.card-info:not(.card-outline) > .card-header,\n.card-info:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-info:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-info.card-outline {\n  border-top: 3px solid #17a2b8;\n}\n\n.card-info.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-info.card-outline-tabs > .card-header a.active,\n.card-info.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #17a2b8;\n}\n\n.bg-info > .card-header .btn-tool,\n.bg-gradient-info > .card-header .btn-tool,\n.card-info:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-info > .card-header .btn-tool:hover,\n.bg-gradient-info > .card-header .btn-tool:hover,\n.card-info:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget .table td,\n.card.bg-info .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #128294;\n  color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table td.active,\n.card.bg-info .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #1fc8e3;\n  color: #fff;\n}\n\n.card-warning:not(.card-outline) > .card-header {\n  background-color: #ffc107;\n}\n\n.card-warning:not(.card-outline) > .card-header,\n.card-warning:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-warning:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-warning.card-outline {\n  border-top: 3px solid #ffc107;\n}\n\n.card-warning.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-warning.card-outline-tabs > .card-header a.active,\n.card-warning.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ffc107;\n}\n\n.bg-warning > .card-header .btn-tool,\n.bg-gradient-warning > .card-header .btn-tool,\n.card-warning:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-warning > .card-header .btn-tool:hover,\n.bg-gradient-warning > .card-header .btn-tool:hover,\n.card-warning:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget .table td,\n.card.bg-warning .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #dda600;\n  color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table td.active,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ffce3a;\n  color: #1f2d3d;\n}\n\n.card-danger:not(.card-outline) > .card-header {\n  background-color: #dc3545;\n}\n\n.card-danger:not(.card-outline) > .card-header,\n.card-danger:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-danger:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-danger.card-outline {\n  border-top: 3px solid #dc3545;\n}\n\n.card-danger.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-danger.card-outline-tabs > .card-header a.active,\n.card-danger.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #dc3545;\n}\n\n.bg-danger > .card-header .btn-tool,\n.bg-gradient-danger > .card-header .btn-tool,\n.card-danger:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-danger > .card-header .btn-tool:hover,\n.bg-gradient-danger > .card-header .btn-tool:hover,\n.card-danger:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget .table td,\n.card.bg-danger .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #c62232;\n  color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table td.active,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e4606d;\n  color: #fff;\n}\n\n.card-light:not(.card-outline) > .card-header {\n  background-color: #f8f9fa;\n}\n\n.card-light:not(.card-outline) > .card-header,\n.card-light:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-light:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-light.card-outline {\n  border-top: 3px solid #f8f9fa;\n}\n\n.card-light.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-light.card-outline-tabs > .card-header a.active,\n.card-light.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f8f9fa;\n}\n\n.bg-light > .card-header .btn-tool,\n.bg-gradient-light > .card-header .btn-tool,\n.card-light:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-light > .card-header .btn-tool:hover,\n.bg-gradient-light > .card-header .btn-tool:hover,\n.card-light:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget .table td,\n.card.bg-light .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e0e5e9;\n  color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table td.active,\n.card.bg-light .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.card-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.card-dark:not(.card-outline) > .card-header,\n.card-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.card-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-dark.card-outline-tabs > .card-header a.active,\n.card-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.bg-dark > .card-header .btn-tool,\n.bg-gradient-dark > .card-header .btn-tool,\n.card-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-dark > .card-header .btn-tool:hover,\n.bg-gradient-dark > .card-header .btn-tool:hover,\n.card-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-dark .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.card-lightblue:not(.card-outline) > .card-header {\n  background-color: #3c8dbc;\n}\n\n.card-lightblue:not(.card-outline) > .card-header,\n.card-lightblue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-lightblue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-lightblue.card-outline {\n  border-top: 3px solid #3c8dbc;\n}\n\n.card-lightblue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-lightblue.card-outline-tabs > .card-header a.active,\n.card-lightblue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3c8dbc;\n}\n\n.bg-lightblue > .card-header .btn-tool,\n.bg-gradient-lightblue > .card-header .btn-tool,\n.card-lightblue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-lightblue > .card-header .btn-tool:hover,\n.bg-gradient-lightblue > .card-header .btn-tool:hover,\n.card-lightblue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget .table td,\n.card.bg-lightblue .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #32769d;\n  color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5fa4cc;\n  color: #fff;\n}\n\n.card-navy:not(.card-outline) > .card-header {\n  background-color: #001f3f;\n}\n\n.card-navy:not(.card-outline) > .card-header,\n.card-navy:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-navy:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-navy.card-outline {\n  border-top: 3px solid #001f3f;\n}\n\n.card-navy.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-navy.card-outline-tabs > .card-header a.active,\n.card-navy.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #001f3f;\n}\n\n.bg-navy > .card-header .btn-tool,\n.bg-gradient-navy > .card-header .btn-tool,\n.card-navy:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-navy > .card-header .btn-tool:hover,\n.bg-gradient-navy > .card-header .btn-tool:hover,\n.card-navy:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget .table td,\n.card.bg-navy .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #000b16;\n  color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table td.active,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #003872;\n  color: #fff;\n}\n\n.card-olive:not(.card-outline) > .card-header {\n  background-color: #3d9970;\n}\n\n.card-olive:not(.card-outline) > .card-header,\n.card-olive:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-olive:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-olive.card-outline {\n  border-top: 3px solid #3d9970;\n}\n\n.card-olive.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-olive.card-outline-tabs > .card-header a.active,\n.card-olive.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3d9970;\n}\n\n.bg-olive > .card-header .btn-tool,\n.bg-gradient-olive > .card-header .btn-tool,\n.card-olive:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-olive > .card-header .btn-tool:hover,\n.bg-gradient-olive > .card-header .btn-tool:hover,\n.card-olive:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget .table td,\n.card.bg-olive .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #317c5b;\n  color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table td.active,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #50b98a;\n  color: #fff;\n}\n\n.card-lime:not(.card-outline) > .card-header {\n  background-color: #01ff70;\n}\n\n.card-lime:not(.card-outline) > .card-header,\n.card-lime:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-lime:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-lime.card-outline {\n  border-top: 3px solid #01ff70;\n}\n\n.card-lime.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-lime.card-outline-tabs > .card-header a.active,\n.card-lime.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #01ff70;\n}\n\n.bg-lime > .card-header .btn-tool,\n.bg-gradient-lime > .card-header .btn-tool,\n.card-lime:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-lime > .card-header .btn-tool:hover,\n.bg-gradient-lime > .card-header .btn-tool:hover,\n.card-lime:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget .table td,\n.card.bg-lime .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #00d75e;\n  color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table td.active,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ff8d;\n  color: #1f2d3d;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header {\n  background-color: #f012be;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header,\n.card-fuchsia:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-fuchsia.card-outline {\n  border-top: 3px solid #f012be;\n}\n\n.card-fuchsia.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-fuchsia.card-outline-tabs > .card-header a.active,\n.card-fuchsia.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f012be;\n}\n\n.bg-fuchsia > .card-header .btn-tool,\n.bg-gradient-fuchsia > .card-header .btn-tool,\n.card-fuchsia:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-fuchsia > .card-header .btn-tool:hover,\n.bg-gradient-fuchsia > .card-header .btn-tool:hover,\n.card-fuchsia:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget .table td,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #cc0da1;\n  color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f342cb;\n  color: #fff;\n}\n\n.card-maroon:not(.card-outline) > .card-header {\n  background-color: #d81b60;\n}\n\n.card-maroon:not(.card-outline) > .card-header,\n.card-maroon:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-maroon:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-maroon.card-outline {\n  border-top: 3px solid #d81b60;\n}\n\n.card-maroon.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-maroon.card-outline-tabs > .card-header a.active,\n.card-maroon.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #d81b60;\n}\n\n.bg-maroon > .card-header .btn-tool,\n.bg-gradient-maroon > .card-header .btn-tool,\n.card-maroon:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-maroon > .card-header .btn-tool:hover,\n.bg-gradient-maroon > .card-header .btn-tool:hover,\n.card-maroon:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget .table td,\n.card.bg-maroon .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #b41650;\n  color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.active,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e73f7c;\n  color: #fff;\n}\n\n.card-blue:not(.card-outline) > .card-header {\n  background-color: #007bff;\n}\n\n.card-blue:not(.card-outline) > .card-header,\n.card-blue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-blue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-blue.card-outline {\n  border-top: 3px solid #007bff;\n}\n\n.card-blue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-blue.card-outline-tabs > .card-header a.active,\n.card-blue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #007bff;\n}\n\n.bg-blue > .card-header .btn-tool,\n.bg-gradient-blue > .card-header .btn-tool,\n.card-blue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-blue > .card-header .btn-tool:hover,\n.bg-gradient-blue > .card-header .btn-tool:hover,\n.card-blue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget .table td,\n.card.bg-blue .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #0067d6;\n  color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3395ff;\n  color: #fff;\n}\n\n.card-indigo:not(.card-outline) > .card-header {\n  background-color: #6610f2;\n}\n\n.card-indigo:not(.card-outline) > .card-header,\n.card-indigo:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-indigo:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-indigo.card-outline {\n  border-top: 3px solid #6610f2;\n}\n\n.card-indigo.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-indigo.card-outline-tabs > .card-header a.active,\n.card-indigo.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6610f2;\n}\n\n.bg-indigo > .card-header .btn-tool,\n.bg-gradient-indigo > .card-header .btn-tool,\n.card-indigo:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-indigo > .card-header .btn-tool:hover,\n.bg-gradient-indigo > .card-header .btn-tool:hover,\n.card-indigo:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget .table td,\n.card.bg-indigo .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #550bce;\n  color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.active,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8540f5;\n  color: #fff;\n}\n\n.card-purple:not(.card-outline) > .card-header {\n  background-color: #6f42c1;\n}\n\n.card-purple:not(.card-outline) > .card-header,\n.card-purple:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-purple:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-purple.card-outline {\n  border-top: 3px solid #6f42c1;\n}\n\n.card-purple.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-purple.card-outline-tabs > .card-header a.active,\n.card-purple.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6f42c1;\n}\n\n.bg-purple > .card-header .btn-tool,\n.bg-gradient-purple > .card-header .btn-tool,\n.card-purple:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-purple > .card-header .btn-tool:hover,\n.bg-gradient-purple > .card-header .btn-tool:hover,\n.card-purple:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget .table td,\n.card.bg-purple .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #5d36a4;\n  color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table td.active,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8c68ce;\n  color: #fff;\n}\n\n.card-pink:not(.card-outline) > .card-header {\n  background-color: #e83e8c;\n}\n\n.card-pink:not(.card-outline) > .card-header,\n.card-pink:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-pink:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-pink.card-outline {\n  border-top: 3px solid #e83e8c;\n}\n\n.card-pink.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-pink.card-outline-tabs > .card-header a.active,\n.card-pink.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e83e8c;\n}\n\n.bg-pink > .card-header .btn-tool,\n.bg-gradient-pink > .card-header .btn-tool,\n.card-pink:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-pink > .card-header .btn-tool:hover,\n.bg-gradient-pink > .card-header .btn-tool:hover,\n.card-pink:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget .table td,\n.card.bg-pink .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e21b76;\n  color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table td.active,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed6ca7;\n  color: #fff;\n}\n\n.card-red:not(.card-outline) > .card-header {\n  background-color: #dc3545;\n}\n\n.card-red:not(.card-outline) > .card-header,\n.card-red:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-red:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-red.card-outline {\n  border-top: 3px solid #dc3545;\n}\n\n.card-red.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-red.card-outline-tabs > .card-header a.active,\n.card-red.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #dc3545;\n}\n\n.bg-red > .card-header .btn-tool,\n.bg-gradient-red > .card-header .btn-tool,\n.card-red:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-red > .card-header .btn-tool:hover,\n.bg-gradient-red > .card-header .btn-tool:hover,\n.card-red:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget .table td,\n.card.bg-red .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #c62232;\n  color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table td.active,\n.card.bg-red .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e4606d;\n  color: #fff;\n}\n\n.card-orange:not(.card-outline) > .card-header {\n  background-color: #fd7e14;\n}\n\n.card-orange:not(.card-outline) > .card-header,\n.card-orange:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-orange:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-orange.card-outline {\n  border-top: 3px solid #fd7e14;\n}\n\n.card-orange.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-orange.card-outline-tabs > .card-header a.active,\n.card-orange.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fd7e14;\n}\n\n.bg-orange > .card-header .btn-tool,\n.bg-gradient-orange > .card-header .btn-tool,\n.card-orange:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-orange > .card-header .btn-tool:hover,\n.bg-gradient-orange > .card-header .btn-tool:hover,\n.card-orange:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget .table td,\n.card.bg-orange .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e66a02;\n  color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table td.active,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #fd9a47;\n  color: #1f2d3d;\n}\n\n.card-yellow:not(.card-outline) > .card-header {\n  background-color: #ffc107;\n}\n\n.card-yellow:not(.card-outline) > .card-header,\n.card-yellow:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-yellow:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-yellow.card-outline {\n  border-top: 3px solid #ffc107;\n}\n\n.card-yellow.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-yellow.card-outline-tabs > .card-header a.active,\n.card-yellow.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ffc107;\n}\n\n.bg-yellow > .card-header .btn-tool,\n.bg-gradient-yellow > .card-header .btn-tool,\n.card-yellow:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-yellow > .card-header .btn-tool:hover,\n.bg-gradient-yellow > .card-header .btn-tool:hover,\n.card-yellow:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget .table td,\n.card.bg-yellow .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #dda600;\n  color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.active,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ffce3a;\n  color: #1f2d3d;\n}\n\n.card-green:not(.card-outline) > .card-header {\n  background-color: #28a745;\n}\n\n.card-green:not(.card-outline) > .card-header,\n.card-green:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-green:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-green.card-outline {\n  border-top: 3px solid #28a745;\n}\n\n.card-green.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-green.card-outline-tabs > .card-header a.active,\n.card-green.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #28a745;\n}\n\n.bg-green > .card-header .btn-tool,\n.bg-gradient-green > .card-header .btn-tool,\n.card-green:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-green > .card-header .btn-tool:hover,\n.bg-gradient-green > .card-header .btn-tool:hover,\n.card-green:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget .table td,\n.card.bg-green .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #208637;\n  color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table td.active,\n.card.bg-green .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ce57;\n  color: #fff;\n}\n\n.card-teal:not(.card-outline) > .card-header {\n  background-color: #20c997;\n}\n\n.card-teal:not(.card-outline) > .card-header,\n.card-teal:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-teal:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-teal.card-outline {\n  border-top: 3px solid #20c997;\n}\n\n.card-teal.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-teal.card-outline-tabs > .card-header a.active,\n.card-teal.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #20c997;\n}\n\n.bg-teal > .card-header .btn-tool,\n.bg-gradient-teal > .card-header .btn-tool,\n.card-teal:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-teal > .card-header .btn-tool:hover,\n.bg-gradient-teal > .card-header .btn-tool:hover,\n.card-teal:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget .table td,\n.card.bg-teal .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #1aa67d;\n  color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table td.active,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3ce0af;\n  color: #fff;\n}\n\n.card-cyan:not(.card-outline) > .card-header {\n  background-color: #17a2b8;\n}\n\n.card-cyan:not(.card-outline) > .card-header,\n.card-cyan:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-cyan:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-cyan.card-outline {\n  border-top: 3px solid #17a2b8;\n}\n\n.card-cyan.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-cyan.card-outline-tabs > .card-header a.active,\n.card-cyan.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #17a2b8;\n}\n\n.bg-cyan > .card-header .btn-tool,\n.bg-gradient-cyan > .card-header .btn-tool,\n.card-cyan:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-cyan > .card-header .btn-tool:hover,\n.bg-gradient-cyan > .card-header .btn-tool:hover,\n.card-cyan:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget .table td,\n.card.bg-cyan .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #128294;\n  color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.active,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #1fc8e3;\n  color: #fff;\n}\n\n.card-white:not(.card-outline) > .card-header {\n  background-color: #fff;\n}\n\n.card-white:not(.card-outline) > .card-header,\n.card-white:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-white:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-white.card-outline {\n  border-top: 3px solid #fff;\n}\n\n.card-white.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-white.card-outline-tabs > .card-header a.active,\n.card-white.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fff;\n}\n\n.bg-white > .card-header .btn-tool,\n.bg-gradient-white > .card-header .btn-tool,\n.card-white:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-white > .card-header .btn-tool:hover,\n.bg-gradient-white > .card-header .btn-tool:hover,\n.card-white:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget .table td,\n.card.bg-white .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #ebebeb;\n  color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table td.active,\n.card.bg-white .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.card-gray:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.card-gray:not(.card-outline) > .card-header,\n.card-gray:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-gray:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-gray.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.card-gray.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-gray.card-outline-tabs > .card-header a.active,\n.card-gray.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.bg-gray > .card-header .btn-tool,\n.bg-gradient-gray > .card-header .btn-tool,\n.card-gray:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-gray > .card-header .btn-tool:hover,\n.bg-gradient-gray > .card-header .btn-tool:hover,\n.card-gray:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget .table td,\n.card.bg-gray .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header,\n.card-gray-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-gray-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.card-gray-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-gray-dark.card-outline-tabs > .card-header a.active,\n.card-gray-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.bg-gray-dark > .card-header .btn-tool,\n.bg-gradient-gray-dark > .card-header .btn-tool,\n.card-gray-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-gray-dark > .card-header .btn-tool:hover,\n.bg-gradient-gray-dark > .card-header .btn-tool:hover,\n.card-gray-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.card {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  margin-bottom: 1rem;\n}\n\n.card.bg-dark .card-header {\n  border-color: #383f45;\n}\n\n.card.bg-dark,\n.card.bg-dark .card-body {\n  color: #fff;\n}\n\n.card.maximized-card {\n  height: 100% !important;\n  left: 0;\n  max-height: 100% !important;\n  max-width: 100% !important;\n  position: fixed;\n  top: 0;\n  width: 100% !important;\n  z-index: 1040;\n}\n\n.card.maximized-card.was-collapsed .card-body {\n  display: block !important;\n}\n\n.card.maximized-card .card-body {\n  overflow: auto;\n}\n\n.card.maximized-card [data-card-widgett=\"collapse\"] {\n  display: none;\n}\n\n.card.maximized-card .card-header,\n.card.maximized-card .card-footer {\n  border-radius: 0 !important;\n}\n\n.card.collapsed-card .card-body,\n.card.collapsed-card .card-footer {\n  display: none;\n}\n\n.card .nav.flex-column:not(.nav-sidebar) > li {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  margin: 0;\n}\n\n.card .nav.flex-column:not(.nav-sidebar) > li:last-of-type {\n  border-bottom: 0;\n}\n\n.card.height-control .card-body {\n  max-height: 300px;\n  overflow: auto;\n}\n\n.card .border-right {\n  border-right: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card .border-left {\n  border-left: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card.card-tabs:not(.card-outline) > .card-header {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.card-outline) > .card-header .nav-item:first-child .nav-link {\n  border-left-color: transparent;\n}\n\n.card.card-tabs.card-outline .nav-item {\n  border-bottom: 0;\n}\n\n.card.card-tabs.card-outline .nav-item:first-child .nav-link {\n  border-left: 0;\n  margin-left: 0;\n}\n\n.card.card-tabs .card-tools {\n  margin: .3rem .5rem;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {\n  margin-bottom: 0;\n}\n\n.card.card-tabs.expanding-card .card-header .nav-tabs .nav-item {\n  margin-bottom: -1px;\n}\n\n.card.card-outline-tabs {\n  border-top: 0;\n}\n\n.card.card-outline-tabs .card-header .nav-item:first-child .nav-link {\n  border-left: 0;\n  margin-left: 0;\n}\n\n.card.card-outline-tabs .card-header a {\n  border-top: 3px solid transparent;\n}\n\n.card.card-outline-tabs .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card.card-outline-tabs .card-header a.active:hover {\n  margin-top: 0;\n}\n\n.card.card-outline-tabs .card-tools {\n  margin: .5rem .5rem .3rem;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {\n  border-bottom: 0;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {\n  margin-bottom: 0;\n}\n\n.card.card-outline-tabs.expanding-card .card-header .nav-tabs .nav-item {\n  margin-bottom: -1px;\n}\n\nhtml.maximized-card {\n  overflow: hidden;\n}\n\n.card-header::after,\n.card-body::after,\n.card-footer::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.card-header {\n  background-color: transparent;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  padding: 0.75rem 1.25rem;\n  position: relative;\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n}\n\n.collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card-header > .card-tools {\n  float: right;\n  margin-right: -0.625rem;\n}\n\n.card-header > .card-tools .input-group,\n.card-header > .card-tools .nav,\n.card-header > .card-tools .pagination {\n  margin-bottom: -0.3rem;\n  margin-top: -0.3rem;\n}\n\n.card-header > .card-tools [data-toggle=\"tooltip\"] {\n  position: relative;\n}\n\n.card-title {\n  float: left;\n  font-size: 1.1rem;\n  font-weight: 400;\n  margin: 0;\n}\n\n.card-text {\n  clear: both;\n}\n\n.btn-tool {\n  background-color: transparent;\n  color: #adb5bd;\n  font-size: 0.875rem;\n  margin: -0.75rem 0;\n  padding: .25rem .5rem;\n}\n\n.btn-group.show .btn-tool, .btn-tool:hover {\n  color: #495057;\n}\n\n.show .btn-tool, .btn-tool:focus {\n  box-shadow: none !important;\n}\n\n.text-sm .card-title {\n  font-size: 1rem;\n}\n\n.text-sm .nav-link {\n  padding: 0.4rem 0.8rem;\n}\n\n.card-body > .table {\n  margin-bottom: 0;\n}\n\n.card-body > .table > thead > tr > th,\n.card-body > .table > thead > tr > td {\n  border-top-width: 0;\n}\n\n.card-body .fc {\n  margin-top: 5px;\n}\n\n.card-body .full-width-chart {\n  margin: -19px;\n}\n\n.card-body.p-0 .full-width-chart {\n  margin: -9px;\n}\n\n.chart-legend {\n  padding-left: 0;\n  list-style: none;\n  margin: 10px 0;\n}\n\n@media (max-width: 576px) {\n  .chart-legend > li {\n    float: left;\n    margin-right: 10px;\n  }\n}\n\n.card-comments {\n  background-color: #f8f9fa;\n}\n\n.card-comments .card-comment {\n  border-bottom: 1px solid #e9ecef;\n  padding: 8px 0;\n}\n\n.card-comments .card-comment::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.card-comments .card-comment:last-of-type {\n  border-bottom: 0;\n}\n\n.card-comments .card-comment:first-of-type {\n  padding-top: 0;\n}\n\n.card-comments .card-comment img {\n  height: 1.875rem;\n  width: 1.875rem;\n  float: left;\n}\n\n.card-comments .comment-text {\n  color: #78838e;\n  margin-left: 40px;\n}\n\n.card-comments .username {\n  color: #495057;\n  display: block;\n  font-weight: 600;\n}\n\n.card-comments .text-muted {\n  font-size: 12px;\n  font-weight: 400;\n}\n\n.todo-list {\n  list-style: none;\n  margin: 0;\n  overflow: auto;\n  padding: 0;\n}\n\n.todo-list > li {\n  border-radius: 2px;\n  background-color: #f8f9fa;\n  border-left: 2px solid #e9ecef;\n  color: #495057;\n  margin-bottom: 2px;\n  padding: 10px;\n}\n\n.todo-list > li:last-of-type {\n  margin-bottom: 0;\n}\n\n.todo-list > li > input[type=\"checkbox\"] {\n  margin: 0 10px 0 5px;\n}\n\n.todo-list > li .text {\n  display: inline-block;\n  font-weight: 600;\n  margin-left: 5px;\n}\n\n.todo-list > li .badge {\n  font-size: .7rem;\n  margin-left: 10px;\n}\n\n.todo-list > li .tools {\n  color: #dc3545;\n  display: none;\n  float: right;\n}\n\n.todo-list > li .tools > .fa,\n.todo-list > li .tools > .fas,\n.todo-list > li .tools > .far,\n.todo-list > li .tools > .fab,\n.todo-list > li .tools > .fal,\n.todo-list > li .tools > .fad,\n.todo-list > li .tools > .svg-inline--fa,\n.todo-list > li .tools > .ion {\n  cursor: pointer;\n  margin-right: 5px;\n}\n\n.todo-list > li:hover .tools {\n  display: inline-block;\n}\n\n.todo-list > li.done {\n  color: #697582;\n}\n\n.todo-list > li.done .text {\n  font-weight: 500;\n  text-decoration: line-through;\n}\n\n.todo-list > li.done .badge {\n  background-color: #adb5bd !important;\n}\n\n.todo-list .primary {\n  border-left-color: #007bff;\n}\n\n.todo-list .secondary {\n  border-left-color: #6c757d;\n}\n\n.todo-list .success {\n  border-left-color: #28a745;\n}\n\n.todo-list .info {\n  border-left-color: #17a2b8;\n}\n\n.todo-list .warning {\n  border-left-color: #ffc107;\n}\n\n.todo-list .danger {\n  border-left-color: #dc3545;\n}\n\n.todo-list .light {\n  border-left-color: #f8f9fa;\n}\n\n.todo-list .dark {\n  border-left-color: #343a40;\n}\n\n.todo-list .lightblue {\n  border-left-color: #3c8dbc;\n}\n\n.todo-list .navy {\n  border-left-color: #001f3f;\n}\n\n.todo-list .olive {\n  border-left-color: #3d9970;\n}\n\n.todo-list .lime {\n  border-left-color: #01ff70;\n}\n\n.todo-list .fuchsia {\n  border-left-color: #f012be;\n}\n\n.todo-list .maroon {\n  border-left-color: #d81b60;\n}\n\n.todo-list .blue {\n  border-left-color: #007bff;\n}\n\n.todo-list .indigo {\n  border-left-color: #6610f2;\n}\n\n.todo-list .purple {\n  border-left-color: #6f42c1;\n}\n\n.todo-list .pink {\n  border-left-color: #e83e8c;\n}\n\n.todo-list .red {\n  border-left-color: #dc3545;\n}\n\n.todo-list .orange {\n  border-left-color: #fd7e14;\n}\n\n.todo-list .yellow {\n  border-left-color: #ffc107;\n}\n\n.todo-list .green {\n  border-left-color: #28a745;\n}\n\n.todo-list .teal {\n  border-left-color: #20c997;\n}\n\n.todo-list .cyan {\n  border-left-color: #17a2b8;\n}\n\n.todo-list .white {\n  border-left-color: #fff;\n}\n\n.todo-list .gray {\n  border-left-color: #6c757d;\n}\n\n.todo-list .gray-dark {\n  border-left-color: #343a40;\n}\n\n.todo-list .handle {\n  cursor: move;\n  display: inline-block;\n  margin: 0 5px;\n}\n\n.card-input {\n  max-width: 200px;\n}\n\n.card-default .nav-item:first-child .nav-link {\n  border-left: 0;\n}\n\n.dark-mode .card-primary:not(.card-outline) > .card-header {\n  background-color: #3f6791;\n}\n\n.dark-mode .card-primary:not(.card-outline) > .card-header,\n.dark-mode .card-primary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-primary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-primary.card-outline {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .card-primary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-primary.card-outline-tabs > .card-header a.active,\n.dark-mode .card-primary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .bg-primary > .card-header .btn-tool,\n.dark-mode .bg-gradient-primary > .card-header .btn-tool,\n.dark-mode .card-primary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-primary > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-primary > .card-header .btn-tool:hover,\n.dark-mode .card-primary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #335375;\n  color: #fff;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5080b3;\n  color: #fff;\n}\n\n.dark-mode .card-secondary:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.dark-mode .card-secondary:not(.card-outline) > .card-header,\n.dark-mode .card-secondary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-secondary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-secondary.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .card-secondary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-secondary.card-outline-tabs > .card-header a.active,\n.dark-mode .card-secondary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .bg-secondary > .card-header .btn-tool,\n.dark-mode .bg-gradient-secondary > .card-header .btn-tool,\n.dark-mode .card-secondary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-secondary > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-secondary > .card-header .btn-tool:hover,\n.dark-mode .card-secondary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.dark-mode .card-success:not(.card-outline) > .card-header {\n  background-color: #00bc8c;\n}\n\n.dark-mode .card-success:not(.card-outline) > .card-header,\n.dark-mode .card-success:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-success:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-success.card-outline {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .card-success.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-success.card-outline-tabs > .card-header a.active,\n.dark-mode .card-success.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .bg-success > .card-header .btn-tool,\n.dark-mode .bg-gradient-success > .card-header .btn-tool,\n.dark-mode .card-success:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-success > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-success > .card-header .btn-tool:hover,\n.dark-mode .card-success:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #00936e;\n  color: #fff;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #00efb2;\n  color: #fff;\n}\n\n.dark-mode .card-info:not(.card-outline) > .card-header {\n  background-color: #3498db;\n}\n\n.dark-mode .card-info:not(.card-outline) > .card-header,\n.dark-mode .card-info:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-info:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-info.card-outline {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .card-info.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-info.card-outline-tabs > .card-header a.active,\n.dark-mode .card-info.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .bg-info > .card-header .btn-tool,\n.dark-mode .bg-gradient-info > .card-header .btn-tool,\n.dark-mode .card-info:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-info > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-info > .card-header .btn-tool:hover,\n.dark-mode .card-info:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #2383c4;\n  color: #fff;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5faee3;\n  color: #fff;\n}\n\n.dark-mode .card-warning:not(.card-outline) > .card-header {\n  background-color: #f39c12;\n}\n\n.dark-mode .card-warning:not(.card-outline) > .card-header,\n.dark-mode .card-warning:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-warning:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-warning.card-outline {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .card-warning.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-warning.card-outline-tabs > .card-header a.active,\n.dark-mode .card-warning.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .bg-warning > .card-header .btn-tool,\n.dark-mode .bg-gradient-warning > .card-header .btn-tool,\n.dark-mode .card-warning:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-warning > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-warning > .card-header .btn-tool:hover,\n.dark-mode .card-warning:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #d2850b;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f5b043;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-danger:not(.card-outline) > .card-header {\n  background-color: #e74c3c;\n}\n\n.dark-mode .card-danger:not(.card-outline) > .card-header,\n.dark-mode .card-danger:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-danger:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-danger.card-outline {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .card-danger.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-danger.card-outline-tabs > .card-header a.active,\n.dark-mode .card-danger.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .bg-danger > .card-header .btn-tool,\n.dark-mode .bg-gradient-danger > .card-header .btn-tool,\n.dark-mode .card-danger:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-danger > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-danger > .card-header .btn-tool:hover,\n.dark-mode .card-danger:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #df2e1b;\n  color: #fff;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed7669;\n  color: #fff;\n}\n\n.dark-mode .card-light:not(.card-outline) > .card-header {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .card-light:not(.card-outline) > .card-header,\n.dark-mode .card-light:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-light:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-light.card-outline {\n  border-top: 3px solid #f8f9fa;\n}\n\n.dark-mode .card-light.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-light.card-outline-tabs > .card-header a.active,\n.dark-mode .card-light.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f8f9fa;\n}\n\n.dark-mode .bg-light > .card-header .btn-tool,\n.dark-mode .bg-gradient-light > .card-header .btn-tool,\n.dark-mode .card-light:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-light > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-light > .card-header .btn-tool:hover,\n.dark-mode .card-light:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e0e5e9;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.dark-mode .card-dark:not(.card-outline) > .card-header,\n.dark-mode .card-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .card-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-dark.card-outline-tabs > .card-header a.active,\n.dark-mode .card-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .bg-dark > .card-header .btn-tool,\n.dark-mode .bg-gradient-dark > .card-header .btn-tool,\n.dark-mode .card-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-dark > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-dark > .card-header .btn-tool:hover,\n.dark-mode .card-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .card-lightblue:not(.card-outline) > .card-header {\n  background-color: #86bad8;\n}\n\n.dark-mode .card-lightblue:not(.card-outline) > .card-header,\n.dark-mode .card-lightblue:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lightblue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lightblue.card-outline {\n  border-top: 3px solid #86bad8;\n}\n\n.dark-mode .card-lightblue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-lightblue.card-outline-tabs > .card-header a.active,\n.dark-mode .card-lightblue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #86bad8;\n}\n\n.dark-mode .bg-lightblue > .card-header .btn-tool,\n.dark-mode .bg-gradient-lightblue > .card-header .btn-tool,\n.dark-mode .card-lightblue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-lightblue > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-lightblue > .card-header .btn-tool:hover,\n.dark-mode .card-lightblue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #67a8ce;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #acd0e5;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-navy:not(.card-outline) > .card-header {\n  background-color: #002c59;\n}\n\n.dark-mode .card-navy:not(.card-outline) > .card-header,\n.dark-mode .card-navy:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-navy:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-navy.card-outline {\n  border-top: 3px solid #002c59;\n}\n\n.dark-mode .card-navy.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-navy.card-outline-tabs > .card-header a.active,\n.dark-mode .card-navy.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #002c59;\n}\n\n.dark-mode .bg-navy > .card-header .btn-tool,\n.dark-mode .bg-gradient-navy > .card-header .btn-tool,\n.dark-mode .card-navy:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-navy > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-navy > .card-header .btn-tool:hover,\n.dark-mode .card-navy:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #001730;\n  color: #fff;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #00458c;\n  color: #fff;\n}\n\n.dark-mode .card-olive:not(.card-outline) > .card-header {\n  background-color: #74c8a3;\n}\n\n.dark-mode .card-olive:not(.card-outline) > .card-header,\n.dark-mode .card-olive:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-olive:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-olive.card-outline {\n  border-top: 3px solid #74c8a3;\n}\n\n.dark-mode .card-olive.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-olive.card-outline-tabs > .card-header a.active,\n.dark-mode .card-olive.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #74c8a3;\n}\n\n.dark-mode .bg-olive > .card-header .btn-tool,\n.dark-mode .bg-gradient-olive > .card-header .btn-tool,\n.dark-mode .card-olive:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-olive > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-olive > .card-header .btn-tool:hover,\n.dark-mode .card-olive:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #57bc8f;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #99d6bb;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lime:not(.card-outline) > .card-header {\n  background-color: #67ffa9;\n}\n\n.dark-mode .card-lime:not(.card-outline) > .card-header,\n.dark-mode .card-lime:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lime:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lime.card-outline {\n  border-top: 3px solid #67ffa9;\n}\n\n.dark-mode .card-lime.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-lime.card-outline-tabs > .card-header a.active,\n.dark-mode .card-lime.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #67ffa9;\n}\n\n.dark-mode .bg-lime > .card-header .btn-tool,\n.dark-mode .bg-gradient-lime > .card-header .btn-tool,\n.dark-mode .card-lime:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-lime > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-lime > .card-header .btn-tool:hover,\n.dark-mode .card-lime:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #3eff92;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #9affc6;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header {\n  background-color: #f672d8;\n}\n\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header,\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-fuchsia.card-outline {\n  border-top: 3px solid #f672d8;\n}\n\n.dark-mode .card-fuchsia.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-fuchsia.card-outline-tabs > .card-header a.active,\n.dark-mode .card-fuchsia.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f672d8;\n}\n\n.dark-mode .bg-fuchsia > .card-header .btn-tool,\n.dark-mode .bg-gradient-fuchsia > .card-header .btn-tool,\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-fuchsia > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-fuchsia > .card-header .btn-tool:hover,\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #f44cce;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f9a2e5;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-maroon:not(.card-outline) > .card-header {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .card-maroon:not(.card-outline) > .card-header,\n.dark-mode .card-maroon:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-maroon:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-maroon.card-outline {\n  border-top: 3px solid #ed6c9b;\n}\n\n.dark-mode .card-maroon.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-maroon.card-outline-tabs > .card-header a.active,\n.dark-mode .card-maroon.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ed6c9b;\n}\n\n.dark-mode .bg-maroon > .card-header .btn-tool,\n.dark-mode .bg-gradient-maroon > .card-header .btn-tool,\n.dark-mode .card-maroon:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-maroon > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-maroon > .card-header .btn-tool:hover,\n.dark-mode .card-maroon:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e84883;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f29aba;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-blue:not(.card-outline) > .card-header {\n  background-color: #3f6791;\n}\n\n.dark-mode .card-blue:not(.card-outline) > .card-header,\n.dark-mode .card-blue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-blue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-blue.card-outline {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .card-blue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-blue.card-outline-tabs > .card-header a.active,\n.dark-mode .card-blue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .bg-blue > .card-header .btn-tool,\n.dark-mode .bg-gradient-blue > .card-header .btn-tool,\n.dark-mode .card-blue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-blue > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-blue > .card-header .btn-tool:hover,\n.dark-mode .card-blue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #335375;\n  color: #fff;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5080b3;\n  color: #fff;\n}\n\n.dark-mode .card-indigo:not(.card-outline) > .card-header {\n  background-color: #6610f2;\n}\n\n.dark-mode .card-indigo:not(.card-outline) > .card-header,\n.dark-mode .card-indigo:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-indigo:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-indigo.card-outline {\n  border-top: 3px solid #6610f2;\n}\n\n.dark-mode .card-indigo.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-indigo.card-outline-tabs > .card-header a.active,\n.dark-mode .card-indigo.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6610f2;\n}\n\n.dark-mode .bg-indigo > .card-header .btn-tool,\n.dark-mode .bg-gradient-indigo > .card-header .btn-tool,\n.dark-mode .card-indigo:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-indigo > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-indigo > .card-header .btn-tool:hover,\n.dark-mode .card-indigo:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #550bce;\n  color: #fff;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8540f5;\n  color: #fff;\n}\n\n.dark-mode .card-purple:not(.card-outline) > .card-header {\n  background-color: #6f42c1;\n}\n\n.dark-mode .card-purple:not(.card-outline) > .card-header,\n.dark-mode .card-purple:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-purple:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-purple.card-outline {\n  border-top: 3px solid #6f42c1;\n}\n\n.dark-mode .card-purple.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-purple.card-outline-tabs > .card-header a.active,\n.dark-mode .card-purple.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6f42c1;\n}\n\n.dark-mode .bg-purple > .card-header .btn-tool,\n.dark-mode .bg-gradient-purple > .card-header .btn-tool,\n.dark-mode .card-purple:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-purple > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-purple > .card-header .btn-tool:hover,\n.dark-mode .card-purple:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #5d36a4;\n  color: #fff;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8c68ce;\n  color: #fff;\n}\n\n.dark-mode .card-pink:not(.card-outline) > .card-header {\n  background-color: #e83e8c;\n}\n\n.dark-mode .card-pink:not(.card-outline) > .card-header,\n.dark-mode .card-pink:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-pink:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-pink.card-outline {\n  border-top: 3px solid #e83e8c;\n}\n\n.dark-mode .card-pink.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-pink.card-outline-tabs > .card-header a.active,\n.dark-mode .card-pink.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e83e8c;\n}\n\n.dark-mode .bg-pink > .card-header .btn-tool,\n.dark-mode .bg-gradient-pink > .card-header .btn-tool,\n.dark-mode .card-pink:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-pink > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-pink > .card-header .btn-tool:hover,\n.dark-mode .card-pink:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e21b76;\n  color: #fff;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed6ca7;\n  color: #fff;\n}\n\n.dark-mode .card-red:not(.card-outline) > .card-header {\n  background-color: #e74c3c;\n}\n\n.dark-mode .card-red:not(.card-outline) > .card-header,\n.dark-mode .card-red:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-red:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-red.card-outline {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .card-red.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-red.card-outline-tabs > .card-header a.active,\n.dark-mode .card-red.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .bg-red > .card-header .btn-tool,\n.dark-mode .bg-gradient-red > .card-header .btn-tool,\n.dark-mode .card-red:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-red > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-red > .card-header .btn-tool:hover,\n.dark-mode .card-red:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #df2e1b;\n  color: #fff;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed7669;\n  color: #fff;\n}\n\n.dark-mode .card-orange:not(.card-outline) > .card-header {\n  background-color: #fd7e14;\n}\n\n.dark-mode .card-orange:not(.card-outline) > .card-header,\n.dark-mode .card-orange:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-orange:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-orange.card-outline {\n  border-top: 3px solid #fd7e14;\n}\n\n.dark-mode .card-orange.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-orange.card-outline-tabs > .card-header a.active,\n.dark-mode .card-orange.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fd7e14;\n}\n\n.dark-mode .bg-orange > .card-header .btn-tool,\n.dark-mode .bg-gradient-orange > .card-header .btn-tool,\n.dark-mode .card-orange:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-orange > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-orange > .card-header .btn-tool:hover,\n.dark-mode .card-orange:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e66a02;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #fd9a47;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-yellow:not(.card-outline) > .card-header {\n  background-color: #f39c12;\n}\n\n.dark-mode .card-yellow:not(.card-outline) > .card-header,\n.dark-mode .card-yellow:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-yellow:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-yellow.card-outline {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .card-yellow.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-yellow.card-outline-tabs > .card-header a.active,\n.dark-mode .card-yellow.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .bg-yellow > .card-header .btn-tool,\n.dark-mode .bg-gradient-yellow > .card-header .btn-tool,\n.dark-mode .card-yellow:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-yellow > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-yellow > .card-header .btn-tool:hover,\n.dark-mode .card-yellow:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #d2850b;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f5b043;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-green:not(.card-outline) > .card-header {\n  background-color: #00bc8c;\n}\n\n.dark-mode .card-green:not(.card-outline) > .card-header,\n.dark-mode .card-green:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-green:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-green.card-outline {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .card-green.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-green.card-outline-tabs > .card-header a.active,\n.dark-mode .card-green.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .bg-green > .card-header .btn-tool,\n.dark-mode .bg-gradient-green > .card-header .btn-tool,\n.dark-mode .card-green:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-green > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-green > .card-header .btn-tool:hover,\n.dark-mode .card-green:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #00936e;\n  color: #fff;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #00efb2;\n  color: #fff;\n}\n\n.dark-mode .card-teal:not(.card-outline) > .card-header {\n  background-color: #20c997;\n}\n\n.dark-mode .card-teal:not(.card-outline) > .card-header,\n.dark-mode .card-teal:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-teal:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-teal.card-outline {\n  border-top: 3px solid #20c997;\n}\n\n.dark-mode .card-teal.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-teal.card-outline-tabs > .card-header a.active,\n.dark-mode .card-teal.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #20c997;\n}\n\n.dark-mode .bg-teal > .card-header .btn-tool,\n.dark-mode .bg-gradient-teal > .card-header .btn-tool,\n.dark-mode .card-teal:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-teal > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-teal > .card-header .btn-tool:hover,\n.dark-mode .card-teal:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #1aa67d;\n  color: #fff;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3ce0af;\n  color: #fff;\n}\n\n.dark-mode .card-cyan:not(.card-outline) > .card-header {\n  background-color: #3498db;\n}\n\n.dark-mode .card-cyan:not(.card-outline) > .card-header,\n.dark-mode .card-cyan:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-cyan:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-cyan.card-outline {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .card-cyan.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-cyan.card-outline-tabs > .card-header a.active,\n.dark-mode .card-cyan.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .bg-cyan > .card-header .btn-tool,\n.dark-mode .bg-gradient-cyan > .card-header .btn-tool,\n.dark-mode .card-cyan:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-cyan > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-cyan > .card-header .btn-tool:hover,\n.dark-mode .card-cyan:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #2383c4;\n  color: #fff;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5faee3;\n  color: #fff;\n}\n\n.dark-mode .card-white:not(.card-outline) > .card-header {\n  background-color: #fff;\n}\n\n.dark-mode .card-white:not(.card-outline) > .card-header,\n.dark-mode .card-white:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-white:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-white.card-outline {\n  border-top: 3px solid #fff;\n}\n\n.dark-mode .card-white.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-white.card-outline-tabs > .card-header a.active,\n.dark-mode .card-white.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fff;\n}\n\n.dark-mode .bg-white > .card-header .btn-tool,\n.dark-mode .bg-gradient-white > .card-header .btn-tool,\n.dark-mode .card-white:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-white > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-white > .card-header .btn-tool:hover,\n.dark-mode .card-white:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #ebebeb;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-gray:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.dark-mode .card-gray:not(.card-outline) > .card-header,\n.dark-mode .card-gray:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-gray:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-gray.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .card-gray.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-gray.card-outline-tabs > .card-header a.active,\n.dark-mode .card-gray.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .bg-gray > .card-header .btn-tool,\n.dark-mode .bg-gradient-gray > .card-header .btn-tool,\n.dark-mode .card-gray:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-gray > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-gray > .card-header .btn-tool:hover,\n.dark-mode .card-gray:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header,\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-gray-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .card-gray-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-gray-dark.card-outline-tabs > .card-header a.active,\n.dark-mode .card-gray-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .bg-gray-dark > .card-header .btn-tool,\n.dark-mode .bg-gradient-gray-dark > .card-header .btn-tool,\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-gray-dark > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-gray-dark > .card-header .btn-tool:hover,\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .card {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .card .card {\n  background-color: #3f474e;\n  color: #fff;\n}\n\n.dark-mode .card .nav.flex-column > li {\n  border-bottom-color: #6c757d;\n}\n\n.dark-mode .card .card-footer {\n  background-color: rgba(0, 0, 0, 0.1);\n}\n\n.dark-mode .card.card-outline-tabs {\n  border-top: 0;\n}\n\n.dark-mode .card.card-outline-tabs .card-header a:hover {\n  border-top-color: #6c757d;\n  border-bottom-color: transparent;\n}\n\n.dark-mode .card:not(.card-outline) > .card-header a.active {\n  color: #fff;\n}\n\n.dark-mode .card-comments {\n  background-color: #373d44;\n}\n\n.dark-mode .card-comments .username {\n  color: #ced4da;\n}\n\n.dark-mode .card-comments .card-comment {\n  border-bottom-color: #454d55;\n}\n\n.dark-mode .todo-list > li {\n  background-color: #3f474e;\n  border-color: #454d55;\n  color: #fff;\n}\n\n.dark-mode .todo-list .primary {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .todo-list .secondary {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .todo-list .success {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .todo-list .info {\n  border-left-color: #3498db;\n}\n\n.dark-mode .todo-list .warning {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .todo-list .danger {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .todo-list .light {\n  border-left-color: #f8f9fa;\n}\n\n.dark-mode .todo-list .dark {\n  border-left-color: #343a40;\n}\n\n.dark-mode .todo-list .lightblue {\n  border-left-color: #86bad8;\n}\n\n.dark-mode .todo-list .navy {\n  border-left-color: #002c59;\n}\n\n.dark-mode .todo-list .olive {\n  border-left-color: #74c8a3;\n}\n\n.dark-mode .todo-list .lime {\n  border-left-color: #67ffa9;\n}\n\n.dark-mode .todo-list .fuchsia {\n  border-left-color: #f672d8;\n}\n\n.dark-mode .todo-list .maroon {\n  border-left-color: #ed6c9b;\n}\n\n.dark-mode .todo-list .blue {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .todo-list .indigo {\n  border-left-color: #6610f2;\n}\n\n.dark-mode .todo-list .purple {\n  border-left-color: #6f42c1;\n}\n\n.dark-mode .todo-list .pink {\n  border-left-color: #e83e8c;\n}\n\n.dark-mode .todo-list .red {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .todo-list .orange {\n  border-left-color: #fd7e14;\n}\n\n.dark-mode .todo-list .yellow {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .todo-list .green {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .todo-list .teal {\n  border-left-color: #20c997;\n}\n\n.dark-mode .todo-list .cyan {\n  border-left-color: #3498db;\n}\n\n.dark-mode .todo-list .white {\n  border-left-color: #fff;\n}\n\n.dark-mode .todo-list .gray {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .todo-list .gray-dark {\n  border-left-color: #343a40;\n}\n\n.modal-dialog .overlay {\n  display: -ms-flexbox;\n  display: flex;\n  position: absolute;\n  left: 0;\n  top: 0;\n  bottom: 0;\n  right: 0;\n  margin: -1px;\n  z-index: 1052;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: rgba(0, 0, 0, 0.7);\n  color: #666f76;\n  border-radius: 0.3rem;\n}\n\n.modal-content.bg-warning .modal-header,\n.modal-content.bg-warning .modal-footer {\n  border-color: #343a40;\n}\n\n.modal-content.bg-primary .close, .modal-content.bg-primary .mailbox-attachment-close, .modal-content.bg-secondary .close, .modal-content.bg-secondary .mailbox-attachment-close, .modal-content.bg-info .close, .modal-content.bg-info .mailbox-attachment-close, .modal-content.bg-danger .close, .modal-content.bg-danger .mailbox-attachment-close, .modal-content.bg-success .close, .modal-content.bg-success .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .modal-header,\n.dark-mode .modal-footer {\n  border-color: #6c757d;\n}\n\n.dark-mode .modal-content {\n  background-color: #343a40;\n}\n\n.dark-mode .modal-content.bg-warning .modal-header,\n.dark-mode .modal-content.bg-warning .modal-footer {\n  border-color: #6c757d;\n}\n\n.dark-mode .modal-content.bg-warning .close, .dark-mode .modal-content.bg-warning .mailbox-attachment-close {\n  color: #343a40 !important;\n  text-shadow: 0 1px 0 #495057 !important;\n}\n\n.dark-mode .modal-content.bg-primary .modal-header,\n.dark-mode .modal-content.bg-primary .modal-footer, .dark-mode .modal-content.bg-secondary .modal-header,\n.dark-mode .modal-content.bg-secondary .modal-footer, .dark-mode .modal-content.bg-info .modal-header,\n.dark-mode .modal-content.bg-info .modal-footer, .dark-mode .modal-content.bg-danger .modal-header,\n.dark-mode .modal-content.bg-danger .modal-footer, .dark-mode .modal-content.bg-success .modal-header,\n.dark-mode .modal-content.bg-success .modal-footer {\n  border-color: #fff;\n}\n\n.toasts-top-right {\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 1040;\n}\n\n.toasts-top-right.fixed {\n  position: fixed;\n}\n\n.toasts-top-left {\n  left: 0;\n  position: absolute;\n  top: 0;\n  z-index: 1040;\n}\n\n.toasts-top-left.fixed {\n  position: fixed;\n}\n\n.toasts-bottom-right {\n  bottom: 0;\n  position: absolute;\n  right: 0;\n  z-index: 1040;\n}\n\n.toasts-bottom-right.fixed {\n  position: fixed;\n}\n\n.toasts-bottom-left {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  z-index: 1040;\n}\n\n.toasts-bottom-left.fixed {\n  position: fixed;\n}\n\n.dark-mode .toast {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast .toast-header {\n  background-color: rgba(52, 58, 64, 0.7);\n  color: #f8f9fa;\n}\n\n.dark-mode .toast.bg-primary {\n  background-color: rgba(63, 103, 145, 0.9) !important;\n}\n\n.dark-mode .toast.bg-primary .close, .dark-mode .toast.bg-primary .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-primary .toast-header {\n  background-color: rgba(63, 103, 145, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-secondary {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.dark-mode .toast.bg-secondary .close, .dark-mode .toast.bg-secondary .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-secondary .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-success {\n  background-color: rgba(0, 188, 140, 0.9) !important;\n}\n\n.dark-mode .toast.bg-success .close, .dark-mode .toast.bg-success .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-success .toast-header {\n  background-color: rgba(0, 188, 140, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-info {\n  background-color: rgba(52, 152, 219, 0.9) !important;\n}\n\n.dark-mode .toast.bg-info .close, .dark-mode .toast.bg-info .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-info .toast-header {\n  background-color: rgba(52, 152, 219, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-warning {\n  background-color: rgba(243, 156, 18, 0.9) !important;\n}\n\n.dark-mode .toast.bg-warning .toast-header {\n  background-color: rgba(243, 156, 18, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-danger {\n  background-color: rgba(231, 76, 60, 0.9) !important;\n}\n\n.dark-mode .toast.bg-danger .close, .dark-mode .toast.bg-danger .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-danger .toast-header {\n  background-color: rgba(231, 76, 60, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-light {\n  background-color: rgba(248, 249, 250, 0.9) !important;\n}\n\n.dark-mode .toast.bg-light .toast-header {\n  background-color: rgba(248, 249, 250, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.dark-mode .toast.bg-dark .close, .dark-mode .toast.bg-dark .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-lightblue {\n  background-color: rgba(134, 186, 216, 0.9) !important;\n}\n\n.dark-mode .toast.bg-lightblue .toast-header {\n  background-color: rgba(134, 186, 216, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-navy {\n  background-color: rgba(0, 44, 89, 0.9) !important;\n}\n\n.dark-mode .toast.bg-navy .close, .dark-mode .toast.bg-navy .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-navy .toast-header {\n  background-color: rgba(0, 44, 89, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-olive {\n  background-color: rgba(116, 200, 163, 0.9) !important;\n}\n\n.dark-mode .toast.bg-olive .toast-header {\n  background-color: rgba(116, 200, 163, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-lime {\n  background-color: rgba(103, 255, 169, 0.9) !important;\n}\n\n.dark-mode .toast.bg-lime .toast-header {\n  background-color: rgba(103, 255, 169, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-fuchsia {\n  background-color: rgba(246, 114, 216, 0.9) !important;\n}\n\n.dark-mode .toast.bg-fuchsia .toast-header {\n  background-color: rgba(246, 114, 216, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-maroon {\n  background-color: rgba(237, 108, 155, 0.9) !important;\n}\n\n.dark-mode .toast.bg-maroon .toast-header {\n  background-color: rgba(237, 108, 155, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-blue {\n  background-color: rgba(63, 103, 145, 0.9) !important;\n}\n\n.dark-mode .toast.bg-blue .close, .dark-mode .toast.bg-blue .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-blue .toast-header {\n  background-color: rgba(63, 103, 145, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-indigo {\n  background-color: rgba(102, 16, 242, 0.9) !important;\n}\n\n.dark-mode .toast.bg-indigo .close, .dark-mode .toast.bg-indigo .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-indigo .toast-header {\n  background-color: rgba(102, 16, 242, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-purple {\n  background-color: rgba(111, 66, 193, 0.9) !important;\n}\n\n.dark-mode .toast.bg-purple .close, .dark-mode .toast.bg-purple .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-purple .toast-header {\n  background-color: rgba(111, 66, 193, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-pink {\n  background-color: rgba(232, 62, 140, 0.9) !important;\n}\n\n.dark-mode .toast.bg-pink .close, .dark-mode .toast.bg-pink .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-pink .toast-header {\n  background-color: rgba(232, 62, 140, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-red {\n  background-color: rgba(231, 76, 60, 0.9) !important;\n}\n\n.dark-mode .toast.bg-red .close, .dark-mode .toast.bg-red .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-red .toast-header {\n  background-color: rgba(231, 76, 60, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-orange {\n  background-color: rgba(253, 126, 20, 0.9) !important;\n}\n\n.dark-mode .toast.bg-orange .toast-header {\n  background-color: rgba(253, 126, 20, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-yellow {\n  background-color: rgba(243, 156, 18, 0.9) !important;\n}\n\n.dark-mode .toast.bg-yellow .toast-header {\n  background-color: rgba(243, 156, 18, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-green {\n  background-color: rgba(0, 188, 140, 0.9) !important;\n}\n\n.dark-mode .toast.bg-green .close, .dark-mode .toast.bg-green .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-green .toast-header {\n  background-color: rgba(0, 188, 140, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-teal {\n  background-color: rgba(32, 201, 151, 0.9) !important;\n}\n\n.dark-mode .toast.bg-teal .close, .dark-mode .toast.bg-teal .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-teal .toast-header {\n  background-color: rgba(32, 201, 151, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-cyan {\n  background-color: rgba(52, 152, 219, 0.9) !important;\n}\n\n.dark-mode .toast.bg-cyan .close, .dark-mode .toast.bg-cyan .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-cyan .toast-header {\n  background-color: rgba(52, 152, 219, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-white {\n  background-color: rgba(255, 255, 255, 0.9) !important;\n}\n\n.dark-mode .toast.bg-white .toast-header {\n  background-color: rgba(255, 255, 255, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-gray {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.dark-mode .toast.bg-gray .close, .dark-mode .toast.bg-gray .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-gray .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-gray-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.dark-mode .toast.bg-gray-dark .close, .dark-mode .toast.bg-gray-dark .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-gray-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.toast.bg-primary {\n  background-color: rgba(0, 123, 255, 0.9) !important;\n}\n\n.toast.bg-primary .close, .toast.bg-primary .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-primary .toast-header {\n  background-color: rgba(0, 123, 255, 0.85);\n  color: #fff;\n}\n\n.toast.bg-secondary {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.toast.bg-secondary .close, .toast.bg-secondary .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-secondary .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.toast.bg-success {\n  background-color: rgba(40, 167, 69, 0.9) !important;\n}\n\n.toast.bg-success .close, .toast.bg-success .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-success .toast-header {\n  background-color: rgba(40, 167, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-info {\n  background-color: rgba(23, 162, 184, 0.9) !important;\n}\n\n.toast.bg-info .close, .toast.bg-info .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-info .toast-header {\n  background-color: rgba(23, 162, 184, 0.85);\n  color: #fff;\n}\n\n.toast.bg-warning {\n  background-color: rgba(255, 193, 7, 0.9) !important;\n}\n\n.toast.bg-warning .toast-header {\n  background-color: rgba(255, 193, 7, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-danger {\n  background-color: rgba(220, 53, 69, 0.9) !important;\n}\n\n.toast.bg-danger .close, .toast.bg-danger .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-danger .toast-header {\n  background-color: rgba(220, 53, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-light {\n  background-color: rgba(248, 249, 250, 0.9) !important;\n}\n\n.toast.bg-light .toast-header {\n  background-color: rgba(248, 249, 250, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.toast.bg-dark .close, .toast.bg-dark .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.toast.bg-lightblue {\n  background-color: rgba(60, 141, 188, 0.9) !important;\n}\n\n.toast.bg-lightblue .close, .toast.bg-lightblue .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-lightblue .toast-header {\n  background-color: rgba(60, 141, 188, 0.85);\n  color: #fff;\n}\n\n.toast.bg-navy {\n  background-color: rgba(0, 31, 63, 0.9) !important;\n}\n\n.toast.bg-navy .close, .toast.bg-navy .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-navy .toast-header {\n  background-color: rgba(0, 31, 63, 0.85);\n  color: #fff;\n}\n\n.toast.bg-olive {\n  background-color: rgba(61, 153, 112, 0.9) !important;\n}\n\n.toast.bg-olive .close, .toast.bg-olive .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-olive .toast-header {\n  background-color: rgba(61, 153, 112, 0.85);\n  color: #fff;\n}\n\n.toast.bg-lime {\n  background-color: rgba(1, 255, 112, 0.9) !important;\n}\n\n.toast.bg-lime .toast-header {\n  background-color: rgba(1, 255, 112, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-fuchsia {\n  background-color: rgba(240, 18, 190, 0.9) !important;\n}\n\n.toast.bg-fuchsia .close, .toast.bg-fuchsia .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-fuchsia .toast-header {\n  background-color: rgba(240, 18, 190, 0.85);\n  color: #fff;\n}\n\n.toast.bg-maroon {\n  background-color: rgba(216, 27, 96, 0.9) !important;\n}\n\n.toast.bg-maroon .close, .toast.bg-maroon .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-maroon .toast-header {\n  background-color: rgba(216, 27, 96, 0.85);\n  color: #fff;\n}\n\n.toast.bg-blue {\n  background-color: rgba(0, 123, 255, 0.9) !important;\n}\n\n.toast.bg-blue .close, .toast.bg-blue .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-blue .toast-header {\n  background-color: rgba(0, 123, 255, 0.85);\n  color: #fff;\n}\n\n.toast.bg-indigo {\n  background-color: rgba(102, 16, 242, 0.9) !important;\n}\n\n.toast.bg-indigo .close, .toast.bg-indigo .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-indigo .toast-header {\n  background-color: rgba(102, 16, 242, 0.85);\n  color: #fff;\n}\n\n.toast.bg-purple {\n  background-color: rgba(111, 66, 193, 0.9) !important;\n}\n\n.toast.bg-purple .close, .toast.bg-purple .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-purple .toast-header {\n  background-color: rgba(111, 66, 193, 0.85);\n  color: #fff;\n}\n\n.toast.bg-pink {\n  background-color: rgba(232, 62, 140, 0.9) !important;\n}\n\n.toast.bg-pink .close, .toast.bg-pink .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-pink .toast-header {\n  background-color: rgba(232, 62, 140, 0.85);\n  color: #fff;\n}\n\n.toast.bg-red {\n  background-color: rgba(220, 53, 69, 0.9) !important;\n}\n\n.toast.bg-red .close, .toast.bg-red .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-red .toast-header {\n  background-color: rgba(220, 53, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-orange {\n  background-color: rgba(253, 126, 20, 0.9) !important;\n}\n\n.toast.bg-orange .toast-header {\n  background-color: rgba(253, 126, 20, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-yellow {\n  background-color: rgba(255, 193, 7, 0.9) !important;\n}\n\n.toast.bg-yellow .toast-header {\n  background-color: rgba(255, 193, 7, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-green {\n  background-color: rgba(40, 167, 69, 0.9) !important;\n}\n\n.toast.bg-green .close, .toast.bg-green .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-green .toast-header {\n  background-color: rgba(40, 167, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-teal {\n  background-color: rgba(32, 201, 151, 0.9) !important;\n}\n\n.toast.bg-teal .close, .toast.bg-teal .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-teal .toast-header {\n  background-color: rgba(32, 201, 151, 0.85);\n  color: #fff;\n}\n\n.toast.bg-cyan {\n  background-color: rgba(23, 162, 184, 0.9) !important;\n}\n\n.toast.bg-cyan .close, .toast.bg-cyan .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-cyan .toast-header {\n  background-color: rgba(23, 162, 184, 0.85);\n  color: #fff;\n}\n\n.toast.bg-white {\n  background-color: rgba(255, 255, 255, 0.9) !important;\n}\n\n.toast.bg-white .toast-header {\n  background-color: rgba(255, 255, 255, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-gray {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.toast.bg-gray .close, .toast.bg-gray .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-gray .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.toast.bg-gray-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.toast.bg-gray-dark .close, .toast.bg-gray-dark .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-gray-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.btn.disabled, .btn:disabled {\n  cursor: not-allowed;\n}\n\n.btn.btn-flat {\n  border-radius: 0;\n  border-width: 1px;\n  box-shadow: none;\n}\n\n.btn.btn-file {\n  overflow: hidden;\n  position: relative;\n}\n\n.btn.btn-file > input[type=\"file\"] {\n  background-color: #fff;\n  cursor: inherit;\n  display: block;\n  font-size: 100px;\n  min-height: 100%;\n  min-width: 100%;\n  opacity: 0;\n  outline: none;\n  position: absolute;\n  right: 0;\n  text-align: right;\n  top: 0;\n}\n\n.text-sm .btn {\n  font-size: 0.875rem !important;\n}\n\n.btn-default {\n  background-color: #f8f9fa;\n  border-color: #ddd;\n  color: #444;\n}\n\n.btn-default:hover, .btn-default:active, .btn-default.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.btn-default.disabled, .btn-default:disabled {\n  color: #444;\n  background-color: #f8f9fa;\n}\n\n.btn-outline-light {\n  color: #bdc6d0;\n  border-color: #bdc6d0;\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n  color: #bdc6d0;\n  border-color: #bdc6d0;\n}\n\n.btn-app {\n  border-radius: 3px;\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  color: #6c757d;\n  font-size: 12px;\n  height: 60px;\n  margin: 0 0 10px 10px;\n  min-width: 80px;\n  padding: 15px 5px;\n  position: relative;\n  text-align: center;\n}\n\n.btn-app > .fa,\n.btn-app > .fas,\n.btn-app > .far,\n.btn-app > .fab,\n.btn-app > .fal,\n.btn-app > .fad,\n.btn-app > .svg-inline--fa,\n.btn-app > .ion {\n  display: block;\n  font-size: 20px;\n}\n\n.btn-app > .svg-inline--fa {\n  margin: 0 auto;\n}\n\n.btn-app:hover {\n  background-color: #f8f9fa;\n  border-color: #aaaaaa;\n  color: #444;\n}\n\n.btn-app:active, .btn-app:focus {\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn-app > .badge {\n  font-size: 10px;\n  font-weight: 400;\n  position: absolute;\n  right: -10px;\n  top: -3px;\n}\n\n.btn-xs {\n  padding: 0.125rem 0.25rem;\n  font-size: 0.75rem;\n  line-height: 1.5;\n  border-radius: 0.15rem;\n}\n\n.dark-mode .btn-default,\n.dark-mode .btn-app {\n  background-color: #3a4047;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-default:hover, .dark-mode .btn-default:focus,\n.dark-mode .btn-app:hover,\n.dark-mode .btn-app:focus {\n  background-color: #3f474e;\n  color: #dee2e6;\n  border-color: #727b84;\n}\n\n.dark-mode .btn-light {\n  background-color: #454d55;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-light:hover, .dark-mode .btn-light:focus {\n  background-color: #4b545c;\n  color: #dee2e6;\n  border-color: #78828a;\n}\n\n.dark-mode .btn-primary {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n  box-shadow: none;\n}\n\n.dark-mode .btn-primary:hover {\n  color: #fff;\n  background-color: #335476;\n  border-color: #304e6d;\n}\n\n.dark-mode .btn-primary:focus, .dark-mode .btn-primary.focus {\n  color: #fff;\n  background-color: #335476;\n  border-color: #304e6d;\n  box-shadow: 0 0 0 0 rgba(92, 126, 162, 0.5);\n}\n\n.dark-mode .btn-primary.disabled, .dark-mode .btn-primary:disabled {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-primary:not(:disabled):not(.disabled):active, .dark-mode .btn-primary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #304e6d;\n  border-color: #2c4765;\n}\n\n.dark-mode .btn-primary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(92, 126, 162, 0.5);\n}\n\n.dark-mode .btn-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n  box-shadow: none;\n}\n\n.dark-mode .btn-secondary:hover {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n}\n\n.dark-mode .btn-secondary:focus, .dark-mode .btn-secondary.focus {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.dark-mode .btn-secondary.disabled, .dark-mode .btn-secondary:disabled {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-secondary:not(:disabled):not(.disabled):active, .dark-mode .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #545b62;\n  border-color: #4e555b;\n}\n\n.dark-mode .btn-secondary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.dark-mode .btn-success {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  box-shadow: none;\n}\n\n.dark-mode .btn-success:hover {\n  color: #fff;\n  background-color: #009670;\n  border-color: #008966;\n}\n\n.dark-mode .btn-success:focus, .dark-mode .btn-success.focus {\n  color: #fff;\n  background-color: #009670;\n  border-color: #008966;\n  box-shadow: 0 0 0 0 rgba(38, 198, 157, 0.5);\n}\n\n.dark-mode .btn-success.disabled, .dark-mode .btn-success:disabled {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-success:not(:disabled):not(.disabled):active, .dark-mode .btn-success:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-success.dropdown-toggle {\n  color: #fff;\n  background-color: #008966;\n  border-color: #007c5d;\n}\n\n.dark-mode .btn-success:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(38, 198, 157, 0.5);\n}\n\n.dark-mode .btn-info {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n  box-shadow: none;\n}\n\n.dark-mode .btn-info:hover {\n  color: #fff;\n  background-color: #2384c6;\n  border-color: #217dbb;\n}\n\n.dark-mode .btn-info:focus, .dark-mode .btn-info.focus {\n  color: #fff;\n  background-color: #2384c6;\n  border-color: #217dbb;\n  box-shadow: 0 0 0 0 rgba(82, 167, 224, 0.5);\n}\n\n.dark-mode .btn-info.disabled, .dark-mode .btn-info:disabled {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-info:not(:disabled):not(.disabled):active, .dark-mode .btn-info:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-info.dropdown-toggle {\n  color: #fff;\n  background-color: #217dbb;\n  border-color: #1f76b0;\n}\n\n.dark-mode .btn-info:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(82, 167, 224, 0.5);\n}\n\n.dark-mode .btn-warning {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n  box-shadow: none;\n}\n\n.dark-mode .btn-warning:hover {\n  color: #fff;\n  background-color: #d4860b;\n  border-color: #c87f0a;\n}\n\n.dark-mode .btn-warning:focus, .dark-mode .btn-warning.focus {\n  color: #fff;\n  background-color: #d4860b;\n  border-color: #c87f0a;\n  box-shadow: 0 0 0 0 rgba(211, 139, 24, 0.5);\n}\n\n.dark-mode .btn-warning.disabled, .dark-mode .btn-warning:disabled {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-warning:not(:disabled):not(.disabled):active, .dark-mode .btn-warning:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-warning.dropdown-toggle {\n  color: #fff;\n  background-color: #c87f0a;\n  border-color: #bc770a;\n}\n\n.dark-mode .btn-warning:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(211, 139, 24, 0.5);\n}\n\n.dark-mode .btn-danger {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  box-shadow: none;\n}\n\n.dark-mode .btn-danger:hover {\n  color: #fff;\n  background-color: #e12e1c;\n  border-color: #d62c1a;\n}\n\n.dark-mode .btn-danger:focus, .dark-mode .btn-danger.focus {\n  color: #fff;\n  background-color: #e12e1c;\n  border-color: #d62c1a;\n  box-shadow: 0 0 0 0 rgba(235, 103, 89, 0.5);\n}\n\n.dark-mode .btn-danger.disabled, .dark-mode .btn-danger:disabled {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-danger:not(:disabled):not(.disabled):active, .dark-mode .btn-danger:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #d62c1a;\n  border-color: #ca2a19;\n}\n\n.dark-mode .btn-danger:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(235, 103, 89, 0.5);\n}\n\n.dark-mode .btn-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  box-shadow: none;\n}\n\n.dark-mode .btn-light:hover {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n}\n\n.dark-mode .btn-light:focus, .dark-mode .btn-light.focus {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.dark-mode .btn-light.disabled, .dark-mode .btn-light:disabled {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-light:not(:disabled):not(.disabled):active, .dark-mode .btn-light:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n  border-color: #d3d9df;\n}\n\n.dark-mode .btn-light:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.dark-mode .btn-dark {\n  color: #fff;\n  background-color: #292d32;\n  border-color: #4b545c;\n  box-shadow: none;\n}\n\n.dark-mode .btn-dark:hover {\n  color: #fff;\n  background-color: #171a1d;\n  border-color: #343a40;\n}\n\n.dark-mode .btn-dark:focus, .dark-mode .btn-dark.focus {\n  color: #fff;\n  background-color: #171a1d;\n  border-color: #343a40;\n  box-shadow: 0 0 0 0 rgba(102, 109, 117, 0.5);\n}\n\n.dark-mode .btn-dark.disabled, .dark-mode .btn-dark:disabled {\n  color: #fff;\n  background-color: #292d32;\n  border-color: #4b545c;\n}\n\n.dark-mode .btn-dark:not(:disabled):not(.disabled):active, .dark-mode .btn-dark:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #121416;\n  border-color: #2e3439;\n}\n\n.dark-mode .btn-dark:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(102, 109, 117, 0.5);\n}\n\n.dark-mode .btn-outline-primary {\n  color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-outline-primary:hover {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-outline-primary:focus, .dark-mode .btn-outline-primary.focus {\n  box-shadow: 0 0 0 0 rgba(63, 103, 145, 0.5);\n}\n\n.dark-mode .btn-outline-primary.disabled, .dark-mode .btn-outline-primary:disabled {\n  color: #3f6791;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-primary:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(63, 103, 145, 0.5);\n}\n\n.dark-mode .btn-outline-secondary {\n  color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-outline-secondary:hover {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-outline-secondary:focus, .dark-mode .btn-outline-secondary.focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.dark-mode .btn-outline-secondary.disabled, .dark-mode .btn-outline-secondary:disabled {\n  color: #6c757d;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-secondary:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.dark-mode .btn-outline-success {\n  color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-outline-success:hover {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-outline-success:focus, .dark-mode .btn-outline-success.focus {\n  box-shadow: 0 0 0 0 rgba(0, 188, 140, 0.5);\n}\n\n.dark-mode .btn-outline-success.disabled, .dark-mode .btn-outline-success:disabled {\n  color: #00bc8c;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-success:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-success.dropdown-toggle {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-outline-success:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(0, 188, 140, 0.5);\n}\n\n.dark-mode .btn-outline-info {\n  color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-outline-info:hover {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-outline-info:focus, .dark-mode .btn-outline-info.focus {\n  box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.5);\n}\n\n.dark-mode .btn-outline-info.disabled, .dark-mode .btn-outline-info:disabled {\n  color: #3498db;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-info:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-info.dropdown-toggle {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-outline-info:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.5);\n}\n\n.dark-mode .btn-outline-warning {\n  color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-outline-warning:hover {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-outline-warning:focus, .dark-mode .btn-outline-warning.focus {\n  box-shadow: 0 0 0 0 rgba(243, 156, 18, 0.5);\n}\n\n.dark-mode .btn-outline-warning.disabled, .dark-mode .btn-outline-warning:disabled {\n  color: #f39c12;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-warning:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(243, 156, 18, 0.5);\n}\n\n.dark-mode .btn-outline-danger {\n  color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-outline-danger:hover {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-outline-danger:focus, .dark-mode .btn-outline-danger.focus {\n  box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.5);\n}\n\n.dark-mode .btn-outline-danger.disabled, .dark-mode .btn-outline-danger:disabled {\n  color: #e74c3c;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-danger:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.5);\n}\n\n.dark-mode .btn-outline-light {\n  color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-outline-light:hover {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-outline-light:focus, .dark-mode .btn-outline-light.focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.dark-mode .btn-outline-light.disabled, .dark-mode .btn-outline-light:disabled {\n  color: #f8f9fa;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-light:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-outline-light:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.dark-mode .btn-outline-dark {\n  color: #060708;\n  border-color: #060708;\n}\n\n.dark-mode .btn-outline-dark:hover {\n  color: #fff;\n  background-color: #060708;\n  border-color: #060708;\n}\n\n.dark-mode .btn-outline-dark:focus, .dark-mode .btn-outline-dark.focus {\n  box-shadow: 0 0 0 0 rgba(6, 7, 8, 0.5);\n}\n\n.dark-mode .btn-outline-dark.disabled, .dark-mode .btn-outline-dark:disabled {\n  color: #060708;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-dark:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #060708;\n  border-color: #060708;\n}\n\n.dark-mode .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(6, 7, 8, 0.5);\n}\n\n.callout {\n  border-radius: 0.25rem;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n  background-color: #fff;\n  border-left: 5px solid #e9ecef;\n  margin-bottom: 1rem;\n  padding: 1rem;\n}\n\n.callout a {\n  color: #495057;\n  text-decoration: underline;\n}\n\n.callout a:hover {\n  color: #e9ecef;\n}\n\n.callout p:last-child {\n  margin-bottom: 0;\n}\n\n.callout.callout-danger {\n  border-left-color: #bd2130;\n}\n\n.callout.callout-warning {\n  border-left-color: #d39e00;\n}\n\n.callout.callout-info {\n  border-left-color: #117a8b;\n}\n\n.callout.callout-success {\n  border-left-color: #1e7e34;\n}\n\n.dark-mode .callout {\n  background-color: #3f474e;\n}\n\n.dark-mode .callout.callout-danger {\n  border-left-color: #ed7669;\n}\n\n.dark-mode .callout.callout-warning {\n  border-left-color: #f5b043;\n}\n\n.dark-mode .callout.callout-info {\n  border-left-color: #5faee3;\n}\n\n.dark-mode .callout.callout-success {\n  border-left-color: #00efb2;\n}\n\n.alert .icon {\n  margin-right: 10px;\n}\n\n.alert .close, .alert .mailbox-attachment-close {\n  color: #000;\n  opacity: .2;\n}\n\n.alert .close:hover, .alert .mailbox-attachment-close:hover {\n  opacity: .5;\n}\n\n.alert a {\n  color: #fff;\n  text-decoration: underline;\n}\n\n.alert-primary {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #006fe6;\n}\n\n.alert-default-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.alert-default-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.alert-default-primary .alert-link {\n  color: #002752;\n}\n\n.alert-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #60686f;\n}\n\n.alert-default-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.alert-default-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.alert-default-secondary .alert-link {\n  color: #202326;\n}\n\n.alert-success {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #23923d;\n}\n\n.alert-default-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.alert-default-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.alert-default-success .alert-link {\n  color: #0b2e13;\n}\n\n.alert-info {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #148ea1;\n}\n\n.alert-default-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.alert-default-info hr {\n  border-top-color: #abdde5;\n}\n\n.alert-default-info .alert-link {\n  color: #062c33;\n}\n\n.alert-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #edb100;\n}\n\n.alert-default-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.alert-default-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.alert-default-warning .alert-link {\n  color: #533f03;\n}\n\n.alert-danger {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #d32535;\n}\n\n.alert-default-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.alert-default-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.alert-default-danger .alert-link {\n  color: #491217;\n}\n\n.alert-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n}\n\n.alert-default-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.alert-default-light hr {\n  border-top-color: #ececf6;\n}\n\n.alert-default-light .alert-link {\n  color: #686868;\n}\n\n.alert-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #292d32;\n}\n\n.alert-default-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.alert-default-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.alert-default-dark .alert-link {\n  color: #040505;\n}\n\n.dark-mode .alert-primary {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #375a7f;\n}\n\n.dark-mode .alert-default-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.dark-mode .alert-default-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.dark-mode .alert-default-primary .alert-link {\n  color: #002752;\n}\n\n.dark-mode .alert-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #60686f;\n}\n\n.dark-mode .alert-default-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.dark-mode .alert-default-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.dark-mode .alert-default-secondary .alert-link {\n  color: #202326;\n}\n\n.dark-mode .alert-success {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00a379;\n}\n\n.dark-mode .alert-default-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.dark-mode .alert-default-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.dark-mode .alert-default-success .alert-link {\n  color: #0b2e13;\n}\n\n.dark-mode .alert-info {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #258cd1;\n}\n\n.dark-mode .alert-default-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.dark-mode .alert-default-info hr {\n  border-top-color: #abdde5;\n}\n\n.dark-mode .alert-default-info .alert-link {\n  color: #062c33;\n}\n\n.dark-mode .alert-warning {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #e08e0b;\n}\n\n.dark-mode .alert-default-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.dark-mode .alert-default-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.dark-mode .alert-default-warning .alert-link {\n  color: #533f03;\n}\n\n.dark-mode .alert-danger {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e43725;\n}\n\n.dark-mode .alert-default-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.dark-mode .alert-default-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.dark-mode .alert-default-danger .alert-link {\n  color: #491217;\n}\n\n.dark-mode .alert-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n}\n\n.dark-mode .alert-default-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.dark-mode .alert-default-light hr {\n  border-top-color: #ececf6;\n}\n\n.dark-mode .alert-default-light .alert-link {\n  color: #686868;\n}\n\n.dark-mode .alert-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #292d32;\n}\n\n.dark-mode .alert-default-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.dark-mode .alert-default-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.dark-mode .alert-default-dark .alert-link {\n  color: #040505;\n}\n\n.table:not(.table-dark) {\n  color: inherit;\n}\n\n.table.table-head-fixed thead tr:nth-child(1) th {\n  background-color: #fff;\n  border-bottom: 0;\n  box-shadow: inset 0 1px 0 #dee2e6, inset 0 -1px 0 #dee2e6;\n  position: -webkit-sticky;\n  position: sticky;\n  top: 0;\n  z-index: 10;\n}\n\n.table.table-head-fixed.table-dark thead tr:nth-child(1) th {\n  background-color: #212529;\n  box-shadow: inset 0 1px 0 #383f45, inset 0 -1px 0 #383f45;\n}\n\n.table.no-border,\n.table.no-border td,\n.table.no-border th {\n  border: 0;\n}\n\n.table.text-center,\n.table.text-center td,\n.table.text-center th {\n  text-align: center;\n}\n\n.table.table-valign-middle thead > tr > th,\n.table.table-valign-middle thead > tr > td,\n.table.table-valign-middle tbody > tr > th,\n.table.table-valign-middle tbody > tr > td {\n  vertical-align: middle;\n}\n\n.card-body.p-0 .table thead > tr > th:first-of-type,\n.card-body.p-0 .table thead > tr > td:first-of-type,\n.card-body.p-0 .table tfoot > tr > th:first-of-type,\n.card-body.p-0 .table tfoot > tr > td:first-of-type,\n.card-body.p-0 .table tbody > tr > th:first-of-type,\n.card-body.p-0 .table tbody > tr > td:first-of-type {\n  padding-left: 1.5rem;\n}\n\n.card-body.p-0 .table thead > tr > th:last-of-type,\n.card-body.p-0 .table thead > tr > td:last-of-type,\n.card-body.p-0 .table tfoot > tr > th:last-of-type,\n.card-body.p-0 .table tfoot > tr > td:last-of-type,\n.card-body.p-0 .table tbody > tr > th:last-of-type,\n.card-body.p-0 .table tbody > tr > td:last-of-type {\n  padding-right: 1.5rem;\n}\n\n.table-hover tbody tr.expandable-body:hover {\n  background-color: inherit !important;\n}\n\n[data-widget=\"expandable-table\"] {\n  cursor: pointer;\n}\n\n[data-widget=\"expandable-table\"] i.expandable-table-caret {\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n[data-widget=\"expandable-table\"][aria-expanded=\"true\"] i.expandable-table-caret[class*=\"right\"] {\n  -webkit-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n[data-widget=\"expandable-table\"][aria-expanded=\"true\"] i.expandable-table-caret[class*=\"left\"] {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n[aria-expanded=\"true\"] {\n  cursor: pointer;\n}\n\n[aria-expanded=\"true\"] i.expandable-table-caret {\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n[aria-expanded=\"true\"] [data-widget=\"expandable-table\"] i.expandable-table-caret[class*=\"right\"] {\n  -webkit-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n[aria-expanded=\"true\"] [data-widget=\"expandable-table\"] i.expandable-table-caret[class*=\"left\"] {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n.expandable-body > td {\n  padding: 0 !important;\n  width: 100%;\n}\n\n.expandable-body > td > div,\n.expandable-body > td > p {\n  padding: 0.75rem;\n}\n\n.expandable-body .table {\n  width: calc(100% - 0.75rem);\n  margin: 0 0 0 0.75rem;\n}\n\n.expandable-body .table tr:first-child td,\n.expandable-body .table tr:first-child th {\n  border-top: none;\n}\n\n.dark-mode .table-bordered,\n.dark-mode .table-bordered td,\n.dark-mode .table-bordered th {\n  border-color: #6c757d;\n}\n\n.dark-mode .table-hover tbody tr:hover {\n  color: #dee2e6;\n  background-color: #3a4047;\n  border-color: #6c757d;\n}\n\n.dark-mode .table thead th {\n  border-bottom-color: #6c757d;\n}\n\n.dark-mode .table th,\n.dark-mode .table td {\n  border-top-color: #6c757d;\n}\n\n.dark-mode .table.table-head-fixed thead tr:nth-child(1) th {\n  background-color: #3f474e;\n}\n\n.carousel-control-prev .carousel-control-custom-icon {\n  margin-left: -20px;\n}\n\n.carousel-control-next .carousel-control-custom-icon {\n  margin-right: 20px;\n}\n\n.carousel-control-custom-icon > .fa,\n.carousel-control-custom-icon > .fas,\n.carousel-control-custom-icon > .far,\n.carousel-control-custom-icon > .fab,\n.carousel-control-custom-icon > .fal,\n.carousel-control-custom-icon > .fad,\n.carousel-control-custom-icon > .svg-inline--fa,\n.carousel-control-custom-icon > .ion {\n  display: inline-block;\n  font-size: 40px;\n  margin-top: -20px;\n  position: absolute;\n  top: 50%;\n  z-index: 5;\n}\n\n.close, .mailbox-attachment-close {\n  float: right;\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: .5;\n}\n\n.close:hover, .mailbox-attachment-close:hover {\n  color: #000;\n  text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .mailbox-attachment-close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus, .mailbox-attachment-close:not(:disabled):not(.disabled):focus {\n  opacity: .75;\n}\n\n.close:focus, .mailbox-attachment-close:focus {\n  outline: none;\n}\n\nbutton.close, button.mailbox-attachment-close {\n  padding: 0;\n  background-color: transparent;\n  border: 0;\n}\n\na.close.disabled, a.disabled.mailbox-attachment-close {\n  pointer-events: none;\n}\n\n.small-box {\n  border-radius: 0.25rem;\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  display: block;\n  margin-bottom: 20px;\n  position: relative;\n}\n\n.small-box > .inner {\n  padding: 10px;\n}\n\n.small-box > .small-box-footer {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: rgba(255, 255, 255, 0.8);\n  display: block;\n  padding: 3px 0;\n  position: relative;\n  text-align: center;\n  text-decoration: none;\n  z-index: 10;\n}\n\n.small-box > .small-box-footer:hover {\n  background-color: rgba(0, 0, 0, 0.15);\n  color: #fff;\n}\n\n.small-box h3 {\n  font-size: 2.2rem;\n  font-weight: 700;\n  margin: 0 0 10px;\n  padding: 0;\n  white-space: nowrap;\n}\n\n@media (min-width: 992px) {\n  .col-xl-2 .small-box h3,\n  .col-lg-2 .small-box h3,\n  .col-md-2 .small-box h3 {\n    font-size: 1.6rem;\n  }\n  .col-xl-3 .small-box h3,\n  .col-lg-3 .small-box h3,\n  .col-md-3 .small-box h3 {\n    font-size: 1.6rem;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl-2 .small-box h3,\n  .col-lg-2 .small-box h3,\n  .col-md-2 .small-box h3 {\n    font-size: 2.2rem;\n  }\n  .col-xl-3 .small-box h3,\n  .col-lg-3 .small-box h3,\n  .col-md-3 .small-box h3 {\n    font-size: 2.2rem;\n  }\n}\n\n.small-box p {\n  font-size: 1rem;\n}\n\n.small-box p > small {\n  color: #f8f9fa;\n  display: block;\n  font-size: .9rem;\n  margin-top: 5px;\n}\n\n.small-box h3,\n.small-box p {\n  z-index: 5;\n}\n\n.small-box .icon {\n  color: rgba(0, 0, 0, 0.15);\n  z-index: 0;\n}\n\n.small-box .icon > i {\n  font-size: 90px;\n  position: absolute;\n  right: 15px;\n  top: 15px;\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n.small-box .icon > i.fa, .small-box .icon > i.fas, .small-box .icon > i.far, .small-box .icon > i.fab, .small-box .icon > i.fal, .small-box .icon > i.fad, .small-box .icon > i.ion {\n  font-size: 70px;\n  top: 20px;\n}\n\n.small-box .icon svg {\n  font-size: 70px;\n  position: absolute;\n  right: 15px;\n  top: 15px;\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n.small-box:hover {\n  text-decoration: none;\n}\n\n.small-box:hover .icon > i, .small-box:hover .icon > i.fa, .small-box:hover .icon > i.fas, .small-box:hover .icon > i.far, .small-box:hover .icon > i.fab, .small-box:hover .icon > i.fal, .small-box:hover .icon > i.fad, .small-box:hover .icon > i.ion {\n  -webkit-transform: scale(1.1);\n  transform: scale(1.1);\n}\n\n.small-box:hover .icon > svg {\n  -webkit-transform: scale(1.1);\n  transform: scale(1.1);\n}\n\n@media (max-width: 767.98px) {\n  .small-box {\n    text-align: center;\n  }\n  .small-box .icon {\n    display: none;\n  }\n  .small-box p {\n    font-size: 12px;\n  }\n}\n\n.info-box {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  display: -ms-flexbox;\n  display: flex;\n  margin-bottom: 1rem;\n  min-height: 80px;\n  padding: .5rem;\n  position: relative;\n  width: 100%;\n}\n\n.info-box .progress {\n  background-color: rgba(0, 0, 0, 0.125);\n  height: 2px;\n  margin: 5px 0;\n}\n\n.info-box .progress .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box-icon {\n  border-radius: 0.25rem;\n  -ms-flex-align: center;\n  align-items: center;\n  display: -ms-flexbox;\n  display: flex;\n  font-size: 1.875rem;\n  -ms-flex-pack: center;\n  justify-content: center;\n  text-align: center;\n  width: 70px;\n}\n\n.info-box .info-box-icon > img {\n  max-width: 100%;\n}\n\n.info-box .info-box-content {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  line-height: 1.8;\n  -ms-flex: 1;\n  flex: 1;\n  padding: 0 10px;\n  overflow: hidden;\n}\n\n.info-box .info-box-number {\n  display: block;\n  margin-top: .25rem;\n  font-weight: 700;\n}\n\n.info-box .progress-description,\n.info-box .info-box-text {\n  display: block;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.info-box .info-box .bg-primary,\n.info-box .info-box .bg-gradient-primary {\n  color: #fff;\n}\n\n.info-box .info-box .bg-primary .progress-bar,\n.info-box .info-box .bg-gradient-primary .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-secondary,\n.info-box .info-box .bg-gradient-secondary {\n  color: #fff;\n}\n\n.info-box .info-box .bg-secondary .progress-bar,\n.info-box .info-box .bg-gradient-secondary .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-success,\n.info-box .info-box .bg-gradient-success {\n  color: #fff;\n}\n\n.info-box .info-box .bg-success .progress-bar,\n.info-box .info-box .bg-gradient-success .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-info,\n.info-box .info-box .bg-gradient-info {\n  color: #fff;\n}\n\n.info-box .info-box .bg-info .progress-bar,\n.info-box .info-box .bg-gradient-info .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-warning,\n.info-box .info-box .bg-gradient-warning {\n  color: #1f2d3d;\n}\n\n.info-box .info-box .bg-warning .progress-bar,\n.info-box .info-box .bg-gradient-warning .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.info-box .info-box .bg-danger,\n.info-box .info-box .bg-gradient-danger {\n  color: #fff;\n}\n\n.info-box .info-box .bg-danger .progress-bar,\n.info-box .info-box .bg-gradient-danger .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-light,\n.info-box .info-box .bg-gradient-light {\n  color: #1f2d3d;\n}\n\n.info-box .info-box .bg-light .progress-bar,\n.info-box .info-box .bg-gradient-light .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.info-box .info-box .bg-dark,\n.info-box .info-box .bg-gradient-dark {\n  color: #fff;\n}\n\n.info-box .info-box .bg-dark .progress-bar,\n.info-box .info-box .bg-gradient-dark .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box-more {\n  display: block;\n}\n\n.info-box .progress-description {\n  margin: 0;\n}\n\n@media (min-width: 768px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    display: none;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    display: none;\n  }\n}\n\n@media (min-width: 992px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    font-size: 0.75rem;\n    display: block;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    font-size: 0.75rem;\n    display: block;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    font-size: 1rem;\n    display: block;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    font-size: 1rem;\n    display: block;\n  }\n}\n\n.dark-mode .info-box {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-primary,\n.dark-mode .info-box .info-box .bg-gradient-primary {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-primary .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-primary .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-secondary,\n.dark-mode .info-box .info-box .bg-gradient-secondary {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-secondary .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-secondary .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-success,\n.dark-mode .info-box .info-box .bg-gradient-success {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-success .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-success .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-info,\n.dark-mode .info-box .info-box .bg-gradient-info {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-info .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-info .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-warning,\n.dark-mode .info-box .info-box .bg-gradient-warning {\n  color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-warning .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-warning .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-danger,\n.dark-mode .info-box .info-box .bg-gradient-danger {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-danger .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-danger .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-light,\n.dark-mode .info-box .info-box .bg-gradient-light {\n  color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-light .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-light .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-dark,\n.dark-mode .info-box .info-box .bg-gradient-dark {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-dark .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-dark .progress-bar {\n  background-color: #fff;\n}\n\n.timeline {\n  margin: 0 0 45px;\n  padding: 0;\n  position: relative;\n}\n\n.timeline::before {\n  border-radius: 0.25rem;\n  background-color: #dee2e6;\n  bottom: 0;\n  content: \"\";\n  left: 31px;\n  margin: 0;\n  position: absolute;\n  top: 0;\n  width: 4px;\n}\n\n.timeline > div {\n  margin-bottom: 15px;\n  margin-right: 10px;\n  position: relative;\n}\n\n.timeline > div::before, .timeline > div::after {\n  content: \"\";\n  display: table;\n}\n\n.timeline > div > .timeline-item {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  color: #495057;\n  margin-left: 60px;\n  margin-right: 15px;\n  margin-top: 0;\n  padding: 0;\n  position: relative;\n}\n\n.timeline > div > .timeline-item > .time {\n  color: #999;\n  float: right;\n  font-size: 12px;\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-header {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  color: #495057;\n  font-size: 16px;\n  line-height: 1.1;\n  margin: 0;\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-header > a {\n  font-weight: 600;\n}\n\n.timeline > div > .timeline-item > .timeline-body,\n.timeline > div > .timeline-item > .timeline-footer {\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-body > img {\n  margin: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-body > dl,\n.timeline > div > .timeline-item > .timeline-body ol,\n.timeline > div > .timeline-item > .timeline-body ul {\n  margin: 0;\n}\n\n.timeline > div > .timeline-item > .timeline-footer > a {\n  color: #fff;\n}\n\n.timeline > div > .fa,\n.timeline > div > .fas,\n.timeline > div > .far,\n.timeline > div > .fab,\n.timeline > div > .fal,\n.timeline > div > .fad,\n.timeline > div > .svg-inline--fa,\n.timeline > div > .ion {\n  background-color: #adb5bd;\n  border-radius: 50%;\n  font-size: 16px;\n  height: 30px;\n  left: 18px;\n  line-height: 30px;\n  position: absolute;\n  text-align: center;\n  top: 0;\n  width: 30px;\n}\n\n.timeline > div > .svg-inline--fa {\n  padding: 7px;\n}\n\n.timeline > .time-label > span {\n  border-radius: 4px;\n  background-color: #fff;\n  display: inline-block;\n  font-weight: 600;\n  padding: 5px;\n}\n\n.timeline-inverse > div > .timeline-item {\n  box-shadow: none;\n  background-color: #f8f9fa;\n  border: 1px solid #dee2e6;\n}\n\n.timeline-inverse > div > .timeline-item > .timeline-header {\n  border-bottom-color: #dee2e6;\n}\n\n.dark-mode .timeline::before {\n  background-color: #6c757d;\n}\n\n.dark-mode .timeline > div > .timeline-item {\n  background-color: #343a40;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .timeline > div > .timeline-item > .timeline-header {\n  color: #ced4da;\n  border-color: #6c757d;\n}\n\n.dark-mode .timeline > div > .timeline-item > .time {\n  color: #ced4da;\n}\n\n.products-list {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n.products-list > .item {\n  border-radius: 0.25rem;\n  background-color: #fff;\n  padding: 10px 0;\n}\n\n.products-list > .item::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.products-list .product-img {\n  float: left;\n}\n\n.products-list .product-img img {\n  height: 50px;\n  width: 50px;\n}\n\n.products-list .product-info {\n  margin-left: 60px;\n}\n\n.products-list .product-title {\n  font-weight: 600;\n}\n\n.products-list .product-description {\n  color: #6c757d;\n  display: block;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.product-list-in-card > .item {\n  border-radius: 0;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.product-list-in-card > .item:last-of-type {\n  border-bottom-width: 0;\n}\n\n.dark-mode .products-list > .item {\n  background-color: #343a40;\n  color: #fff;\n  border-bottom-color: #6c757d;\n}\n\n.dark-mode .product-description {\n  color: #ced4da;\n}\n\n.direct-chat .card-body {\n  overflow-x: hidden;\n  padding: 0;\n  position: relative;\n}\n\n.direct-chat.chat-pane-open .direct-chat-contacts {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n\n.direct-chat.timestamp-light .direct-chat-timestamp {\n  color: #30465f;\n}\n\n.direct-chat.timestamp-dark .direct-chat-timestamp {\n  color: #cccccc;\n}\n\n.direct-chat-messages {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n  height: 250px;\n  overflow: auto;\n  padding: 10px;\n}\n\n.direct-chat-msg,\n.direct-chat-text {\n  display: block;\n}\n\n.direct-chat-msg {\n  margin-bottom: 10px;\n}\n\n.direct-chat-msg::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.direct-chat-messages,\n.direct-chat-contacts {\n  transition: -webkit-transform .5s ease-in-out;\n  transition: transform .5s ease-in-out;\n  transition: transform .5s ease-in-out, -webkit-transform .5s ease-in-out;\n}\n\n.direct-chat-text {\n  border-radius: 0.3rem;\n  background-color: #d2d6de;\n  border: 1px solid #d2d6de;\n  color: #444;\n  margin: 5px 0 0 50px;\n  padding: 5px 10px;\n  position: relative;\n}\n\n.direct-chat-text::after, .direct-chat-text::before {\n  border: solid transparent;\n  border-right-color: #d2d6de;\n  content: \" \";\n  height: 0;\n  pointer-events: none;\n  position: absolute;\n  right: 100%;\n  top: 15px;\n  width: 0;\n}\n\n.direct-chat-text::after {\n  border-width: 5px;\n  margin-top: -5px;\n}\n\n.direct-chat-text::before {\n  border-width: 6px;\n  margin-top: -6px;\n}\n\n.right .direct-chat-text {\n  margin-left: 0;\n  margin-right: 50px;\n}\n\n.right .direct-chat-text::after, .right .direct-chat-text::before {\n  border-left-color: #d2d6de;\n  border-right-color: transparent;\n  left: 100%;\n  right: auto;\n}\n\n.direct-chat-img {\n  border-radius: 50%;\n  float: left;\n  height: 40px;\n  width: 40px;\n}\n\n.right .direct-chat-img {\n  float: right;\n}\n\n.direct-chat-infos {\n  display: block;\n  font-size: 0.875rem;\n  margin-bottom: 2px;\n}\n\n.direct-chat-name {\n  font-weight: 600;\n}\n\n.direct-chat-timestamp {\n  color: #697582;\n}\n\n.direct-chat-contacts-open .direct-chat-contacts {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n\n.direct-chat-contacts {\n  -webkit-transform: translate(101%, 0);\n  transform: translate(101%, 0);\n  background-color: #343a40;\n  bottom: 0;\n  color: #fff;\n  height: 250px;\n  overflow: auto;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.direct-chat-contacts-light {\n  background-color: #f8f9fa;\n}\n\n.direct-chat-contacts-light .contacts-list-name {\n  color: #495057;\n}\n\n.direct-chat-contacts-light .contacts-list-date {\n  color: #6c757d;\n}\n\n.direct-chat-contacts-light .contacts-list-msg {\n  color: #545b62;\n}\n\n.contacts-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.contacts-list > li {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n  margin: 0;\n  padding: 10px;\n}\n\n.contacts-list > li::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.contacts-list > li:last-of-type {\n  border-bottom: 0;\n}\n\n.contacts-list-img {\n  border-radius: 50%;\n  float: left;\n  width: 40px;\n}\n\n.contacts-list-info {\n  color: #fff;\n  margin-left: 45px;\n}\n\n.contacts-list-name,\n.contacts-list-status {\n  display: block;\n}\n\n.contacts-list-name {\n  font-weight: 600;\n}\n\n.contacts-list-status {\n  font-size: 0.875rem;\n}\n\n.contacts-list-date {\n  color: #ced4da;\n  font-weight: 400;\n}\n\n.contacts-list-msg {\n  color: #b1bbc4;\n}\n\n.direct-chat-primary .right > .direct-chat-text {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.direct-chat-primary .right > .direct-chat-text::after, .direct-chat-primary .right > .direct-chat-text::before {\n  border-left-color: #007bff;\n}\n\n.direct-chat-secondary .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.direct-chat-secondary .right > .direct-chat-text::after, .direct-chat-secondary .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.direct-chat-success .right > .direct-chat-text {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.direct-chat-success .right > .direct-chat-text::after, .direct-chat-success .right > .direct-chat-text::before {\n  border-left-color: #28a745;\n}\n\n.direct-chat-info .right > .direct-chat-text {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.direct-chat-info .right > .direct-chat-text::after, .direct-chat-info .right > .direct-chat-text::before {\n  border-left-color: #17a2b8;\n}\n\n.direct-chat-warning .right > .direct-chat-text {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.direct-chat-warning .right > .direct-chat-text::after, .direct-chat-warning .right > .direct-chat-text::before {\n  border-left-color: #ffc107;\n}\n\n.direct-chat-danger .right > .direct-chat-text {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.direct-chat-danger .right > .direct-chat-text::after, .direct-chat-danger .right > .direct-chat-text::before {\n  border-left-color: #dc3545;\n}\n\n.direct-chat-light .right > .direct-chat-text {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.direct-chat-light .right > .direct-chat-text::after, .direct-chat-light .right > .direct-chat-text::before {\n  border-left-color: #f8f9fa;\n}\n\n.direct-chat-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.direct-chat-dark .right > .direct-chat-text::after, .direct-chat-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.direct-chat-lightblue .right > .direct-chat-text {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.direct-chat-lightblue .right > .direct-chat-text::after, .direct-chat-lightblue .right > .direct-chat-text::before {\n  border-left-color: #3c8dbc;\n}\n\n.direct-chat-navy .right > .direct-chat-text {\n  background-color: #001f3f;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.direct-chat-navy .right > .direct-chat-text::after, .direct-chat-navy .right > .direct-chat-text::before {\n  border-left-color: #001f3f;\n}\n\n.direct-chat-olive .right > .direct-chat-text {\n  background-color: #3d9970;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.direct-chat-olive .right > .direct-chat-text::after, .direct-chat-olive .right > .direct-chat-text::before {\n  border-left-color: #3d9970;\n}\n\n.direct-chat-lime .right > .direct-chat-text {\n  background-color: #01ff70;\n  border-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.direct-chat-lime .right > .direct-chat-text::after, .direct-chat-lime .right > .direct-chat-text::before {\n  border-left-color: #01ff70;\n}\n\n.direct-chat-fuchsia .right > .direct-chat-text {\n  background-color: #f012be;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.direct-chat-fuchsia .right > .direct-chat-text::after, .direct-chat-fuchsia .right > .direct-chat-text::before {\n  border-left-color: #f012be;\n}\n\n.direct-chat-maroon .right > .direct-chat-text {\n  background-color: #d81b60;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.direct-chat-maroon .right > .direct-chat-text::after, .direct-chat-maroon .right > .direct-chat-text::before {\n  border-left-color: #d81b60;\n}\n\n.direct-chat-blue .right > .direct-chat-text {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.direct-chat-blue .right > .direct-chat-text::after, .direct-chat-blue .right > .direct-chat-text::before {\n  border-left-color: #007bff;\n}\n\n.direct-chat-indigo .right > .direct-chat-text {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.direct-chat-indigo .right > .direct-chat-text::after, .direct-chat-indigo .right > .direct-chat-text::before {\n  border-left-color: #6610f2;\n}\n\n.direct-chat-purple .right > .direct-chat-text {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.direct-chat-purple .right > .direct-chat-text::after, .direct-chat-purple .right > .direct-chat-text::before {\n  border-left-color: #6f42c1;\n}\n\n.direct-chat-pink .right > .direct-chat-text {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.direct-chat-pink .right > .direct-chat-text::after, .direct-chat-pink .right > .direct-chat-text::before {\n  border-left-color: #e83e8c;\n}\n\n.direct-chat-red .right > .direct-chat-text {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.direct-chat-red .right > .direct-chat-text::after, .direct-chat-red .right > .direct-chat-text::before {\n  border-left-color: #dc3545;\n}\n\n.direct-chat-orange .right > .direct-chat-text {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.direct-chat-orange .right > .direct-chat-text::after, .direct-chat-orange .right > .direct-chat-text::before {\n  border-left-color: #fd7e14;\n}\n\n.direct-chat-yellow .right > .direct-chat-text {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.direct-chat-yellow .right > .direct-chat-text::after, .direct-chat-yellow .right > .direct-chat-text::before {\n  border-left-color: #ffc107;\n}\n\n.direct-chat-green .right > .direct-chat-text {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.direct-chat-green .right > .direct-chat-text::after, .direct-chat-green .right > .direct-chat-text::before {\n  border-left-color: #28a745;\n}\n\n.direct-chat-teal .right > .direct-chat-text {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.direct-chat-teal .right > .direct-chat-text::after, .direct-chat-teal .right > .direct-chat-text::before {\n  border-left-color: #20c997;\n}\n\n.direct-chat-cyan .right > .direct-chat-text {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.direct-chat-cyan .right > .direct-chat-text::after, .direct-chat-cyan .right > .direct-chat-text::before {\n  border-left-color: #17a2b8;\n}\n\n.direct-chat-white .right > .direct-chat-text {\n  background-color: #fff;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.direct-chat-white .right > .direct-chat-text::after, .direct-chat-white .right > .direct-chat-text::before {\n  border-left-color: #fff;\n}\n\n.direct-chat-gray .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.direct-chat-gray .right > .direct-chat-text::after, .direct-chat-gray .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.direct-chat-gray-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.direct-chat-gray-dark .right > .direct-chat-text::after, .direct-chat-gray-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.dark-mode .direct-chat-text {\n  background-color: #454d55;\n  border-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-text::after, .dark-mode .direct-chat-text::before {\n  border-right-color: #4b545c;\n}\n\n.dark-mode .direct-chat-timestamp {\n  color: #adb5bd;\n}\n\n.dark-mode .right > .direct-chat-text::after, .dark-mode .right > .direct-chat-text::before {\n  border-right-color: transparent;\n}\n\n.dark-mode .direct-chat-primary .right > .direct-chat-text {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-primary .right > .direct-chat-text::after, .dark-mode .direct-chat-primary .right > .direct-chat-text::before {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .direct-chat-secondary .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-secondary .right > .direct-chat-text::after, .dark-mode .direct-chat-secondary .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .direct-chat-success .right > .direct-chat-text {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-success .right > .direct-chat-text::after, .dark-mode .direct-chat-success .right > .direct-chat-text::before {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .direct-chat-info .right > .direct-chat-text {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-info .right > .direct-chat-text::after, .dark-mode .direct-chat-info .right > .direct-chat-text::before {\n  border-left-color: #3498db;\n}\n\n.dark-mode .direct-chat-warning .right > .direct-chat-text {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-warning .right > .direct-chat-text::after, .dark-mode .direct-chat-warning .right > .direct-chat-text::before {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .direct-chat-danger .right > .direct-chat-text {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-danger .right > .direct-chat-text::after, .dark-mode .direct-chat-danger .right > .direct-chat-text::before {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .direct-chat-light .right > .direct-chat-text {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-light .right > .direct-chat-text::after, .dark-mode .direct-chat-light .right > .direct-chat-text::before {\n  border-left-color: #f8f9fa;\n}\n\n.dark-mode .direct-chat-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-dark .right > .direct-chat-text::after, .dark-mode .direct-chat-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.dark-mode .direct-chat-lightblue .right > .direct-chat-text {\n  background-color: #86bad8;\n  border-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-lightblue .right > .direct-chat-text::after, .dark-mode .direct-chat-lightblue .right > .direct-chat-text::before {\n  border-left-color: #86bad8;\n}\n\n.dark-mode .direct-chat-navy .right > .direct-chat-text {\n  background-color: #002c59;\n  border-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-navy .right > .direct-chat-text::after, .dark-mode .direct-chat-navy .right > .direct-chat-text::before {\n  border-left-color: #002c59;\n}\n\n.dark-mode .direct-chat-olive .right > .direct-chat-text {\n  background-color: #74c8a3;\n  border-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-olive .right > .direct-chat-text::after, .dark-mode .direct-chat-olive .right > .direct-chat-text::before {\n  border-left-color: #74c8a3;\n}\n\n.dark-mode .direct-chat-lime .right > .direct-chat-text {\n  background-color: #67ffa9;\n  border-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-lime .right > .direct-chat-text::after, .dark-mode .direct-chat-lime .right > .direct-chat-text::before {\n  border-left-color: #67ffa9;\n}\n\n.dark-mode .direct-chat-fuchsia .right > .direct-chat-text {\n  background-color: #f672d8;\n  border-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-fuchsia .right > .direct-chat-text::after, .dark-mode .direct-chat-fuchsia .right > .direct-chat-text::before {\n  border-left-color: #f672d8;\n}\n\n.dark-mode .direct-chat-maroon .right > .direct-chat-text {\n  background-color: #ed6c9b;\n  border-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-maroon .right > .direct-chat-text::after, .dark-mode .direct-chat-maroon .right > .direct-chat-text::before {\n  border-left-color: #ed6c9b;\n}\n\n.dark-mode .direct-chat-blue .right > .direct-chat-text {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-blue .right > .direct-chat-text::after, .dark-mode .direct-chat-blue .right > .direct-chat-text::before {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .direct-chat-indigo .right > .direct-chat-text {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-indigo .right > .direct-chat-text::after, .dark-mode .direct-chat-indigo .right > .direct-chat-text::before {\n  border-left-color: #6610f2;\n}\n\n.dark-mode .direct-chat-purple .right > .direct-chat-text {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-purple .right > .direct-chat-text::after, .dark-mode .direct-chat-purple .right > .direct-chat-text::before {\n  border-left-color: #6f42c1;\n}\n\n.dark-mode .direct-chat-pink .right > .direct-chat-text {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-pink .right > .direct-chat-text::after, .dark-mode .direct-chat-pink .right > .direct-chat-text::before {\n  border-left-color: #e83e8c;\n}\n\n.dark-mode .direct-chat-red .right > .direct-chat-text {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-red .right > .direct-chat-text::after, .dark-mode .direct-chat-red .right > .direct-chat-text::before {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .direct-chat-orange .right > .direct-chat-text {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-orange .right > .direct-chat-text::after, .dark-mode .direct-chat-orange .right > .direct-chat-text::before {\n  border-left-color: #fd7e14;\n}\n\n.dark-mode .direct-chat-yellow .right > .direct-chat-text {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-yellow .right > .direct-chat-text::after, .dark-mode .direct-chat-yellow .right > .direct-chat-text::before {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .direct-chat-green .right > .direct-chat-text {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-green .right > .direct-chat-text::after, .dark-mode .direct-chat-green .right > .direct-chat-text::before {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .direct-chat-teal .right > .direct-chat-text {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-teal .right > .direct-chat-text::after, .dark-mode .direct-chat-teal .right > .direct-chat-text::before {\n  border-left-color: #20c997;\n}\n\n.dark-mode .direct-chat-cyan .right > .direct-chat-text {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-cyan .right > .direct-chat-text::after, .dark-mode .direct-chat-cyan .right > .direct-chat-text::before {\n  border-left-color: #3498db;\n}\n\n.dark-mode .direct-chat-white .right > .direct-chat-text {\n  background-color: #fff;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-white .right > .direct-chat-text::after, .dark-mode .direct-chat-white .right > .direct-chat-text::before {\n  border-left-color: #fff;\n}\n\n.dark-mode .direct-chat-gray .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-gray .right > .direct-chat-text::after, .dark-mode .direct-chat-gray .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .direct-chat-gray-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-gray-dark .right > .direct-chat-text::after, .dark-mode .direct-chat-gray-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.users-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.users-list > li {\n  float: left;\n  padding: 10px;\n  text-align: center;\n  width: 25%;\n}\n\n.users-list > li img {\n  border-radius: 50%;\n  height: auto;\n  max-width: 100%;\n}\n\n.users-list > li > a:hover,\n.users-list > li > a:hover .users-list-name {\n  color: #999;\n}\n\n.users-list-name,\n.users-list-date {\n  display: block;\n}\n\n.users-list-name {\n  color: #495057;\n  font-size: 0.875rem;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.users-list-date {\n  color: #748290;\n  font-size: 12px;\n}\n\n.dark-mode .users-list-name {\n  color: #ced4da;\n}\n\n.dark-mode .users-list-date {\n  color: #adb5bd;\n}\n\n.card-widget {\n  border: 0;\n  position: relative;\n}\n\n.widget-user .widget-user-header {\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n  height: 135px;\n  padding: 1rem;\n  text-align: center;\n}\n\n.widget-user .widget-user-username {\n  font-size: 25px;\n  font-weight: 300;\n  margin-bottom: 0;\n  margin-top: 0;\n  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);\n}\n\n.widget-user .widget-user-desc {\n  margin-top: 0;\n}\n\n.widget-user .widget-user-image {\n  left: 50%;\n  margin-left: -45px;\n  position: absolute;\n  top: 80px;\n}\n\n.widget-user .widget-user-image > img {\n  border: 3px solid #fff;\n  height: auto;\n  width: 90px;\n}\n\n.widget-user .card-footer {\n  padding-top: 50px;\n}\n\n.widget-user-2 .widget-user-header {\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n  padding: 1rem;\n}\n\n.widget-user-2 .widget-user-username {\n  font-size: 25px;\n  font-weight: 300;\n  margin-bottom: 5px;\n  margin-top: 5px;\n}\n\n.widget-user-2 .widget-user-desc {\n  margin-top: 0;\n}\n\n.widget-user-2 .widget-user-username,\n.widget-user-2 .widget-user-desc {\n  margin-left: 75px;\n}\n\n.widget-user-2 .widget-user-image > img {\n  float: left;\n  height: auto;\n  width: 65px;\n}\n\n.mailbox-messages > .table {\n  margin: 0;\n}\n\n.mailbox-controls {\n  padding: 5px;\n}\n\n.mailbox-controls.with-border {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.mailbox-read-info {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  padding: 10px;\n}\n\n.mailbox-read-info h3 {\n  font-size: 20px;\n  margin: 0;\n}\n\n.mailbox-read-info h5 {\n  margin: 0;\n  padding: 5px 0 0;\n}\n\n.mailbox-read-time {\n  color: #999;\n  font-size: 13px;\n}\n\n.mailbox-read-message {\n  padding: 10px;\n}\n\n.mailbox-attachments {\n  padding-left: 0;\n  list-style: none;\n}\n\n.mailbox-attachments li {\n  border: 1px solid #eee;\n  float: left;\n  margin-bottom: 10px;\n  margin-right: 10px;\n  width: 200px;\n}\n\n.mailbox-attachment-name {\n  color: #666;\n  font-weight: 700;\n}\n\n.mailbox-attachment-icon,\n.mailbox-attachment-info,\n.mailbox-attachment-size {\n  display: block;\n}\n\n.mailbox-attachment-info {\n  background-color: #f8f9fa;\n  padding: 10px;\n}\n\n.mailbox-attachment-size {\n  color: #999;\n  font-size: 12px;\n}\n\n.mailbox-attachment-size > span {\n  display: inline-block;\n  padding-top: .75rem;\n}\n\n.mailbox-attachment-icon {\n  color: #666;\n  font-size: 65px;\n  max-height: 132.5px;\n  padding: 20px 10px;\n  text-align: center;\n}\n\n.mailbox-attachment-icon.has-img {\n  padding: 0;\n}\n\n.mailbox-attachment-icon.has-img > img {\n  height: auto;\n  max-width: 100%;\n}\n\n.lockscreen {\n  background-color: #e9ecef;\n}\n\n.lockscreen .lockscreen-name {\n  font-weight: 600;\n  text-align: center;\n}\n\n.lockscreen-logo {\n  font-size: 35px;\n  font-weight: 300;\n  margin-bottom: 25px;\n  text-align: center;\n}\n\n.lockscreen-logo a {\n  color: #495057;\n}\n\n.lockscreen-wrapper {\n  margin: 0 auto;\n  margin-top: 10%;\n  max-width: 400px;\n}\n\n.lockscreen-item {\n  border-radius: 4px;\n  background-color: #fff;\n  margin: 10px auto 30px;\n  padding: 0;\n  position: relative;\n  width: 290px;\n}\n\n.lockscreen-image {\n  border-radius: 50%;\n  background-color: #fff;\n  left: -10px;\n  padding: 5px;\n  position: absolute;\n  top: -25px;\n  z-index: 10;\n}\n\n.lockscreen-image > img {\n  border-radius: 50%;\n  height: 70px;\n  width: 70px;\n}\n\n.lockscreen-credentials {\n  margin-left: 70px;\n}\n\n.lockscreen-credentials .form-control {\n  border: 0;\n}\n\n.lockscreen-credentials .btn {\n  background-color: #fff;\n  border: 0;\n  padding: 0 10px;\n}\n\n.lockscreen-footer {\n  margin-top: 10px;\n}\n\n.dark-mode .lockscreen-item {\n  background-color: #343a40;\n}\n\n.dark-mode .lockscreen-logo a {\n  color: #fff;\n}\n\n.dark-mode .lockscreen-credentials .btn {\n  background-color: #343a40;\n}\n\n.dark-mode .lockscreen-image {\n  background-color: #6c757d;\n}\n\n.login-logo,\n.register-logo {\n  font-size: 2.1rem;\n  font-weight: 300;\n  margin-bottom: .9rem;\n  text-align: center;\n}\n\n.login-logo a,\n.register-logo a {\n  color: #495057;\n}\n\n.login-page,\n.register-page {\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: #e9ecef;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  height: 100vh;\n  -ms-flex-pack: center;\n  justify-content: center;\n}\n\n.login-box,\n.register-box {\n  width: 360px;\n}\n\n@media (max-width: 576px) {\n  .login-box,\n  .register-box {\n    margin-top: .5rem;\n    width: 90%;\n  }\n}\n\n.login-box .card,\n.register-box .card {\n  margin-bottom: 0;\n}\n\n.login-card-body,\n.register-card-body {\n  background-color: #fff;\n  border-top: 0;\n  color: #666;\n  padding: 20px;\n}\n\n.login-card-body .input-group .form-control,\n.register-card-body .input-group .form-control {\n  border-right: 0;\n}\n\n.login-card-body .input-group .form-control:focus,\n.register-card-body .input-group .form-control:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,\n.login-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,\n.register-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text {\n  border-color: #80bdff;\n}\n\n.login-card-body .input-group .form-control.is-valid:focus,\n.register-card-body .input-group .form-control.is-valid:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,\n.login-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,\n.register-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text {\n  border-color: #28a745;\n}\n\n.login-card-body .input-group .form-control.is-invalid:focus,\n.register-card-body .input-group .form-control.is-invalid:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text {\n  border-color: #dc3545;\n}\n\n.login-card-body .input-group .input-group-text,\n.register-card-body .input-group .input-group-text {\n  background-color: transparent;\n  border-bottom-right-radius: 0.25rem;\n  border-left: 0;\n  border-top-right-radius: 0.25rem;\n  color: #777;\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.login-box-msg,\n.register-box-msg {\n  margin: 0;\n  padding: 0 20px 20px;\n  text-align: center;\n}\n\n.social-auth-links {\n  margin: 10px 0;\n}\n\n.dark-mode .login-card-body,\n.dark-mode .register-card-body {\n  background-color: #343a40;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .login-logo a,\n.dark-mode .register-logo a {\n  color: #fff;\n}\n\n.error-page {\n  margin: 20px auto 0;\n  width: 600px;\n}\n\n@media (max-width: 767.98px) {\n  .error-page {\n    width: 100%;\n  }\n}\n\n.error-page > .headline {\n  float: left;\n  font-size: 100px;\n  font-weight: 300;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .headline {\n    float: none;\n    text-align: center;\n  }\n}\n\n.error-page > .error-content {\n  display: block;\n  margin-left: 190px;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .error-content {\n    margin-left: 0;\n  }\n}\n\n.error-page > .error-content > h3 {\n  font-size: 25px;\n  font-weight: 300;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .error-content > h3 {\n    text-align: center;\n  }\n}\n\n.invoice {\n  background-color: #fff;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n  position: relative;\n}\n\n.invoice-title {\n  margin-top: 0;\n}\n\n.dark-mode .invoice {\n  background-color: #343a40;\n}\n\n.profile-user-img {\n  border: 3px solid #adb5bd;\n  margin: 0 auto;\n  padding: 3px;\n  width: 100px;\n}\n\n.profile-username {\n  font-size: 21px;\n  margin-top: 5px;\n}\n\n.post {\n  border-bottom: 1px solid #adb5bd;\n  color: #666;\n  margin-bottom: 15px;\n  padding-bottom: 15px;\n}\n\n.post:last-of-type {\n  border-bottom: 0;\n  margin-bottom: 0;\n  padding-bottom: 0;\n}\n\n.post .user-block {\n  margin-bottom: 15px;\n  width: 100%;\n}\n\n.post .row {\n  width: 100%;\n}\n\n.dark-mode .post {\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.product-image {\n  max-width: 100%;\n  height: auto;\n  width: 100%;\n}\n\n.product-image-thumbs {\n  -ms-flex-align: stretch;\n  align-items: stretch;\n  display: -ms-flexbox;\n  display: flex;\n  margin-top: 2rem;\n}\n\n.product-image-thumb {\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n  display: -ms-flexbox;\n  display: flex;\n  margin-right: 1rem;\n  max-width: 7rem;\n  padding: 0.5rem;\n}\n\n.product-image-thumb img {\n  max-width: 100%;\n  height: auto;\n  -ms-flex-item-align: center;\n  align-self: center;\n}\n\n.product-image-thumb:hover {\n  opacity: .5;\n}\n\n.product-share a {\n  margin-right: .5rem;\n}\n\n.projects td {\n  vertical-align: middle;\n}\n\n.projects .list-inline {\n  margin-bottom: 0;\n}\n\n.projects img.table-avatar,\n.projects .table-avatar img {\n  border-radius: 50%;\n  display: inline;\n  width: 2.5rem;\n}\n\n.projects .project-state {\n  text-align: center;\n}\n\nbody.iframe-mode .main-sidebar {\n  display: none;\n}\n\nbody.iframe-mode .content-wrapper {\n  margin-left: 0 !important;\n  margin-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\nbody.iframe-mode .main-header,\nbody.iframe-mode .main-footer {\n  display: none;\n}\n\nbody.iframe-mode-fullscreen {\n  overflow: hidden;\n}\n\nbody.iframe-mode-fullscreen.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: 0 !important;\n}\n\n.content-wrapper {\n  height: 100%;\n}\n\n.content-wrapper.iframe-mode .btn-iframe-close {\n  color: #dc3545;\n  position: absolute;\n  line-height: 1;\n  right: .125rem;\n  top: .125rem;\n  z-index: 10;\n  visibility: hidden;\n}\n\n.content-wrapper.iframe-mode .btn-iframe-close:hover, .content-wrapper.iframe-mode .btn-iframe-close:focus {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n@media (hover: none) and (pointer: coarse) {\n  .content-wrapper.iframe-mode .btn-iframe-close {\n    visibility: visible;\n  }\n}\n\n.content-wrapper.iframe-mode .navbar-nav {\n  overflow-y: auto;\n  width: 100%;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-link {\n  white-space: nowrap;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-item {\n  position: relative;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-item:hover .btn-iframe-close, .content-wrapper.iframe-mode .navbar-nav .nav-item:focus .btn-iframe-close {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n@media (hover: none) and (pointer: coarse) {\n  .content-wrapper.iframe-mode .navbar-nav .nav-item:hover .btn-iframe-close, .content-wrapper.iframe-mode .navbar-nav .nav-item:focus .btn-iframe-close {\n    visibility: visible;\n  }\n}\n\n.content-wrapper.iframe-mode .tab-content {\n  position: relative;\n}\n\n.content-wrapper.iframe-mode .tab-pane + .tab-empty {\n  display: none;\n}\n\n.content-wrapper.iframe-mode .tab-empty {\n  width: 100%;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.content-wrapper.iframe-mode .tab-loading {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  display: none;\n  background-color: #f4f6f9;\n}\n\n.content-wrapper.iframe-mode .tab-loading > div {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n  width: 100%;\n  height: 100%;\n}\n\n.content-wrapper.iframe-mode iframe {\n  border: 0;\n  width: 100%;\n  height: 100%;\n  margin-bottom: -8px;\n}\n\n.content-wrapper.iframe-mode iframe .content-wrapper {\n  padding-bottom: 0 !important;\n}\n\nbody.iframe-mode-fullscreen .content-wrapper.iframe-mode {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  margin-left: 0 !important;\n  height: 100%;\n  min-height: 100%;\n  z-index: 1048;\n}\n\n.permanent-btn-iframe-close .btn-iframe-close {\n  -webkit-animation: none !important;\n  animation: none !important;\n  visibility: visible !important;\n  opacity: 1;\n}\n\n.dark-mode .content-wrapper.iframe-mode .tab-loading {\n  background-color: #343a40;\n}\n\n.content-wrapper.kanban {\n  height: 1px;\n}\n\n.content-wrapper.kanban .content {\n  height: 100%;\n  overflow-x: auto;\n  overflow-y: hidden;\n}\n\n.content-wrapper.kanban .content .container,\n.content-wrapper.kanban .content .container-fluid,\n.content-wrapper.kanban .content .container-sm,\n.content-wrapper.kanban .content .container-md,\n.content-wrapper.kanban .content .container-lg,\n.content-wrapper.kanban .content .container-xl {\n  width: -webkit-max-content;\n  width: -moz-max-content;\n  width: max-content;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: stretch;\n  align-items: stretch;\n}\n\n.content-wrapper.kanban .content-header + .content {\n  height: calc(100% - ((2 * 15px) + (1.8rem * 1.2)));\n}\n\n.content-wrapper.kanban .card .card-body {\n  padding: .5rem;\n}\n\n.content-wrapper.kanban .card.card-row {\n  width: 340px;\n  display: inline-block;\n  margin: 0 .5rem;\n}\n\n.content-wrapper.kanban .card.card-row:first-child {\n  margin-left: 0;\n}\n\n.content-wrapper.kanban .card.card-row .card-body {\n  height: calc(100% - (12px + (1.8rem * 1.2) + .5rem));\n  overflow-y: auto;\n}\n\n.content-wrapper.kanban .card.card-row .card:last-child {\n  margin-bottom: 0;\n  border-bottom-width: 1px;\n}\n\n.content-wrapper.kanban .card.card-row .card .card-header {\n  padding: .5rem .75rem;\n}\n\n.content-wrapper.kanban .card.card-row .card .card-body {\n  padding: .75rem;\n}\n\n.content-wrapper.kanban .btn-tool.btn-link {\n  text-decoration: underline;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.fc-button {\n  background: #f8f9fa;\n  background-image: none;\n  border-bottom-color: #ddd;\n  border-color: #ddd;\n  color: #495057;\n}\n\n.fc-button:hover, .fc-button:active, .fc-button.hover {\n  background-color: #e9e9e9;\n}\n\n.fc-header-title h2 {\n  color: #666;\n  font-size: 15px;\n  line-height: 1.6em;\n  margin-left: 10px;\n}\n\n.fc-header-right {\n  padding-right: 10px;\n}\n\n.fc-header-left {\n  padding-left: 10px;\n}\n\n.fc-widget-header {\n  background: #fafafa;\n}\n\n.fc-grid {\n  border: 0;\n  width: 100%;\n}\n\n.fc-widget-header:first-of-type,\n.fc-widget-content:first-of-type {\n  border-left: 0;\n  border-right: 0;\n}\n\n.fc-widget-header:last-of-type,\n.fc-widget-content:last-of-type {\n  border-right: 0;\n}\n\n.fc-toolbar,\n.fc-toolbar.fc-header-toolbar {\n  margin: 0;\n  padding: 1rem;\n}\n\n@media (max-width: 575.98px) {\n  .fc-toolbar {\n    -ms-flex-direction: column;\n    flex-direction: column;\n  }\n  .fc-toolbar .fc-left {\n    -ms-flex-order: 1;\n    order: 1;\n    margin-bottom: .5rem;\n  }\n  .fc-toolbar .fc-center {\n    -ms-flex-order: 0;\n    order: 0;\n    margin-bottom: .375rem;\n  }\n  .fc-toolbar .fc-right {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n}\n\n.fc-day-number {\n  font-size: 20px;\n  font-weight: 300;\n  padding-right: 10px;\n}\n\n.fc-color-picker {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n.fc-color-picker > li {\n  float: left;\n  font-size: 30px;\n  line-height: 30px;\n  margin-right: 5px;\n}\n\n.fc-color-picker > li .fa,\n.fc-color-picker > li .fas,\n.fc-color-picker > li .far,\n.fc-color-picker > li .fab,\n.fc-color-picker > li .fal,\n.fc-color-picker > li .fad,\n.fc-color-picker > li .svg-inline--fa,\n.fc-color-picker > li .ion {\n  transition: -webkit-transform linear .3s;\n  transition: transform linear .3s;\n  transition: transform linear .3s, -webkit-transform linear .3s;\n}\n\n.fc-color-picker > li .fa:hover,\n.fc-color-picker > li .fas:hover,\n.fc-color-picker > li .far:hover,\n.fc-color-picker > li .fab:hover,\n.fc-color-picker > li .fal:hover,\n.fc-color-picker > li .fad:hover,\n.fc-color-picker > li .svg-inline--fa:hover,\n.fc-color-picker > li .ion:hover {\n  -webkit-transform: rotate(30deg);\n  transform: rotate(30deg);\n}\n\n#add-new-event {\n  transition: all linear .3s;\n}\n\n.external-event {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  cursor: move;\n  font-weight: 700;\n  margin-bottom: 4px;\n  padding: 5px 10px;\n}\n\n.external-event:hover {\n  box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.2);\n}\n\n.select2-container--default .select2-selection--single {\n  border: 1px solid #ced4da;\n  padding: 0.46875rem 0.75rem;\n  height: calc(2.25rem + 2px);\n}\n\n.select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-dropdown {\n  border: 1px solid #ced4da;\n}\n\n.select2-container--default .select2-results__option {\n  padding: 6px 12px;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__rendered {\n  padding-left: 0;\n  height: auto;\n  margin-top: -3px;\n}\n\n.select2-container--default[dir=\"rtl\"] .select2-selection--single .select2-selection__rendered {\n  padding-right: 6px;\n  padding-left: 20px;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__arrow {\n  height: 31px;\n  right: 6px;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__arrow b {\n  margin-top: 0;\n}\n\n.select2-container--default .select2-dropdown .select2-search__field,\n.select2-container--default .select2-search--inline .select2-search__field {\n  border: 1px solid #ced4da;\n}\n\n.select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-search--inline .select2-search__field:focus {\n  outline: none;\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-dropdown.select2-dropdown--below {\n  border-top: 0;\n}\n\n.select2-container--default .select2-dropdown.select2-dropdown--above {\n  border-bottom: 0;\n}\n\n.select2-container--default .select2-results__option[aria-disabled='true'] {\n  color: #6c757d;\n}\n\n.select2-container--default .select2-results__option[aria-selected='true'] {\n  background-color: #dee2e6;\n}\n\n.select2-container--default .select2-results__option[aria-selected='true'], .select2-container--default .select2-results__option[aria-selected='true']:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-selection--multiple {\n  border: 1px solid #ced4da;\n  min-height: calc(2.25rem + 2px);\n}\n\n.select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered {\n  padding: 0 0.375rem 0.375rem;\n  margin-bottom: -0.375rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline {\n  width: 100%;\n  margin-left: 0.375rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline .select2-search__field {\n  width: 100% !important;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field {\n  border: 0;\n  margin-top: 6px;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n  padding: 0 10px;\n  margin-top: .31rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n  float: right;\n  margin-left: 5px;\n  margin-right: -2px;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-search.select2-search--inline .select2-search__field, .select2-container--default .select2-selection--multiple.text-sm .select2-search.select2-search--inline .select2-search__field {\n  margin-top: 8px;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__choice, .select2-container--default .select2-selection--multiple.text-sm .select2-selection__choice {\n  margin-top: .4rem;\n}\n\n.select2-container--default.select2-container--focus .select2-selection--single,\n.select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-container--default.select2-container--focus .select2-search__field {\n  border: 0;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__rendered li {\n  padding-right: 10px;\n}\n\n.input-group-prepend ~ .select2-container--default .select2-selection {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.input-group > .select2-container--default:not(:last-child) .select2-selection {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.select2-container--bootstrap4.select2-container--focus .select2-selection {\n  box-shadow: none;\n}\n\nselect.form-control-sm ~ .select2-container--default {\n  font-size: 75%;\n}\n\n.text-sm .select2-container--default .select2-selection--single,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single {\n  height: calc(1.8125rem + 2px);\n}\n\n.text-sm .select2-container--default .select2-selection--single .select2-selection__rendered,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single .select2-selection__rendered {\n  margin-top: -.4rem;\n}\n\n.text-sm .select2-container--default .select2-selection--single .select2-selection__arrow,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single .select2-selection__arrow {\n  top: -.12rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple {\n  min-height: calc(1.8125rem + 2px);\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered {\n  padding: 0 0.25rem 0.25rem;\n  margin-top: -0.1rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline {\n  margin-left: 0.25rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field {\n  margin-top: 6px;\n}\n\n.maximized-card .select2-dropdown {\n  z-index: 9999;\n}\n\n.select2-primary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-primary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-primary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-primary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-primary .select2-search--inline .select2-search__field:focus,\n.select2-primary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-primary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-primary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-primary .select2-results__option--highlighted,\n.select2-primary .select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-primary .select2-results__option--highlighted[aria-selected]:hover,\n.select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple:focus,\n.select2-primary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice__remove,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-primary.select2-container--focus .select2-selection--multiple,\n.select2-primary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-secondary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-secondary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-secondary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-secondary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-secondary .select2-search--inline .select2-search__field:focus,\n.select2-secondary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-secondary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-secondary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .select2-secondary .select2-results__option--highlighted,\n.select2-secondary .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-secondary .select2-results__option--highlighted[aria-selected]:hover,\n.select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple:focus,\n.select2-secondary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice__remove,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary.select2-container--focus .select2-selection--multiple,\n.select2-secondary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.select2-success + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-success + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-success.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-success .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-success .select2-search--inline .select2-search__field:focus,\n.select2-success .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-success .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-success .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #71dd8a;\n}\n\n.select2-container--default .select2-success .select2-results__option--highlighted,\n.select2-success .select2-container--default .select2-results__option--highlighted {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-success .select2-results__option--highlighted[aria-selected]:hover,\n.select2-success .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-success .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #259b40;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple:focus,\n.select2-success .select2-container--default .select2-selection--multiple:focus {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #28a745;\n  border-color: #23923d;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice__remove,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-success.select2-container--focus .select2-selection--multiple,\n.select2-success .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #71dd8a;\n}\n\n.select2-info + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-info + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-info.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-info .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-info .select2-search--inline .select2-search__field:focus,\n.select2-info .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-info .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-info .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #63d9ec;\n}\n\n.select2-container--default .select2-info .select2-results__option--highlighted,\n.select2-info .select2-container--default .select2-results__option--highlighted {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-info .select2-results__option--highlighted[aria-selected]:hover,\n.select2-info .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-info .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1596aa;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple:focus,\n.select2-info .select2-container--default .select2-selection--multiple:focus {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #17a2b8;\n  border-color: #148ea1;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice__remove,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-info.select2-container--focus .select2-selection--multiple,\n.select2-info .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #63d9ec;\n}\n\n.select2-warning + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-warning + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-warning.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-warning .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-warning .select2-search--inline .select2-search__field:focus,\n.select2-warning .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-warning .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-warning .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #ffe187;\n}\n\n.select2-container--default .select2-warning .select2-results__option--highlighted,\n.select2-warning .select2-container--default .select2-results__option--highlighted {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-warning .select2-results__option--highlighted[aria-selected]:hover,\n.select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7b900;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple:focus,\n.select2-warning .select2-container--default .select2-selection--multiple:focus {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ffc107;\n  border-color: #edb100;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice__remove,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning.select2-container--focus .select2-selection--multiple,\n.select2-warning .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #ffe187;\n}\n\n.select2-danger + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-danger + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-danger.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-danger .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-danger .select2-search--inline .select2-search__field:focus,\n.select2-danger .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-danger .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-danger .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #efa2a9;\n}\n\n.select2-container--default .select2-danger .select2-results__option--highlighted,\n.select2-danger .select2-container--default .select2-results__option--highlighted {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-danger .select2-results__option--highlighted[aria-selected]:hover,\n.select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #da2839;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple:focus,\n.select2-danger .select2-container--default .select2-selection--multiple:focus {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #dc3545;\n  border-color: #d32535;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice__remove,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-danger.select2-container--focus .select2-selection--multiple,\n.select2-danger .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #efa2a9;\n}\n\n.select2-light + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.select2-light + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .select2-light.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-light .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-light .select2-search--inline .select2-search__field:focus,\n.select2-light .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-light .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-light .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .select2-light .select2-results__option--highlighted,\n.select2-light .select2-container--default .select2-results__option--highlighted {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-light .select2-results__option--highlighted[aria-selected]:hover,\n.select2-light .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-light .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #eff1f4;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple:focus,\n.select2-light .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice__remove,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light.select2-container--focus .select2-selection--multiple,\n.select2-light .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.select2-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-dark .select2-search--inline .select2-search__field:focus,\n.select2-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .select2-dark .select2-results__option--highlighted,\n.select2-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-dark .select2-results__option--highlighted[aria-selected]:hover,\n.select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple:focus,\n.select2-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice__remove,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-dark.select2-container--focus .select2-selection--multiple,\n.select2-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.select2-lightblue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #99c5de;\n}\n\n.select2-lightblue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #99c5de;\n}\n\n.select2-container--default .select2-lightblue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lightblue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lightblue .select2-search--inline .select2-search__field:focus,\n.select2-lightblue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-lightblue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-lightblue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #99c5de;\n}\n\n.select2-container--default .select2-lightblue .select2-results__option--highlighted,\n.select2-lightblue .select2-container--default .select2-results__option--highlighted {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-lightblue .select2-results__option--highlighted[aria-selected]:hover,\n.select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #3884b0;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple:focus,\n.select2-lightblue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #99c5de;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3c8dbc;\n  border-color: #367fa9;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue.select2-container--focus .select2-selection--multiple,\n.select2-lightblue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #99c5de;\n}\n\n.select2-navy + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #005ebf;\n}\n\n.select2-navy + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #005ebf;\n}\n\n.select2-container--default .select2-navy.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-navy .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-navy .select2-search--inline .select2-search__field:focus,\n.select2-navy .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-navy .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-navy .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #005ebf;\n}\n\n.select2-container--default .select2-navy .select2-results__option--highlighted,\n.select2-navy .select2-container--default .select2-results__option--highlighted {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-navy .select2-results__option--highlighted[aria-selected]:hover,\n.select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #001730;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple:focus,\n.select2-navy .select2-container--default .select2-selection--multiple:focus {\n  border-color: #005ebf;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #001f3f;\n  border-color: #001226;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice__remove,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-navy.select2-container--focus .select2-selection--multiple,\n.select2-navy .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #005ebf;\n}\n\n.select2-olive + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #87cfaf;\n}\n\n.select2-olive + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #87cfaf;\n}\n\n.select2-container--default .select2-olive.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-olive .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-olive .select2-search--inline .select2-search__field:focus,\n.select2-olive .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-olive .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-olive .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #87cfaf;\n}\n\n.select2-container--default .select2-olive .select2-results__option--highlighted,\n.select2-olive .select2-container--default .select2-results__option--highlighted {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-olive .select2-results__option--highlighted[aria-selected]:hover,\n.select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #398e68;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple:focus,\n.select2-olive .select2-container--default .select2-selection--multiple:focus {\n  border-color: #87cfaf;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3d9970;\n  border-color: #368763;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice__remove,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-olive.select2-container--focus .select2-selection--multiple,\n.select2-olive .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #87cfaf;\n}\n\n.select2-lime + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #81ffb8;\n}\n\n.select2-lime + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #81ffb8;\n}\n\n.select2-container--default .select2-lime.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lime .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lime .select2-search--inline .select2-search__field:focus,\n.select2-lime .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-lime .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-lime .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #81ffb8;\n}\n\n.select2-container--default .select2-lime .select2-results__option--highlighted,\n.select2-lime .select2-container--default .select2-results__option--highlighted {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-lime .select2-results__option--highlighted[aria-selected]:hover,\n.select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #00f169;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple:focus,\n.select2-lime .select2-container--default .select2-selection--multiple:focus {\n  border-color: #81ffb8;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #01ff70;\n  border-color: #00e765;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice__remove,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime.select2-container--focus .select2-selection--multiple,\n.select2-lime .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #81ffb8;\n}\n\n.select2-fuchsia + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f88adf;\n}\n\n.select2-fuchsia + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f88adf;\n}\n\n.select2-container--default .select2-fuchsia.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-fuchsia .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-fuchsia .select2-search--inline .select2-search__field:focus,\n.select2-fuchsia .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-fuchsia .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-fuchsia .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f88adf;\n}\n\n.select2-container--default .select2-fuchsia .select2-results__option--highlighted,\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-fuchsia .select2-results__option--highlighted[aria-selected]:hover,\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e40eb4;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple:focus,\n.select2-fuchsia .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f88adf;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f012be;\n  border-color: #db0ead;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia.select2-container--focus .select2-selection--multiple,\n.select2-fuchsia .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f88adf;\n}\n\n.select2-maroon + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f083ab;\n}\n\n.select2-maroon + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f083ab;\n}\n\n.select2-container--default .select2-maroon.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-maroon .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-maroon .select2-search--inline .select2-search__field:focus,\n.select2-maroon .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-maroon .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-maroon .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f083ab;\n}\n\n.select2-container--default .select2-maroon .select2-results__option--highlighted,\n.select2-maroon .select2-container--default .select2-results__option--highlighted {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-maroon .select2-results__option--highlighted[aria-selected]:hover,\n.select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #ca195a;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple:focus,\n.select2-maroon .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f083ab;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #d81b60;\n  border-color: #c11856;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice__remove,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon.select2-container--focus .select2-selection--multiple,\n.select2-maroon .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f083ab;\n}\n\n.select2-blue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-blue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-blue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-blue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-blue .select2-search--inline .select2-search__field:focus,\n.select2-blue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-blue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-blue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-blue .select2-results__option--highlighted,\n.select2-blue .select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-blue .select2-results__option--highlighted[aria-selected]:hover,\n.select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple:focus,\n.select2-blue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice__remove,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-blue.select2-container--focus .select2-selection--multiple,\n.select2-blue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-indigo + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-indigo + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-container--default .select2-indigo.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-indigo .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-indigo .select2-search--inline .select2-search__field:focus,\n.select2-indigo .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-indigo .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-indigo .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b389f9;\n}\n\n.select2-container--default .select2-indigo .select2-results__option--highlighted,\n.select2-indigo .select2-container--default .select2-results__option--highlighted {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-indigo .select2-results__option--highlighted[aria-selected]:hover,\n.select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #5f0de6;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple:focus,\n.select2-indigo .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b389f9;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6610f2;\n  border-color: #5b0cdd;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice__remove,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo.select2-container--focus .select2-selection--multiple,\n.select2-indigo .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b389f9;\n}\n\n.select2-purple + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-purple + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .select2-purple.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-purple .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-purple .select2-search--inline .select2-search__field:focus,\n.select2-purple .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-purple .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-purple .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b8a2e0;\n}\n\n.select2-container--default .select2-purple .select2-results__option--highlighted,\n.select2-purple .select2-container--default .select2-results__option--highlighted {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-purple .select2-results__option--highlighted[aria-selected]:hover,\n.select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #683cb8;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple:focus,\n.select2-purple .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6f42c1;\n  border-color: #643ab0;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice__remove,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-purple.select2-container--focus .select2-selection--multiple,\n.select2-purple .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b8a2e0;\n}\n\n.select2-pink + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-pink + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .select2-pink.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-pink .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-pink .select2-search--inline .select2-search__field:focus,\n.select2-pink .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-pink .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-pink .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f6b0d0;\n}\n\n.select2-container--default .select2-pink .select2-results__option--highlighted,\n.select2-pink .select2-container--default .select2-results__option--highlighted {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-pink .select2-results__option--highlighted[aria-selected]:hover,\n.select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e63084;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple:focus,\n.select2-pink .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e83e8c;\n  border-color: #e5277e;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice__remove,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-pink.select2-container--focus .select2-selection--multiple,\n.select2-pink .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f6b0d0;\n}\n\n.select2-red + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-red + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-red.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-red .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-red .select2-search--inline .select2-search__field:focus,\n.select2-red .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-red .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-red .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #efa2a9;\n}\n\n.select2-container--default .select2-red .select2-results__option--highlighted,\n.select2-red .select2-container--default .select2-results__option--highlighted {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-red .select2-results__option--highlighted[aria-selected]:hover,\n.select2-red .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-red .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #da2839;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple:focus,\n.select2-red .select2-container--default .select2-selection--multiple:focus {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #dc3545;\n  border-color: #d32535;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice__remove,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-red.select2-container--focus .select2-selection--multiple,\n.select2-red .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #efa2a9;\n}\n\n.select2-orange + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-orange + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-container--default .select2-orange.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-orange .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-orange .select2-search--inline .select2-search__field:focus,\n.select2-orange .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-orange .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-orange .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #fec392;\n}\n\n.select2-container--default .select2-orange .select2-results__option--highlighted,\n.select2-orange .select2-container--default .select2-results__option--highlighted {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-orange .select2-results__option--highlighted[aria-selected]:hover,\n.select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #fd7605;\n  color: #fff;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple:focus,\n.select2-orange .select2-container--default .select2-selection--multiple:focus {\n  border-color: #fec392;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fd7e14;\n  border-color: #f57102;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice__remove,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange.select2-container--focus .select2-selection--multiple,\n.select2-orange .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #fec392;\n}\n\n.select2-yellow + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-yellow + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-yellow.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-yellow .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-yellow .select2-search--inline .select2-search__field:focus,\n.select2-yellow .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-yellow .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-yellow .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #ffe187;\n}\n\n.select2-container--default .select2-yellow .select2-results__option--highlighted,\n.select2-yellow .select2-container--default .select2-results__option--highlighted {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-yellow .select2-results__option--highlighted[aria-selected]:hover,\n.select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7b900;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple:focus,\n.select2-yellow .select2-container--default .select2-selection--multiple:focus {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ffc107;\n  border-color: #edb100;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice__remove,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow.select2-container--focus .select2-selection--multiple,\n.select2-yellow .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #ffe187;\n}\n\n.select2-green + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-green + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-green.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-green .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-green .select2-search--inline .select2-search__field:focus,\n.select2-green .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-green .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-green .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #71dd8a;\n}\n\n.select2-container--default .select2-green .select2-results__option--highlighted,\n.select2-green .select2-container--default .select2-results__option--highlighted {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-green .select2-results__option--highlighted[aria-selected]:hover,\n.select2-green .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-green .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #259b40;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple:focus,\n.select2-green .select2-container--default .select2-selection--multiple:focus {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #28a745;\n  border-color: #23923d;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice__remove,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-green.select2-container--focus .select2-selection--multiple,\n.select2-green .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #71dd8a;\n}\n\n.select2-teal + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-teal + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .select2-teal.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-teal .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-teal .select2-search--inline .select2-search__field:focus,\n.select2-teal .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-teal .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-teal .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #7eeaca;\n}\n\n.select2-container--default .select2-teal .select2-results__option--highlighted,\n.select2-teal .select2-container--default .select2-results__option--highlighted {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-teal .select2-results__option--highlighted[aria-selected]:hover,\n.select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1ebc8d;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple:focus,\n.select2-teal .select2-container--default .select2-selection--multiple:focus {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #20c997;\n  border-color: #1cb386;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice__remove,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-teal.select2-container--focus .select2-selection--multiple,\n.select2-teal .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #7eeaca;\n}\n\n.select2-cyan + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-cyan + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-cyan.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-cyan .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-cyan .select2-search--inline .select2-search__field:focus,\n.select2-cyan .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-cyan .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-cyan .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #63d9ec;\n}\n\n.select2-container--default .select2-cyan .select2-results__option--highlighted,\n.select2-cyan .select2-container--default .select2-results__option--highlighted {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-cyan .select2-results__option--highlighted[aria-selected]:hover,\n.select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1596aa;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple:focus,\n.select2-cyan .select2-container--default .select2-selection--multiple:focus {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #17a2b8;\n  border-color: #148ea1;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice__remove,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan.select2-container--focus .select2-selection--multiple,\n.select2-cyan .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #63d9ec;\n}\n\n.select2-white + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.select2-white + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .select2-white.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-white .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-white .select2-search--inline .select2-search__field:focus,\n.select2-white .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-white .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-white .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .select2-white .select2-results__option--highlighted,\n.select2-white .select2-container--default .select2-results__option--highlighted {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-white .select2-results__option--highlighted[aria-selected]:hover,\n.select2-white .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-white .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7f7f7;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple:focus,\n.select2-white .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fff;\n  border-color: #f2f2f2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice__remove,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white.select2-container--focus .select2-selection--multiple,\n.select2-white .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.select2-gray + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-gray + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-gray.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray .select2-search--inline .select2-search__field:focus,\n.select2-gray .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-gray .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-gray .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .select2-gray .select2-results__option--highlighted,\n.select2-gray .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-gray .select2-results__option--highlighted[aria-selected]:hover,\n.select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple:focus,\n.select2-gray .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice__remove,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-gray.select2-container--focus .select2-selection--multiple,\n.select2-gray .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.select2-gray-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-gray-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray-dark .select2-search--inline .select2-search__field:focus,\n.select2-gray-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-gray-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-gray-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark .select2-results__option--highlighted,\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-gray-dark .select2-results__option--highlighted[aria-selected]:hover,\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple:focus,\n.select2-gray-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark.select2-container--focus .select2-selection--multiple,\n.select2-gray-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-selection {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.dark-mode .select2-container--disabled .select2-selection--single {\n  background-color: #454d55;\n}\n\n.dark-mode .select2-selection--single {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.dark-mode .select2-selection--single .select2-selection__rendered {\n  color: #fff;\n}\n\n.dark-mode .select2-dropdown .select2-search__field,\n.dark-mode .select2-search--inline .select2-search__field {\n  background-color: #343a40;\n  border-color: #6c757d;\n  color: white;\n}\n\n.dark-mode .select2-dropdown {\n  background-color: #343a40;\n  border-color: #6c757d;\n  color: white;\n}\n\n.dark-mode .select2-results__option[aria-selected=\"true\"] {\n  background-color: #3f474e !important;\n  color: #dee2e6;\n}\n\n.dark-mode .select2-container .select2-search--inline .select2-search__field {\n  background-color: transparent;\n  color: #fff;\n}\n\n.dark-mode .select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice {\n  color: #fff;\n}\n\n.dark-mode .select2-primary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-primary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-primary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-primary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-primary .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-primary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-primary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-primary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-results__option--highlighted,\n.dark-mode .select2-primary .select2-container--default .select2-results__option--highlighted {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-primary .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #3a5f86;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple:focus,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3f6791;\n  border-color: #375a7f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-primary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-secondary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-secondary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-secondary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-secondary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-secondary .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-secondary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-secondary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-secondary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-results__option--highlighted,\n.dark-mode .select2-secondary .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-secondary .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple:focus,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-secondary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-success + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-success + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-success.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-success .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-success .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-success .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-success .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-success .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-results__option--highlighted,\n.dark-mode .select2-success .select2-container--default .select2-results__option--highlighted {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-success .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-success .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-success .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #00ad81;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple:focus,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple:focus {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #00bc8c;\n  border-color: #00a379;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-success .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-info + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-info + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-info.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-info .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-info .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-info .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-info .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-info .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-results__option--highlighted,\n.dark-mode .select2-info .select2-container--default .select2-results__option--highlighted {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-info .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-info .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-info .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2791d9;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple:focus,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple:focus {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3498db;\n  border-color: #258cd1;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-info .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-warning + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-warning + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-warning.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-warning .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-warning .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-warning .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-warning .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-warning .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-results__option--highlighted,\n.dark-mode .select2-warning .select2-container--default .select2-results__option--highlighted {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-warning .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #ea940c;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple:focus,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f39c12;\n  border-color: #e08e0b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-warning .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-danger + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-danger + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-danger.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-danger .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-danger .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-danger .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-danger .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-danger .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-results__option--highlighted,\n.dark-mode .select2-danger .select2-container--default .select2-results__option--highlighted {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-danger .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e53f2e;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple:focus,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e74c3c;\n  border-color: #e43725;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-danger .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-light + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.dark-mode .select2-light + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-light.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-light .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-light .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-light .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-light .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-light .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-results__option--highlighted,\n.dark-mode .select2-light .select2-container--default .select2-results__option--highlighted {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-light .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-light .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-light .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #eff1f4;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple:focus,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-light .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.dark-mode .select2-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-dark .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-results__option--highlighted,\n.dark-mode .select2-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-dark .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple:focus,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-lightblue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .select2-lightblue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #e6f1f7;\n}\n\n.select2-container--default .dark-mode .select2-lightblue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lightblue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lightblue .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-lightblue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lightblue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lightblue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #e6f1f7;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-results__option--highlighted,\n.dark-mode .select2-lightblue .select2-container--default .select2-results__option--highlighted {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-lightblue .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #7ab3d5;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple:focus,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #e6f1f7;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #86bad8;\n  border-color: #72afd2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-lightblue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .select2-navy + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #006ad8;\n}\n\n.dark-mode .select2-navy + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #006ad8;\n}\n\n.select2-container--default .dark-mode .select2-navy.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-navy .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-navy .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-navy .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-navy .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-navy .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #006ad8;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-results__option--highlighted,\n.dark-mode .select2-navy .select2-container--default .select2-results__option--highlighted {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-navy .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #002449;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple:focus,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple:focus {\n  border-color: #006ad8;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #002c59;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-navy .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #006ad8;\n}\n\n.dark-mode .select2-olive + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #cfecdf;\n}\n\n.dark-mode .select2-olive + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #cfecdf;\n}\n\n.select2-container--default .dark-mode .select2-olive.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-olive .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-olive .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-olive .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-olive .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-olive .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #cfecdf;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-results__option--highlighted,\n.dark-mode .select2-olive .select2-container--default .select2-results__option--highlighted {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-olive .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #69c39b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple:focus,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple:focus {\n  border-color: #cfecdf;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #74c8a3;\n  border-color: #62c096;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-olive .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #cfecdf;\n}\n\n.dark-mode .select2-lime + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #e7fff1;\n}\n\n.dark-mode .select2-lime + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #e7fff1;\n}\n\n.select2-container--default .dark-mode .select2-lime.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lime .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lime .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-lime .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lime .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lime .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #e7fff1;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-results__option--highlighted,\n.dark-mode .select2-lime .select2-container--default .select2-results__option--highlighted {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-lime .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #58ffa1;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple:focus,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple:focus {\n  border-color: #e7fff1;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #67ffa9;\n  border-color: #4eff9b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-lime .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #e7fff1;\n}\n\n.dark-mode .select2-fuchsia + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #feeaf9;\n}\n\n.dark-mode .select2-fuchsia + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #feeaf9;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-fuchsia .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-fuchsia .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-fuchsia .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-fuchsia .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-fuchsia .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #feeaf9;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-results__option--highlighted,\n.dark-mode .select2-fuchsia .select2-container--default .select2-results__option--highlighted {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-fuchsia .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f564d4;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple:focus,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple:focus {\n  border-color: #feeaf9;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f672d8;\n  border-color: #f55ad2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-fuchsia .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #feeaf9;\n}\n\n.dark-mode .select2-maroon + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #fbdee8;\n}\n\n.dark-mode .select2-maroon + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #fbdee8;\n}\n\n.select2-container--default .dark-mode .select2-maroon.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-maroon .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-maroon .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-maroon .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-maroon .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-maroon .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #fbdee8;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-results__option--highlighted,\n.dark-mode .select2-maroon .select2-container--default .select2-results__option--highlighted {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-maroon .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #eb5f92;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple:focus,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple:focus {\n  border-color: #fbdee8;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ed6c9b;\n  border-color: #ea568c;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-maroon.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-maroon .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #fbdee8;\n}\n\n.dark-mode .select2-blue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-blue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-blue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-blue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-blue .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-blue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-blue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-blue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-results__option--highlighted,\n.dark-mode .select2-blue .select2-container--default .select2-results__option--highlighted {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-blue .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #3a5f86;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple:focus,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3f6791;\n  border-color: #375a7f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-blue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-indigo + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.dark-mode .select2-indigo + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-container--default .dark-mode .select2-indigo.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-indigo .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-indigo .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-indigo .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-indigo .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-indigo .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b389f9;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-results__option--highlighted,\n.dark-mode .select2-indigo .select2-container--default .select2-results__option--highlighted {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-indigo .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #5f0de6;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple:focus,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b389f9;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6610f2;\n  border-color: #5b0cdd;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-indigo .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b389f9;\n}\n\n.dark-mode .select2-purple + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .select2-purple + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .dark-mode .select2-purple.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-purple .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-purple .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-purple .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-purple .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-purple .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b8a2e0;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-results__option--highlighted,\n.dark-mode .select2-purple .select2-container--default .select2-results__option--highlighted {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-purple .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #683cb8;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple:focus,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6f42c1;\n  border-color: #643ab0;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-purple .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .select2-pink + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .select2-pink + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .dark-mode .select2-pink.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-pink .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-pink .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-pink .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-pink .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-pink .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f6b0d0;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-results__option--highlighted,\n.dark-mode .select2-pink .select2-container--default .select2-results__option--highlighted {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-pink .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e63084;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple:focus,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e83e8c;\n  border-color: #e5277e;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-pink .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .select2-red + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-red + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-red.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-red .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-red .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-red .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-red .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-red .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-results__option--highlighted,\n.dark-mode .select2-red .select2-container--default .select2-results__option--highlighted {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-red .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-red .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-red .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e53f2e;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple:focus,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e74c3c;\n  border-color: #e43725;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-red .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-orange + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #fec392;\n}\n\n.dark-mode .select2-orange + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-container--default .dark-mode .select2-orange.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-orange .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-orange .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-orange .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-orange .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-orange .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #fec392;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-results__option--highlighted,\n.dark-mode .select2-orange .select2-container--default .select2-results__option--highlighted {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-orange .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #fd7605;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple:focus,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple:focus {\n  border-color: #fec392;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fd7e14;\n  border-color: #f57102;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-orange.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-orange .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #fec392;\n}\n\n.dark-mode .select2-yellow + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-yellow + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-yellow.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-yellow .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-yellow .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-yellow .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-yellow .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-yellow .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-results__option--highlighted,\n.dark-mode .select2-yellow .select2-container--default .select2-results__option--highlighted {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-yellow .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #ea940c;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple:focus,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f39c12;\n  border-color: #e08e0b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-yellow .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-green + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-green + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-green.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-green .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-green .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-green .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-green .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-green .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-results__option--highlighted,\n.dark-mode .select2-green .select2-container--default .select2-results__option--highlighted {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-green .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-green .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-green .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #00ad81;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple:focus,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple:focus {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #00bc8c;\n  border-color: #00a379;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-green .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-teal + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.dark-mode .select2-teal + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .dark-mode .select2-teal.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-teal .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-teal .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-teal .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-teal .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-teal .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #7eeaca;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-results__option--highlighted,\n.dark-mode .select2-teal .select2-container--default .select2-results__option--highlighted {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-teal .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1ebc8d;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple:focus,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple:focus {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #20c997;\n  border-color: #1cb386;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-teal .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #7eeaca;\n}\n\n.dark-mode .select2-cyan + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-cyan + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-cyan.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-cyan .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-cyan .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-cyan .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-cyan .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-cyan .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-results__option--highlighted,\n.dark-mode .select2-cyan .select2-container--default .select2-results__option--highlighted {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-cyan .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2791d9;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple:focus,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple:focus {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3498db;\n  border-color: #258cd1;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-cyan .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-white + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.dark-mode .select2-white + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-white.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-white .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-white .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-white .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-white .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-white .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-results__option--highlighted,\n.dark-mode .select2-white .select2-container--default .select2-results__option--highlighted {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-white .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-white .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-white .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7f7f7;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple:focus,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fff;\n  border-color: #f2f2f2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-white .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.dark-mode .select2-gray + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-gray + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-gray.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-gray .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-results__option--highlighted,\n.dark-mode .select2-gray .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-gray .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple:focus,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-gray .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-gray-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-gray-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray-dark .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-gray-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-results__option--highlighted,\n.dark-mode .select2-gray-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-gray-dark .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple:focus,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-gray-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.slider .tooltip.in {\n  opacity: 0.9;\n}\n\n.slider.slider-vertical {\n  height: 100%;\n}\n\n.slider.slider-horizontal {\n  width: 100%;\n}\n\n.slider-primary .slider .slider-selection {\n  background: #007bff;\n}\n\n.slider-secondary .slider .slider-selection {\n  background: #6c757d;\n}\n\n.slider-success .slider .slider-selection {\n  background: #28a745;\n}\n\n.slider-info .slider .slider-selection {\n  background: #17a2b8;\n}\n\n.slider-warning .slider .slider-selection {\n  background: #ffc107;\n}\n\n.slider-danger .slider .slider-selection {\n  background: #dc3545;\n}\n\n.slider-light .slider .slider-selection {\n  background: #f8f9fa;\n}\n\n.slider-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.slider-lightblue .slider .slider-selection {\n  background: #3c8dbc;\n}\n\n.slider-navy .slider .slider-selection {\n  background: #001f3f;\n}\n\n.slider-olive .slider .slider-selection {\n  background: #3d9970;\n}\n\n.slider-lime .slider .slider-selection {\n  background: #01ff70;\n}\n\n.slider-fuchsia .slider .slider-selection {\n  background: #f012be;\n}\n\n.slider-maroon .slider .slider-selection {\n  background: #d81b60;\n}\n\n.slider-blue .slider .slider-selection {\n  background: #007bff;\n}\n\n.slider-indigo .slider .slider-selection {\n  background: #6610f2;\n}\n\n.slider-purple .slider .slider-selection {\n  background: #6f42c1;\n}\n\n.slider-pink .slider .slider-selection {\n  background: #e83e8c;\n}\n\n.slider-red .slider .slider-selection {\n  background: #dc3545;\n}\n\n.slider-orange .slider .slider-selection {\n  background: #fd7e14;\n}\n\n.slider-yellow .slider .slider-selection {\n  background: #ffc107;\n}\n\n.slider-green .slider .slider-selection {\n  background: #28a745;\n}\n\n.slider-teal .slider .slider-selection {\n  background: #20c997;\n}\n\n.slider-cyan .slider .slider-selection {\n  background: #17a2b8;\n}\n\n.slider-white .slider .slider-selection {\n  background: #fff;\n}\n\n.slider-gray .slider .slider-selection {\n  background: #6c757d;\n}\n\n.slider-gray-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.dark-mode .slider-track {\n  background-color: #4b545c;\n  background-image: none;\n}\n\n.dark-mode .slider-primary .slider .slider-selection {\n  background: #3f6791;\n}\n\n.dark-mode .slider-secondary .slider .slider-selection {\n  background: #6c757d;\n}\n\n.dark-mode .slider-success .slider .slider-selection {\n  background: #00bc8c;\n}\n\n.dark-mode .slider-info .slider .slider-selection {\n  background: #3498db;\n}\n\n.dark-mode .slider-warning .slider .slider-selection {\n  background: #f39c12;\n}\n\n.dark-mode .slider-danger .slider .slider-selection {\n  background: #e74c3c;\n}\n\n.dark-mode .slider-light .slider .slider-selection {\n  background: #f8f9fa;\n}\n\n.dark-mode .slider-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.dark-mode .slider-lightblue .slider .slider-selection {\n  background: #86bad8;\n}\n\n.dark-mode .slider-navy .slider .slider-selection {\n  background: #002c59;\n}\n\n.dark-mode .slider-olive .slider .slider-selection {\n  background: #74c8a3;\n}\n\n.dark-mode .slider-lime .slider .slider-selection {\n  background: #67ffa9;\n}\n\n.dark-mode .slider-fuchsia .slider .slider-selection {\n  background: #f672d8;\n}\n\n.dark-mode .slider-maroon .slider .slider-selection {\n  background: #ed6c9b;\n}\n\n.dark-mode .slider-blue .slider .slider-selection {\n  background: #3f6791;\n}\n\n.dark-mode .slider-indigo .slider .slider-selection {\n  background: #6610f2;\n}\n\n.dark-mode .slider-purple .slider .slider-selection {\n  background: #6f42c1;\n}\n\n.dark-mode .slider-pink .slider .slider-selection {\n  background: #e83e8c;\n}\n\n.dark-mode .slider-red .slider .slider-selection {\n  background: #e74c3c;\n}\n\n.dark-mode .slider-orange .slider .slider-selection {\n  background: #fd7e14;\n}\n\n.dark-mode .slider-yellow .slider .slider-selection {\n  background: #f39c12;\n}\n\n.dark-mode .slider-green .slider .slider-selection {\n  background: #00bc8c;\n}\n\n.dark-mode .slider-teal .slider .slider-selection {\n  background: #20c997;\n}\n\n.dark-mode .slider-cyan .slider .slider-selection {\n  background: #3498db;\n}\n\n.dark-mode .slider-white .slider .slider-selection {\n  background: #fff;\n}\n\n.dark-mode .slider-gray .slider .slider-selection {\n  background: #6c757d;\n}\n\n.dark-mode .slider-gray-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.icheck-primary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-primary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-primary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-primary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-primary > input:first-child:checked + label::before,\n.icheck-primary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-secondary > input:first-child:checked + label::before,\n.icheck-secondary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.icheck-success > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-success > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-success > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-success > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-success > input:first-child:checked + label::before,\n.icheck-success > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.icheck-info > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-info > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-info > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-info > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-info > input:first-child:checked + label::before,\n.icheck-info > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.icheck-warning > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-warning > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-warning > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-warning > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-warning > input:first-child:checked + label::before,\n.icheck-warning > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.icheck-danger > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-danger > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-danger > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-danger > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-danger > input:first-child:checked + label::before,\n.icheck-danger > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.icheck-light > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-light > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.icheck-light > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-light > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.icheck-light > input:first-child:checked + label::before,\n.icheck-light > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.icheck-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-dark > input:first-child:checked + label::before,\n.icheck-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3c8dbc;\n}\n\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3c8dbc;\n}\n\n.icheck-lightblue > input:first-child:checked + label::before,\n.icheck-lightblue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n}\n\n.icheck-navy > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-navy > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #001f3f;\n}\n\n.icheck-navy > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-navy > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #001f3f;\n}\n\n.icheck-navy > input:first-child:checked + label::before,\n.icheck-navy > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #001f3f;\n  border-color: #001f3f;\n}\n\n.icheck-olive > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-olive > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3d9970;\n}\n\n.icheck-olive > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-olive > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3d9970;\n}\n\n.icheck-olive > input:first-child:checked + label::before,\n.icheck-olive > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3d9970;\n  border-color: #3d9970;\n}\n\n.icheck-lime > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-lime > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #01ff70;\n}\n\n.icheck-lime > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-lime > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #01ff70;\n}\n\n.icheck-lime > input:first-child:checked + label::before,\n.icheck-lime > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #01ff70;\n  border-color: #01ff70;\n}\n\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f012be;\n}\n\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f012be;\n}\n\n.icheck-fuchsia > input:first-child:checked + label::before,\n.icheck-fuchsia > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f012be;\n  border-color: #f012be;\n}\n\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #d81b60;\n}\n\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #d81b60;\n}\n\n.icheck-maroon > input:first-child:checked + label::before,\n.icheck-maroon > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #d81b60;\n  border-color: #d81b60;\n}\n\n.icheck-blue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-blue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-blue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-blue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-blue > input:first-child:checked + label::before,\n.icheck-blue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.icheck-indigo > input:first-child:checked + label::before,\n.icheck-indigo > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6610f2;\n  border-color: #6610f2;\n}\n\n.icheck-purple > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-purple > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.icheck-purple > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-purple > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.icheck-purple > input:first-child:checked + label::before,\n.icheck-purple > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n}\n\n.icheck-pink > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-pink > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.icheck-pink > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-pink > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.icheck-pink > input:first-child:checked + label::before,\n.icheck-pink > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n}\n\n.icheck-red > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-red > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-red > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-red > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-red > input:first-child:checked + label::before,\n.icheck-red > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.icheck-orange > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-orange > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.icheck-orange > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-orange > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.icheck-orange > input:first-child:checked + label::before,\n.icheck-orange > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n}\n\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-yellow > input:first-child:checked + label::before,\n.icheck-yellow > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.icheck-green > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-green > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-green > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-green > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-green > input:first-child:checked + label::before,\n.icheck-green > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.icheck-teal > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-teal > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.icheck-teal > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-teal > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.icheck-teal > input:first-child:checked + label::before,\n.icheck-teal > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #20c997;\n  border-color: #20c997;\n}\n\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-cyan > input:first-child:checked + label::before,\n.icheck-cyan > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.icheck-white > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-white > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.icheck-white > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-white > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.icheck-white > input:first-child:checked + label::before,\n.icheck-white > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fff;\n  border-color: #fff;\n}\n\n.icheck-gray > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-gray > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-gray > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-gray > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-gray > input:first-child:checked + label::before,\n.icheck-gray > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-gray-dark > input:first-child:checked + label::before,\n.icheck-gray-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.dark-mode [class*=\"icheck-\"] > input:first-child:not(:checked) + input[type=\"hidden\"] + label::before,\n.dark-mode [class*=\"icheck-\"] > input:first-child:not(:checked) + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-primary > input:first-child:checked + label::before,\n.dark-mode .icheck-primary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-secondary > input:first-child:checked + label::before,\n.dark-mode .icheck-secondary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-success > input:first-child:checked + label::before,\n.dark-mode .icheck-success > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-info > input:first-child:checked + label::before,\n.dark-mode .icheck-info > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-warning > input:first-child:checked + label::before,\n.dark-mode .icheck-warning > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-danger > input:first-child:checked + label::before,\n.dark-mode .icheck-danger > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.dark-mode .icheck-light > input:first-child:checked + label::before,\n.dark-mode .icheck-light > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-dark > input:first-child:checked + label::before,\n.dark-mode .icheck-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #86bad8;\n}\n\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #86bad8;\n}\n\n.dark-mode .icheck-lightblue > input:first-child:checked + label::before,\n.dark-mode .icheck-lightblue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #86bad8;\n  border-color: #86bad8;\n}\n\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #002c59;\n}\n\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #002c59;\n}\n\n.dark-mode .icheck-navy > input:first-child:checked + label::before,\n.dark-mode .icheck-navy > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #002c59;\n  border-color: #002c59;\n}\n\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #74c8a3;\n}\n\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #74c8a3;\n}\n\n.dark-mode .icheck-olive > input:first-child:checked + label::before,\n.dark-mode .icheck-olive > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #74c8a3;\n  border-color: #74c8a3;\n}\n\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #67ffa9;\n}\n\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #67ffa9;\n}\n\n.dark-mode .icheck-lime > input:first-child:checked + label::before,\n.dark-mode .icheck-lime > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #67ffa9;\n  border-color: #67ffa9;\n}\n\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f672d8;\n}\n\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f672d8;\n}\n\n.dark-mode .icheck-fuchsia > input:first-child:checked + label::before,\n.dark-mode .icheck-fuchsia > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f672d8;\n  border-color: #f672d8;\n}\n\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ed6c9b;\n}\n\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ed6c9b;\n}\n\n.dark-mode .icheck-maroon > input:first-child:checked + label::before,\n.dark-mode .icheck-maroon > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ed6c9b;\n  border-color: #ed6c9b;\n}\n\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-blue > input:first-child:checked + label::before,\n.dark-mode .icheck-blue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.dark-mode .icheck-indigo > input:first-child:checked + label::before,\n.dark-mode .icheck-indigo > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6610f2;\n  border-color: #6610f2;\n}\n\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.dark-mode .icheck-purple > input:first-child:checked + label::before,\n.dark-mode .icheck-purple > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n}\n\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.dark-mode .icheck-pink > input:first-child:checked + label::before,\n.dark-mode .icheck-pink > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n}\n\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-red > input:first-child:checked + label::before,\n.dark-mode .icheck-red > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.dark-mode .icheck-orange > input:first-child:checked + label::before,\n.dark-mode .icheck-orange > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n}\n\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-yellow > input:first-child:checked + label::before,\n.dark-mode .icheck-yellow > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-green > input:first-child:checked + label::before,\n.dark-mode .icheck-green > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.dark-mode .icheck-teal > input:first-child:checked + label::before,\n.dark-mode .icheck-teal > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #20c997;\n  border-color: #20c997;\n}\n\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-cyan > input:first-child:checked + label::before,\n.dark-mode .icheck-cyan > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.dark-mode .icheck-white > input:first-child:checked + label::before,\n.dark-mode .icheck-white > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fff;\n  border-color: #fff;\n}\n\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-gray > input:first-child:checked + label::before,\n.dark-mode .icheck-gray > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-gray-dark > input:first-child:checked + label::before,\n.dark-mode .icheck-gray-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.mapael .map {\n  position: relative;\n}\n\n.mapael .mapTooltip {\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  border-radius: 0.25rem;\n  font-size: 0.875rem;\n  background-color: #000;\n  color: #fff;\n  display: block;\n  max-width: 200px;\n  padding: 0.25rem 0.5rem;\n  position: absolute;\n  text-align: center;\n  word-wrap: break-word;\n  z-index: 1070;\n}\n\n.mapael .myLegend {\n  background-color: #f8f9fa;\n  border: 1px solid #adb5bd;\n  padding: 10px;\n  width: 600px;\n}\n\n.mapael .zoomButton {\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  border-radius: 0.25rem;\n  color: #444;\n  cursor: pointer;\n  font-weight: 700;\n  height: 16px;\n  left: 10px;\n  line-height: 14px;\n  padding-left: 1px;\n  position: absolute;\n  text-align: center;\n  top: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  width: 16px;\n}\n\n.mapael .zoomButton:hover, .mapael .zoomButton:active, .mapael .zoomButton.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.mapael .zoomReset {\n  line-height: 12px;\n  top: 10px;\n}\n\n.mapael .zoomIn {\n  top: 30px;\n}\n\n.mapael .zoomOut {\n  top: 50px;\n}\n\n.jqvmap-zoomin,\n.jqvmap-zoomout {\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  border-radius: 0.25rem;\n  color: #444;\n  height: 15px;\n  width: 15px;\n  padding: 1px 2px;\n}\n\n.jqvmap-zoomin:hover, .jqvmap-zoomin:active, .jqvmap-zoomin.hover,\n.jqvmap-zoomout:hover,\n.jqvmap-zoomout:active,\n.jqvmap-zoomout.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.swal2-icon.swal2-info {\n  border-color: ligthen(#17a2b8, 20%);\n  color: #17a2b8;\n}\n\n.swal2-icon.swal2-warning {\n  border-color: ligthen(#ffc107, 20%);\n  color: #ffc107;\n}\n\n.swal2-icon.swal2-error {\n  border-color: ligthen(#dc3545, 20%);\n  color: #dc3545;\n}\n\n.swal2-icon.swal2-question {\n  border-color: ligthen(#6c757d, 20%);\n  color: #6c757d;\n}\n\n.swal2-icon.swal2-success {\n  border-color: ligthen(#28a745, 20%);\n  color: #28a745;\n}\n\n.swal2-icon.swal2-success .swal2-success-ring {\n  border-color: ligthen(#28a745, 20%);\n}\n\n.swal2-icon.swal2-success [class^='swal2-success-line'] {\n  background-color: #28a745;\n}\n\n.dark-mode .swal2-popup {\n  background-color: #343a40;\n  color: #e9ecef;\n}\n\n.dark-mode .swal2-popup .swal2-content,\n.dark-mode .swal2-popup .swal2-title {\n  color: #e9ecef;\n}\n\n#toast-container .toast {\n  background-color: #007bff;\n}\n\n#toast-container .toast-success {\n  background-color: #28a745;\n}\n\n#toast-container .toast-error {\n  background-color: #dc3545;\n}\n\n#toast-container .toast-info {\n  background-color: #17a2b8;\n}\n\n#toast-container .toast-warning {\n  background-color: #ffc107;\n}\n\n.toast-bottom-full-width .toast,\n.toast-top-full-width .toast {\n  max-width: inherit;\n}\n\n.pace {\n  z-index: 1048;\n}\n\n.pace .pace-progress {\n  z-index: 1049;\n}\n\n.pace .pace-activity {\n  z-index: 1050;\n}\n\n.pace-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-primary .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-primary .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-primary .pace .pace-progress::after {\n  color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-bounce-primary .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-center-atom-primary .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-primary .pace-progress::before {\n  background: #007bff;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-primary .pace-activity {\n  border-color: #007bff;\n}\n\n.pace-center-atom-primary .pace-activity::after, .pace-center-atom-primary .pace-activity::before {\n  border-color: #007bff;\n}\n\n.pace-center-circle-primary .pace .pace-progress {\n  background: rgba(0, 123, 255, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-primary .pace .pace-activity {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-radar-primary .pace .pace-activity::before {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-simple-primary .pace {\n  background: #fff;\n  border-color: #007bff;\n}\n\n.pace-center-simple-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-material-primary .pace {\n  color: #007bff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::after,\n.pace-corner-indicator-primary .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::before {\n  border-right-color: rgba(0, 123, 255, 0.2);\n  border-left-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::after {\n  border-top-color: rgba(0, 123, 255, 0.2);\n  border-bottom-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-fill-left-primary .pace .pace-progress {\n  background-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-flash-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-flash-primary .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #007bff, 0 0 5px #007bff;\n}\n\n.pace-flash-primary .pace .pace-activity {\n  border-top-color: #007bff;\n  border-left-color: #007bff;\n}\n\n.pace-loading-bar-primary .pace .pace-progress {\n  background: #007bff;\n  color: #007bff;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-primary .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #007bff, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-primary .pace .pace-progress {\n  background-color: #007bff;\n  box-shadow: inset -1px 0 #007bff, inset 0 -1px #007bff, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-primary .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-primary .pace-progress {\n  color: #007bff;\n}\n\n.pace-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-secondary .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-secondary .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-secondary .pace .pace-progress::after {\n  color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-bounce-secondary .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-center-atom-secondary .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-secondary .pace-progress::before {\n  background: #6c757d;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-secondary .pace-activity {\n  border-color: #6c757d;\n}\n\n.pace-center-atom-secondary .pace-activity::after, .pace-center-atom-secondary .pace-activity::before {\n  border-color: #6c757d;\n}\n\n.pace-center-circle-secondary .pace .pace-progress {\n  background: rgba(108, 117, 125, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-secondary .pace .pace-activity {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-radar-secondary .pace .pace-activity::before {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-simple-secondary .pace {\n  background: #fff;\n  border-color: #6c757d;\n}\n\n.pace-center-simple-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-material-secondary .pace {\n  color: #6c757d;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::after,\n.pace-corner-indicator-secondary .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::before {\n  border-right-color: rgba(108, 117, 125, 0.2);\n  border-left-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::after {\n  border-top-color: rgba(108, 117, 125, 0.2);\n  border-bottom-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-fill-left-secondary .pace .pace-progress {\n  background-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-flash-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-flash-secondary .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6c757d, 0 0 5px #6c757d;\n}\n\n.pace-flash-secondary .pace .pace-activity {\n  border-top-color: #6c757d;\n  border-left-color: #6c757d;\n}\n\n.pace-loading-bar-secondary .pace .pace-progress {\n  background: #6c757d;\n  color: #6c757d;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-secondary .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6c757d, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-secondary .pace .pace-progress {\n  background-color: #6c757d;\n  box-shadow: inset -1px 0 #6c757d, inset 0 -1px #6c757d, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-secondary .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-secondary .pace-progress {\n  color: #6c757d;\n}\n\n.pace-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-success .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-success .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-success .pace .pace-progress::after {\n  color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-bounce-success .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-center-atom-success .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-success .pace-progress::before {\n  background: #28a745;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-success .pace-activity {\n  border-color: #28a745;\n}\n\n.pace-center-atom-success .pace-activity::after, .pace-center-atom-success .pace-activity::before {\n  border-color: #28a745;\n}\n\n.pace-center-circle-success .pace .pace-progress {\n  background: rgba(40, 167, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-success .pace .pace-activity {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-radar-success .pace .pace-activity::before {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-simple-success .pace {\n  background: #fff;\n  border-color: #28a745;\n}\n\n.pace-center-simple-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-material-success .pace {\n  color: #28a745;\n}\n\n.pace-corner-indicator-success .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-corner-indicator-success .pace .pace-activity::after,\n.pace-corner-indicator-success .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-success .pace .pace-activity::before {\n  border-right-color: rgba(40, 167, 69, 0.2);\n  border-left-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-corner-indicator-success .pace .pace-activity::after {\n  border-top-color: rgba(40, 167, 69, 0.2);\n  border-bottom-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-fill-left-success .pace .pace-progress {\n  background-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-flash-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-flash-success .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #28a745, 0 0 5px #28a745;\n}\n\n.pace-flash-success .pace .pace-activity {\n  border-top-color: #28a745;\n  border-left-color: #28a745;\n}\n\n.pace-loading-bar-success .pace .pace-progress {\n  background: #28a745;\n  color: #28a745;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-success .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #28a745, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-success .pace .pace-progress {\n  background-color: #28a745;\n  box-shadow: inset -1px 0 #28a745, inset 0 -1px #28a745, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-success .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-success .pace-progress {\n  color: #28a745;\n}\n\n.pace-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-info .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-info .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-info .pace .pace-progress::after {\n  color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-bounce-info .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-center-atom-info .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-info .pace-progress::before {\n  background: #17a2b8;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-info .pace-activity {\n  border-color: #17a2b8;\n}\n\n.pace-center-atom-info .pace-activity::after, .pace-center-atom-info .pace-activity::before {\n  border-color: #17a2b8;\n}\n\n.pace-center-circle-info .pace .pace-progress {\n  background: rgba(23, 162, 184, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-info .pace .pace-activity {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-radar-info .pace .pace-activity::before {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-simple-info .pace {\n  background: #fff;\n  border-color: #17a2b8;\n}\n\n.pace-center-simple-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-material-info .pace {\n  color: #17a2b8;\n}\n\n.pace-corner-indicator-info .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-corner-indicator-info .pace .pace-activity::after,\n.pace-corner-indicator-info .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-info .pace .pace-activity::before {\n  border-right-color: rgba(23, 162, 184, 0.2);\n  border-left-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-corner-indicator-info .pace .pace-activity::after {\n  border-top-color: rgba(23, 162, 184, 0.2);\n  border-bottom-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-fill-left-info .pace .pace-progress {\n  background-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-flash-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-flash-info .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #17a2b8, 0 0 5px #17a2b8;\n}\n\n.pace-flash-info .pace .pace-activity {\n  border-top-color: #17a2b8;\n  border-left-color: #17a2b8;\n}\n\n.pace-loading-bar-info .pace .pace-progress {\n  background: #17a2b8;\n  color: #17a2b8;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-info .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #17a2b8, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-info .pace .pace-progress {\n  background-color: #17a2b8;\n  box-shadow: inset -1px 0 #17a2b8, inset 0 -1px #17a2b8, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-info .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-info .pace-progress {\n  color: #17a2b8;\n}\n\n.pace-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-warning .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-warning .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-warning .pace .pace-progress::after {\n  color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-bounce-warning .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-center-atom-warning .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-warning .pace-progress::before {\n  background: #ffc107;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-warning .pace-activity {\n  border-color: #ffc107;\n}\n\n.pace-center-atom-warning .pace-activity::after, .pace-center-atom-warning .pace-activity::before {\n  border-color: #ffc107;\n}\n\n.pace-center-circle-warning .pace .pace-progress {\n  background: rgba(255, 193, 7, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-warning .pace .pace-activity {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-radar-warning .pace .pace-activity::before {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-simple-warning .pace {\n  background: #1f2d3d;\n  border-color: #ffc107;\n}\n\n.pace-center-simple-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-material-warning .pace {\n  color: #ffc107;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::after,\n.pace-corner-indicator-warning .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::before {\n  border-right-color: rgba(255, 193, 7, 0.2);\n  border-left-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::after {\n  border-top-color: rgba(255, 193, 7, 0.2);\n  border-bottom-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-fill-left-warning .pace .pace-progress {\n  background-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-flash-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-flash-warning .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #ffc107, 0 0 5px #ffc107;\n}\n\n.pace-flash-warning .pace .pace-activity {\n  border-top-color: #ffc107;\n  border-left-color: #ffc107;\n}\n\n.pace-loading-bar-warning .pace .pace-progress {\n  background: #ffc107;\n  color: #ffc107;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-warning .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #ffc107, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-warning .pace .pace-progress {\n  background-color: #ffc107;\n  box-shadow: inset -1px 0 #ffc107, inset 0 -1px #ffc107, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-warning .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-warning .pace-progress {\n  color: #ffc107;\n}\n\n.pace-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-danger .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-danger .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-danger .pace .pace-progress::after {\n  color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-bounce-danger .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-center-atom-danger .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-danger .pace-progress::before {\n  background: #dc3545;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-danger .pace-activity {\n  border-color: #dc3545;\n}\n\n.pace-center-atom-danger .pace-activity::after, .pace-center-atom-danger .pace-activity::before {\n  border-color: #dc3545;\n}\n\n.pace-center-circle-danger .pace .pace-progress {\n  background: rgba(220, 53, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-danger .pace .pace-activity {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-radar-danger .pace .pace-activity::before {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-simple-danger .pace {\n  background: #fff;\n  border-color: #dc3545;\n}\n\n.pace-center-simple-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-material-danger .pace {\n  color: #dc3545;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::after,\n.pace-corner-indicator-danger .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::before {\n  border-right-color: rgba(220, 53, 69, 0.2);\n  border-left-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::after {\n  border-top-color: rgba(220, 53, 69, 0.2);\n  border-bottom-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-fill-left-danger .pace .pace-progress {\n  background-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-flash-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-flash-danger .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #dc3545, 0 0 5px #dc3545;\n}\n\n.pace-flash-danger .pace .pace-activity {\n  border-top-color: #dc3545;\n  border-left-color: #dc3545;\n}\n\n.pace-loading-bar-danger .pace .pace-progress {\n  background: #dc3545;\n  color: #dc3545;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-danger .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #dc3545, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-danger .pace .pace-progress {\n  background-color: #dc3545;\n  box-shadow: inset -1px 0 #dc3545, inset 0 -1px #dc3545, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-danger .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-danger .pace-progress {\n  color: #dc3545;\n}\n\n.pace-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-barber-shop-light .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-barber-shop-light .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-light .pace .pace-progress::after {\n  color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-bounce-light .pace .pace-activity {\n  background: #f8f9fa;\n}\n\n.pace-center-atom-light .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-light .pace-progress::before {\n  background: #f8f9fa;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-light .pace-activity {\n  border-color: #f8f9fa;\n}\n\n.pace-center-atom-light .pace-activity::after, .pace-center-atom-light .pace-activity::before {\n  border-color: #f8f9fa;\n}\n\n.pace-center-circle-light .pace .pace-progress {\n  background: rgba(248, 249, 250, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-light .pace .pace-activity {\n  border-color: #f8f9fa transparent transparent;\n}\n\n.pace-center-radar-light .pace .pace-activity::before {\n  border-color: #f8f9fa transparent transparent;\n}\n\n.pace-center-simple-light .pace {\n  background: #1f2d3d;\n  border-color: #f8f9fa;\n}\n\n.pace-center-simple-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-material-light .pace {\n  color: #f8f9fa;\n}\n\n.pace-corner-indicator-light .pace .pace-activity {\n  background: #f8f9fa;\n}\n\n.pace-corner-indicator-light .pace .pace-activity::after,\n.pace-corner-indicator-light .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-light .pace .pace-activity::before {\n  border-right-color: rgba(248, 249, 250, 0.2);\n  border-left-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-corner-indicator-light .pace .pace-activity::after {\n  border-top-color: rgba(248, 249, 250, 0.2);\n  border-bottom-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-fill-left-light .pace .pace-progress {\n  background-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-flash-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-flash-light .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #f8f9fa, 0 0 5px #f8f9fa;\n}\n\n.pace-flash-light .pace .pace-activity {\n  border-top-color: #f8f9fa;\n  border-left-color: #f8f9fa;\n}\n\n.pace-loading-bar-light .pace .pace-progress {\n  background: #f8f9fa;\n  color: #f8f9fa;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-light .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #f8f9fa, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-light .pace .pace-progress {\n  background-color: #f8f9fa;\n  box-shadow: inset -1px 0 #f8f9fa, inset 0 -1px #f8f9fa, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-light .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-light .pace-progress {\n  color: #f8f9fa;\n}\n\n.pace-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-dark .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-dark .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-dark .pace .pace-progress::after {\n  color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-bounce-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-center-atom-dark .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-dark .pace-progress::before {\n  background: #343a40;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-dark .pace-activity {\n  border-color: #343a40;\n}\n\n.pace-center-atom-dark .pace-activity::after, .pace-center-atom-dark .pace-activity::before {\n  border-color: #343a40;\n}\n\n.pace-center-circle-dark .pace .pace-progress {\n  background: rgba(52, 58, 64, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-dark .pace .pace-activity {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-radar-dark .pace .pace-activity::before {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-simple-dark .pace {\n  background: #fff;\n  border-color: #343a40;\n}\n\n.pace-center-simple-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-material-dark .pace {\n  color: #343a40;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::after,\n.pace-corner-indicator-dark .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::before {\n  border-right-color: rgba(52, 58, 64, 0.2);\n  border-left-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::after {\n  border-top-color: rgba(52, 58, 64, 0.2);\n  border-bottom-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-fill-left-dark .pace .pace-progress {\n  background-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-flash-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-flash-dark .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #343a40, 0 0 5px #343a40;\n}\n\n.pace-flash-dark .pace .pace-activity {\n  border-top-color: #343a40;\n  border-left-color: #343a40;\n}\n\n.pace-loading-bar-dark .pace .pace-progress {\n  background: #343a40;\n  color: #343a40;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-dark .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #343a40, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-dark .pace .pace-progress {\n  background-color: #343a40;\n  box-shadow: inset -1px 0 #343a40, inset 0 -1px #343a40, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-dark .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-dark .pace-progress {\n  color: #343a40;\n}\n\n.pace-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-barber-shop-lightblue .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-barber-shop-lightblue .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-lightblue .pace .pace-progress::after {\n  color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-bounce-lightblue .pace .pace-activity {\n  background: #3c8dbc;\n}\n\n.pace-center-atom-lightblue .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-lightblue .pace-progress::before {\n  background: #3c8dbc;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-lightblue .pace-activity {\n  border-color: #3c8dbc;\n}\n\n.pace-center-atom-lightblue .pace-activity::after, .pace-center-atom-lightblue .pace-activity::before {\n  border-color: #3c8dbc;\n}\n\n.pace-center-circle-lightblue .pace .pace-progress {\n  background: rgba(60, 141, 188, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-lightblue .pace .pace-activity {\n  border-color: #3c8dbc transparent transparent;\n}\n\n.pace-center-radar-lightblue .pace .pace-activity::before {\n  border-color: #3c8dbc transparent transparent;\n}\n\n.pace-center-simple-lightblue .pace {\n  background: #fff;\n  border-color: #3c8dbc;\n}\n\n.pace-center-simple-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-material-lightblue .pace {\n  color: #3c8dbc;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity {\n  background: #3c8dbc;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::after,\n.pace-corner-indicator-lightblue .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::before {\n  border-right-color: rgba(60, 141, 188, 0.2);\n  border-left-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::after {\n  border-top-color: rgba(60, 141, 188, 0.2);\n  border-bottom-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-fill-left-lightblue .pace .pace-progress {\n  background-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-flash-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-flash-lightblue .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #3c8dbc, 0 0 5px #3c8dbc;\n}\n\n.pace-flash-lightblue .pace .pace-activity {\n  border-top-color: #3c8dbc;\n  border-left-color: #3c8dbc;\n}\n\n.pace-loading-bar-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n  color: #3c8dbc;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-lightblue .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #3c8dbc, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-lightblue .pace .pace-progress {\n  background-color: #3c8dbc;\n  box-shadow: inset -1px 0 #3c8dbc, inset 0 -1px #3c8dbc, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-lightblue .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-lightblue .pace-progress {\n  color: #3c8dbc;\n}\n\n.pace-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-barber-shop-navy .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-barber-shop-navy .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-navy .pace .pace-progress::after {\n  color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-bounce-navy .pace .pace-activity {\n  background: #001f3f;\n}\n\n.pace-center-atom-navy .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-navy .pace-progress::before {\n  background: #001f3f;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-navy .pace-activity {\n  border-color: #001f3f;\n}\n\n.pace-center-atom-navy .pace-activity::after, .pace-center-atom-navy .pace-activity::before {\n  border-color: #001f3f;\n}\n\n.pace-center-circle-navy .pace .pace-progress {\n  background: rgba(0, 31, 63, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-navy .pace .pace-activity {\n  border-color: #001f3f transparent transparent;\n}\n\n.pace-center-radar-navy .pace .pace-activity::before {\n  border-color: #001f3f transparent transparent;\n}\n\n.pace-center-simple-navy .pace {\n  background: #fff;\n  border-color: #001f3f;\n}\n\n.pace-center-simple-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-material-navy .pace {\n  color: #001f3f;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity {\n  background: #001f3f;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::after,\n.pace-corner-indicator-navy .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::before {\n  border-right-color: rgba(0, 31, 63, 0.2);\n  border-left-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::after {\n  border-top-color: rgba(0, 31, 63, 0.2);\n  border-bottom-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-fill-left-navy .pace .pace-progress {\n  background-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-flash-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-flash-navy .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #001f3f, 0 0 5px #001f3f;\n}\n\n.pace-flash-navy .pace .pace-activity {\n  border-top-color: #001f3f;\n  border-left-color: #001f3f;\n}\n\n.pace-loading-bar-navy .pace .pace-progress {\n  background: #001f3f;\n  color: #001f3f;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-navy .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #001f3f, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-navy .pace .pace-progress {\n  background-color: #001f3f;\n  box-shadow: inset -1px 0 #001f3f, inset 0 -1px #001f3f, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-navy .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-navy .pace-progress {\n  color: #001f3f;\n}\n\n.pace-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-barber-shop-olive .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-barber-shop-olive .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-olive .pace .pace-progress::after {\n  color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-bounce-olive .pace .pace-activity {\n  background: #3d9970;\n}\n\n.pace-center-atom-olive .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-olive .pace-progress::before {\n  background: #3d9970;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-olive .pace-activity {\n  border-color: #3d9970;\n}\n\n.pace-center-atom-olive .pace-activity::after, .pace-center-atom-olive .pace-activity::before {\n  border-color: #3d9970;\n}\n\n.pace-center-circle-olive .pace .pace-progress {\n  background: rgba(61, 153, 112, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-olive .pace .pace-activity {\n  border-color: #3d9970 transparent transparent;\n}\n\n.pace-center-radar-olive .pace .pace-activity::before {\n  border-color: #3d9970 transparent transparent;\n}\n\n.pace-center-simple-olive .pace {\n  background: #fff;\n  border-color: #3d9970;\n}\n\n.pace-center-simple-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-material-olive .pace {\n  color: #3d9970;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity {\n  background: #3d9970;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::after,\n.pace-corner-indicator-olive .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::before {\n  border-right-color: rgba(61, 153, 112, 0.2);\n  border-left-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::after {\n  border-top-color: rgba(61, 153, 112, 0.2);\n  border-bottom-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-fill-left-olive .pace .pace-progress {\n  background-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-flash-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-flash-olive .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #3d9970, 0 0 5px #3d9970;\n}\n\n.pace-flash-olive .pace .pace-activity {\n  border-top-color: #3d9970;\n  border-left-color: #3d9970;\n}\n\n.pace-loading-bar-olive .pace .pace-progress {\n  background: #3d9970;\n  color: #3d9970;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-olive .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #3d9970, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-olive .pace .pace-progress {\n  background-color: #3d9970;\n  box-shadow: inset -1px 0 #3d9970, inset 0 -1px #3d9970, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-olive .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-olive .pace-progress {\n  color: #3d9970;\n}\n\n.pace-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-barber-shop-lime .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-barber-shop-lime .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-lime .pace .pace-progress::after {\n  color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-bounce-lime .pace .pace-activity {\n  background: #01ff70;\n}\n\n.pace-center-atom-lime .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-lime .pace-progress::before {\n  background: #01ff70;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-lime .pace-activity {\n  border-color: #01ff70;\n}\n\n.pace-center-atom-lime .pace-activity::after, .pace-center-atom-lime .pace-activity::before {\n  border-color: #01ff70;\n}\n\n.pace-center-circle-lime .pace .pace-progress {\n  background: rgba(1, 255, 112, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-lime .pace .pace-activity {\n  border-color: #01ff70 transparent transparent;\n}\n\n.pace-center-radar-lime .pace .pace-activity::before {\n  border-color: #01ff70 transparent transparent;\n}\n\n.pace-center-simple-lime .pace {\n  background: #1f2d3d;\n  border-color: #01ff70;\n}\n\n.pace-center-simple-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-material-lime .pace {\n  color: #01ff70;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity {\n  background: #01ff70;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::after,\n.pace-corner-indicator-lime .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::before {\n  border-right-color: rgba(1, 255, 112, 0.2);\n  border-left-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::after {\n  border-top-color: rgba(1, 255, 112, 0.2);\n  border-bottom-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-fill-left-lime .pace .pace-progress {\n  background-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-flash-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-flash-lime .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #01ff70, 0 0 5px #01ff70;\n}\n\n.pace-flash-lime .pace .pace-activity {\n  border-top-color: #01ff70;\n  border-left-color: #01ff70;\n}\n\n.pace-loading-bar-lime .pace .pace-progress {\n  background: #01ff70;\n  color: #01ff70;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-lime .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #01ff70, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-lime .pace .pace-progress {\n  background-color: #01ff70;\n  box-shadow: inset -1px 0 #01ff70, inset 0 -1px #01ff70, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-lime .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-lime .pace-progress {\n  color: #01ff70;\n}\n\n.pace-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-barber-shop-fuchsia .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-barber-shop-fuchsia .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-fuchsia .pace .pace-progress::after {\n  color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-bounce-fuchsia .pace .pace-activity {\n  background: #f012be;\n}\n\n.pace-center-atom-fuchsia .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-fuchsia .pace-progress::before {\n  background: #f012be;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-fuchsia .pace-activity {\n  border-color: #f012be;\n}\n\n.pace-center-atom-fuchsia .pace-activity::after, .pace-center-atom-fuchsia .pace-activity::before {\n  border-color: #f012be;\n}\n\n.pace-center-circle-fuchsia .pace .pace-progress {\n  background: rgba(240, 18, 190, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-fuchsia .pace .pace-activity {\n  border-color: #f012be transparent transparent;\n}\n\n.pace-center-radar-fuchsia .pace .pace-activity::before {\n  border-color: #f012be transparent transparent;\n}\n\n.pace-center-simple-fuchsia .pace {\n  background: #fff;\n  border-color: #f012be;\n}\n\n.pace-center-simple-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-material-fuchsia .pace {\n  color: #f012be;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity {\n  background: #f012be;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::after,\n.pace-corner-indicator-fuchsia .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::before {\n  border-right-color: rgba(240, 18, 190, 0.2);\n  border-left-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::after {\n  border-top-color: rgba(240, 18, 190, 0.2);\n  border-bottom-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-fill-left-fuchsia .pace .pace-progress {\n  background-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-flash-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-flash-fuchsia .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #f012be, 0 0 5px #f012be;\n}\n\n.pace-flash-fuchsia .pace .pace-activity {\n  border-top-color: #f012be;\n  border-left-color: #f012be;\n}\n\n.pace-loading-bar-fuchsia .pace .pace-progress {\n  background: #f012be;\n  color: #f012be;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-fuchsia .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #f012be, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-fuchsia .pace .pace-progress {\n  background-color: #f012be;\n  box-shadow: inset -1px 0 #f012be, inset 0 -1px #f012be, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-fuchsia .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-fuchsia .pace-progress {\n  color: #f012be;\n}\n\n.pace-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-barber-shop-maroon .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-barber-shop-maroon .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-maroon .pace .pace-progress::after {\n  color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-bounce-maroon .pace .pace-activity {\n  background: #d81b60;\n}\n\n.pace-center-atom-maroon .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-maroon .pace-progress::before {\n  background: #d81b60;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-maroon .pace-activity {\n  border-color: #d81b60;\n}\n\n.pace-center-atom-maroon .pace-activity::after, .pace-center-atom-maroon .pace-activity::before {\n  border-color: #d81b60;\n}\n\n.pace-center-circle-maroon .pace .pace-progress {\n  background: rgba(216, 27, 96, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-maroon .pace .pace-activity {\n  border-color: #d81b60 transparent transparent;\n}\n\n.pace-center-radar-maroon .pace .pace-activity::before {\n  border-color: #d81b60 transparent transparent;\n}\n\n.pace-center-simple-maroon .pace {\n  background: #fff;\n  border-color: #d81b60;\n}\n\n.pace-center-simple-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-material-maroon .pace {\n  color: #d81b60;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity {\n  background: #d81b60;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::after,\n.pace-corner-indicator-maroon .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::before {\n  border-right-color: rgba(216, 27, 96, 0.2);\n  border-left-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::after {\n  border-top-color: rgba(216, 27, 96, 0.2);\n  border-bottom-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-fill-left-maroon .pace .pace-progress {\n  background-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-flash-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-flash-maroon .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #d81b60, 0 0 5px #d81b60;\n}\n\n.pace-flash-maroon .pace .pace-activity {\n  border-top-color: #d81b60;\n  border-left-color: #d81b60;\n}\n\n.pace-loading-bar-maroon .pace .pace-progress {\n  background: #d81b60;\n  color: #d81b60;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-maroon .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #d81b60, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-maroon .pace .pace-progress {\n  background-color: #d81b60;\n  box-shadow: inset -1px 0 #d81b60, inset 0 -1px #d81b60, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-maroon .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-maroon .pace-progress {\n  color: #d81b60;\n}\n\n.pace-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-blue .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-blue .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-blue .pace .pace-progress::after {\n  color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-bounce-blue .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-center-atom-blue .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-blue .pace-progress::before {\n  background: #007bff;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-blue .pace-activity {\n  border-color: #007bff;\n}\n\n.pace-center-atom-blue .pace-activity::after, .pace-center-atom-blue .pace-activity::before {\n  border-color: #007bff;\n}\n\n.pace-center-circle-blue .pace .pace-progress {\n  background: rgba(0, 123, 255, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-blue .pace .pace-activity {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-radar-blue .pace .pace-activity::before {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-simple-blue .pace {\n  background: #fff;\n  border-color: #007bff;\n}\n\n.pace-center-simple-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-material-blue .pace {\n  color: #007bff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::after,\n.pace-corner-indicator-blue .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::before {\n  border-right-color: rgba(0, 123, 255, 0.2);\n  border-left-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::after {\n  border-top-color: rgba(0, 123, 255, 0.2);\n  border-bottom-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-fill-left-blue .pace .pace-progress {\n  background-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-flash-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-flash-blue .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #007bff, 0 0 5px #007bff;\n}\n\n.pace-flash-blue .pace .pace-activity {\n  border-top-color: #007bff;\n  border-left-color: #007bff;\n}\n\n.pace-loading-bar-blue .pace .pace-progress {\n  background: #007bff;\n  color: #007bff;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-blue .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #007bff, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-blue .pace .pace-progress {\n  background-color: #007bff;\n  box-shadow: inset -1px 0 #007bff, inset 0 -1px #007bff, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-blue .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-blue .pace-progress {\n  color: #007bff;\n}\n\n.pace-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-barber-shop-indigo .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-barber-shop-indigo .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-indigo .pace .pace-progress::after {\n  color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-bounce-indigo .pace .pace-activity {\n  background: #6610f2;\n}\n\n.pace-center-atom-indigo .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-indigo .pace-progress::before {\n  background: #6610f2;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-indigo .pace-activity {\n  border-color: #6610f2;\n}\n\n.pace-center-atom-indigo .pace-activity::after, .pace-center-atom-indigo .pace-activity::before {\n  border-color: #6610f2;\n}\n\n.pace-center-circle-indigo .pace .pace-progress {\n  background: rgba(102, 16, 242, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-indigo .pace .pace-activity {\n  border-color: #6610f2 transparent transparent;\n}\n\n.pace-center-radar-indigo .pace .pace-activity::before {\n  border-color: #6610f2 transparent transparent;\n}\n\n.pace-center-simple-indigo .pace {\n  background: #fff;\n  border-color: #6610f2;\n}\n\n.pace-center-simple-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-material-indigo .pace {\n  color: #6610f2;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity {\n  background: #6610f2;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::after,\n.pace-corner-indicator-indigo .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::before {\n  border-right-color: rgba(102, 16, 242, 0.2);\n  border-left-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::after {\n  border-top-color: rgba(102, 16, 242, 0.2);\n  border-bottom-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-fill-left-indigo .pace .pace-progress {\n  background-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-flash-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-flash-indigo .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6610f2, 0 0 5px #6610f2;\n}\n\n.pace-flash-indigo .pace .pace-activity {\n  border-top-color: #6610f2;\n  border-left-color: #6610f2;\n}\n\n.pace-loading-bar-indigo .pace .pace-progress {\n  background: #6610f2;\n  color: #6610f2;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-indigo .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6610f2, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-indigo .pace .pace-progress {\n  background-color: #6610f2;\n  box-shadow: inset -1px 0 #6610f2, inset 0 -1px #6610f2, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-indigo .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-indigo .pace-progress {\n  color: #6610f2;\n}\n\n.pace-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-barber-shop-purple .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-barber-shop-purple .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-purple .pace .pace-progress::after {\n  color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-bounce-purple .pace .pace-activity {\n  background: #6f42c1;\n}\n\n.pace-center-atom-purple .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-purple .pace-progress::before {\n  background: #6f42c1;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-purple .pace-activity {\n  border-color: #6f42c1;\n}\n\n.pace-center-atom-purple .pace-activity::after, .pace-center-atom-purple .pace-activity::before {\n  border-color: #6f42c1;\n}\n\n.pace-center-circle-purple .pace .pace-progress {\n  background: rgba(111, 66, 193, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-purple .pace .pace-activity {\n  border-color: #6f42c1 transparent transparent;\n}\n\n.pace-center-radar-purple .pace .pace-activity::before {\n  border-color: #6f42c1 transparent transparent;\n}\n\n.pace-center-simple-purple .pace {\n  background: #fff;\n  border-color: #6f42c1;\n}\n\n.pace-center-simple-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-material-purple .pace {\n  color: #6f42c1;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity {\n  background: #6f42c1;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::after,\n.pace-corner-indicator-purple .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::before {\n  border-right-color: rgba(111, 66, 193, 0.2);\n  border-left-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::after {\n  border-top-color: rgba(111, 66, 193, 0.2);\n  border-bottom-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-fill-left-purple .pace .pace-progress {\n  background-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-flash-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-flash-purple .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6f42c1, 0 0 5px #6f42c1;\n}\n\n.pace-flash-purple .pace .pace-activity {\n  border-top-color: #6f42c1;\n  border-left-color: #6f42c1;\n}\n\n.pace-loading-bar-purple .pace .pace-progress {\n  background: #6f42c1;\n  color: #6f42c1;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-purple .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6f42c1, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-purple .pace .pace-progress {\n  background-color: #6f42c1;\n  box-shadow: inset -1px 0 #6f42c1, inset 0 -1px #6f42c1, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-purple .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-purple .pace-progress {\n  color: #6f42c1;\n}\n\n.pace-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-barber-shop-pink .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-barber-shop-pink .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-pink .pace .pace-progress::after {\n  color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-bounce-pink .pace .pace-activity {\n  background: #e83e8c;\n}\n\n.pace-center-atom-pink .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-pink .pace-progress::before {\n  background: #e83e8c;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-pink .pace-activity {\n  border-color: #e83e8c;\n}\n\n.pace-center-atom-pink .pace-activity::after, .pace-center-atom-pink .pace-activity::before {\n  border-color: #e83e8c;\n}\n\n.pace-center-circle-pink .pace .pace-progress {\n  background: rgba(232, 62, 140, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-pink .pace .pace-activity {\n  border-color: #e83e8c transparent transparent;\n}\n\n.pace-center-radar-pink .pace .pace-activity::before {\n  border-color: #e83e8c transparent transparent;\n}\n\n.pace-center-simple-pink .pace {\n  background: #fff;\n  border-color: #e83e8c;\n}\n\n.pace-center-simple-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-material-pink .pace {\n  color: #e83e8c;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity {\n  background: #e83e8c;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::after,\n.pace-corner-indicator-pink .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::before {\n  border-right-color: rgba(232, 62, 140, 0.2);\n  border-left-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::after {\n  border-top-color: rgba(232, 62, 140, 0.2);\n  border-bottom-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-fill-left-pink .pace .pace-progress {\n  background-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-flash-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-flash-pink .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #e83e8c, 0 0 5px #e83e8c;\n}\n\n.pace-flash-pink .pace .pace-activity {\n  border-top-color: #e83e8c;\n  border-left-color: #e83e8c;\n}\n\n.pace-loading-bar-pink .pace .pace-progress {\n  background: #e83e8c;\n  color: #e83e8c;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-pink .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #e83e8c, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-pink .pace .pace-progress {\n  background-color: #e83e8c;\n  box-shadow: inset -1px 0 #e83e8c, inset 0 -1px #e83e8c, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-pink .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-pink .pace-progress {\n  color: #e83e8c;\n}\n\n.pace-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-red .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-red .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-red .pace .pace-progress::after {\n  color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-bounce-red .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-center-atom-red .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-red .pace-progress::before {\n  background: #dc3545;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-red .pace-activity {\n  border-color: #dc3545;\n}\n\n.pace-center-atom-red .pace-activity::after, .pace-center-atom-red .pace-activity::before {\n  border-color: #dc3545;\n}\n\n.pace-center-circle-red .pace .pace-progress {\n  background: rgba(220, 53, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-red .pace .pace-activity {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-radar-red .pace .pace-activity::before {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-simple-red .pace {\n  background: #fff;\n  border-color: #dc3545;\n}\n\n.pace-center-simple-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-material-red .pace {\n  color: #dc3545;\n}\n\n.pace-corner-indicator-red .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-corner-indicator-red .pace .pace-activity::after,\n.pace-corner-indicator-red .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-red .pace .pace-activity::before {\n  border-right-color: rgba(220, 53, 69, 0.2);\n  border-left-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-corner-indicator-red .pace .pace-activity::after {\n  border-top-color: rgba(220, 53, 69, 0.2);\n  border-bottom-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-fill-left-red .pace .pace-progress {\n  background-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-flash-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-flash-red .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #dc3545, 0 0 5px #dc3545;\n}\n\n.pace-flash-red .pace .pace-activity {\n  border-top-color: #dc3545;\n  border-left-color: #dc3545;\n}\n\n.pace-loading-bar-red .pace .pace-progress {\n  background: #dc3545;\n  color: #dc3545;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-red .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #dc3545, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-red .pace .pace-progress {\n  background-color: #dc3545;\n  box-shadow: inset -1px 0 #dc3545, inset 0 -1px #dc3545, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-red .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-red .pace-progress {\n  color: #dc3545;\n}\n\n.pace-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-barber-shop-orange .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-barber-shop-orange .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-orange .pace .pace-progress::after {\n  color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-bounce-orange .pace .pace-activity {\n  background: #fd7e14;\n}\n\n.pace-center-atom-orange .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-orange .pace-progress::before {\n  background: #fd7e14;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-orange .pace-activity {\n  border-color: #fd7e14;\n}\n\n.pace-center-atom-orange .pace-activity::after, .pace-center-atom-orange .pace-activity::before {\n  border-color: #fd7e14;\n}\n\n.pace-center-circle-orange .pace .pace-progress {\n  background: rgba(253, 126, 20, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-orange .pace .pace-activity {\n  border-color: #fd7e14 transparent transparent;\n}\n\n.pace-center-radar-orange .pace .pace-activity::before {\n  border-color: #fd7e14 transparent transparent;\n}\n\n.pace-center-simple-orange .pace {\n  background: #1f2d3d;\n  border-color: #fd7e14;\n}\n\n.pace-center-simple-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-material-orange .pace {\n  color: #fd7e14;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity {\n  background: #fd7e14;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::after,\n.pace-corner-indicator-orange .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::before {\n  border-right-color: rgba(253, 126, 20, 0.2);\n  border-left-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::after {\n  border-top-color: rgba(253, 126, 20, 0.2);\n  border-bottom-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-fill-left-orange .pace .pace-progress {\n  background-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-flash-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-flash-orange .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #fd7e14, 0 0 5px #fd7e14;\n}\n\n.pace-flash-orange .pace .pace-activity {\n  border-top-color: #fd7e14;\n  border-left-color: #fd7e14;\n}\n\n.pace-loading-bar-orange .pace .pace-progress {\n  background: #fd7e14;\n  color: #fd7e14;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-orange .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #fd7e14, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-orange .pace .pace-progress {\n  background-color: #fd7e14;\n  box-shadow: inset -1px 0 #fd7e14, inset 0 -1px #fd7e14, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-orange .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-orange .pace-progress {\n  color: #fd7e14;\n}\n\n.pace-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-yellow .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-yellow .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-yellow .pace .pace-progress::after {\n  color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-bounce-yellow .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-center-atom-yellow .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-yellow .pace-progress::before {\n  background: #ffc107;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-yellow .pace-activity {\n  border-color: #ffc107;\n}\n\n.pace-center-atom-yellow .pace-activity::after, .pace-center-atom-yellow .pace-activity::before {\n  border-color: #ffc107;\n}\n\n.pace-center-circle-yellow .pace .pace-progress {\n  background: rgba(255, 193, 7, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-yellow .pace .pace-activity {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-radar-yellow .pace .pace-activity::before {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-simple-yellow .pace {\n  background: #1f2d3d;\n  border-color: #ffc107;\n}\n\n.pace-center-simple-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-material-yellow .pace {\n  color: #ffc107;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::after,\n.pace-corner-indicator-yellow .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::before {\n  border-right-color: rgba(255, 193, 7, 0.2);\n  border-left-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::after {\n  border-top-color: rgba(255, 193, 7, 0.2);\n  border-bottom-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-fill-left-yellow .pace .pace-progress {\n  background-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-flash-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-flash-yellow .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #ffc107, 0 0 5px #ffc107;\n}\n\n.pace-flash-yellow .pace .pace-activity {\n  border-top-color: #ffc107;\n  border-left-color: #ffc107;\n}\n\n.pace-loading-bar-yellow .pace .pace-progress {\n  background: #ffc107;\n  color: #ffc107;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-yellow .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #ffc107, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-yellow .pace .pace-progress {\n  background-color: #ffc107;\n  box-shadow: inset -1px 0 #ffc107, inset 0 -1px #ffc107, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-yellow .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-yellow .pace-progress {\n  color: #ffc107;\n}\n\n.pace-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-green .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-green .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-green .pace .pace-progress::after {\n  color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-bounce-green .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-center-atom-green .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-green .pace-progress::before {\n  background: #28a745;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-green .pace-activity {\n  border-color: #28a745;\n}\n\n.pace-center-atom-green .pace-activity::after, .pace-center-atom-green .pace-activity::before {\n  border-color: #28a745;\n}\n\n.pace-center-circle-green .pace .pace-progress {\n  background: rgba(40, 167, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-green .pace .pace-activity {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-radar-green .pace .pace-activity::before {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-simple-green .pace {\n  background: #fff;\n  border-color: #28a745;\n}\n\n.pace-center-simple-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-material-green .pace {\n  color: #28a745;\n}\n\n.pace-corner-indicator-green .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-corner-indicator-green .pace .pace-activity::after,\n.pace-corner-indicator-green .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-green .pace .pace-activity::before {\n  border-right-color: rgba(40, 167, 69, 0.2);\n  border-left-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-corner-indicator-green .pace .pace-activity::after {\n  border-top-color: rgba(40, 167, 69, 0.2);\n  border-bottom-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-fill-left-green .pace .pace-progress {\n  background-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-flash-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-flash-green .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #28a745, 0 0 5px #28a745;\n}\n\n.pace-flash-green .pace .pace-activity {\n  border-top-color: #28a745;\n  border-left-color: #28a745;\n}\n\n.pace-loading-bar-green .pace .pace-progress {\n  background: #28a745;\n  color: #28a745;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-green .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #28a745, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-green .pace .pace-progress {\n  background-color: #28a745;\n  box-shadow: inset -1px 0 #28a745, inset 0 -1px #28a745, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-green .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-green .pace-progress {\n  color: #28a745;\n}\n\n.pace-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-barber-shop-teal .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-barber-shop-teal .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-teal .pace .pace-progress::after {\n  color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-bounce-teal .pace .pace-activity {\n  background: #20c997;\n}\n\n.pace-center-atom-teal .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-teal .pace-progress::before {\n  background: #20c997;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-teal .pace-activity {\n  border-color: #20c997;\n}\n\n.pace-center-atom-teal .pace-activity::after, .pace-center-atom-teal .pace-activity::before {\n  border-color: #20c997;\n}\n\n.pace-center-circle-teal .pace .pace-progress {\n  background: rgba(32, 201, 151, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-teal .pace .pace-activity {\n  border-color: #20c997 transparent transparent;\n}\n\n.pace-center-radar-teal .pace .pace-activity::before {\n  border-color: #20c997 transparent transparent;\n}\n\n.pace-center-simple-teal .pace {\n  background: #fff;\n  border-color: #20c997;\n}\n\n.pace-center-simple-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-material-teal .pace {\n  color: #20c997;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity {\n  background: #20c997;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::after,\n.pace-corner-indicator-teal .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::before {\n  border-right-color: rgba(32, 201, 151, 0.2);\n  border-left-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::after {\n  border-top-color: rgba(32, 201, 151, 0.2);\n  border-bottom-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-fill-left-teal .pace .pace-progress {\n  background-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-flash-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-flash-teal .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #20c997, 0 0 5px #20c997;\n}\n\n.pace-flash-teal .pace .pace-activity {\n  border-top-color: #20c997;\n  border-left-color: #20c997;\n}\n\n.pace-loading-bar-teal .pace .pace-progress {\n  background: #20c997;\n  color: #20c997;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-teal .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #20c997, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-teal .pace .pace-progress {\n  background-color: #20c997;\n  box-shadow: inset -1px 0 #20c997, inset 0 -1px #20c997, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-teal .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-teal .pace-progress {\n  color: #20c997;\n}\n\n.pace-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-cyan .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-cyan .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-cyan .pace .pace-progress::after {\n  color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-bounce-cyan .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-center-atom-cyan .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-cyan .pace-progress::before {\n  background: #17a2b8;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-cyan .pace-activity {\n  border-color: #17a2b8;\n}\n\n.pace-center-atom-cyan .pace-activity::after, .pace-center-atom-cyan .pace-activity::before {\n  border-color: #17a2b8;\n}\n\n.pace-center-circle-cyan .pace .pace-progress {\n  background: rgba(23, 162, 184, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-cyan .pace .pace-activity {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-radar-cyan .pace .pace-activity::before {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-simple-cyan .pace {\n  background: #fff;\n  border-color: #17a2b8;\n}\n\n.pace-center-simple-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-material-cyan .pace {\n  color: #17a2b8;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::after,\n.pace-corner-indicator-cyan .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::before {\n  border-right-color: rgba(23, 162, 184, 0.2);\n  border-left-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::after {\n  border-top-color: rgba(23, 162, 184, 0.2);\n  border-bottom-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-fill-left-cyan .pace .pace-progress {\n  background-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-flash-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-flash-cyan .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #17a2b8, 0 0 5px #17a2b8;\n}\n\n.pace-flash-cyan .pace .pace-activity {\n  border-top-color: #17a2b8;\n  border-left-color: #17a2b8;\n}\n\n.pace-loading-bar-cyan .pace .pace-progress {\n  background: #17a2b8;\n  color: #17a2b8;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-cyan .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #17a2b8, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-cyan .pace .pace-progress {\n  background-color: #17a2b8;\n  box-shadow: inset -1px 0 #17a2b8, inset 0 -1px #17a2b8, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-cyan .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-cyan .pace-progress {\n  color: #17a2b8;\n}\n\n.pace-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-barber-shop-white .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-barber-shop-white .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-white .pace .pace-progress::after {\n  color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-bounce-white .pace .pace-activity {\n  background: #fff;\n}\n\n.pace-center-atom-white .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-white .pace-progress::before {\n  background: #fff;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-white .pace-activity {\n  border-color: #fff;\n}\n\n.pace-center-atom-white .pace-activity::after, .pace-center-atom-white .pace-activity::before {\n  border-color: #fff;\n}\n\n.pace-center-circle-white .pace .pace-progress {\n  background: rgba(255, 255, 255, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-white .pace .pace-activity {\n  border-color: #fff transparent transparent;\n}\n\n.pace-center-radar-white .pace .pace-activity::before {\n  border-color: #fff transparent transparent;\n}\n\n.pace-center-simple-white .pace {\n  background: #1f2d3d;\n  border-color: #fff;\n}\n\n.pace-center-simple-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-material-white .pace {\n  color: #fff;\n}\n\n.pace-corner-indicator-white .pace .pace-activity {\n  background: #fff;\n}\n\n.pace-corner-indicator-white .pace .pace-activity::after,\n.pace-corner-indicator-white .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-white .pace .pace-activity::before {\n  border-right-color: rgba(255, 255, 255, 0.2);\n  border-left-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-corner-indicator-white .pace .pace-activity::after {\n  border-top-color: rgba(255, 255, 255, 0.2);\n  border-bottom-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-fill-left-white .pace .pace-progress {\n  background-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-flash-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-flash-white .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #fff, 0 0 5px #fff;\n}\n\n.pace-flash-white .pace .pace-activity {\n  border-top-color: #fff;\n  border-left-color: #fff;\n}\n\n.pace-loading-bar-white .pace .pace-progress {\n  background: #fff;\n  color: #fff;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-white .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #fff, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-white .pace .pace-progress {\n  background-color: #fff;\n  box-shadow: inset -1px 0 #fff, inset 0 -1px #fff, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-white .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-white .pace-progress {\n  color: #fff;\n}\n\n.pace-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-gray .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-gray .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-gray .pace .pace-progress::after {\n  color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-bounce-gray .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-center-atom-gray .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-gray .pace-progress::before {\n  background: #6c757d;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-gray .pace-activity {\n  border-color: #6c757d;\n}\n\n.pace-center-atom-gray .pace-activity::after, .pace-center-atom-gray .pace-activity::before {\n  border-color: #6c757d;\n}\n\n.pace-center-circle-gray .pace .pace-progress {\n  background: rgba(108, 117, 125, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-gray .pace .pace-activity {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-radar-gray .pace .pace-activity::before {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-simple-gray .pace {\n  background: #fff;\n  border-color: #6c757d;\n}\n\n.pace-center-simple-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-material-gray .pace {\n  color: #6c757d;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::after,\n.pace-corner-indicator-gray .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::before {\n  border-right-color: rgba(108, 117, 125, 0.2);\n  border-left-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::after {\n  border-top-color: rgba(108, 117, 125, 0.2);\n  border-bottom-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-fill-left-gray .pace .pace-progress {\n  background-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-flash-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-flash-gray .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6c757d, 0 0 5px #6c757d;\n}\n\n.pace-flash-gray .pace .pace-activity {\n  border-top-color: #6c757d;\n  border-left-color: #6c757d;\n}\n\n.pace-loading-bar-gray .pace .pace-progress {\n  background: #6c757d;\n  color: #6c757d;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-gray .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6c757d, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-gray .pace .pace-progress {\n  background-color: #6c757d;\n  box-shadow: inset -1px 0 #6c757d, inset 0 -1px #6c757d, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-gray .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-gray .pace-progress {\n  color: #6c757d;\n}\n\n.pace-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-gray-dark .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-gray-dark .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-gray-dark .pace .pace-progress::after {\n  color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-bounce-gray-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-center-atom-gray-dark .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-gray-dark .pace-progress::before {\n  background: #343a40;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-gray-dark .pace-activity {\n  border-color: #343a40;\n}\n\n.pace-center-atom-gray-dark .pace-activity::after, .pace-center-atom-gray-dark .pace-activity::before {\n  border-color: #343a40;\n}\n\n.pace-center-circle-gray-dark .pace .pace-progress {\n  background: rgba(52, 58, 64, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-gray-dark .pace .pace-activity {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-radar-gray-dark .pace .pace-activity::before {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-simple-gray-dark .pace {\n  background: #fff;\n  border-color: #343a40;\n}\n\n.pace-center-simple-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-material-gray-dark .pace {\n  color: #343a40;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::after,\n.pace-corner-indicator-gray-dark .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::before {\n  border-right-color: rgba(52, 58, 64, 0.2);\n  border-left-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::after {\n  border-top-color: rgba(52, 58, 64, 0.2);\n  border-bottom-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-fill-left-gray-dark .pace .pace-progress {\n  background-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-flash-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-flash-gray-dark .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #343a40, 0 0 5px #343a40;\n}\n\n.pace-flash-gray-dark .pace .pace-activity {\n  border-top-color: #343a40;\n  border-left-color: #343a40;\n}\n\n.pace-loading-bar-gray-dark .pace .pace-progress {\n  background: #343a40;\n  color: #343a40;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-gray-dark .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #343a40, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-gray-dark .pace .pace-progress {\n  background-color: #343a40;\n  box-shadow: inset -1px 0 #343a40, inset 0 -1px #343a40, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-gray-dark .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-gray-dark .pace-progress {\n  color: #343a40;\n}\n\n/**\n  * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches.\n  *\n  * @version v3.4 (MODDED)\n  * @homepage https://bttstrp.github.io/bootstrap-switch\n  * @author Mattia Larentis <mattia@larentis.eu> (http://larentis.eu)\n  * @license MIT\n  */\n.bootstrap-switch {\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  cursor: pointer;\n  direction: ltr;\n  display: inline-block;\n  line-height: .5rem;\n  overflow: hidden;\n  position: relative;\n  text-align: left;\n  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  vertical-align: middle;\n  z-index: 0;\n}\n\n.bootstrap-switch .bootstrap-switch-container {\n  border-radius: 0.25rem;\n  display: inline-block;\n  top: 0;\n  -webkit-transform: translate3d(0, 0, 0);\n  transform: translate3d(0, 0, 0);\n}\n\n.bootstrap-switch:focus-within {\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on,\n.bootstrap-switch .bootstrap-switch-handle-off,\n.bootstrap-switch .bootstrap-switch-label {\n  box-sizing: border-box;\n  cursor: pointer;\n  display: table-cell;\n  font-size: 1rem;\n  font-weight: 500;\n  line-height: 1.2rem;\n  padding: .25rem .5rem;\n  vertical-align: middle;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on,\n.bootstrap-switch .bootstrap-switch-handle-off {\n  text-align: center;\n  z-index: 1;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default {\n  background: #e9ecef;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary {\n  background: #007bff;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-secondary,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-secondary {\n  background: #6c757d;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {\n  background: #28a745;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info {\n  background: #17a2b8;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning {\n  background: #ffc107;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger {\n  background: #dc3545;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-light,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-light {\n  background: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-dark,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lightblue,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lightblue {\n  background: #3c8dbc;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-navy,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-navy {\n  background: #001f3f;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-olive,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-olive {\n  background: #3d9970;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lime,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lime {\n  background: #01ff70;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-fuchsia,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-fuchsia {\n  background: #f012be;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-maroon,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-maroon {\n  background: #d81b60;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-blue,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-blue {\n  background: #007bff;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-indigo,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-indigo {\n  background: #6610f2;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-purple,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-purple {\n  background: #6f42c1;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-pink,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-pink {\n  background: #e83e8c;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-red,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-red {\n  background: #dc3545;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-orange,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-orange {\n  background: #fd7e14;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-yellow,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-yellow {\n  background: #ffc107;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-green,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-green {\n  background: #28a745;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-teal,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-teal {\n  background: #20c997;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-cyan,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-cyan {\n  background: #17a2b8;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-white,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-white {\n  background: #fff;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray {\n  background: #6c757d;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray-dark,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on {\n  border-bottom-left-radius: 0.1rem;\n  border-top-left-radius: 0.1rem;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-off {\n  border-bottom-right-radius: 0.1rem;\n  border-top-right-radius: 0.1rem;\n}\n\n.bootstrap-switch input[type='radio'],\n.bootstrap-switch input[type='checkbox'] {\n  filter: alpha(opacity=0);\n  left: 0;\n  margin: 0;\n  opacity: 0;\n  position: absolute;\n  top: 0;\n  visibility: hidden;\n  z-index: -1;\n}\n\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label {\n  font-size: .875rem;\n  line-height: 1.5;\n  padding: .1rem .3rem;\n}\n\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label {\n  font-size: .875rem;\n  line-height: 1.5;\n  padding: .2rem .4rem;\n}\n\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label {\n  font-size: 1.25rem;\n  line-height: 1.3333333rem;\n  padding: .3rem .5rem;\n}\n\n.bootstrap-switch.bootstrap-switch-disabled, .bootstrap-switch.bootstrap-switch-readonly, .bootstrap-switch.bootstrap-switch-indeterminate {\n  cursor: default;\n}\n\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label, .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label, .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label {\n  cursor: default;\n  filter: alpha(opacity=50);\n  opacity: .5;\n}\n\n.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container {\n  transition: margin-left .5s;\n}\n\n.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on {\n  border-radius: 0 0.1rem 0.1rem 0;\n}\n\n.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off {\n  border-radius: 0.1rem 0 0 0.1rem;\n}\n\n.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label,\n.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label {\n  border-bottom-right-radius: 0.1rem;\n  border-top-right-radius: 0.1rem;\n}\n\n.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label,\n.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label {\n  border-bottom-left-radius: 0.1rem;\n  border-top-left-radius: 0.1rem;\n}\n\n.dark-mode .bootstrap-switch {\n  border-color: #6c757d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default {\n  background-color: #3a4047;\n  color: #fff;\n  border-color: #454d55;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary {\n  background: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-secondary,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-secondary {\n  background: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {\n  background: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info {\n  background: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning {\n  background: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger {\n  background: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-light,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-light {\n  background: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-dark,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lightblue,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lightblue {\n  background: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-navy,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-navy {\n  background: #002c59;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-olive,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-olive {\n  background: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lime,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lime {\n  background: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-fuchsia,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-fuchsia {\n  background: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-maroon,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-maroon {\n  background: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-blue,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-blue {\n  background: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-indigo,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-indigo {\n  background: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-purple,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-purple {\n  background: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-pink,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-pink {\n  background: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-red,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-red {\n  background: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-orange,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-orange {\n  background: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-yellow,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-yellow {\n  background: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-green,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-green {\n  background: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-teal,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-teal {\n  background: #20c997;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-cyan,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-cyan {\n  background: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-white,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-white {\n  background: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray {\n  background: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray-dark,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.dark-mode .daterangepicker {\n  background-color: #3f474e;\n  border: inherit;\n}\n\n.dark-mode .daterangepicker::before, .dark-mode .daterangepicker::after {\n  border-bottom-color: #3f474e;\n}\n\n.dark-mode .daterangepicker td.available:hover,\n.dark-mode .daterangepicker th.available:hover {\n  background-color: #3f474e;\n}\n\n.dark-mode .daterangepicker td.in-range {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .daterangepicker td.off,\n.dark-mode .daterangepicker td.off.in-range,\n.dark-mode .daterangepicker td.off.start-date,\n.dark-mode .daterangepicker td.off.end-date {\n  background-color: #292d32;\n  color: #fff;\n}\n\n.dark-mode .daterangepicker .ranges li:hover {\n  background-color: #343a40;\n}\n\n.dark-mode .daterangepicker.show-ranges.ltr .drp-calendar {\n  border-color: #4b545c;\n}\n\n.dark-mode .daterangepicker.show-ranges.ltr .drp-calendar.left, .dark-mode .daterangepicker.show-ranges.ltr .drp-calendar.right {\n  border-color: #4b545c;\n  padding-top: 0;\n}\n\n.dark-mode .daterangepicker .drp-buttons {\n  border-color: #4b545c;\n}\n\n.dark-mode .daterangepicker .calendar-table {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.dark-mode .daterangepicker .calendar-table th,\n.dark-mode .daterangepicker .calendar-table td {\n  color: #fff;\n}\n\n.dark-mode .daterangepicker .calendar-table .next span,\n.dark-mode .daterangepicker .calendar-table .prev span {\n  border-color: #fff;\n}\n\n.dark-mode .daterangepicker select.hourselect,\n.dark-mode .daterangepicker select.minuteselect,\n.dark-mode .daterangepicker select.secondselect,\n.dark-mode .daterangepicker select.ampmselect {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.jqstooltip {\n  height: auto !important;\n  padding: 5px !important;\n  width: auto !important;\n}\n\n.connectedSortable {\n  min-height: 100px;\n}\n\n.ui-helper-hidden-accessible {\n  border: 0;\n  clip: rect(0 0 0 0);\n  height: 1px;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0;\n  position: absolute;\n  width: 1px;\n}\n\n.sort-highlight {\n  background: #f8f9fa;\n  border: 1px dashed #dee2e6;\n  margin-bottom: 10px;\n}\n\n.chart {\n  overflow: hidden;\n  position: relative;\n}\n\n.dark-mode .irs--flat .irs-line {\n  background-color: #4b545c;\n}\n\n.dark-mode .jsgrid-edit-row > .jsgrid-cell,\n.dark-mode .jsgrid-filter-row > .jsgrid-cell,\n.dark-mode .jsgrid-grid-body, .dark-mode .jsgrid-grid-header,\n.dark-mode .jsgrid-header-row > .jsgrid-header-cell,\n.dark-mode .jsgrid-insert-row > .jsgrid-cell,\n.dark-mode .jsgrid-row > .jsgrid-cell,\n.dark-mode .jsgrid-alt-row > .jsgrid-cell {\n  border-color: #6c757d;\n}\n\n.dark-mode .jsgrid-header-row > .jsgrid-header-cell,\n.dark-mode .jsgrid-row > .jsgrid-cell {\n  background-color: #343a40;\n}\n\n.dark-mode .jsgrid-alt-row > .jsgrid-cell {\n  background-color: #3a4047;\n}\n\n.dark-mode .jsgrid-selected-row > .jsgrid-cell {\n  background-color: #3f474e;\n}\n\n.border-transparent {\n  border-color: transparent !important;\n}\n\n.description-block {\n  display: block;\n  margin: 10px 0;\n  text-align: center;\n}\n\n.description-block.margin-bottom {\n  margin-bottom: 25px;\n}\n\n.description-block > .description-header {\n  font-size: 16px;\n  font-weight: 600;\n  margin: 0;\n  padding: 0;\n}\n\n.description-block > .description-text {\n  text-transform: uppercase;\n}\n\n.description-block .description-icon {\n  font-size: 16px;\n}\n\n.list-group-unbordered > .list-group-item {\n  border-left: 0;\n  border-radius: 0;\n  border-right: 0;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.list-header {\n  color: #6c757d;\n  font-size: 15px;\n  font-weight: 700;\n  padding: 10px 4px;\n}\n\n.list-seperator {\n  background-color: rgba(0, 0, 0, 0.125);\n  height: 1px;\n  margin: 15px 0 9px;\n}\n\n.list-link > a {\n  color: #6c757d;\n  padding: 4px;\n}\n\n.list-link > a:hover {\n  color: #212529;\n}\n\n.user-block {\n  float: left;\n}\n\n.user-block img {\n  float: left;\n  height: 40px;\n  width: 40px;\n}\n\n.user-block .username,\n.user-block .description,\n.user-block .comment {\n  display: block;\n  margin-left: 50px;\n}\n\n.user-block .username {\n  font-size: 16px;\n  font-weight: 600;\n  margin-top: -1px;\n}\n\n.user-block .description {\n  color: #6c757d;\n  font-size: 13px;\n  margin-top: -3px;\n}\n\n.user-block.user-block-sm img {\n  width: 1.875rem;\n  height: 1.875rem;\n}\n\n.user-block.user-block-sm .username,\n.user-block.user-block-sm .description,\n.user-block.user-block-sm .comment {\n  margin-left: 40px;\n}\n\n.user-block.user-block-sm .username {\n  font-size: 14px;\n}\n\n.img-sm,\n.img-md,\n.img-lg {\n  float: left;\n}\n\n.img-sm {\n  height: 1.875rem;\n  width: 1.875rem;\n}\n\n.img-sm + .img-push {\n  margin-left: 2.5rem;\n}\n\n.img-md {\n  width: 3.75rem;\n  height: 3.75rem;\n}\n\n.img-md + .img-push {\n  margin-left: 4.375rem;\n}\n\n.img-lg {\n  width: 6.25rem;\n  height: 6.25rem;\n}\n\n.img-lg + .img-push {\n  margin-left: 6.875rem;\n}\n\n.img-bordered {\n  border: 3px solid #adb5bd;\n  padding: 3px;\n}\n\n.img-bordered-sm {\n  border: 2px solid #adb5bd;\n  padding: 2px;\n}\n\n.img-rounded {\n  border-radius: 0.25rem;\n}\n\n.img-circle {\n  border-radius: 50%;\n}\n\n.img-size-64,\n.img-size-50,\n.img-size-32 {\n  height: auto;\n}\n\n.img-size-64 {\n  width: 64px;\n}\n\n.img-size-50 {\n  width: 50px;\n}\n\n.img-size-32 {\n  width: 32px;\n}\n\n.size-32,\n.size-40,\n.size-50 {\n  display: block;\n  text-align: center;\n}\n\n.size-32 {\n  height: 32px;\n  line-height: 32px;\n  width: 32px;\n}\n\n.size-40 {\n  height: 40px;\n  line-height: 40px;\n  width: 40px;\n}\n\n.size-50 {\n  height: 50px;\n  line-height: 50px;\n  width: 50px;\n}\n\n.attachment-block {\n  background-color: #f8f9fa;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n  margin-bottom: 10px;\n  padding: 5px;\n}\n\n.attachment-block .attachment-img {\n  float: left;\n  height: auto;\n  max-height: 100px;\n  max-width: 100px;\n}\n\n.attachment-block .attachment-pushed {\n  margin-left: 110px;\n}\n\n.attachment-block .attachment-heading {\n  margin: 0;\n}\n\n.attachment-block .attachment-text {\n  color: #495057;\n}\n\n.card > .overlay,\n.card > .loading-img,\n.overlay-wrapper > .overlay,\n.overlay-wrapper > .loading-img,\n.info-box > .overlay,\n.info-box > .loading-img,\n.small-box > .overlay,\n.small-box > .loading-img {\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.card .overlay,\n.overlay-wrapper .overlay,\n.info-box .overlay,\n.small-box .overlay {\n  border-radius: 0.25rem;\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: rgba(255, 255, 255, 0.7);\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  z-index: 50;\n}\n\n.card .overlay > .fa,\n.card .overlay > .fas,\n.card .overlay > .far,\n.card .overlay > .fab,\n.card .overlay > .fal,\n.card .overlay > .fad,\n.card .overlay > .svg-inline--fa,\n.card .overlay > .ion,\n.overlay-wrapper .overlay > .fa,\n.overlay-wrapper .overlay > .fas,\n.overlay-wrapper .overlay > .far,\n.overlay-wrapper .overlay > .fab,\n.overlay-wrapper .overlay > .fal,\n.overlay-wrapper .overlay > .fad,\n.overlay-wrapper .overlay > .svg-inline--fa,\n.overlay-wrapper .overlay > .ion,\n.info-box .overlay > .fa,\n.info-box .overlay > .fas,\n.info-box .overlay > .far,\n.info-box .overlay > .fab,\n.info-box .overlay > .fal,\n.info-box .overlay > .fad,\n.info-box .overlay > .svg-inline--fa,\n.info-box .overlay > .ion,\n.small-box .overlay > .fa,\n.small-box .overlay > .fas,\n.small-box .overlay > .far,\n.small-box .overlay > .fab,\n.small-box .overlay > .fal,\n.small-box .overlay > .fad,\n.small-box .overlay > .svg-inline--fa,\n.small-box .overlay > .ion {\n  color: #343a40;\n}\n\n.card .overlay.dark,\n.overlay-wrapper .overlay.dark,\n.info-box .overlay.dark,\n.small-box .overlay.dark {\n  background-color: rgba(0, 0, 0, 0.5);\n}\n\n.card .overlay.dark > .fa,\n.card .overlay.dark > .fas,\n.card .overlay.dark > .far,\n.card .overlay.dark > .fab,\n.card .overlay.dark > .fal,\n.card .overlay.dark > .fad,\n.card .overlay.dark > .svg-inline--fa,\n.card .overlay.dark > .ion,\n.overlay-wrapper .overlay.dark > .fa,\n.overlay-wrapper .overlay.dark > .fas,\n.overlay-wrapper .overlay.dark > .far,\n.overlay-wrapper .overlay.dark > .fab,\n.overlay-wrapper .overlay.dark > .fal,\n.overlay-wrapper .overlay.dark > .fad,\n.overlay-wrapper .overlay.dark > .svg-inline--fa,\n.overlay-wrapper .overlay.dark > .ion,\n.info-box .overlay.dark > .fa,\n.info-box .overlay.dark > .fas,\n.info-box .overlay.dark > .far,\n.info-box .overlay.dark > .fab,\n.info-box .overlay.dark > .fal,\n.info-box .overlay.dark > .fad,\n.info-box .overlay.dark > .svg-inline--fa,\n.info-box .overlay.dark > .ion,\n.small-box .overlay.dark > .fa,\n.small-box .overlay.dark > .fas,\n.small-box .overlay.dark > .far,\n.small-box .overlay.dark > .fab,\n.small-box .overlay.dark > .fal,\n.small-box .overlay.dark > .fad,\n.small-box .overlay.dark > .svg-inline--fa,\n.small-box .overlay.dark > .ion {\n  color: #ced4da;\n}\n\n.tab-pane > .overlay-wrapper {\n  position: relative;\n}\n\n.tab-pane > .overlay-wrapper > .overlay {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  margin-top: -1.25rem;\n  margin-left: -1.25rem;\n  height: calc(100% + 2 * 1.25rem);\n  width: calc(100% + 2 * 1.25rem);\n}\n\n.tab-pane > .overlay-wrapper > .overlay.dark {\n  color: #fff;\n}\n\n.ribbon-wrapper {\n  height: 70px;\n  overflow: hidden;\n  position: absolute;\n  right: -2px;\n  top: -2px;\n  width: 70px;\n  z-index: 10;\n}\n\n.ribbon-wrapper.ribbon-lg {\n  height: 120px;\n  width: 120px;\n}\n\n.ribbon-wrapper.ribbon-lg .ribbon {\n  right: 0;\n  top: 26px;\n  width: 160px;\n}\n\n.ribbon-wrapper.ribbon-xl {\n  height: 180px;\n  width: 180px;\n}\n\n.ribbon-wrapper.ribbon-xl .ribbon {\n  right: 4px;\n  top: 47px;\n  width: 240px;\n}\n\n.ribbon-wrapper .ribbon {\n  box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);\n  font-size: 0.8rem;\n  line-height: 100%;\n  padding: 0.375rem 0;\n  position: relative;\n  right: -2px;\n  text-align: center;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);\n  text-transform: uppercase;\n  top: 10px;\n  -webkit-transform: rotate(45deg);\n  transform: rotate(45deg);\n  width: 90px;\n}\n\n.ribbon-wrapper .ribbon::before, .ribbon-wrapper .ribbon::after {\n  border-left: 3px solid transparent;\n  border-right: 3px solid transparent;\n  border-top: 3px solid #9e9e9e;\n  bottom: -3px;\n  content: \"\";\n  position: absolute;\n}\n\n.ribbon-wrapper .ribbon::before {\n  left: 0;\n}\n\n.ribbon-wrapper .ribbon::after {\n  right: 0;\n}\n\n.back-to-top {\n  bottom: 1.25rem;\n  position: fixed;\n  right: 1.25rem;\n  z-index: 1032;\n}\n\n.back-to-top:focus {\n  box-shadow: none;\n}\n\npre {\n  padding: .75rem;\n}\n\nblockquote {\n  background-color: #fff;\n  border-left: 0.7rem solid #007bff;\n  margin: 1.5em .7rem;\n  padding: .5em .7rem;\n}\n\n.box blockquote {\n  background-color: #e9ecef;\n}\n\nblockquote p:last-child {\n  margin-bottom: 0;\n}\n\nblockquote h1,\nblockquote h2,\nblockquote h3,\nblockquote h4,\nblockquote h5,\nblockquote h6 {\n  color: #007bff;\n  font-size: 1.25rem;\n  font-weight: 600;\n}\n\nblockquote.quote-primary {\n  border-color: #007bff;\n}\n\nblockquote.quote-primary h1,\nblockquote.quote-primary h2,\nblockquote.quote-primary h3,\nblockquote.quote-primary h4,\nblockquote.quote-primary h5,\nblockquote.quote-primary h6 {\n  color: #007bff;\n}\n\nblockquote.quote-secondary {\n  border-color: #6c757d;\n}\n\nblockquote.quote-secondary h1,\nblockquote.quote-secondary h2,\nblockquote.quote-secondary h3,\nblockquote.quote-secondary h4,\nblockquote.quote-secondary h5,\nblockquote.quote-secondary h6 {\n  color: #6c757d;\n}\n\nblockquote.quote-success {\n  border-color: #28a745;\n}\n\nblockquote.quote-success h1,\nblockquote.quote-success h2,\nblockquote.quote-success h3,\nblockquote.quote-success h4,\nblockquote.quote-success h5,\nblockquote.quote-success h6 {\n  color: #28a745;\n}\n\nblockquote.quote-info {\n  border-color: #17a2b8;\n}\n\nblockquote.quote-info h1,\nblockquote.quote-info h2,\nblockquote.quote-info h3,\nblockquote.quote-info h4,\nblockquote.quote-info h5,\nblockquote.quote-info h6 {\n  color: #17a2b8;\n}\n\nblockquote.quote-warning {\n  border-color: #ffc107;\n}\n\nblockquote.quote-warning h1,\nblockquote.quote-warning h2,\nblockquote.quote-warning h3,\nblockquote.quote-warning h4,\nblockquote.quote-warning h5,\nblockquote.quote-warning h6 {\n  color: #ffc107;\n}\n\nblockquote.quote-danger {\n  border-color: #dc3545;\n}\n\nblockquote.quote-danger h1,\nblockquote.quote-danger h2,\nblockquote.quote-danger h3,\nblockquote.quote-danger h4,\nblockquote.quote-danger h5,\nblockquote.quote-danger h6 {\n  color: #dc3545;\n}\n\nblockquote.quote-light {\n  border-color: #f8f9fa;\n}\n\nblockquote.quote-light h1,\nblockquote.quote-light h2,\nblockquote.quote-light h3,\nblockquote.quote-light h4,\nblockquote.quote-light h5,\nblockquote.quote-light h6 {\n  color: #f8f9fa;\n}\n\nblockquote.quote-dark {\n  border-color: #343a40;\n}\n\nblockquote.quote-dark h1,\nblockquote.quote-dark h2,\nblockquote.quote-dark h3,\nblockquote.quote-dark h4,\nblockquote.quote-dark h5,\nblockquote.quote-dark h6 {\n  color: #343a40;\n}\n\nblockquote.quote-lightblue {\n  border-color: #3c8dbc;\n}\n\nblockquote.quote-lightblue h1,\nblockquote.quote-lightblue h2,\nblockquote.quote-lightblue h3,\nblockquote.quote-lightblue h4,\nblockquote.quote-lightblue h5,\nblockquote.quote-lightblue h6 {\n  color: #3c8dbc;\n}\n\nblockquote.quote-navy {\n  border-color: #001f3f;\n}\n\nblockquote.quote-navy h1,\nblockquote.quote-navy h2,\nblockquote.quote-navy h3,\nblockquote.quote-navy h4,\nblockquote.quote-navy h5,\nblockquote.quote-navy h6 {\n  color: #001f3f;\n}\n\nblockquote.quote-olive {\n  border-color: #3d9970;\n}\n\nblockquote.quote-olive h1,\nblockquote.quote-olive h2,\nblockquote.quote-olive h3,\nblockquote.quote-olive h4,\nblockquote.quote-olive h5,\nblockquote.quote-olive h6 {\n  color: #3d9970;\n}\n\nblockquote.quote-lime {\n  border-color: #01ff70;\n}\n\nblockquote.quote-lime h1,\nblockquote.quote-lime h2,\nblockquote.quote-lime h3,\nblockquote.quote-lime h4,\nblockquote.quote-lime h5,\nblockquote.quote-lime h6 {\n  color: #01ff70;\n}\n\nblockquote.quote-fuchsia {\n  border-color: #f012be;\n}\n\nblockquote.quote-fuchsia h1,\nblockquote.quote-fuchsia h2,\nblockquote.quote-fuchsia h3,\nblockquote.quote-fuchsia h4,\nblockquote.quote-fuchsia h5,\nblockquote.quote-fuchsia h6 {\n  color: #f012be;\n}\n\nblockquote.quote-maroon {\n  border-color: #d81b60;\n}\n\nblockquote.quote-maroon h1,\nblockquote.quote-maroon h2,\nblockquote.quote-maroon h3,\nblockquote.quote-maroon h4,\nblockquote.quote-maroon h5,\nblockquote.quote-maroon h6 {\n  color: #d81b60;\n}\n\nblockquote.quote-blue {\n  border-color: #007bff;\n}\n\nblockquote.quote-blue h1,\nblockquote.quote-blue h2,\nblockquote.quote-blue h3,\nblockquote.quote-blue h4,\nblockquote.quote-blue h5,\nblockquote.quote-blue h6 {\n  color: #007bff;\n}\n\nblockquote.quote-indigo {\n  border-color: #6610f2;\n}\n\nblockquote.quote-indigo h1,\nblockquote.quote-indigo h2,\nblockquote.quote-indigo h3,\nblockquote.quote-indigo h4,\nblockquote.quote-indigo h5,\nblockquote.quote-indigo h6 {\n  color: #6610f2;\n}\n\nblockquote.quote-purple {\n  border-color: #6f42c1;\n}\n\nblockquote.quote-purple h1,\nblockquote.quote-purple h2,\nblockquote.quote-purple h3,\nblockquote.quote-purple h4,\nblockquote.quote-purple h5,\nblockquote.quote-purple h6 {\n  color: #6f42c1;\n}\n\nblockquote.quote-pink {\n  border-color: #e83e8c;\n}\n\nblockquote.quote-pink h1,\nblockquote.quote-pink h2,\nblockquote.quote-pink h3,\nblockquote.quote-pink h4,\nblockquote.quote-pink h5,\nblockquote.quote-pink h6 {\n  color: #e83e8c;\n}\n\nblockquote.quote-red {\n  border-color: #dc3545;\n}\n\nblockquote.quote-red h1,\nblockquote.quote-red h2,\nblockquote.quote-red h3,\nblockquote.quote-red h4,\nblockquote.quote-red h5,\nblockquote.quote-red h6 {\n  color: #dc3545;\n}\n\nblockquote.quote-orange {\n  border-color: #fd7e14;\n}\n\nblockquote.quote-orange h1,\nblockquote.quote-orange h2,\nblockquote.quote-orange h3,\nblockquote.quote-orange h4,\nblockquote.quote-orange h5,\nblockquote.quote-orange h6 {\n  color: #fd7e14;\n}\n\nblockquote.quote-yellow {\n  border-color: #ffc107;\n}\n\nblockquote.quote-yellow h1,\nblockquote.quote-yellow h2,\nblockquote.quote-yellow h3,\nblockquote.quote-yellow h4,\nblockquote.quote-yellow h5,\nblockquote.quote-yellow h6 {\n  color: #ffc107;\n}\n\nblockquote.quote-green {\n  border-color: #28a745;\n}\n\nblockquote.quote-green h1,\nblockquote.quote-green h2,\nblockquote.quote-green h3,\nblockquote.quote-green h4,\nblockquote.quote-green h5,\nblockquote.quote-green h6 {\n  color: #28a745;\n}\n\nblockquote.quote-teal {\n  border-color: #20c997;\n}\n\nblockquote.quote-teal h1,\nblockquote.quote-teal h2,\nblockquote.quote-teal h3,\nblockquote.quote-teal h4,\nblockquote.quote-teal h5,\nblockquote.quote-teal h6 {\n  color: #20c997;\n}\n\nblockquote.quote-cyan {\n  border-color: #17a2b8;\n}\n\nblockquote.quote-cyan h1,\nblockquote.quote-cyan h2,\nblockquote.quote-cyan h3,\nblockquote.quote-cyan h4,\nblockquote.quote-cyan h5,\nblockquote.quote-cyan h6 {\n  color: #17a2b8;\n}\n\nblockquote.quote-white {\n  border-color: #fff;\n}\n\nblockquote.quote-white h1,\nblockquote.quote-white h2,\nblockquote.quote-white h3,\nblockquote.quote-white h4,\nblockquote.quote-white h5,\nblockquote.quote-white h6 {\n  color: #fff;\n}\n\nblockquote.quote-gray {\n  border-color: #6c757d;\n}\n\nblockquote.quote-gray h1,\nblockquote.quote-gray h2,\nblockquote.quote-gray h3,\nblockquote.quote-gray h4,\nblockquote.quote-gray h5,\nblockquote.quote-gray h6 {\n  color: #6c757d;\n}\n\nblockquote.quote-gray-dark {\n  border-color: #343a40;\n}\n\nblockquote.quote-gray-dark h1,\nblockquote.quote-gray-dark h2,\nblockquote.quote-gray-dark h3,\nblockquote.quote-gray-dark h4,\nblockquote.quote-gray-dark h5,\nblockquote.quote-gray-dark h6 {\n  color: #343a40;\n}\n\n.tab-custom-content {\n  border-top: 1px solid #dee2e6;\n  margin-top: .5rem;\n  padding-top: .5rem;\n}\n\n.nav + .tab-custom-content {\n  border-top: none;\n  border-bottom: 1px solid #dee2e6;\n  margin-top: 0;\n  margin-bottom: .5rem;\n  padding-bottom: .5rem;\n}\n\n.badge-btn {\n  border-radius: 0.15rem;\n  font-size: 0.75rem;\n  font-weight: 400;\n  padding: 0.25rem 0.5rem;\n}\n\n.badge-btn.badge-pill {\n  padding: .375rem .6rem;\n}\n\n.dark-mode a:not(.btn):hover {\n  color: #3395ff;\n}\n\n.dark-mode .attachment-block {\n  background-color: #3d444b;\n}\n\n.dark-mode .attachment-block .attachment-text {\n  color: #ced4da;\n}\n\n.dark-mode blockquote {\n  background-color: #3f474e;\n}\n\n.dark-mode blockquote.quote-primary {\n  border-color: #007bff;\n}\n\n.dark-mode blockquote.quote-primary h1,\n.dark-mode blockquote.quote-primary h2,\n.dark-mode blockquote.quote-primary h3,\n.dark-mode blockquote.quote-primary h4,\n.dark-mode blockquote.quote-primary h5,\n.dark-mode blockquote.quote-primary h6 {\n  color: #007bff;\n}\n\n.dark-mode blockquote.quote-secondary {\n  border-color: #6c757d;\n}\n\n.dark-mode blockquote.quote-secondary h1,\n.dark-mode blockquote.quote-secondary h2,\n.dark-mode blockquote.quote-secondary h3,\n.dark-mode blockquote.quote-secondary h4,\n.dark-mode blockquote.quote-secondary h5,\n.dark-mode blockquote.quote-secondary h6 {\n  color: #6c757d;\n}\n\n.dark-mode blockquote.quote-success {\n  border-color: #28a745;\n}\n\n.dark-mode blockquote.quote-success h1,\n.dark-mode blockquote.quote-success h2,\n.dark-mode blockquote.quote-success h3,\n.dark-mode blockquote.quote-success h4,\n.dark-mode blockquote.quote-success h5,\n.dark-mode blockquote.quote-success h6 {\n  color: #28a745;\n}\n\n.dark-mode blockquote.quote-info {\n  border-color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-info h1,\n.dark-mode blockquote.quote-info h2,\n.dark-mode blockquote.quote-info h3,\n.dark-mode blockquote.quote-info h4,\n.dark-mode blockquote.quote-info h5,\n.dark-mode blockquote.quote-info h6 {\n  color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-warning {\n  border-color: #ffc107;\n}\n\n.dark-mode blockquote.quote-warning h1,\n.dark-mode blockquote.quote-warning h2,\n.dark-mode blockquote.quote-warning h3,\n.dark-mode blockquote.quote-warning h4,\n.dark-mode blockquote.quote-warning h5,\n.dark-mode blockquote.quote-warning h6 {\n  color: #ffc107;\n}\n\n.dark-mode blockquote.quote-danger {\n  border-color: #dc3545;\n}\n\n.dark-mode blockquote.quote-danger h1,\n.dark-mode blockquote.quote-danger h2,\n.dark-mode blockquote.quote-danger h3,\n.dark-mode blockquote.quote-danger h4,\n.dark-mode blockquote.quote-danger h5,\n.dark-mode blockquote.quote-danger h6 {\n  color: #dc3545;\n}\n\n.dark-mode blockquote.quote-light {\n  border-color: #f8f9fa;\n}\n\n.dark-mode blockquote.quote-light h1,\n.dark-mode blockquote.quote-light h2,\n.dark-mode blockquote.quote-light h3,\n.dark-mode blockquote.quote-light h4,\n.dark-mode blockquote.quote-light h5,\n.dark-mode blockquote.quote-light h6 {\n  color: #f8f9fa;\n}\n\n.dark-mode blockquote.quote-dark {\n  border-color: #343a40;\n}\n\n.dark-mode blockquote.quote-dark h1,\n.dark-mode blockquote.quote-dark h2,\n.dark-mode blockquote.quote-dark h3,\n.dark-mode blockquote.quote-dark h4,\n.dark-mode blockquote.quote-dark h5,\n.dark-mode blockquote.quote-dark h6 {\n  color: #343a40;\n}\n\n.dark-mode blockquote.quote-lightblue {\n  border-color: #3c8dbc;\n}\n\n.dark-mode blockquote.quote-lightblue h1,\n.dark-mode blockquote.quote-lightblue h2,\n.dark-mode blockquote.quote-lightblue h3,\n.dark-mode blockquote.quote-lightblue h4,\n.dark-mode blockquote.quote-lightblue h5,\n.dark-mode blockquote.quote-lightblue h6 {\n  color: #3c8dbc;\n}\n\n.dark-mode blockquote.quote-navy {\n  border-color: #001f3f;\n}\n\n.dark-mode blockquote.quote-navy h1,\n.dark-mode blockquote.quote-navy h2,\n.dark-mode blockquote.quote-navy h3,\n.dark-mode blockquote.quote-navy h4,\n.dark-mode blockquote.quote-navy h5,\n.dark-mode blockquote.quote-navy h6 {\n  color: #001f3f;\n}\n\n.dark-mode blockquote.quote-olive {\n  border-color: #3d9970;\n}\n\n.dark-mode blockquote.quote-olive h1,\n.dark-mode blockquote.quote-olive h2,\n.dark-mode blockquote.quote-olive h3,\n.dark-mode blockquote.quote-olive h4,\n.dark-mode blockquote.quote-olive h5,\n.dark-mode blockquote.quote-olive h6 {\n  color: #3d9970;\n}\n\n.dark-mode blockquote.quote-lime {\n  border-color: #01ff70;\n}\n\n.dark-mode blockquote.quote-lime h1,\n.dark-mode blockquote.quote-lime h2,\n.dark-mode blockquote.quote-lime h3,\n.dark-mode blockquote.quote-lime h4,\n.dark-mode blockquote.quote-lime h5,\n.dark-mode blockquote.quote-lime h6 {\n  color: #01ff70;\n}\n\n.dark-mode blockquote.quote-fuchsia {\n  border-color: #f012be;\n}\n\n.dark-mode blockquote.quote-fuchsia h1,\n.dark-mode blockquote.quote-fuchsia h2,\n.dark-mode blockquote.quote-fuchsia h3,\n.dark-mode blockquote.quote-fuchsia h4,\n.dark-mode blockquote.quote-fuchsia h5,\n.dark-mode blockquote.quote-fuchsia h6 {\n  color: #f012be;\n}\n\n.dark-mode blockquote.quote-maroon {\n  border-color: #d81b60;\n}\n\n.dark-mode blockquote.quote-maroon h1,\n.dark-mode blockquote.quote-maroon h2,\n.dark-mode blockquote.quote-maroon h3,\n.dark-mode blockquote.quote-maroon h4,\n.dark-mode blockquote.quote-maroon h5,\n.dark-mode blockquote.quote-maroon h6 {\n  color: #d81b60;\n}\n\n.dark-mode blockquote.quote-blue {\n  border-color: #007bff;\n}\n\n.dark-mode blockquote.quote-blue h1,\n.dark-mode blockquote.quote-blue h2,\n.dark-mode blockquote.quote-blue h3,\n.dark-mode blockquote.quote-blue h4,\n.dark-mode blockquote.quote-blue h5,\n.dark-mode blockquote.quote-blue h6 {\n  color: #007bff;\n}\n\n.dark-mode blockquote.quote-indigo {\n  border-color: #6610f2;\n}\n\n.dark-mode blockquote.quote-indigo h1,\n.dark-mode blockquote.quote-indigo h2,\n.dark-mode blockquote.quote-indigo h3,\n.dark-mode blockquote.quote-indigo h4,\n.dark-mode blockquote.quote-indigo h5,\n.dark-mode blockquote.quote-indigo h6 {\n  color: #6610f2;\n}\n\n.dark-mode blockquote.quote-purple {\n  border-color: #6f42c1;\n}\n\n.dark-mode blockquote.quote-purple h1,\n.dark-mode blockquote.quote-purple h2,\n.dark-mode blockquote.quote-purple h3,\n.dark-mode blockquote.quote-purple h4,\n.dark-mode blockquote.quote-purple h5,\n.dark-mode blockquote.quote-purple h6 {\n  color: #6f42c1;\n}\n\n.dark-mode blockquote.quote-pink {\n  border-color: #e83e8c;\n}\n\n.dark-mode blockquote.quote-pink h1,\n.dark-mode blockquote.quote-pink h2,\n.dark-mode blockquote.quote-pink h3,\n.dark-mode blockquote.quote-pink h4,\n.dark-mode blockquote.quote-pink h5,\n.dark-mode blockquote.quote-pink h6 {\n  color: #e83e8c;\n}\n\n.dark-mode blockquote.quote-red {\n  border-color: #dc3545;\n}\n\n.dark-mode blockquote.quote-red h1,\n.dark-mode blockquote.quote-red h2,\n.dark-mode blockquote.quote-red h3,\n.dark-mode blockquote.quote-red h4,\n.dark-mode blockquote.quote-red h5,\n.dark-mode blockquote.quote-red h6 {\n  color: #dc3545;\n}\n\n.dark-mode blockquote.quote-orange {\n  border-color: #fd7e14;\n}\n\n.dark-mode blockquote.quote-orange h1,\n.dark-mode blockquote.quote-orange h2,\n.dark-mode blockquote.quote-orange h3,\n.dark-mode blockquote.quote-orange h4,\n.dark-mode blockquote.quote-orange h5,\n.dark-mode blockquote.quote-orange h6 {\n  color: #fd7e14;\n}\n\n.dark-mode blockquote.quote-yellow {\n  border-color: #ffc107;\n}\n\n.dark-mode blockquote.quote-yellow h1,\n.dark-mode blockquote.quote-yellow h2,\n.dark-mode blockquote.quote-yellow h3,\n.dark-mode blockquote.quote-yellow h4,\n.dark-mode blockquote.quote-yellow h5,\n.dark-mode blockquote.quote-yellow h6 {\n  color: #ffc107;\n}\n\n.dark-mode blockquote.quote-green {\n  border-color: #28a745;\n}\n\n.dark-mode blockquote.quote-green h1,\n.dark-mode blockquote.quote-green h2,\n.dark-mode blockquote.quote-green h3,\n.dark-mode blockquote.quote-green h4,\n.dark-mode blockquote.quote-green h5,\n.dark-mode blockquote.quote-green h6 {\n  color: #28a745;\n}\n\n.dark-mode blockquote.quote-teal {\n  border-color: #20c997;\n}\n\n.dark-mode blockquote.quote-teal h1,\n.dark-mode blockquote.quote-teal h2,\n.dark-mode blockquote.quote-teal h3,\n.dark-mode blockquote.quote-teal h4,\n.dark-mode blockquote.quote-teal h5,\n.dark-mode blockquote.quote-teal h6 {\n  color: #20c997;\n}\n\n.dark-mode blockquote.quote-cyan {\n  border-color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-cyan h1,\n.dark-mode blockquote.quote-cyan h2,\n.dark-mode blockquote.quote-cyan h3,\n.dark-mode blockquote.quote-cyan h4,\n.dark-mode blockquote.quote-cyan h5,\n.dark-mode blockquote.quote-cyan h6 {\n  color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-white {\n  border-color: #fff;\n}\n\n.dark-mode blockquote.quote-white h1,\n.dark-mode blockquote.quote-white h2,\n.dark-mode blockquote.quote-white h3,\n.dark-mode blockquote.quote-white h4,\n.dark-mode blockquote.quote-white h5,\n.dark-mode blockquote.quote-white h6 {\n  color: #fff;\n}\n\n.dark-mode blockquote.quote-gray {\n  border-color: #6c757d;\n}\n\n.dark-mode blockquote.quote-gray h1,\n.dark-mode blockquote.quote-gray h2,\n.dark-mode blockquote.quote-gray h3,\n.dark-mode blockquote.quote-gray h4,\n.dark-mode blockquote.quote-gray h5,\n.dark-mode blockquote.quote-gray h6 {\n  color: #6c757d;\n}\n\n.dark-mode blockquote.quote-gray-dark {\n  border-color: #343a40;\n}\n\n.dark-mode blockquote.quote-gray-dark h1,\n.dark-mode blockquote.quote-gray-dark h2,\n.dark-mode blockquote.quote-gray-dark h3,\n.dark-mode blockquote.quote-gray-dark h4,\n.dark-mode blockquote.quote-gray-dark h5,\n.dark-mode blockquote.quote-gray-dark h6 {\n  color: #343a40;\n}\n\n.dark-mode .close, .dark-mode .mailbox-attachment-close,\n.dark-mode .mailbox-attachment-close {\n  color: #adb5bd;\n  text-shadow: 0 1px 0 #495057;\n}\n\n.dark-mode .tab-custom-content {\n  border-color: #6c757d;\n}\n\n.dark-mode .list-group-item {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n@media print {\n  .no-print, .main-sidebar,\n  .main-header,\n  .content-header {\n    display: none !important;\n  }\n  .content-wrapper,\n  .main-footer {\n    -webkit-transform: translate(0, 0);\n    transform: translate(0, 0);\n    margin-left: 0 !important;\n    min-height: 0 !important;\n  }\n  .layout-fixed .content-wrapper {\n    padding-top: 0 !important;\n  }\n  .invoice {\n    border: 0;\n    margin: 0;\n    padding: 0;\n    width: 100%;\n  }\n  .invoice-col {\n    float: left;\n    width: 33.3333333%;\n  }\n  .table-responsive {\n    overflow: auto;\n  }\n  .table-responsive > .table tr th,\n  .table-responsive > .table tr td {\n    white-space: normal !important;\n  }\n}\n\n.text-bold,\n.text-bold.table td,\n.text-bold.table th {\n  font-weight: 700;\n}\n\n.text-xs {\n  font-size: 0.75rem !important;\n}\n\n.text-sm {\n  font-size: 0.875rem !important;\n}\n\n.text-md {\n  font-size: 1rem !important;\n}\n\n.text-lg {\n  font-size: 1.25rem !important;\n}\n\n.text-xl {\n  font-size: 2rem !important;\n}\n\n.text-lightblue {\n  color: #3c8dbc !important;\n}\n\n.text-navy {\n  color: #001f3f !important;\n}\n\n.text-olive {\n  color: #3d9970 !important;\n}\n\n.text-lime {\n  color: #01ff70 !important;\n}\n\n.text-fuchsia {\n  color: #f012be !important;\n}\n\n.text-maroon {\n  color: #d81b60 !important;\n}\n\n.text-blue {\n  color: #007bff !important;\n}\n\n.text-indigo {\n  color: #6610f2 !important;\n}\n\n.text-purple {\n  color: #6f42c1 !important;\n}\n\n.text-pink {\n  color: #e83e8c !important;\n}\n\n.text-red {\n  color: #dc3545 !important;\n}\n\n.text-orange {\n  color: #fd7e14 !important;\n}\n\n.text-yellow {\n  color: #ffc107 !important;\n}\n\n.text-green {\n  color: #28a745 !important;\n}\n\n.text-teal {\n  color: #20c997 !important;\n}\n\n.text-cyan {\n  color: #17a2b8 !important;\n}\n\n.text-white {\n  color: #fff !important;\n}\n\n.text-gray {\n  color: #6c757d !important;\n}\n\n.text-gray-dark {\n  color: #343a40 !important;\n}\n\n.dark-mode .text-muted {\n  color: #adb5bd !important;\n}\n\n.dark-mode .text-lightblue {\n  color: #86bad8 !important;\n}\n\n.dark-mode .text-navy {\n  color: #002c59 !important;\n}\n\n.dark-mode .text-olive {\n  color: #74c8a3 !important;\n}\n\n.dark-mode .text-lime {\n  color: #67ffa9 !important;\n}\n\n.dark-mode .text-fuchsia {\n  color: #f672d8 !important;\n}\n\n.dark-mode .text-maroon {\n  color: #ed6c9b !important;\n}\n\n.dark-mode .text-blue {\n  color: #3f6791 !important;\n}\n\n.dark-mode .text-indigo {\n  color: #6610f2 !important;\n}\n\n.dark-mode .text-purple {\n  color: #6f42c1 !important;\n}\n\n.dark-mode .text-pink {\n  color: #e83e8c !important;\n}\n\n.dark-mode .text-red {\n  color: #e74c3c !important;\n}\n\n.dark-mode .text-orange {\n  color: #fd7e14 !important;\n}\n\n.dark-mode .text-yellow {\n  color: #f39c12 !important;\n}\n\n.dark-mode .text-green {\n  color: #00bc8c !important;\n}\n\n.dark-mode .text-teal {\n  color: #20c997 !important;\n}\n\n.dark-mode .text-cyan {\n  color: #3498db !important;\n}\n\n.dark-mode .text-white {\n  color: #fff !important;\n}\n\n.dark-mode .text-gray {\n  color: #6c757d !important;\n}\n\n.dark-mode .text-gray-dark {\n  color: #343a40 !important;\n}\n\n.elevation-0 {\n  box-shadow: none !important;\n}\n\n.elevation-1 {\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) !important;\n}\n\n.elevation-2 {\n  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) !important;\n}\n\n.elevation-3 {\n  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) !important;\n}\n\n.elevation-4 {\n  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22) !important;\n}\n\n.elevation-5 {\n  box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22) !important;\n}\n\n.bg-primary {\n  background-color: #007bff !important;\n}\n\n.bg-primary,\n.bg-primary > a {\n  color: #fff !important;\n}\n\n.bg-primary.btn:hover {\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-primary.btn:not(:disabled):not(.disabled):active, .bg-primary.btn:not(:disabled):not(.disabled).active, .bg-primary.btn:active, .bg-primary.btn.active {\n  background-color: #0062cc !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-secondary {\n  background-color: #6c757d !important;\n}\n\n.bg-secondary,\n.bg-secondary > a {\n  color: #fff !important;\n}\n\n.bg-secondary.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-secondary.btn:not(:disabled):not(.disabled):active, .bg-secondary.btn:not(:disabled):not(.disabled).active, .bg-secondary.btn:active, .bg-secondary.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-success {\n  background-color: #28a745 !important;\n}\n\n.bg-success,\n.bg-success > a {\n  color: #fff !important;\n}\n\n.bg-success.btn:hover {\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-success.btn:not(:disabled):not(.disabled):active, .bg-success.btn:not(:disabled):not(.disabled).active, .bg-success.btn:active, .bg-success.btn.active {\n  background-color: #1e7e34 !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-info {\n  background-color: #17a2b8 !important;\n}\n\n.bg-info,\n.bg-info > a {\n  color: #fff !important;\n}\n\n.bg-info.btn:hover {\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-info.btn:not(:disabled):not(.disabled):active, .bg-info.btn:not(:disabled):not(.disabled).active, .bg-info.btn:active, .bg-info.btn.active {\n  background-color: #117a8b !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-warning {\n  background-color: #ffc107 !important;\n}\n\n.bg-warning,\n.bg-warning > a {\n  color: #1f2d3d !important;\n}\n\n.bg-warning.btn:hover {\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-warning.btn:not(:disabled):not(.disabled):active, .bg-warning.btn:not(:disabled):not(.disabled).active, .bg-warning.btn:active, .bg-warning.btn.active {\n  background-color: #d39e00 !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-danger {\n  background-color: #dc3545 !important;\n}\n\n.bg-danger,\n.bg-danger > a {\n  color: #fff !important;\n}\n\n.bg-danger.btn:hover {\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-danger.btn:not(:disabled):not(.disabled):active, .bg-danger.btn:not(:disabled):not(.disabled).active, .bg-danger.btn:active, .bg-danger.btn.active {\n  background-color: #bd2130 !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-light {\n  background-color: #f8f9fa !important;\n}\n\n.bg-light,\n.bg-light > a {\n  color: #1f2d3d !important;\n}\n\n.bg-light.btn:hover {\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.bg-light.btn:not(:disabled):not(.disabled):active, .bg-light.btn:not(:disabled):not(.disabled).active, .bg-light.btn:active, .bg-light.btn.active {\n  background-color: #dae0e5 !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.bg-dark {\n  background-color: #343a40 !important;\n}\n\n.bg-dark,\n.bg-dark > a {\n  color: #fff !important;\n}\n\n.bg-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-dark.btn:not(:disabled):not(.disabled):active, .bg-dark.btn:not(:disabled):not(.disabled).active, .bg-dark.btn:active, .bg-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-lightblue {\n  background-color: #3c8dbc !important;\n}\n\n.bg-lightblue,\n.bg-lightblue > a {\n  color: #fff !important;\n}\n\n.bg-lightblue.btn:hover {\n  border-color: #307095;\n  color: #ececec;\n}\n\n.bg-lightblue.btn:not(:disabled):not(.disabled):active, .bg-lightblue.btn:not(:disabled):not(.disabled).active, .bg-lightblue.btn:active, .bg-lightblue.btn.active {\n  background-color: #307095 !important;\n  border-color: #2d698c;\n  color: #fff;\n}\n\n.bg-navy {\n  background-color: #001f3f !important;\n}\n\n.bg-navy,\n.bg-navy > a {\n  color: #fff !important;\n}\n\n.bg-navy.btn:hover {\n  border-color: #00060c;\n  color: #ececec;\n}\n\n.bg-navy.btn:not(:disabled):not(.disabled):active, .bg-navy.btn:not(:disabled):not(.disabled).active, .bg-navy.btn:active, .bg-navy.btn.active {\n  background-color: #00060c !important;\n  border-color: black;\n  color: #fff;\n}\n\n.bg-olive {\n  background-color: #3d9970 !important;\n}\n\n.bg-olive,\n.bg-olive > a {\n  color: #fff !important;\n}\n\n.bg-olive.btn:hover {\n  border-color: #2e7555;\n  color: #ececec;\n}\n\n.bg-olive.btn:not(:disabled):not(.disabled):active, .bg-olive.btn:not(:disabled):not(.disabled).active, .bg-olive.btn:active, .bg-olive.btn.active {\n  background-color: #2e7555 !important;\n  border-color: #2b6b4f;\n  color: #fff;\n}\n\n.bg-lime {\n  background-color: #01ff70 !important;\n}\n\n.bg-lime,\n.bg-lime > a {\n  color: #1f2d3d !important;\n}\n\n.bg-lime.btn:hover {\n  border-color: #00cd5a;\n  color: #121a24;\n}\n\n.bg-lime.btn:not(:disabled):not(.disabled):active, .bg-lime.btn:not(:disabled):not(.disabled).active, .bg-lime.btn:active, .bg-lime.btn.active {\n  background-color: #00cd5a !important;\n  border-color: #00c054;\n  color: #fff;\n}\n\n.bg-fuchsia {\n  background-color: #f012be !important;\n}\n\n.bg-fuchsia,\n.bg-fuchsia > a {\n  color: #fff !important;\n}\n\n.bg-fuchsia.btn:hover {\n  border-color: #c30c9a;\n  color: #ececec;\n}\n\n.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-fuchsia.btn:not(:disabled):not(.disabled).active, .bg-fuchsia.btn:active, .bg-fuchsia.btn.active {\n  background-color: #c30c9a !important;\n  border-color: #b70c90;\n  color: #fff;\n}\n\n.bg-maroon {\n  background-color: #d81b60 !important;\n}\n\n.bg-maroon,\n.bg-maroon > a {\n  color: #fff !important;\n}\n\n.bg-maroon.btn:hover {\n  border-color: #ab154c;\n  color: #ececec;\n}\n\n.bg-maroon.btn:not(:disabled):not(.disabled):active, .bg-maroon.btn:not(:disabled):not(.disabled).active, .bg-maroon.btn:active, .bg-maroon.btn.active {\n  background-color: #ab154c !important;\n  border-color: #9f1447;\n  color: #fff;\n}\n\n.bg-blue {\n  background-color: #007bff !important;\n}\n\n.bg-blue,\n.bg-blue > a {\n  color: #fff !important;\n}\n\n.bg-blue.btn:hover {\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-blue.btn:not(:disabled):not(.disabled):active, .bg-blue.btn:not(:disabled):not(.disabled).active, .bg-blue.btn:active, .bg-blue.btn.active {\n  background-color: #0062cc !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-indigo {\n  background-color: #6610f2 !important;\n}\n\n.bg-indigo,\n.bg-indigo > a {\n  color: #fff !important;\n}\n\n.bg-indigo.btn:hover {\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.bg-indigo.btn:not(:disabled):not(.disabled):active, .bg-indigo.btn:not(:disabled):not(.disabled).active, .bg-indigo.btn:active, .bg-indigo.btn.active {\n  background-color: #510bc4 !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.bg-purple {\n  background-color: #6f42c1 !important;\n}\n\n.bg-purple,\n.bg-purple > a {\n  color: #fff !important;\n}\n\n.bg-purple.btn:hover {\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.bg-purple.btn:not(:disabled):not(.disabled):active, .bg-purple.btn:not(:disabled):not(.disabled).active, .bg-purple.btn:active, .bg-purple.btn.active {\n  background-color: #59339d !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.bg-pink {\n  background-color: #e83e8c !important;\n}\n\n.bg-pink,\n.bg-pink > a {\n  color: #fff !important;\n}\n\n.bg-pink.btn:hover {\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.bg-pink.btn:not(:disabled):not(.disabled):active, .bg-pink.btn:not(:disabled):not(.disabled).active, .bg-pink.btn:active, .bg-pink.btn.active {\n  background-color: #d91a72 !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.bg-red {\n  background-color: #dc3545 !important;\n}\n\n.bg-red,\n.bg-red > a {\n  color: #fff !important;\n}\n\n.bg-red.btn:hover {\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-red.btn:not(:disabled):not(.disabled):active, .bg-red.btn:not(:disabled):not(.disabled).active, .bg-red.btn:active, .bg-red.btn.active {\n  background-color: #bd2130 !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-orange {\n  background-color: #fd7e14 !important;\n}\n\n.bg-orange,\n.bg-orange > a {\n  color: #1f2d3d !important;\n}\n\n.bg-orange.btn:hover {\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.bg-orange.btn:not(:disabled):not(.disabled):active, .bg-orange.btn:not(:disabled):not(.disabled).active, .bg-orange.btn:active, .bg-orange.btn.active {\n  background-color: #dc6502 !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.bg-yellow {\n  background-color: #ffc107 !important;\n}\n\n.bg-yellow,\n.bg-yellow > a {\n  color: #1f2d3d !important;\n}\n\n.bg-yellow.btn:hover {\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-yellow.btn:not(:disabled):not(.disabled):active, .bg-yellow.btn:not(:disabled):not(.disabled).active, .bg-yellow.btn:active, .bg-yellow.btn.active {\n  background-color: #d39e00 !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-green {\n  background-color: #28a745 !important;\n}\n\n.bg-green,\n.bg-green > a {\n  color: #fff !important;\n}\n\n.bg-green.btn:hover {\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-green.btn:not(:disabled):not(.disabled):active, .bg-green.btn:not(:disabled):not(.disabled).active, .bg-green.btn:active, .bg-green.btn.active {\n  background-color: #1e7e34 !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-teal {\n  background-color: #20c997 !important;\n}\n\n.bg-teal,\n.bg-teal > a {\n  color: #fff !important;\n}\n\n.bg-teal.btn:hover {\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.bg-teal.btn:not(:disabled):not(.disabled):active, .bg-teal.btn:not(:disabled):not(.disabled).active, .bg-teal.btn:active, .bg-teal.btn.active {\n  background-color: #199d76 !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.bg-cyan {\n  background-color: #17a2b8 !important;\n}\n\n.bg-cyan,\n.bg-cyan > a {\n  color: #fff !important;\n}\n\n.bg-cyan.btn:hover {\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-cyan.btn:not(:disabled):not(.disabled):active, .bg-cyan.btn:not(:disabled):not(.disabled).active, .bg-cyan.btn:active, .bg-cyan.btn.active {\n  background-color: #117a8b !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-white {\n  background-color: #fff !important;\n}\n\n.bg-white,\n.bg-white > a {\n  color: #1f2d3d !important;\n}\n\n.bg-white.btn:hover {\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.bg-white.btn:not(:disabled):not(.disabled):active, .bg-white.btn:not(:disabled):not(.disabled).active, .bg-white.btn:active, .bg-white.btn.active {\n  background-color: #e6e6e6 !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.bg-gray {\n  background-color: #6c757d !important;\n}\n\n.bg-gray,\n.bg-gray > a {\n  color: #fff !important;\n}\n\n.bg-gray.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gray.btn:not(:disabled):not(.disabled):active, .bg-gray.btn:not(:disabled):not(.disabled).active, .bg-gray.btn:active, .bg-gray.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gray-dark {\n  background-color: #343a40 !important;\n}\n\n.bg-gray-dark,\n.bg-gray-dark > a {\n  color: #fff !important;\n}\n\n.bg-gray-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gray-dark.btn:not(:disabled):not(.disabled).active, .bg-gray-dark.btn:active, .bg-gray-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n@media print {\n  .table td.bg-primary,\n  .table th.bg-primary {\n    background-color: #007bff !important;\n  }\n  .table td.bg-primary,\n  .table td.bg-primary > a,\n  .table th.bg-primary,\n  .table th.bg-primary > a {\n    color: #fff !important;\n  }\n  .table td.bg-primary.btn:hover,\n  .table th.bg-primary.btn:hover {\n    border-color: #0062cc;\n    color: #ececec;\n  }\n  .table td.bg-primary.btn:not(:disabled):not(.disabled):active, .table td.bg-primary.btn:not(:disabled):not(.disabled).active, .table td.bg-primary.btn:active, .table td.bg-primary.btn.active,\n  .table th.bg-primary.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-primary.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-primary.btn:active,\n  .table th.bg-primary.btn.active {\n    background-color: #0062cc !important;\n    border-color: #005cbf;\n    color: #fff;\n  }\n  .table td.bg-secondary,\n  .table th.bg-secondary {\n    background-color: #6c757d !important;\n  }\n  .table td.bg-secondary,\n  .table td.bg-secondary > a,\n  .table th.bg-secondary,\n  .table th.bg-secondary > a {\n    color: #fff !important;\n  }\n  .table td.bg-secondary.btn:hover,\n  .table th.bg-secondary.btn:hover {\n    border-color: #545b62;\n    color: #ececec;\n  }\n  .table td.bg-secondary.btn:not(:disabled):not(.disabled):active, .table td.bg-secondary.btn:not(:disabled):not(.disabled).active, .table td.bg-secondary.btn:active, .table td.bg-secondary.btn.active,\n  .table th.bg-secondary.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-secondary.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-secondary.btn:active,\n  .table th.bg-secondary.btn.active {\n    background-color: #545b62 !important;\n    border-color: #4e555b;\n    color: #fff;\n  }\n  .table td.bg-success,\n  .table th.bg-success {\n    background-color: #28a745 !important;\n  }\n  .table td.bg-success,\n  .table td.bg-success > a,\n  .table th.bg-success,\n  .table th.bg-success > a {\n    color: #fff !important;\n  }\n  .table td.bg-success.btn:hover,\n  .table th.bg-success.btn:hover {\n    border-color: #1e7e34;\n    color: #ececec;\n  }\n  .table td.bg-success.btn:not(:disabled):not(.disabled):active, .table td.bg-success.btn:not(:disabled):not(.disabled).active, .table td.bg-success.btn:active, .table td.bg-success.btn.active,\n  .table th.bg-success.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-success.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-success.btn:active,\n  .table th.bg-success.btn.active {\n    background-color: #1e7e34 !important;\n    border-color: #1c7430;\n    color: #fff;\n  }\n  .table td.bg-info,\n  .table th.bg-info {\n    background-color: #17a2b8 !important;\n  }\n  .table td.bg-info,\n  .table td.bg-info > a,\n  .table th.bg-info,\n  .table th.bg-info > a {\n    color: #fff !important;\n  }\n  .table td.bg-info.btn:hover,\n  .table th.bg-info.btn:hover {\n    border-color: #117a8b;\n    color: #ececec;\n  }\n  .table td.bg-info.btn:not(:disabled):not(.disabled):active, .table td.bg-info.btn:not(:disabled):not(.disabled).active, .table td.bg-info.btn:active, .table td.bg-info.btn.active,\n  .table th.bg-info.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-info.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-info.btn:active,\n  .table th.bg-info.btn.active {\n    background-color: #117a8b !important;\n    border-color: #10707f;\n    color: #fff;\n  }\n  .table td.bg-warning,\n  .table th.bg-warning {\n    background-color: #ffc107 !important;\n  }\n  .table td.bg-warning,\n  .table td.bg-warning > a,\n  .table th.bg-warning,\n  .table th.bg-warning > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-warning.btn:hover,\n  .table th.bg-warning.btn:hover {\n    border-color: #d39e00;\n    color: #121a24;\n  }\n  .table td.bg-warning.btn:not(:disabled):not(.disabled):active, .table td.bg-warning.btn:not(:disabled):not(.disabled).active, .table td.bg-warning.btn:active, .table td.bg-warning.btn.active,\n  .table th.bg-warning.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-warning.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-warning.btn:active,\n  .table th.bg-warning.btn.active {\n    background-color: #d39e00 !important;\n    border-color: #c69500;\n    color: #1f2d3d;\n  }\n  .table td.bg-danger,\n  .table th.bg-danger {\n    background-color: #dc3545 !important;\n  }\n  .table td.bg-danger,\n  .table td.bg-danger > a,\n  .table th.bg-danger,\n  .table th.bg-danger > a {\n    color: #fff !important;\n  }\n  .table td.bg-danger.btn:hover,\n  .table th.bg-danger.btn:hover {\n    border-color: #bd2130;\n    color: #ececec;\n  }\n  .table td.bg-danger.btn:not(:disabled):not(.disabled):active, .table td.bg-danger.btn:not(:disabled):not(.disabled).active, .table td.bg-danger.btn:active, .table td.bg-danger.btn.active,\n  .table th.bg-danger.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-danger.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-danger.btn:active,\n  .table th.bg-danger.btn.active {\n    background-color: #bd2130 !important;\n    border-color: #b21f2d;\n    color: #fff;\n  }\n  .table td.bg-light,\n  .table th.bg-light {\n    background-color: #f8f9fa !important;\n  }\n  .table td.bg-light,\n  .table td.bg-light > a,\n  .table th.bg-light,\n  .table th.bg-light > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-light.btn:hover,\n  .table th.bg-light.btn:hover {\n    border-color: #dae0e5;\n    color: #121a24;\n  }\n  .table td.bg-light.btn:not(:disabled):not(.disabled):active, .table td.bg-light.btn:not(:disabled):not(.disabled).active, .table td.bg-light.btn:active, .table td.bg-light.btn.active,\n  .table th.bg-light.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-light.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-light.btn:active,\n  .table th.bg-light.btn.active {\n    background-color: #dae0e5 !important;\n    border-color: #d3d9df;\n    color: #1f2d3d;\n  }\n  .table td.bg-dark,\n  .table th.bg-dark {\n    background-color: #343a40 !important;\n  }\n  .table td.bg-dark,\n  .table td.bg-dark > a,\n  .table th.bg-dark,\n  .table th.bg-dark > a {\n    color: #fff !important;\n  }\n  .table td.bg-dark.btn:hover,\n  .table th.bg-dark.btn:hover {\n    border-color: #1d2124;\n    color: #ececec;\n  }\n  .table td.bg-dark.btn:not(:disabled):not(.disabled):active, .table td.bg-dark.btn:not(:disabled):not(.disabled).active, .table td.bg-dark.btn:active, .table td.bg-dark.btn.active,\n  .table th.bg-dark.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-dark.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-dark.btn:active,\n  .table th.bg-dark.btn.active {\n    background-color: #1d2124 !important;\n    border-color: #171a1d;\n    color: #fff;\n  }\n  .table td.bg-lightblue,\n  .table th.bg-lightblue {\n    background-color: #3c8dbc !important;\n  }\n  .table td.bg-lightblue,\n  .table td.bg-lightblue > a,\n  .table th.bg-lightblue,\n  .table th.bg-lightblue > a {\n    color: #fff !important;\n  }\n  .table td.bg-lightblue.btn:hover,\n  .table th.bg-lightblue.btn:hover {\n    border-color: #307095;\n    color: #ececec;\n  }\n  .table td.bg-lightblue.btn:not(:disabled):not(.disabled):active, .table td.bg-lightblue.btn:not(:disabled):not(.disabled).active, .table td.bg-lightblue.btn:active, .table td.bg-lightblue.btn.active,\n  .table th.bg-lightblue.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-lightblue.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-lightblue.btn:active,\n  .table th.bg-lightblue.btn.active {\n    background-color: #307095 !important;\n    border-color: #2d698c;\n    color: #fff;\n  }\n  .table td.bg-navy,\n  .table th.bg-navy {\n    background-color: #001f3f !important;\n  }\n  .table td.bg-navy,\n  .table td.bg-navy > a,\n  .table th.bg-navy,\n  .table th.bg-navy > a {\n    color: #fff !important;\n  }\n  .table td.bg-navy.btn:hover,\n  .table th.bg-navy.btn:hover {\n    border-color: #00060c;\n    color: #ececec;\n  }\n  .table td.bg-navy.btn:not(:disabled):not(.disabled):active, .table td.bg-navy.btn:not(:disabled):not(.disabled).active, .table td.bg-navy.btn:active, .table td.bg-navy.btn.active,\n  .table th.bg-navy.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-navy.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-navy.btn:active,\n  .table th.bg-navy.btn.active {\n    background-color: #00060c !important;\n    border-color: black;\n    color: #fff;\n  }\n  .table td.bg-olive,\n  .table th.bg-olive {\n    background-color: #3d9970 !important;\n  }\n  .table td.bg-olive,\n  .table td.bg-olive > a,\n  .table th.bg-olive,\n  .table th.bg-olive > a {\n    color: #fff !important;\n  }\n  .table td.bg-olive.btn:hover,\n  .table th.bg-olive.btn:hover {\n    border-color: #2e7555;\n    color: #ececec;\n  }\n  .table td.bg-olive.btn:not(:disabled):not(.disabled):active, .table td.bg-olive.btn:not(:disabled):not(.disabled).active, .table td.bg-olive.btn:active, .table td.bg-olive.btn.active,\n  .table th.bg-olive.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-olive.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-olive.btn:active,\n  .table th.bg-olive.btn.active {\n    background-color: #2e7555 !important;\n    border-color: #2b6b4f;\n    color: #fff;\n  }\n  .table td.bg-lime,\n  .table th.bg-lime {\n    background-color: #01ff70 !important;\n  }\n  .table td.bg-lime,\n  .table td.bg-lime > a,\n  .table th.bg-lime,\n  .table th.bg-lime > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-lime.btn:hover,\n  .table th.bg-lime.btn:hover {\n    border-color: #00cd5a;\n    color: #121a24;\n  }\n  .table td.bg-lime.btn:not(:disabled):not(.disabled):active, .table td.bg-lime.btn:not(:disabled):not(.disabled).active, .table td.bg-lime.btn:active, .table td.bg-lime.btn.active,\n  .table th.bg-lime.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-lime.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-lime.btn:active,\n  .table th.bg-lime.btn.active {\n    background-color: #00cd5a !important;\n    border-color: #00c054;\n    color: #fff;\n  }\n  .table td.bg-fuchsia,\n  .table th.bg-fuchsia {\n    background-color: #f012be !important;\n  }\n  .table td.bg-fuchsia,\n  .table td.bg-fuchsia > a,\n  .table th.bg-fuchsia,\n  .table th.bg-fuchsia > a {\n    color: #fff !important;\n  }\n  .table td.bg-fuchsia.btn:hover,\n  .table th.bg-fuchsia.btn:hover {\n    border-color: #c30c9a;\n    color: #ececec;\n  }\n  .table td.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .table td.bg-fuchsia.btn:not(:disabled):not(.disabled).active, .table td.bg-fuchsia.btn:active, .table td.bg-fuchsia.btn.active,\n  .table th.bg-fuchsia.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-fuchsia.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-fuchsia.btn:active,\n  .table th.bg-fuchsia.btn.active {\n    background-color: #c30c9a !important;\n    border-color: #b70c90;\n    color: #fff;\n  }\n  .table td.bg-maroon,\n  .table th.bg-maroon {\n    background-color: #d81b60 !important;\n  }\n  .table td.bg-maroon,\n  .table td.bg-maroon > a,\n  .table th.bg-maroon,\n  .table th.bg-maroon > a {\n    color: #fff !important;\n  }\n  .table td.bg-maroon.btn:hover,\n  .table th.bg-maroon.btn:hover {\n    border-color: #ab154c;\n    color: #ececec;\n  }\n  .table td.bg-maroon.btn:not(:disabled):not(.disabled):active, .table td.bg-maroon.btn:not(:disabled):not(.disabled).active, .table td.bg-maroon.btn:active, .table td.bg-maroon.btn.active,\n  .table th.bg-maroon.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-maroon.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-maroon.btn:active,\n  .table th.bg-maroon.btn.active {\n    background-color: #ab154c !important;\n    border-color: #9f1447;\n    color: #fff;\n  }\n  .table td.bg-blue,\n  .table th.bg-blue {\n    background-color: #007bff !important;\n  }\n  .table td.bg-blue,\n  .table td.bg-blue > a,\n  .table th.bg-blue,\n  .table th.bg-blue > a {\n    color: #fff !important;\n  }\n  .table td.bg-blue.btn:hover,\n  .table th.bg-blue.btn:hover {\n    border-color: #0062cc;\n    color: #ececec;\n  }\n  .table td.bg-blue.btn:not(:disabled):not(.disabled):active, .table td.bg-blue.btn:not(:disabled):not(.disabled).active, .table td.bg-blue.btn:active, .table td.bg-blue.btn.active,\n  .table th.bg-blue.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-blue.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-blue.btn:active,\n  .table th.bg-blue.btn.active {\n    background-color: #0062cc !important;\n    border-color: #005cbf;\n    color: #fff;\n  }\n  .table td.bg-indigo,\n  .table th.bg-indigo {\n    background-color: #6610f2 !important;\n  }\n  .table td.bg-indigo,\n  .table td.bg-indigo > a,\n  .table th.bg-indigo,\n  .table th.bg-indigo > a {\n    color: #fff !important;\n  }\n  .table td.bg-indigo.btn:hover,\n  .table th.bg-indigo.btn:hover {\n    border-color: #510bc4;\n    color: #ececec;\n  }\n  .table td.bg-indigo.btn:not(:disabled):not(.disabled):active, .table td.bg-indigo.btn:not(:disabled):not(.disabled).active, .table td.bg-indigo.btn:active, .table td.bg-indigo.btn.active,\n  .table th.bg-indigo.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-indigo.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-indigo.btn:active,\n  .table th.bg-indigo.btn.active {\n    background-color: #510bc4 !important;\n    border-color: #4c0ab8;\n    color: #fff;\n  }\n  .table td.bg-purple,\n  .table th.bg-purple {\n    background-color: #6f42c1 !important;\n  }\n  .table td.bg-purple,\n  .table td.bg-purple > a,\n  .table th.bg-purple,\n  .table th.bg-purple > a {\n    color: #fff !important;\n  }\n  .table td.bg-purple.btn:hover,\n  .table th.bg-purple.btn:hover {\n    border-color: #59339d;\n    color: #ececec;\n  }\n  .table td.bg-purple.btn:not(:disabled):not(.disabled):active, .table td.bg-purple.btn:not(:disabled):not(.disabled).active, .table td.bg-purple.btn:active, .table td.bg-purple.btn.active,\n  .table th.bg-purple.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-purple.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-purple.btn:active,\n  .table th.bg-purple.btn.active {\n    background-color: #59339d !important;\n    border-color: #533093;\n    color: #fff;\n  }\n  .table td.bg-pink,\n  .table th.bg-pink {\n    background-color: #e83e8c !important;\n  }\n  .table td.bg-pink,\n  .table td.bg-pink > a,\n  .table th.bg-pink,\n  .table th.bg-pink > a {\n    color: #fff !important;\n  }\n  .table td.bg-pink.btn:hover,\n  .table th.bg-pink.btn:hover {\n    border-color: #d91a72;\n    color: #ececec;\n  }\n  .table td.bg-pink.btn:not(:disabled):not(.disabled):active, .table td.bg-pink.btn:not(:disabled):not(.disabled).active, .table td.bg-pink.btn:active, .table td.bg-pink.btn.active,\n  .table th.bg-pink.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-pink.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-pink.btn:active,\n  .table th.bg-pink.btn.active {\n    background-color: #d91a72 !important;\n    border-color: #ce196c;\n    color: #fff;\n  }\n  .table td.bg-red,\n  .table th.bg-red {\n    background-color: #dc3545 !important;\n  }\n  .table td.bg-red,\n  .table td.bg-red > a,\n  .table th.bg-red,\n  .table th.bg-red > a {\n    color: #fff !important;\n  }\n  .table td.bg-red.btn:hover,\n  .table th.bg-red.btn:hover {\n    border-color: #bd2130;\n    color: #ececec;\n  }\n  .table td.bg-red.btn:not(:disabled):not(.disabled):active, .table td.bg-red.btn:not(:disabled):not(.disabled).active, .table td.bg-red.btn:active, .table td.bg-red.btn.active,\n  .table th.bg-red.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-red.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-red.btn:active,\n  .table th.bg-red.btn.active {\n    background-color: #bd2130 !important;\n    border-color: #b21f2d;\n    color: #fff;\n  }\n  .table td.bg-orange,\n  .table th.bg-orange {\n    background-color: #fd7e14 !important;\n  }\n  .table td.bg-orange,\n  .table td.bg-orange > a,\n  .table th.bg-orange,\n  .table th.bg-orange > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-orange.btn:hover,\n  .table th.bg-orange.btn:hover {\n    border-color: #dc6502;\n    color: #121a24;\n  }\n  .table td.bg-orange.btn:not(:disabled):not(.disabled):active, .table td.bg-orange.btn:not(:disabled):not(.disabled).active, .table td.bg-orange.btn:active, .table td.bg-orange.btn.active,\n  .table th.bg-orange.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-orange.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-orange.btn:active,\n  .table th.bg-orange.btn.active {\n    background-color: #dc6502 !important;\n    border-color: #cf5f02;\n    color: #fff;\n  }\n  .table td.bg-yellow,\n  .table th.bg-yellow {\n    background-color: #ffc107 !important;\n  }\n  .table td.bg-yellow,\n  .table td.bg-yellow > a,\n  .table th.bg-yellow,\n  .table th.bg-yellow > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-yellow.btn:hover,\n  .table th.bg-yellow.btn:hover {\n    border-color: #d39e00;\n    color: #121a24;\n  }\n  .table td.bg-yellow.btn:not(:disabled):not(.disabled):active, .table td.bg-yellow.btn:not(:disabled):not(.disabled).active, .table td.bg-yellow.btn:active, .table td.bg-yellow.btn.active,\n  .table th.bg-yellow.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-yellow.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-yellow.btn:active,\n  .table th.bg-yellow.btn.active {\n    background-color: #d39e00 !important;\n    border-color: #c69500;\n    color: #1f2d3d;\n  }\n  .table td.bg-green,\n  .table th.bg-green {\n    background-color: #28a745 !important;\n  }\n  .table td.bg-green,\n  .table td.bg-green > a,\n  .table th.bg-green,\n  .table th.bg-green > a {\n    color: #fff !important;\n  }\n  .table td.bg-green.btn:hover,\n  .table th.bg-green.btn:hover {\n    border-color: #1e7e34;\n    color: #ececec;\n  }\n  .table td.bg-green.btn:not(:disabled):not(.disabled):active, .table td.bg-green.btn:not(:disabled):not(.disabled).active, .table td.bg-green.btn:active, .table td.bg-green.btn.active,\n  .table th.bg-green.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-green.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-green.btn:active,\n  .table th.bg-green.btn.active {\n    background-color: #1e7e34 !important;\n    border-color: #1c7430;\n    color: #fff;\n  }\n  .table td.bg-teal,\n  .table th.bg-teal {\n    background-color: #20c997 !important;\n  }\n  .table td.bg-teal,\n  .table td.bg-teal > a,\n  .table th.bg-teal,\n  .table th.bg-teal > a {\n    color: #fff !important;\n  }\n  .table td.bg-teal.btn:hover,\n  .table th.bg-teal.btn:hover {\n    border-color: #199d76;\n    color: #ececec;\n  }\n  .table td.bg-teal.btn:not(:disabled):not(.disabled):active, .table td.bg-teal.btn:not(:disabled):not(.disabled).active, .table td.bg-teal.btn:active, .table td.bg-teal.btn.active,\n  .table th.bg-teal.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-teal.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-teal.btn:active,\n  .table th.bg-teal.btn.active {\n    background-color: #199d76 !important;\n    border-color: #17926e;\n    color: #fff;\n  }\n  .table td.bg-cyan,\n  .table th.bg-cyan {\n    background-color: #17a2b8 !important;\n  }\n  .table td.bg-cyan,\n  .table td.bg-cyan > a,\n  .table th.bg-cyan,\n  .table th.bg-cyan > a {\n    color: #fff !important;\n  }\n  .table td.bg-cyan.btn:hover,\n  .table th.bg-cyan.btn:hover {\n    border-color: #117a8b;\n    color: #ececec;\n  }\n  .table td.bg-cyan.btn:not(:disabled):not(.disabled):active, .table td.bg-cyan.btn:not(:disabled):not(.disabled).active, .table td.bg-cyan.btn:active, .table td.bg-cyan.btn.active,\n  .table th.bg-cyan.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-cyan.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-cyan.btn:active,\n  .table th.bg-cyan.btn.active {\n    background-color: #117a8b !important;\n    border-color: #10707f;\n    color: #fff;\n  }\n  .table td.bg-white,\n  .table th.bg-white {\n    background-color: #fff !important;\n  }\n  .table td.bg-white,\n  .table td.bg-white > a,\n  .table th.bg-white,\n  .table th.bg-white > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-white.btn:hover,\n  .table th.bg-white.btn:hover {\n    border-color: #e6e6e6;\n    color: #121a24;\n  }\n  .table td.bg-white.btn:not(:disabled):not(.disabled):active, .table td.bg-white.btn:not(:disabled):not(.disabled).active, .table td.bg-white.btn:active, .table td.bg-white.btn.active,\n  .table th.bg-white.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-white.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-white.btn:active,\n  .table th.bg-white.btn.active {\n    background-color: #e6e6e6 !important;\n    border-color: #dfdfdf;\n    color: #1f2d3d;\n  }\n  .table td.bg-gray,\n  .table th.bg-gray {\n    background-color: #6c757d !important;\n  }\n  .table td.bg-gray,\n  .table td.bg-gray > a,\n  .table th.bg-gray,\n  .table th.bg-gray > a {\n    color: #fff !important;\n  }\n  .table td.bg-gray.btn:hover,\n  .table th.bg-gray.btn:hover {\n    border-color: #545b62;\n    color: #ececec;\n  }\n  .table td.bg-gray.btn:not(:disabled):not(.disabled):active, .table td.bg-gray.btn:not(:disabled):not(.disabled).active, .table td.bg-gray.btn:active, .table td.bg-gray.btn.active,\n  .table th.bg-gray.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-gray.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-gray.btn:active,\n  .table th.bg-gray.btn.active {\n    background-color: #545b62 !important;\n    border-color: #4e555b;\n    color: #fff;\n  }\n  .table td.bg-gray-dark,\n  .table th.bg-gray-dark {\n    background-color: #343a40 !important;\n  }\n  .table td.bg-gray-dark,\n  .table td.bg-gray-dark > a,\n  .table th.bg-gray-dark,\n  .table th.bg-gray-dark > a {\n    color: #fff !important;\n  }\n  .table td.bg-gray-dark.btn:hover,\n  .table th.bg-gray-dark.btn:hover {\n    border-color: #1d2124;\n    color: #ececec;\n  }\n  .table td.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .table td.bg-gray-dark.btn:not(:disabled):not(.disabled).active, .table td.bg-gray-dark.btn:active, .table td.bg-gray-dark.btn.active,\n  .table th.bg-gray-dark.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-gray-dark.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-gray-dark.btn:active,\n  .table th.bg-gray-dark.btn.active {\n    background-color: #1d2124 !important;\n    border-color: #171a1d;\n    color: #fff;\n  }\n}\n\n.bg-gray {\n  background-color: #adb5bd;\n  color: #1f2d3d;\n}\n\n.bg-gray-light {\n  background-color: #f2f4f5;\n  color: #1f2d3d !important;\n}\n\n.bg-black {\n  background-color: #000;\n  color: #fff !important;\n}\n\n.bg-white {\n  background-color: #fff;\n  color: #1f2d3d !important;\n}\n\n.bg-gradient-primary {\n  background: #007bff linear-gradient(180deg, #268fff, #007bff) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .bg-gradient-primary.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-primary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-primary.btn:hover {\n  background: #007bff linear-gradient(180deg, #267fde, #0069d9) repeat-x !important;\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .bg-gradient-primary.btn:not(:disabled):not(.disabled).active, .bg-gradient-primary.btn:active, .bg-gradient-primary.btn.active {\n  background: #007bff linear-gradient(180deg, #267ad4, #0062cc) repeat-x !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-gradient-primary.btn:disabled, .bg-gradient-primary.btn.disabled {\n  background-image: none !important;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.bg-gradient-secondary {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-secondary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-secondary.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active, .bg-gradient-secondary.btn:active, .bg-gradient-secondary.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gradient-secondary.btn:disabled, .bg-gradient-secondary.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.bg-gradient-success {\n  background: #28a745 linear-gradient(180deg, #48b461, #28a745) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-success.btn:not(:disabled):not(.disabled):active, .bg-gradient-success.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-success.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-success.btn:hover {\n  background: #28a745 linear-gradient(180deg, #429a56, #218838) repeat-x !important;\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-gradient-success.btn:not(:disabled):not(.disabled):active, .bg-gradient-success.btn:not(:disabled):not(.disabled).active, .bg-gradient-success.btn:active, .bg-gradient-success.btn.active {\n  background: #28a745 linear-gradient(180deg, #409152, #1e7e34) repeat-x !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-gradient-success.btn:disabled, .bg-gradient-success.btn.disabled {\n  background-image: none !important;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.bg-gradient-info {\n  background: #17a2b8 linear-gradient(180deg, #3ab0c3, #17a2b8) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-info.btn:not(:disabled):not(.disabled):active, .bg-gradient-info.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-info.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-info.btn:hover {\n  background: #17a2b8 linear-gradient(180deg, #3697a6, #138496) repeat-x !important;\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-gradient-info.btn:not(:disabled):not(.disabled):active, .bg-gradient-info.btn:not(:disabled):not(.disabled).active, .bg-gradient-info.btn:active, .bg-gradient-info.btn.active {\n  background: #17a2b8 linear-gradient(180deg, #358e9c, #117a8b) repeat-x !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-gradient-info.btn:disabled, .bg-gradient-info.btn.disabled {\n  background-image: none !important;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.bg-gradient-warning {\n  background: #ffc107 linear-gradient(180deg, #ffca2c, #ffc107) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .bg-gradient-warning.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-warning.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-warning.btn:hover {\n  background: #ffc107 linear-gradient(180deg, #e4b526, #e0a800) repeat-x !important;\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .bg-gradient-warning.btn:not(:disabled):not(.disabled).active, .bg-gradient-warning.btn:active, .bg-gradient-warning.btn.active {\n  background: #ffc107 linear-gradient(180deg, #daad26, #d39e00) repeat-x !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-gradient-warning.btn:disabled, .bg-gradient-warning.btn.disabled {\n  background-image: none !important;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.bg-gradient-danger {\n  background: #dc3545 linear-gradient(180deg, #e15361, #dc3545) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .bg-gradient-danger.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-danger.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-danger.btn:hover {\n  background: #dc3545 linear-gradient(180deg, #d04451, #c82333) repeat-x !important;\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .bg-gradient-danger.btn:not(:disabled):not(.disabled).active, .bg-gradient-danger.btn:active, .bg-gradient-danger.btn.active {\n  background: #dc3545 linear-gradient(180deg, #c7424f, #bd2130) repeat-x !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-gradient-danger.btn:disabled, .bg-gradient-danger.btn.disabled {\n  background-image: none !important;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.bg-gradient-light {\n  background: #f8f9fa linear-gradient(180deg, #f9fafb, #f8f9fa) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-light.btn:not(:disabled):not(.disabled):active, .bg-gradient-light.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-light.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-light.btn:hover {\n  background: #f8f9fa linear-gradient(180deg, #e6eaed, #e2e6ea) repeat-x !important;\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.bg-gradient-light.btn:not(:disabled):not(.disabled):active, .bg-gradient-light.btn:not(:disabled):not(.disabled).active, .bg-gradient-light.btn:active, .bg-gradient-light.btn.active {\n  background: #f8f9fa linear-gradient(180deg, #e0e4e9, #dae0e5) repeat-x !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.bg-gradient-light.btn:disabled, .bg-gradient-light.btn.disabled {\n  background-image: none !important;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.bg-gradient-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-dark.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-dark.btn:not(:disabled):not(.disabled).active, .bg-gradient-dark.btn:active, .bg-gradient-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-gradient-dark.btn:disabled, .bg-gradient-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.bg-gradient-lightblue {\n  background: #3c8dbc linear-gradient(180deg, #599ec6, #3c8dbc) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-lightblue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-lightblue.btn:hover {\n  background: #3c8dbc linear-gradient(180deg, #518cad, #33779f) repeat-x !important;\n  border-color: #307095;\n  color: #ececec;\n}\n\n.bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active, .bg-gradient-lightblue.btn:active, .bg-gradient-lightblue.btn.active {\n  background: #3c8dbc linear-gradient(180deg, #4f85a5, #307095) repeat-x !important;\n  border-color: #2d698c;\n  color: #fff;\n}\n\n.bg-gradient-lightblue.btn:disabled, .bg-gradient-lightblue.btn.disabled {\n  background-image: none !important;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.bg-gradient-navy {\n  background: #001f3f linear-gradient(180deg, #26415c, #001f3f) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .bg-gradient-navy.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-navy.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-navy.btn:hover {\n  background: #001f3f linear-gradient(180deg, #26313b, #000c19) repeat-x !important;\n  border-color: #00060c;\n  color: #ececec;\n}\n\n.bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .bg-gradient-navy.btn:not(:disabled):not(.disabled).active, .bg-gradient-navy.btn:active, .bg-gradient-navy.btn.active {\n  background: #001f3f linear-gradient(180deg, #262b30, #00060c) repeat-x !important;\n  border-color: black;\n  color: #fff;\n}\n\n.bg-gradient-navy.btn:disabled, .bg-gradient-navy.btn.disabled {\n  background-image: none !important;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.bg-gradient-olive {\n  background: #3d9970 linear-gradient(180deg, #5aa885, #3d9970) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .bg-gradient-olive.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-olive.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-olive.btn:hover {\n  background: #3d9970 linear-gradient(180deg, #519174, #327e5c) repeat-x !important;\n  border-color: #2e7555;\n  color: #ececec;\n}\n\n.bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .bg-gradient-olive.btn:not(:disabled):not(.disabled).active, .bg-gradient-olive.btn:active, .bg-gradient-olive.btn.active {\n  background: #3d9970 linear-gradient(180deg, #4e896f, #2e7555) repeat-x !important;\n  border-color: #2b6b4f;\n  color: #fff;\n}\n\n.bg-gradient-olive.btn:disabled, .bg-gradient-olive.btn.disabled {\n  background-image: none !important;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.bg-gradient-lime {\n  background: #01ff70 linear-gradient(180deg, #27ff85, #01ff70) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .bg-gradient-lime.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-lime.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-lime.btn:hover {\n  background: #01ff70 linear-gradient(180deg, #26df77, #00da5f) repeat-x !important;\n  border-color: #00cd5a;\n  color: #121a24;\n}\n\n.bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .bg-gradient-lime.btn:not(:disabled):not(.disabled).active, .bg-gradient-lime.btn:active, .bg-gradient-lime.btn.active {\n  background: #01ff70 linear-gradient(180deg, #26d572, #00cd5a) repeat-x !important;\n  border-color: #00c054;\n  color: #fff;\n}\n\n.bg-gradient-lime.btn:disabled, .bg-gradient-lime.btn.disabled {\n  background-image: none !important;\n  border-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.bg-gradient-fuchsia {\n  background: #f012be linear-gradient(180deg, #f236c8, #f012be) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-fuchsia.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-fuchsia.btn:hover {\n  background: #f012be linear-gradient(180deg, #d631b1, #cf0da3) repeat-x !important;\n  border-color: #c30c9a;\n  color: #ececec;\n}\n\n.bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active, .bg-gradient-fuchsia.btn:active, .bg-gradient-fuchsia.btn.active {\n  background: #f012be linear-gradient(180deg, #cc31a9, #c30c9a) repeat-x !important;\n  border-color: #b70c90;\n  color: #fff;\n}\n\n.bg-gradient-fuchsia.btn:disabled, .bg-gradient-fuchsia.btn.disabled {\n  background-image: none !important;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.bg-gradient-maroon {\n  background: #d81b60 linear-gradient(180deg, #de3d78, #d81b60) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-maroon.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-maroon.btn:hover {\n  background: #d81b60 linear-gradient(180deg, #c13a6b, #b61751) repeat-x !important;\n  border-color: #ab154c;\n  color: #ececec;\n}\n\n.bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active, .bg-gradient-maroon.btn:active, .bg-gradient-maroon.btn.active {\n  background: #d81b60 linear-gradient(180deg, #b73867, #ab154c) repeat-x !important;\n  border-color: #9f1447;\n  color: #fff;\n}\n\n.bg-gradient-maroon.btn:disabled, .bg-gradient-maroon.btn.disabled {\n  background-image: none !important;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.bg-gradient-blue {\n  background: #007bff linear-gradient(180deg, #268fff, #007bff) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .bg-gradient-blue.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-blue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-blue.btn:hover {\n  background: #007bff linear-gradient(180deg, #267fde, #0069d9) repeat-x !important;\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .bg-gradient-blue.btn:not(:disabled):not(.disabled).active, .bg-gradient-blue.btn:active, .bg-gradient-blue.btn.active {\n  background: #007bff linear-gradient(180deg, #267ad4, #0062cc) repeat-x !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-gradient-blue.btn:disabled, .bg-gradient-blue.btn.disabled {\n  background-image: none !important;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.bg-gradient-indigo {\n  background: #6610f2 linear-gradient(180deg, #7d34f4, #6610f2) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-indigo.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-indigo.btn:hover {\n  background: #6610f2 linear-gradient(180deg, #7030d7, #560bd0) repeat-x !important;\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active, .bg-gradient-indigo.btn:active, .bg-gradient-indigo.btn.active {\n  background: #6610f2 linear-gradient(180deg, #6b2fcd, #510bc4) repeat-x !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.bg-gradient-indigo.btn:disabled, .bg-gradient-indigo.btn.disabled {\n  background-image: none !important;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.bg-gradient-purple {\n  background: #6f42c1 linear-gradient(180deg, #855eca, #6f42c1) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .bg-gradient-purple.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-purple.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-purple.btn:hover {\n  background: #6f42c1 linear-gradient(180deg, #7655b4, #5e37a6) repeat-x !important;\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .bg-gradient-purple.btn:not(:disabled):not(.disabled).active, .bg-gradient-purple.btn:active, .bg-gradient-purple.btn.active {\n  background: #6f42c1 linear-gradient(180deg, #7252ab, #59339d) repeat-x !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.bg-gradient-purple.btn:disabled, .bg-gradient-purple.btn.disabled {\n  background-image: none !important;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.bg-gradient-pink {\n  background: #e83e8c linear-gradient(180deg, #eb5b9d, #e83e8c) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .bg-gradient-pink.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-pink.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-pink.btn:hover {\n  background: #e83e8c linear-gradient(180deg, #e83e8c, #e41c78) repeat-x !important;\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .bg-gradient-pink.btn:not(:disabled):not(.disabled).active, .bg-gradient-pink.btn:active, .bg-gradient-pink.btn.active {\n  background: #e83e8c linear-gradient(180deg, #df3c87, #d91a72) repeat-x !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.bg-gradient-pink.btn:disabled, .bg-gradient-pink.btn.disabled {\n  background-image: none !important;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.bg-gradient-red {\n  background: #dc3545 linear-gradient(180deg, #e15361, #dc3545) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-red.btn:not(:disabled):not(.disabled):active, .bg-gradient-red.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-red.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-red.btn:hover {\n  background: #dc3545 linear-gradient(180deg, #d04451, #c82333) repeat-x !important;\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-gradient-red.btn:not(:disabled):not(.disabled):active, .bg-gradient-red.btn:not(:disabled):not(.disabled).active, .bg-gradient-red.btn:active, .bg-gradient-red.btn.active {\n  background: #dc3545 linear-gradient(180deg, #c7424f, #bd2130) repeat-x !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-gradient-red.btn:disabled, .bg-gradient-red.btn.disabled {\n  background-image: none !important;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.bg-gradient-orange {\n  background: #fd7e14 linear-gradient(180deg, #fd9137, #fd7e14) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .bg-gradient-orange.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-orange.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-orange.btn:hover {\n  background: #fd7e14 linear-gradient(180deg, #ec8128, #e96b02) repeat-x !important;\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .bg-gradient-orange.btn:not(:disabled):not(.disabled).active, .bg-gradient-orange.btn:active, .bg-gradient-orange.btn.active {\n  background: #fd7e14 linear-gradient(180deg, #e17c28, #dc6502) repeat-x !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.bg-gradient-orange.btn:disabled, .bg-gradient-orange.btn.disabled {\n  background-image: none !important;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow {\n  background: #ffc107 linear-gradient(180deg, #ffca2c, #ffc107) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-yellow.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-yellow.btn:hover {\n  background: #ffc107 linear-gradient(180deg, #e4b526, #e0a800) repeat-x !important;\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active, .bg-gradient-yellow.btn:active, .bg-gradient-yellow.btn.active {\n  background: #ffc107 linear-gradient(180deg, #daad26, #d39e00) repeat-x !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow.btn:disabled, .bg-gradient-yellow.btn.disabled {\n  background-image: none !important;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.bg-gradient-green {\n  background: #28a745 linear-gradient(180deg, #48b461, #28a745) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-green.btn:not(:disabled):not(.disabled):active, .bg-gradient-green.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-green.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-green.btn:hover {\n  background: #28a745 linear-gradient(180deg, #429a56, #218838) repeat-x !important;\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-gradient-green.btn:not(:disabled):not(.disabled):active, .bg-gradient-green.btn:not(:disabled):not(.disabled).active, .bg-gradient-green.btn:active, .bg-gradient-green.btn.active {\n  background: #28a745 linear-gradient(180deg, #409152, #1e7e34) repeat-x !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-gradient-green.btn:disabled, .bg-gradient-green.btn.disabled {\n  background-image: none !important;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.bg-gradient-teal {\n  background: #20c997 linear-gradient(180deg, #41d1a7, #20c997) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .bg-gradient-teal.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-teal.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-teal.btn:hover {\n  background: #20c997 linear-gradient(180deg, #3db592, #1ba87e) repeat-x !important;\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .bg-gradient-teal.btn:not(:disabled):not(.disabled).active, .bg-gradient-teal.btn:active, .bg-gradient-teal.btn.active {\n  background: #20c997 linear-gradient(180deg, #3bac8b, #199d76) repeat-x !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.bg-gradient-teal.btn:disabled, .bg-gradient-teal.btn.disabled {\n  background-image: none !important;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.bg-gradient-cyan {\n  background: #17a2b8 linear-gradient(180deg, #3ab0c3, #17a2b8) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-cyan.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-cyan.btn:hover {\n  background: #17a2b8 linear-gradient(180deg, #3697a6, #138496) repeat-x !important;\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active, .bg-gradient-cyan.btn:active, .bg-gradient-cyan.btn.active {\n  background: #17a2b8 linear-gradient(180deg, #358e9c, #117a8b) repeat-x !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-gradient-cyan.btn:disabled, .bg-gradient-cyan.btn.disabled {\n  background-image: none !important;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.bg-gradient-white {\n  background: #fff linear-gradient(180deg, white, #fff) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-white.btn:not(:disabled):not(.disabled):active, .bg-gradient-white.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-white.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-white.btn:hover {\n  background: #fff linear-gradient(180deg, #efefef, #ececec) repeat-x !important;\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.bg-gradient-white.btn:not(:disabled):not(.disabled):active, .bg-gradient-white.btn:not(:disabled):not(.disabled).active, .bg-gradient-white.btn:active, .bg-gradient-white.btn.active {\n  background: #fff linear-gradient(180deg, #e9e9e9, #e6e6e6) repeat-x !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.bg-gradient-white.btn:disabled, .bg-gradient-white.btn.disabled {\n  background-image: none !important;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.bg-gradient-gray {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-gray.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-gray.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray.btn:not(:disabled):not(.disabled).active, .bg-gradient-gray.btn:active, .bg-gradient-gray.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gradient-gray.btn:disabled, .bg-gradient-gray.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-gray-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-gray-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active, .bg-gradient-gray-dark.btn:active, .bg-gradient-gray-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark.btn:disabled, .bg-gradient-gray-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n[class^=\"bg-\"].disabled {\n  opacity: .65;\n}\n\na.text-muted:hover {\n  color: #007bff !important;\n}\n\n.link-muted {\n  color: #5d6974;\n}\n\n.link-muted:hover, .link-muted:focus {\n  color: #464f58;\n}\n\n.link-black {\n  color: #6c757d;\n}\n\n.link-black:hover, .link-black:focus {\n  color: #e6e8ea;\n}\n\n.accent-primary .btn-link,\n.accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-primary .nav-tabs .nav-link {\n  color: #007bff;\n}\n\n.accent-primary .btn-link:hover,\n.accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-primary .nav-tabs .nav-link:hover {\n  color: #0056b3;\n}\n\n.accent-primary .dropdown-item:active, .accent-primary .dropdown-item.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.accent-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.accent-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-primary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-primary .custom-select:focus,\n.accent-primary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-primary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n}\n\n.accent-primary .page-item .page-link {\n  color: #007bff;\n}\n\n.accent-primary .page-item.active a,\n.accent-primary .page-item.active .page-link {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.accent-primary .page-item.disabled a,\n.accent-primary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-primary .page-item .page-link:hover, .dark-mode.accent-primary .page-item .page-link:focus {\n  color: #1a88ff;\n}\n\n.accent-secondary .btn-link,\n.accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-secondary .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.accent-secondary .btn-link:hover,\n.accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-secondary .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.accent-secondary .dropdown-item:active, .accent-secondary .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.accent-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.accent-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-secondary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-secondary .custom-select:focus,\n.accent-secondary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-secondary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.accent-secondary .page-item .page-link {\n  color: #6c757d;\n}\n\n.accent-secondary .page-item.active a,\n.accent-secondary .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.accent-secondary .page-item.disabled a,\n.accent-secondary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-secondary .page-item .page-link:hover, .dark-mode.accent-secondary .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.accent-success .btn-link,\n.accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-success .nav-tabs .nav-link {\n  color: #28a745;\n}\n\n.accent-success .btn-link:hover,\n.accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-success .nav-tabs .nav-link:hover {\n  color: #19692c;\n}\n\n.accent-success .dropdown-item:active, .accent-success .dropdown-item.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.accent-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.accent-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-success .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-success .custom-select:focus,\n.accent-success .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-success .custom-file-input:focus ~ .custom-file-label {\n  border-color: #71dd8a;\n}\n\n.accent-success .page-item .page-link {\n  color: #28a745;\n}\n\n.accent-success .page-item.active a,\n.accent-success .page-item.active .page-link {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.accent-success .page-item.disabled a,\n.accent-success .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-success .page-item .page-link:hover, .dark-mode.accent-success .page-item .page-link:focus {\n  color: #2dbc4e;\n}\n\n.accent-info .btn-link,\n.accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-info .nav-tabs .nav-link {\n  color: #17a2b8;\n}\n\n.accent-info .btn-link:hover,\n.accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-info .nav-tabs .nav-link:hover {\n  color: #0f6674;\n}\n\n.accent-info .dropdown-item:active, .accent-info .dropdown-item.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.accent-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-info .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-info .custom-select:focus,\n.accent-info .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-info .custom-file-input:focus ~ .custom-file-label {\n  border-color: #63d9ec;\n}\n\n.accent-info .page-item .page-link {\n  color: #17a2b8;\n}\n\n.accent-info .page-item.active a,\n.accent-info .page-item.active .page-link {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-info .page-item.disabled a,\n.accent-info .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-info .page-item .page-link:hover, .dark-mode.accent-info .page-item .page-link:focus {\n  color: #1ab6cf;\n}\n\n.accent-warning .btn-link,\n.accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-warning .nav-tabs .nav-link {\n  color: #ffc107;\n}\n\n.accent-warning .btn-link:hover,\n.accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-warning .nav-tabs .nav-link:hover {\n  color: #ba8b00;\n}\n\n.accent-warning .dropdown-item:active, .accent-warning .dropdown-item.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.accent-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.accent-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-warning .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-warning .custom-select:focus,\n.accent-warning .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-warning .custom-file-input:focus ~ .custom-file-label {\n  border-color: #ffe187;\n}\n\n.accent-warning .page-item .page-link {\n  color: #ffc107;\n}\n\n.accent-warning .page-item.active a,\n.accent-warning .page-item.active .page-link {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #fff;\n}\n\n.accent-warning .page-item.disabled a,\n.accent-warning .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-warning .page-item .page-link:hover, .dark-mode.accent-warning .page-item .page-link:focus {\n  color: #ffc721;\n}\n\n.accent-danger .btn-link,\n.accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-danger .nav-tabs .nav-link {\n  color: #dc3545;\n}\n\n.accent-danger .btn-link:hover,\n.accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-danger .nav-tabs .nav-link:hover {\n  color: #a71d2a;\n}\n\n.accent-danger .dropdown-item:active, .accent-danger .dropdown-item.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.accent-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.accent-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-danger .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-danger .custom-select:focus,\n.accent-danger .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-danger .custom-file-input:focus ~ .custom-file-label {\n  border-color: #efa2a9;\n}\n\n.accent-danger .page-item .page-link {\n  color: #dc3545;\n}\n\n.accent-danger .page-item.active a,\n.accent-danger .page-item.active .page-link {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.accent-danger .page-item.disabled a,\n.accent-danger .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-danger .page-item .page-link:hover, .dark-mode.accent-danger .page-item .page-link:focus {\n  color: #e04b59;\n}\n\n.accent-light .btn-link,\n.accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-light .nav-tabs .nav-link {\n  color: #f8f9fa;\n}\n\n.accent-light .btn-link:hover,\n.accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-light .nav-tabs .nav-link:hover {\n  color: #cbd3da;\n}\n\n.accent-light .dropdown-item:active, .accent-light .dropdown-item.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.accent-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.accent-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-light .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-light .custom-select:focus,\n.accent-light .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-light .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.accent-light .page-item .page-link {\n  color: #f8f9fa;\n}\n\n.accent-light .page-item.active a,\n.accent-light .page-item.active .page-link {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #fff;\n}\n\n.accent-light .page-item.disabled a,\n.accent-light .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-light .page-item .page-link:hover, .dark-mode.accent-light .page-item .page-link:focus {\n  color: white;\n}\n\n.accent-dark .btn-link,\n.accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.accent-dark .btn-link:hover,\n.accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.accent-dark .dropdown-item:active, .accent-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.accent-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.accent-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-dark .custom-select:focus,\n.accent-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.accent-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.accent-dark .page-item.active a,\n.accent-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.accent-dark .page-item.disabled a,\n.accent-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-dark .page-item .page-link:hover, .dark-mode.accent-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n.accent-lightblue .btn-link,\n.accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-lightblue .nav-tabs .nav-link {\n  color: #3c8dbc;\n}\n\n.accent-lightblue .btn-link:hover,\n.accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-lightblue .nav-tabs .nav-link:hover {\n  color: #296282;\n}\n\n.accent-lightblue .dropdown-item:active, .accent-lightblue .dropdown-item.active {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.accent-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.accent-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-lightblue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-lightblue .custom-select:focus,\n.accent-lightblue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-lightblue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #99c5de;\n}\n\n.accent-lightblue .page-item .page-link {\n  color: #3c8dbc;\n}\n\n.accent-lightblue .page-item.active a,\n.accent-lightblue .page-item.active .page-link {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.accent-lightblue .page-item.disabled a,\n.accent-lightblue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-lightblue .page-item .page-link:hover, .dark-mode.accent-lightblue .page-item .page-link:focus {\n  color: #4c99c6;\n}\n\n.accent-navy .btn-link,\n.accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-navy .nav-tabs .nav-link {\n  color: #001f3f;\n}\n\n.accent-navy .btn-link:hover,\n.accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-navy .nav-tabs .nav-link:hover {\n  color: black;\n}\n\n.accent-navy .dropdown-item:active, .accent-navy .dropdown-item.active {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.accent-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.accent-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-navy .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-navy .custom-select:focus,\n.accent-navy .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-navy .custom-file-input:focus ~ .custom-file-label {\n  border-color: #005ebf;\n}\n\n.accent-navy .page-item .page-link {\n  color: #001f3f;\n}\n\n.accent-navy .page-item.active a,\n.accent-navy .page-item.active .page-link {\n  background-color: #001f3f;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.accent-navy .page-item.disabled a,\n.accent-navy .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-navy .page-item .page-link:hover, .dark-mode.accent-navy .page-item .page-link:focus {\n  color: #002c59;\n}\n\n.accent-olive .btn-link,\n.accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-olive .nav-tabs .nav-link {\n  color: #3d9970;\n}\n\n.accent-olive .btn-link:hover,\n.accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-olive .nav-tabs .nav-link:hover {\n  color: #276248;\n}\n\n.accent-olive .dropdown-item:active, .accent-olive .dropdown-item.active {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.accent-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.accent-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-olive .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-olive .custom-select:focus,\n.accent-olive .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-olive .custom-file-input:focus ~ .custom-file-label {\n  border-color: #87cfaf;\n}\n\n.accent-olive .page-item .page-link {\n  color: #3d9970;\n}\n\n.accent-olive .page-item.active a,\n.accent-olive .page-item.active .page-link {\n  background-color: #3d9970;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.accent-olive .page-item.disabled a,\n.accent-olive .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-olive .page-item .page-link:hover, .dark-mode.accent-olive .page-item .page-link:focus {\n  color: #44ab7d;\n}\n\n.accent-lime .btn-link,\n.accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-lime .nav-tabs .nav-link {\n  color: #01ff70;\n}\n\n.accent-lime .btn-link:hover,\n.accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-lime .nav-tabs .nav-link:hover {\n  color: #00b44e;\n}\n\n.accent-lime .dropdown-item:active, .accent-lime .dropdown-item.active {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.accent-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.accent-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-lime .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-lime .custom-select:focus,\n.accent-lime .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-lime .custom-file-input:focus ~ .custom-file-label {\n  border-color: #81ffb8;\n}\n\n.accent-lime .page-item .page-link {\n  color: #01ff70;\n}\n\n.accent-lime .page-item.active a,\n.accent-lime .page-item.active .page-link {\n  background-color: #01ff70;\n  border-color: #01ff70;\n  color: #fff;\n}\n\n.accent-lime .page-item.disabled a,\n.accent-lime .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-lime .page-item .page-link:hover, .dark-mode.accent-lime .page-item .page-link:focus {\n  color: #1bff7e;\n}\n\n.accent-fuchsia .btn-link,\n.accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-fuchsia .nav-tabs .nav-link {\n  color: #f012be;\n}\n\n.accent-fuchsia .btn-link:hover,\n.accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-fuchsia .nav-tabs .nav-link:hover {\n  color: #ab0b87;\n}\n\n.accent-fuchsia .dropdown-item:active, .accent-fuchsia .dropdown-item.active {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.accent-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.accent-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-fuchsia .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-fuchsia .custom-select:focus,\n.accent-fuchsia .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-fuchsia .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f88adf;\n}\n\n.accent-fuchsia .page-item .page-link {\n  color: #f012be;\n}\n\n.accent-fuchsia .page-item.active a,\n.accent-fuchsia .page-item.active .page-link {\n  background-color: #f012be;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.accent-fuchsia .page-item.disabled a,\n.accent-fuchsia .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-fuchsia .page-item .page-link:hover, .dark-mode.accent-fuchsia .page-item .page-link:focus {\n  color: #f22ac5;\n}\n\n.accent-maroon .btn-link,\n.accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-maroon .nav-tabs .nav-link {\n  color: #d81b60;\n}\n\n.accent-maroon .btn-link:hover,\n.accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-maroon .nav-tabs .nav-link:hover {\n  color: #941342;\n}\n\n.accent-maroon .dropdown-item:active, .accent-maroon .dropdown-item.active {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.accent-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.accent-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-maroon .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-maroon .custom-select:focus,\n.accent-maroon .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-maroon .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f083ab;\n}\n\n.accent-maroon .page-item .page-link {\n  color: #d81b60;\n}\n\n.accent-maroon .page-item.active a,\n.accent-maroon .page-item.active .page-link {\n  background-color: #d81b60;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.accent-maroon .page-item.disabled a,\n.accent-maroon .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-maroon .page-item .page-link:hover, .dark-mode.accent-maroon .page-item .page-link:focus {\n  color: #e4286d;\n}\n\n.accent-blue .btn-link,\n.accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-blue .nav-tabs .nav-link {\n  color: #007bff;\n}\n\n.accent-blue .btn-link:hover,\n.accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-blue .nav-tabs .nav-link:hover {\n  color: #0056b3;\n}\n\n.accent-blue .dropdown-item:active, .accent-blue .dropdown-item.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.accent-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.accent-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-blue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-blue .custom-select:focus,\n.accent-blue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-blue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n}\n\n.accent-blue .page-item .page-link {\n  color: #007bff;\n}\n\n.accent-blue .page-item.active a,\n.accent-blue .page-item.active .page-link {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.accent-blue .page-item.disabled a,\n.accent-blue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-blue .page-item .page-link:hover, .dark-mode.accent-blue .page-item .page-link:focus {\n  color: #1a88ff;\n}\n\n.accent-indigo .btn-link,\n.accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-indigo .nav-tabs .nav-link {\n  color: #6610f2;\n}\n\n.accent-indigo .btn-link:hover,\n.accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-indigo .nav-tabs .nav-link:hover {\n  color: #4709ac;\n}\n\n.accent-indigo .dropdown-item:active, .accent-indigo .dropdown-item.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.accent-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.accent-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-indigo .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-indigo .custom-select:focus,\n.accent-indigo .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-indigo .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b389f9;\n}\n\n.accent-indigo .page-item .page-link {\n  color: #6610f2;\n}\n\n.accent-indigo .page-item.active a,\n.accent-indigo .page-item.active .page-link {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.accent-indigo .page-item.disabled a,\n.accent-indigo .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-indigo .page-item .page-link:hover, .dark-mode.accent-indigo .page-item .page-link:focus {\n  color: #7528f3;\n}\n\n.accent-purple .btn-link,\n.accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-purple .nav-tabs .nav-link {\n  color: #6f42c1;\n}\n\n.accent-purple .btn-link:hover,\n.accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-purple .nav-tabs .nav-link:hover {\n  color: #4e2d89;\n}\n\n.accent-purple .dropdown-item:active, .accent-purple .dropdown-item.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.accent-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.accent-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-purple .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-purple .custom-select:focus,\n.accent-purple .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-purple .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b8a2e0;\n}\n\n.accent-purple .page-item .page-link {\n  color: #6f42c1;\n}\n\n.accent-purple .page-item.active a,\n.accent-purple .page-item.active .page-link {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.accent-purple .page-item.disabled a,\n.accent-purple .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-purple .page-item .page-link:hover, .dark-mode.accent-purple .page-item .page-link:focus {\n  color: #7e55c7;\n}\n\n.accent-pink .btn-link,\n.accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-pink .nav-tabs .nav-link {\n  color: #e83e8c;\n}\n\n.accent-pink .btn-link:hover,\n.accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-pink .nav-tabs .nav-link:hover {\n  color: #c21766;\n}\n\n.accent-pink .dropdown-item:active, .accent-pink .dropdown-item.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.accent-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.accent-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-pink .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-pink .custom-select:focus,\n.accent-pink .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-pink .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f6b0d0;\n}\n\n.accent-pink .page-item .page-link {\n  color: #e83e8c;\n}\n\n.accent-pink .page-item.active a,\n.accent-pink .page-item.active .page-link {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.accent-pink .page-item.disabled a,\n.accent-pink .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-pink .page-item .page-link:hover, .dark-mode.accent-pink .page-item .page-link:focus {\n  color: #eb559a;\n}\n\n.accent-red .btn-link,\n.accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-red .nav-tabs .nav-link {\n  color: #dc3545;\n}\n\n.accent-red .btn-link:hover,\n.accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-red .nav-tabs .nav-link:hover {\n  color: #a71d2a;\n}\n\n.accent-red .dropdown-item:active, .accent-red .dropdown-item.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.accent-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.accent-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-red .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-red .custom-select:focus,\n.accent-red .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-red .custom-file-input:focus ~ .custom-file-label {\n  border-color: #efa2a9;\n}\n\n.accent-red .page-item .page-link {\n  color: #dc3545;\n}\n\n.accent-red .page-item.active a,\n.accent-red .page-item.active .page-link {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.accent-red .page-item.disabled a,\n.accent-red .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-red .page-item .page-link:hover, .dark-mode.accent-red .page-item .page-link:focus {\n  color: #e04b59;\n}\n\n.accent-orange .btn-link,\n.accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-orange .nav-tabs .nav-link {\n  color: #fd7e14;\n}\n\n.accent-orange .btn-link:hover,\n.accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-orange .nav-tabs .nav-link:hover {\n  color: #c35a02;\n}\n\n.accent-orange .dropdown-item:active, .accent-orange .dropdown-item.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.accent-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.accent-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-orange .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-orange .custom-select:focus,\n.accent-orange .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-orange .custom-file-input:focus ~ .custom-file-label {\n  border-color: #fec392;\n}\n\n.accent-orange .page-item .page-link {\n  color: #fd7e14;\n}\n\n.accent-orange .page-item.active a,\n.accent-orange .page-item.active .page-link {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #fff;\n}\n\n.accent-orange .page-item.disabled a,\n.accent-orange .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-orange .page-item .page-link:hover, .dark-mode.accent-orange .page-item .page-link:focus {\n  color: #fd8c2d;\n}\n\n.accent-yellow .btn-link,\n.accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-yellow .nav-tabs .nav-link {\n  color: #ffc107;\n}\n\n.accent-yellow .btn-link:hover,\n.accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-yellow .nav-tabs .nav-link:hover {\n  color: #ba8b00;\n}\n\n.accent-yellow .dropdown-item:active, .accent-yellow .dropdown-item.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.accent-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.accent-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-yellow .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-yellow .custom-select:focus,\n.accent-yellow .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-yellow .custom-file-input:focus ~ .custom-file-label {\n  border-color: #ffe187;\n}\n\n.accent-yellow .page-item .page-link {\n  color: #ffc107;\n}\n\n.accent-yellow .page-item.active a,\n.accent-yellow .page-item.active .page-link {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #fff;\n}\n\n.accent-yellow .page-item.disabled a,\n.accent-yellow .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-yellow .page-item .page-link:hover, .dark-mode.accent-yellow .page-item .page-link:focus {\n  color: #ffc721;\n}\n\n.accent-green .btn-link,\n.accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-green .nav-tabs .nav-link {\n  color: #28a745;\n}\n\n.accent-green .btn-link:hover,\n.accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-green .nav-tabs .nav-link:hover {\n  color: #19692c;\n}\n\n.accent-green .dropdown-item:active, .accent-green .dropdown-item.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.accent-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.accent-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-green .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-green .custom-select:focus,\n.accent-green .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-green .custom-file-input:focus ~ .custom-file-label {\n  border-color: #71dd8a;\n}\n\n.accent-green .page-item .page-link {\n  color: #28a745;\n}\n\n.accent-green .page-item.active a,\n.accent-green .page-item.active .page-link {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.accent-green .page-item.disabled a,\n.accent-green .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-green .page-item .page-link:hover, .dark-mode.accent-green .page-item .page-link:focus {\n  color: #2dbc4e;\n}\n\n.accent-teal .btn-link,\n.accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-teal .nav-tabs .nav-link {\n  color: #20c997;\n}\n\n.accent-teal .btn-link:hover,\n.accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-teal .nav-tabs .nav-link:hover {\n  color: #158765;\n}\n\n.accent-teal .dropdown-item:active, .accent-teal .dropdown-item.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.accent-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.accent-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-teal .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-teal .custom-select:focus,\n.accent-teal .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-teal .custom-file-input:focus ~ .custom-file-label {\n  border-color: #7eeaca;\n}\n\n.accent-teal .page-item .page-link {\n  color: #20c997;\n}\n\n.accent-teal .page-item.active a,\n.accent-teal .page-item.active .page-link {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.accent-teal .page-item.disabled a,\n.accent-teal .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-teal .page-item .page-link:hover, .dark-mode.accent-teal .page-item .page-link:focus {\n  color: #26dca6;\n}\n\n.accent-cyan .btn-link,\n.accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-cyan .nav-tabs .nav-link {\n  color: #17a2b8;\n}\n\n.accent-cyan .btn-link:hover,\n.accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-cyan .nav-tabs .nav-link:hover {\n  color: #0f6674;\n}\n\n.accent-cyan .dropdown-item:active, .accent-cyan .dropdown-item.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.accent-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-cyan .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-cyan .custom-select:focus,\n.accent-cyan .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-cyan .custom-file-input:focus ~ .custom-file-label {\n  border-color: #63d9ec;\n}\n\n.accent-cyan .page-item .page-link {\n  color: #17a2b8;\n}\n\n.accent-cyan .page-item.active a,\n.accent-cyan .page-item.active .page-link {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-cyan .page-item.disabled a,\n.accent-cyan .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-cyan .page-item .page-link:hover, .dark-mode.accent-cyan .page-item .page-link:focus {\n  color: #1ab6cf;\n}\n\n.accent-white .btn-link,\n.accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-white .nav-tabs .nav-link {\n  color: #fff;\n}\n\n.accent-white .btn-link:hover,\n.accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-white .nav-tabs .nav-link:hover {\n  color: #d9d9d9;\n}\n\n.accent-white .dropdown-item:active, .accent-white .dropdown-item.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.accent-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.accent-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-white .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-white .custom-select:focus,\n.accent-white .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-white .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.accent-white .page-item .page-link {\n  color: #fff;\n}\n\n.accent-white .page-item.active a,\n.accent-white .page-item.active .page-link {\n  background-color: #fff;\n  border-color: #fff;\n  color: #fff;\n}\n\n.accent-white .page-item.disabled a,\n.accent-white .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-white .page-item .page-link:hover, .dark-mode.accent-white .page-item .page-link:focus {\n  color: white;\n}\n\n.accent-gray .btn-link,\n.accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-gray .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.accent-gray .btn-link:hover,\n.accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-gray .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.accent-gray .dropdown-item:active, .accent-gray .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.accent-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.accent-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-gray .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-gray .custom-select:focus,\n.accent-gray .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-gray .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.accent-gray .page-item .page-link {\n  color: #6c757d;\n}\n\n.accent-gray .page-item.active a,\n.accent-gray .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.accent-gray .page-item.disabled a,\n.accent-gray .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-gray .page-item .page-link:hover, .dark-mode.accent-gray .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.accent-gray-dark .btn-link,\n.accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-gray-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.accent-gray-dark .btn-link:hover,\n.accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-gray-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.accent-gray-dark .dropdown-item:active, .accent-gray-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.accent-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.accent-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-gray-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-gray-dark .custom-select:focus,\n.accent-gray-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-gray-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.accent-gray-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.accent-gray-dark .page-item.active a,\n.accent-gray-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.accent-gray-dark .page-item.disabled a,\n.accent-gray-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-gray-dark .page-item .page-link:hover, .dark-mode.accent-gray-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n[class*=\"accent-\"] a.btn-primary {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-secondary {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-success {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-info {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-warning {\n  color: #1f2d3d;\n}\n\n[class*=\"accent-\"] a.btn-danger {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-light {\n  color: #1f2d3d;\n}\n\n[class*=\"accent-\"] a.btn-dark {\n  color: #fff;\n}\n\n.dark-mode .bg-light {\n  background-color: #454d55 !important;\n  color: #fff !important;\n}\n\n.dark-mode .text-black,\n.dark-mode .text-dark,\n.dark-mode .link-black,\n.dark-mode .link-dark {\n  color: #ced4da !important;\n}\n\n.dark-mode.bg-primary {\n  background-color: #3f6791 !important;\n}\n\n.dark-mode.bg-primary,\n.dark-mode.bg-primary > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-primary.btn:hover {\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode.bg-primary.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-primary.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-primary.btn:active, .dark-mode.bg-primary.btn.active {\n  background-color: #304e6d !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode.bg-secondary {\n  background-color: #6c757d !important;\n}\n\n.dark-mode.bg-secondary,\n.dark-mode.bg-secondary > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-secondary.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode.bg-secondary.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-secondary.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-secondary.btn:active, .dark-mode.bg-secondary.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode.bg-success {\n  background-color: #00bc8c !important;\n}\n\n.dark-mode.bg-success,\n.dark-mode.bg-success > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-success.btn:hover {\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode.bg-success.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-success.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-success.btn:active, .dark-mode.bg-success.btn.active {\n  background-color: #008966 !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode.bg-info {\n  background-color: #3498db !important;\n}\n\n.dark-mode.bg-info,\n.dark-mode.bg-info > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-info.btn:hover {\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode.bg-info.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-info.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-info.btn:active, .dark-mode.bg-info.btn.active {\n  background-color: #217dbb !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode.bg-warning {\n  background-color: #f39c12 !important;\n}\n\n.dark-mode.bg-warning,\n.dark-mode.bg-warning > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-warning.btn:hover {\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode.bg-warning.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-warning.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-warning.btn:active, .dark-mode.bg-warning.btn.active {\n  background-color: #c87f0a !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode.bg-danger {\n  background-color: #e74c3c !important;\n}\n\n.dark-mode.bg-danger,\n.dark-mode.bg-danger > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-danger.btn:hover {\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode.bg-danger.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-danger.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-danger.btn:active, .dark-mode.bg-danger.btn.active {\n  background-color: #d62c1a !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode.bg-light {\n  background-color: #f8f9fa !important;\n}\n\n.dark-mode.bg-light,\n.dark-mode.bg-light > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-light.btn:hover {\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.dark-mode.bg-light.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-light.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-light.btn:active, .dark-mode.bg-light.btn.active {\n  background-color: #dae0e5 !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.dark-mode.bg-dark {\n  background-color: #343a40 !important;\n}\n\n.dark-mode.bg-dark,\n.dark-mode.bg-dark > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode.bg-dark.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-dark.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-dark.btn:active, .dark-mode.bg-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode.bg-lightblue {\n  background-color: #86bad8 !important;\n}\n\n.dark-mode.bg-lightblue,\n.dark-mode.bg-lightblue > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-lightblue.btn:hover {\n  border-color: #5fa4cc;\n  color: #121a24;\n}\n\n.dark-mode.bg-lightblue.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-lightblue.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-lightblue.btn:active, .dark-mode.bg-lightblue.btn.active {\n  background-color: #5fa4cc !important;\n  border-color: #559ec9;\n  color: #fff;\n}\n\n.dark-mode.bg-navy {\n  background-color: #002c59 !important;\n}\n\n.dark-mode.bg-navy,\n.dark-mode.bg-navy > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-navy.btn:hover {\n  border-color: #001226;\n  color: #ececec;\n}\n\n.dark-mode.bg-navy.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-navy.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-navy.btn:active, .dark-mode.bg-navy.btn.active {\n  background-color: #001226 !important;\n  border-color: #000c19;\n  color: #fff;\n}\n\n.dark-mode.bg-olive {\n  background-color: #74c8a3 !important;\n}\n\n.dark-mode.bg-olive,\n.dark-mode.bg-olive > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-olive.btn:hover {\n  border-color: #50b98a;\n  color: #121a24;\n}\n\n.dark-mode.bg-olive.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-olive.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-olive.btn:active, .dark-mode.bg-olive.btn.active {\n  background-color: #50b98a !important;\n  border-color: #48b484;\n  color: #fff;\n}\n\n.dark-mode.bg-lime {\n  background-color: #67ffa9 !important;\n}\n\n.dark-mode.bg-lime,\n.dark-mode.bg-lime > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-lime.btn:hover {\n  border-color: #34ff8d;\n  color: #121a24;\n}\n\n.dark-mode.bg-lime.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-lime.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-lime.btn:active, .dark-mode.bg-lime.btn.active {\n  background-color: #34ff8d !important;\n  border-color: #27ff86;\n  color: #1f2d3d;\n}\n\n.dark-mode.bg-fuchsia {\n  background-color: #f672d8 !important;\n}\n\n.dark-mode.bg-fuchsia,\n.dark-mode.bg-fuchsia > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-fuchsia.btn:hover {\n  border-color: #f342cb;\n  color: #121a24;\n}\n\n.dark-mode.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-fuchsia.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-fuchsia.btn:active, .dark-mode.bg-fuchsia.btn.active {\n  background-color: #f342cb !important;\n  border-color: #f236c8;\n  color: #fff;\n}\n\n.dark-mode.bg-maroon {\n  background-color: #ed6c9b !important;\n}\n\n.dark-mode.bg-maroon,\n.dark-mode.bg-maroon > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-maroon.btn:hover {\n  border-color: #e73f7c;\n  color: #121a24;\n}\n\n.dark-mode.bg-maroon.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-maroon.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-maroon.btn:active, .dark-mode.bg-maroon.btn.active {\n  background-color: #e73f7c !important;\n  border-color: #e63475;\n  color: #fff;\n}\n\n.dark-mode.bg-blue {\n  background-color: #3f6791 !important;\n}\n\n.dark-mode.bg-blue,\n.dark-mode.bg-blue > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-blue.btn:hover {\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode.bg-blue.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-blue.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-blue.btn:active, .dark-mode.bg-blue.btn.active {\n  background-color: #304e6d !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode.bg-indigo {\n  background-color: #6610f2 !important;\n}\n\n.dark-mode.bg-indigo,\n.dark-mode.bg-indigo > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-indigo.btn:hover {\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.dark-mode.bg-indigo.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-indigo.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-indigo.btn:active, .dark-mode.bg-indigo.btn.active {\n  background-color: #510bc4 !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.dark-mode.bg-purple {\n  background-color: #6f42c1 !important;\n}\n\n.dark-mode.bg-purple,\n.dark-mode.bg-purple > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-purple.btn:hover {\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.dark-mode.bg-purple.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-purple.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-purple.btn:active, .dark-mode.bg-purple.btn.active {\n  background-color: #59339d !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.dark-mode.bg-pink {\n  background-color: #e83e8c !important;\n}\n\n.dark-mode.bg-pink,\n.dark-mode.bg-pink > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-pink.btn:hover {\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.dark-mode.bg-pink.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-pink.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-pink.btn:active, .dark-mode.bg-pink.btn.active {\n  background-color: #d91a72 !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.dark-mode.bg-red {\n  background-color: #e74c3c !important;\n}\n\n.dark-mode.bg-red,\n.dark-mode.bg-red > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-red.btn:hover {\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode.bg-red.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-red.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-red.btn:active, .dark-mode.bg-red.btn.active {\n  background-color: #d62c1a !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode.bg-orange {\n  background-color: #fd7e14 !important;\n}\n\n.dark-mode.bg-orange,\n.dark-mode.bg-orange > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-orange.btn:hover {\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.dark-mode.bg-orange.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-orange.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-orange.btn:active, .dark-mode.bg-orange.btn.active {\n  background-color: #dc6502 !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.dark-mode.bg-yellow {\n  background-color: #f39c12 !important;\n}\n\n.dark-mode.bg-yellow,\n.dark-mode.bg-yellow > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-yellow.btn:hover {\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode.bg-yellow.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-yellow.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-yellow.btn:active, .dark-mode.bg-yellow.btn.active {\n  background-color: #c87f0a !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode.bg-green {\n  background-color: #00bc8c !important;\n}\n\n.dark-mode.bg-green,\n.dark-mode.bg-green > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-green.btn:hover {\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode.bg-green.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-green.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-green.btn:active, .dark-mode.bg-green.btn.active {\n  background-color: #008966 !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode.bg-teal {\n  background-color: #20c997 !important;\n}\n\n.dark-mode.bg-teal,\n.dark-mode.bg-teal > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-teal.btn:hover {\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.dark-mode.bg-teal.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-teal.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-teal.btn:active, .dark-mode.bg-teal.btn.active {\n  background-color: #199d76 !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.dark-mode.bg-cyan {\n  background-color: #3498db !important;\n}\n\n.dark-mode.bg-cyan,\n.dark-mode.bg-cyan > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-cyan.btn:hover {\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode.bg-cyan.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-cyan.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-cyan.btn:active, .dark-mode.bg-cyan.btn.active {\n  background-color: #217dbb !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode.bg-white {\n  background-color: #fff !important;\n}\n\n.dark-mode.bg-white,\n.dark-mode.bg-white > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-white.btn:hover {\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.dark-mode.bg-white.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-white.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-white.btn:active, .dark-mode.bg-white.btn.active {\n  background-color: #e6e6e6 !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.dark-mode.bg-gray {\n  background-color: #6c757d !important;\n}\n\n.dark-mode.bg-gray,\n.dark-mode.bg-gray > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-gray.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode.bg-gray.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-gray.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-gray.btn:active, .dark-mode.bg-gray.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode.bg-gray-dark {\n  background-color: #343a40 !important;\n}\n\n.dark-mode.bg-gray-dark,\n.dark-mode.bg-gray-dark > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-gray-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-gray-dark.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-gray-dark.btn:active, .dark-mode.bg-gray-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-primary {\n  background: #3f6791 linear-gradient(180deg, #5c7ea2, #3f6791) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-primary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-primary.btn:hover {\n  background: #3f6791 linear-gradient(180deg, #526e8b, #335476) repeat-x !important;\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-primary.btn:active, .dark-mode .bg-gradient-primary.btn.active {\n  background: #3f6791 linear-gradient(180deg, #4f6883, #304e6d) repeat-x !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-primary.btn:disabled, .dark-mode .bg-gradient-primary.btn.disabled {\n  background-image: none !important;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-secondary {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-secondary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-secondary.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-secondary.btn:active, .dark-mode .bg-gradient-secondary.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-secondary.btn:disabled, .dark-mode .bg-gradient-secondary.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-success {\n  background: #00bc8c linear-gradient(180deg, #26c69d, #00bc8c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-success.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-success.btn:hover {\n  background: #00bc8c linear-gradient(180deg, #26a685, #009670) repeat-x !important;\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-success.btn:active, .dark-mode .bg-gradient-success.btn.active {\n  background: #00bc8c linear-gradient(180deg, #269b7d, #008966) repeat-x !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-success.btn:disabled, .dark-mode .bg-gradient-success.btn.disabled {\n  background-image: none !important;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-info {\n  background: #3498db linear-gradient(180deg, #52a7e0, #3498db) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-info.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-info.btn:hover {\n  background: #3498db linear-gradient(180deg, #4497ce, #2384c6) repeat-x !important;\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-info.btn:active, .dark-mode .bg-gradient-info.btn.active {\n  background: #3498db linear-gradient(180deg, #4291c5, #217dbb) repeat-x !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-info.btn:disabled, .dark-mode .bg-gradient-info.btn.disabled {\n  background-image: none !important;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-warning {\n  background: #f39c12 linear-gradient(180deg, #f5ab36, #f39c12) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-warning.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-warning.btn:hover {\n  background: #f39c12 linear-gradient(180deg, #da982f, #d4860b) repeat-x !important;\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-warning.btn:active, .dark-mode .bg-gradient-warning.btn.active {\n  background: #f39c12 linear-gradient(180deg, #d0922f, #c87f0a) repeat-x !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-warning.btn:disabled, .dark-mode .bg-gradient-warning.btn.disabled {\n  background-image: none !important;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-danger {\n  background: #e74c3c linear-gradient(180deg, #eb6759, #e74c3c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-danger.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-danger.btn:hover {\n  background: #e74c3c linear-gradient(180deg, #e64d3e, #e12e1c) repeat-x !important;\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-danger.btn:active, .dark-mode .bg-gradient-danger.btn.active {\n  background: #e74c3c linear-gradient(180deg, #dc4c3d, #d62c1a) repeat-x !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-danger.btn:disabled, .dark-mode .bg-gradient-danger.btn.disabled {\n  background-image: none !important;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-light {\n  background: #f8f9fa linear-gradient(180deg, #f9fafb, #f8f9fa) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-light.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-light.btn:hover {\n  background: #f8f9fa linear-gradient(180deg, #e6eaed, #e2e6ea) repeat-x !important;\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-light.btn:active, .dark-mode .bg-gradient-light.btn.active {\n  background: #f8f9fa linear-gradient(180deg, #e0e4e9, #dae0e5) repeat-x !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-light.btn:disabled, .dark-mode .bg-gradient-light.btn.disabled {\n  background-image: none !important;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-dark.btn:active, .dark-mode .bg-gradient-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-dark.btn:disabled, .dark-mode .bg-gradient-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-lightblue {\n  background: #86bad8 linear-gradient(180deg, #98c4de, #86bad8) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-lightblue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:hover {\n  background: #86bad8 linear-gradient(180deg, #7fb6d6, #69a9cf) repeat-x !important;\n  border-color: #5fa4cc;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-lightblue.btn:active, .dark-mode .bg-gradient-lightblue.btn.active {\n  background: #86bad8 linear-gradient(180deg, #77b2d4, #5fa4cc) repeat-x !important;\n  border-color: #559ec9;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:disabled, .dark-mode .bg-gradient-lightblue.btn.disabled {\n  background-image: none !important;\n  border-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-navy {\n  background: #002c59 linear-gradient(180deg, #264b71, #002c59) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-navy.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-navy.btn:hover {\n  background: #002c59 linear-gradient(180deg, #263b51, #001932) repeat-x !important;\n  border-color: #001226;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-navy.btn:active, .dark-mode .bg-gradient-navy.btn.active {\n  background: #002c59 linear-gradient(180deg, #263646, #001226) repeat-x !important;\n  border-color: #000c19;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-navy.btn:disabled, .dark-mode .bg-gradient-navy.btn.disabled {\n  background-image: none !important;\n  border-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-olive {\n  background: #74c8a3 linear-gradient(180deg, #89d0b0, #74c8a3) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-olive.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-olive.btn:hover {\n  background: #74c8a3 linear-gradient(180deg, #72c7a1, #59bd90) repeat-x !important;\n  border-color: #50b98a;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-olive.btn:active, .dark-mode .bg-gradient-olive.btn.active {\n  background: #74c8a3 linear-gradient(180deg, #6ac49c, #50b98a) repeat-x !important;\n  border-color: #48b484;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-olive.btn:disabled, .dark-mode .bg-gradient-olive.btn.disabled {\n  background-image: none !important;\n  border-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lime {\n  background: #67ffa9 linear-gradient(180deg, #7effb6, #67ffa9) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-lime.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-lime.btn:hover {\n  background: #67ffa9 linear-gradient(180deg, #5dffa4, #41ff94) repeat-x !important;\n  border-color: #34ff8d;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-lime.btn:active, .dark-mode .bg-gradient-lime.btn.active {\n  background: #67ffa9 linear-gradient(180deg, #52ff9e, #34ff8d) repeat-x !important;\n  border-color: #27ff86;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lime.btn:disabled, .dark-mode .bg-gradient-lime.btn.disabled {\n  background-image: none !important;\n  border-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-fuchsia {\n  background: #f672d8 linear-gradient(180deg, #f787de, #f672d8) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-fuchsia.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:hover {\n  background: #f672d8 linear-gradient(180deg, #f569d6, #f44ece) repeat-x !important;\n  border-color: #f342cb;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-fuchsia.btn:active, .dark-mode .bg-gradient-fuchsia.btn.active {\n  background: #f672d8 linear-gradient(180deg, #f55ed3, #f342cb) repeat-x !important;\n  border-color: #f236c8;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:disabled, .dark-mode .bg-gradient-fuchsia.btn.disabled {\n  background-image: none !important;\n  border-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-maroon {\n  background: #ed6c9b linear-gradient(180deg, #ef82aa, #ed6c9b) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-maroon.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-maroon.btn:hover {\n  background: #ed6c9b linear-gradient(180deg, #ec6596, #e84a84) repeat-x !important;\n  border-color: #e73f7c;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-maroon.btn:active, .dark-mode .bg-gradient-maroon.btn.active {\n  background: #ed6c9b linear-gradient(180deg, #eb5c90, #e73f7c) repeat-x !important;\n  border-color: #e63475;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-maroon.btn:disabled, .dark-mode .bg-gradient-maroon.btn.disabled {\n  background-image: none !important;\n  border-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-blue {\n  background: #3f6791 linear-gradient(180deg, #5c7ea2, #3f6791) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-blue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-blue.btn:hover {\n  background: #3f6791 linear-gradient(180deg, #526e8b, #335476) repeat-x !important;\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-blue.btn:active, .dark-mode .bg-gradient-blue.btn.active {\n  background: #3f6791 linear-gradient(180deg, #4f6883, #304e6d) repeat-x !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-blue.btn:disabled, .dark-mode .bg-gradient-blue.btn.disabled {\n  background-image: none !important;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-indigo {\n  background: #6610f2 linear-gradient(180deg, #7d34f4, #6610f2) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-indigo.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-indigo.btn:hover {\n  background: #6610f2 linear-gradient(180deg, #7030d7, #560bd0) repeat-x !important;\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-indigo.btn:active, .dark-mode .bg-gradient-indigo.btn.active {\n  background: #6610f2 linear-gradient(180deg, #6b2fcd, #510bc4) repeat-x !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-indigo.btn:disabled, .dark-mode .bg-gradient-indigo.btn.disabled {\n  background-image: none !important;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-purple {\n  background: #6f42c1 linear-gradient(180deg, #855eca, #6f42c1) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-purple.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-purple.btn:hover {\n  background: #6f42c1 linear-gradient(180deg, #7655b4, #5e37a6) repeat-x !important;\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-purple.btn:active, .dark-mode .bg-gradient-purple.btn.active {\n  background: #6f42c1 linear-gradient(180deg, #7252ab, #59339d) repeat-x !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-purple.btn:disabled, .dark-mode .bg-gradient-purple.btn.disabled {\n  background-image: none !important;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-pink {\n  background: #e83e8c linear-gradient(180deg, #eb5b9d, #e83e8c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-pink.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-pink.btn:hover {\n  background: #e83e8c linear-gradient(180deg, #e83e8c, #e41c78) repeat-x !important;\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-pink.btn:active, .dark-mode .bg-gradient-pink.btn.active {\n  background: #e83e8c linear-gradient(180deg, #df3c87, #d91a72) repeat-x !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-pink.btn:disabled, .dark-mode .bg-gradient-pink.btn.disabled {\n  background-image: none !important;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-red {\n  background: #e74c3c linear-gradient(180deg, #eb6759, #e74c3c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-red.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-red.btn:hover {\n  background: #e74c3c linear-gradient(180deg, #e64d3e, #e12e1c) repeat-x !important;\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-red.btn:active, .dark-mode .bg-gradient-red.btn.active {\n  background: #e74c3c linear-gradient(180deg, #dc4c3d, #d62c1a) repeat-x !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-red.btn:disabled, .dark-mode .bg-gradient-red.btn.disabled {\n  background-image: none !important;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-orange {\n  background: #fd7e14 linear-gradient(180deg, #fd9137, #fd7e14) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-orange.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-orange.btn:hover {\n  background: #fd7e14 linear-gradient(180deg, #ec8128, #e96b02) repeat-x !important;\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-orange.btn:active, .dark-mode .bg-gradient-orange.btn.active {\n  background: #fd7e14 linear-gradient(180deg, #e17c28, #dc6502) repeat-x !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-orange.btn:disabled, .dark-mode .bg-gradient-orange.btn.disabled {\n  background-image: none !important;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-yellow {\n  background: #f39c12 linear-gradient(180deg, #f5ab36, #f39c12) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-yellow.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-yellow.btn:hover {\n  background: #f39c12 linear-gradient(180deg, #da982f, #d4860b) repeat-x !important;\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-yellow.btn:active, .dark-mode .bg-gradient-yellow.btn.active {\n  background: #f39c12 linear-gradient(180deg, #d0922f, #c87f0a) repeat-x !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-yellow.btn:disabled, .dark-mode .bg-gradient-yellow.btn.disabled {\n  background-image: none !important;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-green {\n  background: #00bc8c linear-gradient(180deg, #26c69d, #00bc8c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-green.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-green.btn:hover {\n  background: #00bc8c linear-gradient(180deg, #26a685, #009670) repeat-x !important;\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-green.btn:active, .dark-mode .bg-gradient-green.btn.active {\n  background: #00bc8c linear-gradient(180deg, #269b7d, #008966) repeat-x !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-green.btn:disabled, .dark-mode .bg-gradient-green.btn.disabled {\n  background-image: none !important;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-teal {\n  background: #20c997 linear-gradient(180deg, #41d1a7, #20c997) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-teal.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-teal.btn:hover {\n  background: #20c997 linear-gradient(180deg, #3db592, #1ba87e) repeat-x !important;\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-teal.btn:active, .dark-mode .bg-gradient-teal.btn.active {\n  background: #20c997 linear-gradient(180deg, #3bac8b, #199d76) repeat-x !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-teal.btn:disabled, .dark-mode .bg-gradient-teal.btn.disabled {\n  background-image: none !important;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-cyan {\n  background: #3498db linear-gradient(180deg, #52a7e0, #3498db) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-cyan.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-cyan.btn:hover {\n  background: #3498db linear-gradient(180deg, #4497ce, #2384c6) repeat-x !important;\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-cyan.btn:active, .dark-mode .bg-gradient-cyan.btn.active {\n  background: #3498db linear-gradient(180deg, #4291c5, #217dbb) repeat-x !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-cyan.btn:disabled, .dark-mode .bg-gradient-cyan.btn.disabled {\n  background-image: none !important;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-white {\n  background: #fff linear-gradient(180deg, white, #fff) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-white.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-white.btn:hover {\n  background: #fff linear-gradient(180deg, #efefef, #ececec) repeat-x !important;\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-white.btn:active, .dark-mode .bg-gradient-white.btn.active {\n  background: #fff linear-gradient(180deg, #e9e9e9, #e6e6e6) repeat-x !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-white.btn:disabled, .dark-mode .bg-gradient-white.btn.disabled {\n  background-image: none !important;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-gray {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-gray.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-gray.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-gray.btn:active, .dark-mode .bg-gradient-gray.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray.btn:disabled, .dark-mode .bg-gradient-gray.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-gray-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-gray-dark.btn:active, .dark-mode .bg-gradient-gray-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:disabled, .dark-mode .bg-gradient-gray-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-primary .btn-link,\n.dark-mode .accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-primary .nav-tabs .nav-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-primary .btn-link:hover,\n.dark-mode .accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-primary .nav-tabs .nav-link:hover {\n  color: #28415c;\n}\n\n.dark-mode .accent-primary .dropdown-item:active, .dark-mode .accent-primary .dropdown-item.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .accent-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-primary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-primary .custom-select:focus,\n.dark-mode .accent-primary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-primary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #85a7ca;\n}\n\n.dark-mode .accent-primary .page-item .page-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-primary .page-item.active a,\n.dark-mode .accent-primary .page-item.active .page-link {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-primary .page-item.disabled a,\n.dark-mode .accent-primary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-primary .page-item .page-link:hover, .dark-mode .dark-mode.accent-primary .page-item .page-link:focus {\n  color: #4774a3;\n}\n\n.dark-mode .accent-secondary .btn-link,\n.dark-mode .accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-secondary .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-secondary .btn-link:hover,\n.dark-mode .accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-secondary .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.dark-mode .accent-secondary .dropdown-item:active, .dark-mode .accent-secondary .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .accent-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-secondary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-secondary .custom-select:focus,\n.dark-mode .accent-secondary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-secondary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.dark-mode .accent-secondary .page-item .page-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-secondary .page-item.active a,\n.dark-mode .accent-secondary .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-secondary .page-item.disabled a,\n.dark-mode .accent-secondary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-secondary .page-item .page-link:hover, .dark-mode .dark-mode.accent-secondary .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.dark-mode .accent-success .btn-link,\n.dark-mode .accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-success .nav-tabs .nav-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-success .btn-link:hover,\n.dark-mode .accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-success .nav-tabs .nav-link:hover {\n  color: #007053;\n}\n\n.dark-mode .accent-success .dropdown-item:active, .dark-mode .accent-success .dropdown-item.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .accent-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-success .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-success .custom-select:focus,\n.dark-mode .accent-success .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-success .custom-file-input:focus ~ .custom-file-label {\n  border-color: #3dffcd;\n}\n\n.dark-mode .accent-success .page-item .page-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-success .page-item.active a,\n.dark-mode .accent-success .page-item.active .page-link {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-success .page-item.disabled a,\n.dark-mode .accent-success .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-success .page-item .page-link:hover, .dark-mode .dark-mode.accent-success .page-item .page-link:focus {\n  color: #00d69f;\n}\n\n.dark-mode .accent-info .btn-link,\n.dark-mode .accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-info .nav-tabs .nav-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-info .btn-link:hover,\n.dark-mode .accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-info .nav-tabs .nav-link:hover {\n  color: #1d6fa5;\n}\n\n.dark-mode .accent-info .dropdown-item:active, .dark-mode .accent-info .dropdown-item.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .accent-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-info .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-info .custom-select:focus,\n.dark-mode .accent-info .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-info .custom-file-input:focus ~ .custom-file-label {\n  border-color: #a0cfee;\n}\n\n.dark-mode .accent-info .page-item .page-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-info .page-item.active a,\n.dark-mode .accent-info .page-item.active .page-link {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-info .page-item.disabled a,\n.dark-mode .accent-info .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-info .page-item .page-link:hover, .dark-mode .dark-mode.accent-info .page-item .page-link:focus {\n  color: #4aa3df;\n}\n\n.dark-mode .accent-warning .btn-link,\n.dark-mode .accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-warning .nav-tabs .nav-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-warning .btn-link:hover,\n.dark-mode .accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-warning .nav-tabs .nav-link:hover {\n  color: #b06f09;\n}\n\n.dark-mode .accent-warning .dropdown-item:active, .dark-mode .accent-warning .dropdown-item.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .accent-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-warning .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-warning .custom-select:focus,\n.dark-mode .accent-warning .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-warning .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .accent-warning .page-item .page-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-warning .page-item.active a,\n.dark-mode .accent-warning .page-item.active .page-link {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #fff;\n}\n\n.dark-mode .accent-warning .page-item.disabled a,\n.dark-mode .accent-warning .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-warning .page-item .page-link:hover, .dark-mode .dark-mode.accent-warning .page-item .page-link:focus {\n  color: #f4a62a;\n}\n\n.dark-mode .accent-danger .btn-link,\n.dark-mode .accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-danger .nav-tabs .nav-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-danger .btn-link:hover,\n.dark-mode .accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-danger .nav-tabs .nav-link:hover {\n  color: #bf2718;\n}\n\n.dark-mode .accent-danger .dropdown-item:active, .dark-mode .accent-danger .dropdown-item.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .accent-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-danger .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-danger .custom-select:focus,\n.dark-mode .accent-danger .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-danger .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .accent-danger .page-item .page-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-danger .page-item.active a,\n.dark-mode .accent-danger .page-item.active .page-link {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-danger .page-item.disabled a,\n.dark-mode .accent-danger .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-danger .page-item .page-link:hover, .dark-mode .dark-mode.accent-danger .page-item .page-link:focus {\n  color: #ea6153;\n}\n\n.dark-mode .accent-light .btn-link,\n.dark-mode .accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-light .nav-tabs .nav-link {\n  color: #f8f9fa;\n}\n\n.dark-mode .accent-light .btn-link:hover,\n.dark-mode .accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-light .nav-tabs .nav-link:hover {\n  color: #cbd3da;\n}\n\n.dark-mode .accent-light .dropdown-item:active, .dark-mode .accent-light .dropdown-item.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.dark-mode .accent-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-light .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-light .custom-select:focus,\n.dark-mode .accent-light .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-light .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.dark-mode .accent-light .page-item .page-link {\n  color: #f8f9fa;\n}\n\n.dark-mode .accent-light .page-item.active a,\n.dark-mode .accent-light .page-item.active .page-link {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #fff;\n}\n\n.dark-mode .accent-light .page-item.disabled a,\n.dark-mode .accent-light .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-light .page-item .page-link:hover, .dark-mode .dark-mode.accent-light .page-item .page-link:focus {\n  color: white;\n}\n\n.dark-mode .accent-dark .btn-link,\n.dark-mode .accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-dark .btn-link:hover,\n.dark-mode .accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.dark-mode .accent-dark .dropdown-item:active, .dark-mode .accent-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .accent-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-dark .custom-select:focus,\n.dark-mode .accent-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.dark-mode .accent-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-dark .page-item.active a,\n.dark-mode .accent-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-dark .page-item.disabled a,\n.dark-mode .accent-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-dark .page-item .page-link:hover, .dark-mode .dark-mode.accent-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-primary {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-secondary {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-success {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-info {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-warning {\n  color: #1f2d3d;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-danger {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-light {\n  color: #1f2d3d;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-dark {\n  color: #fff;\n}\n\n.dark-mode .accent-lightblue .btn-link,\n.dark-mode .accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-lightblue .nav-tabs .nav-link {\n  color: #86bad8;\n}\n\n.dark-mode .accent-lightblue .btn-link:hover,\n.dark-mode .accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-lightblue .nav-tabs .nav-link:hover {\n  color: #4c99c6;\n}\n\n.dark-mode .accent-lightblue .dropdown-item:active, .dark-mode .accent-lightblue .dropdown-item.active {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #86bad8;\n  border-color: #3c8dbc;\n}\n\n.dark-mode .accent-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-lightblue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-lightblue .custom-select:focus,\n.dark-mode .accent-lightblue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-lightblue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .accent-lightblue .page-item .page-link {\n  color: #86bad8;\n}\n\n.dark-mode .accent-lightblue .page-item.active a,\n.dark-mode .accent-lightblue .page-item.active .page-link {\n  background-color: #86bad8;\n  border-color: #86bad8;\n  color: #fff;\n}\n\n.dark-mode .accent-lightblue .page-item.disabled a,\n.dark-mode .accent-lightblue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-lightblue .page-item .page-link:hover, .dark-mode .dark-mode.accent-lightblue .page-item .page-link:focus {\n  color: #99c5de;\n}\n\n.dark-mode .accent-navy .btn-link,\n.dark-mode .accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-navy .nav-tabs .nav-link {\n  color: #002c59;\n}\n\n.dark-mode .accent-navy .btn-link:hover,\n.dark-mode .accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-navy .nav-tabs .nav-link:hover {\n  color: #00060c;\n}\n\n.dark-mode .accent-navy .dropdown-item:active, .dark-mode .accent-navy .dropdown-item.active {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .accent-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #002c59;\n  border-color: black;\n}\n\n.dark-mode .accent-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-navy .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-navy .custom-select:focus,\n.dark-mode .accent-navy .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-navy .custom-file-input:focus ~ .custom-file-label {\n  border-color: #006ad8;\n}\n\n.dark-mode .accent-navy .page-item .page-link {\n  color: #002c59;\n}\n\n.dark-mode .accent-navy .page-item.active a,\n.dark-mode .accent-navy .page-item.active .page-link {\n  background-color: #002c59;\n  border-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .accent-navy .page-item.disabled a,\n.dark-mode .accent-navy .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-navy .page-item .page-link:hover, .dark-mode .dark-mode.accent-navy .page-item .page-link:focus {\n  color: #003872;\n}\n\n.dark-mode .accent-olive .btn-link,\n.dark-mode .accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-olive .nav-tabs .nav-link {\n  color: #74c8a3;\n}\n\n.dark-mode .accent-olive .btn-link:hover,\n.dark-mode .accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-olive .nav-tabs .nav-link:hover {\n  color: #44ab7d;\n}\n\n.dark-mode .accent-olive .dropdown-item:active, .dark-mode .accent-olive .dropdown-item.active {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #74c8a3;\n  border-color: #3d9970;\n}\n\n.dark-mode .accent-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-olive .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-olive .custom-select:focus,\n.dark-mode .accent-olive .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-olive .custom-file-input:focus ~ .custom-file-label {\n  border-color: #cfecdf;\n}\n\n.dark-mode .accent-olive .page-item .page-link {\n  color: #74c8a3;\n}\n\n.dark-mode .accent-olive .page-item.active a,\n.dark-mode .accent-olive .page-item.active .page-link {\n  background-color: #74c8a3;\n  border-color: #74c8a3;\n  color: #fff;\n}\n\n.dark-mode .accent-olive .page-item.disabled a,\n.dark-mode .accent-olive .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-olive .page-item .page-link:hover, .dark-mode .dark-mode.accent-olive .page-item .page-link:focus {\n  color: #87cfaf;\n}\n\n.dark-mode .accent-lime .btn-link,\n.dark-mode .accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-lime .nav-tabs .nav-link {\n  color: #67ffa9;\n}\n\n.dark-mode .accent-lime .btn-link:hover,\n.dark-mode .accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-lime .nav-tabs .nav-link:hover {\n  color: #1bff7e;\n}\n\n.dark-mode .accent-lime .dropdown-item:active, .dark-mode .accent-lime .dropdown-item.active {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #67ffa9;\n  border-color: #01ff70;\n}\n\n.dark-mode .accent-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-lime .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-lime .custom-select:focus,\n.dark-mode .accent-lime .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-lime .custom-file-input:focus ~ .custom-file-label {\n  border-color: #e7fff1;\n}\n\n.dark-mode .accent-lime .page-item .page-link {\n  color: #67ffa9;\n}\n\n.dark-mode .accent-lime .page-item.active a,\n.dark-mode .accent-lime .page-item.active .page-link {\n  background-color: #67ffa9;\n  border-color: #67ffa9;\n  color: #fff;\n}\n\n.dark-mode .accent-lime .page-item.disabled a,\n.dark-mode .accent-lime .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-lime .page-item .page-link:hover, .dark-mode .dark-mode.accent-lime .page-item .page-link:focus {\n  color: #81ffb8;\n}\n\n.dark-mode .accent-fuchsia .btn-link,\n.dark-mode .accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-fuchsia .nav-tabs .nav-link {\n  color: #f672d8;\n}\n\n.dark-mode .accent-fuchsia .btn-link:hover,\n.dark-mode .accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-fuchsia .nav-tabs .nav-link:hover {\n  color: #f22ac5;\n}\n\n.dark-mode .accent-fuchsia .dropdown-item:active, .dark-mode .accent-fuchsia .dropdown-item.active {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f672d8;\n  border-color: #f012be;\n}\n\n.dark-mode .accent-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-fuchsia .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-fuchsia .custom-select:focus,\n.dark-mode .accent-fuchsia .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-fuchsia .custom-file-input:focus ~ .custom-file-label {\n  border-color: #feeaf9;\n}\n\n.dark-mode .accent-fuchsia .page-item .page-link {\n  color: #f672d8;\n}\n\n.dark-mode .accent-fuchsia .page-item.active a,\n.dark-mode .accent-fuchsia .page-item.active .page-link {\n  background-color: #f672d8;\n  border-color: #f672d8;\n  color: #fff;\n}\n\n.dark-mode .accent-fuchsia .page-item.disabled a,\n.dark-mode .accent-fuchsia .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-fuchsia .page-item .page-link:hover, .dark-mode .dark-mode.accent-fuchsia .page-item .page-link:focus {\n  color: #f88adf;\n}\n\n.dark-mode .accent-maroon .btn-link,\n.dark-mode .accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-maroon .nav-tabs .nav-link {\n  color: #ed6c9b;\n}\n\n.dark-mode .accent-maroon .btn-link:hover,\n.dark-mode .accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-maroon .nav-tabs .nav-link:hover {\n  color: #e4286d;\n}\n\n.dark-mode .accent-maroon .dropdown-item:active, .dark-mode .accent-maroon .dropdown-item.active {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ed6c9b;\n  border-color: #d81b60;\n}\n\n.dark-mode .accent-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-maroon .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-maroon .custom-select:focus,\n.dark-mode .accent-maroon .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-maroon .custom-file-input:focus ~ .custom-file-label {\n  border-color: #fbdee8;\n}\n\n.dark-mode .accent-maroon .page-item .page-link {\n  color: #ed6c9b;\n}\n\n.dark-mode .accent-maroon .page-item.active a,\n.dark-mode .accent-maroon .page-item.active .page-link {\n  background-color: #ed6c9b;\n  border-color: #ed6c9b;\n  color: #fff;\n}\n\n.dark-mode .accent-maroon .page-item.disabled a,\n.dark-mode .accent-maroon .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-maroon .page-item .page-link:hover, .dark-mode .dark-mode.accent-maroon .page-item .page-link:focus {\n  color: #f083ab;\n}\n\n.dark-mode .accent-blue .btn-link,\n.dark-mode .accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-blue .nav-tabs .nav-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-blue .btn-link:hover,\n.dark-mode .accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-blue .nav-tabs .nav-link:hover {\n  color: #28415c;\n}\n\n.dark-mode .accent-blue .dropdown-item:active, .dark-mode .accent-blue .dropdown-item.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .accent-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-blue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-blue .custom-select:focus,\n.dark-mode .accent-blue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-blue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #85a7ca;\n}\n\n.dark-mode .accent-blue .page-item .page-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-blue .page-item.active a,\n.dark-mode .accent-blue .page-item.active .page-link {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-blue .page-item.disabled a,\n.dark-mode .accent-blue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-blue .page-item .page-link:hover, .dark-mode .dark-mode.accent-blue .page-item .page-link:focus {\n  color: #4774a3;\n}\n\n.dark-mode .accent-indigo .btn-link,\n.dark-mode .accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-indigo .nav-tabs .nav-link {\n  color: #6610f2;\n}\n\n.dark-mode .accent-indigo .btn-link:hover,\n.dark-mode .accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-indigo .nav-tabs .nav-link:hover {\n  color: #4709ac;\n}\n\n.dark-mode .accent-indigo .dropdown-item:active, .dark-mode .accent-indigo .dropdown-item.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .accent-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.dark-mode .accent-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-indigo .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-indigo .custom-select:focus,\n.dark-mode .accent-indigo .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-indigo .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b389f9;\n}\n\n.dark-mode .accent-indigo .page-item .page-link {\n  color: #6610f2;\n}\n\n.dark-mode .accent-indigo .page-item.active a,\n.dark-mode .accent-indigo .page-item.active .page-link {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .accent-indigo .page-item.disabled a,\n.dark-mode .accent-indigo .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-indigo .page-item .page-link:hover, .dark-mode .dark-mode.accent-indigo .page-item .page-link:focus {\n  color: #7528f3;\n}\n\n.dark-mode .accent-purple .btn-link,\n.dark-mode .accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-purple .nav-tabs .nav-link {\n  color: #6f42c1;\n}\n\n.dark-mode .accent-purple .btn-link:hover,\n.dark-mode .accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-purple .nav-tabs .nav-link:hover {\n  color: #4e2d89;\n}\n\n.dark-mode .accent-purple .dropdown-item:active, .dark-mode .accent-purple .dropdown-item.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .accent-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.dark-mode .accent-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-purple .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-purple .custom-select:focus,\n.dark-mode .accent-purple .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-purple .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .accent-purple .page-item .page-link {\n  color: #6f42c1;\n}\n\n.dark-mode .accent-purple .page-item.active a,\n.dark-mode .accent-purple .page-item.active .page-link {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .accent-purple .page-item.disabled a,\n.dark-mode .accent-purple .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-purple .page-item .page-link:hover, .dark-mode .dark-mode.accent-purple .page-item .page-link:focus {\n  color: #7e55c7;\n}\n\n.dark-mode .accent-pink .btn-link,\n.dark-mode .accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-pink .nav-tabs .nav-link {\n  color: #e83e8c;\n}\n\n.dark-mode .accent-pink .btn-link:hover,\n.dark-mode .accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-pink .nav-tabs .nav-link:hover {\n  color: #c21766;\n}\n\n.dark-mode .accent-pink .dropdown-item:active, .dark-mode .accent-pink .dropdown-item.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .accent-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.dark-mode .accent-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-pink .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-pink .custom-select:focus,\n.dark-mode .accent-pink .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-pink .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .accent-pink .page-item .page-link {\n  color: #e83e8c;\n}\n\n.dark-mode .accent-pink .page-item.active a,\n.dark-mode .accent-pink .page-item.active .page-link {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .accent-pink .page-item.disabled a,\n.dark-mode .accent-pink .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-pink .page-item .page-link:hover, .dark-mode .dark-mode.accent-pink .page-item .page-link:focus {\n  color: #eb559a;\n}\n\n.dark-mode .accent-red .btn-link,\n.dark-mode .accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-red .nav-tabs .nav-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-red .btn-link:hover,\n.dark-mode .accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-red .nav-tabs .nav-link:hover {\n  color: #bf2718;\n}\n\n.dark-mode .accent-red .dropdown-item:active, .dark-mode .accent-red .dropdown-item.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .accent-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-red .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-red .custom-select:focus,\n.dark-mode .accent-red .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-red .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .accent-red .page-item .page-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-red .page-item.active a,\n.dark-mode .accent-red .page-item.active .page-link {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-red .page-item.disabled a,\n.dark-mode .accent-red .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-red .page-item .page-link:hover, .dark-mode .dark-mode.accent-red .page-item .page-link:focus {\n  color: #ea6153;\n}\n\n.dark-mode .accent-orange .btn-link,\n.dark-mode .accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-orange .nav-tabs .nav-link {\n  color: #fd7e14;\n}\n\n.dark-mode .accent-orange .btn-link:hover,\n.dark-mode .accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-orange .nav-tabs .nav-link:hover {\n  color: #c35a02;\n}\n\n.dark-mode .accent-orange .dropdown-item:active, .dark-mode .accent-orange .dropdown-item.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.dark-mode .accent-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-orange .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-orange .custom-select:focus,\n.dark-mode .accent-orange .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-orange .custom-file-input:focus ~ .custom-file-label {\n  border-color: #fec392;\n}\n\n.dark-mode .accent-orange .page-item .page-link {\n  color: #fd7e14;\n}\n\n.dark-mode .accent-orange .page-item.active a,\n.dark-mode .accent-orange .page-item.active .page-link {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #fff;\n}\n\n.dark-mode .accent-orange .page-item.disabled a,\n.dark-mode .accent-orange .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-orange .page-item .page-link:hover, .dark-mode .dark-mode.accent-orange .page-item .page-link:focus {\n  color: #fd8c2d;\n}\n\n.dark-mode .accent-yellow .btn-link,\n.dark-mode .accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-yellow .nav-tabs .nav-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-yellow .btn-link:hover,\n.dark-mode .accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-yellow .nav-tabs .nav-link:hover {\n  color: #b06f09;\n}\n\n.dark-mode .accent-yellow .dropdown-item:active, .dark-mode .accent-yellow .dropdown-item.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .accent-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-yellow .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-yellow .custom-select:focus,\n.dark-mode .accent-yellow .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-yellow .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .accent-yellow .page-item .page-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-yellow .page-item.active a,\n.dark-mode .accent-yellow .page-item.active .page-link {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #fff;\n}\n\n.dark-mode .accent-yellow .page-item.disabled a,\n.dark-mode .accent-yellow .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-yellow .page-item .page-link:hover, .dark-mode .dark-mode.accent-yellow .page-item .page-link:focus {\n  color: #f4a62a;\n}\n\n.dark-mode .accent-green .btn-link,\n.dark-mode .accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-green .nav-tabs .nav-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-green .btn-link:hover,\n.dark-mode .accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-green .nav-tabs .nav-link:hover {\n  color: #007053;\n}\n\n.dark-mode .accent-green .dropdown-item:active, .dark-mode .accent-green .dropdown-item.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .accent-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-green .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-green .custom-select:focus,\n.dark-mode .accent-green .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-green .custom-file-input:focus ~ .custom-file-label {\n  border-color: #3dffcd;\n}\n\n.dark-mode .accent-green .page-item .page-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-green .page-item.active a,\n.dark-mode .accent-green .page-item.active .page-link {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-green .page-item.disabled a,\n.dark-mode .accent-green .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-green .page-item .page-link:hover, .dark-mode .dark-mode.accent-green .page-item .page-link:focus {\n  color: #00d69f;\n}\n\n.dark-mode .accent-teal .btn-link,\n.dark-mode .accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-teal .nav-tabs .nav-link {\n  color: #20c997;\n}\n\n.dark-mode .accent-teal .btn-link:hover,\n.dark-mode .accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-teal .nav-tabs .nav-link:hover {\n  color: #158765;\n}\n\n.dark-mode .accent-teal .dropdown-item:active, .dark-mode .accent-teal .dropdown-item.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .accent-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.dark-mode .accent-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-teal .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-teal .custom-select:focus,\n.dark-mode .accent-teal .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-teal .custom-file-input:focus ~ .custom-file-label {\n  border-color: #7eeaca;\n}\n\n.dark-mode .accent-teal .page-item .page-link {\n  color: #20c997;\n}\n\n.dark-mode .accent-teal .page-item.active a,\n.dark-mode .accent-teal .page-item.active .page-link {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .accent-teal .page-item.disabled a,\n.dark-mode .accent-teal .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-teal .page-item .page-link:hover, .dark-mode .dark-mode.accent-teal .page-item .page-link:focus {\n  color: #26dca6;\n}\n\n.dark-mode .accent-cyan .btn-link,\n.dark-mode .accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-cyan .nav-tabs .nav-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-cyan .btn-link:hover,\n.dark-mode .accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-cyan .nav-tabs .nav-link:hover {\n  color: #1d6fa5;\n}\n\n.dark-mode .accent-cyan .dropdown-item:active, .dark-mode .accent-cyan .dropdown-item.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .accent-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-cyan .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-cyan .custom-select:focus,\n.dark-mode .accent-cyan .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-cyan .custom-file-input:focus ~ .custom-file-label {\n  border-color: #a0cfee;\n}\n\n.dark-mode .accent-cyan .page-item .page-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-cyan .page-item.active a,\n.dark-mode .accent-cyan .page-item.active .page-link {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-cyan .page-item.disabled a,\n.dark-mode .accent-cyan .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-cyan .page-item .page-link:hover, .dark-mode .dark-mode.accent-cyan .page-item .page-link:focus {\n  color: #4aa3df;\n}\n\n.dark-mode .accent-white .btn-link,\n.dark-mode .accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-white .nav-tabs .nav-link {\n  color: #fff;\n}\n\n.dark-mode .accent-white .btn-link:hover,\n.dark-mode .accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-white .nav-tabs .nav-link:hover {\n  color: #d9d9d9;\n}\n\n.dark-mode .accent-white .dropdown-item:active, .dark-mode .accent-white .dropdown-item.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.dark-mode .accent-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-white .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-white .custom-select:focus,\n.dark-mode .accent-white .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-white .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.dark-mode .accent-white .page-item .page-link {\n  color: #fff;\n}\n\n.dark-mode .accent-white .page-item.active a,\n.dark-mode .accent-white .page-item.active .page-link {\n  background-color: #fff;\n  border-color: #fff;\n  color: #fff;\n}\n\n.dark-mode .accent-white .page-item.disabled a,\n.dark-mode .accent-white .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-white .page-item .page-link:hover, .dark-mode .dark-mode.accent-white .page-item .page-link:focus {\n  color: white;\n}\n\n.dark-mode .accent-gray .btn-link,\n.dark-mode .accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-gray .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray .btn-link:hover,\n.dark-mode .accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-gray .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.dark-mode .accent-gray .dropdown-item:active, .dark-mode .accent-gray .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .accent-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-gray .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-gray .custom-select:focus,\n.dark-mode .accent-gray .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-gray .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.dark-mode .accent-gray .page-item .page-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray .page-item.active a,\n.dark-mode .accent-gray .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-gray .page-item.disabled a,\n.dark-mode .accent-gray .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-gray .page-item .page-link:hover, .dark-mode .dark-mode.accent-gray .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.dark-mode .accent-gray-dark .btn-link,\n.dark-mode .accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-gray-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray-dark .btn-link:hover,\n.dark-mode .accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-gray-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.dark-mode .accent-gray-dark .dropdown-item:active, .dark-mode .accent-gray-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .accent-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-gray-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-gray-dark .custom-select:focus,\n.dark-mode .accent-gray-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-gray-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.dark-mode .accent-gray-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray-dark .page-item.active a,\n.dark-mode .accent-gray-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-gray-dark .page-item.disabled a,\n.dark-mode .accent-gray-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-gray-dark .page-item .page-link:hover, .dark-mode .dark-mode.accent-gray-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n.dark-mode .border-dark {\n  border-color: #4b545c !important;\n}\n/*# sourceMappingURL=adminlte.css.map */\n\n/*--------------------------------------------------------------\n# Back to top button\n--------------------------------------------------------------*/\n.back-to-top {\n    position: fixed;\n    visibility: hidden;\n    opacity: 0;\n    right: 15px;\n    bottom: 15px;\n    z-index: 99999;\n    background: #4154f1;\n    width: 40px;\n    height: 40px;\n    border-radius: 4px;\n    transition: all 0.4s;\n}\n\n.back-to-top i {\n    font-size: 24px;\n    color: #fff;\n    line-height: 0;\n}\n\n.back-to-top:hover {\n    background: #6776f4;\n    color: #fff;\n}\n\n.back-to-top.active {\n    visibility: visible;\n    opacity: 1;\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/css/alt/adminlte.components.css",
    "content": "/*!\n *   AdminLTE v3.2.0\n *     Only Components\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n@-webkit-keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n@keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@-webkit-keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@-webkit-keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@-webkit-keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@-webkit-keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.form-group.has-icon {\n  position: relative;\n}\n\n.form-group.has-icon .form-control {\n  padding-right: 35px;\n}\n\n.form-group.has-icon .form-icon {\n  background-color: transparent;\n  border: 0;\n  cursor: pointer;\n  font-size: 1rem;\n  padding: 0.375rem 0.75rem;\n  position: absolute;\n  right: 3px;\n  top: 0;\n}\n\n.btn-group-vertical .btn.btn-flat:first-of-type, .btn-group-vertical .btn.btn-flat:last-of-type {\n  border-radius: 0;\n}\n\n.form-control-feedback.fa, .form-control-feedback.fas, .form-control-feedback.far, .form-control-feedback.fab, .form-control-feedback.fal, .form-control-feedback.fad, .form-control-feedback.svg-inline--fa, .form-control-feedback.ion {\n  line-height: calc(2.25rem + 2px);\n}\n\n.input-lg + .form-control-feedback.fa, .input-lg + .form-control-feedback.fas, .input-lg + .form-control-feedback.far, .input-lg + .form-control-feedback.fab, .input-lg + .form-control-feedback.fal, .input-lg + .form-control-feedback.fad, .input-lg + .form-control-feedback.svg-inline--fa, .input-lg + .form-control-feedback.ion,\n.input-group-lg + .form-control-feedback.fa,\n.input-group-lg + .form-control-feedback.fas,\n.input-group-lg + .form-control-feedback.far,\n.input-group-lg + .form-control-feedback.fab,\n.input-group-lg + .form-control-feedback.fal,\n.input-group-lg + .form-control-feedback.fad,\n.input-group-lg + .form-control-feedback.svg-inline--fa,\n.input-group-lg + .form-control-feedback.ion {\n  line-height: calc(2.875rem + 2px);\n}\n\n.form-group-lg .form-control + .form-control-feedback.fa, .form-group-lg .form-control + .form-control-feedback.fas, .form-group-lg .form-control + .form-control-feedback.far, .form-group-lg .form-control + .form-control-feedback.fab, .form-group-lg .form-control + .form-control-feedback.fal, .form-group-lg .form-control + .form-control-feedback.fad, .form-group-lg .form-control + .form-control-feedback.svg-inline--fa, .form-group-lg .form-control + .form-control-feedback.ion {\n  line-height: calc(2.875rem + 2px);\n}\n\n.input-sm + .form-control-feedback.fa, .input-sm + .form-control-feedback.fas, .input-sm + .form-control-feedback.far, .input-sm + .form-control-feedback.fab, .input-sm + .form-control-feedback.fal, .input-sm + .form-control-feedback.fad, .input-sm + .form-control-feedback.svg-inline--fa, .input-sm + .form-control-feedback.ion,\n.input-group-sm + .form-control-feedback.fa,\n.input-group-sm + .form-control-feedback.fas,\n.input-group-sm + .form-control-feedback.far,\n.input-group-sm + .form-control-feedback.fab,\n.input-group-sm + .form-control-feedback.fal,\n.input-group-sm + .form-control-feedback.fad,\n.input-group-sm + .form-control-feedback.svg-inline--fa,\n.input-group-sm + .form-control-feedback.ion {\n  line-height: calc(1.8125rem + 2px);\n}\n\n.form-group-sm .form-control + .form-control-feedback.fa, .form-group-sm .form-control + .form-control-feedback.fas, .form-group-sm .form-control + .form-control-feedback.far, .form-group-sm .form-control + .form-control-feedback.fab, .form-group-sm .form-control + .form-control-feedback.fal, .form-group-sm .form-control + .form-control-feedback.fad, .form-group-sm .form-control + .form-control-feedback.svg-inline--fa, .form-group-sm .form-control + .form-control-feedback.ion {\n  line-height: calc(1.8125rem + 2px);\n}\n\nlabel:not(.form-check-label):not(.custom-file-label) {\n  font-weight: 700;\n}\n\n.warning-feedback {\n  font-size: 80%;\n  color: #ffc107;\n  display: none;\n  margin-top: 0.25rem;\n  width: 100%;\n}\n\n.warning-tooltip {\n  border-radius: 0.25rem;\n  font-size: 0.875rem;\n  background-color: rgba(255, 193, 7, 0.9);\n  color: #1f2d3d;\n  display: none;\n  line-height: 1.5;\n  margin-top: .1rem;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  position: absolute;\n  top: 100%;\n  z-index: 5;\n}\n\n.form-control.is-warning {\n  border-color: #ffc107;\n}\n\n.form-control.is-warning:focus {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.form-control.is-warning ~ .warning-feedback,\n.form-control.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\ntextarea.form-control.is-warning {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.custom-select.is-warning {\n  border-color: #ffc107;\n}\n\n.custom-select.is-warning:focus {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.custom-select.is-warning ~ .warning-feedback,\n.custom-select.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.form-control-file.is-warning ~ .warning-feedback,\n.form-control-file.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.form-check-input.is-warning ~ .form-check-label {\n  color: #ffc107;\n}\n\n.form-check-input.is-warning ~ .warning-feedback,\n.form-check-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-control-input.is-warning ~ .custom-control-label {\n  color: #ffc107;\n}\n\n.custom-control-input.is-warning ~ .custom-control-label::before {\n  border-color: #ffc107;\n}\n\n.custom-control-input.is-warning ~ .warning-feedback,\n.custom-control-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-control-input.is-warning:checked ~ .custom-control-label::before {\n  background-color: #ffce3a;\n  border-color: #ffce3a;\n}\n\n.custom-control-input.is-warning:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input.is-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffc107;\n}\n\n.custom-file-input.is-warning ~ .custom-file-label {\n  border-color: #ffc107;\n}\n\n.custom-file-input.is-warning ~ .warning-feedback,\n.custom-file-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-file-input.is-warning:focus ~ .custom-file-label {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\nbody.text-sm .input-group-text {\n  font-size: 0.875rem;\n}\n\n.form-control.form-control-border,\n.custom-select.form-control-border {\n  border-top: 0;\n  border-left: 0;\n  border-right: 0;\n  border-radius: 0;\n  box-shadow: inherit;\n}\n\n.form-control.form-control-border.border-width-2,\n.custom-select.form-control-border.border-width-2 {\n  border-bottom-width: 2px;\n}\n\n.form-control.form-control-border.border-width-3,\n.custom-select.form-control-border.border-width-3 {\n  border-bottom-width: 3px;\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::after {\n  background-color: #003e80;\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99caff;\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::after {\n  background-color: #0f401b;\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #86e29b;\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::after {\n  background-color: #093e47;\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7adeee;\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::after {\n  background-color: #876500;\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #ffe7a0;\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::after {\n  background-color: #7c151f;\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f3b7bd;\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::after {\n  background-color: #aeb9c5;\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::after {\n  background-color: #1d455b;\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #acd0e5;\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #006ad8;\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::after {\n  background-color: #193e2d;\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99d6bb;\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::after {\n  background-color: #008138;\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #9affc6;\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::after {\n  background-color: #7b0861;\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f9a2e5;\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::after {\n  background-color: #670d2e;\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f29aba;\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::after {\n  background-color: #003e80;\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99caff;\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::after {\n  background-color: #33077c;\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c3a1fa;\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::after {\n  background-color: #382063;\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c7b5e7;\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::after {\n  background-color: #95124e;\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c7dd;\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::after {\n  background-color: #7c151f;\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f3b7bd;\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::after {\n  background-color: #904201;\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fed1ac;\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::after {\n  background-color: #876500;\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #ffe7a0;\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::after {\n  background-color: #0f401b;\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #86e29b;\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::after {\n  background-color: #0e5b44;\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #94eed3;\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::after {\n  background-color: #093e47;\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7adeee;\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::after {\n  background-color: #bfbfbf;\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.custom-range.custom-range-primary:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-primary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary::-webkit-slider-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-primary::-moz-range-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-primary::-ms-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-secondary:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-secondary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-secondary::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-secondary::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-success:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-success:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success::-webkit-slider-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-webkit-slider-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-success::-moz-range-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-moz-range-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-success::-ms-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-ms-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-info:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-info:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info::-webkit-slider-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-webkit-slider-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-info::-moz-range-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-moz-range-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-info::-ms-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-ms-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-warning:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-warning:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning::-webkit-slider-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-webkit-slider-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-warning::-moz-range-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-moz-range-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-warning::-ms-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-ms-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-danger:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-danger:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger::-webkit-slider-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-webkit-slider-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-danger::-moz-range-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-moz-range-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-danger::-ms-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-ms-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-light:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-light:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light::-webkit-slider-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-light::-moz-range-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-light::-ms-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-ms-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-dark:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-lightblue:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-lightblue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue::-webkit-slider-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-webkit-slider-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-lightblue::-moz-range-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-moz-range-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-lightblue::-ms-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-ms-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-navy:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-navy:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy::-webkit-slider-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-webkit-slider-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-navy::-moz-range-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-moz-range-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-navy::-ms-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-ms-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-olive:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-olive:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive::-webkit-slider-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-webkit-slider-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-olive::-moz-range-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-moz-range-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-olive::-ms-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-ms-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-lime:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-lime:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime::-webkit-slider-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-webkit-slider-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-lime::-moz-range-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-moz-range-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-lime::-ms-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-ms-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-fuchsia:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-fuchsia:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia::-webkit-slider-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-webkit-slider-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-fuchsia::-moz-range-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-moz-range-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-fuchsia::-ms-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-ms-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-maroon:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-maroon:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon::-webkit-slider-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-webkit-slider-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-maroon::-moz-range-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-moz-range-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-maroon::-ms-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-ms-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-blue:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-blue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue::-webkit-slider-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-blue::-moz-range-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-blue::-ms-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-indigo:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-indigo:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo::-webkit-slider-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-webkit-slider-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-indigo::-moz-range-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-moz-range-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-indigo::-ms-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-ms-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-purple:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-purple:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple::-webkit-slider-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-webkit-slider-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-purple::-moz-range-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-moz-range-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-purple::-ms-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-ms-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-pink:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-pink:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink::-webkit-slider-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-webkit-slider-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-pink::-moz-range-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-moz-range-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-pink::-ms-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-ms-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-red:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-red:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red::-webkit-slider-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-webkit-slider-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-red::-moz-range-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-moz-range-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-red::-ms-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-ms-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-orange:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-orange:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange::-webkit-slider-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-webkit-slider-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-orange::-moz-range-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-moz-range-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-orange::-ms-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-ms-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-yellow:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-yellow:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow::-webkit-slider-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-webkit-slider-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-yellow::-moz-range-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-moz-range-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-yellow::-ms-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-ms-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-green:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-green:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green::-webkit-slider-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-webkit-slider-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-green::-moz-range-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-moz-range-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-green::-ms-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-ms-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-teal:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-teal:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal::-webkit-slider-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-webkit-slider-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-teal::-moz-range-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-moz-range-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-teal::-ms-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-ms-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-cyan:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-cyan:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan::-webkit-slider-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-webkit-slider-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-cyan::-moz-range-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-moz-range-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-cyan::-ms-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-ms-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-white:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-white:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white::-webkit-slider-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-white::-moz-range-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-white::-ms-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-ms-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-gray:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-gray:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray-dark:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-gray-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-gray-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-gray-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-control-input-primary:checked ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n}\n\n.custom-control-input-primary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23007bff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-primary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23007bff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-primary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input-primary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input-primary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n}\n\n.custom-control-input-secondary:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.custom-control-input-secondary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-secondary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-secondary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.custom-control-input-secondary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.custom-control-input-secondary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.custom-control-input-success:checked ~ .custom-control-label::before {\n  border-color: #28a745;\n  background-color: #28a745;\n}\n\n.custom-control-input-success.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-success.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2328a745'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-success:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.custom-control-input-success:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #71dd8a;\n}\n\n.custom-control-input-success:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #9be7ac;\n  border-color: #9be7ac;\n}\n\n.custom-control-input-info:checked ~ .custom-control-label::before {\n  border-color: #17a2b8;\n  background-color: #17a2b8;\n}\n\n.custom-control-input-info.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2317a2b8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-info.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2317a2b8'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-info:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(23, 162, 184, 0.25);\n}\n\n.custom-control-input-info:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #63d9ec;\n}\n\n.custom-control-input-info:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #90e4f1;\n  border-color: #90e4f1;\n}\n\n.custom-control-input-warning:checked ~ .custom-control-label::before {\n  border-color: #ffc107;\n  background-color: #ffc107;\n}\n\n.custom-control-input-warning.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffc107' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-warning.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffc107'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-warning:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffe187;\n}\n\n.custom-control-input-warning:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffeeba;\n  border-color: #ffeeba;\n}\n\n.custom-control-input-danger:checked ~ .custom-control-label::before {\n  border-color: #dc3545;\n  background-color: #dc3545;\n}\n\n.custom-control-input-danger.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23dc3545' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-danger.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23dc3545'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-danger:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.custom-control-input-danger:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #efa2a9;\n}\n\n.custom-control-input-danger:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f6cdd1;\n  border-color: #f6cdd1;\n}\n\n.custom-control-input-light:checked ~ .custom-control-label::before {\n  border-color: #f8f9fa;\n  background-color: #f8f9fa;\n}\n\n.custom-control-input-light.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f8f9fa' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-light.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f8f9fa'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-light:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(248, 249, 250, 0.25);\n}\n\n.custom-control-input-light:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.custom-control-input-light:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.custom-control-input-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.custom-control-input-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.custom-control-input-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.custom-control-input-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.custom-control-input-lightblue:checked ~ .custom-control-label::before {\n  border-color: #3c8dbc;\n  background-color: #3c8dbc;\n}\n\n.custom-control-input-lightblue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233c8dbc' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lightblue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233c8dbc'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lightblue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(60, 141, 188, 0.25);\n}\n\n.custom-control-input-lightblue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #99c5de;\n}\n\n.custom-control-input-lightblue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #c0dbeb;\n  border-color: #c0dbeb;\n}\n\n.custom-control-input-navy:checked ~ .custom-control-label::before {\n  border-color: #001f3f;\n  background-color: #001f3f;\n}\n\n.custom-control-input-navy.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23001f3f' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-navy.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23001f3f'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-navy:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 31, 63, 0.25);\n}\n\n.custom-control-input-navy:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #005ebf;\n}\n\n.custom-control-input-navy:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #0077f2;\n  border-color: #0077f2;\n}\n\n.custom-control-input-olive:checked ~ .custom-control-label::before {\n  border-color: #3d9970;\n  background-color: #3d9970;\n}\n\n.custom-control-input-olive.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233d9970' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-olive.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233d9970'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-olive:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(61, 153, 112, 0.25);\n}\n\n.custom-control-input-olive:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #87cfaf;\n}\n\n.custom-control-input-olive:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #abdec7;\n  border-color: #abdec7;\n}\n\n.custom-control-input-lime:checked ~ .custom-control-label::before {\n  border-color: #01ff70;\n  background-color: #01ff70;\n}\n\n.custom-control-input-lime.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2301ff70' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lime.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2301ff70'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lime:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(1, 255, 112, 0.25);\n}\n\n.custom-control-input-lime:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #81ffb8;\n}\n\n.custom-control-input-lime:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b4ffd4;\n  border-color: #b4ffd4;\n}\n\n.custom-control-input-fuchsia:checked ~ .custom-control-label::before {\n  border-color: #f012be;\n  background-color: #f012be;\n}\n\n.custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f012be' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f012be'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-fuchsia:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(240, 18, 190, 0.25);\n}\n\n.custom-control-input-fuchsia:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f88adf;\n}\n\n.custom-control-input-fuchsia:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbbaec;\n  border-color: #fbbaec;\n}\n\n.custom-control-input-maroon:checked ~ .custom-control-label::before {\n  border-color: #d81b60;\n  background-color: #d81b60;\n}\n\n.custom-control-input-maroon.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23d81b60' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-maroon.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23d81b60'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-maroon:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(216, 27, 96, 0.25);\n}\n\n.custom-control-input-maroon:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f083ab;\n}\n\n.custom-control-input-maroon:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f5b0c9;\n  border-color: #f5b0c9;\n}\n\n.custom-control-input-blue:checked ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n}\n\n.custom-control-input-blue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23007bff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-blue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23007bff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-blue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input-blue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input-blue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n}\n\n.custom-control-input-indigo:checked ~ .custom-control-label::before {\n  border-color: #6610f2;\n  background-color: #6610f2;\n}\n\n.custom-control-input-indigo.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236610f2' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-indigo.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236610f2'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-indigo:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(102, 16, 242, 0.25);\n}\n\n.custom-control-input-indigo:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b389f9;\n}\n\n.custom-control-input-indigo:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d2b9fb;\n  border-color: #d2b9fb;\n}\n\n.custom-control-input-purple:checked ~ .custom-control-label::before {\n  border-color: #6f42c1;\n  background-color: #6f42c1;\n}\n\n.custom-control-input-purple.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236f42c1' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-purple.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236f42c1'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-purple:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(111, 66, 193, 0.25);\n}\n\n.custom-control-input-purple:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b8a2e0;\n}\n\n.custom-control-input-purple:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d5c8ed;\n  border-color: #d5c8ed;\n}\n\n.custom-control-input-pink:checked ~ .custom-control-label::before {\n  border-color: #e83e8c;\n  background-color: #e83e8c;\n}\n\n.custom-control-input-pink.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e83e8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-pink.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e83e8c'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-pink:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(232, 62, 140, 0.25);\n}\n\n.custom-control-input-pink:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f6b0d0;\n}\n\n.custom-control-input-pink:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbddeb;\n  border-color: #fbddeb;\n}\n\n.custom-control-input-red:checked ~ .custom-control-label::before {\n  border-color: #dc3545;\n  background-color: #dc3545;\n}\n\n.custom-control-input-red.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23dc3545' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-red.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23dc3545'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-red:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.custom-control-input-red:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #efa2a9;\n}\n\n.custom-control-input-red:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f6cdd1;\n  border-color: #f6cdd1;\n}\n\n.custom-control-input-orange:checked ~ .custom-control-label::before {\n  border-color: #fd7e14;\n  background-color: #fd7e14;\n}\n\n.custom-control-input-orange.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fd7e14' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-orange.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fd7e14'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-orange:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(253, 126, 20, 0.25);\n}\n\n.custom-control-input-orange:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #fec392;\n}\n\n.custom-control-input-orange:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffdfc5;\n  border-color: #ffdfc5;\n}\n\n.custom-control-input-yellow:checked ~ .custom-control-label::before {\n  border-color: #ffc107;\n  background-color: #ffc107;\n}\n\n.custom-control-input-yellow.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffc107' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-yellow.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffc107'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-yellow:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input-yellow:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffe187;\n}\n\n.custom-control-input-yellow:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffeeba;\n  border-color: #ffeeba;\n}\n\n.custom-control-input-green:checked ~ .custom-control-label::before {\n  border-color: #28a745;\n  background-color: #28a745;\n}\n\n.custom-control-input-green.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-green.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2328a745'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-green:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.custom-control-input-green:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #71dd8a;\n}\n\n.custom-control-input-green:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #9be7ac;\n  border-color: #9be7ac;\n}\n\n.custom-control-input-teal:checked ~ .custom-control-label::before {\n  border-color: #20c997;\n  background-color: #20c997;\n}\n\n.custom-control-input-teal.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2320c997' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-teal.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2320c997'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-teal:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(32, 201, 151, 0.25);\n}\n\n.custom-control-input-teal:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #7eeaca;\n}\n\n.custom-control-input-teal:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #aaf1dc;\n  border-color: #aaf1dc;\n}\n\n.custom-control-input-cyan:checked ~ .custom-control-label::before {\n  border-color: #17a2b8;\n  background-color: #17a2b8;\n}\n\n.custom-control-input-cyan.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2317a2b8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-cyan.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2317a2b8'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-cyan:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(23, 162, 184, 0.25);\n}\n\n.custom-control-input-cyan:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #63d9ec;\n}\n\n.custom-control-input-cyan:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #90e4f1;\n  border-color: #90e4f1;\n}\n\n.custom-control-input-white:checked ~ .custom-control-label::before {\n  border-color: #fff;\n  background-color: #fff;\n}\n\n.custom-control-input-white.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-white.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-white:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 255, 255, 0.25);\n}\n\n.custom-control-input-white:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.custom-control-input-white:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.custom-control-input-gray:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.custom-control-input-gray.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.custom-control-input-gray:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.custom-control-input-gray:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.custom-control-input-gray-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.custom-control-input-gray-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.custom-control-input-gray-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.custom-control-input-outline ~ .custom-control-label::before {\n  background-color: transparent !important;\n  box-shadow: none;\n}\n\n.custom-control-input-outline:checked ~ .custom-control-label::before {\n  background-color: transparent;\n}\n\n.navbar-dark .btn-navbar,\n.navbar-dark .form-control-navbar {\n  background-color: #3f474e;\n  border: 1px solid #56606a;\n  color: white;\n}\n\n.navbar-dark .btn-navbar:hover {\n  background-color: #454d55;\n}\n\n.navbar-dark .btn-navbar:focus {\n  background-color: #4b545c;\n}\n\n.navbar-dark .form-control-navbar + .input-group-prepend > .btn-navbar,\n.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3f474e;\n  color: #fff;\n  border: 1px solid #56606a;\n  border-left: none;\n}\n\n.dark-mode .form-control:not(.form-control-navbar):not(.form-control-sidebar),\n.dark-mode .custom-select,\n.dark-mode .custom-file-label,\n.dark-mode .custom-file-label::after,\n.dark-mode .custom-control-label::before,\n.dark-mode .input-group-text {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .form-control:not(.form-control-navbar):not(.form-control-sidebar):not(.is-invalid):not(:focus),\n.dark-mode .custom-file-label,\n.dark-mode .custom-file-label::after {\n  border-color: #6c757d;\n}\n\n.dark-mode select {\n  background-color: #343a40;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .custom-select {\n  background: #343a40 url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23fff' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat;\n}\n\n.dark-mode .custom-select[multiple] {\n  background: #343a40;\n}\n\n.dark-mode .input-group-text {\n  border-color: #6c757d;\n}\n\n.dark-mode .custom-control-input:disabled ~ .custom-control-label::before,\n.dark-mode .custom-control-input[disabled] ~ .custom-control-label::before {\n  background-color: #3f474e;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode input:-webkit-autofill,\n.dark-mode input:-webkit-autofill:hover,\n.dark-mode input:-webkit-autofill:focus,\n.dark-mode textarea:-webkit-autofill,\n.dark-mode textarea:-webkit-autofill:hover,\n.dark-mode textarea:-webkit-autofill:focus,\n.dark-mode select:-webkit-autofill,\n.dark-mode select:-webkit-autofill:hover,\n.dark-mode select:-webkit-autofill:focus {\n  -webkit-text-fill-color: #fff;\n}\n\n.dark-mode .custom-range::-webkit-slider-runnable-track {\n  background-color: #454d55;\n}\n\n.dark-mode .custom-range::-moz-range-track {\n  background-color: #454d55;\n}\n\n.dark-mode .custom-range::-ms-track {\n  background-color: #454d55;\n}\n\n.dark-mode .custom-range.custom-range-primary:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-primary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-primary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-primary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-primary::-webkit-slider-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-primary::-webkit-slider-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-primary::-moz-range-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-primary::-moz-range-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-primary::-ms-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-primary::-ms-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-secondary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-secondary::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-secondary::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-success:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-success:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-success:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-success:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-success::-webkit-slider-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-success::-webkit-slider-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-success::-moz-range-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-success::-moz-range-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-success::-ms-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-success::-ms-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-info:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-info:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-info:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-info:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-info::-webkit-slider-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-info::-webkit-slider-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-info::-moz-range-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-info::-moz-range-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-info::-ms-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-info::-ms-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-warning:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-warning:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-warning:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-warning:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-warning::-webkit-slider-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-warning::-webkit-slider-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-warning::-moz-range-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-warning::-moz-range-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-warning::-ms-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-warning::-ms-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-danger:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-danger:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-danger:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-danger:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-danger::-webkit-slider-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-danger::-webkit-slider-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-danger::-moz-range-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-danger::-moz-range-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-danger::-ms-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-danger::-ms-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-light:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-light:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-light:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-light:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-light::-webkit-slider-thumb {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-range.custom-range-light::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-light::-moz-range-thumb {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-range.custom-range-light::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-light::-ms-thumb {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-range.custom-range-light::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-dark:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lightblue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-webkit-slider-thumb {\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-moz-range-thumb {\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-ms-thumb {\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-range.custom-range-lightblue::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-navy:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-navy:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-navy:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-navy:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-navy::-webkit-slider-thumb {\n  background-color: #002c59;\n}\n\n.dark-mode .custom-range.custom-range-navy::-webkit-slider-thumb:active {\n  background-color: #0c84ff;\n}\n\n.dark-mode .custom-range.custom-range-navy::-moz-range-thumb {\n  background-color: #002c59;\n}\n\n.dark-mode .custom-range.custom-range-navy::-moz-range-thumb:active {\n  background-color: #0c84ff;\n}\n\n.dark-mode .custom-range.custom-range-navy::-ms-thumb {\n  background-color: #002c59;\n}\n\n.dark-mode .custom-range.custom-range-navy::-ms-thumb:active {\n  background-color: #0c84ff;\n}\n\n.dark-mode .custom-range.custom-range-olive:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-olive:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-olive:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-olive:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-olive::-webkit-slider-thumb {\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-range.custom-range-olive::-webkit-slider-thumb:active {\n  background-color: #f4fbf8;\n}\n\n.dark-mode .custom-range.custom-range-olive::-moz-range-thumb {\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-range.custom-range-olive::-moz-range-thumb:active {\n  background-color: #f4fbf8;\n}\n\n.dark-mode .custom-range.custom-range-olive::-ms-thumb {\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-range.custom-range-olive::-ms-thumb:active {\n  background-color: #f4fbf8;\n}\n\n.dark-mode .custom-range.custom-range-lime:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-lime:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lime:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lime:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-lime::-webkit-slider-thumb {\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-range.custom-range-lime::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lime::-moz-range-thumb {\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-range.custom-range-lime::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-lime::-ms-thumb {\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-range.custom-range-lime::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-fuchsia:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-webkit-slider-thumb {\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-moz-range-thumb {\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-ms-thumb {\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-range.custom-range-fuchsia::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-maroon:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-maroon::-webkit-slider-thumb {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-moz-range-thumb {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-ms-thumb {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-range.custom-range-maroon::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-blue:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-blue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-blue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-blue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-blue::-webkit-slider-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-blue::-webkit-slider-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-blue::-moz-range-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-blue::-moz-range-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-blue::-ms-thumb {\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-range.custom-range-blue::-ms-thumb:active {\n  background-color: #a9c1da;\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-indigo:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-indigo::-webkit-slider-thumb {\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-webkit-slider-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-moz-range-thumb {\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-moz-range-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-ms-thumb {\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-range.custom-range-indigo::-ms-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.dark-mode .custom-range.custom-range-purple:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-purple:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-purple:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-purple:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-purple::-webkit-slider-thumb {\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-range.custom-range-purple::-webkit-slider-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.dark-mode .custom-range.custom-range-purple::-moz-range-thumb {\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-range.custom-range-purple::-moz-range-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.dark-mode .custom-range.custom-range-purple::-ms-thumb {\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-range.custom-range-purple::-ms-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.dark-mode .custom-range.custom-range-pink:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-pink:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-pink:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-pink:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-pink::-webkit-slider-thumb {\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-range.custom-range-pink::-webkit-slider-thumb:active {\n  background-color: #fbddeb;\n}\n\n.dark-mode .custom-range.custom-range-pink::-moz-range-thumb {\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-range.custom-range-pink::-moz-range-thumb:active {\n  background-color: #fbddeb;\n}\n\n.dark-mode .custom-range.custom-range-pink::-ms-thumb {\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-range.custom-range-pink::-ms-thumb:active {\n  background-color: #fbddeb;\n}\n\n.dark-mode .custom-range.custom-range-red:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-red:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-red:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-red:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-red::-webkit-slider-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-red::-webkit-slider-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-red::-moz-range-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-red::-moz-range-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-red::-ms-thumb {\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-range.custom-range-red::-ms-thumb:active {\n  background-color: #fbdedb;\n}\n\n.dark-mode .custom-range.custom-range-orange:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-orange:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-orange:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-orange:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-orange::-webkit-slider-thumb {\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-range.custom-range-orange::-webkit-slider-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.dark-mode .custom-range.custom-range-orange::-moz-range-thumb {\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-range.custom-range-orange::-moz-range-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.dark-mode .custom-range.custom-range-orange::-ms-thumb {\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-range.custom-range-orange::-ms-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-yellow:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-yellow::-webkit-slider-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-webkit-slider-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-moz-range-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-moz-range-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-ms-thumb {\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-range.custom-range-yellow::-ms-thumb:active {\n  background-color: #fce3bc;\n}\n\n.dark-mode .custom-range.custom-range-green:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-green:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-green:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-green:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-green::-webkit-slider-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-green::-webkit-slider-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-green::-moz-range-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-green::-moz-range-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-green::-ms-thumb {\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-range.custom-range-green::-ms-thumb:active {\n  background-color: #70ffda;\n}\n\n.dark-mode .custom-range.custom-range-teal:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-teal:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-teal:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-teal:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-teal::-webkit-slider-thumb {\n  background-color: #20c997;\n}\n\n.dark-mode .custom-range.custom-range-teal::-webkit-slider-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.dark-mode .custom-range.custom-range-teal::-moz-range-thumb {\n  background-color: #20c997;\n}\n\n.dark-mode .custom-range.custom-range-teal::-moz-range-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.dark-mode .custom-range.custom-range-teal::-ms-thumb {\n  background-color: #20c997;\n}\n\n.dark-mode .custom-range.custom-range-teal::-ms-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-cyan:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-cyan::-webkit-slider-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-webkit-slider-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-moz-range-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-moz-range-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-ms-thumb {\n  background-color: #3498db;\n}\n\n.dark-mode .custom-range.custom-range-cyan::-ms-thumb:active {\n  background-color: #cce5f6;\n}\n\n.dark-mode .custom-range.custom-range-white:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-white:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-white:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-white:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-white::-webkit-slider-thumb {\n  background-color: #fff;\n}\n\n.dark-mode .custom-range.custom-range-white::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-white::-moz-range-thumb {\n  background-color: #fff;\n}\n\n.dark-mode .custom-range.custom-range-white::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-white::-ms-thumb {\n  background-color: #fff;\n}\n\n.dark-mode .custom-range.custom-range-white::-ms-thumb:active {\n  background-color: white;\n}\n\n.dark-mode .custom-range.custom-range-gray:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-gray:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-gray::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-gray::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-gray::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-gray::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-range.custom-range-gray::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus {\n  outline: none;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.dark-mode .custom-range.custom-range-gray-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.dark-mode .custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-off-primary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::after {\n  background-color: #182838;\n}\n\n.dark-mode .custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-primary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #97b4d2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-off-secondary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.dark-mode .custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-on-secondary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.dark-mode .custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-off-success .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::after {\n  background-color: #003d2d;\n}\n\n.dark-mode .custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-on-success .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #56ffd4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-off-info .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::after {\n  background-color: #16527a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-on-info .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #b6daf2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-off-warning .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::after {\n  background-color: #7f5006;\n}\n\n.dark-mode .custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-on-warning .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fad9a4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-off-danger .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::after {\n  background-color: #921e12;\n}\n\n.dark-mode .custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-on-danger .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c9c4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.dark-mode .custom-switch.custom-switch-off-light .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::after {\n  background-color: #aeb9c5;\n}\n\n.dark-mode .custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.dark-mode .custom-switch.custom-switch-on-light .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-off-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-on-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::before {\n  background-color: #86bad8;\n  border-color: #3c8dbc;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lightblue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::after {\n  background-color: #367fa9;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #86bad8;\n  border-color: #3c8dbc;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lightblue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fafcfd;\n}\n\n.dark-mode .custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::before {\n  background-color: #002c59;\n  border-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-off-navy .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #002c59;\n  border-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-navy .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #0077f2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::before {\n  background-color: #74c8a3;\n  border-color: #3d9970;\n}\n\n.dark-mode .custom-switch.custom-switch-off-olive .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::after {\n  background-color: #368763;\n}\n\n.dark-mode .custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #74c8a3;\n  border-color: #3d9970;\n}\n\n.dark-mode .custom-switch.custom-switch-on-olive .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #e2f3eb;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::before {\n  background-color: #67ffa9;\n  border-color: #01ff70;\n}\n\n.dark-mode .custom-switch.custom-switch-off-lime .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::after {\n  background-color: #00e765;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #67ffa9;\n  border-color: #01ff70;\n}\n\n.dark-mode .custom-switch.custom-switch-on-lime .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::before {\n  background-color: #f672d8;\n  border-color: #f012be;\n}\n\n.dark-mode .custom-switch.custom-switch-off-fuchsia .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::after {\n  background-color: #db0ead;\n}\n\n.dark-mode .custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f672d8;\n  border-color: #f012be;\n}\n\n.dark-mode .custom-switch.custom-switch-on-fuchsia .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::before {\n  background-color: #ed6c9b;\n  border-color: #d81b60;\n}\n\n.dark-mode .custom-switch.custom-switch-off-maroon .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::after {\n  background-color: #c11856;\n}\n\n.dark-mode .custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ed6c9b;\n  border-color: #d81b60;\n}\n\n.dark-mode .custom-switch.custom-switch-on-maroon .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fef4f8;\n}\n\n.dark-mode .custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-off-blue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::after {\n  background-color: #182838;\n}\n\n.dark-mode .custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-blue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #97b4d2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.dark-mode .custom-switch.custom-switch-off-indigo .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::after {\n  background-color: #33077c;\n}\n\n.dark-mode .custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.dark-mode .custom-switch.custom-switch-on-indigo .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c3a1fa;\n}\n\n.dark-mode .custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.dark-mode .custom-switch.custom-switch-off-purple .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::after {\n  background-color: #382063;\n}\n\n.dark-mode .custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.dark-mode .custom-switch.custom-switch-on-purple .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c7b5e7;\n}\n\n.dark-mode .custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.dark-mode .custom-switch.custom-switch-off-pink .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::after {\n  background-color: #95124e;\n}\n\n.dark-mode .custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-pink .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c7dd;\n}\n\n.dark-mode .custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-off-red .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::after {\n  background-color: #921e12;\n}\n\n.dark-mode .custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .custom-switch.custom-switch-on-red .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c9c4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.dark-mode .custom-switch.custom-switch-off-orange .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::after {\n  background-color: #904201;\n}\n\n.dark-mode .custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.dark-mode .custom-switch.custom-switch-on-orange .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fed1ac;\n}\n\n.dark-mode .custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-off-yellow .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::after {\n  background-color: #7f5006;\n}\n\n.dark-mode .custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .custom-switch.custom-switch-on-yellow .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fad9a4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-off-green .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::after {\n  background-color: #003d2d;\n}\n\n.dark-mode .custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .custom-switch.custom-switch-on-green .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #56ffd4;\n}\n\n.dark-mode .custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.dark-mode .custom-switch.custom-switch-off-teal .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::after {\n  background-color: #0e5b44;\n}\n\n.dark-mode .custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.dark-mode .custom-switch.custom-switch-on-teal .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #94eed3;\n}\n\n.dark-mode .custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-off-cyan .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::after {\n  background-color: #16527a;\n}\n\n.dark-mode .custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .custom-switch.custom-switch-on-cyan .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #b6daf2;\n}\n\n.dark-mode .custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.dark-mode .custom-switch.custom-switch-off-white .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::after {\n  background-color: #bfbfbf;\n}\n\n.dark-mode .custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.dark-mode .custom-switch.custom-switch-on-white .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.dark-mode .custom-control-input-primary:checked ~ .custom-control-label::before {\n  border-color: #3f6791;\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-control-input-primary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233f6791' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-primary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233f6791'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-primary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-control-input-primary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #85a7ca;\n}\n\n.dark-mode .custom-control-input-primary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #a9c1da;\n  border-color: #a9c1da;\n}\n\n.dark-mode .custom-control-input-secondary:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-control-input-secondary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-secondary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-secondary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-control-input-secondary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.dark-mode .custom-control-input-secondary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.dark-mode .custom-control-input-success:checked ~ .custom-control-label::before {\n  border-color: #00bc8c;\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-control-input-success.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2300bc8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-success.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2300bc8c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-success:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-control-input-success:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #3dffcd;\n}\n\n.dark-mode .custom-control-input-success:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #70ffda;\n  border-color: #70ffda;\n}\n\n.dark-mode .custom-control-input-info:checked ~ .custom-control-label::before {\n  border-color: #3498db;\n  background-color: #3498db;\n}\n\n.dark-mode .custom-control-input-info.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233498db' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-info.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233498db'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-info:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-control-input-info:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #a0cfee;\n}\n\n.dark-mode .custom-control-input-info:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #cce5f6;\n  border-color: #cce5f6;\n}\n\n.dark-mode .custom-control-input-warning:checked ~ .custom-control-label::before {\n  border-color: #f39c12;\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-control-input-warning.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f39c12' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-warning.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f39c12'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-warning:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-control-input-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .custom-control-input-warning:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fce3bc;\n  border-color: #fce3bc;\n}\n\n.dark-mode .custom-control-input-danger:checked ~ .custom-control-label::before {\n  border-color: #e74c3c;\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-control-input-danger.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e74c3c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-danger.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e74c3c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-danger:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-control-input-danger:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .custom-control-input-danger:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbdedb;\n  border-color: #fbdedb;\n}\n\n.dark-mode .custom-control-input-light:checked ~ .custom-control-label::before {\n  border-color: #f8f9fa;\n  background-color: #f8f9fa;\n}\n\n.dark-mode .custom-control-input-light.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f8f9fa' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-light.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f8f9fa'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-light:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(248, 249, 250, 0.25);\n}\n\n.dark-mode .custom-control-input-light:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-light:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.dark-mode .custom-control-input-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-control-input-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.dark-mode .custom-control-input-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.dark-mode .custom-control-input-lightblue:checked ~ .custom-control-label::before {\n  border-color: #86bad8;\n  background-color: #86bad8;\n}\n\n.dark-mode .custom-control-input-lightblue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2386bad8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lightblue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2386bad8'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lightblue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(134, 186, 216, 0.25);\n}\n\n.dark-mode .custom-control-input-lightblue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .custom-control-input-lightblue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-navy:checked ~ .custom-control-label::before {\n  border-color: #002c59;\n  background-color: #002c59;\n}\n\n.dark-mode .custom-control-input-navy.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23002c59' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-navy.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23002c59'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-navy:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 44, 89, 0.25);\n}\n\n.dark-mode .custom-control-input-navy:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #006ad8;\n}\n\n.dark-mode .custom-control-input-navy:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #0c84ff;\n  border-color: #0c84ff;\n}\n\n.dark-mode .custom-control-input-olive:checked ~ .custom-control-label::before {\n  border-color: #74c8a3;\n  background-color: #74c8a3;\n}\n\n.dark-mode .custom-control-input-olive.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2374c8a3' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-olive.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2374c8a3'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-olive:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(116, 200, 163, 0.25);\n}\n\n.dark-mode .custom-control-input-olive:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #cfecdf;\n}\n\n.dark-mode .custom-control-input-olive:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f4fbf8;\n  border-color: #f4fbf8;\n}\n\n.dark-mode .custom-control-input-lime:checked ~ .custom-control-label::before {\n  border-color: #67ffa9;\n  background-color: #67ffa9;\n}\n\n.dark-mode .custom-control-input-lime.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2367ffa9' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lime.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2367ffa9'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-lime:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(103, 255, 169, 0.25);\n}\n\n.dark-mode .custom-control-input-lime:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #e7fff1;\n}\n\n.dark-mode .custom-control-input-lime:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-fuchsia:checked ~ .custom-control-label::before {\n  border-color: #f672d8;\n  background-color: #f672d8;\n}\n\n.dark-mode .custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f672d8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f672d8'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-fuchsia:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(246, 114, 216, 0.25);\n}\n\n.dark-mode .custom-control-input-fuchsia:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #feeaf9;\n}\n\n.dark-mode .custom-control-input-fuchsia:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-maroon:checked ~ .custom-control-label::before {\n  border-color: #ed6c9b;\n  background-color: #ed6c9b;\n}\n\n.dark-mode .custom-control-input-maroon.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ed6c9b' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-maroon.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ed6c9b'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-maroon:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(237, 108, 155, 0.25);\n}\n\n.dark-mode .custom-control-input-maroon:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #fbdee8;\n}\n\n.dark-mode .custom-control-input-maroon:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-blue:checked ~ .custom-control-label::before {\n  border-color: #3f6791;\n  background-color: #3f6791;\n}\n\n.dark-mode .custom-control-input-blue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233f6791' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-blue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233f6791'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-blue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(63, 103, 145, 0.25);\n}\n\n.dark-mode .custom-control-input-blue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #85a7ca;\n}\n\n.dark-mode .custom-control-input-blue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #a9c1da;\n  border-color: #a9c1da;\n}\n\n.dark-mode .custom-control-input-indigo:checked ~ .custom-control-label::before {\n  border-color: #6610f2;\n  background-color: #6610f2;\n}\n\n.dark-mode .custom-control-input-indigo.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236610f2' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-indigo.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236610f2'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-indigo:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(102, 16, 242, 0.25);\n}\n\n.dark-mode .custom-control-input-indigo:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b389f9;\n}\n\n.dark-mode .custom-control-input-indigo:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d2b9fb;\n  border-color: #d2b9fb;\n}\n\n.dark-mode .custom-control-input-purple:checked ~ .custom-control-label::before {\n  border-color: #6f42c1;\n  background-color: #6f42c1;\n}\n\n.dark-mode .custom-control-input-purple.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236f42c1' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-purple.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236f42c1'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-purple:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(111, 66, 193, 0.25);\n}\n\n.dark-mode .custom-control-input-purple:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .custom-control-input-purple:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d5c8ed;\n  border-color: #d5c8ed;\n}\n\n.dark-mode .custom-control-input-pink:checked ~ .custom-control-label::before {\n  border-color: #e83e8c;\n  background-color: #e83e8c;\n}\n\n.dark-mode .custom-control-input-pink.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e83e8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-pink.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e83e8c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-pink:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(232, 62, 140, 0.25);\n}\n\n.dark-mode .custom-control-input-pink:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .custom-control-input-pink:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbddeb;\n  border-color: #fbddeb;\n}\n\n.dark-mode .custom-control-input-red:checked ~ .custom-control-label::before {\n  border-color: #e74c3c;\n  background-color: #e74c3c;\n}\n\n.dark-mode .custom-control-input-red.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e74c3c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-red.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e74c3c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-red:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(231, 76, 60, 0.25);\n}\n\n.dark-mode .custom-control-input-red:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .custom-control-input-red:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbdedb;\n  border-color: #fbdedb;\n}\n\n.dark-mode .custom-control-input-orange:checked ~ .custom-control-label::before {\n  border-color: #fd7e14;\n  background-color: #fd7e14;\n}\n\n.dark-mode .custom-control-input-orange.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fd7e14' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-orange.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fd7e14'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-orange:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(253, 126, 20, 0.25);\n}\n\n.dark-mode .custom-control-input-orange:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #fec392;\n}\n\n.dark-mode .custom-control-input-orange:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffdfc5;\n  border-color: #ffdfc5;\n}\n\n.dark-mode .custom-control-input-yellow:checked ~ .custom-control-label::before {\n  border-color: #f39c12;\n  background-color: #f39c12;\n}\n\n.dark-mode .custom-control-input-yellow.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f39c12' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-yellow.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f39c12'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-yellow:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(243, 156, 18, 0.25);\n}\n\n.dark-mode .custom-control-input-yellow:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .custom-control-input-yellow:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fce3bc;\n  border-color: #fce3bc;\n}\n\n.dark-mode .custom-control-input-green:checked ~ .custom-control-label::before {\n  border-color: #00bc8c;\n  background-color: #00bc8c;\n}\n\n.dark-mode .custom-control-input-green.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2300bc8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-green.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2300bc8c'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-green:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 188, 140, 0.25);\n}\n\n.dark-mode .custom-control-input-green:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #3dffcd;\n}\n\n.dark-mode .custom-control-input-green:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #70ffda;\n  border-color: #70ffda;\n}\n\n.dark-mode .custom-control-input-teal:checked ~ .custom-control-label::before {\n  border-color: #20c997;\n  background-color: #20c997;\n}\n\n.dark-mode .custom-control-input-teal.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2320c997' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-teal.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2320c997'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-teal:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(32, 201, 151, 0.25);\n}\n\n.dark-mode .custom-control-input-teal:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #7eeaca;\n}\n\n.dark-mode .custom-control-input-teal:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #aaf1dc;\n  border-color: #aaf1dc;\n}\n\n.dark-mode .custom-control-input-cyan:checked ~ .custom-control-label::before {\n  border-color: #3498db;\n  background-color: #3498db;\n}\n\n.dark-mode .custom-control-input-cyan.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233498db' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-cyan.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233498db'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-cyan:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 152, 219, 0.25);\n}\n\n.dark-mode .custom-control-input-cyan:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #a0cfee;\n}\n\n.dark-mode .custom-control-input-cyan:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #cce5f6;\n  border-color: #cce5f6;\n}\n\n.dark-mode .custom-control-input-white:checked ~ .custom-control-label::before {\n  border-color: #fff;\n  background-color: #fff;\n}\n\n.dark-mode .custom-control-input-white.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-white.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-white:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 255, 255, 0.25);\n}\n\n.dark-mode .custom-control-input-white:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-white:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.dark-mode .custom-control-input-gray:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.dark-mode .custom-control-input-gray.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.dark-mode .custom-control-input-gray:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.dark-mode .custom-control-input-gray:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.dark-mode .custom-control-input-gray-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.dark-mode .custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.dark-mode .custom-control-input-gray-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.dark-mode .custom-control-input-gray-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.dark-mode .custom-control-input-gray-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.progress {\n  box-shadow: none;\n  border-radius: 1px;\n}\n\n.progress.vertical {\n  display: inline-block;\n  height: 200px;\n  margin-right: 10px;\n  position: relative;\n  width: 30px;\n}\n\n.progress.vertical > .progress-bar {\n  bottom: 0;\n  position: absolute;\n  width: 100%;\n}\n\n.progress.vertical.sm, .progress.vertical.progress-sm {\n  width: 20px;\n}\n\n.progress.vertical.xs, .progress.vertical.progress-xs {\n  width: 10px;\n}\n\n.progress.vertical.xxs, .progress.vertical.progress-xxs {\n  width: 3px;\n}\n\n.progress-group {\n  margin-bottom: 0.5rem;\n}\n\n.progress-sm {\n  height: 10px;\n}\n\n.progress-xs {\n  height: 7px;\n}\n\n.progress-xxs {\n  height: 3px;\n}\n\n.table tr > td .progress {\n  margin: 0;\n}\n\n.dark-mode .progress {\n  background: #454d55;\n}\n\n.card-primary:not(.card-outline) > .card-header {\n  background-color: #007bff;\n}\n\n.card-primary:not(.card-outline) > .card-header,\n.card-primary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-primary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-primary.card-outline {\n  border-top: 3px solid #007bff;\n}\n\n.card-primary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-primary.card-outline-tabs > .card-header a.active,\n.card-primary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #007bff;\n}\n\n.bg-primary > .card-header .btn-tool,\n.bg-gradient-primary > .card-header .btn-tool,\n.card-primary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-primary > .card-header .btn-tool:hover,\n.bg-gradient-primary > .card-header .btn-tool:hover,\n.card-primary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget .table td,\n.card.bg-primary .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #0067d6;\n  color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3395ff;\n  color: #fff;\n}\n\n.card-secondary:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.card-secondary:not(.card-outline) > .card-header,\n.card-secondary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-secondary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-secondary.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.card-secondary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-secondary.card-outline-tabs > .card-header a.active,\n.card-secondary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.bg-secondary > .card-header .btn-tool,\n.bg-gradient-secondary > .card-header .btn-tool,\n.card-secondary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-secondary > .card-header .btn-tool:hover,\n.bg-gradient-secondary > .card-header .btn-tool:hover,\n.card-secondary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget .table td,\n.card.bg-secondary .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.card-success:not(.card-outline) > .card-header {\n  background-color: #28a745;\n}\n\n.card-success:not(.card-outline) > .card-header,\n.card-success:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-success:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-success.card-outline {\n  border-top: 3px solid #28a745;\n}\n\n.card-success.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-success.card-outline-tabs > .card-header a.active,\n.card-success.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #28a745;\n}\n\n.bg-success > .card-header .btn-tool,\n.bg-gradient-success > .card-header .btn-tool,\n.card-success:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-success > .card-header .btn-tool:hover,\n.bg-gradient-success > .card-header .btn-tool:hover,\n.card-success:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget .table td,\n.card.bg-success .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #208637;\n  color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table td.active,\n.card.bg-success .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ce57;\n  color: #fff;\n}\n\n.card-info:not(.card-outline) > .card-header {\n  background-color: #17a2b8;\n}\n\n.card-info:not(.card-outline) > .card-header,\n.card-info:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-info:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-info.card-outline {\n  border-top: 3px solid #17a2b8;\n}\n\n.card-info.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-info.card-outline-tabs > .card-header a.active,\n.card-info.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #17a2b8;\n}\n\n.bg-info > .card-header .btn-tool,\n.bg-gradient-info > .card-header .btn-tool,\n.card-info:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-info > .card-header .btn-tool:hover,\n.bg-gradient-info > .card-header .btn-tool:hover,\n.card-info:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget .table td,\n.card.bg-info .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #128294;\n  color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table td.active,\n.card.bg-info .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #1fc8e3;\n  color: #fff;\n}\n\n.card-warning:not(.card-outline) > .card-header {\n  background-color: #ffc107;\n}\n\n.card-warning:not(.card-outline) > .card-header,\n.card-warning:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-warning:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-warning.card-outline {\n  border-top: 3px solid #ffc107;\n}\n\n.card-warning.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-warning.card-outline-tabs > .card-header a.active,\n.card-warning.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ffc107;\n}\n\n.bg-warning > .card-header .btn-tool,\n.bg-gradient-warning > .card-header .btn-tool,\n.card-warning:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-warning > .card-header .btn-tool:hover,\n.bg-gradient-warning > .card-header .btn-tool:hover,\n.card-warning:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget .table td,\n.card.bg-warning .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #dda600;\n  color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table td.active,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ffce3a;\n  color: #1f2d3d;\n}\n\n.card-danger:not(.card-outline) > .card-header {\n  background-color: #dc3545;\n}\n\n.card-danger:not(.card-outline) > .card-header,\n.card-danger:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-danger:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-danger.card-outline {\n  border-top: 3px solid #dc3545;\n}\n\n.card-danger.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-danger.card-outline-tabs > .card-header a.active,\n.card-danger.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #dc3545;\n}\n\n.bg-danger > .card-header .btn-tool,\n.bg-gradient-danger > .card-header .btn-tool,\n.card-danger:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-danger > .card-header .btn-tool:hover,\n.bg-gradient-danger > .card-header .btn-tool:hover,\n.card-danger:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget .table td,\n.card.bg-danger .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #c62232;\n  color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table td.active,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e4606d;\n  color: #fff;\n}\n\n.card-light:not(.card-outline) > .card-header {\n  background-color: #f8f9fa;\n}\n\n.card-light:not(.card-outline) > .card-header,\n.card-light:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-light:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-light.card-outline {\n  border-top: 3px solid #f8f9fa;\n}\n\n.card-light.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-light.card-outline-tabs > .card-header a.active,\n.card-light.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f8f9fa;\n}\n\n.bg-light > .card-header .btn-tool,\n.bg-gradient-light > .card-header .btn-tool,\n.card-light:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-light > .card-header .btn-tool:hover,\n.bg-gradient-light > .card-header .btn-tool:hover,\n.card-light:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget .table td,\n.card.bg-light .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e0e5e9;\n  color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table td.active,\n.card.bg-light .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.card-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.card-dark:not(.card-outline) > .card-header,\n.card-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.card-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-dark.card-outline-tabs > .card-header a.active,\n.card-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.bg-dark > .card-header .btn-tool,\n.bg-gradient-dark > .card-header .btn-tool,\n.card-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-dark > .card-header .btn-tool:hover,\n.bg-gradient-dark > .card-header .btn-tool:hover,\n.card-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-dark .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.card-lightblue:not(.card-outline) > .card-header {\n  background-color: #3c8dbc;\n}\n\n.card-lightblue:not(.card-outline) > .card-header,\n.card-lightblue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-lightblue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-lightblue.card-outline {\n  border-top: 3px solid #3c8dbc;\n}\n\n.card-lightblue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-lightblue.card-outline-tabs > .card-header a.active,\n.card-lightblue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3c8dbc;\n}\n\n.bg-lightblue > .card-header .btn-tool,\n.bg-gradient-lightblue > .card-header .btn-tool,\n.card-lightblue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-lightblue > .card-header .btn-tool:hover,\n.bg-gradient-lightblue > .card-header .btn-tool:hover,\n.card-lightblue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget .table td,\n.card.bg-lightblue .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #32769d;\n  color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5fa4cc;\n  color: #fff;\n}\n\n.card-navy:not(.card-outline) > .card-header {\n  background-color: #001f3f;\n}\n\n.card-navy:not(.card-outline) > .card-header,\n.card-navy:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-navy:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-navy.card-outline {\n  border-top: 3px solid #001f3f;\n}\n\n.card-navy.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-navy.card-outline-tabs > .card-header a.active,\n.card-navy.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #001f3f;\n}\n\n.bg-navy > .card-header .btn-tool,\n.bg-gradient-navy > .card-header .btn-tool,\n.card-navy:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-navy > .card-header .btn-tool:hover,\n.bg-gradient-navy > .card-header .btn-tool:hover,\n.card-navy:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget .table td,\n.card.bg-navy .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #000b16;\n  color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table td.active,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #003872;\n  color: #fff;\n}\n\n.card-olive:not(.card-outline) > .card-header {\n  background-color: #3d9970;\n}\n\n.card-olive:not(.card-outline) > .card-header,\n.card-olive:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-olive:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-olive.card-outline {\n  border-top: 3px solid #3d9970;\n}\n\n.card-olive.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-olive.card-outline-tabs > .card-header a.active,\n.card-olive.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3d9970;\n}\n\n.bg-olive > .card-header .btn-tool,\n.bg-gradient-olive > .card-header .btn-tool,\n.card-olive:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-olive > .card-header .btn-tool:hover,\n.bg-gradient-olive > .card-header .btn-tool:hover,\n.card-olive:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget .table td,\n.card.bg-olive .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #317c5b;\n  color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table td.active,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #50b98a;\n  color: #fff;\n}\n\n.card-lime:not(.card-outline) > .card-header {\n  background-color: #01ff70;\n}\n\n.card-lime:not(.card-outline) > .card-header,\n.card-lime:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-lime:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-lime.card-outline {\n  border-top: 3px solid #01ff70;\n}\n\n.card-lime.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-lime.card-outline-tabs > .card-header a.active,\n.card-lime.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #01ff70;\n}\n\n.bg-lime > .card-header .btn-tool,\n.bg-gradient-lime > .card-header .btn-tool,\n.card-lime:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-lime > .card-header .btn-tool:hover,\n.bg-gradient-lime > .card-header .btn-tool:hover,\n.card-lime:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget .table td,\n.card.bg-lime .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #00d75e;\n  color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table td.active,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ff8d;\n  color: #1f2d3d;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header {\n  background-color: #f012be;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header,\n.card-fuchsia:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-fuchsia.card-outline {\n  border-top: 3px solid #f012be;\n}\n\n.card-fuchsia.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-fuchsia.card-outline-tabs > .card-header a.active,\n.card-fuchsia.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f012be;\n}\n\n.bg-fuchsia > .card-header .btn-tool,\n.bg-gradient-fuchsia > .card-header .btn-tool,\n.card-fuchsia:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-fuchsia > .card-header .btn-tool:hover,\n.bg-gradient-fuchsia > .card-header .btn-tool:hover,\n.card-fuchsia:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget .table td,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #cc0da1;\n  color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f342cb;\n  color: #fff;\n}\n\n.card-maroon:not(.card-outline) > .card-header {\n  background-color: #d81b60;\n}\n\n.card-maroon:not(.card-outline) > .card-header,\n.card-maroon:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-maroon:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-maroon.card-outline {\n  border-top: 3px solid #d81b60;\n}\n\n.card-maroon.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-maroon.card-outline-tabs > .card-header a.active,\n.card-maroon.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #d81b60;\n}\n\n.bg-maroon > .card-header .btn-tool,\n.bg-gradient-maroon > .card-header .btn-tool,\n.card-maroon:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-maroon > .card-header .btn-tool:hover,\n.bg-gradient-maroon > .card-header .btn-tool:hover,\n.card-maroon:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget .table td,\n.card.bg-maroon .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #b41650;\n  color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.active,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e73f7c;\n  color: #fff;\n}\n\n.card-blue:not(.card-outline) > .card-header {\n  background-color: #007bff;\n}\n\n.card-blue:not(.card-outline) > .card-header,\n.card-blue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-blue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-blue.card-outline {\n  border-top: 3px solid #007bff;\n}\n\n.card-blue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-blue.card-outline-tabs > .card-header a.active,\n.card-blue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #007bff;\n}\n\n.bg-blue > .card-header .btn-tool,\n.bg-gradient-blue > .card-header .btn-tool,\n.card-blue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-blue > .card-header .btn-tool:hover,\n.bg-gradient-blue > .card-header .btn-tool:hover,\n.card-blue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget .table td,\n.card.bg-blue .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #0067d6;\n  color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3395ff;\n  color: #fff;\n}\n\n.card-indigo:not(.card-outline) > .card-header {\n  background-color: #6610f2;\n}\n\n.card-indigo:not(.card-outline) > .card-header,\n.card-indigo:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-indigo:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-indigo.card-outline {\n  border-top: 3px solid #6610f2;\n}\n\n.card-indigo.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-indigo.card-outline-tabs > .card-header a.active,\n.card-indigo.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6610f2;\n}\n\n.bg-indigo > .card-header .btn-tool,\n.bg-gradient-indigo > .card-header .btn-tool,\n.card-indigo:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-indigo > .card-header .btn-tool:hover,\n.bg-gradient-indigo > .card-header .btn-tool:hover,\n.card-indigo:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget .table td,\n.card.bg-indigo .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #550bce;\n  color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.active,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8540f5;\n  color: #fff;\n}\n\n.card-purple:not(.card-outline) > .card-header {\n  background-color: #6f42c1;\n}\n\n.card-purple:not(.card-outline) > .card-header,\n.card-purple:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-purple:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-purple.card-outline {\n  border-top: 3px solid #6f42c1;\n}\n\n.card-purple.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-purple.card-outline-tabs > .card-header a.active,\n.card-purple.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6f42c1;\n}\n\n.bg-purple > .card-header .btn-tool,\n.bg-gradient-purple > .card-header .btn-tool,\n.card-purple:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-purple > .card-header .btn-tool:hover,\n.bg-gradient-purple > .card-header .btn-tool:hover,\n.card-purple:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget .table td,\n.card.bg-purple .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #5d36a4;\n  color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table td.active,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8c68ce;\n  color: #fff;\n}\n\n.card-pink:not(.card-outline) > .card-header {\n  background-color: #e83e8c;\n}\n\n.card-pink:not(.card-outline) > .card-header,\n.card-pink:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-pink:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-pink.card-outline {\n  border-top: 3px solid #e83e8c;\n}\n\n.card-pink.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-pink.card-outline-tabs > .card-header a.active,\n.card-pink.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e83e8c;\n}\n\n.bg-pink > .card-header .btn-tool,\n.bg-gradient-pink > .card-header .btn-tool,\n.card-pink:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-pink > .card-header .btn-tool:hover,\n.bg-gradient-pink > .card-header .btn-tool:hover,\n.card-pink:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget .table td,\n.card.bg-pink .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e21b76;\n  color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table td.active,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed6ca7;\n  color: #fff;\n}\n\n.card-red:not(.card-outline) > .card-header {\n  background-color: #dc3545;\n}\n\n.card-red:not(.card-outline) > .card-header,\n.card-red:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-red:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-red.card-outline {\n  border-top: 3px solid #dc3545;\n}\n\n.card-red.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-red.card-outline-tabs > .card-header a.active,\n.card-red.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #dc3545;\n}\n\n.bg-red > .card-header .btn-tool,\n.bg-gradient-red > .card-header .btn-tool,\n.card-red:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-red > .card-header .btn-tool:hover,\n.bg-gradient-red > .card-header .btn-tool:hover,\n.card-red:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget .table td,\n.card.bg-red .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #c62232;\n  color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table td.active,\n.card.bg-red .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e4606d;\n  color: #fff;\n}\n\n.card-orange:not(.card-outline) > .card-header {\n  background-color: #fd7e14;\n}\n\n.card-orange:not(.card-outline) > .card-header,\n.card-orange:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-orange:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-orange.card-outline {\n  border-top: 3px solid #fd7e14;\n}\n\n.card-orange.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-orange.card-outline-tabs > .card-header a.active,\n.card-orange.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fd7e14;\n}\n\n.bg-orange > .card-header .btn-tool,\n.bg-gradient-orange > .card-header .btn-tool,\n.card-orange:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-orange > .card-header .btn-tool:hover,\n.bg-gradient-orange > .card-header .btn-tool:hover,\n.card-orange:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget .table td,\n.card.bg-orange .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e66a02;\n  color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table td.active,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #fd9a47;\n  color: #1f2d3d;\n}\n\n.card-yellow:not(.card-outline) > .card-header {\n  background-color: #ffc107;\n}\n\n.card-yellow:not(.card-outline) > .card-header,\n.card-yellow:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-yellow:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-yellow.card-outline {\n  border-top: 3px solid #ffc107;\n}\n\n.card-yellow.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-yellow.card-outline-tabs > .card-header a.active,\n.card-yellow.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ffc107;\n}\n\n.bg-yellow > .card-header .btn-tool,\n.bg-gradient-yellow > .card-header .btn-tool,\n.card-yellow:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-yellow > .card-header .btn-tool:hover,\n.bg-gradient-yellow > .card-header .btn-tool:hover,\n.card-yellow:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget .table td,\n.card.bg-yellow .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #dda600;\n  color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.active,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ffce3a;\n  color: #1f2d3d;\n}\n\n.card-green:not(.card-outline) > .card-header {\n  background-color: #28a745;\n}\n\n.card-green:not(.card-outline) > .card-header,\n.card-green:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-green:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-green.card-outline {\n  border-top: 3px solid #28a745;\n}\n\n.card-green.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-green.card-outline-tabs > .card-header a.active,\n.card-green.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #28a745;\n}\n\n.bg-green > .card-header .btn-tool,\n.bg-gradient-green > .card-header .btn-tool,\n.card-green:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-green > .card-header .btn-tool:hover,\n.bg-gradient-green > .card-header .btn-tool:hover,\n.card-green:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget .table td,\n.card.bg-green .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #208637;\n  color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table td.active,\n.card.bg-green .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ce57;\n  color: #fff;\n}\n\n.card-teal:not(.card-outline) > .card-header {\n  background-color: #20c997;\n}\n\n.card-teal:not(.card-outline) > .card-header,\n.card-teal:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-teal:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-teal.card-outline {\n  border-top: 3px solid #20c997;\n}\n\n.card-teal.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-teal.card-outline-tabs > .card-header a.active,\n.card-teal.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #20c997;\n}\n\n.bg-teal > .card-header .btn-tool,\n.bg-gradient-teal > .card-header .btn-tool,\n.card-teal:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-teal > .card-header .btn-tool:hover,\n.bg-gradient-teal > .card-header .btn-tool:hover,\n.card-teal:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget .table td,\n.card.bg-teal .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #1aa67d;\n  color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table td.active,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3ce0af;\n  color: #fff;\n}\n\n.card-cyan:not(.card-outline) > .card-header {\n  background-color: #17a2b8;\n}\n\n.card-cyan:not(.card-outline) > .card-header,\n.card-cyan:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-cyan:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-cyan.card-outline {\n  border-top: 3px solid #17a2b8;\n}\n\n.card-cyan.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-cyan.card-outline-tabs > .card-header a.active,\n.card-cyan.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #17a2b8;\n}\n\n.bg-cyan > .card-header .btn-tool,\n.bg-gradient-cyan > .card-header .btn-tool,\n.card-cyan:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-cyan > .card-header .btn-tool:hover,\n.bg-gradient-cyan > .card-header .btn-tool:hover,\n.card-cyan:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget .table td,\n.card.bg-cyan .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #128294;\n  color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.active,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #1fc8e3;\n  color: #fff;\n}\n\n.card-white:not(.card-outline) > .card-header {\n  background-color: #fff;\n}\n\n.card-white:not(.card-outline) > .card-header,\n.card-white:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-white:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-white.card-outline {\n  border-top: 3px solid #fff;\n}\n\n.card-white.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-white.card-outline-tabs > .card-header a.active,\n.card-white.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fff;\n}\n\n.bg-white > .card-header .btn-tool,\n.bg-gradient-white > .card-header .btn-tool,\n.card-white:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-white > .card-header .btn-tool:hover,\n.bg-gradient-white > .card-header .btn-tool:hover,\n.card-white:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget .table td,\n.card.bg-white .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #ebebeb;\n  color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table td.active,\n.card.bg-white .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.card-gray:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.card-gray:not(.card-outline) > .card-header,\n.card-gray:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-gray:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-gray.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.card-gray.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-gray.card-outline-tabs > .card-header a.active,\n.card-gray.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.bg-gray > .card-header .btn-tool,\n.bg-gradient-gray > .card-header .btn-tool,\n.card-gray:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-gray > .card-header .btn-tool:hover,\n.bg-gradient-gray > .card-header .btn-tool:hover,\n.card-gray:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget .table td,\n.card.bg-gray .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header,\n.card-gray-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-gray-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.card-gray-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-gray-dark.card-outline-tabs > .card-header a.active,\n.card-gray-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.bg-gray-dark > .card-header .btn-tool,\n.bg-gradient-gray-dark > .card-header .btn-tool,\n.card-gray-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-gray-dark > .card-header .btn-tool:hover,\n.bg-gradient-gray-dark > .card-header .btn-tool:hover,\n.card-gray-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.card {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  margin-bottom: 1rem;\n}\n\n.card.bg-dark .card-header {\n  border-color: #383f45;\n}\n\n.card.bg-dark,\n.card.bg-dark .card-body {\n  color: #fff;\n}\n\n.card.maximized-card {\n  height: 100% !important;\n  left: 0;\n  max-height: 100% !important;\n  max-width: 100% !important;\n  position: fixed;\n  top: 0;\n  width: 100% !important;\n  z-index: 1040;\n}\n\n.card.maximized-card.was-collapsed .card-body {\n  display: block !important;\n}\n\n.card.maximized-card .card-body {\n  overflow: auto;\n}\n\n.card.maximized-card [data-card-widgett=\"collapse\"] {\n  display: none;\n}\n\n.card.maximized-card .card-header,\n.card.maximized-card .card-footer {\n  border-radius: 0 !important;\n}\n\n.card.collapsed-card .card-body,\n.card.collapsed-card .card-footer {\n  display: none;\n}\n\n.card .nav.flex-column:not(.nav-sidebar) > li {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  margin: 0;\n}\n\n.card .nav.flex-column:not(.nav-sidebar) > li:last-of-type {\n  border-bottom: 0;\n}\n\n.card.height-control .card-body {\n  max-height: 300px;\n  overflow: auto;\n}\n\n.card .border-right {\n  border-right: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card .border-left {\n  border-left: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card.card-tabs:not(.card-outline) > .card-header {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.card-outline) > .card-header .nav-item:first-child .nav-link {\n  border-left-color: transparent;\n}\n\n.card.card-tabs.card-outline .nav-item {\n  border-bottom: 0;\n}\n\n.card.card-tabs.card-outline .nav-item:first-child .nav-link {\n  border-left: 0;\n  margin-left: 0;\n}\n\n.card.card-tabs .card-tools {\n  margin: .3rem .5rem;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {\n  margin-bottom: 0;\n}\n\n.card.card-tabs.expanding-card .card-header .nav-tabs .nav-item {\n  margin-bottom: -1px;\n}\n\n.card.card-outline-tabs {\n  border-top: 0;\n}\n\n.card.card-outline-tabs .card-header .nav-item:first-child .nav-link {\n  border-left: 0;\n  margin-left: 0;\n}\n\n.card.card-outline-tabs .card-header a {\n  border-top: 3px solid transparent;\n}\n\n.card.card-outline-tabs .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card.card-outline-tabs .card-header a.active:hover {\n  margin-top: 0;\n}\n\n.card.card-outline-tabs .card-tools {\n  margin: .5rem .5rem .3rem;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {\n  border-bottom: 0;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {\n  margin-bottom: 0;\n}\n\n.card.card-outline-tabs.expanding-card .card-header .nav-tabs .nav-item {\n  margin-bottom: -1px;\n}\n\nhtml.maximized-card {\n  overflow: hidden;\n}\n\n.card-header::after,\n.card-body::after,\n.card-footer::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.card-header {\n  background-color: transparent;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  padding: 0.75rem 1.25rem;\n  position: relative;\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n}\n\n.collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card-header > .card-tools {\n  float: right;\n  margin-right: -0.625rem;\n}\n\n.card-header > .card-tools .input-group,\n.card-header > .card-tools .nav,\n.card-header > .card-tools .pagination {\n  margin-bottom: -0.3rem;\n  margin-top: -0.3rem;\n}\n\n.card-header > .card-tools [data-toggle=\"tooltip\"] {\n  position: relative;\n}\n\n.card-title {\n  float: left;\n  font-size: 1.1rem;\n  font-weight: 400;\n  margin: 0;\n}\n\n.card-text {\n  clear: both;\n}\n\n.btn-tool {\n  background-color: transparent;\n  color: #adb5bd;\n  font-size: 0.875rem;\n  margin: -0.75rem 0;\n  padding: .25rem .5rem;\n}\n\n.btn-group.show .btn-tool, .btn-tool:hover {\n  color: #495057;\n}\n\n.show .btn-tool, .btn-tool:focus {\n  box-shadow: none !important;\n}\n\n.text-sm .card-title {\n  font-size: 1rem;\n}\n\n.text-sm .nav-link {\n  padding: 0.4rem 0.8rem;\n}\n\n.card-body > .table {\n  margin-bottom: 0;\n}\n\n.card-body > .table > thead > tr > th,\n.card-body > .table > thead > tr > td {\n  border-top-width: 0;\n}\n\n.card-body .fc {\n  margin-top: 5px;\n}\n\n.card-body .full-width-chart {\n  margin: -19px;\n}\n\n.card-body.p-0 .full-width-chart {\n  margin: -9px;\n}\n\n.chart-legend {\n  padding-left: 0;\n  list-style: none;\n  margin: 10px 0;\n}\n\n@media (max-width: 576px) {\n  .chart-legend > li {\n    float: left;\n    margin-right: 10px;\n  }\n}\n\n.card-comments {\n  background-color: #f8f9fa;\n}\n\n.card-comments .card-comment {\n  border-bottom: 1px solid #e9ecef;\n  padding: 8px 0;\n}\n\n.card-comments .card-comment::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.card-comments .card-comment:last-of-type {\n  border-bottom: 0;\n}\n\n.card-comments .card-comment:first-of-type {\n  padding-top: 0;\n}\n\n.card-comments .card-comment img {\n  height: 1.875rem;\n  width: 1.875rem;\n  float: left;\n}\n\n.card-comments .comment-text {\n  color: #78838e;\n  margin-left: 40px;\n}\n\n.card-comments .username {\n  color: #495057;\n  display: block;\n  font-weight: 600;\n}\n\n.card-comments .text-muted {\n  font-size: 12px;\n  font-weight: 400;\n}\n\n.todo-list {\n  list-style: none;\n  margin: 0;\n  overflow: auto;\n  padding: 0;\n}\n\n.todo-list > li {\n  border-radius: 2px;\n  background-color: #f8f9fa;\n  border-left: 2px solid #e9ecef;\n  color: #495057;\n  margin-bottom: 2px;\n  padding: 10px;\n}\n\n.todo-list > li:last-of-type {\n  margin-bottom: 0;\n}\n\n.todo-list > li > input[type=\"checkbox\"] {\n  margin: 0 10px 0 5px;\n}\n\n.todo-list > li .text {\n  display: inline-block;\n  font-weight: 600;\n  margin-left: 5px;\n}\n\n.todo-list > li .badge {\n  font-size: .7rem;\n  margin-left: 10px;\n}\n\n.todo-list > li .tools {\n  color: #dc3545;\n  display: none;\n  float: right;\n}\n\n.todo-list > li .tools > .fa,\n.todo-list > li .tools > .fas,\n.todo-list > li .tools > .far,\n.todo-list > li .tools > .fab,\n.todo-list > li .tools > .fal,\n.todo-list > li .tools > .fad,\n.todo-list > li .tools > .svg-inline--fa,\n.todo-list > li .tools > .ion {\n  cursor: pointer;\n  margin-right: 5px;\n}\n\n.todo-list > li:hover .tools {\n  display: inline-block;\n}\n\n.todo-list > li.done {\n  color: #697582;\n}\n\n.todo-list > li.done .text {\n  font-weight: 500;\n  text-decoration: line-through;\n}\n\n.todo-list > li.done .badge {\n  background-color: #adb5bd !important;\n}\n\n.todo-list .primary {\n  border-left-color: #007bff;\n}\n\n.todo-list .secondary {\n  border-left-color: #6c757d;\n}\n\n.todo-list .success {\n  border-left-color: #28a745;\n}\n\n.todo-list .info {\n  border-left-color: #17a2b8;\n}\n\n.todo-list .warning {\n  border-left-color: #ffc107;\n}\n\n.todo-list .danger {\n  border-left-color: #dc3545;\n}\n\n.todo-list .light {\n  border-left-color: #f8f9fa;\n}\n\n.todo-list .dark {\n  border-left-color: #343a40;\n}\n\n.todo-list .lightblue {\n  border-left-color: #3c8dbc;\n}\n\n.todo-list .navy {\n  border-left-color: #001f3f;\n}\n\n.todo-list .olive {\n  border-left-color: #3d9970;\n}\n\n.todo-list .lime {\n  border-left-color: #01ff70;\n}\n\n.todo-list .fuchsia {\n  border-left-color: #f012be;\n}\n\n.todo-list .maroon {\n  border-left-color: #d81b60;\n}\n\n.todo-list .blue {\n  border-left-color: #007bff;\n}\n\n.todo-list .indigo {\n  border-left-color: #6610f2;\n}\n\n.todo-list .purple {\n  border-left-color: #6f42c1;\n}\n\n.todo-list .pink {\n  border-left-color: #e83e8c;\n}\n\n.todo-list .red {\n  border-left-color: #dc3545;\n}\n\n.todo-list .orange {\n  border-left-color: #fd7e14;\n}\n\n.todo-list .yellow {\n  border-left-color: #ffc107;\n}\n\n.todo-list .green {\n  border-left-color: #28a745;\n}\n\n.todo-list .teal {\n  border-left-color: #20c997;\n}\n\n.todo-list .cyan {\n  border-left-color: #17a2b8;\n}\n\n.todo-list .white {\n  border-left-color: #fff;\n}\n\n.todo-list .gray {\n  border-left-color: #6c757d;\n}\n\n.todo-list .gray-dark {\n  border-left-color: #343a40;\n}\n\n.todo-list .handle {\n  cursor: move;\n  display: inline-block;\n  margin: 0 5px;\n}\n\n.card-input {\n  max-width: 200px;\n}\n\n.card-default .nav-item:first-child .nav-link {\n  border-left: 0;\n}\n\n.dark-mode .card-primary:not(.card-outline) > .card-header {\n  background-color: #3f6791;\n}\n\n.dark-mode .card-primary:not(.card-outline) > .card-header,\n.dark-mode .card-primary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-primary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-primary.card-outline {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .card-primary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-primary.card-outline-tabs > .card-header a.active,\n.dark-mode .card-primary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .bg-primary > .card-header .btn-tool,\n.dark-mode .bg-gradient-primary > .card-header .btn-tool,\n.dark-mode .card-primary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-primary > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-primary > .card-header .btn-tool:hover,\n.dark-mode .card-primary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #335375;\n  color: #fff;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-primary .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5080b3;\n  color: #fff;\n}\n\n.dark-mode .card-secondary:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.dark-mode .card-secondary:not(.card-outline) > .card-header,\n.dark-mode .card-secondary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-secondary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-secondary.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .card-secondary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-secondary.card-outline-tabs > .card-header a.active,\n.dark-mode .card-secondary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .bg-secondary > .card-header .btn-tool,\n.dark-mode .bg-gradient-secondary > .card-header .btn-tool,\n.dark-mode .card-secondary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-secondary > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-secondary > .card-header .btn-tool:hover,\n.dark-mode .card-secondary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-secondary .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.dark-mode .card-success:not(.card-outline) > .card-header {\n  background-color: #00bc8c;\n}\n\n.dark-mode .card-success:not(.card-outline) > .card-header,\n.dark-mode .card-success:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-success:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-success.card-outline {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .card-success.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-success.card-outline-tabs > .card-header a.active,\n.dark-mode .card-success.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .bg-success > .card-header .btn-tool,\n.dark-mode .bg-gradient-success > .card-header .btn-tool,\n.dark-mode .card-success:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-success > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-success > .card-header .btn-tool:hover,\n.dark-mode .card-success:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #00936e;\n  color: #fff;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-success .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #00efb2;\n  color: #fff;\n}\n\n.dark-mode .card-info:not(.card-outline) > .card-header {\n  background-color: #3498db;\n}\n\n.dark-mode .card-info:not(.card-outline) > .card-header,\n.dark-mode .card-info:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-info:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-info.card-outline {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .card-info.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-info.card-outline-tabs > .card-header a.active,\n.dark-mode .card-info.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .bg-info > .card-header .btn-tool,\n.dark-mode .bg-gradient-info > .card-header .btn-tool,\n.dark-mode .card-info:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-info > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-info > .card-header .btn-tool:hover,\n.dark-mode .card-info:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #2383c4;\n  color: #fff;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-info .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5faee3;\n  color: #fff;\n}\n\n.dark-mode .card-warning:not(.card-outline) > .card-header {\n  background-color: #f39c12;\n}\n\n.dark-mode .card-warning:not(.card-outline) > .card-header,\n.dark-mode .card-warning:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-warning:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-warning.card-outline {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .card-warning.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-warning.card-outline-tabs > .card-header a.active,\n.dark-mode .card-warning.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .bg-warning > .card-header .btn-tool,\n.dark-mode .bg-gradient-warning > .card-header .btn-tool,\n.dark-mode .card-warning:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-warning > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-warning > .card-header .btn-tool:hover,\n.dark-mode .card-warning:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #d2850b;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-warning .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f5b043;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-danger:not(.card-outline) > .card-header {\n  background-color: #e74c3c;\n}\n\n.dark-mode .card-danger:not(.card-outline) > .card-header,\n.dark-mode .card-danger:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-danger:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-danger.card-outline {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .card-danger.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-danger.card-outline-tabs > .card-header a.active,\n.dark-mode .card-danger.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .bg-danger > .card-header .btn-tool,\n.dark-mode .bg-gradient-danger > .card-header .btn-tool,\n.dark-mode .card-danger:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-danger > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-danger > .card-header .btn-tool:hover,\n.dark-mode .card-danger:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #df2e1b;\n  color: #fff;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-danger .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed7669;\n  color: #fff;\n}\n\n.dark-mode .card-light:not(.card-outline) > .card-header {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .card-light:not(.card-outline) > .card-header,\n.dark-mode .card-light:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-light:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-light.card-outline {\n  border-top: 3px solid #f8f9fa;\n}\n\n.dark-mode .card-light.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-light.card-outline-tabs > .card-header a.active,\n.dark-mode .card-light.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f8f9fa;\n}\n\n.dark-mode .bg-light > .card-header .btn-tool,\n.dark-mode .bg-gradient-light > .card-header .btn-tool,\n.dark-mode .card-light:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-light > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-light > .card-header .btn-tool:hover,\n.dark-mode .card-light:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e0e5e9;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-light .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.dark-mode .card-dark:not(.card-outline) > .card-header,\n.dark-mode .card-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .card-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-dark.card-outline-tabs > .card-header a.active,\n.dark-mode .card-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .bg-dark > .card-header .btn-tool,\n.dark-mode .bg-gradient-dark > .card-header .btn-tool,\n.dark-mode .card-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-dark > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-dark > .card-header .btn-tool:hover,\n.dark-mode .card-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .card-lightblue:not(.card-outline) > .card-header {\n  background-color: #86bad8;\n}\n\n.dark-mode .card-lightblue:not(.card-outline) > .card-header,\n.dark-mode .card-lightblue:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lightblue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lightblue.card-outline {\n  border-top: 3px solid #86bad8;\n}\n\n.dark-mode .card-lightblue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-lightblue.card-outline-tabs > .card-header a.active,\n.dark-mode .card-lightblue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #86bad8;\n}\n\n.dark-mode .bg-lightblue > .card-header .btn-tool,\n.dark-mode .bg-gradient-lightblue > .card-header .btn-tool,\n.dark-mode .card-lightblue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-lightblue > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-lightblue > .card-header .btn-tool:hover,\n.dark-mode .card-lightblue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #67a8ce;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-lightblue .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #acd0e5;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-navy:not(.card-outline) > .card-header {\n  background-color: #002c59;\n}\n\n.dark-mode .card-navy:not(.card-outline) > .card-header,\n.dark-mode .card-navy:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-navy:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-navy.card-outline {\n  border-top: 3px solid #002c59;\n}\n\n.dark-mode .card-navy.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-navy.card-outline-tabs > .card-header a.active,\n.dark-mode .card-navy.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #002c59;\n}\n\n.dark-mode .bg-navy > .card-header .btn-tool,\n.dark-mode .bg-gradient-navy > .card-header .btn-tool,\n.dark-mode .card-navy:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-navy > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-navy > .card-header .btn-tool:hover,\n.dark-mode .card-navy:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #001730;\n  color: #fff;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-navy .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #00458c;\n  color: #fff;\n}\n\n.dark-mode .card-olive:not(.card-outline) > .card-header {\n  background-color: #74c8a3;\n}\n\n.dark-mode .card-olive:not(.card-outline) > .card-header,\n.dark-mode .card-olive:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-olive:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-olive.card-outline {\n  border-top: 3px solid #74c8a3;\n}\n\n.dark-mode .card-olive.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-olive.card-outline-tabs > .card-header a.active,\n.dark-mode .card-olive.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #74c8a3;\n}\n\n.dark-mode .bg-olive > .card-header .btn-tool,\n.dark-mode .bg-gradient-olive > .card-header .btn-tool,\n.dark-mode .card-olive:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-olive > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-olive > .card-header .btn-tool:hover,\n.dark-mode .card-olive:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #57bc8f;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-olive .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #99d6bb;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lime:not(.card-outline) > .card-header {\n  background-color: #67ffa9;\n}\n\n.dark-mode .card-lime:not(.card-outline) > .card-header,\n.dark-mode .card-lime:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lime:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-lime.card-outline {\n  border-top: 3px solid #67ffa9;\n}\n\n.dark-mode .card-lime.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-lime.card-outline-tabs > .card-header a.active,\n.dark-mode .card-lime.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #67ffa9;\n}\n\n.dark-mode .bg-lime > .card-header .btn-tool,\n.dark-mode .bg-gradient-lime > .card-header .btn-tool,\n.dark-mode .card-lime:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-lime > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-lime > .card-header .btn-tool:hover,\n.dark-mode .card-lime:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #3eff92;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-lime .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #9affc6;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header {\n  background-color: #f672d8;\n}\n\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header,\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-fuchsia.card-outline {\n  border-top: 3px solid #f672d8;\n}\n\n.dark-mode .card-fuchsia.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-fuchsia.card-outline-tabs > .card-header a.active,\n.dark-mode .card-fuchsia.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f672d8;\n}\n\n.dark-mode .bg-fuchsia > .card-header .btn-tool,\n.dark-mode .bg-gradient-fuchsia > .card-header .btn-tool,\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-fuchsia > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-fuchsia > .card-header .btn-tool:hover,\n.dark-mode .card-fuchsia:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #f44cce;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f9a2e5;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-maroon:not(.card-outline) > .card-header {\n  background-color: #ed6c9b;\n}\n\n.dark-mode .card-maroon:not(.card-outline) > .card-header,\n.dark-mode .card-maroon:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-maroon:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-maroon.card-outline {\n  border-top: 3px solid #ed6c9b;\n}\n\n.dark-mode .card-maroon.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-maroon.card-outline-tabs > .card-header a.active,\n.dark-mode .card-maroon.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ed6c9b;\n}\n\n.dark-mode .bg-maroon > .card-header .btn-tool,\n.dark-mode .bg-gradient-maroon > .card-header .btn-tool,\n.dark-mode .card-maroon:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-maroon > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-maroon > .card-header .btn-tool:hover,\n.dark-mode .card-maroon:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e84883;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-maroon .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f29aba;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-blue:not(.card-outline) > .card-header {\n  background-color: #3f6791;\n}\n\n.dark-mode .card-blue:not(.card-outline) > .card-header,\n.dark-mode .card-blue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-blue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-blue.card-outline {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .card-blue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-blue.card-outline-tabs > .card-header a.active,\n.dark-mode .card-blue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3f6791;\n}\n\n.dark-mode .bg-blue > .card-header .btn-tool,\n.dark-mode .bg-gradient-blue > .card-header .btn-tool,\n.dark-mode .card-blue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-blue > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-blue > .card-header .btn-tool:hover,\n.dark-mode .card-blue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #335375;\n  color: #fff;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-blue .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5080b3;\n  color: #fff;\n}\n\n.dark-mode .card-indigo:not(.card-outline) > .card-header {\n  background-color: #6610f2;\n}\n\n.dark-mode .card-indigo:not(.card-outline) > .card-header,\n.dark-mode .card-indigo:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-indigo:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-indigo.card-outline {\n  border-top: 3px solid #6610f2;\n}\n\n.dark-mode .card-indigo.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-indigo.card-outline-tabs > .card-header a.active,\n.dark-mode .card-indigo.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6610f2;\n}\n\n.dark-mode .bg-indigo > .card-header .btn-tool,\n.dark-mode .bg-gradient-indigo > .card-header .btn-tool,\n.dark-mode .card-indigo:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-indigo > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-indigo > .card-header .btn-tool:hover,\n.dark-mode .card-indigo:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #550bce;\n  color: #fff;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-indigo .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8540f5;\n  color: #fff;\n}\n\n.dark-mode .card-purple:not(.card-outline) > .card-header {\n  background-color: #6f42c1;\n}\n\n.dark-mode .card-purple:not(.card-outline) > .card-header,\n.dark-mode .card-purple:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-purple:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-purple.card-outline {\n  border-top: 3px solid #6f42c1;\n}\n\n.dark-mode .card-purple.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-purple.card-outline-tabs > .card-header a.active,\n.dark-mode .card-purple.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6f42c1;\n}\n\n.dark-mode .bg-purple > .card-header .btn-tool,\n.dark-mode .bg-gradient-purple > .card-header .btn-tool,\n.dark-mode .card-purple:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-purple > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-purple > .card-header .btn-tool:hover,\n.dark-mode .card-purple:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #5d36a4;\n  color: #fff;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-purple .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8c68ce;\n  color: #fff;\n}\n\n.dark-mode .card-pink:not(.card-outline) > .card-header {\n  background-color: #e83e8c;\n}\n\n.dark-mode .card-pink:not(.card-outline) > .card-header,\n.dark-mode .card-pink:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-pink:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-pink.card-outline {\n  border-top: 3px solid #e83e8c;\n}\n\n.dark-mode .card-pink.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-pink.card-outline-tabs > .card-header a.active,\n.dark-mode .card-pink.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e83e8c;\n}\n\n.dark-mode .bg-pink > .card-header .btn-tool,\n.dark-mode .bg-gradient-pink > .card-header .btn-tool,\n.dark-mode .card-pink:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-pink > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-pink > .card-header .btn-tool:hover,\n.dark-mode .card-pink:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e21b76;\n  color: #fff;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-pink .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed6ca7;\n  color: #fff;\n}\n\n.dark-mode .card-red:not(.card-outline) > .card-header {\n  background-color: #e74c3c;\n}\n\n.dark-mode .card-red:not(.card-outline) > .card-header,\n.dark-mode .card-red:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-red:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-red.card-outline {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .card-red.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-red.card-outline-tabs > .card-header a.active,\n.dark-mode .card-red.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e74c3c;\n}\n\n.dark-mode .bg-red > .card-header .btn-tool,\n.dark-mode .bg-gradient-red > .card-header .btn-tool,\n.dark-mode .card-red:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-red > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-red > .card-header .btn-tool:hover,\n.dark-mode .card-red:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #df2e1b;\n  color: #fff;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-red .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed7669;\n  color: #fff;\n}\n\n.dark-mode .card-orange:not(.card-outline) > .card-header {\n  background-color: #fd7e14;\n}\n\n.dark-mode .card-orange:not(.card-outline) > .card-header,\n.dark-mode .card-orange:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-orange:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-orange.card-outline {\n  border-top: 3px solid #fd7e14;\n}\n\n.dark-mode .card-orange.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-orange.card-outline-tabs > .card-header a.active,\n.dark-mode .card-orange.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fd7e14;\n}\n\n.dark-mode .bg-orange > .card-header .btn-tool,\n.dark-mode .bg-gradient-orange > .card-header .btn-tool,\n.dark-mode .card-orange:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-orange > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-orange > .card-header .btn-tool:hover,\n.dark-mode .card-orange:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e66a02;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-orange .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #fd9a47;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-yellow:not(.card-outline) > .card-header {\n  background-color: #f39c12;\n}\n\n.dark-mode .card-yellow:not(.card-outline) > .card-header,\n.dark-mode .card-yellow:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-yellow:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-yellow.card-outline {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .card-yellow.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-yellow.card-outline-tabs > .card-header a.active,\n.dark-mode .card-yellow.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f39c12;\n}\n\n.dark-mode .bg-yellow > .card-header .btn-tool,\n.dark-mode .bg-gradient-yellow > .card-header .btn-tool,\n.dark-mode .card-yellow:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-yellow > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-yellow > .card-header .btn-tool:hover,\n.dark-mode .card-yellow:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #d2850b;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-yellow .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f5b043;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-green:not(.card-outline) > .card-header {\n  background-color: #00bc8c;\n}\n\n.dark-mode .card-green:not(.card-outline) > .card-header,\n.dark-mode .card-green:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-green:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-green.card-outline {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .card-green.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-green.card-outline-tabs > .card-header a.active,\n.dark-mode .card-green.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #00bc8c;\n}\n\n.dark-mode .bg-green > .card-header .btn-tool,\n.dark-mode .bg-gradient-green > .card-header .btn-tool,\n.dark-mode .card-green:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-green > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-green > .card-header .btn-tool:hover,\n.dark-mode .card-green:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #00936e;\n  color: #fff;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-green .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #00efb2;\n  color: #fff;\n}\n\n.dark-mode .card-teal:not(.card-outline) > .card-header {\n  background-color: #20c997;\n}\n\n.dark-mode .card-teal:not(.card-outline) > .card-header,\n.dark-mode .card-teal:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-teal:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-teal.card-outline {\n  border-top: 3px solid #20c997;\n}\n\n.dark-mode .card-teal.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-teal.card-outline-tabs > .card-header a.active,\n.dark-mode .card-teal.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #20c997;\n}\n\n.dark-mode .bg-teal > .card-header .btn-tool,\n.dark-mode .bg-gradient-teal > .card-header .btn-tool,\n.dark-mode .card-teal:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-teal > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-teal > .card-header .btn-tool:hover,\n.dark-mode .card-teal:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #1aa67d;\n  color: #fff;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-teal .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3ce0af;\n  color: #fff;\n}\n\n.dark-mode .card-cyan:not(.card-outline) > .card-header {\n  background-color: #3498db;\n}\n\n.dark-mode .card-cyan:not(.card-outline) > .card-header,\n.dark-mode .card-cyan:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-cyan:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-cyan.card-outline {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .card-cyan.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-cyan.card-outline-tabs > .card-header a.active,\n.dark-mode .card-cyan.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3498db;\n}\n\n.dark-mode .bg-cyan > .card-header .btn-tool,\n.dark-mode .bg-gradient-cyan > .card-header .btn-tool,\n.dark-mode .card-cyan:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-cyan > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-cyan > .card-header .btn-tool:hover,\n.dark-mode .card-cyan:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #2383c4;\n  color: #fff;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-cyan .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5faee3;\n  color: #fff;\n}\n\n.dark-mode .card-white:not(.card-outline) > .card-header {\n  background-color: #fff;\n}\n\n.dark-mode .card-white:not(.card-outline) > .card-header,\n.dark-mode .card-white:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-white:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-white.card-outline {\n  border-top: 3px solid #fff;\n}\n\n.dark-mode .card-white.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-white.card-outline-tabs > .card-header a.active,\n.dark-mode .card-white.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fff;\n}\n\n.dark-mode .bg-white > .card-header .btn-tool,\n.dark-mode .bg-gradient-white > .card-header .btn-tool,\n.dark-mode .card-white:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.dark-mode .bg-white > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-white > .card-header .btn-tool:hover,\n.dark-mode .card-white:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #ebebeb;\n  color: #1f2d3d;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-white .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.dark-mode .card-gray:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.dark-mode .card-gray:not(.card-outline) > .card-header,\n.dark-mode .card-gray:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-gray:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-gray.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .card-gray.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-gray.card-outline-tabs > .card-header a.active,\n.dark-mode .card-gray.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.dark-mode .bg-gray > .card-header .btn-tool,\n.dark-mode .bg-gradient-gray > .card-header .btn-tool,\n.dark-mode .card-gray:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-gray > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-gray > .card-header .btn-tool:hover,\n.dark-mode .card-gray:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gray .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header,\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.dark-mode .card-gray-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .card-gray-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.dark-mode .card-gray-dark.card-outline-tabs > .card-header a.active,\n.dark-mode .card-gray-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.dark-mode .bg-gray-dark > .card-header .btn-tool,\n.dark-mode .bg-gradient-gray-dark > .card-header .btn-tool,\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .bg-gray-dark > .card-header .btn-tool:hover,\n.dark-mode .bg-gradient-gray-dark > .card-header .btn-tool:hover,\n.dark-mode .card-gray-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget .table th,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table td,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.today::before,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.dark-mode .card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .card {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .card .card {\n  background-color: #3f474e;\n  color: #fff;\n}\n\n.dark-mode .card .nav.flex-column > li {\n  border-bottom-color: #6c757d;\n}\n\n.dark-mode .card .card-footer {\n  background-color: rgba(0, 0, 0, 0.1);\n}\n\n.dark-mode .card.card-outline-tabs {\n  border-top: 0;\n}\n\n.dark-mode .card.card-outline-tabs .card-header a:hover {\n  border-top-color: #6c757d;\n  border-bottom-color: transparent;\n}\n\n.dark-mode .card:not(.card-outline) > .card-header a.active {\n  color: #fff;\n}\n\n.dark-mode .card-comments {\n  background-color: #373d44;\n}\n\n.dark-mode .card-comments .username {\n  color: #ced4da;\n}\n\n.dark-mode .card-comments .card-comment {\n  border-bottom-color: #454d55;\n}\n\n.dark-mode .todo-list > li {\n  background-color: #3f474e;\n  border-color: #454d55;\n  color: #fff;\n}\n\n.dark-mode .todo-list .primary {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .todo-list .secondary {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .todo-list .success {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .todo-list .info {\n  border-left-color: #3498db;\n}\n\n.dark-mode .todo-list .warning {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .todo-list .danger {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .todo-list .light {\n  border-left-color: #f8f9fa;\n}\n\n.dark-mode .todo-list .dark {\n  border-left-color: #343a40;\n}\n\n.dark-mode .todo-list .lightblue {\n  border-left-color: #86bad8;\n}\n\n.dark-mode .todo-list .navy {\n  border-left-color: #002c59;\n}\n\n.dark-mode .todo-list .olive {\n  border-left-color: #74c8a3;\n}\n\n.dark-mode .todo-list .lime {\n  border-left-color: #67ffa9;\n}\n\n.dark-mode .todo-list .fuchsia {\n  border-left-color: #f672d8;\n}\n\n.dark-mode .todo-list .maroon {\n  border-left-color: #ed6c9b;\n}\n\n.dark-mode .todo-list .blue {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .todo-list .indigo {\n  border-left-color: #6610f2;\n}\n\n.dark-mode .todo-list .purple {\n  border-left-color: #6f42c1;\n}\n\n.dark-mode .todo-list .pink {\n  border-left-color: #e83e8c;\n}\n\n.dark-mode .todo-list .red {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .todo-list .orange {\n  border-left-color: #fd7e14;\n}\n\n.dark-mode .todo-list .yellow {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .todo-list .green {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .todo-list .teal {\n  border-left-color: #20c997;\n}\n\n.dark-mode .todo-list .cyan {\n  border-left-color: #3498db;\n}\n\n.dark-mode .todo-list .white {\n  border-left-color: #fff;\n}\n\n.dark-mode .todo-list .gray {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .todo-list .gray-dark {\n  border-left-color: #343a40;\n}\n\n.modal-dialog .overlay {\n  display: -ms-flexbox;\n  display: flex;\n  position: absolute;\n  left: 0;\n  top: 0;\n  bottom: 0;\n  right: 0;\n  margin: -1px;\n  z-index: 1052;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: rgba(0, 0, 0, 0.7);\n  color: #666f76;\n  border-radius: 0.3rem;\n}\n\n.modal-content.bg-warning .modal-header,\n.modal-content.bg-warning .modal-footer {\n  border-color: #343a40;\n}\n\n.modal-content.bg-primary .close, .modal-content.bg-secondary .close, .modal-content.bg-info .close, .modal-content.bg-danger .close, .modal-content.bg-success .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .modal-header,\n.dark-mode .modal-footer {\n  border-color: #6c757d;\n}\n\n.dark-mode .modal-content {\n  background-color: #343a40;\n}\n\n.dark-mode .modal-content.bg-warning .modal-header,\n.dark-mode .modal-content.bg-warning .modal-footer {\n  border-color: #6c757d;\n}\n\n.dark-mode .modal-content.bg-warning .close {\n  color: #343a40 !important;\n  text-shadow: 0 1px 0 #495057 !important;\n}\n\n.dark-mode .modal-content.bg-primary .modal-header,\n.dark-mode .modal-content.bg-primary .modal-footer, .dark-mode .modal-content.bg-secondary .modal-header,\n.dark-mode .modal-content.bg-secondary .modal-footer, .dark-mode .modal-content.bg-info .modal-header,\n.dark-mode .modal-content.bg-info .modal-footer, .dark-mode .modal-content.bg-danger .modal-header,\n.dark-mode .modal-content.bg-danger .modal-footer, .dark-mode .modal-content.bg-success .modal-header,\n.dark-mode .modal-content.bg-success .modal-footer {\n  border-color: #fff;\n}\n\n.toasts-top-right {\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 1040;\n}\n\n.toasts-top-right.fixed {\n  position: fixed;\n}\n\n.toasts-top-left {\n  left: 0;\n  position: absolute;\n  top: 0;\n  z-index: 1040;\n}\n\n.toasts-top-left.fixed {\n  position: fixed;\n}\n\n.toasts-bottom-right {\n  bottom: 0;\n  position: absolute;\n  right: 0;\n  z-index: 1040;\n}\n\n.toasts-bottom-right.fixed {\n  position: fixed;\n}\n\n.toasts-bottom-left {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  z-index: 1040;\n}\n\n.toasts-bottom-left.fixed {\n  position: fixed;\n}\n\n.dark-mode .toast {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast .toast-header {\n  background-color: rgba(52, 58, 64, 0.7);\n  color: #f8f9fa;\n}\n\n.dark-mode .toast.bg-primary {\n  background-color: rgba(63, 103, 145, 0.9) !important;\n}\n\n.dark-mode .toast.bg-primary .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-primary .toast-header {\n  background-color: rgba(63, 103, 145, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-secondary {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.dark-mode .toast.bg-secondary .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-secondary .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-success {\n  background-color: rgba(0, 188, 140, 0.9) !important;\n}\n\n.dark-mode .toast.bg-success .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-success .toast-header {\n  background-color: rgba(0, 188, 140, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-info {\n  background-color: rgba(52, 152, 219, 0.9) !important;\n}\n\n.dark-mode .toast.bg-info .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-info .toast-header {\n  background-color: rgba(52, 152, 219, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-warning {\n  background-color: rgba(243, 156, 18, 0.9) !important;\n}\n\n.dark-mode .toast.bg-warning .toast-header {\n  background-color: rgba(243, 156, 18, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-danger {\n  background-color: rgba(231, 76, 60, 0.9) !important;\n}\n\n.dark-mode .toast.bg-danger .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-danger .toast-header {\n  background-color: rgba(231, 76, 60, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-light {\n  background-color: rgba(248, 249, 250, 0.9) !important;\n}\n\n.dark-mode .toast.bg-light .toast-header {\n  background-color: rgba(248, 249, 250, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.dark-mode .toast.bg-dark .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-lightblue {\n  background-color: rgba(134, 186, 216, 0.9) !important;\n}\n\n.dark-mode .toast.bg-lightblue .toast-header {\n  background-color: rgba(134, 186, 216, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-navy {\n  background-color: rgba(0, 44, 89, 0.9) !important;\n}\n\n.dark-mode .toast.bg-navy .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-navy .toast-header {\n  background-color: rgba(0, 44, 89, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-olive {\n  background-color: rgba(116, 200, 163, 0.9) !important;\n}\n\n.dark-mode .toast.bg-olive .toast-header {\n  background-color: rgba(116, 200, 163, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-lime {\n  background-color: rgba(103, 255, 169, 0.9) !important;\n}\n\n.dark-mode .toast.bg-lime .toast-header {\n  background-color: rgba(103, 255, 169, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-fuchsia {\n  background-color: rgba(246, 114, 216, 0.9) !important;\n}\n\n.dark-mode .toast.bg-fuchsia .toast-header {\n  background-color: rgba(246, 114, 216, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-maroon {\n  background-color: rgba(237, 108, 155, 0.9) !important;\n}\n\n.dark-mode .toast.bg-maroon .toast-header {\n  background-color: rgba(237, 108, 155, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-blue {\n  background-color: rgba(63, 103, 145, 0.9) !important;\n}\n\n.dark-mode .toast.bg-blue .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-blue .toast-header {\n  background-color: rgba(63, 103, 145, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-indigo {\n  background-color: rgba(102, 16, 242, 0.9) !important;\n}\n\n.dark-mode .toast.bg-indigo .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-indigo .toast-header {\n  background-color: rgba(102, 16, 242, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-purple {\n  background-color: rgba(111, 66, 193, 0.9) !important;\n}\n\n.dark-mode .toast.bg-purple .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-purple .toast-header {\n  background-color: rgba(111, 66, 193, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-pink {\n  background-color: rgba(232, 62, 140, 0.9) !important;\n}\n\n.dark-mode .toast.bg-pink .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-pink .toast-header {\n  background-color: rgba(232, 62, 140, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-red {\n  background-color: rgba(231, 76, 60, 0.9) !important;\n}\n\n.dark-mode .toast.bg-red .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-red .toast-header {\n  background-color: rgba(231, 76, 60, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-orange {\n  background-color: rgba(253, 126, 20, 0.9) !important;\n}\n\n.dark-mode .toast.bg-orange .toast-header {\n  background-color: rgba(253, 126, 20, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-yellow {\n  background-color: rgba(243, 156, 18, 0.9) !important;\n}\n\n.dark-mode .toast.bg-yellow .toast-header {\n  background-color: rgba(243, 156, 18, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-green {\n  background-color: rgba(0, 188, 140, 0.9) !important;\n}\n\n.dark-mode .toast.bg-green .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-green .toast-header {\n  background-color: rgba(0, 188, 140, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-teal {\n  background-color: rgba(32, 201, 151, 0.9) !important;\n}\n\n.dark-mode .toast.bg-teal .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-teal .toast-header {\n  background-color: rgba(32, 201, 151, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-cyan {\n  background-color: rgba(52, 152, 219, 0.9) !important;\n}\n\n.dark-mode .toast.bg-cyan .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-cyan .toast-header {\n  background-color: rgba(52, 152, 219, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-white {\n  background-color: rgba(255, 255, 255, 0.9) !important;\n}\n\n.dark-mode .toast.bg-white .toast-header {\n  background-color: rgba(255, 255, 255, 0.85);\n  color: #1f2d3d;\n}\n\n.dark-mode .toast.bg-gray {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.dark-mode .toast.bg-gray .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-gray .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.dark-mode .toast.bg-gray-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.dark-mode .toast.bg-gray-dark .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.dark-mode .toast.bg-gray-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.toast.bg-primary {\n  background-color: rgba(0, 123, 255, 0.9) !important;\n}\n\n.toast.bg-primary .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-primary .toast-header {\n  background-color: rgba(0, 123, 255, 0.85);\n  color: #fff;\n}\n\n.toast.bg-secondary {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.toast.bg-secondary .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-secondary .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.toast.bg-success {\n  background-color: rgba(40, 167, 69, 0.9) !important;\n}\n\n.toast.bg-success .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-success .toast-header {\n  background-color: rgba(40, 167, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-info {\n  background-color: rgba(23, 162, 184, 0.9) !important;\n}\n\n.toast.bg-info .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-info .toast-header {\n  background-color: rgba(23, 162, 184, 0.85);\n  color: #fff;\n}\n\n.toast.bg-warning {\n  background-color: rgba(255, 193, 7, 0.9) !important;\n}\n\n.toast.bg-warning .toast-header {\n  background-color: rgba(255, 193, 7, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-danger {\n  background-color: rgba(220, 53, 69, 0.9) !important;\n}\n\n.toast.bg-danger .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-danger .toast-header {\n  background-color: rgba(220, 53, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-light {\n  background-color: rgba(248, 249, 250, 0.9) !important;\n}\n\n.toast.bg-light .toast-header {\n  background-color: rgba(248, 249, 250, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.toast.bg-dark .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.toast.bg-lightblue {\n  background-color: rgba(60, 141, 188, 0.9) !important;\n}\n\n.toast.bg-lightblue .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-lightblue .toast-header {\n  background-color: rgba(60, 141, 188, 0.85);\n  color: #fff;\n}\n\n.toast.bg-navy {\n  background-color: rgba(0, 31, 63, 0.9) !important;\n}\n\n.toast.bg-navy .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-navy .toast-header {\n  background-color: rgba(0, 31, 63, 0.85);\n  color: #fff;\n}\n\n.toast.bg-olive {\n  background-color: rgba(61, 153, 112, 0.9) !important;\n}\n\n.toast.bg-olive .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-olive .toast-header {\n  background-color: rgba(61, 153, 112, 0.85);\n  color: #fff;\n}\n\n.toast.bg-lime {\n  background-color: rgba(1, 255, 112, 0.9) !important;\n}\n\n.toast.bg-lime .toast-header {\n  background-color: rgba(1, 255, 112, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-fuchsia {\n  background-color: rgba(240, 18, 190, 0.9) !important;\n}\n\n.toast.bg-fuchsia .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-fuchsia .toast-header {\n  background-color: rgba(240, 18, 190, 0.85);\n  color: #fff;\n}\n\n.toast.bg-maroon {\n  background-color: rgba(216, 27, 96, 0.9) !important;\n}\n\n.toast.bg-maroon .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-maroon .toast-header {\n  background-color: rgba(216, 27, 96, 0.85);\n  color: #fff;\n}\n\n.toast.bg-blue {\n  background-color: rgba(0, 123, 255, 0.9) !important;\n}\n\n.toast.bg-blue .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-blue .toast-header {\n  background-color: rgba(0, 123, 255, 0.85);\n  color: #fff;\n}\n\n.toast.bg-indigo {\n  background-color: rgba(102, 16, 242, 0.9) !important;\n}\n\n.toast.bg-indigo .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-indigo .toast-header {\n  background-color: rgba(102, 16, 242, 0.85);\n  color: #fff;\n}\n\n.toast.bg-purple {\n  background-color: rgba(111, 66, 193, 0.9) !important;\n}\n\n.toast.bg-purple .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-purple .toast-header {\n  background-color: rgba(111, 66, 193, 0.85);\n  color: #fff;\n}\n\n.toast.bg-pink {\n  background-color: rgba(232, 62, 140, 0.9) !important;\n}\n\n.toast.bg-pink .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-pink .toast-header {\n  background-color: rgba(232, 62, 140, 0.85);\n  color: #fff;\n}\n\n.toast.bg-red {\n  background-color: rgba(220, 53, 69, 0.9) !important;\n}\n\n.toast.bg-red .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-red .toast-header {\n  background-color: rgba(220, 53, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-orange {\n  background-color: rgba(253, 126, 20, 0.9) !important;\n}\n\n.toast.bg-orange .toast-header {\n  background-color: rgba(253, 126, 20, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-yellow {\n  background-color: rgba(255, 193, 7, 0.9) !important;\n}\n\n.toast.bg-yellow .toast-header {\n  background-color: rgba(255, 193, 7, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-green {\n  background-color: rgba(40, 167, 69, 0.9) !important;\n}\n\n.toast.bg-green .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-green .toast-header {\n  background-color: rgba(40, 167, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-teal {\n  background-color: rgba(32, 201, 151, 0.9) !important;\n}\n\n.toast.bg-teal .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-teal .toast-header {\n  background-color: rgba(32, 201, 151, 0.85);\n  color: #fff;\n}\n\n.toast.bg-cyan {\n  background-color: rgba(23, 162, 184, 0.9) !important;\n}\n\n.toast.bg-cyan .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-cyan .toast-header {\n  background-color: rgba(23, 162, 184, 0.85);\n  color: #fff;\n}\n\n.toast.bg-white {\n  background-color: rgba(255, 255, 255, 0.9) !important;\n}\n\n.toast.bg-white .toast-header {\n  background-color: rgba(255, 255, 255, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-gray {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.toast.bg-gray .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-gray .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.toast.bg-gray-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.toast.bg-gray-dark .close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-gray-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.btn.disabled, .btn:disabled {\n  cursor: not-allowed;\n}\n\n.btn.btn-flat {\n  border-radius: 0;\n  border-width: 1px;\n  box-shadow: none;\n}\n\n.btn.btn-file {\n  overflow: hidden;\n  position: relative;\n}\n\n.btn.btn-file > input[type=\"file\"] {\n  background-color: #fff;\n  cursor: inherit;\n  display: block;\n  font-size: 100px;\n  min-height: 100%;\n  min-width: 100%;\n  opacity: 0;\n  outline: none;\n  position: absolute;\n  right: 0;\n  text-align: right;\n  top: 0;\n}\n\n.text-sm .btn {\n  font-size: 0.875rem !important;\n}\n\n.btn-default {\n  background-color: #f8f9fa;\n  border-color: #ddd;\n  color: #444;\n}\n\n.btn-default:hover, .btn-default:active, .btn-default.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.btn-default.disabled, .btn-default:disabled {\n  color: #444;\n  background-color: #f8f9fa;\n}\n\n.btn-outline-light {\n  color: #bdc6d0;\n  border-color: #bdc6d0;\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n  color: #bdc6d0;\n  border-color: #bdc6d0;\n}\n\n.btn-app {\n  border-radius: 3px;\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  color: #6c757d;\n  font-size: 12px;\n  height: 60px;\n  margin: 0 0 10px 10px;\n  min-width: 80px;\n  padding: 15px 5px;\n  position: relative;\n  text-align: center;\n}\n\n.btn-app > .fa,\n.btn-app > .fas,\n.btn-app > .far,\n.btn-app > .fab,\n.btn-app > .fal,\n.btn-app > .fad,\n.btn-app > .svg-inline--fa,\n.btn-app > .ion {\n  display: block;\n  font-size: 20px;\n}\n\n.btn-app > .svg-inline--fa {\n  margin: 0 auto;\n}\n\n.btn-app:hover {\n  background-color: #f8f9fa;\n  border-color: #aaaaaa;\n  color: #444;\n}\n\n.btn-app:active, .btn-app:focus {\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn-app > .badge {\n  font-size: 10px;\n  font-weight: 400;\n  position: absolute;\n  right: -10px;\n  top: -3px;\n}\n\n.btn-xs {\n  padding: 0.125rem 0.25rem;\n  font-size: 0.75rem;\n  line-height: 1.5;\n  border-radius: 0.15rem;\n}\n\n.dark-mode .btn-default,\n.dark-mode .btn-app {\n  background-color: #3a4047;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-default:hover, .dark-mode .btn-default:focus,\n.dark-mode .btn-app:hover,\n.dark-mode .btn-app:focus {\n  background-color: #3f474e;\n  color: #dee2e6;\n  border-color: #727b84;\n}\n\n.dark-mode .btn-light {\n  background-color: #454d55;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-light:hover, .dark-mode .btn-light:focus {\n  background-color: #4b545c;\n  color: #dee2e6;\n  border-color: #78828a;\n}\n\n.dark-mode .btn-primary {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n  box-shadow: none;\n}\n\n.dark-mode .btn-primary:hover {\n  color: #fff;\n  background-color: #335476;\n  border-color: #304e6d;\n}\n\n.dark-mode .btn-primary:focus, .dark-mode .btn-primary.focus {\n  color: #fff;\n  background-color: #335476;\n  border-color: #304e6d;\n  box-shadow: 0 0 0 0 rgba(92, 126, 162, 0.5);\n}\n\n.dark-mode .btn-primary.disabled, .dark-mode .btn-primary:disabled {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-primary:not(:disabled):not(.disabled):active, .dark-mode .btn-primary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #304e6d;\n  border-color: #2c4765;\n}\n\n.dark-mode .btn-primary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(92, 126, 162, 0.5);\n}\n\n.dark-mode .btn-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n  box-shadow: none;\n}\n\n.dark-mode .btn-secondary:hover {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n}\n\n.dark-mode .btn-secondary:focus, .dark-mode .btn-secondary.focus {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.dark-mode .btn-secondary.disabled, .dark-mode .btn-secondary:disabled {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-secondary:not(:disabled):not(.disabled):active, .dark-mode .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #545b62;\n  border-color: #4e555b;\n}\n\n.dark-mode .btn-secondary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.dark-mode .btn-success {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  box-shadow: none;\n}\n\n.dark-mode .btn-success:hover {\n  color: #fff;\n  background-color: #009670;\n  border-color: #008966;\n}\n\n.dark-mode .btn-success:focus, .dark-mode .btn-success.focus {\n  color: #fff;\n  background-color: #009670;\n  border-color: #008966;\n  box-shadow: 0 0 0 0 rgba(38, 198, 157, 0.5);\n}\n\n.dark-mode .btn-success.disabled, .dark-mode .btn-success:disabled {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-success:not(:disabled):not(.disabled):active, .dark-mode .btn-success:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-success.dropdown-toggle {\n  color: #fff;\n  background-color: #008966;\n  border-color: #007c5d;\n}\n\n.dark-mode .btn-success:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(38, 198, 157, 0.5);\n}\n\n.dark-mode .btn-info {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n  box-shadow: none;\n}\n\n.dark-mode .btn-info:hover {\n  color: #fff;\n  background-color: #2384c6;\n  border-color: #217dbb;\n}\n\n.dark-mode .btn-info:focus, .dark-mode .btn-info.focus {\n  color: #fff;\n  background-color: #2384c6;\n  border-color: #217dbb;\n  box-shadow: 0 0 0 0 rgba(82, 167, 224, 0.5);\n}\n\n.dark-mode .btn-info.disabled, .dark-mode .btn-info:disabled {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-info:not(:disabled):not(.disabled):active, .dark-mode .btn-info:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-info.dropdown-toggle {\n  color: #fff;\n  background-color: #217dbb;\n  border-color: #1f76b0;\n}\n\n.dark-mode .btn-info:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(82, 167, 224, 0.5);\n}\n\n.dark-mode .btn-warning {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n  box-shadow: none;\n}\n\n.dark-mode .btn-warning:hover {\n  color: #fff;\n  background-color: #d4860b;\n  border-color: #c87f0a;\n}\n\n.dark-mode .btn-warning:focus, .dark-mode .btn-warning.focus {\n  color: #fff;\n  background-color: #d4860b;\n  border-color: #c87f0a;\n  box-shadow: 0 0 0 0 rgba(211, 139, 24, 0.5);\n}\n\n.dark-mode .btn-warning.disabled, .dark-mode .btn-warning:disabled {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-warning:not(:disabled):not(.disabled):active, .dark-mode .btn-warning:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-warning.dropdown-toggle {\n  color: #fff;\n  background-color: #c87f0a;\n  border-color: #bc770a;\n}\n\n.dark-mode .btn-warning:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(211, 139, 24, 0.5);\n}\n\n.dark-mode .btn-danger {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  box-shadow: none;\n}\n\n.dark-mode .btn-danger:hover {\n  color: #fff;\n  background-color: #e12e1c;\n  border-color: #d62c1a;\n}\n\n.dark-mode .btn-danger:focus, .dark-mode .btn-danger.focus {\n  color: #fff;\n  background-color: #e12e1c;\n  border-color: #d62c1a;\n  box-shadow: 0 0 0 0 rgba(235, 103, 89, 0.5);\n}\n\n.dark-mode .btn-danger.disabled, .dark-mode .btn-danger:disabled {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-danger:not(:disabled):not(.disabled):active, .dark-mode .btn-danger:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #d62c1a;\n  border-color: #ca2a19;\n}\n\n.dark-mode .btn-danger:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(235, 103, 89, 0.5);\n}\n\n.dark-mode .btn-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  box-shadow: none;\n}\n\n.dark-mode .btn-light:hover {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n}\n\n.dark-mode .btn-light:focus, .dark-mode .btn-light.focus {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.dark-mode .btn-light.disabled, .dark-mode .btn-light:disabled {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-light:not(:disabled):not(.disabled):active, .dark-mode .btn-light:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n  border-color: #d3d9df;\n}\n\n.dark-mode .btn-light:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.dark-mode .btn-dark {\n  color: #fff;\n  background-color: #292d32;\n  border-color: #4b545c;\n  box-shadow: none;\n}\n\n.dark-mode .btn-dark:hover {\n  color: #fff;\n  background-color: #171a1d;\n  border-color: #343a40;\n}\n\n.dark-mode .btn-dark:focus, .dark-mode .btn-dark.focus {\n  color: #fff;\n  background-color: #171a1d;\n  border-color: #343a40;\n  box-shadow: 0 0 0 0 rgba(102, 109, 117, 0.5);\n}\n\n.dark-mode .btn-dark.disabled, .dark-mode .btn-dark:disabled {\n  color: #fff;\n  background-color: #292d32;\n  border-color: #4b545c;\n}\n\n.dark-mode .btn-dark:not(:disabled):not(.disabled):active, .dark-mode .btn-dark:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #121416;\n  border-color: #2e3439;\n}\n\n.dark-mode .btn-dark:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(102, 109, 117, 0.5);\n}\n\n.dark-mode .btn-outline-primary {\n  color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-outline-primary:hover {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-outline-primary:focus, .dark-mode .btn-outline-primary.focus {\n  box-shadow: 0 0 0 0 rgba(63, 103, 145, 0.5);\n}\n\n.dark-mode .btn-outline-primary.disabled, .dark-mode .btn-outline-primary:disabled {\n  color: #3f6791;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-primary:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(63, 103, 145, 0.5);\n}\n\n.dark-mode .btn-outline-secondary {\n  color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-outline-secondary:hover {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-outline-secondary:focus, .dark-mode .btn-outline-secondary.focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.dark-mode .btn-outline-secondary.disabled, .dark-mode .btn-outline-secondary:disabled {\n  color: #6c757d;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-secondary:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.dark-mode .btn-outline-success {\n  color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-outline-success:hover {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-outline-success:focus, .dark-mode .btn-outline-success.focus {\n  box-shadow: 0 0 0 0 rgba(0, 188, 140, 0.5);\n}\n\n.dark-mode .btn-outline-success.disabled, .dark-mode .btn-outline-success:disabled {\n  color: #00bc8c;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-success:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-success.dropdown-toggle {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .btn-outline-success:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(0, 188, 140, 0.5);\n}\n\n.dark-mode .btn-outline-info {\n  color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-outline-info:hover {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-outline-info:focus, .dark-mode .btn-outline-info.focus {\n  box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.5);\n}\n\n.dark-mode .btn-outline-info.disabled, .dark-mode .btn-outline-info:disabled {\n  color: #3498db;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-info:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-info.dropdown-toggle {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .btn-outline-info:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.5);\n}\n\n.dark-mode .btn-outline-warning {\n  color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-outline-warning:hover {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-outline-warning:focus, .dark-mode .btn-outline-warning.focus {\n  box-shadow: 0 0 0 0 rgba(243, 156, 18, 0.5);\n}\n\n.dark-mode .btn-outline-warning.disabled, .dark-mode .btn-outline-warning:disabled {\n  color: #f39c12;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-warning:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(243, 156, 18, 0.5);\n}\n\n.dark-mode .btn-outline-danger {\n  color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-outline-danger:hover {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-outline-danger:focus, .dark-mode .btn-outline-danger.focus {\n  box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.5);\n}\n\n.dark-mode .btn-outline-danger.disabled, .dark-mode .btn-outline-danger:disabled {\n  color: #e74c3c;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-danger:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.5);\n}\n\n.dark-mode .btn-outline-light {\n  color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-outline-light:hover {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-outline-light:focus, .dark-mode .btn-outline-light.focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.dark-mode .btn-outline-light.disabled, .dark-mode .btn-outline-light:disabled {\n  color: #f8f9fa;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-light:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .btn-outline-light:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.dark-mode .btn-outline-dark {\n  color: #060708;\n  border-color: #060708;\n}\n\n.dark-mode .btn-outline-dark:hover {\n  color: #fff;\n  background-color: #060708;\n  border-color: #060708;\n}\n\n.dark-mode .btn-outline-dark:focus, .dark-mode .btn-outline-dark.focus {\n  box-shadow: 0 0 0 0 rgba(6, 7, 8, 0.5);\n}\n\n.dark-mode .btn-outline-dark.disabled, .dark-mode .btn-outline-dark:disabled {\n  color: #060708;\n  background-color: transparent;\n}\n\n.dark-mode .btn-outline-dark:not(:disabled):not(.disabled):active, .dark-mode .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .dark-mode .btn-outline-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #060708;\n  border-color: #060708;\n}\n\n.dark-mode .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .dark-mode .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .dark-mode .btn-outline-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(6, 7, 8, 0.5);\n}\n\n.callout {\n  border-radius: 0.25rem;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n  background-color: #fff;\n  border-left: 5px solid #e9ecef;\n  margin-bottom: 1rem;\n  padding: 1rem;\n}\n\n.callout a {\n  color: #495057;\n  text-decoration: underline;\n}\n\n.callout a:hover {\n  color: #e9ecef;\n}\n\n.callout p:last-child {\n  margin-bottom: 0;\n}\n\n.callout.callout-danger {\n  border-left-color: #bd2130;\n}\n\n.callout.callout-warning {\n  border-left-color: #d39e00;\n}\n\n.callout.callout-info {\n  border-left-color: #117a8b;\n}\n\n.callout.callout-success {\n  border-left-color: #1e7e34;\n}\n\n.dark-mode .callout {\n  background-color: #3f474e;\n}\n\n.dark-mode .callout.callout-danger {\n  border-left-color: #ed7669;\n}\n\n.dark-mode .callout.callout-warning {\n  border-left-color: #f5b043;\n}\n\n.dark-mode .callout.callout-info {\n  border-left-color: #5faee3;\n}\n\n.dark-mode .callout.callout-success {\n  border-left-color: #00efb2;\n}\n\n.alert .icon {\n  margin-right: 10px;\n}\n\n.alert .close {\n  color: #000;\n  opacity: .2;\n}\n\n.alert .close:hover {\n  opacity: .5;\n}\n\n.alert a {\n  color: #fff;\n  text-decoration: underline;\n}\n\n.alert-primary {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #006fe6;\n}\n\n.alert-default-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.alert-default-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.alert-default-primary .alert-link {\n  color: #002752;\n}\n\n.alert-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #60686f;\n}\n\n.alert-default-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.alert-default-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.alert-default-secondary .alert-link {\n  color: #202326;\n}\n\n.alert-success {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #23923d;\n}\n\n.alert-default-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.alert-default-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.alert-default-success .alert-link {\n  color: #0b2e13;\n}\n\n.alert-info {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #148ea1;\n}\n\n.alert-default-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.alert-default-info hr {\n  border-top-color: #abdde5;\n}\n\n.alert-default-info .alert-link {\n  color: #062c33;\n}\n\n.alert-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #edb100;\n}\n\n.alert-default-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.alert-default-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.alert-default-warning .alert-link {\n  color: #533f03;\n}\n\n.alert-danger {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #d32535;\n}\n\n.alert-default-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.alert-default-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.alert-default-danger .alert-link {\n  color: #491217;\n}\n\n.alert-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n}\n\n.alert-default-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.alert-default-light hr {\n  border-top-color: #ececf6;\n}\n\n.alert-default-light .alert-link {\n  color: #686868;\n}\n\n.alert-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #292d32;\n}\n\n.alert-default-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.alert-default-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.alert-default-dark .alert-link {\n  color: #040505;\n}\n\n.dark-mode .alert-primary {\n  color: #fff;\n  background-color: #3f6791;\n  border-color: #375a7f;\n}\n\n.dark-mode .alert-default-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.dark-mode .alert-default-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.dark-mode .alert-default-primary .alert-link {\n  color: #002752;\n}\n\n.dark-mode .alert-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #60686f;\n}\n\n.dark-mode .alert-default-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.dark-mode .alert-default-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.dark-mode .alert-default-secondary .alert-link {\n  color: #202326;\n}\n\n.dark-mode .alert-success {\n  color: #fff;\n  background-color: #00bc8c;\n  border-color: #00a379;\n}\n\n.dark-mode .alert-default-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.dark-mode .alert-default-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.dark-mode .alert-default-success .alert-link {\n  color: #0b2e13;\n}\n\n.dark-mode .alert-info {\n  color: #fff;\n  background-color: #3498db;\n  border-color: #258cd1;\n}\n\n.dark-mode .alert-default-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.dark-mode .alert-default-info hr {\n  border-top-color: #abdde5;\n}\n\n.dark-mode .alert-default-info .alert-link {\n  color: #062c33;\n}\n\n.dark-mode .alert-warning {\n  color: #1f2d3d;\n  background-color: #f39c12;\n  border-color: #e08e0b;\n}\n\n.dark-mode .alert-default-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.dark-mode .alert-default-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.dark-mode .alert-default-warning .alert-link {\n  color: #533f03;\n}\n\n.dark-mode .alert-danger {\n  color: #fff;\n  background-color: #e74c3c;\n  border-color: #e43725;\n}\n\n.dark-mode .alert-default-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.dark-mode .alert-default-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.dark-mode .alert-default-danger .alert-link {\n  color: #491217;\n}\n\n.dark-mode .alert-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n}\n\n.dark-mode .alert-default-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.dark-mode .alert-default-light hr {\n  border-top-color: #ececf6;\n}\n\n.dark-mode .alert-default-light .alert-link {\n  color: #686868;\n}\n\n.dark-mode .alert-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #292d32;\n}\n\n.dark-mode .alert-default-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.dark-mode .alert-default-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.dark-mode .alert-default-dark .alert-link {\n  color: #040505;\n}\n\n.table:not(.table-dark) {\n  color: inherit;\n}\n\n.table.table-head-fixed thead tr:nth-child(1) th {\n  background-color: #fff;\n  border-bottom: 0;\n  box-shadow: inset 0 1px 0 #dee2e6, inset 0 -1px 0 #dee2e6;\n  position: -webkit-sticky;\n  position: sticky;\n  top: 0;\n  z-index: 10;\n}\n\n.table.table-head-fixed.table-dark thead tr:nth-child(1) th {\n  background-color: #212529;\n  box-shadow: inset 0 1px 0 #383f45, inset 0 -1px 0 #383f45;\n}\n\n.table.no-border,\n.table.no-border td,\n.table.no-border th {\n  border: 0;\n}\n\n.table.text-center,\n.table.text-center td,\n.table.text-center th {\n  text-align: center;\n}\n\n.table.table-valign-middle thead > tr > th,\n.table.table-valign-middle thead > tr > td,\n.table.table-valign-middle tbody > tr > th,\n.table.table-valign-middle tbody > tr > td {\n  vertical-align: middle;\n}\n\n.card-body.p-0 .table thead > tr > th:first-of-type,\n.card-body.p-0 .table thead > tr > td:first-of-type,\n.card-body.p-0 .table tfoot > tr > th:first-of-type,\n.card-body.p-0 .table tfoot > tr > td:first-of-type,\n.card-body.p-0 .table tbody > tr > th:first-of-type,\n.card-body.p-0 .table tbody > tr > td:first-of-type {\n  padding-left: 1.5rem;\n}\n\n.card-body.p-0 .table thead > tr > th:last-of-type,\n.card-body.p-0 .table thead > tr > td:last-of-type,\n.card-body.p-0 .table tfoot > tr > th:last-of-type,\n.card-body.p-0 .table tfoot > tr > td:last-of-type,\n.card-body.p-0 .table tbody > tr > th:last-of-type,\n.card-body.p-0 .table tbody > tr > td:last-of-type {\n  padding-right: 1.5rem;\n}\n\n.table-hover tbody tr.expandable-body:hover {\n  background-color: inherit !important;\n}\n\n[data-widget=\"expandable-table\"] {\n  cursor: pointer;\n}\n\n[data-widget=\"expandable-table\"] i.expandable-table-caret {\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n[data-widget=\"expandable-table\"][aria-expanded=\"true\"] i.expandable-table-caret[class*=\"right\"] {\n  -webkit-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n[data-widget=\"expandable-table\"][aria-expanded=\"true\"] i.expandable-table-caret[class*=\"left\"] {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n[aria-expanded=\"true\"] {\n  cursor: pointer;\n}\n\n[aria-expanded=\"true\"] i.expandable-table-caret {\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n[aria-expanded=\"true\"] [data-widget=\"expandable-table\"] i.expandable-table-caret[class*=\"right\"] {\n  -webkit-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n[aria-expanded=\"true\"] [data-widget=\"expandable-table\"] i.expandable-table-caret[class*=\"left\"] {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n.expandable-body > td {\n  padding: 0 !important;\n  width: 100%;\n}\n\n.expandable-body > td > div,\n.expandable-body > td > p {\n  padding: 0.75rem;\n}\n\n.expandable-body .table {\n  width: calc(100% - 0.75rem);\n  margin: 0 0 0 0.75rem;\n}\n\n.expandable-body .table tr:first-child td,\n.expandable-body .table tr:first-child th {\n  border-top: none;\n}\n\n.dark-mode .table-bordered,\n.dark-mode .table-bordered td,\n.dark-mode .table-bordered th {\n  border-color: #6c757d;\n}\n\n.dark-mode .table-hover tbody tr:hover {\n  color: #dee2e6;\n  background-color: #3a4047;\n  border-color: #6c757d;\n}\n\n.dark-mode .table thead th {\n  border-bottom-color: #6c757d;\n}\n\n.dark-mode .table th,\n.dark-mode .table td {\n  border-top-color: #6c757d;\n}\n\n.dark-mode .table.table-head-fixed thead tr:nth-child(1) th {\n  background-color: #3f474e;\n}\n\n.carousel-control-prev .carousel-control-custom-icon {\n  margin-left: -20px;\n}\n\n.carousel-control-next .carousel-control-custom-icon {\n  margin-right: 20px;\n}\n\n.carousel-control-custom-icon > .fa,\n.carousel-control-custom-icon > .fas,\n.carousel-control-custom-icon > .far,\n.carousel-control-custom-icon > .fab,\n.carousel-control-custom-icon > .fal,\n.carousel-control-custom-icon > .fad,\n.carousel-control-custom-icon > .svg-inline--fa,\n.carousel-control-custom-icon > .ion {\n  display: inline-block;\n  font-size: 40px;\n  margin-top: -20px;\n  position: absolute;\n  top: 50%;\n  z-index: 5;\n}\n\n.close {\n  float: right;\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: .5;\n}\n\n.close:hover {\n  color: #000;\n  text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {\n  opacity: .75;\n}\n\n.close:focus {\n  outline: none;\n}\n\nbutton.close {\n  padding: 0;\n  background-color: transparent;\n  border: 0;\n}\n\na.close.disabled {\n  pointer-events: none;\n}\n/*# sourceMappingURL=adminlte.components.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/css/alt/adminlte.core.css",
    "content": "/*!\n *   AdminLTE v3.2.0\n *     Only Core\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n/*!\n * Bootstrap v4.6.1 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --blue: #007bff;\n  --indigo: #6610f2;\n  --purple: #6f42c1;\n  --pink: #e83e8c;\n  --red: #dc3545;\n  --orange: #fd7e14;\n  --yellow: #ffc107;\n  --green: #28a745;\n  --teal: #20c997;\n  --cyan: #17a2b8;\n  --white: #fff;\n  --gray: #6c757d;\n  --gray-dark: #343a40;\n  --primary: #007bff;\n  --secondary: #6c757d;\n  --success: #28a745;\n  --info: #17a2b8;\n  --warning: #ffc107;\n  --danger: #dc3545;\n  --light: #f8f9fa;\n  --dark: #343a40;\n  --breakpoint-xs: 0;\n  --breakpoint-sm: 576px;\n  --breakpoint-md: 768px;\n  --breakpoint-lg: 992px;\n  --breakpoint-xl: 1200px;\n  --font-family-sans-serif: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  line-height: 1.15;\n  -webkit-text-size-adjust: 100%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n  display: block;\n}\n\nbody {\n  margin: 0;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  text-align: left;\n  background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n  outline: 0 !important;\n}\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n  overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n  text-decoration: underline;\n  -webkit-text-decoration: underline dotted;\n  text-decoration: underline dotted;\n  cursor: help;\n  border-bottom: 0;\n  -webkit-text-decoration-skip-ink: none;\n  text-decoration-skip-ink: none;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: 700;\n}\n\ndd {\n  margin-bottom: .5rem;\n  margin-left: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall {\n  font-size: 80%;\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -.25em;\n}\n\nsup {\n  top: -.5em;\n}\n\na {\n  color: #007bff;\n  text-decoration: none;\n  background-color: transparent;\n}\n\na:hover {\n  color: #0056b3;\n  text-decoration: none;\n}\n\na:not([href]):not([class]) {\n  color: inherit;\n  text-decoration: none;\n}\n\na:not([href]):not([class]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  font-size: 1em;\n}\n\npre {\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n  -ms-overflow-style: scrollbar;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg {\n  vertical-align: middle;\n  border-style: none;\n}\n\nsvg {\n  overflow: hidden;\n  vertical-align: middle;\n}\n\ntable {\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.75rem;\n  padding-bottom: 0.75rem;\n  color: #6c757d;\n  text-align: left;\n  caption-side: bottom;\n}\n\nth {\n  text-align: inherit;\n  text-align: -webkit-match-parent;\n}\n\nlabel {\n  display: inline-block;\n  margin-bottom: 0.5rem;\n}\n\nbutton {\n  border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n  outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\ninput {\n  overflow: visible;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n[role=\"button\"] {\n  cursor: pointer;\n}\n\nselect {\n  word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n  -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n  cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  box-sizing: border-box;\n  padding: 0;\n}\n\ntextarea {\n  overflow: auto;\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  max-width: 100%;\n  padding: 0;\n  margin-bottom: .5rem;\n  font-size: 1.5rem;\n  line-height: inherit;\n  color: inherit;\n  white-space: normal;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n[type=\"search\"] {\n  outline-offset: -2px;\n  -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\nsummary {\n  display: list-item;\n  cursor: pointer;\n}\n\ntemplate {\n  display: none;\n}\n\n[hidden] {\n  display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  margin-bottom: 0.5rem;\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.2;\n  color: inherit;\n}\n\nh1, .h1 {\n  font-size: 2.5rem;\n}\n\nh2, .h2 {\n  font-size: 2rem;\n}\n\nh3, .h3 {\n  font-size: 1.75rem;\n}\n\nh4, .h4 {\n  font-size: 1.5rem;\n}\n\nh5, .h5 {\n  font-size: 1.25rem;\n}\n\nh6, .h6 {\n  font-size: 1rem;\n}\n\n.lead {\n  font-size: 1.25rem;\n  font-weight: 300;\n}\n\n.display-1 {\n  font-size: 6rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-2 {\n  font-size: 5.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-3 {\n  font-size: 4.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-4 {\n  font-size: 3.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\nhr {\n  margin-top: 1rem;\n  margin-bottom: 1rem;\n  border: 0;\n  border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n  font-size: 80%;\n  font-weight: 400;\n}\n\nmark,\n.mark {\n  padding: 0.2em;\n  background-color: #fcf8e3;\n}\n\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline-item {\n  display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n  margin-right: 0.5rem;\n}\n\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\n.blockquote {\n  margin-bottom: 1rem;\n  font-size: 1.25rem;\n}\n\n.blockquote-footer {\n  display: block;\n  font-size: 80%;\n  color: #6c757d;\n}\n\n.blockquote-footer::before {\n  content: \"\\2014\\00A0\";\n}\n\n.img-fluid {\n  max-width: 100%;\n  height: auto;\n}\n\n.img-thumbnail {\n  padding: 0.25rem;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n  border-radius: 0.25rem;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  max-width: 100%;\n  height: auto;\n}\n\n.figure {\n  display: inline-block;\n}\n\n.figure-img {\n  margin-bottom: 0.5rem;\n  line-height: 1;\n}\n\n.figure-caption {\n  font-size: 90%;\n  color: #6c757d;\n}\n\ncode {\n  font-size: 87.5%;\n  color: #e83e8c;\n  word-wrap: break-word;\n}\n\na > code {\n  color: inherit;\n}\n\nkbd {\n  padding: 0.2rem 0.4rem;\n  font-size: 87.5%;\n  color: #fff;\n  background-color: #212529;\n  border-radius: 0.2rem;\n  box-shadow: inset 0 -0.1rem 0 rgba(0, 0, 0, 0.25);\n}\n\nkbd kbd {\n  padding: 0;\n  font-size: 100%;\n  font-weight: 700;\n  box-shadow: none;\n}\n\npre {\n  display: block;\n  font-size: 87.5%;\n  color: #212529;\n}\n\npre code {\n  font-size: inherit;\n  color: inherit;\n  word-break: normal;\n}\n\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n\n.container,\n.container-fluid,\n.container-sm,\n.container-md,\n.container-lg,\n.container-xl {\n  width: 100%;\n  padding-right: 7.5px;\n  padding-left: 7.5px;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n@media (min-width: 576px) {\n  .container, .container-sm {\n    max-width: 540px;\n  }\n}\n\n@media (min-width: 768px) {\n  .container, .container-sm, .container-md {\n    max-width: 720px;\n  }\n}\n\n@media (min-width: 992px) {\n  .container, .container-sm, .container-md, .container-lg {\n    max-width: 960px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .container, .container-sm, .container-md, .container-lg, .container-xl {\n    max-width: 1140px;\n  }\n}\n\n.row {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  margin-right: -7.5px;\n  margin-left: -7.5px;\n}\n\n.no-gutters {\n  margin-right: 0;\n  margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .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-sm,\n.col-sm-auto, .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-md,\n.col-md-auto, .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-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n  position: relative;\n  width: 100%;\n  padding-right: 7.5px;\n  padding-left: 7.5px;\n}\n\n.col {\n  -ms-flex-preferred-size: 0;\n  flex-basis: 0;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  max-width: 100%;\n}\n\n.row-cols-1 > * {\n  -ms-flex: 0 0 100%;\n  flex: 0 0 100%;\n  max-width: 100%;\n}\n\n.row-cols-2 > * {\n  -ms-flex: 0 0 50%;\n  flex: 0 0 50%;\n  max-width: 50%;\n}\n\n.row-cols-3 > * {\n  -ms-flex: 0 0 33.333333%;\n  flex: 0 0 33.333333%;\n  max-width: 33.333333%;\n}\n\n.row-cols-4 > * {\n  -ms-flex: 0 0 25%;\n  flex: 0 0 25%;\n  max-width: 25%;\n}\n\n.row-cols-5 > * {\n  -ms-flex: 0 0 20%;\n  flex: 0 0 20%;\n  max-width: 20%;\n}\n\n.row-cols-6 > * {\n  -ms-flex: 0 0 16.666667%;\n  flex: 0 0 16.666667%;\n  max-width: 16.666667%;\n}\n\n.col-auto {\n  -ms-flex: 0 0 auto;\n  flex: 0 0 auto;\n  width: auto;\n  max-width: 100%;\n}\n\n.col-1 {\n  -ms-flex: 0 0 8.333333%;\n  flex: 0 0 8.333333%;\n  max-width: 8.333333%;\n}\n\n.col-2 {\n  -ms-flex: 0 0 16.666667%;\n  flex: 0 0 16.666667%;\n  max-width: 16.666667%;\n}\n\n.col-3 {\n  -ms-flex: 0 0 25%;\n  flex: 0 0 25%;\n  max-width: 25%;\n}\n\n.col-4 {\n  -ms-flex: 0 0 33.333333%;\n  flex: 0 0 33.333333%;\n  max-width: 33.333333%;\n}\n\n.col-5 {\n  -ms-flex: 0 0 41.666667%;\n  flex: 0 0 41.666667%;\n  max-width: 41.666667%;\n}\n\n.col-6 {\n  -ms-flex: 0 0 50%;\n  flex: 0 0 50%;\n  max-width: 50%;\n}\n\n.col-7 {\n  -ms-flex: 0 0 58.333333%;\n  flex: 0 0 58.333333%;\n  max-width: 58.333333%;\n}\n\n.col-8 {\n  -ms-flex: 0 0 66.666667%;\n  flex: 0 0 66.666667%;\n  max-width: 66.666667%;\n}\n\n.col-9 {\n  -ms-flex: 0 0 75%;\n  flex: 0 0 75%;\n  max-width: 75%;\n}\n\n.col-10 {\n  -ms-flex: 0 0 83.333333%;\n  flex: 0 0 83.333333%;\n  max-width: 83.333333%;\n}\n\n.col-11 {\n  -ms-flex: 0 0 91.666667%;\n  flex: 0 0 91.666667%;\n  max-width: 91.666667%;\n}\n\n.col-12 {\n  -ms-flex: 0 0 100%;\n  flex: 0 0 100%;\n  max-width: 100%;\n}\n\n.order-first {\n  -ms-flex-order: -1;\n  order: -1;\n}\n\n.order-last {\n  -ms-flex-order: 13;\n  order: 13;\n}\n\n.order-0 {\n  -ms-flex-order: 0;\n  order: 0;\n}\n\n.order-1 {\n  -ms-flex-order: 1;\n  order: 1;\n}\n\n.order-2 {\n  -ms-flex-order: 2;\n  order: 2;\n}\n\n.order-3 {\n  -ms-flex-order: 3;\n  order: 3;\n}\n\n.order-4 {\n  -ms-flex-order: 4;\n  order: 4;\n}\n\n.order-5 {\n  -ms-flex-order: 5;\n  order: 5;\n}\n\n.order-6 {\n  -ms-flex-order: 6;\n  order: 6;\n}\n\n.order-7 {\n  -ms-flex-order: 7;\n  order: 7;\n}\n\n.order-8 {\n  -ms-flex-order: 8;\n  order: 8;\n}\n\n.order-9 {\n  -ms-flex-order: 9;\n  order: 9;\n}\n\n.order-10 {\n  -ms-flex-order: 10;\n  order: 10;\n}\n\n.order-11 {\n  -ms-flex-order: 11;\n  order: 11;\n}\n\n.order-12 {\n  -ms-flex-order: 12;\n  order: 12;\n}\n\n.offset-1 {\n  margin-left: 8.333333%;\n}\n\n.offset-2 {\n  margin-left: 16.666667%;\n}\n\n.offset-3 {\n  margin-left: 25%;\n}\n\n.offset-4 {\n  margin-left: 33.333333%;\n}\n\n.offset-5 {\n  margin-left: 41.666667%;\n}\n\n.offset-6 {\n  margin-left: 50%;\n}\n\n.offset-7 {\n  margin-left: 58.333333%;\n}\n\n.offset-8 {\n  margin-left: 66.666667%;\n}\n\n.offset-9 {\n  margin-left: 75%;\n}\n\n.offset-10 {\n  margin-left: 83.333333%;\n}\n\n.offset-11 {\n  margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n  .col-sm {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-sm-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-sm-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-sm-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-sm-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-sm-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-sm-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-sm-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-sm-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-sm-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-sm-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-sm-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-sm-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-sm-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-sm-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-sm-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-sm-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-sm-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-sm-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-sm-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-sm-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-sm-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-sm-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-sm-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-sm-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-sm-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-sm-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-sm-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-sm-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-sm-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-sm-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-sm-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-sm-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-sm-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-sm-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-sm-0 {\n    margin-left: 0;\n  }\n  .offset-sm-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-sm-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-sm-3 {\n    margin-left: 25%;\n  }\n  .offset-sm-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-sm-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-sm-6 {\n    margin-left: 50%;\n  }\n  .offset-sm-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-sm-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-sm-9 {\n    margin-left: 75%;\n  }\n  .offset-sm-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-sm-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 768px) {\n  .col-md {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-md-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-md-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-md-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-md-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-md-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-md-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-md-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-md-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-md-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-md-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-md-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-md-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-md-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-md-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-md-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-md-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-md-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-md-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-md-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-md-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-md-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-md-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-md-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-md-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-md-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-md-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-md-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-md-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-md-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-md-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-md-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-md-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-md-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-md-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-md-0 {\n    margin-left: 0;\n  }\n  .offset-md-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-md-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-md-3 {\n    margin-left: 25%;\n  }\n  .offset-md-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-md-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-md-6 {\n    margin-left: 50%;\n  }\n  .offset-md-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-md-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-md-9 {\n    margin-left: 75%;\n  }\n  .offset-md-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-md-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 992px) {\n  .col-lg {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-lg-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-lg-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-lg-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-lg-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-lg-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-lg-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-lg-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-lg-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-lg-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-lg-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-lg-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-lg-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-lg-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-lg-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-lg-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-lg-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-lg-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-lg-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-lg-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-lg-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-lg-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-lg-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-lg-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-lg-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-lg-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-lg-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-lg-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-lg-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-lg-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-lg-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-lg-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-lg-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-lg-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-lg-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-lg-0 {\n    margin-left: 0;\n  }\n  .offset-lg-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-lg-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-lg-3 {\n    margin-left: 25%;\n  }\n  .offset-lg-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-lg-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-lg-6 {\n    margin-left: 50%;\n  }\n  .offset-lg-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-lg-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-lg-9 {\n    margin-left: 75%;\n  }\n  .offset-lg-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-lg-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-xl-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-xl-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-xl-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-xl-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-xl-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-xl-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-xl-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-xl-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-xl-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-xl-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-xl-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-xl-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-xl-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-xl-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-xl-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-xl-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-xl-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-xl-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-xl-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-xl-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-xl-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-xl-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-xl-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-xl-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-xl-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-xl-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-xl-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-xl-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-xl-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-xl-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-xl-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-xl-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-xl-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-xl-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-xl-0 {\n    margin-left: 0;\n  }\n  .offset-xl-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-xl-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-xl-3 {\n    margin-left: 25%;\n  }\n  .offset-xl-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-xl-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-xl-6 {\n    margin-left: 50%;\n  }\n  .offset-xl-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-xl-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-xl-9 {\n    margin-left: 75%;\n  }\n  .offset-xl-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-xl-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n.table {\n  width: 100%;\n  margin-bottom: 1rem;\n  color: #212529;\n  background-color: transparent;\n}\n\n.table th,\n.table td {\n  padding: 0.75rem;\n  vertical-align: top;\n  border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n  border-top: 2px solid #dee2e6;\n}\n\n.table-sm th,\n.table-sm td {\n  padding: 0.3rem;\n}\n\n.table-bordered {\n  border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n  border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n  border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n  border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n  color: #212529;\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n  background-color: #b8daff;\n}\n\n.table-primary th,\n.table-primary td,\n.table-primary thead th,\n.table-primary tbody + tbody {\n  border-color: #7abaff;\n}\n\n.table-hover .table-primary:hover {\n  background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n  background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n  background-color: #d6d8db;\n}\n\n.table-secondary th,\n.table-secondary td,\n.table-secondary thead th,\n.table-secondary tbody + tbody {\n  border-color: #b3b7bb;\n}\n\n.table-hover .table-secondary:hover {\n  background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n  background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n  background-color: #c3e6cb;\n}\n\n.table-success th,\n.table-success td,\n.table-success thead th,\n.table-success tbody + tbody {\n  border-color: #8fd19e;\n}\n\n.table-hover .table-success:hover {\n  background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n  background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n  background-color: #bee5eb;\n}\n\n.table-info th,\n.table-info td,\n.table-info thead th,\n.table-info tbody + tbody {\n  border-color: #86cfda;\n}\n\n.table-hover .table-info:hover {\n  background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n  background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n  background-color: #ffeeba;\n}\n\n.table-warning th,\n.table-warning td,\n.table-warning thead th,\n.table-warning tbody + tbody {\n  border-color: #ffdf7e;\n}\n\n.table-hover .table-warning:hover {\n  background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n  background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n  background-color: #f5c6cb;\n}\n\n.table-danger th,\n.table-danger td,\n.table-danger thead th,\n.table-danger tbody + tbody {\n  border-color: #ed969e;\n}\n\n.table-hover .table-danger:hover {\n  background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n  background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n  background-color: #fdfdfe;\n}\n\n.table-light th,\n.table-light td,\n.table-light thead th,\n.table-light tbody + tbody {\n  border-color: #fbfcfc;\n}\n\n.table-hover .table-light:hover {\n  background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n  background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n  background-color: #c6c8ca;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th,\n.table-dark tbody + tbody {\n  border-color: #95999c;\n}\n\n.table-hover .table-dark:hover {\n  background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n  background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n  color: #fff;\n  background-color: #212529;\n  border-color: #383f45;\n}\n\n.table .thead-light th {\n  color: #495057;\n  background-color: #e9ecef;\n  border-color: #dee2e6;\n}\n\n.table-dark {\n  color: #fff;\n  background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n  border-color: #383f45;\n}\n\n.table-dark.table-bordered {\n  border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n  color: #fff;\n  background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n  .table-responsive-sm {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-sm > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .table-responsive-md {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-md > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .table-responsive-lg {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-lg > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .table-responsive-xl {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-xl > .table-bordered {\n    border: 0;\n  }\n}\n\n.table-responsive {\n  display: block;\n  width: 100%;\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n}\n\n.table-responsive > .table-bordered {\n  border: 0;\n}\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0);\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .form-control {\n    transition: none;\n  }\n}\n\n.form-control::-ms-expand {\n  background-color: transparent;\n  border: 0;\n}\n\n.form-control:focus {\n  color: #495057;\n  background-color: #fff;\n  border-color: #80bdff;\n  outline: 0;\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0);\n}\n\n.form-control::-webkit-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::-moz-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::-ms-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n  background-color: #e9ecef;\n  opacity: 1;\n}\n\ninput[type=\"date\"].form-control,\ninput[type=\"time\"].form-control,\ninput[type=\"datetime-local\"].form-control,\ninput[type=\"month\"].form-control {\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\nselect.form-control:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #495057;\n}\n\nselect.form-control:focus::-ms-value {\n  color: #495057;\n  background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n  display: block;\n  width: 100%;\n}\n\n.col-form-label {\n  padding-top: calc(0.375rem + 1px);\n  padding-bottom: calc(0.375rem + 1px);\n  margin-bottom: 0;\n  font-size: inherit;\n  line-height: 1.5;\n}\n\n.col-form-label-lg {\n  padding-top: calc(0.5rem + 1px);\n  padding-bottom: calc(0.5rem + 1px);\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.col-form-label-sm {\n  padding-top: calc(0.25rem + 1px);\n  padding-bottom: calc(0.25rem + 1px);\n  font-size: 0.875rem;\n  line-height: 1.5;\n}\n\n.form-control-plaintext {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0;\n  margin-bottom: 0;\n  font-size: 1rem;\n  line-height: 1.5;\n  color: #212529;\n  background-color: transparent;\n  border: solid transparent;\n  border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.form-control-sm {\n  height: calc(1.8125rem + 2px);\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.form-control-lg {\n  height: calc(2.875rem + 2px);\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\nselect.form-control[size], select.form-control[multiple] {\n  height: auto;\n}\n\ntextarea.form-control {\n  height: auto;\n}\n\n.form-group {\n  margin-bottom: 1rem;\n}\n\n.form-text {\n  display: block;\n  margin-top: 0.25rem;\n}\n\n.form-row {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  margin-right: -5px;\n  margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n  padding-right: 5px;\n  padding-left: 5px;\n}\n\n.form-check {\n  position: relative;\n  display: block;\n  padding-left: 1.25rem;\n}\n\n.form-check-input {\n  position: absolute;\n  margin-top: 0.3rem;\n  margin-left: -1.25rem;\n}\n\n.form-check-input[disabled] ~ .form-check-label,\n.form-check-input:disabled ~ .form-check-label {\n  color: #6c757d;\n}\n\n.form-check-label {\n  margin-bottom: 0;\n}\n\n.form-check-inline {\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding-left: 0;\n  margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n  position: static;\n  margin-top: 0;\n  margin-right: 0.3125rem;\n  margin-left: 0;\n}\n\n.valid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 80%;\n  color: #28a745;\n}\n\n.valid-tooltip {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: .1rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  color: #fff;\n  background-color: rgba(40, 167, 69, 0.9);\n  border-radius: 0.25rem;\n}\n\n.form-row > .col > .valid-tooltip,\n.form-row > [class*=\"col-\"] > .valid-tooltip {\n  left: 5px;\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n  border-color: #28a745;\n  padding-right: 2.25rem !important;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated select.form-control:valid, select.form-control.is-valid {\n  padding-right: 3rem !important;\n  background-position: right 1.5rem center;\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:valid, .custom-select.is-valid {\n  border-color: #28a745;\n  padding-right: calc(0.75em + 2.3125rem) !important;\n  background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat, #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat;\n}\n\n.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n  color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n  color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n  border-color: #28a745;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n  border-color: #34ce57;\n  background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n  border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 80%;\n  color: #dc3545;\n}\n\n.invalid-tooltip {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: .1rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  color: #fff;\n  background-color: rgba(220, 53, 69, 0.9);\n  border-radius: 0.25rem;\n}\n\n.form-row > .col > .invalid-tooltip,\n.form-row > [class*=\"col-\"] > .invalid-tooltip {\n  left: 5px;\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n  border-color: #dc3545;\n  padding-right: 2.25rem !important;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated select.form-control:invalid, select.form-control.is-invalid {\n  padding-right: 3rem !important;\n  background-position: right 1.5rem center;\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:invalid, .custom-select.is-invalid {\n  border-color: #dc3545;\n  padding-right: calc(0.75em + 2.3125rem) !important;\n  background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat, #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat;\n}\n\n.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n  color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n  color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n  border-color: #e4606d;\n  background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-flow: row wrap;\n  flex-flow: row wrap;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.form-inline .form-check {\n  width: 100%;\n}\n\n@media (min-width: 576px) {\n  .form-inline label {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n    margin-bottom: 0;\n  }\n  .form-inline .form-group {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n    -ms-flex-align: center;\n    align-items: center;\n    margin-bottom: 0;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .form-inline .form-control-plaintext {\n    display: inline-block;\n  }\n  .form-inline .input-group,\n  .form-inline .custom-select {\n    width: auto;\n  }\n  .form-inline .form-check {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n    width: auto;\n    padding-left: 0;\n  }\n  .form-inline .form-check-input {\n    position: relative;\n    -ms-flex-negative: 0;\n    flex-shrink: 0;\n    margin-top: 0;\n    margin-right: 0.25rem;\n    margin-left: 0;\n  }\n  .form-inline .custom-control {\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n  }\n  .form-inline .custom-control-label {\n    margin-bottom: 0;\n  }\n}\n\n.btn {\n  display: inline-block;\n  font-weight: 400;\n  color: #212529;\n  text-align: center;\n  vertical-align: middle;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  background-color: transparent;\n  border: 1px solid transparent;\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  line-height: 1.5;\n  border-radius: 0.25rem;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .btn {\n    transition: none;\n  }\n}\n\n.btn:hover {\n  color: #212529;\n  text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n  outline: 0;\n  box-shadow: none;\n}\n\n.btn.disabled, .btn:disabled {\n  opacity: 0.65;\n  box-shadow: none;\n}\n\n.btn:not(:disabled):not(.disabled) {\n  cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n  box-shadow: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n  pointer-events: none;\n}\n\n.btn-primary {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n  box-shadow: none;\n}\n\n.btn-primary:hover {\n  color: #fff;\n  background-color: #0069d9;\n  border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n  color: #fff;\n  background-color: #0069d9;\n  border-color: #0062cc;\n  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #0062cc;\n  border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);\n}\n\n.btn-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n  box-shadow: none;\n}\n\n.btn-secondary:hover {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #545b62;\n  border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.btn-success {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n  box-shadow: none;\n}\n\n.btn-success:hover {\n  color: #fff;\n  background-color: #218838;\n  border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n  color: #fff;\n  background-color: #218838;\n  border-color: #1e7e34;\n  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n  color: #fff;\n  background-color: #1e7e34;\n  border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);\n}\n\n.btn-info {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  box-shadow: none;\n}\n\n.btn-info:hover {\n  color: #fff;\n  background-color: #138496;\n  border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n  color: #fff;\n  background-color: #138496;\n  border-color: #117a8b;\n  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n  color: #fff;\n  background-color: #117a8b;\n  border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);\n}\n\n.btn-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n  box-shadow: none;\n}\n\n.btn-warning:hover {\n  color: #1f2d3d;\n  background-color: #e0a800;\n  border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n  color: #1f2d3d;\n  background-color: #e0a800;\n  border-color: #d39e00;\n  box-shadow: 0 0 0 0 rgba(221, 171, 15, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #d39e00;\n  border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(221, 171, 15, 0.5);\n}\n\n.btn-danger {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n  box-shadow: none;\n}\n\n.btn-danger:hover {\n  color: #fff;\n  background-color: #c82333;\n  border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n  color: #fff;\n  background-color: #c82333;\n  border-color: #bd2130;\n  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #bd2130;\n  border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);\n}\n\n.btn-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  box-shadow: none;\n}\n\n.btn-light:hover {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n  border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.btn-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n  box-shadow: none;\n}\n\n.btn-dark:hover {\n  color: #fff;\n  background-color: #23272b;\n  border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n  color: #fff;\n  background-color: #23272b;\n  border-color: #1d2124;\n  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #1d2124;\n  border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);\n}\n\n.btn-outline-primary {\n  color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n  color: #007bff;\n  background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n  color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n  color: #6c757d;\n  background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n  color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n  color: #28a745;\n  background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n  color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n  color: #17a2b8;\n  background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n  color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n  color: #ffc107;\n  background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n  color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n  color: #dc3545;\n  background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n  color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n  color: #f8f9fa;\n  background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n  color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n  color: #343a40;\n  background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n  font-weight: 400;\n  color: #007bff;\n  text-decoration: none;\n}\n\n.btn-link:hover {\n  color: #0056b3;\n  text-decoration: none;\n}\n\n.btn-link:focus, .btn-link.focus {\n  text-decoration: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n  color: #6c757d;\n  pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.btn-block {\n  display: block;\n  width: 100%;\n}\n\n.btn-block + .btn-block {\n  margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n\n.fade {\n  transition: opacity 0.15s linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .fade {\n    transition: none;\n  }\n}\n\n.fade:not(.show) {\n  opacity: 0;\n}\n\n.collapse:not(.show) {\n  display: none;\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  transition: height 0.35s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .collapsing {\n    transition: none;\n  }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n  position: relative;\n}\n\n.dropdown-toggle {\n  white-space: nowrap;\n}\n\n.dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0;\n  border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 10rem;\n  padding: 0.5rem 0;\n  margin: 0.125rem 0 0;\n  font-size: 1rem;\n  color: #212529;\n  text-align: left;\n  list-style: none;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 0.25rem;\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.175);\n}\n\n.dropdown-menu-left {\n  right: auto;\n  left: 0;\n}\n\n.dropdown-menu-right {\n  right: 0;\n  left: auto;\n}\n\n@media (min-width: 576px) {\n  .dropdown-menu-sm-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-sm-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 768px) {\n  .dropdown-menu-md-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-md-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 992px) {\n  .dropdown-menu-lg-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-lg-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 1200px) {\n  .dropdown-menu-xl-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-xl-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n.dropup .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-top: 0;\n  margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0.3em solid;\n  border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n  top: 0;\n  right: auto;\n  left: 100%;\n  margin-top: 0;\n  margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0;\n  border-bottom: 0.3em solid transparent;\n  border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n  vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n  top: 0;\n  right: 100%;\n  left: auto;\n  margin-top: 0;\n  margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n  display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0.3em solid;\n  border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n  vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n  right: auto;\n  bottom: auto;\n}\n\n.dropdown-divider {\n  height: 0;\n  margin: 0.5rem 0;\n  overflow: hidden;\n  border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n  display: block;\n  width: 100%;\n  padding: 0.25rem 1rem;\n  clear: both;\n  font-weight: 400;\n  color: #212529;\n  text-align: inherit;\n  white-space: nowrap;\n  background-color: transparent;\n  border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n  color: #16181b;\n  text-decoration: none;\n  background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n  color: #fff;\n  text-decoration: none;\n  background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n  color: #6c757d;\n  pointer-events: none;\n  background-color: transparent;\n}\n\n.dropdown-menu.show {\n  display: block;\n}\n\n.dropdown-header {\n  display: block;\n  padding: 0.5rem 1rem;\n  margin-bottom: 0;\n  font-size: 0.875rem;\n  color: #6c757d;\n  white-space: nowrap;\n}\n\n.dropdown-item-text {\n  display: block;\n  padding: 0.25rem 1rem;\n  color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n  z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n  z-index: 1;\n}\n\n.btn-toolbar {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-pack: start;\n  justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n  width: auto;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) {\n  margin-left: -1px;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n  padding-right: 0.5625rem;\n  padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n  margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n  margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n  padding-right: 0.375rem;\n  padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n  padding-right: 0.75rem;\n  padding-left: 0.75rem;\n}\n\n.btn-group.show .dropdown-toggle {\n  box-shadow: none;\n}\n\n.btn-group.show .dropdown-toggle.btn-link {\n  box-shadow: none;\n}\n\n.btn-group-vertical {\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-align: start;\n  align-items: flex-start;\n  -ms-flex-pack: center;\n  justify-content: center;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n  width: 100%;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n  margin-top: -1px;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n  margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n\n.input-group {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: stretch;\n  align-items: stretch;\n  width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .form-control-plaintext,\n.input-group > .custom-select,\n.input-group > .custom-file {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  width: 1%;\n  min-width: 0;\n  margin-bottom: 0;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .form-control-plaintext + .form-control,\n.input-group > .form-control-plaintext + .custom-select,\n.input-group > .form-control-plaintext + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n  margin-left: -1px;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {\n  z-index: 3;\n}\n\n.input-group > .custom-file .custom-file-input:focus {\n  z-index: 4;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.input-group:not(.has-validation) > .form-control:not(:last-child),\n.input-group:not(.has-validation) > .custom-select:not(:last-child),\n.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label,\n.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group.has-validation > .form-control:nth-last-child(n + 3),\n.input-group.has-validation > .custom-select:nth-last-child(n + 3),\n.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label,\n.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n  position: relative;\n  z-index: 2;\n}\n\n.input-group-prepend .btn:focus,\n.input-group-append .btn:focus {\n  z-index: 3;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n  margin-left: -1px;\n}\n\n.input-group-prepend {\n  margin-right: -1px;\n}\n\n.input-group-append {\n  margin-left: -1px;\n}\n\n.input-group-text {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding: 0.375rem 0.75rem;\n  margin-bottom: 0;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #e9ecef;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n\n.input-group-lg > .form-control:not(textarea),\n.input-group-lg > .custom-select {\n  height: calc(2.875rem + 2px);\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .custom-select,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\n.input-group-sm > .form-control:not(textarea),\n.input-group-sm > .custom-select {\n  height: calc(1.8125rem + 2px);\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .custom-select,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.input-group-lg > .custom-select,\n.input-group-sm > .custom-select {\n  padding-right: 1.75rem;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .btn,\n.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .input-group-text,\n.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .btn,\n.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.custom-control {\n  position: relative;\n  z-index: 1;\n  display: block;\n  min-height: 1.5rem;\n  padding-left: 1.5rem;\n  -webkit-print-color-adjust: exact;\n  color-adjust: exact;\n}\n\n.custom-control-inline {\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  margin-right: 1rem;\n}\n\n.custom-control-input {\n  position: absolute;\n  left: 0;\n  z-index: -1;\n  width: 1rem;\n  height: 1.25rem;\n  opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n  color: #fff;\n  border-color: #007bff;\n  background-color: #007bff;\n  box-shadow: none;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input:not(:disabled):active ~ .custom-control-label::before {\n  color: #fff;\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n  box-shadow: none;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label {\n  color: #6c757d;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before {\n  background-color: #e9ecef;\n}\n\n.custom-control-label {\n  position: relative;\n  margin-bottom: 0;\n  vertical-align: top;\n}\n\n.custom-control-label::before {\n  position: absolute;\n  top: 0.25rem;\n  left: -1.5rem;\n  display: block;\n  width: 1rem;\n  height: 1rem;\n  pointer-events: none;\n  content: \"\";\n  background-color: #dee2e6;\n  border: #adb5bd solid 1px;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-control-label::after {\n  position: absolute;\n  top: 0.25rem;\n  left: -1.5rem;\n  display: block;\n  width: 1rem;\n  height: 1rem;\n  content: \"\";\n  background: 50% / 50% 50% no-repeat;\n}\n\n.custom-checkbox .custom-control-label::before {\n  border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n  box-shadow: none;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n  border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-switch {\n  padding-left: 2.25rem;\n}\n\n.custom-switch .custom-control-label::before {\n  left: -2.25rem;\n  width: 1.75rem;\n  pointer-events: all;\n  border-radius: 0.5rem;\n}\n\n.custom-switch .custom-control-label::after {\n  top: calc(0.25rem + 2px);\n  left: calc(-2.25rem + 2px);\n  width: calc(1rem - 4px);\n  height: calc(1rem - 4px);\n  background-color: #adb5bd;\n  border-radius: 0.5rem;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n  transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-switch .custom-control-label::after {\n    transition: none;\n  }\n}\n\n.custom-switch .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #dee2e6;\n  -webkit-transform: translateX(0.75rem);\n  transform: translateX(0.75rem);\n}\n\n.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n  display: inline-block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  vertical-align: middle;\n  background: #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n.custom-select:focus {\n  border-color: #80bdff;\n  outline: 0;\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n\n.custom-select:focus::-ms-value {\n  color: #495057;\n  background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n  height: auto;\n  padding-right: 0.75rem;\n  background-image: none;\n}\n\n.custom-select:disabled {\n  color: #6c757d;\n  background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n  display: none;\n}\n\n.custom-select:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #495057;\n}\n\n.custom-select-sm {\n  height: calc(1.8125rem + 2px);\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n  padding-left: 0.5rem;\n  font-size: 75%;\n}\n\n.custom-select-lg {\n  height: calc(2.875rem + 2px);\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  padding-left: 1rem;\n  font-size: 125%;\n}\n\n.custom-file {\n  position: relative;\n  display: inline-block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  margin-bottom: 0;\n}\n\n.custom-file-input {\n  position: relative;\n  z-index: 2;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  margin: 0;\n  overflow: hidden;\n  opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n  box-shadow: none;\n}\n\n.custom-file-input[disabled] ~ .custom-file-label,\n.custom-file-input:disabled ~ .custom-file-label {\n  background-color: #e9ecef;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n  content: \"Browse\";\n}\n\n.custom-file-input ~ .custom-file-label[data-browse]::after {\n  content: attr(data-browse);\n}\n\n.custom-file-label {\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 0.75rem;\n  overflow: hidden;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  background-color: #fff;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: none;\n}\n\n.custom-file-label::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 3;\n  display: block;\n  height: 2.25rem;\n  padding: 0.375rem 0.75rem;\n  line-height: 1.5;\n  color: #495057;\n  content: \"Browse\";\n  background-color: #e9ecef;\n  border-left: inherit;\n  border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n  width: 100%;\n  height: 1rem;\n  padding: 0;\n  background-color: transparent;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n.custom-range:focus {\n  outline: 0;\n}\n\n.custom-range:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-focus-outer {\n  border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: -0.25rem;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -webkit-appearance: none;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-webkit-slider-thumb {\n    -webkit-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-moz-range-thumb {\n  width: 1rem;\n  height: 1rem;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-moz-range-thumb {\n    -moz-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-ms-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: 0;\n  margin-right: 0;\n  margin-left: 0;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-ms-thumb {\n    -ms-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: transparent;\n  border-color: transparent;\n  border-width: 0.5rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-ms-fill-lower {\n  background-color: #dee2e6;\n  border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n  margin-right: 15px;\n  background-color: #dee2e6;\n  border-radius: 1rem;\n}\n\n.custom-range:disabled::-webkit-slider-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-range:disabled::-webkit-slider-runnable-track {\n  cursor: default;\n}\n\n.custom-range:disabled::-moz-range-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-range:disabled::-moz-range-track {\n  cursor: default;\n}\n\n.custom-range:disabled::-ms-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-control-label::before,\n.custom-file-label,\n.custom-select {\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-control-label::before,\n  .custom-file-label,\n  .custom-select {\n    transition: none;\n  }\n}\n\n.nav {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.nav-link {\n  display: block;\n  padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n  text-decoration: none;\n}\n\n.nav-link.disabled {\n  color: #6c757d;\n  pointer-events: none;\n  cursor: default;\n}\n\n.nav-tabs {\n  border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-link {\n  margin-bottom: -1px;\n  border: 1px solid transparent;\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n  border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n  color: #6c757d;\n  background-color: transparent;\n  border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n  color: #495057;\n  background-color: #fff;\n  border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n  border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n  color: #fff;\n  background-color: #007bff;\n}\n\n.nav-fill > .nav-link,\n.nav-fill .nav-item {\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  text-align: center;\n}\n\n.nav-justified > .nav-link,\n.nav-justified .nav-item {\n  -ms-flex-preferred-size: 0;\n  flex-basis: 0;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  text-align: center;\n}\n\n.tab-content > .tab-pane {\n  display: none;\n}\n\n.tab-content > .active {\n  display: block;\n}\n\n.navbar {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n  padding: 0.5rem 0.5rem;\n}\n\n.navbar .container,\n.navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n}\n\n.navbar-brand {\n  display: inline-block;\n  padding-top: 0.3125rem;\n  padding-bottom: 0.3125rem;\n  margin-right: 0.5rem;\n  font-size: 1.25rem;\n  line-height: inherit;\n  white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n  text-decoration: none;\n}\n\n.navbar-nav {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.navbar-nav .nav-link {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n  position: static;\n  float: none;\n}\n\n.navbar-text {\n  display: inline-block;\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n  -ms-flex-preferred-size: 100%;\n  flex-basis: 100%;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.navbar-toggler {\n  padding: 0.25rem 0.75rem;\n  font-size: 1.25rem;\n  line-height: 1;\n  background-color: transparent;\n  border: 1px solid transparent;\n  border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n  text-decoration: none;\n}\n\n.navbar-toggler-icon {\n  display: inline-block;\n  width: 1.5em;\n  height: 1.5em;\n  vertical-align: middle;\n  content: \"\";\n  background: 50% / 100% 100% no-repeat;\n}\n\n.navbar-nav-scroll {\n  max-height: 75vh;\n  overflow-y: auto;\n}\n\n@media (max-width: 575.98px) {\n  .navbar-expand-sm > .container,\n  .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 576px) {\n  .navbar-expand-sm {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-sm .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-sm .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-sm .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-sm > .container,\n  .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-sm .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-sm .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-sm .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .navbar-expand-md > .container,\n  .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-expand-md {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-md .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-md .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-md .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-md > .container,\n  .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-md .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-md .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-md .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .navbar-expand-lg > .container,\n  .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .navbar-expand-lg {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-lg .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-lg .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-lg .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-lg > .container,\n  .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-lg .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-lg .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-lg .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .navbar-expand-xl > .container,\n  .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 1200px) {\n  .navbar-expand-xl {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-xl .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-xl .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-xl .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-xl > .container,\n  .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-xl .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-xl .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-xl .navbar-toggler {\n    display: none;\n  }\n}\n\n.navbar-expand {\n  -ms-flex-flow: row nowrap;\n  flex-flow: row nowrap;\n  -ms-flex-pack: start;\n  justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n  padding-right: 1rem;\n  padding-left: 1rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n  -ms-flex-wrap: nowrap;\n  flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-nav-scroll {\n  overflow: visible;\n}\n\n.navbar-expand .navbar-collapse {\n  display: -ms-flexbox !important;\n  display: flex !important;\n  -ms-flex-preferred-size: auto;\n  flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n  display: none;\n}\n\n.navbar-light .navbar-brand {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n  color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n  color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n  color: rgba(0, 0, 0, 0.5);\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n  color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n  color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n  color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n  color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n  color: white;\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n  color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n  color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n  color: rgba(255, 255, 255, 0.75);\n  border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n  color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-text a {\n  color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n  color: #fff;\n}\n\n.card {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  min-width: 0;\n  word-wrap: break-word;\n  background-color: #fff;\n  background-clip: border-box;\n  border: 0 solid rgba(0, 0, 0, 0.125);\n  border-radius: 0.25rem;\n}\n\n.card > hr {\n  margin-right: 0;\n  margin-left: 0;\n}\n\n.card > .list-group {\n  border-top: inherit;\n  border-bottom: inherit;\n}\n\n.card > .list-group:first-child {\n  border-top-width: 0;\n  border-top-left-radius: calc(0.25rem - 0);\n  border-top-right-radius: calc(0.25rem - 0);\n}\n\n.card > .list-group:last-child {\n  border-bottom-width: 0;\n  border-bottom-right-radius: calc(0.25rem - 0);\n  border-bottom-left-radius: calc(0.25rem - 0);\n}\n\n.card > .card-header + .list-group,\n.card > .list-group + .card-footer {\n  border-top: 0;\n}\n\n.card-body {\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  min-height: 1px;\n  padding: 1.25rem;\n}\n\n.card-title {\n  margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n  margin-top: -0.375rem;\n  margin-bottom: 0;\n}\n\n.card-text:last-child {\n  margin-bottom: 0;\n}\n\n.card-link:hover {\n  text-decoration: none;\n}\n\n.card-link + .card-link {\n  margin-left: 1.25rem;\n}\n\n.card-header {\n  padding: 0.75rem 1.25rem;\n  margin-bottom: 0;\n  background-color: rgba(0, 0, 0, 0.03);\n  border-bottom: 0 solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n  border-radius: calc(0.25rem - 0) calc(0.25rem - 0) 0 0;\n}\n\n.card-footer {\n  padding: 0.75rem 1.25rem;\n  background-color: rgba(0, 0, 0, 0.03);\n  border-top: 0 solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n  border-radius: 0 0 calc(0.25rem - 0) calc(0.25rem - 0);\n}\n\n.card-header-tabs {\n  margin-right: -0.625rem;\n  margin-bottom: -0.75rem;\n  margin-left: -0.625rem;\n  border-bottom: 0;\n}\n\n.card-header-pills {\n  margin-right: -0.625rem;\n  margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  padding: 1.25rem;\n  border-radius: calc(0.25rem - 0);\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n  -ms-flex-negative: 0;\n  flex-shrink: 0;\n  width: 100%;\n}\n\n.card-img,\n.card-img-top {\n  border-top-left-radius: calc(0.25rem - 0);\n  border-top-right-radius: calc(0.25rem - 0);\n}\n\n.card-img,\n.card-img-bottom {\n  border-bottom-right-radius: calc(0.25rem - 0);\n  border-bottom-left-radius: calc(0.25rem - 0);\n}\n\n.card-deck .card {\n  margin-bottom: 7.5px;\n}\n\n@media (min-width: 576px) {\n  .card-deck {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n    margin-right: -7.5px;\n    margin-left: -7.5px;\n  }\n  .card-deck .card {\n    -ms-flex: 1 0 0%;\n    flex: 1 0 0%;\n    margin-right: 7.5px;\n    margin-bottom: 0;\n    margin-left: 7.5px;\n  }\n}\n\n.card-group > .card {\n  margin-bottom: 7.5px;\n}\n\n@media (min-width: 576px) {\n  .card-group {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n  }\n  .card-group > .card {\n    -ms-flex: 1 0 0%;\n    flex: 1 0 0%;\n    margin-bottom: 0;\n  }\n  .card-group > .card + .card {\n    margin-left: 0;\n    border-left: 0;\n  }\n  .card-group > .card:not(:last-child) {\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-top,\n  .card-group > .card:not(:last-child) .card-header {\n    border-top-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-bottom,\n  .card-group > .card:not(:last-child) .card-footer {\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:first-child) {\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-top,\n  .card-group > .card:not(:first-child) .card-header {\n    border-top-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-bottom,\n  .card-group > .card:not(:first-child) .card-footer {\n    border-bottom-left-radius: 0;\n  }\n}\n\n.card-columns .card {\n  margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n  .card-columns {\n    -webkit-column-count: 3;\n    -moz-column-count: 3;\n    column-count: 3;\n    -webkit-column-gap: 1.25rem;\n    -moz-column-gap: 1.25rem;\n    column-gap: 1.25rem;\n    orphans: 1;\n    widows: 1;\n  }\n  .card-columns .card {\n    display: inline-block;\n    width: 100%;\n  }\n}\n\n.accordion {\n  overflow-anchor: none;\n}\n\n.accordion > .card {\n  overflow: hidden;\n}\n\n.accordion > .card:not(:last-of-type) {\n  border-bottom: 0;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.accordion > .card:not(:first-of-type) {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.accordion > .card > .card-header {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.breadcrumb {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  padding: 0.75rem 1rem;\n  margin-bottom: 1rem;\n  list-style: none;\n  background-color: #e9ecef;\n  border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n  padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n  float: left;\n  padding-right: 0.5rem;\n  color: #6c757d;\n  content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n  text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n  text-decoration: none;\n}\n\n.breadcrumb-item.active {\n  color: #6c757d;\n}\n\n.pagination {\n  display: -ms-flexbox;\n  display: flex;\n  padding-left: 0;\n  list-style: none;\n  border-radius: 0.25rem;\n}\n\n.page-link {\n  position: relative;\n  display: block;\n  padding: 0.5rem 0.75rem;\n  margin-left: -1px;\n  line-height: 1.25;\n  color: #007bff;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n  z-index: 2;\n  color: #0056b3;\n  text-decoration: none;\n  background-color: #e9ecef;\n  border-color: #dee2e6;\n}\n\n.page-link:focus {\n  z-index: 3;\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-item:first-child .page-link {\n  margin-left: 0;\n  border-top-left-radius: 0.25rem;\n  border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n  border-top-right-radius: 0.25rem;\n  border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n  z-index: 3;\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n  color: #6c757d;\n  pointer-events: none;\n  cursor: auto;\n  background-color: #fff;\n  border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n  padding: 0.75rem 1.5rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n  border-top-left-radius: 0.3rem;\n  border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n  border-top-right-radius: 0.3rem;\n  border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n  border-top-left-radius: 0.2rem;\n  border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n  border-top-right-radius: 0.2rem;\n  border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n  display: inline-block;\n  padding: 0.25em 0.4em;\n  font-size: 75%;\n  font-weight: 700;\n  line-height: 1;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: 0.25rem;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .badge {\n    transition: none;\n  }\n}\n\na.badge:hover, a.badge:focus {\n  text-decoration: none;\n}\n\n.badge:empty {\n  display: none;\n}\n\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n\n.badge-pill {\n  padding-right: 0.6em;\n  padding-left: 0.6em;\n  border-radius: 10rem;\n}\n\n.badge-primary {\n  color: #fff;\n  background-color: #007bff;\n}\n\na.badge-primary:hover, a.badge-primary:focus {\n  color: #fff;\n  background-color: #0062cc;\n}\n\na.badge-primary:focus, a.badge-primary.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.badge-secondary {\n  color: #fff;\n  background-color: #6c757d;\n}\n\na.badge-secondary:hover, a.badge-secondary:focus {\n  color: #fff;\n  background-color: #545b62;\n}\n\na.badge-secondary:focus, a.badge-secondary.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.badge-success {\n  color: #fff;\n  background-color: #28a745;\n}\n\na.badge-success:hover, a.badge-success:focus {\n  color: #fff;\n  background-color: #1e7e34;\n}\n\na.badge-success:focus, a.badge-success.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.badge-info {\n  color: #fff;\n  background-color: #17a2b8;\n}\n\na.badge-info:hover, a.badge-info:focus {\n  color: #fff;\n  background-color: #117a8b;\n}\n\na.badge-info:focus, a.badge-info.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.badge-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n}\n\na.badge-warning:hover, a.badge-warning:focus {\n  color: #1f2d3d;\n  background-color: #d39e00;\n}\n\na.badge-warning:focus, a.badge-warning.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.badge-danger {\n  color: #fff;\n  background-color: #dc3545;\n}\n\na.badge-danger:hover, a.badge-danger:focus {\n  color: #fff;\n  background-color: #bd2130;\n}\n\na.badge-danger:focus, a.badge-danger.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.badge-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n}\n\na.badge-light:hover, a.badge-light:focus {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n}\n\na.badge-light:focus, a.badge-light.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.badge-dark {\n  color: #fff;\n  background-color: #343a40;\n}\n\na.badge-dark:hover, a.badge-dark:focus {\n  color: #fff;\n  background-color: #1d2124;\n}\n\na.badge-dark:focus, a.badge-dark.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.jumbotron {\n  padding: 2rem 1rem;\n  margin-bottom: 2rem;\n  background-color: #e9ecef;\n  border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n  .jumbotron {\n    padding: 4rem 2rem;\n  }\n}\n\n.jumbotron-fluid {\n  padding-right: 0;\n  padding-left: 0;\n  border-radius: 0;\n}\n\n.alert {\n  position: relative;\n  padding: 0.75rem 1.25rem;\n  margin-bottom: 1rem;\n  border: 1px solid transparent;\n  border-radius: 0.25rem;\n}\n\n.alert-heading {\n  color: inherit;\n}\n\n.alert-link {\n  font-weight: 700;\n}\n\n.alert-dismissible {\n  padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  padding: 0.75rem 1.25rem;\n  color: inherit;\n}\n\n.alert-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.alert-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n  color: #002752;\n}\n\n.alert-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n  color: #202326;\n}\n\n.alert-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.alert-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n  color: #0b2e13;\n}\n\n.alert-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.alert-info hr {\n  border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n  color: #062c33;\n}\n\n.alert-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.alert-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n  color: #533f03;\n}\n\n.alert-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n  color: #491217;\n}\n\n.alert-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.alert-light hr {\n  border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n  color: #686868;\n}\n\n.alert-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n  color: #040505;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 1rem 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 1rem 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n.progress {\n  display: -ms-flexbox;\n  display: flex;\n  height: 1rem;\n  overflow: hidden;\n  line-height: 0;\n  font-size: 0.75rem;\n  background-color: #e9ecef;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1);\n}\n\n.progress-bar {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  overflow: hidden;\n  color: #fff;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #007bff;\n  transition: width 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar {\n    transition: none;\n  }\n}\n\n.progress-bar-striped {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n  -webkit-animation: 1s linear infinite progress-bar-stripes;\n  animation: 1s linear infinite progress-bar-stripes;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar-animated {\n    -webkit-animation: none;\n    animation: none;\n  }\n}\n\n.media {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: start;\n  align-items: flex-start;\n}\n\n.media-body {\n  -ms-flex: 1;\n  flex: 1;\n}\n\n.list-group {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  border-radius: 0.25rem;\n}\n\n.list-group-item-action {\n  width: 100%;\n  color: #495057;\n  text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n  z-index: 1;\n  color: #495057;\n  text-decoration: none;\n  background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n  color: #212529;\n  background-color: #e9ecef;\n}\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 0.75rem 1.25rem;\n  background-color: #fff;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n  border-top-left-radius: inherit;\n  border-top-right-radius: inherit;\n}\n\n.list-group-item:last-child {\n  border-bottom-right-radius: inherit;\n  border-bottom-left-radius: inherit;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n  color: #6c757d;\n  pointer-events: none;\n  background-color: #fff;\n}\n\n.list-group-item.active {\n  z-index: 2;\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.list-group-item + .list-group-item {\n  border-top-width: 0;\n}\n\n.list-group-item + .list-group-item.active {\n  margin-top: -1px;\n  border-top-width: 1px;\n}\n\n.list-group-horizontal {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.list-group-horizontal > .list-group-item:first-child {\n  border-bottom-left-radius: 0.25rem;\n  border-top-right-radius: 0;\n}\n\n.list-group-horizontal > .list-group-item:last-child {\n  border-top-right-radius: 0.25rem;\n  border-bottom-left-radius: 0;\n}\n\n.list-group-horizontal > .list-group-item.active {\n  margin-top: 0;\n}\n\n.list-group-horizontal > .list-group-item + .list-group-item {\n  border-top-width: 1px;\n  border-left-width: 0;\n}\n\n.list-group-horizontal > .list-group-item + .list-group-item.active {\n  margin-left: -1px;\n  border-left-width: 1px;\n}\n\n@media (min-width: 576px) {\n  .list-group-horizontal-sm {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-sm > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 768px) {\n  .list-group-horizontal-md {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-md > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 992px) {\n  .list-group-horizontal-lg {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-lg > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .list-group-horizontal-xl {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-xl > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n.list-group-flush {\n  border-radius: 0;\n}\n\n.list-group-flush > .list-group-item {\n  border-width: 0 0 1px;\n}\n\n.list-group-flush > .list-group-item:last-child {\n  border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n  color: #004085;\n  background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n  color: #004085;\n  background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n  color: #fff;\n  background-color: #004085;\n  border-color: #004085;\n}\n\n.list-group-item-secondary {\n  color: #383d41;\n  background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n  color: #383d41;\n  background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n  color: #fff;\n  background-color: #383d41;\n  border-color: #383d41;\n}\n\n.list-group-item-success {\n  color: #155724;\n  background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n  color: #155724;\n  background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n  color: #fff;\n  background-color: #155724;\n  border-color: #155724;\n}\n\n.list-group-item-info {\n  color: #0c5460;\n  background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n  color: #0c5460;\n  background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n  color: #fff;\n  background-color: #0c5460;\n  border-color: #0c5460;\n}\n\n.list-group-item-warning {\n  color: #856404;\n  background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n  color: #856404;\n  background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n  color: #fff;\n  background-color: #856404;\n  border-color: #856404;\n}\n\n.list-group-item-danger {\n  color: #721c24;\n  background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n  color: #721c24;\n  background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n  color: #fff;\n  background-color: #721c24;\n  border-color: #721c24;\n}\n\n.list-group-item-light {\n  color: #818182;\n  background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n  color: #818182;\n  background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n  color: #fff;\n  background-color: #818182;\n  border-color: #818182;\n}\n\n.list-group-item-dark {\n  color: #1b1e21;\n  background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n  color: #1b1e21;\n  background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n  color: #fff;\n  background-color: #1b1e21;\n  border-color: #1b1e21;\n}\n\n.close {\n  float: right;\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: .5;\n}\n\n.close:hover {\n  color: #000;\n  text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {\n  opacity: .75;\n}\n\nbutton.close {\n  padding: 0;\n  background-color: transparent;\n  border: 0;\n}\n\na.close.disabled {\n  pointer-events: none;\n}\n\n.toast {\n  -ms-flex-preferred-size: 350px;\n  flex-basis: 350px;\n  max-width: 350px;\n  font-size: 0.875rem;\n  background-color: rgba(255, 255, 255, 0.85);\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.1);\n  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);\n  opacity: 0;\n  border-radius: 0.25rem;\n}\n\n.toast:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.toast.showing {\n  opacity: 1;\n}\n\n.toast.show {\n  display: block;\n  opacity: 1;\n}\n\n.toast.hide {\n  display: none;\n}\n\n.toast-header {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding: 0.25rem 0.75rem;\n  color: #6c757d;\n  background-color: rgba(255, 255, 255, 0.85);\n  background-clip: padding-box;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n  border-top-left-radius: calc(0.25rem - 1px);\n  border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.toast-body {\n  padding: 0.75rem;\n}\n\n.modal-open {\n  overflow: hidden;\n}\n\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n.modal {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 1050;\n  display: none;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  outline: 0;\n}\n\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 0.5rem;\n  pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n  transition: -webkit-transform 0.3s ease-out;\n  transition: transform 0.3s ease-out;\n  transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;\n  -webkit-transform: translate(0, -50px);\n  transform: translate(0, -50px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .modal.fade .modal-dialog {\n    transition: none;\n  }\n}\n\n.modal.show .modal-dialog {\n  -webkit-transform: none;\n  transform: none;\n}\n\n.modal.modal-static .modal-dialog {\n  -webkit-transform: scale(1.02);\n  transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n  display: -ms-flexbox;\n  display: flex;\n  max-height: calc(100% - 1rem);\n}\n\n.modal-dialog-scrollable .modal-content {\n  max-height: calc(100vh - 1rem);\n  overflow: hidden;\n}\n\n.modal-dialog-scrollable .modal-header,\n.modal-dialog-scrollable .modal-footer {\n  -ms-flex-negative: 0;\n  flex-shrink: 0;\n}\n\n.modal-dialog-scrollable .modal-body {\n  overflow-y: auto;\n}\n\n.modal-dialog-centered {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  min-height: calc(100% - 1rem);\n}\n\n.modal-dialog-centered::before {\n  display: block;\n  height: calc(100vh - 1rem);\n  height: -webkit-min-content;\n  height: -moz-min-content;\n  height: min-content;\n  content: \"\";\n}\n\n.modal-dialog-centered.modal-dialog-scrollable {\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  height: 100%;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable .modal-content {\n  max-height: none;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable::before {\n  content: none;\n}\n\n.modal-content {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  width: 100%;\n  pointer-events: auto;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 0.3rem;\n  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5);\n  outline: 0;\n}\n\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 1040;\n  width: 100vw;\n  height: 100vh;\n  background-color: #000;\n}\n\n.modal-backdrop.fade {\n  opacity: 0;\n}\n\n.modal-backdrop.show {\n  opacity: 0.5;\n}\n\n.modal-header {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: start;\n  align-items: flex-start;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n  padding: 1rem;\n  border-bottom: 1px solid #e9ecef;\n  border-top-left-radius: calc(0.3rem - 1px);\n  border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.modal-header .close {\n  padding: 1rem;\n  margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n  margin-bottom: 0;\n  line-height: 1.5;\n}\n\n.modal-body {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  padding: 1rem;\n}\n\n.modal-footer {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: end;\n  justify-content: flex-end;\n  padding: 0.75rem;\n  border-top: 1px solid #e9ecef;\n  border-bottom-right-radius: calc(0.3rem - 1px);\n  border-bottom-left-radius: calc(0.3rem - 1px);\n}\n\n.modal-footer > * {\n  margin: 0.25rem;\n}\n\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n\n@media (min-width: 576px) {\n  .modal-dialog {\n    max-width: 500px;\n    margin: 1.75rem auto;\n  }\n  .modal-dialog-scrollable {\n    max-height: calc(100% - 3.5rem);\n  }\n  .modal-dialog-scrollable .modal-content {\n    max-height: calc(100vh - 3.5rem);\n  }\n  .modal-dialog-centered {\n    min-height: calc(100% - 3.5rem);\n  }\n  .modal-dialog-centered::before {\n    height: calc(100vh - 3.5rem);\n    height: -webkit-min-content;\n    height: -moz-min-content;\n    height: min-content;\n  }\n  .modal-content {\n    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);\n  }\n  .modal-sm {\n    max-width: 300px;\n  }\n}\n\n@media (min-width: 992px) {\n  .modal-lg,\n  .modal-xl {\n    max-width: 800px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .modal-xl {\n    max-width: 1140px;\n  }\n}\n\n.tooltip {\n  position: absolute;\n  z-index: 1070;\n  display: block;\n  margin: 0;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  font-size: 0.875rem;\n  word-wrap: break-word;\n  opacity: 0;\n}\n\n.tooltip.show {\n  opacity: 0.9;\n}\n\n.tooltip .arrow {\n  position: absolute;\n  display: block;\n  width: 0.8rem;\n  height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n  position: absolute;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n  padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n  bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n  top: 0;\n  border-width: 0.4rem 0.4rem 0;\n  border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n  padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n  left: 0;\n  width: 0.4rem;\n  height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n  right: 0;\n  border-width: 0.4rem 0.4rem 0.4rem 0;\n  border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n  padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n  top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n  bottom: 0;\n  border-width: 0 0.4rem 0.4rem;\n  border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n  padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n  right: 0;\n  width: 0.4rem;\n  height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n  left: 0;\n  border-width: 0.4rem 0 0.4rem 0.4rem;\n  border-left-color: #000;\n}\n\n.tooltip-inner {\n  max-width: 200px;\n  padding: 0.25rem 0.5rem;\n  color: #fff;\n  text-align: center;\n  background-color: #000;\n  border-radius: 0.25rem;\n}\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1060;\n  display: block;\n  max-width: 276px;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  font-size: 0.875rem;\n  word-wrap: break-word;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 0.3rem;\n  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.2);\n}\n\n.popover .arrow {\n  position: absolute;\n  display: block;\n  width: 1rem;\n  height: 0.5rem;\n  margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n  position: absolute;\n  display: block;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n  margin-bottom: 0.5rem;\n}\n\n.bs-popover-top > .arrow, .bs-popover-auto[x-placement^=\"top\"] > .arrow {\n  bottom: calc(-0.5rem - 1px);\n}\n\n.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^=\"top\"] > .arrow::before {\n  bottom: 0;\n  border-width: 0.5rem 0.5rem 0;\n  border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^=\"top\"] > .arrow::after {\n  bottom: 1px;\n  border-width: 0.5rem 0.5rem 0;\n  border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n  margin-left: 0.5rem;\n}\n\n.bs-popover-right > .arrow, .bs-popover-auto[x-placement^=\"right\"] > .arrow {\n  left: calc(-0.5rem - 1px);\n  width: 0.5rem;\n  height: 1rem;\n  margin: 0.3rem 0;\n}\n\n.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^=\"right\"] > .arrow::before {\n  left: 0;\n  border-width: 0.5rem 0.5rem 0.5rem 0;\n  border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^=\"right\"] > .arrow::after {\n  left: 1px;\n  border-width: 0.5rem 0.5rem 0.5rem 0;\n  border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n  margin-top: 0.5rem;\n}\n\n.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow {\n  top: calc(-0.5rem - 1px);\n}\n\n.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::before {\n  top: 0;\n  border-width: 0 0.5rem 0.5rem 0.5rem;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::after {\n  top: 1px;\n  border-width: 0 0.5rem 0.5rem 0.5rem;\n  border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n  position: absolute;\n  top: 0;\n  left: 50%;\n  display: block;\n  width: 1rem;\n  margin-left: -0.5rem;\n  content: \"\";\n  border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n  margin-right: 0.5rem;\n}\n\n.bs-popover-left > .arrow, .bs-popover-auto[x-placement^=\"left\"] > .arrow {\n  right: calc(-0.5rem - 1px);\n  width: 0.5rem;\n  height: 1rem;\n  margin: 0.3rem 0;\n}\n\n.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^=\"left\"] > .arrow::before {\n  right: 0;\n  border-width: 0.5rem 0 0.5rem 0.5rem;\n  border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^=\"left\"] > .arrow::after {\n  right: 1px;\n  border-width: 0.5rem 0 0.5rem 0.5rem;\n  border-left-color: #fff;\n}\n\n.popover-header {\n  padding: 0.5rem 0.75rem;\n  margin-bottom: 0;\n  font-size: 1rem;\n  color: inherit;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-top-left-radius: calc(0.3rem - 1px);\n  border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n  display: none;\n}\n\n.popover-body {\n  padding: 0.5rem 0.75rem;\n  color: #212529;\n}\n\n.carousel {\n  position: relative;\n}\n\n.carousel.pointer-event {\n  -ms-touch-action: pan-y;\n  touch-action: pan-y;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n\n.carousel-inner::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.carousel-item {\n  position: relative;\n  display: none;\n  float: left;\n  width: 100%;\n  margin-right: -100%;\n  -webkit-backface-visibility: hidden;\n  backface-visibility: hidden;\n  transition: -webkit-transform 0.6s ease;\n  transition: transform 0.6s ease;\n  transition: transform 0.6s ease, -webkit-transform 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-item {\n    transition: none;\n  }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n  display: block;\n}\n\n.carousel-item-next:not(.carousel-item-left),\n.active.carousel-item-right {\n  -webkit-transform: translateX(100%);\n  transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-right),\n.active.carousel-item-left {\n  -webkit-transform: translateX(-100%);\n  transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n  opacity: 0;\n  transition-property: opacity;\n  -webkit-transform: none;\n  transform: none;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n  z-index: 1;\n  opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n  z-index: 0;\n  opacity: 0;\n  transition: opacity 0s 0.6s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-fade .active.carousel-item-left,\n  .carousel-fade .active.carousel-item-right {\n    transition: none;\n  }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  z-index: 1;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: center;\n  justify-content: center;\n  width: 15%;\n  padding: 0;\n  color: #fff;\n  text-align: center;\n  background: none;\n  border: 0;\n  opacity: 0.5;\n  transition: opacity 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-control-prev,\n  .carousel-control-next {\n    transition: none;\n  }\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n  color: #fff;\n  text-decoration: none;\n  outline: 0;\n  opacity: 0.9;\n}\n\n.carousel-control-prev {\n  left: 0;\n}\n\n.carousel-control-next {\n  right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n  display: inline-block;\n  width: 20px;\n  height: 20px;\n  background: 50% / 100% 100% no-repeat;\n}\n\n.carousel-control-prev-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 15;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  padding-left: 0;\n  margin-right: 15%;\n  margin-left: 15%;\n  list-style: none;\n}\n\n.carousel-indicators li {\n  box-sizing: content-box;\n  -ms-flex: 0 1 auto;\n  flex: 0 1 auto;\n  width: 30px;\n  height: 3px;\n  margin-right: 3px;\n  margin-left: 3px;\n  text-indent: -999px;\n  cursor: pointer;\n  background-color: #fff;\n  background-clip: padding-box;\n  border-top: 10px solid transparent;\n  border-bottom: 10px solid transparent;\n  opacity: .5;\n  transition: opacity 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-indicators li {\n    transition: none;\n  }\n}\n\n.carousel-indicators .active {\n  opacity: 1;\n}\n\n.carousel-caption {\n  position: absolute;\n  right: 15%;\n  bottom: 20px;\n  left: 15%;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #fff;\n  text-align: center;\n}\n\n@-webkit-keyframes spinner-border {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg);\n  }\n}\n\n@keyframes spinner-border {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg);\n  }\n}\n\n.spinner-border {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  vertical-align: -0.125em;\n  border: 0.25em solid currentColor;\n  border-right-color: transparent;\n  border-radius: 50%;\n  -webkit-animation: .75s linear infinite spinner-border;\n  animation: .75s linear infinite spinner-border;\n}\n\n.spinner-border-sm {\n  width: 1rem;\n  height: 1rem;\n  border-width: 0.2em;\n}\n\n@-webkit-keyframes spinner-grow {\n  0% {\n    -webkit-transform: scale(0);\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes spinner-grow {\n  0% {\n    -webkit-transform: scale(0);\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.spinner-grow {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  vertical-align: -0.125em;\n  background-color: currentColor;\n  border-radius: 50%;\n  opacity: 0;\n  -webkit-animation: .75s linear infinite spinner-grow;\n  animation: .75s linear infinite spinner-grow;\n}\n\n.spinner-grow-sm {\n  width: 1rem;\n  height: 1rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .spinner-border,\n  .spinner-grow {\n    -webkit-animation-duration: 1.5s;\n    animation-duration: 1.5s;\n  }\n}\n\n.align-baseline {\n  vertical-align: baseline !important;\n}\n\n.align-top {\n  vertical-align: top !important;\n}\n\n.align-middle {\n  vertical-align: middle !important;\n}\n\n.align-bottom {\n  vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n  vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n  vertical-align: text-top !important;\n}\n\n.bg-primary {\n  background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n  background-color: #0062cc !important;\n}\n\n.bg-secondary {\n  background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n  background-color: #545b62 !important;\n}\n\n.bg-success {\n  background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n  background-color: #1e7e34 !important;\n}\n\n.bg-info {\n  background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n  background-color: #117a8b !important;\n}\n\n.bg-warning {\n  background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n  background-color: #d39e00 !important;\n}\n\n.bg-danger {\n  background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n  background-color: #bd2130 !important;\n}\n\n.bg-light {\n  background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n  background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n  background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n  background-color: #1d2124 !important;\n}\n\n.bg-white {\n  background-color: #fff !important;\n}\n\n.bg-transparent {\n  background-color: transparent !important;\n}\n\n.border {\n  border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n  border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n  border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n  border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n  border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n  border: 0 !important;\n}\n\n.border-top-0 {\n  border-top: 0 !important;\n}\n\n.border-right-0 {\n  border-right: 0 !important;\n}\n\n.border-bottom-0 {\n  border-bottom: 0 !important;\n}\n\n.border-left-0 {\n  border-left: 0 !important;\n}\n\n.border-primary {\n  border-color: #007bff !important;\n}\n\n.border-secondary {\n  border-color: #6c757d !important;\n}\n\n.border-success {\n  border-color: #28a745 !important;\n}\n\n.border-info {\n  border-color: #17a2b8 !important;\n}\n\n.border-warning {\n  border-color: #ffc107 !important;\n}\n\n.border-danger {\n  border-color: #dc3545 !important;\n}\n\n.border-light {\n  border-color: #f8f9fa !important;\n}\n\n.border-dark {\n  border-color: #343a40 !important;\n}\n\n.border-white {\n  border-color: #fff !important;\n}\n\n.rounded-sm {\n  border-radius: 0.2rem !important;\n}\n\n.rounded {\n  border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n  border-top-left-radius: 0.25rem !important;\n  border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n  border-top-right-radius: 0.25rem !important;\n  border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n  border-bottom-right-radius: 0.25rem !important;\n  border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n  border-top-left-radius: 0.25rem !important;\n  border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-lg {\n  border-radius: 0.3rem !important;\n}\n\n.rounded-circle {\n  border-radius: 50% !important;\n}\n\n.rounded-pill {\n  border-radius: 50rem !important;\n}\n\n.rounded-0 {\n  border-radius: 0 !important;\n}\n\n.clearfix::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.d-none {\n  display: none !important;\n}\n\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: -ms-flexbox !important;\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: -ms-inline-flexbox !important;\n  display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n  .d-sm-none {\n    display: none !important;\n  }\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .d-md-none {\n    display: none !important;\n  }\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .d-lg-none {\n    display: none !important;\n  }\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .d-xl-none {\n    display: none !important;\n  }\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media print {\n  .d-print-none {\n    display: none !important;\n  }\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n.embed-responsive {\n  position: relative;\n  display: block;\n  width: 100%;\n  padding: 0;\n  overflow: hidden;\n}\n\n.embed-responsive::before {\n  display: block;\n  content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  border: 0;\n}\n\n.embed-responsive-21by9::before {\n  padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n  padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n  padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n  padding-top: 100%;\n}\n\n.flex-row {\n  -ms-flex-direction: row !important;\n  flex-direction: row !important;\n}\n\n.flex-column {\n  -ms-flex-direction: column !important;\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  -ms-flex-direction: row-reverse !important;\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  -ms-flex-direction: column-reverse !important;\n  flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n  -ms-flex-wrap: wrap !important;\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  -ms-flex-wrap: nowrap !important;\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  -ms-flex-wrap: wrap-reverse !important;\n  flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n  -ms-flex: 1 1 auto !important;\n  flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n  -ms-flex-positive: 0 !important;\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  -ms-flex-positive: 1 !important;\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  -ms-flex-negative: 0 !important;\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  -ms-flex-negative: 1 !important;\n  flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n  -ms-flex-pack: start !important;\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  -ms-flex-pack: end !important;\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  -ms-flex-pack: center !important;\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  -ms-flex-pack: justify !important;\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  -ms-flex-pack: distribute !important;\n  justify-content: space-around !important;\n}\n\n.align-items-start {\n  -ms-flex-align: start !important;\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  -ms-flex-align: end !important;\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  -ms-flex-align: center !important;\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  -ms-flex-align: baseline !important;\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  -ms-flex-align: stretch !important;\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  -ms-flex-line-pack: start !important;\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  -ms-flex-line-pack: end !important;\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  -ms-flex-line-pack: center !important;\n  align-content: center !important;\n}\n\n.align-content-between {\n  -ms-flex-line-pack: justify !important;\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  -ms-flex-line-pack: distribute !important;\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  -ms-flex-line-pack: stretch !important;\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  -ms-flex-item-align: auto !important;\n  align-self: auto !important;\n}\n\n.align-self-start {\n  -ms-flex-item-align: start !important;\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  -ms-flex-item-align: end !important;\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  -ms-flex-item-align: center !important;\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  -ms-flex-item-align: baseline !important;\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  -ms-flex-item-align: stretch !important;\n  align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n  .flex-sm-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-sm-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-sm-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-sm-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .flex-md-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-md-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-md-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-md-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-md-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .flex-lg-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-lg-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-lg-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-lg-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .flex-xl-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-xl-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-xl-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-xl-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n.float-left {\n  float: left !important;\n}\n\n.float-right {\n  float: right !important;\n}\n\n.float-none {\n  float: none !important;\n}\n\n@media (min-width: 576px) {\n  .float-sm-left {\n    float: left !important;\n  }\n  .float-sm-right {\n    float: right !important;\n  }\n  .float-sm-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .float-md-left {\n    float: left !important;\n  }\n  .float-md-right {\n    float: right !important;\n  }\n  .float-md-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .float-lg-left {\n    float: left !important;\n  }\n  .float-lg-right {\n    float: right !important;\n  }\n  .float-lg-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .float-xl-left {\n    float: left !important;\n  }\n  .float-xl-right {\n    float: right !important;\n  }\n  .float-xl-none {\n    float: none !important;\n  }\n}\n\n.user-select-all {\n  -webkit-user-select: all !important;\n  -moz-user-select: all !important;\n  user-select: all !important;\n}\n\n.user-select-auto {\n  -webkit-user-select: auto !important;\n  -moz-user-select: auto !important;\n  -ms-user-select: auto !important;\n  user-select: auto !important;\n}\n\n.user-select-none {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  -ms-user-select: none !important;\n  user-select: none !important;\n}\n\n.overflow-auto {\n  overflow: auto !important;\n}\n\n.overflow-hidden {\n  overflow: hidden !important;\n}\n\n.position-static {\n  position: static !important;\n}\n\n.position-relative {\n  position: relative !important;\n}\n\n.position-absolute {\n  position: absolute !important;\n}\n\n.position-fixed {\n  position: fixed !important;\n}\n\n.position-sticky {\n  position: -webkit-sticky !important;\n  position: sticky !important;\n}\n\n.fixed-top {\n  position: fixed;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n.fixed-bottom {\n  position: fixed;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n@supports ((position: -webkit-sticky) or (position: sticky)) {\n  .sticky-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  overflow: visible;\n  clip: auto;\n  white-space: normal;\n}\n\n.shadow-sm {\n  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n  box-shadow: none !important;\n}\n\n.w-25 {\n  width: 25% !important;\n}\n\n.w-50 {\n  width: 50% !important;\n}\n\n.w-75 {\n  width: 75% !important;\n}\n\n.w-100 {\n  width: 100% !important;\n}\n\n.w-auto {\n  width: auto !important;\n}\n\n.h-25 {\n  height: 25% !important;\n}\n\n.h-50 {\n  height: 50% !important;\n}\n\n.h-75 {\n  height: 75% !important;\n}\n\n.h-100 {\n  height: 100% !important;\n}\n\n.h-auto {\n  height: auto !important;\n}\n\n.mw-100 {\n  max-width: 100% !important;\n}\n\n.mh-100 {\n  max-height: 100% !important;\n}\n\n.min-vw-100 {\n  min-width: 100vw !important;\n}\n\n.min-vh-100 {\n  min-height: 100vh !important;\n}\n\n.vw-100 {\n  width: 100vw !important;\n}\n\n.vh-100 {\n  height: 100vh !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n  margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n  margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n  margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n  margin-left: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n  margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n  margin-left: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n  margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n  margin-left: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n  margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n  margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n  margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n  margin-left: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n  margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n  margin-left: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n  margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n  margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n  margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n  margin-left: 3rem !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n  padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n  padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n  padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n  padding-left: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n  padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n  padding-left: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n  padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n  padding-left: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n  padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n  padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n  padding-left: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n  padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n  padding-left: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n  padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n  padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n  padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n  padding-left: 3rem !important;\n}\n\n.m-n1 {\n  margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n  margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n  margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n  margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n  margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n  margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n  margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n  margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n  margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n  margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n  margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n  margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n  margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n  margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n  margin-left: -1rem !important;\n}\n\n.m-n4 {\n  margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n  margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n  margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n  margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n  margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n  margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n  margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n  margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n  margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n  margin-left: -3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n  margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n  margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n  margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n  margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .mt-sm-0,\n  .my-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mr-sm-0,\n  .mx-sm-0 {\n    margin-right: 0 !important;\n  }\n  .mb-sm-0,\n  .my-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-sm-0,\n  .mx-sm-0 {\n    margin-left: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-sm-1,\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-sm-1,\n  .mx-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-sm-1,\n  .my-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-sm-1,\n  .mx-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-sm-2,\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-sm-2,\n  .mx-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-sm-2,\n  .my-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-sm-2,\n  .mx-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .mt-sm-3,\n  .my-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-sm-3,\n  .mx-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-sm-3,\n  .my-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-sm-3,\n  .mx-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-sm-4,\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-sm-4,\n  .mx-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-sm-4,\n  .my-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-sm-4,\n  .mx-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .mt-sm-5,\n  .my-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-sm-5,\n  .mx-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-sm-5,\n  .my-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-sm-5,\n  .mx-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .pt-sm-0,\n  .py-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pr-sm-0,\n  .px-sm-0 {\n    padding-right: 0 !important;\n  }\n  .pb-sm-0,\n  .py-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-sm-0,\n  .px-sm-0 {\n    padding-left: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-sm-1,\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-sm-1,\n  .px-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-sm-1,\n  .py-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-sm-1,\n  .px-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-sm-2,\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-sm-2,\n  .px-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-sm-2,\n  .py-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-sm-2,\n  .px-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .pt-sm-3,\n  .py-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-sm-3,\n  .px-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-sm-3,\n  .py-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-sm-3,\n  .px-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-sm-4,\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-sm-4,\n  .px-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-sm-4,\n  .py-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-sm-4,\n  .px-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .pt-sm-5,\n  .py-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-sm-5,\n  .px-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-sm-5,\n  .py-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-sm-5,\n  .px-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .m-sm-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-sm-n1,\n  .my-sm-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-sm-n1,\n  .mx-sm-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-sm-n1,\n  .my-sm-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-sm-n1,\n  .mx-sm-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-sm-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-sm-n2,\n  .my-sm-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-sm-n2,\n  .mx-sm-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-sm-n2,\n  .my-sm-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-sm-n2,\n  .mx-sm-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-sm-n3 {\n    margin: -1rem !important;\n  }\n  .mt-sm-n3,\n  .my-sm-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-sm-n3,\n  .mx-sm-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-sm-n3,\n  .my-sm-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-sm-n3,\n  .mx-sm-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-sm-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-sm-n4,\n  .my-sm-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-sm-n4,\n  .mx-sm-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-sm-n4,\n  .my-sm-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-sm-n4,\n  .mx-sm-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-sm-n5 {\n    margin: -3rem !important;\n  }\n  .mt-sm-n5,\n  .my-sm-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-sm-n5,\n  .mx-sm-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-sm-n5,\n  .my-sm-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-sm-n5,\n  .mx-sm-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mt-sm-auto,\n  .my-sm-auto {\n    margin-top: auto !important;\n  }\n  .mr-sm-auto,\n  .mx-sm-auto {\n    margin-right: auto !important;\n  }\n  .mb-sm-auto,\n  .my-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-sm-auto,\n  .mx-sm-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .mt-md-0,\n  .my-md-0 {\n    margin-top: 0 !important;\n  }\n  .mr-md-0,\n  .mx-md-0 {\n    margin-right: 0 !important;\n  }\n  .mb-md-0,\n  .my-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-md-0,\n  .mx-md-0 {\n    margin-left: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-md-1,\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-md-1,\n  .mx-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-md-1,\n  .my-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-md-1,\n  .mx-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-md-2,\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-md-2,\n  .mx-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-md-2,\n  .my-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-md-2,\n  .mx-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .mt-md-3,\n  .my-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-md-3,\n  .mx-md-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-md-3,\n  .my-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-md-3,\n  .mx-md-3 {\n    margin-left: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-md-4,\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-md-4,\n  .mx-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-md-4,\n  .my-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-md-4,\n  .mx-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .mt-md-5,\n  .my-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-md-5,\n  .mx-md-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-md-5,\n  .my-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-md-5,\n  .mx-md-5 {\n    margin-left: 3rem !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .pt-md-0,\n  .py-md-0 {\n    padding-top: 0 !important;\n  }\n  .pr-md-0,\n  .px-md-0 {\n    padding-right: 0 !important;\n  }\n  .pb-md-0,\n  .py-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-md-0,\n  .px-md-0 {\n    padding-left: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-md-1,\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-md-1,\n  .px-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-md-1,\n  .py-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-md-1,\n  .px-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-md-2,\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-md-2,\n  .px-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-md-2,\n  .py-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-md-2,\n  .px-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .pt-md-3,\n  .py-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-md-3,\n  .px-md-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-md-3,\n  .py-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-md-3,\n  .px-md-3 {\n    padding-left: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-md-4,\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-md-4,\n  .px-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-md-4,\n  .py-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-md-4,\n  .px-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .pt-md-5,\n  .py-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-md-5,\n  .px-md-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-md-5,\n  .py-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-md-5,\n  .px-md-5 {\n    padding-left: 3rem !important;\n  }\n  .m-md-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-md-n1,\n  .my-md-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-md-n1,\n  .mx-md-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-md-n1,\n  .my-md-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-md-n1,\n  .mx-md-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-md-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-md-n2,\n  .my-md-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-md-n2,\n  .mx-md-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-md-n2,\n  .my-md-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-md-n2,\n  .mx-md-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-md-n3 {\n    margin: -1rem !important;\n  }\n  .mt-md-n3,\n  .my-md-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-md-n3,\n  .mx-md-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-md-n3,\n  .my-md-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-md-n3,\n  .mx-md-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-md-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-md-n4,\n  .my-md-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-md-n4,\n  .mx-md-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-md-n4,\n  .my-md-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-md-n4,\n  .mx-md-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-md-n5 {\n    margin: -3rem !important;\n  }\n  .mt-md-n5,\n  .my-md-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-md-n5,\n  .mx-md-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-md-n5,\n  .my-md-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-md-n5,\n  .mx-md-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mt-md-auto,\n  .my-md-auto {\n    margin-top: auto !important;\n  }\n  .mr-md-auto,\n  .mx-md-auto {\n    margin-right: auto !important;\n  }\n  .mb-md-auto,\n  .my-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-md-auto,\n  .mx-md-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .mt-lg-0,\n  .my-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mr-lg-0,\n  .mx-lg-0 {\n    margin-right: 0 !important;\n  }\n  .mb-lg-0,\n  .my-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-lg-0,\n  .mx-lg-0 {\n    margin-left: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-lg-1,\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-lg-1,\n  .mx-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-lg-1,\n  .my-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-lg-1,\n  .mx-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-lg-2,\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-lg-2,\n  .mx-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-lg-2,\n  .my-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-lg-2,\n  .mx-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .mt-lg-3,\n  .my-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-lg-3,\n  .mx-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-lg-3,\n  .my-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-lg-3,\n  .mx-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-lg-4,\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-lg-4,\n  .mx-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-lg-4,\n  .my-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-lg-4,\n  .mx-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .mt-lg-5,\n  .my-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-lg-5,\n  .mx-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-lg-5,\n  .my-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-lg-5,\n  .mx-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .pt-lg-0,\n  .py-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pr-lg-0,\n  .px-lg-0 {\n    padding-right: 0 !important;\n  }\n  .pb-lg-0,\n  .py-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-lg-0,\n  .px-lg-0 {\n    padding-left: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-lg-1,\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-lg-1,\n  .px-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-lg-1,\n  .py-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-lg-1,\n  .px-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-lg-2,\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-lg-2,\n  .px-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-lg-2,\n  .py-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-lg-2,\n  .px-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .pt-lg-3,\n  .py-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-lg-3,\n  .px-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-lg-3,\n  .py-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-lg-3,\n  .px-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-lg-4,\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-lg-4,\n  .px-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-lg-4,\n  .py-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-lg-4,\n  .px-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .pt-lg-5,\n  .py-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-lg-5,\n  .px-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-lg-5,\n  .py-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-lg-5,\n  .px-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .m-lg-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-lg-n1,\n  .my-lg-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-lg-n1,\n  .mx-lg-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-lg-n1,\n  .my-lg-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-lg-n1,\n  .mx-lg-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-lg-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-lg-n2,\n  .my-lg-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-lg-n2,\n  .mx-lg-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-lg-n2,\n  .my-lg-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-lg-n2,\n  .mx-lg-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-lg-n3 {\n    margin: -1rem !important;\n  }\n  .mt-lg-n3,\n  .my-lg-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-lg-n3,\n  .mx-lg-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-lg-n3,\n  .my-lg-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-lg-n3,\n  .mx-lg-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-lg-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-lg-n4,\n  .my-lg-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-lg-n4,\n  .mx-lg-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-lg-n4,\n  .my-lg-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-lg-n4,\n  .mx-lg-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-lg-n5 {\n    margin: -3rem !important;\n  }\n  .mt-lg-n5,\n  .my-lg-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-lg-n5,\n  .mx-lg-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-lg-n5,\n  .my-lg-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-lg-n5,\n  .mx-lg-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mt-lg-auto,\n  .my-lg-auto {\n    margin-top: auto !important;\n  }\n  .mr-lg-auto,\n  .mx-lg-auto {\n    margin-right: auto !important;\n  }\n  .mb-lg-auto,\n  .my-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-lg-auto,\n  .mx-lg-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .mt-xl-0,\n  .my-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mr-xl-0,\n  .mx-xl-0 {\n    margin-right: 0 !important;\n  }\n  .mb-xl-0,\n  .my-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-xl-0,\n  .mx-xl-0 {\n    margin-left: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-xl-1,\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-xl-1,\n  .mx-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-xl-1,\n  .my-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-xl-1,\n  .mx-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-xl-2,\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-xl-2,\n  .mx-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-xl-2,\n  .my-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-xl-2,\n  .mx-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .mt-xl-3,\n  .my-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-xl-3,\n  .mx-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-xl-3,\n  .my-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-xl-3,\n  .mx-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-xl-4,\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-xl-4,\n  .mx-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-xl-4,\n  .my-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-xl-4,\n  .mx-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .mt-xl-5,\n  .my-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-xl-5,\n  .mx-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-xl-5,\n  .my-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-xl-5,\n  .mx-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .pt-xl-0,\n  .py-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pr-xl-0,\n  .px-xl-0 {\n    padding-right: 0 !important;\n  }\n  .pb-xl-0,\n  .py-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-xl-0,\n  .px-xl-0 {\n    padding-left: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-xl-1,\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-xl-1,\n  .px-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-xl-1,\n  .py-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-xl-1,\n  .px-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-xl-2,\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-xl-2,\n  .px-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-xl-2,\n  .py-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-xl-2,\n  .px-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .pt-xl-3,\n  .py-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-xl-3,\n  .px-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-xl-3,\n  .py-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-xl-3,\n  .px-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-xl-4,\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-xl-4,\n  .px-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-xl-4,\n  .py-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-xl-4,\n  .px-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .pt-xl-5,\n  .py-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-xl-5,\n  .px-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xl-5,\n  .py-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-xl-5,\n  .px-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .m-xl-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-xl-n1,\n  .my-xl-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-xl-n1,\n  .mx-xl-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-xl-n1,\n  .my-xl-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-xl-n1,\n  .mx-xl-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-xl-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-xl-n2,\n  .my-xl-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-xl-n2,\n  .mx-xl-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-xl-n2,\n  .my-xl-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-xl-n2,\n  .mx-xl-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-xl-n3 {\n    margin: -1rem !important;\n  }\n  .mt-xl-n3,\n  .my-xl-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-xl-n3,\n  .mx-xl-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-xl-n3,\n  .my-xl-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-xl-n3,\n  .mx-xl-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-xl-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-xl-n4,\n  .my-xl-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-xl-n4,\n  .mx-xl-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-xl-n4,\n  .my-xl-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-xl-n4,\n  .mx-xl-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-xl-n5 {\n    margin: -3rem !important;\n  }\n  .mt-xl-n5,\n  .my-xl-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-xl-n5,\n  .mx-xl-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-xl-n5,\n  .my-xl-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-xl-n5,\n  .mx-xl-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mt-xl-auto,\n  .my-xl-auto {\n    margin-top: auto !important;\n  }\n  .mr-xl-auto,\n  .mx-xl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xl-auto,\n  .my-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-xl-auto,\n  .mx-xl-auto {\n    margin-left: auto !important;\n  }\n}\n\n.stretched-link::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1;\n  pointer-events: auto;\n  content: \"\";\n  background-color: rgba(0, 0, 0, 0);\n}\n\n.text-monospace {\n  font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !important;\n}\n\n.text-justify {\n  text-align: justify !important;\n}\n\n.text-wrap {\n  white-space: normal !important;\n}\n\n.text-nowrap {\n  white-space: nowrap !important;\n}\n\n.text-truncate {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.text-left {\n  text-align: left !important;\n}\n\n.text-right {\n  text-align: right !important;\n}\n\n.text-center {\n  text-align: center !important;\n}\n\n@media (min-width: 576px) {\n  .text-sm-left {\n    text-align: left !important;\n  }\n  .text-sm-right {\n    text-align: right !important;\n  }\n  .text-sm-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .text-md-left {\n    text-align: left !important;\n  }\n  .text-md-right {\n    text-align: right !important;\n  }\n  .text-md-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .text-lg-left {\n    text-align: left !important;\n  }\n  .text-lg-right {\n    text-align: right !important;\n  }\n  .text-lg-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .text-xl-left {\n    text-align: left !important;\n  }\n  .text-xl-right {\n    text-align: right !important;\n  }\n  .text-xl-center {\n    text-align: center !important;\n  }\n}\n\n.text-lowercase {\n  text-transform: lowercase !important;\n}\n\n.text-uppercase {\n  text-transform: uppercase !important;\n}\n\n.text-capitalize {\n  text-transform: capitalize !important;\n}\n\n.font-weight-light {\n  font-weight: 300 !important;\n}\n\n.font-weight-lighter {\n  font-weight: lighter !important;\n}\n\n.font-weight-normal {\n  font-weight: 400 !important;\n}\n\n.font-weight-bold {\n  font-weight: 700 !important;\n}\n\n.font-weight-bolder {\n  font-weight: bolder !important;\n}\n\n.font-italic {\n  font-style: italic !important;\n}\n\n.text-white {\n  color: #fff !important;\n}\n\n.text-primary {\n  color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n  color: #0056b3 !important;\n}\n\n.text-secondary {\n  color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n  color: #494f54 !important;\n}\n\n.text-success {\n  color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n  color: #19692c !important;\n}\n\n.text-info {\n  color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n  color: #0f6674 !important;\n}\n\n.text-warning {\n  color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n  color: #ba8b00 !important;\n}\n\n.text-danger {\n  color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n  color: #a71d2a !important;\n}\n\n.text-light {\n  color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n  color: #cbd3da !important;\n}\n\n.text-dark {\n  color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n  color: #121416 !important;\n}\n\n.text-body {\n  color: #212529 !important;\n}\n\n.text-muted {\n  color: #6c757d !important;\n}\n\n.text-black-50 {\n  color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n.text-decoration-none {\n  text-decoration: none !important;\n}\n\n.text-break {\n  word-break: break-word !important;\n  word-wrap: break-word !important;\n}\n\n.text-reset {\n  color: inherit !important;\n}\n\n.visible {\n  visibility: visible !important;\n}\n\n.invisible {\n  visibility: hidden !important;\n}\n\n@media print {\n  *,\n  *::before,\n  *::after {\n    text-shadow: none !important;\n    box-shadow: none !important;\n  }\n  a:not(.btn) {\n    text-decoration: underline;\n  }\n  abbr[title]::after {\n    content: \" (\" attr(title) \")\";\n  }\n  pre {\n    white-space: pre-wrap !important;\n  }\n  pre,\n  blockquote {\n    border: 1px solid #adb5bd;\n    page-break-inside: avoid;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  @page {\n    size: a3;\n  }\n  body {\n    min-width: 992px !important;\n  }\n  .container {\n    min-width: 992px !important;\n  }\n  .navbar {\n    display: none;\n  }\n  .badge {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #dee2e6 !important;\n  }\n  .table-dark {\n    color: inherit;\n  }\n  .table-dark th,\n  .table-dark td,\n  .table-dark thead th,\n  .table-dark tbody + tbody {\n    border-color: #dee2e6;\n  }\n  .table .thead-dark th {\n    color: inherit;\n    border-color: #dee2e6;\n  }\n}\n\n@-webkit-keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@-webkit-keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@-webkit-keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@-webkit-keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@-webkit-keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.dark-mode :root {\n  --lightblue: #86bad8;\n  --navy: #002c59;\n  --olive: #74c8a3;\n  --lime: #67ffa9;\n  --fuchsia: #f672d8;\n  --maroon: #ed6c9b;\n  --blue: #3f6791;\n  --indigo: #6610f2;\n  --purple: #6f42c1;\n  --pink: #e83e8c;\n  --red: #e74c3c;\n  --orange: #fd7e14;\n  --yellow: #f39c12;\n  --green: #00bc8c;\n  --teal: #20c997;\n  --cyan: #3498db;\n  --white: #fff;\n  --gray: #6c757d;\n  --gray-dark: #343a40;\n  --primary: #3f6791;\n  --secondary: #6c757d;\n  --success: #00bc8c;\n  --info: #3498db;\n  --warning: #f39c12;\n  --danger: #e74c3c;\n  --light: #f8f9fa;\n  --dark: #343a40;\n}\n\n.animation__shake {\n  -webkit-animation: shake 1500ms;\n  animation: shake 1500ms;\n}\n\n.animation__wobble {\n  -webkit-animation: wobble 1500ms;\n  animation: wobble 1500ms;\n}\n\n.preloader {\n  display: -ms-flexbox;\n  display: flex;\n  background-color: #f4f6f9;\n  height: 100vh;\n  width: 100%;\n  transition: height 200ms linear;\n  position: fixed;\n  left: 0;\n  top: 0;\n  z-index: 9999;\n}\n\n.dark-mode .preloader {\n  background-color: #454d55 !important;\n  color: #fff;\n}\n\nhtml.scroll-smooth {\n  scroll-behavior: smooth;\n}\n\nhtml,\nbody,\n.wrapper {\n  min-height: 100%;\n}\n\n.wrapper {\n  position: relative;\n}\n\n.wrapper .content-wrapper {\n  min-height: calc(100vh - calc(3.5rem + 1px) - calc(3.5rem + 1px));\n}\n\n.layout-boxed .wrapper {\n  box-shadow: 0 0 10 rgba(0, 0, 0, 0.3);\n}\n\n.layout-boxed .wrapper, .layout-boxed .wrapper::before {\n  margin: 0 auto;\n  max-width: 1250px;\n  overflow: hidden;\n}\n\n.layout-boxed .wrapper .main-sidebar {\n  left: inherit;\n}\n\n@supports not (-webkit-touch-callout: none) {\n  .layout-fixed .wrapper .sidebar {\n    height: calc(100vh - (3.5rem + 1px));\n  }\n  .layout-fixed.text-sm .wrapper .sidebar {\n    height: calc(100vh - (2.93725rem + 1px));\n  }\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n  top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse .wrapper .brand-link {\n  height: calc(3.5rem + 1px);\n  width: 4.6rem;\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse .wrapper .brand-link.text-sm,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse .wrapper .brand-link.text-sm,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse .wrapper .brand-link.text-sm {\n  height: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse.text-sm .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse.text-sm .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse.text-sm .wrapper .brand-link {\n  height: calc(2.93725rem + 1px);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar {\n  margin-top: calc(calc(2.93725rem + 1px) / -1);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .control-sidebar {\n  top: 0;\n}\n\n.layout-navbar-fixed .wrapper a.anchor {\n  display: block;\n  position: relative;\n  top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n}\n\n.layout-navbar-fixed .wrapper .main-sidebar:hover .brand-link {\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n}\n\n.layout-navbar-fixed .wrapper .brand-link {\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n  z-index: 1035;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-lightblue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-lightblue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-navy .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-navy .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-olive .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-olive .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-lime .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-lime .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-fuchsia .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-fuchsia .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-maroon .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-maroon .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-blue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-blue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-indigo .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-indigo .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-purple .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-purple .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-pink .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-pink .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-red .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-red .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-orange .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-orange .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-yellow .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-yellow .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-green .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-green .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-teal .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-teal .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-cyan .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-cyan .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-white .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-white .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-gray .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-gray .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-gray-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-gray-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .main-header.border-bottom-0 ~ .content-wrapper {\n  margin-top: 3.5rem;\n}\n\n.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header {\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1033;\n}\n\n.layout-navbar-fixed.text-sm .wrapper .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-not-fixed .wrapper .brand-link {\n  position: static;\n}\n\n.layout-navbar-not-fixed .wrapper .sidebar,\n.layout-navbar-not-fixed .wrapper .content-wrapper {\n  margin-top: 0;\n}\n\n.layout-navbar-not-fixed .wrapper .main-header {\n  position: static;\n}\n\n.layout-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: 0;\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n  top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n.layout-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n.layout-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .control-sidebar {\n  top: 0;\n}\n\n.layout-navbar-fixed .wrapper a.anchor {\n  display: block;\n  position: relative;\n  top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n}\n\n.layout-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n  height: calc(3.5rem + 1px);\n  transition: width 0.3s ease-in-out;\n  width: 4.6rem;\n}\n\n.text-sm .layout-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n  height: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n}\n\n.layout-navbar-fixed .wrapper .brand-link {\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n  z-index: 1035;\n}\n\n.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n.layout-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header {\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1037;\n}\n\n.layout-navbar-fixed.text-sm .wrapper .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar {\n  margin-top: calc(calc(2.93725rem + 1px) / -1);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-not-fixed .wrapper .brand-link {\n  position: static;\n}\n\n.layout-navbar-not-fixed .wrapper .sidebar,\n.layout-navbar-not-fixed .wrapper .content-wrapper {\n  margin-top: 0;\n}\n\n.layout-navbar-not-fixed .wrapper .main-header {\n  position: static;\n}\n\n.layout-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: 0;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-sm-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-sm-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-sm-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-sm-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-sm-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-sm-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-sm-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-sm-navbar-not-fixed .wrapper .sidebar,\n  .layout-sm-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-sm-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-sm-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-md-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-md-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-md-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-md-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-md-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-md-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-md-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-md-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-md-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-md-navbar-not-fixed .wrapper .sidebar,\n  .layout-md-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-md-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-md-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-lg-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-lg-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-lg-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-lg-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-lg-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-lg-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-lg-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-lg-navbar-not-fixed .wrapper .sidebar,\n  .layout-lg-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-lg-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-lg-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-xl-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-xl-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-xl-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-xl-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-xl-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-xl-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-xl-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-xl-navbar-not-fixed .wrapper .sidebar,\n  .layout-xl-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-xl-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-xl-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n.layout-footer-fixed .wrapper .control-sidebar {\n  bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .main-footer {\n  bottom: 0;\n  left: 0;\n  position: fixed;\n  right: 0;\n  z-index: 1032;\n}\n\n.layout-footer-not-fixed .wrapper .main-footer {\n  position: static;\n}\n\n.layout-footer-not-fixed .wrapper .content-wrapper {\n  margin-bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .control-sidebar {\n  bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .main-footer {\n  bottom: 0;\n  left: 0;\n  position: fixed;\n  right: 0;\n  z-index: 1032;\n}\n\n.layout-footer-fixed .wrapper .content-wrapper {\n  padding-bottom: calc(3.5rem + 1px);\n}\n\n.layout-footer-not-fixed .wrapper .main-footer {\n  position: static;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-sm-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-sm-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-sm-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-md-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-md-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-md-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-lg-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-lg-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-lg-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-xl-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-xl-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-xl-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n.layout-top-nav .wrapper {\n  margin-left: 0;\n}\n\n.layout-top-nav .wrapper .main-header .brand-image {\n  margin-top: -.5rem;\n  margin-right: .2rem;\n  height: 33px;\n}\n\n.layout-top-nav .wrapper .main-sidebar {\n  bottom: inherit;\n  height: inherit;\n}\n\n.layout-top-nav .wrapper .content-wrapper,\n.layout-top-nav .wrapper .main-header,\n.layout-top-nav .wrapper .main-footer {\n  margin-left: 0;\n}\n\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .content-wrapper, body.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .content-wrapper::before,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-footer,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-footer::before,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-header,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-header::before {\n  margin-left: 0;\n}\n\n@media (min-width: 768px) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper, .sidebar-collapse\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer, .sidebar-collapse\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (max-width: 991.98px) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse .sidebar-mini-md .content-wrapper, .sidebar-collapse\n  .sidebar-mini-md .main-footer, .sidebar-collapse\n  .sidebar-mini-md .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse .sidebar-mini-xs .content-wrapper, .sidebar-collapse\n  .sidebar-mini-xs .main-footer, .sidebar-collapse\n  .sidebar-mini-xs .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n.content-wrapper {\n  background-color: #f4f6f9;\n}\n\n.content-wrapper > .content {\n  padding: 0 0.5rem;\n}\n\n.main-sidebar, .main-sidebar::before {\n  transition: margin-left 0.3s ease-in-out, width 0.3s ease-in-out;\n  width: 250px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .main-sidebar, .main-sidebar::before {\n    transition: none;\n  }\n}\n\n.sidebar-collapse:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-sidebar, .sidebar-collapse:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-sidebar::before {\n  box-shadow: none !important;\n}\n\n.sidebar-collapse .main-sidebar, .sidebar-collapse .main-sidebar::before {\n  margin-left: -250px;\n}\n\n.sidebar-collapse .main-sidebar .nav-sidebar.nav-child-indent .nav-treeview {\n  padding: 0;\n}\n\n@media (max-width: 767.98px) {\n  .main-sidebar, .main-sidebar::before {\n    box-shadow: none !important;\n    margin-left: -250px;\n  }\n  .sidebar-open .main-sidebar, .sidebar-open .main-sidebar::before {\n    margin-left: 0;\n  }\n}\n\nbody:not(.layout-fixed) .main-sidebar {\n  height: inherit;\n  min-height: 100%;\n  position: absolute;\n  top: 0;\n}\n\nbody:not(.layout-fixed) .main-sidebar .sidebar {\n  overflow-y: auto;\n}\n\n.layout-fixed .brand-link {\n  width: 250px;\n}\n\n.layout-fixed .main-sidebar {\n  bottom: 0;\n  float: none;\n  left: 0;\n  position: fixed;\n  top: 0;\n}\n\n.layout-fixed .control-sidebar {\n  bottom: 0;\n  float: none;\n  position: fixed;\n  top: 0;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content {\n  height: calc(100vh - calc(3.5rem + 1px));\n  overflow-y: auto;\n  scrollbar-width: thin;\n  scrollbar-color: #a9a9a9 transparent;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar {\n  width: .5rem;\n  height: .5rem;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-thumb {\n  background-color: #a9a9a9;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-track {\n  background-color: transparent;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n@supports (-webkit-touch-callout: none) {\n  .layout-fixed .main-sidebar {\n    height: inherit;\n  }\n}\n\n.main-footer {\n  background-color: #fff;\n  border-top: 1px solid #dee2e6;\n  color: #869099;\n  padding: 1rem;\n}\n\n.text-sm .main-footer, .main-footer.text-sm {\n  padding: 0.812rem;\n}\n\n.content-header {\n  padding: 15px 0.5rem;\n}\n\n.text-sm .content-header {\n  padding: 10px 0.5rem;\n}\n\n.content-header h1 {\n  font-size: 1.8rem;\n  margin: 0;\n}\n\n.text-sm .content-header h1 {\n  font-size: 1.5rem;\n}\n\n.content-header .breadcrumb {\n  background-color: transparent;\n  line-height: 1.8rem;\n  margin-bottom: 0;\n  padding: 0;\n}\n\n.text-sm .content-header .breadcrumb {\n  line-height: 1.5rem;\n}\n\n.hold-transition .content-wrapper,\n.hold-transition .main-header,\n.hold-transition .main-sidebar,\n.hold-transition .main-sidebar *,\n.hold-transition .control-sidebar,\n.hold-transition .control-sidebar *,\n.hold-transition .main-footer {\n  transition: none !important;\n  -webkit-animation-duration: 0s !important;\n  animation-duration: 0s !important;\n}\n\n.dark-mode {\n  background-color: #454d55 !important;\n  color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-sm-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-md-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-lg-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #343a40;\n  }\n  .layout-xl-navbar-fixed .dark-mode .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n    background-color: #fff;\n  }\n}\n\n.dark-mode .breadcrumb-item.active,\n.dark-mode .breadcrumb-item + .breadcrumb-item::before {\n  color: #adb5bd;\n}\n\n.dark-mode .main-footer {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.dark-mode .content-wrapper {\n  background-color: #454d55;\n  color: #fff;\n}\n\n.dark-mode .content-wrapper .content-header {\n  color: #fff;\n}\n\n.main-header {\n  border-bottom: 1px solid #dee2e6;\n  z-index: 1034;\n}\n\n.main-header .nav-link {\n  height: 2.5rem;\n  position: relative;\n}\n\n.text-sm .main-header .nav-link, .main-header.text-sm .nav-link {\n  height: 1.93725rem;\n  padding: 0.35rem 1rem;\n}\n\n.text-sm .main-header .nav-link > .fa,\n.text-sm .main-header .nav-link > .fas,\n.text-sm .main-header .nav-link > .far,\n.text-sm .main-header .nav-link > .fab,\n.text-sm .main-header .nav-link > .fal,\n.text-sm .main-header .nav-link > .fad,\n.text-sm .main-header .nav-link > .svg-inline--fa,\n.text-sm .main-header .nav-link > .ion, .main-header.text-sm .nav-link > .fa,\n.main-header.text-sm .nav-link > .fas,\n.main-header.text-sm .nav-link > .far,\n.main-header.text-sm .nav-link > .fab,\n.main-header.text-sm .nav-link > .fal,\n.main-header.text-sm .nav-link > .fad,\n.main-header.text-sm .nav-link > .svg-inline--fa,\n.main-header.text-sm .nav-link > .ion {\n  font-size: 0.875rem;\n}\n\n.main-header .navbar-nav .nav-item {\n  margin: 0;\n}\n\n.main-header .navbar-nav[class*=\"-right\"] .dropdown-menu {\n  left: auto;\n  margin-top: -3px;\n  right: 0;\n}\n\n@media (max-width: 575.98px) {\n  .main-header .navbar-nav[class*=\"-right\"] .dropdown-menu {\n    left: 0;\n    right: auto;\n  }\n}\n\n.main-header.dropdown-legacy .dropdown-menu {\n  top: 3rem;\n  margin-top: 0;\n}\n\n.navbar-img {\n  height: calc(calc(3.5rem + 1px) * .5);\n  width: auto;\n}\n\n.navbar-badge {\n  font-size: .6rem;\n  font-weight: 300;\n  padding: 2px 4px;\n  position: absolute;\n  right: 5px;\n  top: 9px;\n}\n\n.btn-navbar {\n  background-color: transparent;\n  border-left-width: 0;\n}\n\n.form-control-navbar {\n  border-right-width: 0;\n}\n\n.form-control-navbar + .input-group-append {\n  margin-left: 0;\n}\n\n.form-control-navbar,\n.btn-navbar {\n  transition: none;\n}\n\n.navbar-dark .form-control-navbar,\n.navbar-dark .btn-navbar {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar:focus,\n.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #495057;\n  border-color: #6c757d !important;\n  color: #ced4da;\n}\n\n.navbar-light .form-control-navbar,\n.navbar-light .btn-navbar {\n  background-color: #dadfe4;\n  border-color: #ced4da;\n}\n\n.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar:focus,\n.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d3d9df;\n  border-color: #c7ced5 !important;\n  color: #ced4da;\n}\n\n.navbar-light .navbar-search-block .form-control-navbar:focus,\n.navbar-light .navbar-search-block .form-control-navbar:focus + .input-group-append .btn-navbar {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-search-block {\n  position: absolute;\n  padding: 0 1rem;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 10;\n  display: none;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  background-color: initial;\n}\n\n.navbar-search-block.navbar-search-open {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.navbar-search-block .input-group {\n  width: 100%;\n}\n\n.brand-link {\n  display: block;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  padding: 0.8125rem 0.5rem;\n  transition: width 0.3s ease-in-out;\n  white-space: nowrap;\n}\n\n.brand-link:hover {\n  color: #fff;\n  text-decoration: none;\n}\n\n.text-sm .brand-link {\n  font-size: inherit;\n}\n\n[class*=\"sidebar-dark\"] .brand-link {\n  border-bottom: 1px solid #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .brand-link,\n[class*=\"sidebar-dark\"] .brand-link .pushmenu {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n[class*=\"sidebar-dark\"] .brand-link:hover,\n[class*=\"sidebar-dark\"] .brand-link .pushmenu:hover {\n  color: #fff;\n}\n\n[class*=\"sidebar-light\"] .brand-link {\n  border-bottom: 1px solid #dee2e6;\n}\n\n[class*=\"sidebar-light\"] .brand-link,\n[class*=\"sidebar-light\"] .brand-link .pushmenu {\n  color: rgba(0, 0, 0, 0.8);\n}\n\n[class*=\"sidebar-light\"] .brand-link:hover,\n[class*=\"sidebar-light\"] .brand-link .pushmenu:hover {\n  color: #000;\n}\n\n.brand-link .pushmenu {\n  margin-right: 0.5rem;\n  font-size: 1rem;\n}\n\n.brand-link .brand-link {\n  padding: 0;\n  border-bottom: none;\n}\n\n.brand-link .brand-image {\n  float: left;\n  line-height: .8;\n  margin-left: .8rem;\n  margin-right: .5rem;\n  margin-top: -3px;\n  max-height: 33px;\n  width: auto;\n}\n\n.brand-link .brand-image-xs {\n  float: left;\n  line-height: .8;\n  margin-top: -.1rem;\n  max-height: 33px;\n  width: auto;\n}\n\n.brand-link .brand-image-xl {\n  line-height: .8;\n  max-height: 40px;\n  width: auto;\n}\n\n.brand-link .brand-image-xl.single {\n  margin-top: -.3rem;\n}\n\n.brand-link.text-sm .brand-image,\n.text-sm .brand-link .brand-image {\n  height: 29px;\n  margin-bottom: -.25rem;\n  margin-left: .95rem;\n  margin-top: -.25rem;\n}\n\n.brand-link.text-sm .brand-image-xs,\n.text-sm .brand-link .brand-image-xs {\n  margin-top: -.2rem;\n  max-height: 29px;\n}\n\n.brand-link.text-sm .brand-image-xl,\n.text-sm .brand-link .brand-image-xl {\n  margin-top: -.225rem;\n  max-height: 38px;\n}\n\n.main-sidebar {\n  height: 100vh;\n  overflow-y: hidden;\n  z-index: 1038;\n}\n\n.main-sidebar a:-moz-focusring {\n  border: 0;\n  outline: none;\n}\n\n.sidebar {\n  height: calc(100% - (3.5rem + 1px));\n  overflow-x: hidden;\n  overflow-y: initial;\n  padding-bottom: 0;\n  padding-left: 0.5rem;\n  padding-right: 0.5rem;\n  padding-top: 0;\n  scrollbar-color: #a9a9a9 transparent;\n  scrollbar-width: none;\n}\n\n.sidebar::-webkit-scrollbar-thumb {\n  background-color: #a9a9a9;\n}\n\n.sidebar::-webkit-scrollbar-track {\n  background-color: transparent;\n}\n\n.sidebar::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n.sidebar::-webkit-scrollbar {\n  width: 0;\n}\n\n.sidebar:hover {\n  scrollbar-width: thin;\n}\n\n.sidebar:hover::-webkit-scrollbar {\n  width: .5rem;\n  height: .5rem;\n}\n\n.brand-link.border-bottom-0 ~ .sidebar {\n  height: calc(100% - 3.5rem);\n}\n\n.user-panel {\n  position: relative;\n}\n\n[class*=\"sidebar-dark\"] .user-panel {\n  border-bottom: 1px solid #4f5962;\n}\n\n[class*=\"sidebar-light\"] .user-panel {\n  border-bottom: 1px solid #dee2e6;\n}\n\n.user-panel,\n.user-panel .info {\n  overflow: hidden;\n  white-space: nowrap;\n}\n\n.user-panel .image {\n  display: inline-block;\n  padding-left: 0.8rem;\n}\n\n.user-panel img {\n  height: auto;\n  width: 2.1rem;\n}\n\n.user-panel .info {\n  display: inline-block;\n  padding: 5px 5px 5px 10px;\n}\n\n.user-panel .status,\n.user-panel .dropdown-menu {\n  font-size: 0.875rem;\n}\n\n.nav-sidebar .nav-item > .nav-link {\n  margin-bottom: .2rem;\n}\n\n.nav-sidebar .nav-item > .nav-link .right {\n  transition: -webkit-transform ease-in-out 0.3s;\n  transition: transform ease-in-out 0.3s;\n  transition: transform ease-in-out 0.3s, -webkit-transform ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .nav-sidebar .nav-item > .nav-link .right {\n    transition: none;\n  }\n}\n\n.nav-sidebar .nav-link > .right,\n.nav-sidebar .nav-link > p > .right {\n  position: absolute;\n  right: 1rem;\n  top: .7rem;\n}\n\n.nav-sidebar .nav-link > .right i,\n.nav-sidebar .nav-link > .right span,\n.nav-sidebar .nav-link > p > .right i,\n.nav-sidebar .nav-link > p > .right span {\n  margin-left: .5rem;\n}\n\n.nav-sidebar .nav-link > .right:nth-child(2),\n.nav-sidebar .nav-link > p > .right:nth-child(2) {\n  right: 2.2rem;\n}\n\n.nav-sidebar .menu-open > .nav-treeview {\n  display: block;\n}\n\n.nav-sidebar .menu-open > .nav-link svg.right,\n.nav-sidebar .menu-open > .nav-link i.right,\n.nav-sidebar .menu-is-opening > .nav-link svg.right,\n.nav-sidebar .menu-is-opening > .nav-link i.right {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n.nav-sidebar > .nav-item {\n  margin-bottom: 0;\n}\n\n.nav-sidebar > .nav-item .nav-icon {\n  margin-left: .05rem;\n  font-size: 1.2rem;\n  margin-right: .2rem;\n  text-align: center;\n  width: 1.6rem;\n}\n\n.nav-sidebar > .nav-item .nav-icon.fa, .nav-sidebar > .nav-item .nav-icon.fas, .nav-sidebar > .nav-item .nav-icon.far, .nav-sidebar > .nav-item .nav-icon.fab, .nav-sidebar > .nav-item .nav-icon.fal, .nav-sidebar > .nav-item .nav-icon.fad, .nav-sidebar > .nav-item .nav-icon.svg-inline--fa, .nav-sidebar > .nav-item .nav-icon.ion {\n  font-size: 1.1rem;\n}\n\n.nav-sidebar > .nav-item .float-right {\n  margin-top: 3px;\n}\n\n.nav-sidebar .nav-treeview {\n  display: none;\n  list-style: none;\n  padding: 0;\n}\n\n.nav-sidebar .nav-treeview > .nav-item > .nav-link > .nav-icon {\n  width: 1.6rem;\n}\n\n.nav-sidebar.nav-child-indent .nav-treeview {\n  transition: padding 0.3s ease-in-out;\n  padding-left: 1rem;\n}\n\n.text-sm .nav-sidebar.nav-child-indent .nav-treeview {\n  padding-left: .5rem;\n}\n\n.nav-sidebar.nav-child-indent.nav-legacy .nav-treeview .nav-treeview {\n  padding-left: 2rem;\n  margin-left: -1rem;\n}\n\n.text-sm .nav-sidebar.nav-child-indent.nav-legacy .nav-treeview .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.nav-sidebar .nav-header {\n  font-size: .9rem;\n  padding: 0.5rem 0.75rem;\n}\n\n.nav-sidebar .nav-link p {\n  display: inline;\n  margin: 0;\n  white-space: normal;\n}\n\n.sidebar-is-opening .sidebar .nav-sidebar .nav-link p {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n#sidebar-overlay {\n  background-color: rgba(0, 0, 0, 0.1);\n  bottom: 0;\n  display: none;\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1037;\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-open #sidebar-overlay {\n    display: block;\n  }\n}\n\n[class*=\"sidebar-light-\"] {\n  background-color: #fff;\n}\n\n[class*=\"sidebar-light-\"] .user-panel a:hover {\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .status {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .status:hover, [class*=\"sidebar-light-\"] .user-panel .status:focus, [class*=\"sidebar-light-\"] .user-panel .status:active {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .dropdown-menu {\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .user-panel .dropdown-item {\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link:active, [class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link:focus {\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item.menu-open > .nav-link,\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item:hover > .nav-link {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link.active {\n  color: #000;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-treeview {\n  background-color: transparent;\n}\n\n[class*=\"sidebar-light-\"] .nav-header {\n  background-color: inherit;\n  color: #292d32;\n}\n\n[class*=\"sidebar-light-\"] .sidebar a {\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .sidebar a:hover {\n  text-decoration: none;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link {\n  color: #777;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:hover, [class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:focus {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #000;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link.active:hover {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview .nav-treeview {\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link, [class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link.active {\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-dark-\"] {\n  background-color: #343a40;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel a:hover {\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .status {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .status:hover, [class*=\"sidebar-dark-\"] .user-panel .status:focus, [class*=\"sidebar-dark-\"] .user-panel .status:active {\n  background-color: rgba(247, 247, 247, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .dropdown-menu {\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);\n  border-color: rgba(242, 242, 242, 0.1);\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .dropdown-item {\n  color: #212529;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link:active {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item.menu-open > .nav-link,\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item:hover > .nav-link,\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link:focus {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link.active {\n  color: #fff;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-treeview {\n  background-color: transparent;\n}\n\n[class*=\"sidebar-dark-\"] .nav-header {\n  background-color: inherit;\n  color: #d0d4db;\n}\n\n[class*=\"sidebar-dark-\"] .sidebar a {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .sidebar a:hover, [class*=\"sidebar-dark-\"] .sidebar a:focus {\n  text-decoration: none;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link:hover, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link:focus {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active:hover, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active:focus {\n  background-color: rgba(255, 255, 255, 0.9);\n  color: #343a40;\n}\n\n[class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview .nav-treeview {\n  border-color: rgba(255, 255, 255, 0.9);\n}\n\n[class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link, [class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link.active {\n  border-color: rgba(255, 255, 255, 0.9);\n}\n\n.sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.sidebar-dark-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #007bff;\n}\n\n.sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.sidebar-dark-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.sidebar-dark-success .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-success .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.sidebar-dark-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #28a745;\n}\n\n.sidebar-dark-info .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-info .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.sidebar-dark-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #17a2b8;\n}\n\n.sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-warning .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ffc107;\n}\n\n.sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-danger .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.sidebar-dark-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #dc3545;\n}\n\n.sidebar-dark-light .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-light .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f8f9fa;\n}\n\n.sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.sidebar-dark-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.sidebar-dark-lightblue .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-lightblue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.sidebar-dark-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3c8dbc;\n}\n\n.sidebar-dark-navy .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-navy .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.sidebar-dark-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #001f3f;\n}\n\n.sidebar-dark-olive .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-olive .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.sidebar-dark-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3d9970;\n}\n\n.sidebar-dark-lime .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-lime .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #01ff70;\n}\n\n.sidebar-dark-fuchsia .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-fuchsia .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.sidebar-dark-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f012be;\n}\n\n.sidebar-dark-maroon .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-maroon .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.sidebar-dark-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #d81b60;\n}\n\n.sidebar-dark-blue .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-blue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.sidebar-dark-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #007bff;\n}\n\n.sidebar-dark-indigo .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-indigo .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.sidebar-dark-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6610f2;\n}\n\n.sidebar-dark-purple .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-purple .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.sidebar-dark-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6f42c1;\n}\n\n.sidebar-dark-pink .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-pink .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.sidebar-dark-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e83e8c;\n}\n\n.sidebar-dark-red .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-red .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.sidebar-dark-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #dc3545;\n}\n\n.sidebar-dark-orange .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-orange .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fd7e14;\n}\n\n.sidebar-dark-yellow .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-yellow .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ffc107;\n}\n\n.sidebar-dark-green .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-green .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.sidebar-dark-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #28a745;\n}\n\n.sidebar-dark-teal .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-teal .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.sidebar-dark-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #20c997;\n}\n\n.sidebar-dark-cyan .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-cyan .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.sidebar-dark-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #17a2b8;\n}\n\n.sidebar-dark-white .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-white .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fff;\n}\n\n.sidebar-dark-gray .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-gray .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.sidebar-dark-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.sidebar-dark-gray-dark .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-gray-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.sidebar-dark-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.nav-flat {\n  margin: -0.25rem -0.5rem 0;\n}\n\n.nav-flat .nav-item > .nav-link {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.nav-flat .nav-item > .nav-link > .nav-icon {\n  margin-left: .55rem;\n}\n\n.nav-flat:not(.nav-child-indent) .nav-treeview .nav-item > .nav-link > .nav-icon {\n  margin-left: .4rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview {\n  padding-left: 0;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-icon {\n  margin-left: .85rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview {\n  border-left: .2rem solid;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.15rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.45rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.75rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 2.05rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-icon {\n  margin-left: .55rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-link {\n  padding-left: calc(1rem - .2rem);\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-icon {\n  margin-left: .35rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: .15rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: -.15rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: -.35rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon {\n  margin-left: .4rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon {\n  margin-left: .85rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.15rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.45rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.75rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 2.05rem;\n}\n\n.nav-flat .nav-icon {\n  transition: margin-left ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .nav-flat .nav-icon {\n    transition: none;\n  }\n}\n\n.nav-flat .nav-treeview .nav-icon {\n  margin-left: -.2rem;\n}\n\n.nav-flat.nav-sidebar > .nav-item .nav-treeview,\n.nav-flat.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n.nav-flat.nav-sidebar > .nav-item .nav-treeview .nav-item > .nav-link,\n.nav-flat.nav-sidebar > .nav-item > .nav-treeview .nav-item > .nav-link {\n  border-left: .2rem solid;\n}\n\n.nav-legacy {\n  margin: -0.25rem -0.5rem 0;\n}\n\n.nav-legacy.nav-sidebar .nav-item > .nav-link {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.nav-legacy.nav-sidebar .nav-item > .nav-link > .nav-icon {\n  margin-left: .55rem;\n}\n\n.text-sm .nav-legacy.nav-sidebar .nav-item > .nav-link > .nav-icon {\n  margin-left: .75rem;\n}\n\n.nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  background-color: inherit;\n  border-left: 3px solid transparent;\n  box-shadow: none;\n}\n\n.nav-legacy.nav-sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.55rem - 3px);\n}\n\n.text-sm .nav-legacy.nav-sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n.text-sm .nav-legacy.nav-sidebar.nav-flat .nav-treeview .nav-item > .nav-link > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n.sidebar-mini .nav-legacy > .nav-item .nav-link .nav-icon,\n.sidebar-mini-md .nav-legacy > .nav-item .nav-link .nav-icon,\n.sidebar-mini-xs .nav-legacy > .nav-item .nav-link .nav-icon {\n  transition: margin-left ease-in-out 0.3s;\n  margin-left: .6rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar-mini .nav-legacy > .nav-item .nav-link .nav-icon,\n  .sidebar-mini-md .nav-legacy > .nav-item .nav-link .nav-icon,\n  .sidebar-mini-xs .nav-legacy > .nav-item .nav-link .nav-icon {\n    transition: none;\n  }\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview {\n  padding-left: 1rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 2rem;\n  margin-left: -1rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview {\n  padding-left: .5rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon {\n  margin-left: .55rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: .36rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 0;\n  margin-left: 0;\n}\n\n.sidebar-mini.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-md.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-xs.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon {\n  margin-left: .75rem;\n}\n\n.sidebar-mini.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-md.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-xs.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item .nav-treeview,\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  color: #fff;\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:focus, [class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: transparent;\n  color: #fff;\n}\n\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item .nav-treeview,\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  color: #000;\n}\n\n[class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:focus, [class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: transparent;\n  color: #000;\n}\n\n.nav-collapse-hide-child .menu-open > .nav-treeview {\n  max-height: -webkit-min-content;\n  max-height: -moz-min-content;\n  max-height: min-content;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.sidebar-collapse .sidebar:not(:hover) .nav-collapse-hide-child .menu-open > .nav-treeview {\n  max-height: 0;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.main-sidebar.sidebar-focused .nav-collapse-hide-child .sidebar-mini.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover .nav-collapse-hide-child .sidebar-mini.sidebar-collapse .menu-open > .nav-treeview, .main-sidebar.sidebar-focused\n.nav-collapse-hide-child .sidebar-mini-md.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover\n.nav-collapse-hide-child .sidebar-mini-md.sidebar-collapse .menu-open > .nav-treeview, .main-sidebar.sidebar-focused\n.nav-collapse-hide-child .sidebar-mini-xs.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover\n.nav-collapse-hide-child .sidebar-mini-xs.sidebar-collapse .menu-open > .nav-treeview {\n  max-height: -webkit-min-content;\n  max-height: -moz-min-content;\n  max-height: min-content;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.nav-compact .nav-link,\n.nav-compact .nav-header {\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n}\n\n.nav-compact .nav-header:not(:first-of-type) {\n  padding-top: 0.75rem;\n  padding-bottom: 0.25rem;\n}\n\n.nav-compact .nav-link > .right,\n.nav-compact .nav-link > p > .right {\n  top: .465rem;\n}\n\n.text-sm .nav-compact .nav-link > .right,\n.text-sm .nav-compact .nav-link > p > .right {\n  top: .7rem;\n}\n\n[class*=\"sidebar-dark\"] .form-control-sidebar,\n[class*=\"sidebar-dark\"] .btn-sidebar {\n  background-color: #3f474e;\n  border: 1px solid #56606a;\n  color: white;\n}\n\n[class*=\"sidebar-dark\"] .form-control-sidebar:focus,\n[class*=\"sidebar-dark\"] .btn-sidebar:focus {\n  border: 1px solid #7a8793;\n}\n\n[class*=\"sidebar-dark\"] .btn-sidebar:hover {\n  background-color: #454d55;\n}\n\n[class*=\"sidebar-dark\"] .btn-sidebar:focus {\n  background-color: #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item {\n  background-color: #454d55;\n  border-color: #56606a;\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item:hover {\n  background-color: #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item:focus {\n  background-color: #515a63;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item .search-path {\n  color: #adb5bd;\n}\n\n[class*=\"sidebar-light\"] .form-control-sidebar,\n[class*=\"sidebar-light\"] .btn-sidebar {\n  background-color: #f2f2f2;\n  border: 1px solid #d9d9d9;\n  color: #1f2d3d;\n}\n\n[class*=\"sidebar-light\"] .form-control-sidebar:focus,\n[class*=\"sidebar-light\"] .btn-sidebar:focus {\n  border: 1px solid #b3b3b3;\n}\n\n[class*=\"sidebar-light\"] .btn-sidebar:hover {\n  background-color: #ececec;\n}\n\n[class*=\"sidebar-light\"] .btn-sidebar:focus {\n  background-color: #e6e6e6;\n}\n\n[class*=\"sidebar-light\"] .list-group-item {\n  border-color: #d9d9d9;\n}\n\n[class*=\"sidebar-light\"] .list-group-item:hover {\n  background-color: #ececec;\n}\n\n[class*=\"sidebar-light\"] .list-group-item:focus {\n  background-color: #e6e6e6;\n}\n\n[class*=\"sidebar-light\"] .list-group-item .search-path {\n  color: #6c757d;\n}\n\n.sidebar .form-inline .input-group {\n  width: 100%;\n  -ms-flex-wrap: nowrap;\n  flex-wrap: nowrap;\n}\n\n.sidebar nav .form-inline {\n  margin-bottom: .2rem;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs).sidebar-collapse .main-sidebar {\n  margin-left: 0;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .content-wrapper,\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-header,\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-footer {\n  z-index: 9999;\n  position: relative;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .control-sidebar {\n  z-index: 9999;\n}\n\n.sidebar-collapse .form-control-sidebar,\n.sidebar-collapse .form-control-sidebar ~ .input-group-append,\n.sidebar-collapse .sidebar-search-results {\n  display: none;\n}\n\n[data-widget=\"sidebar-search\"] input[type=\"search\"]::-ms-clear, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-ms-reveal {\n  display: none;\n  width: 0;\n  height: 0;\n}\n\n[data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-cancel-button, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-decoration, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-results-button, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-results-decoration {\n  display: none;\n}\n\n.sidebar-search-results {\n  position: relative;\n  display: none;\n  width: 100%;\n}\n\n.sidebar-search-open .sidebar-search-results {\n  display: inline-block;\n}\n\n.sidebar-search-results .search-title {\n  margin-bottom: -.1rem;\n}\n\n.sidebar-search-results .list-group {\n  position: absolute;\n  width: 100%;\n  z-index: 1039;\n}\n\n.sidebar-search-results .list-group > .list-group-item {\n  padding: 0.375rem 0.75rem;\n}\n\n.sidebar-search-results .list-group > .list-group-item:-moz-focusring {\n  margin-top: 0;\n  border-left: 1px solid transparent;\n  border-top: 0;\n  border-bottom: 1px solid transparent;\n}\n\n.sidebar-search-results .list-group > .list-group-item:first-child {\n  margin-top: 0;\n  border-top: 0;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.sidebar-search-results .search-path {\n  font-size: 80%;\n}\n\n.sidebar-search-open .btn,\n.sidebar-search-open .form-control {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n[class*=\"sidebar-dark\"] .sidebar-custom {\n  border-top: 1px solid #4f5962;\n}\n\n[class*=\"sidebar-light\"] .sidebar-custom {\n  border-top: 1px solid #dee2e6;\n}\n\n.layout-fixed.sidebar-collapse .hide-on-collapse {\n  display: none;\n}\n\n.layout-fixed.sidebar-collapse:hover .hide-on-collapse {\n  display: block;\n}\n\n.layout-fixed.text-sm .main-sidebar-custom .sidebar {\n  height: calc(100% - ((2.93725rem + 3.8rem) + 1px));\n}\n\n.layout-fixed.text-sm .main-sidebar-custom .sidebar-custom {\n  height: 3.8rem;\n  padding: 0.85rem 0.5rem;\n}\n\n.layout-fixed .main-sidebar-custom {\n  height: -webkit-fill-available;\n  height: -moz-available;\n  height: -ms-stretch;\n  height: stretch;\n}\n\n.layout-fixed .main-sidebar-custom .sidebar {\n  height: calc(100% - ((3.5rem + 4rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom .sidebar-custom {\n  height: 4rem;\n  padding: 0.85rem 0.5rem;\n}\n\n.layout-fixed .main-sidebar-custom-lg .sidebar {\n  height: calc(100% - ((3.5rem + 6rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom-lg .sidebar-custom {\n  height: 6rem;\n}\n\n.layout-fixed .main-sidebar-custom-xl .sidebar {\n  height: calc(100% - ((3.5rem + 8rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom-xl .sidebar-custom {\n  height: 8rem;\n}\n\n.layout-fixed .main-sidebar-custom .pos-right,\n.layout-fixed .main-sidebar-custom-lg .pos-right,\n.layout-fixed .main-sidebar-custom-xl .pos-right {\n  position: absolute;\n  right: .5rem;\n}\n\n.sidebar-hidden .main-sidebar,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .main-sidebar {\n  display: none !important;\n}\n\n.sidebar-hidden .content-wrapper,\n.sidebar-hidden .main-header,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .content-wrapper,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .main-header {\n  margin-left: 0 !important;\n}\n\n.dark-mode .sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3f6791;\n}\n\n.dark-mode .sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.dark-mode .sidebar-dark-success .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-success .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #00bc8c;\n}\n\n.dark-mode .sidebar-dark-info .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-info .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3498db;\n}\n\n.dark-mode .sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-warning .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f39c12;\n}\n\n.dark-mode .sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-danger .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e74c3c;\n}\n\n.dark-mode .sidebar-dark-light .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-light .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f8f9fa;\n}\n\n.dark-mode .sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.dark-mode .sidebar-dark-lightblue .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lightblue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #86bad8;\n}\n\n.dark-mode .sidebar-dark-navy .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-navy .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #002c59;\n}\n\n.dark-mode .sidebar-dark-olive .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-olive .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #74c8a3;\n}\n\n.dark-mode .sidebar-dark-lime .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lime .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #67ffa9;\n}\n\n.dark-mode .sidebar-dark-fuchsia .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-fuchsia .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f672d8;\n}\n\n.dark-mode .sidebar-dark-maroon .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-maroon .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ed6c9b;\n}\n\n.dark-mode .sidebar-dark-blue .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-blue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3f6791;\n}\n\n.dark-mode .sidebar-dark-indigo .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-indigo .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6610f2;\n}\n\n.dark-mode .sidebar-dark-purple .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-purple .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6f42c1;\n}\n\n.dark-mode .sidebar-dark-pink .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-pink .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e83e8c;\n}\n\n.dark-mode .sidebar-dark-red .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-red .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e74c3c;\n}\n\n.dark-mode .sidebar-dark-orange .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-orange .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fd7e14;\n}\n\n.dark-mode .sidebar-dark-yellow .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-yellow .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f39c12;\n}\n\n.dark-mode .sidebar-dark-green .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-green .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #00bc8c;\n}\n\n.dark-mode .sidebar-dark-teal .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-teal .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #20c997;\n}\n\n.dark-mode .sidebar-dark-cyan .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-cyan .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3498db;\n}\n\n.dark-mode .sidebar-dark-white .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-white .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .sidebar-dark-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fff;\n}\n\n.dark-mode .sidebar-dark-gray .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.dark-mode .sidebar-dark-gray-dark .nav-sidebar > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .sidebar-dark-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.dark-mode .sidebar-light-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.dark-mode [class*=\"sidebar-light-\"] .sidebar a {\n  color: #343a40;\n}\n\n.dark-mode [class*=\"sidebar-light-\"] .sidebar a:hover {\n  text-decoration: none;\n}\n\n.logo-xs,\n.logo-xl {\n  opacity: 1;\n  position: absolute;\n  visibility: visible;\n}\n\n.logo-xs.brand-image-xs,\n.logo-xl.brand-image-xs {\n  left: 18px;\n  top: 12px;\n}\n\n.logo-xs.brand-image-xl,\n.logo-xl.brand-image-xl {\n  left: 12px;\n  top: 6px;\n}\n\n.logo-xs {\n  opacity: 0;\n  visibility: hidden;\n}\n\n.logo-xs.brand-image-xl {\n  left: 16px;\n  top: 8px;\n}\n\n.brand-link.logo-switch::before {\n  content: \"\\00a0\";\n}\n\n@media (min-width: 992px) {\n  .sidebar-mini .nav-sidebar,\n  .sidebar-mini .nav-sidebar > .nav-header,\n  .sidebar-mini .nav-sidebar .nav-link {\n    white-space: nowrap;\n  }\n  .sidebar-mini.sidebar-collapse .d-hidden-mini {\n    display: none;\n  }\n  .sidebar-mini.sidebar-collapse .content-wrapper,\n  .sidebar-mini.sidebar-collapse .main-footer,\n  .sidebar-mini.sidebar-collapse .main-header {\n    margin-left: 4.6rem !important;\n  }\n  .sidebar-mini.sidebar-collapse .nav-sidebar .nav-header {\n    display: none;\n  }\n  .sidebar-mini.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n    width: 0;\n    white-space: nowrap;\n  }\n  .sidebar-mini.sidebar-collapse .sidebar .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .brand-text {\n    margin-left: -10px;\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .logo-xl {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .logo-xs {\n    display: inline-block;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar {\n    overflow-x: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar, .sidebar-mini.sidebar-collapse .main-sidebar::before {\n    margin-left: 0;\n    width: 4.6rem;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar .user-panel .image {\n    float: none;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n    text-align: left;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n    float: left;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n    display: inline-block;\n    margin-left: 0;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n    margin-right: .5rem;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n    display: block !important;\n    -webkit-transform: translateZ(0);\n    transform: translateZ(0);\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n    display: inline-block !important;\n  }\n  .sidebar-mini.sidebar-collapse .visible-sidebar-mini {\n    display: block !important;\n  }\n  .sidebar-mini.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse.layout-fixed .brand-link {\n    width: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini.sidebar-collapse .main-sidebar {\n    box-shadow: none !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-md .nav-sidebar,\n  .sidebar-mini-md .nav-sidebar > .nav-header,\n  .sidebar-mini-md .nav-sidebar .nav-link {\n    white-space: nowrap;\n  }\n  .sidebar-mini-md.sidebar-collapse .d-hidden-mini {\n    display: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .content-wrapper,\n  .sidebar-mini-md.sidebar-collapse .main-footer,\n  .sidebar-mini-md.sidebar-collapse .main-header {\n    margin-left: 4.6rem !important;\n  }\n  .sidebar-mini-md.sidebar-collapse .nav-sidebar .nav-header {\n    display: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n    width: 0;\n    white-space: nowrap;\n  }\n  .sidebar-mini-md.sidebar-collapse .sidebar .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .brand-text {\n    margin-left: -10px;\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .logo-xl {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .logo-xs {\n    display: inline-block;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar {\n    overflow-x: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar, .sidebar-mini-md.sidebar-collapse .main-sidebar::before {\n    margin-left: 0;\n    width: 4.6rem;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar .user-panel .image {\n    float: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n    text-align: left;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n    float: left;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n    display: inline-block;\n    margin-left: 0;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n    margin-right: .5rem;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n    display: block !important;\n    -webkit-transform: translateZ(0);\n    transform: translateZ(0);\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n    display: inline-block !important;\n  }\n  .sidebar-mini-md.sidebar-collapse .visible-sidebar-mini {\n    display: block !important;\n  }\n  .sidebar-mini-md.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse.layout-fixed .brand-link {\n    width: 4.6rem;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .sidebar-mini-md.sidebar-collapse .main-sidebar {\n    box-shadow: none !important;\n  }\n}\n\n.sidebar-mini-xs .nav-sidebar,\n.sidebar-mini-xs .nav-sidebar > .nav-header,\n.sidebar-mini-xs .nav-sidebar .nav-link {\n  white-space: nowrap;\n}\n\n.sidebar-mini-xs.sidebar-collapse .d-hidden-mini {\n  display: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .content-wrapper,\n.sidebar-mini-xs.sidebar-collapse .main-footer,\n.sidebar-mini-xs.sidebar-collapse .main-header {\n  margin-left: 4.6rem !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse .nav-sidebar .nav-header {\n  display: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n  width: 0;\n  white-space: nowrap;\n}\n\n.sidebar-mini-xs.sidebar-collapse .sidebar .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .brand-text {\n  margin-left: -10px;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .logo-xl {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .logo-xs {\n  display: inline-block;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar {\n  overflow-x: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar, .sidebar-mini-xs.sidebar-collapse .main-sidebar::before {\n  margin-left: 0;\n  width: 4.6rem;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar .user-panel .image {\n  float: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n  text-align: left;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n  float: left;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n  display: inline-block;\n  margin-left: 0;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n  margin-right: .5rem;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n  display: block !important;\n  -webkit-transform: translateZ(0);\n  transform: translateZ(0);\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n  display: inline-block !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse .visible-sidebar-mini {\n  display: block !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse.layout-fixed .brand-link {\n  width: 4.6rem;\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 3rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 4rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 3rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 4rem);\n}\n\n.sidebar-mini .main-sidebar .nav-flat .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 2);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 3);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 4);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - .5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1.5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2.5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - .5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 2);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 3);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 4);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 5);\n}\n\n.sidebar-mini .main-sidebar .nav-link,\n.sidebar-mini-md .main-sidebar .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-link {\n  width: calc(250px - 0.5rem * 2);\n  transition: width ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar-mini .main-sidebar .nav-link,\n  .sidebar-mini-md .main-sidebar .nav-link,\n  .sidebar-mini-xs .main-sidebar .nav-link {\n    transition: none;\n  }\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar .sidebar-search-results {\n  display: none;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar .nav-link {\n  width: 3.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-legacy .nav-link {\n  width: 4.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview {\n  padding-left: 0 !important;\n  margin-left: 0 !important;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link {\n  width: calc(4.6rem - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 1rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 1rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 2rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 3rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 4rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 3);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 4);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-compact .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - .5rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 3);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 4);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 5);\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover {\n  width: 4.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .nav-header, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .nav-header, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .nav-header {\n  display: none;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .brand-link, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .brand-link, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .brand-link {\n  width: 4.6rem !important;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .user-panel .image, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .user-panel .image, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .user-panel .image {\n  float: none !important;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .logo-xs, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .logo-xs, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .logo-xs {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .logo-xl, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .logo-xl, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .logo-xl {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview {\n  padding-left: 0;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p {\n  margin-left: -10px;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n  display: inline-block;\n  width: 0;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon {\n  margin-right: 0;\n}\n\n.nav-sidebar {\n  position: relative;\n}\n\n.nav-sidebar:hover {\n  overflow: visible;\n}\n\n.sidebar-form,\n.nav-sidebar > .nav-header {\n  overflow: hidden;\n  text-overflow: clip;\n}\n\n.nav-sidebar .nav-item > .nav-link {\n  position: relative;\n}\n\n.nav-sidebar .nav-item > .nav-link > .float-right {\n  margin-top: -7px;\n  position: absolute;\n  right: 10px;\n  top: 50%;\n}\n\n.sidebar .nav-link p,\n.main-sidebar .brand-text,\n.main-sidebar .logo-xs,\n.main-sidebar .logo-xl,\n.sidebar .user-panel .info {\n  transition: margin-left 0.3s linear, opacity 0.3s ease, visibility 0.3s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar .nav-link p,\n  .main-sidebar .brand-text,\n  .main-sidebar .logo-xs,\n  .main-sidebar .logo-xl,\n  .sidebar .user-panel .info {\n    transition: none;\n  }\n}\n\nhtml.control-sidebar-animate {\n  overflow-x: hidden;\n}\n\n.control-sidebar {\n  bottom: calc(3.5rem + 1px);\n  position: absolute;\n  top: calc(3.5rem + 1px);\n  z-index: 1031;\n}\n\n.control-sidebar, .control-sidebar::before {\n  bottom: calc(3.5rem + 1px);\n  display: none;\n  right: -250px;\n  width: 250px;\n  transition: right 0.3s ease-in-out, display 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar, .control-sidebar::before {\n    transition: none;\n  }\n}\n\n.control-sidebar::before {\n  content: \"\";\n  display: block;\n  position: fixed;\n  top: 0;\n  z-index: -1;\n}\n\nbody.text-sm .control-sidebar {\n  bottom: calc(2.9365rem + 1px);\n  top: calc(2.93725rem + 1px);\n}\n\n.main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.main-footer.text-sm ~ .control-sidebar {\n  bottom: calc(2.9365rem + 1px);\n}\n\n.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-push-slide .main-footer {\n  transition: margin-right 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar-push-slide .content-wrapper,\n  .control-sidebar-push-slide .main-footer {\n    transition: none;\n  }\n}\n\n.control-sidebar-open .control-sidebar {\n  display: block !important;\n}\n\n.control-sidebar-open .control-sidebar, .control-sidebar-open .control-sidebar::before {\n  right: 0;\n}\n\n.control-sidebar-open.control-sidebar-push .content-wrapper,\n.control-sidebar-open.control-sidebar-push .main-footer, .control-sidebar-open.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-open.control-sidebar-push-slide .main-footer {\n  margin-right: 250px;\n}\n\n.control-sidebar-slide-open .control-sidebar {\n  display: block;\n}\n\n.control-sidebar-slide-open .control-sidebar, .control-sidebar-slide-open .control-sidebar::before {\n  right: 0;\n  transition: right 0.3s ease-in-out, display 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar-slide-open .control-sidebar, .control-sidebar-slide-open .control-sidebar::before {\n    transition: none;\n  }\n}\n\n.control-sidebar-slide-open.control-sidebar-push .content-wrapper,\n.control-sidebar-slide-open.control-sidebar-push .main-footer, .control-sidebar-slide-open.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-slide-open.control-sidebar-push-slide .main-footer {\n  margin-right: 250px;\n}\n\n.control-sidebar-dark {\n  background-color: #343a40;\n}\n\n.control-sidebar-dark,\n.control-sidebar-dark a,\n.control-sidebar-dark .nav-link {\n  color: #c2c7d0;\n}\n\n.control-sidebar-dark a:hover {\n  color: #fff;\n}\n\n.control-sidebar-dark h1,\n.control-sidebar-dark h2,\n.control-sidebar-dark h3,\n.control-sidebar-dark h4,\n.control-sidebar-dark h5,\n.control-sidebar-dark h6,\n.control-sidebar-dark label {\n  color: #fff;\n}\n\n.control-sidebar-dark .nav-tabs {\n  background-color: rgba(255, 255, 255, 0.1);\n  border-bottom: 0;\n  margin-bottom: 5px;\n}\n\n.control-sidebar-dark .nav-tabs .nav-item {\n  margin: 0;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link {\n  border-radius: 0;\n  padding: 10px 20px;\n  position: relative;\n  text-align: center;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link, .control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active {\n  border: 0;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active {\n  border-bottom-color: transparent;\n  border-left-color: transparent;\n  border-top-color: transparent;\n  color: #fff;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link.active {\n  background-color: #343a40;\n}\n\n.control-sidebar-dark .tab-pane {\n  padding: 10px 15px;\n}\n\n.control-sidebar-light {\n  color: #4b545c;\n  background-color: #fff;\n  border-left: 1px solid #dee2e6;\n}\n\n.text-sm .dropdown-menu {\n  font-size: 0.875rem !important;\n}\n\n.text-sm .dropdown-toggle::after {\n  vertical-align: .2rem;\n}\n\n.dropdown-item-title {\n  font-size: 1rem;\n  margin: 0;\n}\n\n.dropdown-icon::after {\n  margin-left: 0;\n}\n\n.dropdown-menu-lg {\n  max-width: 300px;\n  min-width: 280px;\n  padding: 0;\n}\n\n.dropdown-menu-lg .dropdown-divider {\n  margin: 0;\n}\n\n.dropdown-menu-lg .dropdown-item {\n  padding: 0.5rem 1rem;\n}\n\n.dropdown-menu-lg p {\n  margin: 0;\n  white-space: normal;\n}\n\n.dropdown-submenu {\n  position: relative;\n}\n\n.dropdown-submenu > a::after {\n  border-top: 0.3em solid transparent;\n  border-right: 0;\n  border-bottom: 0.3em solid transparent;\n  border-left: 0.3em solid;\n  float: right;\n  margin-left: .5rem;\n  margin-top: .5rem;\n}\n\n.dropdown-submenu > .dropdown-menu {\n  left: 100%;\n  margin-left: 0;\n  margin-top: 0;\n  top: 0;\n}\n\n.dropdown-hover:hover > .dropdown-menu, .dropdown-hover.nav-item.dropdown:hover > .dropdown-menu,\n.dropdown-hover .dropdown-submenu:hover > .dropdown-menu, .dropdown-hover.dropdown-submenu:hover > .dropdown-menu {\n  display: block;\n}\n\n.dropdown-menu-xl {\n  max-width: 420px;\n  min-width: 360px;\n  padding: 0;\n}\n\n.dropdown-menu-xl .dropdown-divider {\n  margin: 0;\n}\n\n.dropdown-menu-xl .dropdown-item {\n  padding: 0.5rem 1rem;\n}\n\n.dropdown-menu-xl p {\n  margin: 0;\n  white-space: normal;\n}\n\n.dropdown-footer,\n.dropdown-header {\n  display: block;\n  font-size: 0.875rem;\n  padding: 0.5rem 1rem;\n  text-align: center;\n}\n\n.open:not(.dropup) > .animated-dropdown-menu {\n  -webkit-animation: flipInX 0.7s both;\n  animation: flipInX 0.7s both;\n  -webkit-backface-visibility: visible !important;\n  backface-visibility: visible !important;\n}\n\n.navbar-custom-menu > .navbar-nav > li {\n  position: relative;\n}\n\n.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {\n  position: absolute;\n  right: 0;\n  left: auto;\n}\n\n@media (max-width: 767.98px) {\n  .navbar-custom-menu > .navbar-nav {\n    float: right;\n  }\n  .navbar-custom-menu > .navbar-nav > li {\n    position: static;\n  }\n  .navbar-custom-menu > .navbar-nav > li > .dropdown-menu {\n    position: absolute;\n    right: 5%;\n    left: auto;\n    border: 1px solid #ddd;\n    background-color: #fff;\n  }\n}\n\n.navbar-nav > .user-menu > .nav-link::after {\n  content: none;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  padding: 0;\n  width: 280px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu,\n.navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header {\n  height: 175px;\n  padding: 10px;\n  text-align: center;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > img {\n  z-index: 5;\n  height: 90px;\n  width: 90px;\n  border: 3px solid;\n  border-color: transparent;\n  border-color: rgba(255, 255, 255, 0.2);\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p {\n  z-index: 5;\n  font-size: 17px;\n  margin-top: 10px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p > small {\n  display: block;\n  font-size: 12px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-bottom: 1px solid #495057;\n  border-top: 1px solid #dee2e6;\n  padding: 15px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-body::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu > .dropdown-menu > .user-body a {\n    background-color: #fff !important;\n    color: #495057 !important;\n  }\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer {\n  background-color: #f8f9fa;\n  padding: 10px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default {\n  color: #6c757d;\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:hover {\n    background-color: #f8f9fa;\n  }\n}\n\n.navbar-nav > .user-menu .user-image {\n  border-radius: 50%;\n  float: left;\n  height: 2.1rem;\n  margin-right: 10px;\n  margin-top: -2px;\n  width: 2.1rem;\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu .user-image {\n    float: none;\n    line-height: 10px;\n    margin-right: .4rem;\n    margin-top: -8px;\n  }\n}\n\n.dark-mode .dropdown-menu {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .dropdown-item {\n  color: #fff;\n}\n\n.dark-mode .dropdown-item:focus, .dark-mode .dropdown-item:hover {\n  background-color: #3f474e;\n}\n\n.dark-mode .dropdown-divider {\n  border-color: #6c757d;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer {\n  background-color: #3a4047;\n  color: #fff;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default {\n  color: #fff;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:hover, .dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:focus {\n  background-color: #3f474e;\n  color: #dee2e6;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:focus {\n  background-color: #454d55;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-color: #6c757d;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body a {\n  background-color: transparent !important;\n  color: #fff !important;\n}\n\n.dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body a:hover, .dark-mode .navbar-nav > .user-menu > .dropdown-menu > .user-body a:focus {\n  color: #ced4da !important;\n}\n\n.nav-pills .nav-link {\n  color: #6c757d;\n}\n\n.nav-pills .nav-link:not(.active):hover {\n  color: #007bff;\n}\n\n.nav-pills .nav-item.dropdown.show .nav-link:hover {\n  color: #fff;\n}\n\n.nav-tabs.flex-column {\n  border-bottom: 0;\n  border-right: 1px solid #dee2e6;\n}\n\n.nav-tabs.flex-column .nav-link {\n  border-bottom-left-radius: 0.25rem;\n  border-top-right-radius: 0;\n  margin-right: -1px;\n}\n\n.nav-tabs.flex-column .nav-link:hover, .nav-tabs.flex-column .nav-link:focus {\n  border-color: #e9ecef transparent #e9ecef #e9ecef;\n}\n\n.nav-tabs.flex-column .nav-link.active,\n.nav-tabs.flex-column .nav-item.show .nav-link {\n  border-color: #dee2e6 transparent #dee2e6 #dee2e6;\n}\n\n.nav-tabs.flex-column.nav-tabs-right {\n  border-left: 1px solid #dee2e6;\n  border-right: 0;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link {\n  border-bottom-left-radius: 0;\n  border-bottom-right-radius: 0.25rem;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0.25rem;\n  margin-left: -1px;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link:hover, .nav-tabs.flex-column.nav-tabs-right .nav-link:focus {\n  border-color: #e9ecef #e9ecef #e9ecef transparent;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link.active,\n.nav-tabs.flex-column.nav-tabs-right .nav-item.show .nav-link {\n  border-color: #dee2e6 #dee2e6 #dee2e6 transparent;\n}\n\n.navbar-no-expand {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-no-expand .nav-link {\n  padding-left: 1rem;\n  padding-right: 1rem;\n}\n\n.navbar-no-expand .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-light {\n  background-color: #f8f9fa;\n}\n\n.navbar-dark {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.navbar-primary {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar,\n.navbar-primary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #0071eb;\n  border-color: #0065d1;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus,\n.navbar-primary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #006fe6;\n  border-color: #0065d1 !important;\n  color: #343a40;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar,\n.navbar-primary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1486ff;\n  border-color: #2e93ff;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus,\n.navbar-primary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1a88ff;\n  border-color: #2e93ff !important;\n  color: #fff;\n}\n\n.navbar-secondary {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar,\n.navbar-secondary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus,\n.navbar-secondary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar,\n.navbar-secondary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus,\n.navbar-secondary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.navbar-success {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.navbar-success.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar,\n.navbar-success.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #24973e;\n  border-color: #1f8236;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus,\n.navbar-success.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #23923d;\n  border-color: #1f8236 !important;\n  color: #343a40;\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar,\n.navbar-success.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2cb74c;\n  border-color: #31cc54;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus,\n.navbar-success.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #2dbc4e;\n  border-color: #31cc54 !important;\n  color: #fff;\n}\n\n.navbar-info {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.navbar-info.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar,\n.navbar-info.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1592a6;\n  border-color: #127e8f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus,\n.navbar-info.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #148ea1;\n  border-color: #127e8f !important;\n  color: #343a40;\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar,\n.navbar-info.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #19b2ca;\n  border-color: #1cc6e1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus,\n.navbar-info.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1ab6cf;\n  border-color: #1cc6e1 !important;\n  color: #fff;\n}\n\n.navbar-warning {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar,\n.navbar-warning.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f2b500;\n  border-color: #d8a200;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus,\n.navbar-warning.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #edb100;\n  border-color: #d8a200 !important;\n  color: #343a40;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar,\n.navbar-warning.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ffc61b;\n  border-color: #ffcc35;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus,\n.navbar-warning.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ffc721;\n  border-color: #ffcc35 !important;\n  color: #fff;\n}\n\n.navbar-danger {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar,\n.navbar-danger.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #d72536;\n  border-color: #c22231;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus,\n.navbar-danger.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d32535;\n  border-color: #c22231 !important;\n  color: #343a40;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar,\n.navbar-danger.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df4655;\n  border-color: #e35c69;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus,\n.navbar-danger.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e04b59;\n  border-color: #e35c69 !important;\n  color: #fff;\n}\n\n.navbar-lightblue {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar,\n.navbar-lightblue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3781ad;\n  border-color: #317399;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus,\n.navbar-lightblue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #367fa9;\n  border-color: #317399 !important;\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar,\n.navbar-lightblue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #4897c5;\n  border-color: #5ba2cb;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus,\n.navbar-lightblue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4c99c6;\n  border-color: #5ba2cb !important;\n  color: #fff;\n}\n\n.navbar-navy {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar,\n.navbar-navy.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00152b;\n  border-color: #000811;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus,\n.navbar-navy.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #001226;\n  border-color: #000811 !important;\n  color: #343a40;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar,\n.navbar-navy.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #002953;\n  border-color: #00366d;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus,\n.navbar-navy.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #002c59;\n  border-color: #00366d !important;\n  color: #fff;\n}\n\n.navbar-olive {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar,\n.navbar-olive.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #378a65;\n  border-color: #307858;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus,\n.navbar-olive.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #368763;\n  border-color: #307858 !important;\n  color: #343a40;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar,\n.navbar-olive.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #43a87b;\n  border-color: #4cb888;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus,\n.navbar-olive.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #44ab7d;\n  border-color: #4cb888 !important;\n  color: #fff;\n}\n\n.navbar-lime {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar,\n.navbar-lime.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00ec67;\n  border-color: #00d25c;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus,\n.navbar-lime.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00e765;\n  border-color: #00d25c !important;\n  color: #343a40;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar,\n.navbar-lime.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #15ff7b;\n  border-color: #2fff8a;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus,\n.navbar-lime.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1bff7e;\n  border-color: #2fff8a !important;\n  color: #fff;\n}\n\n.navbar-fuchsia {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar,\n.navbar-fuchsia.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df0eb0;\n  border-color: #c70d9d;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus,\n.navbar-fuchsia.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #db0ead;\n  border-color: #c70d9d !important;\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar,\n.navbar-fuchsia.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f125c3;\n  border-color: #f33dca;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus,\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f22ac5;\n  border-color: #f33dca !important;\n  color: #fff;\n}\n\n.navbar-maroon {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar,\n.navbar-maroon.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #c61958;\n  border-color: #af164e;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus,\n.navbar-maroon.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #c11856;\n  border-color: #af164e !important;\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar,\n.navbar-maroon.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e4246a;\n  border-color: #e63a79;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus,\n.navbar-maroon.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e4286d;\n  border-color: #e63a79 !important;\n  color: #fff;\n}\n\n.navbar-blue {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar,\n.navbar-blue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #0071eb;\n  border-color: #0065d1;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus,\n.navbar-blue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #006fe6;\n  border-color: #0065d1 !important;\n  color: #343a40;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar,\n.navbar-blue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1486ff;\n  border-color: #2e93ff;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus,\n.navbar-blue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1a88ff;\n  border-color: #2e93ff !important;\n  color: #fff;\n}\n\n.navbar-indigo {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar,\n.navbar-indigo.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #5d0ce1;\n  border-color: #530bc9;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus,\n.navbar-indigo.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #5b0cdd;\n  border-color: #530bc9 !important;\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar,\n.navbar-indigo.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7223f3;\n  border-color: #823cf4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus,\n.navbar-indigo.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7528f3;\n  border-color: #823cf4 !important;\n  color: #fff;\n}\n\n.navbar-purple {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar,\n.navbar-purple.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #663bb4;\n  border-color: #5b35a0;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus,\n.navbar-purple.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #643ab0;\n  border-color: #5b35a0 !important;\n  color: #343a40;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar,\n.navbar-purple.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7b51c6;\n  border-color: #8965cc;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus,\n.navbar-purple.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7e55c7;\n  border-color: #8965cc !important;\n  color: #fff;\n}\n\n.navbar-pink {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar,\n.navbar-pink.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e62c81;\n  border-color: #de1a74;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus,\n.navbar-pink.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e5277e;\n  border-color: #de1a74 !important;\n  color: #343a40;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar,\n.navbar-pink.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ea5097;\n  border-color: #ed67a4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus,\n.navbar-pink.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #eb559a;\n  border-color: #ed67a4 !important;\n  color: #fff;\n}\n\n.navbar-red {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.navbar-red.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar,\n.navbar-red.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #d72536;\n  border-color: #c22231;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus,\n.navbar-red.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d32535;\n  border-color: #c22231 !important;\n  color: #343a40;\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar,\n.navbar-red.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df4655;\n  border-color: #e35c69;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus,\n.navbar-red.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e04b59;\n  border-color: #e35c69 !important;\n  color: #fff;\n}\n\n.navbar-orange {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar,\n.navbar-orange.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fa7302;\n  border-color: #e16702;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus,\n.navbar-orange.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f57102;\n  border-color: #e16702 !important;\n  color: #343a40;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar,\n.navbar-orange.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fd8928;\n  border-color: #fd9742;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus,\n.navbar-orange.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #fd8c2d;\n  border-color: #fd9742 !important;\n  color: #fff;\n}\n\n.navbar-yellow {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar,\n.navbar-yellow.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f2b500;\n  border-color: #d8a200;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus,\n.navbar-yellow.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #edb100;\n  border-color: #d8a200 !important;\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar,\n.navbar-yellow.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ffc61b;\n  border-color: #ffcc35;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus,\n.navbar-yellow.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ffc721;\n  border-color: #ffcc35 !important;\n  color: #fff;\n}\n\n.navbar-green {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.navbar-green.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar,\n.navbar-green.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #24973e;\n  border-color: #1f8236;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus,\n.navbar-green.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #23923d;\n  border-color: #1f8236 !important;\n  color: #343a40;\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar,\n.navbar-green.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2cb74c;\n  border-color: #31cc54;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus,\n.navbar-green.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #2dbc4e;\n  border-color: #31cc54 !important;\n  color: #fff;\n}\n\n.navbar-teal {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar,\n.navbar-teal.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1db78a;\n  border-color: #1aa179;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus,\n.navbar-teal.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1cb386;\n  border-color: #1aa179 !important;\n  color: #343a40;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar,\n.navbar-teal.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #23dba4;\n  border-color: #38dfae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus,\n.navbar-teal.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #26dca6;\n  border-color: #38dfae !important;\n  color: #fff;\n}\n\n.navbar-cyan {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar,\n.navbar-cyan.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1592a6;\n  border-color: #127e8f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus,\n.navbar-cyan.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #148ea1;\n  border-color: #127e8f !important;\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar,\n.navbar-cyan.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #19b2ca;\n  border-color: #1cc6e1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus,\n.navbar-cyan.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1ab6cf;\n  border-color: #1cc6e1 !important;\n  color: #fff;\n}\n\n.navbar-white {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.navbar-white.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar,\n.navbar-white.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: whitesmoke;\n  border-color: #e8e8e8;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus,\n.navbar-white.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f2f2f2;\n  border-color: #e8e8e8 !important;\n  color: #343a40;\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar,\n.navbar-white.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: white;\n  border-color: white;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus,\n.navbar-white.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: white;\n  border-color: white !important;\n  color: #fff;\n}\n\n.navbar-gray {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar,\n.navbar-gray.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus,\n.navbar-gray.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar,\n.navbar-gray.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus,\n.navbar-gray.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.navbar-gray-dark {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar,\n.navbar-gray-dark.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2b3035;\n  border-color: #1f2327;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus,\n.navbar-gray-dark.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #292d32;\n  border-color: #1f2327 !important;\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar,\n.navbar-gray-dark.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3d444b;\n  border-color: #495159;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus,\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #3f474e;\n  border-color: #495159 !important;\n  color: #fff;\n}\n\n.navbar-nav-not-expanded {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-nav-not-expanded .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-nav-not-expanded .nav-link {\n  padding-right: 1rem;\n  padding-left: 1rem;\n}\n\n.dark-mode .nav-pills .nav-link {\n  color: #ced4da;\n}\n\n.dark-mode .nav-tabs {\n  border-color: #56606a;\n}\n\n.dark-mode .nav-tabs .nav-link:focus,\n.dark-mode .nav-tabs .nav-link:hover {\n  border-color: #56606a;\n}\n\n.dark-mode .nav-tabs .nav-item.show .nav-link,\n.dark-mode .nav-tabs .nav-link.active {\n  background-color: #343a40;\n  border-color: #56606a #56606a transparent #56606a;\n  color: #fff;\n}\n\n.dark-mode .nav-tabs.flex-column .nav-item.show .nav-link.active, .dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:focus, .dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:hover,\n.dark-mode .nav-tabs.flex-column .nav-link.active,\n.dark-mode .nav-tabs.flex-column .nav-link:focus,\n.dark-mode .nav-tabs.flex-column .nav-link:hover {\n  border-color: #56606a transparent #56606a #56606a;\n}\n\n.dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:focus, .dark-mode .nav-tabs.flex-column .nav-item.show .nav-link:hover,\n.dark-mode .nav-tabs.flex-column .nav-link:focus,\n.dark-mode .nav-tabs.flex-column .nav-link:hover {\n  background-color: #3f474e;\n}\n\n.dark-mode .nav-tabs.flex-column.nav-tabs-right {\n  border-color: #56606a;\n}\n\n.dark-mode .nav-tabs.flex-column.nav-tabs-right .nav-link.active, .dark-mode .nav-tabs.flex-column.nav-tabs-right .nav-link:focus, .dark-mode .nav-tabs.flex-column.nav-tabs-right .nav-link:hover {\n  border-color: #56606a #56606a #56606a transparent;\n}\n\n.dark-mode .navbar-light {\n  background-color: #f8f9fa;\n}\n\n.dark-mode .navbar-dark {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.dark-mode .navbar-primary {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar,\n.dark-mode .navbar-primary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #395d83;\n  border-color: #315071;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-primary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #375a7f;\n  border-color: #315071 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar,\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45719f;\n  border-color: #4d7eb1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-primary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4774a3;\n  border-color: #4d7eb1 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar,\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-secondary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar,\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-secondary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-success {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar,\n.dark-mode .navbar-success.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00a87d;\n  border-color: #008e6a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-success.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00a379;\n  border-color: #008e6a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar,\n.dark-mode .navbar-success.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00d09b;\n  border-color: #00eaae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-success.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00d69f;\n  border-color: #00eaae !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-info {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar,\n.dark-mode .navbar-info.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #268fd5;\n  border-color: #2280bf;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-info.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #258cd1;\n  border-color: #2280bf !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar,\n.dark-mode .navbar-info.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45a1de;\n  border-color: #5bace2;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-info.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4aa3df;\n  border-color: #5bace2 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-warning {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar,\n.dark-mode .navbar-warning.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e5910c;\n  border-color: #cd820a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-warning.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e08e0b;\n  border-color: #cd820a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar,\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f4a425;\n  border-color: #f5ae3e;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-warning.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f4a62a;\n  border-color: #f5ae3e !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-danger {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar,\n.dark-mode .navbar-danger.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e53b2a;\n  border-color: #da2d1b;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-danger.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e43725;\n  border-color: #da2d1b !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar,\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e95d4e;\n  border-color: #ec7265;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-danger.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ea6153;\n  border-color: #ec7265 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar,\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #76b1d3;\n  border-color: #63a6cd;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-lightblue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #72afd2;\n  border-color: #63a6cd !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar,\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #95c3dd;\n  border-color: #a9cee3;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-lightblue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #99c5de;\n  border-color: #a9cee3 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-navy {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar,\n.dark-mode .navbar-navy.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #002244;\n  border-color: #00152b;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-navy.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #001f3f;\n  border-color: #00152b !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar,\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00366d;\n  border-color: #004286;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-navy.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #003872;\n  border-color: #004286 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-olive {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar,\n.dark-mode .navbar-olive.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #66c299;\n  border-color: #53bb8d;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-olive.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #62c096;\n  border-color: #53bb8d !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar,\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #83ceac;\n  border-color: #95d5b8;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-olive.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #87cfaf;\n  border-color: #95d5b8 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-lime {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar,\n.dark-mode .navbar-lime.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #53ff9e;\n  border-color: #39ff90;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-lime.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4eff9b;\n  border-color: #39ff90 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar,\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7bffb5;\n  border-color: #95ffc3;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-lime.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #81ffb8;\n  border-color: #95ffc3 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar,\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f55fd3;\n  border-color: #f347cc;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-fuchsia.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f55ad2;\n  border-color: #f347cc !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar,\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f785de;\n  border-color: #f99de4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-fuchsia.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f88adf;\n  border-color: #f99de4 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar,\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ea5a8f;\n  border-color: #e8447f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-maroon.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ea568c;\n  border-color: #e8447f !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar,\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ef7ea8;\n  border-color: #f295b7;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-maroon.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f083ab;\n  border-color: #f295b7 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-blue {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar,\n.dark-mode .navbar-blue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #395d83;\n  border-color: #315071;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-blue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #375a7f;\n  border-color: #315071 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar,\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45719f;\n  border-color: #4d7eb1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-blue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4774a3;\n  border-color: #4d7eb1 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar,\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #5d0ce1;\n  border-color: #530bc9;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-indigo.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #5b0cdd;\n  border-color: #530bc9 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar,\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7223f3;\n  border-color: #823cf4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-indigo.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7528f3;\n  border-color: #823cf4 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-purple {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar,\n.dark-mode .navbar-purple.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #663bb4;\n  border-color: #5b35a0;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-purple.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #643ab0;\n  border-color: #5b35a0 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar,\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7b51c6;\n  border-color: #8965cc;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-purple.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7e55c7;\n  border-color: #8965cc !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-pink {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar,\n.dark-mode .navbar-pink.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e62c81;\n  border-color: #de1a74;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-pink.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e5277e;\n  border-color: #de1a74 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar,\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ea5097;\n  border-color: #ed67a4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-pink.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #eb559a;\n  border-color: #ed67a4 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-red {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar,\n.dark-mode .navbar-red.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e53b2a;\n  border-color: #da2d1b;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-red.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e43725;\n  border-color: #da2d1b !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar,\n.dark-mode .navbar-red.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e95d4e;\n  border-color: #ec7265;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-red.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ea6153;\n  border-color: #ec7265 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-orange {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar,\n.dark-mode .navbar-orange.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fa7302;\n  border-color: #e16702;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-orange.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f57102;\n  border-color: #e16702 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar,\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fd8928;\n  border-color: #fd9742;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-orange.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #fd8c2d;\n  border-color: #fd9742 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar,\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e5910c;\n  border-color: #cd820a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-yellow.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e08e0b;\n  border-color: #cd820a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar,\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f4a425;\n  border-color: #f5ae3e;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-yellow.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f4a62a;\n  border-color: #f5ae3e !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-green {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar,\n.dark-mode .navbar-green.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00a87d;\n  border-color: #008e6a;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-green.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00a379;\n  border-color: #008e6a !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar,\n.dark-mode .navbar-green.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00d09b;\n  border-color: #00eaae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-green.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00d69f;\n  border-color: #00eaae !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-teal {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar,\n.dark-mode .navbar-teal.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1db78a;\n  border-color: #1aa179;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-teal.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1cb386;\n  border-color: #1aa179 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar,\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #23dba4;\n  border-color: #38dfae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-teal.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #26dca6;\n  border-color: #38dfae !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar,\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #268fd5;\n  border-color: #2280bf;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-cyan.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #258cd1;\n  border-color: #2280bf !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar,\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #45a1de;\n  border-color: #5bace2;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-cyan.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4aa3df;\n  border-color: #5bace2 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-white {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar,\n.dark-mode .navbar-white.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: whitesmoke;\n  border-color: #e8e8e8;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-white.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f2f2f2;\n  border-color: #e8e8e8 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar,\n.dark-mode .navbar-white.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: white;\n  border-color: white;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-white.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: white;\n  border-color: white !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar,\n.dark-mode .navbar-gray.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-gray.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar,\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-gray.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar,\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2b3035;\n  border-color: #1f2327;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus,\n.dark-mode .navbar-gray-dark.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #292d32;\n  border-color: #1f2327 !important;\n  color: #343a40;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar,\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3d444b;\n  border-color: #495159;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus,\n.dark-mode .navbar-gray-dark.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #3f474e;\n  border-color: #495159 !important;\n  color: #fff;\n}\n\n.pagination-month .page-item {\n  justify-self: stretch;\n}\n\n.pagination-month .page-item .page-link {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  box-shadow: none;\n}\n\n.pagination-month .page-item:first-child .page-link, .pagination-month .page-item:last-child .page-link {\n  height: 100%;\n  font-size: 1.25rem;\n}\n\n.pagination-month .page-item .page-month {\n  margin-bottom: 0;\n  font-size: 1.25rem;\n  font-weight: 700;\n}\n\n.pagination-month .page-item .page-year {\n  margin-bottom: 0;\n}\n\n.pagination-month.pagination-lg .page-month {\n  font-size: 1.5625rem;\n}\n\n.pagination-month.pagination-sm .page-month {\n  font-size: 1rem;\n}\n\n.dark-mode .page-item.disabled a,\n.dark-mode .page-item.disabled .page-link {\n  background-color: #3a4047 !important;\n  border-color: #6c757d !important;\n  color: #6c757d;\n}\n\n.dark-mode .page-item .page-link {\n  color: #3f6791;\n}\n\n.dark-mode .page-item.active .page-link {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .page-item.active .page-link:hover, .dark-mode .page-item.active .page-link:focus {\n  color: #ced4da !important;\n}\n\n.dark-mode .page-item:not(.active) .page-link {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.dark-mode .page-item:not(.active) .page-link:hover, .dark-mode .page-item:not(.active) .page-link:focus {\n  color: #4774a3;\n  background-color: #3f474e;\n}\n\n.border-transparent {\n  border-color: transparent !important;\n}\n\n.description-block {\n  display: block;\n  margin: 10px 0;\n  text-align: center;\n}\n\n.description-block.margin-bottom {\n  margin-bottom: 25px;\n}\n\n.description-block > .description-header {\n  font-size: 16px;\n  font-weight: 600;\n  margin: 0;\n  padding: 0;\n}\n\n.description-block > .description-text {\n  text-transform: uppercase;\n}\n\n.description-block .description-icon {\n  font-size: 16px;\n}\n\n.list-group-unbordered > .list-group-item {\n  border-left: 0;\n  border-radius: 0;\n  border-right: 0;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.list-header {\n  color: #6c757d;\n  font-size: 15px;\n  font-weight: 700;\n  padding: 10px 4px;\n}\n\n.list-seperator {\n  background-color: rgba(0, 0, 0, 0.125);\n  height: 1px;\n  margin: 15px 0 9px;\n}\n\n.list-link > a {\n  color: #6c757d;\n  padding: 4px;\n}\n\n.list-link > a:hover {\n  color: #212529;\n}\n\n.user-block {\n  float: left;\n}\n\n.user-block img {\n  float: left;\n  height: 40px;\n  width: 40px;\n}\n\n.user-block .username,\n.user-block .description,\n.user-block .comment {\n  display: block;\n  margin-left: 50px;\n}\n\n.user-block .username {\n  font-size: 16px;\n  font-weight: 600;\n  margin-top: -1px;\n}\n\n.user-block .description {\n  color: #6c757d;\n  font-size: 13px;\n  margin-top: -3px;\n}\n\n.user-block.user-block-sm img {\n  width: 1.875rem;\n  height: 1.875rem;\n}\n\n.user-block.user-block-sm .username,\n.user-block.user-block-sm .description,\n.user-block.user-block-sm .comment {\n  margin-left: 40px;\n}\n\n.user-block.user-block-sm .username {\n  font-size: 14px;\n}\n\n.img-sm,\n.img-md,\n.img-lg {\n  float: left;\n}\n\n.img-sm {\n  height: 1.875rem;\n  width: 1.875rem;\n}\n\n.img-sm + .img-push {\n  margin-left: 2.5rem;\n}\n\n.img-md {\n  width: 3.75rem;\n  height: 3.75rem;\n}\n\n.img-md + .img-push {\n  margin-left: 4.375rem;\n}\n\n.img-lg {\n  width: 6.25rem;\n  height: 6.25rem;\n}\n\n.img-lg + .img-push {\n  margin-left: 6.875rem;\n}\n\n.img-bordered {\n  border: 3px solid #adb5bd;\n  padding: 3px;\n}\n\n.img-bordered-sm {\n  border: 2px solid #adb5bd;\n  padding: 2px;\n}\n\n.img-rounded {\n  border-radius: 0.25rem;\n}\n\n.img-circle {\n  border-radius: 50%;\n}\n\n.img-size-64,\n.img-size-50,\n.img-size-32 {\n  height: auto;\n}\n\n.img-size-64 {\n  width: 64px;\n}\n\n.img-size-50 {\n  width: 50px;\n}\n\n.img-size-32 {\n  width: 32px;\n}\n\n.size-32,\n.size-40,\n.size-50 {\n  display: block;\n  text-align: center;\n}\n\n.size-32 {\n  height: 32px;\n  line-height: 32px;\n  width: 32px;\n}\n\n.size-40 {\n  height: 40px;\n  line-height: 40px;\n  width: 40px;\n}\n\n.size-50 {\n  height: 50px;\n  line-height: 50px;\n  width: 50px;\n}\n\n.attachment-block {\n  background-color: #f8f9fa;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n  margin-bottom: 10px;\n  padding: 5px;\n}\n\n.attachment-block .attachment-img {\n  float: left;\n  height: auto;\n  max-height: 100px;\n  max-width: 100px;\n}\n\n.attachment-block .attachment-pushed {\n  margin-left: 110px;\n}\n\n.attachment-block .attachment-heading {\n  margin: 0;\n}\n\n.attachment-block .attachment-text {\n  color: #495057;\n}\n\n.card > .overlay,\n.card > .loading-img,\n.overlay-wrapper > .overlay,\n.overlay-wrapper > .loading-img,\n.info-box > .overlay,\n.info-box > .loading-img,\n.small-box > .overlay,\n.small-box > .loading-img {\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.card .overlay,\n.overlay-wrapper .overlay,\n.info-box .overlay,\n.small-box .overlay {\n  border-radius: 0.25rem;\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: rgba(255, 255, 255, 0.7);\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  z-index: 50;\n}\n\n.card .overlay > .fa,\n.card .overlay > .fas,\n.card .overlay > .far,\n.card .overlay > .fab,\n.card .overlay > .fal,\n.card .overlay > .fad,\n.card .overlay > .svg-inline--fa,\n.card .overlay > .ion,\n.overlay-wrapper .overlay > .fa,\n.overlay-wrapper .overlay > .fas,\n.overlay-wrapper .overlay > .far,\n.overlay-wrapper .overlay > .fab,\n.overlay-wrapper .overlay > .fal,\n.overlay-wrapper .overlay > .fad,\n.overlay-wrapper .overlay > .svg-inline--fa,\n.overlay-wrapper .overlay > .ion,\n.info-box .overlay > .fa,\n.info-box .overlay > .fas,\n.info-box .overlay > .far,\n.info-box .overlay > .fab,\n.info-box .overlay > .fal,\n.info-box .overlay > .fad,\n.info-box .overlay > .svg-inline--fa,\n.info-box .overlay > .ion,\n.small-box .overlay > .fa,\n.small-box .overlay > .fas,\n.small-box .overlay > .far,\n.small-box .overlay > .fab,\n.small-box .overlay > .fal,\n.small-box .overlay > .fad,\n.small-box .overlay > .svg-inline--fa,\n.small-box .overlay > .ion {\n  color: #343a40;\n}\n\n.card .overlay.dark,\n.overlay-wrapper .overlay.dark,\n.info-box .overlay.dark,\n.small-box .overlay.dark {\n  background-color: rgba(0, 0, 0, 0.5);\n}\n\n.card .overlay.dark > .fa,\n.card .overlay.dark > .fas,\n.card .overlay.dark > .far,\n.card .overlay.dark > .fab,\n.card .overlay.dark > .fal,\n.card .overlay.dark > .fad,\n.card .overlay.dark > .svg-inline--fa,\n.card .overlay.dark > .ion,\n.overlay-wrapper .overlay.dark > .fa,\n.overlay-wrapper .overlay.dark > .fas,\n.overlay-wrapper .overlay.dark > .far,\n.overlay-wrapper .overlay.dark > .fab,\n.overlay-wrapper .overlay.dark > .fal,\n.overlay-wrapper .overlay.dark > .fad,\n.overlay-wrapper .overlay.dark > .svg-inline--fa,\n.overlay-wrapper .overlay.dark > .ion,\n.info-box .overlay.dark > .fa,\n.info-box .overlay.dark > .fas,\n.info-box .overlay.dark > .far,\n.info-box .overlay.dark > .fab,\n.info-box .overlay.dark > .fal,\n.info-box .overlay.dark > .fad,\n.info-box .overlay.dark > .svg-inline--fa,\n.info-box .overlay.dark > .ion,\n.small-box .overlay.dark > .fa,\n.small-box .overlay.dark > .fas,\n.small-box .overlay.dark > .far,\n.small-box .overlay.dark > .fab,\n.small-box .overlay.dark > .fal,\n.small-box .overlay.dark > .fad,\n.small-box .overlay.dark > .svg-inline--fa,\n.small-box .overlay.dark > .ion {\n  color: #ced4da;\n}\n\n.tab-pane > .overlay-wrapper {\n  position: relative;\n}\n\n.tab-pane > .overlay-wrapper > .overlay {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  margin-top: -1.25rem;\n  margin-left: -1.25rem;\n  height: calc(100% + 2 * 1.25rem);\n  width: calc(100% + 2 * 1.25rem);\n}\n\n.tab-pane > .overlay-wrapper > .overlay.dark {\n  color: #fff;\n}\n\n.ribbon-wrapper {\n  height: 70px;\n  overflow: hidden;\n  position: absolute;\n  right: -2px;\n  top: -2px;\n  width: 70px;\n  z-index: 10;\n}\n\n.ribbon-wrapper.ribbon-lg {\n  height: 120px;\n  width: 120px;\n}\n\n.ribbon-wrapper.ribbon-lg .ribbon {\n  right: 0;\n  top: 26px;\n  width: 160px;\n}\n\n.ribbon-wrapper.ribbon-xl {\n  height: 180px;\n  width: 180px;\n}\n\n.ribbon-wrapper.ribbon-xl .ribbon {\n  right: 4px;\n  top: 47px;\n  width: 240px;\n}\n\n.ribbon-wrapper .ribbon {\n  box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);\n  font-size: 0.8rem;\n  line-height: 100%;\n  padding: 0.375rem 0;\n  position: relative;\n  right: -2px;\n  text-align: center;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);\n  text-transform: uppercase;\n  top: 10px;\n  -webkit-transform: rotate(45deg);\n  transform: rotate(45deg);\n  width: 90px;\n}\n\n.ribbon-wrapper .ribbon::before, .ribbon-wrapper .ribbon::after {\n  border-left: 3px solid transparent;\n  border-right: 3px solid transparent;\n  border-top: 3px solid #9e9e9e;\n  bottom: -3px;\n  content: \"\";\n  position: absolute;\n}\n\n.ribbon-wrapper .ribbon::before {\n  left: 0;\n}\n\n.ribbon-wrapper .ribbon::after {\n  right: 0;\n}\n\n.back-to-top {\n  bottom: 1.25rem;\n  position: fixed;\n  right: 1.25rem;\n  z-index: 1032;\n}\n\n.back-to-top:focus {\n  box-shadow: none;\n}\n\npre {\n  padding: .75rem;\n}\n\nblockquote {\n  background-color: #fff;\n  border-left: 0.7rem solid #007bff;\n  margin: 1.5em .7rem;\n  padding: .5em .7rem;\n}\n\n.box blockquote {\n  background-color: #e9ecef;\n}\n\nblockquote p:last-child {\n  margin-bottom: 0;\n}\n\nblockquote h1,\nblockquote h2,\nblockquote h3,\nblockquote h4,\nblockquote h5,\nblockquote h6 {\n  color: #007bff;\n  font-size: 1.25rem;\n  font-weight: 600;\n}\n\nblockquote.quote-primary {\n  border-color: #007bff;\n}\n\nblockquote.quote-primary h1,\nblockquote.quote-primary h2,\nblockquote.quote-primary h3,\nblockquote.quote-primary h4,\nblockquote.quote-primary h5,\nblockquote.quote-primary h6 {\n  color: #007bff;\n}\n\nblockquote.quote-secondary {\n  border-color: #6c757d;\n}\n\nblockquote.quote-secondary h1,\nblockquote.quote-secondary h2,\nblockquote.quote-secondary h3,\nblockquote.quote-secondary h4,\nblockquote.quote-secondary h5,\nblockquote.quote-secondary h6 {\n  color: #6c757d;\n}\n\nblockquote.quote-success {\n  border-color: #28a745;\n}\n\nblockquote.quote-success h1,\nblockquote.quote-success h2,\nblockquote.quote-success h3,\nblockquote.quote-success h4,\nblockquote.quote-success h5,\nblockquote.quote-success h6 {\n  color: #28a745;\n}\n\nblockquote.quote-info {\n  border-color: #17a2b8;\n}\n\nblockquote.quote-info h1,\nblockquote.quote-info h2,\nblockquote.quote-info h3,\nblockquote.quote-info h4,\nblockquote.quote-info h5,\nblockquote.quote-info h6 {\n  color: #17a2b8;\n}\n\nblockquote.quote-warning {\n  border-color: #ffc107;\n}\n\nblockquote.quote-warning h1,\nblockquote.quote-warning h2,\nblockquote.quote-warning h3,\nblockquote.quote-warning h4,\nblockquote.quote-warning h5,\nblockquote.quote-warning h6 {\n  color: #ffc107;\n}\n\nblockquote.quote-danger {\n  border-color: #dc3545;\n}\n\nblockquote.quote-danger h1,\nblockquote.quote-danger h2,\nblockquote.quote-danger h3,\nblockquote.quote-danger h4,\nblockquote.quote-danger h5,\nblockquote.quote-danger h6 {\n  color: #dc3545;\n}\n\nblockquote.quote-light {\n  border-color: #f8f9fa;\n}\n\nblockquote.quote-light h1,\nblockquote.quote-light h2,\nblockquote.quote-light h3,\nblockquote.quote-light h4,\nblockquote.quote-light h5,\nblockquote.quote-light h6 {\n  color: #f8f9fa;\n}\n\nblockquote.quote-dark {\n  border-color: #343a40;\n}\n\nblockquote.quote-dark h1,\nblockquote.quote-dark h2,\nblockquote.quote-dark h3,\nblockquote.quote-dark h4,\nblockquote.quote-dark h5,\nblockquote.quote-dark h6 {\n  color: #343a40;\n}\n\nblockquote.quote-lightblue {\n  border-color: #3c8dbc;\n}\n\nblockquote.quote-lightblue h1,\nblockquote.quote-lightblue h2,\nblockquote.quote-lightblue h3,\nblockquote.quote-lightblue h4,\nblockquote.quote-lightblue h5,\nblockquote.quote-lightblue h6 {\n  color: #3c8dbc;\n}\n\nblockquote.quote-navy {\n  border-color: #001f3f;\n}\n\nblockquote.quote-navy h1,\nblockquote.quote-navy h2,\nblockquote.quote-navy h3,\nblockquote.quote-navy h4,\nblockquote.quote-navy h5,\nblockquote.quote-navy h6 {\n  color: #001f3f;\n}\n\nblockquote.quote-olive {\n  border-color: #3d9970;\n}\n\nblockquote.quote-olive h1,\nblockquote.quote-olive h2,\nblockquote.quote-olive h3,\nblockquote.quote-olive h4,\nblockquote.quote-olive h5,\nblockquote.quote-olive h6 {\n  color: #3d9970;\n}\n\nblockquote.quote-lime {\n  border-color: #01ff70;\n}\n\nblockquote.quote-lime h1,\nblockquote.quote-lime h2,\nblockquote.quote-lime h3,\nblockquote.quote-lime h4,\nblockquote.quote-lime h5,\nblockquote.quote-lime h6 {\n  color: #01ff70;\n}\n\nblockquote.quote-fuchsia {\n  border-color: #f012be;\n}\n\nblockquote.quote-fuchsia h1,\nblockquote.quote-fuchsia h2,\nblockquote.quote-fuchsia h3,\nblockquote.quote-fuchsia h4,\nblockquote.quote-fuchsia h5,\nblockquote.quote-fuchsia h6 {\n  color: #f012be;\n}\n\nblockquote.quote-maroon {\n  border-color: #d81b60;\n}\n\nblockquote.quote-maroon h1,\nblockquote.quote-maroon h2,\nblockquote.quote-maroon h3,\nblockquote.quote-maroon h4,\nblockquote.quote-maroon h5,\nblockquote.quote-maroon h6 {\n  color: #d81b60;\n}\n\nblockquote.quote-blue {\n  border-color: #007bff;\n}\n\nblockquote.quote-blue h1,\nblockquote.quote-blue h2,\nblockquote.quote-blue h3,\nblockquote.quote-blue h4,\nblockquote.quote-blue h5,\nblockquote.quote-blue h6 {\n  color: #007bff;\n}\n\nblockquote.quote-indigo {\n  border-color: #6610f2;\n}\n\nblockquote.quote-indigo h1,\nblockquote.quote-indigo h2,\nblockquote.quote-indigo h3,\nblockquote.quote-indigo h4,\nblockquote.quote-indigo h5,\nblockquote.quote-indigo h6 {\n  color: #6610f2;\n}\n\nblockquote.quote-purple {\n  border-color: #6f42c1;\n}\n\nblockquote.quote-purple h1,\nblockquote.quote-purple h2,\nblockquote.quote-purple h3,\nblockquote.quote-purple h4,\nblockquote.quote-purple h5,\nblockquote.quote-purple h6 {\n  color: #6f42c1;\n}\n\nblockquote.quote-pink {\n  border-color: #e83e8c;\n}\n\nblockquote.quote-pink h1,\nblockquote.quote-pink h2,\nblockquote.quote-pink h3,\nblockquote.quote-pink h4,\nblockquote.quote-pink h5,\nblockquote.quote-pink h6 {\n  color: #e83e8c;\n}\n\nblockquote.quote-red {\n  border-color: #dc3545;\n}\n\nblockquote.quote-red h1,\nblockquote.quote-red h2,\nblockquote.quote-red h3,\nblockquote.quote-red h4,\nblockquote.quote-red h5,\nblockquote.quote-red h6 {\n  color: #dc3545;\n}\n\nblockquote.quote-orange {\n  border-color: #fd7e14;\n}\n\nblockquote.quote-orange h1,\nblockquote.quote-orange h2,\nblockquote.quote-orange h3,\nblockquote.quote-orange h4,\nblockquote.quote-orange h5,\nblockquote.quote-orange h6 {\n  color: #fd7e14;\n}\n\nblockquote.quote-yellow {\n  border-color: #ffc107;\n}\n\nblockquote.quote-yellow h1,\nblockquote.quote-yellow h2,\nblockquote.quote-yellow h3,\nblockquote.quote-yellow h4,\nblockquote.quote-yellow h5,\nblockquote.quote-yellow h6 {\n  color: #ffc107;\n}\n\nblockquote.quote-green {\n  border-color: #28a745;\n}\n\nblockquote.quote-green h1,\nblockquote.quote-green h2,\nblockquote.quote-green h3,\nblockquote.quote-green h4,\nblockquote.quote-green h5,\nblockquote.quote-green h6 {\n  color: #28a745;\n}\n\nblockquote.quote-teal {\n  border-color: #20c997;\n}\n\nblockquote.quote-teal h1,\nblockquote.quote-teal h2,\nblockquote.quote-teal h3,\nblockquote.quote-teal h4,\nblockquote.quote-teal h5,\nblockquote.quote-teal h6 {\n  color: #20c997;\n}\n\nblockquote.quote-cyan {\n  border-color: #17a2b8;\n}\n\nblockquote.quote-cyan h1,\nblockquote.quote-cyan h2,\nblockquote.quote-cyan h3,\nblockquote.quote-cyan h4,\nblockquote.quote-cyan h5,\nblockquote.quote-cyan h6 {\n  color: #17a2b8;\n}\n\nblockquote.quote-white {\n  border-color: #fff;\n}\n\nblockquote.quote-white h1,\nblockquote.quote-white h2,\nblockquote.quote-white h3,\nblockquote.quote-white h4,\nblockquote.quote-white h5,\nblockquote.quote-white h6 {\n  color: #fff;\n}\n\nblockquote.quote-gray {\n  border-color: #6c757d;\n}\n\nblockquote.quote-gray h1,\nblockquote.quote-gray h2,\nblockquote.quote-gray h3,\nblockquote.quote-gray h4,\nblockquote.quote-gray h5,\nblockquote.quote-gray h6 {\n  color: #6c757d;\n}\n\nblockquote.quote-gray-dark {\n  border-color: #343a40;\n}\n\nblockquote.quote-gray-dark h1,\nblockquote.quote-gray-dark h2,\nblockquote.quote-gray-dark h3,\nblockquote.quote-gray-dark h4,\nblockquote.quote-gray-dark h5,\nblockquote.quote-gray-dark h6 {\n  color: #343a40;\n}\n\n.tab-custom-content {\n  border-top: 1px solid #dee2e6;\n  margin-top: .5rem;\n  padding-top: .5rem;\n}\n\n.nav + .tab-custom-content {\n  border-top: none;\n  border-bottom: 1px solid #dee2e6;\n  margin-top: 0;\n  margin-bottom: .5rem;\n  padding-bottom: .5rem;\n}\n\n.badge-btn {\n  border-radius: 0.15rem;\n  font-size: 0.75rem;\n  font-weight: 400;\n  padding: 0.25rem 0.5rem;\n}\n\n.badge-btn.badge-pill {\n  padding: .375rem .6rem;\n}\n\n.dark-mode a:not(.btn):hover {\n  color: #3395ff;\n}\n\n.dark-mode .attachment-block {\n  background-color: #3d444b;\n}\n\n.dark-mode .attachment-block .attachment-text {\n  color: #ced4da;\n}\n\n.dark-mode blockquote {\n  background-color: #3f474e;\n}\n\n.dark-mode blockquote.quote-primary {\n  border-color: #007bff;\n}\n\n.dark-mode blockquote.quote-primary h1,\n.dark-mode blockquote.quote-primary h2,\n.dark-mode blockquote.quote-primary h3,\n.dark-mode blockquote.quote-primary h4,\n.dark-mode blockquote.quote-primary h5,\n.dark-mode blockquote.quote-primary h6 {\n  color: #007bff;\n}\n\n.dark-mode blockquote.quote-secondary {\n  border-color: #6c757d;\n}\n\n.dark-mode blockquote.quote-secondary h1,\n.dark-mode blockquote.quote-secondary h2,\n.dark-mode blockquote.quote-secondary h3,\n.dark-mode blockquote.quote-secondary h4,\n.dark-mode blockquote.quote-secondary h5,\n.dark-mode blockquote.quote-secondary h6 {\n  color: #6c757d;\n}\n\n.dark-mode blockquote.quote-success {\n  border-color: #28a745;\n}\n\n.dark-mode blockquote.quote-success h1,\n.dark-mode blockquote.quote-success h2,\n.dark-mode blockquote.quote-success h3,\n.dark-mode blockquote.quote-success h4,\n.dark-mode blockquote.quote-success h5,\n.dark-mode blockquote.quote-success h6 {\n  color: #28a745;\n}\n\n.dark-mode blockquote.quote-info {\n  border-color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-info h1,\n.dark-mode blockquote.quote-info h2,\n.dark-mode blockquote.quote-info h3,\n.dark-mode blockquote.quote-info h4,\n.dark-mode blockquote.quote-info h5,\n.dark-mode blockquote.quote-info h6 {\n  color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-warning {\n  border-color: #ffc107;\n}\n\n.dark-mode blockquote.quote-warning h1,\n.dark-mode blockquote.quote-warning h2,\n.dark-mode blockquote.quote-warning h3,\n.dark-mode blockquote.quote-warning h4,\n.dark-mode blockquote.quote-warning h5,\n.dark-mode blockquote.quote-warning h6 {\n  color: #ffc107;\n}\n\n.dark-mode blockquote.quote-danger {\n  border-color: #dc3545;\n}\n\n.dark-mode blockquote.quote-danger h1,\n.dark-mode blockquote.quote-danger h2,\n.dark-mode blockquote.quote-danger h3,\n.dark-mode blockquote.quote-danger h4,\n.dark-mode blockquote.quote-danger h5,\n.dark-mode blockquote.quote-danger h6 {\n  color: #dc3545;\n}\n\n.dark-mode blockquote.quote-light {\n  border-color: #f8f9fa;\n}\n\n.dark-mode blockquote.quote-light h1,\n.dark-mode blockquote.quote-light h2,\n.dark-mode blockquote.quote-light h3,\n.dark-mode blockquote.quote-light h4,\n.dark-mode blockquote.quote-light h5,\n.dark-mode blockquote.quote-light h6 {\n  color: #f8f9fa;\n}\n\n.dark-mode blockquote.quote-dark {\n  border-color: #343a40;\n}\n\n.dark-mode blockquote.quote-dark h1,\n.dark-mode blockquote.quote-dark h2,\n.dark-mode blockquote.quote-dark h3,\n.dark-mode blockquote.quote-dark h4,\n.dark-mode blockquote.quote-dark h5,\n.dark-mode blockquote.quote-dark h6 {\n  color: #343a40;\n}\n\n.dark-mode blockquote.quote-lightblue {\n  border-color: #3c8dbc;\n}\n\n.dark-mode blockquote.quote-lightblue h1,\n.dark-mode blockquote.quote-lightblue h2,\n.dark-mode blockquote.quote-lightblue h3,\n.dark-mode blockquote.quote-lightblue h4,\n.dark-mode blockquote.quote-lightblue h5,\n.dark-mode blockquote.quote-lightblue h6 {\n  color: #3c8dbc;\n}\n\n.dark-mode blockquote.quote-navy {\n  border-color: #001f3f;\n}\n\n.dark-mode blockquote.quote-navy h1,\n.dark-mode blockquote.quote-navy h2,\n.dark-mode blockquote.quote-navy h3,\n.dark-mode blockquote.quote-navy h4,\n.dark-mode blockquote.quote-navy h5,\n.dark-mode blockquote.quote-navy h6 {\n  color: #001f3f;\n}\n\n.dark-mode blockquote.quote-olive {\n  border-color: #3d9970;\n}\n\n.dark-mode blockquote.quote-olive h1,\n.dark-mode blockquote.quote-olive h2,\n.dark-mode blockquote.quote-olive h3,\n.dark-mode blockquote.quote-olive h4,\n.dark-mode blockquote.quote-olive h5,\n.dark-mode blockquote.quote-olive h6 {\n  color: #3d9970;\n}\n\n.dark-mode blockquote.quote-lime {\n  border-color: #01ff70;\n}\n\n.dark-mode blockquote.quote-lime h1,\n.dark-mode blockquote.quote-lime h2,\n.dark-mode blockquote.quote-lime h3,\n.dark-mode blockquote.quote-lime h4,\n.dark-mode blockquote.quote-lime h5,\n.dark-mode blockquote.quote-lime h6 {\n  color: #01ff70;\n}\n\n.dark-mode blockquote.quote-fuchsia {\n  border-color: #f012be;\n}\n\n.dark-mode blockquote.quote-fuchsia h1,\n.dark-mode blockquote.quote-fuchsia h2,\n.dark-mode blockquote.quote-fuchsia h3,\n.dark-mode blockquote.quote-fuchsia h4,\n.dark-mode blockquote.quote-fuchsia h5,\n.dark-mode blockquote.quote-fuchsia h6 {\n  color: #f012be;\n}\n\n.dark-mode blockquote.quote-maroon {\n  border-color: #d81b60;\n}\n\n.dark-mode blockquote.quote-maroon h1,\n.dark-mode blockquote.quote-maroon h2,\n.dark-mode blockquote.quote-maroon h3,\n.dark-mode blockquote.quote-maroon h4,\n.dark-mode blockquote.quote-maroon h5,\n.dark-mode blockquote.quote-maroon h6 {\n  color: #d81b60;\n}\n\n.dark-mode blockquote.quote-blue {\n  border-color: #007bff;\n}\n\n.dark-mode blockquote.quote-blue h1,\n.dark-mode blockquote.quote-blue h2,\n.dark-mode blockquote.quote-blue h3,\n.dark-mode blockquote.quote-blue h4,\n.dark-mode blockquote.quote-blue h5,\n.dark-mode blockquote.quote-blue h6 {\n  color: #007bff;\n}\n\n.dark-mode blockquote.quote-indigo {\n  border-color: #6610f2;\n}\n\n.dark-mode blockquote.quote-indigo h1,\n.dark-mode blockquote.quote-indigo h2,\n.dark-mode blockquote.quote-indigo h3,\n.dark-mode blockquote.quote-indigo h4,\n.dark-mode blockquote.quote-indigo h5,\n.dark-mode blockquote.quote-indigo h6 {\n  color: #6610f2;\n}\n\n.dark-mode blockquote.quote-purple {\n  border-color: #6f42c1;\n}\n\n.dark-mode blockquote.quote-purple h1,\n.dark-mode blockquote.quote-purple h2,\n.dark-mode blockquote.quote-purple h3,\n.dark-mode blockquote.quote-purple h4,\n.dark-mode blockquote.quote-purple h5,\n.dark-mode blockquote.quote-purple h6 {\n  color: #6f42c1;\n}\n\n.dark-mode blockquote.quote-pink {\n  border-color: #e83e8c;\n}\n\n.dark-mode blockquote.quote-pink h1,\n.dark-mode blockquote.quote-pink h2,\n.dark-mode blockquote.quote-pink h3,\n.dark-mode blockquote.quote-pink h4,\n.dark-mode blockquote.quote-pink h5,\n.dark-mode blockquote.quote-pink h6 {\n  color: #e83e8c;\n}\n\n.dark-mode blockquote.quote-red {\n  border-color: #dc3545;\n}\n\n.dark-mode blockquote.quote-red h1,\n.dark-mode blockquote.quote-red h2,\n.dark-mode blockquote.quote-red h3,\n.dark-mode blockquote.quote-red h4,\n.dark-mode blockquote.quote-red h5,\n.dark-mode blockquote.quote-red h6 {\n  color: #dc3545;\n}\n\n.dark-mode blockquote.quote-orange {\n  border-color: #fd7e14;\n}\n\n.dark-mode blockquote.quote-orange h1,\n.dark-mode blockquote.quote-orange h2,\n.dark-mode blockquote.quote-orange h3,\n.dark-mode blockquote.quote-orange h4,\n.dark-mode blockquote.quote-orange h5,\n.dark-mode blockquote.quote-orange h6 {\n  color: #fd7e14;\n}\n\n.dark-mode blockquote.quote-yellow {\n  border-color: #ffc107;\n}\n\n.dark-mode blockquote.quote-yellow h1,\n.dark-mode blockquote.quote-yellow h2,\n.dark-mode blockquote.quote-yellow h3,\n.dark-mode blockquote.quote-yellow h4,\n.dark-mode blockquote.quote-yellow h5,\n.dark-mode blockquote.quote-yellow h6 {\n  color: #ffc107;\n}\n\n.dark-mode blockquote.quote-green {\n  border-color: #28a745;\n}\n\n.dark-mode blockquote.quote-green h1,\n.dark-mode blockquote.quote-green h2,\n.dark-mode blockquote.quote-green h3,\n.dark-mode blockquote.quote-green h4,\n.dark-mode blockquote.quote-green h5,\n.dark-mode blockquote.quote-green h6 {\n  color: #28a745;\n}\n\n.dark-mode blockquote.quote-teal {\n  border-color: #20c997;\n}\n\n.dark-mode blockquote.quote-teal h1,\n.dark-mode blockquote.quote-teal h2,\n.dark-mode blockquote.quote-teal h3,\n.dark-mode blockquote.quote-teal h4,\n.dark-mode blockquote.quote-teal h5,\n.dark-mode blockquote.quote-teal h6 {\n  color: #20c997;\n}\n\n.dark-mode blockquote.quote-cyan {\n  border-color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-cyan h1,\n.dark-mode blockquote.quote-cyan h2,\n.dark-mode blockquote.quote-cyan h3,\n.dark-mode blockquote.quote-cyan h4,\n.dark-mode blockquote.quote-cyan h5,\n.dark-mode blockquote.quote-cyan h6 {\n  color: #17a2b8;\n}\n\n.dark-mode blockquote.quote-white {\n  border-color: #fff;\n}\n\n.dark-mode blockquote.quote-white h1,\n.dark-mode blockquote.quote-white h2,\n.dark-mode blockquote.quote-white h3,\n.dark-mode blockquote.quote-white h4,\n.dark-mode blockquote.quote-white h5,\n.dark-mode blockquote.quote-white h6 {\n  color: #fff;\n}\n\n.dark-mode blockquote.quote-gray {\n  border-color: #6c757d;\n}\n\n.dark-mode blockquote.quote-gray h1,\n.dark-mode blockquote.quote-gray h2,\n.dark-mode blockquote.quote-gray h3,\n.dark-mode blockquote.quote-gray h4,\n.dark-mode blockquote.quote-gray h5,\n.dark-mode blockquote.quote-gray h6 {\n  color: #6c757d;\n}\n\n.dark-mode blockquote.quote-gray-dark {\n  border-color: #343a40;\n}\n\n.dark-mode blockquote.quote-gray-dark h1,\n.dark-mode blockquote.quote-gray-dark h2,\n.dark-mode blockquote.quote-gray-dark h3,\n.dark-mode blockquote.quote-gray-dark h4,\n.dark-mode blockquote.quote-gray-dark h5,\n.dark-mode blockquote.quote-gray-dark h6 {\n  color: #343a40;\n}\n\n.dark-mode .close,\n.dark-mode .mailbox-attachment-close {\n  color: #adb5bd;\n  text-shadow: 0 1px 0 #495057;\n}\n\n.dark-mode .tab-custom-content {\n  border-color: #6c757d;\n}\n\n.dark-mode .list-group-item {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n@media print {\n  .no-print, .main-sidebar,\n  .main-header,\n  .content-header {\n    display: none !important;\n  }\n  .content-wrapper,\n  .main-footer {\n    -webkit-transform: translate(0, 0);\n    transform: translate(0, 0);\n    margin-left: 0 !important;\n    min-height: 0 !important;\n  }\n  .layout-fixed .content-wrapper {\n    padding-top: 0 !important;\n  }\n  .invoice {\n    border: 0;\n    margin: 0;\n    padding: 0;\n    width: 100%;\n  }\n  .invoice-col {\n    float: left;\n    width: 33.3333333%;\n  }\n  .table-responsive {\n    overflow: auto;\n  }\n  .table-responsive > .table tr th,\n  .table-responsive > .table tr td {\n    white-space: normal !important;\n  }\n}\n\n.text-bold,\n.text-bold.table td,\n.text-bold.table th {\n  font-weight: 700;\n}\n\n.text-xs {\n  font-size: 0.75rem !important;\n}\n\n.text-sm {\n  font-size: 0.875rem !important;\n}\n\n.text-md {\n  font-size: 1rem !important;\n}\n\n.text-lg {\n  font-size: 1.25rem !important;\n}\n\n.text-xl {\n  font-size: 2rem !important;\n}\n\n.text-lightblue {\n  color: #3c8dbc !important;\n}\n\n.text-navy {\n  color: #001f3f !important;\n}\n\n.text-olive {\n  color: #3d9970 !important;\n}\n\n.text-lime {\n  color: #01ff70 !important;\n}\n\n.text-fuchsia {\n  color: #f012be !important;\n}\n\n.text-maroon {\n  color: #d81b60 !important;\n}\n\n.text-blue {\n  color: #007bff !important;\n}\n\n.text-indigo {\n  color: #6610f2 !important;\n}\n\n.text-purple {\n  color: #6f42c1 !important;\n}\n\n.text-pink {\n  color: #e83e8c !important;\n}\n\n.text-red {\n  color: #dc3545 !important;\n}\n\n.text-orange {\n  color: #fd7e14 !important;\n}\n\n.text-yellow {\n  color: #ffc107 !important;\n}\n\n.text-green {\n  color: #28a745 !important;\n}\n\n.text-teal {\n  color: #20c997 !important;\n}\n\n.text-cyan {\n  color: #17a2b8 !important;\n}\n\n.text-white {\n  color: #fff !important;\n}\n\n.text-gray {\n  color: #6c757d !important;\n}\n\n.text-gray-dark {\n  color: #343a40 !important;\n}\n\n.dark-mode .text-muted {\n  color: #adb5bd !important;\n}\n\n.dark-mode .text-lightblue {\n  color: #86bad8 !important;\n}\n\n.dark-mode .text-navy {\n  color: #002c59 !important;\n}\n\n.dark-mode .text-olive {\n  color: #74c8a3 !important;\n}\n\n.dark-mode .text-lime {\n  color: #67ffa9 !important;\n}\n\n.dark-mode .text-fuchsia {\n  color: #f672d8 !important;\n}\n\n.dark-mode .text-maroon {\n  color: #ed6c9b !important;\n}\n\n.dark-mode .text-blue {\n  color: #3f6791 !important;\n}\n\n.dark-mode .text-indigo {\n  color: #6610f2 !important;\n}\n\n.dark-mode .text-purple {\n  color: #6f42c1 !important;\n}\n\n.dark-mode .text-pink {\n  color: #e83e8c !important;\n}\n\n.dark-mode .text-red {\n  color: #e74c3c !important;\n}\n\n.dark-mode .text-orange {\n  color: #fd7e14 !important;\n}\n\n.dark-mode .text-yellow {\n  color: #f39c12 !important;\n}\n\n.dark-mode .text-green {\n  color: #00bc8c !important;\n}\n\n.dark-mode .text-teal {\n  color: #20c997 !important;\n}\n\n.dark-mode .text-cyan {\n  color: #3498db !important;\n}\n\n.dark-mode .text-white {\n  color: #fff !important;\n}\n\n.dark-mode .text-gray {\n  color: #6c757d !important;\n}\n\n.dark-mode .text-gray-dark {\n  color: #343a40 !important;\n}\n\n.elevation-0 {\n  box-shadow: none !important;\n}\n\n.elevation-1 {\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) !important;\n}\n\n.elevation-2 {\n  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) !important;\n}\n\n.elevation-3 {\n  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) !important;\n}\n\n.elevation-4 {\n  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22) !important;\n}\n\n.elevation-5 {\n  box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22) !important;\n}\n\n.bg-primary {\n  background-color: #007bff !important;\n}\n\n.bg-primary,\n.bg-primary > a {\n  color: #fff !important;\n}\n\n.bg-primary.btn:hover {\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-primary.btn:not(:disabled):not(.disabled):active, .bg-primary.btn:not(:disabled):not(.disabled).active, .bg-primary.btn:active, .bg-primary.btn.active {\n  background-color: #0062cc !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-secondary {\n  background-color: #6c757d !important;\n}\n\n.bg-secondary,\n.bg-secondary > a {\n  color: #fff !important;\n}\n\n.bg-secondary.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-secondary.btn:not(:disabled):not(.disabled):active, .bg-secondary.btn:not(:disabled):not(.disabled).active, .bg-secondary.btn:active, .bg-secondary.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-success {\n  background-color: #28a745 !important;\n}\n\n.bg-success,\n.bg-success > a {\n  color: #fff !important;\n}\n\n.bg-success.btn:hover {\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-success.btn:not(:disabled):not(.disabled):active, .bg-success.btn:not(:disabled):not(.disabled).active, .bg-success.btn:active, .bg-success.btn.active {\n  background-color: #1e7e34 !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-info {\n  background-color: #17a2b8 !important;\n}\n\n.bg-info,\n.bg-info > a {\n  color: #fff !important;\n}\n\n.bg-info.btn:hover {\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-info.btn:not(:disabled):not(.disabled):active, .bg-info.btn:not(:disabled):not(.disabled).active, .bg-info.btn:active, .bg-info.btn.active {\n  background-color: #117a8b !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-warning {\n  background-color: #ffc107 !important;\n}\n\n.bg-warning,\n.bg-warning > a {\n  color: #1f2d3d !important;\n}\n\n.bg-warning.btn:hover {\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-warning.btn:not(:disabled):not(.disabled):active, .bg-warning.btn:not(:disabled):not(.disabled).active, .bg-warning.btn:active, .bg-warning.btn.active {\n  background-color: #d39e00 !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-danger {\n  background-color: #dc3545 !important;\n}\n\n.bg-danger,\n.bg-danger > a {\n  color: #fff !important;\n}\n\n.bg-danger.btn:hover {\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-danger.btn:not(:disabled):not(.disabled):active, .bg-danger.btn:not(:disabled):not(.disabled).active, .bg-danger.btn:active, .bg-danger.btn.active {\n  background-color: #bd2130 !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-light {\n  background-color: #f8f9fa !important;\n}\n\n.bg-light,\n.bg-light > a {\n  color: #1f2d3d !important;\n}\n\n.bg-light.btn:hover {\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.bg-light.btn:not(:disabled):not(.disabled):active, .bg-light.btn:not(:disabled):not(.disabled).active, .bg-light.btn:active, .bg-light.btn.active {\n  background-color: #dae0e5 !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.bg-dark {\n  background-color: #343a40 !important;\n}\n\n.bg-dark,\n.bg-dark > a {\n  color: #fff !important;\n}\n\n.bg-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-dark.btn:not(:disabled):not(.disabled):active, .bg-dark.btn:not(:disabled):not(.disabled).active, .bg-dark.btn:active, .bg-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-lightblue {\n  background-color: #3c8dbc !important;\n}\n\n.bg-lightblue,\n.bg-lightblue > a {\n  color: #fff !important;\n}\n\n.bg-lightblue.btn:hover {\n  border-color: #307095;\n  color: #ececec;\n}\n\n.bg-lightblue.btn:not(:disabled):not(.disabled):active, .bg-lightblue.btn:not(:disabled):not(.disabled).active, .bg-lightblue.btn:active, .bg-lightblue.btn.active {\n  background-color: #307095 !important;\n  border-color: #2d698c;\n  color: #fff;\n}\n\n.bg-navy {\n  background-color: #001f3f !important;\n}\n\n.bg-navy,\n.bg-navy > a {\n  color: #fff !important;\n}\n\n.bg-navy.btn:hover {\n  border-color: #00060c;\n  color: #ececec;\n}\n\n.bg-navy.btn:not(:disabled):not(.disabled):active, .bg-navy.btn:not(:disabled):not(.disabled).active, .bg-navy.btn:active, .bg-navy.btn.active {\n  background-color: #00060c !important;\n  border-color: black;\n  color: #fff;\n}\n\n.bg-olive {\n  background-color: #3d9970 !important;\n}\n\n.bg-olive,\n.bg-olive > a {\n  color: #fff !important;\n}\n\n.bg-olive.btn:hover {\n  border-color: #2e7555;\n  color: #ececec;\n}\n\n.bg-olive.btn:not(:disabled):not(.disabled):active, .bg-olive.btn:not(:disabled):not(.disabled).active, .bg-olive.btn:active, .bg-olive.btn.active {\n  background-color: #2e7555 !important;\n  border-color: #2b6b4f;\n  color: #fff;\n}\n\n.bg-lime {\n  background-color: #01ff70 !important;\n}\n\n.bg-lime,\n.bg-lime > a {\n  color: #1f2d3d !important;\n}\n\n.bg-lime.btn:hover {\n  border-color: #00cd5a;\n  color: #121a24;\n}\n\n.bg-lime.btn:not(:disabled):not(.disabled):active, .bg-lime.btn:not(:disabled):not(.disabled).active, .bg-lime.btn:active, .bg-lime.btn.active {\n  background-color: #00cd5a !important;\n  border-color: #00c054;\n  color: #fff;\n}\n\n.bg-fuchsia {\n  background-color: #f012be !important;\n}\n\n.bg-fuchsia,\n.bg-fuchsia > a {\n  color: #fff !important;\n}\n\n.bg-fuchsia.btn:hover {\n  border-color: #c30c9a;\n  color: #ececec;\n}\n\n.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-fuchsia.btn:not(:disabled):not(.disabled).active, .bg-fuchsia.btn:active, .bg-fuchsia.btn.active {\n  background-color: #c30c9a !important;\n  border-color: #b70c90;\n  color: #fff;\n}\n\n.bg-maroon {\n  background-color: #d81b60 !important;\n}\n\n.bg-maroon,\n.bg-maroon > a {\n  color: #fff !important;\n}\n\n.bg-maroon.btn:hover {\n  border-color: #ab154c;\n  color: #ececec;\n}\n\n.bg-maroon.btn:not(:disabled):not(.disabled):active, .bg-maroon.btn:not(:disabled):not(.disabled).active, .bg-maroon.btn:active, .bg-maroon.btn.active {\n  background-color: #ab154c !important;\n  border-color: #9f1447;\n  color: #fff;\n}\n\n.bg-blue {\n  background-color: #007bff !important;\n}\n\n.bg-blue,\n.bg-blue > a {\n  color: #fff !important;\n}\n\n.bg-blue.btn:hover {\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-blue.btn:not(:disabled):not(.disabled):active, .bg-blue.btn:not(:disabled):not(.disabled).active, .bg-blue.btn:active, .bg-blue.btn.active {\n  background-color: #0062cc !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-indigo {\n  background-color: #6610f2 !important;\n}\n\n.bg-indigo,\n.bg-indigo > a {\n  color: #fff !important;\n}\n\n.bg-indigo.btn:hover {\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.bg-indigo.btn:not(:disabled):not(.disabled):active, .bg-indigo.btn:not(:disabled):not(.disabled).active, .bg-indigo.btn:active, .bg-indigo.btn.active {\n  background-color: #510bc4 !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.bg-purple {\n  background-color: #6f42c1 !important;\n}\n\n.bg-purple,\n.bg-purple > a {\n  color: #fff !important;\n}\n\n.bg-purple.btn:hover {\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.bg-purple.btn:not(:disabled):not(.disabled):active, .bg-purple.btn:not(:disabled):not(.disabled).active, .bg-purple.btn:active, .bg-purple.btn.active {\n  background-color: #59339d !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.bg-pink {\n  background-color: #e83e8c !important;\n}\n\n.bg-pink,\n.bg-pink > a {\n  color: #fff !important;\n}\n\n.bg-pink.btn:hover {\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.bg-pink.btn:not(:disabled):not(.disabled):active, .bg-pink.btn:not(:disabled):not(.disabled).active, .bg-pink.btn:active, .bg-pink.btn.active {\n  background-color: #d91a72 !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.bg-red {\n  background-color: #dc3545 !important;\n}\n\n.bg-red,\n.bg-red > a {\n  color: #fff !important;\n}\n\n.bg-red.btn:hover {\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-red.btn:not(:disabled):not(.disabled):active, .bg-red.btn:not(:disabled):not(.disabled).active, .bg-red.btn:active, .bg-red.btn.active {\n  background-color: #bd2130 !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-orange {\n  background-color: #fd7e14 !important;\n}\n\n.bg-orange,\n.bg-orange > a {\n  color: #1f2d3d !important;\n}\n\n.bg-orange.btn:hover {\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.bg-orange.btn:not(:disabled):not(.disabled):active, .bg-orange.btn:not(:disabled):not(.disabled).active, .bg-orange.btn:active, .bg-orange.btn.active {\n  background-color: #dc6502 !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.bg-yellow {\n  background-color: #ffc107 !important;\n}\n\n.bg-yellow,\n.bg-yellow > a {\n  color: #1f2d3d !important;\n}\n\n.bg-yellow.btn:hover {\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-yellow.btn:not(:disabled):not(.disabled):active, .bg-yellow.btn:not(:disabled):not(.disabled).active, .bg-yellow.btn:active, .bg-yellow.btn.active {\n  background-color: #d39e00 !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-green {\n  background-color: #28a745 !important;\n}\n\n.bg-green,\n.bg-green > a {\n  color: #fff !important;\n}\n\n.bg-green.btn:hover {\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-green.btn:not(:disabled):not(.disabled):active, .bg-green.btn:not(:disabled):not(.disabled).active, .bg-green.btn:active, .bg-green.btn.active {\n  background-color: #1e7e34 !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-teal {\n  background-color: #20c997 !important;\n}\n\n.bg-teal,\n.bg-teal > a {\n  color: #fff !important;\n}\n\n.bg-teal.btn:hover {\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.bg-teal.btn:not(:disabled):not(.disabled):active, .bg-teal.btn:not(:disabled):not(.disabled).active, .bg-teal.btn:active, .bg-teal.btn.active {\n  background-color: #199d76 !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.bg-cyan {\n  background-color: #17a2b8 !important;\n}\n\n.bg-cyan,\n.bg-cyan > a {\n  color: #fff !important;\n}\n\n.bg-cyan.btn:hover {\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-cyan.btn:not(:disabled):not(.disabled):active, .bg-cyan.btn:not(:disabled):not(.disabled).active, .bg-cyan.btn:active, .bg-cyan.btn.active {\n  background-color: #117a8b !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-white {\n  background-color: #fff !important;\n}\n\n.bg-white,\n.bg-white > a {\n  color: #1f2d3d !important;\n}\n\n.bg-white.btn:hover {\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.bg-white.btn:not(:disabled):not(.disabled):active, .bg-white.btn:not(:disabled):not(.disabled).active, .bg-white.btn:active, .bg-white.btn.active {\n  background-color: #e6e6e6 !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.bg-gray {\n  background-color: #6c757d !important;\n}\n\n.bg-gray,\n.bg-gray > a {\n  color: #fff !important;\n}\n\n.bg-gray.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gray.btn:not(:disabled):not(.disabled):active, .bg-gray.btn:not(:disabled):not(.disabled).active, .bg-gray.btn:active, .bg-gray.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gray-dark {\n  background-color: #343a40 !important;\n}\n\n.bg-gray-dark,\n.bg-gray-dark > a {\n  color: #fff !important;\n}\n\n.bg-gray-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gray-dark.btn:not(:disabled):not(.disabled).active, .bg-gray-dark.btn:active, .bg-gray-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n@media print {\n  .table td.bg-primary,\n  .table th.bg-primary {\n    background-color: #007bff !important;\n  }\n  .table td.bg-primary,\n  .table td.bg-primary > a,\n  .table th.bg-primary,\n  .table th.bg-primary > a {\n    color: #fff !important;\n  }\n  .table td.bg-primary.btn:hover,\n  .table th.bg-primary.btn:hover {\n    border-color: #0062cc;\n    color: #ececec;\n  }\n  .table td.bg-primary.btn:not(:disabled):not(.disabled):active, .table td.bg-primary.btn:not(:disabled):not(.disabled).active, .table td.bg-primary.btn:active, .table td.bg-primary.btn.active,\n  .table th.bg-primary.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-primary.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-primary.btn:active,\n  .table th.bg-primary.btn.active {\n    background-color: #0062cc !important;\n    border-color: #005cbf;\n    color: #fff;\n  }\n  .table td.bg-secondary,\n  .table th.bg-secondary {\n    background-color: #6c757d !important;\n  }\n  .table td.bg-secondary,\n  .table td.bg-secondary > a,\n  .table th.bg-secondary,\n  .table th.bg-secondary > a {\n    color: #fff !important;\n  }\n  .table td.bg-secondary.btn:hover,\n  .table th.bg-secondary.btn:hover {\n    border-color: #545b62;\n    color: #ececec;\n  }\n  .table td.bg-secondary.btn:not(:disabled):not(.disabled):active, .table td.bg-secondary.btn:not(:disabled):not(.disabled).active, .table td.bg-secondary.btn:active, .table td.bg-secondary.btn.active,\n  .table th.bg-secondary.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-secondary.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-secondary.btn:active,\n  .table th.bg-secondary.btn.active {\n    background-color: #545b62 !important;\n    border-color: #4e555b;\n    color: #fff;\n  }\n  .table td.bg-success,\n  .table th.bg-success {\n    background-color: #28a745 !important;\n  }\n  .table td.bg-success,\n  .table td.bg-success > a,\n  .table th.bg-success,\n  .table th.bg-success > a {\n    color: #fff !important;\n  }\n  .table td.bg-success.btn:hover,\n  .table th.bg-success.btn:hover {\n    border-color: #1e7e34;\n    color: #ececec;\n  }\n  .table td.bg-success.btn:not(:disabled):not(.disabled):active, .table td.bg-success.btn:not(:disabled):not(.disabled).active, .table td.bg-success.btn:active, .table td.bg-success.btn.active,\n  .table th.bg-success.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-success.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-success.btn:active,\n  .table th.bg-success.btn.active {\n    background-color: #1e7e34 !important;\n    border-color: #1c7430;\n    color: #fff;\n  }\n  .table td.bg-info,\n  .table th.bg-info {\n    background-color: #17a2b8 !important;\n  }\n  .table td.bg-info,\n  .table td.bg-info > a,\n  .table th.bg-info,\n  .table th.bg-info > a {\n    color: #fff !important;\n  }\n  .table td.bg-info.btn:hover,\n  .table th.bg-info.btn:hover {\n    border-color: #117a8b;\n    color: #ececec;\n  }\n  .table td.bg-info.btn:not(:disabled):not(.disabled):active, .table td.bg-info.btn:not(:disabled):not(.disabled).active, .table td.bg-info.btn:active, .table td.bg-info.btn.active,\n  .table th.bg-info.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-info.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-info.btn:active,\n  .table th.bg-info.btn.active {\n    background-color: #117a8b !important;\n    border-color: #10707f;\n    color: #fff;\n  }\n  .table td.bg-warning,\n  .table th.bg-warning {\n    background-color: #ffc107 !important;\n  }\n  .table td.bg-warning,\n  .table td.bg-warning > a,\n  .table th.bg-warning,\n  .table th.bg-warning > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-warning.btn:hover,\n  .table th.bg-warning.btn:hover {\n    border-color: #d39e00;\n    color: #121a24;\n  }\n  .table td.bg-warning.btn:not(:disabled):not(.disabled):active, .table td.bg-warning.btn:not(:disabled):not(.disabled).active, .table td.bg-warning.btn:active, .table td.bg-warning.btn.active,\n  .table th.bg-warning.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-warning.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-warning.btn:active,\n  .table th.bg-warning.btn.active {\n    background-color: #d39e00 !important;\n    border-color: #c69500;\n    color: #1f2d3d;\n  }\n  .table td.bg-danger,\n  .table th.bg-danger {\n    background-color: #dc3545 !important;\n  }\n  .table td.bg-danger,\n  .table td.bg-danger > a,\n  .table th.bg-danger,\n  .table th.bg-danger > a {\n    color: #fff !important;\n  }\n  .table td.bg-danger.btn:hover,\n  .table th.bg-danger.btn:hover {\n    border-color: #bd2130;\n    color: #ececec;\n  }\n  .table td.bg-danger.btn:not(:disabled):not(.disabled):active, .table td.bg-danger.btn:not(:disabled):not(.disabled).active, .table td.bg-danger.btn:active, .table td.bg-danger.btn.active,\n  .table th.bg-danger.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-danger.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-danger.btn:active,\n  .table th.bg-danger.btn.active {\n    background-color: #bd2130 !important;\n    border-color: #b21f2d;\n    color: #fff;\n  }\n  .table td.bg-light,\n  .table th.bg-light {\n    background-color: #f8f9fa !important;\n  }\n  .table td.bg-light,\n  .table td.bg-light > a,\n  .table th.bg-light,\n  .table th.bg-light > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-light.btn:hover,\n  .table th.bg-light.btn:hover {\n    border-color: #dae0e5;\n    color: #121a24;\n  }\n  .table td.bg-light.btn:not(:disabled):not(.disabled):active, .table td.bg-light.btn:not(:disabled):not(.disabled).active, .table td.bg-light.btn:active, .table td.bg-light.btn.active,\n  .table th.bg-light.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-light.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-light.btn:active,\n  .table th.bg-light.btn.active {\n    background-color: #dae0e5 !important;\n    border-color: #d3d9df;\n    color: #1f2d3d;\n  }\n  .table td.bg-dark,\n  .table th.bg-dark {\n    background-color: #343a40 !important;\n  }\n  .table td.bg-dark,\n  .table td.bg-dark > a,\n  .table th.bg-dark,\n  .table th.bg-dark > a {\n    color: #fff !important;\n  }\n  .table td.bg-dark.btn:hover,\n  .table th.bg-dark.btn:hover {\n    border-color: #1d2124;\n    color: #ececec;\n  }\n  .table td.bg-dark.btn:not(:disabled):not(.disabled):active, .table td.bg-dark.btn:not(:disabled):not(.disabled).active, .table td.bg-dark.btn:active, .table td.bg-dark.btn.active,\n  .table th.bg-dark.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-dark.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-dark.btn:active,\n  .table th.bg-dark.btn.active {\n    background-color: #1d2124 !important;\n    border-color: #171a1d;\n    color: #fff;\n  }\n  .table td.bg-lightblue,\n  .table th.bg-lightblue {\n    background-color: #3c8dbc !important;\n  }\n  .table td.bg-lightblue,\n  .table td.bg-lightblue > a,\n  .table th.bg-lightblue,\n  .table th.bg-lightblue > a {\n    color: #fff !important;\n  }\n  .table td.bg-lightblue.btn:hover,\n  .table th.bg-lightblue.btn:hover {\n    border-color: #307095;\n    color: #ececec;\n  }\n  .table td.bg-lightblue.btn:not(:disabled):not(.disabled):active, .table td.bg-lightblue.btn:not(:disabled):not(.disabled).active, .table td.bg-lightblue.btn:active, .table td.bg-lightblue.btn.active,\n  .table th.bg-lightblue.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-lightblue.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-lightblue.btn:active,\n  .table th.bg-lightblue.btn.active {\n    background-color: #307095 !important;\n    border-color: #2d698c;\n    color: #fff;\n  }\n  .table td.bg-navy,\n  .table th.bg-navy {\n    background-color: #001f3f !important;\n  }\n  .table td.bg-navy,\n  .table td.bg-navy > a,\n  .table th.bg-navy,\n  .table th.bg-navy > a {\n    color: #fff !important;\n  }\n  .table td.bg-navy.btn:hover,\n  .table th.bg-navy.btn:hover {\n    border-color: #00060c;\n    color: #ececec;\n  }\n  .table td.bg-navy.btn:not(:disabled):not(.disabled):active, .table td.bg-navy.btn:not(:disabled):not(.disabled).active, .table td.bg-navy.btn:active, .table td.bg-navy.btn.active,\n  .table th.bg-navy.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-navy.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-navy.btn:active,\n  .table th.bg-navy.btn.active {\n    background-color: #00060c !important;\n    border-color: black;\n    color: #fff;\n  }\n  .table td.bg-olive,\n  .table th.bg-olive {\n    background-color: #3d9970 !important;\n  }\n  .table td.bg-olive,\n  .table td.bg-olive > a,\n  .table th.bg-olive,\n  .table th.bg-olive > a {\n    color: #fff !important;\n  }\n  .table td.bg-olive.btn:hover,\n  .table th.bg-olive.btn:hover {\n    border-color: #2e7555;\n    color: #ececec;\n  }\n  .table td.bg-olive.btn:not(:disabled):not(.disabled):active, .table td.bg-olive.btn:not(:disabled):not(.disabled).active, .table td.bg-olive.btn:active, .table td.bg-olive.btn.active,\n  .table th.bg-olive.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-olive.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-olive.btn:active,\n  .table th.bg-olive.btn.active {\n    background-color: #2e7555 !important;\n    border-color: #2b6b4f;\n    color: #fff;\n  }\n  .table td.bg-lime,\n  .table th.bg-lime {\n    background-color: #01ff70 !important;\n  }\n  .table td.bg-lime,\n  .table td.bg-lime > a,\n  .table th.bg-lime,\n  .table th.bg-lime > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-lime.btn:hover,\n  .table th.bg-lime.btn:hover {\n    border-color: #00cd5a;\n    color: #121a24;\n  }\n  .table td.bg-lime.btn:not(:disabled):not(.disabled):active, .table td.bg-lime.btn:not(:disabled):not(.disabled).active, .table td.bg-lime.btn:active, .table td.bg-lime.btn.active,\n  .table th.bg-lime.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-lime.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-lime.btn:active,\n  .table th.bg-lime.btn.active {\n    background-color: #00cd5a !important;\n    border-color: #00c054;\n    color: #fff;\n  }\n  .table td.bg-fuchsia,\n  .table th.bg-fuchsia {\n    background-color: #f012be !important;\n  }\n  .table td.bg-fuchsia,\n  .table td.bg-fuchsia > a,\n  .table th.bg-fuchsia,\n  .table th.bg-fuchsia > a {\n    color: #fff !important;\n  }\n  .table td.bg-fuchsia.btn:hover,\n  .table th.bg-fuchsia.btn:hover {\n    border-color: #c30c9a;\n    color: #ececec;\n  }\n  .table td.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .table td.bg-fuchsia.btn:not(:disabled):not(.disabled).active, .table td.bg-fuchsia.btn:active, .table td.bg-fuchsia.btn.active,\n  .table th.bg-fuchsia.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-fuchsia.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-fuchsia.btn:active,\n  .table th.bg-fuchsia.btn.active {\n    background-color: #c30c9a !important;\n    border-color: #b70c90;\n    color: #fff;\n  }\n  .table td.bg-maroon,\n  .table th.bg-maroon {\n    background-color: #d81b60 !important;\n  }\n  .table td.bg-maroon,\n  .table td.bg-maroon > a,\n  .table th.bg-maroon,\n  .table th.bg-maroon > a {\n    color: #fff !important;\n  }\n  .table td.bg-maroon.btn:hover,\n  .table th.bg-maroon.btn:hover {\n    border-color: #ab154c;\n    color: #ececec;\n  }\n  .table td.bg-maroon.btn:not(:disabled):not(.disabled):active, .table td.bg-maroon.btn:not(:disabled):not(.disabled).active, .table td.bg-maroon.btn:active, .table td.bg-maroon.btn.active,\n  .table th.bg-maroon.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-maroon.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-maroon.btn:active,\n  .table th.bg-maroon.btn.active {\n    background-color: #ab154c !important;\n    border-color: #9f1447;\n    color: #fff;\n  }\n  .table td.bg-blue,\n  .table th.bg-blue {\n    background-color: #007bff !important;\n  }\n  .table td.bg-blue,\n  .table td.bg-blue > a,\n  .table th.bg-blue,\n  .table th.bg-blue > a {\n    color: #fff !important;\n  }\n  .table td.bg-blue.btn:hover,\n  .table th.bg-blue.btn:hover {\n    border-color: #0062cc;\n    color: #ececec;\n  }\n  .table td.bg-blue.btn:not(:disabled):not(.disabled):active, .table td.bg-blue.btn:not(:disabled):not(.disabled).active, .table td.bg-blue.btn:active, .table td.bg-blue.btn.active,\n  .table th.bg-blue.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-blue.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-blue.btn:active,\n  .table th.bg-blue.btn.active {\n    background-color: #0062cc !important;\n    border-color: #005cbf;\n    color: #fff;\n  }\n  .table td.bg-indigo,\n  .table th.bg-indigo {\n    background-color: #6610f2 !important;\n  }\n  .table td.bg-indigo,\n  .table td.bg-indigo > a,\n  .table th.bg-indigo,\n  .table th.bg-indigo > a {\n    color: #fff !important;\n  }\n  .table td.bg-indigo.btn:hover,\n  .table th.bg-indigo.btn:hover {\n    border-color: #510bc4;\n    color: #ececec;\n  }\n  .table td.bg-indigo.btn:not(:disabled):not(.disabled):active, .table td.bg-indigo.btn:not(:disabled):not(.disabled).active, .table td.bg-indigo.btn:active, .table td.bg-indigo.btn.active,\n  .table th.bg-indigo.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-indigo.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-indigo.btn:active,\n  .table th.bg-indigo.btn.active {\n    background-color: #510bc4 !important;\n    border-color: #4c0ab8;\n    color: #fff;\n  }\n  .table td.bg-purple,\n  .table th.bg-purple {\n    background-color: #6f42c1 !important;\n  }\n  .table td.bg-purple,\n  .table td.bg-purple > a,\n  .table th.bg-purple,\n  .table th.bg-purple > a {\n    color: #fff !important;\n  }\n  .table td.bg-purple.btn:hover,\n  .table th.bg-purple.btn:hover {\n    border-color: #59339d;\n    color: #ececec;\n  }\n  .table td.bg-purple.btn:not(:disabled):not(.disabled):active, .table td.bg-purple.btn:not(:disabled):not(.disabled).active, .table td.bg-purple.btn:active, .table td.bg-purple.btn.active,\n  .table th.bg-purple.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-purple.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-purple.btn:active,\n  .table th.bg-purple.btn.active {\n    background-color: #59339d !important;\n    border-color: #533093;\n    color: #fff;\n  }\n  .table td.bg-pink,\n  .table th.bg-pink {\n    background-color: #e83e8c !important;\n  }\n  .table td.bg-pink,\n  .table td.bg-pink > a,\n  .table th.bg-pink,\n  .table th.bg-pink > a {\n    color: #fff !important;\n  }\n  .table td.bg-pink.btn:hover,\n  .table th.bg-pink.btn:hover {\n    border-color: #d91a72;\n    color: #ececec;\n  }\n  .table td.bg-pink.btn:not(:disabled):not(.disabled):active, .table td.bg-pink.btn:not(:disabled):not(.disabled).active, .table td.bg-pink.btn:active, .table td.bg-pink.btn.active,\n  .table th.bg-pink.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-pink.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-pink.btn:active,\n  .table th.bg-pink.btn.active {\n    background-color: #d91a72 !important;\n    border-color: #ce196c;\n    color: #fff;\n  }\n  .table td.bg-red,\n  .table th.bg-red {\n    background-color: #dc3545 !important;\n  }\n  .table td.bg-red,\n  .table td.bg-red > a,\n  .table th.bg-red,\n  .table th.bg-red > a {\n    color: #fff !important;\n  }\n  .table td.bg-red.btn:hover,\n  .table th.bg-red.btn:hover {\n    border-color: #bd2130;\n    color: #ececec;\n  }\n  .table td.bg-red.btn:not(:disabled):not(.disabled):active, .table td.bg-red.btn:not(:disabled):not(.disabled).active, .table td.bg-red.btn:active, .table td.bg-red.btn.active,\n  .table th.bg-red.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-red.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-red.btn:active,\n  .table th.bg-red.btn.active {\n    background-color: #bd2130 !important;\n    border-color: #b21f2d;\n    color: #fff;\n  }\n  .table td.bg-orange,\n  .table th.bg-orange {\n    background-color: #fd7e14 !important;\n  }\n  .table td.bg-orange,\n  .table td.bg-orange > a,\n  .table th.bg-orange,\n  .table th.bg-orange > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-orange.btn:hover,\n  .table th.bg-orange.btn:hover {\n    border-color: #dc6502;\n    color: #121a24;\n  }\n  .table td.bg-orange.btn:not(:disabled):not(.disabled):active, .table td.bg-orange.btn:not(:disabled):not(.disabled).active, .table td.bg-orange.btn:active, .table td.bg-orange.btn.active,\n  .table th.bg-orange.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-orange.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-orange.btn:active,\n  .table th.bg-orange.btn.active {\n    background-color: #dc6502 !important;\n    border-color: #cf5f02;\n    color: #fff;\n  }\n  .table td.bg-yellow,\n  .table th.bg-yellow {\n    background-color: #ffc107 !important;\n  }\n  .table td.bg-yellow,\n  .table td.bg-yellow > a,\n  .table th.bg-yellow,\n  .table th.bg-yellow > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-yellow.btn:hover,\n  .table th.bg-yellow.btn:hover {\n    border-color: #d39e00;\n    color: #121a24;\n  }\n  .table td.bg-yellow.btn:not(:disabled):not(.disabled):active, .table td.bg-yellow.btn:not(:disabled):not(.disabled).active, .table td.bg-yellow.btn:active, .table td.bg-yellow.btn.active,\n  .table th.bg-yellow.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-yellow.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-yellow.btn:active,\n  .table th.bg-yellow.btn.active {\n    background-color: #d39e00 !important;\n    border-color: #c69500;\n    color: #1f2d3d;\n  }\n  .table td.bg-green,\n  .table th.bg-green {\n    background-color: #28a745 !important;\n  }\n  .table td.bg-green,\n  .table td.bg-green > a,\n  .table th.bg-green,\n  .table th.bg-green > a {\n    color: #fff !important;\n  }\n  .table td.bg-green.btn:hover,\n  .table th.bg-green.btn:hover {\n    border-color: #1e7e34;\n    color: #ececec;\n  }\n  .table td.bg-green.btn:not(:disabled):not(.disabled):active, .table td.bg-green.btn:not(:disabled):not(.disabled).active, .table td.bg-green.btn:active, .table td.bg-green.btn.active,\n  .table th.bg-green.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-green.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-green.btn:active,\n  .table th.bg-green.btn.active {\n    background-color: #1e7e34 !important;\n    border-color: #1c7430;\n    color: #fff;\n  }\n  .table td.bg-teal,\n  .table th.bg-teal {\n    background-color: #20c997 !important;\n  }\n  .table td.bg-teal,\n  .table td.bg-teal > a,\n  .table th.bg-teal,\n  .table th.bg-teal > a {\n    color: #fff !important;\n  }\n  .table td.bg-teal.btn:hover,\n  .table th.bg-teal.btn:hover {\n    border-color: #199d76;\n    color: #ececec;\n  }\n  .table td.bg-teal.btn:not(:disabled):not(.disabled):active, .table td.bg-teal.btn:not(:disabled):not(.disabled).active, .table td.bg-teal.btn:active, .table td.bg-teal.btn.active,\n  .table th.bg-teal.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-teal.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-teal.btn:active,\n  .table th.bg-teal.btn.active {\n    background-color: #199d76 !important;\n    border-color: #17926e;\n    color: #fff;\n  }\n  .table td.bg-cyan,\n  .table th.bg-cyan {\n    background-color: #17a2b8 !important;\n  }\n  .table td.bg-cyan,\n  .table td.bg-cyan > a,\n  .table th.bg-cyan,\n  .table th.bg-cyan > a {\n    color: #fff !important;\n  }\n  .table td.bg-cyan.btn:hover,\n  .table th.bg-cyan.btn:hover {\n    border-color: #117a8b;\n    color: #ececec;\n  }\n  .table td.bg-cyan.btn:not(:disabled):not(.disabled):active, .table td.bg-cyan.btn:not(:disabled):not(.disabled).active, .table td.bg-cyan.btn:active, .table td.bg-cyan.btn.active,\n  .table th.bg-cyan.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-cyan.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-cyan.btn:active,\n  .table th.bg-cyan.btn.active {\n    background-color: #117a8b !important;\n    border-color: #10707f;\n    color: #fff;\n  }\n  .table td.bg-white,\n  .table th.bg-white {\n    background-color: #fff !important;\n  }\n  .table td.bg-white,\n  .table td.bg-white > a,\n  .table th.bg-white,\n  .table th.bg-white > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-white.btn:hover,\n  .table th.bg-white.btn:hover {\n    border-color: #e6e6e6;\n    color: #121a24;\n  }\n  .table td.bg-white.btn:not(:disabled):not(.disabled):active, .table td.bg-white.btn:not(:disabled):not(.disabled).active, .table td.bg-white.btn:active, .table td.bg-white.btn.active,\n  .table th.bg-white.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-white.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-white.btn:active,\n  .table th.bg-white.btn.active {\n    background-color: #e6e6e6 !important;\n    border-color: #dfdfdf;\n    color: #1f2d3d;\n  }\n  .table td.bg-gray,\n  .table th.bg-gray {\n    background-color: #6c757d !important;\n  }\n  .table td.bg-gray,\n  .table td.bg-gray > a,\n  .table th.bg-gray,\n  .table th.bg-gray > a {\n    color: #fff !important;\n  }\n  .table td.bg-gray.btn:hover,\n  .table th.bg-gray.btn:hover {\n    border-color: #545b62;\n    color: #ececec;\n  }\n  .table td.bg-gray.btn:not(:disabled):not(.disabled):active, .table td.bg-gray.btn:not(:disabled):not(.disabled).active, .table td.bg-gray.btn:active, .table td.bg-gray.btn.active,\n  .table th.bg-gray.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-gray.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-gray.btn:active,\n  .table th.bg-gray.btn.active {\n    background-color: #545b62 !important;\n    border-color: #4e555b;\n    color: #fff;\n  }\n  .table td.bg-gray-dark,\n  .table th.bg-gray-dark {\n    background-color: #343a40 !important;\n  }\n  .table td.bg-gray-dark,\n  .table td.bg-gray-dark > a,\n  .table th.bg-gray-dark,\n  .table th.bg-gray-dark > a {\n    color: #fff !important;\n  }\n  .table td.bg-gray-dark.btn:hover,\n  .table th.bg-gray-dark.btn:hover {\n    border-color: #1d2124;\n    color: #ececec;\n  }\n  .table td.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .table td.bg-gray-dark.btn:not(:disabled):not(.disabled).active, .table td.bg-gray-dark.btn:active, .table td.bg-gray-dark.btn.active,\n  .table th.bg-gray-dark.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-gray-dark.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-gray-dark.btn:active,\n  .table th.bg-gray-dark.btn.active {\n    background-color: #1d2124 !important;\n    border-color: #171a1d;\n    color: #fff;\n  }\n}\n\n.bg-gray {\n  background-color: #adb5bd;\n  color: #1f2d3d;\n}\n\n.bg-gray-light {\n  background-color: #f2f4f5;\n  color: #1f2d3d !important;\n}\n\n.bg-black {\n  background-color: #000;\n  color: #fff !important;\n}\n\n.bg-white {\n  background-color: #fff;\n  color: #1f2d3d !important;\n}\n\n.bg-gradient-primary {\n  background: #007bff linear-gradient(180deg, #268fff, #007bff) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .bg-gradient-primary.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-primary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-primary.btn:hover {\n  background: #007bff linear-gradient(180deg, #267fde, #0069d9) repeat-x !important;\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .bg-gradient-primary.btn:not(:disabled):not(.disabled).active, .bg-gradient-primary.btn:active, .bg-gradient-primary.btn.active {\n  background: #007bff linear-gradient(180deg, #267ad4, #0062cc) repeat-x !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-gradient-primary.btn:disabled, .bg-gradient-primary.btn.disabled {\n  background-image: none !important;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.bg-gradient-secondary {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-secondary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-secondary.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active, .bg-gradient-secondary.btn:active, .bg-gradient-secondary.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gradient-secondary.btn:disabled, .bg-gradient-secondary.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.bg-gradient-success {\n  background: #28a745 linear-gradient(180deg, #48b461, #28a745) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-success.btn:not(:disabled):not(.disabled):active, .bg-gradient-success.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-success.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-success.btn:hover {\n  background: #28a745 linear-gradient(180deg, #429a56, #218838) repeat-x !important;\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-gradient-success.btn:not(:disabled):not(.disabled):active, .bg-gradient-success.btn:not(:disabled):not(.disabled).active, .bg-gradient-success.btn:active, .bg-gradient-success.btn.active {\n  background: #28a745 linear-gradient(180deg, #409152, #1e7e34) repeat-x !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-gradient-success.btn:disabled, .bg-gradient-success.btn.disabled {\n  background-image: none !important;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.bg-gradient-info {\n  background: #17a2b8 linear-gradient(180deg, #3ab0c3, #17a2b8) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-info.btn:not(:disabled):not(.disabled):active, .bg-gradient-info.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-info.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-info.btn:hover {\n  background: #17a2b8 linear-gradient(180deg, #3697a6, #138496) repeat-x !important;\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-gradient-info.btn:not(:disabled):not(.disabled):active, .bg-gradient-info.btn:not(:disabled):not(.disabled).active, .bg-gradient-info.btn:active, .bg-gradient-info.btn.active {\n  background: #17a2b8 linear-gradient(180deg, #358e9c, #117a8b) repeat-x !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-gradient-info.btn:disabled, .bg-gradient-info.btn.disabled {\n  background-image: none !important;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.bg-gradient-warning {\n  background: #ffc107 linear-gradient(180deg, #ffca2c, #ffc107) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .bg-gradient-warning.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-warning.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-warning.btn:hover {\n  background: #ffc107 linear-gradient(180deg, #e4b526, #e0a800) repeat-x !important;\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .bg-gradient-warning.btn:not(:disabled):not(.disabled).active, .bg-gradient-warning.btn:active, .bg-gradient-warning.btn.active {\n  background: #ffc107 linear-gradient(180deg, #daad26, #d39e00) repeat-x !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-gradient-warning.btn:disabled, .bg-gradient-warning.btn.disabled {\n  background-image: none !important;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.bg-gradient-danger {\n  background: #dc3545 linear-gradient(180deg, #e15361, #dc3545) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .bg-gradient-danger.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-danger.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-danger.btn:hover {\n  background: #dc3545 linear-gradient(180deg, #d04451, #c82333) repeat-x !important;\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .bg-gradient-danger.btn:not(:disabled):not(.disabled).active, .bg-gradient-danger.btn:active, .bg-gradient-danger.btn.active {\n  background: #dc3545 linear-gradient(180deg, #c7424f, #bd2130) repeat-x !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-gradient-danger.btn:disabled, .bg-gradient-danger.btn.disabled {\n  background-image: none !important;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.bg-gradient-light {\n  background: #f8f9fa linear-gradient(180deg, #f9fafb, #f8f9fa) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-light.btn:not(:disabled):not(.disabled):active, .bg-gradient-light.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-light.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-light.btn:hover {\n  background: #f8f9fa linear-gradient(180deg, #e6eaed, #e2e6ea) repeat-x !important;\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.bg-gradient-light.btn:not(:disabled):not(.disabled):active, .bg-gradient-light.btn:not(:disabled):not(.disabled).active, .bg-gradient-light.btn:active, .bg-gradient-light.btn.active {\n  background: #f8f9fa linear-gradient(180deg, #e0e4e9, #dae0e5) repeat-x !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.bg-gradient-light.btn:disabled, .bg-gradient-light.btn.disabled {\n  background-image: none !important;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.bg-gradient-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-dark.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-dark.btn:not(:disabled):not(.disabled).active, .bg-gradient-dark.btn:active, .bg-gradient-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-gradient-dark.btn:disabled, .bg-gradient-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.bg-gradient-lightblue {\n  background: #3c8dbc linear-gradient(180deg, #599ec6, #3c8dbc) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-lightblue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-lightblue.btn:hover {\n  background: #3c8dbc linear-gradient(180deg, #518cad, #33779f) repeat-x !important;\n  border-color: #307095;\n  color: #ececec;\n}\n\n.bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active, .bg-gradient-lightblue.btn:active, .bg-gradient-lightblue.btn.active {\n  background: #3c8dbc linear-gradient(180deg, #4f85a5, #307095) repeat-x !important;\n  border-color: #2d698c;\n  color: #fff;\n}\n\n.bg-gradient-lightblue.btn:disabled, .bg-gradient-lightblue.btn.disabled {\n  background-image: none !important;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.bg-gradient-navy {\n  background: #001f3f linear-gradient(180deg, #26415c, #001f3f) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .bg-gradient-navy.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-navy.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-navy.btn:hover {\n  background: #001f3f linear-gradient(180deg, #26313b, #000c19) repeat-x !important;\n  border-color: #00060c;\n  color: #ececec;\n}\n\n.bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .bg-gradient-navy.btn:not(:disabled):not(.disabled).active, .bg-gradient-navy.btn:active, .bg-gradient-navy.btn.active {\n  background: #001f3f linear-gradient(180deg, #262b30, #00060c) repeat-x !important;\n  border-color: black;\n  color: #fff;\n}\n\n.bg-gradient-navy.btn:disabled, .bg-gradient-navy.btn.disabled {\n  background-image: none !important;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.bg-gradient-olive {\n  background: #3d9970 linear-gradient(180deg, #5aa885, #3d9970) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .bg-gradient-olive.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-olive.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-olive.btn:hover {\n  background: #3d9970 linear-gradient(180deg, #519174, #327e5c) repeat-x !important;\n  border-color: #2e7555;\n  color: #ececec;\n}\n\n.bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .bg-gradient-olive.btn:not(:disabled):not(.disabled).active, .bg-gradient-olive.btn:active, .bg-gradient-olive.btn.active {\n  background: #3d9970 linear-gradient(180deg, #4e896f, #2e7555) repeat-x !important;\n  border-color: #2b6b4f;\n  color: #fff;\n}\n\n.bg-gradient-olive.btn:disabled, .bg-gradient-olive.btn.disabled {\n  background-image: none !important;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.bg-gradient-lime {\n  background: #01ff70 linear-gradient(180deg, #27ff85, #01ff70) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .bg-gradient-lime.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-lime.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-lime.btn:hover {\n  background: #01ff70 linear-gradient(180deg, #26df77, #00da5f) repeat-x !important;\n  border-color: #00cd5a;\n  color: #121a24;\n}\n\n.bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .bg-gradient-lime.btn:not(:disabled):not(.disabled).active, .bg-gradient-lime.btn:active, .bg-gradient-lime.btn.active {\n  background: #01ff70 linear-gradient(180deg, #26d572, #00cd5a) repeat-x !important;\n  border-color: #00c054;\n  color: #fff;\n}\n\n.bg-gradient-lime.btn:disabled, .bg-gradient-lime.btn.disabled {\n  background-image: none !important;\n  border-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.bg-gradient-fuchsia {\n  background: #f012be linear-gradient(180deg, #f236c8, #f012be) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-fuchsia.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-fuchsia.btn:hover {\n  background: #f012be linear-gradient(180deg, #d631b1, #cf0da3) repeat-x !important;\n  border-color: #c30c9a;\n  color: #ececec;\n}\n\n.bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active, .bg-gradient-fuchsia.btn:active, .bg-gradient-fuchsia.btn.active {\n  background: #f012be linear-gradient(180deg, #cc31a9, #c30c9a) repeat-x !important;\n  border-color: #b70c90;\n  color: #fff;\n}\n\n.bg-gradient-fuchsia.btn:disabled, .bg-gradient-fuchsia.btn.disabled {\n  background-image: none !important;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.bg-gradient-maroon {\n  background: #d81b60 linear-gradient(180deg, #de3d78, #d81b60) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-maroon.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-maroon.btn:hover {\n  background: #d81b60 linear-gradient(180deg, #c13a6b, #b61751) repeat-x !important;\n  border-color: #ab154c;\n  color: #ececec;\n}\n\n.bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active, .bg-gradient-maroon.btn:active, .bg-gradient-maroon.btn.active {\n  background: #d81b60 linear-gradient(180deg, #b73867, #ab154c) repeat-x !important;\n  border-color: #9f1447;\n  color: #fff;\n}\n\n.bg-gradient-maroon.btn:disabled, .bg-gradient-maroon.btn.disabled {\n  background-image: none !important;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.bg-gradient-blue {\n  background: #007bff linear-gradient(180deg, #268fff, #007bff) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .bg-gradient-blue.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-blue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-blue.btn:hover {\n  background: #007bff linear-gradient(180deg, #267fde, #0069d9) repeat-x !important;\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .bg-gradient-blue.btn:not(:disabled):not(.disabled).active, .bg-gradient-blue.btn:active, .bg-gradient-blue.btn.active {\n  background: #007bff linear-gradient(180deg, #267ad4, #0062cc) repeat-x !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-gradient-blue.btn:disabled, .bg-gradient-blue.btn.disabled {\n  background-image: none !important;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.bg-gradient-indigo {\n  background: #6610f2 linear-gradient(180deg, #7d34f4, #6610f2) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-indigo.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-indigo.btn:hover {\n  background: #6610f2 linear-gradient(180deg, #7030d7, #560bd0) repeat-x !important;\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active, .bg-gradient-indigo.btn:active, .bg-gradient-indigo.btn.active {\n  background: #6610f2 linear-gradient(180deg, #6b2fcd, #510bc4) repeat-x !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.bg-gradient-indigo.btn:disabled, .bg-gradient-indigo.btn.disabled {\n  background-image: none !important;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.bg-gradient-purple {\n  background: #6f42c1 linear-gradient(180deg, #855eca, #6f42c1) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .bg-gradient-purple.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-purple.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-purple.btn:hover {\n  background: #6f42c1 linear-gradient(180deg, #7655b4, #5e37a6) repeat-x !important;\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .bg-gradient-purple.btn:not(:disabled):not(.disabled).active, .bg-gradient-purple.btn:active, .bg-gradient-purple.btn.active {\n  background: #6f42c1 linear-gradient(180deg, #7252ab, #59339d) repeat-x !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.bg-gradient-purple.btn:disabled, .bg-gradient-purple.btn.disabled {\n  background-image: none !important;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.bg-gradient-pink {\n  background: #e83e8c linear-gradient(180deg, #eb5b9d, #e83e8c) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .bg-gradient-pink.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-pink.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-pink.btn:hover {\n  background: #e83e8c linear-gradient(180deg, #e83e8c, #e41c78) repeat-x !important;\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .bg-gradient-pink.btn:not(:disabled):not(.disabled).active, .bg-gradient-pink.btn:active, .bg-gradient-pink.btn.active {\n  background: #e83e8c linear-gradient(180deg, #df3c87, #d91a72) repeat-x !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.bg-gradient-pink.btn:disabled, .bg-gradient-pink.btn.disabled {\n  background-image: none !important;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.bg-gradient-red {\n  background: #dc3545 linear-gradient(180deg, #e15361, #dc3545) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-red.btn:not(:disabled):not(.disabled):active, .bg-gradient-red.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-red.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-red.btn:hover {\n  background: #dc3545 linear-gradient(180deg, #d04451, #c82333) repeat-x !important;\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-gradient-red.btn:not(:disabled):not(.disabled):active, .bg-gradient-red.btn:not(:disabled):not(.disabled).active, .bg-gradient-red.btn:active, .bg-gradient-red.btn.active {\n  background: #dc3545 linear-gradient(180deg, #c7424f, #bd2130) repeat-x !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-gradient-red.btn:disabled, .bg-gradient-red.btn.disabled {\n  background-image: none !important;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.bg-gradient-orange {\n  background: #fd7e14 linear-gradient(180deg, #fd9137, #fd7e14) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .bg-gradient-orange.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-orange.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-orange.btn:hover {\n  background: #fd7e14 linear-gradient(180deg, #ec8128, #e96b02) repeat-x !important;\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .bg-gradient-orange.btn:not(:disabled):not(.disabled).active, .bg-gradient-orange.btn:active, .bg-gradient-orange.btn.active {\n  background: #fd7e14 linear-gradient(180deg, #e17c28, #dc6502) repeat-x !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.bg-gradient-orange.btn:disabled, .bg-gradient-orange.btn.disabled {\n  background-image: none !important;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow {\n  background: #ffc107 linear-gradient(180deg, #ffca2c, #ffc107) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-yellow.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-yellow.btn:hover {\n  background: #ffc107 linear-gradient(180deg, #e4b526, #e0a800) repeat-x !important;\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active, .bg-gradient-yellow.btn:active, .bg-gradient-yellow.btn.active {\n  background: #ffc107 linear-gradient(180deg, #daad26, #d39e00) repeat-x !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow.btn:disabled, .bg-gradient-yellow.btn.disabled {\n  background-image: none !important;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.bg-gradient-green {\n  background: #28a745 linear-gradient(180deg, #48b461, #28a745) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-green.btn:not(:disabled):not(.disabled):active, .bg-gradient-green.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-green.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-green.btn:hover {\n  background: #28a745 linear-gradient(180deg, #429a56, #218838) repeat-x !important;\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-gradient-green.btn:not(:disabled):not(.disabled):active, .bg-gradient-green.btn:not(:disabled):not(.disabled).active, .bg-gradient-green.btn:active, .bg-gradient-green.btn.active {\n  background: #28a745 linear-gradient(180deg, #409152, #1e7e34) repeat-x !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-gradient-green.btn:disabled, .bg-gradient-green.btn.disabled {\n  background-image: none !important;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.bg-gradient-teal {\n  background: #20c997 linear-gradient(180deg, #41d1a7, #20c997) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .bg-gradient-teal.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-teal.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-teal.btn:hover {\n  background: #20c997 linear-gradient(180deg, #3db592, #1ba87e) repeat-x !important;\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .bg-gradient-teal.btn:not(:disabled):not(.disabled).active, .bg-gradient-teal.btn:active, .bg-gradient-teal.btn.active {\n  background: #20c997 linear-gradient(180deg, #3bac8b, #199d76) repeat-x !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.bg-gradient-teal.btn:disabled, .bg-gradient-teal.btn.disabled {\n  background-image: none !important;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.bg-gradient-cyan {\n  background: #17a2b8 linear-gradient(180deg, #3ab0c3, #17a2b8) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-cyan.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-cyan.btn:hover {\n  background: #17a2b8 linear-gradient(180deg, #3697a6, #138496) repeat-x !important;\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active, .bg-gradient-cyan.btn:active, .bg-gradient-cyan.btn.active {\n  background: #17a2b8 linear-gradient(180deg, #358e9c, #117a8b) repeat-x !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-gradient-cyan.btn:disabled, .bg-gradient-cyan.btn.disabled {\n  background-image: none !important;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.bg-gradient-white {\n  background: #fff linear-gradient(180deg, white, #fff) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-white.btn:not(:disabled):not(.disabled):active, .bg-gradient-white.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-white.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-white.btn:hover {\n  background: #fff linear-gradient(180deg, #efefef, #ececec) repeat-x !important;\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.bg-gradient-white.btn:not(:disabled):not(.disabled):active, .bg-gradient-white.btn:not(:disabled):not(.disabled).active, .bg-gradient-white.btn:active, .bg-gradient-white.btn.active {\n  background: #fff linear-gradient(180deg, #e9e9e9, #e6e6e6) repeat-x !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.bg-gradient-white.btn:disabled, .bg-gradient-white.btn.disabled {\n  background-image: none !important;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.bg-gradient-gray {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-gray.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-gray.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray.btn:not(:disabled):not(.disabled).active, .bg-gradient-gray.btn:active, .bg-gradient-gray.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gradient-gray.btn:disabled, .bg-gradient-gray.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-gray-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-gray-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active, .bg-gradient-gray-dark.btn:active, .bg-gradient-gray-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark.btn:disabled, .bg-gradient-gray-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n[class^=\"bg-\"].disabled {\n  opacity: .65;\n}\n\na.text-muted:hover {\n  color: #007bff !important;\n}\n\n.link-muted {\n  color: #5d6974;\n}\n\n.link-muted:hover, .link-muted:focus {\n  color: #464f58;\n}\n\n.link-black {\n  color: #6c757d;\n}\n\n.link-black:hover, .link-black:focus {\n  color: #e6e8ea;\n}\n\n.accent-primary .btn-link,\n.accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-primary .nav-tabs .nav-link {\n  color: #007bff;\n}\n\n.accent-primary .btn-link:hover,\n.accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-primary .nav-tabs .nav-link:hover {\n  color: #0056b3;\n}\n\n.accent-primary .dropdown-item:active, .accent-primary .dropdown-item.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.accent-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.accent-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-primary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-primary .custom-select:focus,\n.accent-primary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-primary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n}\n\n.accent-primary .page-item .page-link {\n  color: #007bff;\n}\n\n.accent-primary .page-item.active a,\n.accent-primary .page-item.active .page-link {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.accent-primary .page-item.disabled a,\n.accent-primary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-primary .page-item .page-link:hover, .dark-mode.accent-primary .page-item .page-link:focus {\n  color: #1a88ff;\n}\n\n.accent-secondary .btn-link,\n.accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-secondary .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.accent-secondary .btn-link:hover,\n.accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-secondary .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.accent-secondary .dropdown-item:active, .accent-secondary .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.accent-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.accent-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-secondary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-secondary .custom-select:focus,\n.accent-secondary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-secondary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.accent-secondary .page-item .page-link {\n  color: #6c757d;\n}\n\n.accent-secondary .page-item.active a,\n.accent-secondary .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.accent-secondary .page-item.disabled a,\n.accent-secondary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-secondary .page-item .page-link:hover, .dark-mode.accent-secondary .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.accent-success .btn-link,\n.accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-success .nav-tabs .nav-link {\n  color: #28a745;\n}\n\n.accent-success .btn-link:hover,\n.accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-success .nav-tabs .nav-link:hover {\n  color: #19692c;\n}\n\n.accent-success .dropdown-item:active, .accent-success .dropdown-item.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.accent-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.accent-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-success .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-success .custom-select:focus,\n.accent-success .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-success .custom-file-input:focus ~ .custom-file-label {\n  border-color: #71dd8a;\n}\n\n.accent-success .page-item .page-link {\n  color: #28a745;\n}\n\n.accent-success .page-item.active a,\n.accent-success .page-item.active .page-link {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.accent-success .page-item.disabled a,\n.accent-success .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-success .page-item .page-link:hover, .dark-mode.accent-success .page-item .page-link:focus {\n  color: #2dbc4e;\n}\n\n.accent-info .btn-link,\n.accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-info .nav-tabs .nav-link {\n  color: #17a2b8;\n}\n\n.accent-info .btn-link:hover,\n.accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-info .nav-tabs .nav-link:hover {\n  color: #0f6674;\n}\n\n.accent-info .dropdown-item:active, .accent-info .dropdown-item.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.accent-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-info .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-info .custom-select:focus,\n.accent-info .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-info .custom-file-input:focus ~ .custom-file-label {\n  border-color: #63d9ec;\n}\n\n.accent-info .page-item .page-link {\n  color: #17a2b8;\n}\n\n.accent-info .page-item.active a,\n.accent-info .page-item.active .page-link {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-info .page-item.disabled a,\n.accent-info .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-info .page-item .page-link:hover, .dark-mode.accent-info .page-item .page-link:focus {\n  color: #1ab6cf;\n}\n\n.accent-warning .btn-link,\n.accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-warning .nav-tabs .nav-link {\n  color: #ffc107;\n}\n\n.accent-warning .btn-link:hover,\n.accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-warning .nav-tabs .nav-link:hover {\n  color: #ba8b00;\n}\n\n.accent-warning .dropdown-item:active, .accent-warning .dropdown-item.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.accent-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.accent-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-warning .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-warning .custom-select:focus,\n.accent-warning .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-warning .custom-file-input:focus ~ .custom-file-label {\n  border-color: #ffe187;\n}\n\n.accent-warning .page-item .page-link {\n  color: #ffc107;\n}\n\n.accent-warning .page-item.active a,\n.accent-warning .page-item.active .page-link {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #fff;\n}\n\n.accent-warning .page-item.disabled a,\n.accent-warning .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-warning .page-item .page-link:hover, .dark-mode.accent-warning .page-item .page-link:focus {\n  color: #ffc721;\n}\n\n.accent-danger .btn-link,\n.accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-danger .nav-tabs .nav-link {\n  color: #dc3545;\n}\n\n.accent-danger .btn-link:hover,\n.accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-danger .nav-tabs .nav-link:hover {\n  color: #a71d2a;\n}\n\n.accent-danger .dropdown-item:active, .accent-danger .dropdown-item.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.accent-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.accent-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-danger .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-danger .custom-select:focus,\n.accent-danger .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-danger .custom-file-input:focus ~ .custom-file-label {\n  border-color: #efa2a9;\n}\n\n.accent-danger .page-item .page-link {\n  color: #dc3545;\n}\n\n.accent-danger .page-item.active a,\n.accent-danger .page-item.active .page-link {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.accent-danger .page-item.disabled a,\n.accent-danger .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-danger .page-item .page-link:hover, .dark-mode.accent-danger .page-item .page-link:focus {\n  color: #e04b59;\n}\n\n.accent-light .btn-link,\n.accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-light .nav-tabs .nav-link {\n  color: #f8f9fa;\n}\n\n.accent-light .btn-link:hover,\n.accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-light .nav-tabs .nav-link:hover {\n  color: #cbd3da;\n}\n\n.accent-light .dropdown-item:active, .accent-light .dropdown-item.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.accent-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.accent-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-light .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-light .custom-select:focus,\n.accent-light .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-light .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.accent-light .page-item .page-link {\n  color: #f8f9fa;\n}\n\n.accent-light .page-item.active a,\n.accent-light .page-item.active .page-link {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #fff;\n}\n\n.accent-light .page-item.disabled a,\n.accent-light .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-light .page-item .page-link:hover, .dark-mode.accent-light .page-item .page-link:focus {\n  color: white;\n}\n\n.accent-dark .btn-link,\n.accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.accent-dark .btn-link:hover,\n.accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.accent-dark .dropdown-item:active, .accent-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.accent-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.accent-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-dark .custom-select:focus,\n.accent-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.accent-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.accent-dark .page-item.active a,\n.accent-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.accent-dark .page-item.disabled a,\n.accent-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-dark .page-item .page-link:hover, .dark-mode.accent-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n.accent-lightblue .btn-link,\n.accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-lightblue .nav-tabs .nav-link {\n  color: #3c8dbc;\n}\n\n.accent-lightblue .btn-link:hover,\n.accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-lightblue .nav-tabs .nav-link:hover {\n  color: #296282;\n}\n\n.accent-lightblue .dropdown-item:active, .accent-lightblue .dropdown-item.active {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.accent-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.accent-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-lightblue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-lightblue .custom-select:focus,\n.accent-lightblue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-lightblue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #99c5de;\n}\n\n.accent-lightblue .page-item .page-link {\n  color: #3c8dbc;\n}\n\n.accent-lightblue .page-item.active a,\n.accent-lightblue .page-item.active .page-link {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.accent-lightblue .page-item.disabled a,\n.accent-lightblue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-lightblue .page-item .page-link:hover, .dark-mode.accent-lightblue .page-item .page-link:focus {\n  color: #4c99c6;\n}\n\n.accent-navy .btn-link,\n.accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-navy .nav-tabs .nav-link {\n  color: #001f3f;\n}\n\n.accent-navy .btn-link:hover,\n.accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-navy .nav-tabs .nav-link:hover {\n  color: black;\n}\n\n.accent-navy .dropdown-item:active, .accent-navy .dropdown-item.active {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.accent-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.accent-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-navy .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-navy .custom-select:focus,\n.accent-navy .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-navy .custom-file-input:focus ~ .custom-file-label {\n  border-color: #005ebf;\n}\n\n.accent-navy .page-item .page-link {\n  color: #001f3f;\n}\n\n.accent-navy .page-item.active a,\n.accent-navy .page-item.active .page-link {\n  background-color: #001f3f;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.accent-navy .page-item.disabled a,\n.accent-navy .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-navy .page-item .page-link:hover, .dark-mode.accent-navy .page-item .page-link:focus {\n  color: #002c59;\n}\n\n.accent-olive .btn-link,\n.accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-olive .nav-tabs .nav-link {\n  color: #3d9970;\n}\n\n.accent-olive .btn-link:hover,\n.accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-olive .nav-tabs .nav-link:hover {\n  color: #276248;\n}\n\n.accent-olive .dropdown-item:active, .accent-olive .dropdown-item.active {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.accent-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.accent-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-olive .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-olive .custom-select:focus,\n.accent-olive .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-olive .custom-file-input:focus ~ .custom-file-label {\n  border-color: #87cfaf;\n}\n\n.accent-olive .page-item .page-link {\n  color: #3d9970;\n}\n\n.accent-olive .page-item.active a,\n.accent-olive .page-item.active .page-link {\n  background-color: #3d9970;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.accent-olive .page-item.disabled a,\n.accent-olive .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-olive .page-item .page-link:hover, .dark-mode.accent-olive .page-item .page-link:focus {\n  color: #44ab7d;\n}\n\n.accent-lime .btn-link,\n.accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-lime .nav-tabs .nav-link {\n  color: #01ff70;\n}\n\n.accent-lime .btn-link:hover,\n.accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-lime .nav-tabs .nav-link:hover {\n  color: #00b44e;\n}\n\n.accent-lime .dropdown-item:active, .accent-lime .dropdown-item.active {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.accent-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.accent-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-lime .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-lime .custom-select:focus,\n.accent-lime .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-lime .custom-file-input:focus ~ .custom-file-label {\n  border-color: #81ffb8;\n}\n\n.accent-lime .page-item .page-link {\n  color: #01ff70;\n}\n\n.accent-lime .page-item.active a,\n.accent-lime .page-item.active .page-link {\n  background-color: #01ff70;\n  border-color: #01ff70;\n  color: #fff;\n}\n\n.accent-lime .page-item.disabled a,\n.accent-lime .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-lime .page-item .page-link:hover, .dark-mode.accent-lime .page-item .page-link:focus {\n  color: #1bff7e;\n}\n\n.accent-fuchsia .btn-link,\n.accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-fuchsia .nav-tabs .nav-link {\n  color: #f012be;\n}\n\n.accent-fuchsia .btn-link:hover,\n.accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-fuchsia .nav-tabs .nav-link:hover {\n  color: #ab0b87;\n}\n\n.accent-fuchsia .dropdown-item:active, .accent-fuchsia .dropdown-item.active {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.accent-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.accent-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-fuchsia .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-fuchsia .custom-select:focus,\n.accent-fuchsia .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-fuchsia .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f88adf;\n}\n\n.accent-fuchsia .page-item .page-link {\n  color: #f012be;\n}\n\n.accent-fuchsia .page-item.active a,\n.accent-fuchsia .page-item.active .page-link {\n  background-color: #f012be;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.accent-fuchsia .page-item.disabled a,\n.accent-fuchsia .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-fuchsia .page-item .page-link:hover, .dark-mode.accent-fuchsia .page-item .page-link:focus {\n  color: #f22ac5;\n}\n\n.accent-maroon .btn-link,\n.accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-maroon .nav-tabs .nav-link {\n  color: #d81b60;\n}\n\n.accent-maroon .btn-link:hover,\n.accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-maroon .nav-tabs .nav-link:hover {\n  color: #941342;\n}\n\n.accent-maroon .dropdown-item:active, .accent-maroon .dropdown-item.active {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.accent-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.accent-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-maroon .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-maroon .custom-select:focus,\n.accent-maroon .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-maroon .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f083ab;\n}\n\n.accent-maroon .page-item .page-link {\n  color: #d81b60;\n}\n\n.accent-maroon .page-item.active a,\n.accent-maroon .page-item.active .page-link {\n  background-color: #d81b60;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.accent-maroon .page-item.disabled a,\n.accent-maroon .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-maroon .page-item .page-link:hover, .dark-mode.accent-maroon .page-item .page-link:focus {\n  color: #e4286d;\n}\n\n.accent-blue .btn-link,\n.accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-blue .nav-tabs .nav-link {\n  color: #007bff;\n}\n\n.accent-blue .btn-link:hover,\n.accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-blue .nav-tabs .nav-link:hover {\n  color: #0056b3;\n}\n\n.accent-blue .dropdown-item:active, .accent-blue .dropdown-item.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.accent-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.accent-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-blue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-blue .custom-select:focus,\n.accent-blue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-blue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n}\n\n.accent-blue .page-item .page-link {\n  color: #007bff;\n}\n\n.accent-blue .page-item.active a,\n.accent-blue .page-item.active .page-link {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.accent-blue .page-item.disabled a,\n.accent-blue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-blue .page-item .page-link:hover, .dark-mode.accent-blue .page-item .page-link:focus {\n  color: #1a88ff;\n}\n\n.accent-indigo .btn-link,\n.accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-indigo .nav-tabs .nav-link {\n  color: #6610f2;\n}\n\n.accent-indigo .btn-link:hover,\n.accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-indigo .nav-tabs .nav-link:hover {\n  color: #4709ac;\n}\n\n.accent-indigo .dropdown-item:active, .accent-indigo .dropdown-item.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.accent-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.accent-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-indigo .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-indigo .custom-select:focus,\n.accent-indigo .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-indigo .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b389f9;\n}\n\n.accent-indigo .page-item .page-link {\n  color: #6610f2;\n}\n\n.accent-indigo .page-item.active a,\n.accent-indigo .page-item.active .page-link {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.accent-indigo .page-item.disabled a,\n.accent-indigo .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-indigo .page-item .page-link:hover, .dark-mode.accent-indigo .page-item .page-link:focus {\n  color: #7528f3;\n}\n\n.accent-purple .btn-link,\n.accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-purple .nav-tabs .nav-link {\n  color: #6f42c1;\n}\n\n.accent-purple .btn-link:hover,\n.accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-purple .nav-tabs .nav-link:hover {\n  color: #4e2d89;\n}\n\n.accent-purple .dropdown-item:active, .accent-purple .dropdown-item.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.accent-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.accent-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-purple .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-purple .custom-select:focus,\n.accent-purple .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-purple .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b8a2e0;\n}\n\n.accent-purple .page-item .page-link {\n  color: #6f42c1;\n}\n\n.accent-purple .page-item.active a,\n.accent-purple .page-item.active .page-link {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.accent-purple .page-item.disabled a,\n.accent-purple .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-purple .page-item .page-link:hover, .dark-mode.accent-purple .page-item .page-link:focus {\n  color: #7e55c7;\n}\n\n.accent-pink .btn-link,\n.accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-pink .nav-tabs .nav-link {\n  color: #e83e8c;\n}\n\n.accent-pink .btn-link:hover,\n.accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-pink .nav-tabs .nav-link:hover {\n  color: #c21766;\n}\n\n.accent-pink .dropdown-item:active, .accent-pink .dropdown-item.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.accent-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.accent-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-pink .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-pink .custom-select:focus,\n.accent-pink .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-pink .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f6b0d0;\n}\n\n.accent-pink .page-item .page-link {\n  color: #e83e8c;\n}\n\n.accent-pink .page-item.active a,\n.accent-pink .page-item.active .page-link {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.accent-pink .page-item.disabled a,\n.accent-pink .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-pink .page-item .page-link:hover, .dark-mode.accent-pink .page-item .page-link:focus {\n  color: #eb559a;\n}\n\n.accent-red .btn-link,\n.accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-red .nav-tabs .nav-link {\n  color: #dc3545;\n}\n\n.accent-red .btn-link:hover,\n.accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-red .nav-tabs .nav-link:hover {\n  color: #a71d2a;\n}\n\n.accent-red .dropdown-item:active, .accent-red .dropdown-item.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.accent-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.accent-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-red .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-red .custom-select:focus,\n.accent-red .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-red .custom-file-input:focus ~ .custom-file-label {\n  border-color: #efa2a9;\n}\n\n.accent-red .page-item .page-link {\n  color: #dc3545;\n}\n\n.accent-red .page-item.active a,\n.accent-red .page-item.active .page-link {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.accent-red .page-item.disabled a,\n.accent-red .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-red .page-item .page-link:hover, .dark-mode.accent-red .page-item .page-link:focus {\n  color: #e04b59;\n}\n\n.accent-orange .btn-link,\n.accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-orange .nav-tabs .nav-link {\n  color: #fd7e14;\n}\n\n.accent-orange .btn-link:hover,\n.accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-orange .nav-tabs .nav-link:hover {\n  color: #c35a02;\n}\n\n.accent-orange .dropdown-item:active, .accent-orange .dropdown-item.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.accent-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.accent-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-orange .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-orange .custom-select:focus,\n.accent-orange .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-orange .custom-file-input:focus ~ .custom-file-label {\n  border-color: #fec392;\n}\n\n.accent-orange .page-item .page-link {\n  color: #fd7e14;\n}\n\n.accent-orange .page-item.active a,\n.accent-orange .page-item.active .page-link {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #fff;\n}\n\n.accent-orange .page-item.disabled a,\n.accent-orange .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-orange .page-item .page-link:hover, .dark-mode.accent-orange .page-item .page-link:focus {\n  color: #fd8c2d;\n}\n\n.accent-yellow .btn-link,\n.accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-yellow .nav-tabs .nav-link {\n  color: #ffc107;\n}\n\n.accent-yellow .btn-link:hover,\n.accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-yellow .nav-tabs .nav-link:hover {\n  color: #ba8b00;\n}\n\n.accent-yellow .dropdown-item:active, .accent-yellow .dropdown-item.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.accent-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.accent-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-yellow .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-yellow .custom-select:focus,\n.accent-yellow .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-yellow .custom-file-input:focus ~ .custom-file-label {\n  border-color: #ffe187;\n}\n\n.accent-yellow .page-item .page-link {\n  color: #ffc107;\n}\n\n.accent-yellow .page-item.active a,\n.accent-yellow .page-item.active .page-link {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #fff;\n}\n\n.accent-yellow .page-item.disabled a,\n.accent-yellow .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-yellow .page-item .page-link:hover, .dark-mode.accent-yellow .page-item .page-link:focus {\n  color: #ffc721;\n}\n\n.accent-green .btn-link,\n.accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-green .nav-tabs .nav-link {\n  color: #28a745;\n}\n\n.accent-green .btn-link:hover,\n.accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-green .nav-tabs .nav-link:hover {\n  color: #19692c;\n}\n\n.accent-green .dropdown-item:active, .accent-green .dropdown-item.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.accent-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.accent-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-green .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-green .custom-select:focus,\n.accent-green .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-green .custom-file-input:focus ~ .custom-file-label {\n  border-color: #71dd8a;\n}\n\n.accent-green .page-item .page-link {\n  color: #28a745;\n}\n\n.accent-green .page-item.active a,\n.accent-green .page-item.active .page-link {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.accent-green .page-item.disabled a,\n.accent-green .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-green .page-item .page-link:hover, .dark-mode.accent-green .page-item .page-link:focus {\n  color: #2dbc4e;\n}\n\n.accent-teal .btn-link,\n.accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-teal .nav-tabs .nav-link {\n  color: #20c997;\n}\n\n.accent-teal .btn-link:hover,\n.accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-teal .nav-tabs .nav-link:hover {\n  color: #158765;\n}\n\n.accent-teal .dropdown-item:active, .accent-teal .dropdown-item.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.accent-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.accent-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-teal .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-teal .custom-select:focus,\n.accent-teal .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-teal .custom-file-input:focus ~ .custom-file-label {\n  border-color: #7eeaca;\n}\n\n.accent-teal .page-item .page-link {\n  color: #20c997;\n}\n\n.accent-teal .page-item.active a,\n.accent-teal .page-item.active .page-link {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.accent-teal .page-item.disabled a,\n.accent-teal .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-teal .page-item .page-link:hover, .dark-mode.accent-teal .page-item .page-link:focus {\n  color: #26dca6;\n}\n\n.accent-cyan .btn-link,\n.accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-cyan .nav-tabs .nav-link {\n  color: #17a2b8;\n}\n\n.accent-cyan .btn-link:hover,\n.accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-cyan .nav-tabs .nav-link:hover {\n  color: #0f6674;\n}\n\n.accent-cyan .dropdown-item:active, .accent-cyan .dropdown-item.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.accent-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-cyan .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-cyan .custom-select:focus,\n.accent-cyan .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-cyan .custom-file-input:focus ~ .custom-file-label {\n  border-color: #63d9ec;\n}\n\n.accent-cyan .page-item .page-link {\n  color: #17a2b8;\n}\n\n.accent-cyan .page-item.active a,\n.accent-cyan .page-item.active .page-link {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-cyan .page-item.disabled a,\n.accent-cyan .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-cyan .page-item .page-link:hover, .dark-mode.accent-cyan .page-item .page-link:focus {\n  color: #1ab6cf;\n}\n\n.accent-white .btn-link,\n.accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-white .nav-tabs .nav-link {\n  color: #fff;\n}\n\n.accent-white .btn-link:hover,\n.accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-white .nav-tabs .nav-link:hover {\n  color: #d9d9d9;\n}\n\n.accent-white .dropdown-item:active, .accent-white .dropdown-item.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.accent-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.accent-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-white .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-white .custom-select:focus,\n.accent-white .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-white .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.accent-white .page-item .page-link {\n  color: #fff;\n}\n\n.accent-white .page-item.active a,\n.accent-white .page-item.active .page-link {\n  background-color: #fff;\n  border-color: #fff;\n  color: #fff;\n}\n\n.accent-white .page-item.disabled a,\n.accent-white .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-white .page-item .page-link:hover, .dark-mode.accent-white .page-item .page-link:focus {\n  color: white;\n}\n\n.accent-gray .btn-link,\n.accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-gray .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.accent-gray .btn-link:hover,\n.accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-gray .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.accent-gray .dropdown-item:active, .accent-gray .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.accent-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.accent-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-gray .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-gray .custom-select:focus,\n.accent-gray .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-gray .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.accent-gray .page-item .page-link {\n  color: #6c757d;\n}\n\n.accent-gray .page-item.active a,\n.accent-gray .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.accent-gray .page-item.disabled a,\n.accent-gray .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-gray .page-item .page-link:hover, .dark-mode.accent-gray .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.accent-gray-dark .btn-link,\n.accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-gray-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.accent-gray-dark .btn-link:hover,\n.accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-gray-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.accent-gray-dark .dropdown-item:active, .accent-gray-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.accent-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.accent-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-gray-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-gray-dark .custom-select:focus,\n.accent-gray-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-gray-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.accent-gray-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.accent-gray-dark .page-item.active a,\n.accent-gray-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.accent-gray-dark .page-item.disabled a,\n.accent-gray-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode.accent-gray-dark .page-item .page-link:hover, .dark-mode.accent-gray-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n[class*=\"accent-\"] a.btn-primary {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-secondary {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-success {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-info {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-warning {\n  color: #1f2d3d;\n}\n\n[class*=\"accent-\"] a.btn-danger {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-light {\n  color: #1f2d3d;\n}\n\n[class*=\"accent-\"] a.btn-dark {\n  color: #fff;\n}\n\n.dark-mode .bg-light {\n  background-color: #454d55 !important;\n  color: #fff !important;\n}\n\n.dark-mode .text-black,\n.dark-mode .text-dark,\n.dark-mode .link-black,\n.dark-mode .link-dark {\n  color: #ced4da !important;\n}\n\n.dark-mode.bg-primary {\n  background-color: #3f6791 !important;\n}\n\n.dark-mode.bg-primary,\n.dark-mode.bg-primary > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-primary.btn:hover {\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode.bg-primary.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-primary.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-primary.btn:active, .dark-mode.bg-primary.btn.active {\n  background-color: #304e6d !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode.bg-secondary {\n  background-color: #6c757d !important;\n}\n\n.dark-mode.bg-secondary,\n.dark-mode.bg-secondary > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-secondary.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode.bg-secondary.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-secondary.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-secondary.btn:active, .dark-mode.bg-secondary.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode.bg-success {\n  background-color: #00bc8c !important;\n}\n\n.dark-mode.bg-success,\n.dark-mode.bg-success > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-success.btn:hover {\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode.bg-success.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-success.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-success.btn:active, .dark-mode.bg-success.btn.active {\n  background-color: #008966 !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode.bg-info {\n  background-color: #3498db !important;\n}\n\n.dark-mode.bg-info,\n.dark-mode.bg-info > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-info.btn:hover {\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode.bg-info.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-info.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-info.btn:active, .dark-mode.bg-info.btn.active {\n  background-color: #217dbb !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode.bg-warning {\n  background-color: #f39c12 !important;\n}\n\n.dark-mode.bg-warning,\n.dark-mode.bg-warning > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-warning.btn:hover {\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode.bg-warning.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-warning.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-warning.btn:active, .dark-mode.bg-warning.btn.active {\n  background-color: #c87f0a !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode.bg-danger {\n  background-color: #e74c3c !important;\n}\n\n.dark-mode.bg-danger,\n.dark-mode.bg-danger > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-danger.btn:hover {\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode.bg-danger.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-danger.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-danger.btn:active, .dark-mode.bg-danger.btn.active {\n  background-color: #d62c1a !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode.bg-light {\n  background-color: #f8f9fa !important;\n}\n\n.dark-mode.bg-light,\n.dark-mode.bg-light > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-light.btn:hover {\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.dark-mode.bg-light.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-light.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-light.btn:active, .dark-mode.bg-light.btn.active {\n  background-color: #dae0e5 !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.dark-mode.bg-dark {\n  background-color: #343a40 !important;\n}\n\n.dark-mode.bg-dark,\n.dark-mode.bg-dark > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode.bg-dark.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-dark.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-dark.btn:active, .dark-mode.bg-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode.bg-lightblue {\n  background-color: #86bad8 !important;\n}\n\n.dark-mode.bg-lightblue,\n.dark-mode.bg-lightblue > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-lightblue.btn:hover {\n  border-color: #5fa4cc;\n  color: #121a24;\n}\n\n.dark-mode.bg-lightblue.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-lightblue.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-lightblue.btn:active, .dark-mode.bg-lightblue.btn.active {\n  background-color: #5fa4cc !important;\n  border-color: #559ec9;\n  color: #fff;\n}\n\n.dark-mode.bg-navy {\n  background-color: #002c59 !important;\n}\n\n.dark-mode.bg-navy,\n.dark-mode.bg-navy > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-navy.btn:hover {\n  border-color: #001226;\n  color: #ececec;\n}\n\n.dark-mode.bg-navy.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-navy.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-navy.btn:active, .dark-mode.bg-navy.btn.active {\n  background-color: #001226 !important;\n  border-color: #000c19;\n  color: #fff;\n}\n\n.dark-mode.bg-olive {\n  background-color: #74c8a3 !important;\n}\n\n.dark-mode.bg-olive,\n.dark-mode.bg-olive > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-olive.btn:hover {\n  border-color: #50b98a;\n  color: #121a24;\n}\n\n.dark-mode.bg-olive.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-olive.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-olive.btn:active, .dark-mode.bg-olive.btn.active {\n  background-color: #50b98a !important;\n  border-color: #48b484;\n  color: #fff;\n}\n\n.dark-mode.bg-lime {\n  background-color: #67ffa9 !important;\n}\n\n.dark-mode.bg-lime,\n.dark-mode.bg-lime > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-lime.btn:hover {\n  border-color: #34ff8d;\n  color: #121a24;\n}\n\n.dark-mode.bg-lime.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-lime.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-lime.btn:active, .dark-mode.bg-lime.btn.active {\n  background-color: #34ff8d !important;\n  border-color: #27ff86;\n  color: #1f2d3d;\n}\n\n.dark-mode.bg-fuchsia {\n  background-color: #f672d8 !important;\n}\n\n.dark-mode.bg-fuchsia,\n.dark-mode.bg-fuchsia > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-fuchsia.btn:hover {\n  border-color: #f342cb;\n  color: #121a24;\n}\n\n.dark-mode.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-fuchsia.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-fuchsia.btn:active, .dark-mode.bg-fuchsia.btn.active {\n  background-color: #f342cb !important;\n  border-color: #f236c8;\n  color: #fff;\n}\n\n.dark-mode.bg-maroon {\n  background-color: #ed6c9b !important;\n}\n\n.dark-mode.bg-maroon,\n.dark-mode.bg-maroon > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-maroon.btn:hover {\n  border-color: #e73f7c;\n  color: #121a24;\n}\n\n.dark-mode.bg-maroon.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-maroon.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-maroon.btn:active, .dark-mode.bg-maroon.btn.active {\n  background-color: #e73f7c !important;\n  border-color: #e63475;\n  color: #fff;\n}\n\n.dark-mode.bg-blue {\n  background-color: #3f6791 !important;\n}\n\n.dark-mode.bg-blue,\n.dark-mode.bg-blue > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-blue.btn:hover {\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode.bg-blue.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-blue.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-blue.btn:active, .dark-mode.bg-blue.btn.active {\n  background-color: #304e6d !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode.bg-indigo {\n  background-color: #6610f2 !important;\n}\n\n.dark-mode.bg-indigo,\n.dark-mode.bg-indigo > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-indigo.btn:hover {\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.dark-mode.bg-indigo.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-indigo.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-indigo.btn:active, .dark-mode.bg-indigo.btn.active {\n  background-color: #510bc4 !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.dark-mode.bg-purple {\n  background-color: #6f42c1 !important;\n}\n\n.dark-mode.bg-purple,\n.dark-mode.bg-purple > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-purple.btn:hover {\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.dark-mode.bg-purple.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-purple.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-purple.btn:active, .dark-mode.bg-purple.btn.active {\n  background-color: #59339d !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.dark-mode.bg-pink {\n  background-color: #e83e8c !important;\n}\n\n.dark-mode.bg-pink,\n.dark-mode.bg-pink > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-pink.btn:hover {\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.dark-mode.bg-pink.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-pink.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-pink.btn:active, .dark-mode.bg-pink.btn.active {\n  background-color: #d91a72 !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.dark-mode.bg-red {\n  background-color: #e74c3c !important;\n}\n\n.dark-mode.bg-red,\n.dark-mode.bg-red > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-red.btn:hover {\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode.bg-red.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-red.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-red.btn:active, .dark-mode.bg-red.btn.active {\n  background-color: #d62c1a !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode.bg-orange {\n  background-color: #fd7e14 !important;\n}\n\n.dark-mode.bg-orange,\n.dark-mode.bg-orange > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-orange.btn:hover {\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.dark-mode.bg-orange.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-orange.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-orange.btn:active, .dark-mode.bg-orange.btn.active {\n  background-color: #dc6502 !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.dark-mode.bg-yellow {\n  background-color: #f39c12 !important;\n}\n\n.dark-mode.bg-yellow,\n.dark-mode.bg-yellow > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-yellow.btn:hover {\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode.bg-yellow.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-yellow.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-yellow.btn:active, .dark-mode.bg-yellow.btn.active {\n  background-color: #c87f0a !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode.bg-green {\n  background-color: #00bc8c !important;\n}\n\n.dark-mode.bg-green,\n.dark-mode.bg-green > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-green.btn:hover {\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode.bg-green.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-green.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-green.btn:active, .dark-mode.bg-green.btn.active {\n  background-color: #008966 !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode.bg-teal {\n  background-color: #20c997 !important;\n}\n\n.dark-mode.bg-teal,\n.dark-mode.bg-teal > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-teal.btn:hover {\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.dark-mode.bg-teal.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-teal.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-teal.btn:active, .dark-mode.bg-teal.btn.active {\n  background-color: #199d76 !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.dark-mode.bg-cyan {\n  background-color: #3498db !important;\n}\n\n.dark-mode.bg-cyan,\n.dark-mode.bg-cyan > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-cyan.btn:hover {\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode.bg-cyan.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-cyan.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-cyan.btn:active, .dark-mode.bg-cyan.btn.active {\n  background-color: #217dbb !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode.bg-white {\n  background-color: #fff !important;\n}\n\n.dark-mode.bg-white,\n.dark-mode.bg-white > a {\n  color: #1f2d3d !important;\n}\n\n.dark-mode.bg-white.btn:hover {\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.dark-mode.bg-white.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-white.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-white.btn:active, .dark-mode.bg-white.btn.active {\n  background-color: #e6e6e6 !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.dark-mode.bg-gray {\n  background-color: #6c757d !important;\n}\n\n.dark-mode.bg-gray,\n.dark-mode.bg-gray > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-gray.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode.bg-gray.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-gray.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-gray.btn:active, .dark-mode.bg-gray.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode.bg-gray-dark {\n  background-color: #343a40 !important;\n}\n\n.dark-mode.bg-gray-dark,\n.dark-mode.bg-gray-dark > a {\n  color: #fff !important;\n}\n\n.dark-mode.bg-gray-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .dark-mode.bg-gray-dark.btn:not(:disabled):not(.disabled).active, .dark-mode.bg-gray-dark.btn:active, .dark-mode.bg-gray-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-primary {\n  background: #3f6791 linear-gradient(180deg, #5c7ea2, #3f6791) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-primary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-primary.btn:hover {\n  background: #3f6791 linear-gradient(180deg, #526e8b, #335476) repeat-x !important;\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-primary.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-primary.btn:active, .dark-mode .bg-gradient-primary.btn.active {\n  background: #3f6791 linear-gradient(180deg, #4f6883, #304e6d) repeat-x !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-primary.btn:disabled, .dark-mode .bg-gradient-primary.btn.disabled {\n  background-image: none !important;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-secondary {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-secondary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-secondary.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-secondary.btn:active, .dark-mode .bg-gradient-secondary.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-secondary.btn:disabled, .dark-mode .bg-gradient-secondary.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-success {\n  background: #00bc8c linear-gradient(180deg, #26c69d, #00bc8c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-success.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-success.btn:hover {\n  background: #00bc8c linear-gradient(180deg, #26a685, #009670) repeat-x !important;\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-success.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-success.btn:active, .dark-mode .bg-gradient-success.btn.active {\n  background: #00bc8c linear-gradient(180deg, #269b7d, #008966) repeat-x !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-success.btn:disabled, .dark-mode .bg-gradient-success.btn.disabled {\n  background-image: none !important;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-info {\n  background: #3498db linear-gradient(180deg, #52a7e0, #3498db) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-info.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-info.btn:hover {\n  background: #3498db linear-gradient(180deg, #4497ce, #2384c6) repeat-x !important;\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-info.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-info.btn:active, .dark-mode .bg-gradient-info.btn.active {\n  background: #3498db linear-gradient(180deg, #4291c5, #217dbb) repeat-x !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-info.btn:disabled, .dark-mode .bg-gradient-info.btn.disabled {\n  background-image: none !important;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-warning {\n  background: #f39c12 linear-gradient(180deg, #f5ab36, #f39c12) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-warning.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-warning.btn:hover {\n  background: #f39c12 linear-gradient(180deg, #da982f, #d4860b) repeat-x !important;\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-warning.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-warning.btn:active, .dark-mode .bg-gradient-warning.btn.active {\n  background: #f39c12 linear-gradient(180deg, #d0922f, #c87f0a) repeat-x !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-warning.btn:disabled, .dark-mode .bg-gradient-warning.btn.disabled {\n  background-image: none !important;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-danger {\n  background: #e74c3c linear-gradient(180deg, #eb6759, #e74c3c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-danger.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-danger.btn:hover {\n  background: #e74c3c linear-gradient(180deg, #e64d3e, #e12e1c) repeat-x !important;\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-danger.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-danger.btn:active, .dark-mode .bg-gradient-danger.btn.active {\n  background: #e74c3c linear-gradient(180deg, #dc4c3d, #d62c1a) repeat-x !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-danger.btn:disabled, .dark-mode .bg-gradient-danger.btn.disabled {\n  background-image: none !important;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-light {\n  background: #f8f9fa linear-gradient(180deg, #f9fafb, #f8f9fa) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-light.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-light.btn:hover {\n  background: #f8f9fa linear-gradient(180deg, #e6eaed, #e2e6ea) repeat-x !important;\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-light.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-light.btn:active, .dark-mode .bg-gradient-light.btn.active {\n  background: #f8f9fa linear-gradient(180deg, #e0e4e9, #dae0e5) repeat-x !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-light.btn:disabled, .dark-mode .bg-gradient-light.btn.disabled {\n  background-image: none !important;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-dark.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-dark.btn:active, .dark-mode .bg-gradient-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-dark.btn:disabled, .dark-mode .bg-gradient-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-lightblue {\n  background: #86bad8 linear-gradient(180deg, #98c4de, #86bad8) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-lightblue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:hover {\n  background: #86bad8 linear-gradient(180deg, #7fb6d6, #69a9cf) repeat-x !important;\n  border-color: #5fa4cc;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-lightblue.btn:active, .dark-mode .bg-gradient-lightblue.btn.active {\n  background: #86bad8 linear-gradient(180deg, #77b2d4, #5fa4cc) repeat-x !important;\n  border-color: #559ec9;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-lightblue.btn:disabled, .dark-mode .bg-gradient-lightblue.btn.disabled {\n  background-image: none !important;\n  border-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-navy {\n  background: #002c59 linear-gradient(180deg, #264b71, #002c59) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-navy.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-navy.btn:hover {\n  background: #002c59 linear-gradient(180deg, #263b51, #001932) repeat-x !important;\n  border-color: #001226;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-navy.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-navy.btn:active, .dark-mode .bg-gradient-navy.btn.active {\n  background: #002c59 linear-gradient(180deg, #263646, #001226) repeat-x !important;\n  border-color: #000c19;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-navy.btn:disabled, .dark-mode .bg-gradient-navy.btn.disabled {\n  background-image: none !important;\n  border-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-olive {\n  background: #74c8a3 linear-gradient(180deg, #89d0b0, #74c8a3) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-olive.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-olive.btn:hover {\n  background: #74c8a3 linear-gradient(180deg, #72c7a1, #59bd90) repeat-x !important;\n  border-color: #50b98a;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-olive.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-olive.btn:active, .dark-mode .bg-gradient-olive.btn.active {\n  background: #74c8a3 linear-gradient(180deg, #6ac49c, #50b98a) repeat-x !important;\n  border-color: #48b484;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-olive.btn:disabled, .dark-mode .bg-gradient-olive.btn.disabled {\n  background-image: none !important;\n  border-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lime {\n  background: #67ffa9 linear-gradient(180deg, #7effb6, #67ffa9) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-lime.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-lime.btn:hover {\n  background: #67ffa9 linear-gradient(180deg, #5dffa4, #41ff94) repeat-x !important;\n  border-color: #34ff8d;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-lime.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-lime.btn:active, .dark-mode .bg-gradient-lime.btn.active {\n  background: #67ffa9 linear-gradient(180deg, #52ff9e, #34ff8d) repeat-x !important;\n  border-color: #27ff86;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-lime.btn:disabled, .dark-mode .bg-gradient-lime.btn.disabled {\n  background-image: none !important;\n  border-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-fuchsia {\n  background: #f672d8 linear-gradient(180deg, #f787de, #f672d8) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-fuchsia.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:hover {\n  background: #f672d8 linear-gradient(180deg, #f569d6, #f44ece) repeat-x !important;\n  border-color: #f342cb;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-fuchsia.btn:active, .dark-mode .bg-gradient-fuchsia.btn.active {\n  background: #f672d8 linear-gradient(180deg, #f55ed3, #f342cb) repeat-x !important;\n  border-color: #f236c8;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-fuchsia.btn:disabled, .dark-mode .bg-gradient-fuchsia.btn.disabled {\n  background-image: none !important;\n  border-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-maroon {\n  background: #ed6c9b linear-gradient(180deg, #ef82aa, #ed6c9b) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-maroon.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-maroon.btn:hover {\n  background: #ed6c9b linear-gradient(180deg, #ec6596, #e84a84) repeat-x !important;\n  border-color: #e73f7c;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-maroon.btn:active, .dark-mode .bg-gradient-maroon.btn.active {\n  background: #ed6c9b linear-gradient(180deg, #eb5c90, #e73f7c) repeat-x !important;\n  border-color: #e63475;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-maroon.btn:disabled, .dark-mode .bg-gradient-maroon.btn.disabled {\n  background-image: none !important;\n  border-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-blue {\n  background: #3f6791 linear-gradient(180deg, #5c7ea2, #3f6791) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-blue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-blue.btn:hover {\n  background: #3f6791 linear-gradient(180deg, #526e8b, #335476) repeat-x !important;\n  border-color: #304e6d;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-blue.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-blue.btn:active, .dark-mode .bg-gradient-blue.btn.active {\n  background: #3f6791 linear-gradient(180deg, #4f6883, #304e6d) repeat-x !important;\n  border-color: #2c4765;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-blue.btn:disabled, .dark-mode .bg-gradient-blue.btn.disabled {\n  background-image: none !important;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-indigo {\n  background: #6610f2 linear-gradient(180deg, #7d34f4, #6610f2) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-indigo.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-indigo.btn:hover {\n  background: #6610f2 linear-gradient(180deg, #7030d7, #560bd0) repeat-x !important;\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-indigo.btn:active, .dark-mode .bg-gradient-indigo.btn.active {\n  background: #6610f2 linear-gradient(180deg, #6b2fcd, #510bc4) repeat-x !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-indigo.btn:disabled, .dark-mode .bg-gradient-indigo.btn.disabled {\n  background-image: none !important;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-purple {\n  background: #6f42c1 linear-gradient(180deg, #855eca, #6f42c1) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-purple.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-purple.btn:hover {\n  background: #6f42c1 linear-gradient(180deg, #7655b4, #5e37a6) repeat-x !important;\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-purple.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-purple.btn:active, .dark-mode .bg-gradient-purple.btn.active {\n  background: #6f42c1 linear-gradient(180deg, #7252ab, #59339d) repeat-x !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-purple.btn:disabled, .dark-mode .bg-gradient-purple.btn.disabled {\n  background-image: none !important;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-pink {\n  background: #e83e8c linear-gradient(180deg, #eb5b9d, #e83e8c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-pink.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-pink.btn:hover {\n  background: #e83e8c linear-gradient(180deg, #e83e8c, #e41c78) repeat-x !important;\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-pink.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-pink.btn:active, .dark-mode .bg-gradient-pink.btn.active {\n  background: #e83e8c linear-gradient(180deg, #df3c87, #d91a72) repeat-x !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-pink.btn:disabled, .dark-mode .bg-gradient-pink.btn.disabled {\n  background-image: none !important;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-red {\n  background: #e74c3c linear-gradient(180deg, #eb6759, #e74c3c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-red.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-red.btn:hover {\n  background: #e74c3c linear-gradient(180deg, #e64d3e, #e12e1c) repeat-x !important;\n  border-color: #d62c1a;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-red.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-red.btn:active, .dark-mode .bg-gradient-red.btn.active {\n  background: #e74c3c linear-gradient(180deg, #dc4c3d, #d62c1a) repeat-x !important;\n  border-color: #ca2a19;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-red.btn:disabled, .dark-mode .bg-gradient-red.btn.disabled {\n  background-image: none !important;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-orange {\n  background: #fd7e14 linear-gradient(180deg, #fd9137, #fd7e14) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-orange.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-orange.btn:hover {\n  background: #fd7e14 linear-gradient(180deg, #ec8128, #e96b02) repeat-x !important;\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-orange.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-orange.btn:active, .dark-mode .bg-gradient-orange.btn.active {\n  background: #fd7e14 linear-gradient(180deg, #e17c28, #dc6502) repeat-x !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-orange.btn:disabled, .dark-mode .bg-gradient-orange.btn.disabled {\n  background-image: none !important;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-yellow {\n  background: #f39c12 linear-gradient(180deg, #f5ab36, #f39c12) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-yellow.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-yellow.btn:hover {\n  background: #f39c12 linear-gradient(180deg, #da982f, #d4860b) repeat-x !important;\n  border-color: #c87f0a;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-yellow.btn:active, .dark-mode .bg-gradient-yellow.btn.active {\n  background: #f39c12 linear-gradient(180deg, #d0922f, #c87f0a) repeat-x !important;\n  border-color: #bc770a;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-yellow.btn:disabled, .dark-mode .bg-gradient-yellow.btn.disabled {\n  background-image: none !important;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-green {\n  background: #00bc8c linear-gradient(180deg, #26c69d, #00bc8c) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-green.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-green.btn:hover {\n  background: #00bc8c linear-gradient(180deg, #26a685, #009670) repeat-x !important;\n  border-color: #008966;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-green.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-green.btn:active, .dark-mode .bg-gradient-green.btn.active {\n  background: #00bc8c linear-gradient(180deg, #269b7d, #008966) repeat-x !important;\n  border-color: #007c5d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-green.btn:disabled, .dark-mode .bg-gradient-green.btn.disabled {\n  background-image: none !important;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-teal {\n  background: #20c997 linear-gradient(180deg, #41d1a7, #20c997) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-teal.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-teal.btn:hover {\n  background: #20c997 linear-gradient(180deg, #3db592, #1ba87e) repeat-x !important;\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-teal.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-teal.btn:active, .dark-mode .bg-gradient-teal.btn.active {\n  background: #20c997 linear-gradient(180deg, #3bac8b, #199d76) repeat-x !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-teal.btn:disabled, .dark-mode .bg-gradient-teal.btn.disabled {\n  background-image: none !important;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-cyan {\n  background: #3498db linear-gradient(180deg, #52a7e0, #3498db) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-cyan.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-cyan.btn:hover {\n  background: #3498db linear-gradient(180deg, #4497ce, #2384c6) repeat-x !important;\n  border-color: #217dbb;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-cyan.btn:active, .dark-mode .bg-gradient-cyan.btn.active {\n  background: #3498db linear-gradient(180deg, #4291c5, #217dbb) repeat-x !important;\n  border-color: #1f76b0;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-cyan.btn:disabled, .dark-mode .bg-gradient-cyan.btn.disabled {\n  background-image: none !important;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-white {\n  background: #fff linear-gradient(180deg, white, #fff) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-white.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-white.btn:hover {\n  background: #fff linear-gradient(180deg, #efefef, #ececec) repeat-x !important;\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-white.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-white.btn:active, .dark-mode .bg-gradient-white.btn.active {\n  background: #fff linear-gradient(180deg, #e9e9e9, #e6e6e6) repeat-x !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-white.btn:disabled, .dark-mode .bg-gradient-white.btn.disabled {\n  background-image: none !important;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .bg-gradient-gray {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-gray.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-gray.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-gray.btn:active, .dark-mode .bg-gradient-gray.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray.btn:disabled, .dark-mode .bg-gradient-gray.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active,\n.show > .dark-mode .bg-gradient-gray-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .dark-mode .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active, .dark-mode .bg-gradient-gray-dark.btn:active, .dark-mode .bg-gradient-gray-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.dark-mode .bg-gradient-gray-dark.btn:disabled, .dark-mode .bg-gradient-gray-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-primary .btn-link,\n.dark-mode .accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-primary .nav-tabs .nav-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-primary .btn-link:hover,\n.dark-mode .accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-primary .nav-tabs .nav-link:hover {\n  color: #28415c;\n}\n\n.dark-mode .accent-primary .dropdown-item:active, .dark-mode .accent-primary .dropdown-item.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .accent-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-primary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-primary .custom-select:focus,\n.dark-mode .accent-primary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-primary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #85a7ca;\n}\n\n.dark-mode .accent-primary .page-item .page-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-primary .page-item.active a,\n.dark-mode .accent-primary .page-item.active .page-link {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-primary .page-item.disabled a,\n.dark-mode .accent-primary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-primary .page-item .page-link:hover, .dark-mode .dark-mode.accent-primary .page-item .page-link:focus {\n  color: #4774a3;\n}\n\n.dark-mode .accent-secondary .btn-link,\n.dark-mode .accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-secondary .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-secondary .btn-link:hover,\n.dark-mode .accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-secondary .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.dark-mode .accent-secondary .dropdown-item:active, .dark-mode .accent-secondary .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .accent-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-secondary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-secondary .custom-select:focus,\n.dark-mode .accent-secondary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-secondary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.dark-mode .accent-secondary .page-item .page-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-secondary .page-item.active a,\n.dark-mode .accent-secondary .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-secondary .page-item.disabled a,\n.dark-mode .accent-secondary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-secondary .page-item .page-link:hover, .dark-mode .dark-mode.accent-secondary .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.dark-mode .accent-success .btn-link,\n.dark-mode .accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-success .nav-tabs .nav-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-success .btn-link:hover,\n.dark-mode .accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-success .nav-tabs .nav-link:hover {\n  color: #007053;\n}\n\n.dark-mode .accent-success .dropdown-item:active, .dark-mode .accent-success .dropdown-item.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .accent-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-success .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-success .custom-select:focus,\n.dark-mode .accent-success .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-success .custom-file-input:focus ~ .custom-file-label {\n  border-color: #3dffcd;\n}\n\n.dark-mode .accent-success .page-item .page-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-success .page-item.active a,\n.dark-mode .accent-success .page-item.active .page-link {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-success .page-item.disabled a,\n.dark-mode .accent-success .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-success .page-item .page-link:hover, .dark-mode .dark-mode.accent-success .page-item .page-link:focus {\n  color: #00d69f;\n}\n\n.dark-mode .accent-info .btn-link,\n.dark-mode .accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-info .nav-tabs .nav-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-info .btn-link:hover,\n.dark-mode .accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-info .nav-tabs .nav-link:hover {\n  color: #1d6fa5;\n}\n\n.dark-mode .accent-info .dropdown-item:active, .dark-mode .accent-info .dropdown-item.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .accent-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-info .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-info .custom-select:focus,\n.dark-mode .accent-info .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-info .custom-file-input:focus ~ .custom-file-label {\n  border-color: #a0cfee;\n}\n\n.dark-mode .accent-info .page-item .page-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-info .page-item.active a,\n.dark-mode .accent-info .page-item.active .page-link {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-info .page-item.disabled a,\n.dark-mode .accent-info .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-info .page-item .page-link:hover, .dark-mode .dark-mode.accent-info .page-item .page-link:focus {\n  color: #4aa3df;\n}\n\n.dark-mode .accent-warning .btn-link,\n.dark-mode .accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-warning .nav-tabs .nav-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-warning .btn-link:hover,\n.dark-mode .accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-warning .nav-tabs .nav-link:hover {\n  color: #b06f09;\n}\n\n.dark-mode .accent-warning .dropdown-item:active, .dark-mode .accent-warning .dropdown-item.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .accent-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-warning .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-warning .custom-select:focus,\n.dark-mode .accent-warning .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-warning .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .accent-warning .page-item .page-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-warning .page-item.active a,\n.dark-mode .accent-warning .page-item.active .page-link {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #fff;\n}\n\n.dark-mode .accent-warning .page-item.disabled a,\n.dark-mode .accent-warning .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-warning .page-item .page-link:hover, .dark-mode .dark-mode.accent-warning .page-item .page-link:focus {\n  color: #f4a62a;\n}\n\n.dark-mode .accent-danger .btn-link,\n.dark-mode .accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-danger .nav-tabs .nav-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-danger .btn-link:hover,\n.dark-mode .accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-danger .nav-tabs .nav-link:hover {\n  color: #bf2718;\n}\n\n.dark-mode .accent-danger .dropdown-item:active, .dark-mode .accent-danger .dropdown-item.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .accent-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-danger .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-danger .custom-select:focus,\n.dark-mode .accent-danger .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-danger .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .accent-danger .page-item .page-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-danger .page-item.active a,\n.dark-mode .accent-danger .page-item.active .page-link {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-danger .page-item.disabled a,\n.dark-mode .accent-danger .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-danger .page-item .page-link:hover, .dark-mode .dark-mode.accent-danger .page-item .page-link:focus {\n  color: #ea6153;\n}\n\n.dark-mode .accent-light .btn-link,\n.dark-mode .accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-light .nav-tabs .nav-link {\n  color: #f8f9fa;\n}\n\n.dark-mode .accent-light .btn-link:hover,\n.dark-mode .accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-light .nav-tabs .nav-link:hover {\n  color: #cbd3da;\n}\n\n.dark-mode .accent-light .dropdown-item:active, .dark-mode .accent-light .dropdown-item.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.dark-mode .accent-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-light .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-light .custom-select:focus,\n.dark-mode .accent-light .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-light .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.dark-mode .accent-light .page-item .page-link {\n  color: #f8f9fa;\n}\n\n.dark-mode .accent-light .page-item.active a,\n.dark-mode .accent-light .page-item.active .page-link {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #fff;\n}\n\n.dark-mode .accent-light .page-item.disabled a,\n.dark-mode .accent-light .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-light .page-item .page-link:hover, .dark-mode .dark-mode.accent-light .page-item .page-link:focus {\n  color: white;\n}\n\n.dark-mode .accent-dark .btn-link,\n.dark-mode .accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-dark .btn-link:hover,\n.dark-mode .accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.dark-mode .accent-dark .dropdown-item:active, .dark-mode .accent-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .accent-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-dark .custom-select:focus,\n.dark-mode .accent-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.dark-mode .accent-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-dark .page-item.active a,\n.dark-mode .accent-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-dark .page-item.disabled a,\n.dark-mode .accent-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-dark .page-item .page-link:hover, .dark-mode .dark-mode.accent-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-primary {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-secondary {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-success {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-info {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-warning {\n  color: #1f2d3d;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-danger {\n  color: #fff;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-light {\n  color: #1f2d3d;\n}\n\n.dark-mode [class*=\"accent-\"] a.btn-dark {\n  color: #fff;\n}\n\n.dark-mode .accent-lightblue .btn-link,\n.dark-mode .accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-lightblue .nav-tabs .nav-link {\n  color: #86bad8;\n}\n\n.dark-mode .accent-lightblue .btn-link:hover,\n.dark-mode .accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-lightblue .nav-tabs .nav-link:hover {\n  color: #4c99c6;\n}\n\n.dark-mode .accent-lightblue .dropdown-item:active, .dark-mode .accent-lightblue .dropdown-item.active {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #86bad8;\n  border-color: #3c8dbc;\n}\n\n.dark-mode .accent-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-lightblue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-lightblue .custom-select:focus,\n.dark-mode .accent-lightblue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-lightblue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .accent-lightblue .page-item .page-link {\n  color: #86bad8;\n}\n\n.dark-mode .accent-lightblue .page-item.active a,\n.dark-mode .accent-lightblue .page-item.active .page-link {\n  background-color: #86bad8;\n  border-color: #86bad8;\n  color: #fff;\n}\n\n.dark-mode .accent-lightblue .page-item.disabled a,\n.dark-mode .accent-lightblue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-lightblue .page-item .page-link:hover, .dark-mode .dark-mode.accent-lightblue .page-item .page-link:focus {\n  color: #99c5de;\n}\n\n.dark-mode .accent-navy .btn-link,\n.dark-mode .accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-navy .nav-tabs .nav-link {\n  color: #002c59;\n}\n\n.dark-mode .accent-navy .btn-link:hover,\n.dark-mode .accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-navy .nav-tabs .nav-link:hover {\n  color: #00060c;\n}\n\n.dark-mode .accent-navy .dropdown-item:active, .dark-mode .accent-navy .dropdown-item.active {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .accent-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #002c59;\n  border-color: black;\n}\n\n.dark-mode .accent-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-navy .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-navy .custom-select:focus,\n.dark-mode .accent-navy .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-navy .custom-file-input:focus ~ .custom-file-label {\n  border-color: #006ad8;\n}\n\n.dark-mode .accent-navy .page-item .page-link {\n  color: #002c59;\n}\n\n.dark-mode .accent-navy .page-item.active a,\n.dark-mode .accent-navy .page-item.active .page-link {\n  background-color: #002c59;\n  border-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .accent-navy .page-item.disabled a,\n.dark-mode .accent-navy .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-navy .page-item .page-link:hover, .dark-mode .dark-mode.accent-navy .page-item .page-link:focus {\n  color: #003872;\n}\n\n.dark-mode .accent-olive .btn-link,\n.dark-mode .accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-olive .nav-tabs .nav-link {\n  color: #74c8a3;\n}\n\n.dark-mode .accent-olive .btn-link:hover,\n.dark-mode .accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-olive .nav-tabs .nav-link:hover {\n  color: #44ab7d;\n}\n\n.dark-mode .accent-olive .dropdown-item:active, .dark-mode .accent-olive .dropdown-item.active {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #74c8a3;\n  border-color: #3d9970;\n}\n\n.dark-mode .accent-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-olive .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-olive .custom-select:focus,\n.dark-mode .accent-olive .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-olive .custom-file-input:focus ~ .custom-file-label {\n  border-color: #cfecdf;\n}\n\n.dark-mode .accent-olive .page-item .page-link {\n  color: #74c8a3;\n}\n\n.dark-mode .accent-olive .page-item.active a,\n.dark-mode .accent-olive .page-item.active .page-link {\n  background-color: #74c8a3;\n  border-color: #74c8a3;\n  color: #fff;\n}\n\n.dark-mode .accent-olive .page-item.disabled a,\n.dark-mode .accent-olive .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-olive .page-item .page-link:hover, .dark-mode .dark-mode.accent-olive .page-item .page-link:focus {\n  color: #87cfaf;\n}\n\n.dark-mode .accent-lime .btn-link,\n.dark-mode .accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-lime .nav-tabs .nav-link {\n  color: #67ffa9;\n}\n\n.dark-mode .accent-lime .btn-link:hover,\n.dark-mode .accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-lime .nav-tabs .nav-link:hover {\n  color: #1bff7e;\n}\n\n.dark-mode .accent-lime .dropdown-item:active, .dark-mode .accent-lime .dropdown-item.active {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #67ffa9;\n  border-color: #01ff70;\n}\n\n.dark-mode .accent-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-lime .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-lime .custom-select:focus,\n.dark-mode .accent-lime .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-lime .custom-file-input:focus ~ .custom-file-label {\n  border-color: #e7fff1;\n}\n\n.dark-mode .accent-lime .page-item .page-link {\n  color: #67ffa9;\n}\n\n.dark-mode .accent-lime .page-item.active a,\n.dark-mode .accent-lime .page-item.active .page-link {\n  background-color: #67ffa9;\n  border-color: #67ffa9;\n  color: #fff;\n}\n\n.dark-mode .accent-lime .page-item.disabled a,\n.dark-mode .accent-lime .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-lime .page-item .page-link:hover, .dark-mode .dark-mode.accent-lime .page-item .page-link:focus {\n  color: #81ffb8;\n}\n\n.dark-mode .accent-fuchsia .btn-link,\n.dark-mode .accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-fuchsia .nav-tabs .nav-link {\n  color: #f672d8;\n}\n\n.dark-mode .accent-fuchsia .btn-link:hover,\n.dark-mode .accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-fuchsia .nav-tabs .nav-link:hover {\n  color: #f22ac5;\n}\n\n.dark-mode .accent-fuchsia .dropdown-item:active, .dark-mode .accent-fuchsia .dropdown-item.active {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f672d8;\n  border-color: #f012be;\n}\n\n.dark-mode .accent-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-fuchsia .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-fuchsia .custom-select:focus,\n.dark-mode .accent-fuchsia .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-fuchsia .custom-file-input:focus ~ .custom-file-label {\n  border-color: #feeaf9;\n}\n\n.dark-mode .accent-fuchsia .page-item .page-link {\n  color: #f672d8;\n}\n\n.dark-mode .accent-fuchsia .page-item.active a,\n.dark-mode .accent-fuchsia .page-item.active .page-link {\n  background-color: #f672d8;\n  border-color: #f672d8;\n  color: #fff;\n}\n\n.dark-mode .accent-fuchsia .page-item.disabled a,\n.dark-mode .accent-fuchsia .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-fuchsia .page-item .page-link:hover, .dark-mode .dark-mode.accent-fuchsia .page-item .page-link:focus {\n  color: #f88adf;\n}\n\n.dark-mode .accent-maroon .btn-link,\n.dark-mode .accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-maroon .nav-tabs .nav-link {\n  color: #ed6c9b;\n}\n\n.dark-mode .accent-maroon .btn-link:hover,\n.dark-mode .accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-maroon .nav-tabs .nav-link:hover {\n  color: #e4286d;\n}\n\n.dark-mode .accent-maroon .dropdown-item:active, .dark-mode .accent-maroon .dropdown-item.active {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ed6c9b;\n  border-color: #d81b60;\n}\n\n.dark-mode .accent-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-maroon .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-maroon .custom-select:focus,\n.dark-mode .accent-maroon .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-maroon .custom-file-input:focus ~ .custom-file-label {\n  border-color: #fbdee8;\n}\n\n.dark-mode .accent-maroon .page-item .page-link {\n  color: #ed6c9b;\n}\n\n.dark-mode .accent-maroon .page-item.active a,\n.dark-mode .accent-maroon .page-item.active .page-link {\n  background-color: #ed6c9b;\n  border-color: #ed6c9b;\n  color: #fff;\n}\n\n.dark-mode .accent-maroon .page-item.disabled a,\n.dark-mode .accent-maroon .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-maroon .page-item .page-link:hover, .dark-mode .dark-mode.accent-maroon .page-item .page-link:focus {\n  color: #f083ab;\n}\n\n.dark-mode .accent-blue .btn-link,\n.dark-mode .accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-blue .nav-tabs .nav-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-blue .btn-link:hover,\n.dark-mode .accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-blue .nav-tabs .nav-link:hover {\n  color: #28415c;\n}\n\n.dark-mode .accent-blue .dropdown-item:active, .dark-mode .accent-blue .dropdown-item.active {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3f6791;\n  border-color: #20344a;\n}\n\n.dark-mode .accent-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-blue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-blue .custom-select:focus,\n.dark-mode .accent-blue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-blue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #85a7ca;\n}\n\n.dark-mode .accent-blue .page-item .page-link {\n  color: #3f6791;\n}\n\n.dark-mode .accent-blue .page-item.active a,\n.dark-mode .accent-blue .page-item.active .page-link {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .accent-blue .page-item.disabled a,\n.dark-mode .accent-blue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-blue .page-item .page-link:hover, .dark-mode .dark-mode.accent-blue .page-item .page-link:focus {\n  color: #4774a3;\n}\n\n.dark-mode .accent-indigo .btn-link,\n.dark-mode .accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-indigo .nav-tabs .nav-link {\n  color: #6610f2;\n}\n\n.dark-mode .accent-indigo .btn-link:hover,\n.dark-mode .accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-indigo .nav-tabs .nav-link:hover {\n  color: #4709ac;\n}\n\n.dark-mode .accent-indigo .dropdown-item:active, .dark-mode .accent-indigo .dropdown-item.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .accent-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.dark-mode .accent-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-indigo .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-indigo .custom-select:focus,\n.dark-mode .accent-indigo .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-indigo .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b389f9;\n}\n\n.dark-mode .accent-indigo .page-item .page-link {\n  color: #6610f2;\n}\n\n.dark-mode .accent-indigo .page-item.active a,\n.dark-mode .accent-indigo .page-item.active .page-link {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .accent-indigo .page-item.disabled a,\n.dark-mode .accent-indigo .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-indigo .page-item .page-link:hover, .dark-mode .dark-mode.accent-indigo .page-item .page-link:focus {\n  color: #7528f3;\n}\n\n.dark-mode .accent-purple .btn-link,\n.dark-mode .accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-purple .nav-tabs .nav-link {\n  color: #6f42c1;\n}\n\n.dark-mode .accent-purple .btn-link:hover,\n.dark-mode .accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-purple .nav-tabs .nav-link:hover {\n  color: #4e2d89;\n}\n\n.dark-mode .accent-purple .dropdown-item:active, .dark-mode .accent-purple .dropdown-item.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .accent-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.dark-mode .accent-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-purple .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-purple .custom-select:focus,\n.dark-mode .accent-purple .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-purple .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .accent-purple .page-item .page-link {\n  color: #6f42c1;\n}\n\n.dark-mode .accent-purple .page-item.active a,\n.dark-mode .accent-purple .page-item.active .page-link {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .accent-purple .page-item.disabled a,\n.dark-mode .accent-purple .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-purple .page-item .page-link:hover, .dark-mode .dark-mode.accent-purple .page-item .page-link:focus {\n  color: #7e55c7;\n}\n\n.dark-mode .accent-pink .btn-link,\n.dark-mode .accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-pink .nav-tabs .nav-link {\n  color: #e83e8c;\n}\n\n.dark-mode .accent-pink .btn-link:hover,\n.dark-mode .accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-pink .nav-tabs .nav-link:hover {\n  color: #c21766;\n}\n\n.dark-mode .accent-pink .dropdown-item:active, .dark-mode .accent-pink .dropdown-item.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .accent-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.dark-mode .accent-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-pink .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-pink .custom-select:focus,\n.dark-mode .accent-pink .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-pink .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .accent-pink .page-item .page-link {\n  color: #e83e8c;\n}\n\n.dark-mode .accent-pink .page-item.active a,\n.dark-mode .accent-pink .page-item.active .page-link {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .accent-pink .page-item.disabled a,\n.dark-mode .accent-pink .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-pink .page-item .page-link:hover, .dark-mode .dark-mode.accent-pink .page-item .page-link:focus {\n  color: #eb559a;\n}\n\n.dark-mode .accent-red .btn-link,\n.dark-mode .accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-red .nav-tabs .nav-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-red .btn-link:hover,\n.dark-mode .accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-red .nav-tabs .nav-link:hover {\n  color: #bf2718;\n}\n\n.dark-mode .accent-red .dropdown-item:active, .dark-mode .accent-red .dropdown-item.active {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e74c3c;\n  border-color: #a82315;\n}\n\n.dark-mode .accent-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-red .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-red .custom-select:focus,\n.dark-mode .accent-red .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-red .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .accent-red .page-item .page-link {\n  color: #e74c3c;\n}\n\n.dark-mode .accent-red .page-item.active a,\n.dark-mode .accent-red .page-item.active .page-link {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .accent-red .page-item.disabled a,\n.dark-mode .accent-red .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-red .page-item .page-link:hover, .dark-mode .dark-mode.accent-red .page-item .page-link:focus {\n  color: #ea6153;\n}\n\n.dark-mode .accent-orange .btn-link,\n.dark-mode .accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-orange .nav-tabs .nav-link {\n  color: #fd7e14;\n}\n\n.dark-mode .accent-orange .btn-link:hover,\n.dark-mode .accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-orange .nav-tabs .nav-link:hover {\n  color: #c35a02;\n}\n\n.dark-mode .accent-orange .dropdown-item:active, .dark-mode .accent-orange .dropdown-item.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.dark-mode .accent-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-orange .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-orange .custom-select:focus,\n.dark-mode .accent-orange .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-orange .custom-file-input:focus ~ .custom-file-label {\n  border-color: #fec392;\n}\n\n.dark-mode .accent-orange .page-item .page-link {\n  color: #fd7e14;\n}\n\n.dark-mode .accent-orange .page-item.active a,\n.dark-mode .accent-orange .page-item.active .page-link {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #fff;\n}\n\n.dark-mode .accent-orange .page-item.disabled a,\n.dark-mode .accent-orange .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-orange .page-item .page-link:hover, .dark-mode .dark-mode.accent-orange .page-item .page-link:focus {\n  color: #fd8c2d;\n}\n\n.dark-mode .accent-yellow .btn-link,\n.dark-mode .accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-yellow .nav-tabs .nav-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-yellow .btn-link:hover,\n.dark-mode .accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-yellow .nav-tabs .nav-link:hover {\n  color: #b06f09;\n}\n\n.dark-mode .accent-yellow .dropdown-item:active, .dark-mode .accent-yellow .dropdown-item.active {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f39c12;\n  border-color: #976008;\n}\n\n.dark-mode .accent-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-yellow .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-yellow .custom-select:focus,\n.dark-mode .accent-yellow .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-yellow .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .accent-yellow .page-item .page-link {\n  color: #f39c12;\n}\n\n.dark-mode .accent-yellow .page-item.active a,\n.dark-mode .accent-yellow .page-item.active .page-link {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #fff;\n}\n\n.dark-mode .accent-yellow .page-item.disabled a,\n.dark-mode .accent-yellow .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-yellow .page-item .page-link:hover, .dark-mode .dark-mode.accent-yellow .page-item .page-link:focus {\n  color: #f4a62a;\n}\n\n.dark-mode .accent-green .btn-link,\n.dark-mode .accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-green .nav-tabs .nav-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-green .btn-link:hover,\n.dark-mode .accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-green .nav-tabs .nav-link:hover {\n  color: #007053;\n}\n\n.dark-mode .accent-green .dropdown-item:active, .dark-mode .accent-green .dropdown-item.active {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #00bc8c;\n  border-color: #005640;\n}\n\n.dark-mode .accent-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-green .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-green .custom-select:focus,\n.dark-mode .accent-green .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-green .custom-file-input:focus ~ .custom-file-label {\n  border-color: #3dffcd;\n}\n\n.dark-mode .accent-green .page-item .page-link {\n  color: #00bc8c;\n}\n\n.dark-mode .accent-green .page-item.active a,\n.dark-mode .accent-green .page-item.active .page-link {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .accent-green .page-item.disabled a,\n.dark-mode .accent-green .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-green .page-item .page-link:hover, .dark-mode .dark-mode.accent-green .page-item .page-link:focus {\n  color: #00d69f;\n}\n\n.dark-mode .accent-teal .btn-link,\n.dark-mode .accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-teal .nav-tabs .nav-link {\n  color: #20c997;\n}\n\n.dark-mode .accent-teal .btn-link:hover,\n.dark-mode .accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-teal .nav-tabs .nav-link:hover {\n  color: #158765;\n}\n\n.dark-mode .accent-teal .dropdown-item:active, .dark-mode .accent-teal .dropdown-item.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .accent-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.dark-mode .accent-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-teal .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-teal .custom-select:focus,\n.dark-mode .accent-teal .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-teal .custom-file-input:focus ~ .custom-file-label {\n  border-color: #7eeaca;\n}\n\n.dark-mode .accent-teal .page-item .page-link {\n  color: #20c997;\n}\n\n.dark-mode .accent-teal .page-item.active a,\n.dark-mode .accent-teal .page-item.active .page-link {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .accent-teal .page-item.disabled a,\n.dark-mode .accent-teal .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-teal .page-item .page-link:hover, .dark-mode .dark-mode.accent-teal .page-item .page-link:focus {\n  color: #26dca6;\n}\n\n.dark-mode .accent-cyan .btn-link,\n.dark-mode .accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-cyan .nav-tabs .nav-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-cyan .btn-link:hover,\n.dark-mode .accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-cyan .nav-tabs .nav-link:hover {\n  color: #1d6fa5;\n}\n\n.dark-mode .accent-cyan .dropdown-item:active, .dark-mode .accent-cyan .dropdown-item.active {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3498db;\n  border-color: #196090;\n}\n\n.dark-mode .accent-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-cyan .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-cyan .custom-select:focus,\n.dark-mode .accent-cyan .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-cyan .custom-file-input:focus ~ .custom-file-label {\n  border-color: #a0cfee;\n}\n\n.dark-mode .accent-cyan .page-item .page-link {\n  color: #3498db;\n}\n\n.dark-mode .accent-cyan .page-item.active a,\n.dark-mode .accent-cyan .page-item.active .page-link {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .accent-cyan .page-item.disabled a,\n.dark-mode .accent-cyan .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-cyan .page-item .page-link:hover, .dark-mode .dark-mode.accent-cyan .page-item .page-link:focus {\n  color: #4aa3df;\n}\n\n.dark-mode .accent-white .btn-link,\n.dark-mode .accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-white .nav-tabs .nav-link {\n  color: #fff;\n}\n\n.dark-mode .accent-white .btn-link:hover,\n.dark-mode .accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-white .nav-tabs .nav-link:hover {\n  color: #d9d9d9;\n}\n\n.dark-mode .accent-white .dropdown-item:active, .dark-mode .accent-white .dropdown-item.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .accent-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.dark-mode .accent-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-white .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-white .custom-select:focus,\n.dark-mode .accent-white .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-white .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.dark-mode .accent-white .page-item .page-link {\n  color: #fff;\n}\n\n.dark-mode .accent-white .page-item.active a,\n.dark-mode .accent-white .page-item.active .page-link {\n  background-color: #fff;\n  border-color: #fff;\n  color: #fff;\n}\n\n.dark-mode .accent-white .page-item.disabled a,\n.dark-mode .accent-white .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-white .page-item .page-link:hover, .dark-mode .dark-mode.accent-white .page-item .page-link:focus {\n  color: white;\n}\n\n.dark-mode .accent-gray .btn-link,\n.dark-mode .accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-gray .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray .btn-link:hover,\n.dark-mode .accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-gray .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.dark-mode .accent-gray .dropdown-item:active, .dark-mode .accent-gray .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.dark-mode .accent-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-gray .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-gray .custom-select:focus,\n.dark-mode .accent-gray .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-gray .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.dark-mode .accent-gray .page-item .page-link {\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray .page-item.active a,\n.dark-mode .accent-gray .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .accent-gray .page-item.disabled a,\n.dark-mode .accent-gray .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-gray .page-item .page-link:hover, .dark-mode .dark-mode.accent-gray .page-item .page-link:focus {\n  color: #78828a;\n}\n\n.dark-mode .accent-gray-dark .btn-link,\n.dark-mode .accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.dark-mode .accent-gray-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray-dark .btn-link:hover,\n.dark-mode .accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.dark-mode .accent-gray-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.dark-mode .accent-gray-dark .dropdown-item:active, .dark-mode .accent-gray-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.dark-mode .accent-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.dark-mode .accent-gray-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.dark-mode .accent-gray-dark .custom-select:focus,\n.dark-mode .accent-gray-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.dark-mode .accent-gray-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.dark-mode .accent-gray-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray-dark .page-item.active a,\n.dark-mode .accent-gray-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .accent-gray-dark .page-item.disabled a,\n.dark-mode .accent-gray-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.dark-mode .accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.dark-mode .dark-mode.accent-gray-dark .page-item .page-link:hover, .dark-mode .dark-mode.accent-gray-dark .page-item .page-link:focus {\n  color: #3f474e;\n}\n\n.dark-mode .border-dark {\n  border-color: #4b545c !important;\n}\n/*# sourceMappingURL=adminlte.core.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/css/alt/adminlte.extra-components.css",
    "content": "/*!\n *   AdminLTE v3.2.0\n *     Only Extra Components\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n@-webkit-keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n@keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@-webkit-keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@-webkit-keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@-webkit-keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@-webkit-keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.small-box {\n  border-radius: 0.25rem;\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  display: block;\n  margin-bottom: 20px;\n  position: relative;\n}\n\n.small-box > .inner {\n  padding: 10px;\n}\n\n.small-box > .small-box-footer {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: rgba(255, 255, 255, 0.8);\n  display: block;\n  padding: 3px 0;\n  position: relative;\n  text-align: center;\n  text-decoration: none;\n  z-index: 10;\n}\n\n.small-box > .small-box-footer:hover {\n  background-color: rgba(0, 0, 0, 0.15);\n  color: #fff;\n}\n\n.small-box h3 {\n  font-size: 2.2rem;\n  font-weight: 700;\n  margin: 0 0 10px;\n  padding: 0;\n  white-space: nowrap;\n}\n\n@media (min-width: 992px) {\n  .col-xl-2 .small-box h3,\n  .col-lg-2 .small-box h3,\n  .col-md-2 .small-box h3 {\n    font-size: 1.6rem;\n  }\n  .col-xl-3 .small-box h3,\n  .col-lg-3 .small-box h3,\n  .col-md-3 .small-box h3 {\n    font-size: 1.6rem;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl-2 .small-box h3,\n  .col-lg-2 .small-box h3,\n  .col-md-2 .small-box h3 {\n    font-size: 2.2rem;\n  }\n  .col-xl-3 .small-box h3,\n  .col-lg-3 .small-box h3,\n  .col-md-3 .small-box h3 {\n    font-size: 2.2rem;\n  }\n}\n\n.small-box p {\n  font-size: 1rem;\n}\n\n.small-box p > small {\n  color: #f8f9fa;\n  display: block;\n  font-size: .9rem;\n  margin-top: 5px;\n}\n\n.small-box h3,\n.small-box p {\n  z-index: 5;\n}\n\n.small-box .icon {\n  color: rgba(0, 0, 0, 0.15);\n  z-index: 0;\n}\n\n.small-box .icon > i {\n  font-size: 90px;\n  position: absolute;\n  right: 15px;\n  top: 15px;\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n.small-box .icon > i.fa, .small-box .icon > i.fas, .small-box .icon > i.far, .small-box .icon > i.fab, .small-box .icon > i.fal, .small-box .icon > i.fad, .small-box .icon > i.ion {\n  font-size: 70px;\n  top: 20px;\n}\n\n.small-box .icon svg {\n  font-size: 70px;\n  position: absolute;\n  right: 15px;\n  top: 15px;\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n.small-box:hover {\n  text-decoration: none;\n}\n\n.small-box:hover .icon > i, .small-box:hover .icon > i.fa, .small-box:hover .icon > i.fas, .small-box:hover .icon > i.far, .small-box:hover .icon > i.fab, .small-box:hover .icon > i.fal, .small-box:hover .icon > i.fad, .small-box:hover .icon > i.ion {\n  -webkit-transform: scale(1.1);\n  transform: scale(1.1);\n}\n\n.small-box:hover .icon > svg {\n  -webkit-transform: scale(1.1);\n  transform: scale(1.1);\n}\n\n@media (max-width: 767.98px) {\n  .small-box {\n    text-align: center;\n  }\n  .small-box .icon {\n    display: none;\n  }\n  .small-box p {\n    font-size: 12px;\n  }\n}\n\n.info-box {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  display: -ms-flexbox;\n  display: flex;\n  margin-bottom: 1rem;\n  min-height: 80px;\n  padding: .5rem;\n  position: relative;\n  width: 100%;\n}\n\n.info-box .progress {\n  background-color: rgba(0, 0, 0, 0.125);\n  height: 2px;\n  margin: 5px 0;\n}\n\n.info-box .progress .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box-icon {\n  border-radius: 0.25rem;\n  -ms-flex-align: center;\n  align-items: center;\n  display: -ms-flexbox;\n  display: flex;\n  font-size: 1.875rem;\n  -ms-flex-pack: center;\n  justify-content: center;\n  text-align: center;\n  width: 70px;\n}\n\n.info-box .info-box-icon > img {\n  max-width: 100%;\n}\n\n.info-box .info-box-content {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  line-height: 1.8;\n  -ms-flex: 1;\n  flex: 1;\n  padding: 0 10px;\n  overflow: hidden;\n}\n\n.info-box .info-box-number {\n  display: block;\n  margin-top: .25rem;\n  font-weight: 700;\n}\n\n.info-box .progress-description,\n.info-box .info-box-text {\n  display: block;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.info-box .info-box .bg-primary,\n.info-box .info-box .bg-gradient-primary {\n  color: #fff;\n}\n\n.info-box .info-box .bg-primary .progress-bar,\n.info-box .info-box .bg-gradient-primary .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-secondary,\n.info-box .info-box .bg-gradient-secondary {\n  color: #fff;\n}\n\n.info-box .info-box .bg-secondary .progress-bar,\n.info-box .info-box .bg-gradient-secondary .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-success,\n.info-box .info-box .bg-gradient-success {\n  color: #fff;\n}\n\n.info-box .info-box .bg-success .progress-bar,\n.info-box .info-box .bg-gradient-success .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-info,\n.info-box .info-box .bg-gradient-info {\n  color: #fff;\n}\n\n.info-box .info-box .bg-info .progress-bar,\n.info-box .info-box .bg-gradient-info .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-warning,\n.info-box .info-box .bg-gradient-warning {\n  color: #1f2d3d;\n}\n\n.info-box .info-box .bg-warning .progress-bar,\n.info-box .info-box .bg-gradient-warning .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.info-box .info-box .bg-danger,\n.info-box .info-box .bg-gradient-danger {\n  color: #fff;\n}\n\n.info-box .info-box .bg-danger .progress-bar,\n.info-box .info-box .bg-gradient-danger .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-light,\n.info-box .info-box .bg-gradient-light {\n  color: #1f2d3d;\n}\n\n.info-box .info-box .bg-light .progress-bar,\n.info-box .info-box .bg-gradient-light .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.info-box .info-box .bg-dark,\n.info-box .info-box .bg-gradient-dark {\n  color: #fff;\n}\n\n.info-box .info-box .bg-dark .progress-bar,\n.info-box .info-box .bg-gradient-dark .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box-more {\n  display: block;\n}\n\n.info-box .progress-description {\n  margin: 0;\n}\n\n@media (min-width: 768px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    display: none;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    display: none;\n  }\n}\n\n@media (min-width: 992px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    font-size: 0.75rem;\n    display: block;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    font-size: 0.75rem;\n    display: block;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    font-size: 1rem;\n    display: block;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    font-size: 1rem;\n    display: block;\n  }\n}\n\n.dark-mode .info-box {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-primary,\n.dark-mode .info-box .info-box .bg-gradient-primary {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-primary .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-primary .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-secondary,\n.dark-mode .info-box .info-box .bg-gradient-secondary {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-secondary .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-secondary .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-success,\n.dark-mode .info-box .info-box .bg-gradient-success {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-success .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-success .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-info,\n.dark-mode .info-box .info-box .bg-gradient-info {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-info .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-info .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-warning,\n.dark-mode .info-box .info-box .bg-gradient-warning {\n  color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-warning .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-warning .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-danger,\n.dark-mode .info-box .info-box .bg-gradient-danger {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-danger .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-danger .progress-bar {\n  background-color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-light,\n.dark-mode .info-box .info-box .bg-gradient-light {\n  color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-light .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-light .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.dark-mode .info-box .info-box .bg-dark,\n.dark-mode .info-box .info-box .bg-gradient-dark {\n  color: #fff;\n}\n\n.dark-mode .info-box .info-box .bg-dark .progress-bar,\n.dark-mode .info-box .info-box .bg-gradient-dark .progress-bar {\n  background-color: #fff;\n}\n\n.timeline {\n  margin: 0 0 45px;\n  padding: 0;\n  position: relative;\n}\n\n.timeline::before {\n  border-radius: 0.25rem;\n  background-color: #dee2e6;\n  bottom: 0;\n  content: \"\";\n  left: 31px;\n  margin: 0;\n  position: absolute;\n  top: 0;\n  width: 4px;\n}\n\n.timeline > div {\n  margin-bottom: 15px;\n  margin-right: 10px;\n  position: relative;\n}\n\n.timeline > div::before, .timeline > div::after {\n  content: \"\";\n  display: table;\n}\n\n.timeline > div > .timeline-item {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  color: #495057;\n  margin-left: 60px;\n  margin-right: 15px;\n  margin-top: 0;\n  padding: 0;\n  position: relative;\n}\n\n.timeline > div > .timeline-item > .time {\n  color: #999;\n  float: right;\n  font-size: 12px;\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-header {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  color: #495057;\n  font-size: 16px;\n  line-height: 1.1;\n  margin: 0;\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-header > a {\n  font-weight: 600;\n}\n\n.timeline > div > .timeline-item > .timeline-body,\n.timeline > div > .timeline-item > .timeline-footer {\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-body > img {\n  margin: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-body > dl,\n.timeline > div > .timeline-item > .timeline-body ol,\n.timeline > div > .timeline-item > .timeline-body ul {\n  margin: 0;\n}\n\n.timeline > div > .timeline-item > .timeline-footer > a {\n  color: #fff;\n}\n\n.timeline > div > .fa,\n.timeline > div > .fas,\n.timeline > div > .far,\n.timeline > div > .fab,\n.timeline > div > .fal,\n.timeline > div > .fad,\n.timeline > div > .svg-inline--fa,\n.timeline > div > .ion {\n  background-color: #adb5bd;\n  border-radius: 50%;\n  font-size: 16px;\n  height: 30px;\n  left: 18px;\n  line-height: 30px;\n  position: absolute;\n  text-align: center;\n  top: 0;\n  width: 30px;\n}\n\n.timeline > div > .svg-inline--fa {\n  padding: 7px;\n}\n\n.timeline > .time-label > span {\n  border-radius: 4px;\n  background-color: #fff;\n  display: inline-block;\n  font-weight: 600;\n  padding: 5px;\n}\n\n.timeline-inverse > div > .timeline-item {\n  box-shadow: none;\n  background-color: #f8f9fa;\n  border: 1px solid #dee2e6;\n}\n\n.timeline-inverse > div > .timeline-item > .timeline-header {\n  border-bottom-color: #dee2e6;\n}\n\n.dark-mode .timeline::before {\n  background-color: #6c757d;\n}\n\n.dark-mode .timeline > div > .timeline-item {\n  background-color: #343a40;\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.dark-mode .timeline > div > .timeline-item > .timeline-header {\n  color: #ced4da;\n  border-color: #6c757d;\n}\n\n.dark-mode .timeline > div > .timeline-item > .time {\n  color: #ced4da;\n}\n\n.products-list {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n.products-list > .item {\n  border-radius: 0.25rem;\n  background-color: #fff;\n  padding: 10px 0;\n}\n\n.products-list > .item::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.products-list .product-img {\n  float: left;\n}\n\n.products-list .product-img img {\n  height: 50px;\n  width: 50px;\n}\n\n.products-list .product-info {\n  margin-left: 60px;\n}\n\n.products-list .product-title {\n  font-weight: 600;\n}\n\n.products-list .product-description {\n  color: #6c757d;\n  display: block;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.product-list-in-card > .item {\n  border-radius: 0;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.product-list-in-card > .item:last-of-type {\n  border-bottom-width: 0;\n}\n\n.dark-mode .products-list > .item {\n  background-color: #343a40;\n  color: #fff;\n  border-bottom-color: #6c757d;\n}\n\n.dark-mode .product-description {\n  color: #ced4da;\n}\n\n.direct-chat .card-body {\n  overflow-x: hidden;\n  padding: 0;\n  position: relative;\n}\n\n.direct-chat.chat-pane-open .direct-chat-contacts {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n\n.direct-chat.timestamp-light .direct-chat-timestamp {\n  color: #30465f;\n}\n\n.direct-chat.timestamp-dark .direct-chat-timestamp {\n  color: #cccccc;\n}\n\n.direct-chat-messages {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n  height: 250px;\n  overflow: auto;\n  padding: 10px;\n}\n\n.direct-chat-msg,\n.direct-chat-text {\n  display: block;\n}\n\n.direct-chat-msg {\n  margin-bottom: 10px;\n}\n\n.direct-chat-msg::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.direct-chat-messages,\n.direct-chat-contacts {\n  transition: -webkit-transform .5s ease-in-out;\n  transition: transform .5s ease-in-out;\n  transition: transform .5s ease-in-out, -webkit-transform .5s ease-in-out;\n}\n\n.direct-chat-text {\n  border-radius: 0.3rem;\n  background-color: #d2d6de;\n  border: 1px solid #d2d6de;\n  color: #444;\n  margin: 5px 0 0 50px;\n  padding: 5px 10px;\n  position: relative;\n}\n\n.direct-chat-text::after, .direct-chat-text::before {\n  border: solid transparent;\n  border-right-color: #d2d6de;\n  content: \" \";\n  height: 0;\n  pointer-events: none;\n  position: absolute;\n  right: 100%;\n  top: 15px;\n  width: 0;\n}\n\n.direct-chat-text::after {\n  border-width: 5px;\n  margin-top: -5px;\n}\n\n.direct-chat-text::before {\n  border-width: 6px;\n  margin-top: -6px;\n}\n\n.right .direct-chat-text {\n  margin-left: 0;\n  margin-right: 50px;\n}\n\n.right .direct-chat-text::after, .right .direct-chat-text::before {\n  border-left-color: #d2d6de;\n  border-right-color: transparent;\n  left: 100%;\n  right: auto;\n}\n\n.direct-chat-img {\n  border-radius: 50%;\n  float: left;\n  height: 40px;\n  width: 40px;\n}\n\n.right .direct-chat-img {\n  float: right;\n}\n\n.direct-chat-infos {\n  display: block;\n  font-size: 0.875rem;\n  margin-bottom: 2px;\n}\n\n.direct-chat-name {\n  font-weight: 600;\n}\n\n.direct-chat-timestamp {\n  color: #697582;\n}\n\n.direct-chat-contacts-open .direct-chat-contacts {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n\n.direct-chat-contacts {\n  -webkit-transform: translate(101%, 0);\n  transform: translate(101%, 0);\n  background-color: #343a40;\n  bottom: 0;\n  color: #fff;\n  height: 250px;\n  overflow: auto;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.direct-chat-contacts-light {\n  background-color: #f8f9fa;\n}\n\n.direct-chat-contacts-light .contacts-list-name {\n  color: #495057;\n}\n\n.direct-chat-contacts-light .contacts-list-date {\n  color: #6c757d;\n}\n\n.direct-chat-contacts-light .contacts-list-msg {\n  color: #545b62;\n}\n\n.contacts-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.contacts-list > li {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n  margin: 0;\n  padding: 10px;\n}\n\n.contacts-list > li::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.contacts-list > li:last-of-type {\n  border-bottom: 0;\n}\n\n.contacts-list-img {\n  border-radius: 50%;\n  float: left;\n  width: 40px;\n}\n\n.contacts-list-info {\n  color: #fff;\n  margin-left: 45px;\n}\n\n.contacts-list-name,\n.contacts-list-status {\n  display: block;\n}\n\n.contacts-list-name {\n  font-weight: 600;\n}\n\n.contacts-list-status {\n  font-size: 0.875rem;\n}\n\n.contacts-list-date {\n  color: #ced4da;\n  font-weight: 400;\n}\n\n.contacts-list-msg {\n  color: #b1bbc4;\n}\n\n.direct-chat-primary .right > .direct-chat-text {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.direct-chat-primary .right > .direct-chat-text::after, .direct-chat-primary .right > .direct-chat-text::before {\n  border-left-color: #007bff;\n}\n\n.direct-chat-secondary .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.direct-chat-secondary .right > .direct-chat-text::after, .direct-chat-secondary .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.direct-chat-success .right > .direct-chat-text {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.direct-chat-success .right > .direct-chat-text::after, .direct-chat-success .right > .direct-chat-text::before {\n  border-left-color: #28a745;\n}\n\n.direct-chat-info .right > .direct-chat-text {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.direct-chat-info .right > .direct-chat-text::after, .direct-chat-info .right > .direct-chat-text::before {\n  border-left-color: #17a2b8;\n}\n\n.direct-chat-warning .right > .direct-chat-text {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.direct-chat-warning .right > .direct-chat-text::after, .direct-chat-warning .right > .direct-chat-text::before {\n  border-left-color: #ffc107;\n}\n\n.direct-chat-danger .right > .direct-chat-text {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.direct-chat-danger .right > .direct-chat-text::after, .direct-chat-danger .right > .direct-chat-text::before {\n  border-left-color: #dc3545;\n}\n\n.direct-chat-light .right > .direct-chat-text {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.direct-chat-light .right > .direct-chat-text::after, .direct-chat-light .right > .direct-chat-text::before {\n  border-left-color: #f8f9fa;\n}\n\n.direct-chat-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.direct-chat-dark .right > .direct-chat-text::after, .direct-chat-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.direct-chat-lightblue .right > .direct-chat-text {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.direct-chat-lightblue .right > .direct-chat-text::after, .direct-chat-lightblue .right > .direct-chat-text::before {\n  border-left-color: #3c8dbc;\n}\n\n.direct-chat-navy .right > .direct-chat-text {\n  background-color: #001f3f;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.direct-chat-navy .right > .direct-chat-text::after, .direct-chat-navy .right > .direct-chat-text::before {\n  border-left-color: #001f3f;\n}\n\n.direct-chat-olive .right > .direct-chat-text {\n  background-color: #3d9970;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.direct-chat-olive .right > .direct-chat-text::after, .direct-chat-olive .right > .direct-chat-text::before {\n  border-left-color: #3d9970;\n}\n\n.direct-chat-lime .right > .direct-chat-text {\n  background-color: #01ff70;\n  border-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.direct-chat-lime .right > .direct-chat-text::after, .direct-chat-lime .right > .direct-chat-text::before {\n  border-left-color: #01ff70;\n}\n\n.direct-chat-fuchsia .right > .direct-chat-text {\n  background-color: #f012be;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.direct-chat-fuchsia .right > .direct-chat-text::after, .direct-chat-fuchsia .right > .direct-chat-text::before {\n  border-left-color: #f012be;\n}\n\n.direct-chat-maroon .right > .direct-chat-text {\n  background-color: #d81b60;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.direct-chat-maroon .right > .direct-chat-text::after, .direct-chat-maroon .right > .direct-chat-text::before {\n  border-left-color: #d81b60;\n}\n\n.direct-chat-blue .right > .direct-chat-text {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.direct-chat-blue .right > .direct-chat-text::after, .direct-chat-blue .right > .direct-chat-text::before {\n  border-left-color: #007bff;\n}\n\n.direct-chat-indigo .right > .direct-chat-text {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.direct-chat-indigo .right > .direct-chat-text::after, .direct-chat-indigo .right > .direct-chat-text::before {\n  border-left-color: #6610f2;\n}\n\n.direct-chat-purple .right > .direct-chat-text {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.direct-chat-purple .right > .direct-chat-text::after, .direct-chat-purple .right > .direct-chat-text::before {\n  border-left-color: #6f42c1;\n}\n\n.direct-chat-pink .right > .direct-chat-text {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.direct-chat-pink .right > .direct-chat-text::after, .direct-chat-pink .right > .direct-chat-text::before {\n  border-left-color: #e83e8c;\n}\n\n.direct-chat-red .right > .direct-chat-text {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.direct-chat-red .right > .direct-chat-text::after, .direct-chat-red .right > .direct-chat-text::before {\n  border-left-color: #dc3545;\n}\n\n.direct-chat-orange .right > .direct-chat-text {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.direct-chat-orange .right > .direct-chat-text::after, .direct-chat-orange .right > .direct-chat-text::before {\n  border-left-color: #fd7e14;\n}\n\n.direct-chat-yellow .right > .direct-chat-text {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.direct-chat-yellow .right > .direct-chat-text::after, .direct-chat-yellow .right > .direct-chat-text::before {\n  border-left-color: #ffc107;\n}\n\n.direct-chat-green .right > .direct-chat-text {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.direct-chat-green .right > .direct-chat-text::after, .direct-chat-green .right > .direct-chat-text::before {\n  border-left-color: #28a745;\n}\n\n.direct-chat-teal .right > .direct-chat-text {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.direct-chat-teal .right > .direct-chat-text::after, .direct-chat-teal .right > .direct-chat-text::before {\n  border-left-color: #20c997;\n}\n\n.direct-chat-cyan .right > .direct-chat-text {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.direct-chat-cyan .right > .direct-chat-text::after, .direct-chat-cyan .right > .direct-chat-text::before {\n  border-left-color: #17a2b8;\n}\n\n.direct-chat-white .right > .direct-chat-text {\n  background-color: #fff;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.direct-chat-white .right > .direct-chat-text::after, .direct-chat-white .right > .direct-chat-text::before {\n  border-left-color: #fff;\n}\n\n.direct-chat-gray .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.direct-chat-gray .right > .direct-chat-text::after, .direct-chat-gray .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.direct-chat-gray-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.direct-chat-gray-dark .right > .direct-chat-text::after, .direct-chat-gray-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.dark-mode .direct-chat-text {\n  background-color: #454d55;\n  border-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-text::after, .dark-mode .direct-chat-text::before {\n  border-right-color: #4b545c;\n}\n\n.dark-mode .direct-chat-timestamp {\n  color: #adb5bd;\n}\n\n.dark-mode .right > .direct-chat-text::after, .dark-mode .right > .direct-chat-text::before {\n  border-right-color: transparent;\n}\n\n.dark-mode .direct-chat-primary .right > .direct-chat-text {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-primary .right > .direct-chat-text::after, .dark-mode .direct-chat-primary .right > .direct-chat-text::before {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .direct-chat-secondary .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-secondary .right > .direct-chat-text::after, .dark-mode .direct-chat-secondary .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .direct-chat-success .right > .direct-chat-text {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-success .right > .direct-chat-text::after, .dark-mode .direct-chat-success .right > .direct-chat-text::before {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .direct-chat-info .right > .direct-chat-text {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-info .right > .direct-chat-text::after, .dark-mode .direct-chat-info .right > .direct-chat-text::before {\n  border-left-color: #3498db;\n}\n\n.dark-mode .direct-chat-warning .right > .direct-chat-text {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-warning .right > .direct-chat-text::after, .dark-mode .direct-chat-warning .right > .direct-chat-text::before {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .direct-chat-danger .right > .direct-chat-text {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-danger .right > .direct-chat-text::after, .dark-mode .direct-chat-danger .right > .direct-chat-text::before {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .direct-chat-light .right > .direct-chat-text {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-light .right > .direct-chat-text::after, .dark-mode .direct-chat-light .right > .direct-chat-text::before {\n  border-left-color: #f8f9fa;\n}\n\n.dark-mode .direct-chat-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-dark .right > .direct-chat-text::after, .dark-mode .direct-chat-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.dark-mode .direct-chat-lightblue .right > .direct-chat-text {\n  background-color: #86bad8;\n  border-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-lightblue .right > .direct-chat-text::after, .dark-mode .direct-chat-lightblue .right > .direct-chat-text::before {\n  border-left-color: #86bad8;\n}\n\n.dark-mode .direct-chat-navy .right > .direct-chat-text {\n  background-color: #002c59;\n  border-color: #002c59;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-navy .right > .direct-chat-text::after, .dark-mode .direct-chat-navy .right > .direct-chat-text::before {\n  border-left-color: #002c59;\n}\n\n.dark-mode .direct-chat-olive .right > .direct-chat-text {\n  background-color: #74c8a3;\n  border-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-olive .right > .direct-chat-text::after, .dark-mode .direct-chat-olive .right > .direct-chat-text::before {\n  border-left-color: #74c8a3;\n}\n\n.dark-mode .direct-chat-lime .right > .direct-chat-text {\n  background-color: #67ffa9;\n  border-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-lime .right > .direct-chat-text::after, .dark-mode .direct-chat-lime .right > .direct-chat-text::before {\n  border-left-color: #67ffa9;\n}\n\n.dark-mode .direct-chat-fuchsia .right > .direct-chat-text {\n  background-color: #f672d8;\n  border-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-fuchsia .right > .direct-chat-text::after, .dark-mode .direct-chat-fuchsia .right > .direct-chat-text::before {\n  border-left-color: #f672d8;\n}\n\n.dark-mode .direct-chat-maroon .right > .direct-chat-text {\n  background-color: #ed6c9b;\n  border-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-maroon .right > .direct-chat-text::after, .dark-mode .direct-chat-maroon .right > .direct-chat-text::before {\n  border-left-color: #ed6c9b;\n}\n\n.dark-mode .direct-chat-blue .right > .direct-chat-text {\n  background-color: #3f6791;\n  border-color: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-blue .right > .direct-chat-text::after, .dark-mode .direct-chat-blue .right > .direct-chat-text::before {\n  border-left-color: #3f6791;\n}\n\n.dark-mode .direct-chat-indigo .right > .direct-chat-text {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-indigo .right > .direct-chat-text::after, .dark-mode .direct-chat-indigo .right > .direct-chat-text::before {\n  border-left-color: #6610f2;\n}\n\n.dark-mode .direct-chat-purple .right > .direct-chat-text {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-purple .right > .direct-chat-text::after, .dark-mode .direct-chat-purple .right > .direct-chat-text::before {\n  border-left-color: #6f42c1;\n}\n\n.dark-mode .direct-chat-pink .right > .direct-chat-text {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-pink .right > .direct-chat-text::after, .dark-mode .direct-chat-pink .right > .direct-chat-text::before {\n  border-left-color: #e83e8c;\n}\n\n.dark-mode .direct-chat-red .right > .direct-chat-text {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-red .right > .direct-chat-text::after, .dark-mode .direct-chat-red .right > .direct-chat-text::before {\n  border-left-color: #e74c3c;\n}\n\n.dark-mode .direct-chat-orange .right > .direct-chat-text {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-orange .right > .direct-chat-text::after, .dark-mode .direct-chat-orange .right > .direct-chat-text::before {\n  border-left-color: #fd7e14;\n}\n\n.dark-mode .direct-chat-yellow .right > .direct-chat-text {\n  background-color: #f39c12;\n  border-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-yellow .right > .direct-chat-text::after, .dark-mode .direct-chat-yellow .right > .direct-chat-text::before {\n  border-left-color: #f39c12;\n}\n\n.dark-mode .direct-chat-green .right > .direct-chat-text {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-green .right > .direct-chat-text::after, .dark-mode .direct-chat-green .right > .direct-chat-text::before {\n  border-left-color: #00bc8c;\n}\n\n.dark-mode .direct-chat-teal .right > .direct-chat-text {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-teal .right > .direct-chat-text::after, .dark-mode .direct-chat-teal .right > .direct-chat-text::before {\n  border-left-color: #20c997;\n}\n\n.dark-mode .direct-chat-cyan .right > .direct-chat-text {\n  background-color: #3498db;\n  border-color: #3498db;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-cyan .right > .direct-chat-text::after, .dark-mode .direct-chat-cyan .right > .direct-chat-text::before {\n  border-left-color: #3498db;\n}\n\n.dark-mode .direct-chat-white .right > .direct-chat-text {\n  background-color: #fff;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .direct-chat-white .right > .direct-chat-text::after, .dark-mode .direct-chat-white .right > .direct-chat-text::before {\n  border-left-color: #fff;\n}\n\n.dark-mode .direct-chat-gray .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-gray .right > .direct-chat-text::after, .dark-mode .direct-chat-gray .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.dark-mode .direct-chat-gray-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.dark-mode .direct-chat-gray-dark .right > .direct-chat-text::after, .dark-mode .direct-chat-gray-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.users-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.users-list > li {\n  float: left;\n  padding: 10px;\n  text-align: center;\n  width: 25%;\n}\n\n.users-list > li img {\n  border-radius: 50%;\n  height: auto;\n  max-width: 100%;\n}\n\n.users-list > li > a:hover,\n.users-list > li > a:hover .users-list-name {\n  color: #999;\n}\n\n.users-list-name,\n.users-list-date {\n  display: block;\n}\n\n.users-list-name {\n  color: #495057;\n  font-size: 0.875rem;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.users-list-date {\n  color: #748290;\n  font-size: 12px;\n}\n\n.dark-mode .users-list-name {\n  color: #ced4da;\n}\n\n.dark-mode .users-list-date {\n  color: #adb5bd;\n}\n\n.card-widget {\n  border: 0;\n  position: relative;\n}\n\n.widget-user .widget-user-header {\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n  height: 135px;\n  padding: 1rem;\n  text-align: center;\n}\n\n.widget-user .widget-user-username {\n  font-size: 25px;\n  font-weight: 300;\n  margin-bottom: 0;\n  margin-top: 0;\n  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);\n}\n\n.widget-user .widget-user-desc {\n  margin-top: 0;\n}\n\n.widget-user .widget-user-image {\n  left: 50%;\n  margin-left: -45px;\n  position: absolute;\n  top: 80px;\n}\n\n.widget-user .widget-user-image > img {\n  border: 3px solid #fff;\n  height: auto;\n  width: 90px;\n}\n\n.widget-user .card-footer {\n  padding-top: 50px;\n}\n\n.widget-user-2 .widget-user-header {\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n  padding: 1rem;\n}\n\n.widget-user-2 .widget-user-username {\n  font-size: 25px;\n  font-weight: 300;\n  margin-bottom: 5px;\n  margin-top: 5px;\n}\n\n.widget-user-2 .widget-user-desc {\n  margin-top: 0;\n}\n\n.widget-user-2 .widget-user-username,\n.widget-user-2 .widget-user-desc {\n  margin-left: 75px;\n}\n\n.widget-user-2 .widget-user-image > img {\n  float: left;\n  height: auto;\n  width: 65px;\n}\n/*# sourceMappingURL=adminlte.extra-components.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/css/alt/adminlte.light.css",
    "content": "/*!\n *   AdminLTE v3.2.0\n *     Without Dark mode\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n/*!\n *   AdminLTE v3.2.0\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n/*!\n * Bootstrap v4.6.1 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --blue: #007bff;\n  --indigo: #6610f2;\n  --purple: #6f42c1;\n  --pink: #e83e8c;\n  --red: #dc3545;\n  --orange: #fd7e14;\n  --yellow: #ffc107;\n  --green: #28a745;\n  --teal: #20c997;\n  --cyan: #17a2b8;\n  --white: #fff;\n  --gray: #6c757d;\n  --gray-dark: #343a40;\n  --primary: #007bff;\n  --secondary: #6c757d;\n  --success: #28a745;\n  --info: #17a2b8;\n  --warning: #ffc107;\n  --danger: #dc3545;\n  --light: #f8f9fa;\n  --dark: #343a40;\n  --breakpoint-xs: 0;\n  --breakpoint-sm: 576px;\n  --breakpoint-md: 768px;\n  --breakpoint-lg: 992px;\n  --breakpoint-xl: 1200px;\n  --font-family-sans-serif: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\nhtml {\n  font-family: sans-serif;\n  line-height: 1.15;\n  -webkit-text-size-adjust: 100%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n  display: block;\n}\n\nbody {\n  margin: 0;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  text-align: left;\n  background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n  outline: 0 !important;\n}\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n  overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n  text-decoration: underline;\n  -webkit-text-decoration: underline dotted;\n  text-decoration: underline dotted;\n  cursor: help;\n  border-bottom: 0;\n  -webkit-text-decoration-skip-ink: none;\n  text-decoration-skip-ink: none;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: 700;\n}\n\ndd {\n  margin-bottom: .5rem;\n  margin-left: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall {\n  font-size: 80%;\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -.25em;\n}\n\nsup {\n  top: -.5em;\n}\n\na {\n  color: #007bff;\n  text-decoration: none;\n  background-color: transparent;\n}\n\na:hover {\n  color: #0056b3;\n  text-decoration: none;\n}\n\na:not([href]):not([class]) {\n  color: inherit;\n  text-decoration: none;\n}\n\na:not([href]):not([class]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  font-size: 1em;\n}\n\npre {\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n  -ms-overflow-style: scrollbar;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg {\n  vertical-align: middle;\n  border-style: none;\n}\n\nsvg {\n  overflow: hidden;\n  vertical-align: middle;\n}\n\ntable {\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.75rem;\n  padding-bottom: 0.75rem;\n  color: #6c757d;\n  text-align: left;\n  caption-side: bottom;\n}\n\nth {\n  text-align: inherit;\n  text-align: -webkit-match-parent;\n}\n\nlabel {\n  display: inline-block;\n  margin-bottom: 0.5rem;\n}\n\nbutton {\n  border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n  outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\ninput {\n  overflow: visible;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n[role=\"button\"] {\n  cursor: pointer;\n}\n\nselect {\n  word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n  -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n  cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  box-sizing: border-box;\n  padding: 0;\n}\n\ntextarea {\n  overflow: auto;\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  max-width: 100%;\n  padding: 0;\n  margin-bottom: .5rem;\n  font-size: 1.5rem;\n  line-height: inherit;\n  color: inherit;\n  white-space: normal;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n[type=\"search\"] {\n  outline-offset: -2px;\n  -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\nsummary {\n  display: list-item;\n  cursor: pointer;\n}\n\ntemplate {\n  display: none;\n}\n\n[hidden] {\n  display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  margin-bottom: 0.5rem;\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.2;\n  color: inherit;\n}\n\nh1, .h1 {\n  font-size: 2.5rem;\n}\n\nh2, .h2 {\n  font-size: 2rem;\n}\n\nh3, .h3 {\n  font-size: 1.75rem;\n}\n\nh4, .h4 {\n  font-size: 1.5rem;\n}\n\nh5, .h5 {\n  font-size: 1.25rem;\n}\n\nh6, .h6 {\n  font-size: 1rem;\n}\n\n.lead {\n  font-size: 1.25rem;\n  font-weight: 300;\n}\n\n.display-1 {\n  font-size: 6rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-2 {\n  font-size: 5.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-3 {\n  font-size: 4.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\n.display-4 {\n  font-size: 3.5rem;\n  font-weight: 300;\n  line-height: 1.2;\n}\n\nhr {\n  margin-top: 1rem;\n  margin-bottom: 1rem;\n  border: 0;\n  border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n  font-size: 80%;\n  font-weight: 400;\n}\n\nmark,\n.mark {\n  padding: 0.2em;\n  background-color: #fcf8e3;\n}\n\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline-item {\n  display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n  margin-right: 0.5rem;\n}\n\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\n.blockquote {\n  margin-bottom: 1rem;\n  font-size: 1.25rem;\n}\n\n.blockquote-footer {\n  display: block;\n  font-size: 80%;\n  color: #6c757d;\n}\n\n.blockquote-footer::before {\n  content: \"\\2014\\00A0\";\n}\n\n.img-fluid {\n  max-width: 100%;\n  height: auto;\n}\n\n.img-thumbnail {\n  padding: 0.25rem;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n  border-radius: 0.25rem;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  max-width: 100%;\n  height: auto;\n}\n\n.figure {\n  display: inline-block;\n}\n\n.figure-img {\n  margin-bottom: 0.5rem;\n  line-height: 1;\n}\n\n.figure-caption {\n  font-size: 90%;\n  color: #6c757d;\n}\n\ncode {\n  font-size: 87.5%;\n  color: #e83e8c;\n  word-wrap: break-word;\n}\n\na > code {\n  color: inherit;\n}\n\nkbd {\n  padding: 0.2rem 0.4rem;\n  font-size: 87.5%;\n  color: #fff;\n  background-color: #212529;\n  border-radius: 0.2rem;\n  box-shadow: inset 0 -0.1rem 0 rgba(0, 0, 0, 0.25);\n}\n\nkbd kbd {\n  padding: 0;\n  font-size: 100%;\n  font-weight: 700;\n  box-shadow: none;\n}\n\npre {\n  display: block;\n  font-size: 87.5%;\n  color: #212529;\n}\n\npre code {\n  font-size: inherit;\n  color: inherit;\n  word-break: normal;\n}\n\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n\n.container,\n.container-fluid,\n.container-sm,\n.container-md,\n.container-lg,\n.container-xl {\n  width: 100%;\n  padding-right: 7.5px;\n  padding-left: 7.5px;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n@media (min-width: 576px) {\n  .container, .container-sm {\n    max-width: 540px;\n  }\n}\n\n@media (min-width: 768px) {\n  .container, .container-sm, .container-md {\n    max-width: 720px;\n  }\n}\n\n@media (min-width: 992px) {\n  .container, .container-sm, .container-md, .container-lg {\n    max-width: 960px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .container, .container-sm, .container-md, .container-lg, .container-xl {\n    max-width: 1140px;\n  }\n}\n\n.row {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  margin-right: -7.5px;\n  margin-left: -7.5px;\n}\n\n.no-gutters {\n  margin-right: 0;\n  margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .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-sm,\n.col-sm-auto, .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-md,\n.col-md-auto, .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-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n  position: relative;\n  width: 100%;\n  padding-right: 7.5px;\n  padding-left: 7.5px;\n}\n\n.col {\n  -ms-flex-preferred-size: 0;\n  flex-basis: 0;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  max-width: 100%;\n}\n\n.row-cols-1 > * {\n  -ms-flex: 0 0 100%;\n  flex: 0 0 100%;\n  max-width: 100%;\n}\n\n.row-cols-2 > * {\n  -ms-flex: 0 0 50%;\n  flex: 0 0 50%;\n  max-width: 50%;\n}\n\n.row-cols-3 > * {\n  -ms-flex: 0 0 33.333333%;\n  flex: 0 0 33.333333%;\n  max-width: 33.333333%;\n}\n\n.row-cols-4 > * {\n  -ms-flex: 0 0 25%;\n  flex: 0 0 25%;\n  max-width: 25%;\n}\n\n.row-cols-5 > * {\n  -ms-flex: 0 0 20%;\n  flex: 0 0 20%;\n  max-width: 20%;\n}\n\n.row-cols-6 > * {\n  -ms-flex: 0 0 16.666667%;\n  flex: 0 0 16.666667%;\n  max-width: 16.666667%;\n}\n\n.col-auto {\n  -ms-flex: 0 0 auto;\n  flex: 0 0 auto;\n  width: auto;\n  max-width: 100%;\n}\n\n.col-1 {\n  -ms-flex: 0 0 8.333333%;\n  flex: 0 0 8.333333%;\n  max-width: 8.333333%;\n}\n\n.col-2 {\n  -ms-flex: 0 0 16.666667%;\n  flex: 0 0 16.666667%;\n  max-width: 16.666667%;\n}\n\n.col-3 {\n  -ms-flex: 0 0 25%;\n  flex: 0 0 25%;\n  max-width: 25%;\n}\n\n.col-4 {\n  -ms-flex: 0 0 33.333333%;\n  flex: 0 0 33.333333%;\n  max-width: 33.333333%;\n}\n\n.col-5 {\n  -ms-flex: 0 0 41.666667%;\n  flex: 0 0 41.666667%;\n  max-width: 41.666667%;\n}\n\n.col-6 {\n  -ms-flex: 0 0 50%;\n  flex: 0 0 50%;\n  max-width: 50%;\n}\n\n.col-7 {\n  -ms-flex: 0 0 58.333333%;\n  flex: 0 0 58.333333%;\n  max-width: 58.333333%;\n}\n\n.col-8 {\n  -ms-flex: 0 0 66.666667%;\n  flex: 0 0 66.666667%;\n  max-width: 66.666667%;\n}\n\n.col-9 {\n  -ms-flex: 0 0 75%;\n  flex: 0 0 75%;\n  max-width: 75%;\n}\n\n.col-10 {\n  -ms-flex: 0 0 83.333333%;\n  flex: 0 0 83.333333%;\n  max-width: 83.333333%;\n}\n\n.col-11 {\n  -ms-flex: 0 0 91.666667%;\n  flex: 0 0 91.666667%;\n  max-width: 91.666667%;\n}\n\n.col-12 {\n  -ms-flex: 0 0 100%;\n  flex: 0 0 100%;\n  max-width: 100%;\n}\n\n.order-first {\n  -ms-flex-order: -1;\n  order: -1;\n}\n\n.order-last {\n  -ms-flex-order: 13;\n  order: 13;\n}\n\n.order-0 {\n  -ms-flex-order: 0;\n  order: 0;\n}\n\n.order-1 {\n  -ms-flex-order: 1;\n  order: 1;\n}\n\n.order-2 {\n  -ms-flex-order: 2;\n  order: 2;\n}\n\n.order-3 {\n  -ms-flex-order: 3;\n  order: 3;\n}\n\n.order-4 {\n  -ms-flex-order: 4;\n  order: 4;\n}\n\n.order-5 {\n  -ms-flex-order: 5;\n  order: 5;\n}\n\n.order-6 {\n  -ms-flex-order: 6;\n  order: 6;\n}\n\n.order-7 {\n  -ms-flex-order: 7;\n  order: 7;\n}\n\n.order-8 {\n  -ms-flex-order: 8;\n  order: 8;\n}\n\n.order-9 {\n  -ms-flex-order: 9;\n  order: 9;\n}\n\n.order-10 {\n  -ms-flex-order: 10;\n  order: 10;\n}\n\n.order-11 {\n  -ms-flex-order: 11;\n  order: 11;\n}\n\n.order-12 {\n  -ms-flex-order: 12;\n  order: 12;\n}\n\n.offset-1 {\n  margin-left: 8.333333%;\n}\n\n.offset-2 {\n  margin-left: 16.666667%;\n}\n\n.offset-3 {\n  margin-left: 25%;\n}\n\n.offset-4 {\n  margin-left: 33.333333%;\n}\n\n.offset-5 {\n  margin-left: 41.666667%;\n}\n\n.offset-6 {\n  margin-left: 50%;\n}\n\n.offset-7 {\n  margin-left: 58.333333%;\n}\n\n.offset-8 {\n  margin-left: 66.666667%;\n}\n\n.offset-9 {\n  margin-left: 75%;\n}\n\n.offset-10 {\n  margin-left: 83.333333%;\n}\n\n.offset-11 {\n  margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n  .col-sm {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-sm-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-sm-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-sm-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-sm-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-sm-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-sm-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-sm-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-sm-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-sm-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-sm-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-sm-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-sm-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-sm-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-sm-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-sm-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-sm-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-sm-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-sm-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-sm-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-sm-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-sm-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-sm-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-sm-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-sm-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-sm-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-sm-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-sm-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-sm-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-sm-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-sm-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-sm-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-sm-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-sm-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-sm-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-sm-0 {\n    margin-left: 0;\n  }\n  .offset-sm-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-sm-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-sm-3 {\n    margin-left: 25%;\n  }\n  .offset-sm-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-sm-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-sm-6 {\n    margin-left: 50%;\n  }\n  .offset-sm-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-sm-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-sm-9 {\n    margin-left: 75%;\n  }\n  .offset-sm-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-sm-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 768px) {\n  .col-md {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-md-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-md-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-md-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-md-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-md-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-md-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-md-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-md-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-md-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-md-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-md-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-md-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-md-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-md-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-md-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-md-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-md-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-md-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-md-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-md-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-md-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-md-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-md-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-md-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-md-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-md-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-md-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-md-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-md-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-md-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-md-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-md-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-md-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-md-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-md-0 {\n    margin-left: 0;\n  }\n  .offset-md-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-md-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-md-3 {\n    margin-left: 25%;\n  }\n  .offset-md-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-md-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-md-6 {\n    margin-left: 50%;\n  }\n  .offset-md-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-md-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-md-9 {\n    margin-left: 75%;\n  }\n  .offset-md-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-md-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 992px) {\n  .col-lg {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-lg-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-lg-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-lg-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-lg-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-lg-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-lg-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-lg-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-lg-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-lg-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-lg-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-lg-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-lg-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-lg-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-lg-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-lg-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-lg-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-lg-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-lg-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-lg-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-lg-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-lg-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-lg-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-lg-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-lg-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-lg-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-lg-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-lg-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-lg-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-lg-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-lg-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-lg-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-lg-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-lg-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-lg-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-lg-0 {\n    margin-left: 0;\n  }\n  .offset-lg-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-lg-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-lg-3 {\n    margin-left: 25%;\n  }\n  .offset-lg-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-lg-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-lg-6 {\n    margin-left: 50%;\n  }\n  .offset-lg-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-lg-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-lg-9 {\n    margin-left: 75%;\n  }\n  .offset-lg-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-lg-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl {\n    -ms-flex-preferred-size: 0;\n    flex-basis: 0;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n    max-width: 100%;\n  }\n  .row-cols-xl-1 > * {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .row-cols-xl-2 > * {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .row-cols-xl-3 > * {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .row-cols-xl-4 > * {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .row-cols-xl-5 > * {\n    -ms-flex: 0 0 20%;\n    flex: 0 0 20%;\n    max-width: 20%;\n  }\n  .row-cols-xl-6 > * {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-xl-auto {\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    width: auto;\n    max-width: 100%;\n  }\n  .col-xl-1 {\n    -ms-flex: 0 0 8.333333%;\n    flex: 0 0 8.333333%;\n    max-width: 8.333333%;\n  }\n  .col-xl-2 {\n    -ms-flex: 0 0 16.666667%;\n    flex: 0 0 16.666667%;\n    max-width: 16.666667%;\n  }\n  .col-xl-3 {\n    -ms-flex: 0 0 25%;\n    flex: 0 0 25%;\n    max-width: 25%;\n  }\n  .col-xl-4 {\n    -ms-flex: 0 0 33.333333%;\n    flex: 0 0 33.333333%;\n    max-width: 33.333333%;\n  }\n  .col-xl-5 {\n    -ms-flex: 0 0 41.666667%;\n    flex: 0 0 41.666667%;\n    max-width: 41.666667%;\n  }\n  .col-xl-6 {\n    -ms-flex: 0 0 50%;\n    flex: 0 0 50%;\n    max-width: 50%;\n  }\n  .col-xl-7 {\n    -ms-flex: 0 0 58.333333%;\n    flex: 0 0 58.333333%;\n    max-width: 58.333333%;\n  }\n  .col-xl-8 {\n    -ms-flex: 0 0 66.666667%;\n    flex: 0 0 66.666667%;\n    max-width: 66.666667%;\n  }\n  .col-xl-9 {\n    -ms-flex: 0 0 75%;\n    flex: 0 0 75%;\n    max-width: 75%;\n  }\n  .col-xl-10 {\n    -ms-flex: 0 0 83.333333%;\n    flex: 0 0 83.333333%;\n    max-width: 83.333333%;\n  }\n  .col-xl-11 {\n    -ms-flex: 0 0 91.666667%;\n    flex: 0 0 91.666667%;\n    max-width: 91.666667%;\n  }\n  .col-xl-12 {\n    -ms-flex: 0 0 100%;\n    flex: 0 0 100%;\n    max-width: 100%;\n  }\n  .order-xl-first {\n    -ms-flex-order: -1;\n    order: -1;\n  }\n  .order-xl-last {\n    -ms-flex-order: 13;\n    order: 13;\n  }\n  .order-xl-0 {\n    -ms-flex-order: 0;\n    order: 0;\n  }\n  .order-xl-1 {\n    -ms-flex-order: 1;\n    order: 1;\n  }\n  .order-xl-2 {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n  .order-xl-3 {\n    -ms-flex-order: 3;\n    order: 3;\n  }\n  .order-xl-4 {\n    -ms-flex-order: 4;\n    order: 4;\n  }\n  .order-xl-5 {\n    -ms-flex-order: 5;\n    order: 5;\n  }\n  .order-xl-6 {\n    -ms-flex-order: 6;\n    order: 6;\n  }\n  .order-xl-7 {\n    -ms-flex-order: 7;\n    order: 7;\n  }\n  .order-xl-8 {\n    -ms-flex-order: 8;\n    order: 8;\n  }\n  .order-xl-9 {\n    -ms-flex-order: 9;\n    order: 9;\n  }\n  .order-xl-10 {\n    -ms-flex-order: 10;\n    order: 10;\n  }\n  .order-xl-11 {\n    -ms-flex-order: 11;\n    order: 11;\n  }\n  .order-xl-12 {\n    -ms-flex-order: 12;\n    order: 12;\n  }\n  .offset-xl-0 {\n    margin-left: 0;\n  }\n  .offset-xl-1 {\n    margin-left: 8.333333%;\n  }\n  .offset-xl-2 {\n    margin-left: 16.666667%;\n  }\n  .offset-xl-3 {\n    margin-left: 25%;\n  }\n  .offset-xl-4 {\n    margin-left: 33.333333%;\n  }\n  .offset-xl-5 {\n    margin-left: 41.666667%;\n  }\n  .offset-xl-6 {\n    margin-left: 50%;\n  }\n  .offset-xl-7 {\n    margin-left: 58.333333%;\n  }\n  .offset-xl-8 {\n    margin-left: 66.666667%;\n  }\n  .offset-xl-9 {\n    margin-left: 75%;\n  }\n  .offset-xl-10 {\n    margin-left: 83.333333%;\n  }\n  .offset-xl-11 {\n    margin-left: 91.666667%;\n  }\n}\n\n.table {\n  width: 100%;\n  margin-bottom: 1rem;\n  color: #212529;\n  background-color: transparent;\n}\n\n.table th,\n.table td {\n  padding: 0.75rem;\n  vertical-align: top;\n  border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n  border-top: 2px solid #dee2e6;\n}\n\n.table-sm th,\n.table-sm td {\n  padding: 0.3rem;\n}\n\n.table-bordered {\n  border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n  border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n  border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n  border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n  color: #212529;\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n  background-color: #b8daff;\n}\n\n.table-primary th,\n.table-primary td,\n.table-primary thead th,\n.table-primary tbody + tbody {\n  border-color: #7abaff;\n}\n\n.table-hover .table-primary:hover {\n  background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n  background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n  background-color: #d6d8db;\n}\n\n.table-secondary th,\n.table-secondary td,\n.table-secondary thead th,\n.table-secondary tbody + tbody {\n  border-color: #b3b7bb;\n}\n\n.table-hover .table-secondary:hover {\n  background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n  background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n  background-color: #c3e6cb;\n}\n\n.table-success th,\n.table-success td,\n.table-success thead th,\n.table-success tbody + tbody {\n  border-color: #8fd19e;\n}\n\n.table-hover .table-success:hover {\n  background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n  background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n  background-color: #bee5eb;\n}\n\n.table-info th,\n.table-info td,\n.table-info thead th,\n.table-info tbody + tbody {\n  border-color: #86cfda;\n}\n\n.table-hover .table-info:hover {\n  background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n  background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n  background-color: #ffeeba;\n}\n\n.table-warning th,\n.table-warning td,\n.table-warning thead th,\n.table-warning tbody + tbody {\n  border-color: #ffdf7e;\n}\n\n.table-hover .table-warning:hover {\n  background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n  background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n  background-color: #f5c6cb;\n}\n\n.table-danger th,\n.table-danger td,\n.table-danger thead th,\n.table-danger tbody + tbody {\n  border-color: #ed969e;\n}\n\n.table-hover .table-danger:hover {\n  background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n  background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n  background-color: #fdfdfe;\n}\n\n.table-light th,\n.table-light td,\n.table-light thead th,\n.table-light tbody + tbody {\n  border-color: #fbfcfc;\n}\n\n.table-hover .table-light:hover {\n  background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n  background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n  background-color: #c6c8ca;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th,\n.table-dark tbody + tbody {\n  border-color: #95999c;\n}\n\n.table-hover .table-dark:hover {\n  background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n  background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n  background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n  color: #fff;\n  background-color: #212529;\n  border-color: #383f45;\n}\n\n.table .thead-light th {\n  color: #495057;\n  background-color: #e9ecef;\n  border-color: #dee2e6;\n}\n\n.table-dark {\n  color: #fff;\n  background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n  border-color: #383f45;\n}\n\n.table-dark.table-bordered {\n  border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n  color: #fff;\n  background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n  .table-responsive-sm {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-sm > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .table-responsive-md {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-md > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .table-responsive-lg {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-lg > .table-bordered {\n    border: 0;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .table-responsive-xl {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive-xl > .table-bordered {\n    border: 0;\n  }\n}\n\n.table-responsive {\n  display: block;\n  width: 100%;\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n}\n\n.table-responsive > .table-bordered {\n  border: 0;\n}\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0);\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .form-control {\n    transition: none;\n  }\n}\n\n.form-control::-ms-expand {\n  background-color: transparent;\n  border: 0;\n}\n\n.form-control:focus {\n  color: #495057;\n  background-color: #fff;\n  border-color: #80bdff;\n  outline: 0;\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0);\n}\n\n.form-control::-webkit-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::-moz-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::-ms-input-placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control::placeholder {\n  color: #939ba2;\n  opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n  background-color: #e9ecef;\n  opacity: 1;\n}\n\ninput[type=\"date\"].form-control,\ninput[type=\"time\"].form-control,\ninput[type=\"datetime-local\"].form-control,\ninput[type=\"month\"].form-control {\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\nselect.form-control:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #495057;\n}\n\nselect.form-control:focus::-ms-value {\n  color: #495057;\n  background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n  display: block;\n  width: 100%;\n}\n\n.col-form-label {\n  padding-top: calc(0.375rem + 1px);\n  padding-bottom: calc(0.375rem + 1px);\n  margin-bottom: 0;\n  font-size: inherit;\n  line-height: 1.5;\n}\n\n.col-form-label-lg {\n  padding-top: calc(0.5rem + 1px);\n  padding-bottom: calc(0.5rem + 1px);\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.col-form-label-sm {\n  padding-top: calc(0.25rem + 1px);\n  padding-bottom: calc(0.25rem + 1px);\n  font-size: 0.875rem;\n  line-height: 1.5;\n}\n\n.form-control-plaintext {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0;\n  margin-bottom: 0;\n  font-size: 1rem;\n  line-height: 1.5;\n  color: #212529;\n  background-color: transparent;\n  border: solid transparent;\n  border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.form-control-sm {\n  height: calc(1.8125rem + 2px);\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.form-control-lg {\n  height: calc(2.875rem + 2px);\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\nselect.form-control[size], select.form-control[multiple] {\n  height: auto;\n}\n\ntextarea.form-control {\n  height: auto;\n}\n\n.form-group {\n  margin-bottom: 1rem;\n}\n\n.form-text {\n  display: block;\n  margin-top: 0.25rem;\n}\n\n.form-row {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  margin-right: -5px;\n  margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n  padding-right: 5px;\n  padding-left: 5px;\n}\n\n.form-check {\n  position: relative;\n  display: block;\n  padding-left: 1.25rem;\n}\n\n.form-check-input {\n  position: absolute;\n  margin-top: 0.3rem;\n  margin-left: -1.25rem;\n}\n\n.form-check-input[disabled] ~ .form-check-label,\n.form-check-input:disabled ~ .form-check-label {\n  color: #6c757d;\n}\n\n.form-check-label {\n  margin-bottom: 0;\n}\n\n.form-check-inline {\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding-left: 0;\n  margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n  position: static;\n  margin-top: 0;\n  margin-right: 0.3125rem;\n  margin-left: 0;\n}\n\n.valid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 80%;\n  color: #28a745;\n}\n\n.valid-tooltip {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: .1rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  color: #fff;\n  background-color: rgba(40, 167, 69, 0.9);\n  border-radius: 0.25rem;\n}\n\n.form-row > .col > .valid-tooltip,\n.form-row > [class*=\"col-\"] > .valid-tooltip {\n  left: 5px;\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n  border-color: #28a745;\n  padding-right: 2.25rem !important;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated select.form-control:valid, select.form-control.is-valid {\n  padding-right: 3rem !important;\n  background-position: right 1.5rem center;\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:valid, .custom-select.is-valid {\n  border-color: #28a745;\n  padding-right: calc(0.75em + 2.3125rem) !important;\n  background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat, #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat;\n}\n\n.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n  color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n  color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n  border-color: #28a745;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n  border-color: #34ce57;\n  background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n  border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n  border-color: #28a745;\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 80%;\n  color: #dc3545;\n}\n\n.invalid-tooltip {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: .1rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  color: #fff;\n  background-color: rgba(220, 53, 69, 0.9);\n  border-radius: 0.25rem;\n}\n\n.form-row > .col > .invalid-tooltip,\n.form-row > [class*=\"col-\"] > .invalid-tooltip {\n  left: 5px;\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n  border-color: #dc3545;\n  padding-right: 2.25rem !important;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated select.form-control:invalid, select.form-control.is-invalid {\n  padding-right: 3rem !important;\n  background-position: right 1.5rem center;\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:invalid, .custom-select.is-invalid {\n  border-color: #dc3545;\n  padding-right: calc(0.75em + 2.3125rem) !important;\n  background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat, #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat;\n}\n\n.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n  color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n  color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n  border-color: #e4606d;\n  background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n  border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-flow: row wrap;\n  flex-flow: row wrap;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.form-inline .form-check {\n  width: 100%;\n}\n\n@media (min-width: 576px) {\n  .form-inline label {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n    margin-bottom: 0;\n  }\n  .form-inline .form-group {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex: 0 0 auto;\n    flex: 0 0 auto;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n    -ms-flex-align: center;\n    align-items: center;\n    margin-bottom: 0;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .form-inline .form-control-plaintext {\n    display: inline-block;\n  }\n  .form-inline .input-group,\n  .form-inline .custom-select {\n    width: auto;\n  }\n  .form-inline .form-check {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n    width: auto;\n    padding-left: 0;\n  }\n  .form-inline .form-check-input {\n    position: relative;\n    -ms-flex-negative: 0;\n    flex-shrink: 0;\n    margin-top: 0;\n    margin-right: 0.25rem;\n    margin-left: 0;\n  }\n  .form-inline .custom-control {\n    -ms-flex-align: center;\n    align-items: center;\n    -ms-flex-pack: center;\n    justify-content: center;\n  }\n  .form-inline .custom-control-label {\n    margin-bottom: 0;\n  }\n}\n\n.btn {\n  display: inline-block;\n  font-weight: 400;\n  color: #212529;\n  text-align: center;\n  vertical-align: middle;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  background-color: transparent;\n  border: 1px solid transparent;\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  line-height: 1.5;\n  border-radius: 0.25rem;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .btn {\n    transition: none;\n  }\n}\n\n.btn:hover {\n  color: #212529;\n  text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n  outline: 0;\n  box-shadow: none;\n}\n\n.btn.disabled, .btn:disabled {\n  opacity: 0.65;\n  box-shadow: none;\n}\n\n.btn:not(:disabled):not(.disabled) {\n  cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n  box-shadow: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n  pointer-events: none;\n}\n\n.btn-primary {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n  box-shadow: none;\n}\n\n.btn-primary:hover {\n  color: #fff;\n  background-color: #0069d9;\n  border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n  color: #fff;\n  background-color: #0069d9;\n  border-color: #0062cc;\n  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #0062cc;\n  border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);\n}\n\n.btn-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n  box-shadow: none;\n}\n\n.btn-secondary:hover {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n  color: #fff;\n  background-color: #5a6268;\n  border-color: #545b62;\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #545b62;\n  border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);\n}\n\n.btn-success {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n  box-shadow: none;\n}\n\n.btn-success:hover {\n  color: #fff;\n  background-color: #218838;\n  border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n  color: #fff;\n  background-color: #218838;\n  border-color: #1e7e34;\n  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n  color: #fff;\n  background-color: #1e7e34;\n  border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);\n}\n\n.btn-info {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  box-shadow: none;\n}\n\n.btn-info:hover {\n  color: #fff;\n  background-color: #138496;\n  border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n  color: #fff;\n  background-color: #138496;\n  border-color: #117a8b;\n  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n  color: #fff;\n  background-color: #117a8b;\n  border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);\n}\n\n.btn-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n  box-shadow: none;\n}\n\n.btn-warning:hover {\n  color: #1f2d3d;\n  background-color: #e0a800;\n  border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n  color: #1f2d3d;\n  background-color: #e0a800;\n  border-color: #d39e00;\n  box-shadow: 0 0 0 0 rgba(221, 171, 15, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #d39e00;\n  border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(221, 171, 15, 0.5);\n}\n\n.btn-danger {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n  box-shadow: none;\n}\n\n.btn-danger:hover {\n  color: #fff;\n  background-color: #c82333;\n  border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n  color: #fff;\n  background-color: #c82333;\n  border-color: #bd2130;\n  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #bd2130;\n  border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);\n}\n\n.btn-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  box-shadow: none;\n}\n\n.btn-light:hover {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n  color: #1f2d3d;\n  background-color: #e2e6ea;\n  border-color: #dae0e5;\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n  border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(215, 218, 222, 0.5);\n}\n\n.btn-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n  box-shadow: none;\n}\n\n.btn-dark:hover {\n  color: #fff;\n  background-color: #23272b;\n  border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n  color: #fff;\n  background-color: #23272b;\n  border-color: #1d2124;\n  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #1d2124;\n  border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);\n}\n\n.btn-outline-primary {\n  color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n  color: #007bff;\n  background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n  color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n  color: #6c757d;\n  background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n  color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n  color: #28a745;\n  background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n  color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n  color: #17a2b8;\n  background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n  color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n  color: #ffc107;\n  background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n  color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n  color: #dc3545;\n  background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n  color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n  color: #f8f9fa;\n  background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n  color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n  color: #343a40;\n  background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n  font-weight: 400;\n  color: #007bff;\n  text-decoration: none;\n}\n\n.btn-link:hover {\n  color: #0056b3;\n  text-decoration: none;\n}\n\n.btn-link:focus, .btn-link.focus {\n  text-decoration: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n  color: #6c757d;\n  pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.btn-block {\n  display: block;\n  width: 100%;\n}\n\n.btn-block + .btn-block {\n  margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n\n.fade {\n  transition: opacity 0.15s linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .fade {\n    transition: none;\n  }\n}\n\n.fade:not(.show) {\n  opacity: 0;\n}\n\n.collapse:not(.show) {\n  display: none;\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  transition: height 0.35s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .collapsing {\n    transition: none;\n  }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n  position: relative;\n}\n\n.dropdown-toggle {\n  white-space: nowrap;\n}\n\n.dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0;\n  border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 10rem;\n  padding: 0.5rem 0;\n  margin: 0.125rem 0 0;\n  font-size: 1rem;\n  color: #212529;\n  text-align: left;\n  list-style: none;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 0.25rem;\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.175);\n}\n\n.dropdown-menu-left {\n  right: auto;\n  left: 0;\n}\n\n.dropdown-menu-right {\n  right: 0;\n  left: auto;\n}\n\n@media (min-width: 576px) {\n  .dropdown-menu-sm-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-sm-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 768px) {\n  .dropdown-menu-md-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-md-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 992px) {\n  .dropdown-menu-lg-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-lg-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n@media (min-width: 1200px) {\n  .dropdown-menu-xl-left {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-xl-right {\n    right: 0;\n    left: auto;\n  }\n}\n\n.dropup .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-top: 0;\n  margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0.3em solid;\n  border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n  top: 0;\n  right: auto;\n  left: 100%;\n  margin-top: 0;\n  margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0;\n  border-bottom: 0.3em solid transparent;\n  border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n  vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n  top: 0;\n  right: 100%;\n  left: auto;\n  margin-top: 0;\n  margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n  display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0.3em solid;\n  border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n  vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n  right: auto;\n  bottom: auto;\n}\n\n.dropdown-divider {\n  height: 0;\n  margin: 0.5rem 0;\n  overflow: hidden;\n  border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n  display: block;\n  width: 100%;\n  padding: 0.25rem 1rem;\n  clear: both;\n  font-weight: 400;\n  color: #212529;\n  text-align: inherit;\n  white-space: nowrap;\n  background-color: transparent;\n  border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n  color: #16181b;\n  text-decoration: none;\n  background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n  color: #fff;\n  text-decoration: none;\n  background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n  color: #6c757d;\n  pointer-events: none;\n  background-color: transparent;\n}\n\n.dropdown-menu.show {\n  display: block;\n}\n\n.dropdown-header {\n  display: block;\n  padding: 0.5rem 1rem;\n  margin-bottom: 0;\n  font-size: 0.875rem;\n  color: #6c757d;\n  white-space: nowrap;\n}\n\n.dropdown-item-text {\n  display: block;\n  padding: 0.25rem 1rem;\n  color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n  z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n  z-index: 1;\n}\n\n.btn-toolbar {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-pack: start;\n  justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n  width: auto;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) {\n  margin-left: -1px;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n  padding-right: 0.5625rem;\n  padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n  margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n  margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n  padding-right: 0.375rem;\n  padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n  padding-right: 0.75rem;\n  padding-left: 0.75rem;\n}\n\n.btn-group.show .dropdown-toggle {\n  box-shadow: none;\n}\n\n.btn-group.show .dropdown-toggle.btn-link {\n  box-shadow: none;\n}\n\n.btn-group-vertical {\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-align: start;\n  align-items: flex-start;\n  -ms-flex-pack: center;\n  justify-content: center;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n  width: 100%;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n  margin-top: -1px;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n  margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n\n.input-group {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: stretch;\n  align-items: stretch;\n  width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .form-control-plaintext,\n.input-group > .custom-select,\n.input-group > .custom-file {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  width: 1%;\n  min-width: 0;\n  margin-bottom: 0;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .form-control-plaintext + .form-control,\n.input-group > .form-control-plaintext + .custom-select,\n.input-group > .form-control-plaintext + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n  margin-left: -1px;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {\n  z-index: 3;\n}\n\n.input-group > .custom-file .custom-file-input:focus {\n  z-index: 4;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.input-group:not(.has-validation) > .form-control:not(:last-child),\n.input-group:not(.has-validation) > .custom-select:not(:last-child),\n.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label,\n.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group.has-validation > .form-control:nth-last-child(n + 3),\n.input-group.has-validation > .custom-select:nth-last-child(n + 3),\n.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label,\n.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label::after {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n  position: relative;\n  z-index: 2;\n}\n\n.input-group-prepend .btn:focus,\n.input-group-append .btn:focus {\n  z-index: 3;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n  margin-left: -1px;\n}\n\n.input-group-prepend {\n  margin-right: -1px;\n}\n\n.input-group-append {\n  margin-left: -1px;\n}\n\n.input-group-text {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding: 0.375rem 0.75rem;\n  margin-bottom: 0;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #e9ecef;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n\n.input-group-lg > .form-control:not(textarea),\n.input-group-lg > .custom-select {\n  height: calc(2.875rem + 2px);\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .custom-select,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  border-radius: 0.3rem;\n}\n\n.input-group-sm > .form-control:not(textarea),\n.input-group-sm > .custom-select {\n  height: calc(1.8125rem + 2px);\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .custom-select,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  border-radius: 0.2rem;\n}\n\n.input-group-lg > .custom-select,\n.input-group-sm > .custom-select {\n  padding-right: 1.75rem;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .btn,\n.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .input-group-text,\n.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .btn,\n.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.custom-control {\n  position: relative;\n  z-index: 1;\n  display: block;\n  min-height: 1.5rem;\n  padding-left: 1.5rem;\n  -webkit-print-color-adjust: exact;\n  color-adjust: exact;\n}\n\n.custom-control-inline {\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  margin-right: 1rem;\n}\n\n.custom-control-input {\n  position: absolute;\n  left: 0;\n  z-index: -1;\n  width: 1rem;\n  height: 1.25rem;\n  opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n  color: #fff;\n  border-color: #007bff;\n  background-color: #007bff;\n  box-shadow: none;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input:not(:disabled):active ~ .custom-control-label::before {\n  color: #fff;\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n  box-shadow: none;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label {\n  color: #6c757d;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before {\n  background-color: #e9ecef;\n}\n\n.custom-control-label {\n  position: relative;\n  margin-bottom: 0;\n  vertical-align: top;\n}\n\n.custom-control-label::before {\n  position: absolute;\n  top: 0.25rem;\n  left: -1.5rem;\n  display: block;\n  width: 1rem;\n  height: 1rem;\n  pointer-events: none;\n  content: \"\";\n  background-color: #dee2e6;\n  border: #adb5bd solid 1px;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-control-label::after {\n  position: absolute;\n  top: 0.25rem;\n  left: -1.5rem;\n  display: block;\n  width: 1rem;\n  height: 1rem;\n  content: \"\";\n  background: 50% / 50% 50% no-repeat;\n}\n\n.custom-checkbox .custom-control-label::before {\n  border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n  box-shadow: none;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n  border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-switch {\n  padding-left: 2.25rem;\n}\n\n.custom-switch .custom-control-label::before {\n  left: -2.25rem;\n  width: 1.75rem;\n  pointer-events: all;\n  border-radius: 0.5rem;\n}\n\n.custom-switch .custom-control-label::after {\n  top: calc(0.25rem + 2px);\n  left: calc(-2.25rem + 2px);\n  width: calc(1rem - 4px);\n  height: calc(1rem - 4px);\n  background-color: #adb5bd;\n  border-radius: 0.5rem;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n  transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-switch .custom-control-label::after {\n    transition: none;\n  }\n}\n\n.custom-switch .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #dee2e6;\n  -webkit-transform: translateX(0.75rem);\n  transform: translateX(0.75rem);\n}\n\n.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {\n  background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n  display: inline-block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  vertical-align: middle;\n  background: #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") right 0.75rem center/8px 10px no-repeat;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n.custom-select:focus {\n  border-color: #80bdff;\n  outline: 0;\n  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n\n.custom-select:focus::-ms-value {\n  color: #495057;\n  background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n  height: auto;\n  padding-right: 0.75rem;\n  background-image: none;\n}\n\n.custom-select:disabled {\n  color: #6c757d;\n  background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n  display: none;\n}\n\n.custom-select:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #495057;\n}\n\n.custom-select-sm {\n  height: calc(1.8125rem + 2px);\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n  padding-left: 0.5rem;\n  font-size: 75%;\n}\n\n.custom-select-lg {\n  height: calc(2.875rem + 2px);\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  padding-left: 1rem;\n  font-size: 125%;\n}\n\n.custom-file {\n  position: relative;\n  display: inline-block;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  margin-bottom: 0;\n}\n\n.custom-file-input {\n  position: relative;\n  z-index: 2;\n  width: 100%;\n  height: calc(2.25rem + 2px);\n  margin: 0;\n  overflow: hidden;\n  opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n  box-shadow: none;\n}\n\n.custom-file-input[disabled] ~ .custom-file-label,\n.custom-file-input:disabled ~ .custom-file-label {\n  background-color: #e9ecef;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n  content: \"Browse\";\n}\n\n.custom-file-input ~ .custom-file-label[data-browse]::after {\n  content: attr(data-browse);\n}\n\n.custom-file-label {\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1;\n  height: calc(2.25rem + 2px);\n  padding: 0.375rem 0.75rem;\n  overflow: hidden;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #495057;\n  background-color: #fff;\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  box-shadow: none;\n}\n\n.custom-file-label::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 3;\n  display: block;\n  height: 2.25rem;\n  padding: 0.375rem 0.75rem;\n  line-height: 1.5;\n  color: #495057;\n  content: \"Browse\";\n  background-color: #e9ecef;\n  border-left: inherit;\n  border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n  width: 100%;\n  height: 1rem;\n  padding: 0;\n  background-color: transparent;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n.custom-range:focus {\n  outline: 0;\n}\n\n.custom-range:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-focus-outer {\n  border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: -0.25rem;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -webkit-appearance: none;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-webkit-slider-thumb {\n    -webkit-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-moz-range-thumb {\n  width: 1rem;\n  height: 1rem;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -moz-appearance: none;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-moz-range-thumb {\n    -moz-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-ms-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: 0;\n  margin-right: 0;\n  margin-left: 0;\n  background-color: #007bff;\n  border: 0;\n  border-radius: 1rem;\n  box-shadow: 0 0.1rem 0.25rem rgba(0, 0, 0, 0.1);\n  -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-range::-ms-thumb {\n    -ms-transition: none;\n    transition: none;\n  }\n}\n\n.custom-range::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: transparent;\n  border-color: transparent;\n  border-width: 0.5rem;\n  box-shadow: inset 0 0.25rem 0.25rem rgba(0, 0, 0, 0.1);\n}\n\n.custom-range::-ms-fill-lower {\n  background-color: #dee2e6;\n  border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n  margin-right: 15px;\n  background-color: #dee2e6;\n  border-radius: 1rem;\n}\n\n.custom-range:disabled::-webkit-slider-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-range:disabled::-webkit-slider-runnable-track {\n  cursor: default;\n}\n\n.custom-range:disabled::-moz-range-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-range:disabled::-moz-range-track {\n  cursor: default;\n}\n\n.custom-range:disabled::-ms-thumb {\n  background-color: #adb5bd;\n}\n\n.custom-control-label::before,\n.custom-file-label,\n.custom-select {\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .custom-control-label::before,\n  .custom-file-label,\n  .custom-select {\n    transition: none;\n  }\n}\n\n.nav {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.nav-link {\n  display: block;\n  padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n  text-decoration: none;\n}\n\n.nav-link.disabled {\n  color: #6c757d;\n  pointer-events: none;\n  cursor: default;\n}\n\n.nav-tabs {\n  border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-link {\n  margin-bottom: -1px;\n  border: 1px solid transparent;\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n  border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n  color: #6c757d;\n  background-color: transparent;\n  border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n  color: #495057;\n  background-color: #fff;\n  border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n  border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n  color: #fff;\n  background-color: #007bff;\n}\n\n.nav-fill > .nav-link,\n.nav-fill .nav-item {\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  text-align: center;\n}\n\n.nav-justified > .nav-link,\n.nav-justified .nav-item {\n  -ms-flex-preferred-size: 0;\n  flex-basis: 0;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  text-align: center;\n}\n\n.tab-content > .tab-pane {\n  display: none;\n}\n\n.tab-content > .active {\n  display: block;\n}\n\n.navbar {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n  padding: 0.5rem 0.5rem;\n}\n\n.navbar .container,\n.navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n}\n\n.navbar-brand {\n  display: inline-block;\n  padding-top: 0.3125rem;\n  padding-bottom: 0.3125rem;\n  margin-right: 0.5rem;\n  font-size: 1.25rem;\n  line-height: inherit;\n  white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n  text-decoration: none;\n}\n\n.navbar-nav {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.navbar-nav .nav-link {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n  position: static;\n  float: none;\n}\n\n.navbar-text {\n  display: inline-block;\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n  -ms-flex-preferred-size: 100%;\n  flex-basis: 100%;\n  -ms-flex-positive: 1;\n  flex-grow: 1;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.navbar-toggler {\n  padding: 0.25rem 0.75rem;\n  font-size: 1.25rem;\n  line-height: 1;\n  background-color: transparent;\n  border: 1px solid transparent;\n  border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n  text-decoration: none;\n}\n\n.navbar-toggler-icon {\n  display: inline-block;\n  width: 1.5em;\n  height: 1.5em;\n  vertical-align: middle;\n  content: \"\";\n  background: 50% / 100% 100% no-repeat;\n}\n\n.navbar-nav-scroll {\n  max-height: 75vh;\n  overflow-y: auto;\n}\n\n@media (max-width: 575.98px) {\n  .navbar-expand-sm > .container,\n  .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 576px) {\n  .navbar-expand-sm {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-sm .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-sm .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-sm .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-sm > .container,\n  .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-sm .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-sm .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-sm .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .navbar-expand-md > .container,\n  .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-expand-md {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-md .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-md .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-md .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-md > .container,\n  .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-md .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-md .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-md .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .navbar-expand-lg > .container,\n  .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .navbar-expand-lg {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-lg .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-lg .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-lg .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-lg > .container,\n  .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-lg .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-lg .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-lg .navbar-toggler {\n    display: none;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .navbar-expand-xl > .container,\n  .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n@media (min-width: 1200px) {\n  .navbar-expand-xl {\n    -ms-flex-flow: row nowrap;\n    flex-flow: row nowrap;\n    -ms-flex-pack: start;\n    justify-content: flex-start;\n  }\n  .navbar-expand-xl .navbar-nav {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .navbar-expand-xl .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-xl .navbar-nav .nav-link {\n    padding-right: 1rem;\n    padding-left: 1rem;\n  }\n  .navbar-expand-xl > .container,\n  .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n    -ms-flex-wrap: nowrap;\n    flex-wrap: nowrap;\n  }\n  .navbar-expand-xl .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-xl .navbar-collapse {\n    display: -ms-flexbox !important;\n    display: flex !important;\n    -ms-flex-preferred-size: auto;\n    flex-basis: auto;\n  }\n  .navbar-expand-xl .navbar-toggler {\n    display: none;\n  }\n}\n\n.navbar-expand {\n  -ms-flex-flow: row nowrap;\n  flex-flow: row nowrap;\n  -ms-flex-pack: start;\n  justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n  padding-right: 1rem;\n  padding-left: 1rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n  -ms-flex-wrap: nowrap;\n  flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-nav-scroll {\n  overflow: visible;\n}\n\n.navbar-expand .navbar-collapse {\n  display: -ms-flexbox !important;\n  display: flex !important;\n  -ms-flex-preferred-size: auto;\n  flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n  display: none;\n}\n\n.navbar-light .navbar-brand {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n  color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n  color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n  color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n  color: rgba(0, 0, 0, 0.5);\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n  color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n  color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n  color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n  color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n  color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n  color: white;\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n  color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n  color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n  color: rgba(255, 255, 255, 0.75);\n  border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n  color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-text a {\n  color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n  color: #fff;\n}\n\n.card {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  min-width: 0;\n  word-wrap: break-word;\n  background-color: #fff;\n  background-clip: border-box;\n  border: 0 solid rgba(0, 0, 0, 0.125);\n  border-radius: 0.25rem;\n}\n\n.card > hr {\n  margin-right: 0;\n  margin-left: 0;\n}\n\n.card > .list-group {\n  border-top: inherit;\n  border-bottom: inherit;\n}\n\n.card > .list-group:first-child {\n  border-top-width: 0;\n  border-top-left-radius: calc(0.25rem - 0);\n  border-top-right-radius: calc(0.25rem - 0);\n}\n\n.card > .list-group:last-child {\n  border-bottom-width: 0;\n  border-bottom-right-radius: calc(0.25rem - 0);\n  border-bottom-left-radius: calc(0.25rem - 0);\n}\n\n.card > .card-header + .list-group,\n.card > .list-group + .card-footer {\n  border-top: 0;\n}\n\n.card-body {\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  min-height: 1px;\n  padding: 1.25rem;\n}\n\n.card-title {\n  margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n  margin-top: -0.375rem;\n  margin-bottom: 0;\n}\n\n.card-text:last-child {\n  margin-bottom: 0;\n}\n\n.card-link:hover {\n  text-decoration: none;\n}\n\n.card-link + .card-link {\n  margin-left: 1.25rem;\n}\n\n.card-header {\n  padding: 0.75rem 1.25rem;\n  margin-bottom: 0;\n  background-color: rgba(0, 0, 0, 0.03);\n  border-bottom: 0 solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n  border-radius: calc(0.25rem - 0) calc(0.25rem - 0) 0 0;\n}\n\n.card-footer {\n  padding: 0.75rem 1.25rem;\n  background-color: rgba(0, 0, 0, 0.03);\n  border-top: 0 solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n  border-radius: 0 0 calc(0.25rem - 0) calc(0.25rem - 0);\n}\n\n.card-header-tabs {\n  margin-right: -0.625rem;\n  margin-bottom: -0.75rem;\n  margin-left: -0.625rem;\n  border-bottom: 0;\n}\n\n.card-header-pills {\n  margin-right: -0.625rem;\n  margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  padding: 1.25rem;\n  border-radius: calc(0.25rem - 0);\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n  -ms-flex-negative: 0;\n  flex-shrink: 0;\n  width: 100%;\n}\n\n.card-img,\n.card-img-top {\n  border-top-left-radius: calc(0.25rem - 0);\n  border-top-right-radius: calc(0.25rem - 0);\n}\n\n.card-img,\n.card-img-bottom {\n  border-bottom-right-radius: calc(0.25rem - 0);\n  border-bottom-left-radius: calc(0.25rem - 0);\n}\n\n.card-deck .card {\n  margin-bottom: 7.5px;\n}\n\n@media (min-width: 576px) {\n  .card-deck {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n    margin-right: -7.5px;\n    margin-left: -7.5px;\n  }\n  .card-deck .card {\n    -ms-flex: 1 0 0%;\n    flex: 1 0 0%;\n    margin-right: 7.5px;\n    margin-bottom: 0;\n    margin-left: 7.5px;\n  }\n}\n\n.card-group > .card {\n  margin-bottom: 7.5px;\n}\n\n@media (min-width: 576px) {\n  .card-group {\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-flow: row wrap;\n    flex-flow: row wrap;\n  }\n  .card-group > .card {\n    -ms-flex: 1 0 0%;\n    flex: 1 0 0%;\n    margin-bottom: 0;\n  }\n  .card-group > .card + .card {\n    margin-left: 0;\n    border-left: 0;\n  }\n  .card-group > .card:not(:last-child) {\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-top,\n  .card-group > .card:not(:last-child) .card-header {\n    border-top-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-bottom,\n  .card-group > .card:not(:last-child) .card-footer {\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:first-child) {\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-top,\n  .card-group > .card:not(:first-child) .card-header {\n    border-top-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-bottom,\n  .card-group > .card:not(:first-child) .card-footer {\n    border-bottom-left-radius: 0;\n  }\n}\n\n.card-columns .card {\n  margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n  .card-columns {\n    -webkit-column-count: 3;\n    -moz-column-count: 3;\n    column-count: 3;\n    -webkit-column-gap: 1.25rem;\n    -moz-column-gap: 1.25rem;\n    column-gap: 1.25rem;\n    orphans: 1;\n    widows: 1;\n  }\n  .card-columns .card {\n    display: inline-block;\n    width: 100%;\n  }\n}\n\n.accordion {\n  overflow-anchor: none;\n}\n\n.accordion > .card {\n  overflow: hidden;\n}\n\n.accordion > .card:not(:last-of-type) {\n  border-bottom: 0;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.accordion > .card:not(:first-of-type) {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.accordion > .card > .card-header {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.breadcrumb {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  padding: 0.75rem 1rem;\n  margin-bottom: 1rem;\n  list-style: none;\n  background-color: #e9ecef;\n  border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n  padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n  float: left;\n  padding-right: 0.5rem;\n  color: #6c757d;\n  content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n  text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n  text-decoration: none;\n}\n\n.breadcrumb-item.active {\n  color: #6c757d;\n}\n\n.pagination {\n  display: -ms-flexbox;\n  display: flex;\n  padding-left: 0;\n  list-style: none;\n  border-radius: 0.25rem;\n}\n\n.page-link {\n  position: relative;\n  display: block;\n  padding: 0.5rem 0.75rem;\n  margin-left: -1px;\n  line-height: 1.25;\n  color: #007bff;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n  z-index: 2;\n  color: #0056b3;\n  text-decoration: none;\n  background-color: #e9ecef;\n  border-color: #dee2e6;\n}\n\n.page-link:focus {\n  z-index: 3;\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-item:first-child .page-link {\n  margin-left: 0;\n  border-top-left-radius: 0.25rem;\n  border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n  border-top-right-radius: 0.25rem;\n  border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n  z-index: 3;\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n  color: #6c757d;\n  pointer-events: none;\n  cursor: auto;\n  background-color: #fff;\n  border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n  padding: 0.75rem 1.5rem;\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n  border-top-left-radius: 0.3rem;\n  border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n  border-top-right-radius: 0.3rem;\n  border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n  border-top-left-radius: 0.2rem;\n  border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n  border-top-right-radius: 0.2rem;\n  border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n  display: inline-block;\n  padding: 0.25em 0.4em;\n  font-size: 75%;\n  font-weight: 700;\n  line-height: 1;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: 0.25rem;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .badge {\n    transition: none;\n  }\n}\n\na.badge:hover, a.badge:focus {\n  text-decoration: none;\n}\n\n.badge:empty {\n  display: none;\n}\n\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n\n.badge-pill {\n  padding-right: 0.6em;\n  padding-left: 0.6em;\n  border-radius: 10rem;\n}\n\n.badge-primary {\n  color: #fff;\n  background-color: #007bff;\n}\n\na.badge-primary:hover, a.badge-primary:focus {\n  color: #fff;\n  background-color: #0062cc;\n}\n\na.badge-primary:focus, a.badge-primary.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.badge-secondary {\n  color: #fff;\n  background-color: #6c757d;\n}\n\na.badge-secondary:hover, a.badge-secondary:focus {\n  color: #fff;\n  background-color: #545b62;\n}\n\na.badge-secondary:focus, a.badge-secondary.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.badge-success {\n  color: #fff;\n  background-color: #28a745;\n}\n\na.badge-success:hover, a.badge-success:focus {\n  color: #fff;\n  background-color: #1e7e34;\n}\n\na.badge-success:focus, a.badge-success.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.badge-info {\n  color: #fff;\n  background-color: #17a2b8;\n}\n\na.badge-info:hover, a.badge-info:focus {\n  color: #fff;\n  background-color: #117a8b;\n}\n\na.badge-info:focus, a.badge-info.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.badge-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n}\n\na.badge-warning:hover, a.badge-warning:focus {\n  color: #1f2d3d;\n  background-color: #d39e00;\n}\n\na.badge-warning:focus, a.badge-warning.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.badge-danger {\n  color: #fff;\n  background-color: #dc3545;\n}\n\na.badge-danger:hover, a.badge-danger:focus {\n  color: #fff;\n  background-color: #bd2130;\n}\n\na.badge-danger:focus, a.badge-danger.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.badge-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n}\n\na.badge-light:hover, a.badge-light:focus {\n  color: #1f2d3d;\n  background-color: #dae0e5;\n}\n\na.badge-light:focus, a.badge-light.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.badge-dark {\n  color: #fff;\n  background-color: #343a40;\n}\n\na.badge-dark:hover, a.badge-dark:focus {\n  color: #fff;\n  background-color: #1d2124;\n}\n\na.badge-dark:focus, a.badge-dark.focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.jumbotron {\n  padding: 2rem 1rem;\n  margin-bottom: 2rem;\n  background-color: #e9ecef;\n  border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n  .jumbotron {\n    padding: 4rem 2rem;\n  }\n}\n\n.jumbotron-fluid {\n  padding-right: 0;\n  padding-left: 0;\n  border-radius: 0;\n}\n\n.alert {\n  position: relative;\n  padding: 0.75rem 1.25rem;\n  margin-bottom: 1rem;\n  border: 1px solid transparent;\n  border-radius: 0.25rem;\n}\n\n.alert-heading {\n  color: inherit;\n}\n\n.alert-link {\n  font-weight: 700;\n}\n\n.alert-dismissible {\n  padding-right: 4rem;\n}\n\n.alert-dismissible .close, .alert-dismissible .mailbox-attachment-close {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  padding: 0.75rem 1.25rem;\n  color: inherit;\n}\n\n.alert-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.alert-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n  color: #002752;\n}\n\n.alert-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n  color: #202326;\n}\n\n.alert-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.alert-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n  color: #0b2e13;\n}\n\n.alert-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.alert-info hr {\n  border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n  color: #062c33;\n}\n\n.alert-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.alert-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n  color: #533f03;\n}\n\n.alert-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n  color: #491217;\n}\n\n.alert-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.alert-light hr {\n  border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n  color: #686868;\n}\n\n.alert-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n  color: #040505;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 1rem 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 1rem 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n.progress {\n  display: -ms-flexbox;\n  display: flex;\n  height: 1rem;\n  overflow: hidden;\n  line-height: 0;\n  font-size: 0.75rem;\n  background-color: #e9ecef;\n  border-radius: 0.25rem;\n  box-shadow: inset 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1);\n}\n\n.progress-bar {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  overflow: hidden;\n  color: #fff;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #007bff;\n  transition: width 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar {\n    transition: none;\n  }\n}\n\n.progress-bar-striped {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n  -webkit-animation: 1s linear infinite progress-bar-stripes;\n  animation: 1s linear infinite progress-bar-stripes;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar-animated {\n    -webkit-animation: none;\n    animation: none;\n  }\n}\n\n.media {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: start;\n  align-items: flex-start;\n}\n\n.media-body {\n  -ms-flex: 1;\n  flex: 1;\n}\n\n.list-group {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  border-radius: 0.25rem;\n}\n\n.list-group-item-action {\n  width: 100%;\n  color: #495057;\n  text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n  z-index: 1;\n  color: #495057;\n  text-decoration: none;\n  background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n  color: #212529;\n  background-color: #e9ecef;\n}\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 0.75rem 1.25rem;\n  background-color: #fff;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n  border-top-left-radius: inherit;\n  border-top-right-radius: inherit;\n}\n\n.list-group-item:last-child {\n  border-bottom-right-radius: inherit;\n  border-bottom-left-radius: inherit;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n  color: #6c757d;\n  pointer-events: none;\n  background-color: #fff;\n}\n\n.list-group-item.active {\n  z-index: 2;\n  color: #fff;\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.list-group-item + .list-group-item {\n  border-top-width: 0;\n}\n\n.list-group-item + .list-group-item.active {\n  margin-top: -1px;\n  border-top-width: 1px;\n}\n\n.list-group-horizontal {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.list-group-horizontal > .list-group-item:first-child {\n  border-bottom-left-radius: 0.25rem;\n  border-top-right-radius: 0;\n}\n\n.list-group-horizontal > .list-group-item:last-child {\n  border-top-right-radius: 0.25rem;\n  border-bottom-left-radius: 0;\n}\n\n.list-group-horizontal > .list-group-item.active {\n  margin-top: 0;\n}\n\n.list-group-horizontal > .list-group-item + .list-group-item {\n  border-top-width: 1px;\n  border-left-width: 0;\n}\n\n.list-group-horizontal > .list-group-item + .list-group-item.active {\n  margin-left: -1px;\n  border-left-width: 1px;\n}\n\n@media (min-width: 576px) {\n  .list-group-horizontal-sm {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-sm > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 768px) {\n  .list-group-horizontal-md {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-md > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 992px) {\n  .list-group-horizontal-lg {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-lg > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .list-group-horizontal-xl {\n    -ms-flex-direction: row;\n    flex-direction: row;\n  }\n  .list-group-horizontal-xl > .list-group-item:first-child {\n    border-bottom-left-radius: 0.25rem;\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item:last-child {\n    border-top-right-radius: 0.25rem;\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item {\n    border-top-width: 1px;\n    border-left-width: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item.active {\n    margin-left: -1px;\n    border-left-width: 1px;\n  }\n}\n\n.list-group-flush {\n  border-radius: 0;\n}\n\n.list-group-flush > .list-group-item {\n  border-width: 0 0 1px;\n}\n\n.list-group-flush > .list-group-item:last-child {\n  border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n  color: #004085;\n  background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n  color: #004085;\n  background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n  color: #fff;\n  background-color: #004085;\n  border-color: #004085;\n}\n\n.list-group-item-secondary {\n  color: #383d41;\n  background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n  color: #383d41;\n  background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n  color: #fff;\n  background-color: #383d41;\n  border-color: #383d41;\n}\n\n.list-group-item-success {\n  color: #155724;\n  background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n  color: #155724;\n  background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n  color: #fff;\n  background-color: #155724;\n  border-color: #155724;\n}\n\n.list-group-item-info {\n  color: #0c5460;\n  background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n  color: #0c5460;\n  background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n  color: #fff;\n  background-color: #0c5460;\n  border-color: #0c5460;\n}\n\n.list-group-item-warning {\n  color: #856404;\n  background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n  color: #856404;\n  background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n  color: #fff;\n  background-color: #856404;\n  border-color: #856404;\n}\n\n.list-group-item-danger {\n  color: #721c24;\n  background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n  color: #721c24;\n  background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n  color: #fff;\n  background-color: #721c24;\n  border-color: #721c24;\n}\n\n.list-group-item-light {\n  color: #818182;\n  background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n  color: #818182;\n  background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n  color: #fff;\n  background-color: #818182;\n  border-color: #818182;\n}\n\n.list-group-item-dark {\n  color: #1b1e21;\n  background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n  color: #1b1e21;\n  background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n  color: #fff;\n  background-color: #1b1e21;\n  border-color: #1b1e21;\n}\n\n.close, .mailbox-attachment-close {\n  float: right;\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: .5;\n}\n\n.close:hover, .mailbox-attachment-close:hover {\n  color: #000;\n  text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .mailbox-attachment-close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus, .mailbox-attachment-close:not(:disabled):not(.disabled):focus {\n  opacity: .75;\n}\n\nbutton.close, button.mailbox-attachment-close {\n  padding: 0;\n  background-color: transparent;\n  border: 0;\n}\n\na.close.disabled, a.disabled.mailbox-attachment-close {\n  pointer-events: none;\n}\n\n.toast {\n  -ms-flex-preferred-size: 350px;\n  flex-basis: 350px;\n  max-width: 350px;\n  font-size: 0.875rem;\n  background-color: rgba(255, 255, 255, 0.85);\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.1);\n  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);\n  opacity: 0;\n  border-radius: 0.25rem;\n}\n\n.toast:not(:last-child) {\n  margin-bottom: 0.75rem;\n}\n\n.toast.showing {\n  opacity: 1;\n}\n\n.toast.show {\n  display: block;\n  opacity: 1;\n}\n\n.toast.hide {\n  display: none;\n}\n\n.toast-header {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  padding: 0.25rem 0.75rem;\n  color: #6c757d;\n  background-color: rgba(255, 255, 255, 0.85);\n  background-clip: padding-box;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n  border-top-left-radius: calc(0.25rem - 1px);\n  border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.toast-body {\n  padding: 0.75rem;\n}\n\n.modal-open {\n  overflow: hidden;\n}\n\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n.modal {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 1050;\n  display: none;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  outline: 0;\n}\n\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 0.5rem;\n  pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n  transition: -webkit-transform 0.3s ease-out;\n  transition: transform 0.3s ease-out;\n  transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;\n  -webkit-transform: translate(0, -50px);\n  transform: translate(0, -50px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .modal.fade .modal-dialog {\n    transition: none;\n  }\n}\n\n.modal.show .modal-dialog {\n  -webkit-transform: none;\n  transform: none;\n}\n\n.modal.modal-static .modal-dialog {\n  -webkit-transform: scale(1.02);\n  transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n  display: -ms-flexbox;\n  display: flex;\n  max-height: calc(100% - 1rem);\n}\n\n.modal-dialog-scrollable .modal-content {\n  max-height: calc(100vh - 1rem);\n  overflow: hidden;\n}\n\n.modal-dialog-scrollable .modal-header,\n.modal-dialog-scrollable .modal-footer {\n  -ms-flex-negative: 0;\n  flex-shrink: 0;\n}\n\n.modal-dialog-scrollable .modal-body {\n  overflow-y: auto;\n}\n\n.modal-dialog-centered {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  min-height: calc(100% - 1rem);\n}\n\n.modal-dialog-centered::before {\n  display: block;\n  height: calc(100vh - 1rem);\n  height: -webkit-min-content;\n  height: -moz-min-content;\n  height: min-content;\n  content: \"\";\n}\n\n.modal-dialog-centered.modal-dialog-scrollable {\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  height: 100%;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable .modal-content {\n  max-height: none;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable::before {\n  content: none;\n}\n\n.modal-content {\n  position: relative;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  width: 100%;\n  pointer-events: auto;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 0.3rem;\n  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5);\n  outline: 0;\n}\n\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 1040;\n  width: 100vw;\n  height: 100vh;\n  background-color: #000;\n}\n\n.modal-backdrop.fade {\n  opacity: 0;\n}\n\n.modal-backdrop.show {\n  opacity: 0.5;\n}\n\n.modal-header {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: start;\n  align-items: flex-start;\n  -ms-flex-pack: justify;\n  justify-content: space-between;\n  padding: 1rem;\n  border-bottom: 1px solid #e9ecef;\n  border-top-left-radius: calc(0.3rem - 1px);\n  border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.modal-header .close, .modal-header .mailbox-attachment-close {\n  padding: 1rem;\n  margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n  margin-bottom: 0;\n  line-height: 1.5;\n}\n\n.modal-body {\n  position: relative;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  padding: 1rem;\n}\n\n.modal-footer {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-wrap: wrap;\n  flex-wrap: wrap;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: end;\n  justify-content: flex-end;\n  padding: 0.75rem;\n  border-top: 1px solid #e9ecef;\n  border-bottom-right-radius: calc(0.3rem - 1px);\n  border-bottom-left-radius: calc(0.3rem - 1px);\n}\n\n.modal-footer > * {\n  margin: 0.25rem;\n}\n\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n\n@media (min-width: 576px) {\n  .modal-dialog {\n    max-width: 500px;\n    margin: 1.75rem auto;\n  }\n  .modal-dialog-scrollable {\n    max-height: calc(100% - 3.5rem);\n  }\n  .modal-dialog-scrollable .modal-content {\n    max-height: calc(100vh - 3.5rem);\n  }\n  .modal-dialog-centered {\n    min-height: calc(100% - 3.5rem);\n  }\n  .modal-dialog-centered::before {\n    height: calc(100vh - 3.5rem);\n    height: -webkit-min-content;\n    height: -moz-min-content;\n    height: min-content;\n  }\n  .modal-content {\n    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);\n  }\n  .modal-sm {\n    max-width: 300px;\n  }\n}\n\n@media (min-width: 992px) {\n  .modal-lg,\n  .modal-xl {\n    max-width: 800px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .modal-xl {\n    max-width: 1140px;\n  }\n}\n\n.tooltip {\n  position: absolute;\n  z-index: 1070;\n  display: block;\n  margin: 0;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  font-size: 0.875rem;\n  word-wrap: break-word;\n  opacity: 0;\n}\n\n.tooltip.show {\n  opacity: 0.9;\n}\n\n.tooltip .arrow {\n  position: absolute;\n  display: block;\n  width: 0.8rem;\n  height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n  position: absolute;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n  padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n  bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n  top: 0;\n  border-width: 0.4rem 0.4rem 0;\n  border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n  padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n  left: 0;\n  width: 0.4rem;\n  height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n  right: 0;\n  border-width: 0.4rem 0.4rem 0.4rem 0;\n  border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n  padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n  top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n  bottom: 0;\n  border-width: 0 0.4rem 0.4rem;\n  border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n  padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n  right: 0;\n  width: 0.4rem;\n  height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n  left: 0;\n  border-width: 0.4rem 0 0.4rem 0.4rem;\n  border-left-color: #000;\n}\n\n.tooltip-inner {\n  max-width: 200px;\n  padding: 0.25rem 0.5rem;\n  color: #fff;\n  text-align: center;\n  background-color: #000;\n  border-radius: 0.25rem;\n}\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1060;\n  display: block;\n  max-width: 276px;\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  font-size: 0.875rem;\n  word-wrap: break-word;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 0.3rem;\n  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.2);\n}\n\n.popover .arrow {\n  position: absolute;\n  display: block;\n  width: 1rem;\n  height: 0.5rem;\n  margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n  position: absolute;\n  display: block;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n  margin-bottom: 0.5rem;\n}\n\n.bs-popover-top > .arrow, .bs-popover-auto[x-placement^=\"top\"] > .arrow {\n  bottom: calc(-0.5rem - 1px);\n}\n\n.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^=\"top\"] > .arrow::before {\n  bottom: 0;\n  border-width: 0.5rem 0.5rem 0;\n  border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^=\"top\"] > .arrow::after {\n  bottom: 1px;\n  border-width: 0.5rem 0.5rem 0;\n  border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n  margin-left: 0.5rem;\n}\n\n.bs-popover-right > .arrow, .bs-popover-auto[x-placement^=\"right\"] > .arrow {\n  left: calc(-0.5rem - 1px);\n  width: 0.5rem;\n  height: 1rem;\n  margin: 0.3rem 0;\n}\n\n.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^=\"right\"] > .arrow::before {\n  left: 0;\n  border-width: 0.5rem 0.5rem 0.5rem 0;\n  border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^=\"right\"] > .arrow::after {\n  left: 1px;\n  border-width: 0.5rem 0.5rem 0.5rem 0;\n  border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n  margin-top: 0.5rem;\n}\n\n.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow {\n  top: calc(-0.5rem - 1px);\n}\n\n.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::before {\n  top: 0;\n  border-width: 0 0.5rem 0.5rem 0.5rem;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::after {\n  top: 1px;\n  border-width: 0 0.5rem 0.5rem 0.5rem;\n  border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n  position: absolute;\n  top: 0;\n  left: 50%;\n  display: block;\n  width: 1rem;\n  margin-left: -0.5rem;\n  content: \"\";\n  border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n  margin-right: 0.5rem;\n}\n\n.bs-popover-left > .arrow, .bs-popover-auto[x-placement^=\"left\"] > .arrow {\n  right: calc(-0.5rem - 1px);\n  width: 0.5rem;\n  height: 1rem;\n  margin: 0.3rem 0;\n}\n\n.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^=\"left\"] > .arrow::before {\n  right: 0;\n  border-width: 0.5rem 0 0.5rem 0.5rem;\n  border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^=\"left\"] > .arrow::after {\n  right: 1px;\n  border-width: 0.5rem 0 0.5rem 0.5rem;\n  border-left-color: #fff;\n}\n\n.popover-header {\n  padding: 0.5rem 0.75rem;\n  margin-bottom: 0;\n  font-size: 1rem;\n  color: inherit;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-top-left-radius: calc(0.3rem - 1px);\n  border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n  display: none;\n}\n\n.popover-body {\n  padding: 0.5rem 0.75rem;\n  color: #212529;\n}\n\n.carousel {\n  position: relative;\n}\n\n.carousel.pointer-event {\n  -ms-touch-action: pan-y;\n  touch-action: pan-y;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n\n.carousel-inner::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.carousel-item {\n  position: relative;\n  display: none;\n  float: left;\n  width: 100%;\n  margin-right: -100%;\n  -webkit-backface-visibility: hidden;\n  backface-visibility: hidden;\n  transition: -webkit-transform 0.6s ease;\n  transition: transform 0.6s ease;\n  transition: transform 0.6s ease, -webkit-transform 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-item {\n    transition: none;\n  }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n  display: block;\n}\n\n.carousel-item-next:not(.carousel-item-left),\n.active.carousel-item-right {\n  -webkit-transform: translateX(100%);\n  transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-right),\n.active.carousel-item-left {\n  -webkit-transform: translateX(-100%);\n  transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n  opacity: 0;\n  transition-property: opacity;\n  -webkit-transform: none;\n  transform: none;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n  z-index: 1;\n  opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n  z-index: 0;\n  opacity: 0;\n  transition: opacity 0s 0.6s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-fade .active.carousel-item-left,\n  .carousel-fade .active.carousel-item-right {\n    transition: none;\n  }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  z-index: 1;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: center;\n  justify-content: center;\n  width: 15%;\n  padding: 0;\n  color: #fff;\n  text-align: center;\n  background: none;\n  border: 0;\n  opacity: 0.5;\n  transition: opacity 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-control-prev,\n  .carousel-control-next {\n    transition: none;\n  }\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n  color: #fff;\n  text-decoration: none;\n  outline: 0;\n  opacity: 0.9;\n}\n\n.carousel-control-prev {\n  left: 0;\n}\n\n.carousel-control-next {\n  right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n  display: inline-block;\n  width: 20px;\n  height: 20px;\n  background: 50% / 100% 100% no-repeat;\n}\n\n.carousel-control-prev-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 15;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  padding-left: 0;\n  margin-right: 15%;\n  margin-left: 15%;\n  list-style: none;\n}\n\n.carousel-indicators li {\n  box-sizing: content-box;\n  -ms-flex: 0 1 auto;\n  flex: 0 1 auto;\n  width: 30px;\n  height: 3px;\n  margin-right: 3px;\n  margin-left: 3px;\n  text-indent: -999px;\n  cursor: pointer;\n  background-color: #fff;\n  background-clip: padding-box;\n  border-top: 10px solid transparent;\n  border-bottom: 10px solid transparent;\n  opacity: .5;\n  transition: opacity 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .carousel-indicators li {\n    transition: none;\n  }\n}\n\n.carousel-indicators .active {\n  opacity: 1;\n}\n\n.carousel-caption {\n  position: absolute;\n  right: 15%;\n  bottom: 20px;\n  left: 15%;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #fff;\n  text-align: center;\n}\n\n@-webkit-keyframes spinner-border {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg);\n  }\n}\n\n@keyframes spinner-border {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg);\n  }\n}\n\n.spinner-border {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  vertical-align: -0.125em;\n  border: 0.25em solid currentColor;\n  border-right-color: transparent;\n  border-radius: 50%;\n  -webkit-animation: .75s linear infinite spinner-border;\n  animation: .75s linear infinite spinner-border;\n}\n\n.spinner-border-sm {\n  width: 1rem;\n  height: 1rem;\n  border-width: 0.2em;\n}\n\n@-webkit-keyframes spinner-grow {\n  0% {\n    -webkit-transform: scale(0);\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes spinner-grow {\n  0% {\n    -webkit-transform: scale(0);\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.spinner-grow {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  vertical-align: -0.125em;\n  background-color: currentColor;\n  border-radius: 50%;\n  opacity: 0;\n  -webkit-animation: .75s linear infinite spinner-grow;\n  animation: .75s linear infinite spinner-grow;\n}\n\n.spinner-grow-sm {\n  width: 1rem;\n  height: 1rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .spinner-border,\n  .spinner-grow {\n    -webkit-animation-duration: 1.5s;\n    animation-duration: 1.5s;\n  }\n}\n\n.align-baseline {\n  vertical-align: baseline !important;\n}\n\n.align-top {\n  vertical-align: top !important;\n}\n\n.align-middle {\n  vertical-align: middle !important;\n}\n\n.align-bottom {\n  vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n  vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n  vertical-align: text-top !important;\n}\n\n.bg-primary {\n  background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n  background-color: #0062cc !important;\n}\n\n.bg-secondary {\n  background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n  background-color: #545b62 !important;\n}\n\n.bg-success {\n  background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n  background-color: #1e7e34 !important;\n}\n\n.bg-info {\n  background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n  background-color: #117a8b !important;\n}\n\n.bg-warning {\n  background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n  background-color: #d39e00 !important;\n}\n\n.bg-danger {\n  background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n  background-color: #bd2130 !important;\n}\n\n.bg-light {\n  background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n  background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n  background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n  background-color: #1d2124 !important;\n}\n\n.bg-white {\n  background-color: #fff !important;\n}\n\n.bg-transparent {\n  background-color: transparent !important;\n}\n\n.border {\n  border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n  border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n  border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n  border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n  border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n  border: 0 !important;\n}\n\n.border-top-0 {\n  border-top: 0 !important;\n}\n\n.border-right-0 {\n  border-right: 0 !important;\n}\n\n.border-bottom-0 {\n  border-bottom: 0 !important;\n}\n\n.border-left-0 {\n  border-left: 0 !important;\n}\n\n.border-primary {\n  border-color: #007bff !important;\n}\n\n.border-secondary {\n  border-color: #6c757d !important;\n}\n\n.border-success {\n  border-color: #28a745 !important;\n}\n\n.border-info {\n  border-color: #17a2b8 !important;\n}\n\n.border-warning {\n  border-color: #ffc107 !important;\n}\n\n.border-danger {\n  border-color: #dc3545 !important;\n}\n\n.border-light {\n  border-color: #f8f9fa !important;\n}\n\n.border-dark {\n  border-color: #343a40 !important;\n}\n\n.border-white {\n  border-color: #fff !important;\n}\n\n.rounded-sm {\n  border-radius: 0.2rem !important;\n}\n\n.rounded {\n  border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n  border-top-left-radius: 0.25rem !important;\n  border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n  border-top-right-radius: 0.25rem !important;\n  border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n  border-bottom-right-radius: 0.25rem !important;\n  border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n  border-top-left-radius: 0.25rem !important;\n  border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-lg {\n  border-radius: 0.3rem !important;\n}\n\n.rounded-circle {\n  border-radius: 50% !important;\n}\n\n.rounded-pill {\n  border-radius: 50rem !important;\n}\n\n.rounded-0 {\n  border-radius: 0 !important;\n}\n\n.clearfix::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.d-none {\n  display: none !important;\n}\n\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: -ms-flexbox !important;\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: -ms-inline-flexbox !important;\n  display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n  .d-sm-none {\n    display: none !important;\n  }\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .d-md-none {\n    display: none !important;\n  }\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .d-lg-none {\n    display: none !important;\n  }\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .d-xl-none {\n    display: none !important;\n  }\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n@media print {\n  .d-print-none {\n    display: none !important;\n  }\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: -ms-flexbox !important;\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: -ms-inline-flexbox !important;\n    display: inline-flex !important;\n  }\n}\n\n.embed-responsive {\n  position: relative;\n  display: block;\n  width: 100%;\n  padding: 0;\n  overflow: hidden;\n}\n\n.embed-responsive::before {\n  display: block;\n  content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  border: 0;\n}\n\n.embed-responsive-21by9::before {\n  padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n  padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n  padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n  padding-top: 100%;\n}\n\n.flex-row {\n  -ms-flex-direction: row !important;\n  flex-direction: row !important;\n}\n\n.flex-column {\n  -ms-flex-direction: column !important;\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  -ms-flex-direction: row-reverse !important;\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  -ms-flex-direction: column-reverse !important;\n  flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n  -ms-flex-wrap: wrap !important;\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  -ms-flex-wrap: nowrap !important;\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  -ms-flex-wrap: wrap-reverse !important;\n  flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n  -ms-flex: 1 1 auto !important;\n  flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n  -ms-flex-positive: 0 !important;\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  -ms-flex-positive: 1 !important;\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  -ms-flex-negative: 0 !important;\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  -ms-flex-negative: 1 !important;\n  flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n  -ms-flex-pack: start !important;\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  -ms-flex-pack: end !important;\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  -ms-flex-pack: center !important;\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  -ms-flex-pack: justify !important;\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  -ms-flex-pack: distribute !important;\n  justify-content: space-around !important;\n}\n\n.align-items-start {\n  -ms-flex-align: start !important;\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  -ms-flex-align: end !important;\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  -ms-flex-align: center !important;\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  -ms-flex-align: baseline !important;\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  -ms-flex-align: stretch !important;\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  -ms-flex-line-pack: start !important;\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  -ms-flex-line-pack: end !important;\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  -ms-flex-line-pack: center !important;\n  align-content: center !important;\n}\n\n.align-content-between {\n  -ms-flex-line-pack: justify !important;\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  -ms-flex-line-pack: distribute !important;\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  -ms-flex-line-pack: stretch !important;\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  -ms-flex-item-align: auto !important;\n  align-self: auto !important;\n}\n\n.align-self-start {\n  -ms-flex-item-align: start !important;\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  -ms-flex-item-align: end !important;\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  -ms-flex-item-align: center !important;\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  -ms-flex-item-align: baseline !important;\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  -ms-flex-item-align: stretch !important;\n  align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n  .flex-sm-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-sm-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-sm-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-sm-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .flex-md-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-md-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-md-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-md-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-md-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .flex-lg-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-lg-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-lg-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-lg-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .flex-xl-row {\n    -ms-flex-direction: row !important;\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    -ms-flex-direction: column !important;\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    -ms-flex-direction: row-reverse !important;\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    -ms-flex-direction: column-reverse !important;\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-wrap {\n    -ms-flex-wrap: wrap !important;\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    -ms-flex-wrap: nowrap !important;\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    -ms-flex-wrap: wrap-reverse !important;\n    flex-wrap: wrap-reverse !important;\n  }\n  .flex-xl-fill {\n    -ms-flex: 1 1 auto !important;\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-grow-0 {\n    -ms-flex-positive: 0 !important;\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    -ms-flex-positive: 1 !important;\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    -ms-flex-negative: 0 !important;\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    -ms-flex-negative: 1 !important;\n    flex-shrink: 1 !important;\n  }\n  .justify-content-xl-start {\n    -ms-flex-pack: start !important;\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    -ms-flex-pack: end !important;\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    -ms-flex-pack: center !important;\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    -ms-flex-pack: justify !important;\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    -ms-flex-pack: distribute !important;\n    justify-content: space-around !important;\n  }\n  .align-items-xl-start {\n    -ms-flex-align: start !important;\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    -ms-flex-align: end !important;\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    -ms-flex-align: center !important;\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    -ms-flex-align: baseline !important;\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    -ms-flex-align: stretch !important;\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    -ms-flex-line-pack: start !important;\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    -ms-flex-line-pack: end !important;\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    -ms-flex-line-pack: center !important;\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    -ms-flex-line-pack: justify !important;\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    -ms-flex-line-pack: distribute !important;\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    -ms-flex-line-pack: stretch !important;\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    -ms-flex-item-align: auto !important;\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    -ms-flex-item-align: start !important;\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    -ms-flex-item-align: end !important;\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    -ms-flex-item-align: center !important;\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    -ms-flex-item-align: baseline !important;\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    -ms-flex-item-align: stretch !important;\n    align-self: stretch !important;\n  }\n}\n\n.float-left {\n  float: left !important;\n}\n\n.float-right {\n  float: right !important;\n}\n\n.float-none {\n  float: none !important;\n}\n\n@media (min-width: 576px) {\n  .float-sm-left {\n    float: left !important;\n  }\n  .float-sm-right {\n    float: right !important;\n  }\n  .float-sm-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .float-md-left {\n    float: left !important;\n  }\n  .float-md-right {\n    float: right !important;\n  }\n  .float-md-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .float-lg-left {\n    float: left !important;\n  }\n  .float-lg-right {\n    float: right !important;\n  }\n  .float-lg-none {\n    float: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .float-xl-left {\n    float: left !important;\n  }\n  .float-xl-right {\n    float: right !important;\n  }\n  .float-xl-none {\n    float: none !important;\n  }\n}\n\n.user-select-all {\n  -webkit-user-select: all !important;\n  -moz-user-select: all !important;\n  user-select: all !important;\n}\n\n.user-select-auto {\n  -webkit-user-select: auto !important;\n  -moz-user-select: auto !important;\n  -ms-user-select: auto !important;\n  user-select: auto !important;\n}\n\n.user-select-none {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  -ms-user-select: none !important;\n  user-select: none !important;\n}\n\n.overflow-auto {\n  overflow: auto !important;\n}\n\n.overflow-hidden {\n  overflow: hidden !important;\n}\n\n.position-static {\n  position: static !important;\n}\n\n.position-relative {\n  position: relative !important;\n}\n\n.position-absolute {\n  position: absolute !important;\n}\n\n.position-fixed {\n  position: fixed !important;\n}\n\n.position-sticky {\n  position: -webkit-sticky !important;\n  position: sticky !important;\n}\n\n.fixed-top {\n  position: fixed;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n.fixed-bottom {\n  position: fixed;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n@supports ((position: -webkit-sticky) or (position: sticky)) {\n  .sticky-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  overflow: visible;\n  clip: auto;\n  white-space: normal;\n}\n\n.shadow-sm {\n  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n  box-shadow: none !important;\n}\n\n.w-25 {\n  width: 25% !important;\n}\n\n.w-50 {\n  width: 50% !important;\n}\n\n.w-75 {\n  width: 75% !important;\n}\n\n.w-100 {\n  width: 100% !important;\n}\n\n.w-auto {\n  width: auto !important;\n}\n\n.h-25 {\n  height: 25% !important;\n}\n\n.h-50 {\n  height: 50% !important;\n}\n\n.h-75 {\n  height: 75% !important;\n}\n\n.h-100 {\n  height: 100% !important;\n}\n\n.h-auto {\n  height: auto !important;\n}\n\n.mw-100 {\n  max-width: 100% !important;\n}\n\n.mh-100 {\n  max-height: 100% !important;\n}\n\n.min-vw-100 {\n  min-width: 100vw !important;\n}\n\n.min-vh-100 {\n  min-height: 100vh !important;\n}\n\n.vw-100 {\n  width: 100vw !important;\n}\n\n.vh-100 {\n  height: 100vh !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n  margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n  margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n  margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n  margin-left: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n  margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n  margin-left: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n  margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n  margin-left: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n  margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n  margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n  margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n  margin-left: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n  margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n  margin-left: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n  margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n  margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n  margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n  margin-left: 3rem !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n  padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n  padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n  padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n  padding-left: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n  padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n  padding-left: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n  padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n  padding-left: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n  padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n  padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n  padding-left: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n  padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n  padding-left: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n  padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n  padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n  padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n  padding-left: 3rem !important;\n}\n\n.m-n1 {\n  margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n  margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n  margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n  margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n  margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n  margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n  margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n  margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n  margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n  margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n  margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n  margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n  margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n  margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n  margin-left: -1rem !important;\n}\n\n.m-n4 {\n  margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n  margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n  margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n  margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n  margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n  margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n  margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n  margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n  margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n  margin-left: -3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n  margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n  margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n  margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n  margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .mt-sm-0,\n  .my-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mr-sm-0,\n  .mx-sm-0 {\n    margin-right: 0 !important;\n  }\n  .mb-sm-0,\n  .my-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-sm-0,\n  .mx-sm-0 {\n    margin-left: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-sm-1,\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-sm-1,\n  .mx-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-sm-1,\n  .my-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-sm-1,\n  .mx-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-sm-2,\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-sm-2,\n  .mx-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-sm-2,\n  .my-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-sm-2,\n  .mx-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .mt-sm-3,\n  .my-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-sm-3,\n  .mx-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-sm-3,\n  .my-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-sm-3,\n  .mx-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-sm-4,\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-sm-4,\n  .mx-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-sm-4,\n  .my-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-sm-4,\n  .mx-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .mt-sm-5,\n  .my-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-sm-5,\n  .mx-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-sm-5,\n  .my-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-sm-5,\n  .mx-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .pt-sm-0,\n  .py-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pr-sm-0,\n  .px-sm-0 {\n    padding-right: 0 !important;\n  }\n  .pb-sm-0,\n  .py-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-sm-0,\n  .px-sm-0 {\n    padding-left: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-sm-1,\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-sm-1,\n  .px-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-sm-1,\n  .py-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-sm-1,\n  .px-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-sm-2,\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-sm-2,\n  .px-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-sm-2,\n  .py-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-sm-2,\n  .px-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .pt-sm-3,\n  .py-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-sm-3,\n  .px-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-sm-3,\n  .py-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-sm-3,\n  .px-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-sm-4,\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-sm-4,\n  .px-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-sm-4,\n  .py-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-sm-4,\n  .px-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .pt-sm-5,\n  .py-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-sm-5,\n  .px-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-sm-5,\n  .py-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-sm-5,\n  .px-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .m-sm-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-sm-n1,\n  .my-sm-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-sm-n1,\n  .mx-sm-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-sm-n1,\n  .my-sm-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-sm-n1,\n  .mx-sm-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-sm-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-sm-n2,\n  .my-sm-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-sm-n2,\n  .mx-sm-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-sm-n2,\n  .my-sm-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-sm-n2,\n  .mx-sm-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-sm-n3 {\n    margin: -1rem !important;\n  }\n  .mt-sm-n3,\n  .my-sm-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-sm-n3,\n  .mx-sm-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-sm-n3,\n  .my-sm-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-sm-n3,\n  .mx-sm-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-sm-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-sm-n4,\n  .my-sm-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-sm-n4,\n  .mx-sm-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-sm-n4,\n  .my-sm-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-sm-n4,\n  .mx-sm-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-sm-n5 {\n    margin: -3rem !important;\n  }\n  .mt-sm-n5,\n  .my-sm-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-sm-n5,\n  .mx-sm-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-sm-n5,\n  .my-sm-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-sm-n5,\n  .mx-sm-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mt-sm-auto,\n  .my-sm-auto {\n    margin-top: auto !important;\n  }\n  .mr-sm-auto,\n  .mx-sm-auto {\n    margin-right: auto !important;\n  }\n  .mb-sm-auto,\n  .my-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-sm-auto,\n  .mx-sm-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .mt-md-0,\n  .my-md-0 {\n    margin-top: 0 !important;\n  }\n  .mr-md-0,\n  .mx-md-0 {\n    margin-right: 0 !important;\n  }\n  .mb-md-0,\n  .my-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-md-0,\n  .mx-md-0 {\n    margin-left: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-md-1,\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-md-1,\n  .mx-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-md-1,\n  .my-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-md-1,\n  .mx-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-md-2,\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-md-2,\n  .mx-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-md-2,\n  .my-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-md-2,\n  .mx-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .mt-md-3,\n  .my-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-md-3,\n  .mx-md-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-md-3,\n  .my-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-md-3,\n  .mx-md-3 {\n    margin-left: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-md-4,\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-md-4,\n  .mx-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-md-4,\n  .my-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-md-4,\n  .mx-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .mt-md-5,\n  .my-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-md-5,\n  .mx-md-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-md-5,\n  .my-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-md-5,\n  .mx-md-5 {\n    margin-left: 3rem !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .pt-md-0,\n  .py-md-0 {\n    padding-top: 0 !important;\n  }\n  .pr-md-0,\n  .px-md-0 {\n    padding-right: 0 !important;\n  }\n  .pb-md-0,\n  .py-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-md-0,\n  .px-md-0 {\n    padding-left: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-md-1,\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-md-1,\n  .px-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-md-1,\n  .py-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-md-1,\n  .px-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-md-2,\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-md-2,\n  .px-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-md-2,\n  .py-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-md-2,\n  .px-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .pt-md-3,\n  .py-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-md-3,\n  .px-md-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-md-3,\n  .py-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-md-3,\n  .px-md-3 {\n    padding-left: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-md-4,\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-md-4,\n  .px-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-md-4,\n  .py-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-md-4,\n  .px-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .pt-md-5,\n  .py-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-md-5,\n  .px-md-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-md-5,\n  .py-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-md-5,\n  .px-md-5 {\n    padding-left: 3rem !important;\n  }\n  .m-md-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-md-n1,\n  .my-md-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-md-n1,\n  .mx-md-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-md-n1,\n  .my-md-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-md-n1,\n  .mx-md-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-md-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-md-n2,\n  .my-md-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-md-n2,\n  .mx-md-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-md-n2,\n  .my-md-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-md-n2,\n  .mx-md-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-md-n3 {\n    margin: -1rem !important;\n  }\n  .mt-md-n3,\n  .my-md-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-md-n3,\n  .mx-md-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-md-n3,\n  .my-md-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-md-n3,\n  .mx-md-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-md-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-md-n4,\n  .my-md-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-md-n4,\n  .mx-md-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-md-n4,\n  .my-md-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-md-n4,\n  .mx-md-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-md-n5 {\n    margin: -3rem !important;\n  }\n  .mt-md-n5,\n  .my-md-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-md-n5,\n  .mx-md-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-md-n5,\n  .my-md-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-md-n5,\n  .mx-md-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mt-md-auto,\n  .my-md-auto {\n    margin-top: auto !important;\n  }\n  .mr-md-auto,\n  .mx-md-auto {\n    margin-right: auto !important;\n  }\n  .mb-md-auto,\n  .my-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-md-auto,\n  .mx-md-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .mt-lg-0,\n  .my-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mr-lg-0,\n  .mx-lg-0 {\n    margin-right: 0 !important;\n  }\n  .mb-lg-0,\n  .my-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-lg-0,\n  .mx-lg-0 {\n    margin-left: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-lg-1,\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-lg-1,\n  .mx-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-lg-1,\n  .my-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-lg-1,\n  .mx-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-lg-2,\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-lg-2,\n  .mx-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-lg-2,\n  .my-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-lg-2,\n  .mx-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .mt-lg-3,\n  .my-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-lg-3,\n  .mx-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-lg-3,\n  .my-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-lg-3,\n  .mx-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-lg-4,\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-lg-4,\n  .mx-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-lg-4,\n  .my-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-lg-4,\n  .mx-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .mt-lg-5,\n  .my-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-lg-5,\n  .mx-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-lg-5,\n  .my-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-lg-5,\n  .mx-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .pt-lg-0,\n  .py-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pr-lg-0,\n  .px-lg-0 {\n    padding-right: 0 !important;\n  }\n  .pb-lg-0,\n  .py-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-lg-0,\n  .px-lg-0 {\n    padding-left: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-lg-1,\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-lg-1,\n  .px-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-lg-1,\n  .py-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-lg-1,\n  .px-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-lg-2,\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-lg-2,\n  .px-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-lg-2,\n  .py-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-lg-2,\n  .px-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .pt-lg-3,\n  .py-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-lg-3,\n  .px-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-lg-3,\n  .py-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-lg-3,\n  .px-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-lg-4,\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-lg-4,\n  .px-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-lg-4,\n  .py-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-lg-4,\n  .px-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .pt-lg-5,\n  .py-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-lg-5,\n  .px-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-lg-5,\n  .py-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-lg-5,\n  .px-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .m-lg-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-lg-n1,\n  .my-lg-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-lg-n1,\n  .mx-lg-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-lg-n1,\n  .my-lg-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-lg-n1,\n  .mx-lg-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-lg-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-lg-n2,\n  .my-lg-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-lg-n2,\n  .mx-lg-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-lg-n2,\n  .my-lg-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-lg-n2,\n  .mx-lg-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-lg-n3 {\n    margin: -1rem !important;\n  }\n  .mt-lg-n3,\n  .my-lg-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-lg-n3,\n  .mx-lg-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-lg-n3,\n  .my-lg-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-lg-n3,\n  .mx-lg-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-lg-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-lg-n4,\n  .my-lg-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-lg-n4,\n  .mx-lg-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-lg-n4,\n  .my-lg-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-lg-n4,\n  .mx-lg-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-lg-n5 {\n    margin: -3rem !important;\n  }\n  .mt-lg-n5,\n  .my-lg-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-lg-n5,\n  .mx-lg-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-lg-n5,\n  .my-lg-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-lg-n5,\n  .mx-lg-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mt-lg-auto,\n  .my-lg-auto {\n    margin-top: auto !important;\n  }\n  .mr-lg-auto,\n  .mx-lg-auto {\n    margin-right: auto !important;\n  }\n  .mb-lg-auto,\n  .my-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-lg-auto,\n  .mx-lg-auto {\n    margin-left: auto !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .mt-xl-0,\n  .my-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mr-xl-0,\n  .mx-xl-0 {\n    margin-right: 0 !important;\n  }\n  .mb-xl-0,\n  .my-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .ml-xl-0,\n  .mx-xl-0 {\n    margin-left: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .mt-xl-1,\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mr-xl-1,\n  .mx-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .mb-xl-1,\n  .my-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .ml-xl-1,\n  .mx-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .mt-xl-2,\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mr-xl-2,\n  .mx-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .mb-xl-2,\n  .my-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .ml-xl-2,\n  .mx-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .mt-xl-3,\n  .my-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mr-xl-3,\n  .mx-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .mb-xl-3,\n  .my-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .ml-xl-3,\n  .mx-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .mt-xl-4,\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mr-xl-4,\n  .mx-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .mb-xl-4,\n  .my-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .ml-xl-4,\n  .mx-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .mt-xl-5,\n  .my-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mr-xl-5,\n  .mx-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .mb-xl-5,\n  .my-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .ml-xl-5,\n  .mx-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .pt-xl-0,\n  .py-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pr-xl-0,\n  .px-xl-0 {\n    padding-right: 0 !important;\n  }\n  .pb-xl-0,\n  .py-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pl-xl-0,\n  .px-xl-0 {\n    padding-left: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .pt-xl-1,\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pr-xl-1,\n  .px-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pb-xl-1,\n  .py-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pl-xl-1,\n  .px-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .pt-xl-2,\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pr-xl-2,\n  .px-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pb-xl-2,\n  .py-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pl-xl-2,\n  .px-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .pt-xl-3,\n  .py-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pr-xl-3,\n  .px-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .pb-xl-3,\n  .py-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pl-xl-3,\n  .px-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .pt-xl-4,\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pr-xl-4,\n  .px-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pb-xl-4,\n  .py-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pl-xl-4,\n  .px-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .pt-xl-5,\n  .py-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pr-xl-5,\n  .px-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xl-5,\n  .py-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .pl-xl-5,\n  .px-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .m-xl-n1 {\n    margin: -0.25rem !important;\n  }\n  .mt-xl-n1,\n  .my-xl-n1 {\n    margin-top: -0.25rem !important;\n  }\n  .mr-xl-n1,\n  .mx-xl-n1 {\n    margin-right: -0.25rem !important;\n  }\n  .mb-xl-n1,\n  .my-xl-n1 {\n    margin-bottom: -0.25rem !important;\n  }\n  .ml-xl-n1,\n  .mx-xl-n1 {\n    margin-left: -0.25rem !important;\n  }\n  .m-xl-n2 {\n    margin: -0.5rem !important;\n  }\n  .mt-xl-n2,\n  .my-xl-n2 {\n    margin-top: -0.5rem !important;\n  }\n  .mr-xl-n2,\n  .mx-xl-n2 {\n    margin-right: -0.5rem !important;\n  }\n  .mb-xl-n2,\n  .my-xl-n2 {\n    margin-bottom: -0.5rem !important;\n  }\n  .ml-xl-n2,\n  .mx-xl-n2 {\n    margin-left: -0.5rem !important;\n  }\n  .m-xl-n3 {\n    margin: -1rem !important;\n  }\n  .mt-xl-n3,\n  .my-xl-n3 {\n    margin-top: -1rem !important;\n  }\n  .mr-xl-n3,\n  .mx-xl-n3 {\n    margin-right: -1rem !important;\n  }\n  .mb-xl-n3,\n  .my-xl-n3 {\n    margin-bottom: -1rem !important;\n  }\n  .ml-xl-n3,\n  .mx-xl-n3 {\n    margin-left: -1rem !important;\n  }\n  .m-xl-n4 {\n    margin: -1.5rem !important;\n  }\n  .mt-xl-n4,\n  .my-xl-n4 {\n    margin-top: -1.5rem !important;\n  }\n  .mr-xl-n4,\n  .mx-xl-n4 {\n    margin-right: -1.5rem !important;\n  }\n  .mb-xl-n4,\n  .my-xl-n4 {\n    margin-bottom: -1.5rem !important;\n  }\n  .ml-xl-n4,\n  .mx-xl-n4 {\n    margin-left: -1.5rem !important;\n  }\n  .m-xl-n5 {\n    margin: -3rem !important;\n  }\n  .mt-xl-n5,\n  .my-xl-n5 {\n    margin-top: -3rem !important;\n  }\n  .mr-xl-n5,\n  .mx-xl-n5 {\n    margin-right: -3rem !important;\n  }\n  .mb-xl-n5,\n  .my-xl-n5 {\n    margin-bottom: -3rem !important;\n  }\n  .ml-xl-n5,\n  .mx-xl-n5 {\n    margin-left: -3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mt-xl-auto,\n  .my-xl-auto {\n    margin-top: auto !important;\n  }\n  .mr-xl-auto,\n  .mx-xl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xl-auto,\n  .my-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ml-xl-auto,\n  .mx-xl-auto {\n    margin-left: auto !important;\n  }\n}\n\n.stretched-link::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1;\n  pointer-events: auto;\n  content: \"\";\n  background-color: rgba(0, 0, 0, 0);\n}\n\n.text-monospace {\n  font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !important;\n}\n\n.text-justify {\n  text-align: justify !important;\n}\n\n.text-wrap {\n  white-space: normal !important;\n}\n\n.text-nowrap {\n  white-space: nowrap !important;\n}\n\n.text-truncate {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.text-left {\n  text-align: left !important;\n}\n\n.text-right {\n  text-align: right !important;\n}\n\n.text-center {\n  text-align: center !important;\n}\n\n@media (min-width: 576px) {\n  .text-sm-left {\n    text-align: left !important;\n  }\n  .text-sm-right {\n    text-align: right !important;\n  }\n  .text-sm-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .text-md-left {\n    text-align: left !important;\n  }\n  .text-md-right {\n    text-align: right !important;\n  }\n  .text-md-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 992px) {\n  .text-lg-left {\n    text-align: left !important;\n  }\n  .text-lg-right {\n    text-align: right !important;\n  }\n  .text-lg-center {\n    text-align: center !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .text-xl-left {\n    text-align: left !important;\n  }\n  .text-xl-right {\n    text-align: right !important;\n  }\n  .text-xl-center {\n    text-align: center !important;\n  }\n}\n\n.text-lowercase {\n  text-transform: lowercase !important;\n}\n\n.text-uppercase {\n  text-transform: uppercase !important;\n}\n\n.text-capitalize {\n  text-transform: capitalize !important;\n}\n\n.font-weight-light {\n  font-weight: 300 !important;\n}\n\n.font-weight-lighter {\n  font-weight: lighter !important;\n}\n\n.font-weight-normal {\n  font-weight: 400 !important;\n}\n\n.font-weight-bold {\n  font-weight: 700 !important;\n}\n\n.font-weight-bolder {\n  font-weight: bolder !important;\n}\n\n.font-italic {\n  font-style: italic !important;\n}\n\n.text-white {\n  color: #fff !important;\n}\n\n.text-primary {\n  color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n  color: #0056b3 !important;\n}\n\n.text-secondary {\n  color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n  color: #494f54 !important;\n}\n\n.text-success {\n  color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n  color: #19692c !important;\n}\n\n.text-info {\n  color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n  color: #0f6674 !important;\n}\n\n.text-warning {\n  color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n  color: #ba8b00 !important;\n}\n\n.text-danger {\n  color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n  color: #a71d2a !important;\n}\n\n.text-light {\n  color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n  color: #cbd3da !important;\n}\n\n.text-dark {\n  color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n  color: #121416 !important;\n}\n\n.text-body {\n  color: #212529 !important;\n}\n\n.text-muted {\n  color: #6c757d !important;\n}\n\n.text-black-50 {\n  color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n.text-decoration-none {\n  text-decoration: none !important;\n}\n\n.text-break {\n  word-break: break-word !important;\n  word-wrap: break-word !important;\n}\n\n.text-reset {\n  color: inherit !important;\n}\n\n.visible {\n  visibility: visible !important;\n}\n\n.invisible {\n  visibility: hidden !important;\n}\n\n@media print {\n  *,\n  *::before,\n  *::after {\n    text-shadow: none !important;\n    box-shadow: none !important;\n  }\n  a:not(.btn) {\n    text-decoration: underline;\n  }\n  abbr[title]::after {\n    content: \" (\" attr(title) \")\";\n  }\n  pre {\n    white-space: pre-wrap !important;\n  }\n  pre,\n  blockquote {\n    border: 1px solid #adb5bd;\n    page-break-inside: avoid;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  @page {\n    size: a3;\n  }\n  body {\n    min-width: 992px !important;\n  }\n  .container {\n    min-width: 992px !important;\n  }\n  .navbar {\n    display: none;\n  }\n  .badge {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #dee2e6 !important;\n  }\n  .table-dark {\n    color: inherit;\n  }\n  .table-dark th,\n  .table-dark td,\n  .table-dark thead th,\n  .table-dark tbody + tbody {\n    border-color: #dee2e6;\n  }\n  .table .thead-dark th {\n    color: inherit;\n    border-color: #dee2e6;\n  }\n}\n\n@-webkit-keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@-webkit-keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@-webkit-keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@-webkit-keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@-webkit-keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.animation__shake {\n  -webkit-animation: shake 1500ms;\n  animation: shake 1500ms;\n}\n\n.animation__wobble {\n  -webkit-animation: wobble 1500ms;\n  animation: wobble 1500ms;\n}\n\n.preloader {\n  display: -ms-flexbox;\n  display: flex;\n  background-color: #f4f6f9;\n  height: 100vh;\n  width: 100%;\n  transition: height 200ms linear;\n  position: fixed;\n  left: 0;\n  top: 0;\n  z-index: 9999;\n}\n\nhtml.scroll-smooth {\n  scroll-behavior: smooth;\n}\n\nhtml,\nbody,\n.wrapper {\n  min-height: 100%;\n}\n\n.wrapper {\n  position: relative;\n}\n\n.wrapper .content-wrapper {\n  min-height: calc(100vh - calc(3.5rem + 1px) - calc(3.5rem + 1px));\n}\n\n.layout-boxed .wrapper {\n  box-shadow: 0 0 10 rgba(0, 0, 0, 0.3);\n}\n\n.layout-boxed .wrapper, .layout-boxed .wrapper::before {\n  margin: 0 auto;\n  max-width: 1250px;\n  overflow: hidden;\n}\n\n.layout-boxed .wrapper .main-sidebar {\n  left: inherit;\n}\n\n@supports not (-webkit-touch-callout: none) {\n  .layout-fixed .wrapper .sidebar {\n    height: calc(100vh - (3.5rem + 1px));\n  }\n  .layout-fixed.text-sm .wrapper .sidebar {\n    height: calc(100vh - (2.93725rem + 1px));\n  }\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n  top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse .wrapper .brand-link {\n  height: calc(3.5rem + 1px);\n  width: 4.6rem;\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse .wrapper .brand-link.text-sm,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse .wrapper .brand-link.text-sm,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse .wrapper .brand-link.text-sm {\n  height: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.sidebar-mini.sidebar-collapse.text-sm .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-md.sidebar-collapse.text-sm .wrapper .brand-link,\n.layout-navbar-fixed.sidebar-mini-xs.sidebar-collapse.text-sm .wrapper .brand-link {\n  height: calc(2.93725rem + 1px);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar {\n  margin-top: calc(calc(2.93725rem + 1px) / -1);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .control-sidebar {\n  top: 0;\n}\n\n.layout-navbar-fixed .wrapper a.anchor {\n  display: block;\n  position: relative;\n  top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n}\n\n.layout-navbar-fixed .wrapper .main-sidebar:hover .brand-link {\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n}\n\n.layout-navbar-fixed .wrapper .brand-link {\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n  z-index: 1035;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-primary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-secondary .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-success .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-info .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-warning .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-danger .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-light .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-lightblue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-lightblue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-navy .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-navy .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-olive .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-olive .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-lime .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-lime .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-fuchsia .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-fuchsia .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-maroon .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-maroon .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-blue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-blue .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-indigo .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-indigo .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-purple .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-purple .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-pink .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-pink .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-red .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-red .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-orange .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-orange .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-yellow .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-yellow .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-green .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-green .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-teal .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-teal .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-cyan .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-cyan .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-white .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-white .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-gray .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-gray .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-dark-gray-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #343a40;\n}\n\n.layout-navbar-fixed .wrapper .sidebar-light-gray-dark .brand-link:not([class*=\"navbar\"]) {\n  background-color: #fff;\n}\n\n.layout-navbar-fixed .wrapper .main-header.border-bottom-0 ~ .content-wrapper {\n  margin-top: 3.5rem;\n}\n\n.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header {\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1033;\n}\n\n.layout-navbar-fixed.text-sm .wrapper .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-not-fixed .wrapper .brand-link {\n  position: static;\n}\n\n.layout-navbar-not-fixed .wrapper .sidebar,\n.layout-navbar-not-fixed .wrapper .content-wrapper {\n  margin-top: 0;\n}\n\n.layout-navbar-not-fixed .wrapper .main-header {\n  position: static;\n}\n\n.layout-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: 0;\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n  top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n.layout-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n.layout-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .control-sidebar {\n  top: 0;\n}\n\n.layout-navbar-fixed .wrapper a.anchor {\n  display: block;\n  position: relative;\n  top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n}\n\n.layout-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n  height: calc(3.5rem + 1px);\n  transition: width 0.3s ease-in-out;\n  width: 4.6rem;\n}\n\n.text-sm .layout-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n  height: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n}\n\n.layout-navbar-fixed .wrapper .brand-link {\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  transition: width 0.3s ease-in-out;\n  width: 250px;\n  z-index: 1035;\n}\n\n.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: calc(3.5rem + 1px);\n}\n\n.text-sm .layout-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n.layout-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-fixed .wrapper .main-header {\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1037;\n}\n\n.layout-navbar-fixed.text-sm .wrapper .content-wrapper {\n  margin-top: calc(2.93725rem + 1px);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar {\n  margin-top: calc(calc(2.93725rem + 1px) / -1);\n}\n\nbody:not(.layout-fixed).layout-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n  margin-top: calc(2.93725rem + 1px);\n}\n\n.layout-navbar-not-fixed .wrapper .brand-link {\n  position: static;\n}\n\n.layout-navbar-not-fixed .wrapper .sidebar,\n.layout-navbar-not-fixed .wrapper .content-wrapper {\n  margin-top: 0;\n}\n\n.layout-navbar-not-fixed .wrapper .main-header {\n  position: static;\n}\n\n.layout-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n  margin-top: 0;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-sm-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-sm-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-sm-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-sm-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-sm-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-sm-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-sm-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-sm-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-sm-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-sm-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-sm-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-sm-navbar-not-fixed .wrapper .sidebar,\n  .layout-sm-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-sm-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-sm-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-md-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-md-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-md-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-md-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-md-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-md-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-md-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-md-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-md-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-md-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-md-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-md-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-md-navbar-not-fixed .wrapper .sidebar,\n  .layout-md-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-md-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-md-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-lg-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-lg-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-lg-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-lg-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-lg-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-lg-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-lg-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-lg-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-lg-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-lg-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-lg-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-lg-navbar-not-fixed .wrapper .sidebar,\n  .layout-lg-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-lg-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-lg-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .control-sidebar {\n    top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed.layout-fixed .wrapper .main-header ~ .control-sidebar,\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .main-header.text-sm ~ .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed.layout-fixed .wrapper .brand-link ~ .sidebar,\n  .layout-xl-navbar-fixed.layout-fixed .wrapper .brand-link.text-sm ~ .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed.text-sm .wrapper .control-sidebar {\n    top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed.layout-fixed.text-sm .wrapper .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper .control-sidebar {\n    top: 0;\n  }\n  .layout-xl-navbar-fixed .wrapper a.anchor {\n    display: block;\n    position: relative;\n    top: calc((3.5rem + 1px + (0.5rem * 2)) / -1);\n  }\n  .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link {\n    height: calc(3.5rem + 1px);\n    transition: width 0.3s ease-in-out;\n    width: 4.6rem;\n  }\n  .text-sm .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link, .layout-xl-navbar-fixed .wrapper.sidebar-collapse .brand-link.text-sm {\n    height: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper.sidebar-collapse .main-sidebar:hover .brand-link {\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n  }\n  .layout-xl-navbar-fixed .wrapper .brand-link {\n    overflow: hidden;\n    position: fixed;\n    top: 0;\n    transition: width 0.3s ease-in-out;\n    width: 250px;\n    z-index: 1035;\n  }\n  .layout-xl-navbar-fixed .wrapper .content-wrapper {\n    margin-top: calc(3.5rem + 1px);\n  }\n  .text-sm .layout-xl-navbar-fixed .wrapper .main-header ~ .content-wrapper,\n  .layout-xl-navbar-fixed .wrapper .main-header.text-sm ~ .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-fixed .wrapper .main-header {\n    left: 0;\n    position: fixed;\n    right: 0;\n    top: 0;\n    z-index: 1037;\n  }\n  .layout-xl-navbar-fixed.text-sm .wrapper .content-wrapper {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  body:not(.layout-fixed).layout-xl-navbar-fixed.text-sm .wrapper .main-sidebar {\n    margin-top: calc(calc(2.93725rem + 1px) / -1);\n  }\n  body:not(.layout-fixed).layout-xl-navbar-fixed.text-sm .wrapper .main-sidebar .sidebar {\n    margin-top: calc(2.93725rem + 1px);\n  }\n  .layout-xl-navbar-not-fixed .wrapper .brand-link {\n    position: static;\n  }\n  .layout-xl-navbar-not-fixed .wrapper .sidebar,\n  .layout-xl-navbar-not-fixed .wrapper .content-wrapper {\n    margin-top: 0;\n  }\n  .layout-xl-navbar-not-fixed .wrapper .main-header {\n    position: static;\n  }\n  .layout-xl-navbar-not-fixed.layout-fixed .wrapper .sidebar {\n    margin-top: 0;\n  }\n}\n\n.layout-footer-fixed .wrapper .control-sidebar {\n  bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .main-footer {\n  bottom: 0;\n  left: 0;\n  position: fixed;\n  right: 0;\n  z-index: 1032;\n}\n\n.layout-footer-not-fixed .wrapper .main-footer {\n  position: static;\n}\n\n.layout-footer-not-fixed .wrapper .content-wrapper {\n  margin-bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .control-sidebar {\n  bottom: 0;\n}\n\n.layout-footer-fixed .wrapper .main-footer {\n  bottom: 0;\n  left: 0;\n  position: fixed;\n  right: 0;\n  z-index: 1032;\n}\n\n.layout-footer-fixed .wrapper .content-wrapper {\n  padding-bottom: calc(3.5rem + 1px);\n}\n\n.layout-footer-not-fixed .wrapper .main-footer {\n  position: static;\n}\n\n@media (min-width: 576px) {\n  .layout-sm-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-sm-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-sm-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-sm-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 768px) {\n  .layout-md-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-md-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-md-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-md-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 992px) {\n  .layout-lg-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-lg-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-lg-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-lg-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n@media (min-width: 1200px) {\n  .layout-xl-footer-fixed .wrapper .control-sidebar {\n    bottom: 0;\n  }\n  .layout-xl-footer-fixed .wrapper .main-footer {\n    bottom: 0;\n    left: 0;\n    position: fixed;\n    right: 0;\n    z-index: 1032;\n  }\n  .layout-xl-footer-fixed .wrapper .content-wrapper {\n    padding-bottom: calc(3.5rem + 1px);\n  }\n  .layout-xl-footer-not-fixed .wrapper .main-footer {\n    position: static;\n  }\n}\n\n.layout-top-nav .wrapper {\n  margin-left: 0;\n}\n\n.layout-top-nav .wrapper .main-header .brand-image {\n  margin-top: -.5rem;\n  margin-right: .2rem;\n  height: 33px;\n}\n\n.layout-top-nav .wrapper .main-sidebar {\n  bottom: inherit;\n  height: inherit;\n}\n\n.layout-top-nav .wrapper .content-wrapper,\n.layout-top-nav .wrapper .main-header,\n.layout-top-nav .wrapper .main-footer {\n  margin-left: 0;\n}\n\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .content-wrapper, body.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .content-wrapper::before,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-footer,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-footer::before,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-header,\nbody.sidebar-collapse:not(.sidebar-mini-xs):not(.sidebar-mini-md):not(.sidebar-mini) .main-header::before {\n  margin-left: 0;\n}\n\n@media (min-width: 768px) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper, .sidebar-collapse\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer, .sidebar-collapse\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (max-width: 991.98px) {\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .content-wrapper,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-footer,\n  body:not(.sidebar-mini-md):not(.sidebar-mini-xs):not(.layout-top-nav) .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse .sidebar-mini-md .content-wrapper, .sidebar-collapse\n  .sidebar-mini-md .main-footer, .sidebar-collapse\n  .sidebar-mini-md .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .sidebar-mini-md .content-wrapper,\n  .sidebar-mini-md .main-footer,\n  .sidebar-mini-md .main-header {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    transition: margin-left 0.3s ease-in-out;\n    margin-left: 250px;\n  }\n}\n\n@media (min-width: 768px) and (prefers-reduced-motion: reduce) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    transition: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-collapse .sidebar-mini-xs .content-wrapper, .sidebar-collapse\n  .sidebar-mini-xs .main-footer, .sidebar-collapse\n  .sidebar-mini-xs .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini-xs .content-wrapper,\n  .sidebar-mini-xs .main-footer,\n  .sidebar-mini-xs .main-header {\n    margin-left: 4.6rem;\n  }\n}\n\n.content-wrapper {\n  background-color: #f4f6f9;\n}\n\n.content-wrapper > .content {\n  padding: 0 0.5rem;\n}\n\n.main-sidebar, .main-sidebar::before {\n  transition: margin-left 0.3s ease-in-out, width 0.3s ease-in-out;\n  width: 250px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .main-sidebar, .main-sidebar::before {\n    transition: none;\n  }\n}\n\n.sidebar-collapse:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-sidebar, .sidebar-collapse:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-sidebar::before {\n  box-shadow: none !important;\n}\n\n.sidebar-collapse .main-sidebar, .sidebar-collapse .main-sidebar::before {\n  margin-left: -250px;\n}\n\n.sidebar-collapse .main-sidebar .nav-sidebar.nav-child-indent .nav-treeview {\n  padding: 0;\n}\n\n@media (max-width: 767.98px) {\n  .main-sidebar, .main-sidebar::before {\n    box-shadow: none !important;\n    margin-left: -250px;\n  }\n  .sidebar-open .main-sidebar, .sidebar-open .main-sidebar::before {\n    margin-left: 0;\n  }\n}\n\nbody:not(.layout-fixed) .main-sidebar {\n  height: inherit;\n  min-height: 100%;\n  position: absolute;\n  top: 0;\n}\n\nbody:not(.layout-fixed) .main-sidebar .sidebar {\n  overflow-y: auto;\n}\n\n.layout-fixed .brand-link {\n  width: 250px;\n}\n\n.layout-fixed .main-sidebar {\n  bottom: 0;\n  float: none;\n  left: 0;\n  position: fixed;\n  top: 0;\n}\n\n.layout-fixed .control-sidebar {\n  bottom: 0;\n  float: none;\n  position: fixed;\n  top: 0;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content {\n  height: calc(100vh - calc(3.5rem + 1px));\n  overflow-y: auto;\n  scrollbar-width: thin;\n  scrollbar-color: #a9a9a9 transparent;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar {\n  width: .5rem;\n  height: .5rem;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-thumb {\n  background-color: #a9a9a9;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-track {\n  background-color: transparent;\n}\n\n.layout-fixed .control-sidebar .control-sidebar-content::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n@supports (-webkit-touch-callout: none) {\n  .layout-fixed .main-sidebar {\n    height: inherit;\n  }\n}\n\n.main-footer {\n  background-color: #fff;\n  border-top: 1px solid #dee2e6;\n  color: #869099;\n  padding: 1rem;\n}\n\n.text-sm .main-footer, .main-footer.text-sm {\n  padding: 0.812rem;\n}\n\n.content-header {\n  padding: 15px 0.5rem;\n}\n\n.text-sm .content-header {\n  padding: 10px 0.5rem;\n}\n\n.content-header h1 {\n  font-size: 1.8rem;\n  margin: 0;\n}\n\n.text-sm .content-header h1 {\n  font-size: 1.5rem;\n}\n\n.content-header .breadcrumb {\n  background-color: transparent;\n  line-height: 1.8rem;\n  margin-bottom: 0;\n  padding: 0;\n}\n\n.text-sm .content-header .breadcrumb {\n  line-height: 1.5rem;\n}\n\n.hold-transition .content-wrapper,\n.hold-transition .main-header,\n.hold-transition .main-sidebar,\n.hold-transition .main-sidebar *,\n.hold-transition .control-sidebar,\n.hold-transition .control-sidebar *,\n.hold-transition .main-footer {\n  transition: none !important;\n  -webkit-animation-duration: 0s !important;\n  animation-duration: 0s !important;\n}\n\n.main-header {\n  border-bottom: 1px solid #dee2e6;\n  z-index: 1034;\n}\n\n.main-header .nav-link {\n  height: 2.5rem;\n  position: relative;\n}\n\n.text-sm .main-header .nav-link, .main-header.text-sm .nav-link {\n  height: 1.93725rem;\n  padding: 0.35rem 1rem;\n}\n\n.text-sm .main-header .nav-link > .fa,\n.text-sm .main-header .nav-link > .fas,\n.text-sm .main-header .nav-link > .far,\n.text-sm .main-header .nav-link > .fab,\n.text-sm .main-header .nav-link > .fal,\n.text-sm .main-header .nav-link > .fad,\n.text-sm .main-header .nav-link > .svg-inline--fa,\n.text-sm .main-header .nav-link > .ion, .main-header.text-sm .nav-link > .fa,\n.main-header.text-sm .nav-link > .fas,\n.main-header.text-sm .nav-link > .far,\n.main-header.text-sm .nav-link > .fab,\n.main-header.text-sm .nav-link > .fal,\n.main-header.text-sm .nav-link > .fad,\n.main-header.text-sm .nav-link > .svg-inline--fa,\n.main-header.text-sm .nav-link > .ion {\n  font-size: 0.875rem;\n}\n\n.main-header .navbar-nav .nav-item {\n  margin: 0;\n}\n\n.main-header .navbar-nav[class*=\"-right\"] .dropdown-menu {\n  left: auto;\n  margin-top: -3px;\n  right: 0;\n}\n\n@media (max-width: 575.98px) {\n  .main-header .navbar-nav[class*=\"-right\"] .dropdown-menu {\n    left: 0;\n    right: auto;\n  }\n}\n\n.main-header.dropdown-legacy .dropdown-menu {\n  top: 3rem;\n  margin-top: 0;\n}\n\n.navbar-img {\n  height: calc(calc(3.5rem + 1px) * .5);\n  width: auto;\n}\n\n.navbar-badge {\n  font-size: .6rem;\n  font-weight: 300;\n  padding: 2px 4px;\n  position: absolute;\n  right: 5px;\n  top: 9px;\n}\n\n.btn-navbar {\n  background-color: transparent;\n  border-left-width: 0;\n}\n\n.form-control-navbar {\n  border-right-width: 0;\n}\n\n.form-control-navbar + .input-group-append {\n  margin-left: 0;\n}\n\n.form-control-navbar,\n.btn-navbar {\n  transition: none;\n}\n\n.navbar-dark .form-control-navbar,\n.navbar-dark .btn-navbar {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  color: rgba(255, 255, 255, 0.6);\n}\n\n.navbar-dark .form-control-navbar:focus,\n.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #495057;\n  border-color: #6c757d !important;\n  color: #ced4da;\n}\n\n.navbar-light .form-control-navbar,\n.navbar-light .btn-navbar {\n  background-color: #dadfe4;\n  border-color: #ced4da;\n}\n\n.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar::placeholder {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-light .form-control-navbar:focus,\n.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d3d9df;\n  border-color: #c7ced5 !important;\n  color: #ced4da;\n}\n\n.navbar-light .navbar-search-block .form-control-navbar:focus,\n.navbar-light .navbar-search-block .form-control-navbar:focus + .input-group-append .btn-navbar {\n  color: rgba(0, 0, 0, 0.6);\n}\n\n.navbar-search-block {\n  position: absolute;\n  padding: 0 1rem;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 10;\n  display: none;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  background-color: initial;\n}\n\n.navbar-search-block.navbar-search-open {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.navbar-search-block .input-group {\n  width: 100%;\n}\n\n.brand-link {\n  display: block;\n  font-size: 1.25rem;\n  line-height: 1.5;\n  padding: 0.8125rem 0.5rem;\n  transition: width 0.3s ease-in-out;\n  white-space: nowrap;\n}\n\n.brand-link:hover {\n  color: #fff;\n  text-decoration: none;\n}\n\n.text-sm .brand-link {\n  font-size: inherit;\n}\n\n[class*=\"sidebar-dark\"] .brand-link {\n  border-bottom: 1px solid #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .brand-link,\n[class*=\"sidebar-dark\"] .brand-link .pushmenu {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n[class*=\"sidebar-dark\"] .brand-link:hover,\n[class*=\"sidebar-dark\"] .brand-link .pushmenu:hover {\n  color: #fff;\n}\n\n[class*=\"sidebar-light\"] .brand-link {\n  border-bottom: 1px solid #dee2e6;\n}\n\n[class*=\"sidebar-light\"] .brand-link,\n[class*=\"sidebar-light\"] .brand-link .pushmenu {\n  color: rgba(0, 0, 0, 0.8);\n}\n\n[class*=\"sidebar-light\"] .brand-link:hover,\n[class*=\"sidebar-light\"] .brand-link .pushmenu:hover {\n  color: #000;\n}\n\n.brand-link .pushmenu {\n  margin-right: 0.5rem;\n  font-size: 1rem;\n}\n\n.brand-link .brand-link {\n  padding: 0;\n  border-bottom: none;\n}\n\n.brand-link .brand-image {\n  float: left;\n  line-height: .8;\n  margin-left: .8rem;\n  margin-right: .5rem;\n  margin-top: -3px;\n  max-height: 33px;\n  width: auto;\n}\n\n.brand-link .brand-image-xs {\n  float: left;\n  line-height: .8;\n  margin-top: -.1rem;\n  max-height: 33px;\n  width: auto;\n}\n\n.brand-link .brand-image-xl {\n  line-height: .8;\n  max-height: 40px;\n  width: auto;\n}\n\n.brand-link .brand-image-xl.single {\n  margin-top: -.3rem;\n}\n\n.brand-link.text-sm .brand-image,\n.text-sm .brand-link .brand-image {\n  height: 29px;\n  margin-bottom: -.25rem;\n  margin-left: .95rem;\n  margin-top: -.25rem;\n}\n\n.brand-link.text-sm .brand-image-xs,\n.text-sm .brand-link .brand-image-xs {\n  margin-top: -.2rem;\n  max-height: 29px;\n}\n\n.brand-link.text-sm .brand-image-xl,\n.text-sm .brand-link .brand-image-xl {\n  margin-top: -.225rem;\n  max-height: 38px;\n}\n\n.main-sidebar {\n  height: 100vh;\n  overflow-y: hidden;\n  z-index: 1038;\n}\n\n.main-sidebar a:-moz-focusring {\n  border: 0;\n  outline: none;\n}\n\n.sidebar {\n  height: calc(100% - (3.5rem + 1px));\n  overflow-x: hidden;\n  overflow-y: initial;\n  padding-bottom: 0;\n  padding-left: 0.5rem;\n  padding-right: 0.5rem;\n  padding-top: 0;\n  scrollbar-color: #a9a9a9 transparent;\n  scrollbar-width: none;\n}\n\n.sidebar::-webkit-scrollbar-thumb {\n  background-color: #a9a9a9;\n}\n\n.sidebar::-webkit-scrollbar-track {\n  background-color: transparent;\n}\n\n.sidebar::-webkit-scrollbar-corner {\n  background-color: transparent;\n}\n\n.sidebar::-webkit-scrollbar {\n  width: 0;\n}\n\n.sidebar:hover {\n  scrollbar-width: thin;\n}\n\n.sidebar:hover::-webkit-scrollbar {\n  width: .5rem;\n  height: .5rem;\n}\n\n.brand-link.border-bottom-0 ~ .sidebar {\n  height: calc(100% - 3.5rem);\n}\n\n.user-panel {\n  position: relative;\n}\n\n[class*=\"sidebar-dark\"] .user-panel {\n  border-bottom: 1px solid #4f5962;\n}\n\n[class*=\"sidebar-light\"] .user-panel {\n  border-bottom: 1px solid #dee2e6;\n}\n\n.user-panel,\n.user-panel .info {\n  overflow: hidden;\n  white-space: nowrap;\n}\n\n.user-panel .image {\n  display: inline-block;\n  padding-left: 0.8rem;\n}\n\n.user-panel img {\n  height: auto;\n  width: 2.1rem;\n}\n\n.user-panel .info {\n  display: inline-block;\n  padding: 5px 5px 5px 10px;\n}\n\n.user-panel .status,\n.user-panel .dropdown-menu {\n  font-size: 0.875rem;\n}\n\n.nav-sidebar .nav-item > .nav-link {\n  margin-bottom: .2rem;\n}\n\n.nav-sidebar .nav-item > .nav-link .right {\n  transition: -webkit-transform ease-in-out 0.3s;\n  transition: transform ease-in-out 0.3s;\n  transition: transform ease-in-out 0.3s, -webkit-transform ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .nav-sidebar .nav-item > .nav-link .right {\n    transition: none;\n  }\n}\n\n.nav-sidebar .nav-link > .right,\n.nav-sidebar .nav-link > p > .right {\n  position: absolute;\n  right: 1rem;\n  top: .7rem;\n}\n\n.nav-sidebar .nav-link > .right i,\n.nav-sidebar .nav-link > .right span,\n.nav-sidebar .nav-link > p > .right i,\n.nav-sidebar .nav-link > p > .right span {\n  margin-left: .5rem;\n}\n\n.nav-sidebar .nav-link > .right:nth-child(2),\n.nav-sidebar .nav-link > p > .right:nth-child(2) {\n  right: 2.2rem;\n}\n\n.nav-sidebar .menu-open > .nav-treeview {\n  display: block;\n}\n\n.nav-sidebar .menu-open > .nav-link svg.right,\n.nav-sidebar .menu-open > .nav-link i.right,\n.nav-sidebar .menu-is-opening > .nav-link svg.right,\n.nav-sidebar .menu-is-opening > .nav-link i.right {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n.nav-sidebar > .nav-item {\n  margin-bottom: 0;\n}\n\n.nav-sidebar > .nav-item .nav-icon {\n  margin-left: .05rem;\n  font-size: 1.2rem;\n  margin-right: .2rem;\n  text-align: center;\n  width: 1.6rem;\n}\n\n.nav-sidebar > .nav-item .nav-icon.fa, .nav-sidebar > .nav-item .nav-icon.fas, .nav-sidebar > .nav-item .nav-icon.far, .nav-sidebar > .nav-item .nav-icon.fab, .nav-sidebar > .nav-item .nav-icon.fal, .nav-sidebar > .nav-item .nav-icon.fad, .nav-sidebar > .nav-item .nav-icon.svg-inline--fa, .nav-sidebar > .nav-item .nav-icon.ion {\n  font-size: 1.1rem;\n}\n\n.nav-sidebar > .nav-item .float-right {\n  margin-top: 3px;\n}\n\n.nav-sidebar .nav-treeview {\n  display: none;\n  list-style: none;\n  padding: 0;\n}\n\n.nav-sidebar .nav-treeview > .nav-item > .nav-link > .nav-icon {\n  width: 1.6rem;\n}\n\n.nav-sidebar.nav-child-indent .nav-treeview {\n  transition: padding 0.3s ease-in-out;\n  padding-left: 1rem;\n}\n\n.text-sm .nav-sidebar.nav-child-indent .nav-treeview {\n  padding-left: .5rem;\n}\n\n.nav-sidebar.nav-child-indent.nav-legacy .nav-treeview .nav-treeview {\n  padding-left: 2rem;\n  margin-left: -1rem;\n}\n\n.text-sm .nav-sidebar.nav-child-indent.nav-legacy .nav-treeview .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.nav-sidebar .nav-header {\n  font-size: .9rem;\n  padding: 0.5rem 0.75rem;\n}\n\n.nav-sidebar .nav-link p {\n  display: inline;\n  margin: 0;\n  white-space: normal;\n}\n\n.sidebar-is-opening .sidebar .nav-sidebar .nav-link p {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n#sidebar-overlay {\n  background-color: rgba(0, 0, 0, 0.1);\n  bottom: 0;\n  display: none;\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1037;\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-open #sidebar-overlay {\n    display: block;\n  }\n}\n\n[class*=\"sidebar-light-\"] {\n  background-color: #fff;\n}\n\n[class*=\"sidebar-light-\"] .user-panel a:hover {\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .status {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .status:hover, [class*=\"sidebar-light-\"] .user-panel .status:focus, [class*=\"sidebar-light-\"] .user-panel .status:active {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .user-panel .dropdown-menu {\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .user-panel .dropdown-item {\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link:active, [class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link:focus {\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item.menu-open > .nav-link,\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item:hover > .nav-link {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-link.active {\n  color: #000;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n\n[class*=\"sidebar-light-\"] .nav-sidebar > .nav-item > .nav-treeview {\n  background-color: transparent;\n}\n\n[class*=\"sidebar-light-\"] .nav-header {\n  background-color: inherit;\n  color: #292d32;\n}\n\n[class*=\"sidebar-light-\"] .sidebar a {\n  color: #343a40;\n}\n\n[class*=\"sidebar-light-\"] .sidebar a:hover {\n  text-decoration: none;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link {\n  color: #777;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:hover, [class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:focus {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #000;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link.active:hover {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: #212529;\n}\n\n[class*=\"sidebar-light-\"] .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview .nav-treeview {\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link, [class*=\"sidebar-light-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link.active {\n  border-color: rgba(0, 0, 0, 0.1);\n}\n\n[class*=\"sidebar-dark-\"] {\n  background-color: #343a40;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel a:hover {\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .status {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .status:hover, [class*=\"sidebar-dark-\"] .user-panel .status:focus, [class*=\"sidebar-dark-\"] .user-panel .status:active {\n  background-color: rgba(247, 247, 247, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .dropdown-menu {\n  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);\n  border-color: rgba(242, 242, 242, 0.1);\n}\n\n[class*=\"sidebar-dark-\"] .user-panel .dropdown-item {\n  color: #212529;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link:active {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item.menu-open > .nav-link,\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item:hover > .nav-link,\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link:focus {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-link.active {\n  color: #fff;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n\n[class*=\"sidebar-dark-\"] .nav-sidebar > .nav-item > .nav-treeview {\n  background-color: transparent;\n}\n\n[class*=\"sidebar-dark-\"] .nav-header {\n  background-color: inherit;\n  color: #d0d4db;\n}\n\n[class*=\"sidebar-dark-\"] .sidebar a {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .sidebar a:hover, [class*=\"sidebar-dark-\"] .sidebar a:focus {\n  text-decoration: none;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link {\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link:hover, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link:focus {\n  background-color: rgba(255, 255, 255, 0.1);\n  color: #fff;\n}\n\n[class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active:hover, [class*=\"sidebar-dark-\"] .nav-treeview > .nav-item > .nav-link.active:focus {\n  background-color: rgba(255, 255, 255, 0.9);\n  color: #343a40;\n}\n\n[class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview .nav-treeview {\n  border-color: rgba(255, 255, 255, 0.9);\n}\n\n[class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link, [class*=\"sidebar-dark-\"] .nav-flat .nav-item .nav-treeview > .nav-item > .nav-link.active {\n  border-color: rgba(255, 255, 255, 0.9);\n}\n\n.sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.sidebar-dark-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-primary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #007bff;\n}\n\n.sidebar-dark-secondary .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-secondary .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.sidebar-dark-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-secondary .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.sidebar-dark-success .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-success .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.sidebar-dark-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-success .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #28a745;\n}\n\n.sidebar-dark-info .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-info .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.sidebar-dark-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-info .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #17a2b8;\n}\n\n.sidebar-dark-warning .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-warning .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-warning .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ffc107;\n}\n\n.sidebar-dark-danger .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-danger .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.sidebar-dark-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-danger .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #dc3545;\n}\n\n.sidebar-dark-light .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-light .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-light .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f8f9fa;\n}\n\n.sidebar-dark-dark .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.sidebar-dark-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.sidebar-dark-lightblue .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-lightblue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.sidebar-dark-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-lightblue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3c8dbc;\n}\n\n.sidebar-dark-navy .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-navy .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.sidebar-dark-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-navy .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #001f3f;\n}\n\n.sidebar-dark-olive .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-olive .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.sidebar-dark-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-olive .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #3d9970;\n}\n\n.sidebar-dark-lime .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-lime .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-lime .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #01ff70;\n}\n\n.sidebar-dark-fuchsia .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-fuchsia .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.sidebar-dark-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-fuchsia .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #f012be;\n}\n\n.sidebar-dark-maroon .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-maroon .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.sidebar-dark-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-maroon .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #d81b60;\n}\n\n.sidebar-dark-blue .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-blue .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.sidebar-dark-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-blue .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #007bff;\n}\n\n.sidebar-dark-indigo .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-indigo .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.sidebar-dark-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-indigo .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6610f2;\n}\n\n.sidebar-dark-purple .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-purple .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.sidebar-dark-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-purple .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6f42c1;\n}\n\n.sidebar-dark-pink .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-pink .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.sidebar-dark-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-pink .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #e83e8c;\n}\n\n.sidebar-dark-red .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-red .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.sidebar-dark-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-red .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #dc3545;\n}\n\n.sidebar-dark-orange .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-orange .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-orange .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fd7e14;\n}\n\n.sidebar-dark-yellow .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-yellow .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-yellow .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #ffc107;\n}\n\n.sidebar-dark-green .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-green .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.sidebar-dark-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-green .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #28a745;\n}\n\n.sidebar-dark-teal .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-teal .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.sidebar-dark-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-teal .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #20c997;\n}\n\n.sidebar-dark-cyan .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-cyan .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.sidebar-dark-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-cyan .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #17a2b8;\n}\n\n.sidebar-dark-white .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-white .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.sidebar-dark-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-white .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #fff;\n}\n\n.sidebar-dark-gray .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-gray .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.sidebar-dark-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-gray .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #6c757d;\n}\n\n.sidebar-dark-gray-dark .nav-sidebar > .nav-item > .nav-link.active,\n.sidebar-light-gray-dark .nav-sidebar > .nav-item > .nav-link.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.sidebar-dark-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active,\n.sidebar-light-gray-dark .nav-sidebar.nav-legacy > .nav-item > .nav-link.active {\n  border-color: #343a40;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand) .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-compact.nav-sidebar.nav-child-indent:not(.nav-flat) .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.nav-flat {\n  margin: -0.25rem -0.5rem 0;\n}\n\n.nav-flat .nav-item > .nav-link {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.nav-flat .nav-item > .nav-link > .nav-icon {\n  margin-left: .55rem;\n}\n\n.nav-flat:not(.nav-child-indent) .nav-treeview .nav-item > .nav-link > .nav-icon {\n  margin-left: .4rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview {\n  padding-left: 0;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-icon {\n  margin-left: .85rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview {\n  border-left: .2rem solid;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.15rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.45rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.75rem;\n}\n\n.nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 2.05rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-icon {\n  margin-left: .55rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-link {\n  padding-left: calc(1rem - .2rem);\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-icon {\n  margin-left: .35rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: .15rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: -.15rem;\n}\n\n.sidebar-collapse .nav-flat.nav-child-indent .sidebar .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: -.35rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-compact.nav-sidebar .nav-treeview .nav-icon {\n  margin-left: .4rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-icon {\n  margin-left: .85rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.15rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.45rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 1.75rem;\n}\n\n.sidebar-mini .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar:not(.sidebar-no-expand):hover .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon,\n.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-sidebar.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-icon {\n  margin-left: 2.05rem;\n}\n\n.nav-flat .nav-icon {\n  transition: margin-left ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .nav-flat .nav-icon {\n    transition: none;\n  }\n}\n\n.nav-flat .nav-treeview .nav-icon {\n  margin-left: -.2rem;\n}\n\n.nav-flat.nav-sidebar > .nav-item .nav-treeview,\n.nav-flat.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n.nav-flat.nav-sidebar > .nav-item .nav-treeview .nav-item > .nav-link,\n.nav-flat.nav-sidebar > .nav-item > .nav-treeview .nav-item > .nav-link {\n  border-left: .2rem solid;\n}\n\n.nav-legacy {\n  margin: -0.25rem -0.5rem 0;\n}\n\n.nav-legacy.nav-sidebar .nav-item > .nav-link {\n  border-radius: 0;\n  margin-bottom: 0;\n}\n\n.nav-legacy.nav-sidebar .nav-item > .nav-link > .nav-icon {\n  margin-left: .55rem;\n}\n\n.text-sm .nav-legacy.nav-sidebar .nav-item > .nav-link > .nav-icon {\n  margin-left: .75rem;\n}\n\n.nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  background-color: inherit;\n  border-left: 3px solid transparent;\n  box-shadow: none;\n}\n\n.nav-legacy.nav-sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.55rem - 3px);\n}\n\n.text-sm .nav-legacy.nav-sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n.text-sm .nav-legacy.nav-sidebar.nav-flat .nav-treeview .nav-item > .nav-link > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n.sidebar-mini .nav-legacy > .nav-item .nav-link .nav-icon,\n.sidebar-mini-md .nav-legacy > .nav-item .nav-link .nav-icon,\n.sidebar-mini-xs .nav-legacy > .nav-item .nav-link .nav-icon {\n  transition: margin-left ease-in-out 0.3s;\n  margin-left: .6rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar-mini .nav-legacy > .nav-item .nav-link .nav-icon,\n  .sidebar-mini-md .nav-legacy > .nav-item .nav-link .nav-icon,\n  .sidebar-mini-xs .nav-legacy > .nav-item .nav-link .nav-icon {\n    transition: none;\n  }\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview {\n  padding-left: 1rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 2rem;\n  margin-left: -1rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview {\n  padding-left: .5rem;\n}\n\n.main-sidebar.sidebar-focused .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover .nav-legacy .sidebar-mini.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-md.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview, .main-sidebar.sidebar-focused\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview,\n.main-sidebar:hover\n.nav-legacy .sidebar-mini-xs.sidebar-collapse.text-sm.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 1rem;\n  margin-left: -.5rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon {\n  margin-left: .55rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: .36rem;\n}\n\n.sidebar-mini.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview,\n.sidebar-mini-md.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview,\n.sidebar-mini-xs.sidebar-collapse .nav-legacy .sidebar.nav-child-indent .nav-treeview .nav-treeview {\n  padding-left: 0;\n  margin-left: 0;\n}\n\n.sidebar-mini.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-md.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon,\n.sidebar-mini-xs.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link .nav-icon {\n  margin-left: .75rem;\n}\n\n.sidebar-mini.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-md.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon,\n.sidebar-mini-xs.sidebar-collapse.text-sm .nav-legacy .sidebar > .nav-item > .nav-link.active > .nav-icon {\n  margin-left: calc(.75rem - 3px);\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item .nav-treeview,\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(255, 255, 255, 0.05);\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  color: #fff;\n}\n\n[class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:focus, [class*=\"sidebar-dark\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: transparent;\n  color: #fff;\n}\n\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item .nav-treeview,\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item > .nav-treeview {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n[class*=\"sidebar-light\"] .nav-legacy.nav-sidebar > .nav-item > .nav-link.active {\n  color: #000;\n}\n\n[class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link.active, [class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:focus, [class*=\"sidebar-light\"] .nav-legacy .nav-treeview > .nav-item > .nav-link:hover {\n  background-color: transparent;\n  color: #000;\n}\n\n.nav-collapse-hide-child .menu-open > .nav-treeview {\n  max-height: -webkit-min-content;\n  max-height: -moz-min-content;\n  max-height: min-content;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.sidebar-collapse .sidebar:not(:hover) .nav-collapse-hide-child .menu-open > .nav-treeview {\n  max-height: 0;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.main-sidebar.sidebar-focused .nav-collapse-hide-child .sidebar-mini.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover .nav-collapse-hide-child .sidebar-mini.sidebar-collapse .menu-open > .nav-treeview, .main-sidebar.sidebar-focused\n.nav-collapse-hide-child .sidebar-mini-md.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover\n.nav-collapse-hide-child .sidebar-mini-md.sidebar-collapse .menu-open > .nav-treeview, .main-sidebar.sidebar-focused\n.nav-collapse-hide-child .sidebar-mini-xs.sidebar-collapse .menu-open > .nav-treeview,\n.main-sidebar:not(.sidebar-no-expand):hover\n.nav-collapse-hide-child .sidebar-mini-xs.sidebar-collapse .menu-open > .nav-treeview {\n  max-height: -webkit-min-content;\n  max-height: -moz-min-content;\n  max-height: min-content;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.nav-compact .nav-link,\n.nav-compact .nav-header {\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n}\n\n.nav-compact .nav-header:not(:first-of-type) {\n  padding-top: 0.75rem;\n  padding-bottom: 0.25rem;\n}\n\n.nav-compact .nav-link > .right,\n.nav-compact .nav-link > p > .right {\n  top: .465rem;\n}\n\n.text-sm .nav-compact .nav-link > .right,\n.text-sm .nav-compact .nav-link > p > .right {\n  top: .7rem;\n}\n\n[class*=\"sidebar-dark\"] .form-control-sidebar,\n[class*=\"sidebar-dark\"] .btn-sidebar {\n  background-color: #3f474e;\n  border: 1px solid #56606a;\n  color: white;\n}\n\n[class*=\"sidebar-dark\"] .form-control-sidebar:focus,\n[class*=\"sidebar-dark\"] .btn-sidebar:focus {\n  border: 1px solid #7a8793;\n}\n\n[class*=\"sidebar-dark\"] .btn-sidebar:hover {\n  background-color: #454d55;\n}\n\n[class*=\"sidebar-dark\"] .btn-sidebar:focus {\n  background-color: #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item {\n  background-color: #454d55;\n  border-color: #56606a;\n  color: #c2c7d0;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item:hover {\n  background-color: #4b545c;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item:focus {\n  background-color: #515a63;\n}\n\n[class*=\"sidebar-dark\"] .list-group-item .search-path {\n  color: #adb5bd;\n}\n\n[class*=\"sidebar-light\"] .form-control-sidebar,\n[class*=\"sidebar-light\"] .btn-sidebar {\n  background-color: #f2f2f2;\n  border: 1px solid #d9d9d9;\n  color: #1f2d3d;\n}\n\n[class*=\"sidebar-light\"] .form-control-sidebar:focus,\n[class*=\"sidebar-light\"] .btn-sidebar:focus {\n  border: 1px solid #b3b3b3;\n}\n\n[class*=\"sidebar-light\"] .btn-sidebar:hover {\n  background-color: #ececec;\n}\n\n[class*=\"sidebar-light\"] .btn-sidebar:focus {\n  background-color: #e6e6e6;\n}\n\n[class*=\"sidebar-light\"] .list-group-item {\n  border-color: #d9d9d9;\n}\n\n[class*=\"sidebar-light\"] .list-group-item:hover {\n  background-color: #ececec;\n}\n\n[class*=\"sidebar-light\"] .list-group-item:focus {\n  background-color: #e6e6e6;\n}\n\n[class*=\"sidebar-light\"] .list-group-item .search-path {\n  color: #6c757d;\n}\n\n.sidebar .form-inline .input-group {\n  width: 100%;\n  -ms-flex-wrap: nowrap;\n  flex-wrap: nowrap;\n}\n\n.sidebar nav .form-inline {\n  margin-bottom: .2rem;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs).sidebar-collapse .main-sidebar {\n  margin-left: 0;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .content-wrapper,\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-header,\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .main-footer {\n  z-index: 9999;\n  position: relative;\n}\n\n.layout-boxed:not(.sidebar-mini):not(.sidebar-mini-md):not(.sidebar-mini-xs) .control-sidebar {\n  z-index: 9999;\n}\n\n.sidebar-collapse .form-control-sidebar,\n.sidebar-collapse .form-control-sidebar ~ .input-group-append,\n.sidebar-collapse .sidebar-search-results {\n  display: none;\n}\n\n[data-widget=\"sidebar-search\"] input[type=\"search\"]::-ms-clear, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-ms-reveal {\n  display: none;\n  width: 0;\n  height: 0;\n}\n\n[data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-cancel-button, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-decoration, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-results-button, [data-widget=\"sidebar-search\"] input[type=\"search\"]::-webkit-search-results-decoration {\n  display: none;\n}\n\n.sidebar-search-results {\n  position: relative;\n  display: none;\n  width: 100%;\n}\n\n.sidebar-search-open .sidebar-search-results {\n  display: inline-block;\n}\n\n.sidebar-search-results .search-title {\n  margin-bottom: -.1rem;\n}\n\n.sidebar-search-results .list-group {\n  position: absolute;\n  width: 100%;\n  z-index: 1039;\n}\n\n.sidebar-search-results .list-group > .list-group-item {\n  padding: 0.375rem 0.75rem;\n}\n\n.sidebar-search-results .list-group > .list-group-item:-moz-focusring {\n  margin-top: 0;\n  border-left: 1px solid transparent;\n  border-top: 0;\n  border-bottom: 1px solid transparent;\n}\n\n.sidebar-search-results .list-group > .list-group-item:first-child {\n  margin-top: 0;\n  border-top: 0;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.sidebar-search-results .search-path {\n  font-size: 80%;\n}\n\n.sidebar-search-open .btn,\n.sidebar-search-open .form-control {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n[class*=\"sidebar-dark\"] .sidebar-custom {\n  border-top: 1px solid #4f5962;\n}\n\n[class*=\"sidebar-light\"] .sidebar-custom {\n  border-top: 1px solid #dee2e6;\n}\n\n.layout-fixed.sidebar-collapse .hide-on-collapse {\n  display: none;\n}\n\n.layout-fixed.sidebar-collapse:hover .hide-on-collapse {\n  display: block;\n}\n\n.layout-fixed.text-sm .main-sidebar-custom .sidebar {\n  height: calc(100% - ((2.93725rem + 3.8rem) + 1px));\n}\n\n.layout-fixed.text-sm .main-sidebar-custom .sidebar-custom {\n  height: 3.8rem;\n  padding: 0.85rem 0.5rem;\n}\n\n.layout-fixed .main-sidebar-custom {\n  height: -webkit-fill-available;\n  height: -moz-available;\n  height: -ms-stretch;\n  height: stretch;\n}\n\n.layout-fixed .main-sidebar-custom .sidebar {\n  height: calc(100% - ((3.5rem + 4rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom .sidebar-custom {\n  height: 4rem;\n  padding: 0.85rem 0.5rem;\n}\n\n.layout-fixed .main-sidebar-custom-lg .sidebar {\n  height: calc(100% - ((3.5rem + 6rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom-lg .sidebar-custom {\n  height: 6rem;\n}\n\n.layout-fixed .main-sidebar-custom-xl .sidebar {\n  height: calc(100% - ((3.5rem + 8rem) + 1px));\n}\n\n.layout-fixed .main-sidebar-custom-xl .sidebar-custom {\n  height: 8rem;\n}\n\n.layout-fixed .main-sidebar-custom .pos-right,\n.layout-fixed .main-sidebar-custom-lg .pos-right,\n.layout-fixed .main-sidebar-custom-xl .pos-right {\n  position: absolute;\n  right: .5rem;\n}\n\n.sidebar-hidden .main-sidebar,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .main-sidebar {\n  display: none !important;\n}\n\n.sidebar-hidden .content-wrapper,\n.sidebar-hidden .main-header,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .content-wrapper,\n.sidebar-hidden.sidebar-mini.sidebar-collapse .main-header {\n  margin-left: 0 !important;\n}\n\n.logo-xs,\n.logo-xl {\n  opacity: 1;\n  position: absolute;\n  visibility: visible;\n}\n\n.logo-xs.brand-image-xs,\n.logo-xl.brand-image-xs {\n  left: 18px;\n  top: 12px;\n}\n\n.logo-xs.brand-image-xl,\n.logo-xl.brand-image-xl {\n  left: 12px;\n  top: 6px;\n}\n\n.logo-xs {\n  opacity: 0;\n  visibility: hidden;\n}\n\n.logo-xs.brand-image-xl {\n  left: 16px;\n  top: 8px;\n}\n\n.brand-link.logo-switch::before {\n  content: \"\\00a0\";\n}\n\n@media (min-width: 992px) {\n  .sidebar-mini .nav-sidebar,\n  .sidebar-mini .nav-sidebar > .nav-header,\n  .sidebar-mini .nav-sidebar .nav-link {\n    white-space: nowrap;\n  }\n  .sidebar-mini.sidebar-collapse .d-hidden-mini {\n    display: none;\n  }\n  .sidebar-mini.sidebar-collapse .content-wrapper,\n  .sidebar-mini.sidebar-collapse .main-footer,\n  .sidebar-mini.sidebar-collapse .main-header {\n    margin-left: 4.6rem !important;\n  }\n  .sidebar-mini.sidebar-collapse .nav-sidebar .nav-header {\n    display: none;\n  }\n  .sidebar-mini.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n    width: 0;\n    white-space: nowrap;\n  }\n  .sidebar-mini.sidebar-collapse .sidebar .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .brand-text {\n    margin-left: -10px;\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .logo-xl {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .logo-xs {\n    display: inline-block;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar {\n    overflow-x: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar, .sidebar-mini.sidebar-collapse .main-sidebar::before {\n    margin-left: 0;\n    width: 4.6rem;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar .user-panel .image {\n    float: none;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n    text-align: left;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n    float: left;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n    display: inline-block;\n    margin-left: 0;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n    margin-right: .5rem;\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n    display: block !important;\n    -webkit-transform: translateZ(0);\n    transform: translateZ(0);\n  }\n  .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n    display: inline-block !important;\n  }\n  .sidebar-mini.sidebar-collapse .visible-sidebar-mini {\n    display: block !important;\n  }\n  .sidebar-mini.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini.sidebar-collapse.layout-fixed .brand-link {\n    width: 4.6rem;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .sidebar-mini.sidebar-collapse .main-sidebar {\n    box-shadow: none !important;\n  }\n}\n\n@media (min-width: 768px) {\n  .sidebar-mini-md .nav-sidebar,\n  .sidebar-mini-md .nav-sidebar > .nav-header,\n  .sidebar-mini-md .nav-sidebar .nav-link {\n    white-space: nowrap;\n  }\n  .sidebar-mini-md.sidebar-collapse .d-hidden-mini {\n    display: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .content-wrapper,\n  .sidebar-mini-md.sidebar-collapse .main-footer,\n  .sidebar-mini-md.sidebar-collapse .main-header {\n    margin-left: 4.6rem !important;\n  }\n  .sidebar-mini-md.sidebar-collapse .nav-sidebar .nav-header {\n    display: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n    width: 0;\n    white-space: nowrap;\n  }\n  .sidebar-mini-md.sidebar-collapse .sidebar .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .brand-text {\n    margin-left: -10px;\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .logo-xl {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .logo-xs {\n    display: inline-block;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar {\n    overflow-x: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar, .sidebar-mini-md.sidebar-collapse .main-sidebar::before {\n    margin-left: 0;\n    width: 4.6rem;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar .user-panel .image {\n    float: none;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n    text-align: left;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n    float: left;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n    display: inline-block;\n    margin-left: 0;\n    -webkit-animation-name: fadeIn;\n    animation-name: fadeIn;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: visible;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n    -webkit-animation-name: fadeOut;\n    animation-name: fadeOut;\n    -webkit-animation-duration: 0.3s;\n    animation-duration: 0.3s;\n    -webkit-animation-fill-mode: both;\n    animation-fill-mode: both;\n    visibility: hidden;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n    margin-right: .5rem;\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n    display: block !important;\n    -webkit-transform: translateZ(0);\n    transform: translateZ(0);\n  }\n  .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini-md.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n    display: inline-block !important;\n  }\n  .sidebar-mini-md.sidebar-collapse .visible-sidebar-mini {\n    display: block !important;\n  }\n  .sidebar-mini-md.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n    width: 250px;\n  }\n  .sidebar-mini-md.sidebar-collapse.layout-fixed .brand-link {\n    width: 4.6rem;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .sidebar-mini-md.sidebar-collapse .main-sidebar {\n    box-shadow: none !important;\n  }\n}\n\n.sidebar-mini-xs .nav-sidebar,\n.sidebar-mini-xs .nav-sidebar > .nav-header,\n.sidebar-mini-xs .nav-sidebar .nav-link {\n  white-space: nowrap;\n}\n\n.sidebar-mini-xs.sidebar-collapse .d-hidden-mini {\n  display: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .content-wrapper,\n.sidebar-mini-xs.sidebar-collapse .main-footer,\n.sidebar-mini-xs.sidebar-collapse .main-header {\n  margin-left: 4.6rem !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse .nav-sidebar .nav-header {\n  display: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .sidebar .nav-sidebar .nav-link p {\n  width: 0;\n  white-space: nowrap;\n}\n\n.sidebar-mini-xs.sidebar-collapse .sidebar .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .brand-text {\n  margin-left: -10px;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .logo-xl {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .logo-xs {\n  display: inline-block;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar {\n  overflow-x: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar, .sidebar-mini-xs.sidebar-collapse .main-sidebar::before {\n  margin-left: 0;\n  width: 4.6rem;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar .user-panel .image {\n  float: none;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-link, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-link {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel {\n  text-align: left;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel .image, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel .image {\n  float: left;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-text,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xl, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-text,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xl {\n  display: inline-block;\n  margin-left: 0;\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .logo-xs, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .logo-xs {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .brand-image, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .brand-image {\n  margin-right: .5rem;\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .sidebar-form,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .user-panel > .info, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .sidebar-form,\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .user-panel > .info {\n  display: block !important;\n  -webkit-transform: translateZ(0);\n  transform: translateZ(0);\n}\n\n.sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand):hover .nav-sidebar > .nav-item > .nav-link > span, .sidebar-mini-xs.sidebar-collapse .main-sidebar:not(.sidebar-no-expand).sidebar-focused .nav-sidebar > .nav-item > .nav-link > span {\n  display: inline-block !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse .visible-sidebar-mini {\n  display: block !important;\n}\n\n.sidebar-mini-xs.sidebar-collapse.layout-fixed .main-sidebar:hover .brand-link {\n  width: 250px;\n}\n\n.sidebar-mini-xs.sidebar-collapse.layout-fixed .brand-link {\n  width: 4.6rem;\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 3rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 4rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 3rem);\n}\n\n.sidebar-mini .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 4rem);\n}\n\n.sidebar-mini .main-sidebar .nav-flat .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 2);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 3);\n}\n\n.sidebar-mini .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 4);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - .5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 1.5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 0.5rem * 2 - 2.5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-link {\n  width: 250px;\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - .5rem);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 2);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 3);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 4);\n}\n\n.sidebar-mini .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-md .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 5);\n}\n\n.sidebar-mini .main-sidebar .nav-link,\n.sidebar-mini-md .main-sidebar .nav-link,\n.sidebar-mini-xs .main-sidebar .nav-link {\n  width: calc(250px - 0.5rem * 2);\n  transition: width ease-in-out 0.3s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar-mini .main-sidebar .nav-link,\n  .sidebar-mini-md .main-sidebar .nav-link,\n  .sidebar-mini-xs .main-sidebar .nav-link {\n    transition: none;\n  }\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar .sidebar-search-results {\n  display: none;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar .nav-link {\n  width: 3.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-legacy .nav-link {\n  width: 4.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-treeview {\n  padding-left: 0 !important;\n  margin-left: 0 !important;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar .nav-sidebar.nav-child-indent.nav-compact .nav-link {\n  width: calc(4.6rem - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.hide-nav-header-on-hover) .nav-header, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.hide-nav-header-on-hover) .nav-header {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .form-control-sidebar ~ .input-group-append {\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover:not(.sidebar-no-expand) .sidebar-search-open .sidebar-search-results {\n  display: inline-block;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px - 1rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 1rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 2rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 3rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-legacy.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - 1rem - 4rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-link {\n  width: calc(250px);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 3);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-flat.nav-child-indent .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .2rem * 4);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-compact .nav-link {\n  width: calc(250px - 0.5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-link {\n  width: 250px;\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-link {\n  width: calc(250px - .5rem);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 2);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 3);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 4);\n}\n\n.sidebar-collapse.sidebar-mini .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-md .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar.sidebar-focused .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link, .sidebar-collapse.sidebar-mini-xs .main-sidebar:hover .nav-child-indent.nav-legacy.nav-compact .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-treeview .nav-link {\n  width: calc(250px - .5rem * 5);\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover {\n  width: 4.6rem;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .nav-header, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .nav-header, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .nav-header,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .nav-header {\n  display: none;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .brand-link, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .brand-link, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .brand-link,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .brand-link {\n  width: 4.6rem !important;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .user-panel .image, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .user-panel .image, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .user-panel .image,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .user-panel .image {\n  float: none !important;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .logo-xs, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .logo-xs, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xs,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .logo-xs {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .logo-xl, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .logo-xl, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .logo-xl,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .logo-xl {\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .nav-sidebar.nav-child-indent .nav-treeview,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .nav-sidebar.nav-child-indent .nav-treeview {\n  padding-left: 0;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar .nav-link p,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .brand-text,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .user-panel > .info,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar .nav-link p {\n  margin-left: -10px;\n  -webkit-animation-name: fadeOut;\n  animation-name: fadeOut;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: hidden;\n  display: inline-block;\n  width: 0;\n}\n\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon, .sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini-md .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon, .sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar.sidebar-focused .sidebar .nav-sidebar > .nav-item .nav-icon,\n.sidebar-collapse.sidebar-mini-xs .sidebar-no-expand.main-sidebar:hover .sidebar .nav-sidebar > .nav-item .nav-icon {\n  margin-right: 0;\n}\n\n.nav-sidebar {\n  position: relative;\n}\n\n.nav-sidebar:hover {\n  overflow: visible;\n}\n\n.sidebar-form,\n.nav-sidebar > .nav-header {\n  overflow: hidden;\n  text-overflow: clip;\n}\n\n.nav-sidebar .nav-item > .nav-link {\n  position: relative;\n}\n\n.nav-sidebar .nav-item > .nav-link > .float-right {\n  margin-top: -7px;\n  position: absolute;\n  right: 10px;\n  top: 50%;\n}\n\n.sidebar .nav-link p,\n.main-sidebar .brand-text,\n.main-sidebar .logo-xs,\n.main-sidebar .logo-xl,\n.sidebar .user-panel .info {\n  transition: margin-left 0.3s linear, opacity 0.3s ease, visibility 0.3s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sidebar .nav-link p,\n  .main-sidebar .brand-text,\n  .main-sidebar .logo-xs,\n  .main-sidebar .logo-xl,\n  .sidebar .user-panel .info {\n    transition: none;\n  }\n}\n\nhtml.control-sidebar-animate {\n  overflow-x: hidden;\n}\n\n.control-sidebar {\n  bottom: calc(3.5rem + 1px);\n  position: absolute;\n  top: calc(3.5rem + 1px);\n  z-index: 1031;\n}\n\n.control-sidebar, .control-sidebar::before {\n  bottom: calc(3.5rem + 1px);\n  display: none;\n  right: -250px;\n  width: 250px;\n  transition: right 0.3s ease-in-out, display 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar, .control-sidebar::before {\n    transition: none;\n  }\n}\n\n.control-sidebar::before {\n  content: \"\";\n  display: block;\n  position: fixed;\n  top: 0;\n  z-index: -1;\n}\n\nbody.text-sm .control-sidebar {\n  bottom: calc(2.9365rem + 1px);\n  top: calc(2.93725rem + 1px);\n}\n\n.main-header.text-sm ~ .control-sidebar {\n  top: calc(2.93725rem + 1px);\n}\n\n.main-footer.text-sm ~ .control-sidebar {\n  bottom: calc(2.9365rem + 1px);\n}\n\n.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-push-slide .main-footer {\n  transition: margin-right 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar-push-slide .content-wrapper,\n  .control-sidebar-push-slide .main-footer {\n    transition: none;\n  }\n}\n\n.control-sidebar-open .control-sidebar {\n  display: block !important;\n}\n\n.control-sidebar-open .control-sidebar, .control-sidebar-open .control-sidebar::before {\n  right: 0;\n}\n\n.control-sidebar-open.control-sidebar-push .content-wrapper,\n.control-sidebar-open.control-sidebar-push .main-footer, .control-sidebar-open.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-open.control-sidebar-push-slide .main-footer {\n  margin-right: 250px;\n}\n\n.control-sidebar-slide-open .control-sidebar {\n  display: block;\n}\n\n.control-sidebar-slide-open .control-sidebar, .control-sidebar-slide-open .control-sidebar::before {\n  right: 0;\n  transition: right 0.3s ease-in-out, display 0.3s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .control-sidebar-slide-open .control-sidebar, .control-sidebar-slide-open .control-sidebar::before {\n    transition: none;\n  }\n}\n\n.control-sidebar-slide-open.control-sidebar-push .content-wrapper,\n.control-sidebar-slide-open.control-sidebar-push .main-footer, .control-sidebar-slide-open.control-sidebar-push-slide .content-wrapper,\n.control-sidebar-slide-open.control-sidebar-push-slide .main-footer {\n  margin-right: 250px;\n}\n\n.control-sidebar-dark {\n  background-color: #343a40;\n}\n\n.control-sidebar-dark,\n.control-sidebar-dark a,\n.control-sidebar-dark .nav-link {\n  color: #c2c7d0;\n}\n\n.control-sidebar-dark a:hover {\n  color: #fff;\n}\n\n.control-sidebar-dark h1,\n.control-sidebar-dark h2,\n.control-sidebar-dark h3,\n.control-sidebar-dark h4,\n.control-sidebar-dark h5,\n.control-sidebar-dark h6,\n.control-sidebar-dark label {\n  color: #fff;\n}\n\n.control-sidebar-dark .nav-tabs {\n  background-color: rgba(255, 255, 255, 0.1);\n  border-bottom: 0;\n  margin-bottom: 5px;\n}\n\n.control-sidebar-dark .nav-tabs .nav-item {\n  margin: 0;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link {\n  border-radius: 0;\n  padding: 10px 20px;\n  position: relative;\n  text-align: center;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link, .control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active {\n  border: 0;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link:hover, .control-sidebar-dark .nav-tabs .nav-link:active, .control-sidebar-dark .nav-tabs .nav-link:focus, .control-sidebar-dark .nav-tabs .nav-link.active {\n  border-bottom-color: transparent;\n  border-left-color: transparent;\n  border-top-color: transparent;\n  color: #fff;\n}\n\n.control-sidebar-dark .nav-tabs .nav-link.active {\n  background-color: #343a40;\n}\n\n.control-sidebar-dark .tab-pane {\n  padding: 10px 15px;\n}\n\n.control-sidebar-light {\n  color: #4b545c;\n  background-color: #fff;\n  border-left: 1px solid #dee2e6;\n}\n\n.text-sm .dropdown-menu {\n  font-size: 0.875rem !important;\n}\n\n.text-sm .dropdown-toggle::after {\n  vertical-align: .2rem;\n}\n\n.dropdown-item-title {\n  font-size: 1rem;\n  margin: 0;\n}\n\n.dropdown-icon::after {\n  margin-left: 0;\n}\n\n.dropdown-menu-lg {\n  max-width: 300px;\n  min-width: 280px;\n  padding: 0;\n}\n\n.dropdown-menu-lg .dropdown-divider {\n  margin: 0;\n}\n\n.dropdown-menu-lg .dropdown-item {\n  padding: 0.5rem 1rem;\n}\n\n.dropdown-menu-lg p {\n  margin: 0;\n  white-space: normal;\n}\n\n.dropdown-submenu {\n  position: relative;\n}\n\n.dropdown-submenu > a::after {\n  border-top: 0.3em solid transparent;\n  border-right: 0;\n  border-bottom: 0.3em solid transparent;\n  border-left: 0.3em solid;\n  float: right;\n  margin-left: .5rem;\n  margin-top: .5rem;\n}\n\n.dropdown-submenu > .dropdown-menu {\n  left: 100%;\n  margin-left: 0;\n  margin-top: 0;\n  top: 0;\n}\n\n.dropdown-hover:hover > .dropdown-menu, .dropdown-hover.nav-item.dropdown:hover > .dropdown-menu,\n.dropdown-hover .dropdown-submenu:hover > .dropdown-menu, .dropdown-hover.dropdown-submenu:hover > .dropdown-menu {\n  display: block;\n}\n\n.dropdown-menu-xl {\n  max-width: 420px;\n  min-width: 360px;\n  padding: 0;\n}\n\n.dropdown-menu-xl .dropdown-divider {\n  margin: 0;\n}\n\n.dropdown-menu-xl .dropdown-item {\n  padding: 0.5rem 1rem;\n}\n\n.dropdown-menu-xl p {\n  margin: 0;\n  white-space: normal;\n}\n\n.dropdown-footer,\n.dropdown-header {\n  display: block;\n  font-size: 0.875rem;\n  padding: 0.5rem 1rem;\n  text-align: center;\n}\n\n.open:not(.dropup) > .animated-dropdown-menu {\n  -webkit-animation: flipInX 0.7s both;\n  animation: flipInX 0.7s both;\n  -webkit-backface-visibility: visible !important;\n  backface-visibility: visible !important;\n}\n\n.navbar-custom-menu > .navbar-nav > li {\n  position: relative;\n}\n\n.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {\n  position: absolute;\n  right: 0;\n  left: auto;\n}\n\n@media (max-width: 767.98px) {\n  .navbar-custom-menu > .navbar-nav {\n    float: right;\n  }\n  .navbar-custom-menu > .navbar-nav > li {\n    position: static;\n  }\n  .navbar-custom-menu > .navbar-nav > li > .dropdown-menu {\n    position: absolute;\n    right: 5%;\n    left: auto;\n    border: 1px solid #ddd;\n    background-color: #fff;\n  }\n}\n\n.navbar-nav > .user-menu > .nav-link::after {\n  content: none;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  padding: 0;\n  width: 280px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu,\n.navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header {\n  height: 175px;\n  padding: 10px;\n  text-align: center;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > img {\n  z-index: 5;\n  height: 90px;\n  width: 90px;\n  border: 3px solid;\n  border-color: transparent;\n  border-color: rgba(255, 255, 255, 0.2);\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p {\n  z-index: 5;\n  font-size: 17px;\n  margin-top: 10px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p > small {\n  display: block;\n  font-size: 12px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-body {\n  border-bottom: 1px solid #495057;\n  border-top: 1px solid #dee2e6;\n  padding: 15px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-body::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu > .dropdown-menu > .user-body a {\n    background-color: #fff !important;\n    color: #495057 !important;\n  }\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer {\n  background-color: #f8f9fa;\n  padding: 10px;\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default {\n  color: #6c757d;\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:hover {\n    background-color: #f8f9fa;\n  }\n}\n\n.navbar-nav > .user-menu .user-image {\n  border-radius: 50%;\n  float: left;\n  height: 2.1rem;\n  margin-right: 10px;\n  margin-top: -2px;\n  width: 2.1rem;\n}\n\n@media (min-width: 576px) {\n  .navbar-nav > .user-menu .user-image {\n    float: none;\n    line-height: 10px;\n    margin-right: .4rem;\n    margin-top: -8px;\n  }\n}\n\n.nav-pills .nav-link {\n  color: #6c757d;\n}\n\n.nav-pills .nav-link:not(.active):hover {\n  color: #007bff;\n}\n\n.nav-pills .nav-item.dropdown.show .nav-link:hover {\n  color: #fff;\n}\n\n.nav-tabs.flex-column {\n  border-bottom: 0;\n  border-right: 1px solid #dee2e6;\n}\n\n.nav-tabs.flex-column .nav-link {\n  border-bottom-left-radius: 0.25rem;\n  border-top-right-radius: 0;\n  margin-right: -1px;\n}\n\n.nav-tabs.flex-column .nav-link:hover, .nav-tabs.flex-column .nav-link:focus {\n  border-color: #e9ecef transparent #e9ecef #e9ecef;\n}\n\n.nav-tabs.flex-column .nav-link.active,\n.nav-tabs.flex-column .nav-item.show .nav-link {\n  border-color: #dee2e6 transparent #dee2e6 #dee2e6;\n}\n\n.nav-tabs.flex-column.nav-tabs-right {\n  border-left: 1px solid #dee2e6;\n  border-right: 0;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link {\n  border-bottom-left-radius: 0;\n  border-bottom-right-radius: 0.25rem;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0.25rem;\n  margin-left: -1px;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link:hover, .nav-tabs.flex-column.nav-tabs-right .nav-link:focus {\n  border-color: #e9ecef #e9ecef #e9ecef transparent;\n}\n\n.nav-tabs.flex-column.nav-tabs-right .nav-link.active,\n.nav-tabs.flex-column.nav-tabs-right .nav-item.show .nav-link {\n  border-color: #dee2e6 #dee2e6 #dee2e6 transparent;\n}\n\n.navbar-no-expand {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-no-expand .nav-link {\n  padding-left: 1rem;\n  padding-right: 1rem;\n}\n\n.navbar-no-expand .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-light {\n  background-color: #f8f9fa;\n}\n\n.navbar-dark {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.navbar-primary {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar,\n.navbar-primary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #0071eb;\n  border-color: #0065d1;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-primary.navbar-light .form-control-navbar:focus,\n.navbar-primary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #006fe6;\n  border-color: #0065d1 !important;\n  color: #343a40;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar,\n.navbar-primary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1486ff;\n  border-color: #2e93ff;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-primary.navbar-dark .form-control-navbar:focus,\n.navbar-primary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1a88ff;\n  border-color: #2e93ff !important;\n  color: #fff;\n}\n\n.navbar-secondary {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar,\n.navbar-secondary.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-light .form-control-navbar:focus,\n.navbar-secondary.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar,\n.navbar-secondary.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-secondary.navbar-dark .form-control-navbar:focus,\n.navbar-secondary.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.navbar-success {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.navbar-success.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar,\n.navbar-success.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #24973e;\n  border-color: #1f8236;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-success.navbar-light .form-control-navbar:focus,\n.navbar-success.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #23923d;\n  border-color: #1f8236 !important;\n  color: #343a40;\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar,\n.navbar-success.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2cb74c;\n  border-color: #31cc54;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-success.navbar-dark .form-control-navbar:focus,\n.navbar-success.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #2dbc4e;\n  border-color: #31cc54 !important;\n  color: #fff;\n}\n\n.navbar-info {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.navbar-info.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar,\n.navbar-info.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1592a6;\n  border-color: #127e8f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-info.navbar-light .form-control-navbar:focus,\n.navbar-info.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #148ea1;\n  border-color: #127e8f !important;\n  color: #343a40;\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar,\n.navbar-info.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #19b2ca;\n  border-color: #1cc6e1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-info.navbar-dark .form-control-navbar:focus,\n.navbar-info.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1ab6cf;\n  border-color: #1cc6e1 !important;\n  color: #fff;\n}\n\n.navbar-warning {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar,\n.navbar-warning.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f2b500;\n  border-color: #d8a200;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-warning.navbar-light .form-control-navbar:focus,\n.navbar-warning.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #edb100;\n  border-color: #d8a200 !important;\n  color: #343a40;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar,\n.navbar-warning.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ffc61b;\n  border-color: #ffcc35;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-warning.navbar-dark .form-control-navbar:focus,\n.navbar-warning.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ffc721;\n  border-color: #ffcc35 !important;\n  color: #fff;\n}\n\n.navbar-danger {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar,\n.navbar-danger.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #d72536;\n  border-color: #c22231;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-danger.navbar-light .form-control-navbar:focus,\n.navbar-danger.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d32535;\n  border-color: #c22231 !important;\n  color: #343a40;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar,\n.navbar-danger.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df4655;\n  border-color: #e35c69;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-danger.navbar-dark .form-control-navbar:focus,\n.navbar-danger.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e04b59;\n  border-color: #e35c69 !important;\n  color: #fff;\n}\n\n.navbar-lightblue {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar,\n.navbar-lightblue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3781ad;\n  border-color: #317399;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-light .form-control-navbar:focus,\n.navbar-lightblue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #367fa9;\n  border-color: #317399 !important;\n  color: #343a40;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar,\n.navbar-lightblue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #4897c5;\n  border-color: #5ba2cb;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-lightblue.navbar-dark .form-control-navbar:focus,\n.navbar-lightblue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #4c99c6;\n  border-color: #5ba2cb !important;\n  color: #fff;\n}\n\n.navbar-navy {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar,\n.navbar-navy.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00152b;\n  border-color: #000811;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-navy.navbar-light .form-control-navbar:focus,\n.navbar-navy.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #001226;\n  border-color: #000811 !important;\n  color: #343a40;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar,\n.navbar-navy.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #002953;\n  border-color: #00366d;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-navy.navbar-dark .form-control-navbar:focus,\n.navbar-navy.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #002c59;\n  border-color: #00366d !important;\n  color: #fff;\n}\n\n.navbar-olive {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar,\n.navbar-olive.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #378a65;\n  border-color: #307858;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-olive.navbar-light .form-control-navbar:focus,\n.navbar-olive.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #368763;\n  border-color: #307858 !important;\n  color: #343a40;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar,\n.navbar-olive.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #43a87b;\n  border-color: #4cb888;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-olive.navbar-dark .form-control-navbar:focus,\n.navbar-olive.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #44ab7d;\n  border-color: #4cb888 !important;\n  color: #fff;\n}\n\n.navbar-lime {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar,\n.navbar-lime.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #00ec67;\n  border-color: #00d25c;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-lime.navbar-light .form-control-navbar:focus,\n.navbar-lime.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #00e765;\n  border-color: #00d25c !important;\n  color: #343a40;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar,\n.navbar-lime.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #15ff7b;\n  border-color: #2fff8a;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-lime.navbar-dark .form-control-navbar:focus,\n.navbar-lime.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1bff7e;\n  border-color: #2fff8a !important;\n  color: #fff;\n}\n\n.navbar-fuchsia {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar,\n.navbar-fuchsia.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df0eb0;\n  border-color: #c70d9d;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-light .form-control-navbar:focus,\n.navbar-fuchsia.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #db0ead;\n  border-color: #c70d9d !important;\n  color: #343a40;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar,\n.navbar-fuchsia.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f125c3;\n  border-color: #f33dca;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus,\n.navbar-fuchsia.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f22ac5;\n  border-color: #f33dca !important;\n  color: #fff;\n}\n\n.navbar-maroon {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar,\n.navbar-maroon.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #c61958;\n  border-color: #af164e;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-light .form-control-navbar:focus,\n.navbar-maroon.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #c11856;\n  border-color: #af164e !important;\n  color: #343a40;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar,\n.navbar-maroon.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e4246a;\n  border-color: #e63a79;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-maroon.navbar-dark .form-control-navbar:focus,\n.navbar-maroon.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e4286d;\n  border-color: #e63a79 !important;\n  color: #fff;\n}\n\n.navbar-blue {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar,\n.navbar-blue.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #0071eb;\n  border-color: #0065d1;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-blue.navbar-light .form-control-navbar:focus,\n.navbar-blue.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #006fe6;\n  border-color: #0065d1 !important;\n  color: #343a40;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar,\n.navbar-blue.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1486ff;\n  border-color: #2e93ff;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-blue.navbar-dark .form-control-navbar:focus,\n.navbar-blue.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1a88ff;\n  border-color: #2e93ff !important;\n  color: #fff;\n}\n\n.navbar-indigo {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar,\n.navbar-indigo.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #5d0ce1;\n  border-color: #530bc9;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-light .form-control-navbar:focus,\n.navbar-indigo.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #5b0cdd;\n  border-color: #530bc9 !important;\n  color: #343a40;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar,\n.navbar-indigo.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7223f3;\n  border-color: #823cf4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-indigo.navbar-dark .form-control-navbar:focus,\n.navbar-indigo.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7528f3;\n  border-color: #823cf4 !important;\n  color: #fff;\n}\n\n.navbar-purple {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar,\n.navbar-purple.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #663bb4;\n  border-color: #5b35a0;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-purple.navbar-light .form-control-navbar:focus,\n.navbar-purple.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #643ab0;\n  border-color: #5b35a0 !important;\n  color: #343a40;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar,\n.navbar-purple.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #7b51c6;\n  border-color: #8965cc;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-purple.navbar-dark .form-control-navbar:focus,\n.navbar-purple.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #7e55c7;\n  border-color: #8965cc !important;\n  color: #fff;\n}\n\n.navbar-pink {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar,\n.navbar-pink.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #e62c81;\n  border-color: #de1a74;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-pink.navbar-light .form-control-navbar:focus,\n.navbar-pink.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e5277e;\n  border-color: #de1a74 !important;\n  color: #343a40;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar,\n.navbar-pink.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ea5097;\n  border-color: #ed67a4;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-pink.navbar-dark .form-control-navbar:focus,\n.navbar-pink.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #eb559a;\n  border-color: #ed67a4 !important;\n  color: #fff;\n}\n\n.navbar-red {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.navbar-red.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar,\n.navbar-red.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #d72536;\n  border-color: #c22231;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-red.navbar-light .form-control-navbar:focus,\n.navbar-red.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #d32535;\n  border-color: #c22231 !important;\n  color: #343a40;\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar,\n.navbar-red.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #df4655;\n  border-color: #e35c69;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-red.navbar-dark .form-control-navbar:focus,\n.navbar-red.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #e04b59;\n  border-color: #e35c69 !important;\n  color: #fff;\n}\n\n.navbar-orange {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar,\n.navbar-orange.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fa7302;\n  border-color: #e16702;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-orange.navbar-light .form-control-navbar:focus,\n.navbar-orange.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f57102;\n  border-color: #e16702 !important;\n  color: #343a40;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar,\n.navbar-orange.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #fd8928;\n  border-color: #fd9742;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-orange.navbar-dark .form-control-navbar:focus,\n.navbar-orange.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #fd8c2d;\n  border-color: #fd9742 !important;\n  color: #fff;\n}\n\n.navbar-yellow {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar,\n.navbar-yellow.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #f2b500;\n  border-color: #d8a200;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-light .form-control-navbar:focus,\n.navbar-yellow.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #edb100;\n  border-color: #d8a200 !important;\n  color: #343a40;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar,\n.navbar-yellow.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #ffc61b;\n  border-color: #ffcc35;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-yellow.navbar-dark .form-control-navbar:focus,\n.navbar-yellow.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #ffc721;\n  border-color: #ffcc35 !important;\n  color: #fff;\n}\n\n.navbar-green {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.navbar-green.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar,\n.navbar-green.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #24973e;\n  border-color: #1f8236;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-green.navbar-light .form-control-navbar:focus,\n.navbar-green.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #23923d;\n  border-color: #1f8236 !important;\n  color: #343a40;\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar,\n.navbar-green.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2cb74c;\n  border-color: #31cc54;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-green.navbar-dark .form-control-navbar:focus,\n.navbar-green.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #2dbc4e;\n  border-color: #31cc54 !important;\n  color: #fff;\n}\n\n.navbar-teal {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar,\n.navbar-teal.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1db78a;\n  border-color: #1aa179;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-teal.navbar-light .form-control-navbar:focus,\n.navbar-teal.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1cb386;\n  border-color: #1aa179 !important;\n  color: #343a40;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar,\n.navbar-teal.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #23dba4;\n  border-color: #38dfae;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-teal.navbar-dark .form-control-navbar:focus,\n.navbar-teal.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #26dca6;\n  border-color: #38dfae !important;\n  color: #fff;\n}\n\n.navbar-cyan {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar,\n.navbar-cyan.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #1592a6;\n  border-color: #127e8f;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-light .form-control-navbar:focus,\n.navbar-cyan.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #148ea1;\n  border-color: #127e8f !important;\n  color: #343a40;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar,\n.navbar-cyan.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #19b2ca;\n  border-color: #1cc6e1;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-cyan.navbar-dark .form-control-navbar:focus,\n.navbar-cyan.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #1ab6cf;\n  border-color: #1cc6e1 !important;\n  color: #fff;\n}\n\n.navbar-white {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.navbar-white.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar,\n.navbar-white.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: whitesmoke;\n  border-color: #e8e8e8;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-white.navbar-light .form-control-navbar:focus,\n.navbar-white.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #f2f2f2;\n  border-color: #e8e8e8 !important;\n  color: #343a40;\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar,\n.navbar-white.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: white;\n  border-color: white;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-white.navbar-dark .form-control-navbar:focus,\n.navbar-white.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: white;\n  border-color: white !important;\n  color: #fff;\n}\n\n.navbar-gray {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar,\n.navbar-gray.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #636b72;\n  border-color: #575e64;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-gray.navbar-light .form-control-navbar:focus,\n.navbar-gray.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #60686f;\n  border-color: #575e64 !important;\n  color: #343a40;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar,\n.navbar-gray.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #757f88;\n  border-color: #838c94;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-gray.navbar-dark .form-control-navbar:focus,\n.navbar-gray.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #78828a;\n  border-color: #838c94 !important;\n  color: #fff;\n}\n\n.navbar-gray-dark {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-moz-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::-ms-input-placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar::placeholder {\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar,\n.navbar-gray-dark.navbar-light .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #2b3035;\n  border-color: #1f2327;\n  color: rgba(52, 58, 64, 0.8);\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-moz-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus:-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::-ms-input-placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus::placeholder {\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-light .form-control-navbar:focus,\n.navbar-gray-dark.navbar-light .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #292d32;\n  border-color: #1f2327 !important;\n  color: #343a40;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-webkit-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-moz-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::-ms-input-placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar::placeholder {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar,\n.navbar-gray-dark.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3d444b;\n  border-color: #495159;\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-webkit-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-moz-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus:-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::-ms-input-placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus::placeholder {\n  color: #fff;\n}\n\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus,\n.navbar-gray-dark.navbar-dark .form-control-navbar:focus + .input-group-append .btn-navbar {\n  background-color: #3f474e;\n  border-color: #495159 !important;\n  color: #fff;\n}\n\n.navbar-nav-not-expanded {\n  -ms-flex-direction: row;\n  flex-direction: row;\n}\n\n.navbar-nav-not-expanded .dropdown-menu {\n  position: absolute;\n}\n\n.navbar-nav-not-expanded .nav-link {\n  padding-right: 1rem;\n  padding-left: 1rem;\n}\n\n.pagination-month .page-item {\n  justify-self: stretch;\n}\n\n.pagination-month .page-item .page-link {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: center;\n  align-items: center;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  box-shadow: none;\n}\n\n.pagination-month .page-item:first-child .page-link, .pagination-month .page-item:last-child .page-link {\n  height: 100%;\n  font-size: 1.25rem;\n}\n\n.pagination-month .page-item .page-month {\n  margin-bottom: 0;\n  font-size: 1.25rem;\n  font-weight: 700;\n}\n\n.pagination-month .page-item .page-year {\n  margin-bottom: 0;\n}\n\n.pagination-month.pagination-lg .page-month {\n  font-size: 1.5625rem;\n}\n\n.pagination-month.pagination-sm .page-month {\n  font-size: 1rem;\n}\n\n.form-group.has-icon {\n  position: relative;\n}\n\n.form-group.has-icon .form-control {\n  padding-right: 35px;\n}\n\n.form-group.has-icon .form-icon {\n  background-color: transparent;\n  border: 0;\n  cursor: pointer;\n  font-size: 1rem;\n  padding: 0.375rem 0.75rem;\n  position: absolute;\n  right: 3px;\n  top: 0;\n}\n\n.btn-group-vertical .btn.btn-flat:first-of-type, .btn-group-vertical .btn.btn-flat:last-of-type {\n  border-radius: 0;\n}\n\n.form-control-feedback.fa, .form-control-feedback.fas, .form-control-feedback.far, .form-control-feedback.fab, .form-control-feedback.fal, .form-control-feedback.fad, .form-control-feedback.svg-inline--fa, .form-control-feedback.ion {\n  line-height: calc(2.25rem + 2px);\n}\n\n.input-lg + .form-control-feedback.fa, .input-lg + .form-control-feedback.fas, .input-lg + .form-control-feedback.far, .input-lg + .form-control-feedback.fab, .input-lg + .form-control-feedback.fal, .input-lg + .form-control-feedback.fad, .input-lg + .form-control-feedback.svg-inline--fa, .input-lg + .form-control-feedback.ion,\n.input-group-lg + .form-control-feedback.fa,\n.input-group-lg + .form-control-feedback.fas,\n.input-group-lg + .form-control-feedback.far,\n.input-group-lg + .form-control-feedback.fab,\n.input-group-lg + .form-control-feedback.fal,\n.input-group-lg + .form-control-feedback.fad,\n.input-group-lg + .form-control-feedback.svg-inline--fa,\n.input-group-lg + .form-control-feedback.ion {\n  line-height: calc(2.875rem + 2px);\n}\n\n.form-group-lg .form-control + .form-control-feedback.fa, .form-group-lg .form-control + .form-control-feedback.fas, .form-group-lg .form-control + .form-control-feedback.far, .form-group-lg .form-control + .form-control-feedback.fab, .form-group-lg .form-control + .form-control-feedback.fal, .form-group-lg .form-control + .form-control-feedback.fad, .form-group-lg .form-control + .form-control-feedback.svg-inline--fa, .form-group-lg .form-control + .form-control-feedback.ion {\n  line-height: calc(2.875rem + 2px);\n}\n\n.input-sm + .form-control-feedback.fa, .input-sm + .form-control-feedback.fas, .input-sm + .form-control-feedback.far, .input-sm + .form-control-feedback.fab, .input-sm + .form-control-feedback.fal, .input-sm + .form-control-feedback.fad, .input-sm + .form-control-feedback.svg-inline--fa, .input-sm + .form-control-feedback.ion,\n.input-group-sm + .form-control-feedback.fa,\n.input-group-sm + .form-control-feedback.fas,\n.input-group-sm + .form-control-feedback.far,\n.input-group-sm + .form-control-feedback.fab,\n.input-group-sm + .form-control-feedback.fal,\n.input-group-sm + .form-control-feedback.fad,\n.input-group-sm + .form-control-feedback.svg-inline--fa,\n.input-group-sm + .form-control-feedback.ion {\n  line-height: calc(1.8125rem + 2px);\n}\n\n.form-group-sm .form-control + .form-control-feedback.fa, .form-group-sm .form-control + .form-control-feedback.fas, .form-group-sm .form-control + .form-control-feedback.far, .form-group-sm .form-control + .form-control-feedback.fab, .form-group-sm .form-control + .form-control-feedback.fal, .form-group-sm .form-control + .form-control-feedback.fad, .form-group-sm .form-control + .form-control-feedback.svg-inline--fa, .form-group-sm .form-control + .form-control-feedback.ion {\n  line-height: calc(1.8125rem + 2px);\n}\n\nlabel:not(.form-check-label):not(.custom-file-label) {\n  font-weight: 700;\n}\n\n.warning-feedback {\n  font-size: 80%;\n  color: #ffc107;\n  display: none;\n  margin-top: 0.25rem;\n  width: 100%;\n}\n\n.warning-tooltip {\n  border-radius: 0.25rem;\n  font-size: 0.875rem;\n  background-color: rgba(255, 193, 7, 0.9);\n  color: #1f2d3d;\n  display: none;\n  line-height: 1.5;\n  margin-top: .1rem;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  position: absolute;\n  top: 100%;\n  z-index: 5;\n}\n\n.form-control.is-warning {\n  border-color: #ffc107;\n}\n\n.form-control.is-warning:focus {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.form-control.is-warning ~ .warning-feedback,\n.form-control.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\ntextarea.form-control.is-warning {\n  padding-right: 2.25rem;\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.custom-select.is-warning {\n  border-color: #ffc107;\n}\n\n.custom-select.is-warning:focus {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.custom-select.is-warning ~ .warning-feedback,\n.custom-select.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.form-control-file.is-warning ~ .warning-feedback,\n.form-control-file.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.form-check-input.is-warning ~ .form-check-label {\n  color: #ffc107;\n}\n\n.form-check-input.is-warning ~ .warning-feedback,\n.form-check-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-control-input.is-warning ~ .custom-control-label {\n  color: #ffc107;\n}\n\n.custom-control-input.is-warning ~ .custom-control-label::before {\n  border-color: #ffc107;\n}\n\n.custom-control-input.is-warning ~ .warning-feedback,\n.custom-control-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-control-input.is-warning:checked ~ .custom-control-label::before {\n  background-color: #ffce3a;\n  border-color: #ffce3a;\n}\n\n.custom-control-input.is-warning:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input.is-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffc107;\n}\n\n.custom-file-input.is-warning ~ .custom-file-label {\n  border-color: #ffc107;\n}\n\n.custom-file-input.is-warning ~ .warning-feedback,\n.custom-file-input.is-warning ~ .warning-tooltip {\n  display: block;\n}\n\n.custom-file-input.is-warning:focus ~ .custom-file-label {\n  border-color: #ffc107;\n  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.25);\n}\n\nbody.text-sm .input-group-text {\n  font-size: 0.875rem;\n}\n\n.form-control.form-control-border,\n.custom-select.form-control-border {\n  border-top: 0;\n  border-left: 0;\n  border-right: 0;\n  border-radius: 0;\n  box-shadow: inherit;\n}\n\n.form-control.form-control-border.border-width-2,\n.custom-select.form-control-border.border-width-2 {\n  border-bottom-width: 2px;\n}\n\n.form-control.form-control-border.border-width-3,\n.custom-select.form-control-border.border-width-3 {\n  border-bottom-width: 3px;\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-primary .custom-control-input ~ .custom-control-label::after {\n  background-color: #003e80;\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99caff;\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-off-secondary .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-on-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-success .custom-control-input ~ .custom-control-label::after {\n  background-color: #0f401b;\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #86e29b;\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-off-info .custom-control-input ~ .custom-control-label::after {\n  background-color: #093e47;\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-on-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7adeee;\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-off-warning .custom-control-input ~ .custom-control-label::after {\n  background-color: #876500;\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-on-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #ffe7a0;\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-danger .custom-control-input ~ .custom-control-label::after {\n  background-color: #7c151f;\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f3b7bd;\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-switch.custom-switch-off-light .custom-control-input ~ .custom-control-label::after {\n  background-color: #aeb9c5;\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-switch.custom-switch-on-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-off-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-on-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-switch.custom-switch-off-lightblue .custom-control-input ~ .custom-control-label::after {\n  background-color: #1d455b;\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-switch.custom-switch-on-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #acd0e5;\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-switch.custom-switch-off-navy .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-switch.custom-switch-on-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #006ad8;\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-switch.custom-switch-off-olive .custom-control-input ~ .custom-control-label::after {\n  background-color: #193e2d;\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-switch.custom-switch-on-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99d6bb;\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-switch.custom-switch-off-lime .custom-control-input ~ .custom-control-label::after {\n  background-color: #008138;\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-switch.custom-switch-on-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #9affc6;\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-switch.custom-switch-off-fuchsia .custom-control-input ~ .custom-control-label::after {\n  background-color: #7b0861;\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-switch.custom-switch-on-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f9a2e5;\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-switch.custom-switch-off-maroon .custom-control-input ~ .custom-control-label::after {\n  background-color: #670d2e;\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-switch.custom-switch-on-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f29aba;\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-blue .custom-control-input ~ .custom-control-label::after {\n  background-color: #003e80;\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #99caff;\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-switch.custom-switch-off-indigo .custom-control-input ~ .custom-control-label::after {\n  background-color: #33077c;\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-switch.custom-switch-on-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c3a1fa;\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-switch.custom-switch-off-purple .custom-control-input ~ .custom-control-label::after {\n  background-color: #382063;\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-switch.custom-switch-on-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #c7b5e7;\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-switch.custom-switch-off-pink .custom-control-input ~ .custom-control-label::after {\n  background-color: #95124e;\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-switch.custom-switch-on-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f8c7dd;\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-red .custom-control-input ~ .custom-control-label::after {\n  background-color: #7c151f;\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #f3b7bd;\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-switch.custom-switch-off-orange .custom-control-input ~ .custom-control-label::after {\n  background-color: #904201;\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-switch.custom-switch-on-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #fed1ac;\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-off-yellow .custom-control-input ~ .custom-control-label::after {\n  background-color: #876500;\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-switch.custom-switch-on-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #ffe7a0;\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-off-green .custom-control-input ~ .custom-control-label::after {\n  background-color: #0f401b;\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-switch.custom-switch-on-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #86e29b;\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-switch.custom-switch-off-teal .custom-control-input ~ .custom-control-label::after {\n  background-color: #0e5b44;\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-switch.custom-switch-on-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #94eed3;\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-off-cyan .custom-control-input ~ .custom-control-label::after {\n  background-color: #093e47;\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-switch.custom-switch-on-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7adeee;\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-switch.custom-switch-off-white .custom-control-input ~ .custom-control-label::after {\n  background-color: #bfbfbf;\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-switch.custom-switch-on-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: white;\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-off-gray .custom-control-input ~ .custom-control-label::after {\n  background-color: #313539;\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-switch.custom-switch-on-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #bcc1c6;\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-off-gray-dark .custom-control-input ~ .custom-control-label::after {\n  background-color: black;\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked:focus ~ .custom-control-label::before {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-switch.custom-switch-on-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-color: #7a8793;\n}\n\n.custom-range.custom-range-primary:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-primary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-primary::-webkit-slider-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-primary::-moz-range-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-primary::-ms-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-primary::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-secondary:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-secondary:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-secondary::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-secondary::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-secondary::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-secondary::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-success:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-success:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-success::-webkit-slider-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-webkit-slider-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-success::-moz-range-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-moz-range-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-success::-ms-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-success::-ms-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-info:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-info:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-info::-webkit-slider-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-webkit-slider-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-info::-moz-range-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-moz-range-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-info::-ms-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-info::-ms-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-warning:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-warning:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-warning::-webkit-slider-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-webkit-slider-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-warning::-moz-range-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-moz-range-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-warning::-ms-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-warning::-ms-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-danger:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-danger:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-danger::-webkit-slider-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-webkit-slider-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-danger::-moz-range-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-moz-range-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-danger::-ms-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-danger::-ms-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-light:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-light:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(248, 249, 250, 0.25);\n}\n\n.custom-range.custom-range-light::-webkit-slider-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-light::-moz-range-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-light::-ms-thumb {\n  background-color: #f8f9fa;\n}\n\n.custom-range.custom-range-light::-ms-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-dark:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-lightblue:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-lightblue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(60, 141, 188, 0.25);\n}\n\n.custom-range.custom-range-lightblue::-webkit-slider-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-webkit-slider-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-lightblue::-moz-range-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-moz-range-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-lightblue::-ms-thumb {\n  background-color: #3c8dbc;\n}\n\n.custom-range.custom-range-lightblue::-ms-thumb:active {\n  background-color: #c0dbeb;\n}\n\n.custom-range.custom-range-navy:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-navy:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 31, 63, 0.25);\n}\n\n.custom-range.custom-range-navy::-webkit-slider-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-webkit-slider-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-navy::-moz-range-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-moz-range-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-navy::-ms-thumb {\n  background-color: #001f3f;\n}\n\n.custom-range.custom-range-navy::-ms-thumb:active {\n  background-color: #0077f2;\n}\n\n.custom-range.custom-range-olive:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-olive:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(61, 153, 112, 0.25);\n}\n\n.custom-range.custom-range-olive::-webkit-slider-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-webkit-slider-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-olive::-moz-range-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-moz-range-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-olive::-ms-thumb {\n  background-color: #3d9970;\n}\n\n.custom-range.custom-range-olive::-ms-thumb:active {\n  background-color: #abdec7;\n}\n\n.custom-range.custom-range-lime:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-lime:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(1, 255, 112, 0.25);\n}\n\n.custom-range.custom-range-lime::-webkit-slider-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-webkit-slider-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-lime::-moz-range-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-moz-range-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-lime::-ms-thumb {\n  background-color: #01ff70;\n}\n\n.custom-range.custom-range-lime::-ms-thumb:active {\n  background-color: #b4ffd4;\n}\n\n.custom-range.custom-range-fuchsia:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-fuchsia:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(240, 18, 190, 0.25);\n}\n\n.custom-range.custom-range-fuchsia::-webkit-slider-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-webkit-slider-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-fuchsia::-moz-range-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-moz-range-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-fuchsia::-ms-thumb {\n  background-color: #f012be;\n}\n\n.custom-range.custom-range-fuchsia::-ms-thumb:active {\n  background-color: #fbbaec;\n}\n\n.custom-range.custom-range-maroon:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-maroon:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(216, 27, 96, 0.25);\n}\n\n.custom-range.custom-range-maroon::-webkit-slider-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-webkit-slider-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-maroon::-moz-range-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-moz-range-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-maroon::-ms-thumb {\n  background-color: #d81b60;\n}\n\n.custom-range.custom-range-maroon::-ms-thumb:active {\n  background-color: #f5b0c9;\n}\n\n.custom-range.custom-range-blue:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-blue:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.custom-range.custom-range-blue::-webkit-slider-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-webkit-slider-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-blue::-moz-range-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-moz-range-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-blue::-ms-thumb {\n  background-color: #007bff;\n}\n\n.custom-range.custom-range-blue::-ms-thumb:active {\n  background-color: #b3d7ff;\n}\n\n.custom-range.custom-range-indigo:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-indigo:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(102, 16, 242, 0.25);\n}\n\n.custom-range.custom-range-indigo::-webkit-slider-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-webkit-slider-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-indigo::-moz-range-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-moz-range-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-indigo::-ms-thumb {\n  background-color: #6610f2;\n}\n\n.custom-range.custom-range-indigo::-ms-thumb:active {\n  background-color: #d2b9fb;\n}\n\n.custom-range.custom-range-purple:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-purple:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(111, 66, 193, 0.25);\n}\n\n.custom-range.custom-range-purple::-webkit-slider-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-webkit-slider-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-purple::-moz-range-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-moz-range-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-purple::-ms-thumb {\n  background-color: #6f42c1;\n}\n\n.custom-range.custom-range-purple::-ms-thumb:active {\n  background-color: #d5c8ed;\n}\n\n.custom-range.custom-range-pink:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-pink:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(232, 62, 140, 0.25);\n}\n\n.custom-range.custom-range-pink::-webkit-slider-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-webkit-slider-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-pink::-moz-range-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-moz-range-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-pink::-ms-thumb {\n  background-color: #e83e8c;\n}\n\n.custom-range.custom-range-pink::-ms-thumb:active {\n  background-color: #fbddeb;\n}\n\n.custom-range.custom-range-red:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-red:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(220, 53, 69, 0.25);\n}\n\n.custom-range.custom-range-red::-webkit-slider-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-webkit-slider-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-red::-moz-range-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-moz-range-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-red::-ms-thumb {\n  background-color: #dc3545;\n}\n\n.custom-range.custom-range-red::-ms-thumb:active {\n  background-color: #f6cdd1;\n}\n\n.custom-range.custom-range-orange:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-orange:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(253, 126, 20, 0.25);\n}\n\n.custom-range.custom-range-orange::-webkit-slider-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-webkit-slider-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-orange::-moz-range-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-moz-range-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-orange::-ms-thumb {\n  background-color: #fd7e14;\n}\n\n.custom-range.custom-range-orange::-ms-thumb:active {\n  background-color: #ffdfc5;\n}\n\n.custom-range.custom-range-yellow:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-yellow:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 193, 7, 0.25);\n}\n\n.custom-range.custom-range-yellow::-webkit-slider-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-webkit-slider-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-yellow::-moz-range-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-moz-range-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-yellow::-ms-thumb {\n  background-color: #ffc107;\n}\n\n.custom-range.custom-range-yellow::-ms-thumb:active {\n  background-color: #ffeeba;\n}\n\n.custom-range.custom-range-green:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-green:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(40, 167, 69, 0.25);\n}\n\n.custom-range.custom-range-green::-webkit-slider-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-webkit-slider-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-green::-moz-range-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-moz-range-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-green::-ms-thumb {\n  background-color: #28a745;\n}\n\n.custom-range.custom-range-green::-ms-thumb:active {\n  background-color: #9be7ac;\n}\n\n.custom-range.custom-range-teal:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-teal:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(32, 201, 151, 0.25);\n}\n\n.custom-range.custom-range-teal::-webkit-slider-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-webkit-slider-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-teal::-moz-range-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-moz-range-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-teal::-ms-thumb {\n  background-color: #20c997;\n}\n\n.custom-range.custom-range-teal::-ms-thumb:active {\n  background-color: #aaf1dc;\n}\n\n.custom-range.custom-range-cyan:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-cyan:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(23, 162, 184, 0.25);\n}\n\n.custom-range.custom-range-cyan::-webkit-slider-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-webkit-slider-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-cyan::-moz-range-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-moz-range-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-cyan::-ms-thumb {\n  background-color: #17a2b8;\n}\n\n.custom-range.custom-range-cyan::-ms-thumb:active {\n  background-color: #90e4f1;\n}\n\n.custom-range.custom-range-white:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-white:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(255, 255, 255, 0.25);\n}\n\n.custom-range.custom-range-white::-webkit-slider-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-webkit-slider-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-white::-moz-range-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-moz-range-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-white::-ms-thumb {\n  background-color: #fff;\n}\n\n.custom-range.custom-range-white::-ms-thumb:active {\n  background-color: white;\n}\n\n.custom-range.custom-range-gray:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-gray:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(108, 117, 125, 0.25);\n}\n\n.custom-range.custom-range-gray::-webkit-slider-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-webkit-slider-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray::-moz-range-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-moz-range-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray::-ms-thumb {\n  background-color: #6c757d;\n}\n\n.custom-range.custom-range-gray::-ms-thumb:active {\n  background-color: #caced1;\n}\n\n.custom-range.custom-range-gray-dark:focus {\n  outline: none;\n}\n\n.custom-range.custom-range-gray-dark:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark:focus::-ms-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba(52, 58, 64, 0.25);\n}\n\n.custom-range.custom-range-gray-dark::-webkit-slider-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-webkit-slider-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-gray-dark::-moz-range-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-moz-range-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-range.custom-range-gray-dark::-ms-thumb {\n  background-color: #343a40;\n}\n\n.custom-range.custom-range-gray-dark::-ms-thumb:active {\n  background-color: #88939e;\n}\n\n.custom-control-input-primary:checked ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n}\n\n.custom-control-input-primary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23007bff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-primary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23007bff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-primary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input-primary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input-primary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n}\n\n.custom-control-input-secondary:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.custom-control-input-secondary.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-secondary.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-secondary:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.custom-control-input-secondary:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.custom-control-input-secondary:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.custom-control-input-success:checked ~ .custom-control-label::before {\n  border-color: #28a745;\n  background-color: #28a745;\n}\n\n.custom-control-input-success.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-success.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2328a745'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-success:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.custom-control-input-success:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #71dd8a;\n}\n\n.custom-control-input-success:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #9be7ac;\n  border-color: #9be7ac;\n}\n\n.custom-control-input-info:checked ~ .custom-control-label::before {\n  border-color: #17a2b8;\n  background-color: #17a2b8;\n}\n\n.custom-control-input-info.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2317a2b8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-info.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2317a2b8'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-info:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(23, 162, 184, 0.25);\n}\n\n.custom-control-input-info:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #63d9ec;\n}\n\n.custom-control-input-info:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #90e4f1;\n  border-color: #90e4f1;\n}\n\n.custom-control-input-warning:checked ~ .custom-control-label::before {\n  border-color: #ffc107;\n  background-color: #ffc107;\n}\n\n.custom-control-input-warning.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffc107' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-warning.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffc107'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-warning:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input-warning:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffe187;\n}\n\n.custom-control-input-warning:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffeeba;\n  border-color: #ffeeba;\n}\n\n.custom-control-input-danger:checked ~ .custom-control-label::before {\n  border-color: #dc3545;\n  background-color: #dc3545;\n}\n\n.custom-control-input-danger.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23dc3545' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-danger.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23dc3545'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-danger:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.custom-control-input-danger:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #efa2a9;\n}\n\n.custom-control-input-danger:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f6cdd1;\n  border-color: #f6cdd1;\n}\n\n.custom-control-input-light:checked ~ .custom-control-label::before {\n  border-color: #f8f9fa;\n  background-color: #f8f9fa;\n}\n\n.custom-control-input-light.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f8f9fa' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-light.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f8f9fa'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-light:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(248, 249, 250, 0.25);\n}\n\n.custom-control-input-light:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.custom-control-input-light:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.custom-control-input-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.custom-control-input-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.custom-control-input-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.custom-control-input-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.custom-control-input-lightblue:checked ~ .custom-control-label::before {\n  border-color: #3c8dbc;\n  background-color: #3c8dbc;\n}\n\n.custom-control-input-lightblue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233c8dbc' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lightblue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233c8dbc'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lightblue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(60, 141, 188, 0.25);\n}\n\n.custom-control-input-lightblue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #99c5de;\n}\n\n.custom-control-input-lightblue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #c0dbeb;\n  border-color: #c0dbeb;\n}\n\n.custom-control-input-navy:checked ~ .custom-control-label::before {\n  border-color: #001f3f;\n  background-color: #001f3f;\n}\n\n.custom-control-input-navy.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23001f3f' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-navy.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23001f3f'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-navy:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 31, 63, 0.25);\n}\n\n.custom-control-input-navy:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #005ebf;\n}\n\n.custom-control-input-navy:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #0077f2;\n  border-color: #0077f2;\n}\n\n.custom-control-input-olive:checked ~ .custom-control-label::before {\n  border-color: #3d9970;\n  background-color: #3d9970;\n}\n\n.custom-control-input-olive.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%233d9970' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-olive.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%233d9970'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-olive:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(61, 153, 112, 0.25);\n}\n\n.custom-control-input-olive:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #87cfaf;\n}\n\n.custom-control-input-olive:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #abdec7;\n  border-color: #abdec7;\n}\n\n.custom-control-input-lime:checked ~ .custom-control-label::before {\n  border-color: #01ff70;\n  background-color: #01ff70;\n}\n\n.custom-control-input-lime.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2301ff70' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lime.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2301ff70'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-lime:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(1, 255, 112, 0.25);\n}\n\n.custom-control-input-lime:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #81ffb8;\n}\n\n.custom-control-input-lime:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b4ffd4;\n  border-color: #b4ffd4;\n}\n\n.custom-control-input-fuchsia:checked ~ .custom-control-label::before {\n  border-color: #f012be;\n  background-color: #f012be;\n}\n\n.custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23f012be' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-fuchsia.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23f012be'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-fuchsia:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(240, 18, 190, 0.25);\n}\n\n.custom-control-input-fuchsia:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f88adf;\n}\n\n.custom-control-input-fuchsia:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbbaec;\n  border-color: #fbbaec;\n}\n\n.custom-control-input-maroon:checked ~ .custom-control-label::before {\n  border-color: #d81b60;\n  background-color: #d81b60;\n}\n\n.custom-control-input-maroon.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23d81b60' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-maroon.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23d81b60'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-maroon:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(216, 27, 96, 0.25);\n}\n\n.custom-control-input-maroon:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f083ab;\n}\n\n.custom-control-input-maroon:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f5b0c9;\n  border-color: #f5b0c9;\n}\n\n.custom-control-input-blue:checked ~ .custom-control-label::before {\n  border-color: #007bff;\n  background-color: #007bff;\n}\n\n.custom-control-input-blue.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23007bff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-blue.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23007bff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-blue:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input-blue:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #80bdff;\n}\n\n.custom-control-input-blue:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #b3d7ff;\n  border-color: #b3d7ff;\n}\n\n.custom-control-input-indigo:checked ~ .custom-control-label::before {\n  border-color: #6610f2;\n  background-color: #6610f2;\n}\n\n.custom-control-input-indigo.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236610f2' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-indigo.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236610f2'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-indigo:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(102, 16, 242, 0.25);\n}\n\n.custom-control-input-indigo:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b389f9;\n}\n\n.custom-control-input-indigo:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d2b9fb;\n  border-color: #d2b9fb;\n}\n\n.custom-control-input-purple:checked ~ .custom-control-label::before {\n  border-color: #6f42c1;\n  background-color: #6f42c1;\n}\n\n.custom-control-input-purple.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236f42c1' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-purple.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236f42c1'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-purple:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(111, 66, 193, 0.25);\n}\n\n.custom-control-input-purple:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #b8a2e0;\n}\n\n.custom-control-input-purple:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #d5c8ed;\n  border-color: #d5c8ed;\n}\n\n.custom-control-input-pink:checked ~ .custom-control-label::before {\n  border-color: #e83e8c;\n  background-color: #e83e8c;\n}\n\n.custom-control-input-pink.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23e83e8c' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-pink.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23e83e8c'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-pink:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(232, 62, 140, 0.25);\n}\n\n.custom-control-input-pink:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #f6b0d0;\n}\n\n.custom-control-input-pink:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #fbddeb;\n  border-color: #fbddeb;\n}\n\n.custom-control-input-red:checked ~ .custom-control-label::before {\n  border-color: #dc3545;\n  background-color: #dc3545;\n}\n\n.custom-control-input-red.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23dc3545' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-red.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23dc3545'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-red:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.custom-control-input-red:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #efa2a9;\n}\n\n.custom-control-input-red:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #f6cdd1;\n  border-color: #f6cdd1;\n}\n\n.custom-control-input-orange:checked ~ .custom-control-label::before {\n  border-color: #fd7e14;\n  background-color: #fd7e14;\n}\n\n.custom-control-input-orange.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fd7e14' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-orange.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fd7e14'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-orange:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(253, 126, 20, 0.25);\n}\n\n.custom-control-input-orange:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #fec392;\n}\n\n.custom-control-input-orange:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffdfc5;\n  border-color: #ffdfc5;\n}\n\n.custom-control-input-yellow:checked ~ .custom-control-label::before {\n  border-color: #ffc107;\n  background-color: #ffc107;\n}\n\n.custom-control-input-yellow.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffc107' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-yellow.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffc107'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-yellow:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 193, 7, 0.25);\n}\n\n.custom-control-input-yellow:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #ffe187;\n}\n\n.custom-control-input-yellow:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #ffeeba;\n  border-color: #ffeeba;\n}\n\n.custom-control-input-green:checked ~ .custom-control-label::before {\n  border-color: #28a745;\n  background-color: #28a745;\n}\n\n.custom-control-input-green.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-green.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2328a745'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-green:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.custom-control-input-green:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #71dd8a;\n}\n\n.custom-control-input-green:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #9be7ac;\n  border-color: #9be7ac;\n}\n\n.custom-control-input-teal:checked ~ .custom-control-label::before {\n  border-color: #20c997;\n  background-color: #20c997;\n}\n\n.custom-control-input-teal.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2320c997' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-teal.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2320c997'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-teal:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(32, 201, 151, 0.25);\n}\n\n.custom-control-input-teal:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #7eeaca;\n}\n\n.custom-control-input-teal:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #aaf1dc;\n  border-color: #aaf1dc;\n}\n\n.custom-control-input-cyan:checked ~ .custom-control-label::before {\n  border-color: #17a2b8;\n  background-color: #17a2b8;\n}\n\n.custom-control-input-cyan.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2317a2b8' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-cyan.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2317a2b8'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-cyan:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(23, 162, 184, 0.25);\n}\n\n.custom-control-input-cyan:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #63d9ec;\n}\n\n.custom-control-input-cyan:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #90e4f1;\n  border-color: #90e4f1;\n}\n\n.custom-control-input-white:checked ~ .custom-control-label::before {\n  border-color: #fff;\n  background-color: #fff;\n}\n\n.custom-control-input-white.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-white.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-white:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(255, 255, 255, 0.25);\n}\n\n.custom-control-input-white:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: white;\n}\n\n.custom-control-input-white:not(:disabled):active ~ .custom-control-label::before {\n  background-color: white;\n  border-color: white;\n}\n\n.custom-control-input-gray:checked ~ .custom-control-label::before {\n  border-color: #6c757d;\n  background-color: #6c757d;\n}\n\n.custom-control-input-gray.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%236c757d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%236c757d'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(108, 117, 125, 0.25);\n}\n\n.custom-control-input-gray:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #afb5ba;\n}\n\n.custom-control-input-gray:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #caced1;\n  border-color: #caced1;\n}\n\n.custom-control-input-gray-dark:checked ~ .custom-control-label::before {\n  border-color: #343a40;\n  background-color: #343a40;\n}\n\n.custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"checkbox\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23343a40' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray-dark.custom-control-input-outline:checked[type=\"radio\"] ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23343a40'/%3E%3C/svg%3E\") !important;\n}\n\n.custom-control-input-gray-dark:focus ~ .custom-control-label::before {\n  box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0.2rem rgba(52, 58, 64, 0.25);\n}\n\n.custom-control-input-gray-dark:focus:not(:checked) ~ .custom-control-label::before {\n  border-color: #6d7a86;\n}\n\n.custom-control-input-gray-dark:not(:disabled):active ~ .custom-control-label::before {\n  background-color: #88939e;\n  border-color: #88939e;\n}\n\n.custom-control-input-outline ~ .custom-control-label::before {\n  background-color: transparent !important;\n  box-shadow: none;\n}\n\n.custom-control-input-outline:checked ~ .custom-control-label::before {\n  background-color: transparent;\n}\n\n.navbar-dark .btn-navbar,\n.navbar-dark .form-control-navbar {\n  background-color: #3f474e;\n  border: 1px solid #56606a;\n  color: white;\n}\n\n.navbar-dark .btn-navbar:hover {\n  background-color: #454d55;\n}\n\n.navbar-dark .btn-navbar:focus {\n  background-color: #4b545c;\n}\n\n.navbar-dark .form-control-navbar + .input-group-prepend > .btn-navbar,\n.navbar-dark .form-control-navbar + .input-group-append > .btn-navbar {\n  background-color: #3f474e;\n  color: #fff;\n  border: 1px solid #56606a;\n  border-left: none;\n}\n\n.progress {\n  box-shadow: none;\n  border-radius: 1px;\n}\n\n.progress.vertical {\n  display: inline-block;\n  height: 200px;\n  margin-right: 10px;\n  position: relative;\n  width: 30px;\n}\n\n.progress.vertical > .progress-bar {\n  bottom: 0;\n  position: absolute;\n  width: 100%;\n}\n\n.progress.vertical.sm, .progress.vertical.progress-sm {\n  width: 20px;\n}\n\n.progress.vertical.xs, .progress.vertical.progress-xs {\n  width: 10px;\n}\n\n.progress.vertical.xxs, .progress.vertical.progress-xxs {\n  width: 3px;\n}\n\n.progress-group {\n  margin-bottom: 0.5rem;\n}\n\n.progress-sm {\n  height: 10px;\n}\n\n.progress-xs {\n  height: 7px;\n}\n\n.progress-xxs {\n  height: 3px;\n}\n\n.table tr > td .progress {\n  margin: 0;\n}\n\n.card-primary:not(.card-outline) > .card-header {\n  background-color: #007bff;\n}\n\n.card-primary:not(.card-outline) > .card-header,\n.card-primary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-primary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-primary.card-outline {\n  border-top: 3px solid #007bff;\n}\n\n.card-primary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-primary.card-outline-tabs > .card-header a.active,\n.card-primary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #007bff;\n}\n\n.bg-primary > .card-header .btn-tool,\n.bg-gradient-primary > .card-header .btn-tool,\n.card-primary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-primary > .card-header .btn-tool:hover,\n.bg-gradient-primary > .card-header .btn-tool:hover,\n.card-primary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget .table td,\n.card.bg-primary .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #0067d6;\n  color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-primary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-primary .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-primary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3395ff;\n  color: #fff;\n}\n\n.card-secondary:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.card-secondary:not(.card-outline) > .card-header,\n.card-secondary:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-secondary:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-secondary.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.card-secondary.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-secondary.card-outline-tabs > .card-header a.active,\n.card-secondary.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.bg-secondary > .card-header .btn-tool,\n.bg-gradient-secondary > .card-header .btn-tool,\n.card-secondary:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-secondary > .card-header .btn-tool:hover,\n.bg-gradient-secondary > .card-header .btn-tool:hover,\n.card-secondary:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget .table td,\n.card.bg-secondary .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-secondary .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-secondary .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.card-success:not(.card-outline) > .card-header {\n  background-color: #28a745;\n}\n\n.card-success:not(.card-outline) > .card-header,\n.card-success:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-success:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-success.card-outline {\n  border-top: 3px solid #28a745;\n}\n\n.card-success.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-success.card-outline-tabs > .card-header a.active,\n.card-success.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #28a745;\n}\n\n.bg-success > .card-header .btn-tool,\n.bg-gradient-success > .card-header .btn-tool,\n.card-success:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-success > .card-header .btn-tool:hover,\n.bg-gradient-success > .card-header .btn-tool:hover,\n.card-success:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget .table td,\n.card.bg-success .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-success .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #208637;\n  color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-success .bootstrap-datetimepicker-widget table td.active,\n.card.bg-success .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-success .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ce57;\n  color: #fff;\n}\n\n.card-info:not(.card-outline) > .card-header {\n  background-color: #17a2b8;\n}\n\n.card-info:not(.card-outline) > .card-header,\n.card-info:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-info:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-info.card-outline {\n  border-top: 3px solid #17a2b8;\n}\n\n.card-info.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-info.card-outline-tabs > .card-header a.active,\n.card-info.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #17a2b8;\n}\n\n.bg-info > .card-header .btn-tool,\n.bg-gradient-info > .card-header .btn-tool,\n.card-info:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-info > .card-header .btn-tool:hover,\n.bg-gradient-info > .card-header .btn-tool:hover,\n.card-info:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget .table td,\n.card.bg-info .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-info .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #128294;\n  color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-info .bootstrap-datetimepicker-widget table td.active,\n.card.bg-info .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-info .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #1fc8e3;\n  color: #fff;\n}\n\n.card-warning:not(.card-outline) > .card-header {\n  background-color: #ffc107;\n}\n\n.card-warning:not(.card-outline) > .card-header,\n.card-warning:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-warning:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-warning.card-outline {\n  border-top: 3px solid #ffc107;\n}\n\n.card-warning.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-warning.card-outline-tabs > .card-header a.active,\n.card-warning.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ffc107;\n}\n\n.bg-warning > .card-header .btn-tool,\n.bg-gradient-warning > .card-header .btn-tool,\n.card-warning:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-warning > .card-header .btn-tool:hover,\n.bg-gradient-warning > .card-header .btn-tool:hover,\n.card-warning:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget .table td,\n.card.bg-warning .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #dda600;\n  color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-warning .bootstrap-datetimepicker-widget table td.active,\n.card.bg-warning .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-warning .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ffce3a;\n  color: #1f2d3d;\n}\n\n.card-danger:not(.card-outline) > .card-header {\n  background-color: #dc3545;\n}\n\n.card-danger:not(.card-outline) > .card-header,\n.card-danger:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-danger:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-danger.card-outline {\n  border-top: 3px solid #dc3545;\n}\n\n.card-danger.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-danger.card-outline-tabs > .card-header a.active,\n.card-danger.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #dc3545;\n}\n\n.bg-danger > .card-header .btn-tool,\n.bg-gradient-danger > .card-header .btn-tool,\n.card-danger:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-danger > .card-header .btn-tool:hover,\n.bg-gradient-danger > .card-header .btn-tool:hover,\n.card-danger:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget .table td,\n.card.bg-danger .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #c62232;\n  color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-danger .bootstrap-datetimepicker-widget table td.active,\n.card.bg-danger .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-danger .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e4606d;\n  color: #fff;\n}\n\n.card-light:not(.card-outline) > .card-header {\n  background-color: #f8f9fa;\n}\n\n.card-light:not(.card-outline) > .card-header,\n.card-light:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-light:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-light.card-outline {\n  border-top: 3px solid #f8f9fa;\n}\n\n.card-light.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-light.card-outline-tabs > .card-header a.active,\n.card-light.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f8f9fa;\n}\n\n.bg-light > .card-header .btn-tool,\n.bg-gradient-light > .card-header .btn-tool,\n.card-light:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-light > .card-header .btn-tool:hover,\n.bg-gradient-light > .card-header .btn-tool:hover,\n.card-light:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget .table td,\n.card.bg-light .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-light .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e0e5e9;\n  color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-light .bootstrap-datetimepicker-widget table td.active,\n.card.bg-light .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-light .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.card-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.card-dark:not(.card-outline) > .card-header,\n.card-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.card-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-dark.card-outline-tabs > .card-header a.active,\n.card-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.bg-dark > .card-header .btn-tool,\n.bg-gradient-dark > .card-header .btn-tool,\n.card-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-dark > .card-header .btn-tool:hover,\n.bg-gradient-dark > .card-header .btn-tool:hover,\n.card-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-dark .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.card-lightblue:not(.card-outline) > .card-header {\n  background-color: #3c8dbc;\n}\n\n.card-lightblue:not(.card-outline) > .card-header,\n.card-lightblue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-lightblue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-lightblue.card-outline {\n  border-top: 3px solid #3c8dbc;\n}\n\n.card-lightblue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-lightblue.card-outline-tabs > .card-header a.active,\n.card-lightblue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3c8dbc;\n}\n\n.bg-lightblue > .card-header .btn-tool,\n.bg-gradient-lightblue > .card-header .btn-tool,\n.card-lightblue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-lightblue > .card-header .btn-tool:hover,\n.bg-gradient-lightblue > .card-header .btn-tool:hover,\n.card-lightblue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget .table td,\n.card.bg-lightblue .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #32769d;\n  color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-lightblue .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-lightblue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #5fa4cc;\n  color: #fff;\n}\n\n.card-navy:not(.card-outline) > .card-header {\n  background-color: #001f3f;\n}\n\n.card-navy:not(.card-outline) > .card-header,\n.card-navy:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-navy:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-navy.card-outline {\n  border-top: 3px solid #001f3f;\n}\n\n.card-navy.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-navy.card-outline-tabs > .card-header a.active,\n.card-navy.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #001f3f;\n}\n\n.bg-navy > .card-header .btn-tool,\n.bg-gradient-navy > .card-header .btn-tool,\n.card-navy:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-navy > .card-header .btn-tool:hover,\n.bg-gradient-navy > .card-header .btn-tool:hover,\n.card-navy:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget .table td,\n.card.bg-navy .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #000b16;\n  color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-navy .bootstrap-datetimepicker-widget table td.active,\n.card.bg-navy .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-navy .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #003872;\n  color: #fff;\n}\n\n.card-olive:not(.card-outline) > .card-header {\n  background-color: #3d9970;\n}\n\n.card-olive:not(.card-outline) > .card-header,\n.card-olive:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-olive:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-olive.card-outline {\n  border-top: 3px solid #3d9970;\n}\n\n.card-olive.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-olive.card-outline-tabs > .card-header a.active,\n.card-olive.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #3d9970;\n}\n\n.bg-olive > .card-header .btn-tool,\n.bg-gradient-olive > .card-header .btn-tool,\n.card-olive:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-olive > .card-header .btn-tool:hover,\n.bg-gradient-olive > .card-header .btn-tool:hover,\n.card-olive:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget .table td,\n.card.bg-olive .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #317c5b;\n  color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-olive .bootstrap-datetimepicker-widget table td.active,\n.card.bg-olive .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-olive .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #50b98a;\n  color: #fff;\n}\n\n.card-lime:not(.card-outline) > .card-header {\n  background-color: #01ff70;\n}\n\n.card-lime:not(.card-outline) > .card-header,\n.card-lime:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-lime:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-lime.card-outline {\n  border-top: 3px solid #01ff70;\n}\n\n.card-lime.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-lime.card-outline-tabs > .card-header a.active,\n.card-lime.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #01ff70;\n}\n\n.bg-lime > .card-header .btn-tool,\n.bg-gradient-lime > .card-header .btn-tool,\n.card-lime:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-lime > .card-header .btn-tool:hover,\n.bg-gradient-lime > .card-header .btn-tool:hover,\n.card-lime:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget .table td,\n.card.bg-lime .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #00d75e;\n  color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-lime .bootstrap-datetimepicker-widget table td.active,\n.card.bg-lime .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-lime .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ff8d;\n  color: #1f2d3d;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header {\n  background-color: #f012be;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header,\n.card-fuchsia:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-fuchsia:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-fuchsia.card-outline {\n  border-top: 3px solid #f012be;\n}\n\n.card-fuchsia.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-fuchsia.card-outline-tabs > .card-header a.active,\n.card-fuchsia.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #f012be;\n}\n\n.bg-fuchsia > .card-header .btn-tool,\n.bg-gradient-fuchsia > .card-header .btn-tool,\n.card-fuchsia:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-fuchsia > .card-header .btn-tool:hover,\n.bg-gradient-fuchsia > .card-header .btn-tool:hover,\n.card-fuchsia:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget .table td,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #cc0da1;\n  color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.card.bg-fuchsia .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-fuchsia .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #f342cb;\n  color: #fff;\n}\n\n.card-maroon:not(.card-outline) > .card-header {\n  background-color: #d81b60;\n}\n\n.card-maroon:not(.card-outline) > .card-header,\n.card-maroon:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-maroon:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-maroon.card-outline {\n  border-top: 3px solid #d81b60;\n}\n\n.card-maroon.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-maroon.card-outline-tabs > .card-header a.active,\n.card-maroon.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #d81b60;\n}\n\n.bg-maroon > .card-header .btn-tool,\n.bg-gradient-maroon > .card-header .btn-tool,\n.card-maroon:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-maroon > .card-header .btn-tool:hover,\n.bg-gradient-maroon > .card-header .btn-tool:hover,\n.card-maroon:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget .table td,\n.card.bg-maroon .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #b41650;\n  color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.active,\n.card.bg-maroon .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-maroon .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e73f7c;\n  color: #fff;\n}\n\n.card-blue:not(.card-outline) > .card-header {\n  background-color: #007bff;\n}\n\n.card-blue:not(.card-outline) > .card-header,\n.card-blue:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-blue:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-blue.card-outline {\n  border-top: 3px solid #007bff;\n}\n\n.card-blue.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-blue.card-outline-tabs > .card-header a.active,\n.card-blue.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #007bff;\n}\n\n.bg-blue > .card-header .btn-tool,\n.bg-gradient-blue > .card-header .btn-tool,\n.card-blue:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-blue > .card-header .btn-tool:hover,\n.bg-gradient-blue > .card-header .btn-tool:hover,\n.card-blue:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget .table td,\n.card.bg-blue .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #0067d6;\n  color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-blue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-blue .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-blue .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3395ff;\n  color: #fff;\n}\n\n.card-indigo:not(.card-outline) > .card-header {\n  background-color: #6610f2;\n}\n\n.card-indigo:not(.card-outline) > .card-header,\n.card-indigo:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-indigo:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-indigo.card-outline {\n  border-top: 3px solid #6610f2;\n}\n\n.card-indigo.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-indigo.card-outline-tabs > .card-header a.active,\n.card-indigo.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6610f2;\n}\n\n.bg-indigo > .card-header .btn-tool,\n.bg-gradient-indigo > .card-header .btn-tool,\n.card-indigo:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-indigo > .card-header .btn-tool:hover,\n.bg-gradient-indigo > .card-header .btn-tool:hover,\n.card-indigo:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget .table td,\n.card.bg-indigo .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #550bce;\n  color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.active,\n.card.bg-indigo .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-indigo .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8540f5;\n  color: #fff;\n}\n\n.card-purple:not(.card-outline) > .card-header {\n  background-color: #6f42c1;\n}\n\n.card-purple:not(.card-outline) > .card-header,\n.card-purple:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-purple:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-purple.card-outline {\n  border-top: 3px solid #6f42c1;\n}\n\n.card-purple.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-purple.card-outline-tabs > .card-header a.active,\n.card-purple.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6f42c1;\n}\n\n.bg-purple > .card-header .btn-tool,\n.bg-gradient-purple > .card-header .btn-tool,\n.card-purple:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-purple > .card-header .btn-tool:hover,\n.bg-gradient-purple > .card-header .btn-tool:hover,\n.card-purple:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget .table td,\n.card.bg-purple .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #5d36a4;\n  color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-purple .bootstrap-datetimepicker-widget table td.active,\n.card.bg-purple .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-purple .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #8c68ce;\n  color: #fff;\n}\n\n.card-pink:not(.card-outline) > .card-header {\n  background-color: #e83e8c;\n}\n\n.card-pink:not(.card-outline) > .card-header,\n.card-pink:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-pink:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-pink.card-outline {\n  border-top: 3px solid #e83e8c;\n}\n\n.card-pink.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-pink.card-outline-tabs > .card-header a.active,\n.card-pink.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #e83e8c;\n}\n\n.bg-pink > .card-header .btn-tool,\n.bg-gradient-pink > .card-header .btn-tool,\n.card-pink:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-pink > .card-header .btn-tool:hover,\n.bg-gradient-pink > .card-header .btn-tool:hover,\n.card-pink:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget .table td,\n.card.bg-pink .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e21b76;\n  color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-pink .bootstrap-datetimepicker-widget table td.active,\n.card.bg-pink .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-pink .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ed6ca7;\n  color: #fff;\n}\n\n.card-red:not(.card-outline) > .card-header {\n  background-color: #dc3545;\n}\n\n.card-red:not(.card-outline) > .card-header,\n.card-red:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-red:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-red.card-outline {\n  border-top: 3px solid #dc3545;\n}\n\n.card-red.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-red.card-outline-tabs > .card-header a.active,\n.card-red.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #dc3545;\n}\n\n.bg-red > .card-header .btn-tool,\n.bg-gradient-red > .card-header .btn-tool,\n.card-red:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-red > .card-header .btn-tool:hover,\n.bg-gradient-red > .card-header .btn-tool:hover,\n.card-red:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget .table td,\n.card.bg-red .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-red .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #c62232;\n  color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-red .bootstrap-datetimepicker-widget table td.active,\n.card.bg-red .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-red .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #e4606d;\n  color: #fff;\n}\n\n.card-orange:not(.card-outline) > .card-header {\n  background-color: #fd7e14;\n}\n\n.card-orange:not(.card-outline) > .card-header,\n.card-orange:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-orange:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-orange.card-outline {\n  border-top: 3px solid #fd7e14;\n}\n\n.card-orange.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-orange.card-outline-tabs > .card-header a.active,\n.card-orange.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fd7e14;\n}\n\n.bg-orange > .card-header .btn-tool,\n.bg-gradient-orange > .card-header .btn-tool,\n.card-orange:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-orange > .card-header .btn-tool:hover,\n.bg-gradient-orange > .card-header .btn-tool:hover,\n.card-orange:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget .table td,\n.card.bg-orange .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #e66a02;\n  color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-orange .bootstrap-datetimepicker-widget table td.active,\n.card.bg-orange .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-orange .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #fd9a47;\n  color: #1f2d3d;\n}\n\n.card-yellow:not(.card-outline) > .card-header {\n  background-color: #ffc107;\n}\n\n.card-yellow:not(.card-outline) > .card-header,\n.card-yellow:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-yellow:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-yellow.card-outline {\n  border-top: 3px solid #ffc107;\n}\n\n.card-yellow.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-yellow.card-outline-tabs > .card-header a.active,\n.card-yellow.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #ffc107;\n}\n\n.bg-yellow > .card-header .btn-tool,\n.bg-gradient-yellow > .card-header .btn-tool,\n.card-yellow:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-yellow > .card-header .btn-tool:hover,\n.bg-gradient-yellow > .card-header .btn-tool:hover,\n.card-yellow:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget .table td,\n.card.bg-yellow .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #dda600;\n  color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.active,\n.card.bg-yellow .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-yellow .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #ffce3a;\n  color: #1f2d3d;\n}\n\n.card-green:not(.card-outline) > .card-header {\n  background-color: #28a745;\n}\n\n.card-green:not(.card-outline) > .card-header,\n.card-green:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-green:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-green.card-outline {\n  border-top: 3px solid #28a745;\n}\n\n.card-green.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-green.card-outline-tabs > .card-header a.active,\n.card-green.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #28a745;\n}\n\n.bg-green > .card-header .btn-tool,\n.bg-gradient-green > .card-header .btn-tool,\n.card-green:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-green > .card-header .btn-tool:hover,\n.bg-gradient-green > .card-header .btn-tool:hover,\n.card-green:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget .table td,\n.card.bg-green .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-green .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #208637;\n  color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-green .bootstrap-datetimepicker-widget table td.active,\n.card.bg-green .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-green .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #34ce57;\n  color: #fff;\n}\n\n.card-teal:not(.card-outline) > .card-header {\n  background-color: #20c997;\n}\n\n.card-teal:not(.card-outline) > .card-header,\n.card-teal:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-teal:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-teal.card-outline {\n  border-top: 3px solid #20c997;\n}\n\n.card-teal.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-teal.card-outline-tabs > .card-header a.active,\n.card-teal.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #20c997;\n}\n\n.bg-teal > .card-header .btn-tool,\n.bg-gradient-teal > .card-header .btn-tool,\n.card-teal:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-teal > .card-header .btn-tool:hover,\n.bg-gradient-teal > .card-header .btn-tool:hover,\n.card-teal:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget .table td,\n.card.bg-teal .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #1aa67d;\n  color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-teal .bootstrap-datetimepicker-widget table td.active,\n.card.bg-teal .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-teal .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #3ce0af;\n  color: #fff;\n}\n\n.card-cyan:not(.card-outline) > .card-header {\n  background-color: #17a2b8;\n}\n\n.card-cyan:not(.card-outline) > .card-header,\n.card-cyan:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-cyan:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-cyan.card-outline {\n  border-top: 3px solid #17a2b8;\n}\n\n.card-cyan.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-cyan.card-outline-tabs > .card-header a.active,\n.card-cyan.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #17a2b8;\n}\n\n.bg-cyan > .card-header .btn-tool,\n.bg-gradient-cyan > .card-header .btn-tool,\n.card-cyan:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-cyan > .card-header .btn-tool:hover,\n.bg-gradient-cyan > .card-header .btn-tool:hover,\n.card-cyan:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget .table td,\n.card.bg-cyan .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #128294;\n  color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.active,\n.card.bg-cyan .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-cyan .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #1fc8e3;\n  color: #fff;\n}\n\n.card-white:not(.card-outline) > .card-header {\n  background-color: #fff;\n}\n\n.card-white:not(.card-outline) > .card-header,\n.card-white:not(.card-outline) > .card-header a {\n  color: #1f2d3d;\n}\n\n.card-white:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-white.card-outline {\n  border-top: 3px solid #fff;\n}\n\n.card-white.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-white.card-outline-tabs > .card-header a.active,\n.card-white.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #fff;\n}\n\n.bg-white > .card-header .btn-tool,\n.bg-gradient-white > .card-header .btn-tool,\n.card-white:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(31, 45, 61, 0.8);\n}\n\n.bg-white > .card-header .btn-tool:hover,\n.bg-gradient-white > .card-header .btn-tool:hover,\n.card-white:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget .table td,\n.card.bg-white .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-white .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #ebebeb;\n  color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #1f2d3d;\n}\n\n.card.bg-white .bootstrap-datetimepicker-widget table td.active,\n.card.bg-white .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-white .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: white;\n  color: #1f2d3d;\n}\n\n.card-gray:not(.card-outline) > .card-header {\n  background-color: #6c757d;\n}\n\n.card-gray:not(.card-outline) > .card-header,\n.card-gray:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-gray:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-gray.card-outline {\n  border-top: 3px solid #6c757d;\n}\n\n.card-gray.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-gray.card-outline-tabs > .card-header a.active,\n.card-gray.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #6c757d;\n}\n\n.bg-gray > .card-header .btn-tool,\n.bg-gradient-gray > .card-header .btn-tool,\n.card-gray:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-gray > .card-header .btn-tool:hover,\n.bg-gradient-gray > .card-header .btn-tool:hover,\n.card-gray:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget .table td,\n.card.bg-gray .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #596167;\n  color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-gray .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gray .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-gray .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #868e96;\n  color: #fff;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header {\n  background-color: #343a40;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header,\n.card-gray-dark:not(.card-outline) > .card-header a {\n  color: #fff;\n}\n\n.card-gray-dark:not(.card-outline) > .card-header a.active {\n  color: #1f2d3d;\n}\n\n.card-gray-dark.card-outline {\n  border-top: 3px solid #343a40;\n}\n\n.card-gray-dark.card-outline-tabs > .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card-gray-dark.card-outline-tabs > .card-header a.active,\n.card-gray-dark.card-outline-tabs > .card-header a.active:hover {\n  border-top: 3px solid #343a40;\n}\n\n.bg-gray-dark > .card-header .btn-tool,\n.bg-gradient-gray-dark > .card-header .btn-tool,\n.card-gray-dark:not(.card-outline) > .card-header .btn-tool {\n  color: rgba(255, 255, 255, 0.8);\n}\n\n.bg-gray-dark > .card-header .btn-tool:hover,\n.bg-gradient-gray-dark > .card-header .btn-tool:hover,\n.card-gray-dark:not(.card-outline) > .card-header .btn-tool:hover {\n  color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget .table th,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table td,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget .table th {\n  border: none;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.second:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table thead tr:first-child th:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.day:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.hour:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.minute:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.second:hover {\n  background-color: #222629;\n  color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.today::before,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.today::before {\n  border-bottom-color: #fff;\n}\n\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gray-dark .bootstrap-datetimepicker-widget table td.active:hover,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active,\n.card.bg-gradient-gray-dark .bootstrap-datetimepicker-widget table td.active:hover {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.card {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  margin-bottom: 1rem;\n}\n\n.card.bg-dark .card-header {\n  border-color: #383f45;\n}\n\n.card.bg-dark,\n.card.bg-dark .card-body {\n  color: #fff;\n}\n\n.card.maximized-card {\n  height: 100% !important;\n  left: 0;\n  max-height: 100% !important;\n  max-width: 100% !important;\n  position: fixed;\n  top: 0;\n  width: 100% !important;\n  z-index: 1040;\n}\n\n.card.maximized-card.was-collapsed .card-body {\n  display: block !important;\n}\n\n.card.maximized-card .card-body {\n  overflow: auto;\n}\n\n.card.maximized-card [data-card-widgett=\"collapse\"] {\n  display: none;\n}\n\n.card.maximized-card .card-header,\n.card.maximized-card .card-footer {\n  border-radius: 0 !important;\n}\n\n.card.collapsed-card .card-body,\n.card.collapsed-card .card-footer {\n  display: none;\n}\n\n.card .nav.flex-column:not(.nav-sidebar) > li {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  margin: 0;\n}\n\n.card .nav.flex-column:not(.nav-sidebar) > li:last-of-type {\n  border-bottom: 0;\n}\n\n.card.height-control .card-body {\n  max-height: 300px;\n  overflow: auto;\n}\n\n.card .border-right {\n  border-right: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card .border-left {\n  border-left: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card.card-tabs:not(.card-outline) > .card-header {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.card-outline) > .card-header .nav-item:first-child .nav-link {\n  border-left-color: transparent;\n}\n\n.card.card-tabs.card-outline .nav-item {\n  border-bottom: 0;\n}\n\n.card.card-tabs.card-outline .nav-item:first-child .nav-link {\n  border-left: 0;\n  margin-left: 0;\n}\n\n.card.card-tabs .card-tools {\n  margin: .3rem .5rem;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {\n  border-bottom: 0;\n}\n\n.card.card-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {\n  margin-bottom: 0;\n}\n\n.card.card-tabs.expanding-card .card-header .nav-tabs .nav-item {\n  margin-bottom: -1px;\n}\n\n.card.card-outline-tabs {\n  border-top: 0;\n}\n\n.card.card-outline-tabs .card-header .nav-item:first-child .nav-link {\n  border-left: 0;\n  margin-left: 0;\n}\n\n.card.card-outline-tabs .card-header a {\n  border-top: 3px solid transparent;\n}\n\n.card.card-outline-tabs .card-header a:hover {\n  border-top: 3px solid #dee2e6;\n}\n\n.card.card-outline-tabs .card-header a.active:hover {\n  margin-top: 0;\n}\n\n.card.card-outline-tabs .card-tools {\n  margin: .5rem .5rem .3rem;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs {\n  border-bottom: 0;\n}\n\n.card.card-outline-tabs:not(.expanding-card).collapsed-card .card-header .nav-tabs .nav-item {\n  margin-bottom: 0;\n}\n\n.card.card-outline-tabs.expanding-card .card-header .nav-tabs .nav-item {\n  margin-bottom: -1px;\n}\n\nhtml.maximized-card {\n  overflow: hidden;\n}\n\n.card-header::after,\n.card-body::after,\n.card-footer::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.card-header {\n  background-color: transparent;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  padding: 0.75rem 1.25rem;\n  position: relative;\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n}\n\n.collapsed-card .card-header {\n  border-bottom: 0;\n}\n\n.card-header > .card-tools {\n  float: right;\n  margin-right: -0.625rem;\n}\n\n.card-header > .card-tools .input-group,\n.card-header > .card-tools .nav,\n.card-header > .card-tools .pagination {\n  margin-bottom: -0.3rem;\n  margin-top: -0.3rem;\n}\n\n.card-header > .card-tools [data-toggle=\"tooltip\"] {\n  position: relative;\n}\n\n.card-title {\n  float: left;\n  font-size: 1.1rem;\n  font-weight: 400;\n  margin: 0;\n}\n\n.card-text {\n  clear: both;\n}\n\n.btn-tool {\n  background-color: transparent;\n  color: #adb5bd;\n  font-size: 0.875rem;\n  margin: -0.75rem 0;\n  padding: .25rem .5rem;\n}\n\n.btn-group.show .btn-tool, .btn-tool:hover {\n  color: #495057;\n}\n\n.show .btn-tool, .btn-tool:focus {\n  box-shadow: none !important;\n}\n\n.text-sm .card-title {\n  font-size: 1rem;\n}\n\n.text-sm .nav-link {\n  padding: 0.4rem 0.8rem;\n}\n\n.card-body > .table {\n  margin-bottom: 0;\n}\n\n.card-body > .table > thead > tr > th,\n.card-body > .table > thead > tr > td {\n  border-top-width: 0;\n}\n\n.card-body .fc {\n  margin-top: 5px;\n}\n\n.card-body .full-width-chart {\n  margin: -19px;\n}\n\n.card-body.p-0 .full-width-chart {\n  margin: -9px;\n}\n\n.chart-legend {\n  padding-left: 0;\n  list-style: none;\n  margin: 10px 0;\n}\n\n@media (max-width: 576px) {\n  .chart-legend > li {\n    float: left;\n    margin-right: 10px;\n  }\n}\n\n.card-comments {\n  background-color: #f8f9fa;\n}\n\n.card-comments .card-comment {\n  border-bottom: 1px solid #e9ecef;\n  padding: 8px 0;\n}\n\n.card-comments .card-comment::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.card-comments .card-comment:last-of-type {\n  border-bottom: 0;\n}\n\n.card-comments .card-comment:first-of-type {\n  padding-top: 0;\n}\n\n.card-comments .card-comment img {\n  height: 1.875rem;\n  width: 1.875rem;\n  float: left;\n}\n\n.card-comments .comment-text {\n  color: #78838e;\n  margin-left: 40px;\n}\n\n.card-comments .username {\n  color: #495057;\n  display: block;\n  font-weight: 600;\n}\n\n.card-comments .text-muted {\n  font-size: 12px;\n  font-weight: 400;\n}\n\n.todo-list {\n  list-style: none;\n  margin: 0;\n  overflow: auto;\n  padding: 0;\n}\n\n.todo-list > li {\n  border-radius: 2px;\n  background-color: #f8f9fa;\n  border-left: 2px solid #e9ecef;\n  color: #495057;\n  margin-bottom: 2px;\n  padding: 10px;\n}\n\n.todo-list > li:last-of-type {\n  margin-bottom: 0;\n}\n\n.todo-list > li > input[type=\"checkbox\"] {\n  margin: 0 10px 0 5px;\n}\n\n.todo-list > li .text {\n  display: inline-block;\n  font-weight: 600;\n  margin-left: 5px;\n}\n\n.todo-list > li .badge {\n  font-size: .7rem;\n  margin-left: 10px;\n}\n\n.todo-list > li .tools {\n  color: #dc3545;\n  display: none;\n  float: right;\n}\n\n.todo-list > li .tools > .fa,\n.todo-list > li .tools > .fas,\n.todo-list > li .tools > .far,\n.todo-list > li .tools > .fab,\n.todo-list > li .tools > .fal,\n.todo-list > li .tools > .fad,\n.todo-list > li .tools > .svg-inline--fa,\n.todo-list > li .tools > .ion {\n  cursor: pointer;\n  margin-right: 5px;\n}\n\n.todo-list > li:hover .tools {\n  display: inline-block;\n}\n\n.todo-list > li.done {\n  color: #697582;\n}\n\n.todo-list > li.done .text {\n  font-weight: 500;\n  text-decoration: line-through;\n}\n\n.todo-list > li.done .badge {\n  background-color: #adb5bd !important;\n}\n\n.todo-list .primary {\n  border-left-color: #007bff;\n}\n\n.todo-list .secondary {\n  border-left-color: #6c757d;\n}\n\n.todo-list .success {\n  border-left-color: #28a745;\n}\n\n.todo-list .info {\n  border-left-color: #17a2b8;\n}\n\n.todo-list .warning {\n  border-left-color: #ffc107;\n}\n\n.todo-list .danger {\n  border-left-color: #dc3545;\n}\n\n.todo-list .light {\n  border-left-color: #f8f9fa;\n}\n\n.todo-list .dark {\n  border-left-color: #343a40;\n}\n\n.todo-list .lightblue {\n  border-left-color: #3c8dbc;\n}\n\n.todo-list .navy {\n  border-left-color: #001f3f;\n}\n\n.todo-list .olive {\n  border-left-color: #3d9970;\n}\n\n.todo-list .lime {\n  border-left-color: #01ff70;\n}\n\n.todo-list .fuchsia {\n  border-left-color: #f012be;\n}\n\n.todo-list .maroon {\n  border-left-color: #d81b60;\n}\n\n.todo-list .blue {\n  border-left-color: #007bff;\n}\n\n.todo-list .indigo {\n  border-left-color: #6610f2;\n}\n\n.todo-list .purple {\n  border-left-color: #6f42c1;\n}\n\n.todo-list .pink {\n  border-left-color: #e83e8c;\n}\n\n.todo-list .red {\n  border-left-color: #dc3545;\n}\n\n.todo-list .orange {\n  border-left-color: #fd7e14;\n}\n\n.todo-list .yellow {\n  border-left-color: #ffc107;\n}\n\n.todo-list .green {\n  border-left-color: #28a745;\n}\n\n.todo-list .teal {\n  border-left-color: #20c997;\n}\n\n.todo-list .cyan {\n  border-left-color: #17a2b8;\n}\n\n.todo-list .white {\n  border-left-color: #fff;\n}\n\n.todo-list .gray {\n  border-left-color: #6c757d;\n}\n\n.todo-list .gray-dark {\n  border-left-color: #343a40;\n}\n\n.todo-list .handle {\n  cursor: move;\n  display: inline-block;\n  margin: 0 5px;\n}\n\n.card-input {\n  max-width: 200px;\n}\n\n.card-default .nav-item:first-child .nav-link {\n  border-left: 0;\n}\n\n.modal-dialog .overlay {\n  display: -ms-flexbox;\n  display: flex;\n  position: absolute;\n  left: 0;\n  top: 0;\n  bottom: 0;\n  right: 0;\n  margin: -1px;\n  z-index: 1052;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: rgba(0, 0, 0, 0.7);\n  color: #666f76;\n  border-radius: 0.3rem;\n}\n\n.modal-content.bg-warning .modal-header,\n.modal-content.bg-warning .modal-footer {\n  border-color: #343a40;\n}\n\n.modal-content.bg-primary .close, .modal-content.bg-primary .mailbox-attachment-close, .modal-content.bg-secondary .close, .modal-content.bg-secondary .mailbox-attachment-close, .modal-content.bg-info .close, .modal-content.bg-info .mailbox-attachment-close, .modal-content.bg-danger .close, .modal-content.bg-danger .mailbox-attachment-close, .modal-content.bg-success .close, .modal-content.bg-success .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toasts-top-right {\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 1040;\n}\n\n.toasts-top-right.fixed {\n  position: fixed;\n}\n\n.toasts-top-left {\n  left: 0;\n  position: absolute;\n  top: 0;\n  z-index: 1040;\n}\n\n.toasts-top-left.fixed {\n  position: fixed;\n}\n\n.toasts-bottom-right {\n  bottom: 0;\n  position: absolute;\n  right: 0;\n  z-index: 1040;\n}\n\n.toasts-bottom-right.fixed {\n  position: fixed;\n}\n\n.toasts-bottom-left {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  z-index: 1040;\n}\n\n.toasts-bottom-left.fixed {\n  position: fixed;\n}\n\n.toast.bg-primary {\n  background-color: rgba(0, 123, 255, 0.9) !important;\n}\n\n.toast.bg-primary .close, .toast.bg-primary .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-primary .toast-header {\n  background-color: rgba(0, 123, 255, 0.85);\n  color: #fff;\n}\n\n.toast.bg-secondary {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.toast.bg-secondary .close, .toast.bg-secondary .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-secondary .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.toast.bg-success {\n  background-color: rgba(40, 167, 69, 0.9) !important;\n}\n\n.toast.bg-success .close, .toast.bg-success .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-success .toast-header {\n  background-color: rgba(40, 167, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-info {\n  background-color: rgba(23, 162, 184, 0.9) !important;\n}\n\n.toast.bg-info .close, .toast.bg-info .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-info .toast-header {\n  background-color: rgba(23, 162, 184, 0.85);\n  color: #fff;\n}\n\n.toast.bg-warning {\n  background-color: rgba(255, 193, 7, 0.9) !important;\n}\n\n.toast.bg-warning .toast-header {\n  background-color: rgba(255, 193, 7, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-danger {\n  background-color: rgba(220, 53, 69, 0.9) !important;\n}\n\n.toast.bg-danger .close, .toast.bg-danger .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-danger .toast-header {\n  background-color: rgba(220, 53, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-light {\n  background-color: rgba(248, 249, 250, 0.9) !important;\n}\n\n.toast.bg-light .toast-header {\n  background-color: rgba(248, 249, 250, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.toast.bg-dark .close, .toast.bg-dark .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.toast.bg-lightblue {\n  background-color: rgba(60, 141, 188, 0.9) !important;\n}\n\n.toast.bg-lightblue .close, .toast.bg-lightblue .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-lightblue .toast-header {\n  background-color: rgba(60, 141, 188, 0.85);\n  color: #fff;\n}\n\n.toast.bg-navy {\n  background-color: rgba(0, 31, 63, 0.9) !important;\n}\n\n.toast.bg-navy .close, .toast.bg-navy .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-navy .toast-header {\n  background-color: rgba(0, 31, 63, 0.85);\n  color: #fff;\n}\n\n.toast.bg-olive {\n  background-color: rgba(61, 153, 112, 0.9) !important;\n}\n\n.toast.bg-olive .close, .toast.bg-olive .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-olive .toast-header {\n  background-color: rgba(61, 153, 112, 0.85);\n  color: #fff;\n}\n\n.toast.bg-lime {\n  background-color: rgba(1, 255, 112, 0.9) !important;\n}\n\n.toast.bg-lime .toast-header {\n  background-color: rgba(1, 255, 112, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-fuchsia {\n  background-color: rgba(240, 18, 190, 0.9) !important;\n}\n\n.toast.bg-fuchsia .close, .toast.bg-fuchsia .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-fuchsia .toast-header {\n  background-color: rgba(240, 18, 190, 0.85);\n  color: #fff;\n}\n\n.toast.bg-maroon {\n  background-color: rgba(216, 27, 96, 0.9) !important;\n}\n\n.toast.bg-maroon .close, .toast.bg-maroon .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-maroon .toast-header {\n  background-color: rgba(216, 27, 96, 0.85);\n  color: #fff;\n}\n\n.toast.bg-blue {\n  background-color: rgba(0, 123, 255, 0.9) !important;\n}\n\n.toast.bg-blue .close, .toast.bg-blue .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-blue .toast-header {\n  background-color: rgba(0, 123, 255, 0.85);\n  color: #fff;\n}\n\n.toast.bg-indigo {\n  background-color: rgba(102, 16, 242, 0.9) !important;\n}\n\n.toast.bg-indigo .close, .toast.bg-indigo .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-indigo .toast-header {\n  background-color: rgba(102, 16, 242, 0.85);\n  color: #fff;\n}\n\n.toast.bg-purple {\n  background-color: rgba(111, 66, 193, 0.9) !important;\n}\n\n.toast.bg-purple .close, .toast.bg-purple .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-purple .toast-header {\n  background-color: rgba(111, 66, 193, 0.85);\n  color: #fff;\n}\n\n.toast.bg-pink {\n  background-color: rgba(232, 62, 140, 0.9) !important;\n}\n\n.toast.bg-pink .close, .toast.bg-pink .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-pink .toast-header {\n  background-color: rgba(232, 62, 140, 0.85);\n  color: #fff;\n}\n\n.toast.bg-red {\n  background-color: rgba(220, 53, 69, 0.9) !important;\n}\n\n.toast.bg-red .close, .toast.bg-red .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-red .toast-header {\n  background-color: rgba(220, 53, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-orange {\n  background-color: rgba(253, 126, 20, 0.9) !important;\n}\n\n.toast.bg-orange .toast-header {\n  background-color: rgba(253, 126, 20, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-yellow {\n  background-color: rgba(255, 193, 7, 0.9) !important;\n}\n\n.toast.bg-yellow .toast-header {\n  background-color: rgba(255, 193, 7, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-green {\n  background-color: rgba(40, 167, 69, 0.9) !important;\n}\n\n.toast.bg-green .close, .toast.bg-green .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-green .toast-header {\n  background-color: rgba(40, 167, 69, 0.85);\n  color: #fff;\n}\n\n.toast.bg-teal {\n  background-color: rgba(32, 201, 151, 0.9) !important;\n}\n\n.toast.bg-teal .close, .toast.bg-teal .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-teal .toast-header {\n  background-color: rgba(32, 201, 151, 0.85);\n  color: #fff;\n}\n\n.toast.bg-cyan {\n  background-color: rgba(23, 162, 184, 0.9) !important;\n}\n\n.toast.bg-cyan .close, .toast.bg-cyan .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-cyan .toast-header {\n  background-color: rgba(23, 162, 184, 0.85);\n  color: #fff;\n}\n\n.toast.bg-white {\n  background-color: rgba(255, 255, 255, 0.9) !important;\n}\n\n.toast.bg-white .toast-header {\n  background-color: rgba(255, 255, 255, 0.85);\n  color: #1f2d3d;\n}\n\n.toast.bg-gray {\n  background-color: rgba(108, 117, 125, 0.9) !important;\n}\n\n.toast.bg-gray .close, .toast.bg-gray .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-gray .toast-header {\n  background-color: rgba(108, 117, 125, 0.85);\n  color: #fff;\n}\n\n.toast.bg-gray-dark {\n  background-color: rgba(52, 58, 64, 0.9) !important;\n}\n\n.toast.bg-gray-dark .close, .toast.bg-gray-dark .mailbox-attachment-close {\n  color: #fff;\n  text-shadow: 0 1px 0 #000;\n}\n\n.toast.bg-gray-dark .toast-header {\n  background-color: rgba(52, 58, 64, 0.85);\n  color: #fff;\n}\n\n.btn.disabled, .btn:disabled {\n  cursor: not-allowed;\n}\n\n.btn.btn-flat {\n  border-radius: 0;\n  border-width: 1px;\n  box-shadow: none;\n}\n\n.btn.btn-file {\n  overflow: hidden;\n  position: relative;\n}\n\n.btn.btn-file > input[type=\"file\"] {\n  background-color: #fff;\n  cursor: inherit;\n  display: block;\n  font-size: 100px;\n  min-height: 100%;\n  min-width: 100%;\n  opacity: 0;\n  outline: none;\n  position: absolute;\n  right: 0;\n  text-align: right;\n  top: 0;\n}\n\n.text-sm .btn {\n  font-size: 0.875rem !important;\n}\n\n.btn-default {\n  background-color: #f8f9fa;\n  border-color: #ddd;\n  color: #444;\n}\n\n.btn-default:hover, .btn-default:active, .btn-default.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.btn-default.disabled, .btn-default:disabled {\n  color: #444;\n  background-color: #f8f9fa;\n}\n\n.btn-outline-light {\n  color: #bdc6d0;\n  border-color: #bdc6d0;\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n  color: #bdc6d0;\n  border-color: #bdc6d0;\n}\n\n.btn-app {\n  border-radius: 3px;\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  color: #6c757d;\n  font-size: 12px;\n  height: 60px;\n  margin: 0 0 10px 10px;\n  min-width: 80px;\n  padding: 15px 5px;\n  position: relative;\n  text-align: center;\n}\n\n.btn-app > .fa,\n.btn-app > .fas,\n.btn-app > .far,\n.btn-app > .fab,\n.btn-app > .fal,\n.btn-app > .fad,\n.btn-app > .svg-inline--fa,\n.btn-app > .ion {\n  display: block;\n  font-size: 20px;\n}\n\n.btn-app > .svg-inline--fa {\n  margin: 0 auto;\n}\n\n.btn-app:hover {\n  background-color: #f8f9fa;\n  border-color: #aaaaaa;\n  color: #444;\n}\n\n.btn-app:active, .btn-app:focus {\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn-app > .badge {\n  font-size: 10px;\n  font-weight: 400;\n  position: absolute;\n  right: -10px;\n  top: -3px;\n}\n\n.btn-xs {\n  padding: 0.125rem 0.25rem;\n  font-size: 0.75rem;\n  line-height: 1.5;\n  border-radius: 0.15rem;\n}\n\n.callout {\n  border-radius: 0.25rem;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n  background-color: #fff;\n  border-left: 5px solid #e9ecef;\n  margin-bottom: 1rem;\n  padding: 1rem;\n}\n\n.callout a {\n  color: #495057;\n  text-decoration: underline;\n}\n\n.callout a:hover {\n  color: #e9ecef;\n}\n\n.callout p:last-child {\n  margin-bottom: 0;\n}\n\n.callout.callout-danger {\n  border-left-color: #bd2130;\n}\n\n.callout.callout-warning {\n  border-left-color: #d39e00;\n}\n\n.callout.callout-info {\n  border-left-color: #117a8b;\n}\n\n.callout.callout-success {\n  border-left-color: #1e7e34;\n}\n\n.alert .icon {\n  margin-right: 10px;\n}\n\n.alert .close, .alert .mailbox-attachment-close {\n  color: #000;\n  opacity: .2;\n}\n\n.alert .close:hover, .alert .mailbox-attachment-close:hover {\n  opacity: .5;\n}\n\n.alert a {\n  color: #fff;\n  text-decoration: underline;\n}\n\n.alert-primary {\n  color: #fff;\n  background-color: #007bff;\n  border-color: #006fe6;\n}\n\n.alert-default-primary {\n  color: #004085;\n  background-color: #cce5ff;\n  border-color: #b8daff;\n}\n\n.alert-default-primary hr {\n  border-top-color: #9fcdff;\n}\n\n.alert-default-primary .alert-link {\n  color: #002752;\n}\n\n.alert-secondary {\n  color: #fff;\n  background-color: #6c757d;\n  border-color: #60686f;\n}\n\n.alert-default-secondary {\n  color: #383d41;\n  background-color: #e2e3e5;\n  border-color: #d6d8db;\n}\n\n.alert-default-secondary hr {\n  border-top-color: #c8cbcf;\n}\n\n.alert-default-secondary .alert-link {\n  color: #202326;\n}\n\n.alert-success {\n  color: #fff;\n  background-color: #28a745;\n  border-color: #23923d;\n}\n\n.alert-default-success {\n  color: #155724;\n  background-color: #d4edda;\n  border-color: #c3e6cb;\n}\n\n.alert-default-success hr {\n  border-top-color: #b1dfbb;\n}\n\n.alert-default-success .alert-link {\n  color: #0b2e13;\n}\n\n.alert-info {\n  color: #fff;\n  background-color: #17a2b8;\n  border-color: #148ea1;\n}\n\n.alert-default-info {\n  color: #0c5460;\n  background-color: #d1ecf1;\n  border-color: #bee5eb;\n}\n\n.alert-default-info hr {\n  border-top-color: #abdde5;\n}\n\n.alert-default-info .alert-link {\n  color: #062c33;\n}\n\n.alert-warning {\n  color: #1f2d3d;\n  background-color: #ffc107;\n  border-color: #edb100;\n}\n\n.alert-default-warning {\n  color: #856404;\n  background-color: #fff3cd;\n  border-color: #ffeeba;\n}\n\n.alert-default-warning hr {\n  border-top-color: #ffe8a1;\n}\n\n.alert-default-warning .alert-link {\n  color: #533f03;\n}\n\n.alert-danger {\n  color: #fff;\n  background-color: #dc3545;\n  border-color: #d32535;\n}\n\n.alert-default-danger {\n  color: #721c24;\n  background-color: #f8d7da;\n  border-color: #f5c6cb;\n}\n\n.alert-default-danger hr {\n  border-top-color: #f1b0b7;\n}\n\n.alert-default-danger .alert-link {\n  color: #491217;\n}\n\n.alert-light {\n  color: #1f2d3d;\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n}\n\n.alert-default-light {\n  color: #818182;\n  background-color: #fefefe;\n  border-color: #fdfdfe;\n}\n\n.alert-default-light hr {\n  border-top-color: #ececf6;\n}\n\n.alert-default-light .alert-link {\n  color: #686868;\n}\n\n.alert-dark {\n  color: #fff;\n  background-color: #343a40;\n  border-color: #292d32;\n}\n\n.alert-default-dark {\n  color: #1b1e21;\n  background-color: #d6d8d9;\n  border-color: #c6c8ca;\n}\n\n.alert-default-dark hr {\n  border-top-color: #b9bbbe;\n}\n\n.alert-default-dark .alert-link {\n  color: #040505;\n}\n\n.table:not(.table-dark) {\n  color: inherit;\n}\n\n.table.table-head-fixed thead tr:nth-child(1) th {\n  background-color: #fff;\n  border-bottom: 0;\n  box-shadow: inset 0 1px 0 #dee2e6, inset 0 -1px 0 #dee2e6;\n  position: -webkit-sticky;\n  position: sticky;\n  top: 0;\n  z-index: 10;\n}\n\n.table.table-head-fixed.table-dark thead tr:nth-child(1) th {\n  background-color: #212529;\n  box-shadow: inset 0 1px 0 #383f45, inset 0 -1px 0 #383f45;\n}\n\n.table.no-border,\n.table.no-border td,\n.table.no-border th {\n  border: 0;\n}\n\n.table.text-center,\n.table.text-center td,\n.table.text-center th {\n  text-align: center;\n}\n\n.table.table-valign-middle thead > tr > th,\n.table.table-valign-middle thead > tr > td,\n.table.table-valign-middle tbody > tr > th,\n.table.table-valign-middle tbody > tr > td {\n  vertical-align: middle;\n}\n\n.card-body.p-0 .table thead > tr > th:first-of-type,\n.card-body.p-0 .table thead > tr > td:first-of-type,\n.card-body.p-0 .table tfoot > tr > th:first-of-type,\n.card-body.p-0 .table tfoot > tr > td:first-of-type,\n.card-body.p-0 .table tbody > tr > th:first-of-type,\n.card-body.p-0 .table tbody > tr > td:first-of-type {\n  padding-left: 1.5rem;\n}\n\n.card-body.p-0 .table thead > tr > th:last-of-type,\n.card-body.p-0 .table thead > tr > td:last-of-type,\n.card-body.p-0 .table tfoot > tr > th:last-of-type,\n.card-body.p-0 .table tfoot > tr > td:last-of-type,\n.card-body.p-0 .table tbody > tr > th:last-of-type,\n.card-body.p-0 .table tbody > tr > td:last-of-type {\n  padding-right: 1.5rem;\n}\n\n.table-hover tbody tr.expandable-body:hover {\n  background-color: inherit !important;\n}\n\n[data-widget=\"expandable-table\"] {\n  cursor: pointer;\n}\n\n[data-widget=\"expandable-table\"] i.expandable-table-caret {\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n[data-widget=\"expandable-table\"][aria-expanded=\"true\"] i.expandable-table-caret[class*=\"right\"] {\n  -webkit-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n[data-widget=\"expandable-table\"][aria-expanded=\"true\"] i.expandable-table-caret[class*=\"left\"] {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n[aria-expanded=\"true\"] {\n  cursor: pointer;\n}\n\n[aria-expanded=\"true\"] i.expandable-table-caret {\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n[aria-expanded=\"true\"] [data-widget=\"expandable-table\"] i.expandable-table-caret[class*=\"right\"] {\n  -webkit-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n[aria-expanded=\"true\"] [data-widget=\"expandable-table\"] i.expandable-table-caret[class*=\"left\"] {\n  -webkit-transform: rotate(-90deg);\n  transform: rotate(-90deg);\n}\n\n.expandable-body > td {\n  padding: 0 !important;\n  width: 100%;\n}\n\n.expandable-body > td > div,\n.expandable-body > td > p {\n  padding: 0.75rem;\n}\n\n.expandable-body .table {\n  width: calc(100% - 0.75rem);\n  margin: 0 0 0 0.75rem;\n}\n\n.expandable-body .table tr:first-child td,\n.expandable-body .table tr:first-child th {\n  border-top: none;\n}\n\n.carousel-control-prev .carousel-control-custom-icon {\n  margin-left: -20px;\n}\n\n.carousel-control-next .carousel-control-custom-icon {\n  margin-right: 20px;\n}\n\n.carousel-control-custom-icon > .fa,\n.carousel-control-custom-icon > .fas,\n.carousel-control-custom-icon > .far,\n.carousel-control-custom-icon > .fab,\n.carousel-control-custom-icon > .fal,\n.carousel-control-custom-icon > .fad,\n.carousel-control-custom-icon > .svg-inline--fa,\n.carousel-control-custom-icon > .ion {\n  display: inline-block;\n  font-size: 40px;\n  margin-top: -20px;\n  position: absolute;\n  top: 50%;\n  z-index: 5;\n}\n\n.close, .mailbox-attachment-close {\n  float: right;\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: .5;\n}\n\n.close:hover, .mailbox-attachment-close:hover {\n  color: #000;\n  text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .mailbox-attachment-close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus, .mailbox-attachment-close:not(:disabled):not(.disabled):focus {\n  opacity: .75;\n}\n\n.close:focus, .mailbox-attachment-close:focus {\n  outline: none;\n}\n\nbutton.close, button.mailbox-attachment-close {\n  padding: 0;\n  background-color: transparent;\n  border: 0;\n}\n\na.close.disabled, a.disabled.mailbox-attachment-close {\n  pointer-events: none;\n}\n\n.small-box {\n  border-radius: 0.25rem;\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  display: block;\n  margin-bottom: 20px;\n  position: relative;\n}\n\n.small-box > .inner {\n  padding: 10px;\n}\n\n.small-box > .small-box-footer {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: rgba(255, 255, 255, 0.8);\n  display: block;\n  padding: 3px 0;\n  position: relative;\n  text-align: center;\n  text-decoration: none;\n  z-index: 10;\n}\n\n.small-box > .small-box-footer:hover {\n  background-color: rgba(0, 0, 0, 0.15);\n  color: #fff;\n}\n\n.small-box h3 {\n  font-size: 2.2rem;\n  font-weight: 700;\n  margin: 0 0 10px;\n  padding: 0;\n  white-space: nowrap;\n}\n\n@media (min-width: 992px) {\n  .col-xl-2 .small-box h3,\n  .col-lg-2 .small-box h3,\n  .col-md-2 .small-box h3 {\n    font-size: 1.6rem;\n  }\n  .col-xl-3 .small-box h3,\n  .col-lg-3 .small-box h3,\n  .col-md-3 .small-box h3 {\n    font-size: 1.6rem;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl-2 .small-box h3,\n  .col-lg-2 .small-box h3,\n  .col-md-2 .small-box h3 {\n    font-size: 2.2rem;\n  }\n  .col-xl-3 .small-box h3,\n  .col-lg-3 .small-box h3,\n  .col-md-3 .small-box h3 {\n    font-size: 2.2rem;\n  }\n}\n\n.small-box p {\n  font-size: 1rem;\n}\n\n.small-box p > small {\n  color: #f8f9fa;\n  display: block;\n  font-size: .9rem;\n  margin-top: 5px;\n}\n\n.small-box h3,\n.small-box p {\n  z-index: 5;\n}\n\n.small-box .icon {\n  color: rgba(0, 0, 0, 0.15);\n  z-index: 0;\n}\n\n.small-box .icon > i {\n  font-size: 90px;\n  position: absolute;\n  right: 15px;\n  top: 15px;\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n.small-box .icon > i.fa, .small-box .icon > i.fas, .small-box .icon > i.far, .small-box .icon > i.fab, .small-box .icon > i.fal, .small-box .icon > i.fad, .small-box .icon > i.ion {\n  font-size: 70px;\n  top: 20px;\n}\n\n.small-box .icon svg {\n  font-size: 70px;\n  position: absolute;\n  right: 15px;\n  top: 15px;\n  transition: -webkit-transform 0.3s linear;\n  transition: transform 0.3s linear;\n  transition: transform 0.3s linear, -webkit-transform 0.3s linear;\n}\n\n.small-box:hover {\n  text-decoration: none;\n}\n\n.small-box:hover .icon > i, .small-box:hover .icon > i.fa, .small-box:hover .icon > i.fas, .small-box:hover .icon > i.far, .small-box:hover .icon > i.fab, .small-box:hover .icon > i.fal, .small-box:hover .icon > i.fad, .small-box:hover .icon > i.ion {\n  -webkit-transform: scale(1.1);\n  transform: scale(1.1);\n}\n\n.small-box:hover .icon > svg {\n  -webkit-transform: scale(1.1);\n  transform: scale(1.1);\n}\n\n@media (max-width: 767.98px) {\n  .small-box {\n    text-align: center;\n  }\n  .small-box .icon {\n    display: none;\n  }\n  .small-box p {\n    font-size: 12px;\n  }\n}\n\n.info-box {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  display: -ms-flexbox;\n  display: flex;\n  margin-bottom: 1rem;\n  min-height: 80px;\n  padding: .5rem;\n  position: relative;\n  width: 100%;\n}\n\n.info-box .progress {\n  background-color: rgba(0, 0, 0, 0.125);\n  height: 2px;\n  margin: 5px 0;\n}\n\n.info-box .progress .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box-icon {\n  border-radius: 0.25rem;\n  -ms-flex-align: center;\n  align-items: center;\n  display: -ms-flexbox;\n  display: flex;\n  font-size: 1.875rem;\n  -ms-flex-pack: center;\n  justify-content: center;\n  text-align: center;\n  width: 70px;\n}\n\n.info-box .info-box-icon > img {\n  max-width: 100%;\n}\n\n.info-box .info-box-content {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  -ms-flex-pack: center;\n  justify-content: center;\n  line-height: 1.8;\n  -ms-flex: 1;\n  flex: 1;\n  padding: 0 10px;\n  overflow: hidden;\n}\n\n.info-box .info-box-number {\n  display: block;\n  margin-top: .25rem;\n  font-weight: 700;\n}\n\n.info-box .progress-description,\n.info-box .info-box-text {\n  display: block;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.info-box .info-box .bg-primary,\n.info-box .info-box .bg-gradient-primary {\n  color: #fff;\n}\n\n.info-box .info-box .bg-primary .progress-bar,\n.info-box .info-box .bg-gradient-primary .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-secondary,\n.info-box .info-box .bg-gradient-secondary {\n  color: #fff;\n}\n\n.info-box .info-box .bg-secondary .progress-bar,\n.info-box .info-box .bg-gradient-secondary .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-success,\n.info-box .info-box .bg-gradient-success {\n  color: #fff;\n}\n\n.info-box .info-box .bg-success .progress-bar,\n.info-box .info-box .bg-gradient-success .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-info,\n.info-box .info-box .bg-gradient-info {\n  color: #fff;\n}\n\n.info-box .info-box .bg-info .progress-bar,\n.info-box .info-box .bg-gradient-info .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-warning,\n.info-box .info-box .bg-gradient-warning {\n  color: #1f2d3d;\n}\n\n.info-box .info-box .bg-warning .progress-bar,\n.info-box .info-box .bg-gradient-warning .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.info-box .info-box .bg-danger,\n.info-box .info-box .bg-gradient-danger {\n  color: #fff;\n}\n\n.info-box .info-box .bg-danger .progress-bar,\n.info-box .info-box .bg-gradient-danger .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box .bg-light,\n.info-box .info-box .bg-gradient-light {\n  color: #1f2d3d;\n}\n\n.info-box .info-box .bg-light .progress-bar,\n.info-box .info-box .bg-gradient-light .progress-bar {\n  background-color: #1f2d3d;\n}\n\n.info-box .info-box .bg-dark,\n.info-box .info-box .bg-gradient-dark {\n  color: #fff;\n}\n\n.info-box .info-box .bg-dark .progress-bar,\n.info-box .info-box .bg-gradient-dark .progress-bar {\n  background-color: #fff;\n}\n\n.info-box .info-box-more {\n  display: block;\n}\n\n.info-box .progress-description {\n  margin: 0;\n}\n\n@media (min-width: 768px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    display: none;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    display: none;\n  }\n}\n\n@media (min-width: 992px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    font-size: 0.75rem;\n    display: block;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    font-size: 0.75rem;\n    display: block;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-xl-2 .info-box .progress-description,\n  .col-lg-2 .info-box .progress-description,\n  .col-md-2 .info-box .progress-description {\n    font-size: 1rem;\n    display: block;\n  }\n  .col-xl-3 .info-box .progress-description,\n  .col-lg-3 .info-box .progress-description,\n  .col-md-3 .info-box .progress-description {\n    font-size: 1rem;\n    display: block;\n  }\n}\n\n.timeline {\n  margin: 0 0 45px;\n  padding: 0;\n  position: relative;\n}\n\n.timeline::before {\n  border-radius: 0.25rem;\n  background-color: #dee2e6;\n  bottom: 0;\n  content: \"\";\n  left: 31px;\n  margin: 0;\n  position: absolute;\n  top: 0;\n  width: 4px;\n}\n\n.timeline > div {\n  margin-bottom: 15px;\n  margin-right: 10px;\n  position: relative;\n}\n\n.timeline > div::before, .timeline > div::after {\n  content: \"\";\n  display: table;\n}\n\n.timeline > div > .timeline-item {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  color: #495057;\n  margin-left: 60px;\n  margin-right: 15px;\n  margin-top: 0;\n  padding: 0;\n  position: relative;\n}\n\n.timeline > div > .timeline-item > .time {\n  color: #999;\n  float: right;\n  font-size: 12px;\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-header {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  color: #495057;\n  font-size: 16px;\n  line-height: 1.1;\n  margin: 0;\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-header > a {\n  font-weight: 600;\n}\n\n.timeline > div > .timeline-item > .timeline-body,\n.timeline > div > .timeline-item > .timeline-footer {\n  padding: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-body > img {\n  margin: 10px;\n}\n\n.timeline > div > .timeline-item > .timeline-body > dl,\n.timeline > div > .timeline-item > .timeline-body ol,\n.timeline > div > .timeline-item > .timeline-body ul {\n  margin: 0;\n}\n\n.timeline > div > .timeline-item > .timeline-footer > a {\n  color: #fff;\n}\n\n.timeline > div > .fa,\n.timeline > div > .fas,\n.timeline > div > .far,\n.timeline > div > .fab,\n.timeline > div > .fal,\n.timeline > div > .fad,\n.timeline > div > .svg-inline--fa,\n.timeline > div > .ion {\n  background-color: #adb5bd;\n  border-radius: 50%;\n  font-size: 16px;\n  height: 30px;\n  left: 18px;\n  line-height: 30px;\n  position: absolute;\n  text-align: center;\n  top: 0;\n  width: 30px;\n}\n\n.timeline > div > .svg-inline--fa {\n  padding: 7px;\n}\n\n.timeline > .time-label > span {\n  border-radius: 4px;\n  background-color: #fff;\n  display: inline-block;\n  font-weight: 600;\n  padding: 5px;\n}\n\n.timeline-inverse > div > .timeline-item {\n  box-shadow: none;\n  background-color: #f8f9fa;\n  border: 1px solid #dee2e6;\n}\n\n.timeline-inverse > div > .timeline-item > .timeline-header {\n  border-bottom-color: #dee2e6;\n}\n\n.products-list {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n.products-list > .item {\n  border-radius: 0.25rem;\n  background-color: #fff;\n  padding: 10px 0;\n}\n\n.products-list > .item::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.products-list .product-img {\n  float: left;\n}\n\n.products-list .product-img img {\n  height: 50px;\n  width: 50px;\n}\n\n.products-list .product-info {\n  margin-left: 60px;\n}\n\n.products-list .product-title {\n  font-weight: 600;\n}\n\n.products-list .product-description {\n  color: #6c757d;\n  display: block;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.product-list-in-card > .item {\n  border-radius: 0;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.product-list-in-card > .item:last-of-type {\n  border-bottom-width: 0;\n}\n\n.direct-chat .card-body {\n  overflow-x: hidden;\n  padding: 0;\n  position: relative;\n}\n\n.direct-chat.chat-pane-open .direct-chat-contacts {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n\n.direct-chat.timestamp-light .direct-chat-timestamp {\n  color: #30465f;\n}\n\n.direct-chat.timestamp-dark .direct-chat-timestamp {\n  color: #cccccc;\n}\n\n.direct-chat-messages {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n  height: 250px;\n  overflow: auto;\n  padding: 10px;\n}\n\n.direct-chat-msg,\n.direct-chat-text {\n  display: block;\n}\n\n.direct-chat-msg {\n  margin-bottom: 10px;\n}\n\n.direct-chat-msg::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.direct-chat-messages,\n.direct-chat-contacts {\n  transition: -webkit-transform .5s ease-in-out;\n  transition: transform .5s ease-in-out;\n  transition: transform .5s ease-in-out, -webkit-transform .5s ease-in-out;\n}\n\n.direct-chat-text {\n  border-radius: 0.3rem;\n  background-color: #d2d6de;\n  border: 1px solid #d2d6de;\n  color: #444;\n  margin: 5px 0 0 50px;\n  padding: 5px 10px;\n  position: relative;\n}\n\n.direct-chat-text::after, .direct-chat-text::before {\n  border: solid transparent;\n  border-right-color: #d2d6de;\n  content: \" \";\n  height: 0;\n  pointer-events: none;\n  position: absolute;\n  right: 100%;\n  top: 15px;\n  width: 0;\n}\n\n.direct-chat-text::after {\n  border-width: 5px;\n  margin-top: -5px;\n}\n\n.direct-chat-text::before {\n  border-width: 6px;\n  margin-top: -6px;\n}\n\n.right .direct-chat-text {\n  margin-left: 0;\n  margin-right: 50px;\n}\n\n.right .direct-chat-text::after, .right .direct-chat-text::before {\n  border-left-color: #d2d6de;\n  border-right-color: transparent;\n  left: 100%;\n  right: auto;\n}\n\n.direct-chat-img {\n  border-radius: 50%;\n  float: left;\n  height: 40px;\n  width: 40px;\n}\n\n.right .direct-chat-img {\n  float: right;\n}\n\n.direct-chat-infos {\n  display: block;\n  font-size: 0.875rem;\n  margin-bottom: 2px;\n}\n\n.direct-chat-name {\n  font-weight: 600;\n}\n\n.direct-chat-timestamp {\n  color: #697582;\n}\n\n.direct-chat-contacts-open .direct-chat-contacts {\n  -webkit-transform: translate(0, 0);\n  transform: translate(0, 0);\n}\n\n.direct-chat-contacts {\n  -webkit-transform: translate(101%, 0);\n  transform: translate(101%, 0);\n  background-color: #343a40;\n  bottom: 0;\n  color: #fff;\n  height: 250px;\n  overflow: auto;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.direct-chat-contacts-light {\n  background-color: #f8f9fa;\n}\n\n.direct-chat-contacts-light .contacts-list-name {\n  color: #495057;\n}\n\n.direct-chat-contacts-light .contacts-list-date {\n  color: #6c757d;\n}\n\n.direct-chat-contacts-light .contacts-list-msg {\n  color: #545b62;\n}\n\n.contacts-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.contacts-list > li {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n  margin: 0;\n  padding: 10px;\n}\n\n.contacts-list > li::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.contacts-list > li:last-of-type {\n  border-bottom: 0;\n}\n\n.contacts-list-img {\n  border-radius: 50%;\n  float: left;\n  width: 40px;\n}\n\n.contacts-list-info {\n  color: #fff;\n  margin-left: 45px;\n}\n\n.contacts-list-name,\n.contacts-list-status {\n  display: block;\n}\n\n.contacts-list-name {\n  font-weight: 600;\n}\n\n.contacts-list-status {\n  font-size: 0.875rem;\n}\n\n.contacts-list-date {\n  color: #ced4da;\n  font-weight: 400;\n}\n\n.contacts-list-msg {\n  color: #b1bbc4;\n}\n\n.direct-chat-primary .right > .direct-chat-text {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.direct-chat-primary .right > .direct-chat-text::after, .direct-chat-primary .right > .direct-chat-text::before {\n  border-left-color: #007bff;\n}\n\n.direct-chat-secondary .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.direct-chat-secondary .right > .direct-chat-text::after, .direct-chat-secondary .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.direct-chat-success .right > .direct-chat-text {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.direct-chat-success .right > .direct-chat-text::after, .direct-chat-success .right > .direct-chat-text::before {\n  border-left-color: #28a745;\n}\n\n.direct-chat-info .right > .direct-chat-text {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.direct-chat-info .right > .direct-chat-text::after, .direct-chat-info .right > .direct-chat-text::before {\n  border-left-color: #17a2b8;\n}\n\n.direct-chat-warning .right > .direct-chat-text {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.direct-chat-warning .right > .direct-chat-text::after, .direct-chat-warning .right > .direct-chat-text::before {\n  border-left-color: #ffc107;\n}\n\n.direct-chat-danger .right > .direct-chat-text {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.direct-chat-danger .right > .direct-chat-text::after, .direct-chat-danger .right > .direct-chat-text::before {\n  border-left-color: #dc3545;\n}\n\n.direct-chat-light .right > .direct-chat-text {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.direct-chat-light .right > .direct-chat-text::after, .direct-chat-light .right > .direct-chat-text::before {\n  border-left-color: #f8f9fa;\n}\n\n.direct-chat-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.direct-chat-dark .right > .direct-chat-text::after, .direct-chat-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.direct-chat-lightblue .right > .direct-chat-text {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.direct-chat-lightblue .right > .direct-chat-text::after, .direct-chat-lightblue .right > .direct-chat-text::before {\n  border-left-color: #3c8dbc;\n}\n\n.direct-chat-navy .right > .direct-chat-text {\n  background-color: #001f3f;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.direct-chat-navy .right > .direct-chat-text::after, .direct-chat-navy .right > .direct-chat-text::before {\n  border-left-color: #001f3f;\n}\n\n.direct-chat-olive .right > .direct-chat-text {\n  background-color: #3d9970;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.direct-chat-olive .right > .direct-chat-text::after, .direct-chat-olive .right > .direct-chat-text::before {\n  border-left-color: #3d9970;\n}\n\n.direct-chat-lime .right > .direct-chat-text {\n  background-color: #01ff70;\n  border-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.direct-chat-lime .right > .direct-chat-text::after, .direct-chat-lime .right > .direct-chat-text::before {\n  border-left-color: #01ff70;\n}\n\n.direct-chat-fuchsia .right > .direct-chat-text {\n  background-color: #f012be;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.direct-chat-fuchsia .right > .direct-chat-text::after, .direct-chat-fuchsia .right > .direct-chat-text::before {\n  border-left-color: #f012be;\n}\n\n.direct-chat-maroon .right > .direct-chat-text {\n  background-color: #d81b60;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.direct-chat-maroon .right > .direct-chat-text::after, .direct-chat-maroon .right > .direct-chat-text::before {\n  border-left-color: #d81b60;\n}\n\n.direct-chat-blue .right > .direct-chat-text {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.direct-chat-blue .right > .direct-chat-text::after, .direct-chat-blue .right > .direct-chat-text::before {\n  border-left-color: #007bff;\n}\n\n.direct-chat-indigo .right > .direct-chat-text {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.direct-chat-indigo .right > .direct-chat-text::after, .direct-chat-indigo .right > .direct-chat-text::before {\n  border-left-color: #6610f2;\n}\n\n.direct-chat-purple .right > .direct-chat-text {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.direct-chat-purple .right > .direct-chat-text::after, .direct-chat-purple .right > .direct-chat-text::before {\n  border-left-color: #6f42c1;\n}\n\n.direct-chat-pink .right > .direct-chat-text {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.direct-chat-pink .right > .direct-chat-text::after, .direct-chat-pink .right > .direct-chat-text::before {\n  border-left-color: #e83e8c;\n}\n\n.direct-chat-red .right > .direct-chat-text {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.direct-chat-red .right > .direct-chat-text::after, .direct-chat-red .right > .direct-chat-text::before {\n  border-left-color: #dc3545;\n}\n\n.direct-chat-orange .right > .direct-chat-text {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.direct-chat-orange .right > .direct-chat-text::after, .direct-chat-orange .right > .direct-chat-text::before {\n  border-left-color: #fd7e14;\n}\n\n.direct-chat-yellow .right > .direct-chat-text {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.direct-chat-yellow .right > .direct-chat-text::after, .direct-chat-yellow .right > .direct-chat-text::before {\n  border-left-color: #ffc107;\n}\n\n.direct-chat-green .right > .direct-chat-text {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.direct-chat-green .right > .direct-chat-text::after, .direct-chat-green .right > .direct-chat-text::before {\n  border-left-color: #28a745;\n}\n\n.direct-chat-teal .right > .direct-chat-text {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.direct-chat-teal .right > .direct-chat-text::after, .direct-chat-teal .right > .direct-chat-text::before {\n  border-left-color: #20c997;\n}\n\n.direct-chat-cyan .right > .direct-chat-text {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.direct-chat-cyan .right > .direct-chat-text::after, .direct-chat-cyan .right > .direct-chat-text::before {\n  border-left-color: #17a2b8;\n}\n\n.direct-chat-white .right > .direct-chat-text {\n  background-color: #fff;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.direct-chat-white .right > .direct-chat-text::after, .direct-chat-white .right > .direct-chat-text::before {\n  border-left-color: #fff;\n}\n\n.direct-chat-gray .right > .direct-chat-text {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.direct-chat-gray .right > .direct-chat-text::after, .direct-chat-gray .right > .direct-chat-text::before {\n  border-left-color: #6c757d;\n}\n\n.direct-chat-gray-dark .right > .direct-chat-text {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.direct-chat-gray-dark .right > .direct-chat-text::after, .direct-chat-gray-dark .right > .direct-chat-text::before {\n  border-left-color: #343a40;\n}\n\n.users-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.users-list > li {\n  float: left;\n  padding: 10px;\n  text-align: center;\n  width: 25%;\n}\n\n.users-list > li img {\n  border-radius: 50%;\n  height: auto;\n  max-width: 100%;\n}\n\n.users-list > li > a:hover,\n.users-list > li > a:hover .users-list-name {\n  color: #999;\n}\n\n.users-list-name,\n.users-list-date {\n  display: block;\n}\n\n.users-list-name {\n  color: #495057;\n  font-size: 0.875rem;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.users-list-date {\n  color: #748290;\n  font-size: 12px;\n}\n\n.card-widget {\n  border: 0;\n  position: relative;\n}\n\n.widget-user .widget-user-header {\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n  height: 135px;\n  padding: 1rem;\n  text-align: center;\n}\n\n.widget-user .widget-user-username {\n  font-size: 25px;\n  font-weight: 300;\n  margin-bottom: 0;\n  margin-top: 0;\n  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);\n}\n\n.widget-user .widget-user-desc {\n  margin-top: 0;\n}\n\n.widget-user .widget-user-image {\n  left: 50%;\n  margin-left: -45px;\n  position: absolute;\n  top: 80px;\n}\n\n.widget-user .widget-user-image > img {\n  border: 3px solid #fff;\n  height: auto;\n  width: 90px;\n}\n\n.widget-user .card-footer {\n  padding-top: 50px;\n}\n\n.widget-user-2 .widget-user-header {\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n  padding: 1rem;\n}\n\n.widget-user-2 .widget-user-username {\n  font-size: 25px;\n  font-weight: 300;\n  margin-bottom: 5px;\n  margin-top: 5px;\n}\n\n.widget-user-2 .widget-user-desc {\n  margin-top: 0;\n}\n\n.widget-user-2 .widget-user-username,\n.widget-user-2 .widget-user-desc {\n  margin-left: 75px;\n}\n\n.widget-user-2 .widget-user-image > img {\n  float: left;\n  height: auto;\n  width: 65px;\n}\n\n.mailbox-messages > .table {\n  margin: 0;\n}\n\n.mailbox-controls {\n  padding: 5px;\n}\n\n.mailbox-controls.with-border {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.mailbox-read-info {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  padding: 10px;\n}\n\n.mailbox-read-info h3 {\n  font-size: 20px;\n  margin: 0;\n}\n\n.mailbox-read-info h5 {\n  margin: 0;\n  padding: 5px 0 0;\n}\n\n.mailbox-read-time {\n  color: #999;\n  font-size: 13px;\n}\n\n.mailbox-read-message {\n  padding: 10px;\n}\n\n.mailbox-attachments {\n  padding-left: 0;\n  list-style: none;\n}\n\n.mailbox-attachments li {\n  border: 1px solid #eee;\n  float: left;\n  margin-bottom: 10px;\n  margin-right: 10px;\n  width: 200px;\n}\n\n.mailbox-attachment-name {\n  color: #666;\n  font-weight: 700;\n}\n\n.mailbox-attachment-icon,\n.mailbox-attachment-info,\n.mailbox-attachment-size {\n  display: block;\n}\n\n.mailbox-attachment-info {\n  background-color: #f8f9fa;\n  padding: 10px;\n}\n\n.mailbox-attachment-size {\n  color: #999;\n  font-size: 12px;\n}\n\n.mailbox-attachment-size > span {\n  display: inline-block;\n  padding-top: .75rem;\n}\n\n.mailbox-attachment-icon {\n  color: #666;\n  font-size: 65px;\n  max-height: 132.5px;\n  padding: 20px 10px;\n  text-align: center;\n}\n\n.mailbox-attachment-icon.has-img {\n  padding: 0;\n}\n\n.mailbox-attachment-icon.has-img > img {\n  height: auto;\n  max-width: 100%;\n}\n\n.lockscreen {\n  background-color: #e9ecef;\n}\n\n.lockscreen .lockscreen-name {\n  font-weight: 600;\n  text-align: center;\n}\n\n.lockscreen-logo {\n  font-size: 35px;\n  font-weight: 300;\n  margin-bottom: 25px;\n  text-align: center;\n}\n\n.lockscreen-logo a {\n  color: #495057;\n}\n\n.lockscreen-wrapper {\n  margin: 0 auto;\n  margin-top: 10%;\n  max-width: 400px;\n}\n\n.lockscreen-item {\n  border-radius: 4px;\n  background-color: #fff;\n  margin: 10px auto 30px;\n  padding: 0;\n  position: relative;\n  width: 290px;\n}\n\n.lockscreen-image {\n  border-radius: 50%;\n  background-color: #fff;\n  left: -10px;\n  padding: 5px;\n  position: absolute;\n  top: -25px;\n  z-index: 10;\n}\n\n.lockscreen-image > img {\n  border-radius: 50%;\n  height: 70px;\n  width: 70px;\n}\n\n.lockscreen-credentials {\n  margin-left: 70px;\n}\n\n.lockscreen-credentials .form-control {\n  border: 0;\n}\n\n.lockscreen-credentials .btn {\n  background-color: #fff;\n  border: 0;\n  padding: 0 10px;\n}\n\n.lockscreen-footer {\n  margin-top: 10px;\n}\n\n.login-logo,\n.register-logo {\n  font-size: 2.1rem;\n  font-weight: 300;\n  margin-bottom: .9rem;\n  text-align: center;\n}\n\n.login-logo a,\n.register-logo a {\n  color: #495057;\n}\n\n.login-page,\n.register-page {\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: #e9ecef;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  height: 100vh;\n  -ms-flex-pack: center;\n  justify-content: center;\n}\n\n.login-box,\n.register-box {\n  width: 360px;\n}\n\n@media (max-width: 576px) {\n  .login-box,\n  .register-box {\n    margin-top: .5rem;\n    width: 90%;\n  }\n}\n\n.login-box .card,\n.register-box .card {\n  margin-bottom: 0;\n}\n\n.login-card-body,\n.register-card-body {\n  background-color: #fff;\n  border-top: 0;\n  color: #666;\n  padding: 20px;\n}\n\n.login-card-body .input-group .form-control,\n.register-card-body .input-group .form-control {\n  border-right: 0;\n}\n\n.login-card-body .input-group .form-control:focus,\n.register-card-body .input-group .form-control:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,\n.login-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,\n.register-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text {\n  border-color: #80bdff;\n}\n\n.login-card-body .input-group .form-control.is-valid:focus,\n.register-card-body .input-group .form-control.is-valid:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,\n.login-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,\n.register-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text {\n  border-color: #28a745;\n}\n\n.login-card-body .input-group .form-control.is-invalid:focus,\n.register-card-body .input-group .form-control.is-invalid:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text {\n  border-color: #dc3545;\n}\n\n.login-card-body .input-group .input-group-text,\n.register-card-body .input-group .input-group-text {\n  background-color: transparent;\n  border-bottom-right-radius: 0.25rem;\n  border-left: 0;\n  border-top-right-radius: 0.25rem;\n  color: #777;\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.login-box-msg,\n.register-box-msg {\n  margin: 0;\n  padding: 0 20px 20px;\n  text-align: center;\n}\n\n.social-auth-links {\n  margin: 10px 0;\n}\n\n.error-page {\n  margin: 20px auto 0;\n  width: 600px;\n}\n\n@media (max-width: 767.98px) {\n  .error-page {\n    width: 100%;\n  }\n}\n\n.error-page > .headline {\n  float: left;\n  font-size: 100px;\n  font-weight: 300;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .headline {\n    float: none;\n    text-align: center;\n  }\n}\n\n.error-page > .error-content {\n  display: block;\n  margin-left: 190px;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .error-content {\n    margin-left: 0;\n  }\n}\n\n.error-page > .error-content > h3 {\n  font-size: 25px;\n  font-weight: 300;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .error-content > h3 {\n    text-align: center;\n  }\n}\n\n.invoice {\n  background-color: #fff;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n  position: relative;\n}\n\n.invoice-title {\n  margin-top: 0;\n}\n\n.profile-user-img {\n  border: 3px solid #adb5bd;\n  margin: 0 auto;\n  padding: 3px;\n  width: 100px;\n}\n\n.profile-username {\n  font-size: 21px;\n  margin-top: 5px;\n}\n\n.post {\n  border-bottom: 1px solid #adb5bd;\n  color: #666;\n  margin-bottom: 15px;\n  padding-bottom: 15px;\n}\n\n.post:last-of-type {\n  border-bottom: 0;\n  margin-bottom: 0;\n  padding-bottom: 0;\n}\n\n.post .user-block {\n  margin-bottom: 15px;\n  width: 100%;\n}\n\n.post .row {\n  width: 100%;\n}\n\n.product-image {\n  max-width: 100%;\n  height: auto;\n  width: 100%;\n}\n\n.product-image-thumbs {\n  -ms-flex-align: stretch;\n  align-items: stretch;\n  display: -ms-flexbox;\n  display: flex;\n  margin-top: 2rem;\n}\n\n.product-image-thumb {\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n  display: -ms-flexbox;\n  display: flex;\n  margin-right: 1rem;\n  max-width: 7rem;\n  padding: 0.5rem;\n}\n\n.product-image-thumb img {\n  max-width: 100%;\n  height: auto;\n  -ms-flex-item-align: center;\n  align-self: center;\n}\n\n.product-image-thumb:hover {\n  opacity: .5;\n}\n\n.product-share a {\n  margin-right: .5rem;\n}\n\n.projects td {\n  vertical-align: middle;\n}\n\n.projects .list-inline {\n  margin-bottom: 0;\n}\n\n.projects img.table-avatar,\n.projects .table-avatar img {\n  border-radius: 50%;\n  display: inline;\n  width: 2.5rem;\n}\n\n.projects .project-state {\n  text-align: center;\n}\n\nbody.iframe-mode .main-sidebar {\n  display: none;\n}\n\nbody.iframe-mode .content-wrapper {\n  margin-left: 0 !important;\n  margin-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\nbody.iframe-mode .main-header,\nbody.iframe-mode .main-footer {\n  display: none;\n}\n\nbody.iframe-mode-fullscreen {\n  overflow: hidden;\n}\n\nbody.iframe-mode-fullscreen.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: 0 !important;\n}\n\n.content-wrapper {\n  height: 100%;\n}\n\n.content-wrapper.iframe-mode .btn-iframe-close {\n  color: #dc3545;\n  position: absolute;\n  line-height: 1;\n  right: .125rem;\n  top: .125rem;\n  z-index: 10;\n  visibility: hidden;\n}\n\n.content-wrapper.iframe-mode .btn-iframe-close:hover, .content-wrapper.iframe-mode .btn-iframe-close:focus {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n@media (hover: none) and (pointer: coarse) {\n  .content-wrapper.iframe-mode .btn-iframe-close {\n    visibility: visible;\n  }\n}\n\n.content-wrapper.iframe-mode .navbar-nav {\n  overflow-y: auto;\n  width: 100%;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-link {\n  white-space: nowrap;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-item {\n  position: relative;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-item:hover .btn-iframe-close, .content-wrapper.iframe-mode .navbar-nav .nav-item:focus .btn-iframe-close {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n@media (hover: none) and (pointer: coarse) {\n  .content-wrapper.iframe-mode .navbar-nav .nav-item:hover .btn-iframe-close, .content-wrapper.iframe-mode .navbar-nav .nav-item:focus .btn-iframe-close {\n    visibility: visible;\n  }\n}\n\n.content-wrapper.iframe-mode .tab-content {\n  position: relative;\n}\n\n.content-wrapper.iframe-mode .tab-pane + .tab-empty {\n  display: none;\n}\n\n.content-wrapper.iframe-mode .tab-empty {\n  width: 100%;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.content-wrapper.iframe-mode .tab-loading {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  display: none;\n  background-color: #f4f6f9;\n}\n\n.content-wrapper.iframe-mode .tab-loading > div {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n  width: 100%;\n  height: 100%;\n}\n\n.content-wrapper.iframe-mode iframe {\n  border: 0;\n  width: 100%;\n  height: 100%;\n  margin-bottom: -8px;\n}\n\n.content-wrapper.iframe-mode iframe .content-wrapper {\n  padding-bottom: 0 !important;\n}\n\nbody.iframe-mode-fullscreen .content-wrapper.iframe-mode {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  margin-left: 0 !important;\n  height: 100%;\n  min-height: 100%;\n  z-index: 1048;\n}\n\n.permanent-btn-iframe-close .btn-iframe-close {\n  -webkit-animation: none !important;\n  animation: none !important;\n  visibility: visible !important;\n  opacity: 1;\n}\n\n.content-wrapper.kanban {\n  height: 1px;\n}\n\n.content-wrapper.kanban .content {\n  height: 100%;\n  overflow-x: auto;\n  overflow-y: hidden;\n}\n\n.content-wrapper.kanban .content .container,\n.content-wrapper.kanban .content .container-fluid,\n.content-wrapper.kanban .content .container-sm,\n.content-wrapper.kanban .content .container-md,\n.content-wrapper.kanban .content .container-lg,\n.content-wrapper.kanban .content .container-xl {\n  width: -webkit-max-content;\n  width: -moz-max-content;\n  width: max-content;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: stretch;\n  align-items: stretch;\n}\n\n.content-wrapper.kanban .content-header + .content {\n  height: calc(100% - ((2 * 15px) + (1.8rem * 1.2)));\n}\n\n.content-wrapper.kanban .card .card-body {\n  padding: .5rem;\n}\n\n.content-wrapper.kanban .card.card-row {\n  width: 340px;\n  display: inline-block;\n  margin: 0 .5rem;\n}\n\n.content-wrapper.kanban .card.card-row:first-child {\n  margin-left: 0;\n}\n\n.content-wrapper.kanban .card.card-row .card-body {\n  height: calc(100% - (12px + (1.8rem * 1.2) + .5rem));\n  overflow-y: auto;\n}\n\n.content-wrapper.kanban .card.card-row .card:last-child {\n  margin-bottom: 0;\n  border-bottom-width: 1px;\n}\n\n.content-wrapper.kanban .card.card-row .card .card-header {\n  padding: .5rem .75rem;\n}\n\n.content-wrapper.kanban .card.card-row .card .card-body {\n  padding: .75rem;\n}\n\n.content-wrapper.kanban .btn-tool.btn-link {\n  text-decoration: underline;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.fc-button {\n  background: #f8f9fa;\n  background-image: none;\n  border-bottom-color: #ddd;\n  border-color: #ddd;\n  color: #495057;\n}\n\n.fc-button:hover, .fc-button:active, .fc-button.hover {\n  background-color: #e9e9e9;\n}\n\n.fc-header-title h2 {\n  color: #666;\n  font-size: 15px;\n  line-height: 1.6em;\n  margin-left: 10px;\n}\n\n.fc-header-right {\n  padding-right: 10px;\n}\n\n.fc-header-left {\n  padding-left: 10px;\n}\n\n.fc-widget-header {\n  background: #fafafa;\n}\n\n.fc-grid {\n  border: 0;\n  width: 100%;\n}\n\n.fc-widget-header:first-of-type,\n.fc-widget-content:first-of-type {\n  border-left: 0;\n  border-right: 0;\n}\n\n.fc-widget-header:last-of-type,\n.fc-widget-content:last-of-type {\n  border-right: 0;\n}\n\n.fc-toolbar,\n.fc-toolbar.fc-header-toolbar {\n  margin: 0;\n  padding: 1rem;\n}\n\n@media (max-width: 575.98px) {\n  .fc-toolbar {\n    -ms-flex-direction: column;\n    flex-direction: column;\n  }\n  .fc-toolbar .fc-left {\n    -ms-flex-order: 1;\n    order: 1;\n    margin-bottom: .5rem;\n  }\n  .fc-toolbar .fc-center {\n    -ms-flex-order: 0;\n    order: 0;\n    margin-bottom: .375rem;\n  }\n  .fc-toolbar .fc-right {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n}\n\n.fc-day-number {\n  font-size: 20px;\n  font-weight: 300;\n  padding-right: 10px;\n}\n\n.fc-color-picker {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n.fc-color-picker > li {\n  float: left;\n  font-size: 30px;\n  line-height: 30px;\n  margin-right: 5px;\n}\n\n.fc-color-picker > li .fa,\n.fc-color-picker > li .fas,\n.fc-color-picker > li .far,\n.fc-color-picker > li .fab,\n.fc-color-picker > li .fal,\n.fc-color-picker > li .fad,\n.fc-color-picker > li .svg-inline--fa,\n.fc-color-picker > li .ion {\n  transition: -webkit-transform linear .3s;\n  transition: transform linear .3s;\n  transition: transform linear .3s, -webkit-transform linear .3s;\n}\n\n.fc-color-picker > li .fa:hover,\n.fc-color-picker > li .fas:hover,\n.fc-color-picker > li .far:hover,\n.fc-color-picker > li .fab:hover,\n.fc-color-picker > li .fal:hover,\n.fc-color-picker > li .fad:hover,\n.fc-color-picker > li .svg-inline--fa:hover,\n.fc-color-picker > li .ion:hover {\n  -webkit-transform: rotate(30deg);\n  transform: rotate(30deg);\n}\n\n#add-new-event {\n  transition: all linear .3s;\n}\n\n.external-event {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  cursor: move;\n  font-weight: 700;\n  margin-bottom: 4px;\n  padding: 5px 10px;\n}\n\n.external-event:hover {\n  box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.2);\n}\n\n.select2-container--default .select2-selection--single {\n  border: 1px solid #ced4da;\n  padding: 0.46875rem 0.75rem;\n  height: calc(2.25rem + 2px);\n}\n\n.select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-dropdown {\n  border: 1px solid #ced4da;\n}\n\n.select2-container--default .select2-results__option {\n  padding: 6px 12px;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__rendered {\n  padding-left: 0;\n  height: auto;\n  margin-top: -3px;\n}\n\n.select2-container--default[dir=\"rtl\"] .select2-selection--single .select2-selection__rendered {\n  padding-right: 6px;\n  padding-left: 20px;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__arrow {\n  height: 31px;\n  right: 6px;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__arrow b {\n  margin-top: 0;\n}\n\n.select2-container--default .select2-dropdown .select2-search__field,\n.select2-container--default .select2-search--inline .select2-search__field {\n  border: 1px solid #ced4da;\n}\n\n.select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-search--inline .select2-search__field:focus {\n  outline: none;\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-dropdown.select2-dropdown--below {\n  border-top: 0;\n}\n\n.select2-container--default .select2-dropdown.select2-dropdown--above {\n  border-bottom: 0;\n}\n\n.select2-container--default .select2-results__option[aria-disabled='true'] {\n  color: #6c757d;\n}\n\n.select2-container--default .select2-results__option[aria-selected='true'] {\n  background-color: #dee2e6;\n}\n\n.select2-container--default .select2-results__option[aria-selected='true'], .select2-container--default .select2-results__option[aria-selected='true']:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-selection--multiple {\n  border: 1px solid #ced4da;\n  min-height: calc(2.25rem + 2px);\n}\n\n.select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered {\n  padding: 0 0.375rem 0.375rem;\n  margin-bottom: -0.375rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline {\n  width: 100%;\n  margin-left: 0.375rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline .select2-search__field {\n  width: 100% !important;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field {\n  border: 0;\n  margin-top: 6px;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n  padding: 0 10px;\n  margin-top: .31rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n  float: right;\n  margin-left: 5px;\n  margin-right: -2px;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-search.select2-search--inline .select2-search__field, .select2-container--default .select2-selection--multiple.text-sm .select2-search.select2-search--inline .select2-search__field {\n  margin-top: 8px;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__choice, .select2-container--default .select2-selection--multiple.text-sm .select2-selection__choice {\n  margin-top: .4rem;\n}\n\n.select2-container--default.select2-container--focus .select2-selection--single,\n.select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-container--default.select2-container--focus .select2-search__field {\n  border: 0;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__rendered li {\n  padding-right: 10px;\n}\n\n.input-group-prepend ~ .select2-container--default .select2-selection {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.input-group > .select2-container--default:not(:last-child) .select2-selection {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.select2-container--bootstrap4.select2-container--focus .select2-selection {\n  box-shadow: none;\n}\n\nselect.form-control-sm ~ .select2-container--default {\n  font-size: 75%;\n}\n\n.text-sm .select2-container--default .select2-selection--single,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single {\n  height: calc(1.8125rem + 2px);\n}\n\n.text-sm .select2-container--default .select2-selection--single .select2-selection__rendered,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single .select2-selection__rendered {\n  margin-top: -.4rem;\n}\n\n.text-sm .select2-container--default .select2-selection--single .select2-selection__arrow,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single .select2-selection__arrow {\n  top: -.12rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple {\n  min-height: calc(1.8125rem + 2px);\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered {\n  padding: 0 0.25rem 0.25rem;\n  margin-top: -0.1rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline {\n  margin-left: 0.25rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field {\n  margin-top: 6px;\n}\n\n.maximized-card .select2-dropdown {\n  z-index: 9999;\n}\n\n.select2-primary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-primary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-primary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-primary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-primary .select2-search--inline .select2-search__field:focus,\n.select2-primary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-primary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-primary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-primary .select2-results__option--highlighted,\n.select2-primary .select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-primary .select2-results__option--highlighted[aria-selected]:hover,\n.select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple:focus,\n.select2-primary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice__remove,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-primary.select2-container--focus .select2-selection--multiple,\n.select2-primary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-secondary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-secondary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-secondary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-secondary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-secondary .select2-search--inline .select2-search__field:focus,\n.select2-secondary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-secondary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-secondary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .select2-secondary .select2-results__option--highlighted,\n.select2-secondary .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-secondary .select2-results__option--highlighted[aria-selected]:hover,\n.select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple:focus,\n.select2-secondary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice__remove,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary.select2-container--focus .select2-selection--multiple,\n.select2-secondary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.select2-success + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-success + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-success.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-success .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-success .select2-search--inline .select2-search__field:focus,\n.select2-success .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-success .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-success .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #71dd8a;\n}\n\n.select2-container--default .select2-success .select2-results__option--highlighted,\n.select2-success .select2-container--default .select2-results__option--highlighted {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-success .select2-results__option--highlighted[aria-selected]:hover,\n.select2-success .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-success .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #259b40;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple:focus,\n.select2-success .select2-container--default .select2-selection--multiple:focus {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #28a745;\n  border-color: #23923d;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice__remove,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-success.select2-container--focus .select2-selection--multiple,\n.select2-success .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #71dd8a;\n}\n\n.select2-info + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-info + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-info.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-info .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-info .select2-search--inline .select2-search__field:focus,\n.select2-info .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-info .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-info .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #63d9ec;\n}\n\n.select2-container--default .select2-info .select2-results__option--highlighted,\n.select2-info .select2-container--default .select2-results__option--highlighted {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-info .select2-results__option--highlighted[aria-selected]:hover,\n.select2-info .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-info .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1596aa;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple:focus,\n.select2-info .select2-container--default .select2-selection--multiple:focus {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #17a2b8;\n  border-color: #148ea1;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice__remove,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-info.select2-container--focus .select2-selection--multiple,\n.select2-info .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #63d9ec;\n}\n\n.select2-warning + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-warning + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-warning.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-warning .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-warning .select2-search--inline .select2-search__field:focus,\n.select2-warning .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-warning .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-warning .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #ffe187;\n}\n\n.select2-container--default .select2-warning .select2-results__option--highlighted,\n.select2-warning .select2-container--default .select2-results__option--highlighted {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-warning .select2-results__option--highlighted[aria-selected]:hover,\n.select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7b900;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple:focus,\n.select2-warning .select2-container--default .select2-selection--multiple:focus {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ffc107;\n  border-color: #edb100;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice__remove,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning.select2-container--focus .select2-selection--multiple,\n.select2-warning .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #ffe187;\n}\n\n.select2-danger + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-danger + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-danger.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-danger .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-danger .select2-search--inline .select2-search__field:focus,\n.select2-danger .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-danger .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-danger .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #efa2a9;\n}\n\n.select2-container--default .select2-danger .select2-results__option--highlighted,\n.select2-danger .select2-container--default .select2-results__option--highlighted {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-danger .select2-results__option--highlighted[aria-selected]:hover,\n.select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #da2839;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple:focus,\n.select2-danger .select2-container--default .select2-selection--multiple:focus {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #dc3545;\n  border-color: #d32535;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice__remove,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-danger.select2-container--focus .select2-selection--multiple,\n.select2-danger .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #efa2a9;\n}\n\n.select2-light + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.select2-light + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .select2-light.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-light .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-light .select2-search--inline .select2-search__field:focus,\n.select2-light .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-light .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-light .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .select2-light .select2-results__option--highlighted,\n.select2-light .select2-container--default .select2-results__option--highlighted {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-light .select2-results__option--highlighted[aria-selected]:hover,\n.select2-light .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-light .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #eff1f4;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple:focus,\n.select2-light .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice__remove,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light.select2-container--focus .select2-selection--multiple,\n.select2-light .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.select2-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-dark .select2-search--inline .select2-search__field:focus,\n.select2-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .select2-dark .select2-results__option--highlighted,\n.select2-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-dark .select2-results__option--highlighted[aria-selected]:hover,\n.select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple:focus,\n.select2-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice__remove,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-dark.select2-container--focus .select2-selection--multiple,\n.select2-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.select2-lightblue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #99c5de;\n}\n\n.select2-lightblue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #99c5de;\n}\n\n.select2-container--default .select2-lightblue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lightblue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lightblue .select2-search--inline .select2-search__field:focus,\n.select2-lightblue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-lightblue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-lightblue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #99c5de;\n}\n\n.select2-container--default .select2-lightblue .select2-results__option--highlighted,\n.select2-lightblue .select2-container--default .select2-results__option--highlighted {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-lightblue .select2-results__option--highlighted[aria-selected]:hover,\n.select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #3884b0;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple:focus,\n.select2-lightblue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #99c5de;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3c8dbc;\n  border-color: #367fa9;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue.select2-container--focus .select2-selection--multiple,\n.select2-lightblue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #99c5de;\n}\n\n.select2-navy + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #005ebf;\n}\n\n.select2-navy + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #005ebf;\n}\n\n.select2-container--default .select2-navy.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-navy .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-navy .select2-search--inline .select2-search__field:focus,\n.select2-navy .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-navy .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-navy .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #005ebf;\n}\n\n.select2-container--default .select2-navy .select2-results__option--highlighted,\n.select2-navy .select2-container--default .select2-results__option--highlighted {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-navy .select2-results__option--highlighted[aria-selected]:hover,\n.select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #001730;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple:focus,\n.select2-navy .select2-container--default .select2-selection--multiple:focus {\n  border-color: #005ebf;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #001f3f;\n  border-color: #001226;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice__remove,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-navy.select2-container--focus .select2-selection--multiple,\n.select2-navy .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #005ebf;\n}\n\n.select2-olive + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #87cfaf;\n}\n\n.select2-olive + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #87cfaf;\n}\n\n.select2-container--default .select2-olive.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-olive .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-olive .select2-search--inline .select2-search__field:focus,\n.select2-olive .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-olive .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-olive .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #87cfaf;\n}\n\n.select2-container--default .select2-olive .select2-results__option--highlighted,\n.select2-olive .select2-container--default .select2-results__option--highlighted {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-olive .select2-results__option--highlighted[aria-selected]:hover,\n.select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #398e68;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple:focus,\n.select2-olive .select2-container--default .select2-selection--multiple:focus {\n  border-color: #87cfaf;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3d9970;\n  border-color: #368763;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice__remove,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-olive.select2-container--focus .select2-selection--multiple,\n.select2-olive .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #87cfaf;\n}\n\n.select2-lime + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #81ffb8;\n}\n\n.select2-lime + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #81ffb8;\n}\n\n.select2-container--default .select2-lime.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lime .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lime .select2-search--inline .select2-search__field:focus,\n.select2-lime .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-lime .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-lime .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #81ffb8;\n}\n\n.select2-container--default .select2-lime .select2-results__option--highlighted,\n.select2-lime .select2-container--default .select2-results__option--highlighted {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-lime .select2-results__option--highlighted[aria-selected]:hover,\n.select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #00f169;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple:focus,\n.select2-lime .select2-container--default .select2-selection--multiple:focus {\n  border-color: #81ffb8;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #01ff70;\n  border-color: #00e765;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice__remove,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime.select2-container--focus .select2-selection--multiple,\n.select2-lime .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #81ffb8;\n}\n\n.select2-fuchsia + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f88adf;\n}\n\n.select2-fuchsia + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f88adf;\n}\n\n.select2-container--default .select2-fuchsia.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-fuchsia .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-fuchsia .select2-search--inline .select2-search__field:focus,\n.select2-fuchsia .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-fuchsia .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-fuchsia .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f88adf;\n}\n\n.select2-container--default .select2-fuchsia .select2-results__option--highlighted,\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-fuchsia .select2-results__option--highlighted[aria-selected]:hover,\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e40eb4;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple:focus,\n.select2-fuchsia .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f88adf;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f012be;\n  border-color: #db0ead;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia.select2-container--focus .select2-selection--multiple,\n.select2-fuchsia .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f88adf;\n}\n\n.select2-maroon + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f083ab;\n}\n\n.select2-maroon + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f083ab;\n}\n\n.select2-container--default .select2-maroon.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-maroon .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-maroon .select2-search--inline .select2-search__field:focus,\n.select2-maroon .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-maroon .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-maroon .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f083ab;\n}\n\n.select2-container--default .select2-maroon .select2-results__option--highlighted,\n.select2-maroon .select2-container--default .select2-results__option--highlighted {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-maroon .select2-results__option--highlighted[aria-selected]:hover,\n.select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #ca195a;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple:focus,\n.select2-maroon .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f083ab;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #d81b60;\n  border-color: #c11856;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice__remove,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon.select2-container--focus .select2-selection--multiple,\n.select2-maroon .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f083ab;\n}\n\n.select2-blue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-blue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-blue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-blue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-blue .select2-search--inline .select2-search__field:focus,\n.select2-blue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-blue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-blue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-blue .select2-results__option--highlighted,\n.select2-blue .select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-blue .select2-results__option--highlighted[aria-selected]:hover,\n.select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple:focus,\n.select2-blue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice__remove,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-blue.select2-container--focus .select2-selection--multiple,\n.select2-blue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-indigo + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-indigo + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-container--default .select2-indigo.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-indigo .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-indigo .select2-search--inline .select2-search__field:focus,\n.select2-indigo .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-indigo .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-indigo .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b389f9;\n}\n\n.select2-container--default .select2-indigo .select2-results__option--highlighted,\n.select2-indigo .select2-container--default .select2-results__option--highlighted {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-indigo .select2-results__option--highlighted[aria-selected]:hover,\n.select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #5f0de6;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple:focus,\n.select2-indigo .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b389f9;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6610f2;\n  border-color: #5b0cdd;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice__remove,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo.select2-container--focus .select2-selection--multiple,\n.select2-indigo .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b389f9;\n}\n\n.select2-purple + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-purple + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .select2-purple.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-purple .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-purple .select2-search--inline .select2-search__field:focus,\n.select2-purple .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-purple .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-purple .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b8a2e0;\n}\n\n.select2-container--default .select2-purple .select2-results__option--highlighted,\n.select2-purple .select2-container--default .select2-results__option--highlighted {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-purple .select2-results__option--highlighted[aria-selected]:hover,\n.select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #683cb8;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple:focus,\n.select2-purple .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6f42c1;\n  border-color: #643ab0;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice__remove,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-purple.select2-container--focus .select2-selection--multiple,\n.select2-purple .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b8a2e0;\n}\n\n.select2-pink + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-pink + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .select2-pink.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-pink .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-pink .select2-search--inline .select2-search__field:focus,\n.select2-pink .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-pink .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-pink .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f6b0d0;\n}\n\n.select2-container--default .select2-pink .select2-results__option--highlighted,\n.select2-pink .select2-container--default .select2-results__option--highlighted {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-pink .select2-results__option--highlighted[aria-selected]:hover,\n.select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e63084;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple:focus,\n.select2-pink .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e83e8c;\n  border-color: #e5277e;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice__remove,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-pink.select2-container--focus .select2-selection--multiple,\n.select2-pink .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f6b0d0;\n}\n\n.select2-red + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-red + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-red.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-red .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-red .select2-search--inline .select2-search__field:focus,\n.select2-red .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-red .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-red .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #efa2a9;\n}\n\n.select2-container--default .select2-red .select2-results__option--highlighted,\n.select2-red .select2-container--default .select2-results__option--highlighted {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-red .select2-results__option--highlighted[aria-selected]:hover,\n.select2-red .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-red .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #da2839;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple:focus,\n.select2-red .select2-container--default .select2-selection--multiple:focus {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #dc3545;\n  border-color: #d32535;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice__remove,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-red.select2-container--focus .select2-selection--multiple,\n.select2-red .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #efa2a9;\n}\n\n.select2-orange + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-orange + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-container--default .select2-orange.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-orange .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-orange .select2-search--inline .select2-search__field:focus,\n.select2-orange .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-orange .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-orange .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #fec392;\n}\n\n.select2-container--default .select2-orange .select2-results__option--highlighted,\n.select2-orange .select2-container--default .select2-results__option--highlighted {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-orange .select2-results__option--highlighted[aria-selected]:hover,\n.select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #fd7605;\n  color: #fff;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple:focus,\n.select2-orange .select2-container--default .select2-selection--multiple:focus {\n  border-color: #fec392;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fd7e14;\n  border-color: #f57102;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice__remove,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange.select2-container--focus .select2-selection--multiple,\n.select2-orange .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #fec392;\n}\n\n.select2-yellow + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-yellow + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-yellow.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-yellow .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-yellow .select2-search--inline .select2-search__field:focus,\n.select2-yellow .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-yellow .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-yellow .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #ffe187;\n}\n\n.select2-container--default .select2-yellow .select2-results__option--highlighted,\n.select2-yellow .select2-container--default .select2-results__option--highlighted {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-yellow .select2-results__option--highlighted[aria-selected]:hover,\n.select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7b900;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple:focus,\n.select2-yellow .select2-container--default .select2-selection--multiple:focus {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ffc107;\n  border-color: #edb100;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice__remove,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow.select2-container--focus .select2-selection--multiple,\n.select2-yellow .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #ffe187;\n}\n\n.select2-green + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-green + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-green.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-green .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-green .select2-search--inline .select2-search__field:focus,\n.select2-green .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-green .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-green .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #71dd8a;\n}\n\n.select2-container--default .select2-green .select2-results__option--highlighted,\n.select2-green .select2-container--default .select2-results__option--highlighted {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-green .select2-results__option--highlighted[aria-selected]:hover,\n.select2-green .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-green .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #259b40;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple:focus,\n.select2-green .select2-container--default .select2-selection--multiple:focus {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #28a745;\n  border-color: #23923d;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice__remove,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-green.select2-container--focus .select2-selection--multiple,\n.select2-green .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #71dd8a;\n}\n\n.select2-teal + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-teal + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .select2-teal.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-teal .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-teal .select2-search--inline .select2-search__field:focus,\n.select2-teal .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-teal .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-teal .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #7eeaca;\n}\n\n.select2-container--default .select2-teal .select2-results__option--highlighted,\n.select2-teal .select2-container--default .select2-results__option--highlighted {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-teal .select2-results__option--highlighted[aria-selected]:hover,\n.select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1ebc8d;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple:focus,\n.select2-teal .select2-container--default .select2-selection--multiple:focus {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #20c997;\n  border-color: #1cb386;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice__remove,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-teal.select2-container--focus .select2-selection--multiple,\n.select2-teal .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #7eeaca;\n}\n\n.select2-cyan + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-cyan + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-cyan.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-cyan .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-cyan .select2-search--inline .select2-search__field:focus,\n.select2-cyan .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-cyan .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-cyan .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #63d9ec;\n}\n\n.select2-container--default .select2-cyan .select2-results__option--highlighted,\n.select2-cyan .select2-container--default .select2-results__option--highlighted {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-cyan .select2-results__option--highlighted[aria-selected]:hover,\n.select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1596aa;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple:focus,\n.select2-cyan .select2-container--default .select2-selection--multiple:focus {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #17a2b8;\n  border-color: #148ea1;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice__remove,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan.select2-container--focus .select2-selection--multiple,\n.select2-cyan .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #63d9ec;\n}\n\n.select2-white + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.select2-white + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .select2-white.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-white .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-white .select2-search--inline .select2-search__field:focus,\n.select2-white .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-white .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-white .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .select2-white .select2-results__option--highlighted,\n.select2-white .select2-container--default .select2-results__option--highlighted {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-white .select2-results__option--highlighted[aria-selected]:hover,\n.select2-white .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-white .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7f7f7;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple:focus,\n.select2-white .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fff;\n  border-color: #f2f2f2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice__remove,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white.select2-container--focus .select2-selection--multiple,\n.select2-white .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.select2-gray + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-gray + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-gray.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray .select2-search--inline .select2-search__field:focus,\n.select2-gray .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-gray .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-gray .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .select2-gray .select2-results__option--highlighted,\n.select2-gray .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-gray .select2-results__option--highlighted[aria-selected]:hover,\n.select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple:focus,\n.select2-gray .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice__remove,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-gray.select2-container--focus .select2-selection--multiple,\n.select2-gray .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.select2-gray-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-gray-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray-dark .select2-search--inline .select2-search__field:focus,\n.select2-gray-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-gray-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-gray-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark .select2-results__option--highlighted,\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-gray-dark .select2-results__option--highlighted[aria-selected]:hover,\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple:focus,\n.select2-gray-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark.select2-container--focus .select2-selection--multiple,\n.select2-gray-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.slider .tooltip.in {\n  opacity: 0.9;\n}\n\n.slider.slider-vertical {\n  height: 100%;\n}\n\n.slider.slider-horizontal {\n  width: 100%;\n}\n\n.slider-primary .slider .slider-selection {\n  background: #007bff;\n}\n\n.slider-secondary .slider .slider-selection {\n  background: #6c757d;\n}\n\n.slider-success .slider .slider-selection {\n  background: #28a745;\n}\n\n.slider-info .slider .slider-selection {\n  background: #17a2b8;\n}\n\n.slider-warning .slider .slider-selection {\n  background: #ffc107;\n}\n\n.slider-danger .slider .slider-selection {\n  background: #dc3545;\n}\n\n.slider-light .slider .slider-selection {\n  background: #f8f9fa;\n}\n\n.slider-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.slider-lightblue .slider .slider-selection {\n  background: #3c8dbc;\n}\n\n.slider-navy .slider .slider-selection {\n  background: #001f3f;\n}\n\n.slider-olive .slider .slider-selection {\n  background: #3d9970;\n}\n\n.slider-lime .slider .slider-selection {\n  background: #01ff70;\n}\n\n.slider-fuchsia .slider .slider-selection {\n  background: #f012be;\n}\n\n.slider-maroon .slider .slider-selection {\n  background: #d81b60;\n}\n\n.slider-blue .slider .slider-selection {\n  background: #007bff;\n}\n\n.slider-indigo .slider .slider-selection {\n  background: #6610f2;\n}\n\n.slider-purple .slider .slider-selection {\n  background: #6f42c1;\n}\n\n.slider-pink .slider .slider-selection {\n  background: #e83e8c;\n}\n\n.slider-red .slider .slider-selection {\n  background: #dc3545;\n}\n\n.slider-orange .slider .slider-selection {\n  background: #fd7e14;\n}\n\n.slider-yellow .slider .slider-selection {\n  background: #ffc107;\n}\n\n.slider-green .slider .slider-selection {\n  background: #28a745;\n}\n\n.slider-teal .slider .slider-selection {\n  background: #20c997;\n}\n\n.slider-cyan .slider .slider-selection {\n  background: #17a2b8;\n}\n\n.slider-white .slider .slider-selection {\n  background: #fff;\n}\n\n.slider-gray .slider .slider-selection {\n  background: #6c757d;\n}\n\n.slider-gray-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.icheck-primary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-primary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-primary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-primary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-primary > input:first-child:checked + label::before,\n.icheck-primary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-secondary > input:first-child:checked + label::before,\n.icheck-secondary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.icheck-success > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-success > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-success > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-success > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-success > input:first-child:checked + label::before,\n.icheck-success > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.icheck-info > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-info > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-info > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-info > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-info > input:first-child:checked + label::before,\n.icheck-info > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.icheck-warning > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-warning > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-warning > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-warning > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-warning > input:first-child:checked + label::before,\n.icheck-warning > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.icheck-danger > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-danger > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-danger > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-danger > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-danger > input:first-child:checked + label::before,\n.icheck-danger > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.icheck-light > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-light > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.icheck-light > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-light > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.icheck-light > input:first-child:checked + label::before,\n.icheck-light > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.icheck-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-dark > input:first-child:checked + label::before,\n.icheck-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3c8dbc;\n}\n\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3c8dbc;\n}\n\n.icheck-lightblue > input:first-child:checked + label::before,\n.icheck-lightblue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n}\n\n.icheck-navy > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-navy > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #001f3f;\n}\n\n.icheck-navy > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-navy > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #001f3f;\n}\n\n.icheck-navy > input:first-child:checked + label::before,\n.icheck-navy > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #001f3f;\n  border-color: #001f3f;\n}\n\n.icheck-olive > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-olive > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3d9970;\n}\n\n.icheck-olive > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-olive > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3d9970;\n}\n\n.icheck-olive > input:first-child:checked + label::before,\n.icheck-olive > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3d9970;\n  border-color: #3d9970;\n}\n\n.icheck-lime > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-lime > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #01ff70;\n}\n\n.icheck-lime > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-lime > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #01ff70;\n}\n\n.icheck-lime > input:first-child:checked + label::before,\n.icheck-lime > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #01ff70;\n  border-color: #01ff70;\n}\n\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f012be;\n}\n\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f012be;\n}\n\n.icheck-fuchsia > input:first-child:checked + label::before,\n.icheck-fuchsia > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f012be;\n  border-color: #f012be;\n}\n\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #d81b60;\n}\n\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #d81b60;\n}\n\n.icheck-maroon > input:first-child:checked + label::before,\n.icheck-maroon > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #d81b60;\n  border-color: #d81b60;\n}\n\n.icheck-blue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-blue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-blue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-blue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-blue > input:first-child:checked + label::before,\n.icheck-blue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.icheck-indigo > input:first-child:checked + label::before,\n.icheck-indigo > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6610f2;\n  border-color: #6610f2;\n}\n\n.icheck-purple > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-purple > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.icheck-purple > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-purple > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.icheck-purple > input:first-child:checked + label::before,\n.icheck-purple > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n}\n\n.icheck-pink > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-pink > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.icheck-pink > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-pink > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.icheck-pink > input:first-child:checked + label::before,\n.icheck-pink > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n}\n\n.icheck-red > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-red > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-red > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-red > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-red > input:first-child:checked + label::before,\n.icheck-red > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.icheck-orange > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-orange > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.icheck-orange > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-orange > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.icheck-orange > input:first-child:checked + label::before,\n.icheck-orange > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n}\n\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-yellow > input:first-child:checked + label::before,\n.icheck-yellow > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.icheck-green > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-green > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-green > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-green > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-green > input:first-child:checked + label::before,\n.icheck-green > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.icheck-teal > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-teal > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.icheck-teal > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-teal > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.icheck-teal > input:first-child:checked + label::before,\n.icheck-teal > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #20c997;\n  border-color: #20c997;\n}\n\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-cyan > input:first-child:checked + label::before,\n.icheck-cyan > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.icheck-white > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-white > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.icheck-white > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-white > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.icheck-white > input:first-child:checked + label::before,\n.icheck-white > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fff;\n  border-color: #fff;\n}\n\n.icheck-gray > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-gray > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-gray > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-gray > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-gray > input:first-child:checked + label::before,\n.icheck-gray > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-gray-dark > input:first-child:checked + label::before,\n.icheck-gray-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.mapael .map {\n  position: relative;\n}\n\n.mapael .mapTooltip {\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  border-radius: 0.25rem;\n  font-size: 0.875rem;\n  background-color: #000;\n  color: #fff;\n  display: block;\n  max-width: 200px;\n  padding: 0.25rem 0.5rem;\n  position: absolute;\n  text-align: center;\n  word-wrap: break-word;\n  z-index: 1070;\n}\n\n.mapael .myLegend {\n  background-color: #f8f9fa;\n  border: 1px solid #adb5bd;\n  padding: 10px;\n  width: 600px;\n}\n\n.mapael .zoomButton {\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  border-radius: 0.25rem;\n  color: #444;\n  cursor: pointer;\n  font-weight: 700;\n  height: 16px;\n  left: 10px;\n  line-height: 14px;\n  padding-left: 1px;\n  position: absolute;\n  text-align: center;\n  top: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  width: 16px;\n}\n\n.mapael .zoomButton:hover, .mapael .zoomButton:active, .mapael .zoomButton.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.mapael .zoomReset {\n  line-height: 12px;\n  top: 10px;\n}\n\n.mapael .zoomIn {\n  top: 30px;\n}\n\n.mapael .zoomOut {\n  top: 50px;\n}\n\n.jqvmap-zoomin,\n.jqvmap-zoomout {\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  border-radius: 0.25rem;\n  color: #444;\n  height: 15px;\n  width: 15px;\n  padding: 1px 2px;\n}\n\n.jqvmap-zoomin:hover, .jqvmap-zoomin:active, .jqvmap-zoomin.hover,\n.jqvmap-zoomout:hover,\n.jqvmap-zoomout:active,\n.jqvmap-zoomout.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.swal2-icon.swal2-info {\n  border-color: ligthen(#17a2b8, 20%);\n  color: #17a2b8;\n}\n\n.swal2-icon.swal2-warning {\n  border-color: ligthen(#ffc107, 20%);\n  color: #ffc107;\n}\n\n.swal2-icon.swal2-error {\n  border-color: ligthen(#dc3545, 20%);\n  color: #dc3545;\n}\n\n.swal2-icon.swal2-question {\n  border-color: ligthen(#6c757d, 20%);\n  color: #6c757d;\n}\n\n.swal2-icon.swal2-success {\n  border-color: ligthen(#28a745, 20%);\n  color: #28a745;\n}\n\n.swal2-icon.swal2-success .swal2-success-ring {\n  border-color: ligthen(#28a745, 20%);\n}\n\n.swal2-icon.swal2-success [class^='swal2-success-line'] {\n  background-color: #28a745;\n}\n\n#toast-container .toast {\n  background-color: #007bff;\n}\n\n#toast-container .toast-success {\n  background-color: #28a745;\n}\n\n#toast-container .toast-error {\n  background-color: #dc3545;\n}\n\n#toast-container .toast-info {\n  background-color: #17a2b8;\n}\n\n#toast-container .toast-warning {\n  background-color: #ffc107;\n}\n\n.toast-bottom-full-width .toast,\n.toast-top-full-width .toast {\n  max-width: inherit;\n}\n\n.pace {\n  z-index: 1048;\n}\n\n.pace .pace-progress {\n  z-index: 1049;\n}\n\n.pace .pace-activity {\n  z-index: 1050;\n}\n\n.pace-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-primary .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-primary .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-primary .pace .pace-progress::after {\n  color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-bounce-primary .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-center-atom-primary .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-primary .pace-progress::before {\n  background: #007bff;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-primary .pace-activity {\n  border-color: #007bff;\n}\n\n.pace-center-atom-primary .pace-activity::after, .pace-center-atom-primary .pace-activity::before {\n  border-color: #007bff;\n}\n\n.pace-center-circle-primary .pace .pace-progress {\n  background: rgba(0, 123, 255, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-primary .pace .pace-activity {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-radar-primary .pace .pace-activity::before {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-simple-primary .pace {\n  background: #fff;\n  border-color: #007bff;\n}\n\n.pace-center-simple-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-material-primary .pace {\n  color: #007bff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::after,\n.pace-corner-indicator-primary .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::before {\n  border-right-color: rgba(0, 123, 255, 0.2);\n  border-left-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::after {\n  border-top-color: rgba(0, 123, 255, 0.2);\n  border-bottom-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-fill-left-primary .pace .pace-progress {\n  background-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-flash-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-flash-primary .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #007bff, 0 0 5px #007bff;\n}\n\n.pace-flash-primary .pace .pace-activity {\n  border-top-color: #007bff;\n  border-left-color: #007bff;\n}\n\n.pace-loading-bar-primary .pace .pace-progress {\n  background: #007bff;\n  color: #007bff;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-primary .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #007bff, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-primary .pace .pace-progress {\n  background-color: #007bff;\n  box-shadow: inset -1px 0 #007bff, inset 0 -1px #007bff, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-primary .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-primary .pace-progress {\n  color: #007bff;\n}\n\n.pace-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-secondary .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-secondary .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-secondary .pace .pace-progress::after {\n  color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-bounce-secondary .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-center-atom-secondary .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-secondary .pace-progress::before {\n  background: #6c757d;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-secondary .pace-activity {\n  border-color: #6c757d;\n}\n\n.pace-center-atom-secondary .pace-activity::after, .pace-center-atom-secondary .pace-activity::before {\n  border-color: #6c757d;\n}\n\n.pace-center-circle-secondary .pace .pace-progress {\n  background: rgba(108, 117, 125, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-secondary .pace .pace-activity {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-radar-secondary .pace .pace-activity::before {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-simple-secondary .pace {\n  background: #fff;\n  border-color: #6c757d;\n}\n\n.pace-center-simple-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-material-secondary .pace {\n  color: #6c757d;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::after,\n.pace-corner-indicator-secondary .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::before {\n  border-right-color: rgba(108, 117, 125, 0.2);\n  border-left-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::after {\n  border-top-color: rgba(108, 117, 125, 0.2);\n  border-bottom-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-fill-left-secondary .pace .pace-progress {\n  background-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-flash-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-flash-secondary .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6c757d, 0 0 5px #6c757d;\n}\n\n.pace-flash-secondary .pace .pace-activity {\n  border-top-color: #6c757d;\n  border-left-color: #6c757d;\n}\n\n.pace-loading-bar-secondary .pace .pace-progress {\n  background: #6c757d;\n  color: #6c757d;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-secondary .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6c757d, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-secondary .pace .pace-progress {\n  background-color: #6c757d;\n  box-shadow: inset -1px 0 #6c757d, inset 0 -1px #6c757d, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-secondary .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-secondary .pace-progress {\n  color: #6c757d;\n}\n\n.pace-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-success .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-success .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-success .pace .pace-progress::after {\n  color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-bounce-success .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-center-atom-success .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-success .pace-progress::before {\n  background: #28a745;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-success .pace-activity {\n  border-color: #28a745;\n}\n\n.pace-center-atom-success .pace-activity::after, .pace-center-atom-success .pace-activity::before {\n  border-color: #28a745;\n}\n\n.pace-center-circle-success .pace .pace-progress {\n  background: rgba(40, 167, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-success .pace .pace-activity {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-radar-success .pace .pace-activity::before {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-simple-success .pace {\n  background: #fff;\n  border-color: #28a745;\n}\n\n.pace-center-simple-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-material-success .pace {\n  color: #28a745;\n}\n\n.pace-corner-indicator-success .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-corner-indicator-success .pace .pace-activity::after,\n.pace-corner-indicator-success .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-success .pace .pace-activity::before {\n  border-right-color: rgba(40, 167, 69, 0.2);\n  border-left-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-corner-indicator-success .pace .pace-activity::after {\n  border-top-color: rgba(40, 167, 69, 0.2);\n  border-bottom-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-fill-left-success .pace .pace-progress {\n  background-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-flash-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-flash-success .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #28a745, 0 0 5px #28a745;\n}\n\n.pace-flash-success .pace .pace-activity {\n  border-top-color: #28a745;\n  border-left-color: #28a745;\n}\n\n.pace-loading-bar-success .pace .pace-progress {\n  background: #28a745;\n  color: #28a745;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-success .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #28a745, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-success .pace .pace-progress {\n  background-color: #28a745;\n  box-shadow: inset -1px 0 #28a745, inset 0 -1px #28a745, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-success .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-success .pace-progress {\n  color: #28a745;\n}\n\n.pace-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-info .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-info .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-info .pace .pace-progress::after {\n  color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-bounce-info .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-center-atom-info .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-info .pace-progress::before {\n  background: #17a2b8;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-info .pace-activity {\n  border-color: #17a2b8;\n}\n\n.pace-center-atom-info .pace-activity::after, .pace-center-atom-info .pace-activity::before {\n  border-color: #17a2b8;\n}\n\n.pace-center-circle-info .pace .pace-progress {\n  background: rgba(23, 162, 184, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-info .pace .pace-activity {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-radar-info .pace .pace-activity::before {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-simple-info .pace {\n  background: #fff;\n  border-color: #17a2b8;\n}\n\n.pace-center-simple-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-material-info .pace {\n  color: #17a2b8;\n}\n\n.pace-corner-indicator-info .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-corner-indicator-info .pace .pace-activity::after,\n.pace-corner-indicator-info .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-info .pace .pace-activity::before {\n  border-right-color: rgba(23, 162, 184, 0.2);\n  border-left-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-corner-indicator-info .pace .pace-activity::after {\n  border-top-color: rgba(23, 162, 184, 0.2);\n  border-bottom-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-fill-left-info .pace .pace-progress {\n  background-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-flash-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-flash-info .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #17a2b8, 0 0 5px #17a2b8;\n}\n\n.pace-flash-info .pace .pace-activity {\n  border-top-color: #17a2b8;\n  border-left-color: #17a2b8;\n}\n\n.pace-loading-bar-info .pace .pace-progress {\n  background: #17a2b8;\n  color: #17a2b8;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-info .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #17a2b8, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-info .pace .pace-progress {\n  background-color: #17a2b8;\n  box-shadow: inset -1px 0 #17a2b8, inset 0 -1px #17a2b8, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-info .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-info .pace-progress {\n  color: #17a2b8;\n}\n\n.pace-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-warning .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-warning .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-warning .pace .pace-progress::after {\n  color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-bounce-warning .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-center-atom-warning .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-warning .pace-progress::before {\n  background: #ffc107;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-warning .pace-activity {\n  border-color: #ffc107;\n}\n\n.pace-center-atom-warning .pace-activity::after, .pace-center-atom-warning .pace-activity::before {\n  border-color: #ffc107;\n}\n\n.pace-center-circle-warning .pace .pace-progress {\n  background: rgba(255, 193, 7, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-warning .pace .pace-activity {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-radar-warning .pace .pace-activity::before {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-simple-warning .pace {\n  background: #1f2d3d;\n  border-color: #ffc107;\n}\n\n.pace-center-simple-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-material-warning .pace {\n  color: #ffc107;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::after,\n.pace-corner-indicator-warning .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::before {\n  border-right-color: rgba(255, 193, 7, 0.2);\n  border-left-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::after {\n  border-top-color: rgba(255, 193, 7, 0.2);\n  border-bottom-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-fill-left-warning .pace .pace-progress {\n  background-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-flash-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-flash-warning .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #ffc107, 0 0 5px #ffc107;\n}\n\n.pace-flash-warning .pace .pace-activity {\n  border-top-color: #ffc107;\n  border-left-color: #ffc107;\n}\n\n.pace-loading-bar-warning .pace .pace-progress {\n  background: #ffc107;\n  color: #ffc107;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-warning .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #ffc107, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-warning .pace .pace-progress {\n  background-color: #ffc107;\n  box-shadow: inset -1px 0 #ffc107, inset 0 -1px #ffc107, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-warning .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-warning .pace-progress {\n  color: #ffc107;\n}\n\n.pace-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-danger .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-danger .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-danger .pace .pace-progress::after {\n  color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-bounce-danger .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-center-atom-danger .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-danger .pace-progress::before {\n  background: #dc3545;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-danger .pace-activity {\n  border-color: #dc3545;\n}\n\n.pace-center-atom-danger .pace-activity::after, .pace-center-atom-danger .pace-activity::before {\n  border-color: #dc3545;\n}\n\n.pace-center-circle-danger .pace .pace-progress {\n  background: rgba(220, 53, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-danger .pace .pace-activity {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-radar-danger .pace .pace-activity::before {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-simple-danger .pace {\n  background: #fff;\n  border-color: #dc3545;\n}\n\n.pace-center-simple-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-material-danger .pace {\n  color: #dc3545;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::after,\n.pace-corner-indicator-danger .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::before {\n  border-right-color: rgba(220, 53, 69, 0.2);\n  border-left-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::after {\n  border-top-color: rgba(220, 53, 69, 0.2);\n  border-bottom-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-fill-left-danger .pace .pace-progress {\n  background-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-flash-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-flash-danger .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #dc3545, 0 0 5px #dc3545;\n}\n\n.pace-flash-danger .pace .pace-activity {\n  border-top-color: #dc3545;\n  border-left-color: #dc3545;\n}\n\n.pace-loading-bar-danger .pace .pace-progress {\n  background: #dc3545;\n  color: #dc3545;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-danger .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #dc3545, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-danger .pace .pace-progress {\n  background-color: #dc3545;\n  box-shadow: inset -1px 0 #dc3545, inset 0 -1px #dc3545, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-danger .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-danger .pace-progress {\n  color: #dc3545;\n}\n\n.pace-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-barber-shop-light .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-barber-shop-light .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-light .pace .pace-progress::after {\n  color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-bounce-light .pace .pace-activity {\n  background: #f8f9fa;\n}\n\n.pace-center-atom-light .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-light .pace-progress::before {\n  background: #f8f9fa;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-light .pace-activity {\n  border-color: #f8f9fa;\n}\n\n.pace-center-atom-light .pace-activity::after, .pace-center-atom-light .pace-activity::before {\n  border-color: #f8f9fa;\n}\n\n.pace-center-circle-light .pace .pace-progress {\n  background: rgba(248, 249, 250, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-light .pace .pace-activity {\n  border-color: #f8f9fa transparent transparent;\n}\n\n.pace-center-radar-light .pace .pace-activity::before {\n  border-color: #f8f9fa transparent transparent;\n}\n\n.pace-center-simple-light .pace {\n  background: #1f2d3d;\n  border-color: #f8f9fa;\n}\n\n.pace-center-simple-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-material-light .pace {\n  color: #f8f9fa;\n}\n\n.pace-corner-indicator-light .pace .pace-activity {\n  background: #f8f9fa;\n}\n\n.pace-corner-indicator-light .pace .pace-activity::after,\n.pace-corner-indicator-light .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-light .pace .pace-activity::before {\n  border-right-color: rgba(248, 249, 250, 0.2);\n  border-left-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-corner-indicator-light .pace .pace-activity::after {\n  border-top-color: rgba(248, 249, 250, 0.2);\n  border-bottom-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-fill-left-light .pace .pace-progress {\n  background-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-flash-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-flash-light .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #f8f9fa, 0 0 5px #f8f9fa;\n}\n\n.pace-flash-light .pace .pace-activity {\n  border-top-color: #f8f9fa;\n  border-left-color: #f8f9fa;\n}\n\n.pace-loading-bar-light .pace .pace-progress {\n  background: #f8f9fa;\n  color: #f8f9fa;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-light .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #f8f9fa, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-light .pace .pace-progress {\n  background-color: #f8f9fa;\n  box-shadow: inset -1px 0 #f8f9fa, inset 0 -1px #f8f9fa, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-light .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-light .pace-progress {\n  color: #f8f9fa;\n}\n\n.pace-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-dark .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-dark .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-dark .pace .pace-progress::after {\n  color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-bounce-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-center-atom-dark .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-dark .pace-progress::before {\n  background: #343a40;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-dark .pace-activity {\n  border-color: #343a40;\n}\n\n.pace-center-atom-dark .pace-activity::after, .pace-center-atom-dark .pace-activity::before {\n  border-color: #343a40;\n}\n\n.pace-center-circle-dark .pace .pace-progress {\n  background: rgba(52, 58, 64, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-dark .pace .pace-activity {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-radar-dark .pace .pace-activity::before {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-simple-dark .pace {\n  background: #fff;\n  border-color: #343a40;\n}\n\n.pace-center-simple-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-material-dark .pace {\n  color: #343a40;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::after,\n.pace-corner-indicator-dark .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::before {\n  border-right-color: rgba(52, 58, 64, 0.2);\n  border-left-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::after {\n  border-top-color: rgba(52, 58, 64, 0.2);\n  border-bottom-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-fill-left-dark .pace .pace-progress {\n  background-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-flash-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-flash-dark .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #343a40, 0 0 5px #343a40;\n}\n\n.pace-flash-dark .pace .pace-activity {\n  border-top-color: #343a40;\n  border-left-color: #343a40;\n}\n\n.pace-loading-bar-dark .pace .pace-progress {\n  background: #343a40;\n  color: #343a40;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-dark .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #343a40, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-dark .pace .pace-progress {\n  background-color: #343a40;\n  box-shadow: inset -1px 0 #343a40, inset 0 -1px #343a40, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-dark .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-dark .pace-progress {\n  color: #343a40;\n}\n\n.pace-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-barber-shop-lightblue .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-barber-shop-lightblue .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-lightblue .pace .pace-progress::after {\n  color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-bounce-lightblue .pace .pace-activity {\n  background: #3c8dbc;\n}\n\n.pace-center-atom-lightblue .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-lightblue .pace-progress::before {\n  background: #3c8dbc;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-lightblue .pace-activity {\n  border-color: #3c8dbc;\n}\n\n.pace-center-atom-lightblue .pace-activity::after, .pace-center-atom-lightblue .pace-activity::before {\n  border-color: #3c8dbc;\n}\n\n.pace-center-circle-lightblue .pace .pace-progress {\n  background: rgba(60, 141, 188, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-lightblue .pace .pace-activity {\n  border-color: #3c8dbc transparent transparent;\n}\n\n.pace-center-radar-lightblue .pace .pace-activity::before {\n  border-color: #3c8dbc transparent transparent;\n}\n\n.pace-center-simple-lightblue .pace {\n  background: #fff;\n  border-color: #3c8dbc;\n}\n\n.pace-center-simple-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-material-lightblue .pace {\n  color: #3c8dbc;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity {\n  background: #3c8dbc;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::after,\n.pace-corner-indicator-lightblue .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::before {\n  border-right-color: rgba(60, 141, 188, 0.2);\n  border-left-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::after {\n  border-top-color: rgba(60, 141, 188, 0.2);\n  border-bottom-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-fill-left-lightblue .pace .pace-progress {\n  background-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-flash-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-flash-lightblue .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #3c8dbc, 0 0 5px #3c8dbc;\n}\n\n.pace-flash-lightblue .pace .pace-activity {\n  border-top-color: #3c8dbc;\n  border-left-color: #3c8dbc;\n}\n\n.pace-loading-bar-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n  color: #3c8dbc;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-lightblue .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #3c8dbc, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-lightblue .pace .pace-progress {\n  background-color: #3c8dbc;\n  box-shadow: inset -1px 0 #3c8dbc, inset 0 -1px #3c8dbc, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-lightblue .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-lightblue .pace-progress {\n  color: #3c8dbc;\n}\n\n.pace-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-barber-shop-navy .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-barber-shop-navy .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-navy .pace .pace-progress::after {\n  color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-bounce-navy .pace .pace-activity {\n  background: #001f3f;\n}\n\n.pace-center-atom-navy .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-navy .pace-progress::before {\n  background: #001f3f;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-navy .pace-activity {\n  border-color: #001f3f;\n}\n\n.pace-center-atom-navy .pace-activity::after, .pace-center-atom-navy .pace-activity::before {\n  border-color: #001f3f;\n}\n\n.pace-center-circle-navy .pace .pace-progress {\n  background: rgba(0, 31, 63, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-navy .pace .pace-activity {\n  border-color: #001f3f transparent transparent;\n}\n\n.pace-center-radar-navy .pace .pace-activity::before {\n  border-color: #001f3f transparent transparent;\n}\n\n.pace-center-simple-navy .pace {\n  background: #fff;\n  border-color: #001f3f;\n}\n\n.pace-center-simple-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-material-navy .pace {\n  color: #001f3f;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity {\n  background: #001f3f;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::after,\n.pace-corner-indicator-navy .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::before {\n  border-right-color: rgba(0, 31, 63, 0.2);\n  border-left-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::after {\n  border-top-color: rgba(0, 31, 63, 0.2);\n  border-bottom-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-fill-left-navy .pace .pace-progress {\n  background-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-flash-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-flash-navy .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #001f3f, 0 0 5px #001f3f;\n}\n\n.pace-flash-navy .pace .pace-activity {\n  border-top-color: #001f3f;\n  border-left-color: #001f3f;\n}\n\n.pace-loading-bar-navy .pace .pace-progress {\n  background: #001f3f;\n  color: #001f3f;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-navy .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #001f3f, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-navy .pace .pace-progress {\n  background-color: #001f3f;\n  box-shadow: inset -1px 0 #001f3f, inset 0 -1px #001f3f, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-navy .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-navy .pace-progress {\n  color: #001f3f;\n}\n\n.pace-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-barber-shop-olive .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-barber-shop-olive .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-olive .pace .pace-progress::after {\n  color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-bounce-olive .pace .pace-activity {\n  background: #3d9970;\n}\n\n.pace-center-atom-olive .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-olive .pace-progress::before {\n  background: #3d9970;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-olive .pace-activity {\n  border-color: #3d9970;\n}\n\n.pace-center-atom-olive .pace-activity::after, .pace-center-atom-olive .pace-activity::before {\n  border-color: #3d9970;\n}\n\n.pace-center-circle-olive .pace .pace-progress {\n  background: rgba(61, 153, 112, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-olive .pace .pace-activity {\n  border-color: #3d9970 transparent transparent;\n}\n\n.pace-center-radar-olive .pace .pace-activity::before {\n  border-color: #3d9970 transparent transparent;\n}\n\n.pace-center-simple-olive .pace {\n  background: #fff;\n  border-color: #3d9970;\n}\n\n.pace-center-simple-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-material-olive .pace {\n  color: #3d9970;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity {\n  background: #3d9970;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::after,\n.pace-corner-indicator-olive .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::before {\n  border-right-color: rgba(61, 153, 112, 0.2);\n  border-left-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::after {\n  border-top-color: rgba(61, 153, 112, 0.2);\n  border-bottom-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-fill-left-olive .pace .pace-progress {\n  background-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-flash-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-flash-olive .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #3d9970, 0 0 5px #3d9970;\n}\n\n.pace-flash-olive .pace .pace-activity {\n  border-top-color: #3d9970;\n  border-left-color: #3d9970;\n}\n\n.pace-loading-bar-olive .pace .pace-progress {\n  background: #3d9970;\n  color: #3d9970;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-olive .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #3d9970, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-olive .pace .pace-progress {\n  background-color: #3d9970;\n  box-shadow: inset -1px 0 #3d9970, inset 0 -1px #3d9970, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-olive .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-olive .pace-progress {\n  color: #3d9970;\n}\n\n.pace-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-barber-shop-lime .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-barber-shop-lime .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-lime .pace .pace-progress::after {\n  color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-bounce-lime .pace .pace-activity {\n  background: #01ff70;\n}\n\n.pace-center-atom-lime .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-lime .pace-progress::before {\n  background: #01ff70;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-lime .pace-activity {\n  border-color: #01ff70;\n}\n\n.pace-center-atom-lime .pace-activity::after, .pace-center-atom-lime .pace-activity::before {\n  border-color: #01ff70;\n}\n\n.pace-center-circle-lime .pace .pace-progress {\n  background: rgba(1, 255, 112, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-lime .pace .pace-activity {\n  border-color: #01ff70 transparent transparent;\n}\n\n.pace-center-radar-lime .pace .pace-activity::before {\n  border-color: #01ff70 transparent transparent;\n}\n\n.pace-center-simple-lime .pace {\n  background: #1f2d3d;\n  border-color: #01ff70;\n}\n\n.pace-center-simple-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-material-lime .pace {\n  color: #01ff70;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity {\n  background: #01ff70;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::after,\n.pace-corner-indicator-lime .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::before {\n  border-right-color: rgba(1, 255, 112, 0.2);\n  border-left-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::after {\n  border-top-color: rgba(1, 255, 112, 0.2);\n  border-bottom-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-fill-left-lime .pace .pace-progress {\n  background-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-flash-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-flash-lime .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #01ff70, 0 0 5px #01ff70;\n}\n\n.pace-flash-lime .pace .pace-activity {\n  border-top-color: #01ff70;\n  border-left-color: #01ff70;\n}\n\n.pace-loading-bar-lime .pace .pace-progress {\n  background: #01ff70;\n  color: #01ff70;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-lime .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #01ff70, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-lime .pace .pace-progress {\n  background-color: #01ff70;\n  box-shadow: inset -1px 0 #01ff70, inset 0 -1px #01ff70, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-lime .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-lime .pace-progress {\n  color: #01ff70;\n}\n\n.pace-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-barber-shop-fuchsia .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-barber-shop-fuchsia .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-fuchsia .pace .pace-progress::after {\n  color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-bounce-fuchsia .pace .pace-activity {\n  background: #f012be;\n}\n\n.pace-center-atom-fuchsia .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-fuchsia .pace-progress::before {\n  background: #f012be;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-fuchsia .pace-activity {\n  border-color: #f012be;\n}\n\n.pace-center-atom-fuchsia .pace-activity::after, .pace-center-atom-fuchsia .pace-activity::before {\n  border-color: #f012be;\n}\n\n.pace-center-circle-fuchsia .pace .pace-progress {\n  background: rgba(240, 18, 190, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-fuchsia .pace .pace-activity {\n  border-color: #f012be transparent transparent;\n}\n\n.pace-center-radar-fuchsia .pace .pace-activity::before {\n  border-color: #f012be transparent transparent;\n}\n\n.pace-center-simple-fuchsia .pace {\n  background: #fff;\n  border-color: #f012be;\n}\n\n.pace-center-simple-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-material-fuchsia .pace {\n  color: #f012be;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity {\n  background: #f012be;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::after,\n.pace-corner-indicator-fuchsia .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::before {\n  border-right-color: rgba(240, 18, 190, 0.2);\n  border-left-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::after {\n  border-top-color: rgba(240, 18, 190, 0.2);\n  border-bottom-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-fill-left-fuchsia .pace .pace-progress {\n  background-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-flash-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-flash-fuchsia .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #f012be, 0 0 5px #f012be;\n}\n\n.pace-flash-fuchsia .pace .pace-activity {\n  border-top-color: #f012be;\n  border-left-color: #f012be;\n}\n\n.pace-loading-bar-fuchsia .pace .pace-progress {\n  background: #f012be;\n  color: #f012be;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-fuchsia .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #f012be, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-fuchsia .pace .pace-progress {\n  background-color: #f012be;\n  box-shadow: inset -1px 0 #f012be, inset 0 -1px #f012be, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-fuchsia .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-fuchsia .pace-progress {\n  color: #f012be;\n}\n\n.pace-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-barber-shop-maroon .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-barber-shop-maroon .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-maroon .pace .pace-progress::after {\n  color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-bounce-maroon .pace .pace-activity {\n  background: #d81b60;\n}\n\n.pace-center-atom-maroon .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-maroon .pace-progress::before {\n  background: #d81b60;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-maroon .pace-activity {\n  border-color: #d81b60;\n}\n\n.pace-center-atom-maroon .pace-activity::after, .pace-center-atom-maroon .pace-activity::before {\n  border-color: #d81b60;\n}\n\n.pace-center-circle-maroon .pace .pace-progress {\n  background: rgba(216, 27, 96, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-maroon .pace .pace-activity {\n  border-color: #d81b60 transparent transparent;\n}\n\n.pace-center-radar-maroon .pace .pace-activity::before {\n  border-color: #d81b60 transparent transparent;\n}\n\n.pace-center-simple-maroon .pace {\n  background: #fff;\n  border-color: #d81b60;\n}\n\n.pace-center-simple-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-material-maroon .pace {\n  color: #d81b60;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity {\n  background: #d81b60;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::after,\n.pace-corner-indicator-maroon .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::before {\n  border-right-color: rgba(216, 27, 96, 0.2);\n  border-left-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::after {\n  border-top-color: rgba(216, 27, 96, 0.2);\n  border-bottom-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-fill-left-maroon .pace .pace-progress {\n  background-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-flash-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-flash-maroon .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #d81b60, 0 0 5px #d81b60;\n}\n\n.pace-flash-maroon .pace .pace-activity {\n  border-top-color: #d81b60;\n  border-left-color: #d81b60;\n}\n\n.pace-loading-bar-maroon .pace .pace-progress {\n  background: #d81b60;\n  color: #d81b60;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-maroon .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #d81b60, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-maroon .pace .pace-progress {\n  background-color: #d81b60;\n  box-shadow: inset -1px 0 #d81b60, inset 0 -1px #d81b60, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-maroon .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-maroon .pace-progress {\n  color: #d81b60;\n}\n\n.pace-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-blue .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-blue .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-blue .pace .pace-progress::after {\n  color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-bounce-blue .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-center-atom-blue .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-blue .pace-progress::before {\n  background: #007bff;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-blue .pace-activity {\n  border-color: #007bff;\n}\n\n.pace-center-atom-blue .pace-activity::after, .pace-center-atom-blue .pace-activity::before {\n  border-color: #007bff;\n}\n\n.pace-center-circle-blue .pace .pace-progress {\n  background: rgba(0, 123, 255, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-blue .pace .pace-activity {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-radar-blue .pace .pace-activity::before {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-simple-blue .pace {\n  background: #fff;\n  border-color: #007bff;\n}\n\n.pace-center-simple-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-material-blue .pace {\n  color: #007bff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::after,\n.pace-corner-indicator-blue .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::before {\n  border-right-color: rgba(0, 123, 255, 0.2);\n  border-left-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::after {\n  border-top-color: rgba(0, 123, 255, 0.2);\n  border-bottom-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-fill-left-blue .pace .pace-progress {\n  background-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-flash-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-flash-blue .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #007bff, 0 0 5px #007bff;\n}\n\n.pace-flash-blue .pace .pace-activity {\n  border-top-color: #007bff;\n  border-left-color: #007bff;\n}\n\n.pace-loading-bar-blue .pace .pace-progress {\n  background: #007bff;\n  color: #007bff;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-blue .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #007bff, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-blue .pace .pace-progress {\n  background-color: #007bff;\n  box-shadow: inset -1px 0 #007bff, inset 0 -1px #007bff, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-blue .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-blue .pace-progress {\n  color: #007bff;\n}\n\n.pace-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-barber-shop-indigo .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-barber-shop-indigo .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-indigo .pace .pace-progress::after {\n  color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-bounce-indigo .pace .pace-activity {\n  background: #6610f2;\n}\n\n.pace-center-atom-indigo .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-indigo .pace-progress::before {\n  background: #6610f2;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-indigo .pace-activity {\n  border-color: #6610f2;\n}\n\n.pace-center-atom-indigo .pace-activity::after, .pace-center-atom-indigo .pace-activity::before {\n  border-color: #6610f2;\n}\n\n.pace-center-circle-indigo .pace .pace-progress {\n  background: rgba(102, 16, 242, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-indigo .pace .pace-activity {\n  border-color: #6610f2 transparent transparent;\n}\n\n.pace-center-radar-indigo .pace .pace-activity::before {\n  border-color: #6610f2 transparent transparent;\n}\n\n.pace-center-simple-indigo .pace {\n  background: #fff;\n  border-color: #6610f2;\n}\n\n.pace-center-simple-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-material-indigo .pace {\n  color: #6610f2;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity {\n  background: #6610f2;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::after,\n.pace-corner-indicator-indigo .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::before {\n  border-right-color: rgba(102, 16, 242, 0.2);\n  border-left-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::after {\n  border-top-color: rgba(102, 16, 242, 0.2);\n  border-bottom-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-fill-left-indigo .pace .pace-progress {\n  background-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-flash-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-flash-indigo .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6610f2, 0 0 5px #6610f2;\n}\n\n.pace-flash-indigo .pace .pace-activity {\n  border-top-color: #6610f2;\n  border-left-color: #6610f2;\n}\n\n.pace-loading-bar-indigo .pace .pace-progress {\n  background: #6610f2;\n  color: #6610f2;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-indigo .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6610f2, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-indigo .pace .pace-progress {\n  background-color: #6610f2;\n  box-shadow: inset -1px 0 #6610f2, inset 0 -1px #6610f2, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-indigo .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-indigo .pace-progress {\n  color: #6610f2;\n}\n\n.pace-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-barber-shop-purple .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-barber-shop-purple .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-purple .pace .pace-progress::after {\n  color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-bounce-purple .pace .pace-activity {\n  background: #6f42c1;\n}\n\n.pace-center-atom-purple .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-purple .pace-progress::before {\n  background: #6f42c1;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-purple .pace-activity {\n  border-color: #6f42c1;\n}\n\n.pace-center-atom-purple .pace-activity::after, .pace-center-atom-purple .pace-activity::before {\n  border-color: #6f42c1;\n}\n\n.pace-center-circle-purple .pace .pace-progress {\n  background: rgba(111, 66, 193, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-purple .pace .pace-activity {\n  border-color: #6f42c1 transparent transparent;\n}\n\n.pace-center-radar-purple .pace .pace-activity::before {\n  border-color: #6f42c1 transparent transparent;\n}\n\n.pace-center-simple-purple .pace {\n  background: #fff;\n  border-color: #6f42c1;\n}\n\n.pace-center-simple-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-material-purple .pace {\n  color: #6f42c1;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity {\n  background: #6f42c1;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::after,\n.pace-corner-indicator-purple .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::before {\n  border-right-color: rgba(111, 66, 193, 0.2);\n  border-left-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::after {\n  border-top-color: rgba(111, 66, 193, 0.2);\n  border-bottom-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-fill-left-purple .pace .pace-progress {\n  background-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-flash-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-flash-purple .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6f42c1, 0 0 5px #6f42c1;\n}\n\n.pace-flash-purple .pace .pace-activity {\n  border-top-color: #6f42c1;\n  border-left-color: #6f42c1;\n}\n\n.pace-loading-bar-purple .pace .pace-progress {\n  background: #6f42c1;\n  color: #6f42c1;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-purple .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6f42c1, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-purple .pace .pace-progress {\n  background-color: #6f42c1;\n  box-shadow: inset -1px 0 #6f42c1, inset 0 -1px #6f42c1, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-purple .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-purple .pace-progress {\n  color: #6f42c1;\n}\n\n.pace-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-barber-shop-pink .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-barber-shop-pink .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-pink .pace .pace-progress::after {\n  color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-bounce-pink .pace .pace-activity {\n  background: #e83e8c;\n}\n\n.pace-center-atom-pink .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-pink .pace-progress::before {\n  background: #e83e8c;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-pink .pace-activity {\n  border-color: #e83e8c;\n}\n\n.pace-center-atom-pink .pace-activity::after, .pace-center-atom-pink .pace-activity::before {\n  border-color: #e83e8c;\n}\n\n.pace-center-circle-pink .pace .pace-progress {\n  background: rgba(232, 62, 140, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-pink .pace .pace-activity {\n  border-color: #e83e8c transparent transparent;\n}\n\n.pace-center-radar-pink .pace .pace-activity::before {\n  border-color: #e83e8c transparent transparent;\n}\n\n.pace-center-simple-pink .pace {\n  background: #fff;\n  border-color: #e83e8c;\n}\n\n.pace-center-simple-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-material-pink .pace {\n  color: #e83e8c;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity {\n  background: #e83e8c;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::after,\n.pace-corner-indicator-pink .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::before {\n  border-right-color: rgba(232, 62, 140, 0.2);\n  border-left-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::after {\n  border-top-color: rgba(232, 62, 140, 0.2);\n  border-bottom-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-fill-left-pink .pace .pace-progress {\n  background-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-flash-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-flash-pink .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #e83e8c, 0 0 5px #e83e8c;\n}\n\n.pace-flash-pink .pace .pace-activity {\n  border-top-color: #e83e8c;\n  border-left-color: #e83e8c;\n}\n\n.pace-loading-bar-pink .pace .pace-progress {\n  background: #e83e8c;\n  color: #e83e8c;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-pink .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #e83e8c, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-pink .pace .pace-progress {\n  background-color: #e83e8c;\n  box-shadow: inset -1px 0 #e83e8c, inset 0 -1px #e83e8c, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-pink .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-pink .pace-progress {\n  color: #e83e8c;\n}\n\n.pace-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-red .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-red .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-red .pace .pace-progress::after {\n  color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-bounce-red .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-center-atom-red .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-red .pace-progress::before {\n  background: #dc3545;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-red .pace-activity {\n  border-color: #dc3545;\n}\n\n.pace-center-atom-red .pace-activity::after, .pace-center-atom-red .pace-activity::before {\n  border-color: #dc3545;\n}\n\n.pace-center-circle-red .pace .pace-progress {\n  background: rgba(220, 53, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-red .pace .pace-activity {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-radar-red .pace .pace-activity::before {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-simple-red .pace {\n  background: #fff;\n  border-color: #dc3545;\n}\n\n.pace-center-simple-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-material-red .pace {\n  color: #dc3545;\n}\n\n.pace-corner-indicator-red .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-corner-indicator-red .pace .pace-activity::after,\n.pace-corner-indicator-red .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-red .pace .pace-activity::before {\n  border-right-color: rgba(220, 53, 69, 0.2);\n  border-left-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-corner-indicator-red .pace .pace-activity::after {\n  border-top-color: rgba(220, 53, 69, 0.2);\n  border-bottom-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-fill-left-red .pace .pace-progress {\n  background-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-flash-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-flash-red .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #dc3545, 0 0 5px #dc3545;\n}\n\n.pace-flash-red .pace .pace-activity {\n  border-top-color: #dc3545;\n  border-left-color: #dc3545;\n}\n\n.pace-loading-bar-red .pace .pace-progress {\n  background: #dc3545;\n  color: #dc3545;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-red .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #dc3545, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-red .pace .pace-progress {\n  background-color: #dc3545;\n  box-shadow: inset -1px 0 #dc3545, inset 0 -1px #dc3545, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-red .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-red .pace-progress {\n  color: #dc3545;\n}\n\n.pace-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-barber-shop-orange .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-barber-shop-orange .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-orange .pace .pace-progress::after {\n  color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-bounce-orange .pace .pace-activity {\n  background: #fd7e14;\n}\n\n.pace-center-atom-orange .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-orange .pace-progress::before {\n  background: #fd7e14;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-orange .pace-activity {\n  border-color: #fd7e14;\n}\n\n.pace-center-atom-orange .pace-activity::after, .pace-center-atom-orange .pace-activity::before {\n  border-color: #fd7e14;\n}\n\n.pace-center-circle-orange .pace .pace-progress {\n  background: rgba(253, 126, 20, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-orange .pace .pace-activity {\n  border-color: #fd7e14 transparent transparent;\n}\n\n.pace-center-radar-orange .pace .pace-activity::before {\n  border-color: #fd7e14 transparent transparent;\n}\n\n.pace-center-simple-orange .pace {\n  background: #1f2d3d;\n  border-color: #fd7e14;\n}\n\n.pace-center-simple-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-material-orange .pace {\n  color: #fd7e14;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity {\n  background: #fd7e14;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::after,\n.pace-corner-indicator-orange .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::before {\n  border-right-color: rgba(253, 126, 20, 0.2);\n  border-left-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::after {\n  border-top-color: rgba(253, 126, 20, 0.2);\n  border-bottom-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-fill-left-orange .pace .pace-progress {\n  background-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-flash-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-flash-orange .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #fd7e14, 0 0 5px #fd7e14;\n}\n\n.pace-flash-orange .pace .pace-activity {\n  border-top-color: #fd7e14;\n  border-left-color: #fd7e14;\n}\n\n.pace-loading-bar-orange .pace .pace-progress {\n  background: #fd7e14;\n  color: #fd7e14;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-orange .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #fd7e14, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-orange .pace .pace-progress {\n  background-color: #fd7e14;\n  box-shadow: inset -1px 0 #fd7e14, inset 0 -1px #fd7e14, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-orange .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-orange .pace-progress {\n  color: #fd7e14;\n}\n\n.pace-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-yellow .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-yellow .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-yellow .pace .pace-progress::after {\n  color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-bounce-yellow .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-center-atom-yellow .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-yellow .pace-progress::before {\n  background: #ffc107;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-yellow .pace-activity {\n  border-color: #ffc107;\n}\n\n.pace-center-atom-yellow .pace-activity::after, .pace-center-atom-yellow .pace-activity::before {\n  border-color: #ffc107;\n}\n\n.pace-center-circle-yellow .pace .pace-progress {\n  background: rgba(255, 193, 7, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-yellow .pace .pace-activity {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-radar-yellow .pace .pace-activity::before {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-simple-yellow .pace {\n  background: #1f2d3d;\n  border-color: #ffc107;\n}\n\n.pace-center-simple-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-material-yellow .pace {\n  color: #ffc107;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::after,\n.pace-corner-indicator-yellow .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::before {\n  border-right-color: rgba(255, 193, 7, 0.2);\n  border-left-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::after {\n  border-top-color: rgba(255, 193, 7, 0.2);\n  border-bottom-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-fill-left-yellow .pace .pace-progress {\n  background-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-flash-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-flash-yellow .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #ffc107, 0 0 5px #ffc107;\n}\n\n.pace-flash-yellow .pace .pace-activity {\n  border-top-color: #ffc107;\n  border-left-color: #ffc107;\n}\n\n.pace-loading-bar-yellow .pace .pace-progress {\n  background: #ffc107;\n  color: #ffc107;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-yellow .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #ffc107, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-yellow .pace .pace-progress {\n  background-color: #ffc107;\n  box-shadow: inset -1px 0 #ffc107, inset 0 -1px #ffc107, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-yellow .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-yellow .pace-progress {\n  color: #ffc107;\n}\n\n.pace-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-green .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-green .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-green .pace .pace-progress::after {\n  color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-bounce-green .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-center-atom-green .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-green .pace-progress::before {\n  background: #28a745;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-green .pace-activity {\n  border-color: #28a745;\n}\n\n.pace-center-atom-green .pace-activity::after, .pace-center-atom-green .pace-activity::before {\n  border-color: #28a745;\n}\n\n.pace-center-circle-green .pace .pace-progress {\n  background: rgba(40, 167, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-green .pace .pace-activity {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-radar-green .pace .pace-activity::before {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-simple-green .pace {\n  background: #fff;\n  border-color: #28a745;\n}\n\n.pace-center-simple-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-material-green .pace {\n  color: #28a745;\n}\n\n.pace-corner-indicator-green .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-corner-indicator-green .pace .pace-activity::after,\n.pace-corner-indicator-green .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-green .pace .pace-activity::before {\n  border-right-color: rgba(40, 167, 69, 0.2);\n  border-left-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-corner-indicator-green .pace .pace-activity::after {\n  border-top-color: rgba(40, 167, 69, 0.2);\n  border-bottom-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-fill-left-green .pace .pace-progress {\n  background-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-flash-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-flash-green .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #28a745, 0 0 5px #28a745;\n}\n\n.pace-flash-green .pace .pace-activity {\n  border-top-color: #28a745;\n  border-left-color: #28a745;\n}\n\n.pace-loading-bar-green .pace .pace-progress {\n  background: #28a745;\n  color: #28a745;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-green .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #28a745, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-green .pace .pace-progress {\n  background-color: #28a745;\n  box-shadow: inset -1px 0 #28a745, inset 0 -1px #28a745, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-green .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-green .pace-progress {\n  color: #28a745;\n}\n\n.pace-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-barber-shop-teal .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-barber-shop-teal .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-teal .pace .pace-progress::after {\n  color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-bounce-teal .pace .pace-activity {\n  background: #20c997;\n}\n\n.pace-center-atom-teal .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-teal .pace-progress::before {\n  background: #20c997;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-teal .pace-activity {\n  border-color: #20c997;\n}\n\n.pace-center-atom-teal .pace-activity::after, .pace-center-atom-teal .pace-activity::before {\n  border-color: #20c997;\n}\n\n.pace-center-circle-teal .pace .pace-progress {\n  background: rgba(32, 201, 151, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-teal .pace .pace-activity {\n  border-color: #20c997 transparent transparent;\n}\n\n.pace-center-radar-teal .pace .pace-activity::before {\n  border-color: #20c997 transparent transparent;\n}\n\n.pace-center-simple-teal .pace {\n  background: #fff;\n  border-color: #20c997;\n}\n\n.pace-center-simple-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-material-teal .pace {\n  color: #20c997;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity {\n  background: #20c997;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::after,\n.pace-corner-indicator-teal .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::before {\n  border-right-color: rgba(32, 201, 151, 0.2);\n  border-left-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::after {\n  border-top-color: rgba(32, 201, 151, 0.2);\n  border-bottom-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-fill-left-teal .pace .pace-progress {\n  background-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-flash-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-flash-teal .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #20c997, 0 0 5px #20c997;\n}\n\n.pace-flash-teal .pace .pace-activity {\n  border-top-color: #20c997;\n  border-left-color: #20c997;\n}\n\n.pace-loading-bar-teal .pace .pace-progress {\n  background: #20c997;\n  color: #20c997;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-teal .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #20c997, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-teal .pace .pace-progress {\n  background-color: #20c997;\n  box-shadow: inset -1px 0 #20c997, inset 0 -1px #20c997, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-teal .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-teal .pace-progress {\n  color: #20c997;\n}\n\n.pace-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-cyan .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-cyan .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-cyan .pace .pace-progress::after {\n  color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-bounce-cyan .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-center-atom-cyan .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-cyan .pace-progress::before {\n  background: #17a2b8;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-cyan .pace-activity {\n  border-color: #17a2b8;\n}\n\n.pace-center-atom-cyan .pace-activity::after, .pace-center-atom-cyan .pace-activity::before {\n  border-color: #17a2b8;\n}\n\n.pace-center-circle-cyan .pace .pace-progress {\n  background: rgba(23, 162, 184, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-cyan .pace .pace-activity {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-radar-cyan .pace .pace-activity::before {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-simple-cyan .pace {\n  background: #fff;\n  border-color: #17a2b8;\n}\n\n.pace-center-simple-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-material-cyan .pace {\n  color: #17a2b8;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::after,\n.pace-corner-indicator-cyan .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::before {\n  border-right-color: rgba(23, 162, 184, 0.2);\n  border-left-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::after {\n  border-top-color: rgba(23, 162, 184, 0.2);\n  border-bottom-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-fill-left-cyan .pace .pace-progress {\n  background-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-flash-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-flash-cyan .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #17a2b8, 0 0 5px #17a2b8;\n}\n\n.pace-flash-cyan .pace .pace-activity {\n  border-top-color: #17a2b8;\n  border-left-color: #17a2b8;\n}\n\n.pace-loading-bar-cyan .pace .pace-progress {\n  background: #17a2b8;\n  color: #17a2b8;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-cyan .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #17a2b8, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-cyan .pace .pace-progress {\n  background-color: #17a2b8;\n  box-shadow: inset -1px 0 #17a2b8, inset 0 -1px #17a2b8, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-cyan .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-cyan .pace-progress {\n  color: #17a2b8;\n}\n\n.pace-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-barber-shop-white .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-barber-shop-white .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-white .pace .pace-progress::after {\n  color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-bounce-white .pace .pace-activity {\n  background: #fff;\n}\n\n.pace-center-atom-white .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-white .pace-progress::before {\n  background: #fff;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-white .pace-activity {\n  border-color: #fff;\n}\n\n.pace-center-atom-white .pace-activity::after, .pace-center-atom-white .pace-activity::before {\n  border-color: #fff;\n}\n\n.pace-center-circle-white .pace .pace-progress {\n  background: rgba(255, 255, 255, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-white .pace .pace-activity {\n  border-color: #fff transparent transparent;\n}\n\n.pace-center-radar-white .pace .pace-activity::before {\n  border-color: #fff transparent transparent;\n}\n\n.pace-center-simple-white .pace {\n  background: #1f2d3d;\n  border-color: #fff;\n}\n\n.pace-center-simple-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-material-white .pace {\n  color: #fff;\n}\n\n.pace-corner-indicator-white .pace .pace-activity {\n  background: #fff;\n}\n\n.pace-corner-indicator-white .pace .pace-activity::after,\n.pace-corner-indicator-white .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-white .pace .pace-activity::before {\n  border-right-color: rgba(255, 255, 255, 0.2);\n  border-left-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-corner-indicator-white .pace .pace-activity::after {\n  border-top-color: rgba(255, 255, 255, 0.2);\n  border-bottom-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-fill-left-white .pace .pace-progress {\n  background-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-flash-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-flash-white .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #fff, 0 0 5px #fff;\n}\n\n.pace-flash-white .pace .pace-activity {\n  border-top-color: #fff;\n  border-left-color: #fff;\n}\n\n.pace-loading-bar-white .pace .pace-progress {\n  background: #fff;\n  color: #fff;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-white .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #fff, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-white .pace .pace-progress {\n  background-color: #fff;\n  box-shadow: inset -1px 0 #fff, inset 0 -1px #fff, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-white .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-white .pace-progress {\n  color: #fff;\n}\n\n.pace-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-gray .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-gray .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-gray .pace .pace-progress::after {\n  color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-bounce-gray .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-center-atom-gray .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-gray .pace-progress::before {\n  background: #6c757d;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-gray .pace-activity {\n  border-color: #6c757d;\n}\n\n.pace-center-atom-gray .pace-activity::after, .pace-center-atom-gray .pace-activity::before {\n  border-color: #6c757d;\n}\n\n.pace-center-circle-gray .pace .pace-progress {\n  background: rgba(108, 117, 125, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-gray .pace .pace-activity {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-radar-gray .pace .pace-activity::before {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-simple-gray .pace {\n  background: #fff;\n  border-color: #6c757d;\n}\n\n.pace-center-simple-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-material-gray .pace {\n  color: #6c757d;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::after,\n.pace-corner-indicator-gray .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::before {\n  border-right-color: rgba(108, 117, 125, 0.2);\n  border-left-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::after {\n  border-top-color: rgba(108, 117, 125, 0.2);\n  border-bottom-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-fill-left-gray .pace .pace-progress {\n  background-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-flash-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-flash-gray .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6c757d, 0 0 5px #6c757d;\n}\n\n.pace-flash-gray .pace .pace-activity {\n  border-top-color: #6c757d;\n  border-left-color: #6c757d;\n}\n\n.pace-loading-bar-gray .pace .pace-progress {\n  background: #6c757d;\n  color: #6c757d;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-gray .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6c757d, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-gray .pace .pace-progress {\n  background-color: #6c757d;\n  box-shadow: inset -1px 0 #6c757d, inset 0 -1px #6c757d, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-gray .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-gray .pace-progress {\n  color: #6c757d;\n}\n\n.pace-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-gray-dark .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-gray-dark .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-gray-dark .pace .pace-progress::after {\n  color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-bounce-gray-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-center-atom-gray-dark .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-gray-dark .pace-progress::before {\n  background: #343a40;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-gray-dark .pace-activity {\n  border-color: #343a40;\n}\n\n.pace-center-atom-gray-dark .pace-activity::after, .pace-center-atom-gray-dark .pace-activity::before {\n  border-color: #343a40;\n}\n\n.pace-center-circle-gray-dark .pace .pace-progress {\n  background: rgba(52, 58, 64, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-gray-dark .pace .pace-activity {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-radar-gray-dark .pace .pace-activity::before {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-simple-gray-dark .pace {\n  background: #fff;\n  border-color: #343a40;\n}\n\n.pace-center-simple-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-material-gray-dark .pace {\n  color: #343a40;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::after,\n.pace-corner-indicator-gray-dark .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::before {\n  border-right-color: rgba(52, 58, 64, 0.2);\n  border-left-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::after {\n  border-top-color: rgba(52, 58, 64, 0.2);\n  border-bottom-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-fill-left-gray-dark .pace .pace-progress {\n  background-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-flash-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-flash-gray-dark .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #343a40, 0 0 5px #343a40;\n}\n\n.pace-flash-gray-dark .pace .pace-activity {\n  border-top-color: #343a40;\n  border-left-color: #343a40;\n}\n\n.pace-loading-bar-gray-dark .pace .pace-progress {\n  background: #343a40;\n  color: #343a40;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-gray-dark .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #343a40, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-gray-dark .pace .pace-progress {\n  background-color: #343a40;\n  box-shadow: inset -1px 0 #343a40, inset 0 -1px #343a40, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-gray-dark .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-gray-dark .pace-progress {\n  color: #343a40;\n}\n\n/**\n  * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches.\n  *\n  * @version v3.4 (MODDED)\n  * @homepage https://bttstrp.github.io/bootstrap-switch\n  * @author Mattia Larentis <mattia@larentis.eu> (http://larentis.eu)\n  * @license MIT\n  */\n.bootstrap-switch {\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  cursor: pointer;\n  direction: ltr;\n  display: inline-block;\n  line-height: .5rem;\n  overflow: hidden;\n  position: relative;\n  text-align: left;\n  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  vertical-align: middle;\n  z-index: 0;\n}\n\n.bootstrap-switch .bootstrap-switch-container {\n  border-radius: 0.25rem;\n  display: inline-block;\n  top: 0;\n  -webkit-transform: translate3d(0, 0, 0);\n  transform: translate3d(0, 0, 0);\n}\n\n.bootstrap-switch:focus-within {\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on,\n.bootstrap-switch .bootstrap-switch-handle-off,\n.bootstrap-switch .bootstrap-switch-label {\n  box-sizing: border-box;\n  cursor: pointer;\n  display: table-cell;\n  font-size: 1rem;\n  font-weight: 500;\n  line-height: 1.2rem;\n  padding: .25rem .5rem;\n  vertical-align: middle;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on,\n.bootstrap-switch .bootstrap-switch-handle-off {\n  text-align: center;\n  z-index: 1;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default {\n  background: #e9ecef;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary {\n  background: #007bff;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-secondary,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-secondary {\n  background: #6c757d;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {\n  background: #28a745;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info {\n  background: #17a2b8;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning {\n  background: #ffc107;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger {\n  background: #dc3545;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-light,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-light {\n  background: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-dark,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lightblue,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lightblue {\n  background: #3c8dbc;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-navy,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-navy {\n  background: #001f3f;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-olive,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-olive {\n  background: #3d9970;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lime,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lime {\n  background: #01ff70;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-fuchsia,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-fuchsia {\n  background: #f012be;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-maroon,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-maroon {\n  background: #d81b60;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-blue,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-blue {\n  background: #007bff;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-indigo,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-indigo {\n  background: #6610f2;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-purple,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-purple {\n  background: #6f42c1;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-pink,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-pink {\n  background: #e83e8c;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-red,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-red {\n  background: #dc3545;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-orange,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-orange {\n  background: #fd7e14;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-yellow,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-yellow {\n  background: #ffc107;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-green,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-green {\n  background: #28a745;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-teal,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-teal {\n  background: #20c997;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-cyan,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-cyan {\n  background: #17a2b8;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-white,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-white {\n  background: #fff;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray {\n  background: #6c757d;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray-dark,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on {\n  border-bottom-left-radius: 0.1rem;\n  border-top-left-radius: 0.1rem;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-off {\n  border-bottom-right-radius: 0.1rem;\n  border-top-right-radius: 0.1rem;\n}\n\n.bootstrap-switch input[type='radio'],\n.bootstrap-switch input[type='checkbox'] {\n  filter: alpha(opacity=0);\n  left: 0;\n  margin: 0;\n  opacity: 0;\n  position: absolute;\n  top: 0;\n  visibility: hidden;\n  z-index: -1;\n}\n\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label {\n  font-size: .875rem;\n  line-height: 1.5;\n  padding: .1rem .3rem;\n}\n\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label {\n  font-size: .875rem;\n  line-height: 1.5;\n  padding: .2rem .4rem;\n}\n\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label {\n  font-size: 1.25rem;\n  line-height: 1.3333333rem;\n  padding: .3rem .5rem;\n}\n\n.bootstrap-switch.bootstrap-switch-disabled, .bootstrap-switch.bootstrap-switch-readonly, .bootstrap-switch.bootstrap-switch-indeterminate {\n  cursor: default;\n}\n\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label, .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label, .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label {\n  cursor: default;\n  filter: alpha(opacity=50);\n  opacity: .5;\n}\n\n.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container {\n  transition: margin-left .5s;\n}\n\n.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on {\n  border-radius: 0 0.1rem 0.1rem 0;\n}\n\n.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off {\n  border-radius: 0.1rem 0 0 0.1rem;\n}\n\n.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label,\n.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label {\n  border-bottom-right-radius: 0.1rem;\n  border-top-right-radius: 0.1rem;\n}\n\n.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label,\n.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label {\n  border-bottom-left-radius: 0.1rem;\n  border-top-left-radius: 0.1rem;\n}\n\n.jqstooltip {\n  height: auto !important;\n  padding: 5px !important;\n  width: auto !important;\n}\n\n.connectedSortable {\n  min-height: 100px;\n}\n\n.ui-helper-hidden-accessible {\n  border: 0;\n  clip: rect(0 0 0 0);\n  height: 1px;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0;\n  position: absolute;\n  width: 1px;\n}\n\n.sort-highlight {\n  background: #f8f9fa;\n  border: 1px dashed #dee2e6;\n  margin-bottom: 10px;\n}\n\n.chart {\n  overflow: hidden;\n  position: relative;\n}\n\n.border-transparent {\n  border-color: transparent !important;\n}\n\n.description-block {\n  display: block;\n  margin: 10px 0;\n  text-align: center;\n}\n\n.description-block.margin-bottom {\n  margin-bottom: 25px;\n}\n\n.description-block > .description-header {\n  font-size: 16px;\n  font-weight: 600;\n  margin: 0;\n  padding: 0;\n}\n\n.description-block > .description-text {\n  text-transform: uppercase;\n}\n\n.description-block .description-icon {\n  font-size: 16px;\n}\n\n.list-group-unbordered > .list-group-item {\n  border-left: 0;\n  border-radius: 0;\n  border-right: 0;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.list-header {\n  color: #6c757d;\n  font-size: 15px;\n  font-weight: 700;\n  padding: 10px 4px;\n}\n\n.list-seperator {\n  background-color: rgba(0, 0, 0, 0.125);\n  height: 1px;\n  margin: 15px 0 9px;\n}\n\n.list-link > a {\n  color: #6c757d;\n  padding: 4px;\n}\n\n.list-link > a:hover {\n  color: #212529;\n}\n\n.user-block {\n  float: left;\n}\n\n.user-block img {\n  float: left;\n  height: 40px;\n  width: 40px;\n}\n\n.user-block .username,\n.user-block .description,\n.user-block .comment {\n  display: block;\n  margin-left: 50px;\n}\n\n.user-block .username {\n  font-size: 16px;\n  font-weight: 600;\n  margin-top: -1px;\n}\n\n.user-block .description {\n  color: #6c757d;\n  font-size: 13px;\n  margin-top: -3px;\n}\n\n.user-block.user-block-sm img {\n  width: 1.875rem;\n  height: 1.875rem;\n}\n\n.user-block.user-block-sm .username,\n.user-block.user-block-sm .description,\n.user-block.user-block-sm .comment {\n  margin-left: 40px;\n}\n\n.user-block.user-block-sm .username {\n  font-size: 14px;\n}\n\n.img-sm,\n.img-md,\n.img-lg {\n  float: left;\n}\n\n.img-sm {\n  height: 1.875rem;\n  width: 1.875rem;\n}\n\n.img-sm + .img-push {\n  margin-left: 2.5rem;\n}\n\n.img-md {\n  width: 3.75rem;\n  height: 3.75rem;\n}\n\n.img-md + .img-push {\n  margin-left: 4.375rem;\n}\n\n.img-lg {\n  width: 6.25rem;\n  height: 6.25rem;\n}\n\n.img-lg + .img-push {\n  margin-left: 6.875rem;\n}\n\n.img-bordered {\n  border: 3px solid #adb5bd;\n  padding: 3px;\n}\n\n.img-bordered-sm {\n  border: 2px solid #adb5bd;\n  padding: 2px;\n}\n\n.img-rounded {\n  border-radius: 0.25rem;\n}\n\n.img-circle {\n  border-radius: 50%;\n}\n\n.img-size-64,\n.img-size-50,\n.img-size-32 {\n  height: auto;\n}\n\n.img-size-64 {\n  width: 64px;\n}\n\n.img-size-50 {\n  width: 50px;\n}\n\n.img-size-32 {\n  width: 32px;\n}\n\n.size-32,\n.size-40,\n.size-50 {\n  display: block;\n  text-align: center;\n}\n\n.size-32 {\n  height: 32px;\n  line-height: 32px;\n  width: 32px;\n}\n\n.size-40 {\n  height: 40px;\n  line-height: 40px;\n  width: 40px;\n}\n\n.size-50 {\n  height: 50px;\n  line-height: 50px;\n  width: 50px;\n}\n\n.attachment-block {\n  background-color: #f8f9fa;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n  margin-bottom: 10px;\n  padding: 5px;\n}\n\n.attachment-block .attachment-img {\n  float: left;\n  height: auto;\n  max-height: 100px;\n  max-width: 100px;\n}\n\n.attachment-block .attachment-pushed {\n  margin-left: 110px;\n}\n\n.attachment-block .attachment-heading {\n  margin: 0;\n}\n\n.attachment-block .attachment-text {\n  color: #495057;\n}\n\n.card > .overlay,\n.card > .loading-img,\n.overlay-wrapper > .overlay,\n.overlay-wrapper > .loading-img,\n.info-box > .overlay,\n.info-box > .loading-img,\n.small-box > .overlay,\n.small-box > .loading-img {\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.card .overlay,\n.overlay-wrapper .overlay,\n.info-box .overlay,\n.small-box .overlay {\n  border-radius: 0.25rem;\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: rgba(255, 255, 255, 0.7);\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  z-index: 50;\n}\n\n.card .overlay > .fa,\n.card .overlay > .fas,\n.card .overlay > .far,\n.card .overlay > .fab,\n.card .overlay > .fal,\n.card .overlay > .fad,\n.card .overlay > .svg-inline--fa,\n.card .overlay > .ion,\n.overlay-wrapper .overlay > .fa,\n.overlay-wrapper .overlay > .fas,\n.overlay-wrapper .overlay > .far,\n.overlay-wrapper .overlay > .fab,\n.overlay-wrapper .overlay > .fal,\n.overlay-wrapper .overlay > .fad,\n.overlay-wrapper .overlay > .svg-inline--fa,\n.overlay-wrapper .overlay > .ion,\n.info-box .overlay > .fa,\n.info-box .overlay > .fas,\n.info-box .overlay > .far,\n.info-box .overlay > .fab,\n.info-box .overlay > .fal,\n.info-box .overlay > .fad,\n.info-box .overlay > .svg-inline--fa,\n.info-box .overlay > .ion,\n.small-box .overlay > .fa,\n.small-box .overlay > .fas,\n.small-box .overlay > .far,\n.small-box .overlay > .fab,\n.small-box .overlay > .fal,\n.small-box .overlay > .fad,\n.small-box .overlay > .svg-inline--fa,\n.small-box .overlay > .ion {\n  color: #343a40;\n}\n\n.card .overlay.dark,\n.overlay-wrapper .overlay.dark,\n.info-box .overlay.dark,\n.small-box .overlay.dark {\n  background-color: rgba(0, 0, 0, 0.5);\n}\n\n.card .overlay.dark > .fa,\n.card .overlay.dark > .fas,\n.card .overlay.dark > .far,\n.card .overlay.dark > .fab,\n.card .overlay.dark > .fal,\n.card .overlay.dark > .fad,\n.card .overlay.dark > .svg-inline--fa,\n.card .overlay.dark > .ion,\n.overlay-wrapper .overlay.dark > .fa,\n.overlay-wrapper .overlay.dark > .fas,\n.overlay-wrapper .overlay.dark > .far,\n.overlay-wrapper .overlay.dark > .fab,\n.overlay-wrapper .overlay.dark > .fal,\n.overlay-wrapper .overlay.dark > .fad,\n.overlay-wrapper .overlay.dark > .svg-inline--fa,\n.overlay-wrapper .overlay.dark > .ion,\n.info-box .overlay.dark > .fa,\n.info-box .overlay.dark > .fas,\n.info-box .overlay.dark > .far,\n.info-box .overlay.dark > .fab,\n.info-box .overlay.dark > .fal,\n.info-box .overlay.dark > .fad,\n.info-box .overlay.dark > .svg-inline--fa,\n.info-box .overlay.dark > .ion,\n.small-box .overlay.dark > .fa,\n.small-box .overlay.dark > .fas,\n.small-box .overlay.dark > .far,\n.small-box .overlay.dark > .fab,\n.small-box .overlay.dark > .fal,\n.small-box .overlay.dark > .fad,\n.small-box .overlay.dark > .svg-inline--fa,\n.small-box .overlay.dark > .ion {\n  color: #ced4da;\n}\n\n.tab-pane > .overlay-wrapper {\n  position: relative;\n}\n\n.tab-pane > .overlay-wrapper > .overlay {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  margin-top: -1.25rem;\n  margin-left: -1.25rem;\n  height: calc(100% + 2 * 1.25rem);\n  width: calc(100% + 2 * 1.25rem);\n}\n\n.tab-pane > .overlay-wrapper > .overlay.dark {\n  color: #fff;\n}\n\n.ribbon-wrapper {\n  height: 70px;\n  overflow: hidden;\n  position: absolute;\n  right: -2px;\n  top: -2px;\n  width: 70px;\n  z-index: 10;\n}\n\n.ribbon-wrapper.ribbon-lg {\n  height: 120px;\n  width: 120px;\n}\n\n.ribbon-wrapper.ribbon-lg .ribbon {\n  right: 0;\n  top: 26px;\n  width: 160px;\n}\n\n.ribbon-wrapper.ribbon-xl {\n  height: 180px;\n  width: 180px;\n}\n\n.ribbon-wrapper.ribbon-xl .ribbon {\n  right: 4px;\n  top: 47px;\n  width: 240px;\n}\n\n.ribbon-wrapper .ribbon {\n  box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);\n  font-size: 0.8rem;\n  line-height: 100%;\n  padding: 0.375rem 0;\n  position: relative;\n  right: -2px;\n  text-align: center;\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);\n  text-transform: uppercase;\n  top: 10px;\n  -webkit-transform: rotate(45deg);\n  transform: rotate(45deg);\n  width: 90px;\n}\n\n.ribbon-wrapper .ribbon::before, .ribbon-wrapper .ribbon::after {\n  border-left: 3px solid transparent;\n  border-right: 3px solid transparent;\n  border-top: 3px solid #9e9e9e;\n  bottom: -3px;\n  content: \"\";\n  position: absolute;\n}\n\n.ribbon-wrapper .ribbon::before {\n  left: 0;\n}\n\n.ribbon-wrapper .ribbon::after {\n  right: 0;\n}\n\n.back-to-top {\n  bottom: 1.25rem;\n  position: fixed;\n  right: 1.25rem;\n  z-index: 1032;\n}\n\n.back-to-top:focus {\n  box-shadow: none;\n}\n\npre {\n  padding: .75rem;\n}\n\nblockquote {\n  background-color: #fff;\n  border-left: 0.7rem solid #007bff;\n  margin: 1.5em .7rem;\n  padding: .5em .7rem;\n}\n\n.box blockquote {\n  background-color: #e9ecef;\n}\n\nblockquote p:last-child {\n  margin-bottom: 0;\n}\n\nblockquote h1,\nblockquote h2,\nblockquote h3,\nblockquote h4,\nblockquote h5,\nblockquote h6 {\n  color: #007bff;\n  font-size: 1.25rem;\n  font-weight: 600;\n}\n\nblockquote.quote-primary {\n  border-color: #007bff;\n}\n\nblockquote.quote-primary h1,\nblockquote.quote-primary h2,\nblockquote.quote-primary h3,\nblockquote.quote-primary h4,\nblockquote.quote-primary h5,\nblockquote.quote-primary h6 {\n  color: #007bff;\n}\n\nblockquote.quote-secondary {\n  border-color: #6c757d;\n}\n\nblockquote.quote-secondary h1,\nblockquote.quote-secondary h2,\nblockquote.quote-secondary h3,\nblockquote.quote-secondary h4,\nblockquote.quote-secondary h5,\nblockquote.quote-secondary h6 {\n  color: #6c757d;\n}\n\nblockquote.quote-success {\n  border-color: #28a745;\n}\n\nblockquote.quote-success h1,\nblockquote.quote-success h2,\nblockquote.quote-success h3,\nblockquote.quote-success h4,\nblockquote.quote-success h5,\nblockquote.quote-success h6 {\n  color: #28a745;\n}\n\nblockquote.quote-info {\n  border-color: #17a2b8;\n}\n\nblockquote.quote-info h1,\nblockquote.quote-info h2,\nblockquote.quote-info h3,\nblockquote.quote-info h4,\nblockquote.quote-info h5,\nblockquote.quote-info h6 {\n  color: #17a2b8;\n}\n\nblockquote.quote-warning {\n  border-color: #ffc107;\n}\n\nblockquote.quote-warning h1,\nblockquote.quote-warning h2,\nblockquote.quote-warning h3,\nblockquote.quote-warning h4,\nblockquote.quote-warning h5,\nblockquote.quote-warning h6 {\n  color: #ffc107;\n}\n\nblockquote.quote-danger {\n  border-color: #dc3545;\n}\n\nblockquote.quote-danger h1,\nblockquote.quote-danger h2,\nblockquote.quote-danger h3,\nblockquote.quote-danger h4,\nblockquote.quote-danger h5,\nblockquote.quote-danger h6 {\n  color: #dc3545;\n}\n\nblockquote.quote-light {\n  border-color: #f8f9fa;\n}\n\nblockquote.quote-light h1,\nblockquote.quote-light h2,\nblockquote.quote-light h3,\nblockquote.quote-light h4,\nblockquote.quote-light h5,\nblockquote.quote-light h6 {\n  color: #f8f9fa;\n}\n\nblockquote.quote-dark {\n  border-color: #343a40;\n}\n\nblockquote.quote-dark h1,\nblockquote.quote-dark h2,\nblockquote.quote-dark h3,\nblockquote.quote-dark h4,\nblockquote.quote-dark h5,\nblockquote.quote-dark h6 {\n  color: #343a40;\n}\n\nblockquote.quote-lightblue {\n  border-color: #3c8dbc;\n}\n\nblockquote.quote-lightblue h1,\nblockquote.quote-lightblue h2,\nblockquote.quote-lightblue h3,\nblockquote.quote-lightblue h4,\nblockquote.quote-lightblue h5,\nblockquote.quote-lightblue h6 {\n  color: #3c8dbc;\n}\n\nblockquote.quote-navy {\n  border-color: #001f3f;\n}\n\nblockquote.quote-navy h1,\nblockquote.quote-navy h2,\nblockquote.quote-navy h3,\nblockquote.quote-navy h4,\nblockquote.quote-navy h5,\nblockquote.quote-navy h6 {\n  color: #001f3f;\n}\n\nblockquote.quote-olive {\n  border-color: #3d9970;\n}\n\nblockquote.quote-olive h1,\nblockquote.quote-olive h2,\nblockquote.quote-olive h3,\nblockquote.quote-olive h4,\nblockquote.quote-olive h5,\nblockquote.quote-olive h6 {\n  color: #3d9970;\n}\n\nblockquote.quote-lime {\n  border-color: #01ff70;\n}\n\nblockquote.quote-lime h1,\nblockquote.quote-lime h2,\nblockquote.quote-lime h3,\nblockquote.quote-lime h4,\nblockquote.quote-lime h5,\nblockquote.quote-lime h6 {\n  color: #01ff70;\n}\n\nblockquote.quote-fuchsia {\n  border-color: #f012be;\n}\n\nblockquote.quote-fuchsia h1,\nblockquote.quote-fuchsia h2,\nblockquote.quote-fuchsia h3,\nblockquote.quote-fuchsia h4,\nblockquote.quote-fuchsia h5,\nblockquote.quote-fuchsia h6 {\n  color: #f012be;\n}\n\nblockquote.quote-maroon {\n  border-color: #d81b60;\n}\n\nblockquote.quote-maroon h1,\nblockquote.quote-maroon h2,\nblockquote.quote-maroon h3,\nblockquote.quote-maroon h4,\nblockquote.quote-maroon h5,\nblockquote.quote-maroon h6 {\n  color: #d81b60;\n}\n\nblockquote.quote-blue {\n  border-color: #007bff;\n}\n\nblockquote.quote-blue h1,\nblockquote.quote-blue h2,\nblockquote.quote-blue h3,\nblockquote.quote-blue h4,\nblockquote.quote-blue h5,\nblockquote.quote-blue h6 {\n  color: #007bff;\n}\n\nblockquote.quote-indigo {\n  border-color: #6610f2;\n}\n\nblockquote.quote-indigo h1,\nblockquote.quote-indigo h2,\nblockquote.quote-indigo h3,\nblockquote.quote-indigo h4,\nblockquote.quote-indigo h5,\nblockquote.quote-indigo h6 {\n  color: #6610f2;\n}\n\nblockquote.quote-purple {\n  border-color: #6f42c1;\n}\n\nblockquote.quote-purple h1,\nblockquote.quote-purple h2,\nblockquote.quote-purple h3,\nblockquote.quote-purple h4,\nblockquote.quote-purple h5,\nblockquote.quote-purple h6 {\n  color: #6f42c1;\n}\n\nblockquote.quote-pink {\n  border-color: #e83e8c;\n}\n\nblockquote.quote-pink h1,\nblockquote.quote-pink h2,\nblockquote.quote-pink h3,\nblockquote.quote-pink h4,\nblockquote.quote-pink h5,\nblockquote.quote-pink h6 {\n  color: #e83e8c;\n}\n\nblockquote.quote-red {\n  border-color: #dc3545;\n}\n\nblockquote.quote-red h1,\nblockquote.quote-red h2,\nblockquote.quote-red h3,\nblockquote.quote-red h4,\nblockquote.quote-red h5,\nblockquote.quote-red h6 {\n  color: #dc3545;\n}\n\nblockquote.quote-orange {\n  border-color: #fd7e14;\n}\n\nblockquote.quote-orange h1,\nblockquote.quote-orange h2,\nblockquote.quote-orange h3,\nblockquote.quote-orange h4,\nblockquote.quote-orange h5,\nblockquote.quote-orange h6 {\n  color: #fd7e14;\n}\n\nblockquote.quote-yellow {\n  border-color: #ffc107;\n}\n\nblockquote.quote-yellow h1,\nblockquote.quote-yellow h2,\nblockquote.quote-yellow h3,\nblockquote.quote-yellow h4,\nblockquote.quote-yellow h5,\nblockquote.quote-yellow h6 {\n  color: #ffc107;\n}\n\nblockquote.quote-green {\n  border-color: #28a745;\n}\n\nblockquote.quote-green h1,\nblockquote.quote-green h2,\nblockquote.quote-green h3,\nblockquote.quote-green h4,\nblockquote.quote-green h5,\nblockquote.quote-green h6 {\n  color: #28a745;\n}\n\nblockquote.quote-teal {\n  border-color: #20c997;\n}\n\nblockquote.quote-teal h1,\nblockquote.quote-teal h2,\nblockquote.quote-teal h3,\nblockquote.quote-teal h4,\nblockquote.quote-teal h5,\nblockquote.quote-teal h6 {\n  color: #20c997;\n}\n\nblockquote.quote-cyan {\n  border-color: #17a2b8;\n}\n\nblockquote.quote-cyan h1,\nblockquote.quote-cyan h2,\nblockquote.quote-cyan h3,\nblockquote.quote-cyan h4,\nblockquote.quote-cyan h5,\nblockquote.quote-cyan h6 {\n  color: #17a2b8;\n}\n\nblockquote.quote-white {\n  border-color: #fff;\n}\n\nblockquote.quote-white h1,\nblockquote.quote-white h2,\nblockquote.quote-white h3,\nblockquote.quote-white h4,\nblockquote.quote-white h5,\nblockquote.quote-white h6 {\n  color: #fff;\n}\n\nblockquote.quote-gray {\n  border-color: #6c757d;\n}\n\nblockquote.quote-gray h1,\nblockquote.quote-gray h2,\nblockquote.quote-gray h3,\nblockquote.quote-gray h4,\nblockquote.quote-gray h5,\nblockquote.quote-gray h6 {\n  color: #6c757d;\n}\n\nblockquote.quote-gray-dark {\n  border-color: #343a40;\n}\n\nblockquote.quote-gray-dark h1,\nblockquote.quote-gray-dark h2,\nblockquote.quote-gray-dark h3,\nblockquote.quote-gray-dark h4,\nblockquote.quote-gray-dark h5,\nblockquote.quote-gray-dark h6 {\n  color: #343a40;\n}\n\n.tab-custom-content {\n  border-top: 1px solid #dee2e6;\n  margin-top: .5rem;\n  padding-top: .5rem;\n}\n\n.nav + .tab-custom-content {\n  border-top: none;\n  border-bottom: 1px solid #dee2e6;\n  margin-top: 0;\n  margin-bottom: .5rem;\n  padding-bottom: .5rem;\n}\n\n.badge-btn {\n  border-radius: 0.15rem;\n  font-size: 0.75rem;\n  font-weight: 400;\n  padding: 0.25rem 0.5rem;\n}\n\n.badge-btn.badge-pill {\n  padding: .375rem .6rem;\n}\n\n@media print {\n  .no-print, .main-sidebar,\n  .main-header,\n  .content-header {\n    display: none !important;\n  }\n  .content-wrapper,\n  .main-footer {\n    -webkit-transform: translate(0, 0);\n    transform: translate(0, 0);\n    margin-left: 0 !important;\n    min-height: 0 !important;\n  }\n  .layout-fixed .content-wrapper {\n    padding-top: 0 !important;\n  }\n  .invoice {\n    border: 0;\n    margin: 0;\n    padding: 0;\n    width: 100%;\n  }\n  .invoice-col {\n    float: left;\n    width: 33.3333333%;\n  }\n  .table-responsive {\n    overflow: auto;\n  }\n  .table-responsive > .table tr th,\n  .table-responsive > .table tr td {\n    white-space: normal !important;\n  }\n}\n\n.text-bold,\n.text-bold.table td,\n.text-bold.table th {\n  font-weight: 700;\n}\n\n.text-xs {\n  font-size: 0.75rem !important;\n}\n\n.text-sm {\n  font-size: 0.875rem !important;\n}\n\n.text-md {\n  font-size: 1rem !important;\n}\n\n.text-lg {\n  font-size: 1.25rem !important;\n}\n\n.text-xl {\n  font-size: 2rem !important;\n}\n\n.text-lightblue {\n  color: #3c8dbc !important;\n}\n\n.text-navy {\n  color: #001f3f !important;\n}\n\n.text-olive {\n  color: #3d9970 !important;\n}\n\n.text-lime {\n  color: #01ff70 !important;\n}\n\n.text-fuchsia {\n  color: #f012be !important;\n}\n\n.text-maroon {\n  color: #d81b60 !important;\n}\n\n.text-blue {\n  color: #007bff !important;\n}\n\n.text-indigo {\n  color: #6610f2 !important;\n}\n\n.text-purple {\n  color: #6f42c1 !important;\n}\n\n.text-pink {\n  color: #e83e8c !important;\n}\n\n.text-red {\n  color: #dc3545 !important;\n}\n\n.text-orange {\n  color: #fd7e14 !important;\n}\n\n.text-yellow {\n  color: #ffc107 !important;\n}\n\n.text-green {\n  color: #28a745 !important;\n}\n\n.text-teal {\n  color: #20c997 !important;\n}\n\n.text-cyan {\n  color: #17a2b8 !important;\n}\n\n.text-white {\n  color: #fff !important;\n}\n\n.text-gray {\n  color: #6c757d !important;\n}\n\n.text-gray-dark {\n  color: #343a40 !important;\n}\n\n.elevation-0 {\n  box-shadow: none !important;\n}\n\n.elevation-1 {\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) !important;\n}\n\n.elevation-2 {\n  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) !important;\n}\n\n.elevation-3 {\n  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) !important;\n}\n\n.elevation-4 {\n  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22) !important;\n}\n\n.elevation-5 {\n  box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22) !important;\n}\n\n.bg-primary {\n  background-color: #007bff !important;\n}\n\n.bg-primary,\n.bg-primary > a {\n  color: #fff !important;\n}\n\n.bg-primary.btn:hover {\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-primary.btn:not(:disabled):not(.disabled):active, .bg-primary.btn:not(:disabled):not(.disabled).active, .bg-primary.btn:active, .bg-primary.btn.active {\n  background-color: #0062cc !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-secondary {\n  background-color: #6c757d !important;\n}\n\n.bg-secondary,\n.bg-secondary > a {\n  color: #fff !important;\n}\n\n.bg-secondary.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-secondary.btn:not(:disabled):not(.disabled):active, .bg-secondary.btn:not(:disabled):not(.disabled).active, .bg-secondary.btn:active, .bg-secondary.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-success {\n  background-color: #28a745 !important;\n}\n\n.bg-success,\n.bg-success > a {\n  color: #fff !important;\n}\n\n.bg-success.btn:hover {\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-success.btn:not(:disabled):not(.disabled):active, .bg-success.btn:not(:disabled):not(.disabled).active, .bg-success.btn:active, .bg-success.btn.active {\n  background-color: #1e7e34 !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-info {\n  background-color: #17a2b8 !important;\n}\n\n.bg-info,\n.bg-info > a {\n  color: #fff !important;\n}\n\n.bg-info.btn:hover {\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-info.btn:not(:disabled):not(.disabled):active, .bg-info.btn:not(:disabled):not(.disabled).active, .bg-info.btn:active, .bg-info.btn.active {\n  background-color: #117a8b !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-warning {\n  background-color: #ffc107 !important;\n}\n\n.bg-warning,\n.bg-warning > a {\n  color: #1f2d3d !important;\n}\n\n.bg-warning.btn:hover {\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-warning.btn:not(:disabled):not(.disabled):active, .bg-warning.btn:not(:disabled):not(.disabled).active, .bg-warning.btn:active, .bg-warning.btn.active {\n  background-color: #d39e00 !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-danger {\n  background-color: #dc3545 !important;\n}\n\n.bg-danger,\n.bg-danger > a {\n  color: #fff !important;\n}\n\n.bg-danger.btn:hover {\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-danger.btn:not(:disabled):not(.disabled):active, .bg-danger.btn:not(:disabled):not(.disabled).active, .bg-danger.btn:active, .bg-danger.btn.active {\n  background-color: #bd2130 !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-light {\n  background-color: #f8f9fa !important;\n}\n\n.bg-light,\n.bg-light > a {\n  color: #1f2d3d !important;\n}\n\n.bg-light.btn:hover {\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.bg-light.btn:not(:disabled):not(.disabled):active, .bg-light.btn:not(:disabled):not(.disabled).active, .bg-light.btn:active, .bg-light.btn.active {\n  background-color: #dae0e5 !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.bg-dark {\n  background-color: #343a40 !important;\n}\n\n.bg-dark,\n.bg-dark > a {\n  color: #fff !important;\n}\n\n.bg-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-dark.btn:not(:disabled):not(.disabled):active, .bg-dark.btn:not(:disabled):not(.disabled).active, .bg-dark.btn:active, .bg-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-lightblue {\n  background-color: #3c8dbc !important;\n}\n\n.bg-lightblue,\n.bg-lightblue > a {\n  color: #fff !important;\n}\n\n.bg-lightblue.btn:hover {\n  border-color: #307095;\n  color: #ececec;\n}\n\n.bg-lightblue.btn:not(:disabled):not(.disabled):active, .bg-lightblue.btn:not(:disabled):not(.disabled).active, .bg-lightblue.btn:active, .bg-lightblue.btn.active {\n  background-color: #307095 !important;\n  border-color: #2d698c;\n  color: #fff;\n}\n\n.bg-navy {\n  background-color: #001f3f !important;\n}\n\n.bg-navy,\n.bg-navy > a {\n  color: #fff !important;\n}\n\n.bg-navy.btn:hover {\n  border-color: #00060c;\n  color: #ececec;\n}\n\n.bg-navy.btn:not(:disabled):not(.disabled):active, .bg-navy.btn:not(:disabled):not(.disabled).active, .bg-navy.btn:active, .bg-navy.btn.active {\n  background-color: #00060c !important;\n  border-color: black;\n  color: #fff;\n}\n\n.bg-olive {\n  background-color: #3d9970 !important;\n}\n\n.bg-olive,\n.bg-olive > a {\n  color: #fff !important;\n}\n\n.bg-olive.btn:hover {\n  border-color: #2e7555;\n  color: #ececec;\n}\n\n.bg-olive.btn:not(:disabled):not(.disabled):active, .bg-olive.btn:not(:disabled):not(.disabled).active, .bg-olive.btn:active, .bg-olive.btn.active {\n  background-color: #2e7555 !important;\n  border-color: #2b6b4f;\n  color: #fff;\n}\n\n.bg-lime {\n  background-color: #01ff70 !important;\n}\n\n.bg-lime,\n.bg-lime > a {\n  color: #1f2d3d !important;\n}\n\n.bg-lime.btn:hover {\n  border-color: #00cd5a;\n  color: #121a24;\n}\n\n.bg-lime.btn:not(:disabled):not(.disabled):active, .bg-lime.btn:not(:disabled):not(.disabled).active, .bg-lime.btn:active, .bg-lime.btn.active {\n  background-color: #00cd5a !important;\n  border-color: #00c054;\n  color: #fff;\n}\n\n.bg-fuchsia {\n  background-color: #f012be !important;\n}\n\n.bg-fuchsia,\n.bg-fuchsia > a {\n  color: #fff !important;\n}\n\n.bg-fuchsia.btn:hover {\n  border-color: #c30c9a;\n  color: #ececec;\n}\n\n.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-fuchsia.btn:not(:disabled):not(.disabled).active, .bg-fuchsia.btn:active, .bg-fuchsia.btn.active {\n  background-color: #c30c9a !important;\n  border-color: #b70c90;\n  color: #fff;\n}\n\n.bg-maroon {\n  background-color: #d81b60 !important;\n}\n\n.bg-maroon,\n.bg-maroon > a {\n  color: #fff !important;\n}\n\n.bg-maroon.btn:hover {\n  border-color: #ab154c;\n  color: #ececec;\n}\n\n.bg-maroon.btn:not(:disabled):not(.disabled):active, .bg-maroon.btn:not(:disabled):not(.disabled).active, .bg-maroon.btn:active, .bg-maroon.btn.active {\n  background-color: #ab154c !important;\n  border-color: #9f1447;\n  color: #fff;\n}\n\n.bg-blue {\n  background-color: #007bff !important;\n}\n\n.bg-blue,\n.bg-blue > a {\n  color: #fff !important;\n}\n\n.bg-blue.btn:hover {\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-blue.btn:not(:disabled):not(.disabled):active, .bg-blue.btn:not(:disabled):not(.disabled).active, .bg-blue.btn:active, .bg-blue.btn.active {\n  background-color: #0062cc !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-indigo {\n  background-color: #6610f2 !important;\n}\n\n.bg-indigo,\n.bg-indigo > a {\n  color: #fff !important;\n}\n\n.bg-indigo.btn:hover {\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.bg-indigo.btn:not(:disabled):not(.disabled):active, .bg-indigo.btn:not(:disabled):not(.disabled).active, .bg-indigo.btn:active, .bg-indigo.btn.active {\n  background-color: #510bc4 !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.bg-purple {\n  background-color: #6f42c1 !important;\n}\n\n.bg-purple,\n.bg-purple > a {\n  color: #fff !important;\n}\n\n.bg-purple.btn:hover {\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.bg-purple.btn:not(:disabled):not(.disabled):active, .bg-purple.btn:not(:disabled):not(.disabled).active, .bg-purple.btn:active, .bg-purple.btn.active {\n  background-color: #59339d !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.bg-pink {\n  background-color: #e83e8c !important;\n}\n\n.bg-pink,\n.bg-pink > a {\n  color: #fff !important;\n}\n\n.bg-pink.btn:hover {\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.bg-pink.btn:not(:disabled):not(.disabled):active, .bg-pink.btn:not(:disabled):not(.disabled).active, .bg-pink.btn:active, .bg-pink.btn.active {\n  background-color: #d91a72 !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.bg-red {\n  background-color: #dc3545 !important;\n}\n\n.bg-red,\n.bg-red > a {\n  color: #fff !important;\n}\n\n.bg-red.btn:hover {\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-red.btn:not(:disabled):not(.disabled):active, .bg-red.btn:not(:disabled):not(.disabled).active, .bg-red.btn:active, .bg-red.btn.active {\n  background-color: #bd2130 !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-orange {\n  background-color: #fd7e14 !important;\n}\n\n.bg-orange,\n.bg-orange > a {\n  color: #1f2d3d !important;\n}\n\n.bg-orange.btn:hover {\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.bg-orange.btn:not(:disabled):not(.disabled):active, .bg-orange.btn:not(:disabled):not(.disabled).active, .bg-orange.btn:active, .bg-orange.btn.active {\n  background-color: #dc6502 !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.bg-yellow {\n  background-color: #ffc107 !important;\n}\n\n.bg-yellow,\n.bg-yellow > a {\n  color: #1f2d3d !important;\n}\n\n.bg-yellow.btn:hover {\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-yellow.btn:not(:disabled):not(.disabled):active, .bg-yellow.btn:not(:disabled):not(.disabled).active, .bg-yellow.btn:active, .bg-yellow.btn.active {\n  background-color: #d39e00 !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-green {\n  background-color: #28a745 !important;\n}\n\n.bg-green,\n.bg-green > a {\n  color: #fff !important;\n}\n\n.bg-green.btn:hover {\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-green.btn:not(:disabled):not(.disabled):active, .bg-green.btn:not(:disabled):not(.disabled).active, .bg-green.btn:active, .bg-green.btn.active {\n  background-color: #1e7e34 !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-teal {\n  background-color: #20c997 !important;\n}\n\n.bg-teal,\n.bg-teal > a {\n  color: #fff !important;\n}\n\n.bg-teal.btn:hover {\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.bg-teal.btn:not(:disabled):not(.disabled):active, .bg-teal.btn:not(:disabled):not(.disabled).active, .bg-teal.btn:active, .bg-teal.btn.active {\n  background-color: #199d76 !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.bg-cyan {\n  background-color: #17a2b8 !important;\n}\n\n.bg-cyan,\n.bg-cyan > a {\n  color: #fff !important;\n}\n\n.bg-cyan.btn:hover {\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-cyan.btn:not(:disabled):not(.disabled):active, .bg-cyan.btn:not(:disabled):not(.disabled).active, .bg-cyan.btn:active, .bg-cyan.btn.active {\n  background-color: #117a8b !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-white {\n  background-color: #fff !important;\n}\n\n.bg-white,\n.bg-white > a {\n  color: #1f2d3d !important;\n}\n\n.bg-white.btn:hover {\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.bg-white.btn:not(:disabled):not(.disabled):active, .bg-white.btn:not(:disabled):not(.disabled).active, .bg-white.btn:active, .bg-white.btn.active {\n  background-color: #e6e6e6 !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.bg-gray {\n  background-color: #6c757d !important;\n}\n\n.bg-gray,\n.bg-gray > a {\n  color: #fff !important;\n}\n\n.bg-gray.btn:hover {\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gray.btn:not(:disabled):not(.disabled):active, .bg-gray.btn:not(:disabled):not(.disabled).active, .bg-gray.btn:active, .bg-gray.btn.active {\n  background-color: #545b62 !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gray-dark {\n  background-color: #343a40 !important;\n}\n\n.bg-gray-dark,\n.bg-gray-dark > a {\n  color: #fff !important;\n}\n\n.bg-gray-dark.btn:hover {\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gray-dark.btn:not(:disabled):not(.disabled).active, .bg-gray-dark.btn:active, .bg-gray-dark.btn.active {\n  background-color: #1d2124 !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n@media print {\n  .table td.bg-primary,\n  .table th.bg-primary {\n    background-color: #007bff !important;\n  }\n  .table td.bg-primary,\n  .table td.bg-primary > a,\n  .table th.bg-primary,\n  .table th.bg-primary > a {\n    color: #fff !important;\n  }\n  .table td.bg-primary.btn:hover,\n  .table th.bg-primary.btn:hover {\n    border-color: #0062cc;\n    color: #ececec;\n  }\n  .table td.bg-primary.btn:not(:disabled):not(.disabled):active, .table td.bg-primary.btn:not(:disabled):not(.disabled).active, .table td.bg-primary.btn:active, .table td.bg-primary.btn.active,\n  .table th.bg-primary.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-primary.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-primary.btn:active,\n  .table th.bg-primary.btn.active {\n    background-color: #0062cc !important;\n    border-color: #005cbf;\n    color: #fff;\n  }\n  .table td.bg-secondary,\n  .table th.bg-secondary {\n    background-color: #6c757d !important;\n  }\n  .table td.bg-secondary,\n  .table td.bg-secondary > a,\n  .table th.bg-secondary,\n  .table th.bg-secondary > a {\n    color: #fff !important;\n  }\n  .table td.bg-secondary.btn:hover,\n  .table th.bg-secondary.btn:hover {\n    border-color: #545b62;\n    color: #ececec;\n  }\n  .table td.bg-secondary.btn:not(:disabled):not(.disabled):active, .table td.bg-secondary.btn:not(:disabled):not(.disabled).active, .table td.bg-secondary.btn:active, .table td.bg-secondary.btn.active,\n  .table th.bg-secondary.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-secondary.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-secondary.btn:active,\n  .table th.bg-secondary.btn.active {\n    background-color: #545b62 !important;\n    border-color: #4e555b;\n    color: #fff;\n  }\n  .table td.bg-success,\n  .table th.bg-success {\n    background-color: #28a745 !important;\n  }\n  .table td.bg-success,\n  .table td.bg-success > a,\n  .table th.bg-success,\n  .table th.bg-success > a {\n    color: #fff !important;\n  }\n  .table td.bg-success.btn:hover,\n  .table th.bg-success.btn:hover {\n    border-color: #1e7e34;\n    color: #ececec;\n  }\n  .table td.bg-success.btn:not(:disabled):not(.disabled):active, .table td.bg-success.btn:not(:disabled):not(.disabled).active, .table td.bg-success.btn:active, .table td.bg-success.btn.active,\n  .table th.bg-success.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-success.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-success.btn:active,\n  .table th.bg-success.btn.active {\n    background-color: #1e7e34 !important;\n    border-color: #1c7430;\n    color: #fff;\n  }\n  .table td.bg-info,\n  .table th.bg-info {\n    background-color: #17a2b8 !important;\n  }\n  .table td.bg-info,\n  .table td.bg-info > a,\n  .table th.bg-info,\n  .table th.bg-info > a {\n    color: #fff !important;\n  }\n  .table td.bg-info.btn:hover,\n  .table th.bg-info.btn:hover {\n    border-color: #117a8b;\n    color: #ececec;\n  }\n  .table td.bg-info.btn:not(:disabled):not(.disabled):active, .table td.bg-info.btn:not(:disabled):not(.disabled).active, .table td.bg-info.btn:active, .table td.bg-info.btn.active,\n  .table th.bg-info.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-info.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-info.btn:active,\n  .table th.bg-info.btn.active {\n    background-color: #117a8b !important;\n    border-color: #10707f;\n    color: #fff;\n  }\n  .table td.bg-warning,\n  .table th.bg-warning {\n    background-color: #ffc107 !important;\n  }\n  .table td.bg-warning,\n  .table td.bg-warning > a,\n  .table th.bg-warning,\n  .table th.bg-warning > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-warning.btn:hover,\n  .table th.bg-warning.btn:hover {\n    border-color: #d39e00;\n    color: #121a24;\n  }\n  .table td.bg-warning.btn:not(:disabled):not(.disabled):active, .table td.bg-warning.btn:not(:disabled):not(.disabled).active, .table td.bg-warning.btn:active, .table td.bg-warning.btn.active,\n  .table th.bg-warning.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-warning.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-warning.btn:active,\n  .table th.bg-warning.btn.active {\n    background-color: #d39e00 !important;\n    border-color: #c69500;\n    color: #1f2d3d;\n  }\n  .table td.bg-danger,\n  .table th.bg-danger {\n    background-color: #dc3545 !important;\n  }\n  .table td.bg-danger,\n  .table td.bg-danger > a,\n  .table th.bg-danger,\n  .table th.bg-danger > a {\n    color: #fff !important;\n  }\n  .table td.bg-danger.btn:hover,\n  .table th.bg-danger.btn:hover {\n    border-color: #bd2130;\n    color: #ececec;\n  }\n  .table td.bg-danger.btn:not(:disabled):not(.disabled):active, .table td.bg-danger.btn:not(:disabled):not(.disabled).active, .table td.bg-danger.btn:active, .table td.bg-danger.btn.active,\n  .table th.bg-danger.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-danger.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-danger.btn:active,\n  .table th.bg-danger.btn.active {\n    background-color: #bd2130 !important;\n    border-color: #b21f2d;\n    color: #fff;\n  }\n  .table td.bg-light,\n  .table th.bg-light {\n    background-color: #f8f9fa !important;\n  }\n  .table td.bg-light,\n  .table td.bg-light > a,\n  .table th.bg-light,\n  .table th.bg-light > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-light.btn:hover,\n  .table th.bg-light.btn:hover {\n    border-color: #dae0e5;\n    color: #121a24;\n  }\n  .table td.bg-light.btn:not(:disabled):not(.disabled):active, .table td.bg-light.btn:not(:disabled):not(.disabled).active, .table td.bg-light.btn:active, .table td.bg-light.btn.active,\n  .table th.bg-light.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-light.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-light.btn:active,\n  .table th.bg-light.btn.active {\n    background-color: #dae0e5 !important;\n    border-color: #d3d9df;\n    color: #1f2d3d;\n  }\n  .table td.bg-dark,\n  .table th.bg-dark {\n    background-color: #343a40 !important;\n  }\n  .table td.bg-dark,\n  .table td.bg-dark > a,\n  .table th.bg-dark,\n  .table th.bg-dark > a {\n    color: #fff !important;\n  }\n  .table td.bg-dark.btn:hover,\n  .table th.bg-dark.btn:hover {\n    border-color: #1d2124;\n    color: #ececec;\n  }\n  .table td.bg-dark.btn:not(:disabled):not(.disabled):active, .table td.bg-dark.btn:not(:disabled):not(.disabled).active, .table td.bg-dark.btn:active, .table td.bg-dark.btn.active,\n  .table th.bg-dark.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-dark.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-dark.btn:active,\n  .table th.bg-dark.btn.active {\n    background-color: #1d2124 !important;\n    border-color: #171a1d;\n    color: #fff;\n  }\n  .table td.bg-lightblue,\n  .table th.bg-lightblue {\n    background-color: #3c8dbc !important;\n  }\n  .table td.bg-lightblue,\n  .table td.bg-lightblue > a,\n  .table th.bg-lightblue,\n  .table th.bg-lightblue > a {\n    color: #fff !important;\n  }\n  .table td.bg-lightblue.btn:hover,\n  .table th.bg-lightblue.btn:hover {\n    border-color: #307095;\n    color: #ececec;\n  }\n  .table td.bg-lightblue.btn:not(:disabled):not(.disabled):active, .table td.bg-lightblue.btn:not(:disabled):not(.disabled).active, .table td.bg-lightblue.btn:active, .table td.bg-lightblue.btn.active,\n  .table th.bg-lightblue.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-lightblue.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-lightblue.btn:active,\n  .table th.bg-lightblue.btn.active {\n    background-color: #307095 !important;\n    border-color: #2d698c;\n    color: #fff;\n  }\n  .table td.bg-navy,\n  .table th.bg-navy {\n    background-color: #001f3f !important;\n  }\n  .table td.bg-navy,\n  .table td.bg-navy > a,\n  .table th.bg-navy,\n  .table th.bg-navy > a {\n    color: #fff !important;\n  }\n  .table td.bg-navy.btn:hover,\n  .table th.bg-navy.btn:hover {\n    border-color: #00060c;\n    color: #ececec;\n  }\n  .table td.bg-navy.btn:not(:disabled):not(.disabled):active, .table td.bg-navy.btn:not(:disabled):not(.disabled).active, .table td.bg-navy.btn:active, .table td.bg-navy.btn.active,\n  .table th.bg-navy.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-navy.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-navy.btn:active,\n  .table th.bg-navy.btn.active {\n    background-color: #00060c !important;\n    border-color: black;\n    color: #fff;\n  }\n  .table td.bg-olive,\n  .table th.bg-olive {\n    background-color: #3d9970 !important;\n  }\n  .table td.bg-olive,\n  .table td.bg-olive > a,\n  .table th.bg-olive,\n  .table th.bg-olive > a {\n    color: #fff !important;\n  }\n  .table td.bg-olive.btn:hover,\n  .table th.bg-olive.btn:hover {\n    border-color: #2e7555;\n    color: #ececec;\n  }\n  .table td.bg-olive.btn:not(:disabled):not(.disabled):active, .table td.bg-olive.btn:not(:disabled):not(.disabled).active, .table td.bg-olive.btn:active, .table td.bg-olive.btn.active,\n  .table th.bg-olive.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-olive.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-olive.btn:active,\n  .table th.bg-olive.btn.active {\n    background-color: #2e7555 !important;\n    border-color: #2b6b4f;\n    color: #fff;\n  }\n  .table td.bg-lime,\n  .table th.bg-lime {\n    background-color: #01ff70 !important;\n  }\n  .table td.bg-lime,\n  .table td.bg-lime > a,\n  .table th.bg-lime,\n  .table th.bg-lime > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-lime.btn:hover,\n  .table th.bg-lime.btn:hover {\n    border-color: #00cd5a;\n    color: #121a24;\n  }\n  .table td.bg-lime.btn:not(:disabled):not(.disabled):active, .table td.bg-lime.btn:not(:disabled):not(.disabled).active, .table td.bg-lime.btn:active, .table td.bg-lime.btn.active,\n  .table th.bg-lime.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-lime.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-lime.btn:active,\n  .table th.bg-lime.btn.active {\n    background-color: #00cd5a !important;\n    border-color: #00c054;\n    color: #fff;\n  }\n  .table td.bg-fuchsia,\n  .table th.bg-fuchsia {\n    background-color: #f012be !important;\n  }\n  .table td.bg-fuchsia,\n  .table td.bg-fuchsia > a,\n  .table th.bg-fuchsia,\n  .table th.bg-fuchsia > a {\n    color: #fff !important;\n  }\n  .table td.bg-fuchsia.btn:hover,\n  .table th.bg-fuchsia.btn:hover {\n    border-color: #c30c9a;\n    color: #ececec;\n  }\n  .table td.bg-fuchsia.btn:not(:disabled):not(.disabled):active, .table td.bg-fuchsia.btn:not(:disabled):not(.disabled).active, .table td.bg-fuchsia.btn:active, .table td.bg-fuchsia.btn.active,\n  .table th.bg-fuchsia.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-fuchsia.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-fuchsia.btn:active,\n  .table th.bg-fuchsia.btn.active {\n    background-color: #c30c9a !important;\n    border-color: #b70c90;\n    color: #fff;\n  }\n  .table td.bg-maroon,\n  .table th.bg-maroon {\n    background-color: #d81b60 !important;\n  }\n  .table td.bg-maroon,\n  .table td.bg-maroon > a,\n  .table th.bg-maroon,\n  .table th.bg-maroon > a {\n    color: #fff !important;\n  }\n  .table td.bg-maroon.btn:hover,\n  .table th.bg-maroon.btn:hover {\n    border-color: #ab154c;\n    color: #ececec;\n  }\n  .table td.bg-maroon.btn:not(:disabled):not(.disabled):active, .table td.bg-maroon.btn:not(:disabled):not(.disabled).active, .table td.bg-maroon.btn:active, .table td.bg-maroon.btn.active,\n  .table th.bg-maroon.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-maroon.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-maroon.btn:active,\n  .table th.bg-maroon.btn.active {\n    background-color: #ab154c !important;\n    border-color: #9f1447;\n    color: #fff;\n  }\n  .table td.bg-blue,\n  .table th.bg-blue {\n    background-color: #007bff !important;\n  }\n  .table td.bg-blue,\n  .table td.bg-blue > a,\n  .table th.bg-blue,\n  .table th.bg-blue > a {\n    color: #fff !important;\n  }\n  .table td.bg-blue.btn:hover,\n  .table th.bg-blue.btn:hover {\n    border-color: #0062cc;\n    color: #ececec;\n  }\n  .table td.bg-blue.btn:not(:disabled):not(.disabled):active, .table td.bg-blue.btn:not(:disabled):not(.disabled).active, .table td.bg-blue.btn:active, .table td.bg-blue.btn.active,\n  .table th.bg-blue.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-blue.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-blue.btn:active,\n  .table th.bg-blue.btn.active {\n    background-color: #0062cc !important;\n    border-color: #005cbf;\n    color: #fff;\n  }\n  .table td.bg-indigo,\n  .table th.bg-indigo {\n    background-color: #6610f2 !important;\n  }\n  .table td.bg-indigo,\n  .table td.bg-indigo > a,\n  .table th.bg-indigo,\n  .table th.bg-indigo > a {\n    color: #fff !important;\n  }\n  .table td.bg-indigo.btn:hover,\n  .table th.bg-indigo.btn:hover {\n    border-color: #510bc4;\n    color: #ececec;\n  }\n  .table td.bg-indigo.btn:not(:disabled):not(.disabled):active, .table td.bg-indigo.btn:not(:disabled):not(.disabled).active, .table td.bg-indigo.btn:active, .table td.bg-indigo.btn.active,\n  .table th.bg-indigo.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-indigo.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-indigo.btn:active,\n  .table th.bg-indigo.btn.active {\n    background-color: #510bc4 !important;\n    border-color: #4c0ab8;\n    color: #fff;\n  }\n  .table td.bg-purple,\n  .table th.bg-purple {\n    background-color: #6f42c1 !important;\n  }\n  .table td.bg-purple,\n  .table td.bg-purple > a,\n  .table th.bg-purple,\n  .table th.bg-purple > a {\n    color: #fff !important;\n  }\n  .table td.bg-purple.btn:hover,\n  .table th.bg-purple.btn:hover {\n    border-color: #59339d;\n    color: #ececec;\n  }\n  .table td.bg-purple.btn:not(:disabled):not(.disabled):active, .table td.bg-purple.btn:not(:disabled):not(.disabled).active, .table td.bg-purple.btn:active, .table td.bg-purple.btn.active,\n  .table th.bg-purple.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-purple.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-purple.btn:active,\n  .table th.bg-purple.btn.active {\n    background-color: #59339d !important;\n    border-color: #533093;\n    color: #fff;\n  }\n  .table td.bg-pink,\n  .table th.bg-pink {\n    background-color: #e83e8c !important;\n  }\n  .table td.bg-pink,\n  .table td.bg-pink > a,\n  .table th.bg-pink,\n  .table th.bg-pink > a {\n    color: #fff !important;\n  }\n  .table td.bg-pink.btn:hover,\n  .table th.bg-pink.btn:hover {\n    border-color: #d91a72;\n    color: #ececec;\n  }\n  .table td.bg-pink.btn:not(:disabled):not(.disabled):active, .table td.bg-pink.btn:not(:disabled):not(.disabled).active, .table td.bg-pink.btn:active, .table td.bg-pink.btn.active,\n  .table th.bg-pink.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-pink.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-pink.btn:active,\n  .table th.bg-pink.btn.active {\n    background-color: #d91a72 !important;\n    border-color: #ce196c;\n    color: #fff;\n  }\n  .table td.bg-red,\n  .table th.bg-red {\n    background-color: #dc3545 !important;\n  }\n  .table td.bg-red,\n  .table td.bg-red > a,\n  .table th.bg-red,\n  .table th.bg-red > a {\n    color: #fff !important;\n  }\n  .table td.bg-red.btn:hover,\n  .table th.bg-red.btn:hover {\n    border-color: #bd2130;\n    color: #ececec;\n  }\n  .table td.bg-red.btn:not(:disabled):not(.disabled):active, .table td.bg-red.btn:not(:disabled):not(.disabled).active, .table td.bg-red.btn:active, .table td.bg-red.btn.active,\n  .table th.bg-red.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-red.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-red.btn:active,\n  .table th.bg-red.btn.active {\n    background-color: #bd2130 !important;\n    border-color: #b21f2d;\n    color: #fff;\n  }\n  .table td.bg-orange,\n  .table th.bg-orange {\n    background-color: #fd7e14 !important;\n  }\n  .table td.bg-orange,\n  .table td.bg-orange > a,\n  .table th.bg-orange,\n  .table th.bg-orange > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-orange.btn:hover,\n  .table th.bg-orange.btn:hover {\n    border-color: #dc6502;\n    color: #121a24;\n  }\n  .table td.bg-orange.btn:not(:disabled):not(.disabled):active, .table td.bg-orange.btn:not(:disabled):not(.disabled).active, .table td.bg-orange.btn:active, .table td.bg-orange.btn.active,\n  .table th.bg-orange.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-orange.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-orange.btn:active,\n  .table th.bg-orange.btn.active {\n    background-color: #dc6502 !important;\n    border-color: #cf5f02;\n    color: #fff;\n  }\n  .table td.bg-yellow,\n  .table th.bg-yellow {\n    background-color: #ffc107 !important;\n  }\n  .table td.bg-yellow,\n  .table td.bg-yellow > a,\n  .table th.bg-yellow,\n  .table th.bg-yellow > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-yellow.btn:hover,\n  .table th.bg-yellow.btn:hover {\n    border-color: #d39e00;\n    color: #121a24;\n  }\n  .table td.bg-yellow.btn:not(:disabled):not(.disabled):active, .table td.bg-yellow.btn:not(:disabled):not(.disabled).active, .table td.bg-yellow.btn:active, .table td.bg-yellow.btn.active,\n  .table th.bg-yellow.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-yellow.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-yellow.btn:active,\n  .table th.bg-yellow.btn.active {\n    background-color: #d39e00 !important;\n    border-color: #c69500;\n    color: #1f2d3d;\n  }\n  .table td.bg-green,\n  .table th.bg-green {\n    background-color: #28a745 !important;\n  }\n  .table td.bg-green,\n  .table td.bg-green > a,\n  .table th.bg-green,\n  .table th.bg-green > a {\n    color: #fff !important;\n  }\n  .table td.bg-green.btn:hover,\n  .table th.bg-green.btn:hover {\n    border-color: #1e7e34;\n    color: #ececec;\n  }\n  .table td.bg-green.btn:not(:disabled):not(.disabled):active, .table td.bg-green.btn:not(:disabled):not(.disabled).active, .table td.bg-green.btn:active, .table td.bg-green.btn.active,\n  .table th.bg-green.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-green.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-green.btn:active,\n  .table th.bg-green.btn.active {\n    background-color: #1e7e34 !important;\n    border-color: #1c7430;\n    color: #fff;\n  }\n  .table td.bg-teal,\n  .table th.bg-teal {\n    background-color: #20c997 !important;\n  }\n  .table td.bg-teal,\n  .table td.bg-teal > a,\n  .table th.bg-teal,\n  .table th.bg-teal > a {\n    color: #fff !important;\n  }\n  .table td.bg-teal.btn:hover,\n  .table th.bg-teal.btn:hover {\n    border-color: #199d76;\n    color: #ececec;\n  }\n  .table td.bg-teal.btn:not(:disabled):not(.disabled):active, .table td.bg-teal.btn:not(:disabled):not(.disabled).active, .table td.bg-teal.btn:active, .table td.bg-teal.btn.active,\n  .table th.bg-teal.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-teal.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-teal.btn:active,\n  .table th.bg-teal.btn.active {\n    background-color: #199d76 !important;\n    border-color: #17926e;\n    color: #fff;\n  }\n  .table td.bg-cyan,\n  .table th.bg-cyan {\n    background-color: #17a2b8 !important;\n  }\n  .table td.bg-cyan,\n  .table td.bg-cyan > a,\n  .table th.bg-cyan,\n  .table th.bg-cyan > a {\n    color: #fff !important;\n  }\n  .table td.bg-cyan.btn:hover,\n  .table th.bg-cyan.btn:hover {\n    border-color: #117a8b;\n    color: #ececec;\n  }\n  .table td.bg-cyan.btn:not(:disabled):not(.disabled):active, .table td.bg-cyan.btn:not(:disabled):not(.disabled).active, .table td.bg-cyan.btn:active, .table td.bg-cyan.btn.active,\n  .table th.bg-cyan.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-cyan.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-cyan.btn:active,\n  .table th.bg-cyan.btn.active {\n    background-color: #117a8b !important;\n    border-color: #10707f;\n    color: #fff;\n  }\n  .table td.bg-white,\n  .table th.bg-white {\n    background-color: #fff !important;\n  }\n  .table td.bg-white,\n  .table td.bg-white > a,\n  .table th.bg-white,\n  .table th.bg-white > a {\n    color: #1f2d3d !important;\n  }\n  .table td.bg-white.btn:hover,\n  .table th.bg-white.btn:hover {\n    border-color: #e6e6e6;\n    color: #121a24;\n  }\n  .table td.bg-white.btn:not(:disabled):not(.disabled):active, .table td.bg-white.btn:not(:disabled):not(.disabled).active, .table td.bg-white.btn:active, .table td.bg-white.btn.active,\n  .table th.bg-white.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-white.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-white.btn:active,\n  .table th.bg-white.btn.active {\n    background-color: #e6e6e6 !important;\n    border-color: #dfdfdf;\n    color: #1f2d3d;\n  }\n  .table td.bg-gray,\n  .table th.bg-gray {\n    background-color: #6c757d !important;\n  }\n  .table td.bg-gray,\n  .table td.bg-gray > a,\n  .table th.bg-gray,\n  .table th.bg-gray > a {\n    color: #fff !important;\n  }\n  .table td.bg-gray.btn:hover,\n  .table th.bg-gray.btn:hover {\n    border-color: #545b62;\n    color: #ececec;\n  }\n  .table td.bg-gray.btn:not(:disabled):not(.disabled):active, .table td.bg-gray.btn:not(:disabled):not(.disabled).active, .table td.bg-gray.btn:active, .table td.bg-gray.btn.active,\n  .table th.bg-gray.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-gray.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-gray.btn:active,\n  .table th.bg-gray.btn.active {\n    background-color: #545b62 !important;\n    border-color: #4e555b;\n    color: #fff;\n  }\n  .table td.bg-gray-dark,\n  .table th.bg-gray-dark {\n    background-color: #343a40 !important;\n  }\n  .table td.bg-gray-dark,\n  .table td.bg-gray-dark > a,\n  .table th.bg-gray-dark,\n  .table th.bg-gray-dark > a {\n    color: #fff !important;\n  }\n  .table td.bg-gray-dark.btn:hover,\n  .table th.bg-gray-dark.btn:hover {\n    border-color: #1d2124;\n    color: #ececec;\n  }\n  .table td.bg-gray-dark.btn:not(:disabled):not(.disabled):active, .table td.bg-gray-dark.btn:not(:disabled):not(.disabled).active, .table td.bg-gray-dark.btn:active, .table td.bg-gray-dark.btn.active,\n  .table th.bg-gray-dark.btn:not(:disabled):not(.disabled):active,\n  .table th.bg-gray-dark.btn:not(:disabled):not(.disabled).active,\n  .table th.bg-gray-dark.btn:active,\n  .table th.bg-gray-dark.btn.active {\n    background-color: #1d2124 !important;\n    border-color: #171a1d;\n    color: #fff;\n  }\n}\n\n.bg-gray {\n  background-color: #adb5bd;\n  color: #1f2d3d;\n}\n\n.bg-gray-light {\n  background-color: #f2f4f5;\n  color: #1f2d3d !important;\n}\n\n.bg-black {\n  background-color: #000;\n  color: #fff !important;\n}\n\n.bg-white {\n  background-color: #fff;\n  color: #1f2d3d !important;\n}\n\n.bg-gradient-primary {\n  background: #007bff linear-gradient(180deg, #268fff, #007bff) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .bg-gradient-primary.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-primary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-primary.btn:hover {\n  background: #007bff linear-gradient(180deg, #267fde, #0069d9) repeat-x !important;\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-gradient-primary.btn:not(:disabled):not(.disabled):active, .bg-gradient-primary.btn:not(:disabled):not(.disabled).active, .bg-gradient-primary.btn:active, .bg-gradient-primary.btn.active {\n  background: #007bff linear-gradient(180deg, #267ad4, #0062cc) repeat-x !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-gradient-primary.btn:disabled, .bg-gradient-primary.btn.disabled {\n  background-image: none !important;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.bg-gradient-secondary {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-secondary.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-secondary.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gradient-secondary.btn:not(:disabled):not(.disabled):active, .bg-gradient-secondary.btn:not(:disabled):not(.disabled).active, .bg-gradient-secondary.btn:active, .bg-gradient-secondary.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gradient-secondary.btn:disabled, .bg-gradient-secondary.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.bg-gradient-success {\n  background: #28a745 linear-gradient(180deg, #48b461, #28a745) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-success.btn:not(:disabled):not(.disabled):active, .bg-gradient-success.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-success.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-success.btn:hover {\n  background: #28a745 linear-gradient(180deg, #429a56, #218838) repeat-x !important;\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-gradient-success.btn:not(:disabled):not(.disabled):active, .bg-gradient-success.btn:not(:disabled):not(.disabled).active, .bg-gradient-success.btn:active, .bg-gradient-success.btn.active {\n  background: #28a745 linear-gradient(180deg, #409152, #1e7e34) repeat-x !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-gradient-success.btn:disabled, .bg-gradient-success.btn.disabled {\n  background-image: none !important;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.bg-gradient-info {\n  background: #17a2b8 linear-gradient(180deg, #3ab0c3, #17a2b8) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-info.btn:not(:disabled):not(.disabled):active, .bg-gradient-info.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-info.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-info.btn:hover {\n  background: #17a2b8 linear-gradient(180deg, #3697a6, #138496) repeat-x !important;\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-gradient-info.btn:not(:disabled):not(.disabled):active, .bg-gradient-info.btn:not(:disabled):not(.disabled).active, .bg-gradient-info.btn:active, .bg-gradient-info.btn.active {\n  background: #17a2b8 linear-gradient(180deg, #358e9c, #117a8b) repeat-x !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-gradient-info.btn:disabled, .bg-gradient-info.btn.disabled {\n  background-image: none !important;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.bg-gradient-warning {\n  background: #ffc107 linear-gradient(180deg, #ffca2c, #ffc107) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .bg-gradient-warning.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-warning.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-warning.btn:hover {\n  background: #ffc107 linear-gradient(180deg, #e4b526, #e0a800) repeat-x !important;\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-gradient-warning.btn:not(:disabled):not(.disabled):active, .bg-gradient-warning.btn:not(:disabled):not(.disabled).active, .bg-gradient-warning.btn:active, .bg-gradient-warning.btn.active {\n  background: #ffc107 linear-gradient(180deg, #daad26, #d39e00) repeat-x !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-gradient-warning.btn:disabled, .bg-gradient-warning.btn.disabled {\n  background-image: none !important;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.bg-gradient-danger {\n  background: #dc3545 linear-gradient(180deg, #e15361, #dc3545) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .bg-gradient-danger.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-danger.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-danger.btn:hover {\n  background: #dc3545 linear-gradient(180deg, #d04451, #c82333) repeat-x !important;\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-gradient-danger.btn:not(:disabled):not(.disabled):active, .bg-gradient-danger.btn:not(:disabled):not(.disabled).active, .bg-gradient-danger.btn:active, .bg-gradient-danger.btn.active {\n  background: #dc3545 linear-gradient(180deg, #c7424f, #bd2130) repeat-x !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-gradient-danger.btn:disabled, .bg-gradient-danger.btn.disabled {\n  background-image: none !important;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.bg-gradient-light {\n  background: #f8f9fa linear-gradient(180deg, #f9fafb, #f8f9fa) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-light.btn:not(:disabled):not(.disabled):active, .bg-gradient-light.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-light.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-light.btn:hover {\n  background: #f8f9fa linear-gradient(180deg, #e6eaed, #e2e6ea) repeat-x !important;\n  border-color: #dae0e5;\n  color: #121a24;\n}\n\n.bg-gradient-light.btn:not(:disabled):not(.disabled):active, .bg-gradient-light.btn:not(:disabled):not(.disabled).active, .bg-gradient-light.btn:active, .bg-gradient-light.btn.active {\n  background: #f8f9fa linear-gradient(180deg, #e0e4e9, #dae0e5) repeat-x !important;\n  border-color: #d3d9df;\n  color: #1f2d3d;\n}\n\n.bg-gradient-light.btn:disabled, .bg-gradient-light.btn.disabled {\n  background-image: none !important;\n  border-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.bg-gradient-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-dark.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gradient-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-dark.btn:not(:disabled):not(.disabled).active, .bg-gradient-dark.btn:active, .bg-gradient-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-gradient-dark.btn:disabled, .bg-gradient-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.bg-gradient-lightblue {\n  background: #3c8dbc linear-gradient(180deg, #599ec6, #3c8dbc) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-lightblue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-lightblue.btn:hover {\n  background: #3c8dbc linear-gradient(180deg, #518cad, #33779f) repeat-x !important;\n  border-color: #307095;\n  color: #ececec;\n}\n\n.bg-gradient-lightblue.btn:not(:disabled):not(.disabled):active, .bg-gradient-lightblue.btn:not(:disabled):not(.disabled).active, .bg-gradient-lightblue.btn:active, .bg-gradient-lightblue.btn.active {\n  background: #3c8dbc linear-gradient(180deg, #4f85a5, #307095) repeat-x !important;\n  border-color: #2d698c;\n  color: #fff;\n}\n\n.bg-gradient-lightblue.btn:disabled, .bg-gradient-lightblue.btn.disabled {\n  background-image: none !important;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.bg-gradient-navy {\n  background: #001f3f linear-gradient(180deg, #26415c, #001f3f) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .bg-gradient-navy.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-navy.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-navy.btn:hover {\n  background: #001f3f linear-gradient(180deg, #26313b, #000c19) repeat-x !important;\n  border-color: #00060c;\n  color: #ececec;\n}\n\n.bg-gradient-navy.btn:not(:disabled):not(.disabled):active, .bg-gradient-navy.btn:not(:disabled):not(.disabled).active, .bg-gradient-navy.btn:active, .bg-gradient-navy.btn.active {\n  background: #001f3f linear-gradient(180deg, #262b30, #00060c) repeat-x !important;\n  border-color: black;\n  color: #fff;\n}\n\n.bg-gradient-navy.btn:disabled, .bg-gradient-navy.btn.disabled {\n  background-image: none !important;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.bg-gradient-olive {\n  background: #3d9970 linear-gradient(180deg, #5aa885, #3d9970) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .bg-gradient-olive.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-olive.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-olive.btn:hover {\n  background: #3d9970 linear-gradient(180deg, #519174, #327e5c) repeat-x !important;\n  border-color: #2e7555;\n  color: #ececec;\n}\n\n.bg-gradient-olive.btn:not(:disabled):not(.disabled):active, .bg-gradient-olive.btn:not(:disabled):not(.disabled).active, .bg-gradient-olive.btn:active, .bg-gradient-olive.btn.active {\n  background: #3d9970 linear-gradient(180deg, #4e896f, #2e7555) repeat-x !important;\n  border-color: #2b6b4f;\n  color: #fff;\n}\n\n.bg-gradient-olive.btn:disabled, .bg-gradient-olive.btn.disabled {\n  background-image: none !important;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.bg-gradient-lime {\n  background: #01ff70 linear-gradient(180deg, #27ff85, #01ff70) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .bg-gradient-lime.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-lime.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-lime.btn:hover {\n  background: #01ff70 linear-gradient(180deg, #26df77, #00da5f) repeat-x !important;\n  border-color: #00cd5a;\n  color: #121a24;\n}\n\n.bg-gradient-lime.btn:not(:disabled):not(.disabled):active, .bg-gradient-lime.btn:not(:disabled):not(.disabled).active, .bg-gradient-lime.btn:active, .bg-gradient-lime.btn.active {\n  background: #01ff70 linear-gradient(180deg, #26d572, #00cd5a) repeat-x !important;\n  border-color: #00c054;\n  color: #fff;\n}\n\n.bg-gradient-lime.btn:disabled, .bg-gradient-lime.btn.disabled {\n  background-image: none !important;\n  border-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.bg-gradient-fuchsia {\n  background: #f012be linear-gradient(180deg, #f236c8, #f012be) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-fuchsia.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-fuchsia.btn:hover {\n  background: #f012be linear-gradient(180deg, #d631b1, #cf0da3) repeat-x !important;\n  border-color: #c30c9a;\n  color: #ececec;\n}\n\n.bg-gradient-fuchsia.btn:not(:disabled):not(.disabled):active, .bg-gradient-fuchsia.btn:not(:disabled):not(.disabled).active, .bg-gradient-fuchsia.btn:active, .bg-gradient-fuchsia.btn.active {\n  background: #f012be linear-gradient(180deg, #cc31a9, #c30c9a) repeat-x !important;\n  border-color: #b70c90;\n  color: #fff;\n}\n\n.bg-gradient-fuchsia.btn:disabled, .bg-gradient-fuchsia.btn.disabled {\n  background-image: none !important;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.bg-gradient-maroon {\n  background: #d81b60 linear-gradient(180deg, #de3d78, #d81b60) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-maroon.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-maroon.btn:hover {\n  background: #d81b60 linear-gradient(180deg, #c13a6b, #b61751) repeat-x !important;\n  border-color: #ab154c;\n  color: #ececec;\n}\n\n.bg-gradient-maroon.btn:not(:disabled):not(.disabled):active, .bg-gradient-maroon.btn:not(:disabled):not(.disabled).active, .bg-gradient-maroon.btn:active, .bg-gradient-maroon.btn.active {\n  background: #d81b60 linear-gradient(180deg, #b73867, #ab154c) repeat-x !important;\n  border-color: #9f1447;\n  color: #fff;\n}\n\n.bg-gradient-maroon.btn:disabled, .bg-gradient-maroon.btn.disabled {\n  background-image: none !important;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.bg-gradient-blue {\n  background: #007bff linear-gradient(180deg, #268fff, #007bff) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .bg-gradient-blue.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-blue.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-blue.btn:hover {\n  background: #007bff linear-gradient(180deg, #267fde, #0069d9) repeat-x !important;\n  border-color: #0062cc;\n  color: #ececec;\n}\n\n.bg-gradient-blue.btn:not(:disabled):not(.disabled):active, .bg-gradient-blue.btn:not(:disabled):not(.disabled).active, .bg-gradient-blue.btn:active, .bg-gradient-blue.btn.active {\n  background: #007bff linear-gradient(180deg, #267ad4, #0062cc) repeat-x !important;\n  border-color: #005cbf;\n  color: #fff;\n}\n\n.bg-gradient-blue.btn:disabled, .bg-gradient-blue.btn.disabled {\n  background-image: none !important;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.bg-gradient-indigo {\n  background: #6610f2 linear-gradient(180deg, #7d34f4, #6610f2) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-indigo.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-indigo.btn:hover {\n  background: #6610f2 linear-gradient(180deg, #7030d7, #560bd0) repeat-x !important;\n  border-color: #510bc4;\n  color: #ececec;\n}\n\n.bg-gradient-indigo.btn:not(:disabled):not(.disabled):active, .bg-gradient-indigo.btn:not(:disabled):not(.disabled).active, .bg-gradient-indigo.btn:active, .bg-gradient-indigo.btn.active {\n  background: #6610f2 linear-gradient(180deg, #6b2fcd, #510bc4) repeat-x !important;\n  border-color: #4c0ab8;\n  color: #fff;\n}\n\n.bg-gradient-indigo.btn:disabled, .bg-gradient-indigo.btn.disabled {\n  background-image: none !important;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.bg-gradient-purple {\n  background: #6f42c1 linear-gradient(180deg, #855eca, #6f42c1) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .bg-gradient-purple.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-purple.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-purple.btn:hover {\n  background: #6f42c1 linear-gradient(180deg, #7655b4, #5e37a6) repeat-x !important;\n  border-color: #59339d;\n  color: #ececec;\n}\n\n.bg-gradient-purple.btn:not(:disabled):not(.disabled):active, .bg-gradient-purple.btn:not(:disabled):not(.disabled).active, .bg-gradient-purple.btn:active, .bg-gradient-purple.btn.active {\n  background: #6f42c1 linear-gradient(180deg, #7252ab, #59339d) repeat-x !important;\n  border-color: #533093;\n  color: #fff;\n}\n\n.bg-gradient-purple.btn:disabled, .bg-gradient-purple.btn.disabled {\n  background-image: none !important;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.bg-gradient-pink {\n  background: #e83e8c linear-gradient(180deg, #eb5b9d, #e83e8c) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .bg-gradient-pink.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-pink.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-pink.btn:hover {\n  background: #e83e8c linear-gradient(180deg, #e83e8c, #e41c78) repeat-x !important;\n  border-color: #d91a72;\n  color: #ececec;\n}\n\n.bg-gradient-pink.btn:not(:disabled):not(.disabled):active, .bg-gradient-pink.btn:not(:disabled):not(.disabled).active, .bg-gradient-pink.btn:active, .bg-gradient-pink.btn.active {\n  background: #e83e8c linear-gradient(180deg, #df3c87, #d91a72) repeat-x !important;\n  border-color: #ce196c;\n  color: #fff;\n}\n\n.bg-gradient-pink.btn:disabled, .bg-gradient-pink.btn.disabled {\n  background-image: none !important;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.bg-gradient-red {\n  background: #dc3545 linear-gradient(180deg, #e15361, #dc3545) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-red.btn:not(:disabled):not(.disabled):active, .bg-gradient-red.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-red.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-red.btn:hover {\n  background: #dc3545 linear-gradient(180deg, #d04451, #c82333) repeat-x !important;\n  border-color: #bd2130;\n  color: #ececec;\n}\n\n.bg-gradient-red.btn:not(:disabled):not(.disabled):active, .bg-gradient-red.btn:not(:disabled):not(.disabled).active, .bg-gradient-red.btn:active, .bg-gradient-red.btn.active {\n  background: #dc3545 linear-gradient(180deg, #c7424f, #bd2130) repeat-x !important;\n  border-color: #b21f2d;\n  color: #fff;\n}\n\n.bg-gradient-red.btn:disabled, .bg-gradient-red.btn.disabled {\n  background-image: none !important;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.bg-gradient-orange {\n  background: #fd7e14 linear-gradient(180deg, #fd9137, #fd7e14) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .bg-gradient-orange.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-orange.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-orange.btn:hover {\n  background: #fd7e14 linear-gradient(180deg, #ec8128, #e96b02) repeat-x !important;\n  border-color: #dc6502;\n  color: #121a24;\n}\n\n.bg-gradient-orange.btn:not(:disabled):not(.disabled):active, .bg-gradient-orange.btn:not(:disabled):not(.disabled).active, .bg-gradient-orange.btn:active, .bg-gradient-orange.btn.active {\n  background: #fd7e14 linear-gradient(180deg, #e17c28, #dc6502) repeat-x !important;\n  border-color: #cf5f02;\n  color: #fff;\n}\n\n.bg-gradient-orange.btn:disabled, .bg-gradient-orange.btn.disabled {\n  background-image: none !important;\n  border-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow {\n  background: #ffc107 linear-gradient(180deg, #ffca2c, #ffc107) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-yellow.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-yellow.btn:hover {\n  background: #ffc107 linear-gradient(180deg, #e4b526, #e0a800) repeat-x !important;\n  border-color: #d39e00;\n  color: #121a24;\n}\n\n.bg-gradient-yellow.btn:not(:disabled):not(.disabled):active, .bg-gradient-yellow.btn:not(:disabled):not(.disabled).active, .bg-gradient-yellow.btn:active, .bg-gradient-yellow.btn.active {\n  background: #ffc107 linear-gradient(180deg, #daad26, #d39e00) repeat-x !important;\n  border-color: #c69500;\n  color: #1f2d3d;\n}\n\n.bg-gradient-yellow.btn:disabled, .bg-gradient-yellow.btn.disabled {\n  background-image: none !important;\n  border-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.bg-gradient-green {\n  background: #28a745 linear-gradient(180deg, #48b461, #28a745) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-green.btn:not(:disabled):not(.disabled):active, .bg-gradient-green.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-green.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-green.btn:hover {\n  background: #28a745 linear-gradient(180deg, #429a56, #218838) repeat-x !important;\n  border-color: #1e7e34;\n  color: #ececec;\n}\n\n.bg-gradient-green.btn:not(:disabled):not(.disabled):active, .bg-gradient-green.btn:not(:disabled):not(.disabled).active, .bg-gradient-green.btn:active, .bg-gradient-green.btn.active {\n  background: #28a745 linear-gradient(180deg, #409152, #1e7e34) repeat-x !important;\n  border-color: #1c7430;\n  color: #fff;\n}\n\n.bg-gradient-green.btn:disabled, .bg-gradient-green.btn.disabled {\n  background-image: none !important;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.bg-gradient-teal {\n  background: #20c997 linear-gradient(180deg, #41d1a7, #20c997) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .bg-gradient-teal.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-teal.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-teal.btn:hover {\n  background: #20c997 linear-gradient(180deg, #3db592, #1ba87e) repeat-x !important;\n  border-color: #199d76;\n  color: #ececec;\n}\n\n.bg-gradient-teal.btn:not(:disabled):not(.disabled):active, .bg-gradient-teal.btn:not(:disabled):not(.disabled).active, .bg-gradient-teal.btn:active, .bg-gradient-teal.btn.active {\n  background: #20c997 linear-gradient(180deg, #3bac8b, #199d76) repeat-x !important;\n  border-color: #17926e;\n  color: #fff;\n}\n\n.bg-gradient-teal.btn:disabled, .bg-gradient-teal.btn.disabled {\n  background-image: none !important;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.bg-gradient-cyan {\n  background: #17a2b8 linear-gradient(180deg, #3ab0c3, #17a2b8) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-cyan.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-cyan.btn:hover {\n  background: #17a2b8 linear-gradient(180deg, #3697a6, #138496) repeat-x !important;\n  border-color: #117a8b;\n  color: #ececec;\n}\n\n.bg-gradient-cyan.btn:not(:disabled):not(.disabled):active, .bg-gradient-cyan.btn:not(:disabled):not(.disabled).active, .bg-gradient-cyan.btn:active, .bg-gradient-cyan.btn.active {\n  background: #17a2b8 linear-gradient(180deg, #358e9c, #117a8b) repeat-x !important;\n  border-color: #10707f;\n  color: #fff;\n}\n\n.bg-gradient-cyan.btn:disabled, .bg-gradient-cyan.btn.disabled {\n  background-image: none !important;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.bg-gradient-white {\n  background: #fff linear-gradient(180deg, white, #fff) repeat-x !important;\n  color: #1f2d3d;\n}\n\n.bg-gradient-white.btn:not(:disabled):not(.disabled):active, .bg-gradient-white.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-white.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-white.btn:hover {\n  background: #fff linear-gradient(180deg, #efefef, #ececec) repeat-x !important;\n  border-color: #e6e6e6;\n  color: #121a24;\n}\n\n.bg-gradient-white.btn:not(:disabled):not(.disabled):active, .bg-gradient-white.btn:not(:disabled):not(.disabled).active, .bg-gradient-white.btn:active, .bg-gradient-white.btn.active {\n  background: #fff linear-gradient(180deg, #e9e9e9, #e6e6e6) repeat-x !important;\n  border-color: #dfdfdf;\n  color: #1f2d3d;\n}\n\n.bg-gradient-white.btn:disabled, .bg-gradient-white.btn.disabled {\n  background-image: none !important;\n  border-color: #fff;\n  color: #1f2d3d;\n}\n\n.bg-gradient-gray {\n  background: #6c757d linear-gradient(180deg, #828a91, #6c757d) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-gray.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-gray.btn:hover {\n  background: #6c757d linear-gradient(180deg, #73797f, #5a6268) repeat-x !important;\n  border-color: #545b62;\n  color: #ececec;\n}\n\n.bg-gradient-gray.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray.btn:not(:disabled):not(.disabled).active, .bg-gradient-gray.btn:active, .bg-gradient-gray.btn.active {\n  background: #6c757d linear-gradient(180deg, #6e7479, #545b62) repeat-x !important;\n  border-color: #4e555b;\n  color: #fff;\n}\n\n.bg-gradient-gray.btn:disabled, .bg-gradient-gray.btn.disabled {\n  background-image: none !important;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark {\n  background: #343a40 linear-gradient(180deg, #52585d, #343a40) repeat-x !important;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active,\n.show > .bg-gradient-gray-dark.btn.dropdown-toggle {\n  background-image: none !important;\n}\n\n.bg-gradient-gray-dark.btn:hover {\n  background: #343a40 linear-gradient(180deg, #44474b, #23272b) repeat-x !important;\n  border-color: #1d2124;\n  color: #ececec;\n}\n\n.bg-gradient-gray-dark.btn:not(:disabled):not(.disabled):active, .bg-gradient-gray-dark.btn:not(:disabled):not(.disabled).active, .bg-gradient-gray-dark.btn:active, .bg-gradient-gray-dark.btn.active {\n  background: #343a40 linear-gradient(180deg, #3f4245, #1d2124) repeat-x !important;\n  border-color: #171a1d;\n  color: #fff;\n}\n\n.bg-gradient-gray-dark.btn:disabled, .bg-gradient-gray-dark.btn.disabled {\n  background-image: none !important;\n  border-color: #343a40;\n  color: #fff;\n}\n\n[class^=\"bg-\"].disabled {\n  opacity: .65;\n}\n\na.text-muted:hover {\n  color: #007bff !important;\n}\n\n.link-muted {\n  color: #5d6974;\n}\n\n.link-muted:hover, .link-muted:focus {\n  color: #464f58;\n}\n\n.link-black {\n  color: #6c757d;\n}\n\n.link-black:hover, .link-black:focus {\n  color: #e6e8ea;\n}\n\n.accent-primary .btn-link,\n.accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-primary .nav-tabs .nav-link {\n  color: #007bff;\n}\n\n.accent-primary .btn-link:hover,\n.accent-primary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-primary .nav-tabs .nav-link:hover {\n  color: #0056b3;\n}\n\n.accent-primary .dropdown-item:active, .accent-primary .dropdown-item.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.accent-primary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.accent-primary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-primary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-primary .custom-select:focus,\n.accent-primary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-primary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n}\n\n.accent-primary .page-item .page-link {\n  color: #007bff;\n}\n\n.accent-primary .page-item.active a,\n.accent-primary .page-item.active .page-link {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.accent-primary .page-item.disabled a,\n.accent-primary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-primary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-primary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-secondary .btn-link,\n.accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-secondary .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.accent-secondary .btn-link:hover,\n.accent-secondary a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-secondary .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.accent-secondary .dropdown-item:active, .accent-secondary .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.accent-secondary .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.accent-secondary .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-secondary .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-secondary .custom-select:focus,\n.accent-secondary .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-secondary .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.accent-secondary .page-item .page-link {\n  color: #6c757d;\n}\n\n.accent-secondary .page-item.active a,\n.accent-secondary .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.accent-secondary .page-item.disabled a,\n.accent-secondary .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-secondary [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-secondary [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-success .btn-link,\n.accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-success .nav-tabs .nav-link {\n  color: #28a745;\n}\n\n.accent-success .btn-link:hover,\n.accent-success a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-success .nav-tabs .nav-link:hover {\n  color: #19692c;\n}\n\n.accent-success .dropdown-item:active, .accent-success .dropdown-item.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.accent-success .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.accent-success .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-success .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-success .custom-select:focus,\n.accent-success .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-success .custom-file-input:focus ~ .custom-file-label {\n  border-color: #71dd8a;\n}\n\n.accent-success .page-item .page-link {\n  color: #28a745;\n}\n\n.accent-success .page-item.active a,\n.accent-success .page-item.active .page-link {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.accent-success .page-item.disabled a,\n.accent-success .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-success [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-success [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-info .btn-link,\n.accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-info .nav-tabs .nav-link {\n  color: #17a2b8;\n}\n\n.accent-info .btn-link:hover,\n.accent-info a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-info .nav-tabs .nav-link:hover {\n  color: #0f6674;\n}\n\n.accent-info .dropdown-item:active, .accent-info .dropdown-item.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-info .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.accent-info .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-info .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-info .custom-select:focus,\n.accent-info .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-info .custom-file-input:focus ~ .custom-file-label {\n  border-color: #63d9ec;\n}\n\n.accent-info .page-item .page-link {\n  color: #17a2b8;\n}\n\n.accent-info .page-item.active a,\n.accent-info .page-item.active .page-link {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-info .page-item.disabled a,\n.accent-info .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-info [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-info [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-warning .btn-link,\n.accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-warning .nav-tabs .nav-link {\n  color: #ffc107;\n}\n\n.accent-warning .btn-link:hover,\n.accent-warning a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-warning .nav-tabs .nav-link:hover {\n  color: #ba8b00;\n}\n\n.accent-warning .dropdown-item:active, .accent-warning .dropdown-item.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.accent-warning .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.accent-warning .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-warning .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-warning .custom-select:focus,\n.accent-warning .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-warning .custom-file-input:focus ~ .custom-file-label {\n  border-color: #ffe187;\n}\n\n.accent-warning .page-item .page-link {\n  color: #ffc107;\n}\n\n.accent-warning .page-item.active a,\n.accent-warning .page-item.active .page-link {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #fff;\n}\n\n.accent-warning .page-item.disabled a,\n.accent-warning .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-warning [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-warning [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-danger .btn-link,\n.accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-danger .nav-tabs .nav-link {\n  color: #dc3545;\n}\n\n.accent-danger .btn-link:hover,\n.accent-danger a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-danger .nav-tabs .nav-link:hover {\n  color: #a71d2a;\n}\n\n.accent-danger .dropdown-item:active, .accent-danger .dropdown-item.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.accent-danger .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.accent-danger .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-danger .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-danger .custom-select:focus,\n.accent-danger .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-danger .custom-file-input:focus ~ .custom-file-label {\n  border-color: #efa2a9;\n}\n\n.accent-danger .page-item .page-link {\n  color: #dc3545;\n}\n\n.accent-danger .page-item.active a,\n.accent-danger .page-item.active .page-link {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.accent-danger .page-item.disabled a,\n.accent-danger .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-danger [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-danger [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-light .btn-link,\n.accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-light .nav-tabs .nav-link {\n  color: #f8f9fa;\n}\n\n.accent-light .btn-link:hover,\n.accent-light a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-light .nav-tabs .nav-link:hover {\n  color: #cbd3da;\n}\n\n.accent-light .dropdown-item:active, .accent-light .dropdown-item.active {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.accent-light .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f8f9fa;\n  border-color: #bdc6d0;\n}\n\n.accent-light .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-light .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-light .custom-select:focus,\n.accent-light .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-light .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.accent-light .page-item .page-link {\n  color: #f8f9fa;\n}\n\n.accent-light .page-item.active a,\n.accent-light .page-item.active .page-link {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n  color: #fff;\n}\n\n.accent-light .page-item.disabled a,\n.accent-light .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-light [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-light [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-dark .btn-link,\n.accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.accent-dark .btn-link:hover,\n.accent-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.accent-dark .dropdown-item:active, .accent-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.accent-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.accent-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-dark .custom-select:focus,\n.accent-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.accent-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.accent-dark .page-item.active a,\n.accent-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.accent-dark .page-item.disabled a,\n.accent-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-lightblue .btn-link,\n.accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-lightblue .nav-tabs .nav-link {\n  color: #3c8dbc;\n}\n\n.accent-lightblue .btn-link:hover,\n.accent-lightblue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-lightblue .nav-tabs .nav-link:hover {\n  color: #296282;\n}\n\n.accent-lightblue .dropdown-item:active, .accent-lightblue .dropdown-item.active {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.accent-lightblue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3c8dbc;\n  border-color: #23536f;\n}\n\n.accent-lightblue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-lightblue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-lightblue .custom-select:focus,\n.accent-lightblue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-lightblue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #99c5de;\n}\n\n.accent-lightblue .page-item .page-link {\n  color: #3c8dbc;\n}\n\n.accent-lightblue .page-item.active a,\n.accent-lightblue .page-item.active .page-link {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n  color: #fff;\n}\n\n.accent-lightblue .page-item.disabled a,\n.accent-lightblue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-lightblue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-lightblue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-navy .btn-link,\n.accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-navy .nav-tabs .nav-link {\n  color: #001f3f;\n}\n\n.accent-navy .btn-link:hover,\n.accent-navy a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-navy .nav-tabs .nav-link:hover {\n  color: black;\n}\n\n.accent-navy .dropdown-item:active, .accent-navy .dropdown-item.active {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.accent-navy .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #001f3f;\n  border-color: black;\n}\n\n.accent-navy .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-navy .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-navy .custom-select:focus,\n.accent-navy .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-navy .custom-file-input:focus ~ .custom-file-label {\n  border-color: #005ebf;\n}\n\n.accent-navy .page-item .page-link {\n  color: #001f3f;\n}\n\n.accent-navy .page-item.active a,\n.accent-navy .page-item.active .page-link {\n  background-color: #001f3f;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.accent-navy .page-item.disabled a,\n.accent-navy .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-navy [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-navy [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-olive .btn-link,\n.accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-olive .nav-tabs .nav-link {\n  color: #3d9970;\n}\n\n.accent-olive .btn-link:hover,\n.accent-olive a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-olive .nav-tabs .nav-link:hover {\n  color: #276248;\n}\n\n.accent-olive .dropdown-item:active, .accent-olive .dropdown-item.active {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.accent-olive .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #3d9970;\n  border-color: #20503b;\n}\n\n.accent-olive .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-olive .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-olive .custom-select:focus,\n.accent-olive .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-olive .custom-file-input:focus ~ .custom-file-label {\n  border-color: #87cfaf;\n}\n\n.accent-olive .page-item .page-link {\n  color: #3d9970;\n}\n\n.accent-olive .page-item.active a,\n.accent-olive .page-item.active .page-link {\n  background-color: #3d9970;\n  border-color: #3d9970;\n  color: #fff;\n}\n\n.accent-olive .page-item.disabled a,\n.accent-olive .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-olive [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-olive [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-lime .btn-link,\n.accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-lime .nav-tabs .nav-link {\n  color: #01ff70;\n}\n\n.accent-lime .btn-link:hover,\n.accent-lime a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-lime .nav-tabs .nav-link:hover {\n  color: #00b44e;\n}\n\n.accent-lime .dropdown-item:active, .accent-lime .dropdown-item.active {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.accent-lime .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #01ff70;\n  border-color: #009a43;\n}\n\n.accent-lime .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-lime .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-lime .custom-select:focus,\n.accent-lime .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-lime .custom-file-input:focus ~ .custom-file-label {\n  border-color: #81ffb8;\n}\n\n.accent-lime .page-item .page-link {\n  color: #01ff70;\n}\n\n.accent-lime .page-item.active a,\n.accent-lime .page-item.active .page-link {\n  background-color: #01ff70;\n  border-color: #01ff70;\n  color: #fff;\n}\n\n.accent-lime .page-item.disabled a,\n.accent-lime .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-lime [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-lime [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-fuchsia .btn-link,\n.accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-fuchsia .nav-tabs .nav-link {\n  color: #f012be;\n}\n\n.accent-fuchsia .btn-link:hover,\n.accent-fuchsia a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-fuchsia .nav-tabs .nav-link:hover {\n  color: #ab0b87;\n}\n\n.accent-fuchsia .dropdown-item:active, .accent-fuchsia .dropdown-item.active {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.accent-fuchsia .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #f012be;\n  border-color: #930974;\n}\n\n.accent-fuchsia .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-fuchsia .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-fuchsia .custom-select:focus,\n.accent-fuchsia .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-fuchsia .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f88adf;\n}\n\n.accent-fuchsia .page-item .page-link {\n  color: #f012be;\n}\n\n.accent-fuchsia .page-item.active a,\n.accent-fuchsia .page-item.active .page-link {\n  background-color: #f012be;\n  border-color: #f012be;\n  color: #fff;\n}\n\n.accent-fuchsia .page-item.disabled a,\n.accent-fuchsia .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-fuchsia [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-fuchsia [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-maroon .btn-link,\n.accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-maroon .nav-tabs .nav-link {\n  color: #d81b60;\n}\n\n.accent-maroon .btn-link:hover,\n.accent-maroon a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-maroon .nav-tabs .nav-link:hover {\n  color: #941342;\n}\n\n.accent-maroon .dropdown-item:active, .accent-maroon .dropdown-item.active {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.accent-maroon .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #d81b60;\n  border-color: #7d1038;\n}\n\n.accent-maroon .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-maroon .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-maroon .custom-select:focus,\n.accent-maroon .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-maroon .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f083ab;\n}\n\n.accent-maroon .page-item .page-link {\n  color: #d81b60;\n}\n\n.accent-maroon .page-item.active a,\n.accent-maroon .page-item.active .page-link {\n  background-color: #d81b60;\n  border-color: #d81b60;\n  color: #fff;\n}\n\n.accent-maroon .page-item.disabled a,\n.accent-maroon .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-maroon [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-maroon [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-blue .btn-link,\n.accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-blue .nav-tabs .nav-link {\n  color: #007bff;\n}\n\n.accent-blue .btn-link:hover,\n.accent-blue a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-blue .nav-tabs .nav-link:hover {\n  color: #0056b3;\n}\n\n.accent-blue .dropdown-item:active, .accent-blue .dropdown-item.active {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.accent-blue .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #007bff;\n  border-color: #004a99;\n}\n\n.accent-blue .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-blue .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-blue .custom-select:focus,\n.accent-blue .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-blue .custom-file-input:focus ~ .custom-file-label {\n  border-color: #80bdff;\n}\n\n.accent-blue .page-item .page-link {\n  color: #007bff;\n}\n\n.accent-blue .page-item.active a,\n.accent-blue .page-item.active .page-link {\n  background-color: #007bff;\n  border-color: #007bff;\n  color: #fff;\n}\n\n.accent-blue .page-item.disabled a,\n.accent-blue .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-blue [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-blue [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-indigo .btn-link,\n.accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-indigo .nav-tabs .nav-link {\n  color: #6610f2;\n}\n\n.accent-indigo .btn-link:hover,\n.accent-indigo a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-indigo .nav-tabs .nav-link:hover {\n  color: #4709ac;\n}\n\n.accent-indigo .dropdown-item:active, .accent-indigo .dropdown-item.active {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.accent-indigo .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6610f2;\n  border-color: #3d0894;\n}\n\n.accent-indigo .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-indigo .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-indigo .custom-select:focus,\n.accent-indigo .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-indigo .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b389f9;\n}\n\n.accent-indigo .page-item .page-link {\n  color: #6610f2;\n}\n\n.accent-indigo .page-item.active a,\n.accent-indigo .page-item.active .page-link {\n  background-color: #6610f2;\n  border-color: #6610f2;\n  color: #fff;\n}\n\n.accent-indigo .page-item.disabled a,\n.accent-indigo .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-indigo [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-indigo [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-purple .btn-link,\n.accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-purple .nav-tabs .nav-link {\n  color: #6f42c1;\n}\n\n.accent-purple .btn-link:hover,\n.accent-purple a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-purple .nav-tabs .nav-link:hover {\n  color: #4e2d89;\n}\n\n.accent-purple .dropdown-item:active, .accent-purple .dropdown-item.active {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.accent-purple .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6f42c1;\n  border-color: #432776;\n}\n\n.accent-purple .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-purple .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-purple .custom-select:focus,\n.accent-purple .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-purple .custom-file-input:focus ~ .custom-file-label {\n  border-color: #b8a2e0;\n}\n\n.accent-purple .page-item .page-link {\n  color: #6f42c1;\n}\n\n.accent-purple .page-item.active a,\n.accent-purple .page-item.active .page-link {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n  color: #fff;\n}\n\n.accent-purple .page-item.disabled a,\n.accent-purple .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-purple [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-purple [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-pink .btn-link,\n.accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-pink .nav-tabs .nav-link {\n  color: #e83e8c;\n}\n\n.accent-pink .btn-link:hover,\n.accent-pink a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-pink .nav-tabs .nav-link:hover {\n  color: #c21766;\n}\n\n.accent-pink .dropdown-item:active, .accent-pink .dropdown-item.active {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.accent-pink .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #e83e8c;\n  border-color: #ac145a;\n}\n\n.accent-pink .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-pink .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-pink .custom-select:focus,\n.accent-pink .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-pink .custom-file-input:focus ~ .custom-file-label {\n  border-color: #f6b0d0;\n}\n\n.accent-pink .page-item .page-link {\n  color: #e83e8c;\n}\n\n.accent-pink .page-item.active a,\n.accent-pink .page-item.active .page-link {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n  color: #fff;\n}\n\n.accent-pink .page-item.disabled a,\n.accent-pink .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-pink [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-pink [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-red .btn-link,\n.accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-red .nav-tabs .nav-link {\n  color: #dc3545;\n}\n\n.accent-red .btn-link:hover,\n.accent-red a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-red .nav-tabs .nav-link:hover {\n  color: #a71d2a;\n}\n\n.accent-red .dropdown-item:active, .accent-red .dropdown-item.active {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.accent-red .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #dc3545;\n  border-color: #921925;\n}\n\n.accent-red .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-red .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-red .custom-select:focus,\n.accent-red .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-red .custom-file-input:focus ~ .custom-file-label {\n  border-color: #efa2a9;\n}\n\n.accent-red .page-item .page-link {\n  color: #dc3545;\n}\n\n.accent-red .page-item.active a,\n.accent-red .page-item.active .page-link {\n  background-color: #dc3545;\n  border-color: #dc3545;\n  color: #fff;\n}\n\n.accent-red .page-item.disabled a,\n.accent-red .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-red [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-red [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-orange .btn-link,\n.accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-orange .nav-tabs .nav-link {\n  color: #fd7e14;\n}\n\n.accent-orange .btn-link:hover,\n.accent-orange a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-orange .nav-tabs .nav-link:hover {\n  color: #c35a02;\n}\n\n.accent-orange .dropdown-item:active, .accent-orange .dropdown-item.active {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.accent-orange .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fd7e14;\n  border-color: #aa4e01;\n}\n\n.accent-orange .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-orange .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-orange .custom-select:focus,\n.accent-orange .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-orange .custom-file-input:focus ~ .custom-file-label {\n  border-color: #fec392;\n}\n\n.accent-orange .page-item .page-link {\n  color: #fd7e14;\n}\n\n.accent-orange .page-item.active a,\n.accent-orange .page-item.active .page-link {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n  color: #fff;\n}\n\n.accent-orange .page-item.disabled a,\n.accent-orange .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-orange [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-orange [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-yellow .btn-link,\n.accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-yellow .nav-tabs .nav-link {\n  color: #ffc107;\n}\n\n.accent-yellow .btn-link:hover,\n.accent-yellow a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-yellow .nav-tabs .nav-link:hover {\n  color: #ba8b00;\n}\n\n.accent-yellow .dropdown-item:active, .accent-yellow .dropdown-item.active {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.accent-yellow .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #ffc107;\n  border-color: #a07800;\n}\n\n.accent-yellow .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-yellow .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-yellow .custom-select:focus,\n.accent-yellow .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-yellow .custom-file-input:focus ~ .custom-file-label {\n  border-color: #ffe187;\n}\n\n.accent-yellow .page-item .page-link {\n  color: #ffc107;\n}\n\n.accent-yellow .page-item.active a,\n.accent-yellow .page-item.active .page-link {\n  background-color: #ffc107;\n  border-color: #ffc107;\n  color: #fff;\n}\n\n.accent-yellow .page-item.disabled a,\n.accent-yellow .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-yellow [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-yellow [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-green .btn-link,\n.accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-green .nav-tabs .nav-link {\n  color: #28a745;\n}\n\n.accent-green .btn-link:hover,\n.accent-green a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-green .nav-tabs .nav-link:hover {\n  color: #19692c;\n}\n\n.accent-green .dropdown-item:active, .accent-green .dropdown-item.active {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.accent-green .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #28a745;\n  border-color: #145523;\n}\n\n.accent-green .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-green .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-green .custom-select:focus,\n.accent-green .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-green .custom-file-input:focus ~ .custom-file-label {\n  border-color: #71dd8a;\n}\n\n.accent-green .page-item .page-link {\n  color: #28a745;\n}\n\n.accent-green .page-item.active a,\n.accent-green .page-item.active .page-link {\n  background-color: #28a745;\n  border-color: #28a745;\n  color: #fff;\n}\n\n.accent-green .page-item.disabled a,\n.accent-green .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-green [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-green [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-teal .btn-link,\n.accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-teal .nav-tabs .nav-link {\n  color: #20c997;\n}\n\n.accent-teal .btn-link:hover,\n.accent-teal a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-teal .nav-tabs .nav-link:hover {\n  color: #158765;\n}\n\n.accent-teal .dropdown-item:active, .accent-teal .dropdown-item.active {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.accent-teal .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #20c997;\n  border-color: #127155;\n}\n\n.accent-teal .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-teal .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-teal .custom-select:focus,\n.accent-teal .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-teal .custom-file-input:focus ~ .custom-file-label {\n  border-color: #7eeaca;\n}\n\n.accent-teal .page-item .page-link {\n  color: #20c997;\n}\n\n.accent-teal .page-item.active a,\n.accent-teal .page-item.active .page-link {\n  background-color: #20c997;\n  border-color: #20c997;\n  color: #fff;\n}\n\n.accent-teal .page-item.disabled a,\n.accent-teal .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-teal [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-teal [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-cyan .btn-link,\n.accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-cyan .nav-tabs .nav-link {\n  color: #17a2b8;\n}\n\n.accent-cyan .btn-link:hover,\n.accent-cyan a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-cyan .nav-tabs .nav-link:hover {\n  color: #0f6674;\n}\n\n.accent-cyan .dropdown-item:active, .accent-cyan .dropdown-item.active {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-cyan .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #17a2b8;\n  border-color: #0c525d;\n}\n\n.accent-cyan .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-cyan .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-cyan .custom-select:focus,\n.accent-cyan .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-cyan .custom-file-input:focus ~ .custom-file-label {\n  border-color: #63d9ec;\n}\n\n.accent-cyan .page-item .page-link {\n  color: #17a2b8;\n}\n\n.accent-cyan .page-item.active a,\n.accent-cyan .page-item.active .page-link {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n  color: #fff;\n}\n\n.accent-cyan .page-item.disabled a,\n.accent-cyan .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-cyan [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-cyan [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-white .btn-link,\n.accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-white .nav-tabs .nav-link {\n  color: #fff;\n}\n\n.accent-white .btn-link:hover,\n.accent-white a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-white .nav-tabs .nav-link:hover {\n  color: #d9d9d9;\n}\n\n.accent-white .dropdown-item:active, .accent-white .dropdown-item.active {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.accent-white .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #fff;\n  border-color: #cccccc;\n}\n\n.accent-white .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%231f2d3d' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-white .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-white .custom-select:focus,\n.accent-white .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-white .custom-file-input:focus ~ .custom-file-label {\n  border-color: white;\n}\n\n.accent-white .page-item .page-link {\n  color: #fff;\n}\n\n.accent-white .page-item.active a,\n.accent-white .page-item.active .page-link {\n  background-color: #fff;\n  border-color: #fff;\n  color: #fff;\n}\n\n.accent-white .page-item.disabled a,\n.accent-white .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-white [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-white [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-gray .btn-link,\n.accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-gray .nav-tabs .nav-link {\n  color: #6c757d;\n}\n\n.accent-gray .btn-link:hover,\n.accent-gray a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-gray .nav-tabs .nav-link:hover {\n  color: #494f54;\n}\n\n.accent-gray .dropdown-item:active, .accent-gray .dropdown-item.active {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.accent-gray .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #6c757d;\n  border-color: #3d4246;\n}\n\n.accent-gray .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-gray .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-gray .custom-select:focus,\n.accent-gray .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-gray .custom-file-input:focus ~ .custom-file-label {\n  border-color: #afb5ba;\n}\n\n.accent-gray .page-item .page-link {\n  color: #6c757d;\n}\n\n.accent-gray .page-item.active a,\n.accent-gray .page-item.active .page-link {\n  background-color: #6c757d;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.accent-gray .page-item.disabled a,\n.accent-gray .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-gray [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-gray [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n.accent-gray-dark .btn-link,\n.accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn),\n.accent-gray-dark .nav-tabs .nav-link {\n  color: #343a40;\n}\n\n.accent-gray-dark .btn-link:hover,\n.accent-gray-dark a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):not(.page-link):not(.badge):not(.btn):hover,\n.accent-gray-dark .nav-tabs .nav-link:hover {\n  color: #121416;\n}\n\n.accent-gray-dark .dropdown-item:active, .accent-gray-dark .dropdown-item.active {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.accent-gray-dark .custom-control-input:checked ~ .custom-control-label::before {\n  background-color: #343a40;\n  border-color: #060708;\n}\n\n.accent-gray-dark .custom-control-input:checked ~ .custom-control-label::after {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.accent-gray-dark .form-control:focus:not(.is-invalid):not(.is-warning):not(.is-valid),\n.accent-gray-dark .custom-select:focus,\n.accent-gray-dark .custom-control-input:focus:not(:checked) ~ .custom-control-label::before,\n.accent-gray-dark .custom-file-input:focus ~ .custom-file-label {\n  border-color: #6d7a86;\n}\n\n.accent-gray-dark .page-item .page-link {\n  color: #343a40;\n}\n\n.accent-gray-dark .page-item.active a,\n.accent-gray-dark .page-item.active .page-link {\n  background-color: #343a40;\n  border-color: #343a40;\n  color: #fff;\n}\n\n.accent-gray-dark .page-item.disabled a,\n.accent-gray-dark .page-item.disabled .page-link {\n  background-color: #fff;\n  border-color: #dee2e6;\n  color: #6c757d;\n}\n\n.accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #c2c7d0;\n}\n\n.accent-gray-dark [class*=\"sidebar-dark-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #fff;\n}\n\n.accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link) {\n  color: #343a40;\n}\n\n.accent-gray-dark [class*=\"sidebar-light-\"] .sidebar a:not(.dropdown-item):not(.btn-app):not(.nav-link):not(.brand-link):hover {\n  color: #212529;\n}\n\n[class*=\"accent-\"] a.btn-primary {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-secondary {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-success {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-info {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-warning {\n  color: #1f2d3d;\n}\n\n[class*=\"accent-\"] a.btn-danger {\n  color: #fff;\n}\n\n[class*=\"accent-\"] a.btn-light {\n  color: #1f2d3d;\n}\n\n[class*=\"accent-\"] a.btn-dark {\n  color: #fff;\n}\n/*# sourceMappingURL=adminlte.light.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/css/alt/adminlte.pages.css",
    "content": "/*!\n *   AdminLTE v3.2.0\n *     Only Pages\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n.close, .mailbox-attachment-close {\n  float: right;\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  opacity: .5;\n}\n\n.close:hover, .mailbox-attachment-close:hover {\n  color: #000;\n  text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .mailbox-attachment-close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus, .mailbox-attachment-close:not(:disabled):not(.disabled):focus {\n  opacity: .75;\n}\n\nbutton.close, button.mailbox-attachment-close {\n  padding: 0;\n  background-color: transparent;\n  border: 0;\n}\n\na.close.disabled, a.disabled.mailbox-attachment-close {\n  pointer-events: none;\n}\n\n@-webkit-keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@-webkit-keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@-webkit-keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@-webkit-keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@-webkit-keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.mailbox-messages > .table {\n  margin: 0;\n}\n\n.mailbox-controls {\n  padding: 5px;\n}\n\n.mailbox-controls.with-border {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.mailbox-read-info {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n  padding: 10px;\n}\n\n.mailbox-read-info h3 {\n  font-size: 20px;\n  margin: 0;\n}\n\n.mailbox-read-info h5 {\n  margin: 0;\n  padding: 5px 0 0;\n}\n\n.mailbox-read-time {\n  color: #999;\n  font-size: 13px;\n}\n\n.mailbox-read-message {\n  padding: 10px;\n}\n\n.mailbox-attachments {\n  padding-left: 0;\n  list-style: none;\n}\n\n.mailbox-attachments li {\n  border: 1px solid #eee;\n  float: left;\n  margin-bottom: 10px;\n  margin-right: 10px;\n  width: 200px;\n}\n\n.mailbox-attachment-name {\n  color: #666;\n  font-weight: 700;\n}\n\n.mailbox-attachment-icon,\n.mailbox-attachment-info,\n.mailbox-attachment-size {\n  display: block;\n}\n\n.mailbox-attachment-info {\n  background-color: #f8f9fa;\n  padding: 10px;\n}\n\n.mailbox-attachment-size {\n  color: #999;\n  font-size: 12px;\n}\n\n.mailbox-attachment-size > span {\n  display: inline-block;\n  padding-top: .75rem;\n}\n\n.mailbox-attachment-icon {\n  color: #666;\n  font-size: 65px;\n  max-height: 132.5px;\n  padding: 20px 10px;\n  text-align: center;\n}\n\n.mailbox-attachment-icon.has-img {\n  padding: 0;\n}\n\n.mailbox-attachment-icon.has-img > img {\n  height: auto;\n  max-width: 100%;\n}\n\n.lockscreen {\n  background-color: #e9ecef;\n}\n\n.lockscreen .lockscreen-name {\n  font-weight: 600;\n  text-align: center;\n}\n\n.lockscreen-logo {\n  font-size: 35px;\n  font-weight: 300;\n  margin-bottom: 25px;\n  text-align: center;\n}\n\n.lockscreen-logo a {\n  color: #495057;\n}\n\n.lockscreen-wrapper {\n  margin: 0 auto;\n  margin-top: 10%;\n  max-width: 400px;\n}\n\n.lockscreen-item {\n  border-radius: 4px;\n  background-color: #fff;\n  margin: 10px auto 30px;\n  padding: 0;\n  position: relative;\n  width: 290px;\n}\n\n.lockscreen-image {\n  border-radius: 50%;\n  background-color: #fff;\n  left: -10px;\n  padding: 5px;\n  position: absolute;\n  top: -25px;\n  z-index: 10;\n}\n\n.lockscreen-image > img {\n  border-radius: 50%;\n  height: 70px;\n  width: 70px;\n}\n\n.lockscreen-credentials {\n  margin-left: 70px;\n}\n\n.lockscreen-credentials .form-control {\n  border: 0;\n}\n\n.lockscreen-credentials .btn {\n  background-color: #fff;\n  border: 0;\n  padding: 0 10px;\n}\n\n.lockscreen-footer {\n  margin-top: 10px;\n}\n\n.dark-mode .lockscreen-item {\n  background-color: #343a40;\n}\n\n.dark-mode .lockscreen-logo a {\n  color: #fff;\n}\n\n.dark-mode .lockscreen-credentials .btn {\n  background-color: #343a40;\n}\n\n.dark-mode .lockscreen-image {\n  background-color: #6c757d;\n}\n\n.login-logo,\n.register-logo {\n  font-size: 2.1rem;\n  font-weight: 300;\n  margin-bottom: .9rem;\n  text-align: center;\n}\n\n.login-logo a,\n.register-logo a {\n  color: #495057;\n}\n\n.login-page,\n.register-page {\n  -ms-flex-align: center;\n  align-items: center;\n  background-color: #e9ecef;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-direction: column;\n  flex-direction: column;\n  height: 100vh;\n  -ms-flex-pack: center;\n  justify-content: center;\n}\n\n.login-box,\n.register-box {\n  width: 360px;\n}\n\n@media (max-width: 576px) {\n  .login-box,\n  .register-box {\n    margin-top: .5rem;\n    width: 90%;\n  }\n}\n\n.login-box .card,\n.register-box .card {\n  margin-bottom: 0;\n}\n\n.login-card-body,\n.register-card-body {\n  background-color: #fff;\n  border-top: 0;\n  color: #666;\n  padding: 20px;\n}\n\n.login-card-body .input-group .form-control,\n.register-card-body .input-group .form-control {\n  border-right: 0;\n}\n\n.login-card-body .input-group .form-control:focus,\n.register-card-body .input-group .form-control:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,\n.login-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,\n.register-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text {\n  border-color: #80bdff;\n}\n\n.login-card-body .input-group .form-control.is-valid:focus,\n.register-card-body .input-group .form-control.is-valid:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,\n.login-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,\n.register-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text {\n  border-color: #28a745;\n}\n\n.login-card-body .input-group .form-control.is-invalid:focus,\n.register-card-body .input-group .form-control.is-invalid:focus {\n  box-shadow: none;\n}\n\n.login-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text,\n.register-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text {\n  border-color: #dc3545;\n}\n\n.login-card-body .input-group .input-group-text,\n.register-card-body .input-group .input-group-text {\n  background-color: transparent;\n  border-bottom-right-radius: 0.25rem;\n  border-left: 0;\n  border-top-right-radius: 0.25rem;\n  color: #777;\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.login-box-msg,\n.register-box-msg {\n  margin: 0;\n  padding: 0 20px 20px;\n  text-align: center;\n}\n\n.social-auth-links {\n  margin: 10px 0;\n}\n\n.dark-mode .login-card-body,\n.dark-mode .register-card-body {\n  background-color: #343a40;\n  border-color: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .login-logo a,\n.dark-mode .register-logo a {\n  color: #fff;\n}\n\n.error-page {\n  margin: 20px auto 0;\n  width: 600px;\n}\n\n@media (max-width: 767.98px) {\n  .error-page {\n    width: 100%;\n  }\n}\n\n.error-page > .headline {\n  float: left;\n  font-size: 100px;\n  font-weight: 300;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .headline {\n    float: none;\n    text-align: center;\n  }\n}\n\n.error-page > .error-content {\n  display: block;\n  margin-left: 190px;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .error-content {\n    margin-left: 0;\n  }\n}\n\n.error-page > .error-content > h3 {\n  font-size: 25px;\n  font-weight: 300;\n}\n\n@media (max-width: 767.98px) {\n  .error-page > .error-content > h3 {\n    text-align: center;\n  }\n}\n\n.invoice {\n  background-color: #fff;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n  position: relative;\n}\n\n.invoice-title {\n  margin-top: 0;\n}\n\n.dark-mode .invoice {\n  background-color: #343a40;\n}\n\n.profile-user-img {\n  border: 3px solid #adb5bd;\n  margin: 0 auto;\n  padding: 3px;\n  width: 100px;\n}\n\n.profile-username {\n  font-size: 21px;\n  margin-top: 5px;\n}\n\n.post {\n  border-bottom: 1px solid #adb5bd;\n  color: #666;\n  margin-bottom: 15px;\n  padding-bottom: 15px;\n}\n\n.post:last-of-type {\n  border-bottom: 0;\n  margin-bottom: 0;\n  padding-bottom: 0;\n}\n\n.post .user-block {\n  margin-bottom: 15px;\n  width: 100%;\n}\n\n.post .row {\n  width: 100%;\n}\n\n.dark-mode .post {\n  color: #fff;\n  border-color: #6c757d;\n}\n\n.product-image {\n  max-width: 100%;\n  height: auto;\n  width: 100%;\n}\n\n.product-image-thumbs {\n  -ms-flex-align: stretch;\n  align-items: stretch;\n  display: -ms-flexbox;\n  display: flex;\n  margin-top: 2rem;\n}\n\n.product-image-thumb {\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n  border-radius: 0.25rem;\n  background-color: #fff;\n  border: 1px solid #dee2e6;\n  display: -ms-flexbox;\n  display: flex;\n  margin-right: 1rem;\n  max-width: 7rem;\n  padding: 0.5rem;\n}\n\n.product-image-thumb img {\n  max-width: 100%;\n  height: auto;\n  -ms-flex-item-align: center;\n  align-self: center;\n}\n\n.product-image-thumb:hover {\n  opacity: .5;\n}\n\n.product-share a {\n  margin-right: .5rem;\n}\n\n.projects td {\n  vertical-align: middle;\n}\n\n.projects .list-inline {\n  margin-bottom: 0;\n}\n\n.projects img.table-avatar,\n.projects .table-avatar img {\n  border-radius: 50%;\n  display: inline;\n  width: 2.5rem;\n}\n\n.projects .project-state {\n  text-align: center;\n}\n\nbody.iframe-mode .main-sidebar {\n  display: none;\n}\n\nbody.iframe-mode .content-wrapper {\n  margin-left: 0 !important;\n  margin-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\nbody.iframe-mode .main-header,\nbody.iframe-mode .main-footer {\n  display: none;\n}\n\nbody.iframe-mode-fullscreen {\n  overflow: hidden;\n}\n\nbody.iframe-mode-fullscreen.layout-navbar-fixed .wrapper .content-wrapper {\n  margin-top: 0 !important;\n}\n\n.content-wrapper {\n  height: 100%;\n}\n\n.content-wrapper.iframe-mode .btn-iframe-close {\n  color: #dc3545;\n  position: absolute;\n  line-height: 1;\n  right: .125rem;\n  top: .125rem;\n  z-index: 10;\n  visibility: hidden;\n}\n\n.content-wrapper.iframe-mode .btn-iframe-close:hover, .content-wrapper.iframe-mode .btn-iframe-close:focus {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n@media (hover: none) and (pointer: coarse) {\n  .content-wrapper.iframe-mode .btn-iframe-close {\n    visibility: visible;\n  }\n}\n\n.content-wrapper.iframe-mode .navbar-nav {\n  overflow-y: auto;\n  width: 100%;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-link {\n  white-space: nowrap;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-item {\n  position: relative;\n}\n\n.content-wrapper.iframe-mode .navbar-nav .nav-item:hover .btn-iframe-close, .content-wrapper.iframe-mode .navbar-nav .nav-item:focus .btn-iframe-close {\n  -webkit-animation-name: fadeIn;\n  animation-name: fadeIn;\n  -webkit-animation-duration: 0.3s;\n  animation-duration: 0.3s;\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n  visibility: visible;\n}\n\n@media (hover: none) and (pointer: coarse) {\n  .content-wrapper.iframe-mode .navbar-nav .nav-item:hover .btn-iframe-close, .content-wrapper.iframe-mode .navbar-nav .nav-item:focus .btn-iframe-close {\n    visibility: visible;\n  }\n}\n\n.content-wrapper.iframe-mode .tab-content {\n  position: relative;\n}\n\n.content-wrapper.iframe-mode .tab-pane + .tab-empty {\n  display: none;\n}\n\n.content-wrapper.iframe-mode .tab-empty {\n  width: 100%;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n}\n\n.content-wrapper.iframe-mode .tab-loading {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  display: none;\n  background-color: #f4f6f9;\n}\n\n.content-wrapper.iframe-mode .tab-loading > div {\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-pack: center;\n  justify-content: center;\n  -ms-flex-align: center;\n  align-items: center;\n  width: 100%;\n  height: 100%;\n}\n\n.content-wrapper.iframe-mode iframe {\n  border: 0;\n  width: 100%;\n  height: 100%;\n  margin-bottom: -8px;\n}\n\n.content-wrapper.iframe-mode iframe .content-wrapper {\n  padding-bottom: 0 !important;\n}\n\nbody.iframe-mode-fullscreen .content-wrapper.iframe-mode {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  margin-left: 0 !important;\n  height: 100%;\n  min-height: 100%;\n  z-index: 1048;\n}\n\n.permanent-btn-iframe-close .btn-iframe-close {\n  -webkit-animation: none !important;\n  animation: none !important;\n  visibility: visible !important;\n  opacity: 1;\n}\n\n.dark-mode .content-wrapper.iframe-mode .tab-loading {\n  background-color: #343a40;\n}\n\n.content-wrapper.kanban {\n  height: 1px;\n}\n\n.content-wrapper.kanban .content {\n  height: 100%;\n  overflow-x: auto;\n  overflow-y: hidden;\n}\n\n.content-wrapper.kanban .content .container,\n.content-wrapper.kanban .content .container-fluid {\n  width: -webkit-max-content;\n  width: -moz-max-content;\n  width: max-content;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-align: stretch;\n  align-items: stretch;\n}\n\n.content-wrapper.kanban .content-header + .content {\n  height: calc(100% - ((2 * 15px) + (1.8rem * 1.2)));\n}\n\n.content-wrapper.kanban .card .card-body {\n  padding: .5rem;\n}\n\n.content-wrapper.kanban .card.card-row {\n  width: 340px;\n  display: inline-block;\n  margin: 0 .5rem;\n}\n\n.content-wrapper.kanban .card.card-row:first-child {\n  margin-left: 0;\n}\n\n.content-wrapper.kanban .card.card-row .card-body {\n  height: calc(100% - (12px + (1.8rem * 1.2) + .5rem));\n  overflow-y: auto;\n}\n\n.content-wrapper.kanban .card.card-row .card:last-child {\n  margin-bottom: 0;\n  border-bottom-width: 1px;\n}\n\n.content-wrapper.kanban .card.card-row .card .card-header {\n  padding: .5rem .75rem;\n}\n\n.content-wrapper.kanban .card.card-row .card .card-body {\n  padding: .75rem;\n}\n\n.content-wrapper.kanban .btn-tool.btn-link {\n  text-decoration: underline;\n  padding-left: 0;\n  padding-right: 0;\n}\n/*# sourceMappingURL=adminlte.pages.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/css/alt/adminlte.plugins.css",
    "content": "/*!\n *   AdminLTE v3.2.0\n *     Only Plugins\n *   Author: Colorlib\n *   Website: AdminLTE.io <https://adminlte.io>\n *   License: Open source - MIT <https://opensource.org/licenses/MIT>\n */\n@-webkit-keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n@keyframes flipInX {\n  0% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\n    transition-timing-function: ease-in;\n    opacity: 0;\n  }\n  40% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\n    transition-timing-function: ease-in;\n  }\n  60% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\n    opacity: 1;\n  }\n  80% {\n    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);\n  }\n  100% {\n    -webkit-transform: perspective(400px);\n    transform: perspective(400px);\n  }\n}\n\n@-webkit-keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@-webkit-keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n  to {\n    opacity: 0;\n  }\n}\n\n@-webkit-keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@keyframes shake {\n  0% {\n    -webkit-transform: translate(2px, 1px) rotate(0deg);\n    transform: translate(2px, 1px) rotate(0deg);\n  }\n  10% {\n    -webkit-transform: translate(-1px, -2px) rotate(-2deg);\n    transform: translate(-1px, -2px) rotate(-2deg);\n  }\n  20% {\n    -webkit-transform: translate(-3px, 0) rotate(3deg);\n    transform: translate(-3px, 0) rotate(3deg);\n  }\n  30% {\n    -webkit-transform: translate(0, 2px) rotate(0deg);\n    transform: translate(0, 2px) rotate(0deg);\n  }\n  40% {\n    -webkit-transform: translate(1px, -1px) rotate(1deg);\n    transform: translate(1px, -1px) rotate(1deg);\n  }\n  50% {\n    -webkit-transform: translate(-1px, 2px) rotate(-1deg);\n    transform: translate(-1px, 2px) rotate(-1deg);\n  }\n  60% {\n    -webkit-transform: translate(-3px, 1px) rotate(0deg);\n    transform: translate(-3px, 1px) rotate(0deg);\n  }\n  70% {\n    -webkit-transform: translate(2px, 1px) rotate(-2deg);\n    transform: translate(2px, 1px) rotate(-2deg);\n  }\n  80% {\n    -webkit-transform: translate(-1px, -1px) rotate(4deg);\n    transform: translate(-1px, -1px) rotate(4deg);\n  }\n  90% {\n    -webkit-transform: translate(2px, 2px) rotate(0deg);\n    transform: translate(2px, 2px) rotate(0deg);\n  }\n  100% {\n    -webkit-transform: translate(1px, -2px) rotate(-1deg);\n    transform: translate(1px, -2px) rotate(-1deg);\n  }\n}\n\n@-webkit-keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n@keyframes wobble {\n  0% {\n    -webkit-transform: none;\n    transform: none;\n  }\n  15% {\n    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);\n  }\n  30% {\n    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);\n  }\n  45% {\n    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);\n  }\n  60% {\n    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);\n  }\n  75% {\n    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);\n  }\n  100% {\n    -webkit-transform: none;\n    transform: none;\n  }\n}\n\n.fc-button {\n  background: #f8f9fa;\n  background-image: none;\n  border-bottom-color: #ddd;\n  border-color: #ddd;\n  color: #495057;\n}\n\n.fc-button:hover, .fc-button:active, .fc-button.hover {\n  background-color: #e9e9e9;\n}\n\n.fc-header-title h2 {\n  color: #666;\n  font-size: 15px;\n  line-height: 1.6em;\n  margin-left: 10px;\n}\n\n.fc-header-right {\n  padding-right: 10px;\n}\n\n.fc-header-left {\n  padding-left: 10px;\n}\n\n.fc-widget-header {\n  background: #fafafa;\n}\n\n.fc-grid {\n  border: 0;\n  width: 100%;\n}\n\n.fc-widget-header:first-of-type,\n.fc-widget-content:first-of-type {\n  border-left: 0;\n  border-right: 0;\n}\n\n.fc-widget-header:last-of-type,\n.fc-widget-content:last-of-type {\n  border-right: 0;\n}\n\n.fc-toolbar,\n.fc-toolbar.fc-header-toolbar {\n  margin: 0;\n  padding: 1rem;\n}\n\n@media (max-width: 575.98px) {\n  .fc-toolbar {\n    -ms-flex-direction: column;\n    flex-direction: column;\n  }\n  .fc-toolbar .fc-left {\n    -ms-flex-order: 1;\n    order: 1;\n    margin-bottom: .5rem;\n  }\n  .fc-toolbar .fc-center {\n    -ms-flex-order: 0;\n    order: 0;\n    margin-bottom: .375rem;\n  }\n  .fc-toolbar .fc-right {\n    -ms-flex-order: 2;\n    order: 2;\n  }\n}\n\n.fc-day-number {\n  font-size: 20px;\n  font-weight: 300;\n  padding-right: 10px;\n}\n\n.fc-color-picker {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n\n.fc-color-picker > li {\n  float: left;\n  font-size: 30px;\n  line-height: 30px;\n  margin-right: 5px;\n}\n\n.fc-color-picker > li .fa,\n.fc-color-picker > li .fas,\n.fc-color-picker > li .far,\n.fc-color-picker > li .fab,\n.fc-color-picker > li .fal,\n.fc-color-picker > li .fad,\n.fc-color-picker > li .svg-inline--fa,\n.fc-color-picker > li .ion {\n  transition: -webkit-transform linear .3s;\n  transition: transform linear .3s;\n  transition: transform linear .3s, -webkit-transform linear .3s;\n}\n\n.fc-color-picker > li .fa:hover,\n.fc-color-picker > li .fas:hover,\n.fc-color-picker > li .far:hover,\n.fc-color-picker > li .fab:hover,\n.fc-color-picker > li .fal:hover,\n.fc-color-picker > li .fad:hover,\n.fc-color-picker > li .svg-inline--fa:hover,\n.fc-color-picker > li .ion:hover {\n  -webkit-transform: rotate(30deg);\n  transform: rotate(30deg);\n}\n\n#add-new-event {\n  transition: all linear .3s;\n}\n\n.external-event {\n  box-shadow: 0 0 1px rgba(0, 0, 0, 0.125), 0 1px 3px rgba(0, 0, 0, 0.2);\n  border-radius: 0.25rem;\n  cursor: move;\n  font-weight: 700;\n  margin-bottom: 4px;\n  padding: 5px 10px;\n}\n\n.external-event:hover {\n  box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.2);\n}\n\n.select2-container--default .select2-selection--single {\n  border: 1px solid #ced4da;\n  padding: 0.46875rem 0.75rem;\n  height: calc(2.25rem + 2px);\n}\n\n.select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-dropdown {\n  border: 1px solid #ced4da;\n}\n\n.select2-container--default .select2-results__option {\n  padding: 6px 12px;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__rendered {\n  padding-left: 0;\n  height: auto;\n  margin-top: -3px;\n}\n\n.select2-container--default[dir=\"rtl\"] .select2-selection--single .select2-selection__rendered {\n  padding-right: 6px;\n  padding-left: 20px;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__arrow {\n  height: 31px;\n  right: 6px;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__arrow b {\n  margin-top: 0;\n}\n\n.select2-container--default .select2-dropdown .select2-search__field,\n.select2-container--default .select2-search--inline .select2-search__field {\n  border: 1px solid #ced4da;\n}\n\n.select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-search--inline .select2-search__field:focus {\n  outline: none;\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-dropdown.select2-dropdown--below {\n  border-top: 0;\n}\n\n.select2-container--default .select2-dropdown.select2-dropdown--above {\n  border-bottom: 0;\n}\n\n.select2-container--default .select2-results__option[aria-disabled='true'] {\n  color: #6c757d;\n}\n\n.select2-container--default .select2-results__option[aria-selected='true'] {\n  background-color: #dee2e6;\n}\n\n.select2-container--default .select2-results__option[aria-selected='true'], .select2-container--default .select2-results__option[aria-selected='true']:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-selection--multiple {\n  border: 1px solid #ced4da;\n  min-height: calc(2.25rem + 2px);\n}\n\n.select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered {\n  padding: 0 0.375rem 0.375rem;\n  margin-bottom: -0.375rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline {\n  width: 100%;\n  margin-left: 0.375rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline .select2-search__field {\n  width: 100% !important;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field {\n  border: 0;\n  margin-top: 6px;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n  padding: 0 10px;\n  margin-top: .31rem;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n  float: right;\n  margin-left: 5px;\n  margin-right: -2px;\n}\n\n.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-search.select2-search--inline .select2-search__field, .select2-container--default .select2-selection--multiple.text-sm .select2-search.select2-search--inline .select2-search__field {\n  margin-top: 8px;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__choice, .select2-container--default .select2-selection--multiple.text-sm .select2-selection__choice {\n  margin-top: .4rem;\n}\n\n.select2-container--default.select2-container--focus .select2-selection--single,\n.select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-container--default.select2-container--focus .select2-search__field {\n  border: 0;\n}\n\n.select2-container--default .select2-selection--single .select2-selection__rendered li {\n  padding-right: 10px;\n}\n\n.input-group-prepend ~ .select2-container--default .select2-selection {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.input-group > .select2-container--default:not(:last-child) .select2-selection {\n  border-bottom-right-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.select2-container--bootstrap4.select2-container--focus .select2-selection {\n  box-shadow: none;\n}\n\nselect.form-control-sm ~ .select2-container--default {\n  font-size: 75%;\n}\n\n.text-sm .select2-container--default .select2-selection--single,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single {\n  height: calc(1.8125rem + 2px);\n}\n\n.text-sm .select2-container--default .select2-selection--single .select2-selection__rendered,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single .select2-selection__rendered {\n  margin-top: -.4rem;\n}\n\n.text-sm .select2-container--default .select2-selection--single .select2-selection__arrow,\nselect.form-control-sm ~ .select2-container--default .select2-selection--single .select2-selection__arrow {\n  top: -.12rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple {\n  min-height: calc(1.8125rem + 2px);\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered {\n  padding: 0 0.25rem 0.25rem;\n  margin-top: -0.1rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered li:first-child.select2-search.select2-search--inline {\n  margin-left: 0.25rem;\n}\n\n.text-sm .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field,\nselect.form-control-sm ~ .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-search.select2-search--inline .select2-search__field {\n  margin-top: 6px;\n}\n\n.maximized-card .select2-dropdown {\n  z-index: 9999;\n}\n\n.select2-primary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-primary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-primary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-primary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-primary .select2-search--inline .select2-search__field:focus,\n.select2-primary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-primary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-primary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-primary .select2-results__option--highlighted,\n.select2-primary .select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-primary .select2-results__option--highlighted[aria-selected]:hover,\n.select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple:focus,\n.select2-primary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice__remove,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-primary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-primary.select2-container--focus .select2-selection--multiple,\n.select2-primary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-secondary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-secondary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-secondary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-secondary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-secondary .select2-search--inline .select2-search__field:focus,\n.select2-secondary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-secondary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-secondary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .select2-secondary .select2-results__option--highlighted,\n.select2-secondary .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-secondary .select2-results__option--highlighted[aria-selected]:hover,\n.select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple:focus,\n.select2-secondary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice__remove,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-secondary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-secondary.select2-container--focus .select2-selection--multiple,\n.select2-secondary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.select2-success + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-success + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-success.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-success .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-success .select2-search--inline .select2-search__field:focus,\n.select2-success .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-success .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-success .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #71dd8a;\n}\n\n.select2-container--default .select2-success .select2-results__option--highlighted,\n.select2-success .select2-container--default .select2-results__option--highlighted {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-success .select2-results__option--highlighted[aria-selected]:hover,\n.select2-success .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-success .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #259b40;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple:focus,\n.select2-success .select2-container--default .select2-selection--multiple:focus {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #28a745;\n  border-color: #23923d;\n  color: #fff;\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice__remove,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-success .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-success.select2-container--focus .select2-selection--multiple,\n.select2-success .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #71dd8a;\n}\n\n.select2-info + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-info + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-info.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-info .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-info .select2-search--inline .select2-search__field:focus,\n.select2-info .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-info .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-info .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #63d9ec;\n}\n\n.select2-container--default .select2-info .select2-results__option--highlighted,\n.select2-info .select2-container--default .select2-results__option--highlighted {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-info .select2-results__option--highlighted[aria-selected]:hover,\n.select2-info .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-info .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1596aa;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple:focus,\n.select2-info .select2-container--default .select2-selection--multiple:focus {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #17a2b8;\n  border-color: #148ea1;\n  color: #fff;\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice__remove,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-info .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-info.select2-container--focus .select2-selection--multiple,\n.select2-info .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #63d9ec;\n}\n\n.select2-warning + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-warning + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-warning.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-warning .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-warning .select2-search--inline .select2-search__field:focus,\n.select2-warning .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-warning .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-warning .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #ffe187;\n}\n\n.select2-container--default .select2-warning .select2-results__option--highlighted,\n.select2-warning .select2-container--default .select2-results__option--highlighted {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-warning .select2-results__option--highlighted[aria-selected]:hover,\n.select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7b900;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple:focus,\n.select2-warning .select2-container--default .select2-selection--multiple:focus {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ffc107;\n  border-color: #edb100;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice__remove,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-warning .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-warning.select2-container--focus .select2-selection--multiple,\n.select2-warning .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #ffe187;\n}\n\n.select2-danger + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-danger + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-danger.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-danger .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-danger .select2-search--inline .select2-search__field:focus,\n.select2-danger .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-danger .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-danger .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #efa2a9;\n}\n\n.select2-container--default .select2-danger .select2-results__option--highlighted,\n.select2-danger .select2-container--default .select2-results__option--highlighted {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-danger .select2-results__option--highlighted[aria-selected]:hover,\n.select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #da2839;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple:focus,\n.select2-danger .select2-container--default .select2-selection--multiple:focus {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #dc3545;\n  border-color: #d32535;\n  color: #fff;\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice__remove,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-danger .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-danger.select2-container--focus .select2-selection--multiple,\n.select2-danger .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #efa2a9;\n}\n\n.select2-light + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.select2-light + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .select2-light.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-light .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-light .select2-search--inline .select2-search__field:focus,\n.select2-light .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-light .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-light .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .select2-light .select2-results__option--highlighted,\n.select2-light .select2-container--default .select2-results__option--highlighted {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-light .select2-results__option--highlighted[aria-selected]:hover,\n.select2-light .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-light .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #eff1f4;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple:focus,\n.select2-light .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice__remove,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-light .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-light.select2-container--focus .select2-selection--multiple,\n.select2-light .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.select2-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-dark .select2-search--inline .select2-search__field:focus,\n.select2-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .select2-dark .select2-results__option--highlighted,\n.select2-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-dark .select2-results__option--highlighted[aria-selected]:hover,\n.select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple:focus,\n.select2-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice__remove,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-dark.select2-container--focus .select2-selection--multiple,\n.select2-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.select2-lightblue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #99c5de;\n}\n\n.select2-lightblue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #99c5de;\n}\n\n.select2-container--default .select2-lightblue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lightblue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lightblue .select2-search--inline .select2-search__field:focus,\n.select2-lightblue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-lightblue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-lightblue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #99c5de;\n}\n\n.select2-container--default .select2-lightblue .select2-results__option--highlighted,\n.select2-lightblue .select2-container--default .select2-results__option--highlighted {\n  background-color: #3c8dbc;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-lightblue .select2-results__option--highlighted[aria-selected]:hover,\n.select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #3884b0;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple:focus,\n.select2-lightblue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #99c5de;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3c8dbc;\n  border-color: #367fa9;\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-lightblue.select2-container--focus .select2-selection--multiple,\n.select2-lightblue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #99c5de;\n}\n\n.select2-navy + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #005ebf;\n}\n\n.select2-navy + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #005ebf;\n}\n\n.select2-container--default .select2-navy.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-navy .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-navy .select2-search--inline .select2-search__field:focus,\n.select2-navy .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-navy .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-navy .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #005ebf;\n}\n\n.select2-container--default .select2-navy .select2-results__option--highlighted,\n.select2-navy .select2-container--default .select2-results__option--highlighted {\n  background-color: #001f3f;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-navy .select2-results__option--highlighted[aria-selected]:hover,\n.select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #001730;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple:focus,\n.select2-navy .select2-container--default .select2-selection--multiple:focus {\n  border-color: #005ebf;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #001f3f;\n  border-color: #001226;\n  color: #fff;\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice__remove,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-navy .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-navy.select2-container--focus .select2-selection--multiple,\n.select2-navy .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #005ebf;\n}\n\n.select2-olive + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #87cfaf;\n}\n\n.select2-olive + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #87cfaf;\n}\n\n.select2-container--default .select2-olive.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-olive .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-olive .select2-search--inline .select2-search__field:focus,\n.select2-olive .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-olive .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-olive .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #87cfaf;\n}\n\n.select2-container--default .select2-olive .select2-results__option--highlighted,\n.select2-olive .select2-container--default .select2-results__option--highlighted {\n  background-color: #3d9970;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-olive .select2-results__option--highlighted[aria-selected]:hover,\n.select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #398e68;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple:focus,\n.select2-olive .select2-container--default .select2-selection--multiple:focus {\n  border-color: #87cfaf;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3d9970;\n  border-color: #368763;\n  color: #fff;\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice__remove,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-olive .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-olive.select2-container--focus .select2-selection--multiple,\n.select2-olive .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #87cfaf;\n}\n\n.select2-lime + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #81ffb8;\n}\n\n.select2-lime + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #81ffb8;\n}\n\n.select2-container--default .select2-lime.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lime .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-lime .select2-search--inline .select2-search__field:focus,\n.select2-lime .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-lime .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-lime .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #81ffb8;\n}\n\n.select2-container--default .select2-lime .select2-results__option--highlighted,\n.select2-lime .select2-container--default .select2-results__option--highlighted {\n  background-color: #01ff70;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-lime .select2-results__option--highlighted[aria-selected]:hover,\n.select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #00f169;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple:focus,\n.select2-lime .select2-container--default .select2-selection--multiple:focus {\n  border-color: #81ffb8;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #01ff70;\n  border-color: #00e765;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice__remove,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-lime .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-lime.select2-container--focus .select2-selection--multiple,\n.select2-lime .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #81ffb8;\n}\n\n.select2-fuchsia + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f88adf;\n}\n\n.select2-fuchsia + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f88adf;\n}\n\n.select2-container--default .select2-fuchsia.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-fuchsia .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-fuchsia .select2-search--inline .select2-search__field:focus,\n.select2-fuchsia .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-fuchsia .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-fuchsia .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f88adf;\n}\n\n.select2-container--default .select2-fuchsia .select2-results__option--highlighted,\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted {\n  background-color: #f012be;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-fuchsia .select2-results__option--highlighted[aria-selected]:hover,\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e40eb4;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple:focus,\n.select2-fuchsia .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f88adf;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f012be;\n  border-color: #db0ead;\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-fuchsia.select2-container--focus .select2-selection--multiple,\n.select2-fuchsia .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f88adf;\n}\n\n.select2-maroon + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f083ab;\n}\n\n.select2-maroon + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f083ab;\n}\n\n.select2-container--default .select2-maroon.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-maroon .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-maroon .select2-search--inline .select2-search__field:focus,\n.select2-maroon .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-maroon .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-maroon .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f083ab;\n}\n\n.select2-container--default .select2-maroon .select2-results__option--highlighted,\n.select2-maroon .select2-container--default .select2-results__option--highlighted {\n  background-color: #d81b60;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-maroon .select2-results__option--highlighted[aria-selected]:hover,\n.select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #ca195a;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple:focus,\n.select2-maroon .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f083ab;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #d81b60;\n  border-color: #c11856;\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice__remove,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-maroon .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-maroon.select2-container--focus .select2-selection--multiple,\n.select2-maroon .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f083ab;\n}\n\n.select2-blue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-blue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-blue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-blue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-blue .select2-search--inline .select2-search__field:focus,\n.select2-blue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-blue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-blue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #80bdff;\n}\n\n.select2-container--default .select2-blue .select2-results__option--highlighted,\n.select2-blue .select2-container--default .select2-results__option--highlighted {\n  background-color: #007bff;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-blue .select2-results__option--highlighted[aria-selected]:hover,\n.select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #0074f0;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple:focus,\n.select2-blue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #80bdff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #007bff;\n  border-color: #006fe6;\n  color: #fff;\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice__remove,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-blue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-blue.select2-container--focus .select2-selection--multiple,\n.select2-blue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #80bdff;\n}\n\n.select2-indigo + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-indigo + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-container--default .select2-indigo.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-indigo .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-indigo .select2-search--inline .select2-search__field:focus,\n.select2-indigo .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-indigo .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-indigo .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b389f9;\n}\n\n.select2-container--default .select2-indigo .select2-results__option--highlighted,\n.select2-indigo .select2-container--default .select2-results__option--highlighted {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-indigo .select2-results__option--highlighted[aria-selected]:hover,\n.select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #5f0de6;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple:focus,\n.select2-indigo .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b389f9;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6610f2;\n  border-color: #5b0cdd;\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice__remove,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-indigo .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-indigo.select2-container--focus .select2-selection--multiple,\n.select2-indigo .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b389f9;\n}\n\n.select2-purple + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-purple + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .select2-purple.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-purple .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-purple .select2-search--inline .select2-search__field:focus,\n.select2-purple .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-purple .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-purple .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b8a2e0;\n}\n\n.select2-container--default .select2-purple .select2-results__option--highlighted,\n.select2-purple .select2-container--default .select2-results__option--highlighted {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-purple .select2-results__option--highlighted[aria-selected]:hover,\n.select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #683cb8;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple:focus,\n.select2-purple .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6f42c1;\n  border-color: #643ab0;\n  color: #fff;\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice__remove,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-purple .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-purple.select2-container--focus .select2-selection--multiple,\n.select2-purple .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b8a2e0;\n}\n\n.select2-pink + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-pink + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .select2-pink.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-pink .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-pink .select2-search--inline .select2-search__field:focus,\n.select2-pink .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-pink .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-pink .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f6b0d0;\n}\n\n.select2-container--default .select2-pink .select2-results__option--highlighted,\n.select2-pink .select2-container--default .select2-results__option--highlighted {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-pink .select2-results__option--highlighted[aria-selected]:hover,\n.select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e63084;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple:focus,\n.select2-pink .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e83e8c;\n  border-color: #e5277e;\n  color: #fff;\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice__remove,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-pink .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-pink.select2-container--focus .select2-selection--multiple,\n.select2-pink .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f6b0d0;\n}\n\n.select2-red + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-red + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-red.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-red .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-red .select2-search--inline .select2-search__field:focus,\n.select2-red .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-red .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-red .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #efa2a9;\n}\n\n.select2-container--default .select2-red .select2-results__option--highlighted,\n.select2-red .select2-container--default .select2-results__option--highlighted {\n  background-color: #dc3545;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-red .select2-results__option--highlighted[aria-selected]:hover,\n.select2-red .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-red .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #da2839;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple:focus,\n.select2-red .select2-container--default .select2-selection--multiple:focus {\n  border-color: #efa2a9;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #dc3545;\n  border-color: #d32535;\n  color: #fff;\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice__remove,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-red .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-red.select2-container--focus .select2-selection--multiple,\n.select2-red .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #efa2a9;\n}\n\n.select2-orange + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-orange + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-container--default .select2-orange.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-orange .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-orange .select2-search--inline .select2-search__field:focus,\n.select2-orange .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-orange .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-orange .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #fec392;\n}\n\n.select2-container--default .select2-orange .select2-results__option--highlighted,\n.select2-orange .select2-container--default .select2-results__option--highlighted {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-orange .select2-results__option--highlighted[aria-selected]:hover,\n.select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #fd7605;\n  color: #fff;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple:focus,\n.select2-orange .select2-container--default .select2-selection--multiple:focus {\n  border-color: #fec392;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fd7e14;\n  border-color: #f57102;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice__remove,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-orange .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-orange.select2-container--focus .select2-selection--multiple,\n.select2-orange .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #fec392;\n}\n\n.select2-yellow + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-yellow + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-yellow.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-yellow .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-yellow .select2-search--inline .select2-search__field:focus,\n.select2-yellow .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-yellow .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-yellow .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #ffe187;\n}\n\n.select2-container--default .select2-yellow .select2-results__option--highlighted,\n.select2-yellow .select2-container--default .select2-results__option--highlighted {\n  background-color: #ffc107;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-yellow .select2-results__option--highlighted[aria-selected]:hover,\n.select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7b900;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple:focus,\n.select2-yellow .select2-container--default .select2-selection--multiple:focus {\n  border-color: #ffe187;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ffc107;\n  border-color: #edb100;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice__remove,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-yellow .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-yellow.select2-container--focus .select2-selection--multiple,\n.select2-yellow .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #ffe187;\n}\n\n.select2-green + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-green + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-green.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-green .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-green .select2-search--inline .select2-search__field:focus,\n.select2-green .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-green .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-green .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #71dd8a;\n}\n\n.select2-container--default .select2-green .select2-results__option--highlighted,\n.select2-green .select2-container--default .select2-results__option--highlighted {\n  background-color: #28a745;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-green .select2-results__option--highlighted[aria-selected]:hover,\n.select2-green .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-green .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #259b40;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple:focus,\n.select2-green .select2-container--default .select2-selection--multiple:focus {\n  border-color: #71dd8a;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #28a745;\n  border-color: #23923d;\n  color: #fff;\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice__remove,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-green .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-green.select2-container--focus .select2-selection--multiple,\n.select2-green .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #71dd8a;\n}\n\n.select2-teal + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-teal + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .select2-teal.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-teal .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-teal .select2-search--inline .select2-search__field:focus,\n.select2-teal .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-teal .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-teal .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #7eeaca;\n}\n\n.select2-container--default .select2-teal .select2-results__option--highlighted,\n.select2-teal .select2-container--default .select2-results__option--highlighted {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-teal .select2-results__option--highlighted[aria-selected]:hover,\n.select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1ebc8d;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple:focus,\n.select2-teal .select2-container--default .select2-selection--multiple:focus {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #20c997;\n  border-color: #1cb386;\n  color: #fff;\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice__remove,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-teal .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-teal.select2-container--focus .select2-selection--multiple,\n.select2-teal .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #7eeaca;\n}\n\n.select2-cyan + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-cyan + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-cyan.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-cyan .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-cyan .select2-search--inline .select2-search__field:focus,\n.select2-cyan .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-cyan .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-cyan .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #63d9ec;\n}\n\n.select2-container--default .select2-cyan .select2-results__option--highlighted,\n.select2-cyan .select2-container--default .select2-results__option--highlighted {\n  background-color: #17a2b8;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-cyan .select2-results__option--highlighted[aria-selected]:hover,\n.select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1596aa;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple:focus,\n.select2-cyan .select2-container--default .select2-selection--multiple:focus {\n  border-color: #63d9ec;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #17a2b8;\n  border-color: #148ea1;\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice__remove,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-cyan .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-cyan.select2-container--focus .select2-selection--multiple,\n.select2-cyan .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #63d9ec;\n}\n\n.select2-white + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.select2-white + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .select2-white.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-white .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-white .select2-search--inline .select2-search__field:focus,\n.select2-white .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-white .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-white .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .select2-white .select2-results__option--highlighted,\n.select2-white .select2-container--default .select2-results__option--highlighted {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-white .select2-results__option--highlighted[aria-selected]:hover,\n.select2-white .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-white .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7f7f7;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple:focus,\n.select2-white .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fff;\n  border-color: #f2f2f2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice__remove,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .select2-white .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .select2-white.select2-container--focus .select2-selection--multiple,\n.select2-white .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.select2-gray + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-gray + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-gray.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray .select2-search--inline .select2-search__field:focus,\n.select2-gray .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-gray .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-gray .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .select2-gray .select2-results__option--highlighted,\n.select2-gray .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-gray .select2-results__option--highlighted[aria-selected]:hover,\n.select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple:focus,\n.select2-gray .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice__remove,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-gray .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-gray.select2-container--focus .select2-selection--multiple,\n.select2-gray .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.select2-gray-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-gray-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .select2-gray-dark .select2-search--inline .select2-search__field:focus,\n.select2-gray-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.select2-gray-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.select2-gray-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark .select2-results__option--highlighted,\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .select2-gray-dark .select2-results__option--highlighted[aria-selected]:hover,\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple:focus,\n.select2-gray-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .select2-gray-dark.select2-container--focus .select2-selection--multiple,\n.select2-gray-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-selection {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.dark-mode .select2-container--disabled .select2-selection--single {\n  background-color: #454d55;\n}\n\n.dark-mode .select2-selection--single {\n  background-color: #343a40;\n  border-color: #6c757d;\n}\n\n.dark-mode .select2-selection--single .select2-selection__rendered {\n  color: #fff;\n}\n\n.dark-mode .select2-dropdown .select2-search__field,\n.dark-mode .select2-search--inline .select2-search__field {\n  background-color: #343a40;\n  border-color: #6c757d;\n  color: white;\n}\n\n.dark-mode .select2-dropdown {\n  background-color: #343a40;\n  border-color: #6c757d;\n  color: white;\n}\n\n.dark-mode .select2-results__option[aria-selected=\"true\"] {\n  background-color: #3f474e !important;\n  color: #dee2e6;\n}\n\n.dark-mode .select2-container .select2-search--inline .select2-search__field {\n  background-color: transparent;\n  color: #fff;\n}\n\n.dark-mode .select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice {\n  color: #fff;\n}\n\n.dark-mode .select2-primary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-primary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-primary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-primary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-primary .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-primary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-primary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-primary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-results__option--highlighted,\n.dark-mode .select2-primary .select2-container--default .select2-results__option--highlighted {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-primary .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-primary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #3a5f86;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple:focus,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3f6791;\n  border-color: #375a7f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-primary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-primary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-primary.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-primary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-secondary + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-secondary + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-secondary.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-secondary .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-secondary .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-secondary .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-secondary .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-secondary .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-results__option--highlighted,\n.dark-mode .select2-secondary .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-secondary .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-secondary .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple:focus,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-secondary .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-secondary .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-secondary.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-secondary .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-success + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-success + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-success.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-success .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-success .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-success .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-success .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-success .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-results__option--highlighted,\n.dark-mode .select2-success .select2-container--default .select2-results__option--highlighted {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-success .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-success .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-success .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #00ad81;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple:focus,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple:focus {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #00bc8c;\n  border-color: #00a379;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-success .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-success .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-success.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-success .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-info + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-info + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-info.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-info .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-info .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-info .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-info .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-info .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-results__option--highlighted,\n.dark-mode .select2-info .select2-container--default .select2-results__option--highlighted {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-info .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-info .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-info .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2791d9;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple:focus,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple:focus {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3498db;\n  border-color: #258cd1;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-info .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-info .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-info.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-info .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-warning + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-warning + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-warning.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-warning .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-warning .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-warning .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-warning .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-warning .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-results__option--highlighted,\n.dark-mode .select2-warning .select2-container--default .select2-results__option--highlighted {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-warning .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-warning .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #ea940c;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple:focus,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f39c12;\n  border-color: #e08e0b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-warning .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-warning .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-warning.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-warning .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-danger + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-danger + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-danger.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-danger .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-danger .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-danger .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-danger .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-danger .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-results__option--highlighted,\n.dark-mode .select2-danger .select2-container--default .select2-results__option--highlighted {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-danger .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-danger .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e53f2e;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple:focus,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e74c3c;\n  border-color: #e43725;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-danger .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-danger .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-danger.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-danger .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-light + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.dark-mode .select2-light + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-light.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-light .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-light .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-light .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-light .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-light .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-results__option--highlighted,\n.dark-mode .select2-light .select2-container--default .select2-results__option--highlighted {\n  background-color: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-light .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-light .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-light .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #eff1f4;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple:focus,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f8f9fa;\n  border-color: #e9ecef;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-light .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-light .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-light.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-light .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.dark-mode .select2-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-dark .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-results__option--highlighted,\n.dark-mode .select2-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-dark .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple:focus,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-dark.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-lightblue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .select2-lightblue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #e6f1f7;\n}\n\n.select2-container--default .dark-mode .select2-lightblue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lightblue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lightblue .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-lightblue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lightblue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lightblue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #e6f1f7;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-results__option--highlighted,\n.dark-mode .select2-lightblue .select2-container--default .select2-results__option--highlighted {\n  background-color: #86bad8;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-lightblue .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-lightblue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #7ab3d5;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple:focus,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #e6f1f7;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #86bad8;\n  border-color: #72afd2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-lightblue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-lightblue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lightblue.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-lightblue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #e6f1f7;\n}\n\n.dark-mode .select2-navy + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #006ad8;\n}\n\n.dark-mode .select2-navy + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #006ad8;\n}\n\n.select2-container--default .dark-mode .select2-navy.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-navy .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-navy .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-navy .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-navy .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-navy .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #006ad8;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-results__option--highlighted,\n.dark-mode .select2-navy .select2-container--default .select2-results__option--highlighted {\n  background-color: #002c59;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-navy .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-navy .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #002449;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple:focus,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple:focus {\n  border-color: #006ad8;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #002c59;\n  border-color: #001f3f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-navy .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-navy .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-navy.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-navy .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #006ad8;\n}\n\n.dark-mode .select2-olive + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #cfecdf;\n}\n\n.dark-mode .select2-olive + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #cfecdf;\n}\n\n.select2-container--default .dark-mode .select2-olive.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-olive .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-olive .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-olive .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-olive .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-olive .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #cfecdf;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-results__option--highlighted,\n.dark-mode .select2-olive .select2-container--default .select2-results__option--highlighted {\n  background-color: #74c8a3;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-olive .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-olive .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #69c39b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple:focus,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple:focus {\n  border-color: #cfecdf;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #74c8a3;\n  border-color: #62c096;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-olive .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-olive .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-olive.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-olive .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #cfecdf;\n}\n\n.dark-mode .select2-lime + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #e7fff1;\n}\n\n.dark-mode .select2-lime + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #e7fff1;\n}\n\n.select2-container--default .dark-mode .select2-lime.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lime .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-lime .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-lime .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lime .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-lime .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #e7fff1;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-results__option--highlighted,\n.dark-mode .select2-lime .select2-container--default .select2-results__option--highlighted {\n  background-color: #67ffa9;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-lime .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-lime .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #58ffa1;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple:focus,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple:focus {\n  border-color: #e7fff1;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #67ffa9;\n  border-color: #4eff9b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-lime .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-lime .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-lime.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-lime .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #e7fff1;\n}\n\n.dark-mode .select2-fuchsia + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #feeaf9;\n}\n\n.dark-mode .select2-fuchsia + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #feeaf9;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-fuchsia .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-fuchsia .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-fuchsia .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-fuchsia .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-fuchsia .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #feeaf9;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-results__option--highlighted,\n.dark-mode .select2-fuchsia .select2-container--default .select2-results__option--highlighted {\n  background-color: #f672d8;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-fuchsia .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-fuchsia .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f564d4;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple:focus,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple:focus {\n  border-color: #feeaf9;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f672d8;\n  border-color: #f55ad2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-fuchsia .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-fuchsia .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-fuchsia.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-fuchsia .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #feeaf9;\n}\n\n.dark-mode .select2-maroon + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #fbdee8;\n}\n\n.dark-mode .select2-maroon + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #fbdee8;\n}\n\n.select2-container--default .dark-mode .select2-maroon.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-maroon .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-maroon .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-maroon .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-maroon .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-maroon .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #fbdee8;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-results__option--highlighted,\n.dark-mode .select2-maroon .select2-container--default .select2-results__option--highlighted {\n  background-color: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-maroon .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-maroon .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #eb5f92;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple:focus,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple:focus {\n  border-color: #fbdee8;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #ed6c9b;\n  border-color: #ea568c;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-maroon .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-maroon .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-maroon.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-maroon .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #fbdee8;\n}\n\n.dark-mode .select2-blue + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-blue + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-blue.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-blue .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-blue .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-blue .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-blue .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-blue .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-results__option--highlighted,\n.dark-mode .select2-blue .select2-container--default .select2-results__option--highlighted {\n  background-color: #3f6791;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-blue .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-blue .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #3a5f86;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple:focus,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple:focus {\n  border-color: #85a7ca;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3f6791;\n  border-color: #375a7f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-blue .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-blue .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-blue.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-blue .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #85a7ca;\n}\n\n.dark-mode .select2-indigo + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.dark-mode .select2-indigo + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b389f9;\n}\n\n.select2-container--default .dark-mode .select2-indigo.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-indigo .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-indigo .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-indigo .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-indigo .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-indigo .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b389f9;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-results__option--highlighted,\n.dark-mode .select2-indigo .select2-container--default .select2-results__option--highlighted {\n  background-color: #6610f2;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-indigo .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-indigo .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #5f0de6;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple:focus,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b389f9;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6610f2;\n  border-color: #5b0cdd;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-indigo .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-indigo .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-indigo.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-indigo .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b389f9;\n}\n\n.dark-mode .select2-purple + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .select2-purple + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .dark-mode .select2-purple.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-purple .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-purple .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-purple .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-purple .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-purple .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #b8a2e0;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-results__option--highlighted,\n.dark-mode .select2-purple .select2-container--default .select2-results__option--highlighted {\n  background-color: #6f42c1;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-purple .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-purple .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #683cb8;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple:focus,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple:focus {\n  border-color: #b8a2e0;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6f42c1;\n  border-color: #643ab0;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-purple .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-purple .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-purple.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-purple .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #b8a2e0;\n}\n\n.dark-mode .select2-pink + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .select2-pink + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .dark-mode .select2-pink.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-pink .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-pink .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-pink .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-pink .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-pink .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f6b0d0;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-results__option--highlighted,\n.dark-mode .select2-pink .select2-container--default .select2-results__option--highlighted {\n  background-color: #e83e8c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-pink .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-pink .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e63084;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple:focus,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f6b0d0;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e83e8c;\n  border-color: #e5277e;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-pink .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-pink .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-pink.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-pink .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f6b0d0;\n}\n\n.dark-mode .select2-red + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-red + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-red.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-red .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-red .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-red .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-red .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-red .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-results__option--highlighted,\n.dark-mode .select2-red .select2-container--default .select2-results__option--highlighted {\n  background-color: #e74c3c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-red .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-red .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-red .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #e53f2e;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple:focus,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f5b4ae;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #e74c3c;\n  border-color: #e43725;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-red .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-red .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-red.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-red .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f5b4ae;\n}\n\n.dark-mode .select2-orange + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #fec392;\n}\n\n.dark-mode .select2-orange + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #fec392;\n}\n\n.select2-container--default .dark-mode .select2-orange.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-orange .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-orange .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-orange .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-orange .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-orange .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #fec392;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-results__option--highlighted,\n.dark-mode .select2-orange .select2-container--default .select2-results__option--highlighted {\n  background-color: #fd7e14;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-orange .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-orange .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #fd7605;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple:focus,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple:focus {\n  border-color: #fec392;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fd7e14;\n  border-color: #f57102;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-orange .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-orange .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-orange.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-orange .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #fec392;\n}\n\n.dark-mode .select2-yellow + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-yellow + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-yellow.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-yellow .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-yellow .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-yellow .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-yellow .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-yellow .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-results__option--highlighted,\n.dark-mode .select2-yellow .select2-container--default .select2-results__option--highlighted {\n  background-color: #f39c12;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-yellow .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-yellow .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #ea940c;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple:focus,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple:focus {\n  border-color: #f9cf8b;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #f39c12;\n  border-color: #e08e0b;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-yellow .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-yellow .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-yellow.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-yellow .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #f9cf8b;\n}\n\n.dark-mode .select2-green + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-green + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-green.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-green .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-green .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-green .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-green .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-green .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-results__option--highlighted,\n.dark-mode .select2-green .select2-container--default .select2-results__option--highlighted {\n  background-color: #00bc8c;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-green .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-green .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-green .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #00ad81;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple:focus,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple:focus {\n  border-color: #3dffcd;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #00bc8c;\n  border-color: #00a379;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-green .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-green .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-green.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-green .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #3dffcd;\n}\n\n.dark-mode .select2-teal + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.dark-mode .select2-teal + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .dark-mode .select2-teal.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-teal .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-teal .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-teal .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-teal .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-teal .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #7eeaca;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-results__option--highlighted,\n.dark-mode .select2-teal .select2-container--default .select2-results__option--highlighted {\n  background-color: #20c997;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-teal .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-teal .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #1ebc8d;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple:focus,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple:focus {\n  border-color: #7eeaca;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #20c997;\n  border-color: #1cb386;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-teal .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-teal .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-teal.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-teal .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #7eeaca;\n}\n\n.dark-mode .select2-cyan + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-cyan + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-cyan.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-cyan .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-cyan .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-cyan .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-cyan .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-cyan .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-results__option--highlighted,\n.dark-mode .select2-cyan .select2-container--default .select2-results__option--highlighted {\n  background-color: #3498db;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-cyan .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-cyan .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2791d9;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple:focus,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple:focus {\n  border-color: #a0cfee;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #3498db;\n  border-color: #258cd1;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-cyan .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-cyan .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-cyan.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-cyan .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #a0cfee;\n}\n\n.dark-mode .select2-white + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: white;\n}\n\n.dark-mode .select2-white + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-white.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-white .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-white .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-white .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-white .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-white .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid white;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-results__option--highlighted,\n.dark-mode .select2-white .select2-container--default .select2-results__option--highlighted {\n  background-color: #fff;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-white .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-white .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-white .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #f7f7f7;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple:focus,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple:focus {\n  border-color: white;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #fff;\n  border-color: #f2f2f2;\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(31, 45, 61, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-white .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-white .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #1f2d3d;\n}\n\n.select2-container--default .dark-mode .select2-white.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-white .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: white;\n}\n\n.dark-mode .select2-gray + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-gray + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-gray.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-gray .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-results__option--highlighted,\n.dark-mode .select2-gray .select2-container--default .select2-results__option--highlighted {\n  background-color: #6c757d;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-gray .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-gray .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #656d75;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple:focus,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple:focus {\n  border-color: #afb5ba;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #6c757d;\n  border-color: #60686f;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-gray .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-gray .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-gray .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #afb5ba;\n}\n\n.dark-mode .select2-gray-dark + .select2-container--default.select2-container--open .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.dark-mode .select2-gray-dark + .select2-container--default.select2-container--focus .select2-selection--single {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark.select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray-dark .select2-dropdown .select2-search__field:focus,\n.select2-container--default .dark-mode .select2-gray-dark .select2-search--inline .select2-search__field:focus,\n.dark-mode .select2-gray-dark .select2-container--default.select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray-dark .select2-container--default .select2-dropdown .select2-search__field:focus,\n.dark-mode .select2-gray-dark .select2-container--default .select2-search--inline .select2-search__field:focus {\n  border: 1px solid #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-results__option--highlighted,\n.dark-mode .select2-gray-dark .select2-container--default .select2-results__option--highlighted {\n  background-color: #343a40;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-results__option--highlighted[aria-selected], .select2-container--default .dark-mode .select2-gray-dark .select2-results__option--highlighted[aria-selected]:hover,\n.dark-mode .select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected],\n.dark-mode .select2-gray-dark .select2-container--default .select2-results__option--highlighted[aria-selected]:hover {\n  background-color: #2d3238;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple:focus,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple:focus {\n  border-color: #6d7a86;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple .select2-selection__choice,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice {\n  background-color: #343a40;\n  border-color: #292d32;\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n  color: rgba(255, 255, 255, 0.7);\n}\n\n.select2-container--default .dark-mode .select2-gray-dark .select2-selection--multiple .select2-selection__choice__remove:hover,\n.dark-mode .select2-gray-dark .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n  color: #fff;\n}\n\n.select2-container--default .dark-mode .select2-gray-dark.select2-container--focus .select2-selection--multiple,\n.dark-mode .select2-gray-dark .select2-container--default.select2-container--focus .select2-selection--multiple {\n  border-color: #6d7a86;\n}\n\n.slider .tooltip.in {\n  opacity: 0.9;\n}\n\n.slider.slider-vertical {\n  height: 100%;\n}\n\n.slider.slider-horizontal {\n  width: 100%;\n}\n\n.slider-primary .slider .slider-selection {\n  background: #007bff;\n}\n\n.slider-secondary .slider .slider-selection {\n  background: #6c757d;\n}\n\n.slider-success .slider .slider-selection {\n  background: #28a745;\n}\n\n.slider-info .slider .slider-selection {\n  background: #17a2b8;\n}\n\n.slider-warning .slider .slider-selection {\n  background: #ffc107;\n}\n\n.slider-danger .slider .slider-selection {\n  background: #dc3545;\n}\n\n.slider-light .slider .slider-selection {\n  background: #f8f9fa;\n}\n\n.slider-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.slider-lightblue .slider .slider-selection {\n  background: #3c8dbc;\n}\n\n.slider-navy .slider .slider-selection {\n  background: #001f3f;\n}\n\n.slider-olive .slider .slider-selection {\n  background: #3d9970;\n}\n\n.slider-lime .slider .slider-selection {\n  background: #01ff70;\n}\n\n.slider-fuchsia .slider .slider-selection {\n  background: #f012be;\n}\n\n.slider-maroon .slider .slider-selection {\n  background: #d81b60;\n}\n\n.slider-blue .slider .slider-selection {\n  background: #007bff;\n}\n\n.slider-indigo .slider .slider-selection {\n  background: #6610f2;\n}\n\n.slider-purple .slider .slider-selection {\n  background: #6f42c1;\n}\n\n.slider-pink .slider .slider-selection {\n  background: #e83e8c;\n}\n\n.slider-red .slider .slider-selection {\n  background: #dc3545;\n}\n\n.slider-orange .slider .slider-selection {\n  background: #fd7e14;\n}\n\n.slider-yellow .slider .slider-selection {\n  background: #ffc107;\n}\n\n.slider-green .slider .slider-selection {\n  background: #28a745;\n}\n\n.slider-teal .slider .slider-selection {\n  background: #20c997;\n}\n\n.slider-cyan .slider .slider-selection {\n  background: #17a2b8;\n}\n\n.slider-white .slider .slider-selection {\n  background: #fff;\n}\n\n.slider-gray .slider .slider-selection {\n  background: #6c757d;\n}\n\n.slider-gray-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.dark-mode .slider-track {\n  background-color: #4b545c;\n  background-image: none;\n}\n\n.dark-mode .slider-primary .slider .slider-selection {\n  background: #3f6791;\n}\n\n.dark-mode .slider-secondary .slider .slider-selection {\n  background: #6c757d;\n}\n\n.dark-mode .slider-success .slider .slider-selection {\n  background: #00bc8c;\n}\n\n.dark-mode .slider-info .slider .slider-selection {\n  background: #3498db;\n}\n\n.dark-mode .slider-warning .slider .slider-selection {\n  background: #f39c12;\n}\n\n.dark-mode .slider-danger .slider .slider-selection {\n  background: #e74c3c;\n}\n\n.dark-mode .slider-light .slider .slider-selection {\n  background: #f8f9fa;\n}\n\n.dark-mode .slider-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.dark-mode .slider-lightblue .slider .slider-selection {\n  background: #86bad8;\n}\n\n.dark-mode .slider-navy .slider .slider-selection {\n  background: #002c59;\n}\n\n.dark-mode .slider-olive .slider .slider-selection {\n  background: #74c8a3;\n}\n\n.dark-mode .slider-lime .slider .slider-selection {\n  background: #67ffa9;\n}\n\n.dark-mode .slider-fuchsia .slider .slider-selection {\n  background: #f672d8;\n}\n\n.dark-mode .slider-maroon .slider .slider-selection {\n  background: #ed6c9b;\n}\n\n.dark-mode .slider-blue .slider .slider-selection {\n  background: #3f6791;\n}\n\n.dark-mode .slider-indigo .slider .slider-selection {\n  background: #6610f2;\n}\n\n.dark-mode .slider-purple .slider .slider-selection {\n  background: #6f42c1;\n}\n\n.dark-mode .slider-pink .slider .slider-selection {\n  background: #e83e8c;\n}\n\n.dark-mode .slider-red .slider .slider-selection {\n  background: #e74c3c;\n}\n\n.dark-mode .slider-orange .slider .slider-selection {\n  background: #fd7e14;\n}\n\n.dark-mode .slider-yellow .slider .slider-selection {\n  background: #f39c12;\n}\n\n.dark-mode .slider-green .slider .slider-selection {\n  background: #00bc8c;\n}\n\n.dark-mode .slider-teal .slider .slider-selection {\n  background: #20c997;\n}\n\n.dark-mode .slider-cyan .slider .slider-selection {\n  background: #3498db;\n}\n\n.dark-mode .slider-white .slider .slider-selection {\n  background: #fff;\n}\n\n.dark-mode .slider-gray .slider .slider-selection {\n  background: #6c757d;\n}\n\n.dark-mode .slider-gray-dark .slider .slider-selection {\n  background: #343a40;\n}\n\n.icheck-primary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-primary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-primary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-primary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-primary > input:first-child:checked + label::before,\n.icheck-primary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-secondary > input:first-child:checked + label::before,\n.icheck-secondary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.icheck-success > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-success > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-success > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-success > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-success > input:first-child:checked + label::before,\n.icheck-success > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.icheck-info > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-info > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-info > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-info > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-info > input:first-child:checked + label::before,\n.icheck-info > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.icheck-warning > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-warning > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-warning > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-warning > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-warning > input:first-child:checked + label::before,\n.icheck-warning > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.icheck-danger > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-danger > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-danger > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-danger > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-danger > input:first-child:checked + label::before,\n.icheck-danger > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.icheck-light > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-light > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.icheck-light > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-light > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.icheck-light > input:first-child:checked + label::before,\n.icheck-light > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.icheck-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-dark > input:first-child:checked + label::before,\n.icheck-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3c8dbc;\n}\n\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3c8dbc;\n}\n\n.icheck-lightblue > input:first-child:checked + label::before,\n.icheck-lightblue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3c8dbc;\n  border-color: #3c8dbc;\n}\n\n.icheck-navy > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-navy > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #001f3f;\n}\n\n.icheck-navy > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-navy > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #001f3f;\n}\n\n.icheck-navy > input:first-child:checked + label::before,\n.icheck-navy > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #001f3f;\n  border-color: #001f3f;\n}\n\n.icheck-olive > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-olive > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3d9970;\n}\n\n.icheck-olive > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-olive > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3d9970;\n}\n\n.icheck-olive > input:first-child:checked + label::before,\n.icheck-olive > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3d9970;\n  border-color: #3d9970;\n}\n\n.icheck-lime > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-lime > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #01ff70;\n}\n\n.icheck-lime > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-lime > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #01ff70;\n}\n\n.icheck-lime > input:first-child:checked + label::before,\n.icheck-lime > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #01ff70;\n  border-color: #01ff70;\n}\n\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f012be;\n}\n\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f012be;\n}\n\n.icheck-fuchsia > input:first-child:checked + label::before,\n.icheck-fuchsia > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f012be;\n  border-color: #f012be;\n}\n\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #d81b60;\n}\n\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #d81b60;\n}\n\n.icheck-maroon > input:first-child:checked + label::before,\n.icheck-maroon > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #d81b60;\n  border-color: #d81b60;\n}\n\n.icheck-blue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-blue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-blue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-blue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #007bff;\n}\n\n.icheck-blue > input:first-child:checked + label::before,\n.icheck-blue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #007bff;\n  border-color: #007bff;\n}\n\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.icheck-indigo > input:first-child:checked + label::before,\n.icheck-indigo > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6610f2;\n  border-color: #6610f2;\n}\n\n.icheck-purple > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-purple > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.icheck-purple > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-purple > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.icheck-purple > input:first-child:checked + label::before,\n.icheck-purple > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n}\n\n.icheck-pink > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-pink > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.icheck-pink > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-pink > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.icheck-pink > input:first-child:checked + label::before,\n.icheck-pink > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n}\n\n.icheck-red > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-red > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-red > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-red > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #dc3545;\n}\n\n.icheck-red > input:first-child:checked + label::before,\n.icheck-red > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #dc3545;\n  border-color: #dc3545;\n}\n\n.icheck-orange > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-orange > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.icheck-orange > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-orange > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.icheck-orange > input:first-child:checked + label::before,\n.icheck-orange > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n}\n\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ffc107;\n}\n\n.icheck-yellow > input:first-child:checked + label::before,\n.icheck-yellow > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ffc107;\n  border-color: #ffc107;\n}\n\n.icheck-green > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-green > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-green > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-green > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #28a745;\n}\n\n.icheck-green > input:first-child:checked + label::before,\n.icheck-green > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #28a745;\n  border-color: #28a745;\n}\n\n.icheck-teal > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-teal > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.icheck-teal > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-teal > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.icheck-teal > input:first-child:checked + label::before,\n.icheck-teal > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #20c997;\n  border-color: #20c997;\n}\n\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #17a2b8;\n}\n\n.icheck-cyan > input:first-child:checked + label::before,\n.icheck-cyan > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #17a2b8;\n  border-color: #17a2b8;\n}\n\n.icheck-white > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-white > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.icheck-white > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-white > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.icheck-white > input:first-child:checked + label::before,\n.icheck-white > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fff;\n  border-color: #fff;\n}\n\n.icheck-gray > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-gray > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-gray > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-gray > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.icheck-gray > input:first-child:checked + label::before,\n.icheck-gray > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.icheck-gray-dark > input:first-child:checked + label::before,\n.icheck-gray-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.dark-mode [class*=\"icheck-\"] > input:first-child:not(:checked) + input[type=\"hidden\"] + label::before,\n.dark-mode [class*=\"icheck-\"] > input:first-child:not(:checked) + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-primary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-primary > input:first-child:checked + label::before,\n.dark-mode .icheck-primary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-secondary > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-secondary > input:first-child:checked + label::before,\n.dark-mode .icheck-secondary > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-success > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-success > input:first-child:checked + label::before,\n.dark-mode .icheck-success > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-info > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-info > input:first-child:checked + label::before,\n.dark-mode .icheck-info > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-warning > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-warning > input:first-child:checked + label::before,\n.dark-mode .icheck-warning > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-danger > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-danger > input:first-child:checked + label::before,\n.dark-mode .icheck-danger > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-light > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f8f9fa;\n}\n\n.dark-mode .icheck-light > input:first-child:checked + label::before,\n.dark-mode .icheck-light > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f8f9fa;\n  border-color: #f8f9fa;\n}\n\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-dark > input:first-child:checked + label::before,\n.dark-mode .icheck-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #86bad8;\n}\n\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-lightblue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #86bad8;\n}\n\n.dark-mode .icheck-lightblue > input:first-child:checked + label::before,\n.dark-mode .icheck-lightblue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #86bad8;\n  border-color: #86bad8;\n}\n\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #002c59;\n}\n\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-navy > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #002c59;\n}\n\n.dark-mode .icheck-navy > input:first-child:checked + label::before,\n.dark-mode .icheck-navy > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #002c59;\n  border-color: #002c59;\n}\n\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #74c8a3;\n}\n\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-olive > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #74c8a3;\n}\n\n.dark-mode .icheck-olive > input:first-child:checked + label::before,\n.dark-mode .icheck-olive > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #74c8a3;\n  border-color: #74c8a3;\n}\n\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #67ffa9;\n}\n\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-lime > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #67ffa9;\n}\n\n.dark-mode .icheck-lime > input:first-child:checked + label::before,\n.dark-mode .icheck-lime > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #67ffa9;\n  border-color: #67ffa9;\n}\n\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f672d8;\n}\n\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-fuchsia > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f672d8;\n}\n\n.dark-mode .icheck-fuchsia > input:first-child:checked + label::before,\n.dark-mode .icheck-fuchsia > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f672d8;\n  border-color: #f672d8;\n}\n\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #ed6c9b;\n}\n\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-maroon > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #ed6c9b;\n}\n\n.dark-mode .icheck-maroon > input:first-child:checked + label::before,\n.dark-mode .icheck-maroon > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #ed6c9b;\n  border-color: #ed6c9b;\n}\n\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-blue > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-blue > input:first-child:checked + label::before,\n.dark-mode .icheck-blue > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3f6791;\n  border-color: #3f6791;\n}\n\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-indigo > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6610f2;\n}\n\n.dark-mode .icheck-indigo > input:first-child:checked + label::before,\n.dark-mode .icheck-indigo > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6610f2;\n  border-color: #6610f2;\n}\n\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-purple > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6f42c1;\n}\n\n.dark-mode .icheck-purple > input:first-child:checked + label::before,\n.dark-mode .icheck-purple > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6f42c1;\n  border-color: #6f42c1;\n}\n\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-pink > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e83e8c;\n}\n\n.dark-mode .icheck-pink > input:first-child:checked + label::before,\n.dark-mode .icheck-pink > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e83e8c;\n  border-color: #e83e8c;\n}\n\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-red > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-red > input:first-child:checked + label::before,\n.dark-mode .icheck-red > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #e74c3c;\n  border-color: #e74c3c;\n}\n\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-orange > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fd7e14;\n}\n\n.dark-mode .icheck-orange > input:first-child:checked + label::before,\n.dark-mode .icheck-orange > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fd7e14;\n  border-color: #fd7e14;\n}\n\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-yellow > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-yellow > input:first-child:checked + label::before,\n.dark-mode .icheck-yellow > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #f39c12;\n  border-color: #f39c12;\n}\n\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-green > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-green > input:first-child:checked + label::before,\n.dark-mode .icheck-green > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #00bc8c;\n  border-color: #00bc8c;\n}\n\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-teal > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #20c997;\n}\n\n.dark-mode .icheck-teal > input:first-child:checked + label::before,\n.dark-mode .icheck-teal > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #20c997;\n  border-color: #20c997;\n}\n\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-cyan > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-cyan > input:first-child:checked + label::before,\n.dark-mode .icheck-cyan > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #3498db;\n  border-color: #3498db;\n}\n\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-white > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #fff;\n}\n\n.dark-mode .icheck-white > input:first-child:checked + label::before,\n.dark-mode .icheck-white > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #fff;\n  border-color: #fff;\n}\n\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-gray > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-gray > input:first-child:checked + label::before,\n.dark-mode .icheck-gray > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #6c757d;\n  border-color: #6c757d;\n}\n\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + label::before,\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):hover + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + label::before,\n.dark-mode .icheck-gray-dark > input:first-child:not(:checked):not(:disabled):focus + input[type=\"hidden\"] + label::before {\n  border-color: #343a40;\n}\n\n.dark-mode .icheck-gray-dark > input:first-child:checked + label::before,\n.dark-mode .icheck-gray-dark > input:first-child:checked + input[type=\"hidden\"] + label::before {\n  background-color: #343a40;\n  border-color: #343a40;\n}\n\n.mapael .map {\n  position: relative;\n}\n\n.mapael .mapTooltip {\n  font-family: \"Source Sans Pro\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  word-spacing: normal;\n  white-space: normal;\n  line-break: auto;\n  border-radius: 0.25rem;\n  font-size: 0.875rem;\n  background-color: #000;\n  color: #fff;\n  display: block;\n  max-width: 200px;\n  padding: 0.25rem 0.5rem;\n  position: absolute;\n  text-align: center;\n  word-wrap: break-word;\n  z-index: 1070;\n}\n\n.mapael .myLegend {\n  background-color: #f8f9fa;\n  border: 1px solid #adb5bd;\n  padding: 10px;\n  width: 600px;\n}\n\n.mapael .zoomButton {\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  border-radius: 0.25rem;\n  color: #444;\n  cursor: pointer;\n  font-weight: 700;\n  height: 16px;\n  left: 10px;\n  line-height: 14px;\n  padding-left: 1px;\n  position: absolute;\n  text-align: center;\n  top: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  width: 16px;\n}\n\n.mapael .zoomButton:hover, .mapael .zoomButton:active, .mapael .zoomButton.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.mapael .zoomReset {\n  line-height: 12px;\n  top: 10px;\n}\n\n.mapael .zoomIn {\n  top: 30px;\n}\n\n.mapael .zoomOut {\n  top: 50px;\n}\n\n.jqvmap-zoomin,\n.jqvmap-zoomout {\n  background-color: #f8f9fa;\n  border: 1px solid #ddd;\n  border-radius: 0.25rem;\n  color: #444;\n  height: 15px;\n  width: 15px;\n  padding: 1px 2px;\n}\n\n.jqvmap-zoomin:hover, .jqvmap-zoomin:active, .jqvmap-zoomin.hover,\n.jqvmap-zoomout:hover,\n.jqvmap-zoomout:active,\n.jqvmap-zoomout.hover {\n  background-color: #e9ecef;\n  color: #2b2b2b;\n}\n\n.swal2-icon.swal2-info {\n  border-color: ligthen(#17a2b8, 20%);\n  color: #17a2b8;\n}\n\n.swal2-icon.swal2-warning {\n  border-color: ligthen(#ffc107, 20%);\n  color: #ffc107;\n}\n\n.swal2-icon.swal2-error {\n  border-color: ligthen(#dc3545, 20%);\n  color: #dc3545;\n}\n\n.swal2-icon.swal2-question {\n  border-color: ligthen(#6c757d, 20%);\n  color: #6c757d;\n}\n\n.swal2-icon.swal2-success {\n  border-color: ligthen(#28a745, 20%);\n  color: #28a745;\n}\n\n.swal2-icon.swal2-success .swal2-success-ring {\n  border-color: ligthen(#28a745, 20%);\n}\n\n.swal2-icon.swal2-success [class^='swal2-success-line'] {\n  background-color: #28a745;\n}\n\n.dark-mode .swal2-popup {\n  background-color: #343a40;\n  color: #e9ecef;\n}\n\n.dark-mode .swal2-popup .swal2-content,\n.dark-mode .swal2-popup .swal2-title {\n  color: #e9ecef;\n}\n\n#toast-container .toast {\n  background-color: #007bff;\n}\n\n#toast-container .toast-success {\n  background-color: #28a745;\n}\n\n#toast-container .toast-error {\n  background-color: #dc3545;\n}\n\n#toast-container .toast-info {\n  background-color: #17a2b8;\n}\n\n#toast-container .toast-warning {\n  background-color: #ffc107;\n}\n\n.toast-bottom-full-width .toast,\n.toast-top-full-width .toast {\n  max-width: inherit;\n}\n\n.pace {\n  z-index: 1048;\n}\n\n.pace .pace-progress {\n  z-index: 1049;\n}\n\n.pace .pace-activity {\n  z-index: 1050;\n}\n\n.pace-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-primary .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-primary .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-primary .pace .pace-progress::after {\n  color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-bounce-primary .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-center-atom-primary .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-primary .pace-progress::before {\n  background: #007bff;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-primary .pace-activity {\n  border-color: #007bff;\n}\n\n.pace-center-atom-primary .pace-activity::after, .pace-center-atom-primary .pace-activity::before {\n  border-color: #007bff;\n}\n\n.pace-center-circle-primary .pace .pace-progress {\n  background: rgba(0, 123, 255, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-primary .pace .pace-activity {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-radar-primary .pace .pace-activity::before {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-simple-primary .pace {\n  background: #fff;\n  border-color: #007bff;\n}\n\n.pace-center-simple-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-material-primary .pace {\n  color: #007bff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::after,\n.pace-corner-indicator-primary .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::before {\n  border-right-color: rgba(0, 123, 255, 0.2);\n  border-left-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-corner-indicator-primary .pace .pace-activity::after {\n  border-top-color: rgba(0, 123, 255, 0.2);\n  border-bottom-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-fill-left-primary .pace .pace-progress {\n  background-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-flash-primary .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-flash-primary .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #007bff, 0 0 5px #007bff;\n}\n\n.pace-flash-primary .pace .pace-activity {\n  border-top-color: #007bff;\n  border-left-color: #007bff;\n}\n\n.pace-loading-bar-primary .pace .pace-progress {\n  background: #007bff;\n  color: #007bff;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-primary .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #007bff, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-primary .pace .pace-progress {\n  background-color: #007bff;\n  box-shadow: inset -1px 0 #007bff, inset 0 -1px #007bff, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-primary .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-primary .pace-progress {\n  color: #007bff;\n}\n\n.pace-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-secondary .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-secondary .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-secondary .pace .pace-progress::after {\n  color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-bounce-secondary .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-center-atom-secondary .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-secondary .pace-progress::before {\n  background: #6c757d;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-secondary .pace-activity {\n  border-color: #6c757d;\n}\n\n.pace-center-atom-secondary .pace-activity::after, .pace-center-atom-secondary .pace-activity::before {\n  border-color: #6c757d;\n}\n\n.pace-center-circle-secondary .pace .pace-progress {\n  background: rgba(108, 117, 125, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-secondary .pace .pace-activity {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-radar-secondary .pace .pace-activity::before {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-simple-secondary .pace {\n  background: #fff;\n  border-color: #6c757d;\n}\n\n.pace-center-simple-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-material-secondary .pace {\n  color: #6c757d;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::after,\n.pace-corner-indicator-secondary .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::before {\n  border-right-color: rgba(108, 117, 125, 0.2);\n  border-left-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-corner-indicator-secondary .pace .pace-activity::after {\n  border-top-color: rgba(108, 117, 125, 0.2);\n  border-bottom-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-fill-left-secondary .pace .pace-progress {\n  background-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-flash-secondary .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-flash-secondary .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6c757d, 0 0 5px #6c757d;\n}\n\n.pace-flash-secondary .pace .pace-activity {\n  border-top-color: #6c757d;\n  border-left-color: #6c757d;\n}\n\n.pace-loading-bar-secondary .pace .pace-progress {\n  background: #6c757d;\n  color: #6c757d;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-secondary .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6c757d, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-secondary .pace .pace-progress {\n  background-color: #6c757d;\n  box-shadow: inset -1px 0 #6c757d, inset 0 -1px #6c757d, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-secondary .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-secondary .pace-progress {\n  color: #6c757d;\n}\n\n.pace-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-success .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-success .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-success .pace .pace-progress::after {\n  color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-bounce-success .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-center-atom-success .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-success .pace-progress::before {\n  background: #28a745;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-success .pace-activity {\n  border-color: #28a745;\n}\n\n.pace-center-atom-success .pace-activity::after, .pace-center-atom-success .pace-activity::before {\n  border-color: #28a745;\n}\n\n.pace-center-circle-success .pace .pace-progress {\n  background: rgba(40, 167, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-success .pace .pace-activity {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-radar-success .pace .pace-activity::before {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-simple-success .pace {\n  background: #fff;\n  border-color: #28a745;\n}\n\n.pace-center-simple-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-material-success .pace {\n  color: #28a745;\n}\n\n.pace-corner-indicator-success .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-corner-indicator-success .pace .pace-activity::after,\n.pace-corner-indicator-success .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-success .pace .pace-activity::before {\n  border-right-color: rgba(40, 167, 69, 0.2);\n  border-left-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-corner-indicator-success .pace .pace-activity::after {\n  border-top-color: rgba(40, 167, 69, 0.2);\n  border-bottom-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-fill-left-success .pace .pace-progress {\n  background-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-flash-success .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-flash-success .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #28a745, 0 0 5px #28a745;\n}\n\n.pace-flash-success .pace .pace-activity {\n  border-top-color: #28a745;\n  border-left-color: #28a745;\n}\n\n.pace-loading-bar-success .pace .pace-progress {\n  background: #28a745;\n  color: #28a745;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-success .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #28a745, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-success .pace .pace-progress {\n  background-color: #28a745;\n  box-shadow: inset -1px 0 #28a745, inset 0 -1px #28a745, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-success .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-success .pace-progress {\n  color: #28a745;\n}\n\n.pace-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-info .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-info .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-info .pace .pace-progress::after {\n  color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-bounce-info .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-center-atom-info .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-info .pace-progress::before {\n  background: #17a2b8;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-info .pace-activity {\n  border-color: #17a2b8;\n}\n\n.pace-center-atom-info .pace-activity::after, .pace-center-atom-info .pace-activity::before {\n  border-color: #17a2b8;\n}\n\n.pace-center-circle-info .pace .pace-progress {\n  background: rgba(23, 162, 184, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-info .pace .pace-activity {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-radar-info .pace .pace-activity::before {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-simple-info .pace {\n  background: #fff;\n  border-color: #17a2b8;\n}\n\n.pace-center-simple-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-material-info .pace {\n  color: #17a2b8;\n}\n\n.pace-corner-indicator-info .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-corner-indicator-info .pace .pace-activity::after,\n.pace-corner-indicator-info .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-info .pace .pace-activity::before {\n  border-right-color: rgba(23, 162, 184, 0.2);\n  border-left-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-corner-indicator-info .pace .pace-activity::after {\n  border-top-color: rgba(23, 162, 184, 0.2);\n  border-bottom-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-fill-left-info .pace .pace-progress {\n  background-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-flash-info .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-flash-info .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #17a2b8, 0 0 5px #17a2b8;\n}\n\n.pace-flash-info .pace .pace-activity {\n  border-top-color: #17a2b8;\n  border-left-color: #17a2b8;\n}\n\n.pace-loading-bar-info .pace .pace-progress {\n  background: #17a2b8;\n  color: #17a2b8;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-info .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #17a2b8, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-info .pace .pace-progress {\n  background-color: #17a2b8;\n  box-shadow: inset -1px 0 #17a2b8, inset 0 -1px #17a2b8, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-info .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-info .pace-progress {\n  color: #17a2b8;\n}\n\n.pace-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-warning .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-warning .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-warning .pace .pace-progress::after {\n  color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-bounce-warning .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-center-atom-warning .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-warning .pace-progress::before {\n  background: #ffc107;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-warning .pace-activity {\n  border-color: #ffc107;\n}\n\n.pace-center-atom-warning .pace-activity::after, .pace-center-atom-warning .pace-activity::before {\n  border-color: #ffc107;\n}\n\n.pace-center-circle-warning .pace .pace-progress {\n  background: rgba(255, 193, 7, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-warning .pace .pace-activity {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-radar-warning .pace .pace-activity::before {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-simple-warning .pace {\n  background: #1f2d3d;\n  border-color: #ffc107;\n}\n\n.pace-center-simple-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-material-warning .pace {\n  color: #ffc107;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::after,\n.pace-corner-indicator-warning .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::before {\n  border-right-color: rgba(255, 193, 7, 0.2);\n  border-left-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-corner-indicator-warning .pace .pace-activity::after {\n  border-top-color: rgba(255, 193, 7, 0.2);\n  border-bottom-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-fill-left-warning .pace .pace-progress {\n  background-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-flash-warning .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-flash-warning .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #ffc107, 0 0 5px #ffc107;\n}\n\n.pace-flash-warning .pace .pace-activity {\n  border-top-color: #ffc107;\n  border-left-color: #ffc107;\n}\n\n.pace-loading-bar-warning .pace .pace-progress {\n  background: #ffc107;\n  color: #ffc107;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-warning .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #ffc107, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-warning .pace .pace-progress {\n  background-color: #ffc107;\n  box-shadow: inset -1px 0 #ffc107, inset 0 -1px #ffc107, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-warning .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-warning .pace-progress {\n  color: #ffc107;\n}\n\n.pace-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-danger .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-danger .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-danger .pace .pace-progress::after {\n  color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-bounce-danger .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-center-atom-danger .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-danger .pace-progress::before {\n  background: #dc3545;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-danger .pace-activity {\n  border-color: #dc3545;\n}\n\n.pace-center-atom-danger .pace-activity::after, .pace-center-atom-danger .pace-activity::before {\n  border-color: #dc3545;\n}\n\n.pace-center-circle-danger .pace .pace-progress {\n  background: rgba(220, 53, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-danger .pace .pace-activity {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-radar-danger .pace .pace-activity::before {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-simple-danger .pace {\n  background: #fff;\n  border-color: #dc3545;\n}\n\n.pace-center-simple-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-material-danger .pace {\n  color: #dc3545;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::after,\n.pace-corner-indicator-danger .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::before {\n  border-right-color: rgba(220, 53, 69, 0.2);\n  border-left-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-corner-indicator-danger .pace .pace-activity::after {\n  border-top-color: rgba(220, 53, 69, 0.2);\n  border-bottom-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-fill-left-danger .pace .pace-progress {\n  background-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-flash-danger .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-flash-danger .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #dc3545, 0 0 5px #dc3545;\n}\n\n.pace-flash-danger .pace .pace-activity {\n  border-top-color: #dc3545;\n  border-left-color: #dc3545;\n}\n\n.pace-loading-bar-danger .pace .pace-progress {\n  background: #dc3545;\n  color: #dc3545;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-danger .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #dc3545, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-danger .pace .pace-progress {\n  background-color: #dc3545;\n  box-shadow: inset -1px 0 #dc3545, inset 0 -1px #dc3545, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-danger .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-danger .pace-progress {\n  color: #dc3545;\n}\n\n.pace-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-barber-shop-light .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-barber-shop-light .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-light .pace .pace-progress::after {\n  color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-bounce-light .pace .pace-activity {\n  background: #f8f9fa;\n}\n\n.pace-center-atom-light .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-light .pace-progress::before {\n  background: #f8f9fa;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-light .pace-activity {\n  border-color: #f8f9fa;\n}\n\n.pace-center-atom-light .pace-activity::after, .pace-center-atom-light .pace-activity::before {\n  border-color: #f8f9fa;\n}\n\n.pace-center-circle-light .pace .pace-progress {\n  background: rgba(248, 249, 250, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-light .pace .pace-activity {\n  border-color: #f8f9fa transparent transparent;\n}\n\n.pace-center-radar-light .pace .pace-activity::before {\n  border-color: #f8f9fa transparent transparent;\n}\n\n.pace-center-simple-light .pace {\n  background: #1f2d3d;\n  border-color: #f8f9fa;\n}\n\n.pace-center-simple-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-material-light .pace {\n  color: #f8f9fa;\n}\n\n.pace-corner-indicator-light .pace .pace-activity {\n  background: #f8f9fa;\n}\n\n.pace-corner-indicator-light .pace .pace-activity::after,\n.pace-corner-indicator-light .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-light .pace .pace-activity::before {\n  border-right-color: rgba(248, 249, 250, 0.2);\n  border-left-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-corner-indicator-light .pace .pace-activity::after {\n  border-top-color: rgba(248, 249, 250, 0.2);\n  border-bottom-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-fill-left-light .pace .pace-progress {\n  background-color: rgba(248, 249, 250, 0.2);\n}\n\n.pace-flash-light .pace .pace-progress {\n  background: #f8f9fa;\n}\n\n.pace-flash-light .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #f8f9fa, 0 0 5px #f8f9fa;\n}\n\n.pace-flash-light .pace .pace-activity {\n  border-top-color: #f8f9fa;\n  border-left-color: #f8f9fa;\n}\n\n.pace-loading-bar-light .pace .pace-progress {\n  background: #f8f9fa;\n  color: #f8f9fa;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-light .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #f8f9fa, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-light .pace .pace-progress {\n  background-color: #f8f9fa;\n  box-shadow: inset -1px 0 #f8f9fa, inset 0 -1px #f8f9fa, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-light .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-light .pace-progress {\n  color: #f8f9fa;\n}\n\n.pace-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-dark .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-dark .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-dark .pace .pace-progress::after {\n  color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-bounce-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-center-atom-dark .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-dark .pace-progress::before {\n  background: #343a40;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-dark .pace-activity {\n  border-color: #343a40;\n}\n\n.pace-center-atom-dark .pace-activity::after, .pace-center-atom-dark .pace-activity::before {\n  border-color: #343a40;\n}\n\n.pace-center-circle-dark .pace .pace-progress {\n  background: rgba(52, 58, 64, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-dark .pace .pace-activity {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-radar-dark .pace .pace-activity::before {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-simple-dark .pace {\n  background: #fff;\n  border-color: #343a40;\n}\n\n.pace-center-simple-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-material-dark .pace {\n  color: #343a40;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::after,\n.pace-corner-indicator-dark .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::before {\n  border-right-color: rgba(52, 58, 64, 0.2);\n  border-left-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-corner-indicator-dark .pace .pace-activity::after {\n  border-top-color: rgba(52, 58, 64, 0.2);\n  border-bottom-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-fill-left-dark .pace .pace-progress {\n  background-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-flash-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-flash-dark .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #343a40, 0 0 5px #343a40;\n}\n\n.pace-flash-dark .pace .pace-activity {\n  border-top-color: #343a40;\n  border-left-color: #343a40;\n}\n\n.pace-loading-bar-dark .pace .pace-progress {\n  background: #343a40;\n  color: #343a40;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-dark .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #343a40, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-dark .pace .pace-progress {\n  background-color: #343a40;\n  box-shadow: inset -1px 0 #343a40, inset 0 -1px #343a40, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-dark .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-dark .pace-progress {\n  color: #343a40;\n}\n\n.pace-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-barber-shop-lightblue .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-barber-shop-lightblue .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-lightblue .pace .pace-progress::after {\n  color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-bounce-lightblue .pace .pace-activity {\n  background: #3c8dbc;\n}\n\n.pace-center-atom-lightblue .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-lightblue .pace-progress::before {\n  background: #3c8dbc;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-lightblue .pace-activity {\n  border-color: #3c8dbc;\n}\n\n.pace-center-atom-lightblue .pace-activity::after, .pace-center-atom-lightblue .pace-activity::before {\n  border-color: #3c8dbc;\n}\n\n.pace-center-circle-lightblue .pace .pace-progress {\n  background: rgba(60, 141, 188, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-lightblue .pace .pace-activity {\n  border-color: #3c8dbc transparent transparent;\n}\n\n.pace-center-radar-lightblue .pace .pace-activity::before {\n  border-color: #3c8dbc transparent transparent;\n}\n\n.pace-center-simple-lightblue .pace {\n  background: #fff;\n  border-color: #3c8dbc;\n}\n\n.pace-center-simple-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-material-lightblue .pace {\n  color: #3c8dbc;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity {\n  background: #3c8dbc;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::after,\n.pace-corner-indicator-lightblue .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::before {\n  border-right-color: rgba(60, 141, 188, 0.2);\n  border-left-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-corner-indicator-lightblue .pace .pace-activity::after {\n  border-top-color: rgba(60, 141, 188, 0.2);\n  border-bottom-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-fill-left-lightblue .pace .pace-progress {\n  background-color: rgba(60, 141, 188, 0.2);\n}\n\n.pace-flash-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n}\n\n.pace-flash-lightblue .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #3c8dbc, 0 0 5px #3c8dbc;\n}\n\n.pace-flash-lightblue .pace .pace-activity {\n  border-top-color: #3c8dbc;\n  border-left-color: #3c8dbc;\n}\n\n.pace-loading-bar-lightblue .pace .pace-progress {\n  background: #3c8dbc;\n  color: #3c8dbc;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-lightblue .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #3c8dbc, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-lightblue .pace .pace-progress {\n  background-color: #3c8dbc;\n  box-shadow: inset -1px 0 #3c8dbc, inset 0 -1px #3c8dbc, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-lightblue .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-lightblue .pace-progress {\n  color: #3c8dbc;\n}\n\n.pace-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-barber-shop-navy .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-barber-shop-navy .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-navy .pace .pace-progress::after {\n  color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-bounce-navy .pace .pace-activity {\n  background: #001f3f;\n}\n\n.pace-center-atom-navy .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-navy .pace-progress::before {\n  background: #001f3f;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-navy .pace-activity {\n  border-color: #001f3f;\n}\n\n.pace-center-atom-navy .pace-activity::after, .pace-center-atom-navy .pace-activity::before {\n  border-color: #001f3f;\n}\n\n.pace-center-circle-navy .pace .pace-progress {\n  background: rgba(0, 31, 63, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-navy .pace .pace-activity {\n  border-color: #001f3f transparent transparent;\n}\n\n.pace-center-radar-navy .pace .pace-activity::before {\n  border-color: #001f3f transparent transparent;\n}\n\n.pace-center-simple-navy .pace {\n  background: #fff;\n  border-color: #001f3f;\n}\n\n.pace-center-simple-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-material-navy .pace {\n  color: #001f3f;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity {\n  background: #001f3f;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::after,\n.pace-corner-indicator-navy .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::before {\n  border-right-color: rgba(0, 31, 63, 0.2);\n  border-left-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-corner-indicator-navy .pace .pace-activity::after {\n  border-top-color: rgba(0, 31, 63, 0.2);\n  border-bottom-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-fill-left-navy .pace .pace-progress {\n  background-color: rgba(0, 31, 63, 0.2);\n}\n\n.pace-flash-navy .pace .pace-progress {\n  background: #001f3f;\n}\n\n.pace-flash-navy .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #001f3f, 0 0 5px #001f3f;\n}\n\n.pace-flash-navy .pace .pace-activity {\n  border-top-color: #001f3f;\n  border-left-color: #001f3f;\n}\n\n.pace-loading-bar-navy .pace .pace-progress {\n  background: #001f3f;\n  color: #001f3f;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-navy .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #001f3f, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-navy .pace .pace-progress {\n  background-color: #001f3f;\n  box-shadow: inset -1px 0 #001f3f, inset 0 -1px #001f3f, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-navy .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-navy .pace-progress {\n  color: #001f3f;\n}\n\n.pace-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-barber-shop-olive .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-barber-shop-olive .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-olive .pace .pace-progress::after {\n  color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-bounce-olive .pace .pace-activity {\n  background: #3d9970;\n}\n\n.pace-center-atom-olive .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-olive .pace-progress::before {\n  background: #3d9970;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-olive .pace-activity {\n  border-color: #3d9970;\n}\n\n.pace-center-atom-olive .pace-activity::after, .pace-center-atom-olive .pace-activity::before {\n  border-color: #3d9970;\n}\n\n.pace-center-circle-olive .pace .pace-progress {\n  background: rgba(61, 153, 112, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-olive .pace .pace-activity {\n  border-color: #3d9970 transparent transparent;\n}\n\n.pace-center-radar-olive .pace .pace-activity::before {\n  border-color: #3d9970 transparent transparent;\n}\n\n.pace-center-simple-olive .pace {\n  background: #fff;\n  border-color: #3d9970;\n}\n\n.pace-center-simple-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-material-olive .pace {\n  color: #3d9970;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity {\n  background: #3d9970;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::after,\n.pace-corner-indicator-olive .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::before {\n  border-right-color: rgba(61, 153, 112, 0.2);\n  border-left-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-corner-indicator-olive .pace .pace-activity::after {\n  border-top-color: rgba(61, 153, 112, 0.2);\n  border-bottom-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-fill-left-olive .pace .pace-progress {\n  background-color: rgba(61, 153, 112, 0.2);\n}\n\n.pace-flash-olive .pace .pace-progress {\n  background: #3d9970;\n}\n\n.pace-flash-olive .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #3d9970, 0 0 5px #3d9970;\n}\n\n.pace-flash-olive .pace .pace-activity {\n  border-top-color: #3d9970;\n  border-left-color: #3d9970;\n}\n\n.pace-loading-bar-olive .pace .pace-progress {\n  background: #3d9970;\n  color: #3d9970;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-olive .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #3d9970, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-olive .pace .pace-progress {\n  background-color: #3d9970;\n  box-shadow: inset -1px 0 #3d9970, inset 0 -1px #3d9970, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-olive .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-olive .pace-progress {\n  color: #3d9970;\n}\n\n.pace-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-barber-shop-lime .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-barber-shop-lime .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-lime .pace .pace-progress::after {\n  color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-bounce-lime .pace .pace-activity {\n  background: #01ff70;\n}\n\n.pace-center-atom-lime .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-lime .pace-progress::before {\n  background: #01ff70;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-lime .pace-activity {\n  border-color: #01ff70;\n}\n\n.pace-center-atom-lime .pace-activity::after, .pace-center-atom-lime .pace-activity::before {\n  border-color: #01ff70;\n}\n\n.pace-center-circle-lime .pace .pace-progress {\n  background: rgba(1, 255, 112, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-lime .pace .pace-activity {\n  border-color: #01ff70 transparent transparent;\n}\n\n.pace-center-radar-lime .pace .pace-activity::before {\n  border-color: #01ff70 transparent transparent;\n}\n\n.pace-center-simple-lime .pace {\n  background: #1f2d3d;\n  border-color: #01ff70;\n}\n\n.pace-center-simple-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-material-lime .pace {\n  color: #01ff70;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity {\n  background: #01ff70;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::after,\n.pace-corner-indicator-lime .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::before {\n  border-right-color: rgba(1, 255, 112, 0.2);\n  border-left-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-corner-indicator-lime .pace .pace-activity::after {\n  border-top-color: rgba(1, 255, 112, 0.2);\n  border-bottom-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-fill-left-lime .pace .pace-progress {\n  background-color: rgba(1, 255, 112, 0.2);\n}\n\n.pace-flash-lime .pace .pace-progress {\n  background: #01ff70;\n}\n\n.pace-flash-lime .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #01ff70, 0 0 5px #01ff70;\n}\n\n.pace-flash-lime .pace .pace-activity {\n  border-top-color: #01ff70;\n  border-left-color: #01ff70;\n}\n\n.pace-loading-bar-lime .pace .pace-progress {\n  background: #01ff70;\n  color: #01ff70;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-lime .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #01ff70, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-lime .pace .pace-progress {\n  background-color: #01ff70;\n  box-shadow: inset -1px 0 #01ff70, inset 0 -1px #01ff70, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-lime .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-lime .pace-progress {\n  color: #01ff70;\n}\n\n.pace-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-barber-shop-fuchsia .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-barber-shop-fuchsia .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-fuchsia .pace .pace-progress::after {\n  color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-bounce-fuchsia .pace .pace-activity {\n  background: #f012be;\n}\n\n.pace-center-atom-fuchsia .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-fuchsia .pace-progress::before {\n  background: #f012be;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-fuchsia .pace-activity {\n  border-color: #f012be;\n}\n\n.pace-center-atom-fuchsia .pace-activity::after, .pace-center-atom-fuchsia .pace-activity::before {\n  border-color: #f012be;\n}\n\n.pace-center-circle-fuchsia .pace .pace-progress {\n  background: rgba(240, 18, 190, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-fuchsia .pace .pace-activity {\n  border-color: #f012be transparent transparent;\n}\n\n.pace-center-radar-fuchsia .pace .pace-activity::before {\n  border-color: #f012be transparent transparent;\n}\n\n.pace-center-simple-fuchsia .pace {\n  background: #fff;\n  border-color: #f012be;\n}\n\n.pace-center-simple-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-material-fuchsia .pace {\n  color: #f012be;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity {\n  background: #f012be;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::after,\n.pace-corner-indicator-fuchsia .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::before {\n  border-right-color: rgba(240, 18, 190, 0.2);\n  border-left-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-corner-indicator-fuchsia .pace .pace-activity::after {\n  border-top-color: rgba(240, 18, 190, 0.2);\n  border-bottom-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-fill-left-fuchsia .pace .pace-progress {\n  background-color: rgba(240, 18, 190, 0.2);\n}\n\n.pace-flash-fuchsia .pace .pace-progress {\n  background: #f012be;\n}\n\n.pace-flash-fuchsia .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #f012be, 0 0 5px #f012be;\n}\n\n.pace-flash-fuchsia .pace .pace-activity {\n  border-top-color: #f012be;\n  border-left-color: #f012be;\n}\n\n.pace-loading-bar-fuchsia .pace .pace-progress {\n  background: #f012be;\n  color: #f012be;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-fuchsia .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #f012be, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-fuchsia .pace .pace-progress {\n  background-color: #f012be;\n  box-shadow: inset -1px 0 #f012be, inset 0 -1px #f012be, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-fuchsia .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-fuchsia .pace-progress {\n  color: #f012be;\n}\n\n.pace-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-barber-shop-maroon .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-barber-shop-maroon .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-maroon .pace .pace-progress::after {\n  color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-bounce-maroon .pace .pace-activity {\n  background: #d81b60;\n}\n\n.pace-center-atom-maroon .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-maroon .pace-progress::before {\n  background: #d81b60;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-maroon .pace-activity {\n  border-color: #d81b60;\n}\n\n.pace-center-atom-maroon .pace-activity::after, .pace-center-atom-maroon .pace-activity::before {\n  border-color: #d81b60;\n}\n\n.pace-center-circle-maroon .pace .pace-progress {\n  background: rgba(216, 27, 96, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-maroon .pace .pace-activity {\n  border-color: #d81b60 transparent transparent;\n}\n\n.pace-center-radar-maroon .pace .pace-activity::before {\n  border-color: #d81b60 transparent transparent;\n}\n\n.pace-center-simple-maroon .pace {\n  background: #fff;\n  border-color: #d81b60;\n}\n\n.pace-center-simple-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-material-maroon .pace {\n  color: #d81b60;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity {\n  background: #d81b60;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::after,\n.pace-corner-indicator-maroon .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::before {\n  border-right-color: rgba(216, 27, 96, 0.2);\n  border-left-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-corner-indicator-maroon .pace .pace-activity::after {\n  border-top-color: rgba(216, 27, 96, 0.2);\n  border-bottom-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-fill-left-maroon .pace .pace-progress {\n  background-color: rgba(216, 27, 96, 0.2);\n}\n\n.pace-flash-maroon .pace .pace-progress {\n  background: #d81b60;\n}\n\n.pace-flash-maroon .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #d81b60, 0 0 5px #d81b60;\n}\n\n.pace-flash-maroon .pace .pace-activity {\n  border-top-color: #d81b60;\n  border-left-color: #d81b60;\n}\n\n.pace-loading-bar-maroon .pace .pace-progress {\n  background: #d81b60;\n  color: #d81b60;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-maroon .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #d81b60, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-maroon .pace .pace-progress {\n  background-color: #d81b60;\n  box-shadow: inset -1px 0 #d81b60, inset 0 -1px #d81b60, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-maroon .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-maroon .pace-progress {\n  color: #d81b60;\n}\n\n.pace-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-blue .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-barber-shop-blue .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-blue .pace .pace-progress::after {\n  color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-bounce-blue .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-center-atom-blue .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-blue .pace-progress::before {\n  background: #007bff;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-blue .pace-activity {\n  border-color: #007bff;\n}\n\n.pace-center-atom-blue .pace-activity::after, .pace-center-atom-blue .pace-activity::before {\n  border-color: #007bff;\n}\n\n.pace-center-circle-blue .pace .pace-progress {\n  background: rgba(0, 123, 255, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-blue .pace .pace-activity {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-radar-blue .pace .pace-activity::before {\n  border-color: #007bff transparent transparent;\n}\n\n.pace-center-simple-blue .pace {\n  background: #fff;\n  border-color: #007bff;\n}\n\n.pace-center-simple-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-material-blue .pace {\n  color: #007bff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity {\n  background: #007bff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::after,\n.pace-corner-indicator-blue .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::before {\n  border-right-color: rgba(0, 123, 255, 0.2);\n  border-left-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-corner-indicator-blue .pace .pace-activity::after {\n  border-top-color: rgba(0, 123, 255, 0.2);\n  border-bottom-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-fill-left-blue .pace .pace-progress {\n  background-color: rgba(0, 123, 255, 0.2);\n}\n\n.pace-flash-blue .pace .pace-progress {\n  background: #007bff;\n}\n\n.pace-flash-blue .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #007bff, 0 0 5px #007bff;\n}\n\n.pace-flash-blue .pace .pace-activity {\n  border-top-color: #007bff;\n  border-left-color: #007bff;\n}\n\n.pace-loading-bar-blue .pace .pace-progress {\n  background: #007bff;\n  color: #007bff;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-blue .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #007bff, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-blue .pace .pace-progress {\n  background-color: #007bff;\n  box-shadow: inset -1px 0 #007bff, inset 0 -1px #007bff, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-blue .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-blue .pace-progress {\n  color: #007bff;\n}\n\n.pace-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-barber-shop-indigo .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-barber-shop-indigo .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-indigo .pace .pace-progress::after {\n  color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-bounce-indigo .pace .pace-activity {\n  background: #6610f2;\n}\n\n.pace-center-atom-indigo .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-indigo .pace-progress::before {\n  background: #6610f2;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-indigo .pace-activity {\n  border-color: #6610f2;\n}\n\n.pace-center-atom-indigo .pace-activity::after, .pace-center-atom-indigo .pace-activity::before {\n  border-color: #6610f2;\n}\n\n.pace-center-circle-indigo .pace .pace-progress {\n  background: rgba(102, 16, 242, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-indigo .pace .pace-activity {\n  border-color: #6610f2 transparent transparent;\n}\n\n.pace-center-radar-indigo .pace .pace-activity::before {\n  border-color: #6610f2 transparent transparent;\n}\n\n.pace-center-simple-indigo .pace {\n  background: #fff;\n  border-color: #6610f2;\n}\n\n.pace-center-simple-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-material-indigo .pace {\n  color: #6610f2;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity {\n  background: #6610f2;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::after,\n.pace-corner-indicator-indigo .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::before {\n  border-right-color: rgba(102, 16, 242, 0.2);\n  border-left-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-corner-indicator-indigo .pace .pace-activity::after {\n  border-top-color: rgba(102, 16, 242, 0.2);\n  border-bottom-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-fill-left-indigo .pace .pace-progress {\n  background-color: rgba(102, 16, 242, 0.2);\n}\n\n.pace-flash-indigo .pace .pace-progress {\n  background: #6610f2;\n}\n\n.pace-flash-indigo .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6610f2, 0 0 5px #6610f2;\n}\n\n.pace-flash-indigo .pace .pace-activity {\n  border-top-color: #6610f2;\n  border-left-color: #6610f2;\n}\n\n.pace-loading-bar-indigo .pace .pace-progress {\n  background: #6610f2;\n  color: #6610f2;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-indigo .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6610f2, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-indigo .pace .pace-progress {\n  background-color: #6610f2;\n  box-shadow: inset -1px 0 #6610f2, inset 0 -1px #6610f2, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-indigo .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-indigo .pace-progress {\n  color: #6610f2;\n}\n\n.pace-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-barber-shop-purple .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-barber-shop-purple .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-purple .pace .pace-progress::after {\n  color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-bounce-purple .pace .pace-activity {\n  background: #6f42c1;\n}\n\n.pace-center-atom-purple .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-purple .pace-progress::before {\n  background: #6f42c1;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-purple .pace-activity {\n  border-color: #6f42c1;\n}\n\n.pace-center-atom-purple .pace-activity::after, .pace-center-atom-purple .pace-activity::before {\n  border-color: #6f42c1;\n}\n\n.pace-center-circle-purple .pace .pace-progress {\n  background: rgba(111, 66, 193, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-purple .pace .pace-activity {\n  border-color: #6f42c1 transparent transparent;\n}\n\n.pace-center-radar-purple .pace .pace-activity::before {\n  border-color: #6f42c1 transparent transparent;\n}\n\n.pace-center-simple-purple .pace {\n  background: #fff;\n  border-color: #6f42c1;\n}\n\n.pace-center-simple-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-material-purple .pace {\n  color: #6f42c1;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity {\n  background: #6f42c1;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::after,\n.pace-corner-indicator-purple .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::before {\n  border-right-color: rgba(111, 66, 193, 0.2);\n  border-left-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-corner-indicator-purple .pace .pace-activity::after {\n  border-top-color: rgba(111, 66, 193, 0.2);\n  border-bottom-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-fill-left-purple .pace .pace-progress {\n  background-color: rgba(111, 66, 193, 0.2);\n}\n\n.pace-flash-purple .pace .pace-progress {\n  background: #6f42c1;\n}\n\n.pace-flash-purple .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6f42c1, 0 0 5px #6f42c1;\n}\n\n.pace-flash-purple .pace .pace-activity {\n  border-top-color: #6f42c1;\n  border-left-color: #6f42c1;\n}\n\n.pace-loading-bar-purple .pace .pace-progress {\n  background: #6f42c1;\n  color: #6f42c1;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-purple .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6f42c1, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-purple .pace .pace-progress {\n  background-color: #6f42c1;\n  box-shadow: inset -1px 0 #6f42c1, inset 0 -1px #6f42c1, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-purple .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-purple .pace-progress {\n  color: #6f42c1;\n}\n\n.pace-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-barber-shop-pink .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-barber-shop-pink .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-pink .pace .pace-progress::after {\n  color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-bounce-pink .pace .pace-activity {\n  background: #e83e8c;\n}\n\n.pace-center-atom-pink .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-pink .pace-progress::before {\n  background: #e83e8c;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-pink .pace-activity {\n  border-color: #e83e8c;\n}\n\n.pace-center-atom-pink .pace-activity::after, .pace-center-atom-pink .pace-activity::before {\n  border-color: #e83e8c;\n}\n\n.pace-center-circle-pink .pace .pace-progress {\n  background: rgba(232, 62, 140, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-pink .pace .pace-activity {\n  border-color: #e83e8c transparent transparent;\n}\n\n.pace-center-radar-pink .pace .pace-activity::before {\n  border-color: #e83e8c transparent transparent;\n}\n\n.pace-center-simple-pink .pace {\n  background: #fff;\n  border-color: #e83e8c;\n}\n\n.pace-center-simple-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-material-pink .pace {\n  color: #e83e8c;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity {\n  background: #e83e8c;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::after,\n.pace-corner-indicator-pink .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::before {\n  border-right-color: rgba(232, 62, 140, 0.2);\n  border-left-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-corner-indicator-pink .pace .pace-activity::after {\n  border-top-color: rgba(232, 62, 140, 0.2);\n  border-bottom-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-fill-left-pink .pace .pace-progress {\n  background-color: rgba(232, 62, 140, 0.2);\n}\n\n.pace-flash-pink .pace .pace-progress {\n  background: #e83e8c;\n}\n\n.pace-flash-pink .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #e83e8c, 0 0 5px #e83e8c;\n}\n\n.pace-flash-pink .pace .pace-activity {\n  border-top-color: #e83e8c;\n  border-left-color: #e83e8c;\n}\n\n.pace-loading-bar-pink .pace .pace-progress {\n  background: #e83e8c;\n  color: #e83e8c;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-pink .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #e83e8c, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-pink .pace .pace-progress {\n  background-color: #e83e8c;\n  box-shadow: inset -1px 0 #e83e8c, inset 0 -1px #e83e8c, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-pink .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-pink .pace-progress {\n  color: #e83e8c;\n}\n\n.pace-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-red .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-barber-shop-red .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-red .pace .pace-progress::after {\n  color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-bounce-red .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-center-atom-red .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-red .pace-progress::before {\n  background: #dc3545;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-red .pace-activity {\n  border-color: #dc3545;\n}\n\n.pace-center-atom-red .pace-activity::after, .pace-center-atom-red .pace-activity::before {\n  border-color: #dc3545;\n}\n\n.pace-center-circle-red .pace .pace-progress {\n  background: rgba(220, 53, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-red .pace .pace-activity {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-radar-red .pace .pace-activity::before {\n  border-color: #dc3545 transparent transparent;\n}\n\n.pace-center-simple-red .pace {\n  background: #fff;\n  border-color: #dc3545;\n}\n\n.pace-center-simple-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-material-red .pace {\n  color: #dc3545;\n}\n\n.pace-corner-indicator-red .pace .pace-activity {\n  background: #dc3545;\n}\n\n.pace-corner-indicator-red .pace .pace-activity::after,\n.pace-corner-indicator-red .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-red .pace .pace-activity::before {\n  border-right-color: rgba(220, 53, 69, 0.2);\n  border-left-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-corner-indicator-red .pace .pace-activity::after {\n  border-top-color: rgba(220, 53, 69, 0.2);\n  border-bottom-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-fill-left-red .pace .pace-progress {\n  background-color: rgba(220, 53, 69, 0.2);\n}\n\n.pace-flash-red .pace .pace-progress {\n  background: #dc3545;\n}\n\n.pace-flash-red .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #dc3545, 0 0 5px #dc3545;\n}\n\n.pace-flash-red .pace .pace-activity {\n  border-top-color: #dc3545;\n  border-left-color: #dc3545;\n}\n\n.pace-loading-bar-red .pace .pace-progress {\n  background: #dc3545;\n  color: #dc3545;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-red .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #dc3545, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-red .pace .pace-progress {\n  background-color: #dc3545;\n  box-shadow: inset -1px 0 #dc3545, inset 0 -1px #dc3545, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-red .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-red .pace-progress {\n  color: #dc3545;\n}\n\n.pace-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-barber-shop-orange .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-barber-shop-orange .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-orange .pace .pace-progress::after {\n  color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-bounce-orange .pace .pace-activity {\n  background: #fd7e14;\n}\n\n.pace-center-atom-orange .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-orange .pace-progress::before {\n  background: #fd7e14;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-orange .pace-activity {\n  border-color: #fd7e14;\n}\n\n.pace-center-atom-orange .pace-activity::after, .pace-center-atom-orange .pace-activity::before {\n  border-color: #fd7e14;\n}\n\n.pace-center-circle-orange .pace .pace-progress {\n  background: rgba(253, 126, 20, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-orange .pace .pace-activity {\n  border-color: #fd7e14 transparent transparent;\n}\n\n.pace-center-radar-orange .pace .pace-activity::before {\n  border-color: #fd7e14 transparent transparent;\n}\n\n.pace-center-simple-orange .pace {\n  background: #1f2d3d;\n  border-color: #fd7e14;\n}\n\n.pace-center-simple-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-material-orange .pace {\n  color: #fd7e14;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity {\n  background: #fd7e14;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::after,\n.pace-corner-indicator-orange .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::before {\n  border-right-color: rgba(253, 126, 20, 0.2);\n  border-left-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-corner-indicator-orange .pace .pace-activity::after {\n  border-top-color: rgba(253, 126, 20, 0.2);\n  border-bottom-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-fill-left-orange .pace .pace-progress {\n  background-color: rgba(253, 126, 20, 0.2);\n}\n\n.pace-flash-orange .pace .pace-progress {\n  background: #fd7e14;\n}\n\n.pace-flash-orange .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #fd7e14, 0 0 5px #fd7e14;\n}\n\n.pace-flash-orange .pace .pace-activity {\n  border-top-color: #fd7e14;\n  border-left-color: #fd7e14;\n}\n\n.pace-loading-bar-orange .pace .pace-progress {\n  background: #fd7e14;\n  color: #fd7e14;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-orange .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #fd7e14, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-orange .pace .pace-progress {\n  background-color: #fd7e14;\n  box-shadow: inset -1px 0 #fd7e14, inset 0 -1px #fd7e14, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-orange .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-orange .pace-progress {\n  color: #fd7e14;\n}\n\n.pace-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-yellow .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-barber-shop-yellow .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-yellow .pace .pace-progress::after {\n  color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-bounce-yellow .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-center-atom-yellow .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-yellow .pace-progress::before {\n  background: #ffc107;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-yellow .pace-activity {\n  border-color: #ffc107;\n}\n\n.pace-center-atom-yellow .pace-activity::after, .pace-center-atom-yellow .pace-activity::before {\n  border-color: #ffc107;\n}\n\n.pace-center-circle-yellow .pace .pace-progress {\n  background: rgba(255, 193, 7, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-yellow .pace .pace-activity {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-radar-yellow .pace .pace-activity::before {\n  border-color: #ffc107 transparent transparent;\n}\n\n.pace-center-simple-yellow .pace {\n  background: #1f2d3d;\n  border-color: #ffc107;\n}\n\n.pace-center-simple-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-material-yellow .pace {\n  color: #ffc107;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity {\n  background: #ffc107;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::after,\n.pace-corner-indicator-yellow .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::before {\n  border-right-color: rgba(255, 193, 7, 0.2);\n  border-left-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-corner-indicator-yellow .pace .pace-activity::after {\n  border-top-color: rgba(255, 193, 7, 0.2);\n  border-bottom-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-fill-left-yellow .pace .pace-progress {\n  background-color: rgba(255, 193, 7, 0.2);\n}\n\n.pace-flash-yellow .pace .pace-progress {\n  background: #ffc107;\n}\n\n.pace-flash-yellow .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #ffc107, 0 0 5px #ffc107;\n}\n\n.pace-flash-yellow .pace .pace-activity {\n  border-top-color: #ffc107;\n  border-left-color: #ffc107;\n}\n\n.pace-loading-bar-yellow .pace .pace-progress {\n  background: #ffc107;\n  color: #ffc107;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-yellow .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #ffc107, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-yellow .pace .pace-progress {\n  background-color: #ffc107;\n  box-shadow: inset -1px 0 #ffc107, inset 0 -1px #ffc107, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-yellow .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-yellow .pace-progress {\n  color: #ffc107;\n}\n\n.pace-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-green .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-barber-shop-green .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-green .pace .pace-progress::after {\n  color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-bounce-green .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-center-atom-green .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-green .pace-progress::before {\n  background: #28a745;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-green .pace-activity {\n  border-color: #28a745;\n}\n\n.pace-center-atom-green .pace-activity::after, .pace-center-atom-green .pace-activity::before {\n  border-color: #28a745;\n}\n\n.pace-center-circle-green .pace .pace-progress {\n  background: rgba(40, 167, 69, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-green .pace .pace-activity {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-radar-green .pace .pace-activity::before {\n  border-color: #28a745 transparent transparent;\n}\n\n.pace-center-simple-green .pace {\n  background: #fff;\n  border-color: #28a745;\n}\n\n.pace-center-simple-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-material-green .pace {\n  color: #28a745;\n}\n\n.pace-corner-indicator-green .pace .pace-activity {\n  background: #28a745;\n}\n\n.pace-corner-indicator-green .pace .pace-activity::after,\n.pace-corner-indicator-green .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-green .pace .pace-activity::before {\n  border-right-color: rgba(40, 167, 69, 0.2);\n  border-left-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-corner-indicator-green .pace .pace-activity::after {\n  border-top-color: rgba(40, 167, 69, 0.2);\n  border-bottom-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-fill-left-green .pace .pace-progress {\n  background-color: rgba(40, 167, 69, 0.2);\n}\n\n.pace-flash-green .pace .pace-progress {\n  background: #28a745;\n}\n\n.pace-flash-green .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #28a745, 0 0 5px #28a745;\n}\n\n.pace-flash-green .pace .pace-activity {\n  border-top-color: #28a745;\n  border-left-color: #28a745;\n}\n\n.pace-loading-bar-green .pace .pace-progress {\n  background: #28a745;\n  color: #28a745;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-green .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #28a745, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-green .pace .pace-progress {\n  background-color: #28a745;\n  box-shadow: inset -1px 0 #28a745, inset 0 -1px #28a745, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-green .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-green .pace-progress {\n  color: #28a745;\n}\n\n.pace-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-barber-shop-teal .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-barber-shop-teal .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-teal .pace .pace-progress::after {\n  color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-bounce-teal .pace .pace-activity {\n  background: #20c997;\n}\n\n.pace-center-atom-teal .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-teal .pace-progress::before {\n  background: #20c997;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-teal .pace-activity {\n  border-color: #20c997;\n}\n\n.pace-center-atom-teal .pace-activity::after, .pace-center-atom-teal .pace-activity::before {\n  border-color: #20c997;\n}\n\n.pace-center-circle-teal .pace .pace-progress {\n  background: rgba(32, 201, 151, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-teal .pace .pace-activity {\n  border-color: #20c997 transparent transparent;\n}\n\n.pace-center-radar-teal .pace .pace-activity::before {\n  border-color: #20c997 transparent transparent;\n}\n\n.pace-center-simple-teal .pace {\n  background: #fff;\n  border-color: #20c997;\n}\n\n.pace-center-simple-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-material-teal .pace {\n  color: #20c997;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity {\n  background: #20c997;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::after,\n.pace-corner-indicator-teal .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::before {\n  border-right-color: rgba(32, 201, 151, 0.2);\n  border-left-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-corner-indicator-teal .pace .pace-activity::after {\n  border-top-color: rgba(32, 201, 151, 0.2);\n  border-bottom-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-fill-left-teal .pace .pace-progress {\n  background-color: rgba(32, 201, 151, 0.2);\n}\n\n.pace-flash-teal .pace .pace-progress {\n  background: #20c997;\n}\n\n.pace-flash-teal .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #20c997, 0 0 5px #20c997;\n}\n\n.pace-flash-teal .pace .pace-activity {\n  border-top-color: #20c997;\n  border-left-color: #20c997;\n}\n\n.pace-loading-bar-teal .pace .pace-progress {\n  background: #20c997;\n  color: #20c997;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-teal .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #20c997, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-teal .pace .pace-progress {\n  background-color: #20c997;\n  box-shadow: inset -1px 0 #20c997, inset 0 -1px #20c997, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-teal .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-teal .pace-progress {\n  color: #20c997;\n}\n\n.pace-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-cyan .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-barber-shop-cyan .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-cyan .pace .pace-progress::after {\n  color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-bounce-cyan .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-center-atom-cyan .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-cyan .pace-progress::before {\n  background: #17a2b8;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-cyan .pace-activity {\n  border-color: #17a2b8;\n}\n\n.pace-center-atom-cyan .pace-activity::after, .pace-center-atom-cyan .pace-activity::before {\n  border-color: #17a2b8;\n}\n\n.pace-center-circle-cyan .pace .pace-progress {\n  background: rgba(23, 162, 184, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-cyan .pace .pace-activity {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-radar-cyan .pace .pace-activity::before {\n  border-color: #17a2b8 transparent transparent;\n}\n\n.pace-center-simple-cyan .pace {\n  background: #fff;\n  border-color: #17a2b8;\n}\n\n.pace-center-simple-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-material-cyan .pace {\n  color: #17a2b8;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity {\n  background: #17a2b8;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::after,\n.pace-corner-indicator-cyan .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::before {\n  border-right-color: rgba(23, 162, 184, 0.2);\n  border-left-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-corner-indicator-cyan .pace .pace-activity::after {\n  border-top-color: rgba(23, 162, 184, 0.2);\n  border-bottom-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-fill-left-cyan .pace .pace-progress {\n  background-color: rgba(23, 162, 184, 0.2);\n}\n\n.pace-flash-cyan .pace .pace-progress {\n  background: #17a2b8;\n}\n\n.pace-flash-cyan .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #17a2b8, 0 0 5px #17a2b8;\n}\n\n.pace-flash-cyan .pace .pace-activity {\n  border-top-color: #17a2b8;\n  border-left-color: #17a2b8;\n}\n\n.pace-loading-bar-cyan .pace .pace-progress {\n  background: #17a2b8;\n  color: #17a2b8;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-cyan .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #17a2b8, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-cyan .pace .pace-progress {\n  background-color: #17a2b8;\n  box-shadow: inset -1px 0 #17a2b8, inset 0 -1px #17a2b8, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-cyan .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-cyan .pace-progress {\n  color: #17a2b8;\n}\n\n.pace-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-barber-shop-white .pace {\n  background: #1f2d3d;\n}\n\n.pace-barber-shop-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-barber-shop-white .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(31, 45, 61, 0.2) 25%, transparent 25%, transparent 50%, rgba(31, 45, 61, 0.2) 50%, rgba(31, 45, 61, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-white .pace .pace-progress::after {\n  color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-bounce-white .pace .pace-activity {\n  background: #fff;\n}\n\n.pace-center-atom-white .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-white .pace-progress::before {\n  background: #fff;\n  color: #1f2d3d;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-white .pace-activity {\n  border-color: #fff;\n}\n\n.pace-center-atom-white .pace-activity::after, .pace-center-atom-white .pace-activity::before {\n  border-color: #fff;\n}\n\n.pace-center-circle-white .pace .pace-progress {\n  background: rgba(255, 255, 255, 0.8);\n  color: #1f2d3d;\n}\n\n.pace-center-radar-white .pace .pace-activity {\n  border-color: #fff transparent transparent;\n}\n\n.pace-center-radar-white .pace .pace-activity::before {\n  border-color: #fff transparent transparent;\n}\n\n.pace-center-simple-white .pace {\n  background: #1f2d3d;\n  border-color: #fff;\n}\n\n.pace-center-simple-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-material-white .pace {\n  color: #fff;\n}\n\n.pace-corner-indicator-white .pace .pace-activity {\n  background: #fff;\n}\n\n.pace-corner-indicator-white .pace .pace-activity::after,\n.pace-corner-indicator-white .pace .pace-activity::before {\n  border: 5px solid #1f2d3d;\n}\n\n.pace-corner-indicator-white .pace .pace-activity::before {\n  border-right-color: rgba(255, 255, 255, 0.2);\n  border-left-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-corner-indicator-white .pace .pace-activity::after {\n  border-top-color: rgba(255, 255, 255, 0.2);\n  border-bottom-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-fill-left-white .pace .pace-progress {\n  background-color: rgba(255, 255, 255, 0.2);\n}\n\n.pace-flash-white .pace .pace-progress {\n  background: #fff;\n}\n\n.pace-flash-white .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #fff, 0 0 5px #fff;\n}\n\n.pace-flash-white .pace .pace-activity {\n  border-top-color: #fff;\n  border-left-color: #fff;\n}\n\n.pace-loading-bar-white .pace .pace-progress {\n  background: #fff;\n  color: #fff;\n  box-shadow: 120px 0 #1f2d3d, 240px 0 #1f2d3d;\n}\n\n.pace-loading-bar-white .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #fff, inset 0 0 0 7px #1f2d3d;\n}\n\n.pace-mac-osx-white .pace .pace-progress {\n  background-color: #fff;\n  box-shadow: inset -1px 0 #fff, inset 0 -1px #fff, inset 0 2px rgba(31, 45, 61, 0.5), inset 0 6px rgba(31, 45, 61, 0.3);\n}\n\n.pace-mac-osx-white .pace .pace-activity {\n  background-image: radial-gradient(rgba(31, 45, 61, 0.65) 0%, rgba(31, 45, 61, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-white .pace-progress {\n  color: #fff;\n}\n\n.pace-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-gray .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-barber-shop-gray .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-gray .pace .pace-progress::after {\n  color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-bounce-gray .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-center-atom-gray .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-gray .pace-progress::before {\n  background: #6c757d;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-gray .pace-activity {\n  border-color: #6c757d;\n}\n\n.pace-center-atom-gray .pace-activity::after, .pace-center-atom-gray .pace-activity::before {\n  border-color: #6c757d;\n}\n\n.pace-center-circle-gray .pace .pace-progress {\n  background: rgba(108, 117, 125, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-gray .pace .pace-activity {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-radar-gray .pace .pace-activity::before {\n  border-color: #6c757d transparent transparent;\n}\n\n.pace-center-simple-gray .pace {\n  background: #fff;\n  border-color: #6c757d;\n}\n\n.pace-center-simple-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-material-gray .pace {\n  color: #6c757d;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity {\n  background: #6c757d;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::after,\n.pace-corner-indicator-gray .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::before {\n  border-right-color: rgba(108, 117, 125, 0.2);\n  border-left-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-corner-indicator-gray .pace .pace-activity::after {\n  border-top-color: rgba(108, 117, 125, 0.2);\n  border-bottom-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-fill-left-gray .pace .pace-progress {\n  background-color: rgba(108, 117, 125, 0.2);\n}\n\n.pace-flash-gray .pace .pace-progress {\n  background: #6c757d;\n}\n\n.pace-flash-gray .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #6c757d, 0 0 5px #6c757d;\n}\n\n.pace-flash-gray .pace .pace-activity {\n  border-top-color: #6c757d;\n  border-left-color: #6c757d;\n}\n\n.pace-loading-bar-gray .pace .pace-progress {\n  background: #6c757d;\n  color: #6c757d;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-gray .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #6c757d, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-gray .pace .pace-progress {\n  background-color: #6c757d;\n  box-shadow: inset -1px 0 #6c757d, inset 0 -1px #6c757d, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-gray .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-gray .pace-progress {\n  color: #6c757d;\n}\n\n.pace-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-gray-dark .pace {\n  background: #fff;\n}\n\n.pace-barber-shop-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-barber-shop-gray-dark .pace .pace-activity {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);\n}\n\n.pace-big-counter-gray-dark .pace .pace-progress::after {\n  color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-bounce-gray-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-center-atom-gray-dark .pace-progress {\n  height: 100px;\n  width: 80px;\n}\n\n.pace-center-atom-gray-dark .pace-progress::before {\n  background: #343a40;\n  color: #fff;\n  font-size: .8rem;\n  line-height: .7rem;\n  padding-top: 17%;\n}\n\n.pace-center-atom-gray-dark .pace-activity {\n  border-color: #343a40;\n}\n\n.pace-center-atom-gray-dark .pace-activity::after, .pace-center-atom-gray-dark .pace-activity::before {\n  border-color: #343a40;\n}\n\n.pace-center-circle-gray-dark .pace .pace-progress {\n  background: rgba(52, 58, 64, 0.8);\n  color: #fff;\n}\n\n.pace-center-radar-gray-dark .pace .pace-activity {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-radar-gray-dark .pace .pace-activity::before {\n  border-color: #343a40 transparent transparent;\n}\n\n.pace-center-simple-gray-dark .pace {\n  background: #fff;\n  border-color: #343a40;\n}\n\n.pace-center-simple-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-material-gray-dark .pace {\n  color: #343a40;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity {\n  background: #343a40;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::after,\n.pace-corner-indicator-gray-dark .pace .pace-activity::before {\n  border: 5px solid #fff;\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::before {\n  border-right-color: rgba(52, 58, 64, 0.2);\n  border-left-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-corner-indicator-gray-dark .pace .pace-activity::after {\n  border-top-color: rgba(52, 58, 64, 0.2);\n  border-bottom-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-fill-left-gray-dark .pace .pace-progress {\n  background-color: rgba(52, 58, 64, 0.2);\n}\n\n.pace-flash-gray-dark .pace .pace-progress {\n  background: #343a40;\n}\n\n.pace-flash-gray-dark .pace .pace-progress-inner {\n  box-shadow: 0 0 10px #343a40, 0 0 5px #343a40;\n}\n\n.pace-flash-gray-dark .pace .pace-activity {\n  border-top-color: #343a40;\n  border-left-color: #343a40;\n}\n\n.pace-loading-bar-gray-dark .pace .pace-progress {\n  background: #343a40;\n  color: #343a40;\n  box-shadow: 120px 0 #fff, 240px 0 #fff;\n}\n\n.pace-loading-bar-gray-dark .pace .pace-activity {\n  box-shadow: inset 0 0 0 2px #343a40, inset 0 0 0 7px #fff;\n}\n\n.pace-mac-osx-gray-dark .pace .pace-progress {\n  background-color: #343a40;\n  box-shadow: inset -1px 0 #343a40, inset 0 -1px #343a40, inset 0 2px rgba(255, 255, 255, 0.5), inset 0 6px rgba(255, 255, 255, 0.3);\n}\n\n.pace-mac-osx-gray-dark .pace .pace-activity {\n  background-image: radial-gradient(rgba(255, 255, 255, 0.65) 0%, rgba(255, 255, 255, 0.15) 100%);\n  height: 12px;\n}\n\n.pace-progress-color-gray-dark .pace-progress {\n  color: #343a40;\n}\n\n/**\n  * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches.\n  *\n  * @version v3.4 (MODDED)\n  * @homepage https://bttstrp.github.io/bootstrap-switch\n  * @author Mattia Larentis <mattia@larentis.eu> (http://larentis.eu)\n  * @license MIT\n  */\n.bootstrap-switch {\n  border: 1px solid #ced4da;\n  border-radius: 0.25rem;\n  cursor: pointer;\n  direction: ltr;\n  display: inline-block;\n  line-height: .5rem;\n  overflow: hidden;\n  position: relative;\n  text-align: left;\n  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  vertical-align: middle;\n  z-index: 0;\n}\n\n.bootstrap-switch .bootstrap-switch-container {\n  border-radius: 0.25rem;\n  display: inline-block;\n  top: 0;\n  -webkit-transform: translate3d(0, 0, 0);\n  transform: translate3d(0, 0, 0);\n}\n\n.bootstrap-switch:focus-within {\n  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on,\n.bootstrap-switch .bootstrap-switch-handle-off,\n.bootstrap-switch .bootstrap-switch-label {\n  box-sizing: border-box;\n  cursor: pointer;\n  display: table-cell;\n  font-size: 1rem;\n  font-weight: 500;\n  line-height: 1.2rem;\n  padding: .25rem .5rem;\n  vertical-align: middle;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on,\n.bootstrap-switch .bootstrap-switch-handle-off {\n  text-align: center;\n  z-index: 1;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default {\n  background: #e9ecef;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary {\n  background: #007bff;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-secondary,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-secondary {\n  background: #6c757d;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {\n  background: #28a745;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info {\n  background: #17a2b8;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning {\n  background: #ffc107;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger {\n  background: #dc3545;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-light,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-light {\n  background: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-dark,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lightblue,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lightblue {\n  background: #3c8dbc;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-navy,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-navy {\n  background: #001f3f;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-olive,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-olive {\n  background: #3d9970;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lime,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lime {\n  background: #01ff70;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-fuchsia,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-fuchsia {\n  background: #f012be;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-maroon,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-maroon {\n  background: #d81b60;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-blue,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-blue {\n  background: #007bff;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-indigo,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-indigo {\n  background: #6610f2;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-purple,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-purple {\n  background: #6f42c1;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-pink,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-pink {\n  background: #e83e8c;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-red,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-red {\n  background: #dc3545;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-orange,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-orange {\n  background: #fd7e14;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-yellow,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-yellow {\n  background: #ffc107;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-green,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-green {\n  background: #28a745;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-teal,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-teal {\n  background: #20c997;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-cyan,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-cyan {\n  background: #17a2b8;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-white,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-white {\n  background: #fff;\n  color: #1f2d3d;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray {\n  background: #6c757d;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray-dark,\n.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-on {\n  border-bottom-left-radius: 0.1rem;\n  border-top-left-radius: 0.1rem;\n}\n\n.bootstrap-switch .bootstrap-switch-handle-off {\n  border-bottom-right-radius: 0.1rem;\n  border-top-right-radius: 0.1rem;\n}\n\n.bootstrap-switch input[type='radio'],\n.bootstrap-switch input[type='checkbox'] {\n  filter: alpha(opacity=0);\n  left: 0;\n  margin: 0;\n  opacity: 0;\n  position: absolute;\n  top: 0;\n  visibility: hidden;\n  z-index: -1;\n}\n\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label {\n  font-size: .875rem;\n  line-height: 1.5;\n  padding: .1rem .3rem;\n}\n\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label {\n  font-size: .875rem;\n  line-height: 1.5;\n  padding: .2rem .4rem;\n}\n\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label {\n  font-size: 1.25rem;\n  line-height: 1.3333333rem;\n  padding: .3rem .5rem;\n}\n\n.bootstrap-switch.bootstrap-switch-disabled, .bootstrap-switch.bootstrap-switch-readonly, .bootstrap-switch.bootstrap-switch-indeterminate {\n  cursor: default;\n}\n\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label, .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label, .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,\n.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,\n.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label {\n  cursor: default;\n  filter: alpha(opacity=50);\n  opacity: .5;\n}\n\n.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container {\n  transition: margin-left .5s;\n}\n\n.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on {\n  border-radius: 0 0.1rem 0.1rem 0;\n}\n\n.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off {\n  border-radius: 0.1rem 0 0 0.1rem;\n}\n\n.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label,\n.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label {\n  border-bottom-right-radius: 0.1rem;\n  border-top-right-radius: 0.1rem;\n}\n\n.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label,\n.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label {\n  border-bottom-left-radius: 0.1rem;\n  border-top-left-radius: 0.1rem;\n}\n\n.dark-mode .bootstrap-switch {\n  border-color: #6c757d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default {\n  background-color: #3a4047;\n  color: #fff;\n  border-color: #454d55;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary {\n  background: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-secondary,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-secondary {\n  background: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success {\n  background: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info {\n  background: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning {\n  background: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger {\n  background: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-light,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-light {\n  background: #f8f9fa;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-dark,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lightblue,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lightblue {\n  background: #86bad8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-navy,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-navy {\n  background: #002c59;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-olive,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-olive {\n  background: #74c8a3;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-lime,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-lime {\n  background: #67ffa9;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-fuchsia,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-fuchsia {\n  background: #f672d8;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-maroon,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-maroon {\n  background: #ed6c9b;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-blue,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-blue {\n  background: #3f6791;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-indigo,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-indigo {\n  background: #6610f2;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-purple,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-purple {\n  background: #6f42c1;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-pink,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-pink {\n  background: #e83e8c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-red,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-red {\n  background: #e74c3c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-orange,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-orange {\n  background: #fd7e14;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-yellow,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-yellow {\n  background: #f39c12;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-green,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-green {\n  background: #00bc8c;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-teal,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-teal {\n  background: #20c997;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-cyan,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-cyan {\n  background: #3498db;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-white,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-white {\n  background: #fff;\n  color: #1f2d3d;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray {\n  background: #6c757d;\n  color: #fff;\n}\n\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-gray-dark,\n.dark-mode .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-gray-dark {\n  background: #343a40;\n  color: #fff;\n}\n\n.dark-mode .daterangepicker {\n  background-color: #3f474e;\n  border: inherit;\n}\n\n.dark-mode .daterangepicker::before, .dark-mode .daterangepicker::after {\n  border-bottom-color: #3f474e;\n}\n\n.dark-mode .daterangepicker td.available:hover,\n.dark-mode .daterangepicker th.available:hover {\n  background-color: #3f474e;\n}\n\n.dark-mode .daterangepicker td.in-range {\n  background-color: #4b545c;\n  color: #fff;\n}\n\n.dark-mode .daterangepicker td.off,\n.dark-mode .daterangepicker td.off.in-range,\n.dark-mode .daterangepicker td.off.start-date,\n.dark-mode .daterangepicker td.off.end-date {\n  background-color: #292d32;\n  color: #fff;\n}\n\n.dark-mode .daterangepicker .ranges li:hover {\n  background-color: #343a40;\n}\n\n.dark-mode .daterangepicker.show-ranges.ltr .drp-calendar {\n  border-color: #4b545c;\n}\n\n.dark-mode .daterangepicker.show-ranges.ltr .drp-calendar.left, .dark-mode .daterangepicker.show-ranges.ltr .drp-calendar.right {\n  border-color: #4b545c;\n  padding-top: 0;\n}\n\n.dark-mode .daterangepicker .drp-buttons {\n  border-color: #4b545c;\n}\n\n.dark-mode .daterangepicker .calendar-table {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.dark-mode .daterangepicker .calendar-table th,\n.dark-mode .daterangepicker .calendar-table td {\n  color: #fff;\n}\n\n.dark-mode .daterangepicker .calendar-table .next span,\n.dark-mode .daterangepicker .calendar-table .prev span {\n  border-color: #fff;\n}\n\n.dark-mode .daterangepicker select.hourselect,\n.dark-mode .daterangepicker select.minuteselect,\n.dark-mode .daterangepicker select.secondselect,\n.dark-mode .daterangepicker select.ampmselect {\n  background-color: #343a40;\n  border-color: #4b545c;\n}\n\n.jqstooltip {\n  height: auto !important;\n  padding: 5px !important;\n  width: auto !important;\n}\n\n.connectedSortable {\n  min-height: 100px;\n}\n\n.ui-helper-hidden-accessible {\n  border: 0;\n  clip: rect(0 0 0 0);\n  height: 1px;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0;\n  position: absolute;\n  width: 1px;\n}\n\n.sort-highlight {\n  background: #f8f9fa;\n  border: 1px dashed #dee2e6;\n  margin-bottom: 10px;\n}\n\n.chart {\n  overflow: hidden;\n  position: relative;\n}\n\n.dark-mode .irs--flat .irs-line {\n  background-color: #4b545c;\n}\n\n.dark-mode .jsgrid-edit-row > .jsgrid-cell,\n.dark-mode .jsgrid-filter-row > .jsgrid-cell,\n.dark-mode .jsgrid-grid-body, .dark-mode .jsgrid-grid-header,\n.dark-mode .jsgrid-header-row > .jsgrid-header-cell,\n.dark-mode .jsgrid-insert-row > .jsgrid-cell,\n.dark-mode .jsgrid-row > .jsgrid-cell,\n.dark-mode .jsgrid-alt-row > .jsgrid-cell {\n  border-color: #6c757d;\n}\n\n.dark-mode .jsgrid-header-row > .jsgrid-header-cell,\n.dark-mode .jsgrid-row > .jsgrid-cell {\n  background-color: #343a40;\n}\n\n.dark-mode .jsgrid-alt-row > .jsgrid-cell {\n  background-color: #3a4047;\n}\n\n.dark-mode .jsgrid-selected-row > .jsgrid-cell {\n  background-color: #3f474e;\n}\n/*# sourceMappingURL=adminlte.plugins.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/js/adminlte.js",
    "content": "/*!\n * AdminLTE v3.2.0 (https://adminlte.io)\n * Copyright 2014-2022 Colorlib <https://colorlib.com>\n * Licensed under MIT (https://github.com/ColorlibHQ/AdminLTE/blob/master/LICENSE)\n */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery')) :\n  typeof define === 'function' && define.amd ? define(['exports', 'jquery'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.adminlte = {}, global.jQuery));\n})(this, (function (exports, $) { 'use strict';\n\n  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }\n\n  var $__default = /*#__PURE__*/_interopDefaultLegacy($);\n\n  /**\n   * --------------------------------------------\n   * AdminLTE CardRefresh.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$e = 'CardRefresh';\n  var DATA_KEY$e = 'lte.cardrefresh';\n  var EVENT_KEY$7 = \".\" + DATA_KEY$e;\n  var JQUERY_NO_CONFLICT$e = $__default[\"default\"].fn[NAME$e];\n  var EVENT_LOADED = \"loaded\" + EVENT_KEY$7;\n  var EVENT_OVERLAY_ADDED = \"overlay.added\" + EVENT_KEY$7;\n  var EVENT_OVERLAY_REMOVED = \"overlay.removed\" + EVENT_KEY$7;\n  var CLASS_NAME_CARD$1 = 'card';\n  var SELECTOR_CARD$1 = \".\" + CLASS_NAME_CARD$1;\n  var SELECTOR_DATA_REFRESH = '[data-card-widget=\"card-refresh\"]';\n  var Default$c = {\n    source: '',\n    sourceSelector: '',\n    params: {},\n    trigger: SELECTOR_DATA_REFRESH,\n    content: '.card-body',\n    loadInContent: true,\n    loadOnInit: true,\n    loadErrorTemplate: true,\n    responseType: '',\n    overlayTemplate: '<div class=\"overlay\"><i class=\"fas fa-2x fa-sync-alt fa-spin\"></i></div>',\n    errorTemplate: '<span class=\"text-danger\"></span>',\n    onLoadStart: function onLoadStart() {},\n    onLoadDone: function onLoadDone(response) {\n      return response;\n    },\n    onLoadFail: function onLoadFail(_jqXHR, _textStatus, _errorThrown) {}\n  };\n\n  var CardRefresh = /*#__PURE__*/function () {\n    function CardRefresh(element, settings) {\n      this._element = element;\n      this._parent = element.parents(SELECTOR_CARD$1).first();\n      this._settings = $__default[\"default\"].extend({}, Default$c, settings);\n      this._overlay = $__default[\"default\"](this._settings.overlayTemplate);\n\n      if (element.hasClass(CLASS_NAME_CARD$1)) {\n        this._parent = element;\n      }\n\n      if (this._settings.source === '') {\n        throw new Error('Source url was not defined. Please specify a url in your CardRefresh source option.');\n      }\n    }\n\n    var _proto = CardRefresh.prototype;\n\n    _proto.load = function load() {\n      var _this = this;\n\n      this._addOverlay();\n\n      this._settings.onLoadStart.call($__default[\"default\"](this));\n\n      $__default[\"default\"].get(this._settings.source, this._settings.params, function (response) {\n        if (_this._settings.loadInContent) {\n          if (_this._settings.sourceSelector !== '') {\n            response = $__default[\"default\"](response).find(_this._settings.sourceSelector).html();\n          }\n\n          _this._parent.find(_this._settings.content).html(response);\n        }\n\n        _this._settings.onLoadDone.call($__default[\"default\"](_this), response);\n\n        _this._removeOverlay();\n      }, this._settings.responseType !== '' && this._settings.responseType).fail(function (jqXHR, textStatus, errorThrown) {\n        _this._removeOverlay();\n\n        if (_this._settings.loadErrorTemplate) {\n          var msg = $__default[\"default\"](_this._settings.errorTemplate).text(errorThrown);\n\n          _this._parent.find(_this._settings.content).empty().append(msg);\n        }\n\n        _this._settings.onLoadFail.call($__default[\"default\"](_this), jqXHR, textStatus, errorThrown);\n      });\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_LOADED));\n    };\n\n    _proto._addOverlay = function _addOverlay() {\n      this._parent.append(this._overlay);\n\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_OVERLAY_ADDED));\n    };\n\n    _proto._removeOverlay = function _removeOverlay() {\n      this._parent.find(this._overlay).remove();\n\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_OVERLAY_REMOVED));\n    } // Private\n    ;\n\n    _proto._init = function _init() {\n      var _this2 = this;\n\n      $__default[\"default\"](this).find(this._settings.trigger).on('click', function () {\n        _this2.load();\n      });\n\n      if (this._settings.loadOnInit) {\n        this.load();\n      }\n    } // Static\n    ;\n\n    CardRefresh._jQueryInterface = function _jQueryInterface(config) {\n      var data = $__default[\"default\"](this).data(DATA_KEY$e);\n\n      var _options = $__default[\"default\"].extend({}, Default$c, $__default[\"default\"](this).data());\n\n      if (!data) {\n        data = new CardRefresh($__default[\"default\"](this), _options);\n        $__default[\"default\"](this).data(DATA_KEY$e, typeof config === 'string' ? data : config);\n      }\n\n      if (typeof config === 'string' && /load/.test(config)) {\n        data[config]();\n      } else {\n        data._init($__default[\"default\"](this));\n      }\n    };\n\n    return CardRefresh;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_REFRESH, function (event) {\n    if (event) {\n      event.preventDefault();\n    }\n\n    CardRefresh._jQueryInterface.call($__default[\"default\"](this), 'load');\n  });\n  $__default[\"default\"](function () {\n    $__default[\"default\"](SELECTOR_DATA_REFRESH).each(function () {\n      CardRefresh._jQueryInterface.call($__default[\"default\"](this));\n    });\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$e] = CardRefresh._jQueryInterface;\n  $__default[\"default\"].fn[NAME$e].Constructor = CardRefresh;\n\n  $__default[\"default\"].fn[NAME$e].noConflict = function () {\n    $__default[\"default\"].fn[NAME$e] = JQUERY_NO_CONFLICT$e;\n    return CardRefresh._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE CardWidget.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$d = 'CardWidget';\n  var DATA_KEY$d = 'lte.cardwidget';\n  var EVENT_KEY$6 = \".\" + DATA_KEY$d;\n  var JQUERY_NO_CONFLICT$d = $__default[\"default\"].fn[NAME$d];\n  var EVENT_EXPANDED$3 = \"expanded\" + EVENT_KEY$6;\n  var EVENT_COLLAPSED$4 = \"collapsed\" + EVENT_KEY$6;\n  var EVENT_MAXIMIZED = \"maximized\" + EVENT_KEY$6;\n  var EVENT_MINIMIZED = \"minimized\" + EVENT_KEY$6;\n  var EVENT_REMOVED$1 = \"removed\" + EVENT_KEY$6;\n  var CLASS_NAME_CARD = 'card';\n  var CLASS_NAME_COLLAPSED$1 = 'collapsed-card';\n  var CLASS_NAME_COLLAPSING = 'collapsing-card';\n  var CLASS_NAME_EXPANDING = 'expanding-card';\n  var CLASS_NAME_WAS_COLLAPSED = 'was-collapsed';\n  var CLASS_NAME_MAXIMIZED = 'maximized-card';\n  var SELECTOR_DATA_REMOVE = '[data-card-widget=\"remove\"]';\n  var SELECTOR_DATA_COLLAPSE = '[data-card-widget=\"collapse\"]';\n  var SELECTOR_DATA_MAXIMIZE = '[data-card-widget=\"maximize\"]';\n  var SELECTOR_CARD = \".\" + CLASS_NAME_CARD;\n  var SELECTOR_CARD_HEADER = '.card-header';\n  var SELECTOR_CARD_BODY = '.card-body';\n  var SELECTOR_CARD_FOOTER = '.card-footer';\n  var Default$b = {\n    animationSpeed: 'normal',\n    collapseTrigger: SELECTOR_DATA_COLLAPSE,\n    removeTrigger: SELECTOR_DATA_REMOVE,\n    maximizeTrigger: SELECTOR_DATA_MAXIMIZE,\n    collapseIcon: 'fa-minus',\n    expandIcon: 'fa-plus',\n    maximizeIcon: 'fa-expand',\n    minimizeIcon: 'fa-compress'\n  };\n\n  var CardWidget = /*#__PURE__*/function () {\n    function CardWidget(element, settings) {\n      this._element = element;\n      this._parent = element.parents(SELECTOR_CARD).first();\n\n      if (element.hasClass(CLASS_NAME_CARD)) {\n        this._parent = element;\n      }\n\n      this._settings = $__default[\"default\"].extend({}, Default$b, settings);\n    }\n\n    var _proto = CardWidget.prototype;\n\n    _proto.collapse = function collapse() {\n      var _this = this;\n\n      this._parent.addClass(CLASS_NAME_COLLAPSING).children(SELECTOR_CARD_BODY + \", \" + SELECTOR_CARD_FOOTER).slideUp(this._settings.animationSpeed, function () {\n        _this._parent.addClass(CLASS_NAME_COLLAPSED$1).removeClass(CLASS_NAME_COLLAPSING);\n      });\n\n      this._parent.find(\"> \" + SELECTOR_CARD_HEADER + \" \" + this._settings.collapseTrigger + \" .\" + this._settings.collapseIcon).addClass(this._settings.expandIcon).removeClass(this._settings.collapseIcon);\n\n      this._element.trigger($__default[\"default\"].Event(EVENT_COLLAPSED$4), this._parent);\n    };\n\n    _proto.expand = function expand() {\n      var _this2 = this;\n\n      this._parent.addClass(CLASS_NAME_EXPANDING).children(SELECTOR_CARD_BODY + \", \" + SELECTOR_CARD_FOOTER).slideDown(this._settings.animationSpeed, function () {\n        _this2._parent.removeClass(CLASS_NAME_COLLAPSED$1).removeClass(CLASS_NAME_EXPANDING);\n      });\n\n      this._parent.find(\"> \" + SELECTOR_CARD_HEADER + \" \" + this._settings.collapseTrigger + \" .\" + this._settings.expandIcon).addClass(this._settings.collapseIcon).removeClass(this._settings.expandIcon);\n\n      this._element.trigger($__default[\"default\"].Event(EVENT_EXPANDED$3), this._parent);\n    };\n\n    _proto.remove = function remove() {\n      this._parent.slideUp();\n\n      this._element.trigger($__default[\"default\"].Event(EVENT_REMOVED$1), this._parent);\n    };\n\n    _proto.toggle = function toggle() {\n      if (this._parent.hasClass(CLASS_NAME_COLLAPSED$1)) {\n        this.expand();\n        return;\n      }\n\n      this.collapse();\n    };\n\n    _proto.maximize = function maximize() {\n      this._parent.find(this._settings.maximizeTrigger + \" .\" + this._settings.maximizeIcon).addClass(this._settings.minimizeIcon).removeClass(this._settings.maximizeIcon);\n\n      this._parent.css({\n        height: this._parent.height(),\n        width: this._parent.width(),\n        transition: 'all .15s'\n      }).delay(150).queue(function () {\n        var $element = $__default[\"default\"](this);\n        $element.addClass(CLASS_NAME_MAXIMIZED);\n        $__default[\"default\"]('html').addClass(CLASS_NAME_MAXIMIZED);\n\n        if ($element.hasClass(CLASS_NAME_COLLAPSED$1)) {\n          $element.addClass(CLASS_NAME_WAS_COLLAPSED);\n        }\n\n        $element.dequeue();\n      });\n\n      this._element.trigger($__default[\"default\"].Event(EVENT_MAXIMIZED), this._parent);\n    };\n\n    _proto.minimize = function minimize() {\n      this._parent.find(this._settings.maximizeTrigger + \" .\" + this._settings.minimizeIcon).addClass(this._settings.maximizeIcon).removeClass(this._settings.minimizeIcon);\n\n      this._parent.css('cssText', \"height: \" + this._parent[0].style.height + \" !important; width: \" + this._parent[0].style.width + \" !important; transition: all .15s;\").delay(10).queue(function () {\n        var $element = $__default[\"default\"](this);\n        $element.removeClass(CLASS_NAME_MAXIMIZED);\n        $__default[\"default\"]('html').removeClass(CLASS_NAME_MAXIMIZED);\n        $element.css({\n          height: 'inherit',\n          width: 'inherit'\n        });\n\n        if ($element.hasClass(CLASS_NAME_WAS_COLLAPSED)) {\n          $element.removeClass(CLASS_NAME_WAS_COLLAPSED);\n        }\n\n        $element.dequeue();\n      });\n\n      this._element.trigger($__default[\"default\"].Event(EVENT_MINIMIZED), this._parent);\n    };\n\n    _proto.toggleMaximize = function toggleMaximize() {\n      if (this._parent.hasClass(CLASS_NAME_MAXIMIZED)) {\n        this.minimize();\n        return;\n      }\n\n      this.maximize();\n    } // Private\n    ;\n\n    _proto._init = function _init(card) {\n      var _this3 = this;\n\n      this._parent = card;\n      $__default[\"default\"](this).find(this._settings.collapseTrigger).click(function () {\n        _this3.toggle();\n      });\n      $__default[\"default\"](this).find(this._settings.maximizeTrigger).click(function () {\n        _this3.toggleMaximize();\n      });\n      $__default[\"default\"](this).find(this._settings.removeTrigger).click(function () {\n        _this3.remove();\n      });\n    } // Static\n    ;\n\n    CardWidget._jQueryInterface = function _jQueryInterface(config) {\n      var data = $__default[\"default\"](this).data(DATA_KEY$d);\n\n      var _options = $__default[\"default\"].extend({}, Default$b, $__default[\"default\"](this).data());\n\n      if (!data) {\n        data = new CardWidget($__default[\"default\"](this), _options);\n        $__default[\"default\"](this).data(DATA_KEY$d, typeof config === 'string' ? data : config);\n      }\n\n      if (typeof config === 'string' && /collapse|expand|remove|toggle|maximize|minimize|toggleMaximize/.test(config)) {\n        data[config]();\n      } else if (typeof config === 'object') {\n        data._init($__default[\"default\"](this));\n      }\n    };\n\n    return CardWidget;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_COLLAPSE, function (event) {\n    if (event) {\n      event.preventDefault();\n    }\n\n    CardWidget._jQueryInterface.call($__default[\"default\"](this), 'toggle');\n  });\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_REMOVE, function (event) {\n    if (event) {\n      event.preventDefault();\n    }\n\n    CardWidget._jQueryInterface.call($__default[\"default\"](this), 'remove');\n  });\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_MAXIMIZE, function (event) {\n    if (event) {\n      event.preventDefault();\n    }\n\n    CardWidget._jQueryInterface.call($__default[\"default\"](this), 'toggleMaximize');\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$d] = CardWidget._jQueryInterface;\n  $__default[\"default\"].fn[NAME$d].Constructor = CardWidget;\n\n  $__default[\"default\"].fn[NAME$d].noConflict = function () {\n    $__default[\"default\"].fn[NAME$d] = JQUERY_NO_CONFLICT$d;\n    return CardWidget._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE ControlSidebar.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$c = 'ControlSidebar';\n  var DATA_KEY$c = 'lte.controlsidebar';\n  var EVENT_KEY$5 = \".\" + DATA_KEY$c;\n  var JQUERY_NO_CONFLICT$c = $__default[\"default\"].fn[NAME$c];\n  var EVENT_COLLAPSED$3 = \"collapsed\" + EVENT_KEY$5;\n  var EVENT_COLLAPSED_DONE$1 = \"collapsed-done\" + EVENT_KEY$5;\n  var EVENT_EXPANDED$2 = \"expanded\" + EVENT_KEY$5;\n  var SELECTOR_CONTROL_SIDEBAR = '.control-sidebar';\n  var SELECTOR_CONTROL_SIDEBAR_CONTENT$1 = '.control-sidebar-content';\n  var SELECTOR_DATA_TOGGLE$4 = '[data-widget=\"control-sidebar\"]';\n  var SELECTOR_HEADER$1 = '.main-header';\n  var SELECTOR_FOOTER$1 = '.main-footer';\n  var CLASS_NAME_CONTROL_SIDEBAR_ANIMATE = 'control-sidebar-animate';\n  var CLASS_NAME_CONTROL_SIDEBAR_OPEN$1 = 'control-sidebar-open';\n  var CLASS_NAME_CONTROL_SIDEBAR_SLIDE = 'control-sidebar-slide-open';\n  var CLASS_NAME_LAYOUT_FIXED$1 = 'layout-fixed';\n  var CLASS_NAME_NAVBAR_FIXED = 'layout-navbar-fixed';\n  var CLASS_NAME_NAVBAR_SM_FIXED = 'layout-sm-navbar-fixed';\n  var CLASS_NAME_NAVBAR_MD_FIXED = 'layout-md-navbar-fixed';\n  var CLASS_NAME_NAVBAR_LG_FIXED = 'layout-lg-navbar-fixed';\n  var CLASS_NAME_NAVBAR_XL_FIXED = 'layout-xl-navbar-fixed';\n  var CLASS_NAME_FOOTER_FIXED = 'layout-footer-fixed';\n  var CLASS_NAME_FOOTER_SM_FIXED = 'layout-sm-footer-fixed';\n  var CLASS_NAME_FOOTER_MD_FIXED = 'layout-md-footer-fixed';\n  var CLASS_NAME_FOOTER_LG_FIXED = 'layout-lg-footer-fixed';\n  var CLASS_NAME_FOOTER_XL_FIXED = 'layout-xl-footer-fixed';\n  var Default$a = {\n    controlsidebarSlide: true,\n    scrollbarTheme: 'os-theme-light',\n    scrollbarAutoHide: 'l',\n    target: SELECTOR_CONTROL_SIDEBAR,\n    animationSpeed: 300\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var ControlSidebar = /*#__PURE__*/function () {\n    function ControlSidebar(element, config) {\n      this._element = element;\n      this._config = config;\n    } // Public\n\n\n    var _proto = ControlSidebar.prototype;\n\n    _proto.collapse = function collapse() {\n      var _this = this;\n\n      var $body = $__default[\"default\"]('body');\n      var $html = $__default[\"default\"]('html'); // Show the control sidebar\n\n      if (this._config.controlsidebarSlide) {\n        $html.addClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE);\n        $body.removeClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE).delay(300).queue(function () {\n          $__default[\"default\"](SELECTOR_CONTROL_SIDEBAR).hide();\n          $html.removeClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE);\n          $__default[\"default\"](this).dequeue();\n        });\n      } else {\n        $body.removeClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN$1);\n      }\n\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_COLLAPSED$3));\n      setTimeout(function () {\n        $__default[\"default\"](_this._element).trigger($__default[\"default\"].Event(EVENT_COLLAPSED_DONE$1));\n      }, this._config.animationSpeed);\n    };\n\n    _proto.show = function show(toggle) {\n      if (toggle === void 0) {\n        toggle = false;\n      }\n\n      var $body = $__default[\"default\"]('body');\n      var $html = $__default[\"default\"]('html');\n\n      if (toggle) {\n        $__default[\"default\"](SELECTOR_CONTROL_SIDEBAR).hide();\n      } // Collapse the control sidebar\n\n\n      if (this._config.controlsidebarSlide) {\n        $html.addClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE);\n        $__default[\"default\"](this._config.target).show().delay(10).queue(function () {\n          $body.addClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE).delay(300).queue(function () {\n            $html.removeClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE);\n            $__default[\"default\"](this).dequeue();\n          });\n          $__default[\"default\"](this).dequeue();\n        });\n      } else {\n        $body.addClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN$1);\n      }\n\n      this._fixHeight();\n\n      this._fixScrollHeight();\n\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_EXPANDED$2));\n    };\n\n    _proto.toggle = function toggle() {\n      var $body = $__default[\"default\"]('body');\n      var target = this._config.target;\n      var notVisible = !$__default[\"default\"](target).is(':visible');\n      var shouldClose = $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN$1) || $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE);\n      var shouldToggle = notVisible && ($body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN$1) || $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE));\n\n      if (notVisible || shouldToggle) {\n        // Open the control sidebar\n        this.show(notVisible);\n      } else if (shouldClose) {\n        // Close the control sidebar\n        this.collapse();\n      }\n    } // Private\n    ;\n\n    _proto._init = function _init() {\n      var _this2 = this;\n\n      var $body = $__default[\"default\"]('body');\n      var shouldNotHideAll = $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN$1) || $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE);\n\n      if (shouldNotHideAll) {\n        $__default[\"default\"](SELECTOR_CONTROL_SIDEBAR).not(this._config.target).hide();\n        $__default[\"default\"](this._config.target).css('display', 'block');\n      } else {\n        $__default[\"default\"](SELECTOR_CONTROL_SIDEBAR).hide();\n      }\n\n      this._fixHeight();\n\n      this._fixScrollHeight();\n\n      $__default[\"default\"](window).resize(function () {\n        _this2._fixHeight();\n\n        _this2._fixScrollHeight();\n      });\n      $__default[\"default\"](window).scroll(function () {\n        var $body = $__default[\"default\"]('body');\n        var shouldFixHeight = $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN$1) || $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE);\n\n        if (shouldFixHeight) {\n          _this2._fixScrollHeight();\n        }\n      });\n    };\n\n    _proto._isNavbarFixed = function _isNavbarFixed() {\n      var $body = $__default[\"default\"]('body');\n      return $body.hasClass(CLASS_NAME_NAVBAR_FIXED) || $body.hasClass(CLASS_NAME_NAVBAR_SM_FIXED) || $body.hasClass(CLASS_NAME_NAVBAR_MD_FIXED) || $body.hasClass(CLASS_NAME_NAVBAR_LG_FIXED) || $body.hasClass(CLASS_NAME_NAVBAR_XL_FIXED);\n    };\n\n    _proto._isFooterFixed = function _isFooterFixed() {\n      var $body = $__default[\"default\"]('body');\n      return $body.hasClass(CLASS_NAME_FOOTER_FIXED) || $body.hasClass(CLASS_NAME_FOOTER_SM_FIXED) || $body.hasClass(CLASS_NAME_FOOTER_MD_FIXED) || $body.hasClass(CLASS_NAME_FOOTER_LG_FIXED) || $body.hasClass(CLASS_NAME_FOOTER_XL_FIXED);\n    };\n\n    _proto._fixScrollHeight = function _fixScrollHeight() {\n      var $body = $__default[\"default\"]('body');\n      var $controlSidebar = $__default[\"default\"](this._config.target);\n\n      if (!$body.hasClass(CLASS_NAME_LAYOUT_FIXED$1)) {\n        return;\n      }\n\n      var heights = {\n        scroll: $__default[\"default\"](document).height(),\n        window: $__default[\"default\"](window).height(),\n        header: $__default[\"default\"](SELECTOR_HEADER$1).outerHeight(),\n        footer: $__default[\"default\"](SELECTOR_FOOTER$1).outerHeight()\n      };\n      var positions = {\n        bottom: Math.abs(heights.window + $__default[\"default\"](window).scrollTop() - heights.scroll),\n        top: $__default[\"default\"](window).scrollTop()\n      };\n      var navbarFixed = this._isNavbarFixed() && $__default[\"default\"](SELECTOR_HEADER$1).css('position') === 'fixed';\n      var footerFixed = this._isFooterFixed() && $__default[\"default\"](SELECTOR_FOOTER$1).css('position') === 'fixed';\n      var $controlsidebarContent = $__default[\"default\"](this._config.target + \", \" + this._config.target + \" \" + SELECTOR_CONTROL_SIDEBAR_CONTENT$1);\n\n      if (positions.top === 0 && positions.bottom === 0) {\n        $controlSidebar.css({\n          bottom: heights.footer,\n          top: heights.header\n        });\n        $controlsidebarContent.css('height', heights.window - (heights.header + heights.footer));\n      } else if (positions.bottom <= heights.footer) {\n        if (footerFixed === false) {\n          var top = heights.header - positions.top;\n          $controlSidebar.css('bottom', heights.footer - positions.bottom).css('top', top >= 0 ? top : 0);\n          $controlsidebarContent.css('height', heights.window - (heights.footer - positions.bottom));\n        } else {\n          $controlSidebar.css('bottom', heights.footer);\n        }\n      } else if (positions.top <= heights.header) {\n        if (navbarFixed === false) {\n          $controlSidebar.css('top', heights.header - positions.top);\n          $controlsidebarContent.css('height', heights.window - (heights.header - positions.top));\n        } else {\n          $controlSidebar.css('top', heights.header);\n        }\n      } else if (navbarFixed === false) {\n        $controlSidebar.css('top', 0);\n        $controlsidebarContent.css('height', heights.window);\n      } else {\n        $controlSidebar.css('top', heights.header);\n      }\n\n      if (footerFixed && navbarFixed) {\n        $controlsidebarContent.css('height', '100%');\n        $controlSidebar.css('height', '');\n      } else if (footerFixed || navbarFixed) {\n        $controlsidebarContent.css('height', '100%');\n        $controlsidebarContent.css('height', '');\n      }\n    };\n\n    _proto._fixHeight = function _fixHeight() {\n      var $body = $__default[\"default\"]('body');\n      var $controlSidebar = $__default[\"default\"](this._config.target + \" \" + SELECTOR_CONTROL_SIDEBAR_CONTENT$1);\n\n      if (!$body.hasClass(CLASS_NAME_LAYOUT_FIXED$1)) {\n        $controlSidebar.attr('style', '');\n        return;\n      }\n\n      var heights = {\n        window: $__default[\"default\"](window).height(),\n        header: $__default[\"default\"](SELECTOR_HEADER$1).outerHeight(),\n        footer: $__default[\"default\"](SELECTOR_FOOTER$1).outerHeight()\n      };\n      var sidebarHeight = heights.window - heights.header;\n\n      if (this._isFooterFixed() && $__default[\"default\"](SELECTOR_FOOTER$1).css('position') === 'fixed') {\n        sidebarHeight = heights.window - heights.header - heights.footer;\n      }\n\n      $controlSidebar.css('height', sidebarHeight);\n\n      if (typeof $__default[\"default\"].fn.overlayScrollbars !== 'undefined') {\n        $controlSidebar.overlayScrollbars({\n          className: this._config.scrollbarTheme,\n          sizeAutoCapable: true,\n          scrollbars: {\n            autoHide: this._config.scrollbarAutoHide,\n            clickScrolling: true\n          }\n        });\n      }\n    } // Static\n    ;\n\n    ControlSidebar._jQueryInterface = function _jQueryInterface(operation) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$c);\n\n        var _options = $__default[\"default\"].extend({}, Default$a, $__default[\"default\"](this).data());\n\n        if (!data) {\n          data = new ControlSidebar(this, _options);\n          $__default[\"default\"](this).data(DATA_KEY$c, data);\n        }\n\n        if (data[operation] === 'undefined') {\n          throw new Error(operation + \" is not a function\");\n        }\n\n        data[operation]();\n      });\n    };\n\n    return ControlSidebar;\n  }();\n  /**\n   *\n   * Data Api implementation\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_TOGGLE$4, function (event) {\n    event.preventDefault();\n\n    ControlSidebar._jQueryInterface.call($__default[\"default\"](this), 'toggle');\n  });\n  $__default[\"default\"](document).ready(function () {\n    ControlSidebar._jQueryInterface.call($__default[\"default\"](SELECTOR_DATA_TOGGLE$4), '_init');\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$c] = ControlSidebar._jQueryInterface;\n  $__default[\"default\"].fn[NAME$c].Constructor = ControlSidebar;\n\n  $__default[\"default\"].fn[NAME$c].noConflict = function () {\n    $__default[\"default\"].fn[NAME$c] = JQUERY_NO_CONFLICT$c;\n    return ControlSidebar._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE DirectChat.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$b = 'DirectChat';\n  var DATA_KEY$b = 'lte.directchat';\n  var EVENT_KEY$4 = \".\" + DATA_KEY$b;\n  var JQUERY_NO_CONFLICT$b = $__default[\"default\"].fn[NAME$b];\n  var EVENT_TOGGLED = \"toggled\" + EVENT_KEY$4;\n  var SELECTOR_DATA_TOGGLE$3 = '[data-widget=\"chat-pane-toggle\"]';\n  var SELECTOR_DIRECT_CHAT = '.direct-chat';\n  var CLASS_NAME_DIRECT_CHAT_OPEN = 'direct-chat-contacts-open';\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var DirectChat = /*#__PURE__*/function () {\n    function DirectChat(element) {\n      this._element = element;\n    }\n\n    var _proto = DirectChat.prototype;\n\n    _proto.toggle = function toggle() {\n      $__default[\"default\"](this._element).parents(SELECTOR_DIRECT_CHAT).first().toggleClass(CLASS_NAME_DIRECT_CHAT_OPEN);\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_TOGGLED));\n    } // Static\n    ;\n\n    DirectChat._jQueryInterface = function _jQueryInterface(config) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$b);\n\n        if (!data) {\n          data = new DirectChat($__default[\"default\"](this));\n          $__default[\"default\"](this).data(DATA_KEY$b, data);\n        }\n\n        data[config]();\n      });\n    };\n\n    return DirectChat;\n  }();\n  /**\n   *\n   * Data Api implementation\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_TOGGLE$3, function (event) {\n    if (event) {\n      event.preventDefault();\n    }\n\n    DirectChat._jQueryInterface.call($__default[\"default\"](this), 'toggle');\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$b] = DirectChat._jQueryInterface;\n  $__default[\"default\"].fn[NAME$b].Constructor = DirectChat;\n\n  $__default[\"default\"].fn[NAME$b].noConflict = function () {\n    $__default[\"default\"].fn[NAME$b] = JQUERY_NO_CONFLICT$b;\n    return DirectChat._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE Dropdown.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$a = 'Dropdown';\n  var DATA_KEY$a = 'lte.dropdown';\n  var JQUERY_NO_CONFLICT$a = $__default[\"default\"].fn[NAME$a];\n  var SELECTOR_NAVBAR = '.navbar';\n  var SELECTOR_DROPDOWN_MENU = '.dropdown-menu';\n  var SELECTOR_DROPDOWN_MENU_ACTIVE = '.dropdown-menu.show';\n  var SELECTOR_DROPDOWN_TOGGLE = '[data-toggle=\"dropdown\"]';\n  var CLASS_NAME_DROPDOWN_RIGHT = 'dropdown-menu-right';\n  var CLASS_NAME_DROPDOWN_SUBMENU = 'dropdown-submenu'; // TODO: this is unused; should be removed along with the extend?\n\n  var Default$9 = {};\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var Dropdown = /*#__PURE__*/function () {\n    function Dropdown(element, config) {\n      this._config = config;\n      this._element = element;\n    } // Public\n\n\n    var _proto = Dropdown.prototype;\n\n    _proto.toggleSubmenu = function toggleSubmenu() {\n      this._element.siblings().show().toggleClass('show');\n\n      if (!this._element.next().hasClass('show')) {\n        this._element.parents(SELECTOR_DROPDOWN_MENU).first().find('.show').removeClass('show').hide();\n      }\n\n      this._element.parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function () {\n        $__default[\"default\"]('.dropdown-submenu .show').removeClass('show').hide();\n      });\n    };\n\n    _proto.fixPosition = function fixPosition() {\n      var $element = $__default[\"default\"](SELECTOR_DROPDOWN_MENU_ACTIVE);\n\n      if ($element.length === 0) {\n        return;\n      }\n\n      if ($element.hasClass(CLASS_NAME_DROPDOWN_RIGHT)) {\n        $element.css({\n          left: 'inherit',\n          right: 0\n        });\n      } else {\n        $element.css({\n          left: 0,\n          right: 'inherit'\n        });\n      }\n\n      var offset = $element.offset();\n      var width = $element.width();\n      var visiblePart = $__default[\"default\"](window).width() - offset.left;\n\n      if (offset.left < 0) {\n        $element.css({\n          left: 'inherit',\n          right: offset.left - 5\n        });\n      } else if (visiblePart < width) {\n        $element.css({\n          left: 'inherit',\n          right: 0\n        });\n      }\n    } // Static\n    ;\n\n    Dropdown._jQueryInterface = function _jQueryInterface(config) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$a);\n\n        var _config = $__default[\"default\"].extend({}, Default$9, $__default[\"default\"](this).data());\n\n        if (!data) {\n          data = new Dropdown($__default[\"default\"](this), _config);\n          $__default[\"default\"](this).data(DATA_KEY$a, data);\n        }\n\n        if (config === 'toggleSubmenu' || config === 'fixPosition') {\n          data[config]();\n        }\n      });\n    };\n\n    return Dropdown;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](SELECTOR_DROPDOWN_MENU + \" \" + SELECTOR_DROPDOWN_TOGGLE).on('click', function (event) {\n    event.preventDefault();\n    event.stopPropagation();\n\n    Dropdown._jQueryInterface.call($__default[\"default\"](this), 'toggleSubmenu');\n  });\n  $__default[\"default\"](SELECTOR_NAVBAR + \" \" + SELECTOR_DROPDOWN_TOGGLE).on('click', function (event) {\n    event.preventDefault();\n\n    if ($__default[\"default\"](event.target).parent().hasClass(CLASS_NAME_DROPDOWN_SUBMENU)) {\n      return;\n    }\n\n    setTimeout(function () {\n      Dropdown._jQueryInterface.call($__default[\"default\"](this), 'fixPosition');\n    }, 1);\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$a] = Dropdown._jQueryInterface;\n  $__default[\"default\"].fn[NAME$a].Constructor = Dropdown;\n\n  $__default[\"default\"].fn[NAME$a].noConflict = function () {\n    $__default[\"default\"].fn[NAME$a] = JQUERY_NO_CONFLICT$a;\n    return Dropdown._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE ExpandableTable.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n    * Constants\n    * ====================================================\n    */\n\n  var NAME$9 = 'ExpandableTable';\n  var DATA_KEY$9 = 'lte.expandableTable';\n  var EVENT_KEY$3 = \".\" + DATA_KEY$9;\n  var JQUERY_NO_CONFLICT$9 = $__default[\"default\"].fn[NAME$9];\n  var EVENT_EXPANDED$1 = \"expanded\" + EVENT_KEY$3;\n  var EVENT_COLLAPSED$2 = \"collapsed\" + EVENT_KEY$3;\n  var SELECTOR_TABLE = '.expandable-table';\n  var SELECTOR_EXPANDABLE_BODY = '.expandable-body';\n  var SELECTOR_DATA_TOGGLE$2 = '[data-widget=\"expandable-table\"]';\n  var SELECTOR_ARIA_ATTR = 'aria-expanded';\n  /**\n    * Class Definition\n    * ====================================================\n    */\n\n  var ExpandableTable = /*#__PURE__*/function () {\n    function ExpandableTable(element, options) {\n      this._options = options;\n      this._element = element;\n    } // Public\n\n\n    var _proto = ExpandableTable.prototype;\n\n    _proto.init = function init() {\n      $__default[\"default\"](SELECTOR_DATA_TOGGLE$2).each(function (_, $header) {\n        var $type = $__default[\"default\"]($header).attr(SELECTOR_ARIA_ATTR);\n        var $body = $__default[\"default\"]($header).next(SELECTOR_EXPANDABLE_BODY).children().first().children();\n\n        if ($type === 'true') {\n          $body.show();\n        } else if ($type === 'false') {\n          $body.hide();\n          $body.parent().parent().addClass('d-none');\n        }\n      });\n    };\n\n    _proto.toggleRow = function toggleRow() {\n      var $element = this._element;\n\n      if ($element[0].nodeName !== 'TR') {\n        $element = $element.parent();\n\n        if ($element[0].nodeName !== 'TR') {\n          $element = $element.parent();\n        }\n      }\n\n      var time = 500;\n      var $type = $element.attr(SELECTOR_ARIA_ATTR);\n      var $body = $element.next(SELECTOR_EXPANDABLE_BODY).children().first().children();\n      $body.stop();\n\n      if ($type === 'true') {\n        $body.slideUp(time, function () {\n          $element.next(SELECTOR_EXPANDABLE_BODY).addClass('d-none');\n        });\n        $element.attr(SELECTOR_ARIA_ATTR, 'false');\n        $element.trigger($__default[\"default\"].Event(EVENT_COLLAPSED$2));\n      } else if ($type === 'false') {\n        $element.next(SELECTOR_EXPANDABLE_BODY).removeClass('d-none');\n        $body.slideDown(time);\n        $element.attr(SELECTOR_ARIA_ATTR, 'true');\n        $element.trigger($__default[\"default\"].Event(EVENT_EXPANDED$1));\n      }\n    } // Static\n    ;\n\n    ExpandableTable._jQueryInterface = function _jQueryInterface(operation) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$9);\n\n        if (!data) {\n          data = new ExpandableTable($__default[\"default\"](this));\n          $__default[\"default\"](this).data(DATA_KEY$9, data);\n        }\n\n        if (typeof operation === 'string' && /init|toggleRow/.test(operation)) {\n          data[operation]();\n        }\n      });\n    };\n\n    return ExpandableTable;\n  }();\n  /**\n    * Data API\n    * ====================================================\n    */\n\n\n  $__default[\"default\"](SELECTOR_TABLE).ready(function () {\n    ExpandableTable._jQueryInterface.call($__default[\"default\"](this), 'init');\n  });\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_TOGGLE$2, function () {\n    ExpandableTable._jQueryInterface.call($__default[\"default\"](this), 'toggleRow');\n  });\n  /**\n    * jQuery API\n    * ====================================================\n    */\n\n  $__default[\"default\"].fn[NAME$9] = ExpandableTable._jQueryInterface;\n  $__default[\"default\"].fn[NAME$9].Constructor = ExpandableTable;\n\n  $__default[\"default\"].fn[NAME$9].noConflict = function () {\n    $__default[\"default\"].fn[NAME$9] = JQUERY_NO_CONFLICT$9;\n    return ExpandableTable._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE Fullscreen.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$8 = 'Fullscreen';\n  var DATA_KEY$8 = 'lte.fullscreen';\n  var JQUERY_NO_CONFLICT$8 = $__default[\"default\"].fn[NAME$8];\n  var SELECTOR_DATA_WIDGET$2 = '[data-widget=\"fullscreen\"]';\n  var SELECTOR_ICON = SELECTOR_DATA_WIDGET$2 + \" i\";\n  var EVENT_FULLSCREEN_CHANGE = 'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange';\n  var Default$8 = {\n    minimizeIcon: 'fa-compress-arrows-alt',\n    maximizeIcon: 'fa-expand-arrows-alt'\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var Fullscreen = /*#__PURE__*/function () {\n    function Fullscreen(_element, _options) {\n      this.element = _element;\n      this.options = $__default[\"default\"].extend({}, Default$8, _options);\n    } // Public\n\n\n    var _proto = Fullscreen.prototype;\n\n    _proto.toggle = function toggle() {\n      if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {\n        this.windowed();\n      } else {\n        this.fullscreen();\n      }\n    };\n\n    _proto.toggleIcon = function toggleIcon() {\n      if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {\n        $__default[\"default\"](SELECTOR_ICON).removeClass(this.options.maximizeIcon).addClass(this.options.minimizeIcon);\n      } else {\n        $__default[\"default\"](SELECTOR_ICON).removeClass(this.options.minimizeIcon).addClass(this.options.maximizeIcon);\n      }\n    };\n\n    _proto.fullscreen = function fullscreen() {\n      if (document.documentElement.requestFullscreen) {\n        document.documentElement.requestFullscreen();\n      } else if (document.documentElement.webkitRequestFullscreen) {\n        document.documentElement.webkitRequestFullscreen();\n      } else if (document.documentElement.msRequestFullscreen) {\n        document.documentElement.msRequestFullscreen();\n      }\n    };\n\n    _proto.windowed = function windowed() {\n      if (document.exitFullscreen) {\n        document.exitFullscreen();\n      } else if (document.webkitExitFullscreen) {\n        document.webkitExitFullscreen();\n      } else if (document.msExitFullscreen) {\n        document.msExitFullscreen();\n      }\n    } // Static\n    ;\n\n    Fullscreen._jQueryInterface = function _jQueryInterface(config) {\n      var data = $__default[\"default\"](this).data(DATA_KEY$8);\n\n      if (!data) {\n        data = $__default[\"default\"](this).data();\n      }\n\n      var _options = $__default[\"default\"].extend({}, Default$8, typeof config === 'object' ? config : data);\n\n      var plugin = new Fullscreen($__default[\"default\"](this), _options);\n      $__default[\"default\"](this).data(DATA_KEY$8, typeof config === 'object' ? config : data);\n\n      if (typeof config === 'string' && /toggle|toggleIcon|fullscreen|windowed/.test(config)) {\n        plugin[config]();\n      } else {\n        plugin.init();\n      }\n    };\n\n    return Fullscreen;\n  }();\n  /**\n    * Data API\n    * ====================================================\n    */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_DATA_WIDGET$2, function () {\n    Fullscreen._jQueryInterface.call($__default[\"default\"](this), 'toggle');\n  });\n  $__default[\"default\"](document).on(EVENT_FULLSCREEN_CHANGE, function () {\n    Fullscreen._jQueryInterface.call($__default[\"default\"](SELECTOR_DATA_WIDGET$2), 'toggleIcon');\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$8] = Fullscreen._jQueryInterface;\n  $__default[\"default\"].fn[NAME$8].Constructor = Fullscreen;\n\n  $__default[\"default\"].fn[NAME$8].noConflict = function () {\n    $__default[\"default\"].fn[NAME$8] = JQUERY_NO_CONFLICT$8;\n    return Fullscreen._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE IFrame.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$7 = 'IFrame';\n  var DATA_KEY$7 = 'lte.iframe';\n  var JQUERY_NO_CONFLICT$7 = $__default[\"default\"].fn[NAME$7];\n  var SELECTOR_DATA_TOGGLE$1 = '[data-widget=\"iframe\"]';\n  var SELECTOR_DATA_TOGGLE_CLOSE = '[data-widget=\"iframe-close\"]';\n  var SELECTOR_DATA_TOGGLE_SCROLL_LEFT = '[data-widget=\"iframe-scrollleft\"]';\n  var SELECTOR_DATA_TOGGLE_SCROLL_RIGHT = '[data-widget=\"iframe-scrollright\"]';\n  var SELECTOR_DATA_TOGGLE_FULLSCREEN = '[data-widget=\"iframe-fullscreen\"]';\n  var SELECTOR_CONTENT_WRAPPER = '.content-wrapper';\n  var SELECTOR_CONTENT_IFRAME = SELECTOR_CONTENT_WRAPPER + \" iframe\";\n  var SELECTOR_TAB_NAV = SELECTOR_CONTENT_WRAPPER + \".iframe-mode .nav\";\n  var SELECTOR_TAB_NAVBAR_NAV = SELECTOR_CONTENT_WRAPPER + \".iframe-mode .navbar-nav\";\n  var SELECTOR_TAB_NAVBAR_NAV_ITEM = SELECTOR_TAB_NAVBAR_NAV + \" .nav-item\";\n  var SELECTOR_TAB_NAVBAR_NAV_LINK = SELECTOR_TAB_NAVBAR_NAV + \" .nav-link\";\n  var SELECTOR_TAB_CONTENT = SELECTOR_CONTENT_WRAPPER + \".iframe-mode .tab-content\";\n  var SELECTOR_TAB_EMPTY = SELECTOR_TAB_CONTENT + \" .tab-empty\";\n  var SELECTOR_TAB_LOADING = SELECTOR_TAB_CONTENT + \" .tab-loading\";\n  var SELECTOR_TAB_PANE = SELECTOR_TAB_CONTENT + \" .tab-pane\";\n  var SELECTOR_SIDEBAR_MENU_ITEM = '.main-sidebar .nav-item > a.nav-link';\n  var SELECTOR_SIDEBAR_SEARCH_ITEM = '.sidebar-search-results .list-group-item';\n  var SELECTOR_HEADER_MENU_ITEM = '.main-header .nav-item a.nav-link';\n  var SELECTOR_HEADER_DROPDOWN_ITEM = '.main-header a.dropdown-item';\n  var CLASS_NAME_IFRAME_MODE$1 = 'iframe-mode';\n  var CLASS_NAME_FULLSCREEN_MODE = 'iframe-mode-fullscreen';\n  var Default$7 = {\n    onTabClick: function onTabClick(item) {\n      return item;\n    },\n    onTabChanged: function onTabChanged(item) {\n      return item;\n    },\n    onTabCreated: function onTabCreated(item) {\n      return item;\n    },\n    autoIframeMode: true,\n    autoItemActive: true,\n    autoShowNewTab: true,\n    autoDarkMode: false,\n    allowDuplicates: false,\n    allowReload: true,\n    loadingScreen: true,\n    useNavbarItems: true,\n    scrollOffset: 40,\n    scrollBehaviorSwap: false,\n    iconMaximize: 'fa-expand',\n    iconMinimize: 'fa-compress'\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var IFrame = /*#__PURE__*/function () {\n    function IFrame(element, config) {\n      this._config = config;\n      this._element = element;\n\n      this._init();\n    } // Public\n\n\n    var _proto = IFrame.prototype;\n\n    _proto.onTabClick = function onTabClick(item) {\n      this._config.onTabClick(item);\n    };\n\n    _proto.onTabChanged = function onTabChanged(item) {\n      this._config.onTabChanged(item);\n    };\n\n    _proto.onTabCreated = function onTabCreated(item) {\n      this._config.onTabCreated(item);\n    };\n\n    _proto.createTab = function createTab(title, link, uniqueName, autoOpen) {\n      var _this = this;\n\n      var tabId = \"panel-\" + uniqueName;\n      var navId = \"tab-\" + uniqueName;\n\n      if (this._config.allowDuplicates) {\n        tabId += \"-\" + Math.floor(Math.random() * 1000);\n        navId += \"-\" + Math.floor(Math.random() * 1000);\n      }\n\n      var newNavItem = \"<li class=\\\"nav-item\\\" role=\\\"presentation\\\"><a href=\\\"#\\\" class=\\\"btn-iframe-close\\\" data-widget=\\\"iframe-close\\\" data-type=\\\"only-this\\\"><i class=\\\"fas fa-times\\\"></i></a><a class=\\\"nav-link\\\" data-toggle=\\\"row\\\" id=\\\"\" + navId + \"\\\" href=\\\"#\" + tabId + \"\\\" role=\\\"tab\\\" aria-controls=\\\"\" + tabId + \"\\\" aria-selected=\\\"false\\\">\" + title + \"</a></li>\";\n      $__default[\"default\"](SELECTOR_TAB_NAVBAR_NAV).append(unescape(escape(newNavItem)));\n      var newTabItem = \"<div class=\\\"tab-pane fade\\\" id=\\\"\" + tabId + \"\\\" role=\\\"tabpanel\\\" aria-labelledby=\\\"\" + navId + \"\\\"><iframe src=\\\"\" + link + \"\\\"></iframe></div>\";\n      $__default[\"default\"](SELECTOR_TAB_CONTENT).append(unescape(escape(newTabItem)));\n\n      if (autoOpen) {\n        if (this._config.loadingScreen) {\n          var $loadingScreen = $__default[\"default\"](SELECTOR_TAB_LOADING);\n          $loadingScreen.fadeIn();\n          $__default[\"default\"](tabId + \" iframe\").ready(function () {\n            if (typeof _this._config.loadingScreen === 'number') {\n              _this.switchTab(\"#\" + navId);\n\n              setTimeout(function () {\n                $loadingScreen.fadeOut();\n              }, _this._config.loadingScreen);\n            } else {\n              _this.switchTab(\"#\" + navId);\n\n              $loadingScreen.fadeOut();\n            }\n          });\n        } else {\n          this.switchTab(\"#\" + navId);\n        }\n      }\n\n      this.onTabCreated($__default[\"default\"](\"#\" + navId));\n    };\n\n    _proto.openTabSidebar = function openTabSidebar(item, autoOpen) {\n      if (autoOpen === void 0) {\n        autoOpen = this._config.autoShowNewTab;\n      }\n\n      var $item = $__default[\"default\"](item).clone();\n\n      if ($item.attr('href') === undefined) {\n        $item = $__default[\"default\"](item).parent('a').clone();\n      }\n\n      $item.find('.right, .search-path').remove();\n      var title = $item.find('p').text();\n\n      if (title === '') {\n        title = $item.text();\n      }\n\n      var link = $item.attr('href');\n\n      if (link === '#' || link === '' || link === undefined) {\n        return;\n      }\n\n      var uniqueName = unescape(link).replace('./', '').replace(/[\"#&'./:=?[\\]]/gi, '-').replace(/(--)/gi, '');\n      var navId = \"tab-\" + uniqueName;\n\n      if (!this._config.allowDuplicates && $__default[\"default\"](\"#\" + navId).length > 0) {\n        return this.switchTab(\"#\" + navId, this._config.allowReload);\n      }\n\n      if (!this._config.allowDuplicates && $__default[\"default\"](\"#\" + navId).length === 0 || this._config.allowDuplicates) {\n        this.createTab(title, link, uniqueName, autoOpen);\n      }\n    };\n\n    _proto.switchTab = function switchTab(item, reload) {\n      var _this2 = this;\n\n      if (reload === void 0) {\n        reload = false;\n      }\n\n      var $item = $__default[\"default\"](item);\n      var tabId = $item.attr('href');\n      $__default[\"default\"](SELECTOR_TAB_EMPTY).hide();\n\n      if (reload) {\n        var $loadingScreen = $__default[\"default\"](SELECTOR_TAB_LOADING);\n\n        if (this._config.loadingScreen) {\n          $loadingScreen.show(0, function () {\n            $__default[\"default\"](tabId + \" iframe\").attr('src', $__default[\"default\"](tabId + \" iframe\").attr('src')).ready(function () {\n              if (_this2._config.loadingScreen) {\n                if (typeof _this2._config.loadingScreen === 'number') {\n                  setTimeout(function () {\n                    $loadingScreen.fadeOut();\n                  }, _this2._config.loadingScreen);\n                } else {\n                  $loadingScreen.fadeOut();\n                }\n              }\n            });\n          });\n        } else {\n          $__default[\"default\"](tabId + \" iframe\").attr('src', $__default[\"default\"](tabId + \" iframe\").attr('src'));\n        }\n      }\n\n      $__default[\"default\"](SELECTOR_TAB_NAVBAR_NAV + \" .active\").tab('dispose').removeClass('active');\n\n      this._fixHeight();\n\n      $item.tab('show');\n      $item.parents('li').addClass('active');\n      this.onTabChanged($item);\n\n      if (this._config.autoItemActive) {\n        this._setItemActive($__default[\"default\"](tabId + \" iframe\").attr('src'));\n      }\n    };\n\n    _proto.removeActiveTab = function removeActiveTab(type, element) {\n      if (type == 'all') {\n        $__default[\"default\"](SELECTOR_TAB_NAVBAR_NAV_ITEM).remove();\n        $__default[\"default\"](SELECTOR_TAB_PANE).remove();\n        $__default[\"default\"](SELECTOR_TAB_EMPTY).show();\n      } else if (type == 'all-other') {\n        $__default[\"default\"](SELECTOR_TAB_NAVBAR_NAV_ITEM + \":not(.active)\").remove();\n        $__default[\"default\"](SELECTOR_TAB_PANE + \":not(.active)\").remove();\n      } else if (type == 'only-this') {\n        var $navClose = $__default[\"default\"](element);\n        var $navItem = $navClose.parent('.nav-item');\n        var $navItemParent = $navItem.parent();\n        var navItemIndex = $navItem.index();\n        var tabId = $navClose.siblings('.nav-link').attr('aria-controls');\n        $navItem.remove();\n        $__default[\"default\"](\"#\" + tabId).remove();\n\n        if ($__default[\"default\"](SELECTOR_TAB_CONTENT).children().length == $__default[\"default\"](SELECTOR_TAB_EMPTY + \", \" + SELECTOR_TAB_LOADING).length) {\n          $__default[\"default\"](SELECTOR_TAB_EMPTY).show();\n        } else {\n          var prevNavItemIndex = navItemIndex - 1;\n          this.switchTab($navItemParent.children().eq(prevNavItemIndex).find('a.nav-link'));\n        }\n      } else {\n        var _$navItem = $__default[\"default\"](SELECTOR_TAB_NAVBAR_NAV_ITEM + \".active\");\n\n        var _$navItemParent = _$navItem.parent();\n\n        var _navItemIndex = _$navItem.index();\n\n        _$navItem.remove();\n\n        $__default[\"default\"](SELECTOR_TAB_PANE + \".active\").remove();\n\n        if ($__default[\"default\"](SELECTOR_TAB_CONTENT).children().length == $__default[\"default\"](SELECTOR_TAB_EMPTY + \", \" + SELECTOR_TAB_LOADING).length) {\n          $__default[\"default\"](SELECTOR_TAB_EMPTY).show();\n        } else {\n          var _prevNavItemIndex = _navItemIndex - 1;\n\n          this.switchTab(_$navItemParent.children().eq(_prevNavItemIndex).find('a.nav-link'));\n        }\n      }\n    };\n\n    _proto.toggleFullscreen = function toggleFullscreen() {\n      if ($__default[\"default\"]('body').hasClass(CLASS_NAME_FULLSCREEN_MODE)) {\n        $__default[\"default\"](SELECTOR_DATA_TOGGLE_FULLSCREEN + \" i\").removeClass(this._config.iconMinimize).addClass(this._config.iconMaximize);\n        $__default[\"default\"]('body').removeClass(CLASS_NAME_FULLSCREEN_MODE);\n        $__default[\"default\"](SELECTOR_TAB_EMPTY + \", \" + SELECTOR_TAB_LOADING).height('100%');\n        $__default[\"default\"](SELECTOR_CONTENT_WRAPPER).height('100%');\n        $__default[\"default\"](SELECTOR_CONTENT_IFRAME).height('100%');\n      } else {\n        $__default[\"default\"](SELECTOR_DATA_TOGGLE_FULLSCREEN + \" i\").removeClass(this._config.iconMaximize).addClass(this._config.iconMinimize);\n        $__default[\"default\"]('body').addClass(CLASS_NAME_FULLSCREEN_MODE);\n      }\n\n      $__default[\"default\"](window).trigger('resize');\n\n      this._fixHeight(true);\n    } // Private\n    ;\n\n    _proto._init = function _init() {\n      var usingDefTab = $__default[\"default\"](SELECTOR_TAB_CONTENT).children().length > 2;\n\n      this._setupListeners();\n\n      this._fixHeight(true);\n\n      if (usingDefTab) {\n        var $el = $__default[\"default\"](\"\" + SELECTOR_TAB_PANE).first(); // eslint-disable-next-line no-console\n\n        console.log($el);\n        var uniqueName = $el.attr('id').replace('panel-', '');\n        var navId = \"#tab-\" + uniqueName;\n        this.switchTab(navId, true);\n      }\n    };\n\n    _proto._initFrameElement = function _initFrameElement() {\n      if (window.frameElement && this._config.autoIframeMode) {\n        var $body = $__default[\"default\"]('body');\n        $body.addClass(CLASS_NAME_IFRAME_MODE$1);\n\n        if (this._config.autoDarkMode) {\n          $body.addClass('dark-mode');\n        }\n      }\n    };\n\n    _proto._navScroll = function _navScroll(offset) {\n      var leftPos = $__default[\"default\"](SELECTOR_TAB_NAVBAR_NAV).scrollLeft();\n      $__default[\"default\"](SELECTOR_TAB_NAVBAR_NAV).animate({\n        scrollLeft: leftPos + offset\n      }, 250, 'linear');\n    };\n\n    _proto._setupListeners = function _setupListeners() {\n      var _this3 = this;\n\n      $__default[\"default\"](window).on('resize', function () {\n        setTimeout(function () {\n          _this3._fixHeight();\n        }, 1);\n      });\n\n      if ($__default[\"default\"](SELECTOR_CONTENT_WRAPPER).hasClass(CLASS_NAME_IFRAME_MODE$1)) {\n        $__default[\"default\"](document).on('click', SELECTOR_SIDEBAR_MENU_ITEM + \", \" + SELECTOR_SIDEBAR_SEARCH_ITEM, function (e) {\n          e.preventDefault();\n\n          _this3.openTabSidebar(e.target);\n        });\n\n        if (this._config.useNavbarItems) {\n          $__default[\"default\"](document).on('click', SELECTOR_HEADER_MENU_ITEM + \", \" + SELECTOR_HEADER_DROPDOWN_ITEM, function (e) {\n            e.preventDefault();\n\n            _this3.openTabSidebar(e.target);\n          });\n        }\n      }\n\n      $__default[\"default\"](document).on('click', SELECTOR_TAB_NAVBAR_NAV_LINK, function (e) {\n        e.preventDefault();\n\n        _this3.onTabClick(e.target);\n\n        _this3.switchTab(e.target);\n      });\n      $__default[\"default\"](document).on('click', SELECTOR_TAB_NAVBAR_NAV_LINK, function (e) {\n        e.preventDefault();\n\n        _this3.onTabClick(e.target);\n\n        _this3.switchTab(e.target);\n      });\n      $__default[\"default\"](document).on('click', SELECTOR_DATA_TOGGLE_CLOSE, function (e) {\n        e.preventDefault();\n        var target = e.target;\n\n        if (target.nodeName == 'I') {\n          target = e.target.offsetParent;\n        }\n\n        _this3.removeActiveTab(target.attributes['data-type'] ? target.attributes['data-type'].nodeValue : null, target);\n      });\n      $__default[\"default\"](document).on('click', SELECTOR_DATA_TOGGLE_FULLSCREEN, function (e) {\n        e.preventDefault();\n\n        _this3.toggleFullscreen();\n      });\n      var mousedown = false;\n      var mousedownInterval = null;\n      $__default[\"default\"](document).on('mousedown', SELECTOR_DATA_TOGGLE_SCROLL_LEFT, function (e) {\n        e.preventDefault();\n        clearInterval(mousedownInterval);\n        var scrollOffset = _this3._config.scrollOffset;\n\n        if (!_this3._config.scrollBehaviorSwap) {\n          scrollOffset = -scrollOffset;\n        }\n\n        mousedown = true;\n\n        _this3._navScroll(scrollOffset);\n\n        mousedownInterval = setInterval(function () {\n          _this3._navScroll(scrollOffset);\n        }, 250);\n      });\n      $__default[\"default\"](document).on('mousedown', SELECTOR_DATA_TOGGLE_SCROLL_RIGHT, function (e) {\n        e.preventDefault();\n        clearInterval(mousedownInterval);\n        var scrollOffset = _this3._config.scrollOffset;\n\n        if (_this3._config.scrollBehaviorSwap) {\n          scrollOffset = -scrollOffset;\n        }\n\n        mousedown = true;\n\n        _this3._navScroll(scrollOffset);\n\n        mousedownInterval = setInterval(function () {\n          _this3._navScroll(scrollOffset);\n        }, 250);\n      });\n      $__default[\"default\"](document).on('mouseup', function () {\n        if (mousedown) {\n          mousedown = false;\n          clearInterval(mousedownInterval);\n          mousedownInterval = null;\n        }\n      });\n    };\n\n    _proto._setItemActive = function _setItemActive(href) {\n      $__default[\"default\"](SELECTOR_SIDEBAR_MENU_ITEM + \", \" + SELECTOR_HEADER_DROPDOWN_ITEM).removeClass('active');\n      $__default[\"default\"](SELECTOR_HEADER_MENU_ITEM).parent().removeClass('active');\n      var $headerMenuItem = $__default[\"default\"](SELECTOR_HEADER_MENU_ITEM + \"[href$=\\\"\" + href + \"\\\"]\");\n      var $headerDropdownItem = $__default[\"default\"](SELECTOR_HEADER_DROPDOWN_ITEM + \"[href$=\\\"\" + href + \"\\\"]\");\n      var $sidebarMenuItem = $__default[\"default\"](SELECTOR_SIDEBAR_MENU_ITEM + \"[href$=\\\"\" + href + \"\\\"]\");\n      $headerMenuItem.each(function (i, e) {\n        $__default[\"default\"](e).parent().addClass('active');\n      });\n      $headerDropdownItem.each(function (i, e) {\n        $__default[\"default\"](e).addClass('active');\n      });\n      $sidebarMenuItem.each(function (i, e) {\n        $__default[\"default\"](e).addClass('active');\n        $__default[\"default\"](e).parents('.nav-treeview').prevAll('.nav-link').addClass('active');\n      });\n    };\n\n    _proto._fixHeight = function _fixHeight(tabEmpty) {\n      if (tabEmpty === void 0) {\n        tabEmpty = false;\n      }\n\n      if ($__default[\"default\"]('body').hasClass(CLASS_NAME_FULLSCREEN_MODE)) {\n        var windowHeight = $__default[\"default\"](window).height();\n        var navbarHeight = $__default[\"default\"](SELECTOR_TAB_NAV).outerHeight();\n        $__default[\"default\"](SELECTOR_TAB_EMPTY + \", \" + SELECTOR_TAB_LOADING + \", \" + SELECTOR_CONTENT_IFRAME).height(windowHeight - navbarHeight);\n        $__default[\"default\"](SELECTOR_CONTENT_WRAPPER).height(windowHeight);\n      } else {\n        var contentWrapperHeight = parseFloat($__default[\"default\"](SELECTOR_CONTENT_WRAPPER).css('height'));\n\n        var _navbarHeight = $__default[\"default\"](SELECTOR_TAB_NAV).outerHeight();\n\n        if (tabEmpty == true) {\n          setTimeout(function () {\n            $__default[\"default\"](SELECTOR_TAB_EMPTY + \", \" + SELECTOR_TAB_LOADING).height(contentWrapperHeight - _navbarHeight);\n          }, 50);\n        } else {\n          $__default[\"default\"](SELECTOR_CONTENT_IFRAME).height(contentWrapperHeight - _navbarHeight);\n        }\n      }\n    } // Static\n    ;\n\n    IFrame._jQueryInterface = function _jQueryInterface(config) {\n      if ($__default[\"default\"](SELECTOR_DATA_TOGGLE$1).length > 0) {\n        var data = $__default[\"default\"](this).data(DATA_KEY$7);\n\n        if (!data) {\n          data = $__default[\"default\"](this).data();\n        }\n\n        var _options = $__default[\"default\"].extend({}, Default$7, typeof config === 'object' ? config : data);\n\n        localStorage.setItem('AdminLTE:IFrame:Options', JSON.stringify(_options));\n        var plugin = new IFrame($__default[\"default\"](this), _options);\n        $__default[\"default\"](this).data(DATA_KEY$7, typeof config === 'object' ? config : data);\n\n        if (typeof config === 'string' && /createTab|openTabSidebar|switchTab|removeActiveTab/.test(config)) {\n          plugin[config]();\n        }\n      } else {\n        new IFrame($__default[\"default\"](this), JSON.parse(localStorage.getItem('AdminLTE:IFrame:Options')))._initFrameElement();\n      }\n    };\n\n    return IFrame;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](window).on('load', function () {\n    IFrame._jQueryInterface.call($__default[\"default\"](SELECTOR_DATA_TOGGLE$1));\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$7] = IFrame._jQueryInterface;\n  $__default[\"default\"].fn[NAME$7].Constructor = IFrame;\n\n  $__default[\"default\"].fn[NAME$7].noConflict = function () {\n    $__default[\"default\"].fn[NAME$7] = JQUERY_NO_CONFLICT$7;\n    return IFrame._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE Layout.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$6 = 'Layout';\n  var DATA_KEY$6 = 'lte.layout';\n  var JQUERY_NO_CONFLICT$6 = $__default[\"default\"].fn[NAME$6];\n  var SELECTOR_HEADER = '.main-header';\n  var SELECTOR_MAIN_SIDEBAR = '.main-sidebar';\n  var SELECTOR_SIDEBAR$1 = '.main-sidebar .sidebar';\n  var SELECTOR_CONTENT = '.content-wrapper';\n  var SELECTOR_CONTROL_SIDEBAR_CONTENT = '.control-sidebar-content';\n  var SELECTOR_CONTROL_SIDEBAR_BTN = '[data-widget=\"control-sidebar\"]';\n  var SELECTOR_FOOTER = '.main-footer';\n  var SELECTOR_PUSHMENU_BTN = '[data-widget=\"pushmenu\"]';\n  var SELECTOR_LOGIN_BOX = '.login-box';\n  var SELECTOR_REGISTER_BOX = '.register-box';\n  var SELECTOR_PRELOADER = '.preloader';\n  var CLASS_NAME_SIDEBAR_COLLAPSED$1 = 'sidebar-collapse';\n  var CLASS_NAME_SIDEBAR_FOCUSED = 'sidebar-focused';\n  var CLASS_NAME_LAYOUT_FIXED = 'layout-fixed';\n  var CLASS_NAME_CONTROL_SIDEBAR_SLIDE_OPEN = 'control-sidebar-slide-open';\n  var CLASS_NAME_CONTROL_SIDEBAR_OPEN = 'control-sidebar-open';\n  var CLASS_NAME_IFRAME_MODE = 'iframe-mode';\n  var Default$6 = {\n    scrollbarTheme: 'os-theme-light',\n    scrollbarAutoHide: 'l',\n    panelAutoHeight: true,\n    panelAutoHeightMode: 'min-height',\n    preloadDuration: 200,\n    loginRegisterAutoHeight: true\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var Layout = /*#__PURE__*/function () {\n    function Layout(element, config) {\n      this._config = config;\n      this._element = element;\n    } // Public\n\n\n    var _proto = Layout.prototype;\n\n    _proto.fixLayoutHeight = function fixLayoutHeight(extra) {\n      if (extra === void 0) {\n        extra = null;\n      }\n\n      var $body = $__default[\"default\"]('body');\n      var controlSidebar = 0;\n\n      if ($body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE_OPEN) || $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN) || extra === 'control_sidebar') {\n        controlSidebar = $__default[\"default\"](SELECTOR_CONTROL_SIDEBAR_CONTENT).outerHeight();\n      }\n\n      var heights = {\n        window: $__default[\"default\"](window).height(),\n        header: $__default[\"default\"](SELECTOR_HEADER).length > 0 ? $__default[\"default\"](SELECTOR_HEADER).outerHeight() : 0,\n        footer: $__default[\"default\"](SELECTOR_FOOTER).length > 0 ? $__default[\"default\"](SELECTOR_FOOTER).outerHeight() : 0,\n        sidebar: $__default[\"default\"](SELECTOR_SIDEBAR$1).length > 0 ? $__default[\"default\"](SELECTOR_SIDEBAR$1).height() : 0,\n        controlSidebar: controlSidebar\n      };\n\n      var max = this._max(heights);\n\n      var offset = this._config.panelAutoHeight;\n\n      if (offset === true) {\n        offset = 0;\n      }\n\n      var $contentSelector = $__default[\"default\"](SELECTOR_CONTENT);\n\n      if (offset !== false) {\n        if (max === heights.controlSidebar) {\n          $contentSelector.css(this._config.panelAutoHeightMode, max + offset);\n        } else if (max === heights.window) {\n          $contentSelector.css(this._config.panelAutoHeightMode, max + offset - heights.header - heights.footer);\n        } else {\n          $contentSelector.css(this._config.panelAutoHeightMode, max + offset - heights.header);\n        }\n\n        if (this._isFooterFixed()) {\n          $contentSelector.css(this._config.panelAutoHeightMode, parseFloat($contentSelector.css(this._config.panelAutoHeightMode)) + heights.footer);\n        }\n      }\n\n      if (!$body.hasClass(CLASS_NAME_LAYOUT_FIXED)) {\n        return;\n      }\n\n      if (typeof $__default[\"default\"].fn.overlayScrollbars !== 'undefined') {\n        $__default[\"default\"](SELECTOR_SIDEBAR$1).overlayScrollbars({\n          className: this._config.scrollbarTheme,\n          sizeAutoCapable: true,\n          scrollbars: {\n            autoHide: this._config.scrollbarAutoHide,\n            clickScrolling: true\n          }\n        });\n      } else {\n        $__default[\"default\"](SELECTOR_SIDEBAR$1).css('overflow-y', 'auto');\n      }\n    };\n\n    _proto.fixLoginRegisterHeight = function fixLoginRegisterHeight() {\n      var $body = $__default[\"default\"]('body');\n      var $selector = $__default[\"default\"](SELECTOR_LOGIN_BOX + \", \" + SELECTOR_REGISTER_BOX);\n\n      if ($body.hasClass(CLASS_NAME_IFRAME_MODE)) {\n        $body.css('height', '100%');\n        $__default[\"default\"]('.wrapper').css('height', '100%');\n        $__default[\"default\"]('html').css('height', '100%');\n      } else if ($selector.length === 0) {\n        $body.css('height', 'auto');\n        $__default[\"default\"]('html').css('height', 'auto');\n      } else {\n        var boxHeight = $selector.height();\n\n        if ($body.css(this._config.panelAutoHeightMode) !== boxHeight) {\n          $body.css(this._config.panelAutoHeightMode, boxHeight);\n        }\n      }\n    } // Private\n    ;\n\n    _proto._init = function _init() {\n      var _this = this;\n\n      // Activate layout height watcher\n      this.fixLayoutHeight();\n\n      if (this._config.loginRegisterAutoHeight === true) {\n        this.fixLoginRegisterHeight();\n      } else if (this._config.loginRegisterAutoHeight === parseInt(this._config.loginRegisterAutoHeight, 10)) {\n        setInterval(this.fixLoginRegisterHeight, this._config.loginRegisterAutoHeight);\n      }\n\n      $__default[\"default\"](SELECTOR_SIDEBAR$1).on('collapsed.lte.treeview expanded.lte.treeview', function () {\n        _this.fixLayoutHeight();\n      });\n      $__default[\"default\"](SELECTOR_MAIN_SIDEBAR).on('mouseenter mouseleave', function () {\n        if ($__default[\"default\"]('body').hasClass(CLASS_NAME_SIDEBAR_COLLAPSED$1)) {\n          _this.fixLayoutHeight();\n        }\n      });\n      $__default[\"default\"](SELECTOR_PUSHMENU_BTN).on('collapsed.lte.pushmenu shown.lte.pushmenu', function () {\n        setTimeout(function () {\n          _this.fixLayoutHeight();\n        }, 300);\n      });\n      $__default[\"default\"](SELECTOR_CONTROL_SIDEBAR_BTN).on('collapsed.lte.controlsidebar', function () {\n        _this.fixLayoutHeight();\n      }).on('expanded.lte.controlsidebar', function () {\n        _this.fixLayoutHeight('control_sidebar');\n      });\n      $__default[\"default\"](window).resize(function () {\n        _this.fixLayoutHeight();\n      });\n      setTimeout(function () {\n        $__default[\"default\"]('body.hold-transition').removeClass('hold-transition');\n      }, 50);\n      setTimeout(function () {\n        var $preloader = $__default[\"default\"](SELECTOR_PRELOADER);\n\n        if ($preloader) {\n          $preloader.css('height', 0);\n          setTimeout(function () {\n            $preloader.children().hide();\n          }, 200);\n        }\n      }, this._config.preloadDuration);\n    };\n\n    _proto._max = function _max(numbers) {\n      // Calculate the maximum number in a list\n      var max = 0;\n      Object.keys(numbers).forEach(function (key) {\n        if (numbers[key] > max) {\n          max = numbers[key];\n        }\n      });\n      return max;\n    };\n\n    _proto._isFooterFixed = function _isFooterFixed() {\n      return $__default[\"default\"](SELECTOR_FOOTER).css('position') === 'fixed';\n    } // Static\n    ;\n\n    Layout._jQueryInterface = function _jQueryInterface(config) {\n      if (config === void 0) {\n        config = '';\n      }\n\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$6);\n\n        var _options = $__default[\"default\"].extend({}, Default$6, $__default[\"default\"](this).data());\n\n        if (!data) {\n          data = new Layout($__default[\"default\"](this), _options);\n          $__default[\"default\"](this).data(DATA_KEY$6, data);\n        }\n\n        if (config === 'init' || config === '') {\n          data._init();\n        } else if (config === 'fixLayoutHeight' || config === 'fixLoginRegisterHeight') {\n          data[config]();\n        }\n      });\n    };\n\n    return Layout;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](window).on('load', function () {\n    Layout._jQueryInterface.call($__default[\"default\"]('body'));\n  });\n  $__default[\"default\"](SELECTOR_SIDEBAR$1 + \" a\").on('focusin', function () {\n    $__default[\"default\"](SELECTOR_MAIN_SIDEBAR).addClass(CLASS_NAME_SIDEBAR_FOCUSED);\n  }).on('focusout', function () {\n    $__default[\"default\"](SELECTOR_MAIN_SIDEBAR).removeClass(CLASS_NAME_SIDEBAR_FOCUSED);\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$6] = Layout._jQueryInterface;\n  $__default[\"default\"].fn[NAME$6].Constructor = Layout;\n\n  $__default[\"default\"].fn[NAME$6].noConflict = function () {\n    $__default[\"default\"].fn[NAME$6] = JQUERY_NO_CONFLICT$6;\n    return Layout._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE PushMenu.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$5 = 'PushMenu';\n  var DATA_KEY$5 = 'lte.pushmenu';\n  var EVENT_KEY$2 = \".\" + DATA_KEY$5;\n  var JQUERY_NO_CONFLICT$5 = $__default[\"default\"].fn[NAME$5];\n  var EVENT_COLLAPSED$1 = \"collapsed\" + EVENT_KEY$2;\n  var EVENT_COLLAPSED_DONE = \"collapsed-done\" + EVENT_KEY$2;\n  var EVENT_SHOWN = \"shown\" + EVENT_KEY$2;\n  var SELECTOR_TOGGLE_BUTTON$1 = '[data-widget=\"pushmenu\"]';\n  var SELECTOR_BODY = 'body';\n  var SELECTOR_OVERLAY = '#sidebar-overlay';\n  var SELECTOR_WRAPPER = '.wrapper';\n  var CLASS_NAME_COLLAPSED = 'sidebar-collapse';\n  var CLASS_NAME_OPEN$3 = 'sidebar-open';\n  var CLASS_NAME_IS_OPENING$1 = 'sidebar-is-opening';\n  var CLASS_NAME_CLOSED = 'sidebar-closed';\n  var Default$5 = {\n    autoCollapseSize: 992,\n    enableRemember: false,\n    noTransitionAfterReload: true,\n    animationSpeed: 300\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var PushMenu = /*#__PURE__*/function () {\n    function PushMenu(element, options) {\n      this._element = element;\n      this._options = $__default[\"default\"].extend({}, Default$5, options);\n\n      if ($__default[\"default\"](SELECTOR_OVERLAY).length === 0) {\n        this._addOverlay();\n      }\n\n      this._init();\n    } // Public\n\n\n    var _proto = PushMenu.prototype;\n\n    _proto.expand = function expand() {\n      var $bodySelector = $__default[\"default\"](SELECTOR_BODY);\n\n      if (this._options.autoCollapseSize && $__default[\"default\"](window).width() <= this._options.autoCollapseSize) {\n        $bodySelector.addClass(CLASS_NAME_OPEN$3);\n      }\n\n      $bodySelector.addClass(CLASS_NAME_IS_OPENING$1).removeClass(CLASS_NAME_COLLAPSED + \" \" + CLASS_NAME_CLOSED).delay(50).queue(function () {\n        $bodySelector.removeClass(CLASS_NAME_IS_OPENING$1);\n        $__default[\"default\"](this).dequeue();\n      });\n\n      if (this._options.enableRemember) {\n        localStorage.setItem(\"remember\" + EVENT_KEY$2, CLASS_NAME_OPEN$3);\n      }\n\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_SHOWN));\n    };\n\n    _proto.collapse = function collapse() {\n      var _this = this;\n\n      var $bodySelector = $__default[\"default\"](SELECTOR_BODY);\n\n      if (this._options.autoCollapseSize && $__default[\"default\"](window).width() <= this._options.autoCollapseSize) {\n        $bodySelector.removeClass(CLASS_NAME_OPEN$3).addClass(CLASS_NAME_CLOSED);\n      }\n\n      $bodySelector.addClass(CLASS_NAME_COLLAPSED);\n\n      if (this._options.enableRemember) {\n        localStorage.setItem(\"remember\" + EVENT_KEY$2, CLASS_NAME_COLLAPSED);\n      }\n\n      $__default[\"default\"](this._element).trigger($__default[\"default\"].Event(EVENT_COLLAPSED$1));\n      setTimeout(function () {\n        $__default[\"default\"](_this._element).trigger($__default[\"default\"].Event(EVENT_COLLAPSED_DONE));\n      }, this._options.animationSpeed);\n    };\n\n    _proto.toggle = function toggle() {\n      if ($__default[\"default\"](SELECTOR_BODY).hasClass(CLASS_NAME_COLLAPSED)) {\n        this.expand();\n      } else {\n        this.collapse();\n      }\n    };\n\n    _proto.autoCollapse = function autoCollapse(resize) {\n      if (resize === void 0) {\n        resize = false;\n      }\n\n      if (!this._options.autoCollapseSize) {\n        return;\n      }\n\n      var $bodySelector = $__default[\"default\"](SELECTOR_BODY);\n\n      if ($__default[\"default\"](window).width() <= this._options.autoCollapseSize) {\n        if (!$bodySelector.hasClass(CLASS_NAME_OPEN$3)) {\n          this.collapse();\n        }\n      } else if (resize === true) {\n        if ($bodySelector.hasClass(CLASS_NAME_OPEN$3)) {\n          $bodySelector.removeClass(CLASS_NAME_OPEN$3);\n        } else if ($bodySelector.hasClass(CLASS_NAME_CLOSED)) {\n          this.expand();\n        }\n      }\n    };\n\n    _proto.remember = function remember() {\n      if (!this._options.enableRemember) {\n        return;\n      }\n\n      var $body = $__default[\"default\"]('body');\n      var toggleState = localStorage.getItem(\"remember\" + EVENT_KEY$2);\n\n      if (toggleState === CLASS_NAME_COLLAPSED) {\n        if (this._options.noTransitionAfterReload) {\n          $body.addClass('hold-transition').addClass(CLASS_NAME_COLLAPSED).delay(50).queue(function () {\n            $__default[\"default\"](this).removeClass('hold-transition');\n            $__default[\"default\"](this).dequeue();\n          });\n        } else {\n          $body.addClass(CLASS_NAME_COLLAPSED);\n        }\n      } else if (this._options.noTransitionAfterReload) {\n        $body.addClass('hold-transition').removeClass(CLASS_NAME_COLLAPSED).delay(50).queue(function () {\n          $__default[\"default\"](this).removeClass('hold-transition');\n          $__default[\"default\"](this).dequeue();\n        });\n      } else {\n        $body.removeClass(CLASS_NAME_COLLAPSED);\n      }\n    } // Private\n    ;\n\n    _proto._init = function _init() {\n      var _this2 = this;\n\n      this.remember();\n      this.autoCollapse();\n      $__default[\"default\"](window).resize(function () {\n        _this2.autoCollapse(true);\n      });\n    };\n\n    _proto._addOverlay = function _addOverlay() {\n      var _this3 = this;\n\n      var overlay = $__default[\"default\"]('<div />', {\n        id: 'sidebar-overlay'\n      });\n      overlay.on('click', function () {\n        _this3.collapse();\n      });\n      $__default[\"default\"](SELECTOR_WRAPPER).append(overlay);\n    } // Static\n    ;\n\n    PushMenu._jQueryInterface = function _jQueryInterface(operation) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$5);\n\n        var _options = $__default[\"default\"].extend({}, Default$5, $__default[\"default\"](this).data());\n\n        if (!data) {\n          data = new PushMenu(this, _options);\n          $__default[\"default\"](this).data(DATA_KEY$5, data);\n        }\n\n        if (typeof operation === 'string' && /collapse|expand|toggle/.test(operation)) {\n          data[operation]();\n        }\n      });\n    };\n\n    return PushMenu;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_TOGGLE_BUTTON$1, function (event) {\n    event.preventDefault();\n    var button = event.currentTarget;\n\n    if ($__default[\"default\"](button).data('widget') !== 'pushmenu') {\n      button = $__default[\"default\"](button).closest(SELECTOR_TOGGLE_BUTTON$1);\n    }\n\n    PushMenu._jQueryInterface.call($__default[\"default\"](button), 'toggle');\n  });\n  $__default[\"default\"](window).on('load', function () {\n    PushMenu._jQueryInterface.call($__default[\"default\"](SELECTOR_TOGGLE_BUTTON$1));\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$5] = PushMenu._jQueryInterface;\n  $__default[\"default\"].fn[NAME$5].Constructor = PushMenu;\n\n  $__default[\"default\"].fn[NAME$5].noConflict = function () {\n    $__default[\"default\"].fn[NAME$5] = JQUERY_NO_CONFLICT$5;\n    return PushMenu._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE SidebarSearch.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$4 = 'SidebarSearch';\n  var DATA_KEY$4 = 'lte.sidebar-search';\n  var JQUERY_NO_CONFLICT$4 = $__default[\"default\"].fn[NAME$4];\n  var CLASS_NAME_OPEN$2 = 'sidebar-search-open';\n  var CLASS_NAME_ICON_SEARCH = 'fa-search';\n  var CLASS_NAME_ICON_CLOSE = 'fa-times';\n  var CLASS_NAME_HEADER = 'nav-header';\n  var CLASS_NAME_SEARCH_RESULTS = 'sidebar-search-results';\n  var CLASS_NAME_LIST_GROUP = 'list-group';\n  var SELECTOR_DATA_WIDGET$1 = '[data-widget=\"sidebar-search\"]';\n  var SELECTOR_SIDEBAR = '.main-sidebar .nav-sidebar';\n  var SELECTOR_NAV_LINK = '.nav-link';\n  var SELECTOR_NAV_TREEVIEW = '.nav-treeview';\n  var SELECTOR_SEARCH_INPUT$1 = SELECTOR_DATA_WIDGET$1 + \" .form-control\";\n  var SELECTOR_SEARCH_BUTTON = SELECTOR_DATA_WIDGET$1 + \" .btn\";\n  var SELECTOR_SEARCH_ICON = SELECTOR_SEARCH_BUTTON + \" i\";\n  var SELECTOR_SEARCH_LIST_GROUP = \".\" + CLASS_NAME_LIST_GROUP;\n  var SELECTOR_SEARCH_RESULTS = \".\" + CLASS_NAME_SEARCH_RESULTS;\n  var SELECTOR_SEARCH_RESULTS_GROUP = SELECTOR_SEARCH_RESULTS + \" .\" + CLASS_NAME_LIST_GROUP;\n  var Default$4 = {\n    arrowSign: '->',\n    minLength: 3,\n    maxResults: 7,\n    highlightName: true,\n    highlightPath: false,\n    highlightClass: 'text-light',\n    notFoundText: 'No element found!'\n  };\n  var SearchItems = [];\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var SidebarSearch = /*#__PURE__*/function () {\n    function SidebarSearch(_element, _options) {\n      this.element = _element;\n      this.options = $__default[\"default\"].extend({}, Default$4, _options);\n      this.items = [];\n    } // Public\n\n\n    var _proto = SidebarSearch.prototype;\n\n    _proto.init = function init() {\n      var _this = this;\n\n      if ($__default[\"default\"](SELECTOR_DATA_WIDGET$1).length === 0) {\n        return;\n      }\n\n      if ($__default[\"default\"](SELECTOR_DATA_WIDGET$1).next(SELECTOR_SEARCH_RESULTS).length === 0) {\n        $__default[\"default\"](SELECTOR_DATA_WIDGET$1).after($__default[\"default\"]('<div />', {\n          class: CLASS_NAME_SEARCH_RESULTS\n        }));\n      }\n\n      if ($__default[\"default\"](SELECTOR_SEARCH_RESULTS).children(SELECTOR_SEARCH_LIST_GROUP).length === 0) {\n        $__default[\"default\"](SELECTOR_SEARCH_RESULTS).append($__default[\"default\"]('<div />', {\n          class: CLASS_NAME_LIST_GROUP\n        }));\n      }\n\n      this._addNotFound();\n\n      $__default[\"default\"](SELECTOR_SIDEBAR).children().each(function (i, child) {\n        _this._parseItem(child);\n      });\n    };\n\n    _proto.search = function search() {\n      var _this2 = this;\n\n      var searchValue = $__default[\"default\"](SELECTOR_SEARCH_INPUT$1).val().toLowerCase();\n\n      if (searchValue.length < this.options.minLength) {\n        $__default[\"default\"](SELECTOR_SEARCH_RESULTS_GROUP).empty();\n\n        this._addNotFound();\n\n        this.close();\n        return;\n      }\n\n      var searchResults = SearchItems.filter(function (item) {\n        return item.name.toLowerCase().includes(searchValue);\n      });\n      var endResults = $__default[\"default\"](searchResults.slice(0, this.options.maxResults));\n      $__default[\"default\"](SELECTOR_SEARCH_RESULTS_GROUP).empty();\n\n      if (endResults.length === 0) {\n        this._addNotFound();\n      } else {\n        endResults.each(function (i, result) {\n          $__default[\"default\"](SELECTOR_SEARCH_RESULTS_GROUP).append(_this2._renderItem(escape(result.name), encodeURI(result.link), result.path));\n        });\n      }\n\n      this.open();\n    };\n\n    _proto.open = function open() {\n      $__default[\"default\"](SELECTOR_DATA_WIDGET$1).parent().addClass(CLASS_NAME_OPEN$2);\n      $__default[\"default\"](SELECTOR_SEARCH_ICON).removeClass(CLASS_NAME_ICON_SEARCH).addClass(CLASS_NAME_ICON_CLOSE);\n    };\n\n    _proto.close = function close() {\n      $__default[\"default\"](SELECTOR_DATA_WIDGET$1).parent().removeClass(CLASS_NAME_OPEN$2);\n      $__default[\"default\"](SELECTOR_SEARCH_ICON).removeClass(CLASS_NAME_ICON_CLOSE).addClass(CLASS_NAME_ICON_SEARCH);\n    };\n\n    _proto.toggle = function toggle() {\n      if ($__default[\"default\"](SELECTOR_DATA_WIDGET$1).parent().hasClass(CLASS_NAME_OPEN$2)) {\n        this.close();\n      } else {\n        this.open();\n      }\n    } // Private\n    ;\n\n    _proto._parseItem = function _parseItem(item, path) {\n      var _this3 = this;\n\n      if (path === void 0) {\n        path = [];\n      }\n\n      if ($__default[\"default\"](item).hasClass(CLASS_NAME_HEADER)) {\n        return;\n      }\n\n      var itemObject = {};\n      var navLink = $__default[\"default\"](item).clone().find(\"> \" + SELECTOR_NAV_LINK);\n      var navTreeview = $__default[\"default\"](item).clone().find(\"> \" + SELECTOR_NAV_TREEVIEW);\n      var link = navLink.attr('href');\n      var name = navLink.find('p').children().remove().end().text();\n      itemObject.name = this._trimText(name);\n      itemObject.link = link;\n      itemObject.path = path;\n\n      if (navTreeview.length === 0) {\n        SearchItems.push(itemObject);\n      } else {\n        var newPath = itemObject.path.concat([itemObject.name]);\n        navTreeview.children().each(function (i, child) {\n          _this3._parseItem(child, newPath);\n        });\n      }\n    };\n\n    _proto._trimText = function _trimText(text) {\n      return $.trim(text.replace(/(\\r\\n|\\n|\\r)/gm, ' '));\n    };\n\n    _proto._renderItem = function _renderItem(name, link, path) {\n      var _this4 = this;\n\n      path = path.join(\" \" + this.options.arrowSign + \" \");\n      name = unescape(name);\n      link = decodeURI(link);\n\n      if (this.options.highlightName || this.options.highlightPath) {\n        var searchValue = $__default[\"default\"](SELECTOR_SEARCH_INPUT$1).val().toLowerCase();\n        var regExp = new RegExp(searchValue, 'gi');\n\n        if (this.options.highlightName) {\n          name = name.replace(regExp, function (str) {\n            return \"<strong class=\\\"\" + _this4.options.highlightClass + \"\\\">\" + str + \"</strong>\";\n          });\n        }\n\n        if (this.options.highlightPath) {\n          path = path.replace(regExp, function (str) {\n            return \"<strong class=\\\"\" + _this4.options.highlightClass + \"\\\">\" + str + \"</strong>\";\n          });\n        }\n      }\n\n      var groupItemElement = $__default[\"default\"]('<a/>', {\n        href: decodeURIComponent(link),\n        class: 'list-group-item'\n      });\n      var searchTitleElement = $__default[\"default\"]('<div/>', {\n        class: 'search-title'\n      }).html(name);\n      var searchPathElement = $__default[\"default\"]('<div/>', {\n        class: 'search-path'\n      }).html(path);\n      groupItemElement.append(searchTitleElement).append(searchPathElement);\n      return groupItemElement;\n    };\n\n    _proto._addNotFound = function _addNotFound() {\n      $__default[\"default\"](SELECTOR_SEARCH_RESULTS_GROUP).append(this._renderItem(this.options.notFoundText, '#', []));\n    } // Static\n    ;\n\n    SidebarSearch._jQueryInterface = function _jQueryInterface(config) {\n      var data = $__default[\"default\"](this).data(DATA_KEY$4);\n\n      if (!data) {\n        data = $__default[\"default\"](this).data();\n      }\n\n      var _options = $__default[\"default\"].extend({}, Default$4, typeof config === 'object' ? config : data);\n\n      var plugin = new SidebarSearch($__default[\"default\"](this), _options);\n      $__default[\"default\"](this).data(DATA_KEY$4, typeof config === 'object' ? config : data);\n\n      if (typeof config === 'string' && /init|toggle|close|open|search/.test(config)) {\n        plugin[config]();\n      } else {\n        plugin.init();\n      }\n    };\n\n    return SidebarSearch;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_SEARCH_BUTTON, function (event) {\n    event.preventDefault();\n\n    SidebarSearch._jQueryInterface.call($__default[\"default\"](SELECTOR_DATA_WIDGET$1), 'toggle');\n  });\n  $__default[\"default\"](document).on('keyup', SELECTOR_SEARCH_INPUT$1, function (event) {\n    if (event.keyCode == 38) {\n      event.preventDefault();\n      $__default[\"default\"](SELECTOR_SEARCH_RESULTS_GROUP).children().last().focus();\n      return;\n    }\n\n    if (event.keyCode == 40) {\n      event.preventDefault();\n      $__default[\"default\"](SELECTOR_SEARCH_RESULTS_GROUP).children().first().focus();\n      return;\n    }\n\n    setTimeout(function () {\n      SidebarSearch._jQueryInterface.call($__default[\"default\"](SELECTOR_DATA_WIDGET$1), 'search');\n    }, 100);\n  });\n  $__default[\"default\"](document).on('keydown', SELECTOR_SEARCH_RESULTS_GROUP, function (event) {\n    var $focused = $__default[\"default\"](':focus');\n\n    if (event.keyCode == 38) {\n      event.preventDefault();\n\n      if ($focused.is(':first-child')) {\n        $focused.siblings().last().focus();\n      } else {\n        $focused.prev().focus();\n      }\n    }\n\n    if (event.keyCode == 40) {\n      event.preventDefault();\n\n      if ($focused.is(':last-child')) {\n        $focused.siblings().first().focus();\n      } else {\n        $focused.next().focus();\n      }\n    }\n  });\n  $__default[\"default\"](window).on('load', function () {\n    SidebarSearch._jQueryInterface.call($__default[\"default\"](SELECTOR_DATA_WIDGET$1), 'init');\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$4] = SidebarSearch._jQueryInterface;\n  $__default[\"default\"].fn[NAME$4].Constructor = SidebarSearch;\n\n  $__default[\"default\"].fn[NAME$4].noConflict = function () {\n    $__default[\"default\"].fn[NAME$4] = JQUERY_NO_CONFLICT$4;\n    return SidebarSearch._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE NavbarSearch.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$3 = 'NavbarSearch';\n  var DATA_KEY$3 = 'lte.navbar-search';\n  var JQUERY_NO_CONFLICT$3 = $__default[\"default\"].fn[NAME$3];\n  var SELECTOR_TOGGLE_BUTTON = '[data-widget=\"navbar-search\"]';\n  var SELECTOR_SEARCH_BLOCK = '.navbar-search-block';\n  var SELECTOR_SEARCH_INPUT = '.form-control';\n  var CLASS_NAME_OPEN$1 = 'navbar-search-open';\n  var Default$3 = {\n    resetOnClose: true,\n    target: SELECTOR_SEARCH_BLOCK\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var NavbarSearch = /*#__PURE__*/function () {\n    function NavbarSearch(_element, _options) {\n      this._element = _element;\n      this._config = $__default[\"default\"].extend({}, Default$3, _options);\n    } // Public\n\n\n    var _proto = NavbarSearch.prototype;\n\n    _proto.open = function open() {\n      $__default[\"default\"](this._config.target).css('display', 'flex').hide().fadeIn().addClass(CLASS_NAME_OPEN$1);\n      $__default[\"default\"](this._config.target + \" \" + SELECTOR_SEARCH_INPUT).focus();\n    };\n\n    _proto.close = function close() {\n      $__default[\"default\"](this._config.target).fadeOut().removeClass(CLASS_NAME_OPEN$1);\n\n      if (this._config.resetOnClose) {\n        $__default[\"default\"](this._config.target + \" \" + SELECTOR_SEARCH_INPUT).val('');\n      }\n    };\n\n    _proto.toggle = function toggle() {\n      if ($__default[\"default\"](this._config.target).hasClass(CLASS_NAME_OPEN$1)) {\n        this.close();\n      } else {\n        this.open();\n      }\n    } // Static\n    ;\n\n    NavbarSearch._jQueryInterface = function _jQueryInterface(options) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$3);\n\n        var _options = $__default[\"default\"].extend({}, Default$3, $__default[\"default\"](this).data());\n\n        if (!data) {\n          data = new NavbarSearch(this, _options);\n          $__default[\"default\"](this).data(DATA_KEY$3, data);\n        }\n\n        if (!/toggle|close|open/.test(options)) {\n          throw new Error(\"Undefined method \" + options);\n        }\n\n        data[options]();\n      });\n    };\n\n    return NavbarSearch;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](document).on('click', SELECTOR_TOGGLE_BUTTON, function (event) {\n    event.preventDefault();\n    var button = $__default[\"default\"](event.currentTarget);\n\n    if (button.data('widget') !== 'navbar-search') {\n      button = button.closest(SELECTOR_TOGGLE_BUTTON);\n    }\n\n    NavbarSearch._jQueryInterface.call(button, 'toggle');\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$3] = NavbarSearch._jQueryInterface;\n  $__default[\"default\"].fn[NAME$3].Constructor = NavbarSearch;\n\n  $__default[\"default\"].fn[NAME$3].noConflict = function () {\n    $__default[\"default\"].fn[NAME$3] = JQUERY_NO_CONFLICT$3;\n    return NavbarSearch._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE Toasts.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$2 = 'Toasts';\n  var DATA_KEY$2 = 'lte.toasts';\n  var EVENT_KEY$1 = \".\" + DATA_KEY$2;\n  var JQUERY_NO_CONFLICT$2 = $__default[\"default\"].fn[NAME$2];\n  var EVENT_INIT = \"init\" + EVENT_KEY$1;\n  var EVENT_CREATED = \"created\" + EVENT_KEY$1;\n  var EVENT_REMOVED = \"removed\" + EVENT_KEY$1;\n  var SELECTOR_CONTAINER_TOP_RIGHT = '#toastsContainerTopRight';\n  var SELECTOR_CONTAINER_TOP_LEFT = '#toastsContainerTopLeft';\n  var SELECTOR_CONTAINER_BOTTOM_RIGHT = '#toastsContainerBottomRight';\n  var SELECTOR_CONTAINER_BOTTOM_LEFT = '#toastsContainerBottomLeft';\n  var CLASS_NAME_TOP_RIGHT = 'toasts-top-right';\n  var CLASS_NAME_TOP_LEFT = 'toasts-top-left';\n  var CLASS_NAME_BOTTOM_RIGHT = 'toasts-bottom-right';\n  var CLASS_NAME_BOTTOM_LEFT = 'toasts-bottom-left';\n  var POSITION_TOP_RIGHT = 'topRight';\n  var POSITION_TOP_LEFT = 'topLeft';\n  var POSITION_BOTTOM_RIGHT = 'bottomRight';\n  var POSITION_BOTTOM_LEFT = 'bottomLeft';\n  var Default$2 = {\n    position: POSITION_TOP_RIGHT,\n    fixed: true,\n    autohide: false,\n    autoremove: true,\n    delay: 1000,\n    fade: true,\n    icon: null,\n    image: null,\n    imageAlt: null,\n    imageHeight: '25px',\n    title: null,\n    subtitle: null,\n    close: true,\n    body: null,\n    class: null\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var Toasts = /*#__PURE__*/function () {\n    function Toasts(element, config) {\n      this._config = config;\n\n      this._prepareContainer();\n\n      $__default[\"default\"]('body').trigger($__default[\"default\"].Event(EVENT_INIT));\n    } // Public\n\n\n    var _proto = Toasts.prototype;\n\n    _proto.create = function create() {\n      var toast = $__default[\"default\"]('<div class=\"toast\" role=\"alert\" aria-live=\"assertive\" aria-atomic=\"true\"/>');\n      toast.data('autohide', this._config.autohide);\n      toast.data('animation', this._config.fade);\n\n      if (this._config.class) {\n        toast.addClass(this._config.class);\n      }\n\n      if (this._config.delay && this._config.delay != 500) {\n        toast.data('delay', this._config.delay);\n      }\n\n      var toastHeader = $__default[\"default\"]('<div class=\"toast-header\">');\n\n      if (this._config.image != null) {\n        var toastImage = $__default[\"default\"]('<img />').addClass('rounded mr-2').attr('src', this._config.image).attr('alt', this._config.imageAlt);\n\n        if (this._config.imageHeight != null) {\n          toastImage.height(this._config.imageHeight).width('auto');\n        }\n\n        toastHeader.append(toastImage);\n      }\n\n      if (this._config.icon != null) {\n        toastHeader.append($__default[\"default\"]('<i />').addClass('mr-2').addClass(this._config.icon));\n      }\n\n      if (this._config.title != null) {\n        toastHeader.append($__default[\"default\"]('<strong />').addClass('mr-auto').html(this._config.title));\n      }\n\n      if (this._config.subtitle != null) {\n        toastHeader.append($__default[\"default\"]('<small />').html(this._config.subtitle));\n      }\n\n      if (this._config.close == true) {\n        var toastClose = $__default[\"default\"]('<button data-dismiss=\"toast\" />').attr('type', 'button').addClass('ml-2 mb-1 close').attr('aria-label', 'Close').append('<span aria-hidden=\"true\">&times;</span>');\n\n        if (this._config.title == null) {\n          toastClose.toggleClass('ml-2 ml-auto');\n        }\n\n        toastHeader.append(toastClose);\n      }\n\n      toast.append(toastHeader);\n\n      if (this._config.body != null) {\n        toast.append($__default[\"default\"]('<div class=\"toast-body\" />').html(this._config.body));\n      }\n\n      $__default[\"default\"](this._getContainerId()).prepend(toast);\n      var $body = $__default[\"default\"]('body');\n      $body.trigger($__default[\"default\"].Event(EVENT_CREATED));\n      toast.toast('show');\n\n      if (this._config.autoremove) {\n        toast.on('hidden.bs.toast', function () {\n          $__default[\"default\"](this).delay(200).remove();\n          $body.trigger($__default[\"default\"].Event(EVENT_REMOVED));\n        });\n      }\n    } // Static\n    ;\n\n    _proto._getContainerId = function _getContainerId() {\n      if (this._config.position == POSITION_TOP_RIGHT) {\n        return SELECTOR_CONTAINER_TOP_RIGHT;\n      }\n\n      if (this._config.position == POSITION_TOP_LEFT) {\n        return SELECTOR_CONTAINER_TOP_LEFT;\n      }\n\n      if (this._config.position == POSITION_BOTTOM_RIGHT) {\n        return SELECTOR_CONTAINER_BOTTOM_RIGHT;\n      }\n\n      if (this._config.position == POSITION_BOTTOM_LEFT) {\n        return SELECTOR_CONTAINER_BOTTOM_LEFT;\n      }\n    };\n\n    _proto._prepareContainer = function _prepareContainer() {\n      if ($__default[\"default\"](this._getContainerId()).length === 0) {\n        var container = $__default[\"default\"]('<div />').attr('id', this._getContainerId().replace('#', ''));\n\n        if (this._config.position == POSITION_TOP_RIGHT) {\n          container.addClass(CLASS_NAME_TOP_RIGHT);\n        } else if (this._config.position == POSITION_TOP_LEFT) {\n          container.addClass(CLASS_NAME_TOP_LEFT);\n        } else if (this._config.position == POSITION_BOTTOM_RIGHT) {\n          container.addClass(CLASS_NAME_BOTTOM_RIGHT);\n        } else if (this._config.position == POSITION_BOTTOM_LEFT) {\n          container.addClass(CLASS_NAME_BOTTOM_LEFT);\n        }\n\n        $__default[\"default\"]('body').append(container);\n      }\n\n      if (this._config.fixed) {\n        $__default[\"default\"](this._getContainerId()).addClass('fixed');\n      } else {\n        $__default[\"default\"](this._getContainerId()).removeClass('fixed');\n      }\n    } // Static\n    ;\n\n    Toasts._jQueryInterface = function _jQueryInterface(option, config) {\n      return this.each(function () {\n        var _options = $__default[\"default\"].extend({}, Default$2, config);\n\n        var toast = new Toasts($__default[\"default\"](this), _options);\n\n        if (option === 'create') {\n          toast[option]();\n        }\n      });\n    };\n\n    return Toasts;\n  }();\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"].fn[NAME$2] = Toasts._jQueryInterface;\n  $__default[\"default\"].fn[NAME$2].Constructor = Toasts;\n\n  $__default[\"default\"].fn[NAME$2].noConflict = function () {\n    $__default[\"default\"].fn[NAME$2] = JQUERY_NO_CONFLICT$2;\n    return Toasts._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE TodoList.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME$1 = 'TodoList';\n  var DATA_KEY$1 = 'lte.todolist';\n  var JQUERY_NO_CONFLICT$1 = $__default[\"default\"].fn[NAME$1];\n  var SELECTOR_DATA_TOGGLE = '[data-widget=\"todo-list\"]';\n  var CLASS_NAME_TODO_LIST_DONE = 'done';\n  var Default$1 = {\n    onCheck: function onCheck(item) {\n      return item;\n    },\n    onUnCheck: function onUnCheck(item) {\n      return item;\n    }\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var TodoList = /*#__PURE__*/function () {\n    function TodoList(element, config) {\n      this._config = config;\n      this._element = element;\n\n      this._init();\n    } // Public\n\n\n    var _proto = TodoList.prototype;\n\n    _proto.toggle = function toggle(item) {\n      item.parents('li').toggleClass(CLASS_NAME_TODO_LIST_DONE);\n\n      if (!$__default[\"default\"](item).prop('checked')) {\n        this.unCheck($__default[\"default\"](item));\n        return;\n      }\n\n      this.check(item);\n    };\n\n    _proto.check = function check(item) {\n      this._config.onCheck.call(item);\n    };\n\n    _proto.unCheck = function unCheck(item) {\n      this._config.onUnCheck.call(item);\n    } // Private\n    ;\n\n    _proto._init = function _init() {\n      var _this = this;\n\n      var $toggleSelector = this._element;\n      $toggleSelector.find('input:checkbox:checked').parents('li').toggleClass(CLASS_NAME_TODO_LIST_DONE);\n      $toggleSelector.on('change', 'input:checkbox', function (event) {\n        _this.toggle($__default[\"default\"](event.target));\n      });\n    } // Static\n    ;\n\n    TodoList._jQueryInterface = function _jQueryInterface(config) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY$1);\n\n        if (!data) {\n          data = $__default[\"default\"](this).data();\n        }\n\n        var _options = $__default[\"default\"].extend({}, Default$1, typeof config === 'object' ? config : data);\n\n        var plugin = new TodoList($__default[\"default\"](this), _options);\n        $__default[\"default\"](this).data(DATA_KEY$1, typeof config === 'object' ? config : data);\n\n        if (config === 'init') {\n          plugin[config]();\n        }\n      });\n    };\n\n    return TodoList;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](window).on('load', function () {\n    TodoList._jQueryInterface.call($__default[\"default\"](SELECTOR_DATA_TOGGLE));\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME$1] = TodoList._jQueryInterface;\n  $__default[\"default\"].fn[NAME$1].Constructor = TodoList;\n\n  $__default[\"default\"].fn[NAME$1].noConflict = function () {\n    $__default[\"default\"].fn[NAME$1] = JQUERY_NO_CONFLICT$1;\n    return TodoList._jQueryInterface;\n  };\n\n  /**\n   * --------------------------------------------\n   * AdminLTE Treeview.js\n   * License MIT\n   * --------------------------------------------\n   */\n  /**\n   * Constants\n   * ====================================================\n   */\n\n  var NAME = 'Treeview';\n  var DATA_KEY = 'lte.treeview';\n  var EVENT_KEY = \".\" + DATA_KEY;\n  var JQUERY_NO_CONFLICT = $__default[\"default\"].fn[NAME];\n  var EVENT_EXPANDED = \"expanded\" + EVENT_KEY;\n  var EVENT_COLLAPSED = \"collapsed\" + EVENT_KEY;\n  var EVENT_LOAD_DATA_API = \"load\" + EVENT_KEY;\n  var SELECTOR_LI = '.nav-item';\n  var SELECTOR_LINK = '.nav-link';\n  var SELECTOR_TREEVIEW_MENU = '.nav-treeview';\n  var SELECTOR_OPEN = '.menu-open';\n  var SELECTOR_DATA_WIDGET = '[data-widget=\"treeview\"]';\n  var CLASS_NAME_OPEN = 'menu-open';\n  var CLASS_NAME_IS_OPENING = 'menu-is-opening';\n  var CLASS_NAME_SIDEBAR_COLLAPSED = 'sidebar-collapse';\n  var Default = {\n    trigger: SELECTOR_DATA_WIDGET + \" \" + SELECTOR_LINK,\n    animationSpeed: 300,\n    accordion: true,\n    expandSidebar: false,\n    sidebarButtonSelector: '[data-widget=\"pushmenu\"]'\n  };\n  /**\n   * Class Definition\n   * ====================================================\n   */\n\n  var Treeview = /*#__PURE__*/function () {\n    function Treeview(element, config) {\n      this._config = config;\n      this._element = element;\n    } // Public\n\n\n    var _proto = Treeview.prototype;\n\n    _proto.init = function init() {\n      $__default[\"default\"](\"\" + SELECTOR_LI + SELECTOR_OPEN + \" \" + SELECTOR_TREEVIEW_MENU + SELECTOR_OPEN).css('display', 'block');\n\n      this._setupListeners();\n    };\n\n    _proto.expand = function expand(treeviewMenu, parentLi) {\n      var _this = this;\n\n      var expandedEvent = $__default[\"default\"].Event(EVENT_EXPANDED);\n\n      if (this._config.accordion) {\n        var openMenuLi = parentLi.siblings(SELECTOR_OPEN).first();\n        var openTreeview = openMenuLi.find(SELECTOR_TREEVIEW_MENU).first();\n        this.collapse(openTreeview, openMenuLi);\n      }\n\n      parentLi.addClass(CLASS_NAME_IS_OPENING);\n      treeviewMenu.stop().slideDown(this._config.animationSpeed, function () {\n        parentLi.addClass(CLASS_NAME_OPEN);\n        $__default[\"default\"](_this._element).trigger(expandedEvent);\n      });\n\n      if (this._config.expandSidebar) {\n        this._expandSidebar();\n      }\n    };\n\n    _proto.collapse = function collapse(treeviewMenu, parentLi) {\n      var _this2 = this;\n\n      var collapsedEvent = $__default[\"default\"].Event(EVENT_COLLAPSED);\n      parentLi.removeClass(CLASS_NAME_IS_OPENING + \" \" + CLASS_NAME_OPEN);\n      treeviewMenu.stop().slideUp(this._config.animationSpeed, function () {\n        $__default[\"default\"](_this2._element).trigger(collapsedEvent);\n        treeviewMenu.find(SELECTOR_OPEN + \" > \" + SELECTOR_TREEVIEW_MENU).slideUp();\n        treeviewMenu.find(SELECTOR_OPEN).removeClass(CLASS_NAME_IS_OPENING + \" \" + CLASS_NAME_OPEN);\n      });\n    };\n\n    _proto.toggle = function toggle(event) {\n      var $relativeTarget = $__default[\"default\"](event.currentTarget);\n      var $parent = $relativeTarget.parent();\n      var treeviewMenu = $parent.find(\"> \" + SELECTOR_TREEVIEW_MENU);\n\n      if (!treeviewMenu.is(SELECTOR_TREEVIEW_MENU)) {\n        if (!$parent.is(SELECTOR_LI)) {\n          treeviewMenu = $parent.parent().find(\"> \" + SELECTOR_TREEVIEW_MENU);\n        }\n\n        if (!treeviewMenu.is(SELECTOR_TREEVIEW_MENU)) {\n          return;\n        }\n      }\n\n      event.preventDefault();\n      var parentLi = $relativeTarget.parents(SELECTOR_LI).first();\n      var isOpen = parentLi.hasClass(CLASS_NAME_OPEN);\n\n      if (isOpen) {\n        this.collapse($__default[\"default\"](treeviewMenu), parentLi);\n      } else {\n        this.expand($__default[\"default\"](treeviewMenu), parentLi);\n      }\n    } // Private\n    ;\n\n    _proto._setupListeners = function _setupListeners() {\n      var _this3 = this;\n\n      var elementId = this._element.attr('id') !== undefined ? \"#\" + this._element.attr('id') : '';\n      $__default[\"default\"](document).on('click', \"\" + elementId + this._config.trigger, function (event) {\n        _this3.toggle(event);\n      });\n    };\n\n    _proto._expandSidebar = function _expandSidebar() {\n      if ($__default[\"default\"]('body').hasClass(CLASS_NAME_SIDEBAR_COLLAPSED)) {\n        $__default[\"default\"](this._config.sidebarButtonSelector).PushMenu('expand');\n      }\n    } // Static\n    ;\n\n    Treeview._jQueryInterface = function _jQueryInterface(config) {\n      return this.each(function () {\n        var data = $__default[\"default\"](this).data(DATA_KEY);\n\n        var _options = $__default[\"default\"].extend({}, Default, $__default[\"default\"](this).data());\n\n        if (!data) {\n          data = new Treeview($__default[\"default\"](this), _options);\n          $__default[\"default\"](this).data(DATA_KEY, data);\n        }\n\n        if (config === 'init') {\n          data[config]();\n        }\n      });\n    };\n\n    return Treeview;\n  }();\n  /**\n   * Data API\n   * ====================================================\n   */\n\n\n  $__default[\"default\"](window).on(EVENT_LOAD_DATA_API, function () {\n    $__default[\"default\"](SELECTOR_DATA_WIDGET).each(function () {\n      Treeview._jQueryInterface.call($__default[\"default\"](this), 'init');\n    });\n  });\n  /**\n   * jQuery API\n   * ====================================================\n   */\n\n  $__default[\"default\"].fn[NAME] = Treeview._jQueryInterface;\n  $__default[\"default\"].fn[NAME].Constructor = Treeview;\n\n  $__default[\"default\"].fn[NAME].noConflict = function () {\n    $__default[\"default\"].fn[NAME] = JQUERY_NO_CONFLICT;\n    return Treeview._jQueryInterface;\n  };\n\n  exports.CardRefresh = CardRefresh;\n  exports.CardWidget = CardWidget;\n  exports.ControlSidebar = ControlSidebar;\n  exports.DirectChat = DirectChat;\n  exports.Dropdown = Dropdown;\n  exports.ExpandableTable = ExpandableTable;\n  exports.Fullscreen = Fullscreen;\n  exports.IFrame = IFrame;\n  exports.Layout = Layout;\n  exports.NavbarSearch = NavbarSearch;\n  exports.PushMenu = PushMenu;\n  exports.SidebarSearch = SidebarSearch;\n  exports.Toasts = Toasts;\n  exports.TodoList = TodoList;\n  exports.Treeview = Treeview;\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n}));\n//# sourceMappingURL=adminlte.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/dist/js/demo.js",
    "content": "/**\n * AdminLTE Demo Menu\n * ------------------\n * You should not use this file in production.\n * This file is for demo purposes only.\n */\n\n/* eslint-disable camelcase */\n\n(function ($) {\n  'use strict'\n\n  // setTimeout(function () {\n  //   if (window.___browserSync___ === undefined && Number(localStorage.getItem('AdminLTE:Demo:MessageShowed')) < Date.now()) {\n  //     localStorage.setItem('AdminLTE:Demo:MessageShowed', (Date.now()) + (15 * 60 * 1000))\n  //     // eslint-disable-next-line no-alert\n  //     alert('You load AdminLTE\\'s \"demo.js\", \\nthis file is only created for testing purposes!')\n  //   }\n  // }, 1000)\n\n  function capitalizeFirstLetter(string) {\n    return string.charAt(0).toUpperCase() + string.slice(1)\n  }\n\n  function createSkinBlock(colors, callback, noneSelected) {\n    var $block = $('<select />', {\n      class: noneSelected ? 'custom-select mb-3 border-0' : 'custom-select mb-3 text-light border-0 ' + colors[0].replace(/accent-|navbar-/, 'bg-')\n    })\n\n    if (noneSelected) {\n      var $default = $('<option />', {\n        text: 'None Selected'\n      })\n\n      $block.append($default)\n    }\n\n    colors.forEach(function (color) {\n      var $color = $('<option />', {\n        class: (typeof color === 'object' ? color.join(' ') : color).replace('navbar-', 'bg-').replace('accent-', 'bg-'),\n        text: capitalizeFirstLetter((typeof color === 'object' ? color.join(' ') : color).replace(/navbar-|accent-|bg-/, '').replace('-', ' '))\n      })\n\n      $block.append($color)\n    })\n    if (callback) {\n      $block.on('change', callback)\n    }\n\n    return $block\n  }\n\n  var $sidebar = $('.control-sidebar')\n  var $container = $('<div />', {\n    class: 'p-3 control-sidebar-content'\n  })\n\n  $sidebar.append($container)\n\n  // Checkboxes\n\n  $container.append(\n    '<h5>Customize AdminLTE</h5><hr class=\"mb-2\"/>'\n  )\n\n  var $dark_mode_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('dark-mode'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('dark-mode')\n    } else {\n      $('body').removeClass('dark-mode')\n    }\n  })\n  var $dark_mode_container = $('<div />', { class: 'mb-4' }).append($dark_mode_checkbox).append('<span>Dark Mode</span>')\n  $container.append($dark_mode_container)\n\n  $container.append('<h6>Header Options</h6>')\n  var $header_fixed_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('layout-navbar-fixed'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('layout-navbar-fixed')\n    } else {\n      $('body').removeClass('layout-navbar-fixed')\n    }\n  })\n  var $header_fixed_container = $('<div />', { class: 'mb-1' }).append($header_fixed_checkbox).append('<span>Fixed</span>')\n  $container.append($header_fixed_container)\n\n  var $dropdown_legacy_offset_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.main-header').hasClass('dropdown-legacy'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.main-header').addClass('dropdown-legacy')\n    } else {\n      $('.main-header').removeClass('dropdown-legacy')\n    }\n  })\n  var $dropdown_legacy_offset_container = $('<div />', { class: 'mb-1' }).append($dropdown_legacy_offset_checkbox).append('<span>Dropdown Legacy Offset</span>')\n  $container.append($dropdown_legacy_offset_container)\n\n  var $no_border_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.main-header').hasClass('border-bottom-0'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.main-header').addClass('border-bottom-0')\n    } else {\n      $('.main-header').removeClass('border-bottom-0')\n    }\n  })\n  var $no_border_container = $('<div />', { class: 'mb-4' }).append($no_border_checkbox).append('<span>No border</span>')\n  $container.append($no_border_container)\n\n  $container.append('<h6>Sidebar Options</h6>')\n\n  var $sidebar_collapsed_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('sidebar-collapse'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('sidebar-collapse')\n      $(window).trigger('resize')\n    } else {\n      $('body').removeClass('sidebar-collapse')\n      $(window).trigger('resize')\n    }\n  })\n  var $sidebar_collapsed_container = $('<div />', { class: 'mb-1' }).append($sidebar_collapsed_checkbox).append('<span>Collapsed</span>')\n  $container.append($sidebar_collapsed_container)\n\n  $(document).on('collapsed.lte.pushmenu', '[data-widget=\"pushmenu\"]', function () {\n    $sidebar_collapsed_checkbox.prop('checked', true)\n  })\n  $(document).on('shown.lte.pushmenu', '[data-widget=\"pushmenu\"]', function () {\n    $sidebar_collapsed_checkbox.prop('checked', false)\n  })\n\n  var $sidebar_fixed_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('layout-fixed'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('layout-fixed')\n      $(window).trigger('resize')\n    } else {\n      $('body').removeClass('layout-fixed')\n      $(window).trigger('resize')\n    }\n  })\n  var $sidebar_fixed_container = $('<div />', { class: 'mb-1' }).append($sidebar_fixed_checkbox).append('<span>Fixed</span>')\n  $container.append($sidebar_fixed_container)\n\n  var $sidebar_mini_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('sidebar-mini'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('sidebar-mini')\n    } else {\n      $('body').removeClass('sidebar-mini')\n    }\n  })\n  var $sidebar_mini_container = $('<div />', { class: 'mb-1' }).append($sidebar_mini_checkbox).append('<span>Sidebar Mini</span>')\n  $container.append($sidebar_mini_container)\n\n  var $sidebar_mini_md_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('sidebar-mini-md'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('sidebar-mini-md')\n    } else {\n      $('body').removeClass('sidebar-mini-md')\n    }\n  })\n  var $sidebar_mini_md_container = $('<div />', { class: 'mb-1' }).append($sidebar_mini_md_checkbox).append('<span>Sidebar Mini MD</span>')\n  $container.append($sidebar_mini_md_container)\n\n  var $sidebar_mini_xs_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('sidebar-mini-xs'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('sidebar-mini-xs')\n    } else {\n      $('body').removeClass('sidebar-mini-xs')\n    }\n  })\n  var $sidebar_mini_xs_container = $('<div />', { class: 'mb-1' }).append($sidebar_mini_xs_checkbox).append('<span>Sidebar Mini XS</span>')\n  $container.append($sidebar_mini_xs_container)\n\n  var $flat_sidebar_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.nav-sidebar').hasClass('nav-flat'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.nav-sidebar').addClass('nav-flat')\n    } else {\n      $('.nav-sidebar').removeClass('nav-flat')\n    }\n  })\n  var $flat_sidebar_container = $('<div />', { class: 'mb-1' }).append($flat_sidebar_checkbox).append('<span>Nav Flat Style</span>')\n  $container.append($flat_sidebar_container)\n\n  var $legacy_sidebar_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.nav-sidebar').hasClass('nav-legacy'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.nav-sidebar').addClass('nav-legacy')\n    } else {\n      $('.nav-sidebar').removeClass('nav-legacy')\n    }\n  })\n  var $legacy_sidebar_container = $('<div />', { class: 'mb-1' }).append($legacy_sidebar_checkbox).append('<span>Nav Legacy Style</span>')\n  $container.append($legacy_sidebar_container)\n\n  var $compact_sidebar_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.nav-sidebar').hasClass('nav-compact'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.nav-sidebar').addClass('nav-compact')\n    } else {\n      $('.nav-sidebar').removeClass('nav-compact')\n    }\n  })\n  var $compact_sidebar_container = $('<div />', { class: 'mb-1' }).append($compact_sidebar_checkbox).append('<span>Nav Compact</span>')\n  $container.append($compact_sidebar_container)\n\n  var $child_indent_sidebar_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.nav-sidebar').hasClass('nav-child-indent'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.nav-sidebar').addClass('nav-child-indent')\n    } else {\n      $('.nav-sidebar').removeClass('nav-child-indent')\n    }\n  })\n  var $child_indent_sidebar_container = $('<div />', { class: 'mb-1' }).append($child_indent_sidebar_checkbox).append('<span>Nav Child Indent</span>')\n  $container.append($child_indent_sidebar_container)\n\n  var $child_hide_sidebar_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.nav-sidebar').hasClass('nav-collapse-hide-child'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.nav-sidebar').addClass('nav-collapse-hide-child')\n    } else {\n      $('.nav-sidebar').removeClass('nav-collapse-hide-child')\n    }\n  })\n  var $child_hide_sidebar_container = $('<div />', { class: 'mb-1' }).append($child_hide_sidebar_checkbox).append('<span>Nav Child Hide on Collapse</span>')\n  $container.append($child_hide_sidebar_container)\n\n  var mainSidebarChecked = false;\n  if($('.main-sidebar') != undefined && $('.main-sidebar').hasClass('sidebar-no-expand')){\n    mainSidebarChecked = true;\n  }\n\n  var $no_expand_sidebar_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: mainSidebarChecked,\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      if($('.main-sidebar') != undefined){\n        $('.main-sidebar').addClass('sidebar-no-expand')\n      }\n    } else {\n      if($('.main-sidebar') != undefined){\n        $('.main-sidebar').removeClass('sidebar-no-expand')\n      }\n    }\n  })\n  var $no_expand_sidebar_container = $('<div />', { class: 'mb-4' }).append($no_expand_sidebar_checkbox).append('<span>Disable Hover/Focus Auto-Expand</span>')\n  $container.append($no_expand_sidebar_container)\n\n  $container.append('<h6>Footer Options</h6>')\n  var $footer_fixed_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('layout-footer-fixed'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('layout-footer-fixed')\n    } else {\n      $('body').removeClass('layout-footer-fixed')\n    }\n  })\n  var $footer_fixed_container = $('<div />', { class: 'mb-4' }).append($footer_fixed_checkbox).append('<span>Fixed</span>')\n  $container.append($footer_fixed_container)\n\n  $container.append('<h6>Small Text Options</h6>')\n\n  var $text_sm_body_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('body').hasClass('text-sm'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('body').addClass('text-sm')\n    } else {\n      $('body').removeClass('text-sm')\n    }\n  })\n  var $text_sm_body_container = $('<div />', { class: 'mb-1' }).append($text_sm_body_checkbox).append('<span>Body</span>')\n  $container.append($text_sm_body_container)\n\n  var $text_sm_header_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.main-header').hasClass('text-sm'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.main-header').addClass('text-sm')\n    } else {\n      $('.main-header').removeClass('text-sm')\n    }\n  })\n  var $text_sm_header_container = $('<div />', { class: 'mb-1' }).append($text_sm_header_checkbox).append('<span>Navbar</span>')\n  $container.append($text_sm_header_container)\n\n  var $text_sm_brand_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.brand-link').hasClass('text-sm'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.brand-link').addClass('text-sm')\n    } else {\n      $('.brand-link').removeClass('text-sm')\n    }\n  })\n  var $text_sm_brand_container = $('<div />', { class: 'mb-1' }).append($text_sm_brand_checkbox).append('<span>Brand</span>')\n  $container.append($text_sm_brand_container)\n\n  var $text_sm_sidebar_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.nav-sidebar').hasClass('text-sm'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.nav-sidebar').addClass('text-sm')\n    } else {\n      $('.nav-sidebar').removeClass('text-sm')\n    }\n  })\n  var $text_sm_sidebar_container = $('<div />', { class: 'mb-1' }).append($text_sm_sidebar_checkbox).append('<span>Sidebar Nav</span>')\n  $container.append($text_sm_sidebar_container)\n\n  var $text_sm_footer_checkbox = $('<input />', {\n    type: 'checkbox',\n    value: 1,\n    checked: $('.main-footer').hasClass('text-sm'),\n    class: 'mr-1'\n  }).on('click', function () {\n    if ($(this).is(':checked')) {\n      $('.main-footer').addClass('text-sm')\n    } else {\n      $('.main-footer').removeClass('text-sm')\n    }\n  })\n  var $text_sm_footer_container = $('<div />', { class: 'mb-4' }).append($text_sm_footer_checkbox).append('<span>Footer</span>')\n  $container.append($text_sm_footer_container)\n\n  // Color Arrays\n\n  var navbar_dark_skins = [\n    'navbar-primary',\n    'navbar-secondary',\n    'navbar-info',\n    'navbar-success',\n    'navbar-danger',\n    'navbar-indigo',\n    'navbar-purple',\n    'navbar-pink',\n    'navbar-navy',\n    'navbar-lightblue',\n    'navbar-teal',\n    'navbar-cyan',\n    'navbar-dark',\n    'navbar-gray-dark',\n    'navbar-gray'\n  ]\n\n  var navbar_light_skins = [\n    'navbar-light',\n    'navbar-warning',\n    'navbar-white',\n    'navbar-orange'\n  ]\n\n  var sidebar_colors = [\n    'bg-primary',\n    'bg-warning',\n    'bg-info',\n    'bg-danger',\n    'bg-success',\n    'bg-indigo',\n    'bg-lightblue',\n    'bg-navy',\n    'bg-purple',\n    'bg-fuchsia',\n    'bg-pink',\n    'bg-maroon',\n    'bg-orange',\n    'bg-lime',\n    'bg-teal',\n    'bg-olive'\n  ]\n\n  var accent_colors = [\n    'accent-primary',\n    'accent-warning',\n    'accent-info',\n    'accent-danger',\n    'accent-success',\n    'accent-indigo',\n    'accent-lightblue',\n    'accent-navy',\n    'accent-purple',\n    'accent-fuchsia',\n    'accent-pink',\n    'accent-maroon',\n    'accent-orange',\n    'accent-lime',\n    'accent-teal',\n    'accent-olive'\n  ]\n\n  var sidebar_skins = [\n    'sidebar-dark-primary',\n    'sidebar-dark-warning',\n    'sidebar-dark-info',\n    'sidebar-dark-danger',\n    'sidebar-dark-success',\n    'sidebar-dark-indigo',\n    'sidebar-dark-lightblue',\n    'sidebar-dark-navy',\n    'sidebar-dark-purple',\n    'sidebar-dark-fuchsia',\n    'sidebar-dark-pink',\n    'sidebar-dark-maroon',\n    'sidebar-dark-orange',\n    'sidebar-dark-lime',\n    'sidebar-dark-teal',\n    'sidebar-dark-olive',\n    'sidebar-light-primary',\n    'sidebar-light-warning',\n    'sidebar-light-info',\n    'sidebar-light-danger',\n    'sidebar-light-success',\n    'sidebar-light-indigo',\n    'sidebar-light-lightblue',\n    'sidebar-light-navy',\n    'sidebar-light-purple',\n    'sidebar-light-fuchsia',\n    'sidebar-light-pink',\n    'sidebar-light-maroon',\n    'sidebar-light-orange',\n    'sidebar-light-lime',\n    'sidebar-light-teal',\n    'sidebar-light-olive'\n  ]\n\n  // Navbar Variants\n\n  $container.append('<h6>Navbar Variants</h6>')\n\n  var $navbar_variants = $('<div />', {\n    class: 'd-flex'\n  })\n  var navbar_all_colors = navbar_dark_skins.concat(navbar_light_skins)\n  var $navbar_variants_colors = createSkinBlock(navbar_all_colors, function () {\n    var color = $(this).find('option:selected').attr('class')\n    var $main_header = $('.main-header')\n    $main_header.removeClass('navbar-dark').removeClass('navbar-light')\n    navbar_all_colors.forEach(function (color) {\n      $main_header.removeClass(color)\n    })\n\n    $(this).removeClass().addClass('custom-select mb-3 text-light border-0 ')\n\n    if (navbar_dark_skins.indexOf(color) > -1) {\n      $main_header.addClass('navbar-dark')\n      $(this).addClass(color).addClass('text-light')\n    } else {\n      $main_header.addClass('navbar-light')\n      $(this).addClass(color)\n    }\n\n    $main_header.addClass(color)\n  })\n\n  var active_navbar_color = null\n  if($('.main-header') != undefined && $('.main-header')[0] != undefined && $('.main-header')[0].classList != undefined){\n    $('.main-header')[0].classList.forEach(function (className) {\n      if (navbar_all_colors.indexOf(className) > -1 && active_navbar_color === null) {\n        active_navbar_color = className.replace('navbar-', 'bg-')\n      }\n    })\n  }\n\n  $navbar_variants_colors.find('option.' + active_navbar_color).prop('selected', true)\n  $navbar_variants_colors.removeClass().addClass('custom-select mb-3 text-light border-0 ').addClass(active_navbar_color)\n\n  $navbar_variants.append($navbar_variants_colors)\n\n  $container.append($navbar_variants)\n\n  // Sidebar Colors\n\n  $container.append('<h6>Accent Color Variants</h6>')\n  var $accent_variants = $('<div />', {\n    class: 'd-flex'\n  })\n  $container.append($accent_variants)\n  $container.append(createSkinBlock(accent_colors, function () {\n    var color = $(this).find('option:selected').attr('class')\n    var $body = $('body')\n    accent_colors.forEach(function (skin) {\n      $body.removeClass(skin)\n    })\n\n    var accent_color_class = color.replace('bg-', 'accent-')\n\n    $body.addClass(accent_color_class)\n  }, true))\n\n  var active_accent_color = null\n  $('body')[0].classList.forEach(function (className) {\n    if (accent_colors.indexOf(className) > -1 && active_accent_color === null) {\n      active_accent_color = className.replace('navbar-', 'bg-')\n    }\n  })\n\n  // $accent_variants.find('option.' + active_accent_color).prop('selected', true)\n  // $accent_variants.removeClass().addClass('custom-select mb-3 text-light border-0 ').addClass(active_accent_color)\n\n  $container.append('<h6>Dark Sidebar Variants</h6>')\n  var $sidebar_variants_dark = $('<div />', {\n    class: 'd-flex'\n  })\n  $container.append($sidebar_variants_dark)\n  var $sidebar_dark_variants = createSkinBlock(sidebar_colors, function () {\n    var color = $(this).find('option:selected').attr('class')\n    var sidebar_class = 'sidebar-dark-' + color.replace('bg-', '')\n    var $sidebar = $('.main-sidebar')\n    sidebar_skins.forEach(function (skin) {\n      $sidebar.removeClass(skin)\n      $sidebar_light_variants.removeClass(skin.replace('sidebar-dark-', 'bg-')).removeClass('text-light')\n    })\n\n    $(this).removeClass().addClass('custom-select mb-3 text-light border-0').addClass(color)\n\n    $sidebar_light_variants.find('option').prop('selected', false)\n    $sidebar.addClass(sidebar_class)\n    $('.sidebar').removeClass('os-theme-dark').addClass('os-theme-light')\n  }, true)\n  $container.append($sidebar_dark_variants)\n\n  var active_sidebar_dark_color = null\n  if($('.main-sidebar') != undefined && $('.main-sidebar')[0] != undefined && $('.main-sidebar')[0].classList != undefined){\n    $('.main-sidebar')[0].classList.forEach(function (className) {\n      var color = className.replace('sidebar-dark-', 'bg-')\n      if (sidebar_colors.indexOf(color) > -1 && active_sidebar_dark_color === null) {\n        active_sidebar_dark_color = color\n      }\n    })\n  }\n\n  $sidebar_dark_variants.find('option.' + active_sidebar_dark_color).prop('selected', true)\n  $sidebar_dark_variants.removeClass().addClass('custom-select mb-3 text-light border-0 ').addClass(active_sidebar_dark_color)\n\n  $container.append('<h6>Light Sidebar Variants</h6>')\n  var $sidebar_variants_light = $('<div />', {\n    class: 'd-flex'\n  })\n  $container.append($sidebar_variants_light)\n  var $sidebar_light_variants = createSkinBlock(sidebar_colors, function () {\n    var color = $(this).find('option:selected').attr('class')\n    var sidebar_class = 'sidebar-light-' + color.replace('bg-', '')\n    var $sidebar = $('.main-sidebar')\n    sidebar_skins.forEach(function (skin) {\n      $sidebar.removeClass(skin)\n      $sidebar_dark_variants.removeClass(skin.replace('sidebar-light-', 'bg-')).removeClass('text-light')\n    })\n\n    $(this).removeClass().addClass('custom-select mb-3 text-light border-0').addClass(color)\n\n    $sidebar_dark_variants.find('option').prop('selected', false)\n    $sidebar.addClass(sidebar_class)\n    $('.sidebar').removeClass('os-theme-light').addClass('os-theme-dark')\n  }, true)\n  $container.append($sidebar_light_variants)\n\n  var active_sidebar_light_color = null\n  if($('.main-sidebar') != undefined && $('.main-sidebar')[0] != undefined && $('.main-sidebar')[0].classList != undefined) {\n    $('.main-sidebar')[0].classList.forEach(function (className) {\n      var color = className.replace('sidebar-light-', 'bg-')\n      if (sidebar_colors.indexOf(color) > -1 && active_sidebar_light_color === null) {\n        active_sidebar_light_color = color\n      }\n    })\n  }\n\n  if (active_sidebar_light_color !== null) {\n    $sidebar_light_variants.find('option.' + active_sidebar_light_color).prop('selected', true)\n    $sidebar_light_variants.removeClass().addClass('custom-select mb-3 text-light border-0 ').addClass(active_sidebar_light_color)\n  }\n\n  var logo_skins = navbar_all_colors\n  $container.append('<h6>Brand Logo Variants</h6>')\n  var $logo_variants = $('<div />', {\n    class: 'd-flex'\n  })\n  $container.append($logo_variants)\n  var $clear_btn = $('<a />', {\n    href: '#'\n  }).text('clear').on('click', function (e) {\n    e.preventDefault()\n    var $logo = $('.brand-link')\n    logo_skins.forEach(function (skin) {\n      $logo.removeClass(skin)\n    })\n  })\n\n  var $brand_variants = createSkinBlock(logo_skins, function () {\n    var color = $(this).find('option:selected').attr('class')\n    var $logo = $('.brand-link')\n\n    if (color === 'navbar-light' || color === 'navbar-white') {\n      $logo.addClass('text-black')\n    } else {\n      $logo.removeClass('text-black')\n    }\n\n    logo_skins.forEach(function (skin) {\n      $logo.removeClass(skin)\n    })\n\n    if (color) {\n      $(this).removeClass().addClass('custom-select mb-3 border-0').addClass(color).addClass(color !== 'navbar-light' && color !== 'navbar-white' ? 'text-light' : '')\n    } else {\n      $(this).removeClass().addClass('custom-select mb-3 border-0')\n    }\n\n    $logo.addClass(color)\n  }, true).append($clear_btn)\n  $container.append($brand_variants)\n\n  var active_brand_color = null\n  if($('.brand-link') != undefined && $('.brand-link')[0] != undefined && $('.brand-link')[0].classList != undefined){\n    $('.brand-link')[0].classList.forEach(function (className) {\n      if (logo_skins.indexOf(className) > -1 && active_brand_color === null) {\n        active_brand_color = className.replace('navbar-', 'bg-')\n      }\n    })\n  }\n\n  if (active_brand_color) {\n    $brand_variants.find('option.' + active_brand_color).prop('selected', true)\n    $brand_variants.removeClass().addClass('custom-select mb-3 text-light border-0 ').addClass(active_brand_color)\n  }\n})(jQuery)\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/appDeploy.js",
    "content": "/**\n * 生成部署实例列表\n */\nfunction generateDeployInfo(contextPath) {\n\n    /**\n     *  1.获取机器信息\n     */\n    var redisMachines = \"\";\n    var sentinelMachines = \"\";\n    var twemproxyMachines = \"\";\n    var pikaMachines = \"\";\n    var redisMachineNum = 0;\n    // 1.获取不同类型的机器信息\n    if (redisMachines == '') {\n        $(\"#redisMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                redisMachines += this.value + \";\";\n                redisMachineNum++;\n            }\n        });\n    }\n    if (sentinelMachines == '') {\n        $(\"#sentinelMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                sentinelMachines += this.value + \";\";\n            }\n        });\n    }\n    if (twemproxyMachines == '') {\n        $(\"#twemproxyMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                twemproxyMachines += this.value + \";\";\n            }\n        });\n    }\n    if (pikaMachines == '') {\n        $(\"#pikaMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                pikaMachines += this.value + \";\";\n            }\n        });\n    }\n    /**\n     * 2.信息验证:\n     *    2.1 部署机器信息\n     *    2.2 资源验证: maxmeory验证\n     */\n    var maxMemory = $.trim($(\"#maxMemory\").val());\n    if (maxMemory == '' || maxMemory == null) {\n        toastr.error(\"请填写maxMemory内存大小!\");\n        $(\"#maxMemory\").focus();\n        return;\n    }\n    var redisNum = $(\"#redisNum option:selected\").val();\n    if(redisNum == '0' && $(\"#appType\").val() != '2'){\n        toastr.error(\"仅redis cluster支持指定单机单实例部署!\");\n        $(\"#redisNum\").focus();\n        return;\n    }\n\n    if(redisNum == '0' && $(\"#appType\").val() == '2'){\n        if(redisMachineNum < 6 || redisMachineNum % 2 != 0){\n            toastr.error(\"单机单实例部署，请选择大于等于6的偶数台机器!\");\n            $(\"#redisNum\").focus();\n            return;\n        }\n    }\n\n    if ($(\"#appType\").val() == '2' || $(\"#appType\").val() == '6') {  //rediscluster /redis standalone\n        if (redisMachines == '') {\n            toastr.error(\"请选择Redis部署机器!\");\n            $(\"#redisMachineList\").focus();\n            return;\n        }\n    } else if ($(\"#appType\").val() == '5') {  // redis sentinel\n        if (redisMachines == '') {\n            toastr.error(\"请选择Redis部署机器!\");\n            $(\"#redisMachineList\").focus();\n            return;\n        }\n        if (sentinelMachines == '') {\n            toastr.error(\"请选择Sentinel机器机器!\");\n            $(\"#sentinelMachineList\").focus();\n            return;\n        }\n    } else if ($(\"#appType\").val() == '7') { // redis twemproxy\n        if (redisMachines == '') {\n            toastr.error(\"请选择Redis部署机器!\");\n            $(\"#redisMachineList\").focus();\n            return;\n        }\n        if (sentinelMachines == '') {\n            toastr.error(\"请选择Sentinel机器机器!\");\n            $(\"#sentinelMachineList\").focus();\n            return;\n        }\n        if (twemproxyMachines == '') {\n            toastr.error(\"请选择Twemproxy机器机器!\");\n            $(\"#twemproxyMachineList\").focus();\n            return;\n        }\n    } else if ($(\"#appType\").val() == '8') { // pika sentinel\n        if (pikaMachines == '') {\n            toastr.error(\"请选择Pika部署机器!\");\n            $(\"#redisMachineList\").focus();\n            return;\n        }\n        if (sentinelMachines == '') {\n            toastr.error(\"请选择Sentinel机器机器!\");\n            $(\"#sentinelMachineList\").focus();\n            return;\n        }\n    } else if ($(\"#appType\").val() == '9') { // pika twemproxy\n        if (pikaMachines == '') {\n            toastr.error(\"请选择Pika部署机器!\");\n            $(\"#redisMachineList\").focus();\n            return;\n        }\n        if (sentinelMachines == '') {\n            toastr.error(\"请选择Sentinel机器机器!\");\n            $(\"#sentinelMachineList\").focus();\n            return;\n        }\n        if (twemproxyMachines == '') {\n            toastr.error(\"请选择Twemproxy机器机器\");\n            $(\"#twemproxyMachineList\").focus();\n            return;\n        }\n    }\n\n    //清除上次记录\n    $('#tableList tbody tr').empty();\n    $(\"#appDeployText\").val(\"\");\n    $(\"#appDeployInfo\").val(\"\");\n\n    $.post(\n        contextPath + '/manage/app/generateDeployInfo.json',\n        {\n            type: $(\"#appType\").val(),\n            hasSalve: 1,\n            maxMemory: maxMemory,\n            redisNum: $(\"#redisNum option:selected\").val(),\n            sentinelNum: $(\"#sentinelNum option:selected\").val(),\n            pikaNum: $(\"#pikaNum option:selected\").val(),\n            twemproxyNum: $(\"#twemproxyNum option:selected\").val(),\n            redisMachines: redisMachines,\n            sentinelMachines: sentinelMachines,\n            pikaMachines: pikaMachines,\n            twemproxyMachines: twemproxyMachines\n        },\n        function (data) {\n            $(\"#startDeployInfoLabel\").val(\"\");\n            if (data.result != 'success') {\n                toastr.error(\"实例部署异常:\" + data.result);\n            } else {\n                /**\n                 * 显示机器部署详情\n                 */\n                $(\"#selectMachineId\").removeAttr(\"style\");\n                $(\"#clearInfo\").attr(\"style\", \"background:#CCCCCC\");\n                if ($(\"#appType\").val() == 2 || $(\"#appType\").val() == 12) {\n                    $(\"#manualSwitch\").attr(\"style\", \"background:#FF0000\");\n                }\n                var resMachines = data.resMachines;\n                var machineDeployStatMap = data.machineDeployStatMap;\n                resMachines.forEach(function (machine) {\n                    var machineDeployStat = machineDeployStatMap[machine.ip];\n                    $(\"#tableList tbody\").prepend(\n                        '<tr class=\"odd gradeX\">\\n' +\n                        \"   <td> <a target='_blank' href='\" + contextPath + \"/manage/machine/index?tabTag=machine&ipLike=\" + machine.ip + \"'>\" + machine.ip + \"</a>\\n\" +\n                        '   <td>' + machine.instanceNum + \"&nbsp;&nbsp;/&nbsp;&nbsp;\" + machine.cpu + '</td>\\n' +\n                        '   <td> ' + (machine.usedMem / 1024 / 1024 / 1024).toFixed(1) + 'G / <font color=\"#FF0000\">' + (machine.mem - (machine.usedMem / 1024 / 1024 / 1024)).toFixed(1) + 'G </font> / ' + machine.mem + 'G</td>\\n' +\n                        '   <td> - G / - G / - G</td>\\n' +\n                        '   <td>' + machineDeployStat.masterNum + \"&nbsp;&nbsp;/&nbsp;&nbsp;\" + machineDeployStat.slaveNum + \"&nbsp;&nbsp;/&nbsp;&nbsp;\" + machineDeployStat.sentinelNum + \"&nbsp;&nbsp;/&nbsp;&nbsp;\" + machineDeployStat.twemproxyNum + '</td>\\n' +\n                        '   <td>' + machine.realIp + \"/&nbsp;&nbsp;\" + machine.rack + '</td>\\n' +\n                        '</tr>'\n                    )\n                })\n                /**\n                 * 展示部署详情\n                 */\n                var deployInfos = data.deployInfoList;\n                var appType = $(\"#appType\").val();\n                if (appType == 2) {//Redis-cluster\n                    deployInfos.forEach(function (deployInfo) {\n                        var masterIp = deployInfo.masterIp;\n                        var memSize = deployInfo.memSize;\n                        var slaveIp = deployInfo.slaveIp;\n                        appDeployInfo.value = appDeployInfo.value + masterIp + \":\" + memSize + (slaveIp == '' ? \"\\n\" : \":\" + slaveIp + \"\\n\");\n                    });\n                } else if (appType == 5) {//Redis-sentinel\n                    deployInfos.forEach(function (deployInfo) {\n                        if (deployInfo.masterIp != null && deployInfo.masterIp != '') {\n                            var masterIp = deployInfo.masterIp;\n                            var memSize = deployInfo.memSize;\n                            var slaveIp = deployInfo.slaveIp;\n                            appDeployInfo.value = appDeployInfo.value + masterIp + \":\" + memSize + (slaveIp == '' ? \"\\n\" : \":\" + slaveIp + \"\\n\");\n                        } else {\n                            var sentinelIp = deployInfo.sentinelIp;\n                            appDeployInfo.value = appDeployInfo.value + \"sentinel:\" + sentinelIp + \"\\n\";\n                        }\n                    });\n                } else if (appType == 6) {//Redis-standalone\n                    deployInfos.forEach(function (deployInfo) {\n                        var masterIp = deployInfo.masterIp;\n                        var memSize = deployInfo.memSize;\n                        appDeployInfo.value = masterIp + \":\" + memSize;\n                    });\n                } else if (appType == 7) {//Redis twemproxy\n                    deployInfos.forEach(function (deployInfo) {\n                        if (deployInfo.masterIp != null && deployInfo.masterIp != '') {\n                            var masterIp = deployInfo.masterIp;\n                            var memSize = deployInfo.memSize;\n                            var slaveIp = deployInfo.slaveIp;\n                            appDeployInfo.value = appDeployInfo.value + masterIp + \":\" + memSize + (slaveIp == '' ? \"\\n\" : \":\" + slaveIp + \"\\n\");\n                        } else if (deployInfo.sentinelIp != null && deployInfo.sentinelIp != '') {\n                            var sentinelIp = deployInfo.sentinelIp;\n                            appDeployInfo.value = appDeployInfo.value + \"sentinel:\" + sentinelIp + \"\\n\";\n                        } else if (deployInfo.twemproxyIp != null && deployInfo.twemproxyIp != '') {\n                            var twemproxyIp = deployInfo.twemproxyIp;\n                            appDeployInfo.value = appDeployInfo.value + \"twemproxy:\" + twemproxyIp + \"\\n\";\n                        }\n                    });\n                } else if (appType == 8) {//pika sentinel\n                    deployInfos.forEach(function (deployInfo) {\n                        if (deployInfo.masterPikaIp != null && deployInfo.masterPikaIp != '') {\n                            var masterPikaIp = deployInfo.masterPikaIp;\n                            var memSize = deployInfo.memSize;\n                            var slavePikaIp = deployInfo.slavePikaIp;\n                            appDeployInfo.value = appDeployInfo.value + masterPikaIp + \":\" + memSize + (slavePikaIp == '' ? \"\\n\" : \":\" + slavePikaIp + \"\\n\");\n                        } else {\n                            var sentinelIp = deployInfo.sentinelIp;\n                            appDeployInfo.value = appDeployInfo.value + sentinelIp + \"\\n\";\n                        }\n                    });\n                } else if (appType == 9) {//pika twemproxy\n                    deployInfos.forEach(function (deployInfo) {\n                        if (deployInfo.masterPikaIp != null && deployInfo.masterPikaIp != '') {\n                            var masterPikaIp = deployInfo.masterPikaIp;\n                            var memSize = deployInfo.memSize;\n                            var slavePikaIp = deployInfo.slavePikaIp;\n                            appDeployInfo.value = appDeployInfo.value + masterPikaIp + \":\" + memSize + (slavePikaIp == '' ? \"\\n\" : \":\" + slavePikaIp + \"\\n\");\n                        } else if (deployInfo.sentinelIp != null && deployInfo.sentinelIp != '') {\n                            var sentinelIp = deployInfo.sentinelIp;\n                            appDeployInfo.value = appDeployInfo.value + \"sentinel:\" + sentinelIp + \"\\n\";\n                        } else if (deployInfo.twemproxyIp != null && deployInfo.twemproxyIp != '') {\n                            var twemproxyIp = deployInfo.twemproxyIp;\n                            appDeployInfo.value = appDeployInfo.value + \"twemproxy:\" + twemproxyIp + \"\\n\";\n                        }\n                    });\n                }\n            }\n        }\n    );\n}\n\n/**\n * 置空相关信息\n */\nfunction clearinfo() {\n    //清除上次记录\n    $('#tableList tbody tr').empty();\n    $(\"#selectMachineId\").attr(\"style\", \"display:none\");\n    $(\"#appDeployText\").val(\"\");\n    $(\"#appDeployInfo\").val(\"\");\n    $(\"#appDeployInfo\").attr(\"disabled\", \"disabled\");\n    $(\"#clearInfo\").attr(\"style\", \"background:#CCCCCC;display:none;\");\n    $(\"#manualSwitch\").attr(\"style\", \"background:#FF0000;display:none;\");\n}\n\n/**\n * 开启手动编辑\n */\nfunction manualSwitchFunc() {\n    if($(\"#appType\").val() == 2 || $(\"#appType\").val() == 12){\n        $(\"#appDeployInfo\").removeAttr(\"disabled\");\n    }\n}\n\nfunction changePwd(defaultPwd){\n    // 是否设置密码\n    var isSetCustomPwd = $(\"#isSetCustomPwd\").prop(\"checked\");\n    if(isSetCustomPwd){\n        $(\"#md5Password\").removeAttr(\"readonly\");\n    }else{\n        $(\"#md5Password\").val(defaultPwd);\n        $(\"#md5Password\").attr(\"readonly\", true);\n    }\n}\n\nfunction changeToSetConfig(){\n    // 是否设置密码\n    var isSetConfig = $(\"#isSetConfig\").prop(\"checked\");\n    if(isSetConfig){\n        $(\"#setConfigId\").removeAttr(\"style\");\n    }else{\n        $(\"#setConfigId\").attr(\"style\", \"display:none\");\n    }\n}\n\n/**\n * 添加应用部署任务\n */\nfunction addAppDeployTask(contextPath) {\n\n    // 是否设置密码\n    var customPassword = null;\n    var isSetCustomPwd = $(\"#isSetCustomPwd\").prop(\"checked\");\n    if(isSetCustomPwd){\n        customPassword = $(\"#md5Password\").val();\n        if(customPassword == ''){\n            toastr.error(\"请填写自定义密码!\");\n            $(\"#md5Password\").focus();\n            return;\n        }\n    }\n    var isSetConfig = $(\"#isSetConfig\").prop(\"checked\");\n    var configInfo = \"\";\n    if(isSetConfig){\n        var pointedConfig1 = $(\"#pointedConfig1\").val();\n        var pointedConfig2 = $(\"#pointedConfig2\").val();\n        if(pointedConfig1 == '' || pointedConfig2 == ''){\n            toastr.error(\"请填写指定配置；如不指定，请取消勾选!\");\n            $(\"#pointedConfig1\").focus();\n            return;\n        }\n        configInfo += \"rocks.blockcachemb:\" + pointedConfig1 + \";\";\n        configInfo += \"rocks.write_buffer_size:\" + (pointedConfig2 * 1024 * 1024) + \";\";\n    }\n    var isSetPasswd = 0;\n    if(isSetCustomPwd){\n        isSetPasswd = 1;\n    }else{\n        isSetPasswd = 0;\n    }\n    var maxMemory = $.trim($(\"#maxMemory\").val());\n    if (maxMemory == '' || maxMemory == null) {\n        toastr.error(\"请填写maxMemory内存大小!\");\n        $(\"#maxMemory\").focus();\n        return;\n    }\n    // 检查部署预览是否为空\n    if ($(\"#appDeployInfo\").val() == '') {\n        toastr.error('请先生成部署预览!');\n        return;\n    }\n\n    var redisMachines = \"\";\n    var sentinelMachines = \"\";\n    var twemproxyMachines = \"\";\n    var pikaMachines = \"\";\n    // 1.获取不同类型的机器信息\n    if (redisMachines == '') {\n        $(\"#redisMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                redisMachines += this.value + \";\";\n            }\n        });\n    }\n    if (sentinelMachines == '') {\n        $(\"#sentinelMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                sentinelMachines += this.value + \";\";\n            }\n        });\n    }\n    if (twemproxyMachines == '') {\n        $(\"#twemproxyMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                twemproxyMachines += this.value + \";\";\n            }\n        });\n    }\n    if (pikaMachines == '') {\n        $(\"#pikaMachineList option:selected\").each(function () {\n            if (this.value != '') {\n                pikaMachines += this.value + \";\";\n            }\n        });\n    }\n    // 获取原始字符串\n    var originalValue = $(\"#appDeployInfo\").val();\n\n    // 使用换行符分割字符串，得到一组主机信息\n    var hostInfoArray = originalValue.split('\\n');\n\n    // 初始化一个 Set 来存储不同的主机IP\n    var uniqueHostIPs = new Set();\n\n    // 遍历主机信息数组，提取每个主机的IP地址\n    for (var i = 0; i < hostInfoArray.length; i++) {\n        var hostInfo = hostInfoArray[i];\n        // 使用冒号分割每行信息\n        var parts = hostInfo.split(':');\n        // 确保有足够的部分\n        var firstIP = parts[0];\n        uniqueHostIPs.add(firstIP);\n        if (parts.length >= 3) {\n            var thirdIP = parts[2];\n            uniqueHostIPs.add(thirdIP);\n        }\n     }\n     var appType = $(\"#appType\").val();\n     if(appType == 2){\n        redisMachines = Array.from(uniqueHostIPs).join(';');\n     }\n\n    $.post(\n        contextPath + '/manage/app/addAppDeployTask.json',\n        {\n            appid: $(\"#hiddenAppId\").val(),\n            appAuditId: $(\"#appAuditId\").val(),\n            isSetPasswd: isSetPasswd,\n            versionId: $(\"#versionId option:selected\").attr(\"versionid\"),\n            importantLevel: $(\"#importantLevel option:selected\").val(),\n            type: $(\"#appType\").val(), // 根据实际选用类型来确认最终部署类型\n            maxMemory: maxMemory,\n            redisNum: $(\"#redisNum option:selected\").val(),\n            sentinelNum: $(\"#sentinelNum option:selected\").val(),\n            pikaNum: $(\"#pikaNum option:selected\").val(),\n            twemproxyNum: $(\"#twemproxyNum option:selected\").val(),\n            appDeployInfo: $(\"#appDeployInfo\").val(),\n            redisMachines: redisMachines,\n            sentinelMachines: sentinelMachines,\n            pikaMachines: pikaMachines,\n            twemproxyMachines: twemproxyMachines,\n            customPassword:customPassword,\n            pointedConfig:configInfo\n        },\n        function (data) {\n            var status = data.status;\n            if (status == 'success') {\n                $('#appDeployBtn').attr(\"disabled\", true);\n                toastr.success(data.message);\n                console.log(\"updateForImport\");\n                updateForImport(data.taskid);\n                setTimeout(function(){reloadAppStatPage(data.taskid, contextPath)}, 2000);\n            } else {\n                toastr.error(\"应用部署失败,请查看系统日志确认相关原因!\");\n            }\n        }\n    );\n}\n\n//重新加载appDetail页面\nfunction reloadAppStatPage(taskid, contextPath) {\n    location.href = contextPath + \"/manage/task/flow?taskId=\" + taskid;\n}\n\nfunction updateForImport(taskId) {\n    var importId = document.getElementById(\"importId\");\n    if (importId != null && importId.value != '') {\n        $.get(\n            '/import/app/goOn.json',\n            {\n                importId: importId.value,\n                appBuildTaskId: taskId,\n            },\n            function (data) {\n                var success = data.success;\n                if (success == 1) {\n                    console.log(\"updateForImport success\");\n                }\n            }\n        );\n    }\n}\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/appDetail.js",
    "content": "//重新加载appDetail页面\nfunction reloadAppDetailPage(appId, contextPath) {\n    location.href = contextPath + \"/admin/app/index?appId=\" + appId + \"&tabTag=app_detail\";\n}\n\n//appDetail页面删除用户\nfunction deleteAppUser(userId, appId, contextPath) {\n    if (window.confirm('确认要删除该用户吗?!')) {\n        var url = contextPath + \"/admin/app/deleteAppToUser?userId=\" + userId + \"&appId=\" + appId;\n        $.ajax({\n            type: \"get\",\n            url: url,\n            async: false,\n            success: function (data) {\n                alert(\"删除成功\");\n                reloadAppDetailPage(appId, contextPath);\n            }\n        });\n    }\n    return false;\n}\n\n\n//改变应用信息\nfunction updateAppDetailChange(appId, contextPath) {\n    var appDescName = document.getElementById(\"appDescName\");\n    if (appDescName.value == \"\") {\n        alert(\"应用名不能为空\");\n        appDescName.focus();\n        return false;\n    }\n    var appDescIntro = document.getElementById(\"appDescIntro\");\n    if (appDescIntro.value == \"\") {\n        alert(\"应用描述不能为空\");\n        appDescIntro.focus();\n        return false;\n    }\n    var officer =  $('#officer_select').selectpicker('val');\n    if (officer == null || officer.length == 0) {\n        alert(\"负责人不能为空\");\n        document.getElementById(\"officer_select\").focus();\n        return false;\n    }\n    var updateAppDetailBtn = document.getElementById(\"updateAppDetailBtn\");\n    updateAppDetailBtn.disabled = true;\n    $.post(\n        contextPath + '/admin/app/updateAppDetail',\n        {\n            appId: appId,\n            appDescName: appDescName.value,\n            appDescIntro: appDescIntro.value,\n            officer: officer.toString()\n        },\n        function (data) {\n            if (data == 1) {\n                alert(\"修改成功！\");\n                $(\"#updateAppDetailInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                setTimeout(function(){reloadAppDetailPage(appId, contextPath)}, 1000);\n            } else {\n                updateAppDetailBtn.disabled = false;\n                $(\"#updateAppDetailInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n    );\n}\n\n\n//改变内存阀值\nfunction appAlertConfigChange(appId, contextPath) {\n    var memAlertValue = document.getElementById(\"memAlertValue\");\n    if (memAlertValue.value == \"\") {\n        alert(\"内存报警阀值不能为空\");\n        memAlertValue.focus();\n        return false;\n    }\n    var clientConnAlertValue = document.getElementById(\"clientConnAlertValue\");\n    if (clientConnAlertValue.value == \"\") {\n        alert(\"客户端连接数报警阀值不能为空\");\n        clientConnAlertValue.focus();\n        return false;\n    }\n    var hitPrecentAlertValue = document.getElementById(\"hitPrecentAlertValue\");\n    if (hitPrecentAlertValue.value == \"\") {\n        alert(\"应用平均命中率报警阀值不能为空\");\n        hitPrecentAlertValue.focus();\n        return false;\n    }\n    var isAccessMonitor = jQuery(\"#isAccessMonitor option:selected\");\n    if (isAccessMonitor.attr(\"value\") == \"\") {\n        alert(\"应用全局报警不能为空\");\n        isAccessMonitor.focus();\n        return false;\n    }\n    var appConfigChangeBtn = document.getElementById(\"appConfigChangeBtn\");\n    appConfigChangeBtn.disabled = true;\n    $.post(\n        contextPath + '/admin/app/changeAppAlertConfig',\n        {\n            appId: appId,\n            memAlertValue: memAlertValue.value,\n            clientConnAlertValue: clientConnAlertValue.value,\n            hitPrecentAlertValue: hitPrecentAlertValue.value,\n            isAccessMonitor: isAccessMonitor.attr(\"value\")\n        },\n        function (data) {\n            if (data == 1) {\n                alert(\"修改成功！\");\n                $(\"#appConfigChangeInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                setTimeout(function(){reloadAppDetailPage(appId, contextPath)}, 1000);\n            } else {\n                appConfigChangeBtn.disabled = false;\n                $(\"#appConfigChangeInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n    );\n}\n\nfunction updateWholeAlertConfigChange(appId, contextPath) {\n    var appConfigKey = document.getElementById(\"appMonitorConfigKey\");\n    if (appConfigKey.value == \"\") {\n        alert(\"配置项不能为空\");\n        appConfigKey.focus();\n        return false;\n    }\n\n    var appConfigValue = document.getElementById(\"appMonitorConfigValue\");\n    if (appConfigValue.value == \"\") {\n        alert(\"配置值不能为空\");\n        appConfigValue.focus();\n        return false;\n    }\n\n    var appConfigReason = document.getElementById(\"appMonitorConfigReason\");\n    if (appConfigReason.value == \"\") {\n        alert(\"配置原因不能为空\");\n        appConfigReason.focus();\n        return false;\n    }\n\n    var updateConfigChangeBtn = document.getElementById(\"updateConfigChangeBtn\");\n    updateConfigChangeBtn.disabled = true;\n\n    $.post(\n        contextPath + '/admin/app/changeAppMonitorConfig',\n        {\n            appId: appId,\n            instanceId: \"\",\n            appConfigKey: appConfigKey.value,\n            appConfigValue: appConfigValue.value,\n            appConfigReason: appConfigReason.value\n        },\n        function (data) {\n            if (data == 1) {\n                alert(\"申请成功，请在邮件中关注申请状况.\");\n                $(\"#updateConfigChangeInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                setTimeout(function(){reloadAppStatPage(appId, contextPath)}, 1000);\n\n            } else {\n                appConfigChangeBtn.disabled = false;\n                $(\"#updateConfigChangeInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n    );\n}\n\n//添加应用用户\nfunction appAddUser(appId, contextPath) {\n    var addAppToUser = $('#addAppToUser').selectpicker('val');\n    if (addAppToUser == null || addAppToUser.length == 0) {\n        alert(\"用户名不能为空\");\n        return false;\n    }\n    var appAddUserBtn = document.getElementById(\"appAddUserBtn\");\n    appAddUserBtn.disabled = true;\n\n    $.post(\n        contextPath + '/admin/app/addAppToUser',\n        {\n            appId: appId,\n            users: addAppToUser.toString()\n        },\n        function (data) {\n            if (data == 1) {\n                alert(\"用户添加成功!\");\n                $(\"#appAddUserInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                setTimeout(function(){reloadAppDetailPage(appId, contextPath)}, 1000);\n            } else {\n                appAddUserBtn.disabled = false;\n                alert(\"cachecloud中不存在该用户，只能添加有cachecloud权限的用户\");\n                $(\"#appAddUserInfo\").html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Error!</strong>添加失败,cachecloud中不存在该用户，只能添加有cachecloud权限的用户！</div>\");\n            }\n        }\n    );\n}\n\n//验证手机号格式\nvar valPhones = /^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$/;\n//验证邮箱格式\nvar valEmails = /^(([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.|\\-]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3};){0,6}([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.|\\-]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$/;\n\nfunction saveOrUpdateUser(userId, appId, contextPath) {\n    var name = document.getElementById(\"name\" + userId);\n    var chName = document.getElementById(\"chName\" + userId);\n    var email = document.getElementById(\"email\" + userId);\n    var mobile = document.getElementById(\"mobile\" + userId);\n    var weChat = document.getElementById(\"weChat\" + userId);\n    var type = document.getElementById(\"type\" + userId);\n    var isAlert = document.getElementById(\"isAlert\" + userId);\n    var bizId = document.getElementById(\"bizId\" + userId);\n    var company = \"\";\n    var purpose = \"\";\n    if (name.value == \"\") {\n        alert(\"域账户名不能为空!\");\n        name.focus();\n        return false;\n    }\n    if (chName.value == \"\") {\n        alert(\"中文名不能为空!\");\n        chName.focus();\n        return false;\n    }\n    if (email.value == \"\") {\n        alert(\"邮箱不能为空!\");\n        email.focus();\n        return false;\n    }\n    if (!valEmails.test(email.value)) {\n        alert(\"邮箱格式错误!\");\n        email.focus();\n        return false;\n    }\n    if (mobile.value == \"\") {\n        alert(\"手机号不能为空!\");\n        mobile.focus();\n        return false;\n    }\n    if (!valPhones.test(mobile.value)) {\n        alert(\"手机号格式错误!\");\n        mobile.focus();\n        return false;\n    }\n    if (weChat.value == \"\") {\n        alert(\"微信不能为空!\");\n        weChat.focus();\n        return false;\n    }\n    $.post(\n        contextPath + '/admin/app/changeAppUserInfo',\n        {\n            name: name.value,\n            chName: chName.value,\n            email: email.value,\n            mobile: mobile.value,\n            weChat: weChat.value,\n            type: type.value,\n            isAlert: isAlert.value,\n            company: company,\n            purpose: purpose,\n            bizId: bizId.value,\n            userId: userId\n        },\n        function (data) {\n            if (data == 1) {\n                $(\"#info\" + userId).html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                var targetId = \"#addUserModal\" + userId;\n                setTimeout(function(){reloadAppDetailPage(appId, contextPath)}, 1000);\n            } else {\n                $(\"#info\" + userId).html(\"<div class='alert alert-error' ><button class='close' data-bs-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n    );\n}\n\n\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/appInit.js",
    "content": "\n//验证是数字\nfunction testisNum(id){\n   var value =document.getElementById(id).value;\n   if(value != \"\" && isNaN(value)){\n      alert(\"请输入数字类型!\");\n      document.getElementById(id).value=\"\";\n      document.getElementById(id).focus();\n   }\n}\n\n//保存应用\nfunction saveAppDesc(){\n\t//应用名\n\tvar appName = document.getElementById(\"appName\");\n\tif(appName.value == \"\"){\n\t\talert(\"应用名不能为空\");\n\t\tappName.focus();\n\t\treturn false;\n\t}\n\t\n\tvar appExist = document.getElementById(\"appExist\");\n\tif(appExist.value == 1){\n\t\talert(\"应用名已经存在,请修改!\");\n\t\tappName.focus();\n\t\treturn false;\n\t}\n\t\n\t//应用描述\n\tvar appIntro = document.getElementById(\"appIntro\");\n\tif(appIntro.value == \"\"){\n\t\talert(\"应用描述不能为空\");\n\t\tappIntro.focus();\n\t\treturn false;\n\t}\n\t\n\t//应用描述\n\tvar memSize = document.getElementById(\"memSize\");\n\tif(memSize.value == \"\"){\n\t\talert(\"内存容量不能为空\");\n\t\tmemSize.focus();\n\t\treturn false;\n\t}\n    if(isNaN(memSize.value)){\n        alert(\"内存总量，请填写数字\");\n        memSize.focus();\n        return false;\n    }\n\t\n\t//项目负责人\n\tvar officer = document.getElementById(\"officer\");\n\tif(officer.value == \"\"){\n\t\talert(\"项目负责人不能为空\");\n\t\tofficer.focus();\n\t\treturn false;\n\t}\n\n\t//redis版本\n\tvar versionId = document.getElementById(\"versionId\");\n\tif(versionId.value == \"-1\"){\n\t\tvar versionName = document.getElementById(\"versionName\");\n\t\tif(versionName.value == \"\"){\n\t\t\talert(\"Redis部署版本不能为空\");\n\t\t\tversionId.focus();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t//预估QPS\n\tvar forecaseQps = document.getElementById(\"forecaseQps\");\n\tif(forecaseQps.value == \"\"){\n\t\talert(\"预估QPS不能为空\");\n\t\tforecaseQps.focus();\n\t\treturn false;\n\t}\n\t\n\t//预估条目数量\n\tvar forecastObjNum = document.getElementById(\"forecastObjNum\");\n\tif(forecastObjNum.value == \"\"){\n\t\talert(\"预估条目数量不能为空\");\n\t\tforecastObjNum.focus();\n\t\treturn false;\n\t}\n\t\n\t//客户端机房信息\n\tvar clientMachineRoom = document.getElementById(\"clientMachineRoom\");\n\tif(clientMachineRoom.value == \"\"){\n\t\talert(\"客户端机房信息不能为空\");\n\t\tclientMachineRoom.focus();\n\t\treturn false;\n\t}\n\t\n\t//内存报警阀值\n\tvar memAlertValue = document.getElementById(\"memAlertValue\");\n\tif(memAlertValue.value == \"\"){\n\t\talert(\"内存报警阀值不能为空\");\n\t\tmemAlertValue.focus();\n\t\treturn false;\n\t}\n\t\n\t//客户端连接数报警阀值\n\tvar clientConnAlertValue = document.getElementById(\"clientConnAlertValue\");\n\tif(clientConnAlertValue.value == \"\"){\n\t\talert(\"客户端连接数报警阀值不能为空\");\n\t\tclientConnAlertValue.focus();\n\t\treturn false;\n\t}\n\t\n\treturn true;\t\n}\n\n//查看应用名是否已经存在\nfunction checkAppNameExist(contextPath){\n\tvar appName = document.getElementById(\"appName\").value;\n\tif(appName != ''){\n\t\t$.post(\n\t\t\tcontextPath + '/admin/app/checkAppNameExist',\n\t\t\t{\n\t\t\t\tappName: appName,\n\t\t\t},\n\t        function(data){\n\t            if(data==1){\n\t            \talert(\"应用名已经存在，请修改\");\n\t            \tdocument.getElementById(\"appName\").focus();\n\t            \tdocument.getElementById(\"appExist\").value = 1;\n\t            }else{\n\t            \tdocument.getElementById(\"appExist\").value = 0;\n\t            }\n\t        }\n\t     );\n\t}\n}\n\n//判断内存总量是否为数字\nfunction checkMemSizeNum() {\n\tvar memSize = document.getElementById('memSize');\n\tif(isNaN(memSize.value)){\n        alert(\"内存总量，请填写数字\");\n\t}\n\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/auditManage.js",
    "content": "//驳回应用请求\nfunction appRefuse(appAuditId, type, contextPath){\n\t//驳回\n\tvar status = -1;\n\t//驳回理由\n\tvar refuseReason = document.getElementById(\"refuseReason\" + appAuditId);\n\tif(refuseReason.value == \"\"){\n\t\talert(\"驳回理由不能为空\");\n\t\trefuseReason.focus();\n\t\treturn false;\n\t}\n\t\n\tvar appRefuseBtn = document.getElementById(\"appRefuseBtn\" + appAuditId);\n\tappRefuseBtn.disabled = true;\n\t\n\tvar url = \"\";\n\tif(type == 3){\n\t\turl = contextPath + \"/manage/user/addAuditStatus\";\n\t}else {\n\t\turl = contextPath + \"/manage/app/addAuditStatus\";\n\t}\n\t$.post(\n\t\turl,\n\t\t{\n\t\t\tappAuditId: appAuditId,\n\t\t\trefuseReason: refuseReason.value,\n\t\t\tstatus: status\n\t\t},\n        function(data){\n            if(data==1){\n            \t$(\"#appRefuseInfo\"+appAuditId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                $('appRefuseModal'+appAuditId).modal('hide');\n            \tsetTimeout(\"window.location.reload()\",1000);\n            }else{\n            \tappRefuseBtn.disabled = false;\n                $(\"#appRefuseInfo\"+appAuditId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n     );\n}\n\n//检查配置项\nfunction checkAppConfig(){\n\t//配置项\n\tvar appConfigKey = document.getElementById(\"appConfigKey\");\n\tif(appConfigKey.value == \"\"){\n\t\talert(\"配置项不能为空\");\n\t\tappConfigKey.focus();\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n//检查配置项\nfunction checkInstanceConfig(){\n\t//配置项\n\tvar instanceConfigKey = document.getElementById(\"instanceConfigKey\");\n\tif(instanceConfigKey.value == \"\"){\n\t\talert(\"配置项不能为空\");\n\t\tinstanceConfigKey.focus();\n\t\treturn false;\n\t}\n\t\n\t//配置值\n\tvar instanceConfigValue = document.getElementById(\"instanceConfigValue\");\n\tif(instanceConfigValue.value == \"\"){\n\t\talert(\"配置值不能为空\");\n\t\tinstanceConfigValue.focus();\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n//检查扩容配置\nfunction checkAppScaleText(){\n\tvar appScaleText = document.getElementById(\"appScaleText\");\n\tif(appScaleText.value == \"\"){\n\t\talert(\"配置不能为空\");\n\t\tappScaleText.focus();\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction startShowDeployLabel(){\n\tvar startDeployLabel = document.getElementById(\"startDeployLabel\");\n\tstartDeployLabel.innerHTML += '.';\n}\n\nfunction testisNum(id){\n   var value =document.getElementById(id).value;\n   if(value != \"\" && isNaN(value)){\n      alert(\"请输入数字类型!\");\n      document.getElementById(id).value=\"\";\n      document.getElementById(id).focus();\n   }\n}\n\n//添加水平扩容验证\nfunction checkHorizontalScale(contextPath){\n\tvar sourceId = document.getElementById(\"sourceId\");\n\tif(sourceId.value == \"\"){\n\t\talert(\"源实例ID不能为空\");\n\t\tsourceId.focus();\n\t\treturn false;\n\t}\n\t\n\tvar targetId = document.getElementById(\"targetId\");\n\tif(targetId.value == \"\"){\n\t\talert(\"目标实例ID不能为空\");\n\t\ttargetId.focus();\n\t\treturn false;\n\t}\n\t\n\tvar startSlot = document.getElementById(\"startSlot\");\n\tif(startSlot.value == \"\"){\n\t\talert(\"开始slot不能为空\");\n\t\tstartSlot.focus();\n\t\treturn false;\n\t}\n\t\n\tvar endSlot = document.getElementById(\"endSlot\");\n\tif(endSlot.value == \"\"){\n\t\talert(\"结束slot不能为空\");\n\t\tendSlot.focus();\n\t\treturn false;\n\t}\n\t\n\tvar migrateType = document.getElementById(\"migrateType\");\n\t\n\tvar appId = document.getElementById(\"appId\");\n\tvar appAuditId = document.getElementById(\"appAuditId\");\n\t$.get(\n\t\tcontextPath + '/manage/app/checkHorizontalScale.json',\n\t\t{\n\t\t\tsourceId: sourceId.value,\n\t\t\ttargetId: targetId.value,\n\t\t\tstartSlot: startSlot.value,\n\t\t\tendSlot: endSlot.value,\n\t\t\tappId: appId.value,\n\t\t\tappAuditId: appAuditId.value,\n\t\t\tmigrateType: migrateType.value\n\t\t},\n        function(data){\n\t\t\tvar status = data.status;\n\t\t\talert(data.message);\n\t\t\tif (status == 1) {\n\t\t\t\tvar submitButton = document.getElementById(\"submitButton\");\n\t    \t\tsubmitButton.disabled = false;\n\t    \t\t\n\t    \t\tvar checkButton = document.getElementById(\"checkButton\");\n\t    \t\tcheckButton.disabled = true;\n\t    \t\tsourceId.disabled = true;\n\t    \t\ttargetId.disabled = true;\n\t    \t\tstartSlot.disabled = true;\n\t    \t\tendSlot.disabled = true;\n\t    \t\tmigrateType.disabled = true;\n\t\t\t}\n        }\n     );\n\treturn true;\n}\n\n\n//开始水平扩容\nfunction startHorizontalScale(contextPath){\n\tvar sourceId = document.getElementById(\"sourceId\");\n\tvar targetId = document.getElementById(\"targetId\");\n\tvar startSlot = document.getElementById(\"startSlot\");\n\tvar endSlot = document.getElementById(\"endSlot\");\n\tvar appId = document.getElementById(\"appId\");\n\tvar appAuditId = document.getElementById(\"appAuditId\");\n\tvar migrateType = document.getElementById(\"migrateType\");\n\t\n\tvar submitButton = document.getElementById(\"submitButton\");\n    submitButton.disabled = true;\n\t\n\t$.get(\n\t\tcontextPath + '/manage/app/startHorizontalScale.json',\n\t\t{\n\t\t\tsourceId: sourceId.value,\n\t\t\ttargetId: targetId.value,\n\t\t\tstartSlot: startSlot.value,\n\t\t\tendSlot: endSlot.value,\n\t\t\tappId: appId.value,\n\t\t\tappAuditId: appAuditId.value,\n\t\t\tmigrateType: migrateType.value\n\t\t},\n        function(data){\n\t\t\tvar status = data.status;\n\t\t\talert(data.message);\n\t\t\tif (status == 1) {\n\t\t\t\twindow.location.href= contextPath + \"/manage/app/handleHorizontalScale?appAuditId=\" + appAuditId.value;\n\t\t\t}\n        }\n     );\n\treturn true;\n}\n\n//重试水平扩容\nfunction retryHorizontalScale(instanceReshardProcessId, contextPath){\n\tvar appAuditId = document.getElementById(\"appAuditId\");\n\tvar retryButton = document.getElementById(\"retryBtn\" + instanceReshardProcessId);\n\tinstanceReshardProcessId.disabled = true;\n\t$.get(\n\t\tcontextPath + '/manage/app/retryHorizontalScale.json',\n\t\t{\n\t\t\tinstanceReshardProcessId: instanceReshardProcessId\n\t\t},\n        function(data){\n\t\t\tvar status = data.status;\n\t\t\talert(data.message);\n\t\t\tif (status == 1) {\n\t\t\t\twindow.location.href= contextPath + \"/manage/app/handleHorizontalScale?appAuditId=\" + appAuditId.value;\n\t\t\t}\n        }\n     );\n\treturn true;\n}\n\n// step forward method\nvar method = \"appLevel\";\n/**\n * 继续/下一步 step forward\n */\nfunction goOn(m){\n    //$(\"#install\").html(\"继续\");\n    method = m;\n}\n/**\n *  跳过步骤\n */\nfunction skip(currentMethod,nextMethod){\n    active(currentMethod);\n    complete(currentMethod);\n    goOn(nextMethod);\n    $(\"#skipbutton\").attr(\"style\",\"display:none;\");\n}\n\nfunction warn(id){\n    $(\"#\"+id).addClass(\"warn\");\n}\nfunction disable(id){\n    $(\"#\"+id).removeClass(\"active\").addClass(\"disabled\");\n}\nfunction active(id){\n    $(\"#\"+id).removeClass(\"disabled\").removeClass(\"warn\").addClass(\"active\");\n}\nfunction complete(id){\n    $(\"#\"+id).removeClass(\"active\").removeClass(\"warn\").addClass(\"complete\");\n}\n\n/**\n * 1.应用评级\n */\nfunction appLevel(){\n    complete(\"appLevel\");\n    $('#appLevelDiv').attr(\"style\",\"display:none;\");\n    $('#redisVersionDiv').attr(\"style\",\"display:'';\");\n    active('redisVersion');\n    goOn(\"redisVersion\");\n}\n\n\n/**\n * 2.redis版本\n */\nfunction redisVersion() {\n    complete(\"redisVersion\");\n    $('#redisVersionDiv').attr(\"style\",\"display:none;\");\n    $('#appDeployDiv').attr(\"style\",\"display:'';\");\n    active('appDeploy');\n    goOn(\"appDeploy\");\n}\n\n/**\n * 3.应用部署\n */\nfunction appDeploy() {\n\tif($('#appDeployBtn').html()!='部署完成'){\n        toastr.error(\"请完成应用部署操作\");\n\t\twarn('appDeploy');\n\t\treturn;\n\t}\n    complete(\"appDeploy\");\n    $('#appDeployDiv').attr(\"style\",\"display:none;\");\n    $('#appPasswordDiv').attr(\"style\",\"display:'';\");\n    active('appPassword');\n    goOn(\"appPassword\");\n}\n\n/**\n * 4.应用密码\n */\nfunction appPassword() {\n    if($('#passUpdateBtn').html()!='设置成功'){\n        toastr.error(\"请设置redis密码\");\n        warn('appPassword');\n        return;\n    }\n    complete(\"appPassword\");\n    $('#appPasswordDiv').attr(\"style\",\"display:none;\");\n    $('#installBtn').attr(\"style\",\"display:none;\");\n    $('#appConfirmDiv').attr(\"style\",\"display:'';\");\n    active('appConfirm');\n    goOn(\"appConfirm\");\n}\n/**\n * 5.应用审核\n */\nfunction appConfirm() {\n    if($('#confirmBtn').html()!='已通过'){\n        toastr.error(\"请审核应用部署\");\n        warn('appConfirm');\n        return;\n    }\n    complete(\"appConfirm\");\n    $('#appConfirmDiv').attr(\"style\",\"display:none;\");\n    $('#deployCompleteDiv').attr(\"style\",\"display:'';\");\n    active('deployComplete');\n    $(\"#install\").html(\"部署完成\");\n\n    goOn(\"deployComplete\");\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/getInstancesByAppId.js",
    "content": "function changeAppIdSelect(appId, instance_select, contextPath) {\n\tdocument.getElementById(instance_select).options.length = 0;\n\t$('#' + instance_select).selectpicker('destroy');\n\t$('#' + instance_select).selectpicker();\n\n\t$.post(contextPath + '/manage/app/tool/diagnostic/appInstances',\n\t\t{\n\t\t\tappId: appId,\n\t\t},\n\t\tfunction (data) {\n\t\t\tvar status = data.status;\n\t\t\tif (status == 1) {\n\t\t\t\t$('#' + instance_select).selectpicker('destroy');\n\t\t\t\tvar appInstanceList = data.appInstanceList;\n\t\t\t\tfor (var i = 0; i < appInstanceList.length; i++) {\n\t\t\t\t\tvar val = appInstanceList[i].hostPort;\n\t\t\t\t\tvar term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n\t\t\t\t\t$('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n\t\t\t\t}\n\t\t\t\t$('#' + instance_select).selectpicker();\n\t\t\t} else {\n\t\t\t\tconsole.log('data.status:' + status);\n\t\t\t}\n\t\t}\n\t);\n}\n\nfunction testisNum(id){\n\tvar value =document.getElementById(id).value;\n\tif(value != \"\" && isNaN(value)){\n\t\talert(\"请输入数字类型!\");\n\t\tdocument.getElementById(id).value=\"\";\n\t\tdocument.getElementById(id).focus();\n\t}\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/jquery-console.js",
    "content": "// JQuery Console 1.0\n// Sun Feb 21 20:28:47 GMT 2010\n//\n// Copyright 2010 Chris Done, Simon David Pratt. All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions\n// are met:\n//\n//    1. Redistributions of source code must retain the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer.\n//\n//    2. Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials\n//       provided with the distribution.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n// POSSIBILITY OF SUCH DAMAGE.\n\n// TESTED ON\n//   Internet Explorer 6\n//   Opera 10.01\n//   Chromium 4.0.237.0 (Ubuntu build 31094)\n//   Firefox 3.5.8, 3.6.2 (Mac)\n//   Safari 4.0.5 (6531.22.7) (Mac)\n//   Google Chrome 5.0.375.55 (Mac)\n\n(function($){\n    var isWebkit = !!~navigator.userAgent.indexOf(' AppleWebKit/');\n\n    $.fn.console = function(config){\n        ////////////////////////////////////////////////////////////////////////\n        // Constants\n        // Some are enums, data types, others just for optimisation\n        var keyCodes = {\n            // left\n            37: moveBackward,\n            // right\n            39: moveForward,\n            // up\n            38: previousHistory,\n            // down\n            40: nextHistory,\n            // backspace\n            8:  backDelete,\n            // delete\n            46: forwardDelete,\n            // end\n            35: moveToEnd,\n            // start\n            36: moveToStart,\n            // return\n            13: commandTrigger,\n            // tab\n            18: doNothing,\n            // tab\n            9: doComplete\n        };\n        var ctrlCodes = {\n            // C-a\n            65: moveToStart,\n            // C-e\n            69: moveToEnd,\n            // C-d\n            68: forwardDelete,\n            // C-n\n            78: nextHistory,\n            // C-p\n            80: previousHistory,\n            // C-b\n            66: moveBackward,\n            // C-f\n            70: moveForward,\n            // C-k\n            75: deleteUntilEnd\n        };\n        if(config.ctrlCodes) {\n            $.extend(ctrlCodes, config.ctrlCodes);\n        }\n        var altCodes = {\n            // M-f\n            70: moveToNextWord,\n            // M-b\n            66: moveToPreviousWord,\n            // M-d\n            68: deleteNextWord\n        };\n        var shiftCodes = {\n            // return\n            13: newLine\n        };\n        var cursor = '<span class=\"jquery-console-cursor\">&nbsp;</span>';\n\n        ////////////////////////////////////////////////////////////////////////\n        // Globals\n        var container = $(this);\n        var inner = $('<div class=\"jquery-console-inner\"></div>');\n        // erjiang: changed this from a text input to a textarea so we\n        // can get pasted newlines\n        var typer = $('<textarea class=\"jquery-console-typer\"></textarea>');\n        // Prompt\n        var promptBox;\n        var prompt;\n        var continuedPromptLabel = config && config.continuedPromptLabel?\n            config.continuedPromptLabel : \"> \";\n        var column = 0;\n        var promptText = '';\n        var restoreText = '';\n        var continuedText = '';\n        var fadeOnReset = config.fadeOnReset !== undefined ? config.fadeOnReset : true;\n        // Prompt history stack\n        var history = [];\n        var ringn = 0;\n        // For reasons unknown to The Sword of Michael himself, Opera\n        // triggers and sends a key character when you hit various\n        // keys like PgUp, End, etc. So there is no way of knowing\n        // when a user has typed '#' or End. My solution is in the\n        // typer.keydown and typer.keypress functions; I use the\n        // variable below to ignore the keypress event if the keydown\n        // event succeeds.\n        var cancelKeyPress = 0;\n        // When this value is false, the prompt will not respond to input\n        var acceptInput = true;\n        // When this value is true, the command has been canceled\n        var cancelCommand = false;\n\n        // External exports object\n        var extern = {};\n\n        ////////////////////////////////////////////////////////////////////////\n        // Main entry point\n        (function(){\n            extern.promptLabel = config && config.promptLabel? config.promptLabel : \"> \";\n            container.append(inner);\n            inner.append(typer);\n            typer.css({position:'absolute',top:0,left:'-9999px'});\n            if (config.welcomeMessage)\n                message(config.welcomeMessage,'jquery-console-welcome');\n            newPromptBox();\n            if (config.autofocus) {\n                inner.addClass('jquery-console-focus');\n                typer.focus();\n                setTimeout(function(){\n                    inner.addClass('jquery-console-focus');\n                    typer.focus();\n                },100);\n            }\n            extern.inner = inner;\n            extern.typer = typer;\n            extern.scrollToBottom = scrollToBottom;\n            extern.report = report;\n        })();\n\n        ////////////////////////////////////////////////////////////////////////\n        // Reset terminal\n        extern.reset = function(){\n            var welcome = (typeof config.welcomeMessage != 'undefined');\n\n            var removeElements = function() {\n                inner.find('div').each(function(){\n                    if (!welcome) {\n                        $(this).remove();\n                    } else {\n                        welcome = false;\n                    }\n                });\n            };\n\n            if (fadeOnReset) {\n                inner.parent().fadeOut(function() {\n                    removeElements();\n                    newPromptBox();\n                    inner.parent().fadeIn(focusConsole);\n                });\n            }\n            else {\n                removeElements();\n                newPromptBox();\n                focusConsole();\n            }\n        };\n\n        var focusConsole = function() {\n            inner.addClass('jquery-console-focus');\n            typer.focus();\n        };\n\n        extern.focus = function(){\n            focusConsole();\n        }\n\n        ////////////////////////////////////////////////////////////////////////\n        // Reset terminal\n        extern.notice = function(msg,style){\n            var n = $('<div class=\"notice\"></div>').append($('<div></div>').text(msg))\n                .css({visibility:'hidden'});\n            container.append(n);\n            var focused = true;\n            if (style=='fadeout')\n                setTimeout(function(){\n                    n.fadeOut(function(){\n                        n.remove();\n                    });\n                },4000);\n            else if (style=='prompt') {\n                var a = $('<br/><div class=\"action\"><a href=\"javascript:\">OK</a><div class=\"clear\"></div></div>');\n                n.append(a);\n                focused = false;\n                a.click(function(){ n.fadeOut(function(){ n.remove();inner.css({opacity:1}) }); });\n            }\n            var h = n.height();\n            n.css({height:'0px',visibility:'visible'})\n                .animate({height:h+'px'},function(){\n                    if (!focused) inner.css({opacity:0.5});\n                });\n            n.css('cursor','default');\n            return n;\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Make a new prompt box\n        function newPromptBox() {\n            column = 0;\n            promptText = '';\n            ringn = 0; // Reset the position of the history ring\n            enableInput();\n            promptBox = $('<div class=\"jquery-console-prompt-box\"></div>');\n            var label = $('<span class=\"jquery-console-prompt-label\"></span>');\n            var labelText = extern.continuedPrompt? continuedPromptLabel : extern.promptLabel;\n            promptBox.append(label.text(labelText).show());\n            label.html(label.html().replace(' ','&nbsp;'));\n            prompt = $('<span class=\"jquery-console-prompt\"></span>');\n            promptBox.append(prompt);\n            inner.append(promptBox);\n            updatePromptDisplay();\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Handle setting focus\n        container.click(function(){\n            // Don't mess with the focus if there is an active selection\n            if (window.getSelection().toString()) {\n                return false;\n            }\n\n            inner.addClass('jquery-console-focus');\n            inner.removeClass('jquery-console-nofocus');\n            if (isWebkit) {\n                typer.focusWithoutScrolling();\n            } else {\n                typer.css('position', 'fixed').focus();\n            }\n            scrollToBottom();\n            return false;\n        });\n\n        ////////////////////////////////////////////////////////////////////////\n        // Handle losing focus\n        typer.blur(function(){\n            inner.removeClass('jquery-console-focus');\n            inner.addClass('jquery-console-nofocus');\n        });\n\n        ////////////////////////////////////////////////////////////////////////\n        // Bind to the paste event of the input box so we know when we\n        // get pasted data\n        typer.bind('paste', function(e) {\n            // wipe typer input clean just in case\n            typer.val(\"\");\n            // this timeout is required because the onpaste event is\n            // fired *before* the text is actually pasted\n            setTimeout(function() {\n                typer.consoleInsert(typer.val());\n                typer.val(\"\");\n            }, 0);\n        });\n\n        ////////////////////////////////////////////////////////////////////////\n        // Handle key hit before translation\n        // For picking up control characters like up/left/down/right\n\n        typer.keydown(function(e){\n            cancelKeyPress = 0;\n            var keyCode = e.keyCode;\n            // C-c: cancel the execution\n            if(e.ctrlKey && keyCode == 67) {\n                cancelKeyPress = keyCode;\n                cancelExecution();\n                return false;\n            }\n            if (acceptInput) {\n                if (e.shiftKey && keyCode in shiftCodes) {\n                    cancelKeyPress = keyCode;\n                    (shiftCodes[keyCode])();\n                    return false;\n                } else if (e.altKey  && keyCode in altCodes) {\n                    cancelKeyPress = keyCode;\n                    (altCodes[keyCode])();\n                    return false;\n                } else if (e.ctrlKey && keyCode in ctrlCodes) {\n                    cancelKeyPress = keyCode;\n                    (ctrlCodes[keyCode])();\n                    return false;\n                } else if (keyCode in keyCodes) {\n                    cancelKeyPress = keyCode;\n                    (keyCodes[keyCode])();\n                    return false;\n                }\n            }\n        });\n\n        ////////////////////////////////////////////////////////////////////////\n        // Handle key press\n        typer.keypress(function(e){\n            var keyCode = e.keyCode || e.which;\n            if (isIgnorableKey(e)) {\n                return false;\n            }\n            // C-v: don't insert on paste event\n            if ((e.ctrlKey || e.metaKey) && String.fromCharCode(keyCode).toLowerCase() == 'v') {\n                return true;\n            }\n            if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){\n                if (cancelKeyPress) return false;\n                if (\n                    typeof config.charInsertTrigger == 'undefined' || (\n                    typeof config.charInsertTrigger == 'function' &&\n                    config.charInsertTrigger(keyCode,promptText)\n                    )\n                    ){\n                    typer.consoleInsert(keyCode);\n                }\n            }\n            if (isWebkit) return false;\n        });\n\n        function isIgnorableKey(e) {\n            // for now just filter alt+tab that we receive on some platforms when\n            // user switches windows (goes away from the browser)\n            return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey);\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Rotate through the command history\n        function rotateHistory(n){\n            if (history.length == 0) return;\n            ringn += n;\n            if (ringn < 0) ringn = history.length;\n            else if (ringn > history.length) ringn = 0;\n            var prevText = promptText;\n            if (ringn == 0) {\n                promptText = restoreText;\n            } else {\n                promptText = history[ringn - 1];\n            }\n            if (config.historyPreserveColumn) {\n                if (promptText.length < column + 1) {\n                    column = promptText.length;\n                } else if (column == 0) {\n                    column = promptText.length;\n                }\n            } else {\n                column = promptText.length;\n            }\n            updatePromptDisplay();\n        };\n\n        function previousHistory() {\n            rotateHistory(-1);\n        };\n\n        function nextHistory() {\n            rotateHistory(1);\n        };\n\n        // Add something to the history ring\n        function addToHistory(line){\n            history.push(line);\n            restoreText = '';\n        };\n\n        // Delete the character at the current position\n        function deleteCharAtPos(){\n            if (column < promptText.length){\n                promptText =\n                    promptText.substring(0,column) +\n                    promptText.substring(column+1);\n                restoreText = promptText;\n                return true;\n            } else return false;\n        };\n\n        function backDelete() {\n            if (moveColumn(-1)){\n                deleteCharAtPos();\n                updatePromptDisplay();\n            }\n        };\n\n        function forwardDelete() {\n            if (deleteCharAtPos()){\n                updatePromptDisplay();\n            }\n        };\n\n        function deleteUntilEnd() {\n            while(deleteCharAtPos()) {\n                updatePromptDisplay();\n            }\n        };\n\n        function deleteNextWord() {\n            // A word is defined within this context as a series of alphanumeric\n            // characters.\n            // Delete up to the next alphanumeric character\n            while(\n                column < promptText.length &&\n                !isCharAlphanumeric(promptText[column])\n                ) {\n                deleteCharAtPos();\n                updatePromptDisplay();\n            }\n            // Then, delete until the next non-alphanumeric character\n            while(\n                column < promptText.length &&\n                isCharAlphanumeric(promptText[column])\n                ) {\n                deleteCharAtPos();\n                updatePromptDisplay();\n            }\n        };\n\n        function newLine() {\n            var lines = promptText.split(\"\\n\");\n            var last_line = lines.slice(-1)[0];\n            var spaces = last_line.match(/^(\\s*)/g)[0];\n            var new_line = \"\\n\" + spaces;\n            promptText += new_line;\n            moveColumn(new_line.length);\n            updatePromptDisplay();\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Validate command and trigger it if valid, or show a validation error\n        function commandTrigger() {\n            var line = promptText;\n            if (typeof config.commandValidate == 'function') {\n                var ret = config.commandValidate(line);\n                if (ret == true || ret == false) {\n                    if (ret) {\n                        handleCommand();\n                    }\n                } else {\n                    commandResult(ret,\"jquery-console-message-error\");\n                }\n            } else {\n                handleCommand();\n            }\n        };\n\n        // Scroll to the bottom of the view\n        function scrollToBottom() {\n            var version = jQuery.fn.jquery.split('.');\n            var major = parseInt(version[0]);\n            var minor = parseInt(version[1]);\n\n            // check if we're using jquery > 1.6\n            if ((major == 1 && minor > 6) || major > 1) {\n                inner.prop({ scrollTop: inner.prop(\"scrollHeight\") });\n            }\n            else {\n                inner.attr({ scrollTop: inner.attr(\"scrollHeight\") });\n            }\n        };\n\n        function cancelExecution() {\n            if(typeof config.cancelHandle == 'function') {\n                config.cancelHandle();\n            }\n        }\n\n        ////////////////////////////////////////////////////////////////////////\n        // Handle a command\n        function handleCommand() {\n            if (typeof config.commandHandle == 'function') {\n                disableInput();\n                addToHistory(promptText);\n                var text = promptText;\n                if (extern.continuedPrompt) {\n                    if (continuedText)\n                        continuedText += '\\n' + promptText;\n                    else continuedText = promptText;\n                } else continuedText = undefined;\n                if (continuedText) text = continuedText;\n                var ret = config.commandHandle(text,function(msgs){\n                    commandResult(msgs);\n                });\n                if (extern.continuedPrompt && !continuedText)\n                    continuedText = promptText;\n                if (typeof ret == 'boolean') {\n                    if (ret) {\n                        // Command succeeded without a result.\n                        commandResult();\n                    } else {\n                        commandResult(\n                            'Command failed.',\n                            \"jquery-console-message-error\"\n                        );\n                    }\n                } else if (typeof ret == \"string\") {\n                    commandResult(ret,\"jquery-console-message-success\");\n                } else if (typeof ret == 'object' && ret.length) {\n                    commandResult(ret);\n                } else if (extern.continuedPrompt) {\n                    commandResult();\n                }\n            }\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Disable input\n        function disableInput() {\n            acceptInput = false;\n        };\n\n        // Enable input\n        function enableInput() {\n            acceptInput = true;\n        }\n\n        ////////////////////////////////////////////////////////////////////////\n        // Reset the prompt in invalid command\n        function commandResult(msg,className) {\n            column = -1;\n            updatePromptDisplay();\n            if (typeof msg == 'string') {\n                message(msg,className);\n            } else if ($.isArray(msg)) {\n                for (var x in msg) {\n                    var ret = msg[x];\n                    message(ret.msg,ret.className);\n                }\n            } else { // Assume it's a DOM node or jQuery object.\n                inner.append(msg);\n            }\n            newPromptBox();\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Report some message into the console\n        function report(msg,className) {\n            var text = promptText;\n            promptBox.remove();\n            commandResult(msg,className);\n            extern.promptText(text);\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Display a message\n        function message(msg,className) {\n            var mesg = $('<div class=\"jquery-console-message\"></div>');\n            if (className) mesg.addClass(className);\n            mesg.filledText(msg).hide();\n            inner.append(mesg);\n            mesg.show();\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Handle normal character insertion\n        // data can either be a number, which will be interpreted as the\n        // numeric value of a single character, or a string\n        typer.consoleInsert = function(data){\n            // TODO: remove redundant indirection\n            var text = (typeof data == 'number') ? String.fromCharCode(data) : data;\n            var before = promptText.substring(0,column);\n            var after = promptText.substring(column);\n            promptText = before + text + after;\n            moveColumn(text.length);\n            restoreText = promptText;\n            updatePromptDisplay();\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Move to another column relative to this one\n        // Negative means go back, positive means go forward.\n        function moveColumn(n){\n            if (column + n >= 0 && column + n <= promptText.length){\n                column += n;\n                return true;\n            } else return false;\n        };\n\n        function moveForward() {\n            if(moveColumn(1)) {\n                updatePromptDisplay();\n                return true;\n            }\n            return false;\n        };\n\n        function moveBackward() {\n            if(moveColumn(-1)) {\n                updatePromptDisplay();\n                return true;\n            }\n            return false;\n        };\n\n        function moveToStart() {\n            if (moveColumn(-column))\n                updatePromptDisplay();\n        };\n\n        function moveToEnd() {\n            if (moveColumn(promptText.length-column))\n                updatePromptDisplay();\n        };\n\n        function moveToNextWord() {\n            while(\n                column < promptText.length &&\n                !isCharAlphanumeric(promptText[column]) &&\n                moveForward()\n                ) {}\n            while(\n                column < promptText.length &&\n                isCharAlphanumeric(promptText[column]) &&\n                moveForward()\n                ) {}\n        };\n\n        function moveToPreviousWord() {\n            // Move backward until we find the first alphanumeric\n            while(\n                column -1 >= 0 &&\n                !isCharAlphanumeric(promptText[column-1]) &&\n                moveBackward()\n                ) {}\n            // Move until we find the first non-alphanumeric\n            while(\n                column -1 >= 0 &&\n                isCharAlphanumeric(promptText[column-1]) &&\n                moveBackward()\n                ) {}\n        };\n\n        function isCharAlphanumeric(charToTest) {\n            if(typeof charToTest == 'string') {\n                var code = charToTest.charCodeAt();\n                return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) ||\n                    (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) ||\n                    (code >= '0'.charCodeAt() && code <= '9'.charCodeAt());\n            }\n            return false;\n        };\n\n        function doComplete() {\n            if(typeof config.completeHandle == 'function') {\n                var completions = config.completeHandle(promptText);\n                var len = completions.length;\n                if (len === 1) {\n                    extern.promptText(promptText + completions[0]);\n                } else if (len > 1 && config.cols) {\n                    var prompt = promptText;\n                    // Compute the number of rows that will fit in the width\n                    var max = 0;\n                    for (var i = 0;i < len;i++) {\n                        max = Math.max(max, completions[i].length);\n                    }\n                    max += 2;\n                    var n = Math.floor(config.cols / max);\n                    var buffer = \"\";\n                    var col = 0;\n                    for (i = 0;i < len;i++) {\n                        var completion = completions[i];\n                        buffer += completions[i];\n                        for (var j = completion.length;j < max;j++) {\n                            buffer += \" \";\n                        }\n                        if (++col >= n) {\n                            buffer += \"\\n\";\n                            col = 0;\n                        }\n                    }\n                    commandResult(buffer,\"jquery-console-message-value\");\n                    extern.promptText(prompt);\n                }\n            }\n        };\n\n        function doNothing() {};\n\n        extern.promptText = function(text){\n            if (typeof text === 'string') {\n                promptText = text;\n                column = promptText.length;\n                updatePromptDisplay();\n            }\n            return promptText;\n        };\n\n        ////////////////////////////////////////////////////////////////////////\n        // Update the prompt display\n        function updatePromptDisplay(){\n            var line = promptText;\n            var html = '';\n            if (column > 0 && line == ''){\n                // When we have an empty line just display a cursor.\n                html = cursor;\n            } else if (column == promptText.length){\n                // We're at the end of the line, so we need to display\n                // the text *and* cursor.\n                html = htmlEncode(line) + cursor;\n            } else {\n                // Grab the current character, if there is one, and\n                // make it the current cursor.\n                var before = line.substring(0, column);\n                var current = line.substring(column,column+1);\n                if (current){\n                    current =\n                        '<span class=\"jquery-console-cursor\">' +\n                        htmlEncode(current) +\n                        '</span>';\n                }\n                var after = line.substring(column+1);\n                html = htmlEncode(before) + current + htmlEncode(after);\n            }\n            prompt.html(html);\n            scrollToBottom();\n        };\n\n        // Simple HTML encoding\n        // Simply replace '<', '>' and '&'\n        // TODO: Use jQuery's .html() trick, or grab a proper, fast\n        // HTML encoder.\n        function htmlEncode(text){\n            return (\n                text.replace(/&/g,'&amp;')\n                    .replace(/</g,'&lt;')\n                    .replace(/</g,'&lt;')\n                    .replace(/ /g,'&nbsp;')\n                    .replace(/\\n/g,'<br />')\n                );\n        };\n\n        return extern;\n    };\n    // Simple utility for printing messages\n    $.fn.filledText = function(txt){\n        $(this).text(txt);\n        $(this).html($(this).html().replace(/\\n/g,'<br/>'));\n        return this;\n    };\n\n    // Alternative method for focus without scrolling\n    $.fn.focusWithoutScrolling = function(){\n        var x = window.scrollX, y = window.scrollY;\n        $(this).focus();\n        window.scrollTo(x, y);\n    };\n})(jQuery);"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/machineManage.js",
    "content": "function removeMachine(id, ip, contextPath) {\n    var removeMachineBtn = document.getElementById(id);\n    removeMachineBtn.disabled = true;\n    $.get(\n        contextPath + '/manage/machine/checkMachineInstances.json',\n        {\n            ip: ip,\n        },\n        function (data) {\n            var machineHasInstance = data.machineHasInstance;\n            var alertMsg;\n            if (machineHasInstance == true) {\n                alertMsg = \"该机器ip=\" + ip + \"还有运行中的Redis节点,确认要删除吗？\";\n            } else {\n                alertMsg = \"确认要删除ip=\" + ip + \"吗?\";\n            }\n            if (confirm(alertMsg)) {\n                location.href = contextPath + \"/manage/machine/delete?machineIp=\" + ip;\n            } else {\n                removeMachineBtn.disabled = false;\n            }\n        }\n    );\n}\n\nfunction saveOrUpdateMachine(machineId, contextPath) {\n    var ip = document.getElementById(\"ip\" + machineId);\n    var room = document.getElementById(\"machineRoom\" + machineId);\n    var mem = document.getElementById(\"mem\" + machineId);\n    var cpu = document.getElementById(\"cpu\" + machineId);\n    var disk = document.getElementById(\"disk\" + machineId);\n    var virtual = document.getElementById(\"virtual\" + machineId);\n    var disType = document.getElementById(\"disType\" + machineId);\n    var realIp = document.getElementById(\"realIp\" + machineId);\n    var machineType = document.getElementById(\"machineType\" + machineId);\n    var useType = document.getElementById(\"useType1\" + machineId);\n    var extraDesc = document.getElementById(\"extraDesc\" + machineId);\n    var collect = document.getElementById(\"collect\" + machineId);\n    var versionInfo = document.getElementById(\"versionInfo\" + machineId);\n    var k8sType = document.getElementById(\"k8sType\" + machineId);\n    var rack = document.getElementById(\"rack\" + machineId);\n\n    if (ip.value == \"\") {\n        alert(\"IP不能为空!\");\n        ip.focus();\n        return false;\n    }\n    if (room.value == \"\") {\n        alert(\"机房不能为空!\");\n        room.focus();\n        return false;\n    }\n    if (mem.value == \"\") {\n        alert(\"内存不能为空!\");\n        mem.focus();\n        return false;\n    }\n    if (cpu.value == \"\") {\n        alert(\"CPU不能为空!\");\n        cpu.focus();\n        return false;\n    }\n    if (disk.value == \"\") {\n        alert(\"磁盘不能为空!\");\n        disk.focus();\n        return false;\n    }\n    if (virtual.value == \"\") {\n        alert(\"是否虚机为空!\");\n        virtual.focus();\n        return false;\n    }\n    var addMachineBtn = document.getElementById(\"addMachineBtn\" + machineId);\n    addMachineBtn.disabled = true;\n\n    $.post(\n        contextPath + '/manage/machine/addMultiple.json',\n        {\n            ip: ip.value,\n            room: room.value,\n            mem: mem.value,\n            cpu: cpu.value,\n            disk: disk.value,\n            virtual: virtual.value,\n            disType: disType.value,\n            realIp: realIp.value,\n            id: machineId,\n            machineType: machineType.value,\n            useType: useType.value,\n            k8sType: k8sType.value,\n            extraDesc: extraDesc.value,\n            rack: rack.value,\n            collect: collect.value,\n            versionInfo: versionInfo.value\n        },\n        function (data) {\n            if (data.result) {\n                $(\"#machineInfo\" + machineId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                var targetId = \"#addMachineModal\" + machineId;\n                setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n            } else {\n                addMachineBtn.disabled = false;\n                $(\"#machineInfo\" + machineId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n    );\n}\n\nfunction downCrashMachineAddSlave(contextPath) {\n    var ip = document.getElementById(\"downMachineIp\");\n    if (ip.value == \"\") {\n        alert(\"IP不能为空!\");\n        ip.focus();\n        return false;\n    }\n    var availableMachineIps = \"\";\n    $(\"#availablePodIp option:selected\").each(function () {\n        if (this.value != '') {\n            availableMachineIps += this.value + \",\";\n        }\n    });\n    if(availableMachineIps == \"\" || availableMachineIps.length <= 0) {\n        alert(\"请选择可用pod!\");\n        return false;\n    }\n    var downCrashMachineModalBtn = document.getElementById(\"downCrashMachineModalBtn\");\n    downCrashMachineModalBtn.disabled = true;\n    $.post(\n        contextPath + '/manage/machine/downCrashAddSlave.json',\n        {\n            ip: ip.value,\n            availableMachineIps: availableMachineIps\n        },\n        function (data) {\n            if (data.result) {\n                $(\"#downMachineInfo\").html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>创建自动补全app缺失slave任务成功</div>\");\n                window.open(contextPath + '/manage/machine/taskInfo?taskId=' + data.taskId, \"_blank\");\n            } else {\n                downCrashMachineModalBtn.disabled = false;\n                $(\"#downMachineInfo\").html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>创建自动补全app缺失slave任务，失败！</div>\");\n            }\n        }\n    );\n}\n\nfunction removeRoom(id, roomId, contextPath) {\n    var removeBtn = document.getElementById(id);\n    removeBtn.disabled = true;\n    var alertMsg = \"确认要删除该机房吗?\";\n    console.log(\"roomid:\"+roomId);\n    if (confirm(alertMsg)) {\n        location.href = contextPath + \"/manage/machine/room/delete?id=\" + roomId;\n    } else {\n        removeBtn.disabled = false;\n    }\n}\n\nfunction saveOrUpdateRoom(roomId, contextPath) {\n    var id = document.getElementById(\"roomId\" + roomId);\n    var name = document.getElementById(\"name\" + roomId);\n    var status = document.getElementById(\"status\" + roomId);\n    var desc = document.getElementById(\"desc\" + roomId);\n    var ipNetwork = document.getElementById(\"ipNetwork\" + roomId);\n    var operator = document.getElementById(\"operator\" + roomId);\n\n\n    if (name.value == \"\") {\n        alert(\"机房名称不能为空!\");\n        ip.focus();\n        return false;\n    }\n    var addRoomBtn = document.getElementById(\"addRoomBtn\" + roomId);\n    addRoomBtn.disabled = true;\n\n    $.post(\n        contextPath + '/manage/machine/room/add.json',\n        {\n            id: id == null ? null : id.value,\n            name: name.value,\n            status: status.value,\n            desc: desc.value,\n            ipNetwork: ipNetwork.value,\n            operator: operator.value,\n        },\n        function (data) {\n            if (data.result) {\n                $(\"#machineRoom\" + roomId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                var targetId = \"#addRoomModal\" + roomId;\n                setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n            } else {\n                addRoomBtn.disabled = false;\n                $(\"#machineRoom\" + roomId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n    );\n}\n\nfunction prepareMachinedownResource(contextPath) {\n    $('#availablePodIp').selectpicker('destroy');\n    $('#availablePodIp').selectpicker();\n    var availablePodIpElement = document.getElementById('availablePodIp');\n    availablePodIpElement.innerHTML = '';\n    $(\"#selectedResourceId\").html(\"\");\n  var downMachineIp = document.getElementById(\"downMachineIp\").value;\n  if (downMachineIp == \"\" || downMachineIp == undefined) {\n      alert(\"机器ip不能为空\");\n      return false;\n  }\n  var downCrashMachineModalBtn = document.getElementById(\"downCrashMachineModalBtn\");\n  var downMachineIpInput = document.getElementById(\"downMachineIp\");\n  $.post(\n      contextPath + '/manage/machine/prepareMachinedownResource.json',\n      {\n          ip: downMachineIp\n      },\n      function (data) {\n          if (data.result) {\n            var podResourceUsedMap = new Map(Object.entries(data.podResourceUsedMap));\n            var deployDetailId = document.getElementById(\"deployDetailId\");\n            var totalUsedCpu = 0;\n            var totalUsedMemRss = 0.00;\n            var totalUsedDisk = 0.00;\n            podResourceUsedMap.forEach((value, key) => {\n                if(value.type == 0 || value.type == 6){\n                    totalUsedCpu += value.instanceNum;\n                    totalUsedMemRss += (value.usedMemRss / 1024 / 1024 / 1024);\n                    totalUsedDisk += (value.usedDisk / 1024 / 1024 / 1024);\n                }\n            });\n            var html = '<p><label style=\"color:red\">汇总部署信息：【cpu：' + totalUsedCpu + '核】 【使用内存：' + totalUsedMemRss.toFixed(2) + 'G】 【使用SSD：' + totalUsedDisk.toFixed(2) + 'G】<label></p>';\n            html += '<table class=\"table table-striped table-bordered table-hover table-sm\" style=\"white-space: nowrap\">' +\n                        '<thead><th scope=\"col\">ip</th>' +\n                        '<th scope=\"col\">实例数/核数</th>' +\n                        '<th scope=\"col\">内存使用</th>' +\n                        '<th scope=\"col\">rss内存使用</th>' +\n                        '<th scope=\"col\">SSD使用</th>' +\n                        '</thead>' +\n                        '<tbody>';\n            podResourceUsedMap.forEach((value, key) => {\n                var migrateTip = \"\";\n                if(value.type != 0 && value.type != 6){\n                    migrateTip =\"--该pod不支持迁移，请手动处理\";\n                }\n                html += \"<tr><td>\" + key + migrateTip + \"</td>\"\n                    + \"<td>\" + value.instanceNum + \"/\" + value.cpu + \"核</td>\"\n                    + \"<td>\" + (value.usedMem / 1024 / 1024 / 1024).toFixed(2) + \"/\" + value.mem + \"G</td>\"\n                    + \"<td>\" + (value.usedMemRss / 1024 / 1024 / 1024).toFixed(2) + \"/\" + value.mem + \"G</td>\"\n                    + \"<td>\" + (value.usedDisk / 1024 / 1024 / 1024).toFixed(2) + \"/\" + value.disk + \"G</td></tr>\";\n            });\n            html += \"</tbody></table>\";\n            deployDetailId.innerHTML = html;\n\n            var tipId = document.getElementById(\"tipId\");\n            var tipHtml = \"\";\n            var optionHtml = \"\";\n            var machineNeedInfoMap = new Map(Object.entries(data.machineNeedInfoMap));\n            var podMemStatInfoMap = new Map(Object.entries(data.podMemStatInfoMap));\n            tipHtml += \"共有\" + machineNeedInfoMap.size + \"种类型pod，请选择合适的数量\";\n            $('#tipId').html(tipHtml);\n            $('#availablePodIp').selectpicker('destroy');\n            machineNeedInfoMap.forEach((value, key) => {\n                $('#availablePodIp').append(\"<option value='' disabled='true'>————机器类型分割线:\" + key.replace(\"MachineBasicInfo\", \"\") + \"————————</option>\");\n                var ips = value.availableMachineIps;\n                for(var i = 0; i < ips.length; i++) {\n                    var memStatInfo = podMemStatInfoMap.get(ips[i]);\n                    var text = \": 【\" + memStatInfo.instanceNum + \"/\" + memStatInfo.cpu + \"核】 【\"\n                                + (memStatInfo.usedMemRss / 1024 / 1024 / 1024).toFixed(2) + \"/\" + memStatInfo.mem + \"G】 【\"\n                                + (memStatInfo.usedDisk / 1024 / 1024 / 1024).toFixed(2)  + \"/\" + memStatInfo.disk + \"G】 【\"\n                                + memStatInfo.realIp + \"】\";\n                    var leftCpu = memStatInfo.cpu - memStatInfo.instanceNum;\n                    if(leftCpu < 0){\n                        leftCpu = 0;\n                    }\n                    var leftMem = (memStatInfo.mem - (memStatInfo.usedMemRss / 1024 / 1024 / 1024)).toFixed(2);\n                    var leftDisk = (memStatInfo.disk - (memStatInfo.usedDisk / 1024 / 1024 / 1024)).toFixed(2);\n                    $('#availablePodIp').append(\"<option value='\" + memStatInfo.ip + \"' leftCpu='\" + leftCpu + \"' leftMem='\" + leftMem + \"' leftDisk='\" + leftDisk + \"'>\" + memStatInfo.ip + text + \"</option>\");\n                }\n            });\n            $('#availablePodIp').selectpicker();\n\n            $('#availableMachineId').show();\n            downCrashMachineModalBtn.disabled = false;\n            downMachineIpInput.disabled = true;\n          } else {\n              $(\"#downMachineInfo\").html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n          }\n      }\n  );\n}\n\n// 计算迁移实例需要内存\nfunction availableMachineChange(){\n    var totalPodCount = 0;\n    var totalLeftCpu = 0;\n    var totalLeftMem = 0.00;\n    var totalLeftDisk = 0.00;\n    $('#availablePodIp option:selected').each(function(){\n        if($(this).attr(\"value\") == ''){\n            return;\n        }\n        totalPodCount += 1;\n        totalLeftCpu += parseFloat($(this).attr(\"leftCpu\"));\n        totalLeftMem += parseFloat($(this).attr(\"leftMem\"));\n        totalLeftDisk += parseFloat($(this).attr(\"leftDisk\"));\n    });\n    totalLeftMem = totalLeftMem.toFixed(2);\n    totalLeftDisk = totalLeftDisk.toFixed(2);\n    $(\"#selectedResourceId\").html(\"<label style=\\\"color:green\\\">已选可用信息:【pod数量：\" + totalPodCount + \"个】 【cpu：\" + totalLeftCpu + \"核】 【内存：\" + totalLeftMem + \"G】 【SSD：\" + totalLeftDisk + \"G】<label>\");\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/myPopover.js",
    "content": "$(function () \n {\n\t const popoverTriggerList = document.querySelectorAll('[data-bs-toggle=\"popover\"]')\n\t const popoverList = [...popoverTriggerList].map(popoverTriggerEl => {\n\t\t var popover1 = new bootstrap.Popover(popoverTriggerEl, {\n\t\t\t trigger: 'hover',\n\t\t\t placement: 'right',\n\t\t\t html: true,\n\t\t\t animation: false,\n\t\t\t // delay: {\n\t\t\t // \t\"show\": 0,\n\t\t\t // \t\"hide\": 1000\n\t\t\t // }\n\t\t });\n\t });\n\n\n\n\t//  $(\"[data-bs-toggle='popover']\").popover(\n\t// {\n\t// \ttrigger : 'manual',\n\t// \tplacement : 'right',\n\t// \thtml : 'true',\n\t// \tcontent : \"<div id='popOverBox'>正在加载，请稍候...</div>\",\n\t// \tanimation : false\n\t// }).on(\n\t// \"mouseenter\",\n\t// function() {\n\t// \tvar _this = this;\n\t// \t$(this).popover(\"show\");\n\t// \t$(this).siblings(\".popover\").on(\"mouseleave\",\n\t// \t\t\tfunction() {\n\t// \t\t\t\t$(_this).popover('hide');\n\t// \t\t\t});\n\t// }).on(\"mouseleave\", function() {\n\t// \tvar _this = this;\n\t// \tsetTimeout(function() {\n\t// \t\tif (!$(\".popover:hover\").length) {\n\t// \t\t\t$(_this).popover(\"hide\");\n\t// \t\t}\n\t// \t}, 100);\n\t// });\n });"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/custom/userManage.js",
    "content": "//验证手机号格式\nvar valPhones=/^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$/;\n//验证邮箱格式\nvar valEmails=/^(([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.|\\-]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3};){0,6}([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.|\\-]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$/;\nfunction saveOrUpdateUser(userId, contextPath){\n\tvar name = document.getElementById(\"user_name\" + userId);\n\tvar chName = document.getElementById(\"chName\" + userId);\n\tvar email = document.getElementById(\"email\" + userId);\n\tvar mobile = document.getElementById(\"mobile\" + userId);\n    var weChat = document.getElementById(\"weChat\" + userId);\n\tvar type = document.getElementById(\"type\" + userId);\n\tvar isAlert = document.getElementById(\"isAlert\" + userId);\n\tvar bizId = document.getElementById(\"bizId\" + userId);\n\tvar company = \"\";\n\tvar purpose = \"\";\n\tif(name.value == \"\"){\n    \talert(\"域账户名不能为空!\");\n\t\tname.focus();\n\t\treturn false;\n    }\n\tif(chName.value == \"\"){\n    \talert(\"中文名不能为空!\");\n\t\tchName.focus();\n\t\treturn false;\n    }\n\tif(email.value == \"\"){\n\t\talert(\"邮箱不能为空!\");\n\t\temail.focus();\n\t\treturn false;\n\t}\n\tif(!valEmails.test(email.value)){\n\t\talert(\"邮箱格式错误!\");\n\t\temail.focus();\n\t\treturn false;\n\t}\n\tif(mobile.value == \"\"){\n\t\talert(\"手机号不能为空!\");\n\t\tmobile.focus();\n\t\treturn false;\n\t}\n    if(!valPhones.test(mobile.value)){\n        alert(\"手机号格式错误!\");\n        mobile.focus();\n        return false;\n    }\n    if(weChat.value == \"\"){\n        alert(\"微信不能为空!\");\n        weChat.focus();\n        return false;\n    }\n\n\tvar userBtn = document.getElementById(\"userBtn\" + userId);\n\tuserBtn.disabled = true;\n\t\n\t$.post(\n\t\tcontextPath + '/manage/user/add',\n\t\t{\n\t\t\tname: name.value,\n\t\t\tchName: chName.value,\n\t\t\temail: email.value,\n\t\t\tmobile: mobile.value,\n            weChat: weChat.value,\n\t\t\ttype: type.value,\n            isAlert: isAlert.value,\n            company: company,\n            purpose: purpose,\n\t\t\tbizId: bizId.value,\n\t\t\tuserId: userId\n\t\t},\n        function(data){\n            if(data==1){\n                $(\"#info\" + userId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n                var targetId = \"#addUserModal\" + userId;\n                setTimeout(\"$('\" + targetId +\"').modal('hide');window.location.reload();\",1000);\n            }else{\n            \tuserBtn.disabled = false;\n                $(\"#info\" + userId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n            }\n        }\n     );\n}\n\n//验证密码强度\nvar pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}');\nfunction checkPassword(userId) {\n\tvar password = $('#updateUserPwdPassword' + userId).val();\n\tif (!pwdRegex.test(password)) {\n\t\talert(\"您的密码复杂度太低，密码中必须包含字母、数字、特殊字符至少8个字符，请修改密码！\");\n\t\t$('#password').focus();\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction checkConfirmPassword(userId) {\n\tvar password = $('#updateUserPwdPassword' + userId).val();\n\tvar password1 = $('#updateUserPwdPassword0' + userId).val();\n\tif (password != password1) {\n\t\talert(\"两次密码输入不一致，请确认密码！\")\n\t\t$('#updateUserPwdPassword' + userId).focus();\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction updateUserPwd(userId, contextPath){\n\tvar password = document.getElementById(\"updateUserPwdPassword\" + userId);\n\tvar password0 = document.getElementById(\"updateUserPwdPassword0\" + userId);\n\tif(password.value == \"\"){\n\t\talert(\"密码不能为空!\");\n\t\tpassword.focus();\n\t\treturn false;\n\t}\n\n\tvar userBtn = document.getElementById(\"updateUserPwdBtn\" + userId);\n\tuserBtn.disabled = true;\n\n\t$.post(\n\t\tcontextPath + '/manage/user/updatePwd',\n\t\t{\n\t\t\tpassword: $.md5(password.value),\n\t\t\tuserId: userId\n\t\t},\n\t\tfunction(data){\n\t\t\tif(data==1){\n\t\t\t\t$(\"#info\" + userId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n\t\t\t\tvar targetId = \"#updateUserPwdModal\" + userId;\n\t\t\t\tsetTimeout(\"$('\" + targetId +\"').modal('hide');window.location.reload();\",1000);\n\t\t\t}else{\n\t\t\t\tuserBtn.disabled = false;\n\t\t\t\t$(\"#info\" + userId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n\t\t\t}\n\t\t}\n\t);\n}\n\nfunction takeOverUser(contextPath){\n\tvar toRemoveUser = document.getElementById(\"toRemoveUser\");\n\tvar toChargeUser = document.getElementById(\"toChargeUser\");\n\tif(toRemoveUser.value == \"\"){\n\t\talert(\"待移交用户，域账户名不能为空!\");\n\t\ttoRemoveUser.focus();\n\t\treturn false;\n\t}\n\tif(toChargeUser.value == \"\"){\n\t\talert(\"接手用户，域账户名不能为空!\");\n\t\ttoChargeUser.focus();\n\t\treturn false;\n\t}\n\tif(toRemoveUser.value == toChargeUser.value){\n\t\talert(\"待移交用户与接收用户不能为同一人!\");\n\t\ttoRemoveUser.focus();\n\t\treturn false;\n\t}\n\n\tvar overUserBtn = document.getElementById(\"overUserBtn\");\n\toverUserBtn.disabled = true;\n\n\t$.post(\n\t\tcontextPath + '/manage/user/takeover',\n\t\t{\n\t\t\ttoRemoverUserName: toRemoveUser.value,\n\t\t\ttoChargeUserName: toChargeUser.value\n\t\t},\n\t\tfunction(data){\n\t\t\tif(data==1){\n\t\t\t\t$(\"#takeoverInfo\").html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>接手成功，窗口会自动关闭</div>\");\n\t\t\t\tvar targetId = \"#overUserModal\";\n\t\t\t\tsetTimeout(\"$('\" + targetId +\"').modal('hide');window.location.reload();\",1000);\n\t\t\t}else{\n\t\t\t\toverUserBtn.disabled = false;\n\t\t\t\t$(\"#takeoverInfo\").html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>接手失败！</div>\");\n\t\t\t}\n\t\t}\n\t);\n}\n\nfunction saveOrUpdateBiz(bizId, contextPath){\n\tvar bizName = document.getElementById(\"biz_name\" + bizId);\n\tvar bizDesc = document.getElementById(\"biz_desc\" + bizId);\n\tif(bizName.value == \"\"){\n\t\talert(\"业务组名称不能为空!\");\n\t\tbizName.focus();\n\t\treturn false;\n\t}\n\n\tvar bizBtn = document.getElementById(\"bizBtn\" + bizId);\n\tbizBtn.disabled = true;\n\n\t$.post(\n\t\tcontextPath + '/manage/user/biz/add',\n\t\t{\n\t\t\tname: bizName.value,\n\t\t\tbizDesc: bizDesc.value,\n\t\t\tbizId: bizId\n\t\t},\n\t\tfunction(data){\n\t\t\tif(data==1){\n\t\t\t\t$(\"#info\" + bizId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>更新成功，窗口会自动关闭</div>\");\n\t\t\t\tvar targetId = \"#addBizModal\" + bizId;\n\t\t\t\tsetTimeout(\"$('\" + targetId +\"').modal('hide');window.location.reload();\",1000);\n\t\t\t}else{\n\t\t\t\tbizBtn.disabled = false;\n\t\t\t\t$(\"#info\" + bizId).html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>更新失败！</div>\");\n\t\t\t}\n\t\t}\n\t);\n}\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/main.js",
    "content": "/**\n* Template Name: NiceAdmin\n* Updated: Mar 09 2023 with Bootstrap v5.2.3\n* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/\n* Author: BootstrapMade.com\n* License: https://bootstrapmade.com/license/\n*/\n(function() {\n  \"use strict\";\n\n  /**\n   * Easy selector helper function\n   */\n  const select = (el, all = false) => {\n    el = el.trim()\n    if (all) {\n      return [...document.querySelectorAll(el)]\n    } else {\n      return document.querySelector(el)\n    }\n  }\n\n  /**\n   * Easy event listener function\n   */\n  const on = (type, el, listener, all = false) => {\n    if (all) {\n      select(el, all).forEach(e => e.addEventListener(type, listener))\n    } else {\n      select(el, all).addEventListener(type, listener)\n    }\n  }\n\n  /**\n   * Easy on scroll event listener \n   */\n  const onscroll = (el, listener) => {\n    el.addEventListener('scroll', listener)\n  }\n\n  /**\n   * Sidebar toggle\n   */\n  if (select('.toggle-sidebar-btn')) {\n    on('click', '.toggle-sidebar-btn', function(e) {\n      select('body').classList.toggle('toggle-sidebar')\n    })\n  }\n\n  /**\n   * Search bar toggle\n   */\n  if (select('.search-bar-toggle')) {\n    on('click', '.search-bar-toggle', function(e) {\n      select('.search-bar').classList.toggle('search-bar-show')\n    })\n  }\n\n  /**\n   * Navbar links active state on scroll\n   */\n  let navbarlinks = select('#navbar .scrollto', true)\n  const navbarlinksActive = () => {\n    let position = window.scrollY + 200\n    navbarlinks.forEach(navbarlink => {\n      if (!navbarlink.hash) return\n      let section = select(navbarlink.hash)\n      if (!section) return\n      if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) {\n        navbarlink.classList.add('active')\n      } else {\n        navbarlink.classList.remove('active')\n      }\n    })\n  }\n  window.addEventListener('load', navbarlinksActive)\n  onscroll(document, navbarlinksActive)\n\n  /**\n   * Toggle .header-scrolled class to #header when page is scrolled\n   */\n  let selectHeader = select('#header')\n  if (selectHeader) {\n    const headerScrolled = () => {\n      if (window.scrollY > 100) {\n        selectHeader.classList.add('header-scrolled')\n      } else {\n        selectHeader.classList.remove('header-scrolled')\n      }\n    }\n    window.addEventListener('load', headerScrolled)\n    onscroll(document, headerScrolled)\n  }\n\n  /**\n   * Back to top button\n   */\n  let backtotop = select('.back-to-top')\n  if (backtotop) {\n    const toggleBacktotop = () => {\n      if (window.scrollY > 100) {\n        backtotop.classList.add('active')\n      } else {\n        backtotop.classList.remove('active')\n      }\n    }\n    window.addEventListener('load', toggleBacktotop)\n    onscroll(document, toggleBacktotop)\n  }\n\n  /**\n   * Initiate tooltips\n   */\n  var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle=\"tooltip\"]'))\n  var tooltipList = tooltipTriggerList.map(function(tooltipTriggerEl) {\n    return new bootstrap.Tooltip(tooltipTriggerEl)\n  })\n\n  /**\n   * Initiate quill editors\n   */\n  if (select('.quill-editor-default')) {\n    new Quill('.quill-editor-default', {\n      theme: 'snow'\n    });\n  }\n\n  if (select('.quill-editor-bubble')) {\n    new Quill('.quill-editor-bubble', {\n      theme: 'bubble'\n    });\n  }\n\n  if (select('.quill-editor-full')) {\n    new Quill(\".quill-editor-full\", {\n      modules: {\n        toolbar: [\n          [{\n            font: []\n          }, {\n            size: []\n          }],\n          [\"bold\", \"italic\", \"underline\", \"strike\"],\n          [{\n              color: []\n            },\n            {\n              background: []\n            }\n          ],\n          [{\n              script: \"super\"\n            },\n            {\n              script: \"sub\"\n            }\n          ],\n          [{\n              list: \"ordered\"\n            },\n            {\n              list: \"bullet\"\n            },\n            {\n              indent: \"-1\"\n            },\n            {\n              indent: \"+1\"\n            }\n          ],\n          [\"direction\", {\n            align: []\n          }],\n          [\"link\", \"image\", \"video\"],\n          [\"clean\"]\n        ]\n      },\n      theme: \"snow\"\n    });\n  }\n\n  /**\n   * Initiate TinyMCE Editor\n   */\n  const useDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;\n  const isSmallScreen = window.matchMedia('(max-width: 1023.5px)').matches;\n\n  tinymce.init({\n    selector: 'textarea.tinymce-editor',\n    plugins: 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media template codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons',\n    editimage_cors_hosts: ['picsum.photos'],\n    menubar: 'file edit view insert format tools table help',\n    toolbar: 'undo redo | bold italic underline strikethrough | fontfamily fontsize blocks | alignleft aligncenter alignright alignjustify | outdent indent |  numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen  preview save print | insertfile image media template link anchor codesample | ltr rtl',\n    toolbar_sticky: true,\n    toolbar_sticky_offset: isSmallScreen ? 102 : 108,\n    autosave_ask_before_unload: true,\n    autosave_interval: '30s',\n    autosave_prefix: '{path}{query}-{id}-',\n    autosave_restore_when_empty: false,\n    autosave_retention: '2m',\n    image_advtab: true,\n    link_list: [{\n        title: 'My page 1',\n        value: 'https://www.tiny.cloud'\n      },\n      {\n        title: 'My page 2',\n        value: 'http://www.moxiecode.com'\n      }\n    ],\n    image_list: [{\n        title: 'My page 1',\n        value: 'https://www.tiny.cloud'\n      },\n      {\n        title: 'My page 2',\n        value: 'http://www.moxiecode.com'\n      }\n    ],\n    image_class_list: [{\n        title: 'None',\n        value: ''\n      },\n      {\n        title: 'Some class',\n        value: 'class-name'\n      }\n    ],\n    importcss_append: true,\n    file_picker_callback: (callback, value, meta) => {\n      /* Provide file and text for the link dialog */\n      if (meta.filetype === 'file') {\n        callback('https://www.google.com/logos/google.jpg', {\n          text: 'My text'\n        });\n      }\n\n      /* Provide image and alt text for the image dialog */\n      if (meta.filetype === 'image') {\n        callback('https://www.google.com/logos/google.jpg', {\n          alt: 'My alt text'\n        });\n      }\n\n      /* Provide alternative source and posted for the media dialog */\n      if (meta.filetype === 'media') {\n        callback('movie.mp4', {\n          source2: 'alt.ogg',\n          poster: 'https://www.google.com/logos/google.jpg'\n        });\n      }\n    },\n    templates: [{\n        title: 'New Table',\n        description: 'creates a new table',\n        content: '<div class=\"mceTmpl\"><table width=\"98%%\"  border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><tr><th scope=\"col\"> </th><th scope=\"col\"> </th></tr><tr><td> </td><td> </td></tr></table></div>'\n      },\n      {\n        title: 'Starting my story',\n        description: 'A cure for writers block',\n        content: 'Once upon a time...'\n      },\n      {\n        title: 'New list with dates',\n        description: 'New List with dates',\n        content: '<div class=\"mceTmpl\"><span class=\"cdate\">cdate</span><br><span class=\"mdate\">mdate</span><h2>My List</h2><ul><li></li><li></li></ul></div>'\n      }\n    ],\n    template_cdate_format: '[Date Created (CDATE): %m/%d/%Y : %H:%M:%S]',\n    template_mdate_format: '[Date Modified (MDATE): %m/%d/%Y : %H:%M:%S]',\n    height: 600,\n    image_caption: true,\n    quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',\n    noneditable_class: 'mceNonEditable',\n    toolbar_mode: 'sliding',\n    contextmenu: 'link image table',\n    skin: useDarkMode ? 'oxide-dark' : 'oxide',\n    content_css: useDarkMode ? 'dark' : 'default',\n    content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'\n  });\n\n  /**\n   * Initiate Bootstrap validation check\n   */\n  var needsValidation = document.querySelectorAll('.needs-validation')\n\n  Array.prototype.slice.call(needsValidation)\n    .forEach(function(form) {\n      form.addEventListener('submit', function(event) {\n        if (!form.checkValidity()) {\n          event.preventDefault()\n          event.stopPropagation()\n        }\n\n        form.classList.add('was-validated')\n      }, false)\n    })\n\n  /**\n   * Initiate Datatables\n   */\n  const datatables = select('.datatable', true)\n  datatables.forEach(datatable => {\n    new simpleDatatables.DataTable(datatable);\n  })\n\n  /**\n   * Autoresize echart charts\n   */\n  const mainContainer = select('#main');\n  if (mainContainer) {\n    setTimeout(() => {\n      new ResizeObserver(function() {\n        select('.echart', true).forEach(getEchart => {\n          echarts.getInstanceByDom(getEchart).resize();\n        })\n      }).observe(mainContainer);\n    }, 200);\n  }\n\n})();"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/js/myhighchart.js",
    "content": "$(function(){\n    Highcharts.setOptions({\n        time: {\n            useUTC: false\n        }\n    });\n});\n\nfunction generateDataPieChart(container, title, dataArr, legendName) {\n    var chartType = \"pie\";\n    var options = {\n        chart: {\n            renderTo: container,\n            animation: Highcharts.svg,\n            borderColor: '#E6E6E6',\n            borderWidth: 2,\n            // backgroundColor: '#E6F1F5',\n            plotBackgroundColor: '#FFFFFF',\n            type: chartType,\n            marginRight: 10\n        },\n        title: {\n            useHTML: true,\n            text: title\n        },\n        xAxis: {\n            type: 'category'\n        },\n        yAxis: {\n            title: {\n                text: ''\n            },\n            plotLines: [{\n                value: 0,\n                width: 1,\n                color: '#808080'\n            }]\n        },\n        plotOptions: {\n            line: {\n                dataLabels: {\n                    enabled: true\n                }\n            },\n            series: {\n                cursor: 'pointer'\n            }\n        },\n        tooltip: {\n            formatter: function () {\n                return '<b>' + this.point.name + '</b><br/>'\n            }\n        },\n        legend: {\n            enabled: true\n        },\n        credits: {\n            enabled: false\n        },\n        exporting: {\n            enabled: true\n        },\n        series: []\n    };\n    var length = dataArr.length;\n    var arr = [];\n    for (var i = 0; i < length; i++) {\n        var data = dataArr[i];\n        var point = {\n            name: data.param + \": \" + data.count,\n            y: data.count\n        };\n        arr.push(point);\n    }\n    var series = {\n        name: legendName,\n        data: arr\n    };\n    options.series.push(series);\n    new Highcharts.Chart(options);\n}\n\n\nfunction getClientCostSeriesPoints(dataType, dataArr, tags, unit) {\n    var length = dataArr.length;\n    var finalPoints = [];\n    for (var k = 0; k < tags.length; k += 2) {\n        var tag = tags[k];\n        var tagName = tags[k + 1];\n        var dataSeries = [];\n        for (var i = 0; i < length; i++) {\n            var data = dataArr[i];\n            var count = data[tag];\n            var pointName = tagName + \":\" + count + unit;\n            pointName = pointName + \"<br/>调用量: \" + data.count;\n            if (dataType == 1 && tag == \"max100\") {\n                pointName = pointName\n                    + \"<br/>实例: \" + data.maxInst\n                    + \"<br/>客户端: \" + data.maxClient;\n            }\n            var dataPoint = {\n                name: pointName,\n                x: data.timeStamp,\n                y: count,\n            };\n            dataSeries.push(dataPoint);\n        }\n        var seriesPoints = {\n            name: tagName,\n            data: dataSeries,\n            marker: {\n                radius: 1, // 曲线点半径，默认是4\n            }\n        };\n        finalPoints.push(seriesPoints);\n    }\n    return finalPoints;\n}\n\nfunction pushOptionSeries(options, data, dates, nameLegendPrefix, unit, defaultCount) {\n    if (typeof (unit) == \"undefined\") {\n        unit = \"次\";\n    }\n    var dataObject = eval(\"(\" + data.data + \")\");\n    for (var t = 0; t < dates.length; t++) {\n        date = dates[t];\n        var dataArr = dataObject[date];\n        var length = 0;\n        if(dataArr == undefined){\n        }else{\n            length = dataArr.length;\n        }\n        var dataSeries = [];\n        var count;\n        for (var i = 0; i < length - 1; i++) {\n            var data = dataArr[i];\n            count = data.y;\n            if (defaultCount > 0) {\n                count = defaultCount;\n            }\n            var pointName = data.date + \":  \" + count + unit;\n            var dataPoint = {\n                name: pointName,\n                x: data.x,\n                y: count,\n            };\n            dataSeries.push(dataPoint);\n        }\n\n        var seriesPoints = {\n            name: nameLegendPrefix + \"(\" + date + \")\",\n            data: dataSeries,\n            marker: {\n                radius: 1,  //曲线点半径，默认是4\n            }\n        };\n        options.series.push(seriesPoints);\n    }\n\n}\n\nfunction pushMemFragRatioOptionSeries(options, data, dates, nameLegendPrefix, unit, defaultCount) {\n    if (typeof (unit) == \"undefined\") {\n        unit = \"\";\n    }\n    var dataObject = eval(\"(\" + data.data + \")\");\n    for (var t = 0; t < dates.length; t++) {\n        date = dates[t];\n        var dataArr = dataObject[date];\n        var length = dataArr.length;\n        var dataSeries = [];\n        var count;\n        for (var i = 0; i < length - 1; i++) {\n            var data = dataArr[i];\n            count = data.y;\n            if (defaultCount > 0) {\n                count = defaultCount;\n            }\n            var pointName = data.date + \":  \" + count + unit;\n            var dataPoint = {\n                name: pointName,\n                x: data.x,\n                y: count,\n            };\n            dataSeries.push(dataPoint);\n        }\n\n        var seriesPoints = {\n            name: nameLegendPrefix + \"(\" + date + \")\",\n            data: dataSeries,\n            marker: {\n                radius: 1,  //曲线点半径，默认是4\n            }\n        };\n        options.series.push(seriesPoints);\n    }\n\n}\n\nfunction getSeriesPoints(data, nameLegend, unit, defaultCount) {\n    if (typeof (unit) == \"undefined\") {\n        unit = \"次\";\n    }\n    var dataArr = eval(\"(\" + data + \")\");\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    var dataSeries = [];\n\n    var count;\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        count = data.y;\n        if (defaultCount > 0) {\n            count = defaultCount;\n        }\n        var pointName = data.date + \":  \" + count + unit;\n        var dataPoint = {\n            name: pointName,\n            x: data.x,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        marker: {\n            radius: 1,  //曲线点半径，默认是4\n        }\n    };\n    return seriesPoints;\n}\n\nfunction getMemFragRatioSeriesPoints(data, nameLegend, unit, defaultCount) {\n    if (typeof (unit) == \"undefined\") {\n        unit = \"\";\n    }\n    var dataArr = eval(\"(\" + data + \")\");\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    var dataSeries = [];\n\n    var count;\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        count = data.y;\n        if (defaultCount > 0) {\n            count = defaultCount;\n        }\n        var pointName = data.date + \":  \" + count + unit;\n        var dataPoint = {\n            name: pointName,\n            x: data.x,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        marker: {\n            radius: 1,  //曲线点半径，默认是4\n        }\n    };\n    return seriesPoints;\n}\n\nfunction getInstanceNetPoints(instanceNetData, nameLegend, command, unit) {\n    var dataArr = instanceNetData.instanceNetStatMapList;\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    //i,o,t\n    var dataSeries = [];\n\n    var unitTxt = \"\";\n    if (typeof (unit) == \"undefined\") {\n        var byteCounter = 0;\n        var kbCounter = 0;\n        var mbCounter = 0;\n        for (var i = 0; i < length - 1; i++) {\n            var data = dataArr[i];\n            var count = data[command];\n            if (count < 1024) {\n                byteCounter = byteCounter + 1;\n            } else if (count >= 1024 && count < 1024 * 1024) {\n                kbCounter = kbCounter + 1;\n            } else {\n                mbCounter = mbCounter + 1;\n            }\n        }\n        if (byteCounter > kbCounter) {\n            if (byteCounter > mbCounter) {\n                unit = 1;\n                unitTxt = \"byte\";\n            } else {\n                unit = 1024 * 1024;\n                unitTxt = \"Mb\";\n            }\n        } else {\n            if (kbCounter > mbCounter) {\n                unit = 1024;\n                unitTxt = \"Kb\";\n            } else {\n                unit = 1024 * 1024;\n                unitTxt = \"Mb\";\n            }\n        }\n    } else {\n        if (unit == 1) {\n            unitTxt = \"byte\";\n        } else if (unit == 1024) {\n            unitTxt = \"Kb\";\n        } else if (unit == 1024 * 1024) {\n            unitTxt = \"Mb\";\n        }\n    }\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = Math.round(data[command] / unit);\n        var pointName = count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.t,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getInstanceCpuPoints(instance, nameLegend, command) {\n    var dataArr = instance.instanceCpuStatMapList;\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    //i,o,t\n    var dataSeries = [];\n    var unit = 1;\n    var unitTxt = \"秒\"\n\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = Math.round(data[command] / unit);\n        var pointName = formatDate(data.t) + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.t,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getInstanceKeyPoints(instance, nameLegend, command) {\n    var dataArr = instance.instanceExpiredEvictedKeysStatMapList;\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    //i,o,t\n    var dataSeries = [];\n    var unit = 1;\n    var unitTxt = \"个\"\n\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = Math.round(data[command] / unit);\n        var pointName = formatDate(data.t) + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.t,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getInstanceBaseCpuPoints(instance, nameLegend, ratio) {\n    var dataArr = instance.instanceCpuStatMapList;\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    //i,o,t\n    var dataSeries = [];\n    var unit = 1;\n    var unitTxt = \"秒\"\n\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = 60 * ratio;\n        var pointName = formatDate(data.t) + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.t,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getInstanceMemFragRatioPoints(instance, nameLegend, command) {\n    var dataArr = instance.instanceMemFragRatioStatMapList;\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    //i,o,t\n    var dataSeries = [];\n    var unit = 1;\n    var unitTxt = \"\"\n\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = data[command] / unit;\n        var pointName = count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.t,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getCpuPoints(dataArr, nameLegend, unit) {\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    var dataSeries = [];\n\n    var unitTxt = \"\";\n\n    if (unit == 1) {\n        unit = 1;\n        unitTxt = \"秒\"\n    }\n\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = Math.round(data.y / unit);\n        var pointName = data.date + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.x,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getBaseCpuPoints(dataArr, nameLegend, unit) {\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    var dataSeries = [];\n\n    var unitTxt = \"\";\n\n    if (unit == 1) {\n        unit = 1;\n        unitTxt = \"秒\"\n    }\n\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = masterNum * 1;\n        var pointName = data.date + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.x,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getMemoryPoints(dataArr, nameLegend, unit, defaultValue) {\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    var dataSeries = [];\n\n    var unitTxt = \"\";\n\n    if (unit == 1) {\n        unit = 1;\n        unitTxt = \"M\"\n    }\n    var count;\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        count = data.y;\n        if (defaultValue > 0) {\n            count = defaultValue;\n        }\n        var pointName = data.date + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.x,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getKeyPoints(dataArr, nameLegend, unit, defaultValue) {\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    var dataSeries = [];\n\n    var unitTxt = \"\";\n\n    if (unit == 1) {\n        unit = 1;\n        unitTxt = \"个\"\n    }\n    var count;\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        count = data.y;\n        if (defaultValue > 0) {\n            count = defaultValue;\n        }\n        var pointName = data.date + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.x,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\nfunction getNetPoints(dataArr, nameLegend, unit, timeUnit) {\n    var length = 0;\n    if(dataArr != undefined && dataArr != null){\n        length = dataArr.length;\n    }\n    var dataSeries = [];\n\n    var unitTxt = \"\";\n    if (typeof (unit) == \"undefined\") {\n        var byteCounter = 0;\n        var kbCounter = 0;\n        var mbCounter = 0;\n        for (var i = 0; i < length - 1; i++) {\n            var data = dataArr[i];\n            var count = data.y;\n            if (count < 1024) {\n                byteCounter = byteCounter + 1;\n            } else if (count >= 1024 && count < 1024 * 1024) {\n                kbCounter = kbCounter + 1;\n            } else {\n                mbCounter = mbCounter + 1;\n            }\n        }\n        if (byteCounter > kbCounter) {\n            if (byteCounter > mbCounter) {\n                unit = 1;\n                unitTxt = \"byte\";\n            } else {\n                unit = 1024 * 1024;\n                unitTxt = \"Mb\";\n            }\n        } else {\n            if (kbCounter > mbCounter) {\n                unit = 1024;\n                unitTxt = \"Kb\";\n            } else {\n                unit = 1024 * 1024;\n                unitTxt = \"Mb\";\n            }\n        }\n    } else {\n        if (unit == 1) {\n            unitTxt = \"byte\";\n        } else if (unit == 1024) {\n            unitTxt = \"Kb\";\n        } else if (unit == 1024 * 1024) {\n            unitTxt = \"Mb\";\n        }\n    }\n\n    if (timeUnit == 1) {\n        unit = 1;\n        unitTxt = \"秒\"\n    }\n\n    for (var i = 0; i < length - 1; i++) {\n        var data = dataArr[i];\n        var count = Math.round(data.y / unit);\n        var pointName = data.date + \":  \" + count + unitTxt;\n        var dataPoint = {\n            name: pointName,\n            x: data.x,\n            y: count,\n        };\n        dataSeries.push(dataPoint);\n    }\n\n    var seriesPoints = {\n        name: nameLegend,\n        data: dataSeries,\n        unit: unit,\n        unitTxt: unitTxt\n    };\n    return seriesPoints;\n}\n\n\nfunction getClientStatisticsByType(dataMap, type, unit, searchDate, exceptionType) {\n    var date = new Date(searchDate.replace('-', '/'));\n    var timestamp = date.getTime();//毫秒级\n    var finalPoints = [];\n    for (var client in dataMap) {\n        console.log(\"client:\" + client);\n        var tagName = client;\n        var statList = dataMap[client];\n        var statMap = setStatMap(statList, type);\n        var dataSeries = [];\n        for (var i = 0; i < 24 * 60; i++) {\n            var xValue = getTimestampAdd(timestamp, i);\n            var yValue = statMap.has(xValue) ? statMap.get(xValue) : 0;\n            var pointName = tagName\n                + \"<br/>\" + type + \": \" + +yValue + unit\n                + \"<br/>时间: \" + formatDate(xValue);\n            var dataPoint = {\n                name: pointName,\n                x: xValue,\n                y: yValue,\n            };\n            dataSeries.push(dataPoint);\n        }\n        console.log(\"dataSeries length:\" + dataSeries.length);\n        var seriesPoints = {\n            name: tagName,\n            data: dataSeries,\n            marker: {\n                radius: 1, // 曲线点半径，默认是4\n            },\n            events: {\n                click: function (e) {\n                    if (exceptionType != undefined) {\n                        var app_id = '&appId=' + appId;\n                        var search_time = '&searchTime=' + e.point.category;\n                        window.open('/client/show/latencyCommandDetails?' + app_id + search_time);\n                    }\n                }\n            }\n        };\n        finalPoints.push(seriesPoints);\n\n    }\n    return finalPoints;\n\n}\n\n\nfunction getAppLatencyInfo(dataMap, type, unit, searchDate, contextPath) {\n    var date = new Date(searchDate.replace('-', '/'));\n    var timestamp = date.getTime();//毫秒级\n    var finalPoints = [];\n    for (var event in dataMap) {\n        console.log(\"event:\" + event);\n        var tagName = event;\n        var statList = dataMap[event];\n        var statMap = setStatMap(statList, type);\n        var dataSeries = [];\n        for (var i = 0; i < 24 * 60; i++) {\n            var xValue = getTimestampAdd(timestamp, i);\n            var yValue = statMap.has(xValue) ? statMap.get(xValue) : 0;\n            if (xValue == 1588995285000) {\n                console.log(\"xValue: \" + xValue);\n                console.log(\"yValue: \" + yValue);\n            }\n            var pointName = tagName\n                + \"<br/>\" + type + \": \" + +yValue + unit\n                + \"<br/>时间: \" + formatDate(xValue);\n            var dataPoint = {\n                name: pointName,\n                x: xValue,\n                y: yValue,\n            };\n            dataSeries.push(dataPoint);\n        }\n        console.log(\"dataSeries length:\" + dataSeries.length);\n        var seriesPoints = {\n            name: tagName,\n            data: dataSeries,\n            marker: {\n                radius: 1, // 曲线点半径，默认是4\n            },\n            events: {\n                click: function (e) {\n                    var app_id = '&appId=' + appId;\n                    var search_time = '&searchTime=' + e.point.category;\n                    window.open(contextPath + '/admin/app/latencyInfoDetails?' + app_id + search_time);\n                }\n            }\n        };\n        finalPoints.push(seriesPoints);\n\n    }\n    return finalPoints;\n}\n\nfunction setStatMap(statList, type) {\n    var map = new Map();\n    for (var i = 0; i < statList.length; i++) {\n        var commandStat = statList[i];\n        var xValue = commandStat.timestamp * 1000;\n        var yValue;\n        if (type == 'count') {\n            yValue = commandStat.count;\n        } else if (type == 'cost') {\n            yValue = commandStat.cost;\n        } else if (type == 'bytesIn') {\n            yValue = commandStat.bytes_in / 1024 / 1024;\n        } else if (type == 'bytesOut') {\n            yValue = commandStat.bytes_out / 1024 / 1024;\n        }\n        map.set(xValue, yValue);\n        //console.log(\"x:y : \"+xValue+\":\"+yValue);\n    }\n    console.log(\"map size:\" + map.size);\n    return map;\n}\n\n/**\n * @param container 图表加载的位置，是页面上的一个DOM对象\n * @param title 图标标题\n * @param titleText 纵坐标标题\n */\nfunction getOption(container, title, titleText) {\n    var chartOption = {\n        chart: {\n            renderTo: container,\n            animation: Highcharts.svg,\n            borderColor: '#E6E6E6',\n            borderWidth: 2,\n            // backgroundColor: '#E6F1F5',\n            plotBackgroundColor: '#FFFFFF',\n            zoomType: 'x',\n            type: 'line',\n            marginRight: 10\n        },\n        title: {\n            useHTML: true,\n            text: title\n        },\n        xAxis: {\n            type: 'datetime',\n        },\n        yAxis: {\n            title: {\n                text: titleText\n            },\n            min: 0\n        },\n        plotOptions: {\n            line: {\n                dataLabels: {\n                    enabled: false\n                },\n                enableMouseTracking: true\n            },\n            series: {\n                //默认只能显示1000个点,如果为0就没有这个限制\n                turboThreshold: 0,\n                marker: {\n                    enabled: false\n                }\n            }\n        },\n        tooltip: {\n            formatter: function () {\n                return '<b>' + this.point.name + '</b><br/>';\n            }\n        },\n        legend: {\n            enabled: true\n        },\n        credits: {\n            enabled: false\n        },\n        exporting: {\n            enabled: true\n        },\n        series: []\n    };\n    return chartOption;\n}\n\nfunction getTimestampAdd(timestamp, min) {\n    var date = new Date(timestamp);\n    date.setMinutes(date.getMinutes() + min);\n    var timestampNew = date.getTime();//毫秒级\n    return timestampNew;\n}\n\nfunction formatDate(timestamp) {\n    var date = new Date(timestamp);\n    var YY = date.getFullYear() + '-';\n    var MM = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';\n    var DD = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate());\n    var hh = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';\n    var mm = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes());\n    return YY + MM + DD + \" \" + hh + mm;\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/fontawesome-free/css/all.css",
    "content": "/*!\n * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com\n * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n */\n.fa,\n.fas,\n.far,\n.fal,\n.fad,\n.fab {\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-font-smoothing: antialiased;\n  display: inline-block;\n  font-style: normal;\n  font-variant: normal;\n  text-rendering: auto;\n  line-height: 1; }\n\n.fa-lg {\n  font-size: 1.33333em;\n  line-height: 0.75em;\n  vertical-align: -.0667em; }\n\n.fa-xs {\n  font-size: .75em; }\n\n.fa-sm {\n  font-size: .875em; }\n\n.fa-1x {\n  font-size: 1em; }\n\n.fa-2x {\n  font-size: 2em; }\n\n.fa-3x {\n  font-size: 3em; }\n\n.fa-4x {\n  font-size: 4em; }\n\n.fa-5x {\n  font-size: 5em; }\n\n.fa-6x {\n  font-size: 6em; }\n\n.fa-7x {\n  font-size: 7em; }\n\n.fa-8x {\n  font-size: 8em; }\n\n.fa-9x {\n  font-size: 9em; }\n\n.fa-10x {\n  font-size: 10em; }\n\n.fa-fw {\n  text-align: center;\n  width: 1.25em; }\n\n.fa-ul {\n  list-style-type: none;\n  margin-left: 2.5em;\n  padding-left: 0; }\n  .fa-ul > li {\n    position: relative; }\n\n.fa-li {\n  left: -2em;\n  position: absolute;\n  text-align: center;\n  width: 2em;\n  line-height: inherit; }\n\n.fa-border {\n  border: solid 0.08em #eee;\n  border-radius: .1em;\n  padding: .2em .25em .15em; }\n\n.fa-pull-left {\n  float: left; }\n\n.fa-pull-right {\n  float: right; }\n\n.fa.fa-pull-left,\n.fas.fa-pull-left,\n.far.fa-pull-left,\n.fal.fa-pull-left,\n.fab.fa-pull-left {\n  margin-right: .3em; }\n\n.fa.fa-pull-right,\n.fas.fa-pull-right,\n.far.fa-pull-right,\n.fal.fa-pull-right,\n.fab.fa-pull-right {\n  margin-left: .3em; }\n\n.fa-spin {\n  -webkit-animation: fa-spin 2s infinite linear;\n          animation: fa-spin 2s infinite linear; }\n\n.fa-pulse {\n  -webkit-animation: fa-spin 1s infinite steps(8);\n          animation: fa-spin 1s infinite steps(8); }\n\n@-webkit-keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg); }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg); } }\n\n@keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg); }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg); } }\n\n.fa-rotate-90 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)\";\n  -webkit-transform: rotate(90deg);\n          transform: rotate(90deg); }\n\n.fa-rotate-180 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)\";\n  -webkit-transform: rotate(180deg);\n          transform: rotate(180deg); }\n\n.fa-rotate-270 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)\";\n  -webkit-transform: rotate(270deg);\n          transform: rotate(270deg); }\n\n.fa-flip-horizontal {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)\";\n  -webkit-transform: scale(-1, 1);\n          transform: scale(-1, 1); }\n\n.fa-flip-vertical {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n  -webkit-transform: scale(1, -1);\n          transform: scale(1, -1); }\n\n.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n  -webkit-transform: scale(-1, -1);\n          transform: scale(-1, -1); }\n\n:root .fa-rotate-90,\n:root .fa-rotate-180,\n:root .fa-rotate-270,\n:root .fa-flip-horizontal,\n:root .fa-flip-vertical,\n:root .fa-flip-both {\n  -webkit-filter: none;\n          filter: none; }\n\n.fa-stack {\n  display: inline-block;\n  height: 2em;\n  line-height: 2em;\n  position: relative;\n  vertical-align: middle;\n  width: 2.5em; }\n\n.fa-stack-1x,\n.fa-stack-2x {\n  left: 0;\n  position: absolute;\n  text-align: center;\n  width: 100%; }\n\n.fa-stack-1x {\n  line-height: inherit; }\n\n.fa-stack-2x {\n  font-size: 2em; }\n\n.fa-inverse {\n  color: #fff; }\n\n/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen\nreaders do not read off random characters that represent icons */\n.fa-500px:before {\n  content: \"\\f26e\"; }\n\n.fa-accessible-icon:before {\n  content: \"\\f368\"; }\n\n.fa-accusoft:before {\n  content: \"\\f369\"; }\n\n.fa-acquisitions-incorporated:before {\n  content: \"\\f6af\"; }\n\n.fa-ad:before {\n  content: \"\\f641\"; }\n\n.fa-address-book:before {\n  content: \"\\f2b9\"; }\n\n.fa-address-card:before {\n  content: \"\\f2bb\"; }\n\n.fa-adjust:before {\n  content: \"\\f042\"; }\n\n.fa-adn:before {\n  content: \"\\f170\"; }\n\n.fa-adversal:before {\n  content: \"\\f36a\"; }\n\n.fa-affiliatetheme:before {\n  content: \"\\f36b\"; }\n\n.fa-air-freshener:before {\n  content: \"\\f5d0\"; }\n\n.fa-airbnb:before {\n  content: \"\\f834\"; }\n\n.fa-algolia:before {\n  content: \"\\f36c\"; }\n\n.fa-align-center:before {\n  content: \"\\f037\"; }\n\n.fa-align-justify:before {\n  content: \"\\f039\"; }\n\n.fa-align-left:before {\n  content: \"\\f036\"; }\n\n.fa-align-right:before {\n  content: \"\\f038\"; }\n\n.fa-alipay:before {\n  content: \"\\f642\"; }\n\n.fa-allergies:before {\n  content: \"\\f461\"; }\n\n.fa-amazon:before {\n  content: \"\\f270\"; }\n\n.fa-amazon-pay:before {\n  content: \"\\f42c\"; }\n\n.fa-ambulance:before {\n  content: \"\\f0f9\"; }\n\n.fa-american-sign-language-interpreting:before {\n  content: \"\\f2a3\"; }\n\n.fa-amilia:before {\n  content: \"\\f36d\"; }\n\n.fa-anchor:before {\n  content: \"\\f13d\"; }\n\n.fa-android:before {\n  content: \"\\f17b\"; }\n\n.fa-angellist:before {\n  content: \"\\f209\"; }\n\n.fa-angle-double-down:before {\n  content: \"\\f103\"; }\n\n.fa-angle-double-left:before {\n  content: \"\\f100\"; }\n\n.fa-angle-double-right:before {\n  content: \"\\f101\"; }\n\n.fa-angle-double-up:before {\n  content: \"\\f102\"; }\n\n.fa-angle-down:before {\n  content: \"\\f107\"; }\n\n.fa-angle-left:before {\n  content: \"\\f104\"; }\n\n.fa-angle-right:before {\n  content: \"\\f105\"; }\n\n.fa-angle-up:before {\n  content: \"\\f106\"; }\n\n.fa-angry:before {\n  content: \"\\f556\"; }\n\n.fa-angrycreative:before {\n  content: \"\\f36e\"; }\n\n.fa-angular:before {\n  content: \"\\f420\"; }\n\n.fa-ankh:before {\n  content: \"\\f644\"; }\n\n.fa-app-store:before {\n  content: \"\\f36f\"; }\n\n.fa-app-store-ios:before {\n  content: \"\\f370\"; }\n\n.fa-apper:before {\n  content: \"\\f371\"; }\n\n.fa-apple:before {\n  content: \"\\f179\"; }\n\n.fa-apple-alt:before {\n  content: \"\\f5d1\"; }\n\n.fa-apple-pay:before {\n  content: \"\\f415\"; }\n\n.fa-archive:before {\n  content: \"\\f187\"; }\n\n.fa-archway:before {\n  content: \"\\f557\"; }\n\n.fa-arrow-alt-circle-down:before {\n  content: \"\\f358\"; }\n\n.fa-arrow-alt-circle-left:before {\n  content: \"\\f359\"; }\n\n.fa-arrow-alt-circle-right:before {\n  content: \"\\f35a\"; }\n\n.fa-arrow-alt-circle-up:before {\n  content: \"\\f35b\"; }\n\n.fa-arrow-circle-down:before {\n  content: \"\\f0ab\"; }\n\n.fa-arrow-circle-left:before {\n  content: \"\\f0a8\"; }\n\n.fa-arrow-circle-right:before {\n  content: \"\\f0a9\"; }\n\n.fa-arrow-circle-up:before {\n  content: \"\\f0aa\"; }\n\n.fa-arrow-down:before {\n  content: \"\\f063\"; }\n\n.fa-arrow-left:before {\n  content: \"\\f060\"; }\n\n.fa-arrow-right:before {\n  content: \"\\f061\"; }\n\n.fa-arrow-up:before {\n  content: \"\\f062\"; }\n\n.fa-arrows-alt:before {\n  content: \"\\f0b2\"; }\n\n.fa-arrows-alt-h:before {\n  content: \"\\f337\"; }\n\n.fa-arrows-alt-v:before {\n  content: \"\\f338\"; }\n\n.fa-artstation:before {\n  content: \"\\f77a\"; }\n\n.fa-assistive-listening-systems:before {\n  content: \"\\f2a2\"; }\n\n.fa-asterisk:before {\n  content: \"\\f069\"; }\n\n.fa-asymmetrik:before {\n  content: \"\\f372\"; }\n\n.fa-at:before {\n  content: \"\\f1fa\"; }\n\n.fa-atlas:before {\n  content: \"\\f558\"; }\n\n.fa-atlassian:before {\n  content: \"\\f77b\"; }\n\n.fa-atom:before {\n  content: \"\\f5d2\"; }\n\n.fa-audible:before {\n  content: \"\\f373\"; }\n\n.fa-audio-description:before {\n  content: \"\\f29e\"; }\n\n.fa-autoprefixer:before {\n  content: \"\\f41c\"; }\n\n.fa-avianex:before {\n  content: \"\\f374\"; }\n\n.fa-aviato:before {\n  content: \"\\f421\"; }\n\n.fa-award:before {\n  content: \"\\f559\"; }\n\n.fa-aws:before {\n  content: \"\\f375\"; }\n\n.fa-baby:before {\n  content: \"\\f77c\"; }\n\n.fa-baby-carriage:before {\n  content: \"\\f77d\"; }\n\n.fa-backspace:before {\n  content: \"\\f55a\"; }\n\n.fa-backward:before {\n  content: \"\\f04a\"; }\n\n.fa-bacon:before {\n  content: \"\\f7e5\"; }\n\n.fa-bacteria:before {\n  content: \"\\e059\"; }\n\n.fa-bacterium:before {\n  content: \"\\e05a\"; }\n\n.fa-bahai:before {\n  content: \"\\f666\"; }\n\n.fa-balance-scale:before {\n  content: \"\\f24e\"; }\n\n.fa-balance-scale-left:before {\n  content: \"\\f515\"; }\n\n.fa-balance-scale-right:before {\n  content: \"\\f516\"; }\n\n.fa-ban:before {\n  content: \"\\f05e\"; }\n\n.fa-band-aid:before {\n  content: \"\\f462\"; }\n\n.fa-bandcamp:before {\n  content: \"\\f2d5\"; }\n\n.fa-barcode:before {\n  content: \"\\f02a\"; }\n\n.fa-bars:before {\n  content: \"\\f0c9\"; }\n\n.fa-baseball-ball:before {\n  content: \"\\f433\"; }\n\n.fa-basketball-ball:before {\n  content: \"\\f434\"; }\n\n.fa-bath:before {\n  content: \"\\f2cd\"; }\n\n.fa-battery-empty:before {\n  content: \"\\f244\"; }\n\n.fa-battery-full:before {\n  content: \"\\f240\"; }\n\n.fa-battery-half:before {\n  content: \"\\f242\"; }\n\n.fa-battery-quarter:before {\n  content: \"\\f243\"; }\n\n.fa-battery-three-quarters:before {\n  content: \"\\f241\"; }\n\n.fa-battle-net:before {\n  content: \"\\f835\"; }\n\n.fa-bed:before {\n  content: \"\\f236\"; }\n\n.fa-beer:before {\n  content: \"\\f0fc\"; }\n\n.fa-behance:before {\n  content: \"\\f1b4\"; }\n\n.fa-behance-square:before {\n  content: \"\\f1b5\"; }\n\n.fa-bell:before {\n  content: \"\\f0f3\"; }\n\n.fa-bell-slash:before {\n  content: \"\\f1f6\"; }\n\n.fa-bezier-curve:before {\n  content: \"\\f55b\"; }\n\n.fa-bible:before {\n  content: \"\\f647\"; }\n\n.fa-bicycle:before {\n  content: \"\\f206\"; }\n\n.fa-biking:before {\n  content: \"\\f84a\"; }\n\n.fa-bimobject:before {\n  content: \"\\f378\"; }\n\n.fa-binoculars:before {\n  content: \"\\f1e5\"; }\n\n.fa-biohazard:before {\n  content: \"\\f780\"; }\n\n.fa-birthday-cake:before {\n  content: \"\\f1fd\"; }\n\n.fa-bitbucket:before {\n  content: \"\\f171\"; }\n\n.fa-bitcoin:before {\n  content: \"\\f379\"; }\n\n.fa-bity:before {\n  content: \"\\f37a\"; }\n\n.fa-black-tie:before {\n  content: \"\\f27e\"; }\n\n.fa-blackberry:before {\n  content: \"\\f37b\"; }\n\n.fa-blender:before {\n  content: \"\\f517\"; }\n\n.fa-blender-phone:before {\n  content: \"\\f6b6\"; }\n\n.fa-blind:before {\n  content: \"\\f29d\"; }\n\n.fa-blog:before {\n  content: \"\\f781\"; }\n\n.fa-blogger:before {\n  content: \"\\f37c\"; }\n\n.fa-blogger-b:before {\n  content: \"\\f37d\"; }\n\n.fa-bluetooth:before {\n  content: \"\\f293\"; }\n\n.fa-bluetooth-b:before {\n  content: \"\\f294\"; }\n\n.fa-bold:before {\n  content: \"\\f032\"; }\n\n.fa-bolt:before {\n  content: \"\\f0e7\"; }\n\n.fa-bomb:before {\n  content: \"\\f1e2\"; }\n\n.fa-bone:before {\n  content: \"\\f5d7\"; }\n\n.fa-bong:before {\n  content: \"\\f55c\"; }\n\n.fa-book:before {\n  content: \"\\f02d\"; }\n\n.fa-book-dead:before {\n  content: \"\\f6b7\"; }\n\n.fa-book-medical:before {\n  content: \"\\f7e6\"; }\n\n.fa-book-open:before {\n  content: \"\\f518\"; }\n\n.fa-book-reader:before {\n  content: \"\\f5da\"; }\n\n.fa-bookmark:before {\n  content: \"\\f02e\"; }\n\n.fa-bootstrap:before {\n  content: \"\\f836\"; }\n\n.fa-border-all:before {\n  content: \"\\f84c\"; }\n\n.fa-border-none:before {\n  content: \"\\f850\"; }\n\n.fa-border-style:before {\n  content: \"\\f853\"; }\n\n.fa-bowling-ball:before {\n  content: \"\\f436\"; }\n\n.fa-box:before {\n  content: \"\\f466\"; }\n\n.fa-box-open:before {\n  content: \"\\f49e\"; }\n\n.fa-box-tissue:before {\n  content: \"\\e05b\"; }\n\n.fa-boxes:before {\n  content: \"\\f468\"; }\n\n.fa-braille:before {\n  content: \"\\f2a1\"; }\n\n.fa-brain:before {\n  content: \"\\f5dc\"; }\n\n.fa-bread-slice:before {\n  content: \"\\f7ec\"; }\n\n.fa-briefcase:before {\n  content: \"\\f0b1\"; }\n\n.fa-briefcase-medical:before {\n  content: \"\\f469\"; }\n\n.fa-broadcast-tower:before {\n  content: \"\\f519\"; }\n\n.fa-broom:before {\n  content: \"\\f51a\"; }\n\n.fa-brush:before {\n  content: \"\\f55d\"; }\n\n.fa-btc:before {\n  content: \"\\f15a\"; }\n\n.fa-buffer:before {\n  content: \"\\f837\"; }\n\n.fa-bug:before {\n  content: \"\\f188\"; }\n\n.fa-building:before {\n  content: \"\\f1ad\"; }\n\n.fa-bullhorn:before {\n  content: \"\\f0a1\"; }\n\n.fa-bullseye:before {\n  content: \"\\f140\"; }\n\n.fa-burn:before {\n  content: \"\\f46a\"; }\n\n.fa-buromobelexperte:before {\n  content: \"\\f37f\"; }\n\n.fa-bus:before {\n  content: \"\\f207\"; }\n\n.fa-bus-alt:before {\n  content: \"\\f55e\"; }\n\n.fa-business-time:before {\n  content: \"\\f64a\"; }\n\n.fa-buy-n-large:before {\n  content: \"\\f8a6\"; }\n\n.fa-buysellads:before {\n  content: \"\\f20d\"; }\n\n.fa-calculator:before {\n  content: \"\\f1ec\"; }\n\n.fa-calendar:before {\n  content: \"\\f133\"; }\n\n.fa-calendar-alt:before {\n  content: \"\\f073\"; }\n\n.fa-calendar-check:before {\n  content: \"\\f274\"; }\n\n.fa-calendar-day:before {\n  content: \"\\f783\"; }\n\n.fa-calendar-minus:before {\n  content: \"\\f272\"; }\n\n.fa-calendar-plus:before {\n  content: \"\\f271\"; }\n\n.fa-calendar-times:before {\n  content: \"\\f273\"; }\n\n.fa-calendar-week:before {\n  content: \"\\f784\"; }\n\n.fa-camera:before {\n  content: \"\\f030\"; }\n\n.fa-camera-retro:before {\n  content: \"\\f083\"; }\n\n.fa-campground:before {\n  content: \"\\f6bb\"; }\n\n.fa-canadian-maple-leaf:before {\n  content: \"\\f785\"; }\n\n.fa-candy-cane:before {\n  content: \"\\f786\"; }\n\n.fa-cannabis:before {\n  content: \"\\f55f\"; }\n\n.fa-capsules:before {\n  content: \"\\f46b\"; }\n\n.fa-car:before {\n  content: \"\\f1b9\"; }\n\n.fa-car-alt:before {\n  content: \"\\f5de\"; }\n\n.fa-car-battery:before {\n  content: \"\\f5df\"; }\n\n.fa-car-crash:before {\n  content: \"\\f5e1\"; }\n\n.fa-car-side:before {\n  content: \"\\f5e4\"; }\n\n.fa-caravan:before {\n  content: \"\\f8ff\"; }\n\n.fa-caret-down:before {\n  content: \"\\f0d7\"; }\n\n.fa-caret-left:before {\n  content: \"\\f0d9\"; }\n\n.fa-caret-right:before {\n  content: \"\\f0da\"; }\n\n.fa-caret-square-down:before {\n  content: \"\\f150\"; }\n\n.fa-caret-square-left:before {\n  content: \"\\f191\"; }\n\n.fa-caret-square-right:before {\n  content: \"\\f152\"; }\n\n.fa-caret-square-up:before {\n  content: \"\\f151\"; }\n\n.fa-caret-up:before {\n  content: \"\\f0d8\"; }\n\n.fa-carrot:before {\n  content: \"\\f787\"; }\n\n.fa-cart-arrow-down:before {\n  content: \"\\f218\"; }\n\n.fa-cart-plus:before {\n  content: \"\\f217\"; }\n\n.fa-cash-register:before {\n  content: \"\\f788\"; }\n\n.fa-cat:before {\n  content: \"\\f6be\"; }\n\n.fa-cc-amazon-pay:before {\n  content: \"\\f42d\"; }\n\n.fa-cc-amex:before {\n  content: \"\\f1f3\"; }\n\n.fa-cc-apple-pay:before {\n  content: \"\\f416\"; }\n\n.fa-cc-diners-club:before {\n  content: \"\\f24c\"; }\n\n.fa-cc-discover:before {\n  content: \"\\f1f2\"; }\n\n.fa-cc-jcb:before {\n  content: \"\\f24b\"; }\n\n.fa-cc-mastercard:before {\n  content: \"\\f1f1\"; }\n\n.fa-cc-paypal:before {\n  content: \"\\f1f4\"; }\n\n.fa-cc-stripe:before {\n  content: \"\\f1f5\"; }\n\n.fa-cc-visa:before {\n  content: \"\\f1f0\"; }\n\n.fa-centercode:before {\n  content: \"\\f380\"; }\n\n.fa-centos:before {\n  content: \"\\f789\"; }\n\n.fa-certificate:before {\n  content: \"\\f0a3\"; }\n\n.fa-chair:before {\n  content: \"\\f6c0\"; }\n\n.fa-chalkboard:before {\n  content: \"\\f51b\"; }\n\n.fa-chalkboard-teacher:before {\n  content: \"\\f51c\"; }\n\n.fa-charging-station:before {\n  content: \"\\f5e7\"; }\n\n.fa-chart-area:before {\n  content: \"\\f1fe\"; }\n\n.fa-chart-bar:before {\n  content: \"\\f080\"; }\n\n.fa-chart-line:before {\n  content: \"\\f201\"; }\n\n.fa-chart-pie:before {\n  content: \"\\f200\"; }\n\n.fa-check:before {\n  content: \"\\f00c\"; }\n\n.fa-check-circle:before {\n  content: \"\\f058\"; }\n\n.fa-check-double:before {\n  content: \"\\f560\"; }\n\n.fa-check-square:before {\n  content: \"\\f14a\"; }\n\n.fa-cheese:before {\n  content: \"\\f7ef\"; }\n\n.fa-chess:before {\n  content: \"\\f439\"; }\n\n.fa-chess-bishop:before {\n  content: \"\\f43a\"; }\n\n.fa-chess-board:before {\n  content: \"\\f43c\"; }\n\n.fa-chess-king:before {\n  content: \"\\f43f\"; }\n\n.fa-chess-knight:before {\n  content: \"\\f441\"; }\n\n.fa-chess-pawn:before {\n  content: \"\\f443\"; }\n\n.fa-chess-queen:before {\n  content: \"\\f445\"; }\n\n.fa-chess-rook:before {\n  content: \"\\f447\"; }\n\n.fa-chevron-circle-down:before {\n  content: \"\\f13a\"; }\n\n.fa-chevron-circle-left:before {\n  content: \"\\f137\"; }\n\n.fa-chevron-circle-right:before {\n  content: \"\\f138\"; }\n\n.fa-chevron-circle-up:before {\n  content: \"\\f139\"; }\n\n.fa-chevron-down:before {\n  content: \"\\f078\"; }\n\n.fa-chevron-left:before {\n  content: \"\\f053\"; }\n\n.fa-chevron-right:before {\n  content: \"\\f054\"; }\n\n.fa-chevron-up:before {\n  content: \"\\f077\"; }\n\n.fa-child:before {\n  content: \"\\f1ae\"; }\n\n.fa-chrome:before {\n  content: \"\\f268\"; }\n\n.fa-chromecast:before {\n  content: \"\\f838\"; }\n\n.fa-church:before {\n  content: \"\\f51d\"; }\n\n.fa-circle:before {\n  content: \"\\f111\"; }\n\n.fa-circle-notch:before {\n  content: \"\\f1ce\"; }\n\n.fa-city:before {\n  content: \"\\f64f\"; }\n\n.fa-clinic-medical:before {\n  content: \"\\f7f2\"; }\n\n.fa-clipboard:before {\n  content: \"\\f328\"; }\n\n.fa-clipboard-check:before {\n  content: \"\\f46c\"; }\n\n.fa-clipboard-list:before {\n  content: \"\\f46d\"; }\n\n.fa-clock:before {\n  content: \"\\f017\"; }\n\n.fa-clone:before {\n  content: \"\\f24d\"; }\n\n.fa-closed-captioning:before {\n  content: \"\\f20a\"; }\n\n.fa-cloud:before {\n  content: \"\\f0c2\"; }\n\n.fa-cloud-download-alt:before {\n  content: \"\\f381\"; }\n\n.fa-cloud-meatball:before {\n  content: \"\\f73b\"; }\n\n.fa-cloud-moon:before {\n  content: \"\\f6c3\"; }\n\n.fa-cloud-moon-rain:before {\n  content: \"\\f73c\"; }\n\n.fa-cloud-rain:before {\n  content: \"\\f73d\"; }\n\n.fa-cloud-showers-heavy:before {\n  content: \"\\f740\"; }\n\n.fa-cloud-sun:before {\n  content: \"\\f6c4\"; }\n\n.fa-cloud-sun-rain:before {\n  content: \"\\f743\"; }\n\n.fa-cloud-upload-alt:before {\n  content: \"\\f382\"; }\n\n.fa-cloudflare:before {\n  content: \"\\e07d\"; }\n\n.fa-cloudscale:before {\n  content: \"\\f383\"; }\n\n.fa-cloudsmith:before {\n  content: \"\\f384\"; }\n\n.fa-cloudversify:before {\n  content: \"\\f385\"; }\n\n.fa-cocktail:before {\n  content: \"\\f561\"; }\n\n.fa-code:before {\n  content: \"\\f121\"; }\n\n.fa-code-branch:before {\n  content: \"\\f126\"; }\n\n.fa-codepen:before {\n  content: \"\\f1cb\"; }\n\n.fa-codiepie:before {\n  content: \"\\f284\"; }\n\n.fa-coffee:before {\n  content: \"\\f0f4\"; }\n\n.fa-cog:before {\n  content: \"\\f013\"; }\n\n.fa-cogs:before {\n  content: \"\\f085\"; }\n\n.fa-coins:before {\n  content: \"\\f51e\"; }\n\n.fa-columns:before {\n  content: \"\\f0db\"; }\n\n.fa-comment:before {\n  content: \"\\f075\"; }\n\n.fa-comment-alt:before {\n  content: \"\\f27a\"; }\n\n.fa-comment-dollar:before {\n  content: \"\\f651\"; }\n\n.fa-comment-dots:before {\n  content: \"\\f4ad\"; }\n\n.fa-comment-medical:before {\n  content: \"\\f7f5\"; }\n\n.fa-comment-slash:before {\n  content: \"\\f4b3\"; }\n\n.fa-comments:before {\n  content: \"\\f086\"; }\n\n.fa-comments-dollar:before {\n  content: \"\\f653\"; }\n\n.fa-compact-disc:before {\n  content: \"\\f51f\"; }\n\n.fa-compass:before {\n  content: \"\\f14e\"; }\n\n.fa-compress:before {\n  content: \"\\f066\"; }\n\n.fa-compress-alt:before {\n  content: \"\\f422\"; }\n\n.fa-compress-arrows-alt:before {\n  content: \"\\f78c\"; }\n\n.fa-concierge-bell:before {\n  content: \"\\f562\"; }\n\n.fa-confluence:before {\n  content: \"\\f78d\"; }\n\n.fa-connectdevelop:before {\n  content: \"\\f20e\"; }\n\n.fa-contao:before {\n  content: \"\\f26d\"; }\n\n.fa-cookie:before {\n  content: \"\\f563\"; }\n\n.fa-cookie-bite:before {\n  content: \"\\f564\"; }\n\n.fa-copy:before {\n  content: \"\\f0c5\"; }\n\n.fa-copyright:before {\n  content: \"\\f1f9\"; }\n\n.fa-cotton-bureau:before {\n  content: \"\\f89e\"; }\n\n.fa-couch:before {\n  content: \"\\f4b8\"; }\n\n.fa-cpanel:before {\n  content: \"\\f388\"; }\n\n.fa-creative-commons:before {\n  content: \"\\f25e\"; }\n\n.fa-creative-commons-by:before {\n  content: \"\\f4e7\"; }\n\n.fa-creative-commons-nc:before {\n  content: \"\\f4e8\"; }\n\n.fa-creative-commons-nc-eu:before {\n  content: \"\\f4e9\"; }\n\n.fa-creative-commons-nc-jp:before {\n  content: \"\\f4ea\"; }\n\n.fa-creative-commons-nd:before {\n  content: \"\\f4eb\"; }\n\n.fa-creative-commons-pd:before {\n  content: \"\\f4ec\"; }\n\n.fa-creative-commons-pd-alt:before {\n  content: \"\\f4ed\"; }\n\n.fa-creative-commons-remix:before {\n  content: \"\\f4ee\"; }\n\n.fa-creative-commons-sa:before {\n  content: \"\\f4ef\"; }\n\n.fa-creative-commons-sampling:before {\n  content: \"\\f4f0\"; }\n\n.fa-creative-commons-sampling-plus:before {\n  content: \"\\f4f1\"; }\n\n.fa-creative-commons-share:before {\n  content: \"\\f4f2\"; }\n\n.fa-creative-commons-zero:before {\n  content: \"\\f4f3\"; }\n\n.fa-credit-card:before {\n  content: \"\\f09d\"; }\n\n.fa-critical-role:before {\n  content: \"\\f6c9\"; }\n\n.fa-crop:before {\n  content: \"\\f125\"; }\n\n.fa-crop-alt:before {\n  content: \"\\f565\"; }\n\n.fa-cross:before {\n  content: \"\\f654\"; }\n\n.fa-crosshairs:before {\n  content: \"\\f05b\"; }\n\n.fa-crow:before {\n  content: \"\\f520\"; }\n\n.fa-crown:before {\n  content: \"\\f521\"; }\n\n.fa-crutch:before {\n  content: \"\\f7f7\"; }\n\n.fa-css3:before {\n  content: \"\\f13c\"; }\n\n.fa-css3-alt:before {\n  content: \"\\f38b\"; }\n\n.fa-cube:before {\n  content: \"\\f1b2\"; }\n\n.fa-cubes:before {\n  content: \"\\f1b3\"; }\n\n.fa-cut:before {\n  content: \"\\f0c4\"; }\n\n.fa-cuttlefish:before {\n  content: \"\\f38c\"; }\n\n.fa-d-and-d:before {\n  content: \"\\f38d\"; }\n\n.fa-d-and-d-beyond:before {\n  content: \"\\f6ca\"; }\n\n.fa-dailymotion:before {\n  content: \"\\e052\"; }\n\n.fa-dashcube:before {\n  content: \"\\f210\"; }\n\n.fa-database:before {\n  content: \"\\f1c0\"; }\n\n.fa-deaf:before {\n  content: \"\\f2a4\"; }\n\n.fa-deezer:before {\n  content: \"\\e077\"; }\n\n.fa-delicious:before {\n  content: \"\\f1a5\"; }\n\n.fa-democrat:before {\n  content: \"\\f747\"; }\n\n.fa-deploydog:before {\n  content: \"\\f38e\"; }\n\n.fa-deskpro:before {\n  content: \"\\f38f\"; }\n\n.fa-desktop:before {\n  content: \"\\f108\"; }\n\n.fa-dev:before {\n  content: \"\\f6cc\"; }\n\n.fa-deviantart:before {\n  content: \"\\f1bd\"; }\n\n.fa-dharmachakra:before {\n  content: \"\\f655\"; }\n\n.fa-dhl:before {\n  content: \"\\f790\"; }\n\n.fa-diagnoses:before {\n  content: \"\\f470\"; }\n\n.fa-diaspora:before {\n  content: \"\\f791\"; }\n\n.fa-dice:before {\n  content: \"\\f522\"; }\n\n.fa-dice-d20:before {\n  content: \"\\f6cf\"; }\n\n.fa-dice-d6:before {\n  content: \"\\f6d1\"; }\n\n.fa-dice-five:before {\n  content: \"\\f523\"; }\n\n.fa-dice-four:before {\n  content: \"\\f524\"; }\n\n.fa-dice-one:before {\n  content: \"\\f525\"; }\n\n.fa-dice-six:before {\n  content: \"\\f526\"; }\n\n.fa-dice-three:before {\n  content: \"\\f527\"; }\n\n.fa-dice-two:before {\n  content: \"\\f528\"; }\n\n.fa-digg:before {\n  content: \"\\f1a6\"; }\n\n.fa-digital-ocean:before {\n  content: \"\\f391\"; }\n\n.fa-digital-tachograph:before {\n  content: \"\\f566\"; }\n\n.fa-directions:before {\n  content: \"\\f5eb\"; }\n\n.fa-discord:before {\n  content: \"\\f392\"; }\n\n.fa-discourse:before {\n  content: \"\\f393\"; }\n\n.fa-disease:before {\n  content: \"\\f7fa\"; }\n\n.fa-divide:before {\n  content: \"\\f529\"; }\n\n.fa-dizzy:before {\n  content: \"\\f567\"; }\n\n.fa-dna:before {\n  content: \"\\f471\"; }\n\n.fa-dochub:before {\n  content: \"\\f394\"; }\n\n.fa-docker:before {\n  content: \"\\f395\"; }\n\n.fa-dog:before {\n  content: \"\\f6d3\"; }\n\n.fa-dollar-sign:before {\n  content: \"\\f155\"; }\n\n.fa-dolly:before {\n  content: \"\\f472\"; }\n\n.fa-dolly-flatbed:before {\n  content: \"\\f474\"; }\n\n.fa-donate:before {\n  content: \"\\f4b9\"; }\n\n.fa-door-closed:before {\n  content: \"\\f52a\"; }\n\n.fa-door-open:before {\n  content: \"\\f52b\"; }\n\n.fa-dot-circle:before {\n  content: \"\\f192\"; }\n\n.fa-dove:before {\n  content: \"\\f4ba\"; }\n\n.fa-download:before {\n  content: \"\\f019\"; }\n\n.fa-draft2digital:before {\n  content: \"\\f396\"; }\n\n.fa-drafting-compass:before {\n  content: \"\\f568\"; }\n\n.fa-dragon:before {\n  content: \"\\f6d5\"; }\n\n.fa-draw-polygon:before {\n  content: \"\\f5ee\"; }\n\n.fa-dribbble:before {\n  content: \"\\f17d\"; }\n\n.fa-dribbble-square:before {\n  content: \"\\f397\"; }\n\n.fa-dropbox:before {\n  content: \"\\f16b\"; }\n\n.fa-drum:before {\n  content: \"\\f569\"; }\n\n.fa-drum-steelpan:before {\n  content: \"\\f56a\"; }\n\n.fa-drumstick-bite:before {\n  content: \"\\f6d7\"; }\n\n.fa-drupal:before {\n  content: \"\\f1a9\"; }\n\n.fa-dumbbell:before {\n  content: \"\\f44b\"; }\n\n.fa-dumpster:before {\n  content: \"\\f793\"; }\n\n.fa-dumpster-fire:before {\n  content: \"\\f794\"; }\n\n.fa-dungeon:before {\n  content: \"\\f6d9\"; }\n\n.fa-dyalog:before {\n  content: \"\\f399\"; }\n\n.fa-earlybirds:before {\n  content: \"\\f39a\"; }\n\n.fa-ebay:before {\n  content: \"\\f4f4\"; }\n\n.fa-edge:before {\n  content: \"\\f282\"; }\n\n.fa-edge-legacy:before {\n  content: \"\\e078\"; }\n\n.fa-edit:before {\n  content: \"\\f044\"; }\n\n.fa-egg:before {\n  content: \"\\f7fb\"; }\n\n.fa-eject:before {\n  content: \"\\f052\"; }\n\n.fa-elementor:before {\n  content: \"\\f430\"; }\n\n.fa-ellipsis-h:before {\n  content: \"\\f141\"; }\n\n.fa-ellipsis-v:before {\n  content: \"\\f142\"; }\n\n.fa-ello:before {\n  content: \"\\f5f1\"; }\n\n.fa-ember:before {\n  content: \"\\f423\"; }\n\n.fa-empire:before {\n  content: \"\\f1d1\"; }\n\n.fa-envelope:before {\n  content: \"\\f0e0\"; }\n\n.fa-envelope-open:before {\n  content: \"\\f2b6\"; }\n\n.fa-envelope-open-text:before {\n  content: \"\\f658\"; }\n\n.fa-envelope-square:before {\n  content: \"\\f199\"; }\n\n.fa-envira:before {\n  content: \"\\f299\"; }\n\n.fa-equals:before {\n  content: \"\\f52c\"; }\n\n.fa-eraser:before {\n  content: \"\\f12d\"; }\n\n.fa-erlang:before {\n  content: \"\\f39d\"; }\n\n.fa-ethereum:before {\n  content: \"\\f42e\"; }\n\n.fa-ethernet:before {\n  content: \"\\f796\"; }\n\n.fa-etsy:before {\n  content: \"\\f2d7\"; }\n\n.fa-euro-sign:before {\n  content: \"\\f153\"; }\n\n.fa-evernote:before {\n  content: \"\\f839\"; }\n\n.fa-exchange-alt:before {\n  content: \"\\f362\"; }\n\n.fa-exclamation:before {\n  content: \"\\f12a\"; }\n\n.fa-exclamation-circle:before {\n  content: \"\\f06a\"; }\n\n.fa-exclamation-triangle:before {\n  content: \"\\f071\"; }\n\n.fa-expand:before {\n  content: \"\\f065\"; }\n\n.fa-expand-alt:before {\n  content: \"\\f424\"; }\n\n.fa-expand-arrows-alt:before {\n  content: \"\\f31e\"; }\n\n.fa-expeditedssl:before {\n  content: \"\\f23e\"; }\n\n.fa-external-link-alt:before {\n  content: \"\\f35d\"; }\n\n.fa-external-link-square-alt:before {\n  content: \"\\f360\"; }\n\n.fa-eye:before {\n  content: \"\\f06e\"; }\n\n.fa-eye-dropper:before {\n  content: \"\\f1fb\"; }\n\n.fa-eye-slash:before {\n  content: \"\\f070\"; }\n\n.fa-facebook:before {\n  content: \"\\f09a\"; }\n\n.fa-facebook-f:before {\n  content: \"\\f39e\"; }\n\n.fa-facebook-messenger:before {\n  content: \"\\f39f\"; }\n\n.fa-facebook-square:before {\n  content: \"\\f082\"; }\n\n.fa-fan:before {\n  content: \"\\f863\"; }\n\n.fa-fantasy-flight-games:before {\n  content: \"\\f6dc\"; }\n\n.fa-fast-backward:before {\n  content: \"\\f049\"; }\n\n.fa-fast-forward:before {\n  content: \"\\f050\"; }\n\n.fa-faucet:before {\n  content: \"\\e005\"; }\n\n.fa-fax:before {\n  content: \"\\f1ac\"; }\n\n.fa-feather:before {\n  content: \"\\f52d\"; }\n\n.fa-feather-alt:before {\n  content: \"\\f56b\"; }\n\n.fa-fedex:before {\n  content: \"\\f797\"; }\n\n.fa-fedora:before {\n  content: \"\\f798\"; }\n\n.fa-female:before {\n  content: \"\\f182\"; }\n\n.fa-fighter-jet:before {\n  content: \"\\f0fb\"; }\n\n.fa-figma:before {\n  content: \"\\f799\"; }\n\n.fa-file:before {\n  content: \"\\f15b\"; }\n\n.fa-file-alt:before {\n  content: \"\\f15c\"; }\n\n.fa-file-archive:before {\n  content: \"\\f1c6\"; }\n\n.fa-file-audio:before {\n  content: \"\\f1c7\"; }\n\n.fa-file-code:before {\n  content: \"\\f1c9\"; }\n\n.fa-file-contract:before {\n  content: \"\\f56c\"; }\n\n.fa-file-csv:before {\n  content: \"\\f6dd\"; }\n\n.fa-file-download:before {\n  content: \"\\f56d\"; }\n\n.fa-file-excel:before {\n  content: \"\\f1c3\"; }\n\n.fa-file-export:before {\n  content: \"\\f56e\"; }\n\n.fa-file-image:before {\n  content: \"\\f1c5\"; }\n\n.fa-file-import:before {\n  content: \"\\f56f\"; }\n\n.fa-file-invoice:before {\n  content: \"\\f570\"; }\n\n.fa-file-invoice-dollar:before {\n  content: \"\\f571\"; }\n\n.fa-file-medical:before {\n  content: \"\\f477\"; }\n\n.fa-file-medical-alt:before {\n  content: \"\\f478\"; }\n\n.fa-file-pdf:before {\n  content: \"\\f1c1\"; }\n\n.fa-file-powerpoint:before {\n  content: \"\\f1c4\"; }\n\n.fa-file-prescription:before {\n  content: \"\\f572\"; }\n\n.fa-file-signature:before {\n  content: \"\\f573\"; }\n\n.fa-file-upload:before {\n  content: \"\\f574\"; }\n\n.fa-file-video:before {\n  content: \"\\f1c8\"; }\n\n.fa-file-word:before {\n  content: \"\\f1c2\"; }\n\n.fa-fill:before {\n  content: \"\\f575\"; }\n\n.fa-fill-drip:before {\n  content: \"\\f576\"; }\n\n.fa-film:before {\n  content: \"\\f008\"; }\n\n.fa-filter:before {\n  content: \"\\f0b0\"; }\n\n.fa-fingerprint:before {\n  content: \"\\f577\"; }\n\n.fa-fire:before {\n  content: \"\\f06d\"; }\n\n.fa-fire-alt:before {\n  content: \"\\f7e4\"; }\n\n.fa-fire-extinguisher:before {\n  content: \"\\f134\"; }\n\n.fa-firefox:before {\n  content: \"\\f269\"; }\n\n.fa-firefox-browser:before {\n  content: \"\\e007\"; }\n\n.fa-first-aid:before {\n  content: \"\\f479\"; }\n\n.fa-first-order:before {\n  content: \"\\f2b0\"; }\n\n.fa-first-order-alt:before {\n  content: \"\\f50a\"; }\n\n.fa-firstdraft:before {\n  content: \"\\f3a1\"; }\n\n.fa-fish:before {\n  content: \"\\f578\"; }\n\n.fa-fist-raised:before {\n  content: \"\\f6de\"; }\n\n.fa-flag:before {\n  content: \"\\f024\"; }\n\n.fa-flag-checkered:before {\n  content: \"\\f11e\"; }\n\n.fa-flag-usa:before {\n  content: \"\\f74d\"; }\n\n.fa-flask:before {\n  content: \"\\f0c3\"; }\n\n.fa-flickr:before {\n  content: \"\\f16e\"; }\n\n.fa-flipboard:before {\n  content: \"\\f44d\"; }\n\n.fa-flushed:before {\n  content: \"\\f579\"; }\n\n.fa-fly:before {\n  content: \"\\f417\"; }\n\n.fa-folder:before {\n  content: \"\\f07b\"; }\n\n.fa-folder-minus:before {\n  content: \"\\f65d\"; }\n\n.fa-folder-open:before {\n  content: \"\\f07c\"; }\n\n.fa-folder-plus:before {\n  content: \"\\f65e\"; }\n\n.fa-font:before {\n  content: \"\\f031\"; }\n\n.fa-font-awesome:before {\n  content: \"\\f2b4\"; }\n\n.fa-font-awesome-alt:before {\n  content: \"\\f35c\"; }\n\n.fa-font-awesome-flag:before {\n  content: \"\\f425\"; }\n\n.fa-font-awesome-logo-full:before {\n  content: \"\\f4e6\"; }\n\n.fa-fonticons:before {\n  content: \"\\f280\"; }\n\n.fa-fonticons-fi:before {\n  content: \"\\f3a2\"; }\n\n.fa-football-ball:before {\n  content: \"\\f44e\"; }\n\n.fa-fort-awesome:before {\n  content: \"\\f286\"; }\n\n.fa-fort-awesome-alt:before {\n  content: \"\\f3a3\"; }\n\n.fa-forumbee:before {\n  content: \"\\f211\"; }\n\n.fa-forward:before {\n  content: \"\\f04e\"; }\n\n.fa-foursquare:before {\n  content: \"\\f180\"; }\n\n.fa-free-code-camp:before {\n  content: \"\\f2c5\"; }\n\n.fa-freebsd:before {\n  content: \"\\f3a4\"; }\n\n.fa-frog:before {\n  content: \"\\f52e\"; }\n\n.fa-frown:before {\n  content: \"\\f119\"; }\n\n.fa-frown-open:before {\n  content: \"\\f57a\"; }\n\n.fa-fulcrum:before {\n  content: \"\\f50b\"; }\n\n.fa-funnel-dollar:before {\n  content: \"\\f662\"; }\n\n.fa-futbol:before {\n  content: \"\\f1e3\"; }\n\n.fa-galactic-republic:before {\n  content: \"\\f50c\"; }\n\n.fa-galactic-senate:before {\n  content: \"\\f50d\"; }\n\n.fa-gamepad:before {\n  content: \"\\f11b\"; }\n\n.fa-gas-pump:before {\n  content: \"\\f52f\"; }\n\n.fa-gavel:before {\n  content: \"\\f0e3\"; }\n\n.fa-gem:before {\n  content: \"\\f3a5\"; }\n\n.fa-genderless:before {\n  content: \"\\f22d\"; }\n\n.fa-get-pocket:before {\n  content: \"\\f265\"; }\n\n.fa-gg:before {\n  content: \"\\f260\"; }\n\n.fa-gg-circle:before {\n  content: \"\\f261\"; }\n\n.fa-ghost:before {\n  content: \"\\f6e2\"; }\n\n.fa-gift:before {\n  content: \"\\f06b\"; }\n\n.fa-gifts:before {\n  content: \"\\f79c\"; }\n\n.fa-git:before {\n  content: \"\\f1d3\"; }\n\n.fa-git-alt:before {\n  content: \"\\f841\"; }\n\n.fa-git-square:before {\n  content: \"\\f1d2\"; }\n\n.fa-github:before {\n  content: \"\\f09b\"; }\n\n.fa-github-alt:before {\n  content: \"\\f113\"; }\n\n.fa-github-square:before {\n  content: \"\\f092\"; }\n\n.fa-gitkraken:before {\n  content: \"\\f3a6\"; }\n\n.fa-gitlab:before {\n  content: \"\\f296\"; }\n\n.fa-gitter:before {\n  content: \"\\f426\"; }\n\n.fa-glass-cheers:before {\n  content: \"\\f79f\"; }\n\n.fa-glass-martini:before {\n  content: \"\\f000\"; }\n\n.fa-glass-martini-alt:before {\n  content: \"\\f57b\"; }\n\n.fa-glass-whiskey:before {\n  content: \"\\f7a0\"; }\n\n.fa-glasses:before {\n  content: \"\\f530\"; }\n\n.fa-glide:before {\n  content: \"\\f2a5\"; }\n\n.fa-glide-g:before {\n  content: \"\\f2a6\"; }\n\n.fa-globe:before {\n  content: \"\\f0ac\"; }\n\n.fa-globe-africa:before {\n  content: \"\\f57c\"; }\n\n.fa-globe-americas:before {\n  content: \"\\f57d\"; }\n\n.fa-globe-asia:before {\n  content: \"\\f57e\"; }\n\n.fa-globe-europe:before {\n  content: \"\\f7a2\"; }\n\n.fa-gofore:before {\n  content: \"\\f3a7\"; }\n\n.fa-golf-ball:before {\n  content: \"\\f450\"; }\n\n.fa-goodreads:before {\n  content: \"\\f3a8\"; }\n\n.fa-goodreads-g:before {\n  content: \"\\f3a9\"; }\n\n.fa-google:before {\n  content: \"\\f1a0\"; }\n\n.fa-google-drive:before {\n  content: \"\\f3aa\"; }\n\n.fa-google-pay:before {\n  content: \"\\e079\"; }\n\n.fa-google-play:before {\n  content: \"\\f3ab\"; }\n\n.fa-google-plus:before {\n  content: \"\\f2b3\"; }\n\n.fa-google-plus-g:before {\n  content: \"\\f0d5\"; }\n\n.fa-google-plus-square:before {\n  content: \"\\f0d4\"; }\n\n.fa-google-wallet:before {\n  content: \"\\f1ee\"; }\n\n.fa-gopuram:before {\n  content: \"\\f664\"; }\n\n.fa-graduation-cap:before {\n  content: \"\\f19d\"; }\n\n.fa-gratipay:before {\n  content: \"\\f184\"; }\n\n.fa-grav:before {\n  content: \"\\f2d6\"; }\n\n.fa-greater-than:before {\n  content: \"\\f531\"; }\n\n.fa-greater-than-equal:before {\n  content: \"\\f532\"; }\n\n.fa-grimace:before {\n  content: \"\\f57f\"; }\n\n.fa-grin:before {\n  content: \"\\f580\"; }\n\n.fa-grin-alt:before {\n  content: \"\\f581\"; }\n\n.fa-grin-beam:before {\n  content: \"\\f582\"; }\n\n.fa-grin-beam-sweat:before {\n  content: \"\\f583\"; }\n\n.fa-grin-hearts:before {\n  content: \"\\f584\"; }\n\n.fa-grin-squint:before {\n  content: \"\\f585\"; }\n\n.fa-grin-squint-tears:before {\n  content: \"\\f586\"; }\n\n.fa-grin-stars:before {\n  content: \"\\f587\"; }\n\n.fa-grin-tears:before {\n  content: \"\\f588\"; }\n\n.fa-grin-tongue:before {\n  content: \"\\f589\"; }\n\n.fa-grin-tongue-squint:before {\n  content: \"\\f58a\"; }\n\n.fa-grin-tongue-wink:before {\n  content: \"\\f58b\"; }\n\n.fa-grin-wink:before {\n  content: \"\\f58c\"; }\n\n.fa-grip-horizontal:before {\n  content: \"\\f58d\"; }\n\n.fa-grip-lines:before {\n  content: \"\\f7a4\"; }\n\n.fa-grip-lines-vertical:before {\n  content: \"\\f7a5\"; }\n\n.fa-grip-vertical:before {\n  content: \"\\f58e\"; }\n\n.fa-gripfire:before {\n  content: \"\\f3ac\"; }\n\n.fa-grunt:before {\n  content: \"\\f3ad\"; }\n\n.fa-guilded:before {\n  content: \"\\e07e\"; }\n\n.fa-guitar:before {\n  content: \"\\f7a6\"; }\n\n.fa-gulp:before {\n  content: \"\\f3ae\"; }\n\n.fa-h-square:before {\n  content: \"\\f0fd\"; }\n\n.fa-hacker-news:before {\n  content: \"\\f1d4\"; }\n\n.fa-hacker-news-square:before {\n  content: \"\\f3af\"; }\n\n.fa-hackerrank:before {\n  content: \"\\f5f7\"; }\n\n.fa-hamburger:before {\n  content: \"\\f805\"; }\n\n.fa-hammer:before {\n  content: \"\\f6e3\"; }\n\n.fa-hamsa:before {\n  content: \"\\f665\"; }\n\n.fa-hand-holding:before {\n  content: \"\\f4bd\"; }\n\n.fa-hand-holding-heart:before {\n  content: \"\\f4be\"; }\n\n.fa-hand-holding-medical:before {\n  content: \"\\e05c\"; }\n\n.fa-hand-holding-usd:before {\n  content: \"\\f4c0\"; }\n\n.fa-hand-holding-water:before {\n  content: \"\\f4c1\"; }\n\n.fa-hand-lizard:before {\n  content: \"\\f258\"; }\n\n.fa-hand-middle-finger:before {\n  content: \"\\f806\"; }\n\n.fa-hand-paper:before {\n  content: \"\\f256\"; }\n\n.fa-hand-peace:before {\n  content: \"\\f25b\"; }\n\n.fa-hand-point-down:before {\n  content: \"\\f0a7\"; }\n\n.fa-hand-point-left:before {\n  content: \"\\f0a5\"; }\n\n.fa-hand-point-right:before {\n  content: \"\\f0a4\"; }\n\n.fa-hand-point-up:before {\n  content: \"\\f0a6\"; }\n\n.fa-hand-pointer:before {\n  content: \"\\f25a\"; }\n\n.fa-hand-rock:before {\n  content: \"\\f255\"; }\n\n.fa-hand-scissors:before {\n  content: \"\\f257\"; }\n\n.fa-hand-sparkles:before {\n  content: \"\\e05d\"; }\n\n.fa-hand-spock:before {\n  content: \"\\f259\"; }\n\n.fa-hands:before {\n  content: \"\\f4c2\"; }\n\n.fa-hands-helping:before {\n  content: \"\\f4c4\"; }\n\n.fa-hands-wash:before {\n  content: \"\\e05e\"; }\n\n.fa-handshake:before {\n  content: \"\\f2b5\"; }\n\n.fa-handshake-alt-slash:before {\n  content: \"\\e05f\"; }\n\n.fa-handshake-slash:before {\n  content: \"\\e060\"; }\n\n.fa-hanukiah:before {\n  content: \"\\f6e6\"; }\n\n.fa-hard-hat:before {\n  content: \"\\f807\"; }\n\n.fa-hashtag:before {\n  content: \"\\f292\"; }\n\n.fa-hat-cowboy:before {\n  content: \"\\f8c0\"; }\n\n.fa-hat-cowboy-side:before {\n  content: \"\\f8c1\"; }\n\n.fa-hat-wizard:before {\n  content: \"\\f6e8\"; }\n\n.fa-hdd:before {\n  content: \"\\f0a0\"; }\n\n.fa-head-side-cough:before {\n  content: \"\\e061\"; }\n\n.fa-head-side-cough-slash:before {\n  content: \"\\e062\"; }\n\n.fa-head-side-mask:before {\n  content: \"\\e063\"; }\n\n.fa-head-side-virus:before {\n  content: \"\\e064\"; }\n\n.fa-heading:before {\n  content: \"\\f1dc\"; }\n\n.fa-headphones:before {\n  content: \"\\f025\"; }\n\n.fa-headphones-alt:before {\n  content: \"\\f58f\"; }\n\n.fa-headset:before {\n  content: \"\\f590\"; }\n\n.fa-heart:before {\n  content: \"\\f004\"; }\n\n.fa-heart-broken:before {\n  content: \"\\f7a9\"; }\n\n.fa-heartbeat:before {\n  content: \"\\f21e\"; }\n\n.fa-helicopter:before {\n  content: \"\\f533\"; }\n\n.fa-highlighter:before {\n  content: \"\\f591\"; }\n\n.fa-hiking:before {\n  content: \"\\f6ec\"; }\n\n.fa-hippo:before {\n  content: \"\\f6ed\"; }\n\n.fa-hips:before {\n  content: \"\\f452\"; }\n\n.fa-hire-a-helper:before {\n  content: \"\\f3b0\"; }\n\n.fa-history:before {\n  content: \"\\f1da\"; }\n\n.fa-hive:before {\n  content: \"\\e07f\"; }\n\n.fa-hockey-puck:before {\n  content: \"\\f453\"; }\n\n.fa-holly-berry:before {\n  content: \"\\f7aa\"; }\n\n.fa-home:before {\n  content: \"\\f015\"; }\n\n.fa-hooli:before {\n  content: \"\\f427\"; }\n\n.fa-hornbill:before {\n  content: \"\\f592\"; }\n\n.fa-horse:before {\n  content: \"\\f6f0\"; }\n\n.fa-horse-head:before {\n  content: \"\\f7ab\"; }\n\n.fa-hospital:before {\n  content: \"\\f0f8\"; }\n\n.fa-hospital-alt:before {\n  content: \"\\f47d\"; }\n\n.fa-hospital-symbol:before {\n  content: \"\\f47e\"; }\n\n.fa-hospital-user:before {\n  content: \"\\f80d\"; }\n\n.fa-hot-tub:before {\n  content: \"\\f593\"; }\n\n.fa-hotdog:before {\n  content: \"\\f80f\"; }\n\n.fa-hotel:before {\n  content: \"\\f594\"; }\n\n.fa-hotjar:before {\n  content: \"\\f3b1\"; }\n\n.fa-hourglass:before {\n  content: \"\\f254\"; }\n\n.fa-hourglass-end:before {\n  content: \"\\f253\"; }\n\n.fa-hourglass-half:before {\n  content: \"\\f252\"; }\n\n.fa-hourglass-start:before {\n  content: \"\\f251\"; }\n\n.fa-house-damage:before {\n  content: \"\\f6f1\"; }\n\n.fa-house-user:before {\n  content: \"\\e065\"; }\n\n.fa-houzz:before {\n  content: \"\\f27c\"; }\n\n.fa-hryvnia:before {\n  content: \"\\f6f2\"; }\n\n.fa-html5:before {\n  content: \"\\f13b\"; }\n\n.fa-hubspot:before {\n  content: \"\\f3b2\"; }\n\n.fa-i-cursor:before {\n  content: \"\\f246\"; }\n\n.fa-ice-cream:before {\n  content: \"\\f810\"; }\n\n.fa-icicles:before {\n  content: \"\\f7ad\"; }\n\n.fa-icons:before {\n  content: \"\\f86d\"; }\n\n.fa-id-badge:before {\n  content: \"\\f2c1\"; }\n\n.fa-id-card:before {\n  content: \"\\f2c2\"; }\n\n.fa-id-card-alt:before {\n  content: \"\\f47f\"; }\n\n.fa-ideal:before {\n  content: \"\\e013\"; }\n\n.fa-igloo:before {\n  content: \"\\f7ae\"; }\n\n.fa-image:before {\n  content: \"\\f03e\"; }\n\n.fa-images:before {\n  content: \"\\f302\"; }\n\n.fa-imdb:before {\n  content: \"\\f2d8\"; }\n\n.fa-inbox:before {\n  content: \"\\f01c\"; }\n\n.fa-indent:before {\n  content: \"\\f03c\"; }\n\n.fa-industry:before {\n  content: \"\\f275\"; }\n\n.fa-infinity:before {\n  content: \"\\f534\"; }\n\n.fa-info:before {\n  content: \"\\f129\"; }\n\n.fa-info-circle:before {\n  content: \"\\f05a\"; }\n\n.fa-innosoft:before {\n  content: \"\\e080\"; }\n\n.fa-instagram:before {\n  content: \"\\f16d\"; }\n\n.fa-instagram-square:before {\n  content: \"\\e055\"; }\n\n.fa-instalod:before {\n  content: \"\\e081\"; }\n\n.fa-intercom:before {\n  content: \"\\f7af\"; }\n\n.fa-internet-explorer:before {\n  content: \"\\f26b\"; }\n\n.fa-invision:before {\n  content: \"\\f7b0\"; }\n\n.fa-ioxhost:before {\n  content: \"\\f208\"; }\n\n.fa-italic:before {\n  content: \"\\f033\"; }\n\n.fa-itch-io:before {\n  content: \"\\f83a\"; }\n\n.fa-itunes:before {\n  content: \"\\f3b4\"; }\n\n.fa-itunes-note:before {\n  content: \"\\f3b5\"; }\n\n.fa-java:before {\n  content: \"\\f4e4\"; }\n\n.fa-jedi:before {\n  content: \"\\f669\"; }\n\n.fa-jedi-order:before {\n  content: \"\\f50e\"; }\n\n.fa-jenkins:before {\n  content: \"\\f3b6\"; }\n\n.fa-jira:before {\n  content: \"\\f7b1\"; }\n\n.fa-joget:before {\n  content: \"\\f3b7\"; }\n\n.fa-joint:before {\n  content: \"\\f595\"; }\n\n.fa-joomla:before {\n  content: \"\\f1aa\"; }\n\n.fa-journal-whills:before {\n  content: \"\\f66a\"; }\n\n.fa-js:before {\n  content: \"\\f3b8\"; }\n\n.fa-js-square:before {\n  content: \"\\f3b9\"; }\n\n.fa-jsfiddle:before {\n  content: \"\\f1cc\"; }\n\n.fa-kaaba:before {\n  content: \"\\f66b\"; }\n\n.fa-kaggle:before {\n  content: \"\\f5fa\"; }\n\n.fa-key:before {\n  content: \"\\f084\"; }\n\n.fa-keybase:before {\n  content: \"\\f4f5\"; }\n\n.fa-keyboard:before {\n  content: \"\\f11c\"; }\n\n.fa-keycdn:before {\n  content: \"\\f3ba\"; }\n\n.fa-khanda:before {\n  content: \"\\f66d\"; }\n\n.fa-kickstarter:before {\n  content: \"\\f3bb\"; }\n\n.fa-kickstarter-k:before {\n  content: \"\\f3bc\"; }\n\n.fa-kiss:before {\n  content: \"\\f596\"; }\n\n.fa-kiss-beam:before {\n  content: \"\\f597\"; }\n\n.fa-kiss-wink-heart:before {\n  content: \"\\f598\"; }\n\n.fa-kiwi-bird:before {\n  content: \"\\f535\"; }\n\n.fa-korvue:before {\n  content: \"\\f42f\"; }\n\n.fa-landmark:before {\n  content: \"\\f66f\"; }\n\n.fa-language:before {\n  content: \"\\f1ab\"; }\n\n.fa-laptop:before {\n  content: \"\\f109\"; }\n\n.fa-laptop-code:before {\n  content: \"\\f5fc\"; }\n\n.fa-laptop-house:before {\n  content: \"\\e066\"; }\n\n.fa-laptop-medical:before {\n  content: \"\\f812\"; }\n\n.fa-laravel:before {\n  content: \"\\f3bd\"; }\n\n.fa-lastfm:before {\n  content: \"\\f202\"; }\n\n.fa-lastfm-square:before {\n  content: \"\\f203\"; }\n\n.fa-laugh:before {\n  content: \"\\f599\"; }\n\n.fa-laugh-beam:before {\n  content: \"\\f59a\"; }\n\n.fa-laugh-squint:before {\n  content: \"\\f59b\"; }\n\n.fa-laugh-wink:before {\n  content: \"\\f59c\"; }\n\n.fa-layer-group:before {\n  content: \"\\f5fd\"; }\n\n.fa-leaf:before {\n  content: \"\\f06c\"; }\n\n.fa-leanpub:before {\n  content: \"\\f212\"; }\n\n.fa-lemon:before {\n  content: \"\\f094\"; }\n\n.fa-less:before {\n  content: \"\\f41d\"; }\n\n.fa-less-than:before {\n  content: \"\\f536\"; }\n\n.fa-less-than-equal:before {\n  content: \"\\f537\"; }\n\n.fa-level-down-alt:before {\n  content: \"\\f3be\"; }\n\n.fa-level-up-alt:before {\n  content: \"\\f3bf\"; }\n\n.fa-life-ring:before {\n  content: \"\\f1cd\"; }\n\n.fa-lightbulb:before {\n  content: \"\\f0eb\"; }\n\n.fa-line:before {\n  content: \"\\f3c0\"; }\n\n.fa-link:before {\n  content: \"\\f0c1\"; }\n\n.fa-linkedin:before {\n  content: \"\\f08c\"; }\n\n.fa-linkedin-in:before {\n  content: \"\\f0e1\"; }\n\n.fa-linode:before {\n  content: \"\\f2b8\"; }\n\n.fa-linux:before {\n  content: \"\\f17c\"; }\n\n.fa-lira-sign:before {\n  content: \"\\f195\"; }\n\n.fa-list:before {\n  content: \"\\f03a\"; }\n\n.fa-list-alt:before {\n  content: \"\\f022\"; }\n\n.fa-list-ol:before {\n  content: \"\\f0cb\"; }\n\n.fa-list-ul:before {\n  content: \"\\f0ca\"; }\n\n.fa-location-arrow:before {\n  content: \"\\f124\"; }\n\n.fa-lock:before {\n  content: \"\\f023\"; }\n\n.fa-lock-open:before {\n  content: \"\\f3c1\"; }\n\n.fa-long-arrow-alt-down:before {\n  content: \"\\f309\"; }\n\n.fa-long-arrow-alt-left:before {\n  content: \"\\f30a\"; }\n\n.fa-long-arrow-alt-right:before {\n  content: \"\\f30b\"; }\n\n.fa-long-arrow-alt-up:before {\n  content: \"\\f30c\"; }\n\n.fa-low-vision:before {\n  content: \"\\f2a8\"; }\n\n.fa-luggage-cart:before {\n  content: \"\\f59d\"; }\n\n.fa-lungs:before {\n  content: \"\\f604\"; }\n\n.fa-lungs-virus:before {\n  content: \"\\e067\"; }\n\n.fa-lyft:before {\n  content: \"\\f3c3\"; }\n\n.fa-magento:before {\n  content: \"\\f3c4\"; }\n\n.fa-magic:before {\n  content: \"\\f0d0\"; }\n\n.fa-magnet:before {\n  content: \"\\f076\"; }\n\n.fa-mail-bulk:before {\n  content: \"\\f674\"; }\n\n.fa-mailchimp:before {\n  content: \"\\f59e\"; }\n\n.fa-male:before {\n  content: \"\\f183\"; }\n\n.fa-mandalorian:before {\n  content: \"\\f50f\"; }\n\n.fa-map:before {\n  content: \"\\f279\"; }\n\n.fa-map-marked:before {\n  content: \"\\f59f\"; }\n\n.fa-map-marked-alt:before {\n  content: \"\\f5a0\"; }\n\n.fa-map-marker:before {\n  content: \"\\f041\"; }\n\n.fa-map-marker-alt:before {\n  content: \"\\f3c5\"; }\n\n.fa-map-pin:before {\n  content: \"\\f276\"; }\n\n.fa-map-signs:before {\n  content: \"\\f277\"; }\n\n.fa-markdown:before {\n  content: \"\\f60f\"; }\n\n.fa-marker:before {\n  content: \"\\f5a1\"; }\n\n.fa-mars:before {\n  content: \"\\f222\"; }\n\n.fa-mars-double:before {\n  content: \"\\f227\"; }\n\n.fa-mars-stroke:before {\n  content: \"\\f229\"; }\n\n.fa-mars-stroke-h:before {\n  content: \"\\f22b\"; }\n\n.fa-mars-stroke-v:before {\n  content: \"\\f22a\"; }\n\n.fa-mask:before {\n  content: \"\\f6fa\"; }\n\n.fa-mastodon:before {\n  content: \"\\f4f6\"; }\n\n.fa-maxcdn:before {\n  content: \"\\f136\"; }\n\n.fa-mdb:before {\n  content: \"\\f8ca\"; }\n\n.fa-medal:before {\n  content: \"\\f5a2\"; }\n\n.fa-medapps:before {\n  content: \"\\f3c6\"; }\n\n.fa-medium:before {\n  content: \"\\f23a\"; }\n\n.fa-medium-m:before {\n  content: \"\\f3c7\"; }\n\n.fa-medkit:before {\n  content: \"\\f0fa\"; }\n\n.fa-medrt:before {\n  content: \"\\f3c8\"; }\n\n.fa-meetup:before {\n  content: \"\\f2e0\"; }\n\n.fa-megaport:before {\n  content: \"\\f5a3\"; }\n\n.fa-meh:before {\n  content: \"\\f11a\"; }\n\n.fa-meh-blank:before {\n  content: \"\\f5a4\"; }\n\n.fa-meh-rolling-eyes:before {\n  content: \"\\f5a5\"; }\n\n.fa-memory:before {\n  content: \"\\f538\"; }\n\n.fa-mendeley:before {\n  content: \"\\f7b3\"; }\n\n.fa-menorah:before {\n  content: \"\\f676\"; }\n\n.fa-mercury:before {\n  content: \"\\f223\"; }\n\n.fa-meteor:before {\n  content: \"\\f753\"; }\n\n.fa-microblog:before {\n  content: \"\\e01a\"; }\n\n.fa-microchip:before {\n  content: \"\\f2db\"; }\n\n.fa-microphone:before {\n  content: \"\\f130\"; }\n\n.fa-microphone-alt:before {\n  content: \"\\f3c9\"; }\n\n.fa-microphone-alt-slash:before {\n  content: \"\\f539\"; }\n\n.fa-microphone-slash:before {\n  content: \"\\f131\"; }\n\n.fa-microscope:before {\n  content: \"\\f610\"; }\n\n.fa-microsoft:before {\n  content: \"\\f3ca\"; }\n\n.fa-minus:before {\n  content: \"\\f068\"; }\n\n.fa-minus-circle:before {\n  content: \"\\f056\"; }\n\n.fa-minus-square:before {\n  content: \"\\f146\"; }\n\n.fa-mitten:before {\n  content: \"\\f7b5\"; }\n\n.fa-mix:before {\n  content: \"\\f3cb\"; }\n\n.fa-mixcloud:before {\n  content: \"\\f289\"; }\n\n.fa-mixer:before {\n  content: \"\\e056\"; }\n\n.fa-mizuni:before {\n  content: \"\\f3cc\"; }\n\n.fa-mobile:before {\n  content: \"\\f10b\"; }\n\n.fa-mobile-alt:before {\n  content: \"\\f3cd\"; }\n\n.fa-modx:before {\n  content: \"\\f285\"; }\n\n.fa-monero:before {\n  content: \"\\f3d0\"; }\n\n.fa-money-bill:before {\n  content: \"\\f0d6\"; }\n\n.fa-money-bill-alt:before {\n  content: \"\\f3d1\"; }\n\n.fa-money-bill-wave:before {\n  content: \"\\f53a\"; }\n\n.fa-money-bill-wave-alt:before {\n  content: \"\\f53b\"; }\n\n.fa-money-check:before {\n  content: \"\\f53c\"; }\n\n.fa-money-check-alt:before {\n  content: \"\\f53d\"; }\n\n.fa-monument:before {\n  content: \"\\f5a6\"; }\n\n.fa-moon:before {\n  content: \"\\f186\"; }\n\n.fa-mortar-pestle:before {\n  content: \"\\f5a7\"; }\n\n.fa-mosque:before {\n  content: \"\\f678\"; }\n\n.fa-motorcycle:before {\n  content: \"\\f21c\"; }\n\n.fa-mountain:before {\n  content: \"\\f6fc\"; }\n\n.fa-mouse:before {\n  content: \"\\f8cc\"; }\n\n.fa-mouse-pointer:before {\n  content: \"\\f245\"; }\n\n.fa-mug-hot:before {\n  content: \"\\f7b6\"; }\n\n.fa-music:before {\n  content: \"\\f001\"; }\n\n.fa-napster:before {\n  content: \"\\f3d2\"; }\n\n.fa-neos:before {\n  content: \"\\f612\"; }\n\n.fa-network-wired:before {\n  content: \"\\f6ff\"; }\n\n.fa-neuter:before {\n  content: \"\\f22c\"; }\n\n.fa-newspaper:before {\n  content: \"\\f1ea\"; }\n\n.fa-nimblr:before {\n  content: \"\\f5a8\"; }\n\n.fa-node:before {\n  content: \"\\f419\"; }\n\n.fa-node-js:before {\n  content: \"\\f3d3\"; }\n\n.fa-not-equal:before {\n  content: \"\\f53e\"; }\n\n.fa-notes-medical:before {\n  content: \"\\f481\"; }\n\n.fa-npm:before {\n  content: \"\\f3d4\"; }\n\n.fa-ns8:before {\n  content: \"\\f3d5\"; }\n\n.fa-nutritionix:before {\n  content: \"\\f3d6\"; }\n\n.fa-object-group:before {\n  content: \"\\f247\"; }\n\n.fa-object-ungroup:before {\n  content: \"\\f248\"; }\n\n.fa-octopus-deploy:before {\n  content: \"\\e082\"; }\n\n.fa-odnoklassniki:before {\n  content: \"\\f263\"; }\n\n.fa-odnoklassniki-square:before {\n  content: \"\\f264\"; }\n\n.fa-oil-can:before {\n  content: \"\\f613\"; }\n\n.fa-old-republic:before {\n  content: \"\\f510\"; }\n\n.fa-om:before {\n  content: \"\\f679\"; }\n\n.fa-opencart:before {\n  content: \"\\f23d\"; }\n\n.fa-openid:before {\n  content: \"\\f19b\"; }\n\n.fa-opera:before {\n  content: \"\\f26a\"; }\n\n.fa-optin-monster:before {\n  content: \"\\f23c\"; }\n\n.fa-orcid:before {\n  content: \"\\f8d2\"; }\n\n.fa-osi:before {\n  content: \"\\f41a\"; }\n\n.fa-otter:before {\n  content: \"\\f700\"; }\n\n.fa-outdent:before {\n  content: \"\\f03b\"; }\n\n.fa-page4:before {\n  content: \"\\f3d7\"; }\n\n.fa-pagelines:before {\n  content: \"\\f18c\"; }\n\n.fa-pager:before {\n  content: \"\\f815\"; }\n\n.fa-paint-brush:before {\n  content: \"\\f1fc\"; }\n\n.fa-paint-roller:before {\n  content: \"\\f5aa\"; }\n\n.fa-palette:before {\n  content: \"\\f53f\"; }\n\n.fa-palfed:before {\n  content: \"\\f3d8\"; }\n\n.fa-pallet:before {\n  content: \"\\f482\"; }\n\n.fa-paper-plane:before {\n  content: \"\\f1d8\"; }\n\n.fa-paperclip:before {\n  content: \"\\f0c6\"; }\n\n.fa-parachute-box:before {\n  content: \"\\f4cd\"; }\n\n.fa-paragraph:before {\n  content: \"\\f1dd\"; }\n\n.fa-parking:before {\n  content: \"\\f540\"; }\n\n.fa-passport:before {\n  content: \"\\f5ab\"; }\n\n.fa-pastafarianism:before {\n  content: \"\\f67b\"; }\n\n.fa-paste:before {\n  content: \"\\f0ea\"; }\n\n.fa-patreon:before {\n  content: \"\\f3d9\"; }\n\n.fa-pause:before {\n  content: \"\\f04c\"; }\n\n.fa-pause-circle:before {\n  content: \"\\f28b\"; }\n\n.fa-paw:before {\n  content: \"\\f1b0\"; }\n\n.fa-paypal:before {\n  content: \"\\f1ed\"; }\n\n.fa-peace:before {\n  content: \"\\f67c\"; }\n\n.fa-pen:before {\n  content: \"\\f304\"; }\n\n.fa-pen-alt:before {\n  content: \"\\f305\"; }\n\n.fa-pen-fancy:before {\n  content: \"\\f5ac\"; }\n\n.fa-pen-nib:before {\n  content: \"\\f5ad\"; }\n\n.fa-pen-square:before {\n  content: \"\\f14b\"; }\n\n.fa-pencil-alt:before {\n  content: \"\\f303\"; }\n\n.fa-pencil-ruler:before {\n  content: \"\\f5ae\"; }\n\n.fa-penny-arcade:before {\n  content: \"\\f704\"; }\n\n.fa-people-arrows:before {\n  content: \"\\e068\"; }\n\n.fa-people-carry:before {\n  content: \"\\f4ce\"; }\n\n.fa-pepper-hot:before {\n  content: \"\\f816\"; }\n\n.fa-perbyte:before {\n  content: \"\\e083\"; }\n\n.fa-percent:before {\n  content: \"\\f295\"; }\n\n.fa-percentage:before {\n  content: \"\\f541\"; }\n\n.fa-periscope:before {\n  content: \"\\f3da\"; }\n\n.fa-person-booth:before {\n  content: \"\\f756\"; }\n\n.fa-phabricator:before {\n  content: \"\\f3db\"; }\n\n.fa-phoenix-framework:before {\n  content: \"\\f3dc\"; }\n\n.fa-phoenix-squadron:before {\n  content: \"\\f511\"; }\n\n.fa-phone:before {\n  content: \"\\f095\"; }\n\n.fa-phone-alt:before {\n  content: \"\\f879\"; }\n\n.fa-phone-slash:before {\n  content: \"\\f3dd\"; }\n\n.fa-phone-square:before {\n  content: \"\\f098\"; }\n\n.fa-phone-square-alt:before {\n  content: \"\\f87b\"; }\n\n.fa-phone-volume:before {\n  content: \"\\f2a0\"; }\n\n.fa-photo-video:before {\n  content: \"\\f87c\"; }\n\n.fa-php:before {\n  content: \"\\f457\"; }\n\n.fa-pied-piper:before {\n  content: \"\\f2ae\"; }\n\n.fa-pied-piper-alt:before {\n  content: \"\\f1a8\"; }\n\n.fa-pied-piper-hat:before {\n  content: \"\\f4e5\"; }\n\n.fa-pied-piper-pp:before {\n  content: \"\\f1a7\"; }\n\n.fa-pied-piper-square:before {\n  content: \"\\e01e\"; }\n\n.fa-piggy-bank:before {\n  content: \"\\f4d3\"; }\n\n.fa-pills:before {\n  content: \"\\f484\"; }\n\n.fa-pinterest:before {\n  content: \"\\f0d2\"; }\n\n.fa-pinterest-p:before {\n  content: \"\\f231\"; }\n\n.fa-pinterest-square:before {\n  content: \"\\f0d3\"; }\n\n.fa-pizza-slice:before {\n  content: \"\\f818\"; }\n\n.fa-place-of-worship:before {\n  content: \"\\f67f\"; }\n\n.fa-plane:before {\n  content: \"\\f072\"; }\n\n.fa-plane-arrival:before {\n  content: \"\\f5af\"; }\n\n.fa-plane-departure:before {\n  content: \"\\f5b0\"; }\n\n.fa-plane-slash:before {\n  content: \"\\e069\"; }\n\n.fa-play:before {\n  content: \"\\f04b\"; }\n\n.fa-play-circle:before {\n  content: \"\\f144\"; }\n\n.fa-playstation:before {\n  content: \"\\f3df\"; }\n\n.fa-plug:before {\n  content: \"\\f1e6\"; }\n\n.fa-plus:before {\n  content: \"\\f067\"; }\n\n.fa-plus-circle:before {\n  content: \"\\f055\"; }\n\n.fa-plus-square:before {\n  content: \"\\f0fe\"; }\n\n.fa-podcast:before {\n  content: \"\\f2ce\"; }\n\n.fa-poll:before {\n  content: \"\\f681\"; }\n\n.fa-poll-h:before {\n  content: \"\\f682\"; }\n\n.fa-poo:before {\n  content: \"\\f2fe\"; }\n\n.fa-poo-storm:before {\n  content: \"\\f75a\"; }\n\n.fa-poop:before {\n  content: \"\\f619\"; }\n\n.fa-portrait:before {\n  content: \"\\f3e0\"; }\n\n.fa-pound-sign:before {\n  content: \"\\f154\"; }\n\n.fa-power-off:before {\n  content: \"\\f011\"; }\n\n.fa-pray:before {\n  content: \"\\f683\"; }\n\n.fa-praying-hands:before {\n  content: \"\\f684\"; }\n\n.fa-prescription:before {\n  content: \"\\f5b1\"; }\n\n.fa-prescription-bottle:before {\n  content: \"\\f485\"; }\n\n.fa-prescription-bottle-alt:before {\n  content: \"\\f486\"; }\n\n.fa-print:before {\n  content: \"\\f02f\"; }\n\n.fa-procedures:before {\n  content: \"\\f487\"; }\n\n.fa-product-hunt:before {\n  content: \"\\f288\"; }\n\n.fa-project-diagram:before {\n  content: \"\\f542\"; }\n\n.fa-pump-medical:before {\n  content: \"\\e06a\"; }\n\n.fa-pump-soap:before {\n  content: \"\\e06b\"; }\n\n.fa-pushed:before {\n  content: \"\\f3e1\"; }\n\n.fa-puzzle-piece:before {\n  content: \"\\f12e\"; }\n\n.fa-python:before {\n  content: \"\\f3e2\"; }\n\n.fa-qq:before {\n  content: \"\\f1d6\"; }\n\n.fa-qrcode:before {\n  content: \"\\f029\"; }\n\n.fa-question:before {\n  content: \"\\f128\"; }\n\n.fa-question-circle:before {\n  content: \"\\f059\"; }\n\n.fa-quidditch:before {\n  content: \"\\f458\"; }\n\n.fa-quinscape:before {\n  content: \"\\f459\"; }\n\n.fa-quora:before {\n  content: \"\\f2c4\"; }\n\n.fa-quote-left:before {\n  content: \"\\f10d\"; }\n\n.fa-quote-right:before {\n  content: \"\\f10e\"; }\n\n.fa-quran:before {\n  content: \"\\f687\"; }\n\n.fa-r-project:before {\n  content: \"\\f4f7\"; }\n\n.fa-radiation:before {\n  content: \"\\f7b9\"; }\n\n.fa-radiation-alt:before {\n  content: \"\\f7ba\"; }\n\n.fa-rainbow:before {\n  content: \"\\f75b\"; }\n\n.fa-random:before {\n  content: \"\\f074\"; }\n\n.fa-raspberry-pi:before {\n  content: \"\\f7bb\"; }\n\n.fa-ravelry:before {\n  content: \"\\f2d9\"; }\n\n.fa-react:before {\n  content: \"\\f41b\"; }\n\n.fa-reacteurope:before {\n  content: \"\\f75d\"; }\n\n.fa-readme:before {\n  content: \"\\f4d5\"; }\n\n.fa-rebel:before {\n  content: \"\\f1d0\"; }\n\n.fa-receipt:before {\n  content: \"\\f543\"; }\n\n.fa-record-vinyl:before {\n  content: \"\\f8d9\"; }\n\n.fa-recycle:before {\n  content: \"\\f1b8\"; }\n\n.fa-red-river:before {\n  content: \"\\f3e3\"; }\n\n.fa-reddit:before {\n  content: \"\\f1a1\"; }\n\n.fa-reddit-alien:before {\n  content: \"\\f281\"; }\n\n.fa-reddit-square:before {\n  content: \"\\f1a2\"; }\n\n.fa-redhat:before {\n  content: \"\\f7bc\"; }\n\n.fa-redo:before {\n  content: \"\\f01e\"; }\n\n.fa-redo-alt:before {\n  content: \"\\f2f9\"; }\n\n.fa-registered:before {\n  content: \"\\f25d\"; }\n\n.fa-remove-format:before {\n  content: \"\\f87d\"; }\n\n.fa-renren:before {\n  content: \"\\f18b\"; }\n\n.fa-reply:before {\n  content: \"\\f3e5\"; }\n\n.fa-reply-all:before {\n  content: \"\\f122\"; }\n\n.fa-replyd:before {\n  content: \"\\f3e6\"; }\n\n.fa-republican:before {\n  content: \"\\f75e\"; }\n\n.fa-researchgate:before {\n  content: \"\\f4f8\"; }\n\n.fa-resolving:before {\n  content: \"\\f3e7\"; }\n\n.fa-restroom:before {\n  content: \"\\f7bd\"; }\n\n.fa-retweet:before {\n  content: \"\\f079\"; }\n\n.fa-rev:before {\n  content: \"\\f5b2\"; }\n\n.fa-ribbon:before {\n  content: \"\\f4d6\"; }\n\n.fa-ring:before {\n  content: \"\\f70b\"; }\n\n.fa-road:before {\n  content: \"\\f018\"; }\n\n.fa-robot:before {\n  content: \"\\f544\"; }\n\n.fa-rocket:before {\n  content: \"\\f135\"; }\n\n.fa-rocketchat:before {\n  content: \"\\f3e8\"; }\n\n.fa-rockrms:before {\n  content: \"\\f3e9\"; }\n\n.fa-route:before {\n  content: \"\\f4d7\"; }\n\n.fa-rss:before {\n  content: \"\\f09e\"; }\n\n.fa-rss-square:before {\n  content: \"\\f143\"; }\n\n.fa-ruble-sign:before {\n  content: \"\\f158\"; }\n\n.fa-ruler:before {\n  content: \"\\f545\"; }\n\n.fa-ruler-combined:before {\n  content: \"\\f546\"; }\n\n.fa-ruler-horizontal:before {\n  content: \"\\f547\"; }\n\n.fa-ruler-vertical:before {\n  content: \"\\f548\"; }\n\n.fa-running:before {\n  content: \"\\f70c\"; }\n\n.fa-rupee-sign:before {\n  content: \"\\f156\"; }\n\n.fa-rust:before {\n  content: \"\\e07a\"; }\n\n.fa-sad-cry:before {\n  content: \"\\f5b3\"; }\n\n.fa-sad-tear:before {\n  content: \"\\f5b4\"; }\n\n.fa-safari:before {\n  content: \"\\f267\"; }\n\n.fa-salesforce:before {\n  content: \"\\f83b\"; }\n\n.fa-sass:before {\n  content: \"\\f41e\"; }\n\n.fa-satellite:before {\n  content: \"\\f7bf\"; }\n\n.fa-satellite-dish:before {\n  content: \"\\f7c0\"; }\n\n.fa-save:before {\n  content: \"\\f0c7\"; }\n\n.fa-schlix:before {\n  content: \"\\f3ea\"; }\n\n.fa-school:before {\n  content: \"\\f549\"; }\n\n.fa-screwdriver:before {\n  content: \"\\f54a\"; }\n\n.fa-scribd:before {\n  content: \"\\f28a\"; }\n\n.fa-scroll:before {\n  content: \"\\f70e\"; }\n\n.fa-sd-card:before {\n  content: \"\\f7c2\"; }\n\n.fa-search:before {\n  content: \"\\f002\"; }\n\n.fa-search-dollar:before {\n  content: \"\\f688\"; }\n\n.fa-search-location:before {\n  content: \"\\f689\"; }\n\n.fa-search-minus:before {\n  content: \"\\f010\"; }\n\n.fa-search-plus:before {\n  content: \"\\f00e\"; }\n\n.fa-searchengin:before {\n  content: \"\\f3eb\"; }\n\n.fa-seedling:before {\n  content: \"\\f4d8\"; }\n\n.fa-sellcast:before {\n  content: \"\\f2da\"; }\n\n.fa-sellsy:before {\n  content: \"\\f213\"; }\n\n.fa-server:before {\n  content: \"\\f233\"; }\n\n.fa-servicestack:before {\n  content: \"\\f3ec\"; }\n\n.fa-shapes:before {\n  content: \"\\f61f\"; }\n\n.fa-share:before {\n  content: \"\\f064\"; }\n\n.fa-share-alt:before {\n  content: \"\\f1e0\"; }\n\n.fa-share-alt-square:before {\n  content: \"\\f1e1\"; }\n\n.fa-share-square:before {\n  content: \"\\f14d\"; }\n\n.fa-shekel-sign:before {\n  content: \"\\f20b\"; }\n\n.fa-shield-alt:before {\n  content: \"\\f3ed\"; }\n\n.fa-shield-virus:before {\n  content: \"\\e06c\"; }\n\n.fa-ship:before {\n  content: \"\\f21a\"; }\n\n.fa-shipping-fast:before {\n  content: \"\\f48b\"; }\n\n.fa-shirtsinbulk:before {\n  content: \"\\f214\"; }\n\n.fa-shoe-prints:before {\n  content: \"\\f54b\"; }\n\n.fa-shopify:before {\n  content: \"\\e057\"; }\n\n.fa-shopping-bag:before {\n  content: \"\\f290\"; }\n\n.fa-shopping-basket:before {\n  content: \"\\f291\"; }\n\n.fa-shopping-cart:before {\n  content: \"\\f07a\"; }\n\n.fa-shopware:before {\n  content: \"\\f5b5\"; }\n\n.fa-shower:before {\n  content: \"\\f2cc\"; }\n\n.fa-shuttle-van:before {\n  content: \"\\f5b6\"; }\n\n.fa-sign:before {\n  content: \"\\f4d9\"; }\n\n.fa-sign-in-alt:before {\n  content: \"\\f2f6\"; }\n\n.fa-sign-language:before {\n  content: \"\\f2a7\"; }\n\n.fa-sign-out-alt:before {\n  content: \"\\f2f5\"; }\n\n.fa-signal:before {\n  content: \"\\f012\"; }\n\n.fa-signature:before {\n  content: \"\\f5b7\"; }\n\n.fa-sim-card:before {\n  content: \"\\f7c4\"; }\n\n.fa-simplybuilt:before {\n  content: \"\\f215\"; }\n\n.fa-sink:before {\n  content: \"\\e06d\"; }\n\n.fa-sistrix:before {\n  content: \"\\f3ee\"; }\n\n.fa-sitemap:before {\n  content: \"\\f0e8\"; }\n\n.fa-sith:before {\n  content: \"\\f512\"; }\n\n.fa-skating:before {\n  content: \"\\f7c5\"; }\n\n.fa-sketch:before {\n  content: \"\\f7c6\"; }\n\n.fa-skiing:before {\n  content: \"\\f7c9\"; }\n\n.fa-skiing-nordic:before {\n  content: \"\\f7ca\"; }\n\n.fa-skull:before {\n  content: \"\\f54c\"; }\n\n.fa-skull-crossbones:before {\n  content: \"\\f714\"; }\n\n.fa-skyatlas:before {\n  content: \"\\f216\"; }\n\n.fa-skype:before {\n  content: \"\\f17e\"; }\n\n.fa-slack:before {\n  content: \"\\f198\"; }\n\n.fa-slack-hash:before {\n  content: \"\\f3ef\"; }\n\n.fa-slash:before {\n  content: \"\\f715\"; }\n\n.fa-sleigh:before {\n  content: \"\\f7cc\"; }\n\n.fa-sliders-h:before {\n  content: \"\\f1de\"; }\n\n.fa-slideshare:before {\n  content: \"\\f1e7\"; }\n\n.fa-smile:before {\n  content: \"\\f118\"; }\n\n.fa-smile-beam:before {\n  content: \"\\f5b8\"; }\n\n.fa-smile-wink:before {\n  content: \"\\f4da\"; }\n\n.fa-smog:before {\n  content: \"\\f75f\"; }\n\n.fa-smoking:before {\n  content: \"\\f48d\"; }\n\n.fa-smoking-ban:before {\n  content: \"\\f54d\"; }\n\n.fa-sms:before {\n  content: \"\\f7cd\"; }\n\n.fa-snapchat:before {\n  content: \"\\f2ab\"; }\n\n.fa-snapchat-ghost:before {\n  content: \"\\f2ac\"; }\n\n.fa-snapchat-square:before {\n  content: \"\\f2ad\"; }\n\n.fa-snowboarding:before {\n  content: \"\\f7ce\"; }\n\n.fa-snowflake:before {\n  content: \"\\f2dc\"; }\n\n.fa-snowman:before {\n  content: \"\\f7d0\"; }\n\n.fa-snowplow:before {\n  content: \"\\f7d2\"; }\n\n.fa-soap:before {\n  content: \"\\e06e\"; }\n\n.fa-socks:before {\n  content: \"\\f696\"; }\n\n.fa-solar-panel:before {\n  content: \"\\f5ba\"; }\n\n.fa-sort:before {\n  content: \"\\f0dc\"; }\n\n.fa-sort-alpha-down:before {\n  content: \"\\f15d\"; }\n\n.fa-sort-alpha-down-alt:before {\n  content: \"\\f881\"; }\n\n.fa-sort-alpha-up:before {\n  content: \"\\f15e\"; }\n\n.fa-sort-alpha-up-alt:before {\n  content: \"\\f882\"; }\n\n.fa-sort-amount-down:before {\n  content: \"\\f160\"; }\n\n.fa-sort-amount-down-alt:before {\n  content: \"\\f884\"; }\n\n.fa-sort-amount-up:before {\n  content: \"\\f161\"; }\n\n.fa-sort-amount-up-alt:before {\n  content: \"\\f885\"; }\n\n.fa-sort-down:before {\n  content: \"\\f0dd\"; }\n\n.fa-sort-numeric-down:before {\n  content: \"\\f162\"; }\n\n.fa-sort-numeric-down-alt:before {\n  content: \"\\f886\"; }\n\n.fa-sort-numeric-up:before {\n  content: \"\\f163\"; }\n\n.fa-sort-numeric-up-alt:before {\n  content: \"\\f887\"; }\n\n.fa-sort-up:before {\n  content: \"\\f0de\"; }\n\n.fa-soundcloud:before {\n  content: \"\\f1be\"; }\n\n.fa-sourcetree:before {\n  content: \"\\f7d3\"; }\n\n.fa-spa:before {\n  content: \"\\f5bb\"; }\n\n.fa-space-shuttle:before {\n  content: \"\\f197\"; }\n\n.fa-speakap:before {\n  content: \"\\f3f3\"; }\n\n.fa-speaker-deck:before {\n  content: \"\\f83c\"; }\n\n.fa-spell-check:before {\n  content: \"\\f891\"; }\n\n.fa-spider:before {\n  content: \"\\f717\"; }\n\n.fa-spinner:before {\n  content: \"\\f110\"; }\n\n.fa-splotch:before {\n  content: \"\\f5bc\"; }\n\n.fa-spotify:before {\n  content: \"\\f1bc\"; }\n\n.fa-spray-can:before {\n  content: \"\\f5bd\"; }\n\n.fa-square:before {\n  content: \"\\f0c8\"; }\n\n.fa-square-full:before {\n  content: \"\\f45c\"; }\n\n.fa-square-root-alt:before {\n  content: \"\\f698\"; }\n\n.fa-squarespace:before {\n  content: \"\\f5be\"; }\n\n.fa-stack-exchange:before {\n  content: \"\\f18d\"; }\n\n.fa-stack-overflow:before {\n  content: \"\\f16c\"; }\n\n.fa-stackpath:before {\n  content: \"\\f842\"; }\n\n.fa-stamp:before {\n  content: \"\\f5bf\"; }\n\n.fa-star:before {\n  content: \"\\f005\"; }\n\n.fa-star-and-crescent:before {\n  content: \"\\f699\"; }\n\n.fa-star-half:before {\n  content: \"\\f089\"; }\n\n.fa-star-half-alt:before {\n  content: \"\\f5c0\"; }\n\n.fa-star-of-david:before {\n  content: \"\\f69a\"; }\n\n.fa-star-of-life:before {\n  content: \"\\f621\"; }\n\n.fa-staylinked:before {\n  content: \"\\f3f5\"; }\n\n.fa-steam:before {\n  content: \"\\f1b6\"; }\n\n.fa-steam-square:before {\n  content: \"\\f1b7\"; }\n\n.fa-steam-symbol:before {\n  content: \"\\f3f6\"; }\n\n.fa-step-backward:before {\n  content: \"\\f048\"; }\n\n.fa-step-forward:before {\n  content: \"\\f051\"; }\n\n.fa-stethoscope:before {\n  content: \"\\f0f1\"; }\n\n.fa-sticker-mule:before {\n  content: \"\\f3f7\"; }\n\n.fa-sticky-note:before {\n  content: \"\\f249\"; }\n\n.fa-stop:before {\n  content: \"\\f04d\"; }\n\n.fa-stop-circle:before {\n  content: \"\\f28d\"; }\n\n.fa-stopwatch:before {\n  content: \"\\f2f2\"; }\n\n.fa-stopwatch-20:before {\n  content: \"\\e06f\"; }\n\n.fa-store:before {\n  content: \"\\f54e\"; }\n\n.fa-store-alt:before {\n  content: \"\\f54f\"; }\n\n.fa-store-alt-slash:before {\n  content: \"\\e070\"; }\n\n.fa-store-slash:before {\n  content: \"\\e071\"; }\n\n.fa-strava:before {\n  content: \"\\f428\"; }\n\n.fa-stream:before {\n  content: \"\\f550\"; }\n\n.fa-street-view:before {\n  content: \"\\f21d\"; }\n\n.fa-strikethrough:before {\n  content: \"\\f0cc\"; }\n\n.fa-stripe:before {\n  content: \"\\f429\"; }\n\n.fa-stripe-s:before {\n  content: \"\\f42a\"; }\n\n.fa-stroopwafel:before {\n  content: \"\\f551\"; }\n\n.fa-studiovinari:before {\n  content: \"\\f3f8\"; }\n\n.fa-stumbleupon:before {\n  content: \"\\f1a4\"; }\n\n.fa-stumbleupon-circle:before {\n  content: \"\\f1a3\"; }\n\n.fa-subscript:before {\n  content: \"\\f12c\"; }\n\n.fa-subway:before {\n  content: \"\\f239\"; }\n\n.fa-suitcase:before {\n  content: \"\\f0f2\"; }\n\n.fa-suitcase-rolling:before {\n  content: \"\\f5c1\"; }\n\n.fa-sun:before {\n  content: \"\\f185\"; }\n\n.fa-superpowers:before {\n  content: \"\\f2dd\"; }\n\n.fa-superscript:before {\n  content: \"\\f12b\"; }\n\n.fa-supple:before {\n  content: \"\\f3f9\"; }\n\n.fa-surprise:before {\n  content: \"\\f5c2\"; }\n\n.fa-suse:before {\n  content: \"\\f7d6\"; }\n\n.fa-swatchbook:before {\n  content: \"\\f5c3\"; }\n\n.fa-swift:before {\n  content: \"\\f8e1\"; }\n\n.fa-swimmer:before {\n  content: \"\\f5c4\"; }\n\n.fa-swimming-pool:before {\n  content: \"\\f5c5\"; }\n\n.fa-symfony:before {\n  content: \"\\f83d\"; }\n\n.fa-synagogue:before {\n  content: \"\\f69b\"; }\n\n.fa-sync:before {\n  content: \"\\f021\"; }\n\n.fa-sync-alt:before {\n  content: \"\\f2f1\"; }\n\n.fa-syringe:before {\n  content: \"\\f48e\"; }\n\n.fa-table:before {\n  content: \"\\f0ce\"; }\n\n.fa-table-tennis:before {\n  content: \"\\f45d\"; }\n\n.fa-tablet:before {\n  content: \"\\f10a\"; }\n\n.fa-tablet-alt:before {\n  content: \"\\f3fa\"; }\n\n.fa-tablets:before {\n  content: \"\\f490\"; }\n\n.fa-tachometer-alt:before {\n  content: \"\\f3fd\"; }\n\n.fa-tag:before {\n  content: \"\\f02b\"; }\n\n.fa-tags:before {\n  content: \"\\f02c\"; }\n\n.fa-tape:before {\n  content: \"\\f4db\"; }\n\n.fa-tasks:before {\n  content: \"\\f0ae\"; }\n\n.fa-taxi:before {\n  content: \"\\f1ba\"; }\n\n.fa-teamspeak:before {\n  content: \"\\f4f9\"; }\n\n.fa-teeth:before {\n  content: \"\\f62e\"; }\n\n.fa-teeth-open:before {\n  content: \"\\f62f\"; }\n\n.fa-telegram:before {\n  content: \"\\f2c6\"; }\n\n.fa-telegram-plane:before {\n  content: \"\\f3fe\"; }\n\n.fa-temperature-high:before {\n  content: \"\\f769\"; }\n\n.fa-temperature-low:before {\n  content: \"\\f76b\"; }\n\n.fa-tencent-weibo:before {\n  content: \"\\f1d5\"; }\n\n.fa-tenge:before {\n  content: \"\\f7d7\"; }\n\n.fa-terminal:before {\n  content: \"\\f120\"; }\n\n.fa-text-height:before {\n  content: \"\\f034\"; }\n\n.fa-text-width:before {\n  content: \"\\f035\"; }\n\n.fa-th:before {\n  content: \"\\f00a\"; }\n\n.fa-th-large:before {\n  content: \"\\f009\"; }\n\n.fa-th-list:before {\n  content: \"\\f00b\"; }\n\n.fa-the-red-yeti:before {\n  content: \"\\f69d\"; }\n\n.fa-theater-masks:before {\n  content: \"\\f630\"; }\n\n.fa-themeco:before {\n  content: \"\\f5c6\"; }\n\n.fa-themeisle:before {\n  content: \"\\f2b2\"; }\n\n.fa-thermometer:before {\n  content: \"\\f491\"; }\n\n.fa-thermometer-empty:before {\n  content: \"\\f2cb\"; }\n\n.fa-thermometer-full:before {\n  content: \"\\f2c7\"; }\n\n.fa-thermometer-half:before {\n  content: \"\\f2c9\"; }\n\n.fa-thermometer-quarter:before {\n  content: \"\\f2ca\"; }\n\n.fa-thermometer-three-quarters:before {\n  content: \"\\f2c8\"; }\n\n.fa-think-peaks:before {\n  content: \"\\f731\"; }\n\n.fa-thumbs-down:before {\n  content: \"\\f165\"; }\n\n.fa-thumbs-up:before {\n  content: \"\\f164\"; }\n\n.fa-thumbtack:before {\n  content: \"\\f08d\"; }\n\n.fa-ticket-alt:before {\n  content: \"\\f3ff\"; }\n\n.fa-tiktok:before {\n  content: \"\\e07b\"; }\n\n.fa-times:before {\n  content: \"\\f00d\"; }\n\n.fa-times-circle:before {\n  content: \"\\f057\"; }\n\n.fa-tint:before {\n  content: \"\\f043\"; }\n\n.fa-tint-slash:before {\n  content: \"\\f5c7\"; }\n\n.fa-tired:before {\n  content: \"\\f5c8\"; }\n\n.fa-toggle-off:before {\n  content: \"\\f204\"; }\n\n.fa-toggle-on:before {\n  content: \"\\f205\"; }\n\n.fa-toilet:before {\n  content: \"\\f7d8\"; }\n\n.fa-toilet-paper:before {\n  content: \"\\f71e\"; }\n\n.fa-toilet-paper-slash:before {\n  content: \"\\e072\"; }\n\n.fa-toolbox:before {\n  content: \"\\f552\"; }\n\n.fa-tools:before {\n  content: \"\\f7d9\"; }\n\n.fa-tooth:before {\n  content: \"\\f5c9\"; }\n\n.fa-torah:before {\n  content: \"\\f6a0\"; }\n\n.fa-torii-gate:before {\n  content: \"\\f6a1\"; }\n\n.fa-tractor:before {\n  content: \"\\f722\"; }\n\n.fa-trade-federation:before {\n  content: \"\\f513\"; }\n\n.fa-trademark:before {\n  content: \"\\f25c\"; }\n\n.fa-traffic-light:before {\n  content: \"\\f637\"; }\n\n.fa-trailer:before {\n  content: \"\\e041\"; }\n\n.fa-train:before {\n  content: \"\\f238\"; }\n\n.fa-tram:before {\n  content: \"\\f7da\"; }\n\n.fa-transgender:before {\n  content: \"\\f224\"; }\n\n.fa-transgender-alt:before {\n  content: \"\\f225\"; }\n\n.fa-trash:before {\n  content: \"\\f1f8\"; }\n\n.fa-trash-alt:before {\n  content: \"\\f2ed\"; }\n\n.fa-trash-restore:before {\n  content: \"\\f829\"; }\n\n.fa-trash-restore-alt:before {\n  content: \"\\f82a\"; }\n\n.fa-tree:before {\n  content: \"\\f1bb\"; }\n\n.fa-trello:before {\n  content: \"\\f181\"; }\n\n.fa-trophy:before {\n  content: \"\\f091\"; }\n\n.fa-truck:before {\n  content: \"\\f0d1\"; }\n\n.fa-truck-loading:before {\n  content: \"\\f4de\"; }\n\n.fa-truck-monster:before {\n  content: \"\\f63b\"; }\n\n.fa-truck-moving:before {\n  content: \"\\f4df\"; }\n\n.fa-truck-pickup:before {\n  content: \"\\f63c\"; }\n\n.fa-tshirt:before {\n  content: \"\\f553\"; }\n\n.fa-tty:before {\n  content: \"\\f1e4\"; }\n\n.fa-tumblr:before {\n  content: \"\\f173\"; }\n\n.fa-tumblr-square:before {\n  content: \"\\f174\"; }\n\n.fa-tv:before {\n  content: \"\\f26c\"; }\n\n.fa-twitch:before {\n  content: \"\\f1e8\"; }\n\n.fa-twitter:before {\n  content: \"\\f099\"; }\n\n.fa-twitter-square:before {\n  content: \"\\f081\"; }\n\n.fa-typo3:before {\n  content: \"\\f42b\"; }\n\n.fa-uber:before {\n  content: \"\\f402\"; }\n\n.fa-ubuntu:before {\n  content: \"\\f7df\"; }\n\n.fa-uikit:before {\n  content: \"\\f403\"; }\n\n.fa-umbraco:before {\n  content: \"\\f8e8\"; }\n\n.fa-umbrella:before {\n  content: \"\\f0e9\"; }\n\n.fa-umbrella-beach:before {\n  content: \"\\f5ca\"; }\n\n.fa-uncharted:before {\n  content: \"\\e084\"; }\n\n.fa-underline:before {\n  content: \"\\f0cd\"; }\n\n.fa-undo:before {\n  content: \"\\f0e2\"; }\n\n.fa-undo-alt:before {\n  content: \"\\f2ea\"; }\n\n.fa-uniregistry:before {\n  content: \"\\f404\"; }\n\n.fa-unity:before {\n  content: \"\\e049\"; }\n\n.fa-universal-access:before {\n  content: \"\\f29a\"; }\n\n.fa-university:before {\n  content: \"\\f19c\"; }\n\n.fa-unlink:before {\n  content: \"\\f127\"; }\n\n.fa-unlock:before {\n  content: \"\\f09c\"; }\n\n.fa-unlock-alt:before {\n  content: \"\\f13e\"; }\n\n.fa-unsplash:before {\n  content: \"\\e07c\"; }\n\n.fa-untappd:before {\n  content: \"\\f405\"; }\n\n.fa-upload:before {\n  content: \"\\f093\"; }\n\n.fa-ups:before {\n  content: \"\\f7e0\"; }\n\n.fa-usb:before {\n  content: \"\\f287\"; }\n\n.fa-user:before {\n  content: \"\\f007\"; }\n\n.fa-user-alt:before {\n  content: \"\\f406\"; }\n\n.fa-user-alt-slash:before {\n  content: \"\\f4fa\"; }\n\n.fa-user-astronaut:before {\n  content: \"\\f4fb\"; }\n\n.fa-user-check:before {\n  content: \"\\f4fc\"; }\n\n.fa-user-circle:before {\n  content: \"\\f2bd\"; }\n\n.fa-user-clock:before {\n  content: \"\\f4fd\"; }\n\n.fa-user-cog:before {\n  content: \"\\f4fe\"; }\n\n.fa-user-edit:before {\n  content: \"\\f4ff\"; }\n\n.fa-user-friends:before {\n  content: \"\\f500\"; }\n\n.fa-user-graduate:before {\n  content: \"\\f501\"; }\n\n.fa-user-injured:before {\n  content: \"\\f728\"; }\n\n.fa-user-lock:before {\n  content: \"\\f502\"; }\n\n.fa-user-md:before {\n  content: \"\\f0f0\"; }\n\n.fa-user-minus:before {\n  content: \"\\f503\"; }\n\n.fa-user-ninja:before {\n  content: \"\\f504\"; }\n\n.fa-user-nurse:before {\n  content: \"\\f82f\"; }\n\n.fa-user-plus:before {\n  content: \"\\f234\"; }\n\n.fa-user-secret:before {\n  content: \"\\f21b\"; }\n\n.fa-user-shield:before {\n  content: \"\\f505\"; }\n\n.fa-user-slash:before {\n  content: \"\\f506\"; }\n\n.fa-user-tag:before {\n  content: \"\\f507\"; }\n\n.fa-user-tie:before {\n  content: \"\\f508\"; }\n\n.fa-user-times:before {\n  content: \"\\f235\"; }\n\n.fa-users:before {\n  content: \"\\f0c0\"; }\n\n.fa-users-cog:before {\n  content: \"\\f509\"; }\n\n.fa-users-slash:before {\n  content: \"\\e073\"; }\n\n.fa-usps:before {\n  content: \"\\f7e1\"; }\n\n.fa-ussunnah:before {\n  content: \"\\f407\"; }\n\n.fa-utensil-spoon:before {\n  content: \"\\f2e5\"; }\n\n.fa-utensils:before {\n  content: \"\\f2e7\"; }\n\n.fa-vaadin:before {\n  content: \"\\f408\"; }\n\n.fa-vector-square:before {\n  content: \"\\f5cb\"; }\n\n.fa-venus:before {\n  content: \"\\f221\"; }\n\n.fa-venus-double:before {\n  content: \"\\f226\"; }\n\n.fa-venus-mars:before {\n  content: \"\\f228\"; }\n\n.fa-vest:before {\n  content: \"\\e085\"; }\n\n.fa-vest-patches:before {\n  content: \"\\e086\"; }\n\n.fa-viacoin:before {\n  content: \"\\f237\"; }\n\n.fa-viadeo:before {\n  content: \"\\f2a9\"; }\n\n.fa-viadeo-square:before {\n  content: \"\\f2aa\"; }\n\n.fa-vial:before {\n  content: \"\\f492\"; }\n\n.fa-vials:before {\n  content: \"\\f493\"; }\n\n.fa-viber:before {\n  content: \"\\f409\"; }\n\n.fa-video:before {\n  content: \"\\f03d\"; }\n\n.fa-video-slash:before {\n  content: \"\\f4e2\"; }\n\n.fa-vihara:before {\n  content: \"\\f6a7\"; }\n\n.fa-vimeo:before {\n  content: \"\\f40a\"; }\n\n.fa-vimeo-square:before {\n  content: \"\\f194\"; }\n\n.fa-vimeo-v:before {\n  content: \"\\f27d\"; }\n\n.fa-vine:before {\n  content: \"\\f1ca\"; }\n\n.fa-virus:before {\n  content: \"\\e074\"; }\n\n.fa-virus-slash:before {\n  content: \"\\e075\"; }\n\n.fa-viruses:before {\n  content: \"\\e076\"; }\n\n.fa-vk:before {\n  content: \"\\f189\"; }\n\n.fa-vnv:before {\n  content: \"\\f40b\"; }\n\n.fa-voicemail:before {\n  content: \"\\f897\"; }\n\n.fa-volleyball-ball:before {\n  content: \"\\f45f\"; }\n\n.fa-volume-down:before {\n  content: \"\\f027\"; }\n\n.fa-volume-mute:before {\n  content: \"\\f6a9\"; }\n\n.fa-volume-off:before {\n  content: \"\\f026\"; }\n\n.fa-volume-up:before {\n  content: \"\\f028\"; }\n\n.fa-vote-yea:before {\n  content: \"\\f772\"; }\n\n.fa-vr-cardboard:before {\n  content: \"\\f729\"; }\n\n.fa-vuejs:before {\n  content: \"\\f41f\"; }\n\n.fa-walking:before {\n  content: \"\\f554\"; }\n\n.fa-wallet:before {\n  content: \"\\f555\"; }\n\n.fa-warehouse:before {\n  content: \"\\f494\"; }\n\n.fa-watchman-monitoring:before {\n  content: \"\\e087\"; }\n\n.fa-water:before {\n  content: \"\\f773\"; }\n\n.fa-wave-square:before {\n  content: \"\\f83e\"; }\n\n.fa-waze:before {\n  content: \"\\f83f\"; }\n\n.fa-weebly:before {\n  content: \"\\f5cc\"; }\n\n.fa-weibo:before {\n  content: \"\\f18a\"; }\n\n.fa-weight:before {\n  content: \"\\f496\"; }\n\n.fa-weight-hanging:before {\n  content: \"\\f5cd\"; }\n\n.fa-weixin:before {\n  content: \"\\f1d7\"; }\n\n.fa-whatsapp:before {\n  content: \"\\f232\"; }\n\n.fa-whatsapp-square:before {\n  content: \"\\f40c\"; }\n\n.fa-wheelchair:before {\n  content: \"\\f193\"; }\n\n.fa-whmcs:before {\n  content: \"\\f40d\"; }\n\n.fa-wifi:before {\n  content: \"\\f1eb\"; }\n\n.fa-wikipedia-w:before {\n  content: \"\\f266\"; }\n\n.fa-wind:before {\n  content: \"\\f72e\"; }\n\n.fa-window-close:before {\n  content: \"\\f410\"; }\n\n.fa-window-maximize:before {\n  content: \"\\f2d0\"; }\n\n.fa-window-minimize:before {\n  content: \"\\f2d1\"; }\n\n.fa-window-restore:before {\n  content: \"\\f2d2\"; }\n\n.fa-windows:before {\n  content: \"\\f17a\"; }\n\n.fa-wine-bottle:before {\n  content: \"\\f72f\"; }\n\n.fa-wine-glass:before {\n  content: \"\\f4e3\"; }\n\n.fa-wine-glass-alt:before {\n  content: \"\\f5ce\"; }\n\n.fa-wix:before {\n  content: \"\\f5cf\"; }\n\n.fa-wizards-of-the-coast:before {\n  content: \"\\f730\"; }\n\n.fa-wodu:before {\n  content: \"\\e088\"; }\n\n.fa-wolf-pack-battalion:before {\n  content: \"\\f514\"; }\n\n.fa-won-sign:before {\n  content: \"\\f159\"; }\n\n.fa-wordpress:before {\n  content: \"\\f19a\"; }\n\n.fa-wordpress-simple:before {\n  content: \"\\f411\"; }\n\n.fa-wpbeginner:before {\n  content: \"\\f297\"; }\n\n.fa-wpexplorer:before {\n  content: \"\\f2de\"; }\n\n.fa-wpforms:before {\n  content: \"\\f298\"; }\n\n.fa-wpressr:before {\n  content: \"\\f3e4\"; }\n\n.fa-wrench:before {\n  content: \"\\f0ad\"; }\n\n.fa-x-ray:before {\n  content: \"\\f497\"; }\n\n.fa-xbox:before {\n  content: \"\\f412\"; }\n\n.fa-xing:before {\n  content: \"\\f168\"; }\n\n.fa-xing-square:before {\n  content: \"\\f169\"; }\n\n.fa-y-combinator:before {\n  content: \"\\f23b\"; }\n\n.fa-yahoo:before {\n  content: \"\\f19e\"; }\n\n.fa-yammer:before {\n  content: \"\\f840\"; }\n\n.fa-yandex:before {\n  content: \"\\f413\"; }\n\n.fa-yandex-international:before {\n  content: \"\\f414\"; }\n\n.fa-yarn:before {\n  content: \"\\f7e3\"; }\n\n.fa-yelp:before {\n  content: \"\\f1e9\"; }\n\n.fa-yen-sign:before {\n  content: \"\\f157\"; }\n\n.fa-yin-yang:before {\n  content: \"\\f6ad\"; }\n\n.fa-yoast:before {\n  content: \"\\f2b1\"; }\n\n.fa-youtube:before {\n  content: \"\\f167\"; }\n\n.fa-youtube-square:before {\n  content: \"\\f431\"; }\n\n.fa-zhihu:before {\n  content: \"\\f63f\"; }\n\n.sr-only {\n  border: 0;\n  clip: rect(0, 0, 0, 0);\n  height: 1px;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0;\n  position: absolute;\n  width: 1px; }\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n  clip: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  position: static;\n  width: auto; }\n@font-face {\n  font-family: 'Font Awesome 5 Brands';\n  font-style: normal;\n  font-weight: 400;\n  font-display: block;\n  src: url(\"../webfonts/fa-brands-400.eot\");\n  src: url(\"../webfonts/fa-brands-400.eot?#iefix\") format(\"embedded-opentype\"), url(\"../webfonts/fa-brands-400.woff2\") format(\"woff2\"), url(\"../webfonts/fa-brands-400.woff\") format(\"woff\"), url(\"../webfonts/fa-brands-400.ttf\") format(\"truetype\"), url(\"../webfonts/fa-brands-400.svg#fontawesome\") format(\"svg\"); }\n\n.fab {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n@font-face {\n  font-family: 'Font Awesome 5 Free';\n  font-style: normal;\n  font-weight: 400;\n  font-display: block;\n  src: url(\"../webfonts/fa-regular-400.eot\");\n  src: url(\"../webfonts/fa-regular-400.eot?#iefix\") format(\"embedded-opentype\"), url(\"../webfonts/fa-regular-400.woff2\") format(\"woff2\"), url(\"../webfonts/fa-regular-400.woff\") format(\"woff\"), url(\"../webfonts/fa-regular-400.ttf\") format(\"truetype\"), url(\"../webfonts/fa-regular-400.svg#fontawesome\") format(\"svg\"); }\n\n.far {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n@font-face {\n  font-family: 'Font Awesome 5 Free';\n  font-style: normal;\n  font-weight: 900;\n  font-display: block;\n  src: url(\"../webfonts/fa-solid-900.eot\");\n  src: url(\"../webfonts/fa-solid-900.eot?#iefix\") format(\"embedded-opentype\"), url(\"../webfonts/fa-solid-900.woff2\") format(\"woff2\"), url(\"../webfonts/fa-solid-900.woff\") format(\"woff\"), url(\"../webfonts/fa-solid-900.ttf\") format(\"truetype\"), url(\"../webfonts/fa-solid-900.svg#fontawesome\") format(\"svg\"); }\n\n.fa,\n.fas {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 900; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/fontawesome-free/css/brands.css",
    "content": "/*!\n * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com\n * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n */\n@font-face {\n  font-family: 'Font Awesome 5 Brands';\n  font-style: normal;\n  font-weight: 400;\n  font-display: block;\n  src: url(\"../webfonts/fa-brands-400.eot\");\n  src: url(\"../webfonts/fa-brands-400.eot?#iefix\") format(\"embedded-opentype\"), url(\"../webfonts/fa-brands-400.woff2\") format(\"woff2\"), url(\"../webfonts/fa-brands-400.woff\") format(\"woff\"), url(\"../webfonts/fa-brands-400.ttf\") format(\"truetype\"), url(\"../webfonts/fa-brands-400.svg#fontawesome\") format(\"svg\"); }\n\n.fab {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/fontawesome-free/css/fontawesome.css",
    "content": "/*!\n * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com\n * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n */\n.fa,\n.fas,\n.far,\n.fal,\n.fad,\n.fab {\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-font-smoothing: antialiased;\n  display: inline-block;\n  font-style: normal;\n  font-variant: normal;\n  text-rendering: auto;\n  line-height: 1; }\n\n.fa-lg {\n  font-size: 1.33333em;\n  line-height: 0.75em;\n  vertical-align: -.0667em; }\n\n.fa-xs {\n  font-size: .75em; }\n\n.fa-sm {\n  font-size: .875em; }\n\n.fa-1x {\n  font-size: 1em; }\n\n.fa-2x {\n  font-size: 2em; }\n\n.fa-3x {\n  font-size: 3em; }\n\n.fa-4x {\n  font-size: 4em; }\n\n.fa-5x {\n  font-size: 5em; }\n\n.fa-6x {\n  font-size: 6em; }\n\n.fa-7x {\n  font-size: 7em; }\n\n.fa-8x {\n  font-size: 8em; }\n\n.fa-9x {\n  font-size: 9em; }\n\n.fa-10x {\n  font-size: 10em; }\n\n.fa-fw {\n  text-align: center;\n  width: 1.25em; }\n\n.fa-ul {\n  list-style-type: none;\n  margin-left: 2.5em;\n  padding-left: 0; }\n  .fa-ul > li {\n    position: relative; }\n\n.fa-li {\n  left: -2em;\n  position: absolute;\n  text-align: center;\n  width: 2em;\n  line-height: inherit; }\n\n.fa-border {\n  border: solid 0.08em #eee;\n  border-radius: .1em;\n  padding: .2em .25em .15em; }\n\n.fa-pull-left {\n  float: left; }\n\n.fa-pull-right {\n  float: right; }\n\n.fa.fa-pull-left,\n.fas.fa-pull-left,\n.far.fa-pull-left,\n.fal.fa-pull-left,\n.fab.fa-pull-left {\n  margin-right: .3em; }\n\n.fa.fa-pull-right,\n.fas.fa-pull-right,\n.far.fa-pull-right,\n.fal.fa-pull-right,\n.fab.fa-pull-right {\n  margin-left: .3em; }\n\n.fa-spin {\n  -webkit-animation: fa-spin 2s infinite linear;\n          animation: fa-spin 2s infinite linear; }\n\n.fa-pulse {\n  -webkit-animation: fa-spin 1s infinite steps(8);\n          animation: fa-spin 1s infinite steps(8); }\n\n@-webkit-keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg); }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg); } }\n\n@keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg); }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg); } }\n\n.fa-rotate-90 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)\";\n  -webkit-transform: rotate(90deg);\n          transform: rotate(90deg); }\n\n.fa-rotate-180 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)\";\n  -webkit-transform: rotate(180deg);\n          transform: rotate(180deg); }\n\n.fa-rotate-270 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)\";\n  -webkit-transform: rotate(270deg);\n          transform: rotate(270deg); }\n\n.fa-flip-horizontal {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)\";\n  -webkit-transform: scale(-1, 1);\n          transform: scale(-1, 1); }\n\n.fa-flip-vertical {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n  -webkit-transform: scale(1, -1);\n          transform: scale(1, -1); }\n\n.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n  -webkit-transform: scale(-1, -1);\n          transform: scale(-1, -1); }\n\n:root .fa-rotate-90,\n:root .fa-rotate-180,\n:root .fa-rotate-270,\n:root .fa-flip-horizontal,\n:root .fa-flip-vertical,\n:root .fa-flip-both {\n  -webkit-filter: none;\n          filter: none; }\n\n.fa-stack {\n  display: inline-block;\n  height: 2em;\n  line-height: 2em;\n  position: relative;\n  vertical-align: middle;\n  width: 2.5em; }\n\n.fa-stack-1x,\n.fa-stack-2x {\n  left: 0;\n  position: absolute;\n  text-align: center;\n  width: 100%; }\n\n.fa-stack-1x {\n  line-height: inherit; }\n\n.fa-stack-2x {\n  font-size: 2em; }\n\n.fa-inverse {\n  color: #fff; }\n\n/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen\nreaders do not read off random characters that represent icons */\n.fa-500px:before {\n  content: \"\\f26e\"; }\n\n.fa-accessible-icon:before {\n  content: \"\\f368\"; }\n\n.fa-accusoft:before {\n  content: \"\\f369\"; }\n\n.fa-acquisitions-incorporated:before {\n  content: \"\\f6af\"; }\n\n.fa-ad:before {\n  content: \"\\f641\"; }\n\n.fa-address-book:before {\n  content: \"\\f2b9\"; }\n\n.fa-address-card:before {\n  content: \"\\f2bb\"; }\n\n.fa-adjust:before {\n  content: \"\\f042\"; }\n\n.fa-adn:before {\n  content: \"\\f170\"; }\n\n.fa-adversal:before {\n  content: \"\\f36a\"; }\n\n.fa-affiliatetheme:before {\n  content: \"\\f36b\"; }\n\n.fa-air-freshener:before {\n  content: \"\\f5d0\"; }\n\n.fa-airbnb:before {\n  content: \"\\f834\"; }\n\n.fa-algolia:before {\n  content: \"\\f36c\"; }\n\n.fa-align-center:before {\n  content: \"\\f037\"; }\n\n.fa-align-justify:before {\n  content: \"\\f039\"; }\n\n.fa-align-left:before {\n  content: \"\\f036\"; }\n\n.fa-align-right:before {\n  content: \"\\f038\"; }\n\n.fa-alipay:before {\n  content: \"\\f642\"; }\n\n.fa-allergies:before {\n  content: \"\\f461\"; }\n\n.fa-amazon:before {\n  content: \"\\f270\"; }\n\n.fa-amazon-pay:before {\n  content: \"\\f42c\"; }\n\n.fa-ambulance:before {\n  content: \"\\f0f9\"; }\n\n.fa-american-sign-language-interpreting:before {\n  content: \"\\f2a3\"; }\n\n.fa-amilia:before {\n  content: \"\\f36d\"; }\n\n.fa-anchor:before {\n  content: \"\\f13d\"; }\n\n.fa-android:before {\n  content: \"\\f17b\"; }\n\n.fa-angellist:before {\n  content: \"\\f209\"; }\n\n.fa-angle-double-down:before {\n  content: \"\\f103\"; }\n\n.fa-angle-double-left:before {\n  content: \"\\f100\"; }\n\n.fa-angle-double-right:before {\n  content: \"\\f101\"; }\n\n.fa-angle-double-up:before {\n  content: \"\\f102\"; }\n\n.fa-angle-down:before {\n  content: \"\\f107\"; }\n\n.fa-angle-left:before {\n  content: \"\\f104\"; }\n\n.fa-angle-right:before {\n  content: \"\\f105\"; }\n\n.fa-angle-up:before {\n  content: \"\\f106\"; }\n\n.fa-angry:before {\n  content: \"\\f556\"; }\n\n.fa-angrycreative:before {\n  content: \"\\f36e\"; }\n\n.fa-angular:before {\n  content: \"\\f420\"; }\n\n.fa-ankh:before {\n  content: \"\\f644\"; }\n\n.fa-app-store:before {\n  content: \"\\f36f\"; }\n\n.fa-app-store-ios:before {\n  content: \"\\f370\"; }\n\n.fa-apper:before {\n  content: \"\\f371\"; }\n\n.fa-apple:before {\n  content: \"\\f179\"; }\n\n.fa-apple-alt:before {\n  content: \"\\f5d1\"; }\n\n.fa-apple-pay:before {\n  content: \"\\f415\"; }\n\n.fa-archive:before {\n  content: \"\\f187\"; }\n\n.fa-archway:before {\n  content: \"\\f557\"; }\n\n.fa-arrow-alt-circle-down:before {\n  content: \"\\f358\"; }\n\n.fa-arrow-alt-circle-left:before {\n  content: \"\\f359\"; }\n\n.fa-arrow-alt-circle-right:before {\n  content: \"\\f35a\"; }\n\n.fa-arrow-alt-circle-up:before {\n  content: \"\\f35b\"; }\n\n.fa-arrow-circle-down:before {\n  content: \"\\f0ab\"; }\n\n.fa-arrow-circle-left:before {\n  content: \"\\f0a8\"; }\n\n.fa-arrow-circle-right:before {\n  content: \"\\f0a9\"; }\n\n.fa-arrow-circle-up:before {\n  content: \"\\f0aa\"; }\n\n.fa-arrow-down:before {\n  content: \"\\f063\"; }\n\n.fa-arrow-left:before {\n  content: \"\\f060\"; }\n\n.fa-arrow-right:before {\n  content: \"\\f061\"; }\n\n.fa-arrow-up:before {\n  content: \"\\f062\"; }\n\n.fa-arrows-alt:before {\n  content: \"\\f0b2\"; }\n\n.fa-arrows-alt-h:before {\n  content: \"\\f337\"; }\n\n.fa-arrows-alt-v:before {\n  content: \"\\f338\"; }\n\n.fa-artstation:before {\n  content: \"\\f77a\"; }\n\n.fa-assistive-listening-systems:before {\n  content: \"\\f2a2\"; }\n\n.fa-asterisk:before {\n  content: \"\\f069\"; }\n\n.fa-asymmetrik:before {\n  content: \"\\f372\"; }\n\n.fa-at:before {\n  content: \"\\f1fa\"; }\n\n.fa-atlas:before {\n  content: \"\\f558\"; }\n\n.fa-atlassian:before {\n  content: \"\\f77b\"; }\n\n.fa-atom:before {\n  content: \"\\f5d2\"; }\n\n.fa-audible:before {\n  content: \"\\f373\"; }\n\n.fa-audio-description:before {\n  content: \"\\f29e\"; }\n\n.fa-autoprefixer:before {\n  content: \"\\f41c\"; }\n\n.fa-avianex:before {\n  content: \"\\f374\"; }\n\n.fa-aviato:before {\n  content: \"\\f421\"; }\n\n.fa-award:before {\n  content: \"\\f559\"; }\n\n.fa-aws:before {\n  content: \"\\f375\"; }\n\n.fa-baby:before {\n  content: \"\\f77c\"; }\n\n.fa-baby-carriage:before {\n  content: \"\\f77d\"; }\n\n.fa-backspace:before {\n  content: \"\\f55a\"; }\n\n.fa-backward:before {\n  content: \"\\f04a\"; }\n\n.fa-bacon:before {\n  content: \"\\f7e5\"; }\n\n.fa-bacteria:before {\n  content: \"\\e059\"; }\n\n.fa-bacterium:before {\n  content: \"\\e05a\"; }\n\n.fa-bahai:before {\n  content: \"\\f666\"; }\n\n.fa-balance-scale:before {\n  content: \"\\f24e\"; }\n\n.fa-balance-scale-left:before {\n  content: \"\\f515\"; }\n\n.fa-balance-scale-right:before {\n  content: \"\\f516\"; }\n\n.fa-ban:before {\n  content: \"\\f05e\"; }\n\n.fa-band-aid:before {\n  content: \"\\f462\"; }\n\n.fa-bandcamp:before {\n  content: \"\\f2d5\"; }\n\n.fa-barcode:before {\n  content: \"\\f02a\"; }\n\n.fa-bars:before {\n  content: \"\\f0c9\"; }\n\n.fa-baseball-ball:before {\n  content: \"\\f433\"; }\n\n.fa-basketball-ball:before {\n  content: \"\\f434\"; }\n\n.fa-bath:before {\n  content: \"\\f2cd\"; }\n\n.fa-battery-empty:before {\n  content: \"\\f244\"; }\n\n.fa-battery-full:before {\n  content: \"\\f240\"; }\n\n.fa-battery-half:before {\n  content: \"\\f242\"; }\n\n.fa-battery-quarter:before {\n  content: \"\\f243\"; }\n\n.fa-battery-three-quarters:before {\n  content: \"\\f241\"; }\n\n.fa-battle-net:before {\n  content: \"\\f835\"; }\n\n.fa-bed:before {\n  content: \"\\f236\"; }\n\n.fa-beer:before {\n  content: \"\\f0fc\"; }\n\n.fa-behance:before {\n  content: \"\\f1b4\"; }\n\n.fa-behance-square:before {\n  content: \"\\f1b5\"; }\n\n.fa-bell:before {\n  content: \"\\f0f3\"; }\n\n.fa-bell-slash:before {\n  content: \"\\f1f6\"; }\n\n.fa-bezier-curve:before {\n  content: \"\\f55b\"; }\n\n.fa-bible:before {\n  content: \"\\f647\"; }\n\n.fa-bicycle:before {\n  content: \"\\f206\"; }\n\n.fa-biking:before {\n  content: \"\\f84a\"; }\n\n.fa-bimobject:before {\n  content: \"\\f378\"; }\n\n.fa-binoculars:before {\n  content: \"\\f1e5\"; }\n\n.fa-biohazard:before {\n  content: \"\\f780\"; }\n\n.fa-birthday-cake:before {\n  content: \"\\f1fd\"; }\n\n.fa-bitbucket:before {\n  content: \"\\f171\"; }\n\n.fa-bitcoin:before {\n  content: \"\\f379\"; }\n\n.fa-bity:before {\n  content: \"\\f37a\"; }\n\n.fa-black-tie:before {\n  content: \"\\f27e\"; }\n\n.fa-blackberry:before {\n  content: \"\\f37b\"; }\n\n.fa-blender:before {\n  content: \"\\f517\"; }\n\n.fa-blender-phone:before {\n  content: \"\\f6b6\"; }\n\n.fa-blind:before {\n  content: \"\\f29d\"; }\n\n.fa-blog:before {\n  content: \"\\f781\"; }\n\n.fa-blogger:before {\n  content: \"\\f37c\"; }\n\n.fa-blogger-b:before {\n  content: \"\\f37d\"; }\n\n.fa-bluetooth:before {\n  content: \"\\f293\"; }\n\n.fa-bluetooth-b:before {\n  content: \"\\f294\"; }\n\n.fa-bold:before {\n  content: \"\\f032\"; }\n\n.fa-bolt:before {\n  content: \"\\f0e7\"; }\n\n.fa-bomb:before {\n  content: \"\\f1e2\"; }\n\n.fa-bone:before {\n  content: \"\\f5d7\"; }\n\n.fa-bong:before {\n  content: \"\\f55c\"; }\n\n.fa-book:before {\n  content: \"\\f02d\"; }\n\n.fa-book-dead:before {\n  content: \"\\f6b7\"; }\n\n.fa-book-medical:before {\n  content: \"\\f7e6\"; }\n\n.fa-book-open:before {\n  content: \"\\f518\"; }\n\n.fa-book-reader:before {\n  content: \"\\f5da\"; }\n\n.fa-bookmark:before {\n  content: \"\\f02e\"; }\n\n.fa-bootstrap:before {\n  content: \"\\f836\"; }\n\n.fa-border-all:before {\n  content: \"\\f84c\"; }\n\n.fa-border-none:before {\n  content: \"\\f850\"; }\n\n.fa-border-style:before {\n  content: \"\\f853\"; }\n\n.fa-bowling-ball:before {\n  content: \"\\f436\"; }\n\n.fa-box:before {\n  content: \"\\f466\"; }\n\n.fa-box-open:before {\n  content: \"\\f49e\"; }\n\n.fa-box-tissue:before {\n  content: \"\\e05b\"; }\n\n.fa-boxes:before {\n  content: \"\\f468\"; }\n\n.fa-braille:before {\n  content: \"\\f2a1\"; }\n\n.fa-brain:before {\n  content: \"\\f5dc\"; }\n\n.fa-bread-slice:before {\n  content: \"\\f7ec\"; }\n\n.fa-briefcase:before {\n  content: \"\\f0b1\"; }\n\n.fa-briefcase-medical:before {\n  content: \"\\f469\"; }\n\n.fa-broadcast-tower:before {\n  content: \"\\f519\"; }\n\n.fa-broom:before {\n  content: \"\\f51a\"; }\n\n.fa-brush:before {\n  content: \"\\f55d\"; }\n\n.fa-btc:before {\n  content: \"\\f15a\"; }\n\n.fa-buffer:before {\n  content: \"\\f837\"; }\n\n.fa-bug:before {\n  content: \"\\f188\"; }\n\n.fa-building:before {\n  content: \"\\f1ad\"; }\n\n.fa-bullhorn:before {\n  content: \"\\f0a1\"; }\n\n.fa-bullseye:before {\n  content: \"\\f140\"; }\n\n.fa-burn:before {\n  content: \"\\f46a\"; }\n\n.fa-buromobelexperte:before {\n  content: \"\\f37f\"; }\n\n.fa-bus:before {\n  content: \"\\f207\"; }\n\n.fa-bus-alt:before {\n  content: \"\\f55e\"; }\n\n.fa-business-time:before {\n  content: \"\\f64a\"; }\n\n.fa-buy-n-large:before {\n  content: \"\\f8a6\"; }\n\n.fa-buysellads:before {\n  content: \"\\f20d\"; }\n\n.fa-calculator:before {\n  content: \"\\f1ec\"; }\n\n.fa-calendar:before {\n  content: \"\\f133\"; }\n\n.fa-calendar-alt:before {\n  content: \"\\f073\"; }\n\n.fa-calendar-check:before {\n  content: \"\\f274\"; }\n\n.fa-calendar-day:before {\n  content: \"\\f783\"; }\n\n.fa-calendar-minus:before {\n  content: \"\\f272\"; }\n\n.fa-calendar-plus:before {\n  content: \"\\f271\"; }\n\n.fa-calendar-times:before {\n  content: \"\\f273\"; }\n\n.fa-calendar-week:before {\n  content: \"\\f784\"; }\n\n.fa-camera:before {\n  content: \"\\f030\"; }\n\n.fa-camera-retro:before {\n  content: \"\\f083\"; }\n\n.fa-campground:before {\n  content: \"\\f6bb\"; }\n\n.fa-canadian-maple-leaf:before {\n  content: \"\\f785\"; }\n\n.fa-candy-cane:before {\n  content: \"\\f786\"; }\n\n.fa-cannabis:before {\n  content: \"\\f55f\"; }\n\n.fa-capsules:before {\n  content: \"\\f46b\"; }\n\n.fa-car:before {\n  content: \"\\f1b9\"; }\n\n.fa-car-alt:before {\n  content: \"\\f5de\"; }\n\n.fa-car-battery:before {\n  content: \"\\f5df\"; }\n\n.fa-car-crash:before {\n  content: \"\\f5e1\"; }\n\n.fa-car-side:before {\n  content: \"\\f5e4\"; }\n\n.fa-caravan:before {\n  content: \"\\f8ff\"; }\n\n.fa-caret-down:before {\n  content: \"\\f0d7\"; }\n\n.fa-caret-left:before {\n  content: \"\\f0d9\"; }\n\n.fa-caret-right:before {\n  content: \"\\f0da\"; }\n\n.fa-caret-square-down:before {\n  content: \"\\f150\"; }\n\n.fa-caret-square-left:before {\n  content: \"\\f191\"; }\n\n.fa-caret-square-right:before {\n  content: \"\\f152\"; }\n\n.fa-caret-square-up:before {\n  content: \"\\f151\"; }\n\n.fa-caret-up:before {\n  content: \"\\f0d8\"; }\n\n.fa-carrot:before {\n  content: \"\\f787\"; }\n\n.fa-cart-arrow-down:before {\n  content: \"\\f218\"; }\n\n.fa-cart-plus:before {\n  content: \"\\f217\"; }\n\n.fa-cash-register:before {\n  content: \"\\f788\"; }\n\n.fa-cat:before {\n  content: \"\\f6be\"; }\n\n.fa-cc-amazon-pay:before {\n  content: \"\\f42d\"; }\n\n.fa-cc-amex:before {\n  content: \"\\f1f3\"; }\n\n.fa-cc-apple-pay:before {\n  content: \"\\f416\"; }\n\n.fa-cc-diners-club:before {\n  content: \"\\f24c\"; }\n\n.fa-cc-discover:before {\n  content: \"\\f1f2\"; }\n\n.fa-cc-jcb:before {\n  content: \"\\f24b\"; }\n\n.fa-cc-mastercard:before {\n  content: \"\\f1f1\"; }\n\n.fa-cc-paypal:before {\n  content: \"\\f1f4\"; }\n\n.fa-cc-stripe:before {\n  content: \"\\f1f5\"; }\n\n.fa-cc-visa:before {\n  content: \"\\f1f0\"; }\n\n.fa-centercode:before {\n  content: \"\\f380\"; }\n\n.fa-centos:before {\n  content: \"\\f789\"; }\n\n.fa-certificate:before {\n  content: \"\\f0a3\"; }\n\n.fa-chair:before {\n  content: \"\\f6c0\"; }\n\n.fa-chalkboard:before {\n  content: \"\\f51b\"; }\n\n.fa-chalkboard-teacher:before {\n  content: \"\\f51c\"; }\n\n.fa-charging-station:before {\n  content: \"\\f5e7\"; }\n\n.fa-chart-area:before {\n  content: \"\\f1fe\"; }\n\n.fa-chart-bar:before {\n  content: \"\\f080\"; }\n\n.fa-chart-line:before {\n  content: \"\\f201\"; }\n\n.fa-chart-pie:before {\n  content: \"\\f200\"; }\n\n.fa-check:before {\n  content: \"\\f00c\"; }\n\n.fa-check-circle:before {\n  content: \"\\f058\"; }\n\n.fa-check-double:before {\n  content: \"\\f560\"; }\n\n.fa-check-square:before {\n  content: \"\\f14a\"; }\n\n.fa-cheese:before {\n  content: \"\\f7ef\"; }\n\n.fa-chess:before {\n  content: \"\\f439\"; }\n\n.fa-chess-bishop:before {\n  content: \"\\f43a\"; }\n\n.fa-chess-board:before {\n  content: \"\\f43c\"; }\n\n.fa-chess-king:before {\n  content: \"\\f43f\"; }\n\n.fa-chess-knight:before {\n  content: \"\\f441\"; }\n\n.fa-chess-pawn:before {\n  content: \"\\f443\"; }\n\n.fa-chess-queen:before {\n  content: \"\\f445\"; }\n\n.fa-chess-rook:before {\n  content: \"\\f447\"; }\n\n.fa-chevron-circle-down:before {\n  content: \"\\f13a\"; }\n\n.fa-chevron-circle-left:before {\n  content: \"\\f137\"; }\n\n.fa-chevron-circle-right:before {\n  content: \"\\f138\"; }\n\n.fa-chevron-circle-up:before {\n  content: \"\\f139\"; }\n\n.fa-chevron-down:before {\n  content: \"\\f078\"; }\n\n.fa-chevron-left:before {\n  content: \"\\f053\"; }\n\n.fa-chevron-right:before {\n  content: \"\\f054\"; }\n\n.fa-chevron-up:before {\n  content: \"\\f077\"; }\n\n.fa-child:before {\n  content: \"\\f1ae\"; }\n\n.fa-chrome:before {\n  content: \"\\f268\"; }\n\n.fa-chromecast:before {\n  content: \"\\f838\"; }\n\n.fa-church:before {\n  content: \"\\f51d\"; }\n\n.fa-circle:before {\n  content: \"\\f111\"; }\n\n.fa-circle-notch:before {\n  content: \"\\f1ce\"; }\n\n.fa-city:before {\n  content: \"\\f64f\"; }\n\n.fa-clinic-medical:before {\n  content: \"\\f7f2\"; }\n\n.fa-clipboard:before {\n  content: \"\\f328\"; }\n\n.fa-clipboard-check:before {\n  content: \"\\f46c\"; }\n\n.fa-clipboard-list:before {\n  content: \"\\f46d\"; }\n\n.fa-clock:before {\n  content: \"\\f017\"; }\n\n.fa-clone:before {\n  content: \"\\f24d\"; }\n\n.fa-closed-captioning:before {\n  content: \"\\f20a\"; }\n\n.fa-cloud:before {\n  content: \"\\f0c2\"; }\n\n.fa-cloud-download-alt:before {\n  content: \"\\f381\"; }\n\n.fa-cloud-meatball:before {\n  content: \"\\f73b\"; }\n\n.fa-cloud-moon:before {\n  content: \"\\f6c3\"; }\n\n.fa-cloud-moon-rain:before {\n  content: \"\\f73c\"; }\n\n.fa-cloud-rain:before {\n  content: \"\\f73d\"; }\n\n.fa-cloud-showers-heavy:before {\n  content: \"\\f740\"; }\n\n.fa-cloud-sun:before {\n  content: \"\\f6c4\"; }\n\n.fa-cloud-sun-rain:before {\n  content: \"\\f743\"; }\n\n.fa-cloud-upload-alt:before {\n  content: \"\\f382\"; }\n\n.fa-cloudflare:before {\n  content: \"\\e07d\"; }\n\n.fa-cloudscale:before {\n  content: \"\\f383\"; }\n\n.fa-cloudsmith:before {\n  content: \"\\f384\"; }\n\n.fa-cloudversify:before {\n  content: \"\\f385\"; }\n\n.fa-cocktail:before {\n  content: \"\\f561\"; }\n\n.fa-code:before {\n  content: \"\\f121\"; }\n\n.fa-code-branch:before {\n  content: \"\\f126\"; }\n\n.fa-codepen:before {\n  content: \"\\f1cb\"; }\n\n.fa-codiepie:before {\n  content: \"\\f284\"; }\n\n.fa-coffee:before {\n  content: \"\\f0f4\"; }\n\n.fa-cog:before {\n  content: \"\\f013\"; }\n\n.fa-cogs:before {\n  content: \"\\f085\"; }\n\n.fa-coins:before {\n  content: \"\\f51e\"; }\n\n.fa-columns:before {\n  content: \"\\f0db\"; }\n\n.fa-comment:before {\n  content: \"\\f075\"; }\n\n.fa-comment-alt:before {\n  content: \"\\f27a\"; }\n\n.fa-comment-dollar:before {\n  content: \"\\f651\"; }\n\n.fa-comment-dots:before {\n  content: \"\\f4ad\"; }\n\n.fa-comment-medical:before {\n  content: \"\\f7f5\"; }\n\n.fa-comment-slash:before {\n  content: \"\\f4b3\"; }\n\n.fa-comments:before {\n  content: \"\\f086\"; }\n\n.fa-comments-dollar:before {\n  content: \"\\f653\"; }\n\n.fa-compact-disc:before {\n  content: \"\\f51f\"; }\n\n.fa-compass:before {\n  content: \"\\f14e\"; }\n\n.fa-compress:before {\n  content: \"\\f066\"; }\n\n.fa-compress-alt:before {\n  content: \"\\f422\"; }\n\n.fa-compress-arrows-alt:before {\n  content: \"\\f78c\"; }\n\n.fa-concierge-bell:before {\n  content: \"\\f562\"; }\n\n.fa-confluence:before {\n  content: \"\\f78d\"; }\n\n.fa-connectdevelop:before {\n  content: \"\\f20e\"; }\n\n.fa-contao:before {\n  content: \"\\f26d\"; }\n\n.fa-cookie:before {\n  content: \"\\f563\"; }\n\n.fa-cookie-bite:before {\n  content: \"\\f564\"; }\n\n.fa-copy:before {\n  content: \"\\f0c5\"; }\n\n.fa-copyright:before {\n  content: \"\\f1f9\"; }\n\n.fa-cotton-bureau:before {\n  content: \"\\f89e\"; }\n\n.fa-couch:before {\n  content: \"\\f4b8\"; }\n\n.fa-cpanel:before {\n  content: \"\\f388\"; }\n\n.fa-creative-commons:before {\n  content: \"\\f25e\"; }\n\n.fa-creative-commons-by:before {\n  content: \"\\f4e7\"; }\n\n.fa-creative-commons-nc:before {\n  content: \"\\f4e8\"; }\n\n.fa-creative-commons-nc-eu:before {\n  content: \"\\f4e9\"; }\n\n.fa-creative-commons-nc-jp:before {\n  content: \"\\f4ea\"; }\n\n.fa-creative-commons-nd:before {\n  content: \"\\f4eb\"; }\n\n.fa-creative-commons-pd:before {\n  content: \"\\f4ec\"; }\n\n.fa-creative-commons-pd-alt:before {\n  content: \"\\f4ed\"; }\n\n.fa-creative-commons-remix:before {\n  content: \"\\f4ee\"; }\n\n.fa-creative-commons-sa:before {\n  content: \"\\f4ef\"; }\n\n.fa-creative-commons-sampling:before {\n  content: \"\\f4f0\"; }\n\n.fa-creative-commons-sampling-plus:before {\n  content: \"\\f4f1\"; }\n\n.fa-creative-commons-share:before {\n  content: \"\\f4f2\"; }\n\n.fa-creative-commons-zero:before {\n  content: \"\\f4f3\"; }\n\n.fa-credit-card:before {\n  content: \"\\f09d\"; }\n\n.fa-critical-role:before {\n  content: \"\\f6c9\"; }\n\n.fa-crop:before {\n  content: \"\\f125\"; }\n\n.fa-crop-alt:before {\n  content: \"\\f565\"; }\n\n.fa-cross:before {\n  content: \"\\f654\"; }\n\n.fa-crosshairs:before {\n  content: \"\\f05b\"; }\n\n.fa-crow:before {\n  content: \"\\f520\"; }\n\n.fa-crown:before {\n  content: \"\\f521\"; }\n\n.fa-crutch:before {\n  content: \"\\f7f7\"; }\n\n.fa-css3:before {\n  content: \"\\f13c\"; }\n\n.fa-css3-alt:before {\n  content: \"\\f38b\"; }\n\n.fa-cube:before {\n  content: \"\\f1b2\"; }\n\n.fa-cubes:before {\n  content: \"\\f1b3\"; }\n\n.fa-cut:before {\n  content: \"\\f0c4\"; }\n\n.fa-cuttlefish:before {\n  content: \"\\f38c\"; }\n\n.fa-d-and-d:before {\n  content: \"\\f38d\"; }\n\n.fa-d-and-d-beyond:before {\n  content: \"\\f6ca\"; }\n\n.fa-dailymotion:before {\n  content: \"\\e052\"; }\n\n.fa-dashcube:before {\n  content: \"\\f210\"; }\n\n.fa-database:before {\n  content: \"\\f1c0\"; }\n\n.fa-deaf:before {\n  content: \"\\f2a4\"; }\n\n.fa-deezer:before {\n  content: \"\\e077\"; }\n\n.fa-delicious:before {\n  content: \"\\f1a5\"; }\n\n.fa-democrat:before {\n  content: \"\\f747\"; }\n\n.fa-deploydog:before {\n  content: \"\\f38e\"; }\n\n.fa-deskpro:before {\n  content: \"\\f38f\"; }\n\n.fa-desktop:before {\n  content: \"\\f108\"; }\n\n.fa-dev:before {\n  content: \"\\f6cc\"; }\n\n.fa-deviantart:before {\n  content: \"\\f1bd\"; }\n\n.fa-dharmachakra:before {\n  content: \"\\f655\"; }\n\n.fa-dhl:before {\n  content: \"\\f790\"; }\n\n.fa-diagnoses:before {\n  content: \"\\f470\"; }\n\n.fa-diaspora:before {\n  content: \"\\f791\"; }\n\n.fa-dice:before {\n  content: \"\\f522\"; }\n\n.fa-dice-d20:before {\n  content: \"\\f6cf\"; }\n\n.fa-dice-d6:before {\n  content: \"\\f6d1\"; }\n\n.fa-dice-five:before {\n  content: \"\\f523\"; }\n\n.fa-dice-four:before {\n  content: \"\\f524\"; }\n\n.fa-dice-one:before {\n  content: \"\\f525\"; }\n\n.fa-dice-six:before {\n  content: \"\\f526\"; }\n\n.fa-dice-three:before {\n  content: \"\\f527\"; }\n\n.fa-dice-two:before {\n  content: \"\\f528\"; }\n\n.fa-digg:before {\n  content: \"\\f1a6\"; }\n\n.fa-digital-ocean:before {\n  content: \"\\f391\"; }\n\n.fa-digital-tachograph:before {\n  content: \"\\f566\"; }\n\n.fa-directions:before {\n  content: \"\\f5eb\"; }\n\n.fa-discord:before {\n  content: \"\\f392\"; }\n\n.fa-discourse:before {\n  content: \"\\f393\"; }\n\n.fa-disease:before {\n  content: \"\\f7fa\"; }\n\n.fa-divide:before {\n  content: \"\\f529\"; }\n\n.fa-dizzy:before {\n  content: \"\\f567\"; }\n\n.fa-dna:before {\n  content: \"\\f471\"; }\n\n.fa-dochub:before {\n  content: \"\\f394\"; }\n\n.fa-docker:before {\n  content: \"\\f395\"; }\n\n.fa-dog:before {\n  content: \"\\f6d3\"; }\n\n.fa-dollar-sign:before {\n  content: \"\\f155\"; }\n\n.fa-dolly:before {\n  content: \"\\f472\"; }\n\n.fa-dolly-flatbed:before {\n  content: \"\\f474\"; }\n\n.fa-donate:before {\n  content: \"\\f4b9\"; }\n\n.fa-door-closed:before {\n  content: \"\\f52a\"; }\n\n.fa-door-open:before {\n  content: \"\\f52b\"; }\n\n.fa-dot-circle:before {\n  content: \"\\f192\"; }\n\n.fa-dove:before {\n  content: \"\\f4ba\"; }\n\n.fa-download:before {\n  content: \"\\f019\"; }\n\n.fa-draft2digital:before {\n  content: \"\\f396\"; }\n\n.fa-drafting-compass:before {\n  content: \"\\f568\"; }\n\n.fa-dragon:before {\n  content: \"\\f6d5\"; }\n\n.fa-draw-polygon:before {\n  content: \"\\f5ee\"; }\n\n.fa-dribbble:before {\n  content: \"\\f17d\"; }\n\n.fa-dribbble-square:before {\n  content: \"\\f397\"; }\n\n.fa-dropbox:before {\n  content: \"\\f16b\"; }\n\n.fa-drum:before {\n  content: \"\\f569\"; }\n\n.fa-drum-steelpan:before {\n  content: \"\\f56a\"; }\n\n.fa-drumstick-bite:before {\n  content: \"\\f6d7\"; }\n\n.fa-drupal:before {\n  content: \"\\f1a9\"; }\n\n.fa-dumbbell:before {\n  content: \"\\f44b\"; }\n\n.fa-dumpster:before {\n  content: \"\\f793\"; }\n\n.fa-dumpster-fire:before {\n  content: \"\\f794\"; }\n\n.fa-dungeon:before {\n  content: \"\\f6d9\"; }\n\n.fa-dyalog:before {\n  content: \"\\f399\"; }\n\n.fa-earlybirds:before {\n  content: \"\\f39a\"; }\n\n.fa-ebay:before {\n  content: \"\\f4f4\"; }\n\n.fa-edge:before {\n  content: \"\\f282\"; }\n\n.fa-edge-legacy:before {\n  content: \"\\e078\"; }\n\n.fa-edit:before {\n  content: \"\\f044\"; }\n\n.fa-egg:before {\n  content: \"\\f7fb\"; }\n\n.fa-eject:before {\n  content: \"\\f052\"; }\n\n.fa-elementor:before {\n  content: \"\\f430\"; }\n\n.fa-ellipsis-h:before {\n  content: \"\\f141\"; }\n\n.fa-ellipsis-v:before {\n  content: \"\\f142\"; }\n\n.fa-ello:before {\n  content: \"\\f5f1\"; }\n\n.fa-ember:before {\n  content: \"\\f423\"; }\n\n.fa-empire:before {\n  content: \"\\f1d1\"; }\n\n.fa-envelope:before {\n  content: \"\\f0e0\"; }\n\n.fa-envelope-open:before {\n  content: \"\\f2b6\"; }\n\n.fa-envelope-open-text:before {\n  content: \"\\f658\"; }\n\n.fa-envelope-square:before {\n  content: \"\\f199\"; }\n\n.fa-envira:before {\n  content: \"\\f299\"; }\n\n.fa-equals:before {\n  content: \"\\f52c\"; }\n\n.fa-eraser:before {\n  content: \"\\f12d\"; }\n\n.fa-erlang:before {\n  content: \"\\f39d\"; }\n\n.fa-ethereum:before {\n  content: \"\\f42e\"; }\n\n.fa-ethernet:before {\n  content: \"\\f796\"; }\n\n.fa-etsy:before {\n  content: \"\\f2d7\"; }\n\n.fa-euro-sign:before {\n  content: \"\\f153\"; }\n\n.fa-evernote:before {\n  content: \"\\f839\"; }\n\n.fa-exchange-alt:before {\n  content: \"\\f362\"; }\n\n.fa-exclamation:before {\n  content: \"\\f12a\"; }\n\n.fa-exclamation-circle:before {\n  content: \"\\f06a\"; }\n\n.fa-exclamation-triangle:before {\n  content: \"\\f071\"; }\n\n.fa-expand:before {\n  content: \"\\f065\"; }\n\n.fa-expand-alt:before {\n  content: \"\\f424\"; }\n\n.fa-expand-arrows-alt:before {\n  content: \"\\f31e\"; }\n\n.fa-expeditedssl:before {\n  content: \"\\f23e\"; }\n\n.fa-external-link-alt:before {\n  content: \"\\f35d\"; }\n\n.fa-external-link-square-alt:before {\n  content: \"\\f360\"; }\n\n.fa-eye:before {\n  content: \"\\f06e\"; }\n\n.fa-eye-dropper:before {\n  content: \"\\f1fb\"; }\n\n.fa-eye-slash:before {\n  content: \"\\f070\"; }\n\n.fa-facebook:before {\n  content: \"\\f09a\"; }\n\n.fa-facebook-f:before {\n  content: \"\\f39e\"; }\n\n.fa-facebook-messenger:before {\n  content: \"\\f39f\"; }\n\n.fa-facebook-square:before {\n  content: \"\\f082\"; }\n\n.fa-fan:before {\n  content: \"\\f863\"; }\n\n.fa-fantasy-flight-games:before {\n  content: \"\\f6dc\"; }\n\n.fa-fast-backward:before {\n  content: \"\\f049\"; }\n\n.fa-fast-forward:before {\n  content: \"\\f050\"; }\n\n.fa-faucet:before {\n  content: \"\\e005\"; }\n\n.fa-fax:before {\n  content: \"\\f1ac\"; }\n\n.fa-feather:before {\n  content: \"\\f52d\"; }\n\n.fa-feather-alt:before {\n  content: \"\\f56b\"; }\n\n.fa-fedex:before {\n  content: \"\\f797\"; }\n\n.fa-fedora:before {\n  content: \"\\f798\"; }\n\n.fa-female:before {\n  content: \"\\f182\"; }\n\n.fa-fighter-jet:before {\n  content: \"\\f0fb\"; }\n\n.fa-figma:before {\n  content: \"\\f799\"; }\n\n.fa-file:before {\n  content: \"\\f15b\"; }\n\n.fa-file-alt:before {\n  content: \"\\f15c\"; }\n\n.fa-file-archive:before {\n  content: \"\\f1c6\"; }\n\n.fa-file-audio:before {\n  content: \"\\f1c7\"; }\n\n.fa-file-code:before {\n  content: \"\\f1c9\"; }\n\n.fa-file-contract:before {\n  content: \"\\f56c\"; }\n\n.fa-file-csv:before {\n  content: \"\\f6dd\"; }\n\n.fa-file-download:before {\n  content: \"\\f56d\"; }\n\n.fa-file-excel:before {\n  content: \"\\f1c3\"; }\n\n.fa-file-export:before {\n  content: \"\\f56e\"; }\n\n.fa-file-image:before {\n  content: \"\\f1c5\"; }\n\n.fa-file-import:before {\n  content: \"\\f56f\"; }\n\n.fa-file-invoice:before {\n  content: \"\\f570\"; }\n\n.fa-file-invoice-dollar:before {\n  content: \"\\f571\"; }\n\n.fa-file-medical:before {\n  content: \"\\f477\"; }\n\n.fa-file-medical-alt:before {\n  content: \"\\f478\"; }\n\n.fa-file-pdf:before {\n  content: \"\\f1c1\"; }\n\n.fa-file-powerpoint:before {\n  content: \"\\f1c4\"; }\n\n.fa-file-prescription:before {\n  content: \"\\f572\"; }\n\n.fa-file-signature:before {\n  content: \"\\f573\"; }\n\n.fa-file-upload:before {\n  content: \"\\f574\"; }\n\n.fa-file-video:before {\n  content: \"\\f1c8\"; }\n\n.fa-file-word:before {\n  content: \"\\f1c2\"; }\n\n.fa-fill:before {\n  content: \"\\f575\"; }\n\n.fa-fill-drip:before {\n  content: \"\\f576\"; }\n\n.fa-film:before {\n  content: \"\\f008\"; }\n\n.fa-filter:before {\n  content: \"\\f0b0\"; }\n\n.fa-fingerprint:before {\n  content: \"\\f577\"; }\n\n.fa-fire:before {\n  content: \"\\f06d\"; }\n\n.fa-fire-alt:before {\n  content: \"\\f7e4\"; }\n\n.fa-fire-extinguisher:before {\n  content: \"\\f134\"; }\n\n.fa-firefox:before {\n  content: \"\\f269\"; }\n\n.fa-firefox-browser:before {\n  content: \"\\e007\"; }\n\n.fa-first-aid:before {\n  content: \"\\f479\"; }\n\n.fa-first-order:before {\n  content: \"\\f2b0\"; }\n\n.fa-first-order-alt:before {\n  content: \"\\f50a\"; }\n\n.fa-firstdraft:before {\n  content: \"\\f3a1\"; }\n\n.fa-fish:before {\n  content: \"\\f578\"; }\n\n.fa-fist-raised:before {\n  content: \"\\f6de\"; }\n\n.fa-flag:before {\n  content: \"\\f024\"; }\n\n.fa-flag-checkered:before {\n  content: \"\\f11e\"; }\n\n.fa-flag-usa:before {\n  content: \"\\f74d\"; }\n\n.fa-flask:before {\n  content: \"\\f0c3\"; }\n\n.fa-flickr:before {\n  content: \"\\f16e\"; }\n\n.fa-flipboard:before {\n  content: \"\\f44d\"; }\n\n.fa-flushed:before {\n  content: \"\\f579\"; }\n\n.fa-fly:before {\n  content: \"\\f417\"; }\n\n.fa-folder:before {\n  content: \"\\f07b\"; }\n\n.fa-folder-minus:before {\n  content: \"\\f65d\"; }\n\n.fa-folder-open:before {\n  content: \"\\f07c\"; }\n\n.fa-folder-plus:before {\n  content: \"\\f65e\"; }\n\n.fa-font:before {\n  content: \"\\f031\"; }\n\n.fa-font-awesome:before {\n  content: \"\\f2b4\"; }\n\n.fa-font-awesome-alt:before {\n  content: \"\\f35c\"; }\n\n.fa-font-awesome-flag:before {\n  content: \"\\f425\"; }\n\n.fa-font-awesome-logo-full:before {\n  content: \"\\f4e6\"; }\n\n.fa-fonticons:before {\n  content: \"\\f280\"; }\n\n.fa-fonticons-fi:before {\n  content: \"\\f3a2\"; }\n\n.fa-football-ball:before {\n  content: \"\\f44e\"; }\n\n.fa-fort-awesome:before {\n  content: \"\\f286\"; }\n\n.fa-fort-awesome-alt:before {\n  content: \"\\f3a3\"; }\n\n.fa-forumbee:before {\n  content: \"\\f211\"; }\n\n.fa-forward:before {\n  content: \"\\f04e\"; }\n\n.fa-foursquare:before {\n  content: \"\\f180\"; }\n\n.fa-free-code-camp:before {\n  content: \"\\f2c5\"; }\n\n.fa-freebsd:before {\n  content: \"\\f3a4\"; }\n\n.fa-frog:before {\n  content: \"\\f52e\"; }\n\n.fa-frown:before {\n  content: \"\\f119\"; }\n\n.fa-frown-open:before {\n  content: \"\\f57a\"; }\n\n.fa-fulcrum:before {\n  content: \"\\f50b\"; }\n\n.fa-funnel-dollar:before {\n  content: \"\\f662\"; }\n\n.fa-futbol:before {\n  content: \"\\f1e3\"; }\n\n.fa-galactic-republic:before {\n  content: \"\\f50c\"; }\n\n.fa-galactic-senate:before {\n  content: \"\\f50d\"; }\n\n.fa-gamepad:before {\n  content: \"\\f11b\"; }\n\n.fa-gas-pump:before {\n  content: \"\\f52f\"; }\n\n.fa-gavel:before {\n  content: \"\\f0e3\"; }\n\n.fa-gem:before {\n  content: \"\\f3a5\"; }\n\n.fa-genderless:before {\n  content: \"\\f22d\"; }\n\n.fa-get-pocket:before {\n  content: \"\\f265\"; }\n\n.fa-gg:before {\n  content: \"\\f260\"; }\n\n.fa-gg-circle:before {\n  content: \"\\f261\"; }\n\n.fa-ghost:before {\n  content: \"\\f6e2\"; }\n\n.fa-gift:before {\n  content: \"\\f06b\"; }\n\n.fa-gifts:before {\n  content: \"\\f79c\"; }\n\n.fa-git:before {\n  content: \"\\f1d3\"; }\n\n.fa-git-alt:before {\n  content: \"\\f841\"; }\n\n.fa-git-square:before {\n  content: \"\\f1d2\"; }\n\n.fa-github:before {\n  content: \"\\f09b\"; }\n\n.fa-github-alt:before {\n  content: \"\\f113\"; }\n\n.fa-github-square:before {\n  content: \"\\f092\"; }\n\n.fa-gitkraken:before {\n  content: \"\\f3a6\"; }\n\n.fa-gitlab:before {\n  content: \"\\f296\"; }\n\n.fa-gitter:before {\n  content: \"\\f426\"; }\n\n.fa-glass-cheers:before {\n  content: \"\\f79f\"; }\n\n.fa-glass-martini:before {\n  content: \"\\f000\"; }\n\n.fa-glass-martini-alt:before {\n  content: \"\\f57b\"; }\n\n.fa-glass-whiskey:before {\n  content: \"\\f7a0\"; }\n\n.fa-glasses:before {\n  content: \"\\f530\"; }\n\n.fa-glide:before {\n  content: \"\\f2a5\"; }\n\n.fa-glide-g:before {\n  content: \"\\f2a6\"; }\n\n.fa-globe:before {\n  content: \"\\f0ac\"; }\n\n.fa-globe-africa:before {\n  content: \"\\f57c\"; }\n\n.fa-globe-americas:before {\n  content: \"\\f57d\"; }\n\n.fa-globe-asia:before {\n  content: \"\\f57e\"; }\n\n.fa-globe-europe:before {\n  content: \"\\f7a2\"; }\n\n.fa-gofore:before {\n  content: \"\\f3a7\"; }\n\n.fa-golf-ball:before {\n  content: \"\\f450\"; }\n\n.fa-goodreads:before {\n  content: \"\\f3a8\"; }\n\n.fa-goodreads-g:before {\n  content: \"\\f3a9\"; }\n\n.fa-google:before {\n  content: \"\\f1a0\"; }\n\n.fa-google-drive:before {\n  content: \"\\f3aa\"; }\n\n.fa-google-pay:before {\n  content: \"\\e079\"; }\n\n.fa-google-play:before {\n  content: \"\\f3ab\"; }\n\n.fa-google-plus:before {\n  content: \"\\f2b3\"; }\n\n.fa-google-plus-g:before {\n  content: \"\\f0d5\"; }\n\n.fa-google-plus-square:before {\n  content: \"\\f0d4\"; }\n\n.fa-google-wallet:before {\n  content: \"\\f1ee\"; }\n\n.fa-gopuram:before {\n  content: \"\\f664\"; }\n\n.fa-graduation-cap:before {\n  content: \"\\f19d\"; }\n\n.fa-gratipay:before {\n  content: \"\\f184\"; }\n\n.fa-grav:before {\n  content: \"\\f2d6\"; }\n\n.fa-greater-than:before {\n  content: \"\\f531\"; }\n\n.fa-greater-than-equal:before {\n  content: \"\\f532\"; }\n\n.fa-grimace:before {\n  content: \"\\f57f\"; }\n\n.fa-grin:before {\n  content: \"\\f580\"; }\n\n.fa-grin-alt:before {\n  content: \"\\f581\"; }\n\n.fa-grin-beam:before {\n  content: \"\\f582\"; }\n\n.fa-grin-beam-sweat:before {\n  content: \"\\f583\"; }\n\n.fa-grin-hearts:before {\n  content: \"\\f584\"; }\n\n.fa-grin-squint:before {\n  content: \"\\f585\"; }\n\n.fa-grin-squint-tears:before {\n  content: \"\\f586\"; }\n\n.fa-grin-stars:before {\n  content: \"\\f587\"; }\n\n.fa-grin-tears:before {\n  content: \"\\f588\"; }\n\n.fa-grin-tongue:before {\n  content: \"\\f589\"; }\n\n.fa-grin-tongue-squint:before {\n  content: \"\\f58a\"; }\n\n.fa-grin-tongue-wink:before {\n  content: \"\\f58b\"; }\n\n.fa-grin-wink:before {\n  content: \"\\f58c\"; }\n\n.fa-grip-horizontal:before {\n  content: \"\\f58d\"; }\n\n.fa-grip-lines:before {\n  content: \"\\f7a4\"; }\n\n.fa-grip-lines-vertical:before {\n  content: \"\\f7a5\"; }\n\n.fa-grip-vertical:before {\n  content: \"\\f58e\"; }\n\n.fa-gripfire:before {\n  content: \"\\f3ac\"; }\n\n.fa-grunt:before {\n  content: \"\\f3ad\"; }\n\n.fa-guilded:before {\n  content: \"\\e07e\"; }\n\n.fa-guitar:before {\n  content: \"\\f7a6\"; }\n\n.fa-gulp:before {\n  content: \"\\f3ae\"; }\n\n.fa-h-square:before {\n  content: \"\\f0fd\"; }\n\n.fa-hacker-news:before {\n  content: \"\\f1d4\"; }\n\n.fa-hacker-news-square:before {\n  content: \"\\f3af\"; }\n\n.fa-hackerrank:before {\n  content: \"\\f5f7\"; }\n\n.fa-hamburger:before {\n  content: \"\\f805\"; }\n\n.fa-hammer:before {\n  content: \"\\f6e3\"; }\n\n.fa-hamsa:before {\n  content: \"\\f665\"; }\n\n.fa-hand-holding:before {\n  content: \"\\f4bd\"; }\n\n.fa-hand-holding-heart:before {\n  content: \"\\f4be\"; }\n\n.fa-hand-holding-medical:before {\n  content: \"\\e05c\"; }\n\n.fa-hand-holding-usd:before {\n  content: \"\\f4c0\"; }\n\n.fa-hand-holding-water:before {\n  content: \"\\f4c1\"; }\n\n.fa-hand-lizard:before {\n  content: \"\\f258\"; }\n\n.fa-hand-middle-finger:before {\n  content: \"\\f806\"; }\n\n.fa-hand-paper:before {\n  content: \"\\f256\"; }\n\n.fa-hand-peace:before {\n  content: \"\\f25b\"; }\n\n.fa-hand-point-down:before {\n  content: \"\\f0a7\"; }\n\n.fa-hand-point-left:before {\n  content: \"\\f0a5\"; }\n\n.fa-hand-point-right:before {\n  content: \"\\f0a4\"; }\n\n.fa-hand-point-up:before {\n  content: \"\\f0a6\"; }\n\n.fa-hand-pointer:before {\n  content: \"\\f25a\"; }\n\n.fa-hand-rock:before {\n  content: \"\\f255\"; }\n\n.fa-hand-scissors:before {\n  content: \"\\f257\"; }\n\n.fa-hand-sparkles:before {\n  content: \"\\e05d\"; }\n\n.fa-hand-spock:before {\n  content: \"\\f259\"; }\n\n.fa-hands:before {\n  content: \"\\f4c2\"; }\n\n.fa-hands-helping:before {\n  content: \"\\f4c4\"; }\n\n.fa-hands-wash:before {\n  content: \"\\e05e\"; }\n\n.fa-handshake:before {\n  content: \"\\f2b5\"; }\n\n.fa-handshake-alt-slash:before {\n  content: \"\\e05f\"; }\n\n.fa-handshake-slash:before {\n  content: \"\\e060\"; }\n\n.fa-hanukiah:before {\n  content: \"\\f6e6\"; }\n\n.fa-hard-hat:before {\n  content: \"\\f807\"; }\n\n.fa-hashtag:before {\n  content: \"\\f292\"; }\n\n.fa-hat-cowboy:before {\n  content: \"\\f8c0\"; }\n\n.fa-hat-cowboy-side:before {\n  content: \"\\f8c1\"; }\n\n.fa-hat-wizard:before {\n  content: \"\\f6e8\"; }\n\n.fa-hdd:before {\n  content: \"\\f0a0\"; }\n\n.fa-head-side-cough:before {\n  content: \"\\e061\"; }\n\n.fa-head-side-cough-slash:before {\n  content: \"\\e062\"; }\n\n.fa-head-side-mask:before {\n  content: \"\\e063\"; }\n\n.fa-head-side-virus:before {\n  content: \"\\e064\"; }\n\n.fa-heading:before {\n  content: \"\\f1dc\"; }\n\n.fa-headphones:before {\n  content: \"\\f025\"; }\n\n.fa-headphones-alt:before {\n  content: \"\\f58f\"; }\n\n.fa-headset:before {\n  content: \"\\f590\"; }\n\n.fa-heart:before {\n  content: \"\\f004\"; }\n\n.fa-heart-broken:before {\n  content: \"\\f7a9\"; }\n\n.fa-heartbeat:before {\n  content: \"\\f21e\"; }\n\n.fa-helicopter:before {\n  content: \"\\f533\"; }\n\n.fa-highlighter:before {\n  content: \"\\f591\"; }\n\n.fa-hiking:before {\n  content: \"\\f6ec\"; }\n\n.fa-hippo:before {\n  content: \"\\f6ed\"; }\n\n.fa-hips:before {\n  content: \"\\f452\"; }\n\n.fa-hire-a-helper:before {\n  content: \"\\f3b0\"; }\n\n.fa-history:before {\n  content: \"\\f1da\"; }\n\n.fa-hive:before {\n  content: \"\\e07f\"; }\n\n.fa-hockey-puck:before {\n  content: \"\\f453\"; }\n\n.fa-holly-berry:before {\n  content: \"\\f7aa\"; }\n\n.fa-home:before {\n  content: \"\\f015\"; }\n\n.fa-hooli:before {\n  content: \"\\f427\"; }\n\n.fa-hornbill:before {\n  content: \"\\f592\"; }\n\n.fa-horse:before {\n  content: \"\\f6f0\"; }\n\n.fa-horse-head:before {\n  content: \"\\f7ab\"; }\n\n.fa-hospital:before {\n  content: \"\\f0f8\"; }\n\n.fa-hospital-alt:before {\n  content: \"\\f47d\"; }\n\n.fa-hospital-symbol:before {\n  content: \"\\f47e\"; }\n\n.fa-hospital-user:before {\n  content: \"\\f80d\"; }\n\n.fa-hot-tub:before {\n  content: \"\\f593\"; }\n\n.fa-hotdog:before {\n  content: \"\\f80f\"; }\n\n.fa-hotel:before {\n  content: \"\\f594\"; }\n\n.fa-hotjar:before {\n  content: \"\\f3b1\"; }\n\n.fa-hourglass:before {\n  content: \"\\f254\"; }\n\n.fa-hourglass-end:before {\n  content: \"\\f253\"; }\n\n.fa-hourglass-half:before {\n  content: \"\\f252\"; }\n\n.fa-hourglass-start:before {\n  content: \"\\f251\"; }\n\n.fa-house-damage:before {\n  content: \"\\f6f1\"; }\n\n.fa-house-user:before {\n  content: \"\\e065\"; }\n\n.fa-houzz:before {\n  content: \"\\f27c\"; }\n\n.fa-hryvnia:before {\n  content: \"\\f6f2\"; }\n\n.fa-html5:before {\n  content: \"\\f13b\"; }\n\n.fa-hubspot:before {\n  content: \"\\f3b2\"; }\n\n.fa-i-cursor:before {\n  content: \"\\f246\"; }\n\n.fa-ice-cream:before {\n  content: \"\\f810\"; }\n\n.fa-icicles:before {\n  content: \"\\f7ad\"; }\n\n.fa-icons:before {\n  content: \"\\f86d\"; }\n\n.fa-id-badge:before {\n  content: \"\\f2c1\"; }\n\n.fa-id-card:before {\n  content: \"\\f2c2\"; }\n\n.fa-id-card-alt:before {\n  content: \"\\f47f\"; }\n\n.fa-ideal:before {\n  content: \"\\e013\"; }\n\n.fa-igloo:before {\n  content: \"\\f7ae\"; }\n\n.fa-image:before {\n  content: \"\\f03e\"; }\n\n.fa-images:before {\n  content: \"\\f302\"; }\n\n.fa-imdb:before {\n  content: \"\\f2d8\"; }\n\n.fa-inbox:before {\n  content: \"\\f01c\"; }\n\n.fa-indent:before {\n  content: \"\\f03c\"; }\n\n.fa-industry:before {\n  content: \"\\f275\"; }\n\n.fa-infinity:before {\n  content: \"\\f534\"; }\n\n.fa-info:before {\n  content: \"\\f129\"; }\n\n.fa-info-circle:before {\n  content: \"\\f05a\"; }\n\n.fa-innosoft:before {\n  content: \"\\e080\"; }\n\n.fa-instagram:before {\n  content: \"\\f16d\"; }\n\n.fa-instagram-square:before {\n  content: \"\\e055\"; }\n\n.fa-instalod:before {\n  content: \"\\e081\"; }\n\n.fa-intercom:before {\n  content: \"\\f7af\"; }\n\n.fa-internet-explorer:before {\n  content: \"\\f26b\"; }\n\n.fa-invision:before {\n  content: \"\\f7b0\"; }\n\n.fa-ioxhost:before {\n  content: \"\\f208\"; }\n\n.fa-italic:before {\n  content: \"\\f033\"; }\n\n.fa-itch-io:before {\n  content: \"\\f83a\"; }\n\n.fa-itunes:before {\n  content: \"\\f3b4\"; }\n\n.fa-itunes-note:before {\n  content: \"\\f3b5\"; }\n\n.fa-java:before {\n  content: \"\\f4e4\"; }\n\n.fa-jedi:before {\n  content: \"\\f669\"; }\n\n.fa-jedi-order:before {\n  content: \"\\f50e\"; }\n\n.fa-jenkins:before {\n  content: \"\\f3b6\"; }\n\n.fa-jira:before {\n  content: \"\\f7b1\"; }\n\n.fa-joget:before {\n  content: \"\\f3b7\"; }\n\n.fa-joint:before {\n  content: \"\\f595\"; }\n\n.fa-joomla:before {\n  content: \"\\f1aa\"; }\n\n.fa-journal-whills:before {\n  content: \"\\f66a\"; }\n\n.fa-js:before {\n  content: \"\\f3b8\"; }\n\n.fa-js-square:before {\n  content: \"\\f3b9\"; }\n\n.fa-jsfiddle:before {\n  content: \"\\f1cc\"; }\n\n.fa-kaaba:before {\n  content: \"\\f66b\"; }\n\n.fa-kaggle:before {\n  content: \"\\f5fa\"; }\n\n.fa-key:before {\n  content: \"\\f084\"; }\n\n.fa-keybase:before {\n  content: \"\\f4f5\"; }\n\n.fa-keyboard:before {\n  content: \"\\f11c\"; }\n\n.fa-keycdn:before {\n  content: \"\\f3ba\"; }\n\n.fa-khanda:before {\n  content: \"\\f66d\"; }\n\n.fa-kickstarter:before {\n  content: \"\\f3bb\"; }\n\n.fa-kickstarter-k:before {\n  content: \"\\f3bc\"; }\n\n.fa-kiss:before {\n  content: \"\\f596\"; }\n\n.fa-kiss-beam:before {\n  content: \"\\f597\"; }\n\n.fa-kiss-wink-heart:before {\n  content: \"\\f598\"; }\n\n.fa-kiwi-bird:before {\n  content: \"\\f535\"; }\n\n.fa-korvue:before {\n  content: \"\\f42f\"; }\n\n.fa-landmark:before {\n  content: \"\\f66f\"; }\n\n.fa-language:before {\n  content: \"\\f1ab\"; }\n\n.fa-laptop:before {\n  content: \"\\f109\"; }\n\n.fa-laptop-code:before {\n  content: \"\\f5fc\"; }\n\n.fa-laptop-house:before {\n  content: \"\\e066\"; }\n\n.fa-laptop-medical:before {\n  content: \"\\f812\"; }\n\n.fa-laravel:before {\n  content: \"\\f3bd\"; }\n\n.fa-lastfm:before {\n  content: \"\\f202\"; }\n\n.fa-lastfm-square:before {\n  content: \"\\f203\"; }\n\n.fa-laugh:before {\n  content: \"\\f599\"; }\n\n.fa-laugh-beam:before {\n  content: \"\\f59a\"; }\n\n.fa-laugh-squint:before {\n  content: \"\\f59b\"; }\n\n.fa-laugh-wink:before {\n  content: \"\\f59c\"; }\n\n.fa-layer-group:before {\n  content: \"\\f5fd\"; }\n\n.fa-leaf:before {\n  content: \"\\f06c\"; }\n\n.fa-leanpub:before {\n  content: \"\\f212\"; }\n\n.fa-lemon:before {\n  content: \"\\f094\"; }\n\n.fa-less:before {\n  content: \"\\f41d\"; }\n\n.fa-less-than:before {\n  content: \"\\f536\"; }\n\n.fa-less-than-equal:before {\n  content: \"\\f537\"; }\n\n.fa-level-down-alt:before {\n  content: \"\\f3be\"; }\n\n.fa-level-up-alt:before {\n  content: \"\\f3bf\"; }\n\n.fa-life-ring:before {\n  content: \"\\f1cd\"; }\n\n.fa-lightbulb:before {\n  content: \"\\f0eb\"; }\n\n.fa-line:before {\n  content: \"\\f3c0\"; }\n\n.fa-link:before {\n  content: \"\\f0c1\"; }\n\n.fa-linkedin:before {\n  content: \"\\f08c\"; }\n\n.fa-linkedin-in:before {\n  content: \"\\f0e1\"; }\n\n.fa-linode:before {\n  content: \"\\f2b8\"; }\n\n.fa-linux:before {\n  content: \"\\f17c\"; }\n\n.fa-lira-sign:before {\n  content: \"\\f195\"; }\n\n.fa-list:before {\n  content: \"\\f03a\"; }\n\n.fa-list-alt:before {\n  content: \"\\f022\"; }\n\n.fa-list-ol:before {\n  content: \"\\f0cb\"; }\n\n.fa-list-ul:before {\n  content: \"\\f0ca\"; }\n\n.fa-location-arrow:before {\n  content: \"\\f124\"; }\n\n.fa-lock:before {\n  content: \"\\f023\"; }\n\n.fa-lock-open:before {\n  content: \"\\f3c1\"; }\n\n.fa-long-arrow-alt-down:before {\n  content: \"\\f309\"; }\n\n.fa-long-arrow-alt-left:before {\n  content: \"\\f30a\"; }\n\n.fa-long-arrow-alt-right:before {\n  content: \"\\f30b\"; }\n\n.fa-long-arrow-alt-up:before {\n  content: \"\\f30c\"; }\n\n.fa-low-vision:before {\n  content: \"\\f2a8\"; }\n\n.fa-luggage-cart:before {\n  content: \"\\f59d\"; }\n\n.fa-lungs:before {\n  content: \"\\f604\"; }\n\n.fa-lungs-virus:before {\n  content: \"\\e067\"; }\n\n.fa-lyft:before {\n  content: \"\\f3c3\"; }\n\n.fa-magento:before {\n  content: \"\\f3c4\"; }\n\n.fa-magic:before {\n  content: \"\\f0d0\"; }\n\n.fa-magnet:before {\n  content: \"\\f076\"; }\n\n.fa-mail-bulk:before {\n  content: \"\\f674\"; }\n\n.fa-mailchimp:before {\n  content: \"\\f59e\"; }\n\n.fa-male:before {\n  content: \"\\f183\"; }\n\n.fa-mandalorian:before {\n  content: \"\\f50f\"; }\n\n.fa-map:before {\n  content: \"\\f279\"; }\n\n.fa-map-marked:before {\n  content: \"\\f59f\"; }\n\n.fa-map-marked-alt:before {\n  content: \"\\f5a0\"; }\n\n.fa-map-marker:before {\n  content: \"\\f041\"; }\n\n.fa-map-marker-alt:before {\n  content: \"\\f3c5\"; }\n\n.fa-map-pin:before {\n  content: \"\\f276\"; }\n\n.fa-map-signs:before {\n  content: \"\\f277\"; }\n\n.fa-markdown:before {\n  content: \"\\f60f\"; }\n\n.fa-marker:before {\n  content: \"\\f5a1\"; }\n\n.fa-mars:before {\n  content: \"\\f222\"; }\n\n.fa-mars-double:before {\n  content: \"\\f227\"; }\n\n.fa-mars-stroke:before {\n  content: \"\\f229\"; }\n\n.fa-mars-stroke-h:before {\n  content: \"\\f22b\"; }\n\n.fa-mars-stroke-v:before {\n  content: \"\\f22a\"; }\n\n.fa-mask:before {\n  content: \"\\f6fa\"; }\n\n.fa-mastodon:before {\n  content: \"\\f4f6\"; }\n\n.fa-maxcdn:before {\n  content: \"\\f136\"; }\n\n.fa-mdb:before {\n  content: \"\\f8ca\"; }\n\n.fa-medal:before {\n  content: \"\\f5a2\"; }\n\n.fa-medapps:before {\n  content: \"\\f3c6\"; }\n\n.fa-medium:before {\n  content: \"\\f23a\"; }\n\n.fa-medium-m:before {\n  content: \"\\f3c7\"; }\n\n.fa-medkit:before {\n  content: \"\\f0fa\"; }\n\n.fa-medrt:before {\n  content: \"\\f3c8\"; }\n\n.fa-meetup:before {\n  content: \"\\f2e0\"; }\n\n.fa-megaport:before {\n  content: \"\\f5a3\"; }\n\n.fa-meh:before {\n  content: \"\\f11a\"; }\n\n.fa-meh-blank:before {\n  content: \"\\f5a4\"; }\n\n.fa-meh-rolling-eyes:before {\n  content: \"\\f5a5\"; }\n\n.fa-memory:before {\n  content: \"\\f538\"; }\n\n.fa-mendeley:before {\n  content: \"\\f7b3\"; }\n\n.fa-menorah:before {\n  content: \"\\f676\"; }\n\n.fa-mercury:before {\n  content: \"\\f223\"; }\n\n.fa-meteor:before {\n  content: \"\\f753\"; }\n\n.fa-microblog:before {\n  content: \"\\e01a\"; }\n\n.fa-microchip:before {\n  content: \"\\f2db\"; }\n\n.fa-microphone:before {\n  content: \"\\f130\"; }\n\n.fa-microphone-alt:before {\n  content: \"\\f3c9\"; }\n\n.fa-microphone-alt-slash:before {\n  content: \"\\f539\"; }\n\n.fa-microphone-slash:before {\n  content: \"\\f131\"; }\n\n.fa-microscope:before {\n  content: \"\\f610\"; }\n\n.fa-microsoft:before {\n  content: \"\\f3ca\"; }\n\n.fa-minus:before {\n  content: \"\\f068\"; }\n\n.fa-minus-circle:before {\n  content: \"\\f056\"; }\n\n.fa-minus-square:before {\n  content: \"\\f146\"; }\n\n.fa-mitten:before {\n  content: \"\\f7b5\"; }\n\n.fa-mix:before {\n  content: \"\\f3cb\"; }\n\n.fa-mixcloud:before {\n  content: \"\\f289\"; }\n\n.fa-mixer:before {\n  content: \"\\e056\"; }\n\n.fa-mizuni:before {\n  content: \"\\f3cc\"; }\n\n.fa-mobile:before {\n  content: \"\\f10b\"; }\n\n.fa-mobile-alt:before {\n  content: \"\\f3cd\"; }\n\n.fa-modx:before {\n  content: \"\\f285\"; }\n\n.fa-monero:before {\n  content: \"\\f3d0\"; }\n\n.fa-money-bill:before {\n  content: \"\\f0d6\"; }\n\n.fa-money-bill-alt:before {\n  content: \"\\f3d1\"; }\n\n.fa-money-bill-wave:before {\n  content: \"\\f53a\"; }\n\n.fa-money-bill-wave-alt:before {\n  content: \"\\f53b\"; }\n\n.fa-money-check:before {\n  content: \"\\f53c\"; }\n\n.fa-money-check-alt:before {\n  content: \"\\f53d\"; }\n\n.fa-monument:before {\n  content: \"\\f5a6\"; }\n\n.fa-moon:before {\n  content: \"\\f186\"; }\n\n.fa-mortar-pestle:before {\n  content: \"\\f5a7\"; }\n\n.fa-mosque:before {\n  content: \"\\f678\"; }\n\n.fa-motorcycle:before {\n  content: \"\\f21c\"; }\n\n.fa-mountain:before {\n  content: \"\\f6fc\"; }\n\n.fa-mouse:before {\n  content: \"\\f8cc\"; }\n\n.fa-mouse-pointer:before {\n  content: \"\\f245\"; }\n\n.fa-mug-hot:before {\n  content: \"\\f7b6\"; }\n\n.fa-music:before {\n  content: \"\\f001\"; }\n\n.fa-napster:before {\n  content: \"\\f3d2\"; }\n\n.fa-neos:before {\n  content: \"\\f612\"; }\n\n.fa-network-wired:before {\n  content: \"\\f6ff\"; }\n\n.fa-neuter:before {\n  content: \"\\f22c\"; }\n\n.fa-newspaper:before {\n  content: \"\\f1ea\"; }\n\n.fa-nimblr:before {\n  content: \"\\f5a8\"; }\n\n.fa-node:before {\n  content: \"\\f419\"; }\n\n.fa-node-js:before {\n  content: \"\\f3d3\"; }\n\n.fa-not-equal:before {\n  content: \"\\f53e\"; }\n\n.fa-notes-medical:before {\n  content: \"\\f481\"; }\n\n.fa-npm:before {\n  content: \"\\f3d4\"; }\n\n.fa-ns8:before {\n  content: \"\\f3d5\"; }\n\n.fa-nutritionix:before {\n  content: \"\\f3d6\"; }\n\n.fa-object-group:before {\n  content: \"\\f247\"; }\n\n.fa-object-ungroup:before {\n  content: \"\\f248\"; }\n\n.fa-octopus-deploy:before {\n  content: \"\\e082\"; }\n\n.fa-odnoklassniki:before {\n  content: \"\\f263\"; }\n\n.fa-odnoklassniki-square:before {\n  content: \"\\f264\"; }\n\n.fa-oil-can:before {\n  content: \"\\f613\"; }\n\n.fa-old-republic:before {\n  content: \"\\f510\"; }\n\n.fa-om:before {\n  content: \"\\f679\"; }\n\n.fa-opencart:before {\n  content: \"\\f23d\"; }\n\n.fa-openid:before {\n  content: \"\\f19b\"; }\n\n.fa-opera:before {\n  content: \"\\f26a\"; }\n\n.fa-optin-monster:before {\n  content: \"\\f23c\"; }\n\n.fa-orcid:before {\n  content: \"\\f8d2\"; }\n\n.fa-osi:before {\n  content: \"\\f41a\"; }\n\n.fa-otter:before {\n  content: \"\\f700\"; }\n\n.fa-outdent:before {\n  content: \"\\f03b\"; }\n\n.fa-page4:before {\n  content: \"\\f3d7\"; }\n\n.fa-pagelines:before {\n  content: \"\\f18c\"; }\n\n.fa-pager:before {\n  content: \"\\f815\"; }\n\n.fa-paint-brush:before {\n  content: \"\\f1fc\"; }\n\n.fa-paint-roller:before {\n  content: \"\\f5aa\"; }\n\n.fa-palette:before {\n  content: \"\\f53f\"; }\n\n.fa-palfed:before {\n  content: \"\\f3d8\"; }\n\n.fa-pallet:before {\n  content: \"\\f482\"; }\n\n.fa-paper-plane:before {\n  content: \"\\f1d8\"; }\n\n.fa-paperclip:before {\n  content: \"\\f0c6\"; }\n\n.fa-parachute-box:before {\n  content: \"\\f4cd\"; }\n\n.fa-paragraph:before {\n  content: \"\\f1dd\"; }\n\n.fa-parking:before {\n  content: \"\\f540\"; }\n\n.fa-passport:before {\n  content: \"\\f5ab\"; }\n\n.fa-pastafarianism:before {\n  content: \"\\f67b\"; }\n\n.fa-paste:before {\n  content: \"\\f0ea\"; }\n\n.fa-patreon:before {\n  content: \"\\f3d9\"; }\n\n.fa-pause:before {\n  content: \"\\f04c\"; }\n\n.fa-pause-circle:before {\n  content: \"\\f28b\"; }\n\n.fa-paw:before {\n  content: \"\\f1b0\"; }\n\n.fa-paypal:before {\n  content: \"\\f1ed\"; }\n\n.fa-peace:before {\n  content: \"\\f67c\"; }\n\n.fa-pen:before {\n  content: \"\\f304\"; }\n\n.fa-pen-alt:before {\n  content: \"\\f305\"; }\n\n.fa-pen-fancy:before {\n  content: \"\\f5ac\"; }\n\n.fa-pen-nib:before {\n  content: \"\\f5ad\"; }\n\n.fa-pen-square:before {\n  content: \"\\f14b\"; }\n\n.fa-pencil-alt:before {\n  content: \"\\f303\"; }\n\n.fa-pencil-ruler:before {\n  content: \"\\f5ae\"; }\n\n.fa-penny-arcade:before {\n  content: \"\\f704\"; }\n\n.fa-people-arrows:before {\n  content: \"\\e068\"; }\n\n.fa-people-carry:before {\n  content: \"\\f4ce\"; }\n\n.fa-pepper-hot:before {\n  content: \"\\f816\"; }\n\n.fa-perbyte:before {\n  content: \"\\e083\"; }\n\n.fa-percent:before {\n  content: \"\\f295\"; }\n\n.fa-percentage:before {\n  content: \"\\f541\"; }\n\n.fa-periscope:before {\n  content: \"\\f3da\"; }\n\n.fa-person-booth:before {\n  content: \"\\f756\"; }\n\n.fa-phabricator:before {\n  content: \"\\f3db\"; }\n\n.fa-phoenix-framework:before {\n  content: \"\\f3dc\"; }\n\n.fa-phoenix-squadron:before {\n  content: \"\\f511\"; }\n\n.fa-phone:before {\n  content: \"\\f095\"; }\n\n.fa-phone-alt:before {\n  content: \"\\f879\"; }\n\n.fa-phone-slash:before {\n  content: \"\\f3dd\"; }\n\n.fa-phone-square:before {\n  content: \"\\f098\"; }\n\n.fa-phone-square-alt:before {\n  content: \"\\f87b\"; }\n\n.fa-phone-volume:before {\n  content: \"\\f2a0\"; }\n\n.fa-photo-video:before {\n  content: \"\\f87c\"; }\n\n.fa-php:before {\n  content: \"\\f457\"; }\n\n.fa-pied-piper:before {\n  content: \"\\f2ae\"; }\n\n.fa-pied-piper-alt:before {\n  content: \"\\f1a8\"; }\n\n.fa-pied-piper-hat:before {\n  content: \"\\f4e5\"; }\n\n.fa-pied-piper-pp:before {\n  content: \"\\f1a7\"; }\n\n.fa-pied-piper-square:before {\n  content: \"\\e01e\"; }\n\n.fa-piggy-bank:before {\n  content: \"\\f4d3\"; }\n\n.fa-pills:before {\n  content: \"\\f484\"; }\n\n.fa-pinterest:before {\n  content: \"\\f0d2\"; }\n\n.fa-pinterest-p:before {\n  content: \"\\f231\"; }\n\n.fa-pinterest-square:before {\n  content: \"\\f0d3\"; }\n\n.fa-pizza-slice:before {\n  content: \"\\f818\"; }\n\n.fa-place-of-worship:before {\n  content: \"\\f67f\"; }\n\n.fa-plane:before {\n  content: \"\\f072\"; }\n\n.fa-plane-arrival:before {\n  content: \"\\f5af\"; }\n\n.fa-plane-departure:before {\n  content: \"\\f5b0\"; }\n\n.fa-plane-slash:before {\n  content: \"\\e069\"; }\n\n.fa-play:before {\n  content: \"\\f04b\"; }\n\n.fa-play-circle:before {\n  content: \"\\f144\"; }\n\n.fa-playstation:before {\n  content: \"\\f3df\"; }\n\n.fa-plug:before {\n  content: \"\\f1e6\"; }\n\n.fa-plus:before {\n  content: \"\\f067\"; }\n\n.fa-plus-circle:before {\n  content: \"\\f055\"; }\n\n.fa-plus-square:before {\n  content: \"\\f0fe\"; }\n\n.fa-podcast:before {\n  content: \"\\f2ce\"; }\n\n.fa-poll:before {\n  content: \"\\f681\"; }\n\n.fa-poll-h:before {\n  content: \"\\f682\"; }\n\n.fa-poo:before {\n  content: \"\\f2fe\"; }\n\n.fa-poo-storm:before {\n  content: \"\\f75a\"; }\n\n.fa-poop:before {\n  content: \"\\f619\"; }\n\n.fa-portrait:before {\n  content: \"\\f3e0\"; }\n\n.fa-pound-sign:before {\n  content: \"\\f154\"; }\n\n.fa-power-off:before {\n  content: \"\\f011\"; }\n\n.fa-pray:before {\n  content: \"\\f683\"; }\n\n.fa-praying-hands:before {\n  content: \"\\f684\"; }\n\n.fa-prescription:before {\n  content: \"\\f5b1\"; }\n\n.fa-prescription-bottle:before {\n  content: \"\\f485\"; }\n\n.fa-prescription-bottle-alt:before {\n  content: \"\\f486\"; }\n\n.fa-print:before {\n  content: \"\\f02f\"; }\n\n.fa-procedures:before {\n  content: \"\\f487\"; }\n\n.fa-product-hunt:before {\n  content: \"\\f288\"; }\n\n.fa-project-diagram:before {\n  content: \"\\f542\"; }\n\n.fa-pump-medical:before {\n  content: \"\\e06a\"; }\n\n.fa-pump-soap:before {\n  content: \"\\e06b\"; }\n\n.fa-pushed:before {\n  content: \"\\f3e1\"; }\n\n.fa-puzzle-piece:before {\n  content: \"\\f12e\"; }\n\n.fa-python:before {\n  content: \"\\f3e2\"; }\n\n.fa-qq:before {\n  content: \"\\f1d6\"; }\n\n.fa-qrcode:before {\n  content: \"\\f029\"; }\n\n.fa-question:before {\n  content: \"\\f128\"; }\n\n.fa-question-circle:before {\n  content: \"\\f059\"; }\n\n.fa-quidditch:before {\n  content: \"\\f458\"; }\n\n.fa-quinscape:before {\n  content: \"\\f459\"; }\n\n.fa-quora:before {\n  content: \"\\f2c4\"; }\n\n.fa-quote-left:before {\n  content: \"\\f10d\"; }\n\n.fa-quote-right:before {\n  content: \"\\f10e\"; }\n\n.fa-quran:before {\n  content: \"\\f687\"; }\n\n.fa-r-project:before {\n  content: \"\\f4f7\"; }\n\n.fa-radiation:before {\n  content: \"\\f7b9\"; }\n\n.fa-radiation-alt:before {\n  content: \"\\f7ba\"; }\n\n.fa-rainbow:before {\n  content: \"\\f75b\"; }\n\n.fa-random:before {\n  content: \"\\f074\"; }\n\n.fa-raspberry-pi:before {\n  content: \"\\f7bb\"; }\n\n.fa-ravelry:before {\n  content: \"\\f2d9\"; }\n\n.fa-react:before {\n  content: \"\\f41b\"; }\n\n.fa-reacteurope:before {\n  content: \"\\f75d\"; }\n\n.fa-readme:before {\n  content: \"\\f4d5\"; }\n\n.fa-rebel:before {\n  content: \"\\f1d0\"; }\n\n.fa-receipt:before {\n  content: \"\\f543\"; }\n\n.fa-record-vinyl:before {\n  content: \"\\f8d9\"; }\n\n.fa-recycle:before {\n  content: \"\\f1b8\"; }\n\n.fa-red-river:before {\n  content: \"\\f3e3\"; }\n\n.fa-reddit:before {\n  content: \"\\f1a1\"; }\n\n.fa-reddit-alien:before {\n  content: \"\\f281\"; }\n\n.fa-reddit-square:before {\n  content: \"\\f1a2\"; }\n\n.fa-redhat:before {\n  content: \"\\f7bc\"; }\n\n.fa-redo:before {\n  content: \"\\f01e\"; }\n\n.fa-redo-alt:before {\n  content: \"\\f2f9\"; }\n\n.fa-registered:before {\n  content: \"\\f25d\"; }\n\n.fa-remove-format:before {\n  content: \"\\f87d\"; }\n\n.fa-renren:before {\n  content: \"\\f18b\"; }\n\n.fa-reply:before {\n  content: \"\\f3e5\"; }\n\n.fa-reply-all:before {\n  content: \"\\f122\"; }\n\n.fa-replyd:before {\n  content: \"\\f3e6\"; }\n\n.fa-republican:before {\n  content: \"\\f75e\"; }\n\n.fa-researchgate:before {\n  content: \"\\f4f8\"; }\n\n.fa-resolving:before {\n  content: \"\\f3e7\"; }\n\n.fa-restroom:before {\n  content: \"\\f7bd\"; }\n\n.fa-retweet:before {\n  content: \"\\f079\"; }\n\n.fa-rev:before {\n  content: \"\\f5b2\"; }\n\n.fa-ribbon:before {\n  content: \"\\f4d6\"; }\n\n.fa-ring:before {\n  content: \"\\f70b\"; }\n\n.fa-road:before {\n  content: \"\\f018\"; }\n\n.fa-robot:before {\n  content: \"\\f544\"; }\n\n.fa-rocket:before {\n  content: \"\\f135\"; }\n\n.fa-rocketchat:before {\n  content: \"\\f3e8\"; }\n\n.fa-rockrms:before {\n  content: \"\\f3e9\"; }\n\n.fa-route:before {\n  content: \"\\f4d7\"; }\n\n.fa-rss:before {\n  content: \"\\f09e\"; }\n\n.fa-rss-square:before {\n  content: \"\\f143\"; }\n\n.fa-ruble-sign:before {\n  content: \"\\f158\"; }\n\n.fa-ruler:before {\n  content: \"\\f545\"; }\n\n.fa-ruler-combined:before {\n  content: \"\\f546\"; }\n\n.fa-ruler-horizontal:before {\n  content: \"\\f547\"; }\n\n.fa-ruler-vertical:before {\n  content: \"\\f548\"; }\n\n.fa-running:before {\n  content: \"\\f70c\"; }\n\n.fa-rupee-sign:before {\n  content: \"\\f156\"; }\n\n.fa-rust:before {\n  content: \"\\e07a\"; }\n\n.fa-sad-cry:before {\n  content: \"\\f5b3\"; }\n\n.fa-sad-tear:before {\n  content: \"\\f5b4\"; }\n\n.fa-safari:before {\n  content: \"\\f267\"; }\n\n.fa-salesforce:before {\n  content: \"\\f83b\"; }\n\n.fa-sass:before {\n  content: \"\\f41e\"; }\n\n.fa-satellite:before {\n  content: \"\\f7bf\"; }\n\n.fa-satellite-dish:before {\n  content: \"\\f7c0\"; }\n\n.fa-save:before {\n  content: \"\\f0c7\"; }\n\n.fa-schlix:before {\n  content: \"\\f3ea\"; }\n\n.fa-school:before {\n  content: \"\\f549\"; }\n\n.fa-screwdriver:before {\n  content: \"\\f54a\"; }\n\n.fa-scribd:before {\n  content: \"\\f28a\"; }\n\n.fa-scroll:before {\n  content: \"\\f70e\"; }\n\n.fa-sd-card:before {\n  content: \"\\f7c2\"; }\n\n.fa-search:before {\n  content: \"\\f002\"; }\n\n.fa-search-dollar:before {\n  content: \"\\f688\"; }\n\n.fa-search-location:before {\n  content: \"\\f689\"; }\n\n.fa-search-minus:before {\n  content: \"\\f010\"; }\n\n.fa-search-plus:before {\n  content: \"\\f00e\"; }\n\n.fa-searchengin:before {\n  content: \"\\f3eb\"; }\n\n.fa-seedling:before {\n  content: \"\\f4d8\"; }\n\n.fa-sellcast:before {\n  content: \"\\f2da\"; }\n\n.fa-sellsy:before {\n  content: \"\\f213\"; }\n\n.fa-server:before {\n  content: \"\\f233\"; }\n\n.fa-servicestack:before {\n  content: \"\\f3ec\"; }\n\n.fa-shapes:before {\n  content: \"\\f61f\"; }\n\n.fa-share:before {\n  content: \"\\f064\"; }\n\n.fa-share-alt:before {\n  content: \"\\f1e0\"; }\n\n.fa-share-alt-square:before {\n  content: \"\\f1e1\"; }\n\n.fa-share-square:before {\n  content: \"\\f14d\"; }\n\n.fa-shekel-sign:before {\n  content: \"\\f20b\"; }\n\n.fa-shield-alt:before {\n  content: \"\\f3ed\"; }\n\n.fa-shield-virus:before {\n  content: \"\\e06c\"; }\n\n.fa-ship:before {\n  content: \"\\f21a\"; }\n\n.fa-shipping-fast:before {\n  content: \"\\f48b\"; }\n\n.fa-shirtsinbulk:before {\n  content: \"\\f214\"; }\n\n.fa-shoe-prints:before {\n  content: \"\\f54b\"; }\n\n.fa-shopify:before {\n  content: \"\\e057\"; }\n\n.fa-shopping-bag:before {\n  content: \"\\f290\"; }\n\n.fa-shopping-basket:before {\n  content: \"\\f291\"; }\n\n.fa-shopping-cart:before {\n  content: \"\\f07a\"; }\n\n.fa-shopware:before {\n  content: \"\\f5b5\"; }\n\n.fa-shower:before {\n  content: \"\\f2cc\"; }\n\n.fa-shuttle-van:before {\n  content: \"\\f5b6\"; }\n\n.fa-sign:before {\n  content: \"\\f4d9\"; }\n\n.fa-sign-in-alt:before {\n  content: \"\\f2f6\"; }\n\n.fa-sign-language:before {\n  content: \"\\f2a7\"; }\n\n.fa-sign-out-alt:before {\n  content: \"\\f2f5\"; }\n\n.fa-signal:before {\n  content: \"\\f012\"; }\n\n.fa-signature:before {\n  content: \"\\f5b7\"; }\n\n.fa-sim-card:before {\n  content: \"\\f7c4\"; }\n\n.fa-simplybuilt:before {\n  content: \"\\f215\"; }\n\n.fa-sink:before {\n  content: \"\\e06d\"; }\n\n.fa-sistrix:before {\n  content: \"\\f3ee\"; }\n\n.fa-sitemap:before {\n  content: \"\\f0e8\"; }\n\n.fa-sith:before {\n  content: \"\\f512\"; }\n\n.fa-skating:before {\n  content: \"\\f7c5\"; }\n\n.fa-sketch:before {\n  content: \"\\f7c6\"; }\n\n.fa-skiing:before {\n  content: \"\\f7c9\"; }\n\n.fa-skiing-nordic:before {\n  content: \"\\f7ca\"; }\n\n.fa-skull:before {\n  content: \"\\f54c\"; }\n\n.fa-skull-crossbones:before {\n  content: \"\\f714\"; }\n\n.fa-skyatlas:before {\n  content: \"\\f216\"; }\n\n.fa-skype:before {\n  content: \"\\f17e\"; }\n\n.fa-slack:before {\n  content: \"\\f198\"; }\n\n.fa-slack-hash:before {\n  content: \"\\f3ef\"; }\n\n.fa-slash:before {\n  content: \"\\f715\"; }\n\n.fa-sleigh:before {\n  content: \"\\f7cc\"; }\n\n.fa-sliders-h:before {\n  content: \"\\f1de\"; }\n\n.fa-slideshare:before {\n  content: \"\\f1e7\"; }\n\n.fa-smile:before {\n  content: \"\\f118\"; }\n\n.fa-smile-beam:before {\n  content: \"\\f5b8\"; }\n\n.fa-smile-wink:before {\n  content: \"\\f4da\"; }\n\n.fa-smog:before {\n  content: \"\\f75f\"; }\n\n.fa-smoking:before {\n  content: \"\\f48d\"; }\n\n.fa-smoking-ban:before {\n  content: \"\\f54d\"; }\n\n.fa-sms:before {\n  content: \"\\f7cd\"; }\n\n.fa-snapchat:before {\n  content: \"\\f2ab\"; }\n\n.fa-snapchat-ghost:before {\n  content: \"\\f2ac\"; }\n\n.fa-snapchat-square:before {\n  content: \"\\f2ad\"; }\n\n.fa-snowboarding:before {\n  content: \"\\f7ce\"; }\n\n.fa-snowflake:before {\n  content: \"\\f2dc\"; }\n\n.fa-snowman:before {\n  content: \"\\f7d0\"; }\n\n.fa-snowplow:before {\n  content: \"\\f7d2\"; }\n\n.fa-soap:before {\n  content: \"\\e06e\"; }\n\n.fa-socks:before {\n  content: \"\\f696\"; }\n\n.fa-solar-panel:before {\n  content: \"\\f5ba\"; }\n\n.fa-sort:before {\n  content: \"\\f0dc\"; }\n\n.fa-sort-alpha-down:before {\n  content: \"\\f15d\"; }\n\n.fa-sort-alpha-down-alt:before {\n  content: \"\\f881\"; }\n\n.fa-sort-alpha-up:before {\n  content: \"\\f15e\"; }\n\n.fa-sort-alpha-up-alt:before {\n  content: \"\\f882\"; }\n\n.fa-sort-amount-down:before {\n  content: \"\\f160\"; }\n\n.fa-sort-amount-down-alt:before {\n  content: \"\\f884\"; }\n\n.fa-sort-amount-up:before {\n  content: \"\\f161\"; }\n\n.fa-sort-amount-up-alt:before {\n  content: \"\\f885\"; }\n\n.fa-sort-down:before {\n  content: \"\\f0dd\"; }\n\n.fa-sort-numeric-down:before {\n  content: \"\\f162\"; }\n\n.fa-sort-numeric-down-alt:before {\n  content: \"\\f886\"; }\n\n.fa-sort-numeric-up:before {\n  content: \"\\f163\"; }\n\n.fa-sort-numeric-up-alt:before {\n  content: \"\\f887\"; }\n\n.fa-sort-up:before {\n  content: \"\\f0de\"; }\n\n.fa-soundcloud:before {\n  content: \"\\f1be\"; }\n\n.fa-sourcetree:before {\n  content: \"\\f7d3\"; }\n\n.fa-spa:before {\n  content: \"\\f5bb\"; }\n\n.fa-space-shuttle:before {\n  content: \"\\f197\"; }\n\n.fa-speakap:before {\n  content: \"\\f3f3\"; }\n\n.fa-speaker-deck:before {\n  content: \"\\f83c\"; }\n\n.fa-spell-check:before {\n  content: \"\\f891\"; }\n\n.fa-spider:before {\n  content: \"\\f717\"; }\n\n.fa-spinner:before {\n  content: \"\\f110\"; }\n\n.fa-splotch:before {\n  content: \"\\f5bc\"; }\n\n.fa-spotify:before {\n  content: \"\\f1bc\"; }\n\n.fa-spray-can:before {\n  content: \"\\f5bd\"; }\n\n.fa-square:before {\n  content: \"\\f0c8\"; }\n\n.fa-square-full:before {\n  content: \"\\f45c\"; }\n\n.fa-square-root-alt:before {\n  content: \"\\f698\"; }\n\n.fa-squarespace:before {\n  content: \"\\f5be\"; }\n\n.fa-stack-exchange:before {\n  content: \"\\f18d\"; }\n\n.fa-stack-overflow:before {\n  content: \"\\f16c\"; }\n\n.fa-stackpath:before {\n  content: \"\\f842\"; }\n\n.fa-stamp:before {\n  content: \"\\f5bf\"; }\n\n.fa-star:before {\n  content: \"\\f005\"; }\n\n.fa-star-and-crescent:before {\n  content: \"\\f699\"; }\n\n.fa-star-half:before {\n  content: \"\\f089\"; }\n\n.fa-star-half-alt:before {\n  content: \"\\f5c0\"; }\n\n.fa-star-of-david:before {\n  content: \"\\f69a\"; }\n\n.fa-star-of-life:before {\n  content: \"\\f621\"; }\n\n.fa-staylinked:before {\n  content: \"\\f3f5\"; }\n\n.fa-steam:before {\n  content: \"\\f1b6\"; }\n\n.fa-steam-square:before {\n  content: \"\\f1b7\"; }\n\n.fa-steam-symbol:before {\n  content: \"\\f3f6\"; }\n\n.fa-step-backward:before {\n  content: \"\\f048\"; }\n\n.fa-step-forward:before {\n  content: \"\\f051\"; }\n\n.fa-stethoscope:before {\n  content: \"\\f0f1\"; }\n\n.fa-sticker-mule:before {\n  content: \"\\f3f7\"; }\n\n.fa-sticky-note:before {\n  content: \"\\f249\"; }\n\n.fa-stop:before {\n  content: \"\\f04d\"; }\n\n.fa-stop-circle:before {\n  content: \"\\f28d\"; }\n\n.fa-stopwatch:before {\n  content: \"\\f2f2\"; }\n\n.fa-stopwatch-20:before {\n  content: \"\\e06f\"; }\n\n.fa-store:before {\n  content: \"\\f54e\"; }\n\n.fa-store-alt:before {\n  content: \"\\f54f\"; }\n\n.fa-store-alt-slash:before {\n  content: \"\\e070\"; }\n\n.fa-store-slash:before {\n  content: \"\\e071\"; }\n\n.fa-strava:before {\n  content: \"\\f428\"; }\n\n.fa-stream:before {\n  content: \"\\f550\"; }\n\n.fa-street-view:before {\n  content: \"\\f21d\"; }\n\n.fa-strikethrough:before {\n  content: \"\\f0cc\"; }\n\n.fa-stripe:before {\n  content: \"\\f429\"; }\n\n.fa-stripe-s:before {\n  content: \"\\f42a\"; }\n\n.fa-stroopwafel:before {\n  content: \"\\f551\"; }\n\n.fa-studiovinari:before {\n  content: \"\\f3f8\"; }\n\n.fa-stumbleupon:before {\n  content: \"\\f1a4\"; }\n\n.fa-stumbleupon-circle:before {\n  content: \"\\f1a3\"; }\n\n.fa-subscript:before {\n  content: \"\\f12c\"; }\n\n.fa-subway:before {\n  content: \"\\f239\"; }\n\n.fa-suitcase:before {\n  content: \"\\f0f2\"; }\n\n.fa-suitcase-rolling:before {\n  content: \"\\f5c1\"; }\n\n.fa-sun:before {\n  content: \"\\f185\"; }\n\n.fa-superpowers:before {\n  content: \"\\f2dd\"; }\n\n.fa-superscript:before {\n  content: \"\\f12b\"; }\n\n.fa-supple:before {\n  content: \"\\f3f9\"; }\n\n.fa-surprise:before {\n  content: \"\\f5c2\"; }\n\n.fa-suse:before {\n  content: \"\\f7d6\"; }\n\n.fa-swatchbook:before {\n  content: \"\\f5c3\"; }\n\n.fa-swift:before {\n  content: \"\\f8e1\"; }\n\n.fa-swimmer:before {\n  content: \"\\f5c4\"; }\n\n.fa-swimming-pool:before {\n  content: \"\\f5c5\"; }\n\n.fa-symfony:before {\n  content: \"\\f83d\"; }\n\n.fa-synagogue:before {\n  content: \"\\f69b\"; }\n\n.fa-sync:before {\n  content: \"\\f021\"; }\n\n.fa-sync-alt:before {\n  content: \"\\f2f1\"; }\n\n.fa-syringe:before {\n  content: \"\\f48e\"; }\n\n.fa-table:before {\n  content: \"\\f0ce\"; }\n\n.fa-table-tennis:before {\n  content: \"\\f45d\"; }\n\n.fa-tablet:before {\n  content: \"\\f10a\"; }\n\n.fa-tablet-alt:before {\n  content: \"\\f3fa\"; }\n\n.fa-tablets:before {\n  content: \"\\f490\"; }\n\n.fa-tachometer-alt:before {\n  content: \"\\f3fd\"; }\n\n.fa-tag:before {\n  content: \"\\f02b\"; }\n\n.fa-tags:before {\n  content: \"\\f02c\"; }\n\n.fa-tape:before {\n  content: \"\\f4db\"; }\n\n.fa-tasks:before {\n  content: \"\\f0ae\"; }\n\n.fa-taxi:before {\n  content: \"\\f1ba\"; }\n\n.fa-teamspeak:before {\n  content: \"\\f4f9\"; }\n\n.fa-teeth:before {\n  content: \"\\f62e\"; }\n\n.fa-teeth-open:before {\n  content: \"\\f62f\"; }\n\n.fa-telegram:before {\n  content: \"\\f2c6\"; }\n\n.fa-telegram-plane:before {\n  content: \"\\f3fe\"; }\n\n.fa-temperature-high:before {\n  content: \"\\f769\"; }\n\n.fa-temperature-low:before {\n  content: \"\\f76b\"; }\n\n.fa-tencent-weibo:before {\n  content: \"\\f1d5\"; }\n\n.fa-tenge:before {\n  content: \"\\f7d7\"; }\n\n.fa-terminal:before {\n  content: \"\\f120\"; }\n\n.fa-text-height:before {\n  content: \"\\f034\"; }\n\n.fa-text-width:before {\n  content: \"\\f035\"; }\n\n.fa-th:before {\n  content: \"\\f00a\"; }\n\n.fa-th-large:before {\n  content: \"\\f009\"; }\n\n.fa-th-list:before {\n  content: \"\\f00b\"; }\n\n.fa-the-red-yeti:before {\n  content: \"\\f69d\"; }\n\n.fa-theater-masks:before {\n  content: \"\\f630\"; }\n\n.fa-themeco:before {\n  content: \"\\f5c6\"; }\n\n.fa-themeisle:before {\n  content: \"\\f2b2\"; }\n\n.fa-thermometer:before {\n  content: \"\\f491\"; }\n\n.fa-thermometer-empty:before {\n  content: \"\\f2cb\"; }\n\n.fa-thermometer-full:before {\n  content: \"\\f2c7\"; }\n\n.fa-thermometer-half:before {\n  content: \"\\f2c9\"; }\n\n.fa-thermometer-quarter:before {\n  content: \"\\f2ca\"; }\n\n.fa-thermometer-three-quarters:before {\n  content: \"\\f2c8\"; }\n\n.fa-think-peaks:before {\n  content: \"\\f731\"; }\n\n.fa-thumbs-down:before {\n  content: \"\\f165\"; }\n\n.fa-thumbs-up:before {\n  content: \"\\f164\"; }\n\n.fa-thumbtack:before {\n  content: \"\\f08d\"; }\n\n.fa-ticket-alt:before {\n  content: \"\\f3ff\"; }\n\n.fa-tiktok:before {\n  content: \"\\e07b\"; }\n\n.fa-times:before {\n  content: \"\\f00d\"; }\n\n.fa-times-circle:before {\n  content: \"\\f057\"; }\n\n.fa-tint:before {\n  content: \"\\f043\"; }\n\n.fa-tint-slash:before {\n  content: \"\\f5c7\"; }\n\n.fa-tired:before {\n  content: \"\\f5c8\"; }\n\n.fa-toggle-off:before {\n  content: \"\\f204\"; }\n\n.fa-toggle-on:before {\n  content: \"\\f205\"; }\n\n.fa-toilet:before {\n  content: \"\\f7d8\"; }\n\n.fa-toilet-paper:before {\n  content: \"\\f71e\"; }\n\n.fa-toilet-paper-slash:before {\n  content: \"\\e072\"; }\n\n.fa-toolbox:before {\n  content: \"\\f552\"; }\n\n.fa-tools:before {\n  content: \"\\f7d9\"; }\n\n.fa-tooth:before {\n  content: \"\\f5c9\"; }\n\n.fa-torah:before {\n  content: \"\\f6a0\"; }\n\n.fa-torii-gate:before {\n  content: \"\\f6a1\"; }\n\n.fa-tractor:before {\n  content: \"\\f722\"; }\n\n.fa-trade-federation:before {\n  content: \"\\f513\"; }\n\n.fa-trademark:before {\n  content: \"\\f25c\"; }\n\n.fa-traffic-light:before {\n  content: \"\\f637\"; }\n\n.fa-trailer:before {\n  content: \"\\e041\"; }\n\n.fa-train:before {\n  content: \"\\f238\"; }\n\n.fa-tram:before {\n  content: \"\\f7da\"; }\n\n.fa-transgender:before {\n  content: \"\\f224\"; }\n\n.fa-transgender-alt:before {\n  content: \"\\f225\"; }\n\n.fa-trash:before {\n  content: \"\\f1f8\"; }\n\n.fa-trash-alt:before {\n  content: \"\\f2ed\"; }\n\n.fa-trash-restore:before {\n  content: \"\\f829\"; }\n\n.fa-trash-restore-alt:before {\n  content: \"\\f82a\"; }\n\n.fa-tree:before {\n  content: \"\\f1bb\"; }\n\n.fa-trello:before {\n  content: \"\\f181\"; }\n\n.fa-trophy:before {\n  content: \"\\f091\"; }\n\n.fa-truck:before {\n  content: \"\\f0d1\"; }\n\n.fa-truck-loading:before {\n  content: \"\\f4de\"; }\n\n.fa-truck-monster:before {\n  content: \"\\f63b\"; }\n\n.fa-truck-moving:before {\n  content: \"\\f4df\"; }\n\n.fa-truck-pickup:before {\n  content: \"\\f63c\"; }\n\n.fa-tshirt:before {\n  content: \"\\f553\"; }\n\n.fa-tty:before {\n  content: \"\\f1e4\"; }\n\n.fa-tumblr:before {\n  content: \"\\f173\"; }\n\n.fa-tumblr-square:before {\n  content: \"\\f174\"; }\n\n.fa-tv:before {\n  content: \"\\f26c\"; }\n\n.fa-twitch:before {\n  content: \"\\f1e8\"; }\n\n.fa-twitter:before {\n  content: \"\\f099\"; }\n\n.fa-twitter-square:before {\n  content: \"\\f081\"; }\n\n.fa-typo3:before {\n  content: \"\\f42b\"; }\n\n.fa-uber:before {\n  content: \"\\f402\"; }\n\n.fa-ubuntu:before {\n  content: \"\\f7df\"; }\n\n.fa-uikit:before {\n  content: \"\\f403\"; }\n\n.fa-umbraco:before {\n  content: \"\\f8e8\"; }\n\n.fa-umbrella:before {\n  content: \"\\f0e9\"; }\n\n.fa-umbrella-beach:before {\n  content: \"\\f5ca\"; }\n\n.fa-uncharted:before {\n  content: \"\\e084\"; }\n\n.fa-underline:before {\n  content: \"\\f0cd\"; }\n\n.fa-undo:before {\n  content: \"\\f0e2\"; }\n\n.fa-undo-alt:before {\n  content: \"\\f2ea\"; }\n\n.fa-uniregistry:before {\n  content: \"\\f404\"; }\n\n.fa-unity:before {\n  content: \"\\e049\"; }\n\n.fa-universal-access:before {\n  content: \"\\f29a\"; }\n\n.fa-university:before {\n  content: \"\\f19c\"; }\n\n.fa-unlink:before {\n  content: \"\\f127\"; }\n\n.fa-unlock:before {\n  content: \"\\f09c\"; }\n\n.fa-unlock-alt:before {\n  content: \"\\f13e\"; }\n\n.fa-unsplash:before {\n  content: \"\\e07c\"; }\n\n.fa-untappd:before {\n  content: \"\\f405\"; }\n\n.fa-upload:before {\n  content: \"\\f093\"; }\n\n.fa-ups:before {\n  content: \"\\f7e0\"; }\n\n.fa-usb:before {\n  content: \"\\f287\"; }\n\n.fa-user:before {\n  content: \"\\f007\"; }\n\n.fa-user-alt:before {\n  content: \"\\f406\"; }\n\n.fa-user-alt-slash:before {\n  content: \"\\f4fa\"; }\n\n.fa-user-astronaut:before {\n  content: \"\\f4fb\"; }\n\n.fa-user-check:before {\n  content: \"\\f4fc\"; }\n\n.fa-user-circle:before {\n  content: \"\\f2bd\"; }\n\n.fa-user-clock:before {\n  content: \"\\f4fd\"; }\n\n.fa-user-cog:before {\n  content: \"\\f4fe\"; }\n\n.fa-user-edit:before {\n  content: \"\\f4ff\"; }\n\n.fa-user-friends:before {\n  content: \"\\f500\"; }\n\n.fa-user-graduate:before {\n  content: \"\\f501\"; }\n\n.fa-user-injured:before {\n  content: \"\\f728\"; }\n\n.fa-user-lock:before {\n  content: \"\\f502\"; }\n\n.fa-user-md:before {\n  content: \"\\f0f0\"; }\n\n.fa-user-minus:before {\n  content: \"\\f503\"; }\n\n.fa-user-ninja:before {\n  content: \"\\f504\"; }\n\n.fa-user-nurse:before {\n  content: \"\\f82f\"; }\n\n.fa-user-plus:before {\n  content: \"\\f234\"; }\n\n.fa-user-secret:before {\n  content: \"\\f21b\"; }\n\n.fa-user-shield:before {\n  content: \"\\f505\"; }\n\n.fa-user-slash:before {\n  content: \"\\f506\"; }\n\n.fa-user-tag:before {\n  content: \"\\f507\"; }\n\n.fa-user-tie:before {\n  content: \"\\f508\"; }\n\n.fa-user-times:before {\n  content: \"\\f235\"; }\n\n.fa-users:before {\n  content: \"\\f0c0\"; }\n\n.fa-users-cog:before {\n  content: \"\\f509\"; }\n\n.fa-users-slash:before {\n  content: \"\\e073\"; }\n\n.fa-usps:before {\n  content: \"\\f7e1\"; }\n\n.fa-ussunnah:before {\n  content: \"\\f407\"; }\n\n.fa-utensil-spoon:before {\n  content: \"\\f2e5\"; }\n\n.fa-utensils:before {\n  content: \"\\f2e7\"; }\n\n.fa-vaadin:before {\n  content: \"\\f408\"; }\n\n.fa-vector-square:before {\n  content: \"\\f5cb\"; }\n\n.fa-venus:before {\n  content: \"\\f221\"; }\n\n.fa-venus-double:before {\n  content: \"\\f226\"; }\n\n.fa-venus-mars:before {\n  content: \"\\f228\"; }\n\n.fa-vest:before {\n  content: \"\\e085\"; }\n\n.fa-vest-patches:before {\n  content: \"\\e086\"; }\n\n.fa-viacoin:before {\n  content: \"\\f237\"; }\n\n.fa-viadeo:before {\n  content: \"\\f2a9\"; }\n\n.fa-viadeo-square:before {\n  content: \"\\f2aa\"; }\n\n.fa-vial:before {\n  content: \"\\f492\"; }\n\n.fa-vials:before {\n  content: \"\\f493\"; }\n\n.fa-viber:before {\n  content: \"\\f409\"; }\n\n.fa-video:before {\n  content: \"\\f03d\"; }\n\n.fa-video-slash:before {\n  content: \"\\f4e2\"; }\n\n.fa-vihara:before {\n  content: \"\\f6a7\"; }\n\n.fa-vimeo:before {\n  content: \"\\f40a\"; }\n\n.fa-vimeo-square:before {\n  content: \"\\f194\"; }\n\n.fa-vimeo-v:before {\n  content: \"\\f27d\"; }\n\n.fa-vine:before {\n  content: \"\\f1ca\"; }\n\n.fa-virus:before {\n  content: \"\\e074\"; }\n\n.fa-virus-slash:before {\n  content: \"\\e075\"; }\n\n.fa-viruses:before {\n  content: \"\\e076\"; }\n\n.fa-vk:before {\n  content: \"\\f189\"; }\n\n.fa-vnv:before {\n  content: \"\\f40b\"; }\n\n.fa-voicemail:before {\n  content: \"\\f897\"; }\n\n.fa-volleyball-ball:before {\n  content: \"\\f45f\"; }\n\n.fa-volume-down:before {\n  content: \"\\f027\"; }\n\n.fa-volume-mute:before {\n  content: \"\\f6a9\"; }\n\n.fa-volume-off:before {\n  content: \"\\f026\"; }\n\n.fa-volume-up:before {\n  content: \"\\f028\"; }\n\n.fa-vote-yea:before {\n  content: \"\\f772\"; }\n\n.fa-vr-cardboard:before {\n  content: \"\\f729\"; }\n\n.fa-vuejs:before {\n  content: \"\\f41f\"; }\n\n.fa-walking:before {\n  content: \"\\f554\"; }\n\n.fa-wallet:before {\n  content: \"\\f555\"; }\n\n.fa-warehouse:before {\n  content: \"\\f494\"; }\n\n.fa-watchman-monitoring:before {\n  content: \"\\e087\"; }\n\n.fa-water:before {\n  content: \"\\f773\"; }\n\n.fa-wave-square:before {\n  content: \"\\f83e\"; }\n\n.fa-waze:before {\n  content: \"\\f83f\"; }\n\n.fa-weebly:before {\n  content: \"\\f5cc\"; }\n\n.fa-weibo:before {\n  content: \"\\f18a\"; }\n\n.fa-weight:before {\n  content: \"\\f496\"; }\n\n.fa-weight-hanging:before {\n  content: \"\\f5cd\"; }\n\n.fa-weixin:before {\n  content: \"\\f1d7\"; }\n\n.fa-whatsapp:before {\n  content: \"\\f232\"; }\n\n.fa-whatsapp-square:before {\n  content: \"\\f40c\"; }\n\n.fa-wheelchair:before {\n  content: \"\\f193\"; }\n\n.fa-whmcs:before {\n  content: \"\\f40d\"; }\n\n.fa-wifi:before {\n  content: \"\\f1eb\"; }\n\n.fa-wikipedia-w:before {\n  content: \"\\f266\"; }\n\n.fa-wind:before {\n  content: \"\\f72e\"; }\n\n.fa-window-close:before {\n  content: \"\\f410\"; }\n\n.fa-window-maximize:before {\n  content: \"\\f2d0\"; }\n\n.fa-window-minimize:before {\n  content: \"\\f2d1\"; }\n\n.fa-window-restore:before {\n  content: \"\\f2d2\"; }\n\n.fa-windows:before {\n  content: \"\\f17a\"; }\n\n.fa-wine-bottle:before {\n  content: \"\\f72f\"; }\n\n.fa-wine-glass:before {\n  content: \"\\f4e3\"; }\n\n.fa-wine-glass-alt:before {\n  content: \"\\f5ce\"; }\n\n.fa-wix:before {\n  content: \"\\f5cf\"; }\n\n.fa-wizards-of-the-coast:before {\n  content: \"\\f730\"; }\n\n.fa-wodu:before {\n  content: \"\\e088\"; }\n\n.fa-wolf-pack-battalion:before {\n  content: \"\\f514\"; }\n\n.fa-won-sign:before {\n  content: \"\\f159\"; }\n\n.fa-wordpress:before {\n  content: \"\\f19a\"; }\n\n.fa-wordpress-simple:before {\n  content: \"\\f411\"; }\n\n.fa-wpbeginner:before {\n  content: \"\\f297\"; }\n\n.fa-wpexplorer:before {\n  content: \"\\f2de\"; }\n\n.fa-wpforms:before {\n  content: \"\\f298\"; }\n\n.fa-wpressr:before {\n  content: \"\\f3e4\"; }\n\n.fa-wrench:before {\n  content: \"\\f0ad\"; }\n\n.fa-x-ray:before {\n  content: \"\\f497\"; }\n\n.fa-xbox:before {\n  content: \"\\f412\"; }\n\n.fa-xing:before {\n  content: \"\\f168\"; }\n\n.fa-xing-square:before {\n  content: \"\\f169\"; }\n\n.fa-y-combinator:before {\n  content: \"\\f23b\"; }\n\n.fa-yahoo:before {\n  content: \"\\f19e\"; }\n\n.fa-yammer:before {\n  content: \"\\f840\"; }\n\n.fa-yandex:before {\n  content: \"\\f413\"; }\n\n.fa-yandex-international:before {\n  content: \"\\f414\"; }\n\n.fa-yarn:before {\n  content: \"\\f7e3\"; }\n\n.fa-yelp:before {\n  content: \"\\f1e9\"; }\n\n.fa-yen-sign:before {\n  content: \"\\f157\"; }\n\n.fa-yin-yang:before {\n  content: \"\\f6ad\"; }\n\n.fa-yoast:before {\n  content: \"\\f2b1\"; }\n\n.fa-youtube:before {\n  content: \"\\f167\"; }\n\n.fa-youtube-square:before {\n  content: \"\\f431\"; }\n\n.fa-zhihu:before {\n  content: \"\\f63f\"; }\n\n.sr-only {\n  border: 0;\n  clip: rect(0, 0, 0, 0);\n  height: 1px;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0;\n  position: absolute;\n  width: 1px; }\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n  clip: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  position: static;\n  width: auto; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/fontawesome-free/css/regular.css",
    "content": "/*!\n * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com\n * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n */\n@font-face {\n  font-family: 'Font Awesome 5 Free';\n  font-style: normal;\n  font-weight: 400;\n  font-display: block;\n  src: url(\"../webfonts/fa-regular-400.eot\");\n  src: url(\"../webfonts/fa-regular-400.eot?#iefix\") format(\"embedded-opentype\"), url(\"../webfonts/fa-regular-400.woff2\") format(\"woff2\"), url(\"../webfonts/fa-regular-400.woff\") format(\"woff\"), url(\"../webfonts/fa-regular-400.ttf\") format(\"truetype\"), url(\"../webfonts/fa-regular-400.svg#fontawesome\") format(\"svg\"); }\n\n.far {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/fontawesome-free/css/solid.css",
    "content": "/*!\n * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com\n * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n */\n@font-face {\n  font-family: 'Font Awesome 5 Free';\n  font-style: normal;\n  font-weight: 900;\n  font-display: block;\n  src: url(\"../webfonts/fa-solid-900.eot\");\n  src: url(\"../webfonts/fa-solid-900.eot?#iefix\") format(\"embedded-opentype\"), url(\"../webfonts/fa-solid-900.woff2\") format(\"woff2\"), url(\"../webfonts/fa-solid-900.woff\") format(\"woff\"), url(\"../webfonts/fa-solid-900.ttf\") format(\"truetype\"), url(\"../webfonts/fa-solid-900.svg#fontawesome\") format(\"svg\"); }\n\n.fa,\n.fas {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 900; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/fontawesome-free/css/svg-with-js.css",
    "content": "/*!\n * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com\n * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n */\nsvg:not(:root).svg-inline--fa {\n  overflow: visible; }\n\n.svg-inline--fa {\n  display: inline-block;\n  font-size: inherit;\n  height: 1em;\n  overflow: visible;\n  vertical-align: -.125em; }\n  .svg-inline--fa.fa-lg {\n    vertical-align: -.225em; }\n  .svg-inline--fa.fa-w-1 {\n    width: 0.0625em; }\n  .svg-inline--fa.fa-w-2 {\n    width: 0.125em; }\n  .svg-inline--fa.fa-w-3 {\n    width: 0.1875em; }\n  .svg-inline--fa.fa-w-4 {\n    width: 0.25em; }\n  .svg-inline--fa.fa-w-5 {\n    width: 0.3125em; }\n  .svg-inline--fa.fa-w-6 {\n    width: 0.375em; }\n  .svg-inline--fa.fa-w-7 {\n    width: 0.4375em; }\n  .svg-inline--fa.fa-w-8 {\n    width: 0.5em; }\n  .svg-inline--fa.fa-w-9 {\n    width: 0.5625em; }\n  .svg-inline--fa.fa-w-10 {\n    width: 0.625em; }\n  .svg-inline--fa.fa-w-11 {\n    width: 0.6875em; }\n  .svg-inline--fa.fa-w-12 {\n    width: 0.75em; }\n  .svg-inline--fa.fa-w-13 {\n    width: 0.8125em; }\n  .svg-inline--fa.fa-w-14 {\n    width: 0.875em; }\n  .svg-inline--fa.fa-w-15 {\n    width: 0.9375em; }\n  .svg-inline--fa.fa-w-16 {\n    width: 1em; }\n  .svg-inline--fa.fa-w-17 {\n    width: 1.0625em; }\n  .svg-inline--fa.fa-w-18 {\n    width: 1.125em; }\n  .svg-inline--fa.fa-w-19 {\n    width: 1.1875em; }\n  .svg-inline--fa.fa-w-20 {\n    width: 1.25em; }\n  .svg-inline--fa.fa-pull-left {\n    margin-right: .3em;\n    width: auto; }\n  .svg-inline--fa.fa-pull-right {\n    margin-left: .3em;\n    width: auto; }\n  .svg-inline--fa.fa-border {\n    height: 1.5em; }\n  .svg-inline--fa.fa-li {\n    width: 2em; }\n  .svg-inline--fa.fa-fw {\n    width: 1.25em; }\n\n.fa-layers svg.svg-inline--fa {\n  bottom: 0;\n  left: 0;\n  margin: auto;\n  position: absolute;\n  right: 0;\n  top: 0; }\n\n.fa-layers {\n  display: inline-block;\n  height: 1em;\n  position: relative;\n  text-align: center;\n  vertical-align: -.125em;\n  width: 1em; }\n  .fa-layers svg.svg-inline--fa {\n    -webkit-transform-origin: center center;\n            transform-origin: center center; }\n\n.fa-layers-text, .fa-layers-counter {\n  display: inline-block;\n  position: absolute;\n  text-align: center; }\n\n.fa-layers-text {\n  left: 50%;\n  top: 50%;\n  -webkit-transform: translate(-50%, -50%);\n          transform: translate(-50%, -50%);\n  -webkit-transform-origin: center center;\n          transform-origin: center center; }\n\n.fa-layers-counter {\n  background-color: #ff253a;\n  border-radius: 1em;\n  -webkit-box-sizing: border-box;\n          box-sizing: border-box;\n  color: #fff;\n  height: 1.5em;\n  line-height: 1;\n  max-width: 5em;\n  min-width: 1.5em;\n  overflow: hidden;\n  padding: .25em;\n  right: 0;\n  text-overflow: ellipsis;\n  top: 0;\n  -webkit-transform: scale(0.25);\n          transform: scale(0.25);\n  -webkit-transform-origin: top right;\n          transform-origin: top right; }\n\n.fa-layers-bottom-right {\n  bottom: 0;\n  right: 0;\n  top: auto;\n  -webkit-transform: scale(0.25);\n          transform: scale(0.25);\n  -webkit-transform-origin: bottom right;\n          transform-origin: bottom right; }\n\n.fa-layers-bottom-left {\n  bottom: 0;\n  left: 0;\n  right: auto;\n  top: auto;\n  -webkit-transform: scale(0.25);\n          transform: scale(0.25);\n  -webkit-transform-origin: bottom left;\n          transform-origin: bottom left; }\n\n.fa-layers-top-right {\n  right: 0;\n  top: 0;\n  -webkit-transform: scale(0.25);\n          transform: scale(0.25);\n  -webkit-transform-origin: top right;\n          transform-origin: top right; }\n\n.fa-layers-top-left {\n  left: 0;\n  right: auto;\n  top: 0;\n  -webkit-transform: scale(0.25);\n          transform: scale(0.25);\n  -webkit-transform-origin: top left;\n          transform-origin: top left; }\n\n.fa-lg {\n  font-size: 1.33333em;\n  line-height: 0.75em;\n  vertical-align: -.0667em; }\n\n.fa-xs {\n  font-size: .75em; }\n\n.fa-sm {\n  font-size: .875em; }\n\n.fa-1x {\n  font-size: 1em; }\n\n.fa-2x {\n  font-size: 2em; }\n\n.fa-3x {\n  font-size: 3em; }\n\n.fa-4x {\n  font-size: 4em; }\n\n.fa-5x {\n  font-size: 5em; }\n\n.fa-6x {\n  font-size: 6em; }\n\n.fa-7x {\n  font-size: 7em; }\n\n.fa-8x {\n  font-size: 8em; }\n\n.fa-9x {\n  font-size: 9em; }\n\n.fa-10x {\n  font-size: 10em; }\n\n.fa-fw {\n  text-align: center;\n  width: 1.25em; }\n\n.fa-ul {\n  list-style-type: none;\n  margin-left: 2.5em;\n  padding-left: 0; }\n  .fa-ul > li {\n    position: relative; }\n\n.fa-li {\n  left: -2em;\n  position: absolute;\n  text-align: center;\n  width: 2em;\n  line-height: inherit; }\n\n.fa-border {\n  border: solid 0.08em #eee;\n  border-radius: .1em;\n  padding: .2em .25em .15em; }\n\n.fa-pull-left {\n  float: left; }\n\n.fa-pull-right {\n  float: right; }\n\n.fa.fa-pull-left,\n.fas.fa-pull-left,\n.far.fa-pull-left,\n.fal.fa-pull-left,\n.fab.fa-pull-left {\n  margin-right: .3em; }\n\n.fa.fa-pull-right,\n.fas.fa-pull-right,\n.far.fa-pull-right,\n.fal.fa-pull-right,\n.fab.fa-pull-right {\n  margin-left: .3em; }\n\n.fa-spin {\n  -webkit-animation: fa-spin 2s infinite linear;\n          animation: fa-spin 2s infinite linear; }\n\n.fa-pulse {\n  -webkit-animation: fa-spin 1s infinite steps(8);\n          animation: fa-spin 1s infinite steps(8); }\n\n@-webkit-keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg); }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg); } }\n\n@keyframes fa-spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n            transform: rotate(0deg); }\n  100% {\n    -webkit-transform: rotate(360deg);\n            transform: rotate(360deg); } }\n\n.fa-rotate-90 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)\";\n  -webkit-transform: rotate(90deg);\n          transform: rotate(90deg); }\n\n.fa-rotate-180 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)\";\n  -webkit-transform: rotate(180deg);\n          transform: rotate(180deg); }\n\n.fa-rotate-270 {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)\";\n  -webkit-transform: rotate(270deg);\n          transform: rotate(270deg); }\n\n.fa-flip-horizontal {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)\";\n  -webkit-transform: scale(-1, 1);\n          transform: scale(-1, 1); }\n\n.fa-flip-vertical {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n  -webkit-transform: scale(1, -1);\n          transform: scale(1, -1); }\n\n.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {\n  -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n  -webkit-transform: scale(-1, -1);\n          transform: scale(-1, -1); }\n\n:root .fa-rotate-90,\n:root .fa-rotate-180,\n:root .fa-rotate-270,\n:root .fa-flip-horizontal,\n:root .fa-flip-vertical,\n:root .fa-flip-both {\n  -webkit-filter: none;\n          filter: none; }\n\n.fa-stack {\n  display: inline-block;\n  height: 2em;\n  position: relative;\n  width: 2.5em; }\n\n.fa-stack-1x,\n.fa-stack-2x {\n  bottom: 0;\n  left: 0;\n  margin: auto;\n  position: absolute;\n  right: 0;\n  top: 0; }\n\n.svg-inline--fa.fa-stack-1x {\n  height: 1em;\n  width: 1.25em; }\n\n.svg-inline--fa.fa-stack-2x {\n  height: 2em;\n  width: 2.5em; }\n\n.fa-inverse {\n  color: #fff; }\n\n.sr-only {\n  border: 0;\n  clip: rect(0, 0, 0, 0);\n  height: 1px;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0;\n  position: absolute;\n  width: 1px; }\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n  clip: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  position: static;\n  width: auto; }\n\n.svg-inline--fa .fa-primary {\n  fill: var(--fa-primary-color, currentColor);\n  opacity: 1;\n  opacity: var(--fa-primary-opacity, 1); }\n\n.svg-inline--fa .fa-secondary {\n  fill: var(--fa-secondary-color, currentColor);\n  opacity: 0.4;\n  opacity: var(--fa-secondary-opacity, 0.4); }\n\n.svg-inline--fa.fa-swap-opacity .fa-primary {\n  opacity: 0.4;\n  opacity: var(--fa-secondary-opacity, 0.4); }\n\n.svg-inline--fa.fa-swap-opacity .fa-secondary {\n  opacity: 1;\n  opacity: var(--fa-primary-opacity, 1); }\n\n.svg-inline--fa mask .fa-primary,\n.svg-inline--fa mask .fa-secondary {\n  fill: black; }\n\n.fad.fa-inverse {\n  color: #fff; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/fontawesome-free/css/v4-shims.css",
    "content": "/*!\n * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com\n * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n */\n.fa.fa-glass:before {\n  content: \"\\f000\"; }\n\n.fa.fa-meetup {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-star-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-star-o:before {\n  content: \"\\f005\"; }\n\n.fa.fa-remove:before {\n  content: \"\\f00d\"; }\n\n.fa.fa-close:before {\n  content: \"\\f00d\"; }\n\n.fa.fa-gear:before {\n  content: \"\\f013\"; }\n\n.fa.fa-trash-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-trash-o:before {\n  content: \"\\f2ed\"; }\n\n.fa.fa-file-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-o:before {\n  content: \"\\f15b\"; }\n\n.fa.fa-clock-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-clock-o:before {\n  content: \"\\f017\"; }\n\n.fa.fa-arrow-circle-o-down {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-arrow-circle-o-down:before {\n  content: \"\\f358\"; }\n\n.fa.fa-arrow-circle-o-up {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-arrow-circle-o-up:before {\n  content: \"\\f35b\"; }\n\n.fa.fa-play-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-play-circle-o:before {\n  content: \"\\f144\"; }\n\n.fa.fa-repeat:before {\n  content: \"\\f01e\"; }\n\n.fa.fa-rotate-right:before {\n  content: \"\\f01e\"; }\n\n.fa.fa-refresh:before {\n  content: \"\\f021\"; }\n\n.fa.fa-list-alt {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-dedent:before {\n  content: \"\\f03b\"; }\n\n.fa.fa-video-camera:before {\n  content: \"\\f03d\"; }\n\n.fa.fa-picture-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-picture-o:before {\n  content: \"\\f03e\"; }\n\n.fa.fa-photo {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-photo:before {\n  content: \"\\f03e\"; }\n\n.fa.fa-image {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-image:before {\n  content: \"\\f03e\"; }\n\n.fa.fa-pencil:before {\n  content: \"\\f303\"; }\n\n.fa.fa-map-marker:before {\n  content: \"\\f3c5\"; }\n\n.fa.fa-pencil-square-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-pencil-square-o:before {\n  content: \"\\f044\"; }\n\n.fa.fa-share-square-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-share-square-o:before {\n  content: \"\\f14d\"; }\n\n.fa.fa-check-square-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-check-square-o:before {\n  content: \"\\f14a\"; }\n\n.fa.fa-arrows:before {\n  content: \"\\f0b2\"; }\n\n.fa.fa-times-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-times-circle-o:before {\n  content: \"\\f057\"; }\n\n.fa.fa-check-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-check-circle-o:before {\n  content: \"\\f058\"; }\n\n.fa.fa-mail-forward:before {\n  content: \"\\f064\"; }\n\n.fa.fa-expand:before {\n  content: \"\\f424\"; }\n\n.fa.fa-compress:before {\n  content: \"\\f422\"; }\n\n.fa.fa-eye {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-eye-slash {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-warning:before {\n  content: \"\\f071\"; }\n\n.fa.fa-calendar:before {\n  content: \"\\f073\"; }\n\n.fa.fa-arrows-v:before {\n  content: \"\\f338\"; }\n\n.fa.fa-arrows-h:before {\n  content: \"\\f337\"; }\n\n.fa.fa-bar-chart {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-bar-chart:before {\n  content: \"\\f080\"; }\n\n.fa.fa-bar-chart-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-bar-chart-o:before {\n  content: \"\\f080\"; }\n\n.fa.fa-twitter-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-facebook-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-gears:before {\n  content: \"\\f085\"; }\n\n.fa.fa-thumbs-o-up {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-thumbs-o-up:before {\n  content: \"\\f164\"; }\n\n.fa.fa-thumbs-o-down {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-thumbs-o-down:before {\n  content: \"\\f165\"; }\n\n.fa.fa-heart-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-heart-o:before {\n  content: \"\\f004\"; }\n\n.fa.fa-sign-out:before {\n  content: \"\\f2f5\"; }\n\n.fa.fa-linkedin-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-linkedin-square:before {\n  content: \"\\f08c\"; }\n\n.fa.fa-thumb-tack:before {\n  content: \"\\f08d\"; }\n\n.fa.fa-external-link:before {\n  content: \"\\f35d\"; }\n\n.fa.fa-sign-in:before {\n  content: \"\\f2f6\"; }\n\n.fa.fa-github-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-lemon-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-lemon-o:before {\n  content: \"\\f094\"; }\n\n.fa.fa-square-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-square-o:before {\n  content: \"\\f0c8\"; }\n\n.fa.fa-bookmark-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-bookmark-o:before {\n  content: \"\\f02e\"; }\n\n.fa.fa-twitter {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-facebook {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-facebook:before {\n  content: \"\\f39e\"; }\n\n.fa.fa-facebook-f {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-facebook-f:before {\n  content: \"\\f39e\"; }\n\n.fa.fa-github {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-credit-card {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-feed:before {\n  content: \"\\f09e\"; }\n\n.fa.fa-hdd-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hdd-o:before {\n  content: \"\\f0a0\"; }\n\n.fa.fa-hand-o-right {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-o-right:before {\n  content: \"\\f0a4\"; }\n\n.fa.fa-hand-o-left {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-o-left:before {\n  content: \"\\f0a5\"; }\n\n.fa.fa-hand-o-up {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-o-up:before {\n  content: \"\\f0a6\"; }\n\n.fa.fa-hand-o-down {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-o-down:before {\n  content: \"\\f0a7\"; }\n\n.fa.fa-arrows-alt:before {\n  content: \"\\f31e\"; }\n\n.fa.fa-group:before {\n  content: \"\\f0c0\"; }\n\n.fa.fa-chain:before {\n  content: \"\\f0c1\"; }\n\n.fa.fa-scissors:before {\n  content: \"\\f0c4\"; }\n\n.fa.fa-files-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-files-o:before {\n  content: \"\\f0c5\"; }\n\n.fa.fa-floppy-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-floppy-o:before {\n  content: \"\\f0c7\"; }\n\n.fa.fa-navicon:before {\n  content: \"\\f0c9\"; }\n\n.fa.fa-reorder:before {\n  content: \"\\f0c9\"; }\n\n.fa.fa-pinterest {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-pinterest-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google-plus-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google-plus {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google-plus:before {\n  content: \"\\f0d5\"; }\n\n.fa.fa-money {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-money:before {\n  content: \"\\f3d1\"; }\n\n.fa.fa-unsorted:before {\n  content: \"\\f0dc\"; }\n\n.fa.fa-sort-desc:before {\n  content: \"\\f0dd\"; }\n\n.fa.fa-sort-asc:before {\n  content: \"\\f0de\"; }\n\n.fa.fa-linkedin {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-linkedin:before {\n  content: \"\\f0e1\"; }\n\n.fa.fa-rotate-left:before {\n  content: \"\\f0e2\"; }\n\n.fa.fa-legal:before {\n  content: \"\\f0e3\"; }\n\n.fa.fa-tachometer:before {\n  content: \"\\f3fd\"; }\n\n.fa.fa-dashboard:before {\n  content: \"\\f3fd\"; }\n\n.fa.fa-comment-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-comment-o:before {\n  content: \"\\f075\"; }\n\n.fa.fa-comments-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-comments-o:before {\n  content: \"\\f086\"; }\n\n.fa.fa-flash:before {\n  content: \"\\f0e7\"; }\n\n.fa.fa-clipboard {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-paste {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-paste:before {\n  content: \"\\f328\"; }\n\n.fa.fa-lightbulb-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-lightbulb-o:before {\n  content: \"\\f0eb\"; }\n\n.fa.fa-exchange:before {\n  content: \"\\f362\"; }\n\n.fa.fa-cloud-download:before {\n  content: \"\\f381\"; }\n\n.fa.fa-cloud-upload:before {\n  content: \"\\f382\"; }\n\n.fa.fa-bell-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-bell-o:before {\n  content: \"\\f0f3\"; }\n\n.fa.fa-cutlery:before {\n  content: \"\\f2e7\"; }\n\n.fa.fa-file-text-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-text-o:before {\n  content: \"\\f15c\"; }\n\n.fa.fa-building-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-building-o:before {\n  content: \"\\f1ad\"; }\n\n.fa.fa-hospital-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hospital-o:before {\n  content: \"\\f0f8\"; }\n\n.fa.fa-tablet:before {\n  content: \"\\f3fa\"; }\n\n.fa.fa-mobile:before {\n  content: \"\\f3cd\"; }\n\n.fa.fa-mobile-phone:before {\n  content: \"\\f3cd\"; }\n\n.fa.fa-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-circle-o:before {\n  content: \"\\f111\"; }\n\n.fa.fa-mail-reply:before {\n  content: \"\\f3e5\"; }\n\n.fa.fa-github-alt {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-folder-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-folder-o:before {\n  content: \"\\f07b\"; }\n\n.fa.fa-folder-open-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-folder-open-o:before {\n  content: \"\\f07c\"; }\n\n.fa.fa-smile-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-smile-o:before {\n  content: \"\\f118\"; }\n\n.fa.fa-frown-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-frown-o:before {\n  content: \"\\f119\"; }\n\n.fa.fa-meh-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-meh-o:before {\n  content: \"\\f11a\"; }\n\n.fa.fa-keyboard-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-keyboard-o:before {\n  content: \"\\f11c\"; }\n\n.fa.fa-flag-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-flag-o:before {\n  content: \"\\f024\"; }\n\n.fa.fa-mail-reply-all:before {\n  content: \"\\f122\"; }\n\n.fa.fa-star-half-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-star-half-o:before {\n  content: \"\\f089\"; }\n\n.fa.fa-star-half-empty {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-star-half-empty:before {\n  content: \"\\f089\"; }\n\n.fa.fa-star-half-full {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-star-half-full:before {\n  content: \"\\f089\"; }\n\n.fa.fa-code-fork:before {\n  content: \"\\f126\"; }\n\n.fa.fa-chain-broken:before {\n  content: \"\\f127\"; }\n\n.fa.fa-shield:before {\n  content: \"\\f3ed\"; }\n\n.fa.fa-calendar-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-calendar-o:before {\n  content: \"\\f133\"; }\n\n.fa.fa-maxcdn {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-html5 {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-css3 {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-ticket:before {\n  content: \"\\f3ff\"; }\n\n.fa.fa-minus-square-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-minus-square-o:before {\n  content: \"\\f146\"; }\n\n.fa.fa-level-up:before {\n  content: \"\\f3bf\"; }\n\n.fa.fa-level-down:before {\n  content: \"\\f3be\"; }\n\n.fa.fa-pencil-square:before {\n  content: \"\\f14b\"; }\n\n.fa.fa-external-link-square:before {\n  content: \"\\f360\"; }\n\n.fa.fa-compass {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-caret-square-o-down {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-caret-square-o-down:before {\n  content: \"\\f150\"; }\n\n.fa.fa-toggle-down {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-toggle-down:before {\n  content: \"\\f150\"; }\n\n.fa.fa-caret-square-o-up {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-caret-square-o-up:before {\n  content: \"\\f151\"; }\n\n.fa.fa-toggle-up {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-toggle-up:before {\n  content: \"\\f151\"; }\n\n.fa.fa-caret-square-o-right {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-caret-square-o-right:before {\n  content: \"\\f152\"; }\n\n.fa.fa-toggle-right {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-toggle-right:before {\n  content: \"\\f152\"; }\n\n.fa.fa-eur:before {\n  content: \"\\f153\"; }\n\n.fa.fa-euro:before {\n  content: \"\\f153\"; }\n\n.fa.fa-gbp:before {\n  content: \"\\f154\"; }\n\n.fa.fa-usd:before {\n  content: \"\\f155\"; }\n\n.fa.fa-dollar:before {\n  content: \"\\f155\"; }\n\n.fa.fa-inr:before {\n  content: \"\\f156\"; }\n\n.fa.fa-rupee:before {\n  content: \"\\f156\"; }\n\n.fa.fa-jpy:before {\n  content: \"\\f157\"; }\n\n.fa.fa-cny:before {\n  content: \"\\f157\"; }\n\n.fa.fa-rmb:before {\n  content: \"\\f157\"; }\n\n.fa.fa-yen:before {\n  content: \"\\f157\"; }\n\n.fa.fa-rub:before {\n  content: \"\\f158\"; }\n\n.fa.fa-ruble:before {\n  content: \"\\f158\"; }\n\n.fa.fa-rouble:before {\n  content: \"\\f158\"; }\n\n.fa.fa-krw:before {\n  content: \"\\f159\"; }\n\n.fa.fa-won:before {\n  content: \"\\f159\"; }\n\n.fa.fa-btc {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-bitcoin {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-bitcoin:before {\n  content: \"\\f15a\"; }\n\n.fa.fa-file-text:before {\n  content: \"\\f15c\"; }\n\n.fa.fa-sort-alpha-asc:before {\n  content: \"\\f15d\"; }\n\n.fa.fa-sort-alpha-desc:before {\n  content: \"\\f881\"; }\n\n.fa.fa-sort-amount-asc:before {\n  content: \"\\f160\"; }\n\n.fa.fa-sort-amount-desc:before {\n  content: \"\\f884\"; }\n\n.fa.fa-sort-numeric-asc:before {\n  content: \"\\f162\"; }\n\n.fa.fa-sort-numeric-desc:before {\n  content: \"\\f886\"; }\n\n.fa.fa-youtube-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-youtube {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-xing {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-xing-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-youtube-play {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-youtube-play:before {\n  content: \"\\f167\"; }\n\n.fa.fa-dropbox {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-stack-overflow {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-instagram {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-flickr {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-adn {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-bitbucket {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-bitbucket-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-bitbucket-square:before {\n  content: \"\\f171\"; }\n\n.fa.fa-tumblr {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-tumblr-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-long-arrow-down:before {\n  content: \"\\f309\"; }\n\n.fa.fa-long-arrow-up:before {\n  content: \"\\f30c\"; }\n\n.fa.fa-long-arrow-left:before {\n  content: \"\\f30a\"; }\n\n.fa.fa-long-arrow-right:before {\n  content: \"\\f30b\"; }\n\n.fa.fa-apple {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-windows {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-android {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-linux {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-dribbble {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-skype {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-foursquare {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-trello {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-gratipay {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-gittip {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-gittip:before {\n  content: \"\\f184\"; }\n\n.fa.fa-sun-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-sun-o:before {\n  content: \"\\f185\"; }\n\n.fa.fa-moon-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-moon-o:before {\n  content: \"\\f186\"; }\n\n.fa.fa-vk {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-weibo {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-renren {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-pagelines {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-stack-exchange {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-arrow-circle-o-right {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-arrow-circle-o-right:before {\n  content: \"\\f35a\"; }\n\n.fa.fa-arrow-circle-o-left {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-arrow-circle-o-left:before {\n  content: \"\\f359\"; }\n\n.fa.fa-caret-square-o-left {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-caret-square-o-left:before {\n  content: \"\\f191\"; }\n\n.fa.fa-toggle-left {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-toggle-left:before {\n  content: \"\\f191\"; }\n\n.fa.fa-dot-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-dot-circle-o:before {\n  content: \"\\f192\"; }\n\n.fa.fa-vimeo-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-try:before {\n  content: \"\\f195\"; }\n\n.fa.fa-turkish-lira:before {\n  content: \"\\f195\"; }\n\n.fa.fa-plus-square-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-plus-square-o:before {\n  content: \"\\f0fe\"; }\n\n.fa.fa-slack {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wordpress {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-openid {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-institution:before {\n  content: \"\\f19c\"; }\n\n.fa.fa-bank:before {\n  content: \"\\f19c\"; }\n\n.fa.fa-mortar-board:before {\n  content: \"\\f19d\"; }\n\n.fa.fa-yahoo {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-reddit {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-reddit-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-stumbleupon-circle {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-stumbleupon {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-delicious {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-digg {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-pied-piper-pp {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-pied-piper-alt {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-drupal {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-joomla {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-spoon:before {\n  content: \"\\f2e5\"; }\n\n.fa.fa-behance {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-behance-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-steam {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-steam-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-automobile:before {\n  content: \"\\f1b9\"; }\n\n.fa.fa-envelope-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-envelope-o:before {\n  content: \"\\f0e0\"; }\n\n.fa.fa-spotify {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-deviantart {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-soundcloud {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-file-pdf-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-pdf-o:before {\n  content: \"\\f1c1\"; }\n\n.fa.fa-file-word-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-word-o:before {\n  content: \"\\f1c2\"; }\n\n.fa.fa-file-excel-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-excel-o:before {\n  content: \"\\f1c3\"; }\n\n.fa.fa-file-powerpoint-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-powerpoint-o:before {\n  content: \"\\f1c4\"; }\n\n.fa.fa-file-image-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-image-o:before {\n  content: \"\\f1c5\"; }\n\n.fa.fa-file-photo-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-photo-o:before {\n  content: \"\\f1c5\"; }\n\n.fa.fa-file-picture-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-picture-o:before {\n  content: \"\\f1c5\"; }\n\n.fa.fa-file-archive-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-archive-o:before {\n  content: \"\\f1c6\"; }\n\n.fa.fa-file-zip-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-zip-o:before {\n  content: \"\\f1c6\"; }\n\n.fa.fa-file-audio-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-audio-o:before {\n  content: \"\\f1c7\"; }\n\n.fa.fa-file-sound-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-sound-o:before {\n  content: \"\\f1c7\"; }\n\n.fa.fa-file-video-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-video-o:before {\n  content: \"\\f1c8\"; }\n\n.fa.fa-file-movie-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-movie-o:before {\n  content: \"\\f1c8\"; }\n\n.fa.fa-file-code-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-file-code-o:before {\n  content: \"\\f1c9\"; }\n\n.fa.fa-vine {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-codepen {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-jsfiddle {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-life-ring {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-life-bouy {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-life-bouy:before {\n  content: \"\\f1cd\"; }\n\n.fa.fa-life-buoy {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-life-buoy:before {\n  content: \"\\f1cd\"; }\n\n.fa.fa-life-saver {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-life-saver:before {\n  content: \"\\f1cd\"; }\n\n.fa.fa-support {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-support:before {\n  content: \"\\f1cd\"; }\n\n.fa.fa-circle-o-notch:before {\n  content: \"\\f1ce\"; }\n\n.fa.fa-rebel {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-ra {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-ra:before {\n  content: \"\\f1d0\"; }\n\n.fa.fa-resistance {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-resistance:before {\n  content: \"\\f1d0\"; }\n\n.fa.fa-empire {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-ge {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-ge:before {\n  content: \"\\f1d1\"; }\n\n.fa.fa-git-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-git {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-hacker-news {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-y-combinator-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-y-combinator-square:before {\n  content: \"\\f1d4\"; }\n\n.fa.fa-yc-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-yc-square:before {\n  content: \"\\f1d4\"; }\n\n.fa.fa-tencent-weibo {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-qq {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-weixin {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wechat {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wechat:before {\n  content: \"\\f1d7\"; }\n\n.fa.fa-send:before {\n  content: \"\\f1d8\"; }\n\n.fa.fa-paper-plane-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-paper-plane-o:before {\n  content: \"\\f1d8\"; }\n\n.fa.fa-send-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-send-o:before {\n  content: \"\\f1d8\"; }\n\n.fa.fa-circle-thin {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-circle-thin:before {\n  content: \"\\f111\"; }\n\n.fa.fa-header:before {\n  content: \"\\f1dc\"; }\n\n.fa.fa-sliders:before {\n  content: \"\\f1de\"; }\n\n.fa.fa-futbol-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-futbol-o:before {\n  content: \"\\f1e3\"; }\n\n.fa.fa-soccer-ball-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-soccer-ball-o:before {\n  content: \"\\f1e3\"; }\n\n.fa.fa-slideshare {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-twitch {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-yelp {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-newspaper-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-newspaper-o:before {\n  content: \"\\f1ea\"; }\n\n.fa.fa-paypal {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google-wallet {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc-visa {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc-mastercard {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc-discover {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc-amex {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc-paypal {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc-stripe {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-bell-slash-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-bell-slash-o:before {\n  content: \"\\f1f6\"; }\n\n.fa.fa-trash:before {\n  content: \"\\f2ed\"; }\n\n.fa.fa-copyright {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-eyedropper:before {\n  content: \"\\f1fb\"; }\n\n.fa.fa-area-chart:before {\n  content: \"\\f1fe\"; }\n\n.fa.fa-pie-chart:before {\n  content: \"\\f200\"; }\n\n.fa.fa-line-chart:before {\n  content: \"\\f201\"; }\n\n.fa.fa-lastfm {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-lastfm-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-ioxhost {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-angellist {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-cc:before {\n  content: \"\\f20a\"; }\n\n.fa.fa-ils:before {\n  content: \"\\f20b\"; }\n\n.fa.fa-shekel:before {\n  content: \"\\f20b\"; }\n\n.fa.fa-sheqel:before {\n  content: \"\\f20b\"; }\n\n.fa.fa-meanpath {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-meanpath:before {\n  content: \"\\f2b4\"; }\n\n.fa.fa-buysellads {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-connectdevelop {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-dashcube {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-forumbee {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-leanpub {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-sellsy {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-shirtsinbulk {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-simplybuilt {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-skyatlas {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-diamond {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-diamond:before {\n  content: \"\\f3a5\"; }\n\n.fa.fa-intersex:before {\n  content: \"\\f224\"; }\n\n.fa.fa-facebook-official {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-facebook-official:before {\n  content: \"\\f09a\"; }\n\n.fa.fa-pinterest-p {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-whatsapp {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-hotel:before {\n  content: \"\\f236\"; }\n\n.fa.fa-viacoin {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-medium {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-y-combinator {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-yc {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-yc:before {\n  content: \"\\f23b\"; }\n\n.fa.fa-optin-monster {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-opencart {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-expeditedssl {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-battery-4:before {\n  content: \"\\f240\"; }\n\n.fa.fa-battery:before {\n  content: \"\\f240\"; }\n\n.fa.fa-battery-3:before {\n  content: \"\\f241\"; }\n\n.fa.fa-battery-2:before {\n  content: \"\\f242\"; }\n\n.fa.fa-battery-1:before {\n  content: \"\\f243\"; }\n\n.fa.fa-battery-0:before {\n  content: \"\\f244\"; }\n\n.fa.fa-object-group {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-object-ungroup {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-sticky-note-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-sticky-note-o:before {\n  content: \"\\f249\"; }\n\n.fa.fa-cc-jcb {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cc-diners-club {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-clone {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hourglass-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hourglass-o:before {\n  content: \"\\f254\"; }\n\n.fa.fa-hourglass-1:before {\n  content: \"\\f251\"; }\n\n.fa.fa-hourglass-2:before {\n  content: \"\\f252\"; }\n\n.fa.fa-hourglass-3:before {\n  content: \"\\f253\"; }\n\n.fa.fa-hand-rock-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-rock-o:before {\n  content: \"\\f255\"; }\n\n.fa.fa-hand-grab-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-grab-o:before {\n  content: \"\\f255\"; }\n\n.fa.fa-hand-paper-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-paper-o:before {\n  content: \"\\f256\"; }\n\n.fa.fa-hand-stop-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-stop-o:before {\n  content: \"\\f256\"; }\n\n.fa.fa-hand-scissors-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-scissors-o:before {\n  content: \"\\f257\"; }\n\n.fa.fa-hand-lizard-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-lizard-o:before {\n  content: \"\\f258\"; }\n\n.fa.fa-hand-spock-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-spock-o:before {\n  content: \"\\f259\"; }\n\n.fa.fa-hand-pointer-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-pointer-o:before {\n  content: \"\\f25a\"; }\n\n.fa.fa-hand-peace-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-hand-peace-o:before {\n  content: \"\\f25b\"; }\n\n.fa.fa-registered {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-creative-commons {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-gg {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-gg-circle {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-tripadvisor {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-odnoklassniki {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-odnoklassniki-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-get-pocket {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wikipedia-w {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-safari {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-chrome {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-firefox {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-opera {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-internet-explorer {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-television:before {\n  content: \"\\f26c\"; }\n\n.fa.fa-contao {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-500px {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-amazon {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-calendar-plus-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-calendar-plus-o:before {\n  content: \"\\f271\"; }\n\n.fa.fa-calendar-minus-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-calendar-minus-o:before {\n  content: \"\\f272\"; }\n\n.fa.fa-calendar-times-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-calendar-times-o:before {\n  content: \"\\f273\"; }\n\n.fa.fa-calendar-check-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-calendar-check-o:before {\n  content: \"\\f274\"; }\n\n.fa.fa-map-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-map-o:before {\n  content: \"\\f279\"; }\n\n.fa.fa-commenting:before {\n  content: \"\\f4ad\"; }\n\n.fa.fa-commenting-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-commenting-o:before {\n  content: \"\\f4ad\"; }\n\n.fa.fa-houzz {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-vimeo {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-vimeo:before {\n  content: \"\\f27d\"; }\n\n.fa.fa-black-tie {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-fonticons {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-reddit-alien {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-edge {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-credit-card-alt:before {\n  content: \"\\f09d\"; }\n\n.fa.fa-codiepie {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-modx {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-fort-awesome {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-usb {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-product-hunt {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-mixcloud {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-scribd {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-pause-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-pause-circle-o:before {\n  content: \"\\f28b\"; }\n\n.fa.fa-stop-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-stop-circle-o:before {\n  content: \"\\f28d\"; }\n\n.fa.fa-bluetooth {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-bluetooth-b {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-gitlab {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wpbeginner {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wpforms {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-envira {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wheelchair-alt {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wheelchair-alt:before {\n  content: \"\\f368\"; }\n\n.fa.fa-question-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-question-circle-o:before {\n  content: \"\\f059\"; }\n\n.fa.fa-volume-control-phone:before {\n  content: \"\\f2a0\"; }\n\n.fa.fa-asl-interpreting:before {\n  content: \"\\f2a3\"; }\n\n.fa.fa-deafness:before {\n  content: \"\\f2a4\"; }\n\n.fa.fa-hard-of-hearing:before {\n  content: \"\\f2a4\"; }\n\n.fa.fa-glide {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-glide-g {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-signing:before {\n  content: \"\\f2a7\"; }\n\n.fa.fa-viadeo {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-viadeo-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-snapchat {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-snapchat-ghost {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-snapchat-square {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-pied-piper {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-first-order {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-yoast {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-themeisle {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google-plus-official {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google-plus-official:before {\n  content: \"\\f2b3\"; }\n\n.fa.fa-google-plus-circle {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-google-plus-circle:before {\n  content: \"\\f2b3\"; }\n\n.fa.fa-font-awesome {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-fa {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-fa:before {\n  content: \"\\f2b4\"; }\n\n.fa.fa-handshake-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-handshake-o:before {\n  content: \"\\f2b5\"; }\n\n.fa.fa-envelope-open-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-envelope-open-o:before {\n  content: \"\\f2b6\"; }\n\n.fa.fa-linode {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-address-book-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-address-book-o:before {\n  content: \"\\f2b9\"; }\n\n.fa.fa-vcard:before {\n  content: \"\\f2bb\"; }\n\n.fa.fa-address-card-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-address-card-o:before {\n  content: \"\\f2bb\"; }\n\n.fa.fa-vcard-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-vcard-o:before {\n  content: \"\\f2bb\"; }\n\n.fa.fa-user-circle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-user-circle-o:before {\n  content: \"\\f2bd\"; }\n\n.fa.fa-user-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-user-o:before {\n  content: \"\\f007\"; }\n\n.fa.fa-id-badge {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-drivers-license:before {\n  content: \"\\f2c2\"; }\n\n.fa.fa-id-card-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-id-card-o:before {\n  content: \"\\f2c2\"; }\n\n.fa.fa-drivers-license-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-drivers-license-o:before {\n  content: \"\\f2c2\"; }\n\n.fa.fa-quora {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-free-code-camp {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-telegram {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-thermometer-4:before {\n  content: \"\\f2c7\"; }\n\n.fa.fa-thermometer:before {\n  content: \"\\f2c7\"; }\n\n.fa.fa-thermometer-3:before {\n  content: \"\\f2c8\"; }\n\n.fa.fa-thermometer-2:before {\n  content: \"\\f2c9\"; }\n\n.fa.fa-thermometer-1:before {\n  content: \"\\f2ca\"; }\n\n.fa.fa-thermometer-0:before {\n  content: \"\\f2cb\"; }\n\n.fa.fa-bathtub:before {\n  content: \"\\f2cd\"; }\n\n.fa.fa-s15:before {\n  content: \"\\f2cd\"; }\n\n.fa.fa-window-maximize {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-window-restore {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-times-rectangle:before {\n  content: \"\\f410\"; }\n\n.fa.fa-window-close-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-window-close-o:before {\n  content: \"\\f410\"; }\n\n.fa.fa-times-rectangle-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-times-rectangle-o:before {\n  content: \"\\f410\"; }\n\n.fa.fa-bandcamp {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-grav {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-etsy {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-imdb {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-ravelry {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-eercast {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-eercast:before {\n  content: \"\\f2da\"; }\n\n.fa.fa-snowflake-o {\n  font-family: 'Font Awesome 5 Free';\n  font-weight: 400; }\n\n.fa.fa-snowflake-o:before {\n  content: \"\\f2dc\"; }\n\n.fa.fa-superpowers {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-wpexplorer {\n  font-family: 'Font Awesome 5 Brands';\n  font-weight: 400; }\n\n.fa.fa-cab:before {\n  content: \"\\f1ba\"; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/overlayScrollbars/css/OverlayScrollbars.css",
    "content": "/*!\n * OverlayScrollbars\n * https://github.com/KingSora/OverlayScrollbars\n *\n * Version: 1.13.0\n *\n * Copyright KingSora | Rene Haas.\n * https://github.com/KingSora\n *\n * Released under the MIT license.\n * Date: 02.08.2020\n */\n\n/*\nOVERLAY SCROLLBARS CORE:\n*/\n\nhtml.os-html,\nhtml.os-html > .os-host {\n    display: block;\n    overflow: hidden;\n    box-sizing: border-box;\n    height: 100% !important;\n    width: 100% !important;\n    min-width: 100% !important;\n    min-height: 100% !important;\n    margin: 0 !important;\n    position: absolute !important; /* could be position: fixed; but it causes issues on iOS (-webkit-overflow-scrolling: touch) */\n}\nhtml.os-html > .os-host > .os-padding {\n    position: absolute; /* could be position: fixed; but it causes issues on iOS (-webkit-overflow-scrolling: touch) */\n}\nbody.os-dragging,\nbody.os-dragging * {\n    cursor: default;\n}\n.os-host,\n.os-host-textarea {\n    position: relative;\n    overflow: visible !important;\n    -webkit-box-orient: vertical;\n    -webkit-box-direction: normal;\n        -ms-flex-direction: column;\n            flex-direction: column;\n    -ms-flex-wrap: nowrap;\n        flex-wrap: nowrap;\n    -webkit-box-pack: start;\n        -ms-flex-pack: start;\n            justify-content: flex-start;\n    -ms-flex-line-pack: start;\n        align-content: flex-start;\n    -webkit-box-align: start;\n        -ms-flex-align: start;\n                -ms-grid-row-align: flex-start;\n            align-items: flex-start;\n}\n.os-host-flexbox {\n    overflow: hidden !important;\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n}\n.os-host-flexbox > .os-size-auto-observer {\n    height: inherit !important;\n}\n.os-host-flexbox > .os-content-glue {\n    -webkit-box-flex: 1;\n        -ms-flex-positive: 1;\n            flex-grow: 1;\n    -ms-flex-negative: 0;\n        flex-shrink: 0;\n}\n.os-host-flexbox > .os-size-auto-observer,\n.os-host-flexbox > .os-content-glue {\n    min-height: 0;\n    min-width: 0;\n    -webkit-box-flex: 0;\n        -ms-flex-positive: 0;\n            flex-grow: 0;\n    -ms-flex-negative: 1;\n        flex-shrink: 1;\n    -ms-flex-preferred-size: auto;\n        flex-basis: auto;\n}\n#os-dummy-scrollbar-size {\n    position: fixed;\n    opacity: 0;\n    -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)';\n    visibility: hidden;\n    overflow: scroll;\n    height: 500px;\n    width: 500px;\n}\n#os-dummy-scrollbar-size > div {\n    width: 200%;\n    height: 200%; \n    margin: 10px 0;\n}\n/* fix restricted measuring */\n#os-dummy-scrollbar-size:before,\n#os-dummy-scrollbar-size:after,\n.os-content:before,\n.os-content:after {\n    content: '';\n    display: table;\n    width: 0.01px;\n    height: 0.01px;\n    line-height: 0;\n    font-size: 0;\n    flex-grow: 0;\n    flex-shrink: 0;\n    visibility: hidden;\n}\n#os-dummy-scrollbar-size,\n.os-viewport {\n    -ms-overflow-style: scrollbar !important;\n}\n.os-viewport-native-scrollbars-invisible#os-dummy-scrollbar-size,\n.os-viewport-native-scrollbars-invisible.os-viewport {\n    scrollbar-width: none !important;\n}\n.os-viewport-native-scrollbars-invisible#os-dummy-scrollbar-size::-webkit-scrollbar,\n.os-viewport-native-scrollbars-invisible.os-viewport::-webkit-scrollbar,\n.os-viewport-native-scrollbars-invisible#os-dummy-scrollbar-size::-webkit-scrollbar-corner,\n.os-viewport-native-scrollbars-invisible.os-viewport::-webkit-scrollbar-corner {\n    display: none !important;\n    width: 0px !important;\n    height: 0px !important;\n    visibility: hidden !important;\n    background: transparent !important;\n}\n.os-content-glue {\n    box-sizing: inherit;\n    max-height: 100%;\n    max-width: 100%;\n    width: 100%;\n    pointer-events: none;\n}\n.os-padding {\n    box-sizing: inherit;\n    direction: inherit;\n    position: absolute;\n    overflow: visible;\n    padding: 0;\n    margin: 0;\n    left: 0;\n    top: 0;\n    bottom: 0;\n    right: 0;\n    width: auto !important;\n    height: auto !important;\n\tz-index: 0;\n}\n.os-host-overflow > .os-padding {\n    overflow: hidden;\n}\n.os-viewport {\n    direction: inherit !important;\n    box-sizing: inherit !important;\n    resize: none !important;\n    outline: none !important;\n    position: absolute;\n    overflow: hidden;\n    top: 0;\n    left: 0;\n    bottom: 0;\n    right: 0;\n    padding: 0;\n    margin: 0;\n    -webkit-overflow-scrolling: touch;\n}\n.os-content-arrange {\n    position: absolute;\n    z-index: -1;\n    min-height: 1px;\n    min-width: 1px;\n    pointer-events: none;\n}\n.os-content {\n    direction: inherit;\n    box-sizing: border-box !important;\n    position: relative;\n    display: block;\n    height: 100%;\n    width: 100%;\n    height: 100%;\n    width: 100%;\n    visibility: visible;\n}\n.os-content > .os-textarea {\n    box-sizing: border-box !important;\n    direction: inherit !important;\n    background: transparent !important;\n    outline: 0px none transparent !important;\n    overflow: hidden !important;\n    position: absolute !important;\n    display: block !important;\n    top: 0 !important;\n    left: 0 !important;\n    margin: 0 !important;\n    border-radius: 0px !important;\n    float: none !important;\n    -webkit-filter: none !important;\n            filter: none !important;\n    border: none !important;\n    resize: none !important;\n    -webkit-transform: none !important;\n            transform: none !important;\n    max-width: none !important;\n    max-height: none !important;\n    box-shadow: none !important;\n    -webkit-perspective: none !important;\n            perspective: none !important;\n    opacity: 1 !important;\n    z-index: 1 !important;\n    clip: auto !important;\n    vertical-align: baseline !important;\n    padding: 0px;\n}\n.os-host-rtl > .os-padding > .os-viewport > .os-content > .os-textarea {\n    right: 0 !important;\n}\n.os-content > .os-textarea-cover {\n    z-index: -1;\n    pointer-events: none;\n}\n.os-content > .os-textarea[wrap='off'] {\n    white-space: pre !important;\n    margin: 0px !important;\n}\n.os-text-inherit {\n    font-family: inherit;\n    font-size: inherit;\n    font-weight: inherit;\n    font-style: inherit;\n    font-variant: inherit;\n    text-transform: inherit;\n    text-decoration: inherit;\n    text-indent: inherit;\n    text-align: inherit;\n    text-shadow: inherit;\n    text-overflow: inherit;\n    letter-spacing: inherit;\n    word-spacing: inherit;\n    line-height: inherit;\n    unicode-bidi: inherit;\n    direction: inherit;\n    color: inherit;\n    cursor: text;\n}\n.os-resize-observer,\n.os-resize-observer-host {\n    box-sizing: inherit;\n    display: block;\n    visibility: hidden;\n    position: absolute;\n    top: 0;\n    left: 0;\n    height: 100%;\n    width: 100%;\n    overflow: hidden;\n    pointer-events: none;\n    z-index: -1;\n}\n.os-resize-observer-host {\n    padding: inherit;\n    border: inherit;\n    border-color: transparent;\n    border-style: solid;\n    box-sizing: border-box;\n}\n.os-resize-observer-host.observed {\n    display: flex;\n    flex-direction: column;\n    justify-content: flex-start;\n    align-items: flex-start;\n}\n.os-resize-observer-host > .os-resize-observer,\n.os-resize-observer-host.observed > .os-resize-observer {\n    height: 200%;\n    width: 200%;\n    padding: inherit;\n    border: inherit;\n    margin: 0;\n    display: block;\n    box-sizing: content-box;\n}\n.os-resize-observer-host.observed > .os-resize-observer,\n.os-resize-observer-host.observed > .os-resize-observer:before {\n    display: flex;\n    position: relative;\n    flex-grow: 1;\n    flex-shrink: 0;\n    flex-basis: auto;\n    box-sizing: border-box;\n}\n.os-resize-observer-host.observed > .os-resize-observer:before {\n    content: '';\n    box-sizing: content-box;\n    padding: inherit;\n    border: inherit;\n    margin: 0;\n}\n.os-size-auto-observer {\n    box-sizing: inherit !important;\n    height: 100%;\n    width: inherit;\n    max-width: 1px;\n    position: relative;\n    float: left;\n    max-height: 1px;\n    overflow: hidden;\n    z-index: -1;\n    padding: 0;\n    margin: 0;\n    pointer-events: none;\n    -webkit-box-flex: inherit;\n        -ms-flex-positive: inherit;\n            flex-grow: inherit;\n    -ms-flex-negative: 0;\n        flex-shrink: 0;\n    -ms-flex-preferred-size: 0;\n        flex-basis: 0;\n}\n.os-size-auto-observer > .os-resize-observer {\n    width: 1000%;\n    height: 1000%;\n    min-height: 1px;\n    min-width: 1px;\n}\n.os-resize-observer-item {\n    position: absolute;\n    top: 0;\n    right: 0;\n    bottom: 0;\n    left: 0;\n    overflow: hidden;\n    z-index: -1;\n    opacity: 0;\n    direction: ltr !important;\n    -webkit-box-flex: 0 !important;\n    -ms-flex: none !important;\n    flex: none !important;\n}\n.os-resize-observer-item-final {\n    position: absolute;\n    left: 0;\n    top: 0;\n    -webkit-transition: none !important;\n    transition: none !important;\n    -webkit-box-flex: 0 !important;\n    -ms-flex: none !important;\n    flex: none !important;\n}\n.os-resize-observer {\n    -webkit-animation-duration: 0.001s;\n    animation-duration: 0.001s;\n    -webkit-animation-name: os-resize-observer-dummy-animation;\n    animation-name: os-resize-observer-dummy-animation;\n}\nobject.os-resize-observer {\n    box-sizing: border-box !important;\n}\n@-webkit-keyframes os-resize-observer-dummy-animation {\n    from {\n        z-index: 0;\n    }\n    to {\n        z-index: -1;\n    }\n}\n@keyframes os-resize-observer-dummy-animation {\n    from {\n        z-index: 0;\n    }\n    to {\n        z-index: -1;\n    }\n}\n\n/*\nCUSTOM SCROLLBARS AND CORNER CORE:\n*/\n\n.os-host-transition > .os-scrollbar,\n.os-host-transition > .os-scrollbar-corner {\n    -webkit-transition: opacity 0.3s, visibility 0.3s, top 0.3s, right 0.3s, bottom 0.3s, left 0.3s;\n    transition: opacity 0.3s, visibility 0.3s, top 0.3s, right 0.3s, bottom 0.3s, left 0.3s;\n}\nhtml.os-html > .os-host > .os-scrollbar {\n    position: absolute; /* could be position: fixed; but it causes issues on iOS (-webkit-overflow-scrolling: touch) */\n    z-index: 999999; /* highest z-index of the page */\n}\n.os-scrollbar,\n.os-scrollbar-corner {\n    position: absolute;\n    opacity: 1;\n    -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';\n    z-index: 1;\n}\n.os-scrollbar-corner {\n    bottom: 0;\n    right: 0;\n}\n.os-scrollbar {\n    pointer-events: none;\n}\n.os-scrollbar-track {\n    pointer-events: auto;\n    position: relative;\n    height: 100%;\n    width: 100%;\n    padding: 0 !important;\n    border: none !important;\n}\n.os-scrollbar-handle {\n    pointer-events: auto;\n    position: absolute;\n    width: 100%;\n    height: 100%;\n}\n.os-scrollbar-handle-off,\n.os-scrollbar-track-off {\n    pointer-events: none;\n}\n.os-scrollbar.os-scrollbar-unusable,\n.os-scrollbar.os-scrollbar-unusable * {\n    pointer-events: none !important;\n}\n.os-scrollbar.os-scrollbar-unusable .os-scrollbar-handle {\n    opacity: 0 !important;\n}\n.os-scrollbar-horizontal {\n    bottom: 0;\n    left: 0;\n}\n.os-scrollbar-vertical {\n    top: 0;\n    right: 0;\n}\n.os-host-rtl > .os-scrollbar-horizontal {\n    right: 0;\n}\n.os-host-rtl > .os-scrollbar-vertical {\n    right: auto;\n    left: 0;\n}\n.os-host-rtl > .os-scrollbar-corner {\n    right: auto;\n    left: 0;\n}\n.os-scrollbar-auto-hidden,\n.os-padding + .os-scrollbar-corner,\n.os-host-resize-disabled.os-host-scrollbar-horizontal-hidden > .os-scrollbar-corner,\n.os-host-scrollbar-horizontal-hidden > .os-scrollbar-horizontal,\n.os-host-resize-disabled.os-host-scrollbar-vertical-hidden > .os-scrollbar-corner,\n.os-host-scrollbar-vertical-hidden > .os-scrollbar-vertical,\n.os-scrollbar-horizontal.os-scrollbar-auto-hidden + .os-scrollbar-vertical + .os-scrollbar-corner,\n.os-scrollbar-horizontal + .os-scrollbar-vertical.os-scrollbar-auto-hidden + .os-scrollbar-corner,\n.os-scrollbar-horizontal.os-scrollbar-auto-hidden + .os-scrollbar-vertical.os-scrollbar-auto-hidden + .os-scrollbar-corner {\n    opacity: 0;\n    visibility: hidden;\n    pointer-events: none;\n}\n.os-scrollbar-corner-resize-both {\n    cursor: nwse-resize;\n}\n.os-host-rtl > .os-scrollbar-corner-resize-both {\n    cursor: nesw-resize;\n}\n.os-scrollbar-corner-resize-horizontal {\n    cursor: ew-resize;\n}\n.os-scrollbar-corner-resize-vertical {\n    cursor: ns-resize;\n}\n.os-dragging .os-scrollbar-corner.os-scrollbar-corner-resize {\n    cursor: default;\n}\n.os-host-resize-disabled.os-host-scrollbar-horizontal-hidden > .os-scrollbar-vertical {\n    top: 0;\n    bottom: 0;\n}\n.os-host-resize-disabled.os-host-scrollbar-vertical-hidden > .os-scrollbar-horizontal,\n.os-host-rtl.os-host-resize-disabled.os-host-scrollbar-vertical-hidden > .os-scrollbar-horizontal {\n    right: 0;\n    left: 0;\n}\n.os-scrollbar:hover,\n.os-scrollbar-corner.os-scrollbar-corner-resize {\n    opacity: 1 !important;\n    visibility: visible !important;\n}\n.os-scrollbar-corner.os-scrollbar-corner-resize {\n    background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB3aWR0aD0iMTAiICAgaGVpZ2h0PSIxMCIgICB2ZXJzaW9uPSIxLjEiPiAgPGcgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsLTEwNDIuMzYyMikiICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmUiPiAgICA8cGF0aCAgICAgICBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjQ5NDExNzY1O2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lIiAgICAgICBkPSJtIDcuNDI0MjE4NywxMDQyLjM2MjIgYyAtMC43MjM1NzkyLDAgLTEuMzEwMTU2MiwwLjU4NjYgLTEuMzEwMTU2MiwxLjMxMDIgMCwwLjI5OSAwLjEwNDM0MTksMC41NzEgMC4yNzI5NDkyLDAuNzkxNSAwLjIwOTEwMjQsMC4xNDEzIDAuNDY1NjIwNiwwLjIxODQgMC43MzY5NjI5LDAuMjE4NCAwLjcyMzU3OTMsMCAxLjMxMDE1NjMsLTAuNTg2NiAxLjMxMDE1NjMsLTEuMzEwMiAwLC0wLjI3MTMgLTAuMDc3MDkzLC0wLjUyNzggLTAuMjE4MzU5NCwtMC43MzcgLTAuMjIwNDk0MSwtMC4xNjg2IC0wLjQ5MjU0NDMsLTAuMjcyOSAtMC43OTE1NTI4LC0wLjI3MjkgeiBtIDAsMy4wODQzIGMgLTAuNzIzNTc5MiwwIC0xLjMxMDE1NjIsMC41ODY2IC0xLjMxMDE1NjIsMS4zMTAyIDAsMC4yOTkgMC4xMDQzNDE5LDAuNTcxIDAuMjcyOTQ5MiwwLjc5MTUgMC4yMDkxMDI0LDAuMTQxMyAwLjQ2NTYyMDYsMC4yMTg0IDAuNzM2OTYyOSwwLjIxODQgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjYgMS4zMTAxNTYzLC0xLjMxMDIgMCwtMC4yNzEzIC0wLjA3NzA5MywtMC41Mjc4IC0wLjIxODM1OTQsLTAuNzM2OSAtMC4yMjA0OTQxLC0wLjE2ODYgLTAuNDkyNTQ0MywtMC4yNzMgLTAuNzkxNTUyOCwtMC4yNzMgeiBtIC0zLjA4NDMyNjEsMCBjIC0wLjcyMzU3OTMsMCAtMS4zMTAxNTYzLDAuNTg2NiAtMS4zMTAxNTYzLDEuMzEwMiAwLDAuMjk5IDAuMTA0MzQxOSwwLjU3MSAwLjI3Mjk0OTIsMC43OTE1IDAuMjA5MTAyNCwwLjE0MTMgMC40NjU2MjA3LDAuMjE4NCAwLjczNjk2MjksMC4yMTg0IDAuNzIzNTc5MywwIDEuMzEwMTU2MywtMC41ODY2IDEuMzEwMTU2MywtMS4zMTAyIDAsLTAuMjcxMyAtMC4wNzcwOTMsLTAuNTI3OCAtMC4yMTgzNTk0LC0wLjczNjkgLTAuMjIwNDk0LC0wLjE2ODYgLTAuNDkyNTQ0MiwtMC4yNzMgLTAuNzkxNTUyNywtMC4yNzMgeiBtIC0zLjAyOTczNjQsMy4wMjk4IEMgMC41ODY1NzY5MywxMDQ4LjQ3NjMgMCwxMDQ5LjA2MjggMCwxMDQ5Ljc4NjQgYyAwLDAuMjk5IDAuMTA0MzQxOSwwLjU3MTEgMC4yNzI5NDkyMiwwLjc5MTYgMC4yMDkxMDIyOSwwLjE0MTIgMC40NjU2MjA2NSwwLjIxODMgMC43MzY5NjI4OCwwLjIxODMgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjUgMS4zMTAxNTYzLC0xLjMxMDEgMCwtMC4yNzE0IC0wLjA3NzA5MywtMC41Mjc5IC0wLjIxODM1OTQsLTAuNzM3IC0wLjIyMDQ5NDEsLTAuMTY4NiAtMC40OTI1NDQzLC0wLjI3MjkgLTAuNzkxNTUyOCwtMC4yNzI5IHogbSAzLjAyOTczNjQsMCBjIC0wLjcyMzU3OTMsMCAtMS4zMTAxNTYzLDAuNTg2NSAtMS4zMTAxNTYzLDEuMzEwMSAwLDAuMjk5IDAuMTA0MzQxOSwwLjU3MTEgMC4yNzI5NDkyLDAuNzkxNiAwLjIwOTEwMjQsMC4xNDEyIDAuNDY1NjIwNywwLjIxODMgMC43MzY5NjI5LDAuMjE4MyAwLjcyMzU3OTMsMCAxLjMxMDE1NjMsLTAuNTg2NSAxLjMxMDE1NjMsLTEuMzEwMSAwLC0wLjI3MTQgLTAuMDc3MDkzLC0wLjUyNzkgLTAuMjE4MzU5NCwtMC43MzcgLTAuMjIwNDk0LC0wLjE2ODYgLTAuNDkyNTQ0MiwtMC4yNzI5IC0wLjc5MTU1MjcsLTAuMjcyOSB6IG0gMy4wODQzMjYxLDAgYyAtMC43MjM1NzkyLDAgLTEuMzEwMTU2MiwwLjU4NjUgLTEuMzEwMTU2MiwxLjMxMDEgMCwwLjI5OSAwLjEwNDM0MTksMC41NzExIDAuMjcyOTQ5MiwwLjc5MTYgMC4yMDkxMDI0LDAuMTQxMiAwLjQ2NTYyMDYsMC4yMTgzIDAuNzM2OTYyOSwwLjIxODMgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjUgMS4zMTAxNTYzLC0xLjMxMDEgMCwtMC4yNzE0IC0wLjA3NzA5MywtMC41Mjc5IC0wLjIxODM1OTQsLTAuNzM3IC0wLjIyMDQ5NDEsLTAuMTY4NiAtMC40OTI1NDQzLC0wLjI3MjkgLTAuNzkxNTUyOCwtMC4yNzI5IHoiLz4gIDwvZz4gIDxnICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmUiPiAgICA8cGF0aCAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lIiAgICAgICBkPSJtIDguMjE1NzcxNSwwLjI3Mjk0OTIyIGMgMC4xNDEyNjY3LDAuMjA5MTAyMjkgMC4yMTgzNTk0LDAuNDY1NjIwNjUgMC4yMTgzNTk0LDAuNzM2OTYyODggMCwwLjcyMzU3OTMgLTAuNTg2NTc3LDEuMzEwMTU2MyAtMS4zMTAxNTYzLDEuMzEwMTU2MyAtMC4yNzEzNDIzLDAgLTAuNTI3ODYwNSwtMC4wNzcwOTMgLTAuNzM2OTYyOSwtMC4yMTgzNTk0IDAuMjM5NDEwNCwwLjMxMzA4NTkgMC42MTI2MzYyLDAuNTE4NjAzNSAxLjAzNzIwNywwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDc2IC0wLjIwNTUxNzYsLTAuNzk3Nzk2NTkgLTAuNTE4NjAzNSwtMS4wMzcyMDY5OCB6IG0gMCwzLjA4NDMyNjE4IGMgMC4xNDEyNjY3LDAuMjA5MTAyMyAwLjIxODM1OTQsMC40NjU2MjA2IDAuMjE4MzU5NCwwLjczNjk2MjkgMCwwLjcyMzU3OTMgLTAuNTg2NTc3LDEuMzEwMTU2MiAtMS4zMTAxNTYzLDEuMzEwMTU2MiAtMC4yNzEzNDIzLDAgLTAuNTI3ODYwNSwtMC4wNzcwOTMgLTAuNzM2OTYyOSwtMC4yMTgzNTkzIDAuMjM5NDEwNCwwLjMxMzA4NTkgMC42MTI2MzYyLDAuNTE4NjAzNSAxLjAzNzIwNywwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NiwtMC43OTc3OTY3IC0wLjUxODYwMzUsLTEuMDM3MjA3IHogbSAtMy4wODQzMjYyLDAgYyAwLjE0MTI2NjcsMC4yMDkxMDIzIDAuMjE4MzU5NCwwLjQ2NTYyMDYgMC4yMTgzNTk0LDAuNzM2OTYyOSAwLDAuNzIzNTc5MyAtMC41ODY1NzcsMS4zMTAxNTYyIC0xLjMxMDE1NjMsMS4zMTAxNTYyIC0wLjI3MTM0MjIsMCAtMC41Mjc4NjA1LC0wLjA3NzA5MyAtMC43MzY5NjI5LC0wLjIxODM1OTMgMC4yMzk0MTA0LDAuMzEzMDg1OSAwLjYxMjYzNjMsMC41MTg2MDM1IDEuMDM3MjA3MSwwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYyLC0wLjU4NjU3NyAxLjMxMDE1NjIsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NSwtMC43OTc3OTY3IC0wLjUxODYwMzUsLTEuMDM3MjA3IHogTSAyLjEwMTcwOSw2LjM4NzAxMTcgYyAwLjE0MTI2NjcsMC4yMDkxMDI0IDAuMjE4MzU5NCwwLjQ2NTYyMDYgMC4yMTgzNTk0LDAuNzM2OTYyOSAwLDAuNzIzNTc5MyAtMC41ODY1NzcsMS4zMTAxNTYzIC0xLjMxMDE1NjMsMS4zMTAxNTYzIC0wLjI3MTM0MjIzLDAgLTAuNTI3ODYwNTksLTAuMDc3MDkzIC0wLjczNjk2Mjg4LC0wLjIxODM1OTQgMC4yMzk0MTAzOSwwLjMxMzA4NTkgMC42MTI2MzYyMiwwLjUxODYwMzUgMS4wMzcyMDY5OCwwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NiwtMC43OTc3OTY2IC0wLjUxODYwMzUsLTEuMDM3MjA3IHogbSAzLjAyOTczNjMsMCBjIDAuMTQxMjY2NywwLjIwOTEwMjQgMC4yMTgzNTk0LDAuNDY1NjIwNiAwLjIxODM1OTQsMC43MzY5NjI5IDAsMC43MjM1NzkzIC0wLjU4NjU3NywxLjMxMDE1NjMgLTEuMzEwMTU2MywxLjMxMDE1NjMgLTAuMjcxMzQyMiwwIC0wLjUyNzg2MDUsLTAuMDc3MDkzIC0wLjczNjk2MjksLTAuMjE4MzU5NCAwLjIzOTQxMDQsMC4zMTMwODU5IDAuNjEyNjM2MywwLjUxODYwMzUgMS4wMzcyMDcxLDAuNTE4NjAzNSAwLjcyMzU3OTMsMCAxLjMxMDE1NjIsLTAuNTg2NTc3IDEuMzEwMTU2MiwtMS4zMTAxNTYzIDAsLTAuNDI0NTcwOCAtMC4yMDU1MTc1LC0wLjc5Nzc5NjYgLTAuNTE4NjAzNSwtMS4wMzcyMDcgeiBtIDMuMDg0MzI2MiwwIGMgMC4xNDEyNjY3LDAuMjA5MTAyNCAwLjIxODM1OTQsMC40NjU2MjA2IDAuMjE4MzU5NCwwLjczNjk2MjkgMCwwLjcyMzU3OTMgLTAuNTg2NTc3LDEuMzEwMTU2MyAtMS4zMTAxNTYzLDEuMzEwMTU2MyAtMC4yNzEzNDIzLDAgLTAuNTI3ODYwNSwtMC4wNzcwOTMgLTAuNzM2OTYyOSwtMC4yMTgzNTk0IDAuMjM5NDEwNCwwLjMxMzA4NTkgMC42MTI2MzYyLDAuNTE4NjAzNSAxLjAzNzIwNywwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NiwtMC43OTc3OTY2IC0wLjUxODYwMzUsLTEuMDM3MjA3IHoiIC8+ICA8L2c+PC9zdmc+);\n    background-repeat: no-repeat;\n    background-position: 100% 100%;\n    pointer-events: auto !important;\n}\n.os-host-rtl > .os-scrollbar-corner.os-scrollbar-corner-resize {\n    -webkit-transform: scale(-1, 1);\n    transform: scale(-1, 1);\n}\n.os-host-overflow {\n    overflow: hidden !important;\n}\n.os-host-overflow-x {\n} \n.os-host-overflow-y {\n} \n\n/*\nTHEMES:\n*/\n\n/* NONE THEME: */\n.os-theme-none > .os-scrollbar-horizontal,\n.os-theme-none > .os-scrollbar-vertical,\n.os-theme-none > .os-scrollbar-corner {\n    display: none !important;\n}\n.os-theme-none > .os-scrollbar-corner-resize {\n    display: block !important;\n    min-width: 10px;\n    min-height: 10px;\n}\n/* DARK & LIGHT THEME: */\n.os-theme-dark > .os-scrollbar-horizontal,\n.os-theme-light > .os-scrollbar-horizontal {\n    right: 10px;\n    height: 10px;\n}\n.os-theme-dark > .os-scrollbar-vertical,\n.os-theme-light > .os-scrollbar-vertical {\n    bottom: 10px;\n    width: 10px;\n}\n.os-theme-dark.os-host-rtl > .os-scrollbar-horizontal,\n.os-theme-light.os-host-rtl > .os-scrollbar-horizontal {\n    left: 10px;\n    right: 0;\n}\n.os-theme-dark > .os-scrollbar-corner,\n.os-theme-light > .os-scrollbar-corner {\n    height: 10px;\n    width: 10px;\n}\n.os-theme-dark > .os-scrollbar-corner,\n.os-theme-light > .os-scrollbar-corner {\n    background-color: transparent;\n}\n.os-theme-dark > .os-scrollbar,\n.os-theme-light > .os-scrollbar {\n    padding: 2px;\n    box-sizing: border-box;\n    background: transparent;\n}\n.os-theme-dark > .os-scrollbar.os-scrollbar-unusable,\n.os-theme-light > .os-scrollbar.os-scrollbar-unusable {\n    background: transparent;\n}\n.os-theme-dark > .os-scrollbar > .os-scrollbar-track,\n.os-theme-light > .os-scrollbar > .os-scrollbar-track {\n    background: transparent;\n}\n.os-theme-dark > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle,\n.os-theme-light > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle {\n    min-width: 30px;\n}\n.os-theme-dark > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle,\n.os-theme-light > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle {\n    min-height: 30px;\n}\n.os-theme-dark.os-host-transition > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle,\n.os-theme-light.os-host-transition > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle {\n    -webkit-transition: background-color 0.3s;\n    transition: background-color 0.3s;\n}\n.os-theme-dark > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle,\n.os-theme-light > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle,\n.os-theme-dark > .os-scrollbar > .os-scrollbar-track,\n.os-theme-light > .os-scrollbar > .os-scrollbar-track {\n    border-radius: 10px;\n}\n.os-theme-dark > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle {\n    background: rgba(0, 0, 0, 0.4);\n}\n.os-theme-light > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle {\n    background: rgba(255, 255, 255, 0.4);\n}\n.os-theme-dark > .os-scrollbar:hover > .os-scrollbar-track > .os-scrollbar-handle {\n    background: rgba(0, 0, 0, .55);\n}\n.os-theme-light > .os-scrollbar:hover > .os-scrollbar-track > .os-scrollbar-handle {\n    background: rgba(255, 255, 255, .55);\n}\n.os-theme-dark > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle.active {\n    background: rgba(0, 0, 0, .7);\n}\n.os-theme-light > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle.active {\n    background: rgba(255, 255, 255, .7);\n}\n.os-theme-dark > .os-scrollbar-horizontal .os-scrollbar-handle:before,\n.os-theme-dark > .os-scrollbar-vertical .os-scrollbar-handle:before,\n.os-theme-light > .os-scrollbar-horizontal .os-scrollbar-handle:before,\n.os-theme-light > .os-scrollbar-vertical .os-scrollbar-handle:before {\n    content: '';\n    position: absolute;\n    left: 0;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    display: block;\n}\n.os-theme-dark.os-host-scrollbar-horizontal-hidden > .os-scrollbar-horizontal .os-scrollbar-handle:before,\n.os-theme-dark.os-host-scrollbar-vertical-hidden > .os-scrollbar-vertical .os-scrollbar-handle:before,\n.os-theme-light.os-host-scrollbar-horizontal-hidden > .os-scrollbar-horizontal .os-scrollbar-handle:before,\n.os-theme-light.os-host-scrollbar-vertical-hidden > .os-scrollbar-vertical .os-scrollbar-handle:before {\n    display: none;\n}\n.os-theme-dark > .os-scrollbar-horizontal .os-scrollbar-handle:before,\n.os-theme-light > .os-scrollbar-horizontal .os-scrollbar-handle:before {\n    top: -6px;\n    bottom: -2px;\n}\n.os-theme-dark > .os-scrollbar-vertical .os-scrollbar-handle:before,\n.os-theme-light > .os-scrollbar-vertical .os-scrollbar-handle:before {\n    left: -6px;\n    right: -2px;\n}\n.os-host-rtl.os-theme-dark > .os-scrollbar-vertical .os-scrollbar-handle:before,\n.os-host-rtl.os-theme-light > .os-scrollbar-vertical .os-scrollbar-handle:before {\n    right: -6px;\n    left: -2px;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/overlayScrollbars/js/OverlayScrollbars.js",
    "content": "/*!\n * OverlayScrollbars\n * https://github.com/KingSora/OverlayScrollbars\n *\n * Version: 1.13.0\n *\n * Copyright KingSora | Rene Haas.\n * https://github.com/KingSora\n *\n * Released under the MIT license.\n * Date: 02.08.2020\n */\n\n(function (global, factory) {\n    if (typeof define === 'function' && define.amd)\n        define(function () { return factory(global, global.document, undefined); });\n    else if (typeof module === 'object' && typeof module.exports === 'object')\n        module.exports = factory(global, global.document, undefined);\n    else\n        factory(global, global.document, undefined);\n}(typeof window !== 'undefined' ? window : this,\n    function (window, document, undefined) {\n        'use strict';\n        var PLUGINNAME = 'OverlayScrollbars';\n        var TYPES = {\n            o: 'object',\n            f: 'function',\n            a: 'array',\n            s: 'string',\n            b: 'boolean',\n            n: 'number',\n            u: 'undefined',\n            z: 'null'\n            //d : 'date',\n            //e : 'error',\n            //r : 'regexp',\n            //y : 'symbol'\n        };\n        var LEXICON = {\n            c: 'class',\n            s: 'style',\n            i: 'id',\n            l: 'length',\n            p: 'prototype',\n            ti: 'tabindex',\n            oH: 'offsetHeight',\n            cH: 'clientHeight',\n            sH: 'scrollHeight',\n            oW: 'offsetWidth',\n            cW: 'clientWidth',\n            sW: 'scrollWidth',\n            hOP: 'hasOwnProperty',\n            bCR: 'getBoundingClientRect'\n        };\n        var VENDORS = (function () {\n            //https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix\n            var jsCache = {};\n            var cssCache = {};\n            var cssPrefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];\n            var jsPrefixes = ['WebKit', 'Moz', 'O', 'MS'];\n            function firstLetterToUpper(str) {\n                return str.charAt(0).toUpperCase() + str.slice(1);\n            }\n\n            return {\n                _cssPrefixes: cssPrefixes,\n                _jsPrefixes: jsPrefixes,\n                _cssProperty: function (name) {\n                    var result = cssCache[name];\n\n                    if (cssCache[LEXICON.hOP](name))\n                        return result;\n\n                    var uppercasedName = firstLetterToUpper(name);\n                    var elmStyle = document.createElement('div')[LEXICON.s];\n                    var resultPossibilities;\n                    var i = 0;\n                    var v;\n                    var currVendorWithoutDashes;\n\n                    for (; i < cssPrefixes.length; i++) {\n                        currVendorWithoutDashes = cssPrefixes[i].replace(/-/g, '');\n                        resultPossibilities = [\n                            name, //transition\n                            cssPrefixes[i] + name, //-webkit-transition\n                            currVendorWithoutDashes + uppercasedName, //webkitTransition\n                            firstLetterToUpper(currVendorWithoutDashes) + uppercasedName //WebkitTransition\n                        ];\n                        for (v = 0; v < resultPossibilities[LEXICON.l]; v++) {\n                            if (elmStyle[resultPossibilities[v]] !== undefined) {\n                                result = resultPossibilities[v];\n                                break;\n                            }\n                        }\n                    }\n\n                    cssCache[name] = result;\n                    return result;\n                },\n                _cssPropertyValue: function (property, values, suffix) {\n                    var name = property + ' ' + values;\n                    var result = cssCache[name];\n\n                    if (cssCache[LEXICON.hOP](name))\n                        return result;\n\n                    var dummyStyle = document.createElement('div')[LEXICON.s];\n                    var possbleValues = values.split(' ');\n                    var preparedSuffix = suffix || '';\n                    var i = 0;\n                    var v = -1;\n                    var prop;\n\n                    for (; i < possbleValues[LEXICON.l]; i++) {\n                        for (; v < VENDORS._cssPrefixes[LEXICON.l]; v++) {\n                            prop = v < 0 ? possbleValues[i] : VENDORS._cssPrefixes[v] + possbleValues[i];\n                            dummyStyle.cssText = property + ':' + prop + preparedSuffix;\n                            if (dummyStyle[LEXICON.l]) {\n                                result = prop;\n                                break;\n                            }\n                        }\n                    }\n\n                    cssCache[name] = result;\n                    return result;\n                },\n                _jsAPI: function (name, isInterface, fallback) {\n                    var i = 0;\n                    var result = jsCache[name];\n\n                    if (!jsCache[LEXICON.hOP](name)) {\n                        result = window[name];\n                        for (; i < jsPrefixes[LEXICON.l]; i++)\n                            result = result || window[(isInterface ? jsPrefixes[i] : jsPrefixes[i].toLowerCase()) + firstLetterToUpper(name)];\n                        jsCache[name] = result;\n                    }\n                    return result || fallback;\n                }\n            }\n        })();\n        var COMPATIBILITY = (function () {\n            function windowSize(x) {\n                return x ? window.innerWidth || document.documentElement[LEXICON.cW] || document.body[LEXICON.cW] : window.innerHeight || document.documentElement[LEXICON.cH] || document.body[LEXICON.cH];\n            }\n            function bind(func, thisObj) {\n                if (typeof func != TYPES.f) {\n                    throw \"Can't bind function!\";\n                    // closest thing possible to the ECMAScript 5\n                    // internal IsCallable function\n                    //throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');\n                }\n                var proto = LEXICON.p;\n                var aArgs = Array[proto].slice.call(arguments, 2);\n                var fNOP = function () { };\n                var fBound = function () { return func.apply(this instanceof fNOP ? this : thisObj, aArgs.concat(Array[proto].slice.call(arguments))); };\n\n                if (func[proto])\n                    fNOP[proto] = func[proto]; // Function.prototype doesn't have a prototype property\n                fBound[proto] = new fNOP();\n\n                return fBound;\n            }\n\n            return {\n                /**\n                 * Gets the current window width.\n                 * @returns {Number|number} The current window width in pixel.\n                 */\n                wW: bind(windowSize, 0, true),\n\n                /**\n                 * Gets the current window height.\n                 * @returns {Number|number} The current window height in pixel.\n                 */\n                wH: bind(windowSize, 0),\n\n                /**\n                 * Gets the MutationObserver Object or undefined if not supported.\n                 * @returns {MutationObserver|*|undefined} The MutationsObserver Object or undefined.\n                 */\n                mO: bind(VENDORS._jsAPI, 0, 'MutationObserver', true),\n\n                /**\n                 * Gets the ResizeObserver Object or undefined if not supported.\n                 * @returns {MutationObserver|*|undefined} The ResizeObserver Object or undefined.\n                 */\n                rO: bind(VENDORS._jsAPI, 0, 'ResizeObserver', true),\n\n                /**\n                 * Gets the RequestAnimationFrame method or it's corresponding polyfill.\n                 * @returns {*|Function} The RequestAnimationFrame method or it's corresponding polyfill.\n                 */\n                rAF: bind(VENDORS._jsAPI, 0, 'requestAnimationFrame', false, function (func) { return window.setTimeout(func, 1000 / 60); }),\n\n                /**\n                 * Gets the CancelAnimationFrame method or it's corresponding polyfill.\n                 * @returns {*|Function} The CancelAnimationFrame method or it's corresponding polyfill.\n                 */\n                cAF: bind(VENDORS._jsAPI, 0, 'cancelAnimationFrame', false, function (id) { return window.clearTimeout(id); }),\n\n                /**\n                 * Gets the current time.\n                 * @returns {number} The current time.\n                 */\n                now: function () {\n                    return Date.now && Date.now() || new Date().getTime();\n                },\n\n                /**\n                 * Stops the propagation of the given event.\n                 * @param event The event of which the propagation shall be stoped.\n                 */\n                stpP: function (event) {\n                    if (event.stopPropagation)\n                        event.stopPropagation();\n                    else\n                        event.cancelBubble = true;\n                },\n\n                /**\n                 * Prevents the default action of the given event.\n                 * @param event The event of which the default action shall be prevented.\n                 */\n                prvD: function (event) {\n                    if (event.preventDefault && event.cancelable)\n                        event.preventDefault();\n                    else\n                        event.returnValue = false;\n                },\n\n                /**\n                 * Gets the pageX and pageY values of the given mouse event.\n                 * @param event The mouse event of which the pageX and pageX shall be got.\n                 * @returns {{x: number, y: number}} x = pageX value, y = pageY value.\n                 */\n                page: function (event) {\n                    event = event.originalEvent || event;\n\n                    var strPage = 'page';\n                    var strClient = 'client';\n                    var strX = 'X';\n                    var strY = 'Y';\n                    var target = event.target || event.srcElement || document;\n                    var eventDoc = target.ownerDocument || document;\n                    var doc = eventDoc.documentElement;\n                    var body = eventDoc.body;\n\n                    //if touch event return return pageX/Y of it\n                    if (event.touches !== undefined) {\n                        var touch = event.touches[0];\n                        return {\n                            x: touch[strPage + strX],\n                            y: touch[strPage + strY]\n                        }\n                    }\n\n                    // Calculate pageX/Y if not native supported\n                    if (!event[strPage + strX] && event[strClient + strX] && event[strClient + strX] != null) {\n\n                        return {\n                            x: event[strClient + strX] +\n                                (doc && doc.scrollLeft || body && body.scrollLeft || 0) -\n                                (doc && doc.clientLeft || body && body.clientLeft || 0),\n                            y: event[strClient + strY] +\n                                (doc && doc.scrollTop || body && body.scrollTop || 0) -\n                                (doc && doc.clientTop || body && body.clientTop || 0)\n                        }\n                    }\n                    return {\n                        x: event[strPage + strX],\n                        y: event[strPage + strY]\n                    };\n                },\n\n                /**\n                 * Gets the clicked mouse button of the given mouse event.\n                 * @param event The mouse event of which the clicked button shal be got.\n                 * @returns {number} The number of the clicked mouse button. (0 : none | 1 : leftButton | 2 : middleButton | 3 : rightButton)\n                 */\n                mBtn: function (event) {\n                    var button = event.button;\n                    if (!event.which && button !== undefined)\n                        return (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n                    else\n                        return event.which;\n                },\n\n                /**\n                 * Checks whether a item is in the given array and returns its index.\n                 * @param item The item of which the position in the array shall be determined.\n                 * @param arr The array.\n                 * @returns {number} The zero based index of the item or -1 if the item isn't in the array.\n                 */\n                inA: function (item, arr) {\n                    for (var i = 0; i < arr[LEXICON.l]; i++)\n                        //Sometiems in IE a \"SCRIPT70\" Permission denied error occurs if HTML elements in a iFrame are compared\n                        try {\n                            if (arr[i] === item)\n                                return i;\n                        }\n                        catch (e) { }\n                    return -1;\n                },\n\n                /**\n                 * Returns true if the given value is a array.\n                 * @param arr The potential array.\n                 * @returns {boolean} True if the given value is a array, false otherwise.\n                 */\n                isA: function (arr) {\n                    var def = Array.isArray;\n                    return def ? def(arr) : this.type(arr) == TYPES.a;\n                },\n\n                /**\n                 * Determine the internal JavaScript [[Class]] of the given object.\n                 * @param obj The object of which the type shall be determined.\n                 * @returns {string} The type of the given object.\n                 */\n                type: function (obj) {\n                    if (obj === undefined)\n                        return obj + '';\n                    if (obj === null)\n                        return obj + '';\n                    return Object[LEXICON.p].toString.call(obj).replace(/^\\[object (.+)\\]$/, '$1').toLowerCase();\n                },\n\n\n                bind: bind\n\n                /**\n                 * Gets the vendor-prefixed CSS property by the given name.\n                 * For example the given name is \"transform\" and you're using a old Firefox browser then the returned value would be \"-moz-transform\".\n                 * If the browser doesn't need a vendor-prefix, then the returned string is the given name.\n                 * If the browser doesn't support the given property name at all (not even with a vendor-prefix) the returned value is null.\n                 * @param propName The unprefixed CSS property name.\n                 * @returns {string|null} The vendor-prefixed CSS property or null if the browser doesn't support the given CSS property.\n\n                cssProp: function(propName) {\n                    return VENDORS._cssProperty(propName);\n                }\n                */\n            }\n        })();\n\n\n        var MATH = Math;\n        var JQUERY = window.jQuery;\n        var EASING = (function () {\n            var _easingsMath = {\n                p: MATH.PI,\n                c: MATH.cos,\n                s: MATH.sin,\n                w: MATH.pow,\n                t: MATH.sqrt,\n                n: MATH.asin,\n                a: MATH.abs,\n                o: 1.70158\n            };\n\n            /*\n             x : current percent (0 - 1),\n             t : current time (duration * percent),\n             b : start value (from),\n             c : end value (to),\n             d : duration\n\n             easingName : function(x, t, b, c, d) { return easedValue; }\n             */\n\n            return {\n                swing: function (x, t, b, c, d) {\n                    return 0.5 - _easingsMath.c(x * _easingsMath.p) / 2;\n                },\n                linear: function (x, t, b, c, d) {\n                    return x;\n                },\n                easeInQuad: function (x, t, b, c, d) {\n                    return c * (t /= d) * t + b;\n                },\n                easeOutQuad: function (x, t, b, c, d) {\n                    return -c * (t /= d) * (t - 2) + b;\n                },\n                easeInOutQuad: function (x, t, b, c, d) {\n                    return ((t /= d / 2) < 1) ? c / 2 * t * t + b : -c / 2 * ((--t) * (t - 2) - 1) + b;\n                },\n                easeInCubic: function (x, t, b, c, d) {\n                    return c * (t /= d) * t * t + b;\n                },\n                easeOutCubic: function (x, t, b, c, d) {\n                    return c * ((t = t / d - 1) * t * t + 1) + b;\n                },\n                easeInOutCubic: function (x, t, b, c, d) {\n                    return ((t /= d / 2) < 1) ? c / 2 * t * t * t + b : c / 2 * ((t -= 2) * t * t + 2) + b;\n                },\n                easeInQuart: function (x, t, b, c, d) {\n                    return c * (t /= d) * t * t * t + b;\n                },\n                easeOutQuart: function (x, t, b, c, d) {\n                    return -c * ((t = t / d - 1) * t * t * t - 1) + b;\n                },\n                easeInOutQuart: function (x, t, b, c, d) {\n                    return ((t /= d / 2) < 1) ? c / 2 * t * t * t * t + b : -c / 2 * ((t -= 2) * t * t * t - 2) + b;\n                },\n                easeInQuint: function (x, t, b, c, d) {\n                    return c * (t /= d) * t * t * t * t + b;\n                },\n                easeOutQuint: function (x, t, b, c, d) {\n                    return c * ((t = t / d - 1) * t * t * t * t + 1) + b;\n                },\n                easeInOutQuint: function (x, t, b, c, d) {\n                    return ((t /= d / 2) < 1) ? c / 2 * t * t * t * t * t + b : c / 2 * ((t -= 2) * t * t * t * t + 2) + b;\n                },\n                easeInSine: function (x, t, b, c, d) {\n                    return -c * _easingsMath.c(t / d * (_easingsMath.p / 2)) + c + b;\n                },\n                easeOutSine: function (x, t, b, c, d) {\n                    return c * _easingsMath.s(t / d * (_easingsMath.p / 2)) + b;\n                },\n                easeInOutSine: function (x, t, b, c, d) {\n                    return -c / 2 * (_easingsMath.c(_easingsMath.p * t / d) - 1) + b;\n                },\n                easeInExpo: function (x, t, b, c, d) {\n                    return (t == 0) ? b : c * _easingsMath.w(2, 10 * (t / d - 1)) + b;\n                },\n                easeOutExpo: function (x, t, b, c, d) {\n                    return (t == d) ? b + c : c * (-_easingsMath.w(2, -10 * t / d) + 1) + b;\n                },\n                easeInOutExpo: function (x, t, b, c, d) {\n                    if (t == 0) return b;\n                    if (t == d) return b + c;\n                    if ((t /= d / 2) < 1) return c / 2 * _easingsMath.w(2, 10 * (t - 1)) + b;\n                    return c / 2 * (-_easingsMath.w(2, -10 * --t) + 2) + b;\n                },\n                easeInCirc: function (x, t, b, c, d) {\n                    return -c * (_easingsMath.t(1 - (t /= d) * t) - 1) + b;\n                },\n                easeOutCirc: function (x, t, b, c, d) {\n                    return c * _easingsMath.t(1 - (t = t / d - 1) * t) + b;\n                },\n                easeInOutCirc: function (x, t, b, c, d) {\n                    return ((t /= d / 2) < 1) ? -c / 2 * (_easingsMath.t(1 - t * t) - 1) + b : c / 2 * (_easingsMath.t(1 - (t -= 2) * t) + 1) + b;\n                },\n                easeInElastic: function (x, t, b, c, d) {\n                    var s = _easingsMath.o; var p = 0; var a = c;\n                    if (t == 0) return b; if ((t /= d) == 1) return b + c; if (!p) p = d * .3;\n                    if (a < _easingsMath.a(c)) { a = c; s = p / 4; }\n                    else s = p / (2 * _easingsMath.p) * _easingsMath.n(c / a);\n                    return -(a * _easingsMath.w(2, 10 * (t -= 1)) * _easingsMath.s((t * d - s) * (2 * _easingsMath.p) / p)) + b;\n                },\n                easeOutElastic: function (x, t, b, c, d) {\n                    var s = _easingsMath.o; var p = 0; var a = c;\n                    if (t == 0) return b;\n                    if ((t /= d) == 1) return b + c;\n                    if (!p) p = d * .3;\n                    if (a < _easingsMath.a(c)) { a = c; s = p / 4; }\n                    else s = p / (2 * _easingsMath.p) * _easingsMath.n(c / a);\n                    return a * _easingsMath.w(2, -10 * t) * _easingsMath.s((t * d - s) * (2 * _easingsMath.p) / p) + c + b;\n                },\n                easeInOutElastic: function (x, t, b, c, d) {\n                    var s = _easingsMath.o; var p = 0; var a = c;\n                    if (t == 0) return b;\n                    if ((t /= d / 2) == 2) return b + c;\n                    if (!p) p = d * (.3 * 1.5);\n                    if (a < _easingsMath.a(c)) { a = c; s = p / 4; }\n                    else s = p / (2 * _easingsMath.p) * _easingsMath.n(c / a);\n                    if (t < 1) return -.5 * (a * _easingsMath.w(2, 10 * (t -= 1)) * _easingsMath.s((t * d - s) * (2 * _easingsMath.p) / p)) + b;\n                    return a * _easingsMath.w(2, -10 * (t -= 1)) * _easingsMath.s((t * d - s) * (2 * _easingsMath.p) / p) * .5 + c + b;\n                },\n                easeInBack: function (x, t, b, c, d, s) {\n                    s = s || _easingsMath.o;\n                    return c * (t /= d) * t * ((s + 1) * t - s) + b;\n                },\n                easeOutBack: function (x, t, b, c, d, s) {\n                    s = s || _easingsMath.o;\n                    return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;\n                },\n                easeInOutBack: function (x, t, b, c, d, s) {\n                    s = s || _easingsMath.o;\n                    return ((t /= d / 2) < 1) ? c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b : c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;\n                },\n                easeInBounce: function (x, t, b, c, d) {\n                    return c - this.easeOutBounce(x, d - t, 0, c, d) + b;\n                },\n                easeOutBounce: function (x, t, b, c, d) {\n                    var o = 7.5625;\n                    if ((t /= d) < (1 / 2.75)) {\n                        return c * (o * t * t) + b;\n                    } else if (t < (2 / 2.75)) {\n                        return c * (o * (t -= (1.5 / 2.75)) * t + .75) + b;\n                    } else if (t < (2.5 / 2.75)) {\n                        return c * (o * (t -= (2.25 / 2.75)) * t + .9375) + b;\n                    } else {\n                        return c * (o * (t -= (2.625 / 2.75)) * t + .984375) + b;\n                    }\n                },\n                easeInOutBounce: function (x, t, b, c, d) {\n                    return (t < d / 2) ? this.easeInBounce(x, t * 2, 0, c, d) * .5 + b : this.easeOutBounce(x, t * 2 - d, 0, c, d) * .5 + c * .5 + b;\n                }\n            };\n            /*\n             *\n             * TERMS OF USE - EASING EQUATIONS\n             * \n             * Open source under the BSD License. \n             * \n             * Copyright Â© 2001 Robert Penner\n             * All rights reserved.\n             * \n             * Redistribution and use in source and binary forms, with or without modification, \n             * are permitted provided that the following conditions are met:\n             * \n             * Redistributions of source code must retain the above copyright notice, this list of \n             * conditions and the following disclaimer.\n             * Redistributions in binary form must reproduce the above copyright notice, this list \n             * of conditions and the following disclaimer in the documentation and/or other materials \n             * provided with the distribution.\n             * \n             * Neither the name of the author nor the names of contributors may be used to endorse \n             * or promote products derived from this software without specific prior written permission.\n             * \n             * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY \n             * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n             * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n             *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n             *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n             *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED \n             * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n             *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED \n             * OF THE POSSIBILITY OF SUCH DAMAGE. \n             *\n             */\n        })();\n        var FRAMEWORK = (function () {\n            var _rnothtmlwhite = (/[^\\x20\\t\\r\\n\\f]+/g);\n            var _strSpace = ' ';\n            var _strEmpty = '';\n            var _strScrollLeft = 'scrollLeft';\n            var _strScrollTop = 'scrollTop';\n            var _animations = [];\n            var _type = COMPATIBILITY.type;\n            var _cssNumber = {\n                animationIterationCount: true,\n                columnCount: true,\n                fillOpacity: true,\n                flexGrow: true,\n                flexShrink: true,\n                fontWeight: true,\n                lineHeight: true,\n                opacity: true,\n                order: true,\n                orphans: true,\n                widows: true,\n                zIndex: true,\n                zoom: true\n            };\n\n            function extend() {\n                var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {},\n                    i = 1,\n                    length = arguments[LEXICON.l],\n                    deep = false;\n\n                // Handle a deep copy situation\n                if (_type(target) == TYPES.b) {\n                    deep = target;\n                    target = arguments[1] || {};\n                    // skip the boolean and the target\n                    i = 2;\n                }\n\n                // Handle case when target is a string or something (possible in deep copy)\n                if (_type(target) != TYPES.o && !_type(target) == TYPES.f) {\n                    target = {};\n                }\n\n                // extend jQuery itself if only one argument is passed\n                if (length === i) {\n                    target = FakejQuery;\n                    --i;\n                }\n\n                for (; i < length; i++) {\n                    // Only deal with non-null/undefined values\n                    if ((options = arguments[i]) != null) {\n                        // Extend the base object\n                        for (name in options) {\n                            src = target[name];\n                            copy = options[name];\n\n                            // Prevent never-ending loop\n                            if (target === copy) {\n                                continue;\n                            }\n\n                            // Recurse if we're merging plain objects or arrays\n                            if (deep && copy && (isPlainObject(copy) || (copyIsArray = COMPATIBILITY.isA(copy)))) {\n                                if (copyIsArray) {\n                                    copyIsArray = false;\n                                    clone = src && COMPATIBILITY.isA(src) ? src : [];\n\n                                } else {\n                                    clone = src && isPlainObject(src) ? src : {};\n                                }\n\n                                // Never move original objects, clone them\n                                target[name] = extend(deep, clone, copy);\n\n                                // Don't bring in undefined values\n                            } else if (copy !== undefined) {\n                                target[name] = copy;\n                            }\n                        }\n                    }\n                }\n\n                // Return the modified object\n                return target;\n            };\n\n            function inArray(item, arr, fromIndex) {\n                for (var i = fromIndex || 0; i < arr[LEXICON.l]; i++)\n                    if (arr[i] === item)\n                        return i;\n                return -1;\n            }\n\n            function isFunction(obj) {\n                return _type(obj) == TYPES.f;\n            };\n\n            function isEmptyObject(obj) {\n                for (var name in obj)\n                    return false;\n                return true;\n            };\n\n            function isPlainObject(obj) {\n                if (!obj || _type(obj) != TYPES.o)\n                    return false;\n\n                var key;\n                var proto = LEXICON.p;\n                var hasOwnProperty = Object[proto].hasOwnProperty;\n                var hasOwnConstructor = hasOwnProperty.call(obj, 'constructor');\n                var hasIsPrototypeOf = obj.constructor && obj.constructor[proto] && hasOwnProperty.call(obj.constructor[proto], 'isPrototypeOf');\n\n                if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {\n                    return false;\n                }\n\n\n                for (key in obj) { /**/ }\n\n                return _type(key) == TYPES.u || hasOwnProperty.call(obj, key);\n            };\n\n            function each(obj, callback) {\n                var i = 0;\n\n                if (isArrayLike(obj)) {\n                    for (; i < obj[LEXICON.l]; i++) {\n                        if (callback.call(obj[i], i, obj[i]) === false)\n                            break;\n                    }\n                }\n                else {\n                    for (i in obj) {\n                        if (callback.call(obj[i], i, obj[i]) === false)\n                            break;\n                    }\n                }\n\n                return obj;\n            };\n\n            function isArrayLike(obj) {\n                var length = !!obj && [LEXICON.l] in obj && obj[LEXICON.l];\n                var t = _type(obj);\n                return isFunction(t) ? false : (t == TYPES.a || length === 0 || _type(length) == TYPES.n && length > 0 && (length - 1) in obj);\n            }\n\n            function stripAndCollapse(value) {\n                var tokens = value.match(_rnothtmlwhite) || [];\n                return tokens.join(_strSpace);\n            }\n\n            function matches(elem, selector) {\n                var nodeList = (elem.parentNode || document).querySelectorAll(selector) || [];\n                var i = nodeList[LEXICON.l];\n\n                while (i--)\n                    if (nodeList[i] == elem)\n                        return true;\n\n                return false;\n            }\n\n            function insertAdjacentElement(el, strategy, child) {\n                if (COMPATIBILITY.isA(child)) {\n                    for (var i = 0; i < child[LEXICON.l]; i++)\n                        insertAdjacentElement(el, strategy, child[i]);\n                }\n                else if (_type(child) == TYPES.s)\n                    el.insertAdjacentHTML(strategy, child);\n                else\n                    el.insertAdjacentElement(strategy, child.nodeType ? child : child[0]);\n            }\n\n            function setCSSVal(el, prop, val) {\n                try {\n                    if (el[LEXICON.s][prop] !== undefined)\n                        el[LEXICON.s][prop] = parseCSSVal(prop, val);\n                } catch (e) { }\n            }\n\n            function parseCSSVal(prop, val) {\n                if (!_cssNumber[prop.toLowerCase()] && _type(val) == TYPES.n)\n                    val += 'px';\n                return val;\n            }\n\n            function startNextAnimationInQ(animObj, removeFromQ) {\n                var index;\n                var nextAnim;\n                if (removeFromQ !== false)\n                    animObj.q.splice(0, 1);\n                if (animObj.q[LEXICON.l] > 0) {\n                    nextAnim = animObj.q[0];\n                    animate(animObj.el, nextAnim.props, nextAnim.duration, nextAnim.easing, nextAnim.complete, true);\n                }\n                else {\n                    index = inArray(animObj, _animations);\n                    if (index > -1)\n                        _animations.splice(index, 1);\n                }\n            }\n\n            function setAnimationValue(el, prop, value) {\n                if (prop === _strScrollLeft || prop === _strScrollTop)\n                    el[prop] = value;\n                else\n                    setCSSVal(el, prop, value);\n            }\n\n            function animate(el, props, options, easing, complete, guaranteedNext) {\n                var hasOptions = isPlainObject(options);\n                var from = {};\n                var to = {};\n                var i = 0;\n                var key;\n                var animObj;\n                var start;\n                var progress;\n                var step;\n                var specialEasing;\n                var duration;\n                if (hasOptions) {\n                    easing = options.easing;\n                    start = options.start;\n                    progress = options.progress;\n                    step = options.step;\n                    specialEasing = options.specialEasing;\n                    complete = options.complete;\n                    duration = options.duration;\n                }\n                else\n                    duration = options;\n                specialEasing = specialEasing || {};\n                duration = duration || 400;\n                easing = easing || 'swing';\n                guaranteedNext = guaranteedNext || false;\n\n                for (; i < _animations[LEXICON.l]; i++) {\n                    if (_animations[i].el === el) {\n                        animObj = _animations[i];\n                        break;\n                    }\n                }\n\n                if (!animObj) {\n                    animObj = {\n                        el: el,\n                        q: []\n                    };\n                    _animations.push(animObj);\n                }\n\n                for (key in props) {\n                    if (key === _strScrollLeft || key === _strScrollTop)\n                        from[key] = el[key];\n                    else\n                        from[key] = FakejQuery(el).css(key);\n                }\n\n                for (key in from) {\n                    if (from[key] !== props[key] && props[key] !== undefined)\n                        to[key] = props[key];\n                }\n\n                if (!isEmptyObject(to)) {\n                    var timeNow;\n                    var end;\n                    var percent;\n                    var fromVal;\n                    var toVal;\n                    var easedVal;\n                    var timeStart;\n                    var frame;\n                    var elapsed;\n                    var qPos = guaranteedNext ? 0 : inArray(qObj, animObj.q);\n                    var qObj = {\n                        props: to,\n                        duration: hasOptions ? options : duration,\n                        easing: easing,\n                        complete: complete\n                    };\n                    if (qPos === -1) {\n                        qPos = animObj.q[LEXICON.l];\n                        animObj.q.push(qObj);\n                    }\n\n                    if (qPos === 0) {\n                        if (duration > 0) {\n                            timeStart = COMPATIBILITY.now();\n                            frame = function () {\n                                timeNow = COMPATIBILITY.now();\n                                elapsed = (timeNow - timeStart);\n                                end = qObj.stop || elapsed >= duration;\n                                percent = 1 - ((MATH.max(0, timeStart + duration - timeNow) / duration) || 0);\n\n                                for (key in to) {\n                                    fromVal = parseFloat(from[key]);\n                                    toVal = parseFloat(to[key]);\n                                    easedVal = (toVal - fromVal) * EASING[specialEasing[key] || easing](percent, percent * duration, 0, 1, duration) + fromVal;\n                                    setAnimationValue(el, key, easedVal);\n                                    if (isFunction(step)) {\n                                        step(easedVal, {\n                                            elem: el,\n                                            prop: key,\n                                            start: fromVal,\n                                            now: easedVal,\n                                            end: toVal,\n                                            pos: percent,\n                                            options: {\n                                                easing: easing,\n                                                speacialEasing: specialEasing,\n                                                duration: duration,\n                                                complete: complete,\n                                                step: step\n                                            },\n                                            startTime: timeStart\n                                        });\n                                    }\n                                }\n\n                                if (isFunction(progress))\n                                    progress({}, percent, MATH.max(0, duration - elapsed));\n\n                                if (end) {\n                                    startNextAnimationInQ(animObj);\n                                    if (isFunction(complete))\n                                        complete();\n                                }\n                                else\n                                    qObj.frame = COMPATIBILITY.rAF()(frame);\n                            };\n                            qObj.frame = COMPATIBILITY.rAF()(frame);\n                        }\n                        else {\n                            for (key in to)\n                                setAnimationValue(el, key, to[key]);\n                            startNextAnimationInQ(animObj);\n                        }\n                    }\n                }\n                else if (guaranteedNext)\n                    startNextAnimationInQ(animObj);\n            }\n\n            function stop(el, clearQ, jumpToEnd) {\n                var animObj;\n                var qObj;\n                var key;\n                var i = 0;\n                for (; i < _animations[LEXICON.l]; i++) {\n                    animObj = _animations[i];\n                    if (animObj.el === el) {\n                        if (animObj.q[LEXICON.l] > 0) {\n                            qObj = animObj.q[0];\n                            qObj.stop = true;\n                            COMPATIBILITY.cAF()(qObj.frame);\n                            animObj.q.splice(0, 1);\n\n                            if (jumpToEnd)\n                                for (key in qObj.props)\n                                    setAnimationValue(el, key, qObj.props[key]);\n\n                            if (clearQ)\n                                animObj.q = [];\n                            else\n                                startNextAnimationInQ(animObj, false);\n                        }\n                        break;\n                    }\n                }\n            }\n\n            function elementIsVisible(el) {\n                return !!(el[LEXICON.oW] || el[LEXICON.oH] || el.getClientRects()[LEXICON.l]);\n            }\n\n            function FakejQuery(selector) {\n                if (arguments[LEXICON.l] === 0)\n                    return this;\n\n                var base = new FakejQuery();\n                var elements = selector;\n                var i = 0;\n                var elms;\n                var el;\n\n                if (_type(selector) == TYPES.s) {\n                    elements = [];\n                    if (selector.charAt(0) === '<') {\n                        el = document.createElement('div');\n                        el.innerHTML = selector;\n                        elms = el.children;\n                    }\n                    else {\n                        elms = document.querySelectorAll(selector);\n                    }\n\n                    for (; i < elms[LEXICON.l]; i++)\n                        elements.push(elms[i]);\n                }\n\n                if (elements) {\n                    if (_type(elements) != TYPES.s && (!isArrayLike(elements) || elements === window || elements === elements.self))\n                        elements = [elements];\n\n                    for (i = 0; i < elements[LEXICON.l]; i++)\n                        base[i] = elements[i];\n\n                    base[LEXICON.l] = elements[LEXICON.l];\n                }\n\n                return base;\n            };\n\n            FakejQuery[LEXICON.p] = {\n\n                //EVENTS:\n\n                on: function (eventName, handler) {\n                    eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [_strEmpty];\n\n                    var eventNameLength = eventName[LEXICON.l];\n                    var i = 0;\n                    var el;\n                    return this.each(function () {\n                        el = this;\n                        try {\n                            if (el.addEventListener) {\n                                for (; i < eventNameLength; i++)\n                                    el.addEventListener(eventName[i], handler);\n                            }\n                            else if (el.detachEvent) {\n                                for (; i < eventNameLength; i++)\n                                    el.attachEvent('on' + eventName[i], handler);\n                            }\n                        } catch (e) { }\n                    });\n                },\n\n                off: function (eventName, handler) {\n                    eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [_strEmpty];\n\n                    var eventNameLength = eventName[LEXICON.l];\n                    var i = 0;\n                    var el;\n                    return this.each(function () {\n                        el = this;\n                        try {\n                            if (el.removeEventListener) {\n                                for (; i < eventNameLength; i++)\n                                    el.removeEventListener(eventName[i], handler);\n                            }\n                            else if (el.detachEvent) {\n                                for (; i < eventNameLength; i++)\n                                    el.detachEvent('on' + eventName[i], handler);\n                            }\n                        } catch (e) { }\n                    });\n                },\n\n                one: function (eventName, handler) {\n                    eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [_strEmpty];\n                    return this.each(function () {\n                        var el = FakejQuery(this);\n                        FakejQuery.each(eventName, function (i, oneEventName) {\n                            var oneHandler = function (e) {\n                                handler.call(this, e);\n                                el.off(oneEventName, oneHandler);\n                            };\n                            el.on(oneEventName, oneHandler);\n                        });\n                    });\n                },\n\n                trigger: function (eventName) {\n                    var el;\n                    var event;\n                    return this.each(function () {\n                        el = this;\n                        if (document.createEvent) {\n                            event = document.createEvent('HTMLEvents');\n                            event.initEvent(eventName, true, false);\n                            el.dispatchEvent(event);\n                        }\n                        else {\n                            el.fireEvent('on' + eventName);\n                        }\n                    });\n                },\n\n                //DOM NODE INSERTING / REMOVING:\n\n                append: function (child) {\n                    return this.each(function () { insertAdjacentElement(this, 'beforeend', child); });\n                },\n\n                prepend: function (child) {\n                    return this.each(function () { insertAdjacentElement(this, 'afterbegin', child); });\n                },\n\n                before: function (child) {\n                    return this.each(function () { insertAdjacentElement(this, 'beforebegin', child); });\n                },\n\n                after: function (child) {\n                    return this.each(function () { insertAdjacentElement(this, 'afterend', child); });\n                },\n\n                remove: function () {\n                    return this.each(function () {\n                        var el = this;\n                        var parentNode = el.parentNode;\n                        if (parentNode != null)\n                            parentNode.removeChild(el);\n                    });\n                },\n\n                unwrap: function () {\n                    var parents = [];\n                    var i;\n                    var el;\n                    var parent;\n\n                    this.each(function () {\n                        parent = this.parentNode;\n                        if (inArray(parent, parents) === - 1)\n                            parents.push(parent);\n                    });\n\n                    for (i = 0; i < parents[LEXICON.l]; i++) {\n                        el = parents[i];\n                        parent = el.parentNode;\n                        while (el.firstChild)\n                            parent.insertBefore(el.firstChild, el);\n                        parent.removeChild(el);\n                    }\n\n                    return this;\n                },\n\n                wrapAll: function (wrapperHTML) {\n                    var i;\n                    var nodes = this;\n                    var wrapper = FakejQuery(wrapperHTML)[0];\n                    var deepest = wrapper;\n                    var parent = nodes[0].parentNode;\n                    var previousSibling = nodes[0].previousSibling;\n                    while (deepest.childNodes[LEXICON.l] > 0)\n                        deepest = deepest.childNodes[0];\n\n                    for (i = 0; nodes[LEXICON.l] - i; deepest.firstChild === nodes[0] && i++)\n                        deepest.appendChild(nodes[i]);\n\n                    var nextSibling = previousSibling ? previousSibling.nextSibling : parent.firstChild;\n                    parent.insertBefore(wrapper, nextSibling);\n\n                    return this;\n                },\n\n                wrapInner: function (wrapperHTML) {\n                    return this.each(function () {\n                        var el = FakejQuery(this);\n                        var contents = el.contents();\n\n                        if (contents[LEXICON.l])\n                            contents.wrapAll(wrapperHTML);\n                        else\n                            el.append(wrapperHTML);\n                    });\n                },\n\n                wrap: function (wrapperHTML) {\n                    return this.each(function () { FakejQuery(this).wrapAll(wrapperHTML); });\n                },\n\n\n                //DOM NODE MANIPULATION / INFORMATION:\n\n                css: function (styles, val) {\n                    var el;\n                    var key;\n                    var cptStyle;\n                    var getCptStyle = window.getComputedStyle;\n                    if (_type(styles) == TYPES.s) {\n                        if (val === undefined) {\n                            el = this[0];\n                            cptStyle = getCptStyle ? getCptStyle(el, null) : el.currentStyle[styles];\n\n                            //https://bugzilla.mozilla.org/show_bug.cgi?id=548397 can be null sometimes if iframe with display: none (firefox only!)\n                            return getCptStyle ? cptStyle != null ? cptStyle.getPropertyValue(styles) : el[LEXICON.s][styles] : cptStyle;\n                        }\n                        else {\n                            return this.each(function () {\n                                setCSSVal(this, styles, val);\n                            });\n                        }\n                    }\n                    else {\n                        return this.each(function () {\n                            for (key in styles)\n                                setCSSVal(this, key, styles[key]);\n                        });\n                    }\n                },\n\n                hasClass: function (className) {\n                    var elem, i = 0;\n                    var classNamePrepared = _strSpace + className + _strSpace;\n                    var classList;\n\n                    while ((elem = this[i++])) {\n                        classList = elem.classList;\n                        if (classList && classList.contains(className))\n                            return true;\n                        else if (elem.nodeType === 1 && (_strSpace + stripAndCollapse(elem.className + _strEmpty) + _strSpace).indexOf(classNamePrepared) > -1)\n                            return true;\n                    }\n\n                    return false;\n                },\n\n                addClass: function (className) {\n                    var classes;\n                    var elem;\n                    var cur;\n                    var curValue;\n                    var clazz;\n                    var finalValue;\n                    var supportClassList;\n                    var elmClassList;\n                    var i = 0;\n                    var v = 0;\n\n                    if (className) {\n                        classes = className.match(_rnothtmlwhite) || [];\n\n                        while ((elem = this[i++])) {\n                            elmClassList = elem.classList;\n                            if (supportClassList === undefined)\n                                supportClassList = elmClassList !== undefined;\n\n                            if (supportClassList) {\n                                while ((clazz = classes[v++]))\n                                    elmClassList.add(clazz);\n                            }\n                            else {\n                                curValue = elem.className + _strEmpty;\n                                cur = elem.nodeType === 1 && (_strSpace + stripAndCollapse(curValue) + _strSpace);\n\n                                if (cur) {\n                                    while ((clazz = classes[v++]))\n                                        if (cur.indexOf(_strSpace + clazz + _strSpace) < 0)\n                                            cur += clazz + _strSpace;\n\n                                    finalValue = stripAndCollapse(cur);\n                                    if (curValue !== finalValue)\n                                        elem.className = finalValue;\n                                }\n                            }\n                        }\n                    }\n\n                    return this;\n                },\n\n                removeClass: function (className) {\n                    var classes;\n                    var elem;\n                    var cur;\n                    var curValue;\n                    var clazz;\n                    var finalValue;\n                    var supportClassList;\n                    var elmClassList;\n                    var i = 0;\n                    var v = 0;\n\n                    if (className) {\n                        classes = className.match(_rnothtmlwhite) || [];\n\n                        while ((elem = this[i++])) {\n                            elmClassList = elem.classList;\n                            if (supportClassList === undefined)\n                                supportClassList = elmClassList !== undefined;\n\n                            if (supportClassList) {\n                                while ((clazz = classes[v++]))\n                                    elmClassList.remove(clazz);\n                            }\n                            else {\n                                curValue = elem.className + _strEmpty;\n                                cur = elem.nodeType === 1 && (_strSpace + stripAndCollapse(curValue) + _strSpace);\n\n                                if (cur) {\n                                    while ((clazz = classes[v++]))\n                                        while (cur.indexOf(_strSpace + clazz + _strSpace) > -1)\n                                            cur = cur.replace(_strSpace + clazz + _strSpace, _strSpace);\n\n                                    finalValue = stripAndCollapse(cur);\n                                    if (curValue !== finalValue)\n                                        elem.className = finalValue;\n                                }\n                            }\n                        }\n                    }\n\n                    return this;\n                },\n\n                hide: function () {\n                    return this.each(function () { this[LEXICON.s].display = 'none'; });\n                },\n\n                show: function () {\n                    return this.each(function () { this[LEXICON.s].display = 'block'; });\n                },\n\n                attr: function (attrName, value) {\n                    var i = 0;\n                    var el;\n                    while (el = this[i++]) {\n                        if (value === undefined)\n                            return el.getAttribute(attrName);\n                        el.setAttribute(attrName, value);\n                    }\n                    return this;\n                },\n\n                removeAttr: function (attrName) {\n                    return this.each(function () { this.removeAttribute(attrName); });\n                },\n\n                offset: function () {\n                    var el = this[0];\n                    var rect = el[LEXICON.bCR]();\n                    var scrollLeft = window.pageXOffset || document.documentElement[_strScrollLeft];\n                    var scrollTop = window.pageYOffset || document.documentElement[_strScrollTop];\n                    return {\n                        top: rect.top + scrollTop,\n                        left: rect.left + scrollLeft\n                    };\n                },\n\n                position: function () {\n                    var el = this[0];\n                    return {\n                        top: el.offsetTop,\n                        left: el.offsetLeft\n                    };\n                },\n\n                scrollLeft: function (value) {\n                    var i = 0;\n                    var el;\n                    while (el = this[i++]) {\n                        if (value === undefined)\n                            return el[_strScrollLeft];\n                        el[_strScrollLeft] = value;\n                    }\n                    return this;\n                },\n\n                scrollTop: function (value) {\n                    var i = 0;\n                    var el;\n                    while (el = this[i++]) {\n                        if (value === undefined)\n                            return el[_strScrollTop];\n                        el[_strScrollTop] = value;\n                    }\n                    return this;\n                },\n\n                val: function (value) {\n                    var el = this[0];\n                    if (!value)\n                        return el.value;\n                    el.value = value;\n                    return this;\n                },\n\n\n                //DOM TRAVERSAL / FILTERING:\n\n                first: function () {\n                    return this.eq(0);\n                },\n\n                last: function () {\n                    return this.eq(-1);\n                },\n\n                eq: function (index) {\n                    return FakejQuery(this[index >= 0 ? index : this[LEXICON.l] + index]);\n                },\n\n                find: function (selector) {\n                    var children = [];\n                    var i;\n                    this.each(function () {\n                        var el = this;\n                        var ch = el.querySelectorAll(selector);\n                        for (i = 0; i < ch[LEXICON.l]; i++)\n                            children.push(ch[i]);\n                    });\n                    return FakejQuery(children);\n                },\n\n                children: function (selector) {\n                    var children = [];\n                    var el;\n                    var ch;\n                    var i;\n\n                    this.each(function () {\n                        ch = this.children;\n                        for (i = 0; i < ch[LEXICON.l]; i++) {\n                            el = ch[i];\n                            if (selector) {\n                                if ((el.matches && el.matches(selector)) || matches(el, selector))\n                                    children.push(el);\n                            }\n                            else\n                                children.push(el);\n                        }\n                    });\n                    return FakejQuery(children);\n                },\n\n                parent: function (selector) {\n                    var parents = [];\n                    var parent;\n                    this.each(function () {\n                        parent = this.parentNode;\n                        if (selector ? FakejQuery(parent).is(selector) : true)\n                            parents.push(parent);\n                    });\n                    return FakejQuery(parents);\n                },\n\n                is: function (selector) {\n\n                    var el;\n                    var i;\n                    for (i = 0; i < this[LEXICON.l]; i++) {\n                        el = this[i];\n                        if (selector === ':visible')\n                            return elementIsVisible(el);\n                        if (selector === ':hidden')\n                            return !elementIsVisible(el);\n                        if ((el.matches && el.matches(selector)) || matches(el, selector))\n                            return true;\n                    }\n                    return false;\n                },\n\n                contents: function () {\n                    var contents = [];\n                    var childs;\n                    var i;\n\n                    this.each(function () {\n                        childs = this.childNodes;\n                        for (i = 0; i < childs[LEXICON.l]; i++)\n                            contents.push(childs[i]);\n                    });\n\n                    return FakejQuery(contents);\n                },\n\n                each: function (callback) {\n                    return each(this, callback);\n                },\n\n\n                //ANIMATION:\n\n                animate: function (props, duration, easing, complete) {\n                    return this.each(function () { animate(this, props, duration, easing, complete); });\n                },\n\n                stop: function (clearQ, jump) {\n                    return this.each(function () { stop(this, clearQ, jump); });\n                }\n            };\n\n            extend(FakejQuery, {\n                extend: extend,\n                inArray: inArray,\n                isEmptyObject: isEmptyObject,\n                isPlainObject: isPlainObject,\n                each: each\n            });\n\n            return FakejQuery;\n        })();\n        var INSTANCES = (function () {\n            var _targets = [];\n            var _instancePropertyString = '__overlayScrollbars__';\n\n            /**\n             * Register, unregister or get a certain (or all) instances.\n             * Register: Pass the target and the instance.\n             * Unregister: Pass the target and null.\n             * Get Instance: Pass the target from which the instance shall be got.\n             * Get Targets: Pass no arguments.\n             * @param target The target to which the instance shall be registered / from which the instance shall be unregistered / the instance shall be got\n             * @param instance The instance.\n             * @returns {*|void} Returns the instance from the given target.\n             */\n            return function (target, instance) {\n                var argLen = arguments[LEXICON.l];\n                if (argLen < 1) {\n                    //return all targets\n                    return _targets;\n                }\n                else {\n                    if (instance) {\n                        //register instance\n                        target[_instancePropertyString] = instance;\n                        _targets.push(target);\n                    }\n                    else {\n                        var index = COMPATIBILITY.inA(target, _targets);\n                        if (index > -1) {\n                            if (argLen > 1) {\n                                //unregister instance\n                                delete target[_instancePropertyString];\n                                _targets.splice(index, 1);\n                            }\n                            else {\n                                //get instance from target\n                                return _targets[index][_instancePropertyString];\n                            }\n                        }\n                    }\n                }\n            }\n        })();\n        var PLUGIN = (function () {\n            var _plugin;\n            var _pluginsGlobals;\n            var _pluginsAutoUpdateLoop;\n            var _pluginsExtensions = [];\n            var _pluginsOptions = (function () {\n                var type = COMPATIBILITY.type;\n                var possibleTemplateTypes = [\n                    TYPES.b, //boolean\n                    TYPES.n, //number\n                    TYPES.s, //string\n                    TYPES.a, //array\n                    TYPES.o, //object\n                    TYPES.f, //function\n                    TYPES.z  //null\n                ];\n                var restrictedStringsSplit = ' ';\n                var restrictedStringsPossibilitiesSplit = ':';\n                var classNameAllowedValues = [TYPES.z, TYPES.s];\n                var numberAllowedValues = TYPES.n;\n                var booleanNullAllowedValues = [TYPES.z, TYPES.b];\n                var booleanTrueTemplate = [true, TYPES.b];\n                var booleanFalseTemplate = [false, TYPES.b];\n                var callbackTemplate = [null, [TYPES.z, TYPES.f]];\n                var updateOnLoadTemplate = [['img'], [TYPES.s, TYPES.a, TYPES.z]];\n                var inheritedAttrsTemplate = [['style', 'class'], [TYPES.s, TYPES.a, TYPES.z]];\n                var resizeAllowedValues = 'n:none b:both h:horizontal v:vertical';\n                var overflowBehaviorAllowedValues = 'v-h:visible-hidden v-s:visible-scroll s:scroll h:hidden';\n                var scrollbarsVisibilityAllowedValues = 'v:visible h:hidden a:auto';\n                var scrollbarsAutoHideAllowedValues = 'n:never s:scroll l:leave m:move';\n                var optionsDefaultsAndTemplate = {\n                    className: ['os-theme-dark', classNameAllowedValues],                //null || string\n                    resize: ['none', resizeAllowedValues],                               //none || both  || horizontal || vertical || n || b || h || v\n                    sizeAutoCapable: booleanTrueTemplate,                                //true || false\n                    clipAlways: booleanTrueTemplate,                                     //true || false\n                    normalizeRTL: booleanTrueTemplate,                                   //true || false\n                    paddingAbsolute: booleanFalseTemplate,                               //true || false\n                    autoUpdate: [null, booleanNullAllowedValues],                        //true || false || null\n                    autoUpdateInterval: [33, numberAllowedValues],                       //number\n                    updateOnLoad: updateOnLoadTemplate,                                  //string || array || null\n                    nativeScrollbarsOverlaid: {\n                        showNativeScrollbars: booleanFalseTemplate,                      //true || false\n                        initialize: booleanTrueTemplate                                  //true || false\n                    },\n                    overflowBehavior: {\n                        x: ['scroll', overflowBehaviorAllowedValues],                    //visible-hidden  || visible-scroll || hidden || scroll || v-h || v-s || h || s\n                        y: ['scroll', overflowBehaviorAllowedValues]                     //visible-hidden  || visible-scroll || hidden || scroll || v-h || v-s || h || s\n                    },\n                    scrollbars: {\n                        visibility: ['auto', scrollbarsVisibilityAllowedValues],         //visible || hidden || auto || v || h || a\n                        autoHide: ['never', scrollbarsAutoHideAllowedValues],            //never || scroll || leave || move || n || s || l || m\n                        autoHideDelay: [800, numberAllowedValues],                       //number\n                        dragScrolling: booleanTrueTemplate,                              //true || false\n                        clickScrolling: booleanFalseTemplate,                            //true || false\n                        touchSupport: booleanTrueTemplate,                               //true || false\n                        snapHandle: booleanFalseTemplate                                 //true || false\n                    },\n                    textarea: {\n                        dynWidth: booleanFalseTemplate,                                  //true || false\n                        dynHeight: booleanFalseTemplate,                                 //true || false\n                        inheritedAttrs: inheritedAttrsTemplate                           //string || array || null\n                    },\n                    callbacks: {\n                        onInitialized: callbackTemplate,                                 //null || function\n                        onInitializationWithdrawn: callbackTemplate,                     //null || function\n                        onDestroyed: callbackTemplate,                                   //null || function\n                        onScrollStart: callbackTemplate,                                 //null || function\n                        onScroll: callbackTemplate,                                      //null || function\n                        onScrollStop: callbackTemplate,                                  //null || function\n                        onOverflowChanged: callbackTemplate,                             //null || function\n                        onOverflowAmountChanged: callbackTemplate,                       //null || function\n                        onDirectionChanged: callbackTemplate,                            //null || function\n                        onContentSizeChanged: callbackTemplate,                          //null || function\n                        onHostSizeChanged: callbackTemplate,                             //null || function\n                        onUpdated: callbackTemplate                                      //null || function\n                    }\n                };\n                var convert = function (template) {\n                    var recursive = function (obj) {\n                        var key;\n                        var val;\n                        var valType;\n                        for (key in obj) {\n                            if (!obj[LEXICON.hOP](key))\n                                continue;\n                            val = obj[key];\n                            valType = type(val);\n                            if (valType == TYPES.a)\n                                obj[key] = val[template ? 1 : 0];\n                            else if (valType == TYPES.o)\n                                obj[key] = recursive(val);\n                        }\n                        return obj;\n                    };\n                    return recursive(FRAMEWORK.extend(true, {}, optionsDefaultsAndTemplate));\n                };\n\n                return {\n                    _defaults: convert(),\n\n                    _template: convert(true),\n\n                    /**\n                     * Validates the passed object by the passed template.\n                     * @param obj The object which shall be validated.\n                     * @param template The template which defines the allowed values and types.\n                     * @param writeErrors True if errors shall be logged to the console.\n                     * @param diffObj If a object is passed then only valid differences to this object will be returned.\n                     * @returns {{}} A object which contains two objects called \"default\" and \"prepared\" which contains only the valid properties of the passed original object and discards not different values compared to the passed diffObj.\n                     */\n                    _validate: function (obj, template, writeErrors, diffObj) {\n                        var validatedOptions = {};\n                        var validatedOptionsPrepared = {};\n                        var objectCopy = FRAMEWORK.extend(true, {}, obj);\n                        var inArray = FRAMEWORK.inArray;\n                        var isEmptyObj = FRAMEWORK.isEmptyObject;\n                        var checkObjectProps = function (data, template, diffData, validatedOptions, validatedOptionsPrepared, prevPropName) {\n                            for (var prop in template) {\n                                if (template[LEXICON.hOP](prop) && data[LEXICON.hOP](prop)) {\n                                    var isValid = false;\n                                    var isDiff = false;\n                                    var templateValue = template[prop];\n                                    var templateValueType = type(templateValue);\n                                    var templateIsComplex = templateValueType == TYPES.o;\n                                    var templateTypes = !COMPATIBILITY.isA(templateValue) ? [templateValue] : templateValue;\n                                    var dataDiffValue = diffData[prop];\n                                    var dataValue = data[prop];\n                                    var dataValueType = type(dataValue);\n                                    var propPrefix = prevPropName ? prevPropName + '.' : '';\n                                    var error = \"The option \\\"\" + propPrefix + prop + \"\\\" wasn't set, because\";\n                                    var errorPossibleTypes = [];\n                                    var errorRestrictedStrings = [];\n                                    var restrictedStringValuesSplit;\n                                    var restrictedStringValuesPossibilitiesSplit;\n                                    var isRestrictedValue;\n                                    var mainPossibility;\n                                    var currType;\n                                    var i;\n                                    var v;\n                                    var j;\n\n                                    dataDiffValue = dataDiffValue === undefined ? {} : dataDiffValue;\n\n                                    //if the template has a object as value, it means that the options are complex (verschachtelt)\n                                    if (templateIsComplex && dataValueType == TYPES.o) {\n                                        validatedOptions[prop] = {};\n                                        validatedOptionsPrepared[prop] = {};\n                                        checkObjectProps(dataValue, templateValue, dataDiffValue, validatedOptions[prop], validatedOptionsPrepared[prop], propPrefix + prop);\n                                        FRAMEWORK.each([data, validatedOptions, validatedOptionsPrepared], function (index, value) {\n                                            if (isEmptyObj(value[prop])) {\n                                                delete value[prop];\n                                            }\n                                        });\n                                    }\n                                    else if (!templateIsComplex) {\n                                        for (i = 0; i < templateTypes[LEXICON.l]; i++) {\n                                            currType = templateTypes[i];\n                                            templateValueType = type(currType);\n                                            //if currtype is string and starts with restrictedStringPrefix and end with restrictedStringSuffix\n                                            isRestrictedValue = templateValueType == TYPES.s && inArray(currType, possibleTemplateTypes) === -1;\n                                            if (isRestrictedValue) {\n                                                errorPossibleTypes.push(TYPES.s);\n\n                                                //split it into a array which contains all possible values for example: [\"y:yes\", \"n:no\", \"m:maybe\"]\n                                                restrictedStringValuesSplit = currType.split(restrictedStringsSplit);\n                                                errorRestrictedStrings = errorRestrictedStrings.concat(restrictedStringValuesSplit);\n                                                for (v = 0; v < restrictedStringValuesSplit[LEXICON.l]; v++) {\n                                                    //split the possible values into their possibiliteis for example: [\"y\", \"yes\"] -> the first is always the mainPossibility\n                                                    restrictedStringValuesPossibilitiesSplit = restrictedStringValuesSplit[v].split(restrictedStringsPossibilitiesSplit);\n                                                    mainPossibility = restrictedStringValuesPossibilitiesSplit[0];\n                                                    for (j = 0; j < restrictedStringValuesPossibilitiesSplit[LEXICON.l]; j++) {\n                                                        //if any possibility matches with the dataValue, its valid\n                                                        if (dataValue === restrictedStringValuesPossibilitiesSplit[j]) {\n                                                            isValid = true;\n                                                            break;\n                                                        }\n                                                    }\n                                                    if (isValid)\n                                                        break;\n                                                }\n                                            }\n                                            else {\n                                                errorPossibleTypes.push(currType);\n\n                                                if (dataValueType === currType) {\n                                                    isValid = true;\n                                                    break;\n                                                }\n                                            }\n                                        }\n\n                                        if (isValid) {\n                                            isDiff = dataValue !== dataDiffValue;\n\n                                            if (isDiff)\n                                                validatedOptions[prop] = dataValue;\n\n                                            if (isRestrictedValue ? inArray(dataDiffValue, restrictedStringValuesPossibilitiesSplit) < 0 : isDiff)\n                                                validatedOptionsPrepared[prop] = isRestrictedValue ? mainPossibility : dataValue;\n                                        }\n                                        else if (writeErrors) {\n                                            console.warn(error + \" it doesn't accept the type [ \" + dataValueType.toUpperCase() + \" ] with the value of \\\"\" + dataValue + \"\\\".\\r\\n\" +\n                                                \"Accepted types are: [ \" + errorPossibleTypes.join(', ').toUpperCase() + \" ].\" +\n                                                (errorRestrictedStrings[length] > 0 ? \"\\r\\nValid strings are: [ \" + errorRestrictedStrings.join(', ').split(restrictedStringsPossibilitiesSplit).join(', ') + \" ].\" : ''));\n                                        }\n                                        delete data[prop];\n                                    }\n                                }\n                            }\n                        };\n                        checkObjectProps(objectCopy, template, diffObj || {}, validatedOptions, validatedOptionsPrepared);\n\n                        //add values which aren't specified in the template to the finished validated object to prevent them from being discarded\n                        /*\n                        if(keepForeignProps) {\n                            FRAMEWORK.extend(true, validatedOptions, objectCopy);\n                            FRAMEWORK.extend(true, validatedOptionsPrepared, objectCopy);\n                        }\n                        */\n\n                        if (!isEmptyObj(objectCopy) && writeErrors)\n                            console.warn('The following options are discarded due to invalidity:\\r\\n' + window.JSON.stringify(objectCopy, null, 2));\n\n                        return {\n                            _default: validatedOptions,\n                            _prepared: validatedOptionsPrepared\n                        };\n                    }\n                }\n            }());\n\n            /**\n             * Initializes the object which contains global information about the plugin and each instance of it.\n             */\n            function initOverlayScrollbarsStatics() {\n                if (!_pluginsGlobals)\n                    _pluginsGlobals = new OverlayScrollbarsGlobals(_pluginsOptions._defaults);\n                if (!_pluginsAutoUpdateLoop)\n                    _pluginsAutoUpdateLoop = new OverlayScrollbarsAutoUpdateLoop(_pluginsGlobals);\n            }\n\n            /**\n             * The global object for the OverlayScrollbars objects. It contains resources which every OverlayScrollbars object needs. This object is initialized only once: if the first OverlayScrollbars object gets initialized.\n             * @param defaultOptions\n             * @constructor\n             */\n            function OverlayScrollbarsGlobals(defaultOptions) {\n                var _base = this;\n                var strOverflow = 'overflow';\n                var strHidden = 'hidden';\n                var strScroll = 'scroll';\n                var bodyElement = FRAMEWORK('body');\n                var scrollbarDummyElement = FRAMEWORK('<div id=\"os-dummy-scrollbar-size\"><div></div></div>');\n                var scrollbarDummyElement0 = scrollbarDummyElement[0];\n                var dummyContainerChild = FRAMEWORK(scrollbarDummyElement.children('div').eq(0));\n\n                bodyElement.append(scrollbarDummyElement);\n                scrollbarDummyElement.hide().show(); //fix IE8 bug (incorrect measuring)\n\n                var nativeScrollbarSize = calcNativeScrollbarSize(scrollbarDummyElement0);\n                var nativeScrollbarIsOverlaid = {\n                    x: nativeScrollbarSize.x === 0,\n                    y: nativeScrollbarSize.y === 0\n                };\n                var msie = (function () {\n                    var ua = window.navigator.userAgent;\n                    var strIndexOf = 'indexOf';\n                    var strSubString = 'substring';\n                    var msie = ua[strIndexOf]('MSIE ');\n                    var trident = ua[strIndexOf]('Trident/');\n                    var edge = ua[strIndexOf]('Edge/');\n                    var rv = ua[strIndexOf]('rv:');\n                    var result;\n                    var parseIntFunc = parseInt;\n\n                    // IE 10 or older => return version number\n                    if (msie > 0)\n                        result = parseIntFunc(ua[strSubString](msie + 5, ua[strIndexOf]('.', msie)), 10);\n\n                    // IE 11 => return version number\n                    else if (trident > 0)\n                        result = parseIntFunc(ua[strSubString](rv + 3, ua[strIndexOf]('.', rv)), 10);\n\n                    // Edge (IE 12+) => return version number\n                    else if (edge > 0)\n                        result = parseIntFunc(ua[strSubString](edge + 5, ua[strIndexOf]('.', edge)), 10);\n\n                    // other browser\n                    return result;\n                })();\n\n                FRAMEWORK.extend(_base, {\n                    defaultOptions: defaultOptions,\n                    msie: msie,\n                    autoUpdateLoop: false,\n                    autoUpdateRecommended: !COMPATIBILITY.mO(),\n                    nativeScrollbarSize: nativeScrollbarSize,\n                    nativeScrollbarIsOverlaid: nativeScrollbarIsOverlaid,\n                    nativeScrollbarStyling: (function () {\n                        var result = false;\n                        scrollbarDummyElement.addClass('os-viewport-native-scrollbars-invisible');\n                        try {\n                            result = (scrollbarDummyElement.css('scrollbar-width') === 'none' && (msie > 9 || !msie)) || window.getComputedStyle(scrollbarDummyElement0, '::-webkit-scrollbar').getPropertyValue('display') === 'none';\n                        } catch (ex) { }\n\n                        //fix opera bug: scrollbar styles will only appear if overflow value is scroll or auto during the activation of the style.\n                        //and set overflow to scroll\n                        //scrollbarDummyElement.css(strOverflow, strHidden).hide().css(strOverflow, strScroll).show();\n                        //return (scrollbarDummyElement0[LEXICON.oH] - scrollbarDummyElement0[LEXICON.cH]) === 0 && (scrollbarDummyElement0[LEXICON.oW] - scrollbarDummyElement0[LEXICON.cW]) === 0;\n\n                        return result;\n                    })(),\n                    overlayScrollbarDummySize: { x: 30, y: 30 },\n                    cssCalc: VENDORS._cssPropertyValue('width', 'calc', '(1px)') || null,\n                    restrictedMeasuring: (function () {\n                        //https://bugzilla.mozilla.org/show_bug.cgi?id=1439305\n                        //since 1.11.0 always false -> fixed via CSS (hopefully)\n                        scrollbarDummyElement.css(strOverflow, strHidden);\n                        var scrollSize = {\n                            w: scrollbarDummyElement0[LEXICON.sW],\n                            h: scrollbarDummyElement0[LEXICON.sH]\n                        };\n                        scrollbarDummyElement.css(strOverflow, 'visible');\n                        var scrollSize2 = {\n                            w: scrollbarDummyElement0[LEXICON.sW],\n                            h: scrollbarDummyElement0[LEXICON.sH]\n                        };\n                        return (scrollSize.w - scrollSize2.w) !== 0 || (scrollSize.h - scrollSize2.h) !== 0;\n                    })(),\n                    rtlScrollBehavior: (function () {\n                        scrollbarDummyElement.css({ 'overflow-y': strHidden, 'overflow-x': strScroll, 'direction': 'rtl' }).scrollLeft(0);\n                        var dummyContainerOffset = scrollbarDummyElement.offset();\n                        var dummyContainerChildOffset = dummyContainerChild.offset();\n                        //https://github.com/KingSora/OverlayScrollbars/issues/187\n                        scrollbarDummyElement.scrollLeft(-999);\n                        var dummyContainerChildOffsetAfterScroll = dummyContainerChild.offset();\n                        return {\n                            //origin direction = determines if the zero scroll position is on the left or right side\n                            //'i' means 'invert' (i === true means that the axis must be inverted to be correct)\n                            //true = on the left side\n                            //false = on the right side\n                            i: dummyContainerOffset.left === dummyContainerChildOffset.left,\n                            //negative = determines if the maximum scroll is positive or negative\n                            //'n' means 'negate' (n === true means that the axis must be negated to be correct)\n                            //true = negative\n                            //false = positive\n                            n: dummyContainerChildOffset.left !== dummyContainerChildOffsetAfterScroll.left\n                        };\n                    })(),\n                    supportTransform: !!VENDORS._cssProperty('transform'),\n                    supportTransition: !!VENDORS._cssProperty('transition'),\n                    supportPassiveEvents: (function () {\n                        var supportsPassive = false;\n                        try {\n                            window.addEventListener('test', null, Object.defineProperty({}, 'passive', {\n                                get: function () {\n                                    supportsPassive = true;\n                                }\n                            }));\n                        } catch (e) { }\n                        return supportsPassive;\n                    })(),\n                    supportResizeObserver: !!COMPATIBILITY.rO(),\n                    supportMutationObserver: !!COMPATIBILITY.mO()\n                });\n\n                scrollbarDummyElement.removeAttr(LEXICON.s).remove();\n\n                //Catch zoom event:\n                (function () {\n                    if (nativeScrollbarIsOverlaid.x && nativeScrollbarIsOverlaid.y)\n                        return;\n\n                    var abs = MATH.abs;\n                    var windowWidth = COMPATIBILITY.wW();\n                    var windowHeight = COMPATIBILITY.wH();\n                    var windowDpr = getWindowDPR();\n                    var onResize = function () {\n                        if (INSTANCES().length > 0) {\n                            var newW = COMPATIBILITY.wW();\n                            var newH = COMPATIBILITY.wH();\n                            var deltaW = newW - windowWidth;\n                            var deltaH = newH - windowHeight;\n\n                            if (deltaW === 0 && deltaH === 0)\n                                return;\n\n                            var deltaWRatio = MATH.round(newW / (windowWidth / 100.0));\n                            var deltaHRatio = MATH.round(newH / (windowHeight / 100.0));\n                            var absDeltaW = abs(deltaW);\n                            var absDeltaH = abs(deltaH);\n                            var absDeltaWRatio = abs(deltaWRatio);\n                            var absDeltaHRatio = abs(deltaHRatio);\n                            var newDPR = getWindowDPR();\n\n                            var deltaIsBigger = absDeltaW > 2 && absDeltaH > 2;\n                            var difference = !differenceIsBiggerThanOne(absDeltaWRatio, absDeltaHRatio);\n                            var dprChanged = newDPR !== windowDpr && windowDpr > 0;\n                            var isZoom = deltaIsBigger && difference && dprChanged;\n                            var oldScrollbarSize = _base.nativeScrollbarSize;\n                            var newScrollbarSize;\n\n                            if (isZoom) {\n                                bodyElement.append(scrollbarDummyElement);\n                                newScrollbarSize = _base.nativeScrollbarSize = calcNativeScrollbarSize(scrollbarDummyElement[0]);\n                                scrollbarDummyElement.remove();\n                                if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {\n                                    FRAMEWORK.each(INSTANCES(), function () {\n                                        if (INSTANCES(this))\n                                            INSTANCES(this).update('zoom');\n                                    });\n                                }\n                            }\n\n                            windowWidth = newW;\n                            windowHeight = newH;\n                            windowDpr = newDPR;\n                        }\n                    };\n\n                    function differenceIsBiggerThanOne(valOne, valTwo) {\n                        var absValOne = abs(valOne);\n                        var absValTwo = abs(valTwo);\n                        return !(absValOne === absValTwo || absValOne + 1 === absValTwo || absValOne - 1 === absValTwo);\n                    }\n\n                    function getWindowDPR() {\n                        var dDPI = window.screen.deviceXDPI || 0;\n                        var sDPI = window.screen.logicalXDPI || 1;\n                        return window.devicePixelRatio || (dDPI / sDPI);\n                    }\n\n                    FRAMEWORK(window).on('resize', onResize);\n                })();\n\n                function calcNativeScrollbarSize(measureElement) {\n                    return {\n                        x: measureElement[LEXICON.oH] - measureElement[LEXICON.cH],\n                        y: measureElement[LEXICON.oW] - measureElement[LEXICON.cW]\n                    };\n                }\n            }\n\n            /**\n             * The object which manages the auto update loop for all OverlayScrollbars objects. This object is initialized only once: if the first OverlayScrollbars object gets initialized.\n             * @constructor\n             */\n            function OverlayScrollbarsAutoUpdateLoop(globals) {\n                var _base = this;\n                var _inArray = FRAMEWORK.inArray;\n                var _getNow = COMPATIBILITY.now;\n                var _strAutoUpdate = 'autoUpdate';\n                var _strAutoUpdateInterval = _strAutoUpdate + 'Interval';\n                var _strLength = LEXICON.l;\n                var _loopingInstances = [];\n                var _loopingInstancesIntervalCache = [];\n                var _loopIsActive = false;\n                var _loopIntervalDefault = 33;\n                var _loopInterval = _loopIntervalDefault;\n                var _loopTimeOld = _getNow();\n                var _loopID;\n\n\n                /**\n                 * The auto update loop which will run every 50 milliseconds or less if the update interval of a instance is lower than 50 milliseconds.\n                 */\n                var loop = function () {\n                    if (_loopingInstances[_strLength] > 0 && _loopIsActive) {\n                        _loopID = COMPATIBILITY.rAF()(function () {\n                            loop();\n                        });\n                        var timeNew = _getNow();\n                        var timeDelta = timeNew - _loopTimeOld;\n                        var lowestInterval;\n                        var instance;\n                        var instanceOptions;\n                        var instanceAutoUpdateAllowed;\n                        var instanceAutoUpdateInterval;\n                        var now;\n\n                        if (timeDelta > _loopInterval) {\n                            _loopTimeOld = timeNew - (timeDelta % _loopInterval);\n                            lowestInterval = _loopIntervalDefault;\n                            for (var i = 0; i < _loopingInstances[_strLength]; i++) {\n                                instance = _loopingInstances[i];\n                                if (instance !== undefined) {\n                                    instanceOptions = instance.options();\n                                    instanceAutoUpdateAllowed = instanceOptions[_strAutoUpdate];\n                                    instanceAutoUpdateInterval = MATH.max(1, instanceOptions[_strAutoUpdateInterval]);\n                                    now = _getNow();\n\n                                    if ((instanceAutoUpdateAllowed === true || instanceAutoUpdateAllowed === null) && (now - _loopingInstancesIntervalCache[i]) > instanceAutoUpdateInterval) {\n                                        instance.update('auto');\n                                        _loopingInstancesIntervalCache[i] = new Date(now += instanceAutoUpdateInterval);\n                                    }\n\n                                    lowestInterval = MATH.max(1, MATH.min(lowestInterval, instanceAutoUpdateInterval));\n                                }\n                            }\n                            _loopInterval = lowestInterval;\n                        }\n                    } else {\n                        _loopInterval = _loopIntervalDefault;\n                    }\n                };\n\n                /**\n                 * Add OverlayScrollbars instance to the auto update loop. Only successful if the instance isn't already added.\n                 * @param instance The instance which shall be updated in a loop automatically.\n                 */\n                _base.add = function (instance) {\n                    if (_inArray(instance, _loopingInstances) === -1) {\n                        _loopingInstances.push(instance);\n                        _loopingInstancesIntervalCache.push(_getNow());\n                        if (_loopingInstances[_strLength] > 0 && !_loopIsActive) {\n                            _loopIsActive = true;\n                            globals.autoUpdateLoop = _loopIsActive;\n                            loop();\n                        }\n                    }\n                };\n\n                /**\n                 * Remove OverlayScrollbars instance from the auto update loop. Only successful if the instance was added before.\n                 * @param instance The instance which shall be updated in a loop automatically.\n                 */\n                _base.remove = function (instance) {\n                    var index = _inArray(instance, _loopingInstances);\n                    if (index > -1) {\n                        //remove from loopingInstances list\n                        _loopingInstancesIntervalCache.splice(index, 1);\n                        _loopingInstances.splice(index, 1);\n\n                        //correct update loop behavior\n                        if (_loopingInstances[_strLength] === 0 && _loopIsActive) {\n                            _loopIsActive = false;\n                            globals.autoUpdateLoop = _loopIsActive;\n                            if (_loopID !== undefined) {\n                                COMPATIBILITY.cAF()(_loopID);\n                                _loopID = -1;\n                            }\n                        }\n                    }\n                };\n            }\n\n            /**\n             * A object which manages the scrollbars visibility of the target element.\n             * @param pluginTargetElement The element from which the scrollbars shall be hidden.\n             * @param options The custom options.\n             * @param extensions The custom extensions.\n             * @param globals\n             * @param autoUpdateLoop\n             * @returns {*}\n             * @constructor\n             */\n            function OverlayScrollbarsInstance(pluginTargetElement, options, extensions, globals, autoUpdateLoop) {\n                //shortcuts\n                var type = COMPATIBILITY.type;\n                var inArray = FRAMEWORK.inArray;\n                var each = FRAMEWORK.each;\n\n                //make correct instanceof\n                var _base = new _plugin();\n                var _frameworkProto = FRAMEWORK[LEXICON.p];\n\n                //if passed element is no HTML element: skip and return\n                if (!isHTMLElement(pluginTargetElement))\n                    return;\n\n                //if passed element is already initialized: set passed options if there are any and return its instance\n                if (INSTANCES(pluginTargetElement)) {\n                    var inst = INSTANCES(pluginTargetElement);\n                    inst.options(options);\n                    return inst;\n                }\n\n                //globals:\n                var _nativeScrollbarIsOverlaid;\n                var _overlayScrollbarDummySize;\n                var _rtlScrollBehavior;\n                var _autoUpdateRecommended;\n                var _msieVersion;\n                var _nativeScrollbarStyling;\n                var _cssCalc;\n                var _nativeScrollbarSize;\n                var _supportTransition;\n                var _supportTransform;\n                var _supportPassiveEvents;\n                var _supportResizeObserver;\n                var _supportMutationObserver;\n                var _restrictedMeasuring;\n\n                //general readonly:\n                var _initialized;\n                var _destroyed;\n                var _isTextarea;\n                var _isBody;\n                var _documentMixed;\n                var _domExists;\n\n                //general:\n                var _isBorderBox;\n                var _sizeAutoObserverAdded;\n                var _paddingX;\n                var _paddingY;\n                var _borderX;\n                var _borderY;\n                var _marginX;\n                var _marginY;\n                var _isRTL;\n                var _sleeping;\n                var _contentBorderSize = {};\n                var _scrollHorizontalInfo = {};\n                var _scrollVerticalInfo = {};\n                var _viewportSize = {};\n                var _nativeScrollbarMinSize = {};\n\n                //naming:\t\n                var _strMinusHidden = '-hidden';\n                var _strMarginMinus = 'margin-';\n                var _strPaddingMinus = 'padding-';\n                var _strBorderMinus = 'border-';\n                var _strTop = 'top';\n                var _strRight = 'right';\n                var _strBottom = 'bottom';\n                var _strLeft = 'left';\n                var _strMinMinus = 'min-';\n                var _strMaxMinus = 'max-';\n                var _strWidth = 'width';\n                var _strHeight = 'height';\n                var _strFloat = 'float';\n                var _strEmpty = '';\n                var _strAuto = 'auto';\n                var _strSync = 'sync';\n                var _strScroll = 'scroll';\n                var _strHundredPercent = '100%';\n                var _strX = 'x';\n                var _strY = 'y';\n                var _strDot = '.';\n                var _strSpace = ' ';\n                var _strScrollbar = 'scrollbar';\n                var _strMinusHorizontal = '-horizontal';\n                var _strMinusVertical = '-vertical';\n                var _strScrollLeft = _strScroll + 'Left';\n                var _strScrollTop = _strScroll + 'Top';\n                var _strMouseTouchDownEvent = 'mousedown touchstart';\n                var _strMouseTouchUpEvent = 'mouseup touchend touchcancel';\n                var _strMouseTouchMoveEvent = 'mousemove touchmove';\n                var _strMouseEnter = 'mouseenter';\n                var _strMouseLeave = 'mouseleave';\n                var _strKeyDownEvent = 'keydown';\n                var _strKeyUpEvent = 'keyup';\n                var _strSelectStartEvent = 'selectstart';\n                var _strTransitionEndEvent = 'transitionend webkitTransitionEnd oTransitionEnd';\n                var _strResizeObserverProperty = '__overlayScrollbarsRO__';\n\n                //class names:\t\n                var _cassNamesPrefix = 'os-';\n                var _classNameHTMLElement = _cassNamesPrefix + 'html';\n                var _classNameHostElement = _cassNamesPrefix + 'host';\n                var _classNameHostElementForeign = _classNameHostElement + '-foreign';\n                var _classNameHostTextareaElement = _classNameHostElement + '-textarea';\n                var _classNameHostScrollbarHorizontalHidden = _classNameHostElement + '-' + _strScrollbar + _strMinusHorizontal + _strMinusHidden;\n                var _classNameHostScrollbarVerticalHidden = _classNameHostElement + '-' + _strScrollbar + _strMinusVertical + _strMinusHidden;\n                var _classNameHostTransition = _classNameHostElement + '-transition';\n                var _classNameHostRTL = _classNameHostElement + '-rtl';\n                var _classNameHostResizeDisabled = _classNameHostElement + '-resize-disabled';\n                var _classNameHostScrolling = _classNameHostElement + '-scrolling';\n                var _classNameHostOverflow = _classNameHostElement + '-overflow';\n                var _classNameHostOverflow = _classNameHostElement + '-overflow';\n                var _classNameHostOverflowX = _classNameHostOverflow + '-x';\n                var _classNameHostOverflowY = _classNameHostOverflow + '-y';\n                var _classNameTextareaElement = _cassNamesPrefix + 'textarea';\n                var _classNameTextareaCoverElement = _classNameTextareaElement + '-cover';\n                var _classNamePaddingElement = _cassNamesPrefix + 'padding';\n                var _classNameViewportElement = _cassNamesPrefix + 'viewport';\n                var _classNameViewportNativeScrollbarsInvisible = _classNameViewportElement + '-native-scrollbars-invisible';\n                var _classNameViewportNativeScrollbarsOverlaid = _classNameViewportElement + '-native-scrollbars-overlaid';\n                var _classNameContentElement = _cassNamesPrefix + 'content';\n                var _classNameContentArrangeElement = _cassNamesPrefix + 'content-arrange';\n                var _classNameContentGlueElement = _cassNamesPrefix + 'content-glue';\n                var _classNameSizeAutoObserverElement = _cassNamesPrefix + 'size-auto-observer';\n                var _classNameResizeObserverElement = _cassNamesPrefix + 'resize-observer';\n                var _classNameResizeObserverItemElement = _cassNamesPrefix + 'resize-observer-item';\n                var _classNameResizeObserverItemFinalElement = _classNameResizeObserverItemElement + '-final';\n                var _classNameTextInherit = _cassNamesPrefix + 'text-inherit';\n                var _classNameScrollbar = _cassNamesPrefix + _strScrollbar;\n                var _classNameScrollbarTrack = _classNameScrollbar + '-track';\n                var _classNameScrollbarTrackOff = _classNameScrollbarTrack + '-off';\n                var _classNameScrollbarHandle = _classNameScrollbar + '-handle';\n                var _classNameScrollbarHandleOff = _classNameScrollbarHandle + '-off';\n                var _classNameScrollbarUnusable = _classNameScrollbar + '-unusable';\n                var _classNameScrollbarAutoHidden = _classNameScrollbar + '-' + _strAuto + _strMinusHidden;\n                var _classNameScrollbarCorner = _classNameScrollbar + '-corner';\n                var _classNameScrollbarCornerResize = _classNameScrollbarCorner + '-resize';\n                var _classNameScrollbarCornerResizeB = _classNameScrollbarCornerResize + '-both';\n                var _classNameScrollbarCornerResizeH = _classNameScrollbarCornerResize + _strMinusHorizontal;\n                var _classNameScrollbarCornerResizeV = _classNameScrollbarCornerResize + _strMinusVertical;\n                var _classNameScrollbarHorizontal = _classNameScrollbar + _strMinusHorizontal;\n                var _classNameScrollbarVertical = _classNameScrollbar + _strMinusVertical;\n                var _classNameDragging = _cassNamesPrefix + 'dragging';\n                var _classNameThemeNone = _cassNamesPrefix + 'theme-none';\n                var _classNamesDynamicDestroy = [\n                    _classNameViewportNativeScrollbarsInvisible,\n                    _classNameViewportNativeScrollbarsOverlaid,\n                    _classNameScrollbarTrackOff,\n                    _classNameScrollbarHandleOff,\n                    _classNameScrollbarUnusable,\n                    _classNameScrollbarAutoHidden,\n                    _classNameScrollbarCornerResize,\n                    _classNameScrollbarCornerResizeB,\n                    _classNameScrollbarCornerResizeH,\n                    _classNameScrollbarCornerResizeV,\n                    _classNameDragging].join(_strSpace);\n\n                //callbacks:\t\n                var _callbacksInitQeueue = [];\n\n                //attrs viewport shall inherit from target\t\n                var _viewportAttrsFromTarget = [LEXICON.ti];\n\n                //options:\t\n                var _defaultOptions;\n                var _currentOptions;\n                var _currentPreparedOptions;\n\n                //extensions:\t\n                var _extensions = {};\n                var _extensionsPrivateMethods = 'added removed on contract';\n\n                //update\t\n                var _lastUpdateTime;\n                var _swallowedUpdateHints = {};\n                var _swallowedUpdateTimeout;\n                var _swallowUpdateLag = 42;\n                var _updateOnLoadEventName = 'load';\n                var _updateOnLoadElms = [];\n\n                //DOM elements:\t\n                var _windowElement;\n                var _documentElement;\n                var _htmlElement;\n                var _bodyElement;\n                var _targetElement;                     //the target element of this OverlayScrollbars object\t\n                var _hostElement;                       //the host element of this OverlayScrollbars object -> may be the same as targetElement\t\n                var _sizeAutoObserverElement;           //observes size auto changes\t\n                var _sizeObserverElement;               //observes size and padding changes\t\n                var _paddingElement;                    //manages the padding\t\n                var _viewportElement;                   //is the viewport of our scrollbar model\t\n                var _contentElement;                    //the element which holds the content\t\n                var _contentArrangeElement;             //is needed for correct sizing of the content element (only if native scrollbars are overlays)\t\n                var _contentGlueElement;                //has always the size of the content element\t\n                var _textareaCoverElement;              //only applied if target is a textarea element. Used for correct size calculation and for prevention of uncontrolled scrolling\t\n                var _scrollbarCornerElement;\n                var _scrollbarHorizontalElement;\n                var _scrollbarHorizontalTrackElement;\n                var _scrollbarHorizontalHandleElement;\n                var _scrollbarVerticalElement;\n                var _scrollbarVerticalTrackElement;\n                var _scrollbarVerticalHandleElement;\n                var _windowElementNative;\n                var _documentElementNative;\n                var _targetElementNative;\n                var _hostElementNative;\n                var _sizeAutoObserverElementNative;\n                var _sizeObserverElementNative;\n                var _paddingElementNative;\n                var _viewportElementNative;\n                var _contentElementNative;\n\n                //Cache:\t\n                var _hostSizeCache;\n                var _contentScrollSizeCache;\n                var _arrangeContentSizeCache;\n                var _hasOverflowCache;\n                var _hideOverflowCache;\n                var _widthAutoCache;\n                var _heightAutoCache;\n                var _cssBoxSizingCache;\n                var _cssPaddingCache;\n                var _cssBorderCache;\n                var _cssMarginCache;\n                var _cssDirectionCache;\n                var _cssDirectionDetectedCache;\n                var _paddingAbsoluteCache;\n                var _clipAlwaysCache;\n                var _contentGlueSizeCache;\n                var _overflowBehaviorCache;\n                var _overflowAmountCache;\n                var _ignoreOverlayScrollbarHidingCache;\n                var _autoUpdateCache;\n                var _sizeAutoCapableCache;\n                var _contentElementScrollSizeChangeDetectedCache;\n                var _hostElementSizeChangeDetectedCache;\n                var _scrollbarsVisibilityCache;\n                var _scrollbarsAutoHideCache;\n                var _scrollbarsClickScrollingCache;\n                var _scrollbarsDragScrollingCache;\n                var _resizeCache;\n                var _normalizeRTLCache;\n                var _classNameCache;\n                var _oldClassName;\n                var _textareaAutoWrappingCache;\n                var _textareaInfoCache;\n                var _textareaSizeCache;\n                var _textareaDynHeightCache;\n                var _textareaDynWidthCache;\n                var _bodyMinSizeCache;\n                var _updateAutoCache = {};\n\n                //MutationObserver:\t\n                var _mutationObserverHost;\n                var _mutationObserverContent;\n                var _mutationObserverHostCallback;\n                var _mutationObserverContentCallback;\n                var _mutationObserversConnected;\n                var _mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];\n                var _mutationObserverAttrsHost = [LEXICON.i, LEXICON.c, LEXICON.s, 'open'].concat(_viewportAttrsFromTarget);\n\n                //events:\t\n                var _destroyEvents = [];\n\n                //textarea:\t\n                var _textareaHasFocus;\n\n                //scrollbars:\t\n                var _scrollbarsAutoHideTimeoutId;\n                var _scrollbarsAutoHideMoveTimeoutId;\n                var _scrollbarsAutoHideDelay;\n                var _scrollbarsAutoHideNever;\n                var _scrollbarsAutoHideScroll;\n                var _scrollbarsAutoHideMove;\n                var _scrollbarsAutoHideLeave;\n                var _scrollbarsHandleHovered;\n                var _scrollbarsHandlesDefineScrollPos;\n\n                //resize\t\n                var _resizeNone;\n                var _resizeBoth;\n                var _resizeHorizontal;\n                var _resizeVertical;\n\n\n                //==== Event Listener ====//\t\n\n                /**\t\n                 * Adds or removes a event listener from the given element. \t\n                 * @param element The element to which the event listener shall be applied or removed.\t\n                 * @param eventNames The name(s) of the events.\t\n                 * @param listener The method which shall be called.\t\n                 * @param remove True if the handler shall be removed, false or undefined if the handler shall be added.\t\n                 * @param passiveOrOptions The options for the event.\n                 */\n                function setupResponsiveEventListener(element, eventNames, listener, remove, passiveOrOptions) {\n                    var collected = COMPATIBILITY.isA(eventNames) && COMPATIBILITY.isA(listener);\n                    var method = remove ? 'removeEventListener' : 'addEventListener';\n                    var onOff = remove ? 'off' : 'on';\n                    var events = collected ? false : eventNames.split(_strSpace)\n                    var i = 0;\n\n                    var passiveOrOptionsIsObj = FRAMEWORK.isPlainObject(passiveOrOptions);\n                    var passive = (_supportPassiveEvents && (passiveOrOptionsIsObj ? (passiveOrOptions._passive) : passiveOrOptions)) || false;\n                    var capture = passiveOrOptionsIsObj && (passiveOrOptions._capture || false);\n                    var nativeParam = _supportPassiveEvents ? {\n                        passive: passive,\n                        capture: capture,\n                    } : capture;\n\n                    if (collected) {\n                        for (; i < eventNames[LEXICON.l]; i++)\n                            setupResponsiveEventListener(element, eventNames[i], listener[i], remove, passiveOrOptions);\n                    }\n                    else {\n                        for (; i < events[LEXICON.l]; i++) {\n                            if(_supportPassiveEvents) {\n                                element[0][method](events[i], listener, nativeParam);\n                            }\n                            else {\n                                element[onOff](events[i], listener);\n                            }     \n                        }\n                    }\n                }\n\n\n                function addDestroyEventListener(element, eventNames, listener, passive) {\n                    setupResponsiveEventListener(element, eventNames, listener, false, passive);\n                    _destroyEvents.push(COMPATIBILITY.bind(setupResponsiveEventListener, 0, element, eventNames, listener, true, passive));\n                }\n\n                //==== Resize Observer ====//\n\n                /**\n                 * Adds or removes a resize observer from the given element.\n                 * @param targetElement The element to which the resize observer shall be added or removed.\n                 * @param onElementResizedCallback The callback which is fired every time the resize observer registers a size change or false / undefined if the resizeObserver shall be removed.\n                 */\n                function setupResizeObserver(targetElement, onElementResizedCallback) {\n                    if (targetElement) {\n                        var resizeObserver = COMPATIBILITY.rO();\n                        var strAnimationStartEvent = 'animationstart mozAnimationStart webkitAnimationStart MSAnimationStart';\n                        var strChildNodes = 'childNodes';\n                        var constScroll = 3333333;\n                        var callback = function () {\n                            targetElement[_strScrollTop](constScroll)[_strScrollLeft](_isRTL ? _rtlScrollBehavior.n ? -constScroll : _rtlScrollBehavior.i ? 0 : constScroll : constScroll);\n                            onElementResizedCallback();\n                        };\n                        //add resize observer:\n                        if (onElementResizedCallback) {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.addClass('observed').append(generateDiv(_classNameResizeObserverElement)).contents()[0];\n                                var observer = element[_strResizeObserverProperty] = new resizeObserver(callback);\n                                observer.observe(element);\n                            }\n                            else {\n                                if (_msieVersion > 9 || !_autoUpdateRecommended) {\n                                    targetElement.prepend(\n                                        generateDiv(_classNameResizeObserverElement,\n                                            generateDiv({ c: _classNameResizeObserverItemElement, dir: 'ltr' },\n                                                generateDiv(_classNameResizeObserverItemElement,\n                                                    generateDiv(_classNameResizeObserverItemFinalElement)\n                                                ) +\n                                                generateDiv(_classNameResizeObserverItemElement,\n                                                    generateDiv({ c: _classNameResizeObserverItemFinalElement, style: 'width: 200%; height: 200%' })\n                                                )\n                                            )\n                                        )\n                                    );\n\n                                    var observerElement = targetElement[0][strChildNodes][0][strChildNodes][0];\n                                    var shrinkElement = FRAMEWORK(observerElement[strChildNodes][1]);\n                                    var expandElement = FRAMEWORK(observerElement[strChildNodes][0]);\n                                    var expandElementChild = FRAMEWORK(expandElement[0][strChildNodes][0]);\n                                    var widthCache = observerElement[LEXICON.oW];\n                                    var heightCache = observerElement[LEXICON.oH];\n                                    var isDirty;\n                                    var rAFId;\n                                    var currWidth;\n                                    var currHeight;\n                                    var factor = 2;\n                                    var nativeScrollbarSize = globals.nativeScrollbarSize; //care don't make changes to this object!!!\n                                    var reset = function () {\n                                        /*\n                                         var sizeResetWidth = observerElement[LEXICON.oW] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;\n                                         var sizeResetHeight = observerElement[LEXICON.oH] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;\n                                         var expandChildCSS = {};\n                                         expandChildCSS[_strWidth] = sizeResetWidth;\n                                         expandChildCSS[_strHeight] = sizeResetHeight;\n                                         expandElementChild.css(expandChildCSS);\n\n\n                                         expandElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);\n                                         shrinkElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);\n                                         */\n                                        expandElement[_strScrollLeft](constScroll)[_strScrollTop](constScroll);\n                                        shrinkElement[_strScrollLeft](constScroll)[_strScrollTop](constScroll);\n                                    };\n                                    var onResized = function () {\n                                        rAFId = 0;\n                                        if (!isDirty)\n                                            return;\n\n                                        widthCache = currWidth;\n                                        heightCache = currHeight;\n                                        callback();\n                                    };\n                                    var onScroll = function (event) {\n                                        currWidth = observerElement[LEXICON.oW];\n                                        currHeight = observerElement[LEXICON.oH];\n                                        isDirty = currWidth != widthCache || currHeight != heightCache;\n\n                                        if (event && isDirty && !rAFId) {\n                                            COMPATIBILITY.cAF()(rAFId);\n                                            rAFId = COMPATIBILITY.rAF()(onResized);\n                                        }\n                                        else if (!event)\n                                            onResized();\n\n                                        reset();\n                                        if (event) {\n                                            COMPATIBILITY.prvD(event);\n                                            COMPATIBILITY.stpP(event);\n                                        }\n                                        return false;\n                                    };\n                                    var expandChildCSS = {};\n                                    var observerElementCSS = {};\n\n                                    setTopRightBottomLeft(observerElementCSS, _strEmpty, [\n                                        -((nativeScrollbarSize.y + 1) * factor),\n                                        nativeScrollbarSize.x * -factor,\n                                        nativeScrollbarSize.y * -factor,\n                                        -((nativeScrollbarSize.x + 1) * factor)\n                                    ]);\n\n                                    FRAMEWORK(observerElement).css(observerElementCSS);\n                                    expandElement.on(_strScroll, onScroll);\n                                    shrinkElement.on(_strScroll, onScroll);\n                                    targetElement.on(strAnimationStartEvent, function () {\n                                        onScroll(false);\n                                    });\n                                    //lets assume that the divs will never be that large and a constant value is enough\n                                    expandChildCSS[_strWidth] = constScroll;\n                                    expandChildCSS[_strHeight] = constScroll;\n                                    expandElementChild.css(expandChildCSS);\n\n                                    reset();\n                                }\n                                else {\n                                    var attachEvent = _documentElementNative.attachEvent;\n                                    var isIE = _msieVersion !== undefined;\n                                    if (attachEvent) {\n                                        targetElement.prepend(generateDiv(_classNameResizeObserverElement));\n                                        findFirst(targetElement, _strDot + _classNameResizeObserverElement)[0].attachEvent('onresize', callback);\n                                    }\n                                    else {\n                                        var obj = _documentElementNative.createElement(TYPES.o);\n                                        obj.setAttribute(LEXICON.ti, '-1');\n                                        obj.setAttribute(LEXICON.c, _classNameResizeObserverElement);\n                                        obj.onload = function () {\n                                            var wnd = this.contentDocument.defaultView;\n                                            wnd.addEventListener('resize', callback);\n                                            wnd.document.documentElement.style.display = 'none';\n                                        };\n                                        obj.type = 'text/html';\n                                        if (isIE)\n                                            targetElement.prepend(obj);\n                                        obj.data = 'about:blank';\n                                        if (!isIE)\n                                            targetElement.prepend(obj);\n                                        targetElement.on(strAnimationStartEvent, callback);\n                                    }\n                                }\n                            }\n\n                            if (targetElement[0] === _sizeObserverElementNative) {\n                                var directionChanged = function () {\n                                    var dir = _hostElement.css('direction');\n                                    var css = {};\n                                    var scrollLeftValue = 0;\n                                    var result = false;\n                                    if (dir !== _cssDirectionDetectedCache) {\n                                        if (dir === 'ltr') {\n                                            css[_strLeft] = 0;\n                                            css[_strRight] = _strAuto;\n                                            scrollLeftValue = constScroll;\n                                        }\n                                        else {\n                                            css[_strLeft] = _strAuto;\n                                            css[_strRight] = 0;\n                                            scrollLeftValue = _rtlScrollBehavior.n ? -constScroll : _rtlScrollBehavior.i ? 0 : constScroll;\n                                        }\n                                        //execution order is important for IE!!!\n                                        _sizeObserverElement.children().eq(0).css(css);\n                                        _sizeObserverElement[_strScrollLeft](scrollLeftValue)[_strScrollTop](constScroll);\n                                        _cssDirectionDetectedCache = dir;\n                                        result = true;\n                                    }\n                                    return result;\n                                };\n                                directionChanged();\n                                addDestroyEventListener(targetElement, _strScroll, function (event) {\n                                    if (directionChanged())\n                                        update();\n                                    COMPATIBILITY.prvD(event);\n                                    COMPATIBILITY.stpP(event);\n                                    return false;\n                                });\n                            }\n                        }\n                        //remove resize observer:\n                        else {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.contents()[0];\n                                var resizeObserverObj = element[_strResizeObserverProperty];\n                                if (resizeObserverObj) {\n                                    resizeObserverObj.disconnect();\n                                    delete element[_strResizeObserverProperty];\n                                }\n                            }\n                            else {\n                                remove(targetElement.children(_strDot + _classNameResizeObserverElement).eq(0));\n                            }\n                        }\n                    }\n                }\n\n                /**\n                 * Freezes or unfreezes the given resize observer.\n                 * @param targetElement The element to which the target resize observer is applied.\n                 * @param freeze True if the resize observer shall be frozen, false otherwise.\n                 \n                function freezeResizeObserver(targetElement, freeze) {\n                    if (targetElement !== undefined) {\n                        if(freeze) {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.contents()[0];\n                                element[_strResizeObserverProperty].unobserve(element);\n                            }\n                            else {\n                                targetElement = targetElement.children(_strDot + _classNameResizeObserverElement).eq(0);\n                                var w = targetElement.css(_strWidth);\n                                var h = targetElement.css(_strHeight);\n                                var css = {};\n                                css[_strWidth] = w;\n                                css[_strHeight] = h;\n                                targetElement.css(css);\n                            }\n                        }\n                        else {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.contents()[0];\n                                element[_strResizeObserverProperty].observe(element);\n                            }\n                            else {\n                                var css = { };\n                                css[_strHeight] = _strEmpty;\n                                css[_strWidth] = _strEmpty;\n                                targetElement.children(_strDot + _classNameResizeObserverElement).eq(0).css(css);\n                            }\n                        }\n                    }\n                }\n                */\n\n\n                //==== Mutation Observers ====//\n\n                /**\n                 * Creates MutationObservers for the host and content Element if they are supported.\n                 */\n                function createMutationObservers() {\n                    if (_supportMutationObserver) {\n                        var mutationObserverContentLag = 11;\n                        var mutationObserver = COMPATIBILITY.mO();\n                        var contentLastUpdate = COMPATIBILITY.now();\n                        var mutationTarget;\n                        var mutationAttrName;\n                        var mutationIsClass;\n                        var oldMutationVal;\n                        var newClassVal;\n                        var hostClassNameRegex;\n                        var contentTimeout;\n                        var now;\n                        var sizeAuto;\n                        var action;\n\n                        _mutationObserverHostCallback = function (mutations) {\n\n                            var doUpdate = false;\n                            var doUpdateForce = false;\n                            var mutation;\n                            var mutatedAttrs = [];\n\n                            if (_initialized && !_sleeping) {\n                                each(mutations, function () {\n                                    mutation = this;\n                                    mutationTarget = mutation.target;\n                                    mutationAttrName = mutation.attributeName;\n                                    mutationIsClass = mutationAttrName === LEXICON.c;\n                                    oldMutationVal = mutation.oldValue;\n                                    newClassVal = mutationTarget.className;\n\n                                    if (_domExists && mutationIsClass && !doUpdateForce) {\n                                        // if old class value contains _classNameHostElementForeign and new class value doesn't\n                                        if (oldMutationVal.indexOf(_classNameHostElementForeign) > -1 && newClassVal.indexOf(_classNameHostElementForeign) < 0) {\n                                            hostClassNameRegex = createHostClassNameRegExp(true);\n                                            _hostElementNative.className = newClassVal.split(_strSpace).concat(oldMutationVal.split(_strSpace).filter(function (name) {\n                                                return name.match(hostClassNameRegex);\n                                            })).join(_strSpace);\n                                            doUpdate = doUpdateForce = true;\n                                        }\n                                    }\n\n                                    if (!doUpdate) {\n                                        doUpdate = mutationIsClass\n                                            ? hostClassNamesChanged(oldMutationVal, newClassVal)\n                                            : mutationAttrName === LEXICON.s\n                                                ? oldMutationVal !== mutationTarget[LEXICON.s].cssText\n                                                : true;\n                                    }\n\n                                    mutatedAttrs.push(mutationAttrName);\n                                });\n\n                                updateViewportAttrsFromTarget(mutatedAttrs);\n\n                                if (doUpdate)\n                                    _base.update(doUpdateForce || _strAuto);\n                            }\n                            return doUpdate;\n                        };\n                        _mutationObserverContentCallback = function (mutations) {\n                            var doUpdate = false;\n                            var mutation;\n\n                            if (_initialized && !_sleeping) {\n                                each(mutations, function () {\n                                    mutation = this;\n                                    doUpdate = isUnknownMutation(mutation);\n                                    return !doUpdate;\n                                });\n\n                                if (doUpdate) {\n                                    now = COMPATIBILITY.now();\n                                    sizeAuto = (_heightAutoCache || _widthAutoCache);\n                                    action = function () {\n                                        if (!_destroyed) {\n                                            contentLastUpdate = now;\n\n                                            //if cols, rows or wrap attr was changed\n                                            if (_isTextarea)\n                                                textareaUpdate();\n\n                                            if (sizeAuto)\n                                                update();\n                                            else\n                                                _base.update(_strAuto);\n                                        }\n                                    };\n                                    clearTimeout(contentTimeout);\n                                    if (mutationObserverContentLag <= 0 || now - contentLastUpdate > mutationObserverContentLag || !sizeAuto)\n                                        action();\n                                    else\n                                        contentTimeout = setTimeout(action, mutationObserverContentLag);\n                                }\n                            }\n                            return doUpdate;\n                        }\n\n                        _mutationObserverHost = new mutationObserver(_mutationObserverHostCallback);\n                        _mutationObserverContent = new mutationObserver(_mutationObserverContentCallback);\n                    }\n                }\n\n                /**\n                 * Connects the MutationObservers if they are supported.\n                 */\n                function connectMutationObservers() {\n                    if (_supportMutationObserver && !_mutationObserversConnected) {\n                        _mutationObserverHost.observe(_hostElementNative, {\n                            attributes: true,\n                            attributeOldValue: true,\n                            attributeFilter: _mutationObserverAttrsHost\n                        });\n\n                        _mutationObserverContent.observe(_isTextarea ? _targetElementNative : _contentElementNative, {\n                            attributes: true,\n                            attributeOldValue: true,\n                            subtree: !_isTextarea,\n                            childList: !_isTextarea,\n                            characterData: !_isTextarea,\n                            attributeFilter: _isTextarea ? _mutationObserverAttrsTextarea : _mutationObserverAttrsHost\n                        });\n\n                        _mutationObserversConnected = true;\n                    }\n                }\n\n                /**\n                 * Disconnects the MutationObservers if they are supported.\n                 */\n                function disconnectMutationObservers() {\n                    if (_supportMutationObserver && _mutationObserversConnected) {\n                        _mutationObserverHost.disconnect();\n                        _mutationObserverContent.disconnect();\n\n                        _mutationObserversConnected = false;\n                    }\n                }\n\n\n                //==== Events of elements ====//\n\n                /**\n                 * This method gets called every time the host element gets resized. IMPORTANT: Padding changes are detected too!!\n                 * It refreshes the hostResizedEventArgs and the hostSizeResizeCache.\n                 * If there are any size changes, the update method gets called.\n                 */\n                function hostOnResized() {\n                    if (!_sleeping) {\n                        var changed;\n                        var hostSize = {\n                            w: _sizeObserverElementNative[LEXICON.sW],\n                            h: _sizeObserverElementNative[LEXICON.sH]\n                        };\n\n                        changed = checkCache(hostSize, _hostElementSizeChangeDetectedCache);\n                        _hostElementSizeChangeDetectedCache = hostSize;\n                        if (changed)\n                            update({ _hostSizeChanged: true });\n                    }\n                }\n\n                /**\n                 * The mouse enter event of the host element. This event is only needed for the autoHide feature.\n                 */\n                function hostOnMouseEnter() {\n                    if (_scrollbarsAutoHideLeave)\n                        refreshScrollbarsAutoHide(true);\n                }\n\n                /**\n                 * The mouse leave event of the host element. This event is only needed for the autoHide feature.\n                 */\n                function hostOnMouseLeave() {\n                    if (_scrollbarsAutoHideLeave && !_bodyElement.hasClass(_classNameDragging))\n                        refreshScrollbarsAutoHide(false);\n                }\n\n                /**\n                 * The mouse move event of the host element. This event is only needed for the autoHide \"move\" feature.\n                 */\n                function hostOnMouseMove() {\n                    if (_scrollbarsAutoHideMove) {\n                        refreshScrollbarsAutoHide(true);\n                        clearTimeout(_scrollbarsAutoHideMoveTimeoutId);\n                        _scrollbarsAutoHideMoveTimeoutId = setTimeout(function () {\n                            if (_scrollbarsAutoHideMove && !_destroyed)\n                                refreshScrollbarsAutoHide(false);\n                        }, 100);\n                    }\n                }\n\n                /**\n                 * Prevents text from deselection if attached to the document element on the mousedown event of a DOM element.\n                 * @param event The select start event.\n                 */\n                function documentOnSelectStart(event) {\n                    COMPATIBILITY.prvD(event);\n                    return false;\n                }\n\n                /**\t\n                 * A callback which will be called after a element has loaded.\t\n                 */\n                function updateOnLoadCallback(event) {\n                    var elm = FRAMEWORK(event.target);\n\n                    eachUpdateOnLoad(function (i, updateOnLoadSelector) {\n                        if (elm.is(updateOnLoadSelector)) {\n                            update({ _contentSizeChanged: true });\n                        }\n                    });\n                }\n\n                /**\n                * Adds or removes mouse & touch events of the host element. (for handling auto-hiding of the scrollbars)\n                * @param destroy Indicates whether the events shall be added or removed.\n                */\n                function setupHostMouseTouchEvents(destroy) {\n                    if (!destroy)\n                        setupHostMouseTouchEvents(true);\n\n                    setupResponsiveEventListener(_hostElement,\n                        _strMouseTouchMoveEvent.split(_strSpace)[0],\n                        hostOnMouseMove,\n                        (!_scrollbarsAutoHideMove || destroy), true);\n                    setupResponsiveEventListener(_hostElement,\n                        [_strMouseEnter, _strMouseLeave],\n                        [hostOnMouseEnter, hostOnMouseLeave],\n                        (!_scrollbarsAutoHideLeave || destroy), true);\n\n                    //if the plugin is initialized and the mouse is over the host element, make the scrollbars visible\n                    if (!_initialized && !destroy)\n                        _hostElement.one('mouseover', hostOnMouseEnter);\n                }\n\n\n                //==== Update Detection ====//\n\n                /**\n                 * Measures the min width and min height of the body element and refreshes the related cache.\n                 * @returns {boolean} True if the min width or min height has changed, false otherwise.\n                 */\n                function bodyMinSizeChanged() {\n                    var bodyMinSize = {};\n                    if (_isBody && _contentArrangeElement) {\n                        bodyMinSize.w = parseToZeroOrNumber(_contentArrangeElement.css(_strMinMinus + _strWidth));\n                        bodyMinSize.h = parseToZeroOrNumber(_contentArrangeElement.css(_strMinMinus + _strHeight));\n                        bodyMinSize.c = checkCache(bodyMinSize, _bodyMinSizeCache);\n                        bodyMinSize.f = true; //flag for \"measured at least once\"\n                    }\n                    _bodyMinSizeCache = bodyMinSize;\n                    return !!bodyMinSize.c;\n                }\n\n                /**\n                 * Returns true if the class names really changed (new class without plugin host prefix)\n                 * @param oldClassNames The old ClassName string or array.\n                 * @param newClassNames The new ClassName string or array.\n                 * @returns {boolean} True if the class names has really changed, false otherwise.\n                 */\n                function hostClassNamesChanged(oldClassNames, newClassNames) {\n                    var currClasses = typeof newClassNames == TYPES.s ? newClassNames.split(_strSpace) : [];\n                    var oldClasses = typeof oldClassNames == TYPES.s ? oldClassNames.split(_strSpace) : [];\n                    var diff = getArrayDifferences(oldClasses, currClasses);\n\n                    // remove none theme from diff list to prevent update\n                    var idx = inArray(_classNameThemeNone, diff);\n                    var i;\n                    var regex;\n\n                    if (idx > -1)\n                        diff.splice(idx, 1);\n\n                    if (diff[LEXICON.l] > 0) {\n                        regex = createHostClassNameRegExp(true, true);\n                        for (i = 0; i < diff.length; i++) {\n                            if (!diff[i].match(regex)) {\n                                return true;\n                            }\n                        }\n                    }\n                    return false;\n                }\n\n                /**\n                 * Returns true if the given mutation is not from a from the plugin generated element. If the target element is a textarea the mutation is always unknown.\n                 * @param mutation The mutation which shall be checked.\n                 * @returns {boolean} True if the mutation is from a unknown element, false otherwise.\n                 */\n                function isUnknownMutation(mutation) {\n                    var attributeName = mutation.attributeName;\n                    var mutationTarget = mutation.target;\n                    var mutationType = mutation.type;\n                    var strClosest = 'closest';\n\n                    if (mutationTarget === _contentElementNative)\n                        return attributeName === null;\n                    if (mutationType === 'attributes' && (attributeName === LEXICON.c || attributeName === LEXICON.s) && !_isTextarea) {\n                        //ignore className changes by the plugin\t\n                        if (attributeName === LEXICON.c && FRAMEWORK(mutationTarget).hasClass(_classNameHostElement))\n                            return hostClassNamesChanged(mutation.oldValue, mutationTarget.className);\n\n                        //only do it of browser support it natively\t\n                        if (typeof mutationTarget[strClosest] != TYPES.f)\n                            return true;\n                        if (mutationTarget[strClosest](_strDot + _classNameResizeObserverElement) !== null ||\n                            mutationTarget[strClosest](_strDot + _classNameScrollbar) !== null ||\n                            mutationTarget[strClosest](_strDot + _classNameScrollbarCorner) !== null)\n                            return false;\n                    }\n                    return true;\n                }\n\n                /**\n                 * Returns true if the content size was changed since the last time this method was called.\n                 * @returns {boolean} True if the content size was changed, false otherwise.\n                 */\n                function updateAutoContentSizeChanged() {\n                    if (_sleeping)\n                        return false;\n\n                    var contentMeasureElement = getContentMeasureElement();\n                    var textareaValueLength = _isTextarea && _widthAutoCache && !_textareaAutoWrappingCache ? _targetElement.val().length : 0;\n                    var setCSS = !_mutationObserversConnected && _widthAutoCache && !_isTextarea;\n                    var css = {};\n                    var float;\n                    var bodyMinSizeC;\n                    var changed;\n                    var contentElementScrollSize;\n\n                    if (setCSS) {\n                        float = _contentElement.css(_strFloat);\n                        css[_strFloat] = _isRTL ? _strRight : _strLeft;\n                        css[_strWidth] = _strAuto;\n                        _contentElement.css(css);\n                    }\n                    contentElementScrollSize = {\n                        w: contentMeasureElement[LEXICON.sW] + textareaValueLength,\n                        h: contentMeasureElement[LEXICON.sH] + textareaValueLength\n                    };\n                    if (setCSS) {\n                        css[_strFloat] = float;\n                        css[_strWidth] = _strHundredPercent;\n                        _contentElement.css(css);\n                    }\n\n                    bodyMinSizeC = bodyMinSizeChanged();\n                    changed = checkCache(contentElementScrollSize, _contentElementScrollSizeChangeDetectedCache);\n\n                    _contentElementScrollSizeChangeDetectedCache = contentElementScrollSize;\n\n                    return changed || bodyMinSizeC;\n                }\n\n                /**\n                 * Returns true when a attribute which the MutationObserver would observe has changed.  \n                 * @returns {boolean} True if one of the attributes which a MutationObserver would observe has changed, false or undefined otherwise.\n                 */\n                function meaningfulAttrsChanged() {\n                    if (_sleeping || _mutationObserversConnected)\n                        return;\n\n                    var elem;\n                    var curr;\n                    var cache;\n                    var changedAttrs = [];\n                    var checks = [\n                        {\n                            _elem: _hostElement,\n                            _attrs: _mutationObserverAttrsHost.concat(':visible')\n                        },\n                        {\n                            _elem: _isTextarea ? _targetElement : undefined,\n                            _attrs: _mutationObserverAttrsTextarea\n                        }\n                    ];\n\n                    each(checks, function (index, check) {\n                        elem = check._elem;\n                        if (elem) {\n                            each(check._attrs, function (index, attr) {\n                                curr = attr.charAt(0) === ':' ? elem.is(attr) : elem.attr(attr);\n                                cache = _updateAutoCache[attr];\n\n                                if (checkCache(curr, cache)) {\n                                    changedAttrs.push(attr);\n                                }\n\n                                _updateAutoCache[attr] = curr;\n                            });\n                        }\n                    });\n\n                    updateViewportAttrsFromTarget(changedAttrs);\n\n                    return changedAttrs[LEXICON.l] > 0;\n                }\n\n                /**\n                 * Checks is a CSS Property of a child element is affecting the scroll size of the content.\n                 * @param propertyName The CSS property name.\n                 * @returns {boolean} True if the property is affecting the content scroll size, false otherwise.\n                 */\n                function isSizeAffectingCSSProperty(propertyName) {\n                    if (!_initialized)\n                        return true;\n                    var flexGrow = 'flex-grow';\n                    var flexShrink = 'flex-shrink';\n                    var flexBasis = 'flex-basis';\n                    var affectingPropsX = [\n                        _strWidth,\n                        _strMinMinus + _strWidth,\n                        _strMaxMinus + _strWidth,\n                        _strMarginMinus + _strLeft,\n                        _strMarginMinus + _strRight,\n                        _strLeft,\n                        _strRight,\n                        'font-weight',\n                        'word-spacing',\n                        flexGrow,\n                        flexShrink,\n                        flexBasis\n                    ];\n                    var affectingPropsXContentBox = [\n                        _strPaddingMinus + _strLeft,\n                        _strPaddingMinus + _strRight,\n                        _strBorderMinus + _strLeft + _strWidth,\n                        _strBorderMinus + _strRight + _strWidth\n                    ];\n                    var affectingPropsY = [\n                        _strHeight,\n                        _strMinMinus + _strHeight,\n                        _strMaxMinus + _strHeight,\n                        _strMarginMinus + _strTop,\n                        _strMarginMinus + _strBottom,\n                        _strTop,\n                        _strBottom,\n                        'line-height',\n                        flexGrow,\n                        flexShrink,\n                        flexBasis\n                    ];\n                    var affectingPropsYContentBox = [\n                        _strPaddingMinus + _strTop,\n                        _strPaddingMinus + _strBottom,\n                        _strBorderMinus + _strTop + _strWidth,\n                        _strBorderMinus + _strBottom + _strWidth\n                    ];\n                    var _strS = 's';\n                    var _strVS = 'v-s';\n                    var checkX = _overflowBehaviorCache.x === _strS || _overflowBehaviorCache.x === _strVS;\n                    var checkY = _overflowBehaviorCache.y === _strS || _overflowBehaviorCache.y === _strVS;\n                    var sizeIsAffected = false;\n                    var checkPropertyName = function (arr, name) {\n                        for (var i = 0; i < arr[LEXICON.l]; i++) {\n                            if (arr[i] === name)\n                                return true;\n                        }\n                        return false;\n                    };\n\n                    if (checkY) {\n                        sizeIsAffected = checkPropertyName(affectingPropsY, propertyName);\n                        if (!sizeIsAffected && !_isBorderBox)\n                            sizeIsAffected = checkPropertyName(affectingPropsYContentBox, propertyName);\n                    }\n                    if (checkX && !sizeIsAffected) {\n                        sizeIsAffected = checkPropertyName(affectingPropsX, propertyName);\n                        if (!sizeIsAffected && !_isBorderBox)\n                            sizeIsAffected = checkPropertyName(affectingPropsXContentBox, propertyName);\n                    }\n                    return sizeIsAffected;\n                }\n\n\n                //==== Update ====//\n\n                /**\n                 * Sets the attribute values of the viewport element to the values from the target element.\n                 * The value of a attribute is only set if the attribute is whitelisted.\n                 * @attrs attrs The array of attributes which shall be set or undefined if all whitelisted shall be set.\n                 */\n                function updateViewportAttrsFromTarget(attrs) {\n                    attrs = attrs || _viewportAttrsFromTarget;\n                    each(attrs, function (index, attr) {\n                        if (COMPATIBILITY.inA(attr, _viewportAttrsFromTarget) > -1) {\n                            var targetAttr = _targetElement.attr(attr);\n                            if (type(targetAttr) == TYPES.s) {\n                                _viewportElement.attr(attr, targetAttr);\n                            }\n                            else {\n                                _viewportElement.removeAttr(attr);\n                            }\n                        }\n                    });\n                }\n\n                /**\n                 * Updates the variables and size of the textarea element, and manages the scroll on new line or new character.\n                 */\n                function textareaUpdate() {\n                    if (!_sleeping) {\n                        var wrapAttrOff = !_textareaAutoWrappingCache;\n                        var minWidth = _viewportSize.w;\n                        var minHeight = _viewportSize.h;\n                        var css = {};\n                        var doMeasure = _widthAutoCache || wrapAttrOff;\n                        var origWidth;\n                        var width;\n                        var origHeight;\n                        var height;\n\n                        //reset min size\n                        css[_strMinMinus + _strWidth] = _strEmpty;\n                        css[_strMinMinus + _strHeight] = _strEmpty;\n\n                        //set width auto\n                        css[_strWidth] = _strAuto;\n                        _targetElement.css(css);\n\n                        //measure width\n                        origWidth = _targetElementNative[LEXICON.oW];\n                        width = doMeasure ? MATH.max(origWidth, _targetElementNative[LEXICON.sW] - 1) : 1;\n                        /*width += (_widthAutoCache ? _marginX + (!_isBorderBox ? wrapAttrOff ? 0 : _paddingX + _borderX : 0) : 0);*/\n\n                        //set measured width\n                        css[_strWidth] = _widthAutoCache ? _strAuto /*width*/ : _strHundredPercent;\n                        css[_strMinMinus + _strWidth] = _strHundredPercent;\n\n                        //set height auto\n                        css[_strHeight] = _strAuto;\n                        _targetElement.css(css);\n\n                        //measure height\n                        origHeight = _targetElementNative[LEXICON.oH];\n                        height = MATH.max(origHeight, _targetElementNative[LEXICON.sH] - 1);\n\n                        //append correct size values\n                        css[_strWidth] = width;\n                        css[_strHeight] = height;\n                        _textareaCoverElement.css(css);\n\n                        //apply min width / min height to prevent textarea collapsing\n                        css[_strMinMinus + _strWidth] = minWidth /*+ (!_isBorderBox && _widthAutoCache ? _paddingX + _borderX : 0)*/;\n                        css[_strMinMinus + _strHeight] = minHeight /*+ (!_isBorderBox && _heightAutoCache ? _paddingY + _borderY : 0)*/;\n                        _targetElement.css(css);\n\n                        return {\n                            _originalWidth: origWidth,\n                            _originalHeight: origHeight,\n                            _dynamicWidth: width,\n                            _dynamicHeight: height\n                        };\n                    }\n                }\n\n                /**\n                 * Updates the plugin and DOM to the current options.\n                 * This method should only be called if a update is 100% required.\n                 * @param updateHints A objects which contains hints for this update:\n                 * {\n                 *   _hostSizeChanged : boolean,\n                 *   _contentSizeChanged : boolean,\n                 *   _force : boolean,                             == preventSwallowing\n                 *   _changedOptions : { },                        == preventSwallowing && preventSleep\n                *  }\n                 */\n                function update(updateHints) {\n                    clearTimeout(_swallowedUpdateTimeout);\n                    updateHints = updateHints || {};\n                    _swallowedUpdateHints._hostSizeChanged |= updateHints._hostSizeChanged;\n                    _swallowedUpdateHints._contentSizeChanged |= updateHints._contentSizeChanged;\n                    _swallowedUpdateHints._force |= updateHints._force;\n\n                    var now = COMPATIBILITY.now();\n                    var hostSizeChanged = !!_swallowedUpdateHints._hostSizeChanged;\n                    var contentSizeChanged = !!_swallowedUpdateHints._contentSizeChanged;\n                    var force = !!_swallowedUpdateHints._force;\n                    var changedOptions = updateHints._changedOptions;\n                    var swallow = _swallowUpdateLag > 0 && _initialized && !_destroyed && !force && !changedOptions && (now - _lastUpdateTime) < _swallowUpdateLag && (!_heightAutoCache && !_widthAutoCache);\n                    var displayIsHidden;\n\n                    if (swallow)\n                        _swallowedUpdateTimeout = setTimeout(update, _swallowUpdateLag);\n\n                    //abort update due to:\n                    //destroyed\n                    //swallowing\n                    //sleeping\n                    //host is hidden or has false display\n                    if (_destroyed || swallow || (_sleeping && !changedOptions) || (_initialized && !force && (displayIsHidden = _hostElement.is(':hidden'))) || _hostElement.css('display') === 'inline')\n                        return;\n\n                    _lastUpdateTime = now;\n                    _swallowedUpdateHints = {};\n\n                    //if scrollbar styling is possible and native scrollbars aren't overlaid the scrollbar styling will be applied which hides the native scrollbars completely.\n                    if (_nativeScrollbarStyling && !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)) {\n                        //native scrollbars are hidden, so change the values to zero\n                        _nativeScrollbarSize.x = 0;\n                        _nativeScrollbarSize.y = 0;\n                    }\n                    else {\n                        //refresh native scrollbar size (in case of zoom)\n                        _nativeScrollbarSize = extendDeep({}, globals.nativeScrollbarSize);\n                    }\n\n                    // Scrollbar padding is needed for firefox, because firefox hides scrollbar automatically if the size of the div is too small.\n                    // The calculation: [scrollbar size +3 *3]\n                    // (+3 because of possible decoration e.g. borders, margins etc., but only if native scrollbar is NOT a overlaid scrollbar)\n                    // (*3 because (1)increase / (2)decrease -button and (3)resize handle)\n                    _nativeScrollbarMinSize = {\n                        x: (_nativeScrollbarSize.x + (_nativeScrollbarIsOverlaid.x ? 0 : 3)) * 3,\n                        y: (_nativeScrollbarSize.y + (_nativeScrollbarIsOverlaid.y ? 0 : 3)) * 3\n                    };\n\n                    changedOptions = changedOptions || {};\n                    //freezeResizeObserver(_sizeObserverElement, true);\n                    //freezeResizeObserver(_sizeAutoObserverElement, true);\n\n                    var checkCacheAutoForce = function () {\n                        return checkCache.apply(this, [].slice.call(arguments).concat([force]));\n                    };\n\n                    //save current scroll offset\n                    var currScroll = {\n                        x: _viewportElement[_strScrollLeft](),\n                        y: _viewportElement[_strScrollTop]()\n                    };\n\n                    var currentPreparedOptionsScrollbars = _currentPreparedOptions.scrollbars;\n                    var currentPreparedOptionsTextarea = _currentPreparedOptions.textarea;\n\n                    //scrollbars visibility:\n                    var scrollbarsVisibility = currentPreparedOptionsScrollbars.visibility;\n                    var scrollbarsVisibilityChanged = checkCacheAutoForce(scrollbarsVisibility, _scrollbarsVisibilityCache);\n\n                    //scrollbars autoHide:\n                    var scrollbarsAutoHide = currentPreparedOptionsScrollbars.autoHide;\n                    var scrollbarsAutoHideChanged = checkCacheAutoForce(scrollbarsAutoHide, _scrollbarsAutoHideCache);\n\n                    //scrollbars click scrolling\n                    var scrollbarsClickScrolling = currentPreparedOptionsScrollbars.clickScrolling;\n                    var scrollbarsClickScrollingChanged = checkCacheAutoForce(scrollbarsClickScrolling, _scrollbarsClickScrollingCache);\n\n                    //scrollbars drag scrolling\n                    var scrollbarsDragScrolling = currentPreparedOptionsScrollbars.dragScrolling;\n                    var scrollbarsDragScrollingChanged = checkCacheAutoForce(scrollbarsDragScrolling, _scrollbarsDragScrollingCache);\n\n                    //className\n                    var className = _currentPreparedOptions.className;\n                    var classNameChanged = checkCacheAutoForce(className, _classNameCache);\n\n                    //resize\n                    var resize = _currentPreparedOptions.resize;\n                    var resizeChanged = checkCacheAutoForce(resize, _resizeCache) && !_isBody; //body can't be resized since the window itself acts as resize possibility.\n\n                    //paddingAbsolute\n                    var paddingAbsolute = _currentPreparedOptions.paddingAbsolute;\n                    var paddingAbsoluteChanged = checkCacheAutoForce(paddingAbsolute, _paddingAbsoluteCache);\n\n                    //clipAlways\n                    var clipAlways = _currentPreparedOptions.clipAlways;\n                    var clipAlwaysChanged = checkCacheAutoForce(clipAlways, _clipAlwaysCache);\n\n                    //sizeAutoCapable\n                    var sizeAutoCapable = _currentPreparedOptions.sizeAutoCapable && !_isBody; //body can never be size auto, because it shall be always as big as the viewport.\n                    var sizeAutoCapableChanged = checkCacheAutoForce(sizeAutoCapable, _sizeAutoCapableCache);\n\n                    //showNativeScrollbars\n                    var ignoreOverlayScrollbarHiding = _currentPreparedOptions.nativeScrollbarsOverlaid.showNativeScrollbars;\n                    var ignoreOverlayScrollbarHidingChanged = checkCacheAutoForce(ignoreOverlayScrollbarHiding, _ignoreOverlayScrollbarHidingCache);\n\n                    //autoUpdate\n                    var autoUpdate = _currentPreparedOptions.autoUpdate;\n                    var autoUpdateChanged = checkCacheAutoForce(autoUpdate, _autoUpdateCache);\n\n                    //overflowBehavior\n                    var overflowBehavior = _currentPreparedOptions.overflowBehavior;\n                    var overflowBehaviorChanged = checkCacheAutoForce(overflowBehavior, _overflowBehaviorCache, force);\n\n                    //dynWidth:\n                    var textareaDynWidth = currentPreparedOptionsTextarea.dynWidth;\n                    var textareaDynWidthChanged = checkCacheAutoForce(_textareaDynWidthCache, textareaDynWidth);\n\n                    //dynHeight:\n                    var textareaDynHeight = currentPreparedOptionsTextarea.dynHeight;\n                    var textareaDynHeightChanged = checkCacheAutoForce(_textareaDynHeightCache, textareaDynHeight);\n\n                    //scrollbars visibility\n                    _scrollbarsAutoHideNever = scrollbarsAutoHide === 'n';\n                    _scrollbarsAutoHideScroll = scrollbarsAutoHide === 's';\n                    _scrollbarsAutoHideMove = scrollbarsAutoHide === 'm';\n                    _scrollbarsAutoHideLeave = scrollbarsAutoHide === 'l';\n\n                    //scrollbars autoHideDelay\n                    _scrollbarsAutoHideDelay = currentPreparedOptionsScrollbars.autoHideDelay;\n\n                    //old className\n                    _oldClassName = _classNameCache;\n\n                    //resize\n                    _resizeNone = resize === 'n';\n                    _resizeBoth = resize === 'b';\n                    _resizeHorizontal = resize === 'h';\n                    _resizeVertical = resize === 'v';\n\n                    //normalizeRTL\n                    _normalizeRTLCache = _currentPreparedOptions.normalizeRTL;\n\n                    //ignore overlay scrollbar hiding\n                    ignoreOverlayScrollbarHiding = ignoreOverlayScrollbarHiding && (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y);\n\n                    //refresh options cache\n                    _scrollbarsVisibilityCache = scrollbarsVisibility;\n                    _scrollbarsAutoHideCache = scrollbarsAutoHide;\n                    _scrollbarsClickScrollingCache = scrollbarsClickScrolling;\n                    _scrollbarsDragScrollingCache = scrollbarsDragScrolling;\n                    _classNameCache = className;\n                    _resizeCache = resize;\n                    _paddingAbsoluteCache = paddingAbsolute;\n                    _clipAlwaysCache = clipAlways;\n                    _sizeAutoCapableCache = sizeAutoCapable;\n                    _ignoreOverlayScrollbarHidingCache = ignoreOverlayScrollbarHiding;\n                    _autoUpdateCache = autoUpdate;\n                    _overflowBehaviorCache = extendDeep({}, overflowBehavior);\n                    _textareaDynWidthCache = textareaDynWidth;\n                    _textareaDynHeightCache = textareaDynHeight;\n                    _hasOverflowCache = _hasOverflowCache || { x: false, y: false };\n\n                    //set correct class name to the host element\n                    if (classNameChanged) {\n                        removeClass(_hostElement, _oldClassName + _strSpace + _classNameThemeNone);\n                        addClass(_hostElement, className !== undefined && className !== null && className.length > 0 ? className : _classNameThemeNone);\n                    }\n\n                    //set correct auto Update\n                    if (autoUpdateChanged) {\n                        if (autoUpdate === true || (autoUpdate === null && _autoUpdateRecommended)) {\n                            disconnectMutationObservers();\n                            autoUpdateLoop.add(_base);\n                        }\n                        else {\n                            autoUpdateLoop.remove(_base);\n                            connectMutationObservers();\n                        }\n                    }\n\n                    //activate or deactivate size auto capability\n                    if (sizeAutoCapableChanged) {\n                        if (sizeAutoCapable) {\n                            if (_contentGlueElement) {\n                                _contentGlueElement.show();\n                            }\n                            else {\n                                _contentGlueElement = FRAMEWORK(generateDiv(_classNameContentGlueElement));\n                                _paddingElement.before(_contentGlueElement);\n                            }\n                            if (_sizeAutoObserverAdded) {\n                                _sizeAutoObserverElement.show();\n                            }\n                            else {\n                                _sizeAutoObserverElement = FRAMEWORK(generateDiv(_classNameSizeAutoObserverElement));\n                                _sizeAutoObserverElementNative = _sizeAutoObserverElement[0];\n\n                                _contentGlueElement.before(_sizeAutoObserverElement);\n                                var oldSize = { w: -1, h: -1 };\n                                setupResizeObserver(_sizeAutoObserverElement, function () {\n                                    var newSize = {\n                                        w: _sizeAutoObserverElementNative[LEXICON.oW],\n                                        h: _sizeAutoObserverElementNative[LEXICON.oH]\n                                    };\n                                    if (checkCache(newSize, oldSize)) {\n                                        if (_initialized && (_heightAutoCache && newSize.h > 0) || (_widthAutoCache && newSize.w > 0)) {\n                                            update();\n                                        }\n                                        else if (_initialized && (!_heightAutoCache && newSize.h === 0) || (!_widthAutoCache && newSize.w === 0)) {\n                                            update();\n                                        }\n                                    }\n                                    oldSize = newSize;\n                                });\n                                _sizeAutoObserverAdded = true;\n                                //fix heightAuto detector bug if height is fixed but contentHeight is 0.\n                                //the probability this bug will ever happen is very very low, thats why its ok if we use calc which isn't supported in IE8.\n                                if (_cssCalc !== null)\n                                    _sizeAutoObserverElement.css(_strHeight, _cssCalc + '(100% + 1px)');\n                            }\n                        }\n                        else {\n                            if (_sizeAutoObserverAdded)\n                                _sizeAutoObserverElement.hide();\n                            if (_contentGlueElement)\n                                _contentGlueElement.hide();\n                        }\n                    }\n\n                    //if force, update all resizeObservers too\n                    if (force) {\n                        _sizeObserverElement.find('*').trigger(_strScroll);\n                        if (_sizeAutoObserverAdded)\n                            _sizeAutoObserverElement.find('*').trigger(_strScroll);\n                    }\n\n                    //display hidden:\n                    displayIsHidden = displayIsHidden === undefined ? _hostElement.is(':hidden') : displayIsHidden;\n\n                    //textarea AutoWrapping:\n                    var textareaAutoWrapping = _isTextarea ? _targetElement.attr('wrap') !== 'off' : false;\n                    var textareaAutoWrappingChanged = checkCacheAutoForce(textareaAutoWrapping, _textareaAutoWrappingCache);\n\n                    //detect direction:\n                    var cssDirection = _hostElement.css('direction');\n                    var cssDirectionChanged = checkCacheAutoForce(cssDirection, _cssDirectionCache);\n\n                    //detect box-sizing:\n                    var boxSizing = _hostElement.css('box-sizing');\n                    var boxSizingChanged = checkCacheAutoForce(boxSizing, _cssBoxSizingCache);\n\n                    //detect padding:\n                    var padding = getTopRightBottomLeftHost(_strPaddingMinus);\n\n                    //width + height auto detecting var:\n                    var sizeAutoObserverElementBCRect;\n                    //exception occurs in IE8 sometimes (unknown exception)\n                    try {\n                        sizeAutoObserverElementBCRect = _sizeAutoObserverAdded ? _sizeAutoObserverElementNative[LEXICON.bCR]() : null;\n                    } catch (ex) {\n                        return;\n                    }\n\n                    _isRTL = cssDirection === 'rtl';\n                    _isBorderBox = (boxSizing === 'border-box');\n                    var isRTLLeft = _isRTL ? _strLeft : _strRight;\n                    var isRTLRight = _isRTL ? _strRight : _strLeft;\n\n                    //detect width auto:\n                    var widthAutoResizeDetection = false;\n                    var widthAutoObserverDetection = (_sizeAutoObserverAdded && (_hostElement.css(_strFloat) !== 'none' /*|| _isTextarea */)) ? (MATH.round(sizeAutoObserverElementBCRect.right - sizeAutoObserverElementBCRect.left) === 0) && (!paddingAbsolute ? (_hostElementNative[LEXICON.cW] - _paddingX) > 0 : true) : false;\n                    if (sizeAutoCapable && !widthAutoObserverDetection) {\n                        var tmpCurrHostWidth = _hostElementNative[LEXICON.oW];\n                        var tmpCurrContentGlueWidth = _contentGlueElement.css(_strWidth);\n                        _contentGlueElement.css(_strWidth, _strAuto);\n\n                        var tmpNewHostWidth = _hostElementNative[LEXICON.oW];\n                        _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);\n                        widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;\n                        if (!widthAutoResizeDetection) {\n                            _contentGlueElement.css(_strWidth, tmpCurrHostWidth + 1);\n                            tmpNewHostWidth = _hostElementNative[LEXICON.oW];\n                            _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);\n                            widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;\n                        }\n                    }\n                    var widthAuto = (widthAutoObserverDetection || widthAutoResizeDetection) && sizeAutoCapable && !displayIsHidden;\n                    var widthAutoChanged = checkCacheAutoForce(widthAuto, _widthAutoCache);\n                    var wasWidthAuto = !widthAuto && _widthAutoCache;\n\n                    //detect height auto:\n                    var heightAuto = _sizeAutoObserverAdded && sizeAutoCapable && !displayIsHidden ? (MATH.round(sizeAutoObserverElementBCRect.bottom - sizeAutoObserverElementBCRect.top) === 0) /* && (!paddingAbsolute && (_msieVersion > 9 || !_msieVersion) ? true : true) */ : false;\n                    var heightAutoChanged = checkCacheAutoForce(heightAuto, _heightAutoCache);\n                    var wasHeightAuto = !heightAuto && _heightAutoCache;\n\n                    //detect border:\n                    //we need the border only if border box and auto size\n                    var updateBorderX = (widthAuto && _isBorderBox) || !_isBorderBox;\n                    var updateBorderY = (heightAuto && _isBorderBox) || !_isBorderBox;\n                    var border = getTopRightBottomLeftHost(_strBorderMinus, '-' + _strWidth, !updateBorderX, !updateBorderY)\n\n                    //detect margin:\n                    var margin = getTopRightBottomLeftHost(_strMarginMinus);\n\n                    //vars to apply correct css\n                    var contentElementCSS = {};\n                    var contentGlueElementCSS = {};\n\n                    //funcs\n                    var getHostSize = function () {\n                        //has to be clientSize because offsetSize respect borders\n                        return {\n                            w: _hostElementNative[LEXICON.cW],\n                            h: _hostElementNative[LEXICON.cH]\n                        };\n                    };\n                    var getViewportSize = function () {\n                        //viewport size is padding container because it never has padding, margin and a border\n                        //determine zoom rounding error -> sometimes scrollWidth/Height is smaller than clientWidth/Height\n                        //if this happens add the difference to the viewportSize to compensate the rounding error\n                        return {\n                            w: _paddingElementNative[LEXICON.oW] + MATH.max(0, _contentElementNative[LEXICON.cW] - _contentElementNative[LEXICON.sW]),\n                            h: _paddingElementNative[LEXICON.oH] + MATH.max(0, _contentElementNative[LEXICON.cH] - _contentElementNative[LEXICON.sH])\n                        };\n                    };\n\n                    //set info for padding\n                    var paddingAbsoluteX = _paddingX = padding.l + padding.r;\n                    var paddingAbsoluteY = _paddingY = padding.t + padding.b;\n                    paddingAbsoluteX *= paddingAbsolute ? 1 : 0;\n                    paddingAbsoluteY *= paddingAbsolute ? 1 : 0;\n                    padding.c = checkCacheAutoForce(padding, _cssPaddingCache);\n\n                    //set info for border\n                    _borderX = border.l + border.r;\n                    _borderY = border.t + border.b;\n                    border.c = checkCacheAutoForce(border, _cssBorderCache);\n\n                    //set info for margin\n                    _marginX = margin.l + margin.r;\n                    _marginY = margin.t + margin.b;\n                    margin.c = checkCacheAutoForce(margin, _cssMarginCache);\n\n                    //refresh cache\n                    _textareaAutoWrappingCache = textareaAutoWrapping;\n                    _cssDirectionCache = cssDirection;\n                    _cssBoxSizingCache = boxSizing;\n                    _widthAutoCache = widthAuto;\n                    _heightAutoCache = heightAuto;\n                    _cssPaddingCache = padding;\n                    _cssBorderCache = border;\n                    _cssMarginCache = margin;\n\n                    //IEFix direction changed\n                    if (cssDirectionChanged && _sizeAutoObserverAdded)\n                        _sizeAutoObserverElement.css(_strFloat, isRTLRight);\n\n                    //apply padding:\n                    if (padding.c || cssDirectionChanged || paddingAbsoluteChanged || widthAutoChanged || heightAutoChanged || boxSizingChanged || sizeAutoCapableChanged) {\n                        var paddingElementCSS = {};\n                        var textareaCSS = {};\n                        var paddingValues = [padding.t, padding.r, padding.b, padding.l];\n\n                        setTopRightBottomLeft(contentGlueElementCSS, _strMarginMinus, [-padding.t, -padding.r, -padding.b, -padding.l]);\n                        if (paddingAbsolute) {\n                            setTopRightBottomLeft(paddingElementCSS, _strEmpty, paddingValues);\n                            setTopRightBottomLeft(_isTextarea ? textareaCSS : contentElementCSS, _strPaddingMinus);\n                        }\n                        else {\n                            setTopRightBottomLeft(paddingElementCSS, _strEmpty);\n                            setTopRightBottomLeft(_isTextarea ? textareaCSS : contentElementCSS, _strPaddingMinus, paddingValues);\n                        }\n\n                        _paddingElement.css(paddingElementCSS);\n                        _targetElement.css(textareaCSS);\n                    }\n\n                    //viewport size is padding container because it never has padding, margin and a border.\n                    _viewportSize = getViewportSize();\n\n                    //update Textarea\n                    var textareaSize = _isTextarea ? textareaUpdate() : false;\n                    var textareaSizeChanged = _isTextarea && checkCacheAutoForce(textareaSize, _textareaSizeCache);\n                    var textareaDynOrigSize = _isTextarea && textareaSize ? {\n                        w: textareaDynWidth ? textareaSize._dynamicWidth : textareaSize._originalWidth,\n                        h: textareaDynHeight ? textareaSize._dynamicHeight : textareaSize._originalHeight\n                    } : {};\n                    _textareaSizeCache = textareaSize;\n\n                    //fix height auto / width auto in cooperation with current padding & boxSizing behavior:\n                    if (heightAuto && (heightAutoChanged || paddingAbsoluteChanged || boxSizingChanged || padding.c || border.c)) {\n                        contentElementCSS[_strHeight] = _strAuto;\n                    }\n                    else if (heightAutoChanged || paddingAbsoluteChanged) {\n                        contentElementCSS[_strHeight] = _strHundredPercent;\n                    }\n                    if (widthAuto && (widthAutoChanged || paddingAbsoluteChanged || boxSizingChanged || padding.c || border.c || cssDirectionChanged)) {\n                        contentElementCSS[_strWidth] = _strAuto;\n                        contentGlueElementCSS[_strMaxMinus + _strWidth] = _strHundredPercent; //IE Fix\n                    }\n                    else if (widthAutoChanged || paddingAbsoluteChanged) {\n                        contentElementCSS[_strWidth] = _strHundredPercent;\n                        contentElementCSS[_strFloat] = _strEmpty;\n                        contentGlueElementCSS[_strMaxMinus + _strWidth] = _strEmpty; //IE Fix\n                    }\n                    if (widthAuto) {\n                        //textareaDynOrigSize.w || _strAuto :: doesnt works because applied margin will shift width\n                        contentGlueElementCSS[_strWidth] = _strAuto;\n\n                        contentElementCSS[_strWidth] = VENDORS._cssPropertyValue(_strWidth, 'max-content intrinsic') || _strAuto;\n                        contentElementCSS[_strFloat] = isRTLRight;\n                    }\n                    else {\n                        contentGlueElementCSS[_strWidth] = _strEmpty;\n                    }\n                    if (heightAuto) {\n                        //textareaDynOrigSize.h || _contentElementNative[LEXICON.cH] :: use for anti scroll jumping\n                        contentGlueElementCSS[_strHeight] = textareaDynOrigSize.h || _contentElementNative[LEXICON.cH];\n                    }\n                    else {\n                        contentGlueElementCSS[_strHeight] = _strEmpty;\n                    }\n                    if (sizeAutoCapable)\n                        _contentGlueElement.css(contentGlueElementCSS);\n                    _contentElement.css(contentElementCSS);\n\n                    //CHECKPOINT HERE ~\n                    contentElementCSS = {};\n                    contentGlueElementCSS = {};\n\n                    //if [content(host) client / scroll size, or target element direction, or content(host) max-sizes] changed, or force is true\n                    if (hostSizeChanged || contentSizeChanged || textareaSizeChanged || cssDirectionChanged || boxSizingChanged || paddingAbsoluteChanged || widthAutoChanged || widthAuto || heightAutoChanged || heightAuto || ignoreOverlayScrollbarHidingChanged || overflowBehaviorChanged || clipAlwaysChanged || resizeChanged || scrollbarsVisibilityChanged || scrollbarsAutoHideChanged || scrollbarsDragScrollingChanged || scrollbarsClickScrollingChanged || textareaDynWidthChanged || textareaDynHeightChanged || textareaAutoWrappingChanged) {\n                        var strOverflow = 'overflow';\n                        var strOverflowX = strOverflow + '-x';\n                        var strOverflowY = strOverflow + '-y';\n                        var strHidden = 'hidden';\n                        var strVisible = 'visible';\n\n                        //Reset the viewport (very important for natively overlaid scrollbars and zoom change\n                        //don't change the overflow prop as it is very expensive and affects performance !A LOT!\n                        if (!_nativeScrollbarStyling) {\n                            var viewportElementResetCSS = {};\n                            var resetXTmp = _hasOverflowCache.y && _hideOverflowCache.ys && !ignoreOverlayScrollbarHiding ? (_nativeScrollbarIsOverlaid.y ? _viewportElement.css(isRTLLeft) : -_nativeScrollbarSize.y) : 0;\n                            var resetBottomTmp = _hasOverflowCache.x && _hideOverflowCache.xs && !ignoreOverlayScrollbarHiding ? (_nativeScrollbarIsOverlaid.x ? _viewportElement.css(_strBottom) : -_nativeScrollbarSize.x) : 0;\n                            setTopRightBottomLeft(viewportElementResetCSS, _strEmpty);\n                            _viewportElement.css(viewportElementResetCSS);\n                        }\n\n                        //measure several sizes:\n                        var contentMeasureElement = getContentMeasureElement();\n                        //in Firefox content element has to have overflow hidden, else element margins aren't calculated properly, this element prevents this bug, but only if scrollbars aren't overlaid\n                        var contentSize = {\n                            //use clientSize because natively overlaidScrollbars add borders\n                            w: textareaDynOrigSize.w || contentMeasureElement[LEXICON.cW],\n                            h: textareaDynOrigSize.h || contentMeasureElement[LEXICON.cH]\n                        };\n                        var scrollSize = {\n                            w: contentMeasureElement[LEXICON.sW],\n                            h: contentMeasureElement[LEXICON.sH]\n                        };\n\n                        //apply the correct viewport style and measure viewport size\n                        if (!_nativeScrollbarStyling) {\n                            viewportElementResetCSS[_strBottom] = wasHeightAuto ? _strEmpty : resetBottomTmp;\n                            viewportElementResetCSS[isRTLLeft] = wasWidthAuto ? _strEmpty : resetXTmp;\n                            _viewportElement.css(viewportElementResetCSS);\n                        }\n                        _viewportSize = getViewportSize();\n\n                        //measure and correct several sizes\n                        var hostSize = getHostSize();\n                        var hostAbsoluteRectSize = {\n                            w: hostSize.w - _marginX - _borderX - (_isBorderBox ? 0 : _paddingX),\n                            h: hostSize.h - _marginY - _borderY - (_isBorderBox ? 0 : _paddingY)\n                        };\n                        var contentGlueSize = {\n                            //client/scrollSize + AbsolutePadding -> because padding is only applied to the paddingElement if its absolute, so you have to add it manually\n                            //hostSize is clientSize -> so padding should be added manually, right? FALSE! Because content glue is inside hostElement, so we don't have to worry about padding\n                            w: MATH.max((widthAuto ? contentSize.w : scrollSize.w) + paddingAbsoluteX, hostAbsoluteRectSize.w),\n                            h: MATH.max((heightAuto ? contentSize.h : scrollSize.h) + paddingAbsoluteY, hostAbsoluteRectSize.h)\n                        };\n                        contentGlueSize.c = checkCacheAutoForce(contentGlueSize, _contentGlueSizeCache);\n                        _contentGlueSizeCache = contentGlueSize;\n\n                        //apply correct contentGlue size\n                        if (sizeAutoCapable) {\n                            //size contentGlue correctly to make sure the element has correct size if the sizing switches to auto\n                            if (contentGlueSize.c || (heightAuto || widthAuto)) {\n                                contentGlueElementCSS[_strWidth] = contentGlueSize.w;\n                                contentGlueElementCSS[_strHeight] = contentGlueSize.h;\n\n                                //textarea-sizes are already calculated correctly at this point\n                                if (!_isTextarea) {\n                                    contentSize = {\n                                        //use clientSize because natively overlaidScrollbars add borders\n                                        w: contentMeasureElement[LEXICON.cW],\n                                        h: contentMeasureElement[LEXICON.cH]\n                                    };\n                                }\n                            }\n                            var textareaCoverCSS = {};\n                            var setContentGlueElementCSSfunction = function (horizontal) {\n                                var scrollbarVars = getScrollbarVars(horizontal);\n                                var wh = scrollbarVars._w_h;\n                                var strWH = scrollbarVars._width_height;\n                                var autoSize = horizontal ? widthAuto : heightAuto;\n                                var borderSize = horizontal ? _borderX : _borderY;\n                                var paddingSize = horizontal ? _paddingX : _paddingY;\n                                var marginSize = horizontal ? _marginX : _marginY;\n                                var viewportSize = _viewportSize[wh] - borderSize - marginSize - (_isBorderBox ? 0 : paddingSize);\n\n                                //make contentGlue size -1 if element is not auto sized, to make sure that a resize event happens when the element shrinks\n                                if (!autoSize || (!autoSize && border.c))\n                                    contentGlueElementCSS[strWH] = hostAbsoluteRectSize[wh] - 1;\n\n                                //if size is auto and host is smaller than size as min size, make content glue size -1 to make sure size changes will be detected (this is only needed if padding is 0)\n                                if (autoSize && (contentSize[wh] < viewportSize) && (horizontal && _isTextarea ? !textareaAutoWrapping : true)) {\n                                    if (_isTextarea)\n                                        textareaCoverCSS[strWH] = parseToZeroOrNumber(_textareaCoverElement.css(strWH)) - 1;\n                                    contentGlueElementCSS[strWH] -= 1;\n                                }\n\n                                //make sure content glue size is at least 1\n                                if (contentSize[wh] > 0)\n                                    contentGlueElementCSS[strWH] = MATH.max(1, contentGlueElementCSS[strWH]);\n                            };\n                            setContentGlueElementCSSfunction(true);\n                            setContentGlueElementCSSfunction(false);\n\n                            if (_isTextarea)\n                                _textareaCoverElement.css(textareaCoverCSS);\n                            _contentGlueElement.css(contentGlueElementCSS);\n                        }\n                        if (widthAuto)\n                            contentElementCSS[_strWidth] = _strHundredPercent;\n                        if (widthAuto && !_isBorderBox && !_mutationObserversConnected)\n                            contentElementCSS[_strFloat] = 'none';\n\n                        //apply and reset content style\n                        _contentElement.css(contentElementCSS);\n                        contentElementCSS = {};\n\n                        //measure again, but this time all correct sizes:\n                        var contentScrollSize = {\n                            w: contentMeasureElement[LEXICON.sW],\n                            h: contentMeasureElement[LEXICON.sH],\n                        };\n                        contentScrollSize.c = contentSizeChanged = checkCacheAutoForce(contentScrollSize, _contentScrollSizeCache);\n                        _contentScrollSizeCache = contentScrollSize;\n\n                        //refresh viewport size after correct measuring\n                        _viewportSize = getViewportSize();\n\n                        hostSize = getHostSize();\n                        hostSizeChanged = checkCacheAutoForce(hostSize, _hostSizeCache);\n                        _hostSizeCache = hostSize;\n\n                        var hideOverflowForceTextarea = _isTextarea && (_viewportSize.w === 0 || _viewportSize.h === 0);\n                        var previousOverflowAmount = _overflowAmountCache;\n                        var overflowBehaviorIsVS = {};\n                        var overflowBehaviorIsVH = {};\n                        var overflowBehaviorIsS = {};\n                        var overflowAmount = {};\n                        var hasOverflow = {};\n                        var hideOverflow = {};\n                        var canScroll = {};\n                        var viewportRect = _paddingElementNative[LEXICON.bCR]();\n                        var setOverflowVariables = function (horizontal) {\n                            var scrollbarVars = getScrollbarVars(horizontal);\n                            var scrollbarVarsInverted = getScrollbarVars(!horizontal);\n                            var xyI = scrollbarVarsInverted._x_y;\n                            var xy = scrollbarVars._x_y;\n                            var wh = scrollbarVars._w_h;\n                            var widthHeight = scrollbarVars._width_height;\n                            var scrollMax = _strScroll + scrollbarVars._Left_Top + 'Max';\n                            var fractionalOverflowAmount = viewportRect[widthHeight] ? MATH.abs(viewportRect[widthHeight] - _viewportSize[wh]) : 0;\n                            var checkFractionalOverflowAmount = previousOverflowAmount && previousOverflowAmount[xy] > 0 && _viewportElementNative[scrollMax] === 0;\n                            overflowBehaviorIsVS[xy] = overflowBehavior[xy] === 'v-s';\n                            overflowBehaviorIsVH[xy] = overflowBehavior[xy] === 'v-h';\n                            overflowBehaviorIsS[xy] = overflowBehavior[xy] === 's';\n                            overflowAmount[xy] = MATH.max(0, MATH.round((contentScrollSize[wh] - _viewportSize[wh]) * 100) / 100);\n                            overflowAmount[xy] *= (hideOverflowForceTextarea || (checkFractionalOverflowAmount && fractionalOverflowAmount > 0 && fractionalOverflowAmount < 1)) ? 0 : 1;\n                            hasOverflow[xy] = overflowAmount[xy] > 0;\n\n                            //hideOverflow:\n                            //x || y : true === overflow is hidden by \"overflow: scroll\" OR \"overflow: hidden\"\n                            //xs || ys : true === overflow is hidden by \"overflow: scroll\"\n                            hideOverflow[xy] = overflowBehaviorIsVS[xy] || overflowBehaviorIsVH[xy] ? (hasOverflow[xyI] && !overflowBehaviorIsVS[xyI] && !overflowBehaviorIsVH[xyI]) : hasOverflow[xy];\n                            hideOverflow[xy + 's'] = hideOverflow[xy] ? (overflowBehaviorIsS[xy] || overflowBehaviorIsVS[xy]) : false;\n\n                            canScroll[xy] = hasOverflow[xy] && hideOverflow[xy + 's'];\n                        };\n                        setOverflowVariables(true);\n                        setOverflowVariables(false);\n\n                        overflowAmount.c = checkCacheAutoForce(overflowAmount, _overflowAmountCache);\n                        _overflowAmountCache = overflowAmount;\n                        hasOverflow.c = checkCacheAutoForce(hasOverflow, _hasOverflowCache);\n                        _hasOverflowCache = hasOverflow;\n                        hideOverflow.c = checkCacheAutoForce(hideOverflow, _hideOverflowCache);\n                        _hideOverflowCache = hideOverflow;\n\n                        //if native scrollbar is overlay at x OR y axis, prepare DOM\n                        if (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) {\n                            var borderDesign = 'px solid transparent';\n                            var contentArrangeElementCSS = {};\n                            var arrangeContent = {};\n                            var arrangeChanged = force;\n                            var setContentElementCSS;\n\n                            if (hasOverflow.x || hasOverflow.y) {\n                                arrangeContent.w = _nativeScrollbarIsOverlaid.y && hasOverflow.y ? contentScrollSize.w + _overlayScrollbarDummySize.y : _strEmpty;\n                                arrangeContent.h = _nativeScrollbarIsOverlaid.x && hasOverflow.x ? contentScrollSize.h + _overlayScrollbarDummySize.x : _strEmpty;\n                                arrangeChanged = checkCacheAutoForce(arrangeContent, _arrangeContentSizeCache);\n                                _arrangeContentSizeCache = arrangeContent;\n                            }\n\n                            if (hasOverflow.c || hideOverflow.c || contentScrollSize.c || cssDirectionChanged || widthAutoChanged || heightAutoChanged || widthAuto || heightAuto || ignoreOverlayScrollbarHidingChanged) {\n                                contentElementCSS[_strMarginMinus + isRTLRight] = contentElementCSS[_strBorderMinus + isRTLRight] = _strEmpty;\n                                setContentElementCSS = function (horizontal) {\n                                    var scrollbarVars = getScrollbarVars(horizontal);\n                                    var scrollbarVarsInverted = getScrollbarVars(!horizontal);\n                                    var xy = scrollbarVars._x_y;\n                                    var strDirection = horizontal ? _strBottom : isRTLLeft;\n                                    var invertedAutoSize = horizontal ? heightAuto : widthAuto;\n\n                                    if (_nativeScrollbarIsOverlaid[xy] && hasOverflow[xy] && hideOverflow[xy + 's']) {\n                                        contentElementCSS[_strMarginMinus + strDirection] = invertedAutoSize ? (ignoreOverlayScrollbarHiding ? _strEmpty : _overlayScrollbarDummySize[xy]) : _strEmpty;\n                                        contentElementCSS[_strBorderMinus + strDirection] = ((horizontal ? !invertedAutoSize : true) && !ignoreOverlayScrollbarHiding) ? (_overlayScrollbarDummySize[xy] + borderDesign) : _strEmpty;\n                                    }\n                                    else {\n                                        arrangeContent[scrollbarVarsInverted._w_h] =\n                                            contentElementCSS[_strMarginMinus + strDirection] =\n                                            contentElementCSS[_strBorderMinus + strDirection] = _strEmpty;\n                                        arrangeChanged = true;\n                                    }\n                                };\n\n                                if (_nativeScrollbarStyling) {\n                                    addRemoveClass(_viewportElement, _classNameViewportNativeScrollbarsInvisible, !ignoreOverlayScrollbarHiding)\n                                }\n                                else {\n                                    setContentElementCSS(true);\n                                    setContentElementCSS(false);\n                                }\n                            }\n                            if (ignoreOverlayScrollbarHiding) {\n                                arrangeContent.w = arrangeContent.h = _strEmpty;\n                                arrangeChanged = true;\n                            }\n                            if (arrangeChanged && !_nativeScrollbarStyling) {\n                                contentArrangeElementCSS[_strWidth] = hideOverflow.y ? arrangeContent.w : _strEmpty;\n                                contentArrangeElementCSS[_strHeight] = hideOverflow.x ? arrangeContent.h : _strEmpty;\n\n                                if (!_contentArrangeElement) {\n                                    _contentArrangeElement = FRAMEWORK(generateDiv(_classNameContentArrangeElement));\n                                    _viewportElement.prepend(_contentArrangeElement);\n                                }\n                                _contentArrangeElement.css(contentArrangeElementCSS);\n                            }\n                            _contentElement.css(contentElementCSS);\n                        }\n\n                        var viewportElementCSS = {};\n                        var paddingElementCSS = {};\n                        var setViewportCSS;\n                        if (hostSizeChanged || hasOverflow.c || hideOverflow.c || contentScrollSize.c || overflowBehaviorChanged || boxSizingChanged || ignoreOverlayScrollbarHidingChanged || cssDirectionChanged || clipAlwaysChanged || heightAutoChanged) {\n                            viewportElementCSS[isRTLRight] = _strEmpty;\n                            setViewportCSS = function (horizontal) {\n                                var scrollbarVars = getScrollbarVars(horizontal);\n                                var scrollbarVarsInverted = getScrollbarVars(!horizontal);\n                                var xy = scrollbarVars._x_y;\n                                var XY = scrollbarVars._X_Y;\n                                var strDirection = horizontal ? _strBottom : isRTLLeft;\n\n                                var reset = function () {\n                                    viewportElementCSS[strDirection] = _strEmpty;\n                                    _contentBorderSize[scrollbarVarsInverted._w_h] = 0;\n                                };\n                                if (hasOverflow[xy] && hideOverflow[xy + 's']) {\n                                    viewportElementCSS[strOverflow + XY] = _strScroll;\n                                    if (ignoreOverlayScrollbarHiding || _nativeScrollbarStyling) {\n                                        reset();\n                                    }\n                                    else {\n                                        viewportElementCSS[strDirection] = -(_nativeScrollbarIsOverlaid[xy] ? _overlayScrollbarDummySize[xy] : _nativeScrollbarSize[xy]);\n                                        _contentBorderSize[scrollbarVarsInverted._w_h] = _nativeScrollbarIsOverlaid[xy] ? _overlayScrollbarDummySize[scrollbarVarsInverted._x_y] : 0;\n                                    }\n                                } else {\n                                    viewportElementCSS[strOverflow + XY] = _strEmpty;\n                                    reset();\n                                }\n                            };\n                            setViewportCSS(true);\n                            setViewportCSS(false);\n\n                            // if the scroll container is too small and if there is any overflow with no overlay scrollbar (and scrollbar styling isn't possible), \n                            // make viewport element greater in size (Firefox hide Scrollbars fix)\n                            // because firefox starts hiding scrollbars on too small elements\n                            // with this behavior the overflow calculation may be incorrect or the scrollbars would appear suddenly\n                            // https://bugzilla.mozilla.org/show_bug.cgi?id=292284\n                            if (!_nativeScrollbarStyling\n                                && (_viewportSize.h < _nativeScrollbarMinSize.x || _viewportSize.w < _nativeScrollbarMinSize.y)\n                                && ((hasOverflow.x && hideOverflow.x && !_nativeScrollbarIsOverlaid.x) || (hasOverflow.y && hideOverflow.y && !_nativeScrollbarIsOverlaid.y))) {\n                                viewportElementCSS[_strPaddingMinus + _strTop] = _nativeScrollbarMinSize.x;\n                                viewportElementCSS[_strMarginMinus + _strTop] = -_nativeScrollbarMinSize.x;\n\n                                viewportElementCSS[_strPaddingMinus + isRTLRight] = _nativeScrollbarMinSize.y;\n                                viewportElementCSS[_strMarginMinus + isRTLRight] = -_nativeScrollbarMinSize.y;\n                            }\n                            else {\n                                viewportElementCSS[_strPaddingMinus + _strTop] =\n                                    viewportElementCSS[_strMarginMinus + _strTop] =\n                                    viewportElementCSS[_strPaddingMinus + isRTLRight] =\n                                    viewportElementCSS[_strMarginMinus + isRTLRight] = _strEmpty;\n                            }\n                            viewportElementCSS[_strPaddingMinus + isRTLLeft] =\n                                viewportElementCSS[_strMarginMinus + isRTLLeft] = _strEmpty;\n\n                            //if there is any overflow (x OR y axis) and this overflow shall be hidden, make overflow hidden, else overflow visible\n                            if ((hasOverflow.x && hideOverflow.x) || (hasOverflow.y && hideOverflow.y) || hideOverflowForceTextarea) {\n                                //only hide if is Textarea\n                                if (_isTextarea && hideOverflowForceTextarea) {\n                                    paddingElementCSS[strOverflowX] =\n                                        paddingElementCSS[strOverflowY] = strHidden;\n                                }\n                            }\n                            else {\n                                if (!clipAlways || (overflowBehaviorIsVH.x || overflowBehaviorIsVS.x || overflowBehaviorIsVH.y || overflowBehaviorIsVS.y)) {\n                                    //only un-hide if Textarea\n                                    if (_isTextarea) {\n                                        paddingElementCSS[strOverflowX] =\n                                            paddingElementCSS[strOverflowY] = _strEmpty;\n                                    }\n                                    viewportElementCSS[strOverflowX] =\n                                        viewportElementCSS[strOverflowY] = strVisible;\n                                }\n                            }\n\n                            _paddingElement.css(paddingElementCSS);\n                            _viewportElement.css(viewportElementCSS);\n                            viewportElementCSS = {};\n\n                            //force soft redraw in webkit because without the scrollbars will may appear because DOM wont be redrawn under special conditions\n                            if ((hasOverflow.c || boxSizingChanged || widthAutoChanged || heightAutoChanged) && !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)) {\n                                var elementStyle = _contentElementNative[LEXICON.s];\n                                var dump;\n                                elementStyle.webkitTransform = 'scale(1)';\n                                elementStyle.display = 'run-in';\n                                dump = _contentElementNative[LEXICON.oH];\n                                elementStyle.display = _strEmpty; //|| dump; //use dump to prevent it from deletion if minify\n                                elementStyle.webkitTransform = _strEmpty;\n                            }\n                            /*\n                            //force hard redraw in webkit if native overlaid scrollbars shall appear\n                            if (ignoreOverlayScrollbarHidingChanged && ignoreOverlayScrollbarHiding) {\n                                _hostElement.hide();\n                                var dump = _hostElementNative[LEXICON.oH];\n                                _hostElement.show();\n                            }\n                            */\n                        }\n\n                        //change to direction RTL and width auto Bugfix in Webkit\n                        //without this fix, the DOM still thinks the scrollbar is LTR and thus the content is shifted to the left\n                        contentElementCSS = {};\n                        if (cssDirectionChanged || widthAutoChanged || heightAutoChanged) {\n                            if (_isRTL && widthAuto) {\n                                var floatTmp = _contentElement.css(_strFloat);\n                                var posLeftWithoutFloat = MATH.round(_contentElement.css(_strFloat, _strEmpty).css(_strLeft, _strEmpty).position().left);\n                                _contentElement.css(_strFloat, floatTmp);\n                                var posLeftWithFloat = MATH.round(_contentElement.position().left);\n\n                                if (posLeftWithoutFloat !== posLeftWithFloat)\n                                    contentElementCSS[_strLeft] = posLeftWithoutFloat;\n                            }\n                            else {\n                                contentElementCSS[_strLeft] = _strEmpty;\n                            }\n                        }\n                        _contentElement.css(contentElementCSS);\n\n                        //handle scroll position\n                        if (_isTextarea && contentSizeChanged) {\n                            var textareaInfo = getTextareaInfo();\n                            if (textareaInfo) {\n                                var textareaRowsChanged = _textareaInfoCache === undefined ? true : textareaInfo._rows !== _textareaInfoCache._rows;\n                                var cursorRow = textareaInfo._cursorRow;\n                                var cursorCol = textareaInfo._cursorColumn;\n                                var widestRow = textareaInfo._widestRow;\n                                var lastRow = textareaInfo._rows;\n                                var lastCol = textareaInfo._columns;\n                                var cursorPos = textareaInfo._cursorPosition;\n                                var cursorMax = textareaInfo._cursorMax;\n                                var cursorIsLastPosition = (cursorPos >= cursorMax && _textareaHasFocus);\n                                var textareaScrollAmount = {\n                                    x: (!textareaAutoWrapping && (cursorCol === lastCol && cursorRow === widestRow)) ? _overflowAmountCache.x : -1,\n                                    y: (textareaAutoWrapping ? cursorIsLastPosition || textareaRowsChanged && (previousOverflowAmount ? (currScroll.y === previousOverflowAmount.y) : false) : (cursorIsLastPosition || textareaRowsChanged) && cursorRow === lastRow) ? _overflowAmountCache.y : -1\n                                };\n                                currScroll.x = textareaScrollAmount.x > -1 ? (_isRTL && _normalizeRTLCache && _rtlScrollBehavior.i ? 0 : textareaScrollAmount.x) : currScroll.x; //if inverted, scroll to 0 -> normalized this means to max scroll offset.\n                                currScroll.y = textareaScrollAmount.y > -1 ? textareaScrollAmount.y : currScroll.y;\n                            }\n                            _textareaInfoCache = textareaInfo;\n                        }\n                        if (_isRTL && _rtlScrollBehavior.i && _nativeScrollbarIsOverlaid.y && hasOverflow.x && _normalizeRTLCache)\n                            currScroll.x += _contentBorderSize.w || 0;\n                        if (widthAuto)\n                            _hostElement[_strScrollLeft](0);\n                        if (heightAuto)\n                            _hostElement[_strScrollTop](0);\n                        _viewportElement[_strScrollLeft](currScroll.x)[_strScrollTop](currScroll.y);\n\n                        //scrollbars management:\n                        var scrollbarsVisibilityVisible = scrollbarsVisibility === 'v';\n                        var scrollbarsVisibilityHidden = scrollbarsVisibility === 'h';\n                        var scrollbarsVisibilityAuto = scrollbarsVisibility === 'a';\n                        var refreshScrollbarsVisibility = function (showX, showY) {\n                            showY = showY === undefined ? showX : showY;\n                            refreshScrollbarAppearance(true, showX, canScroll.x)\n                            refreshScrollbarAppearance(false, showY, canScroll.y)\n                        };\n\n                        //manage class name which indicates scrollable overflow\n                        addRemoveClass(_hostElement, _classNameHostOverflow, hideOverflow.x || hideOverflow.y);\n                        addRemoveClass(_hostElement, _classNameHostOverflowX, hideOverflow.x);\n                        addRemoveClass(_hostElement, _classNameHostOverflowY, hideOverflow.y);\n\n                        //add or remove rtl class name for styling purposes except when its body, then the scrollbar stays\n                        if (cssDirectionChanged && !_isBody) {\n                            addRemoveClass(_hostElement, _classNameHostRTL, _isRTL);\n                        }\n\n                        //manage the resize feature (CSS3 resize \"polyfill\" for this plugin)\n                        if (_isBody)\n                            addClass(_hostElement, _classNameHostResizeDisabled);\n                        if (resizeChanged) {\n                            addRemoveClass(_hostElement, _classNameHostResizeDisabled, _resizeNone);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResize, !_resizeNone);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeB, _resizeBoth);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeH, _resizeHorizontal);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeV, _resizeVertical);\n                        }\n\n                        //manage the scrollbars general visibility + the scrollbar interactivity (unusable class name)\n                        if (scrollbarsVisibilityChanged || overflowBehaviorChanged || hideOverflow.c || hasOverflow.c || ignoreOverlayScrollbarHidingChanged) {\n                            if (ignoreOverlayScrollbarHiding) {\n                                if (ignoreOverlayScrollbarHidingChanged) {\n                                    removeClass(_hostElement, _classNameHostScrolling);\n                                    if (ignoreOverlayScrollbarHiding) {\n                                        refreshScrollbarsVisibility(false);\n                                    }\n                                }\n                            }\n                            else if (scrollbarsVisibilityAuto) {\n                                refreshScrollbarsVisibility(canScroll.x, canScroll.y);\n                            }\n                            else if (scrollbarsVisibilityVisible) {\n                                refreshScrollbarsVisibility(true);\n                            }\n                            else if (scrollbarsVisibilityHidden) {\n                                refreshScrollbarsVisibility(false);\n                            }\n                        }\n\n                        //manage the scrollbars auto hide feature (auto hide them after specific actions)\n                        if (scrollbarsAutoHideChanged || ignoreOverlayScrollbarHidingChanged) {\n                            setupHostMouseTouchEvents(!_scrollbarsAutoHideLeave && !_scrollbarsAutoHideMove);\n                            refreshScrollbarsAutoHide(_scrollbarsAutoHideNever, !_scrollbarsAutoHideNever);\n                        }\n\n                        //manage scrollbars handle length & offset - don't remove!\n                        if (hostSizeChanged || overflowAmount.c || heightAutoChanged || widthAutoChanged || resizeChanged || boxSizingChanged || paddingAbsoluteChanged || ignoreOverlayScrollbarHidingChanged || cssDirectionChanged) {\n                            refreshScrollbarHandleLength(true);\n                            refreshScrollbarHandleOffset(true);\n                            refreshScrollbarHandleLength(false);\n                            refreshScrollbarHandleOffset(false);\n                        }\n\n                        //manage interactivity\n                        if (scrollbarsClickScrollingChanged)\n                            refreshScrollbarsInteractive(true, scrollbarsClickScrolling);\n                        if (scrollbarsDragScrollingChanged)\n                            refreshScrollbarsInteractive(false, scrollbarsDragScrolling);\n\n                        //callbacks:\n                        dispatchCallback('onDirectionChanged', {\n                            isRTL: _isRTL,\n                            dir: cssDirection\n                        }, cssDirectionChanged);\n                        dispatchCallback('onHostSizeChanged', {\n                            width: _hostSizeCache.w,\n                            height: _hostSizeCache.h\n                        }, hostSizeChanged);\n                        dispatchCallback('onContentSizeChanged', {\n                            width: _contentScrollSizeCache.w,\n                            height: _contentScrollSizeCache.h\n                        }, contentSizeChanged);\n                        dispatchCallback('onOverflowChanged', {\n                            x: hasOverflow.x,\n                            y: hasOverflow.y,\n                            xScrollable: hideOverflow.xs,\n                            yScrollable: hideOverflow.ys,\n                            clipped: hideOverflow.x || hideOverflow.y\n                        }, hasOverflow.c || hideOverflow.c);\n                        dispatchCallback('onOverflowAmountChanged', {\n                            x: overflowAmount.x,\n                            y: overflowAmount.y\n                        }, overflowAmount.c);\n                    }\n\n                    //fix body min size\n                    if (_isBody && _bodyMinSizeCache && (_hasOverflowCache.c || _bodyMinSizeCache.c)) {\n                        //its possible that no min size was measured until now, because the content arrange element was just added now, in this case, measure now the min size.\n                        if (!_bodyMinSizeCache.f)\n                            bodyMinSizeChanged();\n                        if (_nativeScrollbarIsOverlaid.y && _hasOverflowCache.x)\n                            _contentElement.css(_strMinMinus + _strWidth, _bodyMinSizeCache.w + _overlayScrollbarDummySize.y);\n                        if (_nativeScrollbarIsOverlaid.x && _hasOverflowCache.y)\n                            _contentElement.css(_strMinMinus + _strHeight, _bodyMinSizeCache.h + _overlayScrollbarDummySize.x);\n                        _bodyMinSizeCache.c = false;\n                    }\n\n                    if (_initialized && changedOptions.updateOnLoad) {\n                        updateElementsOnLoad();\n                    }\n\n                    //freezeResizeObserver(_sizeObserverElement, false);\n                    //freezeResizeObserver(_sizeAutoObserverElement, false);\n\n                    dispatchCallback('onUpdated', { forced: force });\n                }\n\n                /**\n                 * Updates the found elements of which the load event shall be handled.\n                 */\n                function updateElementsOnLoad() {\n                    if (!_isTextarea) {\n                        eachUpdateOnLoad(function (i, updateOnLoadSelector) {\n                            _contentElement.find(updateOnLoadSelector).each(function (i, el) {\n                                // if element doesn't have a updateOnLoadCallback applied\n                                if (COMPATIBILITY.inA(el, _updateOnLoadElms) < 0) {\n                                    _updateOnLoadElms.push(el);\n                                    FRAMEWORK(el)\n                                        .off(_updateOnLoadEventName, updateOnLoadCallback)\n                                        .on(_updateOnLoadEventName, updateOnLoadCallback);\n                                }\n                            });\n                        });\n                    }\n                }\n\n                //==== Options ====//\n\n                /**\n                 * Sets new options but doesn't call the update method.\n                 * @param newOptions The object which contains the new options.\n                 * @returns {*} A object which contains the changed options.\n                 */\n                function setOptions(newOptions) {\n                    var validatedOpts = _pluginsOptions._validate(newOptions, _pluginsOptions._template, true, _currentOptions)\n\n                    _currentOptions = extendDeep({}, _currentOptions, validatedOpts._default);\n                    _currentPreparedOptions = extendDeep({}, _currentPreparedOptions, validatedOpts._prepared);\n\n                    return validatedOpts._prepared;\n                }\n\n\n                //==== Structure ====//\n\n                /**\n                 * Builds or destroys the wrapper and helper DOM elements.\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                /**\n                 * Builds or destroys the wrapper and helper DOM elements.\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                function setupStructureDOM(destroy) {\n                    var strParent = 'parent';\n                    var classNameResizeObserverHost = 'os-resize-observer-host';\n                    var classNameTextareaElementFull = _classNameTextareaElement + _strSpace + _classNameTextInherit;\n                    var textareaClass = _isTextarea ? _strSpace + _classNameTextInherit : _strEmpty;\n                    var adoptAttrs = _currentPreparedOptions.textarea.inheritedAttrs;\n                    var adoptAttrsMap = {};\n                    var applyAdoptedAttrs = function () {\n                        var applyAdoptedAttrsElm = destroy ? _targetElement : _hostElement;\n                        each(adoptAttrsMap, function (key, value) {\n                            if (type(value) == TYPES.s) {\n                                if (key == LEXICON.c)\n                                    applyAdoptedAttrsElm.addClass(value);\n                                else\n                                    applyAdoptedAttrsElm.attr(key, value);\n                            }\n                        });\n                    };\n                    var hostElementClassNames = [\n                        _classNameHostElement,\n                        _classNameHostElementForeign,\n                        _classNameHostTextareaElement,\n                        _classNameHostResizeDisabled,\n                        _classNameHostRTL,\n                        _classNameHostScrollbarHorizontalHidden,\n                        _classNameHostScrollbarVerticalHidden,\n                        _classNameHostTransition,\n                        _classNameHostScrolling,\n                        _classNameHostOverflow,\n                        _classNameHostOverflowX,\n                        _classNameHostOverflowY,\n                        _classNameThemeNone,\n                        _classNameTextareaElement,\n                        _classNameTextInherit,\n                        _classNameCache].join(_strSpace);\n                    var hostElementCSS = {};\n\n                    //get host element as first element, because that's the most upper element and required for the other elements\n                    _hostElement = _hostElement || (_isTextarea ? (_domExists ? _targetElement[strParent]()[strParent]()[strParent]()[strParent]() : FRAMEWORK(generateDiv(_classNameHostTextareaElement))) : _targetElement);\n                    _contentElement = _contentElement || selectOrGenerateDivByClass(_classNameContentElement + textareaClass);\n                    _viewportElement = _viewportElement || selectOrGenerateDivByClass(_classNameViewportElement + textareaClass);\n                    _paddingElement = _paddingElement || selectOrGenerateDivByClass(_classNamePaddingElement + textareaClass);\n                    _sizeObserverElement = _sizeObserverElement || selectOrGenerateDivByClass(classNameResizeObserverHost);\n                    _textareaCoverElement = _textareaCoverElement || (_isTextarea ? selectOrGenerateDivByClass(_classNameTextareaCoverElement) : undefined);\n\n                    //add this class to workaround class changing issues with UI frameworks especially Vue\n                    if (_domExists)\n                        addClass(_hostElement, _classNameHostElementForeign);\n\n                    //on destroy, remove all generated class names from the host element before collecting the adopted attributes \n                    //to prevent adopting generated class names\n                    if (destroy)\n                        removeClass(_hostElement, hostElementClassNames);\n\n                    //collect all adopted attributes\n                    adoptAttrs = type(adoptAttrs) == TYPES.s ? adoptAttrs.split(_strSpace) : adoptAttrs;\n                    if (COMPATIBILITY.isA(adoptAttrs) && _isTextarea) {\n                        each(adoptAttrs, function (i, v) {\n                            if (type(v) == TYPES.s) {\n                                adoptAttrsMap[v] = destroy ? _hostElement.attr(v) : _targetElement.attr(v);\n                            }\n                        });\n                    }\n\n                    if (!destroy) {\n                        if (_isTextarea) {\n                            if (!_currentPreparedOptions.sizeAutoCapable) {\n                                hostElementCSS[_strWidth] = _targetElement.css(_strWidth);\n                                hostElementCSS[_strHeight] = _targetElement.css(_strHeight);\n                            }\n\n                            if (!_domExists)\n                                _targetElement.addClass(_classNameTextInherit).wrap(_hostElement);\n\n                            //jQuery clones elements in wrap functions, so we have to select them again\n                            _hostElement = _targetElement[strParent]().css(hostElementCSS);\n                        }\n\n                        if (!_domExists) {\n                            //add the correct class to the target element\n                            addClass(_targetElement, _isTextarea ? classNameTextareaElementFull : _classNameHostElement);\n\n                            //wrap the content into the generated elements to create the required DOM\n                            _hostElement.wrapInner(_contentElement)\n                                .wrapInner(_viewportElement)\n                                .wrapInner(_paddingElement)\n                                .prepend(_sizeObserverElement);\n\n                            //jQuery clones elements in wrap functions, so we have to select them again\n                            _contentElement = findFirst(_hostElement, _strDot + _classNameContentElement);\n                            _viewportElement = findFirst(_hostElement, _strDot + _classNameViewportElement);\n                            _paddingElement = findFirst(_hostElement, _strDot + _classNamePaddingElement);\n\n                            if (_isTextarea) {\n                                _contentElement.prepend(_textareaCoverElement);\n                                applyAdoptedAttrs();\n                            }\n                        }\n\n                        if (_nativeScrollbarStyling)\n                            addClass(_viewportElement, _classNameViewportNativeScrollbarsInvisible);\n                        if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)\n                            addClass(_viewportElement, _classNameViewportNativeScrollbarsOverlaid);\n                        if (_isBody)\n                            addClass(_htmlElement, _classNameHTMLElement);\n\n                        _sizeObserverElementNative = _sizeObserverElement[0];\n                        _hostElementNative = _hostElement[0];\n                        _paddingElementNative = _paddingElement[0];\n                        _viewportElementNative = _viewportElement[0];\n                        _contentElementNative = _contentElement[0];\n\n                        updateViewportAttrsFromTarget();\n                    }\n                    else {\n                        if (_domExists && _initialized) {\n                            //clear size observer\n                            _sizeObserverElement.children().remove();\n\n                            //remove the style property and classes from already generated elements\n                            each([_paddingElement, _viewportElement, _contentElement, _textareaCoverElement], function (i, elm) {\n                                if (elm) {\n                                    removeClass(elm.removeAttr(LEXICON.s), _classNamesDynamicDestroy);\n                                }\n                            });\n\n                            //add classes to the host element which was removed previously to match the expected DOM\n                            addClass(_hostElement, _isTextarea ? _classNameHostTextareaElement : _classNameHostElement);\n                        }\n                        else {\n                            //remove size observer\n                            remove(_sizeObserverElement);\n\n                            //unwrap the content to restore DOM\n                            _contentElement.contents()\n                                .unwrap()\n                                .unwrap()\n                                .unwrap();\n\n                            if (_isTextarea) {\n                                _targetElement.unwrap();\n                                remove(_hostElement);\n                                remove(_textareaCoverElement);\n                                applyAdoptedAttrs();\n                            }\n                        }\n\n                        if (_isTextarea)\n                            _targetElement.removeAttr(LEXICON.s);\n\n                        if (_isBody)\n                            removeClass(_htmlElement, _classNameHTMLElement);\n                    }\n                }\n\n                /**\n                 * Adds or removes all wrapper elements interactivity events.\n                 * @param destroy Indicates whether the Events shall be added or removed.\n                 */\n                function setupStructureEvents() {\n                    var textareaKeyDownRestrictedKeyCodes = [\n                        112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 123,    //F1 to F12\n                        33, 34,                                                   //page up, page down\n                        37, 38, 39, 40,                                           //left, up, right, down arrows\n                        16, 17, 18, 19, 20, 144                                   //Shift, Ctrl, Alt, Pause, CapsLock, NumLock\n                    ];\n                    var textareaKeyDownKeyCodesList = [];\n                    var textareaUpdateIntervalID;\n                    var scrollStopTimeoutId;\n                    var scrollStopDelay = 175;\n                    var strFocus = 'focus';\n\n                    function updateTextarea(doClearInterval) {\n                        textareaUpdate();\n                        _base.update(_strAuto);\n                        if (doClearInterval && _autoUpdateRecommended)\n                            clearInterval(textareaUpdateIntervalID);\n                    }\n                    function textareaOnScroll(event) {\n                        _targetElement[_strScrollLeft](_rtlScrollBehavior.i && _normalizeRTLCache ? 9999999 : 0);\n                        _targetElement[_strScrollTop](0);\n                        COMPATIBILITY.prvD(event);\n                        COMPATIBILITY.stpP(event);\n                        return false;\n                    }\n                    function textareaOnDrop(event) {\n                        setTimeout(function () {\n                            if (!_destroyed)\n                                updateTextarea();\n                        }, 50);\n                    }\n                    function textareaOnFocus() {\n                        _textareaHasFocus = true;\n                        addClass(_hostElement, strFocus);\n                    }\n                    function textareaOnFocusout() {\n                        _textareaHasFocus = false;\n                        textareaKeyDownKeyCodesList = [];\n                        removeClass(_hostElement, strFocus);\n                        updateTextarea(true);\n                    }\n                    function textareaOnKeyDown(event) {\n                        var keyCode = event.keyCode;\n\n                        if (inArray(keyCode, textareaKeyDownRestrictedKeyCodes) < 0) {\n                            if (!textareaKeyDownKeyCodesList[LEXICON.l]) {\n                                updateTextarea();\n                                textareaUpdateIntervalID = setInterval(updateTextarea, 1000 / 60);\n                            }\n                            if (inArray(keyCode, textareaKeyDownKeyCodesList) < 0)\n                                textareaKeyDownKeyCodesList.push(keyCode);\n                        }\n                    }\n                    function textareaOnKeyUp(event) {\n                        var keyCode = event.keyCode;\n                        var index = inArray(keyCode, textareaKeyDownKeyCodesList);\n\n                        if (inArray(keyCode, textareaKeyDownRestrictedKeyCodes) < 0) {\n                            if (index > -1)\n                                textareaKeyDownKeyCodesList.splice(index, 1);\n                            if (!textareaKeyDownKeyCodesList[LEXICON.l])\n                                updateTextarea(true);\n                        }\n                    }\n                    function contentOnTransitionEnd(event) {\n                        if (_autoUpdateCache === true)\n                            return;\n                        event = event.originalEvent || event;\n                        if (isSizeAffectingCSSProperty(event.propertyName))\n                            _base.update(_strAuto);\n                    }\n                    function viewportOnScroll(event) {\n                        if (!_sleeping) {\n                            if (scrollStopTimeoutId !== undefined)\n                                clearTimeout(scrollStopTimeoutId);\n                            else {\n                                if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                                    refreshScrollbarsAutoHide(true);\n\n                                if (!nativeOverlayScrollbarsAreActive())\n                                    addClass(_hostElement, _classNameHostScrolling);\n\n                                dispatchCallback('onScrollStart', event);\n                            }\n\n                            //if a scrollbars handle gets dragged, the mousemove event is responsible for refreshing the handle offset\n                            //because if CSS scroll-snap is used, the handle offset gets only refreshed on every snap point\n                            //this looks laggy & clunky, it looks much better if the offset refreshes with the mousemove\n                            if (!_scrollbarsHandlesDefineScrollPos) {\n                                refreshScrollbarHandleOffset(true);\n                                refreshScrollbarHandleOffset(false);\n                            }\n                            dispatchCallback('onScroll', event);\n\n                            scrollStopTimeoutId = setTimeout(function () {\n                                if (!_destroyed) {\n                                    //OnScrollStop:\n                                    clearTimeout(scrollStopTimeoutId);\n                                    scrollStopTimeoutId = undefined;\n\n                                    if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                                        refreshScrollbarsAutoHide(false);\n\n                                    if (!nativeOverlayScrollbarsAreActive())\n                                        removeClass(_hostElement, _classNameHostScrolling);\n\n                                    dispatchCallback('onScrollStop', event);\n                                }\n                            }, scrollStopDelay);\n                        }\n                    }\n\n\n                    if (_isTextarea) {\n                        if (_msieVersion > 9 || !_autoUpdateRecommended) {\n                            addDestroyEventListener(_targetElement, 'input', updateTextarea);\n                        }\n                        else {\n                            addDestroyEventListener(_targetElement,\n                                [_strKeyDownEvent, _strKeyUpEvent],\n                                [textareaOnKeyDown, textareaOnKeyUp]);\n                        }\n\n                        addDestroyEventListener(_targetElement,\n                            [_strScroll, 'drop', strFocus, strFocus + 'out'],\n                            [textareaOnScroll, textareaOnDrop, textareaOnFocus, textareaOnFocusout]);\n                    }\n                    else {\n                        addDestroyEventListener(_contentElement, _strTransitionEndEvent, contentOnTransitionEnd);\n                    }\n                    addDestroyEventListener(_viewportElement, _strScroll, viewportOnScroll, true);\n                }\n\n\n                //==== Scrollbars ====//\n\n                /**\n                 * Builds or destroys all scrollbar DOM elements (scrollbar, track, handle)\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                function setupScrollbarsDOM(destroy) {\n                    var selectOrGenerateScrollbarDOM = function (isHorizontal) {\n                        var scrollbarClassName = isHorizontal ? _classNameScrollbarHorizontal : _classNameScrollbarVertical;\n                        var scrollbar = selectOrGenerateDivByClass(_classNameScrollbar + _strSpace + scrollbarClassName, true);\n                        var track = selectOrGenerateDivByClass(_classNameScrollbarTrack, scrollbar);\n                        var handle = selectOrGenerateDivByClass(_classNameScrollbarHandle, scrollbar);\n\n                        if (!_domExists && !destroy) {\n                            scrollbar.append(track);\n                            track.append(handle);\n                        }\n\n                        return {\n                            _scrollbar: scrollbar,\n                            _track: track,\n                            _handle: handle\n                        };\n                    };\n                    function resetScrollbarDOM(isHorizontal) {\n                        var scrollbarVars = getScrollbarVars(isHorizontal);\n                        var scrollbar = scrollbarVars._scrollbar;\n                        var track = scrollbarVars._track;\n                        var handle = scrollbarVars._handle;\n\n                        if (_domExists && _initialized) {\n                            each([scrollbar, track, handle], function (i, elm) {\n                                removeClass(elm.removeAttr(LEXICON.s), _classNamesDynamicDestroy);\n                            });\n                        }\n                        else {\n                            remove(scrollbar || selectOrGenerateScrollbarDOM(isHorizontal)._scrollbar);\n                        }\n                    }\n                    var horizontalElements;\n                    var verticalElements;\n\n                    if (!destroy) {\n                        horizontalElements = selectOrGenerateScrollbarDOM(true);\n                        verticalElements = selectOrGenerateScrollbarDOM();\n\n                        _scrollbarHorizontalElement = horizontalElements._scrollbar;\n                        _scrollbarHorizontalTrackElement = horizontalElements._track;\n                        _scrollbarHorizontalHandleElement = horizontalElements._handle;\n                        _scrollbarVerticalElement = verticalElements._scrollbar;\n                        _scrollbarVerticalTrackElement = verticalElements._track;\n                        _scrollbarVerticalHandleElement = verticalElements._handle;\n\n                        if (!_domExists) {\n                            _paddingElement.after(_scrollbarVerticalElement);\n                            _paddingElement.after(_scrollbarHorizontalElement);\n                        }\n                    }\n                    else {\n                        resetScrollbarDOM(true);\n                        resetScrollbarDOM();\n                    }\n                }\n\n                /**\n                 * Initializes all scrollbar interactivity events. (track and handle dragging, clicking, scrolling)\n                 * @param isHorizontal True if the target scrollbar is the horizontal scrollbar, false if the target scrollbar is the vertical scrollbar.\n                 */\n                function setupScrollbarEvents(isHorizontal) {\n                    var scrollbarVars = getScrollbarVars(isHorizontal);\n                    var scrollbarVarsInfo = scrollbarVars._info;\n                    var insideIFrame = _windowElementNative.top !== _windowElementNative;\n                    var xy = scrollbarVars._x_y;\n                    var XY = scrollbarVars._X_Y;\n                    var scroll = _strScroll + scrollbarVars._Left_Top;\n                    var strActive = 'active';\n                    var strSnapHandle = 'snapHandle';\n                    var strClickEvent = 'click';\n                    var scrollDurationFactor = 1;\n                    var increaseDecreaseScrollAmountKeyCodes = [16, 17]; //shift, ctrl\n                    var trackTimeout;\n                    var mouseDownScroll;\n                    var mouseDownOffset;\n                    var mouseDownInvertedScale;\n\n                    function getPointerPosition(event) {\n                        return _msieVersion && insideIFrame ? event['screen' + XY] : COMPATIBILITY.page(event)[xy]; //use screen coordinates in EDGE & IE because the page values are incorrect in frames.\n                    }\n                    function getPreparedScrollbarsOption(name) {\n                        return _currentPreparedOptions.scrollbars[name];\n                    }\n                    function increaseTrackScrollAmount() {\n                        scrollDurationFactor = 0.5;\n                    }\n                    function decreaseTrackScrollAmount() {\n                        scrollDurationFactor = 1;\n                    }\n                    function stopClickEventPropagation(event) {\n                        COMPATIBILITY.stpP(event);\n                    }\n                    function documentKeyDown(event) {\n                        if (inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1)\n                            increaseTrackScrollAmount();\n                    }\n                    function documentKeyUp(event) {\n                        if (inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1)\n                            decreaseTrackScrollAmount();\n                    }\n                    function onMouseTouchDownContinue(event) {\n                        var originalEvent = event.originalEvent || event;\n                        var isTouchEvent = originalEvent.touches !== undefined;\n                        return _sleeping || _destroyed || nativeOverlayScrollbarsAreActive() || !_scrollbarsDragScrollingCache || (isTouchEvent && !getPreparedScrollbarsOption('touchSupport')) ? false : COMPATIBILITY.mBtn(event) === 1 || isTouchEvent;\n                    }\n                    function documentDragMove(event) {\n                        if (onMouseTouchDownContinue(event)) {\n                            var trackLength = scrollbarVarsInfo._trackLength;\n                            var handleLength = scrollbarVarsInfo._handleLength;\n                            var scrollRange = scrollbarVarsInfo._maxScroll;\n                            var scrollRaw = (getPointerPosition(event) - mouseDownOffset) * mouseDownInvertedScale;\n                            var scrollDeltaPercent = scrollRaw / (trackLength - handleLength);\n                            var scrollDelta = (scrollRange * scrollDeltaPercent);\n                            scrollDelta = isFinite(scrollDelta) ? scrollDelta : 0;\n                            if (_isRTL && isHorizontal && !_rtlScrollBehavior.i)\n                                scrollDelta *= -1;\n\n                            _viewportElement[scroll](MATH.round(mouseDownScroll + scrollDelta));\n\n                            if (_scrollbarsHandlesDefineScrollPos)\n                                refreshScrollbarHandleOffset(isHorizontal, mouseDownScroll + scrollDelta);\n\n                            if (!_supportPassiveEvents)\n                                COMPATIBILITY.prvD(event);\n                        }\n                        else\n                            documentMouseTouchUp(event);\n                    }\n                    function documentMouseTouchUp(event) {\n                        event = event || event.originalEvent;\n\n                        setupResponsiveEventListener(_documentElement,\n                            [_strMouseTouchMoveEvent, _strMouseTouchUpEvent, _strKeyDownEvent, _strKeyUpEvent, _strSelectStartEvent],\n                            [documentDragMove, documentMouseTouchUp, documentKeyDown, documentKeyUp, documentOnSelectStart],\n                            true);\n                        COMPATIBILITY.rAF()(function() {\n                            setupResponsiveEventListener(_documentElement, strClickEvent, stopClickEventPropagation, true, { _capture: true });\n                        });\n                        \n                            \n                        if (_scrollbarsHandlesDefineScrollPos)\n                            refreshScrollbarHandleOffset(isHorizontal, true);\n\n                        _scrollbarsHandlesDefineScrollPos = false;\n                        removeClass(_bodyElement, _classNameDragging);\n                        removeClass(scrollbarVars._handle, strActive);\n                        removeClass(scrollbarVars._track, strActive);\n                        removeClass(scrollbarVars._scrollbar, strActive);\n\n                        mouseDownScroll = undefined;\n                        mouseDownOffset = undefined;\n                        mouseDownInvertedScale = 1;\n\n                        decreaseTrackScrollAmount();\n\n                        if (trackTimeout !== undefined) {\n                            _base.scrollStop();\n                            clearTimeout(trackTimeout);\n                            trackTimeout = undefined;\n                        }\n\n                        if (event) {\n                            var rect = _hostElementNative[LEXICON.bCR]();\n                            var mouseInsideHost = event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;\n\n                            //if mouse is outside host element\n                            if (!mouseInsideHost)\n                                hostOnMouseLeave();\n\n                            if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                                refreshScrollbarsAutoHide(false);\n                        }\n                    }\n                    function onHandleMouseTouchDown(event) {\n                        if (onMouseTouchDownContinue(event))\n                            onHandleMouseTouchDownAction(event);\n                    }\n                    function onHandleMouseTouchDownAction(event) {\n                        mouseDownScroll = _viewportElement[scroll]();\n                        mouseDownScroll = isNaN(mouseDownScroll) ? 0 : mouseDownScroll;\n                        if (_isRTL && isHorizontal && !_rtlScrollBehavior.n || !_isRTL)\n                            mouseDownScroll = mouseDownScroll < 0 ? 0 : mouseDownScroll;\n\n                        mouseDownInvertedScale = getHostElementInvertedScale()[xy];\n                        mouseDownOffset = getPointerPosition(event);\n\n                        _scrollbarsHandlesDefineScrollPos = !getPreparedScrollbarsOption(strSnapHandle);\n                        addClass(_bodyElement, _classNameDragging);\n                        addClass(scrollbarVars._handle, strActive);\n                        addClass(scrollbarVars._scrollbar, strActive);\n\n                        setupResponsiveEventListener(_documentElement,\n                            [_strMouseTouchMoveEvent, _strMouseTouchUpEvent, _strSelectStartEvent],\n                            [documentDragMove, documentMouseTouchUp, documentOnSelectStart]);\n                        COMPATIBILITY.rAF()(function() {\n                            setupResponsiveEventListener(_documentElement, strClickEvent, stopClickEventPropagation, false, { _capture: true });\n                        });\n                        \n\n                        if (_msieVersion || !_documentMixed)\n                            COMPATIBILITY.prvD(event);\n                        COMPATIBILITY.stpP(event);\n                    }\n                    function onTrackMouseTouchDown(event) {\n                        if (onMouseTouchDownContinue(event)) {\n                            var handleToViewportRatio = scrollbarVars._info._handleLength / Math.round(MATH.min(1, _viewportSize[scrollbarVars._w_h] / _contentScrollSizeCache[scrollbarVars._w_h]) * scrollbarVars._info._trackLength);\n                            var scrollDistance = MATH.round(_viewportSize[scrollbarVars._w_h] * handleToViewportRatio);\n                            var scrollBaseDuration = 270 * handleToViewportRatio;\n                            var scrollFirstIterationDelay = 400 * handleToViewportRatio;\n                            var trackOffset = scrollbarVars._track.offset()[scrollbarVars._left_top];\n                            var ctrlKey = event.ctrlKey;\n                            var instantScroll = event.shiftKey;\n                            var instantScrollTransition = instantScroll && ctrlKey;\n                            var isFirstIteration = true;\n                            var easing = 'linear';\n                            var decreaseScroll;\n                            var finishedCondition;\n                            var scrollActionFinsished = function (transition) {\n                                if (_scrollbarsHandlesDefineScrollPos)\n                                    refreshScrollbarHandleOffset(isHorizontal, transition);\n                            };\n                            var scrollActionInstantFinished = function () {\n                                scrollActionFinsished();\n                                onHandleMouseTouchDownAction(event);\n                            };\n                            var scrollAction = function () {\n                                if (!_destroyed) {\n                                    var mouseOffset = (mouseDownOffset - trackOffset) * mouseDownInvertedScale;\n                                    var handleOffset = scrollbarVarsInfo._handleOffset;\n                                    var trackLength = scrollbarVarsInfo._trackLength;\n                                    var handleLength = scrollbarVarsInfo._handleLength;\n                                    var scrollRange = scrollbarVarsInfo._maxScroll;\n                                    var currScroll = scrollbarVarsInfo._currentScroll;\n                                    var scrollDuration = scrollBaseDuration * scrollDurationFactor;\n                                    var timeoutDelay = isFirstIteration ? MATH.max(scrollFirstIterationDelay, scrollDuration) : scrollDuration;\n                                    var instantScrollPosition = scrollRange * ((mouseOffset - (handleLength / 2)) / (trackLength - handleLength)); // 100% * positionPercent\n                                    var rtlIsNormal = _isRTL && isHorizontal && ((!_rtlScrollBehavior.i && !_rtlScrollBehavior.n) || _normalizeRTLCache);\n                                    var decreaseScrollCondition = rtlIsNormal ? handleOffset < mouseOffset : handleOffset > mouseOffset;\n                                    var scrollObj = {};\n                                    var animationObj = {\n                                        easing: easing,\n                                        step: function (now) {\n                                            if (_scrollbarsHandlesDefineScrollPos) {\n                                                _viewportElement[scroll](now); //https://github.com/jquery/jquery/issues/4340\n                                                refreshScrollbarHandleOffset(isHorizontal, now);\n                                            }\n                                        }\n                                    };\n                                    instantScrollPosition = isFinite(instantScrollPosition) ? instantScrollPosition : 0;\n                                    instantScrollPosition = _isRTL && isHorizontal && !_rtlScrollBehavior.i ? (scrollRange - instantScrollPosition) : instantScrollPosition;\n\n                                    //_base.scrollStop();\n\n                                    if (instantScroll) {\n                                        _viewportElement[scroll](instantScrollPosition); //scroll instantly to new position\n                                        if (instantScrollTransition) {\n                                            //get the scroll position after instant scroll (in case CSS Snap Points are used) to get the correct snapped scroll position\n                                            //and the animation stops at the correct point\n                                            instantScrollPosition = _viewportElement[scroll]();\n                                            //scroll back to the position before instant scrolling so animation can be performed\n                                            _viewportElement[scroll](currScroll);\n\n                                            instantScrollPosition = rtlIsNormal && _rtlScrollBehavior.i ? (scrollRange - instantScrollPosition) : instantScrollPosition;\n                                            instantScrollPosition = rtlIsNormal && _rtlScrollBehavior.n ? -instantScrollPosition : instantScrollPosition;\n\n                                            scrollObj[xy] = instantScrollPosition;\n                                            _base.scroll(scrollObj, extendDeep(animationObj, {\n                                                duration: 130,\n                                                complete: scrollActionInstantFinished\n                                            }));\n                                        }\n                                        else\n                                            scrollActionInstantFinished();\n                                    }\n                                    else {\n                                        decreaseScroll = isFirstIteration ? decreaseScrollCondition : decreaseScroll;\n                                        finishedCondition = rtlIsNormal\n                                            ? (decreaseScroll ? handleOffset + handleLength >= mouseOffset : handleOffset <= mouseOffset)\n                                            : (decreaseScroll ? handleOffset <= mouseOffset : handleOffset + handleLength >= mouseOffset);\n\n                                        if (finishedCondition) {\n                                            clearTimeout(trackTimeout);\n                                            _base.scrollStop();\n                                            trackTimeout = undefined;\n                                            scrollActionFinsished(true);\n                                        }\n                                        else {\n                                            trackTimeout = setTimeout(scrollAction, timeoutDelay);\n\n                                            scrollObj[xy] = (decreaseScroll ? '-=' : '+=') + scrollDistance;\n                                            _base.scroll(scrollObj, extendDeep(animationObj, {\n                                                duration: scrollDuration\n                                            }));\n                                        }\n                                        isFirstIteration = false;\n                                    }\n                                }\n                            };\n                            if (ctrlKey)\n                                increaseTrackScrollAmount();\n\n                            mouseDownInvertedScale = getHostElementInvertedScale()[xy];\n                            mouseDownOffset = COMPATIBILITY.page(event)[xy];\n\n                            _scrollbarsHandlesDefineScrollPos = !getPreparedScrollbarsOption(strSnapHandle);\n                            addClass(_bodyElement, _classNameDragging);\n                            addClass(scrollbarVars._track, strActive);\n                            addClass(scrollbarVars._scrollbar, strActive);\n\n                            setupResponsiveEventListener(_documentElement,\n                                [_strMouseTouchUpEvent, _strKeyDownEvent, _strKeyUpEvent, _strSelectStartEvent],\n                                [documentMouseTouchUp, documentKeyDown, documentKeyUp, documentOnSelectStart]);\n\n                            scrollAction();\n                            COMPATIBILITY.prvD(event);\n                            COMPATIBILITY.stpP(event);\n                        }\n                    }\n                    function onTrackMouseTouchEnter(event) {\n                        //make sure both scrollbars will stay visible if one scrollbar is hovered if autoHide is \"scroll\" or \"move\".\n                        _scrollbarsHandleHovered = true;\n                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                            refreshScrollbarsAutoHide(true);\n                    }\n                    function onTrackMouseTouchLeave(event) {\n                        _scrollbarsHandleHovered = false;\n                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                            refreshScrollbarsAutoHide(false);\n                    }\n                    function onScrollbarMouseTouchDown(event) {\n                        COMPATIBILITY.stpP(event);\n                    }\n\n                    addDestroyEventListener(scrollbarVars._handle,\n                        _strMouseTouchDownEvent,\n                        onHandleMouseTouchDown);\n                    addDestroyEventListener(scrollbarVars._track,\n                        [_strMouseTouchDownEvent, _strMouseEnter, _strMouseLeave],\n                        [onTrackMouseTouchDown, onTrackMouseTouchEnter, onTrackMouseTouchLeave]);\n                    addDestroyEventListener(scrollbarVars._scrollbar,\n                        _strMouseTouchDownEvent,\n                        onScrollbarMouseTouchDown);\n\n                    if (_supportTransition) {\n                        addDestroyEventListener(scrollbarVars._scrollbar, _strTransitionEndEvent, function (event) {\n                            if (event.target !== scrollbarVars._scrollbar[0])\n                                return;\n                            refreshScrollbarHandleLength(isHorizontal);\n                            refreshScrollbarHandleOffset(isHorizontal);\n                        });\n                    }\n                }\n\n                /**\n                 * Shows or hides the given scrollbar and applied a class name which indicates if the scrollbar is scrollable or not.\n                 * @param isHorizontal True if the horizontal scrollbar is the target, false if the vertical scrollbar is the target.\n                 * @param shallBeVisible True if the scrollbar shall be shown, false if hidden.\n                 * @param canScroll True if the scrollbar is scrollable, false otherwise.\n                 */\n                function refreshScrollbarAppearance(isHorizontal, shallBeVisible, canScroll) {\n                    var scrollbarHiddenClassName = isHorizontal ? _classNameHostScrollbarHorizontalHidden : _classNameHostScrollbarVerticalHidden;\n                    var scrollbarElement = isHorizontal ? _scrollbarHorizontalElement : _scrollbarVerticalElement;\n\n                    addRemoveClass(_hostElement, scrollbarHiddenClassName, !shallBeVisible);\n                    addRemoveClass(scrollbarElement, _classNameScrollbarUnusable, !canScroll);\n                }\n\n                /**\n                 * Autoshows / autohides both scrollbars with.\n                 * @param shallBeVisible True if the scrollbars shall be autoshown (only the case if they are hidden by a autohide), false if the shall be auto hidden.\n                 * @param delayfree True if the scrollbars shall be hidden without a delay, false or undefined otherwise.\n                 */\n                function refreshScrollbarsAutoHide(shallBeVisible, delayfree) {\n                    clearTimeout(_scrollbarsAutoHideTimeoutId);\n                    if (shallBeVisible) {\n                        //if(_hasOverflowCache.x && _hideOverflowCache.xs)\n                        removeClass(_scrollbarHorizontalElement, _classNameScrollbarAutoHidden);\n                        //if(_hasOverflowCache.y && _hideOverflowCache.ys)\n                        removeClass(_scrollbarVerticalElement, _classNameScrollbarAutoHidden);\n                    }\n                    else {\n                        var anyActive;\n                        var strActive = 'active';\n                        var hide = function () {\n                            if (!_scrollbarsHandleHovered && !_destroyed) {\n                                anyActive = _scrollbarHorizontalHandleElement.hasClass(strActive) || _scrollbarVerticalHandleElement.hasClass(strActive);\n                                if (!anyActive && (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove || _scrollbarsAutoHideLeave))\n                                    addClass(_scrollbarHorizontalElement, _classNameScrollbarAutoHidden);\n                                if (!anyActive && (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove || _scrollbarsAutoHideLeave))\n                                    addClass(_scrollbarVerticalElement, _classNameScrollbarAutoHidden);\n                            }\n                        };\n                        if (_scrollbarsAutoHideDelay > 0 && delayfree !== true)\n                            _scrollbarsAutoHideTimeoutId = setTimeout(hide, _scrollbarsAutoHideDelay);\n                        else\n                            hide();\n                    }\n                }\n\n                /**\n                 * Refreshes the handle length of the given scrollbar.\n                 * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.\n                 */\n                function refreshScrollbarHandleLength(isHorizontal) {\n                    var handleCSS = {};\n                    var scrollbarVars = getScrollbarVars(isHorizontal);\n                    var scrollbarVarsInfo = scrollbarVars._info;\n                    var digit = 1000000;\n                    //get and apply intended handle length\n                    var handleRatio = MATH.min(1, _viewportSize[scrollbarVars._w_h] / _contentScrollSizeCache[scrollbarVars._w_h]);\n                    handleCSS[scrollbarVars._width_height] = (MATH.floor(handleRatio * 100 * digit) / digit) + '%'; //the last * digit / digit is for flooring to the 4th digit\n\n                    if (!nativeOverlayScrollbarsAreActive())\n                        scrollbarVars._handle.css(handleCSS);\n\n                    //measure the handle length to respect min & max length\n                    scrollbarVarsInfo._handleLength = scrollbarVars._handle[0]['offset' + scrollbarVars._Width_Height];\n                    scrollbarVarsInfo._handleLengthRatio = handleRatio;\n                }\n\n                /**\n                 * Refreshes the handle offset of the given scrollbar.\n                 * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.\n                 * @param scrollOrTransition The scroll position of the given scrollbar axis to which the handle shall be moved or a boolean which indicates whether a transition shall be applied. If undefined or boolean if the current scroll-offset is taken. (if isHorizontal ? scrollLeft : scrollTop)\n                 */\n                function refreshScrollbarHandleOffset(isHorizontal, scrollOrTransition) {\n                    var transition = type(scrollOrTransition) == TYPES.b;\n                    var transitionDuration = 250;\n                    var isRTLisHorizontal = _isRTL && isHorizontal;\n                    var scrollbarVars = getScrollbarVars(isHorizontal);\n                    var scrollbarVarsInfo = scrollbarVars._info;\n                    var strTranslateBrace = 'translate(';\n                    var strTransform = VENDORS._cssProperty('transform');\n                    var strTransition = VENDORS._cssProperty('transition');\n                    var nativeScroll = isHorizontal ? _viewportElement[_strScrollLeft]() : _viewportElement[_strScrollTop]();\n                    var currentScroll = scrollOrTransition === undefined || transition ? nativeScroll : scrollOrTransition;\n\n                    //measure the handle length to respect min & max length\n                    var handleLength = scrollbarVarsInfo._handleLength;\n                    var trackLength = scrollbarVars._track[0]['offset' + scrollbarVars._Width_Height];\n                    var handleTrackDiff = trackLength - handleLength;\n                    var handleCSS = {};\n                    var transformOffset;\n                    var translateValue;\n\n                    //DONT use the variable '_contentScrollSizeCache[scrollbarVars._w_h]' instead of '_viewportElement[0]['scroll' + scrollbarVars._Width_Height]'\n                    // because its a bit behind during the small delay when content size updates\n                    //(delay = mutationObserverContentLag, if its 0 then this var could be used)\n                    var maxScroll = (_viewportElementNative[_strScroll + scrollbarVars._Width_Height] - _viewportElementNative['client' + scrollbarVars._Width_Height]) * (_rtlScrollBehavior.n && isRTLisHorizontal ? -1 : 1); //* -1 if rtl scroll max is negative\n                    var getScrollRatio = function (base) {\n                        return isNaN(base / maxScroll) ? 0 : MATH.max(0, MATH.min(1, base / maxScroll));\n                    };\n                    var getHandleOffset = function (scrollRatio) {\n                        var offset = handleTrackDiff * scrollRatio;\n                        offset = isNaN(offset) ? 0 : offset;\n                        offset = (isRTLisHorizontal && !_rtlScrollBehavior.i) ? (trackLength - handleLength - offset) : offset;\n                        offset = MATH.max(0, offset);\n                        return offset;\n                    };\n                    var scrollRatio = getScrollRatio(nativeScroll);\n                    var unsnappedScrollRatio = getScrollRatio(currentScroll);\n                    var handleOffset = getHandleOffset(unsnappedScrollRatio);\n                    var snappedHandleOffset = getHandleOffset(scrollRatio);\n\n                    scrollbarVarsInfo._maxScroll = maxScroll;\n                    scrollbarVarsInfo._currentScroll = nativeScroll;\n                    scrollbarVarsInfo._currentScrollRatio = scrollRatio;\n\n                    if (_supportTransform) {\n                        transformOffset = isRTLisHorizontal ? -(trackLength - handleLength - handleOffset) : handleOffset; //in px\n                        //transformOffset = (transformOffset / trackLength * 100) * (trackLength / handleLength); //in %\n                        translateValue = isHorizontal ? strTranslateBrace + transformOffset + 'px, 0)' : strTranslateBrace + '0, ' + transformOffset + 'px)';\n\n                        handleCSS[strTransform] = translateValue;\n\n                        //apply or clear up transition\n                        if (_supportTransition)\n                            handleCSS[strTransition] = transition && MATH.abs(handleOffset - scrollbarVarsInfo._handleOffset) > 1 ? getCSSTransitionString(scrollbarVars._handle) + ', ' + (strTransform + _strSpace + transitionDuration + 'ms') : _strEmpty;\n                    }\n                    else\n                        handleCSS[scrollbarVars._left_top] = handleOffset;\n\n\n                    //only apply css if offset has changed and overflow exists.\n                    if (!nativeOverlayScrollbarsAreActive()) {\n                        scrollbarVars._handle.css(handleCSS);\n\n                        //clear up transition\n                        if (_supportTransform && _supportTransition && transition) {\n                            scrollbarVars._handle.one(_strTransitionEndEvent, function () {\n                                if (!_destroyed)\n                                    scrollbarVars._handle.css(strTransition, _strEmpty);\n                            });\n                        }\n                    }\n\n                    scrollbarVarsInfo._handleOffset = handleOffset;\n                    scrollbarVarsInfo._snappedHandleOffset = snappedHandleOffset;\n                    scrollbarVarsInfo._trackLength = trackLength;\n                }\n\n                /**\n                 * Refreshes the interactivity of the given scrollbar element.\n                 * @param isTrack True if the track element is the target, false if the handle element is the target.\n                 * @param value True for interactivity false for no interactivity.\n                 */\n                function refreshScrollbarsInteractive(isTrack, value) {\n                    var action = value ? 'removeClass' : 'addClass';\n                    var element1 = isTrack ? _scrollbarHorizontalTrackElement : _scrollbarHorizontalHandleElement;\n                    var element2 = isTrack ? _scrollbarVerticalTrackElement : _scrollbarVerticalHandleElement;\n                    var className = isTrack ? _classNameScrollbarTrackOff : _classNameScrollbarHandleOff;\n\n                    element1[action](className);\n                    element2[action](className);\n                }\n\n                /**\n                 * Returns a object which is used for fast access for specific variables.\n                 * @param isHorizontal True if the horizontal scrollbar vars shall be accessed, false if the vertical scrollbar vars shall be accessed.\n                 * @returns {{wh: string, WH: string, lt: string, _wh: string, _lt: string, t: *, h: *, c: {}, s: *}}\n                 */\n                function getScrollbarVars(isHorizontal) {\n                    return {\n                        _width_height: isHorizontal ? _strWidth : _strHeight,\n                        _Width_Height: isHorizontal ? 'Width' : 'Height',\n                        _left_top: isHorizontal ? _strLeft : _strTop,\n                        _Left_Top: isHorizontal ? 'Left' : 'Top',\n                        _x_y: isHorizontal ? _strX : _strY,\n                        _X_Y: isHorizontal ? 'X' : 'Y',\n                        _w_h: isHorizontal ? 'w' : 'h',\n                        _l_t: isHorizontal ? 'l' : 't',\n                        _track: isHorizontal ? _scrollbarHorizontalTrackElement : _scrollbarVerticalTrackElement,\n                        _handle: isHorizontal ? _scrollbarHorizontalHandleElement : _scrollbarVerticalHandleElement,\n                        _scrollbar: isHorizontal ? _scrollbarHorizontalElement : _scrollbarVerticalElement,\n                        _info: isHorizontal ? _scrollHorizontalInfo : _scrollVerticalInfo\n                    };\n                }\n\n\n                //==== Scrollbar Corner ====//\n\n                /**\n                 * Builds or destroys the scrollbar corner DOM element.\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                function setupScrollbarCornerDOM(destroy) {\n                    _scrollbarCornerElement = _scrollbarCornerElement || selectOrGenerateDivByClass(_classNameScrollbarCorner, true);\n\n                    if (!destroy) {\n                        if (!_domExists) {\n                            _hostElement.append(_scrollbarCornerElement);\n                        }\n                    }\n                    else {\n                        if (_domExists && _initialized) {\n                            removeClass(_scrollbarCornerElement.removeAttr(LEXICON.s), _classNamesDynamicDestroy);\n                        }\n                        else {\n                            remove(_scrollbarCornerElement);\n                        }\n                    }\n                }\n\n                /**\n                 * Initializes all scrollbar corner interactivity events.\n                 */\n                function setupScrollbarCornerEvents() {\n                    var insideIFrame = _windowElementNative.top !== _windowElementNative;\n                    var mouseDownPosition = {};\n                    var mouseDownSize = {};\n                    var mouseDownInvertedScale = {};\n                    var reconnectMutationObserver;\n\n                    function documentDragMove(event) {\n                        if (onMouseTouchDownContinue(event)) {\n                            var pageOffset = getCoordinates(event);\n                            var hostElementCSS = {};\n                            if (_resizeHorizontal || _resizeBoth)\n                                hostElementCSS[_strWidth] = (mouseDownSize.w + (pageOffset.x - mouseDownPosition.x) * mouseDownInvertedScale.x);\n                            if (_resizeVertical || _resizeBoth)\n                                hostElementCSS[_strHeight] = (mouseDownSize.h + (pageOffset.y - mouseDownPosition.y) * mouseDownInvertedScale.y);\n                            _hostElement.css(hostElementCSS);\n                            COMPATIBILITY.stpP(event);\n                        }\n                        else {\n                            documentMouseTouchUp(event);\n                        }\n                    }\n                    function documentMouseTouchUp(event) {\n                        var eventIsTrusted = event !== undefined;\n\n                        setupResponsiveEventListener(_documentElement,\n                            [_strSelectStartEvent, _strMouseTouchMoveEvent, _strMouseTouchUpEvent],\n                            [documentOnSelectStart, documentDragMove, documentMouseTouchUp],\n                            true);\n\n                        removeClass(_bodyElement, _classNameDragging);\n                        if (_scrollbarCornerElement.releaseCapture)\n                            _scrollbarCornerElement.releaseCapture();\n\n                        if (eventIsTrusted) {\n                            if (reconnectMutationObserver)\n                                connectMutationObservers();\n                            _base.update(_strAuto);\n                        }\n                        reconnectMutationObserver = false;\n                    }\n                    function onMouseTouchDownContinue(event) {\n                        var originalEvent = event.originalEvent || event;\n                        var isTouchEvent = originalEvent.touches !== undefined;\n                        return _sleeping || _destroyed ? false : COMPATIBILITY.mBtn(event) === 1 || isTouchEvent;\n                    }\n                    function getCoordinates(event) {\n                        return _msieVersion && insideIFrame ? { x: event.screenX, y: event.screenY } : COMPATIBILITY.page(event);\n                    }\n\n                    addDestroyEventListener(_scrollbarCornerElement, _strMouseTouchDownEvent, function (event) {\n                        if (onMouseTouchDownContinue(event) && !_resizeNone) {\n                            if (_mutationObserversConnected) {\n                                reconnectMutationObserver = true;\n                                disconnectMutationObservers();\n                            }\n\n                            mouseDownPosition = getCoordinates(event);\n\n                            mouseDownSize.w = _hostElementNative[LEXICON.oW] - (!_isBorderBox ? _paddingX : 0);\n                            mouseDownSize.h = _hostElementNative[LEXICON.oH] - (!_isBorderBox ? _paddingY : 0);\n                            mouseDownInvertedScale = getHostElementInvertedScale();\n\n                            setupResponsiveEventListener(_documentElement,\n                                [_strSelectStartEvent, _strMouseTouchMoveEvent, _strMouseTouchUpEvent],\n                                [documentOnSelectStart, documentDragMove, documentMouseTouchUp]);\n\n                            addClass(_bodyElement, _classNameDragging);\n                            if (_scrollbarCornerElement.setCapture)\n                                _scrollbarCornerElement.setCapture();\n\n                            COMPATIBILITY.prvD(event);\n                            COMPATIBILITY.stpP(event);\n                        }\n                    });\n                }\n\n\n                //==== Utils ====//\n\n                /**\n                 * Calls the callback with the given name. The Context of this callback is always _base (this).\n                 * @param name The name of the target which shall be called.\n                 * @param args The args with which the callback shall be called.\n                 * @param dependent Boolean which decides whether the callback shall be fired, undefined is like a \"true\" value.\n                 */\n                function dispatchCallback(name, args, dependent) {\n                    if (dependent === false)\n                        return;\n                    if (_initialized) {\n                        var callback = _currentPreparedOptions.callbacks[name];\n                        var extensionOnName = name;\n                        var ext;\n\n                        if (extensionOnName.substr(0, 2) === 'on')\n                            extensionOnName = extensionOnName.substr(2, 1).toLowerCase() + extensionOnName.substr(3);\n\n                        if (type(callback) == TYPES.f)\n                            callback.call(_base, args);\n\n                        each(_extensions, function () {\n                            ext = this;\n                            if (type(ext.on) == TYPES.f)\n                                ext.on(extensionOnName, args);\n                        });\n                    }\n                    else if (!_destroyed)\n                        _callbacksInitQeueue.push({ n: name, a: args });\n                }\n\n                /**\n                 * Sets the \"top, right, bottom, left\" properties, with a given prefix, of the given css object.\n                 * @param targetCSSObject The css object to which the values shall be applied.\n                 * @param prefix The prefix of the \"top, right, bottom, left\" css properties. (example: 'padding-' is a valid prefix)\n                 * @param values A array of values which shall be applied to the \"top, right, bottom, left\" -properties. The array order is [top, right, bottom, left].\n                 * If this argument is undefined the value '' (empty string) will be applied to all properties.\n                 */\n                function setTopRightBottomLeft(targetCSSObject, prefix, values) {\n                    prefix = prefix || _strEmpty;\n                    values = values || [_strEmpty, _strEmpty, _strEmpty, _strEmpty];\n\n                    targetCSSObject[prefix + _strTop] = values[0];\n                    targetCSSObject[prefix + _strRight] = values[1];\n                    targetCSSObject[prefix + _strBottom] = values[2];\n                    targetCSSObject[prefix + _strLeft] = values[3];\n                }\n\n                /**\n                 * Gets the \"top, right, bottom, left\" CSS properties of the CSS property with the given prefix from the host element.\n                 * @param prefix The prefix of the \"top, right, bottom, left\" css properties. (example: 'padding-' is a valid prefix)\n                 * @param suffix The suffix of the \"top, right, bottom, left\" css properties. (example: 'border-' is a valid prefix with '-width' is a valid suffix)\n                 * @param zeroX True if the x axis shall be 0.\n                 * @param zeroY True if the y axis shall be 0.\n                 * @returns {{}} The object which contains the numbers of the read CSS properties.\n                 */\n                function getTopRightBottomLeftHost(prefix, suffix, zeroX, zeroY) {\n                    suffix = suffix || _strEmpty;\n                    prefix = prefix || _strEmpty;\n                    return {\n                        t: zeroY ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strTop + suffix)),\n                        r: zeroX ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strRight + suffix)),\n                        b: zeroY ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strBottom + suffix)),\n                        l: zeroX ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strLeft + suffix))\n                    };\n                }\n\n                /**\n                 * Returns the computed CSS transition string from the given element.\n                 * @param element The element from which the transition string shall be returned.\n                 * @returns {string} The CSS transition string from the given element.\n                 */\n                function getCSSTransitionString(element) {\n                    var transitionStr = VENDORS._cssProperty('transition');\n                    var assembledValue = element.css(transitionStr);\n                    if (assembledValue)\n                        return assembledValue;\n                    var regExpString = '\\\\s*(' + '([^,(]+(\\\\(.+?\\\\))?)+' + ')[\\\\s,]*';\n                    var regExpMain = new RegExp(regExpString);\n                    var regExpValidate = new RegExp('^(' + regExpString + ')+$');\n                    var properties = 'property duration timing-function delay'.split(' ');\n                    var result = [];\n                    var strResult;\n                    var valueArray;\n                    var i = 0;\n                    var j;\n                    var splitCssStyleByComma = function (str) {\n                        strResult = [];\n                        if (!str.match(regExpValidate))\n                            return str;\n                        while (str.match(regExpMain)) {\n                            strResult.push(RegExp.$1);\n                            str = str.replace(regExpMain, _strEmpty);\n                        }\n\n                        return strResult;\n                    };\n                    for (; i < properties[LEXICON.l]; i++) {\n                        valueArray = splitCssStyleByComma(element.css(transitionStr + '-' + properties[i]));\n                        for (j = 0; j < valueArray[LEXICON.l]; j++)\n                            result[j] = (result[j] ? result[j] + _strSpace : _strEmpty) + valueArray[j];\n                    }\n                    return result.join(', ');\n                }\n\n                /**\n                 * Generates a Regular Expression which matches with a string which starts with 'os-host'.\n                 * @param {boolean} withCurrClassNameOption The Regular Expression also matches if the string is the current ClassName option (multiple values splitted by space possible).\n                 * @param {boolean} withOldClassNameOption The Regular Expression also matches if the string is the old ClassName option (multiple values splitted by space possible).\n                 */\n                function createHostClassNameRegExp(withCurrClassNameOption, withOldClassNameOption) {\n                    var i;\n                    var split;\n                    var appendix;\n                    var appendClasses = function (classes, condition) {\n                        appendix = '';\n                        if (condition && typeof classes == TYPES.s) {\n                            split = classes.split(_strSpace);\n                            for (i = 0; i < split[LEXICON.l]; i++)\n                                appendix += '|' + split[i] + '$';\n                            // split[i].replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&') for escaping regex characters\n                        }\n                        return appendix;\n                    };\n\n                    return new RegExp(\n                        '(^' + _classNameHostElement + '([-_].+|)$)' +\n                        appendClasses(_classNameCache, withCurrClassNameOption) +\n                        appendClasses(_oldClassName, withOldClassNameOption), 'g');\n                }\n\n                /**\n                 * Calculates the host-elements inverted scale. (invertedScale = 1 / scale)\n                 * @returns {{x: number, y: number}} The scale of the host-element.\n                 */\n                function getHostElementInvertedScale() {\n                    var rect = _paddingElementNative[LEXICON.bCR]();\n                    return {\n                        x: _supportTransform ? 1 / (MATH.round(rect.width) / _paddingElementNative[LEXICON.oW]) || 1 : 1,\n                        y: _supportTransform ? 1 / (MATH.round(rect.height) / _paddingElementNative[LEXICON.oH]) || 1 : 1\n                    };\n                }\n\n                /**\n                 * Checks whether the given object is a HTMLElement.\n                 * @param o The object which shall be checked.\n                 * @returns {boolean} True the given object is a HTMLElement, false otherwise.\n                 */\n                function isHTMLElement(o) {\n                    var strOwnerDocument = 'ownerDocument';\n                    var strHTMLElement = 'HTMLElement';\n                    var wnd = o && o[strOwnerDocument] ? (o[strOwnerDocument].parentWindow || window) : window;\n                    return (\n                        typeof wnd[strHTMLElement] == TYPES.o ? o instanceof wnd[strHTMLElement] : //DOM2\n                            o && typeof o == TYPES.o && o !== null && o.nodeType === 1 && typeof o.nodeName == TYPES.s\n                    );\n                }\n\n                /**\n                 * Compares 2 arrays and returns the differences between them as a array.\n                 * @param a1 The first array which shall be compared.\n                 * @param a2 The second array which shall be compared.\n                 * @returns {Array} The differences between the two arrays.\n                 */\n                function getArrayDifferences(a1, a2) {\n                    var a = [];\n                    var diff = [];\n                    var i;\n                    var k;\n                    for (i = 0; i < a1.length; i++)\n                        a[a1[i]] = true;\n                    for (i = 0; i < a2.length; i++) {\n                        if (a[a2[i]])\n                            delete a[a2[i]];\n                        else\n                            a[a2[i]] = true;\n                    }\n                    for (k in a)\n                        diff.push(k);\n                    return diff;\n                }\n\n                /**\n                 * Returns Zero or the number to which the value can be parsed.\n                 * @param value The value which shall be parsed.\n                 * @param toFloat Indicates whether the number shall be parsed to a float.\n                 */\n                function parseToZeroOrNumber(value, toFloat) {\n                    var num = toFloat ? parseFloat(value) : parseInt(value, 10);\n                    return isNaN(num) ? 0 : num;\n                }\n\n                /**\n                 * Gets several information of the textarea and returns them as a object or undefined if the browser doesn't support it.\n                 * @returns {{cursorRow: Number, cursorCol, rows: Number, cols: number, wRow: number, pos: number, max : number}} or undefined if not supported.\n                 */\n                function getTextareaInfo() {\n                    //read needed values\n                    var textareaCursorPosition = _targetElementNative.selectionStart;\n                    if (textareaCursorPosition === undefined)\n                        return;\n\n                    var textareaValue = _targetElement.val();\n                    var textareaLength = textareaValue[LEXICON.l];\n                    var textareaRowSplit = textareaValue.split('\\n');\n                    var textareaLastRow = textareaRowSplit[LEXICON.l];\n                    var textareaCurrentCursorRowSplit = textareaValue.substr(0, textareaCursorPosition).split('\\n');\n                    var widestRow = 0;\n                    var textareaLastCol = 0;\n                    var cursorRow = textareaCurrentCursorRowSplit[LEXICON.l];\n                    var cursorCol = textareaCurrentCursorRowSplit[textareaCurrentCursorRowSplit[LEXICON.l] - 1][LEXICON.l];\n                    var rowCols;\n                    var i;\n\n                    //get widest Row and the last column of the textarea\n                    for (i = 0; i < textareaRowSplit[LEXICON.l]; i++) {\n                        rowCols = textareaRowSplit[i][LEXICON.l];\n                        if (rowCols > textareaLastCol) {\n                            widestRow = i + 1;\n                            textareaLastCol = rowCols;\n                        }\n                    }\n\n                    return {\n                        _cursorRow: cursorRow, //cursorRow\n                        _cursorColumn: cursorCol, //cursorCol\n                        _rows: textareaLastRow, //rows\n                        _columns: textareaLastCol, //cols\n                        _widestRow: widestRow, //wRow\n                        _cursorPosition: textareaCursorPosition, //pos\n                        _cursorMax: textareaLength //max\n                    };\n                }\n\n                /**\n                 * Determines whether native overlay scrollbars are active.\n                 * @returns {boolean} True if native overlay scrollbars are active, false otherwise.\n                 */\n                function nativeOverlayScrollbarsAreActive() {\n                    return (_ignoreOverlayScrollbarHidingCache && (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y));\n                }\n\n                /**\n                 * Gets the element which is used to measure the content size.\n                 * @returns {*} TextareaCover if target element is textarea else the ContentElement.\n                 */\n                function getContentMeasureElement() {\n                    return _isTextarea ? _textareaCoverElement[0] : _contentElementNative;\n                }\n\n                /**\n                 * Generates a string which represents a HTML div with the given classes or attributes.\n                 * @param classesOrAttrs The class of the div as string or a object which represents the attributes of the div. (The class attribute can also be written as \"className\".)\n                 * @param content The content of the div as string.\n                 * @returns {string} The concated string which represents a HTML div and its content.\n                 */\n                function generateDiv(classesOrAttrs, content) {\n                    return '<div ' + (classesOrAttrs ? type(classesOrAttrs) == TYPES.s ?\n                        'class=\"' + classesOrAttrs + '\"' :\n                        (function () {\n                            var key;\n                            var attrs = _strEmpty;\n                            if (FRAMEWORK.isPlainObject(classesOrAttrs)) {\n                                for (key in classesOrAttrs)\n                                    attrs += (key === 'c' ? 'class' : key) + '=\"' + classesOrAttrs[key] + '\" ';\n                            }\n                            return attrs;\n                        })() :\n                        _strEmpty) +\n                        '>' +\n                        (content || _strEmpty) +\n                        '</div>';\n                }\n\n                /**\n                 * Selects or generates a div with the given class attribute.\n                 * @param className The class names (divided by spaces) of the div which shall be selected or generated.\n                 * @param selectParentOrOnlyChildren The parent element from which of the element shall be selected. (if undefined or boolean its hostElement)\n                 * If its a boolean it decides whether only the children of the host element shall be selected.\n                 * @returns {*} The generated or selected element.\n                 */\n                function selectOrGenerateDivByClass(className, selectParentOrOnlyChildren) {\n                    var onlyChildren = type(selectParentOrOnlyChildren) == TYPES.b;\n                    var selectParent = onlyChildren ? _hostElement : (selectParentOrOnlyChildren || _hostElement);\n\n                    return (_domExists && !selectParent[LEXICON.l])\n                        ? null\n                        : _domExists\n                            ? selectParent[onlyChildren ? 'children' : 'find'](_strDot + className.replace(/\\s/g, _strDot)).eq(0)\n                            : FRAMEWORK(generateDiv(className))\n                }\n\n                /**\n                 * Gets the value of the given property from the given object.\n                 * @param obj The object from which the property value shall be got.\n                 * @param path The property of which the value shall be got.\n                 * @returns {*} Returns the value of the searched property or undefined of the property wasn't found.\n                 */\n                function getObjectPropVal(obj, path) {\n                    var splits = path.split(_strDot);\n                    var i = 0;\n                    var val;\n                    for (; i < splits.length; i++) {\n                        if (!obj[LEXICON.hOP](splits[i]))\n                            return;\n                        val = obj[splits[i]];\n                        if (i < splits.length && type(val) == TYPES.o)\n                            obj = val;\n                    }\n                    return val;\n                }\n\n                /**\n                 * Sets the value of the given property from the given object.\n                 * @param obj The object from which the property value shall be set.\n                 * @param path The property of which the value shall be set.\n                 * @param val The value of the property which shall be set.\n                 */\n                function setObjectPropVal(obj, path, val) {\n                    var splits = path.split(_strDot);\n                    var splitsLength = splits.length;\n                    var i = 0;\n                    var extendObj = {};\n                    var extendObjRoot = extendObj;\n                    for (; i < splitsLength; i++)\n                        extendObj = extendObj[splits[i]] = i + 1 < splitsLength ? {} : val;\n                    FRAMEWORK.extend(obj, extendObjRoot, true);\n                }\n\n                /**\t\n                 * Runs a action for each selector inside the updateOnLoad option.\t\n                 * @param {Function} action The action for each updateOnLoad selector, the arguments the function takes is the index and the value (the selector).\t\n                 */\n                function eachUpdateOnLoad(action) {\n                    var updateOnLoad = _currentPreparedOptions.updateOnLoad;\n                    updateOnLoad = type(updateOnLoad) == TYPES.s ? updateOnLoad.split(_strSpace) : updateOnLoad;\n\n                    if (COMPATIBILITY.isA(updateOnLoad) && !_destroyed) {\n                        each(updateOnLoad, action);\n                    }\n                }\n\n\n                //==== Utils Cache ====//\n\n                /**\n                 * Compares two values or objects and returns true if they aren't equal.\n                 * @param current The first value or object which shall be compared.\n                 * @param cache The second value or object which shall be compared.\n                 * @param force If true the returned value is always true.\n                 * @returns {boolean} True if both values or objects aren't equal or force is true, false otherwise.\n                 */\n                function checkCache(current, cache, force) {\n                    if (force)\n                        return force;\n                    if (type(current) == TYPES.o && type(cache) == TYPES.o) {\n                        for (var prop in current) {\n                            if (prop !== 'c') {\n                                if (current[LEXICON.hOP](prop) && cache[LEXICON.hOP](prop)) {\n                                    if (checkCache(current[prop], cache[prop]))\n                                        return true;\n                                }\n                                else {\n                                    return true;\n                                }\n                            }\n                        }\n                    }\n                    else {\n                        return current !== cache;\n                    }\n                    return false;\n                }\n\n\n                //==== Shortcuts ====//\n\n                /**\n                 * jQuery extend method shortcut with a appended \"true\" as first argument.\n                 */\n                function extendDeep() {\n                    return FRAMEWORK.extend.apply(this, [true].concat([].slice.call(arguments)));\n                }\n\n                /**\n                 * jQuery addClass method shortcut.\n                 */\n                function addClass(el, classes) {\n                    return _frameworkProto.addClass.call(el, classes);\n                }\n\n                /**\n                 * jQuery removeClass method shortcut.\n                 */\n                function removeClass(el, classes) {\n                    return _frameworkProto.removeClass.call(el, classes);\n                }\n\n                /**\n                 * Adds or removes the given classes dependent on the boolean value. True for add, false for remove.\n                 */\n                function addRemoveClass(el, classes, doAdd) {\n                    return doAdd ? addClass(el, classes) : removeClass(el, classes);\n                }\n\n                /**\n                 * jQuery remove method shortcut.\n                 */\n                function remove(el) {\n                    return _frameworkProto.remove.call(el);\n                }\n\n                /**\n                 * Finds the first child element with the given selector of the given element.\n                 * @param el The root element from which the selector shall be valid.\n                 * @param selector The selector of the searched element.\n                 * @returns {*} The first element which is a child of the given element and matches the givens selector.\n                 */\n                function findFirst(el, selector) {\n                    return _frameworkProto.find.call(el, selector).eq(0);\n                }\n\n\n                //==== API ====//\n\n                /**\n                 * Puts the instance to sleep. It wont respond to any changes in the DOM and won't update. Scrollbar Interactivity is also disabled as well as the resize handle.\n                 * This behavior can be reset by calling the update method.\n                 */\n                _base.sleep = function () {\n                    _sleeping = true;\n                };\n\n                /**\n                 * Updates the plugin and DOM to the current options.\n                 * This method should only be called if a update is 100% required.\n                 * @param force True if every property shall be updated and the cache shall be ignored.\n                 * !INTERNAL USAGE! : force can be a string \"auto\", \"sync\" or \"zoom\" too\n                 * if \"auto\" then before a real update the content size and host element attributes gets checked, and if they changed only then the update method will be called.\n                 * if \"sync\" then the async update process (MutationObserver or UpdateLoop) gets synchronized and a corresponding update takes place if one was needed due to pending changes.\n                 * if \"zoom\" then a update takes place where it's assumed that content and host size changed\n                 * @returns {boolean|undefined} \n                 * If force is \"sync\" then a boolean is returned which indicates whether a update was needed due to pending changes.\n                 * If force is \"auto\" then a boolean is returned whether a update was needed due to attribute or size changes.\n                 * undefined otherwise.\n                 */\n                _base.update = function (force) {\n                    if (_destroyed)\n                        return;\n\n                    var attrsChanged;\n                    var contentSizeC;\n                    var isString = type(force) == TYPES.s;\n                    var doUpdateAuto;\n                    var mutHost;\n                    var mutContent;\n\n                    if (isString) {\n                        if (force === _strAuto) {\n                            attrsChanged = meaningfulAttrsChanged();\n                            contentSizeC = updateAutoContentSizeChanged();\n                            doUpdateAuto = attrsChanged || contentSizeC;\n                            if (doUpdateAuto) {\n                                update({\n                                    _contentSizeChanged: contentSizeC,\n                                    _changedOptions: _initialized ? undefined : _currentPreparedOptions\n                                });\n                            }\n                        }\n                        else if (force === _strSync) {\n                            if (_mutationObserversConnected) {\n                                mutHost = _mutationObserverHostCallback(_mutationObserverHost.takeRecords());\n                                mutContent = _mutationObserverContentCallback(_mutationObserverContent.takeRecords());\n                            }\n                            else {\n                                mutHost = _base.update(_strAuto);\n                            }\n                        }\n                        else if (force === 'zoom') {\n                            update({\n                                _hostSizeChanged: true,\n                                _contentSizeChanged: true\n                            });\n                        }\n                    }\n                    else {\n                        force = _sleeping || force;\n                        _sleeping = false;\n                        if (!_base.update(_strSync) || force)\n                            update({ _force: force });\n                    }\n\n                    updateElementsOnLoad();\n\n                    return doUpdateAuto || mutHost || mutContent;\n                };\n\n                /**\n                 Gets or sets the current options. The update method will be called automatically if new options were set.\n                 * @param newOptions If new options are given, then the new options will be set, if new options aren't given (undefined or a not a plain object) then the current options will be returned.\n                 * @param value If new options is a property path string, then this value will be used to set the option to which the property path string leads.\n                 * @returns {*}\n                 */\n                _base.options = function (newOptions, value) {\n                    var option = {};\n                    var changedOps;\n\n                    //return current options if newOptions are undefined or empty\n                    if (FRAMEWORK.isEmptyObject(newOptions) || !FRAMEWORK.isPlainObject(newOptions)) {\n                        if (type(newOptions) == TYPES.s) {\n                            if (arguments.length > 1) {\n                                setObjectPropVal(option, newOptions, value);\n                                changedOps = setOptions(option);\n                            }\n                            else\n                                return getObjectPropVal(_currentOptions, newOptions);\n                        }\n                        else\n                            return _currentOptions;\n                    }\n                    else {\n                        changedOps = setOptions(newOptions);\n                    }\n\n                    if (!FRAMEWORK.isEmptyObject(changedOps)) {\n                        update({ _changedOptions: changedOps });\n                    }\n                };\n\n                /**\n                 * Restore the DOM, disconnects all observers, remove all resize observers and put the instance to sleep.\n                 */\n                _base.destroy = function () {\n                    if (_destroyed)\n                        return;\n\n                    //remove this instance from auto update loop\n                    autoUpdateLoop.remove(_base);\n\n                    //disconnect all mutation observers\n                    disconnectMutationObservers();\n\n                    //remove all resize observers\n                    setupResizeObserver(_sizeObserverElement);\n                    setupResizeObserver(_sizeAutoObserverElement);\n\n                    //remove all extensions\n                    for (var extName in _extensions)\n                        _base.removeExt(extName);\n\n                    //remove all 'destroy' events\n                    while (_destroyEvents[LEXICON.l] > 0)\n                        _destroyEvents.pop()();\n\n                    //remove all events from host element\n                    setupHostMouseTouchEvents(true);\n\n                    //remove all helper / detection elements\n                    if (_contentGlueElement)\n                        remove(_contentGlueElement);\n                    if (_contentArrangeElement)\n                        remove(_contentArrangeElement);\n                    if (_sizeAutoObserverAdded)\n                        remove(_sizeAutoObserverElement);\n\n                    //remove all generated DOM\n                    setupScrollbarsDOM(true);\n                    setupScrollbarCornerDOM(true);\n                    setupStructureDOM(true);\n\n                    //remove all generated image load events\n                    for (var i = 0; i < _updateOnLoadElms[LEXICON.l]; i++)\n                        FRAMEWORK(_updateOnLoadElms[i]).off(_updateOnLoadEventName, updateOnLoadCallback);\n                    _updateOnLoadElms = undefined;\n\n                    _destroyed = true;\n                    _sleeping = true;\n\n                    //remove this instance from the instances list\n                    INSTANCES(pluginTargetElement, 0);\n                    dispatchCallback('onDestroyed');\n\n                    //remove all properties and methods\n                    //for (var property in _base)\n                    //    delete _base[property];\n                    //_base = undefined;\n                };\n\n                /**\n                 * Scrolls to a given position or element.\n                 * @param coordinates\n                 * 1. Can be \"coordinates\" which looks like:\n                 *    { x : ?, y : ? } OR          Object with x and y properties\n                 *    { left : ?, top : ? } OR     Object with left and top properties\n                 *    { l : ?, t : ? } OR          Object with l and t properties\n                 *    [ ?, ? ] OR                  Array where the first two element are the coordinates (first is x, second is y)\n                 *    ?                            A single value which stays for both axis\n                 *    A value can be a number, a string or a calculation.\n                 *\n                 *    Operators:\n                 *    [NONE]  The current scroll will be overwritten by the value.\n                 *    '+='    The value will be added to the current scroll offset\n                 *    '-='    The value will be subtracted from the current scroll offset\n                 *    '*='    The current scroll wil be multiplicated by the value.\n                 *    '/='    The current scroll wil be divided by the value.\n                 *\n                 *    Units:\n                 *    [NONE]  The value is the final scroll amount.                   final = (value * 1)\n                 *    'px'    Same as none\n                 *    '%'     The value is dependent on the current scroll value.     final = ((currentScrollValue / 100) * value)\n                 *    'vw'    The value is multiplicated by the viewport width.       final = (value * viewportWidth)\n                 *    'vh'    The value is multiplicated by the viewport height.      final = (value * viewportHeight)\n                 *\n                 *    example final values:\n                 *    200, '200px', '50%', '1vw', '1vh', '+=200', '/=1vw', '*=2px', '-=5vh', '+=33%', '+= 50% - 2px', '-= 1vw - 50%'\n                 *\n                 * 2. Can be a HTML or jQuery element:\n                 *    The final scroll offset is the offset (without margin) of the given HTML / jQuery element.\n                 *\n                 * 3. Can be a object with a HTML or jQuery element with additional settings:\n                 *    {\n                 *      el : [HTMLElement, jQuery element],             MUST be specified, else this object isn't valid.\n                 *      scroll : [string, array, object],               Default value is 'always'.\n                 *      block : [string, array, object],                Default value is 'begin'.\n                 *      margin : [number, boolean, array, object]       Default value is false.\n                 *    }\n                 *\n                 *    Possible scroll settings are:\n                 *    'always'      Scrolls always.\n                 *    'ifneeded'    Scrolls only if the element isnt fully in view.\n                 *    'never'       Scrolls never.\n                 *\n                 *    Possible block settings are:\n                 *    'begin'   Both axis shall be docked to the \"begin\" edge. - The element will be docked to the top and left edge of the viewport.\n                 *    'end'     Both axis shall be docked to the \"end\" edge. - The element will be docked to the bottom and right edge of the viewport. (If direction is RTL to the bottom and left edge.)\n                 *    'center'  Both axis shall be docked to \"center\". - The element will be centered in the viewport.\n                 *    'nearest' The element will be docked to the nearest edge(s).\n                 *\n                 *    Possible margin settings are: -- The actual margin of the element wont be affect, this option affects only the final scroll offset.\n                 *    [BOOLEAN]                                         If true the css margin of the element will be used, if false no margin will be used.\n                 *    [NUMBER]                                          The margin will be used for all edges.\n                 *\n                 * @param duration The duration of the scroll animation, OR a jQuery animation configuration object.\n                 * @param easing The animation easing.\n                 * @param complete The animation complete callback.\n                 * @returns {{\n                 *   position: {x: number, y: number},\n                 *   ratio: {x: number, y: number},\n                 *   max: {x: number, y: number},\n                 *   handleOffset: {x: number, y: number},\n                 *   handleLength: {x: number, y: number},\n                 *   handleLengthRatio: {x: number, y: number}, t\n                 *   rackLength: {x: number, y: number},\n                 *   isRTL: boolean,\n                 *   isRTLNormalized: boolean\n                 *  }}\n                 */\n                _base.scroll = function (coordinates, duration, easing, complete) {\n                    if (arguments.length === 0 || coordinates === undefined) {\n                        var infoX = _scrollHorizontalInfo;\n                        var infoY = _scrollVerticalInfo;\n                        var normalizeInvert = _normalizeRTLCache && _isRTL && _rtlScrollBehavior.i;\n                        var normalizeNegate = _normalizeRTLCache && _isRTL && _rtlScrollBehavior.n;\n                        var scrollX = infoX._currentScroll;\n                        var scrollXRatio = infoX._currentScrollRatio;\n                        var maxScrollX = infoX._maxScroll;\n                        scrollXRatio = normalizeInvert ? 1 - scrollXRatio : scrollXRatio;\n                        scrollX = normalizeInvert ? maxScrollX - scrollX : scrollX;\n                        scrollX *= normalizeNegate ? -1 : 1;\n                        maxScrollX *= normalizeNegate ? -1 : 1;\n\n                        return {\n                            position: {\n                                x: scrollX,\n                                y: infoY._currentScroll\n                            },\n                            ratio: {\n                                x: scrollXRatio,\n                                y: infoY._currentScrollRatio\n                            },\n                            max: {\n                                x: maxScrollX,\n                                y: infoY._maxScroll\n                            },\n                            handleOffset: {\n                                x: infoX._handleOffset,\n                                y: infoY._handleOffset\n                            },\n                            handleLength: {\n                                x: infoX._handleLength,\n                                y: infoY._handleLength\n                            },\n                            handleLengthRatio: {\n                                x: infoX._handleLengthRatio,\n                                y: infoY._handleLengthRatio\n                            },\n                            trackLength: {\n                                x: infoX._trackLength,\n                                y: infoY._trackLength\n                            },\n                            snappedHandleOffset: {\n                                x: infoX._snappedHandleOffset,\n                                y: infoY._snappedHandleOffset\n                            },\n                            isRTL: _isRTL,\n                            isRTLNormalized: _normalizeRTLCache\n                        };\n                    }\n\n                    _base.update(_strSync);\n\n                    var normalizeRTL = _normalizeRTLCache;\n                    var coordinatesXAxisProps = [_strX, _strLeft, 'l'];\n                    var coordinatesYAxisProps = [_strY, _strTop, 't'];\n                    var coordinatesOperators = ['+=', '-=', '*=', '/='];\n                    var durationIsObject = type(duration) == TYPES.o;\n                    var completeCallback = durationIsObject ? duration.complete : complete;\n                    var i;\n                    var finalScroll = {};\n                    var specialEasing = {};\n                    var doScrollLeft;\n                    var doScrollTop;\n                    var animationOptions;\n                    var strEnd = 'end';\n                    var strBegin = 'begin';\n                    var strCenter = 'center';\n                    var strNearest = 'nearest';\n                    var strAlways = 'always';\n                    var strNever = 'never';\n                    var strIfNeeded = 'ifneeded';\n                    var strLength = LEXICON.l;\n                    var settingsAxis;\n                    var settingsScroll;\n                    var settingsBlock;\n                    var settingsMargin;\n                    var finalElement;\n                    var elementObjSettingsAxisValues = [_strX, _strY, 'xy', 'yx'];\n                    var elementObjSettingsBlockValues = [strBegin, strEnd, strCenter, strNearest];\n                    var elementObjSettingsScrollValues = [strAlways, strNever, strIfNeeded];\n                    var coordinatesIsElementObj = coordinates[LEXICON.hOP]('el');\n                    var possibleElement = coordinatesIsElementObj ? coordinates.el : coordinates;\n                    var possibleElementIsJQuery = possibleElement instanceof FRAMEWORK || JQUERY ? possibleElement instanceof JQUERY : false;\n                    var possibleElementIsHTMLElement = possibleElementIsJQuery ? false : isHTMLElement(possibleElement);\n                    var updateScrollbarInfos = function () {\n                        if (doScrollLeft)\n                            refreshScrollbarHandleOffset(true);\n                        if (doScrollTop)\n                            refreshScrollbarHandleOffset(false);\n                    };\n                    var proxyCompleteCallback = type(completeCallback) != TYPES.f ? undefined : function () {\n                        updateScrollbarInfos();\n                        completeCallback();\n                    };\n                    function checkSettingsStringValue(currValue, allowedValues) {\n                        for (i = 0; i < allowedValues[strLength]; i++) {\n                            if (currValue === allowedValues[i])\n                                return true;\n                        }\n                        return false;\n                    }\n                    function getRawScroll(isX, coordinates) {\n                        var coordinateProps = isX ? coordinatesXAxisProps : coordinatesYAxisProps;\n                        coordinates = type(coordinates) == TYPES.s || type(coordinates) == TYPES.n ? [coordinates, coordinates] : coordinates;\n\n                        if (COMPATIBILITY.isA(coordinates))\n                            return isX ? coordinates[0] : coordinates[1];\n                        else if (type(coordinates) == TYPES.o) {\n                            //decides RTL normalization \"hack\" with .n\n                            //normalizeRTL = type(coordinates.n) == TYPES.b ? coordinates.n : normalizeRTL; \n                            for (i = 0; i < coordinateProps[strLength]; i++)\n                                if (coordinateProps[i] in coordinates)\n                                    return coordinates[coordinateProps[i]];\n                        }\n                    }\n                    function getFinalScroll(isX, rawScroll) {\n                        var isString = type(rawScroll) == TYPES.s;\n                        var operator;\n                        var amount;\n                        var scrollInfo = isX ? _scrollHorizontalInfo : _scrollVerticalInfo;\n                        var currScroll = scrollInfo._currentScroll;\n                        var maxScroll = scrollInfo._maxScroll;\n                        var mult = ' * ';\n                        var finalValue;\n                        var isRTLisX = _isRTL && isX;\n                        var normalizeShortcuts = isRTLisX && _rtlScrollBehavior.n && !normalizeRTL;\n                        var strReplace = 'replace';\n                        var evalFunc = eval;\n                        var possibleOperator;\n                        if (isString) {\n                            //check operator\n                            if (rawScroll[strLength] > 2) {\n                                possibleOperator = rawScroll.substr(0, 2);\n                                if (inArray(possibleOperator, coordinatesOperators) > -1)\n                                    operator = possibleOperator;\n                            }\n\n                            //calculate units and shortcuts\n                            rawScroll = operator ? rawScroll.substr(2) : rawScroll;\n                            rawScroll = rawScroll\n                            [strReplace](/min/g, 0) //'min' = 0%\n                            [strReplace](/</g, 0)   //'<'   = 0%\n                            [strReplace](/max/g, (normalizeShortcuts ? '-' : _strEmpty) + _strHundredPercent)    //'max' = 100%\n                            [strReplace](/>/g, (normalizeShortcuts ? '-' : _strEmpty) + _strHundredPercent)      //'>'   = 100%\n                            [strReplace](/px/g, _strEmpty)\n                            [strReplace](/%/g, mult + (maxScroll * (isRTLisX && _rtlScrollBehavior.n ? -1 : 1) / 100.0))\n                            [strReplace](/vw/g, mult + _viewportSize.w)\n                            [strReplace](/vh/g, mult + _viewportSize.h);\n                            amount = parseToZeroOrNumber(isNaN(rawScroll) ? parseToZeroOrNumber(evalFunc(rawScroll), true).toFixed() : rawScroll);\n                        }\n                        else {\n                            amount = rawScroll;\n                        }\n\n                        if (amount !== undefined && !isNaN(amount) && type(amount) == TYPES.n) {\n                            var normalizeIsRTLisX = normalizeRTL && isRTLisX;\n                            var operatorCurrScroll = currScroll * (normalizeIsRTLisX && _rtlScrollBehavior.n ? -1 : 1);\n                            var invert = normalizeIsRTLisX && _rtlScrollBehavior.i;\n                            var negate = normalizeIsRTLisX && _rtlScrollBehavior.n;\n                            operatorCurrScroll = invert ? (maxScroll - operatorCurrScroll) : operatorCurrScroll;\n                            switch (operator) {\n                                case '+=':\n                                    finalValue = operatorCurrScroll + amount;\n                                    break;\n                                case '-=':\n                                    finalValue = operatorCurrScroll - amount;\n                                    break;\n                                case '*=':\n                                    finalValue = operatorCurrScroll * amount;\n                                    break;\n                                case '/=':\n                                    finalValue = operatorCurrScroll / amount;\n                                    break;\n                                default:\n                                    finalValue = amount;\n                                    break;\n                            }\n                            finalValue = invert ? maxScroll - finalValue : finalValue;\n                            finalValue *= negate ? -1 : 1;\n                            finalValue = isRTLisX && _rtlScrollBehavior.n ? MATH.min(0, MATH.max(maxScroll, finalValue)) : MATH.max(0, MATH.min(maxScroll, finalValue));\n                        }\n                        return finalValue === currScroll ? undefined : finalValue;\n                    }\n                    function getPerAxisValue(value, valueInternalType, defaultValue, allowedValues) {\n                        var resultDefault = [defaultValue, defaultValue];\n                        var valueType = type(value);\n                        var valueArrLength;\n                        var valueArrItem;\n\n                        //value can be [ string, or array of two strings ]\n                        if (valueType == valueInternalType) {\n                            value = [value, value];\n                        }\n                        else if (valueType == TYPES.a) {\n                            valueArrLength = value[strLength];\n                            if (valueArrLength > 2 || valueArrLength < 1)\n                                value = resultDefault;\n                            else {\n                                if (valueArrLength === 1)\n                                    value[1] = defaultValue;\n                                for (i = 0; i < valueArrLength; i++) {\n                                    valueArrItem = value[i];\n                                    if (type(valueArrItem) != valueInternalType || !checkSettingsStringValue(valueArrItem, allowedValues)) {\n                                        value = resultDefault;\n                                        break;\n                                    }\n                                }\n                            }\n                        }\n                        else if (valueType == TYPES.o)\n                            value = [value[_strX] || defaultValue, value[_strY] || defaultValue];\n                        else\n                            value = resultDefault;\n                        return { x: value[0], y: value[1] };\n                    }\n                    function generateMargin(marginTopRightBottomLeftArray) {\n                        var result = [];\n                        var currValue;\n                        var currValueType;\n                        var valueDirections = [_strTop, _strRight, _strBottom, _strLeft];\n                        for (i = 0; i < marginTopRightBottomLeftArray[strLength]; i++) {\n                            if (i === valueDirections[strLength])\n                                break;\n                            currValue = marginTopRightBottomLeftArray[i];\n                            currValueType = type(currValue);\n                            if (currValueType == TYPES.b)\n                                result.push(currValue ? parseToZeroOrNumber(finalElement.css(_strMarginMinus + valueDirections[i])) : 0);\n                            else\n                                result.push(currValueType == TYPES.n ? currValue : 0);\n                        }\n                        return result;\n                    }\n\n                    if (possibleElementIsJQuery || possibleElementIsHTMLElement) {\n                        //get settings\n                        var margin = coordinatesIsElementObj ? coordinates.margin : 0;\n                        var axis = coordinatesIsElementObj ? coordinates.axis : 0;\n                        var scroll = coordinatesIsElementObj ? coordinates.scroll : 0;\n                        var block = coordinatesIsElementObj ? coordinates.block : 0;\n                        var marginDefault = [0, 0, 0, 0];\n                        var marginType = type(margin);\n                        var marginLength;\n                        finalElement = possibleElementIsJQuery ? possibleElement : FRAMEWORK(possibleElement);\n\n                        if (finalElement[strLength] > 0) {\n                            //margin can be [ boolean, number, array of 2, array of 4, object ]\n                            if (marginType == TYPES.n || marginType == TYPES.b)\n                                margin = generateMargin([margin, margin, margin, margin]);\n                            else if (marginType == TYPES.a) {\n                                marginLength = margin[strLength];\n                                if (marginLength === 2)\n                                    margin = generateMargin([margin[0], margin[1], margin[0], margin[1]]);\n                                else if (marginLength >= 4)\n                                    margin = generateMargin(margin);\n                                else\n                                    margin = marginDefault;\n                            }\n                            else if (marginType == TYPES.o)\n                                margin = generateMargin([margin[_strTop], margin[_strRight], margin[_strBottom], margin[_strLeft]]);\n                            else\n                                margin = marginDefault;\n\n                            //block = type(block) === TYPES.b ? block ? [ strNearest, strBegin ] : [ strNearest, strEnd ] : block;\n                            settingsAxis = checkSettingsStringValue(axis, elementObjSettingsAxisValues) ? axis : 'xy';\n                            settingsScroll = getPerAxisValue(scroll, TYPES.s, strAlways, elementObjSettingsScrollValues);\n                            settingsBlock = getPerAxisValue(block, TYPES.s, strBegin, elementObjSettingsBlockValues);\n                            settingsMargin = margin;\n\n                            var viewportScroll = {\n                                l: _scrollHorizontalInfo._currentScroll,\n                                t: _scrollVerticalInfo._currentScroll\n                            };\n                            // use padding element instead of viewport element because padding element has never padding, margin or position applied.\n                            var viewportOffset = _paddingElement.offset();\n\n                            //get coordinates\n                            var elementOffset = finalElement.offset();\n                            var doNotScroll = {\n                                x: settingsScroll.x == strNever || settingsAxis == _strY,\n                                y: settingsScroll.y == strNever || settingsAxis == _strX\n                            };\n                            elementOffset[_strTop] -= settingsMargin[0];\n                            elementOffset[_strLeft] -= settingsMargin[3];\n                            var elementScrollCoordinates = {\n                                x: MATH.round(elementOffset[_strLeft] - viewportOffset[_strLeft] + viewportScroll.l),\n                                y: MATH.round(elementOffset[_strTop] - viewportOffset[_strTop] + viewportScroll.t)\n                            };\n                            if (_isRTL) {\n                                if (!_rtlScrollBehavior.n && !_rtlScrollBehavior.i)\n                                    elementScrollCoordinates.x = MATH.round(viewportOffset[_strLeft] - elementOffset[_strLeft] + viewportScroll.l);\n                                if (_rtlScrollBehavior.n && normalizeRTL)\n                                    elementScrollCoordinates.x *= -1;\n                                if (_rtlScrollBehavior.i && normalizeRTL)\n                                    elementScrollCoordinates.x = MATH.round(viewportOffset[_strLeft] - elementOffset[_strLeft] + (_scrollHorizontalInfo._maxScroll - viewportScroll.l));\n                            }\n\n                            //measuring is required\n                            if (settingsBlock.x != strBegin || settingsBlock.y != strBegin || settingsScroll.x == strIfNeeded || settingsScroll.y == strIfNeeded || _isRTL) {\n                                var measuringElm = finalElement[0];\n                                var rawElementSize = _supportTransform ? measuringElm[LEXICON.bCR]() : {\n                                    width: measuringElm[LEXICON.oW],\n                                    height: measuringElm[LEXICON.oH]\n                                };\n                                var elementSize = {\n                                    w: rawElementSize[_strWidth] + settingsMargin[3] + settingsMargin[1],\n                                    h: rawElementSize[_strHeight] + settingsMargin[0] + settingsMargin[2]\n                                };\n                                var finalizeBlock = function (isX) {\n                                    var vars = getScrollbarVars(isX);\n                                    var wh = vars._w_h;\n                                    var lt = vars._left_top;\n                                    var xy = vars._x_y;\n                                    var blockIsEnd = settingsBlock[xy] == (isX ? _isRTL ? strBegin : strEnd : strEnd);\n                                    var blockIsCenter = settingsBlock[xy] == strCenter;\n                                    var blockIsNearest = settingsBlock[xy] == strNearest;\n                                    var scrollNever = settingsScroll[xy] == strNever;\n                                    var scrollIfNeeded = settingsScroll[xy] == strIfNeeded;\n                                    var vpSize = _viewportSize[wh];\n                                    var vpOffset = viewportOffset[lt];\n                                    var elSize = elementSize[wh];\n                                    var elOffset = elementOffset[lt];\n                                    var divide = blockIsCenter ? 2 : 1;\n                                    var elementCenterOffset = elOffset + (elSize / 2);\n                                    var viewportCenterOffset = vpOffset + (vpSize / 2);\n                                    var isInView =\n                                        elSize <= vpSize\n                                        && elOffset >= vpOffset\n                                        && elOffset + elSize <= vpOffset + vpSize;\n\n                                    if (scrollNever)\n                                        doNotScroll[xy] = true;\n                                    else if (!doNotScroll[xy]) {\n                                        if (blockIsNearest || scrollIfNeeded) {\n                                            doNotScroll[xy] = scrollIfNeeded ? isInView : false;\n                                            blockIsEnd = elSize < vpSize ? elementCenterOffset > viewportCenterOffset : elementCenterOffset < viewportCenterOffset;\n                                        }\n                                        elementScrollCoordinates[xy] -= blockIsEnd || blockIsCenter ? ((vpSize / divide) - (elSize / divide)) * (isX && _isRTL && normalizeRTL ? -1 : 1) : 0;\n                                    }\n                                };\n                                finalizeBlock(true);\n                                finalizeBlock(false);\n                            }\n\n                            if (doNotScroll.y)\n                                delete elementScrollCoordinates.y;\n                            if (doNotScroll.x)\n                                delete elementScrollCoordinates.x;\n\n                            coordinates = elementScrollCoordinates;\n                        }\n                    }\n\n                    finalScroll[_strScrollLeft] = getFinalScroll(true, getRawScroll(true, coordinates));\n                    finalScroll[_strScrollTop] = getFinalScroll(false, getRawScroll(false, coordinates));\n                    doScrollLeft = finalScroll[_strScrollLeft] !== undefined;\n                    doScrollTop = finalScroll[_strScrollTop] !== undefined;\n\n                    if ((doScrollLeft || doScrollTop) && (duration > 0 || durationIsObject)) {\n                        if (durationIsObject) {\n                            duration.complete = proxyCompleteCallback;\n                            _viewportElement.animate(finalScroll, duration);\n                        }\n                        else {\n                            animationOptions = {\n                                duration: duration,\n                                complete: proxyCompleteCallback\n                            };\n                            if (COMPATIBILITY.isA(easing) || FRAMEWORK.isPlainObject(easing)) {\n                                specialEasing[_strScrollLeft] = easing[0] || easing.x;\n                                specialEasing[_strScrollTop] = easing[1] || easing.y;\n                                animationOptions.specialEasing = specialEasing;\n                            }\n                            else {\n                                animationOptions.easing = easing;\n                            }\n                            _viewportElement.animate(finalScroll, animationOptions);\n                        }\n                    }\n                    else {\n                        if (doScrollLeft)\n                            _viewportElement[_strScrollLeft](finalScroll[_strScrollLeft]);\n                        if (doScrollTop)\n                            _viewportElement[_strScrollTop](finalScroll[_strScrollTop]);\n                        updateScrollbarInfos();\n                    }\n                };\n\n                /**\n                 * Stops all scroll animations.\n                 * @returns {*} The current OverlayScrollbars instance (for chaining).\n                 */\n                _base.scrollStop = function (param1, param2, param3) {\n                    _viewportElement.stop(param1, param2, param3);\n                    return _base;\n                };\n\n                /**\n                 * Returns all relevant elements.\n                 * @param elementName The name of the element which shall be returned.\n                 * @returns {{target: *, host: *, padding: *, viewport: *, content: *, scrollbarHorizontal: {scrollbar: *, track: *, handle: *}, scrollbarVertical: {scrollbar: *, track: *, handle: *}, scrollbarCorner: *} | *}\n                 */\n                _base.getElements = function (elementName) {\n                    var obj = {\n                        target: _targetElementNative,\n                        host: _hostElementNative,\n                        padding: _paddingElementNative,\n                        viewport: _viewportElementNative,\n                        content: _contentElementNative,\n                        scrollbarHorizontal: {\n                            scrollbar: _scrollbarHorizontalElement[0],\n                            track: _scrollbarHorizontalTrackElement[0],\n                            handle: _scrollbarHorizontalHandleElement[0]\n                        },\n                        scrollbarVertical: {\n                            scrollbar: _scrollbarVerticalElement[0],\n                            track: _scrollbarVerticalTrackElement[0],\n                            handle: _scrollbarVerticalHandleElement[0]\n                        },\n                        scrollbarCorner: _scrollbarCornerElement[0]\n                    };\n                    return type(elementName) == TYPES.s ? getObjectPropVal(obj, elementName) : obj;\n                };\n\n                /**\n                 * Returns a object which describes the current state of this instance.\n                 * @param stateProperty A specific property from the state object which shall be returned.\n                 * @returns {{widthAuto, heightAuto, overflowAmount, hideOverflow, hasOverflow, contentScrollSize, viewportSize, hostSize, autoUpdate} | *}\n                 */\n                _base.getState = function (stateProperty) {\n                    function prepare(obj) {\n                        if (!FRAMEWORK.isPlainObject(obj))\n                            return obj;\n                        var extended = extendDeep({}, obj);\n                        var changePropertyName = function (from, to) {\n                            if (extended[LEXICON.hOP](from)) {\n                                extended[to] = extended[from];\n                                delete extended[from];\n                            }\n                        };\n                        changePropertyName('w', _strWidth); //change w to width\n                        changePropertyName('h', _strHeight); //change h to height\n                        delete extended.c; //delete c (the 'changed' prop)\n                        return extended;\n                    };\n                    var obj = {\n                        destroyed: !!prepare(_destroyed),\n                        sleeping: !!prepare(_sleeping),\n                        autoUpdate: prepare(!_mutationObserversConnected),\n                        widthAuto: prepare(_widthAutoCache),\n                        heightAuto: prepare(_heightAutoCache),\n                        padding: prepare(_cssPaddingCache),\n                        overflowAmount: prepare(_overflowAmountCache),\n                        hideOverflow: prepare(_hideOverflowCache),\n                        hasOverflow: prepare(_hasOverflowCache),\n                        contentScrollSize: prepare(_contentScrollSizeCache),\n                        viewportSize: prepare(_viewportSize),\n                        hostSize: prepare(_hostSizeCache),\n                        documentMixed: prepare(_documentMixed)\n                    };\n                    return type(stateProperty) == TYPES.s ? getObjectPropVal(obj, stateProperty) : obj;\n                };\n\n                /**\n                 * Gets all or specific extension instance.\n                 * @param extName The name of the extension from which the instance shall be got.\n                 * @returns {{}} The instance of the extension with the given name or undefined if the instance couldn't be found.\n                 */\n                _base.ext = function (extName) {\n                    var result;\n                    var privateMethods = _extensionsPrivateMethods.split(' ');\n                    var i = 0;\n                    if (type(extName) == TYPES.s) {\n                        if (_extensions[LEXICON.hOP](extName)) {\n                            result = extendDeep({}, _extensions[extName]);\n                            for (; i < privateMethods.length; i++)\n                                delete result[privateMethods[i]];\n                        }\n                    }\n                    else {\n                        result = {};\n                        for (i in _extensions)\n                            result[i] = extendDeep({}, _base.ext(i));\n                    }\n                    return result;\n                };\n\n                /**\n                 * Adds a extension to this instance.\n                 * @param extName The name of the extension which shall be added.\n                 * @param extensionOptions The extension options which shall be used.\n                 * @returns {{}} The instance of the added extension or undefined if the extension couldn't be added properly.\n                 */\n                _base.addExt = function (extName, extensionOptions) {\n                    var registeredExtensionObj = _plugin.extension(extName);\n                    var instance;\n                    var instanceAdded;\n                    var instanceContract;\n                    var contractResult;\n                    var contractFulfilled = true;\n                    if (registeredExtensionObj) {\n                        if (!_extensions[LEXICON.hOP](extName)) {\n                            instance = registeredExtensionObj.extensionFactory.call(_base,\n                                extendDeep({}, registeredExtensionObj.defaultOptions),\n                                FRAMEWORK,\n                                COMPATIBILITY);\n\n                            if (instance) {\n                                instanceContract = instance.contract;\n                                if (type(instanceContract) == TYPES.f) {\n                                    contractResult = instanceContract(window);\n                                    contractFulfilled = type(contractResult) == TYPES.b ? contractResult : contractFulfilled;\n                                }\n                                if (contractFulfilled) {\n                                    _extensions[extName] = instance;\n                                    instanceAdded = instance.added;\n                                    if (type(instanceAdded) == TYPES.f)\n                                        instanceAdded(extensionOptions);\n\n                                    return _base.ext(extName);\n                                }\n                            }\n                        }\n                        else\n                            return _base.ext(extName);\n                    }\n                    else\n                        console.warn(\"A extension with the name \\\"\" + extName + \"\\\" isn't registered.\");\n                };\n\n                /**\n                 * Removes a extension from this instance.\n                 * @param extName The name of the extension which shall be removed.\n                 * @returns {boolean} True if the extension was removed, false otherwise e.g. if the extension wasn't added before.\n                 */\n                _base.removeExt = function (extName) {\n                    var instance = _extensions[extName];\n                    var instanceRemoved;\n                    if (instance) {\n                        delete _extensions[extName];\n\n                        instanceRemoved = instance.removed;\n                        if (type(instanceRemoved) == TYPES.f)\n                            instanceRemoved();\n\n                        return true;\n                    }\n                    return false;\n                };\n\n                /**\n                 * Constructs the plugin.\n                 * @param targetElement The element to which the plugin shall be applied.\n                 * @param options The initial options of the plugin.\n                 * @param extensions The extension(s) which shall be added right after the initialization.\n                 * @returns {boolean} True if the plugin was successfully initialized, false otherwise.\n                 */\n                function construct(targetElement, options, extensions) {\n                    _defaultOptions = globals.defaultOptions;\n                    _nativeScrollbarStyling = globals.nativeScrollbarStyling;\n                    _nativeScrollbarSize = extendDeep({}, globals.nativeScrollbarSize);\n                    _nativeScrollbarIsOverlaid = extendDeep({}, globals.nativeScrollbarIsOverlaid);\n                    _overlayScrollbarDummySize = extendDeep({}, globals.overlayScrollbarDummySize);\n                    _rtlScrollBehavior = extendDeep({}, globals.rtlScrollBehavior);\n\n                    //parse & set options but don't update\n                    setOptions(extendDeep({}, _defaultOptions, options));\n\n                    _cssCalc = globals.cssCalc;\n                    _msieVersion = globals.msie;\n                    _autoUpdateRecommended = globals.autoUpdateRecommended;\n                    _supportTransition = globals.supportTransition;\n                    _supportTransform = globals.supportTransform;\n                    _supportPassiveEvents = globals.supportPassiveEvents;\n                    _supportResizeObserver = globals.supportResizeObserver;\n                    _supportMutationObserver = globals.supportMutationObserver;\n                    _restrictedMeasuring = globals.restrictedMeasuring;\n                    _documentElement = FRAMEWORK(targetElement.ownerDocument);\n                    _documentElementNative = _documentElement[0];\n                    _windowElement = FRAMEWORK(_documentElementNative.defaultView || _documentElementNative.parentWindow);\n                    _windowElementNative = _windowElement[0];\n                    _htmlElement = findFirst(_documentElement, 'html');\n                    _bodyElement = findFirst(_htmlElement, 'body');\n                    _targetElement = FRAMEWORK(targetElement);\n                    _targetElementNative = _targetElement[0];\n                    _isTextarea = _targetElement.is('textarea');\n                    _isBody = _targetElement.is('body');\n                    _documentMixed = _documentElementNative !== document;\n\n                    /* On a div Element The if checks only whether:\n                     * - the targetElement has the class \"os-host\"\n                     * - the targetElement has a a child with the class \"os-padding\"\n                     * \n                     * If that's the case, its assumed the DOM has already the following structure:\n                     * (The \".os-host\" element is the targetElement)\n                     *\n                     *  <div class=\"os-host\">\n                     *      <div class=\"os-resize-observer-host\"></div>\n                     *      <div class=\"os-padding\">\n                     *          <div class=\"os-viewport\">\n                     *              <div class=\"os-content\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-horizontal \">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-vertical\">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar-corner\"></div>\n                     *  </div>\n                     *\n                     * =====================================================================================\n                     * \n                     * On a Textarea Element The if checks only whether:\n                     * - the targetElement has the class \"os-textarea\" \n                     * - the targetElement is inside a element with the class \"os-content\" \n                     * \n                     * If that's the case, its assumed the DOM has already the following structure:\n                     * (The \".os-textarea\" (textarea) element is the targetElement)\n                     *\n                     *  <div class=\"os-host-textarea\">\n                     *      <div class=\"os-resize-observer-host\"></div>\n                     *      <div class=\"os-padding os-text-inherit\">\n                     *          <div class=\"os-viewport os-text-inherit\">\n                     *              <div class=\"os-content os-text-inherit\">\n                     *                  <div class=\"os-textarea-cover\"></div>\n                     *                  <textarea class=\"os-textarea os-text-inherit\"></textarea>\n                     *              </div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-horizontal \">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-vertical\">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar-corner\"></div>\n                     *  </div>\n                     */\n                    _domExists = _isTextarea\n                        ? _targetElement.hasClass(_classNameTextareaElement) && _targetElement.parent().hasClass(_classNameContentElement)\n                        : _targetElement.hasClass(_classNameHostElement) && _targetElement.children(_strDot + _classNamePaddingElement)[LEXICON.l];\n\n                    var initBodyScroll;\n                    var bodyMouseTouchDownListener;\n\n                    //check if the plugin hasn't to be initialized\n                    if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y && !_currentPreparedOptions.nativeScrollbarsOverlaid.initialize) {\n                        dispatchCallback('onInitializationWithdrawn');\n                        if (_domExists) {\n                            setupStructureDOM(true);\n                            setupScrollbarsDOM(true);\n                            setupScrollbarCornerDOM(true);\n                        }\n\n                        _destroyed = true;\n                        _sleeping = true;\n\n                        return _base;\n                    }\n\n                    if (_isBody) {\n                        initBodyScroll = {};\n                        initBodyScroll.l = MATH.max(_targetElement[_strScrollLeft](), _htmlElement[_strScrollLeft](), _windowElement[_strScrollLeft]());\n                        initBodyScroll.t = MATH.max(_targetElement[_strScrollTop](), _htmlElement[_strScrollTop](), _windowElement[_strScrollTop]());\n\n                        bodyMouseTouchDownListener = function () {\n                            _viewportElement.removeAttr(LEXICON.ti);\n                            setupResponsiveEventListener(_viewportElement, _strMouseTouchDownEvent, bodyMouseTouchDownListener, true, true);\n                        }\n                    }\n\n                    //build OverlayScrollbars DOM\n                    setupStructureDOM();\n                    setupScrollbarsDOM();\n                    setupScrollbarCornerDOM();\n\n                    //create OverlayScrollbars events\n                    setupStructureEvents();\n                    setupScrollbarEvents(true);\n                    setupScrollbarEvents(false);\n                    setupScrollbarCornerEvents();\n\n                    //create mutation observers\n                    createMutationObservers();\n\n                    //build resize observer for the host element\n                    setupResizeObserver(_sizeObserverElement, hostOnResized);\n\n                    if (_isBody) {\n                        //apply the body scroll to handle it right in the update method\n                        _viewportElement[_strScrollLeft](initBodyScroll.l)[_strScrollTop](initBodyScroll.t);\n\n                        //set the focus on the viewport element so you dont have to click on the page to use keyboard keys (up / down / space) for scrolling\n                        if (document.activeElement == targetElement && _viewportElementNative.focus) {\n                            //set a tabindex to make the viewportElement focusable\n                            _viewportElement.attr(LEXICON.ti, '-1');\n                            _viewportElementNative.focus();\n\n                            /* the tabindex has to be removed due to;\n                             * If you set the tabindex attribute on an <div>, then its child content cannot be scrolled with the arrow keys unless you set tabindex on the content, too\n                             * https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex\n                             */\n                            setupResponsiveEventListener(_viewportElement, _strMouseTouchDownEvent, bodyMouseTouchDownListener, false, true);\n                        }\n                    }\n\n                    //update for the first time & initialize cache\n                    _base.update(_strAuto);\n\n                    //the plugin is initialized now!\n                    _initialized = true;\n                    dispatchCallback('onInitialized');\n\n                    //call all callbacks which would fire before the initialized was complete\n                    each(_callbacksInitQeueue, function (index, value) { dispatchCallback(value.n, value.a); });\n                    _callbacksInitQeueue = [];\n\n                    //add extensions\n                    if (type(extensions) == TYPES.s)\n                        extensions = [extensions];\n                    if (COMPATIBILITY.isA(extensions))\n                        each(extensions, function (index, value) { _base.addExt(value); });\n                    else if (FRAMEWORK.isPlainObject(extensions))\n                        each(extensions, function (key, value) { _base.addExt(key, value); });\n\n                    //add the transition class for transitions AFTER the first update & AFTER the applied extensions (for preventing unwanted transitions)\n                    setTimeout(function () {\n                        if (_supportTransition && !_destroyed)\n                            addClass(_hostElement, _classNameHostTransition);\n                    }, 333);\n\n                    return _base;\n                }\n\n                if (_plugin.valid(construct(pluginTargetElement, options, extensions))) {\n                    INSTANCES(pluginTargetElement, _base);\n                }\n\n                return _base;\n            }\n\n            /**\n             * Initializes a new OverlayScrollbarsInstance object or changes options if already initialized or returns the current instance.\n             * @param pluginTargetElements The elements to which the Plugin shall be initialized.\n             * @param options The custom options with which the plugin shall be initialized.\n             * @param extensions The extension(s) which shall be added right after initialization.\n             * @returns {*}\n             */\n            _plugin = window[PLUGINNAME] = function (pluginTargetElements, options, extensions) {\n                if (arguments[LEXICON.l] === 0)\n                    return this;\n\n                var arr = [];\n                var optsIsPlainObj = FRAMEWORK.isPlainObject(options);\n                var inst;\n                var result;\n\n                //pluginTargetElements is null or undefined\n                if (!pluginTargetElements)\n                    return optsIsPlainObj || !options ? result : arr;\n\n                /*\n                   pluginTargetElements will be converted to:\n                   1. A jQueryElement Array\n                   2. A HTMLElement Array\n                   3. A Array with a single HTML Element\n                   so pluginTargetElements is always a array.\n                */\n                pluginTargetElements = pluginTargetElements[LEXICON.l] != undefined ? pluginTargetElements : [pluginTargetElements[0] || pluginTargetElements];\n                initOverlayScrollbarsStatics();\n\n                if (pluginTargetElements[LEXICON.l] > 0) {\n                    if (optsIsPlainObj) {\n                        FRAMEWORK.each(pluginTargetElements, function (i, v) {\n                            inst = v;\n                            if (inst !== undefined)\n                                arr.push(OverlayScrollbarsInstance(inst, options, extensions, _pluginsGlobals, _pluginsAutoUpdateLoop));\n                        });\n                    }\n                    else {\n                        FRAMEWORK.each(pluginTargetElements, function (i, v) {\n                            inst = INSTANCES(v);\n                            if ((options === '!' && _plugin.valid(inst)) || (COMPATIBILITY.type(options) == TYPES.f && options(v, inst)))\n                                arr.push(inst);\n                            else if (options === undefined)\n                                arr.push(inst);\n                        });\n                    }\n                    result = arr[LEXICON.l] === 1 ? arr[0] : arr;\n                }\n                return result;\n            };\n\n            /**\n             * Returns a object which contains global information about the plugin and each instance of it.\n             * The returned object is just a copy, that means that changes to the returned object won't have any effect to the original object.\n             */\n            _plugin.globals = function () {\n                initOverlayScrollbarsStatics();\n                var globals = FRAMEWORK.extend(true, {}, _pluginsGlobals);\n                delete globals['msie'];\n                return globals;\n            };\n\n            /**\n             * Gets or Sets the default options for each new plugin initialization.\n             * @param newDefaultOptions The object with which the default options shall be extended.\n             */\n            _plugin.defaultOptions = function (newDefaultOptions) {\n                initOverlayScrollbarsStatics();\n                var currDefaultOptions = _pluginsGlobals.defaultOptions;\n                if (newDefaultOptions === undefined)\n                    return FRAMEWORK.extend(true, {}, currDefaultOptions);\n\n                //set the new default options\n                _pluginsGlobals.defaultOptions = FRAMEWORK.extend(true, {}, currDefaultOptions, _pluginsOptions._validate(newDefaultOptions, _pluginsOptions._template, true, currDefaultOptions)._default);\n            };\n\n            /**\n             * Checks whether the passed instance is a non-destroyed OverlayScrollbars instance.\n             * @param osInstance The potential OverlayScrollbars instance which shall be checked.\n             * @returns {boolean} True if the passed value is a non-destroyed OverlayScrollbars instance, false otherwise.\n             */\n            _plugin.valid = function (osInstance) {\n                return osInstance instanceof _plugin && !osInstance.getState().destroyed;\n            };\n\n            /**\n             * Registers, Unregisters or returns a extension.\n             * Register: Pass the name and the extension. (defaultOptions is optional)\n             * Unregister: Pass the name and anything except a function as extension parameter.\n             * Get extension: Pass the name of the extension which shall be got.\n             * Get all extensions: Pass no arguments.\n             * @param extensionName The name of the extension which shall be registered, unregistered or returned.\n             * @param extension A function which generates the instance of the extension or anything other to remove a already registered extension.\n             * @param defaultOptions The default options which shall be used for the registered extension.\n             */\n            _plugin.extension = function (extensionName, extension, defaultOptions) {\n                var extNameTypeString = COMPATIBILITY.type(extensionName) == TYPES.s;\n                var argLen = arguments[LEXICON.l];\n                var i = 0;\n                if (argLen < 1 || !extNameTypeString) {\n                    //return a copy of all extension objects\n                    return FRAMEWORK.extend(true, { length: _pluginsExtensions[LEXICON.l] }, _pluginsExtensions);\n                }\n                else if (extNameTypeString) {\n                    if (COMPATIBILITY.type(extension) == TYPES.f) {\n                        //register extension\n                        _pluginsExtensions.push({\n                            name: extensionName,\n                            extensionFactory: extension,\n                            defaultOptions: defaultOptions\n                        });\n                    }\n                    else {\n                        for (; i < _pluginsExtensions[LEXICON.l]; i++) {\n                            if (_pluginsExtensions[i].name === extensionName) {\n                                if (argLen > 1)\n                                    _pluginsExtensions.splice(i, 1); //remove extension\n                                else\n                                    return FRAMEWORK.extend(true, {}, _pluginsExtensions[i]); //return extension with the given name\n                            }\n                        }\n                    }\n                }\n            };\n\n            return _plugin;\n        })();\n\n        if (JQUERY && JQUERY.fn) {\n            /**\n             * The jQuery initialization interface.\n             * @param options The initial options for the construction of the plugin. To initialize the plugin, this option has to be a object! If it isn't a object, the instance(s) are returned and the plugin wont be initialized.\n             * @param extensions The extension(s) which shall be added right after initialization.\n             * @returns {*} After initialization it returns the jQuery element array, else it returns the instance(s) of the elements which are selected.\n             */\n            JQUERY.fn.overlayScrollbars = function (options, extensions) {\n                var _elements = this;\n                if (JQUERY.isPlainObject(options)) {\n                    JQUERY.each(_elements, function () { PLUGIN(this, options, extensions); });\n                    return _elements;\n                }\n                else\n                    return PLUGIN(_elements, options);\n            };\n        }\n        return PLUGIN;\n    }\n));"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/plugins/overlayScrollbars/js/jquery.overlayScrollbars.js",
    "content": "/*!\n * OverlayScrollbars\n * https://github.com/KingSora/OverlayScrollbars\n *\n * Version: 1.13.0\n *\n * Copyright KingSora | Rene Haas.\n * https://github.com/KingSora\n *\n * Released under the MIT license.\n * Date: 02.08.2020\n */\n\n(function (global, factory) {\n    if (typeof define === 'function' && define.amd)\n        define(['jquery'], function (framework) { return factory(global, global.document, undefined, framework); });\n    else if (typeof module === 'object' && typeof module.exports === 'object')\n        module.exports = factory(global, global.document, undefined, require('jquery'));\n    else\n        factory(global, global.document, undefined, global.jQuery);\n}(typeof window !== 'undefined' ? window : this,\n    function (window, document, undefined, framework) {\n        'use strict';\n        var PLUGINNAME = 'OverlayScrollbars';\n        var TYPES = {\n            o: 'object',\n            f: 'function',\n            a: 'array',\n            s: 'string',\n            b: 'boolean',\n            n: 'number',\n            u: 'undefined',\n            z: 'null'\n            //d : 'date',\n            //e : 'error',\n            //r : 'regexp',\n            //y : 'symbol'\n        };\n        var LEXICON = {\n            c: 'class',\n            s: 'style',\n            i: 'id',\n            l: 'length',\n            p: 'prototype',\n            ti: 'tabindex',\n            oH: 'offsetHeight',\n            cH: 'clientHeight',\n            sH: 'scrollHeight',\n            oW: 'offsetWidth',\n            cW: 'clientWidth',\n            sW: 'scrollWidth',\n            hOP: 'hasOwnProperty',\n            bCR: 'getBoundingClientRect'\n        };\n        var VENDORS = (function () {\n            //https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix\n            var jsCache = {};\n            var cssCache = {};\n            var cssPrefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];\n            var jsPrefixes = ['WebKit', 'Moz', 'O', 'MS'];\n            function firstLetterToUpper(str) {\n                return str.charAt(0).toUpperCase() + str.slice(1);\n            }\n\n            return {\n                _cssPrefixes: cssPrefixes,\n                _jsPrefixes: jsPrefixes,\n                _cssProperty: function (name) {\n                    var result = cssCache[name];\n\n                    if (cssCache[LEXICON.hOP](name))\n                        return result;\n\n                    var uppercasedName = firstLetterToUpper(name);\n                    var elmStyle = document.createElement('div')[LEXICON.s];\n                    var resultPossibilities;\n                    var i = 0;\n                    var v;\n                    var currVendorWithoutDashes;\n\n                    for (; i < cssPrefixes.length; i++) {\n                        currVendorWithoutDashes = cssPrefixes[i].replace(/-/g, '');\n                        resultPossibilities = [\n                            name, //transition\n                            cssPrefixes[i] + name, //-webkit-transition\n                            currVendorWithoutDashes + uppercasedName, //webkitTransition\n                            firstLetterToUpper(currVendorWithoutDashes) + uppercasedName //WebkitTransition\n                        ];\n                        for (v = 0; v < resultPossibilities[LEXICON.l]; v++) {\n                            if (elmStyle[resultPossibilities[v]] !== undefined) {\n                                result = resultPossibilities[v];\n                                break;\n                            }\n                        }\n                    }\n\n                    cssCache[name] = result;\n                    return result;\n                },\n                _cssPropertyValue: function (property, values, suffix) {\n                    var name = property + ' ' + values;\n                    var result = cssCache[name];\n\n                    if (cssCache[LEXICON.hOP](name))\n                        return result;\n\n                    var dummyStyle = document.createElement('div')[LEXICON.s];\n                    var possbleValues = values.split(' ');\n                    var preparedSuffix = suffix || '';\n                    var i = 0;\n                    var v = -1;\n                    var prop;\n\n                    for (; i < possbleValues[LEXICON.l]; i++) {\n                        for (; v < VENDORS._cssPrefixes[LEXICON.l]; v++) {\n                            prop = v < 0 ? possbleValues[i] : VENDORS._cssPrefixes[v] + possbleValues[i];\n                            dummyStyle.cssText = property + ':' + prop + preparedSuffix;\n                            if (dummyStyle[LEXICON.l]) {\n                                result = prop;\n                                break;\n                            }\n                        }\n                    }\n\n                    cssCache[name] = result;\n                    return result;\n                },\n                _jsAPI: function (name, isInterface, fallback) {\n                    var i = 0;\n                    var result = jsCache[name];\n\n                    if (!jsCache[LEXICON.hOP](name)) {\n                        result = window[name];\n                        for (; i < jsPrefixes[LEXICON.l]; i++)\n                            result = result || window[(isInterface ? jsPrefixes[i] : jsPrefixes[i].toLowerCase()) + firstLetterToUpper(name)];\n                        jsCache[name] = result;\n                    }\n                    return result || fallback;\n                }\n            }\n        })();\n        var COMPATIBILITY = (function () {\n            function windowSize(x) {\n                return x ? window.innerWidth || document.documentElement[LEXICON.cW] || document.body[LEXICON.cW] : window.innerHeight || document.documentElement[LEXICON.cH] || document.body[LEXICON.cH];\n            }\n            function bind(func, thisObj) {\n                if (typeof func != TYPES.f) {\n                    throw \"Can't bind function!\";\n                    // closest thing possible to the ECMAScript 5\n                    // internal IsCallable function\n                    //throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');\n                }\n                var proto = LEXICON.p;\n                var aArgs = Array[proto].slice.call(arguments, 2);\n                var fNOP = function () { };\n                var fBound = function () { return func.apply(this instanceof fNOP ? this : thisObj, aArgs.concat(Array[proto].slice.call(arguments))); };\n\n                if (func[proto])\n                    fNOP[proto] = func[proto]; // Function.prototype doesn't have a prototype property\n                fBound[proto] = new fNOP();\n\n                return fBound;\n            }\n\n            return {\n                /**\n                 * Gets the current window width.\n                 * @returns {Number|number} The current window width in pixel.\n                 */\n                wW: bind(windowSize, 0, true),\n\n                /**\n                 * Gets the current window height.\n                 * @returns {Number|number} The current window height in pixel.\n                 */\n                wH: bind(windowSize, 0),\n\n                /**\n                 * Gets the MutationObserver Object or undefined if not supported.\n                 * @returns {MutationObserver|*|undefined} The MutationsObserver Object or undefined.\n                 */\n                mO: bind(VENDORS._jsAPI, 0, 'MutationObserver', true),\n\n                /**\n                 * Gets the ResizeObserver Object or undefined if not supported.\n                 * @returns {MutationObserver|*|undefined} The ResizeObserver Object or undefined.\n                 */\n                rO: bind(VENDORS._jsAPI, 0, 'ResizeObserver', true),\n\n                /**\n                 * Gets the RequestAnimationFrame method or it's corresponding polyfill.\n                 * @returns {*|Function} The RequestAnimationFrame method or it's corresponding polyfill.\n                 */\n                rAF: bind(VENDORS._jsAPI, 0, 'requestAnimationFrame', false, function (func) { return window.setTimeout(func, 1000 / 60); }),\n\n                /**\n                 * Gets the CancelAnimationFrame method or it's corresponding polyfill.\n                 * @returns {*|Function} The CancelAnimationFrame method or it's corresponding polyfill.\n                 */\n                cAF: bind(VENDORS._jsAPI, 0, 'cancelAnimationFrame', false, function (id) { return window.clearTimeout(id); }),\n\n                /**\n                 * Gets the current time.\n                 * @returns {number} The current time.\n                 */\n                now: function () {\n                    return Date.now && Date.now() || new Date().getTime();\n                },\n\n                /**\n                 * Stops the propagation of the given event.\n                 * @param event The event of which the propagation shall be stoped.\n                 */\n                stpP: function (event) {\n                    if (event.stopPropagation)\n                        event.stopPropagation();\n                    else\n                        event.cancelBubble = true;\n                },\n\n                /**\n                 * Prevents the default action of the given event.\n                 * @param event The event of which the default action shall be prevented.\n                 */\n                prvD: function (event) {\n                    if (event.preventDefault && event.cancelable)\n                        event.preventDefault();\n                    else\n                        event.returnValue = false;\n                },\n\n                /**\n                 * Gets the pageX and pageY values of the given mouse event.\n                 * @param event The mouse event of which the pageX and pageX shall be got.\n                 * @returns {{x: number, y: number}} x = pageX value, y = pageY value.\n                 */\n                page: function (event) {\n                    event = event.originalEvent || event;\n\n                    var strPage = 'page';\n                    var strClient = 'client';\n                    var strX = 'X';\n                    var strY = 'Y';\n                    var target = event.target || event.srcElement || document;\n                    var eventDoc = target.ownerDocument || document;\n                    var doc = eventDoc.documentElement;\n                    var body = eventDoc.body;\n\n                    //if touch event return return pageX/Y of it\n                    if (event.touches !== undefined) {\n                        var touch = event.touches[0];\n                        return {\n                            x: touch[strPage + strX],\n                            y: touch[strPage + strY]\n                        }\n                    }\n\n                    // Calculate pageX/Y if not native supported\n                    if (!event[strPage + strX] && event[strClient + strX] && event[strClient + strX] != null) {\n\n                        return {\n                            x: event[strClient + strX] +\n                                (doc && doc.scrollLeft || body && body.scrollLeft || 0) -\n                                (doc && doc.clientLeft || body && body.clientLeft || 0),\n                            y: event[strClient + strY] +\n                                (doc && doc.scrollTop || body && body.scrollTop || 0) -\n                                (doc && doc.clientTop || body && body.clientTop || 0)\n                        }\n                    }\n                    return {\n                        x: event[strPage + strX],\n                        y: event[strPage + strY]\n                    };\n                },\n\n                /**\n                 * Gets the clicked mouse button of the given mouse event.\n                 * @param event The mouse event of which the clicked button shal be got.\n                 * @returns {number} The number of the clicked mouse button. (0 : none | 1 : leftButton | 2 : middleButton | 3 : rightButton)\n                 */\n                mBtn: function (event) {\n                    var button = event.button;\n                    if (!event.which && button !== undefined)\n                        return (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n                    else\n                        return event.which;\n                },\n\n                /**\n                 * Checks whether a item is in the given array and returns its index.\n                 * @param item The item of which the position in the array shall be determined.\n                 * @param arr The array.\n                 * @returns {number} The zero based index of the item or -1 if the item isn't in the array.\n                 */\n                inA: function (item, arr) {\n                    for (var i = 0; i < arr[LEXICON.l]; i++)\n                        //Sometiems in IE a \"SCRIPT70\" Permission denied error occurs if HTML elements in a iFrame are compared\n                        try {\n                            if (arr[i] === item)\n                                return i;\n                        }\n                        catch (e) { }\n                    return -1;\n                },\n\n                /**\n                 * Returns true if the given value is a array.\n                 * @param arr The potential array.\n                 * @returns {boolean} True if the given value is a array, false otherwise.\n                 */\n                isA: function (arr) {\n                    var def = Array.isArray;\n                    return def ? def(arr) : this.type(arr) == TYPES.a;\n                },\n\n                /**\n                 * Determine the internal JavaScript [[Class]] of the given object.\n                 * @param obj The object of which the type shall be determined.\n                 * @returns {string} The type of the given object.\n                 */\n                type: function (obj) {\n                    if (obj === undefined)\n                        return obj + '';\n                    if (obj === null)\n                        return obj + '';\n                    return Object[LEXICON.p].toString.call(obj).replace(/^\\[object (.+)\\]$/, '$1').toLowerCase();\n                },\n\n\n                bind: bind\n\n                /**\n                 * Gets the vendor-prefixed CSS property by the given name.\n                 * For example the given name is \"transform\" and you're using a old Firefox browser then the returned value would be \"-moz-transform\".\n                 * If the browser doesn't need a vendor-prefix, then the returned string is the given name.\n                 * If the browser doesn't support the given property name at all (not even with a vendor-prefix) the returned value is null.\n                 * @param propName The unprefixed CSS property name.\n                 * @returns {string|null} The vendor-prefixed CSS property or null if the browser doesn't support the given CSS property.\n\n                cssProp: function(propName) {\n                    return VENDORS._cssProperty(propName);\n                }\n                */\n            }\n        })();\n\n        var MATH = Math;\n        var JQUERY = framework;\n        var EASING = framework.easing;\n        var FRAMEWORK = framework;\n        var INSTANCES = (function () {\n            var _targets = [];\n            var _instancePropertyString = '__overlayScrollbars__';\n\n            /**\n             * Register, unregister or get a certain (or all) instances.\n             * Register: Pass the target and the instance.\n             * Unregister: Pass the target and null.\n             * Get Instance: Pass the target from which the instance shall be got.\n             * Get Targets: Pass no arguments.\n             * @param target The target to which the instance shall be registered / from which the instance shall be unregistered / the instance shall be got\n             * @param instance The instance.\n             * @returns {*|void} Returns the instance from the given target.\n             */\n            return function (target, instance) {\n                var argLen = arguments[LEXICON.l];\n                if (argLen < 1) {\n                    //return all targets\n                    return _targets;\n                }\n                else {\n                    if (instance) {\n                        //register instance\n                        target[_instancePropertyString] = instance;\n                        _targets.push(target);\n                    }\n                    else {\n                        var index = COMPATIBILITY.inA(target, _targets);\n                        if (index > -1) {\n                            if (argLen > 1) {\n                                //unregister instance\n                                delete target[_instancePropertyString];\n                                _targets.splice(index, 1);\n                            }\n                            else {\n                                //get instance from target\n                                return _targets[index][_instancePropertyString];\n                            }\n                        }\n                    }\n                }\n            }\n        })();\n        var PLUGIN = (function () {\n            var _plugin;\n            var _pluginsGlobals;\n            var _pluginsAutoUpdateLoop;\n            var _pluginsExtensions = [];\n            var _pluginsOptions = (function () {\n                var type = COMPATIBILITY.type;\n                var possibleTemplateTypes = [\n                    TYPES.b, //boolean\n                    TYPES.n, //number\n                    TYPES.s, //string\n                    TYPES.a, //array\n                    TYPES.o, //object\n                    TYPES.f, //function\n                    TYPES.z  //null\n                ];\n                var restrictedStringsSplit = ' ';\n                var restrictedStringsPossibilitiesSplit = ':';\n                var classNameAllowedValues = [TYPES.z, TYPES.s];\n                var numberAllowedValues = TYPES.n;\n                var booleanNullAllowedValues = [TYPES.z, TYPES.b];\n                var booleanTrueTemplate = [true, TYPES.b];\n                var booleanFalseTemplate = [false, TYPES.b];\n                var callbackTemplate = [null, [TYPES.z, TYPES.f]];\n                var updateOnLoadTemplate = [['img'], [TYPES.s, TYPES.a, TYPES.z]];\n                var inheritedAttrsTemplate = [['style', 'class'], [TYPES.s, TYPES.a, TYPES.z]];\n                var resizeAllowedValues = 'n:none b:both h:horizontal v:vertical';\n                var overflowBehaviorAllowedValues = 'v-h:visible-hidden v-s:visible-scroll s:scroll h:hidden';\n                var scrollbarsVisibilityAllowedValues = 'v:visible h:hidden a:auto';\n                var scrollbarsAutoHideAllowedValues = 'n:never s:scroll l:leave m:move';\n                var optionsDefaultsAndTemplate = {\n                    className: ['os-theme-dark', classNameAllowedValues],                //null || string\n                    resize: ['none', resizeAllowedValues],                               //none || both  || horizontal || vertical || n || b || h || v\n                    sizeAutoCapable: booleanTrueTemplate,                                //true || false\n                    clipAlways: booleanTrueTemplate,                                     //true || false\n                    normalizeRTL: booleanTrueTemplate,                                   //true || false\n                    paddingAbsolute: booleanFalseTemplate,                               //true || false\n                    autoUpdate: [null, booleanNullAllowedValues],                        //true || false || null\n                    autoUpdateInterval: [33, numberAllowedValues],                       //number\n                    updateOnLoad: updateOnLoadTemplate,                                  //string || array || null\n                    nativeScrollbarsOverlaid: {\n                        showNativeScrollbars: booleanFalseTemplate,                      //true || false\n                        initialize: booleanTrueTemplate                                  //true || false\n                    },\n                    overflowBehavior: {\n                        x: ['scroll', overflowBehaviorAllowedValues],                    //visible-hidden  || visible-scroll || hidden || scroll || v-h || v-s || h || s\n                        y: ['scroll', overflowBehaviorAllowedValues]                     //visible-hidden  || visible-scroll || hidden || scroll || v-h || v-s || h || s\n                    },\n                    scrollbars: {\n                        visibility: ['auto', scrollbarsVisibilityAllowedValues],         //visible || hidden || auto || v || h || a\n                        autoHide: ['never', scrollbarsAutoHideAllowedValues],            //never || scroll || leave || move || n || s || l || m\n                        autoHideDelay: [800, numberAllowedValues],                       //number\n                        dragScrolling: booleanTrueTemplate,                              //true || false\n                        clickScrolling: booleanFalseTemplate,                            //true || false\n                        touchSupport: booleanTrueTemplate,                               //true || false\n                        snapHandle: booleanFalseTemplate                                 //true || false\n                    },\n                    textarea: {\n                        dynWidth: booleanFalseTemplate,                                  //true || false\n                        dynHeight: booleanFalseTemplate,                                 //true || false\n                        inheritedAttrs: inheritedAttrsTemplate                           //string || array || null\n                    },\n                    callbacks: {\n                        onInitialized: callbackTemplate,                                 //null || function\n                        onInitializationWithdrawn: callbackTemplate,                     //null || function\n                        onDestroyed: callbackTemplate,                                   //null || function\n                        onScrollStart: callbackTemplate,                                 //null || function\n                        onScroll: callbackTemplate,                                      //null || function\n                        onScrollStop: callbackTemplate,                                  //null || function\n                        onOverflowChanged: callbackTemplate,                             //null || function\n                        onOverflowAmountChanged: callbackTemplate,                       //null || function\n                        onDirectionChanged: callbackTemplate,                            //null || function\n                        onContentSizeChanged: callbackTemplate,                          //null || function\n                        onHostSizeChanged: callbackTemplate,                             //null || function\n                        onUpdated: callbackTemplate                                      //null || function\n                    }\n                };\n                var convert = function (template) {\n                    var recursive = function (obj) {\n                        var key;\n                        var val;\n                        var valType;\n                        for (key in obj) {\n                            if (!obj[LEXICON.hOP](key))\n                                continue;\n                            val = obj[key];\n                            valType = type(val);\n                            if (valType == TYPES.a)\n                                obj[key] = val[template ? 1 : 0];\n                            else if (valType == TYPES.o)\n                                obj[key] = recursive(val);\n                        }\n                        return obj;\n                    };\n                    return recursive(FRAMEWORK.extend(true, {}, optionsDefaultsAndTemplate));\n                };\n\n                return {\n                    _defaults: convert(),\n\n                    _template: convert(true),\n\n                    /**\n                     * Validates the passed object by the passed template.\n                     * @param obj The object which shall be validated.\n                     * @param template The template which defines the allowed values and types.\n                     * @param writeErrors True if errors shall be logged to the console.\n                     * @param diffObj If a object is passed then only valid differences to this object will be returned.\n                     * @returns {{}} A object which contains two objects called \"default\" and \"prepared\" which contains only the valid properties of the passed original object and discards not different values compared to the passed diffObj.\n                     */\n                    _validate: function (obj, template, writeErrors, diffObj) {\n                        var validatedOptions = {};\n                        var validatedOptionsPrepared = {};\n                        var objectCopy = FRAMEWORK.extend(true, {}, obj);\n                        var inArray = FRAMEWORK.inArray;\n                        var isEmptyObj = FRAMEWORK.isEmptyObject;\n                        var checkObjectProps = function (data, template, diffData, validatedOptions, validatedOptionsPrepared, prevPropName) {\n                            for (var prop in template) {\n                                if (template[LEXICON.hOP](prop) && data[LEXICON.hOP](prop)) {\n                                    var isValid = false;\n                                    var isDiff = false;\n                                    var templateValue = template[prop];\n                                    var templateValueType = type(templateValue);\n                                    var templateIsComplex = templateValueType == TYPES.o;\n                                    var templateTypes = !COMPATIBILITY.isA(templateValue) ? [templateValue] : templateValue;\n                                    var dataDiffValue = diffData[prop];\n                                    var dataValue = data[prop];\n                                    var dataValueType = type(dataValue);\n                                    var propPrefix = prevPropName ? prevPropName + '.' : '';\n                                    var error = \"The option \\\"\" + propPrefix + prop + \"\\\" wasn't set, because\";\n                                    var errorPossibleTypes = [];\n                                    var errorRestrictedStrings = [];\n                                    var restrictedStringValuesSplit;\n                                    var restrictedStringValuesPossibilitiesSplit;\n                                    var isRestrictedValue;\n                                    var mainPossibility;\n                                    var currType;\n                                    var i;\n                                    var v;\n                                    var j;\n\n                                    dataDiffValue = dataDiffValue === undefined ? {} : dataDiffValue;\n\n                                    //if the template has a object as value, it means that the options are complex (verschachtelt)\n                                    if (templateIsComplex && dataValueType == TYPES.o) {\n                                        validatedOptions[prop] = {};\n                                        validatedOptionsPrepared[prop] = {};\n                                        checkObjectProps(dataValue, templateValue, dataDiffValue, validatedOptions[prop], validatedOptionsPrepared[prop], propPrefix + prop);\n                                        FRAMEWORK.each([data, validatedOptions, validatedOptionsPrepared], function (index, value) {\n                                            if (isEmptyObj(value[prop])) {\n                                                delete value[prop];\n                                            }\n                                        });\n                                    }\n                                    else if (!templateIsComplex) {\n                                        for (i = 0; i < templateTypes[LEXICON.l]; i++) {\n                                            currType = templateTypes[i];\n                                            templateValueType = type(currType);\n                                            //if currtype is string and starts with restrictedStringPrefix and end with restrictedStringSuffix\n                                            isRestrictedValue = templateValueType == TYPES.s && inArray(currType, possibleTemplateTypes) === -1;\n                                            if (isRestrictedValue) {\n                                                errorPossibleTypes.push(TYPES.s);\n\n                                                //split it into a array which contains all possible values for example: [\"y:yes\", \"n:no\", \"m:maybe\"]\n                                                restrictedStringValuesSplit = currType.split(restrictedStringsSplit);\n                                                errorRestrictedStrings = errorRestrictedStrings.concat(restrictedStringValuesSplit);\n                                                for (v = 0; v < restrictedStringValuesSplit[LEXICON.l]; v++) {\n                                                    //split the possible values into their possibiliteis for example: [\"y\", \"yes\"] -> the first is always the mainPossibility\n                                                    restrictedStringValuesPossibilitiesSplit = restrictedStringValuesSplit[v].split(restrictedStringsPossibilitiesSplit);\n                                                    mainPossibility = restrictedStringValuesPossibilitiesSplit[0];\n                                                    for (j = 0; j < restrictedStringValuesPossibilitiesSplit[LEXICON.l]; j++) {\n                                                        //if any possibility matches with the dataValue, its valid\n                                                        if (dataValue === restrictedStringValuesPossibilitiesSplit[j]) {\n                                                            isValid = true;\n                                                            break;\n                                                        }\n                                                    }\n                                                    if (isValid)\n                                                        break;\n                                                }\n                                            }\n                                            else {\n                                                errorPossibleTypes.push(currType);\n\n                                                if (dataValueType === currType) {\n                                                    isValid = true;\n                                                    break;\n                                                }\n                                            }\n                                        }\n\n                                        if (isValid) {\n                                            isDiff = dataValue !== dataDiffValue;\n\n                                            if (isDiff)\n                                                validatedOptions[prop] = dataValue;\n\n                                            if (isRestrictedValue ? inArray(dataDiffValue, restrictedStringValuesPossibilitiesSplit) < 0 : isDiff)\n                                                validatedOptionsPrepared[prop] = isRestrictedValue ? mainPossibility : dataValue;\n                                        }\n                                        else if (writeErrors) {\n                                            console.warn(error + \" it doesn't accept the type [ \" + dataValueType.toUpperCase() + \" ] with the value of \\\"\" + dataValue + \"\\\".\\r\\n\" +\n                                                \"Accepted types are: [ \" + errorPossibleTypes.join(', ').toUpperCase() + \" ].\" +\n                                                (errorRestrictedStrings[length] > 0 ? \"\\r\\nValid strings are: [ \" + errorRestrictedStrings.join(', ').split(restrictedStringsPossibilitiesSplit).join(', ') + \" ].\" : ''));\n                                        }\n                                        delete data[prop];\n                                    }\n                                }\n                            }\n                        };\n                        checkObjectProps(objectCopy, template, diffObj || {}, validatedOptions, validatedOptionsPrepared);\n\n                        //add values which aren't specified in the template to the finished validated object to prevent them from being discarded\n                        /*\n                        if(keepForeignProps) {\n                            FRAMEWORK.extend(true, validatedOptions, objectCopy);\n                            FRAMEWORK.extend(true, validatedOptionsPrepared, objectCopy);\n                        }\n                        */\n\n                        if (!isEmptyObj(objectCopy) && writeErrors)\n                            console.warn('The following options are discarded due to invalidity:\\r\\n' + window.JSON.stringify(objectCopy, null, 2));\n\n                        return {\n                            _default: validatedOptions,\n                            _prepared: validatedOptionsPrepared\n                        };\n                    }\n                }\n            }());\n\n            /**\n             * Initializes the object which contains global information about the plugin and each instance of it.\n             */\n            function initOverlayScrollbarsStatics() {\n                if (!_pluginsGlobals)\n                    _pluginsGlobals = new OverlayScrollbarsGlobals(_pluginsOptions._defaults);\n                if (!_pluginsAutoUpdateLoop)\n                    _pluginsAutoUpdateLoop = new OverlayScrollbarsAutoUpdateLoop(_pluginsGlobals);\n            }\n\n            /**\n             * The global object for the OverlayScrollbars objects. It contains resources which every OverlayScrollbars object needs. This object is initialized only once: if the first OverlayScrollbars object gets initialized.\n             * @param defaultOptions\n             * @constructor\n             */\n            function OverlayScrollbarsGlobals(defaultOptions) {\n                var _base = this;\n                var strOverflow = 'overflow';\n                var strHidden = 'hidden';\n                var strScroll = 'scroll';\n                var bodyElement = FRAMEWORK('body');\n                var scrollbarDummyElement = FRAMEWORK('<div id=\"os-dummy-scrollbar-size\"><div></div></div>');\n                var scrollbarDummyElement0 = scrollbarDummyElement[0];\n                var dummyContainerChild = FRAMEWORK(scrollbarDummyElement.children('div').eq(0));\n\n                bodyElement.append(scrollbarDummyElement);\n                scrollbarDummyElement.hide().show(); //fix IE8 bug (incorrect measuring)\n\n                var nativeScrollbarSize = calcNativeScrollbarSize(scrollbarDummyElement0);\n                var nativeScrollbarIsOverlaid = {\n                    x: nativeScrollbarSize.x === 0,\n                    y: nativeScrollbarSize.y === 0\n                };\n                var msie = (function () {\n                    var ua = window.navigator.userAgent;\n                    var strIndexOf = 'indexOf';\n                    var strSubString = 'substring';\n                    var msie = ua[strIndexOf]('MSIE ');\n                    var trident = ua[strIndexOf]('Trident/');\n                    var edge = ua[strIndexOf]('Edge/');\n                    var rv = ua[strIndexOf]('rv:');\n                    var result;\n                    var parseIntFunc = parseInt;\n\n                    // IE 10 or older => return version number\n                    if (msie > 0)\n                        result = parseIntFunc(ua[strSubString](msie + 5, ua[strIndexOf]('.', msie)), 10);\n\n                    // IE 11 => return version number\n                    else if (trident > 0)\n                        result = parseIntFunc(ua[strSubString](rv + 3, ua[strIndexOf]('.', rv)), 10);\n\n                    // Edge (IE 12+) => return version number\n                    else if (edge > 0)\n                        result = parseIntFunc(ua[strSubString](edge + 5, ua[strIndexOf]('.', edge)), 10);\n\n                    // other browser\n                    return result;\n                })();\n\n                FRAMEWORK.extend(_base, {\n                    defaultOptions: defaultOptions,\n                    msie: msie,\n                    autoUpdateLoop: false,\n                    autoUpdateRecommended: !COMPATIBILITY.mO(),\n                    nativeScrollbarSize: nativeScrollbarSize,\n                    nativeScrollbarIsOverlaid: nativeScrollbarIsOverlaid,\n                    nativeScrollbarStyling: (function () {\n                        var result = false;\n                        scrollbarDummyElement.addClass('os-viewport-native-scrollbars-invisible');\n                        try {\n                            result = (scrollbarDummyElement.css('scrollbar-width') === 'none' && (msie > 9 || !msie)) || window.getComputedStyle(scrollbarDummyElement0, '::-webkit-scrollbar').getPropertyValue('display') === 'none';\n                        } catch (ex) { }\n\n                        //fix opera bug: scrollbar styles will only appear if overflow value is scroll or auto during the activation of the style.\n                        //and set overflow to scroll\n                        //scrollbarDummyElement.css(strOverflow, strHidden).hide().css(strOverflow, strScroll).show();\n                        //return (scrollbarDummyElement0[LEXICON.oH] - scrollbarDummyElement0[LEXICON.cH]) === 0 && (scrollbarDummyElement0[LEXICON.oW] - scrollbarDummyElement0[LEXICON.cW]) === 0;\n\n                        return result;\n                    })(),\n                    overlayScrollbarDummySize: { x: 30, y: 30 },\n                    cssCalc: VENDORS._cssPropertyValue('width', 'calc', '(1px)') || null,\n                    restrictedMeasuring: (function () {\n                        //https://bugzilla.mozilla.org/show_bug.cgi?id=1439305\n                        //since 1.11.0 always false -> fixed via CSS (hopefully)\n                        scrollbarDummyElement.css(strOverflow, strHidden);\n                        var scrollSize = {\n                            w: scrollbarDummyElement0[LEXICON.sW],\n                            h: scrollbarDummyElement0[LEXICON.sH]\n                        };\n                        scrollbarDummyElement.css(strOverflow, 'visible');\n                        var scrollSize2 = {\n                            w: scrollbarDummyElement0[LEXICON.sW],\n                            h: scrollbarDummyElement0[LEXICON.sH]\n                        };\n                        return (scrollSize.w - scrollSize2.w) !== 0 || (scrollSize.h - scrollSize2.h) !== 0;\n                    })(),\n                    rtlScrollBehavior: (function () {\n                        scrollbarDummyElement.css({ 'overflow-y': strHidden, 'overflow-x': strScroll, 'direction': 'rtl' }).scrollLeft(0);\n                        var dummyContainerOffset = scrollbarDummyElement.offset();\n                        var dummyContainerChildOffset = dummyContainerChild.offset();\n                        //https://github.com/KingSora/OverlayScrollbars/issues/187\n                        scrollbarDummyElement.scrollLeft(-999);\n                        var dummyContainerChildOffsetAfterScroll = dummyContainerChild.offset();\n                        return {\n                            //origin direction = determines if the zero scroll position is on the left or right side\n                            //'i' means 'invert' (i === true means that the axis must be inverted to be correct)\n                            //true = on the left side\n                            //false = on the right side\n                            i: dummyContainerOffset.left === dummyContainerChildOffset.left,\n                            //negative = determines if the maximum scroll is positive or negative\n                            //'n' means 'negate' (n === true means that the axis must be negated to be correct)\n                            //true = negative\n                            //false = positive\n                            n: dummyContainerChildOffset.left !== dummyContainerChildOffsetAfterScroll.left\n                        };\n                    })(),\n                    supportTransform: !!VENDORS._cssProperty('transform'),\n                    supportTransition: !!VENDORS._cssProperty('transition'),\n                    supportPassiveEvents: (function () {\n                        var supportsPassive = false;\n                        try {\n                            window.addEventListener('test', null, Object.defineProperty({}, 'passive', {\n                                get: function () {\n                                    supportsPassive = true;\n                                }\n                            }));\n                        } catch (e) { }\n                        return supportsPassive;\n                    })(),\n                    supportResizeObserver: !!COMPATIBILITY.rO(),\n                    supportMutationObserver: !!COMPATIBILITY.mO()\n                });\n\n                scrollbarDummyElement.removeAttr(LEXICON.s).remove();\n\n                //Catch zoom event:\n                (function () {\n                    if (nativeScrollbarIsOverlaid.x && nativeScrollbarIsOverlaid.y)\n                        return;\n\n                    var abs = MATH.abs;\n                    var windowWidth = COMPATIBILITY.wW();\n                    var windowHeight = COMPATIBILITY.wH();\n                    var windowDpr = getWindowDPR();\n                    var onResize = function () {\n                        if (INSTANCES().length > 0) {\n                            var newW = COMPATIBILITY.wW();\n                            var newH = COMPATIBILITY.wH();\n                            var deltaW = newW - windowWidth;\n                            var deltaH = newH - windowHeight;\n\n                            if (deltaW === 0 && deltaH === 0)\n                                return;\n\n                            var deltaWRatio = MATH.round(newW / (windowWidth / 100.0));\n                            var deltaHRatio = MATH.round(newH / (windowHeight / 100.0));\n                            var absDeltaW = abs(deltaW);\n                            var absDeltaH = abs(deltaH);\n                            var absDeltaWRatio = abs(deltaWRatio);\n                            var absDeltaHRatio = abs(deltaHRatio);\n                            var newDPR = getWindowDPR();\n\n                            var deltaIsBigger = absDeltaW > 2 && absDeltaH > 2;\n                            var difference = !differenceIsBiggerThanOne(absDeltaWRatio, absDeltaHRatio);\n                            var dprChanged = newDPR !== windowDpr && windowDpr > 0;\n                            var isZoom = deltaIsBigger && difference && dprChanged;\n                            var oldScrollbarSize = _base.nativeScrollbarSize;\n                            var newScrollbarSize;\n\n                            if (isZoom) {\n                                bodyElement.append(scrollbarDummyElement);\n                                newScrollbarSize = _base.nativeScrollbarSize = calcNativeScrollbarSize(scrollbarDummyElement[0]);\n                                scrollbarDummyElement.remove();\n                                if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {\n                                    FRAMEWORK.each(INSTANCES(), function () {\n                                        if (INSTANCES(this))\n                                            INSTANCES(this).update('zoom');\n                                    });\n                                }\n                            }\n\n                            windowWidth = newW;\n                            windowHeight = newH;\n                            windowDpr = newDPR;\n                        }\n                    };\n\n                    function differenceIsBiggerThanOne(valOne, valTwo) {\n                        var absValOne = abs(valOne);\n                        var absValTwo = abs(valTwo);\n                        return !(absValOne === absValTwo || absValOne + 1 === absValTwo || absValOne - 1 === absValTwo);\n                    }\n\n                    function getWindowDPR() {\n                        var dDPI = window.screen.deviceXDPI || 0;\n                        var sDPI = window.screen.logicalXDPI || 1;\n                        return window.devicePixelRatio || (dDPI / sDPI);\n                    }\n\n                    FRAMEWORK(window).on('resize', onResize);\n                })();\n\n                function calcNativeScrollbarSize(measureElement) {\n                    return {\n                        x: measureElement[LEXICON.oH] - measureElement[LEXICON.cH],\n                        y: measureElement[LEXICON.oW] - measureElement[LEXICON.cW]\n                    };\n                }\n            }\n\n            /**\n             * The object which manages the auto update loop for all OverlayScrollbars objects. This object is initialized only once: if the first OverlayScrollbars object gets initialized.\n             * @constructor\n             */\n            function OverlayScrollbarsAutoUpdateLoop(globals) {\n                var _base = this;\n                var _inArray = FRAMEWORK.inArray;\n                var _getNow = COMPATIBILITY.now;\n                var _strAutoUpdate = 'autoUpdate';\n                var _strAutoUpdateInterval = _strAutoUpdate + 'Interval';\n                var _strLength = LEXICON.l;\n                var _loopingInstances = [];\n                var _loopingInstancesIntervalCache = [];\n                var _loopIsActive = false;\n                var _loopIntervalDefault = 33;\n                var _loopInterval = _loopIntervalDefault;\n                var _loopTimeOld = _getNow();\n                var _loopID;\n\n\n                /**\n                 * The auto update loop which will run every 50 milliseconds or less if the update interval of a instance is lower than 50 milliseconds.\n                 */\n                var loop = function () {\n                    if (_loopingInstances[_strLength] > 0 && _loopIsActive) {\n                        _loopID = COMPATIBILITY.rAF()(function () {\n                            loop();\n                        });\n                        var timeNew = _getNow();\n                        var timeDelta = timeNew - _loopTimeOld;\n                        var lowestInterval;\n                        var instance;\n                        var instanceOptions;\n                        var instanceAutoUpdateAllowed;\n                        var instanceAutoUpdateInterval;\n                        var now;\n\n                        if (timeDelta > _loopInterval) {\n                            _loopTimeOld = timeNew - (timeDelta % _loopInterval);\n                            lowestInterval = _loopIntervalDefault;\n                            for (var i = 0; i < _loopingInstances[_strLength]; i++) {\n                                instance = _loopingInstances[i];\n                                if (instance !== undefined) {\n                                    instanceOptions = instance.options();\n                                    instanceAutoUpdateAllowed = instanceOptions[_strAutoUpdate];\n                                    instanceAutoUpdateInterval = MATH.max(1, instanceOptions[_strAutoUpdateInterval]);\n                                    now = _getNow();\n\n                                    if ((instanceAutoUpdateAllowed === true || instanceAutoUpdateAllowed === null) && (now - _loopingInstancesIntervalCache[i]) > instanceAutoUpdateInterval) {\n                                        instance.update('auto');\n                                        _loopingInstancesIntervalCache[i] = new Date(now += instanceAutoUpdateInterval);\n                                    }\n\n                                    lowestInterval = MATH.max(1, MATH.min(lowestInterval, instanceAutoUpdateInterval));\n                                }\n                            }\n                            _loopInterval = lowestInterval;\n                        }\n                    } else {\n                        _loopInterval = _loopIntervalDefault;\n                    }\n                };\n\n                /**\n                 * Add OverlayScrollbars instance to the auto update loop. Only successful if the instance isn't already added.\n                 * @param instance The instance which shall be updated in a loop automatically.\n                 */\n                _base.add = function (instance) {\n                    if (_inArray(instance, _loopingInstances) === -1) {\n                        _loopingInstances.push(instance);\n                        _loopingInstancesIntervalCache.push(_getNow());\n                        if (_loopingInstances[_strLength] > 0 && !_loopIsActive) {\n                            _loopIsActive = true;\n                            globals.autoUpdateLoop = _loopIsActive;\n                            loop();\n                        }\n                    }\n                };\n\n                /**\n                 * Remove OverlayScrollbars instance from the auto update loop. Only successful if the instance was added before.\n                 * @param instance The instance which shall be updated in a loop automatically.\n                 */\n                _base.remove = function (instance) {\n                    var index = _inArray(instance, _loopingInstances);\n                    if (index > -1) {\n                        //remove from loopingInstances list\n                        _loopingInstancesIntervalCache.splice(index, 1);\n                        _loopingInstances.splice(index, 1);\n\n                        //correct update loop behavior\n                        if (_loopingInstances[_strLength] === 0 && _loopIsActive) {\n                            _loopIsActive = false;\n                            globals.autoUpdateLoop = _loopIsActive;\n                            if (_loopID !== undefined) {\n                                COMPATIBILITY.cAF()(_loopID);\n                                _loopID = -1;\n                            }\n                        }\n                    }\n                };\n            }\n\n            /**\n             * A object which manages the scrollbars visibility of the target element.\n             * @param pluginTargetElement The element from which the scrollbars shall be hidden.\n             * @param options The custom options.\n             * @param extensions The custom extensions.\n             * @param globals\n             * @param autoUpdateLoop\n             * @returns {*}\n             * @constructor\n             */\n            function OverlayScrollbarsInstance(pluginTargetElement, options, extensions, globals, autoUpdateLoop) {\n                //shortcuts\n                var type = COMPATIBILITY.type;\n                var inArray = FRAMEWORK.inArray;\n                var each = FRAMEWORK.each;\n\n                //make correct instanceof\n                var _base = new _plugin();\n                var _frameworkProto = FRAMEWORK[LEXICON.p];\n\n                //if passed element is no HTML element: skip and return\n                if (!isHTMLElement(pluginTargetElement))\n                    return;\n\n                //if passed element is already initialized: set passed options if there are any and return its instance\n                if (INSTANCES(pluginTargetElement)) {\n                    var inst = INSTANCES(pluginTargetElement);\n                    inst.options(options);\n                    return inst;\n                }\n\n                //globals:\n                var _nativeScrollbarIsOverlaid;\n                var _overlayScrollbarDummySize;\n                var _rtlScrollBehavior;\n                var _autoUpdateRecommended;\n                var _msieVersion;\n                var _nativeScrollbarStyling;\n                var _cssCalc;\n                var _nativeScrollbarSize;\n                var _supportTransition;\n                var _supportTransform;\n                var _supportPassiveEvents;\n                var _supportResizeObserver;\n                var _supportMutationObserver;\n                var _restrictedMeasuring;\n\n                //general readonly:\n                var _initialized;\n                var _destroyed;\n                var _isTextarea;\n                var _isBody;\n                var _documentMixed;\n                var _domExists;\n\n                //general:\n                var _isBorderBox;\n                var _sizeAutoObserverAdded;\n                var _paddingX;\n                var _paddingY;\n                var _borderX;\n                var _borderY;\n                var _marginX;\n                var _marginY;\n                var _isRTL;\n                var _sleeping;\n                var _contentBorderSize = {};\n                var _scrollHorizontalInfo = {};\n                var _scrollVerticalInfo = {};\n                var _viewportSize = {};\n                var _nativeScrollbarMinSize = {};\n\n                //naming:\t\n                var _strMinusHidden = '-hidden';\n                var _strMarginMinus = 'margin-';\n                var _strPaddingMinus = 'padding-';\n                var _strBorderMinus = 'border-';\n                var _strTop = 'top';\n                var _strRight = 'right';\n                var _strBottom = 'bottom';\n                var _strLeft = 'left';\n                var _strMinMinus = 'min-';\n                var _strMaxMinus = 'max-';\n                var _strWidth = 'width';\n                var _strHeight = 'height';\n                var _strFloat = 'float';\n                var _strEmpty = '';\n                var _strAuto = 'auto';\n                var _strSync = 'sync';\n                var _strScroll = 'scroll';\n                var _strHundredPercent = '100%';\n                var _strX = 'x';\n                var _strY = 'y';\n                var _strDot = '.';\n                var _strSpace = ' ';\n                var _strScrollbar = 'scrollbar';\n                var _strMinusHorizontal = '-horizontal';\n                var _strMinusVertical = '-vertical';\n                var _strScrollLeft = _strScroll + 'Left';\n                var _strScrollTop = _strScroll + 'Top';\n                var _strMouseTouchDownEvent = 'mousedown touchstart';\n                var _strMouseTouchUpEvent = 'mouseup touchend touchcancel';\n                var _strMouseTouchMoveEvent = 'mousemove touchmove';\n                var _strMouseEnter = 'mouseenter';\n                var _strMouseLeave = 'mouseleave';\n                var _strKeyDownEvent = 'keydown';\n                var _strKeyUpEvent = 'keyup';\n                var _strSelectStartEvent = 'selectstart';\n                var _strTransitionEndEvent = 'transitionend webkitTransitionEnd oTransitionEnd';\n                var _strResizeObserverProperty = '__overlayScrollbarsRO__';\n\n                //class names:\t\n                var _cassNamesPrefix = 'os-';\n                var _classNameHTMLElement = _cassNamesPrefix + 'html';\n                var _classNameHostElement = _cassNamesPrefix + 'host';\n                var _classNameHostElementForeign = _classNameHostElement + '-foreign';\n                var _classNameHostTextareaElement = _classNameHostElement + '-textarea';\n                var _classNameHostScrollbarHorizontalHidden = _classNameHostElement + '-' + _strScrollbar + _strMinusHorizontal + _strMinusHidden;\n                var _classNameHostScrollbarVerticalHidden = _classNameHostElement + '-' + _strScrollbar + _strMinusVertical + _strMinusHidden;\n                var _classNameHostTransition = _classNameHostElement + '-transition';\n                var _classNameHostRTL = _classNameHostElement + '-rtl';\n                var _classNameHostResizeDisabled = _classNameHostElement + '-resize-disabled';\n                var _classNameHostScrolling = _classNameHostElement + '-scrolling';\n                var _classNameHostOverflow = _classNameHostElement + '-overflow';\n                var _classNameHostOverflow = _classNameHostElement + '-overflow';\n                var _classNameHostOverflowX = _classNameHostOverflow + '-x';\n                var _classNameHostOverflowY = _classNameHostOverflow + '-y';\n                var _classNameTextareaElement = _cassNamesPrefix + 'textarea';\n                var _classNameTextareaCoverElement = _classNameTextareaElement + '-cover';\n                var _classNamePaddingElement = _cassNamesPrefix + 'padding';\n                var _classNameViewportElement = _cassNamesPrefix + 'viewport';\n                var _classNameViewportNativeScrollbarsInvisible = _classNameViewportElement + '-native-scrollbars-invisible';\n                var _classNameViewportNativeScrollbarsOverlaid = _classNameViewportElement + '-native-scrollbars-overlaid';\n                var _classNameContentElement = _cassNamesPrefix + 'content';\n                var _classNameContentArrangeElement = _cassNamesPrefix + 'content-arrange';\n                var _classNameContentGlueElement = _cassNamesPrefix + 'content-glue';\n                var _classNameSizeAutoObserverElement = _cassNamesPrefix + 'size-auto-observer';\n                var _classNameResizeObserverElement = _cassNamesPrefix + 'resize-observer';\n                var _classNameResizeObserverItemElement = _cassNamesPrefix + 'resize-observer-item';\n                var _classNameResizeObserverItemFinalElement = _classNameResizeObserverItemElement + '-final';\n                var _classNameTextInherit = _cassNamesPrefix + 'text-inherit';\n                var _classNameScrollbar = _cassNamesPrefix + _strScrollbar;\n                var _classNameScrollbarTrack = _classNameScrollbar + '-track';\n                var _classNameScrollbarTrackOff = _classNameScrollbarTrack + '-off';\n                var _classNameScrollbarHandle = _classNameScrollbar + '-handle';\n                var _classNameScrollbarHandleOff = _classNameScrollbarHandle + '-off';\n                var _classNameScrollbarUnusable = _classNameScrollbar + '-unusable';\n                var _classNameScrollbarAutoHidden = _classNameScrollbar + '-' + _strAuto + _strMinusHidden;\n                var _classNameScrollbarCorner = _classNameScrollbar + '-corner';\n                var _classNameScrollbarCornerResize = _classNameScrollbarCorner + '-resize';\n                var _classNameScrollbarCornerResizeB = _classNameScrollbarCornerResize + '-both';\n                var _classNameScrollbarCornerResizeH = _classNameScrollbarCornerResize + _strMinusHorizontal;\n                var _classNameScrollbarCornerResizeV = _classNameScrollbarCornerResize + _strMinusVertical;\n                var _classNameScrollbarHorizontal = _classNameScrollbar + _strMinusHorizontal;\n                var _classNameScrollbarVertical = _classNameScrollbar + _strMinusVertical;\n                var _classNameDragging = _cassNamesPrefix + 'dragging';\n                var _classNameThemeNone = _cassNamesPrefix + 'theme-none';\n                var _classNamesDynamicDestroy = [\n                    _classNameViewportNativeScrollbarsInvisible,\n                    _classNameViewportNativeScrollbarsOverlaid,\n                    _classNameScrollbarTrackOff,\n                    _classNameScrollbarHandleOff,\n                    _classNameScrollbarUnusable,\n                    _classNameScrollbarAutoHidden,\n                    _classNameScrollbarCornerResize,\n                    _classNameScrollbarCornerResizeB,\n                    _classNameScrollbarCornerResizeH,\n                    _classNameScrollbarCornerResizeV,\n                    _classNameDragging].join(_strSpace);\n\n                //callbacks:\t\n                var _callbacksInitQeueue = [];\n\n                //attrs viewport shall inherit from target\t\n                var _viewportAttrsFromTarget = [LEXICON.ti];\n\n                //options:\t\n                var _defaultOptions;\n                var _currentOptions;\n                var _currentPreparedOptions;\n\n                //extensions:\t\n                var _extensions = {};\n                var _extensionsPrivateMethods = 'added removed on contract';\n\n                //update\t\n                var _lastUpdateTime;\n                var _swallowedUpdateHints = {};\n                var _swallowedUpdateTimeout;\n                var _swallowUpdateLag = 42;\n                var _updateOnLoadEventName = 'load';\n                var _updateOnLoadElms = [];\n\n                //DOM elements:\t\n                var _windowElement;\n                var _documentElement;\n                var _htmlElement;\n                var _bodyElement;\n                var _targetElement;                     //the target element of this OverlayScrollbars object\t\n                var _hostElement;                       //the host element of this OverlayScrollbars object -> may be the same as targetElement\t\n                var _sizeAutoObserverElement;           //observes size auto changes\t\n                var _sizeObserverElement;               //observes size and padding changes\t\n                var _paddingElement;                    //manages the padding\t\n                var _viewportElement;                   //is the viewport of our scrollbar model\t\n                var _contentElement;                    //the element which holds the content\t\n                var _contentArrangeElement;             //is needed for correct sizing of the content element (only if native scrollbars are overlays)\t\n                var _contentGlueElement;                //has always the size of the content element\t\n                var _textareaCoverElement;              //only applied if target is a textarea element. Used for correct size calculation and for prevention of uncontrolled scrolling\t\n                var _scrollbarCornerElement;\n                var _scrollbarHorizontalElement;\n                var _scrollbarHorizontalTrackElement;\n                var _scrollbarHorizontalHandleElement;\n                var _scrollbarVerticalElement;\n                var _scrollbarVerticalTrackElement;\n                var _scrollbarVerticalHandleElement;\n                var _windowElementNative;\n                var _documentElementNative;\n                var _targetElementNative;\n                var _hostElementNative;\n                var _sizeAutoObserverElementNative;\n                var _sizeObserverElementNative;\n                var _paddingElementNative;\n                var _viewportElementNative;\n                var _contentElementNative;\n\n                //Cache:\t\n                var _hostSizeCache;\n                var _contentScrollSizeCache;\n                var _arrangeContentSizeCache;\n                var _hasOverflowCache;\n                var _hideOverflowCache;\n                var _widthAutoCache;\n                var _heightAutoCache;\n                var _cssBoxSizingCache;\n                var _cssPaddingCache;\n                var _cssBorderCache;\n                var _cssMarginCache;\n                var _cssDirectionCache;\n                var _cssDirectionDetectedCache;\n                var _paddingAbsoluteCache;\n                var _clipAlwaysCache;\n                var _contentGlueSizeCache;\n                var _overflowBehaviorCache;\n                var _overflowAmountCache;\n                var _ignoreOverlayScrollbarHidingCache;\n                var _autoUpdateCache;\n                var _sizeAutoCapableCache;\n                var _contentElementScrollSizeChangeDetectedCache;\n                var _hostElementSizeChangeDetectedCache;\n                var _scrollbarsVisibilityCache;\n                var _scrollbarsAutoHideCache;\n                var _scrollbarsClickScrollingCache;\n                var _scrollbarsDragScrollingCache;\n                var _resizeCache;\n                var _normalizeRTLCache;\n                var _classNameCache;\n                var _oldClassName;\n                var _textareaAutoWrappingCache;\n                var _textareaInfoCache;\n                var _textareaSizeCache;\n                var _textareaDynHeightCache;\n                var _textareaDynWidthCache;\n                var _bodyMinSizeCache;\n                var _updateAutoCache = {};\n\n                //MutationObserver:\t\n                var _mutationObserverHost;\n                var _mutationObserverContent;\n                var _mutationObserverHostCallback;\n                var _mutationObserverContentCallback;\n                var _mutationObserversConnected;\n                var _mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];\n                var _mutationObserverAttrsHost = [LEXICON.i, LEXICON.c, LEXICON.s, 'open'].concat(_viewportAttrsFromTarget);\n\n                //events:\t\n                var _destroyEvents = [];\n\n                //textarea:\t\n                var _textareaHasFocus;\n\n                //scrollbars:\t\n                var _scrollbarsAutoHideTimeoutId;\n                var _scrollbarsAutoHideMoveTimeoutId;\n                var _scrollbarsAutoHideDelay;\n                var _scrollbarsAutoHideNever;\n                var _scrollbarsAutoHideScroll;\n                var _scrollbarsAutoHideMove;\n                var _scrollbarsAutoHideLeave;\n                var _scrollbarsHandleHovered;\n                var _scrollbarsHandlesDefineScrollPos;\n\n                //resize\t\n                var _resizeNone;\n                var _resizeBoth;\n                var _resizeHorizontal;\n                var _resizeVertical;\n\n\n                //==== Event Listener ====//\t\n\n                /**\t\n                 * Adds or removes a event listener from the given element. \t\n                 * @param element The element to which the event listener shall be applied or removed.\t\n                 * @param eventNames The name(s) of the events.\t\n                 * @param listener The method which shall be called.\t\n                 * @param remove True if the handler shall be removed, false or undefined if the handler shall be added.\t\n                 * @param passiveOrOptions The options for the event.\n                 */\n                function setupResponsiveEventListener(element, eventNames, listener, remove, passiveOrOptions) {\n                    var collected = COMPATIBILITY.isA(eventNames) && COMPATIBILITY.isA(listener);\n                    var method = remove ? 'removeEventListener' : 'addEventListener';\n                    var onOff = remove ? 'off' : 'on';\n                    var events = collected ? false : eventNames.split(_strSpace)\n                    var i = 0;\n\n                    var passiveOrOptionsIsObj = FRAMEWORK.isPlainObject(passiveOrOptions);\n                    var passive = (_supportPassiveEvents && (passiveOrOptionsIsObj ? (passiveOrOptions._passive) : passiveOrOptions)) || false;\n                    var capture = passiveOrOptionsIsObj && (passiveOrOptions._capture || false);\n                    var nativeParam = _supportPassiveEvents ? {\n                        passive: passive,\n                        capture: capture,\n                    } : capture;\n\n                    if (collected) {\n                        for (; i < eventNames[LEXICON.l]; i++)\n                            setupResponsiveEventListener(element, eventNames[i], listener[i], remove, passiveOrOptions);\n                    }\n                    else {\n                        for (; i < events[LEXICON.l]; i++) {\n                            if(_supportPassiveEvents) {\n                                element[0][method](events[i], listener, nativeParam);\n                            }\n                            else {\n                                element[onOff](events[i], listener);\n                            }     \n                        }\n                    }\n                }\n\n\n                function addDestroyEventListener(element, eventNames, listener, passive) {\n                    setupResponsiveEventListener(element, eventNames, listener, false, passive);\n                    _destroyEvents.push(COMPATIBILITY.bind(setupResponsiveEventListener, 0, element, eventNames, listener, true, passive));\n                }\n\n                //==== Resize Observer ====//\n\n                /**\n                 * Adds or removes a resize observer from the given element.\n                 * @param targetElement The element to which the resize observer shall be added or removed.\n                 * @param onElementResizedCallback The callback which is fired every time the resize observer registers a size change or false / undefined if the resizeObserver shall be removed.\n                 */\n                function setupResizeObserver(targetElement, onElementResizedCallback) {\n                    if (targetElement) {\n                        var resizeObserver = COMPATIBILITY.rO();\n                        var strAnimationStartEvent = 'animationstart mozAnimationStart webkitAnimationStart MSAnimationStart';\n                        var strChildNodes = 'childNodes';\n                        var constScroll = 3333333;\n                        var callback = function () {\n                            targetElement[_strScrollTop](constScroll)[_strScrollLeft](_isRTL ? _rtlScrollBehavior.n ? -constScroll : _rtlScrollBehavior.i ? 0 : constScroll : constScroll);\n                            onElementResizedCallback();\n                        };\n                        //add resize observer:\n                        if (onElementResizedCallback) {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.addClass('observed').append(generateDiv(_classNameResizeObserverElement)).contents()[0];\n                                var observer = element[_strResizeObserverProperty] = new resizeObserver(callback);\n                                observer.observe(element);\n                            }\n                            else {\n                                if (_msieVersion > 9 || !_autoUpdateRecommended) {\n                                    targetElement.prepend(\n                                        generateDiv(_classNameResizeObserverElement,\n                                            generateDiv({ c: _classNameResizeObserverItemElement, dir: 'ltr' },\n                                                generateDiv(_classNameResizeObserverItemElement,\n                                                    generateDiv(_classNameResizeObserverItemFinalElement)\n                                                ) +\n                                                generateDiv(_classNameResizeObserverItemElement,\n                                                    generateDiv({ c: _classNameResizeObserverItemFinalElement, style: 'width: 200%; height: 200%' })\n                                                )\n                                            )\n                                        )\n                                    );\n\n                                    var observerElement = targetElement[0][strChildNodes][0][strChildNodes][0];\n                                    var shrinkElement = FRAMEWORK(observerElement[strChildNodes][1]);\n                                    var expandElement = FRAMEWORK(observerElement[strChildNodes][0]);\n                                    var expandElementChild = FRAMEWORK(expandElement[0][strChildNodes][0]);\n                                    var widthCache = observerElement[LEXICON.oW];\n                                    var heightCache = observerElement[LEXICON.oH];\n                                    var isDirty;\n                                    var rAFId;\n                                    var currWidth;\n                                    var currHeight;\n                                    var factor = 2;\n                                    var nativeScrollbarSize = globals.nativeScrollbarSize; //care don't make changes to this object!!!\n                                    var reset = function () {\n                                        /*\n                                         var sizeResetWidth = observerElement[LEXICON.oW] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;\n                                         var sizeResetHeight = observerElement[LEXICON.oH] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;\n                                         var expandChildCSS = {};\n                                         expandChildCSS[_strWidth] = sizeResetWidth;\n                                         expandChildCSS[_strHeight] = sizeResetHeight;\n                                         expandElementChild.css(expandChildCSS);\n\n\n                                         expandElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);\n                                         shrinkElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);\n                                         */\n                                        expandElement[_strScrollLeft](constScroll)[_strScrollTop](constScroll);\n                                        shrinkElement[_strScrollLeft](constScroll)[_strScrollTop](constScroll);\n                                    };\n                                    var onResized = function () {\n                                        rAFId = 0;\n                                        if (!isDirty)\n                                            return;\n\n                                        widthCache = currWidth;\n                                        heightCache = currHeight;\n                                        callback();\n                                    };\n                                    var onScroll = function (event) {\n                                        currWidth = observerElement[LEXICON.oW];\n                                        currHeight = observerElement[LEXICON.oH];\n                                        isDirty = currWidth != widthCache || currHeight != heightCache;\n\n                                        if (event && isDirty && !rAFId) {\n                                            COMPATIBILITY.cAF()(rAFId);\n                                            rAFId = COMPATIBILITY.rAF()(onResized);\n                                        }\n                                        else if (!event)\n                                            onResized();\n\n                                        reset();\n                                        if (event) {\n                                            COMPATIBILITY.prvD(event);\n                                            COMPATIBILITY.stpP(event);\n                                        }\n                                        return false;\n                                    };\n                                    var expandChildCSS = {};\n                                    var observerElementCSS = {};\n\n                                    setTopRightBottomLeft(observerElementCSS, _strEmpty, [\n                                        -((nativeScrollbarSize.y + 1) * factor),\n                                        nativeScrollbarSize.x * -factor,\n                                        nativeScrollbarSize.y * -factor,\n                                        -((nativeScrollbarSize.x + 1) * factor)\n                                    ]);\n\n                                    FRAMEWORK(observerElement).css(observerElementCSS);\n                                    expandElement.on(_strScroll, onScroll);\n                                    shrinkElement.on(_strScroll, onScroll);\n                                    targetElement.on(strAnimationStartEvent, function () {\n                                        onScroll(false);\n                                    });\n                                    //lets assume that the divs will never be that large and a constant value is enough\n                                    expandChildCSS[_strWidth] = constScroll;\n                                    expandChildCSS[_strHeight] = constScroll;\n                                    expandElementChild.css(expandChildCSS);\n\n                                    reset();\n                                }\n                                else {\n                                    var attachEvent = _documentElementNative.attachEvent;\n                                    var isIE = _msieVersion !== undefined;\n                                    if (attachEvent) {\n                                        targetElement.prepend(generateDiv(_classNameResizeObserverElement));\n                                        findFirst(targetElement, _strDot + _classNameResizeObserverElement)[0].attachEvent('onresize', callback);\n                                    }\n                                    else {\n                                        var obj = _documentElementNative.createElement(TYPES.o);\n                                        obj.setAttribute(LEXICON.ti, '-1');\n                                        obj.setAttribute(LEXICON.c, _classNameResizeObserverElement);\n                                        obj.onload = function () {\n                                            var wnd = this.contentDocument.defaultView;\n                                            wnd.addEventListener('resize', callback);\n                                            wnd.document.documentElement.style.display = 'none';\n                                        };\n                                        obj.type = 'text/html';\n                                        if (isIE)\n                                            targetElement.prepend(obj);\n                                        obj.data = 'about:blank';\n                                        if (!isIE)\n                                            targetElement.prepend(obj);\n                                        targetElement.on(strAnimationStartEvent, callback);\n                                    }\n                                }\n                            }\n\n                            if (targetElement[0] === _sizeObserverElementNative) {\n                                var directionChanged = function () {\n                                    var dir = _hostElement.css('direction');\n                                    var css = {};\n                                    var scrollLeftValue = 0;\n                                    var result = false;\n                                    if (dir !== _cssDirectionDetectedCache) {\n                                        if (dir === 'ltr') {\n                                            css[_strLeft] = 0;\n                                            css[_strRight] = _strAuto;\n                                            scrollLeftValue = constScroll;\n                                        }\n                                        else {\n                                            css[_strLeft] = _strAuto;\n                                            css[_strRight] = 0;\n                                            scrollLeftValue = _rtlScrollBehavior.n ? -constScroll : _rtlScrollBehavior.i ? 0 : constScroll;\n                                        }\n                                        //execution order is important for IE!!!\n                                        _sizeObserverElement.children().eq(0).css(css);\n                                        _sizeObserverElement[_strScrollLeft](scrollLeftValue)[_strScrollTop](constScroll);\n                                        _cssDirectionDetectedCache = dir;\n                                        result = true;\n                                    }\n                                    return result;\n                                };\n                                directionChanged();\n                                addDestroyEventListener(targetElement, _strScroll, function (event) {\n                                    if (directionChanged())\n                                        update();\n                                    COMPATIBILITY.prvD(event);\n                                    COMPATIBILITY.stpP(event);\n                                    return false;\n                                });\n                            }\n                        }\n                        //remove resize observer:\n                        else {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.contents()[0];\n                                var resizeObserverObj = element[_strResizeObserverProperty];\n                                if (resizeObserverObj) {\n                                    resizeObserverObj.disconnect();\n                                    delete element[_strResizeObserverProperty];\n                                }\n                            }\n                            else {\n                                remove(targetElement.children(_strDot + _classNameResizeObserverElement).eq(0));\n                            }\n                        }\n                    }\n                }\n\n                /**\n                 * Freezes or unfreezes the given resize observer.\n                 * @param targetElement The element to which the target resize observer is applied.\n                 * @param freeze True if the resize observer shall be frozen, false otherwise.\n                 \n                function freezeResizeObserver(targetElement, freeze) {\n                    if (targetElement !== undefined) {\n                        if(freeze) {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.contents()[0];\n                                element[_strResizeObserverProperty].unobserve(element);\n                            }\n                            else {\n                                targetElement = targetElement.children(_strDot + _classNameResizeObserverElement).eq(0);\n                                var w = targetElement.css(_strWidth);\n                                var h = targetElement.css(_strHeight);\n                                var css = {};\n                                css[_strWidth] = w;\n                                css[_strHeight] = h;\n                                targetElement.css(css);\n                            }\n                        }\n                        else {\n                            if (_supportResizeObserver) {\n                                var element = targetElement.contents()[0];\n                                element[_strResizeObserverProperty].observe(element);\n                            }\n                            else {\n                                var css = { };\n                                css[_strHeight] = _strEmpty;\n                                css[_strWidth] = _strEmpty;\n                                targetElement.children(_strDot + _classNameResizeObserverElement).eq(0).css(css);\n                            }\n                        }\n                    }\n                }\n                */\n\n\n                //==== Mutation Observers ====//\n\n                /**\n                 * Creates MutationObservers for the host and content Element if they are supported.\n                 */\n                function createMutationObservers() {\n                    if (_supportMutationObserver) {\n                        var mutationObserverContentLag = 11;\n                        var mutationObserver = COMPATIBILITY.mO();\n                        var contentLastUpdate = COMPATIBILITY.now();\n                        var mutationTarget;\n                        var mutationAttrName;\n                        var mutationIsClass;\n                        var oldMutationVal;\n                        var newClassVal;\n                        var hostClassNameRegex;\n                        var contentTimeout;\n                        var now;\n                        var sizeAuto;\n                        var action;\n\n                        _mutationObserverHostCallback = function (mutations) {\n\n                            var doUpdate = false;\n                            var doUpdateForce = false;\n                            var mutation;\n                            var mutatedAttrs = [];\n\n                            if (_initialized && !_sleeping) {\n                                each(mutations, function () {\n                                    mutation = this;\n                                    mutationTarget = mutation.target;\n                                    mutationAttrName = mutation.attributeName;\n                                    mutationIsClass = mutationAttrName === LEXICON.c;\n                                    oldMutationVal = mutation.oldValue;\n                                    newClassVal = mutationTarget.className;\n\n                                    if (_domExists && mutationIsClass && !doUpdateForce) {\n                                        // if old class value contains _classNameHostElementForeign and new class value doesn't\n                                        if (oldMutationVal.indexOf(_classNameHostElementForeign) > -1 && newClassVal.indexOf(_classNameHostElementForeign) < 0) {\n                                            hostClassNameRegex = createHostClassNameRegExp(true);\n                                            _hostElementNative.className = newClassVal.split(_strSpace).concat(oldMutationVal.split(_strSpace).filter(function (name) {\n                                                return name.match(hostClassNameRegex);\n                                            })).join(_strSpace);\n                                            doUpdate = doUpdateForce = true;\n                                        }\n                                    }\n\n                                    if (!doUpdate) {\n                                        doUpdate = mutationIsClass\n                                            ? hostClassNamesChanged(oldMutationVal, newClassVal)\n                                            : mutationAttrName === LEXICON.s\n                                                ? oldMutationVal !== mutationTarget[LEXICON.s].cssText\n                                                : true;\n                                    }\n\n                                    mutatedAttrs.push(mutationAttrName);\n                                });\n\n                                updateViewportAttrsFromTarget(mutatedAttrs);\n\n                                if (doUpdate)\n                                    _base.update(doUpdateForce || _strAuto);\n                            }\n                            return doUpdate;\n                        };\n                        _mutationObserverContentCallback = function (mutations) {\n                            var doUpdate = false;\n                            var mutation;\n\n                            if (_initialized && !_sleeping) {\n                                each(mutations, function () {\n                                    mutation = this;\n                                    doUpdate = isUnknownMutation(mutation);\n                                    return !doUpdate;\n                                });\n\n                                if (doUpdate) {\n                                    now = COMPATIBILITY.now();\n                                    sizeAuto = (_heightAutoCache || _widthAutoCache);\n                                    action = function () {\n                                        if (!_destroyed) {\n                                            contentLastUpdate = now;\n\n                                            //if cols, rows or wrap attr was changed\n                                            if (_isTextarea)\n                                                textareaUpdate();\n\n                                            if (sizeAuto)\n                                                update();\n                                            else\n                                                _base.update(_strAuto);\n                                        }\n                                    };\n                                    clearTimeout(contentTimeout);\n                                    if (mutationObserverContentLag <= 0 || now - contentLastUpdate > mutationObserverContentLag || !sizeAuto)\n                                        action();\n                                    else\n                                        contentTimeout = setTimeout(action, mutationObserverContentLag);\n                                }\n                            }\n                            return doUpdate;\n                        }\n\n                        _mutationObserverHost = new mutationObserver(_mutationObserverHostCallback);\n                        _mutationObserverContent = new mutationObserver(_mutationObserverContentCallback);\n                    }\n                }\n\n                /**\n                 * Connects the MutationObservers if they are supported.\n                 */\n                function connectMutationObservers() {\n                    if (_supportMutationObserver && !_mutationObserversConnected) {\n                        _mutationObserverHost.observe(_hostElementNative, {\n                            attributes: true,\n                            attributeOldValue: true,\n                            attributeFilter: _mutationObserverAttrsHost\n                        });\n\n                        _mutationObserverContent.observe(_isTextarea ? _targetElementNative : _contentElementNative, {\n                            attributes: true,\n                            attributeOldValue: true,\n                            subtree: !_isTextarea,\n                            childList: !_isTextarea,\n                            characterData: !_isTextarea,\n                            attributeFilter: _isTextarea ? _mutationObserverAttrsTextarea : _mutationObserverAttrsHost\n                        });\n\n                        _mutationObserversConnected = true;\n                    }\n                }\n\n                /**\n                 * Disconnects the MutationObservers if they are supported.\n                 */\n                function disconnectMutationObservers() {\n                    if (_supportMutationObserver && _mutationObserversConnected) {\n                        _mutationObserverHost.disconnect();\n                        _mutationObserverContent.disconnect();\n\n                        _mutationObserversConnected = false;\n                    }\n                }\n\n\n                //==== Events of elements ====//\n\n                /**\n                 * This method gets called every time the host element gets resized. IMPORTANT: Padding changes are detected too!!\n                 * It refreshes the hostResizedEventArgs and the hostSizeResizeCache.\n                 * If there are any size changes, the update method gets called.\n                 */\n                function hostOnResized() {\n                    if (!_sleeping) {\n                        var changed;\n                        var hostSize = {\n                            w: _sizeObserverElementNative[LEXICON.sW],\n                            h: _sizeObserverElementNative[LEXICON.sH]\n                        };\n\n                        changed = checkCache(hostSize, _hostElementSizeChangeDetectedCache);\n                        _hostElementSizeChangeDetectedCache = hostSize;\n                        if (changed)\n                            update({ _hostSizeChanged: true });\n                    }\n                }\n\n                /**\n                 * The mouse enter event of the host element. This event is only needed for the autoHide feature.\n                 */\n                function hostOnMouseEnter() {\n                    if (_scrollbarsAutoHideLeave)\n                        refreshScrollbarsAutoHide(true);\n                }\n\n                /**\n                 * The mouse leave event of the host element. This event is only needed for the autoHide feature.\n                 */\n                function hostOnMouseLeave() {\n                    if (_scrollbarsAutoHideLeave && !_bodyElement.hasClass(_classNameDragging))\n                        refreshScrollbarsAutoHide(false);\n                }\n\n                /**\n                 * The mouse move event of the host element. This event is only needed for the autoHide \"move\" feature.\n                 */\n                function hostOnMouseMove() {\n                    if (_scrollbarsAutoHideMove) {\n                        refreshScrollbarsAutoHide(true);\n                        clearTimeout(_scrollbarsAutoHideMoveTimeoutId);\n                        _scrollbarsAutoHideMoveTimeoutId = setTimeout(function () {\n                            if (_scrollbarsAutoHideMove && !_destroyed)\n                                refreshScrollbarsAutoHide(false);\n                        }, 100);\n                    }\n                }\n\n                /**\n                 * Prevents text from deselection if attached to the document element on the mousedown event of a DOM element.\n                 * @param event The select start event.\n                 */\n                function documentOnSelectStart(event) {\n                    COMPATIBILITY.prvD(event);\n                    return false;\n                }\n\n                /**\t\n                 * A callback which will be called after a element has loaded.\t\n                 */\n                function updateOnLoadCallback(event) {\n                    var elm = FRAMEWORK(event.target);\n\n                    eachUpdateOnLoad(function (i, updateOnLoadSelector) {\n                        if (elm.is(updateOnLoadSelector)) {\n                            update({ _contentSizeChanged: true });\n                        }\n                    });\n                }\n\n                /**\n                * Adds or removes mouse & touch events of the host element. (for handling auto-hiding of the scrollbars)\n                * @param destroy Indicates whether the events shall be added or removed.\n                */\n                function setupHostMouseTouchEvents(destroy) {\n                    if (!destroy)\n                        setupHostMouseTouchEvents(true);\n\n                    setupResponsiveEventListener(_hostElement,\n                        _strMouseTouchMoveEvent.split(_strSpace)[0],\n                        hostOnMouseMove,\n                        (!_scrollbarsAutoHideMove || destroy), true);\n                    setupResponsiveEventListener(_hostElement,\n                        [_strMouseEnter, _strMouseLeave],\n                        [hostOnMouseEnter, hostOnMouseLeave],\n                        (!_scrollbarsAutoHideLeave || destroy), true);\n\n                    //if the plugin is initialized and the mouse is over the host element, make the scrollbars visible\n                    if (!_initialized && !destroy)\n                        _hostElement.one('mouseover', hostOnMouseEnter);\n                }\n\n\n                //==== Update Detection ====//\n\n                /**\n                 * Measures the min width and min height of the body element and refreshes the related cache.\n                 * @returns {boolean} True if the min width or min height has changed, false otherwise.\n                 */\n                function bodyMinSizeChanged() {\n                    var bodyMinSize = {};\n                    if (_isBody && _contentArrangeElement) {\n                        bodyMinSize.w = parseToZeroOrNumber(_contentArrangeElement.css(_strMinMinus + _strWidth));\n                        bodyMinSize.h = parseToZeroOrNumber(_contentArrangeElement.css(_strMinMinus + _strHeight));\n                        bodyMinSize.c = checkCache(bodyMinSize, _bodyMinSizeCache);\n                        bodyMinSize.f = true; //flag for \"measured at least once\"\n                    }\n                    _bodyMinSizeCache = bodyMinSize;\n                    return !!bodyMinSize.c;\n                }\n\n                /**\n                 * Returns true if the class names really changed (new class without plugin host prefix)\n                 * @param oldClassNames The old ClassName string or array.\n                 * @param newClassNames The new ClassName string or array.\n                 * @returns {boolean} True if the class names has really changed, false otherwise.\n                 */\n                function hostClassNamesChanged(oldClassNames, newClassNames) {\n                    var currClasses = typeof newClassNames == TYPES.s ? newClassNames.split(_strSpace) : [];\n                    var oldClasses = typeof oldClassNames == TYPES.s ? oldClassNames.split(_strSpace) : [];\n                    var diff = getArrayDifferences(oldClasses, currClasses);\n\n                    // remove none theme from diff list to prevent update\n                    var idx = inArray(_classNameThemeNone, diff);\n                    var i;\n                    var regex;\n\n                    if (idx > -1)\n                        diff.splice(idx, 1);\n\n                    if (diff[LEXICON.l] > 0) {\n                        regex = createHostClassNameRegExp(true, true);\n                        for (i = 0; i < diff.length; i++) {\n                            if (!diff[i].match(regex)) {\n                                return true;\n                            }\n                        }\n                    }\n                    return false;\n                }\n\n                /**\n                 * Returns true if the given mutation is not from a from the plugin generated element. If the target element is a textarea the mutation is always unknown.\n                 * @param mutation The mutation which shall be checked.\n                 * @returns {boolean} True if the mutation is from a unknown element, false otherwise.\n                 */\n                function isUnknownMutation(mutation) {\n                    var attributeName = mutation.attributeName;\n                    var mutationTarget = mutation.target;\n                    var mutationType = mutation.type;\n                    var strClosest = 'closest';\n\n                    if (mutationTarget === _contentElementNative)\n                        return attributeName === null;\n                    if (mutationType === 'attributes' && (attributeName === LEXICON.c || attributeName === LEXICON.s) && !_isTextarea) {\n                        //ignore className changes by the plugin\t\n                        if (attributeName === LEXICON.c && FRAMEWORK(mutationTarget).hasClass(_classNameHostElement))\n                            return hostClassNamesChanged(mutation.oldValue, mutationTarget.className);\n\n                        //only do it of browser support it natively\t\n                        if (typeof mutationTarget[strClosest] != TYPES.f)\n                            return true;\n                        if (mutationTarget[strClosest](_strDot + _classNameResizeObserverElement) !== null ||\n                            mutationTarget[strClosest](_strDot + _classNameScrollbar) !== null ||\n                            mutationTarget[strClosest](_strDot + _classNameScrollbarCorner) !== null)\n                            return false;\n                    }\n                    return true;\n                }\n\n                /**\n                 * Returns true if the content size was changed since the last time this method was called.\n                 * @returns {boolean} True if the content size was changed, false otherwise.\n                 */\n                function updateAutoContentSizeChanged() {\n                    if (_sleeping)\n                        return false;\n\n                    var contentMeasureElement = getContentMeasureElement();\n                    var textareaValueLength = _isTextarea && _widthAutoCache && !_textareaAutoWrappingCache ? _targetElement.val().length : 0;\n                    var setCSS = !_mutationObserversConnected && _widthAutoCache && !_isTextarea;\n                    var css = {};\n                    var float;\n                    var bodyMinSizeC;\n                    var changed;\n                    var contentElementScrollSize;\n\n                    if (setCSS) {\n                        float = _contentElement.css(_strFloat);\n                        css[_strFloat] = _isRTL ? _strRight : _strLeft;\n                        css[_strWidth] = _strAuto;\n                        _contentElement.css(css);\n                    }\n                    contentElementScrollSize = {\n                        w: contentMeasureElement[LEXICON.sW] + textareaValueLength,\n                        h: contentMeasureElement[LEXICON.sH] + textareaValueLength\n                    };\n                    if (setCSS) {\n                        css[_strFloat] = float;\n                        css[_strWidth] = _strHundredPercent;\n                        _contentElement.css(css);\n                    }\n\n                    bodyMinSizeC = bodyMinSizeChanged();\n                    changed = checkCache(contentElementScrollSize, _contentElementScrollSizeChangeDetectedCache);\n\n                    _contentElementScrollSizeChangeDetectedCache = contentElementScrollSize;\n\n                    return changed || bodyMinSizeC;\n                }\n\n                /**\n                 * Returns true when a attribute which the MutationObserver would observe has changed.  \n                 * @returns {boolean} True if one of the attributes which a MutationObserver would observe has changed, false or undefined otherwise.\n                 */\n                function meaningfulAttrsChanged() {\n                    if (_sleeping || _mutationObserversConnected)\n                        return;\n\n                    var elem;\n                    var curr;\n                    var cache;\n                    var changedAttrs = [];\n                    var checks = [\n                        {\n                            _elem: _hostElement,\n                            _attrs: _mutationObserverAttrsHost.concat(':visible')\n                        },\n                        {\n                            _elem: _isTextarea ? _targetElement : undefined,\n                            _attrs: _mutationObserverAttrsTextarea\n                        }\n                    ];\n\n                    each(checks, function (index, check) {\n                        elem = check._elem;\n                        if (elem) {\n                            each(check._attrs, function (index, attr) {\n                                curr = attr.charAt(0) === ':' ? elem.is(attr) : elem.attr(attr);\n                                cache = _updateAutoCache[attr];\n\n                                if (checkCache(curr, cache)) {\n                                    changedAttrs.push(attr);\n                                }\n\n                                _updateAutoCache[attr] = curr;\n                            });\n                        }\n                    });\n\n                    updateViewportAttrsFromTarget(changedAttrs);\n\n                    return changedAttrs[LEXICON.l] > 0;\n                }\n\n                /**\n                 * Checks is a CSS Property of a child element is affecting the scroll size of the content.\n                 * @param propertyName The CSS property name.\n                 * @returns {boolean} True if the property is affecting the content scroll size, false otherwise.\n                 */\n                function isSizeAffectingCSSProperty(propertyName) {\n                    if (!_initialized)\n                        return true;\n                    var flexGrow = 'flex-grow';\n                    var flexShrink = 'flex-shrink';\n                    var flexBasis = 'flex-basis';\n                    var affectingPropsX = [\n                        _strWidth,\n                        _strMinMinus + _strWidth,\n                        _strMaxMinus + _strWidth,\n                        _strMarginMinus + _strLeft,\n                        _strMarginMinus + _strRight,\n                        _strLeft,\n                        _strRight,\n                        'font-weight',\n                        'word-spacing',\n                        flexGrow,\n                        flexShrink,\n                        flexBasis\n                    ];\n                    var affectingPropsXContentBox = [\n                        _strPaddingMinus + _strLeft,\n                        _strPaddingMinus + _strRight,\n                        _strBorderMinus + _strLeft + _strWidth,\n                        _strBorderMinus + _strRight + _strWidth\n                    ];\n                    var affectingPropsY = [\n                        _strHeight,\n                        _strMinMinus + _strHeight,\n                        _strMaxMinus + _strHeight,\n                        _strMarginMinus + _strTop,\n                        _strMarginMinus + _strBottom,\n                        _strTop,\n                        _strBottom,\n                        'line-height',\n                        flexGrow,\n                        flexShrink,\n                        flexBasis\n                    ];\n                    var affectingPropsYContentBox = [\n                        _strPaddingMinus + _strTop,\n                        _strPaddingMinus + _strBottom,\n                        _strBorderMinus + _strTop + _strWidth,\n                        _strBorderMinus + _strBottom + _strWidth\n                    ];\n                    var _strS = 's';\n                    var _strVS = 'v-s';\n                    var checkX = _overflowBehaviorCache.x === _strS || _overflowBehaviorCache.x === _strVS;\n                    var checkY = _overflowBehaviorCache.y === _strS || _overflowBehaviorCache.y === _strVS;\n                    var sizeIsAffected = false;\n                    var checkPropertyName = function (arr, name) {\n                        for (var i = 0; i < arr[LEXICON.l]; i++) {\n                            if (arr[i] === name)\n                                return true;\n                        }\n                        return false;\n                    };\n\n                    if (checkY) {\n                        sizeIsAffected = checkPropertyName(affectingPropsY, propertyName);\n                        if (!sizeIsAffected && !_isBorderBox)\n                            sizeIsAffected = checkPropertyName(affectingPropsYContentBox, propertyName);\n                    }\n                    if (checkX && !sizeIsAffected) {\n                        sizeIsAffected = checkPropertyName(affectingPropsX, propertyName);\n                        if (!sizeIsAffected && !_isBorderBox)\n                            sizeIsAffected = checkPropertyName(affectingPropsXContentBox, propertyName);\n                    }\n                    return sizeIsAffected;\n                }\n\n\n                //==== Update ====//\n\n                /**\n                 * Sets the attribute values of the viewport element to the values from the target element.\n                 * The value of a attribute is only set if the attribute is whitelisted.\n                 * @attrs attrs The array of attributes which shall be set or undefined if all whitelisted shall be set.\n                 */\n                function updateViewportAttrsFromTarget(attrs) {\n                    attrs = attrs || _viewportAttrsFromTarget;\n                    each(attrs, function (index, attr) {\n                        if (COMPATIBILITY.inA(attr, _viewportAttrsFromTarget) > -1) {\n                            var targetAttr = _targetElement.attr(attr);\n                            if (type(targetAttr) == TYPES.s) {\n                                _viewportElement.attr(attr, targetAttr);\n                            }\n                            else {\n                                _viewportElement.removeAttr(attr);\n                            }\n                        }\n                    });\n                }\n\n                /**\n                 * Updates the variables and size of the textarea element, and manages the scroll on new line or new character.\n                 */\n                function textareaUpdate() {\n                    if (!_sleeping) {\n                        var wrapAttrOff = !_textareaAutoWrappingCache;\n                        var minWidth = _viewportSize.w;\n                        var minHeight = _viewportSize.h;\n                        var css = {};\n                        var doMeasure = _widthAutoCache || wrapAttrOff;\n                        var origWidth;\n                        var width;\n                        var origHeight;\n                        var height;\n\n                        //reset min size\n                        css[_strMinMinus + _strWidth] = _strEmpty;\n                        css[_strMinMinus + _strHeight] = _strEmpty;\n\n                        //set width auto\n                        css[_strWidth] = _strAuto;\n                        _targetElement.css(css);\n\n                        //measure width\n                        origWidth = _targetElementNative[LEXICON.oW];\n                        width = doMeasure ? MATH.max(origWidth, _targetElementNative[LEXICON.sW] - 1) : 1;\n                        /*width += (_widthAutoCache ? _marginX + (!_isBorderBox ? wrapAttrOff ? 0 : _paddingX + _borderX : 0) : 0);*/\n\n                        //set measured width\n                        css[_strWidth] = _widthAutoCache ? _strAuto /*width*/ : _strHundredPercent;\n                        css[_strMinMinus + _strWidth] = _strHundredPercent;\n\n                        //set height auto\n                        css[_strHeight] = _strAuto;\n                        _targetElement.css(css);\n\n                        //measure height\n                        origHeight = _targetElementNative[LEXICON.oH];\n                        height = MATH.max(origHeight, _targetElementNative[LEXICON.sH] - 1);\n\n                        //append correct size values\n                        css[_strWidth] = width;\n                        css[_strHeight] = height;\n                        _textareaCoverElement.css(css);\n\n                        //apply min width / min height to prevent textarea collapsing\n                        css[_strMinMinus + _strWidth] = minWidth /*+ (!_isBorderBox && _widthAutoCache ? _paddingX + _borderX : 0)*/;\n                        css[_strMinMinus + _strHeight] = minHeight /*+ (!_isBorderBox && _heightAutoCache ? _paddingY + _borderY : 0)*/;\n                        _targetElement.css(css);\n\n                        return {\n                            _originalWidth: origWidth,\n                            _originalHeight: origHeight,\n                            _dynamicWidth: width,\n                            _dynamicHeight: height\n                        };\n                    }\n                }\n\n                /**\n                 * Updates the plugin and DOM to the current options.\n                 * This method should only be called if a update is 100% required.\n                 * @param updateHints A objects which contains hints for this update:\n                 * {\n                 *   _hostSizeChanged : boolean,\n                 *   _contentSizeChanged : boolean,\n                 *   _force : boolean,                             == preventSwallowing\n                 *   _changedOptions : { },                        == preventSwallowing && preventSleep\n                *  }\n                 */\n                function update(updateHints) {\n                    clearTimeout(_swallowedUpdateTimeout);\n                    updateHints = updateHints || {};\n                    _swallowedUpdateHints._hostSizeChanged |= updateHints._hostSizeChanged;\n                    _swallowedUpdateHints._contentSizeChanged |= updateHints._contentSizeChanged;\n                    _swallowedUpdateHints._force |= updateHints._force;\n\n                    var now = COMPATIBILITY.now();\n                    var hostSizeChanged = !!_swallowedUpdateHints._hostSizeChanged;\n                    var contentSizeChanged = !!_swallowedUpdateHints._contentSizeChanged;\n                    var force = !!_swallowedUpdateHints._force;\n                    var changedOptions = updateHints._changedOptions;\n                    var swallow = _swallowUpdateLag > 0 && _initialized && !_destroyed && !force && !changedOptions && (now - _lastUpdateTime) < _swallowUpdateLag && (!_heightAutoCache && !_widthAutoCache);\n                    var displayIsHidden;\n\n                    if (swallow)\n                        _swallowedUpdateTimeout = setTimeout(update, _swallowUpdateLag);\n\n                    //abort update due to:\n                    //destroyed\n                    //swallowing\n                    //sleeping\n                    //host is hidden or has false display\n                    if (_destroyed || swallow || (_sleeping && !changedOptions) || (_initialized && !force && (displayIsHidden = _hostElement.is(':hidden'))) || _hostElement.css('display') === 'inline')\n                        return;\n\n                    _lastUpdateTime = now;\n                    _swallowedUpdateHints = {};\n\n                    //if scrollbar styling is possible and native scrollbars aren't overlaid the scrollbar styling will be applied which hides the native scrollbars completely.\n                    if (_nativeScrollbarStyling && !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)) {\n                        //native scrollbars are hidden, so change the values to zero\n                        _nativeScrollbarSize.x = 0;\n                        _nativeScrollbarSize.y = 0;\n                    }\n                    else {\n                        //refresh native scrollbar size (in case of zoom)\n                        _nativeScrollbarSize = extendDeep({}, globals.nativeScrollbarSize);\n                    }\n\n                    // Scrollbar padding is needed for firefox, because firefox hides scrollbar automatically if the size of the div is too small.\n                    // The calculation: [scrollbar size +3 *3]\n                    // (+3 because of possible decoration e.g. borders, margins etc., but only if native scrollbar is NOT a overlaid scrollbar)\n                    // (*3 because (1)increase / (2)decrease -button and (3)resize handle)\n                    _nativeScrollbarMinSize = {\n                        x: (_nativeScrollbarSize.x + (_nativeScrollbarIsOverlaid.x ? 0 : 3)) * 3,\n                        y: (_nativeScrollbarSize.y + (_nativeScrollbarIsOverlaid.y ? 0 : 3)) * 3\n                    };\n\n                    changedOptions = changedOptions || {};\n                    //freezeResizeObserver(_sizeObserverElement, true);\n                    //freezeResizeObserver(_sizeAutoObserverElement, true);\n\n                    var checkCacheAutoForce = function () {\n                        return checkCache.apply(this, [].slice.call(arguments).concat([force]));\n                    };\n\n                    //save current scroll offset\n                    var currScroll = {\n                        x: _viewportElement[_strScrollLeft](),\n                        y: _viewportElement[_strScrollTop]()\n                    };\n\n                    var currentPreparedOptionsScrollbars = _currentPreparedOptions.scrollbars;\n                    var currentPreparedOptionsTextarea = _currentPreparedOptions.textarea;\n\n                    //scrollbars visibility:\n                    var scrollbarsVisibility = currentPreparedOptionsScrollbars.visibility;\n                    var scrollbarsVisibilityChanged = checkCacheAutoForce(scrollbarsVisibility, _scrollbarsVisibilityCache);\n\n                    //scrollbars autoHide:\n                    var scrollbarsAutoHide = currentPreparedOptionsScrollbars.autoHide;\n                    var scrollbarsAutoHideChanged = checkCacheAutoForce(scrollbarsAutoHide, _scrollbarsAutoHideCache);\n\n                    //scrollbars click scrolling\n                    var scrollbarsClickScrolling = currentPreparedOptionsScrollbars.clickScrolling;\n                    var scrollbarsClickScrollingChanged = checkCacheAutoForce(scrollbarsClickScrolling, _scrollbarsClickScrollingCache);\n\n                    //scrollbars drag scrolling\n                    var scrollbarsDragScrolling = currentPreparedOptionsScrollbars.dragScrolling;\n                    var scrollbarsDragScrollingChanged = checkCacheAutoForce(scrollbarsDragScrolling, _scrollbarsDragScrollingCache);\n\n                    //className\n                    var className = _currentPreparedOptions.className;\n                    var classNameChanged = checkCacheAutoForce(className, _classNameCache);\n\n                    //resize\n                    var resize = _currentPreparedOptions.resize;\n                    var resizeChanged = checkCacheAutoForce(resize, _resizeCache) && !_isBody; //body can't be resized since the window itself acts as resize possibility.\n\n                    //paddingAbsolute\n                    var paddingAbsolute = _currentPreparedOptions.paddingAbsolute;\n                    var paddingAbsoluteChanged = checkCacheAutoForce(paddingAbsolute, _paddingAbsoluteCache);\n\n                    //clipAlways\n                    var clipAlways = _currentPreparedOptions.clipAlways;\n                    var clipAlwaysChanged = checkCacheAutoForce(clipAlways, _clipAlwaysCache);\n\n                    //sizeAutoCapable\n                    var sizeAutoCapable = _currentPreparedOptions.sizeAutoCapable && !_isBody; //body can never be size auto, because it shall be always as big as the viewport.\n                    var sizeAutoCapableChanged = checkCacheAutoForce(sizeAutoCapable, _sizeAutoCapableCache);\n\n                    //showNativeScrollbars\n                    var ignoreOverlayScrollbarHiding = _currentPreparedOptions.nativeScrollbarsOverlaid.showNativeScrollbars;\n                    var ignoreOverlayScrollbarHidingChanged = checkCacheAutoForce(ignoreOverlayScrollbarHiding, _ignoreOverlayScrollbarHidingCache);\n\n                    //autoUpdate\n                    var autoUpdate = _currentPreparedOptions.autoUpdate;\n                    var autoUpdateChanged = checkCacheAutoForce(autoUpdate, _autoUpdateCache);\n\n                    //overflowBehavior\n                    var overflowBehavior = _currentPreparedOptions.overflowBehavior;\n                    var overflowBehaviorChanged = checkCacheAutoForce(overflowBehavior, _overflowBehaviorCache, force);\n\n                    //dynWidth:\n                    var textareaDynWidth = currentPreparedOptionsTextarea.dynWidth;\n                    var textareaDynWidthChanged = checkCacheAutoForce(_textareaDynWidthCache, textareaDynWidth);\n\n                    //dynHeight:\n                    var textareaDynHeight = currentPreparedOptionsTextarea.dynHeight;\n                    var textareaDynHeightChanged = checkCacheAutoForce(_textareaDynHeightCache, textareaDynHeight);\n\n                    //scrollbars visibility\n                    _scrollbarsAutoHideNever = scrollbarsAutoHide === 'n';\n                    _scrollbarsAutoHideScroll = scrollbarsAutoHide === 's';\n                    _scrollbarsAutoHideMove = scrollbarsAutoHide === 'm';\n                    _scrollbarsAutoHideLeave = scrollbarsAutoHide === 'l';\n\n                    //scrollbars autoHideDelay\n                    _scrollbarsAutoHideDelay = currentPreparedOptionsScrollbars.autoHideDelay;\n\n                    //old className\n                    _oldClassName = _classNameCache;\n\n                    //resize\n                    _resizeNone = resize === 'n';\n                    _resizeBoth = resize === 'b';\n                    _resizeHorizontal = resize === 'h';\n                    _resizeVertical = resize === 'v';\n\n                    //normalizeRTL\n                    _normalizeRTLCache = _currentPreparedOptions.normalizeRTL;\n\n                    //ignore overlay scrollbar hiding\n                    ignoreOverlayScrollbarHiding = ignoreOverlayScrollbarHiding && (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y);\n\n                    //refresh options cache\n                    _scrollbarsVisibilityCache = scrollbarsVisibility;\n                    _scrollbarsAutoHideCache = scrollbarsAutoHide;\n                    _scrollbarsClickScrollingCache = scrollbarsClickScrolling;\n                    _scrollbarsDragScrollingCache = scrollbarsDragScrolling;\n                    _classNameCache = className;\n                    _resizeCache = resize;\n                    _paddingAbsoluteCache = paddingAbsolute;\n                    _clipAlwaysCache = clipAlways;\n                    _sizeAutoCapableCache = sizeAutoCapable;\n                    _ignoreOverlayScrollbarHidingCache = ignoreOverlayScrollbarHiding;\n                    _autoUpdateCache = autoUpdate;\n                    _overflowBehaviorCache = extendDeep({}, overflowBehavior);\n                    _textareaDynWidthCache = textareaDynWidth;\n                    _textareaDynHeightCache = textareaDynHeight;\n                    _hasOverflowCache = _hasOverflowCache || { x: false, y: false };\n\n                    //set correct class name to the host element\n                    if (classNameChanged) {\n                        removeClass(_hostElement, _oldClassName + _strSpace + _classNameThemeNone);\n                        addClass(_hostElement, className !== undefined && className !== null && className.length > 0 ? className : _classNameThemeNone);\n                    }\n\n                    //set correct auto Update\n                    if (autoUpdateChanged) {\n                        if (autoUpdate === true || (autoUpdate === null && _autoUpdateRecommended)) {\n                            disconnectMutationObservers();\n                            autoUpdateLoop.add(_base);\n                        }\n                        else {\n                            autoUpdateLoop.remove(_base);\n                            connectMutationObservers();\n                        }\n                    }\n\n                    //activate or deactivate size auto capability\n                    if (sizeAutoCapableChanged) {\n                        if (sizeAutoCapable) {\n                            if (_contentGlueElement) {\n                                _contentGlueElement.show();\n                            }\n                            else {\n                                _contentGlueElement = FRAMEWORK(generateDiv(_classNameContentGlueElement));\n                                _paddingElement.before(_contentGlueElement);\n                            }\n                            if (_sizeAutoObserverAdded) {\n                                _sizeAutoObserverElement.show();\n                            }\n                            else {\n                                _sizeAutoObserverElement = FRAMEWORK(generateDiv(_classNameSizeAutoObserverElement));\n                                _sizeAutoObserverElementNative = _sizeAutoObserverElement[0];\n\n                                _contentGlueElement.before(_sizeAutoObserverElement);\n                                var oldSize = { w: -1, h: -1 };\n                                setupResizeObserver(_sizeAutoObserverElement, function () {\n                                    var newSize = {\n                                        w: _sizeAutoObserverElementNative[LEXICON.oW],\n                                        h: _sizeAutoObserverElementNative[LEXICON.oH]\n                                    };\n                                    if (checkCache(newSize, oldSize)) {\n                                        if (_initialized && (_heightAutoCache && newSize.h > 0) || (_widthAutoCache && newSize.w > 0)) {\n                                            update();\n                                        }\n                                        else if (_initialized && (!_heightAutoCache && newSize.h === 0) || (!_widthAutoCache && newSize.w === 0)) {\n                                            update();\n                                        }\n                                    }\n                                    oldSize = newSize;\n                                });\n                                _sizeAutoObserverAdded = true;\n                                //fix heightAuto detector bug if height is fixed but contentHeight is 0.\n                                //the probability this bug will ever happen is very very low, thats why its ok if we use calc which isn't supported in IE8.\n                                if (_cssCalc !== null)\n                                    _sizeAutoObserverElement.css(_strHeight, _cssCalc + '(100% + 1px)');\n                            }\n                        }\n                        else {\n                            if (_sizeAutoObserverAdded)\n                                _sizeAutoObserverElement.hide();\n                            if (_contentGlueElement)\n                                _contentGlueElement.hide();\n                        }\n                    }\n\n                    //if force, update all resizeObservers too\n                    if (force) {\n                        _sizeObserverElement.find('*').trigger(_strScroll);\n                        if (_sizeAutoObserverAdded)\n                            _sizeAutoObserverElement.find('*').trigger(_strScroll);\n                    }\n\n                    //display hidden:\n                    displayIsHidden = displayIsHidden === undefined ? _hostElement.is(':hidden') : displayIsHidden;\n\n                    //textarea AutoWrapping:\n                    var textareaAutoWrapping = _isTextarea ? _targetElement.attr('wrap') !== 'off' : false;\n                    var textareaAutoWrappingChanged = checkCacheAutoForce(textareaAutoWrapping, _textareaAutoWrappingCache);\n\n                    //detect direction:\n                    var cssDirection = _hostElement.css('direction');\n                    var cssDirectionChanged = checkCacheAutoForce(cssDirection, _cssDirectionCache);\n\n                    //detect box-sizing:\n                    var boxSizing = _hostElement.css('box-sizing');\n                    var boxSizingChanged = checkCacheAutoForce(boxSizing, _cssBoxSizingCache);\n\n                    //detect padding:\n                    var padding = getTopRightBottomLeftHost(_strPaddingMinus);\n\n                    //width + height auto detecting var:\n                    var sizeAutoObserverElementBCRect;\n                    //exception occurs in IE8 sometimes (unknown exception)\n                    try {\n                        sizeAutoObserverElementBCRect = _sizeAutoObserverAdded ? _sizeAutoObserverElementNative[LEXICON.bCR]() : null;\n                    } catch (ex) {\n                        return;\n                    }\n\n                    _isRTL = cssDirection === 'rtl';\n                    _isBorderBox = (boxSizing === 'border-box');\n                    var isRTLLeft = _isRTL ? _strLeft : _strRight;\n                    var isRTLRight = _isRTL ? _strRight : _strLeft;\n\n                    //detect width auto:\n                    var widthAutoResizeDetection = false;\n                    var widthAutoObserverDetection = (_sizeAutoObserverAdded && (_hostElement.css(_strFloat) !== 'none' /*|| _isTextarea */)) ? (MATH.round(sizeAutoObserverElementBCRect.right - sizeAutoObserverElementBCRect.left) === 0) && (!paddingAbsolute ? (_hostElementNative[LEXICON.cW] - _paddingX) > 0 : true) : false;\n                    if (sizeAutoCapable && !widthAutoObserverDetection) {\n                        var tmpCurrHostWidth = _hostElementNative[LEXICON.oW];\n                        var tmpCurrContentGlueWidth = _contentGlueElement.css(_strWidth);\n                        _contentGlueElement.css(_strWidth, _strAuto);\n\n                        var tmpNewHostWidth = _hostElementNative[LEXICON.oW];\n                        _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);\n                        widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;\n                        if (!widthAutoResizeDetection) {\n                            _contentGlueElement.css(_strWidth, tmpCurrHostWidth + 1);\n                            tmpNewHostWidth = _hostElementNative[LEXICON.oW];\n                            _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);\n                            widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;\n                        }\n                    }\n                    var widthAuto = (widthAutoObserverDetection || widthAutoResizeDetection) && sizeAutoCapable && !displayIsHidden;\n                    var widthAutoChanged = checkCacheAutoForce(widthAuto, _widthAutoCache);\n                    var wasWidthAuto = !widthAuto && _widthAutoCache;\n\n                    //detect height auto:\n                    var heightAuto = _sizeAutoObserverAdded && sizeAutoCapable && !displayIsHidden ? (MATH.round(sizeAutoObserverElementBCRect.bottom - sizeAutoObserverElementBCRect.top) === 0) /* && (!paddingAbsolute && (_msieVersion > 9 || !_msieVersion) ? true : true) */ : false;\n                    var heightAutoChanged = checkCacheAutoForce(heightAuto, _heightAutoCache);\n                    var wasHeightAuto = !heightAuto && _heightAutoCache;\n\n                    //detect border:\n                    //we need the border only if border box and auto size\n                    var updateBorderX = (widthAuto && _isBorderBox) || !_isBorderBox;\n                    var updateBorderY = (heightAuto && _isBorderBox) || !_isBorderBox;\n                    var border = getTopRightBottomLeftHost(_strBorderMinus, '-' + _strWidth, !updateBorderX, !updateBorderY)\n\n                    //detect margin:\n                    var margin = getTopRightBottomLeftHost(_strMarginMinus);\n\n                    //vars to apply correct css\n                    var contentElementCSS = {};\n                    var contentGlueElementCSS = {};\n\n                    //funcs\n                    var getHostSize = function () {\n                        //has to be clientSize because offsetSize respect borders\n                        return {\n                            w: _hostElementNative[LEXICON.cW],\n                            h: _hostElementNative[LEXICON.cH]\n                        };\n                    };\n                    var getViewportSize = function () {\n                        //viewport size is padding container because it never has padding, margin and a border\n                        //determine zoom rounding error -> sometimes scrollWidth/Height is smaller than clientWidth/Height\n                        //if this happens add the difference to the viewportSize to compensate the rounding error\n                        return {\n                            w: _paddingElementNative[LEXICON.oW] + MATH.max(0, _contentElementNative[LEXICON.cW] - _contentElementNative[LEXICON.sW]),\n                            h: _paddingElementNative[LEXICON.oH] + MATH.max(0, _contentElementNative[LEXICON.cH] - _contentElementNative[LEXICON.sH])\n                        };\n                    };\n\n                    //set info for padding\n                    var paddingAbsoluteX = _paddingX = padding.l + padding.r;\n                    var paddingAbsoluteY = _paddingY = padding.t + padding.b;\n                    paddingAbsoluteX *= paddingAbsolute ? 1 : 0;\n                    paddingAbsoluteY *= paddingAbsolute ? 1 : 0;\n                    padding.c = checkCacheAutoForce(padding, _cssPaddingCache);\n\n                    //set info for border\n                    _borderX = border.l + border.r;\n                    _borderY = border.t + border.b;\n                    border.c = checkCacheAutoForce(border, _cssBorderCache);\n\n                    //set info for margin\n                    _marginX = margin.l + margin.r;\n                    _marginY = margin.t + margin.b;\n                    margin.c = checkCacheAutoForce(margin, _cssMarginCache);\n\n                    //refresh cache\n                    _textareaAutoWrappingCache = textareaAutoWrapping;\n                    _cssDirectionCache = cssDirection;\n                    _cssBoxSizingCache = boxSizing;\n                    _widthAutoCache = widthAuto;\n                    _heightAutoCache = heightAuto;\n                    _cssPaddingCache = padding;\n                    _cssBorderCache = border;\n                    _cssMarginCache = margin;\n\n                    //IEFix direction changed\n                    if (cssDirectionChanged && _sizeAutoObserverAdded)\n                        _sizeAutoObserverElement.css(_strFloat, isRTLRight);\n\n                    //apply padding:\n                    if (padding.c || cssDirectionChanged || paddingAbsoluteChanged || widthAutoChanged || heightAutoChanged || boxSizingChanged || sizeAutoCapableChanged) {\n                        var paddingElementCSS = {};\n                        var textareaCSS = {};\n                        var paddingValues = [padding.t, padding.r, padding.b, padding.l];\n\n                        setTopRightBottomLeft(contentGlueElementCSS, _strMarginMinus, [-padding.t, -padding.r, -padding.b, -padding.l]);\n                        if (paddingAbsolute) {\n                            setTopRightBottomLeft(paddingElementCSS, _strEmpty, paddingValues);\n                            setTopRightBottomLeft(_isTextarea ? textareaCSS : contentElementCSS, _strPaddingMinus);\n                        }\n                        else {\n                            setTopRightBottomLeft(paddingElementCSS, _strEmpty);\n                            setTopRightBottomLeft(_isTextarea ? textareaCSS : contentElementCSS, _strPaddingMinus, paddingValues);\n                        }\n\n                        _paddingElement.css(paddingElementCSS);\n                        _targetElement.css(textareaCSS);\n                    }\n\n                    //viewport size is padding container because it never has padding, margin and a border.\n                    _viewportSize = getViewportSize();\n\n                    //update Textarea\n                    var textareaSize = _isTextarea ? textareaUpdate() : false;\n                    var textareaSizeChanged = _isTextarea && checkCacheAutoForce(textareaSize, _textareaSizeCache);\n                    var textareaDynOrigSize = _isTextarea && textareaSize ? {\n                        w: textareaDynWidth ? textareaSize._dynamicWidth : textareaSize._originalWidth,\n                        h: textareaDynHeight ? textareaSize._dynamicHeight : textareaSize._originalHeight\n                    } : {};\n                    _textareaSizeCache = textareaSize;\n\n                    //fix height auto / width auto in cooperation with current padding & boxSizing behavior:\n                    if (heightAuto && (heightAutoChanged || paddingAbsoluteChanged || boxSizingChanged || padding.c || border.c)) {\n                        contentElementCSS[_strHeight] = _strAuto;\n                    }\n                    else if (heightAutoChanged || paddingAbsoluteChanged) {\n                        contentElementCSS[_strHeight] = _strHundredPercent;\n                    }\n                    if (widthAuto && (widthAutoChanged || paddingAbsoluteChanged || boxSizingChanged || padding.c || border.c || cssDirectionChanged)) {\n                        contentElementCSS[_strWidth] = _strAuto;\n                        contentGlueElementCSS[_strMaxMinus + _strWidth] = _strHundredPercent; //IE Fix\n                    }\n                    else if (widthAutoChanged || paddingAbsoluteChanged) {\n                        contentElementCSS[_strWidth] = _strHundredPercent;\n                        contentElementCSS[_strFloat] = _strEmpty;\n                        contentGlueElementCSS[_strMaxMinus + _strWidth] = _strEmpty; //IE Fix\n                    }\n                    if (widthAuto) {\n                        //textareaDynOrigSize.w || _strAuto :: doesnt works because applied margin will shift width\n                        contentGlueElementCSS[_strWidth] = _strAuto;\n\n                        contentElementCSS[_strWidth] = VENDORS._cssPropertyValue(_strWidth, 'max-content intrinsic') || _strAuto;\n                        contentElementCSS[_strFloat] = isRTLRight;\n                    }\n                    else {\n                        contentGlueElementCSS[_strWidth] = _strEmpty;\n                    }\n                    if (heightAuto) {\n                        //textareaDynOrigSize.h || _contentElementNative[LEXICON.cH] :: use for anti scroll jumping\n                        contentGlueElementCSS[_strHeight] = textareaDynOrigSize.h || _contentElementNative[LEXICON.cH];\n                    }\n                    else {\n                        contentGlueElementCSS[_strHeight] = _strEmpty;\n                    }\n                    if (sizeAutoCapable)\n                        _contentGlueElement.css(contentGlueElementCSS);\n                    _contentElement.css(contentElementCSS);\n\n                    //CHECKPOINT HERE ~\n                    contentElementCSS = {};\n                    contentGlueElementCSS = {};\n\n                    //if [content(host) client / scroll size, or target element direction, or content(host) max-sizes] changed, or force is true\n                    if (hostSizeChanged || contentSizeChanged || textareaSizeChanged || cssDirectionChanged || boxSizingChanged || paddingAbsoluteChanged || widthAutoChanged || widthAuto || heightAutoChanged || heightAuto || ignoreOverlayScrollbarHidingChanged || overflowBehaviorChanged || clipAlwaysChanged || resizeChanged || scrollbarsVisibilityChanged || scrollbarsAutoHideChanged || scrollbarsDragScrollingChanged || scrollbarsClickScrollingChanged || textareaDynWidthChanged || textareaDynHeightChanged || textareaAutoWrappingChanged) {\n                        var strOverflow = 'overflow';\n                        var strOverflowX = strOverflow + '-x';\n                        var strOverflowY = strOverflow + '-y';\n                        var strHidden = 'hidden';\n                        var strVisible = 'visible';\n\n                        //Reset the viewport (very important for natively overlaid scrollbars and zoom change\n                        //don't change the overflow prop as it is very expensive and affects performance !A LOT!\n                        if (!_nativeScrollbarStyling) {\n                            var viewportElementResetCSS = {};\n                            var resetXTmp = _hasOverflowCache.y && _hideOverflowCache.ys && !ignoreOverlayScrollbarHiding ? (_nativeScrollbarIsOverlaid.y ? _viewportElement.css(isRTLLeft) : -_nativeScrollbarSize.y) : 0;\n                            var resetBottomTmp = _hasOverflowCache.x && _hideOverflowCache.xs && !ignoreOverlayScrollbarHiding ? (_nativeScrollbarIsOverlaid.x ? _viewportElement.css(_strBottom) : -_nativeScrollbarSize.x) : 0;\n                            setTopRightBottomLeft(viewportElementResetCSS, _strEmpty);\n                            _viewportElement.css(viewportElementResetCSS);\n                        }\n\n                        //measure several sizes:\n                        var contentMeasureElement = getContentMeasureElement();\n                        //in Firefox content element has to have overflow hidden, else element margins aren't calculated properly, this element prevents this bug, but only if scrollbars aren't overlaid\n                        var contentSize = {\n                            //use clientSize because natively overlaidScrollbars add borders\n                            w: textareaDynOrigSize.w || contentMeasureElement[LEXICON.cW],\n                            h: textareaDynOrigSize.h || contentMeasureElement[LEXICON.cH]\n                        };\n                        var scrollSize = {\n                            w: contentMeasureElement[LEXICON.sW],\n                            h: contentMeasureElement[LEXICON.sH]\n                        };\n\n                        //apply the correct viewport style and measure viewport size\n                        if (!_nativeScrollbarStyling) {\n                            viewportElementResetCSS[_strBottom] = wasHeightAuto ? _strEmpty : resetBottomTmp;\n                            viewportElementResetCSS[isRTLLeft] = wasWidthAuto ? _strEmpty : resetXTmp;\n                            _viewportElement.css(viewportElementResetCSS);\n                        }\n                        _viewportSize = getViewportSize();\n\n                        //measure and correct several sizes\n                        var hostSize = getHostSize();\n                        var hostAbsoluteRectSize = {\n                            w: hostSize.w - _marginX - _borderX - (_isBorderBox ? 0 : _paddingX),\n                            h: hostSize.h - _marginY - _borderY - (_isBorderBox ? 0 : _paddingY)\n                        };\n                        var contentGlueSize = {\n                            //client/scrollSize + AbsolutePadding -> because padding is only applied to the paddingElement if its absolute, so you have to add it manually\n                            //hostSize is clientSize -> so padding should be added manually, right? FALSE! Because content glue is inside hostElement, so we don't have to worry about padding\n                            w: MATH.max((widthAuto ? contentSize.w : scrollSize.w) + paddingAbsoluteX, hostAbsoluteRectSize.w),\n                            h: MATH.max((heightAuto ? contentSize.h : scrollSize.h) + paddingAbsoluteY, hostAbsoluteRectSize.h)\n                        };\n                        contentGlueSize.c = checkCacheAutoForce(contentGlueSize, _contentGlueSizeCache);\n                        _contentGlueSizeCache = contentGlueSize;\n\n                        //apply correct contentGlue size\n                        if (sizeAutoCapable) {\n                            //size contentGlue correctly to make sure the element has correct size if the sizing switches to auto\n                            if (contentGlueSize.c || (heightAuto || widthAuto)) {\n                                contentGlueElementCSS[_strWidth] = contentGlueSize.w;\n                                contentGlueElementCSS[_strHeight] = contentGlueSize.h;\n\n                                //textarea-sizes are already calculated correctly at this point\n                                if (!_isTextarea) {\n                                    contentSize = {\n                                        //use clientSize because natively overlaidScrollbars add borders\n                                        w: contentMeasureElement[LEXICON.cW],\n                                        h: contentMeasureElement[LEXICON.cH]\n                                    };\n                                }\n                            }\n                            var textareaCoverCSS = {};\n                            var setContentGlueElementCSSfunction = function (horizontal) {\n                                var scrollbarVars = getScrollbarVars(horizontal);\n                                var wh = scrollbarVars._w_h;\n                                var strWH = scrollbarVars._width_height;\n                                var autoSize = horizontal ? widthAuto : heightAuto;\n                                var borderSize = horizontal ? _borderX : _borderY;\n                                var paddingSize = horizontal ? _paddingX : _paddingY;\n                                var marginSize = horizontal ? _marginX : _marginY;\n                                var viewportSize = _viewportSize[wh] - borderSize - marginSize - (_isBorderBox ? 0 : paddingSize);\n\n                                //make contentGlue size -1 if element is not auto sized, to make sure that a resize event happens when the element shrinks\n                                if (!autoSize || (!autoSize && border.c))\n                                    contentGlueElementCSS[strWH] = hostAbsoluteRectSize[wh] - 1;\n\n                                //if size is auto and host is smaller than size as min size, make content glue size -1 to make sure size changes will be detected (this is only needed if padding is 0)\n                                if (autoSize && (contentSize[wh] < viewportSize) && (horizontal && _isTextarea ? !textareaAutoWrapping : true)) {\n                                    if (_isTextarea)\n                                        textareaCoverCSS[strWH] = parseToZeroOrNumber(_textareaCoverElement.css(strWH)) - 1;\n                                    contentGlueElementCSS[strWH] -= 1;\n                                }\n\n                                //make sure content glue size is at least 1\n                                if (contentSize[wh] > 0)\n                                    contentGlueElementCSS[strWH] = MATH.max(1, contentGlueElementCSS[strWH]);\n                            };\n                            setContentGlueElementCSSfunction(true);\n                            setContentGlueElementCSSfunction(false);\n\n                            if (_isTextarea)\n                                _textareaCoverElement.css(textareaCoverCSS);\n                            _contentGlueElement.css(contentGlueElementCSS);\n                        }\n                        if (widthAuto)\n                            contentElementCSS[_strWidth] = _strHundredPercent;\n                        if (widthAuto && !_isBorderBox && !_mutationObserversConnected)\n                            contentElementCSS[_strFloat] = 'none';\n\n                        //apply and reset content style\n                        _contentElement.css(contentElementCSS);\n                        contentElementCSS = {};\n\n                        //measure again, but this time all correct sizes:\n                        var contentScrollSize = {\n                            w: contentMeasureElement[LEXICON.sW],\n                            h: contentMeasureElement[LEXICON.sH],\n                        };\n                        contentScrollSize.c = contentSizeChanged = checkCacheAutoForce(contentScrollSize, _contentScrollSizeCache);\n                        _contentScrollSizeCache = contentScrollSize;\n\n                        //refresh viewport size after correct measuring\n                        _viewportSize = getViewportSize();\n\n                        hostSize = getHostSize();\n                        hostSizeChanged = checkCacheAutoForce(hostSize, _hostSizeCache);\n                        _hostSizeCache = hostSize;\n\n                        var hideOverflowForceTextarea = _isTextarea && (_viewportSize.w === 0 || _viewportSize.h === 0);\n                        var previousOverflowAmount = _overflowAmountCache;\n                        var overflowBehaviorIsVS = {};\n                        var overflowBehaviorIsVH = {};\n                        var overflowBehaviorIsS = {};\n                        var overflowAmount = {};\n                        var hasOverflow = {};\n                        var hideOverflow = {};\n                        var canScroll = {};\n                        var viewportRect = _paddingElementNative[LEXICON.bCR]();\n                        var setOverflowVariables = function (horizontal) {\n                            var scrollbarVars = getScrollbarVars(horizontal);\n                            var scrollbarVarsInverted = getScrollbarVars(!horizontal);\n                            var xyI = scrollbarVarsInverted._x_y;\n                            var xy = scrollbarVars._x_y;\n                            var wh = scrollbarVars._w_h;\n                            var widthHeight = scrollbarVars._width_height;\n                            var scrollMax = _strScroll + scrollbarVars._Left_Top + 'Max';\n                            var fractionalOverflowAmount = viewportRect[widthHeight] ? MATH.abs(viewportRect[widthHeight] - _viewportSize[wh]) : 0;\n                            var checkFractionalOverflowAmount = previousOverflowAmount && previousOverflowAmount[xy] > 0 && _viewportElementNative[scrollMax] === 0;\n                            overflowBehaviorIsVS[xy] = overflowBehavior[xy] === 'v-s';\n                            overflowBehaviorIsVH[xy] = overflowBehavior[xy] === 'v-h';\n                            overflowBehaviorIsS[xy] = overflowBehavior[xy] === 's';\n                            overflowAmount[xy] = MATH.max(0, MATH.round((contentScrollSize[wh] - _viewportSize[wh]) * 100) / 100);\n                            overflowAmount[xy] *= (hideOverflowForceTextarea || (checkFractionalOverflowAmount && fractionalOverflowAmount > 0 && fractionalOverflowAmount < 1)) ? 0 : 1;\n                            hasOverflow[xy] = overflowAmount[xy] > 0;\n\n                            //hideOverflow:\n                            //x || y : true === overflow is hidden by \"overflow: scroll\" OR \"overflow: hidden\"\n                            //xs || ys : true === overflow is hidden by \"overflow: scroll\"\n                            hideOverflow[xy] = overflowBehaviorIsVS[xy] || overflowBehaviorIsVH[xy] ? (hasOverflow[xyI] && !overflowBehaviorIsVS[xyI] && !overflowBehaviorIsVH[xyI]) : hasOverflow[xy];\n                            hideOverflow[xy + 's'] = hideOverflow[xy] ? (overflowBehaviorIsS[xy] || overflowBehaviorIsVS[xy]) : false;\n\n                            canScroll[xy] = hasOverflow[xy] && hideOverflow[xy + 's'];\n                        };\n                        setOverflowVariables(true);\n                        setOverflowVariables(false);\n\n                        overflowAmount.c = checkCacheAutoForce(overflowAmount, _overflowAmountCache);\n                        _overflowAmountCache = overflowAmount;\n                        hasOverflow.c = checkCacheAutoForce(hasOverflow, _hasOverflowCache);\n                        _hasOverflowCache = hasOverflow;\n                        hideOverflow.c = checkCacheAutoForce(hideOverflow, _hideOverflowCache);\n                        _hideOverflowCache = hideOverflow;\n\n                        //if native scrollbar is overlay at x OR y axis, prepare DOM\n                        if (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) {\n                            var borderDesign = 'px solid transparent';\n                            var contentArrangeElementCSS = {};\n                            var arrangeContent = {};\n                            var arrangeChanged = force;\n                            var setContentElementCSS;\n\n                            if (hasOverflow.x || hasOverflow.y) {\n                                arrangeContent.w = _nativeScrollbarIsOverlaid.y && hasOverflow.y ? contentScrollSize.w + _overlayScrollbarDummySize.y : _strEmpty;\n                                arrangeContent.h = _nativeScrollbarIsOverlaid.x && hasOverflow.x ? contentScrollSize.h + _overlayScrollbarDummySize.x : _strEmpty;\n                                arrangeChanged = checkCacheAutoForce(arrangeContent, _arrangeContentSizeCache);\n                                _arrangeContentSizeCache = arrangeContent;\n                            }\n\n                            if (hasOverflow.c || hideOverflow.c || contentScrollSize.c || cssDirectionChanged || widthAutoChanged || heightAutoChanged || widthAuto || heightAuto || ignoreOverlayScrollbarHidingChanged) {\n                                contentElementCSS[_strMarginMinus + isRTLRight] = contentElementCSS[_strBorderMinus + isRTLRight] = _strEmpty;\n                                setContentElementCSS = function (horizontal) {\n                                    var scrollbarVars = getScrollbarVars(horizontal);\n                                    var scrollbarVarsInverted = getScrollbarVars(!horizontal);\n                                    var xy = scrollbarVars._x_y;\n                                    var strDirection = horizontal ? _strBottom : isRTLLeft;\n                                    var invertedAutoSize = horizontal ? heightAuto : widthAuto;\n\n                                    if (_nativeScrollbarIsOverlaid[xy] && hasOverflow[xy] && hideOverflow[xy + 's']) {\n                                        contentElementCSS[_strMarginMinus + strDirection] = invertedAutoSize ? (ignoreOverlayScrollbarHiding ? _strEmpty : _overlayScrollbarDummySize[xy]) : _strEmpty;\n                                        contentElementCSS[_strBorderMinus + strDirection] = ((horizontal ? !invertedAutoSize : true) && !ignoreOverlayScrollbarHiding) ? (_overlayScrollbarDummySize[xy] + borderDesign) : _strEmpty;\n                                    }\n                                    else {\n                                        arrangeContent[scrollbarVarsInverted._w_h] =\n                                            contentElementCSS[_strMarginMinus + strDirection] =\n                                            contentElementCSS[_strBorderMinus + strDirection] = _strEmpty;\n                                        arrangeChanged = true;\n                                    }\n                                };\n\n                                if (_nativeScrollbarStyling) {\n                                    addRemoveClass(_viewportElement, _classNameViewportNativeScrollbarsInvisible, !ignoreOverlayScrollbarHiding)\n                                }\n                                else {\n                                    setContentElementCSS(true);\n                                    setContentElementCSS(false);\n                                }\n                            }\n                            if (ignoreOverlayScrollbarHiding) {\n                                arrangeContent.w = arrangeContent.h = _strEmpty;\n                                arrangeChanged = true;\n                            }\n                            if (arrangeChanged && !_nativeScrollbarStyling) {\n                                contentArrangeElementCSS[_strWidth] = hideOverflow.y ? arrangeContent.w : _strEmpty;\n                                contentArrangeElementCSS[_strHeight] = hideOverflow.x ? arrangeContent.h : _strEmpty;\n\n                                if (!_contentArrangeElement) {\n                                    _contentArrangeElement = FRAMEWORK(generateDiv(_classNameContentArrangeElement));\n                                    _viewportElement.prepend(_contentArrangeElement);\n                                }\n                                _contentArrangeElement.css(contentArrangeElementCSS);\n                            }\n                            _contentElement.css(contentElementCSS);\n                        }\n\n                        var viewportElementCSS = {};\n                        var paddingElementCSS = {};\n                        var setViewportCSS;\n                        if (hostSizeChanged || hasOverflow.c || hideOverflow.c || contentScrollSize.c || overflowBehaviorChanged || boxSizingChanged || ignoreOverlayScrollbarHidingChanged || cssDirectionChanged || clipAlwaysChanged || heightAutoChanged) {\n                            viewportElementCSS[isRTLRight] = _strEmpty;\n                            setViewportCSS = function (horizontal) {\n                                var scrollbarVars = getScrollbarVars(horizontal);\n                                var scrollbarVarsInverted = getScrollbarVars(!horizontal);\n                                var xy = scrollbarVars._x_y;\n                                var XY = scrollbarVars._X_Y;\n                                var strDirection = horizontal ? _strBottom : isRTLLeft;\n\n                                var reset = function () {\n                                    viewportElementCSS[strDirection] = _strEmpty;\n                                    _contentBorderSize[scrollbarVarsInverted._w_h] = 0;\n                                };\n                                if (hasOverflow[xy] && hideOverflow[xy + 's']) {\n                                    viewportElementCSS[strOverflow + XY] = _strScroll;\n                                    if (ignoreOverlayScrollbarHiding || _nativeScrollbarStyling) {\n                                        reset();\n                                    }\n                                    else {\n                                        viewportElementCSS[strDirection] = -(_nativeScrollbarIsOverlaid[xy] ? _overlayScrollbarDummySize[xy] : _nativeScrollbarSize[xy]);\n                                        _contentBorderSize[scrollbarVarsInverted._w_h] = _nativeScrollbarIsOverlaid[xy] ? _overlayScrollbarDummySize[scrollbarVarsInverted._x_y] : 0;\n                                    }\n                                } else {\n                                    viewportElementCSS[strOverflow + XY] = _strEmpty;\n                                    reset();\n                                }\n                            };\n                            setViewportCSS(true);\n                            setViewportCSS(false);\n\n                            // if the scroll container is too small and if there is any overflow with no overlay scrollbar (and scrollbar styling isn't possible), \n                            // make viewport element greater in size (Firefox hide Scrollbars fix)\n                            // because firefox starts hiding scrollbars on too small elements\n                            // with this behavior the overflow calculation may be incorrect or the scrollbars would appear suddenly\n                            // https://bugzilla.mozilla.org/show_bug.cgi?id=292284\n                            if (!_nativeScrollbarStyling\n                                && (_viewportSize.h < _nativeScrollbarMinSize.x || _viewportSize.w < _nativeScrollbarMinSize.y)\n                                && ((hasOverflow.x && hideOverflow.x && !_nativeScrollbarIsOverlaid.x) || (hasOverflow.y && hideOverflow.y && !_nativeScrollbarIsOverlaid.y))) {\n                                viewportElementCSS[_strPaddingMinus + _strTop] = _nativeScrollbarMinSize.x;\n                                viewportElementCSS[_strMarginMinus + _strTop] = -_nativeScrollbarMinSize.x;\n\n                                viewportElementCSS[_strPaddingMinus + isRTLRight] = _nativeScrollbarMinSize.y;\n                                viewportElementCSS[_strMarginMinus + isRTLRight] = -_nativeScrollbarMinSize.y;\n                            }\n                            else {\n                                viewportElementCSS[_strPaddingMinus + _strTop] =\n                                    viewportElementCSS[_strMarginMinus + _strTop] =\n                                    viewportElementCSS[_strPaddingMinus + isRTLRight] =\n                                    viewportElementCSS[_strMarginMinus + isRTLRight] = _strEmpty;\n                            }\n                            viewportElementCSS[_strPaddingMinus + isRTLLeft] =\n                                viewportElementCSS[_strMarginMinus + isRTLLeft] = _strEmpty;\n\n                            //if there is any overflow (x OR y axis) and this overflow shall be hidden, make overflow hidden, else overflow visible\n                            if ((hasOverflow.x && hideOverflow.x) || (hasOverflow.y && hideOverflow.y) || hideOverflowForceTextarea) {\n                                //only hide if is Textarea\n                                if (_isTextarea && hideOverflowForceTextarea) {\n                                    paddingElementCSS[strOverflowX] =\n                                        paddingElementCSS[strOverflowY] = strHidden;\n                                }\n                            }\n                            else {\n                                if (!clipAlways || (overflowBehaviorIsVH.x || overflowBehaviorIsVS.x || overflowBehaviorIsVH.y || overflowBehaviorIsVS.y)) {\n                                    //only un-hide if Textarea\n                                    if (_isTextarea) {\n                                        paddingElementCSS[strOverflowX] =\n                                            paddingElementCSS[strOverflowY] = _strEmpty;\n                                    }\n                                    viewportElementCSS[strOverflowX] =\n                                        viewportElementCSS[strOverflowY] = strVisible;\n                                }\n                            }\n\n                            _paddingElement.css(paddingElementCSS);\n                            _viewportElement.css(viewportElementCSS);\n                            viewportElementCSS = {};\n\n                            //force soft redraw in webkit because without the scrollbars will may appear because DOM wont be redrawn under special conditions\n                            if ((hasOverflow.c || boxSizingChanged || widthAutoChanged || heightAutoChanged) && !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)) {\n                                var elementStyle = _contentElementNative[LEXICON.s];\n                                var dump;\n                                elementStyle.webkitTransform = 'scale(1)';\n                                elementStyle.display = 'run-in';\n                                dump = _contentElementNative[LEXICON.oH];\n                                elementStyle.display = _strEmpty; //|| dump; //use dump to prevent it from deletion if minify\n                                elementStyle.webkitTransform = _strEmpty;\n                            }\n                            /*\n                            //force hard redraw in webkit if native overlaid scrollbars shall appear\n                            if (ignoreOverlayScrollbarHidingChanged && ignoreOverlayScrollbarHiding) {\n                                _hostElement.hide();\n                                var dump = _hostElementNative[LEXICON.oH];\n                                _hostElement.show();\n                            }\n                            */\n                        }\n\n                        //change to direction RTL and width auto Bugfix in Webkit\n                        //without this fix, the DOM still thinks the scrollbar is LTR and thus the content is shifted to the left\n                        contentElementCSS = {};\n                        if (cssDirectionChanged || widthAutoChanged || heightAutoChanged) {\n                            if (_isRTL && widthAuto) {\n                                var floatTmp = _contentElement.css(_strFloat);\n                                var posLeftWithoutFloat = MATH.round(_contentElement.css(_strFloat, _strEmpty).css(_strLeft, _strEmpty).position().left);\n                                _contentElement.css(_strFloat, floatTmp);\n                                var posLeftWithFloat = MATH.round(_contentElement.position().left);\n\n                                if (posLeftWithoutFloat !== posLeftWithFloat)\n                                    contentElementCSS[_strLeft] = posLeftWithoutFloat;\n                            }\n                            else {\n                                contentElementCSS[_strLeft] = _strEmpty;\n                            }\n                        }\n                        _contentElement.css(contentElementCSS);\n\n                        //handle scroll position\n                        if (_isTextarea && contentSizeChanged) {\n                            var textareaInfo = getTextareaInfo();\n                            if (textareaInfo) {\n                                var textareaRowsChanged = _textareaInfoCache === undefined ? true : textareaInfo._rows !== _textareaInfoCache._rows;\n                                var cursorRow = textareaInfo._cursorRow;\n                                var cursorCol = textareaInfo._cursorColumn;\n                                var widestRow = textareaInfo._widestRow;\n                                var lastRow = textareaInfo._rows;\n                                var lastCol = textareaInfo._columns;\n                                var cursorPos = textareaInfo._cursorPosition;\n                                var cursorMax = textareaInfo._cursorMax;\n                                var cursorIsLastPosition = (cursorPos >= cursorMax && _textareaHasFocus);\n                                var textareaScrollAmount = {\n                                    x: (!textareaAutoWrapping && (cursorCol === lastCol && cursorRow === widestRow)) ? _overflowAmountCache.x : -1,\n                                    y: (textareaAutoWrapping ? cursorIsLastPosition || textareaRowsChanged && (previousOverflowAmount ? (currScroll.y === previousOverflowAmount.y) : false) : (cursorIsLastPosition || textareaRowsChanged) && cursorRow === lastRow) ? _overflowAmountCache.y : -1\n                                };\n                                currScroll.x = textareaScrollAmount.x > -1 ? (_isRTL && _normalizeRTLCache && _rtlScrollBehavior.i ? 0 : textareaScrollAmount.x) : currScroll.x; //if inverted, scroll to 0 -> normalized this means to max scroll offset.\n                                currScroll.y = textareaScrollAmount.y > -1 ? textareaScrollAmount.y : currScroll.y;\n                            }\n                            _textareaInfoCache = textareaInfo;\n                        }\n                        if (_isRTL && _rtlScrollBehavior.i && _nativeScrollbarIsOverlaid.y && hasOverflow.x && _normalizeRTLCache)\n                            currScroll.x += _contentBorderSize.w || 0;\n                        if (widthAuto)\n                            _hostElement[_strScrollLeft](0);\n                        if (heightAuto)\n                            _hostElement[_strScrollTop](0);\n                        _viewportElement[_strScrollLeft](currScroll.x)[_strScrollTop](currScroll.y);\n\n                        //scrollbars management:\n                        var scrollbarsVisibilityVisible = scrollbarsVisibility === 'v';\n                        var scrollbarsVisibilityHidden = scrollbarsVisibility === 'h';\n                        var scrollbarsVisibilityAuto = scrollbarsVisibility === 'a';\n                        var refreshScrollbarsVisibility = function (showX, showY) {\n                            showY = showY === undefined ? showX : showY;\n                            refreshScrollbarAppearance(true, showX, canScroll.x)\n                            refreshScrollbarAppearance(false, showY, canScroll.y)\n                        };\n\n                        //manage class name which indicates scrollable overflow\n                        addRemoveClass(_hostElement, _classNameHostOverflow, hideOverflow.x || hideOverflow.y);\n                        addRemoveClass(_hostElement, _classNameHostOverflowX, hideOverflow.x);\n                        addRemoveClass(_hostElement, _classNameHostOverflowY, hideOverflow.y);\n\n                        //add or remove rtl class name for styling purposes except when its body, then the scrollbar stays\n                        if (cssDirectionChanged && !_isBody) {\n                            addRemoveClass(_hostElement, _classNameHostRTL, _isRTL);\n                        }\n\n                        //manage the resize feature (CSS3 resize \"polyfill\" for this plugin)\n                        if (_isBody)\n                            addClass(_hostElement, _classNameHostResizeDisabled);\n                        if (resizeChanged) {\n                            addRemoveClass(_hostElement, _classNameHostResizeDisabled, _resizeNone);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResize, !_resizeNone);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeB, _resizeBoth);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeH, _resizeHorizontal);\n                            addRemoveClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeV, _resizeVertical);\n                        }\n\n                        //manage the scrollbars general visibility + the scrollbar interactivity (unusable class name)\n                        if (scrollbarsVisibilityChanged || overflowBehaviorChanged || hideOverflow.c || hasOverflow.c || ignoreOverlayScrollbarHidingChanged) {\n                            if (ignoreOverlayScrollbarHiding) {\n                                if (ignoreOverlayScrollbarHidingChanged) {\n                                    removeClass(_hostElement, _classNameHostScrolling);\n                                    if (ignoreOverlayScrollbarHiding) {\n                                        refreshScrollbarsVisibility(false);\n                                    }\n                                }\n                            }\n                            else if (scrollbarsVisibilityAuto) {\n                                refreshScrollbarsVisibility(canScroll.x, canScroll.y);\n                            }\n                            else if (scrollbarsVisibilityVisible) {\n                                refreshScrollbarsVisibility(true);\n                            }\n                            else if (scrollbarsVisibilityHidden) {\n                                refreshScrollbarsVisibility(false);\n                            }\n                        }\n\n                        //manage the scrollbars auto hide feature (auto hide them after specific actions)\n                        if (scrollbarsAutoHideChanged || ignoreOverlayScrollbarHidingChanged) {\n                            setupHostMouseTouchEvents(!_scrollbarsAutoHideLeave && !_scrollbarsAutoHideMove);\n                            refreshScrollbarsAutoHide(_scrollbarsAutoHideNever, !_scrollbarsAutoHideNever);\n                        }\n\n                        //manage scrollbars handle length & offset - don't remove!\n                        if (hostSizeChanged || overflowAmount.c || heightAutoChanged || widthAutoChanged || resizeChanged || boxSizingChanged || paddingAbsoluteChanged || ignoreOverlayScrollbarHidingChanged || cssDirectionChanged) {\n                            refreshScrollbarHandleLength(true);\n                            refreshScrollbarHandleOffset(true);\n                            refreshScrollbarHandleLength(false);\n                            refreshScrollbarHandleOffset(false);\n                        }\n\n                        //manage interactivity\n                        if (scrollbarsClickScrollingChanged)\n                            refreshScrollbarsInteractive(true, scrollbarsClickScrolling);\n                        if (scrollbarsDragScrollingChanged)\n                            refreshScrollbarsInteractive(false, scrollbarsDragScrolling);\n\n                        //callbacks:\n                        dispatchCallback('onDirectionChanged', {\n                            isRTL: _isRTL,\n                            dir: cssDirection\n                        }, cssDirectionChanged);\n                        dispatchCallback('onHostSizeChanged', {\n                            width: _hostSizeCache.w,\n                            height: _hostSizeCache.h\n                        }, hostSizeChanged);\n                        dispatchCallback('onContentSizeChanged', {\n                            width: _contentScrollSizeCache.w,\n                            height: _contentScrollSizeCache.h\n                        }, contentSizeChanged);\n                        dispatchCallback('onOverflowChanged', {\n                            x: hasOverflow.x,\n                            y: hasOverflow.y,\n                            xScrollable: hideOverflow.xs,\n                            yScrollable: hideOverflow.ys,\n                            clipped: hideOverflow.x || hideOverflow.y\n                        }, hasOverflow.c || hideOverflow.c);\n                        dispatchCallback('onOverflowAmountChanged', {\n                            x: overflowAmount.x,\n                            y: overflowAmount.y\n                        }, overflowAmount.c);\n                    }\n\n                    //fix body min size\n                    if (_isBody && _bodyMinSizeCache && (_hasOverflowCache.c || _bodyMinSizeCache.c)) {\n                        //its possible that no min size was measured until now, because the content arrange element was just added now, in this case, measure now the min size.\n                        if (!_bodyMinSizeCache.f)\n                            bodyMinSizeChanged();\n                        if (_nativeScrollbarIsOverlaid.y && _hasOverflowCache.x)\n                            _contentElement.css(_strMinMinus + _strWidth, _bodyMinSizeCache.w + _overlayScrollbarDummySize.y);\n                        if (_nativeScrollbarIsOverlaid.x && _hasOverflowCache.y)\n                            _contentElement.css(_strMinMinus + _strHeight, _bodyMinSizeCache.h + _overlayScrollbarDummySize.x);\n                        _bodyMinSizeCache.c = false;\n                    }\n\n                    if (_initialized && changedOptions.updateOnLoad) {\n                        updateElementsOnLoad();\n                    }\n\n                    //freezeResizeObserver(_sizeObserverElement, false);\n                    //freezeResizeObserver(_sizeAutoObserverElement, false);\n\n                    dispatchCallback('onUpdated', { forced: force });\n                }\n\n                /**\n                 * Updates the found elements of which the load event shall be handled.\n                 */\n                function updateElementsOnLoad() {\n                    if (!_isTextarea) {\n                        eachUpdateOnLoad(function (i, updateOnLoadSelector) {\n                            _contentElement.find(updateOnLoadSelector).each(function (i, el) {\n                                // if element doesn't have a updateOnLoadCallback applied\n                                if (COMPATIBILITY.inA(el, _updateOnLoadElms) < 0) {\n                                    _updateOnLoadElms.push(el);\n                                    FRAMEWORK(el)\n                                        .off(_updateOnLoadEventName, updateOnLoadCallback)\n                                        .on(_updateOnLoadEventName, updateOnLoadCallback);\n                                }\n                            });\n                        });\n                    }\n                }\n\n                //==== Options ====//\n\n                /**\n                 * Sets new options but doesn't call the update method.\n                 * @param newOptions The object which contains the new options.\n                 * @returns {*} A object which contains the changed options.\n                 */\n                function setOptions(newOptions) {\n                    var validatedOpts = _pluginsOptions._validate(newOptions, _pluginsOptions._template, true, _currentOptions)\n\n                    _currentOptions = extendDeep({}, _currentOptions, validatedOpts._default);\n                    _currentPreparedOptions = extendDeep({}, _currentPreparedOptions, validatedOpts._prepared);\n\n                    return validatedOpts._prepared;\n                }\n\n\n                //==== Structure ====//\n\n                /**\n                 * Builds or destroys the wrapper and helper DOM elements.\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                /**\n                 * Builds or destroys the wrapper and helper DOM elements.\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                function setupStructureDOM(destroy) {\n                    var strParent = 'parent';\n                    var classNameResizeObserverHost = 'os-resize-observer-host';\n                    var classNameTextareaElementFull = _classNameTextareaElement + _strSpace + _classNameTextInherit;\n                    var textareaClass = _isTextarea ? _strSpace + _classNameTextInherit : _strEmpty;\n                    var adoptAttrs = _currentPreparedOptions.textarea.inheritedAttrs;\n                    var adoptAttrsMap = {};\n                    var applyAdoptedAttrs = function () {\n                        var applyAdoptedAttrsElm = destroy ? _targetElement : _hostElement;\n                        each(adoptAttrsMap, function (key, value) {\n                            if (type(value) == TYPES.s) {\n                                if (key == LEXICON.c)\n                                    applyAdoptedAttrsElm.addClass(value);\n                                else\n                                    applyAdoptedAttrsElm.attr(key, value);\n                            }\n                        });\n                    };\n                    var hostElementClassNames = [\n                        _classNameHostElement,\n                        _classNameHostElementForeign,\n                        _classNameHostTextareaElement,\n                        _classNameHostResizeDisabled,\n                        _classNameHostRTL,\n                        _classNameHostScrollbarHorizontalHidden,\n                        _classNameHostScrollbarVerticalHidden,\n                        _classNameHostTransition,\n                        _classNameHostScrolling,\n                        _classNameHostOverflow,\n                        _classNameHostOverflowX,\n                        _classNameHostOverflowY,\n                        _classNameThemeNone,\n                        _classNameTextareaElement,\n                        _classNameTextInherit,\n                        _classNameCache].join(_strSpace);\n                    var hostElementCSS = {};\n\n                    //get host element as first element, because that's the most upper element and required for the other elements\n                    _hostElement = _hostElement || (_isTextarea ? (_domExists ? _targetElement[strParent]()[strParent]()[strParent]()[strParent]() : FRAMEWORK(generateDiv(_classNameHostTextareaElement))) : _targetElement);\n                    _contentElement = _contentElement || selectOrGenerateDivByClass(_classNameContentElement + textareaClass);\n                    _viewportElement = _viewportElement || selectOrGenerateDivByClass(_classNameViewportElement + textareaClass);\n                    _paddingElement = _paddingElement || selectOrGenerateDivByClass(_classNamePaddingElement + textareaClass);\n                    _sizeObserverElement = _sizeObserverElement || selectOrGenerateDivByClass(classNameResizeObserverHost);\n                    _textareaCoverElement = _textareaCoverElement || (_isTextarea ? selectOrGenerateDivByClass(_classNameTextareaCoverElement) : undefined);\n\n                    //add this class to workaround class changing issues with UI frameworks especially Vue\n                    if (_domExists)\n                        addClass(_hostElement, _classNameHostElementForeign);\n\n                    //on destroy, remove all generated class names from the host element before collecting the adopted attributes \n                    //to prevent adopting generated class names\n                    if (destroy)\n                        removeClass(_hostElement, hostElementClassNames);\n\n                    //collect all adopted attributes\n                    adoptAttrs = type(adoptAttrs) == TYPES.s ? adoptAttrs.split(_strSpace) : adoptAttrs;\n                    if (COMPATIBILITY.isA(adoptAttrs) && _isTextarea) {\n                        each(adoptAttrs, function (i, v) {\n                            if (type(v) == TYPES.s) {\n                                adoptAttrsMap[v] = destroy ? _hostElement.attr(v) : _targetElement.attr(v);\n                            }\n                        });\n                    }\n\n                    if (!destroy) {\n                        if (_isTextarea) {\n                            if (!_currentPreparedOptions.sizeAutoCapable) {\n                                hostElementCSS[_strWidth] = _targetElement.css(_strWidth);\n                                hostElementCSS[_strHeight] = _targetElement.css(_strHeight);\n                            }\n\n                            if (!_domExists)\n                                _targetElement.addClass(_classNameTextInherit).wrap(_hostElement);\n\n                            //jQuery clones elements in wrap functions, so we have to select them again\n                            _hostElement = _targetElement[strParent]().css(hostElementCSS);\n                        }\n\n                        if (!_domExists) {\n                            //add the correct class to the target element\n                            addClass(_targetElement, _isTextarea ? classNameTextareaElementFull : _classNameHostElement);\n\n                            //wrap the content into the generated elements to create the required DOM\n                            _hostElement.wrapInner(_contentElement)\n                                .wrapInner(_viewportElement)\n                                .wrapInner(_paddingElement)\n                                .prepend(_sizeObserverElement);\n\n                            //jQuery clones elements in wrap functions, so we have to select them again\n                            _contentElement = findFirst(_hostElement, _strDot + _classNameContentElement);\n                            _viewportElement = findFirst(_hostElement, _strDot + _classNameViewportElement);\n                            _paddingElement = findFirst(_hostElement, _strDot + _classNamePaddingElement);\n\n                            if (_isTextarea) {\n                                _contentElement.prepend(_textareaCoverElement);\n                                applyAdoptedAttrs();\n                            }\n                        }\n\n                        if (_nativeScrollbarStyling)\n                            addClass(_viewportElement, _classNameViewportNativeScrollbarsInvisible);\n                        if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)\n                            addClass(_viewportElement, _classNameViewportNativeScrollbarsOverlaid);\n                        if (_isBody)\n                            addClass(_htmlElement, _classNameHTMLElement);\n\n                        _sizeObserverElementNative = _sizeObserverElement[0];\n                        _hostElementNative = _hostElement[0];\n                        _paddingElementNative = _paddingElement[0];\n                        _viewportElementNative = _viewportElement[0];\n                        _contentElementNative = _contentElement[0];\n\n                        updateViewportAttrsFromTarget();\n                    }\n                    else {\n                        if (_domExists && _initialized) {\n                            //clear size observer\n                            _sizeObserverElement.children().remove();\n\n                            //remove the style property and classes from already generated elements\n                            each([_paddingElement, _viewportElement, _contentElement, _textareaCoverElement], function (i, elm) {\n                                if (elm) {\n                                    removeClass(elm.removeAttr(LEXICON.s), _classNamesDynamicDestroy);\n                                }\n                            });\n\n                            //add classes to the host element which was removed previously to match the expected DOM\n                            addClass(_hostElement, _isTextarea ? _classNameHostTextareaElement : _classNameHostElement);\n                        }\n                        else {\n                            //remove size observer\n                            remove(_sizeObserverElement);\n\n                            //unwrap the content to restore DOM\n                            _contentElement.contents()\n                                .unwrap()\n                                .unwrap()\n                                .unwrap();\n\n                            if (_isTextarea) {\n                                _targetElement.unwrap();\n                                remove(_hostElement);\n                                remove(_textareaCoverElement);\n                                applyAdoptedAttrs();\n                            }\n                        }\n\n                        if (_isTextarea)\n                            _targetElement.removeAttr(LEXICON.s);\n\n                        if (_isBody)\n                            removeClass(_htmlElement, _classNameHTMLElement);\n                    }\n                }\n\n                /**\n                 * Adds or removes all wrapper elements interactivity events.\n                 * @param destroy Indicates whether the Events shall be added or removed.\n                 */\n                function setupStructureEvents() {\n                    var textareaKeyDownRestrictedKeyCodes = [\n                        112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 123,    //F1 to F12\n                        33, 34,                                                   //page up, page down\n                        37, 38, 39, 40,                                           //left, up, right, down arrows\n                        16, 17, 18, 19, 20, 144                                   //Shift, Ctrl, Alt, Pause, CapsLock, NumLock\n                    ];\n                    var textareaKeyDownKeyCodesList = [];\n                    var textareaUpdateIntervalID;\n                    var scrollStopTimeoutId;\n                    var scrollStopDelay = 175;\n                    var strFocus = 'focus';\n\n                    function updateTextarea(doClearInterval) {\n                        textareaUpdate();\n                        _base.update(_strAuto);\n                        if (doClearInterval && _autoUpdateRecommended)\n                            clearInterval(textareaUpdateIntervalID);\n                    }\n                    function textareaOnScroll(event) {\n                        _targetElement[_strScrollLeft](_rtlScrollBehavior.i && _normalizeRTLCache ? 9999999 : 0);\n                        _targetElement[_strScrollTop](0);\n                        COMPATIBILITY.prvD(event);\n                        COMPATIBILITY.stpP(event);\n                        return false;\n                    }\n                    function textareaOnDrop(event) {\n                        setTimeout(function () {\n                            if (!_destroyed)\n                                updateTextarea();\n                        }, 50);\n                    }\n                    function textareaOnFocus() {\n                        _textareaHasFocus = true;\n                        addClass(_hostElement, strFocus);\n                    }\n                    function textareaOnFocusout() {\n                        _textareaHasFocus = false;\n                        textareaKeyDownKeyCodesList = [];\n                        removeClass(_hostElement, strFocus);\n                        updateTextarea(true);\n                    }\n                    function textareaOnKeyDown(event) {\n                        var keyCode = event.keyCode;\n\n                        if (inArray(keyCode, textareaKeyDownRestrictedKeyCodes) < 0) {\n                            if (!textareaKeyDownKeyCodesList[LEXICON.l]) {\n                                updateTextarea();\n                                textareaUpdateIntervalID = setInterval(updateTextarea, 1000 / 60);\n                            }\n                            if (inArray(keyCode, textareaKeyDownKeyCodesList) < 0)\n                                textareaKeyDownKeyCodesList.push(keyCode);\n                        }\n                    }\n                    function textareaOnKeyUp(event) {\n                        var keyCode = event.keyCode;\n                        var index = inArray(keyCode, textareaKeyDownKeyCodesList);\n\n                        if (inArray(keyCode, textareaKeyDownRestrictedKeyCodes) < 0) {\n                            if (index > -1)\n                                textareaKeyDownKeyCodesList.splice(index, 1);\n                            if (!textareaKeyDownKeyCodesList[LEXICON.l])\n                                updateTextarea(true);\n                        }\n                    }\n                    function contentOnTransitionEnd(event) {\n                        if (_autoUpdateCache === true)\n                            return;\n                        event = event.originalEvent || event;\n                        if (isSizeAffectingCSSProperty(event.propertyName))\n                            _base.update(_strAuto);\n                    }\n                    function viewportOnScroll(event) {\n                        if (!_sleeping) {\n                            if (scrollStopTimeoutId !== undefined)\n                                clearTimeout(scrollStopTimeoutId);\n                            else {\n                                if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                                    refreshScrollbarsAutoHide(true);\n\n                                if (!nativeOverlayScrollbarsAreActive())\n                                    addClass(_hostElement, _classNameHostScrolling);\n\n                                dispatchCallback('onScrollStart', event);\n                            }\n\n                            //if a scrollbars handle gets dragged, the mousemove event is responsible for refreshing the handle offset\n                            //because if CSS scroll-snap is used, the handle offset gets only refreshed on every snap point\n                            //this looks laggy & clunky, it looks much better if the offset refreshes with the mousemove\n                            if (!_scrollbarsHandlesDefineScrollPos) {\n                                refreshScrollbarHandleOffset(true);\n                                refreshScrollbarHandleOffset(false);\n                            }\n                            dispatchCallback('onScroll', event);\n\n                            scrollStopTimeoutId = setTimeout(function () {\n                                if (!_destroyed) {\n                                    //OnScrollStop:\n                                    clearTimeout(scrollStopTimeoutId);\n                                    scrollStopTimeoutId = undefined;\n\n                                    if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                                        refreshScrollbarsAutoHide(false);\n\n                                    if (!nativeOverlayScrollbarsAreActive())\n                                        removeClass(_hostElement, _classNameHostScrolling);\n\n                                    dispatchCallback('onScrollStop', event);\n                                }\n                            }, scrollStopDelay);\n                        }\n                    }\n\n\n                    if (_isTextarea) {\n                        if (_msieVersion > 9 || !_autoUpdateRecommended) {\n                            addDestroyEventListener(_targetElement, 'input', updateTextarea);\n                        }\n                        else {\n                            addDestroyEventListener(_targetElement,\n                                [_strKeyDownEvent, _strKeyUpEvent],\n                                [textareaOnKeyDown, textareaOnKeyUp]);\n                        }\n\n                        addDestroyEventListener(_targetElement,\n                            [_strScroll, 'drop', strFocus, strFocus + 'out'],\n                            [textareaOnScroll, textareaOnDrop, textareaOnFocus, textareaOnFocusout]);\n                    }\n                    else {\n                        addDestroyEventListener(_contentElement, _strTransitionEndEvent, contentOnTransitionEnd);\n                    }\n                    addDestroyEventListener(_viewportElement, _strScroll, viewportOnScroll, true);\n                }\n\n\n                //==== Scrollbars ====//\n\n                /**\n                 * Builds or destroys all scrollbar DOM elements (scrollbar, track, handle)\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                function setupScrollbarsDOM(destroy) {\n                    var selectOrGenerateScrollbarDOM = function (isHorizontal) {\n                        var scrollbarClassName = isHorizontal ? _classNameScrollbarHorizontal : _classNameScrollbarVertical;\n                        var scrollbar = selectOrGenerateDivByClass(_classNameScrollbar + _strSpace + scrollbarClassName, true);\n                        var track = selectOrGenerateDivByClass(_classNameScrollbarTrack, scrollbar);\n                        var handle = selectOrGenerateDivByClass(_classNameScrollbarHandle, scrollbar);\n\n                        if (!_domExists && !destroy) {\n                            scrollbar.append(track);\n                            track.append(handle);\n                        }\n\n                        return {\n                            _scrollbar: scrollbar,\n                            _track: track,\n                            _handle: handle\n                        };\n                    };\n                    function resetScrollbarDOM(isHorizontal) {\n                        var scrollbarVars = getScrollbarVars(isHorizontal);\n                        var scrollbar = scrollbarVars._scrollbar;\n                        var track = scrollbarVars._track;\n                        var handle = scrollbarVars._handle;\n\n                        if (_domExists && _initialized) {\n                            each([scrollbar, track, handle], function (i, elm) {\n                                removeClass(elm.removeAttr(LEXICON.s), _classNamesDynamicDestroy);\n                            });\n                        }\n                        else {\n                            remove(scrollbar || selectOrGenerateScrollbarDOM(isHorizontal)._scrollbar);\n                        }\n                    }\n                    var horizontalElements;\n                    var verticalElements;\n\n                    if (!destroy) {\n                        horizontalElements = selectOrGenerateScrollbarDOM(true);\n                        verticalElements = selectOrGenerateScrollbarDOM();\n\n                        _scrollbarHorizontalElement = horizontalElements._scrollbar;\n                        _scrollbarHorizontalTrackElement = horizontalElements._track;\n                        _scrollbarHorizontalHandleElement = horizontalElements._handle;\n                        _scrollbarVerticalElement = verticalElements._scrollbar;\n                        _scrollbarVerticalTrackElement = verticalElements._track;\n                        _scrollbarVerticalHandleElement = verticalElements._handle;\n\n                        if (!_domExists) {\n                            _paddingElement.after(_scrollbarVerticalElement);\n                            _paddingElement.after(_scrollbarHorizontalElement);\n                        }\n                    }\n                    else {\n                        resetScrollbarDOM(true);\n                        resetScrollbarDOM();\n                    }\n                }\n\n                /**\n                 * Initializes all scrollbar interactivity events. (track and handle dragging, clicking, scrolling)\n                 * @param isHorizontal True if the target scrollbar is the horizontal scrollbar, false if the target scrollbar is the vertical scrollbar.\n                 */\n                function setupScrollbarEvents(isHorizontal) {\n                    var scrollbarVars = getScrollbarVars(isHorizontal);\n                    var scrollbarVarsInfo = scrollbarVars._info;\n                    var insideIFrame = _windowElementNative.top !== _windowElementNative;\n                    var xy = scrollbarVars._x_y;\n                    var XY = scrollbarVars._X_Y;\n                    var scroll = _strScroll + scrollbarVars._Left_Top;\n                    var strActive = 'active';\n                    var strSnapHandle = 'snapHandle';\n                    var strClickEvent = 'click';\n                    var scrollDurationFactor = 1;\n                    var increaseDecreaseScrollAmountKeyCodes = [16, 17]; //shift, ctrl\n                    var trackTimeout;\n                    var mouseDownScroll;\n                    var mouseDownOffset;\n                    var mouseDownInvertedScale;\n\n                    function getPointerPosition(event) {\n                        return _msieVersion && insideIFrame ? event['screen' + XY] : COMPATIBILITY.page(event)[xy]; //use screen coordinates in EDGE & IE because the page values are incorrect in frames.\n                    }\n                    function getPreparedScrollbarsOption(name) {\n                        return _currentPreparedOptions.scrollbars[name];\n                    }\n                    function increaseTrackScrollAmount() {\n                        scrollDurationFactor = 0.5;\n                    }\n                    function decreaseTrackScrollAmount() {\n                        scrollDurationFactor = 1;\n                    }\n                    function stopClickEventPropagation(event) {\n                        COMPATIBILITY.stpP(event);\n                    }\n                    function documentKeyDown(event) {\n                        if (inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1)\n                            increaseTrackScrollAmount();\n                    }\n                    function documentKeyUp(event) {\n                        if (inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1)\n                            decreaseTrackScrollAmount();\n                    }\n                    function onMouseTouchDownContinue(event) {\n                        var originalEvent = event.originalEvent || event;\n                        var isTouchEvent = originalEvent.touches !== undefined;\n                        return _sleeping || _destroyed || nativeOverlayScrollbarsAreActive() || !_scrollbarsDragScrollingCache || (isTouchEvent && !getPreparedScrollbarsOption('touchSupport')) ? false : COMPATIBILITY.mBtn(event) === 1 || isTouchEvent;\n                    }\n                    function documentDragMove(event) {\n                        if (onMouseTouchDownContinue(event)) {\n                            var trackLength = scrollbarVarsInfo._trackLength;\n                            var handleLength = scrollbarVarsInfo._handleLength;\n                            var scrollRange = scrollbarVarsInfo._maxScroll;\n                            var scrollRaw = (getPointerPosition(event) - mouseDownOffset) * mouseDownInvertedScale;\n                            var scrollDeltaPercent = scrollRaw / (trackLength - handleLength);\n                            var scrollDelta = (scrollRange * scrollDeltaPercent);\n                            scrollDelta = isFinite(scrollDelta) ? scrollDelta : 0;\n                            if (_isRTL && isHorizontal && !_rtlScrollBehavior.i)\n                                scrollDelta *= -1;\n\n                            _viewportElement[scroll](MATH.round(mouseDownScroll + scrollDelta));\n\n                            if (_scrollbarsHandlesDefineScrollPos)\n                                refreshScrollbarHandleOffset(isHorizontal, mouseDownScroll + scrollDelta);\n\n                            if (!_supportPassiveEvents)\n                                COMPATIBILITY.prvD(event);\n                        }\n                        else\n                            documentMouseTouchUp(event);\n                    }\n                    function documentMouseTouchUp(event) {\n                        event = event || event.originalEvent;\n\n                        setupResponsiveEventListener(_documentElement,\n                            [_strMouseTouchMoveEvent, _strMouseTouchUpEvent, _strKeyDownEvent, _strKeyUpEvent, _strSelectStartEvent],\n                            [documentDragMove, documentMouseTouchUp, documentKeyDown, documentKeyUp, documentOnSelectStart],\n                            true);\n                        COMPATIBILITY.rAF()(function() {\n                            setupResponsiveEventListener(_documentElement, strClickEvent, stopClickEventPropagation, true, { _capture: true });\n                        });\n                        \n                            \n                        if (_scrollbarsHandlesDefineScrollPos)\n                            refreshScrollbarHandleOffset(isHorizontal, true);\n\n                        _scrollbarsHandlesDefineScrollPos = false;\n                        removeClass(_bodyElement, _classNameDragging);\n                        removeClass(scrollbarVars._handle, strActive);\n                        removeClass(scrollbarVars._track, strActive);\n                        removeClass(scrollbarVars._scrollbar, strActive);\n\n                        mouseDownScroll = undefined;\n                        mouseDownOffset = undefined;\n                        mouseDownInvertedScale = 1;\n\n                        decreaseTrackScrollAmount();\n\n                        if (trackTimeout !== undefined) {\n                            _base.scrollStop();\n                            clearTimeout(trackTimeout);\n                            trackTimeout = undefined;\n                        }\n\n                        if (event) {\n                            var rect = _hostElementNative[LEXICON.bCR]();\n                            var mouseInsideHost = event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;\n\n                            //if mouse is outside host element\n                            if (!mouseInsideHost)\n                                hostOnMouseLeave();\n\n                            if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                                refreshScrollbarsAutoHide(false);\n                        }\n                    }\n                    function onHandleMouseTouchDown(event) {\n                        if (onMouseTouchDownContinue(event))\n                            onHandleMouseTouchDownAction(event);\n                    }\n                    function onHandleMouseTouchDownAction(event) {\n                        mouseDownScroll = _viewportElement[scroll]();\n                        mouseDownScroll = isNaN(mouseDownScroll) ? 0 : mouseDownScroll;\n                        if (_isRTL && isHorizontal && !_rtlScrollBehavior.n || !_isRTL)\n                            mouseDownScroll = mouseDownScroll < 0 ? 0 : mouseDownScroll;\n\n                        mouseDownInvertedScale = getHostElementInvertedScale()[xy];\n                        mouseDownOffset = getPointerPosition(event);\n\n                        _scrollbarsHandlesDefineScrollPos = !getPreparedScrollbarsOption(strSnapHandle);\n                        addClass(_bodyElement, _classNameDragging);\n                        addClass(scrollbarVars._handle, strActive);\n                        addClass(scrollbarVars._scrollbar, strActive);\n\n                        setupResponsiveEventListener(_documentElement,\n                            [_strMouseTouchMoveEvent, _strMouseTouchUpEvent, _strSelectStartEvent],\n                            [documentDragMove, documentMouseTouchUp, documentOnSelectStart]);\n                        COMPATIBILITY.rAF()(function() {\n                            setupResponsiveEventListener(_documentElement, strClickEvent, stopClickEventPropagation, false, { _capture: true });\n                        });\n                        \n\n                        if (_msieVersion || !_documentMixed)\n                            COMPATIBILITY.prvD(event);\n                        COMPATIBILITY.stpP(event);\n                    }\n                    function onTrackMouseTouchDown(event) {\n                        if (onMouseTouchDownContinue(event)) {\n                            var handleToViewportRatio = scrollbarVars._info._handleLength / Math.round(MATH.min(1, _viewportSize[scrollbarVars._w_h] / _contentScrollSizeCache[scrollbarVars._w_h]) * scrollbarVars._info._trackLength);\n                            var scrollDistance = MATH.round(_viewportSize[scrollbarVars._w_h] * handleToViewportRatio);\n                            var scrollBaseDuration = 270 * handleToViewportRatio;\n                            var scrollFirstIterationDelay = 400 * handleToViewportRatio;\n                            var trackOffset = scrollbarVars._track.offset()[scrollbarVars._left_top];\n                            var ctrlKey = event.ctrlKey;\n                            var instantScroll = event.shiftKey;\n                            var instantScrollTransition = instantScroll && ctrlKey;\n                            var isFirstIteration = true;\n                            var easing = 'linear';\n                            var decreaseScroll;\n                            var finishedCondition;\n                            var scrollActionFinsished = function (transition) {\n                                if (_scrollbarsHandlesDefineScrollPos)\n                                    refreshScrollbarHandleOffset(isHorizontal, transition);\n                            };\n                            var scrollActionInstantFinished = function () {\n                                scrollActionFinsished();\n                                onHandleMouseTouchDownAction(event);\n                            };\n                            var scrollAction = function () {\n                                if (!_destroyed) {\n                                    var mouseOffset = (mouseDownOffset - trackOffset) * mouseDownInvertedScale;\n                                    var handleOffset = scrollbarVarsInfo._handleOffset;\n                                    var trackLength = scrollbarVarsInfo._trackLength;\n                                    var handleLength = scrollbarVarsInfo._handleLength;\n                                    var scrollRange = scrollbarVarsInfo._maxScroll;\n                                    var currScroll = scrollbarVarsInfo._currentScroll;\n                                    var scrollDuration = scrollBaseDuration * scrollDurationFactor;\n                                    var timeoutDelay = isFirstIteration ? MATH.max(scrollFirstIterationDelay, scrollDuration) : scrollDuration;\n                                    var instantScrollPosition = scrollRange * ((mouseOffset - (handleLength / 2)) / (trackLength - handleLength)); // 100% * positionPercent\n                                    var rtlIsNormal = _isRTL && isHorizontal && ((!_rtlScrollBehavior.i && !_rtlScrollBehavior.n) || _normalizeRTLCache);\n                                    var decreaseScrollCondition = rtlIsNormal ? handleOffset < mouseOffset : handleOffset > mouseOffset;\n                                    var scrollObj = {};\n                                    var animationObj = {\n                                        easing: easing,\n                                        step: function (now) {\n                                            if (_scrollbarsHandlesDefineScrollPos) {\n                                                _viewportElement[scroll](now); //https://github.com/jquery/jquery/issues/4340\n                                                refreshScrollbarHandleOffset(isHorizontal, now);\n                                            }\n                                        }\n                                    };\n                                    instantScrollPosition = isFinite(instantScrollPosition) ? instantScrollPosition : 0;\n                                    instantScrollPosition = _isRTL && isHorizontal && !_rtlScrollBehavior.i ? (scrollRange - instantScrollPosition) : instantScrollPosition;\n\n                                    //_base.scrollStop();\n\n                                    if (instantScroll) {\n                                        _viewportElement[scroll](instantScrollPosition); //scroll instantly to new position\n                                        if (instantScrollTransition) {\n                                            //get the scroll position after instant scroll (in case CSS Snap Points are used) to get the correct snapped scroll position\n                                            //and the animation stops at the correct point\n                                            instantScrollPosition = _viewportElement[scroll]();\n                                            //scroll back to the position before instant scrolling so animation can be performed\n                                            _viewportElement[scroll](currScroll);\n\n                                            instantScrollPosition = rtlIsNormal && _rtlScrollBehavior.i ? (scrollRange - instantScrollPosition) : instantScrollPosition;\n                                            instantScrollPosition = rtlIsNormal && _rtlScrollBehavior.n ? -instantScrollPosition : instantScrollPosition;\n\n                                            scrollObj[xy] = instantScrollPosition;\n                                            _base.scroll(scrollObj, extendDeep(animationObj, {\n                                                duration: 130,\n                                                complete: scrollActionInstantFinished\n                                            }));\n                                        }\n                                        else\n                                            scrollActionInstantFinished();\n                                    }\n                                    else {\n                                        decreaseScroll = isFirstIteration ? decreaseScrollCondition : decreaseScroll;\n                                        finishedCondition = rtlIsNormal\n                                            ? (decreaseScroll ? handleOffset + handleLength >= mouseOffset : handleOffset <= mouseOffset)\n                                            : (decreaseScroll ? handleOffset <= mouseOffset : handleOffset + handleLength >= mouseOffset);\n\n                                        if (finishedCondition) {\n                                            clearTimeout(trackTimeout);\n                                            _base.scrollStop();\n                                            trackTimeout = undefined;\n                                            scrollActionFinsished(true);\n                                        }\n                                        else {\n                                            trackTimeout = setTimeout(scrollAction, timeoutDelay);\n\n                                            scrollObj[xy] = (decreaseScroll ? '-=' : '+=') + scrollDistance;\n                                            _base.scroll(scrollObj, extendDeep(animationObj, {\n                                                duration: scrollDuration\n                                            }));\n                                        }\n                                        isFirstIteration = false;\n                                    }\n                                }\n                            };\n                            if (ctrlKey)\n                                increaseTrackScrollAmount();\n\n                            mouseDownInvertedScale = getHostElementInvertedScale()[xy];\n                            mouseDownOffset = COMPATIBILITY.page(event)[xy];\n\n                            _scrollbarsHandlesDefineScrollPos = !getPreparedScrollbarsOption(strSnapHandle);\n                            addClass(_bodyElement, _classNameDragging);\n                            addClass(scrollbarVars._track, strActive);\n                            addClass(scrollbarVars._scrollbar, strActive);\n\n                            setupResponsiveEventListener(_documentElement,\n                                [_strMouseTouchUpEvent, _strKeyDownEvent, _strKeyUpEvent, _strSelectStartEvent],\n                                [documentMouseTouchUp, documentKeyDown, documentKeyUp, documentOnSelectStart]);\n\n                            scrollAction();\n                            COMPATIBILITY.prvD(event);\n                            COMPATIBILITY.stpP(event);\n                        }\n                    }\n                    function onTrackMouseTouchEnter(event) {\n                        //make sure both scrollbars will stay visible if one scrollbar is hovered if autoHide is \"scroll\" or \"move\".\n                        _scrollbarsHandleHovered = true;\n                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                            refreshScrollbarsAutoHide(true);\n                    }\n                    function onTrackMouseTouchLeave(event) {\n                        _scrollbarsHandleHovered = false;\n                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)\n                            refreshScrollbarsAutoHide(false);\n                    }\n                    function onScrollbarMouseTouchDown(event) {\n                        COMPATIBILITY.stpP(event);\n                    }\n\n                    addDestroyEventListener(scrollbarVars._handle,\n                        _strMouseTouchDownEvent,\n                        onHandleMouseTouchDown);\n                    addDestroyEventListener(scrollbarVars._track,\n                        [_strMouseTouchDownEvent, _strMouseEnter, _strMouseLeave],\n                        [onTrackMouseTouchDown, onTrackMouseTouchEnter, onTrackMouseTouchLeave]);\n                    addDestroyEventListener(scrollbarVars._scrollbar,\n                        _strMouseTouchDownEvent,\n                        onScrollbarMouseTouchDown);\n\n                    if (_supportTransition) {\n                        addDestroyEventListener(scrollbarVars._scrollbar, _strTransitionEndEvent, function (event) {\n                            if (event.target !== scrollbarVars._scrollbar[0])\n                                return;\n                            refreshScrollbarHandleLength(isHorizontal);\n                            refreshScrollbarHandleOffset(isHorizontal);\n                        });\n                    }\n                }\n\n                /**\n                 * Shows or hides the given scrollbar and applied a class name which indicates if the scrollbar is scrollable or not.\n                 * @param isHorizontal True if the horizontal scrollbar is the target, false if the vertical scrollbar is the target.\n                 * @param shallBeVisible True if the scrollbar shall be shown, false if hidden.\n                 * @param canScroll True if the scrollbar is scrollable, false otherwise.\n                 */\n                function refreshScrollbarAppearance(isHorizontal, shallBeVisible, canScroll) {\n                    var scrollbarHiddenClassName = isHorizontal ? _classNameHostScrollbarHorizontalHidden : _classNameHostScrollbarVerticalHidden;\n                    var scrollbarElement = isHorizontal ? _scrollbarHorizontalElement : _scrollbarVerticalElement;\n\n                    addRemoveClass(_hostElement, scrollbarHiddenClassName, !shallBeVisible);\n                    addRemoveClass(scrollbarElement, _classNameScrollbarUnusable, !canScroll);\n                }\n\n                /**\n                 * Autoshows / autohides both scrollbars with.\n                 * @param shallBeVisible True if the scrollbars shall be autoshown (only the case if they are hidden by a autohide), false if the shall be auto hidden.\n                 * @param delayfree True if the scrollbars shall be hidden without a delay, false or undefined otherwise.\n                 */\n                function refreshScrollbarsAutoHide(shallBeVisible, delayfree) {\n                    clearTimeout(_scrollbarsAutoHideTimeoutId);\n                    if (shallBeVisible) {\n                        //if(_hasOverflowCache.x && _hideOverflowCache.xs)\n                        removeClass(_scrollbarHorizontalElement, _classNameScrollbarAutoHidden);\n                        //if(_hasOverflowCache.y && _hideOverflowCache.ys)\n                        removeClass(_scrollbarVerticalElement, _classNameScrollbarAutoHidden);\n                    }\n                    else {\n                        var anyActive;\n                        var strActive = 'active';\n                        var hide = function () {\n                            if (!_scrollbarsHandleHovered && !_destroyed) {\n                                anyActive = _scrollbarHorizontalHandleElement.hasClass(strActive) || _scrollbarVerticalHandleElement.hasClass(strActive);\n                                if (!anyActive && (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove || _scrollbarsAutoHideLeave))\n                                    addClass(_scrollbarHorizontalElement, _classNameScrollbarAutoHidden);\n                                if (!anyActive && (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove || _scrollbarsAutoHideLeave))\n                                    addClass(_scrollbarVerticalElement, _classNameScrollbarAutoHidden);\n                            }\n                        };\n                        if (_scrollbarsAutoHideDelay > 0 && delayfree !== true)\n                            _scrollbarsAutoHideTimeoutId = setTimeout(hide, _scrollbarsAutoHideDelay);\n                        else\n                            hide();\n                    }\n                }\n\n                /**\n                 * Refreshes the handle length of the given scrollbar.\n                 * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.\n                 */\n                function refreshScrollbarHandleLength(isHorizontal) {\n                    var handleCSS = {};\n                    var scrollbarVars = getScrollbarVars(isHorizontal);\n                    var scrollbarVarsInfo = scrollbarVars._info;\n                    var digit = 1000000;\n                    //get and apply intended handle length\n                    var handleRatio = MATH.min(1, _viewportSize[scrollbarVars._w_h] / _contentScrollSizeCache[scrollbarVars._w_h]);\n                    handleCSS[scrollbarVars._width_height] = (MATH.floor(handleRatio * 100 * digit) / digit) + '%'; //the last * digit / digit is for flooring to the 4th digit\n\n                    if (!nativeOverlayScrollbarsAreActive())\n                        scrollbarVars._handle.css(handleCSS);\n\n                    //measure the handle length to respect min & max length\n                    scrollbarVarsInfo._handleLength = scrollbarVars._handle[0]['offset' + scrollbarVars._Width_Height];\n                    scrollbarVarsInfo._handleLengthRatio = handleRatio;\n                }\n\n                /**\n                 * Refreshes the handle offset of the given scrollbar.\n                 * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.\n                 * @param scrollOrTransition The scroll position of the given scrollbar axis to which the handle shall be moved or a boolean which indicates whether a transition shall be applied. If undefined or boolean if the current scroll-offset is taken. (if isHorizontal ? scrollLeft : scrollTop)\n                 */\n                function refreshScrollbarHandleOffset(isHorizontal, scrollOrTransition) {\n                    var transition = type(scrollOrTransition) == TYPES.b;\n                    var transitionDuration = 250;\n                    var isRTLisHorizontal = _isRTL && isHorizontal;\n                    var scrollbarVars = getScrollbarVars(isHorizontal);\n                    var scrollbarVarsInfo = scrollbarVars._info;\n                    var strTranslateBrace = 'translate(';\n                    var strTransform = VENDORS._cssProperty('transform');\n                    var strTransition = VENDORS._cssProperty('transition');\n                    var nativeScroll = isHorizontal ? _viewportElement[_strScrollLeft]() : _viewportElement[_strScrollTop]();\n                    var currentScroll = scrollOrTransition === undefined || transition ? nativeScroll : scrollOrTransition;\n\n                    //measure the handle length to respect min & max length\n                    var handleLength = scrollbarVarsInfo._handleLength;\n                    var trackLength = scrollbarVars._track[0]['offset' + scrollbarVars._Width_Height];\n                    var handleTrackDiff = trackLength - handleLength;\n                    var handleCSS = {};\n                    var transformOffset;\n                    var translateValue;\n\n                    //DONT use the variable '_contentScrollSizeCache[scrollbarVars._w_h]' instead of '_viewportElement[0]['scroll' + scrollbarVars._Width_Height]'\n                    // because its a bit behind during the small delay when content size updates\n                    //(delay = mutationObserverContentLag, if its 0 then this var could be used)\n                    var maxScroll = (_viewportElementNative[_strScroll + scrollbarVars._Width_Height] - _viewportElementNative['client' + scrollbarVars._Width_Height]) * (_rtlScrollBehavior.n && isRTLisHorizontal ? -1 : 1); //* -1 if rtl scroll max is negative\n                    var getScrollRatio = function (base) {\n                        return isNaN(base / maxScroll) ? 0 : MATH.max(0, MATH.min(1, base / maxScroll));\n                    };\n                    var getHandleOffset = function (scrollRatio) {\n                        var offset = handleTrackDiff * scrollRatio;\n                        offset = isNaN(offset) ? 0 : offset;\n                        offset = (isRTLisHorizontal && !_rtlScrollBehavior.i) ? (trackLength - handleLength - offset) : offset;\n                        offset = MATH.max(0, offset);\n                        return offset;\n                    };\n                    var scrollRatio = getScrollRatio(nativeScroll);\n                    var unsnappedScrollRatio = getScrollRatio(currentScroll);\n                    var handleOffset = getHandleOffset(unsnappedScrollRatio);\n                    var snappedHandleOffset = getHandleOffset(scrollRatio);\n\n                    scrollbarVarsInfo._maxScroll = maxScroll;\n                    scrollbarVarsInfo._currentScroll = nativeScroll;\n                    scrollbarVarsInfo._currentScrollRatio = scrollRatio;\n\n                    if (_supportTransform) {\n                        transformOffset = isRTLisHorizontal ? -(trackLength - handleLength - handleOffset) : handleOffset; //in px\n                        //transformOffset = (transformOffset / trackLength * 100) * (trackLength / handleLength); //in %\n                        translateValue = isHorizontal ? strTranslateBrace + transformOffset + 'px, 0)' : strTranslateBrace + '0, ' + transformOffset + 'px)';\n\n                        handleCSS[strTransform] = translateValue;\n\n                        //apply or clear up transition\n                        if (_supportTransition)\n                            handleCSS[strTransition] = transition && MATH.abs(handleOffset - scrollbarVarsInfo._handleOffset) > 1 ? getCSSTransitionString(scrollbarVars._handle) + ', ' + (strTransform + _strSpace + transitionDuration + 'ms') : _strEmpty;\n                    }\n                    else\n                        handleCSS[scrollbarVars._left_top] = handleOffset;\n\n\n                    //only apply css if offset has changed and overflow exists.\n                    if (!nativeOverlayScrollbarsAreActive()) {\n                        scrollbarVars._handle.css(handleCSS);\n\n                        //clear up transition\n                        if (_supportTransform && _supportTransition && transition) {\n                            scrollbarVars._handle.one(_strTransitionEndEvent, function () {\n                                if (!_destroyed)\n                                    scrollbarVars._handle.css(strTransition, _strEmpty);\n                            });\n                        }\n                    }\n\n                    scrollbarVarsInfo._handleOffset = handleOffset;\n                    scrollbarVarsInfo._snappedHandleOffset = snappedHandleOffset;\n                    scrollbarVarsInfo._trackLength = trackLength;\n                }\n\n                /**\n                 * Refreshes the interactivity of the given scrollbar element.\n                 * @param isTrack True if the track element is the target, false if the handle element is the target.\n                 * @param value True for interactivity false for no interactivity.\n                 */\n                function refreshScrollbarsInteractive(isTrack, value) {\n                    var action = value ? 'removeClass' : 'addClass';\n                    var element1 = isTrack ? _scrollbarHorizontalTrackElement : _scrollbarHorizontalHandleElement;\n                    var element2 = isTrack ? _scrollbarVerticalTrackElement : _scrollbarVerticalHandleElement;\n                    var className = isTrack ? _classNameScrollbarTrackOff : _classNameScrollbarHandleOff;\n\n                    element1[action](className);\n                    element2[action](className);\n                }\n\n                /**\n                 * Returns a object which is used for fast access for specific variables.\n                 * @param isHorizontal True if the horizontal scrollbar vars shall be accessed, false if the vertical scrollbar vars shall be accessed.\n                 * @returns {{wh: string, WH: string, lt: string, _wh: string, _lt: string, t: *, h: *, c: {}, s: *}}\n                 */\n                function getScrollbarVars(isHorizontal) {\n                    return {\n                        _width_height: isHorizontal ? _strWidth : _strHeight,\n                        _Width_Height: isHorizontal ? 'Width' : 'Height',\n                        _left_top: isHorizontal ? _strLeft : _strTop,\n                        _Left_Top: isHorizontal ? 'Left' : 'Top',\n                        _x_y: isHorizontal ? _strX : _strY,\n                        _X_Y: isHorizontal ? 'X' : 'Y',\n                        _w_h: isHorizontal ? 'w' : 'h',\n                        _l_t: isHorizontal ? 'l' : 't',\n                        _track: isHorizontal ? _scrollbarHorizontalTrackElement : _scrollbarVerticalTrackElement,\n                        _handle: isHorizontal ? _scrollbarHorizontalHandleElement : _scrollbarVerticalHandleElement,\n                        _scrollbar: isHorizontal ? _scrollbarHorizontalElement : _scrollbarVerticalElement,\n                        _info: isHorizontal ? _scrollHorizontalInfo : _scrollVerticalInfo\n                    };\n                }\n\n\n                //==== Scrollbar Corner ====//\n\n                /**\n                 * Builds or destroys the scrollbar corner DOM element.\n                 * @param destroy Indicates whether the DOM shall be build or destroyed.\n                 */\n                function setupScrollbarCornerDOM(destroy) {\n                    _scrollbarCornerElement = _scrollbarCornerElement || selectOrGenerateDivByClass(_classNameScrollbarCorner, true);\n\n                    if (!destroy) {\n                        if (!_domExists) {\n                            _hostElement.append(_scrollbarCornerElement);\n                        }\n                    }\n                    else {\n                        if (_domExists && _initialized) {\n                            removeClass(_scrollbarCornerElement.removeAttr(LEXICON.s), _classNamesDynamicDestroy);\n                        }\n                        else {\n                            remove(_scrollbarCornerElement);\n                        }\n                    }\n                }\n\n                /**\n                 * Initializes all scrollbar corner interactivity events.\n                 */\n                function setupScrollbarCornerEvents() {\n                    var insideIFrame = _windowElementNative.top !== _windowElementNative;\n                    var mouseDownPosition = {};\n                    var mouseDownSize = {};\n                    var mouseDownInvertedScale = {};\n                    var reconnectMutationObserver;\n\n                    function documentDragMove(event) {\n                        if (onMouseTouchDownContinue(event)) {\n                            var pageOffset = getCoordinates(event);\n                            var hostElementCSS = {};\n                            if (_resizeHorizontal || _resizeBoth)\n                                hostElementCSS[_strWidth] = (mouseDownSize.w + (pageOffset.x - mouseDownPosition.x) * mouseDownInvertedScale.x);\n                            if (_resizeVertical || _resizeBoth)\n                                hostElementCSS[_strHeight] = (mouseDownSize.h + (pageOffset.y - mouseDownPosition.y) * mouseDownInvertedScale.y);\n                            _hostElement.css(hostElementCSS);\n                            COMPATIBILITY.stpP(event);\n                        }\n                        else {\n                            documentMouseTouchUp(event);\n                        }\n                    }\n                    function documentMouseTouchUp(event) {\n                        var eventIsTrusted = event !== undefined;\n\n                        setupResponsiveEventListener(_documentElement,\n                            [_strSelectStartEvent, _strMouseTouchMoveEvent, _strMouseTouchUpEvent],\n                            [documentOnSelectStart, documentDragMove, documentMouseTouchUp],\n                            true);\n\n                        removeClass(_bodyElement, _classNameDragging);\n                        if (_scrollbarCornerElement.releaseCapture)\n                            _scrollbarCornerElement.releaseCapture();\n\n                        if (eventIsTrusted) {\n                            if (reconnectMutationObserver)\n                                connectMutationObservers();\n                            _base.update(_strAuto);\n                        }\n                        reconnectMutationObserver = false;\n                    }\n                    function onMouseTouchDownContinue(event) {\n                        var originalEvent = event.originalEvent || event;\n                        var isTouchEvent = originalEvent.touches !== undefined;\n                        return _sleeping || _destroyed ? false : COMPATIBILITY.mBtn(event) === 1 || isTouchEvent;\n                    }\n                    function getCoordinates(event) {\n                        return _msieVersion && insideIFrame ? { x: event.screenX, y: event.screenY } : COMPATIBILITY.page(event);\n                    }\n\n                    addDestroyEventListener(_scrollbarCornerElement, _strMouseTouchDownEvent, function (event) {\n                        if (onMouseTouchDownContinue(event) && !_resizeNone) {\n                            if (_mutationObserversConnected) {\n                                reconnectMutationObserver = true;\n                                disconnectMutationObservers();\n                            }\n\n                            mouseDownPosition = getCoordinates(event);\n\n                            mouseDownSize.w = _hostElementNative[LEXICON.oW] - (!_isBorderBox ? _paddingX : 0);\n                            mouseDownSize.h = _hostElementNative[LEXICON.oH] - (!_isBorderBox ? _paddingY : 0);\n                            mouseDownInvertedScale = getHostElementInvertedScale();\n\n                            setupResponsiveEventListener(_documentElement,\n                                [_strSelectStartEvent, _strMouseTouchMoveEvent, _strMouseTouchUpEvent],\n                                [documentOnSelectStart, documentDragMove, documentMouseTouchUp]);\n\n                            addClass(_bodyElement, _classNameDragging);\n                            if (_scrollbarCornerElement.setCapture)\n                                _scrollbarCornerElement.setCapture();\n\n                            COMPATIBILITY.prvD(event);\n                            COMPATIBILITY.stpP(event);\n                        }\n                    });\n                }\n\n\n                //==== Utils ====//\n\n                /**\n                 * Calls the callback with the given name. The Context of this callback is always _base (this).\n                 * @param name The name of the target which shall be called.\n                 * @param args The args with which the callback shall be called.\n                 * @param dependent Boolean which decides whether the callback shall be fired, undefined is like a \"true\" value.\n                 */\n                function dispatchCallback(name, args, dependent) {\n                    if (dependent === false)\n                        return;\n                    if (_initialized) {\n                        var callback = _currentPreparedOptions.callbacks[name];\n                        var extensionOnName = name;\n                        var ext;\n\n                        if (extensionOnName.substr(0, 2) === 'on')\n                            extensionOnName = extensionOnName.substr(2, 1).toLowerCase() + extensionOnName.substr(3);\n\n                        if (type(callback) == TYPES.f)\n                            callback.call(_base, args);\n\n                        each(_extensions, function () {\n                            ext = this;\n                            if (type(ext.on) == TYPES.f)\n                                ext.on(extensionOnName, args);\n                        });\n                    }\n                    else if (!_destroyed)\n                        _callbacksInitQeueue.push({ n: name, a: args });\n                }\n\n                /**\n                 * Sets the \"top, right, bottom, left\" properties, with a given prefix, of the given css object.\n                 * @param targetCSSObject The css object to which the values shall be applied.\n                 * @param prefix The prefix of the \"top, right, bottom, left\" css properties. (example: 'padding-' is a valid prefix)\n                 * @param values A array of values which shall be applied to the \"top, right, bottom, left\" -properties. The array order is [top, right, bottom, left].\n                 * If this argument is undefined the value '' (empty string) will be applied to all properties.\n                 */\n                function setTopRightBottomLeft(targetCSSObject, prefix, values) {\n                    prefix = prefix || _strEmpty;\n                    values = values || [_strEmpty, _strEmpty, _strEmpty, _strEmpty];\n\n                    targetCSSObject[prefix + _strTop] = values[0];\n                    targetCSSObject[prefix + _strRight] = values[1];\n                    targetCSSObject[prefix + _strBottom] = values[2];\n                    targetCSSObject[prefix + _strLeft] = values[3];\n                }\n\n                /**\n                 * Gets the \"top, right, bottom, left\" CSS properties of the CSS property with the given prefix from the host element.\n                 * @param prefix The prefix of the \"top, right, bottom, left\" css properties. (example: 'padding-' is a valid prefix)\n                 * @param suffix The suffix of the \"top, right, bottom, left\" css properties. (example: 'border-' is a valid prefix with '-width' is a valid suffix)\n                 * @param zeroX True if the x axis shall be 0.\n                 * @param zeroY True if the y axis shall be 0.\n                 * @returns {{}} The object which contains the numbers of the read CSS properties.\n                 */\n                function getTopRightBottomLeftHost(prefix, suffix, zeroX, zeroY) {\n                    suffix = suffix || _strEmpty;\n                    prefix = prefix || _strEmpty;\n                    return {\n                        t: zeroY ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strTop + suffix)),\n                        r: zeroX ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strRight + suffix)),\n                        b: zeroY ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strBottom + suffix)),\n                        l: zeroX ? 0 : parseToZeroOrNumber(_hostElement.css(prefix + _strLeft + suffix))\n                    };\n                }\n\n                /**\n                 * Returns the computed CSS transition string from the given element.\n                 * @param element The element from which the transition string shall be returned.\n                 * @returns {string} The CSS transition string from the given element.\n                 */\n                function getCSSTransitionString(element) {\n                    var transitionStr = VENDORS._cssProperty('transition');\n                    var assembledValue = element.css(transitionStr);\n                    if (assembledValue)\n                        return assembledValue;\n                    var regExpString = '\\\\s*(' + '([^,(]+(\\\\(.+?\\\\))?)+' + ')[\\\\s,]*';\n                    var regExpMain = new RegExp(regExpString);\n                    var regExpValidate = new RegExp('^(' + regExpString + ')+$');\n                    var properties = 'property duration timing-function delay'.split(' ');\n                    var result = [];\n                    var strResult;\n                    var valueArray;\n                    var i = 0;\n                    var j;\n                    var splitCssStyleByComma = function (str) {\n                        strResult = [];\n                        if (!str.match(regExpValidate))\n                            return str;\n                        while (str.match(regExpMain)) {\n                            strResult.push(RegExp.$1);\n                            str = str.replace(regExpMain, _strEmpty);\n                        }\n\n                        return strResult;\n                    };\n                    for (; i < properties[LEXICON.l]; i++) {\n                        valueArray = splitCssStyleByComma(element.css(transitionStr + '-' + properties[i]));\n                        for (j = 0; j < valueArray[LEXICON.l]; j++)\n                            result[j] = (result[j] ? result[j] + _strSpace : _strEmpty) + valueArray[j];\n                    }\n                    return result.join(', ');\n                }\n\n                /**\n                 * Generates a Regular Expression which matches with a string which starts with 'os-host'.\n                 * @param {boolean} withCurrClassNameOption The Regular Expression also matches if the string is the current ClassName option (multiple values splitted by space possible).\n                 * @param {boolean} withOldClassNameOption The Regular Expression also matches if the string is the old ClassName option (multiple values splitted by space possible).\n                 */\n                function createHostClassNameRegExp(withCurrClassNameOption, withOldClassNameOption) {\n                    var i;\n                    var split;\n                    var appendix;\n                    var appendClasses = function (classes, condition) {\n                        appendix = '';\n                        if (condition && typeof classes == TYPES.s) {\n                            split = classes.split(_strSpace);\n                            for (i = 0; i < split[LEXICON.l]; i++)\n                                appendix += '|' + split[i] + '$';\n                            // split[i].replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&') for escaping regex characters\n                        }\n                        return appendix;\n                    };\n\n                    return new RegExp(\n                        '(^' + _classNameHostElement + '([-_].+|)$)' +\n                        appendClasses(_classNameCache, withCurrClassNameOption) +\n                        appendClasses(_oldClassName, withOldClassNameOption), 'g');\n                }\n\n                /**\n                 * Calculates the host-elements inverted scale. (invertedScale = 1 / scale)\n                 * @returns {{x: number, y: number}} The scale of the host-element.\n                 */\n                function getHostElementInvertedScale() {\n                    var rect = _paddingElementNative[LEXICON.bCR]();\n                    return {\n                        x: _supportTransform ? 1 / (MATH.round(rect.width) / _paddingElementNative[LEXICON.oW]) || 1 : 1,\n                        y: _supportTransform ? 1 / (MATH.round(rect.height) / _paddingElementNative[LEXICON.oH]) || 1 : 1\n                    };\n                }\n\n                /**\n                 * Checks whether the given object is a HTMLElement.\n                 * @param o The object which shall be checked.\n                 * @returns {boolean} True the given object is a HTMLElement, false otherwise.\n                 */\n                function isHTMLElement(o) {\n                    var strOwnerDocument = 'ownerDocument';\n                    var strHTMLElement = 'HTMLElement';\n                    var wnd = o && o[strOwnerDocument] ? (o[strOwnerDocument].parentWindow || window) : window;\n                    return (\n                        typeof wnd[strHTMLElement] == TYPES.o ? o instanceof wnd[strHTMLElement] : //DOM2\n                            o && typeof o == TYPES.o && o !== null && o.nodeType === 1 && typeof o.nodeName == TYPES.s\n                    );\n                }\n\n                /**\n                 * Compares 2 arrays and returns the differences between them as a array.\n                 * @param a1 The first array which shall be compared.\n                 * @param a2 The second array which shall be compared.\n                 * @returns {Array} The differences between the two arrays.\n                 */\n                function getArrayDifferences(a1, a2) {\n                    var a = [];\n                    var diff = [];\n                    var i;\n                    var k;\n                    for (i = 0; i < a1.length; i++)\n                        a[a1[i]] = true;\n                    for (i = 0; i < a2.length; i++) {\n                        if (a[a2[i]])\n                            delete a[a2[i]];\n                        else\n                            a[a2[i]] = true;\n                    }\n                    for (k in a)\n                        diff.push(k);\n                    return diff;\n                }\n\n                /**\n                 * Returns Zero or the number to which the value can be parsed.\n                 * @param value The value which shall be parsed.\n                 * @param toFloat Indicates whether the number shall be parsed to a float.\n                 */\n                function parseToZeroOrNumber(value, toFloat) {\n                    var num = toFloat ? parseFloat(value) : parseInt(value, 10);\n                    return isNaN(num) ? 0 : num;\n                }\n\n                /**\n                 * Gets several information of the textarea and returns them as a object or undefined if the browser doesn't support it.\n                 * @returns {{cursorRow: Number, cursorCol, rows: Number, cols: number, wRow: number, pos: number, max : number}} or undefined if not supported.\n                 */\n                function getTextareaInfo() {\n                    //read needed values\n                    var textareaCursorPosition = _targetElementNative.selectionStart;\n                    if (textareaCursorPosition === undefined)\n                        return;\n\n                    var textareaValue = _targetElement.val();\n                    var textareaLength = textareaValue[LEXICON.l];\n                    var textareaRowSplit = textareaValue.split('\\n');\n                    var textareaLastRow = textareaRowSplit[LEXICON.l];\n                    var textareaCurrentCursorRowSplit = textareaValue.substr(0, textareaCursorPosition).split('\\n');\n                    var widestRow = 0;\n                    var textareaLastCol = 0;\n                    var cursorRow = textareaCurrentCursorRowSplit[LEXICON.l];\n                    var cursorCol = textareaCurrentCursorRowSplit[textareaCurrentCursorRowSplit[LEXICON.l] - 1][LEXICON.l];\n                    var rowCols;\n                    var i;\n\n                    //get widest Row and the last column of the textarea\n                    for (i = 0; i < textareaRowSplit[LEXICON.l]; i++) {\n                        rowCols = textareaRowSplit[i][LEXICON.l];\n                        if (rowCols > textareaLastCol) {\n                            widestRow = i + 1;\n                            textareaLastCol = rowCols;\n                        }\n                    }\n\n                    return {\n                        _cursorRow: cursorRow, //cursorRow\n                        _cursorColumn: cursorCol, //cursorCol\n                        _rows: textareaLastRow, //rows\n                        _columns: textareaLastCol, //cols\n                        _widestRow: widestRow, //wRow\n                        _cursorPosition: textareaCursorPosition, //pos\n                        _cursorMax: textareaLength //max\n                    };\n                }\n\n                /**\n                 * Determines whether native overlay scrollbars are active.\n                 * @returns {boolean} True if native overlay scrollbars are active, false otherwise.\n                 */\n                function nativeOverlayScrollbarsAreActive() {\n                    return (_ignoreOverlayScrollbarHidingCache && (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y));\n                }\n\n                /**\n                 * Gets the element which is used to measure the content size.\n                 * @returns {*} TextareaCover if target element is textarea else the ContentElement.\n                 */\n                function getContentMeasureElement() {\n                    return _isTextarea ? _textareaCoverElement[0] : _contentElementNative;\n                }\n\n                /**\n                 * Generates a string which represents a HTML div with the given classes or attributes.\n                 * @param classesOrAttrs The class of the div as string or a object which represents the attributes of the div. (The class attribute can also be written as \"className\".)\n                 * @param content The content of the div as string.\n                 * @returns {string} The concated string which represents a HTML div and its content.\n                 */\n                function generateDiv(classesOrAttrs, content) {\n                    return '<div ' + (classesOrAttrs ? type(classesOrAttrs) == TYPES.s ?\n                        'class=\"' + classesOrAttrs + '\"' :\n                        (function () {\n                            var key;\n                            var attrs = _strEmpty;\n                            if (FRAMEWORK.isPlainObject(classesOrAttrs)) {\n                                for (key in classesOrAttrs)\n                                    attrs += (key === 'c' ? 'class' : key) + '=\"' + classesOrAttrs[key] + '\" ';\n                            }\n                            return attrs;\n                        })() :\n                        _strEmpty) +\n                        '>' +\n                        (content || _strEmpty) +\n                        '</div>';\n                }\n\n                /**\n                 * Selects or generates a div with the given class attribute.\n                 * @param className The class names (divided by spaces) of the div which shall be selected or generated.\n                 * @param selectParentOrOnlyChildren The parent element from which of the element shall be selected. (if undefined or boolean its hostElement)\n                 * If its a boolean it decides whether only the children of the host element shall be selected.\n                 * @returns {*} The generated or selected element.\n                 */\n                function selectOrGenerateDivByClass(className, selectParentOrOnlyChildren) {\n                    var onlyChildren = type(selectParentOrOnlyChildren) == TYPES.b;\n                    var selectParent = onlyChildren ? _hostElement : (selectParentOrOnlyChildren || _hostElement);\n\n                    return (_domExists && !selectParent[LEXICON.l])\n                        ? null\n                        : _domExists\n                            ? selectParent[onlyChildren ? 'children' : 'find'](_strDot + className.replace(/\\s/g, _strDot)).eq(0)\n                            : FRAMEWORK(generateDiv(className))\n                }\n\n                /**\n                 * Gets the value of the given property from the given object.\n                 * @param obj The object from which the property value shall be got.\n                 * @param path The property of which the value shall be got.\n                 * @returns {*} Returns the value of the searched property or undefined of the property wasn't found.\n                 */\n                function getObjectPropVal(obj, path) {\n                    var splits = path.split(_strDot);\n                    var i = 0;\n                    var val;\n                    for (; i < splits.length; i++) {\n                        if (!obj[LEXICON.hOP](splits[i]))\n                            return;\n                        val = obj[splits[i]];\n                        if (i < splits.length && type(val) == TYPES.o)\n                            obj = val;\n                    }\n                    return val;\n                }\n\n                /**\n                 * Sets the value of the given property from the given object.\n                 * @param obj The object from which the property value shall be set.\n                 * @param path The property of which the value shall be set.\n                 * @param val The value of the property which shall be set.\n                 */\n                function setObjectPropVal(obj, path, val) {\n                    var splits = path.split(_strDot);\n                    var splitsLength = splits.length;\n                    var i = 0;\n                    var extendObj = {};\n                    var extendObjRoot = extendObj;\n                    for (; i < splitsLength; i++)\n                        extendObj = extendObj[splits[i]] = i + 1 < splitsLength ? {} : val;\n                    FRAMEWORK.extend(obj, extendObjRoot, true);\n                }\n\n                /**\t\n                 * Runs a action for each selector inside the updateOnLoad option.\t\n                 * @param {Function} action The action for each updateOnLoad selector, the arguments the function takes is the index and the value (the selector).\t\n                 */\n                function eachUpdateOnLoad(action) {\n                    var updateOnLoad = _currentPreparedOptions.updateOnLoad;\n                    updateOnLoad = type(updateOnLoad) == TYPES.s ? updateOnLoad.split(_strSpace) : updateOnLoad;\n\n                    if (COMPATIBILITY.isA(updateOnLoad) && !_destroyed) {\n                        each(updateOnLoad, action);\n                    }\n                }\n\n\n                //==== Utils Cache ====//\n\n                /**\n                 * Compares two values or objects and returns true if they aren't equal.\n                 * @param current The first value or object which shall be compared.\n                 * @param cache The second value or object which shall be compared.\n                 * @param force If true the returned value is always true.\n                 * @returns {boolean} True if both values or objects aren't equal or force is true, false otherwise.\n                 */\n                function checkCache(current, cache, force) {\n                    if (force)\n                        return force;\n                    if (type(current) == TYPES.o && type(cache) == TYPES.o) {\n                        for (var prop in current) {\n                            if (prop !== 'c') {\n                                if (current[LEXICON.hOP](prop) && cache[LEXICON.hOP](prop)) {\n                                    if (checkCache(current[prop], cache[prop]))\n                                        return true;\n                                }\n                                else {\n                                    return true;\n                                }\n                            }\n                        }\n                    }\n                    else {\n                        return current !== cache;\n                    }\n                    return false;\n                }\n\n\n                //==== Shortcuts ====//\n\n                /**\n                 * jQuery extend method shortcut with a appended \"true\" as first argument.\n                 */\n                function extendDeep() {\n                    return FRAMEWORK.extend.apply(this, [true].concat([].slice.call(arguments)));\n                }\n\n                /**\n                 * jQuery addClass method shortcut.\n                 */\n                function addClass(el, classes) {\n                    return _frameworkProto.addClass.call(el, classes);\n                }\n\n                /**\n                 * jQuery removeClass method shortcut.\n                 */\n                function removeClass(el, classes) {\n                    return _frameworkProto.removeClass.call(el, classes);\n                }\n\n                /**\n                 * Adds or removes the given classes dependent on the boolean value. True for add, false for remove.\n                 */\n                function addRemoveClass(el, classes, doAdd) {\n                    return doAdd ? addClass(el, classes) : removeClass(el, classes);\n                }\n\n                /**\n                 * jQuery remove method shortcut.\n                 */\n                function remove(el) {\n                    return _frameworkProto.remove.call(el);\n                }\n\n                /**\n                 * Finds the first child element with the given selector of the given element.\n                 * @param el The root element from which the selector shall be valid.\n                 * @param selector The selector of the searched element.\n                 * @returns {*} The first element which is a child of the given element and matches the givens selector.\n                 */\n                function findFirst(el, selector) {\n                    return _frameworkProto.find.call(el, selector).eq(0);\n                }\n\n\n                //==== API ====//\n\n                /**\n                 * Puts the instance to sleep. It wont respond to any changes in the DOM and won't update. Scrollbar Interactivity is also disabled as well as the resize handle.\n                 * This behavior can be reset by calling the update method.\n                 */\n                _base.sleep = function () {\n                    _sleeping = true;\n                };\n\n                /**\n                 * Updates the plugin and DOM to the current options.\n                 * This method should only be called if a update is 100% required.\n                 * @param force True if every property shall be updated and the cache shall be ignored.\n                 * !INTERNAL USAGE! : force can be a string \"auto\", \"sync\" or \"zoom\" too\n                 * if \"auto\" then before a real update the content size and host element attributes gets checked, and if they changed only then the update method will be called.\n                 * if \"sync\" then the async update process (MutationObserver or UpdateLoop) gets synchronized and a corresponding update takes place if one was needed due to pending changes.\n                 * if \"zoom\" then a update takes place where it's assumed that content and host size changed\n                 * @returns {boolean|undefined} \n                 * If force is \"sync\" then a boolean is returned which indicates whether a update was needed due to pending changes.\n                 * If force is \"auto\" then a boolean is returned whether a update was needed due to attribute or size changes.\n                 * undefined otherwise.\n                 */\n                _base.update = function (force) {\n                    if (_destroyed)\n                        return;\n\n                    var attrsChanged;\n                    var contentSizeC;\n                    var isString = type(force) == TYPES.s;\n                    var doUpdateAuto;\n                    var mutHost;\n                    var mutContent;\n\n                    if (isString) {\n                        if (force === _strAuto) {\n                            attrsChanged = meaningfulAttrsChanged();\n                            contentSizeC = updateAutoContentSizeChanged();\n                            doUpdateAuto = attrsChanged || contentSizeC;\n                            if (doUpdateAuto) {\n                                update({\n                                    _contentSizeChanged: contentSizeC,\n                                    _changedOptions: _initialized ? undefined : _currentPreparedOptions\n                                });\n                            }\n                        }\n                        else if (force === _strSync) {\n                            if (_mutationObserversConnected) {\n                                mutHost = _mutationObserverHostCallback(_mutationObserverHost.takeRecords());\n                                mutContent = _mutationObserverContentCallback(_mutationObserverContent.takeRecords());\n                            }\n                            else {\n                                mutHost = _base.update(_strAuto);\n                            }\n                        }\n                        else if (force === 'zoom') {\n                            update({\n                                _hostSizeChanged: true,\n                                _contentSizeChanged: true\n                            });\n                        }\n                    }\n                    else {\n                        force = _sleeping || force;\n                        _sleeping = false;\n                        if (!_base.update(_strSync) || force)\n                            update({ _force: force });\n                    }\n\n                    updateElementsOnLoad();\n\n                    return doUpdateAuto || mutHost || mutContent;\n                };\n\n                /**\n                 Gets or sets the current options. The update method will be called automatically if new options were set.\n                 * @param newOptions If new options are given, then the new options will be set, if new options aren't given (undefined or a not a plain object) then the current options will be returned.\n                 * @param value If new options is a property path string, then this value will be used to set the option to which the property path string leads.\n                 * @returns {*}\n                 */\n                _base.options = function (newOptions, value) {\n                    var option = {};\n                    var changedOps;\n\n                    //return current options if newOptions are undefined or empty\n                    if (FRAMEWORK.isEmptyObject(newOptions) || !FRAMEWORK.isPlainObject(newOptions)) {\n                        if (type(newOptions) == TYPES.s) {\n                            if (arguments.length > 1) {\n                                setObjectPropVal(option, newOptions, value);\n                                changedOps = setOptions(option);\n                            }\n                            else\n                                return getObjectPropVal(_currentOptions, newOptions);\n                        }\n                        else\n                            return _currentOptions;\n                    }\n                    else {\n                        changedOps = setOptions(newOptions);\n                    }\n\n                    if (!FRAMEWORK.isEmptyObject(changedOps)) {\n                        update({ _changedOptions: changedOps });\n                    }\n                };\n\n                /**\n                 * Restore the DOM, disconnects all observers, remove all resize observers and put the instance to sleep.\n                 */\n                _base.destroy = function () {\n                    if (_destroyed)\n                        return;\n\n                    //remove this instance from auto update loop\n                    autoUpdateLoop.remove(_base);\n\n                    //disconnect all mutation observers\n                    disconnectMutationObservers();\n\n                    //remove all resize observers\n                    setupResizeObserver(_sizeObserverElement);\n                    setupResizeObserver(_sizeAutoObserverElement);\n\n                    //remove all extensions\n                    for (var extName in _extensions)\n                        _base.removeExt(extName);\n\n                    //remove all 'destroy' events\n                    while (_destroyEvents[LEXICON.l] > 0)\n                        _destroyEvents.pop()();\n\n                    //remove all events from host element\n                    setupHostMouseTouchEvents(true);\n\n                    //remove all helper / detection elements\n                    if (_contentGlueElement)\n                        remove(_contentGlueElement);\n                    if (_contentArrangeElement)\n                        remove(_contentArrangeElement);\n                    if (_sizeAutoObserverAdded)\n                        remove(_sizeAutoObserverElement);\n\n                    //remove all generated DOM\n                    setupScrollbarsDOM(true);\n                    setupScrollbarCornerDOM(true);\n                    setupStructureDOM(true);\n\n                    //remove all generated image load events\n                    for (var i = 0; i < _updateOnLoadElms[LEXICON.l]; i++)\n                        FRAMEWORK(_updateOnLoadElms[i]).off(_updateOnLoadEventName, updateOnLoadCallback);\n                    _updateOnLoadElms = undefined;\n\n                    _destroyed = true;\n                    _sleeping = true;\n\n                    //remove this instance from the instances list\n                    INSTANCES(pluginTargetElement, 0);\n                    dispatchCallback('onDestroyed');\n\n                    //remove all properties and methods\n                    //for (var property in _base)\n                    //    delete _base[property];\n                    //_base = undefined;\n                };\n\n                /**\n                 * Scrolls to a given position or element.\n                 * @param coordinates\n                 * 1. Can be \"coordinates\" which looks like:\n                 *    { x : ?, y : ? } OR          Object with x and y properties\n                 *    { left : ?, top : ? } OR     Object with left and top properties\n                 *    { l : ?, t : ? } OR          Object with l and t properties\n                 *    [ ?, ? ] OR                  Array where the first two element are the coordinates (first is x, second is y)\n                 *    ?                            A single value which stays for both axis\n                 *    A value can be a number, a string or a calculation.\n                 *\n                 *    Operators:\n                 *    [NONE]  The current scroll will be overwritten by the value.\n                 *    '+='    The value will be added to the current scroll offset\n                 *    '-='    The value will be subtracted from the current scroll offset\n                 *    '*='    The current scroll wil be multiplicated by the value.\n                 *    '/='    The current scroll wil be divided by the value.\n                 *\n                 *    Units:\n                 *    [NONE]  The value is the final scroll amount.                   final = (value * 1)\n                 *    'px'    Same as none\n                 *    '%'     The value is dependent on the current scroll value.     final = ((currentScrollValue / 100) * value)\n                 *    'vw'    The value is multiplicated by the viewport width.       final = (value * viewportWidth)\n                 *    'vh'    The value is multiplicated by the viewport height.      final = (value * viewportHeight)\n                 *\n                 *    example final values:\n                 *    200, '200px', '50%', '1vw', '1vh', '+=200', '/=1vw', '*=2px', '-=5vh', '+=33%', '+= 50% - 2px', '-= 1vw - 50%'\n                 *\n                 * 2. Can be a HTML or jQuery element:\n                 *    The final scroll offset is the offset (without margin) of the given HTML / jQuery element.\n                 *\n                 * 3. Can be a object with a HTML or jQuery element with additional settings:\n                 *    {\n                 *      el : [HTMLElement, jQuery element],             MUST be specified, else this object isn't valid.\n                 *      scroll : [string, array, object],               Default value is 'always'.\n                 *      block : [string, array, object],                Default value is 'begin'.\n                 *      margin : [number, boolean, array, object]       Default value is false.\n                 *    }\n                 *\n                 *    Possible scroll settings are:\n                 *    'always'      Scrolls always.\n                 *    'ifneeded'    Scrolls only if the element isnt fully in view.\n                 *    'never'       Scrolls never.\n                 *\n                 *    Possible block settings are:\n                 *    'begin'   Both axis shall be docked to the \"begin\" edge. - The element will be docked to the top and left edge of the viewport.\n                 *    'end'     Both axis shall be docked to the \"end\" edge. - The element will be docked to the bottom and right edge of the viewport. (If direction is RTL to the bottom and left edge.)\n                 *    'center'  Both axis shall be docked to \"center\". - The element will be centered in the viewport.\n                 *    'nearest' The element will be docked to the nearest edge(s).\n                 *\n                 *    Possible margin settings are: -- The actual margin of the element wont be affect, this option affects only the final scroll offset.\n                 *    [BOOLEAN]                                         If true the css margin of the element will be used, if false no margin will be used.\n                 *    [NUMBER]                                          The margin will be used for all edges.\n                 *\n                 * @param duration The duration of the scroll animation, OR a jQuery animation configuration object.\n                 * @param easing The animation easing.\n                 * @param complete The animation complete callback.\n                 * @returns {{\n                 *   position: {x: number, y: number},\n                 *   ratio: {x: number, y: number},\n                 *   max: {x: number, y: number},\n                 *   handleOffset: {x: number, y: number},\n                 *   handleLength: {x: number, y: number},\n                 *   handleLengthRatio: {x: number, y: number}, t\n                 *   rackLength: {x: number, y: number},\n                 *   isRTL: boolean,\n                 *   isRTLNormalized: boolean\n                 *  }}\n                 */\n                _base.scroll = function (coordinates, duration, easing, complete) {\n                    if (arguments.length === 0 || coordinates === undefined) {\n                        var infoX = _scrollHorizontalInfo;\n                        var infoY = _scrollVerticalInfo;\n                        var normalizeInvert = _normalizeRTLCache && _isRTL && _rtlScrollBehavior.i;\n                        var normalizeNegate = _normalizeRTLCache && _isRTL && _rtlScrollBehavior.n;\n                        var scrollX = infoX._currentScroll;\n                        var scrollXRatio = infoX._currentScrollRatio;\n                        var maxScrollX = infoX._maxScroll;\n                        scrollXRatio = normalizeInvert ? 1 - scrollXRatio : scrollXRatio;\n                        scrollX = normalizeInvert ? maxScrollX - scrollX : scrollX;\n                        scrollX *= normalizeNegate ? -1 : 1;\n                        maxScrollX *= normalizeNegate ? -1 : 1;\n\n                        return {\n                            position: {\n                                x: scrollX,\n                                y: infoY._currentScroll\n                            },\n                            ratio: {\n                                x: scrollXRatio,\n                                y: infoY._currentScrollRatio\n                            },\n                            max: {\n                                x: maxScrollX,\n                                y: infoY._maxScroll\n                            },\n                            handleOffset: {\n                                x: infoX._handleOffset,\n                                y: infoY._handleOffset\n                            },\n                            handleLength: {\n                                x: infoX._handleLength,\n                                y: infoY._handleLength\n                            },\n                            handleLengthRatio: {\n                                x: infoX._handleLengthRatio,\n                                y: infoY._handleLengthRatio\n                            },\n                            trackLength: {\n                                x: infoX._trackLength,\n                                y: infoY._trackLength\n                            },\n                            snappedHandleOffset: {\n                                x: infoX._snappedHandleOffset,\n                                y: infoY._snappedHandleOffset\n                            },\n                            isRTL: _isRTL,\n                            isRTLNormalized: _normalizeRTLCache\n                        };\n                    }\n\n                    _base.update(_strSync);\n\n                    var normalizeRTL = _normalizeRTLCache;\n                    var coordinatesXAxisProps = [_strX, _strLeft, 'l'];\n                    var coordinatesYAxisProps = [_strY, _strTop, 't'];\n                    var coordinatesOperators = ['+=', '-=', '*=', '/='];\n                    var durationIsObject = type(duration) == TYPES.o;\n                    var completeCallback = durationIsObject ? duration.complete : complete;\n                    var i;\n                    var finalScroll = {};\n                    var specialEasing = {};\n                    var doScrollLeft;\n                    var doScrollTop;\n                    var animationOptions;\n                    var strEnd = 'end';\n                    var strBegin = 'begin';\n                    var strCenter = 'center';\n                    var strNearest = 'nearest';\n                    var strAlways = 'always';\n                    var strNever = 'never';\n                    var strIfNeeded = 'ifneeded';\n                    var strLength = LEXICON.l;\n                    var settingsAxis;\n                    var settingsScroll;\n                    var settingsBlock;\n                    var settingsMargin;\n                    var finalElement;\n                    var elementObjSettingsAxisValues = [_strX, _strY, 'xy', 'yx'];\n                    var elementObjSettingsBlockValues = [strBegin, strEnd, strCenter, strNearest];\n                    var elementObjSettingsScrollValues = [strAlways, strNever, strIfNeeded];\n                    var coordinatesIsElementObj = coordinates[LEXICON.hOP]('el');\n                    var possibleElement = coordinatesIsElementObj ? coordinates.el : coordinates;\n                    var possibleElementIsJQuery = possibleElement instanceof FRAMEWORK || JQUERY ? possibleElement instanceof JQUERY : false;\n                    var possibleElementIsHTMLElement = possibleElementIsJQuery ? false : isHTMLElement(possibleElement);\n                    var updateScrollbarInfos = function () {\n                        if (doScrollLeft)\n                            refreshScrollbarHandleOffset(true);\n                        if (doScrollTop)\n                            refreshScrollbarHandleOffset(false);\n                    };\n                    var proxyCompleteCallback = type(completeCallback) != TYPES.f ? undefined : function () {\n                        updateScrollbarInfos();\n                        completeCallback();\n                    };\n                    function checkSettingsStringValue(currValue, allowedValues) {\n                        for (i = 0; i < allowedValues[strLength]; i++) {\n                            if (currValue === allowedValues[i])\n                                return true;\n                        }\n                        return false;\n                    }\n                    function getRawScroll(isX, coordinates) {\n                        var coordinateProps = isX ? coordinatesXAxisProps : coordinatesYAxisProps;\n                        coordinates = type(coordinates) == TYPES.s || type(coordinates) == TYPES.n ? [coordinates, coordinates] : coordinates;\n\n                        if (COMPATIBILITY.isA(coordinates))\n                            return isX ? coordinates[0] : coordinates[1];\n                        else if (type(coordinates) == TYPES.o) {\n                            //decides RTL normalization \"hack\" with .n\n                            //normalizeRTL = type(coordinates.n) == TYPES.b ? coordinates.n : normalizeRTL; \n                            for (i = 0; i < coordinateProps[strLength]; i++)\n                                if (coordinateProps[i] in coordinates)\n                                    return coordinates[coordinateProps[i]];\n                        }\n                    }\n                    function getFinalScroll(isX, rawScroll) {\n                        var isString = type(rawScroll) == TYPES.s;\n                        var operator;\n                        var amount;\n                        var scrollInfo = isX ? _scrollHorizontalInfo : _scrollVerticalInfo;\n                        var currScroll = scrollInfo._currentScroll;\n                        var maxScroll = scrollInfo._maxScroll;\n                        var mult = ' * ';\n                        var finalValue;\n                        var isRTLisX = _isRTL && isX;\n                        var normalizeShortcuts = isRTLisX && _rtlScrollBehavior.n && !normalizeRTL;\n                        var strReplace = 'replace';\n                        var evalFunc = eval;\n                        var possibleOperator;\n                        if (isString) {\n                            //check operator\n                            if (rawScroll[strLength] > 2) {\n                                possibleOperator = rawScroll.substr(0, 2);\n                                if (inArray(possibleOperator, coordinatesOperators) > -1)\n                                    operator = possibleOperator;\n                            }\n\n                            //calculate units and shortcuts\n                            rawScroll = operator ? rawScroll.substr(2) : rawScroll;\n                            rawScroll = rawScroll\n                            [strReplace](/min/g, 0) //'min' = 0%\n                            [strReplace](/</g, 0)   //'<'   = 0%\n                            [strReplace](/max/g, (normalizeShortcuts ? '-' : _strEmpty) + _strHundredPercent)    //'max' = 100%\n                            [strReplace](/>/g, (normalizeShortcuts ? '-' : _strEmpty) + _strHundredPercent)      //'>'   = 100%\n                            [strReplace](/px/g, _strEmpty)\n                            [strReplace](/%/g, mult + (maxScroll * (isRTLisX && _rtlScrollBehavior.n ? -1 : 1) / 100.0))\n                            [strReplace](/vw/g, mult + _viewportSize.w)\n                            [strReplace](/vh/g, mult + _viewportSize.h);\n                            amount = parseToZeroOrNumber(isNaN(rawScroll) ? parseToZeroOrNumber(evalFunc(rawScroll), true).toFixed() : rawScroll);\n                        }\n                        else {\n                            amount = rawScroll;\n                        }\n\n                        if (amount !== undefined && !isNaN(amount) && type(amount) == TYPES.n) {\n                            var normalizeIsRTLisX = normalizeRTL && isRTLisX;\n                            var operatorCurrScroll = currScroll * (normalizeIsRTLisX && _rtlScrollBehavior.n ? -1 : 1);\n                            var invert = normalizeIsRTLisX && _rtlScrollBehavior.i;\n                            var negate = normalizeIsRTLisX && _rtlScrollBehavior.n;\n                            operatorCurrScroll = invert ? (maxScroll - operatorCurrScroll) : operatorCurrScroll;\n                            switch (operator) {\n                                case '+=':\n                                    finalValue = operatorCurrScroll + amount;\n                                    break;\n                                case '-=':\n                                    finalValue = operatorCurrScroll - amount;\n                                    break;\n                                case '*=':\n                                    finalValue = operatorCurrScroll * amount;\n                                    break;\n                                case '/=':\n                                    finalValue = operatorCurrScroll / amount;\n                                    break;\n                                default:\n                                    finalValue = amount;\n                                    break;\n                            }\n                            finalValue = invert ? maxScroll - finalValue : finalValue;\n                            finalValue *= negate ? -1 : 1;\n                            finalValue = isRTLisX && _rtlScrollBehavior.n ? MATH.min(0, MATH.max(maxScroll, finalValue)) : MATH.max(0, MATH.min(maxScroll, finalValue));\n                        }\n                        return finalValue === currScroll ? undefined : finalValue;\n                    }\n                    function getPerAxisValue(value, valueInternalType, defaultValue, allowedValues) {\n                        var resultDefault = [defaultValue, defaultValue];\n                        var valueType = type(value);\n                        var valueArrLength;\n                        var valueArrItem;\n\n                        //value can be [ string, or array of two strings ]\n                        if (valueType == valueInternalType) {\n                            value = [value, value];\n                        }\n                        else if (valueType == TYPES.a) {\n                            valueArrLength = value[strLength];\n                            if (valueArrLength > 2 || valueArrLength < 1)\n                                value = resultDefault;\n                            else {\n                                if (valueArrLength === 1)\n                                    value[1] = defaultValue;\n                                for (i = 0; i < valueArrLength; i++) {\n                                    valueArrItem = value[i];\n                                    if (type(valueArrItem) != valueInternalType || !checkSettingsStringValue(valueArrItem, allowedValues)) {\n                                        value = resultDefault;\n                                        break;\n                                    }\n                                }\n                            }\n                        }\n                        else if (valueType == TYPES.o)\n                            value = [value[_strX] || defaultValue, value[_strY] || defaultValue];\n                        else\n                            value = resultDefault;\n                        return { x: value[0], y: value[1] };\n                    }\n                    function generateMargin(marginTopRightBottomLeftArray) {\n                        var result = [];\n                        var currValue;\n                        var currValueType;\n                        var valueDirections = [_strTop, _strRight, _strBottom, _strLeft];\n                        for (i = 0; i < marginTopRightBottomLeftArray[strLength]; i++) {\n                            if (i === valueDirections[strLength])\n                                break;\n                            currValue = marginTopRightBottomLeftArray[i];\n                            currValueType = type(currValue);\n                            if (currValueType == TYPES.b)\n                                result.push(currValue ? parseToZeroOrNumber(finalElement.css(_strMarginMinus + valueDirections[i])) : 0);\n                            else\n                                result.push(currValueType == TYPES.n ? currValue : 0);\n                        }\n                        return result;\n                    }\n\n                    if (possibleElementIsJQuery || possibleElementIsHTMLElement) {\n                        //get settings\n                        var margin = coordinatesIsElementObj ? coordinates.margin : 0;\n                        var axis = coordinatesIsElementObj ? coordinates.axis : 0;\n                        var scroll = coordinatesIsElementObj ? coordinates.scroll : 0;\n                        var block = coordinatesIsElementObj ? coordinates.block : 0;\n                        var marginDefault = [0, 0, 0, 0];\n                        var marginType = type(margin);\n                        var marginLength;\n                        finalElement = possibleElementIsJQuery ? possibleElement : FRAMEWORK(possibleElement);\n\n                        if (finalElement[strLength] > 0) {\n                            //margin can be [ boolean, number, array of 2, array of 4, object ]\n                            if (marginType == TYPES.n || marginType == TYPES.b)\n                                margin = generateMargin([margin, margin, margin, margin]);\n                            else if (marginType == TYPES.a) {\n                                marginLength = margin[strLength];\n                                if (marginLength === 2)\n                                    margin = generateMargin([margin[0], margin[1], margin[0], margin[1]]);\n                                else if (marginLength >= 4)\n                                    margin = generateMargin(margin);\n                                else\n                                    margin = marginDefault;\n                            }\n                            else if (marginType == TYPES.o)\n                                margin = generateMargin([margin[_strTop], margin[_strRight], margin[_strBottom], margin[_strLeft]]);\n                            else\n                                margin = marginDefault;\n\n                            //block = type(block) === TYPES.b ? block ? [ strNearest, strBegin ] : [ strNearest, strEnd ] : block;\n                            settingsAxis = checkSettingsStringValue(axis, elementObjSettingsAxisValues) ? axis : 'xy';\n                            settingsScroll = getPerAxisValue(scroll, TYPES.s, strAlways, elementObjSettingsScrollValues);\n                            settingsBlock = getPerAxisValue(block, TYPES.s, strBegin, elementObjSettingsBlockValues);\n                            settingsMargin = margin;\n\n                            var viewportScroll = {\n                                l: _scrollHorizontalInfo._currentScroll,\n                                t: _scrollVerticalInfo._currentScroll\n                            };\n                            // use padding element instead of viewport element because padding element has never padding, margin or position applied.\n                            var viewportOffset = _paddingElement.offset();\n\n                            //get coordinates\n                            var elementOffset = finalElement.offset();\n                            var doNotScroll = {\n                                x: settingsScroll.x == strNever || settingsAxis == _strY,\n                                y: settingsScroll.y == strNever || settingsAxis == _strX\n                            };\n                            elementOffset[_strTop] -= settingsMargin[0];\n                            elementOffset[_strLeft] -= settingsMargin[3];\n                            var elementScrollCoordinates = {\n                                x: MATH.round(elementOffset[_strLeft] - viewportOffset[_strLeft] + viewportScroll.l),\n                                y: MATH.round(elementOffset[_strTop] - viewportOffset[_strTop] + viewportScroll.t)\n                            };\n                            if (_isRTL) {\n                                if (!_rtlScrollBehavior.n && !_rtlScrollBehavior.i)\n                                    elementScrollCoordinates.x = MATH.round(viewportOffset[_strLeft] - elementOffset[_strLeft] + viewportScroll.l);\n                                if (_rtlScrollBehavior.n && normalizeRTL)\n                                    elementScrollCoordinates.x *= -1;\n                                if (_rtlScrollBehavior.i && normalizeRTL)\n                                    elementScrollCoordinates.x = MATH.round(viewportOffset[_strLeft] - elementOffset[_strLeft] + (_scrollHorizontalInfo._maxScroll - viewportScroll.l));\n                            }\n\n                            //measuring is required\n                            if (settingsBlock.x != strBegin || settingsBlock.y != strBegin || settingsScroll.x == strIfNeeded || settingsScroll.y == strIfNeeded || _isRTL) {\n                                var measuringElm = finalElement[0];\n                                var rawElementSize = _supportTransform ? measuringElm[LEXICON.bCR]() : {\n                                    width: measuringElm[LEXICON.oW],\n                                    height: measuringElm[LEXICON.oH]\n                                };\n                                var elementSize = {\n                                    w: rawElementSize[_strWidth] + settingsMargin[3] + settingsMargin[1],\n                                    h: rawElementSize[_strHeight] + settingsMargin[0] + settingsMargin[2]\n                                };\n                                var finalizeBlock = function (isX) {\n                                    var vars = getScrollbarVars(isX);\n                                    var wh = vars._w_h;\n                                    var lt = vars._left_top;\n                                    var xy = vars._x_y;\n                                    var blockIsEnd = settingsBlock[xy] == (isX ? _isRTL ? strBegin : strEnd : strEnd);\n                                    var blockIsCenter = settingsBlock[xy] == strCenter;\n                                    var blockIsNearest = settingsBlock[xy] == strNearest;\n                                    var scrollNever = settingsScroll[xy] == strNever;\n                                    var scrollIfNeeded = settingsScroll[xy] == strIfNeeded;\n                                    var vpSize = _viewportSize[wh];\n                                    var vpOffset = viewportOffset[lt];\n                                    var elSize = elementSize[wh];\n                                    var elOffset = elementOffset[lt];\n                                    var divide = blockIsCenter ? 2 : 1;\n                                    var elementCenterOffset = elOffset + (elSize / 2);\n                                    var viewportCenterOffset = vpOffset + (vpSize / 2);\n                                    var isInView =\n                                        elSize <= vpSize\n                                        && elOffset >= vpOffset\n                                        && elOffset + elSize <= vpOffset + vpSize;\n\n                                    if (scrollNever)\n                                        doNotScroll[xy] = true;\n                                    else if (!doNotScroll[xy]) {\n                                        if (blockIsNearest || scrollIfNeeded) {\n                                            doNotScroll[xy] = scrollIfNeeded ? isInView : false;\n                                            blockIsEnd = elSize < vpSize ? elementCenterOffset > viewportCenterOffset : elementCenterOffset < viewportCenterOffset;\n                                        }\n                                        elementScrollCoordinates[xy] -= blockIsEnd || blockIsCenter ? ((vpSize / divide) - (elSize / divide)) * (isX && _isRTL && normalizeRTL ? -1 : 1) : 0;\n                                    }\n                                };\n                                finalizeBlock(true);\n                                finalizeBlock(false);\n                            }\n\n                            if (doNotScroll.y)\n                                delete elementScrollCoordinates.y;\n                            if (doNotScroll.x)\n                                delete elementScrollCoordinates.x;\n\n                            coordinates = elementScrollCoordinates;\n                        }\n                    }\n\n                    finalScroll[_strScrollLeft] = getFinalScroll(true, getRawScroll(true, coordinates));\n                    finalScroll[_strScrollTop] = getFinalScroll(false, getRawScroll(false, coordinates));\n                    doScrollLeft = finalScroll[_strScrollLeft] !== undefined;\n                    doScrollTop = finalScroll[_strScrollTop] !== undefined;\n\n                    if ((doScrollLeft || doScrollTop) && (duration > 0 || durationIsObject)) {\n                        if (durationIsObject) {\n                            duration.complete = proxyCompleteCallback;\n                            _viewportElement.animate(finalScroll, duration);\n                        }\n                        else {\n                            animationOptions = {\n                                duration: duration,\n                                complete: proxyCompleteCallback\n                            };\n                            if (COMPATIBILITY.isA(easing) || FRAMEWORK.isPlainObject(easing)) {\n                                specialEasing[_strScrollLeft] = easing[0] || easing.x;\n                                specialEasing[_strScrollTop] = easing[1] || easing.y;\n                                animationOptions.specialEasing = specialEasing;\n                            }\n                            else {\n                                animationOptions.easing = easing;\n                            }\n                            _viewportElement.animate(finalScroll, animationOptions);\n                        }\n                    }\n                    else {\n                        if (doScrollLeft)\n                            _viewportElement[_strScrollLeft](finalScroll[_strScrollLeft]);\n                        if (doScrollTop)\n                            _viewportElement[_strScrollTop](finalScroll[_strScrollTop]);\n                        updateScrollbarInfos();\n                    }\n                };\n\n                /**\n                 * Stops all scroll animations.\n                 * @returns {*} The current OverlayScrollbars instance (for chaining).\n                 */\n                _base.scrollStop = function (param1, param2, param3) {\n                    _viewportElement.stop(param1, param2, param3);\n                    return _base;\n                };\n\n                /**\n                 * Returns all relevant elements.\n                 * @param elementName The name of the element which shall be returned.\n                 * @returns {{target: *, host: *, padding: *, viewport: *, content: *, scrollbarHorizontal: {scrollbar: *, track: *, handle: *}, scrollbarVertical: {scrollbar: *, track: *, handle: *}, scrollbarCorner: *} | *}\n                 */\n                _base.getElements = function (elementName) {\n                    var obj = {\n                        target: _targetElementNative,\n                        host: _hostElementNative,\n                        padding: _paddingElementNative,\n                        viewport: _viewportElementNative,\n                        content: _contentElementNative,\n                        scrollbarHorizontal: {\n                            scrollbar: _scrollbarHorizontalElement[0],\n                            track: _scrollbarHorizontalTrackElement[0],\n                            handle: _scrollbarHorizontalHandleElement[0]\n                        },\n                        scrollbarVertical: {\n                            scrollbar: _scrollbarVerticalElement[0],\n                            track: _scrollbarVerticalTrackElement[0],\n                            handle: _scrollbarVerticalHandleElement[0]\n                        },\n                        scrollbarCorner: _scrollbarCornerElement[0]\n                    };\n                    return type(elementName) == TYPES.s ? getObjectPropVal(obj, elementName) : obj;\n                };\n\n                /**\n                 * Returns a object which describes the current state of this instance.\n                 * @param stateProperty A specific property from the state object which shall be returned.\n                 * @returns {{widthAuto, heightAuto, overflowAmount, hideOverflow, hasOverflow, contentScrollSize, viewportSize, hostSize, autoUpdate} | *}\n                 */\n                _base.getState = function (stateProperty) {\n                    function prepare(obj) {\n                        if (!FRAMEWORK.isPlainObject(obj))\n                            return obj;\n                        var extended = extendDeep({}, obj);\n                        var changePropertyName = function (from, to) {\n                            if (extended[LEXICON.hOP](from)) {\n                                extended[to] = extended[from];\n                                delete extended[from];\n                            }\n                        };\n                        changePropertyName('w', _strWidth); //change w to width\n                        changePropertyName('h', _strHeight); //change h to height\n                        delete extended.c; //delete c (the 'changed' prop)\n                        return extended;\n                    };\n                    var obj = {\n                        destroyed: !!prepare(_destroyed),\n                        sleeping: !!prepare(_sleeping),\n                        autoUpdate: prepare(!_mutationObserversConnected),\n                        widthAuto: prepare(_widthAutoCache),\n                        heightAuto: prepare(_heightAutoCache),\n                        padding: prepare(_cssPaddingCache),\n                        overflowAmount: prepare(_overflowAmountCache),\n                        hideOverflow: prepare(_hideOverflowCache),\n                        hasOverflow: prepare(_hasOverflowCache),\n                        contentScrollSize: prepare(_contentScrollSizeCache),\n                        viewportSize: prepare(_viewportSize),\n                        hostSize: prepare(_hostSizeCache),\n                        documentMixed: prepare(_documentMixed)\n                    };\n                    return type(stateProperty) == TYPES.s ? getObjectPropVal(obj, stateProperty) : obj;\n                };\n\n                /**\n                 * Gets all or specific extension instance.\n                 * @param extName The name of the extension from which the instance shall be got.\n                 * @returns {{}} The instance of the extension with the given name or undefined if the instance couldn't be found.\n                 */\n                _base.ext = function (extName) {\n                    var result;\n                    var privateMethods = _extensionsPrivateMethods.split(' ');\n                    var i = 0;\n                    if (type(extName) == TYPES.s) {\n                        if (_extensions[LEXICON.hOP](extName)) {\n                            result = extendDeep({}, _extensions[extName]);\n                            for (; i < privateMethods.length; i++)\n                                delete result[privateMethods[i]];\n                        }\n                    }\n                    else {\n                        result = {};\n                        for (i in _extensions)\n                            result[i] = extendDeep({}, _base.ext(i));\n                    }\n                    return result;\n                };\n\n                /**\n                 * Adds a extension to this instance.\n                 * @param extName The name of the extension which shall be added.\n                 * @param extensionOptions The extension options which shall be used.\n                 * @returns {{}} The instance of the added extension or undefined if the extension couldn't be added properly.\n                 */\n                _base.addExt = function (extName, extensionOptions) {\n                    var registeredExtensionObj = _plugin.extension(extName);\n                    var instance;\n                    var instanceAdded;\n                    var instanceContract;\n                    var contractResult;\n                    var contractFulfilled = true;\n                    if (registeredExtensionObj) {\n                        if (!_extensions[LEXICON.hOP](extName)) {\n                            instance = registeredExtensionObj.extensionFactory.call(_base,\n                                extendDeep({}, registeredExtensionObj.defaultOptions),\n                                FRAMEWORK,\n                                COMPATIBILITY);\n\n                            if (instance) {\n                                instanceContract = instance.contract;\n                                if (type(instanceContract) == TYPES.f) {\n                                    contractResult = instanceContract(window);\n                                    contractFulfilled = type(contractResult) == TYPES.b ? contractResult : contractFulfilled;\n                                }\n                                if (contractFulfilled) {\n                                    _extensions[extName] = instance;\n                                    instanceAdded = instance.added;\n                                    if (type(instanceAdded) == TYPES.f)\n                                        instanceAdded(extensionOptions);\n\n                                    return _base.ext(extName);\n                                }\n                            }\n                        }\n                        else\n                            return _base.ext(extName);\n                    }\n                    else\n                        console.warn(\"A extension with the name \\\"\" + extName + \"\\\" isn't registered.\");\n                };\n\n                /**\n                 * Removes a extension from this instance.\n                 * @param extName The name of the extension which shall be removed.\n                 * @returns {boolean} True if the extension was removed, false otherwise e.g. if the extension wasn't added before.\n                 */\n                _base.removeExt = function (extName) {\n                    var instance = _extensions[extName];\n                    var instanceRemoved;\n                    if (instance) {\n                        delete _extensions[extName];\n\n                        instanceRemoved = instance.removed;\n                        if (type(instanceRemoved) == TYPES.f)\n                            instanceRemoved();\n\n                        return true;\n                    }\n                    return false;\n                };\n\n                /**\n                 * Constructs the plugin.\n                 * @param targetElement The element to which the plugin shall be applied.\n                 * @param options The initial options of the plugin.\n                 * @param extensions The extension(s) which shall be added right after the initialization.\n                 * @returns {boolean} True if the plugin was successfully initialized, false otherwise.\n                 */\n                function construct(targetElement, options, extensions) {\n                    _defaultOptions = globals.defaultOptions;\n                    _nativeScrollbarStyling = globals.nativeScrollbarStyling;\n                    _nativeScrollbarSize = extendDeep({}, globals.nativeScrollbarSize);\n                    _nativeScrollbarIsOverlaid = extendDeep({}, globals.nativeScrollbarIsOverlaid);\n                    _overlayScrollbarDummySize = extendDeep({}, globals.overlayScrollbarDummySize);\n                    _rtlScrollBehavior = extendDeep({}, globals.rtlScrollBehavior);\n\n                    //parse & set options but don't update\n                    setOptions(extendDeep({}, _defaultOptions, options));\n\n                    _cssCalc = globals.cssCalc;\n                    _msieVersion = globals.msie;\n                    _autoUpdateRecommended = globals.autoUpdateRecommended;\n                    _supportTransition = globals.supportTransition;\n                    _supportTransform = globals.supportTransform;\n                    _supportPassiveEvents = globals.supportPassiveEvents;\n                    _supportResizeObserver = globals.supportResizeObserver;\n                    _supportMutationObserver = globals.supportMutationObserver;\n                    _restrictedMeasuring = globals.restrictedMeasuring;\n                    _documentElement = FRAMEWORK(targetElement.ownerDocument);\n                    _documentElementNative = _documentElement[0];\n                    _windowElement = FRAMEWORK(_documentElementNative.defaultView || _documentElementNative.parentWindow);\n                    _windowElementNative = _windowElement[0];\n                    _htmlElement = findFirst(_documentElement, 'html');\n                    _bodyElement = findFirst(_htmlElement, 'body');\n                    _targetElement = FRAMEWORK(targetElement);\n                    _targetElementNative = _targetElement[0];\n                    _isTextarea = _targetElement.is('textarea');\n                    _isBody = _targetElement.is('body');\n                    _documentMixed = _documentElementNative !== document;\n\n                    /* On a div Element The if checks only whether:\n                     * - the targetElement has the class \"os-host\"\n                     * - the targetElement has a a child with the class \"os-padding\"\n                     * \n                     * If that's the case, its assumed the DOM has already the following structure:\n                     * (The \".os-host\" element is the targetElement)\n                     *\n                     *  <div class=\"os-host\">\n                     *      <div class=\"os-resize-observer-host\"></div>\n                     *      <div class=\"os-padding\">\n                     *          <div class=\"os-viewport\">\n                     *              <div class=\"os-content\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-horizontal \">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-vertical\">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar-corner\"></div>\n                     *  </div>\n                     *\n                     * =====================================================================================\n                     * \n                     * On a Textarea Element The if checks only whether:\n                     * - the targetElement has the class \"os-textarea\" \n                     * - the targetElement is inside a element with the class \"os-content\" \n                     * \n                     * If that's the case, its assumed the DOM has already the following structure:\n                     * (The \".os-textarea\" (textarea) element is the targetElement)\n                     *\n                     *  <div class=\"os-host-textarea\">\n                     *      <div class=\"os-resize-observer-host\"></div>\n                     *      <div class=\"os-padding os-text-inherit\">\n                     *          <div class=\"os-viewport os-text-inherit\">\n                     *              <div class=\"os-content os-text-inherit\">\n                     *                  <div class=\"os-textarea-cover\"></div>\n                     *                  <textarea class=\"os-textarea os-text-inherit\"></textarea>\n                     *              </div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-horizontal \">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar os-scrollbar-vertical\">\n                     *          <div class=\"os-scrollbar-track\">\n                     *              <div class=\"os-scrollbar-handle\"></div>\n                     *          </div>\n                     *      </div>\n                     *      <div class=\"os-scrollbar-corner\"></div>\n                     *  </div>\n                     */\n                    _domExists = _isTextarea\n                        ? _targetElement.hasClass(_classNameTextareaElement) && _targetElement.parent().hasClass(_classNameContentElement)\n                        : _targetElement.hasClass(_classNameHostElement) && _targetElement.children(_strDot + _classNamePaddingElement)[LEXICON.l];\n\n                    var initBodyScroll;\n                    var bodyMouseTouchDownListener;\n\n                    //check if the plugin hasn't to be initialized\n                    if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y && !_currentPreparedOptions.nativeScrollbarsOverlaid.initialize) {\n                        dispatchCallback('onInitializationWithdrawn');\n                        if (_domExists) {\n                            setupStructureDOM(true);\n                            setupScrollbarsDOM(true);\n                            setupScrollbarCornerDOM(true);\n                        }\n\n                        _destroyed = true;\n                        _sleeping = true;\n\n                        return _base;\n                    }\n\n                    if (_isBody) {\n                        initBodyScroll = {};\n                        initBodyScroll.l = MATH.max(_targetElement[_strScrollLeft](), _htmlElement[_strScrollLeft](), _windowElement[_strScrollLeft]());\n                        initBodyScroll.t = MATH.max(_targetElement[_strScrollTop](), _htmlElement[_strScrollTop](), _windowElement[_strScrollTop]());\n\n                        bodyMouseTouchDownListener = function () {\n                            _viewportElement.removeAttr(LEXICON.ti);\n                            setupResponsiveEventListener(_viewportElement, _strMouseTouchDownEvent, bodyMouseTouchDownListener, true, true);\n                        }\n                    }\n\n                    //build OverlayScrollbars DOM\n                    setupStructureDOM();\n                    setupScrollbarsDOM();\n                    setupScrollbarCornerDOM();\n\n                    //create OverlayScrollbars events\n                    setupStructureEvents();\n                    setupScrollbarEvents(true);\n                    setupScrollbarEvents(false);\n                    setupScrollbarCornerEvents();\n\n                    //create mutation observers\n                    createMutationObservers();\n\n                    //build resize observer for the host element\n                    setupResizeObserver(_sizeObserverElement, hostOnResized);\n\n                    if (_isBody) {\n                        //apply the body scroll to handle it right in the update method\n                        _viewportElement[_strScrollLeft](initBodyScroll.l)[_strScrollTop](initBodyScroll.t);\n\n                        //set the focus on the viewport element so you dont have to click on the page to use keyboard keys (up / down / space) for scrolling\n                        if (document.activeElement == targetElement && _viewportElementNative.focus) {\n                            //set a tabindex to make the viewportElement focusable\n                            _viewportElement.attr(LEXICON.ti, '-1');\n                            _viewportElementNative.focus();\n\n                            /* the tabindex has to be removed due to;\n                             * If you set the tabindex attribute on an <div>, then its child content cannot be scrolled with the arrow keys unless you set tabindex on the content, too\n                             * https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex\n                             */\n                            setupResponsiveEventListener(_viewportElement, _strMouseTouchDownEvent, bodyMouseTouchDownListener, false, true);\n                        }\n                    }\n\n                    //update for the first time & initialize cache\n                    _base.update(_strAuto);\n\n                    //the plugin is initialized now!\n                    _initialized = true;\n                    dispatchCallback('onInitialized');\n\n                    //call all callbacks which would fire before the initialized was complete\n                    each(_callbacksInitQeueue, function (index, value) { dispatchCallback(value.n, value.a); });\n                    _callbacksInitQeueue = [];\n\n                    //add extensions\n                    if (type(extensions) == TYPES.s)\n                        extensions = [extensions];\n                    if (COMPATIBILITY.isA(extensions))\n                        each(extensions, function (index, value) { _base.addExt(value); });\n                    else if (FRAMEWORK.isPlainObject(extensions))\n                        each(extensions, function (key, value) { _base.addExt(key, value); });\n\n                    //add the transition class for transitions AFTER the first update & AFTER the applied extensions (for preventing unwanted transitions)\n                    setTimeout(function () {\n                        if (_supportTransition && !_destroyed)\n                            addClass(_hostElement, _classNameHostTransition);\n                    }, 333);\n\n                    return _base;\n                }\n\n                if (_plugin.valid(construct(pluginTargetElement, options, extensions))) {\n                    INSTANCES(pluginTargetElement, _base);\n                }\n\n                return _base;\n            }\n\n            /**\n             * Initializes a new OverlayScrollbarsInstance object or changes options if already initialized or returns the current instance.\n             * @param pluginTargetElements The elements to which the Plugin shall be initialized.\n             * @param options The custom options with which the plugin shall be initialized.\n             * @param extensions The extension(s) which shall be added right after initialization.\n             * @returns {*}\n             */\n            _plugin = window[PLUGINNAME] = function (pluginTargetElements, options, extensions) {\n                if (arguments[LEXICON.l] === 0)\n                    return this;\n\n                var arr = [];\n                var optsIsPlainObj = FRAMEWORK.isPlainObject(options);\n                var inst;\n                var result;\n\n                //pluginTargetElements is null or undefined\n                if (!pluginTargetElements)\n                    return optsIsPlainObj || !options ? result : arr;\n\n                /*\n                   pluginTargetElements will be converted to:\n                   1. A jQueryElement Array\n                   2. A HTMLElement Array\n                   3. A Array with a single HTML Element\n                   so pluginTargetElements is always a array.\n                */\n                pluginTargetElements = pluginTargetElements[LEXICON.l] != undefined ? pluginTargetElements : [pluginTargetElements[0] || pluginTargetElements];\n                initOverlayScrollbarsStatics();\n\n                if (pluginTargetElements[LEXICON.l] > 0) {\n                    if (optsIsPlainObj) {\n                        FRAMEWORK.each(pluginTargetElements, function (i, v) {\n                            inst = v;\n                            if (inst !== undefined)\n                                arr.push(OverlayScrollbarsInstance(inst, options, extensions, _pluginsGlobals, _pluginsAutoUpdateLoop));\n                        });\n                    }\n                    else {\n                        FRAMEWORK.each(pluginTargetElements, function (i, v) {\n                            inst = INSTANCES(v);\n                            if ((options === '!' && _plugin.valid(inst)) || (COMPATIBILITY.type(options) == TYPES.f && options(v, inst)))\n                                arr.push(inst);\n                            else if (options === undefined)\n                                arr.push(inst);\n                        });\n                    }\n                    result = arr[LEXICON.l] === 1 ? arr[0] : arr;\n                }\n                return result;\n            };\n\n            /**\n             * Returns a object which contains global information about the plugin and each instance of it.\n             * The returned object is just a copy, that means that changes to the returned object won't have any effect to the original object.\n             */\n            _plugin.globals = function () {\n                initOverlayScrollbarsStatics();\n                var globals = FRAMEWORK.extend(true, {}, _pluginsGlobals);\n                delete globals['msie'];\n                return globals;\n            };\n\n            /**\n             * Gets or Sets the default options for each new plugin initialization.\n             * @param newDefaultOptions The object with which the default options shall be extended.\n             */\n            _plugin.defaultOptions = function (newDefaultOptions) {\n                initOverlayScrollbarsStatics();\n                var currDefaultOptions = _pluginsGlobals.defaultOptions;\n                if (newDefaultOptions === undefined)\n                    return FRAMEWORK.extend(true, {}, currDefaultOptions);\n\n                //set the new default options\n                _pluginsGlobals.defaultOptions = FRAMEWORK.extend(true, {}, currDefaultOptions, _pluginsOptions._validate(newDefaultOptions, _pluginsOptions._template, true, currDefaultOptions)._default);\n            };\n\n            /**\n             * Checks whether the passed instance is a non-destroyed OverlayScrollbars instance.\n             * @param osInstance The potential OverlayScrollbars instance which shall be checked.\n             * @returns {boolean} True if the passed value is a non-destroyed OverlayScrollbars instance, false otherwise.\n             */\n            _plugin.valid = function (osInstance) {\n                return osInstance instanceof _plugin && !osInstance.getState().destroyed;\n            };\n\n            /**\n             * Registers, Unregisters or returns a extension.\n             * Register: Pass the name and the extension. (defaultOptions is optional)\n             * Unregister: Pass the name and anything except a function as extension parameter.\n             * Get extension: Pass the name of the extension which shall be got.\n             * Get all extensions: Pass no arguments.\n             * @param extensionName The name of the extension which shall be registered, unregistered or returned.\n             * @param extension A function which generates the instance of the extension or anything other to remove a already registered extension.\n             * @param defaultOptions The default options which shall be used for the registered extension.\n             */\n            _plugin.extension = function (extensionName, extension, defaultOptions) {\n                var extNameTypeString = COMPATIBILITY.type(extensionName) == TYPES.s;\n                var argLen = arguments[LEXICON.l];\n                var i = 0;\n                if (argLen < 1 || !extNameTypeString) {\n                    //return a copy of all extension objects\n                    return FRAMEWORK.extend(true, { length: _pluginsExtensions[LEXICON.l] }, _pluginsExtensions);\n                }\n                else if (extNameTypeString) {\n                    if (COMPATIBILITY.type(extension) == TYPES.f) {\n                        //register extension\n                        _pluginsExtensions.push({\n                            name: extensionName,\n                            extensionFactory: extension,\n                            defaultOptions: defaultOptions\n                        });\n                    }\n                    else {\n                        for (; i < _pluginsExtensions[LEXICON.l]; i++) {\n                            if (_pluginsExtensions[i].name === extensionName) {\n                                if (argLen > 1)\n                                    _pluginsExtensions.splice(i, 1); //remove extension\n                                else\n                                    return FRAMEWORK.extend(true, {}, _pluginsExtensions[i]); //return extension with the given name\n                            }\n                        }\n                    }\n                }\n            };\n\n            return _plugin;\n        })();\n\n        if (JQUERY && JQUERY.fn) {\n            /**\n             * The jQuery initialization interface.\n             * @param options The initial options for the construction of the plugin. To initialize the plugin, this option has to be a object! If it isn't a object, the instance(s) are returned and the plugin wont be initialized.\n             * @param extensions The extension(s) which shall be added right after initialization.\n             * @returns {*} After initialization it returns the jQuery element array, else it returns the instance(s) of the elements which are selected.\n             */\n            JQUERY.fn.overlayScrollbars = function (options, extensions) {\n                var _elements = this;\n                if (JQUERY.isPlainObject(options)) {\n                    JQUERY.each(_elements, function () { PLUGIN(this, options, extensions); });\n                    return _elements;\n                }\n                else\n                    return PLUGIN(_elements, options);\n            };\n        }\n        return PLUGIN;\n    }\n));"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/apexcharts.amd.js",
    "content": "/*! For license information please see apexcharts.amd.js.LICENSE.txt */\n!function(t,e){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define([],e):\"object\"==typeof exports?exports.ApexCharts=e():t.ApexCharts=e()}(self,(function(){return(()=>{var t={922:t=>{\"use strict\";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var i=function(t,e){var i,a,s,r=t[1]||\"\",n=t[3];if(!n)return r;if(e&&\"function\"==typeof btoa){var o=(i=n,a=btoa(unescape(encodeURIComponent(JSON.stringify(i)))),s=\"sourceMappingURL=data:application/json;charset=utf-8;base64,\".concat(a),\"/*# \".concat(s,\" */\")),l=n.sources.map((function(t){return\"/*# sourceURL=\".concat(n.sourceRoot||\"\").concat(t,\" */\")}));return[r].concat(l).concat([o]).join(\"\\n\")}return[r].join(\"\\n\")}(e,t);return e[2]?\"@media \".concat(e[2],\" {\").concat(i,\"}\"):i})).join(\"\")},e.i=function(t,i){\"string\"==typeof t&&(t=[[null,t,\"\"]]);for(var a=0;a<t.length;a++){var s=[].concat(t[a]);i&&(s[2]?s[2]=\"\".concat(i,\" and \").concat(s[2]):s[2]=i),e.push(s)}},e}},372:function(){function t(e){return t=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},t(e)}(function(){function e(t){t.remember(\"_draggable\",this),this.el=t}e.prototype.init=function(t,e){var i=this;this.constraint=t,this.value=e,this.el.on(\"mousedown.drag\",(function(t){i.start(t)})),this.el.on(\"touchstart.drag\",(function(t){i.start(t)}))},e.prototype.transformPoint=function(t,e){var i=(t=t||window.event).changedTouches&&t.changedTouches[0]||t;return this.p.x=i.clientX-(e||0),this.p.y=i.clientY,this.p.matrixTransform(this.m)},e.prototype.getBBox=function(){var t=this.el.bbox();return this.el instanceof SVG.Nested&&(t=this.el.rbox()),(this.el instanceof SVG.G||this.el instanceof SVG.Use||this.el instanceof SVG.Nested)&&(t.x=this.el.x(),t.y=this.el.y()),t},e.prototype.start=function(t){if(\"click\"!=t.type&&\"mousedown\"!=t.type&&\"mousemove\"!=t.type||1==(t.which||t.buttons)){var e=this;if(this.el.fire(\"beforedrag\",{event:t,handler:this}),!this.el.event().defaultPrevented){t.preventDefault(),t.stopPropagation(),this.parent=this.parent||this.el.parent(SVG.Nested)||this.el.parent(SVG.Doc),this.p=this.parent.node.createSVGPoint(),this.m=this.el.node.getScreenCTM().inverse();var i,a=this.getBBox();if(this.el instanceof SVG.Text)switch(i=this.el.node.getComputedTextLength(),this.el.attr(\"text-anchor\")){case\"middle\":i/=2;break;case\"start\":i=0}this.startPoints={point:this.transformPoint(t,i),box:a,transform:this.el.transform()},SVG.on(window,\"mousemove.drag\",(function(t){e.drag(t)})),SVG.on(window,\"touchmove.drag\",(function(t){e.drag(t)})),SVG.on(window,\"mouseup.drag\",(function(t){e.end(t)})),SVG.on(window,\"touchend.drag\",(function(t){e.end(t)})),this.el.fire(\"dragstart\",{event:t,p:this.startPoints.point,m:this.m,handler:this})}}},e.prototype.drag=function(e){var i=this.getBBox(),a=this.transformPoint(e),s=this.startPoints.box.x+a.x-this.startPoints.point.x,r=this.startPoints.box.y+a.y-this.startPoints.point.y,n=this.constraint,o=a.x-this.startPoints.point.x,l=a.y-this.startPoints.point.y;if(this.el.fire(\"dragmove\",{event:e,p:a,m:this.m,handler:this}),this.el.event().defaultPrevented)return a;if(\"function\"==typeof n){var c=n.call(this.el,s,r,this.m);\"boolean\"==typeof c&&(c={x:c,y:c}),!0===c.x?this.el.x(s):!1!==c.x&&this.el.x(c.x),!0===c.y?this.el.y(r):!1!==c.y&&this.el.y(c.y)}else\"object\"==t(n)&&(null!=n.minX&&s<n.minX?o=(s=n.minX)-this.startPoints.box.x:null!=n.maxX&&s>n.maxX-i.width&&(o=(s=n.maxX-i.width)-this.startPoints.box.x),null!=n.minY&&r<n.minY?l=(r=n.minY)-this.startPoints.box.y:null!=n.maxY&&r>n.maxY-i.height&&(l=(r=n.maxY-i.height)-this.startPoints.box.y),null!=n.snapToGrid&&(s-=s%n.snapToGrid,r-=r%n.snapToGrid,o-=o%n.snapToGrid,l-=l%n.snapToGrid),this.el instanceof SVG.G?this.el.matrix(this.startPoints.transform).transform({x:o,y:l},!0):this.el.move(s,r));return a},e.prototype.end=function(t){var e=this.drag(t);this.el.fire(\"dragend\",{event:t,p:e,m:this.m,handler:this}),SVG.off(window,\"mousemove.drag\"),SVG.off(window,\"touchmove.drag\"),SVG.off(window,\"mouseup.drag\"),SVG.off(window,\"touchend.drag\")},SVG.extend(SVG.Element,{draggable:function(i,a){\"function\"!=typeof i&&\"object\"!=t(i)||(a=i,i=!0);var s=this.remember(\"_draggable\")||new e(this);return(i=void 0===i||i)?s.init(a||{},i):(this.off(\"mousedown.drag\"),this.off(\"touchstart.drag\")),this}})}).call(this)},872:function(){(function(){SVG.Filter=SVG.invent({create:\"filter\",inherit:SVG.Parent,extend:{source:\"SourceGraphic\",sourceAlpha:\"SourceAlpha\",background:\"BackgroundImage\",backgroundAlpha:\"BackgroundAlpha\",fill:\"FillPaint\",stroke:\"StrokePaint\",autoSetIn:!0,put:function(t,e){return this.add(t,e),!t.attr(\"in\")&&this.autoSetIn&&t.attr(\"in\",this.source),t.attr(\"result\")||t.attr(\"result\",t),t},blend:function(t,e,i){return this.put(new SVG.BlendEffect(t,e,i))},colorMatrix:function(t,e){return this.put(new SVG.ColorMatrixEffect(t,e))},convolveMatrix:function(t){return this.put(new SVG.ConvolveMatrixEffect(t))},componentTransfer:function(t){return this.put(new SVG.ComponentTransferEffect(t))},composite:function(t,e,i){return this.put(new SVG.CompositeEffect(t,e,i))},flood:function(t,e){return this.put(new SVG.FloodEffect(t,e))},offset:function(t,e){return this.put(new SVG.OffsetEffect(t,e))},image:function(t){return this.put(new SVG.ImageEffect(t))},merge:function(){var t=[void 0];for(var e in arguments)t.push(arguments[e]);return this.put(new(SVG.MergeEffect.bind.apply(SVG.MergeEffect,t)))},gaussianBlur:function(t,e){return this.put(new SVG.GaussianBlurEffect(t,e))},morphology:function(t,e){return this.put(new SVG.MorphologyEffect(t,e))},diffuseLighting:function(t,e,i){return this.put(new SVG.DiffuseLightingEffect(t,e,i))},displacementMap:function(t,e,i,a,s){return this.put(new SVG.DisplacementMapEffect(t,e,i,a,s))},specularLighting:function(t,e,i,a){return this.put(new SVG.SpecularLightingEffect(t,e,i,a))},tile:function(){return this.put(new SVG.TileEffect)},turbulence:function(t,e,i,a,s){return this.put(new SVG.TurbulenceEffect(t,e,i,a,s))},toString:function(){return\"url(#\"+this.attr(\"id\")+\")\"}}}),SVG.extend(SVG.Defs,{filter:function(t){var e=this.put(new SVG.Filter);return\"function\"==typeof t&&t.call(e,e),e}}),SVG.extend(SVG.Container,{filter:function(t){return this.defs().filter(t)}}),SVG.extend(SVG.Element,SVG.G,SVG.Nested,{filter:function(t){return this.filterer=t instanceof SVG.Element?t:this.doc().filter(t),this.doc()&&this.filterer.doc()!==this.doc()&&this.doc().defs().add(this.filterer),this.attr(\"filter\",this.filterer),this.filterer},unfilter:function(t){return this.filterer&&!0===t&&this.filterer.remove(),delete this.filterer,this.attr(\"filter\",null)}}),SVG.Effect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in\")+'\"]').get(0)||this.attr(\"in\"):this.attr(\"in\",t)},result:function(t){return null==t?this.attr(\"result\"):this.attr(\"result\",t)},toString:function(){return this.result()}}}),SVG.ParentEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Parent,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in\")+'\"]').get(0)||this.attr(\"in\"):this.attr(\"in\",t)},result:function(t){return null==t?this.attr(\"result\"):this.attr(\"result\",t)},toString:function(){return this.result()}}});var t={blend:function(t,e){return this.parent()&&this.parent().blend(this,t,e)},colorMatrix:function(t,e){return this.parent()&&this.parent().colorMatrix(t,e).in(this)},convolveMatrix:function(t){return this.parent()&&this.parent().convolveMatrix(t).in(this)},componentTransfer:function(t){return this.parent()&&this.parent().componentTransfer(t).in(this)},composite:function(t,e){return this.parent()&&this.parent().composite(this,t,e)},flood:function(t,e){return this.parent()&&this.parent().flood(t,e)},offset:function(t,e){return this.parent()&&this.parent().offset(t,e).in(this)},image:function(t){return this.parent()&&this.parent().image(t)},merge:function(){return this.parent()&&this.parent().merge.apply(this.parent(),[this].concat(arguments))},gaussianBlur:function(t,e){return this.parent()&&this.parent().gaussianBlur(t,e).in(this)},morphology:function(t,e){return this.parent()&&this.parent().morphology(t,e).in(this)},diffuseLighting:function(t,e,i){return this.parent()&&this.parent().diffuseLighting(t,e,i).in(this)},displacementMap:function(t,e,i,a){return this.parent()&&this.parent().displacementMap(this,t,e,i,a)},specularLighting:function(t,e,i,a){return this.parent()&&this.parent().specularLighting(t,e,i,a).in(this)},tile:function(){return this.parent()&&this.parent().tile().in(this)},turbulence:function(t,e,i,a,s){return this.parent()&&this.parent().turbulence(t,e,i,a,s).in(this)}};SVG.extend(SVG.Effect,t),SVG.extend(SVG.ParentEffect,t),SVG.ChildEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){this.attr(\"in\",t)}}});var e={blend:function(t,e,i){this.attr({in:t,in2:e,mode:i||\"normal\"})},colorMatrix:function(t,e){\"matrix\"==t&&(e=s(e)),this.attr({type:t,values:void 0===e?null:e})},convolveMatrix:function(t){t=s(t),this.attr({order:Math.sqrt(t.split(\" \").length),kernelMatrix:t})},composite:function(t,e,i){this.attr({in:t,in2:e,operator:i})},flood:function(t,e){this.attr(\"flood-color\",t),null!=e&&this.attr(\"flood-opacity\",e)},offset:function(t,e){this.attr({dx:t,dy:e})},image:function(t){this.attr(\"href\",t,SVG.xlink)},displacementMap:function(t,e,i,a,s){this.attr({in:t,in2:e,scale:i,xChannelSelector:a,yChannelSelector:s})},gaussianBlur:function(t,e){null!=t||null!=e?this.attr(\"stdDeviation\",r(Array.prototype.slice.call(arguments))):this.attr(\"stdDeviation\",\"0 0\")},morphology:function(t,e){this.attr({operator:t,radius:e})},tile:function(){},turbulence:function(t,e,i,a,s){this.attr({numOctaves:e,seed:i,stitchTiles:a,baseFrequency:t,type:s})}},i={merge:function(){var t;if(arguments[0]instanceof SVG.Set){var e=this;arguments[0].each((function(t){this instanceof SVG.MergeNode?e.put(this):(this instanceof SVG.Effect||this instanceof SVG.ParentEffect)&&e.put(new SVG.MergeNode(this))}))}else{t=Array.isArray(arguments[0])?arguments[0]:arguments;for(var i=0;i<t.length;i++)t[i]instanceof SVG.MergeNode?this.put(t[i]):this.put(new SVG.MergeNode(t[i]))}},componentTransfer:function(t){if(this.rgb=new SVG.Set,[\"r\",\"g\",\"b\",\"a\"].forEach(function(t){this[t]=new(SVG[\"Func\"+t.toUpperCase()])(\"identity\"),this.rgb.add(this[t]),this.node.appendChild(this[t].node)}.bind(this)),t)for(var e in t.rgb&&([\"r\",\"g\",\"b\"].forEach(function(e){this[e].attr(t.rgb)}.bind(this)),delete t.rgb),t)this[e].attr(t[e])},diffuseLighting:function(t,e,i){this.attr({surfaceScale:t,diffuseConstant:e,kernelUnitLength:i})},specularLighting:function(t,e,i,a){this.attr({surfaceScale:t,diffuseConstant:e,specularExponent:i,kernelUnitLength:a})}},a={distantLight:function(t,e){this.attr({azimuth:t,elevation:e})},pointLight:function(t,e,i){this.attr({x:t,y:e,z:i})},spotLight:function(t,e,i,a,s,r){this.attr({x:t,y:e,z:i,pointsAtX:a,pointsAtY:s,pointsAtZ:r})},mergeNode:function(t){this.attr(\"in\",t)}};function s(t){return Array.isArray(t)&&(t=new SVG.Array(t)),t.toString().replace(/^\\s+/,\"\").replace(/\\s+$/,\"\").replace(/\\s+/g,\" \")}function r(t){if(!Array.isArray(t))return t;for(var e=0,i=t.length,a=[];e<i;e++)a.push(t[e]);return a.join(\" \")}function n(){var t=function(){};for(var e in\"function\"==typeof arguments[arguments.length-1]&&(t=arguments[arguments.length-1],Array.prototype.splice.call(arguments,arguments.length-1,1)),arguments)for(var i in arguments[e])t(arguments[e][i],i,arguments[e])}[\"r\",\"g\",\"b\",\"a\"].forEach((function(t){a[\"Func\"+t.toUpperCase()]=function(t){switch(this.attr(\"type\",t),t){case\"table\":this.attr(\"tableValues\",arguments[1]);break;case\"linear\":this.attr(\"slope\",arguments[1]),this.attr(\"intercept\",arguments[2]);break;case\"gamma\":this.attr(\"amplitude\",arguments[1]),this.attr(\"exponent\",arguments[2]),this.attr(\"offset\",arguments[2])}}})),n(e,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+\"Effect\"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments),this.result(this.attr(\"id\")+\"Out\")},inherit:SVG.Effect,extend:{}})})),n(i,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+\"Effect\"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments),this.result(this.attr(\"id\")+\"Out\")},inherit:SVG.ParentEffect,extend:{}})})),n(a,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments)},inherit:SVG.ChildEffect,extend:{}})})),SVG.extend(SVG.MergeEffect,{in:function(t){return t instanceof SVG.MergeNode?this.add(t,0):this.add(new SVG.MergeNode(t),0),this}}),SVG.extend(SVG.CompositeEffect,SVG.BlendEffect,SVG.DisplacementMapEffect,{in2:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in2\")+'\"]').get(0)||this.attr(\"in2\"):this.attr(\"in2\",t)}}),SVG.filter={sepiatone:[.343,.669,.119,0,0,.249,.626,.13,0,0,.172,.334,.111,0,0,0,0,0,1,0]}}).call(this)},25:()=>{!function(){\"use strict\";function t(t,s,r,n,o,l,c){for(var h=t.slice(s,r||c),d=n.slice(o,l||c),u=0,g={pos:[0,0],start:[0,0]},f={pos:[0,0],start:[0,0]};h[u]=e.call(g,h[u]),d[u]=e.call(f,d[u]),h[u][0]!=d[u][0]||\"M\"==h[u][0]||\"A\"==h[u][0]&&(h[u][4]!=d[u][4]||h[u][5]!=d[u][5])?(Array.prototype.splice.apply(h,[u,1].concat(a.call(g,h[u]))),Array.prototype.splice.apply(d,[u,1].concat(a.call(f,d[u])))):(h[u]=i.call(g,h[u]),d[u]=i.call(f,d[u])),++u!=h.length||u!=d.length;)u==h.length&&h.push([\"C\",g.pos[0],g.pos[1],g.pos[0],g.pos[1],g.pos[0],g.pos[1]]),u==d.length&&d.push([\"C\",f.pos[0],f.pos[1],f.pos[0],f.pos[1],f.pos[0],f.pos[1]]);return{start:h,dest:d}}function e(t){switch(t[0]){case\"z\":case\"Z\":t[0]=\"L\",t[1]=this.start[0],t[2]=this.start[1];break;case\"H\":t[0]=\"L\",t[2]=this.pos[1];break;case\"V\":t[0]=\"L\",t[2]=t[1],t[1]=this.pos[0];break;case\"T\":t[0]=\"Q\",t[3]=t[1],t[4]=t[2],t[1]=this.reflection[1],t[2]=this.reflection[0];break;case\"S\":t[0]=\"C\",t[6]=t[4],t[5]=t[3],t[4]=t[2],t[3]=t[1],t[2]=this.reflection[1],t[1]=this.reflection[0]}return t}function i(t){var e=t.length;return this.pos=[t[e-2],t[e-1]],-1!=\"SCQT\".indexOf(t[0])&&(this.reflection=[2*this.pos[0]-t[e-4],2*this.pos[1]-t[e-3]]),t}function a(t){var e=[t];switch(t[0]){case\"M\":return this.pos=this.start=[t[1],t[2]],e;case\"L\":t[5]=t[3]=t[1],t[6]=t[4]=t[2],t[1]=this.pos[0],t[2]=this.pos[1];break;case\"Q\":t[6]=t[4],t[5]=t[3],t[4]=1*t[4]/3+2*t[2]/3,t[3]=1*t[3]/3+2*t[1]/3,t[2]=1*this.pos[1]/3+2*t[2]/3,t[1]=1*this.pos[0]/3+2*t[1]/3;break;case\"A\":e=function(t,e){var i,a,s,r,n,o,l,c,h,d,u,g,f,p,x,b,v,m,y,w,k,A,S,C,P,L,O=Math.abs(e[1]),T=Math.abs(e[2]),E=e[3]%360,I=e[4],M=e[5],z=e[6],X=e[7],Y=new SVG.Point(t),D=new SVG.Point(z,X),R=[];if(0===O||0===T||Y.x===D.x&&Y.y===D.y)return[[\"C\",Y.x,Y.y,D.x,D.y,D.x,D.y]];for((a=(i=new SVG.Point((Y.x-D.x)/2,(Y.y-D.y)/2).transform((new SVG.Matrix).rotate(E))).x*i.x/(O*O)+i.y*i.y/(T*T))>1&&(O*=a=Math.sqrt(a),T*=a),s=(new SVG.Matrix).rotate(E).scale(1/O,1/T).rotate(-E),Y=Y.transform(s),o=(r=[(D=D.transform(s)).x-Y.x,D.y-Y.y])[0]*r[0]+r[1]*r[1],n=Math.sqrt(o),r[0]/=n,r[1]/=n,l=o<4?Math.sqrt(1-o/4):0,I===M&&(l*=-1),c=new SVG.Point((D.x+Y.x)/2+l*-r[1],(D.y+Y.y)/2+l*r[0]),h=new SVG.Point(Y.x-c.x,Y.y-c.y),d=new SVG.Point(D.x-c.x,D.y-c.y),u=Math.acos(h.x/Math.sqrt(h.x*h.x+h.y*h.y)),h.y<0&&(u*=-1),g=Math.acos(d.x/Math.sqrt(d.x*d.x+d.y*d.y)),d.y<0&&(g*=-1),M&&u>g&&(g+=2*Math.PI),!M&&u<g&&(g-=2*Math.PI),b=[],v=u,f=(g-u)/(p=Math.ceil(2*Math.abs(u-g)/Math.PI)),x=4*Math.tan(f/4)/3,k=0;k<=p;k++)y=Math.cos(v),m=Math.sin(v),w=new SVG.Point(c.x+y,c.y+m),b[k]=[new SVG.Point(w.x+x*m,w.y-x*y),w,new SVG.Point(w.x-x*m,w.y+x*y)],v+=f;for(b[0][0]=b[0][1].clone(),b[b.length-1][2]=b[b.length-1][1].clone(),s=(new SVG.Matrix).rotate(E).scale(O,T).rotate(-E),k=0,A=b.length;k<A;k++)b[k][0]=b[k][0].transform(s),b[k][1]=b[k][1].transform(s),b[k][2]=b[k][2].transform(s);for(k=1,A=b.length;k<A;k++)S=(w=b[k-1][2]).x,C=w.y,P=(w=b[k][0]).x,L=w.y,z=(w=b[k][1]).x,X=w.y,R.push([\"C\",S,C,P,L,z,X]);return R}(this.pos,t),t=e[0]}return t[0]=\"C\",this.pos=[t[5],t[6]],this.reflection=[2*t[5]-t[3],2*t[6]-t[4]],e}function s(t,e){if(!1===e)return!1;for(var i=e,a=t.length;i<a;++i)if(\"M\"==t[i][0])return i;return!1}SVG.extend(SVG.PathArray,{morph:function(e){for(var i=this.value,a=this.parse(e),r=0,n=0,o=!1,l=!1;!1!==r||!1!==n;){var c;o=s(i,!1!==r&&r+1),l=s(a,!1!==n&&n+1),!1===r&&(r=0==(c=new SVG.PathArray(h.start).bbox()).height||0==c.width?i.push(i[0])-1:i.push([\"M\",c.x+c.width/2,c.y+c.height/2])-1),!1===n&&(n=0==(c=new SVG.PathArray(h.dest).bbox()).height||0==c.width?a.push(a[0])-1:a.push([\"M\",c.x+c.width/2,c.y+c.height/2])-1);var h=t(i,r,o,a,n,l);i=i.slice(0,r).concat(h.start,!1===o?[]:i.slice(o)),a=a.slice(0,n).concat(h.dest,!1===l?[]:a.slice(l)),r=!1!==o&&r+h.start.length,n=!1!==l&&n+h.dest.length}return this.value=i,this.destination=new SVG.PathArray,this.destination.value=a,this}})}()},882:()=>{!function(){\"use strict\";(function(){function t(t){t.remember(\"_resizeHandler\",this),this.el=t,this.parameters={},this.lastUpdateCall=null,this.p=t.doc().node.createSVGPoint()}t.prototype.transformPoint=function(t,e,i){return this.p.x=t-(this.offset.x-window.pageXOffset),this.p.y=e-(this.offset.y-window.pageYOffset),this.p.matrixTransform(i||this.m)},t.prototype._extractPosition=function(t){return{x:null!=t.clientX?t.clientX:t.touches[0].clientX,y:null!=t.clientY?t.clientY:t.touches[0].clientY}},t.prototype.init=function(t){var e=this;if(this.stop(),\"stop\"!==t){for(var i in this.options={},this.el.resize.defaults)this.options[i]=this.el.resize.defaults[i],void 0!==t[i]&&(this.options[i]=t[i]);this.el.on(\"lt.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rt.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rb.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"lb.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"t.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"r.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"b.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"l.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rot.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"point.resize\",(function(t){e.resize(t||window.event)})),this.update()}},t.prototype.stop=function(){return this.el.off(\"lt.resize\"),this.el.off(\"rt.resize\"),this.el.off(\"rb.resize\"),this.el.off(\"lb.resize\"),this.el.off(\"t.resize\"),this.el.off(\"r.resize\"),this.el.off(\"b.resize\"),this.el.off(\"l.resize\"),this.el.off(\"rot.resize\"),this.el.off(\"point.resize\"),this},t.prototype.resize=function(t){var e=this;this.m=this.el.node.getScreenCTM().inverse(),this.offset={x:window.pageXOffset,y:window.pageYOffset};var i=this._extractPosition(t.detail.event);if(this.parameters={type:this.el.type,p:this.transformPoint(i.x,i.y),x:t.detail.x,y:t.detail.y,box:this.el.bbox(),rotation:this.el.transform().rotation},\"text\"===this.el.type&&(this.parameters.fontSize=this.el.attr()[\"font-size\"]),void 0!==t.detail.i){var a=this.el.array().valueOf();this.parameters.i=t.detail.i,this.parameters.pointCoords=[a[t.detail.i][0],a[t.detail.i][1]]}switch(t.type){case\"lt\":this.calc=function(t,e){var i=this.snapToGrid(t,e);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y+i[1]).size(this.parameters.box.width-i[0],this.parameters.box.height-i[1])}};break;case\"rt\":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).size(this.parameters.box.width+i[0],this.parameters.box.height-i[1])}};break;case\"rb\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x,this.parameters.box.y).size(this.parameters.box.width+i[0],this.parameters.box.height+i[1])}};break;case\"lb\":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).size(this.parameters.box.width-i[0],this.parameters.box.height+i[1])}};break;case\"t\":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).height(this.parameters.box.height-i[1])}};break;case\"r\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).width(this.parameters.box.width+i[0])}};break;case\"b\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).height(this.parameters.box.height+i[1])}};break;case\"l\":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).width(this.parameters.box.width-i[0])}};break;case\"rot\":this.calc=function(t,e){var i=t+this.parameters.p.x,a=e+this.parameters.p.y,s=Math.atan2(this.parameters.p.y-this.parameters.box.y-this.parameters.box.height/2,this.parameters.p.x-this.parameters.box.x-this.parameters.box.width/2),r=Math.atan2(a-this.parameters.box.y-this.parameters.box.height/2,i-this.parameters.box.x-this.parameters.box.width/2),n=this.parameters.rotation+180*(r-s)/Math.PI+this.options.snapToAngle/2;this.el.center(this.parameters.box.cx,this.parameters.box.cy).rotate(n-n%this.options.snapToAngle,this.parameters.box.cx,this.parameters.box.cy)};break;case\"point\":this.calc=function(t,e){var i=this.snapToGrid(t,e,this.parameters.pointCoords[0],this.parameters.pointCoords[1]),a=this.el.array().valueOf();a[this.parameters.i][0]=this.parameters.pointCoords[0]+i[0],a[this.parameters.i][1]=this.parameters.pointCoords[1]+i[1],this.el.plot(a)}}this.el.fire(\"resizestart\",{dx:this.parameters.x,dy:this.parameters.y,event:t}),SVG.on(window,\"touchmove.resize\",(function(t){e.update(t||window.event)})),SVG.on(window,\"touchend.resize\",(function(){e.done()})),SVG.on(window,\"mousemove.resize\",(function(t){e.update(t||window.event)})),SVG.on(window,\"mouseup.resize\",(function(){e.done()}))},t.prototype.update=function(t){if(t){var e=this._extractPosition(t),i=this.transformPoint(e.x,e.y),a=i.x-this.parameters.p.x,s=i.y-this.parameters.p.y;this.lastUpdateCall=[a,s],this.calc(a,s),this.el.fire(\"resizing\",{dx:a,dy:s,event:t})}else this.lastUpdateCall&&this.calc(this.lastUpdateCall[0],this.lastUpdateCall[1])},t.prototype.done=function(){this.lastUpdateCall=null,SVG.off(window,\"mousemove.resize\"),SVG.off(window,\"mouseup.resize\"),SVG.off(window,\"touchmove.resize\"),SVG.off(window,\"touchend.resize\"),this.el.fire(\"resizedone\")},t.prototype.snapToGrid=function(t,e,i,a){var s;return void 0!==a?s=[(i+t)%this.options.snapToGrid,(a+e)%this.options.snapToGrid]:(i=null==i?3:i,s=[(this.parameters.box.x+t+(1&i?0:this.parameters.box.width))%this.options.snapToGrid,(this.parameters.box.y+e+(2&i?0:this.parameters.box.height))%this.options.snapToGrid]),t<0&&(s[0]-=this.options.snapToGrid),e<0&&(s[1]-=this.options.snapToGrid),t-=Math.abs(s[0])<this.options.snapToGrid/2?s[0]:s[0]-(t<0?-this.options.snapToGrid:this.options.snapToGrid),e-=Math.abs(s[1])<this.options.snapToGrid/2?s[1]:s[1]-(e<0?-this.options.snapToGrid:this.options.snapToGrid),this.constraintToBox(t,e,i,a)},t.prototype.constraintToBox=function(t,e,i,a){var s,r,n=this.options.constraint||{};return void 0!==a?(s=i,r=a):(s=this.parameters.box.x+(1&i?0:this.parameters.box.width),r=this.parameters.box.y+(2&i?0:this.parameters.box.height)),void 0!==n.minX&&s+t<n.minX&&(t=n.minX-s),void 0!==n.maxX&&s+t>n.maxX&&(t=n.maxX-s),void 0!==n.minY&&r+e<n.minY&&(e=n.minY-r),void 0!==n.maxY&&r+e>n.maxY&&(e=n.maxY-r),[t,e]},t.prototype.checkAspectRatio=function(t,e){if(!this.options.saveAspectRatio)return t;var i=t.slice(),a=this.parameters.box.width/this.parameters.box.height,s=this.parameters.box.width+t[0],r=this.parameters.box.height-t[1],n=s/r;return n<a?(i[1]=s/a-this.parameters.box.height,e&&(i[1]=-i[1])):n>a&&(i[0]=this.parameters.box.width-r*a,e&&(i[0]=-i[0])),i},SVG.extend(SVG.Element,{resize:function(e){return(this.remember(\"_resizeHandler\")||new t(this)).init(e||{}),this}}),SVG.Element.prototype.resize.defaults={snapToAngle:.1,snapToGrid:1,constraint:{},saveAspectRatio:!1}}).call(this)}()},769:()=>{function t(e){return t=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},t(e)}!function(){\"use strict\";function e(t){this.el=t,t.remember(\"_selectHandler\",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1},this.pointsList={lt:[0,0],rt:[\"width\",0],rb:[\"width\",\"height\"],lb:[0,\"height\"],t:[\"width\",0],r:[\"width\",\"height\"],b:[\"width\",\"height\"],l:[0,\"height\"]},this.pointCoord=function(t,e,i){var a=\"string\"!=typeof t?t:e[t];return i?a/2:a},this.pointCoords=function(t,e){var i=this.pointsList[t];return{x:this.pointCoord(i[0],e,\"t\"===t||\"b\"===t),y:this.pointCoord(i[1],e,\"r\"===t||\"l\"===t)}}}e.prototype.init=function(t,e){var i=this.el.bbox();this.options={};var a=this.el.selectize.defaults.points;for(var s in this.el.selectize.defaults)this.options[s]=this.el.selectize.defaults[s],void 0!==e[s]&&(this.options[s]=e[s]);var r=[\"points\",\"pointsExclude\"];for(var s in r){var n=this.options[r[s]];\"string\"==typeof n?n=n.length>0?n.split(/\\s*,\\s*/i):[]:\"boolean\"==typeof n&&\"points\"===r[s]&&(n=n?a:[]),this.options[r[s]]=n}this.options.points=[a,this.options.points].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)>-1}))})),this.options.points=[this.options.points,this.options.pointsExclude].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)<0}))})),this.parent=this.el.parent(),this.nested=this.nested||this.parent.group(),this.nested.matrix(new SVG.Matrix(this.el).translate(i.x,i.y)),this.options.deepSelect&&-1!==[\"line\",\"polyline\",\"polygon\"].indexOf(this.el.type)?this.selectPoints(t):this.selectRect(t),this.observe(),this.cleanup()},e.prototype.selectPoints=function(t){return this.pointSelection.isSelected=t,this.pointSelection.set||(this.pointSelection.set=this.parent.set(),this.drawPoints()),this},e.prototype.getPointArray=function(){var t=this.el.bbox();return this.el.array().valueOf().map((function(e){return[e[0]-t.x,e[1]-t.y]}))},e.prototype.drawPoints=function(){for(var t=this,e=this.getPointArray(),i=0,a=e.length;i<a;++i){var s=function(e){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;t.el.fire(\"point\",{x:a,y:s,i:e,event:i})}}(i),r=this.drawPoint(e[i][0],e[i][1]).addClass(this.options.classPoints).addClass(this.options.classPoints+\"_point\").on(\"touchstart\",s).on(\"mousedown\",s);this.pointSelection.set.add(r)}},e.prototype.drawPoint=function(t,e){var i=this.options.pointType;switch(i){case\"circle\":return this.drawCircle(t,e);case\"rect\":return this.drawRect(t,e);default:if(\"function\"==typeof i)return i.call(this,t,e);throw new Error(\"Unknown \"+i+\" point type!\")}},e.prototype.drawCircle=function(t,e){return this.nested.circle(this.options.pointSize).center(t,e)},e.prototype.drawRect=function(t,e){return this.nested.rect(this.options.pointSize,this.options.pointSize).center(t,e)},e.prototype.updatePointSelection=function(){var t=this.getPointArray();this.pointSelection.set.each((function(e){this.cx()===t[e][0]&&this.cy()===t[e][1]||this.center(t[e][0],t[e][1])}))},e.prototype.updateRectSelection=function(){var t=this,e=this.el.bbox();if(this.rectSelection.set.get(0).attr({width:e.width,height:e.height}),this.options.points.length&&this.options.points.map((function(i,a){var s=t.pointCoords(i,e);t.rectSelection.set.get(a+1).center(s.x,s.y)})),this.options.rotationPoint){var i=this.rectSelection.set.length();this.rectSelection.set.get(i-1).center(e.width/2,20)}},e.prototype.selectRect=function(t){var e=this,i=this.el.bbox();function a(t){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;e.el.fire(t,{x:a,y:s,event:i})}}if(this.rectSelection.isSelected=t,this.rectSelection.set=this.rectSelection.set||this.parent.set(),this.rectSelection.set.get(0)||this.rectSelection.set.add(this.nested.rect(i.width,i.height).addClass(this.options.classRect)),this.options.points.length&&this.rectSelection.set.length()<2&&(this.options.points.map((function(t,s){var r=e.pointCoords(t,i),n=e.drawPoint(r.x,r.y).attr(\"class\",e.options.classPoints+\"_\"+t).on(\"mousedown\",a(t)).on(\"touchstart\",a(t));e.rectSelection.set.add(n)})),this.rectSelection.set.each((function(){this.addClass(e.options.classPoints)}))),this.options.rotationPoint&&(this.options.points&&!this.rectSelection.set.get(9)||!this.options.points&&!this.rectSelection.set.get(1))){var s=function(t){(t=t||window.event).preventDefault?t.preventDefault():t.returnValue=!1,t.stopPropagation();var i=t.pageX||t.touches[0].pageX,a=t.pageY||t.touches[0].pageY;e.el.fire(\"rot\",{x:i,y:a,event:t})},r=this.drawPoint(i.width/2,20).attr(\"class\",this.options.classPoints+\"_rot\").on(\"touchstart\",s).on(\"mousedown\",s);this.rectSelection.set.add(r)}},e.prototype.handler=function(){var t=this.el.bbox();this.nested.matrix(new SVG.Matrix(this.el).translate(t.x,t.y)),this.rectSelection.isSelected&&this.updateRectSelection(),this.pointSelection.isSelected&&this.updatePointSelection()},e.prototype.observe=function(){var t=this;if(MutationObserver)if(this.rectSelection.isSelected||this.pointSelection.isSelected)this.observerInst=this.observerInst||new MutationObserver((function(){t.handler()})),this.observerInst.observe(this.el.node,{attributes:!0});else try{this.observerInst.disconnect(),delete this.observerInst}catch(t){}else this.el.off(\"DOMAttrModified.select\"),(this.rectSelection.isSelected||this.pointSelection.isSelected)&&this.el.on(\"DOMAttrModified.select\",(function(){t.handler()}))},e.prototype.cleanup=function(){!this.rectSelection.isSelected&&this.rectSelection.set&&(this.rectSelection.set.each((function(){this.remove()})),this.rectSelection.set.clear(),delete this.rectSelection.set),!this.pointSelection.isSelected&&this.pointSelection.set&&(this.pointSelection.set.each((function(){this.remove()})),this.pointSelection.set.clear(),delete this.pointSelection.set),this.pointSelection.isSelected||this.rectSelection.isSelected||(this.nested.remove(),delete this.nested)},SVG.extend(SVG.Element,{selectize:function(i,a){return\"object\"===t(i)&&(a=i,i=!0),(this.remember(\"_selectHandler\")||new e(this)).init(void 0===i||i,a||{}),this}}),SVG.Element.prototype.selectize.defaults={points:[\"lt\",\"rt\",\"rb\",\"lb\",\"t\",\"r\",\"b\",\"l\"],pointsExclude:[],classRect:\"svg_select_boundingRect\",classPoints:\"svg_select_points\",pointSize:7,rotationPoint:!0,deepSelect:!1,pointType:\"circle\"}}()},978:(t,e,i)=>{\"use strict\";function a(t){return a=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},a(t)}function s(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}i.r(e),i.d(e,{default:()=>qa});var r=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t)}var e,i,r;return e=t,r=[{key:\"bind\",value:function(t,e){return function(){return t.apply(e,arguments)}}},{key:\"isObject\",value:function(t){return t&&\"object\"===a(t)&&!Array.isArray(t)&&null!=t}},{key:\"is\",value:function(t,e){return Object.prototype.toString.call(e)===\"[object \"+t+\"]\"}},{key:\"listToArray\",value:function(t){var e,i=[];for(e=0;e<t.length;e++)i[e]=t[e];return i}},{key:\"extend\",value:function(t,e){var i=this;\"function\"!=typeof Object.assign&&(Object.assign=function(t){if(null==t)throw new TypeError(\"Cannot convert undefined or null to object\");for(var e=Object(t),i=1;i<arguments.length;i++){var a=arguments[i];if(null!=a)for(var s in a)a.hasOwnProperty(s)&&(e[s]=a[s])}return e});var a=Object.assign({},t);return this.isObject(t)&&this.isObject(e)&&Object.keys(e).forEach((function(s){i.isObject(e[s])&&s in t?a[s]=i.extend(t[s],e[s]):Object.assign(a,function(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}({},s,e[s]))})),a}},{key:\"extendArray\",value:function(e,i){var a=[];return e.map((function(e){a.push(t.extend(i,e))})),a}},{key:\"monthMod\",value:function(t){return t%12}},{key:\"clone\",value:function(e){if(t.is(\"Array\",e)){for(var i=[],s=0;s<e.length;s++)i[s]=this.clone(e[s]);return i}if(t.is(\"Null\",e))return null;if(t.is(\"Date\",e))return e;if(\"object\"===a(e)){var r={};for(var n in e)e.hasOwnProperty(n)&&(r[n]=this.clone(e[n]));return r}return e}},{key:\"log10\",value:function(t){return Math.log(t)/Math.LN10}},{key:\"roundToBase10\",value:function(t){return Math.pow(10,Math.floor(Math.log10(t)))}},{key:\"roundToBase\",value:function(t,e){return Math.pow(e,Math.floor(Math.log(t)/Math.log(e)))}},{key:\"parseNumber\",value:function(t){return null===t?t:parseFloat(t)}},{key:\"randomId\",value:function(){return(Math.random()+1).toString(36).substring(4)}},{key:\"noExponents\",value:function(t){var e=String(t).split(/[eE]/);if(1===e.length)return e[0];var i=\"\",a=t<0?\"-\":\"\",s=e[0].replace(\".\",\"\"),r=Number(e[1])+1;if(r<0){for(i=a+\"0.\";r++;)i+=\"0\";return i+s.replace(/^-/,\"\")}for(r-=s.length;r--;)i+=\"0\";return s+i}},{key:\"getDimensions\",value:function(t){var e=getComputedStyle(t,null),i=t.clientHeight,a=t.clientWidth;return i-=parseFloat(e.paddingTop)+parseFloat(e.paddingBottom),[a-=parseFloat(e.paddingLeft)+parseFloat(e.paddingRight),i]}},{key:\"getBoundingClientRect\",value:function(t){var e=t.getBoundingClientRect();return{top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:t.clientWidth,height:t.clientHeight,x:e.left,y:e.top}}},{key:\"getLargestStringFromArr\",value:function(t){return t.reduce((function(t,e){return Array.isArray(e)&&(e=e.reduce((function(t,e){return t.length>e.length?t:e}))),t.length>e.length?t:e}),0)}},{key:\"hexToRgba\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"#999999\",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.6;\"#\"!==t.substring(0,1)&&(t=\"#999999\");var i=t.replace(\"#\",\"\");i=i.match(new RegExp(\"(.{\"+i.length/3+\"})\",\"g\"));for(var a=0;a<i.length;a++)i[a]=parseInt(1===i[a].length?i[a]+i[a]:i[a],16);return void 0!==e&&i.push(e),\"rgba(\"+i.join(\",\")+\")\"}},{key:\"getOpacityFromRGBA\",value:function(t){return parseFloat(t.replace(/^.*,(.+)\\)/,\"$1\"))}},{key:\"rgb2hex\",value:function(t){return(t=t.match(/^rgba?[\\s+]?\\([\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?/i))&&4===t.length?\"#\"+(\"0\"+parseInt(t[1],10).toString(16)).slice(-2)+(\"0\"+parseInt(t[2],10).toString(16)).slice(-2)+(\"0\"+parseInt(t[3],10).toString(16)).slice(-2):\"\"}},{key:\"isColorHex\",value:function(t){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i.test(t)}},{key:\"getPolygonPos\",value:function(t,e){for(var i=[],a=2*Math.PI/e,s=0;s<e;s++){var r={};r.x=t*Math.sin(s*a),r.y=-t*Math.cos(s*a),i.push(r)}return i}},{key:\"polarToCartesian\",value:function(t,e,i,a){var s=(a-90)*Math.PI/180;return{x:t+i*Math.cos(s),y:e+i*Math.sin(s)}}},{key:\"escapeString\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"x\",i=t.toString().slice();return i.replace(/[` ~!@#$%^&*()|+\\=?;:'\",.<>{}[\\]\\\\/]/gi,e)}},{key:\"negToZero\",value:function(t){return t<0?0:t}},{key:\"moveIndexInArray\",value:function(t,e,i){if(i>=t.length)for(var a=i-t.length+1;a--;)t.push(void 0);return t.splice(i,0,t.splice(e,1)[0]),t}},{key:\"extractNumber\",value:function(t){return parseFloat(t.replace(/[^\\d.]*/g,\"\"))}},{key:\"findAncestor\",value:function(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}},{key:\"setELstyles\",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&(t.style.key=e[i])}},{key:\"isNumber\",value:function(t){return!isNaN(t)&&parseFloat(Number(t))===t&&!isNaN(parseInt(t,10))}},{key:\"isFloat\",value:function(t){return Number(t)===t&&t%1!=0}},{key:\"isSafari\",value:function(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}},{key:\"isFirefox\",value:function(){return navigator.userAgent.toLowerCase().indexOf(\"firefox\")>-1}},{key:\"isIE11\",value:function(){if(-1!==window.navigator.userAgent.indexOf(\"MSIE\")||window.navigator.appVersion.indexOf(\"Trident/\")>-1)return!0}},{key:\"isIE\",value:function(){var t=window.navigator.userAgent,e=t.indexOf(\"MSIE \");if(e>0)return parseInt(t.substring(e+5,t.indexOf(\".\",e)),10);if(t.indexOf(\"Trident/\")>0){var i=t.indexOf(\"rv:\");return parseInt(t.substring(i+3,t.indexOf(\".\",i)),10)}var a=t.indexOf(\"Edge/\");return a>0&&parseInt(t.substring(a+5,t.indexOf(\".\",a)),10)}}],(i=[{key:\"shadeRGBColor\",value:function(t,e){var i=e.split(\",\"),a=t<0?0:255,s=t<0?-1*t:t,r=parseInt(i[0].slice(4),10),n=parseInt(i[1],10),o=parseInt(i[2],10);return\"rgb(\"+(Math.round((a-r)*s)+r)+\",\"+(Math.round((a-n)*s)+n)+\",\"+(Math.round((a-o)*s)+o)+\")\"}},{key:\"shadeHexColor\",value:function(t,e){var i=parseInt(e.slice(1),16),a=t<0?0:255,s=t<0?-1*t:t,r=i>>16,n=i>>8&255,o=255&i;return\"#\"+(16777216+65536*(Math.round((a-r)*s)+r)+256*(Math.round((a-n)*s)+n)+(Math.round((a-o)*s)+o)).toString(16).slice(1)}},{key:\"shadeColor\",value:function(e,i){return t.isColorHex(i)?this.shadeHexColor(e,i):this.shadeRGBColor(e,i)}}])&&s(e.prototype,i),r&&s(e,r),t}();const n=r;function o(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var l=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.setEasingFunctions()}var e,i;return e=t,(i=[{key:\"setEasingFunctions\",value:function(){var t;if(!this.w.globals.easing){switch(this.w.config.chart.animations.easing){case\"linear\":t=\"-\";break;case\"easein\":t=\"<\";break;case\"easeout\":t=\">\";break;case\"easeinout\":default:t=\"<>\";break;case\"swing\":t=function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1};break;case\"bounce\":t=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375};break;case\"elastic\":t=function(t){return t===!!t?t:Math.pow(2,-10*t)*Math.sin((t-.075)*(2*Math.PI)/.3)+1}}this.w.globals.easing=t}}},{key:\"animateLine\",value:function(t,e,i,a){t.attr(e).animate(a).attr(i)}},{key:\"animateMarker\",value:function(t,e,i,a,s,r){e||(e=0),t.attr({r:e,width:e,height:e}).animate(a,s).attr({r:i,width:i.width,height:i.height}).afterAll((function(){r()}))}},{key:\"animateCircle\",value:function(t,e,i,a,s){t.attr({r:e.r,cx:e.cx,cy:e.cy}).animate(a,s).attr({r:i.r,cx:i.cx,cy:i.cy})}},{key:\"animateRect\",value:function(t,e,i,a,s){t.attr(e).animate(a).attr(i).afterAll((function(){return s()}))}},{key:\"animatePathsGradually\",value:function(t){var e=t.el,i=t.realIndex,a=t.j,s=t.fill,r=t.pathFrom,n=t.pathTo,o=t.speed,l=t.delay,c=this.w,h=0;c.config.chart.animations.animateGradually.enabled&&(h=c.config.chart.animations.animateGradually.delay),c.config.chart.animations.dynamicAnimation.enabled&&c.globals.dataChanged&&\"bar\"!==c.config.chart.type&&(h=0),this.morphSVG(e,i,a,\"line\"!==c.config.chart.type||c.globals.comboCharts?s:\"stroke\",r,n,o,l*h)}},{key:\"showDelayedElements\",value:function(){this.w.globals.delayedElements.forEach((function(t){t.el.classList.remove(\"apexcharts-element-hidden\")}))}},{key:\"animationCompleted\",value:function(t){var e=this.w;e.globals.animationEnded||(e.globals.animationEnded=!0,this.showDelayedElements(),\"function\"==typeof e.config.chart.events.animationEnd&&e.config.chart.events.animationEnd(this.ctx,{el:t,w:e}))}},{key:\"morphSVG\",value:function(t,e,i,a,s,r,o,l){var c=this,h=this.w;s||(s=t.attr(\"pathFrom\")),r||(r=t.attr(\"pathTo\"));var d=function(t){return\"radar\"===h.config.chart.type&&(o=1),\"M 0 \".concat(h.globals.gridHeight)};(!s||s.indexOf(\"undefined\")>-1||s.indexOf(\"NaN\")>-1)&&(s=d()),(!r||r.indexOf(\"undefined\")>-1||r.indexOf(\"NaN\")>-1)&&(r=d()),h.globals.shouldAnimate||(o=1),t.plot(s).animate(1,h.globals.easing,l).plot(s).animate(o,h.globals.easing,l).plot(r).afterAll((function(){n.isNumber(i)?i===h.globals.series[h.globals.maxValsInArrayIndex].length-2&&h.globals.shouldAnimate&&c.animationCompleted(t):\"none\"!==a&&h.globals.shouldAnimate&&(!h.globals.comboCharts&&e===h.globals.series.length-1||h.globals.comboCharts)&&c.animationCompleted(t),c.showDelayedElements()}))}}])&&o(e.prototype,i),t}();function c(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var h=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,i=[{key:\"getDefaultFilter\",value:function(t,e){var i=this.w;t.unfilter(!0),(new window.SVG.Filter).size(\"120%\",\"180%\",\"-5%\",\"-40%\"),\"none\"!==i.config.states.normal.filter?this.applyFilter(t,e,i.config.states.normal.filter.type,i.config.states.normal.filter.value):i.config.chart.dropShadow.enabled&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:\"addNormalFilter\",value:function(t,e){var i=this.w;i.config.chart.dropShadow.enabled&&!t.node.classList.contains(\"apexcharts-marker\")&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:\"addLightenFilter\",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;t.unfilter(!0),new window.SVG.Filter,t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:\"linear\",slope:1.5,intercept:r}})})),t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)}},{key:\"addDarkenFilter\",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;t.unfilter(!0),new window.SVG.Filter,t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:\"linear\",slope:r}})})),t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)}},{key:\"applyFilter\",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:.5;switch(i){case\"none\":this.addNormalFilter(t,e);break;case\"lighten\":this.addLightenFilter(t,e,{intensity:a});break;case\"darken\":this.addDarkenFilter(t,e,{intensity:a})}}},{key:\"addShadow\",value:function(t,e,i){var a=i.blur,s=i.top,r=i.left,n=i.color,o=i.opacity,l=t.flood(Array.isArray(n)?n[e]:n,o).composite(t.sourceAlpha,\"in\").offset(r,s).gaussianBlur(a).merge(t.source);return t.blend(t.source,l)}},{key:\"dropShadow\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=e.top,s=e.left,r=e.blur,o=e.color,l=e.opacity,c=e.noUserSpaceOnUse,h=this.w;return t.unfilter(!0),n.isIE()&&\"radialBar\"===h.config.chart.type||(o=Array.isArray(o)?o[i]:o,t.filter((function(t){var e;e=n.isSafari()||n.isFirefox()||n.isIE()?t.flood(o,l).composite(t.sourceAlpha,\"in\").offset(s,a).gaussianBlur(r):t.flood(o,l).composite(t.sourceAlpha,\"in\").offset(s,a).gaussianBlur(r).merge(t.source),t.blend(t.source,e)})),c||t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)),t}},{key:\"setSelectionFilter\",value:function(t,e,i){var a=this.w;if(void 0!==a.globals.selectedDataPoints[e]&&a.globals.selectedDataPoints[e].indexOf(i)>-1){t.node.setAttribute(\"selected\",!0);var s=a.config.states.active.filter;\"none\"!==s&&this.applyFilter(t,e,s.type,s.value)}}},{key:\"_scaleFilterSize\",value:function(t){!function(e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}({width:\"200%\",height:\"200%\",x:\"-50%\",y:\"-50%\"})}}],i&&c(e.prototype,i),t}();const d=h;function u(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function g(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?u(Object(i),!0).forEach((function(e){f(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):u(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function f(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function p(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var x=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i,a;return e=t,i=[{key:\"roundPathCorners\",value:function(t,e){function i(t,e,i){var s=e.x-t.x,r=e.y-t.y,n=Math.sqrt(s*s+r*r);return a(t,e,Math.min(1,i/n))}function a(t,e,i){return{x:t.x+(e.x-t.x)*i,y:t.y+(e.y-t.y)*i}}function s(t,e){t.length>2&&(t[t.length-2]=e.x,t[t.length-1]=e.y)}function r(t){return{x:parseFloat(t[t.length-2]),y:parseFloat(t[t.length-1])}}var n=t.split(/[,\\s]/).reduce((function(t,e){var i=e.match(\"([a-zA-Z])(.+)\");return i?(t.push(i[1]),t.push(i[2])):t.push(e),t}),[]).reduce((function(t,e){return parseFloat(e)==e&&t.length?t[t.length-1].push(e):t.push([e]),t}),[]),o=[];if(n.length>1){var l=r(n[0]),c=null;\"Z\"==n[n.length-1][0]&&n[0].length>2&&(c=[\"L\",l.x,l.y],n[n.length-1]=c),o.push(n[0]);for(var h=1;h<n.length;h++){var d=o[o.length-1],u=n[h],g=u==c?n[1]:n[h+1];if(g&&d&&d.length>2&&\"L\"==u[0]&&g.length>2&&\"L\"==g[0]){var f,p,x=r(d),b=r(u),v=r(g);f=i(b,x,e),p=i(b,v,e),s(u,f),u.origPoint=b,o.push(u);var m=a(f,b,.5),y=a(b,p,.5),w=[\"C\",m.x,m.y,y.x,y.y,p.x,p.y];w.origPoint=b,o.push(w)}else o.push(u)}if(c){var k=r(o[o.length-1]);o.push([\"Z\"]),s(o[0],k)}}else o=n;return o.reduce((function(t,e){return t+e.join(\" \")+\" \"}),\"\")}},{key:\"drawLine\",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:\"#a8a8a8\",r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,n=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,o=arguments.length>7&&void 0!==arguments[7]?arguments[7]:\"butt\",l=this.w,c=l.globals.dom.Paper.line().attr({x1:t,y1:e,x2:i,y2:a,stroke:s,\"stroke-dasharray\":r,\"stroke-width\":n,\"stroke-linecap\":o});return c}},{key:\"drawRect\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:\"#fefefe\",n=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1,o=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,l=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,c=arguments.length>9&&void 0!==arguments[9]?arguments[9]:0,h=this.w,d=h.globals.dom.Paper.rect();return d.attr({x:t,y:e,width:i>0?i:0,height:a>0?a:0,rx:s,ry:s,opacity:n,\"stroke-width\":null!==o?o:0,stroke:null!==l?l:\"none\",\"stroke-dasharray\":c}),d.node.setAttribute(\"fill\",r),d}},{key:\"drawPolygon\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"#e1e1e1\",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\"none\",s=this.w,r=s.globals.dom.Paper.polygon(t).attr({fill:a,stroke:e,\"stroke-width\":i});return r}},{key:\"drawCircle\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w;t<0&&(t=0);var a=i.globals.dom.Paper.circle(2*t);return null!==e&&a.attr(e),a}},{key:\"drawPath\",value:function(t){var e=t.d,i=void 0===e?\"\":e,a=t.stroke,s=void 0===a?\"#a8a8a8\":a,r=t.strokeWidth,n=void 0===r?1:r,o=t.fill,l=t.fillOpacity,c=void 0===l?1:l,h=t.strokeOpacity,d=void 0===h?1:h,u=t.classes,g=t.strokeLinecap,f=void 0===g?null:g,p=t.strokeDashArray,x=void 0===p?0:p,b=this.w;return null===f&&(f=b.config.stroke.lineCap),(i.indexOf(\"undefined\")>-1||i.indexOf(\"NaN\")>-1)&&(i=\"M 0 \".concat(b.globals.gridHeight)),b.globals.dom.Paper.path(i).attr({fill:o,\"fill-opacity\":c,stroke:s,\"stroke-opacity\":d,\"stroke-linecap\":f,\"stroke-width\":n,\"stroke-dasharray\":x,class:u})}},{key:\"group\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=e.globals.dom.Paper.group();return null!==t&&i.attr(t),i}},{key:\"move\",value:function(t,e){return[\"M\",t,e].join(\" \")}},{key:\"line\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=null;return null===i?a=[\" L\",t,e].join(\" \"):\"H\"===i?a=[\" H\",t].join(\" \"):\"V\"===i&&(a=[\" V\",e].join(\" \")),a}},{key:\"curve\",value:function(t,e,i,a,s,r){return[\"C\",t,e,i,a,s,r].join(\" \")}},{key:\"quadraticCurve\",value:function(t,e,i,a){return[\"Q\",t,e,i,a].join(\" \")}},{key:\"arc\",value:function(t,e,i,a,s,r,n){var o=arguments.length>7&&void 0!==arguments[7]&&arguments[7],l=\"A\";o&&(l=\"a\");var c=[l,t,e,i,a,s,r,n].join(\" \");return c}},{key:\"renderPaths\",value:function(t){var e,i=t.j,a=t.realIndex,s=t.pathFrom,r=t.pathTo,n=t.stroke,o=t.strokeWidth,c=t.strokeLinecap,h=t.fill,u=t.animationDelay,f=t.initialSpeed,p=t.dataChangeSpeed,x=t.className,b=t.shouldClipToGrid,v=void 0===b||b,m=t.bindEventsOnPaths,y=void 0===m||m,w=t.drawShadow,k=void 0===w||w,A=this.w,S=new d(this.ctx),C=new l(this.ctx),P=this.w.config.chart.animations.enabled,L=P&&this.w.config.chart.animations.dynamicAnimation.enabled,O=!!(P&&!A.globals.resized||L&&A.globals.dataChanged&&A.globals.shouldAnimate);O?e=s:(e=r,A.globals.animationEnded=!0);var T,E=A.config.stroke.dashArray;T=Array.isArray(E)?E[a]:A.config.stroke.dashArray;var I=this.drawPath({d:e,stroke:n,strokeWidth:o,fill:h,fillOpacity:1,classes:x,strokeLinecap:c,strokeDashArray:T});if(I.attr(\"index\",a),v&&I.attr({\"clip-path\":\"url(#gridRectMask\".concat(A.globals.cuid,\")\")}),\"none\"!==A.config.states.normal.filter.type)S.getDefaultFilter(I,a);else if(A.config.chart.dropShadow.enabled&&k&&(!A.config.chart.dropShadow.enabledOnSeries||A.config.chart.dropShadow.enabledOnSeries&&-1!==A.config.chart.dropShadow.enabledOnSeries.indexOf(a))){var M=A.config.chart.dropShadow;S.dropShadow(I,M,a)}y&&(I.node.addEventListener(\"mouseenter\",this.pathMouseEnter.bind(this,I)),I.node.addEventListener(\"mouseleave\",this.pathMouseLeave.bind(this,I)),I.node.addEventListener(\"mousedown\",this.pathMouseDown.bind(this,I))),I.attr({pathTo:r,pathFrom:s});var z={el:I,j:i,realIndex:a,pathFrom:s,pathTo:r,fill:h,strokeWidth:o,delay:u};return!P||A.globals.resized||A.globals.dataChanged?!A.globals.resized&&A.globals.dataChanged||C.showDelayedElements():C.animatePathsGradually(g(g({},z),{},{speed:f})),A.globals.dataChanged&&L&&O&&C.animatePathsGradually(g(g({},z),{},{speed:p})),I}},{key:\"drawPattern\",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\"#a8a8a8\",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=this.w,n=r.globals.dom.Paper.pattern(e,i,(function(r){\"horizontalLines\"===t?r.line(0,0,i,0).stroke({color:a,width:s+1}):\"verticalLines\"===t?r.line(0,0,0,e).stroke({color:a,width:s+1}):\"slantedLines\"===t?r.line(0,0,e,i).stroke({color:a,width:s}):\"squares\"===t?r.rect(e,i).fill(\"none\").stroke({color:a,width:s}):\"circles\"===t&&r.circle(e).fill(\"none\").stroke({color:a,width:s})}));return n}},{key:\"drawGradient\",value:function(t,e,i,a,s){var r,o=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,l=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,c=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:0,d=this.w;e.length<9&&0===e.indexOf(\"#\")&&(e=n.hexToRgba(e,a)),i.length<9&&0===i.indexOf(\"#\")&&(i=n.hexToRgba(i,s));var u=0,g=1,f=1,p=null;null!==l&&(u=void 0!==l[0]?l[0]/100:0,g=void 0!==l[1]?l[1]/100:1,f=void 0!==l[2]?l[2]/100:1,p=void 0!==l[3]?l[3]/100:null);var x=!(\"donut\"!==d.config.chart.type&&\"pie\"!==d.config.chart.type&&\"polarArea\"!==d.config.chart.type&&\"bubble\"!==d.config.chart.type);if(r=null===c||0===c.length?d.globals.dom.Paper.gradient(x?\"radial\":\"linear\",(function(t){t.at(u,e,a),t.at(g,i,s),t.at(f,i,s),null!==p&&t.at(p,e,a)})):d.globals.dom.Paper.gradient(x?\"radial\":\"linear\",(function(t){(Array.isArray(c[h])?c[h]:c).forEach((function(e){t.at(e.offset/100,e.color,e.opacity)}))})),x){var b=d.globals.gridWidth/2,v=d.globals.gridHeight/2;\"bubble\"!==d.config.chart.type?r.attr({gradientUnits:\"userSpaceOnUse\",cx:b,cy:v,r:o}):r.attr({cx:.5,cy:.5,r:.8,fx:.2,fy:.2})}else\"vertical\"===t?r.from(0,0).to(0,1):\"diagonal\"===t?r.from(0,0).to(1,1):\"horizontal\"===t?r.from(0,1).to(1,1):\"diagonal2\"===t&&r.from(1,0).to(0,1);return r}},{key:\"getTextBasedOnMaxWidth\",value:function(t){var e=t.text,i=t.maxWidth,a=t.fontSize,s=t.fontFamily,r=this.getTextRects(e,a,s),n=r.width/e.length,o=Math.floor(i/n);return i<r.width?e.slice(0,o-3)+\"...\":e}},{key:\"drawText\",value:function(t){var e=this,i=t.x,a=t.y,s=t.text,r=t.textAnchor,n=t.fontSize,o=t.fontFamily,l=t.fontWeight,c=t.foreColor,h=t.opacity,d=t.maxWidth,u=t.cssClass,f=void 0===u?\"\":u,p=t.isPlainText,x=void 0===p||p,b=this.w;void 0===s&&(s=\"\");var v=s;r||(r=\"start\"),c&&c.length||(c=b.config.chart.foreColor),o=o||b.config.chart.fontFamily,l=l||\"regular\";var m,y={maxWidth:d,fontSize:n=n||\"11px\",fontFamily:o};return Array.isArray(s)?m=b.globals.dom.Paper.text((function(t){for(var i=0;i<s.length;i++)v=s[i],d&&(v=e.getTextBasedOnMaxWidth(g({text:s[i]},y))),0===i?t.tspan(v):t.tspan(v).newLine()})):(d&&(v=this.getTextBasedOnMaxWidth(g({text:s},y))),m=x?b.globals.dom.Paper.plain(s):b.globals.dom.Paper.text((function(t){return t.tspan(v)}))),m.attr({x:i,y:a,\"text-anchor\":r,\"dominant-baseline\":\"auto\",\"font-size\":n,\"font-family\":o,\"font-weight\":l,fill:c,class:\"apexcharts-text \"+f}),m.node.style.fontFamily=o,m.node.style.opacity=h,m}},{key:\"drawMarker\",value:function(t,e,i){t=t||0;var a=i.pSize||0,s=null;if(\"square\"===i.shape||\"rect\"===i.shape){var r=void 0===i.pRadius?a/2:i.pRadius;null!==e&&a||(a=0,r=0);var o=1.2*a+r,l=this.drawRect(o,o,o,o,r);l.attr({x:t-o/2,y:e-o/2,cx:t,cy:e,class:i.class?i.class:\"\",fill:i.pointFillColor,\"fill-opacity\":i.pointFillOpacity?i.pointFillOpacity:1,stroke:i.pointStrokeColor,\"stroke-width\":i.pointStrokeWidth?i.pointStrokeWidth:0,\"stroke-opacity\":i.pointStrokeOpacity?i.pointStrokeOpacity:1}),s=l}else\"circle\"!==i.shape&&i.shape||(n.isNumber(e)||(a=0,e=0),s=this.drawCircle(a,{cx:t,cy:e,class:i.class?i.class:\"\",stroke:i.pointStrokeColor,fill:i.pointFillColor,\"fill-opacity\":i.pointFillOpacity?i.pointFillOpacity:1,\"stroke-width\":i.pointStrokeWidth?i.pointStrokeWidth:0,\"stroke-opacity\":i.pointStrokeOpacity?i.pointStrokeOpacity:1}));return s}},{key:\"pathMouseEnter\",value:function(t,e){var i=this.w,a=new d(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10);if(\"function\"==typeof i.config.chart.events.dataPointMouseEnter&&i.config.chart.events.dataPointMouseEnter(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent(\"dataPointMouseEnter\",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),(\"none\"===i.config.states.active.filter.type||\"true\"!==t.node.getAttribute(\"selected\"))&&\"none\"!==i.config.states.hover.filter.type&&!i.globals.isTouchDevice){var n=i.config.states.hover.filter;a.applyFilter(t,s,n.type,n.value)}}},{key:\"pathMouseLeave\",value:function(t,e){var i=this.w,a=new d(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10);\"function\"==typeof i.config.chart.events.dataPointMouseLeave&&i.config.chart.events.dataPointMouseLeave(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent(\"dataPointMouseLeave\",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),\"none\"!==i.config.states.active.filter.type&&\"true\"===t.node.getAttribute(\"selected\")||\"none\"!==i.config.states.hover.filter.type&&a.getDefaultFilter(t,s)}},{key:\"pathMouseDown\",value:function(t,e){var i=this.w,a=new d(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10),n=\"false\";if(\"true\"===t.node.getAttribute(\"selected\")){if(t.node.setAttribute(\"selected\",\"false\"),i.globals.selectedDataPoints[s].indexOf(r)>-1){var o=i.globals.selectedDataPoints[s].indexOf(r);i.globals.selectedDataPoints[s].splice(o,1)}}else{if(!i.config.states.active.allowMultipleDataPointsSelection&&i.globals.selectedDataPoints.length>0){i.globals.selectedDataPoints=[];var l=i.globals.dom.Paper.select(\".apexcharts-series path\").members,c=i.globals.dom.Paper.select(\".apexcharts-series circle, .apexcharts-series rect\").members,h=function(t){Array.prototype.forEach.call(t,(function(t){t.node.setAttribute(\"selected\",\"false\"),a.getDefaultFilter(t,s)}))};h(l),h(c)}t.node.setAttribute(\"selected\",\"true\"),n=\"true\",void 0===i.globals.selectedDataPoints[s]&&(i.globals.selectedDataPoints[s]=[]),i.globals.selectedDataPoints[s].push(r)}if(\"true\"===n){var u=i.config.states.active.filter;if(\"none\"!==u)a.applyFilter(t,s,u.type,u.value);else if(\"none\"!==i.config.states.hover.filter&&!i.globals.isTouchDevice){var g=i.config.states.hover.filter;a.applyFilter(t,s,g.type,g.value)}}else\"none\"!==i.config.states.active.filter.type&&(\"none\"===i.config.states.hover.filter.type||i.globals.isTouchDevice?a.getDefaultFilter(t,s):(g=i.config.states.hover.filter,a.applyFilter(t,s,g.type,g.value)));\"function\"==typeof i.config.chart.events.dataPointSelection&&i.config.chart.events.dataPointSelection(e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}),e&&this.ctx.events.fireEvent(\"dataPointSelection\",[e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}])}},{key:\"rotateAroundCenter\",value:function(t){var e={};return t&&\"function\"==typeof t.getBBox&&(e=t.getBBox()),{x:e.x+e.width/2,y:e.y+e.height/2}}},{key:\"getTextRects\",value:function(t,e,i,a){var s=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],r=this.w,n=this.drawText({x:-200,y:-200,text:t,textAnchor:\"start\",fontSize:e,fontFamily:i,foreColor:\"#fff\",opacity:0});a&&n.attr(\"transform\",a),r.globals.dom.Paper.add(n);var o=n.bbox();return s||(o=n.node.getBoundingClientRect()),n.remove(),{width:o.width,height:o.height}}},{key:\"placeTextWithEllipsis\",value:function(t,e,i){if(\"function\"==typeof t.getComputedTextLength&&(t.textContent=e,e.length>0&&t.getComputedTextLength()>=i/1.1)){for(var a=e.length-3;a>0;a-=3)if(t.getSubStringLength(0,a)<=i/1.1)return void(t.textContent=e.substring(0,a)+\"...\");t.textContent=\".\"}}}],a=[{key:\"setAttrs\",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}}],i&&p(e.prototype,i),a&&p(e,a),t}();const b=x;function v(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var m=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i,a;return e=t,i=[{key:\"getStackedSeriesTotals\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=this.w,i=[];if(0===e.globals.series.length)return i;for(var a=0;a<e.globals.series[e.globals.maxValsInArrayIndex].length;a++){for(var s=0,r=0;r<e.globals.series.length;r++)void 0!==e.globals.series[r][a]&&-1===t.indexOf(r)&&(s+=e.globals.series[r][a]);i.push(s)}return i}},{key:\"getSeriesTotalByIndex\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return null===t?this.w.config.series.reduce((function(t,e){return t+e}),0):this.w.globals.series[t].reduce((function(t,e){return t+e}),0)}},{key:\"isSeriesNull\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return 0===(null===t?this.w.config.series.filter((function(t){return null!==t})):this.w.config.series[t].data.filter((function(t){return null!==t}))).length}},{key:\"seriesHaveSameValues\",value:function(t){return this.w.globals.series[t].every((function(t,e,i){return t===i[0]}))}},{key:\"getCategoryLabels\",value:function(t){var e=this.w,i=t.slice();return e.config.xaxis.convertedCatToNumeric&&(i=t.map((function(t,i){return e.config.xaxis.labels.formatter(t-e.globals.minX+1)}))),i}},{key:\"getLargestSeries\",value:function(){var t=this.w;t.globals.maxValsInArrayIndex=t.globals.series.map((function(t){return t.length})).indexOf(Math.max.apply(Math,t.globals.series.map((function(t){return t.length}))))}},{key:\"getLargestMarkerSize\",value:function(){var t=this.w,e=0;return t.globals.markers.size.forEach((function(t){e=Math.max(e,t)})),t.config.markers.discrete&&t.config.markers.discrete.length&&t.config.markers.discrete.forEach((function(t){e=Math.max(e,t.size)})),e>0&&(e+=t.config.markers.hover.sizeOffset+1),t.globals.markers.largestSize=e,e}},{key:\"getSeriesTotals\",value:function(){var t=this.w;t.globals.seriesTotals=t.globals.series.map((function(t,e){var i=0;if(Array.isArray(t))for(var a=0;a<t.length;a++)i+=t[a];else i+=t;return i}))}},{key:\"getSeriesTotalsXRange\",value:function(t,e){var i=this.w;return i.globals.series.map((function(a,s){for(var r=0,n=0;n<a.length;n++)i.globals.seriesX[s][n]>t&&i.globals.seriesX[s][n]<e&&(r+=a[n]);return r}))}},{key:\"getPercentSeries\",value:function(){var t=this.w;t.globals.seriesPercent=t.globals.series.map((function(e,i){var a=[];if(Array.isArray(e))for(var s=0;s<e.length;s++){var r=t.globals.stackedSeriesTotals[s],n=0;r&&(n=100*e[s]/r),a.push(n)}else{var o=100*e/t.globals.seriesTotals.reduce((function(t,e){return t+e}),0);a.push(o)}return a}))}},{key:\"getCalculatedRatios\",value:function(){var t,e,i,a,s=this.w.globals,r=[],n=0,o=[],l=.1,c=0;if(s.yRange=[],s.isMultipleYAxis)for(var h=0;h<s.minYArr.length;h++)s.yRange.push(Math.abs(s.minYArr[h]-s.maxYArr[h])),o.push(0);else s.yRange.push(Math.abs(s.minY-s.maxY));s.xRange=Math.abs(s.maxX-s.minX),s.zRange=Math.abs(s.maxZ-s.minZ);for(var d=0;d<s.yRange.length;d++)r.push(s.yRange[d]/s.gridHeight);if(e=s.xRange/s.gridWidth,i=Math.abs(s.initialMaxX-s.initialMinX)/s.gridWidth,t=s.yRange/s.gridWidth,a=s.xRange/s.gridHeight,(n=s.zRange/s.gridHeight*16)||(n=1),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(s.hasNegs=!0),s.isMultipleYAxis){o=[];for(var u=0;u<r.length;u++)o.push(-s.minYArr[u]/r[u])}else o.push(-s.minY/r[0]),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(l=-s.minY/t,c=s.minX/e);return{yRatio:r,invertedYRatio:t,zRatio:n,xRatio:e,initialXRatio:i,invertedXRatio:a,baseLineInvertedY:l,baseLineY:o,baseLineX:c}}},{key:\"getLogSeries\",value:function(t){var e=this,i=this.w;return i.globals.seriesLog=t.map((function(t,a){return i.config.yaxis[a]&&i.config.yaxis[a].logarithmic?t.map((function(t){return null===t?null:e.getLogVal(i.config.yaxis[a].logBase,t,a)})):t})),i.globals.invalidLogScale?t:i.globals.seriesLog}},{key:\"getBaseLog\",value:function(t,e){return Math.log(e)/Math.log(t)}},{key:\"getLogVal\",value:function(t,e,i){if(0===e)return 0;var a=this.w,s=0===a.globals.minYArr[i]?-1:this.getBaseLog(t,a.globals.minYArr[i]),r=(0===a.globals.maxYArr[i]?0:this.getBaseLog(t,a.globals.maxYArr[i]))-s;return e<1?e/r:(this.getBaseLog(t,e)-s)/r}},{key:\"getLogYRatios\",value:function(t){var e=this,i=this.w,a=this.w.globals;return a.yLogRatio=t.slice(),a.logYRange=a.yRange.map((function(t,s){if(i.config.yaxis[s]&&e.w.config.yaxis[s].logarithmic){var r,n=-Number.MAX_VALUE,o=Number.MIN_VALUE;return a.seriesLog.forEach((function(t,e){t.forEach((function(t){i.config.yaxis[e]&&i.config.yaxis[e].logarithmic&&(n=Math.max(t,n),o=Math.min(t,o))}))})),r=Math.pow(a.yRange[s],Math.abs(o-n)/a.yRange[s]),a.yLogRatio[s]=r/a.gridHeight,r}})),a.invalidLogScale?t.slice():a.yLogRatio}}],a=[{key:\"checkComboSeries\",value:function(t){var e=!1,i=0,a=0;return t.length&&void 0!==t[0].type&&t.forEach((function(t){\"bar\"!==t.type&&\"column\"!==t.type&&\"candlestick\"!==t.type&&\"boxPlot\"!==t.type||i++,void 0!==t.type&&a++})),a>0&&(e=!0),{comboBarCount:i,comboCharts:e}}},{key:\"extendArrayProps\",value:function(t,e,i){return e.yaxis&&(e=t.extendYAxis(e,i)),e.annotations&&(e.annotations.yaxis&&(e=t.extendYAxisAnnotations(e)),e.annotations.xaxis&&(e=t.extendXAxisAnnotations(e)),e.annotations.points&&(e=t.extendPointAnnotations(e))),e}}],i&&v(e.prototype,i),a&&v(e,a),t}();const y=m;function w(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var k=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.annoCtx=e}var e,i;return e=t,i=[{key:\"setOrientations\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w;if(\"vertical\"===t.label.orientation){var a=null!==e?e:0,s=i.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-annotations .apexcharts-xaxis-annotation-label[rel='\".concat(a,\"']\"));if(null!==s){var r=s.getBoundingClientRect();s.setAttribute(\"x\",parseFloat(s.getAttribute(\"x\"))-r.height+4),\"top\"===t.label.position?s.setAttribute(\"y\",parseFloat(s.getAttribute(\"y\"))+r.width):s.setAttribute(\"y\",parseFloat(s.getAttribute(\"y\"))-r.width);var n=this.annoCtx.graphics.rotateAroundCenter(s),o=n.x,l=n.y;s.setAttribute(\"transform\",\"rotate(-90 \".concat(o,\" \").concat(l,\")\"))}}}},{key:\"addBackgroundToAnno\",value:function(t,e){var i=this.w;if(!t||void 0===e.label.text||void 0!==e.label.text&&!String(e.label.text).trim())return null;var a=i.globals.dom.baseEl.querySelector(\".apexcharts-grid\").getBoundingClientRect(),s=t.getBoundingClientRect(),r=e.label.style.padding.left,n=e.label.style.padding.right,o=e.label.style.padding.top,l=e.label.style.padding.bottom;\"vertical\"===e.label.orientation&&(o=e.label.style.padding.left,l=e.label.style.padding.right,r=e.label.style.padding.top,n=e.label.style.padding.bottom);var c=s.left-a.left-r,h=s.top-a.top-o,d=this.annoCtx.graphics.drawRect(c-i.globals.barPadForNumericAxis,h,s.width+r+n,s.height+o+l,e.label.borderRadius,e.label.style.background,1,e.label.borderWidth,e.label.borderColor,0);return e.id&&d.node.classList.add(e.id),d}},{key:\"annotationsBackground\",value:function(){var t=this,e=this.w,i=function(i,a,s){var r=e.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations .apexcharts-\").concat(s,\"-annotation-label[rel='\").concat(a,\"']\"));if(r){var n=r.parentNode,o=t.addBackgroundToAnno(r,i);o&&(n.insertBefore(o.node,r),i.label.mouseEnter&&o.node.addEventListener(\"mouseenter\",i.label.mouseEnter.bind(t,i)),i.label.mouseLeave&&o.node.addEventListener(\"mouseleave\",i.label.mouseLeave.bind(t,i)),i.label.click&&o.node.addEventListener(\"click\",i.label.click.bind(t,i)))}};e.config.annotations.xaxis.map((function(t,e){i(t,e,\"xaxis\")})),e.config.annotations.yaxis.map((function(t,e){i(t,e,\"yaxis\")})),e.config.annotations.points.map((function(t,e){i(t,e,\"point\")}))}},{key:\"getY1Y2\",value:function(t,e){var i,a=\"y1\"===t?e.y:e.y2,s=this.w;if(this.annoCtx.invertAxis){var r=s.globals.labels.indexOf(a);s.config.xaxis.convertedCatToNumeric&&(r=s.globals.categoryLabels.indexOf(a));var n=s.globals.dom.baseEl.querySelector(\".apexcharts-yaxis-texts-g text:nth-child(\"+(r+1)+\")\");n&&(i=parseFloat(n.getAttribute(\"y\")))}else{var o;o=s.config.yaxis[e.yAxisIndex].logarithmic?(a=new y(this.annoCtx.ctx).getLogVal(a,e.yAxisIndex))/s.globals.yLogRatio[e.yAxisIndex]:(a-s.globals.minYArr[e.yAxisIndex])/(s.globals.yRange[e.yAxisIndex]/s.globals.gridHeight),i=s.globals.gridHeight-o,!e.marker||void 0!==e.y&&null!==e.y||(i=0),s.config.yaxis[e.yAxisIndex]&&s.config.yaxis[e.yAxisIndex].reversed&&(i=o)}return\"string\"==typeof a&&a.indexOf(\"px\")>-1&&(i=parseFloat(a)),i}},{key:\"getX1X2\",value:function(t,e){var i=this.w,a=this.annoCtx.invertAxis?i.globals.minY:i.globals.minX,s=this.annoCtx.invertAxis?i.globals.maxY:i.globals.maxX,r=this.annoCtx.invertAxis?i.globals.yRange[0]:i.globals.xRange,n=(e.x-a)/(r/i.globals.gridWidth);this.annoCtx.inversedReversedAxis&&(n=(s-e.x)/(r/i.globals.gridWidth)),\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric||this.annoCtx.invertAxis||i.globals.dataFormatXNumeric||(n=this.getStringX(e.x));var o=(e.x2-a)/(r/i.globals.gridWidth);return this.annoCtx.inversedReversedAxis&&(o=(s-e.x2)/(r/i.globals.gridWidth)),\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric||this.annoCtx.invertAxis||i.globals.dataFormatXNumeric||(o=this.getStringX(e.x2)),void 0!==e.x&&null!==e.x||!e.marker||(n=i.globals.gridWidth),\"x1\"===t&&\"string\"==typeof e.x&&e.x.indexOf(\"px\")>-1&&(n=parseFloat(e.x)),\"x2\"===t&&\"string\"==typeof e.x2&&e.x2.indexOf(\"px\")>-1&&(o=parseFloat(e.x2)),\"x1\"===t?n:o}},{key:\"getStringX\",value:function(t){var e=this.w,i=t;e.config.xaxis.convertedCatToNumeric&&e.globals.categoryLabels.length&&(t=e.globals.categoryLabels.indexOf(t)+1);var a=e.globals.labels.indexOf(t),s=e.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-texts-g text:nth-child(\"+(a+1)+\")\");return s&&(i=parseFloat(s.getAttribute(\"x\"))),i}}],i&&w(e.prototype,i),t}();function A(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var S=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.annoCtx=e,this.invertAxis=this.annoCtx.invertAxis,this.helpers=new k(this.annoCtx)}var e,i;return e=t,(i=[{key:\"addXaxisAnnotation\",value:function(t,e,i){var a,s=this.w,r=this.helpers.getX1X2(\"x1\",t),o=t.label.text,l=t.strokeDashArray;if(n.isNumber(r)){if(null===t.x2||void 0===t.x2){var c=this.annoCtx.graphics.drawLine(r+t.offsetX,0+t.offsetY,r+t.offsetX,s.globals.gridHeight+t.offsetY,t.borderColor,l,t.borderWidth);e.appendChild(c.node),t.id&&c.node.classList.add(t.id)}else{if((a=this.helpers.getX1X2(\"x2\",t))<r){var h=r;r=a,a=h}var d=this.annoCtx.graphics.drawRect(r+t.offsetX,0+t.offsetY,a-r,s.globals.gridHeight+t.offsetY,0,t.fillColor,t.opacity,1,t.borderColor,l);d.node.classList.add(\"apexcharts-annotation-rect\"),d.attr(\"clip-path\",\"url(#gridRectMask\".concat(s.globals.cuid,\")\")),e.appendChild(d.node),t.id&&d.node.classList.add(t.id)}var u=this.annoCtx.graphics.getTextRects(o,parseFloat(t.label.style.fontSize)),g=\"top\"===t.label.position?4:\"center\"===t.label.position?s.globals.gridHeight/2+(\"vertical\"===t.label.orientation?u.width/2:0):s.globals.gridHeight,f=this.annoCtx.graphics.drawText({x:r+t.label.offsetX,y:g+t.label.offsetY-(\"vertical\"===t.label.orientation?\"top\"===t.label.position?u.width/2-12:-u.width/2:0),text:o,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-xaxis-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});f.attr({rel:i}),e.appendChild(f.node),this.annoCtx.helpers.setOrientations(t,i)}}},{key:\"drawXAxisAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-xaxis-annotations\"});return e.config.annotations.xaxis.map((function(e,a){t.addXaxisAnnotation(e,i.node,a)})),i}}])&&A(e.prototype,i),t}();function C(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var P=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.annoCtx=e,this.helpers=new k(this.annoCtx)}var e,i;return e=t,(i=[{key:\"addYaxisAnnotation\",value:function(t,e,i){var a,s=this.w,r=t.strokeDashArray,n=this.helpers.getY1Y2(\"y1\",t),o=t.label.text;if(null===t.y2||void 0===t.y2){var l=this.annoCtx.graphics.drawLine(0+t.offsetX,n+t.offsetY,this._getYAxisAnnotationWidth(t),n+t.offsetY,t.borderColor,r,t.borderWidth);e.appendChild(l.node),t.id&&l.node.classList.add(t.id)}else{if((a=this.helpers.getY1Y2(\"y2\",t))>n){var c=n;n=a,a=c}var h=this.annoCtx.graphics.drawRect(0+t.offsetX,a+t.offsetY,this._getYAxisAnnotationWidth(t),n-a,0,t.fillColor,t.opacity,1,t.borderColor,r);h.node.classList.add(\"apexcharts-annotation-rect\"),h.attr(\"clip-path\",\"url(#gridRectMask\".concat(s.globals.cuid,\")\")),e.appendChild(h.node),t.id&&h.node.classList.add(t.id)}var d=\"right\"===t.label.position?s.globals.gridWidth:\"center\"===t.label.position?s.globals.gridWidth/2:0,u=this.annoCtx.graphics.drawText({x:d+t.label.offsetX,y:(null!=a?a:n)+t.label.offsetY-3,text:o,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-yaxis-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});u.attr({rel:i}),e.appendChild(u.node)}},{key:\"_getYAxisAnnotationWidth\",value:function(t){var e=this.w;return e.globals.gridWidth,(t.width.indexOf(\"%\")>-1?e.globals.gridWidth*parseInt(t.width,10)/100:parseInt(t.width,10))+t.offsetX}},{key:\"drawYAxisAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-yaxis-annotations\"});return e.config.annotations.yaxis.map((function(e,a){t.addYaxisAnnotation(e,i.node,a)})),i}}])&&C(e.prototype,i),t}();function L(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var O=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.annoCtx=e,this.helpers=new k(this.annoCtx)}var e,i;return e=t,(i=[{key:\"addPointAnnotation\",value:function(t,e,i){this.w;var a=this.helpers.getX1X2(\"x1\",t),s=this.helpers.getY1Y2(\"y1\",t);if(n.isNumber(a)){var r={pSize:t.marker.size,pointStrokeWidth:t.marker.strokeWidth,pointFillColor:t.marker.fillColor,pointStrokeColor:t.marker.strokeColor,shape:t.marker.shape,pRadius:t.marker.radius,class:\"apexcharts-point-annotation-marker \".concat(t.marker.cssClass,\" \").concat(t.id?t.id:\"\")},o=this.annoCtx.graphics.drawMarker(a+t.marker.offsetX,s+t.marker.offsetY,r);e.appendChild(o.node);var l=t.label.text?t.label.text:\"\",c=this.annoCtx.graphics.drawText({x:a+t.label.offsetX,y:s+t.label.offsetY-t.marker.size-parseFloat(t.label.style.fontSize)/1.6,text:l,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-point-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});if(c.attr({rel:i}),e.appendChild(c.node),t.customSVG.SVG){var h=this.annoCtx.graphics.group({class:\"apexcharts-point-annotations-custom-svg \"+t.customSVG.cssClass});h.attr({transform:\"translate(\".concat(a+t.customSVG.offsetX,\", \").concat(s+t.customSVG.offsetY,\")\")}),h.node.innerHTML=t.customSVG.SVG,e.appendChild(h.node)}if(t.image.path){var d=t.image.width?t.image.width:20,u=t.image.height?t.image.height:20;o=this.annoCtx.addImage({x:a+t.image.offsetX-d/2,y:s+t.image.offsetY-u/2,width:d,height:u,path:t.image.path,appendTo:\".apexcharts-point-annotations\"})}t.mouseEnter&&o.node.addEventListener(\"mouseenter\",t.mouseEnter.bind(this,t)),t.mouseLeave&&o.node.addEventListener(\"mouseleave\",t.mouseLeave.bind(this,t)),t.click&&o.node.addEventListener(\"click\",t.click.bind(this,t))}}},{key:\"drawPointAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-point-annotations\"});return e.config.annotations.points.map((function(e,a){t.addPointAnnotation(e,i.node,a)})),i}}])&&L(e.prototype,i),t}();const T=JSON.parse('{\"name\":\"en\",\"options\":{\"months\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"shortMonths\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"days\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],\"shortDays\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"toolbar\":{\"exportToSVG\":\"Download SVG\",\"exportToPNG\":\"Download PNG\",\"exportToCSV\":\"Download CSV\",\"menu\":\"Menu\",\"selection\":\"Selection\",\"selectionZoom\":\"Selection Zoom\",\"zoomIn\":\"Zoom In\",\"zoomOut\":\"Zoom Out\",\"pan\":\"Panning\",\"reset\":\"Reset Zoom\"}}}');function E(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var I=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.yAxis={show:!0,showAlways:!1,showForNullSeries:!0,seriesName:void 0,opposite:!1,reversed:!1,logarithmic:!1,logBase:10,tickAmount:void 0,forceNiceScale:!1,max:void 0,min:void 0,floating:!1,decimalsInFloat:void 0,labels:{show:!0,minWidth:0,maxWidth:160,offsetX:0,offsetY:0,align:void 0,rotate:0,padding:20,style:{colors:[],fontSize:\"11px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"},formatter:void 0},axisBorder:{show:!1,color:\"#e0e0e0\",width:1,offsetX:0,offsetY:0},axisTicks:{show:!1,color:\"#e0e0e0\",width:6,offsetX:0,offsetY:0},title:{text:void 0,rotate:-90,offsetY:0,offsetX:0,style:{color:void 0,fontSize:\"11px\",fontWeight:900,fontFamily:void 0,cssClass:\"\"}},tooltip:{enabled:!1,offsetX:0},crosshairs:{show:!0,position:\"front\",stroke:{color:\"#b6b6b6\",width:1,dashArray:0}}},this.pointAnnotation={id:void 0,x:0,y:null,yAxisIndex:0,seriesIndex:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,marker:{size:4,fillColor:\"#fff\",strokeWidth:2,strokeColor:\"#333\",shape:\"circle\",offsetX:0,offsetY:0,radius:2,cssClass:\"\"},label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"middle\",offsetX:0,offsetY:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}},customSVG:{SVG:void 0,cssClass:void 0,offsetX:0,offsetY:0},image:{path:void 0,width:20,height:20,offsetX:0,offsetY:0}},this.yAxisAnnotation={id:void 0,y:0,y2:null,strokeDashArray:1,fillColor:\"#c2c2c2\",borderColor:\"#c2c2c2\",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,width:\"100%\",yAxisIndex:0,label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"end\",position:\"right\",offsetX:0,offsetY:-3,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}}},this.xAxisAnnotation={id:void 0,x:0,x2:null,strokeDashArray:1,fillColor:\"#c2c2c2\",borderColor:\"#c2c2c2\",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"middle\",orientation:\"vertical\",position:\"top\",offsetX:0,offsetY:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}}},this.text={x:0,y:0,text:\"\",textAnchor:\"start\",foreColor:void 0,fontSize:\"13px\",fontFamily:void 0,fontWeight:400,appendTo:\".apexcharts-annotations\",backgroundColor:\"transparent\",borderColor:\"#c2c2c2\",borderRadius:0,borderWidth:0,paddingLeft:4,paddingRight:4,paddingTop:2,paddingBottom:2}}var e,i;return e=t,(i=[{key:\"init\",value:function(){return{annotations:{yaxis:[this.yAxisAnnotation],xaxis:[this.xAxisAnnotation],points:[this.pointAnnotation],texts:[],images:[],shapes:[]},chart:{animations:{enabled:!0,easing:\"easeinout\",speed:800,animateGradually:{delay:150,enabled:!0},dynamicAnimation:{enabled:!0,speed:350}},background:\"transparent\",locales:[T],defaultLocale:\"en\",dropShadow:{enabled:!1,enabledOnSeries:void 0,top:2,left:2,blur:4,color:\"#000\",opacity:.35},events:{animationEnd:void 0,beforeMount:void 0,mounted:void 0,updated:void 0,click:void 0,mouseMove:void 0,mouseLeave:void 0,xAxisLabelClick:void 0,legendClick:void 0,markerClick:void 0,selection:void 0,dataPointSelection:void 0,dataPointMouseEnter:void 0,dataPointMouseLeave:void 0,beforeZoom:void 0,beforeResetZoom:void 0,zoomed:void 0,scrolled:void 0,brushScrolled:void 0},foreColor:\"#373d3f\",fontFamily:\"Helvetica, Arial, sans-serif\",height:\"auto\",parentHeightOffset:15,redrawOnParentResize:!0,redrawOnWindowResize:!0,id:void 0,group:void 0,offsetX:0,offsetY:0,selection:{enabled:!1,type:\"x\",fill:{color:\"#24292e\",opacity:.1},stroke:{width:1,color:\"#24292e\",opacity:.4,dashArray:3},xaxis:{min:void 0,max:void 0},yaxis:{min:void 0,max:void 0}},sparkline:{enabled:!1},brush:{enabled:!1,autoScaleYaxis:!0,target:void 0},stacked:!1,stackType:\"normal\",toolbar:{show:!0,offsetX:0,offsetY:0,tools:{download:!0,selection:!0,zoom:!0,zoomin:!0,zoomout:!0,pan:!0,reset:!0,customIcons:[]},export:{csv:{filename:void 0,columnDelimiter:\",\",headerCategory:\"category\",headerValue:\"value\",dateFormatter:function(t){return new Date(t).toDateString()}},png:{filename:void 0},svg:{filename:void 0}},autoSelected:\"zoom\"},type:\"line\",width:\"100%\",zoom:{enabled:!0,type:\"x\",autoScaleYaxis:!1,zoomedArea:{fill:{color:\"#90CAF9\",opacity:.4},stroke:{color:\"#0D47A1\",opacity:.4,width:1}}}},plotOptions:{area:{fillTo:\"origin\"},bar:{horizontal:!1,columnWidth:\"70%\",barHeight:\"70%\",distributed:!1,borderRadius:0,borderRadiusApplication:\"around\",borderRadiusWhenStacked:\"last\",rangeBarOverlap:!0,rangeBarGroupRows:!1,hideZeroBarsWhenGrouped:!0,colors:{ranges:[],backgroundBarColors:[],backgroundBarOpacity:1,backgroundBarRadius:0},dataLabels:{position:\"top\",maxItems:100,hideOverflowingLabels:!0,orientation:\"horizontal\",total:{enabled:!1,formatter:void 0,offsetX:0,offsetY:0,style:{color:\"#373d3f\",fontSize:\"12px\",fontFamily:void 0,fontWeight:600}}}},bubble:{zScaling:!0,minBubbleRadius:void 0,maxBubbleRadius:void 0},candlestick:{colors:{upward:\"#00B746\",downward:\"#EF403C\"},wick:{useFillColor:!0}},boxPlot:{colors:{upper:\"#00E396\",lower:\"#008FFB\"}},heatmap:{radius:2,enableShades:!0,shadeIntensity:.5,reverseNegativeShade:!1,distributed:!1,useFillColorAsStroke:!1,colorScale:{inverse:!1,ranges:[],min:void 0,max:void 0}},treemap:{enableShades:!0,shadeIntensity:.5,distributed:!1,reverseNegativeShade:!1,useFillColorAsStroke:!1,colorScale:{inverse:!1,ranges:[],min:void 0,max:void 0}},radialBar:{inverseOrder:!1,startAngle:0,endAngle:360,offsetX:0,offsetY:0,hollow:{margin:5,size:\"50%\",background:\"transparent\",image:void 0,imageWidth:150,imageHeight:150,imageOffsetX:0,imageOffsetY:0,imageClipped:!0,position:\"front\",dropShadow:{enabled:!1,top:0,left:0,blur:3,color:\"#000\",opacity:.5}},track:{show:!0,startAngle:void 0,endAngle:void 0,background:\"#f2f2f2\",strokeWidth:\"97%\",opacity:1,margin:5,dropShadow:{enabled:!1,top:0,left:0,blur:3,color:\"#000\",opacity:.5}},dataLabels:{show:!0,name:{show:!0,fontSize:\"16px\",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:0,formatter:function(t){return t}},value:{show:!0,fontSize:\"14px\",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:16,formatter:function(t){return t+\"%\"}},total:{show:!1,label:\"Total\",fontSize:\"16px\",fontWeight:600,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)/t.globals.series.length+\"%\"}}}},pie:{customScale:1,offsetX:0,offsetY:0,startAngle:0,endAngle:360,expandOnClick:!0,dataLabels:{offset:0,minAngleToShowLabel:10},donut:{size:\"65%\",background:\"transparent\",labels:{show:!1,name:{show:!0,fontSize:\"16px\",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:-10,formatter:function(t){return t}},value:{show:!0,fontSize:\"20px\",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:10,formatter:function(t){return t}},total:{show:!1,showAlways:!1,label:\"Total\",fontSize:\"16px\",fontWeight:400,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)}}}}},polarArea:{rings:{strokeWidth:1,strokeColor:\"#e8e8e8\"},spokes:{strokeWidth:1,connectorColors:\"#e8e8e8\"}},radar:{size:void 0,offsetX:0,offsetY:0,polygons:{strokeWidth:1,strokeColors:\"#e8e8e8\",connectorColors:\"#e8e8e8\",fill:{colors:void 0}}}},colors:void 0,dataLabels:{enabled:!0,enabledOnSeries:void 0,formatter:function(t){return null!==t?t:\"\"},textAnchor:\"middle\",distributed:!1,offsetX:0,offsetY:0,style:{fontSize:\"12px\",fontFamily:void 0,fontWeight:600,colors:void 0},background:{enabled:!0,foreColor:\"#fff\",borderRadius:2,padding:4,opacity:.9,borderWidth:1,borderColor:\"#fff\",dropShadow:{enabled:!1,top:1,left:1,blur:1,color:\"#000\",opacity:.45}},dropShadow:{enabled:!1,top:1,left:1,blur:1,color:\"#000\",opacity:.45}},fill:{type:\"solid\",colors:void 0,opacity:.85,gradient:{shade:\"dark\",type:\"horizontal\",shadeIntensity:.5,gradientToColors:void 0,inverseColors:!0,opacityFrom:1,opacityTo:1,stops:[0,50,100],colorStops:[]},image:{src:[],width:void 0,height:void 0},pattern:{style:\"squares\",width:6,height:6,strokeWidth:2}},forecastDataPoints:{count:0,fillOpacity:.5,strokeWidth:void 0,dashArray:4},grid:{show:!0,borderColor:\"#e0e0e0\",strokeDashArray:0,position:\"back\",xaxis:{lines:{show:!1}},yaxis:{lines:{show:!0}},row:{colors:void 0,opacity:.5},column:{colors:void 0,opacity:.5},padding:{top:0,right:10,bottom:0,left:12}},labels:[],legend:{show:!0,showForSingleSeries:!1,showForNullSeries:!0,showForZeroSeries:!0,floating:!1,position:\"bottom\",horizontalAlign:\"center\",inverseOrder:!1,fontSize:\"12px\",fontFamily:void 0,fontWeight:400,width:void 0,height:void 0,formatter:void 0,tooltipHoverFormatter:void 0,offsetX:-20,offsetY:4,customLegendItems:[],labels:{colors:void 0,useSeriesColors:!1},markers:{width:12,height:12,strokeWidth:0,fillColors:void 0,strokeColor:\"#fff\",radius:12,customHTML:void 0,offsetX:0,offsetY:0,onClick:void 0},itemMargin:{horizontal:5,vertical:2},onItemClick:{toggleDataSeries:!0},onItemHover:{highlightDataSeries:!0}},markers:{discrete:[],size:0,colors:void 0,strokeColors:\"#fff\",strokeWidth:2,strokeOpacity:.9,strokeDashArray:0,fillOpacity:1,shape:\"circle\",width:8,height:8,radius:2,offsetX:0,offsetY:0,onClick:void 0,onDblClick:void 0,showNullDataPoints:!0,hover:{size:void 0,sizeOffset:3}},noData:{text:void 0,align:\"center\",verticalAlign:\"middle\",offsetX:0,offsetY:0,style:{color:void 0,fontSize:\"14px\",fontFamily:void 0}},responsive:[],series:void 0,states:{normal:{filter:{type:\"none\",value:0}},hover:{filter:{type:\"lighten\",value:.1}},active:{allowMultipleDataPointsSelection:!1,filter:{type:\"darken\",value:.5}}},title:{text:void 0,align:\"left\",margin:5,offsetX:0,offsetY:0,floating:!1,style:{fontSize:\"14px\",fontWeight:900,fontFamily:void 0,color:void 0}},subtitle:{text:void 0,align:\"left\",margin:5,offsetX:0,offsetY:30,floating:!1,style:{fontSize:\"12px\",fontWeight:400,fontFamily:void 0,color:void 0}},stroke:{show:!0,curve:\"smooth\",lineCap:\"butt\",width:2,colors:void 0,dashArray:0,fill:{type:\"solid\",colors:void 0,opacity:.85,gradient:{shade:\"dark\",type:\"horizontal\",shadeIntensity:.5,gradientToColors:void 0,inverseColors:!0,opacityFrom:1,opacityTo:1,stops:[0,50,100],colorStops:[]}}},tooltip:{enabled:!0,enabledOnSeries:void 0,shared:!0,followCursor:!1,intersect:!1,inverseOrder:!1,custom:void 0,fillSeriesColor:!1,theme:\"light\",cssClass:\"\",style:{fontSize:\"12px\",fontFamily:void 0},onDatasetHover:{highlightDataSeries:!1},x:{show:!0,format:\"dd MMM\",formatter:void 0},y:{formatter:void 0,title:{formatter:function(t){return t?t+\": \":\"\"}}},z:{formatter:void 0,title:\"Size: \"},marker:{show:!0,fillColors:void 0},items:{display:\"flex\"},fixed:{enabled:!1,position:\"topRight\",offsetX:0,offsetY:0}},xaxis:{type:\"category\",categories:[],convertedCatToNumeric:!1,offsetX:0,offsetY:0,overwriteCategories:void 0,labels:{show:!0,rotate:-45,rotateAlways:!1,hideOverlappingLabels:!0,trim:!1,minHeight:void 0,maxHeight:120,showDuplicates:!0,style:{colors:[],fontSize:\"12px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"},offsetX:0,offsetY:0,format:void 0,formatter:void 0,datetimeUTC:!0,datetimeFormatter:{year:\"yyyy\",month:\"MMM 'yy\",day:\"dd MMM\",hour:\"HH:mm\",minute:\"HH:mm:ss\",second:\"HH:mm:ss\"}},group:{groups:[],style:{colors:[],fontSize:\"12px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"}},axisBorder:{show:!0,color:\"#e0e0e0\",width:\"100%\",height:1,offsetX:0,offsetY:0},axisTicks:{show:!0,color:\"#e0e0e0\",height:6,offsetX:0,offsetY:0},tickAmount:void 0,tickPlacement:\"on\",min:void 0,max:void 0,range:void 0,floating:!1,decimalsInFloat:void 0,position:\"bottom\",title:{text:void 0,offsetX:0,offsetY:0,style:{color:void 0,fontSize:\"12px\",fontWeight:900,fontFamily:void 0,cssClass:\"\"}},crosshairs:{show:!0,width:1,position:\"back\",opacity:.9,stroke:{color:\"#b6b6b6\",width:1,dashArray:3},fill:{type:\"solid\",color:\"#B1B9C4\",gradient:{colorFrom:\"#D8E3F0\",colorTo:\"#BED1E6\",stops:[0,100],opacityFrom:.4,opacityTo:.5}},dropShadow:{enabled:!1,left:0,top:0,blur:1,opacity:.4}},tooltip:{enabled:!0,offsetY:0,formatter:void 0,style:{fontSize:\"12px\",fontFamily:void 0}}},yaxis:this.yAxis,theme:{mode:\"light\",palette:\"palette1\",monochrome:{enabled:!1,color:\"#008FFB\",shadeTo:\"light\",shadeIntensity:.65}}}}}])&&E(e.prototype,i),t}();function M(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var z=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.graphics=new b(this.ctx),this.w.globals.isBarHorizontal&&(this.invertAxis=!0),this.helpers=new k(this),this.xAxisAnnotations=new S(this),this.yAxisAnnotations=new P(this),this.pointsAnnotations=new O(this),this.w.globals.isBarHorizontal&&this.w.config.yaxis[0].reversed&&(this.inversedReversedAxis=!0),this.xDivision=this.w.globals.gridWidth/this.w.globals.dataPoints}var e,i;return e=t,(i=[{key:\"drawAxesAnnotations\",value:function(){var t=this.w;if(t.globals.axisCharts){for(var e=this.yAxisAnnotations.drawYAxisAnnotations(),i=this.xAxisAnnotations.drawXAxisAnnotations(),a=this.pointsAnnotations.drawPointAnnotations(),s=t.config.chart.animations.enabled,r=[e,i,a],n=[i.node,e.node,a.node],o=0;o<3;o++)t.globals.dom.elGraphical.add(r[o]),!s||t.globals.resized||t.globals.dataChanged||\"scatter\"!==t.config.chart.type&&\"bubble\"!==t.config.chart.type&&t.globals.dataPoints>1&&n[o].classList.add(\"apexcharts-element-hidden\"),t.globals.delayedElements.push({el:n[o],index:0});this.helpers.annotationsBackground()}}},{key:\"drawImageAnnos\",value:function(){var t=this;this.w.config.annotations.images.map((function(e,i){t.addImage(e,i)}))}},{key:\"drawTextAnnos\",value:function(){var t=this;this.w.config.annotations.texts.map((function(e,i){t.addText(e,i)}))}},{key:\"addXaxisAnnotation\",value:function(t,e,i){this.xAxisAnnotations.addXaxisAnnotation(t,e,i)}},{key:\"addYaxisAnnotation\",value:function(t,e,i){this.yAxisAnnotations.addYaxisAnnotation(t,e,i)}},{key:\"addPointAnnotation\",value:function(t,e,i){this.pointsAnnotations.addPointAnnotation(t,e,i)}},{key:\"addText\",value:function(t,e){var i=t.x,a=t.y,s=t.text,r=t.textAnchor,n=t.foreColor,o=t.fontSize,l=t.fontFamily,c=t.fontWeight,h=t.cssClass,d=t.backgroundColor,u=t.borderWidth,g=t.strokeDashArray,f=t.borderRadius,p=t.borderColor,x=t.appendTo,b=void 0===x?\".apexcharts-annotations\":x,v=t.paddingLeft,m=void 0===v?4:v,y=t.paddingRight,w=void 0===y?4:y,k=t.paddingBottom,A=void 0===k?2:k,S=t.paddingTop,C=void 0===S?2:S,P=this.w,L=this.graphics.drawText({x:i,y:a,text:s,textAnchor:r||\"start\",fontSize:o||\"12px\",fontWeight:c||\"regular\",fontFamily:l||P.config.chart.fontFamily,foreColor:n||P.config.chart.foreColor,cssClass:h}),O=P.globals.dom.baseEl.querySelector(b);O&&O.appendChild(L.node);var T=L.bbox();if(s){var E=this.graphics.drawRect(T.x-m,T.y-C,T.width+m+w,T.height+A+C,f,d||\"transparent\",1,u,p,g);O.insertBefore(E.node,L.node)}}},{key:\"addImage\",value:function(t,e){var i=this.w,a=t.path,s=t.x,r=void 0===s?0:s,n=t.y,o=void 0===n?0:n,l=t.width,c=void 0===l?20:l,h=t.height,d=void 0===h?20:h,u=t.appendTo,g=void 0===u?\".apexcharts-annotations\":u,f=i.globals.dom.Paper.image(a);f.size(c,d).move(r,o);var p=i.globals.dom.baseEl.querySelector(g);return p&&p.appendChild(f.node),f}},{key:\"addXaxisAnnotationExternal\",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"xaxis\",contextMethod:i.addXaxisAnnotation}),i}},{key:\"addYaxisAnnotationExternal\",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"yaxis\",contextMethod:i.addYaxisAnnotation}),i}},{key:\"addPointAnnotationExternal\",value:function(t,e,i){return void 0===this.invertAxis&&(this.invertAxis=i.w.globals.isBarHorizontal),this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"point\",contextMethod:i.addPointAnnotation}),i}},{key:\"addAnnotationExternal\",value:function(t){var e=t.params,i=t.pushToMemory,a=t.context,s=t.type,r=t.contextMethod,o=a,l=o.w,c=l.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations\")),h=c.childNodes.length+1,d=new I,u=Object.assign({},\"xaxis\"===s?d.xAxisAnnotation:\"yaxis\"===s?d.yAxisAnnotation:d.pointAnnotation),g=n.extend(u,e);switch(s){case\"xaxis\":this.addXaxisAnnotation(g,c,h);break;case\"yaxis\":this.addYaxisAnnotation(g,c,h);break;case\"point\":this.addPointAnnotation(g,c,h)}var f=l.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations .apexcharts-\").concat(s,\"-annotation-label[rel='\").concat(h,\"']\")),p=this.helpers.addBackgroundToAnno(f,g);return p&&c.insertBefore(p.node,f),i&&l.globals.memory.methodsToExec.push({context:o,id:g.id?g.id:n.randomId(),method:r,label:\"addAnnotation\",params:e}),a}},{key:\"clearAnnotations\",value:function(t){var e=t.w,i=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis-annotations, .apexcharts-xaxis-annotations, .apexcharts-point-annotations\");e.globals.memory.methodsToExec.map((function(t,i){\"addText\"!==t.label&&\"addAnnotation\"!==t.label||e.globals.memory.methodsToExec.splice(i,1)})),i=n.listToArray(i),Array.prototype.forEach.call(i,(function(t){for(;t.firstChild;)t.removeChild(t.firstChild)}))}},{key:\"removeAnnotation\",value:function(t,e){var i=t.w,a=i.globals.dom.baseEl.querySelectorAll(\".\".concat(e));a&&(i.globals.memory.methodsToExec.map((function(t,a){t.id===e&&i.globals.memory.methodsToExec.splice(a,1)})),Array.prototype.forEach.call(a,(function(t){t.parentElement.removeChild(t)})))}}])&&M(e.prototype,i),t}();function X(t){return function(t){if(Array.isArray(t))return Y(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||function(t,e){if(t){if(\"string\"==typeof t)return Y(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Y(t,e):void 0}}(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function Y(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function D(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const R=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.months31=[1,3,5,7,8,10,12],this.months30=[2,4,6,9,11],this.daysCntOfYear=[0,31,59,90,120,151,181,212,243,273,304,334]}var e,i;return e=t,(i=[{key:\"isValidDate\",value:function(t){return!isNaN(this.parseDate(t))}},{key:\"getTimeStamp\",value:function(t){return Date.parse(t)?this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toISOString().substr(0,25)).getTime():new Date(t).getTime():t}},{key:\"getDate\",value:function(t){return this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toUTCString()):new Date(t)}},{key:\"parseDate\",value:function(t){var e=Date.parse(t);if(!isNaN(e))return this.getTimeStamp(t);var i=Date.parse(t.replace(/-/g,\"/\").replace(/[a-z]+/gi,\" \"));return this.getTimeStamp(i)}},{key:\"parseDateWithTimezone\",value:function(t){return Date.parse(t.replace(/-/g,\"/\").replace(/[a-z]+/gi,\" \"))}},{key:\"formatDate\",value:function(t,e){var i=this.w.globals.locale,a=this.w.config.xaxis.labels.datetimeUTC,s=[\"\\0\"].concat(X(i.months)),r=[\"\u0001\"].concat(X(i.shortMonths)),n=[\"\u0002\"].concat(X(i.days)),o=[\"\u0003\"].concat(X(i.shortDays));function l(t,e){var i=t+\"\";for(e=e||2;i.length<e;)i=\"0\"+i;return i}var c=a?t.getUTCFullYear():t.getFullYear();e=(e=(e=e.replace(/(^|[^\\\\])yyyy+/g,\"$1\"+c)).replace(/(^|[^\\\\])yy/g,\"$1\"+c.toString().substr(2,2))).replace(/(^|[^\\\\])y/g,\"$1\"+c);var h=(a?t.getUTCMonth():t.getMonth())+1;e=(e=(e=(e=e.replace(/(^|[^\\\\])MMMM+/g,\"$1\"+s[0])).replace(/(^|[^\\\\])MMM/g,\"$1\"+r[0])).replace(/(^|[^\\\\])MM/g,\"$1\"+l(h))).replace(/(^|[^\\\\])M/g,\"$1\"+h);var d=a?t.getUTCDate():t.getDate();e=(e=(e=(e=e.replace(/(^|[^\\\\])dddd+/g,\"$1\"+n[0])).replace(/(^|[^\\\\])ddd/g,\"$1\"+o[0])).replace(/(^|[^\\\\])dd/g,\"$1\"+l(d))).replace(/(^|[^\\\\])d/g,\"$1\"+d);var u=a?t.getUTCHours():t.getHours(),g=u>12?u-12:0===u?12:u;e=(e=(e=(e=e.replace(/(^|[^\\\\])HH+/g,\"$1\"+l(u))).replace(/(^|[^\\\\])H/g,\"$1\"+u)).replace(/(^|[^\\\\])hh+/g,\"$1\"+l(g))).replace(/(^|[^\\\\])h/g,\"$1\"+g);var f=a?t.getUTCMinutes():t.getMinutes();e=(e=e.replace(/(^|[^\\\\])mm+/g,\"$1\"+l(f))).replace(/(^|[^\\\\])m/g,\"$1\"+f);var p=a?t.getUTCSeconds():t.getSeconds();e=(e=e.replace(/(^|[^\\\\])ss+/g,\"$1\"+l(p))).replace(/(^|[^\\\\])s/g,\"$1\"+p);var x=a?t.getUTCMilliseconds():t.getMilliseconds();e=e.replace(/(^|[^\\\\])fff+/g,\"$1\"+l(x,3)),x=Math.round(x/10),e=e.replace(/(^|[^\\\\])ff/g,\"$1\"+l(x)),x=Math.round(x/10);var b=u<12?\"AM\":\"PM\";e=(e=(e=e.replace(/(^|[^\\\\])f/g,\"$1\"+x)).replace(/(^|[^\\\\])TT+/g,\"$1\"+b)).replace(/(^|[^\\\\])T/g,\"$1\"+b.charAt(0));var v=b.toLowerCase();e=(e=e.replace(/(^|[^\\\\])tt+/g,\"$1\"+v)).replace(/(^|[^\\\\])t/g,\"$1\"+v.charAt(0));var m=-t.getTimezoneOffset(),y=a||!m?\"Z\":m>0?\"+\":\"-\";if(!a){var w=(m=Math.abs(m))%60;y+=l(Math.floor(m/60))+\":\"+l(w)}e=e.replace(/(^|[^\\\\])K/g,\"$1\"+y);var k=(a?t.getUTCDay():t.getDay())+1;return(e=(e=(e=(e=e.replace(new RegExp(n[0],\"g\"),n[k])).replace(new RegExp(o[0],\"g\"),o[k])).replace(new RegExp(s[0],\"g\"),s[h])).replace(new RegExp(r[0],\"g\"),r[h])).replace(/\\\\(.)/g,\"$1\")}},{key:\"getTimeUnitsfromTimestamp\",value:function(t,e,i){var a=this.w;void 0!==a.config.xaxis.min&&(t=a.config.xaxis.min),void 0!==a.config.xaxis.max&&(e=a.config.xaxis.max);var s=this.getDate(t),r=this.getDate(e),n=this.formatDate(s,\"yyyy MM dd HH mm ss fff\").split(\" \"),o=this.formatDate(r,\"yyyy MM dd HH mm ss fff\").split(\" \");return{minMillisecond:parseInt(n[6],10),maxMillisecond:parseInt(o[6],10),minSecond:parseInt(n[5],10),maxSecond:parseInt(o[5],10),minMinute:parseInt(n[4],10),maxMinute:parseInt(o[4],10),minHour:parseInt(n[3],10),maxHour:parseInt(o[3],10),minDate:parseInt(n[2],10),maxDate:parseInt(o[2],10),minMonth:parseInt(n[1],10)-1,maxMonth:parseInt(o[1],10)-1,minYear:parseInt(n[0],10),maxYear:parseInt(o[0],10)}}},{key:\"isLeapYear\",value:function(t){return t%4==0&&t%100!=0||t%400==0}},{key:\"calculcateLastDaysOfMonth\",value:function(t,e,i){return this.determineDaysOfMonths(t,e)-i}},{key:\"determineDaysOfYear\",value:function(t){var e=365;return this.isLeapYear(t)&&(e=366),e}},{key:\"determineRemainingDaysOfYear\",value:function(t,e,i){var a=this.daysCntOfYear[e]+i;return e>1&&this.isLeapYear()&&a++,a}},{key:\"determineDaysOfMonths\",value:function(t,e){var i=30;switch(t=n.monthMod(t),!0){case this.months30.indexOf(t)>-1:2===t&&(i=this.isLeapYear(e)?29:28);break;case this.months31.indexOf(t)>-1:default:i=31}return i}}])&&D(e.prototype,i),t}();function F(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const H=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.tooltipKeyFormat=\"dd MMM\"}var e,i;return e=t,(i=[{key:\"xLabelFormat\",value:function(t,e,i,a){var s=this.w;if(\"datetime\"===s.config.xaxis.type&&void 0===s.config.xaxis.labels.formatter&&void 0===s.config.tooltip.x.formatter){var r=new R(this.ctx);return r.formatDate(r.getDate(e),s.config.tooltip.x.format)}return t(e,i,a)}},{key:\"defaultGeneralFormatter\",value:function(t){return Array.isArray(t)?t.map((function(t){return t})):t}},{key:\"defaultYFormatter\",value:function(t,e,i){var a=this.w;return n.isNumber(t)&&(t=0!==a.globals.yValueDecimal?t.toFixed(void 0!==e.decimalsInFloat?e.decimalsInFloat:a.globals.yValueDecimal):a.globals.maxYArr[i]-a.globals.minYArr[i]<5?t.toFixed(1):t.toFixed(0)),t}},{key:\"setLabelFormatters\",value:function(){var t=this,e=this.w;return e.globals.xaxisTooltipFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttKeyFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttZFormatter=function(t){return t},e.globals.legendFormatter=function(e){return t.defaultGeneralFormatter(e)},void 0!==e.config.xaxis.labels.formatter?e.globals.xLabelFormatter=e.config.xaxis.labels.formatter:e.globals.xLabelFormatter=function(t){if(n.isNumber(t)){if(!e.config.xaxis.convertedCatToNumeric&&\"numeric\"===e.config.xaxis.type){if(n.isNumber(e.config.xaxis.decimalsInFloat))return t.toFixed(e.config.xaxis.decimalsInFloat);var i=e.globals.maxX-e.globals.minX;return i>0&&i<100?t.toFixed(1):t.toFixed(0)}return e.globals.isBarHorizontal&&e.globals.maxY-e.globals.minYArr<4?t.toFixed(1):t.toFixed(0)}return t},\"function\"==typeof e.config.tooltip.x.formatter?e.globals.ttKeyFormatter=e.config.tooltip.x.formatter:e.globals.ttKeyFormatter=e.globals.xLabelFormatter,\"function\"==typeof e.config.xaxis.tooltip.formatter&&(e.globals.xaxisTooltipFormatter=e.config.xaxis.tooltip.formatter),(Array.isArray(e.config.tooltip.y)||void 0!==e.config.tooltip.y.formatter)&&(e.globals.ttVal=e.config.tooltip.y),void 0!==e.config.tooltip.z.formatter&&(e.globals.ttZFormatter=e.config.tooltip.z.formatter),void 0!==e.config.legend.formatter&&(e.globals.legendFormatter=e.config.legend.formatter),e.config.yaxis.forEach((function(i,a){void 0!==i.labels.formatter?e.globals.yLabelFormatters[a]=i.labels.formatter:e.globals.yLabelFormatters[a]=function(s){return e.globals.xyCharts?Array.isArray(s)?s.map((function(e){return t.defaultYFormatter(e,i,a)})):t.defaultYFormatter(s,i,a):s}})),e.globals}},{key:\"heatmapLabelFormatters\",value:function(){var t=this.w;if(\"heatmap\"===t.config.chart.type){t.globals.yAxisScale[0].result=t.globals.seriesNames.slice();var e=t.globals.seriesNames.reduce((function(t,e){return t.length>e.length?t:e}),0);t.globals.yAxisScale[0].niceMax=e,t.globals.yAxisScale[0].niceMin=e}}}])&&F(e.prototype,i),t}();function N(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function W(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?N(Object(i),!0).forEach((function(e){j(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):N(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function j(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function B(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var G=function(t){var e,i=t.isTimeline,a=t.ctx,s=t.seriesIndex,r=t.dataPointIndex,n=t.y1,o=t.y2,l=t.w,c=l.globals.seriesRangeStart[s][r],h=l.globals.seriesRangeEnd[s][r],d=l.globals.labels[r],u=l.config.series[s].name?l.config.series[s].name:\"\",g=l.globals.ttKeyFormatter,f=l.config.tooltip.y.title.formatter,p={w:l,seriesIndex:s,dataPointIndex:r,start:c,end:h};\"function\"==typeof f&&(u=f(u,p)),null!==(e=l.config.series[s].data[r])&&void 0!==e&&e.x&&(d=l.config.series[s].data[r].x),i||\"datetime\"===l.config.xaxis.type&&(d=new H(a).xLabelFormat(l.globals.ttKeyFormatter,d,d,{i:void 0,dateFormatter:new R(a).formatDate,w:l})),\"function\"==typeof g&&(d=g(d,p)),Number.isFinite(n)&&Number.isFinite(o)&&(c=n,h=o);var x=\"\",b=\"\",v=l.globals.colors[s];if(void 0===l.config.tooltip.x.formatter)if(\"datetime\"===l.config.xaxis.type){var m=new R(a);x=m.formatDate(m.getDate(c),l.config.tooltip.x.format),b=m.formatDate(m.getDate(h),l.config.tooltip.x.format)}else x=c,b=h;else x=l.config.tooltip.x.formatter(c),b=l.config.tooltip.x.formatter(h);return{start:c,end:h,startVal:x,endVal:b,ylabel:d,color:v,seriesName:u}},V=function(t){var e=t.color,i=t.seriesName,a=t.ylabel,s=t.start,r=t.end,n=t.seriesIndex,o=t.dataPointIndex,l=t.ctx.tooltip.tooltipLabels.getFormatters(n);s=l.yLbFormatter(s),r=l.yLbFormatter(r);var c=l.yLbFormatter(t.w.globals.series[n][o]),h='<span class=\"value start-value\">\\n  '.concat(s,'\\n  </span> <span class=\"separator\">-</span> <span class=\"value end-value\">\\n  ').concat(r,\"\\n  </span>\");return'<div class=\"apexcharts-tooltip-rangebar\"><div> <span class=\"series-name\" style=\"color: '+e+'\">'+(i||\"\")+'</span></div><div> <span class=\"category\">'+a+\": </span> \"+(t.w.globals.comboCharts?\"rangeArea\"===t.w.config.series[n].type||\"rangeBar\"===t.w.config.series[n].type?h:\"<span>\".concat(c,\"</span>\"):h)+\" </div></div>\"},_=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.opts=e}var e,i;return e=t,(i=[{key:\"line\",value:function(){return{chart:{animations:{easing:\"swing\"}},dataLabels:{enabled:!1},stroke:{width:5,curve:\"straight\"},markers:{size:0,hover:{sizeOffset:6}},xaxis:{crosshairs:{width:1}}}}},{key:\"sparkline\",value:function(t){return this.opts.yaxis[0].show=!1,this.opts.yaxis[0].title.text=\"\",this.opts.yaxis[0].axisBorder.show=!1,this.opts.yaxis[0].axisTicks.show=!1,this.opts.yaxis[0].floating=!0,n.extend(t,{grid:{show:!1,padding:{left:0,right:0,top:0,bottom:0}},legend:{show:!1},xaxis:{labels:{show:!1},tooltip:{enabled:!1},axisBorder:{show:!1},axisTicks:{show:!1}},chart:{toolbar:{show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1}})}},{key:\"bar\",value:function(){return{chart:{stacked:!1,animations:{easing:\"swing\"}},plotOptions:{bar:{dataLabels:{position:\"center\"}}},dataLabels:{style:{colors:[\"#fff\"]},background:{enabled:!1}},stroke:{width:0,lineCap:\"round\"},fill:{opacity:.85},legend:{markers:{shape:\"square\",radius:2,size:8}},tooltip:{shared:!1,intersect:!0},xaxis:{tooltip:{enabled:!1},tickPlacement:\"between\",crosshairs:{width:\"barWidth\",position:\"back\",fill:{type:\"gradient\"},dropShadow:{enabled:!1},stroke:{width:0}}}}}},{key:\"candlestick\",value:function(){var t=this;return{stroke:{width:1,colors:[\"#333\"]},fill:{opacity:1},dataLabels:{enabled:!1},tooltip:{shared:!0,custom:function(e){var i=e.seriesIndex,a=e.dataPointIndex,s=e.w;return t._getBoxTooltip(s,i,a,[\"Open\",\"High\",\"\",\"Low\",\"Close\"],\"candlestick\")}},states:{active:{filter:{type:\"none\"}}},xaxis:{crosshairs:{width:1}}}}},{key:\"boxPlot\",value:function(){var t=this;return{chart:{animations:{dynamicAnimation:{enabled:!1}}},stroke:{width:1,colors:[\"#24292e\"]},dataLabels:{enabled:!1},tooltip:{shared:!0,custom:function(e){var i=e.seriesIndex,a=e.dataPointIndex,s=e.w;return t._getBoxTooltip(s,i,a,[\"Minimum\",\"Q1\",\"Median\",\"Q3\",\"Maximum\"],\"boxPlot\")}},markers:{size:5,strokeWidth:1,strokeColors:\"#111\"},xaxis:{crosshairs:{width:1}}}}},{key:\"rangeBar\",value:function(){return{stroke:{width:0,lineCap:\"square\"},plotOptions:{bar:{borderRadius:0,dataLabels:{position:\"center\"}}},dataLabels:{enabled:!1,formatter:function(t,e){e.ctx;var i=e.seriesIndex,a=e.dataPointIndex,s=e.w,r=function(){var t=s.globals.seriesRangeStart[i][a];return s.globals.seriesRangeEnd[i][a]-t};return s.globals.comboCharts?\"rangeBar\"===s.config.series[i].type||\"rangeArea\"===s.config.series[i].type?r():t:r()},background:{enabled:!1},style:{colors:[\"#fff\"]}},tooltip:{shared:!1,followCursor:!0,custom:function(t){return t.w.config.plotOptions&&t.w.config.plotOptions.bar&&t.w.config.plotOptions.bar.horizontal?function(t){var e=G(W(W({},t),{},{isTimeline:!0})),i=e.color,a=e.seriesName,s=e.ylabel,r=e.startVal,n=e.endVal;return V(W(W({},t),{},{color:i,seriesName:a,ylabel:s,start:r,end:n}))}(t):function(t){var e=G(t),i=e.color,a=e.seriesName,s=e.ylabel,r=e.start,n=e.end;return V(W(W({},t),{},{color:i,seriesName:a,ylabel:s,start:r,end:n}))}(t)}},xaxis:{tickPlacement:\"between\",tooltip:{enabled:!1},crosshairs:{stroke:{width:0}}}}}},{key:\"area\",value:function(){return{stroke:{width:4,fill:{type:\"solid\",gradient:{inverseColors:!1,shade:\"light\",type:\"vertical\",opacityFrom:.65,opacityTo:.5,stops:[0,100,100]}}},fill:{type:\"gradient\",gradient:{inverseColors:!1,shade:\"light\",type:\"vertical\",opacityFrom:.65,opacityTo:.5,stops:[0,100,100]}},markers:{size:0,hover:{sizeOffset:6}},tooltip:{followCursor:!1}}}},{key:\"rangeArea\",value:function(){return{stroke:{curve:\"straight\",width:0},fill:{type:\"solid\",opacity:.6},markers:{size:0},states:{hover:{filter:{type:\"none\"}},active:{filter:{type:\"none\"}}},tooltip:{intersect:!1,shared:!0,followCursor:!0,custom:function(t){return function(t){var e=G(t),i=e.color,a=e.seriesName,s=e.ylabel,r=e.start,n=e.end;return V(W(W({},t),{},{color:i,seriesName:a,ylabel:s,start:r,end:n}))}(t)}}}}},{key:\"brush\",value:function(t){return n.extend(t,{chart:{toolbar:{autoSelected:\"selection\",show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1},stroke:{width:1},tooltip:{enabled:!1},xaxis:{tooltip:{enabled:!1}}})}},{key:\"stacked100\",value:function(t){t.dataLabels=t.dataLabels||{},t.dataLabels.formatter=t.dataLabels.formatter||void 0;var e=t.dataLabels.formatter;return t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})),\"bar\"===t.chart.type&&(t.dataLabels.formatter=e||function(t){return\"number\"==typeof t&&t?t.toFixed(0)+\"%\":t}),t}},{key:\"stackedBars\",value:function(){var t=this.bar();return W(W({},t),{},{plotOptions:W(W({},t.plotOptions),{},{bar:W(W({},t.plotOptions.bar),{},{borderRadiusApplication:\"end\",borderRadiusWhenStacked:\"last\"})})})}},{key:\"convertCatToNumeric\",value:function(t){return t.xaxis.convertedCatToNumeric=!0,t}},{key:\"convertCatToNumericXaxis\",value:function(t,e,i){t.xaxis.type=\"numeric\",t.xaxis.labels=t.xaxis.labels||{},t.xaxis.labels.formatter=t.xaxis.labels.formatter||function(t){return n.isNumber(t)?Math.floor(t):t};var a=t.xaxis.labels.formatter,s=t.xaxis.categories&&t.xaxis.categories.length?t.xaxis.categories:t.labels;return i&&i.length&&(s=i.map((function(t){return Array.isArray(t)?t:String(t)}))),s&&s.length&&(t.xaxis.labels.formatter=function(t){return n.isNumber(t)?a(s[Math.floor(t)-1]):a(t)}),t.xaxis.categories=[],t.labels=[],t.xaxis.tickAmount=t.xaxis.tickAmount||\"dataPoints\",t}},{key:\"bubble\",value:function(){return{dataLabels:{style:{colors:[\"#fff\"]}},tooltip:{shared:!1,intersect:!0},xaxis:{crosshairs:{width:0}},fill:{type:\"solid\",gradient:{shade:\"light\",inverse:!0,shadeIntensity:.55,opacityFrom:.4,opacityTo:.8}}}}},{key:\"scatter\",value:function(){return{dataLabels:{enabled:!1},tooltip:{shared:!1,intersect:!0},markers:{size:6,strokeWidth:1,hover:{sizeOffset:2}}}}},{key:\"heatmap\",value:function(){return{chart:{stacked:!1},fill:{opacity:1},dataLabels:{style:{colors:[\"#fff\"]}},stroke:{colors:[\"#fff\"]},tooltip:{followCursor:!0,marker:{show:!1},x:{show:!1}},legend:{position:\"top\",markers:{shape:\"square\",size:10,offsetY:2}},grid:{padding:{right:20}}}}},{key:\"treemap\",value:function(){return{chart:{zoom:{enabled:!1}},dataLabels:{style:{fontSize:14,fontWeight:600,colors:[\"#fff\"]}},stroke:{show:!0,width:2,colors:[\"#fff\"]},legend:{show:!1},fill:{gradient:{stops:[0,100]}},tooltip:{followCursor:!0,x:{show:!1}},grid:{padding:{left:0,right:0}},xaxis:{crosshairs:{show:!1},tooltip:{enabled:!1}}}}},{key:\"pie\",value:function(){return{chart:{toolbar:{show:!1}},plotOptions:{pie:{donut:{labels:{show:!1}}}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},style:{colors:[\"#fff\"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:[\"#fff\"]},fill:{opacity:1,gradient:{shade:\"light\",stops:[0,100]}},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"donut\",value:function(){return{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},style:{colors:[\"#fff\"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:[\"#fff\"]},fill:{opacity:1,gradient:{shade:\"light\",shadeIntensity:.35,stops:[80,100],opacityFrom:1,opacityTo:1}},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"polarArea\",value:function(){return this.opts.yaxis[0].tickAmount=this.opts.yaxis[0].tickAmount?this.opts.yaxis[0].tickAmount:6,{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},enabled:!1},stroke:{show:!0,width:2},fill:{opacity:.7},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"radar\",value:function(){return this.opts.yaxis[0].labels.offsetY=this.opts.yaxis[0].labels.offsetY?this.opts.yaxis[0].labels.offsetY:6,{dataLabels:{enabled:!1,style:{fontSize:\"11px\"}},stroke:{width:2},markers:{size:3,strokeWidth:1,strokeOpacity:1},fill:{opacity:.2},tooltip:{shared:!1,intersect:!0,followCursor:!0},grid:{show:!1},xaxis:{labels:{formatter:function(t){return t},style:{colors:[\"#a8a8a8\"],fontSize:\"11px\"}},tooltip:{enabled:!1},crosshairs:{show:!1}}}}},{key:\"radialBar\",value:function(){return{chart:{animations:{dynamicAnimation:{enabled:!0,speed:800}},toolbar:{show:!1}},fill:{gradient:{shade:\"dark\",shadeIntensity:.4,inverseColors:!1,type:\"diagonal2\",opacityFrom:1,opacityTo:1,stops:[70,98,100]}},legend:{show:!1,position:\"right\"},tooltip:{enabled:!1,fillSeriesColor:!0}}}},{key:\"_getBoxTooltip\",value:function(t,e,i,a,s){var r=t.globals.seriesCandleO[e][i],n=t.globals.seriesCandleH[e][i],o=t.globals.seriesCandleM[e][i],l=t.globals.seriesCandleL[e][i],c=t.globals.seriesCandleC[e][i];return t.config.series[e].type&&t.config.series[e].type!==s?'<div class=\"apexcharts-custom-tooltip\">\\n          '.concat(t.config.series[e].name?t.config.series[e].name:\"series-\"+(e+1),\": <strong>\").concat(t.globals.series[e][i],\"</strong>\\n        </div>\"):'<div class=\"apexcharts-tooltip-box apexcharts-tooltip-'.concat(t.config.chart.type,'\">')+\"<div>\".concat(a[0],': <span class=\"value\">')+r+\"</span></div>\"+\"<div>\".concat(a[1],': <span class=\"value\">')+n+\"</span></div>\"+(o?\"<div>\".concat(a[2],': <span class=\"value\">')+o+\"</span></div>\":\"\")+\"<div>\".concat(a[3],': <span class=\"value\">')+l+\"</span></div>\"+\"<div>\".concat(a[4],': <span class=\"value\">')+c+\"</span></div></div>\"}}])&&B(e.prototype,i),t}();function U(t){return U=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},U(t)}function q(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Z=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.opts=e}var e,i;return e=t,(i=[{key:\"init\",value:function(t){var e=t.responsiveOverride,i=this.opts,a=new I,s=new _(i);this.chartType=i.chart.type,i=this.extendYAxis(i),i=this.extendAnnotations(i);var r=a.init(),o={};if(i&&\"object\"===U(i)){var l={};l=-1!==[\"line\",\"area\",\"bar\",\"candlestick\",\"boxPlot\",\"rangeBar\",\"rangeArea\",\"bubble\",\"scatter\",\"heatmap\",\"treemap\",\"pie\",\"polarArea\",\"donut\",\"radar\",\"radialBar\"].indexOf(i.chart.type)?s[i.chart.type]():s.line(),i.chart.stacked&&\"bar\"===i.chart.type&&(l=s.stackedBars()),i.chart.brush&&i.chart.brush.enabled&&(l=s.brush(l)),i.chart.stacked&&\"100%\"===i.chart.stackType&&(i=s.stacked100(i)),this.checkForDarkTheme(window.Apex),this.checkForDarkTheme(i),i.xaxis=i.xaxis||window.Apex.xaxis||{},e||(i.xaxis.convertedCatToNumeric=!1),((i=this.checkForCatToNumericXAxis(this.chartType,l,i)).chart.sparkline&&i.chart.sparkline.enabled||window.Apex.chart&&window.Apex.chart.sparkline&&window.Apex.chart.sparkline.enabled)&&(l=s.sparkline(l)),o=n.extend(r,l)}var c=n.extend(o,window.Apex);return r=n.extend(c,i),this.handleUserInputErrors(r)}},{key:\"checkForCatToNumericXAxis\",value:function(t,e,i){var a=new _(i),s=(\"bar\"===t||\"boxPlot\"===t)&&i.plotOptions&&i.plotOptions.bar&&i.plotOptions.bar.horizontal,r=\"pie\"===t||\"polarArea\"===t||\"donut\"===t||\"radar\"===t||\"radialBar\"===t||\"heatmap\"===t,n=\"datetime\"!==i.xaxis.type&&\"numeric\"!==i.xaxis.type,o=i.xaxis.tickPlacement?i.xaxis.tickPlacement:e.xaxis&&e.xaxis.tickPlacement;return s||r||!n||\"between\"===o||(i=a.convertCatToNumeric(i)),i}},{key:\"extendYAxis\",value:function(t,e){var i=new I;(void 0===t.yaxis||!t.yaxis||Array.isArray(t.yaxis)&&0===t.yaxis.length)&&(t.yaxis={}),t.yaxis.constructor!==Array&&window.Apex.yaxis&&window.Apex.yaxis.constructor!==Array&&(t.yaxis=n.extend(t.yaxis,window.Apex.yaxis)),t.yaxis.constructor!==Array?t.yaxis=[n.extend(i.yAxis,t.yaxis)]:t.yaxis=n.extendArray(t.yaxis,i.yAxis);var a=!1;t.yaxis.forEach((function(t){t.logarithmic&&(a=!0)}));var s=t.series;return e&&!s&&(s=e.config.series),a&&s.length!==t.yaxis.length&&s.length&&(t.yaxis=s.map((function(e,a){if(e.name||(s[a].name=\"series-\".concat(a+1)),t.yaxis[a])return t.yaxis[a].seriesName=s[a].name,t.yaxis[a];var r=n.extend(i.yAxis,t.yaxis[0]);return r.show=!1,r}))),a&&s.length>1&&s.length!==t.yaxis.length&&console.warn(\"A multi-series logarithmic chart should have equal number of series and y-axes. Please make sure to equalize both.\"),t}},{key:\"extendAnnotations\",value:function(t){return void 0===t.annotations&&(t.annotations={},t.annotations.yaxis=[],t.annotations.xaxis=[],t.annotations.points=[]),t=this.extendYAxisAnnotations(t),t=this.extendXAxisAnnotations(t),this.extendPointAnnotations(t)}},{key:\"extendYAxisAnnotations\",value:function(t){var e=new I;return t.annotations.yaxis=n.extendArray(void 0!==t.annotations.yaxis?t.annotations.yaxis:[],e.yAxisAnnotation),t}},{key:\"extendXAxisAnnotations\",value:function(t){var e=new I;return t.annotations.xaxis=n.extendArray(void 0!==t.annotations.xaxis?t.annotations.xaxis:[],e.xAxisAnnotation),t}},{key:\"extendPointAnnotations\",value:function(t){var e=new I;return t.annotations.points=n.extendArray(void 0!==t.annotations.points?t.annotations.points:[],e.pointAnnotation),t}},{key:\"checkForDarkTheme\",value:function(t){t.theme&&\"dark\"===t.theme.mode&&(t.tooltip||(t.tooltip={}),\"light\"!==t.tooltip.theme&&(t.tooltip.theme=\"dark\"),t.chart.foreColor||(t.chart.foreColor=\"#f6f7f8\"),t.chart.background||(t.chart.background=\"#424242\"),t.theme.palette||(t.theme.palette=\"palette4\"))}},{key:\"handleUserInputErrors\",value:function(t){var e=t;if(e.tooltip.shared&&e.tooltip.intersect)throw new Error(\"tooltip.shared cannot be enabled when tooltip.intersect is true. Turn off any other option by setting it to false.\");if(\"bar\"===e.chart.type&&e.plotOptions.bar.horizontal){if(e.yaxis.length>1)throw new Error(\"Multiple Y Axis for bars are not supported. Switch to column chart by setting plotOptions.bar.horizontal=false\");e.yaxis[0].reversed&&(e.yaxis[0].opposite=!0),e.xaxis.tooltip.enabled=!1,e.yaxis[0].tooltip.enabled=!1,e.chart.zoom.enabled=!1}return\"bar\"!==e.chart.type&&\"rangeBar\"!==e.chart.type||e.tooltip.shared&&\"barWidth\"===e.xaxis.crosshairs.width&&e.series.length>1&&(e.xaxis.crosshairs.width=\"tickWidth\"),\"candlestick\"!==e.chart.type&&\"boxPlot\"!==e.chart.type||e.yaxis[0].reversed&&(console.warn(\"Reversed y-axis in \".concat(e.chart.type,\" chart is not supported.\")),e.yaxis[0].reversed=!1),e}}])&&q(e.prototype,i),t}();function $(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var J=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t)}var e,i;return e=t,(i=[{key:\"initGlobalVars\",value:function(t){t.series=[],t.seriesCandleO=[],t.seriesCandleH=[],t.seriesCandleM=[],t.seriesCandleL=[],t.seriesCandleC=[],t.seriesRangeStart=[],t.seriesRangeEnd=[],t.seriesRange=[],t.seriesPercent=[],t.seriesGoals=[],t.seriesX=[],t.seriesZ=[],t.seriesNames=[],t.seriesTotals=[],t.seriesLog=[],t.seriesColors=[],t.stackedSeriesTotals=[],t.seriesXvalues=[],t.seriesYvalues=[],t.labels=[],t.hasGroups=!1,t.groups=[],t.categoryLabels=[],t.timescaleLabels=[],t.noLabelsProvided=!1,t.resizeTimer=null,t.selectionResizeTimer=null,t.delayedElements=[],t.pointsArray=[],t.dataLabelsRects=[],t.isXNumeric=!1,t.skipLastTimelinelabel=!1,t.skipFirstTimelinelabel=!1,t.isDataXYZ=!1,t.isMultiLineX=!1,t.isMultipleYAxis=!1,t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE,t.minYArr=[],t.maxYArr=[],t.maxX=-Number.MAX_VALUE,t.minX=Number.MAX_VALUE,t.initialMaxX=-Number.MAX_VALUE,t.initialMinX=Number.MAX_VALUE,t.maxDate=0,t.minDate=Number.MAX_VALUE,t.minZ=Number.MAX_VALUE,t.maxZ=-Number.MAX_VALUE,t.minXDiff=Number.MAX_VALUE,t.yAxisScale=[],t.xAxisScale=null,t.xAxisTicksPositions=[],t.yLabelsCoords=[],t.yTitleCoords=[],t.barPadForNumericAxis=0,t.padHorizontal=0,t.xRange=0,t.yRange=[],t.zRange=0,t.dataPoints=0,t.xTickAmount=0}},{key:\"globalVars\",value:function(t){return{chartID:null,cuid:null,events:{beforeMount:[],mounted:[],updated:[],clicked:[],selection:[],dataPointSelection:[],zoomed:[],scrolled:[]},colors:[],clientX:null,clientY:null,fill:{colors:[]},stroke:{colors:[]},dataLabels:{style:{colors:[]}},radarPolygons:{fill:{colors:[]}},markers:{colors:[],size:t.markers.size,largestSize:0},animationEnded:!1,isTouchDevice:\"ontouchstart\"in window||navigator.msMaxTouchPoints,isDirty:!1,isExecCalled:!1,initialConfig:null,initialSeries:[],lastXAxis:[],lastYAxis:[],columnSeries:null,labels:[],timescaleLabels:[],noLabelsProvided:!1,allSeriesCollapsed:!1,collapsedSeries:[],collapsedSeriesIndices:[],ancillaryCollapsedSeries:[],ancillaryCollapsedSeriesIndices:[],risingSeries:[],dataFormatXNumeric:!1,capturedSeriesIndex:-1,capturedDataPointIndex:-1,selectedDataPoints:[],goldenPadding:35,invalidLogScale:!1,ignoreYAxisIndexes:[],yAxisSameScaleIndices:[],maxValsInArrayIndex:0,radialSize:0,selection:void 0,zoomEnabled:\"zoom\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.zoom&&t.chart.zoom.enabled,panEnabled:\"pan\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.pan,selectionEnabled:\"selection\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.selection,yaxis:null,mousedown:!1,lastClientPosition:{},visibleXRange:void 0,yValueDecimal:0,total:0,SVGNS:\"http://www.w3.org/2000/svg\",svgWidth:0,svgHeight:0,noData:!1,locale:{},dom:{},memory:{methodsToExec:[]},shouldAnimate:!0,skipLastTimelinelabel:!1,skipFirstTimelinelabel:!1,delayedElements:[],axisCharts:!0,isDataXYZ:!1,resized:!1,resizeTimer:null,comboCharts:!1,dataChanged:!1,previousPaths:[],allSeriesHasEqualX:!0,pointsArray:[],dataLabelsRects:[],lastDrawnDataLabelsIndexes:[],hasNullValues:!1,easing:null,zoomed:!1,gridWidth:0,gridHeight:0,rotateXLabels:!1,defaultLabels:!1,xLabelFormatter:void 0,yLabelFormatters:[],xaxisTooltipFormatter:void 0,ttKeyFormatter:void 0,ttVal:void 0,ttZFormatter:void 0,LINE_HEIGHT_RATIO:1.618,xAxisLabelsHeight:0,xAxisGroupLabelsHeight:0,xAxisLabelsWidth:0,yAxisLabelsWidth:0,scaleX:1,scaleY:1,translateX:0,translateY:0,translateYAxisX:[],yAxisWidths:[],translateXAxisY:0,translateXAxisX:0,tooltip:null}}},{key:\"init\",value:function(t){var e=this.globalVars(t);return this.initGlobalVars(e),e.initialConfig=n.extend({},t),e.initialSeries=n.clone(t.series),e.lastXAxis=n.clone(e.initialConfig.xaxis),e.lastYAxis=n.clone(e.initialConfig.yaxis),e}}])&&$(e.prototype,i),t}();function Q(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var K=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.opts=e}var e,i;return e=t,(i=[{key:\"init\",value:function(){var t=new Z(this.opts).init({responsiveOverride:!1});return{config:t,globals:(new J).init(t)}}}])&&Q(e.prototype,i),t}();function tt(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function et(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?tt(Object(i),!0).forEach((function(e){it(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):tt(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function it(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function at(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const st=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.opts=null,this.seriesIndex=0}var e,i;return e=t,(i=[{key:\"clippedImgArea\",value:function(t){var e=this.w,i=e.config,a=parseInt(e.globals.gridWidth,10),s=parseInt(e.globals.gridHeight,10),r=a>s?a:s,n=t.image,o=0,l=0;void 0===t.width&&void 0===t.height?void 0!==i.fill.image.width&&void 0!==i.fill.image.height?(o=i.fill.image.width+1,l=i.fill.image.height):(o=r+1,l=r):(o=t.width,l=t.height);var c=document.createElementNS(e.globals.SVGNS,\"pattern\");b.setAttrs(c,{id:t.patternID,patternUnits:t.patternUnits?t.patternUnits:\"userSpaceOnUse\",width:o+\"px\",height:l+\"px\"});var h=document.createElementNS(e.globals.SVGNS,\"image\");c.appendChild(h),h.setAttributeNS(window.SVG.xlink,\"href\",n),b.setAttrs(h,{x:0,y:0,preserveAspectRatio:\"none\",width:o+\"px\",height:l+\"px\"}),h.style.opacity=t.opacity,e.globals.dom.elDefs.node.appendChild(c)}},{key:\"getSeriesIndex\",value:function(t){var e=this.w;return(\"bar\"===e.config.chart.type||\"rangeBar\"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||\"heatmap\"===e.config.chart.type||\"treemap\"===e.config.chart.type?this.seriesIndex=t.seriesNumber:this.seriesIndex=t.seriesNumber%e.globals.series.length,this.seriesIndex}},{key:\"fillPath\",value:function(t){var e=this.w;this.opts=t;var i,a,s,r=this.w.config;this.seriesIndex=this.getSeriesIndex(t);var o=this.getFillColors()[this.seriesIndex];void 0!==e.globals.seriesColors[this.seriesIndex]&&(o=e.globals.seriesColors[this.seriesIndex]),\"function\"==typeof o&&(o=o({seriesIndex:this.seriesIndex,dataPointIndex:t.dataPointIndex,value:t.value,w:e}));var l=t.fillType?t.fillType:this.getFillType(this.seriesIndex),c=Array.isArray(r.fill.opacity)?r.fill.opacity[this.seriesIndex]:r.fill.opacity;t.color&&(o=t.color);var h=o;if(-1===o.indexOf(\"rgb\")?o.length<9&&(h=n.hexToRgba(o,c)):o.indexOf(\"rgba\")>-1&&(c=n.getOpacityFromRGBA(o)),t.opacity&&(c=t.opacity),\"pattern\"===l&&(a=this.handlePatternFill({fillConfig:t.fillConfig,patternFill:a,fillColor:o,fillOpacity:c,defaultColor:h})),\"gradient\"===l&&(s=this.handleGradientFill({fillConfig:t.fillConfig,fillColor:o,fillOpacity:c,i:this.seriesIndex})),\"image\"===l){var d=r.fill.image.src,u=t.patternID?t.patternID:\"\";this.clippedImgArea({opacity:c,image:Array.isArray(d)?t.seriesNumber<d.length?d[t.seriesNumber]:d[0]:d,width:t.width?t.width:void 0,height:t.height?t.height:void 0,patternUnits:t.patternUnits,patternID:\"pattern\".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(u)}),i=\"url(#pattern\".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(u,\")\")}else i=\"gradient\"===l?s:\"pattern\"===l?a:h;return t.solid&&(i=h),i}},{key:\"getFillType\",value:function(t){var e=this.w;return Array.isArray(e.config.fill.type)?e.config.fill.type[t]:e.config.fill.type}},{key:\"getFillColors\",value:function(){var t=this.w,e=t.config,i=this.opts,a=[];return t.globals.comboCharts?\"line\"===t.config.series[this.seriesIndex].type?Array.isArray(t.globals.stroke.colors)?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):Array.isArray(t.globals.fill.colors)?a=t.globals.fill.colors:a.push(t.globals.fill.colors):\"line\"===e.chart.type?Array.isArray(t.globals.stroke.colors)?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):Array.isArray(t.globals.fill.colors)?a=t.globals.fill.colors:a.push(t.globals.fill.colors),void 0!==i.fillColors&&(a=[],Array.isArray(i.fillColors)?a=i.fillColors.slice():a.push(i.fillColors)),a}},{key:\"handlePatternFill\",value:function(t){var e=t.fillConfig,i=(t.patternFill,t.fillColor),a=t.fillOpacity,s=t.defaultColor,r=this.w.config.fill;e&&(r=e);var n=this.opts,o=new b(this.ctx),l=Array.isArray(r.pattern.strokeWidth)?r.pattern.strokeWidth[this.seriesIndex]:r.pattern.strokeWidth,c=i;return Array.isArray(r.pattern.style)?void 0!==r.pattern.style[n.seriesNumber]?o.drawPattern(r.pattern.style[n.seriesNumber],r.pattern.width,r.pattern.height,c,l,a):s:o.drawPattern(r.pattern.style,r.pattern.width,r.pattern.height,c,l,a)}},{key:\"handleGradientFill\",value:function(t){var e=t.fillColor,i=t.fillOpacity,a=t.fillConfig,s=t.i,r=this.w.config.fill;a&&(r=et(et({},r),a));var o,l=this.opts,c=new b(this.ctx),h=new n,d=r.gradient.type,u=e,g=void 0===r.gradient.opacityFrom?i:Array.isArray(r.gradient.opacityFrom)?r.gradient.opacityFrom[s]:r.gradient.opacityFrom;u.indexOf(\"rgba\")>-1&&(g=n.getOpacityFromRGBA(u));var f=void 0===r.gradient.opacityTo?i:Array.isArray(r.gradient.opacityTo)?r.gradient.opacityTo[s]:r.gradient.opacityTo;if(void 0===r.gradient.gradientToColors||0===r.gradient.gradientToColors.length)o=\"dark\"===r.gradient.shade?h.shadeColor(-1*parseFloat(r.gradient.shadeIntensity),e.indexOf(\"rgb\")>-1?n.rgb2hex(e):e):h.shadeColor(parseFloat(r.gradient.shadeIntensity),e.indexOf(\"rgb\")>-1?n.rgb2hex(e):e);else if(r.gradient.gradientToColors[l.seriesNumber]){var p=r.gradient.gradientToColors[l.seriesNumber];o=p,p.indexOf(\"rgba\")>-1&&(f=n.getOpacityFromRGBA(p))}else o=e;if(r.gradient.gradientFrom&&(u=r.gradient.gradientFrom),r.gradient.gradientTo&&(o=r.gradient.gradientTo),r.gradient.inverseColors){var x=u;u=o,o=x}return u.indexOf(\"rgb\")>-1&&(u=n.rgb2hex(u)),o.indexOf(\"rgb\")>-1&&(o=n.rgb2hex(o)),c.drawGradient(d,u,o,g,f,l.size,r.gradient.stops,r.gradient.colorStops,s)}}])&&at(e.prototype,i),t}();function rt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var nt=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,i=[{key:\"setGlobalMarkerSize\",value:function(){var t=this.w;if(t.globals.markers.size=Array.isArray(t.config.markers.size)?t.config.markers.size:[t.config.markers.size],t.globals.markers.size.length>0){if(t.globals.markers.size.length<t.globals.series.length+1)for(var e=0;e<=t.globals.series.length;e++)void 0===t.globals.markers.size[e]&&t.globals.markers.size.push(t.globals.markers.size[0])}else t.globals.markers.size=t.config.series.map((function(e){return t.config.markers.size}))}},{key:\"plotChartMarkers\",value:function(t,e,i,a){var s,r=arguments.length>4&&void 0!==arguments[4]&&arguments[4],o=this.w,l=e,c=t,h=null,u=new b(this.ctx),g=o.config.markers.discrete&&o.config.markers.discrete.length;if((o.globals.markers.size[e]>0||r||g)&&(h=u.group({class:r||g?\"\":\"apexcharts-series-markers\"})).attr(\"clip-path\",\"url(#gridRectMarkerMask\".concat(o.globals.cuid,\")\")),Array.isArray(c.x))for(var f=0;f<c.x.length;f++){var p=i;1===i&&0===f&&(p=0),1===i&&1===f&&(p=1);var x=\"apexcharts-marker\";\"line\"!==o.config.chart.type&&\"area\"!==o.config.chart.type||o.globals.comboCharts||o.config.tooltip.intersect||(x+=\" no-pointer-events\");var v=Array.isArray(o.config.markers.size)?o.globals.markers.size[e]>0:o.config.markers.size>0;if(v||r||g){n.isNumber(c.y[f])?x+=\" w\".concat(n.randomId()):x=\"apexcharts-nullpoint\";var m=this.getMarkerConfig({cssClass:x,seriesIndex:e,dataPointIndex:p});o.config.series[l].data[p]&&(o.config.series[l].data[p].fillColor&&(m.pointFillColor=o.config.series[l].data[p].fillColor),o.config.series[l].data[p].strokeColor&&(m.pointStrokeColor=o.config.series[l].data[p].strokeColor)),a&&(m.pSize=a),(s=u.drawMarker(c.x[f],c.y[f],m)).attr(\"rel\",p),s.attr(\"j\",p),s.attr(\"index\",e),s.node.setAttribute(\"default-marker-size\",m.pSize);var y=new d(this.ctx);y.setSelectionFilter(s,e,p),this.addEvents(s),h&&h.add(s)}else void 0===o.globals.pointsArray[e]&&(o.globals.pointsArray[e]=[]),o.globals.pointsArray[e].push([c.x[f],c.y[f]])}return h}},{key:\"getMarkerConfig\",value:function(t){var e=t.cssClass,i=t.seriesIndex,a=t.dataPointIndex,s=void 0===a?null:a,r=t.finishRadius,n=void 0===r?null:r,o=this.w,l=this.getMarkerStyle(i),c=o.globals.markers.size[i],h=o.config.markers;return null!==s&&h.discrete.length&&h.discrete.map((function(t){t.seriesIndex===i&&t.dataPointIndex===s&&(l.pointStrokeColor=t.strokeColor,l.pointFillColor=t.fillColor,c=t.size,l.pointShape=t.shape)})),{pSize:null===n?c:n,pRadius:h.radius,width:Array.isArray(h.width)?h.width[i]:h.width,height:Array.isArray(h.height)?h.height[i]:h.height,pointStrokeWidth:Array.isArray(h.strokeWidth)?h.strokeWidth[i]:h.strokeWidth,pointStrokeColor:l.pointStrokeColor,pointFillColor:l.pointFillColor,shape:l.pointShape||(Array.isArray(h.shape)?h.shape[i]:h.shape),class:e,pointStrokeOpacity:Array.isArray(h.strokeOpacity)?h.strokeOpacity[i]:h.strokeOpacity,pointStrokeDashArray:Array.isArray(h.strokeDashArray)?h.strokeDashArray[i]:h.strokeDashArray,pointFillOpacity:Array.isArray(h.fillOpacity)?h.fillOpacity[i]:h.fillOpacity,seriesIndex:i}}},{key:\"addEvents\",value:function(t){var e=this.w,i=new b(this.ctx);t.node.addEventListener(\"mouseenter\",i.pathMouseEnter.bind(this.ctx,t)),t.node.addEventListener(\"mouseleave\",i.pathMouseLeave.bind(this.ctx,t)),t.node.addEventListener(\"mousedown\",i.pathMouseDown.bind(this.ctx,t)),t.node.addEventListener(\"click\",e.config.markers.onClick),t.node.addEventListener(\"dblclick\",e.config.markers.onDblClick),t.node.addEventListener(\"touchstart\",i.pathMouseDown.bind(this.ctx,t),{passive:!0})}},{key:\"getMarkerStyle\",value:function(t){var e=this.w,i=e.globals.markers.colors,a=e.config.markers.strokeColor||e.config.markers.strokeColors;return{pointStrokeColor:Array.isArray(a)?a[t]:a,pointFillColor:Array.isArray(i)?i[t]:i}}}],i&&rt(e.prototype,i),t}();function ot(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var lt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled}var e,i;return e=t,(i=[{key:\"draw\",value:function(t,e,i){var a=this.w,s=new b(this.ctx),r=i.realIndex,n=i.pointsPos,o=i.zRatio,l=i.elParent,c=s.group({class:\"apexcharts-series-markers apexcharts-series-\".concat(a.config.chart.type)});if(c.attr(\"clip-path\",\"url(#gridRectMarkerMask\".concat(a.globals.cuid,\")\")),Array.isArray(n.x))for(var h=0;h<n.x.length;h++){var d=e+1,u=!0;0===e&&0===h&&(d=0),0===e&&1===h&&(d=1);var g=0,f=a.globals.markers.size[r];if(o!==1/0){var p=a.config.plotOptions.bubble;f=a.globals.seriesZ[r][d],p.zScaling&&(f/=o),p.minBubbleRadius&&f<p.minBubbleRadius&&(f=p.minBubbleRadius),p.maxBubbleRadius&&f>p.maxBubbleRadius&&(f=p.maxBubbleRadius)}a.config.chart.animations.enabled||(g=f);var x=n.x[h],v=n.y[h];if(g=g||0,null!==v&&void 0!==a.globals.series[r][d]||(u=!1),u){var m=this.drawPoint(x,v,g,f,r,d,e);c.add(m)}l.add(c)}}},{key:\"drawPoint\",value:function(t,e,i,a,s,r,n){var o=this.w,c=s,h=new l(this.ctx),u=new d(this.ctx),g=new st(this.ctx),f=new nt(this.ctx),p=new b(this.ctx),x=f.getMarkerConfig({cssClass:\"apexcharts-marker\",seriesIndex:c,dataPointIndex:r,finishRadius:\"bubble\"===o.config.chart.type||o.globals.comboCharts&&o.config.series[s]&&\"bubble\"===o.config.series[s].type?a:null});a=x.pSize;var v,m=g.fillPath({seriesNumber:s,dataPointIndex:r,color:x.pointFillColor,patternUnits:\"objectBoundingBox\",value:o.globals.series[s][n]});if(\"circle\"===x.shape?v=p.drawCircle(i):\"square\"!==x.shape&&\"rect\"!==x.shape||(v=p.drawRect(0,0,x.width-x.pointStrokeWidth/2,x.height-x.pointStrokeWidth/2,x.pRadius)),o.config.series[c].data[r]&&o.config.series[c].data[r].fillColor&&(m=o.config.series[c].data[r].fillColor),v.attr({x:t-x.width/2-x.pointStrokeWidth/2,y:e-x.height/2-x.pointStrokeWidth/2,cx:t,cy:e,fill:m,\"fill-opacity\":x.pointFillOpacity,stroke:x.pointStrokeColor,r:a,\"stroke-width\":x.pointStrokeWidth,\"stroke-dasharray\":x.pointStrokeDashArray,\"stroke-opacity\":x.pointStrokeOpacity}),o.config.chart.dropShadow.enabled){var y=o.config.chart.dropShadow;u.dropShadow(v,y,s)}if(!this.initialAnim||o.globals.dataChanged||o.globals.resized)o.globals.animationEnded=!0;else{var w=o.config.chart.animations.speed;h.animateMarker(v,0,\"circle\"===x.shape?a:{width:x.width,height:x.height},w,o.globals.easing,(function(){window.setTimeout((function(){h.animationCompleted(v)}),100)}))}if(o.globals.dataChanged&&\"circle\"===x.shape)if(this.dynamicAnim){var k,A,S,C,P=o.config.chart.animations.dynamicAnimation.speed;null!=(C=o.globals.previousPaths[s]&&o.globals.previousPaths[s][n])&&(k=C.x,A=C.y,S=void 0!==C.r?C.r:a);for(var L=0;L<o.globals.collapsedSeries.length;L++)o.globals.collapsedSeries[L].index===s&&(P=1,a=0);0===t&&0===e&&(a=0),h.animateCircle(v,{cx:k,cy:A,r:S},{cx:t,cy:e,r:a},P,o.globals.easing)}else v.attr({r:a});return v.attr({rel:r,j:r,index:s,\"default-marker-size\":a}),u.setSelectionFilter(v,s,r),f.addEvents(v),v.node.classList.add(\"apexcharts-marker\"),v}},{key:\"centerTextInBubble\",value:function(t){var e=this.w;return{y:t+=parseInt(e.config.dataLabels.style.fontSize,10)/4}}}])&&ot(e.prototype,i),t}();function ct(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const ht=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"dataLabelsCorrection\",value:function(t,e,i,a,s,r,n){var o=this.w,l=!1,c=new b(this.ctx).getTextRects(i,n),h=c.width,d=c.height;e<0&&(e=0),e>o.globals.gridHeight+d&&(e=o.globals.gridHeight+d/2),void 0===o.globals.dataLabelsRects[a]&&(o.globals.dataLabelsRects[a]=[]),o.globals.dataLabelsRects[a].push({x:t,y:e,width:h,height:d});var u=o.globals.dataLabelsRects[a].length-2,g=void 0!==o.globals.lastDrawnDataLabelsIndexes[a]?o.globals.lastDrawnDataLabelsIndexes[a][o.globals.lastDrawnDataLabelsIndexes[a].length-1]:0;if(void 0!==o.globals.dataLabelsRects[a][u]){var f=o.globals.dataLabelsRects[a][g];(t>f.x+f.width+2||e>f.y+f.height+2||t+h<f.x)&&(l=!0)}return(0===s||r)&&(l=!0),{x:t,y:e,textRects:c,drawnextLabel:l}}},{key:\"drawDataLabel\",value:function(t){var e=this,i=t.type,a=t.pos,s=t.i,r=t.j,n=t.isRangeStart,o=t.strokeWidth,l=void 0===o?2:o,c=this.w,h=new b(this.ctx),d=c.config.dataLabels,u=0,g=0,f=r,p=null;if(!d.enabled||!Array.isArray(a.x))return p;p=h.group({class:\"apexcharts-data-labels\"});for(var x=0;x<a.x.length;x++)if(u=a.x[x]+d.offsetX,g=a.y[x]+d.offsetY+l,!isNaN(u)){1===r&&0===x&&(f=0),1===r&&1===x&&(f=1);var v=c.globals.series[s][f];\"rangeArea\"===i&&(v=n?c.globals.seriesRangeStart[s][f]:c.globals.seriesRangeEnd[s][f]);var m=\"\",y=function(t){return c.config.dataLabels.formatter(t,{ctx:e.ctx,seriesIndex:s,dataPointIndex:f,w:c})};\"bubble\"===c.config.chart.type?(m=y(v=c.globals.seriesZ[s][f]),g=a.y[x],g=new lt(this.ctx).centerTextInBubble(g,s,f).y):void 0!==v&&(m=y(v)),this.plotDataLabelsText({x:u,y:g,text:m,i:s,j:f,parent:p,offsetCorrection:!0,dataLabelsConfig:c.config.dataLabels})}return p}},{key:\"plotDataLabelsText\",value:function(t){var e=this.w,i=new b(this.ctx),a=t.x,s=t.y,r=t.i,n=t.j,o=t.text,l=t.textAnchor,c=t.fontSize,h=t.parent,u=t.dataLabelsConfig,g=t.color,f=t.alwaysDrawDataLabel,p=t.offsetCorrection;if(!(Array.isArray(e.config.dataLabels.enabledOnSeries)&&e.config.dataLabels.enabledOnSeries.indexOf(r)<0)){var x={x:a,y:s,drawnextLabel:!0,textRects:null};p&&(x=this.dataLabelsCorrection(a,s,o,r,n,f,parseInt(u.style.fontSize,10))),e.globals.zoomed||(a=x.x,s=x.y),x.textRects&&(a<-10-x.textRects.width||a>e.globals.gridWidth+x.textRects.width+10)&&(o=\"\");var v=e.globals.dataLabels.style.colors[r];((\"bar\"===e.config.chart.type||\"rangeBar\"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||e.config.dataLabels.distributed)&&(v=e.globals.dataLabels.style.colors[n]),\"function\"==typeof v&&(v=v({series:e.globals.series,seriesIndex:r,dataPointIndex:n,w:e})),g&&(v=g);var m=u.offsetX,y=u.offsetY;if(\"bar\"!==e.config.chart.type&&\"rangeBar\"!==e.config.chart.type||(m=0,y=0),x.drawnextLabel){var w=i.drawText({width:100,height:parseInt(u.style.fontSize,10),x:a+m,y:s+y,foreColor:v,textAnchor:l||u.textAnchor,text:o,fontSize:c||u.style.fontSize,fontFamily:u.style.fontFamily,fontWeight:u.style.fontWeight||\"normal\"});if(w.attr({class:\"apexcharts-datalabel\",cx:a,cy:s}),u.dropShadow.enabled){var k=u.dropShadow;new d(this.ctx).dropShadow(w,k)}h.add(w),void 0===e.globals.lastDrawnDataLabelsIndexes[r]&&(e.globals.lastDrawnDataLabelsIndexes[r]=[]),e.globals.lastDrawnDataLabelsIndexes[r].push(n)}}}},{key:\"addBackgroundToDataLabel\",value:function(t,e){var i=this.w,a=i.config.dataLabels.background,s=a.padding,r=a.padding/2,n=e.width,o=e.height,l=new b(this.ctx).drawRect(e.x-s,e.y-r/2,n+2*s,o+r,a.borderRadius,\"transparent\"===i.config.chart.background?\"#fff\":i.config.chart.background,a.opacity,a.borderWidth,a.borderColor);return a.dropShadow.enabled&&new d(this.ctx).dropShadow(l,a.dropShadow),l}},{key:\"dataLabelsBackground\",value:function(){var t=this.w;if(\"bubble\"!==t.config.chart.type)for(var e=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-datalabels text\"),i=0;i<e.length;i++){var a=e[i],s=a.getBBox(),r=null;if(s.width&&s.height&&(r=this.addBackgroundToDataLabel(a,s)),r){a.parentNode.insertBefore(r.node,a);var n=a.getAttribute(\"fill\");!t.config.chart.animations.enabled||t.globals.resized||t.globals.dataChanged?r.attr({fill:n}):r.animate().attr({fill:n}),a.setAttribute(\"fill\",t.config.dataLabels.background.foreColor)}}}},{key:\"bringForward\",value:function(){for(var t=this.w,e=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-datalabels\"),i=t.globals.dom.baseEl.querySelector(\".apexcharts-plot-series:last-child\"),a=0;a<e.length;a++)i&&i.insertBefore(e[a],i.nextSibling)}}])&&ct(e.prototype,i),t}();function dt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var ut=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.legendInactiveClass=\"legend-mouseover-inactive\"}var e,i;return e=t,i=[{key:\"getAllSeriesEls\",value:function(){return this.w.globals.dom.baseEl.getElementsByClassName(\"apexcharts-series\")}},{key:\"getSeriesByName\",value:function(t){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-inner .apexcharts-series[seriesName='\".concat(n.escapeString(t),\"']\"))}},{key:\"isSeriesHidden\",value:function(t){var e=this.getSeriesByName(t),i=parseInt(e.getAttribute(\"data:realIndex\"),10);return{isHidden:e.classList.contains(\"apexcharts-series-collapsed\"),realIndex:i}}},{key:\"addCollapsedClassToSeries\",value:function(t,e){var i=this.w;function a(i){for(var a=0;a<i.length;a++)i[a].index===e&&t.node.classList.add(\"apexcharts-series-collapsed\")}a(i.globals.collapsedSeries),a(i.globals.ancillaryCollapsedSeries)}},{key:\"toggleSeries\",value:function(t){var e=this.isSeriesHidden(t);return this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,e.isHidden),e.isHidden}},{key:\"showSeries\",value:function(t){var e=this.isSeriesHidden(t);e.isHidden&&this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!0)}},{key:\"hideSeries\",value:function(t){var e=this.isSeriesHidden(t);e.isHidden||this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!1)}},{key:\"resetSeries\",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w,s=n.clone(a.globals.initialSeries);a.globals.previousPaths=[],i?(a.globals.collapsedSeries=[],a.globals.ancillaryCollapsedSeries=[],a.globals.collapsedSeriesIndices=[],a.globals.ancillaryCollapsedSeriesIndices=[]):s=this.emptyCollapsedSeries(s),a.config.series=s,t&&(e&&(a.globals.zoomed=!1,this.ctx.updateHelpers.revertDefaultAxisMinMax()),this.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled))}},{key:\"emptyCollapsedSeries\",value:function(t){for(var e=this.w,i=0;i<t.length;i++)e.globals.collapsedSeriesIndices.indexOf(i)>-1&&(t[i].data=[]);return t}},{key:\"toggleSeriesOnHover\",value:function(t,e){var i=this.w;e||(e=t.target);var a=i.globals.dom.baseEl.querySelectorAll(\".apexcharts-series, .apexcharts-datalabels\");if(\"mousemove\"===t.type){var s=parseInt(e.getAttribute(\"rel\"),10)-1,r=null,n=null;i.globals.axisCharts||\"radialBar\"===i.config.chart.type?i.globals.axisCharts?(r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(s,\"']\")),n=i.globals.dom.baseEl.querySelector(\".apexcharts-datalabels[data\\\\:realIndex='\".concat(s,\"']\"))):r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(s+1,\"']\")):r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(s+1,\"'] path\"));for(var o=0;o<a.length;o++)a[o].classList.add(this.legendInactiveClass);null!==r&&(i.globals.axisCharts||r.parentNode.classList.remove(this.legendInactiveClass),r.classList.remove(this.legendInactiveClass),null!==n&&n.classList.remove(this.legendInactiveClass))}else if(\"mouseout\"===t.type)for(var l=0;l<a.length;l++)a[l].classList.remove(this.legendInactiveClass)}},{key:\"highlightRangeInSeries\",value:function(t,e){var i=this,a=this.w,s=a.globals.dom.baseEl.getElementsByClassName(\"apexcharts-heatmap-rect\"),r=function(t){for(var e=0;e<s.length;e++)s[e].classList[t](i.legendInactiveClass)};if(\"mousemove\"===t.type){var n=parseInt(e.getAttribute(\"rel\"),10)-1;r(\"add\"),function(t){for(var e=0;e<s.length;e++){var a=parseInt(s[e].getAttribute(\"val\"),10);a>=t.from&&a<=t.to&&s[e].classList.remove(i.legendInactiveClass)}}(a.config.plotOptions.heatmap.colorScale.ranges[n])}else\"mouseout\"===t.type&&r(\"remove\")}},{key:\"getActiveConfigSeriesIndex\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"asc\",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],i=this.w,a=0;if(i.config.series.length>1)for(var s=i.config.series.map((function(t,a){return t.data&&t.data.length>0&&-1===i.globals.collapsedSeriesIndices.indexOf(a)&&(!i.globals.comboCharts||0===e.length||e.length&&e.indexOf(i.config.series[a].type)>-1)?a:-1})),r=\"asc\"===t?0:s.length-1;\"asc\"===t?r<s.length:r>=0;\"asc\"===t?r++:r--)if(-1!==s[r]){a=s[r];break}return a}},{key:\"getBarSeriesIndices\",value:function(){return this.w.globals.comboCharts?this.w.config.series.map((function(t,e){return\"bar\"===t.type||\"column\"===t.type?e:-1})).filter((function(t){return-1!==t})):this.w.config.series.map((function(t,e){return e}))}},{key:\"getPreviousPaths\",value:function(){var t=this.w;function e(e,i,a){for(var s=e[i].childNodes,r={type:a,paths:[],realIndex:e[i].getAttribute(\"data:realIndex\")},n=0;n<s.length;n++)if(s[n].hasAttribute(\"pathTo\")){var o=s[n].getAttribute(\"pathTo\");r.paths.push({d:o})}t.globals.previousPaths.push(r)}t.globals.previousPaths=[],[\"line\",\"area\",\"bar\",\"rangebar\",\"rangeArea\",\"candlestick\",\"radar\"].forEach((function(i){for(var a,s=(a=i,t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(a,\"-series .apexcharts-series\"))),r=0;r<s.length;r++)e(s,r,i)})),this.handlePrevBubbleScatterPaths(\"bubble\"),this.handlePrevBubbleScatterPaths(\"scatter\");var i=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t.config.chart.type,\" .apexcharts-series\"));if(i.length>0)for(var a=function(e){for(var i=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t.config.chart.type,\" .apexcharts-series[data\\\\:realIndex='\").concat(e,\"'] rect\")),a=[],s=function(t){var e=function(e){return i[t].getAttribute(e)},s={x:parseFloat(e(\"x\")),y:parseFloat(e(\"y\")),width:parseFloat(e(\"width\")),height:parseFloat(e(\"height\"))};a.push({rect:s,color:i[t].getAttribute(\"color\")})},r=0;r<i.length;r++)s(r);t.globals.previousPaths.push(a)},s=0;s<i.length;s++)a(s);t.globals.axisCharts||(t.globals.previousPaths=t.globals.series)}},{key:\"handlePrevBubbleScatterPaths\",value:function(t){var e=this.w,i=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t,\"-series .apexcharts-series\"));if(i.length>0)for(var a=0;a<i.length;a++){for(var s=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t,\"-series .apexcharts-series[data\\\\:realIndex='\").concat(a,\"'] circle\")),r=[],n=0;n<s.length;n++)r.push({x:s[n].getAttribute(\"cx\"),y:s[n].getAttribute(\"cy\"),r:s[n].getAttribute(\"r\")});e.globals.previousPaths.push(r)}}},{key:\"clearPreviousPaths\",value:function(){var t=this.w;t.globals.previousPaths=[],t.globals.allSeriesCollapsed=!1}},{key:\"handleNoData\",value:function(){var t=this.w,e=t.config.noData,i=new b(this.ctx),a=t.globals.svgWidth/2,s=t.globals.svgHeight/2,r=\"middle\";if(t.globals.noData=!0,t.globals.animationEnded=!0,\"left\"===e.align?(a=10,r=\"start\"):\"right\"===e.align&&(a=t.globals.svgWidth-10,r=\"end\"),\"top\"===e.verticalAlign?s=50:\"bottom\"===e.verticalAlign&&(s=t.globals.svgHeight-50),a+=e.offsetX,s=s+parseInt(e.style.fontSize,10)+2+e.offsetY,void 0!==e.text&&\"\"!==e.text){var n=i.drawText({x:a,y:s,text:e.text,textAnchor:r,fontSize:e.style.fontSize,fontFamily:e.style.fontFamily,foreColor:e.style.color,opacity:1,class:\"apexcharts-text-nodata\"});t.globals.dom.Paper.add(n)}}},{key:\"setNullSeriesToZeroValues\",value:function(t){for(var e=this.w,i=0;i<t.length;i++)if(0===t[i].length)for(var a=0;a<t[e.globals.maxValsInArrayIndex].length;a++)t[i].push(0);return t}},{key:\"hasAllSeriesEqualX\",value:function(){for(var t=!0,e=this.w,i=this.filteredSeriesX(),a=0;a<i.length-1;a++)if(i[a][0]!==i[a+1][0]){t=!1;break}return e.globals.allSeriesHasEqualX=t,t}},{key:\"filteredSeriesX\",value:function(){return this.w.globals.seriesX.map((function(t){return t.length>0?t:[]}))}}],i&&dt(e.prototype,i),t}();function gt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var ft=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.twoDSeries=[],this.threeDSeries=[],this.twoDSeriesX=[],this.seriesGoals=[],this.coreUtils=new y(this.ctx)}var e,i;return e=t,i=[{key:\"isMultiFormat\",value:function(){return this.isFormatXY()||this.isFormat2DArray()}},{key:\"isFormatXY\",value:function(){var t=this.w.config.series.slice(),e=new ut(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&null!==t[this.activeSeriesIndex].data[0]&&void 0!==t[this.activeSeriesIndex].data[0].x&&null!==t[this.activeSeriesIndex].data[0])return!0}},{key:\"isFormat2DArray\",value:function(){var t=this.w.config.series.slice(),e=new ut(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&void 0!==t[this.activeSeriesIndex].data[0]&&null!==t[this.activeSeriesIndex].data[0]&&t[this.activeSeriesIndex].data[0].constructor===Array)return!0}},{key:\"handleFormat2DArray\",value:function(t,e){for(var i=this.w.config,a=this.w.globals,s=\"boxPlot\"===i.chart.type||\"boxPlot\"===i.series[e].type,r=0;r<t[e].data.length;r++)if(void 0!==t[e].data[r][1]&&(Array.isArray(t[e].data[r][1])&&4===t[e].data[r][1].length&&!s?this.twoDSeries.push(n.parseNumber(t[e].data[r][1][3])):t[e].data[r].length>=5?this.twoDSeries.push(n.parseNumber(t[e].data[r][4])):this.twoDSeries.push(n.parseNumber(t[e].data[r][1])),a.dataFormatXNumeric=!0),\"datetime\"===i.xaxis.type){var o=new Date(t[e].data[r][0]);o=new Date(o).getTime(),this.twoDSeriesX.push(o)}else this.twoDSeriesX.push(t[e].data[r][0]);for(var l=0;l<t[e].data.length;l++)void 0!==t[e].data[l][2]&&(this.threeDSeries.push(t[e].data[l][2]),a.isDataXYZ=!0)}},{key:\"handleFormatXY\",value:function(t,e){var i=this.w.config,a=this.w.globals,s=new R(this.ctx),r=e;a.collapsedSeriesIndices.indexOf(e)>-1&&(r=this.activeSeriesIndex);for(var o=0;o<t[e].data.length;o++)void 0!==t[e].data[o].y&&(Array.isArray(t[e].data[o].y)?this.twoDSeries.push(n.parseNumber(t[e].data[o].y[t[e].data[o].y.length-1])):this.twoDSeries.push(n.parseNumber(t[e].data[o].y))),void 0!==t[e].data[o].goals&&Array.isArray(t[e].data[o].goals)?(void 0===this.seriesGoals[e]&&(this.seriesGoals[e]=[]),this.seriesGoals[e].push(t[e].data[o].goals)):(void 0===this.seriesGoals[e]&&(this.seriesGoals[e]=[]),this.seriesGoals[e].push(null));for(var l=0;l<t[r].data.length;l++){var c=\"string\"==typeof t[r].data[l].x,h=Array.isArray(t[r].data[l].x),d=!h&&!!s.isValidDate(t[r].data[l].x.toString());if(c||d)if(c||i.xaxis.convertedCatToNumeric){var u=a.isBarHorizontal&&a.isRangeData;\"datetime\"!==i.xaxis.type||u?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[l].x)):this.twoDSeriesX.push(s.parseDate(t[r].data[l].x))}else\"datetime\"===i.xaxis.type?this.twoDSeriesX.push(s.parseDate(t[r].data[l].x.toString())):(a.dataFormatXNumeric=!0,a.isXNumeric=!0,this.twoDSeriesX.push(parseFloat(t[r].data[l].x)));else h?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[l].x)):(a.isXNumeric=!0,a.dataFormatXNumeric=!0,this.twoDSeriesX.push(t[r].data[l].x))}if(t[e].data[0]&&void 0!==t[e].data[0].z){for(var g=0;g<t[e].data.length;g++)this.threeDSeries.push(t[e].data[g].z);a.isDataXYZ=!0}}},{key:\"handleRangeData\",value:function(t,e){var i=this.w.globals,a={};return this.isFormat2DArray()?a=this.handleRangeDataFormat(\"array\",t,e):this.isFormatXY()&&(a=this.handleRangeDataFormat(\"xy\",t,e)),i.seriesRangeStart.push(a.start),i.seriesRangeEnd.push(a.end),i.seriesRange.push(a.rangeUniques),i.seriesRange.forEach((function(t,e){t&&t.forEach((function(t,e){t.y.forEach((function(e,i){for(var a=0;a<t.y.length;a++)if(i!==a){var s=e.y1,r=e.y2,n=t.y[a].y1;s<=t.y[a].y2&&n<=r&&(t.overlaps.indexOf(e.rangeName)<0&&t.overlaps.push(e.rangeName),t.overlaps.indexOf(t.y[a].rangeName)<0&&t.overlaps.push(t.y[a].rangeName))}}))}))})),a}},{key:\"handleCandleStickBoxData\",value:function(t,e){var i=this.w.globals,a={};return this.isFormat2DArray()?a=this.handleCandleStickBoxDataFormat(\"array\",t,e):this.isFormatXY()&&(a=this.handleCandleStickBoxDataFormat(\"xy\",t,e)),i.seriesCandleO[e]=a.o,i.seriesCandleH[e]=a.h,i.seriesCandleM[e]=a.m,i.seriesCandleL[e]=a.l,i.seriesCandleC[e]=a.c,a}},{key:\"handleRangeDataFormat\",value:function(t,e,i){var a=[],s=[],r=e[i].data.filter((function(t,e,i){return e===i.findIndex((function(e){return e.x===t.x}))})).map((function(t,e){return{x:t.x,overlaps:[],y:[]}}));if(\"array\"===t)for(var o=0;o<e[i].data.length;o++)Array.isArray(e[i].data[o])?(a.push(e[i].data[o][1][0]),s.push(e[i].data[o][1][1])):(a.push(e[i].data[o]),s.push(e[i].data[o]));else if(\"xy\"===t)for(var l=function(t){var o=Array.isArray(e[i].data[t].y),l=n.randomId(),c=e[i].data[t].x,h={y1:o?e[i].data[t].y[0]:e[i].data[t].y,y2:o?e[i].data[t].y[1]:e[i].data[t].y,rangeName:l};e[i].data[t].rangeName=l;var d=r.findIndex((function(t){return t.x===c}));r[d].y.push(h),a.push(h.y1),s.push(h.y2)},c=0;c<e[i].data.length;c++)l(c);return{start:a,end:s,rangeUniques:r}}},{key:\"handleCandleStickBoxDataFormat\",value:function(t,e,i){var a=this.w,s=\"boxPlot\"===a.config.chart.type||\"boxPlot\"===a.config.series[i].type,r=[],n=[],o=[],l=[],c=[];if(\"array\"===t)if(s&&6===e[i].data[0].length||!s&&5===e[i].data[0].length)for(var h=0;h<e[i].data.length;h++)r.push(e[i].data[h][1]),n.push(e[i].data[h][2]),s?(o.push(e[i].data[h][3]),l.push(e[i].data[h][4]),c.push(e[i].data[h][5])):(l.push(e[i].data[h][3]),c.push(e[i].data[h][4]));else for(var d=0;d<e[i].data.length;d++)Array.isArray(e[i].data[d][1])&&(r.push(e[i].data[d][1][0]),n.push(e[i].data[d][1][1]),s?(o.push(e[i].data[d][1][2]),l.push(e[i].data[d][1][3]),c.push(e[i].data[d][1][4])):(l.push(e[i].data[d][1][2]),c.push(e[i].data[d][1][3])));else if(\"xy\"===t)for(var u=0;u<e[i].data.length;u++)Array.isArray(e[i].data[u].y)&&(r.push(e[i].data[u].y[0]),n.push(e[i].data[u].y[1]),s?(o.push(e[i].data[u].y[2]),l.push(e[i].data[u].y[3]),c.push(e[i].data[u].y[4])):(l.push(e[i].data[u].y[2]),c.push(e[i].data[u].y[3])));return{o:r,h:n,m:o,l,c}}},{key:\"parseDataAxisCharts\",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.ctx,a=this.w.config,s=this.w.globals,r=new R(i),o=a.labels.length>0?a.labels.slice():a.xaxis.categories.slice();s.isRangeBar=\"rangeBar\"===a.chart.type&&s.isBarHorizontal,s.hasGroups=\"category\"===a.xaxis.type&&a.xaxis.group.groups.length>0,s.hasGroups&&(s.groups=a.xaxis.group.groups);for(var l=function(){for(var t=0;t<o.length;t++)if(\"string\"==typeof o[t]){if(!r.isValidDate(o[t]))throw new Error(\"You have provided invalid Date format. Please provide a valid JavaScript Date\");e.twoDSeriesX.push(r.parseDate(o[t]))}else e.twoDSeriesX.push(o[t])},c=0;c<t.length;c++){if(this.twoDSeries=[],this.twoDSeriesX=[],this.threeDSeries=[],void 0===t[c].data)return void console.error(\"It is a possibility that you may have not included 'data' property in series.\");if(\"rangeBar\"!==a.chart.type&&\"rangeArea\"!==a.chart.type&&\"rangeBar\"!==t[c].type&&\"rangeArea\"!==t[c].type||(s.isRangeData=!0,s.isComboCharts?\"rangeBar\"!==t[c].type&&\"rangeArea\"!==t[c].type||this.handleRangeData(t,c):\"rangeBar\"!==a.chart.type&&\"rangeArea\"!==a.chart.type||this.handleRangeData(t,c)),this.isMultiFormat())this.isFormat2DArray()?this.handleFormat2DArray(t,c):this.isFormatXY()&&this.handleFormatXY(t,c),\"candlestick\"!==a.chart.type&&\"candlestick\"!==t[c].type&&\"boxPlot\"!==a.chart.type&&\"boxPlot\"!==t[c].type||this.handleCandleStickBoxData(t,c),s.series.push(this.twoDSeries),s.labels.push(this.twoDSeriesX),s.seriesX.push(this.twoDSeriesX),s.seriesGoals=this.seriesGoals,c!==this.activeSeriesIndex||this.fallbackToCategory||(s.isXNumeric=!0);else{\"datetime\"===a.xaxis.type?(s.isXNumeric=!0,l(),s.seriesX.push(this.twoDSeriesX)):\"numeric\"===a.xaxis.type&&(s.isXNumeric=!0,o.length>0&&(this.twoDSeriesX=o,s.seriesX.push(this.twoDSeriesX))),s.labels.push(this.twoDSeriesX);var h=t[c].data.map((function(t){return n.parseNumber(t)}));s.series.push(h)}s.seriesZ.push(this.threeDSeries),void 0!==t[c].name?s.seriesNames.push(t[c].name):s.seriesNames.push(\"series-\"+parseInt(c+1,10)),void 0!==t[c].color?s.seriesColors.push(t[c].color):s.seriesColors.push(void 0)}return this.w}},{key:\"parseDataNonAxisCharts\",value:function(t){var e=this.w.globals,i=this.w.config;e.series=t.slice(),e.seriesNames=i.labels.slice();for(var a=0;a<e.series.length;a++)void 0===e.seriesNames[a]&&e.seriesNames.push(\"series-\"+(a+1));return this.w}},{key:\"handleExternalLabelsData\",value:function(t){var e=this.w.config,i=this.w.globals;e.xaxis.categories.length>0?i.labels=e.xaxis.categories:e.labels.length>0?i.labels=e.labels.slice():this.fallbackToCategory?(i.labels=i.labels[0],i.seriesRange.length&&(i.seriesRange.map((function(t){t.forEach((function(t){i.labels.indexOf(t.x)<0&&t.x&&i.labels.push(t.x)}))})),i.labels=i.labels.filter((function(t,e,i){return i.indexOf(t)===e}))),e.xaxis.convertedCatToNumeric&&(new _(e).convertCatToNumericXaxis(e,this.ctx,i.seriesX[0]),this._generateExternalLabels(t))):this._generateExternalLabels(t)}},{key:\"_generateExternalLabels\",value:function(t){var e=this.w.globals,i=this.w.config,a=[];if(e.axisCharts){if(e.series.length>0)if(this.isFormatXY())for(var s=i.series.map((function(t,e){return t.data.filter((function(t,e,i){return i.findIndex((function(e){return e.x===t.x}))===e}))})),r=s.reduce((function(t,e,i,a){return a[t].length>e.length?t:i}),0),n=0;n<s[r].length;n++)a.push(n+1);else for(var o=0;o<e.series[e.maxValsInArrayIndex].length;o++)a.push(o+1);e.seriesX=[];for(var l=0;l<t.length;l++)e.seriesX.push(a);e.isXNumeric=!0}if(0===a.length){a=e.axisCharts?[]:e.series.map((function(t,e){return e+1}));for(var c=0;c<t.length;c++)e.seriesX.push(a)}e.labels=a,i.xaxis.convertedCatToNumeric&&(e.categoryLabels=a.map((function(t){return i.xaxis.labels.formatter(t)}))),e.noLabelsProvided=!0}},{key:\"parseData\",value:function(t){var e=this.w,i=e.config,a=e.globals;if(this.excludeCollapsedSeriesInYAxis(),this.fallbackToCategory=!1,this.ctx.core.resetGlobals(),this.ctx.core.isMultipleY(),a.axisCharts?(this.parseDataAxisCharts(t),this.coreUtils.getLargestSeries()):this.parseDataNonAxisCharts(t),\"bar\"===i.chart.type&&i.chart.stacked){var s=new ut(this.ctx);a.series=s.setNullSeriesToZeroValues(a.series)}this.coreUtils.getSeriesTotals(),a.axisCharts&&(a.stackedSeriesTotals=this.coreUtils.getStackedSeriesTotals()),this.coreUtils.getPercentSeries(),a.dataFormatXNumeric||a.isXNumeric&&(\"numeric\"!==i.xaxis.type||0!==i.labels.length||0!==i.xaxis.categories.length)||this.handleExternalLabelsData(t);for(var r=this.coreUtils.getCategoryLabels(a.labels),n=0;n<r.length;n++)if(Array.isArray(r[n])){a.isMultiLineX=!0;break}}},{key:\"excludeCollapsedSeriesInYAxis\",value:function(){var t=this,e=this.w;e.globals.ignoreYAxisIndexes=e.globals.collapsedSeries.map((function(i,a){if(t.w.globals.isMultipleYAxis&&!e.config.chart.stacked)return i.index}))}}],i&&gt(e.prototype,i),t}();function pt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var xt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,i=[{key:\"getLabel\",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:\"12px\",n=!(arguments.length>6&&void 0!==arguments[6])||arguments[6],o=this.w,l=void 0===t[a]?\"\":t[a],c=l,h=o.globals.xLabelFormatter,d=o.config.xaxis.labels.formatter,u=!1,g=new H(this.ctx),f=l;n&&(c=g.xLabelFormat(h,l,f,{i:a,dateFormatter:new R(this.ctx).formatDate,w:o}),void 0!==d&&(c=d(l,t[a],{i:a,dateFormatter:new R(this.ctx).formatDate,w:o})));var p=function(t){var i=null;return e.forEach((function(t){\"month\"===t.unit?i=\"year\":\"day\"===t.unit?i=\"month\":\"hour\"===t.unit?i=\"day\":\"minute\"===t.unit&&(i=\"hour\")})),i===t};e.length>0?(u=p(e[a].unit),i=e[a].position,c=e[a].value):\"datetime\"===o.config.xaxis.type&&void 0===d&&(c=\"\"),void 0===c&&(c=\"\"),c=Array.isArray(c)?c:c.toString();var x=new b(this.ctx),v={};v=o.globals.rotateXLabels&&n?x.getTextRects(c,parseInt(r,10),null,\"rotate(\".concat(o.config.xaxis.labels.rotate,\" 0 0)\"),!1):x.getTextRects(c,parseInt(r,10));var m=!o.config.xaxis.labels.showDuplicates&&this.ctx.timeScale;return!Array.isArray(c)&&(0===c.indexOf(\"NaN\")||0===c.toLowerCase().indexOf(\"invalid\")||c.toLowerCase().indexOf(\"infinity\")>=0||s.indexOf(c)>=0&&m)&&(c=\"\"),{x:i,text:c,textRect:v,isBold:u}}},{key:\"checkLabelBasedOnTickamount\",value:function(t,e,i){var a=this.w,s=a.config.xaxis.tickAmount;return\"dataPoints\"===s&&(s=Math.round(a.globals.gridWidth/120)),s>i||t%Math.round(i/(s+1))==0||(e.text=\"\"),e}},{key:\"checkForOverflowingLabels\",value:function(t,e,i,a,s){var r=this.w;if(0===t&&r.globals.skipFirstTimelinelabel&&(e.text=\"\"),t===i-1&&r.globals.skipLastTimelinelabel&&(e.text=\"\"),r.config.xaxis.labels.hideOverlappingLabels&&a.length>0){var n=s[s.length-1];e.x<n.textRect.width/(r.globals.rotateXLabels?Math.abs(r.config.xaxis.labels.rotate)/12:1.01)+n.x&&(e.text=\"\")}return e}},{key:\"checkForReversedLabels\",value:function(t,e){var i=this.w;return i.config.yaxis[t]&&i.config.yaxis[t].reversed&&e.reverse(),e}},{key:\"isYAxisHidden\",value:function(t){var e=this.w,i=new y(this.ctx);return!e.config.yaxis[t].show||!e.config.yaxis[t].showForNullSeries&&i.isSeriesNull(t)&&-1===e.globals.collapsedSeriesIndices.indexOf(t)}},{key:\"getYAxisForeColor\",value:function(t,e){var i=this.w;return Array.isArray(t)&&i.globals.yAxisScale[e]&&this.ctx.theme.pushExtraColors(t,i.globals.yAxisScale[e].result.length,!1),t}},{key:\"drawYAxisTicks\",value:function(t,e,i,a,s,r,n){var o=this.w,l=new b(this.ctx),c=o.globals.translateY;if(a.show&&e>0){!0===o.config.yaxis[s].opposite&&(t+=a.width);for(var h=e;h>=0;h--){var d=c+e/10+o.config.yaxis[s].labels.offsetY-1;o.globals.isBarHorizontal&&(d=r*h),\"heatmap\"===o.config.chart.type&&(d+=r/2);var u=l.drawLine(t+i.offsetX-a.width+a.offsetX,d+a.offsetY,t+i.offsetX+a.offsetX,d+a.offsetY,a.color);n.add(u),c+=r}}}}],i&&pt(e.prototype,i),t}();function bt(t){return function(t){if(Array.isArray(t))return vt(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||function(t,e){if(t){if(\"string\"==typeof t)return vt(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?vt(t,e):void 0}}(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function vt(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function mt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const yt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"scaleSvgNode\",value:function(t,e){var i=parseFloat(t.getAttributeNS(null,\"width\")),a=parseFloat(t.getAttributeNS(null,\"height\"));t.setAttributeNS(null,\"width\",i*e),t.setAttributeNS(null,\"height\",a*e),t.setAttributeNS(null,\"viewBox\",\"0 0 \"+i+\" \"+a)}},{key:\"fixSvgStringForIe11\",value:function(t){if(!n.isIE11())return t.replace(/&nbsp;/g,\"&#160;\");var e=0,i=t.replace(/xmlns=\"http:\\/\\/www.w3.org\\/2000\\/svg\"/g,(function(t){return 2==++e?'xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:svgjs=\"http://svgjs.dev\"':t}));return(i=i.replace(/xmlns:NS\\d+=\"\"/g,\"\")).replace(/NS\\d+:(\\w+:\\w+=\")/g,\"$1\")}},{key:\"getSvgString\",value:function(t){null==t&&(t=1);var e=this.w.globals.dom.Paper.svg();if(1!==t){var i=this.w.globals.dom.Paper.node.cloneNode(!0);this.scaleSvgNode(i,t),e=(new XMLSerializer).serializeToString(i)}return this.fixSvgStringForIe11(e)}},{key:\"cleanup\",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-xcrosshairs\"),i=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-ycrosshairs\"),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-zoom-rect, .apexcharts-selection-rect\");Array.prototype.forEach.call(a,(function(t){t.setAttribute(\"width\",0)})),e&&e[0]&&(e[0].setAttribute(\"x\",-500),e[0].setAttribute(\"x1\",-500),e[0].setAttribute(\"x2\",-500)),i&&i[0]&&(i[0].setAttribute(\"y\",-100),i[0].setAttribute(\"y1\",-100),i[0].setAttribute(\"y2\",-100))}},{key:\"svgUrl\",value:function(){this.cleanup();var t=this.getSvgString(),e=new Blob([t],{type:\"image/svg+xml;charset=utf-8\"});return URL.createObjectURL(e)}},{key:\"dataURI\",value:function(t){var e=this;return new Promise((function(i){var a=e.w,s=t?t.scale||t.width/a.globals.svgWidth:1;e.cleanup();var r=document.createElement(\"canvas\");r.width=a.globals.svgWidth*s,r.height=parseInt(a.globals.dom.elWrap.style.height,10)*s;var o=\"transparent\"===a.config.chart.background?\"#fff\":a.config.chart.background,l=r.getContext(\"2d\");l.fillStyle=o,l.fillRect(0,0,r.width*s,r.height*s);var c=e.getSvgString(s);if(window.canvg&&n.isIE11()){var h=window.canvg.Canvg.fromString(l,c,{ignoreClear:!0,ignoreDimensions:!0});h.start();var d=r.msToBlob();h.stop(),i({blob:d})}else{var u=\"data:image/svg+xml,\"+encodeURIComponent(c),g=new Image;g.crossOrigin=\"anonymous\",g.onload=function(){if(l.drawImage(g,0,0),r.msToBlob){var t=r.msToBlob();i({blob:t})}else{var e=r.toDataURL(\"image/png\");i({imgURI:e})}},g.src=u}}))}},{key:\"exportToSVG\",value:function(){this.triggerDownload(this.svgUrl(),this.w.config.chart.toolbar.export.svg.filename,\".svg\")}},{key:\"exportToPng\",value:function(){var t=this;this.dataURI().then((function(e){var i=e.imgURI,a=e.blob;a?navigator.msSaveOrOpenBlob(a,t.w.globals.chartID+\".png\"):t.triggerDownload(i,t.w.config.chart.toolbar.export.png.filename,\".png\")}))}},{key:\"exportToCSV\",value:function(t){var e=this,i=t.series,a=t.fileName,s=t.columnDelimiter,r=void 0===s?\",\":s,o=t.lineDelimiter,l=void 0===o?\"\\n\":o,c=this.w;i||(i=c.config.series);var h=[],d=[],u=\"\",g=c.globals.series.map((function(t,e){return-1===c.globals.collapsedSeriesIndices.indexOf(e)?t:[]})),f=Math.max.apply(Math,bt(i.map((function(t){return t.data?t.data.length:0})))),p=new ft(this.ctx),x=new xt(this.ctx),b=function(t){var i=\"\";if(c.globals.axisCharts){if(\"category\"===c.config.xaxis.type||c.config.xaxis.convertedCatToNumeric)if(c.globals.isBarHorizontal){var a=c.globals.yLabelFormatters[0],s=new ut(e.ctx).getActiveConfigSeriesIndex();i=a(c.globals.labels[t],{seriesIndex:s,dataPointIndex:t,w:c})}else i=x.getLabel(c.globals.labels,c.globals.timescaleLabels,0,t).text;\"datetime\"===c.config.xaxis.type&&(c.config.xaxis.categories.length?i=c.config.xaxis.categories[t]:c.config.labels.length&&(i=c.config.labels[t]))}else i=c.config.labels[t];return Array.isArray(i)&&(i=i.join(\" \")),n.isNumber(i)?i:i.split(r).join(\"\")};h.push(c.config.chart.toolbar.export.csv.headerCategory),\"boxPlot\"===c.config.chart.type?(h.push(\"minimum\"),h.push(\"q1\"),h.push(\"median\"),h.push(\"q3\"),h.push(\"maximum\")):\"candlestick\"===c.config.chart.type?(h.push(\"open\"),h.push(\"high\"),h.push(\"low\"),h.push(\"close\")):\"rangeBar\"===c.config.chart.type?(h.push(\"minimum\"),h.push(\"maximum\")):i.map((function(t,e){var i=t.name?t.name:\"series-\".concat(e);c.globals.axisCharts&&h.push(i.split(r).join(\"\")?i.split(r).join(\"\"):\"series-\".concat(e))})),c.globals.axisCharts||(h.push(c.config.chart.toolbar.export.csv.headerValue),d.push(h.join(r))),i.map((function(t,e){c.globals.axisCharts?function(t,e){if(h.length&&0===e&&d.push(h.join(r)),t.data){t.data=t.data.length&&t.data||bt(Array(f)).map((function(){return\"\"}));for(var a=0;a<t.data.length;a++){h=[];var s=b(a);if(s||(p.isFormatXY()?s=i[e].data[a].x:p.isFormat2DArray()&&(s=i[e].data[a]?i[e].data[a][0]:\"\")),0===e){h.push((l=s,\"datetime\"===c.config.xaxis.type&&String(l).length>=10?c.config.chart.toolbar.export.csv.dateFormatter(s):n.isNumber(s)?s:s.split(r).join(\"\")));for(var o=0;o<c.globals.series.length;o++)p.isFormatXY()?h.push(i[o].data[a].y):h.push(g[o][a])}(\"candlestick\"===c.config.chart.type||t.type&&\"candlestick\"===t.type)&&(h.pop(),h.push(c.globals.seriesCandleO[e][a]),h.push(c.globals.seriesCandleH[e][a]),h.push(c.globals.seriesCandleL[e][a]),h.push(c.globals.seriesCandleC[e][a])),(\"boxPlot\"===c.config.chart.type||t.type&&\"boxPlot\"===t.type)&&(h.pop(),h.push(c.globals.seriesCandleO[e][a]),h.push(c.globals.seriesCandleH[e][a]),h.push(c.globals.seriesCandleM[e][a]),h.push(c.globals.seriesCandleL[e][a]),h.push(c.globals.seriesCandleC[e][a])),\"rangeBar\"===c.config.chart.type&&(h.pop(),h.push(c.globals.seriesRangeStart[e][a]),h.push(c.globals.seriesRangeEnd[e][a])),h.length&&d.push(h.join(r))}}var l}(t,e):((h=[]).push(c.globals.labels[e].split(r).join(\"\")),h.push(g[e]),d.push(h.join(r)))})),u+=d.join(l),this.triggerDownload(\"data:text/csv; charset=utf-8,\"+encodeURIComponent(\"\\ufeff\"+u),a||c.config.chart.toolbar.export.csv.filename,\".csv\")}},{key:\"triggerDownload\",value:function(t,e,i){var a=document.createElement(\"a\");a.href=t,a.download=(e||this.w.globals.chartID)+i,document.body.appendChild(a),a.click(),document.body.removeChild(a)}}])&&mt(e.prototype,i),t}();function wt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var kt=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.elgrid=i,this.w=e.w;var a=this.w;this.axesUtils=new xt(e),this.xaxisLabels=a.globals.labels.slice(),a.globals.timescaleLabels.length>0&&!a.globals.isBarHorizontal&&(this.xaxisLabels=a.globals.timescaleLabels.slice()),a.config.xaxis.overwriteCategories&&(this.xaxisLabels=a.config.xaxis.overwriteCategories),this.drawnLabels=[],this.drawnLabelsRects=[],\"top\"===a.config.xaxis.position?this.offY=0:this.offY=a.globals.gridHeight+1,this.offY=this.offY+a.config.xaxis.axisBorder.offsetY,this.isCategoryBarHorizontal=\"bar\"===a.config.chart.type&&a.config.plotOptions.bar.horizontal,this.xaxisFontSize=a.config.xaxis.labels.style.fontSize,this.xaxisFontFamily=a.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=a.config.xaxis.labels.style.colors,this.xaxisBorderWidth=a.config.xaxis.axisBorder.width,this.isCategoryBarHorizontal&&(this.xaxisBorderWidth=a.config.yaxis[0].axisBorder.width.toString()),this.xaxisBorderWidth.indexOf(\"%\")>-1?this.xaxisBorderWidth=a.globals.gridWidth*parseInt(this.xaxisBorderWidth,10)/100:this.xaxisBorderWidth=parseInt(this.xaxisBorderWidth,10),this.xaxisBorderHeight=a.config.xaxis.axisBorder.height,this.yaxis=a.config.yaxis[0]}var e,i;return e=t,i=[{key:\"drawXaxis\",value:function(){var t=this.w,e=new b(this.ctx),i=e.group({class:\"apexcharts-xaxis\",transform:\"translate(\".concat(t.config.xaxis.offsetX,\", \").concat(t.config.xaxis.offsetY,\")\")}),a=e.group({class:\"apexcharts-xaxis-texts-g\",transform:\"translate(\".concat(t.globals.translateXAxisX,\", \").concat(t.globals.translateXAxisY,\")\")});i.add(a);for(var s=[],r=0;r<this.xaxisLabels.length;r++)s.push(this.xaxisLabels[r]);if(this.drawXAxisLabelAndGroup(!0,e,a,s,t.globals.isXNumeric,(function(t,e){return e})),t.globals.hasGroups){var n=t.globals.groups;s=[];for(var o=0;o<n.length;o++)s.push(n[o].title);var l={};t.config.xaxis.group.style&&(l.xaxisFontSize=t.config.xaxis.group.style.fontSize,l.xaxisFontFamily=t.config.xaxis.group.style.fontFamily,l.xaxisForeColors=t.config.xaxis.group.style.colors,l.fontWeight=t.config.xaxis.group.style.fontWeight,l.cssClass=t.config.xaxis.group.style.cssClass),this.drawXAxisLabelAndGroup(!1,e,a,s,!1,(function(t,e){return n[t].cols*e}),l)}if(void 0!==t.config.xaxis.title.text){var c=e.group({class:\"apexcharts-xaxis-title\"}),h=e.drawText({x:t.globals.gridWidth/2+t.config.xaxis.title.offsetX,y:this.offY+parseFloat(this.xaxisFontSize)+(\"bottom\"===t.config.xaxis.position?t.globals.xAxisLabelsHeight:-t.globals.xAxisLabelsHeight-10)+t.config.xaxis.title.offsetY,text:t.config.xaxis.title.text,textAnchor:\"middle\",fontSize:t.config.xaxis.title.style.fontSize,fontFamily:t.config.xaxis.title.style.fontFamily,fontWeight:t.config.xaxis.title.style.fontWeight,foreColor:t.config.xaxis.title.style.color,cssClass:\"apexcharts-xaxis-title-text \"+t.config.xaxis.title.style.cssClass});c.add(h),i.add(c)}if(t.config.xaxis.axisBorder.show){var d=t.globals.barPadForNumericAxis,u=e.drawLine(t.globals.padHorizontal+t.config.xaxis.axisBorder.offsetX-d,this.offY,this.xaxisBorderWidth+d,this.offY,t.config.xaxis.axisBorder.color,0,this.xaxisBorderHeight);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(u):i.add(u)}return i}},{key:\"drawXAxisLabelAndGroup\",value:function(t,e,i,a,s,r){var n,o=this,l=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{},c=[],h=[],d=this.w,u=l.xaxisFontSize||this.xaxisFontSize,g=l.xaxisFontFamily||this.xaxisFontFamily,f=l.xaxisForeColors||this.xaxisForeColors,p=l.fontWeight||d.config.xaxis.labels.style.fontWeight,x=l.cssClass||d.config.xaxis.labels.style.cssClass,b=d.globals.padHorizontal,v=a.length,m=\"category\"===d.config.xaxis.type?d.globals.dataPoints:v;if(0===m&&v>m&&(m=v),s){var y=m>1?m-1:m;n=d.globals.gridWidth/y,b=b+r(0,n)/2+d.config.xaxis.labels.offsetX}else n=d.globals.gridWidth/m,b=b+r(0,n)+d.config.xaxis.labels.offsetX;for(var w=function(s){var l=b-r(s,n)/2+d.config.xaxis.labels.offsetX;0===s&&1===v&&n/2===b&&1===m&&(l=d.globals.gridWidth/2);var y=o.axesUtils.getLabel(a,d.globals.timescaleLabels,l,s,c,u,t),w=28;if(d.globals.rotateXLabels&&t&&(w=22),d.config.xaxis.title.text&&\"top\"===d.config.xaxis.position&&(w+=parseFloat(d.config.xaxis.title.style.fontSize)+2),t||(w=w+parseFloat(u)+(d.globals.xAxisLabelsHeight-d.globals.xAxisGroupLabelsHeight)+(d.globals.rotateXLabels?10:0)),y=void 0!==d.config.xaxis.tickAmount&&\"dataPoints\"!==d.config.xaxis.tickAmount&&\"datetime\"!==d.config.xaxis.type?o.axesUtils.checkLabelBasedOnTickamount(s,y,v):o.axesUtils.checkForOverflowingLabels(s,y,v,c,h),d.config.xaxis.labels.show){var k=e.drawText({x:y.x,y:o.offY+d.config.xaxis.labels.offsetY+w-(\"top\"===d.config.xaxis.position?d.globals.xAxisHeight+d.config.xaxis.axisTicks.height-2:0),text:y.text,textAnchor:\"middle\",fontWeight:y.isBold?600:p,fontSize:u,fontFamily:g,foreColor:Array.isArray(f)?t&&d.config.xaxis.convertedCatToNumeric?f[d.globals.minX+s-1]:f[s]:f,isPlainText:!1,cssClass:(t?\"apexcharts-xaxis-label \":\"apexcharts-xaxis-group-label \")+x});if(i.add(k),k.on(\"click\",(function(t){if(\"function\"==typeof d.config.chart.events.xAxisLabelClick){var e=Object.assign({},d,{labelIndex:s});d.config.chart.events.xAxisLabelClick(t,o.ctx,e)}})),t){var A=document.createElementNS(d.globals.SVGNS,\"title\");A.textContent=Array.isArray(y.text)?y.text.join(\" \"):y.text,k.node.appendChild(A),\"\"!==y.text&&(c.push(y.text),h.push(y))}}s<v-1&&(b+=r(s+1,n))},k=0;k<=v-1;k++)w(k)}},{key:\"drawXaxisInversed\",value:function(t){var e,i,a=this,s=this.w,r=new b(this.ctx),n=s.config.yaxis[0].opposite?s.globals.translateYAxisX[t]:0,o=r.group({class:\"apexcharts-yaxis apexcharts-xaxis-inversed\",rel:t}),l=r.group({class:\"apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g\",transform:\"translate(\"+n+\", 0)\"});o.add(l);var c=[];if(s.config.yaxis[t].show)for(var h=0;h<this.xaxisLabels.length;h++)c.push(this.xaxisLabels[h]);e=s.globals.gridHeight/c.length,i=-e/2.2;var d=s.globals.yLabelFormatters[0],u=s.config.yaxis[0].labels;if(u.show)for(var g=function(n){var o=void 0===c[n]?\"\":c[n];o=d(o,{seriesIndex:t,dataPointIndex:n,w:s});var h=a.axesUtils.getYAxisForeColor(u.style.colors,t),g=0;Array.isArray(o)&&(g=o.length/2*parseInt(u.style.fontSize,10));var f=u.offsetX-15,p=\"end\";a.yaxis.opposite&&(p=\"start\"),\"left\"===s.config.yaxis[0].labels.align?(f=u.offsetX,p=\"start\"):\"center\"===s.config.yaxis[0].labels.align?(f=u.offsetX,p=\"middle\"):\"right\"===s.config.yaxis[0].labels.align&&(p=\"end\");var x=r.drawText({x:f,y:i+e+u.offsetY-g,text:o,textAnchor:p,foreColor:Array.isArray(h)?h[n]:h,fontSize:u.style.fontSize,fontFamily:u.style.fontFamily,fontWeight:u.style.fontWeight,isPlainText:!1,cssClass:\"apexcharts-yaxis-label \"+u.style.cssClass,maxWidth:u.maxWidth});l.add(x),x.on(\"click\",(function(t){if(\"function\"==typeof s.config.chart.events.xAxisLabelClick){var e=Object.assign({},s,{labelIndex:n});s.config.chart.events.xAxisLabelClick(t,a.ctx,e)}}));var b=document.createElementNS(s.globals.SVGNS,\"title\");if(b.textContent=Array.isArray(o)?o.join(\" \"):o,x.node.appendChild(b),0!==s.config.yaxis[t].labels.rotate){var v=r.rotateAroundCenter(x.node);x.node.setAttribute(\"transform\",\"rotate(\".concat(s.config.yaxis[t].labels.rotate,\" 0 \").concat(v.y,\")\"))}i+=e},f=0;f<=c.length-1;f++)g(f);if(void 0!==s.config.yaxis[0].title.text){var p=r.group({class:\"apexcharts-yaxis-title apexcharts-xaxis-title-inversed\",transform:\"translate(\"+n+\", 0)\"}),x=r.drawText({x:s.config.yaxis[0].title.offsetX,y:s.globals.gridHeight/2+s.config.yaxis[0].title.offsetY,text:s.config.yaxis[0].title.text,textAnchor:\"middle\",foreColor:s.config.yaxis[0].title.style.color,fontSize:s.config.yaxis[0].title.style.fontSize,fontWeight:s.config.yaxis[0].title.style.fontWeight,fontFamily:s.config.yaxis[0].title.style.fontFamily,cssClass:\"apexcharts-yaxis-title-text \"+s.config.yaxis[0].title.style.cssClass});p.add(x),o.add(p)}var v=0;this.isCategoryBarHorizontal&&s.config.yaxis[0].opposite&&(v=s.globals.gridWidth);var m=s.config.xaxis.axisBorder;if(m.show){var y=r.drawLine(s.globals.padHorizontal+m.offsetX+v,1+m.offsetY,s.globals.padHorizontal+m.offsetX+v,s.globals.gridHeight+m.offsetY,m.color,0);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(y):o.add(y)}return s.config.yaxis[0].axisTicks.show&&this.axesUtils.drawYAxisTicks(v,c.length,s.config.yaxis[0].axisBorder,s.config.yaxis[0].axisTicks,0,e,o),o}},{key:\"drawXaxisTicks\",value:function(t,e,i){var a=this.w,s=t;if(!(t<0||t-2>a.globals.gridWidth)){var r=this.offY+a.config.xaxis.axisTicks.offsetY;if(e=e+r+a.config.xaxis.axisTicks.height,\"top\"===a.config.xaxis.position&&(e=r-a.config.xaxis.axisTicks.height),a.config.xaxis.axisTicks.show){var n=new b(this.ctx).drawLine(t+a.config.xaxis.axisTicks.offsetX,r+a.config.xaxis.offsetY,s+a.config.xaxis.axisTicks.offsetX,e+a.config.xaxis.offsetY,a.config.xaxis.axisTicks.color);i.add(n),n.node.classList.add(\"apexcharts-xaxis-tick\")}}}},{key:\"getXAxisTicksPositions\",value:function(){var t=this.w,e=[],i=this.xaxisLabels.length,a=t.globals.padHorizontal;if(t.globals.timescaleLabels.length>0)for(var s=0;s<i;s++)a=this.xaxisLabels[s].position,e.push(a);else for(var r=i,n=0;n<r;n++){var o=r;t.globals.isXNumeric&&\"bar\"!==t.config.chart.type&&(o-=1),a+=t.globals.gridWidth/o,e.push(a)}return e}},{key:\"xAxisLabelCorrections\",value:function(){var t=this.w,e=new b(this.ctx),i=t.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-texts-g\"),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-xaxis-texts-g text:not(.apexcharts-xaxis-group-label)\"),s=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis-inversed text\"),r=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-xaxis-inversed-texts-g text tspan\");if(t.globals.rotateXLabels||t.config.xaxis.labels.rotateAlways)for(var n=0;n<a.length;n++){var o=e.rotateAroundCenter(a[n]);o.y=o.y-1,o.x=o.x+1,a[n].setAttribute(\"transform\",\"rotate(\".concat(t.config.xaxis.labels.rotate,\" \").concat(o.x,\" \").concat(o.y,\")\")),a[n].setAttribute(\"text-anchor\",\"end\"),i.setAttribute(\"transform\",\"translate(0, \".concat(-10,\")\"));var l=a[n].childNodes;t.config.xaxis.labels.trim&&Array.prototype.forEach.call(l,(function(i){e.placeTextWithEllipsis(i,i.textContent,t.globals.xAxisLabelsHeight-(\"bottom\"===t.config.legend.position?20:10))}))}else!function(){for(var i=t.globals.gridWidth/(t.globals.labels.length+1),s=0;s<a.length;s++){var r=a[s].childNodes;t.config.xaxis.labels.trim&&\"datetime\"!==t.config.xaxis.type&&Array.prototype.forEach.call(r,(function(t){e.placeTextWithEllipsis(t,t.textContent,i)}))}}();if(s.length>0){var c=s[s.length-1].getBBox(),h=s[0].getBBox();c.x<-20&&s[s.length-1].parentNode.removeChild(s[s.length-1]),h.x+h.width>t.globals.gridWidth&&!t.globals.isBarHorizontal&&s[0].parentNode.removeChild(s[0]);for(var d=0;d<r.length;d++)e.placeTextWithEllipsis(r[d],r[d].textContent,t.config.yaxis[0].labels.maxWidth-(t.config.yaxis[0].title.text?2*parseFloat(t.config.yaxis[0].title.style.fontSize):0)-15)}}}],i&&wt(e.prototype,i),t}();function At(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var St=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.xaxisLabels=i.globals.labels.slice(),this.axesUtils=new xt(e),this.isRangeBar=i.globals.seriesRange.length,i.globals.timescaleLabels.length>0&&(this.xaxisLabels=i.globals.timescaleLabels.slice())}var e,i;return e=t,i=[{key:\"drawGridArea\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=new b(this.ctx);null===t&&(t=i.group({class:\"apexcharts-grid\"}));var a=i.drawLine(e.globals.padHorizontal,1,e.globals.padHorizontal,e.globals.gridHeight,\"transparent\"),s=i.drawLine(e.globals.padHorizontal,e.globals.gridHeight,e.globals.gridWidth,e.globals.gridHeight,\"transparent\");return t.add(s),t.add(a),t}},{key:\"drawGrid\",value:function(){var t=null;return this.w.globals.axisCharts&&(t=this.renderGrid(),this.drawGridArea(t.el)),t}},{key:\"createGridMask\",value:function(){var t=this.w,e=t.globals,i=new b(this.ctx),a=Array.isArray(t.config.stroke.width)?0:t.config.stroke.width;if(Array.isArray(t.config.stroke.width)){var s=0;t.config.stroke.width.forEach((function(t){s=Math.max(s,t)})),a=s}e.dom.elGridRectMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elGridRectMask.setAttribute(\"id\",\"gridRectMask\".concat(e.cuid)),e.dom.elGridRectMarkerMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elGridRectMarkerMask.setAttribute(\"id\",\"gridRectMarkerMask\".concat(e.cuid)),e.dom.elForecastMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elForecastMask.setAttribute(\"id\",\"forecastMask\".concat(e.cuid)),e.dom.elNonForecastMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elNonForecastMask.setAttribute(\"id\",\"nonForecastMask\".concat(e.cuid));var r=t.config.chart.type,n=0,o=0;(\"bar\"===r||\"rangeBar\"===r||\"candlestick\"===r||\"boxPlot\"===r||t.globals.comboBarCount>0)&&t.globals.isXNumeric&&!t.globals.isBarHorizontal&&(n=t.config.grid.padding.left,o=t.config.grid.padding.right,e.barPadForNumericAxis>n&&(n=e.barPadForNumericAxis,o=e.barPadForNumericAxis)),e.dom.elGridRect=i.drawRect(-a/2-n-2,-a/2,e.gridWidth+a+o+n+4,e.gridHeight+a,0,\"#fff\");var l=t.globals.markers.largestSize+1;e.dom.elGridRectMarker=i.drawRect(2*-l,2*-l,e.gridWidth+4*l,e.gridHeight+4*l,0,\"#fff\"),e.dom.elGridRectMask.appendChild(e.dom.elGridRect.node),e.dom.elGridRectMarkerMask.appendChild(e.dom.elGridRectMarker.node);var c=e.dom.baseEl.querySelector(\"defs\");c.appendChild(e.dom.elGridRectMask),c.appendChild(e.dom.elForecastMask),c.appendChild(e.dom.elNonForecastMask),c.appendChild(e.dom.elGridRectMarkerMask)}},{key:\"_drawGridLines\",value:function(t){var e=t.i,i=t.x1,a=t.y1,s=t.x2,r=t.y2,n=t.xCount,o=t.parent,l=this.w;if(!(0===e&&l.globals.skipFirstTimelinelabel||e===n-1&&l.globals.skipLastTimelinelabel&&!l.config.xaxis.labels.formatter||\"radar\"===l.config.chart.type)){l.config.grid.xaxis.lines.show&&this._drawGridLine({i:e,x1:i,y1:a,x2:s,y2:r,xCount:n,parent:o});var c=0;if(l.globals.hasGroups&&\"between\"===l.config.xaxis.tickPlacement){var h=l.globals.groups;if(h){for(var d=0,u=0;d<e&&u<h.length;u++)d+=h[u].cols;d===e&&(c=.6*l.globals.xAxisLabelsHeight)}}new kt(this.ctx).drawXaxisTicks(i,c,l.globals.dom.elGraphical)}}},{key:\"_drawGridLine\",value:function(t){var e=t.i,i=t.x1,a=t.y1,s=t.x2,r=t.y2,n=t.xCount,o=t.parent,l=this.w,c=!1,h=o.node.classList.contains(\"apexcharts-gridlines-horizontal\"),d=l.config.grid.strokeDashArray,u=l.globals.barPadForNumericAxis;(0===a&&0===r||0===i&&0===s)&&(c=!0),a===l.globals.gridHeight&&r===l.globals.gridHeight&&(c=!0),!l.globals.isBarHorizontal||0!==e&&e!==n-1||(c=!0);var g=new b(this).drawLine(i-(h?u:0),a,s+(h?u:0),r,l.config.grid.borderColor,d);g.node.classList.add(\"apexcharts-gridline\"),c?this.elGridBorders.add(g):o.add(g)}},{key:\"_drawGridBandRect\",value:function(t){var e=t.c,i=t.x1,a=t.y1,s=t.x2,r=t.y2,n=t.type,o=this.w,l=new b(this.ctx),c=o.globals.barPadForNumericAxis;if(\"column\"!==n||\"datetime\"!==o.config.xaxis.type){var h=o.config.grid[n].colors[e],d=l.drawRect(i-(\"row\"===n?c:0),a,s+(\"row\"===n?2*c:0),r,0,h,o.config.grid[n].opacity);this.elg.add(d),d.attr(\"clip-path\",\"url(#gridRectMask\".concat(o.globals.cuid,\")\")),d.node.classList.add(\"apexcharts-grid-\".concat(n))}}},{key:\"_drawXYLines\",value:function(t){var e=this,i=t.xCount,a=t.tickAmount,s=this.w;if(s.config.grid.xaxis.lines.show||s.config.xaxis.axisTicks.show){var r,n=s.globals.padHorizontal,o=s.globals.gridHeight;s.globals.timescaleLabels.length?function(t){for(var a=t.xC,s=t.x1,r=t.y1,n=t.x2,o=t.y2,l=0;l<a;l++)s=e.xaxisLabels[l].position,n=e.xaxisLabels[l].position,e._drawGridLines({i:l,x1:s,y1:r,x2:n,y2:o,xCount:i,parent:e.elgridLinesV})}({xC:i,x1:n,y1:0,x2:r,y2:o}):(s.globals.isXNumeric&&(i=s.globals.xAxisScale.result.length),function(t){for(var a=t.xC,r=t.x1,n=t.y1,o=t.x2,l=t.y2,c=0;c<a+(s.globals.isXNumeric?0:1);c++)0===c&&1===a&&1===s.globals.dataPoints&&(o=r=s.globals.gridWidth/2),e._drawGridLines({i:c,x1:r,y1:n,x2:o,y2:l,xCount:i,parent:e.elgridLinesV}),o=r+=s.globals.gridWidth/(s.globals.isXNumeric?a-1:a)}({xC:i,x1:n,y1:0,x2:r,y2:o}))}if(s.config.grid.yaxis.lines.show){var l=0,c=0,h=s.globals.gridWidth,d=a+1;this.isRangeBar&&(d=s.globals.labels.length);for(var u=0;u<d+(this.isRangeBar?1:0);u++)this._drawGridLine({i:u,xCount:d+(this.isRangeBar?1:0),x1:0,y1:l,x2:h,y2:c,parent:this.elgridLinesH}),c=l+=s.globals.gridHeight/(this.isRangeBar?d:a)}}},{key:\"_drawInvertedXYLines\",value:function(t){var e=t.xCount,i=this.w;if(i.config.grid.xaxis.lines.show||i.config.xaxis.axisTicks.show)for(var a,s=i.globals.padHorizontal,r=i.globals.gridHeight,n=0;n<e+1;n++)i.config.grid.xaxis.lines.show&&this._drawGridLine({i:n,xCount:e+1,x1:s,y1:0,x2:a,y2:r,parent:this.elgridLinesV}),new kt(this.ctx).drawXaxisTicks(s,0,i.globals.dom.elGraphical),a=s=s+i.globals.gridWidth/e+.3;if(i.config.grid.yaxis.lines.show)for(var o=0,l=0,c=i.globals.gridWidth,h=0;h<i.globals.dataPoints+1;h++)this._drawGridLine({i:h,xCount:i.globals.dataPoints+1,x1:0,y1:o,x2:c,y2:l,parent:this.elgridLinesH}),l=o+=i.globals.gridHeight/i.globals.dataPoints}},{key:\"renderGrid\",value:function(){var t=this.w,e=new b(this.ctx);this.elg=e.group({class:\"apexcharts-grid\"}),this.elgridLinesH=e.group({class:\"apexcharts-gridlines-horizontal\"}),this.elgridLinesV=e.group({class:\"apexcharts-gridlines-vertical\"}),this.elGridBorders=e.group({class:\"apexcharts-grid-borders\"}),this.elg.add(this.elgridLinesH),this.elg.add(this.elgridLinesV),t.config.grid.show||(this.elgridLinesV.hide(),this.elgridLinesH.hide(),this.elGridBorders.hide());for(var i,a=t.globals.yAxisScale.length?t.globals.yAxisScale[0].result.length-1:5,s=0;s<t.globals.series.length&&(void 0!==t.globals.yAxisScale[s]&&(a=t.globals.yAxisScale[s].result.length-1),!(a>2));s++);return!t.globals.isBarHorizontal||this.isRangeBar?(i=this.xaxisLabels.length,this.isRangeBar&&(a=t.globals.labels.length,t.config.xaxis.tickAmount&&t.config.xaxis.labels.formatter&&(i=t.config.xaxis.tickAmount)),this._drawXYLines({xCount:i,tickAmount:a})):(i=a,a=t.globals.xTickAmount,this._drawInvertedXYLines({xCount:i,tickAmount:a})),this.drawGridBands(i,a),{el:this.elg,elGridBorders:this.elGridBorders,xAxisTickWidth:t.globals.gridWidth/i}}},{key:\"drawGridBands\",value:function(t,e){var i=this.w;if(void 0!==i.config.grid.row.colors&&i.config.grid.row.colors.length>0)for(var a=0,s=i.globals.gridHeight/e,r=i.globals.gridWidth,n=0,o=0;n<e;n++,o++)o>=i.config.grid.row.colors.length&&(o=0),this._drawGridBandRect({c:o,x1:0,y1:a,x2:r,y2:s,type:\"row\"}),a+=i.globals.gridHeight/e;if(void 0!==i.config.grid.column.colors&&i.config.grid.column.colors.length>0)for(var l=i.globals.isBarHorizontal||\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric?t:t-1,c=i.globals.padHorizontal,h=i.globals.padHorizontal+i.globals.gridWidth/l,d=i.globals.gridHeight,u=0,g=0;u<t;u++,g++)g>=i.config.grid.column.colors.length&&(g=0),this._drawGridBandRect({c:g,x1:c,y1:0,x2:h,y2:d,type:\"column\"}),c+=i.globals.gridWidth/l}}],i&&At(e.prototype,i),t}();const Ct=St;function Pt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Lt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,i=[{key:\"niceScale\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4?arguments[4]:void 0,r=this.w,o=Math.abs(e-t);if(\"dataPoints\"===(i=this._adjustTicksForSmallRange(i,a,o))&&(i=r.globals.dataPoints-1),t===Number.MIN_VALUE&&0===e||!n.isNumber(t)&&!n.isNumber(e)||t===Number.MIN_VALUE&&e===-Number.MAX_VALUE){t=0,e=i;var l=this.linearScale(t,e,i);return l}t>e?(console.warn(\"axis.min cannot be greater than axis.max\"),e=t+.1):t===e&&(t=0===t?0:t-.5,e=0===e?2:e+.5);var c=[];o<1&&s&&(\"candlestick\"===r.config.chart.type||\"candlestick\"===r.config.series[a].type||\"boxPlot\"===r.config.chart.type||\"boxPlot\"===r.config.series[a].type||r.globals.isRangeData)&&(e*=1.01);var h=i+1;h<2?h=2:h>2&&(h-=2);var d=o/h,u=Math.floor(n.log10(d)),g=Math.pow(10,u),f=Math.round(d/g);f<1&&(f=1);var p=f*g,x=p*Math.floor(t/p),b=p*Math.ceil(e/p),v=x;if(s&&o>2){for(;c.push(v),!((v+=p)>b););return{result:c,niceMin:c[0],niceMax:c[c.length-1]}}var m=t;(c=[]).push(m);for(var y=Math.abs(e-t)/i,w=0;w<=i;w++)m+=y,c.push(m);return c[c.length-2]>=e&&c.pop(),{result:c,niceMin:c[0],niceMax:c[c.length-1]}}},{key:\"linearScale\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=arguments.length>3?arguments[3]:void 0,s=Math.abs(e-t);\"dataPoints\"===(i=this._adjustTicksForSmallRange(i,a,s))&&(i=this.w.globals.dataPoints-1);var r=s/i;i===Number.MAX_VALUE&&(i=10,r=1);for(var n=[],o=t;i>=0;)n.push(o),o+=r,i-=1;return{result:n,niceMin:n[0],niceMax:n[n.length-1]}}},{key:\"logarithmicScaleNice\",value:function(t,e,i){e<=0&&(e=Math.max(t,i)),t<=0&&(t=Math.min(e,i));for(var a=[],s=Math.ceil(Math.log(e)/Math.log(i)+1),r=Math.floor(Math.log(t)/Math.log(i));r<s;r++)a.push(Math.pow(i,r));return{result:a,niceMin:a[0],niceMax:a[a.length-1]}}},{key:\"logarithmicScale\",value:function(t,e,i){e<=0&&(e=Math.max(t,i)),t<=0&&(t=Math.min(e,i));for(var a=[],s=Math.log(e)/Math.log(i),r=Math.log(t)/Math.log(i),n=s-r,o=Math.round(n),l=n/o,c=0,h=r;c<o;c++,h+=l)a.push(Math.pow(i,h));return a.push(Math.pow(i,s)),{result:a,niceMin:t,niceMax:e}}},{key:\"_adjustTicksForSmallRange\",value:function(t,e,i){var a=t;if(void 0!==e&&this.w.config.yaxis[e].labels.formatter&&void 0===this.w.config.yaxis[e].tickAmount){var s=Number(this.w.config.yaxis[e].labels.formatter(1));n.isNumber(s)&&0===this.w.globals.yValueDecimal&&(a=Math.ceil(i))}return a<t?a:t}},{key:\"setYScaleForIndex\",value:function(t,e,i){var a=this.w.globals,s=this.w.config,r=a.isBarHorizontal?s.xaxis:s.yaxis[t];void 0===a.yAxisScale[t]&&(a.yAxisScale[t]=[]);var o=Math.abs(i-e);if(r.logarithmic&&o<=5&&(a.invalidLogScale=!0),r.logarithmic&&o>5)a.allSeriesCollapsed=!1,a.yAxisScale[t]=this.logarithmicScale(e,i,r.logBase),a.yAxisScale[t]=r.forceNiceScale?this.logarithmicScaleNice(e,i,r.logBase):this.logarithmicScale(e,i,r.logBase);else if(i!==-Number.MAX_VALUE&&n.isNumber(i))if(a.allSeriesCollapsed=!1,void 0===r.min&&void 0===r.max||r.forceNiceScale){var l=void 0===s.yaxis[t].max&&void 0===s.yaxis[t].min||s.yaxis[t].forceNiceScale;a.yAxisScale[t]=this.niceScale(e,i,r.tickAmount?r.tickAmount:o<5&&o>1?o+1:5,t,l)}else a.yAxisScale[t]=this.linearScale(e,i,r.tickAmount,t);else a.yAxisScale[t]=this.linearScale(0,5,5)}},{key:\"setXScale\",value:function(t,e){var i=this.w,a=i.globals,s=i.config.xaxis,r=Math.abs(e-t);return e!==-Number.MAX_VALUE&&n.isNumber(e)?a.xAxisScale=this.linearScale(t,e,s.tickAmount?s.tickAmount:r<5&&r>1?r+1:5,0):a.xAxisScale=this.linearScale(0,5,5),a.xAxisScale}},{key:\"setMultipleYScales\",value:function(){var t=this,e=this.w.globals,i=this.w.config,a=e.minYArr.concat([]),s=e.maxYArr.concat([]),r=[];i.yaxis.forEach((function(e,n){var o=n;i.series.forEach((function(t,i){t.name===e.seriesName&&(o=i,n!==i?r.push({index:i,similarIndex:n,alreadyExists:!0}):r.push({index:i}))}));var l=a[o],c=s[o];t.setYScaleForIndex(n,l,c)})),this.sameScaleInMultipleAxes(a,s,r)}},{key:\"sameScaleInMultipleAxes\",value:function(t,e,i){var a=this,s=this.w.config,r=this.w.globals,n=[];i.forEach((function(t){t.alreadyExists&&(void 0===n[t.index]&&(n[t.index]=[]),n[t.index].push(t.index),n[t.index].push(t.similarIndex))})),r.yAxisSameScaleIndices=n,n.forEach((function(t,e){n.forEach((function(i,a){var s,r;e!==a&&(s=t,r=i,s.filter((function(t){return-1!==r.indexOf(t)}))).length>0&&(n[e]=n[e].concat(n[a]))}))}));var o=n.map((function(t){return t.filter((function(e,i){return t.indexOf(e)===i}))})).map((function(t){return t.sort()}));n=n.filter((function(t){return!!t}));var l=o.slice(),c=l.map((function(t){return JSON.stringify(t)}));l=l.filter((function(t,e){return c.indexOf(JSON.stringify(t))===e}));var h=[],d=[];t.forEach((function(t,i){l.forEach((function(a,s){a.indexOf(i)>-1&&(void 0===h[s]&&(h[s]=[],d[s]=[]),h[s].push({key:i,value:t}),d[s].push({key:i,value:e[i]}))}))}));var u=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,Number.MIN_VALUE),g=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,-Number.MAX_VALUE);h.forEach((function(t,e){t.forEach((function(t,i){u[e]=Math.min(t.value,u[e])}))})),d.forEach((function(t,e){t.forEach((function(t,i){g[e]=Math.max(t.value,g[e])}))})),t.forEach((function(t,e){d.forEach((function(t,i){var n=u[i],o=g[i];s.chart.stacked&&(o=0,t.forEach((function(t,e){t.value!==-Number.MAX_VALUE&&(o+=t.value),n!==Number.MIN_VALUE&&(n+=h[i][e].value)}))),t.forEach((function(i,l){t[l].key===e&&(void 0!==s.yaxis[e].min&&(n=\"function\"==typeof s.yaxis[e].min?s.yaxis[e].min(r.minY):s.yaxis[e].min),void 0!==s.yaxis[e].max&&(o=\"function\"==typeof s.yaxis[e].max?s.yaxis[e].max(r.maxY):s.yaxis[e].max),a.setYScaleForIndex(e,n,o))}))}))}))}},{key:\"autoScaleY\",value:function(t,e,i){t||(t=this);var a=t.w;if(a.globals.isMultipleYAxis||a.globals.collapsedSeries.length)return console.warn(\"autoScaleYaxis is not supported in a multi-yaxis chart.\"),e;var s=a.globals.seriesX[0],r=a.config.chart.stacked;return e.forEach((function(t,n){for(var o=0,l=0;l<s.length;l++)if(s[l]>=i.xaxis.min){o=l;break}var c,h,d=a.globals.minYArr[n],u=a.globals.maxYArr[n],g=a.globals.stackedSeriesTotals;a.globals.series.forEach((function(n,l){var f=n[o];r?(f=g[o],c=h=f,g.forEach((function(t,e){s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min&&(t>h&&null!==t&&(h=t),n[e]<c&&null!==n[e]&&(c=n[e]))}))):(c=h=f,n.forEach((function(t,e){if(s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min){var r=t,n=t;a.globals.series.forEach((function(i,a){null!==t&&(r=Math.min(i[e],r),n=Math.max(i[e],n))})),n>h&&null!==n&&(h=n),r<c&&null!==r&&(c=r)}}))),void 0===c&&void 0===h&&(c=d,h=u),h*=h<0?.9:1.1,0==(c*=c<0?1.1:.9)&&0===h&&(c=-1,h=1),h<0&&h<u&&(h=u),c<0&&c>d&&(c=d),e.length>1?(e[l].min=void 0===t.min?c:t.min,e[l].max=void 0===t.max?h:t.max):(e[0].min=void 0===t.min?c:t.min,e[0].max=void 0===t.max?h:t.max)}))})),e}}],i&&Pt(e.prototype,i),t}();function Ot(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Tt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.scales=new Lt(e)}var e,i;return e=t,i=[{key:\"init\",value:function(){this.setYRange(),this.setXRange(),this.setZRange()}},{key:\"getMinYMaxY\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-Number.MAX_VALUE,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w.config,r=this.w.globals,o=-Number.MAX_VALUE,l=Number.MIN_VALUE;null===a&&(a=t+1);var c=r.series,h=c,d=c;\"candlestick\"===s.chart.type?(h=r.seriesCandleL,d=r.seriesCandleH):\"boxPlot\"===s.chart.type?(h=r.seriesCandleO,d=r.seriesCandleC):r.isRangeData&&(h=r.seriesRangeStart,d=r.seriesRangeEnd);for(var u=t;u<a;u++){r.dataPoints=Math.max(r.dataPoints,c[u].length),r.categoryLabels.length&&(r.dataPoints=r.categoryLabels.filter((function(t){return void 0!==t})).length);for(var g=0;g<r.series[u].length;g++){var f=c[u][g];null!==f&&n.isNumber(f)?(void 0!==d[u][g]&&(o=Math.max(o,d[u][g]),e=Math.min(e,d[u][g])),void 0!==h[u][g]&&(e=Math.min(e,h[u][g]),i=Math.max(i,h[u][g])),\"candlestick\"!==this.w.config.chart.type&&\"boxPlot\"!==this.w.config.chart.type&&\"rangeArea\"===this.w.config.chart.type&&\"rangeBar\"===this.w.config.chart.type||(\"candlestick\"!==this.w.config.chart.type&&\"boxPlot\"!==this.w.config.chart.type||void 0!==r.seriesCandleC[u][g]&&(o=Math.max(o,r.seriesCandleO[u][g]),o=Math.max(o,r.seriesCandleH[u][g]),o=Math.max(o,r.seriesCandleL[u][g]),o=Math.max(o,r.seriesCandleC[u][g]),\"boxPlot\"===this.w.config.chart.type&&(o=Math.max(o,r.seriesCandleM[u][g]))),!s.series[u].type||\"candlestick\"===s.series[u].type&&\"boxPlot\"===s.series[u].type&&\"rangeArea\"===s.series[u].type&&\"rangeBar\"===s.series[u].type||(o=Math.max(o,r.series[u][g]),e=Math.min(e,r.series[u][g])),i=o),r.seriesGoals[u]&&r.seriesGoals[u][g]&&Array.isArray(r.seriesGoals[u][g])&&r.seriesGoals[u][g].forEach((function(t){l!==Number.MIN_VALUE&&(l=Math.min(l,t.value),e=l),o=Math.max(o,t.value),i=o})),n.isFloat(f)&&(f=n.noExponents(f),r.yValueDecimal=Math.max(r.yValueDecimal,f.toString().split(\".\")[1].length)),l>h[u][g]&&h[u][g]<0&&(l=h[u][g])):r.hasNullValues=!0}}return\"rangeBar\"===s.chart.type&&r.seriesRangeStart.length&&r.isBarHorizontal&&(l=e),\"bar\"===s.chart.type&&(l<0&&o<0&&(o=0),l===Number.MIN_VALUE&&(l=0)),{minY:l,maxY:o,lowestY:e,highestY:i}}},{key:\"setYRange\",value:function(){var t=this.w.globals,e=this.w.config;t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE;var i=Number.MAX_VALUE;if(t.isMultipleYAxis)for(var a=0;a<t.series.length;a++){var s=this.getMinYMaxY(a,i,null,a+1);t.minYArr.push(s.minY),t.maxYArr.push(s.maxY),i=s.lowestY}var r=this.getMinYMaxY(0,i,null,t.series.length);if(t.minY=r.minY,t.maxY=r.maxY,i=r.lowestY,e.chart.stacked&&this._setStackedMinMax(),(\"line\"===e.chart.type||\"area\"===e.chart.type||\"candlestick\"===e.chart.type||\"boxPlot\"===e.chart.type||\"rangeBar\"===e.chart.type&&!t.isBarHorizontal)&&t.minY===Number.MIN_VALUE&&i!==-Number.MAX_VALUE&&i!==t.maxY){var n=t.maxY-i;(i>=0&&i<=10||void 0!==e.yaxis[0].min||void 0!==e.yaxis[0].max)&&(n=0),t.minY=i-5*n/100,i>0&&t.minY<0&&(t.minY=0),t.maxY=t.maxY+5*n/100}return e.yaxis.forEach((function(e,i){void 0!==e.max&&(\"number\"==typeof e.max?t.maxYArr[i]=e.max:\"function\"==typeof e.max&&(t.maxYArr[i]=e.max(t.isMultipleYAxis?t.maxYArr[i]:t.maxY)),t.maxY=t.maxYArr[i]),void 0!==e.min&&(\"number\"==typeof e.min?t.minYArr[i]=e.min:\"function\"==typeof e.min&&(t.minYArr[i]=e.min(t.isMultipleYAxis?t.minYArr[i]===Number.MIN_VALUE?0:t.minYArr[i]:t.minY)),t.minY=t.minYArr[i])})),t.isBarHorizontal&&[\"min\",\"max\"].forEach((function(i){void 0!==e.xaxis[i]&&\"number\"==typeof e.xaxis[i]&&(\"min\"===i?t.minY=e.xaxis[i]:t.maxY=e.xaxis[i])})),t.isMultipleYAxis?(this.scales.setMultipleYScales(),t.minY=i,t.yAxisScale.forEach((function(e,i){t.minYArr[i]=e.niceMin,t.maxYArr[i]=e.niceMax}))):(this.scales.setYScaleForIndex(0,t.minY,t.maxY),t.minY=t.yAxisScale[0].niceMin,t.maxY=t.yAxisScale[0].niceMax,t.minYArr[0]=t.yAxisScale[0].niceMin,t.maxYArr[0]=t.yAxisScale[0].niceMax),{minY:t.minY,maxY:t.maxY,minYArr:t.minYArr,maxYArr:t.maxYArr,yAxisScale:t.yAxisScale}}},{key:\"setXRange\",value:function(){var t=this.w.globals,e=this.w.config,i=\"numeric\"===e.xaxis.type||\"datetime\"===e.xaxis.type||\"category\"===e.xaxis.type&&!t.noLabelsProvided||t.noLabelsProvided||t.isXNumeric;if(t.isXNumeric&&function(){for(var e=0;e<t.series.length;e++)if(t.labels[e])for(var i=0;i<t.labels[e].length;i++)null!==t.labels[e][i]&&n.isNumber(t.labels[e][i])&&(t.maxX=Math.max(t.maxX,t.labels[e][i]),t.initialMaxX=Math.max(t.maxX,t.labels[e][i]),t.minX=Math.min(t.minX,t.labels[e][i]),t.initialMinX=Math.min(t.minX,t.labels[e][i]))}(),t.noLabelsProvided&&0===e.xaxis.categories.length&&(t.maxX=t.labels[t.labels.length-1],t.initialMaxX=t.labels[t.labels.length-1],t.minX=1,t.initialMinX=1),t.isXNumeric||t.noLabelsProvided||t.dataFormatXNumeric){var a;if(void 0===e.xaxis.tickAmount?(a=Math.round(t.svgWidth/150),\"numeric\"===e.xaxis.type&&t.dataPoints<30&&(a=t.dataPoints-1),a>t.dataPoints&&0!==t.dataPoints&&(a=t.dataPoints-1)):\"dataPoints\"===e.xaxis.tickAmount?(t.series.length>1&&(a=t.series[t.maxValsInArrayIndex].length-1),t.isXNumeric&&(a=t.maxX-t.minX-1)):a=e.xaxis.tickAmount,t.xTickAmount=a,void 0!==e.xaxis.max&&\"number\"==typeof e.xaxis.max&&(t.maxX=e.xaxis.max),void 0!==e.xaxis.min&&\"number\"==typeof e.xaxis.min&&(t.minX=e.xaxis.min),void 0!==e.xaxis.range&&(t.minX=t.maxX-e.xaxis.range),t.minX!==Number.MAX_VALUE&&t.maxX!==-Number.MAX_VALUE)if(e.xaxis.convertedCatToNumeric&&!t.dataFormatXNumeric){for(var s=[],r=t.minX-1;r<t.maxX;r++)s.push(r+1);t.xAxisScale={result:s,niceMin:s[0],niceMax:s[s.length-1]}}else t.xAxisScale=this.scales.setXScale(t.minX,t.maxX);else t.xAxisScale=this.scales.linearScale(1,a,a),t.noLabelsProvided&&t.labels.length>0&&(t.xAxisScale=this.scales.linearScale(1,t.labels.length,a-1),t.seriesX=t.labels.slice());i&&(t.labels=t.xAxisScale.result.slice())}return t.isBarHorizontal&&t.labels.length&&(t.xTickAmount=t.labels.length),this._handleSingleDataPoint(),this._getMinXDiff(),{minX:t.minX,maxX:t.maxX}}},{key:\"setZRange\",value:function(){var t=this.w.globals;if(t.isDataXYZ)for(var e=0;e<t.series.length;e++)if(void 0!==t.seriesZ[e])for(var i=0;i<t.seriesZ[e].length;i++)null!==t.seriesZ[e][i]&&n.isNumber(t.seriesZ[e][i])&&(t.maxZ=Math.max(t.maxZ,t.seriesZ[e][i]),t.minZ=Math.min(t.minZ,t.seriesZ[e][i]))}},{key:\"_handleSingleDataPoint\",value:function(){var t=this.w.globals,e=this.w.config;if(t.minX===t.maxX){var i=new R(this.ctx);if(\"datetime\"===e.xaxis.type){var a=i.getDate(t.minX);e.xaxis.labels.datetimeUTC?a.setUTCDate(a.getUTCDate()-2):a.setDate(a.getDate()-2),t.minX=new Date(a).getTime();var s=i.getDate(t.maxX);e.xaxis.labels.datetimeUTC?s.setUTCDate(s.getUTCDate()+2):s.setDate(s.getDate()+2),t.maxX=new Date(s).getTime()}else(\"numeric\"===e.xaxis.type||\"category\"===e.xaxis.type&&!t.noLabelsProvided)&&(t.minX=t.minX-2,t.initialMinX=t.minX,t.maxX=t.maxX+2,t.initialMaxX=t.maxX)}}},{key:\"_getMinXDiff\",value:function(){var t=this.w.globals;t.isXNumeric&&t.seriesX.forEach((function(e,i){1===e.length&&e.push(t.seriesX[t.maxValsInArrayIndex][t.seriesX[t.maxValsInArrayIndex].length-1]);var a=e.slice();a.sort((function(t,e){return t-e})),a.forEach((function(e,i){if(i>0){var s=e-a[i-1];s>0&&(t.minXDiff=Math.min(s,t.minXDiff))}})),1!==t.dataPoints&&t.minXDiff!==Number.MAX_VALUE||(t.minXDiff=.5)}))}},{key:\"_setStackedMinMax\",value:function(){var t=this.w.globals,e=[],i=[];if(t.series.length)for(var a=0;a<t.series[t.maxValsInArrayIndex].length;a++)for(var s=0,r=0,o=0;o<t.series.length;o++)null!==t.series[o][a]&&n.isNumber(t.series[o][a])&&(t.series[o][a]>0?s=s+parseFloat(t.series[o][a])+1e-4:r+=parseFloat(t.series[o][a])),o===t.series.length-1&&(e.push(s),i.push(r));for(var l=0;l<e.length;l++)t.maxY=Math.max(t.maxY,e[l]),t.minY=Math.min(t.minY,i[l])}}],i&&Ot(e.prototype,i),t}();const Et=Tt;function It(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Mt=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.elgrid=i,this.w=e.w;var a=this.w;this.xaxisFontSize=a.config.xaxis.labels.style.fontSize,this.axisFontFamily=a.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=a.config.xaxis.labels.style.colors,this.isCategoryBarHorizontal=\"bar\"===a.config.chart.type&&a.config.plotOptions.bar.horizontal,this.xAxisoffX=0,\"bottom\"===a.config.xaxis.position&&(this.xAxisoffX=a.globals.gridHeight),this.drawnLabels=[],this.axesUtils=new xt(e)}var e,i;return e=t,(i=[{key:\"drawYaxis\",value:function(t){var e=this,i=this.w,a=new b(this.ctx),s=i.config.yaxis[t].labels.style,r=s.fontSize,n=s.fontFamily,o=s.fontWeight,l=a.group({class:\"apexcharts-yaxis\",rel:t,transform:\"translate(\"+i.globals.translateYAxisX[t]+\", 0)\"});if(this.axesUtils.isYAxisHidden(t))return l;var c=a.group({class:\"apexcharts-yaxis-texts-g\"});l.add(c);var h=i.globals.yAxisScale[t].result.length-1,d=i.globals.gridHeight/h,u=i.globals.translateY,g=i.globals.yLabelFormatters[t],f=i.globals.yAxisScale[t].result.slice();f=this.axesUtils.checkForReversedLabels(t,f);var p=\"\";if(i.config.yaxis[t].labels.show)for(var x=function(l){var x=f[l];x=g(x,l,i);var b=i.config.yaxis[t].labels.padding;i.config.yaxis[t].opposite&&0!==i.config.yaxis.length&&(b*=-1);var v=\"end\";i.config.yaxis[t].opposite&&(v=\"start\"),\"left\"===i.config.yaxis[t].labels.align?v=\"start\":\"center\"===i.config.yaxis[t].labels.align?v=\"middle\":\"right\"===i.config.yaxis[t].labels.align&&(v=\"end\");var m=e.axesUtils.getYAxisForeColor(s.colors,t),y=a.drawText({x:b,y:u+h/10+i.config.yaxis[t].labels.offsetY+1,text:x,textAnchor:v,fontSize:r,fontFamily:n,fontWeight:o,maxWidth:i.config.yaxis[t].labels.maxWidth,foreColor:Array.isArray(m)?m[l]:m,isPlainText:!1,cssClass:\"apexcharts-yaxis-label \"+s.cssClass});l===h&&(p=y),c.add(y);var w=document.createElementNS(i.globals.SVGNS,\"title\");if(w.textContent=Array.isArray(x)?x.join(\" \"):x,y.node.appendChild(w),0!==i.config.yaxis[t].labels.rotate){var k=a.rotateAroundCenter(p.node),A=a.rotateAroundCenter(y.node);y.node.setAttribute(\"transform\",\"rotate(\".concat(i.config.yaxis[t].labels.rotate,\" \").concat(k.x,\" \").concat(A.y,\")\"))}u+=d},v=h;v>=0;v--)x(v);if(void 0!==i.config.yaxis[t].title.text){var m=a.group({class:\"apexcharts-yaxis-title\"}),y=0;i.config.yaxis[t].opposite&&(y=i.globals.translateYAxisX[t]);var w=a.drawText({x:y,y:i.globals.gridHeight/2+i.globals.translateY+i.config.yaxis[t].title.offsetY,text:i.config.yaxis[t].title.text,textAnchor:\"end\",foreColor:i.config.yaxis[t].title.style.color,fontSize:i.config.yaxis[t].title.style.fontSize,fontWeight:i.config.yaxis[t].title.style.fontWeight,fontFamily:i.config.yaxis[t].title.style.fontFamily,cssClass:\"apexcharts-yaxis-title-text \"+i.config.yaxis[t].title.style.cssClass});m.add(w),l.add(m)}var k=i.config.yaxis[t].axisBorder,A=31+k.offsetX;if(i.config.yaxis[t].opposite&&(A=-31-k.offsetX),k.show){var S=a.drawLine(A,i.globals.translateY+k.offsetY-2,A,i.globals.gridHeight+i.globals.translateY+k.offsetY+2,k.color,0,k.width);l.add(S)}return i.config.yaxis[t].axisTicks.show&&this.axesUtils.drawYAxisTicks(A,h,k,i.config.yaxis[t].axisTicks,t,d,l),l}},{key:\"drawYaxisInversed\",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:\"apexcharts-xaxis apexcharts-yaxis-inversed\"}),s=i.group({class:\"apexcharts-xaxis-texts-g\",transform:\"translate(\".concat(e.globals.translateXAxisX,\", \").concat(e.globals.translateXAxisY,\")\")});a.add(s);var r=e.globals.yAxisScale[t].result.length-1,n=e.globals.gridWidth/r+.1,o=n+e.config.xaxis.labels.offsetX,l=e.globals.xLabelFormatter,c=e.globals.yAxisScale[t].result.slice(),h=e.globals.timescaleLabels;h.length>0&&(this.xaxisLabels=h.slice(),r=(c=h.slice()).length),c=this.axesUtils.checkForReversedLabels(t,c);var d=h.length;if(e.config.xaxis.labels.show)for(var u=d?0:r;d?u<d:u>=0;d?u++:u--){var g=c[u];g=l(g,u,e);var f=e.globals.gridWidth+e.globals.padHorizontal-(o-n+e.config.xaxis.labels.offsetX);if(h.length){var p=this.axesUtils.getLabel(c,h,f,u,this.drawnLabels,this.xaxisFontSize);f=p.x,g=p.text,this.drawnLabels.push(p.text),0===u&&e.globals.skipFirstTimelinelabel&&(g=\"\"),u===c.length-1&&e.globals.skipLastTimelinelabel&&(g=\"\")}var x=i.drawText({x:f,y:this.xAxisoffX+e.config.xaxis.labels.offsetY+30-(\"top\"===e.config.xaxis.position?e.globals.xAxisHeight+e.config.xaxis.axisTicks.height-2:0),text:g,textAnchor:\"middle\",foreColor:Array.isArray(this.xaxisForeColors)?this.xaxisForeColors[t]:this.xaxisForeColors,fontSize:this.xaxisFontSize,fontFamily:this.xaxisFontFamily,fontWeight:e.config.xaxis.labels.style.fontWeight,isPlainText:!1,cssClass:\"apexcharts-xaxis-label \"+e.config.xaxis.labels.style.cssClass});s.add(x),x.tspan(g);var v=document.createElementNS(e.globals.SVGNS,\"title\");v.textContent=g,x.node.appendChild(v),o+=n}return this.inversedYAxisTitleText(a),this.inversedYAxisBorder(a),a}},{key:\"inversedYAxisBorder\",value:function(t){var e=this.w,i=new b(this.ctx),a=e.config.xaxis.axisBorder;if(a.show){var s=0;\"bar\"===e.config.chart.type&&e.globals.isXNumeric&&(s-=15);var r=i.drawLine(e.globals.padHorizontal+s+a.offsetX,this.xAxisoffX,e.globals.gridWidth,this.xAxisoffX,a.color,0,a.height);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(r):t.add(r)}}},{key:\"inversedYAxisTitleText\",value:function(t){var e=this.w,i=new b(this.ctx);if(void 0!==e.config.xaxis.title.text){var a=i.group({class:\"apexcharts-xaxis-title apexcharts-yaxis-title-inversed\"}),s=i.drawText({x:e.globals.gridWidth/2+e.config.xaxis.title.offsetX,y:this.xAxisoffX+parseFloat(this.xaxisFontSize)+parseFloat(e.config.xaxis.title.style.fontSize)+e.config.xaxis.title.offsetY+20,text:e.config.xaxis.title.text,textAnchor:\"middle\",fontSize:e.config.xaxis.title.style.fontSize,fontFamily:e.config.xaxis.title.style.fontFamily,fontWeight:e.config.xaxis.title.style.fontWeight,foreColor:e.config.xaxis.title.style.color,cssClass:\"apexcharts-xaxis-title-text \"+e.config.xaxis.title.style.cssClass});a.add(s),t.add(a)}}},{key:\"yAxisTitleRotate\",value:function(t,e){var i=this.w,a=new b(this.ctx),s={width:0,height:0},r={width:0,height:0},n=i.globals.dom.baseEl.querySelector(\" .apexcharts-yaxis[rel='\".concat(t,\"'] .apexcharts-yaxis-texts-g\"));null!==n&&(s=n.getBoundingClientRect());var o=i.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(t,\"'] .apexcharts-yaxis-title text\"));if(null!==o&&(r=o.getBoundingClientRect()),null!==o){var l=this.xPaddingForYAxisTitle(t,s,r,e);o.setAttribute(\"x\",l.xPos-(e?10:0))}if(null!==o){var c=a.rotateAroundCenter(o);o.setAttribute(\"transform\",\"rotate(\".concat(e?-1*i.config.yaxis[t].title.rotate:i.config.yaxis[t].title.rotate,\" \").concat(c.x,\" \").concat(c.y,\")\"))}}},{key:\"xPaddingForYAxisTitle\",value:function(t,e,i,a){var s=this.w,r=0,n=0,o=10;return void 0===s.config.yaxis[t].title.text||t<0?{xPos:n,padd:0}:(a?(n=e.width+s.config.yaxis[t].title.offsetX+i.width/2+o/2,0===(r+=1)&&(n-=o/2)):(n=-1*e.width+s.config.yaxis[t].title.offsetX+o/2+i.width/2,s.globals.isBarHorizontal&&(o=25,n=-1*e.width-s.config.yaxis[t].title.offsetX-o)),{xPos:n,padd:o})}},{key:\"setYAxisXPosition\",value:function(t,e){var i=this.w,a=0,s=0,r=18,n=1;i.config.yaxis.length>1&&(this.multipleYs=!0),i.config.yaxis.map((function(o,l){var c=i.globals.ignoreYAxisIndexes.indexOf(l)>-1||!o.show||o.floating||0===t[l].width,h=t[l].width+e[l].width;o.opposite?i.globals.isBarHorizontal?(s=i.globals.gridWidth+i.globals.translateX-1,i.globals.translateYAxisX[l]=s-o.labels.offsetX):(s=i.globals.gridWidth+i.globals.translateX+n,c||(n=n+h+20),i.globals.translateYAxisX[l]=s-o.labels.offsetX+20):(a=i.globals.translateX-r,c||(r=r+h+20),i.globals.translateYAxisX[l]=a+o.labels.offsetX)}))}},{key:\"setYAxisTextAlignments\",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-yaxis\");(e=n.listToArray(e)).forEach((function(e,i){var a=t.config.yaxis[i];if(a&&!a.floating&&void 0!==a.labels.align){var s=t.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(i,\"'] .apexcharts-yaxis-texts-g\")),r=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis[rel='\".concat(i,\"'] .apexcharts-yaxis-label\"));r=n.listToArray(r);var o=s.getBoundingClientRect();\"left\"===a.labels.align?(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"start\")})),a.opposite||s.setAttribute(\"transform\",\"translate(-\".concat(o.width,\", 0)\"))):\"center\"===a.labels.align?(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"middle\")})),s.setAttribute(\"transform\",\"translate(\".concat(o.width/2*(a.opposite?1:-1),\", 0)\"))):\"right\"===a.labels.align&&(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"end\")})),a.opposite&&s.setAttribute(\"transform\",\"translate(\".concat(o.width,\", 0)\")))}}))}}])&&It(e.prototype,i),t}();function zt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Xt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.documentEvent=n.bind(this.documentEvent,this)}var e,i;return e=t,(i=[{key:\"addEventListener\",value:function(t,e){var i=this.w;i.globals.events.hasOwnProperty(t)?i.globals.events[t].push(e):i.globals.events[t]=[e]}},{key:\"removeEventListener\",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){var a=i.globals.events[t].indexOf(e);-1!==a&&i.globals.events[t].splice(a,1)}}},{key:\"fireEvent\",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){e&&e.length||(e=[]);for(var a=i.globals.events[t],s=a.length,r=0;r<s;r++)a[r].apply(null,e)}}},{key:\"setupEventHandlers\",value:function(){var t=this,e=this.w,i=this.ctx,a=e.globals.dom.baseEl.querySelector(e.globals.chartClass);this.ctx.eventList.forEach((function(t){a.addEventListener(t,(function(t){var a=Object.assign({},e,{seriesIndex:e.globals.capturedSeriesIndex,dataPointIndex:e.globals.capturedDataPointIndex});\"mousemove\"===t.type||\"touchmove\"===t.type?\"function\"==typeof e.config.chart.events.mouseMove&&e.config.chart.events.mouseMove(t,i,a):\"mouseleave\"===t.type||\"touchleave\"===t.type?\"function\"==typeof e.config.chart.events.mouseLeave&&e.config.chart.events.mouseLeave(t,i,a):(\"mouseup\"===t.type&&1===t.which||\"touchend\"===t.type)&&(\"function\"==typeof e.config.chart.events.click&&e.config.chart.events.click(t,i,a),i.ctx.events.fireEvent(\"click\",[t,i,a]))}),{capture:!1,passive:!0})})),this.ctx.eventList.forEach((function(i){e.globals.dom.baseEl.addEventListener(i,t.documentEvent,{passive:!0})})),this.ctx.core.setupBrushHandler()}},{key:\"documentEvent\",value:function(t){var e=this.w,i=t.target.className;if(\"click\"===t.type){var a=e.globals.dom.baseEl.querySelector(\".apexcharts-menu\");a&&a.classList.contains(\"apexcharts-menu-open\")&&\"apexcharts-menu-icon\"!==i&&a.classList.remove(\"apexcharts-menu-open\")}e.globals.clientX=\"touchmove\"===t.type?t.touches[0].clientX:t.clientX,e.globals.clientY=\"touchmove\"===t.type?t.touches[0].clientY:t.clientY}}])&&zt(e.prototype,i),t}();function Yt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Dt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"setCurrentLocaleValues\",value:function(t){var e=this.w.config.chart.locales;window.Apex.chart&&window.Apex.chart.locales&&window.Apex.chart.locales.length>0&&(e=this.w.config.chart.locales.concat(window.Apex.chart.locales));var i=e.filter((function(e){return e.name===t}))[0];if(!i)throw new Error(\"Wrong locale name provided. Please make sure you set the correct locale name in options\");var a=n.extend(T,i);this.w.globals.locale=a.options}}])&&Yt(e.prototype,i),t}();function Rt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Ft=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"drawAxis\",value:function(t,e){var i,a,s=this,r=this.w.globals,n=this.w.config,o=new kt(this.ctx,e),l=new Mt(this.ctx,e);r.axisCharts&&\"radar\"!==t&&(r.isBarHorizontal?(a=l.drawYaxisInversed(0),i=o.drawXaxisInversed(0),r.dom.elGraphical.add(i),r.dom.elGraphical.add(a)):(i=o.drawXaxis(),r.dom.elGraphical.add(i),n.yaxis.map((function(t,e){if(-1===r.ignoreYAxisIndexes.indexOf(e)&&(a=l.drawYaxis(e),r.dom.Paper.add(a),\"back\"===s.w.config.grid.position)){var i=r.dom.Paper.children()[1];i.remove(),r.dom.Paper.add(i)}}))))}}])&&Rt(e.prototype,i),t}();function Ht(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const Nt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"drawXCrosshairs\",value:function(){var t=this.w,e=new b(this.ctx),i=new d(this.ctx),a=t.config.xaxis.crosshairs.fill.gradient,s=t.config.xaxis.crosshairs.dropShadow,r=t.config.xaxis.crosshairs.fill.type,o=a.colorFrom,l=a.colorTo,c=a.opacityFrom,h=a.opacityTo,u=a.stops,g=s.enabled,f=s.left,p=s.top,x=s.blur,v=s.color,m=s.opacity,y=t.config.xaxis.crosshairs.fill.color;if(t.config.xaxis.crosshairs.show){\"gradient\"===r&&(y=e.drawGradient(\"vertical\",o,l,c,h,null,u,null));var w=e.drawRect();1===t.config.xaxis.crosshairs.width&&(w=e.drawLine());var k=t.globals.gridHeight;(!n.isNumber(k)||k<0)&&(k=0);var A=t.config.xaxis.crosshairs.width;(!n.isNumber(A)||A<0)&&(A=0),w.attr({class:\"apexcharts-xcrosshairs\",x:0,y:0,y2:k,width:A,height:k,fill:y,filter:\"none\",\"fill-opacity\":t.config.xaxis.crosshairs.opacity,stroke:t.config.xaxis.crosshairs.stroke.color,\"stroke-width\":t.config.xaxis.crosshairs.stroke.width,\"stroke-dasharray\":t.config.xaxis.crosshairs.stroke.dashArray}),g&&(w=i.dropShadow(w,{left:f,top:p,blur:x,color:v,opacity:m})),t.globals.dom.elGraphical.add(w)}}},{key:\"drawYCrosshairs\",value:function(){var t=this.w,e=new b(this.ctx),i=t.config.yaxis[0].crosshairs,a=t.globals.barPadForNumericAxis;if(t.config.yaxis[0].crosshairs.show){var s=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,i.stroke.dashArray,i.stroke.width);s.attr({class:\"apexcharts-ycrosshairs\"}),t.globals.dom.elGraphical.add(s)}var r=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,0,0);r.attr({class:\"apexcharts-ycrosshairs-hidden\"}),t.globals.dom.elGraphical.add(r)}}])&&Ht(e.prototype,i),t}();function Wt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var jt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,i=[{key:\"checkResponsiveConfig\",value:function(t){var e=this,i=this.w,a=i.config;if(0!==a.responsive.length){var s=a.responsive.slice();s.sort((function(t,e){return t.breakpoint>e.breakpoint?1:e.breakpoint>t.breakpoint?-1:0})).reverse();var r=new Z({}),o=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=s[0].breakpoint,o=window.innerWidth>0?window.innerWidth:screen.width;if(o>a){var l=y.extendArrayProps(r,i.globals.initialConfig,i);t=n.extend(l,t),t=n.extend(i.config,t),e.overrideResponsiveOptions(t)}else for(var c=0;c<s.length;c++)o<s[c].breakpoint&&(t=y.extendArrayProps(r,s[c].options,i),t=n.extend(i.config,t),e.overrideResponsiveOptions(t))};if(t){var l=y.extendArrayProps(r,t,i);l=n.extend(i.config,l),o(l=n.extend(l,t))}else o({})}}},{key:\"overrideResponsiveOptions\",value:function(t){var e=new Z(t).init({responsiveOverride:!0});this.w.config=e}}],i&&Wt(e.prototype,i),t}();function Bt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Gt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.colors=[],this.w=e.w;var i=this.w;this.isColorFn=!1,this.isHeatmapDistributed=\"treemap\"===i.config.chart.type&&i.config.plotOptions.treemap.distributed||\"heatmap\"===i.config.chart.type&&i.config.plotOptions.heatmap.distributed,this.isBarDistributed=i.config.plotOptions.bar.distributed&&(\"bar\"===i.config.chart.type||\"rangeBar\"===i.config.chart.type)}var e,i;return e=t,i=[{key:\"init\",value:function(){this.setDefaultColors()}},{key:\"setDefaultColors\",value:function(){var t=this,e=this.w,i=new n;if(e.globals.dom.elWrap.classList.add(\"apexcharts-theme-\".concat(e.config.theme.mode)),void 0===e.config.colors?e.globals.colors=this.predefined():(e.globals.colors=e.config.colors,Array.isArray(e.config.colors)&&e.config.colors.length>0&&\"function\"==typeof e.config.colors[0]&&(e.globals.colors=e.config.series.map((function(i,a){var s=e.config.colors[a];return s||(s=e.config.colors[0]),\"function\"==typeof s?(t.isColorFn=!0,s({value:e.globals.axisCharts?e.globals.series[a][0]?e.globals.series[a][0]:0:e.globals.series[a],seriesIndex:a,dataPointIndex:a,w:e})):s})))),e.globals.seriesColors.map((function(t,i){t&&(e.globals.colors[i]=t)})),e.config.theme.monochrome.enabled){var a=[],s=e.globals.series.length;(this.isBarDistributed||this.isHeatmapDistributed)&&(s=e.globals.series[0].length*e.globals.series.length);for(var r=e.config.theme.monochrome.color,o=1/(s/e.config.theme.monochrome.shadeIntensity),l=e.config.theme.monochrome.shadeTo,c=0,h=0;h<s;h++){var d=void 0;\"dark\"===l?(d=i.shadeColor(-1*c,r),c+=o):(d=i.shadeColor(c,r),c+=o),a.push(d)}e.globals.colors=a.slice()}var u=e.globals.colors.slice();this.pushExtraColors(e.globals.colors),[\"fill\",\"stroke\"].forEach((function(i){void 0===e.config[i].colors?e.globals[i].colors=t.isColorFn?e.config.colors:u:e.globals[i].colors=e.config[i].colors.slice(),t.pushExtraColors(e.globals[i].colors)})),void 0===e.config.dataLabels.style.colors?e.globals.dataLabels.style.colors=u:e.globals.dataLabels.style.colors=e.config.dataLabels.style.colors.slice(),this.pushExtraColors(e.globals.dataLabels.style.colors,50),void 0===e.config.plotOptions.radar.polygons.fill.colors?e.globals.radarPolygons.fill.colors=[\"dark\"===e.config.theme.mode?\"#424242\":\"none\"]:e.globals.radarPolygons.fill.colors=e.config.plotOptions.radar.polygons.fill.colors.slice(),this.pushExtraColors(e.globals.radarPolygons.fill.colors,20),void 0===e.config.markers.colors?e.globals.markers.colors=u:e.globals.markers.colors=e.config.markers.colors.slice(),this.pushExtraColors(e.globals.markers.colors)}},{key:\"pushExtraColors\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=e||a.globals.series.length;if(null===i&&(i=this.isBarDistributed||this.isHeatmapDistributed||\"heatmap\"===a.config.chart.type&&a.config.plotOptions.heatmap.colorScale.inverse),i&&a.globals.series.length&&(s=a.globals.series[a.globals.maxValsInArrayIndex].length*a.globals.series.length),t.length<s)for(var r=s-t.length,n=0;n<r;n++)t.push(t[n])}},{key:\"updateThemeOptions\",value:function(t){t.chart=t.chart||{},t.tooltip=t.tooltip||{};var e=t.theme.mode||\"light\",i=t.theme.palette?t.theme.palette:\"dark\"===e?\"palette4\":\"palette1\",a=t.chart.foreColor?t.chart.foreColor:\"dark\"===e?\"#f6f7f8\":\"#373d3f\";return t.tooltip.theme=e,t.chart.foreColor=a,t.theme.palette=i,t}},{key:\"predefined\",value:function(){switch(this.w.config.theme.palette){case\"palette1\":default:this.colors=[\"#008FFB\",\"#00E396\",\"#FEB019\",\"#FF4560\",\"#775DD0\"];break;case\"palette2\":this.colors=[\"#3f51b5\",\"#03a9f4\",\"#4caf50\",\"#f9ce1d\",\"#FF9800\"];break;case\"palette3\":this.colors=[\"#33b2df\",\"#546E7A\",\"#d4526e\",\"#13d8aa\",\"#A5978B\"];break;case\"palette4\":this.colors=[\"#4ecdc4\",\"#c7f464\",\"#81D4FA\",\"#fd6a6a\",\"#546E7A\"];break;case\"palette5\":this.colors=[\"#2b908f\",\"#f9a3a4\",\"#90ee7e\",\"#fa4443\",\"#69d2e7\"];break;case\"palette6\":this.colors=[\"#449DD1\",\"#F86624\",\"#EA3546\",\"#662E9B\",\"#C5D86D\"];break;case\"palette7\":this.colors=[\"#D7263D\",\"#1B998B\",\"#2E294E\",\"#F46036\",\"#E2C044\"];break;case\"palette8\":this.colors=[\"#662E9B\",\"#F86624\",\"#F9C80E\",\"#EA3546\",\"#43BCCD\"];break;case\"palette9\":this.colors=[\"#5C4742\",\"#A5978B\",\"#8D5B4C\",\"#5A2A27\",\"#C4BBAF\"];break;case\"palette10\":this.colors=[\"#A300D6\",\"#7D02EB\",\"#5653FE\",\"#2983FF\",\"#00B1F2\"]}return this.colors}}],i&&Bt(e.prototype,i),t}();function Vt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var _t=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"draw\",value:function(){this.drawTitleSubtitle(\"title\"),this.drawTitleSubtitle(\"subtitle\")}},{key:\"drawTitleSubtitle\",value:function(t){var e=this.w,i=\"title\"===t?e.config.title:e.config.subtitle,a=e.globals.svgWidth/2,s=i.offsetY,r=\"middle\";if(\"left\"===i.align?(a=10,r=\"start\"):\"right\"===i.align&&(a=e.globals.svgWidth-10,r=\"end\"),a+=i.offsetX,s=s+parseInt(i.style.fontSize,10)+i.margin/2,void 0!==i.text){var n=new b(this.ctx).drawText({x:a,y:s,text:i.text,textAnchor:r,fontSize:i.style.fontSize,fontFamily:i.style.fontFamily,fontWeight:i.style.fontWeight,foreColor:i.style.color,opacity:1});n.node.setAttribute(\"class\",\"apexcharts-\".concat(t,\"-text\")),e.globals.dom.Paper.add(n)}}}])&&Vt(e.prototype,i),t}();function Ut(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function qt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Zt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.dCtx=e}var e,i;return e=t,(i=[{key:\"getTitleSubtitleCoords\",value:function(t){var e=this.w,i=0,a=0,s=\"title\"===t?e.config.title.floating:e.config.subtitle.floating,r=e.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(t,\"-text\"));if(null!==r&&!s){var n=r.getBoundingClientRect();i=n.width,a=e.globals.axisCharts?n.height+5:n.height}return{width:i,height:a}}},{key:\"getLegendsRect\",value:function(){var t=this.w,e=t.globals.dom.baseEl.querySelector(\".apexcharts-legend\");t.config.legend.height||\"top\"!==t.config.legend.position&&\"bottom\"!==t.config.legend.position||(e.style.maxHeight=t.globals.svgHeight/2+\"px\");var i=Object.assign({},n.getBoundingClientRect(e));return null!==e&&!t.config.legend.floating&&t.config.legend.show?this.dCtx.lgRect={x:i.x,y:i.y,height:i.height,width:0===i.height?0:i.width}:this.dCtx.lgRect={x:0,y:0,height:0,width:0},\"left\"!==t.config.legend.position&&\"right\"!==t.config.legend.position||1.5*this.dCtx.lgRect.width>t.globals.svgWidth&&(this.dCtx.lgRect.width=t.globals.svgWidth/1.5),this.dCtx.lgRect}},{key:\"getLargestStringFromMultiArr\",value:function(t,e){var i=t;if(this.w.globals.isMultiLineX){var a=e.map((function(t,e){return Array.isArray(t)?t.length:1})),s=Math.max.apply(Math,function(t){return function(t){if(Array.isArray(t))return Ut(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||function(t,e){if(t){if(\"string\"==typeof t)return Ut(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Ut(t,e):void 0}}(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}(a));i=e[a.indexOf(s)]}return i}}])&&qt(e.prototype,i),t}();function $t(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Jt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.dCtx=e}var e,i;return e=t,(i=[{key:\"getxAxisLabelsCoords\",value:function(){var t,e=this.w,i=e.globals.labels.slice();if(e.config.xaxis.convertedCatToNumeric&&0===i.length&&(i=e.globals.categoryLabels),e.globals.timescaleLabels.length>0){var a=this.getxAxisTimeScaleLabelsCoords();t={width:a.width,height:a.height},e.globals.rotateXLabels=!1}else{this.dCtx.lgWidthForSideLegends=\"left\"!==e.config.legend.position&&\"right\"!==e.config.legend.position||e.config.legend.floating?0:this.dCtx.lgRect.width;var s=e.globals.xLabelFormatter,r=n.getLargestStringFromArr(i),o=this.dCtx.dimHelpers.getLargestStringFromMultiArr(r,i);e.globals.isBarHorizontal&&(o=r=e.globals.yAxisScale[0].result.reduce((function(t,e){return t.length>e.length?t:e}),0));var l=new H(this.dCtx.ctx),c=r;r=l.xLabelFormat(s,r,c,{i:void 0,dateFormatter:new R(this.dCtx.ctx).formatDate,w:e}),o=l.xLabelFormat(s,o,c,{i:void 0,dateFormatter:new R(this.dCtx.ctx).formatDate,w:e}),(e.config.xaxis.convertedCatToNumeric&&void 0===r||\"\"===String(r).trim())&&(o=r=\"1\");var h=new b(this.dCtx.ctx),d=h.getTextRects(r,e.config.xaxis.labels.style.fontSize),u=d;if(r!==o&&(u=h.getTextRects(o,e.config.xaxis.labels.style.fontSize)),(t={width:d.width>=u.width?d.width:u.width,height:d.height>=u.height?d.height:u.height}).width*i.length>e.globals.svgWidth-this.dCtx.lgWidthForSideLegends-this.dCtx.yAxisWidth-this.dCtx.gridPad.left-this.dCtx.gridPad.right&&0!==e.config.xaxis.labels.rotate||e.config.xaxis.labels.rotateAlways){if(!e.globals.isBarHorizontal){e.globals.rotateXLabels=!0;var g=function(t){return h.getTextRects(t,e.config.xaxis.labels.style.fontSize,e.config.xaxis.labels.style.fontFamily,\"rotate(\".concat(e.config.xaxis.labels.rotate,\" 0 0)\"),!1)};d=g(r),r!==o&&(u=g(o)),t.height=(d.height>u.height?d.height:u.height)/1.5,t.width=d.width>u.width?d.width:u.width}}else e.globals.rotateXLabels=!1}return e.config.xaxis.labels.show||(t={width:0,height:0}),{width:t.width,height:t.height}}},{key:\"getxAxisGroupLabelsCoords\",value:function(){var t,e=this.w;if(!e.globals.hasGroups)return{width:0,height:0};var i,a=(null===(t=e.config.xaxis.group.style)||void 0===t?void 0:t.fontSize)||e.config.xaxis.labels.style.fontSize,s=e.globals.groups.map((function(t){return t.title})),r=n.getLargestStringFromArr(s),o=this.dCtx.dimHelpers.getLargestStringFromMultiArr(r,s),l=new b(this.dCtx.ctx),c=l.getTextRects(r,a),h=c;return r!==o&&(h=l.getTextRects(o,a)),i={width:c.width>=h.width?c.width:h.width,height:c.height>=h.height?c.height:h.height},e.config.xaxis.labels.show||(i={width:0,height:0}),{width:i.width,height:i.height}}},{key:\"getxAxisTitleCoords\",value:function(){var t=this.w,e=0,i=0;if(void 0!==t.config.xaxis.title.text){var a=new b(this.dCtx.ctx).getTextRects(t.config.xaxis.title.text,t.config.xaxis.title.style.fontSize);e=a.width,i=a.height}return{width:e,height:i}}},{key:\"getxAxisTimeScaleLabelsCoords\",value:function(){var t,e=this.w;this.dCtx.timescaleLabels=e.globals.timescaleLabels.slice();var i=this.dCtx.timescaleLabels.map((function(t){return t.value})),a=i.reduce((function(t,e){return void 0===t?(console.error(\"You have possibly supplied invalid Date format. Please supply a valid JavaScript Date\"),0):t.length>e.length?t:e}),0);return 1.05*(t=new b(this.dCtx.ctx).getTextRects(a,e.config.xaxis.labels.style.fontSize)).width*i.length>e.globals.gridWidth&&0!==e.config.xaxis.labels.rotate&&(e.globals.overlappingXLabels=!0),t}},{key:\"additionalPaddingXLabels\",value:function(t){var e=this,i=this.w,a=i.globals,s=i.config,r=s.xaxis.type,n=t.width;a.skipLastTimelinelabel=!1,a.skipFirstTimelinelabel=!1;var o=i.config.yaxis[0].opposite&&i.globals.isBarHorizontal;s.yaxis.forEach((function(t,l){o?(e.dCtx.gridPad.left<n&&(e.dCtx.xPadLeft=n/2+1),e.dCtx.xPadRight=n/2+1):function(t,o){(function(t){return-1!==a.collapsedSeriesIndices.indexOf(t)})(o)||function(t){if(e.dCtx.timescaleLabels&&e.dCtx.timescaleLabels.length){var o=e.dCtx.timescaleLabels[0],l=e.dCtx.timescaleLabels[e.dCtx.timescaleLabels.length-1].position+n/1.75-e.dCtx.yAxisWidthRight,c=o.position-n/1.75+e.dCtx.yAxisWidthLeft,h=\"right\"===i.config.legend.position&&e.dCtx.lgRect.width>0?e.dCtx.lgRect.width:0;l>a.svgWidth-a.translateX-h&&(a.skipLastTimelinelabel=!0),c<-(t.show&&!t.floating||\"bar\"!==s.chart.type&&\"candlestick\"!==s.chart.type&&\"rangeBar\"!==s.chart.type&&\"boxPlot\"!==s.chart.type?10:n/1.75)&&(a.skipFirstTimelinelabel=!0)}else\"datetime\"===r?e.dCtx.gridPad.right<n&&!a.rotateXLabels&&(a.skipLastTimelinelabel=!0):\"datetime\"!==r&&e.dCtx.gridPad.right<n/2-e.dCtx.yAxisWidthRight&&!a.rotateXLabels&&!i.config.xaxis.labels.trim&&(\"between\"!==i.config.xaxis.tickPlacement||i.globals.isBarHorizontal)&&(e.dCtx.xPadRight=n/2+1)}(t)}(t,l)}))}}])&&$t(e.prototype,i),t}();function Qt(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Kt=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.dCtx=e}var e,i;return e=t,(i=[{key:\"getyAxisLabelsCoords\",value:function(){var t=this,e=this.w,i=[],a=10,s=new xt(this.dCtx.ctx);return e.config.yaxis.map((function(r,o){var l=e.globals.yAxisScale[o],c=0;if(!s.isYAxisHidden(o)&&r.labels.show&&void 0!==r.labels.minWidth&&(c=r.labels.minWidth),!s.isYAxisHidden(o)&&r.labels.show&&l.result.length){var h=e.globals.yLabelFormatters[o],d=l.niceMin===Number.MIN_VALUE?0:l.niceMin,u=String(d).length>String(l.niceMax).length?d:l.niceMax,g=h(u,{seriesIndex:o,dataPointIndex:-1,w:e}),f=g;if(void 0!==g&&0!==g.length||(g=u),e.globals.isBarHorizontal){a=0;var p=e.globals.labels.slice();g=h(g=n.getLargestStringFromArr(p),{seriesIndex:o,dataPointIndex:-1,w:e}),f=t.dCtx.dimHelpers.getLargestStringFromMultiArr(g,p)}var x=new b(t.dCtx.ctx),v=\"rotate(\".concat(r.labels.rotate,\" 0 0)\"),m=x.getTextRects(g,r.labels.style.fontSize,r.labels.style.fontFamily,v,!1),y=m;g!==f&&(y=x.getTextRects(f,r.labels.style.fontSize,r.labels.style.fontFamily,v,!1)),i.push({width:(c>y.width||c>m.width?c:y.width>m.width?y.width:m.width)+a,height:y.height>m.height?y.height:m.height})}else i.push({width:0,height:0})})),i}},{key:\"getyAxisTitleCoords\",value:function(){var t=this,e=this.w,i=[];return e.config.yaxis.map((function(e,a){if(e.show&&void 0!==e.title.text){var s=new b(t.dCtx.ctx),r=\"rotate(\".concat(e.title.rotate,\" 0 0)\"),n=s.getTextRects(e.title.text,e.title.style.fontSize,e.title.style.fontFamily,r,!1);i.push({width:n.width,height:n.height})}else i.push({width:0,height:0})})),i}},{key:\"getTotalYAxisWidth\",value:function(){var t=this.w,e=0,i=0,a=0,s=t.globals.yAxisScale.length>1?10:0,r=new xt(this.dCtx.ctx),n=function(n,o){var l=t.config.yaxis[o].floating,c=0;n.width>0&&!l?(c=n.width+s,function(e){return t.globals.ignoreYAxisIndexes.indexOf(e)>-1}(o)&&(c=c-n.width-s)):c=l||r.isYAxisHidden(o)?0:5,t.config.yaxis[o].opposite?a+=c:i+=c,e+=c};return t.globals.yLabelsCoords.map((function(t,e){n(t,e)})),t.globals.yTitleCoords.map((function(t,e){n(t,e)})),t.globals.isBarHorizontal&&!t.config.yaxis[0].floating&&(e=t.globals.yLabelsCoords[0].width+t.globals.yTitleCoords[0].width+15),this.dCtx.yAxisWidthLeft=i,this.dCtx.yAxisWidthRight=a,e}}])&&Qt(e.prototype,i),t}();function te(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var ee=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.dCtx=e}var e,i;return e=t,(i=[{key:\"gridPadForColumnsInNumericAxis\",value:function(t){var e=this.w;if(e.globals.noData||e.globals.allSeriesCollapsed)return 0;var i=function(t){return\"bar\"===t||\"rangeBar\"===t||\"candlestick\"===t||\"boxPlot\"===t},a=e.config.chart.type,s=0,r=i(a)?e.config.series.length:1;if(e.globals.comboBarCount>0&&(r=e.globals.comboBarCount),e.globals.collapsedSeries.forEach((function(t){i(t.type)&&(r-=1)})),e.config.chart.stacked&&(r=1),(i(a)||e.globals.comboBarCount>0)&&e.globals.isXNumeric&&!e.globals.isBarHorizontal&&r>0){var n,o,l=Math.abs(e.globals.initialMaxX-e.globals.initialMinX);l<=3&&(l=e.globals.dataPoints),n=l/t,e.globals.minXDiff&&e.globals.minXDiff/n>0&&(o=e.globals.minXDiff/n),o>t/2&&(o/=2),(s=o/r*parseInt(e.config.plotOptions.bar.columnWidth,10)/100)<1&&(s=1),s=s/(r>1?1:1.5)+5,e.globals.barPadForNumericAxis=s}return s}},{key:\"gridPadFortitleSubtitle\",value:function(){var t=this,e=this.w,i=e.globals,a=this.dCtx.isSparkline||!e.globals.axisCharts?0:10;[\"title\",\"subtitle\"].forEach((function(i){void 0!==e.config[i].text?a+=e.config[i].margin:a+=t.dCtx.isSparkline||!e.globals.axisCharts?0:5})),!e.config.legend.show||\"bottom\"!==e.config.legend.position||e.config.legend.floating||e.globals.axisCharts||(a+=10);var s=this.dCtx.dimHelpers.getTitleSubtitleCoords(\"title\"),r=this.dCtx.dimHelpers.getTitleSubtitleCoords(\"subtitle\");i.gridHeight=i.gridHeight-s.height-r.height-a,i.translateY=i.translateY+s.height+r.height+a}},{key:\"setGridXPosForDualYAxis\",value:function(t,e){var i=this.w,a=new xt(this.dCtx.ctx);i.config.yaxis.map((function(s,r){-1!==i.globals.ignoreYAxisIndexes.indexOf(r)||s.floating||a.isYAxisHidden(r)||(s.opposite&&(i.globals.translateX=i.globals.translateX-(e[r].width+t[r].width)-parseInt(i.config.yaxis[r].labels.style.fontSize,10)/1.2-12),i.globals.translateX<2&&(i.globals.translateX=2))}))}}])&&te(e.prototype,i),t}();function ie(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function ae(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var se=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.lgRect={},this.yAxisWidth=0,this.yAxisWidthLeft=0,this.yAxisWidthRight=0,this.xAxisHeight=0,this.isSparkline=this.w.config.chart.sparkline.enabled,this.dimHelpers=new Zt(this),this.dimYAxis=new Kt(this),this.dimXAxis=new Jt(this),this.dimGrid=new ee(this),this.lgWidthForSideLegends=0,this.gridPad=this.w.config.grid.padding,this.xPadRight=0,this.xPadLeft=0}var e,i;return e=t,(i=[{key:\"plotCoords\",value:function(){var t=this,e=this.w,i=e.globals;this.lgRect=this.dimHelpers.getLegendsRect(),this.isSparkline&&(e.config.markers.discrete.length>0||e.config.markers.size>0)&&Object.entries(this.gridPad).forEach((function(e){var i,a,s=(a=2,function(t){if(Array.isArray(t))return t}(i=e)||function(t,e){var i=null==t?null:\"undefined\"!=typeof Symbol&&t[Symbol.iterator]||t[\"@@iterator\"];if(null!=i){var a,s,r=[],n=!0,o=!1;try{for(i=i.call(t);!(n=(a=i.next()).done)&&(r.push(a.value),!e||r.length!==e);n=!0);}catch(t){o=!0,s=t}finally{try{n||null==i.return||i.return()}finally{if(o)throw s}}return r}}(i,a)||function(t,e){if(t){if(\"string\"==typeof t)return ie(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?ie(t,e):void 0}}(i,a)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()),r=s[0],n=s[1];t.gridPad[r]=Math.max(n,t.w.globals.markers.largestSize/1.5)})),i.axisCharts?this.setDimensionsForAxisCharts():this.setDimensionsForNonAxisCharts(),this.dimGrid.gridPadFortitleSubtitle(),i.gridHeight=i.gridHeight-this.gridPad.top-this.gridPad.bottom,i.gridWidth=i.gridWidth-this.gridPad.left-this.gridPad.right-this.xPadRight-this.xPadLeft;var a=this.dimGrid.gridPadForColumnsInNumericAxis(i.gridWidth);i.gridWidth=i.gridWidth-2*a,i.translateX=i.translateX+this.gridPad.left+this.xPadLeft+(a>0?a+4:0),i.translateY=i.translateY+this.gridPad.top}},{key:\"setDimensionsForAxisCharts\",value:function(){var t=this,e=this.w,i=e.globals,a=this.dimYAxis.getyAxisLabelsCoords(),s=this.dimYAxis.getyAxisTitleCoords();e.globals.yLabelsCoords=[],e.globals.yTitleCoords=[],e.config.yaxis.map((function(t,i){e.globals.yLabelsCoords.push({width:a[i].width,index:i}),e.globals.yTitleCoords.push({width:s[i].width,index:i})})),this.yAxisWidth=this.dimYAxis.getTotalYAxisWidth();var r=this.dimXAxis.getxAxisLabelsCoords(),n=this.dimXAxis.getxAxisGroupLabelsCoords(),o=this.dimXAxis.getxAxisTitleCoords();this.conditionalChecksForAxisCoords(r,o,n),i.translateXAxisY=e.globals.rotateXLabels?this.xAxisHeight/8:-4,i.translateXAxisX=e.globals.rotateXLabels&&e.globals.isXNumeric&&e.config.xaxis.labels.rotate<=-45?-this.xAxisWidth/4:0,e.globals.isBarHorizontal&&(i.rotateXLabels=!1,i.translateXAxisY=parseInt(e.config.xaxis.labels.style.fontSize,10)/1.5*-1),i.translateXAxisY=i.translateXAxisY+e.config.xaxis.labels.offsetY,i.translateXAxisX=i.translateXAxisX+e.config.xaxis.labels.offsetX;var l=this.yAxisWidth,c=this.xAxisHeight;i.xAxisLabelsHeight=this.xAxisHeight-o.height,i.xAxisGroupLabelsHeight=i.xAxisLabelsHeight-r.height,i.xAxisLabelsWidth=this.xAxisWidth,i.xAxisHeight=this.xAxisHeight;var h=10;(\"radar\"===e.config.chart.type||this.isSparkline)&&(l=0,c=i.goldenPadding),this.isSparkline&&(this.lgRect={height:0,width:0}),(this.isSparkline||\"treemap\"===e.config.chart.type)&&(l=0,c=0,h=0),this.isSparkline||this.dimXAxis.additionalPaddingXLabels(r);var d=function(){i.translateX=l,i.gridHeight=i.svgHeight-t.lgRect.height-c-(t.isSparkline||\"treemap\"===e.config.chart.type?0:e.globals.rotateXLabels?10:15),i.gridWidth=i.svgWidth-l};switch(\"top\"===e.config.xaxis.position&&(h=i.xAxisHeight-e.config.xaxis.axisTicks.height-5),e.config.legend.position){case\"bottom\":i.translateY=h,d();break;case\"top\":i.translateY=this.lgRect.height+h,d();break;case\"left\":i.translateY=h,i.translateX=this.lgRect.width+l,i.gridHeight=i.svgHeight-c-12,i.gridWidth=i.svgWidth-this.lgRect.width-l;break;case\"right\":i.translateY=h,i.translateX=l,i.gridHeight=i.svgHeight-c-12,i.gridWidth=i.svgWidth-this.lgRect.width-l-5;break;default:throw new Error(\"Legend position not supported\")}this.dimGrid.setGridXPosForDualYAxis(s,a),new Mt(this.ctx).setYAxisXPosition(a,s)}},{key:\"setDimensionsForNonAxisCharts\",value:function(){var t=this.w,e=t.globals,i=t.config,a=0;t.config.legend.show&&!t.config.legend.floating&&(a=20);var s=\"pie\"===i.chart.type||\"polarArea\"===i.chart.type||\"donut\"===i.chart.type?\"pie\":\"radialBar\",r=i.plotOptions[s].offsetY,n=i.plotOptions[s].offsetX;if(!i.legend.show||i.legend.floating)return e.gridHeight=e.svgHeight-i.grid.padding.left+i.grid.padding.right,e.gridWidth=e.gridHeight,e.translateY=r,void(e.translateX=n+(e.svgWidth-e.gridWidth)/2);switch(i.legend.position){case\"bottom\":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.svgWidth,e.translateY=r-10,e.translateX=n+(e.svgWidth-e.gridWidth)/2;break;case\"top\":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.svgWidth,e.translateY=this.lgRect.height+r+10,e.translateX=n+(e.svgWidth-e.gridWidth)/2;break;case\"left\":e.gridWidth=e.svgWidth-this.lgRect.width-a,e.gridHeight=\"auto\"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=n+this.lgRect.width+a;break;case\"right\":e.gridWidth=e.svgWidth-this.lgRect.width-a-5,e.gridHeight=\"auto\"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=n+10;break;default:throw new Error(\"Legend position not supported\")}}},{key:\"conditionalChecksForAxisCoords\",value:function(t,e,i){var a=this.w,s=a.globals.hasGroups?2:1,r=i.height+t.height+e.height,n=a.globals.isMultiLineX?1.2:a.globals.LINE_HEIGHT_RATIO,o=a.globals.rotateXLabels?22:10,l=a.globals.rotateXLabels&&\"bottom\"===a.config.legend.position?10:0;this.xAxisHeight=r*n+s*o+l,this.xAxisWidth=t.width,this.xAxisHeight-e.height>a.config.xaxis.labels.maxHeight&&(this.xAxisHeight=a.config.xaxis.labels.maxHeight),a.config.xaxis.labels.minHeight&&this.xAxisHeight<a.config.xaxis.labels.minHeight&&(this.xAxisHeight=a.config.xaxis.labels.minHeight),a.config.xaxis.floating&&(this.xAxisHeight=0);var c=0,h=0;a.config.yaxis.forEach((function(t){c+=t.labels.minWidth,h+=t.labels.maxWidth})),this.yAxisWidth<c&&(this.yAxisWidth=c),this.yAxisWidth>h&&(this.yAxisWidth=h)}}])&&ae(e.prototype,i),t}();function re(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var ne=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.lgCtx=e}var e,i;return e=t,(i=[{key:\"getLegendBBox\",value:function(){var t=this.w.globals.dom.baseEl.querySelector(\".apexcharts-legend\").getBoundingClientRect(),e=t.width;return{clwh:t.height,clww:e}}},{key:\"toggleDataSeries\",value:function(t,e){var i=this,a=this.w;if(a.globals.axisCharts||\"radialBar\"===a.config.chart.type){a.globals.resized=!0;var s=null,r=null;a.globals.risingSeries=[],a.globals.axisCharts?(s=a.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(t,\"']\")),r=parseInt(s.getAttribute(\"data:realIndex\"),10)):(s=a.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(t+1,\"']\")),r=parseInt(s.getAttribute(\"rel\"),10)-1),e?[{cs:a.globals.collapsedSeries,csi:a.globals.collapsedSeriesIndices},{cs:a.globals.ancillaryCollapsedSeries,csi:a.globals.ancillaryCollapsedSeriesIndices}].forEach((function(t){i.riseCollapsedSeries(t.cs,t.csi,r)})):this.hideSeries({seriesEl:s,realIndex:r})}else{var n=a.globals.dom.Paper.select(\" .apexcharts-series[rel='\".concat(t+1,\"'] path\")),o=a.config.chart.type;if(\"pie\"===o||\"polarArea\"===o||\"donut\"===o){var l=a.config.plotOptions.pie.donut.labels;new b(this.lgCtx.ctx).pathMouseDown(n.members[0],null),this.lgCtx.ctx.pie.printDataLabelsInner(n.members[0].node,l)}n.fire(\"click\")}}},{key:\"hideSeries\",value:function(t){var e=t.seriesEl,i=t.realIndex,a=this.w,s=n.clone(a.config.series);if(a.globals.axisCharts){var r=!1;if(a.config.yaxis[i]&&a.config.yaxis[i].show&&a.config.yaxis[i].showAlways&&(r=!0,a.globals.ancillaryCollapsedSeriesIndices.indexOf(i)<0&&(a.globals.ancillaryCollapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split(\"-\")[1]}),a.globals.ancillaryCollapsedSeriesIndices.push(i))),!r){a.globals.collapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split(\"-\")[1]}),a.globals.collapsedSeriesIndices.push(i);var o=a.globals.risingSeries.indexOf(i);a.globals.risingSeries.splice(o,1)}}else a.globals.collapsedSeries.push({index:i,data:s[i]}),a.globals.collapsedSeriesIndices.push(i);for(var l=e.childNodes,c=0;c<l.length;c++)l[c].classList.contains(\"apexcharts-series-markers-wrap\")&&(l[c].classList.contains(\"apexcharts-hide\")?l[c].classList.remove(\"apexcharts-hide\"):l[c].classList.add(\"apexcharts-hide\"));a.globals.allSeriesCollapsed=a.globals.collapsedSeries.length===a.config.series.length,s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}},{key:\"riseCollapsedSeries\",value:function(t,e,i){var a=this.w,s=n.clone(a.config.series);if(t.length>0){for(var r=0;r<t.length;r++)t[r].index===i&&(a.globals.axisCharts?(s[i].data=t[r].data.slice(),t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)):(s[i]=t[r].data,t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)));s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}}},{key:\"_getSeriesBasedOnCollapsedState\",value:function(t){var e=this.w;return e.globals.axisCharts?t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a].data=[])})):t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a]=0)})),t}}])&&re(e.prototype,i),t}();function oe(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const le=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.onLegendClick=this.onLegendClick.bind(this),this.onLegendHovered=this.onLegendHovered.bind(this),this.isBarsDistributed=\"bar\"===this.w.config.chart.type&&this.w.config.plotOptions.bar.distributed&&1===this.w.config.series.length,this.legendHelpers=new ne(this)}var e,i;return e=t,(i=[{key:\"init\",value:function(){var t=this.w,e=t.globals,i=t.config;if((i.legend.showForSingleSeries&&1===e.series.length||this.isBarsDistributed||e.series.length>1||!e.axisCharts)&&i.legend.show){for(;e.dom.elLegendWrap.firstChild;)e.dom.elLegendWrap.removeChild(e.dom.elLegendWrap.firstChild);this.drawLegends(),\"bottom\"===i.legend.position||\"top\"===i.legend.position?this.legendAlignHorizontal():\"right\"!==i.legend.position&&\"left\"!==i.legend.position||this.legendAlignVertical()}}},{key:\"drawLegends\",value:function(){var t=this,e=this.w,i=e.config.legend.fontFamily,a=e.globals.seriesNames,s=e.globals.colors.slice();if(\"heatmap\"===e.config.chart.type){var r=e.config.plotOptions.heatmap.colorScale.ranges;a=r.map((function(t){return t.name?t.name:t.from+\" - \"+t.to})),s=r.map((function(t){return t.color}))}else this.isBarsDistributed&&(a=e.globals.labels.slice());e.config.legend.customLegendItems.length&&(a=e.config.legend.customLegendItems);for(var o=e.globals.legendFormatter,l=e.config.legend.inverseOrder,c=l?a.length-1:0;l?c>=0:c<=a.length-1;l?c--:c++){var h=o(a[c],{seriesIndex:c,w:e}),d=!1,u=!1;if(e.globals.collapsedSeries.length>0)for(var g=0;g<e.globals.collapsedSeries.length;g++)e.globals.collapsedSeries[g].index===c&&(d=!0);if(e.globals.ancillaryCollapsedSeriesIndices.length>0)for(var f=0;f<e.globals.ancillaryCollapsedSeriesIndices.length;f++)e.globals.ancillaryCollapsedSeriesIndices[f]===c&&(u=!0);var p=document.createElement(\"span\");p.classList.add(\"apexcharts-legend-marker\");var x=e.config.legend.markers.offsetX,v=e.config.legend.markers.offsetY,m=e.config.legend.markers.height,w=e.config.legend.markers.width,k=e.config.legend.markers.strokeWidth,A=e.config.legend.markers.strokeColor,S=e.config.legend.markers.radius,C=p.style;C.background=s[c],C.color=s[c],C.setProperty(\"background\",s[c],\"important\"),e.config.legend.markers.fillColors&&e.config.legend.markers.fillColors[c]&&(C.background=e.config.legend.markers.fillColors[c]),void 0!==e.globals.seriesColors[c]&&(C.background=e.globals.seriesColors[c],C.color=e.globals.seriesColors[c]),C.height=Array.isArray(m)?parseFloat(m[c])+\"px\":parseFloat(m)+\"px\",C.width=Array.isArray(w)?parseFloat(w[c])+\"px\":parseFloat(w)+\"px\",C.left=(Array.isArray(x)?parseFloat(x[c]):parseFloat(x))+\"px\",C.top=(Array.isArray(v)?parseFloat(v[c]):parseFloat(v))+\"px\",C.borderWidth=Array.isArray(k)?k[c]:k,C.borderColor=Array.isArray(A)?A[c]:A,C.borderRadius=Array.isArray(S)?parseFloat(S[c])+\"px\":parseFloat(S)+\"px\",e.config.legend.markers.customHTML&&(Array.isArray(e.config.legend.markers.customHTML)?e.config.legend.markers.customHTML[c]&&(p.innerHTML=e.config.legend.markers.customHTML[c]()):p.innerHTML=e.config.legend.markers.customHTML()),b.setAttrs(p,{rel:c+1,\"data:collapsed\":d||u}),(d||u)&&p.classList.add(\"apexcharts-inactive-legend\");var P=document.createElement(\"div\"),L=document.createElement(\"span\");L.classList.add(\"apexcharts-legend-text\"),L.innerHTML=Array.isArray(h)?h.join(\" \"):h;var O=e.config.legend.labels.useSeriesColors?e.globals.colors[c]:e.config.legend.labels.colors;O||(O=e.config.chart.foreColor),L.style.color=O,L.style.fontSize=parseFloat(e.config.legend.fontSize)+\"px\",L.style.fontWeight=e.config.legend.fontWeight,L.style.fontFamily=i||e.config.chart.fontFamily,b.setAttrs(L,{rel:c+1,i:c,\"data:default-text\":encodeURIComponent(h),\"data:collapsed\":d||u}),P.appendChild(p),P.appendChild(L);var T=new y(this.ctx);e.config.legend.showForZeroSeries||0===T.getSeriesTotalByIndex(c)&&T.seriesHaveSameValues(c)&&!T.isSeriesNull(c)&&-1===e.globals.collapsedSeriesIndices.indexOf(c)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(c)&&P.classList.add(\"apexcharts-hidden-zero-series\"),e.config.legend.showForNullSeries||T.isSeriesNull(c)&&-1===e.globals.collapsedSeriesIndices.indexOf(c)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(c)&&P.classList.add(\"apexcharts-hidden-null-series\"),e.globals.dom.elLegendWrap.appendChild(P),e.globals.dom.elLegendWrap.classList.add(\"apexcharts-align-\".concat(e.config.legend.horizontalAlign)),e.globals.dom.elLegendWrap.classList.add(\"apx-legend-position-\"+e.config.legend.position),P.classList.add(\"apexcharts-legend-series\"),P.style.margin=\"\".concat(e.config.legend.itemMargin.vertical,\"px \").concat(e.config.legend.itemMargin.horizontal,\"px\"),e.globals.dom.elLegendWrap.style.width=e.config.legend.width?e.config.legend.width+\"px\":\"\",e.globals.dom.elLegendWrap.style.height=e.config.legend.height?e.config.legend.height+\"px\":\"\",b.setAttrs(P,{rel:c+1,seriesName:n.escapeString(a[c]),\"data:collapsed\":d||u}),(d||u)&&P.classList.add(\"apexcharts-inactive-legend\"),e.config.legend.onItemClick.toggleDataSeries||P.classList.add(\"apexcharts-no-click\")}e.globals.dom.elWrap.addEventListener(\"click\",t.onLegendClick,!0),e.globals.dom.elWrap.appendChild(e.globals.dom.elLegendWrap),e.config.legend.onItemHover.highlightDataSeries&&0===e.config.legend.customLegendItems.length&&(e.globals.dom.elWrap.addEventListener(\"mousemove\",t.onLegendHovered,!0),e.globals.dom.elWrap.addEventListener(\"mouseout\",t.onLegendHovered,!0))}},{key:\"setLegendWrapXY\",value:function(t,e){var i=this.w,a=i.globals.dom.elLegendWrap,s=a.getBoundingClientRect(),r=0,n=0;if(\"bottom\"===i.config.legend.position)n+=i.globals.svgHeight-s.height/2;else if(\"top\"===i.config.legend.position){var o=new se(this.ctx),l=o.dimHelpers.getTitleSubtitleCoords(\"title\").height,c=o.dimHelpers.getTitleSubtitleCoords(\"subtitle\").height;n=n+(l>0?l-10:0)+(c>0?c-10:0)}a.style.position=\"absolute\",r=r+t+i.config.legend.offsetX,n=n+e+i.config.legend.offsetY,a.style.left=r+\"px\",a.style.top=n+\"px\",\"bottom\"===i.config.legend.position?(a.style.top=\"auto\",a.style.bottom=5-i.config.legend.offsetY+\"px\"):\"right\"===i.config.legend.position&&(a.style.left=\"auto\",a.style.right=25+i.config.legend.offsetX+\"px\"),[\"width\",\"height\"].forEach((function(t){a.style[t]&&(a.style[t]=parseInt(i.config.legend[t],10)+\"px\")}))}},{key:\"legendAlignHorizontal\",value:function(){var t=this.w;t.globals.dom.elLegendWrap.style.right=0;var e=this.legendHelpers.getLegendBBox(),i=new se(this.ctx),a=i.dimHelpers.getTitleSubtitleCoords(\"title\"),s=i.dimHelpers.getTitleSubtitleCoords(\"subtitle\"),r=0;\"bottom\"===t.config.legend.position?r=-e.clwh/1.8:\"top\"===t.config.legend.position&&(r=a.height+s.height+t.config.title.margin+t.config.subtitle.margin-10),this.setLegendWrapXY(20,r)}},{key:\"legendAlignVertical\",value:function(){var t=this.w,e=this.legendHelpers.getLegendBBox(),i=0;\"left\"===t.config.legend.position&&(i=20),\"right\"===t.config.legend.position&&(i=t.globals.svgWidth-e.clww-10),this.setLegendWrapXY(i,20)}},{key:\"onLegendHovered\",value:function(t){var e=this.w,i=t.target.classList.contains(\"apexcharts-legend-text\")||t.target.classList.contains(\"apexcharts-legend-marker\");if(\"heatmap\"===e.config.chart.type||this.isBarsDistributed){if(i){var a=parseInt(t.target.getAttribute(\"rel\"),10)-1;this.ctx.events.fireEvent(\"legendHover\",[this.ctx,a,this.w]),new ut(this.ctx).highlightRangeInSeries(t,t.target)}}else!t.target.classList.contains(\"apexcharts-inactive-legend\")&&i&&new ut(this.ctx).toggleSeriesOnHover(t,t.target)}},{key:\"onLegendClick\",value:function(t){var e=this.w;if(!e.config.legend.customLegendItems.length&&(t.target.classList.contains(\"apexcharts-legend-text\")||t.target.classList.contains(\"apexcharts-legend-marker\"))){var i=parseInt(t.target.getAttribute(\"rel\"),10)-1,a=\"true\"===t.target.getAttribute(\"data:collapsed\"),s=this.w.config.chart.events.legendClick;\"function\"==typeof s&&s(this.ctx,i,this.w),this.ctx.events.fireEvent(\"legendClick\",[this.ctx,i,this.w]);var r=this.w.config.legend.markers.onClick;\"function\"==typeof r&&t.target.classList.contains(\"apexcharts-legend-marker\")&&(r(this.ctx,i,this.w),this.ctx.events.fireEvent(\"legendMarkerClick\",[this.ctx,i,this.w])),\"treemap\"!==e.config.chart.type&&\"heatmap\"!==e.config.chart.type&&!this.isBarsDistributed&&e.config.legend.onItemClick.toggleDataSeries&&this.legendHelpers.toggleDataSeries(i,a)}}}])&&oe(e.prototype,i),t}();var ce=i(798),he=i.n(ce),de=i(688),ue=i.n(de),ge=i(149),fe=i.n(ge),pe=i(323),xe=i.n(pe),be=i(686),ve=i.n(be),me=i(618),ye=i.n(me),we=i(355),ke=i.n(we);function Ae(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Se=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.ev=this.w.config.chart.events,this.selectedClass=\"apexcharts-selected\",this.localeValues=this.w.globals.locale.toolbar,this.minX=i.globals.minX,this.maxX=i.globals.maxX}var e,i;return e=t,(i=[{key:\"createToolbar\",value:function(){var t=this,e=this.w,i=function(){return document.createElement(\"div\")},a=i();if(a.setAttribute(\"class\",\"apexcharts-toolbar\"),a.style.top=e.config.chart.toolbar.offsetY+\"px\",a.style.right=3-e.config.chart.toolbar.offsetX+\"px\",e.globals.dom.elWrap.appendChild(a),this.elZoom=i(),this.elZoomIn=i(),this.elZoomOut=i(),this.elPan=i(),this.elSelection=i(),this.elZoomReset=i(),this.elMenuIcon=i(),this.elMenu=i(),this.elCustomIcons=[],this.t=e.config.chart.toolbar.tools,Array.isArray(this.t.customIcons))for(var s=0;s<this.t.customIcons.length;s++)this.elCustomIcons.push(i());var r=[],o=function(i,a,s){var n=i.toLowerCase();t.t[n]&&e.config.chart.zoom.enabled&&r.push({el:a,icon:\"string\"==typeof t.t[n]?t.t[n]:s,title:t.localeValues[i],class:\"apexcharts-\".concat(n,\"-icon\")})};o(\"zoomIn\",this.elZoomIn,xe()),o(\"zoomOut\",this.elZoomOut,ve());var l=function(i){t.t[i]&&e.config.chart[i].enabled&&r.push({el:\"zoom\"===i?t.elZoom:t.elSelection,icon:\"string\"==typeof t.t[i]?t.t[i]:\"zoom\"===i?ue():ye(),title:t.localeValues[\"zoom\"===i?\"selectionZoom\":\"selection\"],class:e.globals.isTouchDevice?\"apexcharts-element-hidden\":\"apexcharts-\".concat(i,\"-icon\")})};l(\"zoom\"),l(\"selection\"),this.t.pan&&e.config.chart.zoom.enabled&&r.push({el:this.elPan,icon:\"string\"==typeof this.t.pan?this.t.pan:he(),title:this.localeValues.pan,class:e.globals.isTouchDevice?\"apexcharts-element-hidden\":\"apexcharts-pan-icon\"}),o(\"reset\",this.elZoomReset,fe()),this.t.download&&r.push({el:this.elMenuIcon,icon:\"string\"==typeof this.t.download?this.t.download:ke(),title:this.localeValues.menu,class:\"apexcharts-menu-icon\"});for(var c=0;c<this.elCustomIcons.length;c++)r.push({el:this.elCustomIcons[c],icon:this.t.customIcons[c].icon,title:this.t.customIcons[c].title,index:this.t.customIcons[c].index,class:\"apexcharts-toolbar-custom-icon \"+this.t.customIcons[c].class});r.forEach((function(t,e){t.index&&n.moveIndexInArray(r,e,t.index)}));for(var h=0;h<r.length;h++)b.setAttrs(r[h].el,{class:r[h].class,title:r[h].title}),r[h].el.innerHTML=r[h].icon,a.appendChild(r[h].el);this._createHamburgerMenu(a),e.globals.zoomEnabled?this.elZoom.classList.add(this.selectedClass):e.globals.panEnabled?this.elPan.classList.add(this.selectedClass):e.globals.selectionEnabled&&this.elSelection.classList.add(this.selectedClass),this.addToolbarEventListeners()}},{key:\"_createHamburgerMenu\",value:function(t){this.elMenuItems=[],t.appendChild(this.elMenu),b.setAttrs(this.elMenu,{class:\"apexcharts-menu\"});var e=[{name:\"exportSVG\",title:this.localeValues.exportToSVG},{name:\"exportPNG\",title:this.localeValues.exportToPNG},{name:\"exportCSV\",title:this.localeValues.exportToCSV}];this.w.globals.allSeriesHasEqualX||e.splice(2,1);for(var i=0;i<e.length;i++)this.elMenuItems.push(document.createElement(\"div\")),this.elMenuItems[i].innerHTML=e[i].title,b.setAttrs(this.elMenuItems[i],{class:\"apexcharts-menu-item \".concat(e[i].name),title:e[i].title}),this.elMenu.appendChild(this.elMenuItems[i])}},{key:\"addToolbarEventListeners\",value:function(){var t=this;this.elZoomReset.addEventListener(\"click\",this.handleZoomReset.bind(this)),this.elSelection.addEventListener(\"click\",this.toggleZoomSelection.bind(this,\"selection\")),this.elZoom.addEventListener(\"click\",this.toggleZoomSelection.bind(this,\"zoom\")),this.elZoomIn.addEventListener(\"click\",this.handleZoomIn.bind(this)),this.elZoomOut.addEventListener(\"click\",this.handleZoomOut.bind(this)),this.elPan.addEventListener(\"click\",this.togglePanning.bind(this)),this.elMenuIcon.addEventListener(\"click\",this.toggleMenu.bind(this)),this.elMenuItems.forEach((function(e){e.classList.contains(\"exportSVG\")?e.addEventListener(\"click\",t.handleDownload.bind(t,\"svg\")):e.classList.contains(\"exportPNG\")?e.addEventListener(\"click\",t.handleDownload.bind(t,\"png\")):e.classList.contains(\"exportCSV\")&&e.addEventListener(\"click\",t.handleDownload.bind(t,\"csv\"))}));for(var e=0;e<this.t.customIcons.length;e++)this.elCustomIcons[e].addEventListener(\"click\",this.t.customIcons[e].click.bind(this,this.ctx,this.ctx.w))}},{key:\"toggleZoomSelection\",value:function(t){this.ctx.getSyncedCharts().forEach((function(e){e.ctx.toolbar.toggleOtherControls();var i=\"selection\"===t?e.ctx.toolbar.elSelection:e.ctx.toolbar.elZoom,a=\"selection\"===t?\"selectionEnabled\":\"zoomEnabled\";e.w.globals[a]=!e.w.globals[a],i.classList.contains(e.ctx.toolbar.selectedClass)?i.classList.remove(e.ctx.toolbar.selectedClass):i.classList.add(e.ctx.toolbar.selectedClass)}))}},{key:\"getToolbarIconsReference\",value:function(){var t=this.w;this.elZoom||(this.elZoom=t.globals.dom.baseEl.querySelector(\".apexcharts-zoom-icon\")),this.elPan||(this.elPan=t.globals.dom.baseEl.querySelector(\".apexcharts-pan-icon\")),this.elSelection||(this.elSelection=t.globals.dom.baseEl.querySelector(\".apexcharts-selection-icon\"))}},{key:\"enableZoomPanFromToolbar\",value:function(t){this.toggleOtherControls(),\"pan\"===t?this.w.globals.panEnabled=!0:this.w.globals.zoomEnabled=!0;var e=\"pan\"===t?this.elPan:this.elZoom,i=\"pan\"===t?this.elZoom:this.elPan;e&&e.classList.add(this.selectedClass),i&&i.classList.remove(this.selectedClass)}},{key:\"togglePanning\",value:function(){this.ctx.getSyncedCharts().forEach((function(t){t.ctx.toolbar.toggleOtherControls(),t.w.globals.panEnabled=!t.w.globals.panEnabled,t.ctx.toolbar.elPan.classList.contains(t.ctx.toolbar.selectedClass)?t.ctx.toolbar.elPan.classList.remove(t.ctx.toolbar.selectedClass):t.ctx.toolbar.elPan.classList.add(t.ctx.toolbar.selectedClass)}))}},{key:\"toggleOtherControls\",value:function(){var t=this,e=this.w;e.globals.panEnabled=!1,e.globals.zoomEnabled=!1,e.globals.selectionEnabled=!1,this.getToolbarIconsReference(),[this.elPan,this.elSelection,this.elZoom].forEach((function(e){e&&e.classList.remove(t.selectedClass)}))}},{key:\"handleZoomIn\",value:function(){var t=this.w;t.globals.isRangeBar&&(this.minX=t.globals.minY,this.maxX=t.globals.maxY);var e=(this.minX+this.maxX)/2,i=(this.minX+e)/2,a=(this.maxX+e)/2,s=this._getNewMinXMaxX(i,a);t.globals.disableZoomIn||this.zoomUpdateOptions(s.minX,s.maxX)}},{key:\"handleZoomOut\",value:function(){var t=this.w;if(t.globals.isRangeBar&&(this.minX=t.globals.minY,this.maxX=t.globals.maxY),!(\"datetime\"===t.config.xaxis.type&&new Date(this.minX).getUTCFullYear()<1e3)){var e=(this.minX+this.maxX)/2,i=this.minX-(e-this.minX),a=this.maxX-(e-this.maxX),s=this._getNewMinXMaxX(i,a);t.globals.disableZoomOut||this.zoomUpdateOptions(s.minX,s.maxX)}}},{key:\"_getNewMinXMaxX\",value:function(t,e){var i=this.w.config.xaxis.convertedCatToNumeric;return{minX:i?Math.floor(t):t,maxX:i?Math.floor(e):e}}},{key:\"zoomUpdateOptions\",value:function(t,e){var i=this.w;if(void 0!==t||void 0!==e){if(!(i.config.xaxis.convertedCatToNumeric&&(t<1&&(t=1,e=i.globals.dataPoints),e-t<2))){var a={min:t,max:e},s=this.getBeforeZoomRange(a);s&&(a=s.xaxis);var r={xaxis:a},o=n.clone(i.globals.initialConfig.yaxis);i.config.chart.zoom.autoScaleYaxis&&(o=new Lt(this.ctx).autoScaleY(this.ctx,o,{xaxis:a})),i.config.chart.group||(r.yaxis=o),this.w.globals.zoomed=!0,this.ctx.updateHelpers._updateOptions(r,!1,this.w.config.chart.animations.dynamicAnimation.enabled),this.zoomCallback(a,o)}}else this.handleZoomReset()}},{key:\"zoomCallback\",value:function(t,e){\"function\"==typeof this.ev.zoomed&&this.ev.zoomed(this.ctx,{xaxis:t,yaxis:e})}},{key:\"getBeforeZoomRange\",value:function(t,e){var i=null;return\"function\"==typeof this.ev.beforeZoom&&(i=this.ev.beforeZoom(this,{xaxis:t,yaxis:e})),i}},{key:\"toggleMenu\",value:function(){var t=this;window.setTimeout((function(){t.elMenu.classList.contains(\"apexcharts-menu-open\")?t.elMenu.classList.remove(\"apexcharts-menu-open\"):t.elMenu.classList.add(\"apexcharts-menu-open\")}),0)}},{key:\"handleDownload\",value:function(t){var e=this.w,i=new yt(this.ctx);switch(t){case\"svg\":i.exportToSVG(this.ctx);break;case\"png\":i.exportToPng(this.ctx);break;case\"csv\":i.exportToCSV({series:e.config.series,columnDelimiter:e.config.chart.toolbar.export.csv.columnDelimiter})}}},{key:\"handleZoomReset\",value:function(t){this.ctx.getSyncedCharts().forEach((function(t){var e=t.w;if(e.globals.lastXAxis.min=void 0,e.globals.lastXAxis.max=void 0,t.updateHelpers.revertDefaultAxisMinMax(),\"function\"==typeof e.config.chart.events.beforeResetZoom){var i=e.config.chart.events.beforeResetZoom(t,e);i&&t.updateHelpers.revertDefaultAxisMinMax(i)}\"function\"==typeof e.config.chart.events.zoomed&&t.ctx.toolbar.zoomCallback({min:e.config.xaxis.min,max:e.config.xaxis.max}),e.globals.zoomed=!1;var a=t.ctx.series.emptyCollapsedSeries(n.clone(e.globals.initialSeries));t.updateHelpers._updateSeries(a,e.config.chart.animations.dynamicAnimation.enabled)}))}},{key:\"destroy\",value:function(){this.elZoom=null,this.elZoomIn=null,this.elZoomOut=null,this.elPan=null,this.elSelection=null,this.elZoomReset=null,this.elMenuIcon=null}}])&&Ae(e.prototype,i),t}();function Ce(t){return Ce=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},Ce(t)}function Pe(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function Le(t,e){return Le=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},Le(t,e)}function Oe(t,e){if(e&&(\"object\"===Ce(e)||\"function\"==typeof e))return e;if(void 0!==e)throw new TypeError(\"Derived constructors may only return object or undefined\");return function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t)}function Te(t){return Te=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},Te(t)}var Ee=function(t){!function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&Le(t,e)}(o,t);var e,i,a,s,r=(a=o,s=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}(),function(){var t,e=Te(a);if(s){var i=Te(this).constructor;t=Reflect.construct(e,arguments,i)}else t=e.apply(this,arguments);return Oe(this,t)});function o(t){var e;return function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,o),(e=r.call(this,t)).ctx=t,e.w=t.w,e.dragged=!1,e.graphics=new b(e.ctx),e.eventList=[\"mousedown\",\"mouseleave\",\"mousemove\",\"touchstart\",\"touchmove\",\"mouseup\",\"touchend\"],e.clientX=0,e.clientY=0,e.startX=0,e.endX=0,e.dragX=0,e.startY=0,e.endY=0,e.dragY=0,e.moveDirection=\"none\",e}return e=o,(i=[{key:\"init\",value:function(t){var e=this,i=t.xyRatios,a=this.w,s=this;this.xyRatios=i,this.zoomRect=this.graphics.drawRect(0,0,0,0),this.selectionRect=this.graphics.drawRect(0,0,0,0),this.gridRect=a.globals.dom.baseEl.querySelector(\".apexcharts-grid\"),this.zoomRect.node.classList.add(\"apexcharts-zoom-rect\"),this.selectionRect.node.classList.add(\"apexcharts-selection-rect\"),a.globals.dom.elGraphical.add(this.zoomRect),a.globals.dom.elGraphical.add(this.selectionRect),\"x\"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,minY:0,maxX:a.globals.gridWidth,maxY:a.globals.gridHeight}).on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")):\"y\"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,maxX:a.globals.gridWidth}).on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")):this.slDraggableRect=this.selectionRect.draggable().on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")),this.preselectedSelection(),this.hoverArea=a.globals.dom.baseEl.querySelector(\"\".concat(a.globals.chartClass,\" .apexcharts-svg\")),this.hoverArea.classList.add(\"apexcharts-zoomable\"),this.eventList.forEach((function(t){e.hoverArea.addEventListener(t,s.svgMouseEvents.bind(s,i),{capture:!1,passive:!0})}))}},{key:\"destroy\",value:function(){this.slDraggableRect&&(this.slDraggableRect.draggable(!1),this.slDraggableRect.off(),this.selectionRect.off()),this.selectionRect=null,this.zoomRect=null,this.gridRect=null}},{key:\"svgMouseEvents\",value:function(t,e){var i=this.w,a=this,s=this.ctx.toolbar,r=i.globals.zoomEnabled?i.config.chart.zoom.type:i.config.chart.selection.type,n=i.config.chart.toolbar.autoSelected;if(e.shiftKey?(this.shiftWasPressed=!0,s.enableZoomPanFromToolbar(\"pan\"===n?\"zoom\":\"pan\")):this.shiftWasPressed&&(s.enableZoomPanFromToolbar(n),this.shiftWasPressed=!1),e.target){var o,l=e.target.classList;if(e.target.parentNode&&null!==e.target.parentNode&&(o=e.target.parentNode.classList),!(l.contains(\"apexcharts-selection-rect\")||l.contains(\"apexcharts-legend-marker\")||l.contains(\"apexcharts-legend-text\")||o&&o.contains(\"apexcharts-toolbar\"))){if(a.clientX=\"touchmove\"===e.type||\"touchstart\"===e.type?e.touches[0].clientX:\"touchend\"===e.type?e.changedTouches[0].clientX:e.clientX,a.clientY=\"touchmove\"===e.type||\"touchstart\"===e.type?e.touches[0].clientY:\"touchend\"===e.type?e.changedTouches[0].clientY:e.clientY,\"mousedown\"===e.type&&1===e.which){var c=a.gridRect.getBoundingClientRect();a.startX=a.clientX-c.left,a.startY=a.clientY-c.top,a.dragged=!1,a.w.globals.mousedown=!0}if((\"mousemove\"===e.type&&1===e.which||\"touchmove\"===e.type)&&(a.dragged=!0,i.globals.panEnabled?(i.globals.selection=null,a.w.globals.mousedown&&a.panDragging({context:a,zoomtype:r,xyRatios:t})):(a.w.globals.mousedown&&i.globals.zoomEnabled||a.w.globals.mousedown&&i.globals.selectionEnabled)&&(a.selection=a.selectionDrawing({context:a,zoomtype:r}))),\"mouseup\"===e.type||\"touchend\"===e.type||\"mouseleave\"===e.type){var h=a.gridRect.getBoundingClientRect();a.w.globals.mousedown&&(a.endX=a.clientX-h.left,a.endY=a.clientY-h.top,a.dragX=Math.abs(a.endX-a.startX),a.dragY=Math.abs(a.endY-a.startY),(i.globals.zoomEnabled||i.globals.selectionEnabled)&&a.selectionDrawn({context:a,zoomtype:r}),i.globals.panEnabled&&i.config.xaxis.convertedCatToNumeric&&a.delayedPanScrolled()),i.globals.zoomEnabled&&a.hideSelectionRect(this.selectionRect),a.dragged=!1,a.w.globals.mousedown=!1}this.makeSelectionRectDraggable()}}}},{key:\"makeSelectionRectDraggable\",value:function(){var t=this.w;if(this.selectionRect){var e=this.selectionRect.node.getBoundingClientRect();e.width>0&&e.height>0&&this.slDraggableRect.selectize({points:\"l, r\",pointSize:8,pointType:\"rect\"}).resize({constraint:{minX:0,minY:0,maxX:t.globals.gridWidth,maxY:t.globals.gridHeight}}).on(\"resizing\",this.selectionDragging.bind(this,\"resizing\"))}}},{key:\"preselectedSelection\",value:function(){var t=this.w,e=this.xyRatios;if(!t.globals.zoomEnabled)if(void 0!==t.globals.selection&&null!==t.globals.selection)this.drawSelectionRect(t.globals.selection);else if(void 0!==t.config.chart.selection.xaxis.min&&void 0!==t.config.chart.selection.xaxis.max){var i=(t.config.chart.selection.xaxis.min-t.globals.minX)/e.xRatio,a={x:i,y:0,width:t.globals.gridWidth-(t.globals.maxX-t.config.chart.selection.xaxis.max)/e.xRatio-i,height:t.globals.gridHeight,translateX:0,translateY:0,selectionEnabled:!0};this.drawSelectionRect(a),this.makeSelectionRectDraggable(),\"function\"==typeof t.config.chart.events.selection&&t.config.chart.events.selection(this.ctx,{xaxis:{min:t.config.chart.selection.xaxis.min,max:t.config.chart.selection.xaxis.max},yaxis:{}})}}},{key:\"drawSelectionRect\",value:function(t){var e=t.x,i=t.y,a=t.width,s=t.height,r=t.translateX,n=void 0===r?0:r,o=t.translateY,l=void 0===o?0:o,c=this.w,h=this.zoomRect,d=this.selectionRect;if(this.dragged||null!==c.globals.selection){var u={transform:\"translate(\"+n+\", \"+l+\")\"};c.globals.zoomEnabled&&this.dragged&&(a<0&&(a=1),h.attr({x:e,y:i,width:a,height:s,fill:c.config.chart.zoom.zoomedArea.fill.color,\"fill-opacity\":c.config.chart.zoom.zoomedArea.fill.opacity,stroke:c.config.chart.zoom.zoomedArea.stroke.color,\"stroke-width\":c.config.chart.zoom.zoomedArea.stroke.width,\"stroke-opacity\":c.config.chart.zoom.zoomedArea.stroke.opacity}),b.setAttrs(h.node,u)),c.globals.selectionEnabled&&(d.attr({x:e,y:i,width:a>0?a:0,height:s>0?s:0,fill:c.config.chart.selection.fill.color,\"fill-opacity\":c.config.chart.selection.fill.opacity,stroke:c.config.chart.selection.stroke.color,\"stroke-width\":c.config.chart.selection.stroke.width,\"stroke-dasharray\":c.config.chart.selection.stroke.dashArray,\"stroke-opacity\":c.config.chart.selection.stroke.opacity}),b.setAttrs(d.node,u))}}},{key:\"hideSelectionRect\",value:function(t){t&&t.attr({x:0,y:0,width:0,height:0})}},{key:\"selectionDrawing\",value:function(t){var e,i=t.context,a=t.zoomtype,s=this.w,r=i,n=this.gridRect.getBoundingClientRect(),o=r.startX-1,l=r.startY,c=!1,h=!1,d=r.clientX-n.left-o,u=r.clientY-n.top-l;return Math.abs(d+o)>s.globals.gridWidth?d=s.globals.gridWidth-o:r.clientX-n.left<0&&(d=o),o>r.clientX-n.left&&(c=!0,d=Math.abs(d)),l>r.clientY-n.top&&(h=!0,u=Math.abs(u)),e=\"x\"===a?{x:c?o-d:o,y:0,width:d,height:s.globals.gridHeight}:\"y\"===a?{x:0,y:h?l-u:l,width:s.globals.gridWidth,height:u}:{x:c?o-d:o,y:h?l-u:l,width:d,height:u},r.drawSelectionRect(e),r.selectionDragging(\"resizing\"),e}},{key:\"selectionDragging\",value:function(t,e){var i=this,a=this.w,s=this.xyRatios,r=this.selectionRect,n=0;\"resizing\"===t&&(n=30);var o=function(t){return parseFloat(r.node.getAttribute(t))},l={x:o(\"x\"),y:o(\"y\"),width:o(\"width\"),height:o(\"height\")};a.globals.selection=l,\"function\"==typeof a.config.chart.events.selection&&a.globals.selectionEnabled&&(clearTimeout(this.w.globals.selectionResizeTimer),this.w.globals.selectionResizeTimer=window.setTimeout((function(){var t=i.gridRect.getBoundingClientRect(),e=r.node.getBoundingClientRect(),n={xaxis:{min:a.globals.xAxisScale.niceMin+(e.left-t.left)*s.xRatio,max:a.globals.xAxisScale.niceMin+(e.right-t.left)*s.xRatio},yaxis:{min:a.globals.yAxisScale[0].niceMin+(t.bottom-e.bottom)*s.yRatio[0],max:a.globals.yAxisScale[0].niceMax-(e.top-t.top)*s.yRatio[0]}};a.config.chart.events.selection(i.ctx,n),a.config.chart.brush.enabled&&void 0!==a.config.chart.events.brushScrolled&&a.config.chart.events.brushScrolled(i.ctx,n)}),n))}},{key:\"selectionDrawn\",value:function(t){var e=t.context,i=t.zoomtype,a=this.w,s=e,r=this.xyRatios,o=this.ctx.toolbar;if(s.startX>s.endX){var l=s.startX;s.startX=s.endX,s.endX=l}if(s.startY>s.endY){var c=s.startY;s.startY=s.endY,s.endY=c}var h=void 0,d=void 0;a.globals.isRangeBar?(h=a.globals.yAxisScale[0].niceMin+s.startX*r.invertedYRatio,d=a.globals.yAxisScale[0].niceMin+s.endX*r.invertedYRatio):(h=a.globals.xAxisScale.niceMin+s.startX*r.xRatio,d=a.globals.xAxisScale.niceMin+s.endX*r.xRatio);var u=[],g=[];if(a.config.yaxis.forEach((function(t,e){u.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.startY),g.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.endY)})),s.dragged&&(s.dragX>10||s.dragY>10)&&h!==d)if(a.globals.zoomEnabled){var f=n.clone(a.globals.initialConfig.yaxis),p=n.clone(a.globals.initialConfig.xaxis);if(a.globals.zoomed=!0,a.config.xaxis.convertedCatToNumeric&&(h=Math.floor(h),d=Math.floor(d),h<1&&(h=1,d=a.globals.dataPoints),d-h<2&&(d=h+1)),\"xy\"!==i&&\"x\"!==i||(p={min:h,max:d}),\"xy\"!==i&&\"y\"!==i||f.forEach((function(t,e){f[e].min=g[e],f[e].max=u[e]})),a.config.chart.zoom.autoScaleYaxis){var x=new Lt(s.ctx);f=x.autoScaleY(s.ctx,f,{xaxis:p})}if(o){var b=o.getBeforeZoomRange(p,f);b&&(p=b.xaxis?b.xaxis:p,f=b.yaxis?b.yaxis:f)}var v={xaxis:p};a.config.chart.group||(v.yaxis=f),s.ctx.updateHelpers._updateOptions(v,!1,s.w.config.chart.animations.dynamicAnimation.enabled),\"function\"==typeof a.config.chart.events.zoomed&&o.zoomCallback(p,f)}else if(a.globals.selectionEnabled){var m,y=null;m={min:h,max:d},\"xy\"!==i&&\"y\"!==i||(y=n.clone(a.config.yaxis)).forEach((function(t,e){y[e].min=g[e],y[e].max=u[e]})),a.globals.selection=s.selection,\"function\"==typeof a.config.chart.events.selection&&a.config.chart.events.selection(s.ctx,{xaxis:m,yaxis:y})}}},{key:\"panDragging\",value:function(t){var e=t.context,i=this.w,a=e;if(void 0!==i.globals.lastClientPosition.x){var s=i.globals.lastClientPosition.x-a.clientX,r=i.globals.lastClientPosition.y-a.clientY;Math.abs(s)>Math.abs(r)&&s>0?this.moveDirection=\"left\":Math.abs(s)>Math.abs(r)&&s<0?this.moveDirection=\"right\":Math.abs(r)>Math.abs(s)&&r>0?this.moveDirection=\"up\":Math.abs(r)>Math.abs(s)&&r<0&&(this.moveDirection=\"down\")}i.globals.lastClientPosition={x:a.clientX,y:a.clientY};var n=i.globals.isRangeBar?i.globals.minY:i.globals.minX,o=i.globals.isRangeBar?i.globals.maxY:i.globals.maxX;i.config.xaxis.convertedCatToNumeric||a.panScrolled(n,o)}},{key:\"delayedPanScrolled\",value:function(){var t=this.w,e=t.globals.minX,i=t.globals.maxX,a=(t.globals.maxX-t.globals.minX)/2;\"left\"===this.moveDirection?(e=t.globals.minX+a,i=t.globals.maxX+a):\"right\"===this.moveDirection&&(e=t.globals.minX-a,i=t.globals.maxX-a),e=Math.floor(e),i=Math.floor(i),this.updateScrolledChart({xaxis:{min:e,max:i}},e,i)}},{key:\"panScrolled\",value:function(t,e){var i=this.w,a=this.xyRatios,s=n.clone(i.globals.initialConfig.yaxis),r=a.xRatio,o=i.globals.minX,l=i.globals.maxX;i.globals.isRangeBar&&(r=a.invertedYRatio,o=i.globals.minY,l=i.globals.maxY),\"left\"===this.moveDirection?(t=o+i.globals.gridWidth/15*r,e=l+i.globals.gridWidth/15*r):\"right\"===this.moveDirection&&(t=o-i.globals.gridWidth/15*r,e=l-i.globals.gridWidth/15*r),i.globals.isRangeBar||(t<i.globals.initialMinX||e>i.globals.initialMaxX)&&(t=o,e=l);var c={min:t,max:e};i.config.chart.zoom.autoScaleYaxis&&(s=new Lt(this.ctx).autoScaleY(this.ctx,s,{xaxis:c}));var h={xaxis:{min:t,max:e}};i.config.chart.group||(h.yaxis=s),this.updateScrolledChart(h,t,e)}},{key:\"updateScrolledChart\",value:function(t,e,i){var a=this.w;this.ctx.updateHelpers._updateOptions(t,!1,!1),\"function\"==typeof a.config.chart.events.scrolled&&a.config.chart.events.scrolled(this.ctx,{xaxis:{min:e,max:i}})}}])&&Pe(e.prototype,i),o}(Se);function Ie(t){return function(t){if(Array.isArray(t))return Me(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||function(t,e){if(t){if(\"string\"==typeof t)return Me(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Me(t,e):void 0}}(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function Me(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function ze(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Xe=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.ttCtx=e,this.ctx=e.ctx}var e,i;return e=t,(i=[{key:\"getNearestValues\",value:function(t){var e=t.hoverArea,i=t.elGrid,a=t.clientX,s=t.clientY,r=this.w,o=i.getBoundingClientRect(),l=o.width,c=o.height,h=l/(r.globals.dataPoints-1),d=c/r.globals.dataPoints,u=this.hasBars();!r.globals.comboCharts&&!u||r.config.xaxis.convertedCatToNumeric||(h=l/r.globals.dataPoints);var g=a-o.left-r.globals.barPadForNumericAxis,f=s-o.top;g<0||f<0||g>l||f>c?(e.classList.remove(\"hovering-zoom\"),e.classList.remove(\"hovering-pan\")):r.globals.zoomEnabled?(e.classList.remove(\"hovering-pan\"),e.classList.add(\"hovering-zoom\")):r.globals.panEnabled&&(e.classList.remove(\"hovering-zoom\"),e.classList.add(\"hovering-pan\"));var p=Math.round(g/h),x=Math.floor(f/d);u&&!r.config.xaxis.convertedCatToNumeric&&(p=Math.ceil(g/h),p-=1);var b=null,v=null,m=[],y=[];if(r.globals.seriesXvalues.forEach((function(t){m.push([t[0]+1e-6].concat(t))})),r.globals.seriesYvalues.forEach((function(t){y.push([t[0]+1e-6].concat(t))})),m=m.map((function(t){return t.filter((function(t){return n.isNumber(t)}))})),y=y.map((function(t){return t.filter((function(t){return n.isNumber(t)}))})),r.globals.isXNumeric){var w=this.ttCtx.getElGrid().getBoundingClientRect(),k=g*(w.width/l),A=f*(w.height/c);b=(v=this.closestInMultiArray(k,A,m,y)).index,p=v.j,null!==b&&(m=r.globals.seriesXvalues[b],p=(v=this.closestInArray(k,m)).index)}return r.globals.capturedSeriesIndex=null===b?-1:b,(!p||p<1)&&(p=0),r.globals.isBarHorizontal?r.globals.capturedDataPointIndex=x:r.globals.capturedDataPointIndex=p,{capturedSeries:b,j:r.globals.isBarHorizontal?x:p,hoverX:g,hoverY:f}}},{key:\"closestInMultiArray\",value:function(t,e,i,a){var s=this.w,r=0,n=null,o=-1;s.globals.series.length>1?r=this.getFirstActiveXArray(i):n=0;var l=i[r][0],c=Math.abs(t-l);if(i.forEach((function(e){e.forEach((function(e,i){var a=Math.abs(t-e);a<c&&(c=a,o=i)}))})),-1!==o){var h=a[r][o],d=Math.abs(e-h);n=r,a.forEach((function(t,i){var a=Math.abs(e-t[o]);a<d&&(d=a,n=i)}))}return{index:n,j:o}}},{key:\"getFirstActiveXArray\",value:function(t){for(var e=this.w,i=0,a=t.map((function(t,e){return t.length>0?e:-1})),s=0;s<a.length;s++)if(-1!==a[s]&&-1===e.globals.collapsedSeriesIndices.indexOf(s)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(s)){i=a[s];break}return i}},{key:\"closestInArray\",value:function(t,e){for(var i=e[0],a=null,s=Math.abs(t-i),r=0;r<e.length;r++){var n=Math.abs(t-e[r]);n<s&&(s=n,a=r)}return{index:a}}},{key:\"isXoverlap\",value:function(t){var e=[],i=this.w.globals.seriesX.filter((function(t){return void 0!==t[0]}));if(i.length>0)for(var a=0;a<i.length-1;a++)void 0!==i[a][t]&&void 0!==i[a+1][t]&&i[a][t]!==i[a+1][t]&&e.push(\"unEqual\");return 0===e.length}},{key:\"isInitialSeriesSameLen\",value:function(){for(var t=!0,e=this.w.globals.initialSeries,i=0;i<e.length-1;i++)if(e[i].data.length!==e[i+1].data.length){t=!1;break}return t}},{key:\"getBarsHeight\",value:function(t){return Ie(t).reduce((function(t,e){return t+e.getBBox().height}),0)}},{key:\"getElMarkers\",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(\" .apexcharts-series-markers\")}},{key:\"getAllMarkers\",value:function(){var t=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series-markers-wrap\");(t=Ie(t)).sort((function(t,e){var i=Number(t.getAttribute(\"data:realIndex\")),a=Number(e.getAttribute(\"data:realIndex\"));return a<i?1:a>i?-1:0}));var e=[];return t.forEach((function(t){e.push(t.querySelector(\".apexcharts-marker\"))})),e}},{key:\"hasMarkers\",value:function(){return this.getElMarkers().length>0}},{key:\"getElBars\",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-bar-series,  .apexcharts-candlestick-series, .apexcharts-boxPlot-series, .apexcharts-rangebar-series\")}},{key:\"hasBars\",value:function(){return this.getElBars().length>0}},{key:\"getHoverMarkerSize\",value:function(t){var e=this.w,i=e.config.markers.hover.size;return void 0===i&&(i=e.globals.markers.size[t]+e.config.markers.hover.sizeOffset),i}},{key:\"toggleAllTooltipSeriesGroups\",value:function(t){var e=this.w,i=this.ttCtx;0===i.allTooltipSeriesGroups.length&&(i.allTooltipSeriesGroups=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-tooltip-series-group\"));for(var a=i.allTooltipSeriesGroups,s=0;s<a.length;s++)\"enable\"===t?(a[s].classList.add(\"apexcharts-active\"),a[s].style.display=e.config.tooltip.items.display):(a[s].classList.remove(\"apexcharts-active\"),a[s].style.display=\"none\")}}])&&ze(e.prototype,i),t}();function Ye(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function De(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?Ye(Object(i),!0).forEach((function(e){Re(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):Ye(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function Re(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function Fe(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var He=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.ctx=e.ctx,this.ttCtx=e,this.tooltipUtil=new Xe(e)}var e,i;return e=t,(i=[{key:\"drawSeriesTexts\",value:function(t){var e=t.shared,i=void 0===e||e,a=t.ttItems,s=t.i,r=void 0===s?0:s,n=t.j,o=void 0===n?null:n,l=t.y1,c=t.y2,h=t.e,d=this.w;void 0!==d.config.tooltip.custom?this.handleCustomTooltip({i:r,j:o,y1:l,y2:c,w:d}):this.toggleActiveInactiveSeries(i);var u=this.getValuesToPrint({i:r,j:o});this.printLabels({i:r,j:o,values:u,ttItems:a,shared:i,e:h});var g=this.ttCtx.getElTooltip();this.ttCtx.tooltipRect.ttWidth=g.getBoundingClientRect().width,this.ttCtx.tooltipRect.ttHeight=g.getBoundingClientRect().height}},{key:\"printLabels\",value:function(t){var e,i=this,a=t.i,s=t.j,r=t.values,n=t.ttItems,o=t.shared,l=t.e,c=this.w,h=[],d=function(t){return c.globals.seriesGoals[t]&&c.globals.seriesGoals[t][s]&&Array.isArray(c.globals.seriesGoals[t][s])},u=r.xVal,g=r.zVal,f=r.xAxisTTVal,p=\"\",x=c.globals.colors[a];null!==s&&c.config.plotOptions.bar.distributed&&(x=c.globals.colors[s]);for(var b=function(t,r){var b=i.getFormatters(a);p=i.getSeriesName({fn:b.yLbTitleFormatter,index:a,seriesIndex:a,j:s}),\"treemap\"===c.config.chart.type&&(p=b.yLbTitleFormatter(String(c.config.series[a].data[s].x),{series:c.globals.series,seriesIndex:a,dataPointIndex:s,w:c}));var v=c.config.tooltip.inverseOrder?r:t;if(c.globals.axisCharts){var m=function(t){var e,i,a=\"\";return c.globals.isRangeData&&(a+=b.yLbFormatter(null===(e=c.globals.seriesRangeStart)||void 0===e||null===(i=e[t])||void 0===i?void 0:i[s],{series:c.globals.seriesRangeStart,seriesIndex:t,dataPointIndex:s,w:c})+\" - \"),a+b.yLbFormatter(c.globals.series[t][s],{series:c.globals.series,seriesIndex:t,dataPointIndex:s,w:c})};if(o)b=i.getFormatters(v),p=i.getSeriesName({fn:b.yLbTitleFormatter,index:v,seriesIndex:a,j:s}),x=c.globals.colors[v],e=m(v),d(v)&&(h=c.globals.seriesGoals[v][s].map((function(t){return{attrs:t,val:b.yLbFormatter(t.value,{seriesIndex:v,dataPointIndex:s,w:c})}})));else{var y,w=null==l||null===(y=l.target)||void 0===y?void 0:y.getAttribute(\"fill\");w&&(x=-1!==w.indexOf(\"url\")?document.querySelector(w.substr(4).slice(0,-1)).childNodes[0].getAttribute(\"stroke\"):w),e=m(a),d(a)&&Array.isArray(c.globals.seriesGoals[a][s])&&(h=c.globals.seriesGoals[a][s].map((function(t){return{attrs:t,val:b.yLbFormatter(t.value,{seriesIndex:a,dataPointIndex:s,w:c})}})))}}null===s&&(e=b.yLbFormatter(c.globals.series[a],De(De({},c),{},{seriesIndex:a,dataPointIndex:a}))),i.DOMHandling({i:a,t:v,j:s,ttItems:n,values:{val:e,goalVals:h,xVal:u,xAxisTTVal:f,zVal:g},seriesName:p,shared:o,pColor:x})},v=0,m=c.globals.series.length-1;v<c.globals.series.length;v++,m--)b(v,m)}},{key:\"getFormatters\",value:function(t){var e,i=this.w,a=i.globals.yLabelFormatters[t];return void 0!==i.globals.ttVal?Array.isArray(i.globals.ttVal)?(a=i.globals.ttVal[t]&&i.globals.ttVal[t].formatter,e=i.globals.ttVal[t]&&i.globals.ttVal[t].title&&i.globals.ttVal[t].title.formatter):(a=i.globals.ttVal.formatter,\"function\"==typeof i.globals.ttVal.title.formatter&&(e=i.globals.ttVal.title.formatter)):e=i.config.tooltip.y.title.formatter,\"function\"!=typeof a&&(a=i.globals.yLabelFormatters[0]?i.globals.yLabelFormatters[0]:function(t){return t}),\"function\"!=typeof e&&(e=function(t){return t}),{yLbFormatter:a,yLbTitleFormatter:e}}},{key:\"getSeriesName\",value:function(t){var e=t.fn,i=t.index,a=t.seriesIndex,s=t.j,r=this.w;return e(String(r.globals.seriesNames[i]),{series:r.globals.series,seriesIndex:a,dataPointIndex:s,w:r})}},{key:\"DOMHandling\",value:function(t){t.i;var e=t.t,i=t.j,a=t.ttItems,s=t.values,r=t.seriesName,n=t.shared,o=t.pColor,l=this.w,c=this.ttCtx,h=s.val,d=s.goalVals,u=s.xVal,g=s.xAxisTTVal,f=s.zVal,p=null;p=a[e].children,l.config.tooltip.fillSeriesColor&&(a[e].style.backgroundColor=o,p[0].style.display=\"none\"),c.showTooltipTitle&&(null===c.tooltipTitle&&(c.tooltipTitle=l.globals.dom.baseEl.querySelector(\".apexcharts-tooltip-title\")),c.tooltipTitle.innerHTML=u),c.isXAxisTooltipEnabled&&(c.xaxisTooltipText.innerHTML=\"\"!==g?g:u);var x=a[e].querySelector(\".apexcharts-tooltip-text-y-label\");x&&(x.innerHTML=r||\"\");var b=a[e].querySelector(\".apexcharts-tooltip-text-y-value\");b&&(b.innerHTML=void 0!==h?h:\"\"),p[0]&&p[0].classList.contains(\"apexcharts-tooltip-marker\")&&(l.config.tooltip.marker.fillColors&&Array.isArray(l.config.tooltip.marker.fillColors)&&(o=l.config.tooltip.marker.fillColors[e]),p[0].style.backgroundColor=o),l.config.tooltip.marker.show||(p[0].style.display=\"none\");var v=a[e].querySelector(\".apexcharts-tooltip-text-goals-label\"),m=a[e].querySelector(\".apexcharts-tooltip-text-goals-value\");if(d.length&&l.globals.seriesGoals[e]){var y=function(){var t=\"<div >\",e=\"<div>\";d.forEach((function(i,a){t+=' <div style=\"display: flex\"><span class=\"apexcharts-tooltip-marker\" style=\"background-color: '.concat(i.attrs.strokeColor,'; height: 3px; border-radius: 0; top: 5px;\"></span> ').concat(i.attrs.name,\"</div>\"),e+=\"<div>\".concat(i.val,\"</div>\")})),v.innerHTML=t+\"</div>\",m.innerHTML=e+\"</div>\"};n?l.globals.seriesGoals[e][i]&&Array.isArray(l.globals.seriesGoals[e][i])?y():(v.innerHTML=\"\",m.innerHTML=\"\"):y()}else v.innerHTML=\"\",m.innerHTML=\"\";null!==f&&(a[e].querySelector(\".apexcharts-tooltip-text-z-label\").innerHTML=l.config.tooltip.z.title,a[e].querySelector(\".apexcharts-tooltip-text-z-value\").innerHTML=void 0!==f?f:\"\"),n&&p[0]&&(null==h||l.globals.ancillaryCollapsedSeriesIndices.indexOf(e)>-1||l.globals.collapsedSeriesIndices.indexOf(e)>-1?p[0].parentNode.style.display=\"none\":p[0].parentNode.style.display=l.config.tooltip.items.display)}},{key:\"toggleActiveInactiveSeries\",value:function(t){var e=this.w;if(t)this.tooltipUtil.toggleAllTooltipSeriesGroups(\"enable\");else{this.tooltipUtil.toggleAllTooltipSeriesGroups(\"disable\");var i=e.globals.dom.baseEl.querySelector(\".apexcharts-tooltip-series-group\");i&&(i.classList.add(\"apexcharts-active\"),i.style.display=e.config.tooltip.items.display)}}},{key:\"getValuesToPrint\",value:function(t){var e=t.i,i=t.j,a=this.w,s=this.ctx.series.filteredSeriesX(),r=\"\",n=\"\",o=null,l=null,c={series:a.globals.series,seriesIndex:e,dataPointIndex:i,w:a},h=a.globals.ttZFormatter;null===i?l=a.globals.series[e]:a.globals.isXNumeric&&\"treemap\"!==a.config.chart.type?(r=s[e][i],0===s[e].length&&(r=s[this.tooltipUtil.getFirstActiveXArray(s)][i])):r=void 0!==a.globals.labels[i]?a.globals.labels[i]:\"\";var d=r;return r=a.globals.isXNumeric&&\"datetime\"===a.config.xaxis.type?new H(this.ctx).xLabelFormat(a.globals.ttKeyFormatter,d,d,{i:void 0,dateFormatter:new R(this.ctx).formatDate,w:this.w}):a.globals.isBarHorizontal?a.globals.yLabelFormatters[0](d,c):a.globals.xLabelFormatter(d,c),void 0!==a.config.tooltip.x.formatter&&(r=a.globals.ttKeyFormatter(d,c)),a.globals.seriesZ.length>0&&a.globals.seriesZ[e].length>0&&(o=h(a.globals.seriesZ[e][i],a)),n=\"function\"==typeof a.config.xaxis.tooltip.formatter?a.globals.xaxisTooltipFormatter(d,c):r,{val:Array.isArray(l)?l.join(\" \"):l,xVal:Array.isArray(r)?r.join(\" \"):r,xAxisTTVal:Array.isArray(n)?n.join(\" \"):n,zVal:o}}},{key:\"handleCustomTooltip\",value:function(t){var e=t.i,i=t.j,a=t.y1,s=t.y2,r=t.w,n=this.ttCtx.getElTooltip(),o=r.config.tooltip.custom;Array.isArray(o)&&o[e]&&(o=o[e]),n.innerHTML=o({ctx:this.ctx,series:r.globals.series,seriesIndex:e,dataPointIndex:i,y1:a,y2:s,w:r})}}])&&Fe(e.prototype,i),t}();function Ne(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var We=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ttCtx=e,this.ctx=e.ctx,this.w=e.w}var e,i;return e=t,i=[{key:\"moveXCrosshairs\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.ttCtx,a=this.w,s=i.getElXCrosshairs(),r=t-i.xcrosshairsWidth/2,n=a.globals.labels.slice().length;if(null!==e&&(r=a.globals.gridWidth/n*e),null===s||a.globals.isBarHorizontal||(s.setAttribute(\"x\",r),s.setAttribute(\"x1\",r),s.setAttribute(\"x2\",r),s.setAttribute(\"y2\",a.globals.gridHeight),s.classList.add(\"apexcharts-active\")),r<0&&(r=0),r>a.globals.gridWidth&&(r=a.globals.gridWidth),i.isXAxisTooltipEnabled){var o=r;\"tickWidth\"!==a.config.xaxis.crosshairs.width&&\"barWidth\"!==a.config.xaxis.crosshairs.width||(o=r+i.xcrosshairsWidth/2),this.moveXAxisTooltip(o)}}},{key:\"moveYCrosshairs\",value:function(t){var e=this.ttCtx;null!==e.ycrosshairs&&b.setAttrs(e.ycrosshairs,{y1:t,y2:t}),null!==e.ycrosshairsHidden&&b.setAttrs(e.ycrosshairsHidden,{y1:t,y2:t})}},{key:\"moveXAxisTooltip\",value:function(t){var e=this.w,i=this.ttCtx;if(null!==i.xaxisTooltip&&0!==i.xcrosshairsWidth){i.xaxisTooltip.classList.add(\"apexcharts-active\");var a,s=i.xaxisOffY+e.config.xaxis.tooltip.offsetY+e.globals.translateY+1+e.config.xaxis.offsetY;t-=i.xaxisTooltip.getBoundingClientRect().width/2,isNaN(t)||(t+=e.globals.translateX,a=new b(this.ctx).getTextRects(i.xaxisTooltipText.innerHTML),i.xaxisTooltipText.style.minWidth=a.width+\"px\",i.xaxisTooltip.style.left=t+\"px\",i.xaxisTooltip.style.top=s+\"px\")}}},{key:\"moveYAxisTooltip\",value:function(t){var e=this.w,i=this.ttCtx;null===i.yaxisTTEls&&(i.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxistooltip\"));var a=parseInt(i.ycrosshairsHidden.getAttribute(\"y1\"),10),s=e.globals.translateY+a,r=i.yaxisTTEls[t].getBoundingClientRect().height,n=e.globals.translateYAxisX[t]-2;e.config.yaxis[t].opposite&&(n-=26),s-=r/2,-1===e.globals.ignoreYAxisIndexes.indexOf(t)?(i.yaxisTTEls[t].classList.add(\"apexcharts-active\"),i.yaxisTTEls[t].style.top=s+\"px\",i.yaxisTTEls[t].style.left=n+e.config.yaxis[t].tooltip.offsetX+\"px\"):i.yaxisTTEls[t].classList.remove(\"apexcharts-active\")}},{key:\"moveTooltip\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=this.ttCtx,r=s.getElTooltip(),n=s.tooltipRect,o=null!==i?parseFloat(i):1,l=parseFloat(t)+o+5,c=parseFloat(e)+o/2;if(l>a.globals.gridWidth/2&&(l=l-n.ttWidth-o-10),l>a.globals.gridWidth-n.ttWidth-10&&(l=a.globals.gridWidth-n.ttWidth),l<-20&&(l=-20),a.config.tooltip.followCursor){var h=s.getElGrid(),d=h.getBoundingClientRect();c=s.e.clientY+a.globals.translateY-d.top-n.ttHeight/2}else a.globals.isBarHorizontal||n.ttHeight/2+c>a.globals.gridHeight&&(c=a.globals.gridHeight-n.ttHeight+a.globals.translateY);isNaN(l)||(l+=a.globals.translateX,r.style.left=l+\"px\",r.style.top=c+\"px\")}},{key:\"moveMarkers\",value:function(t,e){var i=this.w,a=this.ttCtx;if(i.globals.markers.size[t]>0)for(var s=i.globals.dom.baseEl.querySelectorAll(\" .apexcharts-series[data\\\\:realIndex='\".concat(t,\"'] .apexcharts-marker\")),r=0;r<s.length;r++)parseInt(s[r].getAttribute(\"rel\"),10)===e&&(a.marker.resetPointsSize(),a.marker.enlargeCurrentPoint(e,s[r]));else a.marker.resetPointsSize(),this.moveDynamicPointOnHover(e,t)}},{key:\"moveDynamicPointOnHover\",value:function(t,e){var i,a,s=this.w,r=this.ttCtx,n=s.globals.pointsArray,o=r.tooltipUtil.getHoverMarkerSize(e),l=s.config.series[e].type;if(!l||\"column\"!==l&&\"candlestick\"!==l&&\"boxPlot\"!==l){i=n[e][t][0],a=n[e][t][1]?n[e][t][1]:0;var c=s.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(e,\"'] .apexcharts-series-markers circle\"));c&&a<s.globals.gridHeight&&a>0&&(c.setAttribute(\"r\",o),c.setAttribute(\"cx\",i),c.setAttribute(\"cy\",a)),this.moveXCrosshairs(i),r.fixedTooltip||this.moveTooltip(i,a,o)}}},{key:\"moveDynamicPointsOnHover\",value:function(t){var e,i=this.ttCtx,a=i.w,s=0,r=0,n=a.globals.pointsArray;e=new ut(this.ctx).getActiveConfigSeriesIndex(\"asc\",[\"line\",\"area\",\"scatter\",\"bubble\"]);var o=i.tooltipUtil.getHoverMarkerSize(e);n[e]&&(s=n[e][t][0],r=n[e][t][1]);var l=i.tooltipUtil.getAllMarkers();if(null!==l)for(var c=0;c<a.globals.series.length;c++){var h=n[c];if(a.globals.comboCharts&&void 0===h&&l.splice(c,0,null),h&&h.length){var d=n[c][t][1],u=void 0;if(l[c].setAttribute(\"cx\",s),\"rangeArea\"===a.config.chart.type&&!a.globals.comboCharts){var g=t+a.globals.series[c].length;u=n[c][g][1],d-=Math.abs(d-u)/2}null!==d&&!isNaN(d)&&d<a.globals.gridHeight+o&&d+o>0?(l[c]&&l[c].setAttribute(\"r\",o),l[c]&&l[c].setAttribute(\"cy\",d)):l[c]&&l[c].setAttribute(\"r\",0)}}if(this.moveXCrosshairs(s),!i.fixedTooltip){var f=r||a.globals.gridHeight;this.moveTooltip(s,f,o)}}},{key:\"moveStickyTooltipOverBars\",value:function(t){var e=this.w,i=this.ttCtx,a=e.globals.columnSeries?e.globals.columnSeries.length:e.globals.series.length,s=a>=2&&a%2==0?Math.floor(a/2):Math.floor(a/2)+1;e.globals.isBarHorizontal&&(s=new ut(this.ctx).getActiveConfigSeriesIndex(\"desc\")+1);var r=e.globals.dom.baseEl.querySelector(\".apexcharts-bar-series .apexcharts-series[rel='\".concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-candlestick-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-boxPlot-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-rangebar-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"']\")),n=r?parseFloat(r.getAttribute(\"cx\")):0,o=r?parseFloat(r.getAttribute(\"cy\")):0,l=r?parseFloat(r.getAttribute(\"barWidth\")):0,c=i.getElGrid().getBoundingClientRect(),h=r.classList.contains(\"apexcharts-candlestick-area\")||r.classList.contains(\"apexcharts-boxPlot-area\");if(e.globals.isXNumeric?(r&&!h&&(n-=a%2!=0?l/2:0),r&&h&&e.globals.comboCharts&&(n-=l/2)):e.globals.isBarHorizontal||(n=i.xAxisTicksPositions[t-1]+i.dataPointsDividedWidth/2,isNaN(n)&&(n=i.xAxisTicksPositions[t]-i.dataPointsDividedWidth/2)),e.globals.isBarHorizontal?o-=i.tooltipRect.ttHeight:e.config.tooltip.followCursor?o=i.e.clientY-c.top-i.tooltipRect.ttHeight/2:o+i.tooltipRect.ttHeight+15>e.globals.gridHeight&&(o=e.globals.gridHeight),e.globals.isBarHorizontal||this.moveXCrosshairs(n),!i.fixedTooltip){var d=o||e.globals.gridHeight;this.moveTooltip(n,d)}}}],i&&Ne(e.prototype,i),t}();function je(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function Be(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Ge=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.ttCtx=e,this.ctx=e.ctx,this.tooltipPosition=new We(e)}var e,i;return e=t,i=[{key:\"drawDynamicPoints\",value:function(){var t,e=this.w,i=new b(this.ctx),a=new nt(this.ctx),s=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-series\");s=function(t){if(Array.isArray(t))return je(t)}(t=s)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||function(t,e){if(t){if(\"string\"==typeof t)return je(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?je(t,e):void 0}}(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}(),e.config.chart.stacked&&s.sort((function(t,e){return parseFloat(t.getAttribute(\"data:realIndex\"))-parseFloat(e.getAttribute(\"data:realIndex\"))}));for(var r=0;r<s.length;r++){var n=s[r].querySelector(\".apexcharts-series-markers-wrap\");if(null!==n){var o=void 0,l=\"apexcharts-marker w\".concat((Math.random()+1).toString(36).substring(4));\"line\"!==e.config.chart.type&&\"area\"!==e.config.chart.type||e.globals.comboCharts||e.config.tooltip.intersect||(l+=\" no-pointer-events\");var c=a.getMarkerConfig({cssClass:l,seriesIndex:Number(n.getAttribute(\"data:realIndex\"))});(o=i.drawMarker(0,0,c)).node.setAttribute(\"default-marker-size\",0);var h=document.createElementNS(e.globals.SVGNS,\"g\");h.classList.add(\"apexcharts-series-markers\"),h.appendChild(o.node),n.appendChild(h)}}}},{key:\"enlargeCurrentPoint\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w;\"bubble\"!==s.config.chart.type&&this.newPointSize(t,e);var r=e.getAttribute(\"cx\"),n=e.getAttribute(\"cy\");if(null!==i&&null!==a&&(r=i,n=a),this.tooltipPosition.moveXCrosshairs(r),!this.fixedTooltip){if(\"radar\"===s.config.chart.type){var o=this.ttCtx.getElGrid(),l=o.getBoundingClientRect();r=this.ttCtx.e.clientX-l.left}this.tooltipPosition.moveTooltip(r,n,s.config.markers.hover.size)}}},{key:\"enlargePoints\",value:function(t){for(var e=this.w,i=this,a=this.ttCtx,s=t,r=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker\"),n=e.config.markers.hover.size,o=0;o<r.length;o++){var l=r[o].getAttribute(\"rel\"),c=r[o].getAttribute(\"index\");if(void 0===n&&(n=e.globals.markers.size[c]+e.config.markers.hover.sizeOffset),s===parseInt(l,10)){i.newPointSize(s,r[o]);var h=r[o].getAttribute(\"cx\"),d=r[o].getAttribute(\"cy\");i.tooltipPosition.moveXCrosshairs(h),a.fixedTooltip||i.tooltipPosition.moveTooltip(h,d,n)}else i.oldPointSize(r[o])}}},{key:\"newPointSize\",value:function(t,e){var i=this.w,a=i.config.markers.hover.size,s=0===t?e.parentNode.firstChild:e.parentNode.lastChild;if(\"0\"!==s.getAttribute(\"default-marker-size\")){var r=parseInt(s.getAttribute(\"index\"),10);void 0===a&&(a=i.globals.markers.size[r]+i.config.markers.hover.sizeOffset),a<0&&(a=0),s.setAttribute(\"r\",a)}}},{key:\"oldPointSize\",value:function(t){var e=parseFloat(t.getAttribute(\"default-marker-size\"));t.setAttribute(\"r\",e)}},{key:\"resetPointsSize\",value:function(){for(var t=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker\"),e=0;e<t.length;e++){var i=parseFloat(t[e].getAttribute(\"default-marker-size\"));n.isNumber(i)&&i>=0?t[e].setAttribute(\"r\",i):t[e].setAttribute(\"r\",0)}}}],i&&Be(e.prototype,i),t}();function Ve(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const _e=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.ttCtx=e}var e,i;return e=t,(i=[{key:\"getAttr\",value:function(t,e){return parseFloat(t.target.getAttribute(e))}},{key:\"handleHeatTreeTooltip\",value:function(t){var e=t.e,i=t.opt,a=t.x,s=t.y,r=t.type,n=this.ttCtx,o=this.w;if(e.target.classList.contains(\"apexcharts-\".concat(r,\"-rect\"))){var l=this.getAttr(e,\"i\"),c=this.getAttr(e,\"j\"),h=this.getAttr(e,\"cx\"),d=this.getAttr(e,\"cy\"),u=this.getAttr(e,\"width\"),g=this.getAttr(e,\"height\");if(n.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:l,j:c,shared:!1,e}),o.globals.capturedSeriesIndex=l,o.globals.capturedDataPointIndex=c,a=h+n.tooltipRect.ttWidth/2+u,s=d+n.tooltipRect.ttHeight/2-g/2,n.tooltipPosition.moveXCrosshairs(h+u/2),a>o.globals.gridWidth/2&&(a=h-n.tooltipRect.ttWidth/2+u),n.w.config.tooltip.followCursor){var f=o.globals.dom.elWrap.getBoundingClientRect();a=o.globals.clientX-f.left-(a>o.globals.gridWidth/2?n.tooltipRect.ttWidth:0),s=o.globals.clientY-f.top-(s>o.globals.gridHeight/2?n.tooltipRect.ttHeight:0)}}return{x:a,y:s}}},{key:\"handleMarkerTooltip\",value:function(t){var e,i,a=t.e,s=t.opt,r=t.x,o=t.y,l=this.w,c=this.ttCtx;if(a.target.classList.contains(\"apexcharts-marker\")){var h=parseInt(s.paths.getAttribute(\"cx\"),10),d=parseInt(s.paths.getAttribute(\"cy\"),10),u=parseFloat(s.paths.getAttribute(\"val\"));if(i=parseInt(s.paths.getAttribute(\"rel\"),10),e=parseInt(s.paths.parentNode.parentNode.parentNode.getAttribute(\"rel\"),10)-1,c.intersect){var g=n.findAncestor(s.paths,\"apexcharts-series\");g&&(e=parseInt(g.getAttribute(\"data:realIndex\"),10))}if(c.tooltipLabels.drawSeriesTexts({ttItems:s.ttItems,i:e,j:i,shared:!c.showOnIntersect&&l.config.tooltip.shared,e:a}),\"mouseup\"===a.type&&c.markerClick(a,e,i),l.globals.capturedSeriesIndex=e,l.globals.capturedDataPointIndex=i,r=h,o=d+l.globals.translateY-1.4*c.tooltipRect.ttHeight,c.w.config.tooltip.followCursor){var f=c.getElGrid().getBoundingClientRect();o=c.e.clientY+l.globals.translateY-f.top}u<0&&(o=d),c.marker.enlargeCurrentPoint(i,s.paths,r,o)}return{x:r,y:o}}},{key:\"handleBarTooltip\",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,n=this.ttCtx,o=n.getElTooltip(),l=0,c=0,h=0,d=this.getBarTooltipXY({e:a,opt:s});e=d.i;var u=d.barHeight,g=d.j;r.globals.capturedSeriesIndex=e,r.globals.capturedDataPointIndex=g,r.globals.isBarHorizontal&&n.tooltipUtil.hasBars()||!r.config.tooltip.shared?(c=d.x,h=d.y,i=Array.isArray(r.config.stroke.width)?r.config.stroke.width[e]:r.config.stroke.width,l=c):r.globals.comboCharts||r.config.tooltip.shared||(l/=2),isNaN(h)&&(h=r.globals.svgHeight-n.tooltipRect.ttHeight);var f=parseInt(s.paths.parentNode.getAttribute(\"data:realIndex\"),10),p=r.globals.isMultipleYAxis?r.config.yaxis[f]&&r.config.yaxis[f].reversed:r.config.yaxis[0].reversed;if(c+n.tooltipRect.ttWidth>r.globals.gridWidth&&!p?c-=n.tooltipRect.ttWidth:c<0&&(c=0),n.w.config.tooltip.followCursor){var x=n.getElGrid().getBoundingClientRect();h=n.e.clientY-x.top}null===n.tooltip&&(n.tooltip=r.globals.dom.baseEl.querySelector(\".apexcharts-tooltip\")),r.config.tooltip.shared||(r.globals.comboBarCount>0?n.tooltipPosition.moveXCrosshairs(l+i/2):n.tooltipPosition.moveXCrosshairs(l)),!n.fixedTooltip&&(!r.config.tooltip.shared||r.globals.isBarHorizontal&&n.tooltipUtil.hasBars())&&(p&&(c-=n.tooltipRect.ttWidth)<0&&(c=0),!p||r.globals.isBarHorizontal&&n.tooltipUtil.hasBars()||(h=h+u-2*(r.globals.series[e][g]<0?u:0)),h=h+r.globals.translateY-n.tooltipRect.ttHeight/2,o.style.left=c+r.globals.translateX+\"px\",o.style.top=h+\"px\")}},{key:\"getBarTooltipXY\",value:function(t){var e=t.e,i=t.opt,a=this.w,s=null,r=this.ttCtx,n=0,o=0,l=0,c=0,h=0,d=e.target.classList;if(d.contains(\"apexcharts-bar-area\")||d.contains(\"apexcharts-candlestick-area\")||d.contains(\"apexcharts-boxPlot-area\")||d.contains(\"apexcharts-rangebar-area\")){var u=e.target,g=u.getBoundingClientRect(),f=i.elGrid.getBoundingClientRect(),p=g.height;h=g.height;var x=g.width,b=parseInt(u.getAttribute(\"cx\"),10),v=parseInt(u.getAttribute(\"cy\"),10);c=parseFloat(u.getAttribute(\"barWidth\"));var m=\"touchmove\"===e.type?e.touches[0].clientX:e.clientX;s=parseInt(u.getAttribute(\"j\"),10),n=parseInt(u.parentNode.getAttribute(\"rel\"),10)-1;var y=u.getAttribute(\"data-range-y1\"),w=u.getAttribute(\"data-range-y2\");a.globals.comboCharts&&(n=parseInt(u.parentNode.getAttribute(\"data:realIndex\"),10)),r.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:n,j:s,y1:y?parseInt(y,10):null,y2:w?parseInt(w,10):null,shared:!r.showOnIntersect&&a.config.tooltip.shared,e}),a.config.tooltip.followCursor?a.globals.isBarHorizontal?(o=m-f.left+15,l=v-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(o=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=e.clientY-f.top-r.tooltipRect.ttHeight/2-15):a.globals.isBarHorizontal?((o=b)<r.xyRatios.baseLineInvertedY&&(o=b-r.tooltipRect.ttWidth),l=v-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(o=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=v)}return{x:o,y:l,barHeight:h,barWidth:c,i:n,j:s}}}])&&Ve(e.prototype,i),t}();function Ue(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const qe=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.ttCtx=e}var e,i;return e=t,(i=[{key:\"drawXaxisTooltip\",value:function(){var t=this.w,e=this.ttCtx,i=\"bottom\"===t.config.xaxis.position;e.xaxisOffY=i?t.globals.gridHeight+1:-t.globals.xAxisHeight-t.config.xaxis.axisTicks.height+3;var a=i?\"apexcharts-xaxistooltip apexcharts-xaxistooltip-bottom\":\"apexcharts-xaxistooltip apexcharts-xaxistooltip-top\",s=t.globals.dom.elWrap;e.isXAxisTooltipEnabled&&null===t.globals.dom.baseEl.querySelector(\".apexcharts-xaxistooltip\")&&(e.xaxisTooltip=document.createElement(\"div\"),e.xaxisTooltip.setAttribute(\"class\",a+\" apexcharts-theme-\"+t.config.tooltip.theme),s.appendChild(e.xaxisTooltip),e.xaxisTooltipText=document.createElement(\"div\"),e.xaxisTooltipText.classList.add(\"apexcharts-xaxistooltip-text\"),e.xaxisTooltipText.style.fontFamily=t.config.xaxis.tooltip.style.fontFamily||t.config.chart.fontFamily,e.xaxisTooltipText.style.fontSize=t.config.xaxis.tooltip.style.fontSize,e.xaxisTooltip.appendChild(e.xaxisTooltipText))}},{key:\"drawYaxisTooltip\",value:function(){for(var t=this.w,e=this.ttCtx,i=function(i){var a=t.config.yaxis[i].opposite||t.config.yaxis[i].crosshairs.opposite;e.yaxisOffX=a?t.globals.gridWidth+1:1;var s=\"apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i,a?\" apexcharts-yaxistooltip-right\":\" apexcharts-yaxistooltip-left\");t.globals.yAxisSameScaleIndices.map((function(e,a){e.map((function(e,a){a===i&&(s+=t.config.yaxis[a].show?\" \":\" apexcharts-yaxistooltip-hidden\")}))}));var r=t.globals.dom.elWrap;null===t.globals.dom.baseEl.querySelector(\".apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i))&&(e.yaxisTooltip=document.createElement(\"div\"),e.yaxisTooltip.setAttribute(\"class\",s+\" apexcharts-theme-\"+t.config.tooltip.theme),r.appendChild(e.yaxisTooltip),0===i&&(e.yaxisTooltipText=[]),e.yaxisTooltipText[i]=document.createElement(\"div\"),e.yaxisTooltipText[i].classList.add(\"apexcharts-yaxistooltip-text\"),e.yaxisTooltip.appendChild(e.yaxisTooltipText[i]))},a=0;a<t.config.yaxis.length;a++)i(a)}},{key:\"setXCrosshairWidth\",value:function(){var t=this.w,e=this.ttCtx,i=e.getElXCrosshairs();if(e.xcrosshairsWidth=parseInt(t.config.xaxis.crosshairs.width,10),t.globals.comboCharts){var a=t.globals.dom.baseEl.querySelector(\".apexcharts-bar-area\");if(null!==a&&\"barWidth\"===t.config.xaxis.crosshairs.width){var s=parseFloat(a.getAttribute(\"barWidth\"));e.xcrosshairsWidth=s}else if(\"tickWidth\"===t.config.xaxis.crosshairs.width){var r=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/r}}else if(\"tickWidth\"===t.config.xaxis.crosshairs.width){var n=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/n}else if(\"barWidth\"===t.config.xaxis.crosshairs.width){var o=t.globals.dom.baseEl.querySelector(\".apexcharts-bar-area\");if(null!==o){var l=parseFloat(o.getAttribute(\"barWidth\"));e.xcrosshairsWidth=l}else e.xcrosshairsWidth=1}t.globals.isBarHorizontal&&(e.xcrosshairsWidth=0),null!==i&&e.xcrosshairsWidth>0&&i.setAttribute(\"width\",e.xcrosshairsWidth)}},{key:\"handleYCrosshair\",value:function(){var t=this.w,e=this.ttCtx;e.ycrosshairs=t.globals.dom.baseEl.querySelector(\".apexcharts-ycrosshairs\"),e.ycrosshairsHidden=t.globals.dom.baseEl.querySelector(\".apexcharts-ycrosshairs-hidden\")}},{key:\"drawYaxisTooltipText\",value:function(t,e,i){var a=this.ttCtx,s=this.w,r=s.globals.yLabelFormatters[t];if(a.yaxisTooltips[t]){var n=a.getElGrid().getBoundingClientRect(),o=(e-n.top)*i.yRatio[t],l=s.globals.maxYArr[t]-s.globals.minYArr[t],c=s.globals.minYArr[t]+(l-o);a.tooltipPosition.moveYCrosshairs(e-n.top),a.yaxisTooltipText[t].innerHTML=r(c),a.tooltipPosition.moveYAxisTooltip(t)}}}])&&Ue(e.prototype,i),t}();function Ze(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function $e(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?Ze(Object(i),!0).forEach((function(e){Je(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):Ze(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function Je(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function Qe(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Ke=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.tConfig=i.config.tooltip,this.tooltipUtil=new Xe(this),this.tooltipLabels=new He(this),this.tooltipPosition=new We(this),this.marker=new Ge(this),this.intersect=new _e(this),this.axesTooltip=new qe(this),this.showOnIntersect=this.tConfig.intersect,this.showTooltipTitle=this.tConfig.x.show,this.fixedTooltip=this.tConfig.fixed.enabled,this.xaxisTooltip=null,this.yaxisTTEls=null,this.isBarShared=!i.globals.isBarHorizontal&&this.tConfig.shared,this.lastHoverTime=Date.now()}var e,i;return e=t,i=[{key:\"getElTooltip\",value:function(t){return t||(t=this),t.w.globals.dom.baseEl?t.w.globals.dom.baseEl.querySelector(\".apexcharts-tooltip\"):null}},{key:\"getElXCrosshairs\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-xcrosshairs\")}},{key:\"getElGrid\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-grid\")}},{key:\"drawTooltip\",value:function(t){var e=this.w;this.xyRatios=t,this.isXAxisTooltipEnabled=e.config.xaxis.tooltip.enabled&&e.globals.axisCharts,this.yaxisTooltips=e.config.yaxis.map((function(t,i){return!!(t.show&&t.tooltip.enabled&&e.globals.axisCharts)})),this.allTooltipSeriesGroups=[],e.globals.axisCharts||(this.showTooltipTitle=!1);var i=document.createElement(\"div\");if(i.classList.add(\"apexcharts-tooltip\"),e.config.tooltip.cssClass&&i.classList.add(e.config.tooltip.cssClass),i.classList.add(\"apexcharts-theme-\".concat(this.tConfig.theme)),e.globals.dom.elWrap.appendChild(i),e.globals.axisCharts){this.axesTooltip.drawXaxisTooltip(),this.axesTooltip.drawYaxisTooltip(),this.axesTooltip.setXCrosshairWidth(),this.axesTooltip.handleYCrosshair();var a=new kt(this.ctx);this.xAxisTicksPositions=a.getXAxisTicksPositions()}if(!e.globals.comboCharts&&!this.tConfig.intersect&&\"rangeBar\"!==e.config.chart.type||this.tConfig.shared||(this.showOnIntersect=!0),0!==e.config.markers.size&&0!==e.globals.markers.largestSize||this.marker.drawDynamicPoints(this),e.globals.collapsedSeries.length!==e.globals.series.length){this.dataPointsDividedHeight=e.globals.gridHeight/e.globals.dataPoints,this.dataPointsDividedWidth=e.globals.gridWidth/e.globals.dataPoints,this.showTooltipTitle&&(this.tooltipTitle=document.createElement(\"div\"),this.tooltipTitle.classList.add(\"apexcharts-tooltip-title\"),this.tooltipTitle.style.fontFamily=this.tConfig.style.fontFamily||e.config.chart.fontFamily,this.tooltipTitle.style.fontSize=this.tConfig.style.fontSize,i.appendChild(this.tooltipTitle));var s=e.globals.series.length;(e.globals.xyCharts||e.globals.comboCharts)&&this.tConfig.shared&&(s=this.showOnIntersect?1:e.globals.series.length),this.legendLabels=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-legend-text\"),this.ttItems=this.createTTElements(s),this.addSVGEvents()}}},{key:\"createTTElements\",value:function(t){for(var e=this,i=this.w,a=[],s=this.getElTooltip(),r=function(r){var n=document.createElement(\"div\");n.classList.add(\"apexcharts-tooltip-series-group\"),n.style.order=i.config.tooltip.inverseOrder?t-r:r+1,e.tConfig.shared&&e.tConfig.enabledOnSeries&&Array.isArray(e.tConfig.enabledOnSeries)&&e.tConfig.enabledOnSeries.indexOf(r)<0&&n.classList.add(\"apexcharts-tooltip-series-group-hidden\");var o=document.createElement(\"span\");o.classList.add(\"apexcharts-tooltip-marker\"),o.style.backgroundColor=i.globals.colors[r],n.appendChild(o);var l=document.createElement(\"div\");l.classList.add(\"apexcharts-tooltip-text\"),l.style.fontFamily=e.tConfig.style.fontFamily||i.config.chart.fontFamily,l.style.fontSize=e.tConfig.style.fontSize,[\"y\",\"goals\",\"z\"].forEach((function(t){var e=document.createElement(\"div\");e.classList.add(\"apexcharts-tooltip-\".concat(t,\"-group\"));var i=document.createElement(\"span\");i.classList.add(\"apexcharts-tooltip-text-\".concat(t,\"-label\")),e.appendChild(i);var a=document.createElement(\"span\");a.classList.add(\"apexcharts-tooltip-text-\".concat(t,\"-value\")),e.appendChild(a),l.appendChild(e)})),n.appendChild(l),s.appendChild(n),a.push(n)},n=0;n<t;n++)r(n);return a}},{key:\"addSVGEvents\",value:function(){var t=this.w,e=t.config.chart.type,i=this.getElTooltip(),a=!(\"bar\"!==e&&\"candlestick\"!==e&&\"boxPlot\"!==e&&\"rangeBar\"!==e),s=\"area\"===e||\"line\"===e||\"scatter\"===e||\"bubble\"===e||\"radar\"===e,r=t.globals.dom.Paper.node,n=this.getElGrid();n&&(this.seriesBound=n.getBoundingClientRect());var o,l=[],c=[],h={hoverArea:r,elGrid:n,tooltipEl:i,tooltipY:l,tooltipX:c,ttItems:this.ttItems};if(t.globals.axisCharts&&(s?o=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series[data\\\\:longestSeries='true'] .apexcharts-marker\"):a?o=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-boxPlot-area, .apexcharts-series .apexcharts-rangebar-area\"):\"heatmap\"!==e&&\"treemap\"!==e||(o=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series .apexcharts-heatmap, .apexcharts-series .apexcharts-treemap\")),o&&o.length))for(var d=0;d<o.length;d++)l.push(o[d].getAttribute(\"cy\")),c.push(o[d].getAttribute(\"cx\"));if(t.globals.xyCharts&&!this.showOnIntersect||t.globals.comboCharts&&!this.showOnIntersect||a&&this.tooltipUtil.hasBars()&&this.tConfig.shared)this.addPathsEventListeners([r],h);else if(a&&!t.globals.comboCharts||s&&this.showOnIntersect)this.addDatapointEventsListeners(h);else if(!t.globals.axisCharts||\"heatmap\"===e||\"treemap\"===e){var u=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series\");this.addPathsEventListeners(u,h)}if(this.showOnIntersect){var g=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-line-series .apexcharts-marker, .apexcharts-area-series .apexcharts-marker\");g.length>0&&this.addPathsEventListeners(g,h),this.tooltipUtil.hasBars()&&!this.tConfig.shared&&this.addDatapointEventsListeners(h)}}},{key:\"drawFixedTooltipRect\",value:function(){var t=this.w,e=this.getElTooltip(),i=e.getBoundingClientRect(),a=i.width+10,s=i.height+10,r=this.tConfig.fixed.offsetX,n=this.tConfig.fixed.offsetY,o=this.tConfig.fixed.position.toLowerCase();return o.indexOf(\"right\")>-1&&(r=r+t.globals.svgWidth-a+10),o.indexOf(\"bottom\")>-1&&(n=n+t.globals.svgHeight-s-10),e.style.left=r+\"px\",e.style.top=n+\"px\",{x:r,y:n,ttWidth:a,ttHeight:s}}},{key:\"addDatapointEventsListeners\",value:function(t){var e=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series-markers .apexcharts-marker, .apexcharts-bar-area, .apexcharts-candlestick-area, .apexcharts-boxPlot-area, .apexcharts-rangebar-area\");this.addPathsEventListeners(e,t)}},{key:\"addPathsEventListeners\",value:function(t,e){for(var i=this,a=function(a){var s={paths:t[a],tooltipEl:e.tooltipEl,tooltipY:e.tooltipY,tooltipX:e.tooltipX,elGrid:e.elGrid,hoverArea:e.hoverArea,ttItems:e.ttItems};[\"mousemove\",\"mouseup\",\"touchmove\",\"mouseout\",\"touchend\"].map((function(e){return t[a].addEventListener(e,i.onSeriesHover.bind(i,s),{capture:!1,passive:!0})}))},s=0;s<t.length;s++)a(s)}},{key:\"onSeriesHover\",value:function(t,e){var i=this,a=Date.now()-this.lastHoverTime;a>=100?this.seriesHover(t,e):(clearTimeout(this.seriesHoverTimeout),this.seriesHoverTimeout=setTimeout((function(){i.seriesHover(t,e)}),100-a))}},{key:\"seriesHover\",value:function(t,e){var i=this;this.lastHoverTime=Date.now();var a=[],s=this.w;s.config.chart.group&&(a=this.ctx.getGroupedCharts()),s.globals.axisCharts&&(s.globals.minX===-1/0&&s.globals.maxX===1/0||0===s.globals.dataPoints)||(a.length?a.forEach((function(a){var s=i.getElTooltip(a),r={paths:t.paths,tooltipEl:s,tooltipY:t.tooltipY,tooltipX:t.tooltipX,elGrid:t.elGrid,hoverArea:t.hoverArea,ttItems:a.w.globals.tooltip.ttItems};a.w.globals.minX===i.w.globals.minX&&a.w.globals.maxX===i.w.globals.maxX&&a.w.globals.tooltip.seriesHoverByContext({chartCtx:a,ttCtx:a.w.globals.tooltip,opt:r,e})})):this.seriesHoverByContext({chartCtx:this.ctx,ttCtx:this.w.globals.tooltip,opt:t,e}))}},{key:\"seriesHoverByContext\",value:function(t){var e=t.chartCtx,i=t.ttCtx,a=t.opt,s=t.e,r=e.w,n=this.getElTooltip();n&&(i.tooltipRect={x:0,y:0,ttWidth:n.getBoundingClientRect().width,ttHeight:n.getBoundingClientRect().height},i.e=s,!i.tooltipUtil.hasBars()||r.globals.comboCharts||i.isBarShared||this.tConfig.onDatasetHover.highlightDataSeries&&new ut(e).toggleSeriesOnHover(s,s.target.parentNode),i.fixedTooltip&&i.drawFixedTooltipRect(),r.globals.axisCharts?i.axisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect}):i.nonAxisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect}))}},{key:\"axisChartsTooltips\",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,n=s.elGrid.getBoundingClientRect(),o=\"touchmove\"===a.type?a.touches[0].clientX:a.clientX,l=\"touchmove\"===a.type?a.touches[0].clientY:a.clientY;if(this.clientY=l,this.clientX=o,r.globals.capturedSeriesIndex=-1,r.globals.capturedDataPointIndex=-1,l<n.top||l>n.top+n.height)this.handleMouseOut(s);else{if(Array.isArray(this.tConfig.enabledOnSeries)&&!r.config.tooltip.shared){var c=parseInt(s.paths.getAttribute(\"index\"),10);if(this.tConfig.enabledOnSeries.indexOf(c)<0)return void this.handleMouseOut(s)}var h=this.getElTooltip(),d=this.getElXCrosshairs(),u=r.globals.xyCharts||\"bar\"===r.config.chart.type&&!r.globals.isBarHorizontal&&this.tooltipUtil.hasBars()&&this.tConfig.shared||r.globals.comboCharts&&this.tooltipUtil.hasBars();if(\"mousemove\"===a.type||\"touchmove\"===a.type||\"mouseup\"===a.type){if(r.globals.collapsedSeries.length+r.globals.ancillaryCollapsedSeries.length===r.globals.series.length)return;null!==d&&d.classList.add(\"apexcharts-active\");var g=this.yaxisTooltips.filter((function(t){return!0===t}));if(null!==this.ycrosshairs&&g.length&&this.ycrosshairs.classList.add(\"apexcharts-active\"),u&&!this.showOnIntersect)this.handleStickyTooltip(a,o,l,s);else if(\"heatmap\"===r.config.chart.type||\"treemap\"===r.config.chart.type){var f=this.intersect.handleHeatTreeTooltip({e:a,opt:s,x:e,y:i,type:r.config.chart.type});e=f.x,i=f.y,h.style.left=e+\"px\",h.style.top=i+\"px\"}else this.tooltipUtil.hasBars()&&this.intersect.handleBarTooltip({e:a,opt:s}),this.tooltipUtil.hasMarkers()&&this.intersect.handleMarkerTooltip({e:a,opt:s,x:e,y:i});if(this.yaxisTooltips.length)for(var p=0;p<r.config.yaxis.length;p++)this.axesTooltip.drawYaxisTooltipText(p,l,this.xyRatios);s.tooltipEl.classList.add(\"apexcharts-active\")}else\"mouseout\"!==a.type&&\"touchend\"!==a.type||this.handleMouseOut(s)}}},{key:\"nonAxisChartsTooltips\",value:function(t){var e=t.e,i=t.opt,a=t.tooltipRect,s=this.w,r=i.paths.getAttribute(\"rel\"),n=this.getElTooltip(),o=s.globals.dom.elWrap.getBoundingClientRect();if(\"mousemove\"===e.type||\"touchmove\"===e.type){n.classList.add(\"apexcharts-active\"),this.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:parseInt(r,10)-1,shared:!1});var l=s.globals.clientX-o.left-a.ttWidth/2,c=s.globals.clientY-o.top-a.ttHeight-10;if(n.style.left=l+\"px\",n.style.top=c+\"px\",s.config.legend.tooltipHoverFormatter){var h=r-1,d=(0,s.config.legend.tooltipHoverFormatter)(this.legendLabels[h].getAttribute(\"data:default-text\"),{seriesIndex:h,dataPointIndex:h,w:s});this.legendLabels[h].innerHTML=d}}else\"mouseout\"!==e.type&&\"touchend\"!==e.type||(n.classList.remove(\"apexcharts-active\"),s.config.legend.tooltipHoverFormatter&&this.legendLabels.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)})))}},{key:\"handleStickyTooltip\",value:function(t,e,i,a){var s=this.w,r=this.tooltipUtil.getNearestValues({context:this,hoverArea:a.hoverArea,elGrid:a.elGrid,clientX:e,clientY:i}),n=r.j,o=r.capturedSeries,l=a.elGrid.getBoundingClientRect();r.hoverX<0||r.hoverX>l.width?this.handleMouseOut(a):null!==o?this.handleStickyCapturedSeries(t,o,a,n):(this.tooltipUtil.isXoverlap(n)||s.globals.isBarHorizontal)&&this.create(t,this,0,n,a.ttItems)}},{key:\"handleStickyCapturedSeries\",value:function(t,e,i,a){var s=this.w;this.tConfig.shared||null!==s.globals.series[e][a]?void 0!==s.globals.series[e][a]?this.tConfig.shared&&this.tooltipUtil.isXoverlap(a)&&this.tooltipUtil.isInitialSeriesSameLen()?this.create(t,this,e,a,i.ttItems):this.create(t,this,e,a,i.ttItems,!1):this.tooltipUtil.isXoverlap(a)&&this.create(t,this,0,a,i.ttItems):this.handleMouseOut(i)}},{key:\"deactivateHoverFilter\",value:function(){for(var t=this.w,e=new b(this.ctx),i=t.globals.dom.Paper.select(\".apexcharts-bar-area\"),a=0;a<i.length;a++)e.pathMouseLeave(i[a])}},{key:\"handleMouseOut\",value:function(t){var e=this.w,i=this.getElXCrosshairs();if(t.tooltipEl.classList.remove(\"apexcharts-active\"),this.deactivateHoverFilter(),\"bubble\"!==e.config.chart.type&&this.marker.resetPointsSize(),null!==i&&i.classList.remove(\"apexcharts-active\"),null!==this.ycrosshairs&&this.ycrosshairs.classList.remove(\"apexcharts-active\"),this.isXAxisTooltipEnabled&&this.xaxisTooltip.classList.remove(\"apexcharts-active\"),this.yaxisTooltips.length){null===this.yaxisTTEls&&(this.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxistooltip\"));for(var a=0;a<this.yaxisTTEls.length;a++)this.yaxisTTEls[a].classList.remove(\"apexcharts-active\")}e.config.legend.tooltipHoverFormatter&&this.legendLabels.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)}))}},{key:\"markerClick\",value:function(t,e,i){var a=this.w;\"function\"==typeof a.config.chart.events.markerClick&&a.config.chart.events.markerClick(t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}),this.ctx.events.fireEvent(\"markerClick\",[t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}])}},{key:\"create\",value:function(t,e,i,a,s){var r,n,o,l,c,h,d,u,g,f,p,x,v,m,y,w,k=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,A=this.w,S=e;\"mouseup\"===t.type&&this.markerClick(t,i,a),null===k&&(k=this.tConfig.shared);var C=this.tooltipUtil.hasMarkers(),P=this.tooltipUtil.getElBars();if(A.config.legend.tooltipHoverFormatter){var L=A.config.legend.tooltipHoverFormatter,O=Array.from(this.legendLabels);O.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)}));for(var T=0;T<O.length;T++){var E=O[T],I=parseInt(E.getAttribute(\"i\"),10),M=decodeURIComponent(E.getAttribute(\"data:default-text\")),z=L(M,{seriesIndex:k?I:i,dataPointIndex:a,w:A});if(k)E.innerHTML=A.globals.collapsedSeriesIndices.indexOf(I)<0?z:M;else if(E.innerHTML=I===i?z:M,i===I)break}}var X=$e($e({ttItems:s,i,j:a},void 0!==(null===(r=A.globals.seriesRange)||void 0===r||null===(n=r[i])||void 0===n||null===(o=n[a])||void 0===o||null===(l=o.y[0])||void 0===l?void 0:l.y1)&&{y1:null===(c=A.globals.seriesRange)||void 0===c||null===(h=c[i])||void 0===h||null===(d=h[a])||void 0===d||null===(u=d.y[0])||void 0===u?void 0:u.y1}),void 0!==(null===(g=A.globals.seriesRange)||void 0===g||null===(f=g[i])||void 0===f||null===(p=f[a])||void 0===p||null===(x=p.y[0])||void 0===x?void 0:x.y2)&&{y2:null===(v=A.globals.seriesRange)||void 0===v||null===(m=v[i])||void 0===m||null===(y=m[a])||void 0===y||null===(w=y.y[0])||void 0===w?void 0:w.y2});if(k){if(S.tooltipLabels.drawSeriesTexts($e($e({},X),{},{shared:!this.showOnIntersect&&this.tConfig.shared})),C&&(A.globals.markers.largestSize>0?S.marker.enlargePoints(a):S.tooltipPosition.moveDynamicPointsOnHover(a)),this.tooltipUtil.hasBars()&&(this.barSeriesHeight=this.tooltipUtil.getBarsHeight(P),this.barSeriesHeight>0)){var Y=new b(this.ctx),D=A.globals.dom.Paper.select(\".apexcharts-bar-area[j='\".concat(a,\"']\"));this.deactivateHoverFilter(),this.tooltipPosition.moveStickyTooltipOverBars(a);for(var R=0;R<D.length;R++)Y.pathMouseEnter(D[R])}}else S.tooltipLabels.drawSeriesTexts($e({shared:!1},X)),this.tooltipUtil.hasBars()&&S.tooltipPosition.moveStickyTooltipOverBars(a),C&&S.tooltipPosition.moveMarkers(i,a)}}],i&&Qe(e.prototype,i),t}();function ti(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function ei(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?ti(Object(i),!0).forEach((function(e){ii(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):ti(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function ii(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function ai(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var si=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.barCtx=e,this.totalFormatter=this.w.config.plotOptions.bar.dataLabels.total.formatter,this.totalFormatter||(this.totalFormatter=this.w.config.dataLabels.formatter)}var e,i;return e=t,(i=[{key:\"handleBarDataLabels\",value:function(t){var e=t.x,i=t.y,a=t.y1,s=t.y2,r=t.i,n=t.j,o=t.realIndex,l=t.series,c=t.barHeight,h=t.barWidth,d=t.barYPosition,u=t.visibleSeries,g=t.renderedPath,f=this.w,p=new b(this.barCtx.ctx),x=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[o]:this.barCtx.strokeWidth,v=e+parseFloat(h*u),m=i+parseFloat(c*u);f.globals.isXNumeric&&!f.globals.isBarHorizontal&&(v=e+parseFloat(h*(u+1)),m=i+parseFloat(c*(u+1))-x);var y,w,k=null,A=e,S=i,C=f.config.dataLabels,P=this.barCtx.barOptions.dataLabels,L=this.barCtx.barOptions.dataLabels.total;void 0!==d&&this.barCtx.isRangeBar&&(m=d,S=d);var O=C.offsetX,T=C.offsetY,E={width:0,height:0};if(f.config.dataLabels.enabled){var I=this.barCtx.series[r][n];E=p.getTextRects(f.globals.yLabelFormatters[0](I),parseFloat(C.style.fontSize))}var M={x:e,y:i,i:r,j:n,realIndex:o,renderedPath:g,bcx:v,bcy:m,barHeight:c,barWidth:h,textRects:E,strokeWidth:x,dataLabelsX:A,dataLabelsY:S,dataLabelsConfig:C,barDataLabelsConfig:P,barTotalDataLabelsConfig:L,offX:O,offY:T};return w=this.barCtx.isHorizontal?this.calculateBarsDataLabelsPosition(M):this.calculateColumnsDataLabelsPosition(M),g.attr({cy:w.bcy,cx:w.bcx,j:n,val:l[r][n],barHeight:c,barWidth:h}),y=this.drawCalculatedDataLabels({x:w.dataLabelsX,y:w.dataLabelsY,val:this.barCtx.isRangeBar?[a,s]:l[r][n],i:o,j:n,barWidth:h,barHeight:c,textRects:E,dataLabelsConfig:C}),f.config.chart.stacked&&L.enabled&&(k=this.drawTotalDataLabels({x:w.totalDataLabelsX,y:w.totalDataLabelsY,realIndex:o,textAnchor:w.totalDataLabelsAnchor,val:this.getStackedTotalDataLabel({realIndex:o,j:n}),dataLabelsConfig:C,barTotalDataLabelsConfig:L})),{dataLabels:y,totalDataLabels:k}}},{key:\"getStackedTotalDataLabel\",value:function(t){var e=t.realIndex,i=t.j,a=this.w,s=this.barCtx.stackedSeriesTotals[i];return this.totalFormatter&&(s=this.totalFormatter(s,ei(ei({},a),{},{seriesIndex:e,dataPointIndex:i,w:a}))),s}},{key:\"calculateColumnsDataLabelsPosition\",value:function(t){var e,i,a,s=this.w,r=t.i,n=t.j,o=t.realIndex,l=t.y,c=t.bcx,h=t.barWidth,d=t.barHeight,u=t.textRects,g=t.dataLabelsY,f=t.dataLabelsConfig,p=t.barDataLabelsConfig,x=t.barTotalDataLabelsConfig,v=t.strokeWidth,m=t.offX,y=t.offY;d=Math.abs(d);var w=\"vertical\"===s.config.plotOptions.bar.dataLabels.orientation;c-=v/2;var k=s.globals.gridWidth/s.globals.dataPoints;e=s.globals.isXNumeric?c-h/2+m:c-k+h/2+m,w&&(e=e+u.height/2-v/2-2);var A=this.barCtx.series[r][n]<0,S=l;switch(this.barCtx.isReversed&&(S=l-d+(A?2*d:0),l-=d),p.position){case\"center\":g=w?A?S+d/2+y:S+d/2-y:A?S-d/2+u.height/2+y:S+d/2+u.height/2-y;break;case\"bottom\":g=w?A?S+d+y:S+d-y:A?S-d+u.height+v+y:S+d-u.height/2+v-y;break;case\"top\":g=w?A?S+y:S-y:A?S-u.height/2-y:S+u.height+y}if(this.barCtx.lastActiveBarSerieIndex===o&&x.enabled){var C=new b(this.barCtx.ctx).getTextRects(this.getStackedTotalDataLabel({realIndex:o,j:n}),f.fontSize);i=A?S-C.height/2-y-x.offsetY+18:S+C.height+y+x.offsetY-18,a=e+x.offsetX}return s.config.chart.stacked||(g<0?g=0+v:g+u.height/3>s.globals.gridHeight&&(g=s.globals.gridHeight-v)),{bcx:c,bcy:l,dataLabelsX:e,dataLabelsY:g,totalDataLabelsX:a,totalDataLabelsY:i,totalDataLabelsAnchor:\"middle\"}}},{key:\"calculateBarsDataLabelsPosition\",value:function(t){var e=this.w,i=t.x,a=t.i,s=t.j,r=t.realIndex,n=t.bcy,o=t.barHeight,l=t.barWidth,c=t.textRects,h=t.dataLabelsX,d=t.strokeWidth,u=t.dataLabelsConfig,g=t.barDataLabelsConfig,f=t.barTotalDataLabelsConfig,p=t.offX,x=t.offY,v=e.globals.gridHeight/e.globals.dataPoints;l=Math.abs(l);var m,y,w=n-(this.barCtx.isRangeBar?0:v)+o/2+c.height/2+x-3,k=\"start\",A=this.barCtx.series[a][s]<0,S=i;switch(this.barCtx.isReversed&&(S=i+l-(A?2*l:0),i=e.globals.gridWidth-l),g.position){case\"center\":h=A?S+l/2-p:Math.max(c.width/2,S-l/2)+p;break;case\"bottom\":h=A?S+l-d-Math.round(c.width/2)-p:S-l+d+Math.round(c.width/2)+p;break;case\"top\":h=A?S-d+Math.round(c.width/2)-p:S-d-Math.round(c.width/2)+p}if(this.barCtx.lastActiveBarSerieIndex===r&&f.enabled){var C=new b(this.barCtx.ctx).getTextRects(this.getStackedTotalDataLabel({realIndex:r,j:s}),u.fontSize);A?(m=S-d+Math.round(C.width/2)-p-f.offsetX-15,k=\"end\"):m=S-d-Math.round(C.width/2)+p+f.offsetX+15,y=w+f.offsetY}return e.config.chart.stacked||(h<0?h=h+c.width+d:h+c.width/2>e.globals.gridWidth&&(h=e.globals.gridWidth-c.width-d)),{bcx:i,bcy:n,dataLabelsX:h,dataLabelsY:w,totalDataLabelsX:m,totalDataLabelsY:y,totalDataLabelsAnchor:k}}},{key:\"drawCalculatedDataLabels\",value:function(t){var e=t.x,i=t.y,a=t.val,s=t.i,r=t.j,n=t.textRects,o=t.barHeight,l=t.barWidth,c=t.dataLabelsConfig,h=this.w,d=\"rotate(0)\";\"vertical\"===h.config.plotOptions.bar.dataLabels.orientation&&(d=\"rotate(-90, \".concat(e,\", \").concat(i,\")\"));var u=new ht(this.barCtx.ctx),g=new b(this.barCtx.ctx),f=c.formatter,p=null,x=h.globals.collapsedSeriesIndices.indexOf(s)>-1;if(c.enabled&&!x){p=g.group({class:\"apexcharts-data-labels\",transform:d});var v=\"\";void 0!==a&&(v=f(a,ei(ei({},h),{},{seriesIndex:s,dataPointIndex:r,w:h})));var m=h.globals.series[s][r]<0,y=h.config.plotOptions.bar.dataLabels.position;\"vertical\"===h.config.plotOptions.bar.dataLabels.orientation&&(\"top\"===y&&(c.textAnchor=m?\"end\":\"start\"),\"center\"===y&&(c.textAnchor=\"middle\"),\"bottom\"===y&&(c.textAnchor=m?\"end\":\"start\")),this.barCtx.isRangeBar&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels&&l<g.getTextRects(v,parseFloat(c.style.fontSize)).width&&(v=\"\"),h.config.chart.stacked&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels&&(this.barCtx.isHorizontal?n.width/1.6>Math.abs(l)&&(v=\"\"):n.height/1.6>Math.abs(o)&&(v=\"\"));var w=ei({},c);this.barCtx.isHorizontal&&a<0&&(\"start\"===c.textAnchor?w.textAnchor=\"end\":\"end\"===c.textAnchor&&(w.textAnchor=\"start\")),u.plotDataLabelsText({x:e,y:i,text:v,i:s,j:r,parent:p,dataLabelsConfig:w,alwaysDrawDataLabel:!0,offsetCorrection:!0})}return p}},{key:\"drawTotalDataLabels\",value:function(t){var e,i=t.x,a=t.y,s=t.val,r=t.realIndex,n=t.textAnchor,o=t.barTotalDataLabelsConfig,l=new b(this.barCtx.ctx);return o.enabled&&void 0!==i&&void 0!==a&&this.barCtx.lastActiveBarSerieIndex===r&&(e=l.drawText({x:i,y:a,foreColor:o.style.color,text:s,textAnchor:n,fontFamily:o.style.fontFamily,fontSize:o.style.fontSize,fontWeight:o.style.fontWeight})),e}}])&&ai(e.prototype,i),t}();function ri(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function ni(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var oi=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.barCtx=e}var e,i;return e=t,i=[{key:\"initVariables\",value:function(t){var e=this.w;this.barCtx.series=t,this.barCtx.totalItems=0,this.barCtx.seriesLen=0,this.barCtx.visibleI=-1,this.barCtx.visibleItems=1;for(var i=0;i<t.length;i++)if(t[i].length>0&&(this.barCtx.seriesLen=this.barCtx.seriesLen+1,this.barCtx.totalItems+=t[i].length),e.globals.isXNumeric)for(var a=0;a<t[i].length;a++)e.globals.seriesX[i][a]>e.globals.minX&&e.globals.seriesX[i][a]<e.globals.maxX&&this.barCtx.visibleItems++;else this.barCtx.visibleItems=e.globals.dataPoints;0===this.barCtx.seriesLen&&(this.barCtx.seriesLen=1),this.barCtx.zeroSerieses=[],this.barCtx.radiusOnSeriesNumber=t.length-1,e.globals.comboCharts||this.checkZeroSeries({series:t})}},{key:\"initialPositions\",value:function(){var t,e,i,a,s,r,n,o,l=this.w,c=l.globals.dataPoints;this.barCtx.isRangeBar&&(c=l.globals.labels.length);var h=this.barCtx.seriesLen;if(l.config.plotOptions.bar.rangeBarGroupRows&&(h=1),this.barCtx.isHorizontal)s=(i=l.globals.gridHeight/c)/h,l.globals.isXNumeric&&(s=(i=l.globals.gridHeight/this.barCtx.totalItems)/this.barCtx.seriesLen),s=s*parseInt(this.barCtx.barOptions.barHeight,10)/100,o=this.barCtx.baseLineInvertedY+l.globals.padHorizontal+(this.barCtx.isReversed?l.globals.gridWidth:0)-(this.barCtx.isReversed?2*this.barCtx.baseLineInvertedY:0),e=(i-s*this.barCtx.seriesLen)/2;else{if(a=l.globals.gridWidth/this.barCtx.visibleItems,l.config.xaxis.convertedCatToNumeric&&(a=l.globals.gridWidth/l.globals.dataPoints),r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100,l.globals.isXNumeric){var d=this.barCtx.xRatio;l.config.xaxis.convertedCatToNumeric&&(d=this.barCtx.initialXRatio),l.globals.minXDiff&&.5!==l.globals.minXDiff&&l.globals.minXDiff/d>0&&(a=l.globals.minXDiff/d),(r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100)<1&&(r=1)}n=l.globals.gridHeight-this.barCtx.baseLineY[this.barCtx.yaxisIndex]-(this.barCtx.isReversed?l.globals.gridHeight:0)+(this.barCtx.isReversed?2*this.barCtx.baseLineY[this.barCtx.yaxisIndex]:0),t=l.globals.padHorizontal+(a-r*this.barCtx.seriesLen)/2}return{x:t,y:e,yDivision:i,xDivision:a,barHeight:s,barWidth:r,zeroH:n,zeroW:o}}},{key:\"getPathFillColor\",value:function(t,e,i,a){var s,r,n,o,l=this.w,c=new st(this.barCtx.ctx),h=null,d=this.barCtx.barOptions.distributed?i:e;return this.barCtx.barOptions.colors.ranges.length>0&&this.barCtx.barOptions.colors.ranges.map((function(a){t[e][i]>=a.from&&t[e][i]<=a.to&&(h=a.color)})),l.config.series[e].data[i]&&l.config.series[e].data[i].fillColor&&(h=l.config.series[e].data[i].fillColor),c.fillPath({seriesNumber:this.barCtx.barOptions.distributed?d:a,dataPointIndex:i,color:h,value:t[e][i],fillConfig:null===(s=l.config.series[e].data[i])||void 0===s?void 0:s.fill,fillType:null!==(r=l.config.series[e].data[i])&&void 0!==r&&null!==(n=r.fill)&&void 0!==n&&n.type?null===(o=l.config.series[e].data[i])||void 0===o?void 0:o.fill.type:l.config.fill.type})}},{key:\"getStrokeWidth\",value:function(t,e,i){var a=0,s=this.w;return this.barCtx.series[t][e]?this.barCtx.isNullValue=!1:this.barCtx.isNullValue=!0,s.config.stroke.show&&(this.barCtx.isNullValue||(a=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[i]:this.barCtx.strokeWidth)),a}},{key:\"shouldApplyRadius\",value:function(t){var e=this.w,i=!1;return e.config.plotOptions.bar.borderRadius>0&&(e.config.chart.stacked&&\"last\"===e.config.plotOptions.bar.borderRadiusWhenStacked?this.barCtx.lastActiveBarSerieIndex===t&&(i=!0):i=!0),i}},{key:\"barBackground\",value:function(t){var e=t.j,i=t.i,a=t.x1,s=t.x2,r=t.y1,n=t.y2,o=t.elSeries,l=this.w,c=new b(this.barCtx.ctx),h=new ut(this.barCtx.ctx).getActiveConfigSeriesIndex();if(this.barCtx.barOptions.colors.backgroundBarColors.length>0&&h===i){e>=this.barCtx.barOptions.colors.backgroundBarColors.length&&(e%=this.barCtx.barOptions.colors.backgroundBarColors.length);var d=this.barCtx.barOptions.colors.backgroundBarColors[e],u=c.drawRect(void 0!==a?a:0,void 0!==r?r:0,void 0!==s?s:l.globals.gridWidth,void 0!==n?n:l.globals.gridHeight,this.barCtx.barOptions.colors.backgroundBarRadius,d,this.barCtx.barOptions.colors.backgroundBarOpacity);o.add(u),u.node.classList.add(\"apexcharts-backgroundBar\")}}},{key:\"getColumnPaths\",value:function(t){var e,i=t.barWidth,a=t.barXPosition,s=t.y1,r=t.y2,n=t.strokeWidth,o=t.realIndex,l=t.i,c=t.j,h=t.w,d=new b(this.barCtx.ctx);(n=Array.isArray(n)?n[o]:n)||(n=0);var u=i,g=a;null!==(e=h.config.series[o].data[c])&&void 0!==e&&e.columnWidthOffset&&(g=a-h.config.series[o].data[c].columnWidthOffset/2,u=i+h.config.series[o].data[c].columnWidthOffset);var f=g,p=g+u;s+=.001,r+=.001;var x=d.move(f,s),v=d.move(f,s),m=d.line(p-n,s);return h.globals.previousPaths.length>0&&(v=this.barCtx.getPreviousPath(o,c,!1)),x=x+d.line(f,r)+d.line(p-n,r)+d.line(p-n,s)+(\"around\"===h.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),v=v+d.line(f,s)+m+m+m+m+m+d.line(f,s)+(\"around\"===h.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),this.shouldApplyRadius(o)&&(x=d.roundPathCorners(x,h.config.plotOptions.bar.borderRadius)),h.config.chart.stacked&&(this.barCtx.yArrj.push(r),this.barCtx.yArrjF.push(Math.abs(s-r)),this.barCtx.yArrjVal.push(this.barCtx.series[l][c])),{pathTo:x,pathFrom:v}}},{key:\"getBarpaths\",value:function(t){var e,i=t.barYPosition,a=t.barHeight,s=t.x1,r=t.x2,n=t.strokeWidth,o=t.realIndex,l=t.i,c=t.j,h=t.w,d=new b(this.barCtx.ctx);(n=Array.isArray(n)?n[o]:n)||(n=0);var u=i,g=a;null!==(e=h.config.series[o].data[c])&&void 0!==e&&e.barHeightOffset&&(u=i-h.config.series[o].data[c].barHeightOffset/2,g=a+h.config.series[o].data[c].barHeightOffset);var f=u,p=u+g;s+=.001,r+=.001;var x=d.move(s,f),v=d.move(s,f);h.globals.previousPaths.length>0&&(v=this.barCtx.getPreviousPath(o,c,!1));var m=d.line(s,p-n);return x=x+d.line(r,f)+d.line(r,p-n)+m+(\"around\"===h.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),v=v+d.line(s,f)+m+m+m+m+m+d.line(s,f)+(\"around\"===h.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),this.shouldApplyRadius(o)&&(x=d.roundPathCorners(x,h.config.plotOptions.bar.borderRadius)),h.config.chart.stacked&&(this.barCtx.xArrj.push(r),this.barCtx.xArrjF.push(Math.abs(s-r)),this.barCtx.xArrjVal.push(this.barCtx.series[l][c])),{pathTo:x,pathFrom:v}}},{key:\"checkZeroSeries\",value:function(t){for(var e=t.series,i=this.w,a=0;a<e.length;a++){for(var s=0,r=0;r<e[i.globals.maxValsInArrayIndex].length;r++)s+=e[a][r];0===s&&this.barCtx.zeroSerieses.push(a)}for(var n=e.length-1;n>=0;n--)this.barCtx.zeroSerieses.indexOf(n)>-1&&n===this.radiusOnSeriesNumber&&(this.barCtx.radiusOnSeriesNumber-=1);for(var o=e.length-1;o>=0;o--)i.globals.collapsedSeriesIndices.indexOf(this.barCtx.radiusOnSeriesNumber)>-1&&(this.barCtx.radiusOnSeriesNumber-=1)}},{key:\"getXForValue\",value:function(t,e){var i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=i?e:null;return null!=t&&(a=e+t/this.barCtx.invertedYRatio-2*(this.barCtx.isReversed?t/this.barCtx.invertedYRatio:0)),a}},{key:\"getYForValue\",value:function(t,e){var i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=i?e:null;return null!=t&&(a=e-t/this.barCtx.yRatio[this.barCtx.yaxisIndex]+2*(this.barCtx.isReversed?t/this.barCtx.yRatio[this.barCtx.yaxisIndex]:0)),a}},{key:\"getGoalValues\",value:function(t,e,i,a,s){var r=this,n=this.w,o=[];return n.globals.seriesGoals[a]&&n.globals.seriesGoals[a][s]&&Array.isArray(n.globals.seriesGoals[a][s])&&n.globals.seriesGoals[a][s].forEach((function(a){var s;o.push((ri(s={},t,\"x\"===t?r.getXForValue(a.value,e,!1):r.getYForValue(a.value,i,!1)),ri(s,\"attrs\",a),s))})),o}},{key:\"drawGoalLine\",value:function(t){var e=t.barXPosition,i=t.barYPosition,a=t.goalX,s=t.goalY,r=t.barWidth,n=t.barHeight,o=new b(this.barCtx.ctx),l=o.group({className:\"apexcharts-bar-goals-groups\"}),c=null;return this.barCtx.isHorizontal?Array.isArray(a)&&a.forEach((function(t){var e=void 0!==t.attrs.strokeHeight?t.attrs.strokeHeight:n/2,a=i+e+n/2;c=o.drawLine(t.x,a-2*e,t.x,a,t.attrs.strokeColor?t.attrs.strokeColor:void 0,t.attrs.strokeDashArray,t.attrs.strokeWidth?t.attrs.strokeWidth:2,t.attrs.strokeLineCap),l.add(c)})):Array.isArray(s)&&s.forEach((function(t){var i=void 0!==t.attrs.strokeWidth?t.attrs.strokeWidth:r/2,a=e+i+r/2;c=o.drawLine(a-2*i,t.y,a,t.y,t.attrs.strokeColor?t.attrs.strokeColor:void 0,t.attrs.strokeDashArray,t.attrs.strokeHeight?t.attrs.strokeHeight:2,t.attrs.strokeLineCap),l.add(c)})),l}}],i&&ni(e.prototype,i),t}();function li(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function ci(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?li(Object(i),!0).forEach((function(e){hi(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):li(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function hi(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function di(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const ui=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w;var a=this.w;this.barOptions=a.config.plotOptions.bar,this.isHorizontal=this.barOptions.horizontal,this.strokeWidth=a.config.stroke.width,this.isNullValue=!1,this.isRangeBar=a.globals.seriesRange.length&&this.isHorizontal,this.xyRatios=i,null!==this.xyRatios&&(this.xRatio=i.xRatio,this.initialXRatio=i.initialXRatio,this.yRatio=i.yRatio,this.invertedXRatio=i.invertedXRatio,this.invertedYRatio=i.invertedYRatio,this.baseLineY=i.baseLineY,this.baseLineInvertedY=i.baseLineInvertedY),this.yaxisIndex=0,this.seriesLen=0;var s=new ut(this.ctx);this.lastActiveBarSerieIndex=s.getActiveConfigSeriesIndex(\"desc\",[\"bar\",\"column\"]);var r=s.getBarSeriesIndices(),n=new y(this.ctx);this.stackedSeriesTotals=n.getStackedSeriesTotals(this.w.config.series.map((function(t,e){return-1===r.indexOf(e)?e:-1})).filter((function(t){return-1!==t}))),this.barHelpers=new oi(this)}var e,i;return e=t,(i=[{key:\"draw\",value:function(t,e){var i=this.w,a=new b(this.ctx),s=new y(this.ctx,i);t=s.getLogSeries(t),this.series=t,this.yRatio=s.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);var r=a.group({class:\"apexcharts-bar-series apexcharts-plot-series\"});i.config.dataLabels.enabled&&this.totalItems>this.barOptions.dataLabels.maxItems&&console.warn(\"WARNING: DataLabels are enabled but there are too many to display. This may cause performance issue when rendering.\");for(var o=0,l=0;o<t.length;o++,l++){var c,h,d,u,g=void 0,f=void 0,p=[],x=[],v=i.globals.comboCharts?e[o]:o,m=a.group({class:\"apexcharts-series\",rel:o+1,seriesName:n.escapeString(i.globals.seriesNames[v]),\"data:realIndex\":v});this.ctx.series.addCollapsedClassToSeries(m,v),t[o].length>0&&(this.visibleI=this.visibleI+1);var w=0,k=0;this.yRatio.length>1&&(this.yaxisIndex=v),this.isReversed=i.config.yaxis[this.yaxisIndex]&&i.config.yaxis[this.yaxisIndex].reversed;var A=this.barHelpers.initialPositions();f=A.y,w=A.barHeight,h=A.yDivision,u=A.zeroW,g=A.x,k=A.barWidth,c=A.xDivision,d=A.zeroH,this.horizontal||x.push(g+k/2);for(var S=a.group({class:\"apexcharts-datalabels\",\"data:realIndex\":v}),C=a.group({class:\"apexcharts-bar-goals-markers\",style:\"pointer-events: none\"}),P=0;P<i.globals.dataPoints;P++){var L=this.barHelpers.getStrokeWidth(o,P,v),O=null,T={indexes:{i:o,j:P,realIndex:v,bc:l},x:g,y:f,strokeWidth:L,elSeries:m};this.isHorizontal?(O=this.drawBarPaths(ci(ci({},T),{},{barHeight:w,zeroW:u,yDivision:h})),k=this.series[o][P]/this.invertedYRatio):(O=this.drawColumnPaths(ci(ci({},T),{},{xDivision:c,barWidth:k,zeroH:d})),w=this.series[o][P]/this.yRatio[this.yaxisIndex]);var E=this.barHelpers.drawGoalLine({barXPosition:O.barXPosition,barYPosition:O.barYPosition,goalX:O.goalX,goalY:O.goalY,barHeight:w,barWidth:k});E&&C.add(E),f=O.y,g=O.x,P>0&&x.push(g+k/2),p.push(f);var I=this.barHelpers.getPathFillColor(t,o,P,v);this.renderSeries({realIndex:v,pathFill:I,j:P,i:o,pathFrom:O.pathFrom,pathTo:O.pathTo,strokeWidth:L,elSeries:m,x:g,y:f,series:t,barHeight:w,barWidth:k,elDataLabelsWrap:S,elGoalsMarkers:C,visibleSeries:this.visibleI,type:\"bar\"})}i.globals.seriesXvalues[v]=x,i.globals.seriesYvalues[v]=p,r.add(m)}return r}},{key:\"renderSeries\",value:function(t){var e=t.realIndex,i=t.pathFill,a=t.lineFill,s=t.j,r=t.i,n=t.pathFrom,o=t.pathTo,l=t.strokeWidth,c=t.elSeries,h=t.x,u=t.y,g=t.y1,f=t.y2,p=t.series,x=t.barHeight,v=t.barWidth,m=t.barYPosition,y=t.elDataLabelsWrap,w=t.elGoalsMarkers,k=t.visibleSeries,A=t.type,S=this.w,C=new b(this.ctx);a||(a=this.barOptions.distributed?S.globals.stroke.colors[s]:S.globals.stroke.colors[e]),S.config.series[r].data[s]&&S.config.series[r].data[s].strokeColor&&(a=S.config.series[r].data[s].strokeColor),this.isNullValue&&(i=\"none\");var P=s/S.config.chart.animations.animateGradually.delay*(S.config.chart.animations.speed/S.globals.dataPoints)/2.4,L=C.renderPaths({i:r,j:s,realIndex:e,pathFrom:n,pathTo:o,stroke:a,strokeWidth:l,strokeLineCap:S.config.stroke.lineCap,fill:i,animationDelay:P,initialSpeed:S.config.chart.animations.speed,dataChangeSpeed:S.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-\".concat(A,\"-area\")});L.attr(\"clip-path\",\"url(#gridRectMask\".concat(S.globals.cuid,\")\"));var O=S.config.forecastDataPoints;O.count>0&&s>=S.globals.dataPoints-O.count&&(L.node.setAttribute(\"stroke-dasharray\",O.dashArray),L.node.setAttribute(\"stroke-width\",O.strokeWidth),L.node.setAttribute(\"fill-opacity\",O.fillOpacity)),void 0!==g&&void 0!==f&&(L.attr(\"data-range-y1\",g),L.attr(\"data-range-y2\",f)),new d(this.ctx).setSelectionFilter(L,e,s),c.add(L);var T=new si(this).handleBarDataLabels({x:h,y:u,y1:g,y2:f,i:r,j:s,series:p,realIndex:e,barHeight:x,barWidth:v,barYPosition:m,renderedPath:L,visibleSeries:k});return null!==T.dataLabels&&y.add(T.dataLabels),T.totalDataLabels&&y.add(T.totalDataLabels),c.add(y),w&&c.add(w),c}},{key:\"drawBarPaths\",value:function(t){var e,i=t.indexes,a=t.barHeight,s=t.strokeWidth,r=t.zeroW,n=t.x,o=t.y,l=t.yDivision,c=t.elSeries,h=this.w,d=i.i,u=i.j;if(h.globals.isXNumeric)e=(o=(h.globals.seriesX[d][u]-h.globals.minX)/this.invertedXRatio-a)+a*this.visibleI;else if(h.config.plotOptions.bar.hideZeroBarsWhenGrouped){var g=0,f=0;h.globals.seriesPercent.forEach((function(t,e){t[u]&&g++,e<d&&0===t[u]&&f++})),e=o+(a=this.seriesLen*a/g)*this.visibleI,e-=a*f}n=this.barHelpers.getXForValue(this.series[d][u],r);var p=this.barHelpers.getBarpaths({barYPosition:e,barHeight:a,x1:r,x2:n,strokeWidth:s,series:this.series,realIndex:i.realIndex,i:d,j:u,w:h});return h.globals.isXNumeric||(o+=l),this.barHelpers.barBackground({j:u,i:d,y1:e-a*this.visibleI,y2:a*this.seriesLen,elSeries:c}),{pathTo:p.pathTo,pathFrom:p.pathFrom,x:n,y:o,goalX:this.barHelpers.getGoalValues(\"x\",r,null,d,u),barYPosition:e}}},{key:\"drawColumnPaths\",value:function(t){var e,i=t.indexes,a=t.x,s=t.y,r=t.xDivision,n=t.barWidth,o=t.zeroH,l=t.strokeWidth,c=t.elSeries,h=this.w,d=i.realIndex,u=i.i,g=i.j,f=i.bc;if(h.globals.isXNumeric){var p=d;h.globals.seriesX[d].length||(p=h.globals.maxValsInArrayIndex),h.globals.seriesX[p][g]&&(a=(h.globals.seriesX[p][g]-h.globals.minX)/this.xRatio-n*this.seriesLen/2),e=a+n*this.visibleI}else if(h.config.plotOptions.bar.hideZeroBarsWhenGrouped){var x=0,b=0;h.globals.seriesPercent.forEach((function(t,e){t[g]&&x++,e<u&&0===t[g]&&b++})),e=a+(n=this.seriesLen*n/x)*this.visibleI,e-=n*b}s=this.barHelpers.getYForValue(this.series[u][g],o);var v=this.barHelpers.getColumnPaths({barXPosition:e,barWidth:n,y1:o,y2:s,strokeWidth:l,series:this.series,realIndex:i.realIndex,i:u,j:g,w:h});return h.globals.isXNumeric||(a+=r),this.barHelpers.barBackground({bc:f,j:g,i:u,x1:e-l/2-n*this.visibleI,x2:n*this.seriesLen+l/2,elSeries:c}),{pathTo:v.pathTo,pathFrom:v.pathFrom,x:a,y:s,goalY:this.barHelpers.getGoalValues(\"y\",null,o,u,g),barXPosition:e}}},{key:\"getPreviousPath\",value:function(t,e){for(var i,a=this.w,s=0;s<a.globals.previousPaths.length;s++){var r=a.globals.previousPaths[s];r.paths&&r.paths.length>0&&parseInt(r.realIndex,10)===parseInt(t,10)&&void 0!==a.globals.previousPaths[s].paths[e]&&(i=a.globals.previousPaths[s].paths[e].d)}return i}}])&&di(e.prototype,i),t}();function gi(t){return gi=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},gi(t)}function fi(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function pi(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?fi(Object(i),!0).forEach((function(e){xi(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):fi(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function xi(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function bi(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}function vi(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function mi(t,e){return mi=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},mi(t,e)}function yi(t,e){if(e&&(\"object\"===gi(e)||\"function\"==typeof e))return e;if(void 0!==e)throw new TypeError(\"Derived constructors may only return object or undefined\");return function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t)}function wi(t){return wi=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},wi(t)}var ki=function(t){!function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&mi(t,e)}(o,t);var e,i,a,s,r=(a=o,s=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}(),function(){var t,e=wi(a);if(s){var i=wi(this).constructor;t=Reflect.construct(e,arguments,i)}else t=e.apply(this,arguments);return yi(this,t)});function o(){return bi(this,o),r.apply(this,arguments)}return e=o,(i=[{key:\"draw\",value:function(t,e){var i=this,a=this.w;this.graphics=new b(this.ctx),this.bar=new ui(this.ctx,this.xyRatios);var s=new y(this.ctx,a);t=s.getLogSeries(t),this.yRatio=s.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t),\"100%\"===a.config.chart.stackType&&(t=a.globals.seriesPercent.slice()),this.series=t,this.totalItems=0,this.prevY=[],this.prevX=[],this.prevYF=[],this.prevXF=[],this.prevYVal=[],this.prevXVal=[],this.xArrj=[],this.xArrjF=[],this.xArrjVal=[],this.yArrj=[],this.yArrjF=[],this.yArrjVal=[];for(var r=0;r<t.length;r++)t[r].length>0&&(this.totalItems+=t[r].length);for(var o=this.graphics.group({class:\"apexcharts-bar-series apexcharts-plot-series\"}),l=0,c=0,h=function(s,r){var h=void 0,d=void 0,u=void 0,g=void 0,f=[],p=[],x=a.globals.comboCharts?e[s]:s;i.yRatio.length>1&&(i.yaxisIndex=x),i.isReversed=a.config.yaxis[i.yaxisIndex]&&a.config.yaxis[i.yaxisIndex].reversed;var b=i.graphics.group({class:\"apexcharts-series\",seriesName:n.escapeString(a.globals.seriesNames[x]),rel:s+1,\"data:realIndex\":x});i.ctx.series.addCollapsedClassToSeries(b,x);var v=i.graphics.group({class:\"apexcharts-datalabels\",\"data:realIndex\":x}),m=i.graphics.group({class:\"apexcharts-bar-goals-markers\",style:\"pointer-events: none\"}),y=0,w=0,k=i.initialPositions(l,c,h,d,u,g);c=k.y,y=k.barHeight,d=k.yDivision,g=k.zeroW,l=k.x,w=k.barWidth,h=k.xDivision,u=k.zeroH,i.yArrj=[],i.yArrjF=[],i.yArrjVal=[],i.xArrj=[],i.xArrjF=[],i.xArrjVal=[],1===i.prevY.length&&i.prevY[0].every((function(t){return isNaN(t)}))&&(i.prevY[0]=i.prevY[0].map((function(t){return u})),i.prevYF[0]=i.prevYF[0].map((function(t){return 0})));for(var A=0;A<a.globals.dataPoints;A++){var S=i.barHelpers.getStrokeWidth(s,A,x),C={indexes:{i:s,j:A,realIndex:x,bc:r},strokeWidth:S,x:l,y:c,elSeries:b},P=null;i.isHorizontal?(P=i.drawStackedBarPaths(pi(pi({},C),{},{zeroW:g,barHeight:y,yDivision:d})),w=i.series[s][A]/i.invertedYRatio):(P=i.drawStackedColumnPaths(pi(pi({},C),{},{xDivision:h,barWidth:w,zeroH:u})),y=i.series[s][A]/i.yRatio[i.yaxisIndex]);var L=i.barHelpers.drawGoalLine({barXPosition:P.barXPosition,barYPosition:P.barYPosition,goalX:P.goalX,goalY:P.goalY,barHeight:y,barWidth:w});L&&m.add(L),c=P.y,l=P.x,f.push(l),p.push(c);var O=i.barHelpers.getPathFillColor(t,s,A,x);b=i.renderSeries({realIndex:x,pathFill:O,j:A,i:s,pathFrom:P.pathFrom,pathTo:P.pathTo,strokeWidth:S,elSeries:b,x:l,y:c,series:t,barHeight:y,barWidth:w,elDataLabelsWrap:v,elGoalsMarkers:m,type:\"bar\",visibleSeries:0})}a.globals.seriesXvalues[x]=f,a.globals.seriesYvalues[x]=p,i.prevY.push(i.yArrj),i.prevYF.push(i.yArrjF),i.prevYVal.push(i.yArrjVal),i.prevX.push(i.xArrj),i.prevXF.push(i.xArrjF),i.prevXVal.push(i.xArrjVal),o.add(b)},d=0,u=0;d<t.length;d++,u++)h(d,u);return o}},{key:\"initialPositions\",value:function(t,e,i,a,s,r){var n,o,l=this.w;return this.isHorizontal?(n=(n=a=l.globals.gridHeight/l.globals.dataPoints)*parseInt(l.config.plotOptions.bar.barHeight,10)/100,r=this.baseLineInvertedY+l.globals.padHorizontal+(this.isReversed?l.globals.gridWidth:0)-(this.isReversed?2*this.baseLineInvertedY:0),e=(a-n)/2):(o=i=l.globals.gridWidth/l.globals.dataPoints,o=l.globals.isXNumeric&&l.globals.dataPoints>1?(i=l.globals.minXDiff/this.xRatio)*parseInt(this.barOptions.columnWidth,10)/100:o*parseInt(l.config.plotOptions.bar.columnWidth,10)/100,s=l.globals.gridHeight-this.baseLineY[this.yaxisIndex]-(this.isReversed?l.globals.gridHeight:0)+(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),t=l.globals.padHorizontal+(i-o)/2),{x:t,y:e,yDivision:a,xDivision:i,barHeight:n,barWidth:o,zeroH:s,zeroW:r}}},{key:\"drawStackedBarPaths\",value:function(t){for(var e,i=t.indexes,a=t.barHeight,s=t.strokeWidth,r=t.zeroW,n=t.x,o=t.y,l=t.yDivision,c=t.elSeries,h=this.w,d=o,u=i.i,g=i.j,f=0,p=0;p<this.prevXF.length;p++)f+=this.prevXF[p][g];if(u>0){var x=r;this.prevXVal[u-1][g]<0?x=this.series[u][g]>=0?this.prevX[u-1][g]+f-2*(this.isReversed?f:0):this.prevX[u-1][g]:this.prevXVal[u-1][g]>=0&&(x=this.series[u][g]>=0?this.prevX[u-1][g]:this.prevX[u-1][g]-f+2*(this.isReversed?f:0)),e=x}else e=r;n=null===this.series[u][g]?e:e+this.series[u][g]/this.invertedYRatio-2*(this.isReversed?this.series[u][g]/this.invertedYRatio:0);var b=this.barHelpers.getBarpaths({barYPosition:d,barHeight:a,x1:e,x2:n,strokeWidth:s,series:this.series,realIndex:i.realIndex,i:u,j:g,w:h});return this.barHelpers.barBackground({j:g,i:u,y1:d,y2:a,elSeries:c}),o+=l,{pathTo:b.pathTo,pathFrom:b.pathFrom,goalX:this.barHelpers.getGoalValues(\"x\",r,null,u,g),barYPosition:d,x:n,y:o}}},{key:\"drawStackedColumnPaths\",value:function(t){var e=t.indexes,i=t.x,a=t.y,s=t.xDivision,r=t.barWidth,n=t.zeroH,o=(t.strokeWidth,t.elSeries),l=this.w,c=e.i,h=e.j,d=e.bc;if(l.globals.isXNumeric){var u=l.globals.seriesX[c][h];u||(u=0),i=(u-l.globals.minX)/this.xRatio-r/2}for(var g,f=i,p=0,x=0;x<this.prevYF.length;x++)p+=isNaN(this.prevYF[x][h])?0:this.prevYF[x][h];if(c>0&&!l.globals.isXNumeric||c>0&&l.globals.isXNumeric&&l.globals.seriesX[c-1][h]===l.globals.seriesX[c][h]){var b,v,m=Math.min(this.yRatio.length+1,c+1);if(void 0!==this.prevY[c-1])for(var y=1;y<m;y++)if(!isNaN(this.prevY[c-y][h])){v=this.prevY[c-y][h];break}for(var w=1;w<m;w++){if(this.prevYVal[c-w][h]<0){b=this.series[c][h]>=0?v-p+2*(this.isReversed?p:0):v;break}if(this.prevYVal[c-w][h]>=0){b=this.series[c][h]>=0?v:v+p-2*(this.isReversed?p:0);break}}void 0===b&&(b=l.globals.gridHeight),g=this.prevYF[0].every((function(t){return 0===t}))&&this.prevYF.slice(1,c).every((function(t){return t.every((function(t){return isNaN(t)}))}))?n:b}else g=n;a=this.series[c][h]?g-this.series[c][h]/this.yRatio[this.yaxisIndex]+2*(this.isReversed?this.series[c][h]/this.yRatio[this.yaxisIndex]:0):g;var k=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:r,y1:g,y2:a,yRatio:this.yRatio[this.yaxisIndex],strokeWidth:this.strokeWidth,series:this.series,realIndex:e.realIndex,i:c,j:h,w:l});return this.barHelpers.barBackground({bc:d,j:h,i:c,x1:f,x2:r,elSeries:o}),i+=s,{pathTo:k.pathTo,pathFrom:k.pathFrom,goalY:this.barHelpers.getGoalValues(\"y\",null,n,c,h),barXPosition:f,x:l.globals.isXNumeric?i-s:i,y:a}}}])&&vi(e.prototype,i),o}(ui);const Ai=ki;function Si(t){return Si=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},Si(t)}function Ci(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function Pi(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?Ci(Object(i),!0).forEach((function(e){Li(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):Ci(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function Li(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function Oi(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}function Ti(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function Ei(t,e){return Ei=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},Ei(t,e)}function Ii(t,e){if(e&&(\"object\"===Si(e)||\"function\"==typeof e))return e;if(void 0!==e)throw new TypeError(\"Derived constructors may only return object or undefined\");return function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t)}function Mi(t){return Mi=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},Mi(t)}var zi=function(t){!function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&Ei(t,e)}(o,t);var e,i,a,s,r=(a=o,s=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}(),function(){var t,e=Mi(a);if(s){var i=Mi(this).constructor;t=Reflect.construct(e,arguments,i)}else t=e.apply(this,arguments);return Ii(this,t)});function o(){return Oi(this,o),r.apply(this,arguments)}return e=o,(i=[{key:\"draw\",value:function(t,e){var i=this,a=this.w,s=new b(this.ctx),r=new st(this.ctx);this.candlestickOptions=this.w.config.plotOptions.candlestick,this.boxOptions=this.w.config.plotOptions.boxPlot,this.isHorizontal=a.config.plotOptions.bar.horizontal;var o=new y(this.ctx,a);t=o.getLogSeries(t),this.series=t,this.yRatio=o.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);for(var l=s.group({class:\"apexcharts-\".concat(a.config.chart.type,\"-series apexcharts-plot-series\")}),c=function(o){i.isBoxPlot=\"boxPlot\"===a.config.chart.type||\"boxPlot\"===a.config.series[o].type;var c,h,d,u,g,f,p=void 0,x=void 0,b=[],v=[],m=a.globals.comboCharts?e[o]:o,y=s.group({class:\"apexcharts-series\",seriesName:n.escapeString(a.globals.seriesNames[m]),rel:o+1,\"data:realIndex\":m});i.ctx.series.addCollapsedClassToSeries(y,m),t[o].length>0&&(i.visibleI=i.visibleI+1),i.yRatio.length>1&&(i.yaxisIndex=m);var w=i.barHelpers.initialPositions();x=w.y,g=w.barHeight,h=w.yDivision,u=w.zeroW,p=w.x,f=w.barWidth,c=w.xDivision,d=w.zeroH,v.push(p+f/2);for(var k=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":m}),A=function(e){var s=i.barHelpers.getStrokeWidth(o,e,m),n=null,l={indexes:{i:o,j:e,realIndex:m},x:p,y:x,strokeWidth:s,elSeries:y};n=i.isHorizontal?i.drawHorizontalBoxPaths(Pi(Pi({},l),{},{yDivision:h,barHeight:g,zeroW:u})):i.drawVerticalBoxPaths(Pi(Pi({},l),{},{xDivision:c,barWidth:f,zeroH:d})),x=n.y,p=n.x,e>0&&v.push(p+f/2),b.push(x),n.pathTo.forEach((function(l,c){var h=!i.isBoxPlot&&i.candlestickOptions.wick.useFillColor?n.color[c]:a.globals.stroke.colors[o],d=r.fillPath({seriesNumber:m,dataPointIndex:e,color:n.color[c],value:t[o][e]});i.renderSeries({realIndex:m,pathFill:d,lineFill:h,j:e,i:o,pathFrom:n.pathFrom,pathTo:l,strokeWidth:s,elSeries:y,x:p,y:x,series:t,barHeight:g,barWidth:f,elDataLabelsWrap:k,visibleSeries:i.visibleI,type:a.config.chart.type})}))},S=0;S<a.globals.dataPoints;S++)A(S);a.globals.seriesXvalues[m]=v,a.globals.seriesYvalues[m]=b,l.add(y)},h=0;h<t.length;h++)c(h);return l}},{key:\"drawVerticalBoxPaths\",value:function(t){var e=t.indexes,i=t.x,a=(t.y,t.xDivision),s=t.barWidth,r=t.zeroH,n=t.strokeWidth,o=this.w,l=new b(this.ctx),c=e.i,h=e.j,d=!0,u=o.config.plotOptions.candlestick.colors.upward,g=o.config.plotOptions.candlestick.colors.downward,f=\"\";this.isBoxPlot&&(f=[this.boxOptions.colors.lower,this.boxOptions.colors.upper]);var p=this.yRatio[this.yaxisIndex],x=e.realIndex,v=this.getOHLCValue(x,h),m=r,y=r;v.o>v.c&&(d=!1);var w=Math.min(v.o,v.c),k=Math.max(v.o,v.c),A=v.m;o.globals.isXNumeric&&(i=(o.globals.seriesX[x][h]-o.globals.minX)/this.xRatio-s/2);var S=i+s*this.visibleI;void 0===this.series[c][h]||null===this.series[c][h]?(w=r,k=r):(w=r-w/p,k=r-k/p,m=r-v.h/p,y=r-v.l/p,A=r-v.m/p);var C=l.move(S,r),P=l.move(S+s/2,w);return o.globals.previousPaths.length>0&&(P=this.getPreviousPath(x,h,!0)),C=this.isBoxPlot?[l.move(S,w)+l.line(S+s/2,w)+l.line(S+s/2,m)+l.line(S+s/4,m)+l.line(S+s-s/4,m)+l.line(S+s/2,m)+l.line(S+s/2,w)+l.line(S+s,w)+l.line(S+s,A)+l.line(S,A)+l.line(S,w+n/2),l.move(S,A)+l.line(S+s,A)+l.line(S+s,k)+l.line(S+s/2,k)+l.line(S+s/2,y)+l.line(S+s-s/4,y)+l.line(S+s/4,y)+l.line(S+s/2,y)+l.line(S+s/2,k)+l.line(S,k)+l.line(S,A)+\"z\"]:[l.move(S,k)+l.line(S+s/2,k)+l.line(S+s/2,m)+l.line(S+s/2,k)+l.line(S+s,k)+l.line(S+s,w)+l.line(S+s/2,w)+l.line(S+s/2,y)+l.line(S+s/2,w)+l.line(S,w)+l.line(S,k-n/2)],P+=l.move(S,w),o.globals.isXNumeric||(i+=a),{pathTo:C,pathFrom:P,x:i,y:k,barXPosition:S,color:this.isBoxPlot?f:d?[u]:[g]}}},{key:\"drawHorizontalBoxPaths\",value:function(t){var e=t.indexes,i=(t.x,t.y),a=t.yDivision,s=t.barHeight,r=t.zeroW,n=t.strokeWidth,o=this.w,l=new b(this.ctx),c=e.i,h=e.j,d=this.boxOptions.colors.lower;this.isBoxPlot&&(d=[this.boxOptions.colors.lower,this.boxOptions.colors.upper]);var u=this.invertedYRatio,g=e.realIndex,f=this.getOHLCValue(g,h),p=r,x=r,v=Math.min(f.o,f.c),m=Math.max(f.o,f.c),y=f.m;o.globals.isXNumeric&&(i=(o.globals.seriesX[g][h]-o.globals.minX)/this.invertedXRatio-s/2);var w=i+s*this.visibleI;void 0===this.series[c][h]||null===this.series[c][h]?(v=r,m=r):(v=r+v/u,m=r+m/u,p=r+f.h/u,x=r+f.l/u,y=r+f.m/u);var k=l.move(r,w),A=l.move(v,w+s/2);return o.globals.previousPaths.length>0&&(A=this.getPreviousPath(g,h,!0)),k=[l.move(v,w)+l.line(v,w+s/2)+l.line(p,w+s/2)+l.line(p,w+s/2-s/4)+l.line(p,w+s/2+s/4)+l.line(p,w+s/2)+l.line(v,w+s/2)+l.line(v,w+s)+l.line(y,w+s)+l.line(y,w)+l.line(v+n/2,w),l.move(y,w)+l.line(y,w+s)+l.line(m,w+s)+l.line(m,w+s/2)+l.line(x,w+s/2)+l.line(x,w+s-s/4)+l.line(x,w+s/4)+l.line(x,w+s/2)+l.line(m,w+s/2)+l.line(m,w)+l.line(y,w)+\"z\"],A+=l.move(v,w),o.globals.isXNumeric||(i+=a),{pathTo:k,pathFrom:A,x:m,y:i,barYPosition:w,color:d}}},{key:\"getOHLCValue\",value:function(t,e){var i=this.w;return{o:this.isBoxPlot?i.globals.seriesCandleH[t][e]:i.globals.seriesCandleO[t][e],h:this.isBoxPlot?i.globals.seriesCandleO[t][e]:i.globals.seriesCandleH[t][e],m:i.globals.seriesCandleM[t][e],l:this.isBoxPlot?i.globals.seriesCandleC[t][e]:i.globals.seriesCandleL[t][e],c:this.isBoxPlot?i.globals.seriesCandleL[t][e]:i.globals.seriesCandleC[t][e]}}}])&&Ti(e.prototype,i),o}(ui);const Xi=zi;function Yi(t){return function(t){if(Array.isArray(t))return Di(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||function(t,e){if(t){if(\"string\"==typeof t)return Di(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Di(t,e):void 0}}(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function Di(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function Ri(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Fi=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"checkColorRange\",value:function(){var t=this.w,e=!1,i=t.config.plotOptions[t.config.chart.type];return i.colorScale.ranges.length>0&&i.colorScale.ranges.map((function(t,i){t.from<=0&&(e=!0)})),e}},{key:\"getShadeColor\",value:function(t,e,i,a){var s=this.w,r=1,o=s.config.plotOptions[t].shadeIntensity,l=this.determineColor(t,e,i);s.globals.hasNegs||a?r=s.config.plotOptions[t].reverseNegativeShade?l.percent<0?l.percent/100*(1.25*o):(1-l.percent/100)*(1.25*o):l.percent<=0?1-(1+l.percent/100)*o:(1-l.percent/100)*o:(r=1-l.percent/100,\"treemap\"===t&&(r=(1-l.percent/100)*(1.25*o)));var c=l.color,h=new n;return s.config.plotOptions[t].enableShades&&(c=\"dark\"===this.w.config.theme.mode?n.hexToRgba(h.shadeColor(-1*r,l.color),s.config.fill.opacity):n.hexToRgba(h.shadeColor(r,l.color),s.config.fill.opacity)),{color:c,colorProps:l}}},{key:\"determineColor\",value:function(t,e,i){var a=this.w,s=a.globals.series[e][i],r=a.config.plotOptions[t],n=r.colorScale.inverse?i:e;r.distributed&&\"treemap\"===a.config.chart.type&&(n=i);var o=a.globals.colors[n],l=null,c=Math.min.apply(Math,Yi(a.globals.series[e])),h=Math.max.apply(Math,Yi(a.globals.series[e]));r.distributed||\"heatmap\"!==t||(c=a.globals.minY,h=a.globals.maxY),void 0!==r.colorScale.min&&(c=r.colorScale.min<a.globals.minY?r.colorScale.min:a.globals.minY,h=r.colorScale.max>a.globals.maxY?r.colorScale.max:a.globals.maxY);var d=Math.abs(h)+Math.abs(c),u=100*s/(0===d?d-1e-6:d);return r.colorScale.ranges.length>0&&r.colorScale.ranges.map((function(t,e){if(s>=t.from&&s<=t.to){o=t.color,l=t.foreColor?t.foreColor:null,c=t.from,h=t.to;var i=Math.abs(h)+Math.abs(c);u=100*s/(0===i?i-1e-6:i)}})),{color:o,foreColor:l,percent:u}}},{key:\"calculateDataLabels\",value:function(t){var e=t.text,i=t.x,a=t.y,s=t.i,r=t.j,n=t.colorProps,o=t.fontSize,l=this.w.config.dataLabels,c=new b(this.ctx),h=new ht(this.ctx),d=null;if(l.enabled){d=c.group({class:\"apexcharts-data-labels\"});var u=l.offsetX,g=l.offsetY,f=i+u,p=a+parseFloat(l.style.fontSize)/3+g;h.plotDataLabelsText({x:f,y:p,text:e,i:s,j:r,color:n.foreColor,parent:d,fontSize:o,dataLabelsConfig:l})}return d}},{key:\"addListeners\",value:function(t){var e=new b(this.ctx);t.node.addEventListener(\"mouseenter\",e.pathMouseEnter.bind(this,t)),t.node.addEventListener(\"mouseleave\",e.pathMouseLeave.bind(this,t)),t.node.addEventListener(\"mousedown\",e.pathMouseDown.bind(this,t))}}])&&Ri(e.prototype,i),t}();function Hi(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Ni=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.xRatio=i.xRatio,this.yRatio=i.yRatio,this.dynamicAnim=this.w.config.chart.animations.dynamicAnimation,this.helpers=new Fi(e),this.rectRadius=this.w.config.plotOptions.heatmap.radius,this.strokeWidth=this.w.config.stroke.show?this.w.config.stroke.width:0}var e,i;return e=t,(i=[{key:\"draw\",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:\"apexcharts-heatmap\"});a.attr(\"clip-path\",\"url(#gridRectMask\".concat(e.globals.cuid,\")\"));var s=e.globals.gridWidth/e.globals.dataPoints,r=e.globals.gridHeight/e.globals.series.length,o=0,l=!1;this.negRange=this.helpers.checkColorRange();var c=t.slice();e.config.yaxis[0].reversed&&(l=!0,c.reverse());for(var h=l?0:c.length-1;l?h<c.length:h>=0;l?h++:h--){var u=i.group({class:\"apexcharts-series apexcharts-heatmap-series\",seriesName:n.escapeString(e.globals.seriesNames[h]),rel:h+1,\"data:realIndex\":h});if(this.ctx.series.addCollapsedClassToSeries(u,h),e.config.chart.dropShadow.enabled){var g=e.config.chart.dropShadow;new d(this.ctx).dropShadow(u,g,h)}for(var f=0,p=e.config.plotOptions.heatmap.shadeIntensity,x=0;x<c[h].length;x++){var v=this.helpers.getShadeColor(e.config.chart.type,h,x,this.negRange),m=v.color,y=v.colorProps;\"image\"===e.config.fill.type&&(m=new st(this.ctx).fillPath({seriesNumber:h,dataPointIndex:x,opacity:e.globals.hasNegs?y.percent<0?1-(1+y.percent/100):p+y.percent/100:y.percent/100,patternID:n.randomId(),width:e.config.fill.image.width?e.config.fill.image.width:s,height:e.config.fill.image.height?e.config.fill.image.height:r}));var w=this.rectRadius,k=i.drawRect(f,o,s,r,w);if(k.attr({cx:f,cy:o}),k.node.classList.add(\"apexcharts-heatmap-rect\"),u.add(k),k.attr({fill:m,i:h,index:h,j:x,val:c[h][x],\"stroke-width\":this.strokeWidth,stroke:e.config.plotOptions.heatmap.useFillColorAsStroke?m:e.globals.stroke.colors[0],color:m}),this.helpers.addListeners(k),e.config.chart.animations.enabled&&!e.globals.dataChanged){var A=1;e.globals.resized||(A=e.config.chart.animations.speed),this.animateHeatMap(k,f,o,s,r,A)}if(e.globals.dataChanged){var S=1;if(this.dynamicAnim.enabled&&e.globals.shouldAnimate){S=this.dynamicAnim.speed;var C=e.globals.previousPaths[h]&&e.globals.previousPaths[h][x]&&e.globals.previousPaths[h][x].color;C||(C=\"rgba(255, 255, 255, 0)\"),this.animateHeatColor(k,n.isColorHex(C)?C:n.rgb2hex(C),n.isColorHex(m)?m:n.rgb2hex(m),S)}}var P=(0,e.config.dataLabels.formatter)(e.globals.series[h][x],{value:e.globals.series[h][x],seriesIndex:h,dataPointIndex:x,w:e}),L=this.helpers.calculateDataLabels({text:P,x:f+s/2,y:o+r/2,i:h,j:x,colorProps:y,series:c});null!==L&&u.add(L),f+=s}o+=r,a.add(u)}var O=e.globals.yAxisScale[0].result.slice();e.config.yaxis[0].reversed?O.unshift(\"\"):O.push(\"\"),e.globals.yAxisScale[0].result=O;var T=e.globals.gridHeight/e.globals.series.length;return e.config.yaxis[0].labels.offsetY=-T/2,a}},{key:\"animateHeatMap\",value:function(t,e,i,a,s,r){var n=new l(this.ctx);n.animateRect(t,{x:e+a/2,y:i+s/2,width:0,height:0},{x:e,y:i,width:a,height:s},r,(function(){n.animationCompleted(t)}))}},{key:\"animateHeatColor\",value:function(t,e,i,a){t.attr({fill:e}).animate(a).attr({fill:i})}}])&&Hi(e.prototype,i),t}();function Wi(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var ji=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"drawYAxisTexts\",value:function(t,e,i,a){var s=this.w,r=s.config.yaxis[0],n=s.globals.yLabelFormatters[0];return new b(this.ctx).drawText({x:t+r.labels.offsetX,y:e+r.labels.offsetY,text:n(a,i),textAnchor:\"middle\",fontSize:r.labels.style.fontSize,fontFamily:r.labels.style.fontFamily,foreColor:Array.isArray(r.labels.style.colors)?r.labels.style.colors[i]:r.labels.style.colors})}}])&&Wi(e.prototype,i),t}();function Bi(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const Gi=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animBeginArr=[0],this.animDur=0,this.donutDataLabels=this.w.config.plotOptions.pie.donut.labels,this.lineColorArr=void 0!==i.globals.stroke.colors?i.globals.stroke.colors:i.globals.colors,this.defaultSize=Math.min(i.globals.gridWidth,i.globals.gridHeight),this.centerY=this.defaultSize/2,this.centerX=i.globals.gridWidth/2,\"radialBar\"===i.config.chart.type?this.fullAngle=360:this.fullAngle=Math.abs(i.config.plotOptions.pie.endAngle-i.config.plotOptions.pie.startAngle),this.initialAngle=i.config.plotOptions.pie.startAngle%this.fullAngle,i.globals.radialSize=this.defaultSize/2.05-i.config.stroke.width-(i.config.chart.sparkline.enabled?0:i.config.chart.dropShadow.blur),this.donutSize=i.globals.radialSize*parseInt(i.config.plotOptions.pie.donut.size,10)/100,this.maxY=0,this.sliceLabels=[],this.sliceSizes=[],this.prevSectorAngleArr=[]}var e,i;return e=t,(i=[{key:\"draw\",value:function(t){var e=this,i=this.w,a=new b(this.ctx);if(this.ret=a.group({class:\"apexcharts-pie\"}),i.globals.noData)return this.ret;for(var s=0,r=0;r<t.length;r++)s+=n.negToZero(t[r]);var o=[],l=a.group();0===s&&(s=1e-5),t.forEach((function(t){e.maxY=Math.max(e.maxY,t)})),i.config.yaxis[0].max&&(this.maxY=i.config.yaxis[0].max),\"back\"===i.config.grid.position&&\"polarArea\"===this.chartType&&this.drawPolarElements(this.ret);for(var c=0;c<t.length;c++){var h=this.fullAngle*n.negToZero(t[c])/s;o.push(h),\"polarArea\"===this.chartType?(o[c]=this.fullAngle/t.length,this.sliceSizes.push(i.globals.radialSize*t[c]/this.maxY)):this.sliceSizes.push(i.globals.radialSize)}if(i.globals.dataChanged){for(var d,u=0,g=0;g<i.globals.previousPaths.length;g++)u+=n.negToZero(i.globals.previousPaths[g]);for(var f=0;f<i.globals.previousPaths.length;f++)d=this.fullAngle*n.negToZero(i.globals.previousPaths[f])/u,this.prevSectorAngleArr.push(d)}this.donutSize<0&&(this.donutSize=0);var p=i.config.plotOptions.pie.customScale,x=i.globals.gridWidth/2,v=i.globals.gridHeight/2,m=x-i.globals.gridWidth/2*p,y=v-i.globals.gridHeight/2*p;if(\"donut\"===this.chartType){var w=a.drawCircle(this.donutSize);w.attr({cx:this.centerX,cy:this.centerY,fill:i.config.plotOptions.pie.donut.background?i.config.plotOptions.pie.donut.background:\"transparent\"}),l.add(w)}var k=this.drawArcs(o,t);if(this.sliceLabels.forEach((function(t){k.add(t)})),l.attr({transform:\"translate(\".concat(m,\", \").concat(y,\") scale(\").concat(p,\")\")}),l.add(k),this.ret.add(l),this.donutDataLabels.show){var A=this.renderInnerDataLabels(this.donutDataLabels,{hollowSize:this.donutSize,centerX:this.centerX,centerY:this.centerY,opacity:this.donutDataLabels.show,translateX:m,translateY:y});this.ret.add(A)}return\"front\"===i.config.grid.position&&\"polarArea\"===this.chartType&&this.drawPolarElements(this.ret),this.ret}},{key:\"drawArcs\",value:function(t,e){var i=this.w,a=new d(this.ctx),s=new b(this.ctx),r=new st(this.ctx),o=s.group({class:\"apexcharts-slices\"}),l=this.initialAngle,c=this.initialAngle,h=this.initialAngle,u=this.initialAngle;this.strokeWidth=i.config.stroke.show?i.config.stroke.width:0;for(var g=0;g<t.length;g++){var f=s.group({class:\"apexcharts-series apexcharts-pie-series\",seriesName:n.escapeString(i.globals.seriesNames[g]),rel:g+1,\"data:realIndex\":g});o.add(f),c=u,h=(l=h)+t[g],u=c+this.prevSectorAngleArr[g];var p=h<l?this.fullAngle+h-l:h-l,x=r.fillPath({seriesNumber:g,size:this.sliceSizes[g],value:e[g]}),v=this.getChangedPath(c,u),m=s.drawPath({d:v,stroke:Array.isArray(this.lineColorArr)?this.lineColorArr[g]:this.lineColorArr,strokeWidth:0,fill:x,fillOpacity:i.config.fill.opacity,classes:\"apexcharts-pie-area apexcharts-\".concat(this.chartType.toLowerCase(),\"-slice-\").concat(g)});if(m.attr({index:0,j:g}),a.setSelectionFilter(m,0,g),i.config.chart.dropShadow.enabled){var y=i.config.chart.dropShadow;a.dropShadow(m,y,g)}this.addListeners(m,this.donutDataLabels),b.setAttrs(m.node,{\"data:angle\":p,\"data:startAngle\":l,\"data:strokeWidth\":this.strokeWidth,\"data:value\":e[g]});var w={x:0,y:0};\"pie\"===this.chartType||\"polarArea\"===this.chartType?w=n.polarToCartesian(this.centerX,this.centerY,i.globals.radialSize/1.25+i.config.plotOptions.pie.dataLabels.offset,(l+p/2)%this.fullAngle):\"donut\"===this.chartType&&(w=n.polarToCartesian(this.centerX,this.centerY,(i.globals.radialSize+this.donutSize)/2+i.config.plotOptions.pie.dataLabels.offset,(l+p/2)%this.fullAngle)),f.add(m);var k=0;if(!this.initialAnim||i.globals.resized||i.globals.dataChanged?this.animBeginArr.push(0):(0==(k=p/this.fullAngle*i.config.chart.animations.speed)&&(k=1),this.animDur=k+this.animDur,this.animBeginArr.push(this.animDur)),this.dynamicAnim&&i.globals.dataChanged?this.animatePaths(m,{size:this.sliceSizes[g],endAngle:h,startAngle:l,prevStartAngle:c,prevEndAngle:u,animateStartingPos:!0,i:g,animBeginArr:this.animBeginArr,shouldSetPrevPaths:!0,dur:i.config.chart.animations.dynamicAnimation.speed}):this.animatePaths(m,{size:this.sliceSizes[g],endAngle:h,startAngle:l,i:g,totalItems:t.length-1,animBeginArr:this.animBeginArr,dur:k}),i.config.plotOptions.pie.expandOnClick&&\"polarArea\"!==this.chartType&&m.click(this.pieClicked.bind(this,g)),void 0!==i.globals.selectedDataPoints[0]&&i.globals.selectedDataPoints[0].indexOf(g)>-1&&this.pieClicked(g),i.config.dataLabels.enabled){var A=w.x,S=w.y,C=100*p/this.fullAngle+\"%\";if(0!==p&&i.config.plotOptions.pie.dataLabels.minAngleToShowLabel<t[g]){var P=i.config.dataLabels.formatter;void 0!==P&&(C=P(i.globals.seriesPercent[g][0],{seriesIndex:g,w:i}));var L=i.globals.dataLabels.style.colors[g],O=s.group({class:\"apexcharts-datalabels\"}),T=s.drawText({x:A,y:S,text:C,textAnchor:\"middle\",fontSize:i.config.dataLabels.style.fontSize,fontFamily:i.config.dataLabels.style.fontFamily,fontWeight:i.config.dataLabels.style.fontWeight,foreColor:L});if(O.add(T),i.config.dataLabels.dropShadow.enabled){var E=i.config.dataLabels.dropShadow;a.dropShadow(T,E)}T.node.classList.add(\"apexcharts-pie-label\"),i.config.chart.animations.animate&&!1===i.globals.resized&&(T.node.classList.add(\"apexcharts-pie-label-delay\"),T.node.style.animationDelay=i.config.chart.animations.speed/940+\"s\"),this.sliceLabels.push(O)}}}return o}},{key:\"addListeners\",value:function(t,e){var i=new b(this.ctx);t.node.addEventListener(\"mouseenter\",i.pathMouseEnter.bind(this,t)),t.node.addEventListener(\"mouseleave\",i.pathMouseLeave.bind(this,t)),t.node.addEventListener(\"mouseleave\",this.revertDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener(\"mousedown\",i.pathMouseDown.bind(this,t)),this.donutDataLabels.total.showAlways||(t.node.addEventListener(\"mouseenter\",this.printDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener(\"mousedown\",this.printDataLabelsInner.bind(this,t.node,e)))}},{key:\"animatePaths\",value:function(t,e){var i=this.w,a=e.endAngle<e.startAngle?this.fullAngle+e.endAngle-e.startAngle:e.endAngle-e.startAngle,s=a,r=e.startAngle,n=e.startAngle;void 0!==e.prevStartAngle&&void 0!==e.prevEndAngle&&(r=e.prevEndAngle,s=e.prevEndAngle<e.prevStartAngle?this.fullAngle+e.prevEndAngle-e.prevStartAngle:e.prevEndAngle-e.prevStartAngle),e.i===i.config.series.length-1&&(a+n>this.fullAngle?e.endAngle=e.endAngle-(a+n):a+n<this.fullAngle&&(e.endAngle=e.endAngle+(this.fullAngle-(a+n)))),a===this.fullAngle&&(a=this.fullAngle-.01),this.animateArc(t,r,n,a,s,e)}},{key:\"animateArc\",value:function(t,e,i,a,s,r){var n,o=this,c=this.w,h=new l(this.ctx),d=r.size;(isNaN(e)||isNaN(s))&&(e=i,s=a,r.dur=0);var u=a,g=i,f=e<i?this.fullAngle+e-i:e-i;c.globals.dataChanged&&r.shouldSetPrevPaths&&r.prevEndAngle&&(n=o.getPiePath({me:o,startAngle:r.prevStartAngle,angle:r.prevEndAngle<r.prevStartAngle?this.fullAngle+r.prevEndAngle-r.prevStartAngle:r.prevEndAngle-r.prevStartAngle,size:d}),t.attr({d:n})),0!==r.dur?t.animate(r.dur,c.globals.easing,r.animBeginArr[r.i]).afterAll((function(){\"pie\"!==o.chartType&&\"donut\"!==o.chartType&&\"polarArea\"!==o.chartType||this.animate(c.config.chart.animations.dynamicAnimation.speed).attr({\"stroke-width\":o.strokeWidth}),r.i===c.config.series.length-1&&h.animationCompleted(t)})).during((function(l){u=f+(a-f)*l,r.animateStartingPos&&(u=s+(a-s)*l,g=e-s+(i-(e-s))*l),n=o.getPiePath({me:o,startAngle:g,angle:u,size:d}),t.node.setAttribute(\"data:pathOrig\",n),t.attr({d:n})})):(n=o.getPiePath({me:o,startAngle:g,angle:a,size:d}),r.isTrack||(c.globals.animationEnded=!0),t.node.setAttribute(\"data:pathOrig\",n),t.attr({d:n,\"stroke-width\":o.strokeWidth}))}},{key:\"pieClicked\",value:function(t){var e,i=this.w,a=this,s=a.sliceSizes[t]+(i.config.plotOptions.pie.expandOnClick?4:0),r=i.globals.dom.Paper.select(\".apexcharts-\".concat(a.chartType.toLowerCase(),\"-slice-\").concat(t)).members[0];if(\"true\"!==r.attr(\"data:pieClicked\")){var n=i.globals.dom.baseEl.getElementsByClassName(\"apexcharts-pie-area\");Array.prototype.forEach.call(n,(function(t){t.setAttribute(\"data:pieClicked\",\"false\");var e=t.getAttribute(\"data:pathOrig\");t.setAttribute(\"d\",e)})),r.attr(\"data:pieClicked\",\"true\");var o=parseInt(r.attr(\"data:startAngle\"),10),l=parseInt(r.attr(\"data:angle\"),10);e=a.getPiePath({me:a,startAngle:o,angle:l,size:s}),360!==l&&r.plot(e)}else{r.attr({\"data:pieClicked\":\"false\"}),this.revertDataLabelsInner(r.node,this.donutDataLabels);var c=r.attr(\"data:pathOrig\");r.attr({d:c})}}},{key:\"getChangedPath\",value:function(t,e){var i=\"\";return this.dynamicAnim&&this.w.globals.dataChanged&&(i=this.getPiePath({me:this,startAngle:t,angle:e-t,size:this.size})),i}},{key:\"getPiePath\",value:function(t){var e=t.me,i=t.startAngle,a=t.angle,s=t.size,r=i,o=Math.PI*(r-90)/180,l=a+i;Math.ceil(l)>=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle&&(l=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle-.01),Math.ceil(l)>this.fullAngle&&(l-=this.fullAngle);var c=Math.PI*(l-90)/180,h=e.centerX+s*Math.cos(o),d=e.centerY+s*Math.sin(o),u=e.centerX+s*Math.cos(c),g=e.centerY+s*Math.sin(c),f=n.polarToCartesian(e.centerX,e.centerY,e.donutSize,l),p=n.polarToCartesian(e.centerX,e.centerY,e.donutSize,r),x=a>180?1:0,b=[\"M\",h,d,\"A\",s,s,0,x,1,u,g];return\"donut\"===e.chartType?[].concat(b,[\"L\",f.x,f.y,\"A\",e.donutSize,e.donutSize,0,x,0,p.x,p.y,\"L\",h,d,\"z\"]).join(\" \"):\"pie\"===e.chartType||\"polarArea\"===e.chartType?[].concat(b,[\"L\",e.centerX,e.centerY,\"L\",h,d]).join(\" \"):[].concat(b).join(\" \")}},{key:\"drawPolarElements\",value:function(t){var e=this.w,i=new Lt(this.ctx),a=new b(this.ctx),s=new ji(this.ctx),r=a.group(),n=a.group(),o=i.niceScale(0,Math.ceil(this.maxY),e.config.yaxis[0].tickAmount,0,!0),l=o.result.reverse(),c=o.result.length;this.maxY=o.niceMax;for(var h=e.globals.radialSize,d=h/(c-1),u=0;u<c-1;u++){var g=a.drawCircle(h);if(g.attr({cx:this.centerX,cy:this.centerY,fill:\"none\",\"stroke-width\":e.config.plotOptions.polarArea.rings.strokeWidth,stroke:e.config.plotOptions.polarArea.rings.strokeColor}),e.config.yaxis[0].show){var f=s.drawYAxisTexts(this.centerX,this.centerY-h+parseInt(e.config.yaxis[0].labels.style.fontSize,10)/2,u,l[u]);n.add(f)}r.add(g),h-=d}this.drawSpokes(t),t.add(r),t.add(n)}},{key:\"renderInnerDataLabels\",value:function(t,e){var i=this.w,a=new b(this.ctx),s=a.group({class:\"apexcharts-datalabels-group\",transform:\"translate(\".concat(e.translateX?e.translateX:0,\", \").concat(e.translateY?e.translateY:0,\") scale(\").concat(i.config.plotOptions.pie.customScale,\")\")}),r=t.total.show;s.node.style.opacity=e.opacity;var n,o,l=e.centerX,c=e.centerY;n=void 0===t.name.color?i.globals.colors[0]:t.name.color;var h=t.name.fontSize,d=t.name.fontFamily,u=t.name.fontWeight;o=void 0===t.value.color?i.config.chart.foreColor:t.value.color;var g=t.value.formatter,f=\"\",p=\"\";if(r?(n=t.total.color,h=t.total.fontSize,d=t.total.fontFamily,u=t.total.fontWeight,p=t.total.label,f=t.total.formatter(i)):1===i.globals.series.length&&(f=g(i.globals.series[0],i),p=i.globals.seriesNames[0]),p&&(p=t.name.formatter(p,t.total.show,i)),t.name.show){var x=a.drawText({x:l,y:c+parseFloat(t.name.offsetY),text:p,textAnchor:\"middle\",foreColor:n,fontSize:h,fontWeight:u,fontFamily:d});x.node.classList.add(\"apexcharts-datalabel-label\"),s.add(x)}if(t.value.show){var v=t.name.show?parseFloat(t.value.offsetY)+16:t.value.offsetY,m=a.drawText({x:l,y:c+v,text:f,textAnchor:\"middle\",foreColor:o,fontWeight:t.value.fontWeight,fontSize:t.value.fontSize,fontFamily:t.value.fontFamily});m.node.classList.add(\"apexcharts-datalabel-value\"),s.add(m)}return s}},{key:\"printInnerLabels\",value:function(t,e,i,a){var s,r=this.w;a?s=void 0===t.name.color?r.globals.colors[parseInt(a.parentNode.getAttribute(\"rel\"),10)-1]:t.name.color:r.globals.series.length>1&&t.total.show&&(s=t.total.color);var n=r.globals.dom.baseEl.querySelector(\".apexcharts-datalabel-label\"),o=r.globals.dom.baseEl.querySelector(\".apexcharts-datalabel-value\");i=(0,t.value.formatter)(i,r),a||\"function\"!=typeof t.total.formatter||(i=t.total.formatter(r));var l=e===t.total.label;e=t.name.formatter(e,l,r),null!==n&&(n.textContent=e),null!==o&&(o.textContent=i),null!==n&&(n.style.fill=s)}},{key:\"printDataLabelsInner\",value:function(t,e){var i=this.w,a=t.getAttribute(\"data:value\"),s=i.globals.seriesNames[parseInt(t.parentNode.getAttribute(\"rel\"),10)-1];i.globals.series.length>1&&this.printInnerLabels(e,s,a,t);var r=i.globals.dom.baseEl.querySelector(\".apexcharts-datalabels-group\");null!==r&&(r.style.opacity=1)}},{key:\"drawSpokes\",value:function(t){var e=this,i=this.w,a=new b(this.ctx),s=i.config.plotOptions.polarArea.spokes;if(0!==s.strokeWidth){for(var r=[],o=360/i.globals.series.length,l=0;l<i.globals.series.length;l++)r.push(n.polarToCartesian(this.centerX,this.centerY,i.globals.radialSize,i.config.plotOptions.pie.startAngle+o*l));r.forEach((function(i,r){var n=a.drawLine(i.x,i.y,e.centerX,e.centerY,Array.isArray(s.connectorColors)?s.connectorColors[r]:s.connectorColors);t.add(n)}))}}},{key:\"revertDataLabelsInner\",value:function(t,e,i){var a=this,s=this.w,r=s.globals.dom.baseEl.querySelector(\".apexcharts-datalabels-group\"),n=!1,o=s.globals.dom.baseEl.getElementsByClassName(\"apexcharts-pie-area\"),l=function(t){var i=t.makeSliceOut,s=t.printLabel;Array.prototype.forEach.call(o,(function(t){\"true\"===t.getAttribute(\"data:pieClicked\")&&(i&&(n=!0),s&&a.printDataLabelsInner(t,e))}))};if(l({makeSliceOut:!0,printLabel:!1}),e.total.show&&s.globals.series.length>1)n&&!e.total.showAlways?l({makeSliceOut:!1,printLabel:!0}):this.printInnerLabels(e,e.total.label,e.total.formatter(s));else if(l({makeSliceOut:!1,printLabel:!0}),!n)if(s.globals.selectedDataPoints.length&&s.globals.series.length>1)if(s.globals.selectedDataPoints[0].length>0){var c=s.globals.selectedDataPoints[0],h=s.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(this.chartType.toLowerCase(),\"-slice-\").concat(c));this.printDataLabelsInner(h,e)}else r&&s.globals.selectedDataPoints.length&&0===s.globals.selectedDataPoints[0].length&&(r.style.opacity=0);else r&&s.globals.series.length>1&&(r.style.opacity=0)}}])&&Bi(e.prototype,i),t}();function Vi(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function _i(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?Vi(Object(i),!0).forEach((function(e){Ui(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):Vi(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function Ui(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function qi(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Zi=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animDur=0;var i=this.w;this.graphics=new b(this.ctx),this.lineColorArr=void 0!==i.globals.stroke.colors?i.globals.stroke.colors:i.globals.colors,this.defaultSize=i.globals.svgHeight<i.globals.svgWidth?i.globals.gridHeight+1.5*i.globals.goldenPadding:i.globals.gridWidth,this.isLog=i.config.yaxis[0].logarithmic,this.coreUtils=new y(this.ctx),this.maxValue=this.isLog?this.coreUtils.getLogVal(i.globals.maxY,0):i.globals.maxY,this.minValue=this.isLog?this.coreUtils.getLogVal(this.w.globals.minY,0):i.globals.minY,this.polygons=i.config.plotOptions.radar.polygons,this.strokeWidth=i.config.stroke.show?i.config.stroke.width:0,this.size=this.defaultSize/2.1-this.strokeWidth-i.config.chart.dropShadow.blur,i.config.xaxis.labels.show&&(this.size=this.size-i.globals.xAxisLabelsWidth/1.75),void 0!==i.config.plotOptions.radar.size&&(this.size=i.config.plotOptions.radar.size),this.dataRadiusOfPercent=[],this.dataRadius=[],this.angleArr=[],this.yaxisLabelsTextsPos=[]}var e,i;return e=t,i=[{key:\"draw\",value:function(t){var e=this,i=this.w,a=new st(this.ctx),s=[],r=new ht(this.ctx);t.length&&(this.dataPointsLen=t[i.globals.maxValsInArrayIndex].length),this.disAngle=2*Math.PI/this.dataPointsLen;var o=i.globals.gridWidth/2,l=i.globals.gridHeight/2,c=o+i.config.plotOptions.radar.offsetX,h=l+i.config.plotOptions.radar.offsetY,u=this.graphics.group({class:\"apexcharts-radar-series apexcharts-plot-series\",transform:\"translate(\".concat(c||0,\", \").concat(h||0,\")\")}),g=[],f=null,p=null;if(this.yaxisLabels=this.graphics.group({class:\"apexcharts-yaxis\"}),t.forEach((function(t,o){var l=t.length===i.globals.dataPoints,c=e.graphics.group().attr({class:\"apexcharts-series\",\"data:longestSeries\":l,seriesName:n.escapeString(i.globals.seriesNames[o]),rel:o+1,\"data:realIndex\":o});e.dataRadiusOfPercent[o]=[],e.dataRadius[o]=[],e.angleArr[o]=[],t.forEach((function(t,i){var a=Math.abs(e.maxValue-e.minValue);t+=Math.abs(e.minValue),e.isLog&&(t=e.coreUtils.getLogVal(t,0)),e.dataRadiusOfPercent[o][i]=t/a,e.dataRadius[o][i]=e.dataRadiusOfPercent[o][i]*e.size,e.angleArr[o][i]=i*e.disAngle})),g=e.getDataPointsPos(e.dataRadius[o],e.angleArr[o]);var h=e.createPaths(g,{x:0,y:0});f=e.graphics.group({class:\"apexcharts-series-markers-wrap apexcharts-element-hidden\"}),p=e.graphics.group({class:\"apexcharts-datalabels\",\"data:realIndex\":o}),i.globals.delayedElements.push({el:f.node,index:o});var u={i:o,realIndex:o,animationDelay:o,initialSpeed:i.config.chart.animations.speed,dataChangeSpeed:i.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-radar\",shouldClipToGrid:!1,bindEventsOnPaths:!1,stroke:i.globals.stroke.colors[o],strokeLineCap:i.config.stroke.lineCap},x=null;i.globals.previousPaths.length>0&&(x=e.getPreviousPath(o));for(var b=0;b<h.linePathsTo.length;b++){var v=e.graphics.renderPaths(_i(_i({},u),{},{pathFrom:null===x?h.linePathsFrom[b]:x,pathTo:h.linePathsTo[b],strokeWidth:Array.isArray(e.strokeWidth)?e.strokeWidth[o]:e.strokeWidth,fill:\"none\",drawShadow:!1}));c.add(v);var m=a.fillPath({seriesNumber:o}),y=e.graphics.renderPaths(_i(_i({},u),{},{pathFrom:null===x?h.areaPathsFrom[b]:x,pathTo:h.areaPathsTo[b],strokeWidth:0,fill:m,drawShadow:!1}));if(i.config.chart.dropShadow.enabled){var w=new d(e.ctx),k=i.config.chart.dropShadow;w.dropShadow(y,Object.assign({},k,{noUserSpaceOnUse:!0}),o)}c.add(y)}t.forEach((function(t,a){var s=new nt(e.ctx).getMarkerConfig({cssClass:\"apexcharts-marker\",seriesIndex:o,dataPointIndex:a}),n=e.graphics.drawMarker(g[a].x,g[a].y,s);n.attr(\"rel\",a),n.attr(\"j\",a),n.attr(\"index\",o),n.node.setAttribute(\"default-marker-size\",s.pSize);var l=e.graphics.group({class:\"apexcharts-series-markers\"});l&&l.add(n),f.add(l),c.add(f);var h=i.config.dataLabels;if(h.enabled){var d=h.formatter(i.globals.series[o][a],{seriesIndex:o,dataPointIndex:a,w:i});r.plotDataLabelsText({x:g[a].x,y:g[a].y,text:d,textAnchor:\"middle\",i:o,j:o,parent:p,offsetCorrection:!1,dataLabelsConfig:_i({},h)})}c.add(p)})),s.push(c)})),this.drawPolygons({parent:u}),i.config.xaxis.labels.show){var x=this.drawXAxisTexts();u.add(x)}return s.forEach((function(t){u.add(t)})),u.add(this.yaxisLabels),u}},{key:\"drawPolygons\",value:function(t){for(var e=this,i=this.w,a=t.parent,s=new ji(this.ctx),r=i.globals.yAxisScale[0].result.reverse(),o=r.length,l=[],c=this.size/(o-1),h=0;h<o;h++)l[h]=c*h;l.reverse();var d=[],u=[];l.forEach((function(t,i){var a=n.getPolygonPos(t,e.dataPointsLen),s=\"\";a.forEach((function(t,a){if(0===i){var r=e.graphics.drawLine(t.x,t.y,0,0,Array.isArray(e.polygons.connectorColors)?e.polygons.connectorColors[a]:e.polygons.connectorColors);u.push(r)}0===a&&e.yaxisLabelsTextsPos.push({x:t.x,y:t.y}),s+=t.x+\",\"+t.y+\" \"})),d.push(s)})),d.forEach((function(t,s){var r=e.polygons.strokeColors,n=e.polygons.strokeWidth,o=e.graphics.drawPolygon(t,Array.isArray(r)?r[s]:r,Array.isArray(n)?n[s]:n,i.globals.radarPolygons.fill.colors[s]);a.add(o)})),u.forEach((function(t){a.add(t)})),i.config.yaxis[0].show&&this.yaxisLabelsTextsPos.forEach((function(t,i){var a=s.drawYAxisTexts(t.x,t.y,i,r[i]);e.yaxisLabels.add(a)}))}},{key:\"drawXAxisTexts\",value:function(){var t=this,e=this.w,i=e.config.xaxis.labels,a=this.graphics.group({class:\"apexcharts-xaxis\"}),s=n.getPolygonPos(this.size,this.dataPointsLen);return e.globals.labels.forEach((function(r,n){var o=e.config.xaxis.labels.formatter,l=new ht(t.ctx);if(s[n]){var c=t.getTextPos(s[n],t.size),h=o(r,{seriesIndex:-1,dataPointIndex:n,w:e});l.plotDataLabelsText({x:c.newX,y:c.newY,text:h,textAnchor:c.textAnchor,i:n,j:n,parent:a,color:Array.isArray(i.style.colors)&&i.style.colors[n]?i.style.colors[n]:\"#a8a8a8\",dataLabelsConfig:_i({textAnchor:c.textAnchor,dropShadow:{enabled:!1}},i),offsetCorrection:!1})}})),a}},{key:\"createPaths\",value:function(t,e){var i=this,a=[],s=[],r=[],n=[];if(t.length){s=[this.graphics.move(e.x,e.y)],n=[this.graphics.move(e.x,e.y)];var o=this.graphics.move(t[0].x,t[0].y),l=this.graphics.move(t[0].x,t[0].y);t.forEach((function(e,a){o+=i.graphics.line(e.x,e.y),l+=i.graphics.line(e.x,e.y),a===t.length-1&&(o+=\"Z\",l+=\"Z\")})),a.push(o),r.push(l)}return{linePathsFrom:s,linePathsTo:a,areaPathsFrom:n,areaPathsTo:r}}},{key:\"getTextPos\",value:function(t,e){var i=\"middle\",a=t.x,s=t.y;return Math.abs(t.x)>=10?t.x>0?(i=\"start\",a+=10):t.x<0&&(i=\"end\",a-=10):i=\"middle\",Math.abs(t.y)>=e-10&&(t.y<0?s-=10:t.y>0&&(s+=10)),{textAnchor:i,newX:a,newY:s}}},{key:\"getPreviousPath\",value:function(t){for(var e=this.w,i=null,a=0;a<e.globals.previousPaths.length;a++){var s=e.globals.previousPaths[a];s.paths.length>0&&parseInt(s.realIndex,10)===parseInt(t,10)&&void 0!==e.globals.previousPaths[a].paths[0]&&(i=e.globals.previousPaths[a].paths[0].d)}return i}},{key:\"getDataPointsPos\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.dataPointsLen;t=t||[],e=e||[];for(var a=[],s=0;s<i;s++){var r={};r.x=t[s]*Math.sin(e[s]),r.y=-t[s]*Math.cos(e[s]),a.push(r)}return a}}],i&&qi(e.prototype,i),t}();const $i=Zi;function Ji(t){return Ji=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},Ji(t)}function Qi(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function Ki(t,e){return Ki=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},Ki(t,e)}function ta(t,e){if(e&&(\"object\"===Ji(e)||\"function\"==typeof e))return e;if(void 0!==e)throw new TypeError(\"Derived constructors may only return object or undefined\");return function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t)}function ea(t){return ea=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},ea(t)}const ia=function(t){!function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&Ki(t,e)}(o,t);var e,i,a,s,r=(a=o,s=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}(),function(){var t,e=ea(a);if(s){var i=ea(this).constructor;t=Reflect.construct(e,arguments,i)}else t=e.apply(this,arguments);return ta(this,t)});function o(t){var e;!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,o),(e=r.call(this,t)).ctx=t,e.w=t.w,e.animBeginArr=[0],e.animDur=0;var i=e.w;return e.startAngle=i.config.plotOptions.radialBar.startAngle,e.endAngle=i.config.plotOptions.radialBar.endAngle,e.totalAngle=Math.abs(i.config.plotOptions.radialBar.endAngle-i.config.plotOptions.radialBar.startAngle),e.trackStartAngle=i.config.plotOptions.radialBar.track.startAngle,e.trackEndAngle=i.config.plotOptions.radialBar.track.endAngle,e.donutDataLabels=e.w.config.plotOptions.radialBar.dataLabels,e.radialDataLabels=e.donutDataLabels,e.trackStartAngle||(e.trackStartAngle=e.startAngle),e.trackEndAngle||(e.trackEndAngle=e.endAngle),360===e.endAngle&&(e.endAngle=359.99),e.margin=parseInt(i.config.plotOptions.radialBar.track.margin,10),e}return e=o,(i=[{key:\"draw\",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:\"apexcharts-radialbar\"});if(e.globals.noData)return a;var s=i.group(),r=this.defaultSize/2,n=e.globals.gridWidth/2,o=this.defaultSize/2.05;e.config.chart.sparkline.enabled||(o=o-e.config.stroke.width-e.config.chart.dropShadow.blur);var l=e.globals.fill.colors;if(e.config.plotOptions.radialBar.track.show){var c=this.drawTracks({size:o,centerX:n,centerY:r,colorArr:l,series:t});s.add(c)}var h=this.drawArcs({size:o,centerX:n,centerY:r,colorArr:l,series:t}),d=360;e.config.plotOptions.radialBar.startAngle<0&&(d=this.totalAngle);var u=(360-d)/360;if(e.globals.radialSize=o-o*u,this.radialDataLabels.value.show){var g=Math.max(this.radialDataLabels.value.offsetY,this.radialDataLabels.name.offsetY);e.globals.radialSize+=g*u}return s.add(h.g),\"front\"===e.config.plotOptions.radialBar.hollow.position&&(h.g.add(h.elHollow),h.dataLabels&&h.g.add(h.dataLabels)),a.add(s),a}},{key:\"drawTracks\",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:\"apexcharts-tracks\"}),s=new d(this.ctx),r=new st(this.ctx),n=this.getStrokeWidth(t);t.size=t.size-n/2;for(var o=0;o<t.series.length;o++){var l=i.group({class:\"apexcharts-radialbar-track apexcharts-track\"});a.add(l),l.attr({rel:o+1}),t.size=t.size-n-this.margin;var c=e.config.plotOptions.radialBar.track,h=r.fillPath({seriesNumber:0,size:t.size,fillColors:Array.isArray(c.background)?c.background[o]:c.background,solid:!0}),u=this.trackStartAngle,g=this.trackEndAngle;Math.abs(g)+Math.abs(u)>=360&&(g=360-Math.abs(this.startAngle)-.1);var f=i.drawPath({d:\"\",stroke:h,strokeWidth:n*parseInt(c.strokeWidth,10)/100,fill:\"none\",strokeOpacity:c.opacity,classes:\"apexcharts-radialbar-area\"});if(c.dropShadow.enabled){var p=c.dropShadow;s.dropShadow(f,p)}l.add(f),f.attr(\"id\",\"apexcharts-radialbarTrack-\"+o),this.animatePaths(f,{centerX:t.centerX,centerY:t.centerY,endAngle:g,startAngle:u,size:t.size,i:o,totalItems:2,animBeginArr:0,dur:0,isTrack:!0,easing:e.globals.easing})}return a}},{key:\"drawArcs\",value:function(t){var e=this.w,i=new b(this.ctx),a=new st(this.ctx),s=new d(this.ctx),r=i.group(),o=this.getStrokeWidth(t);t.size=t.size-o/2;var l=e.config.plotOptions.radialBar.hollow.background,c=t.size-o*t.series.length-this.margin*t.series.length-o*parseInt(e.config.plotOptions.radialBar.track.strokeWidth,10)/100/2,h=c-e.config.plotOptions.radialBar.hollow.margin;void 0!==e.config.plotOptions.radialBar.hollow.image&&(l=this.drawHollowImage(t,r,c,l));var u=this.drawHollow({size:h,centerX:t.centerX,centerY:t.centerY,fill:l||\"transparent\"});if(e.config.plotOptions.radialBar.hollow.dropShadow.enabled){var g=e.config.plotOptions.radialBar.hollow.dropShadow;s.dropShadow(u,g)}var f=1;!this.radialDataLabels.total.show&&e.globals.series.length>1&&(f=0);var p=null;this.radialDataLabels.show&&(p=this.renderInnerDataLabels(this.radialDataLabels,{hollowSize:c,centerX:t.centerX,centerY:t.centerY,opacity:f})),\"back\"===e.config.plotOptions.radialBar.hollow.position&&(r.add(u),p&&r.add(p));var x=!1;e.config.plotOptions.radialBar.inverseOrder&&(x=!0);for(var v=x?t.series.length-1:0;x?v>=0:v<t.series.length;x?v--:v++){var m=i.group({class:\"apexcharts-series apexcharts-radial-series\",seriesName:n.escapeString(e.globals.seriesNames[v])});r.add(m),m.attr({rel:v+1,\"data:realIndex\":v}),this.ctx.series.addCollapsedClassToSeries(m,v),t.size=t.size-o-this.margin;var y=a.fillPath({seriesNumber:v,size:t.size,value:t.series[v]}),w=this.startAngle,k=void 0,A=n.negToZero(t.series[v]>100?100:t.series[v])/100,S=Math.round(this.totalAngle*A)+this.startAngle,C=void 0;e.globals.dataChanged&&(k=this.startAngle,C=Math.round(this.totalAngle*n.negToZero(e.globals.previousPaths[v])/100)+k),Math.abs(S)+Math.abs(w)>=360&&(S-=.01),Math.abs(C)+Math.abs(k)>=360&&(C-=.01);var P=S-w,L=Array.isArray(e.config.stroke.dashArray)?e.config.stroke.dashArray[v]:e.config.stroke.dashArray,O=i.drawPath({d:\"\",stroke:y,strokeWidth:o,fill:\"none\",fillOpacity:e.config.fill.opacity,classes:\"apexcharts-radialbar-area apexcharts-radialbar-slice-\"+v,strokeDashArray:L});if(b.setAttrs(O.node,{\"data:angle\":P,\"data:value\":t.series[v]}),e.config.chart.dropShadow.enabled){var T=e.config.chart.dropShadow;s.dropShadow(O,T,v)}s.setSelectionFilter(O,0,v),this.addListeners(O,this.radialDataLabels),m.add(O),O.attr({index:0,j:v});var E=0;!this.initialAnim||e.globals.resized||e.globals.dataChanged||(E=e.config.chart.animations.speed),e.globals.dataChanged&&(E=e.config.chart.animations.dynamicAnimation.speed),this.animDur=E/(1.2*t.series.length)+this.animDur,this.animBeginArr.push(this.animDur),this.animatePaths(O,{centerX:t.centerX,centerY:t.centerY,endAngle:S,startAngle:w,prevEndAngle:C,prevStartAngle:k,size:t.size,i:v,totalItems:2,animBeginArr:this.animBeginArr,dur:E,shouldSetPrevPaths:!0,easing:e.globals.easing})}return{g:r,elHollow:u,dataLabels:p}}},{key:\"drawHollow\",value:function(t){var e=new b(this.ctx).drawCircle(2*t.size);return e.attr({class:\"apexcharts-radialbar-hollow\",cx:t.centerX,cy:t.centerY,r:t.size,fill:t.fill}),e}},{key:\"drawHollowImage\",value:function(t,e,i,a){var s=this.w,r=new st(this.ctx),o=n.randomId(),l=s.config.plotOptions.radialBar.hollow.image;if(s.config.plotOptions.radialBar.hollow.imageClipped)r.clippedImgArea({width:i,height:i,image:l,patternID:\"pattern\".concat(s.globals.cuid).concat(o)}),a=\"url(#pattern\".concat(s.globals.cuid).concat(o,\")\");else{var c=s.config.plotOptions.radialBar.hollow.imageWidth,h=s.config.plotOptions.radialBar.hollow.imageHeight;if(void 0===c&&void 0===h){var d=s.globals.dom.Paper.image(l).loaded((function(e){this.move(t.centerX-e.width/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-e.height/2+s.config.plotOptions.radialBar.hollow.imageOffsetY)}));e.add(d)}else{var u=s.globals.dom.Paper.image(l).loaded((function(e){this.move(t.centerX-c/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-h/2+s.config.plotOptions.radialBar.hollow.imageOffsetY),this.size(c,h)}));e.add(u)}}return a}},{key:\"getStrokeWidth\",value:function(t){var e=this.w;return t.size*(100-parseInt(e.config.plotOptions.radialBar.hollow.size,10))/100/(t.series.length+1)-this.margin}}])&&Qi(e.prototype,i),o}(Gi);function aa(t){return aa=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},aa(t)}function sa(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function ra(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?sa(Object(i),!0).forEach((function(e){na(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):sa(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function na(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function oa(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}function la(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function ca(t,e){return ca=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},ca(t,e)}function ha(t,e){if(e&&(\"object\"===aa(e)||\"function\"==typeof e))return e;if(void 0!==e)throw new TypeError(\"Derived constructors may only return object or undefined\");return function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t)}function da(t){return da=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},da(t)}var ua=function(t){!function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&ca(t,e)}(o,t);var e,i,a,s,r=(a=o,s=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}(),function(){var t,e=da(a);if(s){var i=da(this).constructor;t=Reflect.construct(e,arguments,i)}else t=e.apply(this,arguments);return ha(this,t)});function o(){return oa(this,o),r.apply(this,arguments)}return e=o,(i=[{key:\"draw\",value:function(t,e){var i=this.w,a=new b(this.ctx);this.rangeBarOptions=this.w.config.plotOptions.rangeBar,this.series=t,this.seriesRangeStart=i.globals.seriesRangeStart,this.seriesRangeEnd=i.globals.seriesRangeEnd,this.barHelpers.initVariables(t);for(var s=a.group({class:\"apexcharts-rangebar-series apexcharts-plot-series\"}),r=0;r<t.length;r++){var o,l,c,h=void 0,d=void 0,u=void 0,g=i.globals.comboCharts?e[r]:r,f=a.group({class:\"apexcharts-series\",seriesName:n.escapeString(i.globals.seriesNames[g]),rel:r+1,\"data:realIndex\":g});this.ctx.series.addCollapsedClassToSeries(f,g),t[r].length>0&&(this.visibleI=this.visibleI+1);var p=0,x=0;this.yRatio.length>1&&(this.yaxisIndex=g);var v=this.barHelpers.initialPositions();d=v.y,c=v.zeroW,h=v.x,x=v.barWidth,o=v.xDivision,l=v.zeroH;for(var m=a.group({class:\"apexcharts-datalabels\",\"data:realIndex\":g}),y=a.group({class:\"apexcharts-rangebar-goals-markers\",style:\"pointer-events: none\"}),w=0;w<i.globals.dataPoints;w++){var k=this.barHelpers.getStrokeWidth(r,w,g),A=this.seriesRangeStart[r][w],S=this.seriesRangeEnd[r][w],C=null,P=null,L={x:h,y:d,strokeWidth:k,elSeries:f};if(u=v.yDivision,p=v.barHeight,this.isHorizontal){P=d+p*this.visibleI;var O=this.seriesLen;i.config.plotOptions.bar.rangeBarGroupRows&&(O=1);var T=(u-p*O)/2;if(void 0===i.config.series[r].data[w])break;if(i.config.series[r].data[w].x){var E=this.detectOverlappingBars({i:r,j:w,barYPosition:P,srty:T,barHeight:p,yDivision:u,initPositions:v});p=E.barHeight,P=E.barYPosition}x=(C=this.drawRangeBarPaths(ra({indexes:{i:r,j:w,realIndex:g},barHeight:p,barYPosition:P,zeroW:c,yDivision:u,y1:A,y2:S},L))).barWidth}else p=(C=this.drawRangeColumnPaths(ra({indexes:{i:r,j:w,realIndex:g},zeroH:l,barWidth:x,xDivision:o},L))).barHeight;var I=this.barHelpers.drawGoalLine({barXPosition:C.barXPosition,barYPosition:P,goalX:C.goalX,goalY:C.goalY,barHeight:p,barWidth:x});I&&y.add(I),d=C.y,h=C.x;var M=this.barHelpers.getPathFillColor(t,r,w,g),z=i.globals.stroke.colors[g];this.renderSeries({realIndex:g,pathFill:M,lineFill:z,j:w,i:r,x:h,y:d,y1:A,y2:S,pathFrom:C.pathFrom,pathTo:C.pathTo,strokeWidth:k,elSeries:f,series:t,barHeight:p,barYPosition:P,barWidth:x,elDataLabelsWrap:m,elGoalsMarkers:y,visibleSeries:this.visibleI,type:\"rangebar\"})}s.add(f)}return s}},{key:\"detectOverlappingBars\",value:function(t){var e=t.i,i=t.j,a=t.barYPosition,s=t.srty,r=t.barHeight,n=t.yDivision,o=t.initPositions,l=this.w,c=[],h=l.config.series[e].data[i].rangeName,d=l.config.series[e].data[i].x,u=l.globals.labels.indexOf(d),g=l.globals.seriesRange[e].findIndex((function(t){return t.x===d&&t.overlaps.length>0}));return a=l.config.plotOptions.bar.rangeBarGroupRows?s+n*u:s+r*this.visibleI+n*u,g>-1&&!l.config.plotOptions.bar.rangeBarOverlap&&(c=l.globals.seriesRange[e][g].overlaps).indexOf(h)>-1&&(a=(r=o.barHeight/c.length)*this.visibleI+n*(100-parseInt(this.barOptions.barHeight,10))/100/2+r*(this.visibleI+c.indexOf(h))+n*u),{barYPosition:a,barHeight:r}}},{key:\"drawRangeColumnPaths\",value:function(t){var e=t.indexes,i=t.x,a=(t.strokeWidth,t.xDivision),s=t.barWidth,r=t.zeroH,n=this.w,o=e.i,l=e.j,c=this.yRatio[this.yaxisIndex],h=e.realIndex,d=this.getRangeValue(h,l),u=Math.min(d.start,d.end),g=Math.max(d.start,d.end);n.globals.isXNumeric&&(i=(n.globals.seriesX[o][l]-n.globals.minX)/this.xRatio-s/2);var f=i+s*this.visibleI;void 0===this.series[o][l]||null===this.series[o][l]?u=r:(u=r-u/c,g=r-g/c);var p=Math.abs(g-u),x=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:s,y1:u,y2:g,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,realIndex:e.realIndex,i:h,j:l,w:n});return n.globals.isXNumeric||(i+=a),{pathTo:x.pathTo,pathFrom:x.pathFrom,barHeight:p,x:i,y:g,goalY:this.barHelpers.getGoalValues(\"y\",null,r,o,l),barXPosition:f}}},{key:\"drawRangeBarPaths\",value:function(t){var e=t.indexes,i=t.y,a=t.y1,s=t.y2,r=t.yDivision,n=t.barHeight,o=t.barYPosition,l=t.zeroW,c=this.w,h=l+a/this.invertedYRatio,d=l+s/this.invertedYRatio,u=Math.abs(d-h),g=this.barHelpers.getBarpaths({barYPosition:o,barHeight:n,x1:h,x2:d,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,i:e.realIndex,realIndex:e.realIndex,j:e.j,w:c});return c.globals.isXNumeric||(i+=r),{pathTo:g.pathTo,pathFrom:g.pathFrom,barWidth:u,x:d,goalX:this.barHelpers.getGoalValues(\"x\",l,null,e.realIndex,e.j),y:i}}},{key:\"getRangeValue\",value:function(t,e){var i=this.w;return{start:i.globals.seriesRangeStart[t][e],end:i.globals.seriesRangeEnd[t][e]}}}])&&la(e.prototype,i),o}(ui);const ga=ua;function fa(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var pa=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.w=e.w,this.lineCtx=e}var e,i;return e=t,(i=[{key:\"sameValueSeriesFix\",value:function(t,e){var i=this.w;if((\"gradient\"===i.config.fill.type||\"gradient\"===i.config.fill.type[t])&&new y(this.lineCtx.ctx,i).seriesHaveSameValues(t)){var a=e[t].slice();a[a.length-1]=a[a.length-1]+1e-6,e[t]=a}return e}},{key:\"calculatePoints\",value:function(t){var e=t.series,i=t.realIndex,a=t.x,s=t.y,r=t.i,o=t.j,l=t.prevY,c=this.w,h=[],d=[];if(0===o){var u=this.lineCtx.categoryAxisCorrection+c.config.markers.offsetX;c.globals.isXNumeric&&(u=(c.globals.seriesX[i][0]-c.globals.minX)/this.lineCtx.xRatio+c.config.markers.offsetX),h.push(u),d.push(n.isNumber(e[r][0])?l+c.config.markers.offsetY:null),h.push(a+c.config.markers.offsetX),d.push(n.isNumber(e[r][o+1])?s+c.config.markers.offsetY:null)}else h.push(a+c.config.markers.offsetX),d.push(n.isNumber(e[r][o+1])?s+c.config.markers.offsetY:null);return{x:h,y:d}}},{key:\"checkPreviousPaths\",value:function(t){for(var e=t.pathFromLine,i=t.pathFromArea,a=t.realIndex,s=this.w,r=0;r<s.globals.previousPaths.length;r++){var n=s.globals.previousPaths[r];(\"line\"===n.type||\"area\"===n.type)&&n.paths.length>0&&parseInt(n.realIndex,10)===parseInt(a,10)&&(\"line\"===n.type?(this.lineCtx.appendPathFrom=!1,e=s.globals.previousPaths[r].paths[0].d):\"area\"===n.type&&(this.lineCtx.appendPathFrom=!1,i=s.globals.previousPaths[r].paths[0].d,s.config.stroke.show&&s.globals.previousPaths[r].paths[1]&&(e=s.globals.previousPaths[r].paths[1].d)))}return{pathFromLine:e,pathFromArea:i}}},{key:\"determineFirstPrevY\",value:function(t){var e,i=t.i,a=t.series,s=t.prevY,r=t.lineYPosition,n=this.w;if(void 0!==(null===(e=a[i])||void 0===e?void 0:e[0]))s=(r=n.config.chart.stacked&&i>0?this.lineCtx.prevSeriesY[i-1][0]:this.lineCtx.zeroY)-a[i][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]+2*(this.lineCtx.isReversed?a[i][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]:0);else if(n.config.chart.stacked&&i>0&&void 0===a[i][0])for(var o=i-1;o>=0;o--)if(null!==a[o][0]&&void 0!==a[o][0]){s=r=this.lineCtx.prevSeriesY[o][0];break}return{prevY:s,lineYPosition:r}}}])&&fa(e.prototype,i),t}();function xa(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function ba(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?xa(Object(i),!0).forEach((function(e){va(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):xa(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function va(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function ma(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const ya=function(){function t(e,i,a){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.xyRatios=i,this.pointsChart=!(\"bubble\"!==this.w.config.chart.type&&\"scatter\"!==this.w.config.chart.type)||a,this.scatter=new lt(this.ctx),this.noNegatives=this.w.globals.minX===Number.MAX_VALUE,this.lineHelpers=new pa(this),this.markers=new nt(this.ctx),this.prevSeriesY=[],this.categoryAxisCorrection=0,this.yaxisIndex=0}var e,i;return e=t,(i=[{key:\"draw\",value:function(t,e,i,a){var s=this.w,r=new b(this.ctx),n=s.globals.comboCharts?e:s.config.chart.type,o=r.group({class:\"apexcharts-\".concat(n,\"-series apexcharts-plot-series\")}),l=new y(this.ctx,s);this.yRatio=this.xyRatios.yRatio,this.zRatio=this.xyRatios.zRatio,this.xRatio=this.xyRatios.xRatio,this.baseLineY=this.xyRatios.baseLineY,t=l.getLogSeries(t),this.yRatio=l.getLogYRatios(this.yRatio);for(var c=[],h=0;h<t.length;h++){t=this.lineHelpers.sameValueSeriesFix(h,t);var d=s.globals.comboCharts?i[h]:h;this._initSerieVariables(t,h,d);var u=[],g=[],f=s.globals.padHorizontal+this.categoryAxisCorrection;this.ctx.series.addCollapsedClassToSeries(this.elSeries,d),s.globals.isXNumeric&&s.globals.seriesX.length>0&&(f=(s.globals.seriesX[d][0]-s.globals.minX)/this.xRatio),g.push(f);var p,x=f,v=void 0,m=x,w=this.zeroY,k=this.zeroY;w=this.lineHelpers.determineFirstPrevY({i:h,series:t,prevY:w,lineYPosition:0}).prevY,u.push(w),p=w,\"rangeArea\"===n&&(v=k=this.lineHelpers.determineFirstPrevY({i:h,series:a,prevY:k,lineYPosition:0}).prevY);var A={type:n,series:t,realIndex:d,i:h,x:f,y:1,pX:x,pY:p,pathsFrom:this._calculatePathsFrom({type:n,series:t,i:h,realIndex:d,prevX:m,prevY:w,prevY2:k}),linePaths:[],areaPaths:[],seriesIndex:i,lineYPosition:0,xArrj:g,yArrj:u,seriesRangeEnd:a},S=this._iterateOverDataPoints(ba(ba({},A),{},{iterations:\"rangeArea\"===n?t[h].length-1:void 0,isRangeStart:!0}));if(\"rangeArea\"===n){var C=this._calculatePathsFrom({series:a,i:h,realIndex:d,prevX:m,prevY:k}),P=this._iterateOverDataPoints(ba(ba({},A),{},{series:a,pY:v,pathsFrom:C,iterations:a[h].length-1,isRangeStart:!1}));S.linePaths[0]=P.linePath+S.linePath,S.pathFromLine=P.pathFromLine+S.pathFromLine}this._handlePaths({type:n,realIndex:d,i:h,paths:S}),this.elSeries.add(this.elPointsMain),this.elSeries.add(this.elDataLabelsWrap),c.push(this.elSeries)}if(s.config.chart.stacked)for(var L=c.length;L>0;L--)o.add(c[L-1]);else for(var O=0;O<c.length;O++)o.add(c[O]);return o}},{key:\"_initSerieVariables\",value:function(t,e,i){var a=this.w,s=new b(this.ctx);this.xDivision=a.globals.gridWidth/(a.globals.dataPoints-(\"on\"===a.config.xaxis.tickPlacement?1:0)),this.strokeWidth=Array.isArray(a.config.stroke.width)?a.config.stroke.width[i]:a.config.stroke.width,this.yRatio.length>1&&(this.yaxisIndex=i),this.isReversed=a.config.yaxis[this.yaxisIndex]&&a.config.yaxis[this.yaxisIndex].reversed,this.zeroY=a.globals.gridHeight-this.baseLineY[this.yaxisIndex]-(this.isReversed?a.globals.gridHeight:0)+(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),this.areaBottomY=this.zeroY,(this.zeroY>a.globals.gridHeight||\"end\"===a.config.plotOptions.area.fillTo)&&(this.areaBottomY=a.globals.gridHeight),this.categoryAxisCorrection=this.xDivision/2,this.elSeries=s.group({class:\"apexcharts-series\",seriesName:n.escapeString(a.globals.seriesNames[i])}),this.elPointsMain=s.group({class:\"apexcharts-series-markers-wrap\",\"data:realIndex\":i}),this.elDataLabelsWrap=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":i});var r=t[e].length===a.globals.dataPoints;this.elSeries.attr({\"data:longestSeries\":r,rel:e+1,\"data:realIndex\":i}),this.appendPathFrom=!0}},{key:\"_calculatePathsFrom\",value:function(t){var e,i,a,s,r=t.type,n=t.series,o=t.i,l=t.realIndex,c=t.prevX,h=t.prevY,d=t.prevY2,u=this.w,g=new b(this.ctx);if(null===n[o][0]){for(var f=0;f<n[o].length;f++)if(null!==n[o][f]){c=this.xDivision*f,h=this.zeroY-n[o][f]/this.yRatio[this.yaxisIndex],e=g.move(c,h),i=g.move(c,this.areaBottomY);break}}else e=g.move(c,h),\"rangeArea\"===r&&(e=g.move(c,d)+g.line(c,h)),i=g.move(c,this.areaBottomY)+g.line(c,h);if(a=g.move(-1,this.zeroY)+g.line(-1,this.zeroY),s=g.move(-1,this.zeroY)+g.line(-1,this.zeroY),u.globals.previousPaths.length>0){var p=this.lineHelpers.checkPreviousPaths({pathFromLine:a,pathFromArea:s,realIndex:l});a=p.pathFromLine,s=p.pathFromArea}return{prevX:c,prevY:h,linePath:e,areaPath:i,pathFromLine:a,pathFromArea:s}}},{key:\"_handlePaths\",value:function(t){var e=t.type,i=t.realIndex,a=t.i,s=t.paths,r=this.w,n=new b(this.ctx),o=new st(this.ctx);this.prevSeriesY.push(s.yArrj),r.globals.seriesXvalues[i]=s.xArrj,r.globals.seriesYvalues[i]=s.yArrj;var l=r.config.forecastDataPoints;if(l.count>0&&\"rangeArea\"!==e){var c=r.globals.seriesXvalues[i][r.globals.seriesXvalues[i].length-l.count-1],h=n.drawRect(c,0,r.globals.gridWidth,r.globals.gridHeight,0);r.globals.dom.elForecastMask.appendChild(h.node);var d=n.drawRect(0,0,c,r.globals.gridHeight,0);r.globals.dom.elNonForecastMask.appendChild(d.node)}this.pointsChart||r.globals.delayedElements.push({el:this.elPointsMain.node,index:i});var u={i:a,realIndex:i,animationDelay:a,initialSpeed:r.config.chart.animations.speed,dataChangeSpeed:r.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-\".concat(e)};if(\"area\"===e)for(var g=o.fillPath({seriesNumber:i}),f=0;f<s.areaPaths.length;f++){var p=n.renderPaths(ba(ba({},u),{},{pathFrom:s.pathFromArea,pathTo:s.areaPaths[f],stroke:\"none\",strokeWidth:0,strokeLineCap:null,fill:g}));this.elSeries.add(p)}if(r.config.stroke.show&&!this.pointsChart){var x=null;if(\"line\"===e)x=o.fillPath({seriesNumber:i,i:a});else if(\"solid\"===r.config.stroke.fill.type)x=r.globals.stroke.colors[i];else{var v=r.config.fill;r.config.fill=r.config.stroke.fill,x=o.fillPath({seriesNumber:i,i:a}),r.config.fill=v}for(var m=0;m<s.linePaths.length;m++){var y=x;\"rangeArea\"===e&&(y=o.fillPath({seriesNumber:i}));var w=ba(ba({},u),{},{pathFrom:s.pathFromLine,pathTo:s.linePaths[m],stroke:x,strokeWidth:this.strokeWidth,strokeLineCap:r.config.stroke.lineCap,fill:\"rangeArea\"===e?y:\"none\"}),k=n.renderPaths(w);if(this.elSeries.add(k),k.attr(\"fill-rule\",\"evenodd\"),l.count>0&&\"rangeArea\"!==e){var A=n.renderPaths(w);A.node.setAttribute(\"stroke-dasharray\",l.dashArray),l.strokeWidth&&A.node.setAttribute(\"stroke-width\",l.strokeWidth),this.elSeries.add(A),A.attr(\"clip-path\",\"url(#forecastMask\".concat(r.globals.cuid,\")\")),k.attr(\"clip-path\",\"url(#nonForecastMask\".concat(r.globals.cuid,\")\"))}}}}},{key:\"_iterateOverDataPoints\",value:function(t){var e=t.type,i=t.series,a=t.iterations,s=t.realIndex,r=t.i,o=t.x,l=t.y,c=t.pX,h=t.pY,d=t.pathsFrom,u=t.linePaths,g=t.areaPaths,f=t.seriesIndex,p=t.lineYPosition,x=t.xArrj,v=t.yArrj,m=t.isRangeStart,y=t.seriesRangeEnd,w=this.w,k=new b(this.ctx),A=this.yRatio,S=d.prevY,C=d.linePath,P=d.areaPath,L=d.pathFromLine,O=d.pathFromArea,T=n.isNumber(w.globals.minYArr[s])?w.globals.minYArr[s]:w.globals.minY;a||(a=w.globals.dataPoints>1?w.globals.dataPoints-1:w.globals.dataPoints);for(var E=l,I=0;I<a;I++){var M=void 0===i[r][I+1]||null===i[r][I+1];if(w.globals.isXNumeric){var z=w.globals.seriesX[s][I+1];void 0===w.globals.seriesX[s][I+1]&&(z=w.globals.seriesX[s][a-1]),o=(z-w.globals.minX)/this.xRatio}else o+=this.xDivision;p=w.config.chart.stacked&&r>0&&w.globals.collapsedSeries.length<w.config.series.length-1?this.prevSeriesY[function(t){for(var e=t,i=0;i<w.globals.series.length;i++)if(w.globals.collapsedSeriesIndices.indexOf(t)>-1){e--;break}return e>=0?e:0}(r-1)][I+1]:this.zeroY,M?l=p-T/A[this.yaxisIndex]+2*(this.isReversed?T/A[this.yaxisIndex]:0):(l=p-i[r][I+1]/A[this.yaxisIndex]+2*(this.isReversed?i[r][I+1]/A[this.yaxisIndex]:0),\"rangeArea\"===e&&(E=p-y[r][I+1]/A[this.yaxisIndex]+2*(this.isReversed?y[r][I+1]/A[this.yaxisIndex]:0))),x.push(o),v.push(l);var X=this.lineHelpers.calculatePoints({series:i,x:o,y:l,realIndex:s,i:r,j:I,prevY:S}),Y=this._createPaths({type:e,series:i,i:r,realIndex:s,j:I,x:o,y:l,y2:E,pX:c,pY:h,linePath:C,areaPath:P,linePaths:u,areaPaths:g,seriesIndex:f,isRangeStart:m});g=Y.areaPaths,u=Y.linePaths,c=Y.pX,h=Y.pY,P=Y.areaPath,C=Y.linePath,this.appendPathFrom&&(L+=k.line(o,this.zeroY),O+=k.line(o,this.zeroY)),this.handleNullDataPoints(i,X,r,I,s),this._handleMarkersAndLabels({type:e,pointsPos:X,i:r,j:I,realIndex:s,isRangeStart:m})}return{yArrj:v,xArrj:x,pathFromArea:O,areaPaths:g,pathFromLine:L,linePaths:u,linePath:C,areaPath:P}}},{key:\"_handleMarkersAndLabels\",value:function(t){var e=t.type,i=t.pointsPos,a=t.isRangeStart,s=t.i,r=t.j,n=t.realIndex,o=this.w,l=new ht(this.ctx);if(this.pointsChart)this.scatter.draw(this.elSeries,r,{realIndex:n,pointsPos:i,zRatio:this.zRatio,elParent:this.elPointsMain});else{o.globals.series[s].length>1&&this.elPointsMain.node.classList.add(\"apexcharts-element-hidden\");var c=this.markers.plotChartMarkers(i,n,r+1);null!==c&&this.elPointsMain.add(c)}var h=l.drawDataLabel({type:e,isRangeStart:a,pos:i,i:n,j:r+1});null!==h&&this.elDataLabelsWrap.add(h)}},{key:\"_createPaths\",value:function(t){var e=t.type,i=t.series,a=t.i,s=t.realIndex,r=t.j,n=t.x,o=t.y,l=t.y2,c=t.pX,h=t.pY,d=t.linePath,u=t.areaPath,g=t.linePaths,f=t.areaPaths,p=t.seriesIndex,x=t.isRangeStart,v=this.w,m=new b(this.ctx),y=v.config.stroke.curve,w=this.areaBottomY;if(Array.isArray(v.config.stroke.curve)&&(y=Array.isArray(p)?v.config.stroke.curve[p[a]]:v.config.stroke.curve[a]),\"smooth\"===y){var k=.35*(n-c);v.globals.hasNullValues?(null!==i[a][r]&&(null!==i[a][r+1]?(d=m.move(c,h)+m.curve(c+k,h,n-k,o,n+1,o),u=m.move(c+1,h)+m.curve(c+k,h,n-k,o,n+1,o)+m.line(n,w)+m.line(c,w)+\"z\"):(d=m.move(c,h),u=m.move(c,h)+\"z\")),g.push(d),f.push(u)):(d+=m.curve(c+k,h,n-k,o,n,o),u+=m.curve(c+k,h,n-k,o,n,o)),c=n,h=o,r===i[a].length-2&&(u=u+m.curve(c,h,n,o,n,w)+m.move(n,o)+\"z\",\"rangeArea\"===e&&x?d=d+m.curve(c,h,n,o,n,l)+m.move(n,l)+\"z\":v.globals.hasNullValues||(g.push(d),f.push(u)))}else{if(null===i[a][r+1]){d+=m.move(n,o);var A=v.globals.isXNumeric?(v.globals.seriesX[s][r]-v.globals.minX)/this.xRatio:n-this.xDivision;u=u+m.line(A,w)+m.move(n,o)+\"z\"}null===i[a][r]&&(d+=m.move(n,o),u+=m.move(n,w)),\"stepline\"===y?(d=d+m.line(n,null,\"H\")+m.line(null,o,\"V\"),u=u+m.line(n,null,\"H\")+m.line(null,o,\"V\")):\"straight\"===y&&(d+=m.line(n,o),u+=m.line(n,o)),r===i[a].length-2&&(u=u+m.line(n,w)+m.move(n,o)+\"z\",\"rangeArea\"===e&&x?d=d+m.line(n,l)+m.move(n,l)+\"z\":(g.push(d),f.push(u)))}return{linePaths:g,areaPaths:f,pX:c,pY:h,linePath:d,areaPath:u}}},{key:\"handleNullDataPoints\",value:function(t,e,i,a,s){var r=this.w;if(null===t[i][a]&&r.config.markers.showNullDataPoints||1===t[i].length){var n=this.markers.plotChartMarkers(e,s,a+1,this.strokeWidth-r.config.markers.strokeWidth/2,!0);null!==n&&this.elPointsMain.add(n)}}}])&&ma(e.prototype,i),t}();function wa(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}i(600);var ka=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.strokeWidth=this.w.config.stroke.width,this.helpers=new Fi(e),this.dynamicAnim=this.w.config.chart.animations.dynamicAnimation,this.labels=[]}var e,i;return e=t,(i=[{key:\"draw\",value:function(t){var e=this,i=this.w,a=new b(this.ctx),s=new st(this.ctx),r=a.group({class:\"apexcharts-treemap\"});if(i.globals.noData)return r;var o=[];return t.forEach((function(t){var e=t.map((function(t){return Math.abs(t)}));o.push(e)})),this.negRange=this.helpers.checkColorRange(),i.config.series.forEach((function(t,i){t.data.forEach((function(t){Array.isArray(e.labels[i])||(e.labels[i]=[]),e.labels[i].push(t.x)}))})),window.TreemapSquared.generate(o,i.globals.gridWidth,i.globals.gridHeight).forEach((function(o,l){var c=a.group({class:\"apexcharts-series apexcharts-treemap-series\",seriesName:n.escapeString(i.globals.seriesNames[l]),rel:l+1,\"data:realIndex\":l});if(i.config.chart.dropShadow.enabled){var h=i.config.chart.dropShadow;new d(e.ctx).dropShadow(r,h,l)}var u=a.group({class:\"apexcharts-data-labels\"});o.forEach((function(r,n){var o=r[0],h=r[1],d=r[2],u=r[3],g=a.drawRect(o,h,d-o,u-h,0,\"#fff\",1,e.strokeWidth,i.config.plotOptions.treemap.useFillColorAsStroke?p:i.globals.stroke.colors[l]);g.attr({cx:o,cy:h,index:l,i:l,j:n,width:d-o,height:u-h});var f=e.helpers.getShadeColor(i.config.chart.type,l,n,e.negRange),p=f.color;void 0!==i.config.series[l].data[n]&&i.config.series[l].data[n].fillColor&&(p=i.config.series[l].data[n].fillColor);var x=s.fillPath({color:p,seriesNumber:l,dataPointIndex:n});g.node.classList.add(\"apexcharts-treemap-rect\"),g.attr({fill:x}),e.helpers.addListeners(g);var b={x:o+(d-o)/2,y:h+(u-h)/2,width:0,height:0},v={x:o,y:h,width:d-o,height:u-h};if(i.config.chart.animations.enabled&&!i.globals.dataChanged){var m=1;i.globals.resized||(m=i.config.chart.animations.speed),e.animateTreemap(g,b,v,m)}if(i.globals.dataChanged){var y=1;e.dynamicAnim.enabled&&i.globals.shouldAnimate&&(y=e.dynamicAnim.speed,i.globals.previousPaths[l]&&i.globals.previousPaths[l][n]&&i.globals.previousPaths[l][n].rect&&(b=i.globals.previousPaths[l][n].rect),e.animateTreemap(g,b,v,y))}var w=e.getFontSize(r),k=i.config.dataLabels.formatter(e.labels[l][n],{value:i.globals.series[l][n],seriesIndex:l,dataPointIndex:n,w:i}),A=e.helpers.calculateDataLabels({text:k,x:(o+d)/2,y:(h+u)/2+e.strokeWidth/2+w/3,i:l,j:n,colorProps:f,fontSize:w,series:t});i.config.dataLabels.enabled&&A&&e.rotateToFitLabel(A,w,k,o,h,d,u),c.add(g),null!==A&&c.add(A)})),c.add(u),r.add(c)})),r}},{key:\"getFontSize\",value:function(t){var e,i,a=this.w,s=function t(e){var i,a=0;if(Array.isArray(e[0]))for(i=0;i<e.length;i++)a+=t(e[i]);else for(i=0;i<e.length;i++)a+=e[i].length;return a}(this.labels)/function t(e){var i,a=0;if(Array.isArray(e[0]))for(i=0;i<e.length;i++)a+=t(e[i]);else for(i=0;i<e.length;i++)a+=1;return a}(this.labels);return e=(t[2]-t[0])*(t[3]-t[1]),i=Math.pow(e,.5),Math.min(i/s,parseInt(a.config.dataLabels.style.fontSize,10))}},{key:\"rotateToFitLabel\",value:function(t,e,i,a,s,r,n){var o=new b(this.ctx),l=o.getTextRects(i,e);if(l.width+this.w.config.stroke.width+5>r-a&&l.width<=n-s){var c=o.rotateAroundCenter(t.node);t.node.setAttribute(\"transform\",\"rotate(-90 \".concat(c.x,\" \").concat(c.y,\")\"))}}},{key:\"animateTreemap\",value:function(t,e,i,a){var s=new l(this.ctx);s.animateRect(t,{x:e.x,y:e.y,width:e.width,height:e.height},{x:i.x,y:i.y,width:i.width,height:i.height},a,(function(){s.animationCompleted(t)}))}}])&&wa(e.prototype,i),t}();function Aa(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function Sa(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?Aa(Object(i),!0).forEach((function(e){Ca(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):Aa(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function Ca(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function Pa(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}const La=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w,this.timeScaleArray=[],this.utc=this.w.config.xaxis.labels.datetimeUTC}var e,i;return e=t,(i=[{key:\"calculateTimeScaleTicks\",value:function(t,e){var i=this,a=this.w;if(a.globals.allSeriesCollapsed)return a.globals.labels=[],a.globals.timescaleLabels=[],[];var s=new R(this.ctx),r=(e-t)/864e5;this.determineInterval(r),a.globals.disableZoomIn=!1,a.globals.disableZoomOut=!1,r<.00011574074074074075?a.globals.disableZoomIn=!0:r>5e4&&(a.globals.disableZoomOut=!0);var n=s.getTimeUnitsfromTimestamp(t,e,this.utc),o=a.globals.gridWidth/r,l=o/24,c=l/60,h=c/60,d=Math.floor(24*r),u=Math.floor(1440*r),g=Math.floor(86400*r),f=Math.floor(r),p=Math.floor(r/30),x=Math.floor(r/365),b={minMillisecond:n.minMillisecond,minSecond:n.minSecond,minMinute:n.minMinute,minHour:n.minHour,minDate:n.minDate,minMonth:n.minMonth,minYear:n.minYear},v={firstVal:b,currentMillisecond:b.minMillisecond,currentSecond:b.minSecond,currentMinute:b.minMinute,currentHour:b.minHour,currentMonthDate:b.minDate,currentDate:b.minDate,currentMonth:b.minMonth,currentYear:b.minYear,daysWidthOnXAxis:o,hoursWidthOnXAxis:l,minutesWidthOnXAxis:c,secondsWidthOnXAxis:h,numberOfSeconds:g,numberOfMinutes:u,numberOfHours:d,numberOfDays:f,numberOfMonths:p,numberOfYears:x};switch(this.tickInterval){case\"years\":this.generateYearScale(v);break;case\"months\":case\"half_year\":this.generateMonthScale(v);break;case\"months_days\":case\"months_fortnight\":case\"days\":case\"week_days\":this.generateDayScale(v);break;case\"hours\":this.generateHourScale(v);break;case\"minutes_fives\":case\"minutes\":this.generateMinuteScale(v);break;case\"seconds_tens\":case\"seconds_fives\":case\"seconds\":this.generateSecondScale(v)}var m=this.timeScaleArray.map((function(t){var e={position:t.position,unit:t.unit,year:t.year,day:t.day?t.day:1,hour:t.hour?t.hour:0,month:t.month+1};return\"month\"===t.unit?Sa(Sa({},e),{},{day:1,value:t.value+1}):\"day\"===t.unit||\"hour\"===t.unit?Sa(Sa({},e),{},{value:t.value}):\"minute\"===t.unit?Sa(Sa({},e),{},{value:t.value,minute:t.value}):\"second\"===t.unit?Sa(Sa({},e),{},{value:t.value,minute:t.minute,second:t.second}):t}));return m.filter((function(t){var e=1,s=Math.ceil(a.globals.gridWidth/120),r=t.value;void 0!==a.config.xaxis.tickAmount&&(s=a.config.xaxis.tickAmount),m.length>s&&(e=Math.floor(m.length/s));var n=!1,o=!1;switch(i.tickInterval){case\"years\":\"year\"===t.unit&&(n=!0);break;case\"half_year\":e=7,\"year\"===t.unit&&(n=!0);break;case\"months\":e=1,\"year\"===t.unit&&(n=!0);break;case\"months_fortnight\":e=15,\"year\"!==t.unit&&\"month\"!==t.unit||(n=!0),30===r&&(o=!0);break;case\"months_days\":e=10,\"month\"===t.unit&&(n=!0),30===r&&(o=!0);break;case\"week_days\":e=8,\"month\"===t.unit&&(n=!0);break;case\"days\":e=1,\"month\"===t.unit&&(n=!0);break;case\"hours\":\"day\"===t.unit&&(n=!0);break;case\"minutes_fives\":case\"seconds_fives\":r%5!=0&&(o=!0);break;case\"seconds_tens\":r%10!=0&&(o=!0)}if(\"hours\"===i.tickInterval||\"minutes_fives\"===i.tickInterval||\"seconds_tens\"===i.tickInterval||\"seconds_fives\"===i.tickInterval){if(!o)return!0}else if((r%e==0||n)&&!o)return!0}))}},{key:\"recalcDimensionsBasedOnFormat\",value:function(t,e){var i=this.w,a=this.formatDates(t),s=this.removeOverlappingTS(a);i.globals.timescaleLabels=s.slice(),new se(this.ctx).plotCoords()}},{key:\"determineInterval\",value:function(t){var e=24*t,i=60*e;switch(!0){case t/365>5:this.tickInterval=\"years\";break;case t>800:this.tickInterval=\"half_year\";break;case t>180:this.tickInterval=\"months\";break;case t>90:this.tickInterval=\"months_fortnight\";break;case t>60:this.tickInterval=\"months_days\";break;case t>30:this.tickInterval=\"week_days\";break;case t>2:this.tickInterval=\"days\";break;case e>2.4:this.tickInterval=\"hours\";break;case i>15:this.tickInterval=\"minutes_fives\";break;case i>5:this.tickInterval=\"minutes\";break;case i>1:this.tickInterval=\"seconds_tens\";break;case 60*i>20:this.tickInterval=\"seconds_fives\";break;default:this.tickInterval=\"seconds\"}}},{key:\"generateYearScale\",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.daysWidthOnXAxis,r=t.numberOfYears,o=e.minYear,l=0,c=new R(this.ctx),h=\"year\";if(e.minDate>1||e.minMonth>0){var d=c.determineRemainingDaysOfYear(e.minYear,e.minMonth,e.minDate);l=(c.determineDaysOfYear(e.minYear)-d+1)*s,o=e.minYear+1,this.timeScaleArray.push({position:l,value:o,unit:h,year:o,month:n.monthMod(i+1)})}else 1===e.minDate&&0===e.minMonth&&this.timeScaleArray.push({position:l,value:o,unit:h,year:a,month:n.monthMod(i+1)});for(var u=o,g=l,f=0;f<r;f++)u++,g=c.determineDaysOfYear(u-1)*s+g,this.timeScaleArray.push({position:g,value:u,unit:h,year:u,month:1})}},{key:\"generateMonthScale\",value:function(t){var e=t.firstVal,i=t.currentMonthDate,a=t.currentMonth,s=t.currentYear,r=t.daysWidthOnXAxis,o=t.numberOfMonths,l=a,c=0,h=new R(this.ctx),d=\"month\",u=0;if(e.minDate>1){c=(h.determineDaysOfMonths(a+1,e.minYear)-i+1)*r,l=n.monthMod(a+1);var g=s+u,f=n.monthMod(l),p=l;0===l&&(d=\"year\",p=g,f=1,g+=u+=1),this.timeScaleArray.push({position:c,value:p,unit:d,year:g,month:f})}else this.timeScaleArray.push({position:c,value:l,unit:d,year:s,month:n.monthMod(a)});for(var x=l+1,b=c,v=0,m=1;v<o;v++,m++){0===(x=n.monthMod(x))?(d=\"year\",u+=1):d=\"month\";var y=this._getYear(s,x,u);b=h.determineDaysOfMonths(x,y)*r+b;var w=0===x?y:x;this.timeScaleArray.push({position:b,value:w,unit:d,year:y,month:0===x?1:x}),x++}}},{key:\"generateDayScale\",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.hoursWidthOnXAxis,r=t.numberOfDays,o=new R(this.ctx),l=\"day\",c=e.minDate+1,h=c,d=function(t,e,i){return t>o.determineDaysOfMonths(e+1,i)?(h=1,l=\"month\",g=e+=1,e):e},u=(24-e.minHour)*s,g=c,f=d(h,i,a);0===e.minHour&&1===e.minDate?(u=0,g=n.monthMod(e.minMonth),l=\"month\",h=e.minDate,r++):1!==e.minDate&&0===e.minHour&&0===e.minMinute&&(u=0,c=e.minDate,g=c,f=d(h=c,i,a)),this.timeScaleArray.push({position:u,value:g,unit:l,year:this._getYear(a,f,0),month:n.monthMod(f),day:h});for(var p=u,x=0;x<r;x++){l=\"day\",f=d(h+=1,f,this._getYear(a,f,0));var b=this._getYear(a,f,0);p=24*s+p;var v=1===h?n.monthMod(f):h;this.timeScaleArray.push({position:p,value:v,unit:l,year:b,month:n.monthMod(f),day:v})}}},{key:\"generateHourScale\",value:function(t){var e=t.firstVal,i=t.currentDate,a=t.currentMonth,s=t.currentYear,r=t.minutesWidthOnXAxis,o=t.numberOfHours,l=new R(this.ctx),c=\"hour\",h=function(t,e){return t>l.determineDaysOfMonths(e+1,s)&&(x=1,e+=1),{month:e,date:x}},d=function(t,e){return t>l.determineDaysOfMonths(e+1,s)?e+=1:e},u=60-(e.minMinute+e.minSecond/60),g=u*r,f=e.minHour+1,p=f+1;60===u&&(g=0,p=(f=e.minHour)+1);var x=i,b=d(x,a);this.timeScaleArray.push({position:g,value:f,unit:c,day:x,hour:p,year:s,month:n.monthMod(b)});for(var v=g,m=0;m<o;m++){c=\"hour\",p>=24&&(p=0,c=\"day\",b=h(x+=1,b).month,b=d(x,b));var y=this._getYear(s,b,0);v=60*r+v;var w=0===p?x:p;this.timeScaleArray.push({position:v,value:w,unit:c,hour:p,day:x,year:y,month:n.monthMod(b)}),p++}}},{key:\"generateMinuteScale\",value:function(t){for(var e=t.currentMillisecond,i=t.currentSecond,a=t.currentMinute,s=t.currentHour,r=t.currentDate,o=t.currentMonth,l=t.currentYear,c=t.minutesWidthOnXAxis,h=t.secondsWidthOnXAxis,d=t.numberOfMinutes,u=a+1,g=r,f=o,p=l,x=s,b=(60-i-e/1e3)*h,v=0;v<d;v++)u>=60&&(u=0,24===(x+=1)&&(x=0)),this.timeScaleArray.push({position:b,value:u,unit:\"minute\",hour:x,minute:u,day:g,year:this._getYear(p,f,0),month:n.monthMod(f)}),b+=c,u++}},{key:\"generateSecondScale\",value:function(t){for(var e=t.currentMillisecond,i=t.currentSecond,a=t.currentMinute,s=t.currentHour,r=t.currentDate,o=t.currentMonth,l=t.currentYear,c=t.secondsWidthOnXAxis,h=t.numberOfSeconds,d=i+1,u=a,g=r,f=o,p=l,x=s,b=(1e3-e)/1e3*c,v=0;v<h;v++)d>=60&&(d=0,++u>=60&&(u=0,24==++x&&(x=0))),this.timeScaleArray.push({position:b,value:d,unit:\"second\",hour:x,minute:u,second:d,day:g,year:this._getYear(p,f,0),month:n.monthMod(f)}),b+=c,d++}},{key:\"createRawDateString\",value:function(t,e){var i=t.year;return 0===t.month&&(t.month=1),i+=\"-\"+(\"0\"+t.month.toString()).slice(-2),\"day\"===t.unit?i+=\"day\"===t.unit?\"-\"+(\"0\"+e).slice(-2):\"-01\":i+=\"-\"+(\"0\"+(t.day?t.day:\"1\")).slice(-2),\"hour\"===t.unit?i+=\"hour\"===t.unit?\"T\"+(\"0\"+e).slice(-2):\"T00\":i+=\"T\"+(\"0\"+(t.hour?t.hour:\"0\")).slice(-2),\"minute\"===t.unit?i+=\":\"+(\"0\"+e).slice(-2):i+=\":\"+(t.minute?(\"0\"+t.minute).slice(-2):\"00\"),\"second\"===t.unit?i+=\":\"+(\"0\"+e).slice(-2):i+=\":00\",this.utc&&(i+=\".000Z\"),i}},{key:\"formatDates\",value:function(t){var e=this,i=this.w;return t.map((function(t){var a=t.value.toString(),s=new R(e.ctx),r=e.createRawDateString(t,a),n=s.getDate(s.parseDate(r));if(e.utc||(n=s.getDate(s.parseDateWithTimezone(r))),void 0===i.config.xaxis.labels.format){var o=\"dd MMM\",l=i.config.xaxis.labels.datetimeFormatter;\"year\"===t.unit&&(o=l.year),\"month\"===t.unit&&(o=l.month),\"day\"===t.unit&&(o=l.day),\"hour\"===t.unit&&(o=l.hour),\"minute\"===t.unit&&(o=l.minute),\"second\"===t.unit&&(o=l.second),a=s.formatDate(n,o)}else a=s.formatDate(n,i.config.xaxis.labels.format);return{dateString:r,position:t.position,value:a,unit:t.unit,year:t.year,month:t.month}}))}},{key:\"removeOverlappingTS\",value:function(t){var e,i=this,a=new b(this.ctx),s=!1;t.length>0&&t[0].value&&t.every((function(e){return e.value.length===t[0].value.length}))&&(s=!0,e=a.getTextRects(t[0].value).width);var r=0,n=t.map((function(n,o){if(o>0&&i.w.config.xaxis.labels.hideOverlappingLabels){var l=s?e:a.getTextRects(t[r].value).width,c=t[r].position;return n.position>c+l+10?(r=o,n):null}return n}));return n.filter((function(t){return null!==t}))}},{key:\"_getYear\",value:function(t,e,i){return t+Math.floor(e/12)+i}}])&&Pa(e.prototype,i),t}();function Oa(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function Ta(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?Oa(Object(i),!0).forEach((function(e){Ea(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):Oa(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function Ea(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function Ia(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}function Ma(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var za=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=i,this.w=i.w,this.el=e}var e,i;return e=t,(i=[{key:\"setupElements\",value:function(){var t=this.w.globals,e=this.w.config,i=e.chart.type;t.axisCharts=[\"line\",\"area\",\"bar\",\"rangeBar\",\"rangeArea\",\"candlestick\",\"boxPlot\",\"scatter\",\"bubble\",\"radar\",\"heatmap\",\"treemap\"].indexOf(i)>-1,t.xyCharts=[\"line\",\"area\",\"bar\",\"rangeBar\",\"rangeArea\",\"candlestick\",\"boxPlot\",\"scatter\",\"bubble\"].indexOf(i)>-1,t.isBarHorizontal=(\"bar\"===e.chart.type||\"rangeBar\"===e.chart.type||\"boxPlot\"===e.chart.type)&&e.plotOptions.bar.horizontal,t.chartClass=\".apexcharts\"+t.chartID,t.dom.baseEl=this.el,t.dom.elWrap=document.createElement(\"div\"),b.setAttrs(t.dom.elWrap,{id:t.chartClass.substring(1),class:\"apexcharts-canvas \"+t.chartClass.substring(1)}),this.el.appendChild(t.dom.elWrap),t.dom.Paper=new window.SVG.Doc(t.dom.elWrap),t.dom.Paper.attr({class:\"apexcharts-svg\",\"xmlns:data\":\"ApexChartsNS\",transform:\"translate(\".concat(e.chart.offsetX,\", \").concat(e.chart.offsetY,\")\")}),t.dom.Paper.node.style.background=e.chart.background,this.setSVGDimensions(),t.dom.elGraphical=t.dom.Paper.group().attr({class:\"apexcharts-inner apexcharts-graphical\"}),t.dom.elAnnotations=t.dom.Paper.group().attr({class:\"apexcharts-annotations\"}),t.dom.elDefs=t.dom.Paper.defs(),t.dom.elLegendWrap=document.createElement(\"div\"),t.dom.elLegendWrap.classList.add(\"apexcharts-legend\"),t.dom.elWrap.appendChild(t.dom.elLegendWrap),t.dom.Paper.add(t.dom.elGraphical),t.dom.elGraphical.add(t.dom.elDefs)}},{key:\"plotChartType\",value:function(t,e){var i=this.w,a=i.config,s=i.globals,r={series:[],i:[]},n={series:[],i:[]},o={series:[],i:[]},l={series:[],i:[]},c={series:[],i:[]},h={series:[],i:[]},d={series:[],i:[]},u={series:[],i:[]},g={series:[],seriesRangeEnd:[],i:[]};s.series.map((function(e,f){var p=0;void 0!==t[f].type?(\"column\"===t[f].type||\"bar\"===t[f].type?(s.series.length>1&&a.plotOptions.bar.horizontal&&console.warn(\"Horizontal bars are not supported in a mixed/combo chart. Please turn off `plotOptions.bar.horizontal`\"),c.series.push(e),c.i.push(f),p++,i.globals.columnSeries=c.series):\"area\"===t[f].type?(n.series.push(e),n.i.push(f),p++):\"line\"===t[f].type?(r.series.push(e),r.i.push(f),p++):\"scatter\"===t[f].type?(o.series.push(e),o.i.push(f)):\"bubble\"===t[f].type?(l.series.push(e),l.i.push(f),p++):\"candlestick\"===t[f].type?(h.series.push(e),h.i.push(f),p++):\"boxPlot\"===t[f].type?(d.series.push(e),d.i.push(f),p++):\"rangeBar\"===t[f].type?(u.series.push(e),u.i.push(f),p++):\"rangeArea\"===t[f].type?(g.series.push(s.seriesRangeStart[f]),g.seriesRangeEnd.push(s.seriesRangeEnd[f]),g.i.push(f),p++):console.warn(\"You have specified an unrecognized chart type. Available types for this property are line/area/column/bar/scatter/bubble\"),p>1&&(s.comboCharts=!0)):(r.series.push(e),r.i.push(f))}));var f=new ya(this.ctx,e),p=new Xi(this.ctx,e);this.ctx.pie=new Gi(this.ctx);var x=new ia(this.ctx);this.ctx.rangeBar=new ga(this.ctx,e);var b=new $i(this.ctx),v=[];if(s.comboCharts){if(n.series.length>0&&v.push(f.draw(n.series,\"area\",n.i)),c.series.length>0)if(i.config.chart.stacked){var m=new Ai(this.ctx,e);v.push(m.draw(c.series,c.i))}else this.ctx.bar=new ui(this.ctx,e),v.push(this.ctx.bar.draw(c.series,c.i));if(g.series.length>0&&v.push(f.draw(g.series,\"rangeArea\",g.i,g.seriesRangeEnd)),r.series.length>0&&v.push(f.draw(r.series,\"line\",r.i)),h.series.length>0&&v.push(p.draw(h.series,h.i)),d.series.length>0&&v.push(p.draw(d.series,d.i)),u.series.length>0&&v.push(this.ctx.rangeBar.draw(u.series,u.i)),o.series.length>0){var y=new ya(this.ctx,e,!0);v.push(y.draw(o.series,\"scatter\",o.i))}if(l.series.length>0){var w=new ya(this.ctx,e,!0);v.push(w.draw(l.series,\"bubble\",l.i))}}else switch(a.chart.type){case\"line\":v=f.draw(s.series,\"line\");break;case\"area\":v=f.draw(s.series,\"area\");break;case\"bar\":a.chart.stacked?v=new Ai(this.ctx,e).draw(s.series):(this.ctx.bar=new ui(this.ctx,e),v=this.ctx.bar.draw(s.series));break;case\"candlestick\":case\"boxPlot\":v=new Xi(this.ctx,e).draw(s.series);break;case\"rangeBar\":v=this.ctx.rangeBar.draw(s.series);break;case\"rangeArea\":v=f.draw(s.seriesRangeStart,\"rangeArea\",void 0,s.seriesRangeEnd);break;case\"heatmap\":v=new Ni(this.ctx,e).draw(s.series);break;case\"treemap\":v=new ka(this.ctx,e).draw(s.series);break;case\"pie\":case\"donut\":case\"polarArea\":v=this.ctx.pie.draw(s.series);break;case\"radialBar\":v=x.draw(s.series);break;case\"radar\":v=b.draw(s.series);break;default:v=f.draw(s.series)}return v}},{key:\"setSVGDimensions\",value:function(){var t=this.w.globals,e=this.w.config;t.svgWidth=e.chart.width,t.svgHeight=e.chart.height;var i=n.getDimensions(this.el),a=e.chart.width.toString().split(/[0-9]+/g).pop();\"%\"===a?n.isNumber(i[0])&&(0===i[0].width&&(i=n.getDimensions(this.el.parentNode)),t.svgWidth=i[0]*parseInt(e.chart.width,10)/100):\"px\"!==a&&\"\"!==a||(t.svgWidth=parseInt(e.chart.width,10));var s=e.chart.height.toString().split(/[0-9]+/g).pop();if(\"auto\"!==t.svgHeight&&\"\"!==t.svgHeight)if(\"%\"===s){var r=n.getDimensions(this.el.parentNode);t.svgHeight=r[1]*parseInt(e.chart.height,10)/100}else t.svgHeight=parseInt(e.chart.height,10);else t.axisCharts?t.svgHeight=t.svgWidth/1.61:t.svgHeight=t.svgWidth/1.2;if(t.svgWidth<0&&(t.svgWidth=0),t.svgHeight<0&&(t.svgHeight=0),b.setAttrs(t.dom.Paper.node,{width:t.svgWidth,height:t.svgHeight}),\"%\"!==s){var o=e.chart.sparkline.enabled?0:t.axisCharts?e.chart.parentHeightOffset:0;t.dom.Paper.node.parentNode.parentNode.style.minHeight=t.svgHeight+o+\"px\"}t.dom.elWrap.style.width=t.svgWidth+\"px\",t.dom.elWrap.style.height=t.svgHeight+\"px\"}},{key:\"shiftGraphPosition\",value:function(){var t=this.w.globals,e=t.translateY,i={transform:\"translate(\"+t.translateX+\", \"+e+\")\"};b.setAttrs(t.dom.elGraphical.node,i)}},{key:\"resizeNonAxisCharts\",value:function(){var t=this.w,e=t.globals,i=0,a=t.config.chart.sparkline.enabled?1:15;a+=t.config.grid.padding.bottom,\"top\"!==t.config.legend.position&&\"bottom\"!==t.config.legend.position||!t.config.legend.show||t.config.legend.floating||(i=new le(this.ctx).legendHelpers.getLegendBBox().clwh+10);var s=t.globals.dom.baseEl.querySelector(\".apexcharts-radialbar, .apexcharts-pie\"),r=2.05*t.globals.radialSize;if(s&&!t.config.chart.sparkline.enabled&&0!==t.config.plotOptions.radialBar.startAngle){var o=n.getBoundingClientRect(s);r=o.bottom;var l=o.bottom-o.top;r=Math.max(2.05*t.globals.radialSize,l)}var c=r+e.translateY+i+a;e.dom.elLegendForeign&&e.dom.elLegendForeign.setAttribute(\"height\",c),t.config.chart.height&&String(t.config.chart.height).indexOf(\"%\")>0||(e.dom.elWrap.style.height=c+\"px\",b.setAttrs(e.dom.Paper.node,{height:c}),e.dom.Paper.node.parentNode.parentNode.style.minHeight=c+\"px\")}},{key:\"coreCalculations\",value:function(){new Et(this.ctx).init()}},{key:\"resetGlobals\",value:function(){var t=this,e=function(){return t.w.config.series.map((function(t){return[]}))},i=new J,a=this.w.globals;i.initGlobalVars(a),a.seriesXvalues=e(),a.seriesYvalues=e()}},{key:\"isMultipleY\",value:function(){if(this.w.config.yaxis.constructor===Array&&this.w.config.yaxis.length>1)return this.w.globals.isMultipleYAxis=!0,!0}},{key:\"xySettings\",value:function(){var t=null,e=this.w;if(e.globals.axisCharts){if(\"back\"===e.config.xaxis.crosshairs.position&&new Nt(this.ctx).drawXCrosshairs(),\"back\"===e.config.yaxis[0].crosshairs.position&&new Nt(this.ctx).drawYCrosshairs(),\"datetime\"===e.config.xaxis.type&&void 0===e.config.xaxis.labels.formatter){this.ctx.timeScale=new La(this.ctx);var i=[];isFinite(e.globals.minX)&&isFinite(e.globals.maxX)&&!e.globals.isBarHorizontal?i=this.ctx.timeScale.calculateTimeScaleTicks(e.globals.minX,e.globals.maxX):e.globals.isBarHorizontal&&(i=this.ctx.timeScale.calculateTimeScaleTicks(e.globals.minY,e.globals.maxY)),this.ctx.timeScale.recalcDimensionsBasedOnFormat(i)}t=new y(this.ctx).getCalculatedRatios()}return t}},{key:\"updateSourceChart\",value:function(t){this.ctx.w.globals.selection=void 0,this.ctx.updateHelpers._updateOptions({chart:{selection:{xaxis:{min:t.w.globals.minX,max:t.w.globals.maxX}}}},!1,!1)}},{key:\"setupBrushHandler\",value:function(){var t=this,e=this.w;if(e.config.chart.brush.enabled&&\"function\"!=typeof e.config.chart.events.selection){var i=e.config.chart.brush.targets||[e.config.chart.brush.target];i.forEach((function(e){var i=ApexCharts.getChartByID(e);i.w.globals.brushSource=t.ctx,\"function\"!=typeof i.w.config.chart.events.zoomed&&(i.w.config.chart.events.zoomed=function(){t.updateSourceChart(i)}),\"function\"!=typeof i.w.config.chart.events.scrolled&&(i.w.config.chart.events.scrolled=function(){t.updateSourceChart(i)})})),e.config.chart.events.selection=function(t,a){i.forEach((function(t){var i=ApexCharts.getChartByID(t),s=n.clone(e.config.yaxis);if(e.config.chart.brush.autoScaleYaxis&&1===i.w.globals.series.length){var r=new Lt(i);s=r.autoScaleY(i,s,a)}var o=i.w.config.yaxis.reduce((function(t,e,a){return[].concat(function(t){if(Array.isArray(t))return Ia(t)}(r=t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(r)||function(t,e){if(t){if(\"string\"==typeof t)return Ia(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Ia(t,e):void 0}}(r)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}(),[Ta(Ta({},i.w.config.yaxis[a]),{},{min:s[0].min,max:s[0].max})]);var r}),[]);i.ctx.updateHelpers._updateOptions({xaxis:{min:a.xaxis.min,max:a.xaxis.max},yaxis:o},!1,!1,!1,!1)}))}}}}])&&Ma(e.prototype,i),t}();function Xa(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function Ya(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?Xa(Object(i),!0).forEach((function(e){Da(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):Xa(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function Da(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function Ra(t){return Ra=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},Ra(t)}function Fa(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Ha=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,i=[{key:\"_updateOptions\",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],a=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],s=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=arguments.length>4&&void 0!==arguments[4]&&arguments[4];return new Promise((function(o){var l=[e.ctx];s&&(l=e.ctx.getSyncedCharts()),e.ctx.w.globals.isExecCalled&&(l=[e.ctx],e.ctx.w.globals.isExecCalled=!1),l.forEach((function(s,c){var h=s.w;if(h.globals.shouldAnimate=a,i||(h.globals.resized=!0,h.globals.dataChanged=!0,a&&s.series.getPreviousPaths()),t&&\"object\"===Ra(t)&&(s.config=new Z(t),t=y.extendArrayProps(s.config,t,h),s.w.globals.chartID!==e.ctx.w.globals.chartID&&delete t.series,h.config=n.extend(h.config,t),r&&(h.globals.lastXAxis=t.xaxis?n.clone(t.xaxis):[],h.globals.lastYAxis=t.yaxis?n.clone(t.yaxis):[],h.globals.initialConfig=n.extend({},h.config),h.globals.initialSeries=n.clone(h.config.series),t.series))){for(var d=0;d<h.globals.collapsedSeriesIndices.length;d++){var u=h.config.series[h.globals.collapsedSeriesIndices[d]];h.globals.collapsedSeries[d].data=h.globals.axisCharts?u.data.slice():u}for(var g=0;g<h.globals.ancillaryCollapsedSeriesIndices.length;g++){var f=h.config.series[h.globals.ancillaryCollapsedSeriesIndices[g]];h.globals.ancillaryCollapsedSeries[g].data=h.globals.axisCharts?f.data.slice():f}s.series.emptyCollapsedSeries(h.config.series)}return s.update(t).then((function(){c===l.length-1&&o(s)}))}))}))}},{key:\"_updateSeries\",value:function(t,e){var i=this,a=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return new Promise((function(s){var r,o=i.w;return o.globals.shouldAnimate=e,o.globals.dataChanged=!0,e&&i.ctx.series.getPreviousPaths(),o.globals.axisCharts?(0===(r=t.map((function(t,e){return i._extendSeries(t,e)}))).length&&(r=[{data:[]}]),o.config.series=r):o.config.series=t.slice(),a&&(o.globals.initialConfig.series=n.clone(o.config.series),o.globals.initialSeries=n.clone(o.config.series)),i.ctx.update().then((function(){s(i.ctx)}))}))}},{key:\"_extendSeries\",value:function(t,e){var i=this.w,a=i.config.series[e];return Ya(Ya({},i.config.series[e]),{},{name:t.name?t.name:a&&a.name,color:t.color?t.color:a&&a.color,type:t.type?t.type:a&&a.type,data:t.data?t.data:a&&a.data})}},{key:\"toggleDataPointSelection\",value:function(t,e){var i=this.w,a=null,s=\".apexcharts-series[data\\\\:realIndex='\".concat(t,\"']\");return i.globals.axisCharts?a=i.globals.dom.Paper.select(\"\".concat(s,\" path[j='\").concat(e,\"'], \").concat(s,\" circle[j='\").concat(e,\"'], \").concat(s,\" rect[j='\").concat(e,\"']\")).members[0]:void 0===e&&(a=i.globals.dom.Paper.select(\"\".concat(s,\" path[j='\").concat(t,\"']\")).members[0],\"pie\"!==i.config.chart.type&&\"polarArea\"!==i.config.chart.type&&\"donut\"!==i.config.chart.type||this.ctx.pie.pieClicked(t)),a?(new b(this.ctx).pathMouseDown(a,null),a.node?a.node:null):(console.warn(\"toggleDataPointSelection: Element not found\"),null)}},{key:\"forceXAxisUpdate\",value:function(t){var e=this.w;if([\"min\",\"max\"].forEach((function(i){void 0!==t.xaxis[i]&&(e.config.xaxis[i]=t.xaxis[i],e.globals.lastXAxis[i]=t.xaxis[i])})),t.xaxis.categories&&t.xaxis.categories.length&&(e.config.xaxis.categories=t.xaxis.categories),e.config.xaxis.convertedCatToNumeric){var i=new _(t);t=i.convertCatToNumericXaxis(t,this.ctx)}return t}},{key:\"forceYAxisUpdate\",value:function(t){return t.chart&&t.chart.stacked&&\"100%\"===t.chart.stackType&&(Array.isArray(t.yaxis)?t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})):(t.yaxis.min=0,t.yaxis.max=100)),t}},{key:\"revertDefaultAxisMinMax\",value:function(t){var e=this,i=this.w,a=i.globals.lastXAxis,s=i.globals.lastYAxis;t&&t.xaxis&&(a=t.xaxis),t&&t.yaxis&&(s=t.yaxis),i.config.xaxis.min=a.min,i.config.xaxis.max=a.max;i.config.yaxis.map((function(t,a){i.globals.zoomed||void 0!==s[a]?function(t){void 0!==s[t]&&(i.config.yaxis[t].min=s[t].min,i.config.yaxis[t].max=s[t].max)}(a):void 0!==e.ctx.opts.yaxis[a]&&(t.min=e.ctx.opts.yaxis[a].min,t.max=e.ctx.opts.yaxis[a].max)}))}}],i&&Fa(e.prototype,i),t}();function Na(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}i(482),i(872),i(25),i(372),i(769),i(882),void 0===window.Apex&&(window.Apex={});var Wa=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"initModules\",value:function(){this.ctx.publicMethods=[\"updateOptions\",\"updateSeries\",\"appendData\",\"appendSeries\",\"toggleSeries\",\"showSeries\",\"hideSeries\",\"setLocale\",\"resetSeries\",\"zoomX\",\"toggleDataPointSelection\",\"dataURI\",\"exportToCSV\",\"addXaxisAnnotation\",\"addYaxisAnnotation\",\"addPointAnnotation\",\"clearAnnotations\",\"removeAnnotation\",\"paper\",\"destroy\"],this.ctx.eventList=[\"click\",\"mousedown\",\"mousemove\",\"mouseleave\",\"touchstart\",\"touchmove\",\"touchleave\",\"mouseup\",\"touchend\"],this.ctx.animations=new l(this.ctx),this.ctx.axes=new Ft(this.ctx),this.ctx.core=new za(this.ctx.el,this.ctx),this.ctx.config=new Z({}),this.ctx.data=new ft(this.ctx),this.ctx.grid=new Ct(this.ctx),this.ctx.graphics=new b(this.ctx),this.ctx.coreUtils=new y(this.ctx),this.ctx.crosshairs=new Nt(this.ctx),this.ctx.events=new Xt(this.ctx),this.ctx.exports=new yt(this.ctx),this.ctx.localization=new Dt(this.ctx),this.ctx.options=new I,this.ctx.responsive=new jt(this.ctx),this.ctx.series=new ut(this.ctx),this.ctx.theme=new Gt(this.ctx),this.ctx.formatters=new H(this.ctx),this.ctx.titleSubtitle=new _t(this.ctx),this.ctx.legend=new le(this.ctx),this.ctx.toolbar=new Se(this.ctx),this.ctx.tooltip=new Ke(this.ctx),this.ctx.dimensions=new se(this.ctx),this.ctx.updateHelpers=new Ha(this.ctx),this.ctx.zoomPanSelection=new Ee(this.ctx),this.ctx.w.globals.tooltip=new Ke(this.ctx)}}])&&Na(e.prototype,i),t}();function ja(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var Ba=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.ctx=e,this.w=e.w}var e,i;return e=t,(i=[{key:\"clear\",value:function(t){var e=t.isUpdating;this.ctx.zoomPanSelection&&this.ctx.zoomPanSelection.destroy(),this.ctx.toolbar&&this.ctx.toolbar.destroy(),this.ctx.animations=null,this.ctx.axes=null,this.ctx.annotations=null,this.ctx.core=null,this.ctx.data=null,this.ctx.grid=null,this.ctx.series=null,this.ctx.responsive=null,this.ctx.theme=null,this.ctx.formatters=null,this.ctx.titleSubtitle=null,this.ctx.legend=null,this.ctx.dimensions=null,this.ctx.options=null,this.ctx.crosshairs=null,this.ctx.zoomPanSelection=null,this.ctx.updateHelpers=null,this.ctx.toolbar=null,this.ctx.localization=null,this.ctx.w.globals.tooltip=null,this.clearDomElements({isUpdating:e})}},{key:\"killSVG\",value:function(t){t.each((function(t,e){this.removeClass(\"*\"),this.off(),this.stop()}),!0),t.ungroup(),t.clear()}},{key:\"clearDomElements\",value:function(t){var e=this,i=t.isUpdating,a=this.w.globals.dom.Paper.node;a.parentNode&&a.parentNode.parentNode&&!i&&(a.parentNode.parentNode.style.minHeight=\"unset\");var s=this.w.globals.dom.baseEl;s&&this.ctx.eventList.forEach((function(t){s.removeEventListener(t,e.ctx.events.documentEvent)}));var r=this.w.globals.dom;if(null!==this.ctx.el)for(;this.ctx.el.firstChild;)this.ctx.el.removeChild(this.ctx.el.firstChild);this.killSVG(r.Paper),r.Paper.remove(),r.elWrap=null,r.elGraphical=null,r.elAnnotations=null,r.elLegendWrap=null,r.baseEl=null,r.elGridRect=null,r.elGridRectMask=null,r.elGridRectMarkerMask=null,r.elForecastMask=null,r.elNonForecastMask=null,r.elDefs=null}}])&&ja(e.prototype,i),t}(),Ga=new WeakMap,Va=i(274),_a=i.n(Va);function Ua(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}var qa=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}(this,t),this.opts=i,this.ctx=this,this.w=new K(i).init(),this.el=e,this.w.globals.cuid=n.randomId(),this.w.globals.chartID=this.w.config.chart.id?n.escapeString(this.w.config.chart.id):this.w.globals.cuid,new Wa(this).initModules(),this.create=n.bind(this.create,this),this.windowResizeHandler=this._windowResizeHandler.bind(this),this.parentResizeHandler=this._parentResizeCallback.bind(this)}var e,i,a;return e=t,i=[{key:\"render\",value:function(){var t=this;return new Promise((function(e,i){if(null!==t.el){void 0===Apex._chartInstances&&(Apex._chartInstances=[]),t.w.config.chart.id&&Apex._chartInstances.push({id:t.w.globals.chartID,group:t.w.config.chart.group,chart:t}),t.setLocale(t.w.config.chart.defaultLocale);var a=t.w.config.chart.events.beforeMount;if(\"function\"==typeof a&&a(t,t.w),t.events.fireEvent(\"beforeMount\",[t,t.w]),window.addEventListener(\"resize\",t.windowResizeHandler),function(t,e){var i=!1;if(t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE){var a=t.getBoundingClientRect();\"none\"!==t.style.display&&0!==a.width||(i=!0)}var s=new ResizeObserver((function(a){i&&e.call(t,a),i=!0}));t.nodeType===Node.DOCUMENT_FRAGMENT_NODE?Array.from(t.children).forEach((function(t){return s.observe(t)})):s.observe(t),Ga.set(e,s)}(t.el.parentNode,t.parentResizeHandler),!t.css){var s=t.el.getRootNode&&t.el.getRootNode(),r=n.is(\"ShadowRoot\",s),o=t.el.ownerDocument,l=o.getElementById(\"apexcharts-css\");!r&&l||(t.css=document.createElement(\"style\"),t.css.id=\"apexcharts-css\",t.css.textContent=_a(),r?s.prepend(t.css):o.head.appendChild(t.css))}var c=t.create(t.w.config.series,{});if(!c)return e(t);t.mount(c).then((function(){\"function\"==typeof t.w.config.chart.events.mounted&&t.w.config.chart.events.mounted(t,t.w),t.events.fireEvent(\"mounted\",[t,t.w]),e(c)})).catch((function(t){i(t)}))}else i(new Error(\"Element not found\"))}))}},{key:\"create\",value:function(t,e){var i=this.w;new Wa(this).initModules();var a=this.w.globals;if(a.noData=!1,a.animationEnded=!1,this.responsive.checkResponsiveConfig(e),i.config.xaxis.convertedCatToNumeric&&new _(i.config).convertCatToNumericXaxis(i.config,this.ctx),null===this.el)return a.animationEnded=!0,null;if(this.core.setupElements(),\"treemap\"===i.config.chart.type&&(i.config.grid.show=!1,i.config.yaxis[0].show=!1),0===a.svgWidth)return a.animationEnded=!0,null;var s=y.checkComboSeries(t);a.comboCharts=s.comboCharts,a.comboBarCount=s.comboBarCount;var r=t.every((function(t){return t.data&&0===t.data.length}));(0===t.length||r)&&this.series.handleNoData(),this.events.setupEventHandlers(),this.data.parseData(t),this.theme.init(),new nt(this).setGlobalMarkerSize(),this.formatters.setLabelFormatters(),this.titleSubtitle.draw(),a.noData&&a.collapsedSeries.length!==a.series.length&&!i.config.legend.showForSingleSeries||this.legend.init(),this.series.hasAllSeriesEqualX(),a.axisCharts&&(this.core.coreCalculations(),\"category\"!==i.config.xaxis.type&&this.formatters.setLabelFormatters(),this.ctx.toolbar.minX=i.globals.minX,this.ctx.toolbar.maxX=i.globals.maxX),this.formatters.heatmapLabelFormatters(),new y(this).getLargestMarkerSize(),this.dimensions.plotCoords();var n=this.core.xySettings();this.grid.createGridMask();var o=this.core.plotChartType(t,n),l=new ht(this);l.bringForward(),i.config.dataLabels.background.enabled&&l.dataLabelsBackground(),this.core.shiftGraphPosition();var c={plot:{left:i.globals.translateX,top:i.globals.translateY,width:i.globals.gridWidth,height:i.globals.gridHeight}};return{elGraph:o,xyRatios:n,elInner:i.globals.dom.elGraphical,dimensions:c}}},{key:\"mount\",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=this,a=i.w;return new Promise((function(s,r){if(null===i.el)return r(new Error(\"Not enough data to display or target element not found\"));(null===e||a.globals.allSeriesCollapsed)&&i.series.handleNoData(),i.grid=new Ct(i);var n=i.grid.drawGrid();if(i.annotations=new z(i),i.annotations.drawImageAnnos(),i.annotations.drawTextAnnos(),\"back\"===a.config.grid.position&&n&&(a.globals.dom.elGraphical.add(n.el),n&&n.elGridBorders&&n.elGridBorders.node&&a.globals.dom.elGraphical.add(n.elGridBorders)),Array.isArray(e.elGraph))for(var o=0;o<e.elGraph.length;o++)a.globals.dom.elGraphical.add(e.elGraph[o]);else a.globals.dom.elGraphical.add(e.elGraph);\"front\"===a.config.grid.position&&n&&(a.globals.dom.elGraphical.add(n.el),n&&n.elGridBorders&&n.elGridBorders.node&&a.globals.dom.elGraphical.add(n.elGridBorders)),\"front\"===a.config.xaxis.crosshairs.position&&i.crosshairs.drawXCrosshairs(),\"front\"===a.config.yaxis[0].crosshairs.position&&i.crosshairs.drawYCrosshairs(),\"treemap\"!==a.config.chart.type&&i.axes.drawAxis(a.config.chart.type,n);var l=new kt(t.ctx,n),c=new Mt(t.ctx,n);if(null!==n&&(l.xAxisLabelCorrections(n.xAxisTickWidth),c.setYAxisTextAlignments(),a.config.yaxis.map((function(t,e){-1===a.globals.ignoreYAxisIndexes.indexOf(e)&&c.yAxisTitleRotate(e,t.opposite)}))),a.globals.dom.Paper.add(a.globals.dom.elAnnotations),i.annotations.drawAxesAnnotations(),!a.globals.noData){if(a.config.tooltip.enabled&&!a.globals.noData&&i.w.globals.tooltip.drawTooltip(e.xyRatios),a.globals.axisCharts&&(a.globals.isXNumeric||a.config.xaxis.convertedCatToNumeric||a.globals.isRangeBar))(a.config.chart.zoom.enabled||a.config.chart.selection&&a.config.chart.selection.enabled||a.config.chart.pan&&a.config.chart.pan.enabled)&&i.zoomPanSelection.init({xyRatios:e.xyRatios});else{var h=a.config.chart.toolbar.tools;[\"zoom\",\"zoomin\",\"zoomout\",\"selection\",\"pan\",\"reset\"].forEach((function(t){h[t]=!1}))}a.config.chart.toolbar.show&&!a.globals.allSeriesCollapsed&&i.toolbar.createToolbar()}a.globals.memory.methodsToExec.length>0&&a.globals.memory.methodsToExec.forEach((function(t){t.method(t.params,!1,t.context)})),a.globals.axisCharts||a.globals.noData||i.core.resizeNonAxisCharts(),s(i)}))}},{key:\"destroy\",value:function(){var t,e;window.removeEventListener(\"resize\",this.windowResizeHandler),this.el.parentNode,t=this.parentResizeHandler,(e=Ga.get(t))&&(e.disconnect(),Ga.delete(t));var i=this.w.config.chart.id;i&&Apex._chartInstances.forEach((function(t,e){t.id===n.escapeString(i)&&Apex._chartInstances.splice(e,1)})),new Ba(this.ctx).clear({isUpdating:!1})}},{key:\"updateOptions\",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],a=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],s=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],n=this.w;return n.globals.selection=void 0,t.series&&(this.series.resetSeries(!1,!0,!1),t.series.length&&t.series[0].data&&(t.series=t.series.map((function(t,i){return e.updateHelpers._extendSeries(t,i)}))),this.updateHelpers.revertDefaultAxisMinMax()),t.xaxis&&(t=this.updateHelpers.forceXAxisUpdate(t)),t.yaxis&&(t=this.updateHelpers.forceYAxisUpdate(t)),n.globals.collapsedSeriesIndices.length>0&&this.series.clearPreviousPaths(),t.theme&&(t=this.theme.updateThemeOptions(t)),this.updateHelpers._updateOptions(t,i,a,s,r)}},{key:\"updateSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(t,e,i)}},{key:\"appendSeries\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w.config.series.slice();return a.push(t),this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(a,e,i)}},{key:\"appendData\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this;i.w.globals.dataChanged=!0,i.series.getPreviousPaths();for(var a=i.w.config.series.slice(),s=0;s<a.length;s++)if(null!==t[s]&&void 0!==t[s])for(var r=0;r<t[s].data.length;r++)a[s].data.push(t[s].data[r]);return i.w.config.series=a,e&&(i.w.globals.initialSeries=n.clone(i.w.config.series)),this.update()}},{key:\"update\",value:function(t){var e=this;return new Promise((function(i,a){new Ba(e.ctx).clear({isUpdating:!0});var s=e.create(e.w.config.series,t);if(!s)return i(e);e.mount(s).then((function(){\"function\"==typeof e.w.config.chart.events.updated&&e.w.config.chart.events.updated(e,e.w),e.events.fireEvent(\"updated\",[e,e.w]),e.w.globals.isDirty=!0,i(e)})).catch((function(t){a(t)}))}))}},{key:\"getSyncedCharts\",value:function(){var t=this.getGroupedCharts(),e=[this];return t.length&&(e=[],t.forEach((function(t){e.push(t)}))),e}},{key:\"getGroupedCharts\",value:function(){var t=this;return Apex._chartInstances.filter((function(t){if(t.group)return!0})).map((function(e){return t.w.config.chart.group===e.group?e.chart:t}))}},{key:\"toggleSeries\",value:function(t){return this.series.toggleSeries(t)}},{key:\"highlightSeriesOnLegendHover\",value:function(t,e){return this.series.toggleSeriesOnHover(t,e)}},{key:\"showSeries\",value:function(t){this.series.showSeries(t)}},{key:\"hideSeries\",value:function(t){this.series.hideSeries(t)}},{key:\"resetSeries\",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.series.resetSeries(t,e)}},{key:\"addEventListener\",value:function(t,e){this.events.addEventListener(t,e)}},{key:\"removeEventListener\",value:function(t,e){this.events.removeEventListener(t,e)}},{key:\"addXaxisAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addXaxisAnnotationExternal(t,e,a)}},{key:\"addYaxisAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addYaxisAnnotationExternal(t,e,a)}},{key:\"addPointAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addPointAnnotationExternal(t,e,a)}},{key:\"clearAnnotations\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,e=this;t&&(e=t),e.annotations.clearAnnotations(e)}},{key:\"removeAnnotation\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,i=this;e&&(i=e),i.annotations.removeAnnotation(i,t)}},{key:\"getChartArea\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-inner\")}},{key:\"getSeriesTotalXRange\",value:function(t,e){return this.coreUtils.getSeriesTotalsXRange(t,e)}},{key:\"getHighestValueInSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new Et(this.ctx);return e.getMinYMaxY(t).highestY}},{key:\"getLowestValueInSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new Et(this.ctx);return e.getMinYMaxY(t).lowestY}},{key:\"getSeriesTotal\",value:function(){return this.w.globals.seriesTotals}},{key:\"toggleDataPointSelection\",value:function(t,e){return this.updateHelpers.toggleDataPointSelection(t,e)}},{key:\"zoomX\",value:function(t,e){this.ctx.toolbar.zoomUpdateOptions(t,e)}},{key:\"setLocale\",value:function(t){this.localization.setCurrentLocaleValues(t)}},{key:\"dataURI\",value:function(t){return new yt(this.ctx).dataURI(t)}},{key:\"exportToCSV\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=new yt(this.ctx);return e.exportToCSV(t)}},{key:\"paper\",value:function(){return this.w.globals.dom.Paper}},{key:\"_parentResizeCallback\",value:function(){this.w.globals.animationEnded&&this.w.config.chart.redrawOnParentResize&&this._windowResize()}},{key:\"_windowResize\",value:function(){var t=this;clearTimeout(this.w.globals.resizeTimer),this.w.globals.resizeTimer=window.setTimeout((function(){t.w.globals.resized=!0,t.w.globals.dataChanged=!1,t.ctx.update()}),150)}},{key:\"_windowResizeHandler\",value:function(){var t=this.w.config.chart.redrawOnWindowResize;\"function\"==typeof t&&(t=t()),t&&this._windowResize()}}],a=[{key:\"getChartByID\",value:function(t){var e=n.escapeString(t),i=Apex._chartInstances.filter((function(t){return t.id===e}))[0];return i&&i.chart}},{key:\"initOnLoad\",value:function(){for(var e=document.querySelectorAll(\"[data-apexcharts]\"),i=0;i<e.length;i++)new t(e[i],JSON.parse(e[i].getAttribute(\"data-options\"))).render()}},{key:\"exec\",value:function(t,e){var i=this.getChartByID(t);if(i){i.w.globals.isExecCalled=!0;var a=null;if(-1!==i.publicMethods.indexOf(e)){for(var s=arguments.length,r=new Array(s>2?s-2:0),n=2;n<s;n++)r[n-2]=arguments[n];a=i[e].apply(i,r)}return a}}},{key:\"merge\",value:function(t,e){return n.extend(t,e)}}],i&&Ua(e.prototype,i),a&&Ua(e,a),t}()},600:()=>{window.TreemapSquared={},function(){\"use strict\";window.TreemapSquared.generate=function(){function t(e,i,a,s){this.xoffset=e,this.yoffset=i,this.height=s,this.width=a,this.shortestEdge=function(){return Math.min(this.height,this.width)},this.getCoordinates=function(t){var e,i=[],a=this.xoffset,s=this.yoffset,n=r(t)/this.height,o=r(t)/this.width;if(this.width>=this.height)for(e=0;e<t.length;e++)i.push([a,s,a+n,s+t[e]/n]),s+=t[e]/n;else for(e=0;e<t.length;e++)i.push([a,s,a+t[e]/o,s+o]),a+=t[e]/o;return i},this.cutArea=function(e){var i;if(this.width>=this.height){var a=e/this.height,s=this.width-a;i=new t(this.xoffset+a,this.yoffset,s,this.height)}else{var r=e/this.width,n=this.height-r;i=new t(this.xoffset,this.yoffset+r,this.width,n)}return i}}function e(e,a,s,n,o){n=void 0===n?0:n,o=void 0===o?0:o;var l=i(function(t,e){var i,a=[],s=e/r(t);for(i=0;i<t.length;i++)a[i]=t[i]*s;return a}(e,a*s),[],new t(n,o,a,s),[]);return function(t){var e,i,a=[];for(e=0;e<t.length;e++)for(i=0;i<t[e].length;i++)a.push(t[e][i]);return a}(l)}function i(t,e,s,n){var o,l,c;if(0!==t.length)return o=s.shortestEdge(),function(t,e,i){var s;return 0===t.length||((s=t.slice()).push(e),a(t,i)>=a(s,i))}(e,l=t[0],o)?(e.push(l),i(t.slice(1),e,s,n)):(c=s.cutArea(r(e),n),n.push(s.getCoordinates(e)),i(t,[],c,n)),n;n.push(s.getCoordinates(e))}function a(t,e){var i=Math.min.apply(Math,t),a=Math.max.apply(Math,t),s=r(t);return Math.max(Math.pow(e,2)*a/Math.pow(s,2),Math.pow(s,2)/(Math.pow(e,2)*i))}function s(t){return t&&t.constructor===Array}function r(t){var e,i=0;for(e=0;e<t.length;e++)i+=t[e];return i}function n(t){var e,i=0;if(s(t[0]))for(e=0;e<t.length;e++)i+=n(t[e]);else i=r(t);return i}return function t(i,a,r,o,l){o=void 0===o?0:o,l=void 0===l?0:l;var c,h,d=[],u=[];if(s(i[0])){for(h=0;h<i.length;h++)d[h]=n(i[h]);for(c=e(d,a,r,o,l),h=0;h<i.length;h++)u.push(t(i[h],c[h][2]-c[h][0],c[h][3]-c[h][1],c[h][0],c[h][1]))}else u=e(i,a,r,o,l);return u}}()}()},482:function(t,e,i){var a,s;function r(t){return r=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},r(t)}s=\"undefined\"!=typeof window?window:this,a=function(){return function(t,e){var i=(void 0!==this?this:t).SVG=function(t){if(i.supported)return t=new i.Doc(t),i.parser.draw||i.prepare(),t};if(i.ns=\"http://www.w3.org/2000/svg\",i.xmlns=\"http://www.w3.org/2000/xmlns/\",i.xlink=\"http://www.w3.org/1999/xlink\",i.svgjs=\"http://svgjs.dev\",i.supported=!0,!i.supported)return!1;i.did=1e3,i.eid=function(t){return\"Svgjs\"+d(t)+i.did++},i.create=function(t){var i=e.createElementNS(this.ns,t);return i.setAttribute(\"id\",this.eid(t)),i},i.extend=function(){var t,e;e=(t=[].slice.call(arguments)).pop();for(var a=t.length-1;a>=0;a--)if(t[a])for(var s in e)t[a].prototype[s]=e[s];i.Set&&i.Set.inherit&&i.Set.inherit()},i.invent=function(t){var e=\"function\"==typeof t.create?t.create:function(){this.constructor.call(this,i.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&i.extend(e,t.extend),t.construct&&i.extend(t.parent||i.Container,t.construct),e},i.adopt=function(e){return e?e.instance?e.instance:((a=\"svg\"==e.nodeName?e.parentNode instanceof t.SVGElement?new i.Nested:new i.Doc:\"linearGradient\"==e.nodeName?new i.Gradient(\"linear\"):\"radialGradient\"==e.nodeName?new i.Gradient(\"radial\"):i[d(e.nodeName)]?new(i[d(e.nodeName)]):new i.Element(e)).type=e.nodeName,a.node=e,e.instance=a,a instanceof i.Doc&&a.namespace().defs(),a.setData(JSON.parse(e.getAttribute(\"svgjs:data\"))||{}),a):null;var a},i.prepare=function(){var t=e.getElementsByTagName(\"body\")[0],a=(t?new i.Doc(t):i.adopt(e.documentElement).nested()).size(2,0);i.parser={body:t||e.documentElement,draw:a.style(\"opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden\").node,poly:a.polyline().node,path:a.path().node,native:i.create(\"svg\")}},i.parser={native:i.create(\"svg\")},e.addEventListener(\"DOMContentLoaded\",(function(){i.parser.draw||i.prepare()}),!1),i.regex={numberAndUnit:/^([+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i,rgb:/rgb\\((\\d+),(\\d+),(\\d+)\\)/,reference:/#([a-z0-9\\-_]+)/i,transforms:/\\)\\s*,?\\s*/,whitespace:/\\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\\s+)?$/,isNumber:/^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?$/i,isPercent:/^-?[\\d\\.]+%$/,isImage:/\\.(jpg|jpeg|png|gif|svg)(\\?[^=]+.*)?/i,delimiter:/[\\s,]+/,hyphen:/([^e])\\-/gi,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,numbersWithDots:/((\\d?\\.\\d+(?:e[+-]?\\d+)?)((?:\\.\\d+(?:e[+-]?\\d+)?)+))+/gi,dots:/\\./g},i.utils={map:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)a.push(e(t[s]));return a},filter:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)e(t[s])&&a.push(t[s]);return a},filterSVGElements:function(e){return this.filter(e,(function(e){return e instanceof t.SVGElement}))}},i.defaults={attrs:{\"fill-opacity\":1,\"stroke-opacity\":1,\"stroke-width\":0,\"stroke-linejoin\":\"miter\",\"stroke-linecap\":\"butt\",fill:\"#000000\",stroke:\"#000000\",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,\"stop-opacity\":1,\"stop-color\":\"#000000\",\"font-size\":16,\"font-family\":\"Helvetica, Arial, sans-serif\",\"text-anchor\":\"start\"}},i.Color=function(t){var e,a;this.r=0,this.g=0,this.b=0,t&&(\"string\"==typeof t?i.regex.isRgb.test(t)?(e=i.regex.rgb.exec(t.replace(i.regex.whitespace,\"\")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):i.regex.isHex.test(t)&&(e=i.regex.hex.exec(4==(a=t).length?[\"#\",a.substring(1,2),a.substring(1,2),a.substring(2,3),a.substring(2,3),a.substring(3,4),a.substring(3,4)].join(\"\"):a),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):\"object\"===r(t)&&(this.r=t.r,this.g=t.g,this.b=t.b))},i.extend(i.Color,{toString:function(){return this.toHex()},toHex:function(){return\"#\"+u(this.r)+u(this.g)+u(this.b)},toRgb:function(){return\"rgb(\"+[this.r,this.g,this.b].join()+\")\"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new i.Color(t),this},at:function(t){return this.destination?(t=t<0?0:t>1?1:t,new i.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),i.Color.test=function(t){return t+=\"\",i.regex.isHex.test(t)||i.regex.isRgb.test(t)},i.Color.isRgb=function(t){return t&&\"number\"==typeof t.r&&\"number\"==typeof t.g&&\"number\"==typeof t.b},i.Color.isColor=function(t){return i.Color.isRgb(t)||i.Color.test(t)},i.Array=function(t,e){0==(t=(t||[]).valueOf()).length&&e&&(t=e.valueOf()),this.value=this.parse(t)},i.extend(i.Array,{toString:function(){return this.value.join(\" \")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)}}),i.PointArray=function(t,e){i.Array.call(this,t,e||[[0,0]])},i.PointArray.prototype=new i.Array,i.PointArray.prototype.constructor=i.PointArray;for(var a={M:function(t,e,i){return e.x=i.x=t[0],e.y=i.y=t[1],[\"M\",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],[\"L\",t[0],t[1]]},H:function(t,e){return e.x=t[0],[\"H\",t[0]]},V:function(t,e){return e.y=t[0],[\"V\",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],[\"C\",t[0],t[1],t[2],t[3],t[4],t[5]]},Q:function(t,e){return e.x=t[2],e.y=t[3],[\"Q\",t[0],t[1],t[2],t[3]]},Z:function(t,e,i){return e.x=i.x,e.y=i.y,[\"Z\"]}},s=\"mlhvqtcsaz\".split(\"\"),n=0,o=s.length;n<o;++n)a[s[n]]=function(t){return function(e,i,s){if(\"H\"==t)e[0]=e[0]+i.x;else if(\"V\"==t)e[0]=e[0]+i.y;else if(\"A\"==t)e[5]=e[5]+i.x,e[6]=e[6]+i.y;else for(var r=0,n=e.length;r<n;++r)e[r]=e[r]+(r%2?i.y:i.x);if(a&&\"function\"==typeof a[t])return a[t](e,i,s)}}(s[n].toUpperCase());i.PathArray=function(t,e){i.Array.call(this,t,e||[[\"M\",0,0]])},i.PathArray.prototype=new i.Array,i.PathArray.prototype.constructor=i.PathArray,i.extend(i.PathArray,{toString:function(){return function(t){for(var e=0,i=t.length,a=\"\";e<i;e++)a+=t[e][0],null!=t[e][1]&&(a+=t[e][1],null!=t[e][2]&&(a+=\" \",a+=t[e][2],null!=t[e][3]&&(a+=\" \",a+=t[e][3],a+=\" \",a+=t[e][4],null!=t[e][5]&&(a+=\" \",a+=t[e][5],a+=\" \",a+=t[e][6],null!=t[e][7]&&(a+=\" \",a+=t[e][7])))));return a+\" \"}(this.value)},move:function(t,e){var i=this.bbox();return i.x,i.y,this},at:function(t){if(!this.destination)return this;for(var e=this.value,a=this.destination.value,s=[],r=new i.PathArray,n=0,o=e.length;n<o;n++){s[n]=[e[n][0]];for(var l=1,c=e[n].length;l<c;l++)s[n][l]=e[n][l]+(a[n][l]-e[n][l])*t;\"A\"===s[n][0]&&(s[n][4]=+(0!=s[n][4]),s[n][5]=+(0!=s[n][5]))}return r.value=s,r},parse:function(t){if(t instanceof i.PathArray)return t.valueOf();var e,s={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7,Z:0};t=\"string\"==typeof t?t.replace(i.regex.numbersWithDots,c).replace(i.regex.pathLetters,\" $& \").replace(i.regex.hyphen,\"$1 -\").trim().split(i.regex.delimiter):t.reduce((function(t,e){return[].concat.call(t,e)}),[]);var r=[],n=new i.Point,o=new i.Point,l=0,h=t.length;do{i.regex.isPathLetter.test(t[l])?(e=t[l],++l):\"M\"==e?e=\"L\":\"m\"==e&&(e=\"l\"),r.push(a[e].call(null,t.slice(l,l+=s[e.toUpperCase()]).map(parseFloat),n,o))}while(h>l);return r},bbox:function(){return i.parser.draw||i.prepare(),i.parser.path.setAttribute(\"d\",this.toString()),i.parser.path.getBBox()}}),i.Number=i.invent({create:function(t,e){this.value=0,this.unit=e||\"\",\"number\"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-34e37:34e37:\"string\"==typeof t?(e=t.match(i.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),\"%\"==e[5]?this.value/=100:\"s\"==e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof i.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return(\"%\"==this.unit?~~(1e8*this.value)/1e6:\"s\"==this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new i.Number(t),new i.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new i.Number(t),new i.Number(this-t,this.unit||t.unit)},times:function(t){return t=new i.Number(t),new i.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new i.Number(t),new i.Number(this/t,this.unit||t.unit)},to:function(t){var e=new i.Number(this);return\"string\"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new i.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new i.Number(this.destination).minus(this).times(t).plus(this):this}}}),i.Element=i.invent({create:function(t){this._stroke=i.defaults.attrs.stroke,this._event=null,this.dom={},(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute(\"stroke\")||this._stroke)},extend:{x:function(t){return this.attr(\"x\",t)},y:function(t){return this.attr(\"y\",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr(\"width\",t)},height:function(t){return this.attr(\"height\",t)},size:function(t,e){var a=g(this,t,e);return this.width(new i.Number(a.width)).height(new i.Number(a.height))},clone:function(t){this.writeDataToDom();var e=x(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},id:function(t){return this.attr(\"id\",t)},show:function(){return this.style(\"display\",\"\")},hide:function(){return this.style(\"display\",\"none\")},visible:function(){return\"none\"!=this.style(\"display\")},toString:function(){return this.attr(\"id\")},classes:function(){var t=this.attr(\"class\");return null==t?[]:t.trim().split(i.regex.delimiter)},hasClass:function(t){return-1!=this.classes().indexOf(t)},addClass:function(t){if(!this.hasClass(t)){var e=this.classes();e.push(t),this.attr(\"class\",e.join(\" \"))}return this},removeClass:function(t){return this.hasClass(t)&&this.attr(\"class\",this.classes().filter((function(e){return e!=t})).join(\" \")),this},toggleClass:function(t){return this.hasClass(t)?this.removeClass(t):this.addClass(t)},reference:function(t){return i.get(this.attr(t))},parent:function(e){var a=this;if(!a.node.parentNode)return null;if(a=i.adopt(a.node.parentNode),!e)return a;for(;a&&a.node instanceof t.SVGElement;){if(\"string\"==typeof e?a.matches(e):a instanceof e)return a;if(!a.node.parentNode||\"#document\"==a.node.parentNode.nodeName)return null;a=i.adopt(a.node.parentNode)}},doc:function(){return this instanceof i.Doc?this:this.parent(i.Doc)},parents:function(t){var e=[],i=this;do{if(!(i=i.parent(t))||!i.node)break;e.push(i)}while(i.parent);return e},matches:function(t){return function(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}(this.node,t)},native:function(){return this.node},svg:function(t){var a=e.createElement(\"svg\");if(!(t&&this instanceof i.Parent))return a.appendChild(t=e.createElement(\"svg\")),this.writeDataToDom(),t.appendChild(this.node.cloneNode(!0)),a.innerHTML.replace(/^<svg>/,\"\").replace(/<\\/svg>$/,\"\");a.innerHTML=\"<svg>\"+t.replace(/\\n/,\"\").replace(/<([\\w:-]+)([^<]+?)\\/>/g,\"<$1$2></$1>\")+\"</svg>\";for(var s=0,r=a.firstChild.childNodes.length;s<r;s++)this.node.appendChild(a.firstChild.firstChild);return this},writeDataToDom:function(){return(this.each||this.lines)&&(this.each?this:this.lines()).each((function(){this.writeDataToDom()})),this.node.removeAttribute(\"svgjs:data\"),Object.keys(this.dom).length&&this.node.setAttribute(\"svgjs:data\",JSON.stringify(this.dom)),this},setData:function(t){return this.dom=t,this},is:function(t){return function(t,e){return t instanceof e}(this,t)}}}),i.easing={\"-\":function(t){return t},\"<>\":function(t){return-Math.cos(t*Math.PI)/2+.5},\">\":function(t){return Math.sin(t*Math.PI/2)},\"<\":function(t){return 1-Math.cos(t*Math.PI/2)}},i.morph=function(t){return function(e,a){return new i.MorphObj(e,a).at(t)}},i.Situation=i.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new i.Number(t.duration).valueOf(),this.delay=new i.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),i.FX=i.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(t,e,a){\"object\"===r(t)&&(e=t.ease,a=t.delay,t=t.duration);var s=new i.Situation({duration:t||1e3,delay:a||0,ease:i.easing[e||\"-\"]||e});return this.queue(s),this},target:function(t){return t&&t instanceof i.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=t.requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){t.cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return(\"function\"==typeof t||t instanceof i.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof i.Situation?this.start():this.situation.call(this)),this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(var a in e.animations){t=this.target()[a](),Array.isArray(t)||(t=[t]),Array.isArray(e.animations[a])||(e.animations[a]=[e.animations[a]]);for(var s=t.length;s--;)e.animations[a][s]instanceof i.Number&&(t[s]=new i.Number(t[s])),e.animations[a][s]=t[s].morph(e.animations[a][s])}for(var a in e.attrs)e.attrs[a]=new i.MorphObj(this.target().attr(a),e.attrs[a]);for(var a in e.styles)e.styles[a]=new i.MorphObj(this.target().style(a),e.styles[a]);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){var i=this.active;return this.active=!1,e&&this.clearQueue(),t&&this.situation&&(!i&&this.startCurrent(),this.atEnd()),this.stopAnimFrame(),this.clearCurrent()},after:function(t){var e=this.last();return this.target().on(\"finished.fx\",(function i(a){a.detail.situation==e&&(t.call(this,e),this.off(\"finished.fx\",i))})),this._callStart()},during:function(t){var e=this.last(),a=function(a){a.detail.situation==e&&t.call(this,a.detail.pos,i.morph(a.detail.pos),a.detail.eased,e)};return this.target().off(\"during.fx\",a).on(\"during.fx\",a),this.after((function(){this.off(\"during.fx\",a)})),this._callStart()},afterAll:function(t){var e=function e(i){t.call(this),this.off(\"allfinished.fx\",e)};return this.target().off(\"allfinished.fx\",e).on(\"allfinished.fx\",e),this._callStart()},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||\"animations\"][t]=e,this._callStart()},step:function(t){var e,i,a;t||(this.absPos=this.timeToAbsPos(+new Date)),!1!==this.situation.loops?(e=Math.max(this.absPos,0),i=Math.floor(e),!0===this.situation.loops||i<this.situation.loops?(this.pos=e-i,a=this.situation.loop,this.situation.loop=i):(this.absPos=this.situation.loops,this.pos=1,a=this.situation.loop-1,this.situation.loop=this.situation.loops),this.situation.reversing&&(this.situation.reversed=this.situation.reversed!=Boolean((this.situation.loop-a)%2))):(this.absPos=Math.min(this.absPos,1),this.pos=this.absPos),this.pos<0&&(this.pos=0),this.situation.reversed&&(this.pos=1-this.pos);var s=this.situation.ease(this.pos);for(var r in this.situation.once)r>this.lastPos&&r<=s&&(this.situation.once[r].call(this.target(),this.pos,s),delete this.situation.once[r]);return this.active&&this.target().fire(\"during\",{pos:this.pos,eased:s,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire(\"finished\",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire(\"allfinished\"),this.situations.length||(this.target().off(\".fx\"),this.active=!1)),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=s,this):this},eachAt:function(){var t,e=this,a=this.target(),s=this.situation;for(var r in s.animations)t=[].concat(s.animations[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),a[r].apply(a,t);for(var r in s.attrs)t=[r].concat(s.attrs[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),a.attr.apply(a,t);for(var r in s.styles)t=[r].concat(s.styles[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),a.style.apply(a,t);if(s.transforms.length){t=s.initialTransformation,r=0;for(var n=s.transforms.length;r<n;r++){var o=s.transforms[r];o instanceof i.Matrix?t=o.relative?t.multiply((new i.Matrix).morph(o).at(s.ease(this.pos))):t.morph(o).at(s.ease(this.pos)):(o.relative||o.undo(t.extract()),t=t.multiply(o.at(s.ease(this.pos))))}a.matrix(t)}return this},once:function(t,e,i){var a=this.last();return i||(t=a.ease(t)),a.once[t]=e,this},_callStart:function(){return setTimeout(function(){this.start()}.bind(this),0),this}},parent:i.Element,construct:{animate:function(t,e,a){return(this.fx||(this.fx=new i.FX(this))).animate(t,e,a)},delay:function(t){return(this.fx||(this.fx=new i.FX(this))).delay(t)},stop:function(t,e){return this.fx&&this.fx.stop(t,e),this},finish:function(){return this.fx&&this.fx.finish(),this}}}),i.MorphObj=i.invent({create:function(t,e){return i.Color.isColor(e)?new i.Color(t).morph(e):i.regex.delimiter.test(t)?i.regex.pathLetters.test(t)?new i.PathArray(t).morph(e):new i.Array(t).morph(e):i.regex.numberAndUnit.test(e)?new i.Number(t).morph(e):(this.value=t,void(this.destination=e))},extend:{at:function(t,e){return e<1?this.value:this.destination},valueOf:function(){return this.value}}}),i.extend(i.FX,{attr:function(t,e,i){if(\"object\"===r(t))for(var a in t)this.attr(a,t[a]);else this.add(t,e,\"attrs\");return this},plot:function(t,e,i,a){return 4==arguments.length?this.plot([t,e,i,a]):this.add(\"plot\",new(this.target().morphArray)(t))}}),i.Box=i.invent({create:function(t,e,a,s){if(!(\"object\"!==r(t)||t instanceof i.Element))return i.Box.call(this,null!=t.left?t.left:t.x,null!=t.top?t.top:t.y,t.width,t.height);4==arguments.length&&(this.x=t,this.y=e,this.width=a,this.height=s),b(this)}}),i.BBox=i.invent({create:function(t){if(i.Box.apply(this,[].slice.call(arguments)),t instanceof i.Element){var a;try{if(!e.documentElement.contains){for(var s=t.node;s.parentNode;)s=s.parentNode;if(s!=e)throw new Error(\"Element not in the dom\")}a=t.node.getBBox()}catch(e){if(t instanceof i.Shape){i.parser.draw||i.prepare();var r=t.clone(i.parser.draw.instance).show();r&&r.node&&\"function\"==typeof r.node.getBBox&&(a=r.node.getBBox()),r&&\"function\"==typeof r.remove&&r.remove()}else a={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}i.Box.call(this,a)}},inherit:i.Box,parent:i.Element,construct:{bbox:function(){return new i.BBox(this)}}}),i.BBox.prototype.constructor=i.BBox,i.Matrix=i.invent({create:function(t){var e=p([1,0,0,1,0,0]);t=null===t?e:t instanceof i.Element?t.matrixify():\"string\"==typeof t?p(t.split(i.regex.delimiter).map(parseFloat)):6==arguments.length?p([].slice.call(arguments)):Array.isArray(t)?p(t):t&&\"object\"===r(t)?t:e;for(var a=m.length-1;a>=0;--a)this[m[a]]=null!=t[m[a]]?t[m[a]]:e[m[a]]},extend:{extract:function(){var t=f(this,0,1),e=(f(this,1,0),180/Math.PI*Math.atan2(t.y,t.x)-90);return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(e*Math.PI/180)+this.f*Math.sin(e*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(e*Math.PI/180)+this.e*Math.sin(-e*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),rotation:e,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new i.Matrix(this)}},clone:function(){return new i.Matrix(this)},morph:function(t){return this.destination=new i.Matrix(t),this},multiply:function(t){return new i.Matrix(this.native().multiply(function(t){return t instanceof i.Matrix||(t=new i.Matrix(t)),t}(t).native()))},inverse:function(){return new i.Matrix(this.native().inverse())},translate:function(t,e){return new i.Matrix(this.native().translate(t||0,e||0))},native:function(){for(var t=i.parser.native.createSVGMatrix(),e=m.length-1;e>=0;e--)t[m[e]]=this[m[e]];return t},toString:function(){return\"matrix(\"+v(this.a)+\",\"+v(this.b)+\",\"+v(this.c)+\",\"+v(this.d)+\",\"+v(this.e)+\",\"+v(this.f)+\")\"}},parent:i.Element,construct:{ctm:function(){return new i.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof i.Nested){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new i.Matrix(e)}return new i.Matrix(this.node.getScreenCTM())}}}),i.Point=i.invent({create:function(t,e){var i;i=Array.isArray(t)?{x:t[0],y:t[1]}:\"object\"===r(t)?{x:t.x,y:t.y}:null!=t?{x:t,y:null!=e?e:t}:{x:0,y:0},this.x=i.x,this.y=i.y},extend:{clone:function(){return new i.Point(this)},morph:function(t,e){return this.destination=new i.Point(t,e),this}}}),i.extend(i.Element,{point:function(t,e){return new i.Point(t,e).transform(this.screenCTM().inverse())}}),i.extend(i.Element,{attr:function(t,e,a){if(null==t){for(t={},a=(e=this.node.attributes).length-1;a>=0;a--)t[e[a].nodeName]=i.regex.isNumber.test(e[a].nodeValue)?parseFloat(e[a].nodeValue):e[a].nodeValue;return t}if(\"object\"===r(t))for(var s in t)this.attr(s,t[s]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return null==(e=this.node.getAttribute(t))?i.defaults.attrs[t]:i.regex.isNumber.test(e)?parseFloat(e):e;\"stroke-width\"==t?this.attr(\"stroke\",parseFloat(e)>0?this._stroke:null):\"stroke\"==t&&(this._stroke=e),\"fill\"!=t&&\"stroke\"!=t||(i.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof i.Image&&(e=this.doc().defs().pattern(0,0,(function(){this.add(e)})))),\"number\"==typeof e?e=new i.Number(e):i.Color.isColor(e)?e=new i.Color(e):Array.isArray(e)&&(e=new i.Array(e)),\"leading\"==t?this.leading&&this.leading(e):\"string\"==typeof a?this.node.setAttributeNS(a,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||\"font-size\"!=t&&\"x\"!=t||this.rebuild(t,e)}return this}}),i.extend(i.Element,{transform:function(t,e){var a;return\"object\"!==r(t)?(a=new i.Matrix(this).extract(),\"string\"==typeof t?a[t]:a):(a=new i.Matrix(this),e=!!e||!!t.relative,null!=t.a&&(a=e?a.multiply(new i.Matrix(t)):new i.Matrix(t)),this.attr(\"transform\",a))}}),i.extend(i.Element,{untransform:function(){return this.attr(\"transform\",null)},matrixify:function(){return(this.attr(\"transform\")||\"\").split(i.regex.transforms).slice(0,-1).map((function(t){var e=t.trim().split(\"(\");return[e[0],e[1].split(i.regex.delimiter).map((function(t){return parseFloat(t)}))]})).reduce((function(t,e){return\"matrix\"==e[0]?t.multiply(p(e[1])):t[e[0]].apply(t,e[1])}),new i.Matrix)},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.screenCTM().inverse();return this.addTo(t).untransform().transform(i.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),i.Transformation=i.invent({create:function(t,e){if(arguments.length>1&&\"boolean\"!=typeof e)return this.constructor.call(this,[].slice.call(arguments));if(Array.isArray(t))for(var i=0,a=this.arguments.length;i<a;++i)this[this.arguments[i]]=t[i];else if(t&&\"object\"===r(t))for(i=0,a=this.arguments.length;i<a;++i)this[this.arguments[i]]=t[this.arguments[i]];this.inversed=!1,!0===e&&(this.inversed=!0)}}),i.Translate=i.invent({parent:i.Matrix,inherit:i.Transformation,create:function(t,e){this.constructor.apply(this,[].slice.call(arguments))},extend:{arguments:[\"transformedX\",\"transformedY\"],method:\"translate\"}}),i.extend(i.Element,{style:function(t,e){if(0==arguments.length)return this.node.style.cssText||\"\";if(arguments.length<2)if(\"object\"===r(t))for(var a in t)this.style(a,t[a]);else{if(!i.regex.isCss.test(t))return this.node.style[h(t)];for(t=t.split(/\\s*;\\s*/).filter((function(t){return!!t})).map((function(t){return t.split(/\\s*:\\s*/)}));e=t.pop();)this.style(e[0],e[1])}else this.node.style[h(t)]=null===e||i.regex.isBlank.test(e)?\"\":e;return this}}),i.Parent=i.invent({create:function(t){this.constructor.call(this,t)},inherit:i.Element,extend:{children:function(){return i.utils.map(i.utils.filterSVGElements(this.node.childNodes),(function(t){return i.adopt(t)}))},add:function(t,e){return null==e?this.node.appendChild(t.node):t.node!=this.node.childNodes[e]&&this.node.insertBefore(t.node,this.node.childNodes[e]),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return i.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){for(var a=this.children(),s=0,r=a.length;s<r;s++)a[s]instanceof i.Element&&t.apply(a[s],[s,a]),e&&a[s]instanceof i.Container&&a[s].each(t,e);return this},removeElement:function(t){return this.node.removeChild(t.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,this},defs:function(){return this.doc().defs()}}}),i.extend(i.Parent,{ungroup:function(t,e){return 0===e||this instanceof i.Defs||this.node==i.parser.draw||(t=t||(this instanceof i.Doc?this:this.parent(i.Parent)),e=e||1/0,this.each((function(){return this instanceof i.Defs?this:this instanceof i.Parent?this.ungroup(t,e-1):this.toParent(t)})),this.node.firstChild||this.remove()),this},flatten:function(t,e){return this.ungroup(t,e)}}),i.Container=i.invent({create:function(t){this.constructor.call(this,t)},inherit:i.Parent}),i.ViewBox=i.invent({parent:i.Container,construct:{}}),[\"click\",\"dblclick\",\"mousedown\",\"mouseup\",\"mouseover\",\"mouseout\",\"mousemove\",\"touchstart\",\"touchmove\",\"touchleave\",\"touchend\",\"touchcancel\"].forEach((function(t){i.Element.prototype[t]=function(e){return i.on(this.node,t,e),this}})),i.listeners=[],i.handlerMap=[],i.listenerId=0,i.on=function(t,e,a,s,r){var n=a.bind(s||t.instance||t),o=(i.handlerMap.indexOf(t)+1||i.handlerMap.push(t))-1,l=e.split(\".\")[0],c=e.split(\".\")[1]||\"*\";i.listeners[o]=i.listeners[o]||{},i.listeners[o][l]=i.listeners[o][l]||{},i.listeners[o][l][c]=i.listeners[o][l][c]||{},a._svgjsListenerId||(a._svgjsListenerId=++i.listenerId),i.listeners[o][l][c][a._svgjsListenerId]=n,t.addEventListener(l,n,r||{passive:!0})},i.off=function(t,e,a){var s=i.handlerMap.indexOf(t),r=e&&e.split(\".\")[0],n=e&&e.split(\".\")[1],o=\"\";if(-1!=s)if(a){if(\"function\"==typeof a&&(a=a._svgjsListenerId),!a)return;i.listeners[s][r]&&i.listeners[s][r][n||\"*\"]&&(t.removeEventListener(r,i.listeners[s][r][n||\"*\"][a],!1),delete i.listeners[s][r][n||\"*\"][a])}else if(n&&r){if(i.listeners[s][r]&&i.listeners[s][r][n]){for(var l in i.listeners[s][r][n])i.off(t,[r,n].join(\".\"),l);delete i.listeners[s][r][n]}}else if(n)for(var c in i.listeners[s])for(var o in i.listeners[s][c])n===o&&i.off(t,[c,n].join(\".\"));else if(r){if(i.listeners[s][r]){for(var o in i.listeners[s][r])i.off(t,[r,o].join(\".\"));delete i.listeners[s][r]}}else{for(var c in i.listeners[s])i.off(t,c);delete i.listeners[s],delete i.handlerMap[s]}},i.extend(i.Element,{on:function(t,e,a,s){return i.on(this.node,t,e,a,s),this},off:function(t,e){return i.off(this.node,t,e),this},fire:function(e,a){return e instanceof t.Event?this.node.dispatchEvent(e):this.node.dispatchEvent(e=new i.CustomEvent(e,{detail:a,cancelable:!0})),this._event=e,this},event:function(){return this._event}}),i.Defs=i.invent({create:\"defs\",inherit:i.Container}),i.G=i.invent({create:\"g\",inherit:i.Container,extend:{x:function(t){return null==t?this.transform(\"x\"):this.transform({x:t-this.x()},!0)}},construct:{group:function(){return this.put(new i.G)}}}),i.Doc=i.invent({create:function(t){t&&(\"svg\"==(t=\"string\"==typeof t?e.getElementById(t):t).nodeName?this.constructor.call(this,t):(this.constructor.call(this,i.create(\"svg\")),t.appendChild(this.node),this.size(\"100%\",\"100%\")),this.namespace().defs())},inherit:i.Container,extend:{namespace:function(){return this.attr({xmlns:i.ns,version:\"1.1\"}).attr(\"xmlns:xlink\",i.xlink,i.xmlns).attr(\"xmlns:svgjs\",i.svgjs,i.xmlns)},defs:function(){var t;return this._defs||((t=this.node.getElementsByTagName(\"defs\")[0])?this._defs=i.adopt(t):this._defs=new i.Defs,this.node.appendChild(this._defs.node)),this._defs},parent:function(){return this.node.parentNode&&\"#document\"!=this.node.parentNode.nodeName?this.node.parentNode:null},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,i.parser.draw&&!i.parser.draw.parentNode&&this.node.appendChild(i.parser.draw),this},clone:function(t){this.writeDataToDom();var e=this.node,i=x(e.cloneNode(!0));return t?(t.node||t).appendChild(i.node):e.parentNode.insertBefore(i.node,e.nextSibling),i}}}),i.extend(i.Element,{}),i.Gradient=i.invent({create:function(t){this.constructor.call(this,i.create(t+\"Gradient\")),this.type=t},inherit:i.Container,extend:{at:function(t,e,a){return this.put(new i.Stop).update(t,e,a)},update:function(t){return this.clear(),\"function\"==typeof t&&t.call(this,this),this},fill:function(){return\"url(#\"+this.id()+\")\"},toString:function(){return this.fill()},attr:function(t,e,a){return\"transform\"==t&&(t=\"gradientTransform\"),i.Container.prototype.attr.call(this,t,e,a)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),i.extend(i.Gradient,i.FX,{from:function(t,e){return\"radial\"==(this._target||this).type?this.attr({fx:new i.Number(t),fy:new i.Number(e)}):this.attr({x1:new i.Number(t),y1:new i.Number(e)})},to:function(t,e){return\"radial\"==(this._target||this).type?this.attr({cx:new i.Number(t),cy:new i.Number(e)}):this.attr({x2:new i.Number(t),y2:new i.Number(e)})}}),i.extend(i.Defs,{gradient:function(t,e){return this.put(new i.Gradient(t)).update(e)}}),i.Stop=i.invent({create:\"stop\",inherit:i.Element,extend:{update:function(t){return(\"number\"==typeof t||t instanceof i.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr(\"stop-opacity\",t.opacity),null!=t.color&&this.attr(\"stop-color\",t.color),null!=t.offset&&this.attr(\"offset\",new i.Number(t.offset)),this}}}),i.Pattern=i.invent({create:\"pattern\",inherit:i.Container,extend:{fill:function(){return\"url(#\"+this.id()+\")\"},update:function(t){return this.clear(),\"function\"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,a){return\"transform\"==t&&(t=\"patternTransform\"),i.Container.prototype.attr.call(this,t,e,a)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),i.extend(i.Defs,{pattern:function(t,e,a){return this.put(new i.Pattern).update(a).attr({x:0,y:0,width:t,height:e,patternUnits:\"userSpaceOnUse\"})}}),i.Shape=i.invent({create:function(t){this.constructor.call(this,t)},inherit:i.Element}),i.Symbol=i.invent({create:\"symbol\",inherit:i.Container,construct:{symbol:function(){return this.put(new i.Symbol)}}}),i.Use=i.invent({create:\"use\",inherit:i.Shape,extend:{element:function(t,e){return this.attr(\"href\",(e||\"\")+\"#\"+t,i.xlink)}},construct:{use:function(t,e){return this.put(new i.Use).element(t,e)}}}),i.Rect=i.invent({create:\"rect\",inherit:i.Shape,construct:{rect:function(t,e){return this.put(new i.Rect).size(t,e)}}}),i.Circle=i.invent({create:\"circle\",inherit:i.Shape,construct:{circle:function(t){return this.put(new i.Circle).rx(new i.Number(t).divide(2)).move(0,0)}}}),i.extend(i.Circle,i.FX,{rx:function(t){return this.attr(\"r\",t)},ry:function(t){return this.rx(t)}}),i.Ellipse=i.invent({create:\"ellipse\",inherit:i.Shape,construct:{ellipse:function(t,e){return this.put(new i.Ellipse).size(t,e).move(0,0)}}}),i.extend(i.Ellipse,i.Rect,i.FX,{rx:function(t){return this.attr(\"rx\",t)},ry:function(t){return this.attr(\"ry\",t)}}),i.extend(i.Circle,i.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr(\"cx\"):this.attr(\"cx\",t)},cy:function(t){return null==t?this.attr(\"cy\"):this.attr(\"cy\",t)},width:function(t){return null==t?2*this.rx():this.rx(new i.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new i.Number(t).divide(2))},size:function(t,e){var a=g(this,t,e);return this.rx(new i.Number(a.width).divide(2)).ry(new i.Number(a.height).divide(2))}}),i.Line=i.invent({create:\"line\",inherit:i.Shape,extend:{array:function(){return new i.PointArray([[this.attr(\"x1\"),this.attr(\"y1\")],[this.attr(\"x2\"),this.attr(\"y2\")]])},plot:function(t,e,a,s){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:a,y2:s}:new i.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=g(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,a,s){return i.Line.prototype.plot.apply(this.put(new i.Line),null!=t?[t,e,a,s]:[0,0,0,0])}}}),i.Polyline=i.invent({create:\"polyline\",inherit:i.Shape,construct:{polyline:function(t){return this.put(new i.Polyline).plot(t||new i.PointArray)}}}),i.Polygon=i.invent({create:\"polygon\",inherit:i.Shape,construct:{polygon:function(t){return this.put(new i.Polygon).plot(t||new i.PointArray)}}}),i.extend(i.Polyline,i.Polygon,{array:function(){return this._array||(this._array=new i.PointArray(this.attr(\"points\")))},plot:function(t){return null==t?this.array():this.clear().attr(\"points\",\"string\"==typeof t?t:this._array=new i.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr(\"points\",this.array().move(t,e))},size:function(t,e){var i=g(this,t,e);return this.attr(\"points\",this.array().size(i.width,i.height))}}),i.extend(i.Line,i.Polyline,i.Polygon,{morphArray:i.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),i.Path=i.invent({create:\"path\",inherit:i.Shape,extend:{morphArray:i.PathArray,array:function(){return this._array||(this._array=new i.PathArray(this.attr(\"d\")))},plot:function(t){return null==t?this.array():this.clear().attr(\"d\",\"string\"==typeof t?t:this._array=new i.PathArray(t))},clear:function(){return delete this._array,this}},construct:{path:function(t){return this.put(new i.Path).plot(t||new i.PathArray)}}}),i.Image=i.invent({create:\"image\",inherit:i.Shape,extend:{load:function(e){if(!e)return this;var a=this,s=new t.Image;return i.on(s,\"load\",(function(){i.off(s);var t=a.parent(i.Pattern);null!==t&&(0==a.width()&&0==a.height()&&a.size(s.width,s.height),t&&0==t.width()&&0==t.height()&&t.size(a.width(),a.height()),\"function\"==typeof a._loaded&&a._loaded.call(a,{width:s.width,height:s.height,ratio:s.width/s.height,url:e}))})),i.on(s,\"error\",(function(t){i.off(s),\"function\"==typeof a._error&&a._error.call(a,t)})),this.attr(\"href\",s.src=this.src=e,i.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,a){return this.put(new i.Image).load(t).size(e||0,a||e||0)}}}),i.Text=i.invent({create:function(){this.constructor.call(this,i.create(\"text\")),this.dom.leading=new i.Number(1.3),this._rebuild=!0,this._build=!1,this.attr(\"font-family\",i.defaults.attrs[\"font-family\"])},inherit:i.Shape,extend:{x:function(t){return null==t?this.attr(\"x\"):this.attr(\"x\",t)},text:function(t){if(void 0===t){t=\"\";for(var e=this.node.childNodes,a=0,s=e.length;a<s;++a)0!=a&&3!=e[a].nodeType&&1==i.adopt(e[a]).dom.newLined&&(t+=\"\\n\"),t+=e[a].textContent;return t}if(this.clear().build(!0),\"function\"==typeof t)t.call(this,this);else{a=0;for(var r=(t=t.split(\"\\n\")).length;a<r;a++)this.tspan(t[a]).newLine()}return this.build(!1).rebuild()},size:function(t){return this.attr(\"font-size\",t).rebuild()},leading:function(t){return null==t?this.dom.leading:(this.dom.leading=new i.Number(t),this.rebuild())},lines:function(){var t=(this.textPath&&this.textPath()||this).node,e=i.utils.map(i.utils.filterSVGElements(t.childNodes),(function(t){return i.adopt(t)}));return new i.Set(e)},rebuild:function(t){if(\"boolean\"==typeof t&&(this._rebuild=t),this._rebuild){var e=this,a=0,s=this.dom.leading*new i.Number(this.attr(\"font-size\"));this.lines().each((function(){this.dom.newLined&&(e.textPath()||this.attr(\"x\",e.attr(\"x\")),\"\\n\"==this.text()?a+=s:(this.attr(\"dy\",s+a),a=0))})),this.fire(\"rebuild\")}return this},build:function(t){return this._build=!!t,this},setData:function(t){return this.dom=t,this.dom.leading=new i.Number(t.leading||1.3),this}},construct:{text:function(t){return this.put(new i.Text).text(t)},plain:function(t){return this.put(new i.Text).plain(t)}}}),i.Tspan=i.invent({create:\"tspan\",inherit:i.Shape,extend:{text:function(t){return null==t?this.node.textContent+(this.dom.newLined?\"\\n\":\"\"):(\"function\"==typeof t?t.call(this,this):this.plain(t),this)},dx:function(t){return this.attr(\"dx\",t)},dy:function(t){return this.attr(\"dy\",t)},newLine:function(){var t=this.parent(i.Text);return this.dom.newLined=!0,this.dy(t.dom.leading*t.attr(\"font-size\")).attr(\"x\",t.x())}}}),i.extend(i.Text,i.Tspan,{plain:function(t){return!1===this._build&&this.clear(),this.node.appendChild(e.createTextNode(t)),this},tspan:function(t){var e=(this.textPath&&this.textPath()||this).node,a=new i.Tspan;return!1===this._build&&this.clear(),e.appendChild(a.node),a.text(t)},clear:function(){for(var t=(this.textPath&&this.textPath()||this).node;t.hasChildNodes();)t.removeChild(t.lastChild);return this},length:function(){return this.node.getComputedTextLength()}}),i.TextPath=i.invent({create:\"textPath\",inherit:i.Parent,parent:i.Text,construct:{morphArray:i.PathArray,array:function(){var t=this.track();return t?t.array():null},plot:function(t){var e=this.track(),i=null;return e&&(i=e.plot(t)),null==t?i:this},track:function(){var t=this.textPath();if(t)return t.reference(\"href\")},textPath:function(){if(this.node.firstChild&&\"textPath\"==this.node.firstChild.nodeName)return i.adopt(this.node.firstChild)}}}),i.Nested=i.invent({create:function(){this.constructor.call(this,i.create(\"svg\")),this.style(\"overflow\",\"visible\")},inherit:i.Container,construct:{nested:function(){return this.put(new i.Nested)}}});var l={stroke:[\"color\",\"width\",\"opacity\",\"linecap\",\"linejoin\",\"miterlimit\",\"dasharray\",\"dashoffset\"],fill:[\"color\",\"opacity\",\"rule\"],prefix:function(t,e){return\"color\"==e?t:t+\"-\"+e}};function c(t,e,a,s){return a+s.replace(i.regex.dots,\" .\")}function h(t){return t.toLowerCase().replace(/-(.)/g,(function(t,e){return e.toUpperCase()}))}function d(t){return t.charAt(0).toUpperCase()+t.slice(1)}function u(t){var e=t.toString(16);return 1==e.length?\"0\"+e:e}function g(t,e,i){if(null==e||null==i){var a=t.bbox();null==e?e=a.width/a.height*i:null==i&&(i=a.height/a.width*e)}return{width:e,height:i}}function f(t,e,i){return{x:e*t.a+i*t.c+0,y:e*t.b+i*t.d+0}}function p(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function x(e){for(var a=e.childNodes.length-1;a>=0;a--)e.childNodes[a]instanceof t.SVGElement&&x(e.childNodes[a]);return i.adopt(e).id(i.eid(e.nodeName))}function b(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function v(t){return Math.abs(t)>1e-37?t:0}[\"fill\",\"stroke\"].forEach((function(t){var e={};e[t]=function(e){if(void 0===e)return this;if(\"string\"==typeof e||i.Color.isRgb(e)||e&&\"function\"==typeof e.fill)this.attr(t,e);else for(var a=l[t].length-1;a>=0;a--)null!=e[l[t][a]]&&this.attr(l.prefix(t,l[t][a]),e[l[t][a]]);return this},i.extend(i.Element,i.FX,e)})),i.extend(i.Element,i.FX,{translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.attr(\"transform\",new i.Matrix(6==arguments.length?[].slice.call(arguments):t))},opacity:function(t){return this.attr(\"opacity\",t)},dx:function(t){return this.x(new i.Number(t).plus(this instanceof i.FX?0:this.x()),!0)},dy:function(t){return this.y(new i.Number(t).plus(this instanceof i.FX?0:this.y()),!0)}}),i.extend(i.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),i.Set=i.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){for(var t=[].slice.call(arguments),e=0,i=t.length;e<i;e++)this.members.push(t[e]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e<i;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},length:function(){return this.members.length},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members}},construct:{set:function(t){return new i.Set(t)}}}),i.FX.Set=i.invent({create:function(t){this.set=t}}),i.Set.inherit=function(){var t=[];for(var e in i.Shape.prototype)\"function\"==typeof i.Shape.prototype[e]&&\"function\"!=typeof i.Set.prototype[e]&&t.push(e);for(var e in t.forEach((function(t){i.Set.prototype[t]=function(){for(var e=0,a=this.members.length;e<a;e++)this.members[e]&&\"function\"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return\"animate\"==t?this.fx||(this.fx=new i.FX.Set(this)):this}})),t=[],i.FX.prototype)\"function\"==typeof i.FX.prototype[e]&&\"function\"!=typeof i.FX.Set.prototype[e]&&t.push(e);t.forEach((function(t){i.FX.Set.prototype[t]=function(){for(var e=0,i=this.set.members.length;e<i;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}}))},i.extend(i.Element,{}),i.extend(i.Element,{remember:function(t,e){if(\"object\"===r(arguments[0]))for(var i in t)this.remember(i,t[i]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),i.get=function(t){var a=e.getElementById(function(t){var e=(t||\"\").toString().match(i.regex.reference);if(e)return e[1]}(t)||t);return i.adopt(a)},i.select=function(t,a){return new i.Set(i.utils.map((a||e).querySelectorAll(t),(function(t){return i.adopt(t)})))},i.extend(i.Parent,{select:function(t){return i.select(t,this.node)}});var m=\"abcdef\".split(\"\");if(\"function\"!=typeof t.CustomEvent){var y=function(t,i){i=i||{bubbles:!1,cancelable:!1,detail:void 0};var a=e.createEvent(\"CustomEvent\");return a.initCustomEvent(t,i.bubbles,i.cancelable,i.detail),a};y.prototype=t.Event.prototype,i.CustomEvent=y}else i.CustomEvent=t.CustomEvent;return i}(s,s.document)}.call(e,i,e,t),void 0===a||(t.exports=a)},539:(t,e,i)=>{(e=i(922)(!1)).push([t.id,'@keyframes opaque {\\n  0% {\\n      opacity: 0\\n  }\\n\\n  to {\\n      opacity: 1\\n  }\\n}\\n\\n@keyframes resizeanim {\\n  0%,to {\\n      opacity: 0\\n  }\\n}\\n\\n.apexcharts-canvas {\\n  position: relative;\\n  user-select: none\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar {\\n  -webkit-appearance: none;\\n  width: 6px\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar-thumb {\\n  border-radius: 4px;\\n  background-color: rgba(0,0,0,.5);\\n  box-shadow: 0 0 1px rgba(255,255,255,.5);\\n  -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5)\\n}\\n\\n.apexcharts-inner {\\n  position: relative\\n}\\n\\n.apexcharts-text tspan {\\n  font-family: inherit\\n}\\n\\n.legend-mouseover-inactive {\\n  transition: .15s ease all;\\n  opacity: .2\\n}\\n\\n.apexcharts-legend-text {\\n  padding-left: 15px;\\n  margin-left: -15px;\\n}\\n\\n.apexcharts-series-collapsed {\\n  opacity: 0\\n}\\n\\n.apexcharts-tooltip {\\n  border-radius: 5px;\\n  box-shadow: 2px 2px 6px -4px #999;\\n  cursor: default;\\n  font-size: 14px;\\n  left: 62px;\\n  opacity: 0;\\n  pointer-events: none;\\n  position: absolute;\\n  top: 20px;\\n  display: flex;\\n  flex-direction: column;\\n  overflow: hidden;\\n  white-space: nowrap;\\n  z-index: 12;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light {\\n  border: 1px solid #e3e3e3;\\n  background: rgba(255,255,255,.96)\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark {\\n  color: #fff;\\n  background: rgba(30,30,30,.8)\\n}\\n\\n.apexcharts-tooltip * {\\n  font-family: inherit\\n}\\n\\n.apexcharts-tooltip-title {\\n  padding: 6px;\\n  font-size: 15px;\\n  margin-bottom: 4px\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {\\n  background: #eceff1;\\n  border-bottom: 1px solid #ddd\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {\\n  background: rgba(0,0,0,.7);\\n  border-bottom: 1px solid #333\\n}\\n\\n.apexcharts-tooltip-text-goals-value,.apexcharts-tooltip-text-y-value,.apexcharts-tooltip-text-z-value {\\n  display: inline-block;\\n  margin-left: 5px;\\n  font-weight: 600\\n}\\n\\n.apexcharts-tooltip-text-goals-label:empty,.apexcharts-tooltip-text-goals-value:empty,.apexcharts-tooltip-text-y-label:empty,.apexcharts-tooltip-text-y-value:empty,.apexcharts-tooltip-text-z-value:empty,.apexcharts-tooltip-title:empty {\\n  display: none\\n}\\n\\n.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-tooltip-goals-group,.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  display: flex\\n}\\n\\n.apexcharts-tooltip-text-goals-label:not(:empty),.apexcharts-tooltip-text-goals-value:not(:empty) {\\n  margin-top: -6px\\n}\\n\\n.apexcharts-tooltip-marker {\\n  width: 12px;\\n  height: 12px;\\n  position: relative;\\n  top: 0;\\n  margin-right: 10px;\\n  border-radius: 50%\\n}\\n\\n.apexcharts-tooltip-series-group {\\n  padding: 0 10px;\\n  display: none;\\n  text-align: left;\\n  justify-content: left;\\n  align-items: center\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {\\n  opacity: 1\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active,.apexcharts-tooltip-series-group:last-child {\\n  padding-bottom: 4px\\n}\\n\\n.apexcharts-tooltip-series-group-hidden {\\n  opacity: 0;\\n  height: 0;\\n  line-height: 0;\\n  padding: 0!important\\n}\\n\\n.apexcharts-tooltip-y-group {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-custom-tooltip,.apexcharts-tooltip-box {\\n  padding: 4px 8px\\n}\\n\\n.apexcharts-tooltip-boxPlot {\\n  display: flex;\\n  flex-direction: column-reverse\\n}\\n\\n.apexcharts-tooltip-box>div {\\n  margin: 4px 0\\n}\\n\\n.apexcharts-tooltip-box span.value {\\n  font-weight: 700\\n}\\n\\n.apexcharts-tooltip-rangebar {\\n  padding: 5px 8px\\n}\\n\\n.apexcharts-tooltip-rangebar .category {\\n  font-weight: 600;\\n  color: #777\\n}\\n\\n.apexcharts-tooltip-rangebar .series-name {\\n  font-weight: 700;\\n  display: block;\\n  margin-bottom: 5px\\n}\\n\\n.apexcharts-xaxistooltip,.apexcharts-yaxistooltip {\\n  opacity: 0;\\n  pointer-events: none;\\n  color: #373d3f;\\n  font-size: 13px;\\n  text-align: center;\\n  border-radius: 2px;\\n  position: absolute;\\n  z-index: 10;\\n  background: #eceff1;\\n  border: 1px solid #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip {\\n  padding: 9px 10px;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-xaxistooltip:after,.apexcharts-xaxistooltip:before {\\n  left: 50%;\\n  border: solid transparent;\\n  content: \" \";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-xaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-left: -6px\\n}\\n\\n.apexcharts-xaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-left: -7px\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after,.apexcharts-xaxistooltip-bottom:before {\\n  bottom: 100%\\n}\\n\\n.apexcharts-xaxistooltip-top:after,.apexcharts-xaxistooltip-top:before {\\n  top: 100%\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after {\\n  border-bottom-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-bottom:before {\\n  border-bottom-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {\\n  border-bottom-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip-top:after {\\n  border-top-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-top:before {\\n  border-top-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {\\n  border-top-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-yaxistooltip {\\n  padding: 4px 10px\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-yaxistooltip:after,.apexcharts-yaxistooltip:before {\\n  top: 50%;\\n  border: solid transparent;\\n  content: \" \";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-yaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-top: -6px\\n}\\n\\n.apexcharts-yaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-top: -7px\\n}\\n\\n.apexcharts-yaxistooltip-left:after,.apexcharts-yaxistooltip-left:before {\\n  left: 100%\\n}\\n\\n.apexcharts-yaxistooltip-right:after,.apexcharts-yaxistooltip-right:before {\\n  right: 100%\\n}\\n\\n.apexcharts-yaxistooltip-left:after {\\n  border-left-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-left:before {\\n  border-left-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {\\n  border-left-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip-right:after {\\n  border-right-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-right:before {\\n  border-right-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {\\n  border-right-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-active {\\n  opacity: 1\\n}\\n\\n.apexcharts-yaxistooltip-hidden {\\n  display: none\\n}\\n\\n.apexcharts-xcrosshairs,.apexcharts-ycrosshairs {\\n  pointer-events: none;\\n  opacity: 0;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xcrosshairs.apexcharts-active,.apexcharts-ycrosshairs.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-ycrosshairs-hidden {\\n  opacity: 0\\n}\\n\\n.apexcharts-selection-rect {\\n  cursor: move\\n}\\n\\n.svg_select_boundingRect,.svg_select_points_rot {\\n  pointer-events: none;\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_boundingRect,.apexcharts-selection-rect+g .svg_select_points_rot {\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_points_l,.apexcharts-selection-rect+g .svg_select_points_r {\\n  cursor: ew-resize;\\n  opacity: 1;\\n  visibility: visible\\n}\\n\\n.svg_select_points {\\n  fill: #efefef;\\n  stroke: #333;\\n  rx: 2\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-zoom {\\n  cursor: crosshair\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-pan {\\n  cursor: move\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-pan-icon,.apexcharts-reset-icon,.apexcharts-selection-icon,.apexcharts-toolbar-custom-icon,.apexcharts-zoom-icon,.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  cursor: pointer;\\n  width: 20px;\\n  height: 20px;\\n  line-height: 24px;\\n  color: #6e8192;\\n  text-align: center\\n}\\n\\n.apexcharts-menu-icon svg,.apexcharts-reset-icon svg,.apexcharts-zoom-icon svg,.apexcharts-zoomin-icon svg,.apexcharts-zoomout-icon svg {\\n  fill: #6e8192\\n}\\n\\n.apexcharts-selection-icon svg {\\n  fill: #444;\\n  transform: scale(.76)\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu-icon svg,.apexcharts-theme-dark .apexcharts-pan-icon svg,.apexcharts-theme-dark .apexcharts-reset-icon svg,.apexcharts-theme-dark .apexcharts-selection-icon svg,.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg,.apexcharts-theme-dark .apexcharts-zoom-icon svg,.apexcharts-theme-dark .apexcharts-zoomin-icon svg,.apexcharts-theme-dark .apexcharts-zoomout-icon svg {\\n  fill: #f3f4f5\\n}\\n\\n.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg {\\n  fill: #008ffb\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-icon:hover svg,.apexcharts-theme-light .apexcharts-reset-icon:hover svg,.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg {\\n  fill: #333\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-selection-icon {\\n  position: relative\\n}\\n\\n.apexcharts-reset-icon {\\n  margin-left: 5px\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-reset-icon,.apexcharts-zoom-icon {\\n  transform: scale(.85)\\n}\\n\\n.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  transform: scale(.7)\\n}\\n\\n.apexcharts-zoomout-icon {\\n  margin-right: 3px\\n}\\n\\n.apexcharts-pan-icon {\\n  transform: scale(.62);\\n  position: relative;\\n  left: 1px;\\n  top: 0\\n}\\n\\n.apexcharts-pan-icon svg {\\n  fill: #fff;\\n  stroke: #6e8192;\\n  stroke-width: 2\\n}\\n\\n.apexcharts-pan-icon.apexcharts-selected svg {\\n  stroke: #008ffb\\n}\\n\\n.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {\\n  stroke: #333\\n}\\n\\n.apexcharts-toolbar {\\n  position: absolute;\\n  z-index: 11;\\n  max-width: 176px;\\n  text-align: right;\\n  border-radius: 3px;\\n  padding: 0 6px 2px;\\n  display: flex;\\n  justify-content: space-between;\\n  align-items: center\\n}\\n\\n.apexcharts-menu {\\n  background: #fff;\\n  position: absolute;\\n  top: 100%;\\n  border: 1px solid #ddd;\\n  border-radius: 3px;\\n  padding: 3px;\\n  right: 10px;\\n  opacity: 0;\\n  min-width: 110px;\\n  transition: .15s ease all;\\n  pointer-events: none\\n}\\n\\n.apexcharts-menu.apexcharts-menu-open {\\n  opacity: 1;\\n  pointer-events: all;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-menu-item {\\n  padding: 6px 7px;\\n  font-size: 12px;\\n  cursor: pointer\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-item:hover {\\n  background: #eee\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu {\\n  background: rgba(0,0,0,.7);\\n  color: #fff\\n}\\n\\n@media screen and (min-width:768px) {\\n  .apexcharts-canvas:hover .apexcharts-toolbar {\\n      opacity: 1\\n  }\\n}\\n\\n.apexcharts-canvas .apexcharts-element-hidden,.apexcharts-datalabel.apexcharts-element-hidden,.apexcharts-hide .apexcharts-series-points {\\n  opacity: 0\\n}\\n\\n.apexcharts-datalabel,.apexcharts-datalabel-label,.apexcharts-datalabel-value,.apexcharts-datalabels,.apexcharts-pie-label {\\n  cursor: default;\\n  pointer-events: none\\n}\\n\\n.apexcharts-pie-label-delay {\\n  opacity: 0;\\n  animation-name: opaque;\\n  animation-duration: .3s;\\n  animation-fill-mode: forwards;\\n  animation-timing-function: ease\\n}\\n\\n.apexcharts-legend {\\t\\n  display: flex;\\t\\n  overflow: auto;\\t\\n  padding: 0 10px;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom, .apexcharts-legend.apx-legend-position-top {\\t\\n  flex-wrap: wrap\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  flex-direction: column;\\t\\n  bottom: 0;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-left, .apexcharts-legend.apx-legend-position-top.apexcharts-align-left, .apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  justify-content: flex-start;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-center, .apexcharts-legend.apx-legend-position-top.apexcharts-align-center {\\t\\n  justify-content: center;  \\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-right, .apexcharts-legend.apx-legend-position-top.apexcharts-align-right {\\t\\n  justify-content: flex-end;\\t\\n}\\t\\n.apexcharts-legend-series {\\t\\n  cursor: pointer;\\t\\n  line-height: normal;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom .apexcharts-legend-series, .apexcharts-legend.apx-legend-position-top .apexcharts-legend-series{\\t\\n  display: flex;\\t\\n  align-items: center;\\t\\n}\\t\\n.apexcharts-legend-text {\\t\\n  position: relative;\\t\\n  font-size: 14px;\\t\\n}\\t\\n.apexcharts-legend-text *, .apexcharts-legend-marker * {\\t\\n  pointer-events: none;\\t\\n}\\t\\n.apexcharts-legend-marker {\\t\\n  position: relative;\\t\\n  display: inline-block;\\t\\n  cursor: pointer;\\t\\n  margin-right: 3px;\\t\\n  border-style: solid;\\n}\\t\\n  \\n.apexcharts-legend.apexcharts-align-right .apexcharts-legend-series, .apexcharts-legend.apexcharts-align-left .apexcharts-legend-series{\\t\\n  display: inline-block;\\t\\n}\\t\\n.apexcharts-legend-series.apexcharts-no-click {\\t\\n  cursor: auto;\\t\\n}\\t\\n.apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {\\t\\n  display: none !important;\\t\\n}\\t\\n.apexcharts-inactive-legend {\\t\\n  opacity: 0.45;\\t\\n}\\n\\n.apexcharts-annotation-rect,.apexcharts-area-series .apexcharts-area,.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-gridline,.apexcharts-line,.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-point-annotation-label,.apexcharts-radar-series path,.apexcharts-radar-series polygon,.apexcharts-toolbar svg,.apexcharts-tooltip .apexcharts-marker,.apexcharts-xaxis-annotation-label,.apexcharts-yaxis-annotation-label,.apexcharts-zoom-rect {\\n  pointer-events: none\\n}\\n\\n.apexcharts-marker {\\n  transition: .15s ease all\\n}\\n\\n.resize-triggers {\\n  animation: 1ms resizeanim;\\n  visibility: hidden;\\n  opacity: 0;\\n  height: 100%;\\n  width: 100%;\\n  overflow: hidden\\n}\\n\\n.contract-trigger:before,.resize-triggers,.resize-triggers>div {\\n  content: \" \";\\n  display: block;\\n  position: absolute;\\n  top: 0;\\n  left: 0\\n}\\n\\n.resize-triggers>div {\\n  height: 100%;\\n  width: 100%;\\n  background: #eee;\\n  overflow: auto\\n}\\n\\n.contract-trigger:before {\\n  overflow: hidden;\\n  width: 200%;\\n  height: 200%\\n}\\n',\"\"]),t.exports=e},274:(t,e,i)=>{var a=i(379),s=i(539);\"string\"==typeof(s=s.__esModule?s.default:s)&&(s=[[t.id,s,\"\"]]);var r=(a(t.id,s,{insert:\"head\",singleton:!1}),s.locals?s.locals:{});t.exports=r},379:(t,e,i)=>{\"use strict\";var a,s=function(){var t={};return function(e){if(void 0===t[e]){var i=document.querySelector(e);if(window.HTMLIFrameElement&&i instanceof window.HTMLIFrameElement)try{i=i.contentDocument.head}catch(t){i=null}t[e]=i}return t[e]}}(),r={};function n(t,e,i){for(var a=0;a<e.length;a++){var s={css:e[a][1],media:e[a][2],sourceMap:e[a][3]};r[t][a]?r[t][a](s):r[t].push(f(s,i))}}function o(t){var e=document.createElement(\"style\"),a=t.attributes||{};if(void 0===a.nonce){var r=i.nc;r&&(a.nonce=r)}if(Object.keys(a).forEach((function(t){e.setAttribute(t,a[t])})),\"function\"==typeof t.insert)t.insert(e);else{var n=s(t.insert||\"head\");if(!n)throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");n.appendChild(e)}return e}var l,c=(l=[],function(t,e){return l[t]=e,l.filter(Boolean).join(\"\\n\")});function h(t,e,i,a){var s=i?\"\":a.css;if(t.styleSheet)t.styleSheet.cssText=c(e,s);else{var r=document.createTextNode(s),n=t.childNodes;n[e]&&t.removeChild(n[e]),n.length?t.insertBefore(r,n[e]):t.appendChild(r)}}function d(t,e,i){var a=i.css,s=i.media,r=i.sourceMap;if(s?t.setAttribute(\"media\",s):t.removeAttribute(\"media\"),r&&btoa&&(a+=\"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r)))),\" */\")),t.styleSheet)t.styleSheet.cssText=a;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(a))}}var u=null,g=0;function f(t,e){var i,a,s;if(e.singleton){var r=g++;i=u||(u=o(e)),a=h.bind(null,i,r,!1),s=h.bind(null,i,r,!0)}else i=o(e),a=d.bind(null,i,e),s=function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(i)};return a(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;a(t=e)}else s()}}t.exports=function(t,e,i){return(i=i||{}).singleton||\"boolean\"==typeof i.singleton||(i.singleton=(void 0===a&&(a=Boolean(window&&document&&document.all&&!window.atob)),a)),t=i.base?t+i.base:t,e=e||[],r[t]||(r[t]=[]),n(t,e,i),function(e){if(e=e||[],\"[object Array]\"===Object.prototype.toString.call(e)){r[t]||(r[t]=[]),n(t,e,i);for(var a=e.length;a<r[t].length;a++)r[t][a]();r[t].length=e.length,0===r[t].length&&delete r[t]}}}},149:t=>{t.exports='<svg fill=\"#000000\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z\"></path><path d=\"M0 0h24v24H0z\" fill=\"none\"></path></svg>'},355:t=>{t.exports='<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"></path><path d=\"M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z\"></path></svg>'},686:t=>{t.exports='<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\" fill=\"none\"></path><path d=\"M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"></path></svg>'},798:t=>{t.exports='<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" fill=\"#000000\" viewBox=\"0 0 24 24\"><defs><path d=\"M0 0h24v24H0z\" id=\"a\"></path></defs><clipPath id=\"b\"><use overflow=\"visible\" xlink:href=\"#a\"></use></clipPath><path clip-path=\"url(#b)\" d=\"M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z\"></path></svg>'},323:t=>{t.exports='<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\" fill=\"none\"></path><path d=\"M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"></path></svg>'},618:t=>{t.exports='<svg fill=\"#6E8192\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"></path><path d=\"M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2z\"></path></svg>'},688:t=>{t.exports='<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"#000000\" viewBox=\"0 0 24 24\"><path d=\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"></path><path d=\"M0 0h24v24H0V0z\" fill=\"none\"></path><path d=\"M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z\"></path></svg>'}},e={};function i(a){if(e[a])return e[a].exports;var s=e[a]={id:a,exports:{}};return t[a].call(s.exports,s,s.exports,i),s.exports}return i.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return i.d(e,{a:e}),e},i.d=(t,e)=>{for(var a in e)i.o(e,a)&&!i.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:e[a]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),i.r=t=>{\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(t,\"__esModule\",{value:!0})},i(978)})()}));"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/apexcharts.common.js",
    "content": "/*!\n * ApexCharts v3.37.1\n * (c) 2018-2023 ApexCharts\n * Released under the MIT License.\n */\n\"use strict\";function t(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function e(e){for(var i=1;i<arguments.length;i++){var a=null!=arguments[i]?arguments[i]:{};i%2?t(Object(a),!0).forEach((function(t){o(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):t(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function i(t){return i=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},i(t)}function a(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}function s(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function r(t,e,i){return e&&s(t.prototype,e),i&&s(t,i),t}function o(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function n(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&h(t,e)}function l(t){return l=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},l(t)}function h(t,e){return h=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},h(t,e)}function c(t,e){if(e&&(\"object\"==typeof e||\"function\"==typeof e))return e;if(void 0!==e)throw new TypeError(\"Derived constructors may only return object or undefined\");return function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t)}function d(t){var e=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,a=l(t);if(e){var s=l(this).constructor;i=Reflect.construct(a,arguments,s)}else i=a.apply(this,arguments);return c(this,i)}}function g(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var i=null==t?null:\"undefined\"!=typeof Symbol&&t[Symbol.iterator]||t[\"@@iterator\"];if(null==i)return;var a,s,r=[],o=!0,n=!1;try{for(i=i.call(t);!(o=(a=i.next()).done)&&(r.push(a.value),!e||r.length!==e);o=!0);}catch(t){n=!0,s=t}finally{try{o||null==i.return||i.return()}finally{if(n)throw s}}return r}(t,e)||f(t,e)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function u(t){return function(t){if(Array.isArray(t))return p(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||f(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function f(t,e){if(t){if(\"string\"==typeof t)return p(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?p(t,e):void 0}}function p(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}var x=function(){function t(){a(this,t)}return r(t,[{key:\"shadeRGBColor\",value:function(t,e){var i=e.split(\",\"),a=t<0?0:255,s=t<0?-1*t:t,r=parseInt(i[0].slice(4),10),o=parseInt(i[1],10),n=parseInt(i[2],10);return\"rgb(\"+(Math.round((a-r)*s)+r)+\",\"+(Math.round((a-o)*s)+o)+\",\"+(Math.round((a-n)*s)+n)+\")\"}},{key:\"shadeHexColor\",value:function(t,e){var i=parseInt(e.slice(1),16),a=t<0?0:255,s=t<0?-1*t:t,r=i>>16,o=i>>8&255,n=255&i;return\"#\"+(16777216+65536*(Math.round((a-r)*s)+r)+256*(Math.round((a-o)*s)+o)+(Math.round((a-n)*s)+n)).toString(16).slice(1)}},{key:\"shadeColor\",value:function(e,i){return t.isColorHex(i)?this.shadeHexColor(e,i):this.shadeRGBColor(e,i)}}],[{key:\"bind\",value:function(t,e){return function(){return t.apply(e,arguments)}}},{key:\"isObject\",value:function(t){return t&&\"object\"===i(t)&&!Array.isArray(t)&&null!=t}},{key:\"is\",value:function(t,e){return Object.prototype.toString.call(e)===\"[object \"+t+\"]\"}},{key:\"listToArray\",value:function(t){var e,i=[];for(e=0;e<t.length;e++)i[e]=t[e];return i}},{key:\"extend\",value:function(t,e){var i=this;\"function\"!=typeof Object.assign&&(Object.assign=function(t){if(null==t)throw new TypeError(\"Cannot convert undefined or null to object\");for(var e=Object(t),i=1;i<arguments.length;i++){var a=arguments[i];if(null!=a)for(var s in a)a.hasOwnProperty(s)&&(e[s]=a[s])}return e});var a=Object.assign({},t);return this.isObject(t)&&this.isObject(e)&&Object.keys(e).forEach((function(s){i.isObject(e[s])&&s in t?a[s]=i.extend(t[s],e[s]):Object.assign(a,o({},s,e[s]))})),a}},{key:\"extendArray\",value:function(e,i){var a=[];return e.map((function(e){a.push(t.extend(i,e))})),e=a}},{key:\"monthMod\",value:function(t){return t%12}},{key:\"clone\",value:function(e){if(t.is(\"Array\",e)){for(var a=[],s=0;s<e.length;s++)a[s]=this.clone(e[s]);return a}if(t.is(\"Null\",e))return null;if(t.is(\"Date\",e))return e;if(\"object\"===i(e)){var r={};for(var o in e)e.hasOwnProperty(o)&&(r[o]=this.clone(e[o]));return r}return e}},{key:\"log10\",value:function(t){return Math.log(t)/Math.LN10}},{key:\"roundToBase10\",value:function(t){return Math.pow(10,Math.floor(Math.log10(t)))}},{key:\"roundToBase\",value:function(t,e){return Math.pow(e,Math.floor(Math.log(t)/Math.log(e)))}},{key:\"parseNumber\",value:function(t){return null===t?t:parseFloat(t)}},{key:\"randomId\",value:function(){return(Math.random()+1).toString(36).substring(4)}},{key:\"noExponents\",value:function(t){var e=String(t).split(/[eE]/);if(1===e.length)return e[0];var i=\"\",a=t<0?\"-\":\"\",s=e[0].replace(\".\",\"\"),r=Number(e[1])+1;if(r<0){for(i=a+\"0.\";r++;)i+=\"0\";return i+s.replace(/^-/,\"\")}for(r-=s.length;r--;)i+=\"0\";return s+i}},{key:\"getDimensions\",value:function(t){var e=getComputedStyle(t,null),i=t.clientHeight,a=t.clientWidth;return i-=parseFloat(e.paddingTop)+parseFloat(e.paddingBottom),[a-=parseFloat(e.paddingLeft)+parseFloat(e.paddingRight),i]}},{key:\"getBoundingClientRect\",value:function(t){var e=t.getBoundingClientRect();return{top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:t.clientWidth,height:t.clientHeight,x:e.left,y:e.top}}},{key:\"getLargestStringFromArr\",value:function(t){return t.reduce((function(t,e){return Array.isArray(e)&&(e=e.reduce((function(t,e){return t.length>e.length?t:e}))),t.length>e.length?t:e}),0)}},{key:\"hexToRgba\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"#999999\",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.6;\"#\"!==t.substring(0,1)&&(t=\"#999999\");var i=t.replace(\"#\",\"\");i=i.match(new RegExp(\"(.{\"+i.length/3+\"})\",\"g\"));for(var a=0;a<i.length;a++)i[a]=parseInt(1===i[a].length?i[a]+i[a]:i[a],16);return void 0!==e&&i.push(e),\"rgba(\"+i.join(\",\")+\")\"}},{key:\"getOpacityFromRGBA\",value:function(t){return parseFloat(t.replace(/^.*,(.+)\\)/,\"$1\"))}},{key:\"rgb2hex\",value:function(t){return(t=t.match(/^rgba?[\\s+]?\\([\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?/i))&&4===t.length?\"#\"+(\"0\"+parseInt(t[1],10).toString(16)).slice(-2)+(\"0\"+parseInt(t[2],10).toString(16)).slice(-2)+(\"0\"+parseInt(t[3],10).toString(16)).slice(-2):\"\"}},{key:\"isColorHex\",value:function(t){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i.test(t)}},{key:\"getPolygonPos\",value:function(t,e){for(var i=[],a=2*Math.PI/e,s=0;s<e;s++){var r={};r.x=t*Math.sin(s*a),r.y=-t*Math.cos(s*a),i.push(r)}return i}},{key:\"polarToCartesian\",value:function(t,e,i,a){var s=(a-90)*Math.PI/180;return{x:t+i*Math.cos(s),y:e+i*Math.sin(s)}}},{key:\"escapeString\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"x\",i=t.toString().slice();return i=i.replace(/[` ~!@#$%^&*()|+\\=?;:'\",.<>{}[\\]\\\\/]/gi,e)}},{key:\"negToZero\",value:function(t){return t<0?0:t}},{key:\"moveIndexInArray\",value:function(t,e,i){if(i>=t.length)for(var a=i-t.length+1;a--;)t.push(void 0);return t.splice(i,0,t.splice(e,1)[0]),t}},{key:\"extractNumber\",value:function(t){return parseFloat(t.replace(/[^\\d.]*/g,\"\"))}},{key:\"findAncestor\",value:function(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}},{key:\"setELstyles\",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&(t.style.key=e[i])}},{key:\"isNumber\",value:function(t){return!isNaN(t)&&parseFloat(Number(t))===t&&!isNaN(parseInt(t,10))}},{key:\"isFloat\",value:function(t){return Number(t)===t&&t%1!=0}},{key:\"isSafari\",value:function(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}},{key:\"isFirefox\",value:function(){return navigator.userAgent.toLowerCase().indexOf(\"firefox\")>-1}},{key:\"isIE11\",value:function(){if(-1!==window.navigator.userAgent.indexOf(\"MSIE\")||window.navigator.appVersion.indexOf(\"Trident/\")>-1)return!0}},{key:\"isIE\",value:function(){var t=window.navigator.userAgent,e=t.indexOf(\"MSIE \");if(e>0)return parseInt(t.substring(e+5,t.indexOf(\".\",e)),10);if(t.indexOf(\"Trident/\")>0){var i=t.indexOf(\"rv:\");return parseInt(t.substring(i+3,t.indexOf(\".\",i)),10)}var a=t.indexOf(\"Edge/\");return a>0&&parseInt(t.substring(a+5,t.indexOf(\".\",a)),10)}}]),t}(),b=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.setEasingFunctions()}return r(t,[{key:\"setEasingFunctions\",value:function(){var t;if(!this.w.globals.easing){switch(this.w.config.chart.animations.easing){case\"linear\":t=\"-\";break;case\"easein\":t=\"<\";break;case\"easeout\":t=\">\";break;case\"easeinout\":default:t=\"<>\";break;case\"swing\":t=function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1};break;case\"bounce\":t=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375};break;case\"elastic\":t=function(t){return t===!!t?t:Math.pow(2,-10*t)*Math.sin((t-.075)*(2*Math.PI)/.3)+1}}this.w.globals.easing=t}}},{key:\"animateLine\",value:function(t,e,i,a){t.attr(e).animate(a).attr(i)}},{key:\"animateMarker\",value:function(t,e,i,a,s,r){e||(e=0),t.attr({r:e,width:e,height:e}).animate(a,s).attr({r:i,width:i.width,height:i.height}).afterAll((function(){r()}))}},{key:\"animateCircle\",value:function(t,e,i,a,s){t.attr({r:e.r,cx:e.cx,cy:e.cy}).animate(a,s).attr({r:i.r,cx:i.cx,cy:i.cy})}},{key:\"animateRect\",value:function(t,e,i,a,s){t.attr(e).animate(a).attr(i).afterAll((function(){return s()}))}},{key:\"animatePathsGradually\",value:function(t){var e=t.el,i=t.realIndex,a=t.j,s=t.fill,r=t.pathFrom,o=t.pathTo,n=t.speed,l=t.delay,h=this.w,c=0;h.config.chart.animations.animateGradually.enabled&&(c=h.config.chart.animations.animateGradually.delay),h.config.chart.animations.dynamicAnimation.enabled&&h.globals.dataChanged&&\"bar\"!==h.config.chart.type&&(c=0),this.morphSVG(e,i,a,\"line\"!==h.config.chart.type||h.globals.comboCharts?s:\"stroke\",r,o,n,l*c)}},{key:\"showDelayedElements\",value:function(){this.w.globals.delayedElements.forEach((function(t){t.el.classList.remove(\"apexcharts-element-hidden\")}))}},{key:\"animationCompleted\",value:function(t){var e=this.w;e.globals.animationEnded||(e.globals.animationEnded=!0,this.showDelayedElements(),\"function\"==typeof e.config.chart.events.animationEnd&&e.config.chart.events.animationEnd(this.ctx,{el:t,w:e}))}},{key:\"morphSVG\",value:function(t,e,i,a,s,r,o,n){var l=this,h=this.w;s||(s=t.attr(\"pathFrom\")),r||(r=t.attr(\"pathTo\"));var c=function(t){return\"radar\"===h.config.chart.type&&(o=1),\"M 0 \".concat(h.globals.gridHeight)};(!s||s.indexOf(\"undefined\")>-1||s.indexOf(\"NaN\")>-1)&&(s=c()),(!r||r.indexOf(\"undefined\")>-1||r.indexOf(\"NaN\")>-1)&&(r=c()),h.globals.shouldAnimate||(o=1),t.plot(s).animate(1,h.globals.easing,n).plot(s).animate(o,h.globals.easing,n).plot(r).afterAll((function(){x.isNumber(i)?i===h.globals.series[h.globals.maxValsInArrayIndex].length-2&&h.globals.shouldAnimate&&l.animationCompleted(t):\"none\"!==a&&h.globals.shouldAnimate&&(!h.globals.comboCharts&&e===h.globals.series.length-1||h.globals.comboCharts)&&l.animationCompleted(t),l.showDelayedElements()}))}}]),t}(),v=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"getDefaultFilter\",value:function(t,e){var i=this.w;t.unfilter(!0),(new window.SVG.Filter).size(\"120%\",\"180%\",\"-5%\",\"-40%\"),\"none\"!==i.config.states.normal.filter?this.applyFilter(t,e,i.config.states.normal.filter.type,i.config.states.normal.filter.value):i.config.chart.dropShadow.enabled&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:\"addNormalFilter\",value:function(t,e){var i=this.w;i.config.chart.dropShadow.enabled&&!t.node.classList.contains(\"apexcharts-marker\")&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:\"addLightenFilter\",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;t.unfilter(!0);new window.SVG.Filter;t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:\"linear\",slope:1.5,intercept:r}})})),t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)}},{key:\"addDarkenFilter\",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;t.unfilter(!0);new window.SVG.Filter;t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:\"linear\",slope:r}})})),t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)}},{key:\"applyFilter\",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:.5;switch(i){case\"none\":this.addNormalFilter(t,e);break;case\"lighten\":this.addLightenFilter(t,e,{intensity:a});break;case\"darken\":this.addDarkenFilter(t,e,{intensity:a})}}},{key:\"addShadow\",value:function(t,e,i){var a=i.blur,s=i.top,r=i.left,o=i.color,n=i.opacity,l=t.flood(Array.isArray(o)?o[e]:o,n).composite(t.sourceAlpha,\"in\").offset(r,s).gaussianBlur(a).merge(t.source);return t.blend(t.source,l)}},{key:\"dropShadow\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=e.top,s=e.left,r=e.blur,o=e.color,n=e.opacity,l=e.noUserSpaceOnUse,h=this.w;return t.unfilter(!0),x.isIE()&&\"radialBar\"===h.config.chart.type||(o=Array.isArray(o)?o[i]:o,t.filter((function(t){var e=null;e=x.isSafari()||x.isFirefox()||x.isIE()?t.flood(o,n).composite(t.sourceAlpha,\"in\").offset(s,a).gaussianBlur(r):t.flood(o,n).composite(t.sourceAlpha,\"in\").offset(s,a).gaussianBlur(r).merge(t.source),t.blend(t.source,e)})),l||t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)),t}},{key:\"setSelectionFilter\",value:function(t,e,i){var a=this.w;if(void 0!==a.globals.selectedDataPoints[e]&&a.globals.selectedDataPoints[e].indexOf(i)>-1){t.node.setAttribute(\"selected\",!0);var s=a.config.states.active.filter;\"none\"!==s&&this.applyFilter(t,e,s.type,s.value)}}},{key:\"_scaleFilterSize\",value:function(t){!function(e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}({width:\"200%\",height:\"200%\",x:\"-50%\",y:\"-50%\"})}}]),t}(),m=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"roundPathCorners\",value:function(t,e){function i(t,e,i){var s=e.x-t.x,r=e.y-t.y,o=Math.sqrt(s*s+r*r);return a(t,e,Math.min(1,i/o))}function a(t,e,i){return{x:t.x+(e.x-t.x)*i,y:t.y+(e.y-t.y)*i}}function s(t,e){t.length>2&&(t[t.length-2]=e.x,t[t.length-1]=e.y)}function r(t){return{x:parseFloat(t[t.length-2]),y:parseFloat(t[t.length-1])}}var o=t.split(/[,\\s]/).reduce((function(t,e){var i=e.match(\"([a-zA-Z])(.+)\");return i?(t.push(i[1]),t.push(i[2])):t.push(e),t}),[]).reduce((function(t,e){return parseFloat(e)==e&&t.length?t[t.length-1].push(e):t.push([e]),t}),[]),n=[];if(o.length>1){var l=r(o[0]),h=null;\"Z\"==o[o.length-1][0]&&o[0].length>2&&(h=[\"L\",l.x,l.y],o[o.length-1]=h),n.push(o[0]);for(var c=1;c<o.length;c++){var d=n[n.length-1],g=o[c],u=g==h?o[1]:o[c+1];if(u&&d&&d.length>2&&\"L\"==g[0]&&u.length>2&&\"L\"==u[0]){var f,p,x=r(d),b=r(g),v=r(u);f=i(b,x,e),p=i(b,v,e),s(g,f),g.origPoint=b,n.push(g);var m=a(f,b,.5),y=a(b,p,.5),w=[\"C\",m.x,m.y,y.x,y.y,p.x,p.y];w.origPoint=b,n.push(w)}else n.push(g)}if(h){var k=r(n[n.length-1]);n.push([\"Z\"]),s(n[0],k)}}else n=o;return n.reduce((function(t,e){return t+e.join(\" \")+\" \"}),\"\")}},{key:\"drawLine\",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:\"#a8a8a8\",r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,o=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,n=arguments.length>7&&void 0!==arguments[7]?arguments[7]:\"butt\",l=this.w,h=l.globals.dom.Paper.line().attr({x1:t,y1:e,x2:i,y2:a,stroke:s,\"stroke-dasharray\":r,\"stroke-width\":o,\"stroke-linecap\":n});return h}},{key:\"drawRect\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:\"#fefefe\",o=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1,n=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,l=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,h=arguments.length>9&&void 0!==arguments[9]?arguments[9]:0,c=this.w,d=c.globals.dom.Paper.rect();return d.attr({x:t,y:e,width:i>0?i:0,height:a>0?a:0,rx:s,ry:s,opacity:o,\"stroke-width\":null!==n?n:0,stroke:null!==l?l:\"none\",\"stroke-dasharray\":h}),d.node.setAttribute(\"fill\",r),d}},{key:\"drawPolygon\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"#e1e1e1\",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\"none\",s=this.w,r=s.globals.dom.Paper.polygon(t).attr({fill:a,stroke:e,\"stroke-width\":i});return r}},{key:\"drawCircle\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w;t<0&&(t=0);var a=i.globals.dom.Paper.circle(2*t);return null!==e&&a.attr(e),a}},{key:\"drawPath\",value:function(t){var e=t.d,i=void 0===e?\"\":e,a=t.stroke,s=void 0===a?\"#a8a8a8\":a,r=t.strokeWidth,o=void 0===r?1:r,n=t.fill,l=t.fillOpacity,h=void 0===l?1:l,c=t.strokeOpacity,d=void 0===c?1:c,g=t.classes,u=t.strokeLinecap,f=void 0===u?null:u,p=t.strokeDashArray,x=void 0===p?0:p,b=this.w;return null===f&&(f=b.config.stroke.lineCap),(i.indexOf(\"undefined\")>-1||i.indexOf(\"NaN\")>-1)&&(i=\"M 0 \".concat(b.globals.gridHeight)),b.globals.dom.Paper.path(i).attr({fill:n,\"fill-opacity\":h,stroke:s,\"stroke-opacity\":d,\"stroke-linecap\":f,\"stroke-width\":o,\"stroke-dasharray\":x,class:g})}},{key:\"group\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=e.globals.dom.Paper.group();return null!==t&&i.attr(t),i}},{key:\"move\",value:function(t,e){var i=[\"M\",t,e].join(\" \");return i}},{key:\"line\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=null;return null===i?a=[\" L\",t,e].join(\" \"):\"H\"===i?a=[\" H\",t].join(\" \"):\"V\"===i&&(a=[\" V\",e].join(\" \")),a}},{key:\"curve\",value:function(t,e,i,a,s,r){var o=[\"C\",t,e,i,a,s,r].join(\" \");return o}},{key:\"quadraticCurve\",value:function(t,e,i,a){return[\"Q\",t,e,i,a].join(\" \")}},{key:\"arc\",value:function(t,e,i,a,s,r,o){var n=arguments.length>7&&void 0!==arguments[7]&&arguments[7],l=\"A\";n&&(l=\"a\");var h=[l,t,e,i,a,s,r,o].join(\" \");return h}},{key:\"renderPaths\",value:function(t){var i,a=t.j,s=t.realIndex,r=t.pathFrom,o=t.pathTo,n=t.stroke,l=t.strokeWidth,h=t.strokeLinecap,c=t.fill,d=t.animationDelay,g=t.initialSpeed,u=t.dataChangeSpeed,f=t.className,p=t.shouldClipToGrid,x=void 0===p||p,m=t.bindEventsOnPaths,y=void 0===m||m,w=t.drawShadow,k=void 0===w||w,A=this.w,S=new v(this.ctx),C=new b(this.ctx),L=this.w.config.chart.animations.enabled,P=L&&this.w.config.chart.animations.dynamicAnimation.enabled,T=!!(L&&!A.globals.resized||P&&A.globals.dataChanged&&A.globals.shouldAnimate);T?i=r:(i=o,A.globals.animationEnded=!0);var M=A.config.stroke.dashArray,I=0;I=Array.isArray(M)?M[s]:A.config.stroke.dashArray;var z=this.drawPath({d:i,stroke:n,strokeWidth:l,fill:c,fillOpacity:1,classes:f,strokeLinecap:h,strokeDashArray:I});if(z.attr(\"index\",s),x&&z.attr({\"clip-path\":\"url(#gridRectMask\".concat(A.globals.cuid,\")\")}),\"none\"!==A.config.states.normal.filter.type)S.getDefaultFilter(z,s);else if(A.config.chart.dropShadow.enabled&&k&&(!A.config.chart.dropShadow.enabledOnSeries||A.config.chart.dropShadow.enabledOnSeries&&-1!==A.config.chart.dropShadow.enabledOnSeries.indexOf(s))){var X=A.config.chart.dropShadow;S.dropShadow(z,X,s)}y&&(z.node.addEventListener(\"mouseenter\",this.pathMouseEnter.bind(this,z)),z.node.addEventListener(\"mouseleave\",this.pathMouseLeave.bind(this,z)),z.node.addEventListener(\"mousedown\",this.pathMouseDown.bind(this,z))),z.attr({pathTo:o,pathFrom:r});var E={el:z,j:a,realIndex:s,pathFrom:r,pathTo:o,fill:c,strokeWidth:l,delay:d};return!L||A.globals.resized||A.globals.dataChanged?!A.globals.resized&&A.globals.dataChanged||C.showDelayedElements():C.animatePathsGradually(e(e({},E),{},{speed:g})),A.globals.dataChanged&&P&&T&&C.animatePathsGradually(e(e({},E),{},{speed:u})),z}},{key:\"drawPattern\",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\"#a8a8a8\",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=this.w,o=r.globals.dom.Paper.pattern(e,i,(function(r){\"horizontalLines\"===t?r.line(0,0,i,0).stroke({color:a,width:s+1}):\"verticalLines\"===t?r.line(0,0,0,e).stroke({color:a,width:s+1}):\"slantedLines\"===t?r.line(0,0,e,i).stroke({color:a,width:s}):\"squares\"===t?r.rect(e,i).fill(\"none\").stroke({color:a,width:s}):\"circles\"===t&&r.circle(e).fill(\"none\").stroke({color:a,width:s})}));return o}},{key:\"drawGradient\",value:function(t,e,i,a,s){var r,o=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,n=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,l=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:0,c=this.w;e.length<9&&0===e.indexOf(\"#\")&&(e=x.hexToRgba(e,a)),i.length<9&&0===i.indexOf(\"#\")&&(i=x.hexToRgba(i,s));var d=0,g=1,u=1,f=null;null!==n&&(d=void 0!==n[0]?n[0]/100:0,g=void 0!==n[1]?n[1]/100:1,u=void 0!==n[2]?n[2]/100:1,f=void 0!==n[3]?n[3]/100:null);var p=!(\"donut\"!==c.config.chart.type&&\"pie\"!==c.config.chart.type&&\"polarArea\"!==c.config.chart.type&&\"bubble\"!==c.config.chart.type);if(r=null===l||0===l.length?c.globals.dom.Paper.gradient(p?\"radial\":\"linear\",(function(t){t.at(d,e,a),t.at(g,i,s),t.at(u,i,s),null!==f&&t.at(f,e,a)})):c.globals.dom.Paper.gradient(p?\"radial\":\"linear\",(function(t){(Array.isArray(l[h])?l[h]:l).forEach((function(e){t.at(e.offset/100,e.color,e.opacity)}))})),p){var b=c.globals.gridWidth/2,v=c.globals.gridHeight/2;\"bubble\"!==c.config.chart.type?r.attr({gradientUnits:\"userSpaceOnUse\",cx:b,cy:v,r:o}):r.attr({cx:.5,cy:.5,r:.8,fx:.2,fy:.2})}else\"vertical\"===t?r.from(0,0).to(0,1):\"diagonal\"===t?r.from(0,0).to(1,1):\"horizontal\"===t?r.from(0,1).to(1,1):\"diagonal2\"===t&&r.from(1,0).to(0,1);return r}},{key:\"getTextBasedOnMaxWidth\",value:function(t){var e=t.text,i=t.maxWidth,a=t.fontSize,s=t.fontFamily,r=this.getTextRects(e,a,s),o=r.width/e.length,n=Math.floor(i/o);return i<r.width?e.slice(0,n-3)+\"...\":e}},{key:\"drawText\",value:function(t){var i=this,a=t.x,s=t.y,r=t.text,o=t.textAnchor,n=t.fontSize,l=t.fontFamily,h=t.fontWeight,c=t.foreColor,d=t.opacity,g=t.maxWidth,u=t.cssClass,f=void 0===u?\"\":u,p=t.isPlainText,x=void 0===p||p,b=this.w;void 0===r&&(r=\"\");var v=r;o||(o=\"start\"),c&&c.length||(c=b.config.chart.foreColor),l=l||b.config.chart.fontFamily,h=h||\"regular\";var m,y={maxWidth:g,fontSize:n=n||\"11px\",fontFamily:l};return Array.isArray(r)?m=b.globals.dom.Paper.text((function(t){for(var a=0;a<r.length;a++)v=r[a],g&&(v=i.getTextBasedOnMaxWidth(e({text:r[a]},y))),0===a?t.tspan(v):t.tspan(v).newLine()})):(g&&(v=this.getTextBasedOnMaxWidth(e({text:r},y))),m=x?b.globals.dom.Paper.plain(r):b.globals.dom.Paper.text((function(t){return t.tspan(v)}))),m.attr({x:a,y:s,\"text-anchor\":o,\"dominant-baseline\":\"auto\",\"font-size\":n,\"font-family\":l,\"font-weight\":h,fill:c,class:\"apexcharts-text \"+f}),m.node.style.fontFamily=l,m.node.style.opacity=d,m}},{key:\"drawMarker\",value:function(t,e,i){t=t||0;var a=i.pSize||0,s=null;if(\"square\"===i.shape||\"rect\"===i.shape){var r=void 0===i.pRadius?a/2:i.pRadius;null!==e&&a||(a=0,r=0);var o=1.2*a+r,n=this.drawRect(o,o,o,o,r);n.attr({x:t-o/2,y:e-o/2,cx:t,cy:e,class:i.class?i.class:\"\",fill:i.pointFillColor,\"fill-opacity\":i.pointFillOpacity?i.pointFillOpacity:1,stroke:i.pointStrokeColor,\"stroke-width\":i.pointStrokeWidth?i.pointStrokeWidth:0,\"stroke-opacity\":i.pointStrokeOpacity?i.pointStrokeOpacity:1}),s=n}else\"circle\"!==i.shape&&i.shape||(x.isNumber(e)||(a=0,e=0),s=this.drawCircle(a,{cx:t,cy:e,class:i.class?i.class:\"\",stroke:i.pointStrokeColor,fill:i.pointFillColor,\"fill-opacity\":i.pointFillOpacity?i.pointFillOpacity:1,\"stroke-width\":i.pointStrokeWidth?i.pointStrokeWidth:0,\"stroke-opacity\":i.pointStrokeOpacity?i.pointStrokeOpacity:1}));return s}},{key:\"pathMouseEnter\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10);if(\"function\"==typeof i.config.chart.events.dataPointMouseEnter&&i.config.chart.events.dataPointMouseEnter(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent(\"dataPointMouseEnter\",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),(\"none\"===i.config.states.active.filter.type||\"true\"!==t.node.getAttribute(\"selected\"))&&\"none\"!==i.config.states.hover.filter.type&&!i.globals.isTouchDevice){var o=i.config.states.hover.filter;a.applyFilter(t,s,o.type,o.value)}}},{key:\"pathMouseLeave\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10);\"function\"==typeof i.config.chart.events.dataPointMouseLeave&&i.config.chart.events.dataPointMouseLeave(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent(\"dataPointMouseLeave\",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),\"none\"!==i.config.states.active.filter.type&&\"true\"===t.node.getAttribute(\"selected\")||\"none\"!==i.config.states.hover.filter.type&&a.getDefaultFilter(t,s)}},{key:\"pathMouseDown\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10),o=\"false\";if(\"true\"===t.node.getAttribute(\"selected\")){if(t.node.setAttribute(\"selected\",\"false\"),i.globals.selectedDataPoints[s].indexOf(r)>-1){var n=i.globals.selectedDataPoints[s].indexOf(r);i.globals.selectedDataPoints[s].splice(n,1)}}else{if(!i.config.states.active.allowMultipleDataPointsSelection&&i.globals.selectedDataPoints.length>0){i.globals.selectedDataPoints=[];var l=i.globals.dom.Paper.select(\".apexcharts-series path\").members,h=i.globals.dom.Paper.select(\".apexcharts-series circle, .apexcharts-series rect\").members,c=function(t){Array.prototype.forEach.call(t,(function(t){t.node.setAttribute(\"selected\",\"false\"),a.getDefaultFilter(t,s)}))};c(l),c(h)}t.node.setAttribute(\"selected\",\"true\"),o=\"true\",void 0===i.globals.selectedDataPoints[s]&&(i.globals.selectedDataPoints[s]=[]),i.globals.selectedDataPoints[s].push(r)}if(\"true\"===o){var d=i.config.states.active.filter;if(\"none\"!==d)a.applyFilter(t,s,d.type,d.value);else if(\"none\"!==i.config.states.hover.filter&&!i.globals.isTouchDevice){var g=i.config.states.hover.filter;a.applyFilter(t,s,g.type,g.value)}}else if(\"none\"!==i.config.states.active.filter.type)if(\"none\"===i.config.states.hover.filter.type||i.globals.isTouchDevice)a.getDefaultFilter(t,s);else{g=i.config.states.hover.filter;a.applyFilter(t,s,g.type,g.value)}\"function\"==typeof i.config.chart.events.dataPointSelection&&i.config.chart.events.dataPointSelection(e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}),e&&this.ctx.events.fireEvent(\"dataPointSelection\",[e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}])}},{key:\"rotateAroundCenter\",value:function(t){var e={};return t&&\"function\"==typeof t.getBBox&&(e=t.getBBox()),{x:e.x+e.width/2,y:e.y+e.height/2}}},{key:\"getTextRects\",value:function(t,e,i,a){var s=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],r=this.w,o=this.drawText({x:-200,y:-200,text:t,textAnchor:\"start\",fontSize:e,fontFamily:i,foreColor:\"#fff\",opacity:0});a&&o.attr(\"transform\",a),r.globals.dom.Paper.add(o);var n=o.bbox();return s||(n=o.node.getBoundingClientRect()),o.remove(),{width:n.width,height:n.height}}},{key:\"placeTextWithEllipsis\",value:function(t,e,i){if(\"function\"==typeof t.getComputedTextLength&&(t.textContent=e,e.length>0&&t.getComputedTextLength()>=i/1.1)){for(var a=e.length-3;a>0;a-=3)if(t.getSubStringLength(0,a)<=i/1.1)return void(t.textContent=e.substring(0,a)+\"...\");t.textContent=\".\"}}}],[{key:\"setAttrs\",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}}]),t}(),y=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"getStackedSeriesTotals\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=this.w,i=[];if(0===e.globals.series.length)return i;for(var a=0;a<e.globals.series[e.globals.maxValsInArrayIndex].length;a++){for(var s=0,r=0;r<e.globals.series.length;r++)void 0!==e.globals.series[r][a]&&-1===t.indexOf(r)&&(s+=e.globals.series[r][a]);i.push(s)}return i}},{key:\"getSeriesTotalByIndex\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return null===t?this.w.config.series.reduce((function(t,e){return t+e}),0):this.w.globals.series[t].reduce((function(t,e){return t+e}),0)}},{key:\"isSeriesNull\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return 0===(null===t?this.w.config.series.filter((function(t){return null!==t})):this.w.config.series[t].data.filter((function(t){return null!==t}))).length}},{key:\"seriesHaveSameValues\",value:function(t){return this.w.globals.series[t].every((function(t,e,i){return t===i[0]}))}},{key:\"getCategoryLabels\",value:function(t){var e=this.w,i=t.slice();return e.config.xaxis.convertedCatToNumeric&&(i=t.map((function(t,i){return e.config.xaxis.labels.formatter(t-e.globals.minX+1)}))),i}},{key:\"getLargestSeries\",value:function(){var t=this.w;t.globals.maxValsInArrayIndex=t.globals.series.map((function(t){return t.length})).indexOf(Math.max.apply(Math,t.globals.series.map((function(t){return t.length}))))}},{key:\"getLargestMarkerSize\",value:function(){var t=this.w,e=0;return t.globals.markers.size.forEach((function(t){e=Math.max(e,t)})),t.config.markers.discrete&&t.config.markers.discrete.length&&t.config.markers.discrete.forEach((function(t){e=Math.max(e,t.size)})),e>0&&(e+=t.config.markers.hover.sizeOffset+1),t.globals.markers.largestSize=e,e}},{key:\"getSeriesTotals\",value:function(){var t=this.w;t.globals.seriesTotals=t.globals.series.map((function(t,e){var i=0;if(Array.isArray(t))for(var a=0;a<t.length;a++)i+=t[a];else i+=t;return i}))}},{key:\"getSeriesTotalsXRange\",value:function(t,e){var i=this.w;return i.globals.series.map((function(a,s){for(var r=0,o=0;o<a.length;o++)i.globals.seriesX[s][o]>t&&i.globals.seriesX[s][o]<e&&(r+=a[o]);return r}))}},{key:\"getPercentSeries\",value:function(){var t=this.w;t.globals.seriesPercent=t.globals.series.map((function(e,i){var a=[];if(Array.isArray(e))for(var s=0;s<e.length;s++){var r=t.globals.stackedSeriesTotals[s],o=0;r&&(o=100*e[s]/r),a.push(o)}else{var n=100*e/t.globals.seriesTotals.reduce((function(t,e){return t+e}),0);a.push(n)}return a}))}},{key:\"getCalculatedRatios\",value:function(){var t,e,i,a,s=this.w.globals,r=[],o=0,n=[],l=.1,h=0;if(s.yRange=[],s.isMultipleYAxis)for(var c=0;c<s.minYArr.length;c++)s.yRange.push(Math.abs(s.minYArr[c]-s.maxYArr[c])),n.push(0);else s.yRange.push(Math.abs(s.minY-s.maxY));s.xRange=Math.abs(s.maxX-s.minX),s.zRange=Math.abs(s.maxZ-s.minZ);for(var d=0;d<s.yRange.length;d++)r.push(s.yRange[d]/s.gridHeight);if(e=s.xRange/s.gridWidth,i=Math.abs(s.initialMaxX-s.initialMinX)/s.gridWidth,t=s.yRange/s.gridWidth,a=s.xRange/s.gridHeight,(o=s.zRange/s.gridHeight*16)||(o=1),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(s.hasNegs=!0),s.isMultipleYAxis){n=[];for(var g=0;g<r.length;g++)n.push(-s.minYArr[g]/r[g])}else n.push(-s.minY/r[0]),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(l=-s.minY/t,h=s.minX/e);return{yRatio:r,invertedYRatio:t,zRatio:o,xRatio:e,initialXRatio:i,invertedXRatio:a,baseLineInvertedY:l,baseLineY:n,baseLineX:h}}},{key:\"getLogSeries\",value:function(t){var e=this,i=this.w;return i.globals.seriesLog=t.map((function(t,a){return i.config.yaxis[a]&&i.config.yaxis[a].logarithmic?t.map((function(t){return null===t?null:e.getLogVal(i.config.yaxis[a].logBase,t,a)})):t})),i.globals.invalidLogScale?t:i.globals.seriesLog}},{key:\"getBaseLog\",value:function(t,e){return Math.log(e)/Math.log(t)}},{key:\"getLogVal\",value:function(t,e,i){if(0===e)return 0;var a=this.w,s=0===a.globals.minYArr[i]?-1:this.getBaseLog(t,a.globals.minYArr[i]),r=(0===a.globals.maxYArr[i]?0:this.getBaseLog(t,a.globals.maxYArr[i]))-s;return e<1?e/r:(this.getBaseLog(t,e)-s)/r}},{key:\"getLogYRatios\",value:function(t){var e=this,i=this.w,a=this.w.globals;return a.yLogRatio=t.slice(),a.logYRange=a.yRange.map((function(t,s){if(i.config.yaxis[s]&&e.w.config.yaxis[s].logarithmic){var r,o=-Number.MAX_VALUE,n=Number.MIN_VALUE;return a.seriesLog.forEach((function(t,e){t.forEach((function(t){i.config.yaxis[e]&&i.config.yaxis[e].logarithmic&&(o=Math.max(t,o),n=Math.min(t,n))}))})),r=Math.pow(a.yRange[s],Math.abs(n-o)/a.yRange[s]),a.yLogRatio[s]=r/a.gridHeight,r}})),a.invalidLogScale?t.slice():a.yLogRatio}}],[{key:\"checkComboSeries\",value:function(t){var e=!1,i=0,a=0;return t.length&&void 0!==t[0].type&&t.forEach((function(t){\"bar\"!==t.type&&\"column\"!==t.type&&\"candlestick\"!==t.type&&\"boxPlot\"!==t.type||i++,void 0!==t.type&&a++})),a>0&&(e=!0),{comboBarCount:i,comboCharts:e}}},{key:\"extendArrayProps\",value:function(t,e,i){return e.yaxis&&(e=t.extendYAxis(e,i)),e.annotations&&(e.annotations.yaxis&&(e=t.extendYAxisAnnotations(e)),e.annotations.xaxis&&(e=t.extendXAxisAnnotations(e)),e.annotations.points&&(e=t.extendPointAnnotations(e))),e}}]),t}(),w=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e}return r(t,[{key:\"setOrientations\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w;if(\"vertical\"===t.label.orientation){var a=null!==e?e:0,s=i.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-annotations .apexcharts-xaxis-annotation-label[rel='\".concat(a,\"']\"));if(null!==s){var r=s.getBoundingClientRect();s.setAttribute(\"x\",parseFloat(s.getAttribute(\"x\"))-r.height+4),\"top\"===t.label.position?s.setAttribute(\"y\",parseFloat(s.getAttribute(\"y\"))+r.width):s.setAttribute(\"y\",parseFloat(s.getAttribute(\"y\"))-r.width);var o=this.annoCtx.graphics.rotateAroundCenter(s),n=o.x,l=o.y;s.setAttribute(\"transform\",\"rotate(-90 \".concat(n,\" \").concat(l,\")\"))}}}},{key:\"addBackgroundToAnno\",value:function(t,e){var i=this.w;if(!t||void 0===e.label.text||void 0!==e.label.text&&!String(e.label.text).trim())return null;var a=i.globals.dom.baseEl.querySelector(\".apexcharts-grid\").getBoundingClientRect(),s=t.getBoundingClientRect(),r=e.label.style.padding.left,o=e.label.style.padding.right,n=e.label.style.padding.top,l=e.label.style.padding.bottom;\"vertical\"===e.label.orientation&&(n=e.label.style.padding.left,l=e.label.style.padding.right,r=e.label.style.padding.top,o=e.label.style.padding.bottom);var h=s.left-a.left-r,c=s.top-a.top-n,d=this.annoCtx.graphics.drawRect(h-i.globals.barPadForNumericAxis,c,s.width+r+o,s.height+n+l,e.label.borderRadius,e.label.style.background,1,e.label.borderWidth,e.label.borderColor,0);return e.id&&d.node.classList.add(e.id),d}},{key:\"annotationsBackground\",value:function(){var t=this,e=this.w,i=function(i,a,s){var r=e.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations .apexcharts-\").concat(s,\"-annotation-label[rel='\").concat(a,\"']\"));if(r){var o=r.parentNode,n=t.addBackgroundToAnno(r,i);n&&(o.insertBefore(n.node,r),i.label.mouseEnter&&n.node.addEventListener(\"mouseenter\",i.label.mouseEnter.bind(t,i)),i.label.mouseLeave&&n.node.addEventListener(\"mouseleave\",i.label.mouseLeave.bind(t,i)),i.label.click&&n.node.addEventListener(\"click\",i.label.click.bind(t,i)))}};e.config.annotations.xaxis.map((function(t,e){i(t,e,\"xaxis\")})),e.config.annotations.yaxis.map((function(t,e){i(t,e,\"yaxis\")})),e.config.annotations.points.map((function(t,e){i(t,e,\"point\")}))}},{key:\"getY1Y2\",value:function(t,e){var i,a=\"y1\"===t?e.y:e.y2,s=this.w;if(this.annoCtx.invertAxis){var r=s.globals.labels.indexOf(a);s.config.xaxis.convertedCatToNumeric&&(r=s.globals.categoryLabels.indexOf(a));var o=s.globals.dom.baseEl.querySelector(\".apexcharts-yaxis-texts-g text:nth-child(\"+(r+1)+\")\");o&&(i=parseFloat(o.getAttribute(\"y\")))}else{var n;if(s.config.yaxis[e.yAxisIndex].logarithmic)n=(a=new y(this.annoCtx.ctx).getLogVal(a,e.yAxisIndex))/s.globals.yLogRatio[e.yAxisIndex];else n=(a-s.globals.minYArr[e.yAxisIndex])/(s.globals.yRange[e.yAxisIndex]/s.globals.gridHeight);i=s.globals.gridHeight-n,!e.marker||void 0!==e.y&&null!==e.y||(i=0),s.config.yaxis[e.yAxisIndex]&&s.config.yaxis[e.yAxisIndex].reversed&&(i=n)}return\"string\"==typeof a&&a.indexOf(\"px\")>-1&&(i=parseFloat(a)),i}},{key:\"getX1X2\",value:function(t,e){var i=this.w,a=this.annoCtx.invertAxis?i.globals.minY:i.globals.minX,s=this.annoCtx.invertAxis?i.globals.maxY:i.globals.maxX,r=this.annoCtx.invertAxis?i.globals.yRange[0]:i.globals.xRange,o=(e.x-a)/(r/i.globals.gridWidth);this.annoCtx.inversedReversedAxis&&(o=(s-e.x)/(r/i.globals.gridWidth)),\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric||this.annoCtx.invertAxis||i.globals.dataFormatXNumeric||(o=this.getStringX(e.x));var n=(e.x2-a)/(r/i.globals.gridWidth);return this.annoCtx.inversedReversedAxis&&(n=(s-e.x2)/(r/i.globals.gridWidth)),\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric||this.annoCtx.invertAxis||i.globals.dataFormatXNumeric||(n=this.getStringX(e.x2)),void 0!==e.x&&null!==e.x||!e.marker||(o=i.globals.gridWidth),\"x1\"===t&&\"string\"==typeof e.x&&e.x.indexOf(\"px\")>-1&&(o=parseFloat(e.x)),\"x2\"===t&&\"string\"==typeof e.x2&&e.x2.indexOf(\"px\")>-1&&(n=parseFloat(e.x2)),\"x1\"===t?o:n}},{key:\"getStringX\",value:function(t){var e=this.w,i=t;e.config.xaxis.convertedCatToNumeric&&e.globals.categoryLabels.length&&(t=e.globals.categoryLabels.indexOf(t)+1);var a=e.globals.labels.indexOf(t),s=e.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-texts-g text:nth-child(\"+(a+1)+\")\");return s&&(i=parseFloat(s.getAttribute(\"x\"))),i}}]),t}(),k=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e,this.invertAxis=this.annoCtx.invertAxis,this.helpers=new w(this.annoCtx)}return r(t,[{key:\"addXaxisAnnotation\",value:function(t,e,i){var a,s=this.w,r=this.helpers.getX1X2(\"x1\",t),o=t.label.text,n=t.strokeDashArray;if(x.isNumber(r)){if(null===t.x2||void 0===t.x2){var l=this.annoCtx.graphics.drawLine(r+t.offsetX,0+t.offsetY,r+t.offsetX,s.globals.gridHeight+t.offsetY,t.borderColor,n,t.borderWidth);e.appendChild(l.node),t.id&&l.node.classList.add(t.id)}else{if((a=this.helpers.getX1X2(\"x2\",t))<r){var h=r;r=a,a=h}var c=this.annoCtx.graphics.drawRect(r+t.offsetX,0+t.offsetY,a-r,s.globals.gridHeight+t.offsetY,0,t.fillColor,t.opacity,1,t.borderColor,n);c.node.classList.add(\"apexcharts-annotation-rect\"),c.attr(\"clip-path\",\"url(#gridRectMask\".concat(s.globals.cuid,\")\")),e.appendChild(c.node),t.id&&c.node.classList.add(t.id)}var d=this.annoCtx.graphics.getTextRects(o,parseFloat(t.label.style.fontSize)),g=\"top\"===t.label.position?4:\"center\"===t.label.position?s.globals.gridHeight/2+(\"vertical\"===t.label.orientation?d.width/2:0):s.globals.gridHeight,u=this.annoCtx.graphics.drawText({x:r+t.label.offsetX,y:g+t.label.offsetY-(\"vertical\"===t.label.orientation?\"top\"===t.label.position?d.width/2-12:-d.width/2:0),text:o,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-xaxis-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});u.attr({rel:i}),e.appendChild(u.node),this.annoCtx.helpers.setOrientations(t,i)}}},{key:\"drawXAxisAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-xaxis-annotations\"});return e.config.annotations.xaxis.map((function(e,a){t.addXaxisAnnotation(e,i.node,a)})),i}}]),t}(),A=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e,this.helpers=new w(this.annoCtx)}return r(t,[{key:\"addYaxisAnnotation\",value:function(t,e,i){var a,s=this.w,r=t.strokeDashArray,o=this.helpers.getY1Y2(\"y1\",t),n=t.label.text;if(null===t.y2||void 0===t.y2){var l=this.annoCtx.graphics.drawLine(0+t.offsetX,o+t.offsetY,this._getYAxisAnnotationWidth(t),o+t.offsetY,t.borderColor,r,t.borderWidth);e.appendChild(l.node),t.id&&l.node.classList.add(t.id)}else{if((a=this.helpers.getY1Y2(\"y2\",t))>o){var h=o;o=a,a=h}var c=this.annoCtx.graphics.drawRect(0+t.offsetX,a+t.offsetY,this._getYAxisAnnotationWidth(t),o-a,0,t.fillColor,t.opacity,1,t.borderColor,r);c.node.classList.add(\"apexcharts-annotation-rect\"),c.attr(\"clip-path\",\"url(#gridRectMask\".concat(s.globals.cuid,\")\")),e.appendChild(c.node),t.id&&c.node.classList.add(t.id)}var d=\"right\"===t.label.position?s.globals.gridWidth:\"center\"===t.label.position?s.globals.gridWidth/2:0,g=this.annoCtx.graphics.drawText({x:d+t.label.offsetX,y:(null!=a?a:o)+t.label.offsetY-3,text:n,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-yaxis-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});g.attr({rel:i}),e.appendChild(g.node)}},{key:\"_getYAxisAnnotationWidth\",value:function(t){var e=this.w;e.globals.gridWidth;return(t.width.indexOf(\"%\")>-1?e.globals.gridWidth*parseInt(t.width,10)/100:parseInt(t.width,10))+t.offsetX}},{key:\"drawYAxisAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-yaxis-annotations\"});return e.config.annotations.yaxis.map((function(e,a){t.addYaxisAnnotation(e,i.node,a)})),i}}]),t}(),S=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e,this.helpers=new w(this.annoCtx)}return r(t,[{key:\"addPointAnnotation\",value:function(t,e,i){this.w;var a=this.helpers.getX1X2(\"x1\",t),s=this.helpers.getY1Y2(\"y1\",t);if(x.isNumber(a)){var r={pSize:t.marker.size,pointStrokeWidth:t.marker.strokeWidth,pointFillColor:t.marker.fillColor,pointStrokeColor:t.marker.strokeColor,shape:t.marker.shape,pRadius:t.marker.radius,class:\"apexcharts-point-annotation-marker \".concat(t.marker.cssClass,\" \").concat(t.id?t.id:\"\")},o=this.annoCtx.graphics.drawMarker(a+t.marker.offsetX,s+t.marker.offsetY,r);e.appendChild(o.node);var n=t.label.text?t.label.text:\"\",l=this.annoCtx.graphics.drawText({x:a+t.label.offsetX,y:s+t.label.offsetY-t.marker.size-parseFloat(t.label.style.fontSize)/1.6,text:n,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-point-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});if(l.attr({rel:i}),e.appendChild(l.node),t.customSVG.SVG){var h=this.annoCtx.graphics.group({class:\"apexcharts-point-annotations-custom-svg \"+t.customSVG.cssClass});h.attr({transform:\"translate(\".concat(a+t.customSVG.offsetX,\", \").concat(s+t.customSVG.offsetY,\")\")}),h.node.innerHTML=t.customSVG.SVG,e.appendChild(h.node)}if(t.image.path){var c=t.image.width?t.image.width:20,d=t.image.height?t.image.height:20;o=this.annoCtx.addImage({x:a+t.image.offsetX-c/2,y:s+t.image.offsetY-d/2,width:c,height:d,path:t.image.path,appendTo:\".apexcharts-point-annotations\"})}t.mouseEnter&&o.node.addEventListener(\"mouseenter\",t.mouseEnter.bind(this,t)),t.mouseLeave&&o.node.addEventListener(\"mouseleave\",t.mouseLeave.bind(this,t)),t.click&&o.node.addEventListener(\"click\",t.click.bind(this,t))}}},{key:\"drawPointAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-point-annotations\"});return e.config.annotations.points.map((function(e,a){t.addPointAnnotation(e,i.node,a)})),i}}]),t}();var C={name:\"en\",options:{months:[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],shortMonths:[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],days:[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],shortDays:[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],toolbar:{exportToSVG:\"Download SVG\",exportToPNG:\"Download PNG\",exportToCSV:\"Download CSV\",menu:\"Menu\",selection:\"Selection\",selectionZoom:\"Selection Zoom\",zoomIn:\"Zoom In\",zoomOut:\"Zoom Out\",pan:\"Panning\",reset:\"Reset Zoom\"}}},L=function(){function t(){a(this,t),this.yAxis={show:!0,showAlways:!1,showForNullSeries:!0,seriesName:void 0,opposite:!1,reversed:!1,logarithmic:!1,logBase:10,tickAmount:void 0,forceNiceScale:!1,max:void 0,min:void 0,floating:!1,decimalsInFloat:void 0,labels:{show:!0,minWidth:0,maxWidth:160,offsetX:0,offsetY:0,align:void 0,rotate:0,padding:20,style:{colors:[],fontSize:\"11px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"},formatter:void 0},axisBorder:{show:!1,color:\"#e0e0e0\",width:1,offsetX:0,offsetY:0},axisTicks:{show:!1,color:\"#e0e0e0\",width:6,offsetX:0,offsetY:0},title:{text:void 0,rotate:-90,offsetY:0,offsetX:0,style:{color:void 0,fontSize:\"11px\",fontWeight:900,fontFamily:void 0,cssClass:\"\"}},tooltip:{enabled:!1,offsetX:0},crosshairs:{show:!0,position:\"front\",stroke:{color:\"#b6b6b6\",width:1,dashArray:0}}},this.pointAnnotation={id:void 0,x:0,y:null,yAxisIndex:0,seriesIndex:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,marker:{size:4,fillColor:\"#fff\",strokeWidth:2,strokeColor:\"#333\",shape:\"circle\",offsetX:0,offsetY:0,radius:2,cssClass:\"\"},label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"middle\",offsetX:0,offsetY:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}},customSVG:{SVG:void 0,cssClass:void 0,offsetX:0,offsetY:0},image:{path:void 0,width:20,height:20,offsetX:0,offsetY:0}},this.yAxisAnnotation={id:void 0,y:0,y2:null,strokeDashArray:1,fillColor:\"#c2c2c2\",borderColor:\"#c2c2c2\",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,width:\"100%\",yAxisIndex:0,label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"end\",position:\"right\",offsetX:0,offsetY:-3,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}}},this.xAxisAnnotation={id:void 0,x:0,x2:null,strokeDashArray:1,fillColor:\"#c2c2c2\",borderColor:\"#c2c2c2\",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"middle\",orientation:\"vertical\",position:\"top\",offsetX:0,offsetY:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}}},this.text={x:0,y:0,text:\"\",textAnchor:\"start\",foreColor:void 0,fontSize:\"13px\",fontFamily:void 0,fontWeight:400,appendTo:\".apexcharts-annotations\",backgroundColor:\"transparent\",borderColor:\"#c2c2c2\",borderRadius:0,borderWidth:0,paddingLeft:4,paddingRight:4,paddingTop:2,paddingBottom:2}}return r(t,[{key:\"init\",value:function(){return{annotations:{yaxis:[this.yAxisAnnotation],xaxis:[this.xAxisAnnotation],points:[this.pointAnnotation],texts:[],images:[],shapes:[]},chart:{animations:{enabled:!0,easing:\"easeinout\",speed:800,animateGradually:{delay:150,enabled:!0},dynamicAnimation:{enabled:!0,speed:350}},background:\"transparent\",locales:[C],defaultLocale:\"en\",dropShadow:{enabled:!1,enabledOnSeries:void 0,top:2,left:2,blur:4,color:\"#000\",opacity:.35},events:{animationEnd:void 0,beforeMount:void 0,mounted:void 0,updated:void 0,click:void 0,mouseMove:void 0,mouseLeave:void 0,xAxisLabelClick:void 0,legendClick:void 0,markerClick:void 0,selection:void 0,dataPointSelection:void 0,dataPointMouseEnter:void 0,dataPointMouseLeave:void 0,beforeZoom:void 0,beforeResetZoom:void 0,zoomed:void 0,scrolled:void 0,brushScrolled:void 0},foreColor:\"#373d3f\",fontFamily:\"Helvetica, Arial, sans-serif\",height:\"auto\",parentHeightOffset:15,redrawOnParentResize:!0,redrawOnWindowResize:!0,id:void 0,group:void 0,offsetX:0,offsetY:0,selection:{enabled:!1,type:\"x\",fill:{color:\"#24292e\",opacity:.1},stroke:{width:1,color:\"#24292e\",opacity:.4,dashArray:3},xaxis:{min:void 0,max:void 0},yaxis:{min:void 0,max:void 0}},sparkline:{enabled:!1},brush:{enabled:!1,autoScaleYaxis:!0,target:void 0},stacked:!1,stackType:\"normal\",toolbar:{show:!0,offsetX:0,offsetY:0,tools:{download:!0,selection:!0,zoom:!0,zoomin:!0,zoomout:!0,pan:!0,reset:!0,customIcons:[]},export:{csv:{filename:void 0,columnDelimiter:\",\",headerCategory:\"category\",headerValue:\"value\",dateFormatter:function(t){return new Date(t).toDateString()}},png:{filename:void 0},svg:{filename:void 0}},autoSelected:\"zoom\"},type:\"line\",width:\"100%\",zoom:{enabled:!0,type:\"x\",autoScaleYaxis:!1,zoomedArea:{fill:{color:\"#90CAF9\",opacity:.4},stroke:{color:\"#0D47A1\",opacity:.4,width:1}}}},plotOptions:{area:{fillTo:\"origin\"},bar:{horizontal:!1,columnWidth:\"70%\",barHeight:\"70%\",distributed:!1,borderRadius:0,borderRadiusApplication:\"around\",borderRadiusWhenStacked:\"last\",rangeBarOverlap:!0,rangeBarGroupRows:!1,hideZeroBarsWhenGrouped:!0,colors:{ranges:[],backgroundBarColors:[],backgroundBarOpacity:1,backgroundBarRadius:0},dataLabels:{position:\"top\",maxItems:100,hideOverflowingLabels:!0,orientation:\"horizontal\",total:{enabled:!1,formatter:void 0,offsetX:0,offsetY:0,style:{color:\"#373d3f\",fontSize:\"12px\",fontFamily:void 0,fontWeight:600}}}},bubble:{zScaling:!0,minBubbleRadius:void 0,maxBubbleRadius:void 0},candlestick:{colors:{upward:\"#00B746\",downward:\"#EF403C\"},wick:{useFillColor:!0}},boxPlot:{colors:{upper:\"#00E396\",lower:\"#008FFB\"}},heatmap:{radius:2,enableShades:!0,shadeIntensity:.5,reverseNegativeShade:!1,distributed:!1,useFillColorAsStroke:!1,colorScale:{inverse:!1,ranges:[],min:void 0,max:void 0}},treemap:{enableShades:!0,shadeIntensity:.5,distributed:!1,reverseNegativeShade:!1,useFillColorAsStroke:!1,colorScale:{inverse:!1,ranges:[],min:void 0,max:void 0}},radialBar:{inverseOrder:!1,startAngle:0,endAngle:360,offsetX:0,offsetY:0,hollow:{margin:5,size:\"50%\",background:\"transparent\",image:void 0,imageWidth:150,imageHeight:150,imageOffsetX:0,imageOffsetY:0,imageClipped:!0,position:\"front\",dropShadow:{enabled:!1,top:0,left:0,blur:3,color:\"#000\",opacity:.5}},track:{show:!0,startAngle:void 0,endAngle:void 0,background:\"#f2f2f2\",strokeWidth:\"97%\",opacity:1,margin:5,dropShadow:{enabled:!1,top:0,left:0,blur:3,color:\"#000\",opacity:.5}},dataLabels:{show:!0,name:{show:!0,fontSize:\"16px\",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:0,formatter:function(t){return t}},value:{show:!0,fontSize:\"14px\",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:16,formatter:function(t){return t+\"%\"}},total:{show:!1,label:\"Total\",fontSize:\"16px\",fontWeight:600,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)/t.globals.series.length+\"%\"}}}},pie:{customScale:1,offsetX:0,offsetY:0,startAngle:0,endAngle:360,expandOnClick:!0,dataLabels:{offset:0,minAngleToShowLabel:10},donut:{size:\"65%\",background:\"transparent\",labels:{show:!1,name:{show:!0,fontSize:\"16px\",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:-10,formatter:function(t){return t}},value:{show:!0,fontSize:\"20px\",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:10,formatter:function(t){return t}},total:{show:!1,showAlways:!1,label:\"Total\",fontSize:\"16px\",fontWeight:400,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)}}}}},polarArea:{rings:{strokeWidth:1,strokeColor:\"#e8e8e8\"},spokes:{strokeWidth:1,connectorColors:\"#e8e8e8\"}},radar:{size:void 0,offsetX:0,offsetY:0,polygons:{strokeWidth:1,strokeColors:\"#e8e8e8\",connectorColors:\"#e8e8e8\",fill:{colors:void 0}}}},colors:void 0,dataLabels:{enabled:!0,enabledOnSeries:void 0,formatter:function(t){return null!==t?t:\"\"},textAnchor:\"middle\",distributed:!1,offsetX:0,offsetY:0,style:{fontSize:\"12px\",fontFamily:void 0,fontWeight:600,colors:void 0},background:{enabled:!0,foreColor:\"#fff\",borderRadius:2,padding:4,opacity:.9,borderWidth:1,borderColor:\"#fff\",dropShadow:{enabled:!1,top:1,left:1,blur:1,color:\"#000\",opacity:.45}},dropShadow:{enabled:!1,top:1,left:1,blur:1,color:\"#000\",opacity:.45}},fill:{type:\"solid\",colors:void 0,opacity:.85,gradient:{shade:\"dark\",type:\"horizontal\",shadeIntensity:.5,gradientToColors:void 0,inverseColors:!0,opacityFrom:1,opacityTo:1,stops:[0,50,100],colorStops:[]},image:{src:[],width:void 0,height:void 0},pattern:{style:\"squares\",width:6,height:6,strokeWidth:2}},forecastDataPoints:{count:0,fillOpacity:.5,strokeWidth:void 0,dashArray:4},grid:{show:!0,borderColor:\"#e0e0e0\",strokeDashArray:0,position:\"back\",xaxis:{lines:{show:!1}},yaxis:{lines:{show:!0}},row:{colors:void 0,opacity:.5},column:{colors:void 0,opacity:.5},padding:{top:0,right:10,bottom:0,left:12}},labels:[],legend:{show:!0,showForSingleSeries:!1,showForNullSeries:!0,showForZeroSeries:!0,floating:!1,position:\"bottom\",horizontalAlign:\"center\",inverseOrder:!1,fontSize:\"12px\",fontFamily:void 0,fontWeight:400,width:void 0,height:void 0,formatter:void 0,tooltipHoverFormatter:void 0,offsetX:-20,offsetY:4,customLegendItems:[],labels:{colors:void 0,useSeriesColors:!1},markers:{width:12,height:12,strokeWidth:0,fillColors:void 0,strokeColor:\"#fff\",radius:12,customHTML:void 0,offsetX:0,offsetY:0,onClick:void 0},itemMargin:{horizontal:5,vertical:2},onItemClick:{toggleDataSeries:!0},onItemHover:{highlightDataSeries:!0}},markers:{discrete:[],size:0,colors:void 0,strokeColors:\"#fff\",strokeWidth:2,strokeOpacity:.9,strokeDashArray:0,fillOpacity:1,shape:\"circle\",width:8,height:8,radius:2,offsetX:0,offsetY:0,onClick:void 0,onDblClick:void 0,showNullDataPoints:!0,hover:{size:void 0,sizeOffset:3}},noData:{text:void 0,align:\"center\",verticalAlign:\"middle\",offsetX:0,offsetY:0,style:{color:void 0,fontSize:\"14px\",fontFamily:void 0}},responsive:[],series:void 0,states:{normal:{filter:{type:\"none\",value:0}},hover:{filter:{type:\"lighten\",value:.1}},active:{allowMultipleDataPointsSelection:!1,filter:{type:\"darken\",value:.5}}},title:{text:void 0,align:\"left\",margin:5,offsetX:0,offsetY:0,floating:!1,style:{fontSize:\"14px\",fontWeight:900,fontFamily:void 0,color:void 0}},subtitle:{text:void 0,align:\"left\",margin:5,offsetX:0,offsetY:30,floating:!1,style:{fontSize:\"12px\",fontWeight:400,fontFamily:void 0,color:void 0}},stroke:{show:!0,curve:\"smooth\",lineCap:\"butt\",width:2,colors:void 0,dashArray:0,fill:{type:\"solid\",colors:void 0,opacity:.85,gradient:{shade:\"dark\",type:\"horizontal\",shadeIntensity:.5,gradientToColors:void 0,inverseColors:!0,opacityFrom:1,opacityTo:1,stops:[0,50,100],colorStops:[]}}},tooltip:{enabled:!0,enabledOnSeries:void 0,shared:!0,followCursor:!1,intersect:!1,inverseOrder:!1,custom:void 0,fillSeriesColor:!1,theme:\"light\",cssClass:\"\",style:{fontSize:\"12px\",fontFamily:void 0},onDatasetHover:{highlightDataSeries:!1},x:{show:!0,format:\"dd MMM\",formatter:void 0},y:{formatter:void 0,title:{formatter:function(t){return t?t+\": \":\"\"}}},z:{formatter:void 0,title:\"Size: \"},marker:{show:!0,fillColors:void 0},items:{display:\"flex\"},fixed:{enabled:!1,position:\"topRight\",offsetX:0,offsetY:0}},xaxis:{type:\"category\",categories:[],convertedCatToNumeric:!1,offsetX:0,offsetY:0,overwriteCategories:void 0,labels:{show:!0,rotate:-45,rotateAlways:!1,hideOverlappingLabels:!0,trim:!1,minHeight:void 0,maxHeight:120,showDuplicates:!0,style:{colors:[],fontSize:\"12px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"},offsetX:0,offsetY:0,format:void 0,formatter:void 0,datetimeUTC:!0,datetimeFormatter:{year:\"yyyy\",month:\"MMM 'yy\",day:\"dd MMM\",hour:\"HH:mm\",minute:\"HH:mm:ss\",second:\"HH:mm:ss\"}},group:{groups:[],style:{colors:[],fontSize:\"12px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"}},axisBorder:{show:!0,color:\"#e0e0e0\",width:\"100%\",height:1,offsetX:0,offsetY:0},axisTicks:{show:!0,color:\"#e0e0e0\",height:6,offsetX:0,offsetY:0},tickAmount:void 0,tickPlacement:\"on\",min:void 0,max:void 0,range:void 0,floating:!1,decimalsInFloat:void 0,position:\"bottom\",title:{text:void 0,offsetX:0,offsetY:0,style:{color:void 0,fontSize:\"12px\",fontWeight:900,fontFamily:void 0,cssClass:\"\"}},crosshairs:{show:!0,width:1,position:\"back\",opacity:.9,stroke:{color:\"#b6b6b6\",width:1,dashArray:3},fill:{type:\"solid\",color:\"#B1B9C4\",gradient:{colorFrom:\"#D8E3F0\",colorTo:\"#BED1E6\",stops:[0,100],opacityFrom:.4,opacityTo:.5}},dropShadow:{enabled:!1,left:0,top:0,blur:1,opacity:.4}},tooltip:{enabled:!0,offsetY:0,formatter:void 0,style:{fontSize:\"12px\",fontFamily:void 0}}},yaxis:this.yAxis,theme:{mode:\"light\",palette:\"palette1\",monochrome:{enabled:!1,color:\"#008FFB\",shadeTo:\"light\",shadeIntensity:.65}}}}}]),t}(),P=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.graphics=new m(this.ctx),this.w.globals.isBarHorizontal&&(this.invertAxis=!0),this.helpers=new w(this),this.xAxisAnnotations=new k(this),this.yAxisAnnotations=new A(this),this.pointsAnnotations=new S(this),this.w.globals.isBarHorizontal&&this.w.config.yaxis[0].reversed&&(this.inversedReversedAxis=!0),this.xDivision=this.w.globals.gridWidth/this.w.globals.dataPoints}return r(t,[{key:\"drawAxesAnnotations\",value:function(){var t=this.w;if(t.globals.axisCharts){for(var e=this.yAxisAnnotations.drawYAxisAnnotations(),i=this.xAxisAnnotations.drawXAxisAnnotations(),a=this.pointsAnnotations.drawPointAnnotations(),s=t.config.chart.animations.enabled,r=[e,i,a],o=[i.node,e.node,a.node],n=0;n<3;n++)t.globals.dom.elGraphical.add(r[n]),!s||t.globals.resized||t.globals.dataChanged||\"scatter\"!==t.config.chart.type&&\"bubble\"!==t.config.chart.type&&t.globals.dataPoints>1&&o[n].classList.add(\"apexcharts-element-hidden\"),t.globals.delayedElements.push({el:o[n],index:0});this.helpers.annotationsBackground()}}},{key:\"drawImageAnnos\",value:function(){var t=this;this.w.config.annotations.images.map((function(e,i){t.addImage(e,i)}))}},{key:\"drawTextAnnos\",value:function(){var t=this;this.w.config.annotations.texts.map((function(e,i){t.addText(e,i)}))}},{key:\"addXaxisAnnotation\",value:function(t,e,i){this.xAxisAnnotations.addXaxisAnnotation(t,e,i)}},{key:\"addYaxisAnnotation\",value:function(t,e,i){this.yAxisAnnotations.addYaxisAnnotation(t,e,i)}},{key:\"addPointAnnotation\",value:function(t,e,i){this.pointsAnnotations.addPointAnnotation(t,e,i)}},{key:\"addText\",value:function(t,e){var i=t.x,a=t.y,s=t.text,r=t.textAnchor,o=t.foreColor,n=t.fontSize,l=t.fontFamily,h=t.fontWeight,c=t.cssClass,d=t.backgroundColor,g=t.borderWidth,u=t.strokeDashArray,f=t.borderRadius,p=t.borderColor,x=t.appendTo,b=void 0===x?\".apexcharts-annotations\":x,v=t.paddingLeft,m=void 0===v?4:v,y=t.paddingRight,w=void 0===y?4:y,k=t.paddingBottom,A=void 0===k?2:k,S=t.paddingTop,C=void 0===S?2:S,L=this.w,P=this.graphics.drawText({x:i,y:a,text:s,textAnchor:r||\"start\",fontSize:n||\"12px\",fontWeight:h||\"regular\",fontFamily:l||L.config.chart.fontFamily,foreColor:o||L.config.chart.foreColor,cssClass:c}),T=L.globals.dom.baseEl.querySelector(b);T&&T.appendChild(P.node);var M=P.bbox();if(s){var I=this.graphics.drawRect(M.x-m,M.y-C,M.width+m+w,M.height+A+C,f,d||\"transparent\",1,g,p,u);T.insertBefore(I.node,P.node)}}},{key:\"addImage\",value:function(t,e){var i=this.w,a=t.path,s=t.x,r=void 0===s?0:s,o=t.y,n=void 0===o?0:o,l=t.width,h=void 0===l?20:l,c=t.height,d=void 0===c?20:c,g=t.appendTo,u=void 0===g?\".apexcharts-annotations\":g,f=i.globals.dom.Paper.image(a);f.size(h,d).move(r,n);var p=i.globals.dom.baseEl.querySelector(u);return p&&p.appendChild(f.node),f}},{key:\"addXaxisAnnotationExternal\",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"xaxis\",contextMethod:i.addXaxisAnnotation}),i}},{key:\"addYaxisAnnotationExternal\",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"yaxis\",contextMethod:i.addYaxisAnnotation}),i}},{key:\"addPointAnnotationExternal\",value:function(t,e,i){return void 0===this.invertAxis&&(this.invertAxis=i.w.globals.isBarHorizontal),this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"point\",contextMethod:i.addPointAnnotation}),i}},{key:\"addAnnotationExternal\",value:function(t){var e=t.params,i=t.pushToMemory,a=t.context,s=t.type,r=t.contextMethod,o=a,n=o.w,l=n.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations\")),h=l.childNodes.length+1,c=new L,d=Object.assign({},\"xaxis\"===s?c.xAxisAnnotation:\"yaxis\"===s?c.yAxisAnnotation:c.pointAnnotation),g=x.extend(d,e);switch(s){case\"xaxis\":this.addXaxisAnnotation(g,l,h);break;case\"yaxis\":this.addYaxisAnnotation(g,l,h);break;case\"point\":this.addPointAnnotation(g,l,h)}var u=n.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations .apexcharts-\").concat(s,\"-annotation-label[rel='\").concat(h,\"']\")),f=this.helpers.addBackgroundToAnno(u,g);return f&&l.insertBefore(f.node,u),i&&n.globals.memory.methodsToExec.push({context:o,id:g.id?g.id:x.randomId(),method:r,label:\"addAnnotation\",params:e}),a}},{key:\"clearAnnotations\",value:function(t){var e=t.w,i=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis-annotations, .apexcharts-xaxis-annotations, .apexcharts-point-annotations\");e.globals.memory.methodsToExec.map((function(t,i){\"addText\"!==t.label&&\"addAnnotation\"!==t.label||e.globals.memory.methodsToExec.splice(i,1)})),i=x.listToArray(i),Array.prototype.forEach.call(i,(function(t){for(;t.firstChild;)t.removeChild(t.firstChild)}))}},{key:\"removeAnnotation\",value:function(t,e){var i=t.w,a=i.globals.dom.baseEl.querySelectorAll(\".\".concat(e));a&&(i.globals.memory.methodsToExec.map((function(t,a){t.id===e&&i.globals.memory.methodsToExec.splice(a,1)})),Array.prototype.forEach.call(a,(function(t){t.parentElement.removeChild(t)})))}}]),t}(),T=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.months31=[1,3,5,7,8,10,12],this.months30=[2,4,6,9,11],this.daysCntOfYear=[0,31,59,90,120,151,181,212,243,273,304,334]}return r(t,[{key:\"isValidDate\",value:function(t){return!isNaN(this.parseDate(t))}},{key:\"getTimeStamp\",value:function(t){return Date.parse(t)?this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toISOString().substr(0,25)).getTime():new Date(t).getTime():t}},{key:\"getDate\",value:function(t){return this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toUTCString()):new Date(t)}},{key:\"parseDate\",value:function(t){var e=Date.parse(t);if(!isNaN(e))return this.getTimeStamp(t);var i=Date.parse(t.replace(/-/g,\"/\").replace(/[a-z]+/gi,\" \"));return i=this.getTimeStamp(i)}},{key:\"parseDateWithTimezone\",value:function(t){return Date.parse(t.replace(/-/g,\"/\").replace(/[a-z]+/gi,\" \"))}},{key:\"formatDate\",value:function(t,e){var i=this.w.globals.locale,a=this.w.config.xaxis.labels.datetimeUTC,s=[\"\\0\"].concat(u(i.months)),r=[\"\\x01\"].concat(u(i.shortMonths)),o=[\"\\x02\"].concat(u(i.days)),n=[\"\\x03\"].concat(u(i.shortDays));function l(t,e){var i=t+\"\";for(e=e||2;i.length<e;)i=\"0\"+i;return i}var h=a?t.getUTCFullYear():t.getFullYear();e=(e=(e=e.replace(/(^|[^\\\\])yyyy+/g,\"$1\"+h)).replace(/(^|[^\\\\])yy/g,\"$1\"+h.toString().substr(2,2))).replace(/(^|[^\\\\])y/g,\"$1\"+h);var c=(a?t.getUTCMonth():t.getMonth())+1;e=(e=(e=(e=e.replace(/(^|[^\\\\])MMMM+/g,\"$1\"+s[0])).replace(/(^|[^\\\\])MMM/g,\"$1\"+r[0])).replace(/(^|[^\\\\])MM/g,\"$1\"+l(c))).replace(/(^|[^\\\\])M/g,\"$1\"+c);var d=a?t.getUTCDate():t.getDate();e=(e=(e=(e=e.replace(/(^|[^\\\\])dddd+/g,\"$1\"+o[0])).replace(/(^|[^\\\\])ddd/g,\"$1\"+n[0])).replace(/(^|[^\\\\])dd/g,\"$1\"+l(d))).replace(/(^|[^\\\\])d/g,\"$1\"+d);var g=a?t.getUTCHours():t.getHours(),f=g>12?g-12:0===g?12:g;e=(e=(e=(e=e.replace(/(^|[^\\\\])HH+/g,\"$1\"+l(g))).replace(/(^|[^\\\\])H/g,\"$1\"+g)).replace(/(^|[^\\\\])hh+/g,\"$1\"+l(f))).replace(/(^|[^\\\\])h/g,\"$1\"+f);var p=a?t.getUTCMinutes():t.getMinutes();e=(e=e.replace(/(^|[^\\\\])mm+/g,\"$1\"+l(p))).replace(/(^|[^\\\\])m/g,\"$1\"+p);var x=a?t.getUTCSeconds():t.getSeconds();e=(e=e.replace(/(^|[^\\\\])ss+/g,\"$1\"+l(x))).replace(/(^|[^\\\\])s/g,\"$1\"+x);var b=a?t.getUTCMilliseconds():t.getMilliseconds();e=e.replace(/(^|[^\\\\])fff+/g,\"$1\"+l(b,3)),b=Math.round(b/10),e=e.replace(/(^|[^\\\\])ff/g,\"$1\"+l(b)),b=Math.round(b/10);var v=g<12?\"AM\":\"PM\";e=(e=(e=e.replace(/(^|[^\\\\])f/g,\"$1\"+b)).replace(/(^|[^\\\\])TT+/g,\"$1\"+v)).replace(/(^|[^\\\\])T/g,\"$1\"+v.charAt(0));var m=v.toLowerCase();e=(e=e.replace(/(^|[^\\\\])tt+/g,\"$1\"+m)).replace(/(^|[^\\\\])t/g,\"$1\"+m.charAt(0));var y=-t.getTimezoneOffset(),w=a||!y?\"Z\":y>0?\"+\":\"-\";if(!a){var k=(y=Math.abs(y))%60;w+=l(Math.floor(y/60))+\":\"+l(k)}e=e.replace(/(^|[^\\\\])K/g,\"$1\"+w);var A=(a?t.getUTCDay():t.getDay())+1;return e=(e=(e=(e=(e=e.replace(new RegExp(o[0],\"g\"),o[A])).replace(new RegExp(n[0],\"g\"),n[A])).replace(new RegExp(s[0],\"g\"),s[c])).replace(new RegExp(r[0],\"g\"),r[c])).replace(/\\\\(.)/g,\"$1\")}},{key:\"getTimeUnitsfromTimestamp\",value:function(t,e,i){var a=this.w;void 0!==a.config.xaxis.min&&(t=a.config.xaxis.min),void 0!==a.config.xaxis.max&&(e=a.config.xaxis.max);var s=this.getDate(t),r=this.getDate(e),o=this.formatDate(s,\"yyyy MM dd HH mm ss fff\").split(\" \"),n=this.formatDate(r,\"yyyy MM dd HH mm ss fff\").split(\" \");return{minMillisecond:parseInt(o[6],10),maxMillisecond:parseInt(n[6],10),minSecond:parseInt(o[5],10),maxSecond:parseInt(n[5],10),minMinute:parseInt(o[4],10),maxMinute:parseInt(n[4],10),minHour:parseInt(o[3],10),maxHour:parseInt(n[3],10),minDate:parseInt(o[2],10),maxDate:parseInt(n[2],10),minMonth:parseInt(o[1],10)-1,maxMonth:parseInt(n[1],10)-1,minYear:parseInt(o[0],10),maxYear:parseInt(n[0],10)}}},{key:\"isLeapYear\",value:function(t){return t%4==0&&t%100!=0||t%400==0}},{key:\"calculcateLastDaysOfMonth\",value:function(t,e,i){return this.determineDaysOfMonths(t,e)-i}},{key:\"determineDaysOfYear\",value:function(t){var e=365;return this.isLeapYear(t)&&(e=366),e}},{key:\"determineRemainingDaysOfYear\",value:function(t,e,i){var a=this.daysCntOfYear[e]+i;return e>1&&this.isLeapYear()&&a++,a}},{key:\"determineDaysOfMonths\",value:function(t,e){var i=30;switch(t=x.monthMod(t),!0){case this.months30.indexOf(t)>-1:2===t&&(i=this.isLeapYear(e)?29:28);break;case this.months31.indexOf(t)>-1:default:i=31}return i}}]),t}(),M=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.tooltipKeyFormat=\"dd MMM\"}return r(t,[{key:\"xLabelFormat\",value:function(t,e,i,a){var s=this.w;if(\"datetime\"===s.config.xaxis.type&&void 0===s.config.xaxis.labels.formatter&&void 0===s.config.tooltip.x.formatter){var r=new T(this.ctx);return r.formatDate(r.getDate(e),s.config.tooltip.x.format)}return t(e,i,a)}},{key:\"defaultGeneralFormatter\",value:function(t){return Array.isArray(t)?t.map((function(t){return t})):t}},{key:\"defaultYFormatter\",value:function(t,e,i){var a=this.w;return x.isNumber(t)&&(t=0!==a.globals.yValueDecimal?t.toFixed(void 0!==e.decimalsInFloat?e.decimalsInFloat:a.globals.yValueDecimal):a.globals.maxYArr[i]-a.globals.minYArr[i]<5?t.toFixed(1):t.toFixed(0)),t}},{key:\"setLabelFormatters\",value:function(){var t=this,e=this.w;return e.globals.xaxisTooltipFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttKeyFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttZFormatter=function(t){return t},e.globals.legendFormatter=function(e){return t.defaultGeneralFormatter(e)},void 0!==e.config.xaxis.labels.formatter?e.globals.xLabelFormatter=e.config.xaxis.labels.formatter:e.globals.xLabelFormatter=function(t){if(x.isNumber(t)){if(!e.config.xaxis.convertedCatToNumeric&&\"numeric\"===e.config.xaxis.type){if(x.isNumber(e.config.xaxis.decimalsInFloat))return t.toFixed(e.config.xaxis.decimalsInFloat);var i=e.globals.maxX-e.globals.minX;return i>0&&i<100?t.toFixed(1):t.toFixed(0)}if(e.globals.isBarHorizontal)if(e.globals.maxY-e.globals.minYArr<4)return t.toFixed(1);return t.toFixed(0)}return t},\"function\"==typeof e.config.tooltip.x.formatter?e.globals.ttKeyFormatter=e.config.tooltip.x.formatter:e.globals.ttKeyFormatter=e.globals.xLabelFormatter,\"function\"==typeof e.config.xaxis.tooltip.formatter&&(e.globals.xaxisTooltipFormatter=e.config.xaxis.tooltip.formatter),(Array.isArray(e.config.tooltip.y)||void 0!==e.config.tooltip.y.formatter)&&(e.globals.ttVal=e.config.tooltip.y),void 0!==e.config.tooltip.z.formatter&&(e.globals.ttZFormatter=e.config.tooltip.z.formatter),void 0!==e.config.legend.formatter&&(e.globals.legendFormatter=e.config.legend.formatter),e.config.yaxis.forEach((function(i,a){void 0!==i.labels.formatter?e.globals.yLabelFormatters[a]=i.labels.formatter:e.globals.yLabelFormatters[a]=function(s){return e.globals.xyCharts?Array.isArray(s)?s.map((function(e){return t.defaultYFormatter(e,i,a)})):t.defaultYFormatter(s,i,a):s}})),e.globals}},{key:\"heatmapLabelFormatters\",value:function(){var t=this.w;if(\"heatmap\"===t.config.chart.type){t.globals.yAxisScale[0].result=t.globals.seriesNames.slice();var e=t.globals.seriesNames.reduce((function(t,e){return t.length>e.length?t:e}),0);t.globals.yAxisScale[0].niceMax=e,t.globals.yAxisScale[0].niceMin=e}}}]),t}(),I=function(t){var e,i=t.isTimeline,a=t.ctx,s=t.seriesIndex,r=t.dataPointIndex,o=t.y1,n=t.y2,l=t.w,h=l.globals.seriesRangeStart[s][r],c=l.globals.seriesRangeEnd[s][r],d=l.globals.labels[r],g=l.config.series[s].name?l.config.series[s].name:\"\",u=l.globals.ttKeyFormatter,f=l.config.tooltip.y.title.formatter,p={w:l,seriesIndex:s,dataPointIndex:r,start:h,end:c};(\"function\"==typeof f&&(g=f(g,p)),null!==(e=l.config.series[s].data[r])&&void 0!==e&&e.x&&(d=l.config.series[s].data[r].x),i)||\"datetime\"===l.config.xaxis.type&&(d=new M(a).xLabelFormat(l.globals.ttKeyFormatter,d,d,{i:void 0,dateFormatter:new T(a).formatDate,w:l}));\"function\"==typeof u&&(d=u(d,p)),Number.isFinite(o)&&Number.isFinite(n)&&(h=o,c=n);var x=\"\",b=\"\",v=l.globals.colors[s];if(void 0===l.config.tooltip.x.formatter)if(\"datetime\"===l.config.xaxis.type){var m=new T(a);x=m.formatDate(m.getDate(h),l.config.tooltip.x.format),b=m.formatDate(m.getDate(c),l.config.tooltip.x.format)}else x=h,b=c;else x=l.config.tooltip.x.formatter(h),b=l.config.tooltip.x.formatter(c);return{start:h,end:c,startVal:x,endVal:b,ylabel:d,color:v,seriesName:g}},z=function(t){var e=t.color,i=t.seriesName,a=t.ylabel,s=t.start,r=t.end,o=t.seriesIndex,n=t.dataPointIndex,l=t.ctx.tooltip.tooltipLabels.getFormatters(o);s=l.yLbFormatter(s),r=l.yLbFormatter(r);var h=l.yLbFormatter(t.w.globals.series[o][n]),c='<span class=\"value start-value\">\\n  '.concat(s,'\\n  </span> <span class=\"separator\">-</span> <span class=\"value end-value\">\\n  ').concat(r,\"\\n  </span>\");return'<div class=\"apexcharts-tooltip-rangebar\"><div> <span class=\"series-name\" style=\"color: '+e+'\">'+(i||\"\")+'</span></div><div> <span class=\"category\">'+a+\": </span> \"+(t.w.globals.comboCharts?\"rangeArea\"===t.w.config.series[o].type||\"rangeBar\"===t.w.config.series[o].type?c:\"<span>\".concat(h,\"</span>\"):c)+\" </div></div>\"},X=function(){function t(e){a(this,t),this.opts=e}return r(t,[{key:\"line\",value:function(){return{chart:{animations:{easing:\"swing\"}},dataLabels:{enabled:!1},stroke:{width:5,curve:\"straight\"},markers:{size:0,hover:{sizeOffset:6}},xaxis:{crosshairs:{width:1}}}}},{key:\"sparkline\",value:function(t){this.opts.yaxis[0].show=!1,this.opts.yaxis[0].title.text=\"\",this.opts.yaxis[0].axisBorder.show=!1,this.opts.yaxis[0].axisTicks.show=!1,this.opts.yaxis[0].floating=!0;return x.extend(t,{grid:{show:!1,padding:{left:0,right:0,top:0,bottom:0}},legend:{show:!1},xaxis:{labels:{show:!1},tooltip:{enabled:!1},axisBorder:{show:!1},axisTicks:{show:!1}},chart:{toolbar:{show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1}})}},{key:\"bar\",value:function(){return{chart:{stacked:!1,animations:{easing:\"swing\"}},plotOptions:{bar:{dataLabels:{position:\"center\"}}},dataLabels:{style:{colors:[\"#fff\"]},background:{enabled:!1}},stroke:{width:0,lineCap:\"round\"},fill:{opacity:.85},legend:{markers:{shape:\"square\",radius:2,size:8}},tooltip:{shared:!1,intersect:!0},xaxis:{tooltip:{enabled:!1},tickPlacement:\"between\",crosshairs:{width:\"barWidth\",position:\"back\",fill:{type:\"gradient\"},dropShadow:{enabled:!1},stroke:{width:0}}}}}},{key:\"candlestick\",value:function(){var t=this;return{stroke:{width:1,colors:[\"#333\"]},fill:{opacity:1},dataLabels:{enabled:!1},tooltip:{shared:!0,custom:function(e){var i=e.seriesIndex,a=e.dataPointIndex,s=e.w;return t._getBoxTooltip(s,i,a,[\"Open\",\"High\",\"\",\"Low\",\"Close\"],\"candlestick\")}},states:{active:{filter:{type:\"none\"}}},xaxis:{crosshairs:{width:1}}}}},{key:\"boxPlot\",value:function(){var t=this;return{chart:{animations:{dynamicAnimation:{enabled:!1}}},stroke:{width:1,colors:[\"#24292e\"]},dataLabels:{enabled:!1},tooltip:{shared:!0,custom:function(e){var i=e.seriesIndex,a=e.dataPointIndex,s=e.w;return t._getBoxTooltip(s,i,a,[\"Minimum\",\"Q1\",\"Median\",\"Q3\",\"Maximum\"],\"boxPlot\")}},markers:{size:5,strokeWidth:1,strokeColors:\"#111\"},xaxis:{crosshairs:{width:1}}}}},{key:\"rangeBar\",value:function(){return{stroke:{width:0,lineCap:\"square\"},plotOptions:{bar:{borderRadius:0,dataLabels:{position:\"center\"}}},dataLabels:{enabled:!1,formatter:function(t,e){e.ctx;var i=e.seriesIndex,a=e.dataPointIndex,s=e.w,r=function(){var t=s.globals.seriesRangeStart[i][a];return s.globals.seriesRangeEnd[i][a]-t};return s.globals.comboCharts?\"rangeBar\"===s.config.series[i].type||\"rangeArea\"===s.config.series[i].type?r():t:r()},background:{enabled:!1},style:{colors:[\"#fff\"]}},tooltip:{shared:!1,followCursor:!0,custom:function(t){return t.w.config.plotOptions&&t.w.config.plotOptions.bar&&t.w.config.plotOptions.bar.horizontal?function(t){var i=I(e(e({},t),{},{isTimeline:!0})),a=i.color,s=i.seriesName,r=i.ylabel,o=i.startVal,n=i.endVal;return z(e(e({},t),{},{color:a,seriesName:s,ylabel:r,start:o,end:n}))}(t):function(t){var i=I(t),a=i.color,s=i.seriesName,r=i.ylabel,o=i.start,n=i.end;return z(e(e({},t),{},{color:a,seriesName:s,ylabel:r,start:o,end:n}))}(t)}},xaxis:{tickPlacement:\"between\",tooltip:{enabled:!1},crosshairs:{stroke:{width:0}}}}}},{key:\"area\",value:function(){return{stroke:{width:4,fill:{type:\"solid\",gradient:{inverseColors:!1,shade:\"light\",type:\"vertical\",opacityFrom:.65,opacityTo:.5,stops:[0,100,100]}}},fill:{type:\"gradient\",gradient:{inverseColors:!1,shade:\"light\",type:\"vertical\",opacityFrom:.65,opacityTo:.5,stops:[0,100,100]}},markers:{size:0,hover:{sizeOffset:6}},tooltip:{followCursor:!1}}}},{key:\"rangeArea\",value:function(){return{stroke:{curve:\"straight\",width:0},fill:{type:\"solid\",opacity:.6},markers:{size:0},states:{hover:{filter:{type:\"none\"}},active:{filter:{type:\"none\"}}},tooltip:{intersect:!1,shared:!0,followCursor:!0,custom:function(t){return function(t){var i=I(t),a=i.color,s=i.seriesName,r=i.ylabel,o=i.start,n=i.end;return z(e(e({},t),{},{color:a,seriesName:s,ylabel:r,start:o,end:n}))}(t)}}}}},{key:\"brush\",value:function(t){return x.extend(t,{chart:{toolbar:{autoSelected:\"selection\",show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1},stroke:{width:1},tooltip:{enabled:!1},xaxis:{tooltip:{enabled:!1}}})}},{key:\"stacked100\",value:function(t){t.dataLabels=t.dataLabels||{},t.dataLabels.formatter=t.dataLabels.formatter||void 0;var e=t.dataLabels.formatter;return t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})),\"bar\"===t.chart.type&&(t.dataLabels.formatter=e||function(t){return\"number\"==typeof t&&t?t.toFixed(0)+\"%\":t}),t}},{key:\"stackedBars\",value:function(){var t=this.bar();return e(e({},t),{},{plotOptions:e(e({},t.plotOptions),{},{bar:e(e({},t.plotOptions.bar),{},{borderRadiusApplication:\"end\",borderRadiusWhenStacked:\"last\"})})})}},{key:\"convertCatToNumeric\",value:function(t){return t.xaxis.convertedCatToNumeric=!0,t}},{key:\"convertCatToNumericXaxis\",value:function(t,e,i){t.xaxis.type=\"numeric\",t.xaxis.labels=t.xaxis.labels||{},t.xaxis.labels.formatter=t.xaxis.labels.formatter||function(t){return x.isNumber(t)?Math.floor(t):t};var a=t.xaxis.labels.formatter,s=t.xaxis.categories&&t.xaxis.categories.length?t.xaxis.categories:t.labels;return i&&i.length&&(s=i.map((function(t){return Array.isArray(t)?t:String(t)}))),s&&s.length&&(t.xaxis.labels.formatter=function(t){return x.isNumber(t)?a(s[Math.floor(t)-1]):a(t)}),t.xaxis.categories=[],t.labels=[],t.xaxis.tickAmount=t.xaxis.tickAmount||\"dataPoints\",t}},{key:\"bubble\",value:function(){return{dataLabels:{style:{colors:[\"#fff\"]}},tooltip:{shared:!1,intersect:!0},xaxis:{crosshairs:{width:0}},fill:{type:\"solid\",gradient:{shade:\"light\",inverse:!0,shadeIntensity:.55,opacityFrom:.4,opacityTo:.8}}}}},{key:\"scatter\",value:function(){return{dataLabels:{enabled:!1},tooltip:{shared:!1,intersect:!0},markers:{size:6,strokeWidth:1,hover:{sizeOffset:2}}}}},{key:\"heatmap\",value:function(){return{chart:{stacked:!1},fill:{opacity:1},dataLabels:{style:{colors:[\"#fff\"]}},stroke:{colors:[\"#fff\"]},tooltip:{followCursor:!0,marker:{show:!1},x:{show:!1}},legend:{position:\"top\",markers:{shape:\"square\",size:10,offsetY:2}},grid:{padding:{right:20}}}}},{key:\"treemap\",value:function(){return{chart:{zoom:{enabled:!1}},dataLabels:{style:{fontSize:14,fontWeight:600,colors:[\"#fff\"]}},stroke:{show:!0,width:2,colors:[\"#fff\"]},legend:{show:!1},fill:{gradient:{stops:[0,100]}},tooltip:{followCursor:!0,x:{show:!1}},grid:{padding:{left:0,right:0}},xaxis:{crosshairs:{show:!1},tooltip:{enabled:!1}}}}},{key:\"pie\",value:function(){return{chart:{toolbar:{show:!1}},plotOptions:{pie:{donut:{labels:{show:!1}}}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},style:{colors:[\"#fff\"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:[\"#fff\"]},fill:{opacity:1,gradient:{shade:\"light\",stops:[0,100]}},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"donut\",value:function(){return{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},style:{colors:[\"#fff\"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:[\"#fff\"]},fill:{opacity:1,gradient:{shade:\"light\",shadeIntensity:.35,stops:[80,100],opacityFrom:1,opacityTo:1}},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"polarArea\",value:function(){return this.opts.yaxis[0].tickAmount=this.opts.yaxis[0].tickAmount?this.opts.yaxis[0].tickAmount:6,{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},enabled:!1},stroke:{show:!0,width:2},fill:{opacity:.7},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"radar\",value:function(){return this.opts.yaxis[0].labels.offsetY=this.opts.yaxis[0].labels.offsetY?this.opts.yaxis[0].labels.offsetY:6,{dataLabels:{enabled:!1,style:{fontSize:\"11px\"}},stroke:{width:2},markers:{size:3,strokeWidth:1,strokeOpacity:1},fill:{opacity:.2},tooltip:{shared:!1,intersect:!0,followCursor:!0},grid:{show:!1},xaxis:{labels:{formatter:function(t){return t},style:{colors:[\"#a8a8a8\"],fontSize:\"11px\"}},tooltip:{enabled:!1},crosshairs:{show:!1}}}}},{key:\"radialBar\",value:function(){return{chart:{animations:{dynamicAnimation:{enabled:!0,speed:800}},toolbar:{show:!1}},fill:{gradient:{shade:\"dark\",shadeIntensity:.4,inverseColors:!1,type:\"diagonal2\",opacityFrom:1,opacityTo:1,stops:[70,98,100]}},legend:{show:!1,position:\"right\"},tooltip:{enabled:!1,fillSeriesColor:!0}}}},{key:\"_getBoxTooltip\",value:function(t,e,i,a,s){var r=t.globals.seriesCandleO[e][i],o=t.globals.seriesCandleH[e][i],n=t.globals.seriesCandleM[e][i],l=t.globals.seriesCandleL[e][i],h=t.globals.seriesCandleC[e][i];return t.config.series[e].type&&t.config.series[e].type!==s?'<div class=\"apexcharts-custom-tooltip\">\\n          '.concat(t.config.series[e].name?t.config.series[e].name:\"series-\"+(e+1),\": <strong>\").concat(t.globals.series[e][i],\"</strong>\\n        </div>\"):'<div class=\"apexcharts-tooltip-box apexcharts-tooltip-'.concat(t.config.chart.type,'\">')+\"<div>\".concat(a[0],': <span class=\"value\">')+r+\"</span></div>\"+\"<div>\".concat(a[1],': <span class=\"value\">')+o+\"</span></div>\"+(n?\"<div>\".concat(a[2],': <span class=\"value\">')+n+\"</span></div>\":\"\")+\"<div>\".concat(a[3],': <span class=\"value\">')+l+\"</span></div>\"+\"<div>\".concat(a[4],': <span class=\"value\">')+h+\"</span></div></div>\"}}]),t}(),E=function(){function t(e){a(this,t),this.opts=e}return r(t,[{key:\"init\",value:function(t){var e=t.responsiveOverride,a=this.opts,s=new L,r=new X(a);this.chartType=a.chart.type,a=this.extendYAxis(a),a=this.extendAnnotations(a);var o=s.init(),n={};if(a&&\"object\"===i(a)){var l={};l=-1!==[\"line\",\"area\",\"bar\",\"candlestick\",\"boxPlot\",\"rangeBar\",\"rangeArea\",\"bubble\",\"scatter\",\"heatmap\",\"treemap\",\"pie\",\"polarArea\",\"donut\",\"radar\",\"radialBar\"].indexOf(a.chart.type)?r[a.chart.type]():r.line(),a.chart.stacked&&\"bar\"===a.chart.type&&(l=r.stackedBars()),a.chart.brush&&a.chart.brush.enabled&&(l=r.brush(l)),a.chart.stacked&&\"100%\"===a.chart.stackType&&(a=r.stacked100(a)),this.checkForDarkTheme(window.Apex),this.checkForDarkTheme(a),a.xaxis=a.xaxis||window.Apex.xaxis||{},e||(a.xaxis.convertedCatToNumeric=!1),((a=this.checkForCatToNumericXAxis(this.chartType,l,a)).chart.sparkline&&a.chart.sparkline.enabled||window.Apex.chart&&window.Apex.chart.sparkline&&window.Apex.chart.sparkline.enabled)&&(l=r.sparkline(l)),n=x.extend(o,l)}var h=x.extend(n,window.Apex);return o=x.extend(h,a),o=this.handleUserInputErrors(o)}},{key:\"checkForCatToNumericXAxis\",value:function(t,e,i){var a=new X(i),s=(\"bar\"===t||\"boxPlot\"===t)&&i.plotOptions&&i.plotOptions.bar&&i.plotOptions.bar.horizontal,r=\"pie\"===t||\"polarArea\"===t||\"donut\"===t||\"radar\"===t||\"radialBar\"===t||\"heatmap\"===t,o=\"datetime\"!==i.xaxis.type&&\"numeric\"!==i.xaxis.type,n=i.xaxis.tickPlacement?i.xaxis.tickPlacement:e.xaxis&&e.xaxis.tickPlacement;return s||r||!o||\"between\"===n||(i=a.convertCatToNumeric(i)),i}},{key:\"extendYAxis\",value:function(t,e){var i=new L;(void 0===t.yaxis||!t.yaxis||Array.isArray(t.yaxis)&&0===t.yaxis.length)&&(t.yaxis={}),t.yaxis.constructor!==Array&&window.Apex.yaxis&&window.Apex.yaxis.constructor!==Array&&(t.yaxis=x.extend(t.yaxis,window.Apex.yaxis)),t.yaxis.constructor!==Array?t.yaxis=[x.extend(i.yAxis,t.yaxis)]:t.yaxis=x.extendArray(t.yaxis,i.yAxis);var a=!1;t.yaxis.forEach((function(t){t.logarithmic&&(a=!0)}));var s=t.series;return e&&!s&&(s=e.config.series),a&&s.length!==t.yaxis.length&&s.length&&(t.yaxis=s.map((function(e,a){if(e.name||(s[a].name=\"series-\".concat(a+1)),t.yaxis[a])return t.yaxis[a].seriesName=s[a].name,t.yaxis[a];var r=x.extend(i.yAxis,t.yaxis[0]);return r.show=!1,r}))),a&&s.length>1&&s.length!==t.yaxis.length&&console.warn(\"A multi-series logarithmic chart should have equal number of series and y-axes. Please make sure to equalize both.\"),t}},{key:\"extendAnnotations\",value:function(t){return void 0===t.annotations&&(t.annotations={},t.annotations.yaxis=[],t.annotations.xaxis=[],t.annotations.points=[]),t=this.extendYAxisAnnotations(t),t=this.extendXAxisAnnotations(t),t=this.extendPointAnnotations(t)}},{key:\"extendYAxisAnnotations\",value:function(t){var e=new L;return t.annotations.yaxis=x.extendArray(void 0!==t.annotations.yaxis?t.annotations.yaxis:[],e.yAxisAnnotation),t}},{key:\"extendXAxisAnnotations\",value:function(t){var e=new L;return t.annotations.xaxis=x.extendArray(void 0!==t.annotations.xaxis?t.annotations.xaxis:[],e.xAxisAnnotation),t}},{key:\"extendPointAnnotations\",value:function(t){var e=new L;return t.annotations.points=x.extendArray(void 0!==t.annotations.points?t.annotations.points:[],e.pointAnnotation),t}},{key:\"checkForDarkTheme\",value:function(t){t.theme&&\"dark\"===t.theme.mode&&(t.tooltip||(t.tooltip={}),\"light\"!==t.tooltip.theme&&(t.tooltip.theme=\"dark\"),t.chart.foreColor||(t.chart.foreColor=\"#f6f7f8\"),t.chart.background||(t.chart.background=\"#424242\"),t.theme.palette||(t.theme.palette=\"palette4\"))}},{key:\"handleUserInputErrors\",value:function(t){var e=t;if(e.tooltip.shared&&e.tooltip.intersect)throw new Error(\"tooltip.shared cannot be enabled when tooltip.intersect is true. Turn off any other option by setting it to false.\");if(\"bar\"===e.chart.type&&e.plotOptions.bar.horizontal){if(e.yaxis.length>1)throw new Error(\"Multiple Y Axis for bars are not supported. Switch to column chart by setting plotOptions.bar.horizontal=false\");e.yaxis[0].reversed&&(e.yaxis[0].opposite=!0),e.xaxis.tooltip.enabled=!1,e.yaxis[0].tooltip.enabled=!1,e.chart.zoom.enabled=!1}return\"bar\"!==e.chart.type&&\"rangeBar\"!==e.chart.type||e.tooltip.shared&&\"barWidth\"===e.xaxis.crosshairs.width&&e.series.length>1&&(e.xaxis.crosshairs.width=\"tickWidth\"),\"candlestick\"!==e.chart.type&&\"boxPlot\"!==e.chart.type||e.yaxis[0].reversed&&(console.warn(\"Reversed y-axis in \".concat(e.chart.type,\" chart is not supported.\")),e.yaxis[0].reversed=!1),e}}]),t}(),Y=function(){function t(){a(this,t)}return r(t,[{key:\"initGlobalVars\",value:function(t){t.series=[],t.seriesCandleO=[],t.seriesCandleH=[],t.seriesCandleM=[],t.seriesCandleL=[],t.seriesCandleC=[],t.seriesRangeStart=[],t.seriesRangeEnd=[],t.seriesRange=[],t.seriesPercent=[],t.seriesGoals=[],t.seriesX=[],t.seriesZ=[],t.seriesNames=[],t.seriesTotals=[],t.seriesLog=[],t.seriesColors=[],t.stackedSeriesTotals=[],t.seriesXvalues=[],t.seriesYvalues=[],t.labels=[],t.hasGroups=!1,t.groups=[],t.categoryLabels=[],t.timescaleLabels=[],t.noLabelsProvided=!1,t.resizeTimer=null,t.selectionResizeTimer=null,t.delayedElements=[],t.pointsArray=[],t.dataLabelsRects=[],t.isXNumeric=!1,t.skipLastTimelinelabel=!1,t.skipFirstTimelinelabel=!1,t.isDataXYZ=!1,t.isMultiLineX=!1,t.isMultipleYAxis=!1,t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE,t.minYArr=[],t.maxYArr=[],t.maxX=-Number.MAX_VALUE,t.minX=Number.MAX_VALUE,t.initialMaxX=-Number.MAX_VALUE,t.initialMinX=Number.MAX_VALUE,t.maxDate=0,t.minDate=Number.MAX_VALUE,t.minZ=Number.MAX_VALUE,t.maxZ=-Number.MAX_VALUE,t.minXDiff=Number.MAX_VALUE,t.yAxisScale=[],t.xAxisScale=null,t.xAxisTicksPositions=[],t.yLabelsCoords=[],t.yTitleCoords=[],t.barPadForNumericAxis=0,t.padHorizontal=0,t.xRange=0,t.yRange=[],t.zRange=0,t.dataPoints=0,t.xTickAmount=0}},{key:\"globalVars\",value:function(t){return{chartID:null,cuid:null,events:{beforeMount:[],mounted:[],updated:[],clicked:[],selection:[],dataPointSelection:[],zoomed:[],scrolled:[]},colors:[],clientX:null,clientY:null,fill:{colors:[]},stroke:{colors:[]},dataLabels:{style:{colors:[]}},radarPolygons:{fill:{colors:[]}},markers:{colors:[],size:t.markers.size,largestSize:0},animationEnded:!1,isTouchDevice:\"ontouchstart\"in window||navigator.msMaxTouchPoints,isDirty:!1,isExecCalled:!1,initialConfig:null,initialSeries:[],lastXAxis:[],lastYAxis:[],columnSeries:null,labels:[],timescaleLabels:[],noLabelsProvided:!1,allSeriesCollapsed:!1,collapsedSeries:[],collapsedSeriesIndices:[],ancillaryCollapsedSeries:[],ancillaryCollapsedSeriesIndices:[],risingSeries:[],dataFormatXNumeric:!1,capturedSeriesIndex:-1,capturedDataPointIndex:-1,selectedDataPoints:[],goldenPadding:35,invalidLogScale:!1,ignoreYAxisIndexes:[],yAxisSameScaleIndices:[],maxValsInArrayIndex:0,radialSize:0,selection:void 0,zoomEnabled:\"zoom\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.zoom&&t.chart.zoom.enabled,panEnabled:\"pan\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.pan,selectionEnabled:\"selection\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.selection,yaxis:null,mousedown:!1,lastClientPosition:{},visibleXRange:void 0,yValueDecimal:0,total:0,SVGNS:\"http://www.w3.org/2000/svg\",svgWidth:0,svgHeight:0,noData:!1,locale:{},dom:{},memory:{methodsToExec:[]},shouldAnimate:!0,skipLastTimelinelabel:!1,skipFirstTimelinelabel:!1,delayedElements:[],axisCharts:!0,isDataXYZ:!1,resized:!1,resizeTimer:null,comboCharts:!1,dataChanged:!1,previousPaths:[],allSeriesHasEqualX:!0,pointsArray:[],dataLabelsRects:[],lastDrawnDataLabelsIndexes:[],hasNullValues:!1,easing:null,zoomed:!1,gridWidth:0,gridHeight:0,rotateXLabels:!1,defaultLabels:!1,xLabelFormatter:void 0,yLabelFormatters:[],xaxisTooltipFormatter:void 0,ttKeyFormatter:void 0,ttVal:void 0,ttZFormatter:void 0,LINE_HEIGHT_RATIO:1.618,xAxisLabelsHeight:0,xAxisGroupLabelsHeight:0,xAxisLabelsWidth:0,yAxisLabelsWidth:0,scaleX:1,scaleY:1,translateX:0,translateY:0,translateYAxisX:[],yAxisWidths:[],translateXAxisY:0,translateXAxisX:0,tooltip:null}}},{key:\"init\",value:function(t){var e=this.globalVars(t);return this.initGlobalVars(e),e.initialConfig=x.extend({},t),e.initialSeries=x.clone(t.series),e.lastXAxis=x.clone(e.initialConfig.xaxis),e.lastYAxis=x.clone(e.initialConfig.yaxis),e}}]),t}(),F=function(){function t(e){a(this,t),this.opts=e}return r(t,[{key:\"init\",value:function(){var t=new E(this.opts).init({responsiveOverride:!1});return{config:t,globals:(new Y).init(t)}}}]),t}(),R=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.opts=null,this.seriesIndex=0}return r(t,[{key:\"clippedImgArea\",value:function(t){var e=this.w,i=e.config,a=parseInt(e.globals.gridWidth,10),s=parseInt(e.globals.gridHeight,10),r=a>s?a:s,o=t.image,n=0,l=0;void 0===t.width&&void 0===t.height?void 0!==i.fill.image.width&&void 0!==i.fill.image.height?(n=i.fill.image.width+1,l=i.fill.image.height):(n=r+1,l=r):(n=t.width,l=t.height);var h=document.createElementNS(e.globals.SVGNS,\"pattern\");m.setAttrs(h,{id:t.patternID,patternUnits:t.patternUnits?t.patternUnits:\"userSpaceOnUse\",width:n+\"px\",height:l+\"px\"});var c=document.createElementNS(e.globals.SVGNS,\"image\");h.appendChild(c),c.setAttributeNS(window.SVG.xlink,\"href\",o),m.setAttrs(c,{x:0,y:0,preserveAspectRatio:\"none\",width:n+\"px\",height:l+\"px\"}),c.style.opacity=t.opacity,e.globals.dom.elDefs.node.appendChild(h)}},{key:\"getSeriesIndex\",value:function(t){var e=this.w;return(\"bar\"===e.config.chart.type||\"rangeBar\"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||\"heatmap\"===e.config.chart.type||\"treemap\"===e.config.chart.type?this.seriesIndex=t.seriesNumber:this.seriesIndex=t.seriesNumber%e.globals.series.length,this.seriesIndex}},{key:\"fillPath\",value:function(t){var e=this.w;this.opts=t;var i,a,s,r=this.w.config;this.seriesIndex=this.getSeriesIndex(t);var o=this.getFillColors()[this.seriesIndex];void 0!==e.globals.seriesColors[this.seriesIndex]&&(o=e.globals.seriesColors[this.seriesIndex]),\"function\"==typeof o&&(o=o({seriesIndex:this.seriesIndex,dataPointIndex:t.dataPointIndex,value:t.value,w:e}));var n=t.fillType?t.fillType:this.getFillType(this.seriesIndex),l=Array.isArray(r.fill.opacity)?r.fill.opacity[this.seriesIndex]:r.fill.opacity;t.color&&(o=t.color);var h=o;if(-1===o.indexOf(\"rgb\")?o.length<9&&(h=x.hexToRgba(o,l)):o.indexOf(\"rgba\")>-1&&(l=x.getOpacityFromRGBA(o)),t.opacity&&(l=t.opacity),\"pattern\"===n&&(a=this.handlePatternFill({fillConfig:t.fillConfig,patternFill:a,fillColor:o,fillOpacity:l,defaultColor:h})),\"gradient\"===n&&(s=this.handleGradientFill({fillConfig:t.fillConfig,fillColor:o,fillOpacity:l,i:this.seriesIndex})),\"image\"===n){var c=r.fill.image.src,d=t.patternID?t.patternID:\"\";this.clippedImgArea({opacity:l,image:Array.isArray(c)?t.seriesNumber<c.length?c[t.seriesNumber]:c[0]:c,width:t.width?t.width:void 0,height:t.height?t.height:void 0,patternUnits:t.patternUnits,patternID:\"pattern\".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(d)}),i=\"url(#pattern\".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(d,\")\")}else i=\"gradient\"===n?s:\"pattern\"===n?a:h;return t.solid&&(i=h),i}},{key:\"getFillType\",value:function(t){var e=this.w;return Array.isArray(e.config.fill.type)?e.config.fill.type[t]:e.config.fill.type}},{key:\"getFillColors\",value:function(){var t=this.w,e=t.config,i=this.opts,a=[];return t.globals.comboCharts?\"line\"===t.config.series[this.seriesIndex].type?Array.isArray(t.globals.stroke.colors)?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):Array.isArray(t.globals.fill.colors)?a=t.globals.fill.colors:a.push(t.globals.fill.colors):\"line\"===e.chart.type?Array.isArray(t.globals.stroke.colors)?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):Array.isArray(t.globals.fill.colors)?a=t.globals.fill.colors:a.push(t.globals.fill.colors),void 0!==i.fillColors&&(a=[],Array.isArray(i.fillColors)?a=i.fillColors.slice():a.push(i.fillColors)),a}},{key:\"handlePatternFill\",value:function(t){var e=t.fillConfig,i=t.patternFill,a=t.fillColor,s=t.fillOpacity,r=t.defaultColor,o=this.w.config.fill;e&&(o=e);var n=this.opts,l=new m(this.ctx),h=Array.isArray(o.pattern.strokeWidth)?o.pattern.strokeWidth[this.seriesIndex]:o.pattern.strokeWidth,c=a;Array.isArray(o.pattern.style)?i=void 0!==o.pattern.style[n.seriesNumber]?l.drawPattern(o.pattern.style[n.seriesNumber],o.pattern.width,o.pattern.height,c,h,s):r:i=l.drawPattern(o.pattern.style,o.pattern.width,o.pattern.height,c,h,s);return i}},{key:\"handleGradientFill\",value:function(t){var i=t.fillColor,a=t.fillOpacity,s=t.fillConfig,r=t.i,o=this.w.config.fill;s&&(o=e(e({},o),s));var n,l=this.opts,h=new m(this.ctx),c=new x,d=o.gradient.type,g=i,u=void 0===o.gradient.opacityFrom?a:Array.isArray(o.gradient.opacityFrom)?o.gradient.opacityFrom[r]:o.gradient.opacityFrom;g.indexOf(\"rgba\")>-1&&(u=x.getOpacityFromRGBA(g));var f=void 0===o.gradient.opacityTo?a:Array.isArray(o.gradient.opacityTo)?o.gradient.opacityTo[r]:o.gradient.opacityTo;if(void 0===o.gradient.gradientToColors||0===o.gradient.gradientToColors.length)n=\"dark\"===o.gradient.shade?c.shadeColor(-1*parseFloat(o.gradient.shadeIntensity),i.indexOf(\"rgb\")>-1?x.rgb2hex(i):i):c.shadeColor(parseFloat(o.gradient.shadeIntensity),i.indexOf(\"rgb\")>-1?x.rgb2hex(i):i);else if(o.gradient.gradientToColors[l.seriesNumber]){var p=o.gradient.gradientToColors[l.seriesNumber];n=p,p.indexOf(\"rgba\")>-1&&(f=x.getOpacityFromRGBA(p))}else n=i;if(o.gradient.gradientFrom&&(g=o.gradient.gradientFrom),o.gradient.gradientTo&&(n=o.gradient.gradientTo),o.gradient.inverseColors){var b=g;g=n,n=b}return g.indexOf(\"rgb\")>-1&&(g=x.rgb2hex(g)),n.indexOf(\"rgb\")>-1&&(n=x.rgb2hex(n)),h.drawGradient(d,g,n,u,f,l.size,o.gradient.stops,o.gradient.colorStops,r)}}]),t}(),D=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"setGlobalMarkerSize\",value:function(){var t=this.w;if(t.globals.markers.size=Array.isArray(t.config.markers.size)?t.config.markers.size:[t.config.markers.size],t.globals.markers.size.length>0){if(t.globals.markers.size.length<t.globals.series.length+1)for(var e=0;e<=t.globals.series.length;e++)void 0===t.globals.markers.size[e]&&t.globals.markers.size.push(t.globals.markers.size[0])}else t.globals.markers.size=t.config.series.map((function(e){return t.config.markers.size}))}},{key:\"plotChartMarkers\",value:function(t,e,i,a){var s,r=arguments.length>4&&void 0!==arguments[4]&&arguments[4],o=this.w,n=e,l=t,h=null,c=new m(this.ctx),d=o.config.markers.discrete&&o.config.markers.discrete.length;if((o.globals.markers.size[e]>0||r||d)&&(h=c.group({class:r||d?\"\":\"apexcharts-series-markers\"})).attr(\"clip-path\",\"url(#gridRectMarkerMask\".concat(o.globals.cuid,\")\")),Array.isArray(l.x))for(var g=0;g<l.x.length;g++){var u=i;1===i&&0===g&&(u=0),1===i&&1===g&&(u=1);var f=\"apexcharts-marker\";\"line\"!==o.config.chart.type&&\"area\"!==o.config.chart.type||o.globals.comboCharts||o.config.tooltip.intersect||(f+=\" no-pointer-events\");var p=Array.isArray(o.config.markers.size)?o.globals.markers.size[e]>0:o.config.markers.size>0;if(p||r||d){x.isNumber(l.y[g])?f+=\" w\".concat(x.randomId()):f=\"apexcharts-nullpoint\";var b=this.getMarkerConfig({cssClass:f,seriesIndex:e,dataPointIndex:u});o.config.series[n].data[u]&&(o.config.series[n].data[u].fillColor&&(b.pointFillColor=o.config.series[n].data[u].fillColor),o.config.series[n].data[u].strokeColor&&(b.pointStrokeColor=o.config.series[n].data[u].strokeColor)),a&&(b.pSize=a),(s=c.drawMarker(l.x[g],l.y[g],b)).attr(\"rel\",u),s.attr(\"j\",u),s.attr(\"index\",e),s.node.setAttribute(\"default-marker-size\",b.pSize);var y=new v(this.ctx);y.setSelectionFilter(s,e,u),this.addEvents(s),h&&h.add(s)}else void 0===o.globals.pointsArray[e]&&(o.globals.pointsArray[e]=[]),o.globals.pointsArray[e].push([l.x[g],l.y[g]])}return h}},{key:\"getMarkerConfig\",value:function(t){var e=t.cssClass,i=t.seriesIndex,a=t.dataPointIndex,s=void 0===a?null:a,r=t.finishRadius,o=void 0===r?null:r,n=this.w,l=this.getMarkerStyle(i),h=n.globals.markers.size[i],c=n.config.markers;return null!==s&&c.discrete.length&&c.discrete.map((function(t){t.seriesIndex===i&&t.dataPointIndex===s&&(l.pointStrokeColor=t.strokeColor,l.pointFillColor=t.fillColor,h=t.size,l.pointShape=t.shape)})),{pSize:null===o?h:o,pRadius:c.radius,width:Array.isArray(c.width)?c.width[i]:c.width,height:Array.isArray(c.height)?c.height[i]:c.height,pointStrokeWidth:Array.isArray(c.strokeWidth)?c.strokeWidth[i]:c.strokeWidth,pointStrokeColor:l.pointStrokeColor,pointFillColor:l.pointFillColor,shape:l.pointShape||(Array.isArray(c.shape)?c.shape[i]:c.shape),class:e,pointStrokeOpacity:Array.isArray(c.strokeOpacity)?c.strokeOpacity[i]:c.strokeOpacity,pointStrokeDashArray:Array.isArray(c.strokeDashArray)?c.strokeDashArray[i]:c.strokeDashArray,pointFillOpacity:Array.isArray(c.fillOpacity)?c.fillOpacity[i]:c.fillOpacity,seriesIndex:i}}},{key:\"addEvents\",value:function(t){var e=this.w,i=new m(this.ctx);t.node.addEventListener(\"mouseenter\",i.pathMouseEnter.bind(this.ctx,t)),t.node.addEventListener(\"mouseleave\",i.pathMouseLeave.bind(this.ctx,t)),t.node.addEventListener(\"mousedown\",i.pathMouseDown.bind(this.ctx,t)),t.node.addEventListener(\"click\",e.config.markers.onClick),t.node.addEventListener(\"dblclick\",e.config.markers.onDblClick),t.node.addEventListener(\"touchstart\",i.pathMouseDown.bind(this.ctx,t),{passive:!0})}},{key:\"getMarkerStyle\",value:function(t){var e=this.w,i=e.globals.markers.colors,a=e.config.markers.strokeColor||e.config.markers.strokeColors;return{pointStrokeColor:Array.isArray(a)?a[t]:a,pointFillColor:Array.isArray(i)?i[t]:i}}}]),t}(),H=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled}return r(t,[{key:\"draw\",value:function(t,e,i){var a=this.w,s=new m(this.ctx),r=i.realIndex,o=i.pointsPos,n=i.zRatio,l=i.elParent,h=s.group({class:\"apexcharts-series-markers apexcharts-series-\".concat(a.config.chart.type)});if(h.attr(\"clip-path\",\"url(#gridRectMarkerMask\".concat(a.globals.cuid,\")\")),Array.isArray(o.x))for(var c=0;c<o.x.length;c++){var d=e+1,g=!0;0===e&&0===c&&(d=0),0===e&&1===c&&(d=1);var u=0,f=a.globals.markers.size[r];if(n!==1/0){var p=a.config.plotOptions.bubble;f=a.globals.seriesZ[r][d],p.zScaling&&(f/=n),p.minBubbleRadius&&f<p.minBubbleRadius&&(f=p.minBubbleRadius),p.maxBubbleRadius&&f>p.maxBubbleRadius&&(f=p.maxBubbleRadius)}a.config.chart.animations.enabled||(u=f);var x=o.x[c],b=o.y[c];if(u=u||0,null!==b&&void 0!==a.globals.series[r][d]||(g=!1),g){var v=this.drawPoint(x,b,u,f,r,d,e);h.add(v)}l.add(h)}}},{key:\"drawPoint\",value:function(t,e,i,a,s,r,o){var n=this.w,l=s,h=new b(this.ctx),c=new v(this.ctx),d=new R(this.ctx),g=new D(this.ctx),u=new m(this.ctx),f=g.getMarkerConfig({cssClass:\"apexcharts-marker\",seriesIndex:l,dataPointIndex:r,finishRadius:\"bubble\"===n.config.chart.type||n.globals.comboCharts&&n.config.series[s]&&\"bubble\"===n.config.series[s].type?a:null});a=f.pSize;var p,x=d.fillPath({seriesNumber:s,dataPointIndex:r,color:f.pointFillColor,patternUnits:\"objectBoundingBox\",value:n.globals.series[s][o]});if(\"circle\"===f.shape?p=u.drawCircle(i):\"square\"!==f.shape&&\"rect\"!==f.shape||(p=u.drawRect(0,0,f.width-f.pointStrokeWidth/2,f.height-f.pointStrokeWidth/2,f.pRadius)),n.config.series[l].data[r]&&n.config.series[l].data[r].fillColor&&(x=n.config.series[l].data[r].fillColor),p.attr({x:t-f.width/2-f.pointStrokeWidth/2,y:e-f.height/2-f.pointStrokeWidth/2,cx:t,cy:e,fill:x,\"fill-opacity\":f.pointFillOpacity,stroke:f.pointStrokeColor,r:a,\"stroke-width\":f.pointStrokeWidth,\"stroke-dasharray\":f.pointStrokeDashArray,\"stroke-opacity\":f.pointStrokeOpacity}),n.config.chart.dropShadow.enabled){var y=n.config.chart.dropShadow;c.dropShadow(p,y,s)}if(!this.initialAnim||n.globals.dataChanged||n.globals.resized)n.globals.animationEnded=!0;else{var w=n.config.chart.animations.speed;h.animateMarker(p,0,\"circle\"===f.shape?a:{width:f.width,height:f.height},w,n.globals.easing,(function(){window.setTimeout((function(){h.animationCompleted(p)}),100)}))}if(n.globals.dataChanged&&\"circle\"===f.shape)if(this.dynamicAnim){var k,A,S,C,L=n.config.chart.animations.dynamicAnimation.speed;null!=(C=n.globals.previousPaths[s]&&n.globals.previousPaths[s][o])&&(k=C.x,A=C.y,S=void 0!==C.r?C.r:a);for(var P=0;P<n.globals.collapsedSeries.length;P++)n.globals.collapsedSeries[P].index===s&&(L=1,a=0);0===t&&0===e&&(a=0),h.animateCircle(p,{cx:k,cy:A,r:S},{cx:t,cy:e,r:a},L,n.globals.easing)}else p.attr({r:a});return p.attr({rel:r,j:r,index:s,\"default-marker-size\":a}),c.setSelectionFilter(p,s,r),g.addEvents(p),p.node.classList.add(\"apexcharts-marker\"),p}},{key:\"centerTextInBubble\",value:function(t){var e=this.w;return{y:t+=parseInt(e.config.dataLabels.style.fontSize,10)/4}}}]),t}(),O=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"dataLabelsCorrection\",value:function(t,e,i,a,s,r,o){var n=this.w,l=!1,h=new m(this.ctx).getTextRects(i,o),c=h.width,d=h.height;e<0&&(e=0),e>n.globals.gridHeight+d&&(e=n.globals.gridHeight+d/2),void 0===n.globals.dataLabelsRects[a]&&(n.globals.dataLabelsRects[a]=[]),n.globals.dataLabelsRects[a].push({x:t,y:e,width:c,height:d});var g=n.globals.dataLabelsRects[a].length-2,u=void 0!==n.globals.lastDrawnDataLabelsIndexes[a]?n.globals.lastDrawnDataLabelsIndexes[a][n.globals.lastDrawnDataLabelsIndexes[a].length-1]:0;if(void 0!==n.globals.dataLabelsRects[a][g]){var f=n.globals.dataLabelsRects[a][u];(t>f.x+f.width+2||e>f.y+f.height+2||t+c<f.x)&&(l=!0)}return(0===s||r)&&(l=!0),{x:t,y:e,textRects:h,drawnextLabel:l}}},{key:\"drawDataLabel\",value:function(t){var e=this,i=t.type,a=t.pos,s=t.i,r=t.j,o=t.isRangeStart,n=t.strokeWidth,l=void 0===n?2:n,h=this.w,c=new m(this.ctx),d=h.config.dataLabels,g=0,u=0,f=r,p=null;if(!d.enabled||!Array.isArray(a.x))return p;p=c.group({class:\"apexcharts-data-labels\"});for(var x=0;x<a.x.length;x++)if(g=a.x[x]+d.offsetX,u=a.y[x]+d.offsetY+l,!isNaN(g)){1===r&&0===x&&(f=0),1===r&&1===x&&(f=1);var b=h.globals.series[s][f];\"rangeArea\"===i&&(b=o?h.globals.seriesRangeStart[s][f]:h.globals.seriesRangeEnd[s][f]);var v=\"\",y=function(t){return h.config.dataLabels.formatter(t,{ctx:e.ctx,seriesIndex:s,dataPointIndex:f,w:h})};if(\"bubble\"===h.config.chart.type)v=y(b=h.globals.seriesZ[s][f]),u=a.y[x],u=new H(this.ctx).centerTextInBubble(u,s,f).y;else void 0!==b&&(v=y(b));this.plotDataLabelsText({x:g,y:u,text:v,i:s,j:f,parent:p,offsetCorrection:!0,dataLabelsConfig:h.config.dataLabels})}return p}},{key:\"plotDataLabelsText\",value:function(t){var e=this.w,i=new m(this.ctx),a=t.x,s=t.y,r=t.i,o=t.j,n=t.text,l=t.textAnchor,h=t.fontSize,c=t.parent,d=t.dataLabelsConfig,g=t.color,u=t.alwaysDrawDataLabel,f=t.offsetCorrection;if(!(Array.isArray(e.config.dataLabels.enabledOnSeries)&&e.config.dataLabels.enabledOnSeries.indexOf(r)<0)){var p={x:a,y:s,drawnextLabel:!0,textRects:null};f&&(p=this.dataLabelsCorrection(a,s,n,r,o,u,parseInt(d.style.fontSize,10))),e.globals.zoomed||(a=p.x,s=p.y),p.textRects&&(a<-10-p.textRects.width||a>e.globals.gridWidth+p.textRects.width+10)&&(n=\"\");var x=e.globals.dataLabels.style.colors[r];((\"bar\"===e.config.chart.type||\"rangeBar\"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||e.config.dataLabels.distributed)&&(x=e.globals.dataLabels.style.colors[o]),\"function\"==typeof x&&(x=x({series:e.globals.series,seriesIndex:r,dataPointIndex:o,w:e})),g&&(x=g);var b=d.offsetX,y=d.offsetY;if(\"bar\"!==e.config.chart.type&&\"rangeBar\"!==e.config.chart.type||(b=0,y=0),p.drawnextLabel){var w=i.drawText({width:100,height:parseInt(d.style.fontSize,10),x:a+b,y:s+y,foreColor:x,textAnchor:l||d.textAnchor,text:n,fontSize:h||d.style.fontSize,fontFamily:d.style.fontFamily,fontWeight:d.style.fontWeight||\"normal\"});if(w.attr({class:\"apexcharts-datalabel\",cx:a,cy:s}),d.dropShadow.enabled){var k=d.dropShadow;new v(this.ctx).dropShadow(w,k)}c.add(w),void 0===e.globals.lastDrawnDataLabelsIndexes[r]&&(e.globals.lastDrawnDataLabelsIndexes[r]=[]),e.globals.lastDrawnDataLabelsIndexes[r].push(o)}}}},{key:\"addBackgroundToDataLabel\",value:function(t,e){var i=this.w,a=i.config.dataLabels.background,s=a.padding,r=a.padding/2,o=e.width,n=e.height,l=new m(this.ctx).drawRect(e.x-s,e.y-r/2,o+2*s,n+r,a.borderRadius,\"transparent\"===i.config.chart.background?\"#fff\":i.config.chart.background,a.opacity,a.borderWidth,a.borderColor);a.dropShadow.enabled&&new v(this.ctx).dropShadow(l,a.dropShadow);return l}},{key:\"dataLabelsBackground\",value:function(){var t=this.w;if(\"bubble\"!==t.config.chart.type)for(var e=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-datalabels text\"),i=0;i<e.length;i++){var a=e[i],s=a.getBBox(),r=null;if(s.width&&s.height&&(r=this.addBackgroundToDataLabel(a,s)),r){a.parentNode.insertBefore(r.node,a);var o=a.getAttribute(\"fill\");t.config.chart.animations.enabled&&!t.globals.resized&&!t.globals.dataChanged?r.animate().attr({fill:o}):r.attr({fill:o}),a.setAttribute(\"fill\",t.config.dataLabels.background.foreColor)}}}},{key:\"bringForward\",value:function(){for(var t=this.w,e=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-datalabels\"),i=t.globals.dom.baseEl.querySelector(\".apexcharts-plot-series:last-child\"),a=0;a<e.length;a++)i&&i.insertBefore(e[a],i.nextSibling)}}]),t}(),N=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.legendInactiveClass=\"legend-mouseover-inactive\"}return r(t,[{key:\"getAllSeriesEls\",value:function(){return this.w.globals.dom.baseEl.getElementsByClassName(\"apexcharts-series\")}},{key:\"getSeriesByName\",value:function(t){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-inner .apexcharts-series[seriesName='\".concat(x.escapeString(t),\"']\"))}},{key:\"isSeriesHidden\",value:function(t){var e=this.getSeriesByName(t),i=parseInt(e.getAttribute(\"data:realIndex\"),10);return{isHidden:e.classList.contains(\"apexcharts-series-collapsed\"),realIndex:i}}},{key:\"addCollapsedClassToSeries\",value:function(t,e){var i=this.w;function a(i){for(var a=0;a<i.length;a++)i[a].index===e&&t.node.classList.add(\"apexcharts-series-collapsed\")}a(i.globals.collapsedSeries),a(i.globals.ancillaryCollapsedSeries)}},{key:\"toggleSeries\",value:function(t){var e=this.isSeriesHidden(t);return this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,e.isHidden),e.isHidden}},{key:\"showSeries\",value:function(t){var e=this.isSeriesHidden(t);e.isHidden&&this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!0)}},{key:\"hideSeries\",value:function(t){var e=this.isSeriesHidden(t);e.isHidden||this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!1)}},{key:\"resetSeries\",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w,s=x.clone(a.globals.initialSeries);a.globals.previousPaths=[],i?(a.globals.collapsedSeries=[],a.globals.ancillaryCollapsedSeries=[],a.globals.collapsedSeriesIndices=[],a.globals.ancillaryCollapsedSeriesIndices=[]):s=this.emptyCollapsedSeries(s),a.config.series=s,t&&(e&&(a.globals.zoomed=!1,this.ctx.updateHelpers.revertDefaultAxisMinMax()),this.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled))}},{key:\"emptyCollapsedSeries\",value:function(t){for(var e=this.w,i=0;i<t.length;i++)e.globals.collapsedSeriesIndices.indexOf(i)>-1&&(t[i].data=[]);return t}},{key:\"toggleSeriesOnHover\",value:function(t,e){var i=this.w;e||(e=t.target);var a=i.globals.dom.baseEl.querySelectorAll(\".apexcharts-series, .apexcharts-datalabels\");if(\"mousemove\"===t.type){var s=parseInt(e.getAttribute(\"rel\"),10)-1,r=null,o=null;i.globals.axisCharts||\"radialBar\"===i.config.chart.type?i.globals.axisCharts?(r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(s,\"']\")),o=i.globals.dom.baseEl.querySelector(\".apexcharts-datalabels[data\\\\:realIndex='\".concat(s,\"']\"))):r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(s+1,\"']\")):r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(s+1,\"'] path\"));for(var n=0;n<a.length;n++)a[n].classList.add(this.legendInactiveClass);null!==r&&(i.globals.axisCharts||r.parentNode.classList.remove(this.legendInactiveClass),r.classList.remove(this.legendInactiveClass),null!==o&&o.classList.remove(this.legendInactiveClass))}else if(\"mouseout\"===t.type)for(var l=0;l<a.length;l++)a[l].classList.remove(this.legendInactiveClass)}},{key:\"highlightRangeInSeries\",value:function(t,e){var i=this,a=this.w,s=a.globals.dom.baseEl.getElementsByClassName(\"apexcharts-heatmap-rect\"),r=function(t){for(var e=0;e<s.length;e++)s[e].classList[t](i.legendInactiveClass)};if(\"mousemove\"===t.type){var o=parseInt(e.getAttribute(\"rel\"),10)-1;r(\"add\"),function(t){for(var e=0;e<s.length;e++){var a=parseInt(s[e].getAttribute(\"val\"),10);a>=t.from&&a<=t.to&&s[e].classList.remove(i.legendInactiveClass)}}(a.config.plotOptions.heatmap.colorScale.ranges[o])}else\"mouseout\"===t.type&&r(\"remove\")}},{key:\"getActiveConfigSeriesIndex\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"asc\",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],i=this.w,a=0;if(i.config.series.length>1)for(var s=i.config.series.map((function(t,a){return t.data&&t.data.length>0&&-1===i.globals.collapsedSeriesIndices.indexOf(a)&&(!i.globals.comboCharts||0===e.length||e.length&&e.indexOf(i.config.series[a].type)>-1)?a:-1})),r=\"asc\"===t?0:s.length-1;\"asc\"===t?r<s.length:r>=0;\"asc\"===t?r++:r--)if(-1!==s[r]){a=s[r];break}return a}},{key:\"getBarSeriesIndices\",value:function(){return this.w.globals.comboCharts?this.w.config.series.map((function(t,e){return\"bar\"===t.type||\"column\"===t.type?e:-1})).filter((function(t){return-1!==t})):this.w.config.series.map((function(t,e){return e}))}},{key:\"getPreviousPaths\",value:function(){var t=this.w;function e(e,i,a){for(var s=e[i].childNodes,r={type:a,paths:[],realIndex:e[i].getAttribute(\"data:realIndex\")},o=0;o<s.length;o++)if(s[o].hasAttribute(\"pathTo\")){var n=s[o].getAttribute(\"pathTo\");r.paths.push({d:n})}t.globals.previousPaths.push(r)}t.globals.previousPaths=[];[\"line\",\"area\",\"bar\",\"rangebar\",\"rangeArea\",\"candlestick\",\"radar\"].forEach((function(i){for(var a,s=(a=i,t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(a,\"-series .apexcharts-series\"))),r=0;r<s.length;r++)e(s,r,i)})),this.handlePrevBubbleScatterPaths(\"bubble\"),this.handlePrevBubbleScatterPaths(\"scatter\");var i=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t.config.chart.type,\" .apexcharts-series\"));if(i.length>0)for(var a=function(e){for(var i=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t.config.chart.type,\" .apexcharts-series[data\\\\:realIndex='\").concat(e,\"'] rect\")),a=[],s=function(t){var e=function(e){return i[t].getAttribute(e)},s={x:parseFloat(e(\"x\")),y:parseFloat(e(\"y\")),width:parseFloat(e(\"width\")),height:parseFloat(e(\"height\"))};a.push({rect:s,color:i[t].getAttribute(\"color\")})},r=0;r<i.length;r++)s(r);t.globals.previousPaths.push(a)},s=0;s<i.length;s++)a(s);t.globals.axisCharts||(t.globals.previousPaths=t.globals.series)}},{key:\"handlePrevBubbleScatterPaths\",value:function(t){var e=this.w,i=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t,\"-series .apexcharts-series\"));if(i.length>0)for(var a=0;a<i.length;a++){for(var s=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t,\"-series .apexcharts-series[data\\\\:realIndex='\").concat(a,\"'] circle\")),r=[],o=0;o<s.length;o++)r.push({x:s[o].getAttribute(\"cx\"),y:s[o].getAttribute(\"cy\"),r:s[o].getAttribute(\"r\")});e.globals.previousPaths.push(r)}}},{key:\"clearPreviousPaths\",value:function(){var t=this.w;t.globals.previousPaths=[],t.globals.allSeriesCollapsed=!1}},{key:\"handleNoData\",value:function(){var t=this.w,e=t.config.noData,i=new m(this.ctx),a=t.globals.svgWidth/2,s=t.globals.svgHeight/2,r=\"middle\";if(t.globals.noData=!0,t.globals.animationEnded=!0,\"left\"===e.align?(a=10,r=\"start\"):\"right\"===e.align&&(a=t.globals.svgWidth-10,r=\"end\"),\"top\"===e.verticalAlign?s=50:\"bottom\"===e.verticalAlign&&(s=t.globals.svgHeight-50),a+=e.offsetX,s=s+parseInt(e.style.fontSize,10)+2+e.offsetY,void 0!==e.text&&\"\"!==e.text){var o=i.drawText({x:a,y:s,text:e.text,textAnchor:r,fontSize:e.style.fontSize,fontFamily:e.style.fontFamily,foreColor:e.style.color,opacity:1,class:\"apexcharts-text-nodata\"});t.globals.dom.Paper.add(o)}}},{key:\"setNullSeriesToZeroValues\",value:function(t){for(var e=this.w,i=0;i<t.length;i++)if(0===t[i].length)for(var a=0;a<t[e.globals.maxValsInArrayIndex].length;a++)t[i].push(0);return t}},{key:\"hasAllSeriesEqualX\",value:function(){for(var t=!0,e=this.w,i=this.filteredSeriesX(),a=0;a<i.length-1;a++)if(i[a][0]!==i[a+1][0]){t=!1;break}return e.globals.allSeriesHasEqualX=t,t}},{key:\"filteredSeriesX\",value:function(){var t=this.w.globals.seriesX.map((function(t){return t.length>0?t:[]}));return t}}]),t}(),W=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.twoDSeries=[],this.threeDSeries=[],this.twoDSeriesX=[],this.seriesGoals=[],this.coreUtils=new y(this.ctx)}return r(t,[{key:\"isMultiFormat\",value:function(){return this.isFormatXY()||this.isFormat2DArray()}},{key:\"isFormatXY\",value:function(){var t=this.w.config.series.slice(),e=new N(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&null!==t[this.activeSeriesIndex].data[0]&&void 0!==t[this.activeSeriesIndex].data[0].x&&null!==t[this.activeSeriesIndex].data[0])return!0}},{key:\"isFormat2DArray\",value:function(){var t=this.w.config.series.slice(),e=new N(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&void 0!==t[this.activeSeriesIndex].data[0]&&null!==t[this.activeSeriesIndex].data[0]&&t[this.activeSeriesIndex].data[0].constructor===Array)return!0}},{key:\"handleFormat2DArray\",value:function(t,e){for(var i=this.w.config,a=this.w.globals,s=\"boxPlot\"===i.chart.type||\"boxPlot\"===i.series[e].type,r=0;r<t[e].data.length;r++)if(void 0!==t[e].data[r][1]&&(Array.isArray(t[e].data[r][1])&&4===t[e].data[r][1].length&&!s?this.twoDSeries.push(x.parseNumber(t[e].data[r][1][3])):t[e].data[r].length>=5?this.twoDSeries.push(x.parseNumber(t[e].data[r][4])):this.twoDSeries.push(x.parseNumber(t[e].data[r][1])),a.dataFormatXNumeric=!0),\"datetime\"===i.xaxis.type){var o=new Date(t[e].data[r][0]);o=new Date(o).getTime(),this.twoDSeriesX.push(o)}else this.twoDSeriesX.push(t[e].data[r][0]);for(var n=0;n<t[e].data.length;n++)void 0!==t[e].data[n][2]&&(this.threeDSeries.push(t[e].data[n][2]),a.isDataXYZ=!0)}},{key:\"handleFormatXY\",value:function(t,e){var i=this.w.config,a=this.w.globals,s=new T(this.ctx),r=e;a.collapsedSeriesIndices.indexOf(e)>-1&&(r=this.activeSeriesIndex);for(var o=0;o<t[e].data.length;o++)void 0!==t[e].data[o].y&&(Array.isArray(t[e].data[o].y)?this.twoDSeries.push(x.parseNumber(t[e].data[o].y[t[e].data[o].y.length-1])):this.twoDSeries.push(x.parseNumber(t[e].data[o].y))),void 0!==t[e].data[o].goals&&Array.isArray(t[e].data[o].goals)?(void 0===this.seriesGoals[e]&&(this.seriesGoals[e]=[]),this.seriesGoals[e].push(t[e].data[o].goals)):(void 0===this.seriesGoals[e]&&(this.seriesGoals[e]=[]),this.seriesGoals[e].push(null));for(var n=0;n<t[r].data.length;n++){var l=\"string\"==typeof t[r].data[n].x,h=Array.isArray(t[r].data[n].x),c=!h&&!!s.isValidDate(t[r].data[n].x.toString());if(l||c)if(l||i.xaxis.convertedCatToNumeric){var d=a.isBarHorizontal&&a.isRangeData;\"datetime\"!==i.xaxis.type||d?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[n].x)):this.twoDSeriesX.push(s.parseDate(t[r].data[n].x))}else\"datetime\"===i.xaxis.type?this.twoDSeriesX.push(s.parseDate(t[r].data[n].x.toString())):(a.dataFormatXNumeric=!0,a.isXNumeric=!0,this.twoDSeriesX.push(parseFloat(t[r].data[n].x)));else h?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[n].x)):(a.isXNumeric=!0,a.dataFormatXNumeric=!0,this.twoDSeriesX.push(t[r].data[n].x))}if(t[e].data[0]&&void 0!==t[e].data[0].z){for(var g=0;g<t[e].data.length;g++)this.threeDSeries.push(t[e].data[g].z);a.isDataXYZ=!0}}},{key:\"handleRangeData\",value:function(t,e){var i=this.w.globals,a={};return this.isFormat2DArray()?a=this.handleRangeDataFormat(\"array\",t,e):this.isFormatXY()&&(a=this.handleRangeDataFormat(\"xy\",t,e)),i.seriesRangeStart.push(a.start),i.seriesRangeEnd.push(a.end),i.seriesRange.push(a.rangeUniques),i.seriesRange.forEach((function(t,e){t&&t.forEach((function(t,e){t.y.forEach((function(e,i){for(var a=0;a<t.y.length;a++)if(i!==a){var s=e.y1,r=e.y2,o=t.y[a].y1;s<=t.y[a].y2&&o<=r&&(t.overlaps.indexOf(e.rangeName)<0&&t.overlaps.push(e.rangeName),t.overlaps.indexOf(t.y[a].rangeName)<0&&t.overlaps.push(t.y[a].rangeName))}}))}))})),a}},{key:\"handleCandleStickBoxData\",value:function(t,e){var i=this.w.globals,a={};return this.isFormat2DArray()?a=this.handleCandleStickBoxDataFormat(\"array\",t,e):this.isFormatXY()&&(a=this.handleCandleStickBoxDataFormat(\"xy\",t,e)),i.seriesCandleO[e]=a.o,i.seriesCandleH[e]=a.h,i.seriesCandleM[e]=a.m,i.seriesCandleL[e]=a.l,i.seriesCandleC[e]=a.c,a}},{key:\"handleRangeDataFormat\",value:function(t,e,i){var a=[],s=[],r=e[i].data.filter((function(t,e,i){return e===i.findIndex((function(e){return e.x===t.x}))})).map((function(t,e){return{x:t.x,overlaps:[],y:[]}}));if(\"array\"===t)for(var o=0;o<e[i].data.length;o++)Array.isArray(e[i].data[o])?(a.push(e[i].data[o][1][0]),s.push(e[i].data[o][1][1])):(a.push(e[i].data[o]),s.push(e[i].data[o]));else if(\"xy\"===t)for(var n=function(t){var o=Array.isArray(e[i].data[t].y),n=x.randomId(),l=e[i].data[t].x,h={y1:o?e[i].data[t].y[0]:e[i].data[t].y,y2:o?e[i].data[t].y[1]:e[i].data[t].y,rangeName:n};e[i].data[t].rangeName=n;var c=r.findIndex((function(t){return t.x===l}));r[c].y.push(h),a.push(h.y1),s.push(h.y2)},l=0;l<e[i].data.length;l++)n(l);return{start:a,end:s,rangeUniques:r}}},{key:\"handleCandleStickBoxDataFormat\",value:function(t,e,i){var a=this.w,s=\"boxPlot\"===a.config.chart.type||\"boxPlot\"===a.config.series[i].type,r=[],o=[],n=[],l=[],h=[];if(\"array\"===t)if(s&&6===e[i].data[0].length||!s&&5===e[i].data[0].length)for(var c=0;c<e[i].data.length;c++)r.push(e[i].data[c][1]),o.push(e[i].data[c][2]),s?(n.push(e[i].data[c][3]),l.push(e[i].data[c][4]),h.push(e[i].data[c][5])):(l.push(e[i].data[c][3]),h.push(e[i].data[c][4]));else for(var d=0;d<e[i].data.length;d++)Array.isArray(e[i].data[d][1])&&(r.push(e[i].data[d][1][0]),o.push(e[i].data[d][1][1]),s?(n.push(e[i].data[d][1][2]),l.push(e[i].data[d][1][3]),h.push(e[i].data[d][1][4])):(l.push(e[i].data[d][1][2]),h.push(e[i].data[d][1][3])));else if(\"xy\"===t)for(var g=0;g<e[i].data.length;g++)Array.isArray(e[i].data[g].y)&&(r.push(e[i].data[g].y[0]),o.push(e[i].data[g].y[1]),s?(n.push(e[i].data[g].y[2]),l.push(e[i].data[g].y[3]),h.push(e[i].data[g].y[4])):(l.push(e[i].data[g].y[2]),h.push(e[i].data[g].y[3])));return{o:r,h:o,m:n,l:l,c:h}}},{key:\"parseDataAxisCharts\",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.ctx,a=this.w.config,s=this.w.globals,r=new T(i),o=a.labels.length>0?a.labels.slice():a.xaxis.categories.slice();s.isRangeBar=\"rangeBar\"===a.chart.type&&s.isBarHorizontal,s.hasGroups=\"category\"===a.xaxis.type&&a.xaxis.group.groups.length>0,s.hasGroups&&(s.groups=a.xaxis.group.groups);for(var n=function(){for(var t=0;t<o.length;t++)if(\"string\"==typeof o[t]){if(!r.isValidDate(o[t]))throw new Error(\"You have provided invalid Date format. Please provide a valid JavaScript Date\");e.twoDSeriesX.push(r.parseDate(o[t]))}else e.twoDSeriesX.push(o[t])},l=0;l<t.length;l++){if(this.twoDSeries=[],this.twoDSeriesX=[],this.threeDSeries=[],void 0===t[l].data)return void console.error(\"It is a possibility that you may have not included 'data' property in series.\");if(\"rangeBar\"!==a.chart.type&&\"rangeArea\"!==a.chart.type&&\"rangeBar\"!==t[l].type&&\"rangeArea\"!==t[l].type||(s.isRangeData=!0,s.isComboCharts?\"rangeBar\"!==t[l].type&&\"rangeArea\"!==t[l].type||this.handleRangeData(t,l):\"rangeBar\"!==a.chart.type&&\"rangeArea\"!==a.chart.type||this.handleRangeData(t,l)),this.isMultiFormat())this.isFormat2DArray()?this.handleFormat2DArray(t,l):this.isFormatXY()&&this.handleFormatXY(t,l),\"candlestick\"!==a.chart.type&&\"candlestick\"!==t[l].type&&\"boxPlot\"!==a.chart.type&&\"boxPlot\"!==t[l].type||this.handleCandleStickBoxData(t,l),s.series.push(this.twoDSeries),s.labels.push(this.twoDSeriesX),s.seriesX.push(this.twoDSeriesX),s.seriesGoals=this.seriesGoals,l!==this.activeSeriesIndex||this.fallbackToCategory||(s.isXNumeric=!0);else{\"datetime\"===a.xaxis.type?(s.isXNumeric=!0,n(),s.seriesX.push(this.twoDSeriesX)):\"numeric\"===a.xaxis.type&&(s.isXNumeric=!0,o.length>0&&(this.twoDSeriesX=o,s.seriesX.push(this.twoDSeriesX))),s.labels.push(this.twoDSeriesX);var h=t[l].data.map((function(t){return x.parseNumber(t)}));s.series.push(h)}s.seriesZ.push(this.threeDSeries),void 0!==t[l].name?s.seriesNames.push(t[l].name):s.seriesNames.push(\"series-\"+parseInt(l+1,10)),void 0!==t[l].color?s.seriesColors.push(t[l].color):s.seriesColors.push(void 0)}return this.w}},{key:\"parseDataNonAxisCharts\",value:function(t){var e=this.w.globals,i=this.w.config;e.series=t.slice(),e.seriesNames=i.labels.slice();for(var a=0;a<e.series.length;a++)void 0===e.seriesNames[a]&&e.seriesNames.push(\"series-\"+(a+1));return this.w}},{key:\"handleExternalLabelsData\",value:function(t){var e=this.w.config,i=this.w.globals;if(e.xaxis.categories.length>0)i.labels=e.xaxis.categories;else if(e.labels.length>0)i.labels=e.labels.slice();else if(this.fallbackToCategory){if(i.labels=i.labels[0],i.seriesRange.length&&(i.seriesRange.map((function(t){t.forEach((function(t){i.labels.indexOf(t.x)<0&&t.x&&i.labels.push(t.x)}))})),i.labels=i.labels.filter((function(t,e,i){return i.indexOf(t)===e}))),e.xaxis.convertedCatToNumeric)new X(e).convertCatToNumericXaxis(e,this.ctx,i.seriesX[0]),this._generateExternalLabels(t)}else this._generateExternalLabels(t)}},{key:\"_generateExternalLabels\",value:function(t){var e=this.w.globals,i=this.w.config,a=[];if(e.axisCharts){if(e.series.length>0)if(this.isFormatXY())for(var s=i.series.map((function(t,e){return t.data.filter((function(t,e,i){return i.findIndex((function(e){return e.x===t.x}))===e}))})),r=s.reduce((function(t,e,i,a){return a[t].length>e.length?t:i}),0),o=0;o<s[r].length;o++)a.push(o+1);else for(var n=0;n<e.series[e.maxValsInArrayIndex].length;n++)a.push(n+1);e.seriesX=[];for(var l=0;l<t.length;l++)e.seriesX.push(a);e.isXNumeric=!0}if(0===a.length){a=e.axisCharts?[]:e.series.map((function(t,e){return e+1}));for(var h=0;h<t.length;h++)e.seriesX.push(a)}e.labels=a,i.xaxis.convertedCatToNumeric&&(e.categoryLabels=a.map((function(t){return i.xaxis.labels.formatter(t)}))),e.noLabelsProvided=!0}},{key:\"parseData\",value:function(t){var e=this.w,i=e.config,a=e.globals;if(this.excludeCollapsedSeriesInYAxis(),this.fallbackToCategory=!1,this.ctx.core.resetGlobals(),this.ctx.core.isMultipleY(),a.axisCharts?(this.parseDataAxisCharts(t),this.coreUtils.getLargestSeries()):this.parseDataNonAxisCharts(t),\"bar\"===i.chart.type&&i.chart.stacked){var s=new N(this.ctx);a.series=s.setNullSeriesToZeroValues(a.series)}this.coreUtils.getSeriesTotals(),a.axisCharts&&(a.stackedSeriesTotals=this.coreUtils.getStackedSeriesTotals()),this.coreUtils.getPercentSeries(),a.dataFormatXNumeric||a.isXNumeric&&(\"numeric\"!==i.xaxis.type||0!==i.labels.length||0!==i.xaxis.categories.length)||this.handleExternalLabelsData(t);for(var r=this.coreUtils.getCategoryLabels(a.labels),o=0;o<r.length;o++)if(Array.isArray(r[o])){a.isMultiLineX=!0;break}}},{key:\"excludeCollapsedSeriesInYAxis\",value:function(){var t=this,e=this.w;e.globals.ignoreYAxisIndexes=e.globals.collapsedSeries.map((function(i,a){if(t.w.globals.isMultipleYAxis&&!e.config.chart.stacked)return i.index}))}}]),t}(),B=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"getLabel\",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:\"12px\",o=!(arguments.length>6&&void 0!==arguments[6])||arguments[6],n=this.w,l=void 0===t[a]?\"\":t[a],h=l,c=n.globals.xLabelFormatter,d=n.config.xaxis.labels.formatter,g=!1,u=new M(this.ctx),f=l;o&&(h=u.xLabelFormat(c,l,f,{i:a,dateFormatter:new T(this.ctx).formatDate,w:n}),void 0!==d&&(h=d(l,t[a],{i:a,dateFormatter:new T(this.ctx).formatDate,w:n})));var p=function(t){var i=null;return e.forEach((function(t){\"month\"===t.unit?i=\"year\":\"day\"===t.unit?i=\"month\":\"hour\"===t.unit?i=\"day\":\"minute\"===t.unit&&(i=\"hour\")})),i===t};e.length>0?(g=p(e[a].unit),i=e[a].position,h=e[a].value):\"datetime\"===n.config.xaxis.type&&void 0===d&&(h=\"\"),void 0===h&&(h=\"\"),h=Array.isArray(h)?h:h.toString();var x=new m(this.ctx),b={};b=n.globals.rotateXLabels&&o?x.getTextRects(h,parseInt(r,10),null,\"rotate(\".concat(n.config.xaxis.labels.rotate,\" 0 0)\"),!1):x.getTextRects(h,parseInt(r,10));var v=!n.config.xaxis.labels.showDuplicates&&this.ctx.timeScale;return!Array.isArray(h)&&(0===h.indexOf(\"NaN\")||0===h.toLowerCase().indexOf(\"invalid\")||h.toLowerCase().indexOf(\"infinity\")>=0||s.indexOf(h)>=0&&v)&&(h=\"\"),{x:i,text:h,textRect:b,isBold:g}}},{key:\"checkLabelBasedOnTickamount\",value:function(t,e,i){var a=this.w,s=a.config.xaxis.tickAmount;return\"dataPoints\"===s&&(s=Math.round(a.globals.gridWidth/120)),s>i||t%Math.round(i/(s+1))==0||(e.text=\"\"),e}},{key:\"checkForOverflowingLabels\",value:function(t,e,i,a,s){var r=this.w;if(0===t&&r.globals.skipFirstTimelinelabel&&(e.text=\"\"),t===i-1&&r.globals.skipLastTimelinelabel&&(e.text=\"\"),r.config.xaxis.labels.hideOverlappingLabels&&a.length>0){var o=s[s.length-1];e.x<o.textRect.width/(r.globals.rotateXLabels?Math.abs(r.config.xaxis.labels.rotate)/12:1.01)+o.x&&(e.text=\"\")}return e}},{key:\"checkForReversedLabels\",value:function(t,e){var i=this.w;return i.config.yaxis[t]&&i.config.yaxis[t].reversed&&e.reverse(),e}},{key:\"isYAxisHidden\",value:function(t){var e=this.w,i=new y(this.ctx);return!e.config.yaxis[t].show||!e.config.yaxis[t].showForNullSeries&&i.isSeriesNull(t)&&-1===e.globals.collapsedSeriesIndices.indexOf(t)}},{key:\"getYAxisForeColor\",value:function(t,e){var i=this.w;return Array.isArray(t)&&i.globals.yAxisScale[e]&&this.ctx.theme.pushExtraColors(t,i.globals.yAxisScale[e].result.length,!1),t}},{key:\"drawYAxisTicks\",value:function(t,e,i,a,s,r,o){var n=this.w,l=new m(this.ctx),h=n.globals.translateY;if(a.show&&e>0){!0===n.config.yaxis[s].opposite&&(t+=a.width);for(var c=e;c>=0;c--){var d=h+e/10+n.config.yaxis[s].labels.offsetY-1;n.globals.isBarHorizontal&&(d=r*c),\"heatmap\"===n.config.chart.type&&(d+=r/2);var g=l.drawLine(t+i.offsetX-a.width+a.offsetX,d+a.offsetY,t+i.offsetX+a.offsetX,d+a.offsetY,a.color);o.add(g),h+=r}}}}]),t}(),G=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"scaleSvgNode\",value:function(t,e){var i=parseFloat(t.getAttributeNS(null,\"width\")),a=parseFloat(t.getAttributeNS(null,\"height\"));t.setAttributeNS(null,\"width\",i*e),t.setAttributeNS(null,\"height\",a*e),t.setAttributeNS(null,\"viewBox\",\"0 0 \"+i+\" \"+a)}},{key:\"fixSvgStringForIe11\",value:function(t){if(!x.isIE11())return t.replace(/&nbsp;/g,\"&#160;\");var e=0,i=t.replace(/xmlns=\"http:\\/\\/www.w3.org\\/2000\\/svg\"/g,(function(t){return 2===++e?'xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:svgjs=\"http://svgjs.dev\"':t}));return i=(i=i.replace(/xmlns:NS\\d+=\"\"/g,\"\")).replace(/NS\\d+:(\\w+:\\w+=\")/g,\"$1\")}},{key:\"getSvgString\",value:function(t){null==t&&(t=1);var e=this.w.globals.dom.Paper.svg();if(1!==t){var i=this.w.globals.dom.Paper.node.cloneNode(!0);this.scaleSvgNode(i,t),e=(new XMLSerializer).serializeToString(i)}return this.fixSvgStringForIe11(e)}},{key:\"cleanup\",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-xcrosshairs\"),i=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-ycrosshairs\"),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-zoom-rect, .apexcharts-selection-rect\");Array.prototype.forEach.call(a,(function(t){t.setAttribute(\"width\",0)})),e&&e[0]&&(e[0].setAttribute(\"x\",-500),e[0].setAttribute(\"x1\",-500),e[0].setAttribute(\"x2\",-500)),i&&i[0]&&(i[0].setAttribute(\"y\",-100),i[0].setAttribute(\"y1\",-100),i[0].setAttribute(\"y2\",-100))}},{key:\"svgUrl\",value:function(){this.cleanup();var t=this.getSvgString(),e=new Blob([t],{type:\"image/svg+xml;charset=utf-8\"});return URL.createObjectURL(e)}},{key:\"dataURI\",value:function(t){var e=this;return new Promise((function(i){var a=e.w,s=t?t.scale||t.width/a.globals.svgWidth:1;e.cleanup();var r=document.createElement(\"canvas\");r.width=a.globals.svgWidth*s,r.height=parseInt(a.globals.dom.elWrap.style.height,10)*s;var o=\"transparent\"===a.config.chart.background?\"#fff\":a.config.chart.background,n=r.getContext(\"2d\");n.fillStyle=o,n.fillRect(0,0,r.width*s,r.height*s);var l=e.getSvgString(s);if(window.canvg&&x.isIE11()){var h=window.canvg.Canvg.fromString(n,l,{ignoreClear:!0,ignoreDimensions:!0});h.start();var c=r.msToBlob();h.stop(),i({blob:c})}else{var d=\"data:image/svg+xml,\"+encodeURIComponent(l),g=new Image;g.crossOrigin=\"anonymous\",g.onload=function(){if(n.drawImage(g,0,0),r.msToBlob){var t=r.msToBlob();i({blob:t})}else{var e=r.toDataURL(\"image/png\");i({imgURI:e})}},g.src=d}}))}},{key:\"exportToSVG\",value:function(){this.triggerDownload(this.svgUrl(),this.w.config.chart.toolbar.export.svg.filename,\".svg\")}},{key:\"exportToPng\",value:function(){var t=this;this.dataURI().then((function(e){var i=e.imgURI,a=e.blob;a?navigator.msSaveOrOpenBlob(a,t.w.globals.chartID+\".png\"):t.triggerDownload(i,t.w.config.chart.toolbar.export.png.filename,\".png\")}))}},{key:\"exportToCSV\",value:function(t){var e=this,i=t.series,a=t.fileName,s=t.columnDelimiter,r=void 0===s?\",\":s,o=t.lineDelimiter,n=void 0===o?\"\\n\":o,l=this.w;i||(i=l.config.series);var h=[],c=[],d=\"\",g=l.globals.series.map((function(t,e){return-1===l.globals.collapsedSeriesIndices.indexOf(e)?t:[]})),f=Math.max.apply(Math,u(i.map((function(t){return t.data?t.data.length:0})))),p=new W(this.ctx),b=new B(this.ctx),v=function(t){var i=\"\";if(l.globals.axisCharts){if(\"category\"===l.config.xaxis.type||l.config.xaxis.convertedCatToNumeric)if(l.globals.isBarHorizontal){var a=l.globals.yLabelFormatters[0],s=new N(e.ctx).getActiveConfigSeriesIndex();i=a(l.globals.labels[t],{seriesIndex:s,dataPointIndex:t,w:l})}else i=b.getLabel(l.globals.labels,l.globals.timescaleLabels,0,t).text;\"datetime\"===l.config.xaxis.type&&(l.config.xaxis.categories.length?i=l.config.xaxis.categories[t]:l.config.labels.length&&(i=l.config.labels[t]))}else i=l.config.labels[t];return Array.isArray(i)&&(i=i.join(\" \")),x.isNumber(i)?i:i.split(r).join(\"\")},m=function(t,e){if(h.length&&0===e&&c.push(h.join(r)),t.data){t.data=t.data.length&&t.data||u(Array(f)).map((function(){return\"\"}));for(var a=0;a<t.data.length;a++){h=[];var s=v(a);if(s||(p.isFormatXY()?s=i[e].data[a].x:p.isFormat2DArray()&&(s=i[e].data[a]?i[e].data[a][0]:\"\")),0===e){h.push((n=s,\"datetime\"===l.config.xaxis.type&&String(n).length>=10?l.config.chart.toolbar.export.csv.dateFormatter(s):x.isNumber(s)?s:s.split(r).join(\"\")));for(var o=0;o<l.globals.series.length;o++)p.isFormatXY()?h.push(i[o].data[a].y):h.push(g[o][a])}(\"candlestick\"===l.config.chart.type||t.type&&\"candlestick\"===t.type)&&(h.pop(),h.push(l.globals.seriesCandleO[e][a]),h.push(l.globals.seriesCandleH[e][a]),h.push(l.globals.seriesCandleL[e][a]),h.push(l.globals.seriesCandleC[e][a])),(\"boxPlot\"===l.config.chart.type||t.type&&\"boxPlot\"===t.type)&&(h.pop(),h.push(l.globals.seriesCandleO[e][a]),h.push(l.globals.seriesCandleH[e][a]),h.push(l.globals.seriesCandleM[e][a]),h.push(l.globals.seriesCandleL[e][a]),h.push(l.globals.seriesCandleC[e][a])),\"rangeBar\"===l.config.chart.type&&(h.pop(),h.push(l.globals.seriesRangeStart[e][a]),h.push(l.globals.seriesRangeEnd[e][a])),h.length&&c.push(h.join(r))}}var n};h.push(l.config.chart.toolbar.export.csv.headerCategory),\"boxPlot\"===l.config.chart.type?(h.push(\"minimum\"),h.push(\"q1\"),h.push(\"median\"),h.push(\"q3\"),h.push(\"maximum\")):\"candlestick\"===l.config.chart.type?(h.push(\"open\"),h.push(\"high\"),h.push(\"low\"),h.push(\"close\")):\"rangeBar\"===l.config.chart.type?(h.push(\"minimum\"),h.push(\"maximum\")):i.map((function(t,e){var i=t.name?t.name:\"series-\".concat(e);l.globals.axisCharts&&h.push(i.split(r).join(\"\")?i.split(r).join(\"\"):\"series-\".concat(e))})),l.globals.axisCharts||(h.push(l.config.chart.toolbar.export.csv.headerValue),c.push(h.join(r))),i.map((function(t,e){l.globals.axisCharts?m(t,e):((h=[]).push(l.globals.labels[e].split(r).join(\"\")),h.push(g[e]),c.push(h.join(r)))})),d+=c.join(n),this.triggerDownload(\"data:text/csv; charset=utf-8,\"+encodeURIComponent(\"\\ufeff\"+d),a||l.config.chart.toolbar.export.csv.filename,\".csv\")}},{key:\"triggerDownload\",value:function(t,e,i){var a=document.createElement(\"a\");a.href=t,a.download=(e||this.w.globals.chartID)+i,document.body.appendChild(a),a.click(),document.body.removeChild(a)}}]),t}(),V=function(){function t(e,i){a(this,t),this.ctx=e,this.elgrid=i,this.w=e.w;var s=this.w;this.axesUtils=new B(e),this.xaxisLabels=s.globals.labels.slice(),s.globals.timescaleLabels.length>0&&!s.globals.isBarHorizontal&&(this.xaxisLabels=s.globals.timescaleLabels.slice()),s.config.xaxis.overwriteCategories&&(this.xaxisLabels=s.config.xaxis.overwriteCategories),this.drawnLabels=[],this.drawnLabelsRects=[],\"top\"===s.config.xaxis.position?this.offY=0:this.offY=s.globals.gridHeight+1,this.offY=this.offY+s.config.xaxis.axisBorder.offsetY,this.isCategoryBarHorizontal=\"bar\"===s.config.chart.type&&s.config.plotOptions.bar.horizontal,this.xaxisFontSize=s.config.xaxis.labels.style.fontSize,this.xaxisFontFamily=s.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=s.config.xaxis.labels.style.colors,this.xaxisBorderWidth=s.config.xaxis.axisBorder.width,this.isCategoryBarHorizontal&&(this.xaxisBorderWidth=s.config.yaxis[0].axisBorder.width.toString()),this.xaxisBorderWidth.indexOf(\"%\")>-1?this.xaxisBorderWidth=s.globals.gridWidth*parseInt(this.xaxisBorderWidth,10)/100:this.xaxisBorderWidth=parseInt(this.xaxisBorderWidth,10),this.xaxisBorderHeight=s.config.xaxis.axisBorder.height,this.yaxis=s.config.yaxis[0]}return r(t,[{key:\"drawXaxis\",value:function(){var t=this.w,e=new m(this.ctx),i=e.group({class:\"apexcharts-xaxis\",transform:\"translate(\".concat(t.config.xaxis.offsetX,\", \").concat(t.config.xaxis.offsetY,\")\")}),a=e.group({class:\"apexcharts-xaxis-texts-g\",transform:\"translate(\".concat(t.globals.translateXAxisX,\", \").concat(t.globals.translateXAxisY,\")\")});i.add(a);for(var s=[],r=0;r<this.xaxisLabels.length;r++)s.push(this.xaxisLabels[r]);if(this.drawXAxisLabelAndGroup(!0,e,a,s,t.globals.isXNumeric,(function(t,e){return e})),t.globals.hasGroups){var o=t.globals.groups;s=[];for(var n=0;n<o.length;n++)s.push(o[n].title);var l={};t.config.xaxis.group.style&&(l.xaxisFontSize=t.config.xaxis.group.style.fontSize,l.xaxisFontFamily=t.config.xaxis.group.style.fontFamily,l.xaxisForeColors=t.config.xaxis.group.style.colors,l.fontWeight=t.config.xaxis.group.style.fontWeight,l.cssClass=t.config.xaxis.group.style.cssClass),this.drawXAxisLabelAndGroup(!1,e,a,s,!1,(function(t,e){return o[t].cols*e}),l)}if(void 0!==t.config.xaxis.title.text){var h=e.group({class:\"apexcharts-xaxis-title\"}),c=e.drawText({x:t.globals.gridWidth/2+t.config.xaxis.title.offsetX,y:this.offY+parseFloat(this.xaxisFontSize)+(\"bottom\"===t.config.xaxis.position?t.globals.xAxisLabelsHeight:-t.globals.xAxisLabelsHeight-10)+t.config.xaxis.title.offsetY,text:t.config.xaxis.title.text,textAnchor:\"middle\",fontSize:t.config.xaxis.title.style.fontSize,fontFamily:t.config.xaxis.title.style.fontFamily,fontWeight:t.config.xaxis.title.style.fontWeight,foreColor:t.config.xaxis.title.style.color,cssClass:\"apexcharts-xaxis-title-text \"+t.config.xaxis.title.style.cssClass});h.add(c),i.add(h)}if(t.config.xaxis.axisBorder.show){var d=t.globals.barPadForNumericAxis,g=e.drawLine(t.globals.padHorizontal+t.config.xaxis.axisBorder.offsetX-d,this.offY,this.xaxisBorderWidth+d,this.offY,t.config.xaxis.axisBorder.color,0,this.xaxisBorderHeight);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(g):i.add(g)}return i}},{key:\"drawXAxisLabelAndGroup\",value:function(t,e,i,a,s,r){var o,n=this,l=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{},h=[],c=[],d=this.w,g=l.xaxisFontSize||this.xaxisFontSize,u=l.xaxisFontFamily||this.xaxisFontFamily,f=l.xaxisForeColors||this.xaxisForeColors,p=l.fontWeight||d.config.xaxis.labels.style.fontWeight,x=l.cssClass||d.config.xaxis.labels.style.cssClass,b=d.globals.padHorizontal,v=a.length,m=\"category\"===d.config.xaxis.type?d.globals.dataPoints:v;if(0===m&&v>m&&(m=v),s){var y=m>1?m-1:m;o=d.globals.gridWidth/y,b=b+r(0,o)/2+d.config.xaxis.labels.offsetX}else o=d.globals.gridWidth/m,b=b+r(0,o)+d.config.xaxis.labels.offsetX;for(var w=function(s){var l=b-r(s,o)/2+d.config.xaxis.labels.offsetX;0===s&&1===v&&o/2===b&&1===m&&(l=d.globals.gridWidth/2);var y=n.axesUtils.getLabel(a,d.globals.timescaleLabels,l,s,h,g,t),w=28;d.globals.rotateXLabels&&t&&(w=22),d.config.xaxis.title.text&&\"top\"===d.config.xaxis.position&&(w+=parseFloat(d.config.xaxis.title.style.fontSize)+2),t||(w=w+parseFloat(g)+(d.globals.xAxisLabelsHeight-d.globals.xAxisGroupLabelsHeight)+(d.globals.rotateXLabels?10:0)),y=void 0!==d.config.xaxis.tickAmount&&\"dataPoints\"!==d.config.xaxis.tickAmount&&\"datetime\"!==d.config.xaxis.type?n.axesUtils.checkLabelBasedOnTickamount(s,y,v):n.axesUtils.checkForOverflowingLabels(s,y,v,h,c);if(d.config.xaxis.labels.show){var k=e.drawText({x:y.x,y:n.offY+d.config.xaxis.labels.offsetY+w-(\"top\"===d.config.xaxis.position?d.globals.xAxisHeight+d.config.xaxis.axisTicks.height-2:0),text:y.text,textAnchor:\"middle\",fontWeight:y.isBold?600:p,fontSize:g,fontFamily:u,foreColor:Array.isArray(f)?t&&d.config.xaxis.convertedCatToNumeric?f[d.globals.minX+s-1]:f[s]:f,isPlainText:!1,cssClass:(t?\"apexcharts-xaxis-label \":\"apexcharts-xaxis-group-label \")+x});if(i.add(k),k.on(\"click\",(function(t){if(\"function\"==typeof d.config.chart.events.xAxisLabelClick){var e=Object.assign({},d,{labelIndex:s});d.config.chart.events.xAxisLabelClick(t,n.ctx,e)}})),t){var A=document.createElementNS(d.globals.SVGNS,\"title\");A.textContent=Array.isArray(y.text)?y.text.join(\" \"):y.text,k.node.appendChild(A),\"\"!==y.text&&(h.push(y.text),c.push(y))}}s<v-1&&(b+=r(s+1,o))},k=0;k<=v-1;k++)w(k)}},{key:\"drawXaxisInversed\",value:function(t){var e,i,a=this,s=this.w,r=new m(this.ctx),o=s.config.yaxis[0].opposite?s.globals.translateYAxisX[t]:0,n=r.group({class:\"apexcharts-yaxis apexcharts-xaxis-inversed\",rel:t}),l=r.group({class:\"apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g\",transform:\"translate(\"+o+\", 0)\"});n.add(l);var h=[];if(s.config.yaxis[t].show)for(var c=0;c<this.xaxisLabels.length;c++)h.push(this.xaxisLabels[c]);e=s.globals.gridHeight/h.length,i=-e/2.2;var d=s.globals.yLabelFormatters[0],g=s.config.yaxis[0].labels;if(g.show)for(var u=function(o){var n=void 0===h[o]?\"\":h[o];n=d(n,{seriesIndex:t,dataPointIndex:o,w:s});var c=a.axesUtils.getYAxisForeColor(g.style.colors,t),u=0;Array.isArray(n)&&(u=n.length/2*parseInt(g.style.fontSize,10));var f=g.offsetX-15,p=\"end\";a.yaxis.opposite&&(p=\"start\"),\"left\"===s.config.yaxis[0].labels.align?(f=g.offsetX,p=\"start\"):\"center\"===s.config.yaxis[0].labels.align?(f=g.offsetX,p=\"middle\"):\"right\"===s.config.yaxis[0].labels.align&&(p=\"end\");var x=r.drawText({x:f,y:i+e+g.offsetY-u,text:n,textAnchor:p,foreColor:Array.isArray(c)?c[o]:c,fontSize:g.style.fontSize,fontFamily:g.style.fontFamily,fontWeight:g.style.fontWeight,isPlainText:!1,cssClass:\"apexcharts-yaxis-label \"+g.style.cssClass,maxWidth:g.maxWidth});l.add(x),x.on(\"click\",(function(t){if(\"function\"==typeof s.config.chart.events.xAxisLabelClick){var e=Object.assign({},s,{labelIndex:o});s.config.chart.events.xAxisLabelClick(t,a.ctx,e)}}));var b=document.createElementNS(s.globals.SVGNS,\"title\");if(b.textContent=Array.isArray(n)?n.join(\" \"):n,x.node.appendChild(b),0!==s.config.yaxis[t].labels.rotate){var v=r.rotateAroundCenter(x.node);x.node.setAttribute(\"transform\",\"rotate(\".concat(s.config.yaxis[t].labels.rotate,\" 0 \").concat(v.y,\")\"))}i+=e},f=0;f<=h.length-1;f++)u(f);if(void 0!==s.config.yaxis[0].title.text){var p=r.group({class:\"apexcharts-yaxis-title apexcharts-xaxis-title-inversed\",transform:\"translate(\"+o+\", 0)\"}),x=r.drawText({x:s.config.yaxis[0].title.offsetX,y:s.globals.gridHeight/2+s.config.yaxis[0].title.offsetY,text:s.config.yaxis[0].title.text,textAnchor:\"middle\",foreColor:s.config.yaxis[0].title.style.color,fontSize:s.config.yaxis[0].title.style.fontSize,fontWeight:s.config.yaxis[0].title.style.fontWeight,fontFamily:s.config.yaxis[0].title.style.fontFamily,cssClass:\"apexcharts-yaxis-title-text \"+s.config.yaxis[0].title.style.cssClass});p.add(x),n.add(p)}var b=0;this.isCategoryBarHorizontal&&s.config.yaxis[0].opposite&&(b=s.globals.gridWidth);var v=s.config.xaxis.axisBorder;if(v.show){var y=r.drawLine(s.globals.padHorizontal+v.offsetX+b,1+v.offsetY,s.globals.padHorizontal+v.offsetX+b,s.globals.gridHeight+v.offsetY,v.color,0);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(y):n.add(y)}return s.config.yaxis[0].axisTicks.show&&this.axesUtils.drawYAxisTicks(b,h.length,s.config.yaxis[0].axisBorder,s.config.yaxis[0].axisTicks,0,e,n),n}},{key:\"drawXaxisTicks\",value:function(t,e,i){var a=this.w,s=t;if(!(t<0||t-2>a.globals.gridWidth)){var r=this.offY+a.config.xaxis.axisTicks.offsetY;if(e=e+r+a.config.xaxis.axisTicks.height,\"top\"===a.config.xaxis.position&&(e=r-a.config.xaxis.axisTicks.height),a.config.xaxis.axisTicks.show){var o=new m(this.ctx).drawLine(t+a.config.xaxis.axisTicks.offsetX,r+a.config.xaxis.offsetY,s+a.config.xaxis.axisTicks.offsetX,e+a.config.xaxis.offsetY,a.config.xaxis.axisTicks.color);i.add(o),o.node.classList.add(\"apexcharts-xaxis-tick\")}}}},{key:\"getXAxisTicksPositions\",value:function(){var t=this.w,e=[],i=this.xaxisLabels.length,a=t.globals.padHorizontal;if(t.globals.timescaleLabels.length>0)for(var s=0;s<i;s++)a=this.xaxisLabels[s].position,e.push(a);else for(var r=i,o=0;o<r;o++){var n=r;t.globals.isXNumeric&&\"bar\"!==t.config.chart.type&&(n-=1),a+=t.globals.gridWidth/n,e.push(a)}return e}},{key:\"xAxisLabelCorrections\",value:function(){var t=this.w,e=new m(this.ctx),i=t.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-texts-g\"),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-xaxis-texts-g text:not(.apexcharts-xaxis-group-label)\"),s=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis-inversed text\"),r=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-xaxis-inversed-texts-g text tspan\");if(t.globals.rotateXLabels||t.config.xaxis.labels.rotateAlways)for(var o=0;o<a.length;o++){var n=e.rotateAroundCenter(a[o]);n.y=n.y-1,n.x=n.x+1,a[o].setAttribute(\"transform\",\"rotate(\".concat(t.config.xaxis.labels.rotate,\" \").concat(n.x,\" \").concat(n.y,\")\")),a[o].setAttribute(\"text-anchor\",\"end\");i.setAttribute(\"transform\",\"translate(0, \".concat(-10,\")\"));var l=a[o].childNodes;t.config.xaxis.labels.trim&&Array.prototype.forEach.call(l,(function(i){e.placeTextWithEllipsis(i,i.textContent,t.globals.xAxisLabelsHeight-(\"bottom\"===t.config.legend.position?20:10))}))}else!function(){for(var i=t.globals.gridWidth/(t.globals.labels.length+1),s=0;s<a.length;s++){var r=a[s].childNodes;t.config.xaxis.labels.trim&&\"datetime\"!==t.config.xaxis.type&&Array.prototype.forEach.call(r,(function(t){e.placeTextWithEllipsis(t,t.textContent,i)}))}}();if(s.length>0){var h=s[s.length-1].getBBox(),c=s[0].getBBox();h.x<-20&&s[s.length-1].parentNode.removeChild(s[s.length-1]),c.x+c.width>t.globals.gridWidth&&!t.globals.isBarHorizontal&&s[0].parentNode.removeChild(s[0]);for(var d=0;d<r.length;d++)e.placeTextWithEllipsis(r[d],r[d].textContent,t.config.yaxis[0].labels.maxWidth-(t.config.yaxis[0].title.text?2*parseFloat(t.config.yaxis[0].title.style.fontSize):0)-15)}}}]),t}(),j=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.xaxisLabels=i.globals.labels.slice(),this.axesUtils=new B(e),this.isRangeBar=i.globals.seriesRange.length,i.globals.timescaleLabels.length>0&&(this.xaxisLabels=i.globals.timescaleLabels.slice())}return r(t,[{key:\"drawGridArea\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=new m(this.ctx);null===t&&(t=i.group({class:\"apexcharts-grid\"}));var a=i.drawLine(e.globals.padHorizontal,1,e.globals.padHorizontal,e.globals.gridHeight,\"transparent\"),s=i.drawLine(e.globals.padHorizontal,e.globals.gridHeight,e.globals.gridWidth,e.globals.gridHeight,\"transparent\");return t.add(s),t.add(a),t}},{key:\"drawGrid\",value:function(){var t=null;return this.w.globals.axisCharts&&(t=this.renderGrid(),this.drawGridArea(t.el)),t}},{key:\"createGridMask\",value:function(){var t=this.w,e=t.globals,i=new m(this.ctx),a=Array.isArray(t.config.stroke.width)?0:t.config.stroke.width;if(Array.isArray(t.config.stroke.width)){var s=0;t.config.stroke.width.forEach((function(t){s=Math.max(s,t)})),a=s}e.dom.elGridRectMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elGridRectMask.setAttribute(\"id\",\"gridRectMask\".concat(e.cuid)),e.dom.elGridRectMarkerMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elGridRectMarkerMask.setAttribute(\"id\",\"gridRectMarkerMask\".concat(e.cuid)),e.dom.elForecastMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elForecastMask.setAttribute(\"id\",\"forecastMask\".concat(e.cuid)),e.dom.elNonForecastMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elNonForecastMask.setAttribute(\"id\",\"nonForecastMask\".concat(e.cuid));var r=t.config.chart.type,o=0,n=0;(\"bar\"===r||\"rangeBar\"===r||\"candlestick\"===r||\"boxPlot\"===r||t.globals.comboBarCount>0)&&t.globals.isXNumeric&&!t.globals.isBarHorizontal&&(o=t.config.grid.padding.left,n=t.config.grid.padding.right,e.barPadForNumericAxis>o&&(o=e.barPadForNumericAxis,n=e.barPadForNumericAxis)),e.dom.elGridRect=i.drawRect(-a/2-o-2,-a/2,e.gridWidth+a+n+o+4,e.gridHeight+a,0,\"#fff\");var l=t.globals.markers.largestSize+1;e.dom.elGridRectMarker=i.drawRect(2*-l,2*-l,e.gridWidth+4*l,e.gridHeight+4*l,0,\"#fff\"),e.dom.elGridRectMask.appendChild(e.dom.elGridRect.node),e.dom.elGridRectMarkerMask.appendChild(e.dom.elGridRectMarker.node);var h=e.dom.baseEl.querySelector(\"defs\");h.appendChild(e.dom.elGridRectMask),h.appendChild(e.dom.elForecastMask),h.appendChild(e.dom.elNonForecastMask),h.appendChild(e.dom.elGridRectMarkerMask)}},{key:\"_drawGridLines\",value:function(t){var e=t.i,i=t.x1,a=t.y1,s=t.x2,r=t.y2,o=t.xCount,n=t.parent,l=this.w;if(!(0===e&&l.globals.skipFirstTimelinelabel||e===o-1&&l.globals.skipLastTimelinelabel&&!l.config.xaxis.labels.formatter||\"radar\"===l.config.chart.type)){l.config.grid.xaxis.lines.show&&this._drawGridLine({i:e,x1:i,y1:a,x2:s,y2:r,xCount:o,parent:n});var h=0;if(l.globals.hasGroups&&\"between\"===l.config.xaxis.tickPlacement){var c=l.globals.groups;if(c){for(var d=0,g=0;d<e&&g<c.length;g++)d+=c[g].cols;d===e&&(h=.6*l.globals.xAxisLabelsHeight)}}new V(this.ctx).drawXaxisTicks(i,h,l.globals.dom.elGraphical)}}},{key:\"_drawGridLine\",value:function(t){var e=t.i,i=t.x1,a=t.y1,s=t.x2,r=t.y2,o=t.xCount,n=t.parent,l=this.w,h=!1,c=n.node.classList.contains(\"apexcharts-gridlines-horizontal\"),d=l.config.grid.strokeDashArray,g=l.globals.barPadForNumericAxis;(0===a&&0===r||0===i&&0===s)&&(h=!0),a===l.globals.gridHeight&&r===l.globals.gridHeight&&(h=!0),!l.globals.isBarHorizontal||0!==e&&e!==o-1||(h=!0);var u=new m(this).drawLine(i-(c?g:0),a,s+(c?g:0),r,l.config.grid.borderColor,d);u.node.classList.add(\"apexcharts-gridline\"),h?this.elGridBorders.add(u):n.add(u)}},{key:\"_drawGridBandRect\",value:function(t){var e=t.c,i=t.x1,a=t.y1,s=t.x2,r=t.y2,o=t.type,n=this.w,l=new m(this.ctx),h=n.globals.barPadForNumericAxis;if(\"column\"!==o||\"datetime\"!==n.config.xaxis.type){var c=n.config.grid[o].colors[e],d=l.drawRect(i-(\"row\"===o?h:0),a,s+(\"row\"===o?2*h:0),r,0,c,n.config.grid[o].opacity);this.elg.add(d),d.attr(\"clip-path\",\"url(#gridRectMask\".concat(n.globals.cuid,\")\")),d.node.classList.add(\"apexcharts-grid-\".concat(o))}}},{key:\"_drawXYLines\",value:function(t){var e=this,i=t.xCount,a=t.tickAmount,s=this.w;if(s.config.grid.xaxis.lines.show||s.config.xaxis.axisTicks.show){var r,o=s.globals.padHorizontal,n=s.globals.gridHeight;s.globals.timescaleLabels.length?function(t){for(var a=t.xC,s=t.x1,r=t.y1,o=t.x2,n=t.y2,l=0;l<a;l++)s=e.xaxisLabels[l].position,o=e.xaxisLabels[l].position,e._drawGridLines({i:l,x1:s,y1:r,x2:o,y2:n,xCount:i,parent:e.elgridLinesV})}({xC:i,x1:o,y1:0,x2:r,y2:n}):(s.globals.isXNumeric&&(i=s.globals.xAxisScale.result.length),function(t){for(var a=t.xC,r=t.x1,o=t.y1,n=t.x2,l=t.y2,h=0;h<a+(s.globals.isXNumeric?0:1);h++)0===h&&1===a&&1===s.globals.dataPoints&&(n=r=s.globals.gridWidth/2),e._drawGridLines({i:h,x1:r,y1:o,x2:n,y2:l,xCount:i,parent:e.elgridLinesV}),n=r+=s.globals.gridWidth/(s.globals.isXNumeric?a-1:a)}({xC:i,x1:o,y1:0,x2:r,y2:n}))}if(s.config.grid.yaxis.lines.show){var l=0,h=0,c=s.globals.gridWidth,d=a+1;this.isRangeBar&&(d=s.globals.labels.length);for(var g=0;g<d+(this.isRangeBar?1:0);g++)this._drawGridLine({i:g,xCount:d+(this.isRangeBar?1:0),x1:0,y1:l,x2:c,y2:h,parent:this.elgridLinesH}),h=l+=s.globals.gridHeight/(this.isRangeBar?d:a)}}},{key:\"_drawInvertedXYLines\",value:function(t){var e=t.xCount,i=this.w;if(i.config.grid.xaxis.lines.show||i.config.xaxis.axisTicks.show)for(var a,s=i.globals.padHorizontal,r=i.globals.gridHeight,o=0;o<e+1;o++){i.config.grid.xaxis.lines.show&&this._drawGridLine({i:o,xCount:e+1,x1:s,y1:0,x2:a,y2:r,parent:this.elgridLinesV}),new V(this.ctx).drawXaxisTicks(s,0,i.globals.dom.elGraphical),a=s=s+i.globals.gridWidth/e+.3}if(i.config.grid.yaxis.lines.show)for(var n=0,l=0,h=i.globals.gridWidth,c=0;c<i.globals.dataPoints+1;c++)this._drawGridLine({i:c,xCount:i.globals.dataPoints+1,x1:0,y1:n,x2:h,y2:l,parent:this.elgridLinesH}),l=n+=i.globals.gridHeight/i.globals.dataPoints}},{key:\"renderGrid\",value:function(){var t=this.w,e=new m(this.ctx);this.elg=e.group({class:\"apexcharts-grid\"}),this.elgridLinesH=e.group({class:\"apexcharts-gridlines-horizontal\"}),this.elgridLinesV=e.group({class:\"apexcharts-gridlines-vertical\"}),this.elGridBorders=e.group({class:\"apexcharts-grid-borders\"}),this.elg.add(this.elgridLinesH),this.elg.add(this.elgridLinesV),t.config.grid.show||(this.elgridLinesV.hide(),this.elgridLinesH.hide(),this.elGridBorders.hide());for(var i,a=t.globals.yAxisScale.length?t.globals.yAxisScale[0].result.length-1:5,s=0;s<t.globals.series.length&&(void 0!==t.globals.yAxisScale[s]&&(a=t.globals.yAxisScale[s].result.length-1),!(a>2));s++);return!t.globals.isBarHorizontal||this.isRangeBar?(i=this.xaxisLabels.length,this.isRangeBar&&(a=t.globals.labels.length,t.config.xaxis.tickAmount&&t.config.xaxis.labels.formatter&&(i=t.config.xaxis.tickAmount)),this._drawXYLines({xCount:i,tickAmount:a})):(i=a,a=t.globals.xTickAmount,this._drawInvertedXYLines({xCount:i,tickAmount:a})),this.drawGridBands(i,a),{el:this.elg,elGridBorders:this.elGridBorders,xAxisTickWidth:t.globals.gridWidth/i}}},{key:\"drawGridBands\",value:function(t,e){var i=this.w;if(void 0!==i.config.grid.row.colors&&i.config.grid.row.colors.length>0)for(var a=0,s=i.globals.gridHeight/e,r=i.globals.gridWidth,o=0,n=0;o<e;o++,n++)n>=i.config.grid.row.colors.length&&(n=0),this._drawGridBandRect({c:n,x1:0,y1:a,x2:r,y2:s,type:\"row\"}),a+=i.globals.gridHeight/e;if(void 0!==i.config.grid.column.colors&&i.config.grid.column.colors.length>0)for(var l=i.globals.isBarHorizontal||\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric?t:t-1,h=i.globals.padHorizontal,c=i.globals.padHorizontal+i.globals.gridWidth/l,d=i.globals.gridHeight,g=0,u=0;g<t;g++,u++)u>=i.config.grid.column.colors.length&&(u=0),this._drawGridBandRect({c:u,x1:h,y1:0,x2:c,y2:d,type:\"column\"}),h+=i.globals.gridWidth/l}}]),t}(),_=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"niceScale\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4?arguments[4]:void 0,r=this.w,o=Math.abs(e-t);if(\"dataPoints\"===(i=this._adjustTicksForSmallRange(i,a,o))&&(i=r.globals.dataPoints-1),t===Number.MIN_VALUE&&0===e||!x.isNumber(t)&&!x.isNumber(e)||t===Number.MIN_VALUE&&e===-Number.MAX_VALUE){t=0,e=i;var n=this.linearScale(t,e,i);return n}t>e?(console.warn(\"axis.min cannot be greater than axis.max\"),e=t+.1):t===e&&(t=0===t?0:t-.5,e=0===e?2:e+.5);var l=[];o<1&&s&&(\"candlestick\"===r.config.chart.type||\"candlestick\"===r.config.series[a].type||\"boxPlot\"===r.config.chart.type||\"boxPlot\"===r.config.series[a].type||r.globals.isRangeData)&&(e*=1.01);var h=i+1;h<2?h=2:h>2&&(h-=2);var c=o/h,d=Math.floor(x.log10(c)),g=Math.pow(10,d),u=Math.round(c/g);u<1&&(u=1);var f=u*g,p=f*Math.floor(t/f),b=f*Math.ceil(e/f),v=p;if(s&&o>2){for(;l.push(v),!((v+=f)>b););return{result:l,niceMin:l[0],niceMax:l[l.length-1]}}var m=t;(l=[]).push(m);for(var y=Math.abs(e-t)/i,w=0;w<=i;w++)m+=y,l.push(m);return l[l.length-2]>=e&&l.pop(),{result:l,niceMin:l[0],niceMax:l[l.length-1]}}},{key:\"linearScale\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=arguments.length>3?arguments[3]:void 0,s=Math.abs(e-t);\"dataPoints\"===(i=this._adjustTicksForSmallRange(i,a,s))&&(i=this.w.globals.dataPoints-1);var r=s/i;i===Number.MAX_VALUE&&(i=10,r=1);for(var o=[],n=t;i>=0;)o.push(n),n+=r,i-=1;return{result:o,niceMin:o[0],niceMax:o[o.length-1]}}},{key:\"logarithmicScaleNice\",value:function(t,e,i){e<=0&&(e=Math.max(t,i)),t<=0&&(t=Math.min(e,i));for(var a=[],s=Math.ceil(Math.log(e)/Math.log(i)+1),r=Math.floor(Math.log(t)/Math.log(i));r<s;r++)a.push(Math.pow(i,r));return{result:a,niceMin:a[0],niceMax:a[a.length-1]}}},{key:\"logarithmicScale\",value:function(t,e,i){e<=0&&(e=Math.max(t,i)),t<=0&&(t=Math.min(e,i));for(var a=[],s=Math.log(e)/Math.log(i),r=Math.log(t)/Math.log(i),o=s-r,n=Math.round(o),l=o/n,h=0,c=r;h<n;h++,c+=l)a.push(Math.pow(i,c));return a.push(Math.pow(i,s)),{result:a,niceMin:t,niceMax:e}}},{key:\"_adjustTicksForSmallRange\",value:function(t,e,i){var a=t;if(void 0!==e&&this.w.config.yaxis[e].labels.formatter&&void 0===this.w.config.yaxis[e].tickAmount){var s=Number(this.w.config.yaxis[e].labels.formatter(1));x.isNumber(s)&&0===this.w.globals.yValueDecimal&&(a=Math.ceil(i))}return a<t?a:t}},{key:\"setYScaleForIndex\",value:function(t,e,i){var a=this.w.globals,s=this.w.config,r=a.isBarHorizontal?s.xaxis:s.yaxis[t];void 0===a.yAxisScale[t]&&(a.yAxisScale[t]=[]);var o=Math.abs(i-e);if(r.logarithmic&&o<=5&&(a.invalidLogScale=!0),r.logarithmic&&o>5)a.allSeriesCollapsed=!1,a.yAxisScale[t]=this.logarithmicScale(e,i,r.logBase),a.yAxisScale[t]=r.forceNiceScale?this.logarithmicScaleNice(e,i,r.logBase):this.logarithmicScale(e,i,r.logBase);else if(i!==-Number.MAX_VALUE&&x.isNumber(i))if(a.allSeriesCollapsed=!1,void 0===r.min&&void 0===r.max||r.forceNiceScale){var n=void 0===s.yaxis[t].max&&void 0===s.yaxis[t].min||s.yaxis[t].forceNiceScale;a.yAxisScale[t]=this.niceScale(e,i,r.tickAmount?r.tickAmount:o<5&&o>1?o+1:5,t,n)}else a.yAxisScale[t]=this.linearScale(e,i,r.tickAmount,t);else a.yAxisScale[t]=this.linearScale(0,5,5)}},{key:\"setXScale\",value:function(t,e){var i=this.w,a=i.globals,s=i.config.xaxis,r=Math.abs(e-t);return e!==-Number.MAX_VALUE&&x.isNumber(e)?a.xAxisScale=this.linearScale(t,e,s.tickAmount?s.tickAmount:r<5&&r>1?r+1:5,0):a.xAxisScale=this.linearScale(0,5,5),a.xAxisScale}},{key:\"setMultipleYScales\",value:function(){var t=this,e=this.w.globals,i=this.w.config,a=e.minYArr.concat([]),s=e.maxYArr.concat([]),r=[];i.yaxis.forEach((function(e,o){var n=o;i.series.forEach((function(t,i){t.name===e.seriesName&&(n=i,o!==i?r.push({index:i,similarIndex:o,alreadyExists:!0}):r.push({index:i}))}));var l=a[n],h=s[n];t.setYScaleForIndex(o,l,h)})),this.sameScaleInMultipleAxes(a,s,r)}},{key:\"sameScaleInMultipleAxes\",value:function(t,e,i){var a=this,s=this.w.config,r=this.w.globals,o=[];i.forEach((function(t){t.alreadyExists&&(void 0===o[t.index]&&(o[t.index]=[]),o[t.index].push(t.index),o[t.index].push(t.similarIndex))})),r.yAxisSameScaleIndices=o,o.forEach((function(t,e){o.forEach((function(i,a){var s,r;e!==a&&(s=t,r=i,s.filter((function(t){return-1!==r.indexOf(t)}))).length>0&&(o[e]=o[e].concat(o[a]))}))}));var n=o.map((function(t){return t.filter((function(e,i){return t.indexOf(e)===i}))})).map((function(t){return t.sort()}));o=o.filter((function(t){return!!t}));var l=n.slice(),h=l.map((function(t){return JSON.stringify(t)}));l=l.filter((function(t,e){return h.indexOf(JSON.stringify(t))===e}));var c=[],d=[];t.forEach((function(t,i){l.forEach((function(a,s){a.indexOf(i)>-1&&(void 0===c[s]&&(c[s]=[],d[s]=[]),c[s].push({key:i,value:t}),d[s].push({key:i,value:e[i]}))}))}));var g=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,Number.MIN_VALUE),u=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,-Number.MAX_VALUE);c.forEach((function(t,e){t.forEach((function(t,i){g[e]=Math.min(t.value,g[e])}))})),d.forEach((function(t,e){t.forEach((function(t,i){u[e]=Math.max(t.value,u[e])}))})),t.forEach((function(t,e){d.forEach((function(t,i){var o=g[i],n=u[i];s.chart.stacked&&(n=0,t.forEach((function(t,e){t.value!==-Number.MAX_VALUE&&(n+=t.value),o!==Number.MIN_VALUE&&(o+=c[i][e].value)}))),t.forEach((function(i,l){t[l].key===e&&(void 0!==s.yaxis[e].min&&(o=\"function\"==typeof s.yaxis[e].min?s.yaxis[e].min(r.minY):s.yaxis[e].min),void 0!==s.yaxis[e].max&&(n=\"function\"==typeof s.yaxis[e].max?s.yaxis[e].max(r.maxY):s.yaxis[e].max),a.setYScaleForIndex(e,o,n))}))}))}))}},{key:\"autoScaleY\",value:function(t,e,i){t||(t=this);var a=t.w;if(a.globals.isMultipleYAxis||a.globals.collapsedSeries.length)return console.warn(\"autoScaleYaxis is not supported in a multi-yaxis chart.\"),e;var s=a.globals.seriesX[0],r=a.config.chart.stacked;return e.forEach((function(t,o){for(var n=0,l=0;l<s.length;l++)if(s[l]>=i.xaxis.min){n=l;break}var h,c,d=a.globals.minYArr[o],g=a.globals.maxYArr[o],u=a.globals.stackedSeriesTotals;a.globals.series.forEach((function(o,l){var f=o[n];r?(f=u[n],h=c=f,u.forEach((function(t,e){s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min&&(t>c&&null!==t&&(c=t),o[e]<h&&null!==o[e]&&(h=o[e]))}))):(h=c=f,o.forEach((function(t,e){if(s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min){var r=t,o=t;a.globals.series.forEach((function(i,a){null!==t&&(r=Math.min(i[e],r),o=Math.max(i[e],o))})),o>c&&null!==o&&(c=o),r<h&&null!==r&&(h=r)}}))),void 0===h&&void 0===c&&(h=d,c=g),c*=c<0?.9:1.1,0===(h*=h<0?1.1:.9)&&0===c&&(h=-1,c=1),c<0&&c<g&&(c=g),h<0&&h>d&&(h=d),e.length>1?(e[l].min=void 0===t.min?h:t.min,e[l].max=void 0===t.max?c:t.max):(e[0].min=void 0===t.min?h:t.min,e[0].max=void 0===t.max?c:t.max)}))})),e}}]),t}(),U=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.scales=new _(e)}return r(t,[{key:\"init\",value:function(){this.setYRange(),this.setXRange(),this.setZRange()}},{key:\"getMinYMaxY\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-Number.MAX_VALUE,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w.config,r=this.w.globals,o=-Number.MAX_VALUE,n=Number.MIN_VALUE;null===a&&(a=t+1);var l=r.series,h=l,c=l;\"candlestick\"===s.chart.type?(h=r.seriesCandleL,c=r.seriesCandleH):\"boxPlot\"===s.chart.type?(h=r.seriesCandleO,c=r.seriesCandleC):r.isRangeData&&(h=r.seriesRangeStart,c=r.seriesRangeEnd);for(var d=t;d<a;d++){r.dataPoints=Math.max(r.dataPoints,l[d].length),r.categoryLabels.length&&(r.dataPoints=r.categoryLabels.filter((function(t){return void 0!==t})).length);for(var g=0;g<r.series[d].length;g++){var u=l[d][g];null!==u&&x.isNumber(u)?(void 0!==c[d][g]&&(o=Math.max(o,c[d][g]),e=Math.min(e,c[d][g])),void 0!==h[d][g]&&(e=Math.min(e,h[d][g]),i=Math.max(i,h[d][g])),\"candlestick\"!==this.w.config.chart.type&&\"boxPlot\"!==this.w.config.chart.type&&\"rangeArea\"===this.w.config.chart.type&&\"rangeBar\"===this.w.config.chart.type||(\"candlestick\"!==this.w.config.chart.type&&\"boxPlot\"!==this.w.config.chart.type||void 0!==r.seriesCandleC[d][g]&&(o=Math.max(o,r.seriesCandleO[d][g]),o=Math.max(o,r.seriesCandleH[d][g]),o=Math.max(o,r.seriesCandleL[d][g]),o=Math.max(o,r.seriesCandleC[d][g]),\"boxPlot\"===this.w.config.chart.type&&(o=Math.max(o,r.seriesCandleM[d][g]))),!s.series[d].type||\"candlestick\"===s.series[d].type&&\"boxPlot\"===s.series[d].type&&\"rangeArea\"===s.series[d].type&&\"rangeBar\"===s.series[d].type||(o=Math.max(o,r.series[d][g]),e=Math.min(e,r.series[d][g])),i=o),r.seriesGoals[d]&&r.seriesGoals[d][g]&&Array.isArray(r.seriesGoals[d][g])&&r.seriesGoals[d][g].forEach((function(t){n!==Number.MIN_VALUE&&(n=Math.min(n,t.value),e=n),o=Math.max(o,t.value),i=o})),x.isFloat(u)&&(u=x.noExponents(u),r.yValueDecimal=Math.max(r.yValueDecimal,u.toString().split(\".\")[1].length)),n>h[d][g]&&h[d][g]<0&&(n=h[d][g])):r.hasNullValues=!0}}return\"rangeBar\"===s.chart.type&&r.seriesRangeStart.length&&r.isBarHorizontal&&(n=e),\"bar\"===s.chart.type&&(n<0&&o<0&&(o=0),n===Number.MIN_VALUE&&(n=0)),{minY:n,maxY:o,lowestY:e,highestY:i}}},{key:\"setYRange\",value:function(){var t=this.w.globals,e=this.w.config;t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE;var i=Number.MAX_VALUE;if(t.isMultipleYAxis)for(var a=0;a<t.series.length;a++){var s=this.getMinYMaxY(a,i,null,a+1);t.minYArr.push(s.minY),t.maxYArr.push(s.maxY),i=s.lowestY}var r=this.getMinYMaxY(0,i,null,t.series.length);if(t.minY=r.minY,t.maxY=r.maxY,i=r.lowestY,e.chart.stacked&&this._setStackedMinMax(),(\"line\"===e.chart.type||\"area\"===e.chart.type||\"candlestick\"===e.chart.type||\"boxPlot\"===e.chart.type||\"rangeBar\"===e.chart.type&&!t.isBarHorizontal)&&t.minY===Number.MIN_VALUE&&i!==-Number.MAX_VALUE&&i!==t.maxY){var o=t.maxY-i;(i>=0&&i<=10||void 0!==e.yaxis[0].min||void 0!==e.yaxis[0].max)&&(o=0),t.minY=i-5*o/100,i>0&&t.minY<0&&(t.minY=0),t.maxY=t.maxY+5*o/100}if(e.yaxis.forEach((function(e,i){void 0!==e.max&&(\"number\"==typeof e.max?t.maxYArr[i]=e.max:\"function\"==typeof e.max&&(t.maxYArr[i]=e.max(t.isMultipleYAxis?t.maxYArr[i]:t.maxY)),t.maxY=t.maxYArr[i]),void 0!==e.min&&(\"number\"==typeof e.min?t.minYArr[i]=e.min:\"function\"==typeof e.min&&(t.minYArr[i]=e.min(t.isMultipleYAxis?t.minYArr[i]===Number.MIN_VALUE?0:t.minYArr[i]:t.minY)),t.minY=t.minYArr[i])})),t.isBarHorizontal){[\"min\",\"max\"].forEach((function(i){void 0!==e.xaxis[i]&&\"number\"==typeof e.xaxis[i]&&(\"min\"===i?t.minY=e.xaxis[i]:t.maxY=e.xaxis[i])}))}return t.isMultipleYAxis?(this.scales.setMultipleYScales(),t.minY=i,t.yAxisScale.forEach((function(e,i){t.minYArr[i]=e.niceMin,t.maxYArr[i]=e.niceMax}))):(this.scales.setYScaleForIndex(0,t.minY,t.maxY),t.minY=t.yAxisScale[0].niceMin,t.maxY=t.yAxisScale[0].niceMax,t.minYArr[0]=t.yAxisScale[0].niceMin,t.maxYArr[0]=t.yAxisScale[0].niceMax),{minY:t.minY,maxY:t.maxY,minYArr:t.minYArr,maxYArr:t.maxYArr,yAxisScale:t.yAxisScale}}},{key:\"setXRange\",value:function(){var t=this.w.globals,e=this.w.config,i=\"numeric\"===e.xaxis.type||\"datetime\"===e.xaxis.type||\"category\"===e.xaxis.type&&!t.noLabelsProvided||t.noLabelsProvided||t.isXNumeric;if(t.isXNumeric&&function(){for(var e=0;e<t.series.length;e++)if(t.labels[e])for(var i=0;i<t.labels[e].length;i++)null!==t.labels[e][i]&&x.isNumber(t.labels[e][i])&&(t.maxX=Math.max(t.maxX,t.labels[e][i]),t.initialMaxX=Math.max(t.maxX,t.labels[e][i]),t.minX=Math.min(t.minX,t.labels[e][i]),t.initialMinX=Math.min(t.minX,t.labels[e][i]))}(),t.noLabelsProvided&&0===e.xaxis.categories.length&&(t.maxX=t.labels[t.labels.length-1],t.initialMaxX=t.labels[t.labels.length-1],t.minX=1,t.initialMinX=1),t.isXNumeric||t.noLabelsProvided||t.dataFormatXNumeric){var a;if(void 0===e.xaxis.tickAmount?(a=Math.round(t.svgWidth/150),\"numeric\"===e.xaxis.type&&t.dataPoints<30&&(a=t.dataPoints-1),a>t.dataPoints&&0!==t.dataPoints&&(a=t.dataPoints-1)):\"dataPoints\"===e.xaxis.tickAmount?(t.series.length>1&&(a=t.series[t.maxValsInArrayIndex].length-1),t.isXNumeric&&(a=t.maxX-t.minX-1)):a=e.xaxis.tickAmount,t.xTickAmount=a,void 0!==e.xaxis.max&&\"number\"==typeof e.xaxis.max&&(t.maxX=e.xaxis.max),void 0!==e.xaxis.min&&\"number\"==typeof e.xaxis.min&&(t.minX=e.xaxis.min),void 0!==e.xaxis.range&&(t.minX=t.maxX-e.xaxis.range),t.minX!==Number.MAX_VALUE&&t.maxX!==-Number.MAX_VALUE)if(e.xaxis.convertedCatToNumeric&&!t.dataFormatXNumeric){for(var s=[],r=t.minX-1;r<t.maxX;r++)s.push(r+1);t.xAxisScale={result:s,niceMin:s[0],niceMax:s[s.length-1]}}else t.xAxisScale=this.scales.setXScale(t.minX,t.maxX);else t.xAxisScale=this.scales.linearScale(1,a,a),t.noLabelsProvided&&t.labels.length>0&&(t.xAxisScale=this.scales.linearScale(1,t.labels.length,a-1),t.seriesX=t.labels.slice());i&&(t.labels=t.xAxisScale.result.slice())}return t.isBarHorizontal&&t.labels.length&&(t.xTickAmount=t.labels.length),this._handleSingleDataPoint(),this._getMinXDiff(),{minX:t.minX,maxX:t.maxX}}},{key:\"setZRange\",value:function(){var t=this.w.globals;if(t.isDataXYZ)for(var e=0;e<t.series.length;e++)if(void 0!==t.seriesZ[e])for(var i=0;i<t.seriesZ[e].length;i++)null!==t.seriesZ[e][i]&&x.isNumber(t.seriesZ[e][i])&&(t.maxZ=Math.max(t.maxZ,t.seriesZ[e][i]),t.minZ=Math.min(t.minZ,t.seriesZ[e][i]))}},{key:\"_handleSingleDataPoint\",value:function(){var t=this.w.globals,e=this.w.config;if(t.minX===t.maxX){var i=new T(this.ctx);if(\"datetime\"===e.xaxis.type){var a=i.getDate(t.minX);e.xaxis.labels.datetimeUTC?a.setUTCDate(a.getUTCDate()-2):a.setDate(a.getDate()-2),t.minX=new Date(a).getTime();var s=i.getDate(t.maxX);e.xaxis.labels.datetimeUTC?s.setUTCDate(s.getUTCDate()+2):s.setDate(s.getDate()+2),t.maxX=new Date(s).getTime()}else(\"numeric\"===e.xaxis.type||\"category\"===e.xaxis.type&&!t.noLabelsProvided)&&(t.minX=t.minX-2,t.initialMinX=t.minX,t.maxX=t.maxX+2,t.initialMaxX=t.maxX)}}},{key:\"_getMinXDiff\",value:function(){var t=this.w.globals;t.isXNumeric&&t.seriesX.forEach((function(e,i){1===e.length&&e.push(t.seriesX[t.maxValsInArrayIndex][t.seriesX[t.maxValsInArrayIndex].length-1]);var a=e.slice();a.sort((function(t,e){return t-e})),a.forEach((function(e,i){if(i>0){var s=e-a[i-1];s>0&&(t.minXDiff=Math.min(s,t.minXDiff))}})),1!==t.dataPoints&&t.minXDiff!==Number.MAX_VALUE||(t.minXDiff=.5)}))}},{key:\"_setStackedMinMax\",value:function(){var t=this.w.globals,e=[],i=[];if(t.series.length)for(var a=0;a<t.series[t.maxValsInArrayIndex].length;a++)for(var s=0,r=0,o=0;o<t.series.length;o++)null!==t.series[o][a]&&x.isNumber(t.series[o][a])&&(t.series[o][a]>0?s=s+parseFloat(t.series[o][a])+1e-4:r+=parseFloat(t.series[o][a])),o===t.series.length-1&&(e.push(s),i.push(r));for(var n=0;n<e.length;n++)t.maxY=Math.max(t.maxY,e[n]),t.minY=Math.min(t.minY,i[n])}}]),t}(),q=function(){function t(e,i){a(this,t),this.ctx=e,this.elgrid=i,this.w=e.w;var s=this.w;this.xaxisFontSize=s.config.xaxis.labels.style.fontSize,this.axisFontFamily=s.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=s.config.xaxis.labels.style.colors,this.isCategoryBarHorizontal=\"bar\"===s.config.chart.type&&s.config.plotOptions.bar.horizontal,this.xAxisoffX=0,\"bottom\"===s.config.xaxis.position&&(this.xAxisoffX=s.globals.gridHeight),this.drawnLabels=[],this.axesUtils=new B(e)}return r(t,[{key:\"drawYaxis\",value:function(t){var e=this,i=this.w,a=new m(this.ctx),s=i.config.yaxis[t].labels.style,r=s.fontSize,o=s.fontFamily,n=s.fontWeight,l=a.group({class:\"apexcharts-yaxis\",rel:t,transform:\"translate(\"+i.globals.translateYAxisX[t]+\", 0)\"});if(this.axesUtils.isYAxisHidden(t))return l;var h=a.group({class:\"apexcharts-yaxis-texts-g\"});l.add(h);var c=i.globals.yAxisScale[t].result.length-1,d=i.globals.gridHeight/c,g=i.globals.translateY,u=i.globals.yLabelFormatters[t],f=i.globals.yAxisScale[t].result.slice();f=this.axesUtils.checkForReversedLabels(t,f);var p=\"\";if(i.config.yaxis[t].labels.show)for(var x=function(l){var x=f[l];x=u(x,l,i);var b=i.config.yaxis[t].labels.padding;i.config.yaxis[t].opposite&&0!==i.config.yaxis.length&&(b*=-1);var v=\"end\";i.config.yaxis[t].opposite&&(v=\"start\"),\"left\"===i.config.yaxis[t].labels.align?v=\"start\":\"center\"===i.config.yaxis[t].labels.align?v=\"middle\":\"right\"===i.config.yaxis[t].labels.align&&(v=\"end\");var m=e.axesUtils.getYAxisForeColor(s.colors,t),y=a.drawText({x:b,y:g+c/10+i.config.yaxis[t].labels.offsetY+1,text:x,textAnchor:v,fontSize:r,fontFamily:o,fontWeight:n,maxWidth:i.config.yaxis[t].labels.maxWidth,foreColor:Array.isArray(m)?m[l]:m,isPlainText:!1,cssClass:\"apexcharts-yaxis-label \"+s.cssClass});l===c&&(p=y),h.add(y);var w=document.createElementNS(i.globals.SVGNS,\"title\");if(w.textContent=Array.isArray(x)?x.join(\" \"):x,y.node.appendChild(w),0!==i.config.yaxis[t].labels.rotate){var k=a.rotateAroundCenter(p.node),A=a.rotateAroundCenter(y.node);y.node.setAttribute(\"transform\",\"rotate(\".concat(i.config.yaxis[t].labels.rotate,\" \").concat(k.x,\" \").concat(A.y,\")\"))}g+=d},b=c;b>=0;b--)x(b);if(void 0!==i.config.yaxis[t].title.text){var v=a.group({class:\"apexcharts-yaxis-title\"}),y=0;i.config.yaxis[t].opposite&&(y=i.globals.translateYAxisX[t]);var w=a.drawText({x:y,y:i.globals.gridHeight/2+i.globals.translateY+i.config.yaxis[t].title.offsetY,text:i.config.yaxis[t].title.text,textAnchor:\"end\",foreColor:i.config.yaxis[t].title.style.color,fontSize:i.config.yaxis[t].title.style.fontSize,fontWeight:i.config.yaxis[t].title.style.fontWeight,fontFamily:i.config.yaxis[t].title.style.fontFamily,cssClass:\"apexcharts-yaxis-title-text \"+i.config.yaxis[t].title.style.cssClass});v.add(w),l.add(v)}var k=i.config.yaxis[t].axisBorder,A=31+k.offsetX;if(i.config.yaxis[t].opposite&&(A=-31-k.offsetX),k.show){var S=a.drawLine(A,i.globals.translateY+k.offsetY-2,A,i.globals.gridHeight+i.globals.translateY+k.offsetY+2,k.color,0,k.width);l.add(S)}return i.config.yaxis[t].axisTicks.show&&this.axesUtils.drawYAxisTicks(A,c,k,i.config.yaxis[t].axisTicks,t,d,l),l}},{key:\"drawYaxisInversed\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-xaxis apexcharts-yaxis-inversed\"}),s=i.group({class:\"apexcharts-xaxis-texts-g\",transform:\"translate(\".concat(e.globals.translateXAxisX,\", \").concat(e.globals.translateXAxisY,\")\")});a.add(s);var r=e.globals.yAxisScale[t].result.length-1,o=e.globals.gridWidth/r+.1,n=o+e.config.xaxis.labels.offsetX,l=e.globals.xLabelFormatter,h=e.globals.yAxisScale[t].result.slice(),c=e.globals.timescaleLabels;c.length>0&&(this.xaxisLabels=c.slice(),r=(h=c.slice()).length),h=this.axesUtils.checkForReversedLabels(t,h);var d=c.length;if(e.config.xaxis.labels.show)for(var g=d?0:r;d?g<d:g>=0;d?g++:g--){var u=h[g];u=l(u,g,e);var f=e.globals.gridWidth+e.globals.padHorizontal-(n-o+e.config.xaxis.labels.offsetX);if(c.length){var p=this.axesUtils.getLabel(h,c,f,g,this.drawnLabels,this.xaxisFontSize);f=p.x,u=p.text,this.drawnLabels.push(p.text),0===g&&e.globals.skipFirstTimelinelabel&&(u=\"\"),g===h.length-1&&e.globals.skipLastTimelinelabel&&(u=\"\")}var x=i.drawText({x:f,y:this.xAxisoffX+e.config.xaxis.labels.offsetY+30-(\"top\"===e.config.xaxis.position?e.globals.xAxisHeight+e.config.xaxis.axisTicks.height-2:0),text:u,textAnchor:\"middle\",foreColor:Array.isArray(this.xaxisForeColors)?this.xaxisForeColors[t]:this.xaxisForeColors,fontSize:this.xaxisFontSize,fontFamily:this.xaxisFontFamily,fontWeight:e.config.xaxis.labels.style.fontWeight,isPlainText:!1,cssClass:\"apexcharts-xaxis-label \"+e.config.xaxis.labels.style.cssClass});s.add(x),x.tspan(u);var b=document.createElementNS(e.globals.SVGNS,\"title\");b.textContent=u,x.node.appendChild(b),n+=o}return this.inversedYAxisTitleText(a),this.inversedYAxisBorder(a),a}},{key:\"inversedYAxisBorder\",value:function(t){var e=this.w,i=new m(this.ctx),a=e.config.xaxis.axisBorder;if(a.show){var s=0;\"bar\"===e.config.chart.type&&e.globals.isXNumeric&&(s-=15);var r=i.drawLine(e.globals.padHorizontal+s+a.offsetX,this.xAxisoffX,e.globals.gridWidth,this.xAxisoffX,a.color,0,a.height);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(r):t.add(r)}}},{key:\"inversedYAxisTitleText\",value:function(t){var e=this.w,i=new m(this.ctx);if(void 0!==e.config.xaxis.title.text){var a=i.group({class:\"apexcharts-xaxis-title apexcharts-yaxis-title-inversed\"}),s=i.drawText({x:e.globals.gridWidth/2+e.config.xaxis.title.offsetX,y:this.xAxisoffX+parseFloat(this.xaxisFontSize)+parseFloat(e.config.xaxis.title.style.fontSize)+e.config.xaxis.title.offsetY+20,text:e.config.xaxis.title.text,textAnchor:\"middle\",fontSize:e.config.xaxis.title.style.fontSize,fontFamily:e.config.xaxis.title.style.fontFamily,fontWeight:e.config.xaxis.title.style.fontWeight,foreColor:e.config.xaxis.title.style.color,cssClass:\"apexcharts-xaxis-title-text \"+e.config.xaxis.title.style.cssClass});a.add(s),t.add(a)}}},{key:\"yAxisTitleRotate\",value:function(t,e){var i=this.w,a=new m(this.ctx),s={width:0,height:0},r={width:0,height:0},o=i.globals.dom.baseEl.querySelector(\" .apexcharts-yaxis[rel='\".concat(t,\"'] .apexcharts-yaxis-texts-g\"));null!==o&&(s=o.getBoundingClientRect());var n=i.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(t,\"'] .apexcharts-yaxis-title text\"));if(null!==n&&(r=n.getBoundingClientRect()),null!==n){var l=this.xPaddingForYAxisTitle(t,s,r,e);n.setAttribute(\"x\",l.xPos-(e?10:0))}if(null!==n){var h=a.rotateAroundCenter(n);n.setAttribute(\"transform\",\"rotate(\".concat(e?-1*i.config.yaxis[t].title.rotate:i.config.yaxis[t].title.rotate,\" \").concat(h.x,\" \").concat(h.y,\")\"))}}},{key:\"xPaddingForYAxisTitle\",value:function(t,e,i,a){var s=this.w,r=0,o=0,n=10;return void 0===s.config.yaxis[t].title.text||t<0?{xPos:o,padd:0}:(a?(o=e.width+s.config.yaxis[t].title.offsetX+i.width/2+n/2,0===(r+=1)&&(o-=n/2)):(o=-1*e.width+s.config.yaxis[t].title.offsetX+n/2+i.width/2,s.globals.isBarHorizontal&&(n=25,o=-1*e.width-s.config.yaxis[t].title.offsetX-n)),{xPos:o,padd:n})}},{key:\"setYAxisXPosition\",value:function(t,e){var i=this.w,a=0,s=0,r=18,o=1;i.config.yaxis.length>1&&(this.multipleYs=!0),i.config.yaxis.map((function(n,l){var h=i.globals.ignoreYAxisIndexes.indexOf(l)>-1||!n.show||n.floating||0===t[l].width,c=t[l].width+e[l].width;n.opposite?i.globals.isBarHorizontal?(s=i.globals.gridWidth+i.globals.translateX-1,i.globals.translateYAxisX[l]=s-n.labels.offsetX):(s=i.globals.gridWidth+i.globals.translateX+o,h||(o=o+c+20),i.globals.translateYAxisX[l]=s-n.labels.offsetX+20):(a=i.globals.translateX-r,h||(r=r+c+20),i.globals.translateYAxisX[l]=a+n.labels.offsetX)}))}},{key:\"setYAxisTextAlignments\",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-yaxis\");(e=x.listToArray(e)).forEach((function(e,i){var a=t.config.yaxis[i];if(a&&!a.floating&&void 0!==a.labels.align){var s=t.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(i,\"'] .apexcharts-yaxis-texts-g\")),r=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis[rel='\".concat(i,\"'] .apexcharts-yaxis-label\"));r=x.listToArray(r);var o=s.getBoundingClientRect();\"left\"===a.labels.align?(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"start\")})),a.opposite||s.setAttribute(\"transform\",\"translate(-\".concat(o.width,\", 0)\"))):\"center\"===a.labels.align?(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"middle\")})),s.setAttribute(\"transform\",\"translate(\".concat(o.width/2*(a.opposite?1:-1),\", 0)\"))):\"right\"===a.labels.align&&(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"end\")})),a.opposite&&s.setAttribute(\"transform\",\"translate(\".concat(o.width,\", 0)\")))}}))}}]),t}(),Z=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.documentEvent=x.bind(this.documentEvent,this)}return r(t,[{key:\"addEventListener\",value:function(t,e){var i=this.w;i.globals.events.hasOwnProperty(t)?i.globals.events[t].push(e):i.globals.events[t]=[e]}},{key:\"removeEventListener\",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){var a=i.globals.events[t].indexOf(e);-1!==a&&i.globals.events[t].splice(a,1)}}},{key:\"fireEvent\",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){e&&e.length||(e=[]);for(var a=i.globals.events[t],s=a.length,r=0;r<s;r++)a[r].apply(null,e)}}},{key:\"setupEventHandlers\",value:function(){var t=this,e=this.w,i=this.ctx,a=e.globals.dom.baseEl.querySelector(e.globals.chartClass);this.ctx.eventList.forEach((function(t){a.addEventListener(t,(function(t){var a=Object.assign({},e,{seriesIndex:e.globals.capturedSeriesIndex,dataPointIndex:e.globals.capturedDataPointIndex});\"mousemove\"===t.type||\"touchmove\"===t.type?\"function\"==typeof e.config.chart.events.mouseMove&&e.config.chart.events.mouseMove(t,i,a):\"mouseleave\"===t.type||\"touchleave\"===t.type?\"function\"==typeof e.config.chart.events.mouseLeave&&e.config.chart.events.mouseLeave(t,i,a):(\"mouseup\"===t.type&&1===t.which||\"touchend\"===t.type)&&(\"function\"==typeof e.config.chart.events.click&&e.config.chart.events.click(t,i,a),i.ctx.events.fireEvent(\"click\",[t,i,a]))}),{capture:!1,passive:!0})})),this.ctx.eventList.forEach((function(i){e.globals.dom.baseEl.addEventListener(i,t.documentEvent,{passive:!0})})),this.ctx.core.setupBrushHandler()}},{key:\"documentEvent\",value:function(t){var e=this.w,i=t.target.className;if(\"click\"===t.type){var a=e.globals.dom.baseEl.querySelector(\".apexcharts-menu\");a&&a.classList.contains(\"apexcharts-menu-open\")&&\"apexcharts-menu-icon\"!==i&&a.classList.remove(\"apexcharts-menu-open\")}e.globals.clientX=\"touchmove\"===t.type?t.touches[0].clientX:t.clientX,e.globals.clientY=\"touchmove\"===t.type?t.touches[0].clientY:t.clientY}}]),t}(),$=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"setCurrentLocaleValues\",value:function(t){var e=this.w.config.chart.locales;window.Apex.chart&&window.Apex.chart.locales&&window.Apex.chart.locales.length>0&&(e=this.w.config.chart.locales.concat(window.Apex.chart.locales));var i=e.filter((function(e){return e.name===t}))[0];if(!i)throw new Error(\"Wrong locale name provided. Please make sure you set the correct locale name in options\");var a=x.extend(C,i);this.w.globals.locale=a.options}}]),t}(),J=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"drawAxis\",value:function(t,e){var i,a,s=this,r=this.w.globals,o=this.w.config,n=new V(this.ctx,e),l=new q(this.ctx,e);r.axisCharts&&\"radar\"!==t&&(r.isBarHorizontal?(a=l.drawYaxisInversed(0),i=n.drawXaxisInversed(0),r.dom.elGraphical.add(i),r.dom.elGraphical.add(a)):(i=n.drawXaxis(),r.dom.elGraphical.add(i),o.yaxis.map((function(t,e){if(-1===r.ignoreYAxisIndexes.indexOf(e)&&(a=l.drawYaxis(e),r.dom.Paper.add(a),\"back\"===s.w.config.grid.position)){var i=r.dom.Paper.children()[1];i.remove(),r.dom.Paper.add(i)}}))))}}]),t}(),Q=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"drawXCrosshairs\",value:function(){var t=this.w,e=new m(this.ctx),i=new v(this.ctx),a=t.config.xaxis.crosshairs.fill.gradient,s=t.config.xaxis.crosshairs.dropShadow,r=t.config.xaxis.crosshairs.fill.type,o=a.colorFrom,n=a.colorTo,l=a.opacityFrom,h=a.opacityTo,c=a.stops,d=s.enabled,g=s.left,u=s.top,f=s.blur,p=s.color,b=s.opacity,y=t.config.xaxis.crosshairs.fill.color;if(t.config.xaxis.crosshairs.show){\"gradient\"===r&&(y=e.drawGradient(\"vertical\",o,n,l,h,null,c,null));var w=e.drawRect();1===t.config.xaxis.crosshairs.width&&(w=e.drawLine());var k=t.globals.gridHeight;(!x.isNumber(k)||k<0)&&(k=0);var A=t.config.xaxis.crosshairs.width;(!x.isNumber(A)||A<0)&&(A=0),w.attr({class:\"apexcharts-xcrosshairs\",x:0,y:0,y2:k,width:A,height:k,fill:y,filter:\"none\",\"fill-opacity\":t.config.xaxis.crosshairs.opacity,stroke:t.config.xaxis.crosshairs.stroke.color,\"stroke-width\":t.config.xaxis.crosshairs.stroke.width,\"stroke-dasharray\":t.config.xaxis.crosshairs.stroke.dashArray}),d&&(w=i.dropShadow(w,{left:g,top:u,blur:f,color:p,opacity:b})),t.globals.dom.elGraphical.add(w)}}},{key:\"drawYCrosshairs\",value:function(){var t=this.w,e=new m(this.ctx),i=t.config.yaxis[0].crosshairs,a=t.globals.barPadForNumericAxis;if(t.config.yaxis[0].crosshairs.show){var s=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,i.stroke.dashArray,i.stroke.width);s.attr({class:\"apexcharts-ycrosshairs\"}),t.globals.dom.elGraphical.add(s)}var r=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,0,0);r.attr({class:\"apexcharts-ycrosshairs-hidden\"}),t.globals.dom.elGraphical.add(r)}}]),t}(),K=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"checkResponsiveConfig\",value:function(t){var e=this,i=this.w,a=i.config;if(0!==a.responsive.length){var s=a.responsive.slice();s.sort((function(t,e){return t.breakpoint>e.breakpoint?1:e.breakpoint>t.breakpoint?-1:0})).reverse();var r=new E({}),o=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=s[0].breakpoint,o=window.innerWidth>0?window.innerWidth:screen.width;if(o>a){var n=y.extendArrayProps(r,i.globals.initialConfig,i);t=x.extend(n,t),t=x.extend(i.config,t),e.overrideResponsiveOptions(t)}else for(var l=0;l<s.length;l++)o<s[l].breakpoint&&(t=y.extendArrayProps(r,s[l].options,i),t=x.extend(i.config,t),e.overrideResponsiveOptions(t))};if(t){var n=y.extendArrayProps(r,t,i);n=x.extend(i.config,n),o(n=x.extend(n,t))}else o({})}}},{key:\"overrideResponsiveOptions\",value:function(t){var e=new E(t).init({responsiveOverride:!0});this.w.config=e}}]),t}(),tt=function(){function t(e){a(this,t),this.ctx=e,this.colors=[],this.w=e.w;var i=this.w;this.isColorFn=!1,this.isHeatmapDistributed=\"treemap\"===i.config.chart.type&&i.config.plotOptions.treemap.distributed||\"heatmap\"===i.config.chart.type&&i.config.plotOptions.heatmap.distributed,this.isBarDistributed=i.config.plotOptions.bar.distributed&&(\"bar\"===i.config.chart.type||\"rangeBar\"===i.config.chart.type)}return r(t,[{key:\"init\",value:function(){this.setDefaultColors()}},{key:\"setDefaultColors\",value:function(){var t=this,e=this.w,i=new x;if(e.globals.dom.elWrap.classList.add(\"apexcharts-theme-\".concat(e.config.theme.mode)),void 0===e.config.colors?e.globals.colors=this.predefined():(e.globals.colors=e.config.colors,Array.isArray(e.config.colors)&&e.config.colors.length>0&&\"function\"==typeof e.config.colors[0]&&(e.globals.colors=e.config.series.map((function(i,a){var s=e.config.colors[a];return s||(s=e.config.colors[0]),\"function\"==typeof s?(t.isColorFn=!0,s({value:e.globals.axisCharts?e.globals.series[a][0]?e.globals.series[a][0]:0:e.globals.series[a],seriesIndex:a,dataPointIndex:a,w:e})):s})))),e.globals.seriesColors.map((function(t,i){t&&(e.globals.colors[i]=t)})),e.config.theme.monochrome.enabled){var a=[],s=e.globals.series.length;(this.isBarDistributed||this.isHeatmapDistributed)&&(s=e.globals.series[0].length*e.globals.series.length);for(var r=e.config.theme.monochrome.color,o=1/(s/e.config.theme.monochrome.shadeIntensity),n=e.config.theme.monochrome.shadeTo,l=0,h=0;h<s;h++){var c=void 0;\"dark\"===n?(c=i.shadeColor(-1*l,r),l+=o):(c=i.shadeColor(l,r),l+=o),a.push(c)}e.globals.colors=a.slice()}var d=e.globals.colors.slice();this.pushExtraColors(e.globals.colors);[\"fill\",\"stroke\"].forEach((function(i){void 0===e.config[i].colors?e.globals[i].colors=t.isColorFn?e.config.colors:d:e.globals[i].colors=e.config[i].colors.slice(),t.pushExtraColors(e.globals[i].colors)})),void 0===e.config.dataLabels.style.colors?e.globals.dataLabels.style.colors=d:e.globals.dataLabels.style.colors=e.config.dataLabels.style.colors.slice(),this.pushExtraColors(e.globals.dataLabels.style.colors,50),void 0===e.config.plotOptions.radar.polygons.fill.colors?e.globals.radarPolygons.fill.colors=[\"dark\"===e.config.theme.mode?\"#424242\":\"none\"]:e.globals.radarPolygons.fill.colors=e.config.plotOptions.radar.polygons.fill.colors.slice(),this.pushExtraColors(e.globals.radarPolygons.fill.colors,20),void 0===e.config.markers.colors?e.globals.markers.colors=d:e.globals.markers.colors=e.config.markers.colors.slice(),this.pushExtraColors(e.globals.markers.colors)}},{key:\"pushExtraColors\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=e||a.globals.series.length;if(null===i&&(i=this.isBarDistributed||this.isHeatmapDistributed||\"heatmap\"===a.config.chart.type&&a.config.plotOptions.heatmap.colorScale.inverse),i&&a.globals.series.length&&(s=a.globals.series[a.globals.maxValsInArrayIndex].length*a.globals.series.length),t.length<s)for(var r=s-t.length,o=0;o<r;o++)t.push(t[o])}},{key:\"updateThemeOptions\",value:function(t){t.chart=t.chart||{},t.tooltip=t.tooltip||{};var e=t.theme.mode||\"light\",i=t.theme.palette?t.theme.palette:\"dark\"===e?\"palette4\":\"palette1\",a=t.chart.foreColor?t.chart.foreColor:\"dark\"===e?\"#f6f7f8\":\"#373d3f\";return t.tooltip.theme=e,t.chart.foreColor=a,t.theme.palette=i,t}},{key:\"predefined\",value:function(){switch(this.w.config.theme.palette){case\"palette1\":default:this.colors=[\"#008FFB\",\"#00E396\",\"#FEB019\",\"#FF4560\",\"#775DD0\"];break;case\"palette2\":this.colors=[\"#3f51b5\",\"#03a9f4\",\"#4caf50\",\"#f9ce1d\",\"#FF9800\"];break;case\"palette3\":this.colors=[\"#33b2df\",\"#546E7A\",\"#d4526e\",\"#13d8aa\",\"#A5978B\"];break;case\"palette4\":this.colors=[\"#4ecdc4\",\"#c7f464\",\"#81D4FA\",\"#fd6a6a\",\"#546E7A\"];break;case\"palette5\":this.colors=[\"#2b908f\",\"#f9a3a4\",\"#90ee7e\",\"#fa4443\",\"#69d2e7\"];break;case\"palette6\":this.colors=[\"#449DD1\",\"#F86624\",\"#EA3546\",\"#662E9B\",\"#C5D86D\"];break;case\"palette7\":this.colors=[\"#D7263D\",\"#1B998B\",\"#2E294E\",\"#F46036\",\"#E2C044\"];break;case\"palette8\":this.colors=[\"#662E9B\",\"#F86624\",\"#F9C80E\",\"#EA3546\",\"#43BCCD\"];break;case\"palette9\":this.colors=[\"#5C4742\",\"#A5978B\",\"#8D5B4C\",\"#5A2A27\",\"#C4BBAF\"];break;case\"palette10\":this.colors=[\"#A300D6\",\"#7D02EB\",\"#5653FE\",\"#2983FF\",\"#00B1F2\"]}return this.colors}}]),t}(),et=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"draw\",value:function(){this.drawTitleSubtitle(\"title\"),this.drawTitleSubtitle(\"subtitle\")}},{key:\"drawTitleSubtitle\",value:function(t){var e=this.w,i=\"title\"===t?e.config.title:e.config.subtitle,a=e.globals.svgWidth/2,s=i.offsetY,r=\"middle\";if(\"left\"===i.align?(a=10,r=\"start\"):\"right\"===i.align&&(a=e.globals.svgWidth-10,r=\"end\"),a+=i.offsetX,s=s+parseInt(i.style.fontSize,10)+i.margin/2,void 0!==i.text){var o=new m(this.ctx).drawText({x:a,y:s,text:i.text,textAnchor:r,fontSize:i.style.fontSize,fontFamily:i.style.fontFamily,fontWeight:i.style.fontWeight,foreColor:i.style.color,opacity:1});o.node.setAttribute(\"class\",\"apexcharts-\".concat(t,\"-text\")),e.globals.dom.Paper.add(o)}}}]),t}(),it=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"getTitleSubtitleCoords\",value:function(t){var e=this.w,i=0,a=0,s=\"title\"===t?e.config.title.floating:e.config.subtitle.floating,r=e.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(t,\"-text\"));if(null!==r&&!s){var o=r.getBoundingClientRect();i=o.width,a=e.globals.axisCharts?o.height+5:o.height}return{width:i,height:a}}},{key:\"getLegendsRect\",value:function(){var t=this.w,e=t.globals.dom.baseEl.querySelector(\".apexcharts-legend\");t.config.legend.height||\"top\"!==t.config.legend.position&&\"bottom\"!==t.config.legend.position||(e.style.maxHeight=t.globals.svgHeight/2+\"px\");var i=Object.assign({},x.getBoundingClientRect(e));return null!==e&&!t.config.legend.floating&&t.config.legend.show?this.dCtx.lgRect={x:i.x,y:i.y,height:i.height,width:0===i.height?0:i.width}:this.dCtx.lgRect={x:0,y:0,height:0,width:0},\"left\"!==t.config.legend.position&&\"right\"!==t.config.legend.position||1.5*this.dCtx.lgRect.width>t.globals.svgWidth&&(this.dCtx.lgRect.width=t.globals.svgWidth/1.5),this.dCtx.lgRect}},{key:\"getLargestStringFromMultiArr\",value:function(t,e){var i=t;if(this.w.globals.isMultiLineX){var a=e.map((function(t,e){return Array.isArray(t)?t.length:1})),s=Math.max.apply(Math,u(a));i=e[a.indexOf(s)]}return i}}]),t}(),at=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"getxAxisLabelsCoords\",value:function(){var t,e=this.w,i=e.globals.labels.slice();if(e.config.xaxis.convertedCatToNumeric&&0===i.length&&(i=e.globals.categoryLabels),e.globals.timescaleLabels.length>0){var a=this.getxAxisTimeScaleLabelsCoords();t={width:a.width,height:a.height},e.globals.rotateXLabels=!1}else{this.dCtx.lgWidthForSideLegends=\"left\"!==e.config.legend.position&&\"right\"!==e.config.legend.position||e.config.legend.floating?0:this.dCtx.lgRect.width;var s=e.globals.xLabelFormatter,r=x.getLargestStringFromArr(i),o=this.dCtx.dimHelpers.getLargestStringFromMultiArr(r,i);e.globals.isBarHorizontal&&(o=r=e.globals.yAxisScale[0].result.reduce((function(t,e){return t.length>e.length?t:e}),0));var n=new M(this.dCtx.ctx),l=r;r=n.xLabelFormat(s,r,l,{i:void 0,dateFormatter:new T(this.dCtx.ctx).formatDate,w:e}),o=n.xLabelFormat(s,o,l,{i:void 0,dateFormatter:new T(this.dCtx.ctx).formatDate,w:e}),(e.config.xaxis.convertedCatToNumeric&&void 0===r||\"\"===String(r).trim())&&(o=r=\"1\");var h=new m(this.dCtx.ctx),c=h.getTextRects(r,e.config.xaxis.labels.style.fontSize),d=c;if(r!==o&&(d=h.getTextRects(o,e.config.xaxis.labels.style.fontSize)),(t={width:c.width>=d.width?c.width:d.width,height:c.height>=d.height?c.height:d.height}).width*i.length>e.globals.svgWidth-this.dCtx.lgWidthForSideLegends-this.dCtx.yAxisWidth-this.dCtx.gridPad.left-this.dCtx.gridPad.right&&0!==e.config.xaxis.labels.rotate||e.config.xaxis.labels.rotateAlways){if(!e.globals.isBarHorizontal){e.globals.rotateXLabels=!0;var g=function(t){return h.getTextRects(t,e.config.xaxis.labels.style.fontSize,e.config.xaxis.labels.style.fontFamily,\"rotate(\".concat(e.config.xaxis.labels.rotate,\" 0 0)\"),!1)};c=g(r),r!==o&&(d=g(o)),t.height=(c.height>d.height?c.height:d.height)/1.5,t.width=c.width>d.width?c.width:d.width}}else e.globals.rotateXLabels=!1}return e.config.xaxis.labels.show||(t={width:0,height:0}),{width:t.width,height:t.height}}},{key:\"getxAxisGroupLabelsCoords\",value:function(){var t,e=this.w;if(!e.globals.hasGroups)return{width:0,height:0};var i,a=(null===(t=e.config.xaxis.group.style)||void 0===t?void 0:t.fontSize)||e.config.xaxis.labels.style.fontSize,s=e.globals.groups.map((function(t){return t.title})),r=x.getLargestStringFromArr(s),o=this.dCtx.dimHelpers.getLargestStringFromMultiArr(r,s),n=new m(this.dCtx.ctx),l=n.getTextRects(r,a),h=l;return r!==o&&(h=n.getTextRects(o,a)),i={width:l.width>=h.width?l.width:h.width,height:l.height>=h.height?l.height:h.height},e.config.xaxis.labels.show||(i={width:0,height:0}),{width:i.width,height:i.height}}},{key:\"getxAxisTitleCoords\",value:function(){var t=this.w,e=0,i=0;if(void 0!==t.config.xaxis.title.text){var a=new m(this.dCtx.ctx).getTextRects(t.config.xaxis.title.text,t.config.xaxis.title.style.fontSize);e=a.width,i=a.height}return{width:e,height:i}}},{key:\"getxAxisTimeScaleLabelsCoords\",value:function(){var t,e=this.w;this.dCtx.timescaleLabels=e.globals.timescaleLabels.slice();var i=this.dCtx.timescaleLabels.map((function(t){return t.value})),a=i.reduce((function(t,e){return void 0===t?(console.error(\"You have possibly supplied invalid Date format. Please supply a valid JavaScript Date\"),0):t.length>e.length?t:e}),0);return 1.05*(t=new m(this.dCtx.ctx).getTextRects(a,e.config.xaxis.labels.style.fontSize)).width*i.length>e.globals.gridWidth&&0!==e.config.xaxis.labels.rotate&&(e.globals.overlappingXLabels=!0),t}},{key:\"additionalPaddingXLabels\",value:function(t){var e=this,i=this.w,a=i.globals,s=i.config,r=s.xaxis.type,o=t.width;a.skipLastTimelinelabel=!1,a.skipFirstTimelinelabel=!1;var n=i.config.yaxis[0].opposite&&i.globals.isBarHorizontal,l=function(t,n){(function(t){return-1!==a.collapsedSeriesIndices.indexOf(t)})(n)||function(t){if(e.dCtx.timescaleLabels&&e.dCtx.timescaleLabels.length){var n=e.dCtx.timescaleLabels[0],l=e.dCtx.timescaleLabels[e.dCtx.timescaleLabels.length-1].position+o/1.75-e.dCtx.yAxisWidthRight,h=n.position-o/1.75+e.dCtx.yAxisWidthLeft,c=\"right\"===i.config.legend.position&&e.dCtx.lgRect.width>0?e.dCtx.lgRect.width:0;l>a.svgWidth-a.translateX-c&&(a.skipLastTimelinelabel=!0),h<-(t.show&&!t.floating||\"bar\"!==s.chart.type&&\"candlestick\"!==s.chart.type&&\"rangeBar\"!==s.chart.type&&\"boxPlot\"!==s.chart.type?10:o/1.75)&&(a.skipFirstTimelinelabel=!0)}else\"datetime\"===r?e.dCtx.gridPad.right<o&&!a.rotateXLabels&&(a.skipLastTimelinelabel=!0):\"datetime\"!==r&&e.dCtx.gridPad.right<o/2-e.dCtx.yAxisWidthRight&&!a.rotateXLabels&&!i.config.xaxis.labels.trim&&(\"between\"!==i.config.xaxis.tickPlacement||i.globals.isBarHorizontal)&&(e.dCtx.xPadRight=o/2+1)}(t)};s.yaxis.forEach((function(t,i){n?(e.dCtx.gridPad.left<o&&(e.dCtx.xPadLeft=o/2+1),e.dCtx.xPadRight=o/2+1):l(t,i)}))}}]),t}(),st=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"getyAxisLabelsCoords\",value:function(){var t=this,e=this.w,i=[],a=10,s=new B(this.dCtx.ctx);return e.config.yaxis.map((function(r,o){var n=e.globals.yAxisScale[o],l=0;if(!s.isYAxisHidden(o)&&r.labels.show&&void 0!==r.labels.minWidth&&(l=r.labels.minWidth),!s.isYAxisHidden(o)&&r.labels.show&&n.result.length){var h=e.globals.yLabelFormatters[o],c=n.niceMin===Number.MIN_VALUE?0:n.niceMin,d=String(c).length>String(n.niceMax).length?c:n.niceMax,g=h(d,{seriesIndex:o,dataPointIndex:-1,w:e}),u=g;if(void 0!==g&&0!==g.length||(g=d),e.globals.isBarHorizontal){a=0;var f=e.globals.labels.slice();g=h(g=x.getLargestStringFromArr(f),{seriesIndex:o,dataPointIndex:-1,w:e}),u=t.dCtx.dimHelpers.getLargestStringFromMultiArr(g,f)}var p=new m(t.dCtx.ctx),b=\"rotate(\".concat(r.labels.rotate,\" 0 0)\"),v=p.getTextRects(g,r.labels.style.fontSize,r.labels.style.fontFamily,b,!1),y=v;g!==u&&(y=p.getTextRects(u,r.labels.style.fontSize,r.labels.style.fontFamily,b,!1)),i.push({width:(l>y.width||l>v.width?l:y.width>v.width?y.width:v.width)+a,height:y.height>v.height?y.height:v.height})}else i.push({width:0,height:0})})),i}},{key:\"getyAxisTitleCoords\",value:function(){var t=this,e=this.w,i=[];return e.config.yaxis.map((function(e,a){if(e.show&&void 0!==e.title.text){var s=new m(t.dCtx.ctx),r=\"rotate(\".concat(e.title.rotate,\" 0 0)\"),o=s.getTextRects(e.title.text,e.title.style.fontSize,e.title.style.fontFamily,r,!1);i.push({width:o.width,height:o.height})}else i.push({width:0,height:0})})),i}},{key:\"getTotalYAxisWidth\",value:function(){var t=this.w,e=0,i=0,a=0,s=t.globals.yAxisScale.length>1?10:0,r=new B(this.dCtx.ctx),o=function(o,n){var l=t.config.yaxis[n].floating,h=0;o.width>0&&!l?(h=o.width+s,function(e){return t.globals.ignoreYAxisIndexes.indexOf(e)>-1}(n)&&(h=h-o.width-s)):h=l||r.isYAxisHidden(n)?0:5,t.config.yaxis[n].opposite?a+=h:i+=h,e+=h};return t.globals.yLabelsCoords.map((function(t,e){o(t,e)})),t.globals.yTitleCoords.map((function(t,e){o(t,e)})),t.globals.isBarHorizontal&&!t.config.yaxis[0].floating&&(e=t.globals.yLabelsCoords[0].width+t.globals.yTitleCoords[0].width+15),this.dCtx.yAxisWidthLeft=i,this.dCtx.yAxisWidthRight=a,e}}]),t}(),rt=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"gridPadForColumnsInNumericAxis\",value:function(t){var e=this.w;if(e.globals.noData||e.globals.allSeriesCollapsed)return 0;var i=function(t){return\"bar\"===t||\"rangeBar\"===t||\"candlestick\"===t||\"boxPlot\"===t},a=e.config.chart.type,s=0,r=i(a)?e.config.series.length:1;if(e.globals.comboBarCount>0&&(r=e.globals.comboBarCount),e.globals.collapsedSeries.forEach((function(t){i(t.type)&&(r-=1)})),e.config.chart.stacked&&(r=1),(i(a)||e.globals.comboBarCount>0)&&e.globals.isXNumeric&&!e.globals.isBarHorizontal&&r>0){var o,n,l=Math.abs(e.globals.initialMaxX-e.globals.initialMinX);l<=3&&(l=e.globals.dataPoints),o=l/t,e.globals.minXDiff&&e.globals.minXDiff/o>0&&(n=e.globals.minXDiff/o),n>t/2&&(n/=2),(s=n/r*parseInt(e.config.plotOptions.bar.columnWidth,10)/100)<1&&(s=1),s=s/(r>1?1:1.5)+5,e.globals.barPadForNumericAxis=s}return s}},{key:\"gridPadFortitleSubtitle\",value:function(){var t=this,e=this.w,i=e.globals,a=this.dCtx.isSparkline||!e.globals.axisCharts?0:10;[\"title\",\"subtitle\"].forEach((function(i){void 0!==e.config[i].text?a+=e.config[i].margin:a+=t.dCtx.isSparkline||!e.globals.axisCharts?0:5})),!e.config.legend.show||\"bottom\"!==e.config.legend.position||e.config.legend.floating||e.globals.axisCharts||(a+=10);var s=this.dCtx.dimHelpers.getTitleSubtitleCoords(\"title\"),r=this.dCtx.dimHelpers.getTitleSubtitleCoords(\"subtitle\");i.gridHeight=i.gridHeight-s.height-r.height-a,i.translateY=i.translateY+s.height+r.height+a}},{key:\"setGridXPosForDualYAxis\",value:function(t,e){var i=this.w,a=new B(this.dCtx.ctx);i.config.yaxis.map((function(s,r){-1!==i.globals.ignoreYAxisIndexes.indexOf(r)||s.floating||a.isYAxisHidden(r)||(s.opposite&&(i.globals.translateX=i.globals.translateX-(e[r].width+t[r].width)-parseInt(i.config.yaxis[r].labels.style.fontSize,10)/1.2-12),i.globals.translateX<2&&(i.globals.translateX=2))}))}}]),t}(),ot=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.lgRect={},this.yAxisWidth=0,this.yAxisWidthLeft=0,this.yAxisWidthRight=0,this.xAxisHeight=0,this.isSparkline=this.w.config.chart.sparkline.enabled,this.dimHelpers=new it(this),this.dimYAxis=new st(this),this.dimXAxis=new at(this),this.dimGrid=new rt(this),this.lgWidthForSideLegends=0,this.gridPad=this.w.config.grid.padding,this.xPadRight=0,this.xPadLeft=0}return r(t,[{key:\"plotCoords\",value:function(){var t=this,e=this.w,i=e.globals;this.lgRect=this.dimHelpers.getLegendsRect(),this.isSparkline&&(e.config.markers.discrete.length>0||e.config.markers.size>0)&&Object.entries(this.gridPad).forEach((function(e){var i=g(e,2),a=i[0],s=i[1];t.gridPad[a]=Math.max(s,t.w.globals.markers.largestSize/1.5)})),i.axisCharts?this.setDimensionsForAxisCharts():this.setDimensionsForNonAxisCharts(),this.dimGrid.gridPadFortitleSubtitle(),i.gridHeight=i.gridHeight-this.gridPad.top-this.gridPad.bottom,i.gridWidth=i.gridWidth-this.gridPad.left-this.gridPad.right-this.xPadRight-this.xPadLeft;var a=this.dimGrid.gridPadForColumnsInNumericAxis(i.gridWidth);i.gridWidth=i.gridWidth-2*a,i.translateX=i.translateX+this.gridPad.left+this.xPadLeft+(a>0?a+4:0),i.translateY=i.translateY+this.gridPad.top}},{key:\"setDimensionsForAxisCharts\",value:function(){var t=this,e=this.w,i=e.globals,a=this.dimYAxis.getyAxisLabelsCoords(),s=this.dimYAxis.getyAxisTitleCoords();e.globals.yLabelsCoords=[],e.globals.yTitleCoords=[],e.config.yaxis.map((function(t,i){e.globals.yLabelsCoords.push({width:a[i].width,index:i}),e.globals.yTitleCoords.push({width:s[i].width,index:i})})),this.yAxisWidth=this.dimYAxis.getTotalYAxisWidth();var r=this.dimXAxis.getxAxisLabelsCoords(),o=this.dimXAxis.getxAxisGroupLabelsCoords(),n=this.dimXAxis.getxAxisTitleCoords();this.conditionalChecksForAxisCoords(r,n,o),i.translateXAxisY=e.globals.rotateXLabels?this.xAxisHeight/8:-4,i.translateXAxisX=e.globals.rotateXLabels&&e.globals.isXNumeric&&e.config.xaxis.labels.rotate<=-45?-this.xAxisWidth/4:0,e.globals.isBarHorizontal&&(i.rotateXLabels=!1,i.translateXAxisY=parseInt(e.config.xaxis.labels.style.fontSize,10)/1.5*-1),i.translateXAxisY=i.translateXAxisY+e.config.xaxis.labels.offsetY,i.translateXAxisX=i.translateXAxisX+e.config.xaxis.labels.offsetX;var l=this.yAxisWidth,h=this.xAxisHeight;i.xAxisLabelsHeight=this.xAxisHeight-n.height,i.xAxisGroupLabelsHeight=i.xAxisLabelsHeight-r.height,i.xAxisLabelsWidth=this.xAxisWidth,i.xAxisHeight=this.xAxisHeight;var c=10;(\"radar\"===e.config.chart.type||this.isSparkline)&&(l=0,h=i.goldenPadding),this.isSparkline&&(this.lgRect={height:0,width:0}),(this.isSparkline||\"treemap\"===e.config.chart.type)&&(l=0,h=0,c=0),this.isSparkline||this.dimXAxis.additionalPaddingXLabels(r);var d=function(){i.translateX=l,i.gridHeight=i.svgHeight-t.lgRect.height-h-(t.isSparkline||\"treemap\"===e.config.chart.type?0:e.globals.rotateXLabels?10:15),i.gridWidth=i.svgWidth-l};switch(\"top\"===e.config.xaxis.position&&(c=i.xAxisHeight-e.config.xaxis.axisTicks.height-5),e.config.legend.position){case\"bottom\":i.translateY=c,d();break;case\"top\":i.translateY=this.lgRect.height+c,d();break;case\"left\":i.translateY=c,i.translateX=this.lgRect.width+l,i.gridHeight=i.svgHeight-h-12,i.gridWidth=i.svgWidth-this.lgRect.width-l;break;case\"right\":i.translateY=c,i.translateX=l,i.gridHeight=i.svgHeight-h-12,i.gridWidth=i.svgWidth-this.lgRect.width-l-5;break;default:throw new Error(\"Legend position not supported\")}this.dimGrid.setGridXPosForDualYAxis(s,a),new q(this.ctx).setYAxisXPosition(a,s)}},{key:\"setDimensionsForNonAxisCharts\",value:function(){var t=this.w,e=t.globals,i=t.config,a=0;t.config.legend.show&&!t.config.legend.floating&&(a=20);var s=\"pie\"===i.chart.type||\"polarArea\"===i.chart.type||\"donut\"===i.chart.type?\"pie\":\"radialBar\",r=i.plotOptions[s].offsetY,o=i.plotOptions[s].offsetX;if(!i.legend.show||i.legend.floating)return e.gridHeight=e.svgHeight-i.grid.padding.left+i.grid.padding.right,e.gridWidth=e.gridHeight,e.translateY=r,void(e.translateX=o+(e.svgWidth-e.gridWidth)/2);switch(i.legend.position){case\"bottom\":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.svgWidth,e.translateY=r-10,e.translateX=o+(e.svgWidth-e.gridWidth)/2;break;case\"top\":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.svgWidth,e.translateY=this.lgRect.height+r+10,e.translateX=o+(e.svgWidth-e.gridWidth)/2;break;case\"left\":e.gridWidth=e.svgWidth-this.lgRect.width-a,e.gridHeight=\"auto\"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=o+this.lgRect.width+a;break;case\"right\":e.gridWidth=e.svgWidth-this.lgRect.width-a-5,e.gridHeight=\"auto\"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=o+10;break;default:throw new Error(\"Legend position not supported\")}}},{key:\"conditionalChecksForAxisCoords\",value:function(t,e,i){var a=this.w,s=a.globals.hasGroups?2:1,r=i.height+t.height+e.height,o=a.globals.isMultiLineX?1.2:a.globals.LINE_HEIGHT_RATIO,n=a.globals.rotateXLabels?22:10,l=a.globals.rotateXLabels&&\"bottom\"===a.config.legend.position?10:0;this.xAxisHeight=r*o+s*n+l,this.xAxisWidth=t.width,this.xAxisHeight-e.height>a.config.xaxis.labels.maxHeight&&(this.xAxisHeight=a.config.xaxis.labels.maxHeight),a.config.xaxis.labels.minHeight&&this.xAxisHeight<a.config.xaxis.labels.minHeight&&(this.xAxisHeight=a.config.xaxis.labels.minHeight),a.config.xaxis.floating&&(this.xAxisHeight=0);var h=0,c=0;a.config.yaxis.forEach((function(t){h+=t.labels.minWidth,c+=t.labels.maxWidth})),this.yAxisWidth<h&&(this.yAxisWidth=h),this.yAxisWidth>c&&(this.yAxisWidth=c)}}]),t}(),nt=function(){function t(e){a(this,t),this.w=e.w,this.lgCtx=e}return r(t,[{key:\"getLegendBBox\",value:function(){var t=this.w.globals.dom.baseEl.querySelector(\".apexcharts-legend\").getBoundingClientRect(),e=t.width;return{clwh:t.height,clww:e}}},{key:\"toggleDataSeries\",value:function(t,e){var i=this,a=this.w;if(a.globals.axisCharts||\"radialBar\"===a.config.chart.type){a.globals.resized=!0;var s=null,r=null;if(a.globals.risingSeries=[],a.globals.axisCharts?(s=a.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(t,\"']\")),r=parseInt(s.getAttribute(\"data:realIndex\"),10)):(s=a.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(t+1,\"']\")),r=parseInt(s.getAttribute(\"rel\"),10)-1),e)[{cs:a.globals.collapsedSeries,csi:a.globals.collapsedSeriesIndices},{cs:a.globals.ancillaryCollapsedSeries,csi:a.globals.ancillaryCollapsedSeriesIndices}].forEach((function(t){i.riseCollapsedSeries(t.cs,t.csi,r)}));else this.hideSeries({seriesEl:s,realIndex:r})}else{var o=a.globals.dom.Paper.select(\" .apexcharts-series[rel='\".concat(t+1,\"'] path\")),n=a.config.chart.type;if(\"pie\"===n||\"polarArea\"===n||\"donut\"===n){var l=a.config.plotOptions.pie.donut.labels;new m(this.lgCtx.ctx).pathMouseDown(o.members[0],null),this.lgCtx.ctx.pie.printDataLabelsInner(o.members[0].node,l)}o.fire(\"click\")}}},{key:\"hideSeries\",value:function(t){var e=t.seriesEl,i=t.realIndex,a=this.w,s=x.clone(a.config.series);if(a.globals.axisCharts){var r=!1;if(a.config.yaxis[i]&&a.config.yaxis[i].show&&a.config.yaxis[i].showAlways&&(r=!0,a.globals.ancillaryCollapsedSeriesIndices.indexOf(i)<0&&(a.globals.ancillaryCollapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split(\"-\")[1]}),a.globals.ancillaryCollapsedSeriesIndices.push(i))),!r){a.globals.collapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split(\"-\")[1]}),a.globals.collapsedSeriesIndices.push(i);var o=a.globals.risingSeries.indexOf(i);a.globals.risingSeries.splice(o,1)}}else a.globals.collapsedSeries.push({index:i,data:s[i]}),a.globals.collapsedSeriesIndices.push(i);for(var n=e.childNodes,l=0;l<n.length;l++)n[l].classList.contains(\"apexcharts-series-markers-wrap\")&&(n[l].classList.contains(\"apexcharts-hide\")?n[l].classList.remove(\"apexcharts-hide\"):n[l].classList.add(\"apexcharts-hide\"));a.globals.allSeriesCollapsed=a.globals.collapsedSeries.length===a.config.series.length,s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}},{key:\"riseCollapsedSeries\",value:function(t,e,i){var a=this.w,s=x.clone(a.config.series);if(t.length>0){for(var r=0;r<t.length;r++)t[r].index===i&&(a.globals.axisCharts?(s[i].data=t[r].data.slice(),t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)):(s[i]=t[r].data,t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)));s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}}},{key:\"_getSeriesBasedOnCollapsedState\",value:function(t){var e=this.w;return e.globals.axisCharts?t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a].data=[])})):t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a]=0)})),t}}]),t}(),lt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.onLegendClick=this.onLegendClick.bind(this),this.onLegendHovered=this.onLegendHovered.bind(this),this.isBarsDistributed=\"bar\"===this.w.config.chart.type&&this.w.config.plotOptions.bar.distributed&&1===this.w.config.series.length,this.legendHelpers=new nt(this)}return r(t,[{key:\"init\",value:function(){var t=this.w,e=t.globals,i=t.config;if((i.legend.showForSingleSeries&&1===e.series.length||this.isBarsDistributed||e.series.length>1||!e.axisCharts)&&i.legend.show){for(;e.dom.elLegendWrap.firstChild;)e.dom.elLegendWrap.removeChild(e.dom.elLegendWrap.firstChild);this.drawLegends(),\"bottom\"===i.legend.position||\"top\"===i.legend.position?this.legendAlignHorizontal():\"right\"!==i.legend.position&&\"left\"!==i.legend.position||this.legendAlignVertical()}}},{key:\"drawLegends\",value:function(){var t=this,e=this.w,i=e.config.legend.fontFamily,a=e.globals.seriesNames,s=e.globals.colors.slice();if(\"heatmap\"===e.config.chart.type){var r=e.config.plotOptions.heatmap.colorScale.ranges;a=r.map((function(t){return t.name?t.name:t.from+\" - \"+t.to})),s=r.map((function(t){return t.color}))}else this.isBarsDistributed&&(a=e.globals.labels.slice());e.config.legend.customLegendItems.length&&(a=e.config.legend.customLegendItems);for(var o=e.globals.legendFormatter,n=e.config.legend.inverseOrder,l=n?a.length-1:0;n?l>=0:l<=a.length-1;n?l--:l++){var h=o(a[l],{seriesIndex:l,w:e}),c=!1,d=!1;if(e.globals.collapsedSeries.length>0)for(var g=0;g<e.globals.collapsedSeries.length;g++)e.globals.collapsedSeries[g].index===l&&(c=!0);if(e.globals.ancillaryCollapsedSeriesIndices.length>0)for(var u=0;u<e.globals.ancillaryCollapsedSeriesIndices.length;u++)e.globals.ancillaryCollapsedSeriesIndices[u]===l&&(d=!0);var f=document.createElement(\"span\");f.classList.add(\"apexcharts-legend-marker\");var p=e.config.legend.markers.offsetX,b=e.config.legend.markers.offsetY,v=e.config.legend.markers.height,w=e.config.legend.markers.width,k=e.config.legend.markers.strokeWidth,A=e.config.legend.markers.strokeColor,S=e.config.legend.markers.radius,C=f.style;C.background=s[l],C.color=s[l],C.setProperty(\"background\",s[l],\"important\"),e.config.legend.markers.fillColors&&e.config.legend.markers.fillColors[l]&&(C.background=e.config.legend.markers.fillColors[l]),void 0!==e.globals.seriesColors[l]&&(C.background=e.globals.seriesColors[l],C.color=e.globals.seriesColors[l]),C.height=Array.isArray(v)?parseFloat(v[l])+\"px\":parseFloat(v)+\"px\",C.width=Array.isArray(w)?parseFloat(w[l])+\"px\":parseFloat(w)+\"px\",C.left=(Array.isArray(p)?parseFloat(p[l]):parseFloat(p))+\"px\",C.top=(Array.isArray(b)?parseFloat(b[l]):parseFloat(b))+\"px\",C.borderWidth=Array.isArray(k)?k[l]:k,C.borderColor=Array.isArray(A)?A[l]:A,C.borderRadius=Array.isArray(S)?parseFloat(S[l])+\"px\":parseFloat(S)+\"px\",e.config.legend.markers.customHTML&&(Array.isArray(e.config.legend.markers.customHTML)?e.config.legend.markers.customHTML[l]&&(f.innerHTML=e.config.legend.markers.customHTML[l]()):f.innerHTML=e.config.legend.markers.customHTML()),m.setAttrs(f,{rel:l+1,\"data:collapsed\":c||d}),(c||d)&&f.classList.add(\"apexcharts-inactive-legend\");var L=document.createElement(\"div\"),P=document.createElement(\"span\");P.classList.add(\"apexcharts-legend-text\"),P.innerHTML=Array.isArray(h)?h.join(\" \"):h;var T=e.config.legend.labels.useSeriesColors?e.globals.colors[l]:e.config.legend.labels.colors;T||(T=e.config.chart.foreColor),P.style.color=T,P.style.fontSize=parseFloat(e.config.legend.fontSize)+\"px\",P.style.fontWeight=e.config.legend.fontWeight,P.style.fontFamily=i||e.config.chart.fontFamily,m.setAttrs(P,{rel:l+1,i:l,\"data:default-text\":encodeURIComponent(h),\"data:collapsed\":c||d}),L.appendChild(f),L.appendChild(P);var M=new y(this.ctx);if(!e.config.legend.showForZeroSeries)0===M.getSeriesTotalByIndex(l)&&M.seriesHaveSameValues(l)&&!M.isSeriesNull(l)&&-1===e.globals.collapsedSeriesIndices.indexOf(l)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(l)&&L.classList.add(\"apexcharts-hidden-zero-series\");e.config.legend.showForNullSeries||M.isSeriesNull(l)&&-1===e.globals.collapsedSeriesIndices.indexOf(l)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(l)&&L.classList.add(\"apexcharts-hidden-null-series\"),e.globals.dom.elLegendWrap.appendChild(L),e.globals.dom.elLegendWrap.classList.add(\"apexcharts-align-\".concat(e.config.legend.horizontalAlign)),e.globals.dom.elLegendWrap.classList.add(\"apx-legend-position-\"+e.config.legend.position),L.classList.add(\"apexcharts-legend-series\"),L.style.margin=\"\".concat(e.config.legend.itemMargin.vertical,\"px \").concat(e.config.legend.itemMargin.horizontal,\"px\"),e.globals.dom.elLegendWrap.style.width=e.config.legend.width?e.config.legend.width+\"px\":\"\",e.globals.dom.elLegendWrap.style.height=e.config.legend.height?e.config.legend.height+\"px\":\"\",m.setAttrs(L,{rel:l+1,seriesName:x.escapeString(a[l]),\"data:collapsed\":c||d}),(c||d)&&L.classList.add(\"apexcharts-inactive-legend\"),e.config.legend.onItemClick.toggleDataSeries||L.classList.add(\"apexcharts-no-click\")}e.globals.dom.elWrap.addEventListener(\"click\",t.onLegendClick,!0),e.globals.dom.elWrap.appendChild(e.globals.dom.elLegendWrap),e.config.legend.onItemHover.highlightDataSeries&&0===e.config.legend.customLegendItems.length&&(e.globals.dom.elWrap.addEventListener(\"mousemove\",t.onLegendHovered,!0),e.globals.dom.elWrap.addEventListener(\"mouseout\",t.onLegendHovered,!0))}},{key:\"setLegendWrapXY\",value:function(t,e){var i=this.w,a=i.globals.dom.elLegendWrap,s=a.getBoundingClientRect(),r=0,o=0;if(\"bottom\"===i.config.legend.position)o+=i.globals.svgHeight-s.height/2;else if(\"top\"===i.config.legend.position){var n=new ot(this.ctx),l=n.dimHelpers.getTitleSubtitleCoords(\"title\").height,h=n.dimHelpers.getTitleSubtitleCoords(\"subtitle\").height;o=o+(l>0?l-10:0)+(h>0?h-10:0)}a.style.position=\"absolute\",r=r+t+i.config.legend.offsetX,o=o+e+i.config.legend.offsetY,a.style.left=r+\"px\",a.style.top=o+\"px\",\"bottom\"===i.config.legend.position?(a.style.top=\"auto\",a.style.bottom=5-i.config.legend.offsetY+\"px\"):\"right\"===i.config.legend.position&&(a.style.left=\"auto\",a.style.right=25+i.config.legend.offsetX+\"px\");[\"width\",\"height\"].forEach((function(t){a.style[t]&&(a.style[t]=parseInt(i.config.legend[t],10)+\"px\")}))}},{key:\"legendAlignHorizontal\",value:function(){var t=this.w;t.globals.dom.elLegendWrap.style.right=0;var e=this.legendHelpers.getLegendBBox(),i=new ot(this.ctx),a=i.dimHelpers.getTitleSubtitleCoords(\"title\"),s=i.dimHelpers.getTitleSubtitleCoords(\"subtitle\"),r=0;\"bottom\"===t.config.legend.position?r=-e.clwh/1.8:\"top\"===t.config.legend.position&&(r=a.height+s.height+t.config.title.margin+t.config.subtitle.margin-10),this.setLegendWrapXY(20,r)}},{key:\"legendAlignVertical\",value:function(){var t=this.w,e=this.legendHelpers.getLegendBBox(),i=0;\"left\"===t.config.legend.position&&(i=20),\"right\"===t.config.legend.position&&(i=t.globals.svgWidth-e.clww-10),this.setLegendWrapXY(i,20)}},{key:\"onLegendHovered\",value:function(t){var e=this.w,i=t.target.classList.contains(\"apexcharts-legend-text\")||t.target.classList.contains(\"apexcharts-legend-marker\");if(\"heatmap\"===e.config.chart.type||this.isBarsDistributed){if(i){var a=parseInt(t.target.getAttribute(\"rel\"),10)-1;this.ctx.events.fireEvent(\"legendHover\",[this.ctx,a,this.w]),new N(this.ctx).highlightRangeInSeries(t,t.target)}}else!t.target.classList.contains(\"apexcharts-inactive-legend\")&&i&&new N(this.ctx).toggleSeriesOnHover(t,t.target)}},{key:\"onLegendClick\",value:function(t){var e=this.w;if(!e.config.legend.customLegendItems.length&&(t.target.classList.contains(\"apexcharts-legend-text\")||t.target.classList.contains(\"apexcharts-legend-marker\"))){var i=parseInt(t.target.getAttribute(\"rel\"),10)-1,a=\"true\"===t.target.getAttribute(\"data:collapsed\"),s=this.w.config.chart.events.legendClick;\"function\"==typeof s&&s(this.ctx,i,this.w),this.ctx.events.fireEvent(\"legendClick\",[this.ctx,i,this.w]);var r=this.w.config.legend.markers.onClick;\"function\"==typeof r&&t.target.classList.contains(\"apexcharts-legend-marker\")&&(r(this.ctx,i,this.w),this.ctx.events.fireEvent(\"legendMarkerClick\",[this.ctx,i,this.w])),\"treemap\"!==e.config.chart.type&&\"heatmap\"!==e.config.chart.type&&!this.isBarsDistributed&&e.config.legend.onItemClick.toggleDataSeries&&this.legendHelpers.toggleDataSeries(i,a)}}}]),t}(),ht=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.ev=this.w.config.chart.events,this.selectedClass=\"apexcharts-selected\",this.localeValues=this.w.globals.locale.toolbar,this.minX=i.globals.minX,this.maxX=i.globals.maxX}return r(t,[{key:\"createToolbar\",value:function(){var t=this,e=this.w,i=function(){return document.createElement(\"div\")},a=i();if(a.setAttribute(\"class\",\"apexcharts-toolbar\"),a.style.top=e.config.chart.toolbar.offsetY+\"px\",a.style.right=3-e.config.chart.toolbar.offsetX+\"px\",e.globals.dom.elWrap.appendChild(a),this.elZoom=i(),this.elZoomIn=i(),this.elZoomOut=i(),this.elPan=i(),this.elSelection=i(),this.elZoomReset=i(),this.elMenuIcon=i(),this.elMenu=i(),this.elCustomIcons=[],this.t=e.config.chart.toolbar.tools,Array.isArray(this.t.customIcons))for(var s=0;s<this.t.customIcons.length;s++)this.elCustomIcons.push(i());var r=[],o=function(i,a,s){var o=i.toLowerCase();t.t[o]&&e.config.chart.zoom.enabled&&r.push({el:a,icon:\"string\"==typeof t.t[o]?t.t[o]:s,title:t.localeValues[i],class:\"apexcharts-\".concat(o,\"-icon\")})};o(\"zoomIn\",this.elZoomIn,'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n    <path d=\"M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"/>\\n</svg>\\n'),o(\"zoomOut\",this.elZoomOut,'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n    <path d=\"M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"/>\\n</svg>\\n');var n=function(i){t.t[i]&&e.config.chart[i].enabled&&r.push({el:\"zoom\"===i?t.elZoom:t.elSelection,icon:\"string\"==typeof t.t[i]?t.t[i]:\"zoom\"===i?'<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\">\\n    <path d=\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"/>\\n    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\\n    <path d=\"M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z\"/>\\n</svg>':'<svg fill=\"#6E8192\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n    <path d=\"M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2z\"/>\\n</svg>',title:t.localeValues[\"zoom\"===i?\"selectionZoom\":\"selection\"],class:e.globals.isTouchDevice?\"apexcharts-element-hidden\":\"apexcharts-\".concat(i,\"-icon\")})};n(\"zoom\"),n(\"selection\"),this.t.pan&&e.config.chart.zoom.enabled&&r.push({el:this.elPan,icon:\"string\"==typeof this.t.pan?this.t.pan:'<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\">\\n    <defs>\\n        <path d=\"M0 0h24v24H0z\" id=\"a\"/>\\n    </defs>\\n    <clipPath id=\"b\">\\n        <use overflow=\"visible\" xlink:href=\"#a\"/>\\n    </clipPath>\\n    <path clip-path=\"url(#b)\" d=\"M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z\"/>\\n</svg>',title:this.localeValues.pan,class:e.globals.isTouchDevice?\"apexcharts-element-hidden\":\"apexcharts-pan-icon\"}),o(\"reset\",this.elZoomReset,'<svg fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\\n    <path d=\"M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z\"/>\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n</svg>'),this.t.download&&r.push({el:this.elMenuIcon,icon:\"string\"==typeof this.t.download?this.t.download:'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z\"/></svg>',title:this.localeValues.menu,class:\"apexcharts-menu-icon\"});for(var l=0;l<this.elCustomIcons.length;l++)r.push({el:this.elCustomIcons[l],icon:this.t.customIcons[l].icon,title:this.t.customIcons[l].title,index:this.t.customIcons[l].index,class:\"apexcharts-toolbar-custom-icon \"+this.t.customIcons[l].class});r.forEach((function(t,e){t.index&&x.moveIndexInArray(r,e,t.index)}));for(var h=0;h<r.length;h++)m.setAttrs(r[h].el,{class:r[h].class,title:r[h].title}),r[h].el.innerHTML=r[h].icon,a.appendChild(r[h].el);this._createHamburgerMenu(a),e.globals.zoomEnabled?this.elZoom.classList.add(this.selectedClass):e.globals.panEnabled?this.elPan.classList.add(this.selectedClass):e.globals.selectionEnabled&&this.elSelection.classList.add(this.selectedClass),this.addToolbarEventListeners()}},{key:\"_createHamburgerMenu\",value:function(t){this.elMenuItems=[],t.appendChild(this.elMenu),m.setAttrs(this.elMenu,{class:\"apexcharts-menu\"});var e=[{name:\"exportSVG\",title:this.localeValues.exportToSVG},{name:\"exportPNG\",title:this.localeValues.exportToPNG},{name:\"exportCSV\",title:this.localeValues.exportToCSV}];this.w.globals.allSeriesHasEqualX||e.splice(2,1);for(var i=0;i<e.length;i++)this.elMenuItems.push(document.createElement(\"div\")),this.elMenuItems[i].innerHTML=e[i].title,m.setAttrs(this.elMenuItems[i],{class:\"apexcharts-menu-item \".concat(e[i].name),title:e[i].title}),this.elMenu.appendChild(this.elMenuItems[i])}},{key:\"addToolbarEventListeners\",value:function(){var t=this;this.elZoomReset.addEventListener(\"click\",this.handleZoomReset.bind(this)),this.elSelection.addEventListener(\"click\",this.toggleZoomSelection.bind(this,\"selection\")),this.elZoom.addEventListener(\"click\",this.toggleZoomSelection.bind(this,\"zoom\")),this.elZoomIn.addEventListener(\"click\",this.handleZoomIn.bind(this)),this.elZoomOut.addEventListener(\"click\",this.handleZoomOut.bind(this)),this.elPan.addEventListener(\"click\",this.togglePanning.bind(this)),this.elMenuIcon.addEventListener(\"click\",this.toggleMenu.bind(this)),this.elMenuItems.forEach((function(e){e.classList.contains(\"exportSVG\")?e.addEventListener(\"click\",t.handleDownload.bind(t,\"svg\")):e.classList.contains(\"exportPNG\")?e.addEventListener(\"click\",t.handleDownload.bind(t,\"png\")):e.classList.contains(\"exportCSV\")&&e.addEventListener(\"click\",t.handleDownload.bind(t,\"csv\"))}));for(var e=0;e<this.t.customIcons.length;e++)this.elCustomIcons[e].addEventListener(\"click\",this.t.customIcons[e].click.bind(this,this.ctx,this.ctx.w))}},{key:\"toggleZoomSelection\",value:function(t){this.ctx.getSyncedCharts().forEach((function(e){e.ctx.toolbar.toggleOtherControls();var i=\"selection\"===t?e.ctx.toolbar.elSelection:e.ctx.toolbar.elZoom,a=\"selection\"===t?\"selectionEnabled\":\"zoomEnabled\";e.w.globals[a]=!e.w.globals[a],i.classList.contains(e.ctx.toolbar.selectedClass)?i.classList.remove(e.ctx.toolbar.selectedClass):i.classList.add(e.ctx.toolbar.selectedClass)}))}},{key:\"getToolbarIconsReference\",value:function(){var t=this.w;this.elZoom||(this.elZoom=t.globals.dom.baseEl.querySelector(\".apexcharts-zoom-icon\")),this.elPan||(this.elPan=t.globals.dom.baseEl.querySelector(\".apexcharts-pan-icon\")),this.elSelection||(this.elSelection=t.globals.dom.baseEl.querySelector(\".apexcharts-selection-icon\"))}},{key:\"enableZoomPanFromToolbar\",value:function(t){this.toggleOtherControls(),\"pan\"===t?this.w.globals.panEnabled=!0:this.w.globals.zoomEnabled=!0;var e=\"pan\"===t?this.elPan:this.elZoom,i=\"pan\"===t?this.elZoom:this.elPan;e&&e.classList.add(this.selectedClass),i&&i.classList.remove(this.selectedClass)}},{key:\"togglePanning\",value:function(){this.ctx.getSyncedCharts().forEach((function(t){t.ctx.toolbar.toggleOtherControls(),t.w.globals.panEnabled=!t.w.globals.panEnabled,t.ctx.toolbar.elPan.classList.contains(t.ctx.toolbar.selectedClass)?t.ctx.toolbar.elPan.classList.remove(t.ctx.toolbar.selectedClass):t.ctx.toolbar.elPan.classList.add(t.ctx.toolbar.selectedClass)}))}},{key:\"toggleOtherControls\",value:function(){var t=this,e=this.w;e.globals.panEnabled=!1,e.globals.zoomEnabled=!1,e.globals.selectionEnabled=!1,this.getToolbarIconsReference(),[this.elPan,this.elSelection,this.elZoom].forEach((function(e){e&&e.classList.remove(t.selectedClass)}))}},{key:\"handleZoomIn\",value:function(){var t=this.w;t.globals.isRangeBar&&(this.minX=t.globals.minY,this.maxX=t.globals.maxY);var e=(this.minX+this.maxX)/2,i=(this.minX+e)/2,a=(this.maxX+e)/2,s=this._getNewMinXMaxX(i,a);t.globals.disableZoomIn||this.zoomUpdateOptions(s.minX,s.maxX)}},{key:\"handleZoomOut\",value:function(){var t=this.w;if(t.globals.isRangeBar&&(this.minX=t.globals.minY,this.maxX=t.globals.maxY),!(\"datetime\"===t.config.xaxis.type&&new Date(this.minX).getUTCFullYear()<1e3)){var e=(this.minX+this.maxX)/2,i=this.minX-(e-this.minX),a=this.maxX-(e-this.maxX),s=this._getNewMinXMaxX(i,a);t.globals.disableZoomOut||this.zoomUpdateOptions(s.minX,s.maxX)}}},{key:\"_getNewMinXMaxX\",value:function(t,e){var i=this.w.config.xaxis.convertedCatToNumeric;return{minX:i?Math.floor(t):t,maxX:i?Math.floor(e):e}}},{key:\"zoomUpdateOptions\",value:function(t,e){var i=this.w;if(void 0!==t||void 0!==e){if(!(i.config.xaxis.convertedCatToNumeric&&(t<1&&(t=1,e=i.globals.dataPoints),e-t<2))){var a={min:t,max:e},s=this.getBeforeZoomRange(a);s&&(a=s.xaxis);var r={xaxis:a},o=x.clone(i.globals.initialConfig.yaxis);if(i.config.chart.zoom.autoScaleYaxis)o=new _(this.ctx).autoScaleY(this.ctx,o,{xaxis:a});i.config.chart.group||(r.yaxis=o),this.w.globals.zoomed=!0,this.ctx.updateHelpers._updateOptions(r,!1,this.w.config.chart.animations.dynamicAnimation.enabled),this.zoomCallback(a,o)}}else this.handleZoomReset()}},{key:\"zoomCallback\",value:function(t,e){\"function\"==typeof this.ev.zoomed&&this.ev.zoomed(this.ctx,{xaxis:t,yaxis:e})}},{key:\"getBeforeZoomRange\",value:function(t,e){var i=null;return\"function\"==typeof this.ev.beforeZoom&&(i=this.ev.beforeZoom(this,{xaxis:t,yaxis:e})),i}},{key:\"toggleMenu\",value:function(){var t=this;window.setTimeout((function(){t.elMenu.classList.contains(\"apexcharts-menu-open\")?t.elMenu.classList.remove(\"apexcharts-menu-open\"):t.elMenu.classList.add(\"apexcharts-menu-open\")}),0)}},{key:\"handleDownload\",value:function(t){var e=this.w,i=new G(this.ctx);switch(t){case\"svg\":i.exportToSVG(this.ctx);break;case\"png\":i.exportToPng(this.ctx);break;case\"csv\":i.exportToCSV({series:e.config.series,columnDelimiter:e.config.chart.toolbar.export.csv.columnDelimiter})}}},{key:\"handleZoomReset\",value:function(t){this.ctx.getSyncedCharts().forEach((function(t){var e=t.w;if(e.globals.lastXAxis.min=void 0,e.globals.lastXAxis.max=void 0,t.updateHelpers.revertDefaultAxisMinMax(),\"function\"==typeof e.config.chart.events.beforeResetZoom){var i=e.config.chart.events.beforeResetZoom(t,e);i&&t.updateHelpers.revertDefaultAxisMinMax(i)}\"function\"==typeof e.config.chart.events.zoomed&&t.ctx.toolbar.zoomCallback({min:e.config.xaxis.min,max:e.config.xaxis.max}),e.globals.zoomed=!1;var a=t.ctx.series.emptyCollapsedSeries(x.clone(e.globals.initialSeries));t.updateHelpers._updateSeries(a,e.config.chart.animations.dynamicAnimation.enabled)}))}},{key:\"destroy\",value:function(){this.elZoom=null,this.elZoomIn=null,this.elZoomOut=null,this.elPan=null,this.elSelection=null,this.elZoomReset=null,this.elMenuIcon=null}}]),t}(),ct=function(t){n(i,ht);var e=d(i);function i(t){var s;return a(this,i),(s=e.call(this,t)).ctx=t,s.w=t.w,s.dragged=!1,s.graphics=new m(s.ctx),s.eventList=[\"mousedown\",\"mouseleave\",\"mousemove\",\"touchstart\",\"touchmove\",\"mouseup\",\"touchend\"],s.clientX=0,s.clientY=0,s.startX=0,s.endX=0,s.dragX=0,s.startY=0,s.endY=0,s.dragY=0,s.moveDirection=\"none\",s}return r(i,[{key:\"init\",value:function(t){var e=this,i=t.xyRatios,a=this.w,s=this;this.xyRatios=i,this.zoomRect=this.graphics.drawRect(0,0,0,0),this.selectionRect=this.graphics.drawRect(0,0,0,0),this.gridRect=a.globals.dom.baseEl.querySelector(\".apexcharts-grid\"),this.zoomRect.node.classList.add(\"apexcharts-zoom-rect\"),this.selectionRect.node.classList.add(\"apexcharts-selection-rect\"),a.globals.dom.elGraphical.add(this.zoomRect),a.globals.dom.elGraphical.add(this.selectionRect),\"x\"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,minY:0,maxX:a.globals.gridWidth,maxY:a.globals.gridHeight}).on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")):\"y\"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,maxX:a.globals.gridWidth}).on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")):this.slDraggableRect=this.selectionRect.draggable().on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")),this.preselectedSelection(),this.hoverArea=a.globals.dom.baseEl.querySelector(\"\".concat(a.globals.chartClass,\" .apexcharts-svg\")),this.hoverArea.classList.add(\"apexcharts-zoomable\"),this.eventList.forEach((function(t){e.hoverArea.addEventListener(t,s.svgMouseEvents.bind(s,i),{capture:!1,passive:!0})}))}},{key:\"destroy\",value:function(){this.slDraggableRect&&(this.slDraggableRect.draggable(!1),this.slDraggableRect.off(),this.selectionRect.off()),this.selectionRect=null,this.zoomRect=null,this.gridRect=null}},{key:\"svgMouseEvents\",value:function(t,e){var i=this.w,a=this,s=this.ctx.toolbar,r=i.globals.zoomEnabled?i.config.chart.zoom.type:i.config.chart.selection.type,o=i.config.chart.toolbar.autoSelected;if(e.shiftKey?(this.shiftWasPressed=!0,s.enableZoomPanFromToolbar(\"pan\"===o?\"zoom\":\"pan\")):this.shiftWasPressed&&(s.enableZoomPanFromToolbar(o),this.shiftWasPressed=!1),e.target){var n,l=e.target.classList;if(e.target.parentNode&&null!==e.target.parentNode&&(n=e.target.parentNode.classList),!(l.contains(\"apexcharts-selection-rect\")||l.contains(\"apexcharts-legend-marker\")||l.contains(\"apexcharts-legend-text\")||n&&n.contains(\"apexcharts-toolbar\"))){if(a.clientX=\"touchmove\"===e.type||\"touchstart\"===e.type?e.touches[0].clientX:\"touchend\"===e.type?e.changedTouches[0].clientX:e.clientX,a.clientY=\"touchmove\"===e.type||\"touchstart\"===e.type?e.touches[0].clientY:\"touchend\"===e.type?e.changedTouches[0].clientY:e.clientY,\"mousedown\"===e.type&&1===e.which){var h=a.gridRect.getBoundingClientRect();a.startX=a.clientX-h.left,a.startY=a.clientY-h.top,a.dragged=!1,a.w.globals.mousedown=!0}if((\"mousemove\"===e.type&&1===e.which||\"touchmove\"===e.type)&&(a.dragged=!0,i.globals.panEnabled?(i.globals.selection=null,a.w.globals.mousedown&&a.panDragging({context:a,zoomtype:r,xyRatios:t})):(a.w.globals.mousedown&&i.globals.zoomEnabled||a.w.globals.mousedown&&i.globals.selectionEnabled)&&(a.selection=a.selectionDrawing({context:a,zoomtype:r}))),\"mouseup\"===e.type||\"touchend\"===e.type||\"mouseleave\"===e.type){var c=a.gridRect.getBoundingClientRect();a.w.globals.mousedown&&(a.endX=a.clientX-c.left,a.endY=a.clientY-c.top,a.dragX=Math.abs(a.endX-a.startX),a.dragY=Math.abs(a.endY-a.startY),(i.globals.zoomEnabled||i.globals.selectionEnabled)&&a.selectionDrawn({context:a,zoomtype:r}),i.globals.panEnabled&&i.config.xaxis.convertedCatToNumeric&&a.delayedPanScrolled()),i.globals.zoomEnabled&&a.hideSelectionRect(this.selectionRect),a.dragged=!1,a.w.globals.mousedown=!1}this.makeSelectionRectDraggable()}}}},{key:\"makeSelectionRectDraggable\",value:function(){var t=this.w;if(this.selectionRect){var e=this.selectionRect.node.getBoundingClientRect();e.width>0&&e.height>0&&this.slDraggableRect.selectize({points:\"l, r\",pointSize:8,pointType:\"rect\"}).resize({constraint:{minX:0,minY:0,maxX:t.globals.gridWidth,maxY:t.globals.gridHeight}}).on(\"resizing\",this.selectionDragging.bind(this,\"resizing\"))}}},{key:\"preselectedSelection\",value:function(){var t=this.w,e=this.xyRatios;if(!t.globals.zoomEnabled)if(void 0!==t.globals.selection&&null!==t.globals.selection)this.drawSelectionRect(t.globals.selection);else if(void 0!==t.config.chart.selection.xaxis.min&&void 0!==t.config.chart.selection.xaxis.max){var i=(t.config.chart.selection.xaxis.min-t.globals.minX)/e.xRatio,a={x:i,y:0,width:t.globals.gridWidth-(t.globals.maxX-t.config.chart.selection.xaxis.max)/e.xRatio-i,height:t.globals.gridHeight,translateX:0,translateY:0,selectionEnabled:!0};this.drawSelectionRect(a),this.makeSelectionRectDraggable(),\"function\"==typeof t.config.chart.events.selection&&t.config.chart.events.selection(this.ctx,{xaxis:{min:t.config.chart.selection.xaxis.min,max:t.config.chart.selection.xaxis.max},yaxis:{}})}}},{key:\"drawSelectionRect\",value:function(t){var e=t.x,i=t.y,a=t.width,s=t.height,r=t.translateX,o=void 0===r?0:r,n=t.translateY,l=void 0===n?0:n,h=this.w,c=this.zoomRect,d=this.selectionRect;if(this.dragged||null!==h.globals.selection){var g={transform:\"translate(\"+o+\", \"+l+\")\"};h.globals.zoomEnabled&&this.dragged&&(a<0&&(a=1),c.attr({x:e,y:i,width:a,height:s,fill:h.config.chart.zoom.zoomedArea.fill.color,\"fill-opacity\":h.config.chart.zoom.zoomedArea.fill.opacity,stroke:h.config.chart.zoom.zoomedArea.stroke.color,\"stroke-width\":h.config.chart.zoom.zoomedArea.stroke.width,\"stroke-opacity\":h.config.chart.zoom.zoomedArea.stroke.opacity}),m.setAttrs(c.node,g)),h.globals.selectionEnabled&&(d.attr({x:e,y:i,width:a>0?a:0,height:s>0?s:0,fill:h.config.chart.selection.fill.color,\"fill-opacity\":h.config.chart.selection.fill.opacity,stroke:h.config.chart.selection.stroke.color,\"stroke-width\":h.config.chart.selection.stroke.width,\"stroke-dasharray\":h.config.chart.selection.stroke.dashArray,\"stroke-opacity\":h.config.chart.selection.stroke.opacity}),m.setAttrs(d.node,g))}}},{key:\"hideSelectionRect\",value:function(t){t&&t.attr({x:0,y:0,width:0,height:0})}},{key:\"selectionDrawing\",value:function(t){var e=t.context,i=t.zoomtype,a=this.w,s=e,r=this.gridRect.getBoundingClientRect(),o=s.startX-1,n=s.startY,l=!1,h=!1,c=s.clientX-r.left-o,d=s.clientY-r.top-n,g={};return Math.abs(c+o)>a.globals.gridWidth?c=a.globals.gridWidth-o:s.clientX-r.left<0&&(c=o),o>s.clientX-r.left&&(l=!0,c=Math.abs(c)),n>s.clientY-r.top&&(h=!0,d=Math.abs(d)),g=\"x\"===i?{x:l?o-c:o,y:0,width:c,height:a.globals.gridHeight}:\"y\"===i?{x:0,y:h?n-d:n,width:a.globals.gridWidth,height:d}:{x:l?o-c:o,y:h?n-d:n,width:c,height:d},s.drawSelectionRect(g),s.selectionDragging(\"resizing\"),g}},{key:\"selectionDragging\",value:function(t,e){var i=this,a=this.w,s=this.xyRatios,r=this.selectionRect,o=0;\"resizing\"===t&&(o=30);var n=function(t){return parseFloat(r.node.getAttribute(t))},l={x:n(\"x\"),y:n(\"y\"),width:n(\"width\"),height:n(\"height\")};a.globals.selection=l,\"function\"==typeof a.config.chart.events.selection&&a.globals.selectionEnabled&&(clearTimeout(this.w.globals.selectionResizeTimer),this.w.globals.selectionResizeTimer=window.setTimeout((function(){var t=i.gridRect.getBoundingClientRect(),e=r.node.getBoundingClientRect(),o={xaxis:{min:a.globals.xAxisScale.niceMin+(e.left-t.left)*s.xRatio,max:a.globals.xAxisScale.niceMin+(e.right-t.left)*s.xRatio},yaxis:{min:a.globals.yAxisScale[0].niceMin+(t.bottom-e.bottom)*s.yRatio[0],max:a.globals.yAxisScale[0].niceMax-(e.top-t.top)*s.yRatio[0]}};a.config.chart.events.selection(i.ctx,o),a.config.chart.brush.enabled&&void 0!==a.config.chart.events.brushScrolled&&a.config.chart.events.brushScrolled(i.ctx,o)}),o))}},{key:\"selectionDrawn\",value:function(t){var e=t.context,i=t.zoomtype,a=this.w,s=e,r=this.xyRatios,o=this.ctx.toolbar;if(s.startX>s.endX){var n=s.startX;s.startX=s.endX,s.endX=n}if(s.startY>s.endY){var l=s.startY;s.startY=s.endY,s.endY=l}var h=void 0,c=void 0;a.globals.isRangeBar?(h=a.globals.yAxisScale[0].niceMin+s.startX*r.invertedYRatio,c=a.globals.yAxisScale[0].niceMin+s.endX*r.invertedYRatio):(h=a.globals.xAxisScale.niceMin+s.startX*r.xRatio,c=a.globals.xAxisScale.niceMin+s.endX*r.xRatio);var d=[],g=[];if(a.config.yaxis.forEach((function(t,e){d.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.startY),g.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.endY)})),s.dragged&&(s.dragX>10||s.dragY>10)&&h!==c)if(a.globals.zoomEnabled){var u=x.clone(a.globals.initialConfig.yaxis),f=x.clone(a.globals.initialConfig.xaxis);if(a.globals.zoomed=!0,a.config.xaxis.convertedCatToNumeric&&(h=Math.floor(h),c=Math.floor(c),h<1&&(h=1,c=a.globals.dataPoints),c-h<2&&(c=h+1)),\"xy\"!==i&&\"x\"!==i||(f={min:h,max:c}),\"xy\"!==i&&\"y\"!==i||u.forEach((function(t,e){u[e].min=g[e],u[e].max=d[e]})),a.config.chart.zoom.autoScaleYaxis){var p=new _(s.ctx);u=p.autoScaleY(s.ctx,u,{xaxis:f})}if(o){var b=o.getBeforeZoomRange(f,u);b&&(f=b.xaxis?b.xaxis:f,u=b.yaxis?b.yaxis:u)}var v={xaxis:f};a.config.chart.group||(v.yaxis=u),s.ctx.updateHelpers._updateOptions(v,!1,s.w.config.chart.animations.dynamicAnimation.enabled),\"function\"==typeof a.config.chart.events.zoomed&&o.zoomCallback(f,u)}else if(a.globals.selectionEnabled){var m,y=null;m={min:h,max:c},\"xy\"!==i&&\"y\"!==i||(y=x.clone(a.config.yaxis)).forEach((function(t,e){y[e].min=g[e],y[e].max=d[e]})),a.globals.selection=s.selection,\"function\"==typeof a.config.chart.events.selection&&a.config.chart.events.selection(s.ctx,{xaxis:m,yaxis:y})}}},{key:\"panDragging\",value:function(t){var e=t.context,i=this.w,a=e;if(void 0!==i.globals.lastClientPosition.x){var s=i.globals.lastClientPosition.x-a.clientX,r=i.globals.lastClientPosition.y-a.clientY;Math.abs(s)>Math.abs(r)&&s>0?this.moveDirection=\"left\":Math.abs(s)>Math.abs(r)&&s<0?this.moveDirection=\"right\":Math.abs(r)>Math.abs(s)&&r>0?this.moveDirection=\"up\":Math.abs(r)>Math.abs(s)&&r<0&&(this.moveDirection=\"down\")}i.globals.lastClientPosition={x:a.clientX,y:a.clientY};var o=i.globals.isRangeBar?i.globals.minY:i.globals.minX,n=i.globals.isRangeBar?i.globals.maxY:i.globals.maxX;i.config.xaxis.convertedCatToNumeric||a.panScrolled(o,n)}},{key:\"delayedPanScrolled\",value:function(){var t=this.w,e=t.globals.minX,i=t.globals.maxX,a=(t.globals.maxX-t.globals.minX)/2;\"left\"===this.moveDirection?(e=t.globals.minX+a,i=t.globals.maxX+a):\"right\"===this.moveDirection&&(e=t.globals.minX-a,i=t.globals.maxX-a),e=Math.floor(e),i=Math.floor(i),this.updateScrolledChart({xaxis:{min:e,max:i}},e,i)}},{key:\"panScrolled\",value:function(t,e){var i=this.w,a=this.xyRatios,s=x.clone(i.globals.initialConfig.yaxis),r=a.xRatio,o=i.globals.minX,n=i.globals.maxX;i.globals.isRangeBar&&(r=a.invertedYRatio,o=i.globals.minY,n=i.globals.maxY),\"left\"===this.moveDirection?(t=o+i.globals.gridWidth/15*r,e=n+i.globals.gridWidth/15*r):\"right\"===this.moveDirection&&(t=o-i.globals.gridWidth/15*r,e=n-i.globals.gridWidth/15*r),i.globals.isRangeBar||(t<i.globals.initialMinX||e>i.globals.initialMaxX)&&(t=o,e=n);var l={min:t,max:e};i.config.chart.zoom.autoScaleYaxis&&(s=new _(this.ctx).autoScaleY(this.ctx,s,{xaxis:l}));var h={xaxis:{min:t,max:e}};i.config.chart.group||(h.yaxis=s),this.updateScrolledChart(h,t,e)}},{key:\"updateScrolledChart\",value:function(t,e,i){var a=this.w;this.ctx.updateHelpers._updateOptions(t,!1,!1),\"function\"==typeof a.config.chart.events.scrolled&&a.config.chart.events.scrolled(this.ctx,{xaxis:{min:e,max:i}})}}]),i}(),dt=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e,this.ctx=e.ctx}return r(t,[{key:\"getNearestValues\",value:function(t){var e=t.hoverArea,i=t.elGrid,a=t.clientX,s=t.clientY,r=this.w,o=i.getBoundingClientRect(),n=o.width,l=o.height,h=n/(r.globals.dataPoints-1),c=l/r.globals.dataPoints,d=this.hasBars();!r.globals.comboCharts&&!d||r.config.xaxis.convertedCatToNumeric||(h=n/r.globals.dataPoints);var g=a-o.left-r.globals.barPadForNumericAxis,u=s-o.top;g<0||u<0||g>n||u>l?(e.classList.remove(\"hovering-zoom\"),e.classList.remove(\"hovering-pan\")):r.globals.zoomEnabled?(e.classList.remove(\"hovering-pan\"),e.classList.add(\"hovering-zoom\")):r.globals.panEnabled&&(e.classList.remove(\"hovering-zoom\"),e.classList.add(\"hovering-pan\"));var f=Math.round(g/h),p=Math.floor(u/c);d&&!r.config.xaxis.convertedCatToNumeric&&(f=Math.ceil(g/h),f-=1);var b=null,v=null,m=[],y=[];if(r.globals.seriesXvalues.forEach((function(t){m.push([t[0]+1e-6].concat(t))})),r.globals.seriesYvalues.forEach((function(t){y.push([t[0]+1e-6].concat(t))})),m=m.map((function(t){return t.filter((function(t){return x.isNumber(t)}))})),y=y.map((function(t){return t.filter((function(t){return x.isNumber(t)}))})),r.globals.isXNumeric){var w=this.ttCtx.getElGrid().getBoundingClientRect(),k=g*(w.width/n),A=u*(w.height/l);b=(v=this.closestInMultiArray(k,A,m,y)).index,f=v.j,null!==b&&(m=r.globals.seriesXvalues[b],f=(v=this.closestInArray(k,m)).index)}return r.globals.capturedSeriesIndex=null===b?-1:b,(!f||f<1)&&(f=0),r.globals.isBarHorizontal?r.globals.capturedDataPointIndex=p:r.globals.capturedDataPointIndex=f,{capturedSeries:b,j:r.globals.isBarHorizontal?p:f,hoverX:g,hoverY:u}}},{key:\"closestInMultiArray\",value:function(t,e,i,a){var s=this.w,r=0,o=null,n=-1;s.globals.series.length>1?r=this.getFirstActiveXArray(i):o=0;var l=i[r][0],h=Math.abs(t-l);if(i.forEach((function(e){e.forEach((function(e,i){var a=Math.abs(t-e);a<h&&(h=a,n=i)}))})),-1!==n){var c=a[r][n],d=Math.abs(e-c);o=r,a.forEach((function(t,i){var a=Math.abs(e-t[n]);a<d&&(d=a,o=i)}))}return{index:o,j:n}}},{key:\"getFirstActiveXArray\",value:function(t){for(var e=this.w,i=0,a=t.map((function(t,e){return t.length>0?e:-1})),s=0;s<a.length;s++)if(-1!==a[s]&&-1===e.globals.collapsedSeriesIndices.indexOf(s)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(s)){i=a[s];break}return i}},{key:\"closestInArray\",value:function(t,e){for(var i=e[0],a=null,s=Math.abs(t-i),r=0;r<e.length;r++){var o=Math.abs(t-e[r]);o<s&&(s=o,a=r)}return{index:a}}},{key:\"isXoverlap\",value:function(t){var e=[],i=this.w.globals.seriesX.filter((function(t){return void 0!==t[0]}));if(i.length>0)for(var a=0;a<i.length-1;a++)void 0!==i[a][t]&&void 0!==i[a+1][t]&&i[a][t]!==i[a+1][t]&&e.push(\"unEqual\");return 0===e.length}},{key:\"isInitialSeriesSameLen\",value:function(){for(var t=!0,e=this.w.globals.initialSeries,i=0;i<e.length-1;i++)if(e[i].data.length!==e[i+1].data.length){t=!1;break}return t}},{key:\"getBarsHeight\",value:function(t){return u(t).reduce((function(t,e){return t+e.getBBox().height}),0)}},{key:\"getElMarkers\",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(\" .apexcharts-series-markers\")}},{key:\"getAllMarkers\",value:function(){var t=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series-markers-wrap\");(t=u(t)).sort((function(t,e){var i=Number(t.getAttribute(\"data:realIndex\")),a=Number(e.getAttribute(\"data:realIndex\"));return a<i?1:a>i?-1:0}));var e=[];return t.forEach((function(t){e.push(t.querySelector(\".apexcharts-marker\"))})),e}},{key:\"hasMarkers\",value:function(){return this.getElMarkers().length>0}},{key:\"getElBars\",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-bar-series,  .apexcharts-candlestick-series, .apexcharts-boxPlot-series, .apexcharts-rangebar-series\")}},{key:\"hasBars\",value:function(){return this.getElBars().length>0}},{key:\"getHoverMarkerSize\",value:function(t){var e=this.w,i=e.config.markers.hover.size;return void 0===i&&(i=e.globals.markers.size[t]+e.config.markers.hover.sizeOffset),i}},{key:\"toggleAllTooltipSeriesGroups\",value:function(t){var e=this.w,i=this.ttCtx;0===i.allTooltipSeriesGroups.length&&(i.allTooltipSeriesGroups=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-tooltip-series-group\"));for(var a=i.allTooltipSeriesGroups,s=0;s<a.length;s++)\"enable\"===t?(a[s].classList.add(\"apexcharts-active\"),a[s].style.display=e.config.tooltip.items.display):(a[s].classList.remove(\"apexcharts-active\"),a[s].style.display=\"none\")}}]),t}(),gt=function(){function t(e){a(this,t),this.w=e.w,this.ctx=e.ctx,this.ttCtx=e,this.tooltipUtil=new dt(e)}return r(t,[{key:\"drawSeriesTexts\",value:function(t){var e=t.shared,i=void 0===e||e,a=t.ttItems,s=t.i,r=void 0===s?0:s,o=t.j,n=void 0===o?null:o,l=t.y1,h=t.y2,c=t.e,d=this.w;void 0!==d.config.tooltip.custom?this.handleCustomTooltip({i:r,j:n,y1:l,y2:h,w:d}):this.toggleActiveInactiveSeries(i);var g=this.getValuesToPrint({i:r,j:n});this.printLabels({i:r,j:n,values:g,ttItems:a,shared:i,e:c});var u=this.ttCtx.getElTooltip();this.ttCtx.tooltipRect.ttWidth=u.getBoundingClientRect().width,this.ttCtx.tooltipRect.ttHeight=u.getBoundingClientRect().height}},{key:\"printLabels\",value:function(t){var i,a=this,s=t.i,r=t.j,o=t.values,n=t.ttItems,l=t.shared,h=t.e,c=this.w,d=[],g=function(t){return c.globals.seriesGoals[t]&&c.globals.seriesGoals[t][r]&&Array.isArray(c.globals.seriesGoals[t][r])},u=o.xVal,f=o.zVal,p=o.xAxisTTVal,x=\"\",b=c.globals.colors[s];null!==r&&c.config.plotOptions.bar.distributed&&(b=c.globals.colors[r]);for(var v=function(t,o){var v=a.getFormatters(s);x=a.getSeriesName({fn:v.yLbTitleFormatter,index:s,seriesIndex:s,j:r}),\"treemap\"===c.config.chart.type&&(x=v.yLbTitleFormatter(String(c.config.series[s].data[r].x),{series:c.globals.series,seriesIndex:s,dataPointIndex:r,w:c}));var m=c.config.tooltip.inverseOrder?o:t;if(c.globals.axisCharts){var y=function(t){var e,i,a=\"\";c.globals.isRangeData&&(a+=v.yLbFormatter(null===(e=c.globals.seriesRangeStart)||void 0===e||null===(i=e[t])||void 0===i?void 0:i[r],{series:c.globals.seriesRangeStart,seriesIndex:t,dataPointIndex:r,w:c})+\" - \");return a+=v.yLbFormatter(c.globals.series[t][r],{series:c.globals.series,seriesIndex:t,dataPointIndex:r,w:c})};if(l)v=a.getFormatters(m),x=a.getSeriesName({fn:v.yLbTitleFormatter,index:m,seriesIndex:s,j:r}),b=c.globals.colors[m],i=y(m),g(m)&&(d=c.globals.seriesGoals[m][r].map((function(t){return{attrs:t,val:v.yLbFormatter(t.value,{seriesIndex:m,dataPointIndex:r,w:c})}})));else{var w,k=null==h||null===(w=h.target)||void 0===w?void 0:w.getAttribute(\"fill\");k&&(b=-1!==k.indexOf(\"url\")?document.querySelector(k.substr(4).slice(0,-1)).childNodes[0].getAttribute(\"stroke\"):k),i=y(s),g(s)&&Array.isArray(c.globals.seriesGoals[s][r])&&(d=c.globals.seriesGoals[s][r].map((function(t){return{attrs:t,val:v.yLbFormatter(t.value,{seriesIndex:s,dataPointIndex:r,w:c})}})))}}null===r&&(i=v.yLbFormatter(c.globals.series[s],e(e({},c),{},{seriesIndex:s,dataPointIndex:s}))),a.DOMHandling({i:s,t:m,j:r,ttItems:n,values:{val:i,goalVals:d,xVal:u,xAxisTTVal:p,zVal:f},seriesName:x,shared:l,pColor:b})},m=0,y=c.globals.series.length-1;m<c.globals.series.length;m++,y--)v(m,y)}},{key:\"getFormatters\",value:function(t){var e,i=this.w,a=i.globals.yLabelFormatters[t];return void 0!==i.globals.ttVal?Array.isArray(i.globals.ttVal)?(a=i.globals.ttVal[t]&&i.globals.ttVal[t].formatter,e=i.globals.ttVal[t]&&i.globals.ttVal[t].title&&i.globals.ttVal[t].title.formatter):(a=i.globals.ttVal.formatter,\"function\"==typeof i.globals.ttVal.title.formatter&&(e=i.globals.ttVal.title.formatter)):e=i.config.tooltip.y.title.formatter,\"function\"!=typeof a&&(a=i.globals.yLabelFormatters[0]?i.globals.yLabelFormatters[0]:function(t){return t}),\"function\"!=typeof e&&(e=function(t){return t}),{yLbFormatter:a,yLbTitleFormatter:e}}},{key:\"getSeriesName\",value:function(t){var e=t.fn,i=t.index,a=t.seriesIndex,s=t.j,r=this.w;return e(String(r.globals.seriesNames[i]),{series:r.globals.series,seriesIndex:a,dataPointIndex:s,w:r})}},{key:\"DOMHandling\",value:function(t){t.i;var e=t.t,i=t.j,a=t.ttItems,s=t.values,r=t.seriesName,o=t.shared,n=t.pColor,l=this.w,h=this.ttCtx,c=s.val,d=s.goalVals,g=s.xVal,u=s.xAxisTTVal,f=s.zVal,p=null;p=a[e].children,l.config.tooltip.fillSeriesColor&&(a[e].style.backgroundColor=n,p[0].style.display=\"none\"),h.showTooltipTitle&&(null===h.tooltipTitle&&(h.tooltipTitle=l.globals.dom.baseEl.querySelector(\".apexcharts-tooltip-title\")),h.tooltipTitle.innerHTML=g),h.isXAxisTooltipEnabled&&(h.xaxisTooltipText.innerHTML=\"\"!==u?u:g);var x=a[e].querySelector(\".apexcharts-tooltip-text-y-label\");x&&(x.innerHTML=r||\"\");var b=a[e].querySelector(\".apexcharts-tooltip-text-y-value\");b&&(b.innerHTML=void 0!==c?c:\"\"),p[0]&&p[0].classList.contains(\"apexcharts-tooltip-marker\")&&(l.config.tooltip.marker.fillColors&&Array.isArray(l.config.tooltip.marker.fillColors)&&(n=l.config.tooltip.marker.fillColors[e]),p[0].style.backgroundColor=n),l.config.tooltip.marker.show||(p[0].style.display=\"none\");var v=a[e].querySelector(\".apexcharts-tooltip-text-goals-label\"),m=a[e].querySelector(\".apexcharts-tooltip-text-goals-value\");if(d.length&&l.globals.seriesGoals[e]){var y=function(){var t=\"<div >\",e=\"<div>\";d.forEach((function(i,a){t+=' <div style=\"display: flex\"><span class=\"apexcharts-tooltip-marker\" style=\"background-color: '.concat(i.attrs.strokeColor,'; height: 3px; border-radius: 0; top: 5px;\"></span> ').concat(i.attrs.name,\"</div>\"),e+=\"<div>\".concat(i.val,\"</div>\")})),v.innerHTML=t+\"</div>\",m.innerHTML=e+\"</div>\"};o?l.globals.seriesGoals[e][i]&&Array.isArray(l.globals.seriesGoals[e][i])?y():(v.innerHTML=\"\",m.innerHTML=\"\"):y()}else v.innerHTML=\"\",m.innerHTML=\"\";null!==f&&(a[e].querySelector(\".apexcharts-tooltip-text-z-label\").innerHTML=l.config.tooltip.z.title,a[e].querySelector(\".apexcharts-tooltip-text-z-value\").innerHTML=void 0!==f?f:\"\");o&&p[0]&&(null==c||l.globals.ancillaryCollapsedSeriesIndices.indexOf(e)>-1||l.globals.collapsedSeriesIndices.indexOf(e)>-1?p[0].parentNode.style.display=\"none\":p[0].parentNode.style.display=l.config.tooltip.items.display)}},{key:\"toggleActiveInactiveSeries\",value:function(t){var e=this.w;if(t)this.tooltipUtil.toggleAllTooltipSeriesGroups(\"enable\");else{this.tooltipUtil.toggleAllTooltipSeriesGroups(\"disable\");var i=e.globals.dom.baseEl.querySelector(\".apexcharts-tooltip-series-group\");i&&(i.classList.add(\"apexcharts-active\"),i.style.display=e.config.tooltip.items.display)}}},{key:\"getValuesToPrint\",value:function(t){var e=t.i,i=t.j,a=this.w,s=this.ctx.series.filteredSeriesX(),r=\"\",o=\"\",n=null,l=null,h={series:a.globals.series,seriesIndex:e,dataPointIndex:i,w:a},c=a.globals.ttZFormatter;null===i?l=a.globals.series[e]:a.globals.isXNumeric&&\"treemap\"!==a.config.chart.type?(r=s[e][i],0===s[e].length&&(r=s[this.tooltipUtil.getFirstActiveXArray(s)][i])):r=void 0!==a.globals.labels[i]?a.globals.labels[i]:\"\";var d=r;a.globals.isXNumeric&&\"datetime\"===a.config.xaxis.type?r=new M(this.ctx).xLabelFormat(a.globals.ttKeyFormatter,d,d,{i:void 0,dateFormatter:new T(this.ctx).formatDate,w:this.w}):r=a.globals.isBarHorizontal?a.globals.yLabelFormatters[0](d,h):a.globals.xLabelFormatter(d,h);return void 0!==a.config.tooltip.x.formatter&&(r=a.globals.ttKeyFormatter(d,h)),a.globals.seriesZ.length>0&&a.globals.seriesZ[e].length>0&&(n=c(a.globals.seriesZ[e][i],a)),o=\"function\"==typeof a.config.xaxis.tooltip.formatter?a.globals.xaxisTooltipFormatter(d,h):r,{val:Array.isArray(l)?l.join(\" \"):l,xVal:Array.isArray(r)?r.join(\" \"):r,xAxisTTVal:Array.isArray(o)?o.join(\" \"):o,zVal:n}}},{key:\"handleCustomTooltip\",value:function(t){var e=t.i,i=t.j,a=t.y1,s=t.y2,r=t.w,o=this.ttCtx.getElTooltip(),n=r.config.tooltip.custom;Array.isArray(n)&&n[e]&&(n=n[e]),o.innerHTML=n({ctx:this.ctx,series:r.globals.series,seriesIndex:e,dataPointIndex:i,y1:a,y2:s,w:r})}}]),t}(),ut=function(){function t(e){a(this,t),this.ttCtx=e,this.ctx=e.ctx,this.w=e.w}return r(t,[{key:\"moveXCrosshairs\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.ttCtx,a=this.w,s=i.getElXCrosshairs(),r=t-i.xcrosshairsWidth/2,o=a.globals.labels.slice().length;if(null!==e&&(r=a.globals.gridWidth/o*e),null===s||a.globals.isBarHorizontal||(s.setAttribute(\"x\",r),s.setAttribute(\"x1\",r),s.setAttribute(\"x2\",r),s.setAttribute(\"y2\",a.globals.gridHeight),s.classList.add(\"apexcharts-active\")),r<0&&(r=0),r>a.globals.gridWidth&&(r=a.globals.gridWidth),i.isXAxisTooltipEnabled){var n=r;\"tickWidth\"!==a.config.xaxis.crosshairs.width&&\"barWidth\"!==a.config.xaxis.crosshairs.width||(n=r+i.xcrosshairsWidth/2),this.moveXAxisTooltip(n)}}},{key:\"moveYCrosshairs\",value:function(t){var e=this.ttCtx;null!==e.ycrosshairs&&m.setAttrs(e.ycrosshairs,{y1:t,y2:t}),null!==e.ycrosshairsHidden&&m.setAttrs(e.ycrosshairsHidden,{y1:t,y2:t})}},{key:\"moveXAxisTooltip\",value:function(t){var e=this.w,i=this.ttCtx;if(null!==i.xaxisTooltip&&0!==i.xcrosshairsWidth){i.xaxisTooltip.classList.add(\"apexcharts-active\");var a=i.xaxisOffY+e.config.xaxis.tooltip.offsetY+e.globals.translateY+1+e.config.xaxis.offsetY;if(t-=i.xaxisTooltip.getBoundingClientRect().width/2,!isNaN(t)){t+=e.globals.translateX;var s;s=new m(this.ctx).getTextRects(i.xaxisTooltipText.innerHTML),i.xaxisTooltipText.style.minWidth=s.width+\"px\",i.xaxisTooltip.style.left=t+\"px\",i.xaxisTooltip.style.top=a+\"px\"}}}},{key:\"moveYAxisTooltip\",value:function(t){var e=this.w,i=this.ttCtx;null===i.yaxisTTEls&&(i.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxistooltip\"));var a=parseInt(i.ycrosshairsHidden.getAttribute(\"y1\"),10),s=e.globals.translateY+a,r=i.yaxisTTEls[t].getBoundingClientRect().height,o=e.globals.translateYAxisX[t]-2;e.config.yaxis[t].opposite&&(o-=26),s-=r/2,-1===e.globals.ignoreYAxisIndexes.indexOf(t)?(i.yaxisTTEls[t].classList.add(\"apexcharts-active\"),i.yaxisTTEls[t].style.top=s+\"px\",i.yaxisTTEls[t].style.left=o+e.config.yaxis[t].tooltip.offsetX+\"px\"):i.yaxisTTEls[t].classList.remove(\"apexcharts-active\")}},{key:\"moveTooltip\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=this.ttCtx,r=s.getElTooltip(),o=s.tooltipRect,n=null!==i?parseFloat(i):1,l=parseFloat(t)+n+5,h=parseFloat(e)+n/2;if(l>a.globals.gridWidth/2&&(l=l-o.ttWidth-n-10),l>a.globals.gridWidth-o.ttWidth-10&&(l=a.globals.gridWidth-o.ttWidth),l<-20&&(l=-20),a.config.tooltip.followCursor){var c=s.getElGrid(),d=c.getBoundingClientRect();h=s.e.clientY+a.globals.translateY-d.top-o.ttHeight/2}else a.globals.isBarHorizontal||o.ttHeight/2+h>a.globals.gridHeight&&(h=a.globals.gridHeight-o.ttHeight+a.globals.translateY);isNaN(l)||(l+=a.globals.translateX,r.style.left=l+\"px\",r.style.top=h+\"px\")}},{key:\"moveMarkers\",value:function(t,e){var i=this.w,a=this.ttCtx;if(i.globals.markers.size[t]>0)for(var s=i.globals.dom.baseEl.querySelectorAll(\" .apexcharts-series[data\\\\:realIndex='\".concat(t,\"'] .apexcharts-marker\")),r=0;r<s.length;r++)parseInt(s[r].getAttribute(\"rel\"),10)===e&&(a.marker.resetPointsSize(),a.marker.enlargeCurrentPoint(e,s[r]));else a.marker.resetPointsSize(),this.moveDynamicPointOnHover(e,t)}},{key:\"moveDynamicPointOnHover\",value:function(t,e){var i,a,s=this.w,r=this.ttCtx,o=s.globals.pointsArray,n=r.tooltipUtil.getHoverMarkerSize(e),l=s.config.series[e].type;if(!l||\"column\"!==l&&\"candlestick\"!==l&&\"boxPlot\"!==l){i=o[e][t][0],a=o[e][t][1]?o[e][t][1]:0;var h=s.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(e,\"'] .apexcharts-series-markers circle\"));h&&a<s.globals.gridHeight&&a>0&&(h.setAttribute(\"r\",n),h.setAttribute(\"cx\",i),h.setAttribute(\"cy\",a)),this.moveXCrosshairs(i),r.fixedTooltip||this.moveTooltip(i,a,n)}}},{key:\"moveDynamicPointsOnHover\",value:function(t){var e,i=this.ttCtx,a=i.w,s=0,r=0,o=a.globals.pointsArray;e=new N(this.ctx).getActiveConfigSeriesIndex(\"asc\",[\"line\",\"area\",\"scatter\",\"bubble\"]);var n=i.tooltipUtil.getHoverMarkerSize(e);o[e]&&(s=o[e][t][0],r=o[e][t][1]);var l=i.tooltipUtil.getAllMarkers();if(null!==l)for(var h=0;h<a.globals.series.length;h++){var c=o[h];if(a.globals.comboCharts&&void 0===c&&l.splice(h,0,null),c&&c.length){var d=o[h][t][1],g=void 0;if(l[h].setAttribute(\"cx\",s),\"rangeArea\"===a.config.chart.type&&!a.globals.comboCharts){var u=t+a.globals.series[h].length;g=o[h][u][1],d-=Math.abs(d-g)/2}null!==d&&!isNaN(d)&&d<a.globals.gridHeight+n&&d+n>0?(l[h]&&l[h].setAttribute(\"r\",n),l[h]&&l[h].setAttribute(\"cy\",d)):l[h]&&l[h].setAttribute(\"r\",0)}}if(this.moveXCrosshairs(s),!i.fixedTooltip){var f=r||a.globals.gridHeight;this.moveTooltip(s,f,n)}}},{key:\"moveStickyTooltipOverBars\",value:function(t){var e=this.w,i=this.ttCtx,a=e.globals.columnSeries?e.globals.columnSeries.length:e.globals.series.length,s=a>=2&&a%2==0?Math.floor(a/2):Math.floor(a/2)+1;e.globals.isBarHorizontal&&(s=new N(this.ctx).getActiveConfigSeriesIndex(\"desc\")+1);var r=e.globals.dom.baseEl.querySelector(\".apexcharts-bar-series .apexcharts-series[rel='\".concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-candlestick-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-boxPlot-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-rangebar-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"']\")),o=r?parseFloat(r.getAttribute(\"cx\")):0,n=r?parseFloat(r.getAttribute(\"cy\")):0,l=r?parseFloat(r.getAttribute(\"barWidth\")):0,h=i.getElGrid().getBoundingClientRect(),c=r.classList.contains(\"apexcharts-candlestick-area\")||r.classList.contains(\"apexcharts-boxPlot-area\");if(e.globals.isXNumeric?(r&&!c&&(o-=a%2!=0?l/2:0),r&&c&&e.globals.comboCharts&&(o-=l/2)):e.globals.isBarHorizontal||(o=i.xAxisTicksPositions[t-1]+i.dataPointsDividedWidth/2,isNaN(o)&&(o=i.xAxisTicksPositions[t]-i.dataPointsDividedWidth/2)),e.globals.isBarHorizontal?n-=i.tooltipRect.ttHeight:e.config.tooltip.followCursor?n=i.e.clientY-h.top-i.tooltipRect.ttHeight/2:n+i.tooltipRect.ttHeight+15>e.globals.gridHeight&&(n=e.globals.gridHeight),e.globals.isBarHorizontal||this.moveXCrosshairs(o),!i.fixedTooltip){var d=n||e.globals.gridHeight;this.moveTooltip(o,d)}}}]),t}(),ft=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e,this.ctx=e.ctx,this.tooltipPosition=new ut(e)}return r(t,[{key:\"drawDynamicPoints\",value:function(){var t=this.w,e=new m(this.ctx),i=new D(this.ctx),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series\");a=u(a),t.config.chart.stacked&&a.sort((function(t,e){return parseFloat(t.getAttribute(\"data:realIndex\"))-parseFloat(e.getAttribute(\"data:realIndex\"))}));for(var s=0;s<a.length;s++){var r=a[s].querySelector(\".apexcharts-series-markers-wrap\");if(null!==r){var o=void 0,n=\"apexcharts-marker w\".concat((Math.random()+1).toString(36).substring(4));\"line\"!==t.config.chart.type&&\"area\"!==t.config.chart.type||t.globals.comboCharts||t.config.tooltip.intersect||(n+=\" no-pointer-events\");var l=i.getMarkerConfig({cssClass:n,seriesIndex:Number(r.getAttribute(\"data:realIndex\"))});(o=e.drawMarker(0,0,l)).node.setAttribute(\"default-marker-size\",0);var h=document.createElementNS(t.globals.SVGNS,\"g\");h.classList.add(\"apexcharts-series-markers\"),h.appendChild(o.node),r.appendChild(h)}}}},{key:\"enlargeCurrentPoint\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w;\"bubble\"!==s.config.chart.type&&this.newPointSize(t,e);var r=e.getAttribute(\"cx\"),o=e.getAttribute(\"cy\");if(null!==i&&null!==a&&(r=i,o=a),this.tooltipPosition.moveXCrosshairs(r),!this.fixedTooltip){if(\"radar\"===s.config.chart.type){var n=this.ttCtx.getElGrid(),l=n.getBoundingClientRect();r=this.ttCtx.e.clientX-l.left}this.tooltipPosition.moveTooltip(r,o,s.config.markers.hover.size)}}},{key:\"enlargePoints\",value:function(t){for(var e=this.w,i=this,a=this.ttCtx,s=t,r=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker\"),o=e.config.markers.hover.size,n=0;n<r.length;n++){var l=r[n].getAttribute(\"rel\"),h=r[n].getAttribute(\"index\");if(void 0===o&&(o=e.globals.markers.size[h]+e.config.markers.hover.sizeOffset),s===parseInt(l,10)){i.newPointSize(s,r[n]);var c=r[n].getAttribute(\"cx\"),d=r[n].getAttribute(\"cy\");i.tooltipPosition.moveXCrosshairs(c),a.fixedTooltip||i.tooltipPosition.moveTooltip(c,d,o)}else i.oldPointSize(r[n])}}},{key:\"newPointSize\",value:function(t,e){var i=this.w,a=i.config.markers.hover.size,s=0===t?e.parentNode.firstChild:e.parentNode.lastChild;if(\"0\"!==s.getAttribute(\"default-marker-size\")){var r=parseInt(s.getAttribute(\"index\"),10);void 0===a&&(a=i.globals.markers.size[r]+i.config.markers.hover.sizeOffset),a<0&&(a=0),s.setAttribute(\"r\",a)}}},{key:\"oldPointSize\",value:function(t){var e=parseFloat(t.getAttribute(\"default-marker-size\"));t.setAttribute(\"r\",e)}},{key:\"resetPointsSize\",value:function(){for(var t=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker\"),e=0;e<t.length;e++){var i=parseFloat(t[e].getAttribute(\"default-marker-size\"));x.isNumber(i)&&i>=0?t[e].setAttribute(\"r\",i):t[e].setAttribute(\"r\",0)}}}]),t}(),pt=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e}return r(t,[{key:\"getAttr\",value:function(t,e){return parseFloat(t.target.getAttribute(e))}},{key:\"handleHeatTreeTooltip\",value:function(t){var e=t.e,i=t.opt,a=t.x,s=t.y,r=t.type,o=this.ttCtx,n=this.w;if(e.target.classList.contains(\"apexcharts-\".concat(r,\"-rect\"))){var l=this.getAttr(e,\"i\"),h=this.getAttr(e,\"j\"),c=this.getAttr(e,\"cx\"),d=this.getAttr(e,\"cy\"),g=this.getAttr(e,\"width\"),u=this.getAttr(e,\"height\");if(o.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:l,j:h,shared:!1,e:e}),n.globals.capturedSeriesIndex=l,n.globals.capturedDataPointIndex=h,a=c+o.tooltipRect.ttWidth/2+g,s=d+o.tooltipRect.ttHeight/2-u/2,o.tooltipPosition.moveXCrosshairs(c+g/2),a>n.globals.gridWidth/2&&(a=c-o.tooltipRect.ttWidth/2+g),o.w.config.tooltip.followCursor){var f=n.globals.dom.elWrap.getBoundingClientRect();a=n.globals.clientX-f.left-(a>n.globals.gridWidth/2?o.tooltipRect.ttWidth:0),s=n.globals.clientY-f.top-(s>n.globals.gridHeight/2?o.tooltipRect.ttHeight:0)}}return{x:a,y:s}}},{key:\"handleMarkerTooltip\",value:function(t){var e,i,a=t.e,s=t.opt,r=t.x,o=t.y,n=this.w,l=this.ttCtx;if(a.target.classList.contains(\"apexcharts-marker\")){var h=parseInt(s.paths.getAttribute(\"cx\"),10),c=parseInt(s.paths.getAttribute(\"cy\"),10),d=parseFloat(s.paths.getAttribute(\"val\"));if(i=parseInt(s.paths.getAttribute(\"rel\"),10),e=parseInt(s.paths.parentNode.parentNode.parentNode.getAttribute(\"rel\"),10)-1,l.intersect){var g=x.findAncestor(s.paths,\"apexcharts-series\");g&&(e=parseInt(g.getAttribute(\"data:realIndex\"),10))}if(l.tooltipLabels.drawSeriesTexts({ttItems:s.ttItems,i:e,j:i,shared:!l.showOnIntersect&&n.config.tooltip.shared,e:a}),\"mouseup\"===a.type&&l.markerClick(a,e,i),n.globals.capturedSeriesIndex=e,n.globals.capturedDataPointIndex=i,r=h,o=c+n.globals.translateY-1.4*l.tooltipRect.ttHeight,l.w.config.tooltip.followCursor){var u=l.getElGrid().getBoundingClientRect();o=l.e.clientY+n.globals.translateY-u.top}d<0&&(o=c),l.marker.enlargeCurrentPoint(i,s.paths,r,o)}return{x:r,y:o}}},{key:\"handleBarTooltip\",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,o=this.ttCtx,n=o.getElTooltip(),l=0,h=0,c=0,d=this.getBarTooltipXY({e:a,opt:s});e=d.i;var g=d.barHeight,u=d.j;r.globals.capturedSeriesIndex=e,r.globals.capturedDataPointIndex=u,r.globals.isBarHorizontal&&o.tooltipUtil.hasBars()||!r.config.tooltip.shared?(h=d.x,c=d.y,i=Array.isArray(r.config.stroke.width)?r.config.stroke.width[e]:r.config.stroke.width,l=h):r.globals.comboCharts||r.config.tooltip.shared||(l/=2),isNaN(c)&&(c=r.globals.svgHeight-o.tooltipRect.ttHeight);var f=parseInt(s.paths.parentNode.getAttribute(\"data:realIndex\"),10),p=r.globals.isMultipleYAxis?r.config.yaxis[f]&&r.config.yaxis[f].reversed:r.config.yaxis[0].reversed;if(h+o.tooltipRect.ttWidth>r.globals.gridWidth&&!p?h-=o.tooltipRect.ttWidth:h<0&&(h=0),o.w.config.tooltip.followCursor){var x=o.getElGrid().getBoundingClientRect();c=o.e.clientY-x.top}null===o.tooltip&&(o.tooltip=r.globals.dom.baseEl.querySelector(\".apexcharts-tooltip\")),r.config.tooltip.shared||(r.globals.comboBarCount>0?o.tooltipPosition.moveXCrosshairs(l+i/2):o.tooltipPosition.moveXCrosshairs(l)),!o.fixedTooltip&&(!r.config.tooltip.shared||r.globals.isBarHorizontal&&o.tooltipUtil.hasBars())&&(p&&(h-=o.tooltipRect.ttWidth)<0&&(h=0),!p||r.globals.isBarHorizontal&&o.tooltipUtil.hasBars()||(c=c+g-2*(r.globals.series[e][u]<0?g:0)),c=c+r.globals.translateY-o.tooltipRect.ttHeight/2,n.style.left=h+r.globals.translateX+\"px\",n.style.top=c+\"px\")}},{key:\"getBarTooltipXY\",value:function(t){var e=t.e,i=t.opt,a=this.w,s=null,r=this.ttCtx,o=0,n=0,l=0,h=0,c=0,d=e.target.classList;if(d.contains(\"apexcharts-bar-area\")||d.contains(\"apexcharts-candlestick-area\")||d.contains(\"apexcharts-boxPlot-area\")||d.contains(\"apexcharts-rangebar-area\")){var g=e.target,u=g.getBoundingClientRect(),f=i.elGrid.getBoundingClientRect(),p=u.height;c=u.height;var x=u.width,b=parseInt(g.getAttribute(\"cx\"),10),v=parseInt(g.getAttribute(\"cy\"),10);h=parseFloat(g.getAttribute(\"barWidth\"));var m=\"touchmove\"===e.type?e.touches[0].clientX:e.clientX;s=parseInt(g.getAttribute(\"j\"),10),o=parseInt(g.parentNode.getAttribute(\"rel\"),10)-1;var y=g.getAttribute(\"data-range-y1\"),w=g.getAttribute(\"data-range-y2\");a.globals.comboCharts&&(o=parseInt(g.parentNode.getAttribute(\"data:realIndex\"),10)),r.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:o,j:s,y1:y?parseInt(y,10):null,y2:w?parseInt(w,10):null,shared:!r.showOnIntersect&&a.config.tooltip.shared,e:e}),a.config.tooltip.followCursor?a.globals.isBarHorizontal?(n=m-f.left+15,l=v-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(n=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=e.clientY-f.top-r.tooltipRect.ttHeight/2-15):a.globals.isBarHorizontal?((n=b)<r.xyRatios.baseLineInvertedY&&(n=b-r.tooltipRect.ttWidth),l=v-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(n=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=v)}return{x:n,y:l,barHeight:c,barWidth:h,i:o,j:s}}}]),t}(),xt=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e}return r(t,[{key:\"drawXaxisTooltip\",value:function(){var t=this.w,e=this.ttCtx,i=\"bottom\"===t.config.xaxis.position;e.xaxisOffY=i?t.globals.gridHeight+1:-t.globals.xAxisHeight-t.config.xaxis.axisTicks.height+3;var a=i?\"apexcharts-xaxistooltip apexcharts-xaxistooltip-bottom\":\"apexcharts-xaxistooltip apexcharts-xaxistooltip-top\",s=t.globals.dom.elWrap;e.isXAxisTooltipEnabled&&(null===t.globals.dom.baseEl.querySelector(\".apexcharts-xaxistooltip\")&&(e.xaxisTooltip=document.createElement(\"div\"),e.xaxisTooltip.setAttribute(\"class\",a+\" apexcharts-theme-\"+t.config.tooltip.theme),s.appendChild(e.xaxisTooltip),e.xaxisTooltipText=document.createElement(\"div\"),e.xaxisTooltipText.classList.add(\"apexcharts-xaxistooltip-text\"),e.xaxisTooltipText.style.fontFamily=t.config.xaxis.tooltip.style.fontFamily||t.config.chart.fontFamily,e.xaxisTooltipText.style.fontSize=t.config.xaxis.tooltip.style.fontSize,e.xaxisTooltip.appendChild(e.xaxisTooltipText)))}},{key:\"drawYaxisTooltip\",value:function(){for(var t=this.w,e=this.ttCtx,i=function(i){var a=t.config.yaxis[i].opposite||t.config.yaxis[i].crosshairs.opposite;e.yaxisOffX=a?t.globals.gridWidth+1:1;var s=\"apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i,a?\" apexcharts-yaxistooltip-right\":\" apexcharts-yaxistooltip-left\");t.globals.yAxisSameScaleIndices.map((function(e,a){e.map((function(e,a){a===i&&(s+=t.config.yaxis[a].show?\" \":\" apexcharts-yaxistooltip-hidden\")}))}));var r=t.globals.dom.elWrap;null===t.globals.dom.baseEl.querySelector(\".apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i))&&(e.yaxisTooltip=document.createElement(\"div\"),e.yaxisTooltip.setAttribute(\"class\",s+\" apexcharts-theme-\"+t.config.tooltip.theme),r.appendChild(e.yaxisTooltip),0===i&&(e.yaxisTooltipText=[]),e.yaxisTooltipText[i]=document.createElement(\"div\"),e.yaxisTooltipText[i].classList.add(\"apexcharts-yaxistooltip-text\"),e.yaxisTooltip.appendChild(e.yaxisTooltipText[i]))},a=0;a<t.config.yaxis.length;a++)i(a)}},{key:\"setXCrosshairWidth\",value:function(){var t=this.w,e=this.ttCtx,i=e.getElXCrosshairs();if(e.xcrosshairsWidth=parseInt(t.config.xaxis.crosshairs.width,10),t.globals.comboCharts){var a=t.globals.dom.baseEl.querySelector(\".apexcharts-bar-area\");if(null!==a&&\"barWidth\"===t.config.xaxis.crosshairs.width){var s=parseFloat(a.getAttribute(\"barWidth\"));e.xcrosshairsWidth=s}else if(\"tickWidth\"===t.config.xaxis.crosshairs.width){var r=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/r}}else if(\"tickWidth\"===t.config.xaxis.crosshairs.width){var o=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/o}else if(\"barWidth\"===t.config.xaxis.crosshairs.width){var n=t.globals.dom.baseEl.querySelector(\".apexcharts-bar-area\");if(null!==n){var l=parseFloat(n.getAttribute(\"barWidth\"));e.xcrosshairsWidth=l}else e.xcrosshairsWidth=1}t.globals.isBarHorizontal&&(e.xcrosshairsWidth=0),null!==i&&e.xcrosshairsWidth>0&&i.setAttribute(\"width\",e.xcrosshairsWidth)}},{key:\"handleYCrosshair\",value:function(){var t=this.w,e=this.ttCtx;e.ycrosshairs=t.globals.dom.baseEl.querySelector(\".apexcharts-ycrosshairs\"),e.ycrosshairsHidden=t.globals.dom.baseEl.querySelector(\".apexcharts-ycrosshairs-hidden\")}},{key:\"drawYaxisTooltipText\",value:function(t,e,i){var a=this.ttCtx,s=this.w,r=s.globals.yLabelFormatters[t];if(a.yaxisTooltips[t]){var o=a.getElGrid().getBoundingClientRect(),n=(e-o.top)*i.yRatio[t],l=s.globals.maxYArr[t]-s.globals.minYArr[t],h=s.globals.minYArr[t]+(l-n);a.tooltipPosition.moveYCrosshairs(e-o.top),a.yaxisTooltipText[t].innerHTML=r(h),a.tooltipPosition.moveYAxisTooltip(t)}}}]),t}(),bt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.tConfig=i.config.tooltip,this.tooltipUtil=new dt(this),this.tooltipLabels=new gt(this),this.tooltipPosition=new ut(this),this.marker=new ft(this),this.intersect=new pt(this),this.axesTooltip=new xt(this),this.showOnIntersect=this.tConfig.intersect,this.showTooltipTitle=this.tConfig.x.show,this.fixedTooltip=this.tConfig.fixed.enabled,this.xaxisTooltip=null,this.yaxisTTEls=null,this.isBarShared=!i.globals.isBarHorizontal&&this.tConfig.shared,this.lastHoverTime=Date.now()}return r(t,[{key:\"getElTooltip\",value:function(t){return t||(t=this),t.w.globals.dom.baseEl?t.w.globals.dom.baseEl.querySelector(\".apexcharts-tooltip\"):null}},{key:\"getElXCrosshairs\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-xcrosshairs\")}},{key:\"getElGrid\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-grid\")}},{key:\"drawTooltip\",value:function(t){var e=this.w;this.xyRatios=t,this.isXAxisTooltipEnabled=e.config.xaxis.tooltip.enabled&&e.globals.axisCharts,this.yaxisTooltips=e.config.yaxis.map((function(t,i){return!!(t.show&&t.tooltip.enabled&&e.globals.axisCharts)})),this.allTooltipSeriesGroups=[],e.globals.axisCharts||(this.showTooltipTitle=!1);var i=document.createElement(\"div\");if(i.classList.add(\"apexcharts-tooltip\"),e.config.tooltip.cssClass&&i.classList.add(e.config.tooltip.cssClass),i.classList.add(\"apexcharts-theme-\".concat(this.tConfig.theme)),e.globals.dom.elWrap.appendChild(i),e.globals.axisCharts){this.axesTooltip.drawXaxisTooltip(),this.axesTooltip.drawYaxisTooltip(),this.axesTooltip.setXCrosshairWidth(),this.axesTooltip.handleYCrosshair();var a=new V(this.ctx);this.xAxisTicksPositions=a.getXAxisTicksPositions()}if(!e.globals.comboCharts&&!this.tConfig.intersect&&\"rangeBar\"!==e.config.chart.type||this.tConfig.shared||(this.showOnIntersect=!0),0!==e.config.markers.size&&0!==e.globals.markers.largestSize||this.marker.drawDynamicPoints(this),e.globals.collapsedSeries.length!==e.globals.series.length){this.dataPointsDividedHeight=e.globals.gridHeight/e.globals.dataPoints,this.dataPointsDividedWidth=e.globals.gridWidth/e.globals.dataPoints,this.showTooltipTitle&&(this.tooltipTitle=document.createElement(\"div\"),this.tooltipTitle.classList.add(\"apexcharts-tooltip-title\"),this.tooltipTitle.style.fontFamily=this.tConfig.style.fontFamily||e.config.chart.fontFamily,this.tooltipTitle.style.fontSize=this.tConfig.style.fontSize,i.appendChild(this.tooltipTitle));var s=e.globals.series.length;(e.globals.xyCharts||e.globals.comboCharts)&&this.tConfig.shared&&(s=this.showOnIntersect?1:e.globals.series.length),this.legendLabels=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-legend-text\"),this.ttItems=this.createTTElements(s),this.addSVGEvents()}}},{key:\"createTTElements\",value:function(t){for(var e=this,i=this.w,a=[],s=this.getElTooltip(),r=function(r){var o=document.createElement(\"div\");o.classList.add(\"apexcharts-tooltip-series-group\"),o.style.order=i.config.tooltip.inverseOrder?t-r:r+1,e.tConfig.shared&&e.tConfig.enabledOnSeries&&Array.isArray(e.tConfig.enabledOnSeries)&&e.tConfig.enabledOnSeries.indexOf(r)<0&&o.classList.add(\"apexcharts-tooltip-series-group-hidden\");var n=document.createElement(\"span\");n.classList.add(\"apexcharts-tooltip-marker\"),n.style.backgroundColor=i.globals.colors[r],o.appendChild(n);var l=document.createElement(\"div\");l.classList.add(\"apexcharts-tooltip-text\"),l.style.fontFamily=e.tConfig.style.fontFamily||i.config.chart.fontFamily,l.style.fontSize=e.tConfig.style.fontSize,[\"y\",\"goals\",\"z\"].forEach((function(t){var e=document.createElement(\"div\");e.classList.add(\"apexcharts-tooltip-\".concat(t,\"-group\"));var i=document.createElement(\"span\");i.classList.add(\"apexcharts-tooltip-text-\".concat(t,\"-label\")),e.appendChild(i);var a=document.createElement(\"span\");a.classList.add(\"apexcharts-tooltip-text-\".concat(t,\"-value\")),e.appendChild(a),l.appendChild(e)})),o.appendChild(l),s.appendChild(o),a.push(o)},o=0;o<t;o++)r(o);return a}},{key:\"addSVGEvents\",value:function(){var t=this.w,e=t.config.chart.type,i=this.getElTooltip(),a=!(\"bar\"!==e&&\"candlestick\"!==e&&\"boxPlot\"!==e&&\"rangeBar\"!==e),s=\"area\"===e||\"line\"===e||\"scatter\"===e||\"bubble\"===e||\"radar\"===e,r=t.globals.dom.Paper.node,o=this.getElGrid();o&&(this.seriesBound=o.getBoundingClientRect());var n,l=[],h=[],c={hoverArea:r,elGrid:o,tooltipEl:i,tooltipY:l,tooltipX:h,ttItems:this.ttItems};if(t.globals.axisCharts&&(s?n=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series[data\\\\:longestSeries='true'] .apexcharts-marker\"):a?n=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-boxPlot-area, .apexcharts-series .apexcharts-rangebar-area\"):\"heatmap\"!==e&&\"treemap\"!==e||(n=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series .apexcharts-heatmap, .apexcharts-series .apexcharts-treemap\")),n&&n.length))for(var d=0;d<n.length;d++)l.push(n[d].getAttribute(\"cy\")),h.push(n[d].getAttribute(\"cx\"));if(t.globals.xyCharts&&!this.showOnIntersect||t.globals.comboCharts&&!this.showOnIntersect||a&&this.tooltipUtil.hasBars()&&this.tConfig.shared)this.addPathsEventListeners([r],c);else if(a&&!t.globals.comboCharts||s&&this.showOnIntersect)this.addDatapointEventsListeners(c);else if(!t.globals.axisCharts||\"heatmap\"===e||\"treemap\"===e){var g=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series\");this.addPathsEventListeners(g,c)}if(this.showOnIntersect){var u=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-line-series .apexcharts-marker, .apexcharts-area-series .apexcharts-marker\");u.length>0&&this.addPathsEventListeners(u,c),this.tooltipUtil.hasBars()&&!this.tConfig.shared&&this.addDatapointEventsListeners(c)}}},{key:\"drawFixedTooltipRect\",value:function(){var t=this.w,e=this.getElTooltip(),i=e.getBoundingClientRect(),a=i.width+10,s=i.height+10,r=this.tConfig.fixed.offsetX,o=this.tConfig.fixed.offsetY,n=this.tConfig.fixed.position.toLowerCase();return n.indexOf(\"right\")>-1&&(r=r+t.globals.svgWidth-a+10),n.indexOf(\"bottom\")>-1&&(o=o+t.globals.svgHeight-s-10),e.style.left=r+\"px\",e.style.top=o+\"px\",{x:r,y:o,ttWidth:a,ttHeight:s}}},{key:\"addDatapointEventsListeners\",value:function(t){var e=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series-markers .apexcharts-marker, .apexcharts-bar-area, .apexcharts-candlestick-area, .apexcharts-boxPlot-area, .apexcharts-rangebar-area\");this.addPathsEventListeners(e,t)}},{key:\"addPathsEventListeners\",value:function(t,e){for(var i=this,a=function(a){var s={paths:t[a],tooltipEl:e.tooltipEl,tooltipY:e.tooltipY,tooltipX:e.tooltipX,elGrid:e.elGrid,hoverArea:e.hoverArea,ttItems:e.ttItems};[\"mousemove\",\"mouseup\",\"touchmove\",\"mouseout\",\"touchend\"].map((function(e){return t[a].addEventListener(e,i.onSeriesHover.bind(i,s),{capture:!1,passive:!0})}))},s=0;s<t.length;s++)a(s)}},{key:\"onSeriesHover\",value:function(t,e){var i=this,a=Date.now()-this.lastHoverTime;a>=100?this.seriesHover(t,e):(clearTimeout(this.seriesHoverTimeout),this.seriesHoverTimeout=setTimeout((function(){i.seriesHover(t,e)}),100-a))}},{key:\"seriesHover\",value:function(t,e){var i=this;this.lastHoverTime=Date.now();var a=[],s=this.w;s.config.chart.group&&(a=this.ctx.getGroupedCharts()),s.globals.axisCharts&&(s.globals.minX===-1/0&&s.globals.maxX===1/0||0===s.globals.dataPoints)||(a.length?a.forEach((function(a){var s=i.getElTooltip(a),r={paths:t.paths,tooltipEl:s,tooltipY:t.tooltipY,tooltipX:t.tooltipX,elGrid:t.elGrid,hoverArea:t.hoverArea,ttItems:a.w.globals.tooltip.ttItems};a.w.globals.minX===i.w.globals.minX&&a.w.globals.maxX===i.w.globals.maxX&&a.w.globals.tooltip.seriesHoverByContext({chartCtx:a,ttCtx:a.w.globals.tooltip,opt:r,e:e})})):this.seriesHoverByContext({chartCtx:this.ctx,ttCtx:this.w.globals.tooltip,opt:t,e:e}))}},{key:\"seriesHoverByContext\",value:function(t){var e=t.chartCtx,i=t.ttCtx,a=t.opt,s=t.e,r=e.w,o=this.getElTooltip();if(o){if(i.tooltipRect={x:0,y:0,ttWidth:o.getBoundingClientRect().width,ttHeight:o.getBoundingClientRect().height},i.e=s,i.tooltipUtil.hasBars()&&!r.globals.comboCharts&&!i.isBarShared)if(this.tConfig.onDatasetHover.highlightDataSeries)new N(e).toggleSeriesOnHover(s,s.target.parentNode);i.fixedTooltip&&i.drawFixedTooltipRect(),r.globals.axisCharts?i.axisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect}):i.nonAxisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect})}}},{key:\"axisChartsTooltips\",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,o=s.elGrid.getBoundingClientRect(),n=\"touchmove\"===a.type?a.touches[0].clientX:a.clientX,l=\"touchmove\"===a.type?a.touches[0].clientY:a.clientY;if(this.clientY=l,this.clientX=n,r.globals.capturedSeriesIndex=-1,r.globals.capturedDataPointIndex=-1,l<o.top||l>o.top+o.height)this.handleMouseOut(s);else{if(Array.isArray(this.tConfig.enabledOnSeries)&&!r.config.tooltip.shared){var h=parseInt(s.paths.getAttribute(\"index\"),10);if(this.tConfig.enabledOnSeries.indexOf(h)<0)return void this.handleMouseOut(s)}var c=this.getElTooltip(),d=this.getElXCrosshairs(),g=r.globals.xyCharts||\"bar\"===r.config.chart.type&&!r.globals.isBarHorizontal&&this.tooltipUtil.hasBars()&&this.tConfig.shared||r.globals.comboCharts&&this.tooltipUtil.hasBars();if(\"mousemove\"===a.type||\"touchmove\"===a.type||\"mouseup\"===a.type){if(r.globals.collapsedSeries.length+r.globals.ancillaryCollapsedSeries.length===r.globals.series.length)return;null!==d&&d.classList.add(\"apexcharts-active\");var u=this.yaxisTooltips.filter((function(t){return!0===t}));if(null!==this.ycrosshairs&&u.length&&this.ycrosshairs.classList.add(\"apexcharts-active\"),g&&!this.showOnIntersect)this.handleStickyTooltip(a,n,l,s);else if(\"heatmap\"===r.config.chart.type||\"treemap\"===r.config.chart.type){var f=this.intersect.handleHeatTreeTooltip({e:a,opt:s,x:e,y:i,type:r.config.chart.type});e=f.x,i=f.y,c.style.left=e+\"px\",c.style.top=i+\"px\"}else this.tooltipUtil.hasBars()&&this.intersect.handleBarTooltip({e:a,opt:s}),this.tooltipUtil.hasMarkers()&&this.intersect.handleMarkerTooltip({e:a,opt:s,x:e,y:i});if(this.yaxisTooltips.length)for(var p=0;p<r.config.yaxis.length;p++)this.axesTooltip.drawYaxisTooltipText(p,l,this.xyRatios);s.tooltipEl.classList.add(\"apexcharts-active\")}else\"mouseout\"!==a.type&&\"touchend\"!==a.type||this.handleMouseOut(s)}}},{key:\"nonAxisChartsTooltips\",value:function(t){var e=t.e,i=t.opt,a=t.tooltipRect,s=this.w,r=i.paths.getAttribute(\"rel\"),o=this.getElTooltip(),n=s.globals.dom.elWrap.getBoundingClientRect();if(\"mousemove\"===e.type||\"touchmove\"===e.type){o.classList.add(\"apexcharts-active\"),this.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:parseInt(r,10)-1,shared:!1});var l=s.globals.clientX-n.left-a.ttWidth/2,h=s.globals.clientY-n.top-a.ttHeight-10;if(o.style.left=l+\"px\",o.style.top=h+\"px\",s.config.legend.tooltipHoverFormatter){var c=r-1,d=(0,s.config.legend.tooltipHoverFormatter)(this.legendLabels[c].getAttribute(\"data:default-text\"),{seriesIndex:c,dataPointIndex:c,w:s});this.legendLabels[c].innerHTML=d}}else\"mouseout\"!==e.type&&\"touchend\"!==e.type||(o.classList.remove(\"apexcharts-active\"),s.config.legend.tooltipHoverFormatter&&this.legendLabels.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)})))}},{key:\"handleStickyTooltip\",value:function(t,e,i,a){var s=this.w,r=this.tooltipUtil.getNearestValues({context:this,hoverArea:a.hoverArea,elGrid:a.elGrid,clientX:e,clientY:i}),o=r.j,n=r.capturedSeries,l=a.elGrid.getBoundingClientRect();r.hoverX<0||r.hoverX>l.width?this.handleMouseOut(a):null!==n?this.handleStickyCapturedSeries(t,n,a,o):(this.tooltipUtil.isXoverlap(o)||s.globals.isBarHorizontal)&&this.create(t,this,0,o,a.ttItems)}},{key:\"handleStickyCapturedSeries\",value:function(t,e,i,a){var s=this.w;if(!this.tConfig.shared&&null===s.globals.series[e][a])return void this.handleMouseOut(i);void 0!==s.globals.series[e][a]?this.tConfig.shared&&this.tooltipUtil.isXoverlap(a)&&this.tooltipUtil.isInitialSeriesSameLen()?this.create(t,this,e,a,i.ttItems):this.create(t,this,e,a,i.ttItems,!1):this.tooltipUtil.isXoverlap(a)&&this.create(t,this,0,a,i.ttItems)}},{key:\"deactivateHoverFilter\",value:function(){for(var t=this.w,e=new m(this.ctx),i=t.globals.dom.Paper.select(\".apexcharts-bar-area\"),a=0;a<i.length;a++)e.pathMouseLeave(i[a])}},{key:\"handleMouseOut\",value:function(t){var e=this.w,i=this.getElXCrosshairs();if(t.tooltipEl.classList.remove(\"apexcharts-active\"),this.deactivateHoverFilter(),\"bubble\"!==e.config.chart.type&&this.marker.resetPointsSize(),null!==i&&i.classList.remove(\"apexcharts-active\"),null!==this.ycrosshairs&&this.ycrosshairs.classList.remove(\"apexcharts-active\"),this.isXAxisTooltipEnabled&&this.xaxisTooltip.classList.remove(\"apexcharts-active\"),this.yaxisTooltips.length){null===this.yaxisTTEls&&(this.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxistooltip\"));for(var a=0;a<this.yaxisTTEls.length;a++)this.yaxisTTEls[a].classList.remove(\"apexcharts-active\")}e.config.legend.tooltipHoverFormatter&&this.legendLabels.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)}))}},{key:\"markerClick\",value:function(t,e,i){var a=this.w;\"function\"==typeof a.config.chart.events.markerClick&&a.config.chart.events.markerClick(t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}),this.ctx.events.fireEvent(\"markerClick\",[t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}])}},{key:\"create\",value:function(t,i,a,s,r){var o,n,l,h,c,d,g,u,f,p,x,b,v,y,w,k,A=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,S=this.w,C=i;\"mouseup\"===t.type&&this.markerClick(t,a,s),null===A&&(A=this.tConfig.shared);var L=this.tooltipUtil.hasMarkers(),P=this.tooltipUtil.getElBars();if(S.config.legend.tooltipHoverFormatter){var T=S.config.legend.tooltipHoverFormatter,M=Array.from(this.legendLabels);M.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)}));for(var I=0;I<M.length;I++){var z=M[I],X=parseInt(z.getAttribute(\"i\"),10),E=decodeURIComponent(z.getAttribute(\"data:default-text\")),Y=T(E,{seriesIndex:A?X:a,dataPointIndex:s,w:S});if(A)z.innerHTML=S.globals.collapsedSeriesIndices.indexOf(X)<0?Y:E;else if(z.innerHTML=X===a?Y:E,a===X)break}}var F=e(e({ttItems:r,i:a,j:s},void 0!==(null===(o=S.globals.seriesRange)||void 0===o||null===(n=o[a])||void 0===n||null===(l=n[s])||void 0===l||null===(h=l.y[0])||void 0===h?void 0:h.y1)&&{y1:null===(c=S.globals.seriesRange)||void 0===c||null===(d=c[a])||void 0===d||null===(g=d[s])||void 0===g||null===(u=g.y[0])||void 0===u?void 0:u.y1}),void 0!==(null===(f=S.globals.seriesRange)||void 0===f||null===(p=f[a])||void 0===p||null===(x=p[s])||void 0===x||null===(b=x.y[0])||void 0===b?void 0:b.y2)&&{y2:null===(v=S.globals.seriesRange)||void 0===v||null===(y=v[a])||void 0===y||null===(w=y[s])||void 0===w||null===(k=w.y[0])||void 0===k?void 0:k.y2});if(A){if(C.tooltipLabels.drawSeriesTexts(e(e({},F),{},{shared:!this.showOnIntersect&&this.tConfig.shared})),L&&(S.globals.markers.largestSize>0?C.marker.enlargePoints(s):C.tooltipPosition.moveDynamicPointsOnHover(s)),this.tooltipUtil.hasBars()&&(this.barSeriesHeight=this.tooltipUtil.getBarsHeight(P),this.barSeriesHeight>0)){var R=new m(this.ctx),D=S.globals.dom.Paper.select(\".apexcharts-bar-area[j='\".concat(s,\"']\"));this.deactivateHoverFilter(),this.tooltipPosition.moveStickyTooltipOverBars(s);for(var H=0;H<D.length;H++)R.pathMouseEnter(D[H])}}else C.tooltipLabels.drawSeriesTexts(e({shared:!1},F)),this.tooltipUtil.hasBars()&&C.tooltipPosition.moveStickyTooltipOverBars(s),L&&C.tooltipPosition.moveMarkers(a,s)}}]),t}(),vt=function(){function t(e){a(this,t),this.w=e.w,this.barCtx=e,this.totalFormatter=this.w.config.plotOptions.bar.dataLabels.total.formatter,this.totalFormatter||(this.totalFormatter=this.w.config.dataLabels.formatter)}return r(t,[{key:\"handleBarDataLabels\",value:function(t){var e=t.x,i=t.y,a=t.y1,s=t.y2,r=t.i,o=t.j,n=t.realIndex,l=t.series,h=t.barHeight,c=t.barWidth,d=t.barYPosition,g=t.visibleSeries,u=t.renderedPath,f=this.w,p=new m(this.barCtx.ctx),x=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[n]:this.barCtx.strokeWidth,b=e+parseFloat(c*g),v=i+parseFloat(h*g);f.globals.isXNumeric&&!f.globals.isBarHorizontal&&(b=e+parseFloat(c*(g+1)),v=i+parseFloat(h*(g+1))-x);var y,w=null,k=e,A=i,S={},C=f.config.dataLabels,L=this.barCtx.barOptions.dataLabels,P=this.barCtx.barOptions.dataLabels.total;void 0!==d&&this.barCtx.isRangeBar&&(v=d,A=d);var T=C.offsetX,M=C.offsetY,I={width:0,height:0};if(f.config.dataLabels.enabled){var z=this.barCtx.series[r][o];I=p.getTextRects(f.globals.yLabelFormatters[0](z),parseFloat(C.style.fontSize))}var X={x:e,y:i,i:r,j:o,realIndex:n,renderedPath:u,bcx:b,bcy:v,barHeight:h,barWidth:c,textRects:I,strokeWidth:x,dataLabelsX:k,dataLabelsY:A,dataLabelsConfig:C,barDataLabelsConfig:L,barTotalDataLabelsConfig:P,offX:T,offY:M};return S=this.barCtx.isHorizontal?this.calculateBarsDataLabelsPosition(X):this.calculateColumnsDataLabelsPosition(X),u.attr({cy:S.bcy,cx:S.bcx,j:o,val:l[r][o],barHeight:h,barWidth:c}),y=this.drawCalculatedDataLabels({x:S.dataLabelsX,y:S.dataLabelsY,val:this.barCtx.isRangeBar?[a,s]:l[r][o],i:n,j:o,barWidth:c,barHeight:h,textRects:I,dataLabelsConfig:C}),f.config.chart.stacked&&P.enabled&&(w=this.drawTotalDataLabels({x:S.totalDataLabelsX,y:S.totalDataLabelsY,realIndex:n,textAnchor:S.totalDataLabelsAnchor,val:this.getStackedTotalDataLabel({realIndex:n,j:o}),dataLabelsConfig:C,barTotalDataLabelsConfig:P})),{dataLabels:y,totalDataLabels:w}}},{key:\"getStackedTotalDataLabel\",value:function(t){var i=t.realIndex,a=t.j,s=this.w,r=this.barCtx.stackedSeriesTotals[a];return this.totalFormatter&&(r=this.totalFormatter(r,e(e({},s),{},{seriesIndex:i,dataPointIndex:a,w:s}))),r}},{key:\"calculateColumnsDataLabelsPosition\",value:function(t){var e,i,a,s=this.w,r=t.i,o=t.j,n=t.realIndex,l=t.y,h=t.bcx,c=t.barWidth,d=t.barHeight,g=t.textRects,u=t.dataLabelsY,f=t.dataLabelsConfig,p=t.barDataLabelsConfig,x=t.barTotalDataLabelsConfig,b=t.strokeWidth,v=t.offX,y=t.offY;d=Math.abs(d);var w=\"vertical\"===s.config.plotOptions.bar.dataLabels.orientation;h-=b/2;var k=s.globals.gridWidth/s.globals.dataPoints;if(e=s.globals.isXNumeric?h-c/2+v:h-k+c/2+v,w){e=e+g.height/2-b/2-2}var A=this.barCtx.series[r][o]<0,S=l;switch(this.barCtx.isReversed&&(S=l-d+(A?2*d:0),l-=d),p.position){case\"center\":u=w?A?S+d/2+y:S+d/2-y:A?S-d/2+g.height/2+y:S+d/2+g.height/2-y;break;case\"bottom\":u=w?A?S+d+y:S+d-y:A?S-d+g.height+b+y:S+d-g.height/2+b-y;break;case\"top\":u=w?A?S+y:S-y:A?S-g.height/2-y:S+g.height+y}if(this.barCtx.lastActiveBarSerieIndex===n&&x.enabled){var C=new m(this.barCtx.ctx).getTextRects(this.getStackedTotalDataLabel({realIndex:n,j:o}),f.fontSize);i=A?S-C.height/2-y-x.offsetY+18:S+C.height+y+x.offsetY-18,a=e+x.offsetX}return s.config.chart.stacked||(u<0?u=0+b:u+g.height/3>s.globals.gridHeight&&(u=s.globals.gridHeight-b)),{bcx:h,bcy:l,dataLabelsX:e,dataLabelsY:u,totalDataLabelsX:a,totalDataLabelsY:i,totalDataLabelsAnchor:\"middle\"}}},{key:\"calculateBarsDataLabelsPosition\",value:function(t){var e=this.w,i=t.x,a=t.i,s=t.j,r=t.realIndex,o=t.bcy,n=t.barHeight,l=t.barWidth,h=t.textRects,c=t.dataLabelsX,d=t.strokeWidth,g=t.dataLabelsConfig,u=t.barDataLabelsConfig,f=t.barTotalDataLabelsConfig,p=t.offX,x=t.offY,b=e.globals.gridHeight/e.globals.dataPoints;l=Math.abs(l);var v,y,w=o-(this.barCtx.isRangeBar?0:b)+n/2+h.height/2+x-3,k=\"start\",A=this.barCtx.series[a][s]<0,S=i;switch(this.barCtx.isReversed&&(S=i+l-(A?2*l:0),i=e.globals.gridWidth-l),u.position){case\"center\":c=A?S+l/2-p:Math.max(h.width/2,S-l/2)+p;break;case\"bottom\":c=A?S+l-d-Math.round(h.width/2)-p:S-l+d+Math.round(h.width/2)+p;break;case\"top\":c=A?S-d+Math.round(h.width/2)-p:S-d-Math.round(h.width/2)+p}if(this.barCtx.lastActiveBarSerieIndex===r&&f.enabled){var C=new m(this.barCtx.ctx).getTextRects(this.getStackedTotalDataLabel({realIndex:r,j:s}),g.fontSize);A?(v=S-d+Math.round(C.width/2)-p-f.offsetX-15,k=\"end\"):v=S-d-Math.round(C.width/2)+p+f.offsetX+15,y=w+f.offsetY}return e.config.chart.stacked||(c<0?c=c+h.width+d:c+h.width/2>e.globals.gridWidth&&(c=e.globals.gridWidth-h.width-d)),{bcx:i,bcy:o,dataLabelsX:c,dataLabelsY:w,totalDataLabelsX:v,totalDataLabelsY:y,totalDataLabelsAnchor:k}}},{key:\"drawCalculatedDataLabels\",value:function(t){var i=t.x,a=t.y,s=t.val,r=t.i,o=t.j,n=t.textRects,l=t.barHeight,h=t.barWidth,c=t.dataLabelsConfig,d=this.w,g=\"rotate(0)\";\"vertical\"===d.config.plotOptions.bar.dataLabels.orientation&&(g=\"rotate(-90, \".concat(i,\", \").concat(a,\")\"));var u=new O(this.barCtx.ctx),f=new m(this.barCtx.ctx),p=c.formatter,x=null,b=d.globals.collapsedSeriesIndices.indexOf(r)>-1;if(c.enabled&&!b){x=f.group({class:\"apexcharts-data-labels\",transform:g});var v=\"\";void 0!==s&&(v=p(s,e(e({},d),{},{seriesIndex:r,dataPointIndex:o,w:d})));var y=d.globals.series[r][o]<0,w=d.config.plotOptions.bar.dataLabels.position;if(\"vertical\"===d.config.plotOptions.bar.dataLabels.orientation&&(\"top\"===w&&(c.textAnchor=y?\"end\":\"start\"),\"center\"===w&&(c.textAnchor=\"middle\"),\"bottom\"===w&&(c.textAnchor=y?\"end\":\"start\")),this.barCtx.isRangeBar&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels)h<f.getTextRects(v,parseFloat(c.style.fontSize)).width&&(v=\"\");d.config.chart.stacked&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels&&(this.barCtx.isHorizontal?n.width/1.6>Math.abs(h)&&(v=\"\"):n.height/1.6>Math.abs(l)&&(v=\"\"));var k=e({},c);this.barCtx.isHorizontal&&s<0&&(\"start\"===c.textAnchor?k.textAnchor=\"end\":\"end\"===c.textAnchor&&(k.textAnchor=\"start\")),u.plotDataLabelsText({x:i,y:a,text:v,i:r,j:o,parent:x,dataLabelsConfig:k,alwaysDrawDataLabel:!0,offsetCorrection:!0})}return x}},{key:\"drawTotalDataLabels\",value:function(t){var e,i=t.x,a=t.y,s=t.val,r=t.realIndex,o=t.textAnchor,n=t.barTotalDataLabelsConfig,l=new m(this.barCtx.ctx);return n.enabled&&void 0!==i&&void 0!==a&&this.barCtx.lastActiveBarSerieIndex===r&&(e=l.drawText({x:i,y:a,foreColor:n.style.color,text:s,textAnchor:o,fontFamily:n.style.fontFamily,fontSize:n.style.fontSize,fontWeight:n.style.fontWeight})),e}}]),t}(),mt=function(){function t(e){a(this,t),this.w=e.w,this.barCtx=e}return r(t,[{key:\"initVariables\",value:function(t){var e=this.w;this.barCtx.series=t,this.barCtx.totalItems=0,this.barCtx.seriesLen=0,this.barCtx.visibleI=-1,this.barCtx.visibleItems=1;for(var i=0;i<t.length;i++)if(t[i].length>0&&(this.barCtx.seriesLen=this.barCtx.seriesLen+1,this.barCtx.totalItems+=t[i].length),e.globals.isXNumeric)for(var a=0;a<t[i].length;a++)e.globals.seriesX[i][a]>e.globals.minX&&e.globals.seriesX[i][a]<e.globals.maxX&&this.barCtx.visibleItems++;else this.barCtx.visibleItems=e.globals.dataPoints;0===this.barCtx.seriesLen&&(this.barCtx.seriesLen=1),this.barCtx.zeroSerieses=[],this.barCtx.radiusOnSeriesNumber=t.length-1,e.globals.comboCharts||this.checkZeroSeries({series:t})}},{key:\"initialPositions\",value:function(){var t,e,i,a,s,r,o,n,l=this.w,h=l.globals.dataPoints;this.barCtx.isRangeBar&&(h=l.globals.labels.length);var c=this.barCtx.seriesLen;if(l.config.plotOptions.bar.rangeBarGroupRows&&(c=1),this.barCtx.isHorizontal)s=(i=l.globals.gridHeight/h)/c,l.globals.isXNumeric&&(s=(i=l.globals.gridHeight/this.barCtx.totalItems)/this.barCtx.seriesLen),s=s*parseInt(this.barCtx.barOptions.barHeight,10)/100,n=this.barCtx.baseLineInvertedY+l.globals.padHorizontal+(this.barCtx.isReversed?l.globals.gridWidth:0)-(this.barCtx.isReversed?2*this.barCtx.baseLineInvertedY:0),e=(i-s*this.barCtx.seriesLen)/2;else{if(a=l.globals.gridWidth/this.barCtx.visibleItems,l.config.xaxis.convertedCatToNumeric&&(a=l.globals.gridWidth/l.globals.dataPoints),r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100,l.globals.isXNumeric){var d=this.barCtx.xRatio;l.config.xaxis.convertedCatToNumeric&&(d=this.barCtx.initialXRatio),l.globals.minXDiff&&.5!==l.globals.minXDiff&&l.globals.minXDiff/d>0&&(a=l.globals.minXDiff/d),(r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100)<1&&(r=1)}o=l.globals.gridHeight-this.barCtx.baseLineY[this.barCtx.yaxisIndex]-(this.barCtx.isReversed?l.globals.gridHeight:0)+(this.barCtx.isReversed?2*this.barCtx.baseLineY[this.barCtx.yaxisIndex]:0),t=l.globals.padHorizontal+(a-r*this.barCtx.seriesLen)/2}return{x:t,y:e,yDivision:i,xDivision:a,barHeight:s,barWidth:r,zeroH:o,zeroW:n}}},{key:\"getPathFillColor\",value:function(t,e,i,a){var s,r,o,n,l=this.w,h=new R(this.barCtx.ctx),c=null,d=this.barCtx.barOptions.distributed?i:e;this.barCtx.barOptions.colors.ranges.length>0&&this.barCtx.barOptions.colors.ranges.map((function(a){t[e][i]>=a.from&&t[e][i]<=a.to&&(c=a.color)}));return l.config.series[e].data[i]&&l.config.series[e].data[i].fillColor&&(c=l.config.series[e].data[i].fillColor),h.fillPath({seriesNumber:this.barCtx.barOptions.distributed?d:a,dataPointIndex:i,color:c,value:t[e][i],fillConfig:null===(s=l.config.series[e].data[i])||void 0===s?void 0:s.fill,fillType:null!==(r=l.config.series[e].data[i])&&void 0!==r&&null!==(o=r.fill)&&void 0!==o&&o.type?null===(n=l.config.series[e].data[i])||void 0===n?void 0:n.fill.type:l.config.fill.type})}},{key:\"getStrokeWidth\",value:function(t,e,i){var a=0,s=this.w;return this.barCtx.series[t][e]?this.barCtx.isNullValue=!1:this.barCtx.isNullValue=!0,s.config.stroke.show&&(this.barCtx.isNullValue||(a=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[i]:this.barCtx.strokeWidth)),a}},{key:\"shouldApplyRadius\",value:function(t){var e=this.w,i=!1;return e.config.plotOptions.bar.borderRadius>0&&(e.config.chart.stacked&&\"last\"===e.config.plotOptions.bar.borderRadiusWhenStacked?this.barCtx.lastActiveBarSerieIndex===t&&(i=!0):i=!0),i}},{key:\"barBackground\",value:function(t){var e=t.j,i=t.i,a=t.x1,s=t.x2,r=t.y1,o=t.y2,n=t.elSeries,l=this.w,h=new m(this.barCtx.ctx),c=new N(this.barCtx.ctx).getActiveConfigSeriesIndex();if(this.barCtx.barOptions.colors.backgroundBarColors.length>0&&c===i){e>=this.barCtx.barOptions.colors.backgroundBarColors.length&&(e%=this.barCtx.barOptions.colors.backgroundBarColors.length);var d=this.barCtx.barOptions.colors.backgroundBarColors[e],g=h.drawRect(void 0!==a?a:0,void 0!==r?r:0,void 0!==s?s:l.globals.gridWidth,void 0!==o?o:l.globals.gridHeight,this.barCtx.barOptions.colors.backgroundBarRadius,d,this.barCtx.barOptions.colors.backgroundBarOpacity);n.add(g),g.node.classList.add(\"apexcharts-backgroundBar\")}}},{key:\"getColumnPaths\",value:function(t){var e,i=t.barWidth,a=t.barXPosition,s=t.y1,r=t.y2,o=t.strokeWidth,n=t.realIndex,l=t.i,h=t.j,c=t.w,d=new m(this.barCtx.ctx);(o=Array.isArray(o)?o[n]:o)||(o=0);var g=i,u=a;null!==(e=c.config.series[n].data[h])&&void 0!==e&&e.columnWidthOffset&&(u=a-c.config.series[n].data[h].columnWidthOffset/2,g=i+c.config.series[n].data[h].columnWidthOffset);var f=u,p=u+g;s+=.001,r+=.001;var x=d.move(f,s),b=d.move(f,s),v=d.line(p-o,s);return c.globals.previousPaths.length>0&&(b=this.barCtx.getPreviousPath(n,h,!1)),x=x+d.line(f,r)+d.line(p-o,r)+d.line(p-o,s)+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),b=b+d.line(f,s)+v+v+v+v+v+d.line(f,s)+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),this.shouldApplyRadius(n)&&(x=d.roundPathCorners(x,c.config.plotOptions.bar.borderRadius)),c.config.chart.stacked&&(this.barCtx.yArrj.push(r),this.barCtx.yArrjF.push(Math.abs(s-r)),this.barCtx.yArrjVal.push(this.barCtx.series[l][h])),{pathTo:x,pathFrom:b}}},{key:\"getBarpaths\",value:function(t){var e,i=t.barYPosition,a=t.barHeight,s=t.x1,r=t.x2,o=t.strokeWidth,n=t.realIndex,l=t.i,h=t.j,c=t.w,d=new m(this.barCtx.ctx);(o=Array.isArray(o)?o[n]:o)||(o=0);var g=i,u=a;null!==(e=c.config.series[n].data[h])&&void 0!==e&&e.barHeightOffset&&(g=i-c.config.series[n].data[h].barHeightOffset/2,u=a+c.config.series[n].data[h].barHeightOffset);var f=g,p=g+u;s+=.001,r+=.001;var x=d.move(s,f),b=d.move(s,f);c.globals.previousPaths.length>0&&(b=this.barCtx.getPreviousPath(n,h,!1));var v=d.line(s,p-o);return x=x+d.line(r,f)+d.line(r,p-o)+v+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),b=b+d.line(s,f)+v+v+v+v+v+d.line(s,f)+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),this.shouldApplyRadius(n)&&(x=d.roundPathCorners(x,c.config.plotOptions.bar.borderRadius)),c.config.chart.stacked&&(this.barCtx.xArrj.push(r),this.barCtx.xArrjF.push(Math.abs(s-r)),this.barCtx.xArrjVal.push(this.barCtx.series[l][h])),{pathTo:x,pathFrom:b}}},{key:\"checkZeroSeries\",value:function(t){for(var e=t.series,i=this.w,a=0;a<e.length;a++){for(var s=0,r=0;r<e[i.globals.maxValsInArrayIndex].length;r++)s+=e[a][r];0===s&&this.barCtx.zeroSerieses.push(a)}for(var o=e.length-1;o>=0;o--)this.barCtx.zeroSerieses.indexOf(o)>-1&&o===this.radiusOnSeriesNumber&&(this.barCtx.radiusOnSeriesNumber-=1);for(var n=e.length-1;n>=0;n--)i.globals.collapsedSeriesIndices.indexOf(this.barCtx.radiusOnSeriesNumber)>-1&&(this.barCtx.radiusOnSeriesNumber-=1)}},{key:\"getXForValue\",value:function(t,e){var i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=i?e:null;return null!=t&&(a=e+t/this.barCtx.invertedYRatio-2*(this.barCtx.isReversed?t/this.barCtx.invertedYRatio:0)),a}},{key:\"getYForValue\",value:function(t,e){var i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=i?e:null;return null!=t&&(a=e-t/this.barCtx.yRatio[this.barCtx.yaxisIndex]+2*(this.barCtx.isReversed?t/this.barCtx.yRatio[this.barCtx.yaxisIndex]:0)),a}},{key:\"getGoalValues\",value:function(t,e,i,a,s){var r=this,n=this.w,l=[];return n.globals.seriesGoals[a]&&n.globals.seriesGoals[a][s]&&Array.isArray(n.globals.seriesGoals[a][s])&&n.globals.seriesGoals[a][s].forEach((function(a){var s;l.push((o(s={},t,\"x\"===t?r.getXForValue(a.value,e,!1):r.getYForValue(a.value,i,!1)),o(s,\"attrs\",a),s))})),l}},{key:\"drawGoalLine\",value:function(t){var e=t.barXPosition,i=t.barYPosition,a=t.goalX,s=t.goalY,r=t.barWidth,o=t.barHeight,n=new m(this.barCtx.ctx),l=n.group({className:\"apexcharts-bar-goals-groups\"}),h=null;return this.barCtx.isHorizontal?Array.isArray(a)&&a.forEach((function(t){var e=void 0!==t.attrs.strokeHeight?t.attrs.strokeHeight:o/2,a=i+e+o/2;h=n.drawLine(t.x,a-2*e,t.x,a,t.attrs.strokeColor?t.attrs.strokeColor:void 0,t.attrs.strokeDashArray,t.attrs.strokeWidth?t.attrs.strokeWidth:2,t.attrs.strokeLineCap),l.add(h)})):Array.isArray(s)&&s.forEach((function(t){var i=void 0!==t.attrs.strokeWidth?t.attrs.strokeWidth:r/2,a=e+i+r/2;h=n.drawLine(a-2*i,t.y,a,t.y,t.attrs.strokeColor?t.attrs.strokeColor:void 0,t.attrs.strokeDashArray,t.attrs.strokeHeight?t.attrs.strokeHeight:2,t.attrs.strokeLineCap),l.add(h)})),l}}]),t}(),yt=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w;var s=this.w;this.barOptions=s.config.plotOptions.bar,this.isHorizontal=this.barOptions.horizontal,this.strokeWidth=s.config.stroke.width,this.isNullValue=!1,this.isRangeBar=s.globals.seriesRange.length&&this.isHorizontal,this.xyRatios=i,null!==this.xyRatios&&(this.xRatio=i.xRatio,this.initialXRatio=i.initialXRatio,this.yRatio=i.yRatio,this.invertedXRatio=i.invertedXRatio,this.invertedYRatio=i.invertedYRatio,this.baseLineY=i.baseLineY,this.baseLineInvertedY=i.baseLineInvertedY),this.yaxisIndex=0,this.seriesLen=0;var r=new N(this.ctx);this.lastActiveBarSerieIndex=r.getActiveConfigSeriesIndex(\"desc\",[\"bar\",\"column\"]);var o=r.getBarSeriesIndices(),n=new y(this.ctx);this.stackedSeriesTotals=n.getStackedSeriesTotals(this.w.config.series.map((function(t,e){return-1===o.indexOf(e)?e:-1})).filter((function(t){return-1!==t}))),this.barHelpers=new mt(this)}return r(t,[{key:\"draw\",value:function(t,i){var a=this.w,s=new m(this.ctx),r=new y(this.ctx,a);t=r.getLogSeries(t),this.series=t,this.yRatio=r.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);var o=s.group({class:\"apexcharts-bar-series apexcharts-plot-series\"});a.config.dataLabels.enabled&&this.totalItems>this.barOptions.dataLabels.maxItems&&console.warn(\"WARNING: DataLabels are enabled but there are too many to display. This may cause performance issue when rendering.\");for(var n=0,l=0;n<t.length;n++,l++){var h,c,d,g,u=void 0,f=void 0,p=[],b=[],v=a.globals.comboCharts?i[n]:n,w=s.group({class:\"apexcharts-series\",rel:n+1,seriesName:x.escapeString(a.globals.seriesNames[v]),\"data:realIndex\":v});this.ctx.series.addCollapsedClassToSeries(w,v),t[n].length>0&&(this.visibleI=this.visibleI+1);var k=0,A=0;this.yRatio.length>1&&(this.yaxisIndex=v),this.isReversed=a.config.yaxis[this.yaxisIndex]&&a.config.yaxis[this.yaxisIndex].reversed;var S=this.barHelpers.initialPositions();f=S.y,k=S.barHeight,c=S.yDivision,g=S.zeroW,u=S.x,A=S.barWidth,h=S.xDivision,d=S.zeroH,this.horizontal||b.push(u+A/2);for(var C=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":v}),L=s.group({class:\"apexcharts-bar-goals-markers\",style:\"pointer-events: none\"}),P=0;P<a.globals.dataPoints;P++){var T=this.barHelpers.getStrokeWidth(n,P,v),M=null,I={indexes:{i:n,j:P,realIndex:v,bc:l},x:u,y:f,strokeWidth:T,elSeries:w};this.isHorizontal?(M=this.drawBarPaths(e(e({},I),{},{barHeight:k,zeroW:g,yDivision:c})),A=this.series[n][P]/this.invertedYRatio):(M=this.drawColumnPaths(e(e({},I),{},{xDivision:h,barWidth:A,zeroH:d})),k=this.series[n][P]/this.yRatio[this.yaxisIndex]);var z=this.barHelpers.drawGoalLine({barXPosition:M.barXPosition,barYPosition:M.barYPosition,goalX:M.goalX,goalY:M.goalY,barHeight:k,barWidth:A});z&&L.add(z),f=M.y,u=M.x,P>0&&b.push(u+A/2),p.push(f);var X=this.barHelpers.getPathFillColor(t,n,P,v);this.renderSeries({realIndex:v,pathFill:X,j:P,i:n,pathFrom:M.pathFrom,pathTo:M.pathTo,strokeWidth:T,elSeries:w,x:u,y:f,series:t,barHeight:k,barWidth:A,elDataLabelsWrap:C,elGoalsMarkers:L,visibleSeries:this.visibleI,type:\"bar\"})}a.globals.seriesXvalues[v]=b,a.globals.seriesYvalues[v]=p,o.add(w)}return o}},{key:\"renderSeries\",value:function(t){var e=t.realIndex,i=t.pathFill,a=t.lineFill,s=t.j,r=t.i,o=t.pathFrom,n=t.pathTo,l=t.strokeWidth,h=t.elSeries,c=t.x,d=t.y,g=t.y1,u=t.y2,f=t.series,p=t.barHeight,x=t.barWidth,b=t.barYPosition,y=t.elDataLabelsWrap,w=t.elGoalsMarkers,k=t.visibleSeries,A=t.type,S=this.w,C=new m(this.ctx);a||(a=this.barOptions.distributed?S.globals.stroke.colors[s]:S.globals.stroke.colors[e]),S.config.series[r].data[s]&&S.config.series[r].data[s].strokeColor&&(a=S.config.series[r].data[s].strokeColor),this.isNullValue&&(i=\"none\");var L=s/S.config.chart.animations.animateGradually.delay*(S.config.chart.animations.speed/S.globals.dataPoints)/2.4,P=C.renderPaths({i:r,j:s,realIndex:e,pathFrom:o,pathTo:n,stroke:a,strokeWidth:l,strokeLineCap:S.config.stroke.lineCap,fill:i,animationDelay:L,initialSpeed:S.config.chart.animations.speed,dataChangeSpeed:S.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-\".concat(A,\"-area\")});P.attr(\"clip-path\",\"url(#gridRectMask\".concat(S.globals.cuid,\")\"));var T=S.config.forecastDataPoints;T.count>0&&s>=S.globals.dataPoints-T.count&&(P.node.setAttribute(\"stroke-dasharray\",T.dashArray),P.node.setAttribute(\"stroke-width\",T.strokeWidth),P.node.setAttribute(\"fill-opacity\",T.fillOpacity)),void 0!==g&&void 0!==u&&(P.attr(\"data-range-y1\",g),P.attr(\"data-range-y2\",u)),new v(this.ctx).setSelectionFilter(P,e,s),h.add(P);var M=new vt(this).handleBarDataLabels({x:c,y:d,y1:g,y2:u,i:r,j:s,series:f,realIndex:e,barHeight:p,barWidth:x,barYPosition:b,renderedPath:P,visibleSeries:k});return null!==M.dataLabels&&y.add(M.dataLabels),M.totalDataLabels&&y.add(M.totalDataLabels),h.add(y),w&&h.add(w),h}},{key:\"drawBarPaths\",value:function(t){var e,i=t.indexes,a=t.barHeight,s=t.strokeWidth,r=t.zeroW,o=t.x,n=t.y,l=t.yDivision,h=t.elSeries,c=this.w,d=i.i,g=i.j;if(c.globals.isXNumeric)e=(n=(c.globals.seriesX[d][g]-c.globals.minX)/this.invertedXRatio-a)+a*this.visibleI;else if(c.config.plotOptions.bar.hideZeroBarsWhenGrouped){var u=0,f=0;c.globals.seriesPercent.forEach((function(t,e){t[g]&&u++,e<d&&0===t[g]&&f++})),e=n+(a=this.seriesLen*a/u)*this.visibleI,e-=a*f}o=this.barHelpers.getXForValue(this.series[d][g],r);var p=this.barHelpers.getBarpaths({barYPosition:e,barHeight:a,x1:r,x2:o,strokeWidth:s,series:this.series,realIndex:i.realIndex,i:d,j:g,w:c});return c.globals.isXNumeric||(n+=l),this.barHelpers.barBackground({j:g,i:d,y1:e-a*this.visibleI,y2:a*this.seriesLen,elSeries:h}),{pathTo:p.pathTo,pathFrom:p.pathFrom,x:o,y:n,goalX:this.barHelpers.getGoalValues(\"x\",r,null,d,g),barYPosition:e}}},{key:\"drawColumnPaths\",value:function(t){var e,i=t.indexes,a=t.x,s=t.y,r=t.xDivision,o=t.barWidth,n=t.zeroH,l=t.strokeWidth,h=t.elSeries,c=this.w,d=i.realIndex,g=i.i,u=i.j,f=i.bc;if(c.globals.isXNumeric){var p=d;c.globals.seriesX[d].length||(p=c.globals.maxValsInArrayIndex),c.globals.seriesX[p][u]&&(a=(c.globals.seriesX[p][u]-c.globals.minX)/this.xRatio-o*this.seriesLen/2),e=a+o*this.visibleI}else if(c.config.plotOptions.bar.hideZeroBarsWhenGrouped){var x=0,b=0;c.globals.seriesPercent.forEach((function(t,e){t[u]&&x++,e<g&&0===t[u]&&b++})),e=a+(o=this.seriesLen*o/x)*this.visibleI,e-=o*b}s=this.barHelpers.getYForValue(this.series[g][u],n);var v=this.barHelpers.getColumnPaths({barXPosition:e,barWidth:o,y1:n,y2:s,strokeWidth:l,series:this.series,realIndex:i.realIndex,i:g,j:u,w:c});return c.globals.isXNumeric||(a+=r),this.barHelpers.barBackground({bc:f,j:u,i:g,x1:e-l/2-o*this.visibleI,x2:o*this.seriesLen+l/2,elSeries:h}),{pathTo:v.pathTo,pathFrom:v.pathFrom,x:a,y:s,goalY:this.barHelpers.getGoalValues(\"y\",null,n,g,u),barXPosition:e}}},{key:\"getPreviousPath\",value:function(t,e){for(var i,a=this.w,s=0;s<a.globals.previousPaths.length;s++){var r=a.globals.previousPaths[s];r.paths&&r.paths.length>0&&parseInt(r.realIndex,10)===parseInt(t,10)&&void 0!==a.globals.previousPaths[s].paths[e]&&(i=a.globals.previousPaths[s].paths[e].d)}return i}}]),t}(),wt=function(t){n(s,yt);var i=d(s);function s(){return a(this,s),i.apply(this,arguments)}return r(s,[{key:\"draw\",value:function(t,i){var a=this,s=this.w;this.graphics=new m(this.ctx),this.bar=new yt(this.ctx,this.xyRatios);var r=new y(this.ctx,s);t=r.getLogSeries(t),this.yRatio=r.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t),\"100%\"===s.config.chart.stackType&&(t=s.globals.seriesPercent.slice()),this.series=t,this.totalItems=0,this.prevY=[],this.prevX=[],this.prevYF=[],this.prevXF=[],this.prevYVal=[],this.prevXVal=[],this.xArrj=[],this.xArrjF=[],this.xArrjVal=[],this.yArrj=[],this.yArrjF=[],this.yArrjVal=[];for(var o=0;o<t.length;o++)t[o].length>0&&(this.totalItems+=t[o].length);for(var n=this.graphics.group({class:\"apexcharts-bar-series apexcharts-plot-series\"}),l=0,h=0,c=function(r,o){var c=void 0,d=void 0,g=void 0,u=void 0,f=[],p=[],b=s.globals.comboCharts?i[r]:r;a.yRatio.length>1&&(a.yaxisIndex=b),a.isReversed=s.config.yaxis[a.yaxisIndex]&&s.config.yaxis[a.yaxisIndex].reversed;var v=a.graphics.group({class:\"apexcharts-series\",seriesName:x.escapeString(s.globals.seriesNames[b]),rel:r+1,\"data:realIndex\":b});a.ctx.series.addCollapsedClassToSeries(v,b);var m=a.graphics.group({class:\"apexcharts-datalabels\",\"data:realIndex\":b}),y=a.graphics.group({class:\"apexcharts-bar-goals-markers\",style:\"pointer-events: none\"}),w=0,k=0,A=a.initialPositions(l,h,c,d,g,u);h=A.y,w=A.barHeight,d=A.yDivision,u=A.zeroW,l=A.x,k=A.barWidth,c=A.xDivision,g=A.zeroH,a.yArrj=[],a.yArrjF=[],a.yArrjVal=[],a.xArrj=[],a.xArrjF=[],a.xArrjVal=[],1===a.prevY.length&&a.prevY[0].every((function(t){return isNaN(t)}))&&(a.prevY[0]=a.prevY[0].map((function(t){return g})),a.prevYF[0]=a.prevYF[0].map((function(t){return 0})));for(var S=0;S<s.globals.dataPoints;S++){var C=a.barHelpers.getStrokeWidth(r,S,b),L={indexes:{i:r,j:S,realIndex:b,bc:o},strokeWidth:C,x:l,y:h,elSeries:v},P=null;a.isHorizontal?(P=a.drawStackedBarPaths(e(e({},L),{},{zeroW:u,barHeight:w,yDivision:d})),k=a.series[r][S]/a.invertedYRatio):(P=a.drawStackedColumnPaths(e(e({},L),{},{xDivision:c,barWidth:k,zeroH:g})),w=a.series[r][S]/a.yRatio[a.yaxisIndex]);var T=a.barHelpers.drawGoalLine({barXPosition:P.barXPosition,barYPosition:P.barYPosition,goalX:P.goalX,goalY:P.goalY,barHeight:w,barWidth:k});T&&y.add(T),h=P.y,l=P.x,f.push(l),p.push(h);var M=a.barHelpers.getPathFillColor(t,r,S,b);v=a.renderSeries({realIndex:b,pathFill:M,j:S,i:r,pathFrom:P.pathFrom,pathTo:P.pathTo,strokeWidth:C,elSeries:v,x:l,y:h,series:t,barHeight:w,barWidth:k,elDataLabelsWrap:m,elGoalsMarkers:y,type:\"bar\",visibleSeries:0})}s.globals.seriesXvalues[b]=f,s.globals.seriesYvalues[b]=p,a.prevY.push(a.yArrj),a.prevYF.push(a.yArrjF),a.prevYVal.push(a.yArrjVal),a.prevX.push(a.xArrj),a.prevXF.push(a.xArrjF),a.prevXVal.push(a.xArrjVal),n.add(v)},d=0,g=0;d<t.length;d++,g++)c(d,g);return n}},{key:\"initialPositions\",value:function(t,e,i,a,s,r){var o,n,l=this.w;return this.isHorizontal?(o=(o=a=l.globals.gridHeight/l.globals.dataPoints)*parseInt(l.config.plotOptions.bar.barHeight,10)/100,r=this.baseLineInvertedY+l.globals.padHorizontal+(this.isReversed?l.globals.gridWidth:0)-(this.isReversed?2*this.baseLineInvertedY:0),e=(a-o)/2):(n=i=l.globals.gridWidth/l.globals.dataPoints,n=l.globals.isXNumeric&&l.globals.dataPoints>1?(i=l.globals.minXDiff/this.xRatio)*parseInt(this.barOptions.columnWidth,10)/100:n*parseInt(l.config.plotOptions.bar.columnWidth,10)/100,s=l.globals.gridHeight-this.baseLineY[this.yaxisIndex]-(this.isReversed?l.globals.gridHeight:0)+(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),t=l.globals.padHorizontal+(i-n)/2),{x:t,y:e,yDivision:a,xDivision:i,barHeight:o,barWidth:n,zeroH:s,zeroW:r}}},{key:\"drawStackedBarPaths\",value:function(t){for(var e,i=t.indexes,a=t.barHeight,s=t.strokeWidth,r=t.zeroW,o=t.x,n=t.y,l=t.yDivision,h=t.elSeries,c=this.w,d=n,g=i.i,u=i.j,f=0,p=0;p<this.prevXF.length;p++)f+=this.prevXF[p][u];if(g>0){var x=r;this.prevXVal[g-1][u]<0?x=this.series[g][u]>=0?this.prevX[g-1][u]+f-2*(this.isReversed?f:0):this.prevX[g-1][u]:this.prevXVal[g-1][u]>=0&&(x=this.series[g][u]>=0?this.prevX[g-1][u]:this.prevX[g-1][u]-f+2*(this.isReversed?f:0)),e=x}else e=r;o=null===this.series[g][u]?e:e+this.series[g][u]/this.invertedYRatio-2*(this.isReversed?this.series[g][u]/this.invertedYRatio:0);var b=this.barHelpers.getBarpaths({barYPosition:d,barHeight:a,x1:e,x2:o,strokeWidth:s,series:this.series,realIndex:i.realIndex,i:g,j:u,w:c});return this.barHelpers.barBackground({j:u,i:g,y1:d,y2:a,elSeries:h}),n+=l,{pathTo:b.pathTo,pathFrom:b.pathFrom,goalX:this.barHelpers.getGoalValues(\"x\",r,null,g,u),barYPosition:d,x:o,y:n}}},{key:\"drawStackedColumnPaths\",value:function(t){var e=t.indexes,i=t.x,a=t.y,s=t.xDivision,r=t.barWidth,o=t.zeroH;t.strokeWidth;var n=t.elSeries,l=this.w,h=e.i,c=e.j,d=e.bc;if(l.globals.isXNumeric){var g=l.globals.seriesX[h][c];g||(g=0),i=(g-l.globals.minX)/this.xRatio-r/2}for(var u,f=i,p=0,x=0;x<this.prevYF.length;x++)p+=isNaN(this.prevYF[x][c])?0:this.prevYF[x][c];if(h>0&&!l.globals.isXNumeric||h>0&&l.globals.isXNumeric&&l.globals.seriesX[h-1][c]===l.globals.seriesX[h][c]){var b,v,m=Math.min(this.yRatio.length+1,h+1);if(void 0!==this.prevY[h-1])for(var y=1;y<m;y++)if(!isNaN(this.prevY[h-y][c])){v=this.prevY[h-y][c];break}for(var w=1;w<m;w++){if(this.prevYVal[h-w][c]<0){b=this.series[h][c]>=0?v-p+2*(this.isReversed?p:0):v;break}if(this.prevYVal[h-w][c]>=0){b=this.series[h][c]>=0?v:v+p-2*(this.isReversed?p:0);break}}void 0===b&&(b=l.globals.gridHeight),u=this.prevYF[0].every((function(t){return 0===t}))&&this.prevYF.slice(1,h).every((function(t){return t.every((function(t){return isNaN(t)}))}))?o:b}else u=o;a=this.series[h][c]?u-this.series[h][c]/this.yRatio[this.yaxisIndex]+2*(this.isReversed?this.series[h][c]/this.yRatio[this.yaxisIndex]:0):u;var k=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:r,y1:u,y2:a,yRatio:this.yRatio[this.yaxisIndex],strokeWidth:this.strokeWidth,series:this.series,realIndex:e.realIndex,i:h,j:c,w:l});return this.barHelpers.barBackground({bc:d,j:c,i:h,x1:f,x2:r,elSeries:n}),i+=s,{pathTo:k.pathTo,pathFrom:k.pathFrom,goalY:this.barHelpers.getGoalValues(\"y\",null,o,h,c),barXPosition:f,x:l.globals.isXNumeric?i-s:i,y:a}}}]),s}(),kt=function(t){n(s,yt);var i=d(s);function s(){return a(this,s),i.apply(this,arguments)}return r(s,[{key:\"draw\",value:function(t,i){var a=this,s=this.w,r=new m(this.ctx),o=new R(this.ctx);this.candlestickOptions=this.w.config.plotOptions.candlestick,this.boxOptions=this.w.config.plotOptions.boxPlot,this.isHorizontal=s.config.plotOptions.bar.horizontal;var n=new y(this.ctx,s);t=n.getLogSeries(t),this.series=t,this.yRatio=n.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);for(var l=r.group({class:\"apexcharts-\".concat(s.config.chart.type,\"-series apexcharts-plot-series\")}),h=function(n){a.isBoxPlot=\"boxPlot\"===s.config.chart.type||\"boxPlot\"===s.config.series[n].type;var h,c,d,g,u=void 0,f=void 0,p=[],b=[],v=s.globals.comboCharts?i[n]:n,m=r.group({class:\"apexcharts-series\",seriesName:x.escapeString(s.globals.seriesNames[v]),rel:n+1,\"data:realIndex\":v});a.ctx.series.addCollapsedClassToSeries(m,v),t[n].length>0&&(a.visibleI=a.visibleI+1);var y,w;a.yRatio.length>1&&(a.yaxisIndex=v);var k=a.barHelpers.initialPositions();f=k.y,y=k.barHeight,c=k.yDivision,g=k.zeroW,u=k.x,w=k.barWidth,h=k.xDivision,d=k.zeroH,b.push(u+w/2);for(var A=r.group({class:\"apexcharts-datalabels\",\"data:realIndex\":v}),S=function(i){var r=a.barHelpers.getStrokeWidth(n,i,v),l=null,x={indexes:{i:n,j:i,realIndex:v},x:u,y:f,strokeWidth:r,elSeries:m};l=a.isHorizontal?a.drawHorizontalBoxPaths(e(e({},x),{},{yDivision:c,barHeight:y,zeroW:g})):a.drawVerticalBoxPaths(e(e({},x),{},{xDivision:h,barWidth:w,zeroH:d})),f=l.y,u=l.x,i>0&&b.push(u+w/2),p.push(f),l.pathTo.forEach((function(e,h){var c=!a.isBoxPlot&&a.candlestickOptions.wick.useFillColor?l.color[h]:s.globals.stroke.colors[n],d=o.fillPath({seriesNumber:v,dataPointIndex:i,color:l.color[h],value:t[n][i]});a.renderSeries({realIndex:v,pathFill:d,lineFill:c,j:i,i:n,pathFrom:l.pathFrom,pathTo:e,strokeWidth:r,elSeries:m,x:u,y:f,series:t,barHeight:y,barWidth:w,elDataLabelsWrap:A,visibleSeries:a.visibleI,type:s.config.chart.type})}))},C=0;C<s.globals.dataPoints;C++)S(C);s.globals.seriesXvalues[v]=b,s.globals.seriesYvalues[v]=p,l.add(m)},c=0;c<t.length;c++)h(c);return l}},{key:\"drawVerticalBoxPaths\",value:function(t){var e=t.indexes,i=t.x;t.y;var a=t.xDivision,s=t.barWidth,r=t.zeroH,o=t.strokeWidth,n=this.w,l=new m(this.ctx),h=e.i,c=e.j,d=!0,g=n.config.plotOptions.candlestick.colors.upward,u=n.config.plotOptions.candlestick.colors.downward,f=\"\";this.isBoxPlot&&(f=[this.boxOptions.colors.lower,this.boxOptions.colors.upper]);var p=this.yRatio[this.yaxisIndex],x=e.realIndex,b=this.getOHLCValue(x,c),v=r,y=r;b.o>b.c&&(d=!1);var w=Math.min(b.o,b.c),k=Math.max(b.o,b.c),A=b.m;n.globals.isXNumeric&&(i=(n.globals.seriesX[x][c]-n.globals.minX)/this.xRatio-s/2);var S=i+s*this.visibleI;void 0===this.series[h][c]||null===this.series[h][c]?(w=r,k=r):(w=r-w/p,k=r-k/p,v=r-b.h/p,y=r-b.l/p,A=r-b.m/p);var C=l.move(S,r),L=l.move(S+s/2,w);return n.globals.previousPaths.length>0&&(L=this.getPreviousPath(x,c,!0)),C=this.isBoxPlot?[l.move(S,w)+l.line(S+s/2,w)+l.line(S+s/2,v)+l.line(S+s/4,v)+l.line(S+s-s/4,v)+l.line(S+s/2,v)+l.line(S+s/2,w)+l.line(S+s,w)+l.line(S+s,A)+l.line(S,A)+l.line(S,w+o/2),l.move(S,A)+l.line(S+s,A)+l.line(S+s,k)+l.line(S+s/2,k)+l.line(S+s/2,y)+l.line(S+s-s/4,y)+l.line(S+s/4,y)+l.line(S+s/2,y)+l.line(S+s/2,k)+l.line(S,k)+l.line(S,A)+\"z\"]:[l.move(S,k)+l.line(S+s/2,k)+l.line(S+s/2,v)+l.line(S+s/2,k)+l.line(S+s,k)+l.line(S+s,w)+l.line(S+s/2,w)+l.line(S+s/2,y)+l.line(S+s/2,w)+l.line(S,w)+l.line(S,k-o/2)],L+=l.move(S,w),n.globals.isXNumeric||(i+=a),{pathTo:C,pathFrom:L,x:i,y:k,barXPosition:S,color:this.isBoxPlot?f:d?[g]:[u]}}},{key:\"drawHorizontalBoxPaths\",value:function(t){var e=t.indexes;t.x;var i=t.y,a=t.yDivision,s=t.barHeight,r=t.zeroW,o=t.strokeWidth,n=this.w,l=new m(this.ctx),h=e.i,c=e.j,d=this.boxOptions.colors.lower;this.isBoxPlot&&(d=[this.boxOptions.colors.lower,this.boxOptions.colors.upper]);var g=this.invertedYRatio,u=e.realIndex,f=this.getOHLCValue(u,c),p=r,x=r,b=Math.min(f.o,f.c),v=Math.max(f.o,f.c),y=f.m;n.globals.isXNumeric&&(i=(n.globals.seriesX[u][c]-n.globals.minX)/this.invertedXRatio-s/2);var w=i+s*this.visibleI;void 0===this.series[h][c]||null===this.series[h][c]?(b=r,v=r):(b=r+b/g,v=r+v/g,p=r+f.h/g,x=r+f.l/g,y=r+f.m/g);var k=l.move(r,w),A=l.move(b,w+s/2);return n.globals.previousPaths.length>0&&(A=this.getPreviousPath(u,c,!0)),k=[l.move(b,w)+l.line(b,w+s/2)+l.line(p,w+s/2)+l.line(p,w+s/2-s/4)+l.line(p,w+s/2+s/4)+l.line(p,w+s/2)+l.line(b,w+s/2)+l.line(b,w+s)+l.line(y,w+s)+l.line(y,w)+l.line(b+o/2,w),l.move(y,w)+l.line(y,w+s)+l.line(v,w+s)+l.line(v,w+s/2)+l.line(x,w+s/2)+l.line(x,w+s-s/4)+l.line(x,w+s/4)+l.line(x,w+s/2)+l.line(v,w+s/2)+l.line(v,w)+l.line(y,w)+\"z\"],A+=l.move(b,w),n.globals.isXNumeric||(i+=a),{pathTo:k,pathFrom:A,x:v,y:i,barYPosition:w,color:d}}},{key:\"getOHLCValue\",value:function(t,e){var i=this.w;return{o:this.isBoxPlot?i.globals.seriesCandleH[t][e]:i.globals.seriesCandleO[t][e],h:this.isBoxPlot?i.globals.seriesCandleO[t][e]:i.globals.seriesCandleH[t][e],m:i.globals.seriesCandleM[t][e],l:this.isBoxPlot?i.globals.seriesCandleC[t][e]:i.globals.seriesCandleL[t][e],c:this.isBoxPlot?i.globals.seriesCandleL[t][e]:i.globals.seriesCandleC[t][e]}}}]),s}(),At=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"checkColorRange\",value:function(){var t=this.w,e=!1,i=t.config.plotOptions[t.config.chart.type];return i.colorScale.ranges.length>0&&i.colorScale.ranges.map((function(t,i){t.from<=0&&(e=!0)})),e}},{key:\"getShadeColor\",value:function(t,e,i,a){var s=this.w,r=1,o=s.config.plotOptions[t].shadeIntensity,n=this.determineColor(t,e,i);s.globals.hasNegs||a?r=s.config.plotOptions[t].reverseNegativeShade?n.percent<0?n.percent/100*(1.25*o):(1-n.percent/100)*(1.25*o):n.percent<=0?1-(1+n.percent/100)*o:(1-n.percent/100)*o:(r=1-n.percent/100,\"treemap\"===t&&(r=(1-n.percent/100)*(1.25*o)));var l=n.color,h=new x;return s.config.plotOptions[t].enableShades&&(l=\"dark\"===this.w.config.theme.mode?x.hexToRgba(h.shadeColor(-1*r,n.color),s.config.fill.opacity):x.hexToRgba(h.shadeColor(r,n.color),s.config.fill.opacity)),{color:l,colorProps:n}}},{key:\"determineColor\",value:function(t,e,i){var a=this.w,s=a.globals.series[e][i],r=a.config.plotOptions[t],o=r.colorScale.inverse?i:e;r.distributed&&\"treemap\"===a.config.chart.type&&(o=i);var n=a.globals.colors[o],l=null,h=Math.min.apply(Math,u(a.globals.series[e])),c=Math.max.apply(Math,u(a.globals.series[e]));r.distributed||\"heatmap\"!==t||(h=a.globals.minY,c=a.globals.maxY),void 0!==r.colorScale.min&&(h=r.colorScale.min<a.globals.minY?r.colorScale.min:a.globals.minY,c=r.colorScale.max>a.globals.maxY?r.colorScale.max:a.globals.maxY);var d=Math.abs(c)+Math.abs(h),g=100*s/(0===d?d-1e-6:d);r.colorScale.ranges.length>0&&r.colorScale.ranges.map((function(t,e){if(s>=t.from&&s<=t.to){n=t.color,l=t.foreColor?t.foreColor:null,h=t.from,c=t.to;var i=Math.abs(c)+Math.abs(h);g=100*s/(0===i?i-1e-6:i)}}));return{color:n,foreColor:l,percent:g}}},{key:\"calculateDataLabels\",value:function(t){var e=t.text,i=t.x,a=t.y,s=t.i,r=t.j,o=t.colorProps,n=t.fontSize,l=this.w.config.dataLabels,h=new m(this.ctx),c=new O(this.ctx),d=null;if(l.enabled){d=h.group({class:\"apexcharts-data-labels\"});var g=l.offsetX,u=l.offsetY,f=i+g,p=a+parseFloat(l.style.fontSize)/3+u;c.plotDataLabelsText({x:f,y:p,text:e,i:s,j:r,color:o.foreColor,parent:d,fontSize:n,dataLabelsConfig:l})}return d}},{key:\"addListeners\",value:function(t){var e=new m(this.ctx);t.node.addEventListener(\"mouseenter\",e.pathMouseEnter.bind(this,t)),t.node.addEventListener(\"mouseleave\",e.pathMouseLeave.bind(this,t)),t.node.addEventListener(\"mousedown\",e.pathMouseDown.bind(this,t))}}]),t}(),St=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w,this.xRatio=i.xRatio,this.yRatio=i.yRatio,this.dynamicAnim=this.w.config.chart.animations.dynamicAnimation,this.helpers=new At(e),this.rectRadius=this.w.config.plotOptions.heatmap.radius,this.strokeWidth=this.w.config.stroke.show?this.w.config.stroke.width:0}return r(t,[{key:\"draw\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-heatmap\"});a.attr(\"clip-path\",\"url(#gridRectMask\".concat(e.globals.cuid,\")\"));var s=e.globals.gridWidth/e.globals.dataPoints,r=e.globals.gridHeight/e.globals.series.length,o=0,n=!1;this.negRange=this.helpers.checkColorRange();var l=t.slice();e.config.yaxis[0].reversed&&(n=!0,l.reverse());for(var h=n?0:l.length-1;n?h<l.length:h>=0;n?h++:h--){var c=i.group({class:\"apexcharts-series apexcharts-heatmap-series\",seriesName:x.escapeString(e.globals.seriesNames[h]),rel:h+1,\"data:realIndex\":h});if(this.ctx.series.addCollapsedClassToSeries(c,h),e.config.chart.dropShadow.enabled){var d=e.config.chart.dropShadow;new v(this.ctx).dropShadow(c,d,h)}for(var g=0,u=e.config.plotOptions.heatmap.shadeIntensity,f=0;f<l[h].length;f++){var p=this.helpers.getShadeColor(e.config.chart.type,h,f,this.negRange),b=p.color,y=p.colorProps;if(\"image\"===e.config.fill.type)b=new R(this.ctx).fillPath({seriesNumber:h,dataPointIndex:f,opacity:e.globals.hasNegs?y.percent<0?1-(1+y.percent/100):u+y.percent/100:y.percent/100,patternID:x.randomId(),width:e.config.fill.image.width?e.config.fill.image.width:s,height:e.config.fill.image.height?e.config.fill.image.height:r});var w=this.rectRadius,k=i.drawRect(g,o,s,r,w);if(k.attr({cx:g,cy:o}),k.node.classList.add(\"apexcharts-heatmap-rect\"),c.add(k),k.attr({fill:b,i:h,index:h,j:f,val:l[h][f],\"stroke-width\":this.strokeWidth,stroke:e.config.plotOptions.heatmap.useFillColorAsStroke?b:e.globals.stroke.colors[0],color:b}),this.helpers.addListeners(k),e.config.chart.animations.enabled&&!e.globals.dataChanged){var A=1;e.globals.resized||(A=e.config.chart.animations.speed),this.animateHeatMap(k,g,o,s,r,A)}if(e.globals.dataChanged){var S=1;if(this.dynamicAnim.enabled&&e.globals.shouldAnimate){S=this.dynamicAnim.speed;var C=e.globals.previousPaths[h]&&e.globals.previousPaths[h][f]&&e.globals.previousPaths[h][f].color;C||(C=\"rgba(255, 255, 255, 0)\"),this.animateHeatColor(k,x.isColorHex(C)?C:x.rgb2hex(C),x.isColorHex(b)?b:x.rgb2hex(b),S)}}var L=(0,e.config.dataLabels.formatter)(e.globals.series[h][f],{value:e.globals.series[h][f],seriesIndex:h,dataPointIndex:f,w:e}),P=this.helpers.calculateDataLabels({text:L,x:g+s/2,y:o+r/2,i:h,j:f,colorProps:y,series:l});null!==P&&c.add(P),g+=s}o+=r,a.add(c)}var T=e.globals.yAxisScale[0].result.slice();e.config.yaxis[0].reversed?T.unshift(\"\"):T.push(\"\"),e.globals.yAxisScale[0].result=T;var M=e.globals.gridHeight/e.globals.series.length;return e.config.yaxis[0].labels.offsetY=-M/2,a}},{key:\"animateHeatMap\",value:function(t,e,i,a,s,r){var o=new b(this.ctx);o.animateRect(t,{x:e+a/2,y:i+s/2,width:0,height:0},{x:e,y:i,width:a,height:s},r,(function(){o.animationCompleted(t)}))}},{key:\"animateHeatColor\",value:function(t,e,i,a){t.attr({fill:e}).animate(a).attr({fill:i})}}]),t}(),Ct=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"drawYAxisTexts\",value:function(t,e,i,a){var s=this.w,r=s.config.yaxis[0],o=s.globals.yLabelFormatters[0];return new m(this.ctx).drawText({x:t+r.labels.offsetX,y:e+r.labels.offsetY,text:o(a,i),textAnchor:\"middle\",fontSize:r.labels.style.fontSize,fontFamily:r.labels.style.fontFamily,foreColor:Array.isArray(r.labels.style.colors)?r.labels.style.colors[i]:r.labels.style.colors})}}]),t}(),Lt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animBeginArr=[0],this.animDur=0,this.donutDataLabels=this.w.config.plotOptions.pie.donut.labels,this.lineColorArr=void 0!==i.globals.stroke.colors?i.globals.stroke.colors:i.globals.colors,this.defaultSize=Math.min(i.globals.gridWidth,i.globals.gridHeight),this.centerY=this.defaultSize/2,this.centerX=i.globals.gridWidth/2,\"radialBar\"===i.config.chart.type?this.fullAngle=360:this.fullAngle=Math.abs(i.config.plotOptions.pie.endAngle-i.config.plotOptions.pie.startAngle),this.initialAngle=i.config.plotOptions.pie.startAngle%this.fullAngle,i.globals.radialSize=this.defaultSize/2.05-i.config.stroke.width-(i.config.chart.sparkline.enabled?0:i.config.chart.dropShadow.blur),this.donutSize=i.globals.radialSize*parseInt(i.config.plotOptions.pie.donut.size,10)/100,this.maxY=0,this.sliceLabels=[],this.sliceSizes=[],this.prevSectorAngleArr=[]}return r(t,[{key:\"draw\",value:function(t){var e=this,i=this.w,a=new m(this.ctx);if(this.ret=a.group({class:\"apexcharts-pie\"}),i.globals.noData)return this.ret;for(var s=0,r=0;r<t.length;r++)s+=x.negToZero(t[r]);var o=[],n=a.group();0===s&&(s=1e-5),t.forEach((function(t){e.maxY=Math.max(e.maxY,t)})),i.config.yaxis[0].max&&(this.maxY=i.config.yaxis[0].max),\"back\"===i.config.grid.position&&\"polarArea\"===this.chartType&&this.drawPolarElements(this.ret);for(var l=0;l<t.length;l++){var h=this.fullAngle*x.negToZero(t[l])/s;o.push(h),\"polarArea\"===this.chartType?(o[l]=this.fullAngle/t.length,this.sliceSizes.push(i.globals.radialSize*t[l]/this.maxY)):this.sliceSizes.push(i.globals.radialSize)}if(i.globals.dataChanged){for(var c,d=0,g=0;g<i.globals.previousPaths.length;g++)d+=x.negToZero(i.globals.previousPaths[g]);for(var u=0;u<i.globals.previousPaths.length;u++)c=this.fullAngle*x.negToZero(i.globals.previousPaths[u])/d,this.prevSectorAngleArr.push(c)}this.donutSize<0&&(this.donutSize=0);var f=i.config.plotOptions.pie.customScale,p=i.globals.gridWidth/2,b=i.globals.gridHeight/2,v=p-i.globals.gridWidth/2*f,y=b-i.globals.gridHeight/2*f;if(\"donut\"===this.chartType){var w=a.drawCircle(this.donutSize);w.attr({cx:this.centerX,cy:this.centerY,fill:i.config.plotOptions.pie.donut.background?i.config.plotOptions.pie.donut.background:\"transparent\"}),n.add(w)}var k=this.drawArcs(o,t);if(this.sliceLabels.forEach((function(t){k.add(t)})),n.attr({transform:\"translate(\".concat(v,\", \").concat(y,\") scale(\").concat(f,\")\")}),n.add(k),this.ret.add(n),this.donutDataLabels.show){var A=this.renderInnerDataLabels(this.donutDataLabels,{hollowSize:this.donutSize,centerX:this.centerX,centerY:this.centerY,opacity:this.donutDataLabels.show,translateX:v,translateY:y});this.ret.add(A)}return\"front\"===i.config.grid.position&&\"polarArea\"===this.chartType&&this.drawPolarElements(this.ret),this.ret}},{key:\"drawArcs\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=new m(this.ctx),r=new R(this.ctx),o=s.group({class:\"apexcharts-slices\"}),n=this.initialAngle,l=this.initialAngle,h=this.initialAngle,c=this.initialAngle;this.strokeWidth=i.config.stroke.show?i.config.stroke.width:0;for(var d=0;d<t.length;d++){var g=s.group({class:\"apexcharts-series apexcharts-pie-series\",seriesName:x.escapeString(i.globals.seriesNames[d]),rel:d+1,\"data:realIndex\":d});o.add(g),l=c,h=(n=h)+t[d],c=l+this.prevSectorAngleArr[d];var u=h<n?this.fullAngle+h-n:h-n,f=r.fillPath({seriesNumber:d,size:this.sliceSizes[d],value:e[d]}),p=this.getChangedPath(l,c),b=s.drawPath({d:p,stroke:Array.isArray(this.lineColorArr)?this.lineColorArr[d]:this.lineColorArr,strokeWidth:0,fill:f,fillOpacity:i.config.fill.opacity,classes:\"apexcharts-pie-area apexcharts-\".concat(this.chartType.toLowerCase(),\"-slice-\").concat(d)});if(b.attr({index:0,j:d}),a.setSelectionFilter(b,0,d),i.config.chart.dropShadow.enabled){var y=i.config.chart.dropShadow;a.dropShadow(b,y,d)}this.addListeners(b,this.donutDataLabels),m.setAttrs(b.node,{\"data:angle\":u,\"data:startAngle\":n,\"data:strokeWidth\":this.strokeWidth,\"data:value\":e[d]});var w={x:0,y:0};\"pie\"===this.chartType||\"polarArea\"===this.chartType?w=x.polarToCartesian(this.centerX,this.centerY,i.globals.radialSize/1.25+i.config.plotOptions.pie.dataLabels.offset,(n+u/2)%this.fullAngle):\"donut\"===this.chartType&&(w=x.polarToCartesian(this.centerX,this.centerY,(i.globals.radialSize+this.donutSize)/2+i.config.plotOptions.pie.dataLabels.offset,(n+u/2)%this.fullAngle)),g.add(b);var k=0;if(!this.initialAnim||i.globals.resized||i.globals.dataChanged?this.animBeginArr.push(0):(0===(k=u/this.fullAngle*i.config.chart.animations.speed)&&(k=1),this.animDur=k+this.animDur,this.animBeginArr.push(this.animDur)),this.dynamicAnim&&i.globals.dataChanged?this.animatePaths(b,{size:this.sliceSizes[d],endAngle:h,startAngle:n,prevStartAngle:l,prevEndAngle:c,animateStartingPos:!0,i:d,animBeginArr:this.animBeginArr,shouldSetPrevPaths:!0,dur:i.config.chart.animations.dynamicAnimation.speed}):this.animatePaths(b,{size:this.sliceSizes[d],endAngle:h,startAngle:n,i:d,totalItems:t.length-1,animBeginArr:this.animBeginArr,dur:k}),i.config.plotOptions.pie.expandOnClick&&\"polarArea\"!==this.chartType&&b.click(this.pieClicked.bind(this,d)),void 0!==i.globals.selectedDataPoints[0]&&i.globals.selectedDataPoints[0].indexOf(d)>-1&&this.pieClicked(d),i.config.dataLabels.enabled){var A=w.x,S=w.y,C=100*u/this.fullAngle+\"%\";if(0!==u&&i.config.plotOptions.pie.dataLabels.minAngleToShowLabel<t[d]){var L=i.config.dataLabels.formatter;void 0!==L&&(C=L(i.globals.seriesPercent[d][0],{seriesIndex:d,w:i}));var P=i.globals.dataLabels.style.colors[d],T=s.group({class:\"apexcharts-datalabels\"}),M=s.drawText({x:A,y:S,text:C,textAnchor:\"middle\",fontSize:i.config.dataLabels.style.fontSize,fontFamily:i.config.dataLabels.style.fontFamily,fontWeight:i.config.dataLabels.style.fontWeight,foreColor:P});if(T.add(M),i.config.dataLabels.dropShadow.enabled){var I=i.config.dataLabels.dropShadow;a.dropShadow(M,I)}M.node.classList.add(\"apexcharts-pie-label\"),i.config.chart.animations.animate&&!1===i.globals.resized&&(M.node.classList.add(\"apexcharts-pie-label-delay\"),M.node.style.animationDelay=i.config.chart.animations.speed/940+\"s\"),this.sliceLabels.push(T)}}}return o}},{key:\"addListeners\",value:function(t,e){var i=new m(this.ctx);t.node.addEventListener(\"mouseenter\",i.pathMouseEnter.bind(this,t)),t.node.addEventListener(\"mouseleave\",i.pathMouseLeave.bind(this,t)),t.node.addEventListener(\"mouseleave\",this.revertDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener(\"mousedown\",i.pathMouseDown.bind(this,t)),this.donutDataLabels.total.showAlways||(t.node.addEventListener(\"mouseenter\",this.printDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener(\"mousedown\",this.printDataLabelsInner.bind(this,t.node,e)))}},{key:\"animatePaths\",value:function(t,e){var i=this.w,a=e.endAngle<e.startAngle?this.fullAngle+e.endAngle-e.startAngle:e.endAngle-e.startAngle,s=a,r=e.startAngle,o=e.startAngle;void 0!==e.prevStartAngle&&void 0!==e.prevEndAngle&&(r=e.prevEndAngle,s=e.prevEndAngle<e.prevStartAngle?this.fullAngle+e.prevEndAngle-e.prevStartAngle:e.prevEndAngle-e.prevStartAngle),e.i===i.config.series.length-1&&(a+o>this.fullAngle?e.endAngle=e.endAngle-(a+o):a+o<this.fullAngle&&(e.endAngle=e.endAngle+(this.fullAngle-(a+o)))),a===this.fullAngle&&(a=this.fullAngle-.01),this.animateArc(t,r,o,a,s,e)}},{key:\"animateArc\",value:function(t,e,i,a,s,r){var o,n=this,l=this.w,h=new b(this.ctx),c=r.size;(isNaN(e)||isNaN(s))&&(e=i,s=a,r.dur=0);var d=a,g=i,u=e<i?this.fullAngle+e-i:e-i;l.globals.dataChanged&&r.shouldSetPrevPaths&&r.prevEndAngle&&(o=n.getPiePath({me:n,startAngle:r.prevStartAngle,angle:r.prevEndAngle<r.prevStartAngle?this.fullAngle+r.prevEndAngle-r.prevStartAngle:r.prevEndAngle-r.prevStartAngle,size:c}),t.attr({d:o})),0!==r.dur?t.animate(r.dur,l.globals.easing,r.animBeginArr[r.i]).afterAll((function(){\"pie\"!==n.chartType&&\"donut\"!==n.chartType&&\"polarArea\"!==n.chartType||this.animate(l.config.chart.animations.dynamicAnimation.speed).attr({\"stroke-width\":n.strokeWidth}),r.i===l.config.series.length-1&&h.animationCompleted(t)})).during((function(l){d=u+(a-u)*l,r.animateStartingPos&&(d=s+(a-s)*l,g=e-s+(i-(e-s))*l),o=n.getPiePath({me:n,startAngle:g,angle:d,size:c}),t.node.setAttribute(\"data:pathOrig\",o),t.attr({d:o})})):(o=n.getPiePath({me:n,startAngle:g,angle:a,size:c}),r.isTrack||(l.globals.animationEnded=!0),t.node.setAttribute(\"data:pathOrig\",o),t.attr({d:o,\"stroke-width\":n.strokeWidth}))}},{key:\"pieClicked\",value:function(t){var e,i=this.w,a=this,s=a.sliceSizes[t]+(i.config.plotOptions.pie.expandOnClick?4:0),r=i.globals.dom.Paper.select(\".apexcharts-\".concat(a.chartType.toLowerCase(),\"-slice-\").concat(t)).members[0];if(\"true\"!==r.attr(\"data:pieClicked\")){var o=i.globals.dom.baseEl.getElementsByClassName(\"apexcharts-pie-area\");Array.prototype.forEach.call(o,(function(t){t.setAttribute(\"data:pieClicked\",\"false\");var e=t.getAttribute(\"data:pathOrig\");t.setAttribute(\"d\",e)})),r.attr(\"data:pieClicked\",\"true\");var n=parseInt(r.attr(\"data:startAngle\"),10),l=parseInt(r.attr(\"data:angle\"),10);e=a.getPiePath({me:a,startAngle:n,angle:l,size:s}),360!==l&&r.plot(e)}else{r.attr({\"data:pieClicked\":\"false\"}),this.revertDataLabelsInner(r.node,this.donutDataLabels);var h=r.attr(\"data:pathOrig\");r.attr({d:h})}}},{key:\"getChangedPath\",value:function(t,e){var i=\"\";return this.dynamicAnim&&this.w.globals.dataChanged&&(i=this.getPiePath({me:this,startAngle:t,angle:e-t,size:this.size})),i}},{key:\"getPiePath\",value:function(t){var e=t.me,i=t.startAngle,a=t.angle,s=t.size,r=i,o=Math.PI*(r-90)/180,n=a+i;Math.ceil(n)>=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle&&(n=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle-.01),Math.ceil(n)>this.fullAngle&&(n-=this.fullAngle);var l=Math.PI*(n-90)/180,h=e.centerX+s*Math.cos(o),c=e.centerY+s*Math.sin(o),d=e.centerX+s*Math.cos(l),g=e.centerY+s*Math.sin(l),u=x.polarToCartesian(e.centerX,e.centerY,e.donutSize,n),f=x.polarToCartesian(e.centerX,e.centerY,e.donutSize,r),p=a>180?1:0,b=[\"M\",h,c,\"A\",s,s,0,p,1,d,g];return\"donut\"===e.chartType?[].concat(b,[\"L\",u.x,u.y,\"A\",e.donutSize,e.donutSize,0,p,0,f.x,f.y,\"L\",h,c,\"z\"]).join(\" \"):\"pie\"===e.chartType||\"polarArea\"===e.chartType?[].concat(b,[\"L\",e.centerX,e.centerY,\"L\",h,c]).join(\" \"):[].concat(b).join(\" \")}},{key:\"drawPolarElements\",value:function(t){var e=this.w,i=new _(this.ctx),a=new m(this.ctx),s=new Ct(this.ctx),r=a.group(),o=a.group(),n=i.niceScale(0,Math.ceil(this.maxY),e.config.yaxis[0].tickAmount,0,!0),l=n.result.reverse(),h=n.result.length;this.maxY=n.niceMax;for(var c=e.globals.radialSize,d=c/(h-1),g=0;g<h-1;g++){var u=a.drawCircle(c);if(u.attr({cx:this.centerX,cy:this.centerY,fill:\"none\",\"stroke-width\":e.config.plotOptions.polarArea.rings.strokeWidth,stroke:e.config.plotOptions.polarArea.rings.strokeColor}),e.config.yaxis[0].show){var f=s.drawYAxisTexts(this.centerX,this.centerY-c+parseInt(e.config.yaxis[0].labels.style.fontSize,10)/2,g,l[g]);o.add(f)}r.add(u),c-=d}this.drawSpokes(t),t.add(r),t.add(o)}},{key:\"renderInnerDataLabels\",value:function(t,e){var i=this.w,a=new m(this.ctx),s=a.group({class:\"apexcharts-datalabels-group\",transform:\"translate(\".concat(e.translateX?e.translateX:0,\", \").concat(e.translateY?e.translateY:0,\") scale(\").concat(i.config.plotOptions.pie.customScale,\")\")}),r=t.total.show;s.node.style.opacity=e.opacity;var o,n,l=e.centerX,h=e.centerY;o=void 0===t.name.color?i.globals.colors[0]:t.name.color;var c=t.name.fontSize,d=t.name.fontFamily,g=t.name.fontWeight;n=void 0===t.value.color?i.config.chart.foreColor:t.value.color;var u=t.value.formatter,f=\"\",p=\"\";if(r?(o=t.total.color,c=t.total.fontSize,d=t.total.fontFamily,g=t.total.fontWeight,p=t.total.label,f=t.total.formatter(i)):1===i.globals.series.length&&(f=u(i.globals.series[0],i),p=i.globals.seriesNames[0]),p&&(p=t.name.formatter(p,t.total.show,i)),t.name.show){var x=a.drawText({x:l,y:h+parseFloat(t.name.offsetY),text:p,textAnchor:\"middle\",foreColor:o,fontSize:c,fontWeight:g,fontFamily:d});x.node.classList.add(\"apexcharts-datalabel-label\"),s.add(x)}if(t.value.show){var b=t.name.show?parseFloat(t.value.offsetY)+16:t.value.offsetY,v=a.drawText({x:l,y:h+b,text:f,textAnchor:\"middle\",foreColor:n,fontWeight:t.value.fontWeight,fontSize:t.value.fontSize,fontFamily:t.value.fontFamily});v.node.classList.add(\"apexcharts-datalabel-value\"),s.add(v)}return s}},{key:\"printInnerLabels\",value:function(t,e,i,a){var s,r=this.w;a?s=void 0===t.name.color?r.globals.colors[parseInt(a.parentNode.getAttribute(\"rel\"),10)-1]:t.name.color:r.globals.series.length>1&&t.total.show&&(s=t.total.color);var o=r.globals.dom.baseEl.querySelector(\".apexcharts-datalabel-label\"),n=r.globals.dom.baseEl.querySelector(\".apexcharts-datalabel-value\");i=(0,t.value.formatter)(i,r),a||\"function\"!=typeof t.total.formatter||(i=t.total.formatter(r));var l=e===t.total.label;e=t.name.formatter(e,l,r),null!==o&&(o.textContent=e),null!==n&&(n.textContent=i),null!==o&&(o.style.fill=s)}},{key:\"printDataLabelsInner\",value:function(t,e){var i=this.w,a=t.getAttribute(\"data:value\"),s=i.globals.seriesNames[parseInt(t.parentNode.getAttribute(\"rel\"),10)-1];i.globals.series.length>1&&this.printInnerLabels(e,s,a,t);var r=i.globals.dom.baseEl.querySelector(\".apexcharts-datalabels-group\");null!==r&&(r.style.opacity=1)}},{key:\"drawSpokes\",value:function(t){var e=this,i=this.w,a=new m(this.ctx),s=i.config.plotOptions.polarArea.spokes;if(0!==s.strokeWidth){for(var r=[],o=360/i.globals.series.length,n=0;n<i.globals.series.length;n++)r.push(x.polarToCartesian(this.centerX,this.centerY,i.globals.radialSize,i.config.plotOptions.pie.startAngle+o*n));r.forEach((function(i,r){var o=a.drawLine(i.x,i.y,e.centerX,e.centerY,Array.isArray(s.connectorColors)?s.connectorColors[r]:s.connectorColors);t.add(o)}))}}},{key:\"revertDataLabelsInner\",value:function(t,e,i){var a=this,s=this.w,r=s.globals.dom.baseEl.querySelector(\".apexcharts-datalabels-group\"),o=!1,n=s.globals.dom.baseEl.getElementsByClassName(\"apexcharts-pie-area\"),l=function(t){var i=t.makeSliceOut,s=t.printLabel;Array.prototype.forEach.call(n,(function(t){\"true\"===t.getAttribute(\"data:pieClicked\")&&(i&&(o=!0),s&&a.printDataLabelsInner(t,e))}))};if(l({makeSliceOut:!0,printLabel:!1}),e.total.show&&s.globals.series.length>1)o&&!e.total.showAlways?l({makeSliceOut:!1,printLabel:!0}):this.printInnerLabels(e,e.total.label,e.total.formatter(s));else if(l({makeSliceOut:!1,printLabel:!0}),!o)if(s.globals.selectedDataPoints.length&&s.globals.series.length>1)if(s.globals.selectedDataPoints[0].length>0){var h=s.globals.selectedDataPoints[0],c=s.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(this.chartType.toLowerCase(),\"-slice-\").concat(h));this.printDataLabelsInner(c,e)}else r&&s.globals.selectedDataPoints.length&&0===s.globals.selectedDataPoints[0].length&&(r.style.opacity=0);else r&&s.globals.series.length>1&&(r.style.opacity=0)}}]),t}(),Pt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animDur=0;var i=this.w;this.graphics=new m(this.ctx),this.lineColorArr=void 0!==i.globals.stroke.colors?i.globals.stroke.colors:i.globals.colors,this.defaultSize=i.globals.svgHeight<i.globals.svgWidth?i.globals.gridHeight+1.5*i.globals.goldenPadding:i.globals.gridWidth,this.isLog=i.config.yaxis[0].logarithmic,this.coreUtils=new y(this.ctx),this.maxValue=this.isLog?this.coreUtils.getLogVal(i.globals.maxY,0):i.globals.maxY,this.minValue=this.isLog?this.coreUtils.getLogVal(this.w.globals.minY,0):i.globals.minY,this.polygons=i.config.plotOptions.radar.polygons,this.strokeWidth=i.config.stroke.show?i.config.stroke.width:0,this.size=this.defaultSize/2.1-this.strokeWidth-i.config.chart.dropShadow.blur,i.config.xaxis.labels.show&&(this.size=this.size-i.globals.xAxisLabelsWidth/1.75),void 0!==i.config.plotOptions.radar.size&&(this.size=i.config.plotOptions.radar.size),this.dataRadiusOfPercent=[],this.dataRadius=[],this.angleArr=[],this.yaxisLabelsTextsPos=[]}return r(t,[{key:\"draw\",value:function(t){var i=this,a=this.w,s=new R(this.ctx),r=[],o=new O(this.ctx);t.length&&(this.dataPointsLen=t[a.globals.maxValsInArrayIndex].length),this.disAngle=2*Math.PI/this.dataPointsLen;var n=a.globals.gridWidth/2,l=a.globals.gridHeight/2,h=n+a.config.plotOptions.radar.offsetX,c=l+a.config.plotOptions.radar.offsetY,d=this.graphics.group({class:\"apexcharts-radar-series apexcharts-plot-series\",transform:\"translate(\".concat(h||0,\", \").concat(c||0,\")\")}),g=[],u=null,f=null;if(this.yaxisLabels=this.graphics.group({class:\"apexcharts-yaxis\"}),t.forEach((function(t,n){var l=t.length===a.globals.dataPoints,h=i.graphics.group().attr({class:\"apexcharts-series\",\"data:longestSeries\":l,seriesName:x.escapeString(a.globals.seriesNames[n]),rel:n+1,\"data:realIndex\":n});i.dataRadiusOfPercent[n]=[],i.dataRadius[n]=[],i.angleArr[n]=[],t.forEach((function(t,e){var a=Math.abs(i.maxValue-i.minValue);t+=Math.abs(i.minValue),i.isLog&&(t=i.coreUtils.getLogVal(t,0)),i.dataRadiusOfPercent[n][e]=t/a,i.dataRadius[n][e]=i.dataRadiusOfPercent[n][e]*i.size,i.angleArr[n][e]=e*i.disAngle})),g=i.getDataPointsPos(i.dataRadius[n],i.angleArr[n]);var c=i.createPaths(g,{x:0,y:0});u=i.graphics.group({class:\"apexcharts-series-markers-wrap apexcharts-element-hidden\"}),f=i.graphics.group({class:\"apexcharts-datalabels\",\"data:realIndex\":n}),a.globals.delayedElements.push({el:u.node,index:n});var d={i:n,realIndex:n,animationDelay:n,initialSpeed:a.config.chart.animations.speed,dataChangeSpeed:a.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-radar\",shouldClipToGrid:!1,bindEventsOnPaths:!1,stroke:a.globals.stroke.colors[n],strokeLineCap:a.config.stroke.lineCap},p=null;a.globals.previousPaths.length>0&&(p=i.getPreviousPath(n));for(var b=0;b<c.linePathsTo.length;b++){var m=i.graphics.renderPaths(e(e({},d),{},{pathFrom:null===p?c.linePathsFrom[b]:p,pathTo:c.linePathsTo[b],strokeWidth:Array.isArray(i.strokeWidth)?i.strokeWidth[n]:i.strokeWidth,fill:\"none\",drawShadow:!1}));h.add(m);var y=s.fillPath({seriesNumber:n}),w=i.graphics.renderPaths(e(e({},d),{},{pathFrom:null===p?c.areaPathsFrom[b]:p,pathTo:c.areaPathsTo[b],strokeWidth:0,fill:y,drawShadow:!1}));if(a.config.chart.dropShadow.enabled){var k=new v(i.ctx),A=a.config.chart.dropShadow;k.dropShadow(w,Object.assign({},A,{noUserSpaceOnUse:!0}),n)}h.add(w)}t.forEach((function(t,s){var r=new D(i.ctx).getMarkerConfig({cssClass:\"apexcharts-marker\",seriesIndex:n,dataPointIndex:s}),l=i.graphics.drawMarker(g[s].x,g[s].y,r);l.attr(\"rel\",s),l.attr(\"j\",s),l.attr(\"index\",n),l.node.setAttribute(\"default-marker-size\",r.pSize);var c=i.graphics.group({class:\"apexcharts-series-markers\"});c&&c.add(l),u.add(c),h.add(u);var d=a.config.dataLabels;if(d.enabled){var p=d.formatter(a.globals.series[n][s],{seriesIndex:n,dataPointIndex:s,w:a});o.plotDataLabelsText({x:g[s].x,y:g[s].y,text:p,textAnchor:\"middle\",i:n,j:n,parent:f,offsetCorrection:!1,dataLabelsConfig:e({},d)})}h.add(f)})),r.push(h)})),this.drawPolygons({parent:d}),a.config.xaxis.labels.show){var p=this.drawXAxisTexts();d.add(p)}return r.forEach((function(t){d.add(t)})),d.add(this.yaxisLabels),d}},{key:\"drawPolygons\",value:function(t){for(var e=this,i=this.w,a=t.parent,s=new Ct(this.ctx),r=i.globals.yAxisScale[0].result.reverse(),o=r.length,n=[],l=this.size/(o-1),h=0;h<o;h++)n[h]=l*h;n.reverse();var c=[],d=[];n.forEach((function(t,i){var a=x.getPolygonPos(t,e.dataPointsLen),s=\"\";a.forEach((function(t,a){if(0===i){var r=e.graphics.drawLine(t.x,t.y,0,0,Array.isArray(e.polygons.connectorColors)?e.polygons.connectorColors[a]:e.polygons.connectorColors);d.push(r)}0===a&&e.yaxisLabelsTextsPos.push({x:t.x,y:t.y}),s+=t.x+\",\"+t.y+\" \"})),c.push(s)})),c.forEach((function(t,s){var r=e.polygons.strokeColors,o=e.polygons.strokeWidth,n=e.graphics.drawPolygon(t,Array.isArray(r)?r[s]:r,Array.isArray(o)?o[s]:o,i.globals.radarPolygons.fill.colors[s]);a.add(n)})),d.forEach((function(t){a.add(t)})),i.config.yaxis[0].show&&this.yaxisLabelsTextsPos.forEach((function(t,i){var a=s.drawYAxisTexts(t.x,t.y,i,r[i]);e.yaxisLabels.add(a)}))}},{key:\"drawXAxisTexts\",value:function(){var t=this,i=this.w,a=i.config.xaxis.labels,s=this.graphics.group({class:\"apexcharts-xaxis\"}),r=x.getPolygonPos(this.size,this.dataPointsLen);return i.globals.labels.forEach((function(o,n){var l=i.config.xaxis.labels.formatter,h=new O(t.ctx);if(r[n]){var c=t.getTextPos(r[n],t.size),d=l(o,{seriesIndex:-1,dataPointIndex:n,w:i});h.plotDataLabelsText({x:c.newX,y:c.newY,text:d,textAnchor:c.textAnchor,i:n,j:n,parent:s,color:Array.isArray(a.style.colors)&&a.style.colors[n]?a.style.colors[n]:\"#a8a8a8\",dataLabelsConfig:e({textAnchor:c.textAnchor,dropShadow:{enabled:!1}},a),offsetCorrection:!1})}})),s}},{key:\"createPaths\",value:function(t,e){var i=this,a=[],s=[],r=[],o=[];if(t.length){s=[this.graphics.move(e.x,e.y)],o=[this.graphics.move(e.x,e.y)];var n=this.graphics.move(t[0].x,t[0].y),l=this.graphics.move(t[0].x,t[0].y);t.forEach((function(e,a){n+=i.graphics.line(e.x,e.y),l+=i.graphics.line(e.x,e.y),a===t.length-1&&(n+=\"Z\",l+=\"Z\")})),a.push(n),r.push(l)}return{linePathsFrom:s,linePathsTo:a,areaPathsFrom:o,areaPathsTo:r}}},{key:\"getTextPos\",value:function(t,e){var i=\"middle\",a=t.x,s=t.y;return Math.abs(t.x)>=10?t.x>0?(i=\"start\",a+=10):t.x<0&&(i=\"end\",a-=10):i=\"middle\",Math.abs(t.y)>=e-10&&(t.y<0?s-=10:t.y>0&&(s+=10)),{textAnchor:i,newX:a,newY:s}}},{key:\"getPreviousPath\",value:function(t){for(var e=this.w,i=null,a=0;a<e.globals.previousPaths.length;a++){var s=e.globals.previousPaths[a];s.paths.length>0&&parseInt(s.realIndex,10)===parseInt(t,10)&&void 0!==e.globals.previousPaths[a].paths[0]&&(i=e.globals.previousPaths[a].paths[0].d)}return i}},{key:\"getDataPointsPos\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.dataPointsLen;t=t||[],e=e||[];for(var a=[],s=0;s<i;s++){var r={};r.x=t[s]*Math.sin(e[s]),r.y=-t[s]*Math.cos(e[s]),a.push(r)}return a}}]),t}(),Tt=function(t){n(i,Lt);var e=d(i);function i(t){var s;a(this,i),(s=e.call(this,t)).ctx=t,s.w=t.w,s.animBeginArr=[0],s.animDur=0;var r=s.w;return s.startAngle=r.config.plotOptions.radialBar.startAngle,s.endAngle=r.config.plotOptions.radialBar.endAngle,s.totalAngle=Math.abs(r.config.plotOptions.radialBar.endAngle-r.config.plotOptions.radialBar.startAngle),s.trackStartAngle=r.config.plotOptions.radialBar.track.startAngle,s.trackEndAngle=r.config.plotOptions.radialBar.track.endAngle,s.donutDataLabels=s.w.config.plotOptions.radialBar.dataLabels,s.radialDataLabels=s.donutDataLabels,s.trackStartAngle||(s.trackStartAngle=s.startAngle),s.trackEndAngle||(s.trackEndAngle=s.endAngle),360===s.endAngle&&(s.endAngle=359.99),s.margin=parseInt(r.config.plotOptions.radialBar.track.margin,10),s}return r(i,[{key:\"draw\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-radialbar\"});if(e.globals.noData)return a;var s=i.group(),r=this.defaultSize/2,o=e.globals.gridWidth/2,n=this.defaultSize/2.05;e.config.chart.sparkline.enabled||(n=n-e.config.stroke.width-e.config.chart.dropShadow.blur);var l=e.globals.fill.colors;if(e.config.plotOptions.radialBar.track.show){var h=this.drawTracks({size:n,centerX:o,centerY:r,colorArr:l,series:t});s.add(h)}var c=this.drawArcs({size:n,centerX:o,centerY:r,colorArr:l,series:t}),d=360;e.config.plotOptions.radialBar.startAngle<0&&(d=this.totalAngle);var g=(360-d)/360;if(e.globals.radialSize=n-n*g,this.radialDataLabels.value.show){var u=Math.max(this.radialDataLabels.value.offsetY,this.radialDataLabels.name.offsetY);e.globals.radialSize+=u*g}return s.add(c.g),\"front\"===e.config.plotOptions.radialBar.hollow.position&&(c.g.add(c.elHollow),c.dataLabels&&c.g.add(c.dataLabels)),a.add(s),a}},{key:\"drawTracks\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-tracks\"}),s=new v(this.ctx),r=new R(this.ctx),o=this.getStrokeWidth(t);t.size=t.size-o/2;for(var n=0;n<t.series.length;n++){var l=i.group({class:\"apexcharts-radialbar-track apexcharts-track\"});a.add(l),l.attr({rel:n+1}),t.size=t.size-o-this.margin;var h=e.config.plotOptions.radialBar.track,c=r.fillPath({seriesNumber:0,size:t.size,fillColors:Array.isArray(h.background)?h.background[n]:h.background,solid:!0}),d=this.trackStartAngle,g=this.trackEndAngle;Math.abs(g)+Math.abs(d)>=360&&(g=360-Math.abs(this.startAngle)-.1);var u=i.drawPath({d:\"\",stroke:c,strokeWidth:o*parseInt(h.strokeWidth,10)/100,fill:\"none\",strokeOpacity:h.opacity,classes:\"apexcharts-radialbar-area\"});if(h.dropShadow.enabled){var f=h.dropShadow;s.dropShadow(u,f)}l.add(u),u.attr(\"id\",\"apexcharts-radialbarTrack-\"+n),this.animatePaths(u,{centerX:t.centerX,centerY:t.centerY,endAngle:g,startAngle:d,size:t.size,i:n,totalItems:2,animBeginArr:0,dur:0,isTrack:!0,easing:e.globals.easing})}return a}},{key:\"drawArcs\",value:function(t){var e=this.w,i=new m(this.ctx),a=new R(this.ctx),s=new v(this.ctx),r=i.group(),o=this.getStrokeWidth(t);t.size=t.size-o/2;var n=e.config.plotOptions.radialBar.hollow.background,l=t.size-o*t.series.length-this.margin*t.series.length-o*parseInt(e.config.plotOptions.radialBar.track.strokeWidth,10)/100/2,h=l-e.config.plotOptions.radialBar.hollow.margin;void 0!==e.config.plotOptions.radialBar.hollow.image&&(n=this.drawHollowImage(t,r,l,n));var c=this.drawHollow({size:h,centerX:t.centerX,centerY:t.centerY,fill:n||\"transparent\"});if(e.config.plotOptions.radialBar.hollow.dropShadow.enabled){var d=e.config.plotOptions.radialBar.hollow.dropShadow;s.dropShadow(c,d)}var g=1;!this.radialDataLabels.total.show&&e.globals.series.length>1&&(g=0);var u=null;this.radialDataLabels.show&&(u=this.renderInnerDataLabels(this.radialDataLabels,{hollowSize:l,centerX:t.centerX,centerY:t.centerY,opacity:g})),\"back\"===e.config.plotOptions.radialBar.hollow.position&&(r.add(c),u&&r.add(u));var f=!1;e.config.plotOptions.radialBar.inverseOrder&&(f=!0);for(var p=f?t.series.length-1:0;f?p>=0:p<t.series.length;f?p--:p++){var b=i.group({class:\"apexcharts-series apexcharts-radial-series\",seriesName:x.escapeString(e.globals.seriesNames[p])});r.add(b),b.attr({rel:p+1,\"data:realIndex\":p}),this.ctx.series.addCollapsedClassToSeries(b,p),t.size=t.size-o-this.margin;var y=a.fillPath({seriesNumber:p,size:t.size,value:t.series[p]}),w=this.startAngle,k=void 0,A=x.negToZero(t.series[p]>100?100:t.series[p])/100,S=Math.round(this.totalAngle*A)+this.startAngle,C=void 0;e.globals.dataChanged&&(k=this.startAngle,C=Math.round(this.totalAngle*x.negToZero(e.globals.previousPaths[p])/100)+k),Math.abs(S)+Math.abs(w)>=360&&(S-=.01),Math.abs(C)+Math.abs(k)>=360&&(C-=.01);var L=S-w,P=Array.isArray(e.config.stroke.dashArray)?e.config.stroke.dashArray[p]:e.config.stroke.dashArray,T=i.drawPath({d:\"\",stroke:y,strokeWidth:o,fill:\"none\",fillOpacity:e.config.fill.opacity,classes:\"apexcharts-radialbar-area apexcharts-radialbar-slice-\"+p,strokeDashArray:P});if(m.setAttrs(T.node,{\"data:angle\":L,\"data:value\":t.series[p]}),e.config.chart.dropShadow.enabled){var M=e.config.chart.dropShadow;s.dropShadow(T,M,p)}s.setSelectionFilter(T,0,p),this.addListeners(T,this.radialDataLabels),b.add(T),T.attr({index:0,j:p});var I=0;!this.initialAnim||e.globals.resized||e.globals.dataChanged||(I=e.config.chart.animations.speed),e.globals.dataChanged&&(I=e.config.chart.animations.dynamicAnimation.speed),this.animDur=I/(1.2*t.series.length)+this.animDur,this.animBeginArr.push(this.animDur),this.animatePaths(T,{centerX:t.centerX,centerY:t.centerY,endAngle:S,startAngle:w,prevEndAngle:C,prevStartAngle:k,size:t.size,i:p,totalItems:2,animBeginArr:this.animBeginArr,dur:I,shouldSetPrevPaths:!0,easing:e.globals.easing})}return{g:r,elHollow:c,dataLabels:u}}},{key:\"drawHollow\",value:function(t){var e=new m(this.ctx).drawCircle(2*t.size);return e.attr({class:\"apexcharts-radialbar-hollow\",cx:t.centerX,cy:t.centerY,r:t.size,fill:t.fill}),e}},{key:\"drawHollowImage\",value:function(t,e,i,a){var s=this.w,r=new R(this.ctx),o=x.randomId(),n=s.config.plotOptions.radialBar.hollow.image;if(s.config.plotOptions.radialBar.hollow.imageClipped)r.clippedImgArea({width:i,height:i,image:n,patternID:\"pattern\".concat(s.globals.cuid).concat(o)}),a=\"url(#pattern\".concat(s.globals.cuid).concat(o,\")\");else{var l=s.config.plotOptions.radialBar.hollow.imageWidth,h=s.config.plotOptions.radialBar.hollow.imageHeight;if(void 0===l&&void 0===h){var c=s.globals.dom.Paper.image(n).loaded((function(e){this.move(t.centerX-e.width/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-e.height/2+s.config.plotOptions.radialBar.hollow.imageOffsetY)}));e.add(c)}else{var d=s.globals.dom.Paper.image(n).loaded((function(e){this.move(t.centerX-l/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-h/2+s.config.plotOptions.radialBar.hollow.imageOffsetY),this.size(l,h)}));e.add(d)}}return a}},{key:\"getStrokeWidth\",value:function(t){var e=this.w;return t.size*(100-parseInt(e.config.plotOptions.radialBar.hollow.size,10))/100/(t.series.length+1)-this.margin}}]),i}(),Mt=function(t){n(s,yt);var i=d(s);function s(){return a(this,s),i.apply(this,arguments)}return r(s,[{key:\"draw\",value:function(t,i){var a=this.w,s=new m(this.ctx);this.rangeBarOptions=this.w.config.plotOptions.rangeBar,this.series=t,this.seriesRangeStart=a.globals.seriesRangeStart,this.seriesRangeEnd=a.globals.seriesRangeEnd,this.barHelpers.initVariables(t);for(var r=s.group({class:\"apexcharts-rangebar-series apexcharts-plot-series\"}),o=0;o<t.length;o++){var n,l,h,c=void 0,d=void 0,g=void 0,u=a.globals.comboCharts?i[o]:o,f=s.group({class:\"apexcharts-series\",seriesName:x.escapeString(a.globals.seriesNames[u]),rel:o+1,\"data:realIndex\":u});this.ctx.series.addCollapsedClassToSeries(f,u),t[o].length>0&&(this.visibleI=this.visibleI+1);var p=0,b=0;this.yRatio.length>1&&(this.yaxisIndex=u);var v=this.barHelpers.initialPositions();d=v.y,h=v.zeroW,c=v.x,b=v.barWidth,n=v.xDivision,l=v.zeroH;for(var y=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":u}),w=s.group({class:\"apexcharts-rangebar-goals-markers\",style:\"pointer-events: none\"}),k=0;k<a.globals.dataPoints;k++){var A=this.barHelpers.getStrokeWidth(o,k,u),S=this.seriesRangeStart[o][k],C=this.seriesRangeEnd[o][k],L=null,P=null,T={x:c,y:d,strokeWidth:A,elSeries:f};if(g=v.yDivision,p=v.barHeight,this.isHorizontal){P=d+p*this.visibleI;var M=this.seriesLen;a.config.plotOptions.bar.rangeBarGroupRows&&(M=1);var I=(g-p*M)/2;if(void 0===a.config.series[o].data[k])break;if(a.config.series[o].data[k].x){var z=this.detectOverlappingBars({i:o,j:k,barYPosition:P,srty:I,barHeight:p,yDivision:g,initPositions:v});p=z.barHeight,P=z.barYPosition}b=(L=this.drawRangeBarPaths(e({indexes:{i:o,j:k,realIndex:u},barHeight:p,barYPosition:P,zeroW:h,yDivision:g,y1:S,y2:C},T))).barWidth}else p=(L=this.drawRangeColumnPaths(e({indexes:{i:o,j:k,realIndex:u},zeroH:l,barWidth:b,xDivision:n},T))).barHeight;var X=this.barHelpers.drawGoalLine({barXPosition:L.barXPosition,barYPosition:P,goalX:L.goalX,goalY:L.goalY,barHeight:p,barWidth:b});X&&w.add(X),d=L.y,c=L.x;var E=this.barHelpers.getPathFillColor(t,o,k,u),Y=a.globals.stroke.colors[u];this.renderSeries({realIndex:u,pathFill:E,lineFill:Y,j:k,i:o,x:c,y:d,y1:S,y2:C,pathFrom:L.pathFrom,pathTo:L.pathTo,strokeWidth:A,elSeries:f,series:t,barHeight:p,barYPosition:P,barWidth:b,elDataLabelsWrap:y,elGoalsMarkers:w,visibleSeries:this.visibleI,type:\"rangebar\"})}r.add(f)}return r}},{key:\"detectOverlappingBars\",value:function(t){var e=t.i,i=t.j,a=t.barYPosition,s=t.srty,r=t.barHeight,o=t.yDivision,n=t.initPositions,l=this.w,h=[],c=l.config.series[e].data[i].rangeName,d=l.config.series[e].data[i].x,g=l.globals.labels.indexOf(d),u=l.globals.seriesRange[e].findIndex((function(t){return t.x===d&&t.overlaps.length>0}));return a=l.config.plotOptions.bar.rangeBarGroupRows?s+o*g:s+r*this.visibleI+o*g,u>-1&&!l.config.plotOptions.bar.rangeBarOverlap&&(h=l.globals.seriesRange[e][u].overlaps).indexOf(c)>-1&&(a=(r=n.barHeight/h.length)*this.visibleI+o*(100-parseInt(this.barOptions.barHeight,10))/100/2+r*(this.visibleI+h.indexOf(c))+o*g),{barYPosition:a,barHeight:r}}},{key:\"drawRangeColumnPaths\",value:function(t){var e=t.indexes,i=t.x;t.strokeWidth;var a=t.xDivision,s=t.barWidth,r=t.zeroH,o=this.w,n=e.i,l=e.j,h=this.yRatio[this.yaxisIndex],c=e.realIndex,d=this.getRangeValue(c,l),g=Math.min(d.start,d.end),u=Math.max(d.start,d.end);o.globals.isXNumeric&&(i=(o.globals.seriesX[n][l]-o.globals.minX)/this.xRatio-s/2);var f=i+s*this.visibleI;void 0===this.series[n][l]||null===this.series[n][l]?g=r:(g=r-g/h,u=r-u/h);var p=Math.abs(u-g),x=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:s,y1:g,y2:u,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,realIndex:e.realIndex,i:c,j:l,w:o});return o.globals.isXNumeric||(i+=a),{pathTo:x.pathTo,pathFrom:x.pathFrom,barHeight:p,x:i,y:u,goalY:this.barHelpers.getGoalValues(\"y\",null,r,n,l),barXPosition:f}}},{key:\"drawRangeBarPaths\",value:function(t){var e=t.indexes,i=t.y,a=t.y1,s=t.y2,r=t.yDivision,o=t.barHeight,n=t.barYPosition,l=t.zeroW,h=this.w,c=l+a/this.invertedYRatio,d=l+s/this.invertedYRatio,g=Math.abs(d-c),u=this.barHelpers.getBarpaths({barYPosition:n,barHeight:o,x1:c,x2:d,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,i:e.realIndex,realIndex:e.realIndex,j:e.j,w:h});return h.globals.isXNumeric||(i+=r),{pathTo:u.pathTo,pathFrom:u.pathFrom,barWidth:g,x:d,goalX:this.barHelpers.getGoalValues(\"x\",l,null,e.realIndex,e.j),y:i}}},{key:\"getRangeValue\",value:function(t,e){var i=this.w;return{start:i.globals.seriesRangeStart[t][e],end:i.globals.seriesRangeEnd[t][e]}}}]),s}(),It=function(){function t(e){a(this,t),this.w=e.w,this.lineCtx=e}return r(t,[{key:\"sameValueSeriesFix\",value:function(t,e){var i=this.w;if((\"gradient\"===i.config.fill.type||\"gradient\"===i.config.fill.type[t])&&new y(this.lineCtx.ctx,i).seriesHaveSameValues(t)){var a=e[t].slice();a[a.length-1]=a[a.length-1]+1e-6,e[t]=a}return e}},{key:\"calculatePoints\",value:function(t){var e=t.series,i=t.realIndex,a=t.x,s=t.y,r=t.i,o=t.j,n=t.prevY,l=this.w,h=[],c=[];if(0===o){var d=this.lineCtx.categoryAxisCorrection+l.config.markers.offsetX;l.globals.isXNumeric&&(d=(l.globals.seriesX[i][0]-l.globals.minX)/this.lineCtx.xRatio+l.config.markers.offsetX),h.push(d),c.push(x.isNumber(e[r][0])?n+l.config.markers.offsetY:null),h.push(a+l.config.markers.offsetX),c.push(x.isNumber(e[r][o+1])?s+l.config.markers.offsetY:null)}else h.push(a+l.config.markers.offsetX),c.push(x.isNumber(e[r][o+1])?s+l.config.markers.offsetY:null);return{x:h,y:c}}},{key:\"checkPreviousPaths\",value:function(t){for(var e=t.pathFromLine,i=t.pathFromArea,a=t.realIndex,s=this.w,r=0;r<s.globals.previousPaths.length;r++){var o=s.globals.previousPaths[r];(\"line\"===o.type||\"area\"===o.type)&&o.paths.length>0&&parseInt(o.realIndex,10)===parseInt(a,10)&&(\"line\"===o.type?(this.lineCtx.appendPathFrom=!1,e=s.globals.previousPaths[r].paths[0].d):\"area\"===o.type&&(this.lineCtx.appendPathFrom=!1,i=s.globals.previousPaths[r].paths[0].d,s.config.stroke.show&&s.globals.previousPaths[r].paths[1]&&(e=s.globals.previousPaths[r].paths[1].d)))}return{pathFromLine:e,pathFromArea:i}}},{key:\"determineFirstPrevY\",value:function(t){var e,i=t.i,a=t.series,s=t.prevY,r=t.lineYPosition,o=this.w;if(void 0!==(null===(e=a[i])||void 0===e?void 0:e[0]))s=(r=o.config.chart.stacked&&i>0?this.lineCtx.prevSeriesY[i-1][0]:this.lineCtx.zeroY)-a[i][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]+2*(this.lineCtx.isReversed?a[i][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]:0);else if(o.config.chart.stacked&&i>0&&void 0===a[i][0])for(var n=i-1;n>=0;n--)if(null!==a[n][0]&&void 0!==a[n][0]){s=r=this.lineCtx.prevSeriesY[n][0];break}return{prevY:s,lineYPosition:r}}}]),t}(),zt=function(){function t(e,i,s){a(this,t),this.ctx=e,this.w=e.w,this.xyRatios=i,this.pointsChart=!(\"bubble\"!==this.w.config.chart.type&&\"scatter\"!==this.w.config.chart.type)||s,this.scatter=new H(this.ctx),this.noNegatives=this.w.globals.minX===Number.MAX_VALUE,this.lineHelpers=new It(this),this.markers=new D(this.ctx),this.prevSeriesY=[],this.categoryAxisCorrection=0,this.yaxisIndex=0}return r(t,[{key:\"draw\",value:function(t,i,a,s){var r=this.w,o=new m(this.ctx),n=r.globals.comboCharts?i:r.config.chart.type,l=o.group({class:\"apexcharts-\".concat(n,\"-series apexcharts-plot-series\")}),h=new y(this.ctx,r);this.yRatio=this.xyRatios.yRatio,this.zRatio=this.xyRatios.zRatio,this.xRatio=this.xyRatios.xRatio,this.baseLineY=this.xyRatios.baseLineY,t=h.getLogSeries(t),this.yRatio=h.getLogYRatios(this.yRatio);for(var c=[],d=0;d<t.length;d++){t=this.lineHelpers.sameValueSeriesFix(d,t);var g=r.globals.comboCharts?a[d]:d;this._initSerieVariables(t,d,g);var u=[],f=[],p=r.globals.padHorizontal+this.categoryAxisCorrection;this.ctx.series.addCollapsedClassToSeries(this.elSeries,g),r.globals.isXNumeric&&r.globals.seriesX.length>0&&(p=(r.globals.seriesX[g][0]-r.globals.minX)/this.xRatio),f.push(p);var x,b=p,v=void 0,w=b,k=this.zeroY,A=this.zeroY;k=this.lineHelpers.determineFirstPrevY({i:d,series:t,prevY:k,lineYPosition:0}).prevY,u.push(k),x=k;\"rangeArea\"===n&&(v=A=this.lineHelpers.determineFirstPrevY({i:d,series:s,prevY:A,lineYPosition:0}).prevY);var S={type:n,series:t,realIndex:g,i:d,x:p,y:1,pX:b,pY:x,pathsFrom:this._calculatePathsFrom({type:n,series:t,i:d,realIndex:g,prevX:w,prevY:k,prevY2:A}),linePaths:[],areaPaths:[],seriesIndex:a,lineYPosition:0,xArrj:f,yArrj:u,seriesRangeEnd:s},C=this._iterateOverDataPoints(e(e({},S),{},{iterations:\"rangeArea\"===n?t[d].length-1:void 0,isRangeStart:!0}));if(\"rangeArea\"===n){var L=this._calculatePathsFrom({series:s,i:d,realIndex:g,prevX:w,prevY:A}),P=this._iterateOverDataPoints(e(e({},S),{},{series:s,pY:v,pathsFrom:L,iterations:s[d].length-1,isRangeStart:!1}));C.linePaths[0]=P.linePath+C.linePath,C.pathFromLine=P.pathFromLine+C.pathFromLine}this._handlePaths({type:n,realIndex:g,i:d,paths:C}),this.elSeries.add(this.elPointsMain),this.elSeries.add(this.elDataLabelsWrap),c.push(this.elSeries)}if(r.config.chart.stacked)for(var T=c.length;T>0;T--)l.add(c[T-1]);else for(var M=0;M<c.length;M++)l.add(c[M]);return l}},{key:\"_initSerieVariables\",value:function(t,e,i){var a=this.w,s=new m(this.ctx);this.xDivision=a.globals.gridWidth/(a.globals.dataPoints-(\"on\"===a.config.xaxis.tickPlacement?1:0)),this.strokeWidth=Array.isArray(a.config.stroke.width)?a.config.stroke.width[i]:a.config.stroke.width,this.yRatio.length>1&&(this.yaxisIndex=i),this.isReversed=a.config.yaxis[this.yaxisIndex]&&a.config.yaxis[this.yaxisIndex].reversed,this.zeroY=a.globals.gridHeight-this.baseLineY[this.yaxisIndex]-(this.isReversed?a.globals.gridHeight:0)+(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),this.areaBottomY=this.zeroY,(this.zeroY>a.globals.gridHeight||\"end\"===a.config.plotOptions.area.fillTo)&&(this.areaBottomY=a.globals.gridHeight),this.categoryAxisCorrection=this.xDivision/2,this.elSeries=s.group({class:\"apexcharts-series\",seriesName:x.escapeString(a.globals.seriesNames[i])}),this.elPointsMain=s.group({class:\"apexcharts-series-markers-wrap\",\"data:realIndex\":i}),this.elDataLabelsWrap=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":i});var r=t[e].length===a.globals.dataPoints;this.elSeries.attr({\"data:longestSeries\":r,rel:e+1,\"data:realIndex\":i}),this.appendPathFrom=!0}},{key:\"_calculatePathsFrom\",value:function(t){var e,i,a,s,r=t.type,o=t.series,n=t.i,l=t.realIndex,h=t.prevX,c=t.prevY,d=t.prevY2,g=this.w,u=new m(this.ctx);if(null===o[n][0]){for(var f=0;f<o[n].length;f++)if(null!==o[n][f]){h=this.xDivision*f,c=this.zeroY-o[n][f]/this.yRatio[this.yaxisIndex],e=u.move(h,c),i=u.move(h,this.areaBottomY);break}}else e=u.move(h,c),\"rangeArea\"===r&&(e=u.move(h,d)+u.line(h,c)),i=u.move(h,this.areaBottomY)+u.line(h,c);if(a=u.move(-1,this.zeroY)+u.line(-1,this.zeroY),s=u.move(-1,this.zeroY)+u.line(-1,this.zeroY),g.globals.previousPaths.length>0){var p=this.lineHelpers.checkPreviousPaths({pathFromLine:a,pathFromArea:s,realIndex:l});a=p.pathFromLine,s=p.pathFromArea}return{prevX:h,prevY:c,linePath:e,areaPath:i,pathFromLine:a,pathFromArea:s}}},{key:\"_handlePaths\",value:function(t){var i=t.type,a=t.realIndex,s=t.i,r=t.paths,o=this.w,n=new m(this.ctx),l=new R(this.ctx);this.prevSeriesY.push(r.yArrj),o.globals.seriesXvalues[a]=r.xArrj,o.globals.seriesYvalues[a]=r.yArrj;var h=o.config.forecastDataPoints;if(h.count>0&&\"rangeArea\"!==i){var c=o.globals.seriesXvalues[a][o.globals.seriesXvalues[a].length-h.count-1],d=n.drawRect(c,0,o.globals.gridWidth,o.globals.gridHeight,0);o.globals.dom.elForecastMask.appendChild(d.node);var g=n.drawRect(0,0,c,o.globals.gridHeight,0);o.globals.dom.elNonForecastMask.appendChild(g.node)}this.pointsChart||o.globals.delayedElements.push({el:this.elPointsMain.node,index:a});var u={i:s,realIndex:a,animationDelay:s,initialSpeed:o.config.chart.animations.speed,dataChangeSpeed:o.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-\".concat(i)};if(\"area\"===i)for(var f=l.fillPath({seriesNumber:a}),p=0;p<r.areaPaths.length;p++){var x=n.renderPaths(e(e({},u),{},{pathFrom:r.pathFromArea,pathTo:r.areaPaths[p],stroke:\"none\",strokeWidth:0,strokeLineCap:null,fill:f}));this.elSeries.add(x)}if(o.config.stroke.show&&!this.pointsChart){var b=null;if(\"line\"===i)b=l.fillPath({seriesNumber:a,i:s});else if(\"solid\"===o.config.stroke.fill.type)b=o.globals.stroke.colors[a];else{var v=o.config.fill;o.config.fill=o.config.stroke.fill,b=l.fillPath({seriesNumber:a,i:s}),o.config.fill=v}for(var y=0;y<r.linePaths.length;y++){var w=b;\"rangeArea\"===i&&(w=l.fillPath({seriesNumber:a}));var k=e(e({},u),{},{pathFrom:r.pathFromLine,pathTo:r.linePaths[y],stroke:b,strokeWidth:this.strokeWidth,strokeLineCap:o.config.stroke.lineCap,fill:\"rangeArea\"===i?w:\"none\"}),A=n.renderPaths(k);if(this.elSeries.add(A),A.attr(\"fill-rule\",\"evenodd\"),h.count>0&&\"rangeArea\"!==i){var S=n.renderPaths(k);S.node.setAttribute(\"stroke-dasharray\",h.dashArray),h.strokeWidth&&S.node.setAttribute(\"stroke-width\",h.strokeWidth),this.elSeries.add(S),S.attr(\"clip-path\",\"url(#forecastMask\".concat(o.globals.cuid,\")\")),A.attr(\"clip-path\",\"url(#nonForecastMask\".concat(o.globals.cuid,\")\"))}}}}},{key:\"_iterateOverDataPoints\",value:function(t){var e=t.type,i=t.series,a=t.iterations,s=t.realIndex,r=t.i,o=t.x,n=t.y,l=t.pX,h=t.pY,c=t.pathsFrom,d=t.linePaths,g=t.areaPaths,u=t.seriesIndex,f=t.lineYPosition,p=t.xArrj,b=t.yArrj,v=t.isRangeStart,y=t.seriesRangeEnd,w=this.w,k=new m(this.ctx),A=this.yRatio,S=c.prevY,C=c.linePath,L=c.areaPath,P=c.pathFromLine,T=c.pathFromArea,M=x.isNumber(w.globals.minYArr[s])?w.globals.minYArr[s]:w.globals.minY;a||(a=w.globals.dataPoints>1?w.globals.dataPoints-1:w.globals.dataPoints);for(var I=n,z=0;z<a;z++){var X=void 0===i[r][z+1]||null===i[r][z+1];if(w.globals.isXNumeric){var E=w.globals.seriesX[s][z+1];void 0===w.globals.seriesX[s][z+1]&&(E=w.globals.seriesX[s][a-1]),o=(E-w.globals.minX)/this.xRatio}else o+=this.xDivision;if(w.config.chart.stacked)if(r>0&&w.globals.collapsedSeries.length<w.config.series.length-1){f=this.prevSeriesY[function(t){for(var e=t,i=0;i<w.globals.series.length;i++)if(w.globals.collapsedSeriesIndices.indexOf(t)>-1){e--;break}return e>=0?e:0}(r-1)][z+1]}else f=this.zeroY;else f=this.zeroY;X?n=f-M/A[this.yaxisIndex]+2*(this.isReversed?M/A[this.yaxisIndex]:0):(n=f-i[r][z+1]/A[this.yaxisIndex]+2*(this.isReversed?i[r][z+1]/A[this.yaxisIndex]:0),\"rangeArea\"===e&&(I=f-y[r][z+1]/A[this.yaxisIndex]+2*(this.isReversed?y[r][z+1]/A[this.yaxisIndex]:0))),p.push(o),b.push(n);var Y=this.lineHelpers.calculatePoints({series:i,x:o,y:n,realIndex:s,i:r,j:z,prevY:S}),F=this._createPaths({type:e,series:i,i:r,realIndex:s,j:z,x:o,y:n,y2:I,pX:l,pY:h,linePath:C,areaPath:L,linePaths:d,areaPaths:g,seriesIndex:u,isRangeStart:v});g=F.areaPaths,d=F.linePaths,l=F.pX,h=F.pY,L=F.areaPath,C=F.linePath,this.appendPathFrom&&(P+=k.line(o,this.zeroY),T+=k.line(o,this.zeroY)),this.handleNullDataPoints(i,Y,r,z,s),this._handleMarkersAndLabels({type:e,pointsPos:Y,i:r,j:z,realIndex:s,isRangeStart:v})}return{yArrj:b,xArrj:p,pathFromArea:T,areaPaths:g,pathFromLine:P,linePaths:d,linePath:C,areaPath:L}}},{key:\"_handleMarkersAndLabels\",value:function(t){var e=t.type,i=t.pointsPos,a=t.isRangeStart,s=t.i,r=t.j,o=t.realIndex,n=this.w,l=new O(this.ctx);if(this.pointsChart)this.scatter.draw(this.elSeries,r,{realIndex:o,pointsPos:i,zRatio:this.zRatio,elParent:this.elPointsMain});else{n.globals.series[s].length>1&&this.elPointsMain.node.classList.add(\"apexcharts-element-hidden\");var h=this.markers.plotChartMarkers(i,o,r+1);null!==h&&this.elPointsMain.add(h)}var c=l.drawDataLabel({type:e,isRangeStart:a,pos:i,i:o,j:r+1});null!==c&&this.elDataLabelsWrap.add(c)}},{key:\"_createPaths\",value:function(t){var e=t.type,i=t.series,a=t.i,s=t.realIndex,r=t.j,o=t.x,n=t.y,l=t.y2,h=t.pX,c=t.pY,d=t.linePath,g=t.areaPath,u=t.linePaths,f=t.areaPaths,p=t.seriesIndex,x=t.isRangeStart,b=this.w,v=new m(this.ctx),y=b.config.stroke.curve,w=this.areaBottomY;if(Array.isArray(b.config.stroke.curve)&&(y=Array.isArray(p)?b.config.stroke.curve[p[a]]:b.config.stroke.curve[a]),\"smooth\"===y){var k=.35*(o-h);b.globals.hasNullValues?(null!==i[a][r]&&(null!==i[a][r+1]?(d=v.move(h,c)+v.curve(h+k,c,o-k,n,o+1,n),g=v.move(h+1,c)+v.curve(h+k,c,o-k,n,o+1,n)+v.line(o,w)+v.line(h,w)+\"z\"):(d=v.move(h,c),g=v.move(h,c)+\"z\")),u.push(d),f.push(g)):(d+=v.curve(h+k,c,o-k,n,o,n),g+=v.curve(h+k,c,o-k,n,o,n)),h=o,c=n,r===i[a].length-2&&(g=g+v.curve(h,c,o,n,o,w)+v.move(o,n)+\"z\",\"rangeArea\"===e&&x?d=d+v.curve(h,c,o,n,o,l)+v.move(o,l)+\"z\":b.globals.hasNullValues||(u.push(d),f.push(g)))}else{if(null===i[a][r+1]){d+=v.move(o,n);var A=b.globals.isXNumeric?(b.globals.seriesX[s][r]-b.globals.minX)/this.xRatio:o-this.xDivision;g=g+v.line(A,w)+v.move(o,n)+\"z\"}null===i[a][r]&&(d+=v.move(o,n),g+=v.move(o,w)),\"stepline\"===y?(d=d+v.line(o,null,\"H\")+v.line(null,n,\"V\"),g=g+v.line(o,null,\"H\")+v.line(null,n,\"V\")):\"straight\"===y&&(d+=v.line(o,n),g+=v.line(o,n)),r===i[a].length-2&&(g=g+v.line(o,w)+v.move(o,n)+\"z\",\"rangeArea\"===e&&x?d=d+v.line(o,l)+v.move(o,l)+\"z\":(u.push(d),f.push(g)))}return{linePaths:u,areaPaths:f,pX:h,pY:c,linePath:d,areaPath:g}}},{key:\"handleNullDataPoints\",value:function(t,e,i,a,s){var r=this.w;if(null===t[i][a]&&r.config.markers.showNullDataPoints||1===t[i].length){var o=this.markers.plotChartMarkers(e,s,a+1,this.strokeWidth-r.config.markers.strokeWidth/2,!0);null!==o&&this.elPointsMain.add(o)}}}]),t}();window.TreemapSquared={},window.TreemapSquared.generate=function(){function t(e,i,a,s){this.xoffset=e,this.yoffset=i,this.height=s,this.width=a,this.shortestEdge=function(){return Math.min(this.height,this.width)},this.getCoordinates=function(t){var e,i=[],a=this.xoffset,s=this.yoffset,o=r(t)/this.height,n=r(t)/this.width;if(this.width>=this.height)for(e=0;e<t.length;e++)i.push([a,s,a+o,s+t[e]/o]),s+=t[e]/o;else for(e=0;e<t.length;e++)i.push([a,s,a+t[e]/n,s+n]),a+=t[e]/n;return i},this.cutArea=function(e){var i;if(this.width>=this.height){var a=e/this.height,s=this.width-a;i=new t(this.xoffset+a,this.yoffset,s,this.height)}else{var r=e/this.width,o=this.height-r;i=new t(this.xoffset,this.yoffset+r,this.width,o)}return i}}function e(e,a,s,o,n){o=void 0===o?0:o,n=void 0===n?0:n;var l=i(function(t,e){var i,a=[],s=e/r(t);for(i=0;i<t.length;i++)a[i]=t[i]*s;return a}(e,a*s),[],new t(o,n,a,s),[]);return function(t){var e,i,a=[];for(e=0;e<t.length;e++)for(i=0;i<t[e].length;i++)a.push(t[e][i]);return a}(l)}function i(t,e,s,o){var n,l,h;if(0!==t.length)return n=s.shortestEdge(),function(t,e,i){var s;if(0===t.length)return!0;(s=t.slice()).push(e);var r=a(t,i),o=a(s,i);return r>=o}(e,l=t[0],n)?(e.push(l),i(t.slice(1),e,s,o)):(h=s.cutArea(r(e),o),o.push(s.getCoordinates(e)),i(t,[],h,o)),o;o.push(s.getCoordinates(e))}function a(t,e){var i=Math.min.apply(Math,t),a=Math.max.apply(Math,t),s=r(t);return Math.max(Math.pow(e,2)*a/Math.pow(s,2),Math.pow(s,2)/(Math.pow(e,2)*i))}function s(t){return t&&t.constructor===Array}function r(t){var e,i=0;for(e=0;e<t.length;e++)i+=t[e];return i}function o(t){var e,i=0;if(s(t[0]))for(e=0;e<t.length;e++)i+=o(t[e]);else i=r(t);return i}return function t(i,a,r,n,l){n=void 0===n?0:n,l=void 0===l?0:l;var h,c,d=[],g=[];if(s(i[0])){for(c=0;c<i.length;c++)d[c]=o(i[c]);for(h=e(d,a,r,n,l),c=0;c<i.length;c++)g.push(t(i[c],h[c][2]-h[c][0],h[c][3]-h[c][1],h[c][0],h[c][1]))}else g=e(i,a,r,n,l);return g}}();var Xt,Et,Yt=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w,this.strokeWidth=this.w.config.stroke.width,this.helpers=new At(e),this.dynamicAnim=this.w.config.chart.animations.dynamicAnimation,this.labels=[]}return r(t,[{key:\"draw\",value:function(t){var e=this,i=this.w,a=new m(this.ctx),s=new R(this.ctx),r=a.group({class:\"apexcharts-treemap\"});if(i.globals.noData)return r;var o=[];return t.forEach((function(t){var e=t.map((function(t){return Math.abs(t)}));o.push(e)})),this.negRange=this.helpers.checkColorRange(),i.config.series.forEach((function(t,i){t.data.forEach((function(t){Array.isArray(e.labels[i])||(e.labels[i]=[]),e.labels[i].push(t.x)}))})),window.TreemapSquared.generate(o,i.globals.gridWidth,i.globals.gridHeight).forEach((function(o,n){var l=a.group({class:\"apexcharts-series apexcharts-treemap-series\",seriesName:x.escapeString(i.globals.seriesNames[n]),rel:n+1,\"data:realIndex\":n});if(i.config.chart.dropShadow.enabled){var h=i.config.chart.dropShadow;new v(e.ctx).dropShadow(r,h,n)}var c=a.group({class:\"apexcharts-data-labels\"});o.forEach((function(r,o){var h=r[0],c=r[1],d=r[2],g=r[3],u=a.drawRect(h,c,d-h,g-c,0,\"#fff\",1,e.strokeWidth,i.config.plotOptions.treemap.useFillColorAsStroke?p:i.globals.stroke.colors[n]);u.attr({cx:h,cy:c,index:n,i:n,j:o,width:d-h,height:g-c});var f=e.helpers.getShadeColor(i.config.chart.type,n,o,e.negRange),p=f.color;void 0!==i.config.series[n].data[o]&&i.config.series[n].data[o].fillColor&&(p=i.config.series[n].data[o].fillColor);var x=s.fillPath({color:p,seriesNumber:n,dataPointIndex:o});u.node.classList.add(\"apexcharts-treemap-rect\"),u.attr({fill:x}),e.helpers.addListeners(u);var b={x:h+(d-h)/2,y:c+(g-c)/2,width:0,height:0},v={x:h,y:c,width:d-h,height:g-c};if(i.config.chart.animations.enabled&&!i.globals.dataChanged){var m=1;i.globals.resized||(m=i.config.chart.animations.speed),e.animateTreemap(u,b,v,m)}if(i.globals.dataChanged){var y=1;e.dynamicAnim.enabled&&i.globals.shouldAnimate&&(y=e.dynamicAnim.speed,i.globals.previousPaths[n]&&i.globals.previousPaths[n][o]&&i.globals.previousPaths[n][o].rect&&(b=i.globals.previousPaths[n][o].rect),e.animateTreemap(u,b,v,y))}var w=e.getFontSize(r),k=i.config.dataLabels.formatter(e.labels[n][o],{value:i.globals.series[n][o],seriesIndex:n,dataPointIndex:o,w:i}),A=e.helpers.calculateDataLabels({text:k,x:(h+d)/2,y:(c+g)/2+e.strokeWidth/2+w/3,i:n,j:o,colorProps:f,fontSize:w,series:t});i.config.dataLabels.enabled&&A&&e.rotateToFitLabel(A,w,k,h,c,d,g),l.add(u),null!==A&&l.add(A)})),l.add(c),r.add(l)})),r}},{key:\"getFontSize\",value:function(t){var e=this.w;var i,a,s,r,o=function t(e){var i,a=0;if(Array.isArray(e[0]))for(i=0;i<e.length;i++)a+=t(e[i]);else for(i=0;i<e.length;i++)a+=e[i].length;return a}(this.labels)/function t(e){var i,a=0;if(Array.isArray(e[0]))for(i=0;i<e.length;i++)a+=t(e[i]);else for(i=0;i<e.length;i++)a+=1;return a}(this.labels);return i=t[2]-t[0],a=t[3]-t[1],s=i*a,r=Math.pow(s,.5),Math.min(r/o,parseInt(e.config.dataLabels.style.fontSize,10))}},{key:\"rotateToFitLabel\",value:function(t,e,i,a,s,r,o){var n=new m(this.ctx),l=n.getTextRects(i,e);if(l.width+this.w.config.stroke.width+5>r-a&&l.width<=o-s){var h=n.rotateAroundCenter(t.node);t.node.setAttribute(\"transform\",\"rotate(-90 \".concat(h.x,\" \").concat(h.y,\")\"))}}},{key:\"animateTreemap\",value:function(t,e,i,a){var s=new b(this.ctx);s.animateRect(t,{x:e.x,y:e.y,width:e.width,height:e.height},{x:i.x,y:i.y,width:i.width,height:i.height},a,(function(){s.animationCompleted(t)}))}}]),t}(),Ft=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.timeScaleArray=[],this.utc=this.w.config.xaxis.labels.datetimeUTC}return r(t,[{key:\"calculateTimeScaleTicks\",value:function(t,i){var a=this,s=this.w;if(s.globals.allSeriesCollapsed)return s.globals.labels=[],s.globals.timescaleLabels=[],[];var r=new T(this.ctx),o=(i-t)/864e5;this.determineInterval(o),s.globals.disableZoomIn=!1,s.globals.disableZoomOut=!1,o<.00011574074074074075?s.globals.disableZoomIn=!0:o>5e4&&(s.globals.disableZoomOut=!0);var n=r.getTimeUnitsfromTimestamp(t,i,this.utc),l=s.globals.gridWidth/o,h=l/24,c=h/60,d=c/60,g=Math.floor(24*o),u=Math.floor(1440*o),f=Math.floor(86400*o),p=Math.floor(o),x=Math.floor(o/30),b=Math.floor(o/365),v={minMillisecond:n.minMillisecond,minSecond:n.minSecond,minMinute:n.minMinute,minHour:n.minHour,minDate:n.minDate,minMonth:n.minMonth,minYear:n.minYear},m={firstVal:v,currentMillisecond:v.minMillisecond,currentSecond:v.minSecond,currentMinute:v.minMinute,currentHour:v.minHour,currentMonthDate:v.minDate,currentDate:v.minDate,currentMonth:v.minMonth,currentYear:v.minYear,daysWidthOnXAxis:l,hoursWidthOnXAxis:h,minutesWidthOnXAxis:c,secondsWidthOnXAxis:d,numberOfSeconds:f,numberOfMinutes:u,numberOfHours:g,numberOfDays:p,numberOfMonths:x,numberOfYears:b};switch(this.tickInterval){case\"years\":this.generateYearScale(m);break;case\"months\":case\"half_year\":this.generateMonthScale(m);break;case\"months_days\":case\"months_fortnight\":case\"days\":case\"week_days\":this.generateDayScale(m);break;case\"hours\":this.generateHourScale(m);break;case\"minutes_fives\":case\"minutes\":this.generateMinuteScale(m);break;case\"seconds_tens\":case\"seconds_fives\":case\"seconds\":this.generateSecondScale(m)}var y=this.timeScaleArray.map((function(t){var i={position:t.position,unit:t.unit,year:t.year,day:t.day?t.day:1,hour:t.hour?t.hour:0,month:t.month+1};return\"month\"===t.unit?e(e({},i),{},{day:1,value:t.value+1}):\"day\"===t.unit||\"hour\"===t.unit?e(e({},i),{},{value:t.value}):\"minute\"===t.unit?e(e({},i),{},{value:t.value,minute:t.value}):\"second\"===t.unit?e(e({},i),{},{value:t.value,minute:t.minute,second:t.second}):t}));return y.filter((function(t){var e=1,i=Math.ceil(s.globals.gridWidth/120),r=t.value;void 0!==s.config.xaxis.tickAmount&&(i=s.config.xaxis.tickAmount),y.length>i&&(e=Math.floor(y.length/i));var o=!1,n=!1;switch(a.tickInterval){case\"years\":\"year\"===t.unit&&(o=!0);break;case\"half_year\":e=7,\"year\"===t.unit&&(o=!0);break;case\"months\":e=1,\"year\"===t.unit&&(o=!0);break;case\"months_fortnight\":e=15,\"year\"!==t.unit&&\"month\"!==t.unit||(o=!0),30===r&&(n=!0);break;case\"months_days\":e=10,\"month\"===t.unit&&(o=!0),30===r&&(n=!0);break;case\"week_days\":e=8,\"month\"===t.unit&&(o=!0);break;case\"days\":e=1,\"month\"===t.unit&&(o=!0);break;case\"hours\":\"day\"===t.unit&&(o=!0);break;case\"minutes_fives\":case\"seconds_fives\":r%5!=0&&(n=!0);break;case\"seconds_tens\":r%10!=0&&(n=!0)}if(\"hours\"===a.tickInterval||\"minutes_fives\"===a.tickInterval||\"seconds_tens\"===a.tickInterval||\"seconds_fives\"===a.tickInterval){if(!n)return!0}else if((r%e==0||o)&&!n)return!0}))}},{key:\"recalcDimensionsBasedOnFormat\",value:function(t,e){var i=this.w,a=this.formatDates(t),s=this.removeOverlappingTS(a);i.globals.timescaleLabels=s.slice(),new ot(this.ctx).plotCoords()}},{key:\"determineInterval\",value:function(t){var e=24*t,i=60*e;switch(!0){case t/365>5:this.tickInterval=\"years\";break;case t>800:this.tickInterval=\"half_year\";break;case t>180:this.tickInterval=\"months\";break;case t>90:this.tickInterval=\"months_fortnight\";break;case t>60:this.tickInterval=\"months_days\";break;case t>30:this.tickInterval=\"week_days\";break;case t>2:this.tickInterval=\"days\";break;case e>2.4:this.tickInterval=\"hours\";break;case i>15:this.tickInterval=\"minutes_fives\";break;case i>5:this.tickInterval=\"minutes\";break;case i>1:this.tickInterval=\"seconds_tens\";break;case 60*i>20:this.tickInterval=\"seconds_fives\";break;default:this.tickInterval=\"seconds\"}}},{key:\"generateYearScale\",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.daysWidthOnXAxis,r=t.numberOfYears,o=e.minYear,n=0,l=new T(this.ctx),h=\"year\";if(e.minDate>1||e.minMonth>0){var c=l.determineRemainingDaysOfYear(e.minYear,e.minMonth,e.minDate);n=(l.determineDaysOfYear(e.minYear)-c+1)*s,o=e.minYear+1,this.timeScaleArray.push({position:n,value:o,unit:h,year:o,month:x.monthMod(i+1)})}else 1===e.minDate&&0===e.minMonth&&this.timeScaleArray.push({position:n,value:o,unit:h,year:a,month:x.monthMod(i+1)});for(var d=o,g=n,u=0;u<r;u++)d++,g=l.determineDaysOfYear(d-1)*s+g,this.timeScaleArray.push({position:g,value:d,unit:h,year:d,month:1})}},{key:\"generateMonthScale\",value:function(t){var e=t.firstVal,i=t.currentMonthDate,a=t.currentMonth,s=t.currentYear,r=t.daysWidthOnXAxis,o=t.numberOfMonths,n=a,l=0,h=new T(this.ctx),c=\"month\",d=0;if(e.minDate>1){l=(h.determineDaysOfMonths(a+1,e.minYear)-i+1)*r,n=x.monthMod(a+1);var g=s+d,u=x.monthMod(n),f=n;0===n&&(c=\"year\",f=g,u=1,g+=d+=1),this.timeScaleArray.push({position:l,value:f,unit:c,year:g,month:u})}else this.timeScaleArray.push({position:l,value:n,unit:c,year:s,month:x.monthMod(a)});for(var p=n+1,b=l,v=0,m=1;v<o;v++,m++){0===(p=x.monthMod(p))?(c=\"year\",d+=1):c=\"month\";var y=this._getYear(s,p,d);b=h.determineDaysOfMonths(p,y)*r+b;var w=0===p?y:p;this.timeScaleArray.push({position:b,value:w,unit:c,year:y,month:0===p?1:p}),p++}}},{key:\"generateDayScale\",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.hoursWidthOnXAxis,r=t.numberOfDays,o=new T(this.ctx),n=\"day\",l=e.minDate+1,h=l,c=function(t,e,i){return t>o.determineDaysOfMonths(e+1,i)?(h=1,n=\"month\",g=e+=1,e):e},d=(24-e.minHour)*s,g=l,u=c(h,i,a);0===e.minHour&&1===e.minDate?(d=0,g=x.monthMod(e.minMonth),n=\"month\",h=e.minDate,r++):1!==e.minDate&&0===e.minHour&&0===e.minMinute&&(d=0,l=e.minDate,g=l,u=c(h=l,i,a)),this.timeScaleArray.push({position:d,value:g,unit:n,year:this._getYear(a,u,0),month:x.monthMod(u),day:h});for(var f=d,p=0;p<r;p++){n=\"day\",u=c(h+=1,u,this._getYear(a,u,0));var b=this._getYear(a,u,0);f=24*s+f;var v=1===h?x.monthMod(u):h;this.timeScaleArray.push({position:f,value:v,unit:n,year:b,month:x.monthMod(u),day:v})}}},{key:\"generateHourScale\",value:function(t){var e=t.firstVal,i=t.currentDate,a=t.currentMonth,s=t.currentYear,r=t.minutesWidthOnXAxis,o=t.numberOfHours,n=new T(this.ctx),l=\"hour\",h=function(t,e){return t>n.determineDaysOfMonths(e+1,s)&&(p=1,e+=1),{month:e,date:p}},c=function(t,e){return t>n.determineDaysOfMonths(e+1,s)?e+=1:e},d=60-(e.minMinute+e.minSecond/60),g=d*r,u=e.minHour+1,f=u+1;60===d&&(g=0,f=(u=e.minHour)+1);var p=i,b=c(p,a);this.timeScaleArray.push({position:g,value:u,unit:l,day:p,hour:f,year:s,month:x.monthMod(b)});for(var v=g,m=0;m<o;m++){if(l=\"hour\",f>=24)f=0,l=\"day\",b=h(p+=1,b).month,b=c(p,b);var y=this._getYear(s,b,0);v=60*r+v;var w=0===f?p:f;this.timeScaleArray.push({position:v,value:w,unit:l,hour:f,day:p,year:y,month:x.monthMod(b)}),f++}}},{key:\"generateMinuteScale\",value:function(t){for(var e=t.currentMillisecond,i=t.currentSecond,a=t.currentMinute,s=t.currentHour,r=t.currentDate,o=t.currentMonth,n=t.currentYear,l=t.minutesWidthOnXAxis,h=t.secondsWidthOnXAxis,c=t.numberOfMinutes,d=a+1,g=r,u=o,f=n,p=s,b=(60-i-e/1e3)*h,v=0;v<c;v++)d>=60&&(d=0,24===(p+=1)&&(p=0)),this.timeScaleArray.push({position:b,value:d,unit:\"minute\",hour:p,minute:d,day:g,year:this._getYear(f,u,0),month:x.monthMod(u)}),b+=l,d++}},{key:\"generateSecondScale\",value:function(t){for(var e=t.currentMillisecond,i=t.currentSecond,a=t.currentMinute,s=t.currentHour,r=t.currentDate,o=t.currentMonth,n=t.currentYear,l=t.secondsWidthOnXAxis,h=t.numberOfSeconds,c=i+1,d=a,g=r,u=o,f=n,p=s,b=(1e3-e)/1e3*l,v=0;v<h;v++)c>=60&&(c=0,++d>=60&&(d=0,24===++p&&(p=0))),this.timeScaleArray.push({position:b,value:c,unit:\"second\",hour:p,minute:d,second:c,day:g,year:this._getYear(f,u,0),month:x.monthMod(u)}),b+=l,c++}},{key:\"createRawDateString\",value:function(t,e){var i=t.year;return 0===t.month&&(t.month=1),i+=\"-\"+(\"0\"+t.month.toString()).slice(-2),\"day\"===t.unit?i+=\"day\"===t.unit?\"-\"+(\"0\"+e).slice(-2):\"-01\":i+=\"-\"+(\"0\"+(t.day?t.day:\"1\")).slice(-2),\"hour\"===t.unit?i+=\"hour\"===t.unit?\"T\"+(\"0\"+e).slice(-2):\"T00\":i+=\"T\"+(\"0\"+(t.hour?t.hour:\"0\")).slice(-2),\"minute\"===t.unit?i+=\":\"+(\"0\"+e).slice(-2):i+=\":\"+(t.minute?(\"0\"+t.minute).slice(-2):\"00\"),\"second\"===t.unit?i+=\":\"+(\"0\"+e).slice(-2):i+=\":00\",this.utc&&(i+=\".000Z\"),i}},{key:\"formatDates\",value:function(t){var e=this,i=this.w;return t.map((function(t){var a=t.value.toString(),s=new T(e.ctx),r=e.createRawDateString(t,a),o=s.getDate(s.parseDate(r));if(e.utc||(o=s.getDate(s.parseDateWithTimezone(r))),void 0===i.config.xaxis.labels.format){var n=\"dd MMM\",l=i.config.xaxis.labels.datetimeFormatter;\"year\"===t.unit&&(n=l.year),\"month\"===t.unit&&(n=l.month),\"day\"===t.unit&&(n=l.day),\"hour\"===t.unit&&(n=l.hour),\"minute\"===t.unit&&(n=l.minute),\"second\"===t.unit&&(n=l.second),a=s.formatDate(o,n)}else a=s.formatDate(o,i.config.xaxis.labels.format);return{dateString:r,position:t.position,value:a,unit:t.unit,year:t.year,month:t.month}}))}},{key:\"removeOverlappingTS\",value:function(t){var e,i=this,a=new m(this.ctx),s=!1;t.length>0&&t[0].value&&t.every((function(e){return e.value.length===t[0].value.length}))&&(s=!0,e=a.getTextRects(t[0].value).width);var r=0,o=t.map((function(o,n){if(n>0&&i.w.config.xaxis.labels.hideOverlappingLabels){var l=s?e:a.getTextRects(t[r].value).width,h=t[r].position;return o.position>h+l+10?(r=n,o):null}return o}));return o=o.filter((function(t){return null!==t}))}},{key:\"_getYear\",value:function(t,e,i){return t+Math.floor(e/12)+i}}]),t}(),Rt=function(){function t(e,i){a(this,t),this.ctx=i,this.w=i.w,this.el=e}return r(t,[{key:\"setupElements\",value:function(){var t=this.w.globals,e=this.w.config,i=e.chart.type;t.axisCharts=[\"line\",\"area\",\"bar\",\"rangeBar\",\"rangeArea\",\"candlestick\",\"boxPlot\",\"scatter\",\"bubble\",\"radar\",\"heatmap\",\"treemap\"].indexOf(i)>-1,t.xyCharts=[\"line\",\"area\",\"bar\",\"rangeBar\",\"rangeArea\",\"candlestick\",\"boxPlot\",\"scatter\",\"bubble\"].indexOf(i)>-1,t.isBarHorizontal=(\"bar\"===e.chart.type||\"rangeBar\"===e.chart.type||\"boxPlot\"===e.chart.type)&&e.plotOptions.bar.horizontal,t.chartClass=\".apexcharts\"+t.chartID,t.dom.baseEl=this.el,t.dom.elWrap=document.createElement(\"div\"),m.setAttrs(t.dom.elWrap,{id:t.chartClass.substring(1),class:\"apexcharts-canvas \"+t.chartClass.substring(1)}),this.el.appendChild(t.dom.elWrap),t.dom.Paper=new window.SVG.Doc(t.dom.elWrap),t.dom.Paper.attr({class:\"apexcharts-svg\",\"xmlns:data\":\"ApexChartsNS\",transform:\"translate(\".concat(e.chart.offsetX,\", \").concat(e.chart.offsetY,\")\")}),t.dom.Paper.node.style.background=e.chart.background,this.setSVGDimensions(),t.dom.elGraphical=t.dom.Paper.group().attr({class:\"apexcharts-inner apexcharts-graphical\"}),t.dom.elAnnotations=t.dom.Paper.group().attr({class:\"apexcharts-annotations\"}),t.dom.elDefs=t.dom.Paper.defs(),t.dom.elLegendWrap=document.createElement(\"div\"),t.dom.elLegendWrap.classList.add(\"apexcharts-legend\"),t.dom.elWrap.appendChild(t.dom.elLegendWrap),t.dom.Paper.add(t.dom.elGraphical),t.dom.elGraphical.add(t.dom.elDefs)}},{key:\"plotChartType\",value:function(t,e){var i=this.w,a=i.config,s=i.globals,r={series:[],i:[]},o={series:[],i:[]},n={series:[],i:[]},l={series:[],i:[]},h={series:[],i:[]},c={series:[],i:[]},d={series:[],i:[]},g={series:[],i:[]},u={series:[],seriesRangeEnd:[],i:[]};s.series.map((function(e,f){var p=0;void 0!==t[f].type?(\"column\"===t[f].type||\"bar\"===t[f].type?(s.series.length>1&&a.plotOptions.bar.horizontal&&console.warn(\"Horizontal bars are not supported in a mixed/combo chart. Please turn off `plotOptions.bar.horizontal`\"),h.series.push(e),h.i.push(f),p++,i.globals.columnSeries=h.series):\"area\"===t[f].type?(o.series.push(e),o.i.push(f),p++):\"line\"===t[f].type?(r.series.push(e),r.i.push(f),p++):\"scatter\"===t[f].type?(n.series.push(e),n.i.push(f)):\"bubble\"===t[f].type?(l.series.push(e),l.i.push(f),p++):\"candlestick\"===t[f].type?(c.series.push(e),c.i.push(f),p++):\"boxPlot\"===t[f].type?(d.series.push(e),d.i.push(f),p++):\"rangeBar\"===t[f].type?(g.series.push(e),g.i.push(f),p++):\"rangeArea\"===t[f].type?(u.series.push(s.seriesRangeStart[f]),u.seriesRangeEnd.push(s.seriesRangeEnd[f]),u.i.push(f),p++):console.warn(\"You have specified an unrecognized chart type. Available types for this property are line/area/column/bar/scatter/bubble\"),p>1&&(s.comboCharts=!0)):(r.series.push(e),r.i.push(f))}));var f=new zt(this.ctx,e),p=new kt(this.ctx,e);this.ctx.pie=new Lt(this.ctx);var x=new Tt(this.ctx);this.ctx.rangeBar=new Mt(this.ctx,e);var b=new Pt(this.ctx),v=[];if(s.comboCharts){if(o.series.length>0&&v.push(f.draw(o.series,\"area\",o.i)),h.series.length>0)if(i.config.chart.stacked){var m=new wt(this.ctx,e);v.push(m.draw(h.series,h.i))}else this.ctx.bar=new yt(this.ctx,e),v.push(this.ctx.bar.draw(h.series,h.i));if(u.series.length>0&&v.push(f.draw(u.series,\"rangeArea\",u.i,u.seriesRangeEnd)),r.series.length>0&&v.push(f.draw(r.series,\"line\",r.i)),c.series.length>0&&v.push(p.draw(c.series,c.i)),d.series.length>0&&v.push(p.draw(d.series,d.i)),g.series.length>0&&v.push(this.ctx.rangeBar.draw(g.series,g.i)),n.series.length>0){var y=new zt(this.ctx,e,!0);v.push(y.draw(n.series,\"scatter\",n.i))}if(l.series.length>0){var w=new zt(this.ctx,e,!0);v.push(w.draw(l.series,\"bubble\",l.i))}}else switch(a.chart.type){case\"line\":v=f.draw(s.series,\"line\");break;case\"area\":v=f.draw(s.series,\"area\");break;case\"bar\":if(a.chart.stacked)v=new wt(this.ctx,e).draw(s.series);else this.ctx.bar=new yt(this.ctx,e),v=this.ctx.bar.draw(s.series);break;case\"candlestick\":v=new kt(this.ctx,e).draw(s.series);break;case\"boxPlot\":v=new kt(this.ctx,e).draw(s.series);break;case\"rangeBar\":v=this.ctx.rangeBar.draw(s.series);break;case\"rangeArea\":v=f.draw(s.seriesRangeStart,\"rangeArea\",void 0,s.seriesRangeEnd);break;case\"heatmap\":v=new St(this.ctx,e).draw(s.series);break;case\"treemap\":v=new Yt(this.ctx,e).draw(s.series);break;case\"pie\":case\"donut\":case\"polarArea\":v=this.ctx.pie.draw(s.series);break;case\"radialBar\":v=x.draw(s.series);break;case\"radar\":v=b.draw(s.series);break;default:v=f.draw(s.series)}return v}},{key:\"setSVGDimensions\",value:function(){var t=this.w.globals,e=this.w.config;t.svgWidth=e.chart.width,t.svgHeight=e.chart.height;var i=x.getDimensions(this.el),a=e.chart.width.toString().split(/[0-9]+/g).pop();\"%\"===a?x.isNumber(i[0])&&(0===i[0].width&&(i=x.getDimensions(this.el.parentNode)),t.svgWidth=i[0]*parseInt(e.chart.width,10)/100):\"px\"!==a&&\"\"!==a||(t.svgWidth=parseInt(e.chart.width,10));var s=e.chart.height.toString().split(/[0-9]+/g).pop();if(\"auto\"!==t.svgHeight&&\"\"!==t.svgHeight)if(\"%\"===s){var r=x.getDimensions(this.el.parentNode);t.svgHeight=r[1]*parseInt(e.chart.height,10)/100}else t.svgHeight=parseInt(e.chart.height,10);else t.axisCharts?t.svgHeight=t.svgWidth/1.61:t.svgHeight=t.svgWidth/1.2;if(t.svgWidth<0&&(t.svgWidth=0),t.svgHeight<0&&(t.svgHeight=0),m.setAttrs(t.dom.Paper.node,{width:t.svgWidth,height:t.svgHeight}),\"%\"!==s){var o=e.chart.sparkline.enabled?0:t.axisCharts?e.chart.parentHeightOffset:0;t.dom.Paper.node.parentNode.parentNode.style.minHeight=t.svgHeight+o+\"px\"}t.dom.elWrap.style.width=t.svgWidth+\"px\",t.dom.elWrap.style.height=t.svgHeight+\"px\"}},{key:\"shiftGraphPosition\",value:function(){var t=this.w.globals,e=t.translateY,i={transform:\"translate(\"+t.translateX+\", \"+e+\")\"};m.setAttrs(t.dom.elGraphical.node,i)}},{key:\"resizeNonAxisCharts\",value:function(){var t=this.w,e=t.globals,i=0,a=t.config.chart.sparkline.enabled?1:15;a+=t.config.grid.padding.bottom,\"top\"!==t.config.legend.position&&\"bottom\"!==t.config.legend.position||!t.config.legend.show||t.config.legend.floating||(i=new lt(this.ctx).legendHelpers.getLegendBBox().clwh+10);var s=t.globals.dom.baseEl.querySelector(\".apexcharts-radialbar, .apexcharts-pie\"),r=2.05*t.globals.radialSize;if(s&&!t.config.chart.sparkline.enabled&&0!==t.config.plotOptions.radialBar.startAngle){var o=x.getBoundingClientRect(s);r=o.bottom;var n=o.bottom-o.top;r=Math.max(2.05*t.globals.radialSize,n)}var l=r+e.translateY+i+a;e.dom.elLegendForeign&&e.dom.elLegendForeign.setAttribute(\"height\",l),t.config.chart.height&&String(t.config.chart.height).indexOf(\"%\")>0||(e.dom.elWrap.style.height=l+\"px\",m.setAttrs(e.dom.Paper.node,{height:l}),e.dom.Paper.node.parentNode.parentNode.style.minHeight=l+\"px\")}},{key:\"coreCalculations\",value:function(){new U(this.ctx).init()}},{key:\"resetGlobals\",value:function(){var t=this,e=function(){return t.w.config.series.map((function(t){return[]}))},i=new Y,a=this.w.globals;i.initGlobalVars(a),a.seriesXvalues=e(),a.seriesYvalues=e()}},{key:\"isMultipleY\",value:function(){if(this.w.config.yaxis.constructor===Array&&this.w.config.yaxis.length>1)return this.w.globals.isMultipleYAxis=!0,!0}},{key:\"xySettings\",value:function(){var t=null,e=this.w;if(e.globals.axisCharts){if(\"back\"===e.config.xaxis.crosshairs.position)new Q(this.ctx).drawXCrosshairs();if(\"back\"===e.config.yaxis[0].crosshairs.position)new Q(this.ctx).drawYCrosshairs();if(\"datetime\"===e.config.xaxis.type&&void 0===e.config.xaxis.labels.formatter){this.ctx.timeScale=new Ft(this.ctx);var i=[];isFinite(e.globals.minX)&&isFinite(e.globals.maxX)&&!e.globals.isBarHorizontal?i=this.ctx.timeScale.calculateTimeScaleTicks(e.globals.minX,e.globals.maxX):e.globals.isBarHorizontal&&(i=this.ctx.timeScale.calculateTimeScaleTicks(e.globals.minY,e.globals.maxY)),this.ctx.timeScale.recalcDimensionsBasedOnFormat(i)}t=new y(this.ctx).getCalculatedRatios()}return t}},{key:\"updateSourceChart\",value:function(t){this.ctx.w.globals.selection=void 0,this.ctx.updateHelpers._updateOptions({chart:{selection:{xaxis:{min:t.w.globals.minX,max:t.w.globals.maxX}}}},!1,!1)}},{key:\"setupBrushHandler\",value:function(){var t=this,i=this.w;if(i.config.chart.brush.enabled&&\"function\"!=typeof i.config.chart.events.selection){var a=i.config.chart.brush.targets||[i.config.chart.brush.target];a.forEach((function(e){var i=ApexCharts.getChartByID(e);i.w.globals.brushSource=t.ctx,\"function\"!=typeof i.w.config.chart.events.zoomed&&(i.w.config.chart.events.zoomed=function(){t.updateSourceChart(i)}),\"function\"!=typeof i.w.config.chart.events.scrolled&&(i.w.config.chart.events.scrolled=function(){t.updateSourceChart(i)})})),i.config.chart.events.selection=function(t,s){a.forEach((function(t){var a=ApexCharts.getChartByID(t),r=x.clone(i.config.yaxis);if(i.config.chart.brush.autoScaleYaxis&&1===a.w.globals.series.length){var o=new _(a);r=o.autoScaleY(a,r,s)}var n=a.w.config.yaxis.reduce((function(t,i,s){return[].concat(u(t),[e(e({},a.w.config.yaxis[s]),{},{min:r[0].min,max:r[0].max})])}),[]);a.ctx.updateHelpers._updateOptions({xaxis:{min:s.xaxis.min,max:s.xaxis.max},yaxis:n},!1,!1,!1,!1)}))}}}}]),t}(),Dt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"_updateOptions\",value:function(t){var e=this,a=arguments.length>1&&void 0!==arguments[1]&&arguments[1],s=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];return new Promise((function(n){var l=[e.ctx];r&&(l=e.ctx.getSyncedCharts()),e.ctx.w.globals.isExecCalled&&(l=[e.ctx],e.ctx.w.globals.isExecCalled=!1),l.forEach((function(r,h){var c=r.w;if(c.globals.shouldAnimate=s,a||(c.globals.resized=!0,c.globals.dataChanged=!0,s&&r.series.getPreviousPaths()),t&&\"object\"===i(t)&&(r.config=new E(t),t=y.extendArrayProps(r.config,t,c),r.w.globals.chartID!==e.ctx.w.globals.chartID&&delete t.series,c.config=x.extend(c.config,t),o&&(c.globals.lastXAxis=t.xaxis?x.clone(t.xaxis):[],c.globals.lastYAxis=t.yaxis?x.clone(t.yaxis):[],c.globals.initialConfig=x.extend({},c.config),c.globals.initialSeries=x.clone(c.config.series),t.series))){for(var d=0;d<c.globals.collapsedSeriesIndices.length;d++){var g=c.config.series[c.globals.collapsedSeriesIndices[d]];c.globals.collapsedSeries[d].data=c.globals.axisCharts?g.data.slice():g}for(var u=0;u<c.globals.ancillaryCollapsedSeriesIndices.length;u++){var f=c.config.series[c.globals.ancillaryCollapsedSeriesIndices[u]];c.globals.ancillaryCollapsedSeries[u].data=c.globals.axisCharts?f.data.slice():f}r.series.emptyCollapsedSeries(c.config.series)}return r.update(t).then((function(){h===l.length-1&&n(r)}))}))}))}},{key:\"_updateSeries\",value:function(t,e){var i=this,a=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return new Promise((function(s){var r,o=i.w;return o.globals.shouldAnimate=e,o.globals.dataChanged=!0,e&&i.ctx.series.getPreviousPaths(),o.globals.axisCharts?(0===(r=t.map((function(t,e){return i._extendSeries(t,e)}))).length&&(r=[{data:[]}]),o.config.series=r):o.config.series=t.slice(),a&&(o.globals.initialConfig.series=x.clone(o.config.series),o.globals.initialSeries=x.clone(o.config.series)),i.ctx.update().then((function(){s(i.ctx)}))}))}},{key:\"_extendSeries\",value:function(t,i){var a=this.w,s=a.config.series[i];return e(e({},a.config.series[i]),{},{name:t.name?t.name:s&&s.name,color:t.color?t.color:s&&s.color,type:t.type?t.type:s&&s.type,data:t.data?t.data:s&&s.data})}},{key:\"toggleDataPointSelection\",value:function(t,e){var i=this.w,a=null,s=\".apexcharts-series[data\\\\:realIndex='\".concat(t,\"']\");return i.globals.axisCharts?a=i.globals.dom.Paper.select(\"\".concat(s,\" path[j='\").concat(e,\"'], \").concat(s,\" circle[j='\").concat(e,\"'], \").concat(s,\" rect[j='\").concat(e,\"']\")).members[0]:void 0===e&&(a=i.globals.dom.Paper.select(\"\".concat(s,\" path[j='\").concat(t,\"']\")).members[0],\"pie\"!==i.config.chart.type&&\"polarArea\"!==i.config.chart.type&&\"donut\"!==i.config.chart.type||this.ctx.pie.pieClicked(t)),a?(new m(this.ctx).pathMouseDown(a,null),a.node?a.node:null):(console.warn(\"toggleDataPointSelection: Element not found\"),null)}},{key:\"forceXAxisUpdate\",value:function(t){var e=this.w;if([\"min\",\"max\"].forEach((function(i){void 0!==t.xaxis[i]&&(e.config.xaxis[i]=t.xaxis[i],e.globals.lastXAxis[i]=t.xaxis[i])})),t.xaxis.categories&&t.xaxis.categories.length&&(e.config.xaxis.categories=t.xaxis.categories),e.config.xaxis.convertedCatToNumeric){var i=new X(t);t=i.convertCatToNumericXaxis(t,this.ctx)}return t}},{key:\"forceYAxisUpdate\",value:function(t){return t.chart&&t.chart.stacked&&\"100%\"===t.chart.stackType&&(Array.isArray(t.yaxis)?t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})):(t.yaxis.min=0,t.yaxis.max=100)),t}},{key:\"revertDefaultAxisMinMax\",value:function(t){var e=this,i=this.w,a=i.globals.lastXAxis,s=i.globals.lastYAxis;t&&t.xaxis&&(a=t.xaxis),t&&t.yaxis&&(s=t.yaxis),i.config.xaxis.min=a.min,i.config.xaxis.max=a.max;var r=function(t){void 0!==s[t]&&(i.config.yaxis[t].min=s[t].min,i.config.yaxis[t].max=s[t].max)};i.config.yaxis.map((function(t,a){i.globals.zoomed||void 0!==s[a]?r(a):void 0!==e.ctx.opts.yaxis[a]&&(t.min=e.ctx.opts.yaxis[a].min,t.max=e.ctx.opts.yaxis[a].max)}))}}]),t}();Xt=\"undefined\"!=typeof window?window:void 0,Et=function(t,e){var a=(void 0!==this?this:t).SVG=function(t){if(a.supported)return t=new a.Doc(t),a.parser.draw||a.prepare(),t};if(a.ns=\"http://www.w3.org/2000/svg\",a.xmlns=\"http://www.w3.org/2000/xmlns/\",a.xlink=\"http://www.w3.org/1999/xlink\",a.svgjs=\"http://svgjs.dev\",a.supported=!0,!a.supported)return!1;a.did=1e3,a.eid=function(t){return\"Svgjs\"+d(t)+a.did++},a.create=function(t){var i=e.createElementNS(this.ns,t);return i.setAttribute(\"id\",this.eid(t)),i},a.extend=function(){var t,e;e=(t=[].slice.call(arguments)).pop();for(var i=t.length-1;i>=0;i--)if(t[i])for(var s in e)t[i].prototype[s]=e[s];a.Set&&a.Set.inherit&&a.Set.inherit()},a.invent=function(t){var e=\"function\"==typeof t.create?t.create:function(){this.constructor.call(this,a.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&a.extend(e,t.extend),t.construct&&a.extend(t.parent||a.Container,t.construct),e},a.adopt=function(e){return e?e.instance?e.instance:((i=\"svg\"==e.nodeName?e.parentNode instanceof t.SVGElement?new a.Nested:new a.Doc:\"linearGradient\"==e.nodeName?new a.Gradient(\"linear\"):\"radialGradient\"==e.nodeName?new a.Gradient(\"radial\"):a[d(e.nodeName)]?new(a[d(e.nodeName)]):new a.Element(e)).type=e.nodeName,i.node=e,e.instance=i,i instanceof a.Doc&&i.namespace().defs(),i.setData(JSON.parse(e.getAttribute(\"svgjs:data\"))||{}),i):null;var i},a.prepare=function(){var t=e.getElementsByTagName(\"body\")[0],i=(t?new a.Doc(t):a.adopt(e.documentElement).nested()).size(2,0);a.parser={body:t||e.documentElement,draw:i.style(\"opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden\").node,poly:i.polyline().node,path:i.path().node,native:a.create(\"svg\")}},a.parser={native:a.create(\"svg\")},e.addEventListener(\"DOMContentLoaded\",(function(){a.parser.draw||a.prepare()}),!1),a.regex={numberAndUnit:/^([+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i,rgb:/rgb\\((\\d+),(\\d+),(\\d+)\\)/,reference:/#([a-z0-9\\-_]+)/i,transforms:/\\)\\s*,?\\s*/,whitespace:/\\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\\s+)?$/,isNumber:/^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?$/i,isPercent:/^-?[\\d\\.]+%$/,isImage:/\\.(jpg|jpeg|png|gif|svg)(\\?[^=]+.*)?/i,delimiter:/[\\s,]+/,hyphen:/([^e])\\-/gi,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,numbersWithDots:/((\\d?\\.\\d+(?:e[+-]?\\d+)?)((?:\\.\\d+(?:e[+-]?\\d+)?)+))+/gi,dots:/\\./g},a.utils={map:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)a.push(e(t[s]));return a},filter:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)e(t[s])&&a.push(t[s]);return a},filterSVGElements:function(e){return this.filter(e,(function(e){return e instanceof t.SVGElement}))}},a.defaults={attrs:{\"fill-opacity\":1,\"stroke-opacity\":1,\"stroke-width\":0,\"stroke-linejoin\":\"miter\",\"stroke-linecap\":\"butt\",fill:\"#000000\",stroke:\"#000000\",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,\"stop-opacity\":1,\"stop-color\":\"#000000\",\"font-size\":16,\"font-family\":\"Helvetica, Arial, sans-serif\",\"text-anchor\":\"start\"}},a.Color=function(t){var e,s;this.r=0,this.g=0,this.b=0,t&&(\"string\"==typeof t?a.regex.isRgb.test(t)?(e=a.regex.rgb.exec(t.replace(a.regex.whitespace,\"\")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):a.regex.isHex.test(t)&&(e=a.regex.hex.exec(4==(s=t).length?[\"#\",s.substring(1,2),s.substring(1,2),s.substring(2,3),s.substring(2,3),s.substring(3,4),s.substring(3,4)].join(\"\"):s),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):\"object\"===i(t)&&(this.r=t.r,this.g=t.g,this.b=t.b))},a.extend(a.Color,{toString:function(){return this.toHex()},toHex:function(){return\"#\"+g(this.r)+g(this.g)+g(this.b)},toRgb:function(){return\"rgb(\"+[this.r,this.g,this.b].join()+\")\"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new a.Color(t),this},at:function(t){return this.destination?(t=t<0?0:t>1?1:t,new a.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),a.Color.test=function(t){return t+=\"\",a.regex.isHex.test(t)||a.regex.isRgb.test(t)},a.Color.isRgb=function(t){return t&&\"number\"==typeof t.r&&\"number\"==typeof t.g&&\"number\"==typeof t.b},a.Color.isColor=function(t){return a.Color.isRgb(t)||a.Color.test(t)},a.Array=function(t,e){0==(t=(t||[]).valueOf()).length&&e&&(t=e.valueOf()),this.value=this.parse(t)},a.extend(a.Array,{toString:function(){return this.value.join(\" \")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)}}),a.PointArray=function(t,e){a.Array.call(this,t,e||[[0,0]])},a.PointArray.prototype=new a.Array,a.PointArray.prototype.constructor=a.PointArray;for(var s={M:function(t,e,i){return e.x=i.x=t[0],e.y=i.y=t[1],[\"M\",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],[\"L\",t[0],t[1]]},H:function(t,e){return e.x=t[0],[\"H\",t[0]]},V:function(t,e){return e.y=t[0],[\"V\",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],[\"C\",t[0],t[1],t[2],t[3],t[4],t[5]]},Q:function(t,e){return e.x=t[2],e.y=t[3],[\"Q\",t[0],t[1],t[2],t[3]]},Z:function(t,e,i){return e.x=i.x,e.y=i.y,[\"Z\"]}},r=\"mlhvqtcsaz\".split(\"\"),o=0,n=r.length;o<n;++o)s[r[o]]=function(t){return function(e,i,a){if(\"H\"==t)e[0]=e[0]+i.x;else if(\"V\"==t)e[0]=e[0]+i.y;else if(\"A\"==t)e[5]=e[5]+i.x,e[6]=e[6]+i.y;else for(var r=0,o=e.length;r<o;++r)e[r]=e[r]+(r%2?i.y:i.x);if(s&&\"function\"==typeof s[t])return s[t](e,i,a)}}(r[o].toUpperCase());a.PathArray=function(t,e){a.Array.call(this,t,e||[[\"M\",0,0]])},a.PathArray.prototype=new a.Array,a.PathArray.prototype.constructor=a.PathArray,a.extend(a.PathArray,{toString:function(){return function(t){for(var e=0,i=t.length,a=\"\";e<i;e++)a+=t[e][0],null!=t[e][1]&&(a+=t[e][1],null!=t[e][2]&&(a+=\" \",a+=t[e][2],null!=t[e][3]&&(a+=\" \",a+=t[e][3],a+=\" \",a+=t[e][4],null!=t[e][5]&&(a+=\" \",a+=t[e][5],a+=\" \",a+=t[e][6],null!=t[e][7]&&(a+=\" \",a+=t[e][7])))));return a+\" \"}(this.value)},move:function(t,e){var i=this.bbox();return i.x,i.y,this},at:function(t){if(!this.destination)return this;for(var e=this.value,i=this.destination.value,s=[],r=new a.PathArray,o=0,n=e.length;o<n;o++){s[o]=[e[o][0]];for(var l=1,h=e[o].length;l<h;l++)s[o][l]=e[o][l]+(i[o][l]-e[o][l])*t;\"A\"===s[o][0]&&(s[o][4]=+(0!=s[o][4]),s[o][5]=+(0!=s[o][5]))}return r.value=s,r},parse:function(t){if(t instanceof a.PathArray)return t.valueOf();var e,i={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7,Z:0};t=\"string\"==typeof t?t.replace(a.regex.numbersWithDots,h).replace(a.regex.pathLetters,\" $& \").replace(a.regex.hyphen,\"$1 -\").trim().split(a.regex.delimiter):t.reduce((function(t,e){return[].concat.call(t,e)}),[]);var r=[],o=new a.Point,n=new a.Point,l=0,c=t.length;do{a.regex.isPathLetter.test(t[l])?(e=t[l],++l):\"M\"==e?e=\"L\":\"m\"==e&&(e=\"l\"),r.push(s[e].call(null,t.slice(l,l+=i[e.toUpperCase()]).map(parseFloat),o,n))}while(c>l);return r},bbox:function(){return a.parser.draw||a.prepare(),a.parser.path.setAttribute(\"d\",this.toString()),a.parser.path.getBBox()}}),a.Number=a.invent({create:function(t,e){this.value=0,this.unit=e||\"\",\"number\"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-34e37:34e37:\"string\"==typeof t?(e=t.match(a.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),\"%\"==e[5]?this.value/=100:\"s\"==e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof a.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return(\"%\"==this.unit?~~(1e8*this.value)/1e6:\"s\"==this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new a.Number(t),new a.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new a.Number(t),new a.Number(this-t,this.unit||t.unit)},times:function(t){return t=new a.Number(t),new a.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new a.Number(t),new a.Number(this/t,this.unit||t.unit)},to:function(t){var e=new a.Number(this);return\"string\"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new a.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new a.Number(this.destination).minus(this).times(t).plus(this):this}}}),a.Element=a.invent({create:function(t){this._stroke=a.defaults.attrs.stroke,this._event=null,this.dom={},(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute(\"stroke\")||this._stroke)},extend:{x:function(t){return this.attr(\"x\",t)},y:function(t){return this.attr(\"y\",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr(\"width\",t)},height:function(t){return this.attr(\"height\",t)},size:function(t,e){var i=u(this,t,e);return this.width(new a.Number(i.width)).height(new a.Number(i.height))},clone:function(t){this.writeDataToDom();var e=x(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},id:function(t){return this.attr(\"id\",t)},show:function(){return this.style(\"display\",\"\")},hide:function(){return this.style(\"display\",\"none\")},visible:function(){return\"none\"!=this.style(\"display\")},toString:function(){return this.attr(\"id\")},classes:function(){var t=this.attr(\"class\");return null==t?[]:t.trim().split(a.regex.delimiter)},hasClass:function(t){return-1!=this.classes().indexOf(t)},addClass:function(t){if(!this.hasClass(t)){var e=this.classes();e.push(t),this.attr(\"class\",e.join(\" \"))}return this},removeClass:function(t){return this.hasClass(t)&&this.attr(\"class\",this.classes().filter((function(e){return e!=t})).join(\" \")),this},toggleClass:function(t){return this.hasClass(t)?this.removeClass(t):this.addClass(t)},reference:function(t){return a.get(this.attr(t))},parent:function(e){var i=this;if(!i.node.parentNode)return null;if(i=a.adopt(i.node.parentNode),!e)return i;for(;i&&i.node instanceof t.SVGElement;){if(\"string\"==typeof e?i.matches(e):i instanceof e)return i;if(!i.node.parentNode||\"#document\"==i.node.parentNode.nodeName)return null;i=a.adopt(i.node.parentNode)}},doc:function(){return this instanceof a.Doc?this:this.parent(a.Doc)},parents:function(t){var e=[],i=this;do{if(!(i=i.parent(t))||!i.node)break;e.push(i)}while(i.parent);return e},matches:function(t){return function(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}(this.node,t)},native:function(){return this.node},svg:function(t){var i=e.createElement(\"svg\");if(!(t&&this instanceof a.Parent))return i.appendChild(t=e.createElement(\"svg\")),this.writeDataToDom(),t.appendChild(this.node.cloneNode(!0)),i.innerHTML.replace(/^<svg>/,\"\").replace(/<\\/svg>$/,\"\");i.innerHTML=\"<svg>\"+t.replace(/\\n/,\"\").replace(/<([\\w:-]+)([^<]+?)\\/>/g,\"<$1$2></$1>\")+\"</svg>\";for(var s=0,r=i.firstChild.childNodes.length;s<r;s++)this.node.appendChild(i.firstChild.firstChild);return this},writeDataToDom:function(){return(this.each||this.lines)&&(this.each?this:this.lines()).each((function(){this.writeDataToDom()})),this.node.removeAttribute(\"svgjs:data\"),Object.keys(this.dom).length&&this.node.setAttribute(\"svgjs:data\",JSON.stringify(this.dom)),this},setData:function(t){return this.dom=t,this},is:function(t){return function(t,e){return t instanceof e}(this,t)}}}),a.easing={\"-\":function(t){return t},\"<>\":function(t){return-Math.cos(t*Math.PI)/2+.5},\">\":function(t){return Math.sin(t*Math.PI/2)},\"<\":function(t){return 1-Math.cos(t*Math.PI/2)}},a.morph=function(t){return function(e,i){return new a.MorphObj(e,i).at(t)}},a.Situation=a.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new a.Number(t.duration).valueOf(),this.delay=new a.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),a.FX=a.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(t,e,s){\"object\"===i(t)&&(e=t.ease,s=t.delay,t=t.duration);var r=new a.Situation({duration:t||1e3,delay:s||0,ease:a.easing[e||\"-\"]||e});return this.queue(r),this},target:function(t){return t&&t instanceof a.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=t.requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){t.cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return(\"function\"==typeof t||t instanceof a.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof a.Situation?this.start():this.situation.call(this)),this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(var i in e.animations){t=this.target()[i](),Array.isArray(t)||(t=[t]),Array.isArray(e.animations[i])||(e.animations[i]=[e.animations[i]]);for(var s=t.length;s--;)e.animations[i][s]instanceof a.Number&&(t[s]=new a.Number(t[s])),e.animations[i][s]=t[s].morph(e.animations[i][s])}for(var i in e.attrs)e.attrs[i]=new a.MorphObj(this.target().attr(i),e.attrs[i]);for(var i in e.styles)e.styles[i]=new a.MorphObj(this.target().style(i),e.styles[i]);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){var i=this.active;return this.active=!1,e&&this.clearQueue(),t&&this.situation&&(!i&&this.startCurrent(),this.atEnd()),this.stopAnimFrame(),this.clearCurrent()},after:function(t){var e=this.last();return this.target().on(\"finished.fx\",(function i(a){a.detail.situation==e&&(t.call(this,e),this.off(\"finished.fx\",i))})),this._callStart()},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,a.morph(i.detail.pos),i.detail.eased,e)};return this.target().off(\"during.fx\",i).on(\"during.fx\",i),this.after((function(){this.off(\"during.fx\",i)})),this._callStart()},afterAll:function(t){var e=function e(i){t.call(this),this.off(\"allfinished.fx\",e)};return this.target().off(\"allfinished.fx\",e).on(\"allfinished.fx\",e),this._callStart()},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||\"animations\"][t]=e,this._callStart()},step:function(t){var e,i,a;t||(this.absPos=this.timeToAbsPos(+new Date)),!1!==this.situation.loops?(e=Math.max(this.absPos,0),i=Math.floor(e),!0===this.situation.loops||i<this.situation.loops?(this.pos=e-i,a=this.situation.loop,this.situation.loop=i):(this.absPos=this.situation.loops,this.pos=1,a=this.situation.loop-1,this.situation.loop=this.situation.loops),this.situation.reversing&&(this.situation.reversed=this.situation.reversed!=Boolean((this.situation.loop-a)%2))):(this.absPos=Math.min(this.absPos,1),this.pos=this.absPos),this.pos<0&&(this.pos=0),this.situation.reversed&&(this.pos=1-this.pos);var s=this.situation.ease(this.pos);for(var r in this.situation.once)r>this.lastPos&&r<=s&&(this.situation.once[r].call(this.target(),this.pos,s),delete this.situation.once[r]);return this.active&&this.target().fire(\"during\",{pos:this.pos,eased:s,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire(\"finished\",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire(\"allfinished\"),this.situations.length||(this.target().off(\".fx\"),this.active=!1)),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=s,this):this},eachAt:function(){var t,e=this,i=this.target(),s=this.situation;for(var r in s.animations)t=[].concat(s.animations[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i[r].apply(i,t);for(var r in s.attrs)t=[r].concat(s.attrs[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i.attr.apply(i,t);for(var r in s.styles)t=[r].concat(s.styles[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i.style.apply(i,t);if(s.transforms.length){t=s.initialTransformation,r=0;for(var o=s.transforms.length;r<o;r++){var n=s.transforms[r];n instanceof a.Matrix?t=n.relative?t.multiply((new a.Matrix).morph(n).at(s.ease(this.pos))):t.morph(n).at(s.ease(this.pos)):(n.relative||n.undo(t.extract()),t=t.multiply(n.at(s.ease(this.pos))))}i.matrix(t)}return this},once:function(t,e,i){var a=this.last();return i||(t=a.ease(t)),a.once[t]=e,this},_callStart:function(){return setTimeout(function(){this.start()}.bind(this),0),this}},parent:a.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new a.FX(this))).animate(t,e,i)},delay:function(t){return(this.fx||(this.fx=new a.FX(this))).delay(t)},stop:function(t,e){return this.fx&&this.fx.stop(t,e),this},finish:function(){return this.fx&&this.fx.finish(),this}}}),a.MorphObj=a.invent({create:function(t,e){return a.Color.isColor(e)?new a.Color(t).morph(e):a.regex.delimiter.test(t)?a.regex.pathLetters.test(t)?new a.PathArray(t).morph(e):new a.Array(t).morph(e):a.regex.numberAndUnit.test(e)?new a.Number(t).morph(e):(this.value=t,void(this.destination=e))},extend:{at:function(t,e){return e<1?this.value:this.destination},valueOf:function(){return this.value}}}),a.extend(a.FX,{attr:function(t,e,a){if(\"object\"===i(t))for(var s in t)this.attr(s,t[s]);else this.add(t,e,\"attrs\");return this},plot:function(t,e,i,a){return 4==arguments.length?this.plot([t,e,i,a]):this.add(\"plot\",new(this.target().morphArray)(t))}}),a.Box=a.invent({create:function(t,e,s,r){if(!(\"object\"!==i(t)||t instanceof a.Element))return a.Box.call(this,null!=t.left?t.left:t.x,null!=t.top?t.top:t.y,t.width,t.height);4==arguments.length&&(this.x=t,this.y=e,this.width=s,this.height=r),b(this)}}),a.BBox=a.invent({create:function(t){if(a.Box.apply(this,[].slice.call(arguments)),t instanceof a.Element){var i;try{if(!e.documentElement.contains){for(var s=t.node;s.parentNode;)s=s.parentNode;if(s!=e)throw new Error(\"Element not in the dom\")}i=t.node.getBBox()}catch(e){if(t instanceof a.Shape){a.parser.draw||a.prepare();var r=t.clone(a.parser.draw.instance).show();r&&r.node&&\"function\"==typeof r.node.getBBox&&(i=r.node.getBBox()),r&&\"function\"==typeof r.remove&&r.remove()}else i={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}a.Box.call(this,i)}},inherit:a.Box,parent:a.Element,construct:{bbox:function(){return new a.BBox(this)}}}),a.BBox.prototype.constructor=a.BBox,a.Matrix=a.invent({create:function(t){var e=p([1,0,0,1,0,0]);t=null===t?e:t instanceof a.Element?t.matrixify():\"string\"==typeof t?p(t.split(a.regex.delimiter).map(parseFloat)):6==arguments.length?p([].slice.call(arguments)):Array.isArray(t)?p(t):t&&\"object\"===i(t)?t:e;for(var s=m.length-1;s>=0;--s)this[m[s]]=null!=t[m[s]]?t[m[s]]:e[m[s]]},extend:{extract:function(){var t=f(this,0,1);f(this,1,0);var e=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(e*Math.PI/180)+this.f*Math.sin(e*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(e*Math.PI/180)+this.e*Math.sin(-e*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),rotation:e,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new a.Matrix(this)}},clone:function(){return new a.Matrix(this)},morph:function(t){return this.destination=new a.Matrix(t),this},multiply:function(t){return new a.Matrix(this.native().multiply(function(t){return t instanceof a.Matrix||(t=new a.Matrix(t)),t}(t).native()))},inverse:function(){return new a.Matrix(this.native().inverse())},translate:function(t,e){return new a.Matrix(this.native().translate(t||0,e||0))},native:function(){for(var t=a.parser.native.createSVGMatrix(),e=m.length-1;e>=0;e--)t[m[e]]=this[m[e]];return t},toString:function(){return\"matrix(\"+v(this.a)+\",\"+v(this.b)+\",\"+v(this.c)+\",\"+v(this.d)+\",\"+v(this.e)+\",\"+v(this.f)+\")\"}},parent:a.Element,construct:{ctm:function(){return new a.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof a.Nested){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new a.Matrix(e)}return new a.Matrix(this.node.getScreenCTM())}}}),a.Point=a.invent({create:function(t,e){var a;a=Array.isArray(t)?{x:t[0],y:t[1]}:\"object\"===i(t)?{x:t.x,y:t.y}:null!=t?{x:t,y:null!=e?e:t}:{x:0,y:0},this.x=a.x,this.y=a.y},extend:{clone:function(){return new a.Point(this)},morph:function(t,e){return this.destination=new a.Point(t,e),this}}}),a.extend(a.Element,{point:function(t,e){return new a.Point(t,e).transform(this.screenCTM().inverse())}}),a.extend(a.Element,{attr:function(t,e,s){if(null==t){for(t={},s=(e=this.node.attributes).length-1;s>=0;s--)t[e[s].nodeName]=a.regex.isNumber.test(e[s].nodeValue)?parseFloat(e[s].nodeValue):e[s].nodeValue;return t}if(\"object\"===i(t))for(var r in t)this.attr(r,t[r]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return null==(e=this.node.getAttribute(t))?a.defaults.attrs[t]:a.regex.isNumber.test(e)?parseFloat(e):e;\"stroke-width\"==t?this.attr(\"stroke\",parseFloat(e)>0?this._stroke:null):\"stroke\"==t&&(this._stroke=e),\"fill\"!=t&&\"stroke\"!=t||(a.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof a.Image&&(e=this.doc().defs().pattern(0,0,(function(){this.add(e)})))),\"number\"==typeof e?e=new a.Number(e):a.Color.isColor(e)?e=new a.Color(e):Array.isArray(e)&&(e=new a.Array(e)),\"leading\"==t?this.leading&&this.leading(e):\"string\"==typeof s?this.node.setAttributeNS(s,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||\"font-size\"!=t&&\"x\"!=t||this.rebuild(t,e)}return this}}),a.extend(a.Element,{transform:function(t,e){var s;return\"object\"!==i(t)?(s=new a.Matrix(this).extract(),\"string\"==typeof t?s[t]:s):(s=new a.Matrix(this),e=!!e||!!t.relative,null!=t.a&&(s=e?s.multiply(new a.Matrix(t)):new a.Matrix(t)),this.attr(\"transform\",s))}}),a.extend(a.Element,{untransform:function(){return this.attr(\"transform\",null)},matrixify:function(){return(this.attr(\"transform\")||\"\").split(a.regex.transforms).slice(0,-1).map((function(t){var e=t.trim().split(\"(\");return[e[0],e[1].split(a.regex.delimiter).map((function(t){return parseFloat(t)}))]})).reduce((function(t,e){return\"matrix\"==e[0]?t.multiply(p(e[1])):t[e[0]].apply(t,e[1])}),new a.Matrix)},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.screenCTM().inverse();return this.addTo(t).untransform().transform(i.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),a.Transformation=a.invent({create:function(t,e){if(arguments.length>1&&\"boolean\"!=typeof e)return this.constructor.call(this,[].slice.call(arguments));if(Array.isArray(t))for(var a=0,s=this.arguments.length;a<s;++a)this[this.arguments[a]]=t[a];else if(t&&\"object\"===i(t))for(a=0,s=this.arguments.length;a<s;++a)this[this.arguments[a]]=t[this.arguments[a]];this.inversed=!1,!0===e&&(this.inversed=!0)}}),a.Translate=a.invent({parent:a.Matrix,inherit:a.Transformation,create:function(t,e){this.constructor.apply(this,[].slice.call(arguments))},extend:{arguments:[\"transformedX\",\"transformedY\"],method:\"translate\"}}),a.extend(a.Element,{style:function(t,e){if(0==arguments.length)return this.node.style.cssText||\"\";if(arguments.length<2)if(\"object\"===i(t))for(var s in t)this.style(s,t[s]);else{if(!a.regex.isCss.test(t))return this.node.style[c(t)];for(t=t.split(/\\s*;\\s*/).filter((function(t){return!!t})).map((function(t){return t.split(/\\s*:\\s*/)}));e=t.pop();)this.style(e[0],e[1])}else this.node.style[c(t)]=null===e||a.regex.isBlank.test(e)?\"\":e;return this}}),a.Parent=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Element,extend:{children:function(){return a.utils.map(a.utils.filterSVGElements(this.node.childNodes),(function(t){return a.adopt(t)}))},add:function(t,e){return null==e?this.node.appendChild(t.node):t.node!=this.node.childNodes[e]&&this.node.insertBefore(t.node,this.node.childNodes[e]),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return a.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){for(var i=this.children(),s=0,r=i.length;s<r;s++)i[s]instanceof a.Element&&t.apply(i[s],[s,i]),e&&i[s]instanceof a.Container&&i[s].each(t,e);return this},removeElement:function(t){return this.node.removeChild(t.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,this},defs:function(){return this.doc().defs()}}}),a.extend(a.Parent,{ungroup:function(t,e){return 0===e||this instanceof a.Defs||this.node==a.parser.draw||(t=t||(this instanceof a.Doc?this:this.parent(a.Parent)),e=e||1/0,this.each((function(){return this instanceof a.Defs?this:this instanceof a.Parent?this.ungroup(t,e-1):this.toParent(t)})),this.node.firstChild||this.remove()),this},flatten:function(t,e){return this.ungroup(t,e)}}),a.Container=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Parent}),a.ViewBox=a.invent({parent:a.Container,construct:{}}),[\"click\",\"dblclick\",\"mousedown\",\"mouseup\",\"mouseover\",\"mouseout\",\"mousemove\",\"touchstart\",\"touchmove\",\"touchleave\",\"touchend\",\"touchcancel\"].forEach((function(t){a.Element.prototype[t]=function(e){return a.on(this.node,t,e),this}})),a.listeners=[],a.handlerMap=[],a.listenerId=0,a.on=function(t,e,i,s,r){var o=i.bind(s||t.instance||t),n=(a.handlerMap.indexOf(t)+1||a.handlerMap.push(t))-1,l=e.split(\".\")[0],h=e.split(\".\")[1]||\"*\";a.listeners[n]=a.listeners[n]||{},a.listeners[n][l]=a.listeners[n][l]||{},a.listeners[n][l][h]=a.listeners[n][l][h]||{},i._svgjsListenerId||(i._svgjsListenerId=++a.listenerId),a.listeners[n][l][h][i._svgjsListenerId]=o,t.addEventListener(l,o,r||{passive:!0})},a.off=function(t,e,i){var s=a.handlerMap.indexOf(t),r=e&&e.split(\".\")[0],o=e&&e.split(\".\")[1],n=\"\";if(-1!=s)if(i){if(\"function\"==typeof i&&(i=i._svgjsListenerId),!i)return;a.listeners[s][r]&&a.listeners[s][r][o||\"*\"]&&(t.removeEventListener(r,a.listeners[s][r][o||\"*\"][i],!1),delete a.listeners[s][r][o||\"*\"][i])}else if(o&&r){if(a.listeners[s][r]&&a.listeners[s][r][o]){for(var l in a.listeners[s][r][o])a.off(t,[r,o].join(\".\"),l);delete a.listeners[s][r][o]}}else if(o)for(var h in a.listeners[s])for(var n in a.listeners[s][h])o===n&&a.off(t,[h,o].join(\".\"));else if(r){if(a.listeners[s][r]){for(var n in a.listeners[s][r])a.off(t,[r,n].join(\".\"));delete a.listeners[s][r]}}else{for(var h in a.listeners[s])a.off(t,h);delete a.listeners[s],delete a.handlerMap[s]}},a.extend(a.Element,{on:function(t,e,i,s){return a.on(this.node,t,e,i,s),this},off:function(t,e){return a.off(this.node,t,e),this},fire:function(e,i){return e instanceof t.Event?this.node.dispatchEvent(e):this.node.dispatchEvent(e=new a.CustomEvent(e,{detail:i,cancelable:!0})),this._event=e,this},event:function(){return this._event}}),a.Defs=a.invent({create:\"defs\",inherit:a.Container}),a.G=a.invent({create:\"g\",inherit:a.Container,extend:{x:function(t){return null==t?this.transform(\"x\"):this.transform({x:t-this.x()},!0)}},construct:{group:function(){return this.put(new a.G)}}}),a.Doc=a.invent({create:function(t){t&&(\"svg\"==(t=\"string\"==typeof t?e.getElementById(t):t).nodeName?this.constructor.call(this,t):(this.constructor.call(this,a.create(\"svg\")),t.appendChild(this.node),this.size(\"100%\",\"100%\")),this.namespace().defs())},inherit:a.Container,extend:{namespace:function(){return this.attr({xmlns:a.ns,version:\"1.1\"}).attr(\"xmlns:xlink\",a.xlink,a.xmlns).attr(\"xmlns:svgjs\",a.svgjs,a.xmlns)},defs:function(){var t;return this._defs||((t=this.node.getElementsByTagName(\"defs\")[0])?this._defs=a.adopt(t):this._defs=new a.Defs,this.node.appendChild(this._defs.node)),this._defs},parent:function(){return this.node.parentNode&&\"#document\"!=this.node.parentNode.nodeName?this.node.parentNode:null},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,a.parser.draw&&!a.parser.draw.parentNode&&this.node.appendChild(a.parser.draw),this},clone:function(t){this.writeDataToDom();var e=this.node,i=x(e.cloneNode(!0));return t?(t.node||t).appendChild(i.node):e.parentNode.insertBefore(i.node,e.nextSibling),i}}}),a.extend(a.Element,{}),a.Gradient=a.invent({create:function(t){this.constructor.call(this,a.create(t+\"Gradient\")),this.type=t},inherit:a.Container,extend:{at:function(t,e,i){return this.put(new a.Stop).update(t,e,i)},update:function(t){return this.clear(),\"function\"==typeof t&&t.call(this,this),this},fill:function(){return\"url(#\"+this.id()+\")\"},toString:function(){return this.fill()},attr:function(t,e,i){return\"transform\"==t&&(t=\"gradientTransform\"),a.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),a.extend(a.Gradient,a.FX,{from:function(t,e){return\"radial\"==(this._target||this).type?this.attr({fx:new a.Number(t),fy:new a.Number(e)}):this.attr({x1:new a.Number(t),y1:new a.Number(e)})},to:function(t,e){return\"radial\"==(this._target||this).type?this.attr({cx:new a.Number(t),cy:new a.Number(e)}):this.attr({x2:new a.Number(t),y2:new a.Number(e)})}}),a.extend(a.Defs,{gradient:function(t,e){return this.put(new a.Gradient(t)).update(e)}}),a.Stop=a.invent({create:\"stop\",inherit:a.Element,extend:{update:function(t){return(\"number\"==typeof t||t instanceof a.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr(\"stop-opacity\",t.opacity),null!=t.color&&this.attr(\"stop-color\",t.color),null!=t.offset&&this.attr(\"offset\",new a.Number(t.offset)),this}}}),a.Pattern=a.invent({create:\"pattern\",inherit:a.Container,extend:{fill:function(){return\"url(#\"+this.id()+\")\"},update:function(t){return this.clear(),\"function\"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return\"transform\"==t&&(t=\"patternTransform\"),a.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),a.extend(a.Defs,{pattern:function(t,e,i){return this.put(new a.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:\"userSpaceOnUse\"})}}),a.Shape=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Element}),a.Symbol=a.invent({create:\"symbol\",inherit:a.Container,construct:{symbol:function(){return this.put(new a.Symbol)}}}),a.Use=a.invent({create:\"use\",inherit:a.Shape,extend:{element:function(t,e){return this.attr(\"href\",(e||\"\")+\"#\"+t,a.xlink)}},construct:{use:function(t,e){return this.put(new a.Use).element(t,e)}}}),a.Rect=a.invent({create:\"rect\",inherit:a.Shape,construct:{rect:function(t,e){return this.put(new a.Rect).size(t,e)}}}),a.Circle=a.invent({create:\"circle\",inherit:a.Shape,construct:{circle:function(t){return this.put(new a.Circle).rx(new a.Number(t).divide(2)).move(0,0)}}}),a.extend(a.Circle,a.FX,{rx:function(t){return this.attr(\"r\",t)},ry:function(t){return this.rx(t)}}),a.Ellipse=a.invent({create:\"ellipse\",inherit:a.Shape,construct:{ellipse:function(t,e){return this.put(new a.Ellipse).size(t,e).move(0,0)}}}),a.extend(a.Ellipse,a.Rect,a.FX,{rx:function(t){return this.attr(\"rx\",t)},ry:function(t){return this.attr(\"ry\",t)}}),a.extend(a.Circle,a.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr(\"cx\"):this.attr(\"cx\",t)},cy:function(t){return null==t?this.attr(\"cy\"):this.attr(\"cy\",t)},width:function(t){return null==t?2*this.rx():this.rx(new a.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new a.Number(t).divide(2))},size:function(t,e){var i=u(this,t,e);return this.rx(new a.Number(i.width).divide(2)).ry(new a.Number(i.height).divide(2))}}),a.Line=a.invent({create:\"line\",inherit:a.Shape,extend:{array:function(){return new a.PointArray([[this.attr(\"x1\"),this.attr(\"y1\")],[this.attr(\"x2\"),this.attr(\"y2\")]])},plot:function(t,e,i,s){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:i,y2:s}:new a.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=u(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,s){return a.Line.prototype.plot.apply(this.put(new a.Line),null!=t?[t,e,i,s]:[0,0,0,0])}}}),a.Polyline=a.invent({create:\"polyline\",inherit:a.Shape,construct:{polyline:function(t){return this.put(new a.Polyline).plot(t||new a.PointArray)}}}),a.Polygon=a.invent({create:\"polygon\",inherit:a.Shape,construct:{polygon:function(t){return this.put(new a.Polygon).plot(t||new a.PointArray)}}}),a.extend(a.Polyline,a.Polygon,{array:function(){return this._array||(this._array=new a.PointArray(this.attr(\"points\")))},plot:function(t){return null==t?this.array():this.clear().attr(\"points\",\"string\"==typeof t?t:this._array=new a.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr(\"points\",this.array().move(t,e))},size:function(t,e){var i=u(this,t,e);return this.attr(\"points\",this.array().size(i.width,i.height))}}),a.extend(a.Line,a.Polyline,a.Polygon,{morphArray:a.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),a.Path=a.invent({create:\"path\",inherit:a.Shape,extend:{morphArray:a.PathArray,array:function(){return this._array||(this._array=new a.PathArray(this.attr(\"d\")))},plot:function(t){return null==t?this.array():this.clear().attr(\"d\",\"string\"==typeof t?t:this._array=new a.PathArray(t))},clear:function(){return delete this._array,this}},construct:{path:function(t){return this.put(new a.Path).plot(t||new a.PathArray)}}}),a.Image=a.invent({create:\"image\",inherit:a.Shape,extend:{load:function(e){if(!e)return this;var i=this,s=new t.Image;return a.on(s,\"load\",(function(){a.off(s);var t=i.parent(a.Pattern);null!==t&&(0==i.width()&&0==i.height()&&i.size(s.width,s.height),t&&0==t.width()&&0==t.height()&&t.size(i.width(),i.height()),\"function\"==typeof i._loaded&&i._loaded.call(i,{width:s.width,height:s.height,ratio:s.width/s.height,url:e}))})),a.on(s,\"error\",(function(t){a.off(s),\"function\"==typeof i._error&&i._error.call(i,t)})),this.attr(\"href\",s.src=this.src=e,a.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new a.Image).load(t).size(e||0,i||e||0)}}}),a.Text=a.invent({create:function(){this.constructor.call(this,a.create(\"text\")),this.dom.leading=new a.Number(1.3),this._rebuild=!0,this._build=!1,this.attr(\"font-family\",a.defaults.attrs[\"font-family\"])},inherit:a.Shape,extend:{x:function(t){return null==t?this.attr(\"x\"):this.attr(\"x\",t)},text:function(t){if(void 0===t){t=\"\";for(var e=this.node.childNodes,i=0,s=e.length;i<s;++i)0!=i&&3!=e[i].nodeType&&1==a.adopt(e[i]).dom.newLined&&(t+=\"\\n\"),t+=e[i].textContent;return t}if(this.clear().build(!0),\"function\"==typeof t)t.call(this,this);else{i=0;for(var r=(t=t.split(\"\\n\")).length;i<r;i++)this.tspan(t[i]).newLine()}return this.build(!1).rebuild()},size:function(t){return this.attr(\"font-size\",t).rebuild()},leading:function(t){return null==t?this.dom.leading:(this.dom.leading=new a.Number(t),this.rebuild())},lines:function(){var t=(this.textPath&&this.textPath()||this).node,e=a.utils.map(a.utils.filterSVGElements(t.childNodes),(function(t){return a.adopt(t)}));return new a.Set(e)},rebuild:function(t){if(\"boolean\"==typeof t&&(this._rebuild=t),this._rebuild){var e=this,i=0,s=this.dom.leading*new a.Number(this.attr(\"font-size\"));this.lines().each((function(){this.dom.newLined&&(e.textPath()||this.attr(\"x\",e.attr(\"x\")),\"\\n\"==this.text()?i+=s:(this.attr(\"dy\",s+i),i=0))})),this.fire(\"rebuild\")}return this},build:function(t){return this._build=!!t,this},setData:function(t){return this.dom=t,this.dom.leading=new a.Number(t.leading||1.3),this}},construct:{text:function(t){return this.put(new a.Text).text(t)},plain:function(t){return this.put(new a.Text).plain(t)}}}),a.Tspan=a.invent({create:\"tspan\",inherit:a.Shape,extend:{text:function(t){return null==t?this.node.textContent+(this.dom.newLined?\"\\n\":\"\"):(\"function\"==typeof t?t.call(this,this):this.plain(t),this)},dx:function(t){return this.attr(\"dx\",t)},dy:function(t){return this.attr(\"dy\",t)},newLine:function(){var t=this.parent(a.Text);return this.dom.newLined=!0,this.dy(t.dom.leading*t.attr(\"font-size\")).attr(\"x\",t.x())}}}),a.extend(a.Text,a.Tspan,{plain:function(t){return!1===this._build&&this.clear(),this.node.appendChild(e.createTextNode(t)),this},tspan:function(t){var e=(this.textPath&&this.textPath()||this).node,i=new a.Tspan;return!1===this._build&&this.clear(),e.appendChild(i.node),i.text(t)},clear:function(){for(var t=(this.textPath&&this.textPath()||this).node;t.hasChildNodes();)t.removeChild(t.lastChild);return this},length:function(){return this.node.getComputedTextLength()}}),a.TextPath=a.invent({create:\"textPath\",inherit:a.Parent,parent:a.Text,construct:{morphArray:a.PathArray,array:function(){var t=this.track();return t?t.array():null},plot:function(t){var e=this.track(),i=null;return e&&(i=e.plot(t)),null==t?i:this},track:function(){var t=this.textPath();if(t)return t.reference(\"href\")},textPath:function(){if(this.node.firstChild&&\"textPath\"==this.node.firstChild.nodeName)return a.adopt(this.node.firstChild)}}}),a.Nested=a.invent({create:function(){this.constructor.call(this,a.create(\"svg\")),this.style(\"overflow\",\"visible\")},inherit:a.Container,construct:{nested:function(){return this.put(new a.Nested)}}});var l={stroke:[\"color\",\"width\",\"opacity\",\"linecap\",\"linejoin\",\"miterlimit\",\"dasharray\",\"dashoffset\"],fill:[\"color\",\"opacity\",\"rule\"],prefix:function(t,e){return\"color\"==e?t:t+\"-\"+e}};function h(t,e,i,s){return i+s.replace(a.regex.dots,\" .\")}function c(t){return t.toLowerCase().replace(/-(.)/g,(function(t,e){return e.toUpperCase()}))}function d(t){return t.charAt(0).toUpperCase()+t.slice(1)}function g(t){var e=t.toString(16);return 1==e.length?\"0\"+e:e}function u(t,e,i){if(null==e||null==i){var a=t.bbox();null==e?e=a.width/a.height*i:null==i&&(i=a.height/a.width*e)}return{width:e,height:i}}function f(t,e,i){return{x:e*t.a+i*t.c+0,y:e*t.b+i*t.d+0}}function p(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function x(e){for(var i=e.childNodes.length-1;i>=0;i--)e.childNodes[i]instanceof t.SVGElement&&x(e.childNodes[i]);return a.adopt(e).id(a.eid(e.nodeName))}function b(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function v(t){return Math.abs(t)>1e-37?t:0}[\"fill\",\"stroke\"].forEach((function(t){var e={};e[t]=function(e){if(void 0===e)return this;if(\"string\"==typeof e||a.Color.isRgb(e)||e&&\"function\"==typeof e.fill)this.attr(t,e);else for(var i=l[t].length-1;i>=0;i--)null!=e[l[t][i]]&&this.attr(l.prefix(t,l[t][i]),e[l[t][i]]);return this},a.extend(a.Element,a.FX,e)})),a.extend(a.Element,a.FX,{translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.attr(\"transform\",new a.Matrix(6==arguments.length?[].slice.call(arguments):t))},opacity:function(t){return this.attr(\"opacity\",t)},dx:function(t){return this.x(new a.Number(t).plus(this instanceof a.FX?0:this.x()),!0)},dy:function(t){return this.y(new a.Number(t).plus(this instanceof a.FX?0:this.y()),!0)}}),a.extend(a.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),a.Set=a.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){for(var t=[].slice.call(arguments),e=0,i=t.length;e<i;e++)this.members.push(t[e]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e<i;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},length:function(){return this.members.length},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members}},construct:{set:function(t){return new a.Set(t)}}}),a.FX.Set=a.invent({create:function(t){this.set=t}}),a.Set.inherit=function(){var t=[];for(var e in a.Shape.prototype)\"function\"==typeof a.Shape.prototype[e]&&\"function\"!=typeof a.Set.prototype[e]&&t.push(e);for(var e in t.forEach((function(t){a.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e<i;e++)this.members[e]&&\"function\"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return\"animate\"==t?this.fx||(this.fx=new a.FX.Set(this)):this}})),t=[],a.FX.prototype)\"function\"==typeof a.FX.prototype[e]&&\"function\"!=typeof a.FX.Set.prototype[e]&&t.push(e);t.forEach((function(t){a.FX.Set.prototype[t]=function(){for(var e=0,i=this.set.members.length;e<i;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}}))},a.extend(a.Element,{}),a.extend(a.Element,{remember:function(t,e){if(\"object\"===i(arguments[0]))for(var a in t)this.remember(a,t[a]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),a.get=function(t){var i=e.getElementById(function(t){var e=(t||\"\").toString().match(a.regex.reference);if(e)return e[1]}(t)||t);return a.adopt(i)},a.select=function(t,i){return new a.Set(a.utils.map((i||e).querySelectorAll(t),(function(t){return a.adopt(t)})))},a.extend(a.Parent,{select:function(t){return a.select(t,this.node)}});var m=\"abcdef\".split(\"\");if(\"function\"!=typeof t.CustomEvent){var y=function(t,i){i=i||{bubbles:!1,cancelable:!1,detail:void 0};var a=e.createEvent(\"CustomEvent\");return a.initCustomEvent(t,i.bubbles,i.cancelable,i.detail),a};y.prototype=t.Event.prototype,a.CustomEvent=y}else a.CustomEvent=t.CustomEvent;return a},\"function\"==typeof define&&define.amd?define((function(){return Et(Xt,Xt.document)})):\"object\"===(\"undefined\"==typeof exports?\"undefined\":i(exports))&&\"undefined\"!=typeof module?module.exports=Xt.document?Et(Xt,Xt.document):function(t){return Et(t,t.document)}:Xt.SVG=Et(Xt,Xt.document),\n/*! svg.filter.js - v2.0.2 - 2016-02-24\n* https://github.com/wout/svg.filter.js\n* Copyright (c) 2016 Wout Fierens; Licensed MIT */\nfunction(){SVG.Filter=SVG.invent({create:\"filter\",inherit:SVG.Parent,extend:{source:\"SourceGraphic\",sourceAlpha:\"SourceAlpha\",background:\"BackgroundImage\",backgroundAlpha:\"BackgroundAlpha\",fill:\"FillPaint\",stroke:\"StrokePaint\",autoSetIn:!0,put:function(t,e){return this.add(t,e),!t.attr(\"in\")&&this.autoSetIn&&t.attr(\"in\",this.source),t.attr(\"result\")||t.attr(\"result\",t),t},blend:function(t,e,i){return this.put(new SVG.BlendEffect(t,e,i))},colorMatrix:function(t,e){return this.put(new SVG.ColorMatrixEffect(t,e))},convolveMatrix:function(t){return this.put(new SVG.ConvolveMatrixEffect(t))},componentTransfer:function(t){return this.put(new SVG.ComponentTransferEffect(t))},composite:function(t,e,i){return this.put(new SVG.CompositeEffect(t,e,i))},flood:function(t,e){return this.put(new SVG.FloodEffect(t,e))},offset:function(t,e){return this.put(new SVG.OffsetEffect(t,e))},image:function(t){return this.put(new SVG.ImageEffect(t))},merge:function(){var t=[void 0];for(var e in arguments)t.push(arguments[e]);return this.put(new(SVG.MergeEffect.bind.apply(SVG.MergeEffect,t)))},gaussianBlur:function(t,e){return this.put(new SVG.GaussianBlurEffect(t,e))},morphology:function(t,e){return this.put(new SVG.MorphologyEffect(t,e))},diffuseLighting:function(t,e,i){return this.put(new SVG.DiffuseLightingEffect(t,e,i))},displacementMap:function(t,e,i,a,s){return this.put(new SVG.DisplacementMapEffect(t,e,i,a,s))},specularLighting:function(t,e,i,a){return this.put(new SVG.SpecularLightingEffect(t,e,i,a))},tile:function(){return this.put(new SVG.TileEffect)},turbulence:function(t,e,i,a,s){return this.put(new SVG.TurbulenceEffect(t,e,i,a,s))},toString:function(){return\"url(#\"+this.attr(\"id\")+\")\"}}}),SVG.extend(SVG.Defs,{filter:function(t){var e=this.put(new SVG.Filter);return\"function\"==typeof t&&t.call(e,e),e}}),SVG.extend(SVG.Container,{filter:function(t){return this.defs().filter(t)}}),SVG.extend(SVG.Element,SVG.G,SVG.Nested,{filter:function(t){return this.filterer=t instanceof SVG.Element?t:this.doc().filter(t),this.doc()&&this.filterer.doc()!==this.doc()&&this.doc().defs().add(this.filterer),this.attr(\"filter\",this.filterer),this.filterer},unfilter:function(t){return this.filterer&&!0===t&&this.filterer.remove(),delete this.filterer,this.attr(\"filter\",null)}}),SVG.Effect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in\")+'\"]').get(0)||this.attr(\"in\"):this.attr(\"in\",t)},result:function(t){return null==t?this.attr(\"result\"):this.attr(\"result\",t)},toString:function(){return this.result()}}}),SVG.ParentEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Parent,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in\")+'\"]').get(0)||this.attr(\"in\"):this.attr(\"in\",t)},result:function(t){return null==t?this.attr(\"result\"):this.attr(\"result\",t)},toString:function(){return this.result()}}});var t={blend:function(t,e){return this.parent()&&this.parent().blend(this,t,e)},colorMatrix:function(t,e){return this.parent()&&this.parent().colorMatrix(t,e).in(this)},convolveMatrix:function(t){return this.parent()&&this.parent().convolveMatrix(t).in(this)},componentTransfer:function(t){return this.parent()&&this.parent().componentTransfer(t).in(this)},composite:function(t,e){return this.parent()&&this.parent().composite(this,t,e)},flood:function(t,e){return this.parent()&&this.parent().flood(t,e)},offset:function(t,e){return this.parent()&&this.parent().offset(t,e).in(this)},image:function(t){return this.parent()&&this.parent().image(t)},merge:function(){return this.parent()&&this.parent().merge.apply(this.parent(),[this].concat(arguments))},gaussianBlur:function(t,e){return this.parent()&&this.parent().gaussianBlur(t,e).in(this)},morphology:function(t,e){return this.parent()&&this.parent().morphology(t,e).in(this)},diffuseLighting:function(t,e,i){return this.parent()&&this.parent().diffuseLighting(t,e,i).in(this)},displacementMap:function(t,e,i,a){return this.parent()&&this.parent().displacementMap(this,t,e,i,a)},specularLighting:function(t,e,i,a){return this.parent()&&this.parent().specularLighting(t,e,i,a).in(this)},tile:function(){return this.parent()&&this.parent().tile().in(this)},turbulence:function(t,e,i,a,s){return this.parent()&&this.parent().turbulence(t,e,i,a,s).in(this)}};SVG.extend(SVG.Effect,t),SVG.extend(SVG.ParentEffect,t),SVG.ChildEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){this.attr(\"in\",t)}}});var e={blend:function(t,e,i){this.attr({in:t,in2:e,mode:i||\"normal\"})},colorMatrix:function(t,e){\"matrix\"==t&&(e=s(e)),this.attr({type:t,values:void 0===e?null:e})},convolveMatrix:function(t){t=s(t),this.attr({order:Math.sqrt(t.split(\" \").length),kernelMatrix:t})},composite:function(t,e,i){this.attr({in:t,in2:e,operator:i})},flood:function(t,e){this.attr(\"flood-color\",t),null!=e&&this.attr(\"flood-opacity\",e)},offset:function(t,e){this.attr({dx:t,dy:e})},image:function(t){this.attr(\"href\",t,SVG.xlink)},displacementMap:function(t,e,i,a,s){this.attr({in:t,in2:e,scale:i,xChannelSelector:a,yChannelSelector:s})},gaussianBlur:function(t,e){null!=t||null!=e?this.attr(\"stdDeviation\",r(Array.prototype.slice.call(arguments))):this.attr(\"stdDeviation\",\"0 0\")},morphology:function(t,e){this.attr({operator:t,radius:e})},tile:function(){},turbulence:function(t,e,i,a,s){this.attr({numOctaves:e,seed:i,stitchTiles:a,baseFrequency:t,type:s})}},i={merge:function(){var t;if(arguments[0]instanceof SVG.Set){var e=this;arguments[0].each((function(t){this instanceof SVG.MergeNode?e.put(this):(this instanceof SVG.Effect||this instanceof SVG.ParentEffect)&&e.put(new SVG.MergeNode(this))}))}else{t=Array.isArray(arguments[0])?arguments[0]:arguments;for(var i=0;i<t.length;i++)t[i]instanceof SVG.MergeNode?this.put(t[i]):this.put(new SVG.MergeNode(t[i]))}},componentTransfer:function(t){if(this.rgb=new SVG.Set,[\"r\",\"g\",\"b\",\"a\"].forEach(function(t){this[t]=new(SVG[\"Func\"+t.toUpperCase()])(\"identity\"),this.rgb.add(this[t]),this.node.appendChild(this[t].node)}.bind(this)),t)for(var e in t.rgb&&([\"r\",\"g\",\"b\"].forEach(function(e){this[e].attr(t.rgb)}.bind(this)),delete t.rgb),t)this[e].attr(t[e])},diffuseLighting:function(t,e,i){this.attr({surfaceScale:t,diffuseConstant:e,kernelUnitLength:i})},specularLighting:function(t,e,i,a){this.attr({surfaceScale:t,diffuseConstant:e,specularExponent:i,kernelUnitLength:a})}},a={distantLight:function(t,e){this.attr({azimuth:t,elevation:e})},pointLight:function(t,e,i){this.attr({x:t,y:e,z:i})},spotLight:function(t,e,i,a,s,r){this.attr({x:t,y:e,z:i,pointsAtX:a,pointsAtY:s,pointsAtZ:r})},mergeNode:function(t){this.attr(\"in\",t)}};function s(t){return Array.isArray(t)&&(t=new SVG.Array(t)),t.toString().replace(/^\\s+/,\"\").replace(/\\s+$/,\"\").replace(/\\s+/g,\" \")}function r(t){if(!Array.isArray(t))return t;for(var e=0,i=t.length,a=[];e<i;e++)a.push(t[e]);return a.join(\" \")}function o(){var t=function(){};for(var e in\"function\"==typeof arguments[arguments.length-1]&&(t=arguments[arguments.length-1],Array.prototype.splice.call(arguments,arguments.length-1,1)),arguments)for(var i in arguments[e])t(arguments[e][i],i,arguments[e])}[\"r\",\"g\",\"b\",\"a\"].forEach((function(t){a[\"Func\"+t.toUpperCase()]=function(t){switch(this.attr(\"type\",t),t){case\"table\":this.attr(\"tableValues\",arguments[1]);break;case\"linear\":this.attr(\"slope\",arguments[1]),this.attr(\"intercept\",arguments[2]);break;case\"gamma\":this.attr(\"amplitude\",arguments[1]),this.attr(\"exponent\",arguments[2]),this.attr(\"offset\",arguments[2])}}})),o(e,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+\"Effect\"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments),this.result(this.attr(\"id\")+\"Out\")},inherit:SVG.Effect,extend:{}})})),o(i,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+\"Effect\"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments),this.result(this.attr(\"id\")+\"Out\")},inherit:SVG.ParentEffect,extend:{}})})),o(a,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments)},inherit:SVG.ChildEffect,extend:{}})})),SVG.extend(SVG.MergeEffect,{in:function(t){return t instanceof SVG.MergeNode?this.add(t,0):this.add(new SVG.MergeNode(t),0),this}}),SVG.extend(SVG.CompositeEffect,SVG.BlendEffect,SVG.DisplacementMapEffect,{in2:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in2\")+'\"]').get(0)||this.attr(\"in2\"):this.attr(\"in2\",t)}}),SVG.filter={sepiatone:[.343,.669,.119,0,0,.249,.626,.13,0,0,.172,.334,.111,0,0,0,0,0,1,0]}}.call(void 0),function(){function t(t,s,r,o,n,l,h){for(var c=t.slice(s,r||h),d=o.slice(n,l||h),g=0,u={pos:[0,0],start:[0,0]},f={pos:[0,0],start:[0,0]};;){if(c[g]=e.call(u,c[g]),d[g]=e.call(f,d[g]),c[g][0]!=d[g][0]||\"M\"==c[g][0]||\"A\"==c[g][0]&&(c[g][4]!=d[g][4]||c[g][5]!=d[g][5])?(Array.prototype.splice.apply(c,[g,1].concat(a.call(u,c[g]))),Array.prototype.splice.apply(d,[g,1].concat(a.call(f,d[g])))):(c[g]=i.call(u,c[g]),d[g]=i.call(f,d[g])),++g==c.length&&g==d.length)break;g==c.length&&c.push([\"C\",u.pos[0],u.pos[1],u.pos[0],u.pos[1],u.pos[0],u.pos[1]]),g==d.length&&d.push([\"C\",f.pos[0],f.pos[1],f.pos[0],f.pos[1],f.pos[0],f.pos[1]])}return{start:c,dest:d}}function e(t){switch(t[0]){case\"z\":case\"Z\":t[0]=\"L\",t[1]=this.start[0],t[2]=this.start[1];break;case\"H\":t[0]=\"L\",t[2]=this.pos[1];break;case\"V\":t[0]=\"L\",t[2]=t[1],t[1]=this.pos[0];break;case\"T\":t[0]=\"Q\",t[3]=t[1],t[4]=t[2],t[1]=this.reflection[1],t[2]=this.reflection[0];break;case\"S\":t[0]=\"C\",t[6]=t[4],t[5]=t[3],t[4]=t[2],t[3]=t[1],t[2]=this.reflection[1],t[1]=this.reflection[0]}return t}function i(t){var e=t.length;return this.pos=[t[e-2],t[e-1]],-1!=\"SCQT\".indexOf(t[0])&&(this.reflection=[2*this.pos[0]-t[e-4],2*this.pos[1]-t[e-3]]),t}function a(t){var e=[t];switch(t[0]){case\"M\":return this.pos=this.start=[t[1],t[2]],e;case\"L\":t[5]=t[3]=t[1],t[6]=t[4]=t[2],t[1]=this.pos[0],t[2]=this.pos[1];break;case\"Q\":t[6]=t[4],t[5]=t[3],t[4]=1*t[4]/3+2*t[2]/3,t[3]=1*t[3]/3+2*t[1]/3,t[2]=1*this.pos[1]/3+2*t[2]/3,t[1]=1*this.pos[0]/3+2*t[1]/3;break;case\"A\":e=function(t,e){var i,a,s,r,o,n,l,h,c,d,g,u,f,p,x,b,v,m,y,w,k,A,S,C,L,P,T=Math.abs(e[1]),M=Math.abs(e[2]),I=e[3]%360,z=e[4],X=e[5],E=e[6],Y=e[7],F=new SVG.Point(t),R=new SVG.Point(E,Y),D=[];if(0===T||0===M||F.x===R.x&&F.y===R.y)return[[\"C\",F.x,F.y,R.x,R.y,R.x,R.y]];i=new SVG.Point((F.x-R.x)/2,(F.y-R.y)/2).transform((new SVG.Matrix).rotate(I)),(a=i.x*i.x/(T*T)+i.y*i.y/(M*M))>1&&(T*=a=Math.sqrt(a),M*=a);s=(new SVG.Matrix).rotate(I).scale(1/T,1/M).rotate(-I),F=F.transform(s),R=R.transform(s),r=[R.x-F.x,R.y-F.y],n=r[0]*r[0]+r[1]*r[1],o=Math.sqrt(n),r[0]/=o,r[1]/=o,l=n<4?Math.sqrt(1-n/4):0,z===X&&(l*=-1);h=new SVG.Point((R.x+F.x)/2+l*-r[1],(R.y+F.y)/2+l*r[0]),c=new SVG.Point(F.x-h.x,F.y-h.y),d=new SVG.Point(R.x-h.x,R.y-h.y),g=Math.acos(c.x/Math.sqrt(c.x*c.x+c.y*c.y)),c.y<0&&(g*=-1);u=Math.acos(d.x/Math.sqrt(d.x*d.x+d.y*d.y)),d.y<0&&(u*=-1);X&&g>u&&(u+=2*Math.PI);!X&&g<u&&(u-=2*Math.PI);for(p=Math.ceil(2*Math.abs(g-u)/Math.PI),b=[],v=g,f=(u-g)/p,x=4*Math.tan(f/4)/3,k=0;k<=p;k++)y=Math.cos(v),m=Math.sin(v),w=new SVG.Point(h.x+y,h.y+m),b[k]=[new SVG.Point(w.x+x*m,w.y-x*y),w,new SVG.Point(w.x-x*m,w.y+x*y)],v+=f;for(b[0][0]=b[0][1].clone(),b[b.length-1][2]=b[b.length-1][1].clone(),s=(new SVG.Matrix).rotate(I).scale(T,M).rotate(-I),k=0,A=b.length;k<A;k++)b[k][0]=b[k][0].transform(s),b[k][1]=b[k][1].transform(s),b[k][2]=b[k][2].transform(s);for(k=1,A=b.length;k<A;k++)S=(w=b[k-1][2]).x,C=w.y,L=(w=b[k][0]).x,P=w.y,E=(w=b[k][1]).x,Y=w.y,D.push([\"C\",S,C,L,P,E,Y]);return D}(this.pos,t),t=e[0]}return t[0]=\"C\",this.pos=[t[5],t[6]],this.reflection=[2*t[5]-t[3],2*t[6]-t[4]],e}function s(t,e){if(!1===e)return!1;for(var i=e,a=t.length;i<a;++i)if(\"M\"==t[i][0])return i;return!1}SVG.extend(SVG.PathArray,{morph:function(e){for(var i=this.value,a=this.parse(e),r=0,o=0,n=!1,l=!1;!1!==r||!1!==o;){var h;n=s(i,!1!==r&&r+1),l=s(a,!1!==o&&o+1),!1===r&&(r=0==(h=new SVG.PathArray(c.start).bbox()).height||0==h.width?i.push(i[0])-1:i.push([\"M\",h.x+h.width/2,h.y+h.height/2])-1),!1===o&&(o=0==(h=new SVG.PathArray(c.dest).bbox()).height||0==h.width?a.push(a[0])-1:a.push([\"M\",h.x+h.width/2,h.y+h.height/2])-1);var c=t(i,r,n,a,o,l);i=i.slice(0,r).concat(c.start,!1===n?[]:i.slice(n)),a=a.slice(0,o).concat(c.dest,!1===l?[]:a.slice(l)),r=!1!==n&&r+c.start.length,o=!1!==l&&o+c.dest.length}return this.value=i,this.destination=new SVG.PathArray,this.destination.value=a,this}})}(),\n/*! svg.draggable.js - v2.2.2 - 2019-01-08\n* https://github.com/svgdotjs/svg.draggable.js\n* Copyright (c) 2019 Wout Fierens; Licensed MIT */\nfunction(){function t(t){t.remember(\"_draggable\",this),this.el=t}t.prototype.init=function(t,e){var i=this;this.constraint=t,this.value=e,this.el.on(\"mousedown.drag\",(function(t){i.start(t)})),this.el.on(\"touchstart.drag\",(function(t){i.start(t)}))},t.prototype.transformPoint=function(t,e){var i=(t=t||window.event).changedTouches&&t.changedTouches[0]||t;return this.p.x=i.clientX-(e||0),this.p.y=i.clientY,this.p.matrixTransform(this.m)},t.prototype.getBBox=function(){var t=this.el.bbox();return this.el instanceof SVG.Nested&&(t=this.el.rbox()),(this.el instanceof SVG.G||this.el instanceof SVG.Use||this.el instanceof SVG.Nested)&&(t.x=this.el.x(),t.y=this.el.y()),t},t.prototype.start=function(t){if(\"click\"!=t.type&&\"mousedown\"!=t.type&&\"mousemove\"!=t.type||1==(t.which||t.buttons)){var e=this;if(this.el.fire(\"beforedrag\",{event:t,handler:this}),!this.el.event().defaultPrevented){t.preventDefault(),t.stopPropagation(),this.parent=this.parent||this.el.parent(SVG.Nested)||this.el.parent(SVG.Doc),this.p=this.parent.node.createSVGPoint(),this.m=this.el.node.getScreenCTM().inverse();var i,a=this.getBBox();if(this.el instanceof SVG.Text)switch(i=this.el.node.getComputedTextLength(),this.el.attr(\"text-anchor\")){case\"middle\":i/=2;break;case\"start\":i=0}this.startPoints={point:this.transformPoint(t,i),box:a,transform:this.el.transform()},SVG.on(window,\"mousemove.drag\",(function(t){e.drag(t)})),SVG.on(window,\"touchmove.drag\",(function(t){e.drag(t)})),SVG.on(window,\"mouseup.drag\",(function(t){e.end(t)})),SVG.on(window,\"touchend.drag\",(function(t){e.end(t)})),this.el.fire(\"dragstart\",{event:t,p:this.startPoints.point,m:this.m,handler:this})}}},t.prototype.drag=function(t){var e=this.getBBox(),i=this.transformPoint(t),a=this.startPoints.box.x+i.x-this.startPoints.point.x,s=this.startPoints.box.y+i.y-this.startPoints.point.y,r=this.constraint,o=i.x-this.startPoints.point.x,n=i.y-this.startPoints.point.y;if(this.el.fire(\"dragmove\",{event:t,p:i,m:this.m,handler:this}),this.el.event().defaultPrevented)return i;if(\"function\"==typeof r){var l=r.call(this.el,a,s,this.m);\"boolean\"==typeof l&&(l={x:l,y:l}),!0===l.x?this.el.x(a):!1!==l.x&&this.el.x(l.x),!0===l.y?this.el.y(s):!1!==l.y&&this.el.y(l.y)}else\"object\"==typeof r&&(null!=r.minX&&a<r.minX?o=(a=r.minX)-this.startPoints.box.x:null!=r.maxX&&a>r.maxX-e.width&&(o=(a=r.maxX-e.width)-this.startPoints.box.x),null!=r.minY&&s<r.minY?n=(s=r.minY)-this.startPoints.box.y:null!=r.maxY&&s>r.maxY-e.height&&(n=(s=r.maxY-e.height)-this.startPoints.box.y),null!=r.snapToGrid&&(a-=a%r.snapToGrid,s-=s%r.snapToGrid,o-=o%r.snapToGrid,n-=n%r.snapToGrid),this.el instanceof SVG.G?this.el.matrix(this.startPoints.transform).transform({x:o,y:n},!0):this.el.move(a,s));return i},t.prototype.end=function(t){var e=this.drag(t);this.el.fire(\"dragend\",{event:t,p:e,m:this.m,handler:this}),SVG.off(window,\"mousemove.drag\"),SVG.off(window,\"touchmove.drag\"),SVG.off(window,\"mouseup.drag\"),SVG.off(window,\"touchend.drag\")},SVG.extend(SVG.Element,{draggable:function(e,i){\"function\"!=typeof e&&\"object\"!=typeof e||(i=e,e=!0);var a=this.remember(\"_draggable\")||new t(this);return(e=void 0===e||e)?a.init(i||{},e):(this.off(\"mousedown.drag\"),this.off(\"touchstart.drag\")),this}})}.call(void 0),function(){function t(t){this.el=t,t.remember(\"_selectHandler\",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1},this.pointsList={lt:[0,0],rt:[\"width\",0],rb:[\"width\",\"height\"],lb:[0,\"height\"],t:[\"width\",0],r:[\"width\",\"height\"],b:[\"width\",\"height\"],l:[0,\"height\"]},this.pointCoord=function(t,e,i){var a=\"string\"!=typeof t?t:e[t];return i?a/2:a},this.pointCoords=function(t,e){var i=this.pointsList[t];return{x:this.pointCoord(i[0],e,\"t\"===t||\"b\"===t),y:this.pointCoord(i[1],e,\"r\"===t||\"l\"===t)}}}t.prototype.init=function(t,e){var i=this.el.bbox();this.options={};var a=this.el.selectize.defaults.points;for(var s in this.el.selectize.defaults)this.options[s]=this.el.selectize.defaults[s],void 0!==e[s]&&(this.options[s]=e[s]);var r=[\"points\",\"pointsExclude\"];for(var s in r){var o=this.options[r[s]];\"string\"==typeof o?o=o.length>0?o.split(/\\s*,\\s*/i):[]:\"boolean\"==typeof o&&\"points\"===r[s]&&(o=o?a:[]),this.options[r[s]]=o}this.options.points=[a,this.options.points].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)>-1}))})),this.options.points=[this.options.points,this.options.pointsExclude].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)<0}))})),this.parent=this.el.parent(),this.nested=this.nested||this.parent.group(),this.nested.matrix(new SVG.Matrix(this.el).translate(i.x,i.y)),this.options.deepSelect&&-1!==[\"line\",\"polyline\",\"polygon\"].indexOf(this.el.type)?this.selectPoints(t):this.selectRect(t),this.observe(),this.cleanup()},t.prototype.selectPoints=function(t){return this.pointSelection.isSelected=t,this.pointSelection.set||(this.pointSelection.set=this.parent.set(),this.drawPoints()),this},t.prototype.getPointArray=function(){var t=this.el.bbox();return this.el.array().valueOf().map((function(e){return[e[0]-t.x,e[1]-t.y]}))},t.prototype.drawPoints=function(){for(var t=this,e=this.getPointArray(),i=0,a=e.length;i<a;++i){var s=function(e){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;t.el.fire(\"point\",{x:a,y:s,i:e,event:i})}}(i),r=this.drawPoint(e[i][0],e[i][1]).addClass(this.options.classPoints).addClass(this.options.classPoints+\"_point\").on(\"touchstart\",s).on(\"mousedown\",s);this.pointSelection.set.add(r)}},t.prototype.drawPoint=function(t,e){var i=this.options.pointType;switch(i){case\"circle\":return this.drawCircle(t,e);case\"rect\":return this.drawRect(t,e);default:if(\"function\"==typeof i)return i.call(this,t,e);throw new Error(\"Unknown \"+i+\" point type!\")}},t.prototype.drawCircle=function(t,e){return this.nested.circle(this.options.pointSize).center(t,e)},t.prototype.drawRect=function(t,e){return this.nested.rect(this.options.pointSize,this.options.pointSize).center(t,e)},t.prototype.updatePointSelection=function(){var t=this.getPointArray();this.pointSelection.set.each((function(e){this.cx()===t[e][0]&&this.cy()===t[e][1]||this.center(t[e][0],t[e][1])}))},t.prototype.updateRectSelection=function(){var t=this,e=this.el.bbox();if(this.rectSelection.set.get(0).attr({width:e.width,height:e.height}),this.options.points.length&&this.options.points.map((function(i,a){var s=t.pointCoords(i,e);t.rectSelection.set.get(a+1).center(s.x,s.y)})),this.options.rotationPoint){var i=this.rectSelection.set.length();this.rectSelection.set.get(i-1).center(e.width/2,20)}},t.prototype.selectRect=function(t){var e=this,i=this.el.bbox();function a(t){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;e.el.fire(t,{x:a,y:s,event:i})}}if(this.rectSelection.isSelected=t,this.rectSelection.set=this.rectSelection.set||this.parent.set(),this.rectSelection.set.get(0)||this.rectSelection.set.add(this.nested.rect(i.width,i.height).addClass(this.options.classRect)),this.options.points.length&&this.rectSelection.set.length()<2){this.options.points.map((function(t,s){var r=e.pointCoords(t,i),o=e.drawPoint(r.x,r.y).attr(\"class\",e.options.classPoints+\"_\"+t).on(\"mousedown\",a(t)).on(\"touchstart\",a(t));e.rectSelection.set.add(o)})),this.rectSelection.set.each((function(){this.addClass(e.options.classPoints)}))}if(this.options.rotationPoint&&(this.options.points&&!this.rectSelection.set.get(9)||!this.options.points&&!this.rectSelection.set.get(1))){var s=function(t){(t=t||window.event).preventDefault?t.preventDefault():t.returnValue=!1,t.stopPropagation();var i=t.pageX||t.touches[0].pageX,a=t.pageY||t.touches[0].pageY;e.el.fire(\"rot\",{x:i,y:a,event:t})},r=this.drawPoint(i.width/2,20).attr(\"class\",this.options.classPoints+\"_rot\").on(\"touchstart\",s).on(\"mousedown\",s);this.rectSelection.set.add(r)}},t.prototype.handler=function(){var t=this.el.bbox();this.nested.matrix(new SVG.Matrix(this.el).translate(t.x,t.y)),this.rectSelection.isSelected&&this.updateRectSelection(),this.pointSelection.isSelected&&this.updatePointSelection()},t.prototype.observe=function(){var t=this;if(MutationObserver)if(this.rectSelection.isSelected||this.pointSelection.isSelected)this.observerInst=this.observerInst||new MutationObserver((function(){t.handler()})),this.observerInst.observe(this.el.node,{attributes:!0});else try{this.observerInst.disconnect(),delete this.observerInst}catch(t){}else this.el.off(\"DOMAttrModified.select\"),(this.rectSelection.isSelected||this.pointSelection.isSelected)&&this.el.on(\"DOMAttrModified.select\",(function(){t.handler()}))},t.prototype.cleanup=function(){!this.rectSelection.isSelected&&this.rectSelection.set&&(this.rectSelection.set.each((function(){this.remove()})),this.rectSelection.set.clear(),delete this.rectSelection.set),!this.pointSelection.isSelected&&this.pointSelection.set&&(this.pointSelection.set.each((function(){this.remove()})),this.pointSelection.set.clear(),delete this.pointSelection.set),this.pointSelection.isSelected||this.rectSelection.isSelected||(this.nested.remove(),delete this.nested)},SVG.extend(SVG.Element,{selectize:function(e,i){return\"object\"==typeof e&&(i=e,e=!0),(this.remember(\"_selectHandler\")||new t(this)).init(void 0===e||e,i||{}),this}}),SVG.Element.prototype.selectize.defaults={points:[\"lt\",\"rt\",\"rb\",\"lb\",\"t\",\"r\",\"b\",\"l\"],pointsExclude:[],classRect:\"svg_select_boundingRect\",classPoints:\"svg_select_points\",pointSize:7,rotationPoint:!0,deepSelect:!1,pointType:\"circle\"}}(),function(){(function(){function t(t){t.remember(\"_resizeHandler\",this),this.el=t,this.parameters={},this.lastUpdateCall=null,this.p=t.doc().node.createSVGPoint()}t.prototype.transformPoint=function(t,e,i){return this.p.x=t-(this.offset.x-window.pageXOffset),this.p.y=e-(this.offset.y-window.pageYOffset),this.p.matrixTransform(i||this.m)},t.prototype._extractPosition=function(t){return{x:null!=t.clientX?t.clientX:t.touches[0].clientX,y:null!=t.clientY?t.clientY:t.touches[0].clientY}},t.prototype.init=function(t){var e=this;if(this.stop(),\"stop\"!==t){for(var i in this.options={},this.el.resize.defaults)this.options[i]=this.el.resize.defaults[i],void 0!==t[i]&&(this.options[i]=t[i]);this.el.on(\"lt.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rt.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rb.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"lb.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"t.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"r.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"b.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"l.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rot.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"point.resize\",(function(t){e.resize(t||window.event)})),this.update()}},t.prototype.stop=function(){return this.el.off(\"lt.resize\"),this.el.off(\"rt.resize\"),this.el.off(\"rb.resize\"),this.el.off(\"lb.resize\"),this.el.off(\"t.resize\"),this.el.off(\"r.resize\"),this.el.off(\"b.resize\"),this.el.off(\"l.resize\"),this.el.off(\"rot.resize\"),this.el.off(\"point.resize\"),this},t.prototype.resize=function(t){var e=this;this.m=this.el.node.getScreenCTM().inverse(),this.offset={x:window.pageXOffset,y:window.pageYOffset};var i=this._extractPosition(t.detail.event);if(this.parameters={type:this.el.type,p:this.transformPoint(i.x,i.y),x:t.detail.x,y:t.detail.y,box:this.el.bbox(),rotation:this.el.transform().rotation},\"text\"===this.el.type&&(this.parameters.fontSize=this.el.attr()[\"font-size\"]),void 0!==t.detail.i){var a=this.el.array().valueOf();this.parameters.i=t.detail.i,this.parameters.pointCoords=[a[t.detail.i][0],a[t.detail.i][1]]}switch(t.type){case\"lt\":this.calc=function(t,e){var i=this.snapToGrid(t,e);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y+i[1]).size(this.parameters.box.width-i[0],this.parameters.box.height-i[1])}};break;case\"rt\":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).size(this.parameters.box.width+i[0],this.parameters.box.height-i[1])}};break;case\"rb\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x,this.parameters.box.y).size(this.parameters.box.width+i[0],this.parameters.box.height+i[1])}};break;case\"lb\":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).size(this.parameters.box.width-i[0],this.parameters.box.height+i[1])}};break;case\"t\":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).height(this.parameters.box.height-i[1])}};break;case\"r\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).width(this.parameters.box.width+i[0])}};break;case\"b\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).height(this.parameters.box.height+i[1])}};break;case\"l\":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).width(this.parameters.box.width-i[0])}};break;case\"rot\":this.calc=function(t,e){var i=t+this.parameters.p.x,a=e+this.parameters.p.y,s=Math.atan2(this.parameters.p.y-this.parameters.box.y-this.parameters.box.height/2,this.parameters.p.x-this.parameters.box.x-this.parameters.box.width/2),r=Math.atan2(a-this.parameters.box.y-this.parameters.box.height/2,i-this.parameters.box.x-this.parameters.box.width/2),o=this.parameters.rotation+180*(r-s)/Math.PI+this.options.snapToAngle/2;this.el.center(this.parameters.box.cx,this.parameters.box.cy).rotate(o-o%this.options.snapToAngle,this.parameters.box.cx,this.parameters.box.cy)};break;case\"point\":this.calc=function(t,e){var i=this.snapToGrid(t,e,this.parameters.pointCoords[0],this.parameters.pointCoords[1]),a=this.el.array().valueOf();a[this.parameters.i][0]=this.parameters.pointCoords[0]+i[0],a[this.parameters.i][1]=this.parameters.pointCoords[1]+i[1],this.el.plot(a)}}this.el.fire(\"resizestart\",{dx:this.parameters.x,dy:this.parameters.y,event:t}),SVG.on(window,\"touchmove.resize\",(function(t){e.update(t||window.event)})),SVG.on(window,\"touchend.resize\",(function(){e.done()})),SVG.on(window,\"mousemove.resize\",(function(t){e.update(t||window.event)})),SVG.on(window,\"mouseup.resize\",(function(){e.done()}))},t.prototype.update=function(t){if(t){var e=this._extractPosition(t),i=this.transformPoint(e.x,e.y),a=i.x-this.parameters.p.x,s=i.y-this.parameters.p.y;this.lastUpdateCall=[a,s],this.calc(a,s),this.el.fire(\"resizing\",{dx:a,dy:s,event:t})}else this.lastUpdateCall&&this.calc(this.lastUpdateCall[0],this.lastUpdateCall[1])},t.prototype.done=function(){this.lastUpdateCall=null,SVG.off(window,\"mousemove.resize\"),SVG.off(window,\"mouseup.resize\"),SVG.off(window,\"touchmove.resize\"),SVG.off(window,\"touchend.resize\"),this.el.fire(\"resizedone\")},t.prototype.snapToGrid=function(t,e,i,a){var s;return void 0!==a?s=[(i+t)%this.options.snapToGrid,(a+e)%this.options.snapToGrid]:(i=null==i?3:i,s=[(this.parameters.box.x+t+(1&i?0:this.parameters.box.width))%this.options.snapToGrid,(this.parameters.box.y+e+(2&i?0:this.parameters.box.height))%this.options.snapToGrid]),t<0&&(s[0]-=this.options.snapToGrid),e<0&&(s[1]-=this.options.snapToGrid),t-=Math.abs(s[0])<this.options.snapToGrid/2?s[0]:s[0]-(t<0?-this.options.snapToGrid:this.options.snapToGrid),e-=Math.abs(s[1])<this.options.snapToGrid/2?s[1]:s[1]-(e<0?-this.options.snapToGrid:this.options.snapToGrid),this.constraintToBox(t,e,i,a)},t.prototype.constraintToBox=function(t,e,i,a){var s,r,o=this.options.constraint||{};return void 0!==a?(s=i,r=a):(s=this.parameters.box.x+(1&i?0:this.parameters.box.width),r=this.parameters.box.y+(2&i?0:this.parameters.box.height)),void 0!==o.minX&&s+t<o.minX&&(t=o.minX-s),void 0!==o.maxX&&s+t>o.maxX&&(t=o.maxX-s),void 0!==o.minY&&r+e<o.minY&&(e=o.minY-r),void 0!==o.maxY&&r+e>o.maxY&&(e=o.maxY-r),[t,e]},t.prototype.checkAspectRatio=function(t,e){if(!this.options.saveAspectRatio)return t;var i=t.slice(),a=this.parameters.box.width/this.parameters.box.height,s=this.parameters.box.width+t[0],r=this.parameters.box.height-t[1],o=s/r;return o<a?(i[1]=s/a-this.parameters.box.height,e&&(i[1]=-i[1])):o>a&&(i[0]=this.parameters.box.width-r*a,e&&(i[0]=-i[0])),i},SVG.extend(SVG.Element,{resize:function(e){return(this.remember(\"_resizeHandler\")||new t(this)).init(e||{}),this}}),SVG.Element.prototype.resize.defaults={snapToAngle:.1,snapToGrid:1,constraint:{},saveAspectRatio:!1}}).call(this)}(),void 0===window.Apex&&(window.Apex={});var Ht=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"initModules\",value:function(){this.ctx.publicMethods=[\"updateOptions\",\"updateSeries\",\"appendData\",\"appendSeries\",\"toggleSeries\",\"showSeries\",\"hideSeries\",\"setLocale\",\"resetSeries\",\"zoomX\",\"toggleDataPointSelection\",\"dataURI\",\"exportToCSV\",\"addXaxisAnnotation\",\"addYaxisAnnotation\",\"addPointAnnotation\",\"clearAnnotations\",\"removeAnnotation\",\"paper\",\"destroy\"],this.ctx.eventList=[\"click\",\"mousedown\",\"mousemove\",\"mouseleave\",\"touchstart\",\"touchmove\",\"touchleave\",\"mouseup\",\"touchend\"],this.ctx.animations=new b(this.ctx),this.ctx.axes=new J(this.ctx),this.ctx.core=new Rt(this.ctx.el,this.ctx),this.ctx.config=new E({}),this.ctx.data=new W(this.ctx),this.ctx.grid=new j(this.ctx),this.ctx.graphics=new m(this.ctx),this.ctx.coreUtils=new y(this.ctx),this.ctx.crosshairs=new Q(this.ctx),this.ctx.events=new Z(this.ctx),this.ctx.exports=new G(this.ctx),this.ctx.localization=new $(this.ctx),this.ctx.options=new L,this.ctx.responsive=new K(this.ctx),this.ctx.series=new N(this.ctx),this.ctx.theme=new tt(this.ctx),this.ctx.formatters=new M(this.ctx),this.ctx.titleSubtitle=new et(this.ctx),this.ctx.legend=new lt(this.ctx),this.ctx.toolbar=new ht(this.ctx),this.ctx.tooltip=new bt(this.ctx),this.ctx.dimensions=new ot(this.ctx),this.ctx.updateHelpers=new Dt(this.ctx),this.ctx.zoomPanSelection=new ct(this.ctx),this.ctx.w.globals.tooltip=new bt(this.ctx)}}]),t}(),Ot=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"clear\",value:function(t){var e=t.isUpdating;this.ctx.zoomPanSelection&&this.ctx.zoomPanSelection.destroy(),this.ctx.toolbar&&this.ctx.toolbar.destroy(),this.ctx.animations=null,this.ctx.axes=null,this.ctx.annotations=null,this.ctx.core=null,this.ctx.data=null,this.ctx.grid=null,this.ctx.series=null,this.ctx.responsive=null,this.ctx.theme=null,this.ctx.formatters=null,this.ctx.titleSubtitle=null,this.ctx.legend=null,this.ctx.dimensions=null,this.ctx.options=null,this.ctx.crosshairs=null,this.ctx.zoomPanSelection=null,this.ctx.updateHelpers=null,this.ctx.toolbar=null,this.ctx.localization=null,this.ctx.w.globals.tooltip=null,this.clearDomElements({isUpdating:e})}},{key:\"killSVG\",value:function(t){t.each((function(t,e){this.removeClass(\"*\"),this.off(),this.stop()}),!0),t.ungroup(),t.clear()}},{key:\"clearDomElements\",value:function(t){var e=this,i=t.isUpdating,a=this.w.globals.dom.Paper.node;a.parentNode&&a.parentNode.parentNode&&!i&&(a.parentNode.parentNode.style.minHeight=\"unset\");var s=this.w.globals.dom.baseEl;s&&this.ctx.eventList.forEach((function(t){s.removeEventListener(t,e.ctx.events.documentEvent)}));var r=this.w.globals.dom;if(null!==this.ctx.el)for(;this.ctx.el.firstChild;)this.ctx.el.removeChild(this.ctx.el.firstChild);this.killSVG(r.Paper),r.Paper.remove(),r.elWrap=null,r.elGraphical=null,r.elAnnotations=null,r.elLegendWrap=null,r.baseEl=null,r.elGridRect=null,r.elGridRectMask=null,r.elGridRectMarkerMask=null,r.elForecastMask=null,r.elNonForecastMask=null,r.elDefs=null}}]),t}(),Nt=new WeakMap;var Wt=function(){function t(e,i){a(this,t),this.opts=i,this.ctx=this,this.w=new F(i).init(),this.el=e,this.w.globals.cuid=x.randomId(),this.w.globals.chartID=this.w.config.chart.id?x.escapeString(this.w.config.chart.id):this.w.globals.cuid,new Ht(this).initModules(),this.create=x.bind(this.create,this),this.windowResizeHandler=this._windowResizeHandler.bind(this),this.parentResizeHandler=this._parentResizeCallback.bind(this)}return r(t,[{key:\"render\",value:function(){var t=this;return new Promise((function(e,i){if(null!==t.el){void 0===Apex._chartInstances&&(Apex._chartInstances=[]),t.w.config.chart.id&&Apex._chartInstances.push({id:t.w.globals.chartID,group:t.w.config.chart.group,chart:t}),t.setLocale(t.w.config.chart.defaultLocale);var a=t.w.config.chart.events.beforeMount;if(\"function\"==typeof a&&a(t,t.w),t.events.fireEvent(\"beforeMount\",[t,t.w]),window.addEventListener(\"resize\",t.windowResizeHandler),function(t,e){var i=!1;if(t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE){var a=t.getBoundingClientRect();\"none\"!==t.style.display&&0!==a.width||(i=!0)}var s=new ResizeObserver((function(a){i&&e.call(t,a),i=!0}));t.nodeType===Node.DOCUMENT_FRAGMENT_NODE?Array.from(t.children).forEach((function(t){return s.observe(t)})):s.observe(t),Nt.set(e,s)}(t.el.parentNode,t.parentResizeHandler),!t.css){var s=t.el.getRootNode&&t.el.getRootNode(),r=x.is(\"ShadowRoot\",s),o=t.el.ownerDocument,n=o.getElementById(\"apexcharts-css\");!r&&n||(t.css=document.createElement(\"style\"),t.css.id=\"apexcharts-css\",t.css.textContent='@keyframes opaque {\\n  0% {\\n      opacity: 0\\n  }\\n\\n  to {\\n      opacity: 1\\n  }\\n}\\n\\n@keyframes resizeanim {\\n  0%,to {\\n      opacity: 0\\n  }\\n}\\n\\n.apexcharts-canvas {\\n  position: relative;\\n  user-select: none\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar {\\n  -webkit-appearance: none;\\n  width: 6px\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar-thumb {\\n  border-radius: 4px;\\n  background-color: rgba(0,0,0,.5);\\n  box-shadow: 0 0 1px rgba(255,255,255,.5);\\n  -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5)\\n}\\n\\n.apexcharts-inner {\\n  position: relative\\n}\\n\\n.apexcharts-text tspan {\\n  font-family: inherit\\n}\\n\\n.legend-mouseover-inactive {\\n  transition: .15s ease all;\\n  opacity: .2\\n}\\n\\n.apexcharts-legend-text {\\n  padding-left: 15px;\\n  margin-left: -15px;\\n}\\n\\n.apexcharts-series-collapsed {\\n  opacity: 0\\n}\\n\\n.apexcharts-tooltip {\\n  border-radius: 5px;\\n  box-shadow: 2px 2px 6px -4px #999;\\n  cursor: default;\\n  font-size: 14px;\\n  left: 62px;\\n  opacity: 0;\\n  pointer-events: none;\\n  position: absolute;\\n  top: 20px;\\n  display: flex;\\n  flex-direction: column;\\n  overflow: hidden;\\n  white-space: nowrap;\\n  z-index: 12;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light {\\n  border: 1px solid #e3e3e3;\\n  background: rgba(255,255,255,.96)\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark {\\n  color: #fff;\\n  background: rgba(30,30,30,.8)\\n}\\n\\n.apexcharts-tooltip * {\\n  font-family: inherit\\n}\\n\\n.apexcharts-tooltip-title {\\n  padding: 6px;\\n  font-size: 15px;\\n  margin-bottom: 4px\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {\\n  background: #eceff1;\\n  border-bottom: 1px solid #ddd\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {\\n  background: rgba(0,0,0,.7);\\n  border-bottom: 1px solid #333\\n}\\n\\n.apexcharts-tooltip-text-goals-value,.apexcharts-tooltip-text-y-value,.apexcharts-tooltip-text-z-value {\\n  display: inline-block;\\n  margin-left: 5px;\\n  font-weight: 600\\n}\\n\\n.apexcharts-tooltip-text-goals-label:empty,.apexcharts-tooltip-text-goals-value:empty,.apexcharts-tooltip-text-y-label:empty,.apexcharts-tooltip-text-y-value:empty,.apexcharts-tooltip-text-z-value:empty,.apexcharts-tooltip-title:empty {\\n  display: none\\n}\\n\\n.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-tooltip-goals-group,.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  display: flex\\n}\\n\\n.apexcharts-tooltip-text-goals-label:not(:empty),.apexcharts-tooltip-text-goals-value:not(:empty) {\\n  margin-top: -6px\\n}\\n\\n.apexcharts-tooltip-marker {\\n  width: 12px;\\n  height: 12px;\\n  position: relative;\\n  top: 0;\\n  margin-right: 10px;\\n  border-radius: 50%\\n}\\n\\n.apexcharts-tooltip-series-group {\\n  padding: 0 10px;\\n  display: none;\\n  text-align: left;\\n  justify-content: left;\\n  align-items: center\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {\\n  opacity: 1\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active,.apexcharts-tooltip-series-group:last-child {\\n  padding-bottom: 4px\\n}\\n\\n.apexcharts-tooltip-series-group-hidden {\\n  opacity: 0;\\n  height: 0;\\n  line-height: 0;\\n  padding: 0!important\\n}\\n\\n.apexcharts-tooltip-y-group {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-custom-tooltip,.apexcharts-tooltip-box {\\n  padding: 4px 8px\\n}\\n\\n.apexcharts-tooltip-boxPlot {\\n  display: flex;\\n  flex-direction: column-reverse\\n}\\n\\n.apexcharts-tooltip-box>div {\\n  margin: 4px 0\\n}\\n\\n.apexcharts-tooltip-box span.value {\\n  font-weight: 700\\n}\\n\\n.apexcharts-tooltip-rangebar {\\n  padding: 5px 8px\\n}\\n\\n.apexcharts-tooltip-rangebar .category {\\n  font-weight: 600;\\n  color: #777\\n}\\n\\n.apexcharts-tooltip-rangebar .series-name {\\n  font-weight: 700;\\n  display: block;\\n  margin-bottom: 5px\\n}\\n\\n.apexcharts-xaxistooltip,.apexcharts-yaxistooltip {\\n  opacity: 0;\\n  pointer-events: none;\\n  color: #373d3f;\\n  font-size: 13px;\\n  text-align: center;\\n  border-radius: 2px;\\n  position: absolute;\\n  z-index: 10;\\n  background: #eceff1;\\n  border: 1px solid #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip {\\n  padding: 9px 10px;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-xaxistooltip:after,.apexcharts-xaxistooltip:before {\\n  left: 50%;\\n  border: solid transparent;\\n  content: \" \";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-xaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-left: -6px\\n}\\n\\n.apexcharts-xaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-left: -7px\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after,.apexcharts-xaxistooltip-bottom:before {\\n  bottom: 100%\\n}\\n\\n.apexcharts-xaxistooltip-top:after,.apexcharts-xaxistooltip-top:before {\\n  top: 100%\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after {\\n  border-bottom-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-bottom:before {\\n  border-bottom-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {\\n  border-bottom-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip-top:after {\\n  border-top-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-top:before {\\n  border-top-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {\\n  border-top-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-yaxistooltip {\\n  padding: 4px 10px\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-yaxistooltip:after,.apexcharts-yaxistooltip:before {\\n  top: 50%;\\n  border: solid transparent;\\n  content: \" \";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-yaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-top: -6px\\n}\\n\\n.apexcharts-yaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-top: -7px\\n}\\n\\n.apexcharts-yaxistooltip-left:after,.apexcharts-yaxistooltip-left:before {\\n  left: 100%\\n}\\n\\n.apexcharts-yaxistooltip-right:after,.apexcharts-yaxistooltip-right:before {\\n  right: 100%\\n}\\n\\n.apexcharts-yaxistooltip-left:after {\\n  border-left-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-left:before {\\n  border-left-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {\\n  border-left-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip-right:after {\\n  border-right-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-right:before {\\n  border-right-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {\\n  border-right-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-active {\\n  opacity: 1\\n}\\n\\n.apexcharts-yaxistooltip-hidden {\\n  display: none\\n}\\n\\n.apexcharts-xcrosshairs,.apexcharts-ycrosshairs {\\n  pointer-events: none;\\n  opacity: 0;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xcrosshairs.apexcharts-active,.apexcharts-ycrosshairs.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-ycrosshairs-hidden {\\n  opacity: 0\\n}\\n\\n.apexcharts-selection-rect {\\n  cursor: move\\n}\\n\\n.svg_select_boundingRect,.svg_select_points_rot {\\n  pointer-events: none;\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_boundingRect,.apexcharts-selection-rect+g .svg_select_points_rot {\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_points_l,.apexcharts-selection-rect+g .svg_select_points_r {\\n  cursor: ew-resize;\\n  opacity: 1;\\n  visibility: visible\\n}\\n\\n.svg_select_points {\\n  fill: #efefef;\\n  stroke: #333;\\n  rx: 2\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-zoom {\\n  cursor: crosshair\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-pan {\\n  cursor: move\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-pan-icon,.apexcharts-reset-icon,.apexcharts-selection-icon,.apexcharts-toolbar-custom-icon,.apexcharts-zoom-icon,.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  cursor: pointer;\\n  width: 20px;\\n  height: 20px;\\n  line-height: 24px;\\n  color: #6e8192;\\n  text-align: center\\n}\\n\\n.apexcharts-menu-icon svg,.apexcharts-reset-icon svg,.apexcharts-zoom-icon svg,.apexcharts-zoomin-icon svg,.apexcharts-zoomout-icon svg {\\n  fill: #6e8192\\n}\\n\\n.apexcharts-selection-icon svg {\\n  fill: #444;\\n  transform: scale(.76)\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu-icon svg,.apexcharts-theme-dark .apexcharts-pan-icon svg,.apexcharts-theme-dark .apexcharts-reset-icon svg,.apexcharts-theme-dark .apexcharts-selection-icon svg,.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg,.apexcharts-theme-dark .apexcharts-zoom-icon svg,.apexcharts-theme-dark .apexcharts-zoomin-icon svg,.apexcharts-theme-dark .apexcharts-zoomout-icon svg {\\n  fill: #f3f4f5\\n}\\n\\n.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg {\\n  fill: #008ffb\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-icon:hover svg,.apexcharts-theme-light .apexcharts-reset-icon:hover svg,.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg {\\n  fill: #333\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-selection-icon {\\n  position: relative\\n}\\n\\n.apexcharts-reset-icon {\\n  margin-left: 5px\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-reset-icon,.apexcharts-zoom-icon {\\n  transform: scale(.85)\\n}\\n\\n.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  transform: scale(.7)\\n}\\n\\n.apexcharts-zoomout-icon {\\n  margin-right: 3px\\n}\\n\\n.apexcharts-pan-icon {\\n  transform: scale(.62);\\n  position: relative;\\n  left: 1px;\\n  top: 0\\n}\\n\\n.apexcharts-pan-icon svg {\\n  fill: #fff;\\n  stroke: #6e8192;\\n  stroke-width: 2\\n}\\n\\n.apexcharts-pan-icon.apexcharts-selected svg {\\n  stroke: #008ffb\\n}\\n\\n.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {\\n  stroke: #333\\n}\\n\\n.apexcharts-toolbar {\\n  position: absolute;\\n  z-index: 11;\\n  max-width: 176px;\\n  text-align: right;\\n  border-radius: 3px;\\n  padding: 0 6px 2px;\\n  display: flex;\\n  justify-content: space-between;\\n  align-items: center\\n}\\n\\n.apexcharts-menu {\\n  background: #fff;\\n  position: absolute;\\n  top: 100%;\\n  border: 1px solid #ddd;\\n  border-radius: 3px;\\n  padding: 3px;\\n  right: 10px;\\n  opacity: 0;\\n  min-width: 110px;\\n  transition: .15s ease all;\\n  pointer-events: none\\n}\\n\\n.apexcharts-menu.apexcharts-menu-open {\\n  opacity: 1;\\n  pointer-events: all;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-menu-item {\\n  padding: 6px 7px;\\n  font-size: 12px;\\n  cursor: pointer\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-item:hover {\\n  background: #eee\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu {\\n  background: rgba(0,0,0,.7);\\n  color: #fff\\n}\\n\\n@media screen and (min-width:768px) {\\n  .apexcharts-canvas:hover .apexcharts-toolbar {\\n      opacity: 1\\n  }\\n}\\n\\n.apexcharts-canvas .apexcharts-element-hidden,.apexcharts-datalabel.apexcharts-element-hidden,.apexcharts-hide .apexcharts-series-points {\\n  opacity: 0\\n}\\n\\n.apexcharts-datalabel,.apexcharts-datalabel-label,.apexcharts-datalabel-value,.apexcharts-datalabels,.apexcharts-pie-label {\\n  cursor: default;\\n  pointer-events: none\\n}\\n\\n.apexcharts-pie-label-delay {\\n  opacity: 0;\\n  animation-name: opaque;\\n  animation-duration: .3s;\\n  animation-fill-mode: forwards;\\n  animation-timing-function: ease\\n}\\n\\n.apexcharts-legend {\\t\\n  display: flex;\\t\\n  overflow: auto;\\t\\n  padding: 0 10px;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom, .apexcharts-legend.apx-legend-position-top {\\t\\n  flex-wrap: wrap\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  flex-direction: column;\\t\\n  bottom: 0;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-left, .apexcharts-legend.apx-legend-position-top.apexcharts-align-left, .apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  justify-content: flex-start;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-center, .apexcharts-legend.apx-legend-position-top.apexcharts-align-center {\\t\\n  justify-content: center;  \\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-right, .apexcharts-legend.apx-legend-position-top.apexcharts-align-right {\\t\\n  justify-content: flex-end;\\t\\n}\\t\\n.apexcharts-legend-series {\\t\\n  cursor: pointer;\\t\\n  line-height: normal;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom .apexcharts-legend-series, .apexcharts-legend.apx-legend-position-top .apexcharts-legend-series{\\t\\n  display: flex;\\t\\n  align-items: center;\\t\\n}\\t\\n.apexcharts-legend-text {\\t\\n  position: relative;\\t\\n  font-size: 14px;\\t\\n}\\t\\n.apexcharts-legend-text *, .apexcharts-legend-marker * {\\t\\n  pointer-events: none;\\t\\n}\\t\\n.apexcharts-legend-marker {\\t\\n  position: relative;\\t\\n  display: inline-block;\\t\\n  cursor: pointer;\\t\\n  margin-right: 3px;\\t\\n  border-style: solid;\\n}\\t\\n  \\n.apexcharts-legend.apexcharts-align-right .apexcharts-legend-series, .apexcharts-legend.apexcharts-align-left .apexcharts-legend-series{\\t\\n  display: inline-block;\\t\\n}\\t\\n.apexcharts-legend-series.apexcharts-no-click {\\t\\n  cursor: auto;\\t\\n}\\t\\n.apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {\\t\\n  display: none !important;\\t\\n}\\t\\n.apexcharts-inactive-legend {\\t\\n  opacity: 0.45;\\t\\n}\\n\\n.apexcharts-annotation-rect,.apexcharts-area-series .apexcharts-area,.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-gridline,.apexcharts-line,.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-point-annotation-label,.apexcharts-radar-series path,.apexcharts-radar-series polygon,.apexcharts-toolbar svg,.apexcharts-tooltip .apexcharts-marker,.apexcharts-xaxis-annotation-label,.apexcharts-yaxis-annotation-label,.apexcharts-zoom-rect {\\n  pointer-events: none\\n}\\n\\n.apexcharts-marker {\\n  transition: .15s ease all\\n}\\n\\n.resize-triggers {\\n  animation: 1ms resizeanim;\\n  visibility: hidden;\\n  opacity: 0;\\n  height: 100%;\\n  width: 100%;\\n  overflow: hidden\\n}\\n\\n.contract-trigger:before,.resize-triggers,.resize-triggers>div {\\n  content: \" \";\\n  display: block;\\n  position: absolute;\\n  top: 0;\\n  left: 0\\n}\\n\\n.resize-triggers>div {\\n  height: 100%;\\n  width: 100%;\\n  background: #eee;\\n  overflow: auto\\n}\\n\\n.contract-trigger:before {\\n  overflow: hidden;\\n  width: 200%;\\n  height: 200%\\n}\\n',r?s.prepend(t.css):o.head.appendChild(t.css))}var l=t.create(t.w.config.series,{});if(!l)return e(t);t.mount(l).then((function(){\"function\"==typeof t.w.config.chart.events.mounted&&t.w.config.chart.events.mounted(t,t.w),t.events.fireEvent(\"mounted\",[t,t.w]),e(l)})).catch((function(t){i(t)}))}else i(new Error(\"Element not found\"))}))}},{key:\"create\",value:function(t,e){var i=this.w;new Ht(this).initModules();var a=this.w.globals;(a.noData=!1,a.animationEnded=!1,this.responsive.checkResponsiveConfig(e),i.config.xaxis.convertedCatToNumeric)&&new X(i.config).convertCatToNumericXaxis(i.config,this.ctx);if(null===this.el)return a.animationEnded=!0,null;if(this.core.setupElements(),\"treemap\"===i.config.chart.type&&(i.config.grid.show=!1,i.config.yaxis[0].show=!1),0===a.svgWidth)return a.animationEnded=!0,null;var s=y.checkComboSeries(t);a.comboCharts=s.comboCharts,a.comboBarCount=s.comboBarCount;var r=t.every((function(t){return t.data&&0===t.data.length}));(0===t.length||r)&&this.series.handleNoData(),this.events.setupEventHandlers(),this.data.parseData(t),this.theme.init(),new D(this).setGlobalMarkerSize(),this.formatters.setLabelFormatters(),this.titleSubtitle.draw(),a.noData&&a.collapsedSeries.length!==a.series.length&&!i.config.legend.showForSingleSeries||this.legend.init(),this.series.hasAllSeriesEqualX(),a.axisCharts&&(this.core.coreCalculations(),\"category\"!==i.config.xaxis.type&&this.formatters.setLabelFormatters(),this.ctx.toolbar.minX=i.globals.minX,this.ctx.toolbar.maxX=i.globals.maxX),this.formatters.heatmapLabelFormatters(),new y(this).getLargestMarkerSize(),this.dimensions.plotCoords();var o=this.core.xySettings();this.grid.createGridMask();var n=this.core.plotChartType(t,o),l=new O(this);l.bringForward(),i.config.dataLabels.background.enabled&&l.dataLabelsBackground(),this.core.shiftGraphPosition();var h={plot:{left:i.globals.translateX,top:i.globals.translateY,width:i.globals.gridWidth,height:i.globals.gridHeight}};return{elGraph:n,xyRatios:o,elInner:i.globals.dom.elGraphical,dimensions:h}}},{key:\"mount\",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=this,a=i.w;return new Promise((function(s,r){if(null===i.el)return r(new Error(\"Not enough data to display or target element not found\"));(null===e||a.globals.allSeriesCollapsed)&&i.series.handleNoData(),i.grid=new j(i);var o=i.grid.drawGrid();if(i.annotations=new P(i),i.annotations.drawImageAnnos(),i.annotations.drawTextAnnos(),\"back\"===a.config.grid.position&&o&&(a.globals.dom.elGraphical.add(o.el),o&&o.elGridBorders&&o.elGridBorders.node&&a.globals.dom.elGraphical.add(o.elGridBorders)),Array.isArray(e.elGraph))for(var n=0;n<e.elGraph.length;n++)a.globals.dom.elGraphical.add(e.elGraph[n]);else a.globals.dom.elGraphical.add(e.elGraph);\"front\"===a.config.grid.position&&o&&(a.globals.dom.elGraphical.add(o.el),o&&o.elGridBorders&&o.elGridBorders.node&&a.globals.dom.elGraphical.add(o.elGridBorders)),\"front\"===a.config.xaxis.crosshairs.position&&i.crosshairs.drawXCrosshairs(),\"front\"===a.config.yaxis[0].crosshairs.position&&i.crosshairs.drawYCrosshairs(),\"treemap\"!==a.config.chart.type&&i.axes.drawAxis(a.config.chart.type,o);var l=new V(t.ctx,o),h=new q(t.ctx,o);if(null!==o&&(l.xAxisLabelCorrections(o.xAxisTickWidth),h.setYAxisTextAlignments(),a.config.yaxis.map((function(t,e){-1===a.globals.ignoreYAxisIndexes.indexOf(e)&&h.yAxisTitleRotate(e,t.opposite)}))),a.globals.dom.Paper.add(a.globals.dom.elAnnotations),i.annotations.drawAxesAnnotations(),!a.globals.noData){if(a.config.tooltip.enabled&&!a.globals.noData&&i.w.globals.tooltip.drawTooltip(e.xyRatios),a.globals.axisCharts&&(a.globals.isXNumeric||a.config.xaxis.convertedCatToNumeric||a.globals.isRangeBar))(a.config.chart.zoom.enabled||a.config.chart.selection&&a.config.chart.selection.enabled||a.config.chart.pan&&a.config.chart.pan.enabled)&&i.zoomPanSelection.init({xyRatios:e.xyRatios});else{var c=a.config.chart.toolbar.tools;[\"zoom\",\"zoomin\",\"zoomout\",\"selection\",\"pan\",\"reset\"].forEach((function(t){c[t]=!1}))}a.config.chart.toolbar.show&&!a.globals.allSeriesCollapsed&&i.toolbar.createToolbar()}a.globals.memory.methodsToExec.length>0&&a.globals.memory.methodsToExec.forEach((function(t){t.method(t.params,!1,t.context)})),a.globals.axisCharts||a.globals.noData||i.core.resizeNonAxisCharts(),s(i)}))}},{key:\"destroy\",value:function(){var t,e;window.removeEventListener(\"resize\",this.windowResizeHandler),this.el.parentNode,t=this.parentResizeHandler,(e=Nt.get(t))&&(e.disconnect(),Nt.delete(t));var i=this.w.config.chart.id;i&&Apex._chartInstances.forEach((function(t,e){t.id===x.escapeString(i)&&Apex._chartInstances.splice(e,1)})),new Ot(this.ctx).clear({isUpdating:!1})}},{key:\"updateOptions\",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],a=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],s=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],o=this.w;return o.globals.selection=void 0,t.series&&(this.series.resetSeries(!1,!0,!1),t.series.length&&t.series[0].data&&(t.series=t.series.map((function(t,i){return e.updateHelpers._extendSeries(t,i)}))),this.updateHelpers.revertDefaultAxisMinMax()),t.xaxis&&(t=this.updateHelpers.forceXAxisUpdate(t)),t.yaxis&&(t=this.updateHelpers.forceYAxisUpdate(t)),o.globals.collapsedSeriesIndices.length>0&&this.series.clearPreviousPaths(),t.theme&&(t=this.theme.updateThemeOptions(t)),this.updateHelpers._updateOptions(t,i,a,s,r)}},{key:\"updateSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(t,e,i)}},{key:\"appendSeries\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w.config.series.slice();return a.push(t),this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(a,e,i)}},{key:\"appendData\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this;i.w.globals.dataChanged=!0,i.series.getPreviousPaths();for(var a=i.w.config.series.slice(),s=0;s<a.length;s++)if(null!==t[s]&&void 0!==t[s])for(var r=0;r<t[s].data.length;r++)a[s].data.push(t[s].data[r]);return i.w.config.series=a,e&&(i.w.globals.initialSeries=x.clone(i.w.config.series)),this.update()}},{key:\"update\",value:function(t){var e=this;return new Promise((function(i,a){new Ot(e.ctx).clear({isUpdating:!0});var s=e.create(e.w.config.series,t);if(!s)return i(e);e.mount(s).then((function(){\"function\"==typeof e.w.config.chart.events.updated&&e.w.config.chart.events.updated(e,e.w),e.events.fireEvent(\"updated\",[e,e.w]),e.w.globals.isDirty=!0,i(e)})).catch((function(t){a(t)}))}))}},{key:\"getSyncedCharts\",value:function(){var t=this.getGroupedCharts(),e=[this];return t.length&&(e=[],t.forEach((function(t){e.push(t)}))),e}},{key:\"getGroupedCharts\",value:function(){var t=this;return Apex._chartInstances.filter((function(t){if(t.group)return!0})).map((function(e){return t.w.config.chart.group===e.group?e.chart:t}))}},{key:\"toggleSeries\",value:function(t){return this.series.toggleSeries(t)}},{key:\"highlightSeriesOnLegendHover\",value:function(t,e){return this.series.toggleSeriesOnHover(t,e)}},{key:\"showSeries\",value:function(t){this.series.showSeries(t)}},{key:\"hideSeries\",value:function(t){this.series.hideSeries(t)}},{key:\"resetSeries\",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.series.resetSeries(t,e)}},{key:\"addEventListener\",value:function(t,e){this.events.addEventListener(t,e)}},{key:\"removeEventListener\",value:function(t,e){this.events.removeEventListener(t,e)}},{key:\"addXaxisAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addXaxisAnnotationExternal(t,e,a)}},{key:\"addYaxisAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addYaxisAnnotationExternal(t,e,a)}},{key:\"addPointAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addPointAnnotationExternal(t,e,a)}},{key:\"clearAnnotations\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,e=this;t&&(e=t),e.annotations.clearAnnotations(e)}},{key:\"removeAnnotation\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,i=this;e&&(i=e),i.annotations.removeAnnotation(i,t)}},{key:\"getChartArea\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-inner\")}},{key:\"getSeriesTotalXRange\",value:function(t,e){return this.coreUtils.getSeriesTotalsXRange(t,e)}},{key:\"getHighestValueInSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new U(this.ctx);return e.getMinYMaxY(t).highestY}},{key:\"getLowestValueInSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new U(this.ctx);return e.getMinYMaxY(t).lowestY}},{key:\"getSeriesTotal\",value:function(){return this.w.globals.seriesTotals}},{key:\"toggleDataPointSelection\",value:function(t,e){return this.updateHelpers.toggleDataPointSelection(t,e)}},{key:\"zoomX\",value:function(t,e){this.ctx.toolbar.zoomUpdateOptions(t,e)}},{key:\"setLocale\",value:function(t){this.localization.setCurrentLocaleValues(t)}},{key:\"dataURI\",value:function(t){return new G(this.ctx).dataURI(t)}},{key:\"exportToCSV\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=new G(this.ctx);return e.exportToCSV(t)}},{key:\"paper\",value:function(){return this.w.globals.dom.Paper}},{key:\"_parentResizeCallback\",value:function(){this.w.globals.animationEnded&&this.w.config.chart.redrawOnParentResize&&this._windowResize()}},{key:\"_windowResize\",value:function(){var t=this;clearTimeout(this.w.globals.resizeTimer),this.w.globals.resizeTimer=window.setTimeout((function(){t.w.globals.resized=!0,t.w.globals.dataChanged=!1,t.ctx.update()}),150)}},{key:\"_windowResizeHandler\",value:function(){var t=this.w.config.chart.redrawOnWindowResize;\"function\"==typeof t&&(t=t()),t&&this._windowResize()}}],[{key:\"getChartByID\",value:function(t){var e=x.escapeString(t),i=Apex._chartInstances.filter((function(t){return t.id===e}))[0];return i&&i.chart}},{key:\"initOnLoad\",value:function(){for(var e=document.querySelectorAll(\"[data-apexcharts]\"),i=0;i<e.length;i++){new t(e[i],JSON.parse(e[i].getAttribute(\"data-options\"))).render()}}},{key:\"exec\",value:function(t,e){var i=this.getChartByID(t);if(i){i.w.globals.isExecCalled=!0;var a=null;if(-1!==i.publicMethods.indexOf(e)){for(var s=arguments.length,r=new Array(s>2?s-2:0),o=2;o<s;o++)r[o-2]=arguments[o];a=i[e].apply(i,r)}return a}}},{key:\"merge\",value:function(t,e){return x.extend(t,e)}}]),t}();module.exports=Wt;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/apexcharts.css",
    "content": "@keyframes opaque {\n  0% {\n      opacity: 0\n  }\n\n  to {\n      opacity: 1\n  }\n}\n\n@keyframes resizeanim {\n  0%,to {\n      opacity: 0\n  }\n}\n\n.apexcharts-canvas {\n  position: relative;\n  user-select: none\n}\n\n.apexcharts-canvas ::-webkit-scrollbar {\n  -webkit-appearance: none;\n  width: 6px\n}\n\n.apexcharts-canvas ::-webkit-scrollbar-thumb {\n  border-radius: 4px;\n  background-color: rgba(0,0,0,.5);\n  box-shadow: 0 0 1px rgba(255,255,255,.5);\n  -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5)\n}\n\n.apexcharts-inner {\n  position: relative\n}\n\n.apexcharts-text tspan {\n  font-family: inherit\n}\n\n.legend-mouseover-inactive {\n  transition: .15s ease all;\n  opacity: .2\n}\n\n.apexcharts-legend-text {\n  padding-left: 15px;\n  margin-left: -15px;\n}\n\n.apexcharts-series-collapsed {\n  opacity: 0\n}\n\n.apexcharts-tooltip {\n  border-radius: 5px;\n  box-shadow: 2px 2px 6px -4px #999;\n  cursor: default;\n  font-size: 14px;\n  left: 62px;\n  opacity: 0;\n  pointer-events: none;\n  position: absolute;\n  top: 20px;\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n  white-space: nowrap;\n  z-index: 12;\n  transition: .15s ease all\n}\n\n.apexcharts-tooltip.apexcharts-active {\n  opacity: 1;\n  transition: .15s ease all\n}\n\n.apexcharts-tooltip.apexcharts-theme-light {\n  border: 1px solid #e3e3e3;\n  background: rgba(255,255,255,.96)\n}\n\n.apexcharts-tooltip.apexcharts-theme-dark {\n  color: #fff;\n  background: rgba(30,30,30,.8)\n}\n\n.apexcharts-tooltip * {\n  font-family: inherit\n}\n\n.apexcharts-tooltip-title {\n  padding: 6px;\n  font-size: 15px;\n  margin-bottom: 4px\n}\n\n.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {\n  background: #eceff1;\n  border-bottom: 1px solid #ddd\n}\n\n.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {\n  background: rgba(0,0,0,.7);\n  border-bottom: 1px solid #333\n}\n\n.apexcharts-tooltip-text-goals-value,.apexcharts-tooltip-text-y-value,.apexcharts-tooltip-text-z-value {\n  display: inline-block;\n  margin-left: 5px;\n  font-weight: 600\n}\n\n.apexcharts-tooltip-text-goals-label:empty,.apexcharts-tooltip-text-goals-value:empty,.apexcharts-tooltip-text-y-label:empty,.apexcharts-tooltip-text-y-value:empty,.apexcharts-tooltip-text-z-value:empty,.apexcharts-tooltip-title:empty {\n  display: none\n}\n\n.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\n  padding: 6px 0 5px\n}\n\n.apexcharts-tooltip-goals-group,.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\n  display: flex\n}\n\n.apexcharts-tooltip-text-goals-label:not(:empty),.apexcharts-tooltip-text-goals-value:not(:empty) {\n  margin-top: -6px\n}\n\n.apexcharts-tooltip-marker {\n  width: 12px;\n  height: 12px;\n  position: relative;\n  top: 0;\n  margin-right: 10px;\n  border-radius: 50%\n}\n\n.apexcharts-tooltip-series-group {\n  padding: 0 10px;\n  display: none;\n  text-align: left;\n  justify-content: left;\n  align-items: center\n}\n\n.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {\n  opacity: 1\n}\n\n.apexcharts-tooltip-series-group.apexcharts-active,.apexcharts-tooltip-series-group:last-child {\n  padding-bottom: 4px\n}\n\n.apexcharts-tooltip-series-group-hidden {\n  opacity: 0;\n  height: 0;\n  line-height: 0;\n  padding: 0!important\n}\n\n.apexcharts-tooltip-y-group {\n  padding: 6px 0 5px\n}\n\n.apexcharts-custom-tooltip,.apexcharts-tooltip-box {\n  padding: 4px 8px\n}\n\n.apexcharts-tooltip-boxPlot {\n  display: flex;\n  flex-direction: column-reverse\n}\n\n.apexcharts-tooltip-box>div {\n  margin: 4px 0\n}\n\n.apexcharts-tooltip-box span.value {\n  font-weight: 700\n}\n\n.apexcharts-tooltip-rangebar {\n  padding: 5px 8px\n}\n\n.apexcharts-tooltip-rangebar .category {\n  font-weight: 600;\n  color: #777\n}\n\n.apexcharts-tooltip-rangebar .series-name {\n  font-weight: 700;\n  display: block;\n  margin-bottom: 5px\n}\n\n.apexcharts-xaxistooltip,.apexcharts-yaxistooltip {\n  opacity: 0;\n  pointer-events: none;\n  color: #373d3f;\n  font-size: 13px;\n  text-align: center;\n  border-radius: 2px;\n  position: absolute;\n  z-index: 10;\n  background: #eceff1;\n  border: 1px solid #90a4ae\n}\n\n.apexcharts-xaxistooltip {\n  padding: 9px 10px;\n  transition: .15s ease all\n}\n\n.apexcharts-xaxistooltip.apexcharts-theme-dark {\n  background: rgba(0,0,0,.7);\n  border: 1px solid rgba(0,0,0,.5);\n  color: #fff\n}\n\n.apexcharts-xaxistooltip:after,.apexcharts-xaxistooltip:before {\n  left: 50%;\n  border: solid transparent;\n  content: \" \";\n  height: 0;\n  width: 0;\n  position: absolute;\n  pointer-events: none\n}\n\n.apexcharts-xaxistooltip:after {\n  border-color: transparent;\n  border-width: 6px;\n  margin-left: -6px\n}\n\n.apexcharts-xaxistooltip:before {\n  border-color: transparent;\n  border-width: 7px;\n  margin-left: -7px\n}\n\n.apexcharts-xaxistooltip-bottom:after,.apexcharts-xaxistooltip-bottom:before {\n  bottom: 100%\n}\n\n.apexcharts-xaxistooltip-top:after,.apexcharts-xaxistooltip-top:before {\n  top: 100%\n}\n\n.apexcharts-xaxistooltip-bottom:after {\n  border-bottom-color: #eceff1\n}\n\n.apexcharts-xaxistooltip-bottom:before {\n  border-bottom-color: #90a4ae\n}\n\n.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {\n  border-bottom-color: rgba(0,0,0,.5)\n}\n\n.apexcharts-xaxistooltip-top:after {\n  border-top-color: #eceff1\n}\n\n.apexcharts-xaxistooltip-top:before {\n  border-top-color: #90a4ae\n}\n\n.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {\n  border-top-color: rgba(0,0,0,.5)\n}\n\n.apexcharts-xaxistooltip.apexcharts-active {\n  opacity: 1;\n  transition: .15s ease all\n}\n\n.apexcharts-yaxistooltip {\n  padding: 4px 10px\n}\n\n.apexcharts-yaxistooltip.apexcharts-theme-dark {\n  background: rgba(0,0,0,.7);\n  border: 1px solid rgba(0,0,0,.5);\n  color: #fff\n}\n\n.apexcharts-yaxistooltip:after,.apexcharts-yaxistooltip:before {\n  top: 50%;\n  border: solid transparent;\n  content: \" \";\n  height: 0;\n  width: 0;\n  position: absolute;\n  pointer-events: none\n}\n\n.apexcharts-yaxistooltip:after {\n  border-color: transparent;\n  border-width: 6px;\n  margin-top: -6px\n}\n\n.apexcharts-yaxistooltip:before {\n  border-color: transparent;\n  border-width: 7px;\n  margin-top: -7px\n}\n\n.apexcharts-yaxistooltip-left:after,.apexcharts-yaxistooltip-left:before {\n  left: 100%\n}\n\n.apexcharts-yaxistooltip-right:after,.apexcharts-yaxistooltip-right:before {\n  right: 100%\n}\n\n.apexcharts-yaxistooltip-left:after {\n  border-left-color: #eceff1\n}\n\n.apexcharts-yaxistooltip-left:before {\n  border-left-color: #90a4ae\n}\n\n.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {\n  border-left-color: rgba(0,0,0,.5)\n}\n\n.apexcharts-yaxistooltip-right:after {\n  border-right-color: #eceff1\n}\n\n.apexcharts-yaxistooltip-right:before {\n  border-right-color: #90a4ae\n}\n\n.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {\n  border-right-color: rgba(0,0,0,.5)\n}\n\n.apexcharts-yaxistooltip.apexcharts-active {\n  opacity: 1\n}\n\n.apexcharts-yaxistooltip-hidden {\n  display: none\n}\n\n.apexcharts-xcrosshairs,.apexcharts-ycrosshairs {\n  pointer-events: none;\n  opacity: 0;\n  transition: .15s ease all\n}\n\n.apexcharts-xcrosshairs.apexcharts-active,.apexcharts-ycrosshairs.apexcharts-active {\n  opacity: 1;\n  transition: .15s ease all\n}\n\n.apexcharts-ycrosshairs-hidden {\n  opacity: 0\n}\n\n.apexcharts-selection-rect {\n  cursor: move\n}\n\n.svg_select_boundingRect,.svg_select_points_rot {\n  pointer-events: none;\n  opacity: 0;\n  visibility: hidden\n}\n\n.apexcharts-selection-rect+g .svg_select_boundingRect,.apexcharts-selection-rect+g .svg_select_points_rot {\n  opacity: 0;\n  visibility: hidden\n}\n\n.apexcharts-selection-rect+g .svg_select_points_l,.apexcharts-selection-rect+g .svg_select_points_r {\n  cursor: ew-resize;\n  opacity: 1;\n  visibility: visible\n}\n\n.svg_select_points {\n  fill: #efefef;\n  stroke: #333;\n  rx: 2\n}\n\n.apexcharts-svg.apexcharts-zoomable.hovering-zoom {\n  cursor: crosshair\n}\n\n.apexcharts-svg.apexcharts-zoomable.hovering-pan {\n  cursor: move\n}\n\n.apexcharts-menu-icon,.apexcharts-pan-icon,.apexcharts-reset-icon,.apexcharts-selection-icon,.apexcharts-toolbar-custom-icon,.apexcharts-zoom-icon,.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\n  cursor: pointer;\n  width: 20px;\n  height: 20px;\n  line-height: 24px;\n  color: #6e8192;\n  text-align: center\n}\n\n.apexcharts-menu-icon svg,.apexcharts-reset-icon svg,.apexcharts-zoom-icon svg,.apexcharts-zoomin-icon svg,.apexcharts-zoomout-icon svg {\n  fill: #6e8192\n}\n\n.apexcharts-selection-icon svg {\n  fill: #444;\n  transform: scale(.76)\n}\n\n.apexcharts-theme-dark .apexcharts-menu-icon svg,.apexcharts-theme-dark .apexcharts-pan-icon svg,.apexcharts-theme-dark .apexcharts-reset-icon svg,.apexcharts-theme-dark .apexcharts-selection-icon svg,.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg,.apexcharts-theme-dark .apexcharts-zoom-icon svg,.apexcharts-theme-dark .apexcharts-zoomin-icon svg,.apexcharts-theme-dark .apexcharts-zoomout-icon svg {\n  fill: #f3f4f5\n}\n\n.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg {\n  fill: #008ffb\n}\n\n.apexcharts-theme-light .apexcharts-menu-icon:hover svg,.apexcharts-theme-light .apexcharts-reset-icon:hover svg,.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg {\n  fill: #333\n}\n\n.apexcharts-menu-icon,.apexcharts-selection-icon {\n  position: relative\n}\n\n.apexcharts-reset-icon {\n  margin-left: 5px\n}\n\n.apexcharts-menu-icon,.apexcharts-reset-icon,.apexcharts-zoom-icon {\n  transform: scale(.85)\n}\n\n.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\n  transform: scale(.7)\n}\n\n.apexcharts-zoomout-icon {\n  margin-right: 3px\n}\n\n.apexcharts-pan-icon {\n  transform: scale(.62);\n  position: relative;\n  left: 1px;\n  top: 0\n}\n\n.apexcharts-pan-icon svg {\n  fill: #fff;\n  stroke: #6e8192;\n  stroke-width: 2\n}\n\n.apexcharts-pan-icon.apexcharts-selected svg {\n  stroke: #008ffb\n}\n\n.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {\n  stroke: #333\n}\n\n.apexcharts-toolbar {\n  position: absolute;\n  z-index: 11;\n  max-width: 176px;\n  text-align: right;\n  border-radius: 3px;\n  padding: 0 6px 2px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center\n}\n\n.apexcharts-menu {\n  background: #fff;\n  position: absolute;\n  top: 100%;\n  border: 1px solid #ddd;\n  border-radius: 3px;\n  padding: 3px;\n  right: 10px;\n  opacity: 0;\n  min-width: 110px;\n  transition: .15s ease all;\n  pointer-events: none\n}\n\n.apexcharts-menu.apexcharts-menu-open {\n  opacity: 1;\n  pointer-events: all;\n  transition: .15s ease all\n}\n\n.apexcharts-menu-item {\n  padding: 6px 7px;\n  font-size: 12px;\n  cursor: pointer\n}\n\n.apexcharts-theme-light .apexcharts-menu-item:hover {\n  background: #eee\n}\n\n.apexcharts-theme-dark .apexcharts-menu {\n  background: rgba(0,0,0,.7);\n  color: #fff\n}\n\n@media screen and (min-width:768px) {\n  .apexcharts-canvas:hover .apexcharts-toolbar {\n      opacity: 1\n  }\n}\n\n.apexcharts-canvas .apexcharts-element-hidden,.apexcharts-datalabel.apexcharts-element-hidden,.apexcharts-hide .apexcharts-series-points {\n  opacity: 0\n}\n\n.apexcharts-datalabel,.apexcharts-datalabel-label,.apexcharts-datalabel-value,.apexcharts-datalabels,.apexcharts-pie-label {\n  cursor: default;\n  pointer-events: none\n}\n\n.apexcharts-pie-label-delay {\n  opacity: 0;\n  animation-name: opaque;\n  animation-duration: .3s;\n  animation-fill-mode: forwards;\n  animation-timing-function: ease\n}\n\n.apexcharts-legend {\t\n  display: flex;\t\n  overflow: auto;\t\n  padding: 0 10px;\t\n}\t\n.apexcharts-legend.apx-legend-position-bottom, .apexcharts-legend.apx-legend-position-top {\t\n  flex-wrap: wrap\t\n}\t\n.apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\t\n  flex-direction: column;\t\n  bottom: 0;\t\n}\t\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-left, .apexcharts-legend.apx-legend-position-top.apexcharts-align-left, .apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\t\n  justify-content: flex-start;\t\n}\t\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-center, .apexcharts-legend.apx-legend-position-top.apexcharts-align-center {\t\n  justify-content: center;  \t\n}\t\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-right, .apexcharts-legend.apx-legend-position-top.apexcharts-align-right {\t\n  justify-content: flex-end;\t\n}\t\n.apexcharts-legend-series {\t\n  cursor: pointer;\t\n  line-height: normal;\t\n}\t\n.apexcharts-legend.apx-legend-position-bottom .apexcharts-legend-series, .apexcharts-legend.apx-legend-position-top .apexcharts-legend-series{\t\n  display: flex;\t\n  align-items: center;\t\n}\t\n.apexcharts-legend-text {\t\n  position: relative;\t\n  font-size: 14px;\t\n}\t\n.apexcharts-legend-text *, .apexcharts-legend-marker * {\t\n  pointer-events: none;\t\n}\t\n.apexcharts-legend-marker {\t\n  position: relative;\t\n  display: inline-block;\t\n  cursor: pointer;\t\n  margin-right: 3px;\t\n  border-style: solid;\n}\t\n  \n.apexcharts-legend.apexcharts-align-right .apexcharts-legend-series, .apexcharts-legend.apexcharts-align-left .apexcharts-legend-series{\t\n  display: inline-block;\t\n}\t\n.apexcharts-legend-series.apexcharts-no-click {\t\n  cursor: auto;\t\n}\t\n.apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {\t\n  display: none !important;\t\n}\t\n.apexcharts-inactive-legend {\t\n  opacity: 0.45;\t\n}\n\n.apexcharts-annotation-rect,.apexcharts-area-series .apexcharts-area,.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-gridline,.apexcharts-line,.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-point-annotation-label,.apexcharts-radar-series path,.apexcharts-radar-series polygon,.apexcharts-toolbar svg,.apexcharts-tooltip .apexcharts-marker,.apexcharts-xaxis-annotation-label,.apexcharts-yaxis-annotation-label,.apexcharts-zoom-rect {\n  pointer-events: none\n}\n\n.apexcharts-marker {\n  transition: .15s ease all\n}\n\n.resize-triggers {\n  animation: 1ms resizeanim;\n  visibility: hidden;\n  opacity: 0;\n  height: 100%;\n  width: 100%;\n  overflow: hidden\n}\n\n.contract-trigger:before,.resize-triggers,.resize-triggers>div {\n  content: \" \";\n  display: block;\n  position: absolute;\n  top: 0;\n  left: 0\n}\n\n.resize-triggers>div {\n  height: 100%;\n  width: 100%;\n  background: #eee;\n  overflow: auto\n}\n\n.contract-trigger:before {\n  overflow: hidden;\n  width: 200%;\n  height: 200%\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/apexcharts.esm.js",
    "content": "/*!\n * ApexCharts v3.37.1\n * (c) 2018-2023 ApexCharts\n * Released under the MIT License.\n */\nfunction t(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function e(e){for(var i=1;i<arguments.length;i++){var a=null!=arguments[i]?arguments[i]:{};i%2?t(Object(a),!0).forEach((function(t){o(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):t(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function i(t){return i=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},i(t)}function a(t,e){if(!(t instanceof e))throw new TypeError(\"Cannot call a class as a function\")}function s(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,\"value\"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function r(t,e,i){return e&&s(t.prototype,e),i&&s(t,i),t}function o(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function n(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function\");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&h(t,e)}function l(t){return l=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},l(t)}function h(t,e){return h=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},h(t,e)}function c(t,e){if(e&&(\"object\"==typeof e||\"function\"==typeof e))return e;if(void 0!==e)throw new TypeError(\"Derived constructors may only return object or undefined\");return function(t){if(void 0===t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return t}(t)}function d(t){var e=function(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,a=l(t);if(e){var s=l(this).constructor;i=Reflect.construct(a,arguments,s)}else i=a.apply(this,arguments);return c(this,i)}}function g(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var i=null==t?null:\"undefined\"!=typeof Symbol&&t[Symbol.iterator]||t[\"@@iterator\"];if(null==i)return;var a,s,r=[],o=!0,n=!1;try{for(i=i.call(t);!(o=(a=i.next()).done)&&(r.push(a.value),!e||r.length!==e);o=!0);}catch(t){n=!0,s=t}finally{try{o||null==i.return||i.return()}finally{if(n)throw s}}return r}(t,e)||f(t,e)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function u(t){return function(t){if(Array.isArray(t))return p(t)}(t)||function(t){if(\"undefined\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\"@@iterator\"])return Array.from(t)}(t)||f(t)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\")}()}function f(t,e){if(t){if(\"string\"==typeof t)return p(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return\"Object\"===i&&t.constructor&&(i=t.constructor.name),\"Map\"===i||\"Set\"===i?Array.from(t):\"Arguments\"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?p(t,e):void 0}}function p(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}var x=function(){function t(){a(this,t)}return r(t,[{key:\"shadeRGBColor\",value:function(t,e){var i=e.split(\",\"),a=t<0?0:255,s=t<0?-1*t:t,r=parseInt(i[0].slice(4),10),o=parseInt(i[1],10),n=parseInt(i[2],10);return\"rgb(\"+(Math.round((a-r)*s)+r)+\",\"+(Math.round((a-o)*s)+o)+\",\"+(Math.round((a-n)*s)+n)+\")\"}},{key:\"shadeHexColor\",value:function(t,e){var i=parseInt(e.slice(1),16),a=t<0?0:255,s=t<0?-1*t:t,r=i>>16,o=i>>8&255,n=255&i;return\"#\"+(16777216+65536*(Math.round((a-r)*s)+r)+256*(Math.round((a-o)*s)+o)+(Math.round((a-n)*s)+n)).toString(16).slice(1)}},{key:\"shadeColor\",value:function(e,i){return t.isColorHex(i)?this.shadeHexColor(e,i):this.shadeRGBColor(e,i)}}],[{key:\"bind\",value:function(t,e){return function(){return t.apply(e,arguments)}}},{key:\"isObject\",value:function(t){return t&&\"object\"===i(t)&&!Array.isArray(t)&&null!=t}},{key:\"is\",value:function(t,e){return Object.prototype.toString.call(e)===\"[object \"+t+\"]\"}},{key:\"listToArray\",value:function(t){var e,i=[];for(e=0;e<t.length;e++)i[e]=t[e];return i}},{key:\"extend\",value:function(t,e){var i=this;\"function\"!=typeof Object.assign&&(Object.assign=function(t){if(null==t)throw new TypeError(\"Cannot convert undefined or null to object\");for(var e=Object(t),i=1;i<arguments.length;i++){var a=arguments[i];if(null!=a)for(var s in a)a.hasOwnProperty(s)&&(e[s]=a[s])}return e});var a=Object.assign({},t);return this.isObject(t)&&this.isObject(e)&&Object.keys(e).forEach((function(s){i.isObject(e[s])&&s in t?a[s]=i.extend(t[s],e[s]):Object.assign(a,o({},s,e[s]))})),a}},{key:\"extendArray\",value:function(e,i){var a=[];return e.map((function(e){a.push(t.extend(i,e))})),e=a}},{key:\"monthMod\",value:function(t){return t%12}},{key:\"clone\",value:function(e){if(t.is(\"Array\",e)){for(var a=[],s=0;s<e.length;s++)a[s]=this.clone(e[s]);return a}if(t.is(\"Null\",e))return null;if(t.is(\"Date\",e))return e;if(\"object\"===i(e)){var r={};for(var o in e)e.hasOwnProperty(o)&&(r[o]=this.clone(e[o]));return r}return e}},{key:\"log10\",value:function(t){return Math.log(t)/Math.LN10}},{key:\"roundToBase10\",value:function(t){return Math.pow(10,Math.floor(Math.log10(t)))}},{key:\"roundToBase\",value:function(t,e){return Math.pow(e,Math.floor(Math.log(t)/Math.log(e)))}},{key:\"parseNumber\",value:function(t){return null===t?t:parseFloat(t)}},{key:\"randomId\",value:function(){return(Math.random()+1).toString(36).substring(4)}},{key:\"noExponents\",value:function(t){var e=String(t).split(/[eE]/);if(1===e.length)return e[0];var i=\"\",a=t<0?\"-\":\"\",s=e[0].replace(\".\",\"\"),r=Number(e[1])+1;if(r<0){for(i=a+\"0.\";r++;)i+=\"0\";return i+s.replace(/^-/,\"\")}for(r-=s.length;r--;)i+=\"0\";return s+i}},{key:\"getDimensions\",value:function(t){var e=getComputedStyle(t,null),i=t.clientHeight,a=t.clientWidth;return i-=parseFloat(e.paddingTop)+parseFloat(e.paddingBottom),[a-=parseFloat(e.paddingLeft)+parseFloat(e.paddingRight),i]}},{key:\"getBoundingClientRect\",value:function(t){var e=t.getBoundingClientRect();return{top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:t.clientWidth,height:t.clientHeight,x:e.left,y:e.top}}},{key:\"getLargestStringFromArr\",value:function(t){return t.reduce((function(t,e){return Array.isArray(e)&&(e=e.reduce((function(t,e){return t.length>e.length?t:e}))),t.length>e.length?t:e}),0)}},{key:\"hexToRgba\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"#999999\",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.6;\"#\"!==t.substring(0,1)&&(t=\"#999999\");var i=t.replace(\"#\",\"\");i=i.match(new RegExp(\"(.{\"+i.length/3+\"})\",\"g\"));for(var a=0;a<i.length;a++)i[a]=parseInt(1===i[a].length?i[a]+i[a]:i[a],16);return void 0!==e&&i.push(e),\"rgba(\"+i.join(\",\")+\")\"}},{key:\"getOpacityFromRGBA\",value:function(t){return parseFloat(t.replace(/^.*,(.+)\\)/,\"$1\"))}},{key:\"rgb2hex\",value:function(t){return(t=t.match(/^rgba?[\\s+]?\\([\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?/i))&&4===t.length?\"#\"+(\"0\"+parseInt(t[1],10).toString(16)).slice(-2)+(\"0\"+parseInt(t[2],10).toString(16)).slice(-2)+(\"0\"+parseInt(t[3],10).toString(16)).slice(-2):\"\"}},{key:\"isColorHex\",value:function(t){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i.test(t)}},{key:\"getPolygonPos\",value:function(t,e){for(var i=[],a=2*Math.PI/e,s=0;s<e;s++){var r={};r.x=t*Math.sin(s*a),r.y=-t*Math.cos(s*a),i.push(r)}return i}},{key:\"polarToCartesian\",value:function(t,e,i,a){var s=(a-90)*Math.PI/180;return{x:t+i*Math.cos(s),y:e+i*Math.sin(s)}}},{key:\"escapeString\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"x\",i=t.toString().slice();return i=i.replace(/[` ~!@#$%^&*()|+\\=?;:'\",.<>{}[\\]\\\\/]/gi,e)}},{key:\"negToZero\",value:function(t){return t<0?0:t}},{key:\"moveIndexInArray\",value:function(t,e,i){if(i>=t.length)for(var a=i-t.length+1;a--;)t.push(void 0);return t.splice(i,0,t.splice(e,1)[0]),t}},{key:\"extractNumber\",value:function(t){return parseFloat(t.replace(/[^\\d.]*/g,\"\"))}},{key:\"findAncestor\",value:function(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}},{key:\"setELstyles\",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&(t.style.key=e[i])}},{key:\"isNumber\",value:function(t){return!isNaN(t)&&parseFloat(Number(t))===t&&!isNaN(parseInt(t,10))}},{key:\"isFloat\",value:function(t){return Number(t)===t&&t%1!=0}},{key:\"isSafari\",value:function(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}},{key:\"isFirefox\",value:function(){return navigator.userAgent.toLowerCase().indexOf(\"firefox\")>-1}},{key:\"isIE11\",value:function(){if(-1!==window.navigator.userAgent.indexOf(\"MSIE\")||window.navigator.appVersion.indexOf(\"Trident/\")>-1)return!0}},{key:\"isIE\",value:function(){var t=window.navigator.userAgent,e=t.indexOf(\"MSIE \");if(e>0)return parseInt(t.substring(e+5,t.indexOf(\".\",e)),10);if(t.indexOf(\"Trident/\")>0){var i=t.indexOf(\"rv:\");return parseInt(t.substring(i+3,t.indexOf(\".\",i)),10)}var a=t.indexOf(\"Edge/\");return a>0&&parseInt(t.substring(a+5,t.indexOf(\".\",a)),10)}}]),t}(),b=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.setEasingFunctions()}return r(t,[{key:\"setEasingFunctions\",value:function(){var t;if(!this.w.globals.easing){switch(this.w.config.chart.animations.easing){case\"linear\":t=\"-\";break;case\"easein\":t=\"<\";break;case\"easeout\":t=\">\";break;case\"easeinout\":default:t=\"<>\";break;case\"swing\":t=function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1};break;case\"bounce\":t=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375};break;case\"elastic\":t=function(t){return t===!!t?t:Math.pow(2,-10*t)*Math.sin((t-.075)*(2*Math.PI)/.3)+1}}this.w.globals.easing=t}}},{key:\"animateLine\",value:function(t,e,i,a){t.attr(e).animate(a).attr(i)}},{key:\"animateMarker\",value:function(t,e,i,a,s,r){e||(e=0),t.attr({r:e,width:e,height:e}).animate(a,s).attr({r:i,width:i.width,height:i.height}).afterAll((function(){r()}))}},{key:\"animateCircle\",value:function(t,e,i,a,s){t.attr({r:e.r,cx:e.cx,cy:e.cy}).animate(a,s).attr({r:i.r,cx:i.cx,cy:i.cy})}},{key:\"animateRect\",value:function(t,e,i,a,s){t.attr(e).animate(a).attr(i).afterAll((function(){return s()}))}},{key:\"animatePathsGradually\",value:function(t){var e=t.el,i=t.realIndex,a=t.j,s=t.fill,r=t.pathFrom,o=t.pathTo,n=t.speed,l=t.delay,h=this.w,c=0;h.config.chart.animations.animateGradually.enabled&&(c=h.config.chart.animations.animateGradually.delay),h.config.chart.animations.dynamicAnimation.enabled&&h.globals.dataChanged&&\"bar\"!==h.config.chart.type&&(c=0),this.morphSVG(e,i,a,\"line\"!==h.config.chart.type||h.globals.comboCharts?s:\"stroke\",r,o,n,l*c)}},{key:\"showDelayedElements\",value:function(){this.w.globals.delayedElements.forEach((function(t){t.el.classList.remove(\"apexcharts-element-hidden\")}))}},{key:\"animationCompleted\",value:function(t){var e=this.w;e.globals.animationEnded||(e.globals.animationEnded=!0,this.showDelayedElements(),\"function\"==typeof e.config.chart.events.animationEnd&&e.config.chart.events.animationEnd(this.ctx,{el:t,w:e}))}},{key:\"morphSVG\",value:function(t,e,i,a,s,r,o,n){var l=this,h=this.w;s||(s=t.attr(\"pathFrom\")),r||(r=t.attr(\"pathTo\"));var c=function(t){return\"radar\"===h.config.chart.type&&(o=1),\"M 0 \".concat(h.globals.gridHeight)};(!s||s.indexOf(\"undefined\")>-1||s.indexOf(\"NaN\")>-1)&&(s=c()),(!r||r.indexOf(\"undefined\")>-1||r.indexOf(\"NaN\")>-1)&&(r=c()),h.globals.shouldAnimate||(o=1),t.plot(s).animate(1,h.globals.easing,n).plot(s).animate(o,h.globals.easing,n).plot(r).afterAll((function(){x.isNumber(i)?i===h.globals.series[h.globals.maxValsInArrayIndex].length-2&&h.globals.shouldAnimate&&l.animationCompleted(t):\"none\"!==a&&h.globals.shouldAnimate&&(!h.globals.comboCharts&&e===h.globals.series.length-1||h.globals.comboCharts)&&l.animationCompleted(t),l.showDelayedElements()}))}}]),t}(),v=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"getDefaultFilter\",value:function(t,e){var i=this.w;t.unfilter(!0),(new window.SVG.Filter).size(\"120%\",\"180%\",\"-5%\",\"-40%\"),\"none\"!==i.config.states.normal.filter?this.applyFilter(t,e,i.config.states.normal.filter.type,i.config.states.normal.filter.value):i.config.chart.dropShadow.enabled&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:\"addNormalFilter\",value:function(t,e){var i=this.w;i.config.chart.dropShadow.enabled&&!t.node.classList.contains(\"apexcharts-marker\")&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:\"addLightenFilter\",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;t.unfilter(!0);new window.SVG.Filter;t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:\"linear\",slope:1.5,intercept:r}})})),t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)}},{key:\"addDarkenFilter\",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;t.unfilter(!0);new window.SVG.Filter;t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:\"linear\",slope:r}})})),t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)}},{key:\"applyFilter\",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:.5;switch(i){case\"none\":this.addNormalFilter(t,e);break;case\"lighten\":this.addLightenFilter(t,e,{intensity:a});break;case\"darken\":this.addDarkenFilter(t,e,{intensity:a})}}},{key:\"addShadow\",value:function(t,e,i){var a=i.blur,s=i.top,r=i.left,o=i.color,n=i.opacity,l=t.flood(Array.isArray(o)?o[e]:o,n).composite(t.sourceAlpha,\"in\").offset(r,s).gaussianBlur(a).merge(t.source);return t.blend(t.source,l)}},{key:\"dropShadow\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=e.top,s=e.left,r=e.blur,o=e.color,n=e.opacity,l=e.noUserSpaceOnUse,h=this.w;return t.unfilter(!0),x.isIE()&&\"radialBar\"===h.config.chart.type||(o=Array.isArray(o)?o[i]:o,t.filter((function(t){var e=null;e=x.isSafari()||x.isFirefox()||x.isIE()?t.flood(o,n).composite(t.sourceAlpha,\"in\").offset(s,a).gaussianBlur(r):t.flood(o,n).composite(t.sourceAlpha,\"in\").offset(s,a).gaussianBlur(r).merge(t.source),t.blend(t.source,e)})),l||t.filterer.node.setAttribute(\"filterUnits\",\"userSpaceOnUse\"),this._scaleFilterSize(t.filterer.node)),t}},{key:\"setSelectionFilter\",value:function(t,e,i){var a=this.w;if(void 0!==a.globals.selectedDataPoints[e]&&a.globals.selectedDataPoints[e].indexOf(i)>-1){t.node.setAttribute(\"selected\",!0);var s=a.config.states.active.filter;\"none\"!==s&&this.applyFilter(t,e,s.type,s.value)}}},{key:\"_scaleFilterSize\",value:function(t){!function(e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}({width:\"200%\",height:\"200%\",x:\"-50%\",y:\"-50%\"})}}]),t}(),m=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"roundPathCorners\",value:function(t,e){function i(t,e,i){var s=e.x-t.x,r=e.y-t.y,o=Math.sqrt(s*s+r*r);return a(t,e,Math.min(1,i/o))}function a(t,e,i){return{x:t.x+(e.x-t.x)*i,y:t.y+(e.y-t.y)*i}}function s(t,e){t.length>2&&(t[t.length-2]=e.x,t[t.length-1]=e.y)}function r(t){return{x:parseFloat(t[t.length-2]),y:parseFloat(t[t.length-1])}}var o=t.split(/[,\\s]/).reduce((function(t,e){var i=e.match(\"([a-zA-Z])(.+)\");return i?(t.push(i[1]),t.push(i[2])):t.push(e),t}),[]).reduce((function(t,e){return parseFloat(e)==e&&t.length?t[t.length-1].push(e):t.push([e]),t}),[]),n=[];if(o.length>1){var l=r(o[0]),h=null;\"Z\"==o[o.length-1][0]&&o[0].length>2&&(h=[\"L\",l.x,l.y],o[o.length-1]=h),n.push(o[0]);for(var c=1;c<o.length;c++){var d=n[n.length-1],g=o[c],u=g==h?o[1]:o[c+1];if(u&&d&&d.length>2&&\"L\"==g[0]&&u.length>2&&\"L\"==u[0]){var f,p,x=r(d),b=r(g),v=r(u);f=i(b,x,e),p=i(b,v,e),s(g,f),g.origPoint=b,n.push(g);var m=a(f,b,.5),y=a(b,p,.5),w=[\"C\",m.x,m.y,y.x,y.y,p.x,p.y];w.origPoint=b,n.push(w)}else n.push(g)}if(h){var k=r(n[n.length-1]);n.push([\"Z\"]),s(n[0],k)}}else n=o;return n.reduce((function(t,e){return t+e.join(\" \")+\" \"}),\"\")}},{key:\"drawLine\",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:\"#a8a8a8\",r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,o=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,n=arguments.length>7&&void 0!==arguments[7]?arguments[7]:\"butt\",l=this.w,h=l.globals.dom.Paper.line().attr({x1:t,y1:e,x2:i,y2:a,stroke:s,\"stroke-dasharray\":r,\"stroke-width\":o,\"stroke-linecap\":n});return h}},{key:\"drawRect\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:\"#fefefe\",o=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1,n=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,l=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,h=arguments.length>9&&void 0!==arguments[9]?arguments[9]:0,c=this.w,d=c.globals.dom.Paper.rect();return d.attr({x:t,y:e,width:i>0?i:0,height:a>0?a:0,rx:s,ry:s,opacity:o,\"stroke-width\":null!==n?n:0,stroke:null!==l?l:\"none\",\"stroke-dasharray\":h}),d.node.setAttribute(\"fill\",r),d}},{key:\"drawPolygon\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"#e1e1e1\",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\"none\",s=this.w,r=s.globals.dom.Paper.polygon(t).attr({fill:a,stroke:e,\"stroke-width\":i});return r}},{key:\"drawCircle\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w;t<0&&(t=0);var a=i.globals.dom.Paper.circle(2*t);return null!==e&&a.attr(e),a}},{key:\"drawPath\",value:function(t){var e=t.d,i=void 0===e?\"\":e,a=t.stroke,s=void 0===a?\"#a8a8a8\":a,r=t.strokeWidth,o=void 0===r?1:r,n=t.fill,l=t.fillOpacity,h=void 0===l?1:l,c=t.strokeOpacity,d=void 0===c?1:c,g=t.classes,u=t.strokeLinecap,f=void 0===u?null:u,p=t.strokeDashArray,x=void 0===p?0:p,b=this.w;return null===f&&(f=b.config.stroke.lineCap),(i.indexOf(\"undefined\")>-1||i.indexOf(\"NaN\")>-1)&&(i=\"M 0 \".concat(b.globals.gridHeight)),b.globals.dom.Paper.path(i).attr({fill:n,\"fill-opacity\":h,stroke:s,\"stroke-opacity\":d,\"stroke-linecap\":f,\"stroke-width\":o,\"stroke-dasharray\":x,class:g})}},{key:\"group\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=e.globals.dom.Paper.group();return null!==t&&i.attr(t),i}},{key:\"move\",value:function(t,e){var i=[\"M\",t,e].join(\" \");return i}},{key:\"line\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=null;return null===i?a=[\" L\",t,e].join(\" \"):\"H\"===i?a=[\" H\",t].join(\" \"):\"V\"===i&&(a=[\" V\",e].join(\" \")),a}},{key:\"curve\",value:function(t,e,i,a,s,r){var o=[\"C\",t,e,i,a,s,r].join(\" \");return o}},{key:\"quadraticCurve\",value:function(t,e,i,a){return[\"Q\",t,e,i,a].join(\" \")}},{key:\"arc\",value:function(t,e,i,a,s,r,o){var n=arguments.length>7&&void 0!==arguments[7]&&arguments[7],l=\"A\";n&&(l=\"a\");var h=[l,t,e,i,a,s,r,o].join(\" \");return h}},{key:\"renderPaths\",value:function(t){var i,a=t.j,s=t.realIndex,r=t.pathFrom,o=t.pathTo,n=t.stroke,l=t.strokeWidth,h=t.strokeLinecap,c=t.fill,d=t.animationDelay,g=t.initialSpeed,u=t.dataChangeSpeed,f=t.className,p=t.shouldClipToGrid,x=void 0===p||p,m=t.bindEventsOnPaths,y=void 0===m||m,w=t.drawShadow,k=void 0===w||w,A=this.w,S=new v(this.ctx),C=new b(this.ctx),L=this.w.config.chart.animations.enabled,P=L&&this.w.config.chart.animations.dynamicAnimation.enabled,T=!!(L&&!A.globals.resized||P&&A.globals.dataChanged&&A.globals.shouldAnimate);T?i=r:(i=o,A.globals.animationEnded=!0);var M=A.config.stroke.dashArray,I=0;I=Array.isArray(M)?M[s]:A.config.stroke.dashArray;var z=this.drawPath({d:i,stroke:n,strokeWidth:l,fill:c,fillOpacity:1,classes:f,strokeLinecap:h,strokeDashArray:I});if(z.attr(\"index\",s),x&&z.attr({\"clip-path\":\"url(#gridRectMask\".concat(A.globals.cuid,\")\")}),\"none\"!==A.config.states.normal.filter.type)S.getDefaultFilter(z,s);else if(A.config.chart.dropShadow.enabled&&k&&(!A.config.chart.dropShadow.enabledOnSeries||A.config.chart.dropShadow.enabledOnSeries&&-1!==A.config.chart.dropShadow.enabledOnSeries.indexOf(s))){var X=A.config.chart.dropShadow;S.dropShadow(z,X,s)}y&&(z.node.addEventListener(\"mouseenter\",this.pathMouseEnter.bind(this,z)),z.node.addEventListener(\"mouseleave\",this.pathMouseLeave.bind(this,z)),z.node.addEventListener(\"mousedown\",this.pathMouseDown.bind(this,z))),z.attr({pathTo:o,pathFrom:r});var E={el:z,j:a,realIndex:s,pathFrom:r,pathTo:o,fill:c,strokeWidth:l,delay:d};return!L||A.globals.resized||A.globals.dataChanged?!A.globals.resized&&A.globals.dataChanged||C.showDelayedElements():C.animatePathsGradually(e(e({},E),{},{speed:g})),A.globals.dataChanged&&P&&T&&C.animatePathsGradually(e(e({},E),{},{speed:u})),z}},{key:\"drawPattern\",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\"#a8a8a8\",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=this.w,o=r.globals.dom.Paper.pattern(e,i,(function(r){\"horizontalLines\"===t?r.line(0,0,i,0).stroke({color:a,width:s+1}):\"verticalLines\"===t?r.line(0,0,0,e).stroke({color:a,width:s+1}):\"slantedLines\"===t?r.line(0,0,e,i).stroke({color:a,width:s}):\"squares\"===t?r.rect(e,i).fill(\"none\").stroke({color:a,width:s}):\"circles\"===t&&r.circle(e).fill(\"none\").stroke({color:a,width:s})}));return o}},{key:\"drawGradient\",value:function(t,e,i,a,s){var r,o=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,n=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,l=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:0,c=this.w;e.length<9&&0===e.indexOf(\"#\")&&(e=x.hexToRgba(e,a)),i.length<9&&0===i.indexOf(\"#\")&&(i=x.hexToRgba(i,s));var d=0,g=1,u=1,f=null;null!==n&&(d=void 0!==n[0]?n[0]/100:0,g=void 0!==n[1]?n[1]/100:1,u=void 0!==n[2]?n[2]/100:1,f=void 0!==n[3]?n[3]/100:null);var p=!(\"donut\"!==c.config.chart.type&&\"pie\"!==c.config.chart.type&&\"polarArea\"!==c.config.chart.type&&\"bubble\"!==c.config.chart.type);if(r=null===l||0===l.length?c.globals.dom.Paper.gradient(p?\"radial\":\"linear\",(function(t){t.at(d,e,a),t.at(g,i,s),t.at(u,i,s),null!==f&&t.at(f,e,a)})):c.globals.dom.Paper.gradient(p?\"radial\":\"linear\",(function(t){(Array.isArray(l[h])?l[h]:l).forEach((function(e){t.at(e.offset/100,e.color,e.opacity)}))})),p){var b=c.globals.gridWidth/2,v=c.globals.gridHeight/2;\"bubble\"!==c.config.chart.type?r.attr({gradientUnits:\"userSpaceOnUse\",cx:b,cy:v,r:o}):r.attr({cx:.5,cy:.5,r:.8,fx:.2,fy:.2})}else\"vertical\"===t?r.from(0,0).to(0,1):\"diagonal\"===t?r.from(0,0).to(1,1):\"horizontal\"===t?r.from(0,1).to(1,1):\"diagonal2\"===t&&r.from(1,0).to(0,1);return r}},{key:\"getTextBasedOnMaxWidth\",value:function(t){var e=t.text,i=t.maxWidth,a=t.fontSize,s=t.fontFamily,r=this.getTextRects(e,a,s),o=r.width/e.length,n=Math.floor(i/o);return i<r.width?e.slice(0,n-3)+\"...\":e}},{key:\"drawText\",value:function(t){var i=this,a=t.x,s=t.y,r=t.text,o=t.textAnchor,n=t.fontSize,l=t.fontFamily,h=t.fontWeight,c=t.foreColor,d=t.opacity,g=t.maxWidth,u=t.cssClass,f=void 0===u?\"\":u,p=t.isPlainText,x=void 0===p||p,b=this.w;void 0===r&&(r=\"\");var v=r;o||(o=\"start\"),c&&c.length||(c=b.config.chart.foreColor),l=l||b.config.chart.fontFamily,h=h||\"regular\";var m,y={maxWidth:g,fontSize:n=n||\"11px\",fontFamily:l};return Array.isArray(r)?m=b.globals.dom.Paper.text((function(t){for(var a=0;a<r.length;a++)v=r[a],g&&(v=i.getTextBasedOnMaxWidth(e({text:r[a]},y))),0===a?t.tspan(v):t.tspan(v).newLine()})):(g&&(v=this.getTextBasedOnMaxWidth(e({text:r},y))),m=x?b.globals.dom.Paper.plain(r):b.globals.dom.Paper.text((function(t){return t.tspan(v)}))),m.attr({x:a,y:s,\"text-anchor\":o,\"dominant-baseline\":\"auto\",\"font-size\":n,\"font-family\":l,\"font-weight\":h,fill:c,class:\"apexcharts-text \"+f}),m.node.style.fontFamily=l,m.node.style.opacity=d,m}},{key:\"drawMarker\",value:function(t,e,i){t=t||0;var a=i.pSize||0,s=null;if(\"square\"===i.shape||\"rect\"===i.shape){var r=void 0===i.pRadius?a/2:i.pRadius;null!==e&&a||(a=0,r=0);var o=1.2*a+r,n=this.drawRect(o,o,o,o,r);n.attr({x:t-o/2,y:e-o/2,cx:t,cy:e,class:i.class?i.class:\"\",fill:i.pointFillColor,\"fill-opacity\":i.pointFillOpacity?i.pointFillOpacity:1,stroke:i.pointStrokeColor,\"stroke-width\":i.pointStrokeWidth?i.pointStrokeWidth:0,\"stroke-opacity\":i.pointStrokeOpacity?i.pointStrokeOpacity:1}),s=n}else\"circle\"!==i.shape&&i.shape||(x.isNumber(e)||(a=0,e=0),s=this.drawCircle(a,{cx:t,cy:e,class:i.class?i.class:\"\",stroke:i.pointStrokeColor,fill:i.pointFillColor,\"fill-opacity\":i.pointFillOpacity?i.pointFillOpacity:1,\"stroke-width\":i.pointStrokeWidth?i.pointStrokeWidth:0,\"stroke-opacity\":i.pointStrokeOpacity?i.pointStrokeOpacity:1}));return s}},{key:\"pathMouseEnter\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10);if(\"function\"==typeof i.config.chart.events.dataPointMouseEnter&&i.config.chart.events.dataPointMouseEnter(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent(\"dataPointMouseEnter\",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),(\"none\"===i.config.states.active.filter.type||\"true\"!==t.node.getAttribute(\"selected\"))&&\"none\"!==i.config.states.hover.filter.type&&!i.globals.isTouchDevice){var o=i.config.states.hover.filter;a.applyFilter(t,s,o.type,o.value)}}},{key:\"pathMouseLeave\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10);\"function\"==typeof i.config.chart.events.dataPointMouseLeave&&i.config.chart.events.dataPointMouseLeave(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent(\"dataPointMouseLeave\",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),\"none\"!==i.config.states.active.filter.type&&\"true\"===t.node.getAttribute(\"selected\")||\"none\"!==i.config.states.hover.filter.type&&a.getDefaultFilter(t,s)}},{key:\"pathMouseDown\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=parseInt(t.node.getAttribute(\"index\"),10),r=parseInt(t.node.getAttribute(\"j\"),10),o=\"false\";if(\"true\"===t.node.getAttribute(\"selected\")){if(t.node.setAttribute(\"selected\",\"false\"),i.globals.selectedDataPoints[s].indexOf(r)>-1){var n=i.globals.selectedDataPoints[s].indexOf(r);i.globals.selectedDataPoints[s].splice(n,1)}}else{if(!i.config.states.active.allowMultipleDataPointsSelection&&i.globals.selectedDataPoints.length>0){i.globals.selectedDataPoints=[];var l=i.globals.dom.Paper.select(\".apexcharts-series path\").members,h=i.globals.dom.Paper.select(\".apexcharts-series circle, .apexcharts-series rect\").members,c=function(t){Array.prototype.forEach.call(t,(function(t){t.node.setAttribute(\"selected\",\"false\"),a.getDefaultFilter(t,s)}))};c(l),c(h)}t.node.setAttribute(\"selected\",\"true\"),o=\"true\",void 0===i.globals.selectedDataPoints[s]&&(i.globals.selectedDataPoints[s]=[]),i.globals.selectedDataPoints[s].push(r)}if(\"true\"===o){var d=i.config.states.active.filter;if(\"none\"!==d)a.applyFilter(t,s,d.type,d.value);else if(\"none\"!==i.config.states.hover.filter&&!i.globals.isTouchDevice){var g=i.config.states.hover.filter;a.applyFilter(t,s,g.type,g.value)}}else if(\"none\"!==i.config.states.active.filter.type)if(\"none\"===i.config.states.hover.filter.type||i.globals.isTouchDevice)a.getDefaultFilter(t,s);else{g=i.config.states.hover.filter;a.applyFilter(t,s,g.type,g.value)}\"function\"==typeof i.config.chart.events.dataPointSelection&&i.config.chart.events.dataPointSelection(e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}),e&&this.ctx.events.fireEvent(\"dataPointSelection\",[e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}])}},{key:\"rotateAroundCenter\",value:function(t){var e={};return t&&\"function\"==typeof t.getBBox&&(e=t.getBBox()),{x:e.x+e.width/2,y:e.y+e.height/2}}},{key:\"getTextRects\",value:function(t,e,i,a){var s=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],r=this.w,o=this.drawText({x:-200,y:-200,text:t,textAnchor:\"start\",fontSize:e,fontFamily:i,foreColor:\"#fff\",opacity:0});a&&o.attr(\"transform\",a),r.globals.dom.Paper.add(o);var n=o.bbox();return s||(n=o.node.getBoundingClientRect()),o.remove(),{width:n.width,height:n.height}}},{key:\"placeTextWithEllipsis\",value:function(t,e,i){if(\"function\"==typeof t.getComputedTextLength&&(t.textContent=e,e.length>0&&t.getComputedTextLength()>=i/1.1)){for(var a=e.length-3;a>0;a-=3)if(t.getSubStringLength(0,a)<=i/1.1)return void(t.textContent=e.substring(0,a)+\"...\");t.textContent=\".\"}}}],[{key:\"setAttrs\",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}}]),t}(),y=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"getStackedSeriesTotals\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=this.w,i=[];if(0===e.globals.series.length)return i;for(var a=0;a<e.globals.series[e.globals.maxValsInArrayIndex].length;a++){for(var s=0,r=0;r<e.globals.series.length;r++)void 0!==e.globals.series[r][a]&&-1===t.indexOf(r)&&(s+=e.globals.series[r][a]);i.push(s)}return i}},{key:\"getSeriesTotalByIndex\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return null===t?this.w.config.series.reduce((function(t,e){return t+e}),0):this.w.globals.series[t].reduce((function(t,e){return t+e}),0)}},{key:\"isSeriesNull\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return 0===(null===t?this.w.config.series.filter((function(t){return null!==t})):this.w.config.series[t].data.filter((function(t){return null!==t}))).length}},{key:\"seriesHaveSameValues\",value:function(t){return this.w.globals.series[t].every((function(t,e,i){return t===i[0]}))}},{key:\"getCategoryLabels\",value:function(t){var e=this.w,i=t.slice();return e.config.xaxis.convertedCatToNumeric&&(i=t.map((function(t,i){return e.config.xaxis.labels.formatter(t-e.globals.minX+1)}))),i}},{key:\"getLargestSeries\",value:function(){var t=this.w;t.globals.maxValsInArrayIndex=t.globals.series.map((function(t){return t.length})).indexOf(Math.max.apply(Math,t.globals.series.map((function(t){return t.length}))))}},{key:\"getLargestMarkerSize\",value:function(){var t=this.w,e=0;return t.globals.markers.size.forEach((function(t){e=Math.max(e,t)})),t.config.markers.discrete&&t.config.markers.discrete.length&&t.config.markers.discrete.forEach((function(t){e=Math.max(e,t.size)})),e>0&&(e+=t.config.markers.hover.sizeOffset+1),t.globals.markers.largestSize=e,e}},{key:\"getSeriesTotals\",value:function(){var t=this.w;t.globals.seriesTotals=t.globals.series.map((function(t,e){var i=0;if(Array.isArray(t))for(var a=0;a<t.length;a++)i+=t[a];else i+=t;return i}))}},{key:\"getSeriesTotalsXRange\",value:function(t,e){var i=this.w;return i.globals.series.map((function(a,s){for(var r=0,o=0;o<a.length;o++)i.globals.seriesX[s][o]>t&&i.globals.seriesX[s][o]<e&&(r+=a[o]);return r}))}},{key:\"getPercentSeries\",value:function(){var t=this.w;t.globals.seriesPercent=t.globals.series.map((function(e,i){var a=[];if(Array.isArray(e))for(var s=0;s<e.length;s++){var r=t.globals.stackedSeriesTotals[s],o=0;r&&(o=100*e[s]/r),a.push(o)}else{var n=100*e/t.globals.seriesTotals.reduce((function(t,e){return t+e}),0);a.push(n)}return a}))}},{key:\"getCalculatedRatios\",value:function(){var t,e,i,a,s=this.w.globals,r=[],o=0,n=[],l=.1,h=0;if(s.yRange=[],s.isMultipleYAxis)for(var c=0;c<s.minYArr.length;c++)s.yRange.push(Math.abs(s.minYArr[c]-s.maxYArr[c])),n.push(0);else s.yRange.push(Math.abs(s.minY-s.maxY));s.xRange=Math.abs(s.maxX-s.minX),s.zRange=Math.abs(s.maxZ-s.minZ);for(var d=0;d<s.yRange.length;d++)r.push(s.yRange[d]/s.gridHeight);if(e=s.xRange/s.gridWidth,i=Math.abs(s.initialMaxX-s.initialMinX)/s.gridWidth,t=s.yRange/s.gridWidth,a=s.xRange/s.gridHeight,(o=s.zRange/s.gridHeight*16)||(o=1),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(s.hasNegs=!0),s.isMultipleYAxis){n=[];for(var g=0;g<r.length;g++)n.push(-s.minYArr[g]/r[g])}else n.push(-s.minY/r[0]),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(l=-s.minY/t,h=s.minX/e);return{yRatio:r,invertedYRatio:t,zRatio:o,xRatio:e,initialXRatio:i,invertedXRatio:a,baseLineInvertedY:l,baseLineY:n,baseLineX:h}}},{key:\"getLogSeries\",value:function(t){var e=this,i=this.w;return i.globals.seriesLog=t.map((function(t,a){return i.config.yaxis[a]&&i.config.yaxis[a].logarithmic?t.map((function(t){return null===t?null:e.getLogVal(i.config.yaxis[a].logBase,t,a)})):t})),i.globals.invalidLogScale?t:i.globals.seriesLog}},{key:\"getBaseLog\",value:function(t,e){return Math.log(e)/Math.log(t)}},{key:\"getLogVal\",value:function(t,e,i){if(0===e)return 0;var a=this.w,s=0===a.globals.minYArr[i]?-1:this.getBaseLog(t,a.globals.minYArr[i]),r=(0===a.globals.maxYArr[i]?0:this.getBaseLog(t,a.globals.maxYArr[i]))-s;return e<1?e/r:(this.getBaseLog(t,e)-s)/r}},{key:\"getLogYRatios\",value:function(t){var e=this,i=this.w,a=this.w.globals;return a.yLogRatio=t.slice(),a.logYRange=a.yRange.map((function(t,s){if(i.config.yaxis[s]&&e.w.config.yaxis[s].logarithmic){var r,o=-Number.MAX_VALUE,n=Number.MIN_VALUE;return a.seriesLog.forEach((function(t,e){t.forEach((function(t){i.config.yaxis[e]&&i.config.yaxis[e].logarithmic&&(o=Math.max(t,o),n=Math.min(t,n))}))})),r=Math.pow(a.yRange[s],Math.abs(n-o)/a.yRange[s]),a.yLogRatio[s]=r/a.gridHeight,r}})),a.invalidLogScale?t.slice():a.yLogRatio}}],[{key:\"checkComboSeries\",value:function(t){var e=!1,i=0,a=0;return t.length&&void 0!==t[0].type&&t.forEach((function(t){\"bar\"!==t.type&&\"column\"!==t.type&&\"candlestick\"!==t.type&&\"boxPlot\"!==t.type||i++,void 0!==t.type&&a++})),a>0&&(e=!0),{comboBarCount:i,comboCharts:e}}},{key:\"extendArrayProps\",value:function(t,e,i){return e.yaxis&&(e=t.extendYAxis(e,i)),e.annotations&&(e.annotations.yaxis&&(e=t.extendYAxisAnnotations(e)),e.annotations.xaxis&&(e=t.extendXAxisAnnotations(e)),e.annotations.points&&(e=t.extendPointAnnotations(e))),e}}]),t}(),w=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e}return r(t,[{key:\"setOrientations\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w;if(\"vertical\"===t.label.orientation){var a=null!==e?e:0,s=i.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-annotations .apexcharts-xaxis-annotation-label[rel='\".concat(a,\"']\"));if(null!==s){var r=s.getBoundingClientRect();s.setAttribute(\"x\",parseFloat(s.getAttribute(\"x\"))-r.height+4),\"top\"===t.label.position?s.setAttribute(\"y\",parseFloat(s.getAttribute(\"y\"))+r.width):s.setAttribute(\"y\",parseFloat(s.getAttribute(\"y\"))-r.width);var o=this.annoCtx.graphics.rotateAroundCenter(s),n=o.x,l=o.y;s.setAttribute(\"transform\",\"rotate(-90 \".concat(n,\" \").concat(l,\")\"))}}}},{key:\"addBackgroundToAnno\",value:function(t,e){var i=this.w;if(!t||void 0===e.label.text||void 0!==e.label.text&&!String(e.label.text).trim())return null;var a=i.globals.dom.baseEl.querySelector(\".apexcharts-grid\").getBoundingClientRect(),s=t.getBoundingClientRect(),r=e.label.style.padding.left,o=e.label.style.padding.right,n=e.label.style.padding.top,l=e.label.style.padding.bottom;\"vertical\"===e.label.orientation&&(n=e.label.style.padding.left,l=e.label.style.padding.right,r=e.label.style.padding.top,o=e.label.style.padding.bottom);var h=s.left-a.left-r,c=s.top-a.top-n,d=this.annoCtx.graphics.drawRect(h-i.globals.barPadForNumericAxis,c,s.width+r+o,s.height+n+l,e.label.borderRadius,e.label.style.background,1,e.label.borderWidth,e.label.borderColor,0);return e.id&&d.node.classList.add(e.id),d}},{key:\"annotationsBackground\",value:function(){var t=this,e=this.w,i=function(i,a,s){var r=e.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations .apexcharts-\").concat(s,\"-annotation-label[rel='\").concat(a,\"']\"));if(r){var o=r.parentNode,n=t.addBackgroundToAnno(r,i);n&&(o.insertBefore(n.node,r),i.label.mouseEnter&&n.node.addEventListener(\"mouseenter\",i.label.mouseEnter.bind(t,i)),i.label.mouseLeave&&n.node.addEventListener(\"mouseleave\",i.label.mouseLeave.bind(t,i)),i.label.click&&n.node.addEventListener(\"click\",i.label.click.bind(t,i)))}};e.config.annotations.xaxis.map((function(t,e){i(t,e,\"xaxis\")})),e.config.annotations.yaxis.map((function(t,e){i(t,e,\"yaxis\")})),e.config.annotations.points.map((function(t,e){i(t,e,\"point\")}))}},{key:\"getY1Y2\",value:function(t,e){var i,a=\"y1\"===t?e.y:e.y2,s=this.w;if(this.annoCtx.invertAxis){var r=s.globals.labels.indexOf(a);s.config.xaxis.convertedCatToNumeric&&(r=s.globals.categoryLabels.indexOf(a));var o=s.globals.dom.baseEl.querySelector(\".apexcharts-yaxis-texts-g text:nth-child(\"+(r+1)+\")\");o&&(i=parseFloat(o.getAttribute(\"y\")))}else{var n;if(s.config.yaxis[e.yAxisIndex].logarithmic)n=(a=new y(this.annoCtx.ctx).getLogVal(a,e.yAxisIndex))/s.globals.yLogRatio[e.yAxisIndex];else n=(a-s.globals.minYArr[e.yAxisIndex])/(s.globals.yRange[e.yAxisIndex]/s.globals.gridHeight);i=s.globals.gridHeight-n,!e.marker||void 0!==e.y&&null!==e.y||(i=0),s.config.yaxis[e.yAxisIndex]&&s.config.yaxis[e.yAxisIndex].reversed&&(i=n)}return\"string\"==typeof a&&a.indexOf(\"px\")>-1&&(i=parseFloat(a)),i}},{key:\"getX1X2\",value:function(t,e){var i=this.w,a=this.annoCtx.invertAxis?i.globals.minY:i.globals.minX,s=this.annoCtx.invertAxis?i.globals.maxY:i.globals.maxX,r=this.annoCtx.invertAxis?i.globals.yRange[0]:i.globals.xRange,o=(e.x-a)/(r/i.globals.gridWidth);this.annoCtx.inversedReversedAxis&&(o=(s-e.x)/(r/i.globals.gridWidth)),\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric||this.annoCtx.invertAxis||i.globals.dataFormatXNumeric||(o=this.getStringX(e.x));var n=(e.x2-a)/(r/i.globals.gridWidth);return this.annoCtx.inversedReversedAxis&&(n=(s-e.x2)/(r/i.globals.gridWidth)),\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric||this.annoCtx.invertAxis||i.globals.dataFormatXNumeric||(n=this.getStringX(e.x2)),void 0!==e.x&&null!==e.x||!e.marker||(o=i.globals.gridWidth),\"x1\"===t&&\"string\"==typeof e.x&&e.x.indexOf(\"px\")>-1&&(o=parseFloat(e.x)),\"x2\"===t&&\"string\"==typeof e.x2&&e.x2.indexOf(\"px\")>-1&&(n=parseFloat(e.x2)),\"x1\"===t?o:n}},{key:\"getStringX\",value:function(t){var e=this.w,i=t;e.config.xaxis.convertedCatToNumeric&&e.globals.categoryLabels.length&&(t=e.globals.categoryLabels.indexOf(t)+1);var a=e.globals.labels.indexOf(t),s=e.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-texts-g text:nth-child(\"+(a+1)+\")\");return s&&(i=parseFloat(s.getAttribute(\"x\"))),i}}]),t}(),k=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e,this.invertAxis=this.annoCtx.invertAxis,this.helpers=new w(this.annoCtx)}return r(t,[{key:\"addXaxisAnnotation\",value:function(t,e,i){var a,s=this.w,r=this.helpers.getX1X2(\"x1\",t),o=t.label.text,n=t.strokeDashArray;if(x.isNumber(r)){if(null===t.x2||void 0===t.x2){var l=this.annoCtx.graphics.drawLine(r+t.offsetX,0+t.offsetY,r+t.offsetX,s.globals.gridHeight+t.offsetY,t.borderColor,n,t.borderWidth);e.appendChild(l.node),t.id&&l.node.classList.add(t.id)}else{if((a=this.helpers.getX1X2(\"x2\",t))<r){var h=r;r=a,a=h}var c=this.annoCtx.graphics.drawRect(r+t.offsetX,0+t.offsetY,a-r,s.globals.gridHeight+t.offsetY,0,t.fillColor,t.opacity,1,t.borderColor,n);c.node.classList.add(\"apexcharts-annotation-rect\"),c.attr(\"clip-path\",\"url(#gridRectMask\".concat(s.globals.cuid,\")\")),e.appendChild(c.node),t.id&&c.node.classList.add(t.id)}var d=this.annoCtx.graphics.getTextRects(o,parseFloat(t.label.style.fontSize)),g=\"top\"===t.label.position?4:\"center\"===t.label.position?s.globals.gridHeight/2+(\"vertical\"===t.label.orientation?d.width/2:0):s.globals.gridHeight,u=this.annoCtx.graphics.drawText({x:r+t.label.offsetX,y:g+t.label.offsetY-(\"vertical\"===t.label.orientation?\"top\"===t.label.position?d.width/2-12:-d.width/2:0),text:o,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-xaxis-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});u.attr({rel:i}),e.appendChild(u.node),this.annoCtx.helpers.setOrientations(t,i)}}},{key:\"drawXAxisAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-xaxis-annotations\"});return e.config.annotations.xaxis.map((function(e,a){t.addXaxisAnnotation(e,i.node,a)})),i}}]),t}(),A=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e,this.helpers=new w(this.annoCtx)}return r(t,[{key:\"addYaxisAnnotation\",value:function(t,e,i){var a,s=this.w,r=t.strokeDashArray,o=this.helpers.getY1Y2(\"y1\",t),n=t.label.text;if(null===t.y2||void 0===t.y2){var l=this.annoCtx.graphics.drawLine(0+t.offsetX,o+t.offsetY,this._getYAxisAnnotationWidth(t),o+t.offsetY,t.borderColor,r,t.borderWidth);e.appendChild(l.node),t.id&&l.node.classList.add(t.id)}else{if((a=this.helpers.getY1Y2(\"y2\",t))>o){var h=o;o=a,a=h}var c=this.annoCtx.graphics.drawRect(0+t.offsetX,a+t.offsetY,this._getYAxisAnnotationWidth(t),o-a,0,t.fillColor,t.opacity,1,t.borderColor,r);c.node.classList.add(\"apexcharts-annotation-rect\"),c.attr(\"clip-path\",\"url(#gridRectMask\".concat(s.globals.cuid,\")\")),e.appendChild(c.node),t.id&&c.node.classList.add(t.id)}var d=\"right\"===t.label.position?s.globals.gridWidth:\"center\"===t.label.position?s.globals.gridWidth/2:0,g=this.annoCtx.graphics.drawText({x:d+t.label.offsetX,y:(null!=a?a:o)+t.label.offsetY-3,text:n,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-yaxis-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});g.attr({rel:i}),e.appendChild(g.node)}},{key:\"_getYAxisAnnotationWidth\",value:function(t){var e=this.w;e.globals.gridWidth;return(t.width.indexOf(\"%\")>-1?e.globals.gridWidth*parseInt(t.width,10)/100:parseInt(t.width,10))+t.offsetX}},{key:\"drawYAxisAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-yaxis-annotations\"});return e.config.annotations.yaxis.map((function(e,a){t.addYaxisAnnotation(e,i.node,a)})),i}}]),t}(),S=function(){function t(e){a(this,t),this.w=e.w,this.annoCtx=e,this.helpers=new w(this.annoCtx)}return r(t,[{key:\"addPointAnnotation\",value:function(t,e,i){this.w;var a=this.helpers.getX1X2(\"x1\",t),s=this.helpers.getY1Y2(\"y1\",t);if(x.isNumber(a)){var r={pSize:t.marker.size,pointStrokeWidth:t.marker.strokeWidth,pointFillColor:t.marker.fillColor,pointStrokeColor:t.marker.strokeColor,shape:t.marker.shape,pRadius:t.marker.radius,class:\"apexcharts-point-annotation-marker \".concat(t.marker.cssClass,\" \").concat(t.id?t.id:\"\")},o=this.annoCtx.graphics.drawMarker(a+t.marker.offsetX,s+t.marker.offsetY,r);e.appendChild(o.node);var n=t.label.text?t.label.text:\"\",l=this.annoCtx.graphics.drawText({x:a+t.label.offsetX,y:s+t.label.offsetY-t.marker.size-parseFloat(t.label.style.fontSize)/1.6,text:n,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:\"apexcharts-point-annotation-label \".concat(t.label.style.cssClass,\" \").concat(t.id?t.id:\"\")});if(l.attr({rel:i}),e.appendChild(l.node),t.customSVG.SVG){var h=this.annoCtx.graphics.group({class:\"apexcharts-point-annotations-custom-svg \"+t.customSVG.cssClass});h.attr({transform:\"translate(\".concat(a+t.customSVG.offsetX,\", \").concat(s+t.customSVG.offsetY,\")\")}),h.node.innerHTML=t.customSVG.SVG,e.appendChild(h.node)}if(t.image.path){var c=t.image.width?t.image.width:20,d=t.image.height?t.image.height:20;o=this.annoCtx.addImage({x:a+t.image.offsetX-c/2,y:s+t.image.offsetY-d/2,width:c,height:d,path:t.image.path,appendTo:\".apexcharts-point-annotations\"})}t.mouseEnter&&o.node.addEventListener(\"mouseenter\",t.mouseEnter.bind(this,t)),t.mouseLeave&&o.node.addEventListener(\"mouseleave\",t.mouseLeave.bind(this,t)),t.click&&o.node.addEventListener(\"click\",t.click.bind(this,t))}}},{key:\"drawPointAnnotations\",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:\"apexcharts-point-annotations\"});return e.config.annotations.points.map((function(e,a){t.addPointAnnotation(e,i.node,a)})),i}}]),t}();var C={name:\"en\",options:{months:[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],shortMonths:[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],days:[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],shortDays:[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],toolbar:{exportToSVG:\"Download SVG\",exportToPNG:\"Download PNG\",exportToCSV:\"Download CSV\",menu:\"Menu\",selection:\"Selection\",selectionZoom:\"Selection Zoom\",zoomIn:\"Zoom In\",zoomOut:\"Zoom Out\",pan:\"Panning\",reset:\"Reset Zoom\"}}},L=function(){function t(){a(this,t),this.yAxis={show:!0,showAlways:!1,showForNullSeries:!0,seriesName:void 0,opposite:!1,reversed:!1,logarithmic:!1,logBase:10,tickAmount:void 0,forceNiceScale:!1,max:void 0,min:void 0,floating:!1,decimalsInFloat:void 0,labels:{show:!0,minWidth:0,maxWidth:160,offsetX:0,offsetY:0,align:void 0,rotate:0,padding:20,style:{colors:[],fontSize:\"11px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"},formatter:void 0},axisBorder:{show:!1,color:\"#e0e0e0\",width:1,offsetX:0,offsetY:0},axisTicks:{show:!1,color:\"#e0e0e0\",width:6,offsetX:0,offsetY:0},title:{text:void 0,rotate:-90,offsetY:0,offsetX:0,style:{color:void 0,fontSize:\"11px\",fontWeight:900,fontFamily:void 0,cssClass:\"\"}},tooltip:{enabled:!1,offsetX:0},crosshairs:{show:!0,position:\"front\",stroke:{color:\"#b6b6b6\",width:1,dashArray:0}}},this.pointAnnotation={id:void 0,x:0,y:null,yAxisIndex:0,seriesIndex:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,marker:{size:4,fillColor:\"#fff\",strokeWidth:2,strokeColor:\"#333\",shape:\"circle\",offsetX:0,offsetY:0,radius:2,cssClass:\"\"},label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"middle\",offsetX:0,offsetY:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}},customSVG:{SVG:void 0,cssClass:void 0,offsetX:0,offsetY:0},image:{path:void 0,width:20,height:20,offsetX:0,offsetY:0}},this.yAxisAnnotation={id:void 0,y:0,y2:null,strokeDashArray:1,fillColor:\"#c2c2c2\",borderColor:\"#c2c2c2\",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,width:\"100%\",yAxisIndex:0,label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"end\",position:\"right\",offsetX:0,offsetY:-3,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}}},this.xAxisAnnotation={id:void 0,x:0,x2:null,strokeDashArray:1,fillColor:\"#c2c2c2\",borderColor:\"#c2c2c2\",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,label:{borderColor:\"#c2c2c2\",borderWidth:1,borderRadius:2,text:void 0,textAnchor:\"middle\",orientation:\"vertical\",position:\"top\",offsetX:0,offsetY:0,mouseEnter:void 0,mouseLeave:void 0,click:void 0,style:{background:\"#fff\",color:void 0,fontSize:\"11px\",fontFamily:void 0,fontWeight:400,cssClass:\"\",padding:{left:5,right:5,top:2,bottom:2}}}},this.text={x:0,y:0,text:\"\",textAnchor:\"start\",foreColor:void 0,fontSize:\"13px\",fontFamily:void 0,fontWeight:400,appendTo:\".apexcharts-annotations\",backgroundColor:\"transparent\",borderColor:\"#c2c2c2\",borderRadius:0,borderWidth:0,paddingLeft:4,paddingRight:4,paddingTop:2,paddingBottom:2}}return r(t,[{key:\"init\",value:function(){return{annotations:{yaxis:[this.yAxisAnnotation],xaxis:[this.xAxisAnnotation],points:[this.pointAnnotation],texts:[],images:[],shapes:[]},chart:{animations:{enabled:!0,easing:\"easeinout\",speed:800,animateGradually:{delay:150,enabled:!0},dynamicAnimation:{enabled:!0,speed:350}},background:\"transparent\",locales:[C],defaultLocale:\"en\",dropShadow:{enabled:!1,enabledOnSeries:void 0,top:2,left:2,blur:4,color:\"#000\",opacity:.35},events:{animationEnd:void 0,beforeMount:void 0,mounted:void 0,updated:void 0,click:void 0,mouseMove:void 0,mouseLeave:void 0,xAxisLabelClick:void 0,legendClick:void 0,markerClick:void 0,selection:void 0,dataPointSelection:void 0,dataPointMouseEnter:void 0,dataPointMouseLeave:void 0,beforeZoom:void 0,beforeResetZoom:void 0,zoomed:void 0,scrolled:void 0,brushScrolled:void 0},foreColor:\"#373d3f\",fontFamily:\"Helvetica, Arial, sans-serif\",height:\"auto\",parentHeightOffset:15,redrawOnParentResize:!0,redrawOnWindowResize:!0,id:void 0,group:void 0,offsetX:0,offsetY:0,selection:{enabled:!1,type:\"x\",fill:{color:\"#24292e\",opacity:.1},stroke:{width:1,color:\"#24292e\",opacity:.4,dashArray:3},xaxis:{min:void 0,max:void 0},yaxis:{min:void 0,max:void 0}},sparkline:{enabled:!1},brush:{enabled:!1,autoScaleYaxis:!0,target:void 0},stacked:!1,stackType:\"normal\",toolbar:{show:!0,offsetX:0,offsetY:0,tools:{download:!0,selection:!0,zoom:!0,zoomin:!0,zoomout:!0,pan:!0,reset:!0,customIcons:[]},export:{csv:{filename:void 0,columnDelimiter:\",\",headerCategory:\"category\",headerValue:\"value\",dateFormatter:function(t){return new Date(t).toDateString()}},png:{filename:void 0},svg:{filename:void 0}},autoSelected:\"zoom\"},type:\"line\",width:\"100%\",zoom:{enabled:!0,type:\"x\",autoScaleYaxis:!1,zoomedArea:{fill:{color:\"#90CAF9\",opacity:.4},stroke:{color:\"#0D47A1\",opacity:.4,width:1}}}},plotOptions:{area:{fillTo:\"origin\"},bar:{horizontal:!1,columnWidth:\"70%\",barHeight:\"70%\",distributed:!1,borderRadius:0,borderRadiusApplication:\"around\",borderRadiusWhenStacked:\"last\",rangeBarOverlap:!0,rangeBarGroupRows:!1,hideZeroBarsWhenGrouped:!0,colors:{ranges:[],backgroundBarColors:[],backgroundBarOpacity:1,backgroundBarRadius:0},dataLabels:{position:\"top\",maxItems:100,hideOverflowingLabels:!0,orientation:\"horizontal\",total:{enabled:!1,formatter:void 0,offsetX:0,offsetY:0,style:{color:\"#373d3f\",fontSize:\"12px\",fontFamily:void 0,fontWeight:600}}}},bubble:{zScaling:!0,minBubbleRadius:void 0,maxBubbleRadius:void 0},candlestick:{colors:{upward:\"#00B746\",downward:\"#EF403C\"},wick:{useFillColor:!0}},boxPlot:{colors:{upper:\"#00E396\",lower:\"#008FFB\"}},heatmap:{radius:2,enableShades:!0,shadeIntensity:.5,reverseNegativeShade:!1,distributed:!1,useFillColorAsStroke:!1,colorScale:{inverse:!1,ranges:[],min:void 0,max:void 0}},treemap:{enableShades:!0,shadeIntensity:.5,distributed:!1,reverseNegativeShade:!1,useFillColorAsStroke:!1,colorScale:{inverse:!1,ranges:[],min:void 0,max:void 0}},radialBar:{inverseOrder:!1,startAngle:0,endAngle:360,offsetX:0,offsetY:0,hollow:{margin:5,size:\"50%\",background:\"transparent\",image:void 0,imageWidth:150,imageHeight:150,imageOffsetX:0,imageOffsetY:0,imageClipped:!0,position:\"front\",dropShadow:{enabled:!1,top:0,left:0,blur:3,color:\"#000\",opacity:.5}},track:{show:!0,startAngle:void 0,endAngle:void 0,background:\"#f2f2f2\",strokeWidth:\"97%\",opacity:1,margin:5,dropShadow:{enabled:!1,top:0,left:0,blur:3,color:\"#000\",opacity:.5}},dataLabels:{show:!0,name:{show:!0,fontSize:\"16px\",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:0,formatter:function(t){return t}},value:{show:!0,fontSize:\"14px\",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:16,formatter:function(t){return t+\"%\"}},total:{show:!1,label:\"Total\",fontSize:\"16px\",fontWeight:600,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)/t.globals.series.length+\"%\"}}}},pie:{customScale:1,offsetX:0,offsetY:0,startAngle:0,endAngle:360,expandOnClick:!0,dataLabels:{offset:0,minAngleToShowLabel:10},donut:{size:\"65%\",background:\"transparent\",labels:{show:!1,name:{show:!0,fontSize:\"16px\",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:-10,formatter:function(t){return t}},value:{show:!0,fontSize:\"20px\",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:10,formatter:function(t){return t}},total:{show:!1,showAlways:!1,label:\"Total\",fontSize:\"16px\",fontWeight:400,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)}}}}},polarArea:{rings:{strokeWidth:1,strokeColor:\"#e8e8e8\"},spokes:{strokeWidth:1,connectorColors:\"#e8e8e8\"}},radar:{size:void 0,offsetX:0,offsetY:0,polygons:{strokeWidth:1,strokeColors:\"#e8e8e8\",connectorColors:\"#e8e8e8\",fill:{colors:void 0}}}},colors:void 0,dataLabels:{enabled:!0,enabledOnSeries:void 0,formatter:function(t){return null!==t?t:\"\"},textAnchor:\"middle\",distributed:!1,offsetX:0,offsetY:0,style:{fontSize:\"12px\",fontFamily:void 0,fontWeight:600,colors:void 0},background:{enabled:!0,foreColor:\"#fff\",borderRadius:2,padding:4,opacity:.9,borderWidth:1,borderColor:\"#fff\",dropShadow:{enabled:!1,top:1,left:1,blur:1,color:\"#000\",opacity:.45}},dropShadow:{enabled:!1,top:1,left:1,blur:1,color:\"#000\",opacity:.45}},fill:{type:\"solid\",colors:void 0,opacity:.85,gradient:{shade:\"dark\",type:\"horizontal\",shadeIntensity:.5,gradientToColors:void 0,inverseColors:!0,opacityFrom:1,opacityTo:1,stops:[0,50,100],colorStops:[]},image:{src:[],width:void 0,height:void 0},pattern:{style:\"squares\",width:6,height:6,strokeWidth:2}},forecastDataPoints:{count:0,fillOpacity:.5,strokeWidth:void 0,dashArray:4},grid:{show:!0,borderColor:\"#e0e0e0\",strokeDashArray:0,position:\"back\",xaxis:{lines:{show:!1}},yaxis:{lines:{show:!0}},row:{colors:void 0,opacity:.5},column:{colors:void 0,opacity:.5},padding:{top:0,right:10,bottom:0,left:12}},labels:[],legend:{show:!0,showForSingleSeries:!1,showForNullSeries:!0,showForZeroSeries:!0,floating:!1,position:\"bottom\",horizontalAlign:\"center\",inverseOrder:!1,fontSize:\"12px\",fontFamily:void 0,fontWeight:400,width:void 0,height:void 0,formatter:void 0,tooltipHoverFormatter:void 0,offsetX:-20,offsetY:4,customLegendItems:[],labels:{colors:void 0,useSeriesColors:!1},markers:{width:12,height:12,strokeWidth:0,fillColors:void 0,strokeColor:\"#fff\",radius:12,customHTML:void 0,offsetX:0,offsetY:0,onClick:void 0},itemMargin:{horizontal:5,vertical:2},onItemClick:{toggleDataSeries:!0},onItemHover:{highlightDataSeries:!0}},markers:{discrete:[],size:0,colors:void 0,strokeColors:\"#fff\",strokeWidth:2,strokeOpacity:.9,strokeDashArray:0,fillOpacity:1,shape:\"circle\",width:8,height:8,radius:2,offsetX:0,offsetY:0,onClick:void 0,onDblClick:void 0,showNullDataPoints:!0,hover:{size:void 0,sizeOffset:3}},noData:{text:void 0,align:\"center\",verticalAlign:\"middle\",offsetX:0,offsetY:0,style:{color:void 0,fontSize:\"14px\",fontFamily:void 0}},responsive:[],series:void 0,states:{normal:{filter:{type:\"none\",value:0}},hover:{filter:{type:\"lighten\",value:.1}},active:{allowMultipleDataPointsSelection:!1,filter:{type:\"darken\",value:.5}}},title:{text:void 0,align:\"left\",margin:5,offsetX:0,offsetY:0,floating:!1,style:{fontSize:\"14px\",fontWeight:900,fontFamily:void 0,color:void 0}},subtitle:{text:void 0,align:\"left\",margin:5,offsetX:0,offsetY:30,floating:!1,style:{fontSize:\"12px\",fontWeight:400,fontFamily:void 0,color:void 0}},stroke:{show:!0,curve:\"smooth\",lineCap:\"butt\",width:2,colors:void 0,dashArray:0,fill:{type:\"solid\",colors:void 0,opacity:.85,gradient:{shade:\"dark\",type:\"horizontal\",shadeIntensity:.5,gradientToColors:void 0,inverseColors:!0,opacityFrom:1,opacityTo:1,stops:[0,50,100],colorStops:[]}}},tooltip:{enabled:!0,enabledOnSeries:void 0,shared:!0,followCursor:!1,intersect:!1,inverseOrder:!1,custom:void 0,fillSeriesColor:!1,theme:\"light\",cssClass:\"\",style:{fontSize:\"12px\",fontFamily:void 0},onDatasetHover:{highlightDataSeries:!1},x:{show:!0,format:\"dd MMM\",formatter:void 0},y:{formatter:void 0,title:{formatter:function(t){return t?t+\": \":\"\"}}},z:{formatter:void 0,title:\"Size: \"},marker:{show:!0,fillColors:void 0},items:{display:\"flex\"},fixed:{enabled:!1,position:\"topRight\",offsetX:0,offsetY:0}},xaxis:{type:\"category\",categories:[],convertedCatToNumeric:!1,offsetX:0,offsetY:0,overwriteCategories:void 0,labels:{show:!0,rotate:-45,rotateAlways:!1,hideOverlappingLabels:!0,trim:!1,minHeight:void 0,maxHeight:120,showDuplicates:!0,style:{colors:[],fontSize:\"12px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"},offsetX:0,offsetY:0,format:void 0,formatter:void 0,datetimeUTC:!0,datetimeFormatter:{year:\"yyyy\",month:\"MMM 'yy\",day:\"dd MMM\",hour:\"HH:mm\",minute:\"HH:mm:ss\",second:\"HH:mm:ss\"}},group:{groups:[],style:{colors:[],fontSize:\"12px\",fontWeight:400,fontFamily:void 0,cssClass:\"\"}},axisBorder:{show:!0,color:\"#e0e0e0\",width:\"100%\",height:1,offsetX:0,offsetY:0},axisTicks:{show:!0,color:\"#e0e0e0\",height:6,offsetX:0,offsetY:0},tickAmount:void 0,tickPlacement:\"on\",min:void 0,max:void 0,range:void 0,floating:!1,decimalsInFloat:void 0,position:\"bottom\",title:{text:void 0,offsetX:0,offsetY:0,style:{color:void 0,fontSize:\"12px\",fontWeight:900,fontFamily:void 0,cssClass:\"\"}},crosshairs:{show:!0,width:1,position:\"back\",opacity:.9,stroke:{color:\"#b6b6b6\",width:1,dashArray:3},fill:{type:\"solid\",color:\"#B1B9C4\",gradient:{colorFrom:\"#D8E3F0\",colorTo:\"#BED1E6\",stops:[0,100],opacityFrom:.4,opacityTo:.5}},dropShadow:{enabled:!1,left:0,top:0,blur:1,opacity:.4}},tooltip:{enabled:!0,offsetY:0,formatter:void 0,style:{fontSize:\"12px\",fontFamily:void 0}}},yaxis:this.yAxis,theme:{mode:\"light\",palette:\"palette1\",monochrome:{enabled:!1,color:\"#008FFB\",shadeTo:\"light\",shadeIntensity:.65}}}}}]),t}(),P=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.graphics=new m(this.ctx),this.w.globals.isBarHorizontal&&(this.invertAxis=!0),this.helpers=new w(this),this.xAxisAnnotations=new k(this),this.yAxisAnnotations=new A(this),this.pointsAnnotations=new S(this),this.w.globals.isBarHorizontal&&this.w.config.yaxis[0].reversed&&(this.inversedReversedAxis=!0),this.xDivision=this.w.globals.gridWidth/this.w.globals.dataPoints}return r(t,[{key:\"drawAxesAnnotations\",value:function(){var t=this.w;if(t.globals.axisCharts){for(var e=this.yAxisAnnotations.drawYAxisAnnotations(),i=this.xAxisAnnotations.drawXAxisAnnotations(),a=this.pointsAnnotations.drawPointAnnotations(),s=t.config.chart.animations.enabled,r=[e,i,a],o=[i.node,e.node,a.node],n=0;n<3;n++)t.globals.dom.elGraphical.add(r[n]),!s||t.globals.resized||t.globals.dataChanged||\"scatter\"!==t.config.chart.type&&\"bubble\"!==t.config.chart.type&&t.globals.dataPoints>1&&o[n].classList.add(\"apexcharts-element-hidden\"),t.globals.delayedElements.push({el:o[n],index:0});this.helpers.annotationsBackground()}}},{key:\"drawImageAnnos\",value:function(){var t=this;this.w.config.annotations.images.map((function(e,i){t.addImage(e,i)}))}},{key:\"drawTextAnnos\",value:function(){var t=this;this.w.config.annotations.texts.map((function(e,i){t.addText(e,i)}))}},{key:\"addXaxisAnnotation\",value:function(t,e,i){this.xAxisAnnotations.addXaxisAnnotation(t,e,i)}},{key:\"addYaxisAnnotation\",value:function(t,e,i){this.yAxisAnnotations.addYaxisAnnotation(t,e,i)}},{key:\"addPointAnnotation\",value:function(t,e,i){this.pointsAnnotations.addPointAnnotation(t,e,i)}},{key:\"addText\",value:function(t,e){var i=t.x,a=t.y,s=t.text,r=t.textAnchor,o=t.foreColor,n=t.fontSize,l=t.fontFamily,h=t.fontWeight,c=t.cssClass,d=t.backgroundColor,g=t.borderWidth,u=t.strokeDashArray,f=t.borderRadius,p=t.borderColor,x=t.appendTo,b=void 0===x?\".apexcharts-annotations\":x,v=t.paddingLeft,m=void 0===v?4:v,y=t.paddingRight,w=void 0===y?4:y,k=t.paddingBottom,A=void 0===k?2:k,S=t.paddingTop,C=void 0===S?2:S,L=this.w,P=this.graphics.drawText({x:i,y:a,text:s,textAnchor:r||\"start\",fontSize:n||\"12px\",fontWeight:h||\"regular\",fontFamily:l||L.config.chart.fontFamily,foreColor:o||L.config.chart.foreColor,cssClass:c}),T=L.globals.dom.baseEl.querySelector(b);T&&T.appendChild(P.node);var M=P.bbox();if(s){var I=this.graphics.drawRect(M.x-m,M.y-C,M.width+m+w,M.height+A+C,f,d||\"transparent\",1,g,p,u);T.insertBefore(I.node,P.node)}}},{key:\"addImage\",value:function(t,e){var i=this.w,a=t.path,s=t.x,r=void 0===s?0:s,o=t.y,n=void 0===o?0:o,l=t.width,h=void 0===l?20:l,c=t.height,d=void 0===c?20:c,g=t.appendTo,u=void 0===g?\".apexcharts-annotations\":g,f=i.globals.dom.Paper.image(a);f.size(h,d).move(r,n);var p=i.globals.dom.baseEl.querySelector(u);return p&&p.appendChild(f.node),f}},{key:\"addXaxisAnnotationExternal\",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"xaxis\",contextMethod:i.addXaxisAnnotation}),i}},{key:\"addYaxisAnnotationExternal\",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"yaxis\",contextMethod:i.addYaxisAnnotation}),i}},{key:\"addPointAnnotationExternal\",value:function(t,e,i){return void 0===this.invertAxis&&(this.invertAxis=i.w.globals.isBarHorizontal),this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:\"point\",contextMethod:i.addPointAnnotation}),i}},{key:\"addAnnotationExternal\",value:function(t){var e=t.params,i=t.pushToMemory,a=t.context,s=t.type,r=t.contextMethod,o=a,n=o.w,l=n.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations\")),h=l.childNodes.length+1,c=new L,d=Object.assign({},\"xaxis\"===s?c.xAxisAnnotation:\"yaxis\"===s?c.yAxisAnnotation:c.pointAnnotation),g=x.extend(d,e);switch(s){case\"xaxis\":this.addXaxisAnnotation(g,l,h);break;case\"yaxis\":this.addYaxisAnnotation(g,l,h);break;case\"point\":this.addPointAnnotation(g,l,h)}var u=n.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(s,\"-annotations .apexcharts-\").concat(s,\"-annotation-label[rel='\").concat(h,\"']\")),f=this.helpers.addBackgroundToAnno(u,g);return f&&l.insertBefore(f.node,u),i&&n.globals.memory.methodsToExec.push({context:o,id:g.id?g.id:x.randomId(),method:r,label:\"addAnnotation\",params:e}),a}},{key:\"clearAnnotations\",value:function(t){var e=t.w,i=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis-annotations, .apexcharts-xaxis-annotations, .apexcharts-point-annotations\");e.globals.memory.methodsToExec.map((function(t,i){\"addText\"!==t.label&&\"addAnnotation\"!==t.label||e.globals.memory.methodsToExec.splice(i,1)})),i=x.listToArray(i),Array.prototype.forEach.call(i,(function(t){for(;t.firstChild;)t.removeChild(t.firstChild)}))}},{key:\"removeAnnotation\",value:function(t,e){var i=t.w,a=i.globals.dom.baseEl.querySelectorAll(\".\".concat(e));a&&(i.globals.memory.methodsToExec.map((function(t,a){t.id===e&&i.globals.memory.methodsToExec.splice(a,1)})),Array.prototype.forEach.call(a,(function(t){t.parentElement.removeChild(t)})))}}]),t}(),T=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.months31=[1,3,5,7,8,10,12],this.months30=[2,4,6,9,11],this.daysCntOfYear=[0,31,59,90,120,151,181,212,243,273,304,334]}return r(t,[{key:\"isValidDate\",value:function(t){return!isNaN(this.parseDate(t))}},{key:\"getTimeStamp\",value:function(t){return Date.parse(t)?this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toISOString().substr(0,25)).getTime():new Date(t).getTime():t}},{key:\"getDate\",value:function(t){return this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toUTCString()):new Date(t)}},{key:\"parseDate\",value:function(t){var e=Date.parse(t);if(!isNaN(e))return this.getTimeStamp(t);var i=Date.parse(t.replace(/-/g,\"/\").replace(/[a-z]+/gi,\" \"));return i=this.getTimeStamp(i)}},{key:\"parseDateWithTimezone\",value:function(t){return Date.parse(t.replace(/-/g,\"/\").replace(/[a-z]+/gi,\" \"))}},{key:\"formatDate\",value:function(t,e){var i=this.w.globals.locale,a=this.w.config.xaxis.labels.datetimeUTC,s=[\"\\0\"].concat(u(i.months)),r=[\"\\x01\"].concat(u(i.shortMonths)),o=[\"\\x02\"].concat(u(i.days)),n=[\"\\x03\"].concat(u(i.shortDays));function l(t,e){var i=t+\"\";for(e=e||2;i.length<e;)i=\"0\"+i;return i}var h=a?t.getUTCFullYear():t.getFullYear();e=(e=(e=e.replace(/(^|[^\\\\])yyyy+/g,\"$1\"+h)).replace(/(^|[^\\\\])yy/g,\"$1\"+h.toString().substr(2,2))).replace(/(^|[^\\\\])y/g,\"$1\"+h);var c=(a?t.getUTCMonth():t.getMonth())+1;e=(e=(e=(e=e.replace(/(^|[^\\\\])MMMM+/g,\"$1\"+s[0])).replace(/(^|[^\\\\])MMM/g,\"$1\"+r[0])).replace(/(^|[^\\\\])MM/g,\"$1\"+l(c))).replace(/(^|[^\\\\])M/g,\"$1\"+c);var d=a?t.getUTCDate():t.getDate();e=(e=(e=(e=e.replace(/(^|[^\\\\])dddd+/g,\"$1\"+o[0])).replace(/(^|[^\\\\])ddd/g,\"$1\"+n[0])).replace(/(^|[^\\\\])dd/g,\"$1\"+l(d))).replace(/(^|[^\\\\])d/g,\"$1\"+d);var g=a?t.getUTCHours():t.getHours(),f=g>12?g-12:0===g?12:g;e=(e=(e=(e=e.replace(/(^|[^\\\\])HH+/g,\"$1\"+l(g))).replace(/(^|[^\\\\])H/g,\"$1\"+g)).replace(/(^|[^\\\\])hh+/g,\"$1\"+l(f))).replace(/(^|[^\\\\])h/g,\"$1\"+f);var p=a?t.getUTCMinutes():t.getMinutes();e=(e=e.replace(/(^|[^\\\\])mm+/g,\"$1\"+l(p))).replace(/(^|[^\\\\])m/g,\"$1\"+p);var x=a?t.getUTCSeconds():t.getSeconds();e=(e=e.replace(/(^|[^\\\\])ss+/g,\"$1\"+l(x))).replace(/(^|[^\\\\])s/g,\"$1\"+x);var b=a?t.getUTCMilliseconds():t.getMilliseconds();e=e.replace(/(^|[^\\\\])fff+/g,\"$1\"+l(b,3)),b=Math.round(b/10),e=e.replace(/(^|[^\\\\])ff/g,\"$1\"+l(b)),b=Math.round(b/10);var v=g<12?\"AM\":\"PM\";e=(e=(e=e.replace(/(^|[^\\\\])f/g,\"$1\"+b)).replace(/(^|[^\\\\])TT+/g,\"$1\"+v)).replace(/(^|[^\\\\])T/g,\"$1\"+v.charAt(0));var m=v.toLowerCase();e=(e=e.replace(/(^|[^\\\\])tt+/g,\"$1\"+m)).replace(/(^|[^\\\\])t/g,\"$1\"+m.charAt(0));var y=-t.getTimezoneOffset(),w=a||!y?\"Z\":y>0?\"+\":\"-\";if(!a){var k=(y=Math.abs(y))%60;w+=l(Math.floor(y/60))+\":\"+l(k)}e=e.replace(/(^|[^\\\\])K/g,\"$1\"+w);var A=(a?t.getUTCDay():t.getDay())+1;return e=(e=(e=(e=(e=e.replace(new RegExp(o[0],\"g\"),o[A])).replace(new RegExp(n[0],\"g\"),n[A])).replace(new RegExp(s[0],\"g\"),s[c])).replace(new RegExp(r[0],\"g\"),r[c])).replace(/\\\\(.)/g,\"$1\")}},{key:\"getTimeUnitsfromTimestamp\",value:function(t,e,i){var a=this.w;void 0!==a.config.xaxis.min&&(t=a.config.xaxis.min),void 0!==a.config.xaxis.max&&(e=a.config.xaxis.max);var s=this.getDate(t),r=this.getDate(e),o=this.formatDate(s,\"yyyy MM dd HH mm ss fff\").split(\" \"),n=this.formatDate(r,\"yyyy MM dd HH mm ss fff\").split(\" \");return{minMillisecond:parseInt(o[6],10),maxMillisecond:parseInt(n[6],10),minSecond:parseInt(o[5],10),maxSecond:parseInt(n[5],10),minMinute:parseInt(o[4],10),maxMinute:parseInt(n[4],10),minHour:parseInt(o[3],10),maxHour:parseInt(n[3],10),minDate:parseInt(o[2],10),maxDate:parseInt(n[2],10),minMonth:parseInt(o[1],10)-1,maxMonth:parseInt(n[1],10)-1,minYear:parseInt(o[0],10),maxYear:parseInt(n[0],10)}}},{key:\"isLeapYear\",value:function(t){return t%4==0&&t%100!=0||t%400==0}},{key:\"calculcateLastDaysOfMonth\",value:function(t,e,i){return this.determineDaysOfMonths(t,e)-i}},{key:\"determineDaysOfYear\",value:function(t){var e=365;return this.isLeapYear(t)&&(e=366),e}},{key:\"determineRemainingDaysOfYear\",value:function(t,e,i){var a=this.daysCntOfYear[e]+i;return e>1&&this.isLeapYear()&&a++,a}},{key:\"determineDaysOfMonths\",value:function(t,e){var i=30;switch(t=x.monthMod(t),!0){case this.months30.indexOf(t)>-1:2===t&&(i=this.isLeapYear(e)?29:28);break;case this.months31.indexOf(t)>-1:default:i=31}return i}}]),t}(),M=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.tooltipKeyFormat=\"dd MMM\"}return r(t,[{key:\"xLabelFormat\",value:function(t,e,i,a){var s=this.w;if(\"datetime\"===s.config.xaxis.type&&void 0===s.config.xaxis.labels.formatter&&void 0===s.config.tooltip.x.formatter){var r=new T(this.ctx);return r.formatDate(r.getDate(e),s.config.tooltip.x.format)}return t(e,i,a)}},{key:\"defaultGeneralFormatter\",value:function(t){return Array.isArray(t)?t.map((function(t){return t})):t}},{key:\"defaultYFormatter\",value:function(t,e,i){var a=this.w;return x.isNumber(t)&&(t=0!==a.globals.yValueDecimal?t.toFixed(void 0!==e.decimalsInFloat?e.decimalsInFloat:a.globals.yValueDecimal):a.globals.maxYArr[i]-a.globals.minYArr[i]<5?t.toFixed(1):t.toFixed(0)),t}},{key:\"setLabelFormatters\",value:function(){var t=this,e=this.w;return e.globals.xaxisTooltipFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttKeyFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttZFormatter=function(t){return t},e.globals.legendFormatter=function(e){return t.defaultGeneralFormatter(e)},void 0!==e.config.xaxis.labels.formatter?e.globals.xLabelFormatter=e.config.xaxis.labels.formatter:e.globals.xLabelFormatter=function(t){if(x.isNumber(t)){if(!e.config.xaxis.convertedCatToNumeric&&\"numeric\"===e.config.xaxis.type){if(x.isNumber(e.config.xaxis.decimalsInFloat))return t.toFixed(e.config.xaxis.decimalsInFloat);var i=e.globals.maxX-e.globals.minX;return i>0&&i<100?t.toFixed(1):t.toFixed(0)}if(e.globals.isBarHorizontal)if(e.globals.maxY-e.globals.minYArr<4)return t.toFixed(1);return t.toFixed(0)}return t},\"function\"==typeof e.config.tooltip.x.formatter?e.globals.ttKeyFormatter=e.config.tooltip.x.formatter:e.globals.ttKeyFormatter=e.globals.xLabelFormatter,\"function\"==typeof e.config.xaxis.tooltip.formatter&&(e.globals.xaxisTooltipFormatter=e.config.xaxis.tooltip.formatter),(Array.isArray(e.config.tooltip.y)||void 0!==e.config.tooltip.y.formatter)&&(e.globals.ttVal=e.config.tooltip.y),void 0!==e.config.tooltip.z.formatter&&(e.globals.ttZFormatter=e.config.tooltip.z.formatter),void 0!==e.config.legend.formatter&&(e.globals.legendFormatter=e.config.legend.formatter),e.config.yaxis.forEach((function(i,a){void 0!==i.labels.formatter?e.globals.yLabelFormatters[a]=i.labels.formatter:e.globals.yLabelFormatters[a]=function(s){return e.globals.xyCharts?Array.isArray(s)?s.map((function(e){return t.defaultYFormatter(e,i,a)})):t.defaultYFormatter(s,i,a):s}})),e.globals}},{key:\"heatmapLabelFormatters\",value:function(){var t=this.w;if(\"heatmap\"===t.config.chart.type){t.globals.yAxisScale[0].result=t.globals.seriesNames.slice();var e=t.globals.seriesNames.reduce((function(t,e){return t.length>e.length?t:e}),0);t.globals.yAxisScale[0].niceMax=e,t.globals.yAxisScale[0].niceMin=e}}}]),t}(),I=function(t){var e,i=t.isTimeline,a=t.ctx,s=t.seriesIndex,r=t.dataPointIndex,o=t.y1,n=t.y2,l=t.w,h=l.globals.seriesRangeStart[s][r],c=l.globals.seriesRangeEnd[s][r],d=l.globals.labels[r],g=l.config.series[s].name?l.config.series[s].name:\"\",u=l.globals.ttKeyFormatter,f=l.config.tooltip.y.title.formatter,p={w:l,seriesIndex:s,dataPointIndex:r,start:h,end:c};(\"function\"==typeof f&&(g=f(g,p)),null!==(e=l.config.series[s].data[r])&&void 0!==e&&e.x&&(d=l.config.series[s].data[r].x),i)||\"datetime\"===l.config.xaxis.type&&(d=new M(a).xLabelFormat(l.globals.ttKeyFormatter,d,d,{i:void 0,dateFormatter:new T(a).formatDate,w:l}));\"function\"==typeof u&&(d=u(d,p)),Number.isFinite(o)&&Number.isFinite(n)&&(h=o,c=n);var x=\"\",b=\"\",v=l.globals.colors[s];if(void 0===l.config.tooltip.x.formatter)if(\"datetime\"===l.config.xaxis.type){var m=new T(a);x=m.formatDate(m.getDate(h),l.config.tooltip.x.format),b=m.formatDate(m.getDate(c),l.config.tooltip.x.format)}else x=h,b=c;else x=l.config.tooltip.x.formatter(h),b=l.config.tooltip.x.formatter(c);return{start:h,end:c,startVal:x,endVal:b,ylabel:d,color:v,seriesName:g}},z=function(t){var e=t.color,i=t.seriesName,a=t.ylabel,s=t.start,r=t.end,o=t.seriesIndex,n=t.dataPointIndex,l=t.ctx.tooltip.tooltipLabels.getFormatters(o);s=l.yLbFormatter(s),r=l.yLbFormatter(r);var h=l.yLbFormatter(t.w.globals.series[o][n]),c='<span class=\"value start-value\">\\n  '.concat(s,'\\n  </span> <span class=\"separator\">-</span> <span class=\"value end-value\">\\n  ').concat(r,\"\\n  </span>\");return'<div class=\"apexcharts-tooltip-rangebar\"><div> <span class=\"series-name\" style=\"color: '+e+'\">'+(i||\"\")+'</span></div><div> <span class=\"category\">'+a+\": </span> \"+(t.w.globals.comboCharts?\"rangeArea\"===t.w.config.series[o].type||\"rangeBar\"===t.w.config.series[o].type?c:\"<span>\".concat(h,\"</span>\"):c)+\" </div></div>\"},X=function(){function t(e){a(this,t),this.opts=e}return r(t,[{key:\"line\",value:function(){return{chart:{animations:{easing:\"swing\"}},dataLabels:{enabled:!1},stroke:{width:5,curve:\"straight\"},markers:{size:0,hover:{sizeOffset:6}},xaxis:{crosshairs:{width:1}}}}},{key:\"sparkline\",value:function(t){this.opts.yaxis[0].show=!1,this.opts.yaxis[0].title.text=\"\",this.opts.yaxis[0].axisBorder.show=!1,this.opts.yaxis[0].axisTicks.show=!1,this.opts.yaxis[0].floating=!0;return x.extend(t,{grid:{show:!1,padding:{left:0,right:0,top:0,bottom:0}},legend:{show:!1},xaxis:{labels:{show:!1},tooltip:{enabled:!1},axisBorder:{show:!1},axisTicks:{show:!1}},chart:{toolbar:{show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1}})}},{key:\"bar\",value:function(){return{chart:{stacked:!1,animations:{easing:\"swing\"}},plotOptions:{bar:{dataLabels:{position:\"center\"}}},dataLabels:{style:{colors:[\"#fff\"]},background:{enabled:!1}},stroke:{width:0,lineCap:\"round\"},fill:{opacity:.85},legend:{markers:{shape:\"square\",radius:2,size:8}},tooltip:{shared:!1,intersect:!0},xaxis:{tooltip:{enabled:!1},tickPlacement:\"between\",crosshairs:{width:\"barWidth\",position:\"back\",fill:{type:\"gradient\"},dropShadow:{enabled:!1},stroke:{width:0}}}}}},{key:\"candlestick\",value:function(){var t=this;return{stroke:{width:1,colors:[\"#333\"]},fill:{opacity:1},dataLabels:{enabled:!1},tooltip:{shared:!0,custom:function(e){var i=e.seriesIndex,a=e.dataPointIndex,s=e.w;return t._getBoxTooltip(s,i,a,[\"Open\",\"High\",\"\",\"Low\",\"Close\"],\"candlestick\")}},states:{active:{filter:{type:\"none\"}}},xaxis:{crosshairs:{width:1}}}}},{key:\"boxPlot\",value:function(){var t=this;return{chart:{animations:{dynamicAnimation:{enabled:!1}}},stroke:{width:1,colors:[\"#24292e\"]},dataLabels:{enabled:!1},tooltip:{shared:!0,custom:function(e){var i=e.seriesIndex,a=e.dataPointIndex,s=e.w;return t._getBoxTooltip(s,i,a,[\"Minimum\",\"Q1\",\"Median\",\"Q3\",\"Maximum\"],\"boxPlot\")}},markers:{size:5,strokeWidth:1,strokeColors:\"#111\"},xaxis:{crosshairs:{width:1}}}}},{key:\"rangeBar\",value:function(){return{stroke:{width:0,lineCap:\"square\"},plotOptions:{bar:{borderRadius:0,dataLabels:{position:\"center\"}}},dataLabels:{enabled:!1,formatter:function(t,e){e.ctx;var i=e.seriesIndex,a=e.dataPointIndex,s=e.w,r=function(){var t=s.globals.seriesRangeStart[i][a];return s.globals.seriesRangeEnd[i][a]-t};return s.globals.comboCharts?\"rangeBar\"===s.config.series[i].type||\"rangeArea\"===s.config.series[i].type?r():t:r()},background:{enabled:!1},style:{colors:[\"#fff\"]}},tooltip:{shared:!1,followCursor:!0,custom:function(t){return t.w.config.plotOptions&&t.w.config.plotOptions.bar&&t.w.config.plotOptions.bar.horizontal?function(t){var i=I(e(e({},t),{},{isTimeline:!0})),a=i.color,s=i.seriesName,r=i.ylabel,o=i.startVal,n=i.endVal;return z(e(e({},t),{},{color:a,seriesName:s,ylabel:r,start:o,end:n}))}(t):function(t){var i=I(t),a=i.color,s=i.seriesName,r=i.ylabel,o=i.start,n=i.end;return z(e(e({},t),{},{color:a,seriesName:s,ylabel:r,start:o,end:n}))}(t)}},xaxis:{tickPlacement:\"between\",tooltip:{enabled:!1},crosshairs:{stroke:{width:0}}}}}},{key:\"area\",value:function(){return{stroke:{width:4,fill:{type:\"solid\",gradient:{inverseColors:!1,shade:\"light\",type:\"vertical\",opacityFrom:.65,opacityTo:.5,stops:[0,100,100]}}},fill:{type:\"gradient\",gradient:{inverseColors:!1,shade:\"light\",type:\"vertical\",opacityFrom:.65,opacityTo:.5,stops:[0,100,100]}},markers:{size:0,hover:{sizeOffset:6}},tooltip:{followCursor:!1}}}},{key:\"rangeArea\",value:function(){return{stroke:{curve:\"straight\",width:0},fill:{type:\"solid\",opacity:.6},markers:{size:0},states:{hover:{filter:{type:\"none\"}},active:{filter:{type:\"none\"}}},tooltip:{intersect:!1,shared:!0,followCursor:!0,custom:function(t){return function(t){var i=I(t),a=i.color,s=i.seriesName,r=i.ylabel,o=i.start,n=i.end;return z(e(e({},t),{},{color:a,seriesName:s,ylabel:r,start:o,end:n}))}(t)}}}}},{key:\"brush\",value:function(t){return x.extend(t,{chart:{toolbar:{autoSelected:\"selection\",show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1},stroke:{width:1},tooltip:{enabled:!1},xaxis:{tooltip:{enabled:!1}}})}},{key:\"stacked100\",value:function(t){t.dataLabels=t.dataLabels||{},t.dataLabels.formatter=t.dataLabels.formatter||void 0;var e=t.dataLabels.formatter;return t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})),\"bar\"===t.chart.type&&(t.dataLabels.formatter=e||function(t){return\"number\"==typeof t&&t?t.toFixed(0)+\"%\":t}),t}},{key:\"stackedBars\",value:function(){var t=this.bar();return e(e({},t),{},{plotOptions:e(e({},t.plotOptions),{},{bar:e(e({},t.plotOptions.bar),{},{borderRadiusApplication:\"end\",borderRadiusWhenStacked:\"last\"})})})}},{key:\"convertCatToNumeric\",value:function(t){return t.xaxis.convertedCatToNumeric=!0,t}},{key:\"convertCatToNumericXaxis\",value:function(t,e,i){t.xaxis.type=\"numeric\",t.xaxis.labels=t.xaxis.labels||{},t.xaxis.labels.formatter=t.xaxis.labels.formatter||function(t){return x.isNumber(t)?Math.floor(t):t};var a=t.xaxis.labels.formatter,s=t.xaxis.categories&&t.xaxis.categories.length?t.xaxis.categories:t.labels;return i&&i.length&&(s=i.map((function(t){return Array.isArray(t)?t:String(t)}))),s&&s.length&&(t.xaxis.labels.formatter=function(t){return x.isNumber(t)?a(s[Math.floor(t)-1]):a(t)}),t.xaxis.categories=[],t.labels=[],t.xaxis.tickAmount=t.xaxis.tickAmount||\"dataPoints\",t}},{key:\"bubble\",value:function(){return{dataLabels:{style:{colors:[\"#fff\"]}},tooltip:{shared:!1,intersect:!0},xaxis:{crosshairs:{width:0}},fill:{type:\"solid\",gradient:{shade:\"light\",inverse:!0,shadeIntensity:.55,opacityFrom:.4,opacityTo:.8}}}}},{key:\"scatter\",value:function(){return{dataLabels:{enabled:!1},tooltip:{shared:!1,intersect:!0},markers:{size:6,strokeWidth:1,hover:{sizeOffset:2}}}}},{key:\"heatmap\",value:function(){return{chart:{stacked:!1},fill:{opacity:1},dataLabels:{style:{colors:[\"#fff\"]}},stroke:{colors:[\"#fff\"]},tooltip:{followCursor:!0,marker:{show:!1},x:{show:!1}},legend:{position:\"top\",markers:{shape:\"square\",size:10,offsetY:2}},grid:{padding:{right:20}}}}},{key:\"treemap\",value:function(){return{chart:{zoom:{enabled:!1}},dataLabels:{style:{fontSize:14,fontWeight:600,colors:[\"#fff\"]}},stroke:{show:!0,width:2,colors:[\"#fff\"]},legend:{show:!1},fill:{gradient:{stops:[0,100]}},tooltip:{followCursor:!0,x:{show:!1}},grid:{padding:{left:0,right:0}},xaxis:{crosshairs:{show:!1},tooltip:{enabled:!1}}}}},{key:\"pie\",value:function(){return{chart:{toolbar:{show:!1}},plotOptions:{pie:{donut:{labels:{show:!1}}}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},style:{colors:[\"#fff\"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:[\"#fff\"]},fill:{opacity:1,gradient:{shade:\"light\",stops:[0,100]}},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"donut\",value:function(){return{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},style:{colors:[\"#fff\"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:[\"#fff\"]},fill:{opacity:1,gradient:{shade:\"light\",shadeIntensity:.35,stops:[80,100],opacityFrom:1,opacityTo:1}},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"polarArea\",value:function(){return this.opts.yaxis[0].tickAmount=this.opts.yaxis[0].tickAmount?this.opts.yaxis[0].tickAmount:6,{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+\"%\"},enabled:!1},stroke:{show:!0,width:2},fill:{opacity:.7},tooltip:{theme:\"dark\",fillSeriesColor:!0},legend:{position:\"right\"}}}},{key:\"radar\",value:function(){return this.opts.yaxis[0].labels.offsetY=this.opts.yaxis[0].labels.offsetY?this.opts.yaxis[0].labels.offsetY:6,{dataLabels:{enabled:!1,style:{fontSize:\"11px\"}},stroke:{width:2},markers:{size:3,strokeWidth:1,strokeOpacity:1},fill:{opacity:.2},tooltip:{shared:!1,intersect:!0,followCursor:!0},grid:{show:!1},xaxis:{labels:{formatter:function(t){return t},style:{colors:[\"#a8a8a8\"],fontSize:\"11px\"}},tooltip:{enabled:!1},crosshairs:{show:!1}}}}},{key:\"radialBar\",value:function(){return{chart:{animations:{dynamicAnimation:{enabled:!0,speed:800}},toolbar:{show:!1}},fill:{gradient:{shade:\"dark\",shadeIntensity:.4,inverseColors:!1,type:\"diagonal2\",opacityFrom:1,opacityTo:1,stops:[70,98,100]}},legend:{show:!1,position:\"right\"},tooltip:{enabled:!1,fillSeriesColor:!0}}}},{key:\"_getBoxTooltip\",value:function(t,e,i,a,s){var r=t.globals.seriesCandleO[e][i],o=t.globals.seriesCandleH[e][i],n=t.globals.seriesCandleM[e][i],l=t.globals.seriesCandleL[e][i],h=t.globals.seriesCandleC[e][i];return t.config.series[e].type&&t.config.series[e].type!==s?'<div class=\"apexcharts-custom-tooltip\">\\n          '.concat(t.config.series[e].name?t.config.series[e].name:\"series-\"+(e+1),\": <strong>\").concat(t.globals.series[e][i],\"</strong>\\n        </div>\"):'<div class=\"apexcharts-tooltip-box apexcharts-tooltip-'.concat(t.config.chart.type,'\">')+\"<div>\".concat(a[0],': <span class=\"value\">')+r+\"</span></div>\"+\"<div>\".concat(a[1],': <span class=\"value\">')+o+\"</span></div>\"+(n?\"<div>\".concat(a[2],': <span class=\"value\">')+n+\"</span></div>\":\"\")+\"<div>\".concat(a[3],': <span class=\"value\">')+l+\"</span></div>\"+\"<div>\".concat(a[4],': <span class=\"value\">')+h+\"</span></div></div>\"}}]),t}(),E=function(){function t(e){a(this,t),this.opts=e}return r(t,[{key:\"init\",value:function(t){var e=t.responsiveOverride,a=this.opts,s=new L,r=new X(a);this.chartType=a.chart.type,a=this.extendYAxis(a),a=this.extendAnnotations(a);var o=s.init(),n={};if(a&&\"object\"===i(a)){var l={};l=-1!==[\"line\",\"area\",\"bar\",\"candlestick\",\"boxPlot\",\"rangeBar\",\"rangeArea\",\"bubble\",\"scatter\",\"heatmap\",\"treemap\",\"pie\",\"polarArea\",\"donut\",\"radar\",\"radialBar\"].indexOf(a.chart.type)?r[a.chart.type]():r.line(),a.chart.stacked&&\"bar\"===a.chart.type&&(l=r.stackedBars()),a.chart.brush&&a.chart.brush.enabled&&(l=r.brush(l)),a.chart.stacked&&\"100%\"===a.chart.stackType&&(a=r.stacked100(a)),this.checkForDarkTheme(window.Apex),this.checkForDarkTheme(a),a.xaxis=a.xaxis||window.Apex.xaxis||{},e||(a.xaxis.convertedCatToNumeric=!1),((a=this.checkForCatToNumericXAxis(this.chartType,l,a)).chart.sparkline&&a.chart.sparkline.enabled||window.Apex.chart&&window.Apex.chart.sparkline&&window.Apex.chart.sparkline.enabled)&&(l=r.sparkline(l)),n=x.extend(o,l)}var h=x.extend(n,window.Apex);return o=x.extend(h,a),o=this.handleUserInputErrors(o)}},{key:\"checkForCatToNumericXAxis\",value:function(t,e,i){var a=new X(i),s=(\"bar\"===t||\"boxPlot\"===t)&&i.plotOptions&&i.plotOptions.bar&&i.plotOptions.bar.horizontal,r=\"pie\"===t||\"polarArea\"===t||\"donut\"===t||\"radar\"===t||\"radialBar\"===t||\"heatmap\"===t,o=\"datetime\"!==i.xaxis.type&&\"numeric\"!==i.xaxis.type,n=i.xaxis.tickPlacement?i.xaxis.tickPlacement:e.xaxis&&e.xaxis.tickPlacement;return s||r||!o||\"between\"===n||(i=a.convertCatToNumeric(i)),i}},{key:\"extendYAxis\",value:function(t,e){var i=new L;(void 0===t.yaxis||!t.yaxis||Array.isArray(t.yaxis)&&0===t.yaxis.length)&&(t.yaxis={}),t.yaxis.constructor!==Array&&window.Apex.yaxis&&window.Apex.yaxis.constructor!==Array&&(t.yaxis=x.extend(t.yaxis,window.Apex.yaxis)),t.yaxis.constructor!==Array?t.yaxis=[x.extend(i.yAxis,t.yaxis)]:t.yaxis=x.extendArray(t.yaxis,i.yAxis);var a=!1;t.yaxis.forEach((function(t){t.logarithmic&&(a=!0)}));var s=t.series;return e&&!s&&(s=e.config.series),a&&s.length!==t.yaxis.length&&s.length&&(t.yaxis=s.map((function(e,a){if(e.name||(s[a].name=\"series-\".concat(a+1)),t.yaxis[a])return t.yaxis[a].seriesName=s[a].name,t.yaxis[a];var r=x.extend(i.yAxis,t.yaxis[0]);return r.show=!1,r}))),a&&s.length>1&&s.length!==t.yaxis.length&&console.warn(\"A multi-series logarithmic chart should have equal number of series and y-axes. Please make sure to equalize both.\"),t}},{key:\"extendAnnotations\",value:function(t){return void 0===t.annotations&&(t.annotations={},t.annotations.yaxis=[],t.annotations.xaxis=[],t.annotations.points=[]),t=this.extendYAxisAnnotations(t),t=this.extendXAxisAnnotations(t),t=this.extendPointAnnotations(t)}},{key:\"extendYAxisAnnotations\",value:function(t){var e=new L;return t.annotations.yaxis=x.extendArray(void 0!==t.annotations.yaxis?t.annotations.yaxis:[],e.yAxisAnnotation),t}},{key:\"extendXAxisAnnotations\",value:function(t){var e=new L;return t.annotations.xaxis=x.extendArray(void 0!==t.annotations.xaxis?t.annotations.xaxis:[],e.xAxisAnnotation),t}},{key:\"extendPointAnnotations\",value:function(t){var e=new L;return t.annotations.points=x.extendArray(void 0!==t.annotations.points?t.annotations.points:[],e.pointAnnotation),t}},{key:\"checkForDarkTheme\",value:function(t){t.theme&&\"dark\"===t.theme.mode&&(t.tooltip||(t.tooltip={}),\"light\"!==t.tooltip.theme&&(t.tooltip.theme=\"dark\"),t.chart.foreColor||(t.chart.foreColor=\"#f6f7f8\"),t.chart.background||(t.chart.background=\"#424242\"),t.theme.palette||(t.theme.palette=\"palette4\"))}},{key:\"handleUserInputErrors\",value:function(t){var e=t;if(e.tooltip.shared&&e.tooltip.intersect)throw new Error(\"tooltip.shared cannot be enabled when tooltip.intersect is true. Turn off any other option by setting it to false.\");if(\"bar\"===e.chart.type&&e.plotOptions.bar.horizontal){if(e.yaxis.length>1)throw new Error(\"Multiple Y Axis for bars are not supported. Switch to column chart by setting plotOptions.bar.horizontal=false\");e.yaxis[0].reversed&&(e.yaxis[0].opposite=!0),e.xaxis.tooltip.enabled=!1,e.yaxis[0].tooltip.enabled=!1,e.chart.zoom.enabled=!1}return\"bar\"!==e.chart.type&&\"rangeBar\"!==e.chart.type||e.tooltip.shared&&\"barWidth\"===e.xaxis.crosshairs.width&&e.series.length>1&&(e.xaxis.crosshairs.width=\"tickWidth\"),\"candlestick\"!==e.chart.type&&\"boxPlot\"!==e.chart.type||e.yaxis[0].reversed&&(console.warn(\"Reversed y-axis in \".concat(e.chart.type,\" chart is not supported.\")),e.yaxis[0].reversed=!1),e}}]),t}(),Y=function(){function t(){a(this,t)}return r(t,[{key:\"initGlobalVars\",value:function(t){t.series=[],t.seriesCandleO=[],t.seriesCandleH=[],t.seriesCandleM=[],t.seriesCandleL=[],t.seriesCandleC=[],t.seriesRangeStart=[],t.seriesRangeEnd=[],t.seriesRange=[],t.seriesPercent=[],t.seriesGoals=[],t.seriesX=[],t.seriesZ=[],t.seriesNames=[],t.seriesTotals=[],t.seriesLog=[],t.seriesColors=[],t.stackedSeriesTotals=[],t.seriesXvalues=[],t.seriesYvalues=[],t.labels=[],t.hasGroups=!1,t.groups=[],t.categoryLabels=[],t.timescaleLabels=[],t.noLabelsProvided=!1,t.resizeTimer=null,t.selectionResizeTimer=null,t.delayedElements=[],t.pointsArray=[],t.dataLabelsRects=[],t.isXNumeric=!1,t.skipLastTimelinelabel=!1,t.skipFirstTimelinelabel=!1,t.isDataXYZ=!1,t.isMultiLineX=!1,t.isMultipleYAxis=!1,t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE,t.minYArr=[],t.maxYArr=[],t.maxX=-Number.MAX_VALUE,t.minX=Number.MAX_VALUE,t.initialMaxX=-Number.MAX_VALUE,t.initialMinX=Number.MAX_VALUE,t.maxDate=0,t.minDate=Number.MAX_VALUE,t.minZ=Number.MAX_VALUE,t.maxZ=-Number.MAX_VALUE,t.minXDiff=Number.MAX_VALUE,t.yAxisScale=[],t.xAxisScale=null,t.xAxisTicksPositions=[],t.yLabelsCoords=[],t.yTitleCoords=[],t.barPadForNumericAxis=0,t.padHorizontal=0,t.xRange=0,t.yRange=[],t.zRange=0,t.dataPoints=0,t.xTickAmount=0}},{key:\"globalVars\",value:function(t){return{chartID:null,cuid:null,events:{beforeMount:[],mounted:[],updated:[],clicked:[],selection:[],dataPointSelection:[],zoomed:[],scrolled:[]},colors:[],clientX:null,clientY:null,fill:{colors:[]},stroke:{colors:[]},dataLabels:{style:{colors:[]}},radarPolygons:{fill:{colors:[]}},markers:{colors:[],size:t.markers.size,largestSize:0},animationEnded:!1,isTouchDevice:\"ontouchstart\"in window||navigator.msMaxTouchPoints,isDirty:!1,isExecCalled:!1,initialConfig:null,initialSeries:[],lastXAxis:[],lastYAxis:[],columnSeries:null,labels:[],timescaleLabels:[],noLabelsProvided:!1,allSeriesCollapsed:!1,collapsedSeries:[],collapsedSeriesIndices:[],ancillaryCollapsedSeries:[],ancillaryCollapsedSeriesIndices:[],risingSeries:[],dataFormatXNumeric:!1,capturedSeriesIndex:-1,capturedDataPointIndex:-1,selectedDataPoints:[],goldenPadding:35,invalidLogScale:!1,ignoreYAxisIndexes:[],yAxisSameScaleIndices:[],maxValsInArrayIndex:0,radialSize:0,selection:void 0,zoomEnabled:\"zoom\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.zoom&&t.chart.zoom.enabled,panEnabled:\"pan\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.pan,selectionEnabled:\"selection\"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.selection,yaxis:null,mousedown:!1,lastClientPosition:{},visibleXRange:void 0,yValueDecimal:0,total:0,SVGNS:\"http://www.w3.org/2000/svg\",svgWidth:0,svgHeight:0,noData:!1,locale:{},dom:{},memory:{methodsToExec:[]},shouldAnimate:!0,skipLastTimelinelabel:!1,skipFirstTimelinelabel:!1,delayedElements:[],axisCharts:!0,isDataXYZ:!1,resized:!1,resizeTimer:null,comboCharts:!1,dataChanged:!1,previousPaths:[],allSeriesHasEqualX:!0,pointsArray:[],dataLabelsRects:[],lastDrawnDataLabelsIndexes:[],hasNullValues:!1,easing:null,zoomed:!1,gridWidth:0,gridHeight:0,rotateXLabels:!1,defaultLabels:!1,xLabelFormatter:void 0,yLabelFormatters:[],xaxisTooltipFormatter:void 0,ttKeyFormatter:void 0,ttVal:void 0,ttZFormatter:void 0,LINE_HEIGHT_RATIO:1.618,xAxisLabelsHeight:0,xAxisGroupLabelsHeight:0,xAxisLabelsWidth:0,yAxisLabelsWidth:0,scaleX:1,scaleY:1,translateX:0,translateY:0,translateYAxisX:[],yAxisWidths:[],translateXAxisY:0,translateXAxisX:0,tooltip:null}}},{key:\"init\",value:function(t){var e=this.globalVars(t);return this.initGlobalVars(e),e.initialConfig=x.extend({},t),e.initialSeries=x.clone(t.series),e.lastXAxis=x.clone(e.initialConfig.xaxis),e.lastYAxis=x.clone(e.initialConfig.yaxis),e}}]),t}(),F=function(){function t(e){a(this,t),this.opts=e}return r(t,[{key:\"init\",value:function(){var t=new E(this.opts).init({responsiveOverride:!1});return{config:t,globals:(new Y).init(t)}}}]),t}(),R=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.opts=null,this.seriesIndex=0}return r(t,[{key:\"clippedImgArea\",value:function(t){var e=this.w,i=e.config,a=parseInt(e.globals.gridWidth,10),s=parseInt(e.globals.gridHeight,10),r=a>s?a:s,o=t.image,n=0,l=0;void 0===t.width&&void 0===t.height?void 0!==i.fill.image.width&&void 0!==i.fill.image.height?(n=i.fill.image.width+1,l=i.fill.image.height):(n=r+1,l=r):(n=t.width,l=t.height);var h=document.createElementNS(e.globals.SVGNS,\"pattern\");m.setAttrs(h,{id:t.patternID,patternUnits:t.patternUnits?t.patternUnits:\"userSpaceOnUse\",width:n+\"px\",height:l+\"px\"});var c=document.createElementNS(e.globals.SVGNS,\"image\");h.appendChild(c),c.setAttributeNS(window.SVG.xlink,\"href\",o),m.setAttrs(c,{x:0,y:0,preserveAspectRatio:\"none\",width:n+\"px\",height:l+\"px\"}),c.style.opacity=t.opacity,e.globals.dom.elDefs.node.appendChild(h)}},{key:\"getSeriesIndex\",value:function(t){var e=this.w;return(\"bar\"===e.config.chart.type||\"rangeBar\"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||\"heatmap\"===e.config.chart.type||\"treemap\"===e.config.chart.type?this.seriesIndex=t.seriesNumber:this.seriesIndex=t.seriesNumber%e.globals.series.length,this.seriesIndex}},{key:\"fillPath\",value:function(t){var e=this.w;this.opts=t;var i,a,s,r=this.w.config;this.seriesIndex=this.getSeriesIndex(t);var o=this.getFillColors()[this.seriesIndex];void 0!==e.globals.seriesColors[this.seriesIndex]&&(o=e.globals.seriesColors[this.seriesIndex]),\"function\"==typeof o&&(o=o({seriesIndex:this.seriesIndex,dataPointIndex:t.dataPointIndex,value:t.value,w:e}));var n=t.fillType?t.fillType:this.getFillType(this.seriesIndex),l=Array.isArray(r.fill.opacity)?r.fill.opacity[this.seriesIndex]:r.fill.opacity;t.color&&(o=t.color);var h=o;if(-1===o.indexOf(\"rgb\")?o.length<9&&(h=x.hexToRgba(o,l)):o.indexOf(\"rgba\")>-1&&(l=x.getOpacityFromRGBA(o)),t.opacity&&(l=t.opacity),\"pattern\"===n&&(a=this.handlePatternFill({fillConfig:t.fillConfig,patternFill:a,fillColor:o,fillOpacity:l,defaultColor:h})),\"gradient\"===n&&(s=this.handleGradientFill({fillConfig:t.fillConfig,fillColor:o,fillOpacity:l,i:this.seriesIndex})),\"image\"===n){var c=r.fill.image.src,d=t.patternID?t.patternID:\"\";this.clippedImgArea({opacity:l,image:Array.isArray(c)?t.seriesNumber<c.length?c[t.seriesNumber]:c[0]:c,width:t.width?t.width:void 0,height:t.height?t.height:void 0,patternUnits:t.patternUnits,patternID:\"pattern\".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(d)}),i=\"url(#pattern\".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(d,\")\")}else i=\"gradient\"===n?s:\"pattern\"===n?a:h;return t.solid&&(i=h),i}},{key:\"getFillType\",value:function(t){var e=this.w;return Array.isArray(e.config.fill.type)?e.config.fill.type[t]:e.config.fill.type}},{key:\"getFillColors\",value:function(){var t=this.w,e=t.config,i=this.opts,a=[];return t.globals.comboCharts?\"line\"===t.config.series[this.seriesIndex].type?Array.isArray(t.globals.stroke.colors)?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):Array.isArray(t.globals.fill.colors)?a=t.globals.fill.colors:a.push(t.globals.fill.colors):\"line\"===e.chart.type?Array.isArray(t.globals.stroke.colors)?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):Array.isArray(t.globals.fill.colors)?a=t.globals.fill.colors:a.push(t.globals.fill.colors),void 0!==i.fillColors&&(a=[],Array.isArray(i.fillColors)?a=i.fillColors.slice():a.push(i.fillColors)),a}},{key:\"handlePatternFill\",value:function(t){var e=t.fillConfig,i=t.patternFill,a=t.fillColor,s=t.fillOpacity,r=t.defaultColor,o=this.w.config.fill;e&&(o=e);var n=this.opts,l=new m(this.ctx),h=Array.isArray(o.pattern.strokeWidth)?o.pattern.strokeWidth[this.seriesIndex]:o.pattern.strokeWidth,c=a;Array.isArray(o.pattern.style)?i=void 0!==o.pattern.style[n.seriesNumber]?l.drawPattern(o.pattern.style[n.seriesNumber],o.pattern.width,o.pattern.height,c,h,s):r:i=l.drawPattern(o.pattern.style,o.pattern.width,o.pattern.height,c,h,s);return i}},{key:\"handleGradientFill\",value:function(t){var i=t.fillColor,a=t.fillOpacity,s=t.fillConfig,r=t.i,o=this.w.config.fill;s&&(o=e(e({},o),s));var n,l=this.opts,h=new m(this.ctx),c=new x,d=o.gradient.type,g=i,u=void 0===o.gradient.opacityFrom?a:Array.isArray(o.gradient.opacityFrom)?o.gradient.opacityFrom[r]:o.gradient.opacityFrom;g.indexOf(\"rgba\")>-1&&(u=x.getOpacityFromRGBA(g));var f=void 0===o.gradient.opacityTo?a:Array.isArray(o.gradient.opacityTo)?o.gradient.opacityTo[r]:o.gradient.opacityTo;if(void 0===o.gradient.gradientToColors||0===o.gradient.gradientToColors.length)n=\"dark\"===o.gradient.shade?c.shadeColor(-1*parseFloat(o.gradient.shadeIntensity),i.indexOf(\"rgb\")>-1?x.rgb2hex(i):i):c.shadeColor(parseFloat(o.gradient.shadeIntensity),i.indexOf(\"rgb\")>-1?x.rgb2hex(i):i);else if(o.gradient.gradientToColors[l.seriesNumber]){var p=o.gradient.gradientToColors[l.seriesNumber];n=p,p.indexOf(\"rgba\")>-1&&(f=x.getOpacityFromRGBA(p))}else n=i;if(o.gradient.gradientFrom&&(g=o.gradient.gradientFrom),o.gradient.gradientTo&&(n=o.gradient.gradientTo),o.gradient.inverseColors){var b=g;g=n,n=b}return g.indexOf(\"rgb\")>-1&&(g=x.rgb2hex(g)),n.indexOf(\"rgb\")>-1&&(n=x.rgb2hex(n)),h.drawGradient(d,g,n,u,f,l.size,o.gradient.stops,o.gradient.colorStops,r)}}]),t}(),D=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"setGlobalMarkerSize\",value:function(){var t=this.w;if(t.globals.markers.size=Array.isArray(t.config.markers.size)?t.config.markers.size:[t.config.markers.size],t.globals.markers.size.length>0){if(t.globals.markers.size.length<t.globals.series.length+1)for(var e=0;e<=t.globals.series.length;e++)void 0===t.globals.markers.size[e]&&t.globals.markers.size.push(t.globals.markers.size[0])}else t.globals.markers.size=t.config.series.map((function(e){return t.config.markers.size}))}},{key:\"plotChartMarkers\",value:function(t,e,i,a){var s,r=arguments.length>4&&void 0!==arguments[4]&&arguments[4],o=this.w,n=e,l=t,h=null,c=new m(this.ctx),d=o.config.markers.discrete&&o.config.markers.discrete.length;if((o.globals.markers.size[e]>0||r||d)&&(h=c.group({class:r||d?\"\":\"apexcharts-series-markers\"})).attr(\"clip-path\",\"url(#gridRectMarkerMask\".concat(o.globals.cuid,\")\")),Array.isArray(l.x))for(var g=0;g<l.x.length;g++){var u=i;1===i&&0===g&&(u=0),1===i&&1===g&&(u=1);var f=\"apexcharts-marker\";\"line\"!==o.config.chart.type&&\"area\"!==o.config.chart.type||o.globals.comboCharts||o.config.tooltip.intersect||(f+=\" no-pointer-events\");var p=Array.isArray(o.config.markers.size)?o.globals.markers.size[e]>0:o.config.markers.size>0;if(p||r||d){x.isNumber(l.y[g])?f+=\" w\".concat(x.randomId()):f=\"apexcharts-nullpoint\";var b=this.getMarkerConfig({cssClass:f,seriesIndex:e,dataPointIndex:u});o.config.series[n].data[u]&&(o.config.series[n].data[u].fillColor&&(b.pointFillColor=o.config.series[n].data[u].fillColor),o.config.series[n].data[u].strokeColor&&(b.pointStrokeColor=o.config.series[n].data[u].strokeColor)),a&&(b.pSize=a),(s=c.drawMarker(l.x[g],l.y[g],b)).attr(\"rel\",u),s.attr(\"j\",u),s.attr(\"index\",e),s.node.setAttribute(\"default-marker-size\",b.pSize);var y=new v(this.ctx);y.setSelectionFilter(s,e,u),this.addEvents(s),h&&h.add(s)}else void 0===o.globals.pointsArray[e]&&(o.globals.pointsArray[e]=[]),o.globals.pointsArray[e].push([l.x[g],l.y[g]])}return h}},{key:\"getMarkerConfig\",value:function(t){var e=t.cssClass,i=t.seriesIndex,a=t.dataPointIndex,s=void 0===a?null:a,r=t.finishRadius,o=void 0===r?null:r,n=this.w,l=this.getMarkerStyle(i),h=n.globals.markers.size[i],c=n.config.markers;return null!==s&&c.discrete.length&&c.discrete.map((function(t){t.seriesIndex===i&&t.dataPointIndex===s&&(l.pointStrokeColor=t.strokeColor,l.pointFillColor=t.fillColor,h=t.size,l.pointShape=t.shape)})),{pSize:null===o?h:o,pRadius:c.radius,width:Array.isArray(c.width)?c.width[i]:c.width,height:Array.isArray(c.height)?c.height[i]:c.height,pointStrokeWidth:Array.isArray(c.strokeWidth)?c.strokeWidth[i]:c.strokeWidth,pointStrokeColor:l.pointStrokeColor,pointFillColor:l.pointFillColor,shape:l.pointShape||(Array.isArray(c.shape)?c.shape[i]:c.shape),class:e,pointStrokeOpacity:Array.isArray(c.strokeOpacity)?c.strokeOpacity[i]:c.strokeOpacity,pointStrokeDashArray:Array.isArray(c.strokeDashArray)?c.strokeDashArray[i]:c.strokeDashArray,pointFillOpacity:Array.isArray(c.fillOpacity)?c.fillOpacity[i]:c.fillOpacity,seriesIndex:i}}},{key:\"addEvents\",value:function(t){var e=this.w,i=new m(this.ctx);t.node.addEventListener(\"mouseenter\",i.pathMouseEnter.bind(this.ctx,t)),t.node.addEventListener(\"mouseleave\",i.pathMouseLeave.bind(this.ctx,t)),t.node.addEventListener(\"mousedown\",i.pathMouseDown.bind(this.ctx,t)),t.node.addEventListener(\"click\",e.config.markers.onClick),t.node.addEventListener(\"dblclick\",e.config.markers.onDblClick),t.node.addEventListener(\"touchstart\",i.pathMouseDown.bind(this.ctx,t),{passive:!0})}},{key:\"getMarkerStyle\",value:function(t){var e=this.w,i=e.globals.markers.colors,a=e.config.markers.strokeColor||e.config.markers.strokeColors;return{pointStrokeColor:Array.isArray(a)?a[t]:a,pointFillColor:Array.isArray(i)?i[t]:i}}}]),t}(),H=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled}return r(t,[{key:\"draw\",value:function(t,e,i){var a=this.w,s=new m(this.ctx),r=i.realIndex,o=i.pointsPos,n=i.zRatio,l=i.elParent,h=s.group({class:\"apexcharts-series-markers apexcharts-series-\".concat(a.config.chart.type)});if(h.attr(\"clip-path\",\"url(#gridRectMarkerMask\".concat(a.globals.cuid,\")\")),Array.isArray(o.x))for(var c=0;c<o.x.length;c++){var d=e+1,g=!0;0===e&&0===c&&(d=0),0===e&&1===c&&(d=1);var u=0,f=a.globals.markers.size[r];if(n!==1/0){var p=a.config.plotOptions.bubble;f=a.globals.seriesZ[r][d],p.zScaling&&(f/=n),p.minBubbleRadius&&f<p.minBubbleRadius&&(f=p.minBubbleRadius),p.maxBubbleRadius&&f>p.maxBubbleRadius&&(f=p.maxBubbleRadius)}a.config.chart.animations.enabled||(u=f);var x=o.x[c],b=o.y[c];if(u=u||0,null!==b&&void 0!==a.globals.series[r][d]||(g=!1),g){var v=this.drawPoint(x,b,u,f,r,d,e);h.add(v)}l.add(h)}}},{key:\"drawPoint\",value:function(t,e,i,a,s,r,o){var n=this.w,l=s,h=new b(this.ctx),c=new v(this.ctx),d=new R(this.ctx),g=new D(this.ctx),u=new m(this.ctx),f=g.getMarkerConfig({cssClass:\"apexcharts-marker\",seriesIndex:l,dataPointIndex:r,finishRadius:\"bubble\"===n.config.chart.type||n.globals.comboCharts&&n.config.series[s]&&\"bubble\"===n.config.series[s].type?a:null});a=f.pSize;var p,x=d.fillPath({seriesNumber:s,dataPointIndex:r,color:f.pointFillColor,patternUnits:\"objectBoundingBox\",value:n.globals.series[s][o]});if(\"circle\"===f.shape?p=u.drawCircle(i):\"square\"!==f.shape&&\"rect\"!==f.shape||(p=u.drawRect(0,0,f.width-f.pointStrokeWidth/2,f.height-f.pointStrokeWidth/2,f.pRadius)),n.config.series[l].data[r]&&n.config.series[l].data[r].fillColor&&(x=n.config.series[l].data[r].fillColor),p.attr({x:t-f.width/2-f.pointStrokeWidth/2,y:e-f.height/2-f.pointStrokeWidth/2,cx:t,cy:e,fill:x,\"fill-opacity\":f.pointFillOpacity,stroke:f.pointStrokeColor,r:a,\"stroke-width\":f.pointStrokeWidth,\"stroke-dasharray\":f.pointStrokeDashArray,\"stroke-opacity\":f.pointStrokeOpacity}),n.config.chart.dropShadow.enabled){var y=n.config.chart.dropShadow;c.dropShadow(p,y,s)}if(!this.initialAnim||n.globals.dataChanged||n.globals.resized)n.globals.animationEnded=!0;else{var w=n.config.chart.animations.speed;h.animateMarker(p,0,\"circle\"===f.shape?a:{width:f.width,height:f.height},w,n.globals.easing,(function(){window.setTimeout((function(){h.animationCompleted(p)}),100)}))}if(n.globals.dataChanged&&\"circle\"===f.shape)if(this.dynamicAnim){var k,A,S,C,L=n.config.chart.animations.dynamicAnimation.speed;null!=(C=n.globals.previousPaths[s]&&n.globals.previousPaths[s][o])&&(k=C.x,A=C.y,S=void 0!==C.r?C.r:a);for(var P=0;P<n.globals.collapsedSeries.length;P++)n.globals.collapsedSeries[P].index===s&&(L=1,a=0);0===t&&0===e&&(a=0),h.animateCircle(p,{cx:k,cy:A,r:S},{cx:t,cy:e,r:a},L,n.globals.easing)}else p.attr({r:a});return p.attr({rel:r,j:r,index:s,\"default-marker-size\":a}),c.setSelectionFilter(p,s,r),g.addEvents(p),p.node.classList.add(\"apexcharts-marker\"),p}},{key:\"centerTextInBubble\",value:function(t){var e=this.w;return{y:t+=parseInt(e.config.dataLabels.style.fontSize,10)/4}}}]),t}(),O=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"dataLabelsCorrection\",value:function(t,e,i,a,s,r,o){var n=this.w,l=!1,h=new m(this.ctx).getTextRects(i,o),c=h.width,d=h.height;e<0&&(e=0),e>n.globals.gridHeight+d&&(e=n.globals.gridHeight+d/2),void 0===n.globals.dataLabelsRects[a]&&(n.globals.dataLabelsRects[a]=[]),n.globals.dataLabelsRects[a].push({x:t,y:e,width:c,height:d});var g=n.globals.dataLabelsRects[a].length-2,u=void 0!==n.globals.lastDrawnDataLabelsIndexes[a]?n.globals.lastDrawnDataLabelsIndexes[a][n.globals.lastDrawnDataLabelsIndexes[a].length-1]:0;if(void 0!==n.globals.dataLabelsRects[a][g]){var f=n.globals.dataLabelsRects[a][u];(t>f.x+f.width+2||e>f.y+f.height+2||t+c<f.x)&&(l=!0)}return(0===s||r)&&(l=!0),{x:t,y:e,textRects:h,drawnextLabel:l}}},{key:\"drawDataLabel\",value:function(t){var e=this,i=t.type,a=t.pos,s=t.i,r=t.j,o=t.isRangeStart,n=t.strokeWidth,l=void 0===n?2:n,h=this.w,c=new m(this.ctx),d=h.config.dataLabels,g=0,u=0,f=r,p=null;if(!d.enabled||!Array.isArray(a.x))return p;p=c.group({class:\"apexcharts-data-labels\"});for(var x=0;x<a.x.length;x++)if(g=a.x[x]+d.offsetX,u=a.y[x]+d.offsetY+l,!isNaN(g)){1===r&&0===x&&(f=0),1===r&&1===x&&(f=1);var b=h.globals.series[s][f];\"rangeArea\"===i&&(b=o?h.globals.seriesRangeStart[s][f]:h.globals.seriesRangeEnd[s][f]);var v=\"\",y=function(t){return h.config.dataLabels.formatter(t,{ctx:e.ctx,seriesIndex:s,dataPointIndex:f,w:h})};if(\"bubble\"===h.config.chart.type)v=y(b=h.globals.seriesZ[s][f]),u=a.y[x],u=new H(this.ctx).centerTextInBubble(u,s,f).y;else void 0!==b&&(v=y(b));this.plotDataLabelsText({x:g,y:u,text:v,i:s,j:f,parent:p,offsetCorrection:!0,dataLabelsConfig:h.config.dataLabels})}return p}},{key:\"plotDataLabelsText\",value:function(t){var e=this.w,i=new m(this.ctx),a=t.x,s=t.y,r=t.i,o=t.j,n=t.text,l=t.textAnchor,h=t.fontSize,c=t.parent,d=t.dataLabelsConfig,g=t.color,u=t.alwaysDrawDataLabel,f=t.offsetCorrection;if(!(Array.isArray(e.config.dataLabels.enabledOnSeries)&&e.config.dataLabels.enabledOnSeries.indexOf(r)<0)){var p={x:a,y:s,drawnextLabel:!0,textRects:null};f&&(p=this.dataLabelsCorrection(a,s,n,r,o,u,parseInt(d.style.fontSize,10))),e.globals.zoomed||(a=p.x,s=p.y),p.textRects&&(a<-10-p.textRects.width||a>e.globals.gridWidth+p.textRects.width+10)&&(n=\"\");var x=e.globals.dataLabels.style.colors[r];((\"bar\"===e.config.chart.type||\"rangeBar\"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||e.config.dataLabels.distributed)&&(x=e.globals.dataLabels.style.colors[o]),\"function\"==typeof x&&(x=x({series:e.globals.series,seriesIndex:r,dataPointIndex:o,w:e})),g&&(x=g);var b=d.offsetX,y=d.offsetY;if(\"bar\"!==e.config.chart.type&&\"rangeBar\"!==e.config.chart.type||(b=0,y=0),p.drawnextLabel){var w=i.drawText({width:100,height:parseInt(d.style.fontSize,10),x:a+b,y:s+y,foreColor:x,textAnchor:l||d.textAnchor,text:n,fontSize:h||d.style.fontSize,fontFamily:d.style.fontFamily,fontWeight:d.style.fontWeight||\"normal\"});if(w.attr({class:\"apexcharts-datalabel\",cx:a,cy:s}),d.dropShadow.enabled){var k=d.dropShadow;new v(this.ctx).dropShadow(w,k)}c.add(w),void 0===e.globals.lastDrawnDataLabelsIndexes[r]&&(e.globals.lastDrawnDataLabelsIndexes[r]=[]),e.globals.lastDrawnDataLabelsIndexes[r].push(o)}}}},{key:\"addBackgroundToDataLabel\",value:function(t,e){var i=this.w,a=i.config.dataLabels.background,s=a.padding,r=a.padding/2,o=e.width,n=e.height,l=new m(this.ctx).drawRect(e.x-s,e.y-r/2,o+2*s,n+r,a.borderRadius,\"transparent\"===i.config.chart.background?\"#fff\":i.config.chart.background,a.opacity,a.borderWidth,a.borderColor);a.dropShadow.enabled&&new v(this.ctx).dropShadow(l,a.dropShadow);return l}},{key:\"dataLabelsBackground\",value:function(){var t=this.w;if(\"bubble\"!==t.config.chart.type)for(var e=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-datalabels text\"),i=0;i<e.length;i++){var a=e[i],s=a.getBBox(),r=null;if(s.width&&s.height&&(r=this.addBackgroundToDataLabel(a,s)),r){a.parentNode.insertBefore(r.node,a);var o=a.getAttribute(\"fill\");t.config.chart.animations.enabled&&!t.globals.resized&&!t.globals.dataChanged?r.animate().attr({fill:o}):r.attr({fill:o}),a.setAttribute(\"fill\",t.config.dataLabels.background.foreColor)}}}},{key:\"bringForward\",value:function(){for(var t=this.w,e=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-datalabels\"),i=t.globals.dom.baseEl.querySelector(\".apexcharts-plot-series:last-child\"),a=0;a<e.length;a++)i&&i.insertBefore(e[a],i.nextSibling)}}]),t}(),N=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.legendInactiveClass=\"legend-mouseover-inactive\"}return r(t,[{key:\"getAllSeriesEls\",value:function(){return this.w.globals.dom.baseEl.getElementsByClassName(\"apexcharts-series\")}},{key:\"getSeriesByName\",value:function(t){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-inner .apexcharts-series[seriesName='\".concat(x.escapeString(t),\"']\"))}},{key:\"isSeriesHidden\",value:function(t){var e=this.getSeriesByName(t),i=parseInt(e.getAttribute(\"data:realIndex\"),10);return{isHidden:e.classList.contains(\"apexcharts-series-collapsed\"),realIndex:i}}},{key:\"addCollapsedClassToSeries\",value:function(t,e){var i=this.w;function a(i){for(var a=0;a<i.length;a++)i[a].index===e&&t.node.classList.add(\"apexcharts-series-collapsed\")}a(i.globals.collapsedSeries),a(i.globals.ancillaryCollapsedSeries)}},{key:\"toggleSeries\",value:function(t){var e=this.isSeriesHidden(t);return this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,e.isHidden),e.isHidden}},{key:\"showSeries\",value:function(t){var e=this.isSeriesHidden(t);e.isHidden&&this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!0)}},{key:\"hideSeries\",value:function(t){var e=this.isSeriesHidden(t);e.isHidden||this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!1)}},{key:\"resetSeries\",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w,s=x.clone(a.globals.initialSeries);a.globals.previousPaths=[],i?(a.globals.collapsedSeries=[],a.globals.ancillaryCollapsedSeries=[],a.globals.collapsedSeriesIndices=[],a.globals.ancillaryCollapsedSeriesIndices=[]):s=this.emptyCollapsedSeries(s),a.config.series=s,t&&(e&&(a.globals.zoomed=!1,this.ctx.updateHelpers.revertDefaultAxisMinMax()),this.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled))}},{key:\"emptyCollapsedSeries\",value:function(t){for(var e=this.w,i=0;i<t.length;i++)e.globals.collapsedSeriesIndices.indexOf(i)>-1&&(t[i].data=[]);return t}},{key:\"toggleSeriesOnHover\",value:function(t,e){var i=this.w;e||(e=t.target);var a=i.globals.dom.baseEl.querySelectorAll(\".apexcharts-series, .apexcharts-datalabels\");if(\"mousemove\"===t.type){var s=parseInt(e.getAttribute(\"rel\"),10)-1,r=null,o=null;i.globals.axisCharts||\"radialBar\"===i.config.chart.type?i.globals.axisCharts?(r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(s,\"']\")),o=i.globals.dom.baseEl.querySelector(\".apexcharts-datalabels[data\\\\:realIndex='\".concat(s,\"']\"))):r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(s+1,\"']\")):r=i.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(s+1,\"'] path\"));for(var n=0;n<a.length;n++)a[n].classList.add(this.legendInactiveClass);null!==r&&(i.globals.axisCharts||r.parentNode.classList.remove(this.legendInactiveClass),r.classList.remove(this.legendInactiveClass),null!==o&&o.classList.remove(this.legendInactiveClass))}else if(\"mouseout\"===t.type)for(var l=0;l<a.length;l++)a[l].classList.remove(this.legendInactiveClass)}},{key:\"highlightRangeInSeries\",value:function(t,e){var i=this,a=this.w,s=a.globals.dom.baseEl.getElementsByClassName(\"apexcharts-heatmap-rect\"),r=function(t){for(var e=0;e<s.length;e++)s[e].classList[t](i.legendInactiveClass)};if(\"mousemove\"===t.type){var o=parseInt(e.getAttribute(\"rel\"),10)-1;r(\"add\"),function(t){for(var e=0;e<s.length;e++){var a=parseInt(s[e].getAttribute(\"val\"),10);a>=t.from&&a<=t.to&&s[e].classList.remove(i.legendInactiveClass)}}(a.config.plotOptions.heatmap.colorScale.ranges[o])}else\"mouseout\"===t.type&&r(\"remove\")}},{key:\"getActiveConfigSeriesIndex\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"asc\",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],i=this.w,a=0;if(i.config.series.length>1)for(var s=i.config.series.map((function(t,a){return t.data&&t.data.length>0&&-1===i.globals.collapsedSeriesIndices.indexOf(a)&&(!i.globals.comboCharts||0===e.length||e.length&&e.indexOf(i.config.series[a].type)>-1)?a:-1})),r=\"asc\"===t?0:s.length-1;\"asc\"===t?r<s.length:r>=0;\"asc\"===t?r++:r--)if(-1!==s[r]){a=s[r];break}return a}},{key:\"getBarSeriesIndices\",value:function(){return this.w.globals.comboCharts?this.w.config.series.map((function(t,e){return\"bar\"===t.type||\"column\"===t.type?e:-1})).filter((function(t){return-1!==t})):this.w.config.series.map((function(t,e){return e}))}},{key:\"getPreviousPaths\",value:function(){var t=this.w;function e(e,i,a){for(var s=e[i].childNodes,r={type:a,paths:[],realIndex:e[i].getAttribute(\"data:realIndex\")},o=0;o<s.length;o++)if(s[o].hasAttribute(\"pathTo\")){var n=s[o].getAttribute(\"pathTo\");r.paths.push({d:n})}t.globals.previousPaths.push(r)}t.globals.previousPaths=[];[\"line\",\"area\",\"bar\",\"rangebar\",\"rangeArea\",\"candlestick\",\"radar\"].forEach((function(i){for(var a,s=(a=i,t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(a,\"-series .apexcharts-series\"))),r=0;r<s.length;r++)e(s,r,i)})),this.handlePrevBubbleScatterPaths(\"bubble\"),this.handlePrevBubbleScatterPaths(\"scatter\");var i=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t.config.chart.type,\" .apexcharts-series\"));if(i.length>0)for(var a=function(e){for(var i=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t.config.chart.type,\" .apexcharts-series[data\\\\:realIndex='\").concat(e,\"'] rect\")),a=[],s=function(t){var e=function(e){return i[t].getAttribute(e)},s={x:parseFloat(e(\"x\")),y:parseFloat(e(\"y\")),width:parseFloat(e(\"width\")),height:parseFloat(e(\"height\"))};a.push({rect:s,color:i[t].getAttribute(\"color\")})},r=0;r<i.length;r++)s(r);t.globals.previousPaths.push(a)},s=0;s<i.length;s++)a(s);t.globals.axisCharts||(t.globals.previousPaths=t.globals.series)}},{key:\"handlePrevBubbleScatterPaths\",value:function(t){var e=this.w,i=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t,\"-series .apexcharts-series\"));if(i.length>0)for(var a=0;a<i.length;a++){for(var s=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(t,\"-series .apexcharts-series[data\\\\:realIndex='\").concat(a,\"'] circle\")),r=[],o=0;o<s.length;o++)r.push({x:s[o].getAttribute(\"cx\"),y:s[o].getAttribute(\"cy\"),r:s[o].getAttribute(\"r\")});e.globals.previousPaths.push(r)}}},{key:\"clearPreviousPaths\",value:function(){var t=this.w;t.globals.previousPaths=[],t.globals.allSeriesCollapsed=!1}},{key:\"handleNoData\",value:function(){var t=this.w,e=t.config.noData,i=new m(this.ctx),a=t.globals.svgWidth/2,s=t.globals.svgHeight/2,r=\"middle\";if(t.globals.noData=!0,t.globals.animationEnded=!0,\"left\"===e.align?(a=10,r=\"start\"):\"right\"===e.align&&(a=t.globals.svgWidth-10,r=\"end\"),\"top\"===e.verticalAlign?s=50:\"bottom\"===e.verticalAlign&&(s=t.globals.svgHeight-50),a+=e.offsetX,s=s+parseInt(e.style.fontSize,10)+2+e.offsetY,void 0!==e.text&&\"\"!==e.text){var o=i.drawText({x:a,y:s,text:e.text,textAnchor:r,fontSize:e.style.fontSize,fontFamily:e.style.fontFamily,foreColor:e.style.color,opacity:1,class:\"apexcharts-text-nodata\"});t.globals.dom.Paper.add(o)}}},{key:\"setNullSeriesToZeroValues\",value:function(t){for(var e=this.w,i=0;i<t.length;i++)if(0===t[i].length)for(var a=0;a<t[e.globals.maxValsInArrayIndex].length;a++)t[i].push(0);return t}},{key:\"hasAllSeriesEqualX\",value:function(){for(var t=!0,e=this.w,i=this.filteredSeriesX(),a=0;a<i.length-1;a++)if(i[a][0]!==i[a+1][0]){t=!1;break}return e.globals.allSeriesHasEqualX=t,t}},{key:\"filteredSeriesX\",value:function(){var t=this.w.globals.seriesX.map((function(t){return t.length>0?t:[]}));return t}}]),t}(),W=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.twoDSeries=[],this.threeDSeries=[],this.twoDSeriesX=[],this.seriesGoals=[],this.coreUtils=new y(this.ctx)}return r(t,[{key:\"isMultiFormat\",value:function(){return this.isFormatXY()||this.isFormat2DArray()}},{key:\"isFormatXY\",value:function(){var t=this.w.config.series.slice(),e=new N(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&null!==t[this.activeSeriesIndex].data[0]&&void 0!==t[this.activeSeriesIndex].data[0].x&&null!==t[this.activeSeriesIndex].data[0])return!0}},{key:\"isFormat2DArray\",value:function(){var t=this.w.config.series.slice(),e=new N(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&void 0!==t[this.activeSeriesIndex].data[0]&&null!==t[this.activeSeriesIndex].data[0]&&t[this.activeSeriesIndex].data[0].constructor===Array)return!0}},{key:\"handleFormat2DArray\",value:function(t,e){for(var i=this.w.config,a=this.w.globals,s=\"boxPlot\"===i.chart.type||\"boxPlot\"===i.series[e].type,r=0;r<t[e].data.length;r++)if(void 0!==t[e].data[r][1]&&(Array.isArray(t[e].data[r][1])&&4===t[e].data[r][1].length&&!s?this.twoDSeries.push(x.parseNumber(t[e].data[r][1][3])):t[e].data[r].length>=5?this.twoDSeries.push(x.parseNumber(t[e].data[r][4])):this.twoDSeries.push(x.parseNumber(t[e].data[r][1])),a.dataFormatXNumeric=!0),\"datetime\"===i.xaxis.type){var o=new Date(t[e].data[r][0]);o=new Date(o).getTime(),this.twoDSeriesX.push(o)}else this.twoDSeriesX.push(t[e].data[r][0]);for(var n=0;n<t[e].data.length;n++)void 0!==t[e].data[n][2]&&(this.threeDSeries.push(t[e].data[n][2]),a.isDataXYZ=!0)}},{key:\"handleFormatXY\",value:function(t,e){var i=this.w.config,a=this.w.globals,s=new T(this.ctx),r=e;a.collapsedSeriesIndices.indexOf(e)>-1&&(r=this.activeSeriesIndex);for(var o=0;o<t[e].data.length;o++)void 0!==t[e].data[o].y&&(Array.isArray(t[e].data[o].y)?this.twoDSeries.push(x.parseNumber(t[e].data[o].y[t[e].data[o].y.length-1])):this.twoDSeries.push(x.parseNumber(t[e].data[o].y))),void 0!==t[e].data[o].goals&&Array.isArray(t[e].data[o].goals)?(void 0===this.seriesGoals[e]&&(this.seriesGoals[e]=[]),this.seriesGoals[e].push(t[e].data[o].goals)):(void 0===this.seriesGoals[e]&&(this.seriesGoals[e]=[]),this.seriesGoals[e].push(null));for(var n=0;n<t[r].data.length;n++){var l=\"string\"==typeof t[r].data[n].x,h=Array.isArray(t[r].data[n].x),c=!h&&!!s.isValidDate(t[r].data[n].x.toString());if(l||c)if(l||i.xaxis.convertedCatToNumeric){var d=a.isBarHorizontal&&a.isRangeData;\"datetime\"!==i.xaxis.type||d?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[n].x)):this.twoDSeriesX.push(s.parseDate(t[r].data[n].x))}else\"datetime\"===i.xaxis.type?this.twoDSeriesX.push(s.parseDate(t[r].data[n].x.toString())):(a.dataFormatXNumeric=!0,a.isXNumeric=!0,this.twoDSeriesX.push(parseFloat(t[r].data[n].x)));else h?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[n].x)):(a.isXNumeric=!0,a.dataFormatXNumeric=!0,this.twoDSeriesX.push(t[r].data[n].x))}if(t[e].data[0]&&void 0!==t[e].data[0].z){for(var g=0;g<t[e].data.length;g++)this.threeDSeries.push(t[e].data[g].z);a.isDataXYZ=!0}}},{key:\"handleRangeData\",value:function(t,e){var i=this.w.globals,a={};return this.isFormat2DArray()?a=this.handleRangeDataFormat(\"array\",t,e):this.isFormatXY()&&(a=this.handleRangeDataFormat(\"xy\",t,e)),i.seriesRangeStart.push(a.start),i.seriesRangeEnd.push(a.end),i.seriesRange.push(a.rangeUniques),i.seriesRange.forEach((function(t,e){t&&t.forEach((function(t,e){t.y.forEach((function(e,i){for(var a=0;a<t.y.length;a++)if(i!==a){var s=e.y1,r=e.y2,o=t.y[a].y1;s<=t.y[a].y2&&o<=r&&(t.overlaps.indexOf(e.rangeName)<0&&t.overlaps.push(e.rangeName),t.overlaps.indexOf(t.y[a].rangeName)<0&&t.overlaps.push(t.y[a].rangeName))}}))}))})),a}},{key:\"handleCandleStickBoxData\",value:function(t,e){var i=this.w.globals,a={};return this.isFormat2DArray()?a=this.handleCandleStickBoxDataFormat(\"array\",t,e):this.isFormatXY()&&(a=this.handleCandleStickBoxDataFormat(\"xy\",t,e)),i.seriesCandleO[e]=a.o,i.seriesCandleH[e]=a.h,i.seriesCandleM[e]=a.m,i.seriesCandleL[e]=a.l,i.seriesCandleC[e]=a.c,a}},{key:\"handleRangeDataFormat\",value:function(t,e,i){var a=[],s=[],r=e[i].data.filter((function(t,e,i){return e===i.findIndex((function(e){return e.x===t.x}))})).map((function(t,e){return{x:t.x,overlaps:[],y:[]}}));if(\"array\"===t)for(var o=0;o<e[i].data.length;o++)Array.isArray(e[i].data[o])?(a.push(e[i].data[o][1][0]),s.push(e[i].data[o][1][1])):(a.push(e[i].data[o]),s.push(e[i].data[o]));else if(\"xy\"===t)for(var n=function(t){var o=Array.isArray(e[i].data[t].y),n=x.randomId(),l=e[i].data[t].x,h={y1:o?e[i].data[t].y[0]:e[i].data[t].y,y2:o?e[i].data[t].y[1]:e[i].data[t].y,rangeName:n};e[i].data[t].rangeName=n;var c=r.findIndex((function(t){return t.x===l}));r[c].y.push(h),a.push(h.y1),s.push(h.y2)},l=0;l<e[i].data.length;l++)n(l);return{start:a,end:s,rangeUniques:r}}},{key:\"handleCandleStickBoxDataFormat\",value:function(t,e,i){var a=this.w,s=\"boxPlot\"===a.config.chart.type||\"boxPlot\"===a.config.series[i].type,r=[],o=[],n=[],l=[],h=[];if(\"array\"===t)if(s&&6===e[i].data[0].length||!s&&5===e[i].data[0].length)for(var c=0;c<e[i].data.length;c++)r.push(e[i].data[c][1]),o.push(e[i].data[c][2]),s?(n.push(e[i].data[c][3]),l.push(e[i].data[c][4]),h.push(e[i].data[c][5])):(l.push(e[i].data[c][3]),h.push(e[i].data[c][4]));else for(var d=0;d<e[i].data.length;d++)Array.isArray(e[i].data[d][1])&&(r.push(e[i].data[d][1][0]),o.push(e[i].data[d][1][1]),s?(n.push(e[i].data[d][1][2]),l.push(e[i].data[d][1][3]),h.push(e[i].data[d][1][4])):(l.push(e[i].data[d][1][2]),h.push(e[i].data[d][1][3])));else if(\"xy\"===t)for(var g=0;g<e[i].data.length;g++)Array.isArray(e[i].data[g].y)&&(r.push(e[i].data[g].y[0]),o.push(e[i].data[g].y[1]),s?(n.push(e[i].data[g].y[2]),l.push(e[i].data[g].y[3]),h.push(e[i].data[g].y[4])):(l.push(e[i].data[g].y[2]),h.push(e[i].data[g].y[3])));return{o:r,h:o,m:n,l:l,c:h}}},{key:\"parseDataAxisCharts\",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.ctx,a=this.w.config,s=this.w.globals,r=new T(i),o=a.labels.length>0?a.labels.slice():a.xaxis.categories.slice();s.isRangeBar=\"rangeBar\"===a.chart.type&&s.isBarHorizontal,s.hasGroups=\"category\"===a.xaxis.type&&a.xaxis.group.groups.length>0,s.hasGroups&&(s.groups=a.xaxis.group.groups);for(var n=function(){for(var t=0;t<o.length;t++)if(\"string\"==typeof o[t]){if(!r.isValidDate(o[t]))throw new Error(\"You have provided invalid Date format. Please provide a valid JavaScript Date\");e.twoDSeriesX.push(r.parseDate(o[t]))}else e.twoDSeriesX.push(o[t])},l=0;l<t.length;l++){if(this.twoDSeries=[],this.twoDSeriesX=[],this.threeDSeries=[],void 0===t[l].data)return void console.error(\"It is a possibility that you may have not included 'data' property in series.\");if(\"rangeBar\"!==a.chart.type&&\"rangeArea\"!==a.chart.type&&\"rangeBar\"!==t[l].type&&\"rangeArea\"!==t[l].type||(s.isRangeData=!0,s.isComboCharts?\"rangeBar\"!==t[l].type&&\"rangeArea\"!==t[l].type||this.handleRangeData(t,l):\"rangeBar\"!==a.chart.type&&\"rangeArea\"!==a.chart.type||this.handleRangeData(t,l)),this.isMultiFormat())this.isFormat2DArray()?this.handleFormat2DArray(t,l):this.isFormatXY()&&this.handleFormatXY(t,l),\"candlestick\"!==a.chart.type&&\"candlestick\"!==t[l].type&&\"boxPlot\"!==a.chart.type&&\"boxPlot\"!==t[l].type||this.handleCandleStickBoxData(t,l),s.series.push(this.twoDSeries),s.labels.push(this.twoDSeriesX),s.seriesX.push(this.twoDSeriesX),s.seriesGoals=this.seriesGoals,l!==this.activeSeriesIndex||this.fallbackToCategory||(s.isXNumeric=!0);else{\"datetime\"===a.xaxis.type?(s.isXNumeric=!0,n(),s.seriesX.push(this.twoDSeriesX)):\"numeric\"===a.xaxis.type&&(s.isXNumeric=!0,o.length>0&&(this.twoDSeriesX=o,s.seriesX.push(this.twoDSeriesX))),s.labels.push(this.twoDSeriesX);var h=t[l].data.map((function(t){return x.parseNumber(t)}));s.series.push(h)}s.seriesZ.push(this.threeDSeries),void 0!==t[l].name?s.seriesNames.push(t[l].name):s.seriesNames.push(\"series-\"+parseInt(l+1,10)),void 0!==t[l].color?s.seriesColors.push(t[l].color):s.seriesColors.push(void 0)}return this.w}},{key:\"parseDataNonAxisCharts\",value:function(t){var e=this.w.globals,i=this.w.config;e.series=t.slice(),e.seriesNames=i.labels.slice();for(var a=0;a<e.series.length;a++)void 0===e.seriesNames[a]&&e.seriesNames.push(\"series-\"+(a+1));return this.w}},{key:\"handleExternalLabelsData\",value:function(t){var e=this.w.config,i=this.w.globals;if(e.xaxis.categories.length>0)i.labels=e.xaxis.categories;else if(e.labels.length>0)i.labels=e.labels.slice();else if(this.fallbackToCategory){if(i.labels=i.labels[0],i.seriesRange.length&&(i.seriesRange.map((function(t){t.forEach((function(t){i.labels.indexOf(t.x)<0&&t.x&&i.labels.push(t.x)}))})),i.labels=i.labels.filter((function(t,e,i){return i.indexOf(t)===e}))),e.xaxis.convertedCatToNumeric)new X(e).convertCatToNumericXaxis(e,this.ctx,i.seriesX[0]),this._generateExternalLabels(t)}else this._generateExternalLabels(t)}},{key:\"_generateExternalLabels\",value:function(t){var e=this.w.globals,i=this.w.config,a=[];if(e.axisCharts){if(e.series.length>0)if(this.isFormatXY())for(var s=i.series.map((function(t,e){return t.data.filter((function(t,e,i){return i.findIndex((function(e){return e.x===t.x}))===e}))})),r=s.reduce((function(t,e,i,a){return a[t].length>e.length?t:i}),0),o=0;o<s[r].length;o++)a.push(o+1);else for(var n=0;n<e.series[e.maxValsInArrayIndex].length;n++)a.push(n+1);e.seriesX=[];for(var l=0;l<t.length;l++)e.seriesX.push(a);e.isXNumeric=!0}if(0===a.length){a=e.axisCharts?[]:e.series.map((function(t,e){return e+1}));for(var h=0;h<t.length;h++)e.seriesX.push(a)}e.labels=a,i.xaxis.convertedCatToNumeric&&(e.categoryLabels=a.map((function(t){return i.xaxis.labels.formatter(t)}))),e.noLabelsProvided=!0}},{key:\"parseData\",value:function(t){var e=this.w,i=e.config,a=e.globals;if(this.excludeCollapsedSeriesInYAxis(),this.fallbackToCategory=!1,this.ctx.core.resetGlobals(),this.ctx.core.isMultipleY(),a.axisCharts?(this.parseDataAxisCharts(t),this.coreUtils.getLargestSeries()):this.parseDataNonAxisCharts(t),\"bar\"===i.chart.type&&i.chart.stacked){var s=new N(this.ctx);a.series=s.setNullSeriesToZeroValues(a.series)}this.coreUtils.getSeriesTotals(),a.axisCharts&&(a.stackedSeriesTotals=this.coreUtils.getStackedSeriesTotals()),this.coreUtils.getPercentSeries(),a.dataFormatXNumeric||a.isXNumeric&&(\"numeric\"!==i.xaxis.type||0!==i.labels.length||0!==i.xaxis.categories.length)||this.handleExternalLabelsData(t);for(var r=this.coreUtils.getCategoryLabels(a.labels),o=0;o<r.length;o++)if(Array.isArray(r[o])){a.isMultiLineX=!0;break}}},{key:\"excludeCollapsedSeriesInYAxis\",value:function(){var t=this,e=this.w;e.globals.ignoreYAxisIndexes=e.globals.collapsedSeries.map((function(i,a){if(t.w.globals.isMultipleYAxis&&!e.config.chart.stacked)return i.index}))}}]),t}(),B=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"getLabel\",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:\"12px\",o=!(arguments.length>6&&void 0!==arguments[6])||arguments[6],n=this.w,l=void 0===t[a]?\"\":t[a],h=l,c=n.globals.xLabelFormatter,d=n.config.xaxis.labels.formatter,g=!1,u=new M(this.ctx),f=l;o&&(h=u.xLabelFormat(c,l,f,{i:a,dateFormatter:new T(this.ctx).formatDate,w:n}),void 0!==d&&(h=d(l,t[a],{i:a,dateFormatter:new T(this.ctx).formatDate,w:n})));var p=function(t){var i=null;return e.forEach((function(t){\"month\"===t.unit?i=\"year\":\"day\"===t.unit?i=\"month\":\"hour\"===t.unit?i=\"day\":\"minute\"===t.unit&&(i=\"hour\")})),i===t};e.length>0?(g=p(e[a].unit),i=e[a].position,h=e[a].value):\"datetime\"===n.config.xaxis.type&&void 0===d&&(h=\"\"),void 0===h&&(h=\"\"),h=Array.isArray(h)?h:h.toString();var x=new m(this.ctx),b={};b=n.globals.rotateXLabels&&o?x.getTextRects(h,parseInt(r,10),null,\"rotate(\".concat(n.config.xaxis.labels.rotate,\" 0 0)\"),!1):x.getTextRects(h,parseInt(r,10));var v=!n.config.xaxis.labels.showDuplicates&&this.ctx.timeScale;return!Array.isArray(h)&&(0===h.indexOf(\"NaN\")||0===h.toLowerCase().indexOf(\"invalid\")||h.toLowerCase().indexOf(\"infinity\")>=0||s.indexOf(h)>=0&&v)&&(h=\"\"),{x:i,text:h,textRect:b,isBold:g}}},{key:\"checkLabelBasedOnTickamount\",value:function(t,e,i){var a=this.w,s=a.config.xaxis.tickAmount;return\"dataPoints\"===s&&(s=Math.round(a.globals.gridWidth/120)),s>i||t%Math.round(i/(s+1))==0||(e.text=\"\"),e}},{key:\"checkForOverflowingLabels\",value:function(t,e,i,a,s){var r=this.w;if(0===t&&r.globals.skipFirstTimelinelabel&&(e.text=\"\"),t===i-1&&r.globals.skipLastTimelinelabel&&(e.text=\"\"),r.config.xaxis.labels.hideOverlappingLabels&&a.length>0){var o=s[s.length-1];e.x<o.textRect.width/(r.globals.rotateXLabels?Math.abs(r.config.xaxis.labels.rotate)/12:1.01)+o.x&&(e.text=\"\")}return e}},{key:\"checkForReversedLabels\",value:function(t,e){var i=this.w;return i.config.yaxis[t]&&i.config.yaxis[t].reversed&&e.reverse(),e}},{key:\"isYAxisHidden\",value:function(t){var e=this.w,i=new y(this.ctx);return!e.config.yaxis[t].show||!e.config.yaxis[t].showForNullSeries&&i.isSeriesNull(t)&&-1===e.globals.collapsedSeriesIndices.indexOf(t)}},{key:\"getYAxisForeColor\",value:function(t,e){var i=this.w;return Array.isArray(t)&&i.globals.yAxisScale[e]&&this.ctx.theme.pushExtraColors(t,i.globals.yAxisScale[e].result.length,!1),t}},{key:\"drawYAxisTicks\",value:function(t,e,i,a,s,r,o){var n=this.w,l=new m(this.ctx),h=n.globals.translateY;if(a.show&&e>0){!0===n.config.yaxis[s].opposite&&(t+=a.width);for(var c=e;c>=0;c--){var d=h+e/10+n.config.yaxis[s].labels.offsetY-1;n.globals.isBarHorizontal&&(d=r*c),\"heatmap\"===n.config.chart.type&&(d+=r/2);var g=l.drawLine(t+i.offsetX-a.width+a.offsetX,d+a.offsetY,t+i.offsetX+a.offsetX,d+a.offsetY,a.color);o.add(g),h+=r}}}}]),t}(),G=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"scaleSvgNode\",value:function(t,e){var i=parseFloat(t.getAttributeNS(null,\"width\")),a=parseFloat(t.getAttributeNS(null,\"height\"));t.setAttributeNS(null,\"width\",i*e),t.setAttributeNS(null,\"height\",a*e),t.setAttributeNS(null,\"viewBox\",\"0 0 \"+i+\" \"+a)}},{key:\"fixSvgStringForIe11\",value:function(t){if(!x.isIE11())return t.replace(/&nbsp;/g,\"&#160;\");var e=0,i=t.replace(/xmlns=\"http:\\/\\/www.w3.org\\/2000\\/svg\"/g,(function(t){return 2===++e?'xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:svgjs=\"http://svgjs.dev\"':t}));return i=(i=i.replace(/xmlns:NS\\d+=\"\"/g,\"\")).replace(/NS\\d+:(\\w+:\\w+=\")/g,\"$1\")}},{key:\"getSvgString\",value:function(t){null==t&&(t=1);var e=this.w.globals.dom.Paper.svg();if(1!==t){var i=this.w.globals.dom.Paper.node.cloneNode(!0);this.scaleSvgNode(i,t),e=(new XMLSerializer).serializeToString(i)}return this.fixSvgStringForIe11(e)}},{key:\"cleanup\",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-xcrosshairs\"),i=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-ycrosshairs\"),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-zoom-rect, .apexcharts-selection-rect\");Array.prototype.forEach.call(a,(function(t){t.setAttribute(\"width\",0)})),e&&e[0]&&(e[0].setAttribute(\"x\",-500),e[0].setAttribute(\"x1\",-500),e[0].setAttribute(\"x2\",-500)),i&&i[0]&&(i[0].setAttribute(\"y\",-100),i[0].setAttribute(\"y1\",-100),i[0].setAttribute(\"y2\",-100))}},{key:\"svgUrl\",value:function(){this.cleanup();var t=this.getSvgString(),e=new Blob([t],{type:\"image/svg+xml;charset=utf-8\"});return URL.createObjectURL(e)}},{key:\"dataURI\",value:function(t){var e=this;return new Promise((function(i){var a=e.w,s=t?t.scale||t.width/a.globals.svgWidth:1;e.cleanup();var r=document.createElement(\"canvas\");r.width=a.globals.svgWidth*s,r.height=parseInt(a.globals.dom.elWrap.style.height,10)*s;var o=\"transparent\"===a.config.chart.background?\"#fff\":a.config.chart.background,n=r.getContext(\"2d\");n.fillStyle=o,n.fillRect(0,0,r.width*s,r.height*s);var l=e.getSvgString(s);if(window.canvg&&x.isIE11()){var h=window.canvg.Canvg.fromString(n,l,{ignoreClear:!0,ignoreDimensions:!0});h.start();var c=r.msToBlob();h.stop(),i({blob:c})}else{var d=\"data:image/svg+xml,\"+encodeURIComponent(l),g=new Image;g.crossOrigin=\"anonymous\",g.onload=function(){if(n.drawImage(g,0,0),r.msToBlob){var t=r.msToBlob();i({blob:t})}else{var e=r.toDataURL(\"image/png\");i({imgURI:e})}},g.src=d}}))}},{key:\"exportToSVG\",value:function(){this.triggerDownload(this.svgUrl(),this.w.config.chart.toolbar.export.svg.filename,\".svg\")}},{key:\"exportToPng\",value:function(){var t=this;this.dataURI().then((function(e){var i=e.imgURI,a=e.blob;a?navigator.msSaveOrOpenBlob(a,t.w.globals.chartID+\".png\"):t.triggerDownload(i,t.w.config.chart.toolbar.export.png.filename,\".png\")}))}},{key:\"exportToCSV\",value:function(t){var e=this,i=t.series,a=t.fileName,s=t.columnDelimiter,r=void 0===s?\",\":s,o=t.lineDelimiter,n=void 0===o?\"\\n\":o,l=this.w;i||(i=l.config.series);var h=[],c=[],d=\"\",g=l.globals.series.map((function(t,e){return-1===l.globals.collapsedSeriesIndices.indexOf(e)?t:[]})),f=Math.max.apply(Math,u(i.map((function(t){return t.data?t.data.length:0})))),p=new W(this.ctx),b=new B(this.ctx),v=function(t){var i=\"\";if(l.globals.axisCharts){if(\"category\"===l.config.xaxis.type||l.config.xaxis.convertedCatToNumeric)if(l.globals.isBarHorizontal){var a=l.globals.yLabelFormatters[0],s=new N(e.ctx).getActiveConfigSeriesIndex();i=a(l.globals.labels[t],{seriesIndex:s,dataPointIndex:t,w:l})}else i=b.getLabel(l.globals.labels,l.globals.timescaleLabels,0,t).text;\"datetime\"===l.config.xaxis.type&&(l.config.xaxis.categories.length?i=l.config.xaxis.categories[t]:l.config.labels.length&&(i=l.config.labels[t]))}else i=l.config.labels[t];return Array.isArray(i)&&(i=i.join(\" \")),x.isNumber(i)?i:i.split(r).join(\"\")},m=function(t,e){if(h.length&&0===e&&c.push(h.join(r)),t.data){t.data=t.data.length&&t.data||u(Array(f)).map((function(){return\"\"}));for(var a=0;a<t.data.length;a++){h=[];var s=v(a);if(s||(p.isFormatXY()?s=i[e].data[a].x:p.isFormat2DArray()&&(s=i[e].data[a]?i[e].data[a][0]:\"\")),0===e){h.push((n=s,\"datetime\"===l.config.xaxis.type&&String(n).length>=10?l.config.chart.toolbar.export.csv.dateFormatter(s):x.isNumber(s)?s:s.split(r).join(\"\")));for(var o=0;o<l.globals.series.length;o++)p.isFormatXY()?h.push(i[o].data[a].y):h.push(g[o][a])}(\"candlestick\"===l.config.chart.type||t.type&&\"candlestick\"===t.type)&&(h.pop(),h.push(l.globals.seriesCandleO[e][a]),h.push(l.globals.seriesCandleH[e][a]),h.push(l.globals.seriesCandleL[e][a]),h.push(l.globals.seriesCandleC[e][a])),(\"boxPlot\"===l.config.chart.type||t.type&&\"boxPlot\"===t.type)&&(h.pop(),h.push(l.globals.seriesCandleO[e][a]),h.push(l.globals.seriesCandleH[e][a]),h.push(l.globals.seriesCandleM[e][a]),h.push(l.globals.seriesCandleL[e][a]),h.push(l.globals.seriesCandleC[e][a])),\"rangeBar\"===l.config.chart.type&&(h.pop(),h.push(l.globals.seriesRangeStart[e][a]),h.push(l.globals.seriesRangeEnd[e][a])),h.length&&c.push(h.join(r))}}var n};h.push(l.config.chart.toolbar.export.csv.headerCategory),\"boxPlot\"===l.config.chart.type?(h.push(\"minimum\"),h.push(\"q1\"),h.push(\"median\"),h.push(\"q3\"),h.push(\"maximum\")):\"candlestick\"===l.config.chart.type?(h.push(\"open\"),h.push(\"high\"),h.push(\"low\"),h.push(\"close\")):\"rangeBar\"===l.config.chart.type?(h.push(\"minimum\"),h.push(\"maximum\")):i.map((function(t,e){var i=t.name?t.name:\"series-\".concat(e);l.globals.axisCharts&&h.push(i.split(r).join(\"\")?i.split(r).join(\"\"):\"series-\".concat(e))})),l.globals.axisCharts||(h.push(l.config.chart.toolbar.export.csv.headerValue),c.push(h.join(r))),i.map((function(t,e){l.globals.axisCharts?m(t,e):((h=[]).push(l.globals.labels[e].split(r).join(\"\")),h.push(g[e]),c.push(h.join(r)))})),d+=c.join(n),this.triggerDownload(\"data:text/csv; charset=utf-8,\"+encodeURIComponent(\"\\ufeff\"+d),a||l.config.chart.toolbar.export.csv.filename,\".csv\")}},{key:\"triggerDownload\",value:function(t,e,i){var a=document.createElement(\"a\");a.href=t,a.download=(e||this.w.globals.chartID)+i,document.body.appendChild(a),a.click(),document.body.removeChild(a)}}]),t}(),V=function(){function t(e,i){a(this,t),this.ctx=e,this.elgrid=i,this.w=e.w;var s=this.w;this.axesUtils=new B(e),this.xaxisLabels=s.globals.labels.slice(),s.globals.timescaleLabels.length>0&&!s.globals.isBarHorizontal&&(this.xaxisLabels=s.globals.timescaleLabels.slice()),s.config.xaxis.overwriteCategories&&(this.xaxisLabels=s.config.xaxis.overwriteCategories),this.drawnLabels=[],this.drawnLabelsRects=[],\"top\"===s.config.xaxis.position?this.offY=0:this.offY=s.globals.gridHeight+1,this.offY=this.offY+s.config.xaxis.axisBorder.offsetY,this.isCategoryBarHorizontal=\"bar\"===s.config.chart.type&&s.config.plotOptions.bar.horizontal,this.xaxisFontSize=s.config.xaxis.labels.style.fontSize,this.xaxisFontFamily=s.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=s.config.xaxis.labels.style.colors,this.xaxisBorderWidth=s.config.xaxis.axisBorder.width,this.isCategoryBarHorizontal&&(this.xaxisBorderWidth=s.config.yaxis[0].axisBorder.width.toString()),this.xaxisBorderWidth.indexOf(\"%\")>-1?this.xaxisBorderWidth=s.globals.gridWidth*parseInt(this.xaxisBorderWidth,10)/100:this.xaxisBorderWidth=parseInt(this.xaxisBorderWidth,10),this.xaxisBorderHeight=s.config.xaxis.axisBorder.height,this.yaxis=s.config.yaxis[0]}return r(t,[{key:\"drawXaxis\",value:function(){var t=this.w,e=new m(this.ctx),i=e.group({class:\"apexcharts-xaxis\",transform:\"translate(\".concat(t.config.xaxis.offsetX,\", \").concat(t.config.xaxis.offsetY,\")\")}),a=e.group({class:\"apexcharts-xaxis-texts-g\",transform:\"translate(\".concat(t.globals.translateXAxisX,\", \").concat(t.globals.translateXAxisY,\")\")});i.add(a);for(var s=[],r=0;r<this.xaxisLabels.length;r++)s.push(this.xaxisLabels[r]);if(this.drawXAxisLabelAndGroup(!0,e,a,s,t.globals.isXNumeric,(function(t,e){return e})),t.globals.hasGroups){var o=t.globals.groups;s=[];for(var n=0;n<o.length;n++)s.push(o[n].title);var l={};t.config.xaxis.group.style&&(l.xaxisFontSize=t.config.xaxis.group.style.fontSize,l.xaxisFontFamily=t.config.xaxis.group.style.fontFamily,l.xaxisForeColors=t.config.xaxis.group.style.colors,l.fontWeight=t.config.xaxis.group.style.fontWeight,l.cssClass=t.config.xaxis.group.style.cssClass),this.drawXAxisLabelAndGroup(!1,e,a,s,!1,(function(t,e){return o[t].cols*e}),l)}if(void 0!==t.config.xaxis.title.text){var h=e.group({class:\"apexcharts-xaxis-title\"}),c=e.drawText({x:t.globals.gridWidth/2+t.config.xaxis.title.offsetX,y:this.offY+parseFloat(this.xaxisFontSize)+(\"bottom\"===t.config.xaxis.position?t.globals.xAxisLabelsHeight:-t.globals.xAxisLabelsHeight-10)+t.config.xaxis.title.offsetY,text:t.config.xaxis.title.text,textAnchor:\"middle\",fontSize:t.config.xaxis.title.style.fontSize,fontFamily:t.config.xaxis.title.style.fontFamily,fontWeight:t.config.xaxis.title.style.fontWeight,foreColor:t.config.xaxis.title.style.color,cssClass:\"apexcharts-xaxis-title-text \"+t.config.xaxis.title.style.cssClass});h.add(c),i.add(h)}if(t.config.xaxis.axisBorder.show){var d=t.globals.barPadForNumericAxis,g=e.drawLine(t.globals.padHorizontal+t.config.xaxis.axisBorder.offsetX-d,this.offY,this.xaxisBorderWidth+d,this.offY,t.config.xaxis.axisBorder.color,0,this.xaxisBorderHeight);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(g):i.add(g)}return i}},{key:\"drawXAxisLabelAndGroup\",value:function(t,e,i,a,s,r){var o,n=this,l=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{},h=[],c=[],d=this.w,g=l.xaxisFontSize||this.xaxisFontSize,u=l.xaxisFontFamily||this.xaxisFontFamily,f=l.xaxisForeColors||this.xaxisForeColors,p=l.fontWeight||d.config.xaxis.labels.style.fontWeight,x=l.cssClass||d.config.xaxis.labels.style.cssClass,b=d.globals.padHorizontal,v=a.length,m=\"category\"===d.config.xaxis.type?d.globals.dataPoints:v;if(0===m&&v>m&&(m=v),s){var y=m>1?m-1:m;o=d.globals.gridWidth/y,b=b+r(0,o)/2+d.config.xaxis.labels.offsetX}else o=d.globals.gridWidth/m,b=b+r(0,o)+d.config.xaxis.labels.offsetX;for(var w=function(s){var l=b-r(s,o)/2+d.config.xaxis.labels.offsetX;0===s&&1===v&&o/2===b&&1===m&&(l=d.globals.gridWidth/2);var y=n.axesUtils.getLabel(a,d.globals.timescaleLabels,l,s,h,g,t),w=28;d.globals.rotateXLabels&&t&&(w=22),d.config.xaxis.title.text&&\"top\"===d.config.xaxis.position&&(w+=parseFloat(d.config.xaxis.title.style.fontSize)+2),t||(w=w+parseFloat(g)+(d.globals.xAxisLabelsHeight-d.globals.xAxisGroupLabelsHeight)+(d.globals.rotateXLabels?10:0)),y=void 0!==d.config.xaxis.tickAmount&&\"dataPoints\"!==d.config.xaxis.tickAmount&&\"datetime\"!==d.config.xaxis.type?n.axesUtils.checkLabelBasedOnTickamount(s,y,v):n.axesUtils.checkForOverflowingLabels(s,y,v,h,c);if(d.config.xaxis.labels.show){var k=e.drawText({x:y.x,y:n.offY+d.config.xaxis.labels.offsetY+w-(\"top\"===d.config.xaxis.position?d.globals.xAxisHeight+d.config.xaxis.axisTicks.height-2:0),text:y.text,textAnchor:\"middle\",fontWeight:y.isBold?600:p,fontSize:g,fontFamily:u,foreColor:Array.isArray(f)?t&&d.config.xaxis.convertedCatToNumeric?f[d.globals.minX+s-1]:f[s]:f,isPlainText:!1,cssClass:(t?\"apexcharts-xaxis-label \":\"apexcharts-xaxis-group-label \")+x});if(i.add(k),k.on(\"click\",(function(t){if(\"function\"==typeof d.config.chart.events.xAxisLabelClick){var e=Object.assign({},d,{labelIndex:s});d.config.chart.events.xAxisLabelClick(t,n.ctx,e)}})),t){var A=document.createElementNS(d.globals.SVGNS,\"title\");A.textContent=Array.isArray(y.text)?y.text.join(\" \"):y.text,k.node.appendChild(A),\"\"!==y.text&&(h.push(y.text),c.push(y))}}s<v-1&&(b+=r(s+1,o))},k=0;k<=v-1;k++)w(k)}},{key:\"drawXaxisInversed\",value:function(t){var e,i,a=this,s=this.w,r=new m(this.ctx),o=s.config.yaxis[0].opposite?s.globals.translateYAxisX[t]:0,n=r.group({class:\"apexcharts-yaxis apexcharts-xaxis-inversed\",rel:t}),l=r.group({class:\"apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g\",transform:\"translate(\"+o+\", 0)\"});n.add(l);var h=[];if(s.config.yaxis[t].show)for(var c=0;c<this.xaxisLabels.length;c++)h.push(this.xaxisLabels[c]);e=s.globals.gridHeight/h.length,i=-e/2.2;var d=s.globals.yLabelFormatters[0],g=s.config.yaxis[0].labels;if(g.show)for(var u=function(o){var n=void 0===h[o]?\"\":h[o];n=d(n,{seriesIndex:t,dataPointIndex:o,w:s});var c=a.axesUtils.getYAxisForeColor(g.style.colors,t),u=0;Array.isArray(n)&&(u=n.length/2*parseInt(g.style.fontSize,10));var f=g.offsetX-15,p=\"end\";a.yaxis.opposite&&(p=\"start\"),\"left\"===s.config.yaxis[0].labels.align?(f=g.offsetX,p=\"start\"):\"center\"===s.config.yaxis[0].labels.align?(f=g.offsetX,p=\"middle\"):\"right\"===s.config.yaxis[0].labels.align&&(p=\"end\");var x=r.drawText({x:f,y:i+e+g.offsetY-u,text:n,textAnchor:p,foreColor:Array.isArray(c)?c[o]:c,fontSize:g.style.fontSize,fontFamily:g.style.fontFamily,fontWeight:g.style.fontWeight,isPlainText:!1,cssClass:\"apexcharts-yaxis-label \"+g.style.cssClass,maxWidth:g.maxWidth});l.add(x),x.on(\"click\",(function(t){if(\"function\"==typeof s.config.chart.events.xAxisLabelClick){var e=Object.assign({},s,{labelIndex:o});s.config.chart.events.xAxisLabelClick(t,a.ctx,e)}}));var b=document.createElementNS(s.globals.SVGNS,\"title\");if(b.textContent=Array.isArray(n)?n.join(\" \"):n,x.node.appendChild(b),0!==s.config.yaxis[t].labels.rotate){var v=r.rotateAroundCenter(x.node);x.node.setAttribute(\"transform\",\"rotate(\".concat(s.config.yaxis[t].labels.rotate,\" 0 \").concat(v.y,\")\"))}i+=e},f=0;f<=h.length-1;f++)u(f);if(void 0!==s.config.yaxis[0].title.text){var p=r.group({class:\"apexcharts-yaxis-title apexcharts-xaxis-title-inversed\",transform:\"translate(\"+o+\", 0)\"}),x=r.drawText({x:s.config.yaxis[0].title.offsetX,y:s.globals.gridHeight/2+s.config.yaxis[0].title.offsetY,text:s.config.yaxis[0].title.text,textAnchor:\"middle\",foreColor:s.config.yaxis[0].title.style.color,fontSize:s.config.yaxis[0].title.style.fontSize,fontWeight:s.config.yaxis[0].title.style.fontWeight,fontFamily:s.config.yaxis[0].title.style.fontFamily,cssClass:\"apexcharts-yaxis-title-text \"+s.config.yaxis[0].title.style.cssClass});p.add(x),n.add(p)}var b=0;this.isCategoryBarHorizontal&&s.config.yaxis[0].opposite&&(b=s.globals.gridWidth);var v=s.config.xaxis.axisBorder;if(v.show){var y=r.drawLine(s.globals.padHorizontal+v.offsetX+b,1+v.offsetY,s.globals.padHorizontal+v.offsetX+b,s.globals.gridHeight+v.offsetY,v.color,0);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(y):n.add(y)}return s.config.yaxis[0].axisTicks.show&&this.axesUtils.drawYAxisTicks(b,h.length,s.config.yaxis[0].axisBorder,s.config.yaxis[0].axisTicks,0,e,n),n}},{key:\"drawXaxisTicks\",value:function(t,e,i){var a=this.w,s=t;if(!(t<0||t-2>a.globals.gridWidth)){var r=this.offY+a.config.xaxis.axisTicks.offsetY;if(e=e+r+a.config.xaxis.axisTicks.height,\"top\"===a.config.xaxis.position&&(e=r-a.config.xaxis.axisTicks.height),a.config.xaxis.axisTicks.show){var o=new m(this.ctx).drawLine(t+a.config.xaxis.axisTicks.offsetX,r+a.config.xaxis.offsetY,s+a.config.xaxis.axisTicks.offsetX,e+a.config.xaxis.offsetY,a.config.xaxis.axisTicks.color);i.add(o),o.node.classList.add(\"apexcharts-xaxis-tick\")}}}},{key:\"getXAxisTicksPositions\",value:function(){var t=this.w,e=[],i=this.xaxisLabels.length,a=t.globals.padHorizontal;if(t.globals.timescaleLabels.length>0)for(var s=0;s<i;s++)a=this.xaxisLabels[s].position,e.push(a);else for(var r=i,o=0;o<r;o++){var n=r;t.globals.isXNumeric&&\"bar\"!==t.config.chart.type&&(n-=1),a+=t.globals.gridWidth/n,e.push(a)}return e}},{key:\"xAxisLabelCorrections\",value:function(){var t=this.w,e=new m(this.ctx),i=t.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-texts-g\"),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-xaxis-texts-g text:not(.apexcharts-xaxis-group-label)\"),s=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis-inversed text\"),r=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-xaxis-inversed-texts-g text tspan\");if(t.globals.rotateXLabels||t.config.xaxis.labels.rotateAlways)for(var o=0;o<a.length;o++){var n=e.rotateAroundCenter(a[o]);n.y=n.y-1,n.x=n.x+1,a[o].setAttribute(\"transform\",\"rotate(\".concat(t.config.xaxis.labels.rotate,\" \").concat(n.x,\" \").concat(n.y,\")\")),a[o].setAttribute(\"text-anchor\",\"end\");i.setAttribute(\"transform\",\"translate(0, \".concat(-10,\")\"));var l=a[o].childNodes;t.config.xaxis.labels.trim&&Array.prototype.forEach.call(l,(function(i){e.placeTextWithEllipsis(i,i.textContent,t.globals.xAxisLabelsHeight-(\"bottom\"===t.config.legend.position?20:10))}))}else!function(){for(var i=t.globals.gridWidth/(t.globals.labels.length+1),s=0;s<a.length;s++){var r=a[s].childNodes;t.config.xaxis.labels.trim&&\"datetime\"!==t.config.xaxis.type&&Array.prototype.forEach.call(r,(function(t){e.placeTextWithEllipsis(t,t.textContent,i)}))}}();if(s.length>0){var h=s[s.length-1].getBBox(),c=s[0].getBBox();h.x<-20&&s[s.length-1].parentNode.removeChild(s[s.length-1]),c.x+c.width>t.globals.gridWidth&&!t.globals.isBarHorizontal&&s[0].parentNode.removeChild(s[0]);for(var d=0;d<r.length;d++)e.placeTextWithEllipsis(r[d],r[d].textContent,t.config.yaxis[0].labels.maxWidth-(t.config.yaxis[0].title.text?2*parseFloat(t.config.yaxis[0].title.style.fontSize):0)-15)}}}]),t}(),j=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.xaxisLabels=i.globals.labels.slice(),this.axesUtils=new B(e),this.isRangeBar=i.globals.seriesRange.length,i.globals.timescaleLabels.length>0&&(this.xaxisLabels=i.globals.timescaleLabels.slice())}return r(t,[{key:\"drawGridArea\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=new m(this.ctx);null===t&&(t=i.group({class:\"apexcharts-grid\"}));var a=i.drawLine(e.globals.padHorizontal,1,e.globals.padHorizontal,e.globals.gridHeight,\"transparent\"),s=i.drawLine(e.globals.padHorizontal,e.globals.gridHeight,e.globals.gridWidth,e.globals.gridHeight,\"transparent\");return t.add(s),t.add(a),t}},{key:\"drawGrid\",value:function(){var t=null;return this.w.globals.axisCharts&&(t=this.renderGrid(),this.drawGridArea(t.el)),t}},{key:\"createGridMask\",value:function(){var t=this.w,e=t.globals,i=new m(this.ctx),a=Array.isArray(t.config.stroke.width)?0:t.config.stroke.width;if(Array.isArray(t.config.stroke.width)){var s=0;t.config.stroke.width.forEach((function(t){s=Math.max(s,t)})),a=s}e.dom.elGridRectMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elGridRectMask.setAttribute(\"id\",\"gridRectMask\".concat(e.cuid)),e.dom.elGridRectMarkerMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elGridRectMarkerMask.setAttribute(\"id\",\"gridRectMarkerMask\".concat(e.cuid)),e.dom.elForecastMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elForecastMask.setAttribute(\"id\",\"forecastMask\".concat(e.cuid)),e.dom.elNonForecastMask=document.createElementNS(e.SVGNS,\"clipPath\"),e.dom.elNonForecastMask.setAttribute(\"id\",\"nonForecastMask\".concat(e.cuid));var r=t.config.chart.type,o=0,n=0;(\"bar\"===r||\"rangeBar\"===r||\"candlestick\"===r||\"boxPlot\"===r||t.globals.comboBarCount>0)&&t.globals.isXNumeric&&!t.globals.isBarHorizontal&&(o=t.config.grid.padding.left,n=t.config.grid.padding.right,e.barPadForNumericAxis>o&&(o=e.barPadForNumericAxis,n=e.barPadForNumericAxis)),e.dom.elGridRect=i.drawRect(-a/2-o-2,-a/2,e.gridWidth+a+n+o+4,e.gridHeight+a,0,\"#fff\");var l=t.globals.markers.largestSize+1;e.dom.elGridRectMarker=i.drawRect(2*-l,2*-l,e.gridWidth+4*l,e.gridHeight+4*l,0,\"#fff\"),e.dom.elGridRectMask.appendChild(e.dom.elGridRect.node),e.dom.elGridRectMarkerMask.appendChild(e.dom.elGridRectMarker.node);var h=e.dom.baseEl.querySelector(\"defs\");h.appendChild(e.dom.elGridRectMask),h.appendChild(e.dom.elForecastMask),h.appendChild(e.dom.elNonForecastMask),h.appendChild(e.dom.elGridRectMarkerMask)}},{key:\"_drawGridLines\",value:function(t){var e=t.i,i=t.x1,a=t.y1,s=t.x2,r=t.y2,o=t.xCount,n=t.parent,l=this.w;if(!(0===e&&l.globals.skipFirstTimelinelabel||e===o-1&&l.globals.skipLastTimelinelabel&&!l.config.xaxis.labels.formatter||\"radar\"===l.config.chart.type)){l.config.grid.xaxis.lines.show&&this._drawGridLine({i:e,x1:i,y1:a,x2:s,y2:r,xCount:o,parent:n});var h=0;if(l.globals.hasGroups&&\"between\"===l.config.xaxis.tickPlacement){var c=l.globals.groups;if(c){for(var d=0,g=0;d<e&&g<c.length;g++)d+=c[g].cols;d===e&&(h=.6*l.globals.xAxisLabelsHeight)}}new V(this.ctx).drawXaxisTicks(i,h,l.globals.dom.elGraphical)}}},{key:\"_drawGridLine\",value:function(t){var e=t.i,i=t.x1,a=t.y1,s=t.x2,r=t.y2,o=t.xCount,n=t.parent,l=this.w,h=!1,c=n.node.classList.contains(\"apexcharts-gridlines-horizontal\"),d=l.config.grid.strokeDashArray,g=l.globals.barPadForNumericAxis;(0===a&&0===r||0===i&&0===s)&&(h=!0),a===l.globals.gridHeight&&r===l.globals.gridHeight&&(h=!0),!l.globals.isBarHorizontal||0!==e&&e!==o-1||(h=!0);var u=new m(this).drawLine(i-(c?g:0),a,s+(c?g:0),r,l.config.grid.borderColor,d);u.node.classList.add(\"apexcharts-gridline\"),h?this.elGridBorders.add(u):n.add(u)}},{key:\"_drawGridBandRect\",value:function(t){var e=t.c,i=t.x1,a=t.y1,s=t.x2,r=t.y2,o=t.type,n=this.w,l=new m(this.ctx),h=n.globals.barPadForNumericAxis;if(\"column\"!==o||\"datetime\"!==n.config.xaxis.type){var c=n.config.grid[o].colors[e],d=l.drawRect(i-(\"row\"===o?h:0),a,s+(\"row\"===o?2*h:0),r,0,c,n.config.grid[o].opacity);this.elg.add(d),d.attr(\"clip-path\",\"url(#gridRectMask\".concat(n.globals.cuid,\")\")),d.node.classList.add(\"apexcharts-grid-\".concat(o))}}},{key:\"_drawXYLines\",value:function(t){var e=this,i=t.xCount,a=t.tickAmount,s=this.w;if(s.config.grid.xaxis.lines.show||s.config.xaxis.axisTicks.show){var r,o=s.globals.padHorizontal,n=s.globals.gridHeight;s.globals.timescaleLabels.length?function(t){for(var a=t.xC,s=t.x1,r=t.y1,o=t.x2,n=t.y2,l=0;l<a;l++)s=e.xaxisLabels[l].position,o=e.xaxisLabels[l].position,e._drawGridLines({i:l,x1:s,y1:r,x2:o,y2:n,xCount:i,parent:e.elgridLinesV})}({xC:i,x1:o,y1:0,x2:r,y2:n}):(s.globals.isXNumeric&&(i=s.globals.xAxisScale.result.length),function(t){for(var a=t.xC,r=t.x1,o=t.y1,n=t.x2,l=t.y2,h=0;h<a+(s.globals.isXNumeric?0:1);h++)0===h&&1===a&&1===s.globals.dataPoints&&(n=r=s.globals.gridWidth/2),e._drawGridLines({i:h,x1:r,y1:o,x2:n,y2:l,xCount:i,parent:e.elgridLinesV}),n=r+=s.globals.gridWidth/(s.globals.isXNumeric?a-1:a)}({xC:i,x1:o,y1:0,x2:r,y2:n}))}if(s.config.grid.yaxis.lines.show){var l=0,h=0,c=s.globals.gridWidth,d=a+1;this.isRangeBar&&(d=s.globals.labels.length);for(var g=0;g<d+(this.isRangeBar?1:0);g++)this._drawGridLine({i:g,xCount:d+(this.isRangeBar?1:0),x1:0,y1:l,x2:c,y2:h,parent:this.elgridLinesH}),h=l+=s.globals.gridHeight/(this.isRangeBar?d:a)}}},{key:\"_drawInvertedXYLines\",value:function(t){var e=t.xCount,i=this.w;if(i.config.grid.xaxis.lines.show||i.config.xaxis.axisTicks.show)for(var a,s=i.globals.padHorizontal,r=i.globals.gridHeight,o=0;o<e+1;o++){i.config.grid.xaxis.lines.show&&this._drawGridLine({i:o,xCount:e+1,x1:s,y1:0,x2:a,y2:r,parent:this.elgridLinesV}),new V(this.ctx).drawXaxisTicks(s,0,i.globals.dom.elGraphical),a=s=s+i.globals.gridWidth/e+.3}if(i.config.grid.yaxis.lines.show)for(var n=0,l=0,h=i.globals.gridWidth,c=0;c<i.globals.dataPoints+1;c++)this._drawGridLine({i:c,xCount:i.globals.dataPoints+1,x1:0,y1:n,x2:h,y2:l,parent:this.elgridLinesH}),l=n+=i.globals.gridHeight/i.globals.dataPoints}},{key:\"renderGrid\",value:function(){var t=this.w,e=new m(this.ctx);this.elg=e.group({class:\"apexcharts-grid\"}),this.elgridLinesH=e.group({class:\"apexcharts-gridlines-horizontal\"}),this.elgridLinesV=e.group({class:\"apexcharts-gridlines-vertical\"}),this.elGridBorders=e.group({class:\"apexcharts-grid-borders\"}),this.elg.add(this.elgridLinesH),this.elg.add(this.elgridLinesV),t.config.grid.show||(this.elgridLinesV.hide(),this.elgridLinesH.hide(),this.elGridBorders.hide());for(var i,a=t.globals.yAxisScale.length?t.globals.yAxisScale[0].result.length-1:5,s=0;s<t.globals.series.length&&(void 0!==t.globals.yAxisScale[s]&&(a=t.globals.yAxisScale[s].result.length-1),!(a>2));s++);return!t.globals.isBarHorizontal||this.isRangeBar?(i=this.xaxisLabels.length,this.isRangeBar&&(a=t.globals.labels.length,t.config.xaxis.tickAmount&&t.config.xaxis.labels.formatter&&(i=t.config.xaxis.tickAmount)),this._drawXYLines({xCount:i,tickAmount:a})):(i=a,a=t.globals.xTickAmount,this._drawInvertedXYLines({xCount:i,tickAmount:a})),this.drawGridBands(i,a),{el:this.elg,elGridBorders:this.elGridBorders,xAxisTickWidth:t.globals.gridWidth/i}}},{key:\"drawGridBands\",value:function(t,e){var i=this.w;if(void 0!==i.config.grid.row.colors&&i.config.grid.row.colors.length>0)for(var a=0,s=i.globals.gridHeight/e,r=i.globals.gridWidth,o=0,n=0;o<e;o++,n++)n>=i.config.grid.row.colors.length&&(n=0),this._drawGridBandRect({c:n,x1:0,y1:a,x2:r,y2:s,type:\"row\"}),a+=i.globals.gridHeight/e;if(void 0!==i.config.grid.column.colors&&i.config.grid.column.colors.length>0)for(var l=i.globals.isBarHorizontal||\"category\"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric?t:t-1,h=i.globals.padHorizontal,c=i.globals.padHorizontal+i.globals.gridWidth/l,d=i.globals.gridHeight,g=0,u=0;g<t;g++,u++)u>=i.config.grid.column.colors.length&&(u=0),this._drawGridBandRect({c:u,x1:h,y1:0,x2:c,y2:d,type:\"column\"}),h+=i.globals.gridWidth/l}}]),t}(),_=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"niceScale\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4?arguments[4]:void 0,r=this.w,o=Math.abs(e-t);if(\"dataPoints\"===(i=this._adjustTicksForSmallRange(i,a,o))&&(i=r.globals.dataPoints-1),t===Number.MIN_VALUE&&0===e||!x.isNumber(t)&&!x.isNumber(e)||t===Number.MIN_VALUE&&e===-Number.MAX_VALUE){t=0,e=i;var n=this.linearScale(t,e,i);return n}t>e?(console.warn(\"axis.min cannot be greater than axis.max\"),e=t+.1):t===e&&(t=0===t?0:t-.5,e=0===e?2:e+.5);var l=[];o<1&&s&&(\"candlestick\"===r.config.chart.type||\"candlestick\"===r.config.series[a].type||\"boxPlot\"===r.config.chart.type||\"boxPlot\"===r.config.series[a].type||r.globals.isRangeData)&&(e*=1.01);var h=i+1;h<2?h=2:h>2&&(h-=2);var c=o/h,d=Math.floor(x.log10(c)),g=Math.pow(10,d),u=Math.round(c/g);u<1&&(u=1);var f=u*g,p=f*Math.floor(t/f),b=f*Math.ceil(e/f),v=p;if(s&&o>2){for(;l.push(v),!((v+=f)>b););return{result:l,niceMin:l[0],niceMax:l[l.length-1]}}var m=t;(l=[]).push(m);for(var y=Math.abs(e-t)/i,w=0;w<=i;w++)m+=y,l.push(m);return l[l.length-2]>=e&&l.pop(),{result:l,niceMin:l[0],niceMax:l[l.length-1]}}},{key:\"linearScale\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=arguments.length>3?arguments[3]:void 0,s=Math.abs(e-t);\"dataPoints\"===(i=this._adjustTicksForSmallRange(i,a,s))&&(i=this.w.globals.dataPoints-1);var r=s/i;i===Number.MAX_VALUE&&(i=10,r=1);for(var o=[],n=t;i>=0;)o.push(n),n+=r,i-=1;return{result:o,niceMin:o[0],niceMax:o[o.length-1]}}},{key:\"logarithmicScaleNice\",value:function(t,e,i){e<=0&&(e=Math.max(t,i)),t<=0&&(t=Math.min(e,i));for(var a=[],s=Math.ceil(Math.log(e)/Math.log(i)+1),r=Math.floor(Math.log(t)/Math.log(i));r<s;r++)a.push(Math.pow(i,r));return{result:a,niceMin:a[0],niceMax:a[a.length-1]}}},{key:\"logarithmicScale\",value:function(t,e,i){e<=0&&(e=Math.max(t,i)),t<=0&&(t=Math.min(e,i));for(var a=[],s=Math.log(e)/Math.log(i),r=Math.log(t)/Math.log(i),o=s-r,n=Math.round(o),l=o/n,h=0,c=r;h<n;h++,c+=l)a.push(Math.pow(i,c));return a.push(Math.pow(i,s)),{result:a,niceMin:t,niceMax:e}}},{key:\"_adjustTicksForSmallRange\",value:function(t,e,i){var a=t;if(void 0!==e&&this.w.config.yaxis[e].labels.formatter&&void 0===this.w.config.yaxis[e].tickAmount){var s=Number(this.w.config.yaxis[e].labels.formatter(1));x.isNumber(s)&&0===this.w.globals.yValueDecimal&&(a=Math.ceil(i))}return a<t?a:t}},{key:\"setYScaleForIndex\",value:function(t,e,i){var a=this.w.globals,s=this.w.config,r=a.isBarHorizontal?s.xaxis:s.yaxis[t];void 0===a.yAxisScale[t]&&(a.yAxisScale[t]=[]);var o=Math.abs(i-e);if(r.logarithmic&&o<=5&&(a.invalidLogScale=!0),r.logarithmic&&o>5)a.allSeriesCollapsed=!1,a.yAxisScale[t]=this.logarithmicScale(e,i,r.logBase),a.yAxisScale[t]=r.forceNiceScale?this.logarithmicScaleNice(e,i,r.logBase):this.logarithmicScale(e,i,r.logBase);else if(i!==-Number.MAX_VALUE&&x.isNumber(i))if(a.allSeriesCollapsed=!1,void 0===r.min&&void 0===r.max||r.forceNiceScale){var n=void 0===s.yaxis[t].max&&void 0===s.yaxis[t].min||s.yaxis[t].forceNiceScale;a.yAxisScale[t]=this.niceScale(e,i,r.tickAmount?r.tickAmount:o<5&&o>1?o+1:5,t,n)}else a.yAxisScale[t]=this.linearScale(e,i,r.tickAmount,t);else a.yAxisScale[t]=this.linearScale(0,5,5)}},{key:\"setXScale\",value:function(t,e){var i=this.w,a=i.globals,s=i.config.xaxis,r=Math.abs(e-t);return e!==-Number.MAX_VALUE&&x.isNumber(e)?a.xAxisScale=this.linearScale(t,e,s.tickAmount?s.tickAmount:r<5&&r>1?r+1:5,0):a.xAxisScale=this.linearScale(0,5,5),a.xAxisScale}},{key:\"setMultipleYScales\",value:function(){var t=this,e=this.w.globals,i=this.w.config,a=e.minYArr.concat([]),s=e.maxYArr.concat([]),r=[];i.yaxis.forEach((function(e,o){var n=o;i.series.forEach((function(t,i){t.name===e.seriesName&&(n=i,o!==i?r.push({index:i,similarIndex:o,alreadyExists:!0}):r.push({index:i}))}));var l=a[n],h=s[n];t.setYScaleForIndex(o,l,h)})),this.sameScaleInMultipleAxes(a,s,r)}},{key:\"sameScaleInMultipleAxes\",value:function(t,e,i){var a=this,s=this.w.config,r=this.w.globals,o=[];i.forEach((function(t){t.alreadyExists&&(void 0===o[t.index]&&(o[t.index]=[]),o[t.index].push(t.index),o[t.index].push(t.similarIndex))})),r.yAxisSameScaleIndices=o,o.forEach((function(t,e){o.forEach((function(i,a){var s,r;e!==a&&(s=t,r=i,s.filter((function(t){return-1!==r.indexOf(t)}))).length>0&&(o[e]=o[e].concat(o[a]))}))}));var n=o.map((function(t){return t.filter((function(e,i){return t.indexOf(e)===i}))})).map((function(t){return t.sort()}));o=o.filter((function(t){return!!t}));var l=n.slice(),h=l.map((function(t){return JSON.stringify(t)}));l=l.filter((function(t,e){return h.indexOf(JSON.stringify(t))===e}));var c=[],d=[];t.forEach((function(t,i){l.forEach((function(a,s){a.indexOf(i)>-1&&(void 0===c[s]&&(c[s]=[],d[s]=[]),c[s].push({key:i,value:t}),d[s].push({key:i,value:e[i]}))}))}));var g=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,Number.MIN_VALUE),u=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,-Number.MAX_VALUE);c.forEach((function(t,e){t.forEach((function(t,i){g[e]=Math.min(t.value,g[e])}))})),d.forEach((function(t,e){t.forEach((function(t,i){u[e]=Math.max(t.value,u[e])}))})),t.forEach((function(t,e){d.forEach((function(t,i){var o=g[i],n=u[i];s.chart.stacked&&(n=0,t.forEach((function(t,e){t.value!==-Number.MAX_VALUE&&(n+=t.value),o!==Number.MIN_VALUE&&(o+=c[i][e].value)}))),t.forEach((function(i,l){t[l].key===e&&(void 0!==s.yaxis[e].min&&(o=\"function\"==typeof s.yaxis[e].min?s.yaxis[e].min(r.minY):s.yaxis[e].min),void 0!==s.yaxis[e].max&&(n=\"function\"==typeof s.yaxis[e].max?s.yaxis[e].max(r.maxY):s.yaxis[e].max),a.setYScaleForIndex(e,o,n))}))}))}))}},{key:\"autoScaleY\",value:function(t,e,i){t||(t=this);var a=t.w;if(a.globals.isMultipleYAxis||a.globals.collapsedSeries.length)return console.warn(\"autoScaleYaxis is not supported in a multi-yaxis chart.\"),e;var s=a.globals.seriesX[0],r=a.config.chart.stacked;return e.forEach((function(t,o){for(var n=0,l=0;l<s.length;l++)if(s[l]>=i.xaxis.min){n=l;break}var h,c,d=a.globals.minYArr[o],g=a.globals.maxYArr[o],u=a.globals.stackedSeriesTotals;a.globals.series.forEach((function(o,l){var f=o[n];r?(f=u[n],h=c=f,u.forEach((function(t,e){s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min&&(t>c&&null!==t&&(c=t),o[e]<h&&null!==o[e]&&(h=o[e]))}))):(h=c=f,o.forEach((function(t,e){if(s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min){var r=t,o=t;a.globals.series.forEach((function(i,a){null!==t&&(r=Math.min(i[e],r),o=Math.max(i[e],o))})),o>c&&null!==o&&(c=o),r<h&&null!==r&&(h=r)}}))),void 0===h&&void 0===c&&(h=d,c=g),c*=c<0?.9:1.1,0===(h*=h<0?1.1:.9)&&0===c&&(h=-1,c=1),c<0&&c<g&&(c=g),h<0&&h>d&&(h=d),e.length>1?(e[l].min=void 0===t.min?h:t.min,e[l].max=void 0===t.max?c:t.max):(e[0].min=void 0===t.min?h:t.min,e[0].max=void 0===t.max?c:t.max)}))})),e}}]),t}(),U=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.scales=new _(e)}return r(t,[{key:\"init\",value:function(){this.setYRange(),this.setXRange(),this.setZRange()}},{key:\"getMinYMaxY\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-Number.MAX_VALUE,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w.config,r=this.w.globals,o=-Number.MAX_VALUE,n=Number.MIN_VALUE;null===a&&(a=t+1);var l=r.series,h=l,c=l;\"candlestick\"===s.chart.type?(h=r.seriesCandleL,c=r.seriesCandleH):\"boxPlot\"===s.chart.type?(h=r.seriesCandleO,c=r.seriesCandleC):r.isRangeData&&(h=r.seriesRangeStart,c=r.seriesRangeEnd);for(var d=t;d<a;d++){r.dataPoints=Math.max(r.dataPoints,l[d].length),r.categoryLabels.length&&(r.dataPoints=r.categoryLabels.filter((function(t){return void 0!==t})).length);for(var g=0;g<r.series[d].length;g++){var u=l[d][g];null!==u&&x.isNumber(u)?(void 0!==c[d][g]&&(o=Math.max(o,c[d][g]),e=Math.min(e,c[d][g])),void 0!==h[d][g]&&(e=Math.min(e,h[d][g]),i=Math.max(i,h[d][g])),\"candlestick\"!==this.w.config.chart.type&&\"boxPlot\"!==this.w.config.chart.type&&\"rangeArea\"===this.w.config.chart.type&&\"rangeBar\"===this.w.config.chart.type||(\"candlestick\"!==this.w.config.chart.type&&\"boxPlot\"!==this.w.config.chart.type||void 0!==r.seriesCandleC[d][g]&&(o=Math.max(o,r.seriesCandleO[d][g]),o=Math.max(o,r.seriesCandleH[d][g]),o=Math.max(o,r.seriesCandleL[d][g]),o=Math.max(o,r.seriesCandleC[d][g]),\"boxPlot\"===this.w.config.chart.type&&(o=Math.max(o,r.seriesCandleM[d][g]))),!s.series[d].type||\"candlestick\"===s.series[d].type&&\"boxPlot\"===s.series[d].type&&\"rangeArea\"===s.series[d].type&&\"rangeBar\"===s.series[d].type||(o=Math.max(o,r.series[d][g]),e=Math.min(e,r.series[d][g])),i=o),r.seriesGoals[d]&&r.seriesGoals[d][g]&&Array.isArray(r.seriesGoals[d][g])&&r.seriesGoals[d][g].forEach((function(t){n!==Number.MIN_VALUE&&(n=Math.min(n,t.value),e=n),o=Math.max(o,t.value),i=o})),x.isFloat(u)&&(u=x.noExponents(u),r.yValueDecimal=Math.max(r.yValueDecimal,u.toString().split(\".\")[1].length)),n>h[d][g]&&h[d][g]<0&&(n=h[d][g])):r.hasNullValues=!0}}return\"rangeBar\"===s.chart.type&&r.seriesRangeStart.length&&r.isBarHorizontal&&(n=e),\"bar\"===s.chart.type&&(n<0&&o<0&&(o=0),n===Number.MIN_VALUE&&(n=0)),{minY:n,maxY:o,lowestY:e,highestY:i}}},{key:\"setYRange\",value:function(){var t=this.w.globals,e=this.w.config;t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE;var i=Number.MAX_VALUE;if(t.isMultipleYAxis)for(var a=0;a<t.series.length;a++){var s=this.getMinYMaxY(a,i,null,a+1);t.minYArr.push(s.minY),t.maxYArr.push(s.maxY),i=s.lowestY}var r=this.getMinYMaxY(0,i,null,t.series.length);if(t.minY=r.minY,t.maxY=r.maxY,i=r.lowestY,e.chart.stacked&&this._setStackedMinMax(),(\"line\"===e.chart.type||\"area\"===e.chart.type||\"candlestick\"===e.chart.type||\"boxPlot\"===e.chart.type||\"rangeBar\"===e.chart.type&&!t.isBarHorizontal)&&t.minY===Number.MIN_VALUE&&i!==-Number.MAX_VALUE&&i!==t.maxY){var o=t.maxY-i;(i>=0&&i<=10||void 0!==e.yaxis[0].min||void 0!==e.yaxis[0].max)&&(o=0),t.minY=i-5*o/100,i>0&&t.minY<0&&(t.minY=0),t.maxY=t.maxY+5*o/100}if(e.yaxis.forEach((function(e,i){void 0!==e.max&&(\"number\"==typeof e.max?t.maxYArr[i]=e.max:\"function\"==typeof e.max&&(t.maxYArr[i]=e.max(t.isMultipleYAxis?t.maxYArr[i]:t.maxY)),t.maxY=t.maxYArr[i]),void 0!==e.min&&(\"number\"==typeof e.min?t.minYArr[i]=e.min:\"function\"==typeof e.min&&(t.minYArr[i]=e.min(t.isMultipleYAxis?t.minYArr[i]===Number.MIN_VALUE?0:t.minYArr[i]:t.minY)),t.minY=t.minYArr[i])})),t.isBarHorizontal){[\"min\",\"max\"].forEach((function(i){void 0!==e.xaxis[i]&&\"number\"==typeof e.xaxis[i]&&(\"min\"===i?t.minY=e.xaxis[i]:t.maxY=e.xaxis[i])}))}return t.isMultipleYAxis?(this.scales.setMultipleYScales(),t.minY=i,t.yAxisScale.forEach((function(e,i){t.minYArr[i]=e.niceMin,t.maxYArr[i]=e.niceMax}))):(this.scales.setYScaleForIndex(0,t.minY,t.maxY),t.minY=t.yAxisScale[0].niceMin,t.maxY=t.yAxisScale[0].niceMax,t.minYArr[0]=t.yAxisScale[0].niceMin,t.maxYArr[0]=t.yAxisScale[0].niceMax),{minY:t.minY,maxY:t.maxY,minYArr:t.minYArr,maxYArr:t.maxYArr,yAxisScale:t.yAxisScale}}},{key:\"setXRange\",value:function(){var t=this.w.globals,e=this.w.config,i=\"numeric\"===e.xaxis.type||\"datetime\"===e.xaxis.type||\"category\"===e.xaxis.type&&!t.noLabelsProvided||t.noLabelsProvided||t.isXNumeric;if(t.isXNumeric&&function(){for(var e=0;e<t.series.length;e++)if(t.labels[e])for(var i=0;i<t.labels[e].length;i++)null!==t.labels[e][i]&&x.isNumber(t.labels[e][i])&&(t.maxX=Math.max(t.maxX,t.labels[e][i]),t.initialMaxX=Math.max(t.maxX,t.labels[e][i]),t.minX=Math.min(t.minX,t.labels[e][i]),t.initialMinX=Math.min(t.minX,t.labels[e][i]))}(),t.noLabelsProvided&&0===e.xaxis.categories.length&&(t.maxX=t.labels[t.labels.length-1],t.initialMaxX=t.labels[t.labels.length-1],t.minX=1,t.initialMinX=1),t.isXNumeric||t.noLabelsProvided||t.dataFormatXNumeric){var a;if(void 0===e.xaxis.tickAmount?(a=Math.round(t.svgWidth/150),\"numeric\"===e.xaxis.type&&t.dataPoints<30&&(a=t.dataPoints-1),a>t.dataPoints&&0!==t.dataPoints&&(a=t.dataPoints-1)):\"dataPoints\"===e.xaxis.tickAmount?(t.series.length>1&&(a=t.series[t.maxValsInArrayIndex].length-1),t.isXNumeric&&(a=t.maxX-t.minX-1)):a=e.xaxis.tickAmount,t.xTickAmount=a,void 0!==e.xaxis.max&&\"number\"==typeof e.xaxis.max&&(t.maxX=e.xaxis.max),void 0!==e.xaxis.min&&\"number\"==typeof e.xaxis.min&&(t.minX=e.xaxis.min),void 0!==e.xaxis.range&&(t.minX=t.maxX-e.xaxis.range),t.minX!==Number.MAX_VALUE&&t.maxX!==-Number.MAX_VALUE)if(e.xaxis.convertedCatToNumeric&&!t.dataFormatXNumeric){for(var s=[],r=t.minX-1;r<t.maxX;r++)s.push(r+1);t.xAxisScale={result:s,niceMin:s[0],niceMax:s[s.length-1]}}else t.xAxisScale=this.scales.setXScale(t.minX,t.maxX);else t.xAxisScale=this.scales.linearScale(1,a,a),t.noLabelsProvided&&t.labels.length>0&&(t.xAxisScale=this.scales.linearScale(1,t.labels.length,a-1),t.seriesX=t.labels.slice());i&&(t.labels=t.xAxisScale.result.slice())}return t.isBarHorizontal&&t.labels.length&&(t.xTickAmount=t.labels.length),this._handleSingleDataPoint(),this._getMinXDiff(),{minX:t.minX,maxX:t.maxX}}},{key:\"setZRange\",value:function(){var t=this.w.globals;if(t.isDataXYZ)for(var e=0;e<t.series.length;e++)if(void 0!==t.seriesZ[e])for(var i=0;i<t.seriesZ[e].length;i++)null!==t.seriesZ[e][i]&&x.isNumber(t.seriesZ[e][i])&&(t.maxZ=Math.max(t.maxZ,t.seriesZ[e][i]),t.minZ=Math.min(t.minZ,t.seriesZ[e][i]))}},{key:\"_handleSingleDataPoint\",value:function(){var t=this.w.globals,e=this.w.config;if(t.minX===t.maxX){var i=new T(this.ctx);if(\"datetime\"===e.xaxis.type){var a=i.getDate(t.minX);e.xaxis.labels.datetimeUTC?a.setUTCDate(a.getUTCDate()-2):a.setDate(a.getDate()-2),t.minX=new Date(a).getTime();var s=i.getDate(t.maxX);e.xaxis.labels.datetimeUTC?s.setUTCDate(s.getUTCDate()+2):s.setDate(s.getDate()+2),t.maxX=new Date(s).getTime()}else(\"numeric\"===e.xaxis.type||\"category\"===e.xaxis.type&&!t.noLabelsProvided)&&(t.minX=t.minX-2,t.initialMinX=t.minX,t.maxX=t.maxX+2,t.initialMaxX=t.maxX)}}},{key:\"_getMinXDiff\",value:function(){var t=this.w.globals;t.isXNumeric&&t.seriesX.forEach((function(e,i){1===e.length&&e.push(t.seriesX[t.maxValsInArrayIndex][t.seriesX[t.maxValsInArrayIndex].length-1]);var a=e.slice();a.sort((function(t,e){return t-e})),a.forEach((function(e,i){if(i>0){var s=e-a[i-1];s>0&&(t.minXDiff=Math.min(s,t.minXDiff))}})),1!==t.dataPoints&&t.minXDiff!==Number.MAX_VALUE||(t.minXDiff=.5)}))}},{key:\"_setStackedMinMax\",value:function(){var t=this.w.globals,e=[],i=[];if(t.series.length)for(var a=0;a<t.series[t.maxValsInArrayIndex].length;a++)for(var s=0,r=0,o=0;o<t.series.length;o++)null!==t.series[o][a]&&x.isNumber(t.series[o][a])&&(t.series[o][a]>0?s=s+parseFloat(t.series[o][a])+1e-4:r+=parseFloat(t.series[o][a])),o===t.series.length-1&&(e.push(s),i.push(r));for(var n=0;n<e.length;n++)t.maxY=Math.max(t.maxY,e[n]),t.minY=Math.min(t.minY,i[n])}}]),t}(),q=function(){function t(e,i){a(this,t),this.ctx=e,this.elgrid=i,this.w=e.w;var s=this.w;this.xaxisFontSize=s.config.xaxis.labels.style.fontSize,this.axisFontFamily=s.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=s.config.xaxis.labels.style.colors,this.isCategoryBarHorizontal=\"bar\"===s.config.chart.type&&s.config.plotOptions.bar.horizontal,this.xAxisoffX=0,\"bottom\"===s.config.xaxis.position&&(this.xAxisoffX=s.globals.gridHeight),this.drawnLabels=[],this.axesUtils=new B(e)}return r(t,[{key:\"drawYaxis\",value:function(t){var e=this,i=this.w,a=new m(this.ctx),s=i.config.yaxis[t].labels.style,r=s.fontSize,o=s.fontFamily,n=s.fontWeight,l=a.group({class:\"apexcharts-yaxis\",rel:t,transform:\"translate(\"+i.globals.translateYAxisX[t]+\", 0)\"});if(this.axesUtils.isYAxisHidden(t))return l;var h=a.group({class:\"apexcharts-yaxis-texts-g\"});l.add(h);var c=i.globals.yAxisScale[t].result.length-1,d=i.globals.gridHeight/c,g=i.globals.translateY,u=i.globals.yLabelFormatters[t],f=i.globals.yAxisScale[t].result.slice();f=this.axesUtils.checkForReversedLabels(t,f);var p=\"\";if(i.config.yaxis[t].labels.show)for(var x=function(l){var x=f[l];x=u(x,l,i);var b=i.config.yaxis[t].labels.padding;i.config.yaxis[t].opposite&&0!==i.config.yaxis.length&&(b*=-1);var v=\"end\";i.config.yaxis[t].opposite&&(v=\"start\"),\"left\"===i.config.yaxis[t].labels.align?v=\"start\":\"center\"===i.config.yaxis[t].labels.align?v=\"middle\":\"right\"===i.config.yaxis[t].labels.align&&(v=\"end\");var m=e.axesUtils.getYAxisForeColor(s.colors,t),y=a.drawText({x:b,y:g+c/10+i.config.yaxis[t].labels.offsetY+1,text:x,textAnchor:v,fontSize:r,fontFamily:o,fontWeight:n,maxWidth:i.config.yaxis[t].labels.maxWidth,foreColor:Array.isArray(m)?m[l]:m,isPlainText:!1,cssClass:\"apexcharts-yaxis-label \"+s.cssClass});l===c&&(p=y),h.add(y);var w=document.createElementNS(i.globals.SVGNS,\"title\");if(w.textContent=Array.isArray(x)?x.join(\" \"):x,y.node.appendChild(w),0!==i.config.yaxis[t].labels.rotate){var k=a.rotateAroundCenter(p.node),A=a.rotateAroundCenter(y.node);y.node.setAttribute(\"transform\",\"rotate(\".concat(i.config.yaxis[t].labels.rotate,\" \").concat(k.x,\" \").concat(A.y,\")\"))}g+=d},b=c;b>=0;b--)x(b);if(void 0!==i.config.yaxis[t].title.text){var v=a.group({class:\"apexcharts-yaxis-title\"}),y=0;i.config.yaxis[t].opposite&&(y=i.globals.translateYAxisX[t]);var w=a.drawText({x:y,y:i.globals.gridHeight/2+i.globals.translateY+i.config.yaxis[t].title.offsetY,text:i.config.yaxis[t].title.text,textAnchor:\"end\",foreColor:i.config.yaxis[t].title.style.color,fontSize:i.config.yaxis[t].title.style.fontSize,fontWeight:i.config.yaxis[t].title.style.fontWeight,fontFamily:i.config.yaxis[t].title.style.fontFamily,cssClass:\"apexcharts-yaxis-title-text \"+i.config.yaxis[t].title.style.cssClass});v.add(w),l.add(v)}var k=i.config.yaxis[t].axisBorder,A=31+k.offsetX;if(i.config.yaxis[t].opposite&&(A=-31-k.offsetX),k.show){var S=a.drawLine(A,i.globals.translateY+k.offsetY-2,A,i.globals.gridHeight+i.globals.translateY+k.offsetY+2,k.color,0,k.width);l.add(S)}return i.config.yaxis[t].axisTicks.show&&this.axesUtils.drawYAxisTicks(A,c,k,i.config.yaxis[t].axisTicks,t,d,l),l}},{key:\"drawYaxisInversed\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-xaxis apexcharts-yaxis-inversed\"}),s=i.group({class:\"apexcharts-xaxis-texts-g\",transform:\"translate(\".concat(e.globals.translateXAxisX,\", \").concat(e.globals.translateXAxisY,\")\")});a.add(s);var r=e.globals.yAxisScale[t].result.length-1,o=e.globals.gridWidth/r+.1,n=o+e.config.xaxis.labels.offsetX,l=e.globals.xLabelFormatter,h=e.globals.yAxisScale[t].result.slice(),c=e.globals.timescaleLabels;c.length>0&&(this.xaxisLabels=c.slice(),r=(h=c.slice()).length),h=this.axesUtils.checkForReversedLabels(t,h);var d=c.length;if(e.config.xaxis.labels.show)for(var g=d?0:r;d?g<d:g>=0;d?g++:g--){var u=h[g];u=l(u,g,e);var f=e.globals.gridWidth+e.globals.padHorizontal-(n-o+e.config.xaxis.labels.offsetX);if(c.length){var p=this.axesUtils.getLabel(h,c,f,g,this.drawnLabels,this.xaxisFontSize);f=p.x,u=p.text,this.drawnLabels.push(p.text),0===g&&e.globals.skipFirstTimelinelabel&&(u=\"\"),g===h.length-1&&e.globals.skipLastTimelinelabel&&(u=\"\")}var x=i.drawText({x:f,y:this.xAxisoffX+e.config.xaxis.labels.offsetY+30-(\"top\"===e.config.xaxis.position?e.globals.xAxisHeight+e.config.xaxis.axisTicks.height-2:0),text:u,textAnchor:\"middle\",foreColor:Array.isArray(this.xaxisForeColors)?this.xaxisForeColors[t]:this.xaxisForeColors,fontSize:this.xaxisFontSize,fontFamily:this.xaxisFontFamily,fontWeight:e.config.xaxis.labels.style.fontWeight,isPlainText:!1,cssClass:\"apexcharts-xaxis-label \"+e.config.xaxis.labels.style.cssClass});s.add(x),x.tspan(u);var b=document.createElementNS(e.globals.SVGNS,\"title\");b.textContent=u,x.node.appendChild(b),n+=o}return this.inversedYAxisTitleText(a),this.inversedYAxisBorder(a),a}},{key:\"inversedYAxisBorder\",value:function(t){var e=this.w,i=new m(this.ctx),a=e.config.xaxis.axisBorder;if(a.show){var s=0;\"bar\"===e.config.chart.type&&e.globals.isXNumeric&&(s-=15);var r=i.drawLine(e.globals.padHorizontal+s+a.offsetX,this.xAxisoffX,e.globals.gridWidth,this.xAxisoffX,a.color,0,a.height);this.elgrid&&this.elgrid.elGridBorders?this.elgrid.elGridBorders.add(r):t.add(r)}}},{key:\"inversedYAxisTitleText\",value:function(t){var e=this.w,i=new m(this.ctx);if(void 0!==e.config.xaxis.title.text){var a=i.group({class:\"apexcharts-xaxis-title apexcharts-yaxis-title-inversed\"}),s=i.drawText({x:e.globals.gridWidth/2+e.config.xaxis.title.offsetX,y:this.xAxisoffX+parseFloat(this.xaxisFontSize)+parseFloat(e.config.xaxis.title.style.fontSize)+e.config.xaxis.title.offsetY+20,text:e.config.xaxis.title.text,textAnchor:\"middle\",fontSize:e.config.xaxis.title.style.fontSize,fontFamily:e.config.xaxis.title.style.fontFamily,fontWeight:e.config.xaxis.title.style.fontWeight,foreColor:e.config.xaxis.title.style.color,cssClass:\"apexcharts-xaxis-title-text \"+e.config.xaxis.title.style.cssClass});a.add(s),t.add(a)}}},{key:\"yAxisTitleRotate\",value:function(t,e){var i=this.w,a=new m(this.ctx),s={width:0,height:0},r={width:0,height:0},o=i.globals.dom.baseEl.querySelector(\" .apexcharts-yaxis[rel='\".concat(t,\"'] .apexcharts-yaxis-texts-g\"));null!==o&&(s=o.getBoundingClientRect());var n=i.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(t,\"'] .apexcharts-yaxis-title text\"));if(null!==n&&(r=n.getBoundingClientRect()),null!==n){var l=this.xPaddingForYAxisTitle(t,s,r,e);n.setAttribute(\"x\",l.xPos-(e?10:0))}if(null!==n){var h=a.rotateAroundCenter(n);n.setAttribute(\"transform\",\"rotate(\".concat(e?-1*i.config.yaxis[t].title.rotate:i.config.yaxis[t].title.rotate,\" \").concat(h.x,\" \").concat(h.y,\")\"))}}},{key:\"xPaddingForYAxisTitle\",value:function(t,e,i,a){var s=this.w,r=0,o=0,n=10;return void 0===s.config.yaxis[t].title.text||t<0?{xPos:o,padd:0}:(a?(o=e.width+s.config.yaxis[t].title.offsetX+i.width/2+n/2,0===(r+=1)&&(o-=n/2)):(o=-1*e.width+s.config.yaxis[t].title.offsetX+n/2+i.width/2,s.globals.isBarHorizontal&&(n=25,o=-1*e.width-s.config.yaxis[t].title.offsetX-n)),{xPos:o,padd:n})}},{key:\"setYAxisXPosition\",value:function(t,e){var i=this.w,a=0,s=0,r=18,o=1;i.config.yaxis.length>1&&(this.multipleYs=!0),i.config.yaxis.map((function(n,l){var h=i.globals.ignoreYAxisIndexes.indexOf(l)>-1||!n.show||n.floating||0===t[l].width,c=t[l].width+e[l].width;n.opposite?i.globals.isBarHorizontal?(s=i.globals.gridWidth+i.globals.translateX-1,i.globals.translateYAxisX[l]=s-n.labels.offsetX):(s=i.globals.gridWidth+i.globals.translateX+o,h||(o=o+c+20),i.globals.translateYAxisX[l]=s-n.labels.offsetX+20):(a=i.globals.translateX-r,h||(r=r+c+20),i.globals.translateYAxisX[l]=a+n.labels.offsetX)}))}},{key:\"setYAxisTextAlignments\",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName(\"apexcharts-yaxis\");(e=x.listToArray(e)).forEach((function(e,i){var a=t.config.yaxis[i];if(a&&!a.floating&&void 0!==a.labels.align){var s=t.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(i,\"'] .apexcharts-yaxis-texts-g\")),r=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis[rel='\".concat(i,\"'] .apexcharts-yaxis-label\"));r=x.listToArray(r);var o=s.getBoundingClientRect();\"left\"===a.labels.align?(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"start\")})),a.opposite||s.setAttribute(\"transform\",\"translate(-\".concat(o.width,\", 0)\"))):\"center\"===a.labels.align?(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"middle\")})),s.setAttribute(\"transform\",\"translate(\".concat(o.width/2*(a.opposite?1:-1),\", 0)\"))):\"right\"===a.labels.align&&(r.forEach((function(t,e){t.setAttribute(\"text-anchor\",\"end\")})),a.opposite&&s.setAttribute(\"transform\",\"translate(\".concat(o.width,\", 0)\")))}}))}}]),t}(),Z=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.documentEvent=x.bind(this.documentEvent,this)}return r(t,[{key:\"addEventListener\",value:function(t,e){var i=this.w;i.globals.events.hasOwnProperty(t)?i.globals.events[t].push(e):i.globals.events[t]=[e]}},{key:\"removeEventListener\",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){var a=i.globals.events[t].indexOf(e);-1!==a&&i.globals.events[t].splice(a,1)}}},{key:\"fireEvent\",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){e&&e.length||(e=[]);for(var a=i.globals.events[t],s=a.length,r=0;r<s;r++)a[r].apply(null,e)}}},{key:\"setupEventHandlers\",value:function(){var t=this,e=this.w,i=this.ctx,a=e.globals.dom.baseEl.querySelector(e.globals.chartClass);this.ctx.eventList.forEach((function(t){a.addEventListener(t,(function(t){var a=Object.assign({},e,{seriesIndex:e.globals.capturedSeriesIndex,dataPointIndex:e.globals.capturedDataPointIndex});\"mousemove\"===t.type||\"touchmove\"===t.type?\"function\"==typeof e.config.chart.events.mouseMove&&e.config.chart.events.mouseMove(t,i,a):\"mouseleave\"===t.type||\"touchleave\"===t.type?\"function\"==typeof e.config.chart.events.mouseLeave&&e.config.chart.events.mouseLeave(t,i,a):(\"mouseup\"===t.type&&1===t.which||\"touchend\"===t.type)&&(\"function\"==typeof e.config.chart.events.click&&e.config.chart.events.click(t,i,a),i.ctx.events.fireEvent(\"click\",[t,i,a]))}),{capture:!1,passive:!0})})),this.ctx.eventList.forEach((function(i){e.globals.dom.baseEl.addEventListener(i,t.documentEvent,{passive:!0})})),this.ctx.core.setupBrushHandler()}},{key:\"documentEvent\",value:function(t){var e=this.w,i=t.target.className;if(\"click\"===t.type){var a=e.globals.dom.baseEl.querySelector(\".apexcharts-menu\");a&&a.classList.contains(\"apexcharts-menu-open\")&&\"apexcharts-menu-icon\"!==i&&a.classList.remove(\"apexcharts-menu-open\")}e.globals.clientX=\"touchmove\"===t.type?t.touches[0].clientX:t.clientX,e.globals.clientY=\"touchmove\"===t.type?t.touches[0].clientY:t.clientY}}]),t}(),$=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"setCurrentLocaleValues\",value:function(t){var e=this.w.config.chart.locales;window.Apex.chart&&window.Apex.chart.locales&&window.Apex.chart.locales.length>0&&(e=this.w.config.chart.locales.concat(window.Apex.chart.locales));var i=e.filter((function(e){return e.name===t}))[0];if(!i)throw new Error(\"Wrong locale name provided. Please make sure you set the correct locale name in options\");var a=x.extend(C,i);this.w.globals.locale=a.options}}]),t}(),J=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"drawAxis\",value:function(t,e){var i,a,s=this,r=this.w.globals,o=this.w.config,n=new V(this.ctx,e),l=new q(this.ctx,e);r.axisCharts&&\"radar\"!==t&&(r.isBarHorizontal?(a=l.drawYaxisInversed(0),i=n.drawXaxisInversed(0),r.dom.elGraphical.add(i),r.dom.elGraphical.add(a)):(i=n.drawXaxis(),r.dom.elGraphical.add(i),o.yaxis.map((function(t,e){if(-1===r.ignoreYAxisIndexes.indexOf(e)&&(a=l.drawYaxis(e),r.dom.Paper.add(a),\"back\"===s.w.config.grid.position)){var i=r.dom.Paper.children()[1];i.remove(),r.dom.Paper.add(i)}}))))}}]),t}(),Q=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"drawXCrosshairs\",value:function(){var t=this.w,e=new m(this.ctx),i=new v(this.ctx),a=t.config.xaxis.crosshairs.fill.gradient,s=t.config.xaxis.crosshairs.dropShadow,r=t.config.xaxis.crosshairs.fill.type,o=a.colorFrom,n=a.colorTo,l=a.opacityFrom,h=a.opacityTo,c=a.stops,d=s.enabled,g=s.left,u=s.top,f=s.blur,p=s.color,b=s.opacity,y=t.config.xaxis.crosshairs.fill.color;if(t.config.xaxis.crosshairs.show){\"gradient\"===r&&(y=e.drawGradient(\"vertical\",o,n,l,h,null,c,null));var w=e.drawRect();1===t.config.xaxis.crosshairs.width&&(w=e.drawLine());var k=t.globals.gridHeight;(!x.isNumber(k)||k<0)&&(k=0);var A=t.config.xaxis.crosshairs.width;(!x.isNumber(A)||A<0)&&(A=0),w.attr({class:\"apexcharts-xcrosshairs\",x:0,y:0,y2:k,width:A,height:k,fill:y,filter:\"none\",\"fill-opacity\":t.config.xaxis.crosshairs.opacity,stroke:t.config.xaxis.crosshairs.stroke.color,\"stroke-width\":t.config.xaxis.crosshairs.stroke.width,\"stroke-dasharray\":t.config.xaxis.crosshairs.stroke.dashArray}),d&&(w=i.dropShadow(w,{left:g,top:u,blur:f,color:p,opacity:b})),t.globals.dom.elGraphical.add(w)}}},{key:\"drawYCrosshairs\",value:function(){var t=this.w,e=new m(this.ctx),i=t.config.yaxis[0].crosshairs,a=t.globals.barPadForNumericAxis;if(t.config.yaxis[0].crosshairs.show){var s=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,i.stroke.dashArray,i.stroke.width);s.attr({class:\"apexcharts-ycrosshairs\"}),t.globals.dom.elGraphical.add(s)}var r=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,0,0);r.attr({class:\"apexcharts-ycrosshairs-hidden\"}),t.globals.dom.elGraphical.add(r)}}]),t}(),K=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"checkResponsiveConfig\",value:function(t){var e=this,i=this.w,a=i.config;if(0!==a.responsive.length){var s=a.responsive.slice();s.sort((function(t,e){return t.breakpoint>e.breakpoint?1:e.breakpoint>t.breakpoint?-1:0})).reverse();var r=new E({}),o=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=s[0].breakpoint,o=window.innerWidth>0?window.innerWidth:screen.width;if(o>a){var n=y.extendArrayProps(r,i.globals.initialConfig,i);t=x.extend(n,t),t=x.extend(i.config,t),e.overrideResponsiveOptions(t)}else for(var l=0;l<s.length;l++)o<s[l].breakpoint&&(t=y.extendArrayProps(r,s[l].options,i),t=x.extend(i.config,t),e.overrideResponsiveOptions(t))};if(t){var n=y.extendArrayProps(r,t,i);n=x.extend(i.config,n),o(n=x.extend(n,t))}else o({})}}},{key:\"overrideResponsiveOptions\",value:function(t){var e=new E(t).init({responsiveOverride:!0});this.w.config=e}}]),t}(),tt=function(){function t(e){a(this,t),this.ctx=e,this.colors=[],this.w=e.w;var i=this.w;this.isColorFn=!1,this.isHeatmapDistributed=\"treemap\"===i.config.chart.type&&i.config.plotOptions.treemap.distributed||\"heatmap\"===i.config.chart.type&&i.config.plotOptions.heatmap.distributed,this.isBarDistributed=i.config.plotOptions.bar.distributed&&(\"bar\"===i.config.chart.type||\"rangeBar\"===i.config.chart.type)}return r(t,[{key:\"init\",value:function(){this.setDefaultColors()}},{key:\"setDefaultColors\",value:function(){var t=this,e=this.w,i=new x;if(e.globals.dom.elWrap.classList.add(\"apexcharts-theme-\".concat(e.config.theme.mode)),void 0===e.config.colors?e.globals.colors=this.predefined():(e.globals.colors=e.config.colors,Array.isArray(e.config.colors)&&e.config.colors.length>0&&\"function\"==typeof e.config.colors[0]&&(e.globals.colors=e.config.series.map((function(i,a){var s=e.config.colors[a];return s||(s=e.config.colors[0]),\"function\"==typeof s?(t.isColorFn=!0,s({value:e.globals.axisCharts?e.globals.series[a][0]?e.globals.series[a][0]:0:e.globals.series[a],seriesIndex:a,dataPointIndex:a,w:e})):s})))),e.globals.seriesColors.map((function(t,i){t&&(e.globals.colors[i]=t)})),e.config.theme.monochrome.enabled){var a=[],s=e.globals.series.length;(this.isBarDistributed||this.isHeatmapDistributed)&&(s=e.globals.series[0].length*e.globals.series.length);for(var r=e.config.theme.monochrome.color,o=1/(s/e.config.theme.monochrome.shadeIntensity),n=e.config.theme.monochrome.shadeTo,l=0,h=0;h<s;h++){var c=void 0;\"dark\"===n?(c=i.shadeColor(-1*l,r),l+=o):(c=i.shadeColor(l,r),l+=o),a.push(c)}e.globals.colors=a.slice()}var d=e.globals.colors.slice();this.pushExtraColors(e.globals.colors);[\"fill\",\"stroke\"].forEach((function(i){void 0===e.config[i].colors?e.globals[i].colors=t.isColorFn?e.config.colors:d:e.globals[i].colors=e.config[i].colors.slice(),t.pushExtraColors(e.globals[i].colors)})),void 0===e.config.dataLabels.style.colors?e.globals.dataLabels.style.colors=d:e.globals.dataLabels.style.colors=e.config.dataLabels.style.colors.slice(),this.pushExtraColors(e.globals.dataLabels.style.colors,50),void 0===e.config.plotOptions.radar.polygons.fill.colors?e.globals.radarPolygons.fill.colors=[\"dark\"===e.config.theme.mode?\"#424242\":\"none\"]:e.globals.radarPolygons.fill.colors=e.config.plotOptions.radar.polygons.fill.colors.slice(),this.pushExtraColors(e.globals.radarPolygons.fill.colors,20),void 0===e.config.markers.colors?e.globals.markers.colors=d:e.globals.markers.colors=e.config.markers.colors.slice(),this.pushExtraColors(e.globals.markers.colors)}},{key:\"pushExtraColors\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=e||a.globals.series.length;if(null===i&&(i=this.isBarDistributed||this.isHeatmapDistributed||\"heatmap\"===a.config.chart.type&&a.config.plotOptions.heatmap.colorScale.inverse),i&&a.globals.series.length&&(s=a.globals.series[a.globals.maxValsInArrayIndex].length*a.globals.series.length),t.length<s)for(var r=s-t.length,o=0;o<r;o++)t.push(t[o])}},{key:\"updateThemeOptions\",value:function(t){t.chart=t.chart||{},t.tooltip=t.tooltip||{};var e=t.theme.mode||\"light\",i=t.theme.palette?t.theme.palette:\"dark\"===e?\"palette4\":\"palette1\",a=t.chart.foreColor?t.chart.foreColor:\"dark\"===e?\"#f6f7f8\":\"#373d3f\";return t.tooltip.theme=e,t.chart.foreColor=a,t.theme.palette=i,t}},{key:\"predefined\",value:function(){switch(this.w.config.theme.palette){case\"palette1\":default:this.colors=[\"#008FFB\",\"#00E396\",\"#FEB019\",\"#FF4560\",\"#775DD0\"];break;case\"palette2\":this.colors=[\"#3f51b5\",\"#03a9f4\",\"#4caf50\",\"#f9ce1d\",\"#FF9800\"];break;case\"palette3\":this.colors=[\"#33b2df\",\"#546E7A\",\"#d4526e\",\"#13d8aa\",\"#A5978B\"];break;case\"palette4\":this.colors=[\"#4ecdc4\",\"#c7f464\",\"#81D4FA\",\"#fd6a6a\",\"#546E7A\"];break;case\"palette5\":this.colors=[\"#2b908f\",\"#f9a3a4\",\"#90ee7e\",\"#fa4443\",\"#69d2e7\"];break;case\"palette6\":this.colors=[\"#449DD1\",\"#F86624\",\"#EA3546\",\"#662E9B\",\"#C5D86D\"];break;case\"palette7\":this.colors=[\"#D7263D\",\"#1B998B\",\"#2E294E\",\"#F46036\",\"#E2C044\"];break;case\"palette8\":this.colors=[\"#662E9B\",\"#F86624\",\"#F9C80E\",\"#EA3546\",\"#43BCCD\"];break;case\"palette9\":this.colors=[\"#5C4742\",\"#A5978B\",\"#8D5B4C\",\"#5A2A27\",\"#C4BBAF\"];break;case\"palette10\":this.colors=[\"#A300D6\",\"#7D02EB\",\"#5653FE\",\"#2983FF\",\"#00B1F2\"]}return this.colors}}]),t}(),et=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"draw\",value:function(){this.drawTitleSubtitle(\"title\"),this.drawTitleSubtitle(\"subtitle\")}},{key:\"drawTitleSubtitle\",value:function(t){var e=this.w,i=\"title\"===t?e.config.title:e.config.subtitle,a=e.globals.svgWidth/2,s=i.offsetY,r=\"middle\";if(\"left\"===i.align?(a=10,r=\"start\"):\"right\"===i.align&&(a=e.globals.svgWidth-10,r=\"end\"),a+=i.offsetX,s=s+parseInt(i.style.fontSize,10)+i.margin/2,void 0!==i.text){var o=new m(this.ctx).drawText({x:a,y:s,text:i.text,textAnchor:r,fontSize:i.style.fontSize,fontFamily:i.style.fontFamily,fontWeight:i.style.fontWeight,foreColor:i.style.color,opacity:1});o.node.setAttribute(\"class\",\"apexcharts-\".concat(t,\"-text\")),e.globals.dom.Paper.add(o)}}}]),t}(),it=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"getTitleSubtitleCoords\",value:function(t){var e=this.w,i=0,a=0,s=\"title\"===t?e.config.title.floating:e.config.subtitle.floating,r=e.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(t,\"-text\"));if(null!==r&&!s){var o=r.getBoundingClientRect();i=o.width,a=e.globals.axisCharts?o.height+5:o.height}return{width:i,height:a}}},{key:\"getLegendsRect\",value:function(){var t=this.w,e=t.globals.dom.baseEl.querySelector(\".apexcharts-legend\");t.config.legend.height||\"top\"!==t.config.legend.position&&\"bottom\"!==t.config.legend.position||(e.style.maxHeight=t.globals.svgHeight/2+\"px\");var i=Object.assign({},x.getBoundingClientRect(e));return null!==e&&!t.config.legend.floating&&t.config.legend.show?this.dCtx.lgRect={x:i.x,y:i.y,height:i.height,width:0===i.height?0:i.width}:this.dCtx.lgRect={x:0,y:0,height:0,width:0},\"left\"!==t.config.legend.position&&\"right\"!==t.config.legend.position||1.5*this.dCtx.lgRect.width>t.globals.svgWidth&&(this.dCtx.lgRect.width=t.globals.svgWidth/1.5),this.dCtx.lgRect}},{key:\"getLargestStringFromMultiArr\",value:function(t,e){var i=t;if(this.w.globals.isMultiLineX){var a=e.map((function(t,e){return Array.isArray(t)?t.length:1})),s=Math.max.apply(Math,u(a));i=e[a.indexOf(s)]}return i}}]),t}(),at=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"getxAxisLabelsCoords\",value:function(){var t,e=this.w,i=e.globals.labels.slice();if(e.config.xaxis.convertedCatToNumeric&&0===i.length&&(i=e.globals.categoryLabels),e.globals.timescaleLabels.length>0){var a=this.getxAxisTimeScaleLabelsCoords();t={width:a.width,height:a.height},e.globals.rotateXLabels=!1}else{this.dCtx.lgWidthForSideLegends=\"left\"!==e.config.legend.position&&\"right\"!==e.config.legend.position||e.config.legend.floating?0:this.dCtx.lgRect.width;var s=e.globals.xLabelFormatter,r=x.getLargestStringFromArr(i),o=this.dCtx.dimHelpers.getLargestStringFromMultiArr(r,i);e.globals.isBarHorizontal&&(o=r=e.globals.yAxisScale[0].result.reduce((function(t,e){return t.length>e.length?t:e}),0));var n=new M(this.dCtx.ctx),l=r;r=n.xLabelFormat(s,r,l,{i:void 0,dateFormatter:new T(this.dCtx.ctx).formatDate,w:e}),o=n.xLabelFormat(s,o,l,{i:void 0,dateFormatter:new T(this.dCtx.ctx).formatDate,w:e}),(e.config.xaxis.convertedCatToNumeric&&void 0===r||\"\"===String(r).trim())&&(o=r=\"1\");var h=new m(this.dCtx.ctx),c=h.getTextRects(r,e.config.xaxis.labels.style.fontSize),d=c;if(r!==o&&(d=h.getTextRects(o,e.config.xaxis.labels.style.fontSize)),(t={width:c.width>=d.width?c.width:d.width,height:c.height>=d.height?c.height:d.height}).width*i.length>e.globals.svgWidth-this.dCtx.lgWidthForSideLegends-this.dCtx.yAxisWidth-this.dCtx.gridPad.left-this.dCtx.gridPad.right&&0!==e.config.xaxis.labels.rotate||e.config.xaxis.labels.rotateAlways){if(!e.globals.isBarHorizontal){e.globals.rotateXLabels=!0;var g=function(t){return h.getTextRects(t,e.config.xaxis.labels.style.fontSize,e.config.xaxis.labels.style.fontFamily,\"rotate(\".concat(e.config.xaxis.labels.rotate,\" 0 0)\"),!1)};c=g(r),r!==o&&(d=g(o)),t.height=(c.height>d.height?c.height:d.height)/1.5,t.width=c.width>d.width?c.width:d.width}}else e.globals.rotateXLabels=!1}return e.config.xaxis.labels.show||(t={width:0,height:0}),{width:t.width,height:t.height}}},{key:\"getxAxisGroupLabelsCoords\",value:function(){var t,e=this.w;if(!e.globals.hasGroups)return{width:0,height:0};var i,a=(null===(t=e.config.xaxis.group.style)||void 0===t?void 0:t.fontSize)||e.config.xaxis.labels.style.fontSize,s=e.globals.groups.map((function(t){return t.title})),r=x.getLargestStringFromArr(s),o=this.dCtx.dimHelpers.getLargestStringFromMultiArr(r,s),n=new m(this.dCtx.ctx),l=n.getTextRects(r,a),h=l;return r!==o&&(h=n.getTextRects(o,a)),i={width:l.width>=h.width?l.width:h.width,height:l.height>=h.height?l.height:h.height},e.config.xaxis.labels.show||(i={width:0,height:0}),{width:i.width,height:i.height}}},{key:\"getxAxisTitleCoords\",value:function(){var t=this.w,e=0,i=0;if(void 0!==t.config.xaxis.title.text){var a=new m(this.dCtx.ctx).getTextRects(t.config.xaxis.title.text,t.config.xaxis.title.style.fontSize);e=a.width,i=a.height}return{width:e,height:i}}},{key:\"getxAxisTimeScaleLabelsCoords\",value:function(){var t,e=this.w;this.dCtx.timescaleLabels=e.globals.timescaleLabels.slice();var i=this.dCtx.timescaleLabels.map((function(t){return t.value})),a=i.reduce((function(t,e){return void 0===t?(console.error(\"You have possibly supplied invalid Date format. Please supply a valid JavaScript Date\"),0):t.length>e.length?t:e}),0);return 1.05*(t=new m(this.dCtx.ctx).getTextRects(a,e.config.xaxis.labels.style.fontSize)).width*i.length>e.globals.gridWidth&&0!==e.config.xaxis.labels.rotate&&(e.globals.overlappingXLabels=!0),t}},{key:\"additionalPaddingXLabels\",value:function(t){var e=this,i=this.w,a=i.globals,s=i.config,r=s.xaxis.type,o=t.width;a.skipLastTimelinelabel=!1,a.skipFirstTimelinelabel=!1;var n=i.config.yaxis[0].opposite&&i.globals.isBarHorizontal,l=function(t,n){(function(t){return-1!==a.collapsedSeriesIndices.indexOf(t)})(n)||function(t){if(e.dCtx.timescaleLabels&&e.dCtx.timescaleLabels.length){var n=e.dCtx.timescaleLabels[0],l=e.dCtx.timescaleLabels[e.dCtx.timescaleLabels.length-1].position+o/1.75-e.dCtx.yAxisWidthRight,h=n.position-o/1.75+e.dCtx.yAxisWidthLeft,c=\"right\"===i.config.legend.position&&e.dCtx.lgRect.width>0?e.dCtx.lgRect.width:0;l>a.svgWidth-a.translateX-c&&(a.skipLastTimelinelabel=!0),h<-(t.show&&!t.floating||\"bar\"!==s.chart.type&&\"candlestick\"!==s.chart.type&&\"rangeBar\"!==s.chart.type&&\"boxPlot\"!==s.chart.type?10:o/1.75)&&(a.skipFirstTimelinelabel=!0)}else\"datetime\"===r?e.dCtx.gridPad.right<o&&!a.rotateXLabels&&(a.skipLastTimelinelabel=!0):\"datetime\"!==r&&e.dCtx.gridPad.right<o/2-e.dCtx.yAxisWidthRight&&!a.rotateXLabels&&!i.config.xaxis.labels.trim&&(\"between\"!==i.config.xaxis.tickPlacement||i.globals.isBarHorizontal)&&(e.dCtx.xPadRight=o/2+1)}(t)};s.yaxis.forEach((function(t,i){n?(e.dCtx.gridPad.left<o&&(e.dCtx.xPadLeft=o/2+1),e.dCtx.xPadRight=o/2+1):l(t,i)}))}}]),t}(),st=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"getyAxisLabelsCoords\",value:function(){var t=this,e=this.w,i=[],a=10,s=new B(this.dCtx.ctx);return e.config.yaxis.map((function(r,o){var n=e.globals.yAxisScale[o],l=0;if(!s.isYAxisHidden(o)&&r.labels.show&&void 0!==r.labels.minWidth&&(l=r.labels.minWidth),!s.isYAxisHidden(o)&&r.labels.show&&n.result.length){var h=e.globals.yLabelFormatters[o],c=n.niceMin===Number.MIN_VALUE?0:n.niceMin,d=String(c).length>String(n.niceMax).length?c:n.niceMax,g=h(d,{seriesIndex:o,dataPointIndex:-1,w:e}),u=g;if(void 0!==g&&0!==g.length||(g=d),e.globals.isBarHorizontal){a=0;var f=e.globals.labels.slice();g=h(g=x.getLargestStringFromArr(f),{seriesIndex:o,dataPointIndex:-1,w:e}),u=t.dCtx.dimHelpers.getLargestStringFromMultiArr(g,f)}var p=new m(t.dCtx.ctx),b=\"rotate(\".concat(r.labels.rotate,\" 0 0)\"),v=p.getTextRects(g,r.labels.style.fontSize,r.labels.style.fontFamily,b,!1),y=v;g!==u&&(y=p.getTextRects(u,r.labels.style.fontSize,r.labels.style.fontFamily,b,!1)),i.push({width:(l>y.width||l>v.width?l:y.width>v.width?y.width:v.width)+a,height:y.height>v.height?y.height:v.height})}else i.push({width:0,height:0})})),i}},{key:\"getyAxisTitleCoords\",value:function(){var t=this,e=this.w,i=[];return e.config.yaxis.map((function(e,a){if(e.show&&void 0!==e.title.text){var s=new m(t.dCtx.ctx),r=\"rotate(\".concat(e.title.rotate,\" 0 0)\"),o=s.getTextRects(e.title.text,e.title.style.fontSize,e.title.style.fontFamily,r,!1);i.push({width:o.width,height:o.height})}else i.push({width:0,height:0})})),i}},{key:\"getTotalYAxisWidth\",value:function(){var t=this.w,e=0,i=0,a=0,s=t.globals.yAxisScale.length>1?10:0,r=new B(this.dCtx.ctx),o=function(o,n){var l=t.config.yaxis[n].floating,h=0;o.width>0&&!l?(h=o.width+s,function(e){return t.globals.ignoreYAxisIndexes.indexOf(e)>-1}(n)&&(h=h-o.width-s)):h=l||r.isYAxisHidden(n)?0:5,t.config.yaxis[n].opposite?a+=h:i+=h,e+=h};return t.globals.yLabelsCoords.map((function(t,e){o(t,e)})),t.globals.yTitleCoords.map((function(t,e){o(t,e)})),t.globals.isBarHorizontal&&!t.config.yaxis[0].floating&&(e=t.globals.yLabelsCoords[0].width+t.globals.yTitleCoords[0].width+15),this.dCtx.yAxisWidthLeft=i,this.dCtx.yAxisWidthRight=a,e}}]),t}(),rt=function(){function t(e){a(this,t),this.w=e.w,this.dCtx=e}return r(t,[{key:\"gridPadForColumnsInNumericAxis\",value:function(t){var e=this.w;if(e.globals.noData||e.globals.allSeriesCollapsed)return 0;var i=function(t){return\"bar\"===t||\"rangeBar\"===t||\"candlestick\"===t||\"boxPlot\"===t},a=e.config.chart.type,s=0,r=i(a)?e.config.series.length:1;if(e.globals.comboBarCount>0&&(r=e.globals.comboBarCount),e.globals.collapsedSeries.forEach((function(t){i(t.type)&&(r-=1)})),e.config.chart.stacked&&(r=1),(i(a)||e.globals.comboBarCount>0)&&e.globals.isXNumeric&&!e.globals.isBarHorizontal&&r>0){var o,n,l=Math.abs(e.globals.initialMaxX-e.globals.initialMinX);l<=3&&(l=e.globals.dataPoints),o=l/t,e.globals.minXDiff&&e.globals.minXDiff/o>0&&(n=e.globals.minXDiff/o),n>t/2&&(n/=2),(s=n/r*parseInt(e.config.plotOptions.bar.columnWidth,10)/100)<1&&(s=1),s=s/(r>1?1:1.5)+5,e.globals.barPadForNumericAxis=s}return s}},{key:\"gridPadFortitleSubtitle\",value:function(){var t=this,e=this.w,i=e.globals,a=this.dCtx.isSparkline||!e.globals.axisCharts?0:10;[\"title\",\"subtitle\"].forEach((function(i){void 0!==e.config[i].text?a+=e.config[i].margin:a+=t.dCtx.isSparkline||!e.globals.axisCharts?0:5})),!e.config.legend.show||\"bottom\"!==e.config.legend.position||e.config.legend.floating||e.globals.axisCharts||(a+=10);var s=this.dCtx.dimHelpers.getTitleSubtitleCoords(\"title\"),r=this.dCtx.dimHelpers.getTitleSubtitleCoords(\"subtitle\");i.gridHeight=i.gridHeight-s.height-r.height-a,i.translateY=i.translateY+s.height+r.height+a}},{key:\"setGridXPosForDualYAxis\",value:function(t,e){var i=this.w,a=new B(this.dCtx.ctx);i.config.yaxis.map((function(s,r){-1!==i.globals.ignoreYAxisIndexes.indexOf(r)||s.floating||a.isYAxisHidden(r)||(s.opposite&&(i.globals.translateX=i.globals.translateX-(e[r].width+t[r].width)-parseInt(i.config.yaxis[r].labels.style.fontSize,10)/1.2-12),i.globals.translateX<2&&(i.globals.translateX=2))}))}}]),t}(),ot=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.lgRect={},this.yAxisWidth=0,this.yAxisWidthLeft=0,this.yAxisWidthRight=0,this.xAxisHeight=0,this.isSparkline=this.w.config.chart.sparkline.enabled,this.dimHelpers=new it(this),this.dimYAxis=new st(this),this.dimXAxis=new at(this),this.dimGrid=new rt(this),this.lgWidthForSideLegends=0,this.gridPad=this.w.config.grid.padding,this.xPadRight=0,this.xPadLeft=0}return r(t,[{key:\"plotCoords\",value:function(){var t=this,e=this.w,i=e.globals;this.lgRect=this.dimHelpers.getLegendsRect(),this.isSparkline&&(e.config.markers.discrete.length>0||e.config.markers.size>0)&&Object.entries(this.gridPad).forEach((function(e){var i=g(e,2),a=i[0],s=i[1];t.gridPad[a]=Math.max(s,t.w.globals.markers.largestSize/1.5)})),i.axisCharts?this.setDimensionsForAxisCharts():this.setDimensionsForNonAxisCharts(),this.dimGrid.gridPadFortitleSubtitle(),i.gridHeight=i.gridHeight-this.gridPad.top-this.gridPad.bottom,i.gridWidth=i.gridWidth-this.gridPad.left-this.gridPad.right-this.xPadRight-this.xPadLeft;var a=this.dimGrid.gridPadForColumnsInNumericAxis(i.gridWidth);i.gridWidth=i.gridWidth-2*a,i.translateX=i.translateX+this.gridPad.left+this.xPadLeft+(a>0?a+4:0),i.translateY=i.translateY+this.gridPad.top}},{key:\"setDimensionsForAxisCharts\",value:function(){var t=this,e=this.w,i=e.globals,a=this.dimYAxis.getyAxisLabelsCoords(),s=this.dimYAxis.getyAxisTitleCoords();e.globals.yLabelsCoords=[],e.globals.yTitleCoords=[],e.config.yaxis.map((function(t,i){e.globals.yLabelsCoords.push({width:a[i].width,index:i}),e.globals.yTitleCoords.push({width:s[i].width,index:i})})),this.yAxisWidth=this.dimYAxis.getTotalYAxisWidth();var r=this.dimXAxis.getxAxisLabelsCoords(),o=this.dimXAxis.getxAxisGroupLabelsCoords(),n=this.dimXAxis.getxAxisTitleCoords();this.conditionalChecksForAxisCoords(r,n,o),i.translateXAxisY=e.globals.rotateXLabels?this.xAxisHeight/8:-4,i.translateXAxisX=e.globals.rotateXLabels&&e.globals.isXNumeric&&e.config.xaxis.labels.rotate<=-45?-this.xAxisWidth/4:0,e.globals.isBarHorizontal&&(i.rotateXLabels=!1,i.translateXAxisY=parseInt(e.config.xaxis.labels.style.fontSize,10)/1.5*-1),i.translateXAxisY=i.translateXAxisY+e.config.xaxis.labels.offsetY,i.translateXAxisX=i.translateXAxisX+e.config.xaxis.labels.offsetX;var l=this.yAxisWidth,h=this.xAxisHeight;i.xAxisLabelsHeight=this.xAxisHeight-n.height,i.xAxisGroupLabelsHeight=i.xAxisLabelsHeight-r.height,i.xAxisLabelsWidth=this.xAxisWidth,i.xAxisHeight=this.xAxisHeight;var c=10;(\"radar\"===e.config.chart.type||this.isSparkline)&&(l=0,h=i.goldenPadding),this.isSparkline&&(this.lgRect={height:0,width:0}),(this.isSparkline||\"treemap\"===e.config.chart.type)&&(l=0,h=0,c=0),this.isSparkline||this.dimXAxis.additionalPaddingXLabels(r);var d=function(){i.translateX=l,i.gridHeight=i.svgHeight-t.lgRect.height-h-(t.isSparkline||\"treemap\"===e.config.chart.type?0:e.globals.rotateXLabels?10:15),i.gridWidth=i.svgWidth-l};switch(\"top\"===e.config.xaxis.position&&(c=i.xAxisHeight-e.config.xaxis.axisTicks.height-5),e.config.legend.position){case\"bottom\":i.translateY=c,d();break;case\"top\":i.translateY=this.lgRect.height+c,d();break;case\"left\":i.translateY=c,i.translateX=this.lgRect.width+l,i.gridHeight=i.svgHeight-h-12,i.gridWidth=i.svgWidth-this.lgRect.width-l;break;case\"right\":i.translateY=c,i.translateX=l,i.gridHeight=i.svgHeight-h-12,i.gridWidth=i.svgWidth-this.lgRect.width-l-5;break;default:throw new Error(\"Legend position not supported\")}this.dimGrid.setGridXPosForDualYAxis(s,a),new q(this.ctx).setYAxisXPosition(a,s)}},{key:\"setDimensionsForNonAxisCharts\",value:function(){var t=this.w,e=t.globals,i=t.config,a=0;t.config.legend.show&&!t.config.legend.floating&&(a=20);var s=\"pie\"===i.chart.type||\"polarArea\"===i.chart.type||\"donut\"===i.chart.type?\"pie\":\"radialBar\",r=i.plotOptions[s].offsetY,o=i.plotOptions[s].offsetX;if(!i.legend.show||i.legend.floating)return e.gridHeight=e.svgHeight-i.grid.padding.left+i.grid.padding.right,e.gridWidth=e.gridHeight,e.translateY=r,void(e.translateX=o+(e.svgWidth-e.gridWidth)/2);switch(i.legend.position){case\"bottom\":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.svgWidth,e.translateY=r-10,e.translateX=o+(e.svgWidth-e.gridWidth)/2;break;case\"top\":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.svgWidth,e.translateY=this.lgRect.height+r+10,e.translateX=o+(e.svgWidth-e.gridWidth)/2;break;case\"left\":e.gridWidth=e.svgWidth-this.lgRect.width-a,e.gridHeight=\"auto\"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=o+this.lgRect.width+a;break;case\"right\":e.gridWidth=e.svgWidth-this.lgRect.width-a-5,e.gridHeight=\"auto\"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=o+10;break;default:throw new Error(\"Legend position not supported\")}}},{key:\"conditionalChecksForAxisCoords\",value:function(t,e,i){var a=this.w,s=a.globals.hasGroups?2:1,r=i.height+t.height+e.height,o=a.globals.isMultiLineX?1.2:a.globals.LINE_HEIGHT_RATIO,n=a.globals.rotateXLabels?22:10,l=a.globals.rotateXLabels&&\"bottom\"===a.config.legend.position?10:0;this.xAxisHeight=r*o+s*n+l,this.xAxisWidth=t.width,this.xAxisHeight-e.height>a.config.xaxis.labels.maxHeight&&(this.xAxisHeight=a.config.xaxis.labels.maxHeight),a.config.xaxis.labels.minHeight&&this.xAxisHeight<a.config.xaxis.labels.minHeight&&(this.xAxisHeight=a.config.xaxis.labels.minHeight),a.config.xaxis.floating&&(this.xAxisHeight=0);var h=0,c=0;a.config.yaxis.forEach((function(t){h+=t.labels.minWidth,c+=t.labels.maxWidth})),this.yAxisWidth<h&&(this.yAxisWidth=h),this.yAxisWidth>c&&(this.yAxisWidth=c)}}]),t}(),nt=function(){function t(e){a(this,t),this.w=e.w,this.lgCtx=e}return r(t,[{key:\"getLegendBBox\",value:function(){var t=this.w.globals.dom.baseEl.querySelector(\".apexcharts-legend\").getBoundingClientRect(),e=t.width;return{clwh:t.height,clww:e}}},{key:\"toggleDataSeries\",value:function(t,e){var i=this,a=this.w;if(a.globals.axisCharts||\"radialBar\"===a.config.chart.type){a.globals.resized=!0;var s=null,r=null;if(a.globals.risingSeries=[],a.globals.axisCharts?(s=a.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(t,\"']\")),r=parseInt(s.getAttribute(\"data:realIndex\"),10)):(s=a.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(t+1,\"']\")),r=parseInt(s.getAttribute(\"rel\"),10)-1),e)[{cs:a.globals.collapsedSeries,csi:a.globals.collapsedSeriesIndices},{cs:a.globals.ancillaryCollapsedSeries,csi:a.globals.ancillaryCollapsedSeriesIndices}].forEach((function(t){i.riseCollapsedSeries(t.cs,t.csi,r)}));else this.hideSeries({seriesEl:s,realIndex:r})}else{var o=a.globals.dom.Paper.select(\" .apexcharts-series[rel='\".concat(t+1,\"'] path\")),n=a.config.chart.type;if(\"pie\"===n||\"polarArea\"===n||\"donut\"===n){var l=a.config.plotOptions.pie.donut.labels;new m(this.lgCtx.ctx).pathMouseDown(o.members[0],null),this.lgCtx.ctx.pie.printDataLabelsInner(o.members[0].node,l)}o.fire(\"click\")}}},{key:\"hideSeries\",value:function(t){var e=t.seriesEl,i=t.realIndex,a=this.w,s=x.clone(a.config.series);if(a.globals.axisCharts){var r=!1;if(a.config.yaxis[i]&&a.config.yaxis[i].show&&a.config.yaxis[i].showAlways&&(r=!0,a.globals.ancillaryCollapsedSeriesIndices.indexOf(i)<0&&(a.globals.ancillaryCollapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split(\"-\")[1]}),a.globals.ancillaryCollapsedSeriesIndices.push(i))),!r){a.globals.collapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split(\"-\")[1]}),a.globals.collapsedSeriesIndices.push(i);var o=a.globals.risingSeries.indexOf(i);a.globals.risingSeries.splice(o,1)}}else a.globals.collapsedSeries.push({index:i,data:s[i]}),a.globals.collapsedSeriesIndices.push(i);for(var n=e.childNodes,l=0;l<n.length;l++)n[l].classList.contains(\"apexcharts-series-markers-wrap\")&&(n[l].classList.contains(\"apexcharts-hide\")?n[l].classList.remove(\"apexcharts-hide\"):n[l].classList.add(\"apexcharts-hide\"));a.globals.allSeriesCollapsed=a.globals.collapsedSeries.length===a.config.series.length,s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}},{key:\"riseCollapsedSeries\",value:function(t,e,i){var a=this.w,s=x.clone(a.config.series);if(t.length>0){for(var r=0;r<t.length;r++)t[r].index===i&&(a.globals.axisCharts?(s[i].data=t[r].data.slice(),t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)):(s[i]=t[r].data,t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)));s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}}},{key:\"_getSeriesBasedOnCollapsedState\",value:function(t){var e=this.w;return e.globals.axisCharts?t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a].data=[])})):t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a]=0)})),t}}]),t}(),lt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.onLegendClick=this.onLegendClick.bind(this),this.onLegendHovered=this.onLegendHovered.bind(this),this.isBarsDistributed=\"bar\"===this.w.config.chart.type&&this.w.config.plotOptions.bar.distributed&&1===this.w.config.series.length,this.legendHelpers=new nt(this)}return r(t,[{key:\"init\",value:function(){var t=this.w,e=t.globals,i=t.config;if((i.legend.showForSingleSeries&&1===e.series.length||this.isBarsDistributed||e.series.length>1||!e.axisCharts)&&i.legend.show){for(;e.dom.elLegendWrap.firstChild;)e.dom.elLegendWrap.removeChild(e.dom.elLegendWrap.firstChild);this.drawLegends(),\"bottom\"===i.legend.position||\"top\"===i.legend.position?this.legendAlignHorizontal():\"right\"!==i.legend.position&&\"left\"!==i.legend.position||this.legendAlignVertical()}}},{key:\"drawLegends\",value:function(){var t=this,e=this.w,i=e.config.legend.fontFamily,a=e.globals.seriesNames,s=e.globals.colors.slice();if(\"heatmap\"===e.config.chart.type){var r=e.config.plotOptions.heatmap.colorScale.ranges;a=r.map((function(t){return t.name?t.name:t.from+\" - \"+t.to})),s=r.map((function(t){return t.color}))}else this.isBarsDistributed&&(a=e.globals.labels.slice());e.config.legend.customLegendItems.length&&(a=e.config.legend.customLegendItems);for(var o=e.globals.legendFormatter,n=e.config.legend.inverseOrder,l=n?a.length-1:0;n?l>=0:l<=a.length-1;n?l--:l++){var h=o(a[l],{seriesIndex:l,w:e}),c=!1,d=!1;if(e.globals.collapsedSeries.length>0)for(var g=0;g<e.globals.collapsedSeries.length;g++)e.globals.collapsedSeries[g].index===l&&(c=!0);if(e.globals.ancillaryCollapsedSeriesIndices.length>0)for(var u=0;u<e.globals.ancillaryCollapsedSeriesIndices.length;u++)e.globals.ancillaryCollapsedSeriesIndices[u]===l&&(d=!0);var f=document.createElement(\"span\");f.classList.add(\"apexcharts-legend-marker\");var p=e.config.legend.markers.offsetX,b=e.config.legend.markers.offsetY,v=e.config.legend.markers.height,w=e.config.legend.markers.width,k=e.config.legend.markers.strokeWidth,A=e.config.legend.markers.strokeColor,S=e.config.legend.markers.radius,C=f.style;C.background=s[l],C.color=s[l],C.setProperty(\"background\",s[l],\"important\"),e.config.legend.markers.fillColors&&e.config.legend.markers.fillColors[l]&&(C.background=e.config.legend.markers.fillColors[l]),void 0!==e.globals.seriesColors[l]&&(C.background=e.globals.seriesColors[l],C.color=e.globals.seriesColors[l]),C.height=Array.isArray(v)?parseFloat(v[l])+\"px\":parseFloat(v)+\"px\",C.width=Array.isArray(w)?parseFloat(w[l])+\"px\":parseFloat(w)+\"px\",C.left=(Array.isArray(p)?parseFloat(p[l]):parseFloat(p))+\"px\",C.top=(Array.isArray(b)?parseFloat(b[l]):parseFloat(b))+\"px\",C.borderWidth=Array.isArray(k)?k[l]:k,C.borderColor=Array.isArray(A)?A[l]:A,C.borderRadius=Array.isArray(S)?parseFloat(S[l])+\"px\":parseFloat(S)+\"px\",e.config.legend.markers.customHTML&&(Array.isArray(e.config.legend.markers.customHTML)?e.config.legend.markers.customHTML[l]&&(f.innerHTML=e.config.legend.markers.customHTML[l]()):f.innerHTML=e.config.legend.markers.customHTML()),m.setAttrs(f,{rel:l+1,\"data:collapsed\":c||d}),(c||d)&&f.classList.add(\"apexcharts-inactive-legend\");var L=document.createElement(\"div\"),P=document.createElement(\"span\");P.classList.add(\"apexcharts-legend-text\"),P.innerHTML=Array.isArray(h)?h.join(\" \"):h;var T=e.config.legend.labels.useSeriesColors?e.globals.colors[l]:e.config.legend.labels.colors;T||(T=e.config.chart.foreColor),P.style.color=T,P.style.fontSize=parseFloat(e.config.legend.fontSize)+\"px\",P.style.fontWeight=e.config.legend.fontWeight,P.style.fontFamily=i||e.config.chart.fontFamily,m.setAttrs(P,{rel:l+1,i:l,\"data:default-text\":encodeURIComponent(h),\"data:collapsed\":c||d}),L.appendChild(f),L.appendChild(P);var M=new y(this.ctx);if(!e.config.legend.showForZeroSeries)0===M.getSeriesTotalByIndex(l)&&M.seriesHaveSameValues(l)&&!M.isSeriesNull(l)&&-1===e.globals.collapsedSeriesIndices.indexOf(l)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(l)&&L.classList.add(\"apexcharts-hidden-zero-series\");e.config.legend.showForNullSeries||M.isSeriesNull(l)&&-1===e.globals.collapsedSeriesIndices.indexOf(l)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(l)&&L.classList.add(\"apexcharts-hidden-null-series\"),e.globals.dom.elLegendWrap.appendChild(L),e.globals.dom.elLegendWrap.classList.add(\"apexcharts-align-\".concat(e.config.legend.horizontalAlign)),e.globals.dom.elLegendWrap.classList.add(\"apx-legend-position-\"+e.config.legend.position),L.classList.add(\"apexcharts-legend-series\"),L.style.margin=\"\".concat(e.config.legend.itemMargin.vertical,\"px \").concat(e.config.legend.itemMargin.horizontal,\"px\"),e.globals.dom.elLegendWrap.style.width=e.config.legend.width?e.config.legend.width+\"px\":\"\",e.globals.dom.elLegendWrap.style.height=e.config.legend.height?e.config.legend.height+\"px\":\"\",m.setAttrs(L,{rel:l+1,seriesName:x.escapeString(a[l]),\"data:collapsed\":c||d}),(c||d)&&L.classList.add(\"apexcharts-inactive-legend\"),e.config.legend.onItemClick.toggleDataSeries||L.classList.add(\"apexcharts-no-click\")}e.globals.dom.elWrap.addEventListener(\"click\",t.onLegendClick,!0),e.globals.dom.elWrap.appendChild(e.globals.dom.elLegendWrap),e.config.legend.onItemHover.highlightDataSeries&&0===e.config.legend.customLegendItems.length&&(e.globals.dom.elWrap.addEventListener(\"mousemove\",t.onLegendHovered,!0),e.globals.dom.elWrap.addEventListener(\"mouseout\",t.onLegendHovered,!0))}},{key:\"setLegendWrapXY\",value:function(t,e){var i=this.w,a=i.globals.dom.elLegendWrap,s=a.getBoundingClientRect(),r=0,o=0;if(\"bottom\"===i.config.legend.position)o+=i.globals.svgHeight-s.height/2;else if(\"top\"===i.config.legend.position){var n=new ot(this.ctx),l=n.dimHelpers.getTitleSubtitleCoords(\"title\").height,h=n.dimHelpers.getTitleSubtitleCoords(\"subtitle\").height;o=o+(l>0?l-10:0)+(h>0?h-10:0)}a.style.position=\"absolute\",r=r+t+i.config.legend.offsetX,o=o+e+i.config.legend.offsetY,a.style.left=r+\"px\",a.style.top=o+\"px\",\"bottom\"===i.config.legend.position?(a.style.top=\"auto\",a.style.bottom=5-i.config.legend.offsetY+\"px\"):\"right\"===i.config.legend.position&&(a.style.left=\"auto\",a.style.right=25+i.config.legend.offsetX+\"px\");[\"width\",\"height\"].forEach((function(t){a.style[t]&&(a.style[t]=parseInt(i.config.legend[t],10)+\"px\")}))}},{key:\"legendAlignHorizontal\",value:function(){var t=this.w;t.globals.dom.elLegendWrap.style.right=0;var e=this.legendHelpers.getLegendBBox(),i=new ot(this.ctx),a=i.dimHelpers.getTitleSubtitleCoords(\"title\"),s=i.dimHelpers.getTitleSubtitleCoords(\"subtitle\"),r=0;\"bottom\"===t.config.legend.position?r=-e.clwh/1.8:\"top\"===t.config.legend.position&&(r=a.height+s.height+t.config.title.margin+t.config.subtitle.margin-10),this.setLegendWrapXY(20,r)}},{key:\"legendAlignVertical\",value:function(){var t=this.w,e=this.legendHelpers.getLegendBBox(),i=0;\"left\"===t.config.legend.position&&(i=20),\"right\"===t.config.legend.position&&(i=t.globals.svgWidth-e.clww-10),this.setLegendWrapXY(i,20)}},{key:\"onLegendHovered\",value:function(t){var e=this.w,i=t.target.classList.contains(\"apexcharts-legend-text\")||t.target.classList.contains(\"apexcharts-legend-marker\");if(\"heatmap\"===e.config.chart.type||this.isBarsDistributed){if(i){var a=parseInt(t.target.getAttribute(\"rel\"),10)-1;this.ctx.events.fireEvent(\"legendHover\",[this.ctx,a,this.w]),new N(this.ctx).highlightRangeInSeries(t,t.target)}}else!t.target.classList.contains(\"apexcharts-inactive-legend\")&&i&&new N(this.ctx).toggleSeriesOnHover(t,t.target)}},{key:\"onLegendClick\",value:function(t){var e=this.w;if(!e.config.legend.customLegendItems.length&&(t.target.classList.contains(\"apexcharts-legend-text\")||t.target.classList.contains(\"apexcharts-legend-marker\"))){var i=parseInt(t.target.getAttribute(\"rel\"),10)-1,a=\"true\"===t.target.getAttribute(\"data:collapsed\"),s=this.w.config.chart.events.legendClick;\"function\"==typeof s&&s(this.ctx,i,this.w),this.ctx.events.fireEvent(\"legendClick\",[this.ctx,i,this.w]);var r=this.w.config.legend.markers.onClick;\"function\"==typeof r&&t.target.classList.contains(\"apexcharts-legend-marker\")&&(r(this.ctx,i,this.w),this.ctx.events.fireEvent(\"legendMarkerClick\",[this.ctx,i,this.w])),\"treemap\"!==e.config.chart.type&&\"heatmap\"!==e.config.chart.type&&!this.isBarsDistributed&&e.config.legend.onItemClick.toggleDataSeries&&this.legendHelpers.toggleDataSeries(i,a)}}}]),t}(),ht=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.ev=this.w.config.chart.events,this.selectedClass=\"apexcharts-selected\",this.localeValues=this.w.globals.locale.toolbar,this.minX=i.globals.minX,this.maxX=i.globals.maxX}return r(t,[{key:\"createToolbar\",value:function(){var t=this,e=this.w,i=function(){return document.createElement(\"div\")},a=i();if(a.setAttribute(\"class\",\"apexcharts-toolbar\"),a.style.top=e.config.chart.toolbar.offsetY+\"px\",a.style.right=3-e.config.chart.toolbar.offsetX+\"px\",e.globals.dom.elWrap.appendChild(a),this.elZoom=i(),this.elZoomIn=i(),this.elZoomOut=i(),this.elPan=i(),this.elSelection=i(),this.elZoomReset=i(),this.elMenuIcon=i(),this.elMenu=i(),this.elCustomIcons=[],this.t=e.config.chart.toolbar.tools,Array.isArray(this.t.customIcons))for(var s=0;s<this.t.customIcons.length;s++)this.elCustomIcons.push(i());var r=[],o=function(i,a,s){var o=i.toLowerCase();t.t[o]&&e.config.chart.zoom.enabled&&r.push({el:a,icon:\"string\"==typeof t.t[o]?t.t[o]:s,title:t.localeValues[i],class:\"apexcharts-\".concat(o,\"-icon\")})};o(\"zoomIn\",this.elZoomIn,'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n    <path d=\"M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"/>\\n</svg>\\n'),o(\"zoomOut\",this.elZoomOut,'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n    <path d=\"M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\"/>\\n</svg>\\n');var n=function(i){t.t[i]&&e.config.chart[i].enabled&&r.push({el:\"zoom\"===i?t.elZoom:t.elSelection,icon:\"string\"==typeof t.t[i]?t.t[i]:\"zoom\"===i?'<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\">\\n    <path d=\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"/>\\n    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\\n    <path d=\"M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z\"/>\\n</svg>':'<svg fill=\"#6E8192\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n    <path d=\"M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2z\"/>\\n</svg>',title:t.localeValues[\"zoom\"===i?\"selectionZoom\":\"selection\"],class:e.globals.isTouchDevice?\"apexcharts-element-hidden\":\"apexcharts-\".concat(i,\"-icon\")})};n(\"zoom\"),n(\"selection\"),this.t.pan&&e.config.chart.zoom.enabled&&r.push({el:this.elPan,icon:\"string\"==typeof this.t.pan?this.t.pan:'<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\">\\n    <defs>\\n        <path d=\"M0 0h24v24H0z\" id=\"a\"/>\\n    </defs>\\n    <clipPath id=\"b\">\\n        <use overflow=\"visible\" xlink:href=\"#a\"/>\\n    </clipPath>\\n    <path clip-path=\"url(#b)\" d=\"M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z\"/>\\n</svg>',title:this.localeValues.pan,class:e.globals.isTouchDevice?\"apexcharts-element-hidden\":\"apexcharts-pan-icon\"}),o(\"reset\",this.elZoomReset,'<svg fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\\n    <path d=\"M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z\"/>\\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\\n</svg>'),this.t.download&&r.push({el:this.elMenuIcon,icon:\"string\"==typeof this.t.download?this.t.download:'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z\"/></svg>',title:this.localeValues.menu,class:\"apexcharts-menu-icon\"});for(var l=0;l<this.elCustomIcons.length;l++)r.push({el:this.elCustomIcons[l],icon:this.t.customIcons[l].icon,title:this.t.customIcons[l].title,index:this.t.customIcons[l].index,class:\"apexcharts-toolbar-custom-icon \"+this.t.customIcons[l].class});r.forEach((function(t,e){t.index&&x.moveIndexInArray(r,e,t.index)}));for(var h=0;h<r.length;h++)m.setAttrs(r[h].el,{class:r[h].class,title:r[h].title}),r[h].el.innerHTML=r[h].icon,a.appendChild(r[h].el);this._createHamburgerMenu(a),e.globals.zoomEnabled?this.elZoom.classList.add(this.selectedClass):e.globals.panEnabled?this.elPan.classList.add(this.selectedClass):e.globals.selectionEnabled&&this.elSelection.classList.add(this.selectedClass),this.addToolbarEventListeners()}},{key:\"_createHamburgerMenu\",value:function(t){this.elMenuItems=[],t.appendChild(this.elMenu),m.setAttrs(this.elMenu,{class:\"apexcharts-menu\"});var e=[{name:\"exportSVG\",title:this.localeValues.exportToSVG},{name:\"exportPNG\",title:this.localeValues.exportToPNG},{name:\"exportCSV\",title:this.localeValues.exportToCSV}];this.w.globals.allSeriesHasEqualX||e.splice(2,1);for(var i=0;i<e.length;i++)this.elMenuItems.push(document.createElement(\"div\")),this.elMenuItems[i].innerHTML=e[i].title,m.setAttrs(this.elMenuItems[i],{class:\"apexcharts-menu-item \".concat(e[i].name),title:e[i].title}),this.elMenu.appendChild(this.elMenuItems[i])}},{key:\"addToolbarEventListeners\",value:function(){var t=this;this.elZoomReset.addEventListener(\"click\",this.handleZoomReset.bind(this)),this.elSelection.addEventListener(\"click\",this.toggleZoomSelection.bind(this,\"selection\")),this.elZoom.addEventListener(\"click\",this.toggleZoomSelection.bind(this,\"zoom\")),this.elZoomIn.addEventListener(\"click\",this.handleZoomIn.bind(this)),this.elZoomOut.addEventListener(\"click\",this.handleZoomOut.bind(this)),this.elPan.addEventListener(\"click\",this.togglePanning.bind(this)),this.elMenuIcon.addEventListener(\"click\",this.toggleMenu.bind(this)),this.elMenuItems.forEach((function(e){e.classList.contains(\"exportSVG\")?e.addEventListener(\"click\",t.handleDownload.bind(t,\"svg\")):e.classList.contains(\"exportPNG\")?e.addEventListener(\"click\",t.handleDownload.bind(t,\"png\")):e.classList.contains(\"exportCSV\")&&e.addEventListener(\"click\",t.handleDownload.bind(t,\"csv\"))}));for(var e=0;e<this.t.customIcons.length;e++)this.elCustomIcons[e].addEventListener(\"click\",this.t.customIcons[e].click.bind(this,this.ctx,this.ctx.w))}},{key:\"toggleZoomSelection\",value:function(t){this.ctx.getSyncedCharts().forEach((function(e){e.ctx.toolbar.toggleOtherControls();var i=\"selection\"===t?e.ctx.toolbar.elSelection:e.ctx.toolbar.elZoom,a=\"selection\"===t?\"selectionEnabled\":\"zoomEnabled\";e.w.globals[a]=!e.w.globals[a],i.classList.contains(e.ctx.toolbar.selectedClass)?i.classList.remove(e.ctx.toolbar.selectedClass):i.classList.add(e.ctx.toolbar.selectedClass)}))}},{key:\"getToolbarIconsReference\",value:function(){var t=this.w;this.elZoom||(this.elZoom=t.globals.dom.baseEl.querySelector(\".apexcharts-zoom-icon\")),this.elPan||(this.elPan=t.globals.dom.baseEl.querySelector(\".apexcharts-pan-icon\")),this.elSelection||(this.elSelection=t.globals.dom.baseEl.querySelector(\".apexcharts-selection-icon\"))}},{key:\"enableZoomPanFromToolbar\",value:function(t){this.toggleOtherControls(),\"pan\"===t?this.w.globals.panEnabled=!0:this.w.globals.zoomEnabled=!0;var e=\"pan\"===t?this.elPan:this.elZoom,i=\"pan\"===t?this.elZoom:this.elPan;e&&e.classList.add(this.selectedClass),i&&i.classList.remove(this.selectedClass)}},{key:\"togglePanning\",value:function(){this.ctx.getSyncedCharts().forEach((function(t){t.ctx.toolbar.toggleOtherControls(),t.w.globals.panEnabled=!t.w.globals.panEnabled,t.ctx.toolbar.elPan.classList.contains(t.ctx.toolbar.selectedClass)?t.ctx.toolbar.elPan.classList.remove(t.ctx.toolbar.selectedClass):t.ctx.toolbar.elPan.classList.add(t.ctx.toolbar.selectedClass)}))}},{key:\"toggleOtherControls\",value:function(){var t=this,e=this.w;e.globals.panEnabled=!1,e.globals.zoomEnabled=!1,e.globals.selectionEnabled=!1,this.getToolbarIconsReference(),[this.elPan,this.elSelection,this.elZoom].forEach((function(e){e&&e.classList.remove(t.selectedClass)}))}},{key:\"handleZoomIn\",value:function(){var t=this.w;t.globals.isRangeBar&&(this.minX=t.globals.minY,this.maxX=t.globals.maxY);var e=(this.minX+this.maxX)/2,i=(this.minX+e)/2,a=(this.maxX+e)/2,s=this._getNewMinXMaxX(i,a);t.globals.disableZoomIn||this.zoomUpdateOptions(s.minX,s.maxX)}},{key:\"handleZoomOut\",value:function(){var t=this.w;if(t.globals.isRangeBar&&(this.minX=t.globals.minY,this.maxX=t.globals.maxY),!(\"datetime\"===t.config.xaxis.type&&new Date(this.minX).getUTCFullYear()<1e3)){var e=(this.minX+this.maxX)/2,i=this.minX-(e-this.minX),a=this.maxX-(e-this.maxX),s=this._getNewMinXMaxX(i,a);t.globals.disableZoomOut||this.zoomUpdateOptions(s.minX,s.maxX)}}},{key:\"_getNewMinXMaxX\",value:function(t,e){var i=this.w.config.xaxis.convertedCatToNumeric;return{minX:i?Math.floor(t):t,maxX:i?Math.floor(e):e}}},{key:\"zoomUpdateOptions\",value:function(t,e){var i=this.w;if(void 0!==t||void 0!==e){if(!(i.config.xaxis.convertedCatToNumeric&&(t<1&&(t=1,e=i.globals.dataPoints),e-t<2))){var a={min:t,max:e},s=this.getBeforeZoomRange(a);s&&(a=s.xaxis);var r={xaxis:a},o=x.clone(i.globals.initialConfig.yaxis);if(i.config.chart.zoom.autoScaleYaxis)o=new _(this.ctx).autoScaleY(this.ctx,o,{xaxis:a});i.config.chart.group||(r.yaxis=o),this.w.globals.zoomed=!0,this.ctx.updateHelpers._updateOptions(r,!1,this.w.config.chart.animations.dynamicAnimation.enabled),this.zoomCallback(a,o)}}else this.handleZoomReset()}},{key:\"zoomCallback\",value:function(t,e){\"function\"==typeof this.ev.zoomed&&this.ev.zoomed(this.ctx,{xaxis:t,yaxis:e})}},{key:\"getBeforeZoomRange\",value:function(t,e){var i=null;return\"function\"==typeof this.ev.beforeZoom&&(i=this.ev.beforeZoom(this,{xaxis:t,yaxis:e})),i}},{key:\"toggleMenu\",value:function(){var t=this;window.setTimeout((function(){t.elMenu.classList.contains(\"apexcharts-menu-open\")?t.elMenu.classList.remove(\"apexcharts-menu-open\"):t.elMenu.classList.add(\"apexcharts-menu-open\")}),0)}},{key:\"handleDownload\",value:function(t){var e=this.w,i=new G(this.ctx);switch(t){case\"svg\":i.exportToSVG(this.ctx);break;case\"png\":i.exportToPng(this.ctx);break;case\"csv\":i.exportToCSV({series:e.config.series,columnDelimiter:e.config.chart.toolbar.export.csv.columnDelimiter})}}},{key:\"handleZoomReset\",value:function(t){this.ctx.getSyncedCharts().forEach((function(t){var e=t.w;if(e.globals.lastXAxis.min=void 0,e.globals.lastXAxis.max=void 0,t.updateHelpers.revertDefaultAxisMinMax(),\"function\"==typeof e.config.chart.events.beforeResetZoom){var i=e.config.chart.events.beforeResetZoom(t,e);i&&t.updateHelpers.revertDefaultAxisMinMax(i)}\"function\"==typeof e.config.chart.events.zoomed&&t.ctx.toolbar.zoomCallback({min:e.config.xaxis.min,max:e.config.xaxis.max}),e.globals.zoomed=!1;var a=t.ctx.series.emptyCollapsedSeries(x.clone(e.globals.initialSeries));t.updateHelpers._updateSeries(a,e.config.chart.animations.dynamicAnimation.enabled)}))}},{key:\"destroy\",value:function(){this.elZoom=null,this.elZoomIn=null,this.elZoomOut=null,this.elPan=null,this.elSelection=null,this.elZoomReset=null,this.elMenuIcon=null}}]),t}(),ct=function(t){n(i,ht);var e=d(i);function i(t){var s;return a(this,i),(s=e.call(this,t)).ctx=t,s.w=t.w,s.dragged=!1,s.graphics=new m(s.ctx),s.eventList=[\"mousedown\",\"mouseleave\",\"mousemove\",\"touchstart\",\"touchmove\",\"mouseup\",\"touchend\"],s.clientX=0,s.clientY=0,s.startX=0,s.endX=0,s.dragX=0,s.startY=0,s.endY=0,s.dragY=0,s.moveDirection=\"none\",s}return r(i,[{key:\"init\",value:function(t){var e=this,i=t.xyRatios,a=this.w,s=this;this.xyRatios=i,this.zoomRect=this.graphics.drawRect(0,0,0,0),this.selectionRect=this.graphics.drawRect(0,0,0,0),this.gridRect=a.globals.dom.baseEl.querySelector(\".apexcharts-grid\"),this.zoomRect.node.classList.add(\"apexcharts-zoom-rect\"),this.selectionRect.node.classList.add(\"apexcharts-selection-rect\"),a.globals.dom.elGraphical.add(this.zoomRect),a.globals.dom.elGraphical.add(this.selectionRect),\"x\"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,minY:0,maxX:a.globals.gridWidth,maxY:a.globals.gridHeight}).on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")):\"y\"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,maxX:a.globals.gridWidth}).on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")):this.slDraggableRect=this.selectionRect.draggable().on(\"dragmove\",this.selectionDragging.bind(this,\"dragging\")),this.preselectedSelection(),this.hoverArea=a.globals.dom.baseEl.querySelector(\"\".concat(a.globals.chartClass,\" .apexcharts-svg\")),this.hoverArea.classList.add(\"apexcharts-zoomable\"),this.eventList.forEach((function(t){e.hoverArea.addEventListener(t,s.svgMouseEvents.bind(s,i),{capture:!1,passive:!0})}))}},{key:\"destroy\",value:function(){this.slDraggableRect&&(this.slDraggableRect.draggable(!1),this.slDraggableRect.off(),this.selectionRect.off()),this.selectionRect=null,this.zoomRect=null,this.gridRect=null}},{key:\"svgMouseEvents\",value:function(t,e){var i=this.w,a=this,s=this.ctx.toolbar,r=i.globals.zoomEnabled?i.config.chart.zoom.type:i.config.chart.selection.type,o=i.config.chart.toolbar.autoSelected;if(e.shiftKey?(this.shiftWasPressed=!0,s.enableZoomPanFromToolbar(\"pan\"===o?\"zoom\":\"pan\")):this.shiftWasPressed&&(s.enableZoomPanFromToolbar(o),this.shiftWasPressed=!1),e.target){var n,l=e.target.classList;if(e.target.parentNode&&null!==e.target.parentNode&&(n=e.target.parentNode.classList),!(l.contains(\"apexcharts-selection-rect\")||l.contains(\"apexcharts-legend-marker\")||l.contains(\"apexcharts-legend-text\")||n&&n.contains(\"apexcharts-toolbar\"))){if(a.clientX=\"touchmove\"===e.type||\"touchstart\"===e.type?e.touches[0].clientX:\"touchend\"===e.type?e.changedTouches[0].clientX:e.clientX,a.clientY=\"touchmove\"===e.type||\"touchstart\"===e.type?e.touches[0].clientY:\"touchend\"===e.type?e.changedTouches[0].clientY:e.clientY,\"mousedown\"===e.type&&1===e.which){var h=a.gridRect.getBoundingClientRect();a.startX=a.clientX-h.left,a.startY=a.clientY-h.top,a.dragged=!1,a.w.globals.mousedown=!0}if((\"mousemove\"===e.type&&1===e.which||\"touchmove\"===e.type)&&(a.dragged=!0,i.globals.panEnabled?(i.globals.selection=null,a.w.globals.mousedown&&a.panDragging({context:a,zoomtype:r,xyRatios:t})):(a.w.globals.mousedown&&i.globals.zoomEnabled||a.w.globals.mousedown&&i.globals.selectionEnabled)&&(a.selection=a.selectionDrawing({context:a,zoomtype:r}))),\"mouseup\"===e.type||\"touchend\"===e.type||\"mouseleave\"===e.type){var c=a.gridRect.getBoundingClientRect();a.w.globals.mousedown&&(a.endX=a.clientX-c.left,a.endY=a.clientY-c.top,a.dragX=Math.abs(a.endX-a.startX),a.dragY=Math.abs(a.endY-a.startY),(i.globals.zoomEnabled||i.globals.selectionEnabled)&&a.selectionDrawn({context:a,zoomtype:r}),i.globals.panEnabled&&i.config.xaxis.convertedCatToNumeric&&a.delayedPanScrolled()),i.globals.zoomEnabled&&a.hideSelectionRect(this.selectionRect),a.dragged=!1,a.w.globals.mousedown=!1}this.makeSelectionRectDraggable()}}}},{key:\"makeSelectionRectDraggable\",value:function(){var t=this.w;if(this.selectionRect){var e=this.selectionRect.node.getBoundingClientRect();e.width>0&&e.height>0&&this.slDraggableRect.selectize({points:\"l, r\",pointSize:8,pointType:\"rect\"}).resize({constraint:{minX:0,minY:0,maxX:t.globals.gridWidth,maxY:t.globals.gridHeight}}).on(\"resizing\",this.selectionDragging.bind(this,\"resizing\"))}}},{key:\"preselectedSelection\",value:function(){var t=this.w,e=this.xyRatios;if(!t.globals.zoomEnabled)if(void 0!==t.globals.selection&&null!==t.globals.selection)this.drawSelectionRect(t.globals.selection);else if(void 0!==t.config.chart.selection.xaxis.min&&void 0!==t.config.chart.selection.xaxis.max){var i=(t.config.chart.selection.xaxis.min-t.globals.minX)/e.xRatio,a={x:i,y:0,width:t.globals.gridWidth-(t.globals.maxX-t.config.chart.selection.xaxis.max)/e.xRatio-i,height:t.globals.gridHeight,translateX:0,translateY:0,selectionEnabled:!0};this.drawSelectionRect(a),this.makeSelectionRectDraggable(),\"function\"==typeof t.config.chart.events.selection&&t.config.chart.events.selection(this.ctx,{xaxis:{min:t.config.chart.selection.xaxis.min,max:t.config.chart.selection.xaxis.max},yaxis:{}})}}},{key:\"drawSelectionRect\",value:function(t){var e=t.x,i=t.y,a=t.width,s=t.height,r=t.translateX,o=void 0===r?0:r,n=t.translateY,l=void 0===n?0:n,h=this.w,c=this.zoomRect,d=this.selectionRect;if(this.dragged||null!==h.globals.selection){var g={transform:\"translate(\"+o+\", \"+l+\")\"};h.globals.zoomEnabled&&this.dragged&&(a<0&&(a=1),c.attr({x:e,y:i,width:a,height:s,fill:h.config.chart.zoom.zoomedArea.fill.color,\"fill-opacity\":h.config.chart.zoom.zoomedArea.fill.opacity,stroke:h.config.chart.zoom.zoomedArea.stroke.color,\"stroke-width\":h.config.chart.zoom.zoomedArea.stroke.width,\"stroke-opacity\":h.config.chart.zoom.zoomedArea.stroke.opacity}),m.setAttrs(c.node,g)),h.globals.selectionEnabled&&(d.attr({x:e,y:i,width:a>0?a:0,height:s>0?s:0,fill:h.config.chart.selection.fill.color,\"fill-opacity\":h.config.chart.selection.fill.opacity,stroke:h.config.chart.selection.stroke.color,\"stroke-width\":h.config.chart.selection.stroke.width,\"stroke-dasharray\":h.config.chart.selection.stroke.dashArray,\"stroke-opacity\":h.config.chart.selection.stroke.opacity}),m.setAttrs(d.node,g))}}},{key:\"hideSelectionRect\",value:function(t){t&&t.attr({x:0,y:0,width:0,height:0})}},{key:\"selectionDrawing\",value:function(t){var e=t.context,i=t.zoomtype,a=this.w,s=e,r=this.gridRect.getBoundingClientRect(),o=s.startX-1,n=s.startY,l=!1,h=!1,c=s.clientX-r.left-o,d=s.clientY-r.top-n,g={};return Math.abs(c+o)>a.globals.gridWidth?c=a.globals.gridWidth-o:s.clientX-r.left<0&&(c=o),o>s.clientX-r.left&&(l=!0,c=Math.abs(c)),n>s.clientY-r.top&&(h=!0,d=Math.abs(d)),g=\"x\"===i?{x:l?o-c:o,y:0,width:c,height:a.globals.gridHeight}:\"y\"===i?{x:0,y:h?n-d:n,width:a.globals.gridWidth,height:d}:{x:l?o-c:o,y:h?n-d:n,width:c,height:d},s.drawSelectionRect(g),s.selectionDragging(\"resizing\"),g}},{key:\"selectionDragging\",value:function(t,e){var i=this,a=this.w,s=this.xyRatios,r=this.selectionRect,o=0;\"resizing\"===t&&(o=30);var n=function(t){return parseFloat(r.node.getAttribute(t))},l={x:n(\"x\"),y:n(\"y\"),width:n(\"width\"),height:n(\"height\")};a.globals.selection=l,\"function\"==typeof a.config.chart.events.selection&&a.globals.selectionEnabled&&(clearTimeout(this.w.globals.selectionResizeTimer),this.w.globals.selectionResizeTimer=window.setTimeout((function(){var t=i.gridRect.getBoundingClientRect(),e=r.node.getBoundingClientRect(),o={xaxis:{min:a.globals.xAxisScale.niceMin+(e.left-t.left)*s.xRatio,max:a.globals.xAxisScale.niceMin+(e.right-t.left)*s.xRatio},yaxis:{min:a.globals.yAxisScale[0].niceMin+(t.bottom-e.bottom)*s.yRatio[0],max:a.globals.yAxisScale[0].niceMax-(e.top-t.top)*s.yRatio[0]}};a.config.chart.events.selection(i.ctx,o),a.config.chart.brush.enabled&&void 0!==a.config.chart.events.brushScrolled&&a.config.chart.events.brushScrolled(i.ctx,o)}),o))}},{key:\"selectionDrawn\",value:function(t){var e=t.context,i=t.zoomtype,a=this.w,s=e,r=this.xyRatios,o=this.ctx.toolbar;if(s.startX>s.endX){var n=s.startX;s.startX=s.endX,s.endX=n}if(s.startY>s.endY){var l=s.startY;s.startY=s.endY,s.endY=l}var h=void 0,c=void 0;a.globals.isRangeBar?(h=a.globals.yAxisScale[0].niceMin+s.startX*r.invertedYRatio,c=a.globals.yAxisScale[0].niceMin+s.endX*r.invertedYRatio):(h=a.globals.xAxisScale.niceMin+s.startX*r.xRatio,c=a.globals.xAxisScale.niceMin+s.endX*r.xRatio);var d=[],g=[];if(a.config.yaxis.forEach((function(t,e){d.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.startY),g.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.endY)})),s.dragged&&(s.dragX>10||s.dragY>10)&&h!==c)if(a.globals.zoomEnabled){var u=x.clone(a.globals.initialConfig.yaxis),f=x.clone(a.globals.initialConfig.xaxis);if(a.globals.zoomed=!0,a.config.xaxis.convertedCatToNumeric&&(h=Math.floor(h),c=Math.floor(c),h<1&&(h=1,c=a.globals.dataPoints),c-h<2&&(c=h+1)),\"xy\"!==i&&\"x\"!==i||(f={min:h,max:c}),\"xy\"!==i&&\"y\"!==i||u.forEach((function(t,e){u[e].min=g[e],u[e].max=d[e]})),a.config.chart.zoom.autoScaleYaxis){var p=new _(s.ctx);u=p.autoScaleY(s.ctx,u,{xaxis:f})}if(o){var b=o.getBeforeZoomRange(f,u);b&&(f=b.xaxis?b.xaxis:f,u=b.yaxis?b.yaxis:u)}var v={xaxis:f};a.config.chart.group||(v.yaxis=u),s.ctx.updateHelpers._updateOptions(v,!1,s.w.config.chart.animations.dynamicAnimation.enabled),\"function\"==typeof a.config.chart.events.zoomed&&o.zoomCallback(f,u)}else if(a.globals.selectionEnabled){var m,y=null;m={min:h,max:c},\"xy\"!==i&&\"y\"!==i||(y=x.clone(a.config.yaxis)).forEach((function(t,e){y[e].min=g[e],y[e].max=d[e]})),a.globals.selection=s.selection,\"function\"==typeof a.config.chart.events.selection&&a.config.chart.events.selection(s.ctx,{xaxis:m,yaxis:y})}}},{key:\"panDragging\",value:function(t){var e=t.context,i=this.w,a=e;if(void 0!==i.globals.lastClientPosition.x){var s=i.globals.lastClientPosition.x-a.clientX,r=i.globals.lastClientPosition.y-a.clientY;Math.abs(s)>Math.abs(r)&&s>0?this.moveDirection=\"left\":Math.abs(s)>Math.abs(r)&&s<0?this.moveDirection=\"right\":Math.abs(r)>Math.abs(s)&&r>0?this.moveDirection=\"up\":Math.abs(r)>Math.abs(s)&&r<0&&(this.moveDirection=\"down\")}i.globals.lastClientPosition={x:a.clientX,y:a.clientY};var o=i.globals.isRangeBar?i.globals.minY:i.globals.minX,n=i.globals.isRangeBar?i.globals.maxY:i.globals.maxX;i.config.xaxis.convertedCatToNumeric||a.panScrolled(o,n)}},{key:\"delayedPanScrolled\",value:function(){var t=this.w,e=t.globals.minX,i=t.globals.maxX,a=(t.globals.maxX-t.globals.minX)/2;\"left\"===this.moveDirection?(e=t.globals.minX+a,i=t.globals.maxX+a):\"right\"===this.moveDirection&&(e=t.globals.minX-a,i=t.globals.maxX-a),e=Math.floor(e),i=Math.floor(i),this.updateScrolledChart({xaxis:{min:e,max:i}},e,i)}},{key:\"panScrolled\",value:function(t,e){var i=this.w,a=this.xyRatios,s=x.clone(i.globals.initialConfig.yaxis),r=a.xRatio,o=i.globals.minX,n=i.globals.maxX;i.globals.isRangeBar&&(r=a.invertedYRatio,o=i.globals.minY,n=i.globals.maxY),\"left\"===this.moveDirection?(t=o+i.globals.gridWidth/15*r,e=n+i.globals.gridWidth/15*r):\"right\"===this.moveDirection&&(t=o-i.globals.gridWidth/15*r,e=n-i.globals.gridWidth/15*r),i.globals.isRangeBar||(t<i.globals.initialMinX||e>i.globals.initialMaxX)&&(t=o,e=n);var l={min:t,max:e};i.config.chart.zoom.autoScaleYaxis&&(s=new _(this.ctx).autoScaleY(this.ctx,s,{xaxis:l}));var h={xaxis:{min:t,max:e}};i.config.chart.group||(h.yaxis=s),this.updateScrolledChart(h,t,e)}},{key:\"updateScrolledChart\",value:function(t,e,i){var a=this.w;this.ctx.updateHelpers._updateOptions(t,!1,!1),\"function\"==typeof a.config.chart.events.scrolled&&a.config.chart.events.scrolled(this.ctx,{xaxis:{min:e,max:i}})}}]),i}(),dt=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e,this.ctx=e.ctx}return r(t,[{key:\"getNearestValues\",value:function(t){var e=t.hoverArea,i=t.elGrid,a=t.clientX,s=t.clientY,r=this.w,o=i.getBoundingClientRect(),n=o.width,l=o.height,h=n/(r.globals.dataPoints-1),c=l/r.globals.dataPoints,d=this.hasBars();!r.globals.comboCharts&&!d||r.config.xaxis.convertedCatToNumeric||(h=n/r.globals.dataPoints);var g=a-o.left-r.globals.barPadForNumericAxis,u=s-o.top;g<0||u<0||g>n||u>l?(e.classList.remove(\"hovering-zoom\"),e.classList.remove(\"hovering-pan\")):r.globals.zoomEnabled?(e.classList.remove(\"hovering-pan\"),e.classList.add(\"hovering-zoom\")):r.globals.panEnabled&&(e.classList.remove(\"hovering-zoom\"),e.classList.add(\"hovering-pan\"));var f=Math.round(g/h),p=Math.floor(u/c);d&&!r.config.xaxis.convertedCatToNumeric&&(f=Math.ceil(g/h),f-=1);var b=null,v=null,m=[],y=[];if(r.globals.seriesXvalues.forEach((function(t){m.push([t[0]+1e-6].concat(t))})),r.globals.seriesYvalues.forEach((function(t){y.push([t[0]+1e-6].concat(t))})),m=m.map((function(t){return t.filter((function(t){return x.isNumber(t)}))})),y=y.map((function(t){return t.filter((function(t){return x.isNumber(t)}))})),r.globals.isXNumeric){var w=this.ttCtx.getElGrid().getBoundingClientRect(),k=g*(w.width/n),A=u*(w.height/l);b=(v=this.closestInMultiArray(k,A,m,y)).index,f=v.j,null!==b&&(m=r.globals.seriesXvalues[b],f=(v=this.closestInArray(k,m)).index)}return r.globals.capturedSeriesIndex=null===b?-1:b,(!f||f<1)&&(f=0),r.globals.isBarHorizontal?r.globals.capturedDataPointIndex=p:r.globals.capturedDataPointIndex=f,{capturedSeries:b,j:r.globals.isBarHorizontal?p:f,hoverX:g,hoverY:u}}},{key:\"closestInMultiArray\",value:function(t,e,i,a){var s=this.w,r=0,o=null,n=-1;s.globals.series.length>1?r=this.getFirstActiveXArray(i):o=0;var l=i[r][0],h=Math.abs(t-l);if(i.forEach((function(e){e.forEach((function(e,i){var a=Math.abs(t-e);a<h&&(h=a,n=i)}))})),-1!==n){var c=a[r][n],d=Math.abs(e-c);o=r,a.forEach((function(t,i){var a=Math.abs(e-t[n]);a<d&&(d=a,o=i)}))}return{index:o,j:n}}},{key:\"getFirstActiveXArray\",value:function(t){for(var e=this.w,i=0,a=t.map((function(t,e){return t.length>0?e:-1})),s=0;s<a.length;s++)if(-1!==a[s]&&-1===e.globals.collapsedSeriesIndices.indexOf(s)&&-1===e.globals.ancillaryCollapsedSeriesIndices.indexOf(s)){i=a[s];break}return i}},{key:\"closestInArray\",value:function(t,e){for(var i=e[0],a=null,s=Math.abs(t-i),r=0;r<e.length;r++){var o=Math.abs(t-e[r]);o<s&&(s=o,a=r)}return{index:a}}},{key:\"isXoverlap\",value:function(t){var e=[],i=this.w.globals.seriesX.filter((function(t){return void 0!==t[0]}));if(i.length>0)for(var a=0;a<i.length-1;a++)void 0!==i[a][t]&&void 0!==i[a+1][t]&&i[a][t]!==i[a+1][t]&&e.push(\"unEqual\");return 0===e.length}},{key:\"isInitialSeriesSameLen\",value:function(){for(var t=!0,e=this.w.globals.initialSeries,i=0;i<e.length-1;i++)if(e[i].data.length!==e[i+1].data.length){t=!1;break}return t}},{key:\"getBarsHeight\",value:function(t){return u(t).reduce((function(t,e){return t+e.getBBox().height}),0)}},{key:\"getElMarkers\",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(\" .apexcharts-series-markers\")}},{key:\"getAllMarkers\",value:function(){var t=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series-markers-wrap\");(t=u(t)).sort((function(t,e){var i=Number(t.getAttribute(\"data:realIndex\")),a=Number(e.getAttribute(\"data:realIndex\"));return a<i?1:a>i?-1:0}));var e=[];return t.forEach((function(t){e.push(t.querySelector(\".apexcharts-marker\"))})),e}},{key:\"hasMarkers\",value:function(){return this.getElMarkers().length>0}},{key:\"getElBars\",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-bar-series,  .apexcharts-candlestick-series, .apexcharts-boxPlot-series, .apexcharts-rangebar-series\")}},{key:\"hasBars\",value:function(){return this.getElBars().length>0}},{key:\"getHoverMarkerSize\",value:function(t){var e=this.w,i=e.config.markers.hover.size;return void 0===i&&(i=e.globals.markers.size[t]+e.config.markers.hover.sizeOffset),i}},{key:\"toggleAllTooltipSeriesGroups\",value:function(t){var e=this.w,i=this.ttCtx;0===i.allTooltipSeriesGroups.length&&(i.allTooltipSeriesGroups=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-tooltip-series-group\"));for(var a=i.allTooltipSeriesGroups,s=0;s<a.length;s++)\"enable\"===t?(a[s].classList.add(\"apexcharts-active\"),a[s].style.display=e.config.tooltip.items.display):(a[s].classList.remove(\"apexcharts-active\"),a[s].style.display=\"none\")}}]),t}(),gt=function(){function t(e){a(this,t),this.w=e.w,this.ctx=e.ctx,this.ttCtx=e,this.tooltipUtil=new dt(e)}return r(t,[{key:\"drawSeriesTexts\",value:function(t){var e=t.shared,i=void 0===e||e,a=t.ttItems,s=t.i,r=void 0===s?0:s,o=t.j,n=void 0===o?null:o,l=t.y1,h=t.y2,c=t.e,d=this.w;void 0!==d.config.tooltip.custom?this.handleCustomTooltip({i:r,j:n,y1:l,y2:h,w:d}):this.toggleActiveInactiveSeries(i);var g=this.getValuesToPrint({i:r,j:n});this.printLabels({i:r,j:n,values:g,ttItems:a,shared:i,e:c});var u=this.ttCtx.getElTooltip();this.ttCtx.tooltipRect.ttWidth=u.getBoundingClientRect().width,this.ttCtx.tooltipRect.ttHeight=u.getBoundingClientRect().height}},{key:\"printLabels\",value:function(t){var i,a=this,s=t.i,r=t.j,o=t.values,n=t.ttItems,l=t.shared,h=t.e,c=this.w,d=[],g=function(t){return c.globals.seriesGoals[t]&&c.globals.seriesGoals[t][r]&&Array.isArray(c.globals.seriesGoals[t][r])},u=o.xVal,f=o.zVal,p=o.xAxisTTVal,x=\"\",b=c.globals.colors[s];null!==r&&c.config.plotOptions.bar.distributed&&(b=c.globals.colors[r]);for(var v=function(t,o){var v=a.getFormatters(s);x=a.getSeriesName({fn:v.yLbTitleFormatter,index:s,seriesIndex:s,j:r}),\"treemap\"===c.config.chart.type&&(x=v.yLbTitleFormatter(String(c.config.series[s].data[r].x),{series:c.globals.series,seriesIndex:s,dataPointIndex:r,w:c}));var m=c.config.tooltip.inverseOrder?o:t;if(c.globals.axisCharts){var y=function(t){var e,i,a=\"\";c.globals.isRangeData&&(a+=v.yLbFormatter(null===(e=c.globals.seriesRangeStart)||void 0===e||null===(i=e[t])||void 0===i?void 0:i[r],{series:c.globals.seriesRangeStart,seriesIndex:t,dataPointIndex:r,w:c})+\" - \");return a+=v.yLbFormatter(c.globals.series[t][r],{series:c.globals.series,seriesIndex:t,dataPointIndex:r,w:c})};if(l)v=a.getFormatters(m),x=a.getSeriesName({fn:v.yLbTitleFormatter,index:m,seriesIndex:s,j:r}),b=c.globals.colors[m],i=y(m),g(m)&&(d=c.globals.seriesGoals[m][r].map((function(t){return{attrs:t,val:v.yLbFormatter(t.value,{seriesIndex:m,dataPointIndex:r,w:c})}})));else{var w,k=null==h||null===(w=h.target)||void 0===w?void 0:w.getAttribute(\"fill\");k&&(b=-1!==k.indexOf(\"url\")?document.querySelector(k.substr(4).slice(0,-1)).childNodes[0].getAttribute(\"stroke\"):k),i=y(s),g(s)&&Array.isArray(c.globals.seriesGoals[s][r])&&(d=c.globals.seriesGoals[s][r].map((function(t){return{attrs:t,val:v.yLbFormatter(t.value,{seriesIndex:s,dataPointIndex:r,w:c})}})))}}null===r&&(i=v.yLbFormatter(c.globals.series[s],e(e({},c),{},{seriesIndex:s,dataPointIndex:s}))),a.DOMHandling({i:s,t:m,j:r,ttItems:n,values:{val:i,goalVals:d,xVal:u,xAxisTTVal:p,zVal:f},seriesName:x,shared:l,pColor:b})},m=0,y=c.globals.series.length-1;m<c.globals.series.length;m++,y--)v(m,y)}},{key:\"getFormatters\",value:function(t){var e,i=this.w,a=i.globals.yLabelFormatters[t];return void 0!==i.globals.ttVal?Array.isArray(i.globals.ttVal)?(a=i.globals.ttVal[t]&&i.globals.ttVal[t].formatter,e=i.globals.ttVal[t]&&i.globals.ttVal[t].title&&i.globals.ttVal[t].title.formatter):(a=i.globals.ttVal.formatter,\"function\"==typeof i.globals.ttVal.title.formatter&&(e=i.globals.ttVal.title.formatter)):e=i.config.tooltip.y.title.formatter,\"function\"!=typeof a&&(a=i.globals.yLabelFormatters[0]?i.globals.yLabelFormatters[0]:function(t){return t}),\"function\"!=typeof e&&(e=function(t){return t}),{yLbFormatter:a,yLbTitleFormatter:e}}},{key:\"getSeriesName\",value:function(t){var e=t.fn,i=t.index,a=t.seriesIndex,s=t.j,r=this.w;return e(String(r.globals.seriesNames[i]),{series:r.globals.series,seriesIndex:a,dataPointIndex:s,w:r})}},{key:\"DOMHandling\",value:function(t){t.i;var e=t.t,i=t.j,a=t.ttItems,s=t.values,r=t.seriesName,o=t.shared,n=t.pColor,l=this.w,h=this.ttCtx,c=s.val,d=s.goalVals,g=s.xVal,u=s.xAxisTTVal,f=s.zVal,p=null;p=a[e].children,l.config.tooltip.fillSeriesColor&&(a[e].style.backgroundColor=n,p[0].style.display=\"none\"),h.showTooltipTitle&&(null===h.tooltipTitle&&(h.tooltipTitle=l.globals.dom.baseEl.querySelector(\".apexcharts-tooltip-title\")),h.tooltipTitle.innerHTML=g),h.isXAxisTooltipEnabled&&(h.xaxisTooltipText.innerHTML=\"\"!==u?u:g);var x=a[e].querySelector(\".apexcharts-tooltip-text-y-label\");x&&(x.innerHTML=r||\"\");var b=a[e].querySelector(\".apexcharts-tooltip-text-y-value\");b&&(b.innerHTML=void 0!==c?c:\"\"),p[0]&&p[0].classList.contains(\"apexcharts-tooltip-marker\")&&(l.config.tooltip.marker.fillColors&&Array.isArray(l.config.tooltip.marker.fillColors)&&(n=l.config.tooltip.marker.fillColors[e]),p[0].style.backgroundColor=n),l.config.tooltip.marker.show||(p[0].style.display=\"none\");var v=a[e].querySelector(\".apexcharts-tooltip-text-goals-label\"),m=a[e].querySelector(\".apexcharts-tooltip-text-goals-value\");if(d.length&&l.globals.seriesGoals[e]){var y=function(){var t=\"<div >\",e=\"<div>\";d.forEach((function(i,a){t+=' <div style=\"display: flex\"><span class=\"apexcharts-tooltip-marker\" style=\"background-color: '.concat(i.attrs.strokeColor,'; height: 3px; border-radius: 0; top: 5px;\"></span> ').concat(i.attrs.name,\"</div>\"),e+=\"<div>\".concat(i.val,\"</div>\")})),v.innerHTML=t+\"</div>\",m.innerHTML=e+\"</div>\"};o?l.globals.seriesGoals[e][i]&&Array.isArray(l.globals.seriesGoals[e][i])?y():(v.innerHTML=\"\",m.innerHTML=\"\"):y()}else v.innerHTML=\"\",m.innerHTML=\"\";null!==f&&(a[e].querySelector(\".apexcharts-tooltip-text-z-label\").innerHTML=l.config.tooltip.z.title,a[e].querySelector(\".apexcharts-tooltip-text-z-value\").innerHTML=void 0!==f?f:\"\");o&&p[0]&&(null==c||l.globals.ancillaryCollapsedSeriesIndices.indexOf(e)>-1||l.globals.collapsedSeriesIndices.indexOf(e)>-1?p[0].parentNode.style.display=\"none\":p[0].parentNode.style.display=l.config.tooltip.items.display)}},{key:\"toggleActiveInactiveSeries\",value:function(t){var e=this.w;if(t)this.tooltipUtil.toggleAllTooltipSeriesGroups(\"enable\");else{this.tooltipUtil.toggleAllTooltipSeriesGroups(\"disable\");var i=e.globals.dom.baseEl.querySelector(\".apexcharts-tooltip-series-group\");i&&(i.classList.add(\"apexcharts-active\"),i.style.display=e.config.tooltip.items.display)}}},{key:\"getValuesToPrint\",value:function(t){var e=t.i,i=t.j,a=this.w,s=this.ctx.series.filteredSeriesX(),r=\"\",o=\"\",n=null,l=null,h={series:a.globals.series,seriesIndex:e,dataPointIndex:i,w:a},c=a.globals.ttZFormatter;null===i?l=a.globals.series[e]:a.globals.isXNumeric&&\"treemap\"!==a.config.chart.type?(r=s[e][i],0===s[e].length&&(r=s[this.tooltipUtil.getFirstActiveXArray(s)][i])):r=void 0!==a.globals.labels[i]?a.globals.labels[i]:\"\";var d=r;a.globals.isXNumeric&&\"datetime\"===a.config.xaxis.type?r=new M(this.ctx).xLabelFormat(a.globals.ttKeyFormatter,d,d,{i:void 0,dateFormatter:new T(this.ctx).formatDate,w:this.w}):r=a.globals.isBarHorizontal?a.globals.yLabelFormatters[0](d,h):a.globals.xLabelFormatter(d,h);return void 0!==a.config.tooltip.x.formatter&&(r=a.globals.ttKeyFormatter(d,h)),a.globals.seriesZ.length>0&&a.globals.seriesZ[e].length>0&&(n=c(a.globals.seriesZ[e][i],a)),o=\"function\"==typeof a.config.xaxis.tooltip.formatter?a.globals.xaxisTooltipFormatter(d,h):r,{val:Array.isArray(l)?l.join(\" \"):l,xVal:Array.isArray(r)?r.join(\" \"):r,xAxisTTVal:Array.isArray(o)?o.join(\" \"):o,zVal:n}}},{key:\"handleCustomTooltip\",value:function(t){var e=t.i,i=t.j,a=t.y1,s=t.y2,r=t.w,o=this.ttCtx.getElTooltip(),n=r.config.tooltip.custom;Array.isArray(n)&&n[e]&&(n=n[e]),o.innerHTML=n({ctx:this.ctx,series:r.globals.series,seriesIndex:e,dataPointIndex:i,y1:a,y2:s,w:r})}}]),t}(),ut=function(){function t(e){a(this,t),this.ttCtx=e,this.ctx=e.ctx,this.w=e.w}return r(t,[{key:\"moveXCrosshairs\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.ttCtx,a=this.w,s=i.getElXCrosshairs(),r=t-i.xcrosshairsWidth/2,o=a.globals.labels.slice().length;if(null!==e&&(r=a.globals.gridWidth/o*e),null===s||a.globals.isBarHorizontal||(s.setAttribute(\"x\",r),s.setAttribute(\"x1\",r),s.setAttribute(\"x2\",r),s.setAttribute(\"y2\",a.globals.gridHeight),s.classList.add(\"apexcharts-active\")),r<0&&(r=0),r>a.globals.gridWidth&&(r=a.globals.gridWidth),i.isXAxisTooltipEnabled){var n=r;\"tickWidth\"!==a.config.xaxis.crosshairs.width&&\"barWidth\"!==a.config.xaxis.crosshairs.width||(n=r+i.xcrosshairsWidth/2),this.moveXAxisTooltip(n)}}},{key:\"moveYCrosshairs\",value:function(t){var e=this.ttCtx;null!==e.ycrosshairs&&m.setAttrs(e.ycrosshairs,{y1:t,y2:t}),null!==e.ycrosshairsHidden&&m.setAttrs(e.ycrosshairsHidden,{y1:t,y2:t})}},{key:\"moveXAxisTooltip\",value:function(t){var e=this.w,i=this.ttCtx;if(null!==i.xaxisTooltip&&0!==i.xcrosshairsWidth){i.xaxisTooltip.classList.add(\"apexcharts-active\");var a=i.xaxisOffY+e.config.xaxis.tooltip.offsetY+e.globals.translateY+1+e.config.xaxis.offsetY;if(t-=i.xaxisTooltip.getBoundingClientRect().width/2,!isNaN(t)){t+=e.globals.translateX;var s;s=new m(this.ctx).getTextRects(i.xaxisTooltipText.innerHTML),i.xaxisTooltipText.style.minWidth=s.width+\"px\",i.xaxisTooltip.style.left=t+\"px\",i.xaxisTooltip.style.top=a+\"px\"}}}},{key:\"moveYAxisTooltip\",value:function(t){var e=this.w,i=this.ttCtx;null===i.yaxisTTEls&&(i.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxistooltip\"));var a=parseInt(i.ycrosshairsHidden.getAttribute(\"y1\"),10),s=e.globals.translateY+a,r=i.yaxisTTEls[t].getBoundingClientRect().height,o=e.globals.translateYAxisX[t]-2;e.config.yaxis[t].opposite&&(o-=26),s-=r/2,-1===e.globals.ignoreYAxisIndexes.indexOf(t)?(i.yaxisTTEls[t].classList.add(\"apexcharts-active\"),i.yaxisTTEls[t].style.top=s+\"px\",i.yaxisTTEls[t].style.left=o+e.config.yaxis[t].tooltip.offsetX+\"px\"):i.yaxisTTEls[t].classList.remove(\"apexcharts-active\")}},{key:\"moveTooltip\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=this.ttCtx,r=s.getElTooltip(),o=s.tooltipRect,n=null!==i?parseFloat(i):1,l=parseFloat(t)+n+5,h=parseFloat(e)+n/2;if(l>a.globals.gridWidth/2&&(l=l-o.ttWidth-n-10),l>a.globals.gridWidth-o.ttWidth-10&&(l=a.globals.gridWidth-o.ttWidth),l<-20&&(l=-20),a.config.tooltip.followCursor){var c=s.getElGrid(),d=c.getBoundingClientRect();h=s.e.clientY+a.globals.translateY-d.top-o.ttHeight/2}else a.globals.isBarHorizontal||o.ttHeight/2+h>a.globals.gridHeight&&(h=a.globals.gridHeight-o.ttHeight+a.globals.translateY);isNaN(l)||(l+=a.globals.translateX,r.style.left=l+\"px\",r.style.top=h+\"px\")}},{key:\"moveMarkers\",value:function(t,e){var i=this.w,a=this.ttCtx;if(i.globals.markers.size[t]>0)for(var s=i.globals.dom.baseEl.querySelectorAll(\" .apexcharts-series[data\\\\:realIndex='\".concat(t,\"'] .apexcharts-marker\")),r=0;r<s.length;r++)parseInt(s[r].getAttribute(\"rel\"),10)===e&&(a.marker.resetPointsSize(),a.marker.enlargeCurrentPoint(e,s[r]));else a.marker.resetPointsSize(),this.moveDynamicPointOnHover(e,t)}},{key:\"moveDynamicPointOnHover\",value:function(t,e){var i,a,s=this.w,r=this.ttCtx,o=s.globals.pointsArray,n=r.tooltipUtil.getHoverMarkerSize(e),l=s.config.series[e].type;if(!l||\"column\"!==l&&\"candlestick\"!==l&&\"boxPlot\"!==l){i=o[e][t][0],a=o[e][t][1]?o[e][t][1]:0;var h=s.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(e,\"'] .apexcharts-series-markers circle\"));h&&a<s.globals.gridHeight&&a>0&&(h.setAttribute(\"r\",n),h.setAttribute(\"cx\",i),h.setAttribute(\"cy\",a)),this.moveXCrosshairs(i),r.fixedTooltip||this.moveTooltip(i,a,n)}}},{key:\"moveDynamicPointsOnHover\",value:function(t){var e,i=this.ttCtx,a=i.w,s=0,r=0,o=a.globals.pointsArray;e=new N(this.ctx).getActiveConfigSeriesIndex(\"asc\",[\"line\",\"area\",\"scatter\",\"bubble\"]);var n=i.tooltipUtil.getHoverMarkerSize(e);o[e]&&(s=o[e][t][0],r=o[e][t][1]);var l=i.tooltipUtil.getAllMarkers();if(null!==l)for(var h=0;h<a.globals.series.length;h++){var c=o[h];if(a.globals.comboCharts&&void 0===c&&l.splice(h,0,null),c&&c.length){var d=o[h][t][1],g=void 0;if(l[h].setAttribute(\"cx\",s),\"rangeArea\"===a.config.chart.type&&!a.globals.comboCharts){var u=t+a.globals.series[h].length;g=o[h][u][1],d-=Math.abs(d-g)/2}null!==d&&!isNaN(d)&&d<a.globals.gridHeight+n&&d+n>0?(l[h]&&l[h].setAttribute(\"r\",n),l[h]&&l[h].setAttribute(\"cy\",d)):l[h]&&l[h].setAttribute(\"r\",0)}}if(this.moveXCrosshairs(s),!i.fixedTooltip){var f=r||a.globals.gridHeight;this.moveTooltip(s,f,n)}}},{key:\"moveStickyTooltipOverBars\",value:function(t){var e=this.w,i=this.ttCtx,a=e.globals.columnSeries?e.globals.columnSeries.length:e.globals.series.length,s=a>=2&&a%2==0?Math.floor(a/2):Math.floor(a/2)+1;e.globals.isBarHorizontal&&(s=new N(this.ctx).getActiveConfigSeriesIndex(\"desc\")+1);var r=e.globals.dom.baseEl.querySelector(\".apexcharts-bar-series .apexcharts-series[rel='\".concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-candlestick-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-boxPlot-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"'], .apexcharts-rangebar-series .apexcharts-series[rel='\").concat(s,\"'] path[j='\").concat(t,\"']\")),o=r?parseFloat(r.getAttribute(\"cx\")):0,n=r?parseFloat(r.getAttribute(\"cy\")):0,l=r?parseFloat(r.getAttribute(\"barWidth\")):0,h=i.getElGrid().getBoundingClientRect(),c=r.classList.contains(\"apexcharts-candlestick-area\")||r.classList.contains(\"apexcharts-boxPlot-area\");if(e.globals.isXNumeric?(r&&!c&&(o-=a%2!=0?l/2:0),r&&c&&e.globals.comboCharts&&(o-=l/2)):e.globals.isBarHorizontal||(o=i.xAxisTicksPositions[t-1]+i.dataPointsDividedWidth/2,isNaN(o)&&(o=i.xAxisTicksPositions[t]-i.dataPointsDividedWidth/2)),e.globals.isBarHorizontal?n-=i.tooltipRect.ttHeight:e.config.tooltip.followCursor?n=i.e.clientY-h.top-i.tooltipRect.ttHeight/2:n+i.tooltipRect.ttHeight+15>e.globals.gridHeight&&(n=e.globals.gridHeight),e.globals.isBarHorizontal||this.moveXCrosshairs(o),!i.fixedTooltip){var d=n||e.globals.gridHeight;this.moveTooltip(o,d)}}}]),t}(),ft=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e,this.ctx=e.ctx,this.tooltipPosition=new ut(e)}return r(t,[{key:\"drawDynamicPoints\",value:function(){var t=this.w,e=new m(this.ctx),i=new D(this.ctx),a=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series\");a=u(a),t.config.chart.stacked&&a.sort((function(t,e){return parseFloat(t.getAttribute(\"data:realIndex\"))-parseFloat(e.getAttribute(\"data:realIndex\"))}));for(var s=0;s<a.length;s++){var r=a[s].querySelector(\".apexcharts-series-markers-wrap\");if(null!==r){var o=void 0,n=\"apexcharts-marker w\".concat((Math.random()+1).toString(36).substring(4));\"line\"!==t.config.chart.type&&\"area\"!==t.config.chart.type||t.globals.comboCharts||t.config.tooltip.intersect||(n+=\" no-pointer-events\");var l=i.getMarkerConfig({cssClass:n,seriesIndex:Number(r.getAttribute(\"data:realIndex\"))});(o=e.drawMarker(0,0,l)).node.setAttribute(\"default-marker-size\",0);var h=document.createElementNS(t.globals.SVGNS,\"g\");h.classList.add(\"apexcharts-series-markers\"),h.appendChild(o.node),r.appendChild(h)}}}},{key:\"enlargeCurrentPoint\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w;\"bubble\"!==s.config.chart.type&&this.newPointSize(t,e);var r=e.getAttribute(\"cx\"),o=e.getAttribute(\"cy\");if(null!==i&&null!==a&&(r=i,o=a),this.tooltipPosition.moveXCrosshairs(r),!this.fixedTooltip){if(\"radar\"===s.config.chart.type){var n=this.ttCtx.getElGrid(),l=n.getBoundingClientRect();r=this.ttCtx.e.clientX-l.left}this.tooltipPosition.moveTooltip(r,o,s.config.markers.hover.size)}}},{key:\"enlargePoints\",value:function(t){for(var e=this.w,i=this,a=this.ttCtx,s=t,r=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker\"),o=e.config.markers.hover.size,n=0;n<r.length;n++){var l=r[n].getAttribute(\"rel\"),h=r[n].getAttribute(\"index\");if(void 0===o&&(o=e.globals.markers.size[h]+e.config.markers.hover.sizeOffset),s===parseInt(l,10)){i.newPointSize(s,r[n]);var c=r[n].getAttribute(\"cx\"),d=r[n].getAttribute(\"cy\");i.tooltipPosition.moveXCrosshairs(c),a.fixedTooltip||i.tooltipPosition.moveTooltip(c,d,o)}else i.oldPointSize(r[n])}}},{key:\"newPointSize\",value:function(t,e){var i=this.w,a=i.config.markers.hover.size,s=0===t?e.parentNode.firstChild:e.parentNode.lastChild;if(\"0\"!==s.getAttribute(\"default-marker-size\")){var r=parseInt(s.getAttribute(\"index\"),10);void 0===a&&(a=i.globals.markers.size[r]+i.config.markers.hover.sizeOffset),a<0&&(a=0),s.setAttribute(\"r\",a)}}},{key:\"oldPointSize\",value:function(t){var e=parseFloat(t.getAttribute(\"default-marker-size\"));t.setAttribute(\"r\",e)}},{key:\"resetPointsSize\",value:function(){for(var t=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker\"),e=0;e<t.length;e++){var i=parseFloat(t[e].getAttribute(\"default-marker-size\"));x.isNumber(i)&&i>=0?t[e].setAttribute(\"r\",i):t[e].setAttribute(\"r\",0)}}}]),t}(),pt=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e}return r(t,[{key:\"getAttr\",value:function(t,e){return parseFloat(t.target.getAttribute(e))}},{key:\"handleHeatTreeTooltip\",value:function(t){var e=t.e,i=t.opt,a=t.x,s=t.y,r=t.type,o=this.ttCtx,n=this.w;if(e.target.classList.contains(\"apexcharts-\".concat(r,\"-rect\"))){var l=this.getAttr(e,\"i\"),h=this.getAttr(e,\"j\"),c=this.getAttr(e,\"cx\"),d=this.getAttr(e,\"cy\"),g=this.getAttr(e,\"width\"),u=this.getAttr(e,\"height\");if(o.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:l,j:h,shared:!1,e:e}),n.globals.capturedSeriesIndex=l,n.globals.capturedDataPointIndex=h,a=c+o.tooltipRect.ttWidth/2+g,s=d+o.tooltipRect.ttHeight/2-u/2,o.tooltipPosition.moveXCrosshairs(c+g/2),a>n.globals.gridWidth/2&&(a=c-o.tooltipRect.ttWidth/2+g),o.w.config.tooltip.followCursor){var f=n.globals.dom.elWrap.getBoundingClientRect();a=n.globals.clientX-f.left-(a>n.globals.gridWidth/2?o.tooltipRect.ttWidth:0),s=n.globals.clientY-f.top-(s>n.globals.gridHeight/2?o.tooltipRect.ttHeight:0)}}return{x:a,y:s}}},{key:\"handleMarkerTooltip\",value:function(t){var e,i,a=t.e,s=t.opt,r=t.x,o=t.y,n=this.w,l=this.ttCtx;if(a.target.classList.contains(\"apexcharts-marker\")){var h=parseInt(s.paths.getAttribute(\"cx\"),10),c=parseInt(s.paths.getAttribute(\"cy\"),10),d=parseFloat(s.paths.getAttribute(\"val\"));if(i=parseInt(s.paths.getAttribute(\"rel\"),10),e=parseInt(s.paths.parentNode.parentNode.parentNode.getAttribute(\"rel\"),10)-1,l.intersect){var g=x.findAncestor(s.paths,\"apexcharts-series\");g&&(e=parseInt(g.getAttribute(\"data:realIndex\"),10))}if(l.tooltipLabels.drawSeriesTexts({ttItems:s.ttItems,i:e,j:i,shared:!l.showOnIntersect&&n.config.tooltip.shared,e:a}),\"mouseup\"===a.type&&l.markerClick(a,e,i),n.globals.capturedSeriesIndex=e,n.globals.capturedDataPointIndex=i,r=h,o=c+n.globals.translateY-1.4*l.tooltipRect.ttHeight,l.w.config.tooltip.followCursor){var u=l.getElGrid().getBoundingClientRect();o=l.e.clientY+n.globals.translateY-u.top}d<0&&(o=c),l.marker.enlargeCurrentPoint(i,s.paths,r,o)}return{x:r,y:o}}},{key:\"handleBarTooltip\",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,o=this.ttCtx,n=o.getElTooltip(),l=0,h=0,c=0,d=this.getBarTooltipXY({e:a,opt:s});e=d.i;var g=d.barHeight,u=d.j;r.globals.capturedSeriesIndex=e,r.globals.capturedDataPointIndex=u,r.globals.isBarHorizontal&&o.tooltipUtil.hasBars()||!r.config.tooltip.shared?(h=d.x,c=d.y,i=Array.isArray(r.config.stroke.width)?r.config.stroke.width[e]:r.config.stroke.width,l=h):r.globals.comboCharts||r.config.tooltip.shared||(l/=2),isNaN(c)&&(c=r.globals.svgHeight-o.tooltipRect.ttHeight);var f=parseInt(s.paths.parentNode.getAttribute(\"data:realIndex\"),10),p=r.globals.isMultipleYAxis?r.config.yaxis[f]&&r.config.yaxis[f].reversed:r.config.yaxis[0].reversed;if(h+o.tooltipRect.ttWidth>r.globals.gridWidth&&!p?h-=o.tooltipRect.ttWidth:h<0&&(h=0),o.w.config.tooltip.followCursor){var x=o.getElGrid().getBoundingClientRect();c=o.e.clientY-x.top}null===o.tooltip&&(o.tooltip=r.globals.dom.baseEl.querySelector(\".apexcharts-tooltip\")),r.config.tooltip.shared||(r.globals.comboBarCount>0?o.tooltipPosition.moveXCrosshairs(l+i/2):o.tooltipPosition.moveXCrosshairs(l)),!o.fixedTooltip&&(!r.config.tooltip.shared||r.globals.isBarHorizontal&&o.tooltipUtil.hasBars())&&(p&&(h-=o.tooltipRect.ttWidth)<0&&(h=0),!p||r.globals.isBarHorizontal&&o.tooltipUtil.hasBars()||(c=c+g-2*(r.globals.series[e][u]<0?g:0)),c=c+r.globals.translateY-o.tooltipRect.ttHeight/2,n.style.left=h+r.globals.translateX+\"px\",n.style.top=c+\"px\")}},{key:\"getBarTooltipXY\",value:function(t){var e=t.e,i=t.opt,a=this.w,s=null,r=this.ttCtx,o=0,n=0,l=0,h=0,c=0,d=e.target.classList;if(d.contains(\"apexcharts-bar-area\")||d.contains(\"apexcharts-candlestick-area\")||d.contains(\"apexcharts-boxPlot-area\")||d.contains(\"apexcharts-rangebar-area\")){var g=e.target,u=g.getBoundingClientRect(),f=i.elGrid.getBoundingClientRect(),p=u.height;c=u.height;var x=u.width,b=parseInt(g.getAttribute(\"cx\"),10),v=parseInt(g.getAttribute(\"cy\"),10);h=parseFloat(g.getAttribute(\"barWidth\"));var m=\"touchmove\"===e.type?e.touches[0].clientX:e.clientX;s=parseInt(g.getAttribute(\"j\"),10),o=parseInt(g.parentNode.getAttribute(\"rel\"),10)-1;var y=g.getAttribute(\"data-range-y1\"),w=g.getAttribute(\"data-range-y2\");a.globals.comboCharts&&(o=parseInt(g.parentNode.getAttribute(\"data:realIndex\"),10)),r.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:o,j:s,y1:y?parseInt(y,10):null,y2:w?parseInt(w,10):null,shared:!r.showOnIntersect&&a.config.tooltip.shared,e:e}),a.config.tooltip.followCursor?a.globals.isBarHorizontal?(n=m-f.left+15,l=v-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(n=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=e.clientY-f.top-r.tooltipRect.ttHeight/2-15):a.globals.isBarHorizontal?((n=b)<r.xyRatios.baseLineInvertedY&&(n=b-r.tooltipRect.ttWidth),l=v-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(n=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=v)}return{x:n,y:l,barHeight:c,barWidth:h,i:o,j:s}}}]),t}(),xt=function(){function t(e){a(this,t),this.w=e.w,this.ttCtx=e}return r(t,[{key:\"drawXaxisTooltip\",value:function(){var t=this.w,e=this.ttCtx,i=\"bottom\"===t.config.xaxis.position;e.xaxisOffY=i?t.globals.gridHeight+1:-t.globals.xAxisHeight-t.config.xaxis.axisTicks.height+3;var a=i?\"apexcharts-xaxistooltip apexcharts-xaxistooltip-bottom\":\"apexcharts-xaxistooltip apexcharts-xaxistooltip-top\",s=t.globals.dom.elWrap;e.isXAxisTooltipEnabled&&(null===t.globals.dom.baseEl.querySelector(\".apexcharts-xaxistooltip\")&&(e.xaxisTooltip=document.createElement(\"div\"),e.xaxisTooltip.setAttribute(\"class\",a+\" apexcharts-theme-\"+t.config.tooltip.theme),s.appendChild(e.xaxisTooltip),e.xaxisTooltipText=document.createElement(\"div\"),e.xaxisTooltipText.classList.add(\"apexcharts-xaxistooltip-text\"),e.xaxisTooltipText.style.fontFamily=t.config.xaxis.tooltip.style.fontFamily||t.config.chart.fontFamily,e.xaxisTooltipText.style.fontSize=t.config.xaxis.tooltip.style.fontSize,e.xaxisTooltip.appendChild(e.xaxisTooltipText)))}},{key:\"drawYaxisTooltip\",value:function(){for(var t=this.w,e=this.ttCtx,i=function(i){var a=t.config.yaxis[i].opposite||t.config.yaxis[i].crosshairs.opposite;e.yaxisOffX=a?t.globals.gridWidth+1:1;var s=\"apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i,a?\" apexcharts-yaxistooltip-right\":\" apexcharts-yaxistooltip-left\");t.globals.yAxisSameScaleIndices.map((function(e,a){e.map((function(e,a){a===i&&(s+=t.config.yaxis[a].show?\" \":\" apexcharts-yaxistooltip-hidden\")}))}));var r=t.globals.dom.elWrap;null===t.globals.dom.baseEl.querySelector(\".apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i))&&(e.yaxisTooltip=document.createElement(\"div\"),e.yaxisTooltip.setAttribute(\"class\",s+\" apexcharts-theme-\"+t.config.tooltip.theme),r.appendChild(e.yaxisTooltip),0===i&&(e.yaxisTooltipText=[]),e.yaxisTooltipText[i]=document.createElement(\"div\"),e.yaxisTooltipText[i].classList.add(\"apexcharts-yaxistooltip-text\"),e.yaxisTooltip.appendChild(e.yaxisTooltipText[i]))},a=0;a<t.config.yaxis.length;a++)i(a)}},{key:\"setXCrosshairWidth\",value:function(){var t=this.w,e=this.ttCtx,i=e.getElXCrosshairs();if(e.xcrosshairsWidth=parseInt(t.config.xaxis.crosshairs.width,10),t.globals.comboCharts){var a=t.globals.dom.baseEl.querySelector(\".apexcharts-bar-area\");if(null!==a&&\"barWidth\"===t.config.xaxis.crosshairs.width){var s=parseFloat(a.getAttribute(\"barWidth\"));e.xcrosshairsWidth=s}else if(\"tickWidth\"===t.config.xaxis.crosshairs.width){var r=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/r}}else if(\"tickWidth\"===t.config.xaxis.crosshairs.width){var o=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/o}else if(\"barWidth\"===t.config.xaxis.crosshairs.width){var n=t.globals.dom.baseEl.querySelector(\".apexcharts-bar-area\");if(null!==n){var l=parseFloat(n.getAttribute(\"barWidth\"));e.xcrosshairsWidth=l}else e.xcrosshairsWidth=1}t.globals.isBarHorizontal&&(e.xcrosshairsWidth=0),null!==i&&e.xcrosshairsWidth>0&&i.setAttribute(\"width\",e.xcrosshairsWidth)}},{key:\"handleYCrosshair\",value:function(){var t=this.w,e=this.ttCtx;e.ycrosshairs=t.globals.dom.baseEl.querySelector(\".apexcharts-ycrosshairs\"),e.ycrosshairsHidden=t.globals.dom.baseEl.querySelector(\".apexcharts-ycrosshairs-hidden\")}},{key:\"drawYaxisTooltipText\",value:function(t,e,i){var a=this.ttCtx,s=this.w,r=s.globals.yLabelFormatters[t];if(a.yaxisTooltips[t]){var o=a.getElGrid().getBoundingClientRect(),n=(e-o.top)*i.yRatio[t],l=s.globals.maxYArr[t]-s.globals.minYArr[t],h=s.globals.minYArr[t]+(l-n);a.tooltipPosition.moveYCrosshairs(e-o.top),a.yaxisTooltipText[t].innerHTML=r(h),a.tooltipPosition.moveYAxisTooltip(t)}}}]),t}(),bt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.tConfig=i.config.tooltip,this.tooltipUtil=new dt(this),this.tooltipLabels=new gt(this),this.tooltipPosition=new ut(this),this.marker=new ft(this),this.intersect=new pt(this),this.axesTooltip=new xt(this),this.showOnIntersect=this.tConfig.intersect,this.showTooltipTitle=this.tConfig.x.show,this.fixedTooltip=this.tConfig.fixed.enabled,this.xaxisTooltip=null,this.yaxisTTEls=null,this.isBarShared=!i.globals.isBarHorizontal&&this.tConfig.shared,this.lastHoverTime=Date.now()}return r(t,[{key:\"getElTooltip\",value:function(t){return t||(t=this),t.w.globals.dom.baseEl?t.w.globals.dom.baseEl.querySelector(\".apexcharts-tooltip\"):null}},{key:\"getElXCrosshairs\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-xcrosshairs\")}},{key:\"getElGrid\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-grid\")}},{key:\"drawTooltip\",value:function(t){var e=this.w;this.xyRatios=t,this.isXAxisTooltipEnabled=e.config.xaxis.tooltip.enabled&&e.globals.axisCharts,this.yaxisTooltips=e.config.yaxis.map((function(t,i){return!!(t.show&&t.tooltip.enabled&&e.globals.axisCharts)})),this.allTooltipSeriesGroups=[],e.globals.axisCharts||(this.showTooltipTitle=!1);var i=document.createElement(\"div\");if(i.classList.add(\"apexcharts-tooltip\"),e.config.tooltip.cssClass&&i.classList.add(e.config.tooltip.cssClass),i.classList.add(\"apexcharts-theme-\".concat(this.tConfig.theme)),e.globals.dom.elWrap.appendChild(i),e.globals.axisCharts){this.axesTooltip.drawXaxisTooltip(),this.axesTooltip.drawYaxisTooltip(),this.axesTooltip.setXCrosshairWidth(),this.axesTooltip.handleYCrosshair();var a=new V(this.ctx);this.xAxisTicksPositions=a.getXAxisTicksPositions()}if(!e.globals.comboCharts&&!this.tConfig.intersect&&\"rangeBar\"!==e.config.chart.type||this.tConfig.shared||(this.showOnIntersect=!0),0!==e.config.markers.size&&0!==e.globals.markers.largestSize||this.marker.drawDynamicPoints(this),e.globals.collapsedSeries.length!==e.globals.series.length){this.dataPointsDividedHeight=e.globals.gridHeight/e.globals.dataPoints,this.dataPointsDividedWidth=e.globals.gridWidth/e.globals.dataPoints,this.showTooltipTitle&&(this.tooltipTitle=document.createElement(\"div\"),this.tooltipTitle.classList.add(\"apexcharts-tooltip-title\"),this.tooltipTitle.style.fontFamily=this.tConfig.style.fontFamily||e.config.chart.fontFamily,this.tooltipTitle.style.fontSize=this.tConfig.style.fontSize,i.appendChild(this.tooltipTitle));var s=e.globals.series.length;(e.globals.xyCharts||e.globals.comboCharts)&&this.tConfig.shared&&(s=this.showOnIntersect?1:e.globals.series.length),this.legendLabels=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-legend-text\"),this.ttItems=this.createTTElements(s),this.addSVGEvents()}}},{key:\"createTTElements\",value:function(t){for(var e=this,i=this.w,a=[],s=this.getElTooltip(),r=function(r){var o=document.createElement(\"div\");o.classList.add(\"apexcharts-tooltip-series-group\"),o.style.order=i.config.tooltip.inverseOrder?t-r:r+1,e.tConfig.shared&&e.tConfig.enabledOnSeries&&Array.isArray(e.tConfig.enabledOnSeries)&&e.tConfig.enabledOnSeries.indexOf(r)<0&&o.classList.add(\"apexcharts-tooltip-series-group-hidden\");var n=document.createElement(\"span\");n.classList.add(\"apexcharts-tooltip-marker\"),n.style.backgroundColor=i.globals.colors[r],o.appendChild(n);var l=document.createElement(\"div\");l.classList.add(\"apexcharts-tooltip-text\"),l.style.fontFamily=e.tConfig.style.fontFamily||i.config.chart.fontFamily,l.style.fontSize=e.tConfig.style.fontSize,[\"y\",\"goals\",\"z\"].forEach((function(t){var e=document.createElement(\"div\");e.classList.add(\"apexcharts-tooltip-\".concat(t,\"-group\"));var i=document.createElement(\"span\");i.classList.add(\"apexcharts-tooltip-text-\".concat(t,\"-label\")),e.appendChild(i);var a=document.createElement(\"span\");a.classList.add(\"apexcharts-tooltip-text-\".concat(t,\"-value\")),e.appendChild(a),l.appendChild(e)})),o.appendChild(l),s.appendChild(o),a.push(o)},o=0;o<t;o++)r(o);return a}},{key:\"addSVGEvents\",value:function(){var t=this.w,e=t.config.chart.type,i=this.getElTooltip(),a=!(\"bar\"!==e&&\"candlestick\"!==e&&\"boxPlot\"!==e&&\"rangeBar\"!==e),s=\"area\"===e||\"line\"===e||\"scatter\"===e||\"bubble\"===e||\"radar\"===e,r=t.globals.dom.Paper.node,o=this.getElGrid();o&&(this.seriesBound=o.getBoundingClientRect());var n,l=[],h=[],c={hoverArea:r,elGrid:o,tooltipEl:i,tooltipY:l,tooltipX:h,ttItems:this.ttItems};if(t.globals.axisCharts&&(s?n=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series[data\\\\:longestSeries='true'] .apexcharts-marker\"):a?n=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-boxPlot-area, .apexcharts-series .apexcharts-rangebar-area\"):\"heatmap\"!==e&&\"treemap\"!==e||(n=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series .apexcharts-heatmap, .apexcharts-series .apexcharts-treemap\")),n&&n.length))for(var d=0;d<n.length;d++)l.push(n[d].getAttribute(\"cy\")),h.push(n[d].getAttribute(\"cx\"));if(t.globals.xyCharts&&!this.showOnIntersect||t.globals.comboCharts&&!this.showOnIntersect||a&&this.tooltipUtil.hasBars()&&this.tConfig.shared)this.addPathsEventListeners([r],c);else if(a&&!t.globals.comboCharts||s&&this.showOnIntersect)this.addDatapointEventsListeners(c);else if(!t.globals.axisCharts||\"heatmap\"===e||\"treemap\"===e){var g=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-series\");this.addPathsEventListeners(g,c)}if(this.showOnIntersect){var u=t.globals.dom.baseEl.querySelectorAll(\".apexcharts-line-series .apexcharts-marker, .apexcharts-area-series .apexcharts-marker\");u.length>0&&this.addPathsEventListeners(u,c),this.tooltipUtil.hasBars()&&!this.tConfig.shared&&this.addDatapointEventsListeners(c)}}},{key:\"drawFixedTooltipRect\",value:function(){var t=this.w,e=this.getElTooltip(),i=e.getBoundingClientRect(),a=i.width+10,s=i.height+10,r=this.tConfig.fixed.offsetX,o=this.tConfig.fixed.offsetY,n=this.tConfig.fixed.position.toLowerCase();return n.indexOf(\"right\")>-1&&(r=r+t.globals.svgWidth-a+10),n.indexOf(\"bottom\")>-1&&(o=o+t.globals.svgHeight-s-10),e.style.left=r+\"px\",e.style.top=o+\"px\",{x:r,y:o,ttWidth:a,ttHeight:s}}},{key:\"addDatapointEventsListeners\",value:function(t){var e=this.w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series-markers .apexcharts-marker, .apexcharts-bar-area, .apexcharts-candlestick-area, .apexcharts-boxPlot-area, .apexcharts-rangebar-area\");this.addPathsEventListeners(e,t)}},{key:\"addPathsEventListeners\",value:function(t,e){for(var i=this,a=function(a){var s={paths:t[a],tooltipEl:e.tooltipEl,tooltipY:e.tooltipY,tooltipX:e.tooltipX,elGrid:e.elGrid,hoverArea:e.hoverArea,ttItems:e.ttItems};[\"mousemove\",\"mouseup\",\"touchmove\",\"mouseout\",\"touchend\"].map((function(e){return t[a].addEventListener(e,i.onSeriesHover.bind(i,s),{capture:!1,passive:!0})}))},s=0;s<t.length;s++)a(s)}},{key:\"onSeriesHover\",value:function(t,e){var i=this,a=Date.now()-this.lastHoverTime;a>=100?this.seriesHover(t,e):(clearTimeout(this.seriesHoverTimeout),this.seriesHoverTimeout=setTimeout((function(){i.seriesHover(t,e)}),100-a))}},{key:\"seriesHover\",value:function(t,e){var i=this;this.lastHoverTime=Date.now();var a=[],s=this.w;s.config.chart.group&&(a=this.ctx.getGroupedCharts()),s.globals.axisCharts&&(s.globals.minX===-1/0&&s.globals.maxX===1/0||0===s.globals.dataPoints)||(a.length?a.forEach((function(a){var s=i.getElTooltip(a),r={paths:t.paths,tooltipEl:s,tooltipY:t.tooltipY,tooltipX:t.tooltipX,elGrid:t.elGrid,hoverArea:t.hoverArea,ttItems:a.w.globals.tooltip.ttItems};a.w.globals.minX===i.w.globals.minX&&a.w.globals.maxX===i.w.globals.maxX&&a.w.globals.tooltip.seriesHoverByContext({chartCtx:a,ttCtx:a.w.globals.tooltip,opt:r,e:e})})):this.seriesHoverByContext({chartCtx:this.ctx,ttCtx:this.w.globals.tooltip,opt:t,e:e}))}},{key:\"seriesHoverByContext\",value:function(t){var e=t.chartCtx,i=t.ttCtx,a=t.opt,s=t.e,r=e.w,o=this.getElTooltip();if(o){if(i.tooltipRect={x:0,y:0,ttWidth:o.getBoundingClientRect().width,ttHeight:o.getBoundingClientRect().height},i.e=s,i.tooltipUtil.hasBars()&&!r.globals.comboCharts&&!i.isBarShared)if(this.tConfig.onDatasetHover.highlightDataSeries)new N(e).toggleSeriesOnHover(s,s.target.parentNode);i.fixedTooltip&&i.drawFixedTooltipRect(),r.globals.axisCharts?i.axisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect}):i.nonAxisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect})}}},{key:\"axisChartsTooltips\",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,o=s.elGrid.getBoundingClientRect(),n=\"touchmove\"===a.type?a.touches[0].clientX:a.clientX,l=\"touchmove\"===a.type?a.touches[0].clientY:a.clientY;if(this.clientY=l,this.clientX=n,r.globals.capturedSeriesIndex=-1,r.globals.capturedDataPointIndex=-1,l<o.top||l>o.top+o.height)this.handleMouseOut(s);else{if(Array.isArray(this.tConfig.enabledOnSeries)&&!r.config.tooltip.shared){var h=parseInt(s.paths.getAttribute(\"index\"),10);if(this.tConfig.enabledOnSeries.indexOf(h)<0)return void this.handleMouseOut(s)}var c=this.getElTooltip(),d=this.getElXCrosshairs(),g=r.globals.xyCharts||\"bar\"===r.config.chart.type&&!r.globals.isBarHorizontal&&this.tooltipUtil.hasBars()&&this.tConfig.shared||r.globals.comboCharts&&this.tooltipUtil.hasBars();if(\"mousemove\"===a.type||\"touchmove\"===a.type||\"mouseup\"===a.type){if(r.globals.collapsedSeries.length+r.globals.ancillaryCollapsedSeries.length===r.globals.series.length)return;null!==d&&d.classList.add(\"apexcharts-active\");var u=this.yaxisTooltips.filter((function(t){return!0===t}));if(null!==this.ycrosshairs&&u.length&&this.ycrosshairs.classList.add(\"apexcharts-active\"),g&&!this.showOnIntersect)this.handleStickyTooltip(a,n,l,s);else if(\"heatmap\"===r.config.chart.type||\"treemap\"===r.config.chart.type){var f=this.intersect.handleHeatTreeTooltip({e:a,opt:s,x:e,y:i,type:r.config.chart.type});e=f.x,i=f.y,c.style.left=e+\"px\",c.style.top=i+\"px\"}else this.tooltipUtil.hasBars()&&this.intersect.handleBarTooltip({e:a,opt:s}),this.tooltipUtil.hasMarkers()&&this.intersect.handleMarkerTooltip({e:a,opt:s,x:e,y:i});if(this.yaxisTooltips.length)for(var p=0;p<r.config.yaxis.length;p++)this.axesTooltip.drawYaxisTooltipText(p,l,this.xyRatios);s.tooltipEl.classList.add(\"apexcharts-active\")}else\"mouseout\"!==a.type&&\"touchend\"!==a.type||this.handleMouseOut(s)}}},{key:\"nonAxisChartsTooltips\",value:function(t){var e=t.e,i=t.opt,a=t.tooltipRect,s=this.w,r=i.paths.getAttribute(\"rel\"),o=this.getElTooltip(),n=s.globals.dom.elWrap.getBoundingClientRect();if(\"mousemove\"===e.type||\"touchmove\"===e.type){o.classList.add(\"apexcharts-active\"),this.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:parseInt(r,10)-1,shared:!1});var l=s.globals.clientX-n.left-a.ttWidth/2,h=s.globals.clientY-n.top-a.ttHeight-10;if(o.style.left=l+\"px\",o.style.top=h+\"px\",s.config.legend.tooltipHoverFormatter){var c=r-1,d=(0,s.config.legend.tooltipHoverFormatter)(this.legendLabels[c].getAttribute(\"data:default-text\"),{seriesIndex:c,dataPointIndex:c,w:s});this.legendLabels[c].innerHTML=d}}else\"mouseout\"!==e.type&&\"touchend\"!==e.type||(o.classList.remove(\"apexcharts-active\"),s.config.legend.tooltipHoverFormatter&&this.legendLabels.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)})))}},{key:\"handleStickyTooltip\",value:function(t,e,i,a){var s=this.w,r=this.tooltipUtil.getNearestValues({context:this,hoverArea:a.hoverArea,elGrid:a.elGrid,clientX:e,clientY:i}),o=r.j,n=r.capturedSeries,l=a.elGrid.getBoundingClientRect();r.hoverX<0||r.hoverX>l.width?this.handleMouseOut(a):null!==n?this.handleStickyCapturedSeries(t,n,a,o):(this.tooltipUtil.isXoverlap(o)||s.globals.isBarHorizontal)&&this.create(t,this,0,o,a.ttItems)}},{key:\"handleStickyCapturedSeries\",value:function(t,e,i,a){var s=this.w;if(!this.tConfig.shared&&null===s.globals.series[e][a])return void this.handleMouseOut(i);void 0!==s.globals.series[e][a]?this.tConfig.shared&&this.tooltipUtil.isXoverlap(a)&&this.tooltipUtil.isInitialSeriesSameLen()?this.create(t,this,e,a,i.ttItems):this.create(t,this,e,a,i.ttItems,!1):this.tooltipUtil.isXoverlap(a)&&this.create(t,this,0,a,i.ttItems)}},{key:\"deactivateHoverFilter\",value:function(){for(var t=this.w,e=new m(this.ctx),i=t.globals.dom.Paper.select(\".apexcharts-bar-area\"),a=0;a<i.length;a++)e.pathMouseLeave(i[a])}},{key:\"handleMouseOut\",value:function(t){var e=this.w,i=this.getElXCrosshairs();if(t.tooltipEl.classList.remove(\"apexcharts-active\"),this.deactivateHoverFilter(),\"bubble\"!==e.config.chart.type&&this.marker.resetPointsSize(),null!==i&&i.classList.remove(\"apexcharts-active\"),null!==this.ycrosshairs&&this.ycrosshairs.classList.remove(\"apexcharts-active\"),this.isXAxisTooltipEnabled&&this.xaxisTooltip.classList.remove(\"apexcharts-active\"),this.yaxisTooltips.length){null===this.yaxisTTEls&&(this.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxistooltip\"));for(var a=0;a<this.yaxisTTEls.length;a++)this.yaxisTTEls[a].classList.remove(\"apexcharts-active\")}e.config.legend.tooltipHoverFormatter&&this.legendLabels.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)}))}},{key:\"markerClick\",value:function(t,e,i){var a=this.w;\"function\"==typeof a.config.chart.events.markerClick&&a.config.chart.events.markerClick(t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}),this.ctx.events.fireEvent(\"markerClick\",[t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}])}},{key:\"create\",value:function(t,i,a,s,r){var o,n,l,h,c,d,g,u,f,p,x,b,v,y,w,k,A=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,S=this.w,C=i;\"mouseup\"===t.type&&this.markerClick(t,a,s),null===A&&(A=this.tConfig.shared);var L=this.tooltipUtil.hasMarkers(),P=this.tooltipUtil.getElBars();if(S.config.legend.tooltipHoverFormatter){var T=S.config.legend.tooltipHoverFormatter,M=Array.from(this.legendLabels);M.forEach((function(t){var e=t.getAttribute(\"data:default-text\");t.innerHTML=decodeURIComponent(e)}));for(var I=0;I<M.length;I++){var z=M[I],X=parseInt(z.getAttribute(\"i\"),10),E=decodeURIComponent(z.getAttribute(\"data:default-text\")),Y=T(E,{seriesIndex:A?X:a,dataPointIndex:s,w:S});if(A)z.innerHTML=S.globals.collapsedSeriesIndices.indexOf(X)<0?Y:E;else if(z.innerHTML=X===a?Y:E,a===X)break}}var F=e(e({ttItems:r,i:a,j:s},void 0!==(null===(o=S.globals.seriesRange)||void 0===o||null===(n=o[a])||void 0===n||null===(l=n[s])||void 0===l||null===(h=l.y[0])||void 0===h?void 0:h.y1)&&{y1:null===(c=S.globals.seriesRange)||void 0===c||null===(d=c[a])||void 0===d||null===(g=d[s])||void 0===g||null===(u=g.y[0])||void 0===u?void 0:u.y1}),void 0!==(null===(f=S.globals.seriesRange)||void 0===f||null===(p=f[a])||void 0===p||null===(x=p[s])||void 0===x||null===(b=x.y[0])||void 0===b?void 0:b.y2)&&{y2:null===(v=S.globals.seriesRange)||void 0===v||null===(y=v[a])||void 0===y||null===(w=y[s])||void 0===w||null===(k=w.y[0])||void 0===k?void 0:k.y2});if(A){if(C.tooltipLabels.drawSeriesTexts(e(e({},F),{},{shared:!this.showOnIntersect&&this.tConfig.shared})),L&&(S.globals.markers.largestSize>0?C.marker.enlargePoints(s):C.tooltipPosition.moveDynamicPointsOnHover(s)),this.tooltipUtil.hasBars()&&(this.barSeriesHeight=this.tooltipUtil.getBarsHeight(P),this.barSeriesHeight>0)){var R=new m(this.ctx),D=S.globals.dom.Paper.select(\".apexcharts-bar-area[j='\".concat(s,\"']\"));this.deactivateHoverFilter(),this.tooltipPosition.moveStickyTooltipOverBars(s);for(var H=0;H<D.length;H++)R.pathMouseEnter(D[H])}}else C.tooltipLabels.drawSeriesTexts(e({shared:!1},F)),this.tooltipUtil.hasBars()&&C.tooltipPosition.moveStickyTooltipOverBars(s),L&&C.tooltipPosition.moveMarkers(a,s)}}]),t}(),vt=function(){function t(e){a(this,t),this.w=e.w,this.barCtx=e,this.totalFormatter=this.w.config.plotOptions.bar.dataLabels.total.formatter,this.totalFormatter||(this.totalFormatter=this.w.config.dataLabels.formatter)}return r(t,[{key:\"handleBarDataLabels\",value:function(t){var e=t.x,i=t.y,a=t.y1,s=t.y2,r=t.i,o=t.j,n=t.realIndex,l=t.series,h=t.barHeight,c=t.barWidth,d=t.barYPosition,g=t.visibleSeries,u=t.renderedPath,f=this.w,p=new m(this.barCtx.ctx),x=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[n]:this.barCtx.strokeWidth,b=e+parseFloat(c*g),v=i+parseFloat(h*g);f.globals.isXNumeric&&!f.globals.isBarHorizontal&&(b=e+parseFloat(c*(g+1)),v=i+parseFloat(h*(g+1))-x);var y,w=null,k=e,A=i,S={},C=f.config.dataLabels,L=this.barCtx.barOptions.dataLabels,P=this.barCtx.barOptions.dataLabels.total;void 0!==d&&this.barCtx.isRangeBar&&(v=d,A=d);var T=C.offsetX,M=C.offsetY,I={width:0,height:0};if(f.config.dataLabels.enabled){var z=this.barCtx.series[r][o];I=p.getTextRects(f.globals.yLabelFormatters[0](z),parseFloat(C.style.fontSize))}var X={x:e,y:i,i:r,j:o,realIndex:n,renderedPath:u,bcx:b,bcy:v,barHeight:h,barWidth:c,textRects:I,strokeWidth:x,dataLabelsX:k,dataLabelsY:A,dataLabelsConfig:C,barDataLabelsConfig:L,barTotalDataLabelsConfig:P,offX:T,offY:M};return S=this.barCtx.isHorizontal?this.calculateBarsDataLabelsPosition(X):this.calculateColumnsDataLabelsPosition(X),u.attr({cy:S.bcy,cx:S.bcx,j:o,val:l[r][o],barHeight:h,barWidth:c}),y=this.drawCalculatedDataLabels({x:S.dataLabelsX,y:S.dataLabelsY,val:this.barCtx.isRangeBar?[a,s]:l[r][o],i:n,j:o,barWidth:c,barHeight:h,textRects:I,dataLabelsConfig:C}),f.config.chart.stacked&&P.enabled&&(w=this.drawTotalDataLabels({x:S.totalDataLabelsX,y:S.totalDataLabelsY,realIndex:n,textAnchor:S.totalDataLabelsAnchor,val:this.getStackedTotalDataLabel({realIndex:n,j:o}),dataLabelsConfig:C,barTotalDataLabelsConfig:P})),{dataLabels:y,totalDataLabels:w}}},{key:\"getStackedTotalDataLabel\",value:function(t){var i=t.realIndex,a=t.j,s=this.w,r=this.barCtx.stackedSeriesTotals[a];return this.totalFormatter&&(r=this.totalFormatter(r,e(e({},s),{},{seriesIndex:i,dataPointIndex:a,w:s}))),r}},{key:\"calculateColumnsDataLabelsPosition\",value:function(t){var e,i,a,s=this.w,r=t.i,o=t.j,n=t.realIndex,l=t.y,h=t.bcx,c=t.barWidth,d=t.barHeight,g=t.textRects,u=t.dataLabelsY,f=t.dataLabelsConfig,p=t.barDataLabelsConfig,x=t.barTotalDataLabelsConfig,b=t.strokeWidth,v=t.offX,y=t.offY;d=Math.abs(d);var w=\"vertical\"===s.config.plotOptions.bar.dataLabels.orientation;h-=b/2;var k=s.globals.gridWidth/s.globals.dataPoints;if(e=s.globals.isXNumeric?h-c/2+v:h-k+c/2+v,w){e=e+g.height/2-b/2-2}var A=this.barCtx.series[r][o]<0,S=l;switch(this.barCtx.isReversed&&(S=l-d+(A?2*d:0),l-=d),p.position){case\"center\":u=w?A?S+d/2+y:S+d/2-y:A?S-d/2+g.height/2+y:S+d/2+g.height/2-y;break;case\"bottom\":u=w?A?S+d+y:S+d-y:A?S-d+g.height+b+y:S+d-g.height/2+b-y;break;case\"top\":u=w?A?S+y:S-y:A?S-g.height/2-y:S+g.height+y}if(this.barCtx.lastActiveBarSerieIndex===n&&x.enabled){var C=new m(this.barCtx.ctx).getTextRects(this.getStackedTotalDataLabel({realIndex:n,j:o}),f.fontSize);i=A?S-C.height/2-y-x.offsetY+18:S+C.height+y+x.offsetY-18,a=e+x.offsetX}return s.config.chart.stacked||(u<0?u=0+b:u+g.height/3>s.globals.gridHeight&&(u=s.globals.gridHeight-b)),{bcx:h,bcy:l,dataLabelsX:e,dataLabelsY:u,totalDataLabelsX:a,totalDataLabelsY:i,totalDataLabelsAnchor:\"middle\"}}},{key:\"calculateBarsDataLabelsPosition\",value:function(t){var e=this.w,i=t.x,a=t.i,s=t.j,r=t.realIndex,o=t.bcy,n=t.barHeight,l=t.barWidth,h=t.textRects,c=t.dataLabelsX,d=t.strokeWidth,g=t.dataLabelsConfig,u=t.barDataLabelsConfig,f=t.barTotalDataLabelsConfig,p=t.offX,x=t.offY,b=e.globals.gridHeight/e.globals.dataPoints;l=Math.abs(l);var v,y,w=o-(this.barCtx.isRangeBar?0:b)+n/2+h.height/2+x-3,k=\"start\",A=this.barCtx.series[a][s]<0,S=i;switch(this.barCtx.isReversed&&(S=i+l-(A?2*l:0),i=e.globals.gridWidth-l),u.position){case\"center\":c=A?S+l/2-p:Math.max(h.width/2,S-l/2)+p;break;case\"bottom\":c=A?S+l-d-Math.round(h.width/2)-p:S-l+d+Math.round(h.width/2)+p;break;case\"top\":c=A?S-d+Math.round(h.width/2)-p:S-d-Math.round(h.width/2)+p}if(this.barCtx.lastActiveBarSerieIndex===r&&f.enabled){var C=new m(this.barCtx.ctx).getTextRects(this.getStackedTotalDataLabel({realIndex:r,j:s}),g.fontSize);A?(v=S-d+Math.round(C.width/2)-p-f.offsetX-15,k=\"end\"):v=S-d-Math.round(C.width/2)+p+f.offsetX+15,y=w+f.offsetY}return e.config.chart.stacked||(c<0?c=c+h.width+d:c+h.width/2>e.globals.gridWidth&&(c=e.globals.gridWidth-h.width-d)),{bcx:i,bcy:o,dataLabelsX:c,dataLabelsY:w,totalDataLabelsX:v,totalDataLabelsY:y,totalDataLabelsAnchor:k}}},{key:\"drawCalculatedDataLabels\",value:function(t){var i=t.x,a=t.y,s=t.val,r=t.i,o=t.j,n=t.textRects,l=t.barHeight,h=t.barWidth,c=t.dataLabelsConfig,d=this.w,g=\"rotate(0)\";\"vertical\"===d.config.plotOptions.bar.dataLabels.orientation&&(g=\"rotate(-90, \".concat(i,\", \").concat(a,\")\"));var u=new O(this.barCtx.ctx),f=new m(this.barCtx.ctx),p=c.formatter,x=null,b=d.globals.collapsedSeriesIndices.indexOf(r)>-1;if(c.enabled&&!b){x=f.group({class:\"apexcharts-data-labels\",transform:g});var v=\"\";void 0!==s&&(v=p(s,e(e({},d),{},{seriesIndex:r,dataPointIndex:o,w:d})));var y=d.globals.series[r][o]<0,w=d.config.plotOptions.bar.dataLabels.position;if(\"vertical\"===d.config.plotOptions.bar.dataLabels.orientation&&(\"top\"===w&&(c.textAnchor=y?\"end\":\"start\"),\"center\"===w&&(c.textAnchor=\"middle\"),\"bottom\"===w&&(c.textAnchor=y?\"end\":\"start\")),this.barCtx.isRangeBar&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels)h<f.getTextRects(v,parseFloat(c.style.fontSize)).width&&(v=\"\");d.config.chart.stacked&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels&&(this.barCtx.isHorizontal?n.width/1.6>Math.abs(h)&&(v=\"\"):n.height/1.6>Math.abs(l)&&(v=\"\"));var k=e({},c);this.barCtx.isHorizontal&&s<0&&(\"start\"===c.textAnchor?k.textAnchor=\"end\":\"end\"===c.textAnchor&&(k.textAnchor=\"start\")),u.plotDataLabelsText({x:i,y:a,text:v,i:r,j:o,parent:x,dataLabelsConfig:k,alwaysDrawDataLabel:!0,offsetCorrection:!0})}return x}},{key:\"drawTotalDataLabels\",value:function(t){var e,i=t.x,a=t.y,s=t.val,r=t.realIndex,o=t.textAnchor,n=t.barTotalDataLabelsConfig,l=new m(this.barCtx.ctx);return n.enabled&&void 0!==i&&void 0!==a&&this.barCtx.lastActiveBarSerieIndex===r&&(e=l.drawText({x:i,y:a,foreColor:n.style.color,text:s,textAnchor:o,fontFamily:n.style.fontFamily,fontSize:n.style.fontSize,fontWeight:n.style.fontWeight})),e}}]),t}(),mt=function(){function t(e){a(this,t),this.w=e.w,this.barCtx=e}return r(t,[{key:\"initVariables\",value:function(t){var e=this.w;this.barCtx.series=t,this.barCtx.totalItems=0,this.barCtx.seriesLen=0,this.barCtx.visibleI=-1,this.barCtx.visibleItems=1;for(var i=0;i<t.length;i++)if(t[i].length>0&&(this.barCtx.seriesLen=this.barCtx.seriesLen+1,this.barCtx.totalItems+=t[i].length),e.globals.isXNumeric)for(var a=0;a<t[i].length;a++)e.globals.seriesX[i][a]>e.globals.minX&&e.globals.seriesX[i][a]<e.globals.maxX&&this.barCtx.visibleItems++;else this.barCtx.visibleItems=e.globals.dataPoints;0===this.barCtx.seriesLen&&(this.barCtx.seriesLen=1),this.barCtx.zeroSerieses=[],this.barCtx.radiusOnSeriesNumber=t.length-1,e.globals.comboCharts||this.checkZeroSeries({series:t})}},{key:\"initialPositions\",value:function(){var t,e,i,a,s,r,o,n,l=this.w,h=l.globals.dataPoints;this.barCtx.isRangeBar&&(h=l.globals.labels.length);var c=this.barCtx.seriesLen;if(l.config.plotOptions.bar.rangeBarGroupRows&&(c=1),this.barCtx.isHorizontal)s=(i=l.globals.gridHeight/h)/c,l.globals.isXNumeric&&(s=(i=l.globals.gridHeight/this.barCtx.totalItems)/this.barCtx.seriesLen),s=s*parseInt(this.barCtx.barOptions.barHeight,10)/100,n=this.barCtx.baseLineInvertedY+l.globals.padHorizontal+(this.barCtx.isReversed?l.globals.gridWidth:0)-(this.barCtx.isReversed?2*this.barCtx.baseLineInvertedY:0),e=(i-s*this.barCtx.seriesLen)/2;else{if(a=l.globals.gridWidth/this.barCtx.visibleItems,l.config.xaxis.convertedCatToNumeric&&(a=l.globals.gridWidth/l.globals.dataPoints),r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100,l.globals.isXNumeric){var d=this.barCtx.xRatio;l.config.xaxis.convertedCatToNumeric&&(d=this.barCtx.initialXRatio),l.globals.minXDiff&&.5!==l.globals.minXDiff&&l.globals.minXDiff/d>0&&(a=l.globals.minXDiff/d),(r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100)<1&&(r=1)}o=l.globals.gridHeight-this.barCtx.baseLineY[this.barCtx.yaxisIndex]-(this.barCtx.isReversed?l.globals.gridHeight:0)+(this.barCtx.isReversed?2*this.barCtx.baseLineY[this.barCtx.yaxisIndex]:0),t=l.globals.padHorizontal+(a-r*this.barCtx.seriesLen)/2}return{x:t,y:e,yDivision:i,xDivision:a,barHeight:s,barWidth:r,zeroH:o,zeroW:n}}},{key:\"getPathFillColor\",value:function(t,e,i,a){var s,r,o,n,l=this.w,h=new R(this.barCtx.ctx),c=null,d=this.barCtx.barOptions.distributed?i:e;this.barCtx.barOptions.colors.ranges.length>0&&this.barCtx.barOptions.colors.ranges.map((function(a){t[e][i]>=a.from&&t[e][i]<=a.to&&(c=a.color)}));return l.config.series[e].data[i]&&l.config.series[e].data[i].fillColor&&(c=l.config.series[e].data[i].fillColor),h.fillPath({seriesNumber:this.barCtx.barOptions.distributed?d:a,dataPointIndex:i,color:c,value:t[e][i],fillConfig:null===(s=l.config.series[e].data[i])||void 0===s?void 0:s.fill,fillType:null!==(r=l.config.series[e].data[i])&&void 0!==r&&null!==(o=r.fill)&&void 0!==o&&o.type?null===(n=l.config.series[e].data[i])||void 0===n?void 0:n.fill.type:l.config.fill.type})}},{key:\"getStrokeWidth\",value:function(t,e,i){var a=0,s=this.w;return this.barCtx.series[t][e]?this.barCtx.isNullValue=!1:this.barCtx.isNullValue=!0,s.config.stroke.show&&(this.barCtx.isNullValue||(a=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[i]:this.barCtx.strokeWidth)),a}},{key:\"shouldApplyRadius\",value:function(t){var e=this.w,i=!1;return e.config.plotOptions.bar.borderRadius>0&&(e.config.chart.stacked&&\"last\"===e.config.plotOptions.bar.borderRadiusWhenStacked?this.barCtx.lastActiveBarSerieIndex===t&&(i=!0):i=!0),i}},{key:\"barBackground\",value:function(t){var e=t.j,i=t.i,a=t.x1,s=t.x2,r=t.y1,o=t.y2,n=t.elSeries,l=this.w,h=new m(this.barCtx.ctx),c=new N(this.barCtx.ctx).getActiveConfigSeriesIndex();if(this.barCtx.barOptions.colors.backgroundBarColors.length>0&&c===i){e>=this.barCtx.barOptions.colors.backgroundBarColors.length&&(e%=this.barCtx.barOptions.colors.backgroundBarColors.length);var d=this.barCtx.barOptions.colors.backgroundBarColors[e],g=h.drawRect(void 0!==a?a:0,void 0!==r?r:0,void 0!==s?s:l.globals.gridWidth,void 0!==o?o:l.globals.gridHeight,this.barCtx.barOptions.colors.backgroundBarRadius,d,this.barCtx.barOptions.colors.backgroundBarOpacity);n.add(g),g.node.classList.add(\"apexcharts-backgroundBar\")}}},{key:\"getColumnPaths\",value:function(t){var e,i=t.barWidth,a=t.barXPosition,s=t.y1,r=t.y2,o=t.strokeWidth,n=t.realIndex,l=t.i,h=t.j,c=t.w,d=new m(this.barCtx.ctx);(o=Array.isArray(o)?o[n]:o)||(o=0);var g=i,u=a;null!==(e=c.config.series[n].data[h])&&void 0!==e&&e.columnWidthOffset&&(u=a-c.config.series[n].data[h].columnWidthOffset/2,g=i+c.config.series[n].data[h].columnWidthOffset);var f=u,p=u+g;s+=.001,r+=.001;var x=d.move(f,s),b=d.move(f,s),v=d.line(p-o,s);return c.globals.previousPaths.length>0&&(b=this.barCtx.getPreviousPath(n,h,!1)),x=x+d.line(f,r)+d.line(p-o,r)+d.line(p-o,s)+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),b=b+d.line(f,s)+v+v+v+v+v+d.line(f,s)+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),this.shouldApplyRadius(n)&&(x=d.roundPathCorners(x,c.config.plotOptions.bar.borderRadius)),c.config.chart.stacked&&(this.barCtx.yArrj.push(r),this.barCtx.yArrjF.push(Math.abs(s-r)),this.barCtx.yArrjVal.push(this.barCtx.series[l][h])),{pathTo:x,pathFrom:b}}},{key:\"getBarpaths\",value:function(t){var e,i=t.barYPosition,a=t.barHeight,s=t.x1,r=t.x2,o=t.strokeWidth,n=t.realIndex,l=t.i,h=t.j,c=t.w,d=new m(this.barCtx.ctx);(o=Array.isArray(o)?o[n]:o)||(o=0);var g=i,u=a;null!==(e=c.config.series[n].data[h])&&void 0!==e&&e.barHeightOffset&&(g=i-c.config.series[n].data[h].barHeightOffset/2,u=a+c.config.series[n].data[h].barHeightOffset);var f=g,p=g+u;s+=.001,r+=.001;var x=d.move(s,f),b=d.move(s,f);c.globals.previousPaths.length>0&&(b=this.barCtx.getPreviousPath(n,h,!1));var v=d.line(s,p-o);return x=x+d.line(r,f)+d.line(r,p-o)+v+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),b=b+d.line(s,f)+v+v+v+v+v+d.line(s,f)+(\"around\"===c.config.plotOptions.bar.borderRadiusApplication?\" Z\":\" z\"),this.shouldApplyRadius(n)&&(x=d.roundPathCorners(x,c.config.plotOptions.bar.borderRadius)),c.config.chart.stacked&&(this.barCtx.xArrj.push(r),this.barCtx.xArrjF.push(Math.abs(s-r)),this.barCtx.xArrjVal.push(this.barCtx.series[l][h])),{pathTo:x,pathFrom:b}}},{key:\"checkZeroSeries\",value:function(t){for(var e=t.series,i=this.w,a=0;a<e.length;a++){for(var s=0,r=0;r<e[i.globals.maxValsInArrayIndex].length;r++)s+=e[a][r];0===s&&this.barCtx.zeroSerieses.push(a)}for(var o=e.length-1;o>=0;o--)this.barCtx.zeroSerieses.indexOf(o)>-1&&o===this.radiusOnSeriesNumber&&(this.barCtx.radiusOnSeriesNumber-=1);for(var n=e.length-1;n>=0;n--)i.globals.collapsedSeriesIndices.indexOf(this.barCtx.radiusOnSeriesNumber)>-1&&(this.barCtx.radiusOnSeriesNumber-=1)}},{key:\"getXForValue\",value:function(t,e){var i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=i?e:null;return null!=t&&(a=e+t/this.barCtx.invertedYRatio-2*(this.barCtx.isReversed?t/this.barCtx.invertedYRatio:0)),a}},{key:\"getYForValue\",value:function(t,e){var i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=i?e:null;return null!=t&&(a=e-t/this.barCtx.yRatio[this.barCtx.yaxisIndex]+2*(this.barCtx.isReversed?t/this.barCtx.yRatio[this.barCtx.yaxisIndex]:0)),a}},{key:\"getGoalValues\",value:function(t,e,i,a,s){var r=this,n=this.w,l=[];return n.globals.seriesGoals[a]&&n.globals.seriesGoals[a][s]&&Array.isArray(n.globals.seriesGoals[a][s])&&n.globals.seriesGoals[a][s].forEach((function(a){var s;l.push((o(s={},t,\"x\"===t?r.getXForValue(a.value,e,!1):r.getYForValue(a.value,i,!1)),o(s,\"attrs\",a),s))})),l}},{key:\"drawGoalLine\",value:function(t){var e=t.barXPosition,i=t.barYPosition,a=t.goalX,s=t.goalY,r=t.barWidth,o=t.barHeight,n=new m(this.barCtx.ctx),l=n.group({className:\"apexcharts-bar-goals-groups\"}),h=null;return this.barCtx.isHorizontal?Array.isArray(a)&&a.forEach((function(t){var e=void 0!==t.attrs.strokeHeight?t.attrs.strokeHeight:o/2,a=i+e+o/2;h=n.drawLine(t.x,a-2*e,t.x,a,t.attrs.strokeColor?t.attrs.strokeColor:void 0,t.attrs.strokeDashArray,t.attrs.strokeWidth?t.attrs.strokeWidth:2,t.attrs.strokeLineCap),l.add(h)})):Array.isArray(s)&&s.forEach((function(t){var i=void 0!==t.attrs.strokeWidth?t.attrs.strokeWidth:r/2,a=e+i+r/2;h=n.drawLine(a-2*i,t.y,a,t.y,t.attrs.strokeColor?t.attrs.strokeColor:void 0,t.attrs.strokeDashArray,t.attrs.strokeHeight?t.attrs.strokeHeight:2,t.attrs.strokeLineCap),l.add(h)})),l}}]),t}(),yt=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w;var s=this.w;this.barOptions=s.config.plotOptions.bar,this.isHorizontal=this.barOptions.horizontal,this.strokeWidth=s.config.stroke.width,this.isNullValue=!1,this.isRangeBar=s.globals.seriesRange.length&&this.isHorizontal,this.xyRatios=i,null!==this.xyRatios&&(this.xRatio=i.xRatio,this.initialXRatio=i.initialXRatio,this.yRatio=i.yRatio,this.invertedXRatio=i.invertedXRatio,this.invertedYRatio=i.invertedYRatio,this.baseLineY=i.baseLineY,this.baseLineInvertedY=i.baseLineInvertedY),this.yaxisIndex=0,this.seriesLen=0;var r=new N(this.ctx);this.lastActiveBarSerieIndex=r.getActiveConfigSeriesIndex(\"desc\",[\"bar\",\"column\"]);var o=r.getBarSeriesIndices(),n=new y(this.ctx);this.stackedSeriesTotals=n.getStackedSeriesTotals(this.w.config.series.map((function(t,e){return-1===o.indexOf(e)?e:-1})).filter((function(t){return-1!==t}))),this.barHelpers=new mt(this)}return r(t,[{key:\"draw\",value:function(t,i){var a=this.w,s=new m(this.ctx),r=new y(this.ctx,a);t=r.getLogSeries(t),this.series=t,this.yRatio=r.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);var o=s.group({class:\"apexcharts-bar-series apexcharts-plot-series\"});a.config.dataLabels.enabled&&this.totalItems>this.barOptions.dataLabels.maxItems&&console.warn(\"WARNING: DataLabels are enabled but there are too many to display. This may cause performance issue when rendering.\");for(var n=0,l=0;n<t.length;n++,l++){var h,c,d,g,u=void 0,f=void 0,p=[],b=[],v=a.globals.comboCharts?i[n]:n,w=s.group({class:\"apexcharts-series\",rel:n+1,seriesName:x.escapeString(a.globals.seriesNames[v]),\"data:realIndex\":v});this.ctx.series.addCollapsedClassToSeries(w,v),t[n].length>0&&(this.visibleI=this.visibleI+1);var k=0,A=0;this.yRatio.length>1&&(this.yaxisIndex=v),this.isReversed=a.config.yaxis[this.yaxisIndex]&&a.config.yaxis[this.yaxisIndex].reversed;var S=this.barHelpers.initialPositions();f=S.y,k=S.barHeight,c=S.yDivision,g=S.zeroW,u=S.x,A=S.barWidth,h=S.xDivision,d=S.zeroH,this.horizontal||b.push(u+A/2);for(var C=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":v}),L=s.group({class:\"apexcharts-bar-goals-markers\",style:\"pointer-events: none\"}),P=0;P<a.globals.dataPoints;P++){var T=this.barHelpers.getStrokeWidth(n,P,v),M=null,I={indexes:{i:n,j:P,realIndex:v,bc:l},x:u,y:f,strokeWidth:T,elSeries:w};this.isHorizontal?(M=this.drawBarPaths(e(e({},I),{},{barHeight:k,zeroW:g,yDivision:c})),A=this.series[n][P]/this.invertedYRatio):(M=this.drawColumnPaths(e(e({},I),{},{xDivision:h,barWidth:A,zeroH:d})),k=this.series[n][P]/this.yRatio[this.yaxisIndex]);var z=this.barHelpers.drawGoalLine({barXPosition:M.barXPosition,barYPosition:M.barYPosition,goalX:M.goalX,goalY:M.goalY,barHeight:k,barWidth:A});z&&L.add(z),f=M.y,u=M.x,P>0&&b.push(u+A/2),p.push(f);var X=this.barHelpers.getPathFillColor(t,n,P,v);this.renderSeries({realIndex:v,pathFill:X,j:P,i:n,pathFrom:M.pathFrom,pathTo:M.pathTo,strokeWidth:T,elSeries:w,x:u,y:f,series:t,barHeight:k,barWidth:A,elDataLabelsWrap:C,elGoalsMarkers:L,visibleSeries:this.visibleI,type:\"bar\"})}a.globals.seriesXvalues[v]=b,a.globals.seriesYvalues[v]=p,o.add(w)}return o}},{key:\"renderSeries\",value:function(t){var e=t.realIndex,i=t.pathFill,a=t.lineFill,s=t.j,r=t.i,o=t.pathFrom,n=t.pathTo,l=t.strokeWidth,h=t.elSeries,c=t.x,d=t.y,g=t.y1,u=t.y2,f=t.series,p=t.barHeight,x=t.barWidth,b=t.barYPosition,y=t.elDataLabelsWrap,w=t.elGoalsMarkers,k=t.visibleSeries,A=t.type,S=this.w,C=new m(this.ctx);a||(a=this.barOptions.distributed?S.globals.stroke.colors[s]:S.globals.stroke.colors[e]),S.config.series[r].data[s]&&S.config.series[r].data[s].strokeColor&&(a=S.config.series[r].data[s].strokeColor),this.isNullValue&&(i=\"none\");var L=s/S.config.chart.animations.animateGradually.delay*(S.config.chart.animations.speed/S.globals.dataPoints)/2.4,P=C.renderPaths({i:r,j:s,realIndex:e,pathFrom:o,pathTo:n,stroke:a,strokeWidth:l,strokeLineCap:S.config.stroke.lineCap,fill:i,animationDelay:L,initialSpeed:S.config.chart.animations.speed,dataChangeSpeed:S.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-\".concat(A,\"-area\")});P.attr(\"clip-path\",\"url(#gridRectMask\".concat(S.globals.cuid,\")\"));var T=S.config.forecastDataPoints;T.count>0&&s>=S.globals.dataPoints-T.count&&(P.node.setAttribute(\"stroke-dasharray\",T.dashArray),P.node.setAttribute(\"stroke-width\",T.strokeWidth),P.node.setAttribute(\"fill-opacity\",T.fillOpacity)),void 0!==g&&void 0!==u&&(P.attr(\"data-range-y1\",g),P.attr(\"data-range-y2\",u)),new v(this.ctx).setSelectionFilter(P,e,s),h.add(P);var M=new vt(this).handleBarDataLabels({x:c,y:d,y1:g,y2:u,i:r,j:s,series:f,realIndex:e,barHeight:p,barWidth:x,barYPosition:b,renderedPath:P,visibleSeries:k});return null!==M.dataLabels&&y.add(M.dataLabels),M.totalDataLabels&&y.add(M.totalDataLabels),h.add(y),w&&h.add(w),h}},{key:\"drawBarPaths\",value:function(t){var e,i=t.indexes,a=t.barHeight,s=t.strokeWidth,r=t.zeroW,o=t.x,n=t.y,l=t.yDivision,h=t.elSeries,c=this.w,d=i.i,g=i.j;if(c.globals.isXNumeric)e=(n=(c.globals.seriesX[d][g]-c.globals.minX)/this.invertedXRatio-a)+a*this.visibleI;else if(c.config.plotOptions.bar.hideZeroBarsWhenGrouped){var u=0,f=0;c.globals.seriesPercent.forEach((function(t,e){t[g]&&u++,e<d&&0===t[g]&&f++})),e=n+(a=this.seriesLen*a/u)*this.visibleI,e-=a*f}o=this.barHelpers.getXForValue(this.series[d][g],r);var p=this.barHelpers.getBarpaths({barYPosition:e,barHeight:a,x1:r,x2:o,strokeWidth:s,series:this.series,realIndex:i.realIndex,i:d,j:g,w:c});return c.globals.isXNumeric||(n+=l),this.barHelpers.barBackground({j:g,i:d,y1:e-a*this.visibleI,y2:a*this.seriesLen,elSeries:h}),{pathTo:p.pathTo,pathFrom:p.pathFrom,x:o,y:n,goalX:this.barHelpers.getGoalValues(\"x\",r,null,d,g),barYPosition:e}}},{key:\"drawColumnPaths\",value:function(t){var e,i=t.indexes,a=t.x,s=t.y,r=t.xDivision,o=t.barWidth,n=t.zeroH,l=t.strokeWidth,h=t.elSeries,c=this.w,d=i.realIndex,g=i.i,u=i.j,f=i.bc;if(c.globals.isXNumeric){var p=d;c.globals.seriesX[d].length||(p=c.globals.maxValsInArrayIndex),c.globals.seriesX[p][u]&&(a=(c.globals.seriesX[p][u]-c.globals.minX)/this.xRatio-o*this.seriesLen/2),e=a+o*this.visibleI}else if(c.config.plotOptions.bar.hideZeroBarsWhenGrouped){var x=0,b=0;c.globals.seriesPercent.forEach((function(t,e){t[u]&&x++,e<g&&0===t[u]&&b++})),e=a+(o=this.seriesLen*o/x)*this.visibleI,e-=o*b}s=this.barHelpers.getYForValue(this.series[g][u],n);var v=this.barHelpers.getColumnPaths({barXPosition:e,barWidth:o,y1:n,y2:s,strokeWidth:l,series:this.series,realIndex:i.realIndex,i:g,j:u,w:c});return c.globals.isXNumeric||(a+=r),this.barHelpers.barBackground({bc:f,j:u,i:g,x1:e-l/2-o*this.visibleI,x2:o*this.seriesLen+l/2,elSeries:h}),{pathTo:v.pathTo,pathFrom:v.pathFrom,x:a,y:s,goalY:this.barHelpers.getGoalValues(\"y\",null,n,g,u),barXPosition:e}}},{key:\"getPreviousPath\",value:function(t,e){for(var i,a=this.w,s=0;s<a.globals.previousPaths.length;s++){var r=a.globals.previousPaths[s];r.paths&&r.paths.length>0&&parseInt(r.realIndex,10)===parseInt(t,10)&&void 0!==a.globals.previousPaths[s].paths[e]&&(i=a.globals.previousPaths[s].paths[e].d)}return i}}]),t}(),wt=function(t){n(s,yt);var i=d(s);function s(){return a(this,s),i.apply(this,arguments)}return r(s,[{key:\"draw\",value:function(t,i){var a=this,s=this.w;this.graphics=new m(this.ctx),this.bar=new yt(this.ctx,this.xyRatios);var r=new y(this.ctx,s);t=r.getLogSeries(t),this.yRatio=r.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t),\"100%\"===s.config.chart.stackType&&(t=s.globals.seriesPercent.slice()),this.series=t,this.totalItems=0,this.prevY=[],this.prevX=[],this.prevYF=[],this.prevXF=[],this.prevYVal=[],this.prevXVal=[],this.xArrj=[],this.xArrjF=[],this.xArrjVal=[],this.yArrj=[],this.yArrjF=[],this.yArrjVal=[];for(var o=0;o<t.length;o++)t[o].length>0&&(this.totalItems+=t[o].length);for(var n=this.graphics.group({class:\"apexcharts-bar-series apexcharts-plot-series\"}),l=0,h=0,c=function(r,o){var c=void 0,d=void 0,g=void 0,u=void 0,f=[],p=[],b=s.globals.comboCharts?i[r]:r;a.yRatio.length>1&&(a.yaxisIndex=b),a.isReversed=s.config.yaxis[a.yaxisIndex]&&s.config.yaxis[a.yaxisIndex].reversed;var v=a.graphics.group({class:\"apexcharts-series\",seriesName:x.escapeString(s.globals.seriesNames[b]),rel:r+1,\"data:realIndex\":b});a.ctx.series.addCollapsedClassToSeries(v,b);var m=a.graphics.group({class:\"apexcharts-datalabels\",\"data:realIndex\":b}),y=a.graphics.group({class:\"apexcharts-bar-goals-markers\",style:\"pointer-events: none\"}),w=0,k=0,A=a.initialPositions(l,h,c,d,g,u);h=A.y,w=A.barHeight,d=A.yDivision,u=A.zeroW,l=A.x,k=A.barWidth,c=A.xDivision,g=A.zeroH,a.yArrj=[],a.yArrjF=[],a.yArrjVal=[],a.xArrj=[],a.xArrjF=[],a.xArrjVal=[],1===a.prevY.length&&a.prevY[0].every((function(t){return isNaN(t)}))&&(a.prevY[0]=a.prevY[0].map((function(t){return g})),a.prevYF[0]=a.prevYF[0].map((function(t){return 0})));for(var S=0;S<s.globals.dataPoints;S++){var C=a.barHelpers.getStrokeWidth(r,S,b),L={indexes:{i:r,j:S,realIndex:b,bc:o},strokeWidth:C,x:l,y:h,elSeries:v},P=null;a.isHorizontal?(P=a.drawStackedBarPaths(e(e({},L),{},{zeroW:u,barHeight:w,yDivision:d})),k=a.series[r][S]/a.invertedYRatio):(P=a.drawStackedColumnPaths(e(e({},L),{},{xDivision:c,barWidth:k,zeroH:g})),w=a.series[r][S]/a.yRatio[a.yaxisIndex]);var T=a.barHelpers.drawGoalLine({barXPosition:P.barXPosition,barYPosition:P.barYPosition,goalX:P.goalX,goalY:P.goalY,barHeight:w,barWidth:k});T&&y.add(T),h=P.y,l=P.x,f.push(l),p.push(h);var M=a.barHelpers.getPathFillColor(t,r,S,b);v=a.renderSeries({realIndex:b,pathFill:M,j:S,i:r,pathFrom:P.pathFrom,pathTo:P.pathTo,strokeWidth:C,elSeries:v,x:l,y:h,series:t,barHeight:w,barWidth:k,elDataLabelsWrap:m,elGoalsMarkers:y,type:\"bar\",visibleSeries:0})}s.globals.seriesXvalues[b]=f,s.globals.seriesYvalues[b]=p,a.prevY.push(a.yArrj),a.prevYF.push(a.yArrjF),a.prevYVal.push(a.yArrjVal),a.prevX.push(a.xArrj),a.prevXF.push(a.xArrjF),a.prevXVal.push(a.xArrjVal),n.add(v)},d=0,g=0;d<t.length;d++,g++)c(d,g);return n}},{key:\"initialPositions\",value:function(t,e,i,a,s,r){var o,n,l=this.w;return this.isHorizontal?(o=(o=a=l.globals.gridHeight/l.globals.dataPoints)*parseInt(l.config.plotOptions.bar.barHeight,10)/100,r=this.baseLineInvertedY+l.globals.padHorizontal+(this.isReversed?l.globals.gridWidth:0)-(this.isReversed?2*this.baseLineInvertedY:0),e=(a-o)/2):(n=i=l.globals.gridWidth/l.globals.dataPoints,n=l.globals.isXNumeric&&l.globals.dataPoints>1?(i=l.globals.minXDiff/this.xRatio)*parseInt(this.barOptions.columnWidth,10)/100:n*parseInt(l.config.plotOptions.bar.columnWidth,10)/100,s=l.globals.gridHeight-this.baseLineY[this.yaxisIndex]-(this.isReversed?l.globals.gridHeight:0)+(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),t=l.globals.padHorizontal+(i-n)/2),{x:t,y:e,yDivision:a,xDivision:i,barHeight:o,barWidth:n,zeroH:s,zeroW:r}}},{key:\"drawStackedBarPaths\",value:function(t){for(var e,i=t.indexes,a=t.barHeight,s=t.strokeWidth,r=t.zeroW,o=t.x,n=t.y,l=t.yDivision,h=t.elSeries,c=this.w,d=n,g=i.i,u=i.j,f=0,p=0;p<this.prevXF.length;p++)f+=this.prevXF[p][u];if(g>0){var x=r;this.prevXVal[g-1][u]<0?x=this.series[g][u]>=0?this.prevX[g-1][u]+f-2*(this.isReversed?f:0):this.prevX[g-1][u]:this.prevXVal[g-1][u]>=0&&(x=this.series[g][u]>=0?this.prevX[g-1][u]:this.prevX[g-1][u]-f+2*(this.isReversed?f:0)),e=x}else e=r;o=null===this.series[g][u]?e:e+this.series[g][u]/this.invertedYRatio-2*(this.isReversed?this.series[g][u]/this.invertedYRatio:0);var b=this.barHelpers.getBarpaths({barYPosition:d,barHeight:a,x1:e,x2:o,strokeWidth:s,series:this.series,realIndex:i.realIndex,i:g,j:u,w:c});return this.barHelpers.barBackground({j:u,i:g,y1:d,y2:a,elSeries:h}),n+=l,{pathTo:b.pathTo,pathFrom:b.pathFrom,goalX:this.barHelpers.getGoalValues(\"x\",r,null,g,u),barYPosition:d,x:o,y:n}}},{key:\"drawStackedColumnPaths\",value:function(t){var e=t.indexes,i=t.x,a=t.y,s=t.xDivision,r=t.barWidth,o=t.zeroH;t.strokeWidth;var n=t.elSeries,l=this.w,h=e.i,c=e.j,d=e.bc;if(l.globals.isXNumeric){var g=l.globals.seriesX[h][c];g||(g=0),i=(g-l.globals.minX)/this.xRatio-r/2}for(var u,f=i,p=0,x=0;x<this.prevYF.length;x++)p+=isNaN(this.prevYF[x][c])?0:this.prevYF[x][c];if(h>0&&!l.globals.isXNumeric||h>0&&l.globals.isXNumeric&&l.globals.seriesX[h-1][c]===l.globals.seriesX[h][c]){var b,v,m=Math.min(this.yRatio.length+1,h+1);if(void 0!==this.prevY[h-1])for(var y=1;y<m;y++)if(!isNaN(this.prevY[h-y][c])){v=this.prevY[h-y][c];break}for(var w=1;w<m;w++){if(this.prevYVal[h-w][c]<0){b=this.series[h][c]>=0?v-p+2*(this.isReversed?p:0):v;break}if(this.prevYVal[h-w][c]>=0){b=this.series[h][c]>=0?v:v+p-2*(this.isReversed?p:0);break}}void 0===b&&(b=l.globals.gridHeight),u=this.prevYF[0].every((function(t){return 0===t}))&&this.prevYF.slice(1,h).every((function(t){return t.every((function(t){return isNaN(t)}))}))?o:b}else u=o;a=this.series[h][c]?u-this.series[h][c]/this.yRatio[this.yaxisIndex]+2*(this.isReversed?this.series[h][c]/this.yRatio[this.yaxisIndex]:0):u;var k=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:r,y1:u,y2:a,yRatio:this.yRatio[this.yaxisIndex],strokeWidth:this.strokeWidth,series:this.series,realIndex:e.realIndex,i:h,j:c,w:l});return this.barHelpers.barBackground({bc:d,j:c,i:h,x1:f,x2:r,elSeries:n}),i+=s,{pathTo:k.pathTo,pathFrom:k.pathFrom,goalY:this.barHelpers.getGoalValues(\"y\",null,o,h,c),barXPosition:f,x:l.globals.isXNumeric?i-s:i,y:a}}}]),s}(),kt=function(t){n(s,yt);var i=d(s);function s(){return a(this,s),i.apply(this,arguments)}return r(s,[{key:\"draw\",value:function(t,i){var a=this,s=this.w,r=new m(this.ctx),o=new R(this.ctx);this.candlestickOptions=this.w.config.plotOptions.candlestick,this.boxOptions=this.w.config.plotOptions.boxPlot,this.isHorizontal=s.config.plotOptions.bar.horizontal;var n=new y(this.ctx,s);t=n.getLogSeries(t),this.series=t,this.yRatio=n.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);for(var l=r.group({class:\"apexcharts-\".concat(s.config.chart.type,\"-series apexcharts-plot-series\")}),h=function(n){a.isBoxPlot=\"boxPlot\"===s.config.chart.type||\"boxPlot\"===s.config.series[n].type;var h,c,d,g,u=void 0,f=void 0,p=[],b=[],v=s.globals.comboCharts?i[n]:n,m=r.group({class:\"apexcharts-series\",seriesName:x.escapeString(s.globals.seriesNames[v]),rel:n+1,\"data:realIndex\":v});a.ctx.series.addCollapsedClassToSeries(m,v),t[n].length>0&&(a.visibleI=a.visibleI+1);var y,w;a.yRatio.length>1&&(a.yaxisIndex=v);var k=a.barHelpers.initialPositions();f=k.y,y=k.barHeight,c=k.yDivision,g=k.zeroW,u=k.x,w=k.barWidth,h=k.xDivision,d=k.zeroH,b.push(u+w/2);for(var A=r.group({class:\"apexcharts-datalabels\",\"data:realIndex\":v}),S=function(i){var r=a.barHelpers.getStrokeWidth(n,i,v),l=null,x={indexes:{i:n,j:i,realIndex:v},x:u,y:f,strokeWidth:r,elSeries:m};l=a.isHorizontal?a.drawHorizontalBoxPaths(e(e({},x),{},{yDivision:c,barHeight:y,zeroW:g})):a.drawVerticalBoxPaths(e(e({},x),{},{xDivision:h,barWidth:w,zeroH:d})),f=l.y,u=l.x,i>0&&b.push(u+w/2),p.push(f),l.pathTo.forEach((function(e,h){var c=!a.isBoxPlot&&a.candlestickOptions.wick.useFillColor?l.color[h]:s.globals.stroke.colors[n],d=o.fillPath({seriesNumber:v,dataPointIndex:i,color:l.color[h],value:t[n][i]});a.renderSeries({realIndex:v,pathFill:d,lineFill:c,j:i,i:n,pathFrom:l.pathFrom,pathTo:e,strokeWidth:r,elSeries:m,x:u,y:f,series:t,barHeight:y,barWidth:w,elDataLabelsWrap:A,visibleSeries:a.visibleI,type:s.config.chart.type})}))},C=0;C<s.globals.dataPoints;C++)S(C);s.globals.seriesXvalues[v]=b,s.globals.seriesYvalues[v]=p,l.add(m)},c=0;c<t.length;c++)h(c);return l}},{key:\"drawVerticalBoxPaths\",value:function(t){var e=t.indexes,i=t.x;t.y;var a=t.xDivision,s=t.barWidth,r=t.zeroH,o=t.strokeWidth,n=this.w,l=new m(this.ctx),h=e.i,c=e.j,d=!0,g=n.config.plotOptions.candlestick.colors.upward,u=n.config.plotOptions.candlestick.colors.downward,f=\"\";this.isBoxPlot&&(f=[this.boxOptions.colors.lower,this.boxOptions.colors.upper]);var p=this.yRatio[this.yaxisIndex],x=e.realIndex,b=this.getOHLCValue(x,c),v=r,y=r;b.o>b.c&&(d=!1);var w=Math.min(b.o,b.c),k=Math.max(b.o,b.c),A=b.m;n.globals.isXNumeric&&(i=(n.globals.seriesX[x][c]-n.globals.minX)/this.xRatio-s/2);var S=i+s*this.visibleI;void 0===this.series[h][c]||null===this.series[h][c]?(w=r,k=r):(w=r-w/p,k=r-k/p,v=r-b.h/p,y=r-b.l/p,A=r-b.m/p);var C=l.move(S,r),L=l.move(S+s/2,w);return n.globals.previousPaths.length>0&&(L=this.getPreviousPath(x,c,!0)),C=this.isBoxPlot?[l.move(S,w)+l.line(S+s/2,w)+l.line(S+s/2,v)+l.line(S+s/4,v)+l.line(S+s-s/4,v)+l.line(S+s/2,v)+l.line(S+s/2,w)+l.line(S+s,w)+l.line(S+s,A)+l.line(S,A)+l.line(S,w+o/2),l.move(S,A)+l.line(S+s,A)+l.line(S+s,k)+l.line(S+s/2,k)+l.line(S+s/2,y)+l.line(S+s-s/4,y)+l.line(S+s/4,y)+l.line(S+s/2,y)+l.line(S+s/2,k)+l.line(S,k)+l.line(S,A)+\"z\"]:[l.move(S,k)+l.line(S+s/2,k)+l.line(S+s/2,v)+l.line(S+s/2,k)+l.line(S+s,k)+l.line(S+s,w)+l.line(S+s/2,w)+l.line(S+s/2,y)+l.line(S+s/2,w)+l.line(S,w)+l.line(S,k-o/2)],L+=l.move(S,w),n.globals.isXNumeric||(i+=a),{pathTo:C,pathFrom:L,x:i,y:k,barXPosition:S,color:this.isBoxPlot?f:d?[g]:[u]}}},{key:\"drawHorizontalBoxPaths\",value:function(t){var e=t.indexes;t.x;var i=t.y,a=t.yDivision,s=t.barHeight,r=t.zeroW,o=t.strokeWidth,n=this.w,l=new m(this.ctx),h=e.i,c=e.j,d=this.boxOptions.colors.lower;this.isBoxPlot&&(d=[this.boxOptions.colors.lower,this.boxOptions.colors.upper]);var g=this.invertedYRatio,u=e.realIndex,f=this.getOHLCValue(u,c),p=r,x=r,b=Math.min(f.o,f.c),v=Math.max(f.o,f.c),y=f.m;n.globals.isXNumeric&&(i=(n.globals.seriesX[u][c]-n.globals.minX)/this.invertedXRatio-s/2);var w=i+s*this.visibleI;void 0===this.series[h][c]||null===this.series[h][c]?(b=r,v=r):(b=r+b/g,v=r+v/g,p=r+f.h/g,x=r+f.l/g,y=r+f.m/g);var k=l.move(r,w),A=l.move(b,w+s/2);return n.globals.previousPaths.length>0&&(A=this.getPreviousPath(u,c,!0)),k=[l.move(b,w)+l.line(b,w+s/2)+l.line(p,w+s/2)+l.line(p,w+s/2-s/4)+l.line(p,w+s/2+s/4)+l.line(p,w+s/2)+l.line(b,w+s/2)+l.line(b,w+s)+l.line(y,w+s)+l.line(y,w)+l.line(b+o/2,w),l.move(y,w)+l.line(y,w+s)+l.line(v,w+s)+l.line(v,w+s/2)+l.line(x,w+s/2)+l.line(x,w+s-s/4)+l.line(x,w+s/4)+l.line(x,w+s/2)+l.line(v,w+s/2)+l.line(v,w)+l.line(y,w)+\"z\"],A+=l.move(b,w),n.globals.isXNumeric||(i+=a),{pathTo:k,pathFrom:A,x:v,y:i,barYPosition:w,color:d}}},{key:\"getOHLCValue\",value:function(t,e){var i=this.w;return{o:this.isBoxPlot?i.globals.seriesCandleH[t][e]:i.globals.seriesCandleO[t][e],h:this.isBoxPlot?i.globals.seriesCandleO[t][e]:i.globals.seriesCandleH[t][e],m:i.globals.seriesCandleM[t][e],l:this.isBoxPlot?i.globals.seriesCandleC[t][e]:i.globals.seriesCandleL[t][e],c:this.isBoxPlot?i.globals.seriesCandleL[t][e]:i.globals.seriesCandleC[t][e]}}}]),s}(),At=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"checkColorRange\",value:function(){var t=this.w,e=!1,i=t.config.plotOptions[t.config.chart.type];return i.colorScale.ranges.length>0&&i.colorScale.ranges.map((function(t,i){t.from<=0&&(e=!0)})),e}},{key:\"getShadeColor\",value:function(t,e,i,a){var s=this.w,r=1,o=s.config.plotOptions[t].shadeIntensity,n=this.determineColor(t,e,i);s.globals.hasNegs||a?r=s.config.plotOptions[t].reverseNegativeShade?n.percent<0?n.percent/100*(1.25*o):(1-n.percent/100)*(1.25*o):n.percent<=0?1-(1+n.percent/100)*o:(1-n.percent/100)*o:(r=1-n.percent/100,\"treemap\"===t&&(r=(1-n.percent/100)*(1.25*o)));var l=n.color,h=new x;return s.config.plotOptions[t].enableShades&&(l=\"dark\"===this.w.config.theme.mode?x.hexToRgba(h.shadeColor(-1*r,n.color),s.config.fill.opacity):x.hexToRgba(h.shadeColor(r,n.color),s.config.fill.opacity)),{color:l,colorProps:n}}},{key:\"determineColor\",value:function(t,e,i){var a=this.w,s=a.globals.series[e][i],r=a.config.plotOptions[t],o=r.colorScale.inverse?i:e;r.distributed&&\"treemap\"===a.config.chart.type&&(o=i);var n=a.globals.colors[o],l=null,h=Math.min.apply(Math,u(a.globals.series[e])),c=Math.max.apply(Math,u(a.globals.series[e]));r.distributed||\"heatmap\"!==t||(h=a.globals.minY,c=a.globals.maxY),void 0!==r.colorScale.min&&(h=r.colorScale.min<a.globals.minY?r.colorScale.min:a.globals.minY,c=r.colorScale.max>a.globals.maxY?r.colorScale.max:a.globals.maxY);var d=Math.abs(c)+Math.abs(h),g=100*s/(0===d?d-1e-6:d);r.colorScale.ranges.length>0&&r.colorScale.ranges.map((function(t,e){if(s>=t.from&&s<=t.to){n=t.color,l=t.foreColor?t.foreColor:null,h=t.from,c=t.to;var i=Math.abs(c)+Math.abs(h);g=100*s/(0===i?i-1e-6:i)}}));return{color:n,foreColor:l,percent:g}}},{key:\"calculateDataLabels\",value:function(t){var e=t.text,i=t.x,a=t.y,s=t.i,r=t.j,o=t.colorProps,n=t.fontSize,l=this.w.config.dataLabels,h=new m(this.ctx),c=new O(this.ctx),d=null;if(l.enabled){d=h.group({class:\"apexcharts-data-labels\"});var g=l.offsetX,u=l.offsetY,f=i+g,p=a+parseFloat(l.style.fontSize)/3+u;c.plotDataLabelsText({x:f,y:p,text:e,i:s,j:r,color:o.foreColor,parent:d,fontSize:n,dataLabelsConfig:l})}return d}},{key:\"addListeners\",value:function(t){var e=new m(this.ctx);t.node.addEventListener(\"mouseenter\",e.pathMouseEnter.bind(this,t)),t.node.addEventListener(\"mouseleave\",e.pathMouseLeave.bind(this,t)),t.node.addEventListener(\"mousedown\",e.pathMouseDown.bind(this,t))}}]),t}(),St=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w,this.xRatio=i.xRatio,this.yRatio=i.yRatio,this.dynamicAnim=this.w.config.chart.animations.dynamicAnimation,this.helpers=new At(e),this.rectRadius=this.w.config.plotOptions.heatmap.radius,this.strokeWidth=this.w.config.stroke.show?this.w.config.stroke.width:0}return r(t,[{key:\"draw\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-heatmap\"});a.attr(\"clip-path\",\"url(#gridRectMask\".concat(e.globals.cuid,\")\"));var s=e.globals.gridWidth/e.globals.dataPoints,r=e.globals.gridHeight/e.globals.series.length,o=0,n=!1;this.negRange=this.helpers.checkColorRange();var l=t.slice();e.config.yaxis[0].reversed&&(n=!0,l.reverse());for(var h=n?0:l.length-1;n?h<l.length:h>=0;n?h++:h--){var c=i.group({class:\"apexcharts-series apexcharts-heatmap-series\",seriesName:x.escapeString(e.globals.seriesNames[h]),rel:h+1,\"data:realIndex\":h});if(this.ctx.series.addCollapsedClassToSeries(c,h),e.config.chart.dropShadow.enabled){var d=e.config.chart.dropShadow;new v(this.ctx).dropShadow(c,d,h)}for(var g=0,u=e.config.plotOptions.heatmap.shadeIntensity,f=0;f<l[h].length;f++){var p=this.helpers.getShadeColor(e.config.chart.type,h,f,this.negRange),b=p.color,y=p.colorProps;if(\"image\"===e.config.fill.type)b=new R(this.ctx).fillPath({seriesNumber:h,dataPointIndex:f,opacity:e.globals.hasNegs?y.percent<0?1-(1+y.percent/100):u+y.percent/100:y.percent/100,patternID:x.randomId(),width:e.config.fill.image.width?e.config.fill.image.width:s,height:e.config.fill.image.height?e.config.fill.image.height:r});var w=this.rectRadius,k=i.drawRect(g,o,s,r,w);if(k.attr({cx:g,cy:o}),k.node.classList.add(\"apexcharts-heatmap-rect\"),c.add(k),k.attr({fill:b,i:h,index:h,j:f,val:l[h][f],\"stroke-width\":this.strokeWidth,stroke:e.config.plotOptions.heatmap.useFillColorAsStroke?b:e.globals.stroke.colors[0],color:b}),this.helpers.addListeners(k),e.config.chart.animations.enabled&&!e.globals.dataChanged){var A=1;e.globals.resized||(A=e.config.chart.animations.speed),this.animateHeatMap(k,g,o,s,r,A)}if(e.globals.dataChanged){var S=1;if(this.dynamicAnim.enabled&&e.globals.shouldAnimate){S=this.dynamicAnim.speed;var C=e.globals.previousPaths[h]&&e.globals.previousPaths[h][f]&&e.globals.previousPaths[h][f].color;C||(C=\"rgba(255, 255, 255, 0)\"),this.animateHeatColor(k,x.isColorHex(C)?C:x.rgb2hex(C),x.isColorHex(b)?b:x.rgb2hex(b),S)}}var L=(0,e.config.dataLabels.formatter)(e.globals.series[h][f],{value:e.globals.series[h][f],seriesIndex:h,dataPointIndex:f,w:e}),P=this.helpers.calculateDataLabels({text:L,x:g+s/2,y:o+r/2,i:h,j:f,colorProps:y,series:l});null!==P&&c.add(P),g+=s}o+=r,a.add(c)}var T=e.globals.yAxisScale[0].result.slice();e.config.yaxis[0].reversed?T.unshift(\"\"):T.push(\"\"),e.globals.yAxisScale[0].result=T;var M=e.globals.gridHeight/e.globals.series.length;return e.config.yaxis[0].labels.offsetY=-M/2,a}},{key:\"animateHeatMap\",value:function(t,e,i,a,s,r){var o=new b(this.ctx);o.animateRect(t,{x:e+a/2,y:i+s/2,width:0,height:0},{x:e,y:i,width:a,height:s},r,(function(){o.animationCompleted(t)}))}},{key:\"animateHeatColor\",value:function(t,e,i,a){t.attr({fill:e}).animate(a).attr({fill:i})}}]),t}(),Ct=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"drawYAxisTexts\",value:function(t,e,i,a){var s=this.w,r=s.config.yaxis[0],o=s.globals.yLabelFormatters[0];return new m(this.ctx).drawText({x:t+r.labels.offsetX,y:e+r.labels.offsetY,text:o(a,i),textAnchor:\"middle\",fontSize:r.labels.style.fontSize,fontFamily:r.labels.style.fontFamily,foreColor:Array.isArray(r.labels.style.colors)?r.labels.style.colors[i]:r.labels.style.colors})}}]),t}(),Lt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w;var i=this.w;this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animBeginArr=[0],this.animDur=0,this.donutDataLabels=this.w.config.plotOptions.pie.donut.labels,this.lineColorArr=void 0!==i.globals.stroke.colors?i.globals.stroke.colors:i.globals.colors,this.defaultSize=Math.min(i.globals.gridWidth,i.globals.gridHeight),this.centerY=this.defaultSize/2,this.centerX=i.globals.gridWidth/2,\"radialBar\"===i.config.chart.type?this.fullAngle=360:this.fullAngle=Math.abs(i.config.plotOptions.pie.endAngle-i.config.plotOptions.pie.startAngle),this.initialAngle=i.config.plotOptions.pie.startAngle%this.fullAngle,i.globals.radialSize=this.defaultSize/2.05-i.config.stroke.width-(i.config.chart.sparkline.enabled?0:i.config.chart.dropShadow.blur),this.donutSize=i.globals.radialSize*parseInt(i.config.plotOptions.pie.donut.size,10)/100,this.maxY=0,this.sliceLabels=[],this.sliceSizes=[],this.prevSectorAngleArr=[]}return r(t,[{key:\"draw\",value:function(t){var e=this,i=this.w,a=new m(this.ctx);if(this.ret=a.group({class:\"apexcharts-pie\"}),i.globals.noData)return this.ret;for(var s=0,r=0;r<t.length;r++)s+=x.negToZero(t[r]);var o=[],n=a.group();0===s&&(s=1e-5),t.forEach((function(t){e.maxY=Math.max(e.maxY,t)})),i.config.yaxis[0].max&&(this.maxY=i.config.yaxis[0].max),\"back\"===i.config.grid.position&&\"polarArea\"===this.chartType&&this.drawPolarElements(this.ret);for(var l=0;l<t.length;l++){var h=this.fullAngle*x.negToZero(t[l])/s;o.push(h),\"polarArea\"===this.chartType?(o[l]=this.fullAngle/t.length,this.sliceSizes.push(i.globals.radialSize*t[l]/this.maxY)):this.sliceSizes.push(i.globals.radialSize)}if(i.globals.dataChanged){for(var c,d=0,g=0;g<i.globals.previousPaths.length;g++)d+=x.negToZero(i.globals.previousPaths[g]);for(var u=0;u<i.globals.previousPaths.length;u++)c=this.fullAngle*x.negToZero(i.globals.previousPaths[u])/d,this.prevSectorAngleArr.push(c)}this.donutSize<0&&(this.donutSize=0);var f=i.config.plotOptions.pie.customScale,p=i.globals.gridWidth/2,b=i.globals.gridHeight/2,v=p-i.globals.gridWidth/2*f,y=b-i.globals.gridHeight/2*f;if(\"donut\"===this.chartType){var w=a.drawCircle(this.donutSize);w.attr({cx:this.centerX,cy:this.centerY,fill:i.config.plotOptions.pie.donut.background?i.config.plotOptions.pie.donut.background:\"transparent\"}),n.add(w)}var k=this.drawArcs(o,t);if(this.sliceLabels.forEach((function(t){k.add(t)})),n.attr({transform:\"translate(\".concat(v,\", \").concat(y,\") scale(\").concat(f,\")\")}),n.add(k),this.ret.add(n),this.donutDataLabels.show){var A=this.renderInnerDataLabels(this.donutDataLabels,{hollowSize:this.donutSize,centerX:this.centerX,centerY:this.centerY,opacity:this.donutDataLabels.show,translateX:v,translateY:y});this.ret.add(A)}return\"front\"===i.config.grid.position&&\"polarArea\"===this.chartType&&this.drawPolarElements(this.ret),this.ret}},{key:\"drawArcs\",value:function(t,e){var i=this.w,a=new v(this.ctx),s=new m(this.ctx),r=new R(this.ctx),o=s.group({class:\"apexcharts-slices\"}),n=this.initialAngle,l=this.initialAngle,h=this.initialAngle,c=this.initialAngle;this.strokeWidth=i.config.stroke.show?i.config.stroke.width:0;for(var d=0;d<t.length;d++){var g=s.group({class:\"apexcharts-series apexcharts-pie-series\",seriesName:x.escapeString(i.globals.seriesNames[d]),rel:d+1,\"data:realIndex\":d});o.add(g),l=c,h=(n=h)+t[d],c=l+this.prevSectorAngleArr[d];var u=h<n?this.fullAngle+h-n:h-n,f=r.fillPath({seriesNumber:d,size:this.sliceSizes[d],value:e[d]}),p=this.getChangedPath(l,c),b=s.drawPath({d:p,stroke:Array.isArray(this.lineColorArr)?this.lineColorArr[d]:this.lineColorArr,strokeWidth:0,fill:f,fillOpacity:i.config.fill.opacity,classes:\"apexcharts-pie-area apexcharts-\".concat(this.chartType.toLowerCase(),\"-slice-\").concat(d)});if(b.attr({index:0,j:d}),a.setSelectionFilter(b,0,d),i.config.chart.dropShadow.enabled){var y=i.config.chart.dropShadow;a.dropShadow(b,y,d)}this.addListeners(b,this.donutDataLabels),m.setAttrs(b.node,{\"data:angle\":u,\"data:startAngle\":n,\"data:strokeWidth\":this.strokeWidth,\"data:value\":e[d]});var w={x:0,y:0};\"pie\"===this.chartType||\"polarArea\"===this.chartType?w=x.polarToCartesian(this.centerX,this.centerY,i.globals.radialSize/1.25+i.config.plotOptions.pie.dataLabels.offset,(n+u/2)%this.fullAngle):\"donut\"===this.chartType&&(w=x.polarToCartesian(this.centerX,this.centerY,(i.globals.radialSize+this.donutSize)/2+i.config.plotOptions.pie.dataLabels.offset,(n+u/2)%this.fullAngle)),g.add(b);var k=0;if(!this.initialAnim||i.globals.resized||i.globals.dataChanged?this.animBeginArr.push(0):(0===(k=u/this.fullAngle*i.config.chart.animations.speed)&&(k=1),this.animDur=k+this.animDur,this.animBeginArr.push(this.animDur)),this.dynamicAnim&&i.globals.dataChanged?this.animatePaths(b,{size:this.sliceSizes[d],endAngle:h,startAngle:n,prevStartAngle:l,prevEndAngle:c,animateStartingPos:!0,i:d,animBeginArr:this.animBeginArr,shouldSetPrevPaths:!0,dur:i.config.chart.animations.dynamicAnimation.speed}):this.animatePaths(b,{size:this.sliceSizes[d],endAngle:h,startAngle:n,i:d,totalItems:t.length-1,animBeginArr:this.animBeginArr,dur:k}),i.config.plotOptions.pie.expandOnClick&&\"polarArea\"!==this.chartType&&b.click(this.pieClicked.bind(this,d)),void 0!==i.globals.selectedDataPoints[0]&&i.globals.selectedDataPoints[0].indexOf(d)>-1&&this.pieClicked(d),i.config.dataLabels.enabled){var A=w.x,S=w.y,C=100*u/this.fullAngle+\"%\";if(0!==u&&i.config.plotOptions.pie.dataLabels.minAngleToShowLabel<t[d]){var L=i.config.dataLabels.formatter;void 0!==L&&(C=L(i.globals.seriesPercent[d][0],{seriesIndex:d,w:i}));var P=i.globals.dataLabels.style.colors[d],T=s.group({class:\"apexcharts-datalabels\"}),M=s.drawText({x:A,y:S,text:C,textAnchor:\"middle\",fontSize:i.config.dataLabels.style.fontSize,fontFamily:i.config.dataLabels.style.fontFamily,fontWeight:i.config.dataLabels.style.fontWeight,foreColor:P});if(T.add(M),i.config.dataLabels.dropShadow.enabled){var I=i.config.dataLabels.dropShadow;a.dropShadow(M,I)}M.node.classList.add(\"apexcharts-pie-label\"),i.config.chart.animations.animate&&!1===i.globals.resized&&(M.node.classList.add(\"apexcharts-pie-label-delay\"),M.node.style.animationDelay=i.config.chart.animations.speed/940+\"s\"),this.sliceLabels.push(T)}}}return o}},{key:\"addListeners\",value:function(t,e){var i=new m(this.ctx);t.node.addEventListener(\"mouseenter\",i.pathMouseEnter.bind(this,t)),t.node.addEventListener(\"mouseleave\",i.pathMouseLeave.bind(this,t)),t.node.addEventListener(\"mouseleave\",this.revertDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener(\"mousedown\",i.pathMouseDown.bind(this,t)),this.donutDataLabels.total.showAlways||(t.node.addEventListener(\"mouseenter\",this.printDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener(\"mousedown\",this.printDataLabelsInner.bind(this,t.node,e)))}},{key:\"animatePaths\",value:function(t,e){var i=this.w,a=e.endAngle<e.startAngle?this.fullAngle+e.endAngle-e.startAngle:e.endAngle-e.startAngle,s=a,r=e.startAngle,o=e.startAngle;void 0!==e.prevStartAngle&&void 0!==e.prevEndAngle&&(r=e.prevEndAngle,s=e.prevEndAngle<e.prevStartAngle?this.fullAngle+e.prevEndAngle-e.prevStartAngle:e.prevEndAngle-e.prevStartAngle),e.i===i.config.series.length-1&&(a+o>this.fullAngle?e.endAngle=e.endAngle-(a+o):a+o<this.fullAngle&&(e.endAngle=e.endAngle+(this.fullAngle-(a+o)))),a===this.fullAngle&&(a=this.fullAngle-.01),this.animateArc(t,r,o,a,s,e)}},{key:\"animateArc\",value:function(t,e,i,a,s,r){var o,n=this,l=this.w,h=new b(this.ctx),c=r.size;(isNaN(e)||isNaN(s))&&(e=i,s=a,r.dur=0);var d=a,g=i,u=e<i?this.fullAngle+e-i:e-i;l.globals.dataChanged&&r.shouldSetPrevPaths&&r.prevEndAngle&&(o=n.getPiePath({me:n,startAngle:r.prevStartAngle,angle:r.prevEndAngle<r.prevStartAngle?this.fullAngle+r.prevEndAngle-r.prevStartAngle:r.prevEndAngle-r.prevStartAngle,size:c}),t.attr({d:o})),0!==r.dur?t.animate(r.dur,l.globals.easing,r.animBeginArr[r.i]).afterAll((function(){\"pie\"!==n.chartType&&\"donut\"!==n.chartType&&\"polarArea\"!==n.chartType||this.animate(l.config.chart.animations.dynamicAnimation.speed).attr({\"stroke-width\":n.strokeWidth}),r.i===l.config.series.length-1&&h.animationCompleted(t)})).during((function(l){d=u+(a-u)*l,r.animateStartingPos&&(d=s+(a-s)*l,g=e-s+(i-(e-s))*l),o=n.getPiePath({me:n,startAngle:g,angle:d,size:c}),t.node.setAttribute(\"data:pathOrig\",o),t.attr({d:o})})):(o=n.getPiePath({me:n,startAngle:g,angle:a,size:c}),r.isTrack||(l.globals.animationEnded=!0),t.node.setAttribute(\"data:pathOrig\",o),t.attr({d:o,\"stroke-width\":n.strokeWidth}))}},{key:\"pieClicked\",value:function(t){var e,i=this.w,a=this,s=a.sliceSizes[t]+(i.config.plotOptions.pie.expandOnClick?4:0),r=i.globals.dom.Paper.select(\".apexcharts-\".concat(a.chartType.toLowerCase(),\"-slice-\").concat(t)).members[0];if(\"true\"!==r.attr(\"data:pieClicked\")){var o=i.globals.dom.baseEl.getElementsByClassName(\"apexcharts-pie-area\");Array.prototype.forEach.call(o,(function(t){t.setAttribute(\"data:pieClicked\",\"false\");var e=t.getAttribute(\"data:pathOrig\");t.setAttribute(\"d\",e)})),r.attr(\"data:pieClicked\",\"true\");var n=parseInt(r.attr(\"data:startAngle\"),10),l=parseInt(r.attr(\"data:angle\"),10);e=a.getPiePath({me:a,startAngle:n,angle:l,size:s}),360!==l&&r.plot(e)}else{r.attr({\"data:pieClicked\":\"false\"}),this.revertDataLabelsInner(r.node,this.donutDataLabels);var h=r.attr(\"data:pathOrig\");r.attr({d:h})}}},{key:\"getChangedPath\",value:function(t,e){var i=\"\";return this.dynamicAnim&&this.w.globals.dataChanged&&(i=this.getPiePath({me:this,startAngle:t,angle:e-t,size:this.size})),i}},{key:\"getPiePath\",value:function(t){var e=t.me,i=t.startAngle,a=t.angle,s=t.size,r=i,o=Math.PI*(r-90)/180,n=a+i;Math.ceil(n)>=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle&&(n=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle-.01),Math.ceil(n)>this.fullAngle&&(n-=this.fullAngle);var l=Math.PI*(n-90)/180,h=e.centerX+s*Math.cos(o),c=e.centerY+s*Math.sin(o),d=e.centerX+s*Math.cos(l),g=e.centerY+s*Math.sin(l),u=x.polarToCartesian(e.centerX,e.centerY,e.donutSize,n),f=x.polarToCartesian(e.centerX,e.centerY,e.donutSize,r),p=a>180?1:0,b=[\"M\",h,c,\"A\",s,s,0,p,1,d,g];return\"donut\"===e.chartType?[].concat(b,[\"L\",u.x,u.y,\"A\",e.donutSize,e.donutSize,0,p,0,f.x,f.y,\"L\",h,c,\"z\"]).join(\" \"):\"pie\"===e.chartType||\"polarArea\"===e.chartType?[].concat(b,[\"L\",e.centerX,e.centerY,\"L\",h,c]).join(\" \"):[].concat(b).join(\" \")}},{key:\"drawPolarElements\",value:function(t){var e=this.w,i=new _(this.ctx),a=new m(this.ctx),s=new Ct(this.ctx),r=a.group(),o=a.group(),n=i.niceScale(0,Math.ceil(this.maxY),e.config.yaxis[0].tickAmount,0,!0),l=n.result.reverse(),h=n.result.length;this.maxY=n.niceMax;for(var c=e.globals.radialSize,d=c/(h-1),g=0;g<h-1;g++){var u=a.drawCircle(c);if(u.attr({cx:this.centerX,cy:this.centerY,fill:\"none\",\"stroke-width\":e.config.plotOptions.polarArea.rings.strokeWidth,stroke:e.config.plotOptions.polarArea.rings.strokeColor}),e.config.yaxis[0].show){var f=s.drawYAxisTexts(this.centerX,this.centerY-c+parseInt(e.config.yaxis[0].labels.style.fontSize,10)/2,g,l[g]);o.add(f)}r.add(u),c-=d}this.drawSpokes(t),t.add(r),t.add(o)}},{key:\"renderInnerDataLabels\",value:function(t,e){var i=this.w,a=new m(this.ctx),s=a.group({class:\"apexcharts-datalabels-group\",transform:\"translate(\".concat(e.translateX?e.translateX:0,\", \").concat(e.translateY?e.translateY:0,\") scale(\").concat(i.config.plotOptions.pie.customScale,\")\")}),r=t.total.show;s.node.style.opacity=e.opacity;var o,n,l=e.centerX,h=e.centerY;o=void 0===t.name.color?i.globals.colors[0]:t.name.color;var c=t.name.fontSize,d=t.name.fontFamily,g=t.name.fontWeight;n=void 0===t.value.color?i.config.chart.foreColor:t.value.color;var u=t.value.formatter,f=\"\",p=\"\";if(r?(o=t.total.color,c=t.total.fontSize,d=t.total.fontFamily,g=t.total.fontWeight,p=t.total.label,f=t.total.formatter(i)):1===i.globals.series.length&&(f=u(i.globals.series[0],i),p=i.globals.seriesNames[0]),p&&(p=t.name.formatter(p,t.total.show,i)),t.name.show){var x=a.drawText({x:l,y:h+parseFloat(t.name.offsetY),text:p,textAnchor:\"middle\",foreColor:o,fontSize:c,fontWeight:g,fontFamily:d});x.node.classList.add(\"apexcharts-datalabel-label\"),s.add(x)}if(t.value.show){var b=t.name.show?parseFloat(t.value.offsetY)+16:t.value.offsetY,v=a.drawText({x:l,y:h+b,text:f,textAnchor:\"middle\",foreColor:n,fontWeight:t.value.fontWeight,fontSize:t.value.fontSize,fontFamily:t.value.fontFamily});v.node.classList.add(\"apexcharts-datalabel-value\"),s.add(v)}return s}},{key:\"printInnerLabels\",value:function(t,e,i,a){var s,r=this.w;a?s=void 0===t.name.color?r.globals.colors[parseInt(a.parentNode.getAttribute(\"rel\"),10)-1]:t.name.color:r.globals.series.length>1&&t.total.show&&(s=t.total.color);var o=r.globals.dom.baseEl.querySelector(\".apexcharts-datalabel-label\"),n=r.globals.dom.baseEl.querySelector(\".apexcharts-datalabel-value\");i=(0,t.value.formatter)(i,r),a||\"function\"!=typeof t.total.formatter||(i=t.total.formatter(r));var l=e===t.total.label;e=t.name.formatter(e,l,r),null!==o&&(o.textContent=e),null!==n&&(n.textContent=i),null!==o&&(o.style.fill=s)}},{key:\"printDataLabelsInner\",value:function(t,e){var i=this.w,a=t.getAttribute(\"data:value\"),s=i.globals.seriesNames[parseInt(t.parentNode.getAttribute(\"rel\"),10)-1];i.globals.series.length>1&&this.printInnerLabels(e,s,a,t);var r=i.globals.dom.baseEl.querySelector(\".apexcharts-datalabels-group\");null!==r&&(r.style.opacity=1)}},{key:\"drawSpokes\",value:function(t){var e=this,i=this.w,a=new m(this.ctx),s=i.config.plotOptions.polarArea.spokes;if(0!==s.strokeWidth){for(var r=[],o=360/i.globals.series.length,n=0;n<i.globals.series.length;n++)r.push(x.polarToCartesian(this.centerX,this.centerY,i.globals.radialSize,i.config.plotOptions.pie.startAngle+o*n));r.forEach((function(i,r){var o=a.drawLine(i.x,i.y,e.centerX,e.centerY,Array.isArray(s.connectorColors)?s.connectorColors[r]:s.connectorColors);t.add(o)}))}}},{key:\"revertDataLabelsInner\",value:function(t,e,i){var a=this,s=this.w,r=s.globals.dom.baseEl.querySelector(\".apexcharts-datalabels-group\"),o=!1,n=s.globals.dom.baseEl.getElementsByClassName(\"apexcharts-pie-area\"),l=function(t){var i=t.makeSliceOut,s=t.printLabel;Array.prototype.forEach.call(n,(function(t){\"true\"===t.getAttribute(\"data:pieClicked\")&&(i&&(o=!0),s&&a.printDataLabelsInner(t,e))}))};if(l({makeSliceOut:!0,printLabel:!1}),e.total.show&&s.globals.series.length>1)o&&!e.total.showAlways?l({makeSliceOut:!1,printLabel:!0}):this.printInnerLabels(e,e.total.label,e.total.formatter(s));else if(l({makeSliceOut:!1,printLabel:!0}),!o)if(s.globals.selectedDataPoints.length&&s.globals.series.length>1)if(s.globals.selectedDataPoints[0].length>0){var h=s.globals.selectedDataPoints[0],c=s.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(this.chartType.toLowerCase(),\"-slice-\").concat(h));this.printDataLabelsInner(c,e)}else r&&s.globals.selectedDataPoints.length&&0===s.globals.selectedDataPoints[0].length&&(r.style.opacity=0);else r&&s.globals.series.length>1&&(r.style.opacity=0)}}]),t}(),Pt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animDur=0;var i=this.w;this.graphics=new m(this.ctx),this.lineColorArr=void 0!==i.globals.stroke.colors?i.globals.stroke.colors:i.globals.colors,this.defaultSize=i.globals.svgHeight<i.globals.svgWidth?i.globals.gridHeight+1.5*i.globals.goldenPadding:i.globals.gridWidth,this.isLog=i.config.yaxis[0].logarithmic,this.coreUtils=new y(this.ctx),this.maxValue=this.isLog?this.coreUtils.getLogVal(i.globals.maxY,0):i.globals.maxY,this.minValue=this.isLog?this.coreUtils.getLogVal(this.w.globals.minY,0):i.globals.minY,this.polygons=i.config.plotOptions.radar.polygons,this.strokeWidth=i.config.stroke.show?i.config.stroke.width:0,this.size=this.defaultSize/2.1-this.strokeWidth-i.config.chart.dropShadow.blur,i.config.xaxis.labels.show&&(this.size=this.size-i.globals.xAxisLabelsWidth/1.75),void 0!==i.config.plotOptions.radar.size&&(this.size=i.config.plotOptions.radar.size),this.dataRadiusOfPercent=[],this.dataRadius=[],this.angleArr=[],this.yaxisLabelsTextsPos=[]}return r(t,[{key:\"draw\",value:function(t){var i=this,a=this.w,s=new R(this.ctx),r=[],o=new O(this.ctx);t.length&&(this.dataPointsLen=t[a.globals.maxValsInArrayIndex].length),this.disAngle=2*Math.PI/this.dataPointsLen;var n=a.globals.gridWidth/2,l=a.globals.gridHeight/2,h=n+a.config.plotOptions.radar.offsetX,c=l+a.config.plotOptions.radar.offsetY,d=this.graphics.group({class:\"apexcharts-radar-series apexcharts-plot-series\",transform:\"translate(\".concat(h||0,\", \").concat(c||0,\")\")}),g=[],u=null,f=null;if(this.yaxisLabels=this.graphics.group({class:\"apexcharts-yaxis\"}),t.forEach((function(t,n){var l=t.length===a.globals.dataPoints,h=i.graphics.group().attr({class:\"apexcharts-series\",\"data:longestSeries\":l,seriesName:x.escapeString(a.globals.seriesNames[n]),rel:n+1,\"data:realIndex\":n});i.dataRadiusOfPercent[n]=[],i.dataRadius[n]=[],i.angleArr[n]=[],t.forEach((function(t,e){var a=Math.abs(i.maxValue-i.minValue);t+=Math.abs(i.minValue),i.isLog&&(t=i.coreUtils.getLogVal(t,0)),i.dataRadiusOfPercent[n][e]=t/a,i.dataRadius[n][e]=i.dataRadiusOfPercent[n][e]*i.size,i.angleArr[n][e]=e*i.disAngle})),g=i.getDataPointsPos(i.dataRadius[n],i.angleArr[n]);var c=i.createPaths(g,{x:0,y:0});u=i.graphics.group({class:\"apexcharts-series-markers-wrap apexcharts-element-hidden\"}),f=i.graphics.group({class:\"apexcharts-datalabels\",\"data:realIndex\":n}),a.globals.delayedElements.push({el:u.node,index:n});var d={i:n,realIndex:n,animationDelay:n,initialSpeed:a.config.chart.animations.speed,dataChangeSpeed:a.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-radar\",shouldClipToGrid:!1,bindEventsOnPaths:!1,stroke:a.globals.stroke.colors[n],strokeLineCap:a.config.stroke.lineCap},p=null;a.globals.previousPaths.length>0&&(p=i.getPreviousPath(n));for(var b=0;b<c.linePathsTo.length;b++){var m=i.graphics.renderPaths(e(e({},d),{},{pathFrom:null===p?c.linePathsFrom[b]:p,pathTo:c.linePathsTo[b],strokeWidth:Array.isArray(i.strokeWidth)?i.strokeWidth[n]:i.strokeWidth,fill:\"none\",drawShadow:!1}));h.add(m);var y=s.fillPath({seriesNumber:n}),w=i.graphics.renderPaths(e(e({},d),{},{pathFrom:null===p?c.areaPathsFrom[b]:p,pathTo:c.areaPathsTo[b],strokeWidth:0,fill:y,drawShadow:!1}));if(a.config.chart.dropShadow.enabled){var k=new v(i.ctx),A=a.config.chart.dropShadow;k.dropShadow(w,Object.assign({},A,{noUserSpaceOnUse:!0}),n)}h.add(w)}t.forEach((function(t,s){var r=new D(i.ctx).getMarkerConfig({cssClass:\"apexcharts-marker\",seriesIndex:n,dataPointIndex:s}),l=i.graphics.drawMarker(g[s].x,g[s].y,r);l.attr(\"rel\",s),l.attr(\"j\",s),l.attr(\"index\",n),l.node.setAttribute(\"default-marker-size\",r.pSize);var c=i.graphics.group({class:\"apexcharts-series-markers\"});c&&c.add(l),u.add(c),h.add(u);var d=a.config.dataLabels;if(d.enabled){var p=d.formatter(a.globals.series[n][s],{seriesIndex:n,dataPointIndex:s,w:a});o.plotDataLabelsText({x:g[s].x,y:g[s].y,text:p,textAnchor:\"middle\",i:n,j:n,parent:f,offsetCorrection:!1,dataLabelsConfig:e({},d)})}h.add(f)})),r.push(h)})),this.drawPolygons({parent:d}),a.config.xaxis.labels.show){var p=this.drawXAxisTexts();d.add(p)}return r.forEach((function(t){d.add(t)})),d.add(this.yaxisLabels),d}},{key:\"drawPolygons\",value:function(t){for(var e=this,i=this.w,a=t.parent,s=new Ct(this.ctx),r=i.globals.yAxisScale[0].result.reverse(),o=r.length,n=[],l=this.size/(o-1),h=0;h<o;h++)n[h]=l*h;n.reverse();var c=[],d=[];n.forEach((function(t,i){var a=x.getPolygonPos(t,e.dataPointsLen),s=\"\";a.forEach((function(t,a){if(0===i){var r=e.graphics.drawLine(t.x,t.y,0,0,Array.isArray(e.polygons.connectorColors)?e.polygons.connectorColors[a]:e.polygons.connectorColors);d.push(r)}0===a&&e.yaxisLabelsTextsPos.push({x:t.x,y:t.y}),s+=t.x+\",\"+t.y+\" \"})),c.push(s)})),c.forEach((function(t,s){var r=e.polygons.strokeColors,o=e.polygons.strokeWidth,n=e.graphics.drawPolygon(t,Array.isArray(r)?r[s]:r,Array.isArray(o)?o[s]:o,i.globals.radarPolygons.fill.colors[s]);a.add(n)})),d.forEach((function(t){a.add(t)})),i.config.yaxis[0].show&&this.yaxisLabelsTextsPos.forEach((function(t,i){var a=s.drawYAxisTexts(t.x,t.y,i,r[i]);e.yaxisLabels.add(a)}))}},{key:\"drawXAxisTexts\",value:function(){var t=this,i=this.w,a=i.config.xaxis.labels,s=this.graphics.group({class:\"apexcharts-xaxis\"}),r=x.getPolygonPos(this.size,this.dataPointsLen);return i.globals.labels.forEach((function(o,n){var l=i.config.xaxis.labels.formatter,h=new O(t.ctx);if(r[n]){var c=t.getTextPos(r[n],t.size),d=l(o,{seriesIndex:-1,dataPointIndex:n,w:i});h.plotDataLabelsText({x:c.newX,y:c.newY,text:d,textAnchor:c.textAnchor,i:n,j:n,parent:s,color:Array.isArray(a.style.colors)&&a.style.colors[n]?a.style.colors[n]:\"#a8a8a8\",dataLabelsConfig:e({textAnchor:c.textAnchor,dropShadow:{enabled:!1}},a),offsetCorrection:!1})}})),s}},{key:\"createPaths\",value:function(t,e){var i=this,a=[],s=[],r=[],o=[];if(t.length){s=[this.graphics.move(e.x,e.y)],o=[this.graphics.move(e.x,e.y)];var n=this.graphics.move(t[0].x,t[0].y),l=this.graphics.move(t[0].x,t[0].y);t.forEach((function(e,a){n+=i.graphics.line(e.x,e.y),l+=i.graphics.line(e.x,e.y),a===t.length-1&&(n+=\"Z\",l+=\"Z\")})),a.push(n),r.push(l)}return{linePathsFrom:s,linePathsTo:a,areaPathsFrom:o,areaPathsTo:r}}},{key:\"getTextPos\",value:function(t,e){var i=\"middle\",a=t.x,s=t.y;return Math.abs(t.x)>=10?t.x>0?(i=\"start\",a+=10):t.x<0&&(i=\"end\",a-=10):i=\"middle\",Math.abs(t.y)>=e-10&&(t.y<0?s-=10:t.y>0&&(s+=10)),{textAnchor:i,newX:a,newY:s}}},{key:\"getPreviousPath\",value:function(t){for(var e=this.w,i=null,a=0;a<e.globals.previousPaths.length;a++){var s=e.globals.previousPaths[a];s.paths.length>0&&parseInt(s.realIndex,10)===parseInt(t,10)&&void 0!==e.globals.previousPaths[a].paths[0]&&(i=e.globals.previousPaths[a].paths[0].d)}return i}},{key:\"getDataPointsPos\",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.dataPointsLen;t=t||[],e=e||[];for(var a=[],s=0;s<i;s++){var r={};r.x=t[s]*Math.sin(e[s]),r.y=-t[s]*Math.cos(e[s]),a.push(r)}return a}}]),t}(),Tt=function(t){n(i,Lt);var e=d(i);function i(t){var s;a(this,i),(s=e.call(this,t)).ctx=t,s.w=t.w,s.animBeginArr=[0],s.animDur=0;var r=s.w;return s.startAngle=r.config.plotOptions.radialBar.startAngle,s.endAngle=r.config.plotOptions.radialBar.endAngle,s.totalAngle=Math.abs(r.config.plotOptions.radialBar.endAngle-r.config.plotOptions.radialBar.startAngle),s.trackStartAngle=r.config.plotOptions.radialBar.track.startAngle,s.trackEndAngle=r.config.plotOptions.radialBar.track.endAngle,s.donutDataLabels=s.w.config.plotOptions.radialBar.dataLabels,s.radialDataLabels=s.donutDataLabels,s.trackStartAngle||(s.trackStartAngle=s.startAngle),s.trackEndAngle||(s.trackEndAngle=s.endAngle),360===s.endAngle&&(s.endAngle=359.99),s.margin=parseInt(r.config.plotOptions.radialBar.track.margin,10),s}return r(i,[{key:\"draw\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-radialbar\"});if(e.globals.noData)return a;var s=i.group(),r=this.defaultSize/2,o=e.globals.gridWidth/2,n=this.defaultSize/2.05;e.config.chart.sparkline.enabled||(n=n-e.config.stroke.width-e.config.chart.dropShadow.blur);var l=e.globals.fill.colors;if(e.config.plotOptions.radialBar.track.show){var h=this.drawTracks({size:n,centerX:o,centerY:r,colorArr:l,series:t});s.add(h)}var c=this.drawArcs({size:n,centerX:o,centerY:r,colorArr:l,series:t}),d=360;e.config.plotOptions.radialBar.startAngle<0&&(d=this.totalAngle);var g=(360-d)/360;if(e.globals.radialSize=n-n*g,this.radialDataLabels.value.show){var u=Math.max(this.radialDataLabels.value.offsetY,this.radialDataLabels.name.offsetY);e.globals.radialSize+=u*g}return s.add(c.g),\"front\"===e.config.plotOptions.radialBar.hollow.position&&(c.g.add(c.elHollow),c.dataLabels&&c.g.add(c.dataLabels)),a.add(s),a}},{key:\"drawTracks\",value:function(t){var e=this.w,i=new m(this.ctx),a=i.group({class:\"apexcharts-tracks\"}),s=new v(this.ctx),r=new R(this.ctx),o=this.getStrokeWidth(t);t.size=t.size-o/2;for(var n=0;n<t.series.length;n++){var l=i.group({class:\"apexcharts-radialbar-track apexcharts-track\"});a.add(l),l.attr({rel:n+1}),t.size=t.size-o-this.margin;var h=e.config.plotOptions.radialBar.track,c=r.fillPath({seriesNumber:0,size:t.size,fillColors:Array.isArray(h.background)?h.background[n]:h.background,solid:!0}),d=this.trackStartAngle,g=this.trackEndAngle;Math.abs(g)+Math.abs(d)>=360&&(g=360-Math.abs(this.startAngle)-.1);var u=i.drawPath({d:\"\",stroke:c,strokeWidth:o*parseInt(h.strokeWidth,10)/100,fill:\"none\",strokeOpacity:h.opacity,classes:\"apexcharts-radialbar-area\"});if(h.dropShadow.enabled){var f=h.dropShadow;s.dropShadow(u,f)}l.add(u),u.attr(\"id\",\"apexcharts-radialbarTrack-\"+n),this.animatePaths(u,{centerX:t.centerX,centerY:t.centerY,endAngle:g,startAngle:d,size:t.size,i:n,totalItems:2,animBeginArr:0,dur:0,isTrack:!0,easing:e.globals.easing})}return a}},{key:\"drawArcs\",value:function(t){var e=this.w,i=new m(this.ctx),a=new R(this.ctx),s=new v(this.ctx),r=i.group(),o=this.getStrokeWidth(t);t.size=t.size-o/2;var n=e.config.plotOptions.radialBar.hollow.background,l=t.size-o*t.series.length-this.margin*t.series.length-o*parseInt(e.config.plotOptions.radialBar.track.strokeWidth,10)/100/2,h=l-e.config.plotOptions.radialBar.hollow.margin;void 0!==e.config.plotOptions.radialBar.hollow.image&&(n=this.drawHollowImage(t,r,l,n));var c=this.drawHollow({size:h,centerX:t.centerX,centerY:t.centerY,fill:n||\"transparent\"});if(e.config.plotOptions.radialBar.hollow.dropShadow.enabled){var d=e.config.plotOptions.radialBar.hollow.dropShadow;s.dropShadow(c,d)}var g=1;!this.radialDataLabels.total.show&&e.globals.series.length>1&&(g=0);var u=null;this.radialDataLabels.show&&(u=this.renderInnerDataLabels(this.radialDataLabels,{hollowSize:l,centerX:t.centerX,centerY:t.centerY,opacity:g})),\"back\"===e.config.plotOptions.radialBar.hollow.position&&(r.add(c),u&&r.add(u));var f=!1;e.config.plotOptions.radialBar.inverseOrder&&(f=!0);for(var p=f?t.series.length-1:0;f?p>=0:p<t.series.length;f?p--:p++){var b=i.group({class:\"apexcharts-series apexcharts-radial-series\",seriesName:x.escapeString(e.globals.seriesNames[p])});r.add(b),b.attr({rel:p+1,\"data:realIndex\":p}),this.ctx.series.addCollapsedClassToSeries(b,p),t.size=t.size-o-this.margin;var y=a.fillPath({seriesNumber:p,size:t.size,value:t.series[p]}),w=this.startAngle,k=void 0,A=x.negToZero(t.series[p]>100?100:t.series[p])/100,S=Math.round(this.totalAngle*A)+this.startAngle,C=void 0;e.globals.dataChanged&&(k=this.startAngle,C=Math.round(this.totalAngle*x.negToZero(e.globals.previousPaths[p])/100)+k),Math.abs(S)+Math.abs(w)>=360&&(S-=.01),Math.abs(C)+Math.abs(k)>=360&&(C-=.01);var L=S-w,P=Array.isArray(e.config.stroke.dashArray)?e.config.stroke.dashArray[p]:e.config.stroke.dashArray,T=i.drawPath({d:\"\",stroke:y,strokeWidth:o,fill:\"none\",fillOpacity:e.config.fill.opacity,classes:\"apexcharts-radialbar-area apexcharts-radialbar-slice-\"+p,strokeDashArray:P});if(m.setAttrs(T.node,{\"data:angle\":L,\"data:value\":t.series[p]}),e.config.chart.dropShadow.enabled){var M=e.config.chart.dropShadow;s.dropShadow(T,M,p)}s.setSelectionFilter(T,0,p),this.addListeners(T,this.radialDataLabels),b.add(T),T.attr({index:0,j:p});var I=0;!this.initialAnim||e.globals.resized||e.globals.dataChanged||(I=e.config.chart.animations.speed),e.globals.dataChanged&&(I=e.config.chart.animations.dynamicAnimation.speed),this.animDur=I/(1.2*t.series.length)+this.animDur,this.animBeginArr.push(this.animDur),this.animatePaths(T,{centerX:t.centerX,centerY:t.centerY,endAngle:S,startAngle:w,prevEndAngle:C,prevStartAngle:k,size:t.size,i:p,totalItems:2,animBeginArr:this.animBeginArr,dur:I,shouldSetPrevPaths:!0,easing:e.globals.easing})}return{g:r,elHollow:c,dataLabels:u}}},{key:\"drawHollow\",value:function(t){var e=new m(this.ctx).drawCircle(2*t.size);return e.attr({class:\"apexcharts-radialbar-hollow\",cx:t.centerX,cy:t.centerY,r:t.size,fill:t.fill}),e}},{key:\"drawHollowImage\",value:function(t,e,i,a){var s=this.w,r=new R(this.ctx),o=x.randomId(),n=s.config.plotOptions.radialBar.hollow.image;if(s.config.plotOptions.radialBar.hollow.imageClipped)r.clippedImgArea({width:i,height:i,image:n,patternID:\"pattern\".concat(s.globals.cuid).concat(o)}),a=\"url(#pattern\".concat(s.globals.cuid).concat(o,\")\");else{var l=s.config.plotOptions.radialBar.hollow.imageWidth,h=s.config.plotOptions.radialBar.hollow.imageHeight;if(void 0===l&&void 0===h){var c=s.globals.dom.Paper.image(n).loaded((function(e){this.move(t.centerX-e.width/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-e.height/2+s.config.plotOptions.radialBar.hollow.imageOffsetY)}));e.add(c)}else{var d=s.globals.dom.Paper.image(n).loaded((function(e){this.move(t.centerX-l/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-h/2+s.config.plotOptions.radialBar.hollow.imageOffsetY),this.size(l,h)}));e.add(d)}}return a}},{key:\"getStrokeWidth\",value:function(t){var e=this.w;return t.size*(100-parseInt(e.config.plotOptions.radialBar.hollow.size,10))/100/(t.series.length+1)-this.margin}}]),i}(),Mt=function(t){n(s,yt);var i=d(s);function s(){return a(this,s),i.apply(this,arguments)}return r(s,[{key:\"draw\",value:function(t,i){var a=this.w,s=new m(this.ctx);this.rangeBarOptions=this.w.config.plotOptions.rangeBar,this.series=t,this.seriesRangeStart=a.globals.seriesRangeStart,this.seriesRangeEnd=a.globals.seriesRangeEnd,this.barHelpers.initVariables(t);for(var r=s.group({class:\"apexcharts-rangebar-series apexcharts-plot-series\"}),o=0;o<t.length;o++){var n,l,h,c=void 0,d=void 0,g=void 0,u=a.globals.comboCharts?i[o]:o,f=s.group({class:\"apexcharts-series\",seriesName:x.escapeString(a.globals.seriesNames[u]),rel:o+1,\"data:realIndex\":u});this.ctx.series.addCollapsedClassToSeries(f,u),t[o].length>0&&(this.visibleI=this.visibleI+1);var p=0,b=0;this.yRatio.length>1&&(this.yaxisIndex=u);var v=this.barHelpers.initialPositions();d=v.y,h=v.zeroW,c=v.x,b=v.barWidth,n=v.xDivision,l=v.zeroH;for(var y=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":u}),w=s.group({class:\"apexcharts-rangebar-goals-markers\",style:\"pointer-events: none\"}),k=0;k<a.globals.dataPoints;k++){var A=this.barHelpers.getStrokeWidth(o,k,u),S=this.seriesRangeStart[o][k],C=this.seriesRangeEnd[o][k],L=null,P=null,T={x:c,y:d,strokeWidth:A,elSeries:f};if(g=v.yDivision,p=v.barHeight,this.isHorizontal){P=d+p*this.visibleI;var M=this.seriesLen;a.config.plotOptions.bar.rangeBarGroupRows&&(M=1);var I=(g-p*M)/2;if(void 0===a.config.series[o].data[k])break;if(a.config.series[o].data[k].x){var z=this.detectOverlappingBars({i:o,j:k,barYPosition:P,srty:I,barHeight:p,yDivision:g,initPositions:v});p=z.barHeight,P=z.barYPosition}b=(L=this.drawRangeBarPaths(e({indexes:{i:o,j:k,realIndex:u},barHeight:p,barYPosition:P,zeroW:h,yDivision:g,y1:S,y2:C},T))).barWidth}else p=(L=this.drawRangeColumnPaths(e({indexes:{i:o,j:k,realIndex:u},zeroH:l,barWidth:b,xDivision:n},T))).barHeight;var X=this.barHelpers.drawGoalLine({barXPosition:L.barXPosition,barYPosition:P,goalX:L.goalX,goalY:L.goalY,barHeight:p,barWidth:b});X&&w.add(X),d=L.y,c=L.x;var E=this.barHelpers.getPathFillColor(t,o,k,u),Y=a.globals.stroke.colors[u];this.renderSeries({realIndex:u,pathFill:E,lineFill:Y,j:k,i:o,x:c,y:d,y1:S,y2:C,pathFrom:L.pathFrom,pathTo:L.pathTo,strokeWidth:A,elSeries:f,series:t,barHeight:p,barYPosition:P,barWidth:b,elDataLabelsWrap:y,elGoalsMarkers:w,visibleSeries:this.visibleI,type:\"rangebar\"})}r.add(f)}return r}},{key:\"detectOverlappingBars\",value:function(t){var e=t.i,i=t.j,a=t.barYPosition,s=t.srty,r=t.barHeight,o=t.yDivision,n=t.initPositions,l=this.w,h=[],c=l.config.series[e].data[i].rangeName,d=l.config.series[e].data[i].x,g=l.globals.labels.indexOf(d),u=l.globals.seriesRange[e].findIndex((function(t){return t.x===d&&t.overlaps.length>0}));return a=l.config.plotOptions.bar.rangeBarGroupRows?s+o*g:s+r*this.visibleI+o*g,u>-1&&!l.config.plotOptions.bar.rangeBarOverlap&&(h=l.globals.seriesRange[e][u].overlaps).indexOf(c)>-1&&(a=(r=n.barHeight/h.length)*this.visibleI+o*(100-parseInt(this.barOptions.barHeight,10))/100/2+r*(this.visibleI+h.indexOf(c))+o*g),{barYPosition:a,barHeight:r}}},{key:\"drawRangeColumnPaths\",value:function(t){var e=t.indexes,i=t.x;t.strokeWidth;var a=t.xDivision,s=t.barWidth,r=t.zeroH,o=this.w,n=e.i,l=e.j,h=this.yRatio[this.yaxisIndex],c=e.realIndex,d=this.getRangeValue(c,l),g=Math.min(d.start,d.end),u=Math.max(d.start,d.end);o.globals.isXNumeric&&(i=(o.globals.seriesX[n][l]-o.globals.minX)/this.xRatio-s/2);var f=i+s*this.visibleI;void 0===this.series[n][l]||null===this.series[n][l]?g=r:(g=r-g/h,u=r-u/h);var p=Math.abs(u-g),x=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:s,y1:g,y2:u,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,realIndex:e.realIndex,i:c,j:l,w:o});return o.globals.isXNumeric||(i+=a),{pathTo:x.pathTo,pathFrom:x.pathFrom,barHeight:p,x:i,y:u,goalY:this.barHelpers.getGoalValues(\"y\",null,r,n,l),barXPosition:f}}},{key:\"drawRangeBarPaths\",value:function(t){var e=t.indexes,i=t.y,a=t.y1,s=t.y2,r=t.yDivision,o=t.barHeight,n=t.barYPosition,l=t.zeroW,h=this.w,c=l+a/this.invertedYRatio,d=l+s/this.invertedYRatio,g=Math.abs(d-c),u=this.barHelpers.getBarpaths({barYPosition:n,barHeight:o,x1:c,x2:d,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,i:e.realIndex,realIndex:e.realIndex,j:e.j,w:h});return h.globals.isXNumeric||(i+=r),{pathTo:u.pathTo,pathFrom:u.pathFrom,barWidth:g,x:d,goalX:this.barHelpers.getGoalValues(\"x\",l,null,e.realIndex,e.j),y:i}}},{key:\"getRangeValue\",value:function(t,e){var i=this.w;return{start:i.globals.seriesRangeStart[t][e],end:i.globals.seriesRangeEnd[t][e]}}}]),s}(),It=function(){function t(e){a(this,t),this.w=e.w,this.lineCtx=e}return r(t,[{key:\"sameValueSeriesFix\",value:function(t,e){var i=this.w;if((\"gradient\"===i.config.fill.type||\"gradient\"===i.config.fill.type[t])&&new y(this.lineCtx.ctx,i).seriesHaveSameValues(t)){var a=e[t].slice();a[a.length-1]=a[a.length-1]+1e-6,e[t]=a}return e}},{key:\"calculatePoints\",value:function(t){var e=t.series,i=t.realIndex,a=t.x,s=t.y,r=t.i,o=t.j,n=t.prevY,l=this.w,h=[],c=[];if(0===o){var d=this.lineCtx.categoryAxisCorrection+l.config.markers.offsetX;l.globals.isXNumeric&&(d=(l.globals.seriesX[i][0]-l.globals.minX)/this.lineCtx.xRatio+l.config.markers.offsetX),h.push(d),c.push(x.isNumber(e[r][0])?n+l.config.markers.offsetY:null),h.push(a+l.config.markers.offsetX),c.push(x.isNumber(e[r][o+1])?s+l.config.markers.offsetY:null)}else h.push(a+l.config.markers.offsetX),c.push(x.isNumber(e[r][o+1])?s+l.config.markers.offsetY:null);return{x:h,y:c}}},{key:\"checkPreviousPaths\",value:function(t){for(var e=t.pathFromLine,i=t.pathFromArea,a=t.realIndex,s=this.w,r=0;r<s.globals.previousPaths.length;r++){var o=s.globals.previousPaths[r];(\"line\"===o.type||\"area\"===o.type)&&o.paths.length>0&&parseInt(o.realIndex,10)===parseInt(a,10)&&(\"line\"===o.type?(this.lineCtx.appendPathFrom=!1,e=s.globals.previousPaths[r].paths[0].d):\"area\"===o.type&&(this.lineCtx.appendPathFrom=!1,i=s.globals.previousPaths[r].paths[0].d,s.config.stroke.show&&s.globals.previousPaths[r].paths[1]&&(e=s.globals.previousPaths[r].paths[1].d)))}return{pathFromLine:e,pathFromArea:i}}},{key:\"determineFirstPrevY\",value:function(t){var e,i=t.i,a=t.series,s=t.prevY,r=t.lineYPosition,o=this.w;if(void 0!==(null===(e=a[i])||void 0===e?void 0:e[0]))s=(r=o.config.chart.stacked&&i>0?this.lineCtx.prevSeriesY[i-1][0]:this.lineCtx.zeroY)-a[i][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]+2*(this.lineCtx.isReversed?a[i][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]:0);else if(o.config.chart.stacked&&i>0&&void 0===a[i][0])for(var n=i-1;n>=0;n--)if(null!==a[n][0]&&void 0!==a[n][0]){s=r=this.lineCtx.prevSeriesY[n][0];break}return{prevY:s,lineYPosition:r}}}]),t}(),zt=function(){function t(e,i,s){a(this,t),this.ctx=e,this.w=e.w,this.xyRatios=i,this.pointsChart=!(\"bubble\"!==this.w.config.chart.type&&\"scatter\"!==this.w.config.chart.type)||s,this.scatter=new H(this.ctx),this.noNegatives=this.w.globals.minX===Number.MAX_VALUE,this.lineHelpers=new It(this),this.markers=new D(this.ctx),this.prevSeriesY=[],this.categoryAxisCorrection=0,this.yaxisIndex=0}return r(t,[{key:\"draw\",value:function(t,i,a,s){var r=this.w,o=new m(this.ctx),n=r.globals.comboCharts?i:r.config.chart.type,l=o.group({class:\"apexcharts-\".concat(n,\"-series apexcharts-plot-series\")}),h=new y(this.ctx,r);this.yRatio=this.xyRatios.yRatio,this.zRatio=this.xyRatios.zRatio,this.xRatio=this.xyRatios.xRatio,this.baseLineY=this.xyRatios.baseLineY,t=h.getLogSeries(t),this.yRatio=h.getLogYRatios(this.yRatio);for(var c=[],d=0;d<t.length;d++){t=this.lineHelpers.sameValueSeriesFix(d,t);var g=r.globals.comboCharts?a[d]:d;this._initSerieVariables(t,d,g);var u=[],f=[],p=r.globals.padHorizontal+this.categoryAxisCorrection;this.ctx.series.addCollapsedClassToSeries(this.elSeries,g),r.globals.isXNumeric&&r.globals.seriesX.length>0&&(p=(r.globals.seriesX[g][0]-r.globals.minX)/this.xRatio),f.push(p);var x,b=p,v=void 0,w=b,k=this.zeroY,A=this.zeroY;k=this.lineHelpers.determineFirstPrevY({i:d,series:t,prevY:k,lineYPosition:0}).prevY,u.push(k),x=k;\"rangeArea\"===n&&(v=A=this.lineHelpers.determineFirstPrevY({i:d,series:s,prevY:A,lineYPosition:0}).prevY);var S={type:n,series:t,realIndex:g,i:d,x:p,y:1,pX:b,pY:x,pathsFrom:this._calculatePathsFrom({type:n,series:t,i:d,realIndex:g,prevX:w,prevY:k,prevY2:A}),linePaths:[],areaPaths:[],seriesIndex:a,lineYPosition:0,xArrj:f,yArrj:u,seriesRangeEnd:s},C=this._iterateOverDataPoints(e(e({},S),{},{iterations:\"rangeArea\"===n?t[d].length-1:void 0,isRangeStart:!0}));if(\"rangeArea\"===n){var L=this._calculatePathsFrom({series:s,i:d,realIndex:g,prevX:w,prevY:A}),P=this._iterateOverDataPoints(e(e({},S),{},{series:s,pY:v,pathsFrom:L,iterations:s[d].length-1,isRangeStart:!1}));C.linePaths[0]=P.linePath+C.linePath,C.pathFromLine=P.pathFromLine+C.pathFromLine}this._handlePaths({type:n,realIndex:g,i:d,paths:C}),this.elSeries.add(this.elPointsMain),this.elSeries.add(this.elDataLabelsWrap),c.push(this.elSeries)}if(r.config.chart.stacked)for(var T=c.length;T>0;T--)l.add(c[T-1]);else for(var M=0;M<c.length;M++)l.add(c[M]);return l}},{key:\"_initSerieVariables\",value:function(t,e,i){var a=this.w,s=new m(this.ctx);this.xDivision=a.globals.gridWidth/(a.globals.dataPoints-(\"on\"===a.config.xaxis.tickPlacement?1:0)),this.strokeWidth=Array.isArray(a.config.stroke.width)?a.config.stroke.width[i]:a.config.stroke.width,this.yRatio.length>1&&(this.yaxisIndex=i),this.isReversed=a.config.yaxis[this.yaxisIndex]&&a.config.yaxis[this.yaxisIndex].reversed,this.zeroY=a.globals.gridHeight-this.baseLineY[this.yaxisIndex]-(this.isReversed?a.globals.gridHeight:0)+(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),this.areaBottomY=this.zeroY,(this.zeroY>a.globals.gridHeight||\"end\"===a.config.plotOptions.area.fillTo)&&(this.areaBottomY=a.globals.gridHeight),this.categoryAxisCorrection=this.xDivision/2,this.elSeries=s.group({class:\"apexcharts-series\",seriesName:x.escapeString(a.globals.seriesNames[i])}),this.elPointsMain=s.group({class:\"apexcharts-series-markers-wrap\",\"data:realIndex\":i}),this.elDataLabelsWrap=s.group({class:\"apexcharts-datalabels\",\"data:realIndex\":i});var r=t[e].length===a.globals.dataPoints;this.elSeries.attr({\"data:longestSeries\":r,rel:e+1,\"data:realIndex\":i}),this.appendPathFrom=!0}},{key:\"_calculatePathsFrom\",value:function(t){var e,i,a,s,r=t.type,o=t.series,n=t.i,l=t.realIndex,h=t.prevX,c=t.prevY,d=t.prevY2,g=this.w,u=new m(this.ctx);if(null===o[n][0]){for(var f=0;f<o[n].length;f++)if(null!==o[n][f]){h=this.xDivision*f,c=this.zeroY-o[n][f]/this.yRatio[this.yaxisIndex],e=u.move(h,c),i=u.move(h,this.areaBottomY);break}}else e=u.move(h,c),\"rangeArea\"===r&&(e=u.move(h,d)+u.line(h,c)),i=u.move(h,this.areaBottomY)+u.line(h,c);if(a=u.move(-1,this.zeroY)+u.line(-1,this.zeroY),s=u.move(-1,this.zeroY)+u.line(-1,this.zeroY),g.globals.previousPaths.length>0){var p=this.lineHelpers.checkPreviousPaths({pathFromLine:a,pathFromArea:s,realIndex:l});a=p.pathFromLine,s=p.pathFromArea}return{prevX:h,prevY:c,linePath:e,areaPath:i,pathFromLine:a,pathFromArea:s}}},{key:\"_handlePaths\",value:function(t){var i=t.type,a=t.realIndex,s=t.i,r=t.paths,o=this.w,n=new m(this.ctx),l=new R(this.ctx);this.prevSeriesY.push(r.yArrj),o.globals.seriesXvalues[a]=r.xArrj,o.globals.seriesYvalues[a]=r.yArrj;var h=o.config.forecastDataPoints;if(h.count>0&&\"rangeArea\"!==i){var c=o.globals.seriesXvalues[a][o.globals.seriesXvalues[a].length-h.count-1],d=n.drawRect(c,0,o.globals.gridWidth,o.globals.gridHeight,0);o.globals.dom.elForecastMask.appendChild(d.node);var g=n.drawRect(0,0,c,o.globals.gridHeight,0);o.globals.dom.elNonForecastMask.appendChild(g.node)}this.pointsChart||o.globals.delayedElements.push({el:this.elPointsMain.node,index:a});var u={i:s,realIndex:a,animationDelay:s,initialSpeed:o.config.chart.animations.speed,dataChangeSpeed:o.config.chart.animations.dynamicAnimation.speed,className:\"apexcharts-\".concat(i)};if(\"area\"===i)for(var f=l.fillPath({seriesNumber:a}),p=0;p<r.areaPaths.length;p++){var x=n.renderPaths(e(e({},u),{},{pathFrom:r.pathFromArea,pathTo:r.areaPaths[p],stroke:\"none\",strokeWidth:0,strokeLineCap:null,fill:f}));this.elSeries.add(x)}if(o.config.stroke.show&&!this.pointsChart){var b=null;if(\"line\"===i)b=l.fillPath({seriesNumber:a,i:s});else if(\"solid\"===o.config.stroke.fill.type)b=o.globals.stroke.colors[a];else{var v=o.config.fill;o.config.fill=o.config.stroke.fill,b=l.fillPath({seriesNumber:a,i:s}),o.config.fill=v}for(var y=0;y<r.linePaths.length;y++){var w=b;\"rangeArea\"===i&&(w=l.fillPath({seriesNumber:a}));var k=e(e({},u),{},{pathFrom:r.pathFromLine,pathTo:r.linePaths[y],stroke:b,strokeWidth:this.strokeWidth,strokeLineCap:o.config.stroke.lineCap,fill:\"rangeArea\"===i?w:\"none\"}),A=n.renderPaths(k);if(this.elSeries.add(A),A.attr(\"fill-rule\",\"evenodd\"),h.count>0&&\"rangeArea\"!==i){var S=n.renderPaths(k);S.node.setAttribute(\"stroke-dasharray\",h.dashArray),h.strokeWidth&&S.node.setAttribute(\"stroke-width\",h.strokeWidth),this.elSeries.add(S),S.attr(\"clip-path\",\"url(#forecastMask\".concat(o.globals.cuid,\")\")),A.attr(\"clip-path\",\"url(#nonForecastMask\".concat(o.globals.cuid,\")\"))}}}}},{key:\"_iterateOverDataPoints\",value:function(t){var e=t.type,i=t.series,a=t.iterations,s=t.realIndex,r=t.i,o=t.x,n=t.y,l=t.pX,h=t.pY,c=t.pathsFrom,d=t.linePaths,g=t.areaPaths,u=t.seriesIndex,f=t.lineYPosition,p=t.xArrj,b=t.yArrj,v=t.isRangeStart,y=t.seriesRangeEnd,w=this.w,k=new m(this.ctx),A=this.yRatio,S=c.prevY,C=c.linePath,L=c.areaPath,P=c.pathFromLine,T=c.pathFromArea,M=x.isNumber(w.globals.minYArr[s])?w.globals.minYArr[s]:w.globals.minY;a||(a=w.globals.dataPoints>1?w.globals.dataPoints-1:w.globals.dataPoints);for(var I=n,z=0;z<a;z++){var X=void 0===i[r][z+1]||null===i[r][z+1];if(w.globals.isXNumeric){var E=w.globals.seriesX[s][z+1];void 0===w.globals.seriesX[s][z+1]&&(E=w.globals.seriesX[s][a-1]),o=(E-w.globals.minX)/this.xRatio}else o+=this.xDivision;if(w.config.chart.stacked)if(r>0&&w.globals.collapsedSeries.length<w.config.series.length-1){f=this.prevSeriesY[function(t){for(var e=t,i=0;i<w.globals.series.length;i++)if(w.globals.collapsedSeriesIndices.indexOf(t)>-1){e--;break}return e>=0?e:0}(r-1)][z+1]}else f=this.zeroY;else f=this.zeroY;X?n=f-M/A[this.yaxisIndex]+2*(this.isReversed?M/A[this.yaxisIndex]:0):(n=f-i[r][z+1]/A[this.yaxisIndex]+2*(this.isReversed?i[r][z+1]/A[this.yaxisIndex]:0),\"rangeArea\"===e&&(I=f-y[r][z+1]/A[this.yaxisIndex]+2*(this.isReversed?y[r][z+1]/A[this.yaxisIndex]:0))),p.push(o),b.push(n);var Y=this.lineHelpers.calculatePoints({series:i,x:o,y:n,realIndex:s,i:r,j:z,prevY:S}),F=this._createPaths({type:e,series:i,i:r,realIndex:s,j:z,x:o,y:n,y2:I,pX:l,pY:h,linePath:C,areaPath:L,linePaths:d,areaPaths:g,seriesIndex:u,isRangeStart:v});g=F.areaPaths,d=F.linePaths,l=F.pX,h=F.pY,L=F.areaPath,C=F.linePath,this.appendPathFrom&&(P+=k.line(o,this.zeroY),T+=k.line(o,this.zeroY)),this.handleNullDataPoints(i,Y,r,z,s),this._handleMarkersAndLabels({type:e,pointsPos:Y,i:r,j:z,realIndex:s,isRangeStart:v})}return{yArrj:b,xArrj:p,pathFromArea:T,areaPaths:g,pathFromLine:P,linePaths:d,linePath:C,areaPath:L}}},{key:\"_handleMarkersAndLabels\",value:function(t){var e=t.type,i=t.pointsPos,a=t.isRangeStart,s=t.i,r=t.j,o=t.realIndex,n=this.w,l=new O(this.ctx);if(this.pointsChart)this.scatter.draw(this.elSeries,r,{realIndex:o,pointsPos:i,zRatio:this.zRatio,elParent:this.elPointsMain});else{n.globals.series[s].length>1&&this.elPointsMain.node.classList.add(\"apexcharts-element-hidden\");var h=this.markers.plotChartMarkers(i,o,r+1);null!==h&&this.elPointsMain.add(h)}var c=l.drawDataLabel({type:e,isRangeStart:a,pos:i,i:o,j:r+1});null!==c&&this.elDataLabelsWrap.add(c)}},{key:\"_createPaths\",value:function(t){var e=t.type,i=t.series,a=t.i,s=t.realIndex,r=t.j,o=t.x,n=t.y,l=t.y2,h=t.pX,c=t.pY,d=t.linePath,g=t.areaPath,u=t.linePaths,f=t.areaPaths,p=t.seriesIndex,x=t.isRangeStart,b=this.w,v=new m(this.ctx),y=b.config.stroke.curve,w=this.areaBottomY;if(Array.isArray(b.config.stroke.curve)&&(y=Array.isArray(p)?b.config.stroke.curve[p[a]]:b.config.stroke.curve[a]),\"smooth\"===y){var k=.35*(o-h);b.globals.hasNullValues?(null!==i[a][r]&&(null!==i[a][r+1]?(d=v.move(h,c)+v.curve(h+k,c,o-k,n,o+1,n),g=v.move(h+1,c)+v.curve(h+k,c,o-k,n,o+1,n)+v.line(o,w)+v.line(h,w)+\"z\"):(d=v.move(h,c),g=v.move(h,c)+\"z\")),u.push(d),f.push(g)):(d+=v.curve(h+k,c,o-k,n,o,n),g+=v.curve(h+k,c,o-k,n,o,n)),h=o,c=n,r===i[a].length-2&&(g=g+v.curve(h,c,o,n,o,w)+v.move(o,n)+\"z\",\"rangeArea\"===e&&x?d=d+v.curve(h,c,o,n,o,l)+v.move(o,l)+\"z\":b.globals.hasNullValues||(u.push(d),f.push(g)))}else{if(null===i[a][r+1]){d+=v.move(o,n);var A=b.globals.isXNumeric?(b.globals.seriesX[s][r]-b.globals.minX)/this.xRatio:o-this.xDivision;g=g+v.line(A,w)+v.move(o,n)+\"z\"}null===i[a][r]&&(d+=v.move(o,n),g+=v.move(o,w)),\"stepline\"===y?(d=d+v.line(o,null,\"H\")+v.line(null,n,\"V\"),g=g+v.line(o,null,\"H\")+v.line(null,n,\"V\")):\"straight\"===y&&(d+=v.line(o,n),g+=v.line(o,n)),r===i[a].length-2&&(g=g+v.line(o,w)+v.move(o,n)+\"z\",\"rangeArea\"===e&&x?d=d+v.line(o,l)+v.move(o,l)+\"z\":(u.push(d),f.push(g)))}return{linePaths:u,areaPaths:f,pX:h,pY:c,linePath:d,areaPath:g}}},{key:\"handleNullDataPoints\",value:function(t,e,i,a,s){var r=this.w;if(null===t[i][a]&&r.config.markers.showNullDataPoints||1===t[i].length){var o=this.markers.plotChartMarkers(e,s,a+1,this.strokeWidth-r.config.markers.strokeWidth/2,!0);null!==o&&this.elPointsMain.add(o)}}}]),t}();window.TreemapSquared={},window.TreemapSquared.generate=function(){function t(e,i,a,s){this.xoffset=e,this.yoffset=i,this.height=s,this.width=a,this.shortestEdge=function(){return Math.min(this.height,this.width)},this.getCoordinates=function(t){var e,i=[],a=this.xoffset,s=this.yoffset,o=r(t)/this.height,n=r(t)/this.width;if(this.width>=this.height)for(e=0;e<t.length;e++)i.push([a,s,a+o,s+t[e]/o]),s+=t[e]/o;else for(e=0;e<t.length;e++)i.push([a,s,a+t[e]/n,s+n]),a+=t[e]/n;return i},this.cutArea=function(e){var i;if(this.width>=this.height){var a=e/this.height,s=this.width-a;i=new t(this.xoffset+a,this.yoffset,s,this.height)}else{var r=e/this.width,o=this.height-r;i=new t(this.xoffset,this.yoffset+r,this.width,o)}return i}}function e(e,a,s,o,n){o=void 0===o?0:o,n=void 0===n?0:n;var l=i(function(t,e){var i,a=[],s=e/r(t);for(i=0;i<t.length;i++)a[i]=t[i]*s;return a}(e,a*s),[],new t(o,n,a,s),[]);return function(t){var e,i,a=[];for(e=0;e<t.length;e++)for(i=0;i<t[e].length;i++)a.push(t[e][i]);return a}(l)}function i(t,e,s,o){var n,l,h;if(0!==t.length)return n=s.shortestEdge(),function(t,e,i){var s;if(0===t.length)return!0;(s=t.slice()).push(e);var r=a(t,i),o=a(s,i);return r>=o}(e,l=t[0],n)?(e.push(l),i(t.slice(1),e,s,o)):(h=s.cutArea(r(e),o),o.push(s.getCoordinates(e)),i(t,[],h,o)),o;o.push(s.getCoordinates(e))}function a(t,e){var i=Math.min.apply(Math,t),a=Math.max.apply(Math,t),s=r(t);return Math.max(Math.pow(e,2)*a/Math.pow(s,2),Math.pow(s,2)/(Math.pow(e,2)*i))}function s(t){return t&&t.constructor===Array}function r(t){var e,i=0;for(e=0;e<t.length;e++)i+=t[e];return i}function o(t){var e,i=0;if(s(t[0]))for(e=0;e<t.length;e++)i+=o(t[e]);else i=r(t);return i}return function t(i,a,r,n,l){n=void 0===n?0:n,l=void 0===l?0:l;var h,c,d=[],g=[];if(s(i[0])){for(c=0;c<i.length;c++)d[c]=o(i[c]);for(h=e(d,a,r,n,l),c=0;c<i.length;c++)g.push(t(i[c],h[c][2]-h[c][0],h[c][3]-h[c][1],h[c][0],h[c][1]))}else g=e(i,a,r,n,l);return g}}();var Xt,Et,Yt=function(){function t(e,i){a(this,t),this.ctx=e,this.w=e.w,this.strokeWidth=this.w.config.stroke.width,this.helpers=new At(e),this.dynamicAnim=this.w.config.chart.animations.dynamicAnimation,this.labels=[]}return r(t,[{key:\"draw\",value:function(t){var e=this,i=this.w,a=new m(this.ctx),s=new R(this.ctx),r=a.group({class:\"apexcharts-treemap\"});if(i.globals.noData)return r;var o=[];return t.forEach((function(t){var e=t.map((function(t){return Math.abs(t)}));o.push(e)})),this.negRange=this.helpers.checkColorRange(),i.config.series.forEach((function(t,i){t.data.forEach((function(t){Array.isArray(e.labels[i])||(e.labels[i]=[]),e.labels[i].push(t.x)}))})),window.TreemapSquared.generate(o,i.globals.gridWidth,i.globals.gridHeight).forEach((function(o,n){var l=a.group({class:\"apexcharts-series apexcharts-treemap-series\",seriesName:x.escapeString(i.globals.seriesNames[n]),rel:n+1,\"data:realIndex\":n});if(i.config.chart.dropShadow.enabled){var h=i.config.chart.dropShadow;new v(e.ctx).dropShadow(r,h,n)}var c=a.group({class:\"apexcharts-data-labels\"});o.forEach((function(r,o){var h=r[0],c=r[1],d=r[2],g=r[3],u=a.drawRect(h,c,d-h,g-c,0,\"#fff\",1,e.strokeWidth,i.config.plotOptions.treemap.useFillColorAsStroke?p:i.globals.stroke.colors[n]);u.attr({cx:h,cy:c,index:n,i:n,j:o,width:d-h,height:g-c});var f=e.helpers.getShadeColor(i.config.chart.type,n,o,e.negRange),p=f.color;void 0!==i.config.series[n].data[o]&&i.config.series[n].data[o].fillColor&&(p=i.config.series[n].data[o].fillColor);var x=s.fillPath({color:p,seriesNumber:n,dataPointIndex:o});u.node.classList.add(\"apexcharts-treemap-rect\"),u.attr({fill:x}),e.helpers.addListeners(u);var b={x:h+(d-h)/2,y:c+(g-c)/2,width:0,height:0},v={x:h,y:c,width:d-h,height:g-c};if(i.config.chart.animations.enabled&&!i.globals.dataChanged){var m=1;i.globals.resized||(m=i.config.chart.animations.speed),e.animateTreemap(u,b,v,m)}if(i.globals.dataChanged){var y=1;e.dynamicAnim.enabled&&i.globals.shouldAnimate&&(y=e.dynamicAnim.speed,i.globals.previousPaths[n]&&i.globals.previousPaths[n][o]&&i.globals.previousPaths[n][o].rect&&(b=i.globals.previousPaths[n][o].rect),e.animateTreemap(u,b,v,y))}var w=e.getFontSize(r),k=i.config.dataLabels.formatter(e.labels[n][o],{value:i.globals.series[n][o],seriesIndex:n,dataPointIndex:o,w:i}),A=e.helpers.calculateDataLabels({text:k,x:(h+d)/2,y:(c+g)/2+e.strokeWidth/2+w/3,i:n,j:o,colorProps:f,fontSize:w,series:t});i.config.dataLabels.enabled&&A&&e.rotateToFitLabel(A,w,k,h,c,d,g),l.add(u),null!==A&&l.add(A)})),l.add(c),r.add(l)})),r}},{key:\"getFontSize\",value:function(t){var e=this.w;var i,a,s,r,o=function t(e){var i,a=0;if(Array.isArray(e[0]))for(i=0;i<e.length;i++)a+=t(e[i]);else for(i=0;i<e.length;i++)a+=e[i].length;return a}(this.labels)/function t(e){var i,a=0;if(Array.isArray(e[0]))for(i=0;i<e.length;i++)a+=t(e[i]);else for(i=0;i<e.length;i++)a+=1;return a}(this.labels);return i=t[2]-t[0],a=t[3]-t[1],s=i*a,r=Math.pow(s,.5),Math.min(r/o,parseInt(e.config.dataLabels.style.fontSize,10))}},{key:\"rotateToFitLabel\",value:function(t,e,i,a,s,r,o){var n=new m(this.ctx),l=n.getTextRects(i,e);if(l.width+this.w.config.stroke.width+5>r-a&&l.width<=o-s){var h=n.rotateAroundCenter(t.node);t.node.setAttribute(\"transform\",\"rotate(-90 \".concat(h.x,\" \").concat(h.y,\")\"))}}},{key:\"animateTreemap\",value:function(t,e,i,a){var s=new b(this.ctx);s.animateRect(t,{x:e.x,y:e.y,width:e.width,height:e.height},{x:i.x,y:i.y,width:i.width,height:i.height},a,(function(){s.animationCompleted(t)}))}}]),t}(),Ft=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w,this.timeScaleArray=[],this.utc=this.w.config.xaxis.labels.datetimeUTC}return r(t,[{key:\"calculateTimeScaleTicks\",value:function(t,i){var a=this,s=this.w;if(s.globals.allSeriesCollapsed)return s.globals.labels=[],s.globals.timescaleLabels=[],[];var r=new T(this.ctx),o=(i-t)/864e5;this.determineInterval(o),s.globals.disableZoomIn=!1,s.globals.disableZoomOut=!1,o<.00011574074074074075?s.globals.disableZoomIn=!0:o>5e4&&(s.globals.disableZoomOut=!0);var n=r.getTimeUnitsfromTimestamp(t,i,this.utc),l=s.globals.gridWidth/o,h=l/24,c=h/60,d=c/60,g=Math.floor(24*o),u=Math.floor(1440*o),f=Math.floor(86400*o),p=Math.floor(o),x=Math.floor(o/30),b=Math.floor(o/365),v={minMillisecond:n.minMillisecond,minSecond:n.minSecond,minMinute:n.minMinute,minHour:n.minHour,minDate:n.minDate,minMonth:n.minMonth,minYear:n.minYear},m={firstVal:v,currentMillisecond:v.minMillisecond,currentSecond:v.minSecond,currentMinute:v.minMinute,currentHour:v.minHour,currentMonthDate:v.minDate,currentDate:v.minDate,currentMonth:v.minMonth,currentYear:v.minYear,daysWidthOnXAxis:l,hoursWidthOnXAxis:h,minutesWidthOnXAxis:c,secondsWidthOnXAxis:d,numberOfSeconds:f,numberOfMinutes:u,numberOfHours:g,numberOfDays:p,numberOfMonths:x,numberOfYears:b};switch(this.tickInterval){case\"years\":this.generateYearScale(m);break;case\"months\":case\"half_year\":this.generateMonthScale(m);break;case\"months_days\":case\"months_fortnight\":case\"days\":case\"week_days\":this.generateDayScale(m);break;case\"hours\":this.generateHourScale(m);break;case\"minutes_fives\":case\"minutes\":this.generateMinuteScale(m);break;case\"seconds_tens\":case\"seconds_fives\":case\"seconds\":this.generateSecondScale(m)}var y=this.timeScaleArray.map((function(t){var i={position:t.position,unit:t.unit,year:t.year,day:t.day?t.day:1,hour:t.hour?t.hour:0,month:t.month+1};return\"month\"===t.unit?e(e({},i),{},{day:1,value:t.value+1}):\"day\"===t.unit||\"hour\"===t.unit?e(e({},i),{},{value:t.value}):\"minute\"===t.unit?e(e({},i),{},{value:t.value,minute:t.value}):\"second\"===t.unit?e(e({},i),{},{value:t.value,minute:t.minute,second:t.second}):t}));return y.filter((function(t){var e=1,i=Math.ceil(s.globals.gridWidth/120),r=t.value;void 0!==s.config.xaxis.tickAmount&&(i=s.config.xaxis.tickAmount),y.length>i&&(e=Math.floor(y.length/i));var o=!1,n=!1;switch(a.tickInterval){case\"years\":\"year\"===t.unit&&(o=!0);break;case\"half_year\":e=7,\"year\"===t.unit&&(o=!0);break;case\"months\":e=1,\"year\"===t.unit&&(o=!0);break;case\"months_fortnight\":e=15,\"year\"!==t.unit&&\"month\"!==t.unit||(o=!0),30===r&&(n=!0);break;case\"months_days\":e=10,\"month\"===t.unit&&(o=!0),30===r&&(n=!0);break;case\"week_days\":e=8,\"month\"===t.unit&&(o=!0);break;case\"days\":e=1,\"month\"===t.unit&&(o=!0);break;case\"hours\":\"day\"===t.unit&&(o=!0);break;case\"minutes_fives\":case\"seconds_fives\":r%5!=0&&(n=!0);break;case\"seconds_tens\":r%10!=0&&(n=!0)}if(\"hours\"===a.tickInterval||\"minutes_fives\"===a.tickInterval||\"seconds_tens\"===a.tickInterval||\"seconds_fives\"===a.tickInterval){if(!n)return!0}else if((r%e==0||o)&&!n)return!0}))}},{key:\"recalcDimensionsBasedOnFormat\",value:function(t,e){var i=this.w,a=this.formatDates(t),s=this.removeOverlappingTS(a);i.globals.timescaleLabels=s.slice(),new ot(this.ctx).plotCoords()}},{key:\"determineInterval\",value:function(t){var e=24*t,i=60*e;switch(!0){case t/365>5:this.tickInterval=\"years\";break;case t>800:this.tickInterval=\"half_year\";break;case t>180:this.tickInterval=\"months\";break;case t>90:this.tickInterval=\"months_fortnight\";break;case t>60:this.tickInterval=\"months_days\";break;case t>30:this.tickInterval=\"week_days\";break;case t>2:this.tickInterval=\"days\";break;case e>2.4:this.tickInterval=\"hours\";break;case i>15:this.tickInterval=\"minutes_fives\";break;case i>5:this.tickInterval=\"minutes\";break;case i>1:this.tickInterval=\"seconds_tens\";break;case 60*i>20:this.tickInterval=\"seconds_fives\";break;default:this.tickInterval=\"seconds\"}}},{key:\"generateYearScale\",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.daysWidthOnXAxis,r=t.numberOfYears,o=e.minYear,n=0,l=new T(this.ctx),h=\"year\";if(e.minDate>1||e.minMonth>0){var c=l.determineRemainingDaysOfYear(e.minYear,e.minMonth,e.minDate);n=(l.determineDaysOfYear(e.minYear)-c+1)*s,o=e.minYear+1,this.timeScaleArray.push({position:n,value:o,unit:h,year:o,month:x.monthMod(i+1)})}else 1===e.minDate&&0===e.minMonth&&this.timeScaleArray.push({position:n,value:o,unit:h,year:a,month:x.monthMod(i+1)});for(var d=o,g=n,u=0;u<r;u++)d++,g=l.determineDaysOfYear(d-1)*s+g,this.timeScaleArray.push({position:g,value:d,unit:h,year:d,month:1})}},{key:\"generateMonthScale\",value:function(t){var e=t.firstVal,i=t.currentMonthDate,a=t.currentMonth,s=t.currentYear,r=t.daysWidthOnXAxis,o=t.numberOfMonths,n=a,l=0,h=new T(this.ctx),c=\"month\",d=0;if(e.minDate>1){l=(h.determineDaysOfMonths(a+1,e.minYear)-i+1)*r,n=x.monthMod(a+1);var g=s+d,u=x.monthMod(n),f=n;0===n&&(c=\"year\",f=g,u=1,g+=d+=1),this.timeScaleArray.push({position:l,value:f,unit:c,year:g,month:u})}else this.timeScaleArray.push({position:l,value:n,unit:c,year:s,month:x.monthMod(a)});for(var p=n+1,b=l,v=0,m=1;v<o;v++,m++){0===(p=x.monthMod(p))?(c=\"year\",d+=1):c=\"month\";var y=this._getYear(s,p,d);b=h.determineDaysOfMonths(p,y)*r+b;var w=0===p?y:p;this.timeScaleArray.push({position:b,value:w,unit:c,year:y,month:0===p?1:p}),p++}}},{key:\"generateDayScale\",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.hoursWidthOnXAxis,r=t.numberOfDays,o=new T(this.ctx),n=\"day\",l=e.minDate+1,h=l,c=function(t,e,i){return t>o.determineDaysOfMonths(e+1,i)?(h=1,n=\"month\",g=e+=1,e):e},d=(24-e.minHour)*s,g=l,u=c(h,i,a);0===e.minHour&&1===e.minDate?(d=0,g=x.monthMod(e.minMonth),n=\"month\",h=e.minDate,r++):1!==e.minDate&&0===e.minHour&&0===e.minMinute&&(d=0,l=e.minDate,g=l,u=c(h=l,i,a)),this.timeScaleArray.push({position:d,value:g,unit:n,year:this._getYear(a,u,0),month:x.monthMod(u),day:h});for(var f=d,p=0;p<r;p++){n=\"day\",u=c(h+=1,u,this._getYear(a,u,0));var b=this._getYear(a,u,0);f=24*s+f;var v=1===h?x.monthMod(u):h;this.timeScaleArray.push({position:f,value:v,unit:n,year:b,month:x.monthMod(u),day:v})}}},{key:\"generateHourScale\",value:function(t){var e=t.firstVal,i=t.currentDate,a=t.currentMonth,s=t.currentYear,r=t.minutesWidthOnXAxis,o=t.numberOfHours,n=new T(this.ctx),l=\"hour\",h=function(t,e){return t>n.determineDaysOfMonths(e+1,s)&&(p=1,e+=1),{month:e,date:p}},c=function(t,e){return t>n.determineDaysOfMonths(e+1,s)?e+=1:e},d=60-(e.minMinute+e.minSecond/60),g=d*r,u=e.minHour+1,f=u+1;60===d&&(g=0,f=(u=e.minHour)+1);var p=i,b=c(p,a);this.timeScaleArray.push({position:g,value:u,unit:l,day:p,hour:f,year:s,month:x.monthMod(b)});for(var v=g,m=0;m<o;m++){if(l=\"hour\",f>=24)f=0,l=\"day\",b=h(p+=1,b).month,b=c(p,b);var y=this._getYear(s,b,0);v=60*r+v;var w=0===f?p:f;this.timeScaleArray.push({position:v,value:w,unit:l,hour:f,day:p,year:y,month:x.monthMod(b)}),f++}}},{key:\"generateMinuteScale\",value:function(t){for(var e=t.currentMillisecond,i=t.currentSecond,a=t.currentMinute,s=t.currentHour,r=t.currentDate,o=t.currentMonth,n=t.currentYear,l=t.minutesWidthOnXAxis,h=t.secondsWidthOnXAxis,c=t.numberOfMinutes,d=a+1,g=r,u=o,f=n,p=s,b=(60-i-e/1e3)*h,v=0;v<c;v++)d>=60&&(d=0,24===(p+=1)&&(p=0)),this.timeScaleArray.push({position:b,value:d,unit:\"minute\",hour:p,minute:d,day:g,year:this._getYear(f,u,0),month:x.monthMod(u)}),b+=l,d++}},{key:\"generateSecondScale\",value:function(t){for(var e=t.currentMillisecond,i=t.currentSecond,a=t.currentMinute,s=t.currentHour,r=t.currentDate,o=t.currentMonth,n=t.currentYear,l=t.secondsWidthOnXAxis,h=t.numberOfSeconds,c=i+1,d=a,g=r,u=o,f=n,p=s,b=(1e3-e)/1e3*l,v=0;v<h;v++)c>=60&&(c=0,++d>=60&&(d=0,24===++p&&(p=0))),this.timeScaleArray.push({position:b,value:c,unit:\"second\",hour:p,minute:d,second:c,day:g,year:this._getYear(f,u,0),month:x.monthMod(u)}),b+=l,c++}},{key:\"createRawDateString\",value:function(t,e){var i=t.year;return 0===t.month&&(t.month=1),i+=\"-\"+(\"0\"+t.month.toString()).slice(-2),\"day\"===t.unit?i+=\"day\"===t.unit?\"-\"+(\"0\"+e).slice(-2):\"-01\":i+=\"-\"+(\"0\"+(t.day?t.day:\"1\")).slice(-2),\"hour\"===t.unit?i+=\"hour\"===t.unit?\"T\"+(\"0\"+e).slice(-2):\"T00\":i+=\"T\"+(\"0\"+(t.hour?t.hour:\"0\")).slice(-2),\"minute\"===t.unit?i+=\":\"+(\"0\"+e).slice(-2):i+=\":\"+(t.minute?(\"0\"+t.minute).slice(-2):\"00\"),\"second\"===t.unit?i+=\":\"+(\"0\"+e).slice(-2):i+=\":00\",this.utc&&(i+=\".000Z\"),i}},{key:\"formatDates\",value:function(t){var e=this,i=this.w;return t.map((function(t){var a=t.value.toString(),s=new T(e.ctx),r=e.createRawDateString(t,a),o=s.getDate(s.parseDate(r));if(e.utc||(o=s.getDate(s.parseDateWithTimezone(r))),void 0===i.config.xaxis.labels.format){var n=\"dd MMM\",l=i.config.xaxis.labels.datetimeFormatter;\"year\"===t.unit&&(n=l.year),\"month\"===t.unit&&(n=l.month),\"day\"===t.unit&&(n=l.day),\"hour\"===t.unit&&(n=l.hour),\"minute\"===t.unit&&(n=l.minute),\"second\"===t.unit&&(n=l.second),a=s.formatDate(o,n)}else a=s.formatDate(o,i.config.xaxis.labels.format);return{dateString:r,position:t.position,value:a,unit:t.unit,year:t.year,month:t.month}}))}},{key:\"removeOverlappingTS\",value:function(t){var e,i=this,a=new m(this.ctx),s=!1;t.length>0&&t[0].value&&t.every((function(e){return e.value.length===t[0].value.length}))&&(s=!0,e=a.getTextRects(t[0].value).width);var r=0,o=t.map((function(o,n){if(n>0&&i.w.config.xaxis.labels.hideOverlappingLabels){var l=s?e:a.getTextRects(t[r].value).width,h=t[r].position;return o.position>h+l+10?(r=n,o):null}return o}));return o=o.filter((function(t){return null!==t}))}},{key:\"_getYear\",value:function(t,e,i){return t+Math.floor(e/12)+i}}]),t}(),Rt=function(){function t(e,i){a(this,t),this.ctx=i,this.w=i.w,this.el=e}return r(t,[{key:\"setupElements\",value:function(){var t=this.w.globals,e=this.w.config,i=e.chart.type;t.axisCharts=[\"line\",\"area\",\"bar\",\"rangeBar\",\"rangeArea\",\"candlestick\",\"boxPlot\",\"scatter\",\"bubble\",\"radar\",\"heatmap\",\"treemap\"].indexOf(i)>-1,t.xyCharts=[\"line\",\"area\",\"bar\",\"rangeBar\",\"rangeArea\",\"candlestick\",\"boxPlot\",\"scatter\",\"bubble\"].indexOf(i)>-1,t.isBarHorizontal=(\"bar\"===e.chart.type||\"rangeBar\"===e.chart.type||\"boxPlot\"===e.chart.type)&&e.plotOptions.bar.horizontal,t.chartClass=\".apexcharts\"+t.chartID,t.dom.baseEl=this.el,t.dom.elWrap=document.createElement(\"div\"),m.setAttrs(t.dom.elWrap,{id:t.chartClass.substring(1),class:\"apexcharts-canvas \"+t.chartClass.substring(1)}),this.el.appendChild(t.dom.elWrap),t.dom.Paper=new window.SVG.Doc(t.dom.elWrap),t.dom.Paper.attr({class:\"apexcharts-svg\",\"xmlns:data\":\"ApexChartsNS\",transform:\"translate(\".concat(e.chart.offsetX,\", \").concat(e.chart.offsetY,\")\")}),t.dom.Paper.node.style.background=e.chart.background,this.setSVGDimensions(),t.dom.elGraphical=t.dom.Paper.group().attr({class:\"apexcharts-inner apexcharts-graphical\"}),t.dom.elAnnotations=t.dom.Paper.group().attr({class:\"apexcharts-annotations\"}),t.dom.elDefs=t.dom.Paper.defs(),t.dom.elLegendWrap=document.createElement(\"div\"),t.dom.elLegendWrap.classList.add(\"apexcharts-legend\"),t.dom.elWrap.appendChild(t.dom.elLegendWrap),t.dom.Paper.add(t.dom.elGraphical),t.dom.elGraphical.add(t.dom.elDefs)}},{key:\"plotChartType\",value:function(t,e){var i=this.w,a=i.config,s=i.globals,r={series:[],i:[]},o={series:[],i:[]},n={series:[],i:[]},l={series:[],i:[]},h={series:[],i:[]},c={series:[],i:[]},d={series:[],i:[]},g={series:[],i:[]},u={series:[],seriesRangeEnd:[],i:[]};s.series.map((function(e,f){var p=0;void 0!==t[f].type?(\"column\"===t[f].type||\"bar\"===t[f].type?(s.series.length>1&&a.plotOptions.bar.horizontal&&console.warn(\"Horizontal bars are not supported in a mixed/combo chart. Please turn off `plotOptions.bar.horizontal`\"),h.series.push(e),h.i.push(f),p++,i.globals.columnSeries=h.series):\"area\"===t[f].type?(o.series.push(e),o.i.push(f),p++):\"line\"===t[f].type?(r.series.push(e),r.i.push(f),p++):\"scatter\"===t[f].type?(n.series.push(e),n.i.push(f)):\"bubble\"===t[f].type?(l.series.push(e),l.i.push(f),p++):\"candlestick\"===t[f].type?(c.series.push(e),c.i.push(f),p++):\"boxPlot\"===t[f].type?(d.series.push(e),d.i.push(f),p++):\"rangeBar\"===t[f].type?(g.series.push(e),g.i.push(f),p++):\"rangeArea\"===t[f].type?(u.series.push(s.seriesRangeStart[f]),u.seriesRangeEnd.push(s.seriesRangeEnd[f]),u.i.push(f),p++):console.warn(\"You have specified an unrecognized chart type. Available types for this property are line/area/column/bar/scatter/bubble\"),p>1&&(s.comboCharts=!0)):(r.series.push(e),r.i.push(f))}));var f=new zt(this.ctx,e),p=new kt(this.ctx,e);this.ctx.pie=new Lt(this.ctx);var x=new Tt(this.ctx);this.ctx.rangeBar=new Mt(this.ctx,e);var b=new Pt(this.ctx),v=[];if(s.comboCharts){if(o.series.length>0&&v.push(f.draw(o.series,\"area\",o.i)),h.series.length>0)if(i.config.chart.stacked){var m=new wt(this.ctx,e);v.push(m.draw(h.series,h.i))}else this.ctx.bar=new yt(this.ctx,e),v.push(this.ctx.bar.draw(h.series,h.i));if(u.series.length>0&&v.push(f.draw(u.series,\"rangeArea\",u.i,u.seriesRangeEnd)),r.series.length>0&&v.push(f.draw(r.series,\"line\",r.i)),c.series.length>0&&v.push(p.draw(c.series,c.i)),d.series.length>0&&v.push(p.draw(d.series,d.i)),g.series.length>0&&v.push(this.ctx.rangeBar.draw(g.series,g.i)),n.series.length>0){var y=new zt(this.ctx,e,!0);v.push(y.draw(n.series,\"scatter\",n.i))}if(l.series.length>0){var w=new zt(this.ctx,e,!0);v.push(w.draw(l.series,\"bubble\",l.i))}}else switch(a.chart.type){case\"line\":v=f.draw(s.series,\"line\");break;case\"area\":v=f.draw(s.series,\"area\");break;case\"bar\":if(a.chart.stacked)v=new wt(this.ctx,e).draw(s.series);else this.ctx.bar=new yt(this.ctx,e),v=this.ctx.bar.draw(s.series);break;case\"candlestick\":v=new kt(this.ctx,e).draw(s.series);break;case\"boxPlot\":v=new kt(this.ctx,e).draw(s.series);break;case\"rangeBar\":v=this.ctx.rangeBar.draw(s.series);break;case\"rangeArea\":v=f.draw(s.seriesRangeStart,\"rangeArea\",void 0,s.seriesRangeEnd);break;case\"heatmap\":v=new St(this.ctx,e).draw(s.series);break;case\"treemap\":v=new Yt(this.ctx,e).draw(s.series);break;case\"pie\":case\"donut\":case\"polarArea\":v=this.ctx.pie.draw(s.series);break;case\"radialBar\":v=x.draw(s.series);break;case\"radar\":v=b.draw(s.series);break;default:v=f.draw(s.series)}return v}},{key:\"setSVGDimensions\",value:function(){var t=this.w.globals,e=this.w.config;t.svgWidth=e.chart.width,t.svgHeight=e.chart.height;var i=x.getDimensions(this.el),a=e.chart.width.toString().split(/[0-9]+/g).pop();\"%\"===a?x.isNumber(i[0])&&(0===i[0].width&&(i=x.getDimensions(this.el.parentNode)),t.svgWidth=i[0]*parseInt(e.chart.width,10)/100):\"px\"!==a&&\"\"!==a||(t.svgWidth=parseInt(e.chart.width,10));var s=e.chart.height.toString().split(/[0-9]+/g).pop();if(\"auto\"!==t.svgHeight&&\"\"!==t.svgHeight)if(\"%\"===s){var r=x.getDimensions(this.el.parentNode);t.svgHeight=r[1]*parseInt(e.chart.height,10)/100}else t.svgHeight=parseInt(e.chart.height,10);else t.axisCharts?t.svgHeight=t.svgWidth/1.61:t.svgHeight=t.svgWidth/1.2;if(t.svgWidth<0&&(t.svgWidth=0),t.svgHeight<0&&(t.svgHeight=0),m.setAttrs(t.dom.Paper.node,{width:t.svgWidth,height:t.svgHeight}),\"%\"!==s){var o=e.chart.sparkline.enabled?0:t.axisCharts?e.chart.parentHeightOffset:0;t.dom.Paper.node.parentNode.parentNode.style.minHeight=t.svgHeight+o+\"px\"}t.dom.elWrap.style.width=t.svgWidth+\"px\",t.dom.elWrap.style.height=t.svgHeight+\"px\"}},{key:\"shiftGraphPosition\",value:function(){var t=this.w.globals,e=t.translateY,i={transform:\"translate(\"+t.translateX+\", \"+e+\")\"};m.setAttrs(t.dom.elGraphical.node,i)}},{key:\"resizeNonAxisCharts\",value:function(){var t=this.w,e=t.globals,i=0,a=t.config.chart.sparkline.enabled?1:15;a+=t.config.grid.padding.bottom,\"top\"!==t.config.legend.position&&\"bottom\"!==t.config.legend.position||!t.config.legend.show||t.config.legend.floating||(i=new lt(this.ctx).legendHelpers.getLegendBBox().clwh+10);var s=t.globals.dom.baseEl.querySelector(\".apexcharts-radialbar, .apexcharts-pie\"),r=2.05*t.globals.radialSize;if(s&&!t.config.chart.sparkline.enabled&&0!==t.config.plotOptions.radialBar.startAngle){var o=x.getBoundingClientRect(s);r=o.bottom;var n=o.bottom-o.top;r=Math.max(2.05*t.globals.radialSize,n)}var l=r+e.translateY+i+a;e.dom.elLegendForeign&&e.dom.elLegendForeign.setAttribute(\"height\",l),t.config.chart.height&&String(t.config.chart.height).indexOf(\"%\")>0||(e.dom.elWrap.style.height=l+\"px\",m.setAttrs(e.dom.Paper.node,{height:l}),e.dom.Paper.node.parentNode.parentNode.style.minHeight=l+\"px\")}},{key:\"coreCalculations\",value:function(){new U(this.ctx).init()}},{key:\"resetGlobals\",value:function(){var t=this,e=function(){return t.w.config.series.map((function(t){return[]}))},i=new Y,a=this.w.globals;i.initGlobalVars(a),a.seriesXvalues=e(),a.seriesYvalues=e()}},{key:\"isMultipleY\",value:function(){if(this.w.config.yaxis.constructor===Array&&this.w.config.yaxis.length>1)return this.w.globals.isMultipleYAxis=!0,!0}},{key:\"xySettings\",value:function(){var t=null,e=this.w;if(e.globals.axisCharts){if(\"back\"===e.config.xaxis.crosshairs.position)new Q(this.ctx).drawXCrosshairs();if(\"back\"===e.config.yaxis[0].crosshairs.position)new Q(this.ctx).drawYCrosshairs();if(\"datetime\"===e.config.xaxis.type&&void 0===e.config.xaxis.labels.formatter){this.ctx.timeScale=new Ft(this.ctx);var i=[];isFinite(e.globals.minX)&&isFinite(e.globals.maxX)&&!e.globals.isBarHorizontal?i=this.ctx.timeScale.calculateTimeScaleTicks(e.globals.minX,e.globals.maxX):e.globals.isBarHorizontal&&(i=this.ctx.timeScale.calculateTimeScaleTicks(e.globals.minY,e.globals.maxY)),this.ctx.timeScale.recalcDimensionsBasedOnFormat(i)}t=new y(this.ctx).getCalculatedRatios()}return t}},{key:\"updateSourceChart\",value:function(t){this.ctx.w.globals.selection=void 0,this.ctx.updateHelpers._updateOptions({chart:{selection:{xaxis:{min:t.w.globals.minX,max:t.w.globals.maxX}}}},!1,!1)}},{key:\"setupBrushHandler\",value:function(){var t=this,i=this.w;if(i.config.chart.brush.enabled&&\"function\"!=typeof i.config.chart.events.selection){var a=i.config.chart.brush.targets||[i.config.chart.brush.target];a.forEach((function(e){var i=ApexCharts.getChartByID(e);i.w.globals.brushSource=t.ctx,\"function\"!=typeof i.w.config.chart.events.zoomed&&(i.w.config.chart.events.zoomed=function(){t.updateSourceChart(i)}),\"function\"!=typeof i.w.config.chart.events.scrolled&&(i.w.config.chart.events.scrolled=function(){t.updateSourceChart(i)})})),i.config.chart.events.selection=function(t,s){a.forEach((function(t){var a=ApexCharts.getChartByID(t),r=x.clone(i.config.yaxis);if(i.config.chart.brush.autoScaleYaxis&&1===a.w.globals.series.length){var o=new _(a);r=o.autoScaleY(a,r,s)}var n=a.w.config.yaxis.reduce((function(t,i,s){return[].concat(u(t),[e(e({},a.w.config.yaxis[s]),{},{min:r[0].min,max:r[0].max})])}),[]);a.ctx.updateHelpers._updateOptions({xaxis:{min:s.xaxis.min,max:s.xaxis.max},yaxis:n},!1,!1,!1,!1)}))}}}}]),t}(),Dt=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"_updateOptions\",value:function(t){var e=this,a=arguments.length>1&&void 0!==arguments[1]&&arguments[1],s=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];return new Promise((function(n){var l=[e.ctx];r&&(l=e.ctx.getSyncedCharts()),e.ctx.w.globals.isExecCalled&&(l=[e.ctx],e.ctx.w.globals.isExecCalled=!1),l.forEach((function(r,h){var c=r.w;if(c.globals.shouldAnimate=s,a||(c.globals.resized=!0,c.globals.dataChanged=!0,s&&r.series.getPreviousPaths()),t&&\"object\"===i(t)&&(r.config=new E(t),t=y.extendArrayProps(r.config,t,c),r.w.globals.chartID!==e.ctx.w.globals.chartID&&delete t.series,c.config=x.extend(c.config,t),o&&(c.globals.lastXAxis=t.xaxis?x.clone(t.xaxis):[],c.globals.lastYAxis=t.yaxis?x.clone(t.yaxis):[],c.globals.initialConfig=x.extend({},c.config),c.globals.initialSeries=x.clone(c.config.series),t.series))){for(var d=0;d<c.globals.collapsedSeriesIndices.length;d++){var g=c.config.series[c.globals.collapsedSeriesIndices[d]];c.globals.collapsedSeries[d].data=c.globals.axisCharts?g.data.slice():g}for(var u=0;u<c.globals.ancillaryCollapsedSeriesIndices.length;u++){var f=c.config.series[c.globals.ancillaryCollapsedSeriesIndices[u]];c.globals.ancillaryCollapsedSeries[u].data=c.globals.axisCharts?f.data.slice():f}r.series.emptyCollapsedSeries(c.config.series)}return r.update(t).then((function(){h===l.length-1&&n(r)}))}))}))}},{key:\"_updateSeries\",value:function(t,e){var i=this,a=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return new Promise((function(s){var r,o=i.w;return o.globals.shouldAnimate=e,o.globals.dataChanged=!0,e&&i.ctx.series.getPreviousPaths(),o.globals.axisCharts?(0===(r=t.map((function(t,e){return i._extendSeries(t,e)}))).length&&(r=[{data:[]}]),o.config.series=r):o.config.series=t.slice(),a&&(o.globals.initialConfig.series=x.clone(o.config.series),o.globals.initialSeries=x.clone(o.config.series)),i.ctx.update().then((function(){s(i.ctx)}))}))}},{key:\"_extendSeries\",value:function(t,i){var a=this.w,s=a.config.series[i];return e(e({},a.config.series[i]),{},{name:t.name?t.name:s&&s.name,color:t.color?t.color:s&&s.color,type:t.type?t.type:s&&s.type,data:t.data?t.data:s&&s.data})}},{key:\"toggleDataPointSelection\",value:function(t,e){var i=this.w,a=null,s=\".apexcharts-series[data\\\\:realIndex='\".concat(t,\"']\");return i.globals.axisCharts?a=i.globals.dom.Paper.select(\"\".concat(s,\" path[j='\").concat(e,\"'], \").concat(s,\" circle[j='\").concat(e,\"'], \").concat(s,\" rect[j='\").concat(e,\"']\")).members[0]:void 0===e&&(a=i.globals.dom.Paper.select(\"\".concat(s,\" path[j='\").concat(t,\"']\")).members[0],\"pie\"!==i.config.chart.type&&\"polarArea\"!==i.config.chart.type&&\"donut\"!==i.config.chart.type||this.ctx.pie.pieClicked(t)),a?(new m(this.ctx).pathMouseDown(a,null),a.node?a.node:null):(console.warn(\"toggleDataPointSelection: Element not found\"),null)}},{key:\"forceXAxisUpdate\",value:function(t){var e=this.w;if([\"min\",\"max\"].forEach((function(i){void 0!==t.xaxis[i]&&(e.config.xaxis[i]=t.xaxis[i],e.globals.lastXAxis[i]=t.xaxis[i])})),t.xaxis.categories&&t.xaxis.categories.length&&(e.config.xaxis.categories=t.xaxis.categories),e.config.xaxis.convertedCatToNumeric){var i=new X(t);t=i.convertCatToNumericXaxis(t,this.ctx)}return t}},{key:\"forceYAxisUpdate\",value:function(t){return t.chart&&t.chart.stacked&&\"100%\"===t.chart.stackType&&(Array.isArray(t.yaxis)?t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})):(t.yaxis.min=0,t.yaxis.max=100)),t}},{key:\"revertDefaultAxisMinMax\",value:function(t){var e=this,i=this.w,a=i.globals.lastXAxis,s=i.globals.lastYAxis;t&&t.xaxis&&(a=t.xaxis),t&&t.yaxis&&(s=t.yaxis),i.config.xaxis.min=a.min,i.config.xaxis.max=a.max;var r=function(t){void 0!==s[t]&&(i.config.yaxis[t].min=s[t].min,i.config.yaxis[t].max=s[t].max)};i.config.yaxis.map((function(t,a){i.globals.zoomed||void 0!==s[a]?r(a):void 0!==e.ctx.opts.yaxis[a]&&(t.min=e.ctx.opts.yaxis[a].min,t.max=e.ctx.opts.yaxis[a].max)}))}}]),t}();Xt=\"undefined\"!=typeof window?window:void 0,Et=function(t,e){var a=(void 0!==this?this:t).SVG=function(t){if(a.supported)return t=new a.Doc(t),a.parser.draw||a.prepare(),t};if(a.ns=\"http://www.w3.org/2000/svg\",a.xmlns=\"http://www.w3.org/2000/xmlns/\",a.xlink=\"http://www.w3.org/1999/xlink\",a.svgjs=\"http://svgjs.dev\",a.supported=!0,!a.supported)return!1;a.did=1e3,a.eid=function(t){return\"Svgjs\"+d(t)+a.did++},a.create=function(t){var i=e.createElementNS(this.ns,t);return i.setAttribute(\"id\",this.eid(t)),i},a.extend=function(){var t,e;e=(t=[].slice.call(arguments)).pop();for(var i=t.length-1;i>=0;i--)if(t[i])for(var s in e)t[i].prototype[s]=e[s];a.Set&&a.Set.inherit&&a.Set.inherit()},a.invent=function(t){var e=\"function\"==typeof t.create?t.create:function(){this.constructor.call(this,a.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&a.extend(e,t.extend),t.construct&&a.extend(t.parent||a.Container,t.construct),e},a.adopt=function(e){return e?e.instance?e.instance:((i=\"svg\"==e.nodeName?e.parentNode instanceof t.SVGElement?new a.Nested:new a.Doc:\"linearGradient\"==e.nodeName?new a.Gradient(\"linear\"):\"radialGradient\"==e.nodeName?new a.Gradient(\"radial\"):a[d(e.nodeName)]?new(a[d(e.nodeName)]):new a.Element(e)).type=e.nodeName,i.node=e,e.instance=i,i instanceof a.Doc&&i.namespace().defs(),i.setData(JSON.parse(e.getAttribute(\"svgjs:data\"))||{}),i):null;var i},a.prepare=function(){var t=e.getElementsByTagName(\"body\")[0],i=(t?new a.Doc(t):a.adopt(e.documentElement).nested()).size(2,0);a.parser={body:t||e.documentElement,draw:i.style(\"opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden\").node,poly:i.polyline().node,path:i.path().node,native:a.create(\"svg\")}},a.parser={native:a.create(\"svg\")},e.addEventListener(\"DOMContentLoaded\",(function(){a.parser.draw||a.prepare()}),!1),a.regex={numberAndUnit:/^([+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i,rgb:/rgb\\((\\d+),(\\d+),(\\d+)\\)/,reference:/#([a-z0-9\\-_]+)/i,transforms:/\\)\\s*,?\\s*/,whitespace:/\\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\\s+)?$/,isNumber:/^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?$/i,isPercent:/^-?[\\d\\.]+%$/,isImage:/\\.(jpg|jpeg|png|gif|svg)(\\?[^=]+.*)?/i,delimiter:/[\\s,]+/,hyphen:/([^e])\\-/gi,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,numbersWithDots:/((\\d?\\.\\d+(?:e[+-]?\\d+)?)((?:\\.\\d+(?:e[+-]?\\d+)?)+))+/gi,dots:/\\./g},a.utils={map:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)a.push(e(t[s]));return a},filter:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)e(t[s])&&a.push(t[s]);return a},filterSVGElements:function(e){return this.filter(e,(function(e){return e instanceof t.SVGElement}))}},a.defaults={attrs:{\"fill-opacity\":1,\"stroke-opacity\":1,\"stroke-width\":0,\"stroke-linejoin\":\"miter\",\"stroke-linecap\":\"butt\",fill:\"#000000\",stroke:\"#000000\",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,\"stop-opacity\":1,\"stop-color\":\"#000000\",\"font-size\":16,\"font-family\":\"Helvetica, Arial, sans-serif\",\"text-anchor\":\"start\"}},a.Color=function(t){var e,s;this.r=0,this.g=0,this.b=0,t&&(\"string\"==typeof t?a.regex.isRgb.test(t)?(e=a.regex.rgb.exec(t.replace(a.regex.whitespace,\"\")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):a.regex.isHex.test(t)&&(e=a.regex.hex.exec(4==(s=t).length?[\"#\",s.substring(1,2),s.substring(1,2),s.substring(2,3),s.substring(2,3),s.substring(3,4),s.substring(3,4)].join(\"\"):s),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):\"object\"===i(t)&&(this.r=t.r,this.g=t.g,this.b=t.b))},a.extend(a.Color,{toString:function(){return this.toHex()},toHex:function(){return\"#\"+g(this.r)+g(this.g)+g(this.b)},toRgb:function(){return\"rgb(\"+[this.r,this.g,this.b].join()+\")\"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new a.Color(t),this},at:function(t){return this.destination?(t=t<0?0:t>1?1:t,new a.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),a.Color.test=function(t){return t+=\"\",a.regex.isHex.test(t)||a.regex.isRgb.test(t)},a.Color.isRgb=function(t){return t&&\"number\"==typeof t.r&&\"number\"==typeof t.g&&\"number\"==typeof t.b},a.Color.isColor=function(t){return a.Color.isRgb(t)||a.Color.test(t)},a.Array=function(t,e){0==(t=(t||[]).valueOf()).length&&e&&(t=e.valueOf()),this.value=this.parse(t)},a.extend(a.Array,{toString:function(){return this.value.join(\" \")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)}}),a.PointArray=function(t,e){a.Array.call(this,t,e||[[0,0]])},a.PointArray.prototype=new a.Array,a.PointArray.prototype.constructor=a.PointArray;for(var s={M:function(t,e,i){return e.x=i.x=t[0],e.y=i.y=t[1],[\"M\",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],[\"L\",t[0],t[1]]},H:function(t,e){return e.x=t[0],[\"H\",t[0]]},V:function(t,e){return e.y=t[0],[\"V\",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],[\"C\",t[0],t[1],t[2],t[3],t[4],t[5]]},Q:function(t,e){return e.x=t[2],e.y=t[3],[\"Q\",t[0],t[1],t[2],t[3]]},Z:function(t,e,i){return e.x=i.x,e.y=i.y,[\"Z\"]}},r=\"mlhvqtcsaz\".split(\"\"),o=0,n=r.length;o<n;++o)s[r[o]]=function(t){return function(e,i,a){if(\"H\"==t)e[0]=e[0]+i.x;else if(\"V\"==t)e[0]=e[0]+i.y;else if(\"A\"==t)e[5]=e[5]+i.x,e[6]=e[6]+i.y;else for(var r=0,o=e.length;r<o;++r)e[r]=e[r]+(r%2?i.y:i.x);if(s&&\"function\"==typeof s[t])return s[t](e,i,a)}}(r[o].toUpperCase());a.PathArray=function(t,e){a.Array.call(this,t,e||[[\"M\",0,0]])},a.PathArray.prototype=new a.Array,a.PathArray.prototype.constructor=a.PathArray,a.extend(a.PathArray,{toString:function(){return function(t){for(var e=0,i=t.length,a=\"\";e<i;e++)a+=t[e][0],null!=t[e][1]&&(a+=t[e][1],null!=t[e][2]&&(a+=\" \",a+=t[e][2],null!=t[e][3]&&(a+=\" \",a+=t[e][3],a+=\" \",a+=t[e][4],null!=t[e][5]&&(a+=\" \",a+=t[e][5],a+=\" \",a+=t[e][6],null!=t[e][7]&&(a+=\" \",a+=t[e][7])))));return a+\" \"}(this.value)},move:function(t,e){var i=this.bbox();return i.x,i.y,this},at:function(t){if(!this.destination)return this;for(var e=this.value,i=this.destination.value,s=[],r=new a.PathArray,o=0,n=e.length;o<n;o++){s[o]=[e[o][0]];for(var l=1,h=e[o].length;l<h;l++)s[o][l]=e[o][l]+(i[o][l]-e[o][l])*t;\"A\"===s[o][0]&&(s[o][4]=+(0!=s[o][4]),s[o][5]=+(0!=s[o][5]))}return r.value=s,r},parse:function(t){if(t instanceof a.PathArray)return t.valueOf();var e,i={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7,Z:0};t=\"string\"==typeof t?t.replace(a.regex.numbersWithDots,h).replace(a.regex.pathLetters,\" $& \").replace(a.regex.hyphen,\"$1 -\").trim().split(a.regex.delimiter):t.reduce((function(t,e){return[].concat.call(t,e)}),[]);var r=[],o=new a.Point,n=new a.Point,l=0,c=t.length;do{a.regex.isPathLetter.test(t[l])?(e=t[l],++l):\"M\"==e?e=\"L\":\"m\"==e&&(e=\"l\"),r.push(s[e].call(null,t.slice(l,l+=i[e.toUpperCase()]).map(parseFloat),o,n))}while(c>l);return r},bbox:function(){return a.parser.draw||a.prepare(),a.parser.path.setAttribute(\"d\",this.toString()),a.parser.path.getBBox()}}),a.Number=a.invent({create:function(t,e){this.value=0,this.unit=e||\"\",\"number\"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-34e37:34e37:\"string\"==typeof t?(e=t.match(a.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),\"%\"==e[5]?this.value/=100:\"s\"==e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof a.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return(\"%\"==this.unit?~~(1e8*this.value)/1e6:\"s\"==this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new a.Number(t),new a.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new a.Number(t),new a.Number(this-t,this.unit||t.unit)},times:function(t){return t=new a.Number(t),new a.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new a.Number(t),new a.Number(this/t,this.unit||t.unit)},to:function(t){var e=new a.Number(this);return\"string\"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new a.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new a.Number(this.destination).minus(this).times(t).plus(this):this}}}),a.Element=a.invent({create:function(t){this._stroke=a.defaults.attrs.stroke,this._event=null,this.dom={},(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute(\"stroke\")||this._stroke)},extend:{x:function(t){return this.attr(\"x\",t)},y:function(t){return this.attr(\"y\",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr(\"width\",t)},height:function(t){return this.attr(\"height\",t)},size:function(t,e){var i=u(this,t,e);return this.width(new a.Number(i.width)).height(new a.Number(i.height))},clone:function(t){this.writeDataToDom();var e=x(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},id:function(t){return this.attr(\"id\",t)},show:function(){return this.style(\"display\",\"\")},hide:function(){return this.style(\"display\",\"none\")},visible:function(){return\"none\"!=this.style(\"display\")},toString:function(){return this.attr(\"id\")},classes:function(){var t=this.attr(\"class\");return null==t?[]:t.trim().split(a.regex.delimiter)},hasClass:function(t){return-1!=this.classes().indexOf(t)},addClass:function(t){if(!this.hasClass(t)){var e=this.classes();e.push(t),this.attr(\"class\",e.join(\" \"))}return this},removeClass:function(t){return this.hasClass(t)&&this.attr(\"class\",this.classes().filter((function(e){return e!=t})).join(\" \")),this},toggleClass:function(t){return this.hasClass(t)?this.removeClass(t):this.addClass(t)},reference:function(t){return a.get(this.attr(t))},parent:function(e){var i=this;if(!i.node.parentNode)return null;if(i=a.adopt(i.node.parentNode),!e)return i;for(;i&&i.node instanceof t.SVGElement;){if(\"string\"==typeof e?i.matches(e):i instanceof e)return i;if(!i.node.parentNode||\"#document\"==i.node.parentNode.nodeName)return null;i=a.adopt(i.node.parentNode)}},doc:function(){return this instanceof a.Doc?this:this.parent(a.Doc)},parents:function(t){var e=[],i=this;do{if(!(i=i.parent(t))||!i.node)break;e.push(i)}while(i.parent);return e},matches:function(t){return function(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}(this.node,t)},native:function(){return this.node},svg:function(t){var i=e.createElement(\"svg\");if(!(t&&this instanceof a.Parent))return i.appendChild(t=e.createElement(\"svg\")),this.writeDataToDom(),t.appendChild(this.node.cloneNode(!0)),i.innerHTML.replace(/^<svg>/,\"\").replace(/<\\/svg>$/,\"\");i.innerHTML=\"<svg>\"+t.replace(/\\n/,\"\").replace(/<([\\w:-]+)([^<]+?)\\/>/g,\"<$1$2></$1>\")+\"</svg>\";for(var s=0,r=i.firstChild.childNodes.length;s<r;s++)this.node.appendChild(i.firstChild.firstChild);return this},writeDataToDom:function(){return(this.each||this.lines)&&(this.each?this:this.lines()).each((function(){this.writeDataToDom()})),this.node.removeAttribute(\"svgjs:data\"),Object.keys(this.dom).length&&this.node.setAttribute(\"svgjs:data\",JSON.stringify(this.dom)),this},setData:function(t){return this.dom=t,this},is:function(t){return function(t,e){return t instanceof e}(this,t)}}}),a.easing={\"-\":function(t){return t},\"<>\":function(t){return-Math.cos(t*Math.PI)/2+.5},\">\":function(t){return Math.sin(t*Math.PI/2)},\"<\":function(t){return 1-Math.cos(t*Math.PI/2)}},a.morph=function(t){return function(e,i){return new a.MorphObj(e,i).at(t)}},a.Situation=a.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new a.Number(t.duration).valueOf(),this.delay=new a.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),a.FX=a.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(t,e,s){\"object\"===i(t)&&(e=t.ease,s=t.delay,t=t.duration);var r=new a.Situation({duration:t||1e3,delay:s||0,ease:a.easing[e||\"-\"]||e});return this.queue(r),this},target:function(t){return t&&t instanceof a.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=t.requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){t.cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return(\"function\"==typeof t||t instanceof a.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof a.Situation?this.start():this.situation.call(this)),this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(var i in e.animations){t=this.target()[i](),Array.isArray(t)||(t=[t]),Array.isArray(e.animations[i])||(e.animations[i]=[e.animations[i]]);for(var s=t.length;s--;)e.animations[i][s]instanceof a.Number&&(t[s]=new a.Number(t[s])),e.animations[i][s]=t[s].morph(e.animations[i][s])}for(var i in e.attrs)e.attrs[i]=new a.MorphObj(this.target().attr(i),e.attrs[i]);for(var i in e.styles)e.styles[i]=new a.MorphObj(this.target().style(i),e.styles[i]);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){var i=this.active;return this.active=!1,e&&this.clearQueue(),t&&this.situation&&(!i&&this.startCurrent(),this.atEnd()),this.stopAnimFrame(),this.clearCurrent()},after:function(t){var e=this.last();return this.target().on(\"finished.fx\",(function i(a){a.detail.situation==e&&(t.call(this,e),this.off(\"finished.fx\",i))})),this._callStart()},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,a.morph(i.detail.pos),i.detail.eased,e)};return this.target().off(\"during.fx\",i).on(\"during.fx\",i),this.after((function(){this.off(\"during.fx\",i)})),this._callStart()},afterAll:function(t){var e=function e(i){t.call(this),this.off(\"allfinished.fx\",e)};return this.target().off(\"allfinished.fx\",e).on(\"allfinished.fx\",e),this._callStart()},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||\"animations\"][t]=e,this._callStart()},step:function(t){var e,i,a;t||(this.absPos=this.timeToAbsPos(+new Date)),!1!==this.situation.loops?(e=Math.max(this.absPos,0),i=Math.floor(e),!0===this.situation.loops||i<this.situation.loops?(this.pos=e-i,a=this.situation.loop,this.situation.loop=i):(this.absPos=this.situation.loops,this.pos=1,a=this.situation.loop-1,this.situation.loop=this.situation.loops),this.situation.reversing&&(this.situation.reversed=this.situation.reversed!=Boolean((this.situation.loop-a)%2))):(this.absPos=Math.min(this.absPos,1),this.pos=this.absPos),this.pos<0&&(this.pos=0),this.situation.reversed&&(this.pos=1-this.pos);var s=this.situation.ease(this.pos);for(var r in this.situation.once)r>this.lastPos&&r<=s&&(this.situation.once[r].call(this.target(),this.pos,s),delete this.situation.once[r]);return this.active&&this.target().fire(\"during\",{pos:this.pos,eased:s,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire(\"finished\",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire(\"allfinished\"),this.situations.length||(this.target().off(\".fx\"),this.active=!1)),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=s,this):this},eachAt:function(){var t,e=this,i=this.target(),s=this.situation;for(var r in s.animations)t=[].concat(s.animations[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i[r].apply(i,t);for(var r in s.attrs)t=[r].concat(s.attrs[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i.attr.apply(i,t);for(var r in s.styles)t=[r].concat(s.styles[r]).map((function(t){return\"string\"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i.style.apply(i,t);if(s.transforms.length){t=s.initialTransformation,r=0;for(var o=s.transforms.length;r<o;r++){var n=s.transforms[r];n instanceof a.Matrix?t=n.relative?t.multiply((new a.Matrix).morph(n).at(s.ease(this.pos))):t.morph(n).at(s.ease(this.pos)):(n.relative||n.undo(t.extract()),t=t.multiply(n.at(s.ease(this.pos))))}i.matrix(t)}return this},once:function(t,e,i){var a=this.last();return i||(t=a.ease(t)),a.once[t]=e,this},_callStart:function(){return setTimeout(function(){this.start()}.bind(this),0),this}},parent:a.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new a.FX(this))).animate(t,e,i)},delay:function(t){return(this.fx||(this.fx=new a.FX(this))).delay(t)},stop:function(t,e){return this.fx&&this.fx.stop(t,e),this},finish:function(){return this.fx&&this.fx.finish(),this}}}),a.MorphObj=a.invent({create:function(t,e){return a.Color.isColor(e)?new a.Color(t).morph(e):a.regex.delimiter.test(t)?a.regex.pathLetters.test(t)?new a.PathArray(t).morph(e):new a.Array(t).morph(e):a.regex.numberAndUnit.test(e)?new a.Number(t).morph(e):(this.value=t,void(this.destination=e))},extend:{at:function(t,e){return e<1?this.value:this.destination},valueOf:function(){return this.value}}}),a.extend(a.FX,{attr:function(t,e,a){if(\"object\"===i(t))for(var s in t)this.attr(s,t[s]);else this.add(t,e,\"attrs\");return this},plot:function(t,e,i,a){return 4==arguments.length?this.plot([t,e,i,a]):this.add(\"plot\",new(this.target().morphArray)(t))}}),a.Box=a.invent({create:function(t,e,s,r){if(!(\"object\"!==i(t)||t instanceof a.Element))return a.Box.call(this,null!=t.left?t.left:t.x,null!=t.top?t.top:t.y,t.width,t.height);4==arguments.length&&(this.x=t,this.y=e,this.width=s,this.height=r),b(this)}}),a.BBox=a.invent({create:function(t){if(a.Box.apply(this,[].slice.call(arguments)),t instanceof a.Element){var i;try{if(!e.documentElement.contains){for(var s=t.node;s.parentNode;)s=s.parentNode;if(s!=e)throw new Error(\"Element not in the dom\")}i=t.node.getBBox()}catch(e){if(t instanceof a.Shape){a.parser.draw||a.prepare();var r=t.clone(a.parser.draw.instance).show();r&&r.node&&\"function\"==typeof r.node.getBBox&&(i=r.node.getBBox()),r&&\"function\"==typeof r.remove&&r.remove()}else i={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}a.Box.call(this,i)}},inherit:a.Box,parent:a.Element,construct:{bbox:function(){return new a.BBox(this)}}}),a.BBox.prototype.constructor=a.BBox,a.Matrix=a.invent({create:function(t){var e=p([1,0,0,1,0,0]);t=null===t?e:t instanceof a.Element?t.matrixify():\"string\"==typeof t?p(t.split(a.regex.delimiter).map(parseFloat)):6==arguments.length?p([].slice.call(arguments)):Array.isArray(t)?p(t):t&&\"object\"===i(t)?t:e;for(var s=m.length-1;s>=0;--s)this[m[s]]=null!=t[m[s]]?t[m[s]]:e[m[s]]},extend:{extract:function(){var t=f(this,0,1);f(this,1,0);var e=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(e*Math.PI/180)+this.f*Math.sin(e*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(e*Math.PI/180)+this.e*Math.sin(-e*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),rotation:e,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new a.Matrix(this)}},clone:function(){return new a.Matrix(this)},morph:function(t){return this.destination=new a.Matrix(t),this},multiply:function(t){return new a.Matrix(this.native().multiply(function(t){return t instanceof a.Matrix||(t=new a.Matrix(t)),t}(t).native()))},inverse:function(){return new a.Matrix(this.native().inverse())},translate:function(t,e){return new a.Matrix(this.native().translate(t||0,e||0))},native:function(){for(var t=a.parser.native.createSVGMatrix(),e=m.length-1;e>=0;e--)t[m[e]]=this[m[e]];return t},toString:function(){return\"matrix(\"+v(this.a)+\",\"+v(this.b)+\",\"+v(this.c)+\",\"+v(this.d)+\",\"+v(this.e)+\",\"+v(this.f)+\")\"}},parent:a.Element,construct:{ctm:function(){return new a.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof a.Nested){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new a.Matrix(e)}return new a.Matrix(this.node.getScreenCTM())}}}),a.Point=a.invent({create:function(t,e){var a;a=Array.isArray(t)?{x:t[0],y:t[1]}:\"object\"===i(t)?{x:t.x,y:t.y}:null!=t?{x:t,y:null!=e?e:t}:{x:0,y:0},this.x=a.x,this.y=a.y},extend:{clone:function(){return new a.Point(this)},morph:function(t,e){return this.destination=new a.Point(t,e),this}}}),a.extend(a.Element,{point:function(t,e){return new a.Point(t,e).transform(this.screenCTM().inverse())}}),a.extend(a.Element,{attr:function(t,e,s){if(null==t){for(t={},s=(e=this.node.attributes).length-1;s>=0;s--)t[e[s].nodeName]=a.regex.isNumber.test(e[s].nodeValue)?parseFloat(e[s].nodeValue):e[s].nodeValue;return t}if(\"object\"===i(t))for(var r in t)this.attr(r,t[r]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return null==(e=this.node.getAttribute(t))?a.defaults.attrs[t]:a.regex.isNumber.test(e)?parseFloat(e):e;\"stroke-width\"==t?this.attr(\"stroke\",parseFloat(e)>0?this._stroke:null):\"stroke\"==t&&(this._stroke=e),\"fill\"!=t&&\"stroke\"!=t||(a.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof a.Image&&(e=this.doc().defs().pattern(0,0,(function(){this.add(e)})))),\"number\"==typeof e?e=new a.Number(e):a.Color.isColor(e)?e=new a.Color(e):Array.isArray(e)&&(e=new a.Array(e)),\"leading\"==t?this.leading&&this.leading(e):\"string\"==typeof s?this.node.setAttributeNS(s,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||\"font-size\"!=t&&\"x\"!=t||this.rebuild(t,e)}return this}}),a.extend(a.Element,{transform:function(t,e){var s;return\"object\"!==i(t)?(s=new a.Matrix(this).extract(),\"string\"==typeof t?s[t]:s):(s=new a.Matrix(this),e=!!e||!!t.relative,null!=t.a&&(s=e?s.multiply(new a.Matrix(t)):new a.Matrix(t)),this.attr(\"transform\",s))}}),a.extend(a.Element,{untransform:function(){return this.attr(\"transform\",null)},matrixify:function(){return(this.attr(\"transform\")||\"\").split(a.regex.transforms).slice(0,-1).map((function(t){var e=t.trim().split(\"(\");return[e[0],e[1].split(a.regex.delimiter).map((function(t){return parseFloat(t)}))]})).reduce((function(t,e){return\"matrix\"==e[0]?t.multiply(p(e[1])):t[e[0]].apply(t,e[1])}),new a.Matrix)},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.screenCTM().inverse();return this.addTo(t).untransform().transform(i.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),a.Transformation=a.invent({create:function(t,e){if(arguments.length>1&&\"boolean\"!=typeof e)return this.constructor.call(this,[].slice.call(arguments));if(Array.isArray(t))for(var a=0,s=this.arguments.length;a<s;++a)this[this.arguments[a]]=t[a];else if(t&&\"object\"===i(t))for(a=0,s=this.arguments.length;a<s;++a)this[this.arguments[a]]=t[this.arguments[a]];this.inversed=!1,!0===e&&(this.inversed=!0)}}),a.Translate=a.invent({parent:a.Matrix,inherit:a.Transformation,create:function(t,e){this.constructor.apply(this,[].slice.call(arguments))},extend:{arguments:[\"transformedX\",\"transformedY\"],method:\"translate\"}}),a.extend(a.Element,{style:function(t,e){if(0==arguments.length)return this.node.style.cssText||\"\";if(arguments.length<2)if(\"object\"===i(t))for(var s in t)this.style(s,t[s]);else{if(!a.regex.isCss.test(t))return this.node.style[c(t)];for(t=t.split(/\\s*;\\s*/).filter((function(t){return!!t})).map((function(t){return t.split(/\\s*:\\s*/)}));e=t.pop();)this.style(e[0],e[1])}else this.node.style[c(t)]=null===e||a.regex.isBlank.test(e)?\"\":e;return this}}),a.Parent=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Element,extend:{children:function(){return a.utils.map(a.utils.filterSVGElements(this.node.childNodes),(function(t){return a.adopt(t)}))},add:function(t,e){return null==e?this.node.appendChild(t.node):t.node!=this.node.childNodes[e]&&this.node.insertBefore(t.node,this.node.childNodes[e]),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return a.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){for(var i=this.children(),s=0,r=i.length;s<r;s++)i[s]instanceof a.Element&&t.apply(i[s],[s,i]),e&&i[s]instanceof a.Container&&i[s].each(t,e);return this},removeElement:function(t){return this.node.removeChild(t.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,this},defs:function(){return this.doc().defs()}}}),a.extend(a.Parent,{ungroup:function(t,e){return 0===e||this instanceof a.Defs||this.node==a.parser.draw||(t=t||(this instanceof a.Doc?this:this.parent(a.Parent)),e=e||1/0,this.each((function(){return this instanceof a.Defs?this:this instanceof a.Parent?this.ungroup(t,e-1):this.toParent(t)})),this.node.firstChild||this.remove()),this},flatten:function(t,e){return this.ungroup(t,e)}}),a.Container=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Parent}),a.ViewBox=a.invent({parent:a.Container,construct:{}}),[\"click\",\"dblclick\",\"mousedown\",\"mouseup\",\"mouseover\",\"mouseout\",\"mousemove\",\"touchstart\",\"touchmove\",\"touchleave\",\"touchend\",\"touchcancel\"].forEach((function(t){a.Element.prototype[t]=function(e){return a.on(this.node,t,e),this}})),a.listeners=[],a.handlerMap=[],a.listenerId=0,a.on=function(t,e,i,s,r){var o=i.bind(s||t.instance||t),n=(a.handlerMap.indexOf(t)+1||a.handlerMap.push(t))-1,l=e.split(\".\")[0],h=e.split(\".\")[1]||\"*\";a.listeners[n]=a.listeners[n]||{},a.listeners[n][l]=a.listeners[n][l]||{},a.listeners[n][l][h]=a.listeners[n][l][h]||{},i._svgjsListenerId||(i._svgjsListenerId=++a.listenerId),a.listeners[n][l][h][i._svgjsListenerId]=o,t.addEventListener(l,o,r||{passive:!0})},a.off=function(t,e,i){var s=a.handlerMap.indexOf(t),r=e&&e.split(\".\")[0],o=e&&e.split(\".\")[1],n=\"\";if(-1!=s)if(i){if(\"function\"==typeof i&&(i=i._svgjsListenerId),!i)return;a.listeners[s][r]&&a.listeners[s][r][o||\"*\"]&&(t.removeEventListener(r,a.listeners[s][r][o||\"*\"][i],!1),delete a.listeners[s][r][o||\"*\"][i])}else if(o&&r){if(a.listeners[s][r]&&a.listeners[s][r][o]){for(var l in a.listeners[s][r][o])a.off(t,[r,o].join(\".\"),l);delete a.listeners[s][r][o]}}else if(o)for(var h in a.listeners[s])for(var n in a.listeners[s][h])o===n&&a.off(t,[h,o].join(\".\"));else if(r){if(a.listeners[s][r]){for(var n in a.listeners[s][r])a.off(t,[r,n].join(\".\"));delete a.listeners[s][r]}}else{for(var h in a.listeners[s])a.off(t,h);delete a.listeners[s],delete a.handlerMap[s]}},a.extend(a.Element,{on:function(t,e,i,s){return a.on(this.node,t,e,i,s),this},off:function(t,e){return a.off(this.node,t,e),this},fire:function(e,i){return e instanceof t.Event?this.node.dispatchEvent(e):this.node.dispatchEvent(e=new a.CustomEvent(e,{detail:i,cancelable:!0})),this._event=e,this},event:function(){return this._event}}),a.Defs=a.invent({create:\"defs\",inherit:a.Container}),a.G=a.invent({create:\"g\",inherit:a.Container,extend:{x:function(t){return null==t?this.transform(\"x\"):this.transform({x:t-this.x()},!0)}},construct:{group:function(){return this.put(new a.G)}}}),a.Doc=a.invent({create:function(t){t&&(\"svg\"==(t=\"string\"==typeof t?e.getElementById(t):t).nodeName?this.constructor.call(this,t):(this.constructor.call(this,a.create(\"svg\")),t.appendChild(this.node),this.size(\"100%\",\"100%\")),this.namespace().defs())},inherit:a.Container,extend:{namespace:function(){return this.attr({xmlns:a.ns,version:\"1.1\"}).attr(\"xmlns:xlink\",a.xlink,a.xmlns).attr(\"xmlns:svgjs\",a.svgjs,a.xmlns)},defs:function(){var t;return this._defs||((t=this.node.getElementsByTagName(\"defs\")[0])?this._defs=a.adopt(t):this._defs=new a.Defs,this.node.appendChild(this._defs.node)),this._defs},parent:function(){return this.node.parentNode&&\"#document\"!=this.node.parentNode.nodeName?this.node.parentNode:null},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,a.parser.draw&&!a.parser.draw.parentNode&&this.node.appendChild(a.parser.draw),this},clone:function(t){this.writeDataToDom();var e=this.node,i=x(e.cloneNode(!0));return t?(t.node||t).appendChild(i.node):e.parentNode.insertBefore(i.node,e.nextSibling),i}}}),a.extend(a.Element,{}),a.Gradient=a.invent({create:function(t){this.constructor.call(this,a.create(t+\"Gradient\")),this.type=t},inherit:a.Container,extend:{at:function(t,e,i){return this.put(new a.Stop).update(t,e,i)},update:function(t){return this.clear(),\"function\"==typeof t&&t.call(this,this),this},fill:function(){return\"url(#\"+this.id()+\")\"},toString:function(){return this.fill()},attr:function(t,e,i){return\"transform\"==t&&(t=\"gradientTransform\"),a.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),a.extend(a.Gradient,a.FX,{from:function(t,e){return\"radial\"==(this._target||this).type?this.attr({fx:new a.Number(t),fy:new a.Number(e)}):this.attr({x1:new a.Number(t),y1:new a.Number(e)})},to:function(t,e){return\"radial\"==(this._target||this).type?this.attr({cx:new a.Number(t),cy:new a.Number(e)}):this.attr({x2:new a.Number(t),y2:new a.Number(e)})}}),a.extend(a.Defs,{gradient:function(t,e){return this.put(new a.Gradient(t)).update(e)}}),a.Stop=a.invent({create:\"stop\",inherit:a.Element,extend:{update:function(t){return(\"number\"==typeof t||t instanceof a.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr(\"stop-opacity\",t.opacity),null!=t.color&&this.attr(\"stop-color\",t.color),null!=t.offset&&this.attr(\"offset\",new a.Number(t.offset)),this}}}),a.Pattern=a.invent({create:\"pattern\",inherit:a.Container,extend:{fill:function(){return\"url(#\"+this.id()+\")\"},update:function(t){return this.clear(),\"function\"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return\"transform\"==t&&(t=\"patternTransform\"),a.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),a.extend(a.Defs,{pattern:function(t,e,i){return this.put(new a.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:\"userSpaceOnUse\"})}}),a.Shape=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Element}),a.Symbol=a.invent({create:\"symbol\",inherit:a.Container,construct:{symbol:function(){return this.put(new a.Symbol)}}}),a.Use=a.invent({create:\"use\",inherit:a.Shape,extend:{element:function(t,e){return this.attr(\"href\",(e||\"\")+\"#\"+t,a.xlink)}},construct:{use:function(t,e){return this.put(new a.Use).element(t,e)}}}),a.Rect=a.invent({create:\"rect\",inherit:a.Shape,construct:{rect:function(t,e){return this.put(new a.Rect).size(t,e)}}}),a.Circle=a.invent({create:\"circle\",inherit:a.Shape,construct:{circle:function(t){return this.put(new a.Circle).rx(new a.Number(t).divide(2)).move(0,0)}}}),a.extend(a.Circle,a.FX,{rx:function(t){return this.attr(\"r\",t)},ry:function(t){return this.rx(t)}}),a.Ellipse=a.invent({create:\"ellipse\",inherit:a.Shape,construct:{ellipse:function(t,e){return this.put(new a.Ellipse).size(t,e).move(0,0)}}}),a.extend(a.Ellipse,a.Rect,a.FX,{rx:function(t){return this.attr(\"rx\",t)},ry:function(t){return this.attr(\"ry\",t)}}),a.extend(a.Circle,a.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr(\"cx\"):this.attr(\"cx\",t)},cy:function(t){return null==t?this.attr(\"cy\"):this.attr(\"cy\",t)},width:function(t){return null==t?2*this.rx():this.rx(new a.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new a.Number(t).divide(2))},size:function(t,e){var i=u(this,t,e);return this.rx(new a.Number(i.width).divide(2)).ry(new a.Number(i.height).divide(2))}}),a.Line=a.invent({create:\"line\",inherit:a.Shape,extend:{array:function(){return new a.PointArray([[this.attr(\"x1\"),this.attr(\"y1\")],[this.attr(\"x2\"),this.attr(\"y2\")]])},plot:function(t,e,i,s){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:i,y2:s}:new a.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=u(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,s){return a.Line.prototype.plot.apply(this.put(new a.Line),null!=t?[t,e,i,s]:[0,0,0,0])}}}),a.Polyline=a.invent({create:\"polyline\",inherit:a.Shape,construct:{polyline:function(t){return this.put(new a.Polyline).plot(t||new a.PointArray)}}}),a.Polygon=a.invent({create:\"polygon\",inherit:a.Shape,construct:{polygon:function(t){return this.put(new a.Polygon).plot(t||new a.PointArray)}}}),a.extend(a.Polyline,a.Polygon,{array:function(){return this._array||(this._array=new a.PointArray(this.attr(\"points\")))},plot:function(t){return null==t?this.array():this.clear().attr(\"points\",\"string\"==typeof t?t:this._array=new a.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr(\"points\",this.array().move(t,e))},size:function(t,e){var i=u(this,t,e);return this.attr(\"points\",this.array().size(i.width,i.height))}}),a.extend(a.Line,a.Polyline,a.Polygon,{morphArray:a.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),a.Path=a.invent({create:\"path\",inherit:a.Shape,extend:{morphArray:a.PathArray,array:function(){return this._array||(this._array=new a.PathArray(this.attr(\"d\")))},plot:function(t){return null==t?this.array():this.clear().attr(\"d\",\"string\"==typeof t?t:this._array=new a.PathArray(t))},clear:function(){return delete this._array,this}},construct:{path:function(t){return this.put(new a.Path).plot(t||new a.PathArray)}}}),a.Image=a.invent({create:\"image\",inherit:a.Shape,extend:{load:function(e){if(!e)return this;var i=this,s=new t.Image;return a.on(s,\"load\",(function(){a.off(s);var t=i.parent(a.Pattern);null!==t&&(0==i.width()&&0==i.height()&&i.size(s.width,s.height),t&&0==t.width()&&0==t.height()&&t.size(i.width(),i.height()),\"function\"==typeof i._loaded&&i._loaded.call(i,{width:s.width,height:s.height,ratio:s.width/s.height,url:e}))})),a.on(s,\"error\",(function(t){a.off(s),\"function\"==typeof i._error&&i._error.call(i,t)})),this.attr(\"href\",s.src=this.src=e,a.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new a.Image).load(t).size(e||0,i||e||0)}}}),a.Text=a.invent({create:function(){this.constructor.call(this,a.create(\"text\")),this.dom.leading=new a.Number(1.3),this._rebuild=!0,this._build=!1,this.attr(\"font-family\",a.defaults.attrs[\"font-family\"])},inherit:a.Shape,extend:{x:function(t){return null==t?this.attr(\"x\"):this.attr(\"x\",t)},text:function(t){if(void 0===t){t=\"\";for(var e=this.node.childNodes,i=0,s=e.length;i<s;++i)0!=i&&3!=e[i].nodeType&&1==a.adopt(e[i]).dom.newLined&&(t+=\"\\n\"),t+=e[i].textContent;return t}if(this.clear().build(!0),\"function\"==typeof t)t.call(this,this);else{i=0;for(var r=(t=t.split(\"\\n\")).length;i<r;i++)this.tspan(t[i]).newLine()}return this.build(!1).rebuild()},size:function(t){return this.attr(\"font-size\",t).rebuild()},leading:function(t){return null==t?this.dom.leading:(this.dom.leading=new a.Number(t),this.rebuild())},lines:function(){var t=(this.textPath&&this.textPath()||this).node,e=a.utils.map(a.utils.filterSVGElements(t.childNodes),(function(t){return a.adopt(t)}));return new a.Set(e)},rebuild:function(t){if(\"boolean\"==typeof t&&(this._rebuild=t),this._rebuild){var e=this,i=0,s=this.dom.leading*new a.Number(this.attr(\"font-size\"));this.lines().each((function(){this.dom.newLined&&(e.textPath()||this.attr(\"x\",e.attr(\"x\")),\"\\n\"==this.text()?i+=s:(this.attr(\"dy\",s+i),i=0))})),this.fire(\"rebuild\")}return this},build:function(t){return this._build=!!t,this},setData:function(t){return this.dom=t,this.dom.leading=new a.Number(t.leading||1.3),this}},construct:{text:function(t){return this.put(new a.Text).text(t)},plain:function(t){return this.put(new a.Text).plain(t)}}}),a.Tspan=a.invent({create:\"tspan\",inherit:a.Shape,extend:{text:function(t){return null==t?this.node.textContent+(this.dom.newLined?\"\\n\":\"\"):(\"function\"==typeof t?t.call(this,this):this.plain(t),this)},dx:function(t){return this.attr(\"dx\",t)},dy:function(t){return this.attr(\"dy\",t)},newLine:function(){var t=this.parent(a.Text);return this.dom.newLined=!0,this.dy(t.dom.leading*t.attr(\"font-size\")).attr(\"x\",t.x())}}}),a.extend(a.Text,a.Tspan,{plain:function(t){return!1===this._build&&this.clear(),this.node.appendChild(e.createTextNode(t)),this},tspan:function(t){var e=(this.textPath&&this.textPath()||this).node,i=new a.Tspan;return!1===this._build&&this.clear(),e.appendChild(i.node),i.text(t)},clear:function(){for(var t=(this.textPath&&this.textPath()||this).node;t.hasChildNodes();)t.removeChild(t.lastChild);return this},length:function(){return this.node.getComputedTextLength()}}),a.TextPath=a.invent({create:\"textPath\",inherit:a.Parent,parent:a.Text,construct:{morphArray:a.PathArray,array:function(){var t=this.track();return t?t.array():null},plot:function(t){var e=this.track(),i=null;return e&&(i=e.plot(t)),null==t?i:this},track:function(){var t=this.textPath();if(t)return t.reference(\"href\")},textPath:function(){if(this.node.firstChild&&\"textPath\"==this.node.firstChild.nodeName)return a.adopt(this.node.firstChild)}}}),a.Nested=a.invent({create:function(){this.constructor.call(this,a.create(\"svg\")),this.style(\"overflow\",\"visible\")},inherit:a.Container,construct:{nested:function(){return this.put(new a.Nested)}}});var l={stroke:[\"color\",\"width\",\"opacity\",\"linecap\",\"linejoin\",\"miterlimit\",\"dasharray\",\"dashoffset\"],fill:[\"color\",\"opacity\",\"rule\"],prefix:function(t,e){return\"color\"==e?t:t+\"-\"+e}};function h(t,e,i,s){return i+s.replace(a.regex.dots,\" .\")}function c(t){return t.toLowerCase().replace(/-(.)/g,(function(t,e){return e.toUpperCase()}))}function d(t){return t.charAt(0).toUpperCase()+t.slice(1)}function g(t){var e=t.toString(16);return 1==e.length?\"0\"+e:e}function u(t,e,i){if(null==e||null==i){var a=t.bbox();null==e?e=a.width/a.height*i:null==i&&(i=a.height/a.width*e)}return{width:e,height:i}}function f(t,e,i){return{x:e*t.a+i*t.c+0,y:e*t.b+i*t.d+0}}function p(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function x(e){for(var i=e.childNodes.length-1;i>=0;i--)e.childNodes[i]instanceof t.SVGElement&&x(e.childNodes[i]);return a.adopt(e).id(a.eid(e.nodeName))}function b(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function v(t){return Math.abs(t)>1e-37?t:0}[\"fill\",\"stroke\"].forEach((function(t){var e={};e[t]=function(e){if(void 0===e)return this;if(\"string\"==typeof e||a.Color.isRgb(e)||e&&\"function\"==typeof e.fill)this.attr(t,e);else for(var i=l[t].length-1;i>=0;i--)null!=e[l[t][i]]&&this.attr(l.prefix(t,l[t][i]),e[l[t][i]]);return this},a.extend(a.Element,a.FX,e)})),a.extend(a.Element,a.FX,{translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.attr(\"transform\",new a.Matrix(6==arguments.length?[].slice.call(arguments):t))},opacity:function(t){return this.attr(\"opacity\",t)},dx:function(t){return this.x(new a.Number(t).plus(this instanceof a.FX?0:this.x()),!0)},dy:function(t){return this.y(new a.Number(t).plus(this instanceof a.FX?0:this.y()),!0)}}),a.extend(a.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),a.Set=a.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){for(var t=[].slice.call(arguments),e=0,i=t.length;e<i;e++)this.members.push(t[e]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e<i;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},length:function(){return this.members.length},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members}},construct:{set:function(t){return new a.Set(t)}}}),a.FX.Set=a.invent({create:function(t){this.set=t}}),a.Set.inherit=function(){var t=[];for(var e in a.Shape.prototype)\"function\"==typeof a.Shape.prototype[e]&&\"function\"!=typeof a.Set.prototype[e]&&t.push(e);for(var e in t.forEach((function(t){a.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e<i;e++)this.members[e]&&\"function\"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return\"animate\"==t?this.fx||(this.fx=new a.FX.Set(this)):this}})),t=[],a.FX.prototype)\"function\"==typeof a.FX.prototype[e]&&\"function\"!=typeof a.FX.Set.prototype[e]&&t.push(e);t.forEach((function(t){a.FX.Set.prototype[t]=function(){for(var e=0,i=this.set.members.length;e<i;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}}))},a.extend(a.Element,{}),a.extend(a.Element,{remember:function(t,e){if(\"object\"===i(arguments[0]))for(var a in t)this.remember(a,t[a]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),a.get=function(t){var i=e.getElementById(function(t){var e=(t||\"\").toString().match(a.regex.reference);if(e)return e[1]}(t)||t);return a.adopt(i)},a.select=function(t,i){return new a.Set(a.utils.map((i||e).querySelectorAll(t),(function(t){return a.adopt(t)})))},a.extend(a.Parent,{select:function(t){return a.select(t,this.node)}});var m=\"abcdef\".split(\"\");if(\"function\"!=typeof t.CustomEvent){var y=function(t,i){i=i||{bubbles:!1,cancelable:!1,detail:void 0};var a=e.createEvent(\"CustomEvent\");return a.initCustomEvent(t,i.bubbles,i.cancelable,i.detail),a};y.prototype=t.Event.prototype,a.CustomEvent=y}else a.CustomEvent=t.CustomEvent;return a},\"function\"==typeof define&&define.amd?define((function(){return Et(Xt,Xt.document)})):\"object\"===(\"undefined\"==typeof exports?\"undefined\":i(exports))&&\"undefined\"!=typeof module?module.exports=Xt.document?Et(Xt,Xt.document):function(t){return Et(t,t.document)}:Xt.SVG=Et(Xt,Xt.document),\n/*! svg.filter.js - v2.0.2 - 2016-02-24\n* https://github.com/wout/svg.filter.js\n* Copyright (c) 2016 Wout Fierens; Licensed MIT */\nfunction(){SVG.Filter=SVG.invent({create:\"filter\",inherit:SVG.Parent,extend:{source:\"SourceGraphic\",sourceAlpha:\"SourceAlpha\",background:\"BackgroundImage\",backgroundAlpha:\"BackgroundAlpha\",fill:\"FillPaint\",stroke:\"StrokePaint\",autoSetIn:!0,put:function(t,e){return this.add(t,e),!t.attr(\"in\")&&this.autoSetIn&&t.attr(\"in\",this.source),t.attr(\"result\")||t.attr(\"result\",t),t},blend:function(t,e,i){return this.put(new SVG.BlendEffect(t,e,i))},colorMatrix:function(t,e){return this.put(new SVG.ColorMatrixEffect(t,e))},convolveMatrix:function(t){return this.put(new SVG.ConvolveMatrixEffect(t))},componentTransfer:function(t){return this.put(new SVG.ComponentTransferEffect(t))},composite:function(t,e,i){return this.put(new SVG.CompositeEffect(t,e,i))},flood:function(t,e){return this.put(new SVG.FloodEffect(t,e))},offset:function(t,e){return this.put(new SVG.OffsetEffect(t,e))},image:function(t){return this.put(new SVG.ImageEffect(t))},merge:function(){var t=[void 0];for(var e in arguments)t.push(arguments[e]);return this.put(new(SVG.MergeEffect.bind.apply(SVG.MergeEffect,t)))},gaussianBlur:function(t,e){return this.put(new SVG.GaussianBlurEffect(t,e))},morphology:function(t,e){return this.put(new SVG.MorphologyEffect(t,e))},diffuseLighting:function(t,e,i){return this.put(new SVG.DiffuseLightingEffect(t,e,i))},displacementMap:function(t,e,i,a,s){return this.put(new SVG.DisplacementMapEffect(t,e,i,a,s))},specularLighting:function(t,e,i,a){return this.put(new SVG.SpecularLightingEffect(t,e,i,a))},tile:function(){return this.put(new SVG.TileEffect)},turbulence:function(t,e,i,a,s){return this.put(new SVG.TurbulenceEffect(t,e,i,a,s))},toString:function(){return\"url(#\"+this.attr(\"id\")+\")\"}}}),SVG.extend(SVG.Defs,{filter:function(t){var e=this.put(new SVG.Filter);return\"function\"==typeof t&&t.call(e,e),e}}),SVG.extend(SVG.Container,{filter:function(t){return this.defs().filter(t)}}),SVG.extend(SVG.Element,SVG.G,SVG.Nested,{filter:function(t){return this.filterer=t instanceof SVG.Element?t:this.doc().filter(t),this.doc()&&this.filterer.doc()!==this.doc()&&this.doc().defs().add(this.filterer),this.attr(\"filter\",this.filterer),this.filterer},unfilter:function(t){return this.filterer&&!0===t&&this.filterer.remove(),delete this.filterer,this.attr(\"filter\",null)}}),SVG.Effect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in\")+'\"]').get(0)||this.attr(\"in\"):this.attr(\"in\",t)},result:function(t){return null==t?this.attr(\"result\"):this.attr(\"result\",t)},toString:function(){return this.result()}}}),SVG.ParentEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Parent,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in\")+'\"]').get(0)||this.attr(\"in\"):this.attr(\"in\",t)},result:function(t){return null==t?this.attr(\"result\"):this.attr(\"result\",t)},toString:function(){return this.result()}}});var t={blend:function(t,e){return this.parent()&&this.parent().blend(this,t,e)},colorMatrix:function(t,e){return this.parent()&&this.parent().colorMatrix(t,e).in(this)},convolveMatrix:function(t){return this.parent()&&this.parent().convolveMatrix(t).in(this)},componentTransfer:function(t){return this.parent()&&this.parent().componentTransfer(t).in(this)},composite:function(t,e){return this.parent()&&this.parent().composite(this,t,e)},flood:function(t,e){return this.parent()&&this.parent().flood(t,e)},offset:function(t,e){return this.parent()&&this.parent().offset(t,e).in(this)},image:function(t){return this.parent()&&this.parent().image(t)},merge:function(){return this.parent()&&this.parent().merge.apply(this.parent(),[this].concat(arguments))},gaussianBlur:function(t,e){return this.parent()&&this.parent().gaussianBlur(t,e).in(this)},morphology:function(t,e){return this.parent()&&this.parent().morphology(t,e).in(this)},diffuseLighting:function(t,e,i){return this.parent()&&this.parent().diffuseLighting(t,e,i).in(this)},displacementMap:function(t,e,i,a){return this.parent()&&this.parent().displacementMap(this,t,e,i,a)},specularLighting:function(t,e,i,a){return this.parent()&&this.parent().specularLighting(t,e,i,a).in(this)},tile:function(){return this.parent()&&this.parent().tile().in(this)},turbulence:function(t,e,i,a,s){return this.parent()&&this.parent().turbulence(t,e,i,a,s).in(this)}};SVG.extend(SVG.Effect,t),SVG.extend(SVG.ParentEffect,t),SVG.ChildEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){this.attr(\"in\",t)}}});var e={blend:function(t,e,i){this.attr({in:t,in2:e,mode:i||\"normal\"})},colorMatrix:function(t,e){\"matrix\"==t&&(e=s(e)),this.attr({type:t,values:void 0===e?null:e})},convolveMatrix:function(t){t=s(t),this.attr({order:Math.sqrt(t.split(\" \").length),kernelMatrix:t})},composite:function(t,e,i){this.attr({in:t,in2:e,operator:i})},flood:function(t,e){this.attr(\"flood-color\",t),null!=e&&this.attr(\"flood-opacity\",e)},offset:function(t,e){this.attr({dx:t,dy:e})},image:function(t){this.attr(\"href\",t,SVG.xlink)},displacementMap:function(t,e,i,a,s){this.attr({in:t,in2:e,scale:i,xChannelSelector:a,yChannelSelector:s})},gaussianBlur:function(t,e){null!=t||null!=e?this.attr(\"stdDeviation\",r(Array.prototype.slice.call(arguments))):this.attr(\"stdDeviation\",\"0 0\")},morphology:function(t,e){this.attr({operator:t,radius:e})},tile:function(){},turbulence:function(t,e,i,a,s){this.attr({numOctaves:e,seed:i,stitchTiles:a,baseFrequency:t,type:s})}},i={merge:function(){var t;if(arguments[0]instanceof SVG.Set){var e=this;arguments[0].each((function(t){this instanceof SVG.MergeNode?e.put(this):(this instanceof SVG.Effect||this instanceof SVG.ParentEffect)&&e.put(new SVG.MergeNode(this))}))}else{t=Array.isArray(arguments[0])?arguments[0]:arguments;for(var i=0;i<t.length;i++)t[i]instanceof SVG.MergeNode?this.put(t[i]):this.put(new SVG.MergeNode(t[i]))}},componentTransfer:function(t){if(this.rgb=new SVG.Set,[\"r\",\"g\",\"b\",\"a\"].forEach(function(t){this[t]=new(SVG[\"Func\"+t.toUpperCase()])(\"identity\"),this.rgb.add(this[t]),this.node.appendChild(this[t].node)}.bind(this)),t)for(var e in t.rgb&&([\"r\",\"g\",\"b\"].forEach(function(e){this[e].attr(t.rgb)}.bind(this)),delete t.rgb),t)this[e].attr(t[e])},diffuseLighting:function(t,e,i){this.attr({surfaceScale:t,diffuseConstant:e,kernelUnitLength:i})},specularLighting:function(t,e,i,a){this.attr({surfaceScale:t,diffuseConstant:e,specularExponent:i,kernelUnitLength:a})}},a={distantLight:function(t,e){this.attr({azimuth:t,elevation:e})},pointLight:function(t,e,i){this.attr({x:t,y:e,z:i})},spotLight:function(t,e,i,a,s,r){this.attr({x:t,y:e,z:i,pointsAtX:a,pointsAtY:s,pointsAtZ:r})},mergeNode:function(t){this.attr(\"in\",t)}};function s(t){return Array.isArray(t)&&(t=new SVG.Array(t)),t.toString().replace(/^\\s+/,\"\").replace(/\\s+$/,\"\").replace(/\\s+/g,\" \")}function r(t){if(!Array.isArray(t))return t;for(var e=0,i=t.length,a=[];e<i;e++)a.push(t[e]);return a.join(\" \")}function o(){var t=function(){};for(var e in\"function\"==typeof arguments[arguments.length-1]&&(t=arguments[arguments.length-1],Array.prototype.splice.call(arguments,arguments.length-1,1)),arguments)for(var i in arguments[e])t(arguments[e][i],i,arguments[e])}[\"r\",\"g\",\"b\",\"a\"].forEach((function(t){a[\"Func\"+t.toUpperCase()]=function(t){switch(this.attr(\"type\",t),t){case\"table\":this.attr(\"tableValues\",arguments[1]);break;case\"linear\":this.attr(\"slope\",arguments[1]),this.attr(\"intercept\",arguments[2]);break;case\"gamma\":this.attr(\"amplitude\",arguments[1]),this.attr(\"exponent\",arguments[2]),this.attr(\"offset\",arguments[2])}}})),o(e,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+\"Effect\"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments),this.result(this.attr(\"id\")+\"Out\")},inherit:SVG.Effect,extend:{}})})),o(i,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+\"Effect\"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments),this.result(this.attr(\"id\")+\"Out\")},inherit:SVG.ParentEffect,extend:{}})})),o(a,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i]=SVG.invent({create:function(){this.constructor.call(this,SVG.create(\"fe\"+i)),t.apply(this,arguments)},inherit:SVG.ChildEffect,extend:{}})})),SVG.extend(SVG.MergeEffect,{in:function(t){return t instanceof SVG.MergeNode?this.add(t,0):this.add(new SVG.MergeNode(t),0),this}}),SVG.extend(SVG.CompositeEffect,SVG.BlendEffect,SVG.DisplacementMapEffect,{in2:function(t){return null==t?this.parent()&&this.parent().select('[result=\"'+this.attr(\"in2\")+'\"]').get(0)||this.attr(\"in2\"):this.attr(\"in2\",t)}}),SVG.filter={sepiatone:[.343,.669,.119,0,0,.249,.626,.13,0,0,.172,.334,.111,0,0,0,0,0,1,0]}}.call(void 0),function(){function t(t,s,r,o,n,l,h){for(var c=t.slice(s,r||h),d=o.slice(n,l||h),g=0,u={pos:[0,0],start:[0,0]},f={pos:[0,0],start:[0,0]};;){if(c[g]=e.call(u,c[g]),d[g]=e.call(f,d[g]),c[g][0]!=d[g][0]||\"M\"==c[g][0]||\"A\"==c[g][0]&&(c[g][4]!=d[g][4]||c[g][5]!=d[g][5])?(Array.prototype.splice.apply(c,[g,1].concat(a.call(u,c[g]))),Array.prototype.splice.apply(d,[g,1].concat(a.call(f,d[g])))):(c[g]=i.call(u,c[g]),d[g]=i.call(f,d[g])),++g==c.length&&g==d.length)break;g==c.length&&c.push([\"C\",u.pos[0],u.pos[1],u.pos[0],u.pos[1],u.pos[0],u.pos[1]]),g==d.length&&d.push([\"C\",f.pos[0],f.pos[1],f.pos[0],f.pos[1],f.pos[0],f.pos[1]])}return{start:c,dest:d}}function e(t){switch(t[0]){case\"z\":case\"Z\":t[0]=\"L\",t[1]=this.start[0],t[2]=this.start[1];break;case\"H\":t[0]=\"L\",t[2]=this.pos[1];break;case\"V\":t[0]=\"L\",t[2]=t[1],t[1]=this.pos[0];break;case\"T\":t[0]=\"Q\",t[3]=t[1],t[4]=t[2],t[1]=this.reflection[1],t[2]=this.reflection[0];break;case\"S\":t[0]=\"C\",t[6]=t[4],t[5]=t[3],t[4]=t[2],t[3]=t[1],t[2]=this.reflection[1],t[1]=this.reflection[0]}return t}function i(t){var e=t.length;return this.pos=[t[e-2],t[e-1]],-1!=\"SCQT\".indexOf(t[0])&&(this.reflection=[2*this.pos[0]-t[e-4],2*this.pos[1]-t[e-3]]),t}function a(t){var e=[t];switch(t[0]){case\"M\":return this.pos=this.start=[t[1],t[2]],e;case\"L\":t[5]=t[3]=t[1],t[6]=t[4]=t[2],t[1]=this.pos[0],t[2]=this.pos[1];break;case\"Q\":t[6]=t[4],t[5]=t[3],t[4]=1*t[4]/3+2*t[2]/3,t[3]=1*t[3]/3+2*t[1]/3,t[2]=1*this.pos[1]/3+2*t[2]/3,t[1]=1*this.pos[0]/3+2*t[1]/3;break;case\"A\":e=function(t,e){var i,a,s,r,o,n,l,h,c,d,g,u,f,p,x,b,v,m,y,w,k,A,S,C,L,P,T=Math.abs(e[1]),M=Math.abs(e[2]),I=e[3]%360,z=e[4],X=e[5],E=e[6],Y=e[7],F=new SVG.Point(t),R=new SVG.Point(E,Y),D=[];if(0===T||0===M||F.x===R.x&&F.y===R.y)return[[\"C\",F.x,F.y,R.x,R.y,R.x,R.y]];i=new SVG.Point((F.x-R.x)/2,(F.y-R.y)/2).transform((new SVG.Matrix).rotate(I)),(a=i.x*i.x/(T*T)+i.y*i.y/(M*M))>1&&(T*=a=Math.sqrt(a),M*=a);s=(new SVG.Matrix).rotate(I).scale(1/T,1/M).rotate(-I),F=F.transform(s),R=R.transform(s),r=[R.x-F.x,R.y-F.y],n=r[0]*r[0]+r[1]*r[1],o=Math.sqrt(n),r[0]/=o,r[1]/=o,l=n<4?Math.sqrt(1-n/4):0,z===X&&(l*=-1);h=new SVG.Point((R.x+F.x)/2+l*-r[1],(R.y+F.y)/2+l*r[0]),c=new SVG.Point(F.x-h.x,F.y-h.y),d=new SVG.Point(R.x-h.x,R.y-h.y),g=Math.acos(c.x/Math.sqrt(c.x*c.x+c.y*c.y)),c.y<0&&(g*=-1);u=Math.acos(d.x/Math.sqrt(d.x*d.x+d.y*d.y)),d.y<0&&(u*=-1);X&&g>u&&(u+=2*Math.PI);!X&&g<u&&(u-=2*Math.PI);for(p=Math.ceil(2*Math.abs(g-u)/Math.PI),b=[],v=g,f=(u-g)/p,x=4*Math.tan(f/4)/3,k=0;k<=p;k++)y=Math.cos(v),m=Math.sin(v),w=new SVG.Point(h.x+y,h.y+m),b[k]=[new SVG.Point(w.x+x*m,w.y-x*y),w,new SVG.Point(w.x-x*m,w.y+x*y)],v+=f;for(b[0][0]=b[0][1].clone(),b[b.length-1][2]=b[b.length-1][1].clone(),s=(new SVG.Matrix).rotate(I).scale(T,M).rotate(-I),k=0,A=b.length;k<A;k++)b[k][0]=b[k][0].transform(s),b[k][1]=b[k][1].transform(s),b[k][2]=b[k][2].transform(s);for(k=1,A=b.length;k<A;k++)S=(w=b[k-1][2]).x,C=w.y,L=(w=b[k][0]).x,P=w.y,E=(w=b[k][1]).x,Y=w.y,D.push([\"C\",S,C,L,P,E,Y]);return D}(this.pos,t),t=e[0]}return t[0]=\"C\",this.pos=[t[5],t[6]],this.reflection=[2*t[5]-t[3],2*t[6]-t[4]],e}function s(t,e){if(!1===e)return!1;for(var i=e,a=t.length;i<a;++i)if(\"M\"==t[i][0])return i;return!1}SVG.extend(SVG.PathArray,{morph:function(e){for(var i=this.value,a=this.parse(e),r=0,o=0,n=!1,l=!1;!1!==r||!1!==o;){var h;n=s(i,!1!==r&&r+1),l=s(a,!1!==o&&o+1),!1===r&&(r=0==(h=new SVG.PathArray(c.start).bbox()).height||0==h.width?i.push(i[0])-1:i.push([\"M\",h.x+h.width/2,h.y+h.height/2])-1),!1===o&&(o=0==(h=new SVG.PathArray(c.dest).bbox()).height||0==h.width?a.push(a[0])-1:a.push([\"M\",h.x+h.width/2,h.y+h.height/2])-1);var c=t(i,r,n,a,o,l);i=i.slice(0,r).concat(c.start,!1===n?[]:i.slice(n)),a=a.slice(0,o).concat(c.dest,!1===l?[]:a.slice(l)),r=!1!==n&&r+c.start.length,o=!1!==l&&o+c.dest.length}return this.value=i,this.destination=new SVG.PathArray,this.destination.value=a,this}})}(),\n/*! svg.draggable.js - v2.2.2 - 2019-01-08\n* https://github.com/svgdotjs/svg.draggable.js\n* Copyright (c) 2019 Wout Fierens; Licensed MIT */\nfunction(){function t(t){t.remember(\"_draggable\",this),this.el=t}t.prototype.init=function(t,e){var i=this;this.constraint=t,this.value=e,this.el.on(\"mousedown.drag\",(function(t){i.start(t)})),this.el.on(\"touchstart.drag\",(function(t){i.start(t)}))},t.prototype.transformPoint=function(t,e){var i=(t=t||window.event).changedTouches&&t.changedTouches[0]||t;return this.p.x=i.clientX-(e||0),this.p.y=i.clientY,this.p.matrixTransform(this.m)},t.prototype.getBBox=function(){var t=this.el.bbox();return this.el instanceof SVG.Nested&&(t=this.el.rbox()),(this.el instanceof SVG.G||this.el instanceof SVG.Use||this.el instanceof SVG.Nested)&&(t.x=this.el.x(),t.y=this.el.y()),t},t.prototype.start=function(t){if(\"click\"!=t.type&&\"mousedown\"!=t.type&&\"mousemove\"!=t.type||1==(t.which||t.buttons)){var e=this;if(this.el.fire(\"beforedrag\",{event:t,handler:this}),!this.el.event().defaultPrevented){t.preventDefault(),t.stopPropagation(),this.parent=this.parent||this.el.parent(SVG.Nested)||this.el.parent(SVG.Doc),this.p=this.parent.node.createSVGPoint(),this.m=this.el.node.getScreenCTM().inverse();var i,a=this.getBBox();if(this.el instanceof SVG.Text)switch(i=this.el.node.getComputedTextLength(),this.el.attr(\"text-anchor\")){case\"middle\":i/=2;break;case\"start\":i=0}this.startPoints={point:this.transformPoint(t,i),box:a,transform:this.el.transform()},SVG.on(window,\"mousemove.drag\",(function(t){e.drag(t)})),SVG.on(window,\"touchmove.drag\",(function(t){e.drag(t)})),SVG.on(window,\"mouseup.drag\",(function(t){e.end(t)})),SVG.on(window,\"touchend.drag\",(function(t){e.end(t)})),this.el.fire(\"dragstart\",{event:t,p:this.startPoints.point,m:this.m,handler:this})}}},t.prototype.drag=function(t){var e=this.getBBox(),i=this.transformPoint(t),a=this.startPoints.box.x+i.x-this.startPoints.point.x,s=this.startPoints.box.y+i.y-this.startPoints.point.y,r=this.constraint,o=i.x-this.startPoints.point.x,n=i.y-this.startPoints.point.y;if(this.el.fire(\"dragmove\",{event:t,p:i,m:this.m,handler:this}),this.el.event().defaultPrevented)return i;if(\"function\"==typeof r){var l=r.call(this.el,a,s,this.m);\"boolean\"==typeof l&&(l={x:l,y:l}),!0===l.x?this.el.x(a):!1!==l.x&&this.el.x(l.x),!0===l.y?this.el.y(s):!1!==l.y&&this.el.y(l.y)}else\"object\"==typeof r&&(null!=r.minX&&a<r.minX?o=(a=r.minX)-this.startPoints.box.x:null!=r.maxX&&a>r.maxX-e.width&&(o=(a=r.maxX-e.width)-this.startPoints.box.x),null!=r.minY&&s<r.minY?n=(s=r.minY)-this.startPoints.box.y:null!=r.maxY&&s>r.maxY-e.height&&(n=(s=r.maxY-e.height)-this.startPoints.box.y),null!=r.snapToGrid&&(a-=a%r.snapToGrid,s-=s%r.snapToGrid,o-=o%r.snapToGrid,n-=n%r.snapToGrid),this.el instanceof SVG.G?this.el.matrix(this.startPoints.transform).transform({x:o,y:n},!0):this.el.move(a,s));return i},t.prototype.end=function(t){var e=this.drag(t);this.el.fire(\"dragend\",{event:t,p:e,m:this.m,handler:this}),SVG.off(window,\"mousemove.drag\"),SVG.off(window,\"touchmove.drag\"),SVG.off(window,\"mouseup.drag\"),SVG.off(window,\"touchend.drag\")},SVG.extend(SVG.Element,{draggable:function(e,i){\"function\"!=typeof e&&\"object\"!=typeof e||(i=e,e=!0);var a=this.remember(\"_draggable\")||new t(this);return(e=void 0===e||e)?a.init(i||{},e):(this.off(\"mousedown.drag\"),this.off(\"touchstart.drag\")),this}})}.call(void 0),function(){function t(t){this.el=t,t.remember(\"_selectHandler\",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1},this.pointsList={lt:[0,0],rt:[\"width\",0],rb:[\"width\",\"height\"],lb:[0,\"height\"],t:[\"width\",0],r:[\"width\",\"height\"],b:[\"width\",\"height\"],l:[0,\"height\"]},this.pointCoord=function(t,e,i){var a=\"string\"!=typeof t?t:e[t];return i?a/2:a},this.pointCoords=function(t,e){var i=this.pointsList[t];return{x:this.pointCoord(i[0],e,\"t\"===t||\"b\"===t),y:this.pointCoord(i[1],e,\"r\"===t||\"l\"===t)}}}t.prototype.init=function(t,e){var i=this.el.bbox();this.options={};var a=this.el.selectize.defaults.points;for(var s in this.el.selectize.defaults)this.options[s]=this.el.selectize.defaults[s],void 0!==e[s]&&(this.options[s]=e[s]);var r=[\"points\",\"pointsExclude\"];for(var s in r){var o=this.options[r[s]];\"string\"==typeof o?o=o.length>0?o.split(/\\s*,\\s*/i):[]:\"boolean\"==typeof o&&\"points\"===r[s]&&(o=o?a:[]),this.options[r[s]]=o}this.options.points=[a,this.options.points].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)>-1}))})),this.options.points=[this.options.points,this.options.pointsExclude].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)<0}))})),this.parent=this.el.parent(),this.nested=this.nested||this.parent.group(),this.nested.matrix(new SVG.Matrix(this.el).translate(i.x,i.y)),this.options.deepSelect&&-1!==[\"line\",\"polyline\",\"polygon\"].indexOf(this.el.type)?this.selectPoints(t):this.selectRect(t),this.observe(),this.cleanup()},t.prototype.selectPoints=function(t){return this.pointSelection.isSelected=t,this.pointSelection.set||(this.pointSelection.set=this.parent.set(),this.drawPoints()),this},t.prototype.getPointArray=function(){var t=this.el.bbox();return this.el.array().valueOf().map((function(e){return[e[0]-t.x,e[1]-t.y]}))},t.prototype.drawPoints=function(){for(var t=this,e=this.getPointArray(),i=0,a=e.length;i<a;++i){var s=function(e){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;t.el.fire(\"point\",{x:a,y:s,i:e,event:i})}}(i),r=this.drawPoint(e[i][0],e[i][1]).addClass(this.options.classPoints).addClass(this.options.classPoints+\"_point\").on(\"touchstart\",s).on(\"mousedown\",s);this.pointSelection.set.add(r)}},t.prototype.drawPoint=function(t,e){var i=this.options.pointType;switch(i){case\"circle\":return this.drawCircle(t,e);case\"rect\":return this.drawRect(t,e);default:if(\"function\"==typeof i)return i.call(this,t,e);throw new Error(\"Unknown \"+i+\" point type!\")}},t.prototype.drawCircle=function(t,e){return this.nested.circle(this.options.pointSize).center(t,e)},t.prototype.drawRect=function(t,e){return this.nested.rect(this.options.pointSize,this.options.pointSize).center(t,e)},t.prototype.updatePointSelection=function(){var t=this.getPointArray();this.pointSelection.set.each((function(e){this.cx()===t[e][0]&&this.cy()===t[e][1]||this.center(t[e][0],t[e][1])}))},t.prototype.updateRectSelection=function(){var t=this,e=this.el.bbox();if(this.rectSelection.set.get(0).attr({width:e.width,height:e.height}),this.options.points.length&&this.options.points.map((function(i,a){var s=t.pointCoords(i,e);t.rectSelection.set.get(a+1).center(s.x,s.y)})),this.options.rotationPoint){var i=this.rectSelection.set.length();this.rectSelection.set.get(i-1).center(e.width/2,20)}},t.prototype.selectRect=function(t){var e=this,i=this.el.bbox();function a(t){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;e.el.fire(t,{x:a,y:s,event:i})}}if(this.rectSelection.isSelected=t,this.rectSelection.set=this.rectSelection.set||this.parent.set(),this.rectSelection.set.get(0)||this.rectSelection.set.add(this.nested.rect(i.width,i.height).addClass(this.options.classRect)),this.options.points.length&&this.rectSelection.set.length()<2){this.options.points.map((function(t,s){var r=e.pointCoords(t,i),o=e.drawPoint(r.x,r.y).attr(\"class\",e.options.classPoints+\"_\"+t).on(\"mousedown\",a(t)).on(\"touchstart\",a(t));e.rectSelection.set.add(o)})),this.rectSelection.set.each((function(){this.addClass(e.options.classPoints)}))}if(this.options.rotationPoint&&(this.options.points&&!this.rectSelection.set.get(9)||!this.options.points&&!this.rectSelection.set.get(1))){var s=function(t){(t=t||window.event).preventDefault?t.preventDefault():t.returnValue=!1,t.stopPropagation();var i=t.pageX||t.touches[0].pageX,a=t.pageY||t.touches[0].pageY;e.el.fire(\"rot\",{x:i,y:a,event:t})},r=this.drawPoint(i.width/2,20).attr(\"class\",this.options.classPoints+\"_rot\").on(\"touchstart\",s).on(\"mousedown\",s);this.rectSelection.set.add(r)}},t.prototype.handler=function(){var t=this.el.bbox();this.nested.matrix(new SVG.Matrix(this.el).translate(t.x,t.y)),this.rectSelection.isSelected&&this.updateRectSelection(),this.pointSelection.isSelected&&this.updatePointSelection()},t.prototype.observe=function(){var t=this;if(MutationObserver)if(this.rectSelection.isSelected||this.pointSelection.isSelected)this.observerInst=this.observerInst||new MutationObserver((function(){t.handler()})),this.observerInst.observe(this.el.node,{attributes:!0});else try{this.observerInst.disconnect(),delete this.observerInst}catch(t){}else this.el.off(\"DOMAttrModified.select\"),(this.rectSelection.isSelected||this.pointSelection.isSelected)&&this.el.on(\"DOMAttrModified.select\",(function(){t.handler()}))},t.prototype.cleanup=function(){!this.rectSelection.isSelected&&this.rectSelection.set&&(this.rectSelection.set.each((function(){this.remove()})),this.rectSelection.set.clear(),delete this.rectSelection.set),!this.pointSelection.isSelected&&this.pointSelection.set&&(this.pointSelection.set.each((function(){this.remove()})),this.pointSelection.set.clear(),delete this.pointSelection.set),this.pointSelection.isSelected||this.rectSelection.isSelected||(this.nested.remove(),delete this.nested)},SVG.extend(SVG.Element,{selectize:function(e,i){return\"object\"==typeof e&&(i=e,e=!0),(this.remember(\"_selectHandler\")||new t(this)).init(void 0===e||e,i||{}),this}}),SVG.Element.prototype.selectize.defaults={points:[\"lt\",\"rt\",\"rb\",\"lb\",\"t\",\"r\",\"b\",\"l\"],pointsExclude:[],classRect:\"svg_select_boundingRect\",classPoints:\"svg_select_points\",pointSize:7,rotationPoint:!0,deepSelect:!1,pointType:\"circle\"}}(),function(){(function(){function t(t){t.remember(\"_resizeHandler\",this),this.el=t,this.parameters={},this.lastUpdateCall=null,this.p=t.doc().node.createSVGPoint()}t.prototype.transformPoint=function(t,e,i){return this.p.x=t-(this.offset.x-window.pageXOffset),this.p.y=e-(this.offset.y-window.pageYOffset),this.p.matrixTransform(i||this.m)},t.prototype._extractPosition=function(t){return{x:null!=t.clientX?t.clientX:t.touches[0].clientX,y:null!=t.clientY?t.clientY:t.touches[0].clientY}},t.prototype.init=function(t){var e=this;if(this.stop(),\"stop\"!==t){for(var i in this.options={},this.el.resize.defaults)this.options[i]=this.el.resize.defaults[i],void 0!==t[i]&&(this.options[i]=t[i]);this.el.on(\"lt.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rt.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rb.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"lb.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"t.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"r.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"b.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"l.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"rot.resize\",(function(t){e.resize(t||window.event)})),this.el.on(\"point.resize\",(function(t){e.resize(t||window.event)})),this.update()}},t.prototype.stop=function(){return this.el.off(\"lt.resize\"),this.el.off(\"rt.resize\"),this.el.off(\"rb.resize\"),this.el.off(\"lb.resize\"),this.el.off(\"t.resize\"),this.el.off(\"r.resize\"),this.el.off(\"b.resize\"),this.el.off(\"l.resize\"),this.el.off(\"rot.resize\"),this.el.off(\"point.resize\"),this},t.prototype.resize=function(t){var e=this;this.m=this.el.node.getScreenCTM().inverse(),this.offset={x:window.pageXOffset,y:window.pageYOffset};var i=this._extractPosition(t.detail.event);if(this.parameters={type:this.el.type,p:this.transformPoint(i.x,i.y),x:t.detail.x,y:t.detail.y,box:this.el.bbox(),rotation:this.el.transform().rotation},\"text\"===this.el.type&&(this.parameters.fontSize=this.el.attr()[\"font-size\"]),void 0!==t.detail.i){var a=this.el.array().valueOf();this.parameters.i=t.detail.i,this.parameters.pointCoords=[a[t.detail.i][0],a[t.detail.i][1]]}switch(t.type){case\"lt\":this.calc=function(t,e){var i=this.snapToGrid(t,e);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y+i[1]).size(this.parameters.box.width-i[0],this.parameters.box.height-i[1])}};break;case\"rt\":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).size(this.parameters.box.width+i[0],this.parameters.box.height-i[1])}};break;case\"rb\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x,this.parameters.box.y).size(this.parameters.box.width+i[0],this.parameters.box.height+i[1])}};break;case\"lb\":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr(\"font-size\",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).size(this.parameters.box.width-i[0],this.parameters.box.height+i[1])}};break;case\"t\":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.height-i[1]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).height(this.parameters.box.height-i[1])}};break;case\"r\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).width(this.parameters.box.width+i[0])}};break;case\"b\":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.height+i[1]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).height(this.parameters.box.height+i[1])}};break;case\"l\":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0){if(\"text\"===this.parameters.type)return;this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).width(this.parameters.box.width-i[0])}};break;case\"rot\":this.calc=function(t,e){var i=t+this.parameters.p.x,a=e+this.parameters.p.y,s=Math.atan2(this.parameters.p.y-this.parameters.box.y-this.parameters.box.height/2,this.parameters.p.x-this.parameters.box.x-this.parameters.box.width/2),r=Math.atan2(a-this.parameters.box.y-this.parameters.box.height/2,i-this.parameters.box.x-this.parameters.box.width/2),o=this.parameters.rotation+180*(r-s)/Math.PI+this.options.snapToAngle/2;this.el.center(this.parameters.box.cx,this.parameters.box.cy).rotate(o-o%this.options.snapToAngle,this.parameters.box.cx,this.parameters.box.cy)};break;case\"point\":this.calc=function(t,e){var i=this.snapToGrid(t,e,this.parameters.pointCoords[0],this.parameters.pointCoords[1]),a=this.el.array().valueOf();a[this.parameters.i][0]=this.parameters.pointCoords[0]+i[0],a[this.parameters.i][1]=this.parameters.pointCoords[1]+i[1],this.el.plot(a)}}this.el.fire(\"resizestart\",{dx:this.parameters.x,dy:this.parameters.y,event:t}),SVG.on(window,\"touchmove.resize\",(function(t){e.update(t||window.event)})),SVG.on(window,\"touchend.resize\",(function(){e.done()})),SVG.on(window,\"mousemove.resize\",(function(t){e.update(t||window.event)})),SVG.on(window,\"mouseup.resize\",(function(){e.done()}))},t.prototype.update=function(t){if(t){var e=this._extractPosition(t),i=this.transformPoint(e.x,e.y),a=i.x-this.parameters.p.x,s=i.y-this.parameters.p.y;this.lastUpdateCall=[a,s],this.calc(a,s),this.el.fire(\"resizing\",{dx:a,dy:s,event:t})}else this.lastUpdateCall&&this.calc(this.lastUpdateCall[0],this.lastUpdateCall[1])},t.prototype.done=function(){this.lastUpdateCall=null,SVG.off(window,\"mousemove.resize\"),SVG.off(window,\"mouseup.resize\"),SVG.off(window,\"touchmove.resize\"),SVG.off(window,\"touchend.resize\"),this.el.fire(\"resizedone\")},t.prototype.snapToGrid=function(t,e,i,a){var s;return void 0!==a?s=[(i+t)%this.options.snapToGrid,(a+e)%this.options.snapToGrid]:(i=null==i?3:i,s=[(this.parameters.box.x+t+(1&i?0:this.parameters.box.width))%this.options.snapToGrid,(this.parameters.box.y+e+(2&i?0:this.parameters.box.height))%this.options.snapToGrid]),t<0&&(s[0]-=this.options.snapToGrid),e<0&&(s[1]-=this.options.snapToGrid),t-=Math.abs(s[0])<this.options.snapToGrid/2?s[0]:s[0]-(t<0?-this.options.snapToGrid:this.options.snapToGrid),e-=Math.abs(s[1])<this.options.snapToGrid/2?s[1]:s[1]-(e<0?-this.options.snapToGrid:this.options.snapToGrid),this.constraintToBox(t,e,i,a)},t.prototype.constraintToBox=function(t,e,i,a){var s,r,o=this.options.constraint||{};return void 0!==a?(s=i,r=a):(s=this.parameters.box.x+(1&i?0:this.parameters.box.width),r=this.parameters.box.y+(2&i?0:this.parameters.box.height)),void 0!==o.minX&&s+t<o.minX&&(t=o.minX-s),void 0!==o.maxX&&s+t>o.maxX&&(t=o.maxX-s),void 0!==o.minY&&r+e<o.minY&&(e=o.minY-r),void 0!==o.maxY&&r+e>o.maxY&&(e=o.maxY-r),[t,e]},t.prototype.checkAspectRatio=function(t,e){if(!this.options.saveAspectRatio)return t;var i=t.slice(),a=this.parameters.box.width/this.parameters.box.height,s=this.parameters.box.width+t[0],r=this.parameters.box.height-t[1],o=s/r;return o<a?(i[1]=s/a-this.parameters.box.height,e&&(i[1]=-i[1])):o>a&&(i[0]=this.parameters.box.width-r*a,e&&(i[0]=-i[0])),i},SVG.extend(SVG.Element,{resize:function(e){return(this.remember(\"_resizeHandler\")||new t(this)).init(e||{}),this}}),SVG.Element.prototype.resize.defaults={snapToAngle:.1,snapToGrid:1,constraint:{},saveAspectRatio:!1}}).call(this)}(),void 0===window.Apex&&(window.Apex={});var Ht=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"initModules\",value:function(){this.ctx.publicMethods=[\"updateOptions\",\"updateSeries\",\"appendData\",\"appendSeries\",\"toggleSeries\",\"showSeries\",\"hideSeries\",\"setLocale\",\"resetSeries\",\"zoomX\",\"toggleDataPointSelection\",\"dataURI\",\"exportToCSV\",\"addXaxisAnnotation\",\"addYaxisAnnotation\",\"addPointAnnotation\",\"clearAnnotations\",\"removeAnnotation\",\"paper\",\"destroy\"],this.ctx.eventList=[\"click\",\"mousedown\",\"mousemove\",\"mouseleave\",\"touchstart\",\"touchmove\",\"touchleave\",\"mouseup\",\"touchend\"],this.ctx.animations=new b(this.ctx),this.ctx.axes=new J(this.ctx),this.ctx.core=new Rt(this.ctx.el,this.ctx),this.ctx.config=new E({}),this.ctx.data=new W(this.ctx),this.ctx.grid=new j(this.ctx),this.ctx.graphics=new m(this.ctx),this.ctx.coreUtils=new y(this.ctx),this.ctx.crosshairs=new Q(this.ctx),this.ctx.events=new Z(this.ctx),this.ctx.exports=new G(this.ctx),this.ctx.localization=new $(this.ctx),this.ctx.options=new L,this.ctx.responsive=new K(this.ctx),this.ctx.series=new N(this.ctx),this.ctx.theme=new tt(this.ctx),this.ctx.formatters=new M(this.ctx),this.ctx.titleSubtitle=new et(this.ctx),this.ctx.legend=new lt(this.ctx),this.ctx.toolbar=new ht(this.ctx),this.ctx.tooltip=new bt(this.ctx),this.ctx.dimensions=new ot(this.ctx),this.ctx.updateHelpers=new Dt(this.ctx),this.ctx.zoomPanSelection=new ct(this.ctx),this.ctx.w.globals.tooltip=new bt(this.ctx)}}]),t}(),Ot=function(){function t(e){a(this,t),this.ctx=e,this.w=e.w}return r(t,[{key:\"clear\",value:function(t){var e=t.isUpdating;this.ctx.zoomPanSelection&&this.ctx.zoomPanSelection.destroy(),this.ctx.toolbar&&this.ctx.toolbar.destroy(),this.ctx.animations=null,this.ctx.axes=null,this.ctx.annotations=null,this.ctx.core=null,this.ctx.data=null,this.ctx.grid=null,this.ctx.series=null,this.ctx.responsive=null,this.ctx.theme=null,this.ctx.formatters=null,this.ctx.titleSubtitle=null,this.ctx.legend=null,this.ctx.dimensions=null,this.ctx.options=null,this.ctx.crosshairs=null,this.ctx.zoomPanSelection=null,this.ctx.updateHelpers=null,this.ctx.toolbar=null,this.ctx.localization=null,this.ctx.w.globals.tooltip=null,this.clearDomElements({isUpdating:e})}},{key:\"killSVG\",value:function(t){t.each((function(t,e){this.removeClass(\"*\"),this.off(),this.stop()}),!0),t.ungroup(),t.clear()}},{key:\"clearDomElements\",value:function(t){var e=this,i=t.isUpdating,a=this.w.globals.dom.Paper.node;a.parentNode&&a.parentNode.parentNode&&!i&&(a.parentNode.parentNode.style.minHeight=\"unset\");var s=this.w.globals.dom.baseEl;s&&this.ctx.eventList.forEach((function(t){s.removeEventListener(t,e.ctx.events.documentEvent)}));var r=this.w.globals.dom;if(null!==this.ctx.el)for(;this.ctx.el.firstChild;)this.ctx.el.removeChild(this.ctx.el.firstChild);this.killSVG(r.Paper),r.Paper.remove(),r.elWrap=null,r.elGraphical=null,r.elAnnotations=null,r.elLegendWrap=null,r.baseEl=null,r.elGridRect=null,r.elGridRectMask=null,r.elGridRectMarkerMask=null,r.elForecastMask=null,r.elNonForecastMask=null,r.elDefs=null}}]),t}(),Nt=new WeakMap;var Wt=function(){function t(e,i){a(this,t),this.opts=i,this.ctx=this,this.w=new F(i).init(),this.el=e,this.w.globals.cuid=x.randomId(),this.w.globals.chartID=this.w.config.chart.id?x.escapeString(this.w.config.chart.id):this.w.globals.cuid,new Ht(this).initModules(),this.create=x.bind(this.create,this),this.windowResizeHandler=this._windowResizeHandler.bind(this),this.parentResizeHandler=this._parentResizeCallback.bind(this)}return r(t,[{key:\"render\",value:function(){var t=this;return new Promise((function(e,i){if(null!==t.el){void 0===Apex._chartInstances&&(Apex._chartInstances=[]),t.w.config.chart.id&&Apex._chartInstances.push({id:t.w.globals.chartID,group:t.w.config.chart.group,chart:t}),t.setLocale(t.w.config.chart.defaultLocale);var a=t.w.config.chart.events.beforeMount;if(\"function\"==typeof a&&a(t,t.w),t.events.fireEvent(\"beforeMount\",[t,t.w]),window.addEventListener(\"resize\",t.windowResizeHandler),function(t,e){var i=!1;if(t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE){var a=t.getBoundingClientRect();\"none\"!==t.style.display&&0!==a.width||(i=!0)}var s=new ResizeObserver((function(a){i&&e.call(t,a),i=!0}));t.nodeType===Node.DOCUMENT_FRAGMENT_NODE?Array.from(t.children).forEach((function(t){return s.observe(t)})):s.observe(t),Nt.set(e,s)}(t.el.parentNode,t.parentResizeHandler),!t.css){var s=t.el.getRootNode&&t.el.getRootNode(),r=x.is(\"ShadowRoot\",s),o=t.el.ownerDocument,n=o.getElementById(\"apexcharts-css\");!r&&n||(t.css=document.createElement(\"style\"),t.css.id=\"apexcharts-css\",t.css.textContent='@keyframes opaque {\\n  0% {\\n      opacity: 0\\n  }\\n\\n  to {\\n      opacity: 1\\n  }\\n}\\n\\n@keyframes resizeanim {\\n  0%,to {\\n      opacity: 0\\n  }\\n}\\n\\n.apexcharts-canvas {\\n  position: relative;\\n  user-select: none\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar {\\n  -webkit-appearance: none;\\n  width: 6px\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar-thumb {\\n  border-radius: 4px;\\n  background-color: rgba(0,0,0,.5);\\n  box-shadow: 0 0 1px rgba(255,255,255,.5);\\n  -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5)\\n}\\n\\n.apexcharts-inner {\\n  position: relative\\n}\\n\\n.apexcharts-text tspan {\\n  font-family: inherit\\n}\\n\\n.legend-mouseover-inactive {\\n  transition: .15s ease all;\\n  opacity: .2\\n}\\n\\n.apexcharts-legend-text {\\n  padding-left: 15px;\\n  margin-left: -15px;\\n}\\n\\n.apexcharts-series-collapsed {\\n  opacity: 0\\n}\\n\\n.apexcharts-tooltip {\\n  border-radius: 5px;\\n  box-shadow: 2px 2px 6px -4px #999;\\n  cursor: default;\\n  font-size: 14px;\\n  left: 62px;\\n  opacity: 0;\\n  pointer-events: none;\\n  position: absolute;\\n  top: 20px;\\n  display: flex;\\n  flex-direction: column;\\n  overflow: hidden;\\n  white-space: nowrap;\\n  z-index: 12;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light {\\n  border: 1px solid #e3e3e3;\\n  background: rgba(255,255,255,.96)\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark {\\n  color: #fff;\\n  background: rgba(30,30,30,.8)\\n}\\n\\n.apexcharts-tooltip * {\\n  font-family: inherit\\n}\\n\\n.apexcharts-tooltip-title {\\n  padding: 6px;\\n  font-size: 15px;\\n  margin-bottom: 4px\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {\\n  background: #eceff1;\\n  border-bottom: 1px solid #ddd\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {\\n  background: rgba(0,0,0,.7);\\n  border-bottom: 1px solid #333\\n}\\n\\n.apexcharts-tooltip-text-goals-value,.apexcharts-tooltip-text-y-value,.apexcharts-tooltip-text-z-value {\\n  display: inline-block;\\n  margin-left: 5px;\\n  font-weight: 600\\n}\\n\\n.apexcharts-tooltip-text-goals-label:empty,.apexcharts-tooltip-text-goals-value:empty,.apexcharts-tooltip-text-y-label:empty,.apexcharts-tooltip-text-y-value:empty,.apexcharts-tooltip-text-z-value:empty,.apexcharts-tooltip-title:empty {\\n  display: none\\n}\\n\\n.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-tooltip-goals-group,.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  display: flex\\n}\\n\\n.apexcharts-tooltip-text-goals-label:not(:empty),.apexcharts-tooltip-text-goals-value:not(:empty) {\\n  margin-top: -6px\\n}\\n\\n.apexcharts-tooltip-marker {\\n  width: 12px;\\n  height: 12px;\\n  position: relative;\\n  top: 0;\\n  margin-right: 10px;\\n  border-radius: 50%\\n}\\n\\n.apexcharts-tooltip-series-group {\\n  padding: 0 10px;\\n  display: none;\\n  text-align: left;\\n  justify-content: left;\\n  align-items: center\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {\\n  opacity: 1\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active,.apexcharts-tooltip-series-group:last-child {\\n  padding-bottom: 4px\\n}\\n\\n.apexcharts-tooltip-series-group-hidden {\\n  opacity: 0;\\n  height: 0;\\n  line-height: 0;\\n  padding: 0!important\\n}\\n\\n.apexcharts-tooltip-y-group {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-custom-tooltip,.apexcharts-tooltip-box {\\n  padding: 4px 8px\\n}\\n\\n.apexcharts-tooltip-boxPlot {\\n  display: flex;\\n  flex-direction: column-reverse\\n}\\n\\n.apexcharts-tooltip-box>div {\\n  margin: 4px 0\\n}\\n\\n.apexcharts-tooltip-box span.value {\\n  font-weight: 700\\n}\\n\\n.apexcharts-tooltip-rangebar {\\n  padding: 5px 8px\\n}\\n\\n.apexcharts-tooltip-rangebar .category {\\n  font-weight: 600;\\n  color: #777\\n}\\n\\n.apexcharts-tooltip-rangebar .series-name {\\n  font-weight: 700;\\n  display: block;\\n  margin-bottom: 5px\\n}\\n\\n.apexcharts-xaxistooltip,.apexcharts-yaxistooltip {\\n  opacity: 0;\\n  pointer-events: none;\\n  color: #373d3f;\\n  font-size: 13px;\\n  text-align: center;\\n  border-radius: 2px;\\n  position: absolute;\\n  z-index: 10;\\n  background: #eceff1;\\n  border: 1px solid #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip {\\n  padding: 9px 10px;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-xaxistooltip:after,.apexcharts-xaxistooltip:before {\\n  left: 50%;\\n  border: solid transparent;\\n  content: \" \";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-xaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-left: -6px\\n}\\n\\n.apexcharts-xaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-left: -7px\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after,.apexcharts-xaxistooltip-bottom:before {\\n  bottom: 100%\\n}\\n\\n.apexcharts-xaxistooltip-top:after,.apexcharts-xaxistooltip-top:before {\\n  top: 100%\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after {\\n  border-bottom-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-bottom:before {\\n  border-bottom-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {\\n  border-bottom-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip-top:after {\\n  border-top-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-top:before {\\n  border-top-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {\\n  border-top-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-yaxistooltip {\\n  padding: 4px 10px\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-yaxistooltip:after,.apexcharts-yaxistooltip:before {\\n  top: 50%;\\n  border: solid transparent;\\n  content: \" \";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-yaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-top: -6px\\n}\\n\\n.apexcharts-yaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-top: -7px\\n}\\n\\n.apexcharts-yaxistooltip-left:after,.apexcharts-yaxistooltip-left:before {\\n  left: 100%\\n}\\n\\n.apexcharts-yaxistooltip-right:after,.apexcharts-yaxistooltip-right:before {\\n  right: 100%\\n}\\n\\n.apexcharts-yaxistooltip-left:after {\\n  border-left-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-left:before {\\n  border-left-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {\\n  border-left-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip-right:after {\\n  border-right-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-right:before {\\n  border-right-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {\\n  border-right-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-active {\\n  opacity: 1\\n}\\n\\n.apexcharts-yaxistooltip-hidden {\\n  display: none\\n}\\n\\n.apexcharts-xcrosshairs,.apexcharts-ycrosshairs {\\n  pointer-events: none;\\n  opacity: 0;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xcrosshairs.apexcharts-active,.apexcharts-ycrosshairs.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-ycrosshairs-hidden {\\n  opacity: 0\\n}\\n\\n.apexcharts-selection-rect {\\n  cursor: move\\n}\\n\\n.svg_select_boundingRect,.svg_select_points_rot {\\n  pointer-events: none;\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_boundingRect,.apexcharts-selection-rect+g .svg_select_points_rot {\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_points_l,.apexcharts-selection-rect+g .svg_select_points_r {\\n  cursor: ew-resize;\\n  opacity: 1;\\n  visibility: visible\\n}\\n\\n.svg_select_points {\\n  fill: #efefef;\\n  stroke: #333;\\n  rx: 2\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-zoom {\\n  cursor: crosshair\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-pan {\\n  cursor: move\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-pan-icon,.apexcharts-reset-icon,.apexcharts-selection-icon,.apexcharts-toolbar-custom-icon,.apexcharts-zoom-icon,.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  cursor: pointer;\\n  width: 20px;\\n  height: 20px;\\n  line-height: 24px;\\n  color: #6e8192;\\n  text-align: center\\n}\\n\\n.apexcharts-menu-icon svg,.apexcharts-reset-icon svg,.apexcharts-zoom-icon svg,.apexcharts-zoomin-icon svg,.apexcharts-zoomout-icon svg {\\n  fill: #6e8192\\n}\\n\\n.apexcharts-selection-icon svg {\\n  fill: #444;\\n  transform: scale(.76)\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu-icon svg,.apexcharts-theme-dark .apexcharts-pan-icon svg,.apexcharts-theme-dark .apexcharts-reset-icon svg,.apexcharts-theme-dark .apexcharts-selection-icon svg,.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg,.apexcharts-theme-dark .apexcharts-zoom-icon svg,.apexcharts-theme-dark .apexcharts-zoomin-icon svg,.apexcharts-theme-dark .apexcharts-zoomout-icon svg {\\n  fill: #f3f4f5\\n}\\n\\n.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg {\\n  fill: #008ffb\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-icon:hover svg,.apexcharts-theme-light .apexcharts-reset-icon:hover svg,.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg {\\n  fill: #333\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-selection-icon {\\n  position: relative\\n}\\n\\n.apexcharts-reset-icon {\\n  margin-left: 5px\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-reset-icon,.apexcharts-zoom-icon {\\n  transform: scale(.85)\\n}\\n\\n.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  transform: scale(.7)\\n}\\n\\n.apexcharts-zoomout-icon {\\n  margin-right: 3px\\n}\\n\\n.apexcharts-pan-icon {\\n  transform: scale(.62);\\n  position: relative;\\n  left: 1px;\\n  top: 0\\n}\\n\\n.apexcharts-pan-icon svg {\\n  fill: #fff;\\n  stroke: #6e8192;\\n  stroke-width: 2\\n}\\n\\n.apexcharts-pan-icon.apexcharts-selected svg {\\n  stroke: #008ffb\\n}\\n\\n.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {\\n  stroke: #333\\n}\\n\\n.apexcharts-toolbar {\\n  position: absolute;\\n  z-index: 11;\\n  max-width: 176px;\\n  text-align: right;\\n  border-radius: 3px;\\n  padding: 0 6px 2px;\\n  display: flex;\\n  justify-content: space-between;\\n  align-items: center\\n}\\n\\n.apexcharts-menu {\\n  background: #fff;\\n  position: absolute;\\n  top: 100%;\\n  border: 1px solid #ddd;\\n  border-radius: 3px;\\n  padding: 3px;\\n  right: 10px;\\n  opacity: 0;\\n  min-width: 110px;\\n  transition: .15s ease all;\\n  pointer-events: none\\n}\\n\\n.apexcharts-menu.apexcharts-menu-open {\\n  opacity: 1;\\n  pointer-events: all;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-menu-item {\\n  padding: 6px 7px;\\n  font-size: 12px;\\n  cursor: pointer\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-item:hover {\\n  background: #eee\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu {\\n  background: rgba(0,0,0,.7);\\n  color: #fff\\n}\\n\\n@media screen and (min-width:768px) {\\n  .apexcharts-canvas:hover .apexcharts-toolbar {\\n      opacity: 1\\n  }\\n}\\n\\n.apexcharts-canvas .apexcharts-element-hidden,.apexcharts-datalabel.apexcharts-element-hidden,.apexcharts-hide .apexcharts-series-points {\\n  opacity: 0\\n}\\n\\n.apexcharts-datalabel,.apexcharts-datalabel-label,.apexcharts-datalabel-value,.apexcharts-datalabels,.apexcharts-pie-label {\\n  cursor: default;\\n  pointer-events: none\\n}\\n\\n.apexcharts-pie-label-delay {\\n  opacity: 0;\\n  animation-name: opaque;\\n  animation-duration: .3s;\\n  animation-fill-mode: forwards;\\n  animation-timing-function: ease\\n}\\n\\n.apexcharts-legend {\\t\\n  display: flex;\\t\\n  overflow: auto;\\t\\n  padding: 0 10px;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom, .apexcharts-legend.apx-legend-position-top {\\t\\n  flex-wrap: wrap\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  flex-direction: column;\\t\\n  bottom: 0;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-left, .apexcharts-legend.apx-legend-position-top.apexcharts-align-left, .apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  justify-content: flex-start;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-center, .apexcharts-legend.apx-legend-position-top.apexcharts-align-center {\\t\\n  justify-content: center;  \\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-right, .apexcharts-legend.apx-legend-position-top.apexcharts-align-right {\\t\\n  justify-content: flex-end;\\t\\n}\\t\\n.apexcharts-legend-series {\\t\\n  cursor: pointer;\\t\\n  line-height: normal;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom .apexcharts-legend-series, .apexcharts-legend.apx-legend-position-top .apexcharts-legend-series{\\t\\n  display: flex;\\t\\n  align-items: center;\\t\\n}\\t\\n.apexcharts-legend-text {\\t\\n  position: relative;\\t\\n  font-size: 14px;\\t\\n}\\t\\n.apexcharts-legend-text *, .apexcharts-legend-marker * {\\t\\n  pointer-events: none;\\t\\n}\\t\\n.apexcharts-legend-marker {\\t\\n  position: relative;\\t\\n  display: inline-block;\\t\\n  cursor: pointer;\\t\\n  margin-right: 3px;\\t\\n  border-style: solid;\\n}\\t\\n  \\n.apexcharts-legend.apexcharts-align-right .apexcharts-legend-series, .apexcharts-legend.apexcharts-align-left .apexcharts-legend-series{\\t\\n  display: inline-block;\\t\\n}\\t\\n.apexcharts-legend-series.apexcharts-no-click {\\t\\n  cursor: auto;\\t\\n}\\t\\n.apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {\\t\\n  display: none !important;\\t\\n}\\t\\n.apexcharts-inactive-legend {\\t\\n  opacity: 0.45;\\t\\n}\\n\\n.apexcharts-annotation-rect,.apexcharts-area-series .apexcharts-area,.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-gridline,.apexcharts-line,.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-point-annotation-label,.apexcharts-radar-series path,.apexcharts-radar-series polygon,.apexcharts-toolbar svg,.apexcharts-tooltip .apexcharts-marker,.apexcharts-xaxis-annotation-label,.apexcharts-yaxis-annotation-label,.apexcharts-zoom-rect {\\n  pointer-events: none\\n}\\n\\n.apexcharts-marker {\\n  transition: .15s ease all\\n}\\n\\n.resize-triggers {\\n  animation: 1ms resizeanim;\\n  visibility: hidden;\\n  opacity: 0;\\n  height: 100%;\\n  width: 100%;\\n  overflow: hidden\\n}\\n\\n.contract-trigger:before,.resize-triggers,.resize-triggers>div {\\n  content: \" \";\\n  display: block;\\n  position: absolute;\\n  top: 0;\\n  left: 0\\n}\\n\\n.resize-triggers>div {\\n  height: 100%;\\n  width: 100%;\\n  background: #eee;\\n  overflow: auto\\n}\\n\\n.contract-trigger:before {\\n  overflow: hidden;\\n  width: 200%;\\n  height: 200%\\n}\\n',r?s.prepend(t.css):o.head.appendChild(t.css))}var l=t.create(t.w.config.series,{});if(!l)return e(t);t.mount(l).then((function(){\"function\"==typeof t.w.config.chart.events.mounted&&t.w.config.chart.events.mounted(t,t.w),t.events.fireEvent(\"mounted\",[t,t.w]),e(l)})).catch((function(t){i(t)}))}else i(new Error(\"Element not found\"))}))}},{key:\"create\",value:function(t,e){var i=this.w;new Ht(this).initModules();var a=this.w.globals;(a.noData=!1,a.animationEnded=!1,this.responsive.checkResponsiveConfig(e),i.config.xaxis.convertedCatToNumeric)&&new X(i.config).convertCatToNumericXaxis(i.config,this.ctx);if(null===this.el)return a.animationEnded=!0,null;if(this.core.setupElements(),\"treemap\"===i.config.chart.type&&(i.config.grid.show=!1,i.config.yaxis[0].show=!1),0===a.svgWidth)return a.animationEnded=!0,null;var s=y.checkComboSeries(t);a.comboCharts=s.comboCharts,a.comboBarCount=s.comboBarCount;var r=t.every((function(t){return t.data&&0===t.data.length}));(0===t.length||r)&&this.series.handleNoData(),this.events.setupEventHandlers(),this.data.parseData(t),this.theme.init(),new D(this).setGlobalMarkerSize(),this.formatters.setLabelFormatters(),this.titleSubtitle.draw(),a.noData&&a.collapsedSeries.length!==a.series.length&&!i.config.legend.showForSingleSeries||this.legend.init(),this.series.hasAllSeriesEqualX(),a.axisCharts&&(this.core.coreCalculations(),\"category\"!==i.config.xaxis.type&&this.formatters.setLabelFormatters(),this.ctx.toolbar.minX=i.globals.minX,this.ctx.toolbar.maxX=i.globals.maxX),this.formatters.heatmapLabelFormatters(),new y(this).getLargestMarkerSize(),this.dimensions.plotCoords();var o=this.core.xySettings();this.grid.createGridMask();var n=this.core.plotChartType(t,o),l=new O(this);l.bringForward(),i.config.dataLabels.background.enabled&&l.dataLabelsBackground(),this.core.shiftGraphPosition();var h={plot:{left:i.globals.translateX,top:i.globals.translateY,width:i.globals.gridWidth,height:i.globals.gridHeight}};return{elGraph:n,xyRatios:o,elInner:i.globals.dom.elGraphical,dimensions:h}}},{key:\"mount\",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=this,a=i.w;return new Promise((function(s,r){if(null===i.el)return r(new Error(\"Not enough data to display or target element not found\"));(null===e||a.globals.allSeriesCollapsed)&&i.series.handleNoData(),i.grid=new j(i);var o=i.grid.drawGrid();if(i.annotations=new P(i),i.annotations.drawImageAnnos(),i.annotations.drawTextAnnos(),\"back\"===a.config.grid.position&&o&&(a.globals.dom.elGraphical.add(o.el),o&&o.elGridBorders&&o.elGridBorders.node&&a.globals.dom.elGraphical.add(o.elGridBorders)),Array.isArray(e.elGraph))for(var n=0;n<e.elGraph.length;n++)a.globals.dom.elGraphical.add(e.elGraph[n]);else a.globals.dom.elGraphical.add(e.elGraph);\"front\"===a.config.grid.position&&o&&(a.globals.dom.elGraphical.add(o.el),o&&o.elGridBorders&&o.elGridBorders.node&&a.globals.dom.elGraphical.add(o.elGridBorders)),\"front\"===a.config.xaxis.crosshairs.position&&i.crosshairs.drawXCrosshairs(),\"front\"===a.config.yaxis[0].crosshairs.position&&i.crosshairs.drawYCrosshairs(),\"treemap\"!==a.config.chart.type&&i.axes.drawAxis(a.config.chart.type,o);var l=new V(t.ctx,o),h=new q(t.ctx,o);if(null!==o&&(l.xAxisLabelCorrections(o.xAxisTickWidth),h.setYAxisTextAlignments(),a.config.yaxis.map((function(t,e){-1===a.globals.ignoreYAxisIndexes.indexOf(e)&&h.yAxisTitleRotate(e,t.opposite)}))),a.globals.dom.Paper.add(a.globals.dom.elAnnotations),i.annotations.drawAxesAnnotations(),!a.globals.noData){if(a.config.tooltip.enabled&&!a.globals.noData&&i.w.globals.tooltip.drawTooltip(e.xyRatios),a.globals.axisCharts&&(a.globals.isXNumeric||a.config.xaxis.convertedCatToNumeric||a.globals.isRangeBar))(a.config.chart.zoom.enabled||a.config.chart.selection&&a.config.chart.selection.enabled||a.config.chart.pan&&a.config.chart.pan.enabled)&&i.zoomPanSelection.init({xyRatios:e.xyRatios});else{var c=a.config.chart.toolbar.tools;[\"zoom\",\"zoomin\",\"zoomout\",\"selection\",\"pan\",\"reset\"].forEach((function(t){c[t]=!1}))}a.config.chart.toolbar.show&&!a.globals.allSeriesCollapsed&&i.toolbar.createToolbar()}a.globals.memory.methodsToExec.length>0&&a.globals.memory.methodsToExec.forEach((function(t){t.method(t.params,!1,t.context)})),a.globals.axisCharts||a.globals.noData||i.core.resizeNonAxisCharts(),s(i)}))}},{key:\"destroy\",value:function(){var t,e;window.removeEventListener(\"resize\",this.windowResizeHandler),this.el.parentNode,t=this.parentResizeHandler,(e=Nt.get(t))&&(e.disconnect(),Nt.delete(t));var i=this.w.config.chart.id;i&&Apex._chartInstances.forEach((function(t,e){t.id===x.escapeString(i)&&Apex._chartInstances.splice(e,1)})),new Ot(this.ctx).clear({isUpdating:!1})}},{key:\"updateOptions\",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],a=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],s=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],o=this.w;return o.globals.selection=void 0,t.series&&(this.series.resetSeries(!1,!0,!1),t.series.length&&t.series[0].data&&(t.series=t.series.map((function(t,i){return e.updateHelpers._extendSeries(t,i)}))),this.updateHelpers.revertDefaultAxisMinMax()),t.xaxis&&(t=this.updateHelpers.forceXAxisUpdate(t)),t.yaxis&&(t=this.updateHelpers.forceYAxisUpdate(t)),o.globals.collapsedSeriesIndices.length>0&&this.series.clearPreviousPaths(),t.theme&&(t=this.theme.updateThemeOptions(t)),this.updateHelpers._updateOptions(t,i,a,s,r)}},{key:\"updateSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(t,e,i)}},{key:\"appendSeries\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w.config.series.slice();return a.push(t),this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(a,e,i)}},{key:\"appendData\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this;i.w.globals.dataChanged=!0,i.series.getPreviousPaths();for(var a=i.w.config.series.slice(),s=0;s<a.length;s++)if(null!==t[s]&&void 0!==t[s])for(var r=0;r<t[s].data.length;r++)a[s].data.push(t[s].data[r]);return i.w.config.series=a,e&&(i.w.globals.initialSeries=x.clone(i.w.config.series)),this.update()}},{key:\"update\",value:function(t){var e=this;return new Promise((function(i,a){new Ot(e.ctx).clear({isUpdating:!0});var s=e.create(e.w.config.series,t);if(!s)return i(e);e.mount(s).then((function(){\"function\"==typeof e.w.config.chart.events.updated&&e.w.config.chart.events.updated(e,e.w),e.events.fireEvent(\"updated\",[e,e.w]),e.w.globals.isDirty=!0,i(e)})).catch((function(t){a(t)}))}))}},{key:\"getSyncedCharts\",value:function(){var t=this.getGroupedCharts(),e=[this];return t.length&&(e=[],t.forEach((function(t){e.push(t)}))),e}},{key:\"getGroupedCharts\",value:function(){var t=this;return Apex._chartInstances.filter((function(t){if(t.group)return!0})).map((function(e){return t.w.config.chart.group===e.group?e.chart:t}))}},{key:\"toggleSeries\",value:function(t){return this.series.toggleSeries(t)}},{key:\"highlightSeriesOnLegendHover\",value:function(t,e){return this.series.toggleSeriesOnHover(t,e)}},{key:\"showSeries\",value:function(t){this.series.showSeries(t)}},{key:\"hideSeries\",value:function(t){this.series.hideSeries(t)}},{key:\"resetSeries\",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.series.resetSeries(t,e)}},{key:\"addEventListener\",value:function(t,e){this.events.addEventListener(t,e)}},{key:\"removeEventListener\",value:function(t,e){this.events.removeEventListener(t,e)}},{key:\"addXaxisAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addXaxisAnnotationExternal(t,e,a)}},{key:\"addYaxisAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addYaxisAnnotationExternal(t,e,a)}},{key:\"addPointAnnotation\",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addPointAnnotationExternal(t,e,a)}},{key:\"clearAnnotations\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,e=this;t&&(e=t),e.annotations.clearAnnotations(e)}},{key:\"removeAnnotation\",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,i=this;e&&(i=e),i.annotations.removeAnnotation(i,t)}},{key:\"getChartArea\",value:function(){return this.w.globals.dom.baseEl.querySelector(\".apexcharts-inner\")}},{key:\"getSeriesTotalXRange\",value:function(t,e){return this.coreUtils.getSeriesTotalsXRange(t,e)}},{key:\"getHighestValueInSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new U(this.ctx);return e.getMinYMaxY(t).highestY}},{key:\"getLowestValueInSeries\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new U(this.ctx);return e.getMinYMaxY(t).lowestY}},{key:\"getSeriesTotal\",value:function(){return this.w.globals.seriesTotals}},{key:\"toggleDataPointSelection\",value:function(t,e){return this.updateHelpers.toggleDataPointSelection(t,e)}},{key:\"zoomX\",value:function(t,e){this.ctx.toolbar.zoomUpdateOptions(t,e)}},{key:\"setLocale\",value:function(t){this.localization.setCurrentLocaleValues(t)}},{key:\"dataURI\",value:function(t){return new G(this.ctx).dataURI(t)}},{key:\"exportToCSV\",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=new G(this.ctx);return e.exportToCSV(t)}},{key:\"paper\",value:function(){return this.w.globals.dom.Paper}},{key:\"_parentResizeCallback\",value:function(){this.w.globals.animationEnded&&this.w.config.chart.redrawOnParentResize&&this._windowResize()}},{key:\"_windowResize\",value:function(){var t=this;clearTimeout(this.w.globals.resizeTimer),this.w.globals.resizeTimer=window.setTimeout((function(){t.w.globals.resized=!0,t.w.globals.dataChanged=!1,t.ctx.update()}),150)}},{key:\"_windowResizeHandler\",value:function(){var t=this.w.config.chart.redrawOnWindowResize;\"function\"==typeof t&&(t=t()),t&&this._windowResize()}}],[{key:\"getChartByID\",value:function(t){var e=x.escapeString(t),i=Apex._chartInstances.filter((function(t){return t.id===e}))[0];return i&&i.chart}},{key:\"initOnLoad\",value:function(){for(var e=document.querySelectorAll(\"[data-apexcharts]\"),i=0;i<e.length;i++){new t(e[i],JSON.parse(e[i].getAttribute(\"data-options\"))).render()}}},{key:\"exec\",value:function(t,e){var i=this.getChartByID(t);if(i){i.w.globals.isExecCalled=!0;var a=null;if(-1!==i.publicMethods.indexOf(e)){for(var s=arguments.length,r=new Array(s>2?s-2:0),o=2;o<s;o++)r[o-2]=arguments[o];a=i[e].apply(i,r)}return a}}},{key:\"merge\",value:function(t,e){return x.extend(t,e)}}]),t}();export{Wt as default};\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/apexcharts.js",
    "content": "/*!\n * ApexCharts v3.37.1\n * (c) 2018-2023 ApexCharts\n * Released under the MIT License.\n */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ApexCharts = factory());\n}(this, (function () { 'use strict';\n\n  function ownKeys(object, enumerableOnly) {\n    var keys = Object.keys(object);\n\n    if (Object.getOwnPropertySymbols) {\n      var symbols = Object.getOwnPropertySymbols(object);\n\n      if (enumerableOnly) {\n        symbols = symbols.filter(function (sym) {\n          return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n        });\n      }\n\n      keys.push.apply(keys, symbols);\n    }\n\n    return keys;\n  }\n\n  function _objectSpread2(target) {\n    for (var i = 1; i < arguments.length; i++) {\n      var source = arguments[i] != null ? arguments[i] : {};\n\n      if (i % 2) {\n        ownKeys(Object(source), true).forEach(function (key) {\n          _defineProperty(target, key, source[key]);\n        });\n      } else if (Object.getOwnPropertyDescriptors) {\n        Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n      } else {\n        ownKeys(Object(source)).forEach(function (key) {\n          Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n        });\n      }\n    }\n\n    return target;\n  }\n\n  function _typeof(obj) {\n    \"@babel/helpers - typeof\";\n\n    if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n      _typeof = function (obj) {\n        return typeof obj;\n      };\n    } else {\n      _typeof = function (obj) {\n        return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n      };\n    }\n\n    return _typeof(obj);\n  }\n\n  function _classCallCheck(instance, Constructor) {\n    if (!(instance instanceof Constructor)) {\n      throw new TypeError(\"Cannot call a class as a function\");\n    }\n  }\n\n  function _defineProperties(target, props) {\n    for (var i = 0; i < props.length; i++) {\n      var descriptor = props[i];\n      descriptor.enumerable = descriptor.enumerable || false;\n      descriptor.configurable = true;\n      if (\"value\" in descriptor) descriptor.writable = true;\n      Object.defineProperty(target, descriptor.key, descriptor);\n    }\n  }\n\n  function _createClass(Constructor, protoProps, staticProps) {\n    if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n    if (staticProps) _defineProperties(Constructor, staticProps);\n    return Constructor;\n  }\n\n  function _defineProperty(obj, key, value) {\n    if (key in obj) {\n      Object.defineProperty(obj, key, {\n        value: value,\n        enumerable: true,\n        configurable: true,\n        writable: true\n      });\n    } else {\n      obj[key] = value;\n    }\n\n    return obj;\n  }\n\n  function _inherits(subClass, superClass) {\n    if (typeof superClass !== \"function\" && superClass !== null) {\n      throw new TypeError(\"Super expression must either be null or a function\");\n    }\n\n    subClass.prototype = Object.create(superClass && superClass.prototype, {\n      constructor: {\n        value: subClass,\n        writable: true,\n        configurable: true\n      }\n    });\n    if (superClass) _setPrototypeOf(subClass, superClass);\n  }\n\n  function _getPrototypeOf(o) {\n    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {\n      return o.__proto__ || Object.getPrototypeOf(o);\n    };\n    return _getPrototypeOf(o);\n  }\n\n  function _setPrototypeOf(o, p) {\n    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n      o.__proto__ = p;\n      return o;\n    };\n\n    return _setPrototypeOf(o, p);\n  }\n\n  function _isNativeReflectConstruct() {\n    if (typeof Reflect === \"undefined\" || !Reflect.construct) return false;\n    if (Reflect.construct.sham) return false;\n    if (typeof Proxy === \"function\") return true;\n\n    try {\n      Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));\n      return true;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  function _assertThisInitialized(self) {\n    if (self === void 0) {\n      throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n    }\n\n    return self;\n  }\n\n  function _possibleConstructorReturn(self, call) {\n    if (call && (typeof call === \"object\" || typeof call === \"function\")) {\n      return call;\n    } else if (call !== void 0) {\n      throw new TypeError(\"Derived constructors may only return object or undefined\");\n    }\n\n    return _assertThisInitialized(self);\n  }\n\n  function _createSuper(Derived) {\n    var hasNativeReflectConstruct = _isNativeReflectConstruct();\n\n    return function _createSuperInternal() {\n      var Super = _getPrototypeOf(Derived),\n          result;\n\n      if (hasNativeReflectConstruct) {\n        var NewTarget = _getPrototypeOf(this).constructor;\n\n        result = Reflect.construct(Super, arguments, NewTarget);\n      } else {\n        result = Super.apply(this, arguments);\n      }\n\n      return _possibleConstructorReturn(this, result);\n    };\n  }\n\n  function _slicedToArray(arr, i) {\n    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();\n  }\n\n  function _toConsumableArray(arr) {\n    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();\n  }\n\n  function _arrayWithoutHoles(arr) {\n    if (Array.isArray(arr)) return _arrayLikeToArray(arr);\n  }\n\n  function _arrayWithHoles(arr) {\n    if (Array.isArray(arr)) return arr;\n  }\n\n  function _iterableToArray(iter) {\n    if (typeof Symbol !== \"undefined\" && iter[Symbol.iterator] != null || iter[\"@@iterator\"] != null) return Array.from(iter);\n  }\n\n  function _iterableToArrayLimit(arr, i) {\n    var _i = arr == null ? null : typeof Symbol !== \"undefined\" && arr[Symbol.iterator] || arr[\"@@iterator\"];\n\n    if (_i == null) return;\n    var _arr = [];\n    var _n = true;\n    var _d = false;\n\n    var _s, _e;\n\n    try {\n      for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {\n        _arr.push(_s.value);\n\n        if (i && _arr.length === i) break;\n      }\n    } catch (err) {\n      _d = true;\n      _e = err;\n    } finally {\n      try {\n        if (!_n && _i[\"return\"] != null) _i[\"return\"]();\n      } finally {\n        if (_d) throw _e;\n      }\n    }\n\n    return _arr;\n  }\n\n  function _unsupportedIterableToArray(o, minLen) {\n    if (!o) return;\n    if (typeof o === \"string\") return _arrayLikeToArray(o, minLen);\n    var n = Object.prototype.toString.call(o).slice(8, -1);\n    if (n === \"Object\" && o.constructor) n = o.constructor.name;\n    if (n === \"Map\" || n === \"Set\") return Array.from(o);\n    if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);\n  }\n\n  function _arrayLikeToArray(arr, len) {\n    if (len == null || len > arr.length) len = arr.length;\n\n    for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];\n\n    return arr2;\n  }\n\n  function _nonIterableSpread() {\n    throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n  }\n\n  function _nonIterableRest() {\n    throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n  }\n\n  /*\n   ** Generic functions which are not dependent on ApexCharts\n   */\n  var Utils$1 = /*#__PURE__*/function () {\n    function Utils() {\n      _classCallCheck(this, Utils);\n    }\n\n    _createClass(Utils, [{\n      key: \"shadeRGBColor\",\n      value: function shadeRGBColor(percent, color) {\n        var f = color.split(','),\n            t = percent < 0 ? 0 : 255,\n            p = percent < 0 ? percent * -1 : percent,\n            R = parseInt(f[0].slice(4), 10),\n            G = parseInt(f[1], 10),\n            B = parseInt(f[2], 10);\n        return 'rgb(' + (Math.round((t - R) * p) + R) + ',' + (Math.round((t - G) * p) + G) + ',' + (Math.round((t - B) * p) + B) + ')';\n      }\n    }, {\n      key: \"shadeHexColor\",\n      value: function shadeHexColor(percent, color) {\n        var f = parseInt(color.slice(1), 16),\n            t = percent < 0 ? 0 : 255,\n            p = percent < 0 ? percent * -1 : percent,\n            R = f >> 16,\n            G = f >> 8 & 0x00ff,\n            B = f & 0x0000ff;\n        return '#' + (0x1000000 + (Math.round((t - R) * p) + R) * 0x10000 + (Math.round((t - G) * p) + G) * 0x100 + (Math.round((t - B) * p) + B)).toString(16).slice(1);\n      } // beautiful color shading blending code\n      // http://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors\n\n    }, {\n      key: \"shadeColor\",\n      value: function shadeColor(p, color) {\n        if (Utils.isColorHex(color)) {\n          return this.shadeHexColor(p, color);\n        } else {\n          return this.shadeRGBColor(p, color);\n        }\n      }\n    }], [{\n      key: \"bind\",\n      value: function bind(fn, me) {\n        return function () {\n          return fn.apply(me, arguments);\n        };\n      }\n    }, {\n      key: \"isObject\",\n      value: function isObject(item) {\n        return item && _typeof(item) === 'object' && !Array.isArray(item) && item != null;\n      } // Type checking that works across different window objects\n\n    }, {\n      key: \"is\",\n      value: function is(type, val) {\n        return Object.prototype.toString.call(val) === '[object ' + type + ']';\n      }\n    }, {\n      key: \"listToArray\",\n      value: function listToArray(list) {\n        var i,\n            array = [];\n\n        for (i = 0; i < list.length; i++) {\n          array[i] = list[i];\n        }\n\n        return array;\n      } // to extend defaults with user options\n      // credit: http://stackoverflow.com/questions/27936772/deep-object-merging-in-es6-es7#answer-34749873\n\n    }, {\n      key: \"extend\",\n      value: function extend(target, source) {\n        var _this = this;\n\n        if (typeof Object.assign !== 'function') {\n\n          (function () {\n            Object.assign = function (target) {\n\n              if (target === undefined || target === null) {\n                throw new TypeError('Cannot convert undefined or null to object');\n              }\n\n              var output = Object(target);\n\n              for (var index = 1; index < arguments.length; index++) {\n                var _source = arguments[index];\n\n                if (_source !== undefined && _source !== null) {\n                  for (var nextKey in _source) {\n                    if (_source.hasOwnProperty(nextKey)) {\n                      output[nextKey] = _source[nextKey];\n                    }\n                  }\n                }\n              }\n\n              return output;\n            };\n          })();\n        }\n\n        var output = Object.assign({}, target);\n\n        if (this.isObject(target) && this.isObject(source)) {\n          Object.keys(source).forEach(function (key) {\n            if (_this.isObject(source[key])) {\n              if (!(key in target)) {\n                Object.assign(output, _defineProperty({}, key, source[key]));\n              } else {\n                output[key] = _this.extend(target[key], source[key]);\n              }\n            } else {\n              Object.assign(output, _defineProperty({}, key, source[key]));\n            }\n          });\n        }\n\n        return output;\n      }\n    }, {\n      key: \"extendArray\",\n      value: function extendArray(arrToExtend, resultArr) {\n        var extendedArr = [];\n        arrToExtend.map(function (item) {\n          extendedArr.push(Utils.extend(resultArr, item));\n        });\n        arrToExtend = extendedArr;\n        return arrToExtend;\n      } // If month counter exceeds 12, it starts again from 1\n\n    }, {\n      key: \"monthMod\",\n      value: function monthMod(month) {\n        return month % 12;\n      }\n    }, {\n      key: \"clone\",\n      value: function clone(source) {\n        if (Utils.is('Array', source)) {\n          var cloneResult = [];\n\n          for (var i = 0; i < source.length; i++) {\n            cloneResult[i] = this.clone(source[i]);\n          }\n\n          return cloneResult;\n        } else if (Utils.is('Null', source)) {\n          // fixes an issue where null values were converted to {}\n          return null;\n        } else if (Utils.is('Date', source)) {\n          return source;\n        } else if (_typeof(source) === 'object') {\n          var _cloneResult = {};\n\n          for (var prop in source) {\n            if (source.hasOwnProperty(prop)) {\n              _cloneResult[prop] = this.clone(source[prop]);\n            }\n          }\n\n          return _cloneResult;\n        } else {\n          return source;\n        }\n      }\n    }, {\n      key: \"log10\",\n      value: function log10(x) {\n        return Math.log(x) / Math.LN10;\n      }\n    }, {\n      key: \"roundToBase10\",\n      value: function roundToBase10(x) {\n        return Math.pow(10, Math.floor(Math.log10(x)));\n      }\n    }, {\n      key: \"roundToBase\",\n      value: function roundToBase(x, base) {\n        return Math.pow(base, Math.floor(Math.log(x) / Math.log(base)));\n      }\n    }, {\n      key: \"parseNumber\",\n      value: function parseNumber(val) {\n        if (val === null) return val;\n        return parseFloat(val);\n      }\n    }, {\n      key: \"randomId\",\n      value: function randomId() {\n        return (Math.random() + 1).toString(36).substring(4);\n      }\n    }, {\n      key: \"noExponents\",\n      value: function noExponents(val) {\n        var data = String(val).split(/[eE]/);\n        if (data.length === 1) return data[0];\n        var z = '',\n            sign = val < 0 ? '-' : '',\n            str = data[0].replace('.', ''),\n            mag = Number(data[1]) + 1;\n\n        if (mag < 0) {\n          z = sign + '0.';\n\n          while (mag++) {\n            z += '0';\n          }\n\n          return z + str.replace(/^-/, '');\n        }\n\n        mag -= str.length;\n\n        while (mag--) {\n          z += '0';\n        }\n\n        return str + z;\n      }\n    }, {\n      key: \"getDimensions\",\n      value: function getDimensions(el) {\n        var computedStyle = getComputedStyle(el, null);\n        var elementHeight = el.clientHeight;\n        var elementWidth = el.clientWidth;\n        elementHeight -= parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom);\n        elementWidth -= parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight);\n        return [elementWidth, elementHeight];\n      }\n    }, {\n      key: \"getBoundingClientRect\",\n      value: function getBoundingClientRect(element) {\n        var rect = element.getBoundingClientRect();\n        return {\n          top: rect.top,\n          right: rect.right,\n          bottom: rect.bottom,\n          left: rect.left,\n          width: element.clientWidth,\n          height: element.clientHeight,\n          x: rect.left,\n          y: rect.top\n        };\n      }\n    }, {\n      key: \"getLargestStringFromArr\",\n      value: function getLargestStringFromArr(arr) {\n        return arr.reduce(function (a, b) {\n          if (Array.isArray(b)) {\n            b = b.reduce(function (aa, bb) {\n              return aa.length > bb.length ? aa : bb;\n            });\n          }\n\n          return a.length > b.length ? a : b;\n        }, 0);\n      } // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-12342275\n\n    }, {\n      key: \"hexToRgba\",\n      value: function hexToRgba() {\n        var hex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '#999999';\n        var opacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.6;\n\n        if (hex.substring(0, 1) !== '#') {\n          hex = '#999999';\n        }\n\n        var h = hex.replace('#', '');\n        h = h.match(new RegExp('(.{' + h.length / 3 + '})', 'g'));\n\n        for (var i = 0; i < h.length; i++) {\n          h[i] = parseInt(h[i].length === 1 ? h[i] + h[i] : h[i], 16);\n        }\n\n        if (typeof opacity !== 'undefined') h.push(opacity);\n        return 'rgba(' + h.join(',') + ')';\n      }\n    }, {\n      key: \"getOpacityFromRGBA\",\n      value: function getOpacityFromRGBA(rgba) {\n        return parseFloat(rgba.replace(/^.*,(.+)\\)/, '$1'));\n      }\n    }, {\n      key: \"rgb2hex\",\n      value: function rgb2hex(rgb) {\n        rgb = rgb.match(/^rgba?[\\s+]?\\([\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?/i);\n        return rgb && rgb.length === 4 ? '#' + ('0' + parseInt(rgb[1], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[2], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';\n      }\n    }, {\n      key: \"isColorHex\",\n      value: function isColorHex(color) {\n        return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i.test(color);\n      }\n    }, {\n      key: \"getPolygonPos\",\n      value: function getPolygonPos(size, dataPointsLen) {\n        var dotsArray = [];\n        var angle = Math.PI * 2 / dataPointsLen;\n\n        for (var i = 0; i < dataPointsLen; i++) {\n          var curPos = {};\n          curPos.x = size * Math.sin(i * angle);\n          curPos.y = -size * Math.cos(i * angle);\n          dotsArray.push(curPos);\n        }\n\n        return dotsArray;\n      }\n    }, {\n      key: \"polarToCartesian\",\n      value: function polarToCartesian(centerX, centerY, radius, angleInDegrees) {\n        var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;\n        return {\n          x: centerX + radius * Math.cos(angleInRadians),\n          y: centerY + radius * Math.sin(angleInRadians)\n        };\n      }\n    }, {\n      key: \"escapeString\",\n      value: function escapeString(str) {\n        var escapeWith = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'x';\n        var newStr = str.toString().slice();\n        newStr = newStr.replace(/[` ~!@#$%^&*()|+\\=?;:'\",.<>{}[\\]\\\\/]/gi, escapeWith);\n        return newStr;\n      }\n    }, {\n      key: \"negToZero\",\n      value: function negToZero(val) {\n        return val < 0 ? 0 : val;\n      }\n    }, {\n      key: \"moveIndexInArray\",\n      value: function moveIndexInArray(arr, old_index, new_index) {\n        if (new_index >= arr.length) {\n          var k = new_index - arr.length + 1;\n\n          while (k--) {\n            arr.push(undefined);\n          }\n        }\n\n        arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);\n        return arr;\n      }\n    }, {\n      key: \"extractNumber\",\n      value: function extractNumber(s) {\n        return parseFloat(s.replace(/[^\\d.]*/g, ''));\n      }\n    }, {\n      key: \"findAncestor\",\n      value: function findAncestor(el, cls) {\n        while ((el = el.parentElement) && !el.classList.contains(cls)) {\n        }\n\n        return el;\n      }\n    }, {\n      key: \"setELstyles\",\n      value: function setELstyles(el, styles) {\n        for (var key in styles) {\n          if (styles.hasOwnProperty(key)) {\n            el.style.key = styles[key];\n          }\n        }\n      }\n    }, {\n      key: \"isNumber\",\n      value: function isNumber(value) {\n        return !isNaN(value) && parseFloat(Number(value)) === value && !isNaN(parseInt(value, 10));\n      }\n    }, {\n      key: \"isFloat\",\n      value: function isFloat(n) {\n        return Number(n) === n && n % 1 !== 0;\n      }\n    }, {\n      key: \"isSafari\",\n      value: function isSafari() {\n        return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n      }\n    }, {\n      key: \"isFirefox\",\n      value: function isFirefox() {\n        return navigator.userAgent.toLowerCase().indexOf('firefox') > -1;\n      }\n    }, {\n      key: \"isIE11\",\n      value: function isIE11() {\n        if (window.navigator.userAgent.indexOf('MSIE') !== -1 || window.navigator.appVersion.indexOf('Trident/') > -1) {\n          return true;\n        }\n      }\n    }, {\n      key: \"isIE\",\n      value: function isIE() {\n        var ua = window.navigator.userAgent;\n        var msie = ua.indexOf('MSIE ');\n\n        if (msie > 0) {\n          // IE 10 or older => return version number\n          return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);\n        }\n\n        var trident = ua.indexOf('Trident/');\n\n        if (trident > 0) {\n          // IE 11 => return version number\n          var rv = ua.indexOf('rv:');\n          return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);\n        }\n\n        var edge = ua.indexOf('Edge/');\n\n        if (edge > 0) {\n          // Edge (IE 12+) => return version number\n          return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);\n        } // other browser\n\n\n        return false;\n      }\n    }]);\n\n    return Utils;\n  }();\n\n  /**\n   * ApexCharts Animation Class.\n   *\n   * @module Animations\n   **/\n\n  var Animations = /*#__PURE__*/function () {\n    function Animations(ctx) {\n      _classCallCheck(this, Animations);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.setEasingFunctions();\n    }\n\n    _createClass(Animations, [{\n      key: \"setEasingFunctions\",\n      value: function setEasingFunctions() {\n        var easing;\n        if (this.w.globals.easing) return;\n        var userDefinedEasing = this.w.config.chart.animations.easing;\n\n        switch (userDefinedEasing) {\n          case 'linear':\n            {\n              easing = '-';\n              break;\n            }\n\n          case 'easein':\n            {\n              easing = '<';\n              break;\n            }\n\n          case 'easeout':\n            {\n              easing = '>';\n              break;\n            }\n\n          case 'easeinout':\n            {\n              easing = '<>';\n              break;\n            }\n\n          case 'swing':\n            {\n              easing = function easing(pos) {\n                var s = 1.70158;\n                var ret = (pos -= 1) * pos * ((s + 1) * pos + s) + 1;\n                return ret;\n              };\n\n              break;\n            }\n\n          case 'bounce':\n            {\n              easing = function easing(pos) {\n                var ret = '';\n\n                if (pos < 1 / 2.75) {\n                  ret = 7.5625 * pos * pos;\n                } else if (pos < 2 / 2.75) {\n                  ret = 7.5625 * (pos -= 1.5 / 2.75) * pos + 0.75;\n                } else if (pos < 2.5 / 2.75) {\n                  ret = 7.5625 * (pos -= 2.25 / 2.75) * pos + 0.9375;\n                } else {\n                  ret = 7.5625 * (pos -= 2.625 / 2.75) * pos + 0.984375;\n                }\n\n                return ret;\n              };\n\n              break;\n            }\n\n          case 'elastic':\n            {\n              easing = function easing(pos) {\n                if (pos === !!pos) return pos;\n                return Math.pow(2, -10 * pos) * Math.sin((pos - 0.075) * (2 * Math.PI) / 0.3) + 1;\n              };\n\n              break;\n            }\n\n          default:\n            {\n              easing = '<>';\n            }\n        }\n\n        this.w.globals.easing = easing;\n      }\n    }, {\n      key: \"animateLine\",\n      value: function animateLine(el, from, to, speed) {\n        el.attr(from).animate(speed).attr(to);\n      }\n      /*\n       ** Animate radius of a circle element\n       */\n\n    }, {\n      key: \"animateMarker\",\n      value: function animateMarker(el, from, to, speed, easing, cb) {\n        if (!from) from = 0;\n        el.attr({\n          r: from,\n          width: from,\n          height: from\n        }).animate(speed, easing).attr({\n          r: to,\n          width: to.width,\n          height: to.height\n        }).afterAll(function () {\n          cb();\n        });\n      }\n      /*\n       ** Animate radius and position of a circle element\n       */\n\n    }, {\n      key: \"animateCircle\",\n      value: function animateCircle(el, from, to, speed, easing) {\n        el.attr({\n          r: from.r,\n          cx: from.cx,\n          cy: from.cy\n        }).animate(speed, easing).attr({\n          r: to.r,\n          cx: to.cx,\n          cy: to.cy\n        });\n      }\n      /*\n       ** Animate rect properties\n       */\n\n    }, {\n      key: \"animateRect\",\n      value: function animateRect(el, from, to, speed, fn) {\n        el.attr(from).animate(speed).attr(to).afterAll(function () {\n          return fn();\n        });\n      }\n    }, {\n      key: \"animatePathsGradually\",\n      value: function animatePathsGradually(params) {\n        var el = params.el,\n            realIndex = params.realIndex,\n            j = params.j,\n            fill = params.fill,\n            pathFrom = params.pathFrom,\n            pathTo = params.pathTo,\n            speed = params.speed,\n            delay = params.delay;\n        var me = this;\n        var w = this.w;\n        var delayFactor = 0;\n\n        if (w.config.chart.animations.animateGradually.enabled) {\n          delayFactor = w.config.chart.animations.animateGradually.delay;\n        }\n\n        if (w.config.chart.animations.dynamicAnimation.enabled && w.globals.dataChanged && w.config.chart.type !== 'bar') {\n          // disabled due to this bug - https://github.com/apexcharts/vue-apexcharts/issues/75\n          delayFactor = 0;\n        }\n\n        me.morphSVG(el, realIndex, j, w.config.chart.type === 'line' && !w.globals.comboCharts ? 'stroke' : fill, pathFrom, pathTo, speed, delay * delayFactor);\n      }\n    }, {\n      key: \"showDelayedElements\",\n      value: function showDelayedElements() {\n        this.w.globals.delayedElements.forEach(function (d) {\n          var ele = d.el;\n          ele.classList.remove('apexcharts-element-hidden');\n        });\n      }\n    }, {\n      key: \"animationCompleted\",\n      value: function animationCompleted(el) {\n        var w = this.w;\n        if (w.globals.animationEnded) return;\n        w.globals.animationEnded = true;\n        this.showDelayedElements();\n\n        if (typeof w.config.chart.events.animationEnd === 'function') {\n          w.config.chart.events.animationEnd(this.ctx, {\n            el: el,\n            w: w\n          });\n        }\n      } // SVG.js animation for morphing one path to another\n\n    }, {\n      key: \"morphSVG\",\n      value: function morphSVG(el, realIndex, j, fill, pathFrom, pathTo, speed, delay) {\n        var _this = this;\n\n        var w = this.w;\n\n        if (!pathFrom) {\n          pathFrom = el.attr('pathFrom');\n        }\n\n        if (!pathTo) {\n          pathTo = el.attr('pathTo');\n        }\n\n        var disableAnimationForCorrupPath = function disableAnimationForCorrupPath(path) {\n          if (w.config.chart.type === 'radar') {\n            // radar chart drops the path to bottom and hence a corrup path looks ugly\n            // therefore, disable animation for such a case\n            speed = 1;\n          }\n\n          return \"M 0 \".concat(w.globals.gridHeight);\n        };\n\n        if (!pathFrom || pathFrom.indexOf('undefined') > -1 || pathFrom.indexOf('NaN') > -1) {\n          pathFrom = disableAnimationForCorrupPath();\n        }\n\n        if (!pathTo || pathTo.indexOf('undefined') > -1 || pathTo.indexOf('NaN') > -1) {\n          pathTo = disableAnimationForCorrupPath();\n        }\n\n        if (!w.globals.shouldAnimate) {\n          speed = 1;\n        }\n\n        el.plot(pathFrom).animate(1, w.globals.easing, delay).plot(pathFrom).animate(speed, w.globals.easing, delay).plot(pathTo).afterAll(function () {\n          // a flag to indicate that the original mount function can return true now as animation finished here\n          if (Utils$1.isNumber(j)) {\n            if (j === w.globals.series[w.globals.maxValsInArrayIndex].length - 2 && w.globals.shouldAnimate) {\n              _this.animationCompleted(el);\n            }\n          } else if (fill !== 'none' && w.globals.shouldAnimate) {\n            if (!w.globals.comboCharts && realIndex === w.globals.series.length - 1 || w.globals.comboCharts) {\n              _this.animationCompleted(el);\n            }\n          }\n\n          _this.showDelayedElements();\n        });\n      }\n    }]);\n\n    return Animations;\n  }();\n\n  /**\n   * ApexCharts Filters Class for setting hover/active states on the paths.\n   *\n   * @module Formatters\n   **/\n\n  var Filters = /*#__PURE__*/function () {\n    function Filters(ctx) {\n      _classCallCheck(this, Filters);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    } // create a re-usable filter which can be appended other filter effects and applied to multiple elements\n\n\n    _createClass(Filters, [{\n      key: \"getDefaultFilter\",\n      value: function getDefaultFilter(el, i) {\n        var w = this.w;\n        el.unfilter(true);\n        var filter = new window.SVG.Filter();\n        filter.size('120%', '180%', '-5%', '-40%');\n\n        if (w.config.states.normal.filter !== 'none') {\n          this.applyFilter(el, i, w.config.states.normal.filter.type, w.config.states.normal.filter.value);\n        } else {\n          if (w.config.chart.dropShadow.enabled) {\n            this.dropShadow(el, w.config.chart.dropShadow, i);\n          }\n        }\n      }\n    }, {\n      key: \"addNormalFilter\",\n      value: function addNormalFilter(el, i) {\n        var w = this.w; // revert shadow if it was there\n        // but, ignore marker as marker don't have dropshadow yet\n\n        if (w.config.chart.dropShadow.enabled && !el.node.classList.contains('apexcharts-marker')) {\n          this.dropShadow(el, w.config.chart.dropShadow, i);\n        }\n      } // appends dropShadow to the filter object which can be chained with other filter effects\n\n    }, {\n      key: \"addLightenFilter\",\n      value: function addLightenFilter(el, i, attrs) {\n        var _this = this;\n\n        var w = this.w;\n        var intensity = attrs.intensity;\n        el.unfilter(true);\n        var filter = new window.SVG.Filter();\n        el.filter(function (add) {\n          var shadowAttr = w.config.chart.dropShadow;\n\n          if (shadowAttr.enabled) {\n            filter = _this.addShadow(add, i, shadowAttr);\n          } else {\n            filter = add;\n          }\n\n          filter.componentTransfer({\n            rgb: {\n              type: 'linear',\n              slope: 1.5,\n              intercept: intensity\n            }\n          });\n        });\n        el.filterer.node.setAttribute('filterUnits', 'userSpaceOnUse');\n\n        this._scaleFilterSize(el.filterer.node);\n      } // appends dropShadow to the filter object which can be chained with other filter effects\n\n    }, {\n      key: \"addDarkenFilter\",\n      value: function addDarkenFilter(el, i, attrs) {\n        var _this2 = this;\n\n        var w = this.w;\n        var intensity = attrs.intensity;\n        el.unfilter(true);\n        var filter = new window.SVG.Filter();\n        el.filter(function (add) {\n          var shadowAttr = w.config.chart.dropShadow;\n\n          if (shadowAttr.enabled) {\n            filter = _this2.addShadow(add, i, shadowAttr);\n          } else {\n            filter = add;\n          }\n\n          filter.componentTransfer({\n            rgb: {\n              type: 'linear',\n              slope: intensity\n            }\n          });\n        });\n        el.filterer.node.setAttribute('filterUnits', 'userSpaceOnUse');\n\n        this._scaleFilterSize(el.filterer.node);\n      }\n    }, {\n      key: \"applyFilter\",\n      value: function applyFilter(el, i, filter) {\n        var intensity = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.5;\n\n        switch (filter) {\n          case 'none':\n            {\n              this.addNormalFilter(el, i);\n              break;\n            }\n\n          case 'lighten':\n            {\n              this.addLightenFilter(el, i, {\n                intensity: intensity\n              });\n              break;\n            }\n\n          case 'darken':\n            {\n              this.addDarkenFilter(el, i, {\n                intensity: intensity\n              });\n              break;\n            }\n        }\n      } // appends dropShadow to the filter object which can be chained with other filter effects\n\n    }, {\n      key: \"addShadow\",\n      value: function addShadow(add, i, attrs) {\n        var blur = attrs.blur,\n            top = attrs.top,\n            left = attrs.left,\n            color = attrs.color,\n            opacity = attrs.opacity;\n        var shadowBlur = add.flood(Array.isArray(color) ? color[i] : color, opacity).composite(add.sourceAlpha, 'in').offset(left, top).gaussianBlur(blur).merge(add.source);\n        return add.blend(add.source, shadowBlur);\n      } // directly adds dropShadow to the element and returns the same element.\n      // the only way it is different from the addShadow() function is that addShadow is chainable to other filters, while this function discards all filters and add dropShadow\n\n    }, {\n      key: \"dropShadow\",\n      value: function dropShadow(el, attrs) {\n        var i = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n        var top = attrs.top,\n            left = attrs.left,\n            blur = attrs.blur,\n            color = attrs.color,\n            opacity = attrs.opacity,\n            noUserSpaceOnUse = attrs.noUserSpaceOnUse;\n        var w = this.w;\n        el.unfilter(true);\n\n        if (Utils$1.isIE() && w.config.chart.type === 'radialBar') {\n          // in radialbar charts, dropshadow is clipping actual drawing in IE\n          return el;\n        }\n\n        color = Array.isArray(color) ? color[i] : color;\n        el.filter(function (add) {\n          var shadowBlur = null;\n\n          if (Utils$1.isSafari() || Utils$1.isFirefox() || Utils$1.isIE()) {\n            // safari/firefox/IE have some alternative way to use this filter\n            shadowBlur = add.flood(color, opacity).composite(add.sourceAlpha, 'in').offset(left, top).gaussianBlur(blur);\n          } else {\n            shadowBlur = add.flood(color, opacity).composite(add.sourceAlpha, 'in').offset(left, top).gaussianBlur(blur).merge(add.source);\n          }\n\n          add.blend(add.source, shadowBlur);\n        });\n\n        if (!noUserSpaceOnUse) {\n          el.filterer.node.setAttribute('filterUnits', 'userSpaceOnUse');\n        }\n\n        this._scaleFilterSize(el.filterer.node);\n\n        return el;\n      }\n    }, {\n      key: \"setSelectionFilter\",\n      value: function setSelectionFilter(el, realIndex, dataPointIndex) {\n        var w = this.w;\n\n        if (typeof w.globals.selectedDataPoints[realIndex] !== 'undefined') {\n          if (w.globals.selectedDataPoints[realIndex].indexOf(dataPointIndex) > -1) {\n            el.node.setAttribute('selected', true);\n            var activeFilter = w.config.states.active.filter;\n\n            if (activeFilter !== 'none') {\n              this.applyFilter(el, realIndex, activeFilter.type, activeFilter.value);\n            }\n          }\n        }\n      }\n    }, {\n      key: \"_scaleFilterSize\",\n      value: function _scaleFilterSize(el) {\n        var setAttributes = function setAttributes(attrs) {\n          for (var key in attrs) {\n            if (attrs.hasOwnProperty(key)) {\n              el.setAttribute(key, attrs[key]);\n            }\n          }\n        };\n\n        setAttributes({\n          width: '200%',\n          height: '200%',\n          x: '-50%',\n          y: '-50%'\n        });\n      }\n    }]);\n\n    return Filters;\n  }();\n\n  /**\n   * ApexCharts Graphics Class for all drawing operations.\n   *\n   * @module Graphics\n   **/\n\n  var Graphics = /*#__PURE__*/function () {\n    function Graphics(ctx) {\n      _classCallCheck(this, Graphics);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n    /*****************************************************************************\n     *                                                                            *\n     *  SVG Path Rounding Function                                                *\n     *  Copyright (C) 2014 Yona Appletree                                         *\n     *                                                                            *\n     *  Licensed under the Apache License, Version 2.0 (the \"License\");           *\n     *  you may not use this file except in compliance with the License.          *\n     *  You may obtain a copy of the License at                                   *\n     *                                                                            *\n     *      http://www.apache.org/licenses/LICENSE-2.0                            *\n     *                                                                            *\n     *  Unless required by applicable law or agreed to in writing, software       *\n     *  distributed under the License is distributed on an \"AS IS\" BASIS,         *\n     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *\n     *  See the License for the specific language governing permissions and       *\n     *  limitations under the License.                                            *\n     *                                                                            *\n     *****************************************************************************/\n\n    /**\n     * SVG Path rounding function. Takes an input path string and outputs a path\n     * string where all line-line corners have been rounded. Only supports absolute\n     * commands at the moment.\n     *\n     * @param pathString The SVG input path\n     * @param radius The amount to round the corners, either a value in the SVG\n     *               coordinate space, or, if useFractionalRadius is true, a value\n     *               from 0 to 1.\n     * @returns A new SVG path string with the rounding\n     */\n\n\n    _createClass(Graphics, [{\n      key: \"roundPathCorners\",\n      value: function roundPathCorners(pathString, radius) {\n        function moveTowardsLength(movingPoint, targetPoint, amount) {\n          var width = targetPoint.x - movingPoint.x;\n          var height = targetPoint.y - movingPoint.y;\n          var distance = Math.sqrt(width * width + height * height);\n          return moveTowardsFractional(movingPoint, targetPoint, Math.min(1, amount / distance));\n        }\n\n        function moveTowardsFractional(movingPoint, targetPoint, fraction) {\n          return {\n            x: movingPoint.x + (targetPoint.x - movingPoint.x) * fraction,\n            y: movingPoint.y + (targetPoint.y - movingPoint.y) * fraction\n          };\n        } // Adjusts the ending position of a command\n\n\n        function adjustCommand(cmd, newPoint) {\n          if (cmd.length > 2) {\n            cmd[cmd.length - 2] = newPoint.x;\n            cmd[cmd.length - 1] = newPoint.y;\n          }\n        } // Gives an {x, y} object for a command's ending position\n\n\n        function pointForCommand(cmd) {\n          return {\n            x: parseFloat(cmd[cmd.length - 2]),\n            y: parseFloat(cmd[cmd.length - 1])\n          };\n        } // Split apart the path, handing concatonated letters and numbers\n\n\n        var pathParts = pathString.split(/[,\\s]/).reduce(function (parts, part) {\n          var match = part.match('([a-zA-Z])(.+)');\n\n          if (match) {\n            parts.push(match[1]);\n            parts.push(match[2]);\n          } else {\n            parts.push(part);\n          }\n\n          return parts;\n        }, []); // Group the commands with their arguments for easier handling\n\n        var commands = pathParts.reduce(function (commands, part) {\n          if (parseFloat(part) == part && commands.length) {\n            commands[commands.length - 1].push(part);\n          } else {\n            commands.push([part]);\n          }\n\n          return commands;\n        }, []); // The resulting commands, also grouped\n\n        var resultCommands = [];\n\n        if (commands.length > 1) {\n          var startPoint = pointForCommand(commands[0]); // Handle the close path case with a \"virtual\" closing line\n\n          var virtualCloseLine = null;\n\n          if (commands[commands.length - 1][0] == 'Z' && commands[0].length > 2) {\n            virtualCloseLine = ['L', startPoint.x, startPoint.y];\n            commands[commands.length - 1] = virtualCloseLine;\n          } // We always use the first command (but it may be mutated)\n\n\n          resultCommands.push(commands[0]);\n\n          for (var cmdIndex = 1; cmdIndex < commands.length; cmdIndex++) {\n            var prevCmd = resultCommands[resultCommands.length - 1];\n            var curCmd = commands[cmdIndex]; // Handle closing case\n\n            var nextCmd = curCmd == virtualCloseLine ? commands[1] : commands[cmdIndex + 1]; // Nasty logic to decide if this path is a candidite.\n\n            if (nextCmd && prevCmd && prevCmd.length > 2 && curCmd[0] == 'L' && nextCmd.length > 2 && nextCmd[0] == 'L') {\n              // Calc the points we're dealing with\n              var prevPoint = pointForCommand(prevCmd);\n              var curPoint = pointForCommand(curCmd);\n              var nextPoint = pointForCommand(nextCmd); // The start and end of the cuve are just our point moved towards the previous and next points, respectivly\n\n              var curveStart, curveEnd;\n              curveStart = moveTowardsLength(curPoint, prevPoint, radius);\n              curveEnd = moveTowardsLength(curPoint, nextPoint, radius); // Adjust the current command and add it\n\n              adjustCommand(curCmd, curveStart);\n              curCmd.origPoint = curPoint;\n              resultCommands.push(curCmd); // The curve control points are halfway between the start/end of the curve and\n              // the original point\n\n              var startControl = moveTowardsFractional(curveStart, curPoint, 0.5);\n              var endControl = moveTowardsFractional(curPoint, curveEnd, 0.5); // Create the curve\n\n              var curveCmd = ['C', startControl.x, startControl.y, endControl.x, endControl.y, curveEnd.x, curveEnd.y]; // Save the original point for fractional calculations\n\n              curveCmd.origPoint = curPoint;\n              resultCommands.push(curveCmd);\n            } else {\n              // Pass through commands that don't qualify\n              resultCommands.push(curCmd);\n            }\n          } // Fix up the starting point and restore the close path if the path was orignally closed\n\n\n          if (virtualCloseLine) {\n            var newStartPoint = pointForCommand(resultCommands[resultCommands.length - 1]);\n            resultCommands.push(['Z']);\n            adjustCommand(resultCommands[0], newStartPoint);\n          }\n        } else {\n          resultCommands = commands;\n        }\n\n        return resultCommands.reduce(function (str, c) {\n          return str + c.join(' ') + ' ';\n        }, '');\n      }\n    }, {\n      key: \"drawLine\",\n      value: function drawLine(x1, y1, x2, y2) {\n        var lineColor = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '#a8a8a8';\n        var dashArray = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;\n        var strokeWidth = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null;\n        var strokeLineCap = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 'butt';\n        var w = this.w;\n        var line = w.globals.dom.Paper.line().attr({\n          x1: x1,\n          y1: y1,\n          x2: x2,\n          y2: y2,\n          stroke: lineColor,\n          'stroke-dasharray': dashArray,\n          'stroke-width': strokeWidth,\n          'stroke-linecap': strokeLineCap\n        });\n        return line;\n      }\n    }, {\n      key: \"drawRect\",\n      value: function drawRect() {\n        var x1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n        var y1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n        var x2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n        var y2 = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;\n        var radius = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;\n        var color = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : '#fefefe';\n        var opacity = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 1;\n        var strokeWidth = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;\n        var strokeColor = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;\n        var strokeDashArray = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : 0;\n        var w = this.w;\n        var rect = w.globals.dom.Paper.rect();\n        rect.attr({\n          x: x1,\n          y: y1,\n          width: x2 > 0 ? x2 : 0,\n          height: y2 > 0 ? y2 : 0,\n          rx: radius,\n          ry: radius,\n          opacity: opacity,\n          'stroke-width': strokeWidth !== null ? strokeWidth : 0,\n          stroke: strokeColor !== null ? strokeColor : 'none',\n          'stroke-dasharray': strokeDashArray\n        }); // fix apexcharts.js#1410\n\n        rect.node.setAttribute('fill', color);\n        return rect;\n      }\n    }, {\n      key: \"drawPolygon\",\n      value: function drawPolygon(polygonString) {\n        var stroke = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '#e1e1e1';\n        var strokeWidth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;\n        var fill = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'none';\n        var w = this.w;\n        var polygon = w.globals.dom.Paper.polygon(polygonString).attr({\n          fill: fill,\n          stroke: stroke,\n          'stroke-width': strokeWidth\n        });\n        return polygon;\n      }\n    }, {\n      key: \"drawCircle\",\n      value: function drawCircle(radius) {\n        var attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n        var w = this.w;\n        if (radius < 0) radius = 0;\n        var c = w.globals.dom.Paper.circle(radius * 2);\n\n        if (attrs !== null) {\n          c.attr(attrs);\n        }\n\n        return c;\n      }\n    }, {\n      key: \"drawPath\",\n      value: function drawPath(_ref) {\n        var _ref$d = _ref.d,\n            d = _ref$d === void 0 ? '' : _ref$d,\n            _ref$stroke = _ref.stroke,\n            stroke = _ref$stroke === void 0 ? '#a8a8a8' : _ref$stroke,\n            _ref$strokeWidth = _ref.strokeWidth,\n            strokeWidth = _ref$strokeWidth === void 0 ? 1 : _ref$strokeWidth,\n            fill = _ref.fill,\n            _ref$fillOpacity = _ref.fillOpacity,\n            fillOpacity = _ref$fillOpacity === void 0 ? 1 : _ref$fillOpacity,\n            _ref$strokeOpacity = _ref.strokeOpacity,\n            strokeOpacity = _ref$strokeOpacity === void 0 ? 1 : _ref$strokeOpacity,\n            classes = _ref.classes,\n            _ref$strokeLinecap = _ref.strokeLinecap,\n            strokeLinecap = _ref$strokeLinecap === void 0 ? null : _ref$strokeLinecap,\n            _ref$strokeDashArray = _ref.strokeDashArray,\n            strokeDashArray = _ref$strokeDashArray === void 0 ? 0 : _ref$strokeDashArray;\n        var w = this.w;\n\n        if (strokeLinecap === null) {\n          strokeLinecap = w.config.stroke.lineCap;\n        }\n\n        if (d.indexOf('undefined') > -1 || d.indexOf('NaN') > -1) {\n          d = \"M 0 \".concat(w.globals.gridHeight);\n        }\n\n        var p = w.globals.dom.Paper.path(d).attr({\n          fill: fill,\n          'fill-opacity': fillOpacity,\n          stroke: stroke,\n          'stroke-opacity': strokeOpacity,\n          'stroke-linecap': strokeLinecap,\n          'stroke-width': strokeWidth,\n          'stroke-dasharray': strokeDashArray,\n          class: classes\n        });\n        return p;\n      }\n    }, {\n      key: \"group\",\n      value: function group() {\n        var attrs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n        var w = this.w;\n        var g = w.globals.dom.Paper.group();\n\n        if (attrs !== null) {\n          g.attr(attrs);\n        }\n\n        return g;\n      }\n    }, {\n      key: \"move\",\n      value: function move(x, y) {\n        var move = ['M', x, y].join(' ');\n        return move;\n      }\n    }, {\n      key: \"line\",\n      value: function line(x, y) {\n        var hORv = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n        var line = null;\n\n        if (hORv === null) {\n          line = [' L', x, y].join(' ');\n        } else if (hORv === 'H') {\n          line = [' H', x].join(' ');\n        } else if (hORv === 'V') {\n          line = [' V', y].join(' ');\n        }\n\n        return line;\n      }\n    }, {\n      key: \"curve\",\n      value: function curve(x1, y1, x2, y2, x, y) {\n        var curve = ['C', x1, y1, x2, y2, x, y].join(' ');\n        return curve;\n      }\n    }, {\n      key: \"quadraticCurve\",\n      value: function quadraticCurve(x1, y1, x, y) {\n        var curve = ['Q', x1, y1, x, y].join(' ');\n        return curve;\n      }\n    }, {\n      key: \"arc\",\n      value: function arc(rx, ry, axisRotation, largeArcFlag, sweepFlag, x, y) {\n        var relative = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;\n        var coord = 'A';\n        if (relative) coord = 'a';\n        var arc = [coord, rx, ry, axisRotation, largeArcFlag, sweepFlag, x, y].join(' ');\n        return arc;\n      }\n      /**\n       * @memberof Graphics\n       * @param {object}\n       *  i = series's index\n       *  realIndex = realIndex is series's actual index when it was drawn time. After several redraws, the iterating \"i\" may change in loops, but realIndex doesn't\n       *  pathFrom = existing pathFrom to animateTo\n       *  pathTo = new Path to which d attr will be animated from pathFrom to pathTo\n       *  stroke = line Color\n       *  strokeWidth = width of path Line\n       *  fill = it can be gradient, single color, pattern or image\n       *  animationDelay = how much to delay when starting animation (in milliseconds)\n       *  dataChangeSpeed = for dynamic animations, when data changes\n       *  className = class attribute to add\n       * @return {object} svg.js path object\n       **/\n\n    }, {\n      key: \"renderPaths\",\n      value: function renderPaths(_ref2) {\n        var j = _ref2.j,\n            realIndex = _ref2.realIndex,\n            pathFrom = _ref2.pathFrom,\n            pathTo = _ref2.pathTo,\n            stroke = _ref2.stroke,\n            strokeWidth = _ref2.strokeWidth,\n            strokeLinecap = _ref2.strokeLinecap,\n            fill = _ref2.fill,\n            animationDelay = _ref2.animationDelay,\n            initialSpeed = _ref2.initialSpeed,\n            dataChangeSpeed = _ref2.dataChangeSpeed,\n            className = _ref2.className,\n            _ref2$shouldClipToGri = _ref2.shouldClipToGrid,\n            shouldClipToGrid = _ref2$shouldClipToGri === void 0 ? true : _ref2$shouldClipToGri,\n            _ref2$bindEventsOnPat = _ref2.bindEventsOnPaths,\n            bindEventsOnPaths = _ref2$bindEventsOnPat === void 0 ? true : _ref2$bindEventsOnPat,\n            _ref2$drawShadow = _ref2.drawShadow,\n            drawShadow = _ref2$drawShadow === void 0 ? true : _ref2$drawShadow;\n        var w = this.w;\n        var filters = new Filters(this.ctx);\n        var anim = new Animations(this.ctx);\n        var initialAnim = this.w.config.chart.animations.enabled;\n        var dynamicAnim = initialAnim && this.w.config.chart.animations.dynamicAnimation.enabled;\n        var d;\n        var shouldAnimate = !!(initialAnim && !w.globals.resized || dynamicAnim && w.globals.dataChanged && w.globals.shouldAnimate);\n\n        if (shouldAnimate) {\n          d = pathFrom;\n        } else {\n          d = pathTo;\n          w.globals.animationEnded = true;\n        }\n\n        var strokeDashArrayOpt = w.config.stroke.dashArray;\n        var strokeDashArray = 0;\n\n        if (Array.isArray(strokeDashArrayOpt)) {\n          strokeDashArray = strokeDashArrayOpt[realIndex];\n        } else {\n          strokeDashArray = w.config.stroke.dashArray;\n        }\n\n        var el = this.drawPath({\n          d: d,\n          stroke: stroke,\n          strokeWidth: strokeWidth,\n          fill: fill,\n          fillOpacity: 1,\n          classes: className,\n          strokeLinecap: strokeLinecap,\n          strokeDashArray: strokeDashArray\n        });\n        el.attr('index', realIndex);\n\n        if (shouldClipToGrid) {\n          el.attr({\n            'clip-path': \"url(#gridRectMask\".concat(w.globals.cuid, \")\")\n          });\n        } // const defaultFilter = el.filterer\n\n\n        if (w.config.states.normal.filter.type !== 'none') {\n          filters.getDefaultFilter(el, realIndex);\n        } else {\n          if (w.config.chart.dropShadow.enabled && drawShadow) {\n            if (!w.config.chart.dropShadow.enabledOnSeries || w.config.chart.dropShadow.enabledOnSeries && w.config.chart.dropShadow.enabledOnSeries.indexOf(realIndex) !== -1) {\n              var shadow = w.config.chart.dropShadow;\n              filters.dropShadow(el, shadow, realIndex);\n            }\n          }\n        }\n\n        if (bindEventsOnPaths) {\n          el.node.addEventListener('mouseenter', this.pathMouseEnter.bind(this, el));\n          el.node.addEventListener('mouseleave', this.pathMouseLeave.bind(this, el));\n          el.node.addEventListener('mousedown', this.pathMouseDown.bind(this, el));\n        }\n\n        el.attr({\n          pathTo: pathTo,\n          pathFrom: pathFrom\n        });\n        var defaultAnimateOpts = {\n          el: el,\n          j: j,\n          realIndex: realIndex,\n          pathFrom: pathFrom,\n          pathTo: pathTo,\n          fill: fill,\n          strokeWidth: strokeWidth,\n          delay: animationDelay\n        };\n\n        if (initialAnim && !w.globals.resized && !w.globals.dataChanged) {\n          anim.animatePathsGradually(_objectSpread2(_objectSpread2({}, defaultAnimateOpts), {}, {\n            speed: initialSpeed\n          }));\n        } else {\n          if (w.globals.resized || !w.globals.dataChanged) {\n            anim.showDelayedElements();\n          }\n        }\n\n        if (w.globals.dataChanged && dynamicAnim && shouldAnimate) {\n          anim.animatePathsGradually(_objectSpread2(_objectSpread2({}, defaultAnimateOpts), {}, {\n            speed: dataChangeSpeed\n          }));\n        }\n\n        return el;\n      }\n    }, {\n      key: \"drawPattern\",\n      value: function drawPattern(style, width, height) {\n        var stroke = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '#a8a8a8';\n        var strokeWidth = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;\n        var w = this.w;\n        var p = w.globals.dom.Paper.pattern(width, height, function (add) {\n          if (style === 'horizontalLines') {\n            add.line(0, 0, height, 0).stroke({\n              color: stroke,\n              width: strokeWidth + 1\n            });\n          } else if (style === 'verticalLines') {\n            add.line(0, 0, 0, width).stroke({\n              color: stroke,\n              width: strokeWidth + 1\n            });\n          } else if (style === 'slantedLines') {\n            add.line(0, 0, width, height).stroke({\n              color: stroke,\n              width: strokeWidth\n            });\n          } else if (style === 'squares') {\n            add.rect(width, height).fill('none').stroke({\n              color: stroke,\n              width: strokeWidth\n            });\n          } else if (style === 'circles') {\n            add.circle(width).fill('none').stroke({\n              color: stroke,\n              width: strokeWidth\n            });\n          }\n        });\n        return p;\n      }\n    }, {\n      key: \"drawGradient\",\n      value: function drawGradient(style, gfrom, gto, opacityFrom, opacityTo) {\n        var size = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;\n        var stops = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null;\n        var colorStops = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;\n        var i = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : 0;\n        var w = this.w;\n        var g;\n\n        if (gfrom.length < 9 && gfrom.indexOf('#') === 0) {\n          // if the hex contains alpha and is of 9 digit, skip the opacity\n          gfrom = Utils$1.hexToRgba(gfrom, opacityFrom);\n        }\n\n        if (gto.length < 9 && gto.indexOf('#') === 0) {\n          gto = Utils$1.hexToRgba(gto, opacityTo);\n        }\n\n        var stop1 = 0;\n        var stop2 = 1;\n        var stop3 = 1;\n        var stop4 = null;\n\n        if (stops !== null) {\n          stop1 = typeof stops[0] !== 'undefined' ? stops[0] / 100 : 0;\n          stop2 = typeof stops[1] !== 'undefined' ? stops[1] / 100 : 1;\n          stop3 = typeof stops[2] !== 'undefined' ? stops[2] / 100 : 1;\n          stop4 = typeof stops[3] !== 'undefined' ? stops[3] / 100 : null;\n        }\n\n        var radial = !!(w.config.chart.type === 'donut' || w.config.chart.type === 'pie' || w.config.chart.type === 'polarArea' || w.config.chart.type === 'bubble');\n\n        if (colorStops === null || colorStops.length === 0) {\n          g = w.globals.dom.Paper.gradient(radial ? 'radial' : 'linear', function (stop) {\n            stop.at(stop1, gfrom, opacityFrom);\n            stop.at(stop2, gto, opacityTo);\n            stop.at(stop3, gto, opacityTo);\n\n            if (stop4 !== null) {\n              stop.at(stop4, gfrom, opacityFrom);\n            }\n          });\n        } else {\n          g = w.globals.dom.Paper.gradient(radial ? 'radial' : 'linear', function (stop) {\n            var gradientStops = Array.isArray(colorStops[i]) ? colorStops[i] : colorStops;\n            gradientStops.forEach(function (s) {\n              stop.at(s.offset / 100, s.color, s.opacity);\n            });\n          });\n        }\n\n        if (!radial) {\n          if (style === 'vertical') {\n            g.from(0, 0).to(0, 1);\n          } else if (style === 'diagonal') {\n            g.from(0, 0).to(1, 1);\n          } else if (style === 'horizontal') {\n            g.from(0, 1).to(1, 1);\n          } else if (style === 'diagonal2') {\n            g.from(1, 0).to(0, 1);\n          }\n        } else {\n          var offx = w.globals.gridWidth / 2;\n          var offy = w.globals.gridHeight / 2;\n\n          if (w.config.chart.type !== 'bubble') {\n            g.attr({\n              gradientUnits: 'userSpaceOnUse',\n              cx: offx,\n              cy: offy,\n              r: size\n            });\n          } else {\n            g.attr({\n              cx: 0.5,\n              cy: 0.5,\n              r: 0.8,\n              fx: 0.2,\n              fy: 0.2\n            });\n          }\n        }\n\n        return g;\n      }\n    }, {\n      key: \"getTextBasedOnMaxWidth\",\n      value: function getTextBasedOnMaxWidth(_ref3) {\n        var text = _ref3.text,\n            maxWidth = _ref3.maxWidth,\n            fontSize = _ref3.fontSize,\n            fontFamily = _ref3.fontFamily;\n        var tRects = this.getTextRects(text, fontSize, fontFamily);\n        var wordWidth = tRects.width / text.length;\n        var wordsBasedOnWidth = Math.floor(maxWidth / wordWidth);\n\n        if (maxWidth < tRects.width) {\n          return text.slice(0, wordsBasedOnWidth - 3) + '...';\n        }\n\n        return text;\n      }\n    }, {\n      key: \"drawText\",\n      value: function drawText(_ref4) {\n        var _this = this;\n\n        var x = _ref4.x,\n            y = _ref4.y,\n            text = _ref4.text,\n            textAnchor = _ref4.textAnchor,\n            fontSize = _ref4.fontSize,\n            fontFamily = _ref4.fontFamily,\n            fontWeight = _ref4.fontWeight,\n            foreColor = _ref4.foreColor,\n            opacity = _ref4.opacity,\n            maxWidth = _ref4.maxWidth,\n            _ref4$cssClass = _ref4.cssClass,\n            cssClass = _ref4$cssClass === void 0 ? '' : _ref4$cssClass,\n            _ref4$isPlainText = _ref4.isPlainText,\n            isPlainText = _ref4$isPlainText === void 0 ? true : _ref4$isPlainText;\n        var w = this.w;\n        if (typeof text === 'undefined') text = '';\n        var truncatedText = text;\n\n        if (!textAnchor) {\n          textAnchor = 'start';\n        }\n\n        if (!foreColor || !foreColor.length) {\n          foreColor = w.config.chart.foreColor;\n        }\n\n        fontFamily = fontFamily || w.config.chart.fontFamily;\n        fontSize = fontSize || '11px';\n        fontWeight = fontWeight || 'regular';\n        var commonProps = {\n          maxWidth: maxWidth,\n          fontSize: fontSize,\n          fontFamily: fontFamily\n        };\n        var elText;\n\n        if (Array.isArray(text)) {\n          elText = w.globals.dom.Paper.text(function (add) {\n            for (var i = 0; i < text.length; i++) {\n              truncatedText = text[i];\n\n              if (maxWidth) {\n                truncatedText = _this.getTextBasedOnMaxWidth(_objectSpread2({\n                  text: text[i]\n                }, commonProps));\n              }\n\n              i === 0 ? add.tspan(truncatedText) : add.tspan(truncatedText).newLine();\n            }\n          });\n        } else {\n          if (maxWidth) {\n            truncatedText = this.getTextBasedOnMaxWidth(_objectSpread2({\n              text: text\n            }, commonProps));\n          }\n\n          elText = isPlainText ? w.globals.dom.Paper.plain(text) : w.globals.dom.Paper.text(function (add) {\n            return add.tspan(truncatedText);\n          });\n        }\n\n        elText.attr({\n          x: x,\n          y: y,\n          'text-anchor': textAnchor,\n          'dominant-baseline': 'auto',\n          'font-size': fontSize,\n          'font-family': fontFamily,\n          'font-weight': fontWeight,\n          fill: foreColor,\n          class: 'apexcharts-text ' + cssClass\n        });\n        elText.node.style.fontFamily = fontFamily;\n        elText.node.style.opacity = opacity;\n        return elText;\n      }\n    }, {\n      key: \"drawMarker\",\n      value: function drawMarker(x, y, opts) {\n        x = x || 0;\n        var size = opts.pSize || 0;\n        var elPoint = null;\n\n        if (opts.shape === 'square' || opts.shape === 'rect') {\n          var radius = opts.pRadius === undefined ? size / 2 : opts.pRadius;\n\n          if (y === null || !size) {\n            size = 0;\n            radius = 0;\n          }\n\n          var nSize = size * 1.2 + radius;\n          var p = this.drawRect(nSize, nSize, nSize, nSize, radius);\n          p.attr({\n            x: x - nSize / 2,\n            y: y - nSize / 2,\n            cx: x,\n            cy: y,\n            class: opts.class ? opts.class : '',\n            fill: opts.pointFillColor,\n            'fill-opacity': opts.pointFillOpacity ? opts.pointFillOpacity : 1,\n            stroke: opts.pointStrokeColor,\n            'stroke-width': opts.pointStrokeWidth ? opts.pointStrokeWidth : 0,\n            'stroke-opacity': opts.pointStrokeOpacity ? opts.pointStrokeOpacity : 1\n          });\n          elPoint = p;\n        } else if (opts.shape === 'circle' || !opts.shape) {\n          if (!Utils$1.isNumber(y)) {\n            size = 0;\n            y = 0;\n          } // let nSize = size - opts.pRadius / 2 < 0 ? 0 : size - opts.pRadius / 2\n\n\n          elPoint = this.drawCircle(size, {\n            cx: x,\n            cy: y,\n            class: opts.class ? opts.class : '',\n            stroke: opts.pointStrokeColor,\n            fill: opts.pointFillColor,\n            'fill-opacity': opts.pointFillOpacity ? opts.pointFillOpacity : 1,\n            'stroke-width': opts.pointStrokeWidth ? opts.pointStrokeWidth : 0,\n            'stroke-opacity': opts.pointStrokeOpacity ? opts.pointStrokeOpacity : 1\n          });\n        }\n\n        return elPoint;\n      }\n    }, {\n      key: \"pathMouseEnter\",\n      value: function pathMouseEnter(path, e) {\n        var w = this.w;\n        var filters = new Filters(this.ctx);\n        var i = parseInt(path.node.getAttribute('index'), 10);\n        var j = parseInt(path.node.getAttribute('j'), 10);\n\n        if (typeof w.config.chart.events.dataPointMouseEnter === 'function') {\n          w.config.chart.events.dataPointMouseEnter(e, this.ctx, {\n            seriesIndex: i,\n            dataPointIndex: j,\n            w: w\n          });\n        }\n\n        this.ctx.events.fireEvent('dataPointMouseEnter', [e, this.ctx, {\n          seriesIndex: i,\n          dataPointIndex: j,\n          w: w\n        }]);\n\n        if (w.config.states.active.filter.type !== 'none') {\n          if (path.node.getAttribute('selected') === 'true') {\n            return;\n          }\n        }\n\n        if (w.config.states.hover.filter.type !== 'none') {\n          if (!w.globals.isTouchDevice) {\n            var hoverFilter = w.config.states.hover.filter;\n            filters.applyFilter(path, i, hoverFilter.type, hoverFilter.value);\n          }\n        }\n      }\n    }, {\n      key: \"pathMouseLeave\",\n      value: function pathMouseLeave(path, e) {\n        var w = this.w;\n        var filters = new Filters(this.ctx);\n        var i = parseInt(path.node.getAttribute('index'), 10);\n        var j = parseInt(path.node.getAttribute('j'), 10);\n\n        if (typeof w.config.chart.events.dataPointMouseLeave === 'function') {\n          w.config.chart.events.dataPointMouseLeave(e, this.ctx, {\n            seriesIndex: i,\n            dataPointIndex: j,\n            w: w\n          });\n        }\n\n        this.ctx.events.fireEvent('dataPointMouseLeave', [e, this.ctx, {\n          seriesIndex: i,\n          dataPointIndex: j,\n          w: w\n        }]);\n\n        if (w.config.states.active.filter.type !== 'none') {\n          if (path.node.getAttribute('selected') === 'true') {\n            return;\n          }\n        }\n\n        if (w.config.states.hover.filter.type !== 'none') {\n          filters.getDefaultFilter(path, i);\n        }\n      }\n    }, {\n      key: \"pathMouseDown\",\n      value: function pathMouseDown(path, e) {\n        var w = this.w;\n        var filters = new Filters(this.ctx);\n        var i = parseInt(path.node.getAttribute('index'), 10);\n        var j = parseInt(path.node.getAttribute('j'), 10);\n        var selected = 'false';\n\n        if (path.node.getAttribute('selected') === 'true') {\n          path.node.setAttribute('selected', 'false');\n\n          if (w.globals.selectedDataPoints[i].indexOf(j) > -1) {\n            var index = w.globals.selectedDataPoints[i].indexOf(j);\n            w.globals.selectedDataPoints[i].splice(index, 1);\n          }\n        } else {\n          if (!w.config.states.active.allowMultipleDataPointsSelection && w.globals.selectedDataPoints.length > 0) {\n            w.globals.selectedDataPoints = [];\n            var elPaths = w.globals.dom.Paper.select('.apexcharts-series path').members;\n            var elCircles = w.globals.dom.Paper.select('.apexcharts-series circle, .apexcharts-series rect').members;\n\n            var deSelect = function deSelect(els) {\n              Array.prototype.forEach.call(els, function (el) {\n                el.node.setAttribute('selected', 'false');\n                filters.getDefaultFilter(el, i);\n              });\n            };\n\n            deSelect(elPaths);\n            deSelect(elCircles);\n          }\n\n          path.node.setAttribute('selected', 'true');\n          selected = 'true';\n\n          if (typeof w.globals.selectedDataPoints[i] === 'undefined') {\n            w.globals.selectedDataPoints[i] = [];\n          }\n\n          w.globals.selectedDataPoints[i].push(j);\n        }\n\n        if (selected === 'true') {\n          var activeFilter = w.config.states.active.filter;\n\n          if (activeFilter !== 'none') {\n            filters.applyFilter(path, i, activeFilter.type, activeFilter.value);\n          } else {\n            // Reapply the hover filter in case it was removed by `deselect`when there is no active filter and it is not a touch device\n            if (w.config.states.hover.filter !== 'none') {\n              if (!w.globals.isTouchDevice) {\n                var hoverFilter = w.config.states.hover.filter;\n                filters.applyFilter(path, i, hoverFilter.type, hoverFilter.value);\n              }\n            }\n          }\n        } else {\n          // If the item was deselected, apply hover state filter if it is not a touch device\n          if (w.config.states.active.filter.type !== 'none') {\n            if (w.config.states.hover.filter.type !== 'none' && !w.globals.isTouchDevice) {\n              var hoverFilter = w.config.states.hover.filter;\n              filters.applyFilter(path, i, hoverFilter.type, hoverFilter.value);\n            } else {\n              filters.getDefaultFilter(path, i);\n            }\n          }\n        }\n\n        if (typeof w.config.chart.events.dataPointSelection === 'function') {\n          w.config.chart.events.dataPointSelection(e, this.ctx, {\n            selectedDataPoints: w.globals.selectedDataPoints,\n            seriesIndex: i,\n            dataPointIndex: j,\n            w: w\n          });\n        }\n\n        if (e) {\n          this.ctx.events.fireEvent('dataPointSelection', [e, this.ctx, {\n            selectedDataPoints: w.globals.selectedDataPoints,\n            seriesIndex: i,\n            dataPointIndex: j,\n            w: w\n          }]);\n        }\n      }\n    }, {\n      key: \"rotateAroundCenter\",\n      value: function rotateAroundCenter(el) {\n        var coord = {};\n\n        if (el && typeof el.getBBox === 'function') {\n          coord = el.getBBox();\n        }\n\n        var x = coord.x + coord.width / 2;\n        var y = coord.y + coord.height / 2;\n        return {\n          x: x,\n          y: y\n        };\n      }\n    }, {\n      key: \"getTextRects\",\n      value: function getTextRects(text, fontSize, fontFamily, transform) {\n        var useBBox = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;\n        var w = this.w;\n        var virtualText = this.drawText({\n          x: -200,\n          y: -200,\n          text: text,\n          textAnchor: 'start',\n          fontSize: fontSize,\n          fontFamily: fontFamily,\n          foreColor: '#fff',\n          opacity: 0\n        });\n\n        if (transform) {\n          virtualText.attr('transform', transform);\n        }\n\n        w.globals.dom.Paper.add(virtualText);\n        var rect = virtualText.bbox();\n\n        if (!useBBox) {\n          rect = virtualText.node.getBoundingClientRect();\n        }\n\n        virtualText.remove();\n        return {\n          width: rect.width,\n          height: rect.height\n        };\n      }\n      /**\n       * append ... to long text\n       * http://stackoverflow.com/questions/9241315/trimming-text-to-a-given-pixel-width-in-svg\n       * @memberof Graphics\n       **/\n\n    }, {\n      key: \"placeTextWithEllipsis\",\n      value: function placeTextWithEllipsis(textObj, textString, width) {\n        if (typeof textObj.getComputedTextLength !== 'function') return;\n        textObj.textContent = textString;\n\n        if (textString.length > 0) {\n          // ellipsis is needed\n          if (textObj.getComputedTextLength() >= width / 1.1) {\n            for (var x = textString.length - 3; x > 0; x -= 3) {\n              if (textObj.getSubStringLength(0, x) <= width / 1.1) {\n                textObj.textContent = textString.substring(0, x) + '...';\n                return;\n              }\n            }\n\n            textObj.textContent = '.'; // can't place at all\n          }\n        }\n      }\n    }], [{\n      key: \"setAttrs\",\n      value: function setAttrs(el, attrs) {\n        for (var key in attrs) {\n          if (attrs.hasOwnProperty(key)) {\n            el.setAttribute(key, attrs[key]);\n          }\n        }\n      }\n    }]);\n\n    return Graphics;\n  }();\n\n  /*\n   ** Util functions which are dependent on ApexCharts instance\n   */\n  var CoreUtils = /*#__PURE__*/function () {\n    function CoreUtils(ctx) {\n      _classCallCheck(this, CoreUtils);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(CoreUtils, [{\n      key: \"getStackedSeriesTotals\",\n      value:\n      /**\n       * @memberof CoreUtils\n       * returns the sum of all individual values in a multiple stacked series\n       * Eg. w.globals.series = [[32,33,43,12], [2,3,5,1]]\n       *  @return [34,36,48,13]\n       **/\n      function getStackedSeriesTotals() {\n        var excludedSeriesIndices = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n        var w = this.w;\n        var total = [];\n        if (w.globals.series.length === 0) return total;\n\n        for (var i = 0; i < w.globals.series[w.globals.maxValsInArrayIndex].length; i++) {\n          var t = 0;\n\n          for (var j = 0; j < w.globals.series.length; j++) {\n            if (typeof w.globals.series[j][i] !== 'undefined' && excludedSeriesIndices.indexOf(j) === -1) {\n              t += w.globals.series[j][i];\n            }\n          }\n\n          total.push(t);\n        }\n\n        return total;\n      } // get total of the all values inside all series\n\n    }, {\n      key: \"getSeriesTotalByIndex\",\n      value: function getSeriesTotalByIndex() {\n        var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n\n        if (index === null) {\n          // non-plot chart types - pie / donut / circle\n          return this.w.config.series.reduce(function (acc, cur) {\n            return acc + cur;\n          }, 0);\n        } else {\n          // axis charts - supporting multiple series\n          return this.w.globals.series[index].reduce(function (acc, cur) {\n            return acc + cur;\n          }, 0);\n        }\n      }\n    }, {\n      key: \"isSeriesNull\",\n      value: function isSeriesNull() {\n        var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n        var r = [];\n\n        if (index === null) {\n          // non-plot chart types - pie / donut / circle\n          r = this.w.config.series.filter(function (d) {\n            return d !== null;\n          });\n        } else {\n          // axis charts - supporting multiple series\n          r = this.w.config.series[index].data.filter(function (d) {\n            return d !== null;\n          });\n        }\n\n        return r.length === 0;\n      }\n    }, {\n      key: \"seriesHaveSameValues\",\n      value: function seriesHaveSameValues(index) {\n        return this.w.globals.series[index].every(function (val, i, arr) {\n          return val === arr[0];\n        });\n      }\n    }, {\n      key: \"getCategoryLabels\",\n      value: function getCategoryLabels(labels) {\n        var w = this.w;\n        var catLabels = labels.slice();\n\n        if (w.config.xaxis.convertedCatToNumeric) {\n          catLabels = labels.map(function (i, li) {\n            return w.config.xaxis.labels.formatter(i - w.globals.minX + 1);\n          });\n        }\n\n        return catLabels;\n      } // maxValsInArrayIndex is the index of series[] which has the largest number of items\n\n    }, {\n      key: \"getLargestSeries\",\n      value: function getLargestSeries() {\n        var w = this.w;\n        w.globals.maxValsInArrayIndex = w.globals.series.map(function (a) {\n          return a.length;\n        }).indexOf(Math.max.apply(Math, w.globals.series.map(function (a) {\n          return a.length;\n        })));\n      }\n    }, {\n      key: \"getLargestMarkerSize\",\n      value: function getLargestMarkerSize() {\n        var w = this.w;\n        var size = 0;\n        w.globals.markers.size.forEach(function (m) {\n          size = Math.max(size, m);\n        });\n\n        if (w.config.markers.discrete && w.config.markers.discrete.length) {\n          w.config.markers.discrete.forEach(function (m) {\n            size = Math.max(size, m.size);\n          });\n        }\n\n        if (size > 0) {\n          size += w.config.markers.hover.sizeOffset + 1;\n        }\n\n        w.globals.markers.largestSize = size;\n        return size;\n      }\n      /**\n       * @memberof Core\n       * returns the sum of all values in a series\n       * Eg. w.globals.series = [[32,33,43,12], [2,3,5,1]]\n       *  @return [120, 11]\n       **/\n\n    }, {\n      key: \"getSeriesTotals\",\n      value: function getSeriesTotals() {\n        var w = this.w;\n        w.globals.seriesTotals = w.globals.series.map(function (ser, index) {\n          var total = 0;\n\n          if (Array.isArray(ser)) {\n            for (var j = 0; j < ser.length; j++) {\n              total += ser[j];\n            }\n          } else {\n            // for pie/donuts/gauges\n            total += ser;\n          }\n\n          return total;\n        });\n      }\n    }, {\n      key: \"getSeriesTotalsXRange\",\n      value: function getSeriesTotalsXRange(minX, maxX) {\n        var w = this.w;\n        var seriesTotalsXRange = w.globals.series.map(function (ser, index) {\n          var total = 0;\n\n          for (var j = 0; j < ser.length; j++) {\n            if (w.globals.seriesX[index][j] > minX && w.globals.seriesX[index][j] < maxX) {\n              total += ser[j];\n            }\n          }\n\n          return total;\n        });\n        return seriesTotalsXRange;\n      }\n      /**\n       * @memberof CoreUtils\n       * returns the percentage value of all individual values which can be used in a 100% stacked series\n       * Eg. w.globals.series = [[32, 33, 43, 12], [2, 3, 5, 1]]\n       *  @return [[94.11, 91.66, 89.58, 92.30], [5.88, 8.33, 10.41, 7.7]]\n       **/\n\n    }, {\n      key: \"getPercentSeries\",\n      value: function getPercentSeries() {\n        var w = this.w;\n        w.globals.seriesPercent = w.globals.series.map(function (ser, index) {\n          var seriesPercent = [];\n\n          if (Array.isArray(ser)) {\n            for (var j = 0; j < ser.length; j++) {\n              var total = w.globals.stackedSeriesTotals[j];\n              var percent = 0;\n\n              if (total) {\n                percent = 100 * ser[j] / total;\n              }\n\n              seriesPercent.push(percent);\n            }\n          } else {\n            var _total = w.globals.seriesTotals.reduce(function (acc, val) {\n              return acc + val;\n            }, 0);\n\n            var _percent = 100 * ser / _total;\n\n            seriesPercent.push(_percent);\n          }\n\n          return seriesPercent;\n        });\n      }\n    }, {\n      key: \"getCalculatedRatios\",\n      value: function getCalculatedRatios() {\n        var gl = this.w.globals;\n        var yRatio = [];\n        var invertedYRatio = 0;\n        var xRatio = 0;\n        var initialXRatio = 0;\n        var invertedXRatio = 0;\n        var zRatio = 0;\n        var baseLineY = [];\n        var baseLineInvertedY = 0.1;\n        var baseLineX = 0;\n        gl.yRange = [];\n\n        if (gl.isMultipleYAxis) {\n          for (var i = 0; i < gl.minYArr.length; i++) {\n            gl.yRange.push(Math.abs(gl.minYArr[i] - gl.maxYArr[i]));\n            baseLineY.push(0);\n          }\n        } else {\n          gl.yRange.push(Math.abs(gl.minY - gl.maxY));\n        }\n\n        gl.xRange = Math.abs(gl.maxX - gl.minX);\n        gl.zRange = Math.abs(gl.maxZ - gl.minZ); // multiple y axis\n\n        for (var _i = 0; _i < gl.yRange.length; _i++) {\n          yRatio.push(gl.yRange[_i] / gl.gridHeight);\n        }\n\n        xRatio = gl.xRange / gl.gridWidth;\n        initialXRatio = Math.abs(gl.initialMaxX - gl.initialMinX) / gl.gridWidth;\n        invertedYRatio = gl.yRange / gl.gridWidth;\n        invertedXRatio = gl.xRange / gl.gridHeight;\n        zRatio = gl.zRange / gl.gridHeight * 16;\n\n        if (!zRatio) {\n          zRatio = 1;\n        }\n\n        if (gl.minY !== Number.MIN_VALUE && Math.abs(gl.minY) !== 0) {\n          // Negative numbers present in series\n          gl.hasNegs = true;\n        }\n\n        if (gl.isMultipleYAxis) {\n          baseLineY = []; // baseline variables is the 0 of the yaxis which will be needed when there are negatives\n\n          for (var _i2 = 0; _i2 < yRatio.length; _i2++) {\n            baseLineY.push(-gl.minYArr[_i2] / yRatio[_i2]);\n          }\n        } else {\n          baseLineY.push(-gl.minY / yRatio[0]);\n\n          if (gl.minY !== Number.MIN_VALUE && Math.abs(gl.minY) !== 0) {\n            baseLineInvertedY = -gl.minY / invertedYRatio; // this is for bar chart\n\n            baseLineX = gl.minX / xRatio;\n          }\n        }\n\n        return {\n          yRatio: yRatio,\n          invertedYRatio: invertedYRatio,\n          zRatio: zRatio,\n          xRatio: xRatio,\n          initialXRatio: initialXRatio,\n          invertedXRatio: invertedXRatio,\n          baseLineInvertedY: baseLineInvertedY,\n          baseLineY: baseLineY,\n          baseLineX: baseLineX\n        };\n      }\n    }, {\n      key: \"getLogSeries\",\n      value: function getLogSeries(series) {\n        var _this = this;\n\n        var w = this.w;\n        w.globals.seriesLog = series.map(function (s, i) {\n          if (w.config.yaxis[i] && w.config.yaxis[i].logarithmic) {\n            return s.map(function (d) {\n              if (d === null) return null;\n              return _this.getLogVal(w.config.yaxis[i].logBase, d, i);\n            });\n          } else {\n            return s;\n          }\n        });\n        return w.globals.invalidLogScale ? series : w.globals.seriesLog;\n      }\n    }, {\n      key: \"getBaseLog\",\n      value: function getBaseLog(base, value) {\n        return Math.log(value) / Math.log(base);\n      }\n    }, {\n      key: \"getLogVal\",\n      value: function getLogVal(b, d, yIndex) {\n        if (d === 0) {\n          return 0;\n        }\n\n        var w = this.w;\n        var min_log_val = w.globals.minYArr[yIndex] === 0 ? -1 // make sure we dont calculate log of 0\n        : this.getBaseLog(b, w.globals.minYArr[yIndex]);\n        var max_log_val = w.globals.maxYArr[yIndex] === 0 ? 0 // make sure we dont calculate log of 0\n        : this.getBaseLog(b, w.globals.maxYArr[yIndex]);\n        var number_of_height_levels = max_log_val - min_log_val;\n        if (d < 1) return d / number_of_height_levels;\n        var log_height_value = this.getBaseLog(b, d) - min_log_val;\n        return log_height_value / number_of_height_levels;\n      }\n    }, {\n      key: \"getLogYRatios\",\n      value: function getLogYRatios(yRatio) {\n        var _this2 = this;\n\n        var w = this.w;\n        var gl = this.w.globals;\n        gl.yLogRatio = yRatio.slice();\n        gl.logYRange = gl.yRange.map(function (yRange, i) {\n          if (w.config.yaxis[i] && _this2.w.config.yaxis[i].logarithmic) {\n            var maxY = -Number.MAX_VALUE;\n            var minY = Number.MIN_VALUE;\n            var range = 1;\n            gl.seriesLog.forEach(function (s, si) {\n              s.forEach(function (v) {\n                if (w.config.yaxis[si] && w.config.yaxis[si].logarithmic) {\n                  maxY = Math.max(v, maxY);\n                  minY = Math.min(v, minY);\n                }\n              });\n            });\n            range = Math.pow(gl.yRange[i], Math.abs(minY - maxY) / gl.yRange[i]);\n            gl.yLogRatio[i] = range / gl.gridHeight;\n            return range;\n          }\n        });\n        return gl.invalidLogScale ? yRatio.slice() : gl.yLogRatio;\n      } // Some config objects can be array - and we need to extend them correctly\n\n    }], [{\n      key: \"checkComboSeries\",\n      value: function checkComboSeries(series) {\n        var comboCharts = false;\n        var comboBarCount = 0;\n        var comboCount = 0; // if user specified a type in series too, turn on comboCharts flag\n\n        if (series.length && typeof series[0].type !== 'undefined') {\n          series.forEach(function (s) {\n            if (s.type === 'bar' || s.type === 'column' || s.type === 'candlestick' || s.type === 'boxPlot') {\n              comboBarCount++;\n            }\n\n            if (typeof s.type !== 'undefined') {\n              comboCount++;\n            }\n          });\n        }\n\n        if (comboCount > 0) {\n          comboCharts = true;\n        }\n\n        return {\n          comboBarCount: comboBarCount,\n          comboCharts: comboCharts\n        };\n      }\n    }, {\n      key: \"extendArrayProps\",\n      value: function extendArrayProps(configInstance, options, w) {\n        if (options.yaxis) {\n          options = configInstance.extendYAxis(options, w);\n        }\n\n        if (options.annotations) {\n          if (options.annotations.yaxis) {\n            options = configInstance.extendYAxisAnnotations(options);\n          }\n\n          if (options.annotations.xaxis) {\n            options = configInstance.extendXAxisAnnotations(options);\n          }\n\n          if (options.annotations.points) {\n            options = configInstance.extendPointAnnotations(options);\n          }\n        }\n\n        return options;\n      }\n    }]);\n\n    return CoreUtils;\n  }();\n\n  var Helpers$4 = /*#__PURE__*/function () {\n    function Helpers(annoCtx) {\n      _classCallCheck(this, Helpers);\n\n      this.w = annoCtx.w;\n      this.annoCtx = annoCtx;\n    }\n\n    _createClass(Helpers, [{\n      key: \"setOrientations\",\n      value: function setOrientations(anno) {\n        var annoIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n        var w = this.w;\n\n        if (anno.label.orientation === 'vertical') {\n          var i = annoIndex !== null ? annoIndex : 0;\n          var xAnno = w.globals.dom.baseEl.querySelector(\".apexcharts-xaxis-annotations .apexcharts-xaxis-annotation-label[rel='\".concat(i, \"']\"));\n\n          if (xAnno !== null) {\n            var xAnnoCoord = xAnno.getBoundingClientRect();\n            xAnno.setAttribute('x', parseFloat(xAnno.getAttribute('x')) - xAnnoCoord.height + 4);\n\n            if (anno.label.position === 'top') {\n              xAnno.setAttribute('y', parseFloat(xAnno.getAttribute('y')) + xAnnoCoord.width);\n            } else {\n              xAnno.setAttribute('y', parseFloat(xAnno.getAttribute('y')) - xAnnoCoord.width);\n            }\n\n            var annoRotatingCenter = this.annoCtx.graphics.rotateAroundCenter(xAnno);\n            var x = annoRotatingCenter.x;\n            var y = annoRotatingCenter.y;\n            xAnno.setAttribute('transform', \"rotate(-90 \".concat(x, \" \").concat(y, \")\"));\n          }\n        }\n      }\n    }, {\n      key: \"addBackgroundToAnno\",\n      value: function addBackgroundToAnno(annoEl, anno) {\n        var w = this.w;\n        if (!annoEl || typeof anno.label.text === 'undefined' || typeof anno.label.text !== 'undefined' && !String(anno.label.text).trim()) return null;\n        var elGridRect = w.globals.dom.baseEl.querySelector('.apexcharts-grid').getBoundingClientRect();\n        var coords = annoEl.getBoundingClientRect();\n        var pleft = anno.label.style.padding.left;\n        var pright = anno.label.style.padding.right;\n        var ptop = anno.label.style.padding.top;\n        var pbottom = anno.label.style.padding.bottom;\n\n        if (anno.label.orientation === 'vertical') {\n          ptop = anno.label.style.padding.left;\n          pbottom = anno.label.style.padding.right;\n          pleft = anno.label.style.padding.top;\n          pright = anno.label.style.padding.bottom;\n        }\n\n        var x1 = coords.left - elGridRect.left - pleft;\n        var y1 = coords.top - elGridRect.top - ptop;\n        var elRect = this.annoCtx.graphics.drawRect(x1 - w.globals.barPadForNumericAxis, y1, coords.width + pleft + pright, coords.height + ptop + pbottom, anno.label.borderRadius, anno.label.style.background, 1, anno.label.borderWidth, anno.label.borderColor, 0);\n\n        if (anno.id) {\n          // don't escapeString for this ID as it causes duplicate rects\n          elRect.node.classList.add(anno.id);\n        }\n\n        return elRect;\n      }\n    }, {\n      key: \"annotationsBackground\",\n      value: function annotationsBackground() {\n        var _this = this;\n\n        var w = this.w;\n\n        var add = function add(anno, i, type) {\n          var annoLabel = w.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(type, \"-annotations .apexcharts-\").concat(type, \"-annotation-label[rel='\").concat(i, \"']\"));\n\n          if (annoLabel) {\n            var parent = annoLabel.parentNode;\n\n            var elRect = _this.addBackgroundToAnno(annoLabel, anno);\n\n            if (elRect) {\n              parent.insertBefore(elRect.node, annoLabel);\n\n              if (anno.label.mouseEnter) {\n                elRect.node.addEventListener('mouseenter', anno.label.mouseEnter.bind(_this, anno));\n              }\n\n              if (anno.label.mouseLeave) {\n                elRect.node.addEventListener('mouseleave', anno.label.mouseLeave.bind(_this, anno));\n              }\n\n              if (anno.label.click) {\n                elRect.node.addEventListener('click', anno.label.click.bind(_this, anno));\n              }\n            }\n          }\n        };\n\n        w.config.annotations.xaxis.map(function (anno, i) {\n          add(anno, i, 'xaxis');\n        });\n        w.config.annotations.yaxis.map(function (anno, i) {\n          add(anno, i, 'yaxis');\n        });\n        w.config.annotations.points.map(function (anno, i) {\n          add(anno, i, 'point');\n        });\n      }\n    }, {\n      key: \"getY1Y2\",\n      value: function getY1Y2(type, anno) {\n        var y = type === 'y1' ? anno.y : anno.y2;\n        var yP;\n        var w = this.w;\n\n        if (this.annoCtx.invertAxis) {\n          var catIndex = w.globals.labels.indexOf(y);\n\n          if (w.config.xaxis.convertedCatToNumeric) {\n            catIndex = w.globals.categoryLabels.indexOf(y);\n          }\n\n          var xLabel = w.globals.dom.baseEl.querySelector('.apexcharts-yaxis-texts-g text:nth-child(' + (catIndex + 1) + ')');\n\n          if (xLabel) {\n            yP = parseFloat(xLabel.getAttribute('y'));\n          }\n        } else {\n          var yPos;\n\n          if (w.config.yaxis[anno.yAxisIndex].logarithmic) {\n            var coreUtils = new CoreUtils(this.annoCtx.ctx);\n            y = coreUtils.getLogVal(y, anno.yAxisIndex);\n            yPos = y / w.globals.yLogRatio[anno.yAxisIndex];\n          } else {\n            yPos = (y - w.globals.minYArr[anno.yAxisIndex]) / (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight);\n          }\n\n          yP = w.globals.gridHeight - yPos;\n\n          if (anno.marker && (anno.y === undefined || anno.y === null)) {\n            // point annotation\n            yP = 0;\n          }\n\n          if (w.config.yaxis[anno.yAxisIndex] && w.config.yaxis[anno.yAxisIndex].reversed) {\n            yP = yPos;\n          }\n        }\n\n        if (typeof y === 'string' && y.indexOf('px') > -1) {\n          yP = parseFloat(y);\n        }\n\n        return yP;\n      }\n    }, {\n      key: \"getX1X2\",\n      value: function getX1X2(type, anno) {\n        var w = this.w;\n        var min = this.annoCtx.invertAxis ? w.globals.minY : w.globals.minX;\n        var max = this.annoCtx.invertAxis ? w.globals.maxY : w.globals.maxX;\n        var range = this.annoCtx.invertAxis ? w.globals.yRange[0] : w.globals.xRange;\n        var x1 = (anno.x - min) / (range / w.globals.gridWidth);\n\n        if (this.annoCtx.inversedReversedAxis) {\n          x1 = (max - anno.x) / (range / w.globals.gridWidth);\n        }\n\n        if ((w.config.xaxis.type === 'category' || w.config.xaxis.convertedCatToNumeric) && !this.annoCtx.invertAxis && !w.globals.dataFormatXNumeric) {\n          x1 = this.getStringX(anno.x);\n        }\n\n        var x2 = (anno.x2 - min) / (range / w.globals.gridWidth);\n\n        if (this.annoCtx.inversedReversedAxis) {\n          x2 = (max - anno.x2) / (range / w.globals.gridWidth);\n        }\n\n        if ((w.config.xaxis.type === 'category' || w.config.xaxis.convertedCatToNumeric) && !this.annoCtx.invertAxis && !w.globals.dataFormatXNumeric) {\n          x2 = this.getStringX(anno.x2);\n        }\n\n        if ((anno.x === undefined || anno.x === null) && anno.marker) {\n          // point annotation in a horizontal chart\n          x1 = w.globals.gridWidth;\n        }\n\n        if (type === 'x1' && typeof anno.x === 'string' && anno.x.indexOf('px') > -1) {\n          x1 = parseFloat(anno.x);\n        }\n\n        if (type === 'x2' && typeof anno.x2 === 'string' && anno.x2.indexOf('px') > -1) {\n          x2 = parseFloat(anno.x2);\n        }\n\n        return type === 'x1' ? x1 : x2;\n      }\n    }, {\n      key: \"getStringX\",\n      value: function getStringX(x) {\n        var w = this.w;\n        var rX = x;\n\n        if (w.config.xaxis.convertedCatToNumeric && w.globals.categoryLabels.length) {\n          x = w.globals.categoryLabels.indexOf(x) + 1;\n        }\n\n        var catIndex = w.globals.labels.indexOf(x);\n        var xLabel = w.globals.dom.baseEl.querySelector('.apexcharts-xaxis-texts-g text:nth-child(' + (catIndex + 1) + ')');\n\n        if (xLabel) {\n          rX = parseFloat(xLabel.getAttribute('x'));\n        }\n\n        return rX;\n      }\n    }]);\n\n    return Helpers;\n  }();\n\n  var XAnnotations = /*#__PURE__*/function () {\n    function XAnnotations(annoCtx) {\n      _classCallCheck(this, XAnnotations);\n\n      this.w = annoCtx.w;\n      this.annoCtx = annoCtx;\n      this.invertAxis = this.annoCtx.invertAxis;\n      this.helpers = new Helpers$4(this.annoCtx);\n    }\n\n    _createClass(XAnnotations, [{\n      key: \"addXaxisAnnotation\",\n      value: function addXaxisAnnotation(anno, parent, index) {\n        var w = this.w;\n        var x1 = this.helpers.getX1X2('x1', anno);\n        var x2;\n        var text = anno.label.text;\n        var strokeDashArray = anno.strokeDashArray;\n        if (!Utils$1.isNumber(x1)) return;\n\n        if (anno.x2 === null || typeof anno.x2 === 'undefined') {\n          var line = this.annoCtx.graphics.drawLine(x1 + anno.offsetX, // x1\n          0 + anno.offsetY, // y1\n          x1 + anno.offsetX, // x2\n          w.globals.gridHeight + anno.offsetY, // y2\n          anno.borderColor, // lineColor\n          strokeDashArray, //dashArray\n          anno.borderWidth);\n          parent.appendChild(line.node);\n\n          if (anno.id) {\n            line.node.classList.add(anno.id);\n          }\n        } else {\n          x2 = this.helpers.getX1X2('x2', anno);\n\n          if (x2 < x1) {\n            var temp = x1;\n            x1 = x2;\n            x2 = temp;\n          }\n\n          var rect = this.annoCtx.graphics.drawRect(x1 + anno.offsetX, // x1\n          0 + anno.offsetY, // y1\n          x2 - x1, // x2\n          w.globals.gridHeight + anno.offsetY, // y2\n          0, // radius\n          anno.fillColor, // color\n          anno.opacity, // opacity,\n          1, // strokeWidth\n          anno.borderColor, // strokeColor\n          strokeDashArray // stokeDashArray\n          );\n          rect.node.classList.add('apexcharts-annotation-rect');\n          rect.attr('clip-path', \"url(#gridRectMask\".concat(w.globals.cuid, \")\"));\n          parent.appendChild(rect.node);\n\n          if (anno.id) {\n            rect.node.classList.add(anno.id);\n          }\n        }\n\n        var textRects = this.annoCtx.graphics.getTextRects(text, parseFloat(anno.label.style.fontSize));\n        var textY = anno.label.position === 'top' ? 4 : anno.label.position === 'center' ? w.globals.gridHeight / 2 + (anno.label.orientation === 'vertical' ? textRects.width / 2 : 0) : w.globals.gridHeight;\n        var elText = this.annoCtx.graphics.drawText({\n          x: x1 + anno.label.offsetX,\n          y: textY + anno.label.offsetY - (anno.label.orientation === 'vertical' ? anno.label.position === 'top' ? textRects.width / 2 - 12 : -textRects.width / 2 : 0),\n          text: text,\n          textAnchor: anno.label.textAnchor,\n          fontSize: anno.label.style.fontSize,\n          fontFamily: anno.label.style.fontFamily,\n          fontWeight: anno.label.style.fontWeight,\n          foreColor: anno.label.style.color,\n          cssClass: \"apexcharts-xaxis-annotation-label \".concat(anno.label.style.cssClass, \" \").concat(anno.id ? anno.id : '')\n        });\n        elText.attr({\n          rel: index\n        });\n        parent.appendChild(elText.node); // after placing the annotations on svg, set any vertically placed annotations\n\n        this.annoCtx.helpers.setOrientations(anno, index);\n      }\n    }, {\n      key: \"drawXAxisAnnotations\",\n      value: function drawXAxisAnnotations() {\n        var _this = this;\n\n        var w = this.w;\n        var elg = this.annoCtx.graphics.group({\n          class: 'apexcharts-xaxis-annotations'\n        });\n        w.config.annotations.xaxis.map(function (anno, index) {\n          _this.addXaxisAnnotation(anno, elg.node, index);\n        });\n        return elg;\n      }\n    }]);\n\n    return XAnnotations;\n  }();\n\n  var YAnnotations = /*#__PURE__*/function () {\n    function YAnnotations(annoCtx) {\n      _classCallCheck(this, YAnnotations);\n\n      this.w = annoCtx.w;\n      this.annoCtx = annoCtx;\n      this.helpers = new Helpers$4(this.annoCtx);\n    }\n\n    _createClass(YAnnotations, [{\n      key: \"addYaxisAnnotation\",\n      value: function addYaxisAnnotation(anno, parent, index) {\n        var w = this.w;\n        var strokeDashArray = anno.strokeDashArray;\n        var y1 = this.helpers.getY1Y2('y1', anno);\n        var y2;\n        var text = anno.label.text;\n\n        if (anno.y2 === null || typeof anno.y2 === 'undefined') {\n          var line = this.annoCtx.graphics.drawLine(0 + anno.offsetX, // x1\n          y1 + anno.offsetY, // y1\n          this._getYAxisAnnotationWidth(anno), // x2\n          y1 + anno.offsetY, // y2\n          anno.borderColor, // lineColor\n          strokeDashArray, // dashArray\n          anno.borderWidth);\n          parent.appendChild(line.node);\n\n          if (anno.id) {\n            line.node.classList.add(anno.id);\n          }\n        } else {\n          y2 = this.helpers.getY1Y2('y2', anno);\n\n          if (y2 > y1) {\n            var temp = y1;\n            y1 = y2;\n            y2 = temp;\n          }\n\n          var rect = this.annoCtx.graphics.drawRect(0 + anno.offsetX, // x1\n          y2 + anno.offsetY, // y1\n          this._getYAxisAnnotationWidth(anno), // x2\n          y1 - y2, // y2\n          0, // radius\n          anno.fillColor, // color\n          anno.opacity, // opacity,\n          1, // strokeWidth\n          anno.borderColor, // strokeColor\n          strokeDashArray // stokeDashArray\n          );\n          rect.node.classList.add('apexcharts-annotation-rect');\n          rect.attr('clip-path', \"url(#gridRectMask\".concat(w.globals.cuid, \")\"));\n          parent.appendChild(rect.node);\n\n          if (anno.id) {\n            rect.node.classList.add(anno.id);\n          }\n        }\n\n        var textX = anno.label.position === 'right' ? w.globals.gridWidth : anno.label.position === 'center' ? w.globals.gridWidth / 2 : 0;\n        var elText = this.annoCtx.graphics.drawText({\n          x: textX + anno.label.offsetX,\n          y: (y2 != null ? y2 : y1) + anno.label.offsetY - 3,\n          text: text,\n          textAnchor: anno.label.textAnchor,\n          fontSize: anno.label.style.fontSize,\n          fontFamily: anno.label.style.fontFamily,\n          fontWeight: anno.label.style.fontWeight,\n          foreColor: anno.label.style.color,\n          cssClass: \"apexcharts-yaxis-annotation-label \".concat(anno.label.style.cssClass, \" \").concat(anno.id ? anno.id : '')\n        });\n        elText.attr({\n          rel: index\n        });\n        parent.appendChild(elText.node);\n      }\n    }, {\n      key: \"_getYAxisAnnotationWidth\",\n      value: function _getYAxisAnnotationWidth(anno) {\n        // issue apexcharts.js#2009\n        var w = this.w;\n        var width = w.globals.gridWidth;\n\n        if (anno.width.indexOf('%') > -1) {\n          width = w.globals.gridWidth * parseInt(anno.width, 10) / 100;\n        } else {\n          width = parseInt(anno.width, 10);\n        }\n\n        return width + anno.offsetX;\n      }\n    }, {\n      key: \"drawYAxisAnnotations\",\n      value: function drawYAxisAnnotations() {\n        var _this = this;\n\n        var w = this.w;\n        var elg = this.annoCtx.graphics.group({\n          class: 'apexcharts-yaxis-annotations'\n        });\n        w.config.annotations.yaxis.map(function (anno, index) {\n          _this.addYaxisAnnotation(anno, elg.node, index);\n        });\n        return elg;\n      }\n    }]);\n\n    return YAnnotations;\n  }();\n\n  var PointAnnotations = /*#__PURE__*/function () {\n    function PointAnnotations(annoCtx) {\n      _classCallCheck(this, PointAnnotations);\n\n      this.w = annoCtx.w;\n      this.annoCtx = annoCtx;\n      this.helpers = new Helpers$4(this.annoCtx);\n    }\n\n    _createClass(PointAnnotations, [{\n      key: \"addPointAnnotation\",\n      value: function addPointAnnotation(anno, parent, index) {\n        this.w;\n        var x = this.helpers.getX1X2('x1', anno);\n        var y = this.helpers.getY1Y2('y1', anno);\n        if (!Utils$1.isNumber(x)) return;\n        var optsPoints = {\n          pSize: anno.marker.size,\n          pointStrokeWidth: anno.marker.strokeWidth,\n          pointFillColor: anno.marker.fillColor,\n          pointStrokeColor: anno.marker.strokeColor,\n          shape: anno.marker.shape,\n          pRadius: anno.marker.radius,\n          class: \"apexcharts-point-annotation-marker \".concat(anno.marker.cssClass, \" \").concat(anno.id ? anno.id : '')\n        };\n        var point = this.annoCtx.graphics.drawMarker(x + anno.marker.offsetX, y + anno.marker.offsetY, optsPoints);\n        parent.appendChild(point.node);\n        var text = anno.label.text ? anno.label.text : '';\n        var elText = this.annoCtx.graphics.drawText({\n          x: x + anno.label.offsetX,\n          y: y + anno.label.offsetY - anno.marker.size - parseFloat(anno.label.style.fontSize) / 1.6,\n          text: text,\n          textAnchor: anno.label.textAnchor,\n          fontSize: anno.label.style.fontSize,\n          fontFamily: anno.label.style.fontFamily,\n          fontWeight: anno.label.style.fontWeight,\n          foreColor: anno.label.style.color,\n          cssClass: \"apexcharts-point-annotation-label \".concat(anno.label.style.cssClass, \" \").concat(anno.id ? anno.id : '')\n        });\n        elText.attr({\n          rel: index\n        });\n        parent.appendChild(elText.node); // TODO: deprecate this as we will use custom\n\n        if (anno.customSVG.SVG) {\n          var g = this.annoCtx.graphics.group({\n            class: 'apexcharts-point-annotations-custom-svg ' + anno.customSVG.cssClass\n          });\n          g.attr({\n            transform: \"translate(\".concat(x + anno.customSVG.offsetX, \", \").concat(y + anno.customSVG.offsetY, \")\")\n          });\n          g.node.innerHTML = anno.customSVG.SVG;\n          parent.appendChild(g.node);\n        }\n\n        if (anno.image.path) {\n          var imgWidth = anno.image.width ? anno.image.width : 20;\n          var imgHeight = anno.image.height ? anno.image.height : 20;\n          point = this.annoCtx.addImage({\n            x: x + anno.image.offsetX - imgWidth / 2,\n            y: y + anno.image.offsetY - imgHeight / 2,\n            width: imgWidth,\n            height: imgHeight,\n            path: anno.image.path,\n            appendTo: '.apexcharts-point-annotations'\n          });\n        }\n\n        if (anno.mouseEnter) {\n          point.node.addEventListener('mouseenter', anno.mouseEnter.bind(this, anno));\n        }\n\n        if (anno.mouseLeave) {\n          point.node.addEventListener('mouseleave', anno.mouseLeave.bind(this, anno));\n        }\n\n        if (anno.click) {\n          point.node.addEventListener('click', anno.click.bind(this, anno));\n        }\n      }\n    }, {\n      key: \"drawPointAnnotations\",\n      value: function drawPointAnnotations() {\n        var _this = this;\n\n        var w = this.w;\n        var elg = this.annoCtx.graphics.group({\n          class: 'apexcharts-point-annotations'\n        });\n        w.config.annotations.points.map(function (anno, index) {\n          _this.addPointAnnotation(anno, elg.node, index);\n        });\n        return elg;\n      }\n    }]);\n\n    return PointAnnotations;\n  }();\n\n  const name = \"en\";\n  const options = {\n  \tmonths: [\n  \t\t\"January\",\n  \t\t\"February\",\n  \t\t\"March\",\n  \t\t\"April\",\n  \t\t\"May\",\n  \t\t\"June\",\n  \t\t\"July\",\n  \t\t\"August\",\n  \t\t\"September\",\n  \t\t\"October\",\n  \t\t\"November\",\n  \t\t\"December\"\n  \t],\n  \tshortMonths: [\n  \t\t\"Jan\",\n  \t\t\"Feb\",\n  \t\t\"Mar\",\n  \t\t\"Apr\",\n  \t\t\"May\",\n  \t\t\"Jun\",\n  \t\t\"Jul\",\n  \t\t\"Aug\",\n  \t\t\"Sep\",\n  \t\t\"Oct\",\n  \t\t\"Nov\",\n  \t\t\"Dec\"\n  \t],\n  \tdays: [\n  \t\t\"Sunday\",\n  \t\t\"Monday\",\n  \t\t\"Tuesday\",\n  \t\t\"Wednesday\",\n  \t\t\"Thursday\",\n  \t\t\"Friday\",\n  \t\t\"Saturday\"\n  \t],\n  \tshortDays: [\n  \t\t\"Sun\",\n  \t\t\"Mon\",\n  \t\t\"Tue\",\n  \t\t\"Wed\",\n  \t\t\"Thu\",\n  \t\t\"Fri\",\n  \t\t\"Sat\"\n  \t],\n  \ttoolbar: {\n  \t\texportToSVG: \"Download SVG\",\n  \t\texportToPNG: \"Download PNG\",\n  \t\texportToCSV: \"Download CSV\",\n  \t\tmenu: \"Menu\",\n  \t\tselection: \"Selection\",\n  \t\tselectionZoom: \"Selection Zoom\",\n  \t\tzoomIn: \"Zoom In\",\n  \t\tzoomOut: \"Zoom Out\",\n  \t\tpan: \"Panning\",\n  \t\treset: \"Reset Zoom\"\n  \t}\n  };\n  var en = {\n  \tname: name,\n  \toptions: options\n  };\n\n  var Options = /*#__PURE__*/function () {\n    function Options() {\n      _classCallCheck(this, Options);\n\n      this.yAxis = {\n        show: true,\n        showAlways: false,\n        showForNullSeries: true,\n        seriesName: undefined,\n        opposite: false,\n        reversed: false,\n        logarithmic: false,\n        logBase: 10,\n        tickAmount: undefined,\n        forceNiceScale: false,\n        max: undefined,\n        min: undefined,\n        floating: false,\n        decimalsInFloat: undefined,\n        labels: {\n          show: true,\n          minWidth: 0,\n          maxWidth: 160,\n          offsetX: 0,\n          offsetY: 0,\n          align: undefined,\n          rotate: 0,\n          padding: 20,\n          style: {\n            colors: [],\n            fontSize: '11px',\n            fontWeight: 400,\n            fontFamily: undefined,\n            cssClass: ''\n          },\n          formatter: undefined\n        },\n        axisBorder: {\n          show: false,\n          color: '#e0e0e0',\n          width: 1,\n          offsetX: 0,\n          offsetY: 0\n        },\n        axisTicks: {\n          show: false,\n          color: '#e0e0e0',\n          width: 6,\n          offsetX: 0,\n          offsetY: 0\n        },\n        title: {\n          text: undefined,\n          rotate: -90,\n          offsetY: 0,\n          offsetX: 0,\n          style: {\n            color: undefined,\n            fontSize: '11px',\n            fontWeight: 900,\n            fontFamily: undefined,\n            cssClass: ''\n          }\n        },\n        tooltip: {\n          enabled: false,\n          offsetX: 0\n        },\n        crosshairs: {\n          show: true,\n          position: 'front',\n          stroke: {\n            color: '#b6b6b6',\n            width: 1,\n            dashArray: 0\n          }\n        }\n      };\n      this.pointAnnotation = {\n        id: undefined,\n        x: 0,\n        y: null,\n        yAxisIndex: 0,\n        seriesIndex: 0,\n        mouseEnter: undefined,\n        mouseLeave: undefined,\n        click: undefined,\n        marker: {\n          size: 4,\n          fillColor: '#fff',\n          strokeWidth: 2,\n          strokeColor: '#333',\n          shape: 'circle',\n          offsetX: 0,\n          offsetY: 0,\n          radius: 2,\n          cssClass: ''\n        },\n        label: {\n          borderColor: '#c2c2c2',\n          borderWidth: 1,\n          borderRadius: 2,\n          text: undefined,\n          textAnchor: 'middle',\n          offsetX: 0,\n          offsetY: 0,\n          mouseEnter: undefined,\n          mouseLeave: undefined,\n          click: undefined,\n          style: {\n            background: '#fff',\n            color: undefined,\n            fontSize: '11px',\n            fontFamily: undefined,\n            fontWeight: 400,\n            cssClass: '',\n            padding: {\n              left: 5,\n              right: 5,\n              top: 2,\n              bottom: 2\n            }\n          }\n        },\n        customSVG: {\n          // this will be deprecated in the next major version as it is going to be replaced with a better alternative below\n          SVG: undefined,\n          cssClass: undefined,\n          offsetX: 0,\n          offsetY: 0\n        },\n        image: {\n          path: undefined,\n          width: 20,\n          height: 20,\n          offsetX: 0,\n          offsetY: 0\n        }\n      };\n      this.yAxisAnnotation = {\n        id: undefined,\n        y: 0,\n        y2: null,\n        strokeDashArray: 1,\n        fillColor: '#c2c2c2',\n        borderColor: '#c2c2c2',\n        borderWidth: 1,\n        opacity: 0.3,\n        offsetX: 0,\n        offsetY: 0,\n        width: '100%',\n        yAxisIndex: 0,\n        label: {\n          borderColor: '#c2c2c2',\n          borderWidth: 1,\n          borderRadius: 2,\n          text: undefined,\n          textAnchor: 'end',\n          position: 'right',\n          offsetX: 0,\n          offsetY: -3,\n          mouseEnter: undefined,\n          mouseLeave: undefined,\n          click: undefined,\n          style: {\n            background: '#fff',\n            color: undefined,\n            fontSize: '11px',\n            fontFamily: undefined,\n            fontWeight: 400,\n            cssClass: '',\n            padding: {\n              left: 5,\n              right: 5,\n              top: 2,\n              bottom: 2\n            }\n          }\n        }\n      };\n      this.xAxisAnnotation = {\n        id: undefined,\n        x: 0,\n        x2: null,\n        strokeDashArray: 1,\n        fillColor: '#c2c2c2',\n        borderColor: '#c2c2c2',\n        borderWidth: 1,\n        opacity: 0.3,\n        offsetX: 0,\n        offsetY: 0,\n        label: {\n          borderColor: '#c2c2c2',\n          borderWidth: 1,\n          borderRadius: 2,\n          text: undefined,\n          textAnchor: 'middle',\n          orientation: 'vertical',\n          position: 'top',\n          offsetX: 0,\n          offsetY: 0,\n          mouseEnter: undefined,\n          mouseLeave: undefined,\n          click: undefined,\n          style: {\n            background: '#fff',\n            color: undefined,\n            fontSize: '11px',\n            fontFamily: undefined,\n            fontWeight: 400,\n            cssClass: '',\n            padding: {\n              left: 5,\n              right: 5,\n              top: 2,\n              bottom: 2\n            }\n          }\n        }\n      };\n      this.text = {\n        x: 0,\n        y: 0,\n        text: '',\n        textAnchor: 'start',\n        foreColor: undefined,\n        fontSize: '13px',\n        fontFamily: undefined,\n        fontWeight: 400,\n        appendTo: '.apexcharts-annotations',\n        backgroundColor: 'transparent',\n        borderColor: '#c2c2c2',\n        borderRadius: 0,\n        borderWidth: 0,\n        paddingLeft: 4,\n        paddingRight: 4,\n        paddingTop: 2,\n        paddingBottom: 2\n      };\n    }\n\n    _createClass(Options, [{\n      key: \"init\",\n      value: function init() {\n        return {\n          annotations: {\n            yaxis: [this.yAxisAnnotation],\n            xaxis: [this.xAxisAnnotation],\n            points: [this.pointAnnotation],\n            texts: [],\n            images: [],\n            shapes: []\n          },\n          chart: {\n            animations: {\n              enabled: true,\n              easing: 'easeinout',\n              // linear, easeout, easein, easeinout, swing, bounce, elastic\n              speed: 800,\n              animateGradually: {\n                delay: 150,\n                enabled: true\n              },\n              dynamicAnimation: {\n                enabled: true,\n                speed: 350\n              }\n            },\n            background: 'transparent',\n            locales: [en],\n            defaultLocale: 'en',\n            dropShadow: {\n              enabled: false,\n              enabledOnSeries: undefined,\n              top: 2,\n              left: 2,\n              blur: 4,\n              color: '#000',\n              opacity: 0.35\n            },\n            events: {\n              animationEnd: undefined,\n              beforeMount: undefined,\n              mounted: undefined,\n              updated: undefined,\n              click: undefined,\n              mouseMove: undefined,\n              mouseLeave: undefined,\n              xAxisLabelClick: undefined,\n              legendClick: undefined,\n              markerClick: undefined,\n              selection: undefined,\n              dataPointSelection: undefined,\n              dataPointMouseEnter: undefined,\n              dataPointMouseLeave: undefined,\n              beforeZoom: undefined,\n              beforeResetZoom: undefined,\n              zoomed: undefined,\n              scrolled: undefined,\n              brushScrolled: undefined\n            },\n            foreColor: '#373d3f',\n            fontFamily: 'Helvetica, Arial, sans-serif',\n            height: 'auto',\n            parentHeightOffset: 15,\n            redrawOnParentResize: true,\n            redrawOnWindowResize: true,\n            id: undefined,\n            group: undefined,\n            offsetX: 0,\n            offsetY: 0,\n            selection: {\n              enabled: false,\n              type: 'x',\n              // selectedPoints: undefined, // default datapoints that should be selected automatically\n              fill: {\n                color: '#24292e',\n                opacity: 0.1\n              },\n              stroke: {\n                width: 1,\n                color: '#24292e',\n                opacity: 0.4,\n                dashArray: 3\n              },\n              xaxis: {\n                min: undefined,\n                max: undefined\n              },\n              yaxis: {\n                min: undefined,\n                max: undefined\n              }\n            },\n            sparkline: {\n              enabled: false\n            },\n            brush: {\n              enabled: false,\n              autoScaleYaxis: true,\n              target: undefined\n            },\n            stacked: false,\n            stackType: 'normal',\n            toolbar: {\n              show: true,\n              offsetX: 0,\n              offsetY: 0,\n              tools: {\n                download: true,\n                selection: true,\n                zoom: true,\n                zoomin: true,\n                zoomout: true,\n                pan: true,\n                reset: true,\n                customIcons: []\n              },\n              export: {\n                csv: {\n                  filename: undefined,\n                  columnDelimiter: ',',\n                  headerCategory: 'category',\n                  headerValue: 'value',\n                  dateFormatter: function dateFormatter(timestamp) {\n                    return new Date(timestamp).toDateString();\n                  }\n                },\n                png: {\n                  filename: undefined\n                },\n                svg: {\n                  filename: undefined\n                }\n              },\n              autoSelected: 'zoom' // accepts -> zoom, pan, selection\n\n            },\n            type: 'line',\n            width: '100%',\n            zoom: {\n              enabled: true,\n              type: 'x',\n              autoScaleYaxis: false,\n              zoomedArea: {\n                fill: {\n                  color: '#90CAF9',\n                  opacity: 0.4\n                },\n                stroke: {\n                  color: '#0D47A1',\n                  opacity: 0.4,\n                  width: 1\n                }\n              }\n            }\n          },\n          plotOptions: {\n            area: {\n              fillTo: 'origin'\n            },\n            bar: {\n              horizontal: false,\n              columnWidth: '70%',\n              // should be in percent 0 - 100\n              barHeight: '70%',\n              // should be in percent 0 - 100\n              distributed: false,\n              borderRadius: 0,\n              borderRadiusApplication: 'around',\n              // [around, end]\n              borderRadiusWhenStacked: 'last',\n              // [all, last]\n              rangeBarOverlap: true,\n              rangeBarGroupRows: false,\n              hideZeroBarsWhenGrouped: true,\n              colors: {\n                ranges: [],\n                backgroundBarColors: [],\n                backgroundBarOpacity: 1,\n                backgroundBarRadius: 0\n              },\n              dataLabels: {\n                position: 'top',\n                // top, center, bottom\n                maxItems: 100,\n                hideOverflowingLabels: true,\n                orientation: 'horizontal',\n                total: {\n                  enabled: false,\n                  formatter: undefined,\n                  offsetX: 0,\n                  offsetY: 0,\n                  style: {\n                    color: '#373d3f',\n                    fontSize: '12px',\n                    fontFamily: undefined,\n                    fontWeight: 600\n                  }\n                }\n              }\n            },\n            bubble: {\n              zScaling: true,\n              minBubbleRadius: undefined,\n              maxBubbleRadius: undefined\n            },\n            candlestick: {\n              colors: {\n                upward: '#00B746',\n                downward: '#EF403C'\n              },\n              wick: {\n                useFillColor: true\n              }\n            },\n            boxPlot: {\n              colors: {\n                upper: '#00E396',\n                lower: '#008FFB'\n              }\n            },\n            heatmap: {\n              radius: 2,\n              enableShades: true,\n              shadeIntensity: 0.5,\n              reverseNegativeShade: false,\n              distributed: false,\n              useFillColorAsStroke: false,\n              colorScale: {\n                inverse: false,\n                ranges: [],\n                min: undefined,\n                max: undefined\n              }\n            },\n            treemap: {\n              enableShades: true,\n              shadeIntensity: 0.5,\n              distributed: false,\n              reverseNegativeShade: false,\n              useFillColorAsStroke: false,\n              colorScale: {\n                inverse: false,\n                ranges: [],\n                min: undefined,\n                max: undefined\n              }\n            },\n            radialBar: {\n              inverseOrder: false,\n              startAngle: 0,\n              endAngle: 360,\n              offsetX: 0,\n              offsetY: 0,\n              hollow: {\n                margin: 5,\n                size: '50%',\n                background: 'transparent',\n                image: undefined,\n                imageWidth: 150,\n                imageHeight: 150,\n                imageOffsetX: 0,\n                imageOffsetY: 0,\n                imageClipped: true,\n                position: 'front',\n                dropShadow: {\n                  enabled: false,\n                  top: 0,\n                  left: 0,\n                  blur: 3,\n                  color: '#000',\n                  opacity: 0.5\n                }\n              },\n              track: {\n                show: true,\n                startAngle: undefined,\n                endAngle: undefined,\n                background: '#f2f2f2',\n                strokeWidth: '97%',\n                opacity: 1,\n                margin: 5,\n                // margin is in pixels\n                dropShadow: {\n                  enabled: false,\n                  top: 0,\n                  left: 0,\n                  blur: 3,\n                  color: '#000',\n                  opacity: 0.5\n                }\n              },\n              dataLabels: {\n                show: true,\n                name: {\n                  show: true,\n                  fontSize: '16px',\n                  fontFamily: undefined,\n                  fontWeight: 600,\n                  color: undefined,\n                  offsetY: 0,\n                  formatter: function formatter(val) {\n                    return val;\n                  }\n                },\n                value: {\n                  show: true,\n                  fontSize: '14px',\n                  fontFamily: undefined,\n                  fontWeight: 400,\n                  color: undefined,\n                  offsetY: 16,\n                  formatter: function formatter(val) {\n                    return val + '%';\n                  }\n                },\n                total: {\n                  show: false,\n                  label: 'Total',\n                  fontSize: '16px',\n                  fontWeight: 600,\n                  fontFamily: undefined,\n                  color: undefined,\n                  formatter: function formatter(w) {\n                    return w.globals.seriesTotals.reduce(function (a, b) {\n                      return a + b;\n                    }, 0) / w.globals.series.length + '%';\n                  }\n                }\n              }\n            },\n            pie: {\n              customScale: 1,\n              offsetX: 0,\n              offsetY: 0,\n              startAngle: 0,\n              endAngle: 360,\n              expandOnClick: true,\n              dataLabels: {\n                // These are the percentage values which are displayed on slice\n                offset: 0,\n                // offset by which labels will move outside\n                minAngleToShowLabel: 10\n              },\n              donut: {\n                size: '65%',\n                background: 'transparent',\n                labels: {\n                  // These are the inner labels appearing inside donut\n                  show: false,\n                  name: {\n                    show: true,\n                    fontSize: '16px',\n                    fontFamily: undefined,\n                    fontWeight: 600,\n                    color: undefined,\n                    offsetY: -10,\n                    formatter: function formatter(val) {\n                      return val;\n                    }\n                  },\n                  value: {\n                    show: true,\n                    fontSize: '20px',\n                    fontFamily: undefined,\n                    fontWeight: 400,\n                    color: undefined,\n                    offsetY: 10,\n                    formatter: function formatter(val) {\n                      return val;\n                    }\n                  },\n                  total: {\n                    show: false,\n                    showAlways: false,\n                    label: 'Total',\n                    fontSize: '16px',\n                    fontWeight: 400,\n                    fontFamily: undefined,\n                    color: undefined,\n                    formatter: function formatter(w) {\n                      return w.globals.seriesTotals.reduce(function (a, b) {\n                        return a + b;\n                      }, 0);\n                    }\n                  }\n                }\n              }\n            },\n            polarArea: {\n              rings: {\n                strokeWidth: 1,\n                strokeColor: '#e8e8e8'\n              },\n              spokes: {\n                strokeWidth: 1,\n                connectorColors: '#e8e8e8'\n              }\n            },\n            radar: {\n              size: undefined,\n              offsetX: 0,\n              offsetY: 0,\n              polygons: {\n                // strokeColor: '#e8e8e8', // should be deprecated in the minor version i.e 3.2\n                strokeWidth: 1,\n                strokeColors: '#e8e8e8',\n                connectorColors: '#e8e8e8',\n                fill: {\n                  colors: undefined\n                }\n              }\n            }\n          },\n          colors: undefined,\n          dataLabels: {\n            enabled: true,\n            enabledOnSeries: undefined,\n            formatter: function formatter(val) {\n              return val !== null ? val : '';\n            },\n            textAnchor: 'middle',\n            distributed: false,\n            offsetX: 0,\n            offsetY: 0,\n            style: {\n              fontSize: '12px',\n              fontFamily: undefined,\n              fontWeight: 600,\n              colors: undefined\n            },\n            background: {\n              enabled: true,\n              foreColor: '#fff',\n              borderRadius: 2,\n              padding: 4,\n              opacity: 0.9,\n              borderWidth: 1,\n              borderColor: '#fff',\n              dropShadow: {\n                enabled: false,\n                top: 1,\n                left: 1,\n                blur: 1,\n                color: '#000',\n                opacity: 0.45\n              }\n            },\n            dropShadow: {\n              enabled: false,\n              top: 1,\n              left: 1,\n              blur: 1,\n              color: '#000',\n              opacity: 0.45\n            }\n          },\n          fill: {\n            type: 'solid',\n            colors: undefined,\n            // array of colors\n            opacity: 0.85,\n            gradient: {\n              shade: 'dark',\n              type: 'horizontal',\n              shadeIntensity: 0.5,\n              gradientToColors: undefined,\n              inverseColors: true,\n              opacityFrom: 1,\n              opacityTo: 1,\n              stops: [0, 50, 100],\n              colorStops: []\n            },\n            image: {\n              src: [],\n              width: undefined,\n              // optional\n              height: undefined // optional\n\n            },\n            pattern: {\n              style: 'squares',\n              // String | Array of Strings\n              width: 6,\n              height: 6,\n              strokeWidth: 2\n            }\n          },\n          forecastDataPoints: {\n            count: 0,\n            fillOpacity: 0.5,\n            strokeWidth: undefined,\n            dashArray: 4\n          },\n          grid: {\n            show: true,\n            borderColor: '#e0e0e0',\n            strokeDashArray: 0,\n            position: 'back',\n            xaxis: {\n              lines: {\n                show: false\n              }\n            },\n            yaxis: {\n              lines: {\n                show: true\n              }\n            },\n            row: {\n              colors: undefined,\n              // takes as array which will be repeated on rows\n              opacity: 0.5\n            },\n            column: {\n              colors: undefined,\n              // takes an array which will be repeated on columns\n              opacity: 0.5\n            },\n            padding: {\n              top: 0,\n              right: 10,\n              bottom: 0,\n              left: 12\n            }\n          },\n          labels: [],\n          legend: {\n            show: true,\n            showForSingleSeries: false,\n            showForNullSeries: true,\n            showForZeroSeries: true,\n            floating: false,\n            position: 'bottom',\n            // whether to position legends in 1 of 4\n            // direction - top, bottom, left, right\n            horizontalAlign: 'center',\n            // when position top/bottom, you can specify whether to align legends left, right or center\n            inverseOrder: false,\n            fontSize: '12px',\n            fontFamily: undefined,\n            fontWeight: 400,\n            width: undefined,\n            height: undefined,\n            formatter: undefined,\n            tooltipHoverFormatter: undefined,\n            offsetX: -20,\n            offsetY: 4,\n            customLegendItems: [],\n            labels: {\n              colors: undefined,\n              useSeriesColors: false\n            },\n            markers: {\n              width: 12,\n              height: 12,\n              strokeWidth: 0,\n              fillColors: undefined,\n              strokeColor: '#fff',\n              radius: 12,\n              customHTML: undefined,\n              offsetX: 0,\n              offsetY: 0,\n              onClick: undefined\n            },\n            itemMargin: {\n              horizontal: 5,\n              vertical: 2\n            },\n            onItemClick: {\n              toggleDataSeries: true\n            },\n            onItemHover: {\n              highlightDataSeries: true\n            }\n          },\n          markers: {\n            discrete: [],\n            size: 0,\n            colors: undefined,\n            //strokeColor: '#fff', // TODO: deprecate in major version 4.0\n            strokeColors: '#fff',\n            strokeWidth: 2,\n            strokeOpacity: 0.9,\n            strokeDashArray: 0,\n            fillOpacity: 1,\n            shape: 'circle',\n            width: 8,\n            // only applicable when shape is rect/square\n            height: 8,\n            // only applicable when shape is rect/square\n            radius: 2,\n            offsetX: 0,\n            offsetY: 0,\n            onClick: undefined,\n            onDblClick: undefined,\n            showNullDataPoints: true,\n            hover: {\n              size: undefined,\n              sizeOffset: 3\n            }\n          },\n          noData: {\n            text: undefined,\n            align: 'center',\n            verticalAlign: 'middle',\n            offsetX: 0,\n            offsetY: 0,\n            style: {\n              color: undefined,\n              fontSize: '14px',\n              fontFamily: undefined\n            }\n          },\n          responsive: [],\n          // breakpoints should follow ascending order 400, then 700, then 1000\n          series: undefined,\n          states: {\n            normal: {\n              filter: {\n                type: 'none',\n                value: 0\n              }\n            },\n            hover: {\n              filter: {\n                type: 'lighten',\n                value: 0.1\n              }\n            },\n            active: {\n              allowMultipleDataPointsSelection: false,\n              filter: {\n                type: 'darken',\n                value: 0.5\n              }\n            }\n          },\n          title: {\n            text: undefined,\n            align: 'left',\n            margin: 5,\n            offsetX: 0,\n            offsetY: 0,\n            floating: false,\n            style: {\n              fontSize: '14px',\n              fontWeight: 900,\n              fontFamily: undefined,\n              color: undefined\n            }\n          },\n          subtitle: {\n            text: undefined,\n            align: 'left',\n            margin: 5,\n            offsetX: 0,\n            offsetY: 30,\n            floating: false,\n            style: {\n              fontSize: '12px',\n              fontWeight: 400,\n              fontFamily: undefined,\n              color: undefined\n            }\n          },\n          stroke: {\n            show: true,\n            curve: 'smooth',\n            // \"smooth\" / \"straight\" / \"stepline\"\n            lineCap: 'butt',\n            // round, butt , square\n            width: 2,\n            colors: undefined,\n            // array of colors\n            dashArray: 0,\n            // single value or array of values\n            fill: {\n              type: 'solid',\n              colors: undefined,\n              // array of colors\n              opacity: 0.85,\n              gradient: {\n                shade: 'dark',\n                type: 'horizontal',\n                shadeIntensity: 0.5,\n                gradientToColors: undefined,\n                inverseColors: true,\n                opacityFrom: 1,\n                opacityTo: 1,\n                stops: [0, 50, 100],\n                colorStops: []\n              }\n            }\n          },\n          tooltip: {\n            enabled: true,\n            enabledOnSeries: undefined,\n            shared: true,\n            followCursor: false,\n            // when disabled, the tooltip will show on top of the series instead of mouse position\n            intersect: false,\n            // when enabled, tooltip will only show when user directly hovers over point\n            inverseOrder: false,\n            custom: undefined,\n            fillSeriesColor: false,\n            theme: 'light',\n            cssClass: '',\n            style: {\n              fontSize: '12px',\n              fontFamily: undefined\n            },\n            onDatasetHover: {\n              highlightDataSeries: false\n            },\n            x: {\n              // x value\n              show: true,\n              format: 'dd MMM',\n              // dd/MM, dd MMM yy, dd MMM yyyy\n              formatter: undefined // a custom user supplied formatter function\n\n            },\n            y: {\n              formatter: undefined,\n              title: {\n                formatter: function formatter(seriesName) {\n                  return seriesName ? seriesName + ': ' : '';\n                }\n              }\n            },\n            z: {\n              formatter: undefined,\n              title: 'Size: '\n            },\n            marker: {\n              show: true,\n              fillColors: undefined\n            },\n            items: {\n              display: 'flex'\n            },\n            fixed: {\n              enabled: false,\n              position: 'topRight',\n              // topRight, topLeft, bottomRight, bottomLeft\n              offsetX: 0,\n              offsetY: 0\n            }\n          },\n          xaxis: {\n            type: 'category',\n            categories: [],\n            convertedCatToNumeric: false,\n            // internal property which should not be altered outside\n            offsetX: 0,\n            offsetY: 0,\n            overwriteCategories: undefined,\n            labels: {\n              show: true,\n              rotate: -45,\n              rotateAlways: false,\n              hideOverlappingLabels: true,\n              trim: false,\n              minHeight: undefined,\n              maxHeight: 120,\n              showDuplicates: true,\n              style: {\n                colors: [],\n                fontSize: '12px',\n                fontWeight: 400,\n                fontFamily: undefined,\n                cssClass: ''\n              },\n              offsetX: 0,\n              offsetY: 0,\n              format: undefined,\n              formatter: undefined,\n              // custom formatter function which will override format\n              datetimeUTC: true,\n              datetimeFormatter: {\n                year: 'yyyy',\n                month: \"MMM 'yy\",\n                day: 'dd MMM',\n                hour: 'HH:mm',\n                minute: 'HH:mm:ss',\n                second: 'HH:mm:ss'\n              }\n            },\n            group: {\n              groups: [],\n              style: {\n                colors: [],\n                fontSize: '12px',\n                fontWeight: 400,\n                fontFamily: undefined,\n                cssClass: ''\n              }\n            },\n            axisBorder: {\n              show: true,\n              color: '#e0e0e0',\n              width: '100%',\n              height: 1,\n              offsetX: 0,\n              offsetY: 0\n            },\n            axisTicks: {\n              show: true,\n              color: '#e0e0e0',\n              height: 6,\n              offsetX: 0,\n              offsetY: 0\n            },\n            tickAmount: undefined,\n            tickPlacement: 'on',\n            min: undefined,\n            max: undefined,\n            range: undefined,\n            floating: false,\n            decimalsInFloat: undefined,\n            position: 'bottom',\n            title: {\n              text: undefined,\n              offsetX: 0,\n              offsetY: 0,\n              style: {\n                color: undefined,\n                fontSize: '12px',\n                fontWeight: 900,\n                fontFamily: undefined,\n                cssClass: ''\n              }\n            },\n            crosshairs: {\n              show: true,\n              width: 1,\n              // tickWidth/barWidth or an integer\n              position: 'back',\n              opacity: 0.9,\n              stroke: {\n                color: '#b6b6b6',\n                width: 1,\n                dashArray: 3\n              },\n              fill: {\n                type: 'solid',\n                // solid, gradient\n                color: '#B1B9C4',\n                gradient: {\n                  colorFrom: '#D8E3F0',\n                  colorTo: '#BED1E6',\n                  stops: [0, 100],\n                  opacityFrom: 0.4,\n                  opacityTo: 0.5\n                }\n              },\n              dropShadow: {\n                enabled: false,\n                left: 0,\n                top: 0,\n                blur: 1,\n                opacity: 0.4\n              }\n            },\n            tooltip: {\n              enabled: true,\n              offsetY: 0,\n              formatter: undefined,\n              style: {\n                fontSize: '12px',\n                fontFamily: undefined\n              }\n            }\n          },\n          yaxis: this.yAxis,\n          theme: {\n            mode: 'light',\n            palette: 'palette1',\n            // If defined, it will overwrite globals.colors variable\n            monochrome: {\n              // monochrome allows you to select just 1 color and fill out the rest with light/dark shade (intensity can be selected)\n              enabled: false,\n              color: '#008FFB',\n              shadeTo: 'light',\n              shadeIntensity: 0.65\n            }\n          }\n        };\n      }\n    }]);\n\n    return Options;\n  }();\n\n  /**\n   * ApexCharts Annotations Class for drawing lines/rects on both xaxis and yaxis.\n   *\n   * @module Annotations\n   **/\n\n  var Annotations = /*#__PURE__*/function () {\n    function Annotations(ctx) {\n      _classCallCheck(this, Annotations);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.graphics = new Graphics(this.ctx);\n\n      if (this.w.globals.isBarHorizontal) {\n        this.invertAxis = true;\n      }\n\n      this.helpers = new Helpers$4(this);\n      this.xAxisAnnotations = new XAnnotations(this);\n      this.yAxisAnnotations = new YAnnotations(this);\n      this.pointsAnnotations = new PointAnnotations(this);\n\n      if (this.w.globals.isBarHorizontal && this.w.config.yaxis[0].reversed) {\n        this.inversedReversedAxis = true;\n      }\n\n      this.xDivision = this.w.globals.gridWidth / this.w.globals.dataPoints;\n    }\n\n    _createClass(Annotations, [{\n      key: \"drawAxesAnnotations\",\n      value: function drawAxesAnnotations() {\n        var w = this.w;\n\n        if (w.globals.axisCharts) {\n          var yAnnotations = this.yAxisAnnotations.drawYAxisAnnotations();\n          var xAnnotations = this.xAxisAnnotations.drawXAxisAnnotations();\n          var pointAnnotations = this.pointsAnnotations.drawPointAnnotations();\n          var initialAnim = w.config.chart.animations.enabled;\n          var annoArray = [yAnnotations, xAnnotations, pointAnnotations];\n          var annoElArray = [xAnnotations.node, yAnnotations.node, pointAnnotations.node];\n\n          for (var i = 0; i < 3; i++) {\n            w.globals.dom.elGraphical.add(annoArray[i]);\n\n            if (initialAnim && !w.globals.resized && !w.globals.dataChanged) {\n              // fixes apexcharts/apexcharts.js#685\n              if (w.config.chart.type !== 'scatter' && w.config.chart.type !== 'bubble' && w.globals.dataPoints > 1) {\n                annoElArray[i].classList.add('apexcharts-element-hidden');\n              }\n            }\n\n            w.globals.delayedElements.push({\n              el: annoElArray[i],\n              index: 0\n            });\n          } // background sizes needs to be calculated after text is drawn, so calling them last\n\n\n          this.helpers.annotationsBackground();\n        }\n      }\n    }, {\n      key: \"drawImageAnnos\",\n      value: function drawImageAnnos() {\n        var _this = this;\n\n        var w = this.w;\n        w.config.annotations.images.map(function (s, index) {\n          _this.addImage(s, index);\n        });\n      }\n    }, {\n      key: \"drawTextAnnos\",\n      value: function drawTextAnnos() {\n        var _this2 = this;\n\n        var w = this.w;\n        w.config.annotations.texts.map(function (t, index) {\n          _this2.addText(t, index);\n        });\n      }\n    }, {\n      key: \"addXaxisAnnotation\",\n      value: function addXaxisAnnotation(anno, parent, index) {\n        this.xAxisAnnotations.addXaxisAnnotation(anno, parent, index);\n      }\n    }, {\n      key: \"addYaxisAnnotation\",\n      value: function addYaxisAnnotation(anno, parent, index) {\n        this.yAxisAnnotations.addYaxisAnnotation(anno, parent, index);\n      }\n    }, {\n      key: \"addPointAnnotation\",\n      value: function addPointAnnotation(anno, parent, index) {\n        this.pointsAnnotations.addPointAnnotation(anno, parent, index);\n      }\n    }, {\n      key: \"addText\",\n      value: function addText(params, index) {\n        var x = params.x,\n            y = params.y,\n            text = params.text,\n            textAnchor = params.textAnchor,\n            foreColor = params.foreColor,\n            fontSize = params.fontSize,\n            fontFamily = params.fontFamily,\n            fontWeight = params.fontWeight,\n            cssClass = params.cssClass,\n            backgroundColor = params.backgroundColor,\n            borderWidth = params.borderWidth,\n            strokeDashArray = params.strokeDashArray,\n            borderRadius = params.borderRadius,\n            borderColor = params.borderColor,\n            _params$appendTo = params.appendTo,\n            appendTo = _params$appendTo === void 0 ? '.apexcharts-annotations' : _params$appendTo,\n            _params$paddingLeft = params.paddingLeft,\n            paddingLeft = _params$paddingLeft === void 0 ? 4 : _params$paddingLeft,\n            _params$paddingRight = params.paddingRight,\n            paddingRight = _params$paddingRight === void 0 ? 4 : _params$paddingRight,\n            _params$paddingBottom = params.paddingBottom,\n            paddingBottom = _params$paddingBottom === void 0 ? 2 : _params$paddingBottom,\n            _params$paddingTop = params.paddingTop,\n            paddingTop = _params$paddingTop === void 0 ? 2 : _params$paddingTop;\n        var w = this.w;\n        var elText = this.graphics.drawText({\n          x: x,\n          y: y,\n          text: text,\n          textAnchor: textAnchor || 'start',\n          fontSize: fontSize || '12px',\n          fontWeight: fontWeight || 'regular',\n          fontFamily: fontFamily || w.config.chart.fontFamily,\n          foreColor: foreColor || w.config.chart.foreColor,\n          cssClass: 'apexcharts-text ' + cssClass ? cssClass : ''\n        });\n        var parent = w.globals.dom.baseEl.querySelector(appendTo);\n\n        if (parent) {\n          parent.appendChild(elText.node);\n        }\n\n        var textRect = elText.bbox();\n\n        if (text) {\n          var elRect = this.graphics.drawRect(textRect.x - paddingLeft, textRect.y - paddingTop, textRect.width + paddingLeft + paddingRight, textRect.height + paddingBottom + paddingTop, borderRadius, backgroundColor ? backgroundColor : 'transparent', 1, borderWidth, borderColor, strokeDashArray);\n          parent.insertBefore(elRect.node, elText.node);\n        }\n      }\n    }, {\n      key: \"addImage\",\n      value: function addImage(params, index) {\n        var w = this.w;\n        var path = params.path,\n            _params$x = params.x,\n            x = _params$x === void 0 ? 0 : _params$x,\n            _params$y = params.y,\n            y = _params$y === void 0 ? 0 : _params$y,\n            _params$width = params.width,\n            width = _params$width === void 0 ? 20 : _params$width,\n            _params$height = params.height,\n            height = _params$height === void 0 ? 20 : _params$height,\n            _params$appendTo2 = params.appendTo,\n            appendTo = _params$appendTo2 === void 0 ? '.apexcharts-annotations' : _params$appendTo2;\n        var img = w.globals.dom.Paper.image(path);\n        img.size(width, height).move(x, y);\n        var parent = w.globals.dom.baseEl.querySelector(appendTo);\n\n        if (parent) {\n          parent.appendChild(img.node);\n        }\n\n        return img;\n      } // The addXaxisAnnotation method requires a parent class, and user calling this method externally on the chart instance may not specify parent, hence a different method\n\n    }, {\n      key: \"addXaxisAnnotationExternal\",\n      value: function addXaxisAnnotationExternal(params, pushToMemory, context) {\n        this.addAnnotationExternal({\n          params: params,\n          pushToMemory: pushToMemory,\n          context: context,\n          type: 'xaxis',\n          contextMethod: context.addXaxisAnnotation\n        });\n        return context;\n      }\n    }, {\n      key: \"addYaxisAnnotationExternal\",\n      value: function addYaxisAnnotationExternal(params, pushToMemory, context) {\n        this.addAnnotationExternal({\n          params: params,\n          pushToMemory: pushToMemory,\n          context: context,\n          type: 'yaxis',\n          contextMethod: context.addYaxisAnnotation\n        });\n        return context;\n      }\n    }, {\n      key: \"addPointAnnotationExternal\",\n      value: function addPointAnnotationExternal(params, pushToMemory, context) {\n        if (typeof this.invertAxis === 'undefined') {\n          this.invertAxis = context.w.globals.isBarHorizontal;\n        }\n\n        this.addAnnotationExternal({\n          params: params,\n          pushToMemory: pushToMemory,\n          context: context,\n          type: 'point',\n          contextMethod: context.addPointAnnotation\n        });\n        return context;\n      }\n    }, {\n      key: \"addAnnotationExternal\",\n      value: function addAnnotationExternal(_ref) {\n        var params = _ref.params,\n            pushToMemory = _ref.pushToMemory,\n            context = _ref.context,\n            type = _ref.type,\n            contextMethod = _ref.contextMethod;\n        var me = context;\n        var w = me.w;\n        var parent = w.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(type, \"-annotations\"));\n        var index = parent.childNodes.length + 1;\n        var options = new Options();\n        var axesAnno = Object.assign({}, type === 'xaxis' ? options.xAxisAnnotation : type === 'yaxis' ? options.yAxisAnnotation : options.pointAnnotation);\n        var anno = Utils$1.extend(axesAnno, params);\n\n        switch (type) {\n          case 'xaxis':\n            this.addXaxisAnnotation(anno, parent, index);\n            break;\n\n          case 'yaxis':\n            this.addYaxisAnnotation(anno, parent, index);\n            break;\n\n          case 'point':\n            this.addPointAnnotation(anno, parent, index);\n            break;\n        } // add background\n\n\n        var axesAnnoLabel = w.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(type, \"-annotations .apexcharts-\").concat(type, \"-annotation-label[rel='\").concat(index, \"']\"));\n        var elRect = this.helpers.addBackgroundToAnno(axesAnnoLabel, anno);\n\n        if (elRect) {\n          parent.insertBefore(elRect.node, axesAnnoLabel);\n        }\n\n        if (pushToMemory) {\n          w.globals.memory.methodsToExec.push({\n            context: me,\n            id: anno.id ? anno.id : Utils$1.randomId(),\n            method: contextMethod,\n            label: 'addAnnotation',\n            params: params\n          });\n        }\n\n        return context;\n      }\n    }, {\n      key: \"clearAnnotations\",\n      value: function clearAnnotations(ctx) {\n        var w = ctx.w;\n        var annos = w.globals.dom.baseEl.querySelectorAll('.apexcharts-yaxis-annotations, .apexcharts-xaxis-annotations, .apexcharts-point-annotations'); // annotations added externally should be cleared out too\n\n        w.globals.memory.methodsToExec.map(function (m, i) {\n          if (m.label === 'addText' || m.label === 'addAnnotation') {\n            w.globals.memory.methodsToExec.splice(i, 1);\n          }\n        });\n        annos = Utils$1.listToArray(annos); // delete the DOM elements\n\n        Array.prototype.forEach.call(annos, function (a) {\n          while (a.firstChild) {\n            a.removeChild(a.firstChild);\n          }\n        });\n      }\n    }, {\n      key: \"removeAnnotation\",\n      value: function removeAnnotation(ctx, id) {\n        var w = ctx.w;\n        var annos = w.globals.dom.baseEl.querySelectorAll(\".\".concat(id));\n\n        if (annos) {\n          w.globals.memory.methodsToExec.map(function (m, i) {\n            if (m.id === id) {\n              w.globals.memory.methodsToExec.splice(i, 1);\n            }\n          });\n          Array.prototype.forEach.call(annos, function (a) {\n            a.parentElement.removeChild(a);\n          });\n        }\n      }\n    }]);\n\n    return Annotations;\n  }();\n\n  /**\n   * DateTime Class to manipulate datetime values.\n   *\n   * @module DateTime\n   **/\n\n  var DateTime = /*#__PURE__*/function () {\n    function DateTime(ctx) {\n      _classCallCheck(this, DateTime);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.months31 = [1, 3, 5, 7, 8, 10, 12];\n      this.months30 = [2, 4, 6, 9, 11];\n      this.daysCntOfYear = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n    }\n\n    _createClass(DateTime, [{\n      key: \"isValidDate\",\n      value: function isValidDate(date) {\n        return !isNaN(this.parseDate(date));\n      }\n    }, {\n      key: \"getTimeStamp\",\n      value: function getTimeStamp(dateStr) {\n        if (!Date.parse(dateStr)) {\n          return dateStr;\n        }\n\n        var utc = this.w.config.xaxis.labels.datetimeUTC;\n        return !utc ? new Date(dateStr).getTime() : new Date(new Date(dateStr).toISOString().substr(0, 25)).getTime();\n      }\n    }, {\n      key: \"getDate\",\n      value: function getDate(timestamp) {\n        var utc = this.w.config.xaxis.labels.datetimeUTC;\n        return utc ? new Date(new Date(timestamp).toUTCString()) : new Date(timestamp);\n      }\n    }, {\n      key: \"parseDate\",\n      value: function parseDate(dateStr) {\n        var parsed = Date.parse(dateStr);\n\n        if (!isNaN(parsed)) {\n          return this.getTimeStamp(dateStr);\n        }\n\n        var output = Date.parse(dateStr.replace(/-/g, '/').replace(/[a-z]+/gi, ' '));\n        output = this.getTimeStamp(output);\n        return output;\n      } // This fixes the difference of x-axis labels between chrome/safari\n      // Fixes #1726, #1544, #1485, #1255\n\n    }, {\n      key: \"parseDateWithTimezone\",\n      value: function parseDateWithTimezone(dateStr) {\n        return Date.parse(dateStr.replace(/-/g, '/').replace(/[a-z]+/gi, ' '));\n      } // http://stackoverflow.com/questions/14638018/current-time-formatting-with-javascript#answer-14638191\n\n    }, {\n      key: \"formatDate\",\n      value: function formatDate(date, format) {\n        var locale = this.w.globals.locale;\n        var utc = this.w.config.xaxis.labels.datetimeUTC;\n        var MMMM = ['\\x00'].concat(_toConsumableArray(locale.months));\n        var MMM = ['\\x01'].concat(_toConsumableArray(locale.shortMonths));\n        var dddd = ['\\x02'].concat(_toConsumableArray(locale.days));\n        var ddd = ['\\x03'].concat(_toConsumableArray(locale.shortDays));\n\n        function ii(i, len) {\n          var s = i + '';\n          len = len || 2;\n\n          while (s.length < len) {\n            s = '0' + s;\n          }\n\n          return s;\n        }\n\n        var y = utc ? date.getUTCFullYear() : date.getFullYear();\n        format = format.replace(/(^|[^\\\\])yyyy+/g, '$1' + y);\n        format = format.replace(/(^|[^\\\\])yy/g, '$1' + y.toString().substr(2, 2));\n        format = format.replace(/(^|[^\\\\])y/g, '$1' + y);\n        var M = (utc ? date.getUTCMonth() : date.getMonth()) + 1;\n        format = format.replace(/(^|[^\\\\])MMMM+/g, '$1' + MMMM[0]);\n        format = format.replace(/(^|[^\\\\])MMM/g, '$1' + MMM[0]);\n        format = format.replace(/(^|[^\\\\])MM/g, '$1' + ii(M));\n        format = format.replace(/(^|[^\\\\])M/g, '$1' + M);\n        var d = utc ? date.getUTCDate() : date.getDate();\n        format = format.replace(/(^|[^\\\\])dddd+/g, '$1' + dddd[0]);\n        format = format.replace(/(^|[^\\\\])ddd/g, '$1' + ddd[0]);\n        format = format.replace(/(^|[^\\\\])dd/g, '$1' + ii(d));\n        format = format.replace(/(^|[^\\\\])d/g, '$1' + d);\n        var H = utc ? date.getUTCHours() : date.getHours();\n        format = format.replace(/(^|[^\\\\])HH+/g, '$1' + ii(H));\n        format = format.replace(/(^|[^\\\\])H/g, '$1' + H);\n        var h = H > 12 ? H - 12 : H === 0 ? 12 : H;\n        format = format.replace(/(^|[^\\\\])hh+/g, '$1' + ii(h));\n        format = format.replace(/(^|[^\\\\])h/g, '$1' + h);\n        var m = utc ? date.getUTCMinutes() : date.getMinutes();\n        format = format.replace(/(^|[^\\\\])mm+/g, '$1' + ii(m));\n        format = format.replace(/(^|[^\\\\])m/g, '$1' + m);\n        var s = utc ? date.getUTCSeconds() : date.getSeconds();\n        format = format.replace(/(^|[^\\\\])ss+/g, '$1' + ii(s));\n        format = format.replace(/(^|[^\\\\])s/g, '$1' + s);\n        var f = utc ? date.getUTCMilliseconds() : date.getMilliseconds();\n        format = format.replace(/(^|[^\\\\])fff+/g, '$1' + ii(f, 3));\n        f = Math.round(f / 10);\n        format = format.replace(/(^|[^\\\\])ff/g, '$1' + ii(f));\n        f = Math.round(f / 10);\n        format = format.replace(/(^|[^\\\\])f/g, '$1' + f);\n        var T = H < 12 ? 'AM' : 'PM';\n        format = format.replace(/(^|[^\\\\])TT+/g, '$1' + T);\n        format = format.replace(/(^|[^\\\\])T/g, '$1' + T.charAt(0));\n        var t = T.toLowerCase();\n        format = format.replace(/(^|[^\\\\])tt+/g, '$1' + t);\n        format = format.replace(/(^|[^\\\\])t/g, '$1' + t.charAt(0));\n        var tz = -date.getTimezoneOffset();\n        var K = utc || !tz ? 'Z' : tz > 0 ? '+' : '-';\n\n        if (!utc) {\n          tz = Math.abs(tz);\n          var tzHrs = Math.floor(tz / 60);\n          var tzMin = tz % 60;\n          K += ii(tzHrs) + ':' + ii(tzMin);\n        }\n\n        format = format.replace(/(^|[^\\\\])K/g, '$1' + K);\n        var day = (utc ? date.getUTCDay() : date.getDay()) + 1;\n        format = format.replace(new RegExp(dddd[0], 'g'), dddd[day]);\n        format = format.replace(new RegExp(ddd[0], 'g'), ddd[day]);\n        format = format.replace(new RegExp(MMMM[0], 'g'), MMMM[M]);\n        format = format.replace(new RegExp(MMM[0], 'g'), MMM[M]);\n        format = format.replace(/\\\\(.)/g, '$1');\n        return format;\n      }\n    }, {\n      key: \"getTimeUnitsfromTimestamp\",\n      value: function getTimeUnitsfromTimestamp(minX, maxX, utc) {\n        var w = this.w;\n\n        if (w.config.xaxis.min !== undefined) {\n          minX = w.config.xaxis.min;\n        }\n\n        if (w.config.xaxis.max !== undefined) {\n          maxX = w.config.xaxis.max;\n        }\n\n        var tsMin = this.getDate(minX);\n        var tsMax = this.getDate(maxX);\n        var minD = this.formatDate(tsMin, 'yyyy MM dd HH mm ss fff').split(' ');\n        var maxD = this.formatDate(tsMax, 'yyyy MM dd HH mm ss fff').split(' ');\n        return {\n          minMillisecond: parseInt(minD[6], 10),\n          maxMillisecond: parseInt(maxD[6], 10),\n          minSecond: parseInt(minD[5], 10),\n          maxSecond: parseInt(maxD[5], 10),\n          minMinute: parseInt(minD[4], 10),\n          maxMinute: parseInt(maxD[4], 10),\n          minHour: parseInt(minD[3], 10),\n          maxHour: parseInt(maxD[3], 10),\n          minDate: parseInt(minD[2], 10),\n          maxDate: parseInt(maxD[2], 10),\n          minMonth: parseInt(minD[1], 10) - 1,\n          maxMonth: parseInt(maxD[1], 10) - 1,\n          minYear: parseInt(minD[0], 10),\n          maxYear: parseInt(maxD[0], 10)\n        };\n      }\n    }, {\n      key: \"isLeapYear\",\n      value: function isLeapYear(year) {\n        return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;\n      }\n    }, {\n      key: \"calculcateLastDaysOfMonth\",\n      value: function calculcateLastDaysOfMonth(month, year, subtract) {\n        var days = this.determineDaysOfMonths(month, year); // whatever days we get, subtract the number of days asked\n\n        return days - subtract;\n      }\n    }, {\n      key: \"determineDaysOfYear\",\n      value: function determineDaysOfYear(year) {\n        var days = 365;\n\n        if (this.isLeapYear(year)) {\n          days = 366;\n        }\n\n        return days;\n      }\n    }, {\n      key: \"determineRemainingDaysOfYear\",\n      value: function determineRemainingDaysOfYear(year, month, date) {\n        var dayOfYear = this.daysCntOfYear[month] + date;\n        if (month > 1 && this.isLeapYear()) dayOfYear++;\n        return dayOfYear;\n      }\n    }, {\n      key: \"determineDaysOfMonths\",\n      value: function determineDaysOfMonths(month, year) {\n        var days = 30;\n        month = Utils$1.monthMod(month);\n\n        switch (true) {\n          case this.months30.indexOf(month) > -1:\n            if (month === 2) {\n              if (this.isLeapYear(year)) {\n                days = 29;\n              } else {\n                days = 28;\n              }\n            }\n\n            break;\n\n          case this.months31.indexOf(month) > -1:\n            days = 31;\n            break;\n\n          default:\n            days = 31;\n            break;\n        }\n\n        return days;\n      }\n    }]);\n\n    return DateTime;\n  }();\n\n  /**\n   * ApexCharts Formatter Class for setting value formatters for axes as well as tooltips.\n   *\n   * @module Formatters\n   **/\n\n  var Formatters = /*#__PURE__*/function () {\n    function Formatters(ctx) {\n      _classCallCheck(this, Formatters);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.tooltipKeyFormat = 'dd MMM';\n    }\n\n    _createClass(Formatters, [{\n      key: \"xLabelFormat\",\n      value: function xLabelFormat(fn, val, timestamp, opts) {\n        var w = this.w;\n\n        if (w.config.xaxis.type === 'datetime') {\n          if (w.config.xaxis.labels.formatter === undefined) {\n            // if user has not specified a custom formatter, use the default tooltip.x.format\n            if (w.config.tooltip.x.formatter === undefined) {\n              var datetimeObj = new DateTime(this.ctx);\n              return datetimeObj.formatDate(datetimeObj.getDate(val), w.config.tooltip.x.format);\n            }\n          }\n        }\n\n        return fn(val, timestamp, opts);\n      }\n    }, {\n      key: \"defaultGeneralFormatter\",\n      value: function defaultGeneralFormatter(val) {\n        if (Array.isArray(val)) {\n          return val.map(function (v) {\n            return v;\n          });\n        } else {\n          return val;\n        }\n      }\n    }, {\n      key: \"defaultYFormatter\",\n      value: function defaultYFormatter(v, yaxe, i) {\n        var w = this.w;\n\n        if (Utils$1.isNumber(v)) {\n          if (w.globals.yValueDecimal !== 0) {\n            v = v.toFixed(yaxe.decimalsInFloat !== undefined ? yaxe.decimalsInFloat : w.globals.yValueDecimal);\n          } else if (w.globals.maxYArr[i] - w.globals.minYArr[i] < 5) {\n            v = v.toFixed(1);\n          } else {\n            v = v.toFixed(0);\n          }\n        }\n\n        return v;\n      }\n    }, {\n      key: \"setLabelFormatters\",\n      value: function setLabelFormatters() {\n        var _this = this;\n\n        var w = this.w;\n\n        w.globals.xaxisTooltipFormatter = function (val) {\n          return _this.defaultGeneralFormatter(val);\n        };\n\n        w.globals.ttKeyFormatter = function (val) {\n          return _this.defaultGeneralFormatter(val);\n        };\n\n        w.globals.ttZFormatter = function (val) {\n          return val;\n        };\n\n        w.globals.legendFormatter = function (val) {\n          return _this.defaultGeneralFormatter(val);\n        }; // formatter function will always overwrite format property\n\n\n        if (w.config.xaxis.labels.formatter !== undefined) {\n          w.globals.xLabelFormatter = w.config.xaxis.labels.formatter;\n        } else {\n          w.globals.xLabelFormatter = function (val) {\n            if (Utils$1.isNumber(val)) {\n              if (!w.config.xaxis.convertedCatToNumeric && w.config.xaxis.type === 'numeric') {\n                if (Utils$1.isNumber(w.config.xaxis.decimalsInFloat)) {\n                  return val.toFixed(w.config.xaxis.decimalsInFloat);\n                } else {\n                  var diff = w.globals.maxX - w.globals.minX;\n\n                  if (diff > 0 && diff < 100) {\n                    return val.toFixed(1);\n                  }\n\n                  return val.toFixed(0);\n                }\n              }\n\n              if (w.globals.isBarHorizontal) {\n                var range = w.globals.maxY - w.globals.minYArr;\n\n                if (range < 4) {\n                  return val.toFixed(1);\n                }\n              }\n\n              return val.toFixed(0);\n            }\n\n            return val;\n          };\n        }\n\n        if (typeof w.config.tooltip.x.formatter === 'function') {\n          w.globals.ttKeyFormatter = w.config.tooltip.x.formatter;\n        } else {\n          w.globals.ttKeyFormatter = w.globals.xLabelFormatter;\n        }\n\n        if (typeof w.config.xaxis.tooltip.formatter === 'function') {\n          w.globals.xaxisTooltipFormatter = w.config.xaxis.tooltip.formatter;\n        }\n\n        if (Array.isArray(w.config.tooltip.y)) {\n          w.globals.ttVal = w.config.tooltip.y;\n        } else {\n          if (w.config.tooltip.y.formatter !== undefined) {\n            w.globals.ttVal = w.config.tooltip.y;\n          }\n        }\n\n        if (w.config.tooltip.z.formatter !== undefined) {\n          w.globals.ttZFormatter = w.config.tooltip.z.formatter;\n        } // legend formatter - if user wants to append any global values of series to legend text\n\n\n        if (w.config.legend.formatter !== undefined) {\n          w.globals.legendFormatter = w.config.legend.formatter;\n        } // formatter function will always overwrite format property\n\n\n        w.config.yaxis.forEach(function (yaxe, i) {\n          if (yaxe.labels.formatter !== undefined) {\n            w.globals.yLabelFormatters[i] = yaxe.labels.formatter;\n          } else {\n            w.globals.yLabelFormatters[i] = function (val) {\n              if (!w.globals.xyCharts) return val;\n\n              if (Array.isArray(val)) {\n                return val.map(function (v) {\n                  return _this.defaultYFormatter(v, yaxe, i);\n                });\n              } else {\n                return _this.defaultYFormatter(val, yaxe, i);\n              }\n            };\n          }\n        });\n        return w.globals;\n      }\n    }, {\n      key: \"heatmapLabelFormatters\",\n      value: function heatmapLabelFormatters() {\n        var w = this.w;\n\n        if (w.config.chart.type === 'heatmap') {\n          w.globals.yAxisScale[0].result = w.globals.seriesNames.slice(); //  get the longest string from the labels array and also apply label formatter to it\n\n          var longest = w.globals.seriesNames.reduce(function (a, b) {\n            return a.length > b.length ? a : b;\n          }, 0);\n          w.globals.yAxisScale[0].niceMax = longest;\n          w.globals.yAxisScale[0].niceMin = longest;\n        }\n      }\n    }]);\n\n    return Formatters;\n  }();\n\n  /**\n   * ApexCharts Default Class for setting default options for all chart types.\n   *\n   * @module Defaults\n   **/\n\n  var getRangeValues = function getRangeValues(_ref) {\n    var _w$config$series$seri;\n\n    var isTimeline = _ref.isTimeline,\n        ctx = _ref.ctx,\n        seriesIndex = _ref.seriesIndex,\n        dataPointIndex = _ref.dataPointIndex,\n        y1 = _ref.y1,\n        y2 = _ref.y2,\n        w = _ref.w;\n    var start = w.globals.seriesRangeStart[seriesIndex][dataPointIndex];\n    var end = w.globals.seriesRangeEnd[seriesIndex][dataPointIndex];\n    var ylabel = w.globals.labels[dataPointIndex];\n    var seriesName = w.config.series[seriesIndex].name ? w.config.series[seriesIndex].name : '';\n    var yLbFormatter = w.globals.ttKeyFormatter;\n    var yLbTitleFormatter = w.config.tooltip.y.title.formatter;\n    var opts = {\n      w: w,\n      seriesIndex: seriesIndex,\n      dataPointIndex: dataPointIndex,\n      start: start,\n      end: end\n    };\n\n    if (typeof yLbTitleFormatter === 'function') {\n      seriesName = yLbTitleFormatter(seriesName, opts);\n    }\n\n    if ((_w$config$series$seri = w.config.series[seriesIndex].data[dataPointIndex]) !== null && _w$config$series$seri !== void 0 && _w$config$series$seri.x) {\n      ylabel = w.config.series[seriesIndex].data[dataPointIndex].x;\n    }\n\n    if (!isTimeline) {\n      if (w.config.xaxis.type === 'datetime') {\n        var xFormat = new Formatters(ctx);\n        ylabel = xFormat.xLabelFormat(w.globals.ttKeyFormatter, ylabel, ylabel, {\n          i: undefined,\n          dateFormatter: new DateTime(ctx).formatDate,\n          w: w\n        });\n      }\n    }\n\n    if (typeof yLbFormatter === 'function') {\n      ylabel = yLbFormatter(ylabel, opts);\n    }\n\n    if (Number.isFinite(y1) && Number.isFinite(y2)) {\n      start = y1;\n      end = y2;\n    }\n\n    var startVal = '';\n    var endVal = '';\n    var color = w.globals.colors[seriesIndex];\n\n    if (w.config.tooltip.x.formatter === undefined) {\n      if (w.config.xaxis.type === 'datetime') {\n        var datetimeObj = new DateTime(ctx);\n        startVal = datetimeObj.formatDate(datetimeObj.getDate(start), w.config.tooltip.x.format);\n        endVal = datetimeObj.formatDate(datetimeObj.getDate(end), w.config.tooltip.x.format);\n      } else {\n        startVal = start;\n        endVal = end;\n      }\n    } else {\n      startVal = w.config.tooltip.x.formatter(start);\n      endVal = w.config.tooltip.x.formatter(end);\n    }\n\n    return {\n      start: start,\n      end: end,\n      startVal: startVal,\n      endVal: endVal,\n      ylabel: ylabel,\n      color: color,\n      seriesName: seriesName\n    };\n  };\n\n  var buildRangeTooltipHTML = function buildRangeTooltipHTML(opts) {\n    var color = opts.color,\n        seriesName = opts.seriesName,\n        ylabel = opts.ylabel,\n        start = opts.start,\n        end = opts.end,\n        seriesIndex = opts.seriesIndex,\n        dataPointIndex = opts.dataPointIndex;\n    var formatter = opts.ctx.tooltip.tooltipLabels.getFormatters(seriesIndex);\n    start = formatter.yLbFormatter(start);\n    end = formatter.yLbFormatter(end);\n    var val = formatter.yLbFormatter(opts.w.globals.series[seriesIndex][dataPointIndex]);\n    var valueHTML = '';\n    var rangeValues = \"<span class=\\\"value start-value\\\">\\n  \".concat(start, \"\\n  </span> <span class=\\\"separator\\\">-</span> <span class=\\\"value end-value\\\">\\n  \").concat(end, \"\\n  </span>\");\n\n    if (opts.w.globals.comboCharts) {\n      if (opts.w.config.series[seriesIndex].type === 'rangeArea' || opts.w.config.series[seriesIndex].type === 'rangeBar') {\n        valueHTML = rangeValues;\n      } else {\n        valueHTML = \"<span>\".concat(val, \"</span>\");\n      }\n    } else {\n      valueHTML = rangeValues;\n    }\n\n    return '<div class=\"apexcharts-tooltip-rangebar\">' + '<div> <span class=\"series-name\" style=\"color: ' + color + '\">' + (seriesName ? seriesName : '') + '</span></div>' + '<div> <span class=\"category\">' + ylabel + ': </span> ' + valueHTML + ' </div>' + '</div>';\n  };\n\n  var Defaults = /*#__PURE__*/function () {\n    function Defaults(opts) {\n      _classCallCheck(this, Defaults);\n\n      this.opts = opts;\n    }\n\n    _createClass(Defaults, [{\n      key: \"line\",\n      value: function line() {\n        return {\n          chart: {\n            animations: {\n              easing: 'swing'\n            }\n          },\n          dataLabels: {\n            enabled: false\n          },\n          stroke: {\n            width: 5,\n            curve: 'straight'\n          },\n          markers: {\n            size: 0,\n            hover: {\n              sizeOffset: 6\n            }\n          },\n          xaxis: {\n            crosshairs: {\n              width: 1\n            }\n          }\n        };\n      }\n    }, {\n      key: \"sparkline\",\n      value: function sparkline(defaults) {\n        this.opts.yaxis[0].show = false;\n        this.opts.yaxis[0].title.text = '';\n        this.opts.yaxis[0].axisBorder.show = false;\n        this.opts.yaxis[0].axisTicks.show = false;\n        this.opts.yaxis[0].floating = true;\n        var ret = {\n          grid: {\n            show: false,\n            padding: {\n              left: 0,\n              right: 0,\n              top: 0,\n              bottom: 0\n            }\n          },\n          legend: {\n            show: false\n          },\n          xaxis: {\n            labels: {\n              show: false\n            },\n            tooltip: {\n              enabled: false\n            },\n            axisBorder: {\n              show: false\n            },\n            axisTicks: {\n              show: false\n            }\n          },\n          chart: {\n            toolbar: {\n              show: false\n            },\n            zoom: {\n              enabled: false\n            }\n          },\n          dataLabels: {\n            enabled: false\n          }\n        };\n        return Utils$1.extend(defaults, ret);\n      }\n    }, {\n      key: \"bar\",\n      value: function bar() {\n        return {\n          chart: {\n            stacked: false,\n            animations: {\n              easing: 'swing'\n            }\n          },\n          plotOptions: {\n            bar: {\n              dataLabels: {\n                position: 'center'\n              }\n            }\n          },\n          dataLabels: {\n            style: {\n              colors: ['#fff']\n            },\n            background: {\n              enabled: false\n            }\n          },\n          stroke: {\n            width: 0,\n            lineCap: 'round'\n          },\n          fill: {\n            opacity: 0.85\n          },\n          legend: {\n            markers: {\n              shape: 'square',\n              radius: 2,\n              size: 8\n            }\n          },\n          tooltip: {\n            shared: false,\n            intersect: true\n          },\n          xaxis: {\n            tooltip: {\n              enabled: false\n            },\n            tickPlacement: 'between',\n            crosshairs: {\n              width: 'barWidth',\n              position: 'back',\n              fill: {\n                type: 'gradient'\n              },\n              dropShadow: {\n                enabled: false\n              },\n              stroke: {\n                width: 0\n              }\n            }\n          }\n        };\n      }\n    }, {\n      key: \"candlestick\",\n      value: function candlestick() {\n        var _this = this;\n\n        return {\n          stroke: {\n            width: 1,\n            colors: ['#333']\n          },\n          fill: {\n            opacity: 1\n          },\n          dataLabels: {\n            enabled: false\n          },\n          tooltip: {\n            shared: true,\n            custom: function custom(_ref2) {\n              var seriesIndex = _ref2.seriesIndex,\n                  dataPointIndex = _ref2.dataPointIndex,\n                  w = _ref2.w;\n              return _this._getBoxTooltip(w, seriesIndex, dataPointIndex, ['Open', 'High', '', 'Low', 'Close'], 'candlestick');\n            }\n          },\n          states: {\n            active: {\n              filter: {\n                type: 'none'\n              }\n            }\n          },\n          xaxis: {\n            crosshairs: {\n              width: 1\n            }\n          }\n        };\n      }\n    }, {\n      key: \"boxPlot\",\n      value: function boxPlot() {\n        var _this2 = this;\n\n        return {\n          chart: {\n            animations: {\n              dynamicAnimation: {\n                enabled: false\n              }\n            }\n          },\n          stroke: {\n            width: 1,\n            colors: ['#24292e']\n          },\n          dataLabels: {\n            enabled: false\n          },\n          tooltip: {\n            shared: true,\n            custom: function custom(_ref3) {\n              var seriesIndex = _ref3.seriesIndex,\n                  dataPointIndex = _ref3.dataPointIndex,\n                  w = _ref3.w;\n              return _this2._getBoxTooltip(w, seriesIndex, dataPointIndex, ['Minimum', 'Q1', 'Median', 'Q3', 'Maximum'], 'boxPlot');\n            }\n          },\n          markers: {\n            size: 5,\n            strokeWidth: 1,\n            strokeColors: '#111'\n          },\n          xaxis: {\n            crosshairs: {\n              width: 1\n            }\n          }\n        };\n      }\n    }, {\n      key: \"rangeBar\",\n      value: function rangeBar() {\n        var handleTimelineTooltip = function handleTimelineTooltip(opts) {\n          var _getRangeValues = getRangeValues(_objectSpread2(_objectSpread2({}, opts), {}, {\n            isTimeline: true\n          })),\n              color = _getRangeValues.color,\n              seriesName = _getRangeValues.seriesName,\n              ylabel = _getRangeValues.ylabel,\n              startVal = _getRangeValues.startVal,\n              endVal = _getRangeValues.endVal;\n\n          return buildRangeTooltipHTML(_objectSpread2(_objectSpread2({}, opts), {}, {\n            color: color,\n            seriesName: seriesName,\n            ylabel: ylabel,\n            start: startVal,\n            end: endVal\n          }));\n        };\n\n        var handleRangeColumnTooltip = function handleRangeColumnTooltip(opts) {\n          var _getRangeValues2 = getRangeValues(opts),\n              color = _getRangeValues2.color,\n              seriesName = _getRangeValues2.seriesName,\n              ylabel = _getRangeValues2.ylabel,\n              start = _getRangeValues2.start,\n              end = _getRangeValues2.end;\n\n          return buildRangeTooltipHTML(_objectSpread2(_objectSpread2({}, opts), {}, {\n            color: color,\n            seriesName: seriesName,\n            ylabel: ylabel,\n            start: start,\n            end: end\n          }));\n        };\n\n        return {\n          stroke: {\n            width: 0,\n            lineCap: 'square'\n          },\n          plotOptions: {\n            bar: {\n              borderRadius: 0,\n              dataLabels: {\n                position: 'center'\n              }\n            }\n          },\n          dataLabels: {\n            enabled: false,\n            formatter: function formatter(val, _ref4) {\n              _ref4.ctx;\n                  var seriesIndex = _ref4.seriesIndex,\n                  dataPointIndex = _ref4.dataPointIndex,\n                  w = _ref4.w;\n\n              var getVal = function getVal() {\n                var start = w.globals.seriesRangeStart[seriesIndex][dataPointIndex];\n                var end = w.globals.seriesRangeEnd[seriesIndex][dataPointIndex];\n                return end - start;\n              };\n\n              if (w.globals.comboCharts) {\n                if (w.config.series[seriesIndex].type === 'rangeBar' || w.config.series[seriesIndex].type === 'rangeArea') {\n                  return getVal();\n                } else {\n                  return val;\n                }\n              } else {\n                return getVal();\n              }\n            },\n            background: {\n              enabled: false\n            },\n            style: {\n              colors: ['#fff']\n            }\n          },\n          tooltip: {\n            shared: false,\n            followCursor: true,\n            custom: function custom(opts) {\n              if (opts.w.config.plotOptions && opts.w.config.plotOptions.bar && opts.w.config.plotOptions.bar.horizontal) {\n                return handleTimelineTooltip(opts);\n              } else {\n                return handleRangeColumnTooltip(opts);\n              }\n            }\n          },\n          xaxis: {\n            tickPlacement: 'between',\n            tooltip: {\n              enabled: false\n            },\n            crosshairs: {\n              stroke: {\n                width: 0\n              }\n            }\n          }\n        };\n      }\n    }, {\n      key: \"area\",\n      value: function area() {\n        return {\n          stroke: {\n            width: 4,\n            fill: {\n              type: 'solid',\n              gradient: {\n                inverseColors: false,\n                shade: 'light',\n                type: 'vertical',\n                opacityFrom: 0.65,\n                opacityTo: 0.5,\n                stops: [0, 100, 100]\n              }\n            }\n          },\n          fill: {\n            type: 'gradient',\n            gradient: {\n              inverseColors: false,\n              shade: 'light',\n              type: 'vertical',\n              opacityFrom: 0.65,\n              opacityTo: 0.5,\n              stops: [0, 100, 100]\n            }\n          },\n          markers: {\n            size: 0,\n            hover: {\n              sizeOffset: 6\n            }\n          },\n          tooltip: {\n            followCursor: false\n          }\n        };\n      }\n    }, {\n      key: \"rangeArea\",\n      value: function rangeArea() {\n        var handleRangeAreaTooltip = function handleRangeAreaTooltip(opts) {\n          var _getRangeValues3 = getRangeValues(opts),\n              color = _getRangeValues3.color,\n              seriesName = _getRangeValues3.seriesName,\n              ylabel = _getRangeValues3.ylabel,\n              start = _getRangeValues3.start,\n              end = _getRangeValues3.end;\n\n          return buildRangeTooltipHTML(_objectSpread2(_objectSpread2({}, opts), {}, {\n            color: color,\n            seriesName: seriesName,\n            ylabel: ylabel,\n            start: start,\n            end: end\n          }));\n        };\n\n        return {\n          stroke: {\n            curve: 'straight',\n            width: 0\n          },\n          fill: {\n            type: 'solid',\n            opacity: 0.6\n          },\n          markers: {\n            size: 0\n          },\n          states: {\n            hover: {\n              filter: {\n                type: 'none'\n              }\n            },\n            active: {\n              filter: {\n                type: 'none'\n              }\n            }\n          },\n          tooltip: {\n            intersect: false,\n            shared: true,\n            followCursor: true,\n            custom: function custom(opts) {\n              return handleRangeAreaTooltip(opts);\n            }\n          }\n        };\n      }\n    }, {\n      key: \"brush\",\n      value: function brush(defaults) {\n        var ret = {\n          chart: {\n            toolbar: {\n              autoSelected: 'selection',\n              show: false\n            },\n            zoom: {\n              enabled: false\n            }\n          },\n          dataLabels: {\n            enabled: false\n          },\n          stroke: {\n            width: 1\n          },\n          tooltip: {\n            enabled: false\n          },\n          xaxis: {\n            tooltip: {\n              enabled: false\n            }\n          }\n        };\n        return Utils$1.extend(defaults, ret);\n      }\n    }, {\n      key: \"stacked100\",\n      value: function stacked100(opts) {\n        opts.dataLabels = opts.dataLabels || {};\n        opts.dataLabels.formatter = opts.dataLabels.formatter || undefined;\n        var existingDataLabelFormatter = opts.dataLabels.formatter;\n        opts.yaxis.forEach(function (yaxe, index) {\n          opts.yaxis[index].min = 0;\n          opts.yaxis[index].max = 100;\n        });\n        var isBar = opts.chart.type === 'bar';\n\n        if (isBar) {\n          opts.dataLabels.formatter = existingDataLabelFormatter || function (val) {\n            if (typeof val === 'number') {\n              return val ? val.toFixed(0) + '%' : val;\n            }\n\n            return val;\n          };\n        }\n\n        return opts;\n      }\n    }, {\n      key: \"stackedBars\",\n      value: function stackedBars() {\n        var barDefaults = this.bar();\n        return _objectSpread2(_objectSpread2({}, barDefaults), {}, {\n          plotOptions: _objectSpread2(_objectSpread2({}, barDefaults.plotOptions), {}, {\n            bar: _objectSpread2(_objectSpread2({}, barDefaults.plotOptions.bar), {}, {\n              borderRadiusApplication: 'end',\n              borderRadiusWhenStacked: 'last'\n            })\n          })\n        });\n      } // This function removes the left and right spacing in chart for line/area/scatter if xaxis type = category for those charts by converting xaxis = numeric. Numeric/Datetime xaxis prevents the unnecessary spacing in the left/right of the chart area\n\n    }, {\n      key: \"convertCatToNumeric\",\n      value: function convertCatToNumeric(opts) {\n        opts.xaxis.convertedCatToNumeric = true;\n        return opts;\n      }\n    }, {\n      key: \"convertCatToNumericXaxis\",\n      value: function convertCatToNumericXaxis(opts, ctx, cats) {\n        opts.xaxis.type = 'numeric';\n        opts.xaxis.labels = opts.xaxis.labels || {};\n\n        opts.xaxis.labels.formatter = opts.xaxis.labels.formatter || function (val) {\n          return Utils$1.isNumber(val) ? Math.floor(val) : val;\n        };\n\n        var defaultFormatter = opts.xaxis.labels.formatter;\n        var labels = opts.xaxis.categories && opts.xaxis.categories.length ? opts.xaxis.categories : opts.labels;\n\n        if (cats && cats.length) {\n          labels = cats.map(function (c) {\n            return Array.isArray(c) ? c : String(c);\n          });\n        }\n\n        if (labels && labels.length) {\n          opts.xaxis.labels.formatter = function (val) {\n            return Utils$1.isNumber(val) ? defaultFormatter(labels[Math.floor(val) - 1]) : defaultFormatter(val);\n          };\n        }\n\n        opts.xaxis.categories = [];\n        opts.labels = [];\n        opts.xaxis.tickAmount = opts.xaxis.tickAmount || 'dataPoints';\n        return opts;\n      }\n    }, {\n      key: \"bubble\",\n      value: function bubble() {\n        return {\n          dataLabels: {\n            style: {\n              colors: ['#fff']\n            }\n          },\n          tooltip: {\n            shared: false,\n            intersect: true\n          },\n          xaxis: {\n            crosshairs: {\n              width: 0\n            }\n          },\n          fill: {\n            type: 'solid',\n            gradient: {\n              shade: 'light',\n              inverse: true,\n              shadeIntensity: 0.55,\n              opacityFrom: 0.4,\n              opacityTo: 0.8\n            }\n          }\n        };\n      }\n    }, {\n      key: \"scatter\",\n      value: function scatter() {\n        return {\n          dataLabels: {\n            enabled: false\n          },\n          tooltip: {\n            shared: false,\n            intersect: true\n          },\n          markers: {\n            size: 6,\n            strokeWidth: 1,\n            hover: {\n              sizeOffset: 2\n            }\n          }\n        };\n      }\n    }, {\n      key: \"heatmap\",\n      value: function heatmap() {\n        return {\n          chart: {\n            stacked: false\n          },\n          fill: {\n            opacity: 1\n          },\n          dataLabels: {\n            style: {\n              colors: ['#fff']\n            }\n          },\n          stroke: {\n            colors: ['#fff']\n          },\n          tooltip: {\n            followCursor: true,\n            marker: {\n              show: false\n            },\n            x: {\n              show: false\n            }\n          },\n          legend: {\n            position: 'top',\n            markers: {\n              shape: 'square',\n              size: 10,\n              offsetY: 2\n            }\n          },\n          grid: {\n            padding: {\n              right: 20\n            }\n          }\n        };\n      }\n    }, {\n      key: \"treemap\",\n      value: function treemap() {\n        return {\n          chart: {\n            zoom: {\n              enabled: false\n            }\n          },\n          dataLabels: {\n            style: {\n              fontSize: 14,\n              fontWeight: 600,\n              colors: ['#fff']\n            }\n          },\n          stroke: {\n            show: true,\n            width: 2,\n            colors: ['#fff']\n          },\n          legend: {\n            show: false\n          },\n          fill: {\n            gradient: {\n              stops: [0, 100]\n            }\n          },\n          tooltip: {\n            followCursor: true,\n            x: {\n              show: false\n            }\n          },\n          grid: {\n            padding: {\n              left: 0,\n              right: 0\n            }\n          },\n          xaxis: {\n            crosshairs: {\n              show: false\n            },\n            tooltip: {\n              enabled: false\n            }\n          }\n        };\n      }\n    }, {\n      key: \"pie\",\n      value: function pie() {\n        return {\n          chart: {\n            toolbar: {\n              show: false\n            }\n          },\n          plotOptions: {\n            pie: {\n              donut: {\n                labels: {\n                  show: false\n                }\n              }\n            }\n          },\n          dataLabels: {\n            formatter: function formatter(val) {\n              return val.toFixed(1) + '%';\n            },\n            style: {\n              colors: ['#fff']\n            },\n            background: {\n              enabled: false\n            },\n            dropShadow: {\n              enabled: true\n            }\n          },\n          stroke: {\n            colors: ['#fff']\n          },\n          fill: {\n            opacity: 1,\n            gradient: {\n              shade: 'light',\n              stops: [0, 100]\n            }\n          },\n          tooltip: {\n            theme: 'dark',\n            fillSeriesColor: true\n          },\n          legend: {\n            position: 'right'\n          }\n        };\n      }\n    }, {\n      key: \"donut\",\n      value: function donut() {\n        return {\n          chart: {\n            toolbar: {\n              show: false\n            }\n          },\n          dataLabels: {\n            formatter: function formatter(val) {\n              return val.toFixed(1) + '%';\n            },\n            style: {\n              colors: ['#fff']\n            },\n            background: {\n              enabled: false\n            },\n            dropShadow: {\n              enabled: true\n            }\n          },\n          stroke: {\n            colors: ['#fff']\n          },\n          fill: {\n            opacity: 1,\n            gradient: {\n              shade: 'light',\n              shadeIntensity: 0.35,\n              stops: [80, 100],\n              opacityFrom: 1,\n              opacityTo: 1\n            }\n          },\n          tooltip: {\n            theme: 'dark',\n            fillSeriesColor: true\n          },\n          legend: {\n            position: 'right'\n          }\n        };\n      }\n    }, {\n      key: \"polarArea\",\n      value: function polarArea() {\n        this.opts.yaxis[0].tickAmount = this.opts.yaxis[0].tickAmount ? this.opts.yaxis[0].tickAmount : 6;\n        return {\n          chart: {\n            toolbar: {\n              show: false\n            }\n          },\n          dataLabels: {\n            formatter: function formatter(val) {\n              return val.toFixed(1) + '%';\n            },\n            enabled: false\n          },\n          stroke: {\n            show: true,\n            width: 2\n          },\n          fill: {\n            opacity: 0.7\n          },\n          tooltip: {\n            theme: 'dark',\n            fillSeriesColor: true\n          },\n          legend: {\n            position: 'right'\n          }\n        };\n      }\n    }, {\n      key: \"radar\",\n      value: function radar() {\n        this.opts.yaxis[0].labels.offsetY = this.opts.yaxis[0].labels.offsetY ? this.opts.yaxis[0].labels.offsetY : 6;\n        return {\n          dataLabels: {\n            enabled: false,\n            style: {\n              fontSize: '11px'\n            }\n          },\n          stroke: {\n            width: 2\n          },\n          markers: {\n            size: 3,\n            strokeWidth: 1,\n            strokeOpacity: 1\n          },\n          fill: {\n            opacity: 0.2\n          },\n          tooltip: {\n            shared: false,\n            intersect: true,\n            followCursor: true\n          },\n          grid: {\n            show: false\n          },\n          xaxis: {\n            labels: {\n              formatter: function formatter(val) {\n                return val;\n              },\n              style: {\n                colors: ['#a8a8a8'],\n                fontSize: '11px'\n              }\n            },\n            tooltip: {\n              enabled: false\n            },\n            crosshairs: {\n              show: false\n            }\n          }\n        };\n      }\n    }, {\n      key: \"radialBar\",\n      value: function radialBar() {\n        return {\n          chart: {\n            animations: {\n              dynamicAnimation: {\n                enabled: true,\n                speed: 800\n              }\n            },\n            toolbar: {\n              show: false\n            }\n          },\n          fill: {\n            gradient: {\n              shade: 'dark',\n              shadeIntensity: 0.4,\n              inverseColors: false,\n              type: 'diagonal2',\n              opacityFrom: 1,\n              opacityTo: 1,\n              stops: [70, 98, 100]\n            }\n          },\n          legend: {\n            show: false,\n            position: 'right'\n          },\n          tooltip: {\n            enabled: false,\n            fillSeriesColor: true\n          }\n        };\n      }\n    }, {\n      key: \"_getBoxTooltip\",\n      value: function _getBoxTooltip(w, seriesIndex, dataPointIndex, labels, chartType) {\n        var o = w.globals.seriesCandleO[seriesIndex][dataPointIndex];\n        var h = w.globals.seriesCandleH[seriesIndex][dataPointIndex];\n        var m = w.globals.seriesCandleM[seriesIndex][dataPointIndex];\n        var l = w.globals.seriesCandleL[seriesIndex][dataPointIndex];\n        var c = w.globals.seriesCandleC[seriesIndex][dataPointIndex];\n\n        if (w.config.series[seriesIndex].type && w.config.series[seriesIndex].type !== chartType) {\n          return \"<div class=\\\"apexcharts-custom-tooltip\\\">\\n          \".concat(w.config.series[seriesIndex].name ? w.config.series[seriesIndex].name : 'series-' + (seriesIndex + 1), \": <strong>\").concat(w.globals.series[seriesIndex][dataPointIndex], \"</strong>\\n        </div>\");\n        } else {\n          return \"<div class=\\\"apexcharts-tooltip-box apexcharts-tooltip-\".concat(w.config.chart.type, \"\\\">\") + \"<div>\".concat(labels[0], \": <span class=\\\"value\\\">\") + o + '</span></div>' + \"<div>\".concat(labels[1], \": <span class=\\\"value\\\">\") + h + '</span></div>' + (m ? \"<div>\".concat(labels[2], \": <span class=\\\"value\\\">\") + m + '</span></div>' : '') + \"<div>\".concat(labels[3], \": <span class=\\\"value\\\">\") + l + '</span></div>' + \"<div>\".concat(labels[4], \": <span class=\\\"value\\\">\") + c + '</span></div>' + '</div>';\n        }\n      }\n    }]);\n\n    return Defaults;\n  }();\n\n  /**\n   * ApexCharts Config Class for extending user options with pre-defined ApexCharts config.\n   *\n   * @module Config\n   **/\n\n  var Config = /*#__PURE__*/function () {\n    function Config(opts) {\n      _classCallCheck(this, Config);\n\n      this.opts = opts;\n    }\n\n    _createClass(Config, [{\n      key: \"init\",\n      value: function init(_ref) {\n        var responsiveOverride = _ref.responsiveOverride;\n        var opts = this.opts;\n        var options = new Options();\n        var defaults = new Defaults(opts);\n        this.chartType = opts.chart.type;\n        opts = this.extendYAxis(opts);\n        opts = this.extendAnnotations(opts);\n        var config = options.init();\n        var newDefaults = {};\n\n        if (opts && _typeof(opts) === 'object') {\n          var chartDefaults = {};\n          var chartTypes = ['line', 'area', 'bar', 'candlestick', 'boxPlot', 'rangeBar', 'rangeArea', 'bubble', 'scatter', 'heatmap', 'treemap', 'pie', 'polarArea', 'donut', 'radar', 'radialBar'];\n\n          if (chartTypes.indexOf(opts.chart.type) !== -1) {\n            chartDefaults = defaults[opts.chart.type]();\n          } else {\n            chartDefaults = defaults.line();\n          }\n\n          if (opts.chart.stacked && opts.chart.type === 'bar') {\n            chartDefaults = defaults.stackedBars();\n          }\n\n          if (opts.chart.brush && opts.chart.brush.enabled) {\n            chartDefaults = defaults.brush(chartDefaults);\n          }\n\n          if (opts.chart.stacked && opts.chart.stackType === '100%') {\n            opts = defaults.stacked100(opts);\n          } // If user has specified a dark theme, make the tooltip dark too\n\n\n          this.checkForDarkTheme(window.Apex); // check global window Apex options\n\n          this.checkForDarkTheme(opts); // check locally passed options\n\n          opts.xaxis = opts.xaxis || window.Apex.xaxis || {}; // an important boolean needs to be set here\n          // otherwise all the charts will have this flag set to true window.Apex.xaxis is set globally\n\n          if (!responsiveOverride) {\n            opts.xaxis.convertedCatToNumeric = false;\n          }\n\n          opts = this.checkForCatToNumericXAxis(this.chartType, chartDefaults, opts);\n\n          if (opts.chart.sparkline && opts.chart.sparkline.enabled || window.Apex.chart && window.Apex.chart.sparkline && window.Apex.chart.sparkline.enabled) {\n            chartDefaults = defaults.sparkline(chartDefaults);\n          }\n\n          newDefaults = Utils$1.extend(config, chartDefaults);\n        } // config should cascade in this fashion\n        // default-config < global-apex-variable-config < user-defined-config\n        // get GLOBALLY defined options and merge with the default config\n\n\n        var mergedWithDefaultConfig = Utils$1.extend(newDefaults, window.Apex); // get the merged config and extend with user defined config\n\n        config = Utils$1.extend(mergedWithDefaultConfig, opts); // some features are not supported. those mismatches should be handled\n\n        config = this.handleUserInputErrors(config);\n        return config;\n      }\n    }, {\n      key: \"checkForCatToNumericXAxis\",\n      value: function checkForCatToNumericXAxis(chartType, chartDefaults, opts) {\n        var defaults = new Defaults(opts);\n        var isBarHorizontal = (chartType === 'bar' || chartType === 'boxPlot') && opts.plotOptions && opts.plotOptions.bar && opts.plotOptions.bar.horizontal;\n        var unsupportedZoom = chartType === 'pie' || chartType === 'polarArea' || chartType === 'donut' || chartType === 'radar' || chartType === 'radialBar' || chartType === 'heatmap';\n        var notNumericXAxis = opts.xaxis.type !== 'datetime' && opts.xaxis.type !== 'numeric';\n        var tickPlacement = opts.xaxis.tickPlacement ? opts.xaxis.tickPlacement : chartDefaults.xaxis && chartDefaults.xaxis.tickPlacement;\n\n        if (!isBarHorizontal && !unsupportedZoom && notNumericXAxis && tickPlacement !== 'between') {\n          opts = defaults.convertCatToNumeric(opts);\n        }\n\n        return opts;\n      }\n    }, {\n      key: \"extendYAxis\",\n      value: function extendYAxis(opts, w) {\n        var options = new Options();\n\n        if (typeof opts.yaxis === 'undefined' || !opts.yaxis || Array.isArray(opts.yaxis) && opts.yaxis.length === 0) {\n          opts.yaxis = {};\n        } // extend global yaxis config (only if object is provided / not an array)\n\n\n        if (opts.yaxis.constructor !== Array && window.Apex.yaxis && window.Apex.yaxis.constructor !== Array) {\n          opts.yaxis = Utils$1.extend(opts.yaxis, window.Apex.yaxis);\n        } // as we can't extend nested object's array with extend, we need to do it first\n        // user can provide either an array or object in yaxis config\n\n\n        if (opts.yaxis.constructor !== Array) {\n          // convert the yaxis to array if user supplied object\n          opts.yaxis = [Utils$1.extend(options.yAxis, opts.yaxis)];\n        } else {\n          opts.yaxis = Utils$1.extendArray(opts.yaxis, options.yAxis);\n        }\n\n        var isLogY = false;\n        opts.yaxis.forEach(function (y) {\n          if (y.logarithmic) {\n            isLogY = true;\n          }\n        });\n        var series = opts.series;\n\n        if (w && !series) {\n          series = w.config.series;\n        } // A logarithmic chart works correctly when each series has a corresponding y-axis\n        // If this is not the case, we manually create yaxis for multi-series log chart\n\n\n        if (isLogY && series.length !== opts.yaxis.length && series.length) {\n          opts.yaxis = series.map(function (s, i) {\n            if (!s.name) {\n              series[i].name = \"series-\".concat(i + 1);\n            }\n\n            if (opts.yaxis[i]) {\n              opts.yaxis[i].seriesName = series[i].name;\n              return opts.yaxis[i];\n            } else {\n              var newYaxis = Utils$1.extend(options.yAxis, opts.yaxis[0]);\n              newYaxis.show = false;\n              return newYaxis;\n            }\n          });\n        }\n\n        if (isLogY && series.length > 1 && series.length !== opts.yaxis.length) {\n          console.warn('A multi-series logarithmic chart should have equal number of series and y-axes. Please make sure to equalize both.');\n        }\n\n        return opts;\n      } // annotations also accepts array, so we need to extend them manually\n\n    }, {\n      key: \"extendAnnotations\",\n      value: function extendAnnotations(opts) {\n        if (typeof opts.annotations === 'undefined') {\n          opts.annotations = {};\n          opts.annotations.yaxis = [];\n          opts.annotations.xaxis = [];\n          opts.annotations.points = [];\n        }\n\n        opts = this.extendYAxisAnnotations(opts);\n        opts = this.extendXAxisAnnotations(opts);\n        opts = this.extendPointAnnotations(opts);\n        return opts;\n      }\n    }, {\n      key: \"extendYAxisAnnotations\",\n      value: function extendYAxisAnnotations(opts) {\n        var options = new Options();\n        opts.annotations.yaxis = Utils$1.extendArray(typeof opts.annotations.yaxis !== 'undefined' ? opts.annotations.yaxis : [], options.yAxisAnnotation);\n        return opts;\n      }\n    }, {\n      key: \"extendXAxisAnnotations\",\n      value: function extendXAxisAnnotations(opts) {\n        var options = new Options();\n        opts.annotations.xaxis = Utils$1.extendArray(typeof opts.annotations.xaxis !== 'undefined' ? opts.annotations.xaxis : [], options.xAxisAnnotation);\n        return opts;\n      }\n    }, {\n      key: \"extendPointAnnotations\",\n      value: function extendPointAnnotations(opts) {\n        var options = new Options();\n        opts.annotations.points = Utils$1.extendArray(typeof opts.annotations.points !== 'undefined' ? opts.annotations.points : [], options.pointAnnotation);\n        return opts;\n      }\n    }, {\n      key: \"checkForDarkTheme\",\n      value: function checkForDarkTheme(opts) {\n        if (opts.theme && opts.theme.mode === 'dark') {\n          if (!opts.tooltip) {\n            opts.tooltip = {};\n          }\n\n          if (opts.tooltip.theme !== 'light') {\n            opts.tooltip.theme = 'dark';\n          }\n\n          if (!opts.chart.foreColor) {\n            opts.chart.foreColor = '#f6f7f8';\n          }\n\n          if (!opts.chart.background) {\n            opts.chart.background = '#424242';\n          }\n\n          if (!opts.theme.palette) {\n            opts.theme.palette = 'palette4';\n          }\n        }\n      }\n    }, {\n      key: \"handleUserInputErrors\",\n      value: function handleUserInputErrors(opts) {\n        var config = opts; // conflicting tooltip option. intersect makes sure to focus on 1 point at a time. Shared cannot be used along with it\n\n        if (config.tooltip.shared && config.tooltip.intersect) {\n          throw new Error('tooltip.shared cannot be enabled when tooltip.intersect is true. Turn off any other option by setting it to false.');\n        }\n\n        if (config.chart.type === 'bar' && config.plotOptions.bar.horizontal) {\n          // No multiple yaxis for bars\n          if (config.yaxis.length > 1) {\n            throw new Error('Multiple Y Axis for bars are not supported. Switch to column chart by setting plotOptions.bar.horizontal=false');\n          } // if yaxis is reversed in horizontal bar chart, you should draw the y-axis on right side\n\n\n          if (config.yaxis[0].reversed) {\n            config.yaxis[0].opposite = true;\n          }\n\n          config.xaxis.tooltip.enabled = false; // no xaxis tooltip for horizontal bar\n\n          config.yaxis[0].tooltip.enabled = false; // no xaxis tooltip for horizontal bar\n\n          config.chart.zoom.enabled = false; // no zooming for horz bars\n        }\n\n        if (config.chart.type === 'bar' || config.chart.type === 'rangeBar') {\n          if (config.tooltip.shared) {\n            if (config.xaxis.crosshairs.width === 'barWidth' && config.series.length > 1) {\n              config.xaxis.crosshairs.width = 'tickWidth';\n            }\n          }\n        }\n\n        if (config.chart.type === 'candlestick' || config.chart.type === 'boxPlot') {\n          if (config.yaxis[0].reversed) {\n            console.warn(\"Reversed y-axis in \".concat(config.chart.type, \" chart is not supported.\"));\n            config.yaxis[0].reversed = false;\n          }\n        }\n\n        return config;\n      }\n    }]);\n\n    return Config;\n  }();\n\n  var Globals = /*#__PURE__*/function () {\n    function Globals() {\n      _classCallCheck(this, Globals);\n    }\n\n    _createClass(Globals, [{\n      key: \"initGlobalVars\",\n      value: function initGlobalVars(gl) {\n        gl.series = []; // the MAIN series array (y values)\n\n        gl.seriesCandleO = [];\n        gl.seriesCandleH = [];\n        gl.seriesCandleM = [];\n        gl.seriesCandleL = [];\n        gl.seriesCandleC = [];\n        gl.seriesRangeStart = [];\n        gl.seriesRangeEnd = [];\n        gl.seriesRange = [];\n        gl.seriesPercent = [];\n        gl.seriesGoals = [];\n        gl.seriesX = [];\n        gl.seriesZ = [];\n        gl.seriesNames = [];\n        gl.seriesTotals = [];\n        gl.seriesLog = [];\n        gl.seriesColors = [];\n        gl.stackedSeriesTotals = [];\n        gl.seriesXvalues = []; // we will need this in tooltip (it's x position)\n        // when we will have unequal x values, we will need\n        // some way to get x value depending on mouse pointer\n\n        gl.seriesYvalues = []; // we will need this when deciding which series\n        // user hovered on\n\n        gl.labels = [];\n        gl.hasGroups = false;\n        gl.groups = [];\n        gl.categoryLabels = [];\n        gl.timescaleLabels = [];\n        gl.noLabelsProvided = false;\n        gl.resizeTimer = null;\n        gl.selectionResizeTimer = null;\n        gl.delayedElements = [];\n        gl.pointsArray = [];\n        gl.dataLabelsRects = [];\n        gl.isXNumeric = false;\n        gl.skipLastTimelinelabel = false;\n        gl.skipFirstTimelinelabel = false;\n        gl.isDataXYZ = false;\n        gl.isMultiLineX = false;\n        gl.isMultipleYAxis = false;\n        gl.maxY = -Number.MAX_VALUE;\n        gl.minY = Number.MIN_VALUE;\n        gl.minYArr = [];\n        gl.maxYArr = [];\n        gl.maxX = -Number.MAX_VALUE;\n        gl.minX = Number.MAX_VALUE;\n        gl.initialMaxX = -Number.MAX_VALUE;\n        gl.initialMinX = Number.MAX_VALUE;\n        gl.maxDate = 0;\n        gl.minDate = Number.MAX_VALUE;\n        gl.minZ = Number.MAX_VALUE;\n        gl.maxZ = -Number.MAX_VALUE;\n        gl.minXDiff = Number.MAX_VALUE;\n        gl.yAxisScale = [];\n        gl.xAxisScale = null;\n        gl.xAxisTicksPositions = [];\n        gl.yLabelsCoords = [];\n        gl.yTitleCoords = [];\n        gl.barPadForNumericAxis = 0;\n        gl.padHorizontal = 0;\n        gl.xRange = 0;\n        gl.yRange = [];\n        gl.zRange = 0;\n        gl.dataPoints = 0;\n        gl.xTickAmount = 0;\n      }\n    }, {\n      key: \"globalVars\",\n      value: function globalVars(config) {\n        return {\n          chartID: null,\n          // chart ID - apexcharts-cuid\n          cuid: null,\n          // chart ID - random numbers excluding \"apexcharts\" part\n          events: {\n            beforeMount: [],\n            mounted: [],\n            updated: [],\n            clicked: [],\n            selection: [],\n            dataPointSelection: [],\n            zoomed: [],\n            scrolled: []\n          },\n          colors: [],\n          clientX: null,\n          clientY: null,\n          fill: {\n            colors: []\n          },\n          stroke: {\n            colors: []\n          },\n          dataLabels: {\n            style: {\n              colors: []\n            }\n          },\n          radarPolygons: {\n            fill: {\n              colors: []\n            }\n          },\n          markers: {\n            colors: [],\n            size: config.markers.size,\n            largestSize: 0\n          },\n          animationEnded: false,\n          isTouchDevice: 'ontouchstart' in window || navigator.msMaxTouchPoints,\n          isDirty: false,\n          // chart has been updated after the initial render. This is different than dataChanged property. isDirty means user manually called some method to update\n          isExecCalled: false,\n          // whether user updated the chart through the exec method\n          initialConfig: null,\n          // we will store the first config user has set to go back when user finishes interactions like zooming and come out of it\n          initialSeries: [],\n          lastXAxis: [],\n          lastYAxis: [],\n          columnSeries: null,\n          labels: [],\n          // store the text to draw on x axis\n          // Don't mutate the labels, many things including tooltips depends on it!\n          timescaleLabels: [],\n          // store the timescaleLabels Labels in another variable\n          noLabelsProvided: false,\n          // if user didn't provide any categories/labels or x values, fallback to 1,2,3,4...\n          allSeriesCollapsed: false,\n          collapsedSeries: [],\n          // when user collapses a series, it goes into this array\n          collapsedSeriesIndices: [],\n          // this stores the index of the collapsedSeries instead of whole object for quick access\n          ancillaryCollapsedSeries: [],\n          // when user collapses an \"alwaysVisible\" series, it goes into this array\n          ancillaryCollapsedSeriesIndices: [],\n          // this stores the index of the ancillaryCollapsedSeries whose y-axis is always visible\n          risingSeries: [],\n          // when user re-opens a collapsed series, it goes here\n          dataFormatXNumeric: false,\n          // boolean value to indicate user has passed numeric x values\n          capturedSeriesIndex: -1,\n          capturedDataPointIndex: -1,\n          selectedDataPoints: [],\n          goldenPadding: 35,\n          // this value is used at a lot of places for spacing purpose\n          invalidLogScale: false,\n          // if a user enabled log scale but the data provided is not valid to generate a log scale, turn on this flag\n          ignoreYAxisIndexes: [],\n          // when series are being collapsed in multiple y axes, ignore certain index\n          yAxisSameScaleIndices: [],\n          maxValsInArrayIndex: 0,\n          radialSize: 0,\n          selection: undefined,\n          zoomEnabled: config.chart.toolbar.autoSelected === 'zoom' && config.chart.toolbar.tools.zoom && config.chart.zoom.enabled,\n          panEnabled: config.chart.toolbar.autoSelected === 'pan' && config.chart.toolbar.tools.pan,\n          selectionEnabled: config.chart.toolbar.autoSelected === 'selection' && config.chart.toolbar.tools.selection,\n          yaxis: null,\n          mousedown: false,\n          lastClientPosition: {},\n          // don't reset this variable this the chart is destroyed. It is used to detect right or left mousemove in panning\n          visibleXRange: undefined,\n          yValueDecimal: 0,\n          // are there floating numbers in the series. If yes, this represent the len of the decimals\n          total: 0,\n          SVGNS: 'http://www.w3.org/2000/svg',\n          // svg namespace\n          svgWidth: 0,\n          // the whole svg width\n          svgHeight: 0,\n          // the whole svg height\n          noData: false,\n          // whether there is any data to display or not\n          locale: {},\n          // the current locale values will be preserved here for global access\n          dom: {},\n          // for storing all dom nodes in this particular property\n          memory: {\n            methodsToExec: []\n          },\n          shouldAnimate: true,\n          skipLastTimelinelabel: false,\n          // when last label is cropped, skip drawing it\n          skipFirstTimelinelabel: false,\n          // when first label is cropped, skip drawing it\n          delayedElements: [],\n          // element which appear after animation has finished\n          axisCharts: true,\n          // chart type = line or area or bar\n          // (refer them also as plot charts in the code)\n          isDataXYZ: false,\n          // bool: data was provided in a {[x,y,z]} pattern\n          resized: false,\n          // bool: user has resized\n          resizeTimer: null,\n          // timeout function to make a small delay before\n          // drawing when user resized\n          comboCharts: false,\n          // bool: whether it's a combination of line/column\n          dataChanged: false,\n          // bool: has data changed dynamically\n          previousPaths: [],\n          // array: when data is changed, it will animate from\n          // previous paths\n          allSeriesHasEqualX: true,\n          pointsArray: [],\n          // store the points positions here to draw later on hover\n          // format is - [[x,y],[x,y]... [x,y]]\n          dataLabelsRects: [],\n          // store the positions of datalabels to prevent collision\n          lastDrawnDataLabelsIndexes: [],\n          hasNullValues: false,\n          // bool: whether series contains null values\n          easing: null,\n          // function: animation effect to apply\n          zoomed: false,\n          // whether user has zoomed or not\n          gridWidth: 0,\n          // drawable width of actual graphs (series paths)\n          gridHeight: 0,\n          // drawable height of actual graphs (series paths)\n          rotateXLabels: false,\n          defaultLabels: false,\n          xLabelFormatter: undefined,\n          // formatter for x axis labels\n          yLabelFormatters: [],\n          xaxisTooltipFormatter: undefined,\n          // formatter for x axis tooltip\n          ttKeyFormatter: undefined,\n          ttVal: undefined,\n          ttZFormatter: undefined,\n          LINE_HEIGHT_RATIO: 1.618,\n          xAxisLabelsHeight: 0,\n          xAxisGroupLabelsHeight: 0,\n          xAxisLabelsWidth: 0,\n          yAxisLabelsWidth: 0,\n          scaleX: 1,\n          scaleY: 1,\n          translateX: 0,\n          translateY: 0,\n          translateYAxisX: [],\n          yAxisWidths: [],\n          translateXAxisY: 0,\n          translateXAxisX: 0,\n          tooltip: null\n        };\n      }\n    }, {\n      key: \"init\",\n      value: function init(config) {\n        var globals = this.globalVars(config);\n        this.initGlobalVars(globals);\n        globals.initialConfig = Utils$1.extend({}, config);\n        globals.initialSeries = Utils$1.clone(config.series);\n        globals.lastXAxis = Utils$1.clone(globals.initialConfig.xaxis);\n        globals.lastYAxis = Utils$1.clone(globals.initialConfig.yaxis);\n        return globals;\n      }\n    }]);\n\n    return Globals;\n  }();\n\n  /**\n   * ApexCharts Base Class for extending user options with pre-defined ApexCharts config.\n   *\n   * @module Base\n   **/\n\n  var Base = /*#__PURE__*/function () {\n    function Base(opts) {\n      _classCallCheck(this, Base);\n\n      this.opts = opts;\n    }\n\n    _createClass(Base, [{\n      key: \"init\",\n      value: function init() {\n        var config = new Config(this.opts).init({\n          responsiveOverride: false\n        });\n        var globals = new Globals().init(config);\n        var w = {\n          config: config,\n          globals: globals\n        };\n        return w;\n      }\n    }]);\n\n    return Base;\n  }();\n\n  /**\n   * ApexCharts Fill Class for setting fill options of the paths.\n   *\n   * @module Fill\n   **/\n\n  var Fill = /*#__PURE__*/function () {\n    function Fill(ctx) {\n      _classCallCheck(this, Fill);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.opts = null;\n      this.seriesIndex = 0;\n    }\n\n    _createClass(Fill, [{\n      key: \"clippedImgArea\",\n      value: function clippedImgArea(params) {\n        var w = this.w;\n        var cnf = w.config;\n        var svgW = parseInt(w.globals.gridWidth, 10);\n        var svgH = parseInt(w.globals.gridHeight, 10);\n        var size = svgW > svgH ? svgW : svgH;\n        var fillImg = params.image;\n        var imgWidth = 0;\n        var imgHeight = 0;\n\n        if (typeof params.width === 'undefined' && typeof params.height === 'undefined') {\n          if (cnf.fill.image.width !== undefined && cnf.fill.image.height !== undefined) {\n            imgWidth = cnf.fill.image.width + 1;\n            imgHeight = cnf.fill.image.height;\n          } else {\n            imgWidth = size + 1;\n            imgHeight = size;\n          }\n        } else {\n          imgWidth = params.width;\n          imgHeight = params.height;\n        }\n\n        var elPattern = document.createElementNS(w.globals.SVGNS, 'pattern');\n        Graphics.setAttrs(elPattern, {\n          id: params.patternID,\n          patternUnits: params.patternUnits ? params.patternUnits : 'userSpaceOnUse',\n          width: imgWidth + 'px',\n          height: imgHeight + 'px'\n        });\n        var elImage = document.createElementNS(w.globals.SVGNS, 'image');\n        elPattern.appendChild(elImage);\n        elImage.setAttributeNS(window.SVG.xlink, 'href', fillImg);\n        Graphics.setAttrs(elImage, {\n          x: 0,\n          y: 0,\n          preserveAspectRatio: 'none',\n          width: imgWidth + 'px',\n          height: imgHeight + 'px'\n        });\n        elImage.style.opacity = params.opacity;\n        w.globals.dom.elDefs.node.appendChild(elPattern);\n      }\n    }, {\n      key: \"getSeriesIndex\",\n      value: function getSeriesIndex(opts) {\n        var w = this.w;\n\n        if ((w.config.chart.type === 'bar' || w.config.chart.type === 'rangeBar') && w.config.plotOptions.bar.distributed || w.config.chart.type === 'heatmap' || w.config.chart.type === 'treemap') {\n          this.seriesIndex = opts.seriesNumber;\n        } else {\n          this.seriesIndex = opts.seriesNumber % w.globals.series.length;\n        }\n\n        return this.seriesIndex;\n      }\n    }, {\n      key: \"fillPath\",\n      value: function fillPath(opts) {\n        var w = this.w;\n        this.opts = opts;\n        var cnf = this.w.config;\n        var pathFill;\n        var patternFill, gradientFill;\n        this.seriesIndex = this.getSeriesIndex(opts);\n        var fillColors = this.getFillColors();\n        var fillColor = fillColors[this.seriesIndex]; //override fillcolor if user inputted color with data\n\n        if (w.globals.seriesColors[this.seriesIndex] !== undefined) {\n          fillColor = w.globals.seriesColors[this.seriesIndex];\n        }\n\n        if (typeof fillColor === 'function') {\n          fillColor = fillColor({\n            seriesIndex: this.seriesIndex,\n            dataPointIndex: opts.dataPointIndex,\n            value: opts.value,\n            w: w\n          });\n        }\n\n        var fillType = opts.fillType ? opts.fillType : this.getFillType(this.seriesIndex);\n        var fillOpacity = Array.isArray(cnf.fill.opacity) ? cnf.fill.opacity[this.seriesIndex] : cnf.fill.opacity;\n\n        if (opts.color) {\n          fillColor = opts.color;\n        }\n\n        var defaultColor = fillColor;\n\n        if (fillColor.indexOf('rgb') === -1) {\n          if (fillColor.length < 9) {\n            // if the hex contains alpha and is of 9 digit, skip the opacity\n            defaultColor = Utils$1.hexToRgba(fillColor, fillOpacity);\n          }\n        } else {\n          if (fillColor.indexOf('rgba') > -1) {\n            fillOpacity = Utils$1.getOpacityFromRGBA(fillColor);\n          }\n        }\n\n        if (opts.opacity) fillOpacity = opts.opacity;\n\n        if (fillType === 'pattern') {\n          patternFill = this.handlePatternFill({\n            fillConfig: opts.fillConfig,\n            patternFill: patternFill,\n            fillColor: fillColor,\n            fillOpacity: fillOpacity,\n            defaultColor: defaultColor\n          });\n        }\n\n        if (fillType === 'gradient') {\n          gradientFill = this.handleGradientFill({\n            fillConfig: opts.fillConfig,\n            fillColor: fillColor,\n            fillOpacity: fillOpacity,\n            i: this.seriesIndex\n          });\n        }\n\n        if (fillType === 'image') {\n          var imgSrc = cnf.fill.image.src;\n          var patternID = opts.patternID ? opts.patternID : '';\n          this.clippedImgArea({\n            opacity: fillOpacity,\n            image: Array.isArray(imgSrc) ? opts.seriesNumber < imgSrc.length ? imgSrc[opts.seriesNumber] : imgSrc[0] : imgSrc,\n            width: opts.width ? opts.width : undefined,\n            height: opts.height ? opts.height : undefined,\n            patternUnits: opts.patternUnits,\n            patternID: \"pattern\".concat(w.globals.cuid).concat(opts.seriesNumber + 1).concat(patternID)\n          });\n          pathFill = \"url(#pattern\".concat(w.globals.cuid).concat(opts.seriesNumber + 1).concat(patternID, \")\");\n        } else if (fillType === 'gradient') {\n          pathFill = gradientFill;\n        } else if (fillType === 'pattern') {\n          pathFill = patternFill;\n        } else {\n          pathFill = defaultColor;\n        } // override pattern/gradient if opts.solid is true\n\n\n        if (opts.solid) {\n          pathFill = defaultColor;\n        }\n\n        return pathFill;\n      }\n    }, {\n      key: \"getFillType\",\n      value: function getFillType(seriesIndex) {\n        var w = this.w;\n\n        if (Array.isArray(w.config.fill.type)) {\n          return w.config.fill.type[seriesIndex];\n        } else {\n          return w.config.fill.type;\n        }\n      }\n    }, {\n      key: \"getFillColors\",\n      value: function getFillColors() {\n        var w = this.w;\n        var cnf = w.config;\n        var opts = this.opts;\n        var fillColors = [];\n\n        if (w.globals.comboCharts) {\n          if (w.config.series[this.seriesIndex].type === 'line') {\n            if (Array.isArray(w.globals.stroke.colors)) {\n              fillColors = w.globals.stroke.colors;\n            } else {\n              fillColors.push(w.globals.stroke.colors);\n            }\n          } else {\n            if (Array.isArray(w.globals.fill.colors)) {\n              fillColors = w.globals.fill.colors;\n            } else {\n              fillColors.push(w.globals.fill.colors);\n            }\n          }\n        } else {\n          if (cnf.chart.type === 'line') {\n            if (Array.isArray(w.globals.stroke.colors)) {\n              fillColors = w.globals.stroke.colors;\n            } else {\n              fillColors.push(w.globals.stroke.colors);\n            }\n          } else {\n            if (Array.isArray(w.globals.fill.colors)) {\n              fillColors = w.globals.fill.colors;\n            } else {\n              fillColors.push(w.globals.fill.colors);\n            }\n          }\n        } // colors passed in arguments\n\n\n        if (typeof opts.fillColors !== 'undefined') {\n          fillColors = [];\n\n          if (Array.isArray(opts.fillColors)) {\n            fillColors = opts.fillColors.slice();\n          } else {\n            fillColors.push(opts.fillColors);\n          }\n        }\n\n        return fillColors;\n      }\n    }, {\n      key: \"handlePatternFill\",\n      value: function handlePatternFill(_ref) {\n        var fillConfig = _ref.fillConfig,\n            patternFill = _ref.patternFill,\n            fillColor = _ref.fillColor,\n            fillOpacity = _ref.fillOpacity,\n            defaultColor = _ref.defaultColor;\n        var fillCnf = this.w.config.fill;\n\n        if (fillConfig) {\n          fillCnf = fillConfig;\n        }\n\n        var opts = this.opts;\n        var graphics = new Graphics(this.ctx);\n        var patternStrokeWidth = Array.isArray(fillCnf.pattern.strokeWidth) ? fillCnf.pattern.strokeWidth[this.seriesIndex] : fillCnf.pattern.strokeWidth;\n        var patternLineColor = fillColor;\n\n        if (Array.isArray(fillCnf.pattern.style)) {\n          if (typeof fillCnf.pattern.style[opts.seriesNumber] !== 'undefined') {\n            var pf = graphics.drawPattern(fillCnf.pattern.style[opts.seriesNumber], fillCnf.pattern.width, fillCnf.pattern.height, patternLineColor, patternStrokeWidth, fillOpacity);\n            patternFill = pf;\n          } else {\n            patternFill = defaultColor;\n          }\n        } else {\n          patternFill = graphics.drawPattern(fillCnf.pattern.style, fillCnf.pattern.width, fillCnf.pattern.height, patternLineColor, patternStrokeWidth, fillOpacity);\n        }\n\n        return patternFill;\n      }\n    }, {\n      key: \"handleGradientFill\",\n      value: function handleGradientFill(_ref2) {\n        var fillColor = _ref2.fillColor,\n            fillOpacity = _ref2.fillOpacity,\n            fillConfig = _ref2.fillConfig,\n            i = _ref2.i;\n        var fillCnf = this.w.config.fill;\n\n        if (fillConfig) {\n          fillCnf = _objectSpread2(_objectSpread2({}, fillCnf), fillConfig);\n        }\n\n        var opts = this.opts;\n        var graphics = new Graphics(this.ctx);\n        var utils = new Utils$1();\n        var type = fillCnf.gradient.type;\n        var gradientFrom = fillColor;\n        var gradientTo;\n        var opacityFrom = fillCnf.gradient.opacityFrom === undefined ? fillOpacity : Array.isArray(fillCnf.gradient.opacityFrom) ? fillCnf.gradient.opacityFrom[i] : fillCnf.gradient.opacityFrom;\n\n        if (gradientFrom.indexOf('rgba') > -1) {\n          opacityFrom = Utils$1.getOpacityFromRGBA(gradientFrom);\n        }\n\n        var opacityTo = fillCnf.gradient.opacityTo === undefined ? fillOpacity : Array.isArray(fillCnf.gradient.opacityTo) ? fillCnf.gradient.opacityTo[i] : fillCnf.gradient.opacityTo;\n\n        if (fillCnf.gradient.gradientToColors === undefined || fillCnf.gradient.gradientToColors.length === 0) {\n          if (fillCnf.gradient.shade === 'dark') {\n            gradientTo = utils.shadeColor(parseFloat(fillCnf.gradient.shadeIntensity) * -1, fillColor.indexOf('rgb') > -1 ? Utils$1.rgb2hex(fillColor) : fillColor);\n          } else {\n            gradientTo = utils.shadeColor(parseFloat(fillCnf.gradient.shadeIntensity), fillColor.indexOf('rgb') > -1 ? Utils$1.rgb2hex(fillColor) : fillColor);\n          }\n        } else {\n          if (fillCnf.gradient.gradientToColors[opts.seriesNumber]) {\n            var gToColor = fillCnf.gradient.gradientToColors[opts.seriesNumber];\n            gradientTo = gToColor;\n\n            if (gToColor.indexOf('rgba') > -1) {\n              opacityTo = Utils$1.getOpacityFromRGBA(gToColor);\n            }\n          } else {\n            gradientTo = fillColor;\n          }\n        }\n\n        if (fillCnf.gradient.gradientFrom) {\n          gradientFrom = fillCnf.gradient.gradientFrom;\n        }\n\n        if (fillCnf.gradient.gradientTo) {\n          gradientTo = fillCnf.gradient.gradientTo;\n        }\n\n        if (fillCnf.gradient.inverseColors) {\n          var t = gradientFrom;\n          gradientFrom = gradientTo;\n          gradientTo = t;\n        }\n\n        if (gradientFrom.indexOf('rgb') > -1) {\n          gradientFrom = Utils$1.rgb2hex(gradientFrom);\n        }\n\n        if (gradientTo.indexOf('rgb') > -1) {\n          gradientTo = Utils$1.rgb2hex(gradientTo);\n        }\n\n        return graphics.drawGradient(type, gradientFrom, gradientTo, opacityFrom, opacityTo, opts.size, fillCnf.gradient.stops, fillCnf.gradient.colorStops, i);\n      }\n    }]);\n\n    return Fill;\n  }();\n\n  /**\n   * ApexCharts Markers Class for drawing points on y values in axes charts.\n   *\n   * @module Markers\n   **/\n\n  var Markers = /*#__PURE__*/function () {\n    function Markers(ctx, opts) {\n      _classCallCheck(this, Markers);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(Markers, [{\n      key: \"setGlobalMarkerSize\",\n      value: function setGlobalMarkerSize() {\n        var w = this.w;\n        w.globals.markers.size = Array.isArray(w.config.markers.size) ? w.config.markers.size : [w.config.markers.size];\n\n        if (w.globals.markers.size.length > 0) {\n          if (w.globals.markers.size.length < w.globals.series.length + 1) {\n            for (var i = 0; i <= w.globals.series.length; i++) {\n              if (typeof w.globals.markers.size[i] === 'undefined') {\n                w.globals.markers.size.push(w.globals.markers.size[0]);\n              }\n            }\n          }\n        } else {\n          w.globals.markers.size = w.config.series.map(function (s) {\n            return w.config.markers.size;\n          });\n        }\n      }\n    }, {\n      key: \"plotChartMarkers\",\n      value: function plotChartMarkers(pointsPos, seriesIndex, j, pSize) {\n        var alwaysDrawMarker = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n        var w = this.w;\n        var i = seriesIndex;\n        var p = pointsPos;\n        var elPointsWrap = null;\n        var graphics = new Graphics(this.ctx);\n        var point;\n        var hasDiscreteMarkers = w.config.markers.discrete && w.config.markers.discrete.length;\n\n        if (w.globals.markers.size[seriesIndex] > 0 || alwaysDrawMarker || hasDiscreteMarkers) {\n          elPointsWrap = graphics.group({\n            class: alwaysDrawMarker || hasDiscreteMarkers ? '' : 'apexcharts-series-markers'\n          });\n          elPointsWrap.attr('clip-path', \"url(#gridRectMarkerMask\".concat(w.globals.cuid, \")\"));\n        }\n\n        if (Array.isArray(p.x)) {\n          for (var q = 0; q < p.x.length; q++) {\n            var dataPointIndex = j; // a small hack as we have 2 points for the first val to connect it\n\n            if (j === 1 && q === 0) dataPointIndex = 0;\n            if (j === 1 && q === 1) dataPointIndex = 1;\n            var PointClasses = 'apexcharts-marker';\n\n            if ((w.config.chart.type === 'line' || w.config.chart.type === 'area') && !w.globals.comboCharts && !w.config.tooltip.intersect) {\n              PointClasses += ' no-pointer-events';\n            }\n\n            var shouldMarkerDraw = Array.isArray(w.config.markers.size) ? w.globals.markers.size[seriesIndex] > 0 : w.config.markers.size > 0;\n\n            if (shouldMarkerDraw || alwaysDrawMarker || hasDiscreteMarkers) {\n              if (Utils$1.isNumber(p.y[q])) {\n                PointClasses += \" w\".concat(Utils$1.randomId());\n              } else {\n                PointClasses = 'apexcharts-nullpoint';\n              }\n\n              var opts = this.getMarkerConfig({\n                cssClass: PointClasses,\n                seriesIndex: seriesIndex,\n                dataPointIndex: dataPointIndex\n              });\n\n              if (w.config.series[i].data[dataPointIndex]) {\n                if (w.config.series[i].data[dataPointIndex].fillColor) {\n                  opts.pointFillColor = w.config.series[i].data[dataPointIndex].fillColor;\n                }\n\n                if (w.config.series[i].data[dataPointIndex].strokeColor) {\n                  opts.pointStrokeColor = w.config.series[i].data[dataPointIndex].strokeColor;\n                }\n              }\n\n              if (pSize) {\n                opts.pSize = pSize;\n              }\n\n              point = graphics.drawMarker(p.x[q], p.y[q], opts);\n              point.attr('rel', dataPointIndex);\n              point.attr('j', dataPointIndex);\n              point.attr('index', seriesIndex);\n              point.node.setAttribute('default-marker-size', opts.pSize);\n              var filters = new Filters(this.ctx);\n              filters.setSelectionFilter(point, seriesIndex, dataPointIndex);\n              this.addEvents(point);\n\n              if (elPointsWrap) {\n                elPointsWrap.add(point);\n              }\n            } else {\n              // dynamic array creation - multidimensional\n              if (typeof w.globals.pointsArray[seriesIndex] === 'undefined') w.globals.pointsArray[seriesIndex] = [];\n              w.globals.pointsArray[seriesIndex].push([p.x[q], p.y[q]]);\n            }\n          }\n        }\n\n        return elPointsWrap;\n      }\n    }, {\n      key: \"getMarkerConfig\",\n      value: function getMarkerConfig(_ref) {\n        var cssClass = _ref.cssClass,\n            seriesIndex = _ref.seriesIndex,\n            _ref$dataPointIndex = _ref.dataPointIndex,\n            dataPointIndex = _ref$dataPointIndex === void 0 ? null : _ref$dataPointIndex,\n            _ref$finishRadius = _ref.finishRadius,\n            finishRadius = _ref$finishRadius === void 0 ? null : _ref$finishRadius;\n        var w = this.w;\n        var pStyle = this.getMarkerStyle(seriesIndex);\n        var pSize = w.globals.markers.size[seriesIndex];\n        var m = w.config.markers; // discrete markers is an option where user can specify a particular marker with different shape, size and color\n\n        if (dataPointIndex !== null && m.discrete.length) {\n          m.discrete.map(function (marker) {\n            if (marker.seriesIndex === seriesIndex && marker.dataPointIndex === dataPointIndex) {\n              pStyle.pointStrokeColor = marker.strokeColor;\n              pStyle.pointFillColor = marker.fillColor;\n              pSize = marker.size;\n              pStyle.pointShape = marker.shape;\n            }\n          });\n        }\n\n        return {\n          pSize: finishRadius === null ? pSize : finishRadius,\n          pRadius: m.radius,\n          width: Array.isArray(m.width) ? m.width[seriesIndex] : m.width,\n          height: Array.isArray(m.height) ? m.height[seriesIndex] : m.height,\n          pointStrokeWidth: Array.isArray(m.strokeWidth) ? m.strokeWidth[seriesIndex] : m.strokeWidth,\n          pointStrokeColor: pStyle.pointStrokeColor,\n          pointFillColor: pStyle.pointFillColor,\n          shape: pStyle.pointShape || (Array.isArray(m.shape) ? m.shape[seriesIndex] : m.shape),\n          class: cssClass,\n          pointStrokeOpacity: Array.isArray(m.strokeOpacity) ? m.strokeOpacity[seriesIndex] : m.strokeOpacity,\n          pointStrokeDashArray: Array.isArray(m.strokeDashArray) ? m.strokeDashArray[seriesIndex] : m.strokeDashArray,\n          pointFillOpacity: Array.isArray(m.fillOpacity) ? m.fillOpacity[seriesIndex] : m.fillOpacity,\n          seriesIndex: seriesIndex\n        };\n      }\n    }, {\n      key: \"addEvents\",\n      value: function addEvents(circle) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        circle.node.addEventListener('mouseenter', graphics.pathMouseEnter.bind(this.ctx, circle));\n        circle.node.addEventListener('mouseleave', graphics.pathMouseLeave.bind(this.ctx, circle));\n        circle.node.addEventListener('mousedown', graphics.pathMouseDown.bind(this.ctx, circle));\n        circle.node.addEventListener('click', w.config.markers.onClick);\n        circle.node.addEventListener('dblclick', w.config.markers.onDblClick);\n        circle.node.addEventListener('touchstart', graphics.pathMouseDown.bind(this.ctx, circle), {\n          passive: true\n        });\n      }\n    }, {\n      key: \"getMarkerStyle\",\n      value: function getMarkerStyle(seriesIndex) {\n        var w = this.w;\n        var colors = w.globals.markers.colors;\n        var strokeColors = w.config.markers.strokeColor || w.config.markers.strokeColors;\n        var pointStrokeColor = Array.isArray(strokeColors) ? strokeColors[seriesIndex] : strokeColors;\n        var pointFillColor = Array.isArray(colors) ? colors[seriesIndex] : colors;\n        return {\n          pointStrokeColor: pointStrokeColor,\n          pointFillColor: pointFillColor\n        };\n      }\n    }]);\n\n    return Markers;\n  }();\n\n  /**\n   * ApexCharts Scatter Class.\n   * This Class also handles bubbles chart as currently there is no major difference in drawing them,\n   * @module Scatter\n   **/\n\n  var Scatter = /*#__PURE__*/function () {\n    function Scatter(ctx) {\n      _classCallCheck(this, Scatter);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.initialAnim = this.w.config.chart.animations.enabled;\n      this.dynamicAnim = this.initialAnim && this.w.config.chart.animations.dynamicAnimation.enabled;\n    }\n\n    _createClass(Scatter, [{\n      key: \"draw\",\n      value: function draw(elSeries, j, opts) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var realIndex = opts.realIndex;\n        var pointsPos = opts.pointsPos;\n        var zRatio = opts.zRatio;\n        var elPointsMain = opts.elParent;\n        var elPointsWrap = graphics.group({\n          class: \"apexcharts-series-markers apexcharts-series-\".concat(w.config.chart.type)\n        });\n        elPointsWrap.attr('clip-path', \"url(#gridRectMarkerMask\".concat(w.globals.cuid, \")\"));\n\n        if (Array.isArray(pointsPos.x)) {\n          for (var q = 0; q < pointsPos.x.length; q++) {\n            var dataPointIndex = j + 1;\n            var shouldDraw = true; // a small hack as we have 2 points for the first val to connect it\n\n            if (j === 0 && q === 0) dataPointIndex = 0;\n            if (j === 0 && q === 1) dataPointIndex = 1;\n            var radius = 0;\n            var finishRadius = w.globals.markers.size[realIndex];\n\n            if (zRatio !== Infinity) {\n              // means we have a bubble\n              var bubble = w.config.plotOptions.bubble;\n              finishRadius = w.globals.seriesZ[realIndex][dataPointIndex];\n\n              if (bubble.zScaling) {\n                finishRadius /= zRatio;\n              }\n\n              if (bubble.minBubbleRadius && finishRadius < bubble.minBubbleRadius) {\n                finishRadius = bubble.minBubbleRadius;\n              }\n\n              if (bubble.maxBubbleRadius && finishRadius > bubble.maxBubbleRadius) {\n                finishRadius = bubble.maxBubbleRadius;\n              }\n            }\n\n            if (!w.config.chart.animations.enabled) {\n              radius = finishRadius;\n            }\n\n            var x = pointsPos.x[q];\n            var y = pointsPos.y[q];\n            radius = radius || 0;\n\n            if (y === null || typeof w.globals.series[realIndex][dataPointIndex] === 'undefined') {\n              shouldDraw = false;\n            }\n\n            if (shouldDraw) {\n              var point = this.drawPoint(x, y, radius, finishRadius, realIndex, dataPointIndex, j);\n              elPointsWrap.add(point);\n            }\n\n            elPointsMain.add(elPointsWrap);\n          }\n        }\n      }\n    }, {\n      key: \"drawPoint\",\n      value: function drawPoint(x, y, radius, finishRadius, realIndex, dataPointIndex, j) {\n        var w = this.w;\n        var i = realIndex;\n        var anim = new Animations(this.ctx);\n        var filters = new Filters(this.ctx);\n        var fill = new Fill(this.ctx);\n        var markers = new Markers(this.ctx);\n        var graphics = new Graphics(this.ctx);\n        var markerConfig = markers.getMarkerConfig({\n          cssClass: 'apexcharts-marker',\n          seriesIndex: i,\n          dataPointIndex: dataPointIndex,\n          finishRadius: w.config.chart.type === 'bubble' || w.globals.comboCharts && w.config.series[realIndex] && w.config.series[realIndex].type === 'bubble' ? finishRadius : null\n        });\n        finishRadius = markerConfig.pSize;\n        var pathFillCircle = fill.fillPath({\n          seriesNumber: realIndex,\n          dataPointIndex: dataPointIndex,\n          color: markerConfig.pointFillColor,\n          patternUnits: 'objectBoundingBox',\n          value: w.globals.series[realIndex][j]\n        });\n        var el;\n\n        if (markerConfig.shape === 'circle') {\n          el = graphics.drawCircle(radius);\n        } else if (markerConfig.shape === 'square' || markerConfig.shape === 'rect') {\n          el = graphics.drawRect(0, 0, markerConfig.width - markerConfig.pointStrokeWidth / 2, markerConfig.height - markerConfig.pointStrokeWidth / 2, markerConfig.pRadius);\n        }\n\n        if (w.config.series[i].data[dataPointIndex]) {\n          if (w.config.series[i].data[dataPointIndex].fillColor) {\n            pathFillCircle = w.config.series[i].data[dataPointIndex].fillColor;\n          }\n        }\n\n        el.attr({\n          x: x - markerConfig.width / 2 - markerConfig.pointStrokeWidth / 2,\n          y: y - markerConfig.height / 2 - markerConfig.pointStrokeWidth / 2,\n          cx: x,\n          cy: y,\n          fill: pathFillCircle,\n          'fill-opacity': markerConfig.pointFillOpacity,\n          stroke: markerConfig.pointStrokeColor,\n          r: finishRadius,\n          'stroke-width': markerConfig.pointStrokeWidth,\n          'stroke-dasharray': markerConfig.pointStrokeDashArray,\n          'stroke-opacity': markerConfig.pointStrokeOpacity\n        });\n\n        if (w.config.chart.dropShadow.enabled) {\n          var dropShadow = w.config.chart.dropShadow;\n          filters.dropShadow(el, dropShadow, realIndex);\n        }\n\n        if (this.initialAnim && !w.globals.dataChanged && !w.globals.resized) {\n          var speed = w.config.chart.animations.speed;\n          anim.animateMarker(el, 0, markerConfig.shape === 'circle' ? finishRadius : {\n            width: markerConfig.width,\n            height: markerConfig.height\n          }, speed, w.globals.easing, function () {\n            window.setTimeout(function () {\n              anim.animationCompleted(el);\n            }, 100);\n          });\n        } else {\n          w.globals.animationEnded = true;\n        }\n\n        if (w.globals.dataChanged && markerConfig.shape === 'circle') {\n          if (this.dynamicAnim) {\n            var _speed = w.config.chart.animations.dynamicAnimation.speed;\n            var prevX, prevY, prevR;\n            var prevPathJ = null;\n            prevPathJ = w.globals.previousPaths[realIndex] && w.globals.previousPaths[realIndex][j];\n\n            if (typeof prevPathJ !== 'undefined' && prevPathJ !== null) {\n              // series containing less elements will ignore these values and revert to 0\n              prevX = prevPathJ.x;\n              prevY = prevPathJ.y;\n              prevR = typeof prevPathJ.r !== 'undefined' ? prevPathJ.r : finishRadius;\n            }\n\n            for (var cs = 0; cs < w.globals.collapsedSeries.length; cs++) {\n              if (w.globals.collapsedSeries[cs].index === realIndex) {\n                _speed = 1;\n                finishRadius = 0;\n              }\n            }\n\n            if (x === 0 && y === 0) finishRadius = 0;\n            anim.animateCircle(el, {\n              cx: prevX,\n              cy: prevY,\n              r: prevR\n            }, {\n              cx: x,\n              cy: y,\n              r: finishRadius\n            }, _speed, w.globals.easing);\n          } else {\n            el.attr({\n              r: finishRadius\n            });\n          }\n        }\n\n        el.attr({\n          rel: dataPointIndex,\n          j: dataPointIndex,\n          index: realIndex,\n          'default-marker-size': finishRadius\n        });\n        filters.setSelectionFilter(el, realIndex, dataPointIndex);\n        markers.addEvents(el);\n        el.node.classList.add('apexcharts-marker');\n        return el;\n      }\n    }, {\n      key: \"centerTextInBubble\",\n      value: function centerTextInBubble(y) {\n        var w = this.w;\n        y = y + parseInt(w.config.dataLabels.style.fontSize, 10) / 4;\n        return {\n          y: y\n        };\n      }\n    }]);\n\n    return Scatter;\n  }();\n\n  /**\n   * ApexCharts DataLabels Class for drawing dataLabels on Axes based Charts.\n   *\n   * @module DataLabels\n   **/\n\n  var DataLabels = /*#__PURE__*/function () {\n    function DataLabels(ctx) {\n      _classCallCheck(this, DataLabels);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    } // When there are many datalabels to be printed, and some of them overlaps each other in the same series, this method will take care of that\n    // Also, when datalabels exceeds the drawable area and get clipped off, we need to adjust and move some pixels to make them visible again\n\n\n    _createClass(DataLabels, [{\n      key: \"dataLabelsCorrection\",\n      value: function dataLabelsCorrection(x, y, val, i, dataPointIndex, alwaysDrawDataLabel, fontSize) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var drawnextLabel = false; //\n\n        var textRects = graphics.getTextRects(val, fontSize);\n        var width = textRects.width;\n        var height = textRects.height;\n        if (y < 0) y = 0;\n        if (y > w.globals.gridHeight + height) y = w.globals.gridHeight + height / 2; // first value in series, so push an empty array\n\n        if (typeof w.globals.dataLabelsRects[i] === 'undefined') w.globals.dataLabelsRects[i] = []; // then start pushing actual rects in that sub-array\n\n        w.globals.dataLabelsRects[i].push({\n          x: x,\n          y: y,\n          width: width,\n          height: height\n        });\n        var len = w.globals.dataLabelsRects[i].length - 2;\n        var lastDrawnIndex = typeof w.globals.lastDrawnDataLabelsIndexes[i] !== 'undefined' ? w.globals.lastDrawnDataLabelsIndexes[i][w.globals.lastDrawnDataLabelsIndexes[i].length - 1] : 0;\n\n        if (typeof w.globals.dataLabelsRects[i][len] !== 'undefined') {\n          var lastDataLabelRect = w.globals.dataLabelsRects[i][lastDrawnIndex];\n\n          if ( // next label forward and x not intersecting\n          x > lastDataLabelRect.x + lastDataLabelRect.width + 2 || y > lastDataLabelRect.y + lastDataLabelRect.height + 2 || x + width < lastDataLabelRect.x // next label is going to be drawn backwards\n          ) {\n            // the 2 indexes don't override, so OK to draw next label\n            drawnextLabel = true;\n          }\n        }\n\n        if (dataPointIndex === 0 || alwaysDrawDataLabel) {\n          drawnextLabel = true;\n        }\n\n        return {\n          x: x,\n          y: y,\n          textRects: textRects,\n          drawnextLabel: drawnextLabel\n        };\n      }\n    }, {\n      key: \"drawDataLabel\",\n      value: function drawDataLabel(_ref) {\n        var _this = this;\n\n        var type = _ref.type,\n            pos = _ref.pos,\n            i = _ref.i,\n            j = _ref.j,\n            isRangeStart = _ref.isRangeStart,\n            _ref$strokeWidth = _ref.strokeWidth,\n            strokeWidth = _ref$strokeWidth === void 0 ? 2 : _ref$strokeWidth;\n        // this method handles line, area, bubble, scatter charts as those charts contains markers/points which have pre-defined x/y positions\n        // all other charts like radar / bars / heatmaps will define their own drawDataLabel routine\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var dataLabelsConfig = w.config.dataLabels;\n        var x = 0;\n        var y = 0;\n        var dataPointIndex = j;\n        var elDataLabelsWrap = null;\n\n        if (!dataLabelsConfig.enabled || !Array.isArray(pos.x)) {\n          return elDataLabelsWrap;\n        }\n\n        elDataLabelsWrap = graphics.group({\n          class: 'apexcharts-data-labels'\n        });\n\n        for (var q = 0; q < pos.x.length; q++) {\n          x = pos.x[q] + dataLabelsConfig.offsetX;\n          y = pos.y[q] + dataLabelsConfig.offsetY + strokeWidth;\n\n          if (!isNaN(x)) {\n            // a small hack as we have 2 points for the first val to connect it\n            if (j === 1 && q === 0) dataPointIndex = 0;\n            if (j === 1 && q === 1) dataPointIndex = 1;\n            var val = w.globals.series[i][dataPointIndex];\n\n            if (type === 'rangeArea') {\n              if (isRangeStart) {\n                val = w.globals.seriesRangeStart[i][dataPointIndex];\n              } else {\n                val = w.globals.seriesRangeEnd[i][dataPointIndex];\n              }\n            }\n\n            var text = '';\n\n            var getText = function getText(v) {\n              return w.config.dataLabels.formatter(v, {\n                ctx: _this.ctx,\n                seriesIndex: i,\n                dataPointIndex: dataPointIndex,\n                w: w\n              });\n            };\n\n            if (w.config.chart.type === 'bubble') {\n              val = w.globals.seriesZ[i][dataPointIndex];\n              text = getText(val);\n              y = pos.y[q];\n              var scatter = new Scatter(this.ctx);\n              var centerTextInBubbleCoords = scatter.centerTextInBubble(y, i, dataPointIndex);\n              y = centerTextInBubbleCoords.y;\n            } else {\n              if (typeof val !== 'undefined') {\n                text = getText(val);\n              }\n            }\n\n            this.plotDataLabelsText({\n              x: x,\n              y: y,\n              text: text,\n              i: i,\n              j: dataPointIndex,\n              parent: elDataLabelsWrap,\n              offsetCorrection: true,\n              dataLabelsConfig: w.config.dataLabels\n            });\n          }\n        }\n\n        return elDataLabelsWrap;\n      }\n    }, {\n      key: \"plotDataLabelsText\",\n      value: function plotDataLabelsText(opts) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var x = opts.x,\n            y = opts.y,\n            i = opts.i,\n            j = opts.j,\n            text = opts.text,\n            textAnchor = opts.textAnchor,\n            fontSize = opts.fontSize,\n            parent = opts.parent,\n            dataLabelsConfig = opts.dataLabelsConfig,\n            color = opts.color,\n            alwaysDrawDataLabel = opts.alwaysDrawDataLabel,\n            offsetCorrection = opts.offsetCorrection;\n\n        if (Array.isArray(w.config.dataLabels.enabledOnSeries)) {\n          if (w.config.dataLabels.enabledOnSeries.indexOf(i) < 0) {\n            return;\n          }\n        }\n\n        var correctedLabels = {\n          x: x,\n          y: y,\n          drawnextLabel: true,\n          textRects: null\n        };\n\n        if (offsetCorrection) {\n          correctedLabels = this.dataLabelsCorrection(x, y, text, i, j, alwaysDrawDataLabel, parseInt(dataLabelsConfig.style.fontSize, 10));\n        } // when zoomed, we don't need to correct labels offsets,\n        // but if normally, labels get cropped, correct them\n\n\n        if (!w.globals.zoomed) {\n          x = correctedLabels.x;\n          y = correctedLabels.y;\n        }\n\n        if (correctedLabels.textRects) {\n          // fixes #2264\n          if (x < -10 - correctedLabels.textRects.width || x > w.globals.gridWidth + correctedLabels.textRects.width + 10) {\n            // datalabels fall outside drawing area, so draw a blank label\n            text = '';\n          }\n        }\n\n        var dataLabelColor = w.globals.dataLabels.style.colors[i];\n\n        if ((w.config.chart.type === 'bar' || w.config.chart.type === 'rangeBar') && w.config.plotOptions.bar.distributed || w.config.dataLabels.distributed) {\n          dataLabelColor = w.globals.dataLabels.style.colors[j];\n        }\n\n        if (typeof dataLabelColor === 'function') {\n          dataLabelColor = dataLabelColor({\n            series: w.globals.series,\n            seriesIndex: i,\n            dataPointIndex: j,\n            w: w\n          });\n        }\n\n        if (color) {\n          dataLabelColor = color;\n        }\n\n        var offX = dataLabelsConfig.offsetX;\n        var offY = dataLabelsConfig.offsetY;\n\n        if (w.config.chart.type === 'bar' || w.config.chart.type === 'rangeBar') {\n          // for certain chart types, we handle offsets while calculating datalabels pos\n          // why? because bars/column may have negative values and based on that\n          // offsets becomes reversed\n          offX = 0;\n          offY = 0;\n        }\n\n        if (correctedLabels.drawnextLabel) {\n          var dataLabelText = graphics.drawText({\n            width: 100,\n            height: parseInt(dataLabelsConfig.style.fontSize, 10),\n            x: x + offX,\n            y: y + offY,\n            foreColor: dataLabelColor,\n            textAnchor: textAnchor || dataLabelsConfig.textAnchor,\n            text: text,\n            fontSize: fontSize || dataLabelsConfig.style.fontSize,\n            fontFamily: dataLabelsConfig.style.fontFamily,\n            fontWeight: dataLabelsConfig.style.fontWeight || 'normal'\n          });\n          dataLabelText.attr({\n            class: 'apexcharts-datalabel',\n            cx: x,\n            cy: y\n          });\n\n          if (dataLabelsConfig.dropShadow.enabled) {\n            var textShadow = dataLabelsConfig.dropShadow;\n            var filters = new Filters(this.ctx);\n            filters.dropShadow(dataLabelText, textShadow);\n          }\n\n          parent.add(dataLabelText);\n\n          if (typeof w.globals.lastDrawnDataLabelsIndexes[i] === 'undefined') {\n            w.globals.lastDrawnDataLabelsIndexes[i] = [];\n          }\n\n          w.globals.lastDrawnDataLabelsIndexes[i].push(j);\n        }\n      }\n    }, {\n      key: \"addBackgroundToDataLabel\",\n      value: function addBackgroundToDataLabel(el, coords) {\n        var w = this.w;\n        var bCnf = w.config.dataLabels.background;\n        var paddingH = bCnf.padding;\n        var paddingV = bCnf.padding / 2;\n        var width = coords.width;\n        var height = coords.height;\n        var graphics = new Graphics(this.ctx);\n        var elRect = graphics.drawRect(coords.x - paddingH, coords.y - paddingV / 2, width + paddingH * 2, height + paddingV, bCnf.borderRadius, w.config.chart.background === 'transparent' ? '#fff' : w.config.chart.background, bCnf.opacity, bCnf.borderWidth, bCnf.borderColor);\n\n        if (bCnf.dropShadow.enabled) {\n          var filters = new Filters(this.ctx);\n          filters.dropShadow(elRect, bCnf.dropShadow);\n        }\n\n        return elRect;\n      }\n    }, {\n      key: \"dataLabelsBackground\",\n      value: function dataLabelsBackground() {\n        var w = this.w;\n        if (w.config.chart.type === 'bubble') return;\n        var elDataLabels = w.globals.dom.baseEl.querySelectorAll('.apexcharts-datalabels text');\n\n        for (var i = 0; i < elDataLabels.length; i++) {\n          var el = elDataLabels[i];\n          var coords = el.getBBox();\n          var elRect = null;\n\n          if (coords.width && coords.height) {\n            elRect = this.addBackgroundToDataLabel(el, coords);\n          }\n\n          if (elRect) {\n            el.parentNode.insertBefore(elRect.node, el);\n            var background = el.getAttribute('fill');\n            var shouldAnim = w.config.chart.animations.enabled && !w.globals.resized && !w.globals.dataChanged;\n\n            if (shouldAnim) {\n              elRect.animate().attr({\n                fill: background\n              });\n            } else {\n              elRect.attr({\n                fill: background\n              });\n            }\n\n            el.setAttribute('fill', w.config.dataLabels.background.foreColor);\n          }\n        }\n      }\n    }, {\n      key: \"bringForward\",\n      value: function bringForward() {\n        var w = this.w;\n        var elDataLabelsNodes = w.globals.dom.baseEl.querySelectorAll('.apexcharts-datalabels');\n        var elSeries = w.globals.dom.baseEl.querySelector('.apexcharts-plot-series:last-child');\n\n        for (var i = 0; i < elDataLabelsNodes.length; i++) {\n          if (elSeries) {\n            elSeries.insertBefore(elDataLabelsNodes[i], elSeries.nextSibling);\n          }\n        }\n      }\n    }]);\n\n    return DataLabels;\n  }();\n\n  /**\n   * ApexCharts Series Class for interaction with the Series of the chart.\n   *\n   * @module Series\n   **/\n\n  var Series = /*#__PURE__*/function () {\n    function Series(ctx) {\n      _classCallCheck(this, Series);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.legendInactiveClass = 'legend-mouseover-inactive';\n    }\n\n    _createClass(Series, [{\n      key: \"getAllSeriesEls\",\n      value: function getAllSeriesEls() {\n        return this.w.globals.dom.baseEl.getElementsByClassName(\"apexcharts-series\");\n      }\n    }, {\n      key: \"getSeriesByName\",\n      value: function getSeriesByName(seriesName) {\n        return this.w.globals.dom.baseEl.querySelector(\".apexcharts-inner .apexcharts-series[seriesName='\".concat(Utils$1.escapeString(seriesName), \"']\"));\n      }\n    }, {\n      key: \"isSeriesHidden\",\n      value: function isSeriesHidden(seriesName) {\n        var targetElement = this.getSeriesByName(seriesName);\n        var realIndex = parseInt(targetElement.getAttribute('data:realIndex'), 10);\n        var isHidden = targetElement.classList.contains('apexcharts-series-collapsed');\n        return {\n          isHidden: isHidden,\n          realIndex: realIndex\n        };\n      }\n    }, {\n      key: \"addCollapsedClassToSeries\",\n      value: function addCollapsedClassToSeries(elSeries, index) {\n        var w = this.w;\n\n        function iterateOnAllCollapsedSeries(series) {\n          for (var cs = 0; cs < series.length; cs++) {\n            if (series[cs].index === index) {\n              elSeries.node.classList.add('apexcharts-series-collapsed');\n            }\n          }\n        }\n\n        iterateOnAllCollapsedSeries(w.globals.collapsedSeries);\n        iterateOnAllCollapsedSeries(w.globals.ancillaryCollapsedSeries);\n      }\n    }, {\n      key: \"toggleSeries\",\n      value: function toggleSeries(seriesName) {\n        var isSeriesHidden = this.isSeriesHidden(seriesName);\n        this.ctx.legend.legendHelpers.toggleDataSeries(isSeriesHidden.realIndex, isSeriesHidden.isHidden);\n        return isSeriesHidden.isHidden;\n      }\n    }, {\n      key: \"showSeries\",\n      value: function showSeries(seriesName) {\n        var isSeriesHidden = this.isSeriesHidden(seriesName);\n\n        if (isSeriesHidden.isHidden) {\n          this.ctx.legend.legendHelpers.toggleDataSeries(isSeriesHidden.realIndex, true);\n        }\n      }\n    }, {\n      key: \"hideSeries\",\n      value: function hideSeries(seriesName) {\n        var isSeriesHidden = this.isSeriesHidden(seriesName);\n\n        if (!isSeriesHidden.isHidden) {\n          this.ctx.legend.legendHelpers.toggleDataSeries(isSeriesHidden.realIndex, false);\n        }\n      }\n    }, {\n      key: \"resetSeries\",\n      value: function resetSeries() {\n        var shouldUpdateChart = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n        var shouldResetZoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        var shouldResetCollapsed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n        var w = this.w;\n        var series = Utils$1.clone(w.globals.initialSeries);\n        w.globals.previousPaths = [];\n\n        if (shouldResetCollapsed) {\n          w.globals.collapsedSeries = [];\n          w.globals.ancillaryCollapsedSeries = [];\n          w.globals.collapsedSeriesIndices = [];\n          w.globals.ancillaryCollapsedSeriesIndices = [];\n        } else {\n          series = this.emptyCollapsedSeries(series);\n        }\n\n        w.config.series = series;\n\n        if (shouldUpdateChart) {\n          if (shouldResetZoom) {\n            w.globals.zoomed = false;\n            this.ctx.updateHelpers.revertDefaultAxisMinMax();\n          }\n\n          this.ctx.updateHelpers._updateSeries(series, w.config.chart.animations.dynamicAnimation.enabled);\n        }\n      }\n    }, {\n      key: \"emptyCollapsedSeries\",\n      value: function emptyCollapsedSeries(series) {\n        var w = this.w;\n\n        for (var i = 0; i < series.length; i++) {\n          if (w.globals.collapsedSeriesIndices.indexOf(i) > -1) {\n            series[i].data = [];\n          }\n        }\n\n        return series;\n      }\n    }, {\n      key: \"toggleSeriesOnHover\",\n      value: function toggleSeriesOnHover(e, targetElement) {\n        var w = this.w;\n        if (!targetElement) targetElement = e.target;\n        var allSeriesEls = w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series, .apexcharts-datalabels\");\n\n        if (e.type === 'mousemove') {\n          var seriesCnt = parseInt(targetElement.getAttribute('rel'), 10) - 1;\n          var seriesEl = null;\n          var dataLabelEl = null;\n\n          if (w.globals.axisCharts || w.config.chart.type === 'radialBar') {\n            if (w.globals.axisCharts) {\n              seriesEl = w.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(seriesCnt, \"']\"));\n              dataLabelEl = w.globals.dom.baseEl.querySelector(\".apexcharts-datalabels[data\\\\:realIndex='\".concat(seriesCnt, \"']\"));\n            } else {\n              seriesEl = w.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(seriesCnt + 1, \"']\"));\n            }\n          } else {\n            seriesEl = w.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(seriesCnt + 1, \"'] path\"));\n          }\n\n          for (var se = 0; se < allSeriesEls.length; se++) {\n            allSeriesEls[se].classList.add(this.legendInactiveClass);\n          }\n\n          if (seriesEl !== null) {\n            if (!w.globals.axisCharts) {\n              seriesEl.parentNode.classList.remove(this.legendInactiveClass);\n            }\n\n            seriesEl.classList.remove(this.legendInactiveClass);\n\n            if (dataLabelEl !== null) {\n              dataLabelEl.classList.remove(this.legendInactiveClass);\n            }\n          }\n        } else if (e.type === 'mouseout') {\n          for (var _se = 0; _se < allSeriesEls.length; _se++) {\n            allSeriesEls[_se].classList.remove(this.legendInactiveClass);\n          }\n        }\n      }\n    }, {\n      key: \"highlightRangeInSeries\",\n      value: function highlightRangeInSeries(e, targetElement) {\n        var _this = this;\n\n        var w = this.w;\n        var allHeatMapElements = w.globals.dom.baseEl.getElementsByClassName('apexcharts-heatmap-rect');\n\n        var activeInactive = function activeInactive(action) {\n          for (var i = 0; i < allHeatMapElements.length; i++) {\n            allHeatMapElements[i].classList[action](_this.legendInactiveClass);\n          }\n        };\n\n        var removeInactiveClassFromHoveredRange = function removeInactiveClassFromHoveredRange(range) {\n          for (var i = 0; i < allHeatMapElements.length; i++) {\n            var val = parseInt(allHeatMapElements[i].getAttribute('val'), 10);\n\n            if (val >= range.from && val <= range.to) {\n              allHeatMapElements[i].classList.remove(_this.legendInactiveClass);\n            }\n          }\n        };\n\n        if (e.type === 'mousemove') {\n          var seriesCnt = parseInt(targetElement.getAttribute('rel'), 10) - 1;\n          activeInactive('add');\n          var range = w.config.plotOptions.heatmap.colorScale.ranges[seriesCnt];\n          removeInactiveClassFromHoveredRange(range);\n        } else if (e.type === 'mouseout') {\n          activeInactive('remove');\n        }\n      }\n    }, {\n      key: \"getActiveConfigSeriesIndex\",\n      value: function getActiveConfigSeriesIndex() {\n        var order = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'asc';\n        var chartTypes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];\n        var w = this.w;\n        var activeIndex = 0;\n\n        if (w.config.series.length > 1) {\n          // active series flag is required to know if user has not deactivated via legend click\n          var activeSeriesIndex = w.config.series.map(function (s, index) {\n            var checkChartType = function checkChartType() {\n              if (w.globals.comboCharts) {\n                return chartTypes.length === 0 || chartTypes.length && chartTypes.indexOf(w.config.series[index].type) > -1;\n              }\n\n              return true;\n            };\n\n            var hasData = s.data && s.data.length > 0 && w.globals.collapsedSeriesIndices.indexOf(index) === -1;\n            return hasData && checkChartType() ? index : -1;\n          });\n\n          for (var a = order === 'asc' ? 0 : activeSeriesIndex.length - 1; order === 'asc' ? a < activeSeriesIndex.length : a >= 0; order === 'asc' ? a++ : a--) {\n            if (activeSeriesIndex[a] !== -1) {\n              activeIndex = activeSeriesIndex[a];\n              break;\n            }\n          }\n        }\n\n        return activeIndex;\n      }\n    }, {\n      key: \"getBarSeriesIndices\",\n      value: function getBarSeriesIndices() {\n        var w = this.w;\n\n        if (w.globals.comboCharts) {\n          return this.w.config.series.map(function (s, i) {\n            return s.type === 'bar' || s.type === 'column' ? i : -1;\n          }).filter(function (i) {\n            return i !== -1;\n          });\n        }\n\n        return this.w.config.series.map(function (s, i) {\n          return i;\n        });\n      }\n    }, {\n      key: \"getPreviousPaths\",\n      value: function getPreviousPaths() {\n        var w = this.w;\n        w.globals.previousPaths = [];\n\n        function pushPaths(seriesEls, i, type) {\n          var paths = seriesEls[i].childNodes;\n          var dArr = {\n            type: type,\n            paths: [],\n            realIndex: seriesEls[i].getAttribute('data:realIndex')\n          };\n\n          for (var j = 0; j < paths.length; j++) {\n            if (paths[j].hasAttribute('pathTo')) {\n              var d = paths[j].getAttribute('pathTo');\n              dArr.paths.push({\n                d: d\n              });\n            }\n          }\n\n          w.globals.previousPaths.push(dArr);\n        }\n\n        var getPaths = function getPaths(chartType) {\n          return w.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(chartType, \"-series .apexcharts-series\"));\n        };\n\n        var chartTypes = ['line', 'area', 'bar', 'rangebar', 'rangeArea', 'candlestick', 'radar'];\n        chartTypes.forEach(function (type) {\n          var paths = getPaths(type);\n\n          for (var p = 0; p < paths.length; p++) {\n            pushPaths(paths, p, type);\n          }\n        });\n        this.handlePrevBubbleScatterPaths('bubble');\n        this.handlePrevBubbleScatterPaths('scatter');\n        var heatTreeSeries = w.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(w.config.chart.type, \" .apexcharts-series\"));\n\n        if (heatTreeSeries.length > 0) {\n          var _loop = function _loop(h) {\n            var seriesEls = w.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(w.config.chart.type, \" .apexcharts-series[data\\\\:realIndex='\").concat(h, \"'] rect\"));\n            var dArr = [];\n\n            var _loop2 = function _loop2(i) {\n              var getAttr = function getAttr(x) {\n                return seriesEls[i].getAttribute(x);\n              };\n\n              var rect = {\n                x: parseFloat(getAttr('x')),\n                y: parseFloat(getAttr('y')),\n                width: parseFloat(getAttr('width')),\n                height: parseFloat(getAttr('height'))\n              };\n              dArr.push({\n                rect: rect,\n                color: seriesEls[i].getAttribute('color')\n              });\n            };\n\n            for (var i = 0; i < seriesEls.length; i++) {\n              _loop2(i);\n            }\n\n            w.globals.previousPaths.push(dArr);\n          };\n\n          for (var h = 0; h < heatTreeSeries.length; h++) {\n            _loop(h);\n          }\n        }\n\n        if (!w.globals.axisCharts) {\n          // for non-axis charts (i.e., circular charts, pathFrom is not usable. We need whole series)\n          w.globals.previousPaths = w.globals.series;\n        }\n      }\n    }, {\n      key: \"handlePrevBubbleScatterPaths\",\n      value: function handlePrevBubbleScatterPaths(type) {\n        var w = this.w;\n        var paths = w.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(type, \"-series .apexcharts-series\"));\n\n        if (paths.length > 0) {\n          for (var s = 0; s < paths.length; s++) {\n            var seriesEls = w.globals.dom.baseEl.querySelectorAll(\".apexcharts-\".concat(type, \"-series .apexcharts-series[data\\\\:realIndex='\").concat(s, \"'] circle\"));\n            var dArr = [];\n\n            for (var i = 0; i < seriesEls.length; i++) {\n              dArr.push({\n                x: seriesEls[i].getAttribute('cx'),\n                y: seriesEls[i].getAttribute('cy'),\n                r: seriesEls[i].getAttribute('r')\n              });\n            }\n\n            w.globals.previousPaths.push(dArr);\n          }\n        }\n      }\n    }, {\n      key: \"clearPreviousPaths\",\n      value: function clearPreviousPaths() {\n        var w = this.w;\n        w.globals.previousPaths = [];\n        w.globals.allSeriesCollapsed = false;\n      }\n    }, {\n      key: \"handleNoData\",\n      value: function handleNoData() {\n        var w = this.w;\n        var me = this;\n        var noDataOpts = w.config.noData;\n        var graphics = new Graphics(me.ctx);\n        var x = w.globals.svgWidth / 2;\n        var y = w.globals.svgHeight / 2;\n        var textAnchor = 'middle';\n        w.globals.noData = true;\n        w.globals.animationEnded = true;\n\n        if (noDataOpts.align === 'left') {\n          x = 10;\n          textAnchor = 'start';\n        } else if (noDataOpts.align === 'right') {\n          x = w.globals.svgWidth - 10;\n          textAnchor = 'end';\n        }\n\n        if (noDataOpts.verticalAlign === 'top') {\n          y = 50;\n        } else if (noDataOpts.verticalAlign === 'bottom') {\n          y = w.globals.svgHeight - 50;\n        }\n\n        x = x + noDataOpts.offsetX;\n        y = y + parseInt(noDataOpts.style.fontSize, 10) + 2 + noDataOpts.offsetY;\n\n        if (noDataOpts.text !== undefined && noDataOpts.text !== '') {\n          var titleText = graphics.drawText({\n            x: x,\n            y: y,\n            text: noDataOpts.text,\n            textAnchor: textAnchor,\n            fontSize: noDataOpts.style.fontSize,\n            fontFamily: noDataOpts.style.fontFamily,\n            foreColor: noDataOpts.style.color,\n            opacity: 1,\n            class: 'apexcharts-text-nodata'\n          });\n          w.globals.dom.Paper.add(titleText);\n        }\n      } // When user clicks on legends, the collapsed series is filled with [0,0,0,...,0]\n      // This is because we don't want to alter the series' length as it is used at many places\n\n    }, {\n      key: \"setNullSeriesToZeroValues\",\n      value: function setNullSeriesToZeroValues(series) {\n        var w = this.w;\n\n        for (var sl = 0; sl < series.length; sl++) {\n          if (series[sl].length === 0) {\n            for (var j = 0; j < series[w.globals.maxValsInArrayIndex].length; j++) {\n              series[sl].push(0);\n            }\n          }\n        }\n\n        return series;\n      }\n    }, {\n      key: \"hasAllSeriesEqualX\",\n      value: function hasAllSeriesEqualX() {\n        var equalLen = true;\n        var w = this.w;\n        var filteredSerX = this.filteredSeriesX();\n\n        for (var i = 0; i < filteredSerX.length - 1; i++) {\n          if (filteredSerX[i][0] !== filteredSerX[i + 1][0]) {\n            equalLen = false;\n            break;\n          }\n        }\n\n        w.globals.allSeriesHasEqualX = equalLen;\n        return equalLen;\n      }\n    }, {\n      key: \"filteredSeriesX\",\n      value: function filteredSeriesX() {\n        var w = this.w;\n        var filteredSeriesX = w.globals.seriesX.map(function (ser) {\n          return ser.length > 0 ? ser : [];\n        });\n        return filteredSeriesX;\n      }\n    }]);\n\n    return Series;\n  }();\n\n  var Data = /*#__PURE__*/function () {\n    function Data(ctx) {\n      _classCallCheck(this, Data);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.twoDSeries = [];\n      this.threeDSeries = [];\n      this.twoDSeriesX = [];\n      this.seriesGoals = [];\n      this.coreUtils = new CoreUtils(this.ctx);\n    }\n\n    _createClass(Data, [{\n      key: \"isMultiFormat\",\n      value: function isMultiFormat() {\n        return this.isFormatXY() || this.isFormat2DArray();\n      } // given format is [{x, y}, {x, y}]\n\n    }, {\n      key: \"isFormatXY\",\n      value: function isFormatXY() {\n        var series = this.w.config.series.slice();\n        var sr = new Series(this.ctx);\n        this.activeSeriesIndex = sr.getActiveConfigSeriesIndex();\n\n        if (typeof series[this.activeSeriesIndex].data !== 'undefined' && series[this.activeSeriesIndex].data.length > 0 && series[this.activeSeriesIndex].data[0] !== null && typeof series[this.activeSeriesIndex].data[0].x !== 'undefined' && series[this.activeSeriesIndex].data[0] !== null) {\n          return true;\n        }\n      } // given format is [[x, y], [x, y]]\n\n    }, {\n      key: \"isFormat2DArray\",\n      value: function isFormat2DArray() {\n        var series = this.w.config.series.slice();\n        var sr = new Series(this.ctx);\n        this.activeSeriesIndex = sr.getActiveConfigSeriesIndex();\n\n        if (typeof series[this.activeSeriesIndex].data !== 'undefined' && series[this.activeSeriesIndex].data.length > 0 && typeof series[this.activeSeriesIndex].data[0] !== 'undefined' && series[this.activeSeriesIndex].data[0] !== null && series[this.activeSeriesIndex].data[0].constructor === Array) {\n          return true;\n        }\n      }\n    }, {\n      key: \"handleFormat2DArray\",\n      value: function handleFormat2DArray(ser, i) {\n        var cnf = this.w.config;\n        var gl = this.w.globals;\n        var isBoxPlot = cnf.chart.type === 'boxPlot' || cnf.series[i].type === 'boxPlot';\n\n        for (var j = 0; j < ser[i].data.length; j++) {\n          if (typeof ser[i].data[j][1] !== 'undefined') {\n            if (Array.isArray(ser[i].data[j][1]) && ser[i].data[j][1].length === 4 && !isBoxPlot) {\n              // candlestick nested ohlc format\n              this.twoDSeries.push(Utils$1.parseNumber(ser[i].data[j][1][3]));\n            } else if (ser[i].data[j].length >= 5) {\n              // candlestick non-nested ohlc format\n              this.twoDSeries.push(Utils$1.parseNumber(ser[i].data[j][4]));\n            } else {\n              this.twoDSeries.push(Utils$1.parseNumber(ser[i].data[j][1]));\n            }\n\n            gl.dataFormatXNumeric = true;\n          }\n\n          if (cnf.xaxis.type === 'datetime') {\n            // if timestamps are provided and xaxis type is datetime,\n            var ts = new Date(ser[i].data[j][0]);\n            ts = new Date(ts).getTime();\n            this.twoDSeriesX.push(ts);\n          } else {\n            this.twoDSeriesX.push(ser[i].data[j][0]);\n          }\n        }\n\n        for (var _j = 0; _j < ser[i].data.length; _j++) {\n          if (typeof ser[i].data[_j][2] !== 'undefined') {\n            this.threeDSeries.push(ser[i].data[_j][2]);\n            gl.isDataXYZ = true;\n          }\n        }\n      }\n    }, {\n      key: \"handleFormatXY\",\n      value: function handleFormatXY(ser, i) {\n        var cnf = this.w.config;\n        var gl = this.w.globals;\n        var dt = new DateTime(this.ctx);\n        var activeI = i;\n\n        if (gl.collapsedSeriesIndices.indexOf(i) > -1) {\n          // fix #368\n          activeI = this.activeSeriesIndex;\n        } // get series\n\n\n        for (var j = 0; j < ser[i].data.length; j++) {\n          if (typeof ser[i].data[j].y !== 'undefined') {\n            if (Array.isArray(ser[i].data[j].y)) {\n              this.twoDSeries.push(Utils$1.parseNumber(ser[i].data[j].y[ser[i].data[j].y.length - 1]));\n            } else {\n              this.twoDSeries.push(Utils$1.parseNumber(ser[i].data[j].y));\n            }\n          }\n\n          if (typeof ser[i].data[j].goals !== 'undefined' && Array.isArray(ser[i].data[j].goals)) {\n            if (typeof this.seriesGoals[i] === 'undefined') {\n              this.seriesGoals[i] = [];\n            }\n\n            this.seriesGoals[i].push(ser[i].data[j].goals);\n          } else {\n            if (typeof this.seriesGoals[i] === 'undefined') {\n              this.seriesGoals[i] = [];\n            }\n\n            this.seriesGoals[i].push(null);\n          }\n        } // get seriesX\n\n\n        for (var _j2 = 0; _j2 < ser[activeI].data.length; _j2++) {\n          var isXString = typeof ser[activeI].data[_j2].x === 'string';\n          var isXArr = Array.isArray(ser[activeI].data[_j2].x);\n          var isXDate = !isXArr && !!dt.isValidDate(ser[activeI].data[_j2].x.toString());\n\n          if (isXString || isXDate) {\n            // user supplied '01/01/2017' or a date string (a JS date object is not supported)\n            if (isXString || cnf.xaxis.convertedCatToNumeric) {\n              var isRangeColumn = gl.isBarHorizontal && gl.isRangeData;\n\n              if (cnf.xaxis.type === 'datetime' && !isRangeColumn) {\n                this.twoDSeriesX.push(dt.parseDate(ser[activeI].data[_j2].x));\n              } else {\n                // a category and not a numeric x value\n                this.fallbackToCategory = true;\n                this.twoDSeriesX.push(ser[activeI].data[_j2].x);\n              }\n            } else {\n              if (cnf.xaxis.type === 'datetime') {\n                this.twoDSeriesX.push(dt.parseDate(ser[activeI].data[_j2].x.toString()));\n              } else {\n                gl.dataFormatXNumeric = true;\n                gl.isXNumeric = true;\n                this.twoDSeriesX.push(parseFloat(ser[activeI].data[_j2].x));\n              }\n            }\n          } else if (isXArr) {\n            // a multiline label described in array format\n            this.fallbackToCategory = true;\n            this.twoDSeriesX.push(ser[activeI].data[_j2].x);\n          } else {\n            // a numeric value in x property\n            gl.isXNumeric = true;\n            gl.dataFormatXNumeric = true;\n            this.twoDSeriesX.push(ser[activeI].data[_j2].x);\n          }\n        }\n\n        if (ser[i].data[0] && typeof ser[i].data[0].z !== 'undefined') {\n          for (var t = 0; t < ser[i].data.length; t++) {\n            this.threeDSeries.push(ser[i].data[t].z);\n          }\n\n          gl.isDataXYZ = true;\n        }\n      }\n    }, {\n      key: \"handleRangeData\",\n      value: function handleRangeData(ser, i) {\n        var gl = this.w.globals;\n        var range = {};\n\n        if (this.isFormat2DArray()) {\n          range = this.handleRangeDataFormat('array', ser, i);\n        } else if (this.isFormatXY()) {\n          range = this.handleRangeDataFormat('xy', ser, i);\n        }\n\n        gl.seriesRangeStart.push(range.start);\n        gl.seriesRangeEnd.push(range.end);\n        gl.seriesRange.push(range.rangeUniques); // check for overlaps to avoid clashes in a timeline chart\n\n        gl.seriesRange.forEach(function (sr, si) {\n          if (sr) {\n            sr.forEach(function (sarr, sarri) {\n              sarr.y.forEach(function (arr, arri) {\n                for (var sri = 0; sri < sarr.y.length; sri++) {\n                  if (arri !== sri) {\n                    var range1y1 = arr.y1;\n                    var range1y2 = arr.y2;\n                    var range2y1 = sarr.y[sri].y1;\n                    var range2y2 = sarr.y[sri].y2;\n\n                    if (range1y1 <= range2y2 && range2y1 <= range1y2) {\n                      if (sarr.overlaps.indexOf(arr.rangeName) < 0) {\n                        sarr.overlaps.push(arr.rangeName);\n                      }\n\n                      if (sarr.overlaps.indexOf(sarr.y[sri].rangeName) < 0) {\n                        sarr.overlaps.push(sarr.y[sri].rangeName);\n                      }\n                    }\n                  }\n                }\n              });\n            });\n          }\n        });\n        return range;\n      }\n    }, {\n      key: \"handleCandleStickBoxData\",\n      value: function handleCandleStickBoxData(ser, i) {\n        var gl = this.w.globals;\n        var ohlc = {};\n\n        if (this.isFormat2DArray()) {\n          ohlc = this.handleCandleStickBoxDataFormat('array', ser, i);\n        } else if (this.isFormatXY()) {\n          ohlc = this.handleCandleStickBoxDataFormat('xy', ser, i);\n        }\n\n        gl.seriesCandleO[i] = ohlc.o;\n        gl.seriesCandleH[i] = ohlc.h;\n        gl.seriesCandleM[i] = ohlc.m;\n        gl.seriesCandleL[i] = ohlc.l;\n        gl.seriesCandleC[i] = ohlc.c;\n        return ohlc;\n      }\n    }, {\n      key: \"handleRangeDataFormat\",\n      value: function handleRangeDataFormat(format, ser, i) {\n        var rangeStart = [];\n        var rangeEnd = [];\n        var uniqueKeys = ser[i].data.filter(function (thing, index, self) {\n          return index === self.findIndex(function (t) {\n            return t.x === thing.x;\n          });\n        }).map(function (r, index) {\n          return {\n            x: r.x,\n            overlaps: [],\n            y: []\n          };\n        });\n\n        if (format === 'array') {\n          for (var j = 0; j < ser[i].data.length; j++) {\n            if (Array.isArray(ser[i].data[j])) {\n              rangeStart.push(ser[i].data[j][1][0]);\n              rangeEnd.push(ser[i].data[j][1][1]);\n            } else {\n              rangeStart.push(ser[i].data[j]);\n              rangeEnd.push(ser[i].data[j]);\n            }\n          }\n        } else if (format === 'xy') {\n          var _loop = function _loop(_j3) {\n            var isDataPoint2D = Array.isArray(ser[i].data[_j3].y);\n            var id = Utils$1.randomId();\n            var x = ser[i].data[_j3].x;\n            var y = {\n              y1: isDataPoint2D ? ser[i].data[_j3].y[0] : ser[i].data[_j3].y,\n              y2: isDataPoint2D ? ser[i].data[_j3].y[1] : ser[i].data[_j3].y,\n              rangeName: id\n            }; // mutating config object by adding a new property\n            // TODO: As this is specifically for timeline rangebar charts, update the docs mentioning the series only supports xy format\n\n            ser[i].data[_j3].rangeName = id;\n            var uI = uniqueKeys.findIndex(function (t) {\n              return t.x === x;\n            });\n            uniqueKeys[uI].y.push(y);\n            rangeStart.push(y.y1);\n            rangeEnd.push(y.y2);\n          };\n\n          for (var _j3 = 0; _j3 < ser[i].data.length; _j3++) {\n            _loop(_j3);\n          }\n        }\n\n        return {\n          start: rangeStart,\n          end: rangeEnd,\n          rangeUniques: uniqueKeys\n        };\n      }\n    }, {\n      key: \"handleCandleStickBoxDataFormat\",\n      value: function handleCandleStickBoxDataFormat(format, ser, i) {\n        var w = this.w;\n        var isBoxPlot = w.config.chart.type === 'boxPlot' || w.config.series[i].type === 'boxPlot';\n        var serO = [];\n        var serH = [];\n        var serM = [];\n        var serL = [];\n        var serC = [];\n\n        if (format === 'array') {\n          if (isBoxPlot && ser[i].data[0].length === 6 || !isBoxPlot && ser[i].data[0].length === 5) {\n            for (var j = 0; j < ser[i].data.length; j++) {\n              serO.push(ser[i].data[j][1]);\n              serH.push(ser[i].data[j][2]);\n\n              if (isBoxPlot) {\n                serM.push(ser[i].data[j][3]);\n                serL.push(ser[i].data[j][4]);\n                serC.push(ser[i].data[j][5]);\n              } else {\n                serL.push(ser[i].data[j][3]);\n                serC.push(ser[i].data[j][4]);\n              }\n            }\n          } else {\n            for (var _j4 = 0; _j4 < ser[i].data.length; _j4++) {\n              if (Array.isArray(ser[i].data[_j4][1])) {\n                serO.push(ser[i].data[_j4][1][0]);\n                serH.push(ser[i].data[_j4][1][1]);\n\n                if (isBoxPlot) {\n                  serM.push(ser[i].data[_j4][1][2]);\n                  serL.push(ser[i].data[_j4][1][3]);\n                  serC.push(ser[i].data[_j4][1][4]);\n                } else {\n                  serL.push(ser[i].data[_j4][1][2]);\n                  serC.push(ser[i].data[_j4][1][3]);\n                }\n              }\n            }\n          }\n        } else if (format === 'xy') {\n          for (var _j5 = 0; _j5 < ser[i].data.length; _j5++) {\n            if (Array.isArray(ser[i].data[_j5].y)) {\n              serO.push(ser[i].data[_j5].y[0]);\n              serH.push(ser[i].data[_j5].y[1]);\n\n              if (isBoxPlot) {\n                serM.push(ser[i].data[_j5].y[2]);\n                serL.push(ser[i].data[_j5].y[3]);\n                serC.push(ser[i].data[_j5].y[4]);\n              } else {\n                serL.push(ser[i].data[_j5].y[2]);\n                serC.push(ser[i].data[_j5].y[3]);\n              }\n            }\n          }\n        }\n\n        return {\n          o: serO,\n          h: serH,\n          m: serM,\n          l: serL,\n          c: serC\n        };\n      }\n    }, {\n      key: \"parseDataAxisCharts\",\n      value: function parseDataAxisCharts(ser) {\n        var _this = this;\n\n        var ctx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.ctx;\n        var cnf = this.w.config;\n        var gl = this.w.globals;\n        var dt = new DateTime(ctx);\n        var xlabels = cnf.labels.length > 0 ? cnf.labels.slice() : cnf.xaxis.categories.slice();\n        gl.isRangeBar = cnf.chart.type === 'rangeBar' && gl.isBarHorizontal;\n        gl.hasGroups = cnf.xaxis.type === 'category' && cnf.xaxis.group.groups.length > 0;\n\n        if (gl.hasGroups) {\n          gl.groups = cnf.xaxis.group.groups;\n        }\n\n        var handleDates = function handleDates() {\n          for (var j = 0; j < xlabels.length; j++) {\n            if (typeof xlabels[j] === 'string') {\n              // user provided date strings\n              var isDate = dt.isValidDate(xlabels[j]);\n\n              if (isDate) {\n                _this.twoDSeriesX.push(dt.parseDate(xlabels[j]));\n              } else {\n                throw new Error('You have provided invalid Date format. Please provide a valid JavaScript Date');\n              }\n            } else {\n              // user provided timestamps\n              _this.twoDSeriesX.push(xlabels[j]);\n            }\n          }\n        };\n\n        for (var i = 0; i < ser.length; i++) {\n          this.twoDSeries = [];\n          this.twoDSeriesX = [];\n          this.threeDSeries = [];\n\n          if (typeof ser[i].data === 'undefined') {\n            console.error(\"It is a possibility that you may have not included 'data' property in series.\");\n            return;\n          }\n\n          if (cnf.chart.type === 'rangeBar' || cnf.chart.type === 'rangeArea' || ser[i].type === 'rangeBar' || ser[i].type === 'rangeArea') {\n            gl.isRangeData = true;\n\n            if (gl.isComboCharts) {\n              if (ser[i].type === 'rangeBar' || ser[i].type === 'rangeArea') {\n                this.handleRangeData(ser, i);\n              }\n            } else if (cnf.chart.type === 'rangeBar' || cnf.chart.type === 'rangeArea') {\n              this.handleRangeData(ser, i);\n            }\n          }\n\n          if (this.isMultiFormat()) {\n            if (this.isFormat2DArray()) {\n              this.handleFormat2DArray(ser, i);\n            } else if (this.isFormatXY()) {\n              this.handleFormatXY(ser, i);\n            }\n\n            if (cnf.chart.type === 'candlestick' || ser[i].type === 'candlestick' || cnf.chart.type === 'boxPlot' || ser[i].type === 'boxPlot') {\n              this.handleCandleStickBoxData(ser, i);\n            }\n\n            gl.series.push(this.twoDSeries);\n            gl.labels.push(this.twoDSeriesX);\n            gl.seriesX.push(this.twoDSeriesX);\n            gl.seriesGoals = this.seriesGoals;\n\n            if (i === this.activeSeriesIndex && !this.fallbackToCategory) {\n              gl.isXNumeric = true;\n            }\n          } else {\n            if (cnf.xaxis.type === 'datetime') {\n              // user didn't supplied [{x,y}] or [[x,y]], but single array in data.\n              // Also labels/categories were supplied differently\n              gl.isXNumeric = true;\n              handleDates();\n              gl.seriesX.push(this.twoDSeriesX);\n            } else if (cnf.xaxis.type === 'numeric') {\n              gl.isXNumeric = true;\n\n              if (xlabels.length > 0) {\n                this.twoDSeriesX = xlabels;\n                gl.seriesX.push(this.twoDSeriesX);\n              }\n            }\n\n            gl.labels.push(this.twoDSeriesX);\n            var singleArray = ser[i].data.map(function (d) {\n              return Utils$1.parseNumber(d);\n            });\n            gl.series.push(singleArray);\n          }\n\n          gl.seriesZ.push(this.threeDSeries);\n\n          if (ser[i].name !== undefined) {\n            gl.seriesNames.push(ser[i].name);\n          } else {\n            gl.seriesNames.push('series-' + parseInt(i + 1, 10));\n          } // overrided default color if user inputs color with series data\n\n\n          if (ser[i].color !== undefined) {\n            gl.seriesColors.push(ser[i].color);\n          } else {\n            gl.seriesColors.push(undefined);\n          }\n        }\n\n        return this.w;\n      }\n    }, {\n      key: \"parseDataNonAxisCharts\",\n      value: function parseDataNonAxisCharts(ser) {\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n        gl.series = ser.slice();\n        gl.seriesNames = cnf.labels.slice();\n\n        for (var i = 0; i < gl.series.length; i++) {\n          if (gl.seriesNames[i] === undefined) {\n            gl.seriesNames.push('series-' + (i + 1));\n          }\n        }\n\n        return this.w;\n      }\n      /** User possibly set string categories in xaxis.categories or labels prop\n       * Or didn't set xaxis labels at all - in which case we manually do it.\n       * If user passed series data as [[3, 2], [4, 5]] or [{ x: 3, y: 55 }],\n       * this shouldn't be called\n       * @param {array} ser - the series which user passed to the config\n       */\n\n    }, {\n      key: \"handleExternalLabelsData\",\n      value: function handleExternalLabelsData(ser) {\n        var cnf = this.w.config;\n        var gl = this.w.globals;\n\n        if (cnf.xaxis.categories.length > 0) {\n          // user provided labels in xaxis.category prop\n          gl.labels = cnf.xaxis.categories;\n        } else if (cnf.labels.length > 0) {\n          // user provided labels in labels props\n          gl.labels = cnf.labels.slice();\n        } else if (this.fallbackToCategory) {\n          // user provided labels in x prop in [{ x: 3, y: 55 }] data, and those labels are already stored in gl.labels[0], so just re-arrange the gl.labels array\n          gl.labels = gl.labels[0];\n\n          if (gl.seriesRange.length) {\n            gl.seriesRange.map(function (srt) {\n              srt.forEach(function (sr) {\n                if (gl.labels.indexOf(sr.x) < 0 && sr.x) {\n                  gl.labels.push(sr.x);\n                }\n              });\n            });\n            gl.labels = gl.labels.filter(function (elem, pos, arr) {\n              return arr.indexOf(elem) === pos;\n            });\n          }\n\n          if (cnf.xaxis.convertedCatToNumeric) {\n            var defaults = new Defaults(cnf);\n            defaults.convertCatToNumericXaxis(cnf, this.ctx, gl.seriesX[0]);\n\n            this._generateExternalLabels(ser);\n          }\n        } else {\n          this._generateExternalLabels(ser);\n        }\n      }\n    }, {\n      key: \"_generateExternalLabels\",\n      value: function _generateExternalLabels(ser) {\n        var gl = this.w.globals;\n        var cnf = this.w.config; // user didn't provided any labels, fallback to 1-2-3-4-5\n\n        var labelArr = [];\n\n        if (gl.axisCharts) {\n          if (gl.series.length > 0) {\n            if (this.isFormatXY()) {\n              // in case there is a combo chart (boxplot/scatter)\n              // and there are duplicated x values, we need to eliminate duplicates\n              var seriesDataFiltered = cnf.series.map(function (serie, s) {\n                return serie.data.filter(function (v, i, a) {\n                  return a.findIndex(function (t) {\n                    return t.x === v.x;\n                  }) === i;\n                });\n              });\n              var len = seriesDataFiltered.reduce(function (p, c, i, a) {\n                return a[p].length > c.length ? p : i;\n              }, 0);\n\n              for (var i = 0; i < seriesDataFiltered[len].length; i++) {\n                labelArr.push(i + 1);\n              }\n            } else {\n              for (var _i = 0; _i < gl.series[gl.maxValsInArrayIndex].length; _i++) {\n                labelArr.push(_i + 1);\n              }\n            }\n          }\n\n          gl.seriesX = []; // create gl.seriesX as it will be used in calculations of x positions\n\n          for (var _i2 = 0; _i2 < ser.length; _i2++) {\n            gl.seriesX.push(labelArr);\n          } // turn on the isXNumeric flag to allow minX and maxX to function properly\n\n\n          gl.isXNumeric = true;\n        } // no series to pull labels from, put a 0-10 series\n        // possibly, user collapsed all series. Hence we can't work with above calc\n\n\n        if (labelArr.length === 0) {\n          labelArr = gl.axisCharts ? [] : gl.series.map(function (gls, glsi) {\n            return glsi + 1;\n          });\n\n          for (var _i3 = 0; _i3 < ser.length; _i3++) {\n            gl.seriesX.push(labelArr);\n          }\n        } // Finally, pass the labelArr in gl.labels which will be printed on x-axis\n\n\n        gl.labels = labelArr;\n\n        if (cnf.xaxis.convertedCatToNumeric) {\n          gl.categoryLabels = labelArr.map(function (l) {\n            return cnf.xaxis.labels.formatter(l);\n          });\n        } // Turn on this global flag to indicate no labels were provided by user\n\n\n        gl.noLabelsProvided = true;\n      } // Segregate user provided data into appropriate vars\n\n    }, {\n      key: \"parseData\",\n      value: function parseData(ser) {\n        var w = this.w;\n        var cnf = w.config;\n        var gl = w.globals;\n        this.excludeCollapsedSeriesInYAxis(); // If we detected string in X prop of series, we fallback to category x-axis\n\n        this.fallbackToCategory = false;\n        this.ctx.core.resetGlobals();\n        this.ctx.core.isMultipleY();\n\n        if (gl.axisCharts) {\n          // axisCharts includes line / area / column / scatter\n          this.parseDataAxisCharts(ser);\n          this.coreUtils.getLargestSeries();\n        } else {\n          // non-axis charts are pie / donut\n          this.parseDataNonAxisCharts(ser);\n        } // set Null values to 0 in all series when user hides/shows some series\n\n\n        if (cnf.chart.type === 'bar' && cnf.chart.stacked) {\n          var series = new Series(this.ctx);\n          gl.series = series.setNullSeriesToZeroValues(gl.series);\n        }\n\n        this.coreUtils.getSeriesTotals();\n\n        if (gl.axisCharts) {\n          gl.stackedSeriesTotals = this.coreUtils.getStackedSeriesTotals();\n        }\n\n        this.coreUtils.getPercentSeries();\n\n        if (!gl.dataFormatXNumeric && (!gl.isXNumeric || cnf.xaxis.type === 'numeric' && cnf.labels.length === 0 && cnf.xaxis.categories.length === 0)) {\n          // x-axis labels couldn't be detected; hence try searching every option in config\n          this.handleExternalLabelsData(ser);\n        } // check for multiline xaxis\n\n\n        var catLabels = this.coreUtils.getCategoryLabels(gl.labels);\n\n        for (var l = 0; l < catLabels.length; l++) {\n          if (Array.isArray(catLabels[l])) {\n            gl.isMultiLineX = true;\n            break;\n          }\n        }\n      }\n    }, {\n      key: \"excludeCollapsedSeriesInYAxis\",\n      value: function excludeCollapsedSeriesInYAxis() {\n        var _this2 = this;\n\n        var w = this.w;\n        w.globals.ignoreYAxisIndexes = w.globals.collapsedSeries.map(function (collapsed, i) {\n          // fix issue #1215\n          // if stacked, not returning collapsed.index to preserve yaxis\n          if (_this2.w.globals.isMultipleYAxis && !w.config.chart.stacked) {\n            return collapsed.index;\n          }\n        });\n      }\n    }]);\n\n    return Data;\n  }();\n\n  var AxesUtils = /*#__PURE__*/function () {\n    function AxesUtils(ctx) {\n      _classCallCheck(this, AxesUtils);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    } // Based on the formatter function, get the label text and position\n\n\n    _createClass(AxesUtils, [{\n      key: \"getLabel\",\n      value: function getLabel(labels, timescaleLabels, x, i) {\n        var drawnLabels = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];\n        var fontSize = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : '12px';\n        var isLeafGroup = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : true;\n        var w = this.w;\n        var rawLabel = typeof labels[i] === 'undefined' ? '' : labels[i];\n        var label = rawLabel;\n        var xlbFormatter = w.globals.xLabelFormatter;\n        var customFormatter = w.config.xaxis.labels.formatter;\n        var isBold = false;\n        var xFormat = new Formatters(this.ctx);\n        var timestamp = rawLabel;\n\n        if (isLeafGroup) {\n          label = xFormat.xLabelFormat(xlbFormatter, rawLabel, timestamp, {\n            i: i,\n            dateFormatter: new DateTime(this.ctx).formatDate,\n            w: w\n          });\n\n          if (customFormatter !== undefined) {\n            label = customFormatter(rawLabel, labels[i], {\n              i: i,\n              dateFormatter: new DateTime(this.ctx).formatDate,\n              w: w\n            });\n          }\n        }\n\n        var determineHighestUnit = function determineHighestUnit(unit) {\n          var highestUnit = null;\n          timescaleLabels.forEach(function (t) {\n            if (t.unit === 'month') {\n              highestUnit = 'year';\n            } else if (t.unit === 'day') {\n              highestUnit = 'month';\n            } else if (t.unit === 'hour') {\n              highestUnit = 'day';\n            } else if (t.unit === 'minute') {\n              highestUnit = 'hour';\n            }\n          });\n          return highestUnit === unit;\n        };\n\n        if (timescaleLabels.length > 0) {\n          isBold = determineHighestUnit(timescaleLabels[i].unit);\n          x = timescaleLabels[i].position;\n          label = timescaleLabels[i].value;\n        } else {\n          if (w.config.xaxis.type === 'datetime' && customFormatter === undefined) {\n            label = '';\n          }\n        }\n\n        if (typeof label === 'undefined') label = '';\n        label = Array.isArray(label) ? label : label.toString();\n        var graphics = new Graphics(this.ctx);\n        var textRect = {};\n\n        if (w.globals.rotateXLabels && isLeafGroup) {\n          textRect = graphics.getTextRects(label, parseInt(fontSize, 10), null, \"rotate(\".concat(w.config.xaxis.labels.rotate, \" 0 0)\"), false);\n        } else {\n          textRect = graphics.getTextRects(label, parseInt(fontSize, 10));\n        }\n\n        var allowDuplicatesInTimeScale = !w.config.xaxis.labels.showDuplicates && this.ctx.timeScale;\n\n        if (!Array.isArray(label) && (label.indexOf('NaN') === 0 || label.toLowerCase().indexOf('invalid') === 0 || label.toLowerCase().indexOf('infinity') >= 0 || drawnLabels.indexOf(label) >= 0 && allowDuplicatesInTimeScale)) {\n          label = '';\n        }\n\n        return {\n          x: x,\n          text: label,\n          textRect: textRect,\n          isBold: isBold\n        };\n      }\n    }, {\n      key: \"checkLabelBasedOnTickamount\",\n      value: function checkLabelBasedOnTickamount(i, label, labelsLen) {\n        var w = this.w;\n        var ticks = w.config.xaxis.tickAmount;\n        if (ticks === 'dataPoints') ticks = Math.round(w.globals.gridWidth / 120);\n        if (ticks > labelsLen) return label;\n        var tickMultiple = Math.round(labelsLen / (ticks + 1));\n\n        if (i % tickMultiple === 0) {\n          return label;\n        } else {\n          label.text = '';\n        }\n\n        return label;\n      }\n    }, {\n      key: \"checkForOverflowingLabels\",\n      value: function checkForOverflowingLabels(i, label, labelsLen, drawnLabels, drawnLabelsRects) {\n        var w = this.w;\n\n        if (i === 0) {\n          // check if first label is being truncated\n          if (w.globals.skipFirstTimelinelabel) {\n            label.text = '';\n          }\n        }\n\n        if (i === labelsLen - 1) {\n          // check if last label is being truncated\n          if (w.globals.skipLastTimelinelabel) {\n            label.text = '';\n          }\n        }\n\n        if (w.config.xaxis.labels.hideOverlappingLabels && drawnLabels.length > 0) {\n          var prev = drawnLabelsRects[drawnLabelsRects.length - 1];\n\n          if (label.x < prev.textRect.width / (w.globals.rotateXLabels ? Math.abs(w.config.xaxis.labels.rotate) / 12 : 1.01) + prev.x) {\n            label.text = '';\n          }\n        }\n\n        return label;\n      }\n    }, {\n      key: \"checkForReversedLabels\",\n      value: function checkForReversedLabels(i, labels) {\n        var w = this.w;\n\n        if (w.config.yaxis[i] && w.config.yaxis[i].reversed) {\n          labels.reverse();\n        }\n\n        return labels;\n      }\n    }, {\n      key: \"isYAxisHidden\",\n      value: function isYAxisHidden(index) {\n        var w = this.w;\n        var coreUtils = new CoreUtils(this.ctx);\n        return !w.config.yaxis[index].show || !w.config.yaxis[index].showForNullSeries && coreUtils.isSeriesNull(index) && w.globals.collapsedSeriesIndices.indexOf(index) === -1;\n      } // get the label color for y-axis\n      // realIndex is the actual series index, while i is the tick Index\n\n    }, {\n      key: \"getYAxisForeColor\",\n      value: function getYAxisForeColor(yColors, realIndex) {\n        var w = this.w;\n\n        if (Array.isArray(yColors) && w.globals.yAxisScale[realIndex]) {\n          this.ctx.theme.pushExtraColors(yColors, w.globals.yAxisScale[realIndex].result.length, false);\n        }\n\n        return yColors;\n      }\n    }, {\n      key: \"drawYAxisTicks\",\n      value: function drawYAxisTicks(x, tickAmount, axisBorder, axisTicks, realIndex, labelsDivider, elYaxis) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx); // initial label position = 0;\n\n        var t = w.globals.translateY;\n\n        if (axisTicks.show && tickAmount > 0) {\n          if (w.config.yaxis[realIndex].opposite === true) x = x + axisTicks.width;\n\n          for (var i = tickAmount; i >= 0; i--) {\n            var tY = t + tickAmount / 10 + w.config.yaxis[realIndex].labels.offsetY - 1;\n\n            if (w.globals.isBarHorizontal) {\n              tY = labelsDivider * i;\n            }\n\n            if (w.config.chart.type === 'heatmap') {\n              tY = tY + labelsDivider / 2;\n            }\n\n            var elTick = graphics.drawLine(x + axisBorder.offsetX - axisTicks.width + axisTicks.offsetX, tY + axisTicks.offsetY, x + axisBorder.offsetX + axisTicks.offsetX, tY + axisTicks.offsetY, axisTicks.color);\n            elYaxis.add(elTick);\n            t = t + labelsDivider;\n          }\n        }\n      }\n    }]);\n\n    return AxesUtils;\n  }();\n\n  var Exports = /*#__PURE__*/function () {\n    function Exports(ctx) {\n      _classCallCheck(this, Exports);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(Exports, [{\n      key: \"scaleSvgNode\",\n      value: function scaleSvgNode(svg, scale) {\n        // get current both width and height of the svg\n        var svgWidth = parseFloat(svg.getAttributeNS(null, 'width'));\n        var svgHeight = parseFloat(svg.getAttributeNS(null, 'height')); // set new width and height based on the scale\n\n        svg.setAttributeNS(null, 'width', svgWidth * scale);\n        svg.setAttributeNS(null, 'height', svgHeight * scale);\n        svg.setAttributeNS(null, 'viewBox', '0 0 ' + svgWidth + ' ' + svgHeight);\n      }\n    }, {\n      key: \"fixSvgStringForIe11\",\n      value: function fixSvgStringForIe11(svgData) {\n        // IE11 generates broken SVG that we have to fix by using regex\n        if (!Utils$1.isIE11()) {\n          // not IE11 - noop\n          return svgData.replace(/&nbsp;/g, '&#160;');\n        } // replace second occurrence of \"xmlns\" attribute with \"xmlns:xlink\" with correct url + add xmlns:svgjs\n\n\n        var nXmlnsSeen = 0;\n        var result = svgData.replace(/xmlns=\"http:\\/\\/www.w3.org\\/2000\\/svg\"/g, function (match) {\n          nXmlnsSeen++;\n          return nXmlnsSeen === 2 ? 'xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:svgjs=\"http://svgjs.dev\"' : match;\n        }); // remove the invalid empty namespace declarations\n\n        result = result.replace(/xmlns:NS\\d+=\"\"/g, ''); // remove these broken namespaces from attributes\n\n        result = result.replace(/NS\\d+:(\\w+:\\w+=\")/g, '$1');\n        return result;\n      }\n    }, {\n      key: \"getSvgString\",\n      value: function getSvgString(scale) {\n        if (scale == undefined) {\n          scale = 1; // if no scale is specified, don't scale...\n        }\n\n        var svgString = this.w.globals.dom.Paper.svg(); // in case the scale is different than 1, the svg needs to be rescaled\n\n        if (scale !== 1) {\n          // clone the svg node so it remains intact in the UI\n          var svgNode = this.w.globals.dom.Paper.node.cloneNode(true); // scale the image\n\n          this.scaleSvgNode(svgNode, scale); // get the string representation of the svgNode\n\n          svgString = new XMLSerializer().serializeToString(svgNode);\n        }\n\n        return this.fixSvgStringForIe11(svgString);\n      }\n    }, {\n      key: \"cleanup\",\n      value: function cleanup() {\n        var w = this.w; // hide some elements to avoid printing them on exported svg\n\n        var xcrosshairs = w.globals.dom.baseEl.getElementsByClassName('apexcharts-xcrosshairs');\n        var ycrosshairs = w.globals.dom.baseEl.getElementsByClassName('apexcharts-ycrosshairs');\n        var zoomSelectionRects = w.globals.dom.baseEl.querySelectorAll('.apexcharts-zoom-rect, .apexcharts-selection-rect');\n        Array.prototype.forEach.call(zoomSelectionRects, function (z) {\n          z.setAttribute('width', 0);\n        });\n\n        if (xcrosshairs && xcrosshairs[0]) {\n          xcrosshairs[0].setAttribute('x', -500);\n          xcrosshairs[0].setAttribute('x1', -500);\n          xcrosshairs[0].setAttribute('x2', -500);\n        }\n\n        if (ycrosshairs && ycrosshairs[0]) {\n          ycrosshairs[0].setAttribute('y', -100);\n          ycrosshairs[0].setAttribute('y1', -100);\n          ycrosshairs[0].setAttribute('y2', -100);\n        }\n      }\n    }, {\n      key: \"svgUrl\",\n      value: function svgUrl() {\n        this.cleanup();\n        var svgData = this.getSvgString();\n        var svgBlob = new Blob([svgData], {\n          type: 'image/svg+xml;charset=utf-8'\n        });\n        return URL.createObjectURL(svgBlob);\n      }\n    }, {\n      key: \"dataURI\",\n      value: function dataURI(options) {\n        var _this = this;\n\n        return new Promise(function (resolve) {\n          var w = _this.w;\n          var scale = options ? options.scale || options.width / w.globals.svgWidth : 1;\n\n          _this.cleanup();\n\n          var canvas = document.createElement('canvas');\n          canvas.width = w.globals.svgWidth * scale;\n          canvas.height = parseInt(w.globals.dom.elWrap.style.height, 10) * scale; // because of resizeNonAxisCharts\n\n          var canvasBg = w.config.chart.background === 'transparent' ? '#fff' : w.config.chart.background;\n          var ctx = canvas.getContext('2d');\n          ctx.fillStyle = canvasBg;\n          ctx.fillRect(0, 0, canvas.width * scale, canvas.height * scale);\n\n          var svgData = _this.getSvgString(scale);\n\n          if (window.canvg && Utils$1.isIE11()) {\n            // use canvg as a polyfill to workaround ie11 considering a canvas with loaded svg 'unsafe'\n            // without ignoreClear we lose our background color; without ignoreDimensions some grid lines become invisible\n            var v = window.canvg.Canvg.fromString(ctx, svgData, {\n              ignoreClear: true,\n              ignoreDimensions: true\n            }); // render the svg to canvas\n\n            v.start();\n            var blob = canvas.msToBlob(); // dispose - missing this will cause a memory leak\n\n            v.stop();\n            resolve({\n              blob: blob\n            });\n          } else {\n            var svgUrl = 'data:image/svg+xml,' + encodeURIComponent(svgData);\n            var img = new Image();\n            img.crossOrigin = 'anonymous';\n\n            img.onload = function () {\n              ctx.drawImage(img, 0, 0);\n\n              if (canvas.msToBlob) {\n                // IE and Edge can't navigate to data urls, so we return the blob instead\n                var _blob = canvas.msToBlob();\n\n                resolve({\n                  blob: _blob\n                });\n              } else {\n                var imgURI = canvas.toDataURL('image/png');\n                resolve({\n                  imgURI: imgURI\n                });\n              }\n            };\n\n            img.src = svgUrl;\n          }\n        });\n      }\n    }, {\n      key: \"exportToSVG\",\n      value: function exportToSVG() {\n        this.triggerDownload(this.svgUrl(), this.w.config.chart.toolbar.export.svg.filename, '.svg');\n      }\n    }, {\n      key: \"exportToPng\",\n      value: function exportToPng() {\n        var _this2 = this;\n\n        this.dataURI().then(function (_ref) {\n          var imgURI = _ref.imgURI,\n              blob = _ref.blob;\n\n          if (blob) {\n            navigator.msSaveOrOpenBlob(blob, _this2.w.globals.chartID + '.png');\n          } else {\n            _this2.triggerDownload(imgURI, _this2.w.config.chart.toolbar.export.png.filename, '.png');\n          }\n        });\n      }\n    }, {\n      key: \"exportToCSV\",\n      value: function exportToCSV(_ref2) {\n        var _this3 = this;\n\n        var series = _ref2.series,\n            fileName = _ref2.fileName,\n            _ref2$columnDelimiter = _ref2.columnDelimiter,\n            columnDelimiter = _ref2$columnDelimiter === void 0 ? ',' : _ref2$columnDelimiter,\n            _ref2$lineDelimiter = _ref2.lineDelimiter,\n            lineDelimiter = _ref2$lineDelimiter === void 0 ? '\\n' : _ref2$lineDelimiter;\n        var w = this.w;\n        if (!series) series = w.config.series;\n        var columns = [];\n        var rows = [];\n        var result = '';\n        var universalBOM = \"\\uFEFF\";\n        var gSeries = w.globals.series.map(function (s, i) {\n          return w.globals.collapsedSeriesIndices.indexOf(i) === -1 ? s : [];\n        });\n\n        var isTimeStamp = function isTimeStamp(num) {\n          return w.config.xaxis.type === 'datetime' && String(num).length >= 10;\n        };\n\n        var seriesMaxDataLength = Math.max.apply(Math, _toConsumableArray(series.map(function (s) {\n          return s.data ? s.data.length : 0;\n        })));\n        var dataFormat = new Data(this.ctx);\n        var axesUtils = new AxesUtils(this.ctx);\n\n        var getCat = function getCat(i) {\n          var cat = ''; // pie / donut/ radial\n\n          if (!w.globals.axisCharts) {\n            cat = w.config.labels[i];\n          } else {\n            // xy charts\n            // non datetime\n            if (w.config.xaxis.type === 'category' || w.config.xaxis.convertedCatToNumeric) {\n              if (w.globals.isBarHorizontal) {\n                var lbFormatter = w.globals.yLabelFormatters[0];\n                var sr = new Series(_this3.ctx);\n                var activeSeries = sr.getActiveConfigSeriesIndex();\n                cat = lbFormatter(w.globals.labels[i], {\n                  seriesIndex: activeSeries,\n                  dataPointIndex: i,\n                  w: w\n                });\n              } else {\n                cat = axesUtils.getLabel(w.globals.labels, w.globals.timescaleLabels, 0, i).text;\n              }\n            } // datetime, but labels specified in categories or labels\n\n\n            if (w.config.xaxis.type === 'datetime') {\n              if (w.config.xaxis.categories.length) {\n                cat = w.config.xaxis.categories[i];\n              } else if (w.config.labels.length) {\n                cat = w.config.labels[i];\n              }\n            }\n          }\n\n          if (Array.isArray(cat)) {\n            cat = cat.join(' ');\n          }\n\n          return Utils$1.isNumber(cat) ? cat : cat.split(columnDelimiter).join('');\n        }; // Fix https://github.com/apexcharts/apexcharts.js/issues/3365\n\n\n        var getEmptyDataForCsvColumn = function getEmptyDataForCsvColumn() {\n          return _toConsumableArray(Array(seriesMaxDataLength)).map(function () {\n            return '';\n          });\n        };\n\n        var handleAxisRowsColumns = function handleAxisRowsColumns(s, sI) {\n          if (columns.length && sI === 0) {\n            // It's the first series.  Go ahead and create the first row with header information.\n            rows.push(columns.join(columnDelimiter));\n          }\n\n          if (s.data) {\n            // Use the data we have, or generate a properly sized empty array with empty data if some data is missing.\n            s.data = s.data.length && s.data || getEmptyDataForCsvColumn();\n\n            for (var i = 0; i < s.data.length; i++) {\n              // Reset the columns array so that we can start building columns for this row.\n              columns = [];\n              var cat = getCat(i);\n\n              if (!cat) {\n                if (dataFormat.isFormatXY()) {\n                  cat = series[sI].data[i].x;\n                } else if (dataFormat.isFormat2DArray()) {\n                  cat = series[sI].data[i] ? series[sI].data[i][0] : '';\n                }\n              }\n\n              if (sI === 0) {\n                // It's the first series.  Also handle the category.\n                columns.push(isTimeStamp(cat) ? w.config.chart.toolbar.export.csv.dateFormatter(cat) : Utils$1.isNumber(cat) ? cat : cat.split(columnDelimiter).join(''));\n\n                for (var ci = 0; ci < w.globals.series.length; ci++) {\n                  if (dataFormat.isFormatXY()) {\n                    columns.push(series[ci].data[i].y);\n                  } else {\n                    columns.push(gSeries[ci][i]);\n                  }\n                }\n              }\n\n              if (w.config.chart.type === 'candlestick' || s.type && s.type === 'candlestick') {\n                columns.pop();\n                columns.push(w.globals.seriesCandleO[sI][i]);\n                columns.push(w.globals.seriesCandleH[sI][i]);\n                columns.push(w.globals.seriesCandleL[sI][i]);\n                columns.push(w.globals.seriesCandleC[sI][i]);\n              }\n\n              if (w.config.chart.type === 'boxPlot' || s.type && s.type === 'boxPlot') {\n                columns.pop();\n                columns.push(w.globals.seriesCandleO[sI][i]);\n                columns.push(w.globals.seriesCandleH[sI][i]);\n                columns.push(w.globals.seriesCandleM[sI][i]);\n                columns.push(w.globals.seriesCandleL[sI][i]);\n                columns.push(w.globals.seriesCandleC[sI][i]);\n              }\n\n              if (w.config.chart.type === 'rangeBar') {\n                columns.pop();\n                columns.push(w.globals.seriesRangeStart[sI][i]);\n                columns.push(w.globals.seriesRangeEnd[sI][i]);\n              }\n\n              if (columns.length) {\n                rows.push(columns.join(columnDelimiter));\n              }\n            }\n          }\n        };\n\n        columns.push(w.config.chart.toolbar.export.csv.headerCategory);\n\n        if (w.config.chart.type === 'boxPlot') {\n          columns.push('minimum');\n          columns.push('q1');\n          columns.push('median');\n          columns.push('q3');\n          columns.push('maximum');\n        } else if (w.config.chart.type === 'candlestick') {\n          columns.push('open');\n          columns.push('high');\n          columns.push('low');\n          columns.push('close');\n        } else if (w.config.chart.type === 'rangeBar') {\n          columns.push('minimum');\n          columns.push('maximum');\n        } else {\n          series.map(function (s, sI) {\n            var sname = s.name ? s.name : \"series-\".concat(sI);\n\n            if (w.globals.axisCharts) {\n              columns.push(sname.split(columnDelimiter).join('') ? sname.split(columnDelimiter).join('') : \"series-\".concat(sI));\n            }\n          });\n        }\n\n        if (!w.globals.axisCharts) {\n          columns.push(w.config.chart.toolbar.export.csv.headerValue);\n          rows.push(columns.join(columnDelimiter));\n        }\n\n        series.map(function (s, sI) {\n          if (w.globals.axisCharts) {\n            handleAxisRowsColumns(s, sI);\n          } else {\n            columns = [];\n            columns.push(w.globals.labels[sI].split(columnDelimiter).join(''));\n            columns.push(gSeries[sI]);\n            rows.push(columns.join(columnDelimiter));\n          }\n        });\n        result += rows.join(lineDelimiter);\n        this.triggerDownload('data:text/csv; charset=utf-8,' + encodeURIComponent(universalBOM + result), fileName ? fileName : w.config.chart.toolbar.export.csv.filename, '.csv');\n      }\n    }, {\n      key: \"triggerDownload\",\n      value: function triggerDownload(href, filename, ext) {\n        var downloadLink = document.createElement('a');\n        downloadLink.href = href;\n        downloadLink.download = (filename ? filename : this.w.globals.chartID) + ext;\n        document.body.appendChild(downloadLink);\n        downloadLink.click();\n        document.body.removeChild(downloadLink);\n      }\n    }]);\n\n    return Exports;\n  }();\n\n  /**\n   * ApexCharts XAxis Class for drawing X-Axis.\n   *\n   * @module XAxis\n   **/\n\n  var XAxis = /*#__PURE__*/function () {\n    function XAxis(ctx, elgrid) {\n      _classCallCheck(this, XAxis);\n\n      this.ctx = ctx;\n      this.elgrid = elgrid;\n      this.w = ctx.w;\n      var w = this.w;\n      this.axesUtils = new AxesUtils(ctx);\n      this.xaxisLabels = w.globals.labels.slice();\n\n      if (w.globals.timescaleLabels.length > 0 && !w.globals.isBarHorizontal) {\n        //  timeline labels are there and chart is not rangeabr timeline\n        this.xaxisLabels = w.globals.timescaleLabels.slice();\n      }\n\n      if (w.config.xaxis.overwriteCategories) {\n        this.xaxisLabels = w.config.xaxis.overwriteCategories;\n      }\n\n      this.drawnLabels = [];\n      this.drawnLabelsRects = [];\n\n      if (w.config.xaxis.position === 'top') {\n        this.offY = 0;\n      } else {\n        this.offY = w.globals.gridHeight + 1;\n      }\n\n      this.offY = this.offY + w.config.xaxis.axisBorder.offsetY;\n      this.isCategoryBarHorizontal = w.config.chart.type === 'bar' && w.config.plotOptions.bar.horizontal;\n      this.xaxisFontSize = w.config.xaxis.labels.style.fontSize;\n      this.xaxisFontFamily = w.config.xaxis.labels.style.fontFamily;\n      this.xaxisForeColors = w.config.xaxis.labels.style.colors;\n      this.xaxisBorderWidth = w.config.xaxis.axisBorder.width;\n\n      if (this.isCategoryBarHorizontal) {\n        this.xaxisBorderWidth = w.config.yaxis[0].axisBorder.width.toString();\n      }\n\n      if (this.xaxisBorderWidth.indexOf('%') > -1) {\n        this.xaxisBorderWidth = w.globals.gridWidth * parseInt(this.xaxisBorderWidth, 10) / 100;\n      } else {\n        this.xaxisBorderWidth = parseInt(this.xaxisBorderWidth, 10);\n      }\n\n      this.xaxisBorderHeight = w.config.xaxis.axisBorder.height; // For bars, we will only consider single y xais,\n      // as we are not providing multiple yaxis for bar charts\n\n      this.yaxis = w.config.yaxis[0];\n    }\n\n    _createClass(XAxis, [{\n      key: \"drawXaxis\",\n      value: function drawXaxis() {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var elXaxis = graphics.group({\n          class: 'apexcharts-xaxis',\n          transform: \"translate(\".concat(w.config.xaxis.offsetX, \", \").concat(w.config.xaxis.offsetY, \")\")\n        });\n        var elXaxisTexts = graphics.group({\n          class: 'apexcharts-xaxis-texts-g',\n          transform: \"translate(\".concat(w.globals.translateXAxisX, \", \").concat(w.globals.translateXAxisY, \")\")\n        });\n        elXaxis.add(elXaxisTexts);\n        var labels = [];\n\n        for (var i = 0; i < this.xaxisLabels.length; i++) {\n          labels.push(this.xaxisLabels[i]);\n        }\n\n        this.drawXAxisLabelAndGroup(true, graphics, elXaxisTexts, labels, w.globals.isXNumeric, function (i, colWidth) {\n          return colWidth;\n        });\n\n        if (w.globals.hasGroups) {\n          var labelsGroup = w.globals.groups;\n          labels = [];\n\n          for (var _i = 0; _i < labelsGroup.length; _i++) {\n            labels.push(labelsGroup[_i].title);\n          }\n\n          var overwriteStyles = {};\n\n          if (w.config.xaxis.group.style) {\n            overwriteStyles.xaxisFontSize = w.config.xaxis.group.style.fontSize;\n            overwriteStyles.xaxisFontFamily = w.config.xaxis.group.style.fontFamily;\n            overwriteStyles.xaxisForeColors = w.config.xaxis.group.style.colors;\n            overwriteStyles.fontWeight = w.config.xaxis.group.style.fontWeight;\n            overwriteStyles.cssClass = w.config.xaxis.group.style.cssClass;\n          }\n\n          this.drawXAxisLabelAndGroup(false, graphics, elXaxisTexts, labels, false, function (i, colWidth) {\n            return labelsGroup[i].cols * colWidth;\n          }, overwriteStyles);\n        }\n\n        if (w.config.xaxis.title.text !== undefined) {\n          var elXaxisTitle = graphics.group({\n            class: 'apexcharts-xaxis-title'\n          });\n          var elXAxisTitleText = graphics.drawText({\n            x: w.globals.gridWidth / 2 + w.config.xaxis.title.offsetX,\n            y: this.offY + parseFloat(this.xaxisFontSize) + (w.config.xaxis.position === 'bottom' ? w.globals.xAxisLabelsHeight : -w.globals.xAxisLabelsHeight - 10) + w.config.xaxis.title.offsetY,\n            text: w.config.xaxis.title.text,\n            textAnchor: 'middle',\n            fontSize: w.config.xaxis.title.style.fontSize,\n            fontFamily: w.config.xaxis.title.style.fontFamily,\n            fontWeight: w.config.xaxis.title.style.fontWeight,\n            foreColor: w.config.xaxis.title.style.color,\n            cssClass: 'apexcharts-xaxis-title-text ' + w.config.xaxis.title.style.cssClass\n          });\n          elXaxisTitle.add(elXAxisTitleText);\n          elXaxis.add(elXaxisTitle);\n        }\n\n        if (w.config.xaxis.axisBorder.show) {\n          var offX = w.globals.barPadForNumericAxis;\n          var elHorzLine = graphics.drawLine(w.globals.padHorizontal + w.config.xaxis.axisBorder.offsetX - offX, this.offY, this.xaxisBorderWidth + offX, this.offY, w.config.xaxis.axisBorder.color, 0, this.xaxisBorderHeight);\n\n          if (this.elgrid && this.elgrid.elGridBorders) {\n            this.elgrid.elGridBorders.add(elHorzLine);\n          } else {\n            elXaxis.add(elHorzLine);\n          }\n        }\n\n        return elXaxis;\n      }\n    }, {\n      key: \"drawXAxisLabelAndGroup\",\n      value: function drawXAxisLabelAndGroup(isLeafGroup, graphics, elXaxisTexts, labels, isXNumeric, colWidthCb) {\n        var _this = this;\n\n        var overwriteStyles = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};\n        var drawnLabels = [];\n        var drawnLabelsRects = [];\n        var w = this.w;\n        var xaxisFontSize = overwriteStyles.xaxisFontSize || this.xaxisFontSize;\n        var xaxisFontFamily = overwriteStyles.xaxisFontFamily || this.xaxisFontFamily;\n        var xaxisForeColors = overwriteStyles.xaxisForeColors || this.xaxisForeColors;\n        var fontWeight = overwriteStyles.fontWeight || w.config.xaxis.labels.style.fontWeight;\n        var cssClass = overwriteStyles.cssClass || w.config.xaxis.labels.style.cssClass;\n        var colWidth; // initial x Position (keep adding column width in the loop)\n\n        var xPos = w.globals.padHorizontal;\n        var labelsLen = labels.length;\n        /**\n         * labelsLen can be different (whether you are drawing x-axis labels or x-axis group labels)\n         * hence, we introduce dataPoints to be consistent.\n         * Also, in datetime/numeric xaxis, dataPoints can be misleading, so we resort to labelsLen for such xaxis type\n         */\n\n        var dataPoints = w.config.xaxis.type === 'category' ? w.globals.dataPoints : labelsLen; // when all series are collapsed, fixes #3381\n\n        if (dataPoints === 0 && labelsLen > dataPoints) dataPoints = labelsLen;\n\n        if (isXNumeric) {\n          var len = dataPoints > 1 ? dataPoints - 1 : dataPoints;\n          colWidth = w.globals.gridWidth / len;\n          xPos = xPos + colWidthCb(0, colWidth) / 2 + w.config.xaxis.labels.offsetX;\n        } else {\n          colWidth = w.globals.gridWidth / dataPoints;\n          xPos = xPos + colWidthCb(0, colWidth) + w.config.xaxis.labels.offsetX;\n        }\n\n        var _loop = function _loop(i) {\n          var x = xPos - colWidthCb(i, colWidth) / 2 + w.config.xaxis.labels.offsetX;\n\n          if (i === 0 && labelsLen === 1 && colWidth / 2 === xPos && dataPoints === 1) {\n            // single datapoint\n            x = w.globals.gridWidth / 2;\n          }\n\n          var label = _this.axesUtils.getLabel(labels, w.globals.timescaleLabels, x, i, drawnLabels, xaxisFontSize, isLeafGroup);\n\n          var offsetYCorrection = 28;\n\n          if (w.globals.rotateXLabels && isLeafGroup) {\n            offsetYCorrection = 22;\n          }\n\n          if (w.config.xaxis.title.text && w.config.xaxis.position === 'top') {\n            offsetYCorrection += parseFloat(w.config.xaxis.title.style.fontSize) + 2;\n          }\n\n          if (!isLeafGroup) {\n            offsetYCorrection = offsetYCorrection + parseFloat(xaxisFontSize) + (w.globals.xAxisLabelsHeight - w.globals.xAxisGroupLabelsHeight) + (w.globals.rotateXLabels ? 10 : 0);\n          }\n\n          var isCategoryTickAmounts = typeof w.config.xaxis.tickAmount !== 'undefined' && w.config.xaxis.tickAmount !== 'dataPoints' && w.config.xaxis.type !== 'datetime';\n\n          if (isCategoryTickAmounts) {\n            label = _this.axesUtils.checkLabelBasedOnTickamount(i, label, labelsLen);\n          } else {\n            label = _this.axesUtils.checkForOverflowingLabels(i, label, labelsLen, drawnLabels, drawnLabelsRects);\n          }\n\n          var getCatForeColor = function getCatForeColor() {\n            return isLeafGroup && w.config.xaxis.convertedCatToNumeric ? xaxisForeColors[w.globals.minX + i - 1] : xaxisForeColors[i];\n          };\n\n          if (w.config.xaxis.labels.show) {\n            var elText = graphics.drawText({\n              x: label.x,\n              y: _this.offY + w.config.xaxis.labels.offsetY + offsetYCorrection - (w.config.xaxis.position === 'top' ? w.globals.xAxisHeight + w.config.xaxis.axisTicks.height - 2 : 0),\n              text: label.text,\n              textAnchor: 'middle',\n              fontWeight: label.isBold ? 600 : fontWeight,\n              fontSize: xaxisFontSize,\n              fontFamily: xaxisFontFamily,\n              foreColor: Array.isArray(xaxisForeColors) ? getCatForeColor() : xaxisForeColors,\n              isPlainText: false,\n              cssClass: (isLeafGroup ? 'apexcharts-xaxis-label ' : 'apexcharts-xaxis-group-label ') + cssClass\n            });\n            elXaxisTexts.add(elText);\n            elText.on('click', function (e) {\n              if (typeof w.config.chart.events.xAxisLabelClick === 'function') {\n                var opts = Object.assign({}, w, {\n                  labelIndex: i\n                });\n                w.config.chart.events.xAxisLabelClick(e, _this.ctx, opts);\n              }\n            });\n\n            if (isLeafGroup) {\n              var elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title');\n              elTooltipTitle.textContent = Array.isArray(label.text) ? label.text.join(' ') : label.text;\n              elText.node.appendChild(elTooltipTitle);\n\n              if (label.text !== '') {\n                drawnLabels.push(label.text);\n                drawnLabelsRects.push(label);\n              }\n            }\n          }\n\n          if (i < labelsLen - 1) {\n            xPos = xPos + colWidthCb(i + 1, colWidth);\n          }\n        };\n\n        for (var i = 0; i <= labelsLen - 1; i++) {\n          _loop(i);\n        }\n      } // this actually becomes the vertical axis (for bar charts)\n\n    }, {\n      key: \"drawXaxisInversed\",\n      value: function drawXaxisInversed(realIndex) {\n        var _this2 = this;\n\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var translateYAxisX = w.config.yaxis[0].opposite ? w.globals.translateYAxisX[realIndex] : 0;\n        var elYaxis = graphics.group({\n          class: 'apexcharts-yaxis apexcharts-xaxis-inversed',\n          rel: realIndex\n        });\n        var elYaxisTexts = graphics.group({\n          class: 'apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g',\n          transform: 'translate(' + translateYAxisX + ', 0)'\n        });\n        elYaxis.add(elYaxisTexts);\n        var colHeight; // initial x Position (keep adding column width in the loop)\n\n        var yPos;\n        var labels = [];\n\n        if (w.config.yaxis[realIndex].show) {\n          for (var i = 0; i < this.xaxisLabels.length; i++) {\n            labels.push(this.xaxisLabels[i]);\n          }\n        }\n\n        colHeight = w.globals.gridHeight / labels.length;\n        yPos = -(colHeight / 2.2);\n        var lbFormatter = w.globals.yLabelFormatters[0];\n        var ylabels = w.config.yaxis[0].labels;\n\n        if (ylabels.show) {\n          var _loop2 = function _loop2(_i2) {\n            var label = typeof labels[_i2] === 'undefined' ? '' : labels[_i2];\n            label = lbFormatter(label, {\n              seriesIndex: realIndex,\n              dataPointIndex: _i2,\n              w: w\n            });\n\n            var yColors = _this2.axesUtils.getYAxisForeColor(ylabels.style.colors, realIndex);\n\n            var getForeColor = function getForeColor() {\n              return Array.isArray(yColors) ? yColors[_i2] : yColors;\n            };\n\n            var multiY = 0;\n\n            if (Array.isArray(label)) {\n              multiY = label.length / 2 * parseInt(ylabels.style.fontSize, 10);\n            }\n\n            var offsetX = ylabels.offsetX - 15;\n            var textAnchor = 'end';\n\n            if (_this2.yaxis.opposite) {\n              textAnchor = 'start';\n            }\n\n            if (w.config.yaxis[0].labels.align === 'left') {\n              offsetX = ylabels.offsetX;\n              textAnchor = 'start';\n            } else if (w.config.yaxis[0].labels.align === 'center') {\n              offsetX = ylabels.offsetX;\n              textAnchor = 'middle';\n            } else if (w.config.yaxis[0].labels.align === 'right') {\n              textAnchor = 'end';\n            }\n\n            var elLabel = graphics.drawText({\n              x: offsetX,\n              y: yPos + colHeight + ylabels.offsetY - multiY,\n              text: label,\n              textAnchor: textAnchor,\n              foreColor: getForeColor(),\n              fontSize: ylabels.style.fontSize,\n              fontFamily: ylabels.style.fontFamily,\n              fontWeight: ylabels.style.fontWeight,\n              isPlainText: false,\n              cssClass: 'apexcharts-yaxis-label ' + ylabels.style.cssClass,\n              maxWidth: ylabels.maxWidth\n            });\n            elYaxisTexts.add(elLabel);\n            elLabel.on('click', function (e) {\n              if (typeof w.config.chart.events.xAxisLabelClick === 'function') {\n                var opts = Object.assign({}, w, {\n                  labelIndex: _i2\n                });\n                w.config.chart.events.xAxisLabelClick(e, _this2.ctx, opts);\n              }\n            });\n            var elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title');\n            elTooltipTitle.textContent = Array.isArray(label) ? label.join(' ') : label;\n            elLabel.node.appendChild(elTooltipTitle);\n\n            if (w.config.yaxis[realIndex].labels.rotate !== 0) {\n              var labelRotatingCenter = graphics.rotateAroundCenter(elLabel.node);\n              elLabel.node.setAttribute('transform', \"rotate(\".concat(w.config.yaxis[realIndex].labels.rotate, \" 0 \").concat(labelRotatingCenter.y, \")\"));\n            }\n\n            yPos = yPos + colHeight;\n          };\n\n          for (var _i2 = 0; _i2 <= labels.length - 1; _i2++) {\n            _loop2(_i2);\n          }\n        }\n\n        if (w.config.yaxis[0].title.text !== undefined) {\n          var elXaxisTitle = graphics.group({\n            class: 'apexcharts-yaxis-title apexcharts-xaxis-title-inversed',\n            transform: 'translate(' + translateYAxisX + ', 0)'\n          });\n          var elXAxisTitleText = graphics.drawText({\n            x: w.config.yaxis[0].title.offsetX,\n            y: w.globals.gridHeight / 2 + w.config.yaxis[0].title.offsetY,\n            text: w.config.yaxis[0].title.text,\n            textAnchor: 'middle',\n            foreColor: w.config.yaxis[0].title.style.color,\n            fontSize: w.config.yaxis[0].title.style.fontSize,\n            fontWeight: w.config.yaxis[0].title.style.fontWeight,\n            fontFamily: w.config.yaxis[0].title.style.fontFamily,\n            cssClass: 'apexcharts-yaxis-title-text ' + w.config.yaxis[0].title.style.cssClass\n          });\n          elXaxisTitle.add(elXAxisTitleText);\n          elYaxis.add(elXaxisTitle);\n        }\n\n        var offX = 0;\n\n        if (this.isCategoryBarHorizontal && w.config.yaxis[0].opposite) {\n          offX = w.globals.gridWidth;\n        }\n\n        var axisBorder = w.config.xaxis.axisBorder;\n\n        if (axisBorder.show) {\n          var elVerticalLine = graphics.drawLine(w.globals.padHorizontal + axisBorder.offsetX + offX, 1 + axisBorder.offsetY, w.globals.padHorizontal + axisBorder.offsetX + offX, w.globals.gridHeight + axisBorder.offsetY, axisBorder.color, 0);\n\n          if (this.elgrid && this.elgrid.elGridBorders) {\n            this.elgrid.elGridBorders.add(elVerticalLine);\n          } else {\n            elYaxis.add(elVerticalLine);\n          }\n        }\n\n        if (w.config.yaxis[0].axisTicks.show) {\n          this.axesUtils.drawYAxisTicks(offX, labels.length, w.config.yaxis[0].axisBorder, w.config.yaxis[0].axisTicks, 0, colHeight, elYaxis);\n        }\n\n        return elYaxis;\n      }\n    }, {\n      key: \"drawXaxisTicks\",\n      value: function drawXaxisTicks(x1, y2, appendToElement) {\n        var w = this.w;\n        var x2 = x1;\n        if (x1 < 0 || x1 - 2 > w.globals.gridWidth) return;\n        var y1 = this.offY + w.config.xaxis.axisTicks.offsetY;\n        y2 = y2 + y1 + w.config.xaxis.axisTicks.height;\n\n        if (w.config.xaxis.position === 'top') {\n          y2 = y1 - w.config.xaxis.axisTicks.height;\n        }\n\n        if (w.config.xaxis.axisTicks.show) {\n          var graphics = new Graphics(this.ctx);\n          var line = graphics.drawLine(x1 + w.config.xaxis.axisTicks.offsetX, y1 + w.config.xaxis.offsetY, x2 + w.config.xaxis.axisTicks.offsetX, y2 + w.config.xaxis.offsetY, w.config.xaxis.axisTicks.color); // we are not returning anything, but appending directly to the element passed in param\n\n          appendToElement.add(line);\n          line.node.classList.add('apexcharts-xaxis-tick');\n        }\n      }\n    }, {\n      key: \"getXAxisTicksPositions\",\n      value: function getXAxisTicksPositions() {\n        var w = this.w;\n        var xAxisTicksPositions = [];\n        var xCount = this.xaxisLabels.length;\n        var x1 = w.globals.padHorizontal;\n\n        if (w.globals.timescaleLabels.length > 0) {\n          for (var i = 0; i < xCount; i++) {\n            x1 = this.xaxisLabels[i].position;\n            xAxisTicksPositions.push(x1);\n          }\n        } else {\n          var xCountForCategoryCharts = xCount;\n\n          for (var _i3 = 0; _i3 < xCountForCategoryCharts; _i3++) {\n            var x1Count = xCountForCategoryCharts;\n\n            if (w.globals.isXNumeric && w.config.chart.type !== 'bar') {\n              x1Count -= 1;\n            }\n\n            x1 = x1 + w.globals.gridWidth / x1Count;\n            xAxisTicksPositions.push(x1);\n          }\n        }\n\n        return xAxisTicksPositions;\n      } // to rotate x-axis labels or to put ... for longer text in xaxis\n\n    }, {\n      key: \"xAxisLabelCorrections\",\n      value: function xAxisLabelCorrections() {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var xAxis = w.globals.dom.baseEl.querySelector('.apexcharts-xaxis-texts-g');\n        var xAxisTexts = w.globals.dom.baseEl.querySelectorAll('.apexcharts-xaxis-texts-g text:not(.apexcharts-xaxis-group-label)');\n        var yAxisTextsInversed = w.globals.dom.baseEl.querySelectorAll('.apexcharts-yaxis-inversed text');\n        var xAxisTextsInversed = w.globals.dom.baseEl.querySelectorAll('.apexcharts-xaxis-inversed-texts-g text tspan');\n\n        if (w.globals.rotateXLabels || w.config.xaxis.labels.rotateAlways) {\n          for (var xat = 0; xat < xAxisTexts.length; xat++) {\n            var textRotatingCenter = graphics.rotateAroundCenter(xAxisTexts[xat]);\n            textRotatingCenter.y = textRotatingCenter.y - 1; // + tickWidth/4;\n\n            textRotatingCenter.x = textRotatingCenter.x + 1;\n            xAxisTexts[xat].setAttribute('transform', \"rotate(\".concat(w.config.xaxis.labels.rotate, \" \").concat(textRotatingCenter.x, \" \").concat(textRotatingCenter.y, \")\"));\n            xAxisTexts[xat].setAttribute('text-anchor', \"end\");\n            var offsetHeight = 10;\n            xAxis.setAttribute('transform', \"translate(0, \".concat(-offsetHeight, \")\"));\n            var tSpan = xAxisTexts[xat].childNodes;\n\n            if (w.config.xaxis.labels.trim) {\n              Array.prototype.forEach.call(tSpan, function (ts) {\n                graphics.placeTextWithEllipsis(ts, ts.textContent, w.globals.xAxisLabelsHeight - (w.config.legend.position === 'bottom' ? 20 : 10));\n              });\n            }\n          }\n        } else {\n          (function () {\n            var width = w.globals.gridWidth / (w.globals.labels.length + 1);\n\n            for (var _xat = 0; _xat < xAxisTexts.length; _xat++) {\n              var _tSpan = xAxisTexts[_xat].childNodes;\n\n              if (w.config.xaxis.labels.trim && w.config.xaxis.type !== 'datetime') {\n                Array.prototype.forEach.call(_tSpan, function (ts) {\n                  graphics.placeTextWithEllipsis(ts, ts.textContent, width);\n                });\n              }\n            }\n          })();\n        }\n\n        if (yAxisTextsInversed.length > 0) {\n          // truncate rotated y axis in bar chart (x axis)\n          var firstLabelPosX = yAxisTextsInversed[yAxisTextsInversed.length - 1].getBBox();\n          var lastLabelPosX = yAxisTextsInversed[0].getBBox();\n\n          if (firstLabelPosX.x < -20) {\n            yAxisTextsInversed[yAxisTextsInversed.length - 1].parentNode.removeChild(yAxisTextsInversed[yAxisTextsInversed.length - 1]);\n          }\n\n          if (lastLabelPosX.x + lastLabelPosX.width > w.globals.gridWidth && !w.globals.isBarHorizontal) {\n            yAxisTextsInversed[0].parentNode.removeChild(yAxisTextsInversed[0]);\n          } // truncate rotated x axis in bar chart (y axis)\n\n\n          for (var _xat2 = 0; _xat2 < xAxisTextsInversed.length; _xat2++) {\n            graphics.placeTextWithEllipsis(xAxisTextsInversed[_xat2], xAxisTextsInversed[_xat2].textContent, w.config.yaxis[0].labels.maxWidth - (w.config.yaxis[0].title.text ? parseFloat(w.config.yaxis[0].title.style.fontSize) * 2 : 0) - 15);\n          }\n        }\n      } // renderXAxisBands() {\n      //   let w = this.w;\n      //   let plotBand = document.createElementNS(w.globals.SVGNS, 'rect')\n      //   w.globals.dom.elGraphical.add(plotBand)\n      // }\n\n    }]);\n\n    return XAxis;\n  }();\n\n  /**\n   * ApexCharts Grid Class for drawing Cartesian Grid.\n   *\n   * @module Grid\n   **/\n\n  var Grid = /*#__PURE__*/function () {\n    function Grid(ctx) {\n      _classCallCheck(this, Grid);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      var w = this.w;\n      this.xaxisLabels = w.globals.labels.slice();\n      this.axesUtils = new AxesUtils(ctx);\n      this.isRangeBar = w.globals.seriesRange.length;\n\n      if (w.globals.timescaleLabels.length > 0) {\n        //  timescaleLabels labels are there\n        this.xaxisLabels = w.globals.timescaleLabels.slice();\n      }\n    } // when using sparklines or when showing no grid, we need to have a grid area which is reused at many places for other calculations as well\n\n\n    _createClass(Grid, [{\n      key: \"drawGridArea\",\n      value: function drawGridArea() {\n        var elGrid = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n\n        if (elGrid === null) {\n          elGrid = graphics.group({\n            class: 'apexcharts-grid'\n          });\n        }\n\n        var elVerticalLine = graphics.drawLine(w.globals.padHorizontal, 1, w.globals.padHorizontal, w.globals.gridHeight, 'transparent');\n        var elHorzLine = graphics.drawLine(w.globals.padHorizontal, w.globals.gridHeight, w.globals.gridWidth, w.globals.gridHeight, 'transparent');\n        elGrid.add(elHorzLine);\n        elGrid.add(elVerticalLine);\n        return elGrid;\n      }\n    }, {\n      key: \"drawGrid\",\n      value: function drawGrid() {\n        var gl = this.w.globals;\n        var elgrid = null;\n\n        if (gl.axisCharts) {\n          // grid is drawn after xaxis and yaxis are drawn\n          elgrid = this.renderGrid();\n          this.drawGridArea(elgrid.el);\n        }\n\n        return elgrid;\n      } // This mask will clip off overflowing graphics from the drawable area\n\n    }, {\n      key: \"createGridMask\",\n      value: function createGridMask() {\n        var w = this.w;\n        var gl = w.globals;\n        var graphics = new Graphics(this.ctx);\n        var strokeSize = Array.isArray(w.config.stroke.width) ? 0 : w.config.stroke.width;\n\n        if (Array.isArray(w.config.stroke.width)) {\n          var strokeMaxSize = 0;\n          w.config.stroke.width.forEach(function (m) {\n            strokeMaxSize = Math.max(strokeMaxSize, m);\n          });\n          strokeSize = strokeMaxSize;\n        }\n\n        gl.dom.elGridRectMask = document.createElementNS(gl.SVGNS, 'clipPath');\n        gl.dom.elGridRectMask.setAttribute('id', \"gridRectMask\".concat(gl.cuid));\n        gl.dom.elGridRectMarkerMask = document.createElementNS(gl.SVGNS, 'clipPath');\n        gl.dom.elGridRectMarkerMask.setAttribute('id', \"gridRectMarkerMask\".concat(gl.cuid));\n        gl.dom.elForecastMask = document.createElementNS(gl.SVGNS, 'clipPath');\n        gl.dom.elForecastMask.setAttribute('id', \"forecastMask\".concat(gl.cuid));\n        gl.dom.elNonForecastMask = document.createElementNS(gl.SVGNS, 'clipPath');\n        gl.dom.elNonForecastMask.setAttribute('id', \"nonForecastMask\".concat(gl.cuid)); // let barHalfWidth = 0\n\n        var type = w.config.chart.type;\n        var hasBar = type === 'bar' || type === 'rangeBar' || type === 'candlestick' || type === 'boxPlot' || w.globals.comboBarCount > 0;\n        var barWidthLeft = 0;\n        var barWidthRight = 0;\n\n        if (hasBar && w.globals.isXNumeric && !w.globals.isBarHorizontal) {\n          barWidthLeft = w.config.grid.padding.left;\n          barWidthRight = w.config.grid.padding.right;\n\n          if (gl.barPadForNumericAxis > barWidthLeft) {\n            barWidthLeft = gl.barPadForNumericAxis;\n            barWidthRight = gl.barPadForNumericAxis;\n          }\n        }\n\n        gl.dom.elGridRect = graphics.drawRect(-strokeSize / 2 - barWidthLeft - 2, -strokeSize / 2, gl.gridWidth + strokeSize + barWidthRight + barWidthLeft + 4, gl.gridHeight + strokeSize, 0, '#fff');\n        var markerSize = w.globals.markers.largestSize + 1;\n        gl.dom.elGridRectMarker = graphics.drawRect(-markerSize * 2, -markerSize * 2, gl.gridWidth + markerSize * 4, gl.gridHeight + markerSize * 4, 0, '#fff');\n        gl.dom.elGridRectMask.appendChild(gl.dom.elGridRect.node);\n        gl.dom.elGridRectMarkerMask.appendChild(gl.dom.elGridRectMarker.node);\n        var defs = gl.dom.baseEl.querySelector('defs');\n        defs.appendChild(gl.dom.elGridRectMask);\n        defs.appendChild(gl.dom.elForecastMask);\n        defs.appendChild(gl.dom.elNonForecastMask);\n        defs.appendChild(gl.dom.elGridRectMarkerMask);\n      }\n    }, {\n      key: \"_drawGridLines\",\n      value: function _drawGridLines(_ref) {\n        var i = _ref.i,\n            x1 = _ref.x1,\n            y1 = _ref.y1,\n            x2 = _ref.x2,\n            y2 = _ref.y2,\n            xCount = _ref.xCount,\n            parent = _ref.parent;\n        var w = this.w;\n\n        var shouldDraw = function shouldDraw() {\n          if (i === 0 && w.globals.skipFirstTimelinelabel) {\n            return false;\n          }\n\n          if (i === xCount - 1 && w.globals.skipLastTimelinelabel && !w.config.xaxis.labels.formatter) {\n            return false;\n          }\n\n          if (w.config.chart.type === 'radar') {\n            return false;\n          }\n\n          return true;\n        };\n\n        if (shouldDraw()) {\n          if (w.config.grid.xaxis.lines.show) {\n            this._drawGridLine({\n              i: i,\n              x1: x1,\n              y1: y1,\n              x2: x2,\n              y2: y2,\n              xCount: xCount,\n              parent: parent\n            });\n          }\n\n          var y_2 = 0;\n\n          if (w.globals.hasGroups && w.config.xaxis.tickPlacement === 'between') {\n            var groups = w.globals.groups;\n\n            if (groups) {\n              var gacc = 0;\n\n              for (var gi = 0; gacc < i && gi < groups.length; gi++) {\n                gacc += groups[gi].cols;\n              }\n\n              if (gacc === i) {\n                y_2 = w.globals.xAxisLabelsHeight * 0.6;\n              }\n            }\n          }\n\n          var xAxis = new XAxis(this.ctx);\n          xAxis.drawXaxisTicks(x1, y_2, w.globals.dom.elGraphical);\n        }\n      }\n    }, {\n      key: \"_drawGridLine\",\n      value: function _drawGridLine(_ref2) {\n        var i = _ref2.i,\n            x1 = _ref2.x1,\n            y1 = _ref2.y1,\n            x2 = _ref2.x2,\n            y2 = _ref2.y2,\n            xCount = _ref2.xCount,\n            parent = _ref2.parent;\n        var w = this.w;\n        var excludeBorders = false;\n        var isHorzLine = parent.node.classList.contains('apexcharts-gridlines-horizontal');\n        var strokeDashArray = w.config.grid.strokeDashArray;\n        var offX = w.globals.barPadForNumericAxis;\n\n        if (y1 === 0 && y2 === 0 || x1 === 0 && x2 === 0) {\n          excludeBorders = true;\n        }\n\n        if (y1 === w.globals.gridHeight && y2 === w.globals.gridHeight) {\n          excludeBorders = true;\n        }\n\n        if (w.globals.isBarHorizontal && (i === 0 || i === xCount - 1)) {\n          excludeBorders = true;\n        }\n\n        var graphics = new Graphics(this);\n        var line = graphics.drawLine(x1 - (isHorzLine ? offX : 0), y1, x2 + (isHorzLine ? offX : 0), y2, w.config.grid.borderColor, strokeDashArray);\n        line.node.classList.add('apexcharts-gridline');\n\n        if (excludeBorders) {\n          this.elGridBorders.add(line);\n        } else {\n          parent.add(line);\n        }\n      }\n    }, {\n      key: \"_drawGridBandRect\",\n      value: function _drawGridBandRect(_ref3) {\n        var c = _ref3.c,\n            x1 = _ref3.x1,\n            y1 = _ref3.y1,\n            x2 = _ref3.x2,\n            y2 = _ref3.y2,\n            type = _ref3.type;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var offX = w.globals.barPadForNumericAxis;\n        if (type === 'column' && w.config.xaxis.type === 'datetime') return;\n        var color = w.config.grid[type].colors[c];\n        var rect = graphics.drawRect(x1 - (type === 'row' ? offX : 0), y1, x2 + (type === 'row' ? offX * 2 : 0), y2, 0, color, w.config.grid[type].opacity);\n        this.elg.add(rect);\n        rect.attr('clip-path', \"url(#gridRectMask\".concat(w.globals.cuid, \")\"));\n        rect.node.classList.add(\"apexcharts-grid-\".concat(type));\n      }\n    }, {\n      key: \"_drawXYLines\",\n      value: function _drawXYLines(_ref4) {\n        var _this = this;\n\n        var xCount = _ref4.xCount,\n            tickAmount = _ref4.tickAmount;\n        var w = this.w;\n\n        var datetimeLines = function datetimeLines(_ref5) {\n          var xC = _ref5.xC,\n              x1 = _ref5.x1,\n              y1 = _ref5.y1,\n              x2 = _ref5.x2,\n              y2 = _ref5.y2;\n\n          for (var i = 0; i < xC; i++) {\n            x1 = _this.xaxisLabels[i].position;\n            x2 = _this.xaxisLabels[i].position;\n\n            _this._drawGridLines({\n              i: i,\n              x1: x1,\n              y1: y1,\n              x2: x2,\n              y2: y2,\n              xCount: xCount,\n              parent: _this.elgridLinesV\n            });\n          }\n        };\n\n        var categoryLines = function categoryLines(_ref6) {\n          var xC = _ref6.xC,\n              x1 = _ref6.x1,\n              y1 = _ref6.y1,\n              x2 = _ref6.x2,\n              y2 = _ref6.y2;\n\n          for (var i = 0; i < xC + (w.globals.isXNumeric ? 0 : 1); i++) {\n            if (i === 0 && xC === 1 && w.globals.dataPoints === 1) {\n              // single datapoint\n              x1 = w.globals.gridWidth / 2;\n              x2 = x1;\n            }\n\n            _this._drawGridLines({\n              i: i,\n              x1: x1,\n              y1: y1,\n              x2: x2,\n              y2: y2,\n              xCount: xCount,\n              parent: _this.elgridLinesV\n            });\n\n            x1 = x1 + w.globals.gridWidth / (w.globals.isXNumeric ? xC - 1 : xC);\n            x2 = x1;\n          }\n        }; // draw vertical lines\n\n\n        if (w.config.grid.xaxis.lines.show || w.config.xaxis.axisTicks.show) {\n          var x1 = w.globals.padHorizontal;\n          var y1 = 0;\n          var x2;\n          var y2 = w.globals.gridHeight;\n\n          if (w.globals.timescaleLabels.length) {\n            datetimeLines({\n              xC: xCount,\n              x1: x1,\n              y1: y1,\n              x2: x2,\n              y2: y2\n            });\n          } else {\n            if (w.globals.isXNumeric) {\n              xCount = w.globals.xAxisScale.result.length;\n            }\n\n            categoryLines({\n              xC: xCount,\n              x1: x1,\n              y1: y1,\n              x2: x2,\n              y2: y2\n            });\n          }\n        } // draw horizontal lines\n\n\n        if (w.config.grid.yaxis.lines.show) {\n          var _x = 0;\n          var _y = 0;\n          var _y2 = 0;\n          var _x2 = w.globals.gridWidth;\n          var tA = tickAmount + 1;\n\n          if (this.isRangeBar) {\n            tA = w.globals.labels.length;\n          }\n\n          for (var i = 0; i < tA + (this.isRangeBar ? 1 : 0); i++) {\n            this._drawGridLine({\n              i: i,\n              xCount: tA + (this.isRangeBar ? 1 : 0),\n              x1: _x,\n              y1: _y,\n              x2: _x2,\n              y2: _y2,\n              parent: this.elgridLinesH\n            });\n\n            _y = _y + w.globals.gridHeight / (this.isRangeBar ? tA : tickAmount);\n            _y2 = _y;\n          }\n        }\n      }\n    }, {\n      key: \"_drawInvertedXYLines\",\n      value: function _drawInvertedXYLines(_ref7) {\n        var xCount = _ref7.xCount;\n        var w = this.w; // draw vertical lines\n\n        if (w.config.grid.xaxis.lines.show || w.config.xaxis.axisTicks.show) {\n          var x1 = w.globals.padHorizontal;\n          var y1 = 0;\n          var x2;\n          var y2 = w.globals.gridHeight;\n\n          for (var i = 0; i < xCount + 1; i++) {\n            if (w.config.grid.xaxis.lines.show) {\n              this._drawGridLine({\n                i: i,\n                xCount: xCount + 1,\n                x1: x1,\n                y1: y1,\n                x2: x2,\n                y2: y2,\n                parent: this.elgridLinesV\n              });\n            }\n\n            var xAxis = new XAxis(this.ctx);\n            xAxis.drawXaxisTicks(x1, 0, w.globals.dom.elGraphical);\n            x1 = x1 + w.globals.gridWidth / xCount + 0.3;\n            x2 = x1;\n          }\n        } // draw horizontal lines\n\n\n        if (w.config.grid.yaxis.lines.show) {\n          var _x3 = 0;\n          var _y3 = 0;\n          var _y4 = 0;\n          var _x4 = w.globals.gridWidth;\n\n          for (var _i = 0; _i < w.globals.dataPoints + 1; _i++) {\n            this._drawGridLine({\n              i: _i,\n              xCount: w.globals.dataPoints + 1,\n              x1: _x3,\n              y1: _y3,\n              x2: _x4,\n              y2: _y4,\n              parent: this.elgridLinesH\n            });\n\n            _y3 = _y3 + w.globals.gridHeight / w.globals.dataPoints;\n            _y4 = _y3;\n          }\n        }\n      } // actual grid rendering\n\n    }, {\n      key: \"renderGrid\",\n      value: function renderGrid() {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        this.elg = graphics.group({\n          class: 'apexcharts-grid'\n        });\n        this.elgridLinesH = graphics.group({\n          class: 'apexcharts-gridlines-horizontal'\n        });\n        this.elgridLinesV = graphics.group({\n          class: 'apexcharts-gridlines-vertical'\n        });\n        this.elGridBorders = graphics.group({\n          class: 'apexcharts-grid-borders'\n        });\n        this.elg.add(this.elgridLinesH);\n        this.elg.add(this.elgridLinesV);\n\n        if (!w.config.grid.show) {\n          this.elgridLinesV.hide();\n          this.elgridLinesH.hide();\n          this.elGridBorders.hide();\n        }\n\n        var yTickAmount = w.globals.yAxisScale.length ? w.globals.yAxisScale[0].result.length - 1 : 5;\n\n        for (var i = 0; i < w.globals.series.length; i++) {\n          if (typeof w.globals.yAxisScale[i] !== 'undefined') {\n            yTickAmount = w.globals.yAxisScale[i].result.length - 1;\n          }\n\n          if (yTickAmount > 2) break;\n        }\n\n        var xCount;\n\n        if (!w.globals.isBarHorizontal || this.isRangeBar) {\n          xCount = this.xaxisLabels.length;\n\n          if (this.isRangeBar) {\n            yTickAmount = w.globals.labels.length;\n\n            if (w.config.xaxis.tickAmount && w.config.xaxis.labels.formatter) {\n              xCount = w.config.xaxis.tickAmount;\n            }\n          }\n\n          this._drawXYLines({\n            xCount: xCount,\n            tickAmount: yTickAmount\n          });\n        } else {\n          xCount = yTickAmount; // for horizontal bar chart, get the xaxis tickamount\n\n          yTickAmount = w.globals.xTickAmount;\n\n          this._drawInvertedXYLines({\n            xCount: xCount,\n            tickAmount: yTickAmount\n          });\n        }\n\n        this.drawGridBands(xCount, yTickAmount);\n        return {\n          el: this.elg,\n          elGridBorders: this.elGridBorders,\n          xAxisTickWidth: w.globals.gridWidth / xCount\n        };\n      }\n    }, {\n      key: \"drawGridBands\",\n      value: function drawGridBands(xCount, tickAmount) {\n        var w = this.w; // rows background bands\n\n        if (w.config.grid.row.colors !== undefined && w.config.grid.row.colors.length > 0) {\n          var x1 = 0;\n          var y1 = 0;\n          var y2 = w.globals.gridHeight / tickAmount;\n          var x2 = w.globals.gridWidth;\n\n          for (var i = 0, c = 0; i < tickAmount; i++, c++) {\n            if (c >= w.config.grid.row.colors.length) {\n              c = 0;\n            }\n\n            this._drawGridBandRect({\n              c: c,\n              x1: x1,\n              y1: y1,\n              x2: x2,\n              y2: y2,\n              type: 'row'\n            });\n\n            y1 = y1 + w.globals.gridHeight / tickAmount;\n          }\n        } // columns background bands\n\n\n        if (w.config.grid.column.colors !== undefined && w.config.grid.column.colors.length > 0) {\n          var xc = !w.globals.isBarHorizontal && (w.config.xaxis.type === 'category' || w.config.xaxis.convertedCatToNumeric) ? xCount - 1 : xCount;\n          var _x5 = w.globals.padHorizontal;\n          var _y5 = 0;\n\n          var _x6 = w.globals.padHorizontal + w.globals.gridWidth / xc;\n\n          var _y6 = w.globals.gridHeight;\n\n          for (var _i2 = 0, _c = 0; _i2 < xCount; _i2++, _c++) {\n            if (_c >= w.config.grid.column.colors.length) {\n              _c = 0;\n            }\n\n            this._drawGridBandRect({\n              c: _c,\n              x1: _x5,\n              y1: _y5,\n              x2: _x6,\n              y2: _y6,\n              type: 'column'\n            });\n\n            _x5 = _x5 + w.globals.gridWidth / xc;\n          }\n        }\n      }\n    }]);\n\n    return Grid;\n  }();\n\n  var Range$1 = /*#__PURE__*/function () {\n    function Range(ctx) {\n      _classCallCheck(this, Range);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    } // http://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axiss\n    // This routine creates the Y axis values for a graph.\n\n\n    _createClass(Range, [{\n      key: \"niceScale\",\n      value: function niceScale(yMin, yMax) {\n        var ticks = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;\n        var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;\n        var NO_MIN_MAX_PROVIDED = arguments.length > 4 ? arguments[4] : undefined;\n        var w = this.w; // Determine Range\n\n        var range = Math.abs(yMax - yMin);\n        ticks = this._adjustTicksForSmallRange(ticks, index, range);\n\n        if (ticks === 'dataPoints') {\n          ticks = w.globals.dataPoints - 1;\n        }\n\n        if (yMin === Number.MIN_VALUE && yMax === 0 || !Utils$1.isNumber(yMin) && !Utils$1.isNumber(yMax) || yMin === Number.MIN_VALUE && yMax === -Number.MAX_VALUE) {\n          // when all values are 0\n          yMin = 0;\n          yMax = ticks;\n          var linearScale = this.linearScale(yMin, yMax, ticks);\n          return linearScale;\n        }\n\n        if (yMin > yMax) {\n          // if somehow due to some wrong config, user sent max less than min,\n          // adjust the min/max again\n          console.warn('axis.min cannot be greater than axis.max');\n          yMax = yMin + 0.1;\n        } else if (yMin === yMax) {\n          // If yMin and yMax are identical, then\n          // adjust the yMin and yMax values to actually\n          // make a graph. Also avoids division by zero errors.\n          yMin = yMin === 0 ? 0 : yMin - 0.5; // some small value\n\n          yMax = yMax === 0 ? 2 : yMax + 0.5; // some small value\n        } // Calculate Min amd Max graphical labels and graph\n        // increments.  The number of ticks defaults to\n        // 10 which is the SUGGESTED value.  Any tick value\n        // entered is used as a suggested value which is\n        // adjusted to be a 'pretty' value.\n        //\n        // Output will be an array of the Y axis values that\n        // encompass the Y values.\n\n\n        var result = [];\n\n        if (range < 1 && NO_MIN_MAX_PROVIDED && (w.config.chart.type === 'candlestick' || w.config.series[index].type === 'candlestick' || w.config.chart.type === 'boxPlot' || w.config.series[index].type === 'boxPlot' || w.globals.isRangeData)) {\n          /* fix https://github.com/apexcharts/apexcharts.js/issues/430 */\n          yMax = yMax * 1.01;\n        }\n\n        var tiks = ticks + 1; // Adjust ticks if needed\n\n        if (tiks < 2) {\n          tiks = 2;\n        } else if (tiks > 2) {\n          tiks -= 2;\n        } // Get raw step value\n\n\n        var tempStep = range / tiks; // Calculate pretty step value\n\n        var mag = Math.floor(Utils$1.log10(tempStep));\n        var magPow = Math.pow(10, mag);\n        var magMsd = Math.round(tempStep / magPow);\n\n        if (magMsd < 1) {\n          magMsd = 1;\n        }\n\n        var stepSize = magMsd * magPow; // build Y label array.\n        // Lower and upper bounds calculations\n\n        var lb = stepSize * Math.floor(yMin / stepSize);\n        var ub = stepSize * Math.ceil(yMax / stepSize); // Build array\n\n        var val = lb;\n\n        if (NO_MIN_MAX_PROVIDED && range > 2) {\n          while (1) {\n            result.push(val);\n            val += stepSize;\n\n            if (val > ub) {\n              break;\n            }\n          }\n\n          return {\n            result: result,\n            niceMin: result[0],\n            niceMax: result[result.length - 1]\n          };\n        } else {\n          result = [];\n          var v = yMin;\n          result.push(v);\n          var valuesDivider = Math.abs(yMax - yMin) / ticks;\n\n          for (var i = 0; i <= ticks; i++) {\n            v = v + valuesDivider;\n            result.push(v);\n          }\n\n          if (result[result.length - 2] >= yMax) {\n            result.pop();\n          }\n\n          return {\n            result: result,\n            niceMin: result[0],\n            niceMax: result[result.length - 1]\n          };\n        }\n      }\n    }, {\n      key: \"linearScale\",\n      value: function linearScale(yMin, yMax) {\n        var ticks = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;\n        var index = arguments.length > 3 ? arguments[3] : undefined;\n        var range = Math.abs(yMax - yMin);\n        ticks = this._adjustTicksForSmallRange(ticks, index, range);\n\n        if (ticks === 'dataPoints') {\n          ticks = this.w.globals.dataPoints - 1;\n        }\n\n        var step = range / ticks;\n\n        if (ticks === Number.MAX_VALUE) {\n          ticks = 10;\n          step = 1;\n        }\n\n        var result = [];\n        var v = yMin;\n\n        while (ticks >= 0) {\n          result.push(v);\n          v = v + step;\n          ticks -= 1;\n        }\n\n        return {\n          result: result,\n          niceMin: result[0],\n          niceMax: result[result.length - 1]\n        };\n      }\n    }, {\n      key: \"logarithmicScaleNice\",\n      value: function logarithmicScaleNice(yMin, yMax, base) {\n        // Basic validation to avoid for loop starting at -inf.\n        if (yMax <= 0) yMax = Math.max(yMin, base);\n        if (yMin <= 0) yMin = Math.min(yMax, base);\n        var logs = [];\n        var logMax = Math.ceil(Math.log(yMax) / Math.log(base) + 1); // Get powers of base for our max and min\n\n        var logMin = Math.floor(Math.log(yMin) / Math.log(base));\n\n        for (var i = logMin; i < logMax; i++) {\n          logs.push(Math.pow(base, i));\n        }\n\n        return {\n          result: logs,\n          niceMin: logs[0],\n          niceMax: logs[logs.length - 1]\n        };\n      }\n    }, {\n      key: \"logarithmicScale\",\n      value: function logarithmicScale(yMin, yMax, base) {\n        // Basic validation to avoid for loop starting at -inf.\n        if (yMax <= 0) yMax = Math.max(yMin, base);\n        if (yMin <= 0) yMin = Math.min(yMax, base);\n        var logs = []; // Get the logarithmic range.\n\n        var logMax = Math.log(yMax) / Math.log(base);\n        var logMin = Math.log(yMin) / Math.log(base); // Get the exact logarithmic range.\n        // (This is the exact number of multiples of the base there are between yMin and yMax).\n\n        var logRange = logMax - logMin; // Round the logarithmic range to get the number of ticks we will create.\n        // If the chosen min/max values are multiples of each other WRT the base, this will be neat.\n        // If the chosen min/max aren't, we will at least still provide USEFUL ticks.\n\n        var ticks = Math.round(logRange); // Get the logarithmic spacing between ticks.\n\n        var logTickSpacing = logRange / ticks; // Create as many ticks as there is range in the logs.\n\n        for (var i = 0, logTick = logMin; i < ticks; i++, logTick += logTickSpacing) {\n          logs.push(Math.pow(base, logTick));\n        } // Add a final tick at the yMax.\n\n\n        logs.push(Math.pow(base, logMax));\n        return {\n          result: logs,\n          niceMin: yMin,\n          niceMax: yMax\n        };\n      }\n    }, {\n      key: \"_adjustTicksForSmallRange\",\n      value: function _adjustTicksForSmallRange(ticks, index, range) {\n        var newTicks = ticks;\n\n        if (typeof index !== 'undefined' && this.w.config.yaxis[index].labels.formatter && this.w.config.yaxis[index].tickAmount === undefined) {\n          var formattedVal = Number(this.w.config.yaxis[index].labels.formatter(1));\n\n          if (Utils$1.isNumber(formattedVal) && this.w.globals.yValueDecimal === 0) {\n            newTicks = Math.ceil(range);\n          }\n        }\n\n        return newTicks < ticks ? newTicks : ticks;\n      }\n    }, {\n      key: \"setYScaleForIndex\",\n      value: function setYScaleForIndex(index, minY, maxY) {\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n        var y = gl.isBarHorizontal ? cnf.xaxis : cnf.yaxis[index];\n\n        if (typeof gl.yAxisScale[index] === 'undefined') {\n          gl.yAxisScale[index] = [];\n        }\n\n        var diff = Math.abs(maxY - minY);\n\n        if (y.logarithmic && diff <= 5) {\n          gl.invalidLogScale = true;\n        }\n\n        if (y.logarithmic && diff > 5) {\n          gl.allSeriesCollapsed = false;\n          gl.yAxisScale[index] = this.logarithmicScale(minY, maxY, y.logBase);\n          gl.yAxisScale[index] = y.forceNiceScale ? this.logarithmicScaleNice(minY, maxY, y.logBase) : this.logarithmicScale(minY, maxY, y.logBase);\n        } else {\n          if (maxY === -Number.MAX_VALUE || !Utils$1.isNumber(maxY)) {\n            // no data in the chart. Either all series collapsed or user passed a blank array\n            gl.yAxisScale[index] = this.linearScale(0, 5, 5);\n          } else {\n            // there is some data. Turn off the allSeriesCollapsed flag\n            gl.allSeriesCollapsed = false;\n\n            if ((y.min !== undefined || y.max !== undefined) && !y.forceNiceScale) {\n              // fix https://github.com/apexcharts/apexcharts.js/issues/492\n              gl.yAxisScale[index] = this.linearScale(minY, maxY, y.tickAmount, index);\n            } else {\n              var noMinMaxProvided = cnf.yaxis[index].max === undefined && cnf.yaxis[index].min === undefined || cnf.yaxis[index].forceNiceScale;\n              gl.yAxisScale[index] = this.niceScale(minY, maxY, y.tickAmount ? y.tickAmount : diff < 5 && diff > 1 ? diff + 1 : 5, index, // fix https://github.com/apexcharts/apexcharts.js/issues/397\n              noMinMaxProvided);\n            }\n          }\n        }\n      }\n    }, {\n      key: \"setXScale\",\n      value: function setXScale(minX, maxX) {\n        var w = this.w;\n        var gl = w.globals;\n        var x = w.config.xaxis;\n        var diff = Math.abs(maxX - minX);\n\n        if (maxX === -Number.MAX_VALUE || !Utils$1.isNumber(maxX)) {\n          // no data in the chart. Either all series collapsed or user passed a blank array\n          gl.xAxisScale = this.linearScale(0, 5, 5);\n        } else {\n          gl.xAxisScale = this.linearScale(minX, maxX, x.tickAmount ? x.tickAmount : diff < 5 && diff > 1 ? diff + 1 : 5, 0);\n        }\n\n        return gl.xAxisScale;\n      }\n    }, {\n      key: \"setMultipleYScales\",\n      value: function setMultipleYScales() {\n        var _this = this;\n\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n        var minYArr = gl.minYArr.concat([]);\n        var maxYArr = gl.maxYArr.concat([]);\n        var scalesIndices = []; // here, we loop through the yaxis array and find the item which has \"seriesName\" property\n\n        cnf.yaxis.forEach(function (yaxe, i) {\n          var index = i;\n          cnf.series.forEach(function (s, si) {\n            // if seriesName matches and that series is not collapsed, we use that scale\n            // fix issue #1215\n            // proceed even if si is in gl.collapsedSeriesIndices\n            if (s.name === yaxe.seriesName) {\n              index = si;\n\n              if (i !== si) {\n                scalesIndices.push({\n                  index: si,\n                  similarIndex: i,\n                  alreadyExists: true\n                });\n              } else {\n                scalesIndices.push({\n                  index: si\n                });\n              }\n            }\n          });\n          var minY = minYArr[index];\n          var maxY = maxYArr[index];\n\n          _this.setYScaleForIndex(i, minY, maxY);\n        });\n        this.sameScaleInMultipleAxes(minYArr, maxYArr, scalesIndices);\n      }\n    }, {\n      key: \"sameScaleInMultipleAxes\",\n      value: function sameScaleInMultipleAxes(minYArr, maxYArr, scalesIndices) {\n        var _this2 = this;\n\n        var cnf = this.w.config;\n        var gl = this.w.globals; // we got the scalesIndices array in the above code, but we need to filter out the items which doesn't have same scales\n\n        var similarIndices = [];\n        scalesIndices.forEach(function (scale) {\n          if (scale.alreadyExists) {\n            if (typeof similarIndices[scale.index] === 'undefined') {\n              similarIndices[scale.index] = [];\n            }\n\n            similarIndices[scale.index].push(scale.index);\n            similarIndices[scale.index].push(scale.similarIndex);\n          }\n        });\n\n        function intersect(a, b) {\n          return a.filter(function (value) {\n            return b.indexOf(value) !== -1;\n          });\n        }\n\n        gl.yAxisSameScaleIndices = similarIndices;\n        similarIndices.forEach(function (si, i) {\n          similarIndices.forEach(function (sj, j) {\n            if (i !== j) {\n              if (intersect(si, sj).length > 0) {\n                similarIndices[i] = similarIndices[i].concat(similarIndices[j]);\n              }\n            }\n          });\n        }); // then, we remove duplicates from the similarScale array\n\n        var uniqueSimilarIndices = similarIndices.map(function (item) {\n          return item.filter(function (i, pos) {\n            return item.indexOf(i) === pos;\n          });\n        }); // sort further to remove whole duplicate arrays later\n\n        var sortedIndices = uniqueSimilarIndices.map(function (s) {\n          return s.sort();\n        }); // remove undefined items\n\n        similarIndices = similarIndices.filter(function (s) {\n          return !!s;\n        });\n        var indices = sortedIndices.slice();\n        var stringIndices = indices.map(function (ind) {\n          return JSON.stringify(ind);\n        });\n        indices = indices.filter(function (ind, p) {\n          return stringIndices.indexOf(JSON.stringify(ind)) === p;\n        });\n        var sameScaleMinYArr = [];\n        var sameScaleMaxYArr = [];\n        minYArr.forEach(function (minYValue, yi) {\n          indices.forEach(function (scale, i) {\n            // we compare only the yIndex which exists in the indices array\n            if (scale.indexOf(yi) > -1) {\n              if (typeof sameScaleMinYArr[i] === 'undefined') {\n                sameScaleMinYArr[i] = [];\n                sameScaleMaxYArr[i] = [];\n              }\n\n              sameScaleMinYArr[i].push({\n                key: yi,\n                value: minYValue\n              });\n              sameScaleMaxYArr[i].push({\n                key: yi,\n                value: maxYArr[yi]\n              });\n            }\n          });\n        });\n        var sameScaleMin = Array.apply(null, Array(indices.length)).map(Number.prototype.valueOf, Number.MIN_VALUE);\n        var sameScaleMax = Array.apply(null, Array(indices.length)).map(Number.prototype.valueOf, -Number.MAX_VALUE);\n        sameScaleMinYArr.forEach(function (s, i) {\n          s.forEach(function (sc, j) {\n            sameScaleMin[i] = Math.min(sc.value, sameScaleMin[i]);\n          });\n        });\n        sameScaleMaxYArr.forEach(function (s, i) {\n          s.forEach(function (sc, j) {\n            sameScaleMax[i] = Math.max(sc.value, sameScaleMax[i]);\n          });\n        });\n        minYArr.forEach(function (min, i) {\n          sameScaleMaxYArr.forEach(function (s, si) {\n            var minY = sameScaleMin[si];\n            var maxY = sameScaleMax[si];\n\n            if (cnf.chart.stacked) {\n              // for stacked charts, we need to add the values\n              maxY = 0;\n              s.forEach(function (ind, k) {\n                // fix incorrectly adjust y scale issue #1215\n                if (ind.value !== -Number.MAX_VALUE) {\n                  maxY += ind.value;\n                }\n\n                if (minY !== Number.MIN_VALUE) {\n                  minY += sameScaleMinYArr[si][k].value;\n                }\n              });\n            }\n\n            s.forEach(function (ind, k) {\n              if (s[k].key === i) {\n                if (cnf.yaxis[i].min !== undefined) {\n                  if (typeof cnf.yaxis[i].min === 'function') {\n                    minY = cnf.yaxis[i].min(gl.minY);\n                  } else {\n                    minY = cnf.yaxis[i].min;\n                  }\n                }\n\n                if (cnf.yaxis[i].max !== undefined) {\n                  if (typeof cnf.yaxis[i].max === 'function') {\n                    maxY = cnf.yaxis[i].max(gl.maxY);\n                  } else {\n                    maxY = cnf.yaxis[i].max;\n                  }\n                }\n\n                _this2.setYScaleForIndex(i, minY, maxY);\n              }\n            });\n          });\n        });\n      } // experimental feature which scales the y-axis to a min/max based on x-axis range\n\n    }, {\n      key: \"autoScaleY\",\n      value: function autoScaleY(ctx, yaxis, e) {\n        if (!ctx) {\n          ctx = this;\n        }\n\n        var w = ctx.w;\n\n        if (w.globals.isMultipleYAxis || w.globals.collapsedSeries.length) {\n          // The autoScale option for multiple y-axis is turned off as it leads to buggy behavior.\n          // Also, when a series is collapsed, it results in incorrect behavior. Hence turned it off for that too - fixes apexcharts.js#795\n          console.warn('autoScaleYaxis is not supported in a multi-yaxis chart.');\n          return yaxis;\n        }\n\n        var seriesX = w.globals.seriesX[0];\n        var isStacked = w.config.chart.stacked;\n        yaxis.forEach(function (yaxe, yi) {\n          var firstXIndex = 0;\n\n          for (var xi = 0; xi < seriesX.length; xi++) {\n            if (seriesX[xi] >= e.xaxis.min) {\n              firstXIndex = xi;\n              break;\n            }\n          }\n\n          var initialMin = w.globals.minYArr[yi];\n          var initialMax = w.globals.maxYArr[yi];\n          var min, max;\n          var stackedSer = w.globals.stackedSeriesTotals;\n          w.globals.series.forEach(function (serie, sI) {\n            var firstValue = serie[firstXIndex];\n\n            if (isStacked) {\n              firstValue = stackedSer[firstXIndex];\n              min = max = firstValue;\n              stackedSer.forEach(function (y, yI) {\n                if (seriesX[yI] <= e.xaxis.max && seriesX[yI] >= e.xaxis.min) {\n                  if (y > max && y !== null) max = y;\n                  if (serie[yI] < min && serie[yI] !== null) min = serie[yI];\n                }\n              });\n            } else {\n              min = max = firstValue;\n              serie.forEach(function (y, yI) {\n                if (seriesX[yI] <= e.xaxis.max && seriesX[yI] >= e.xaxis.min) {\n                  var valMin = y;\n                  var valMax = y;\n                  w.globals.series.forEach(function (wS, wSI) {\n                    if (y !== null) {\n                      valMin = Math.min(wS[yI], valMin);\n                      valMax = Math.max(wS[yI], valMax);\n                    }\n                  });\n                  if (valMax > max && valMax !== null) max = valMax;\n                  if (valMin < min && valMin !== null) min = valMin;\n                }\n              });\n            }\n\n            if (min === undefined && max === undefined) {\n              min = initialMin;\n              max = initialMax;\n            }\n\n            min *= min < 0 ? 1.1 : 0.9;\n            max *= max < 0 ? 0.9 : 1.1;\n\n            if (min === 0 && max === 0) {\n              min = -1;\n              max = 1;\n            }\n\n            if (max < 0 && max < initialMax) {\n              max = initialMax;\n            }\n\n            if (min < 0 && min > initialMin) {\n              min = initialMin;\n            }\n\n            if (yaxis.length > 1) {\n              yaxis[sI].min = yaxe.min === undefined ? min : yaxe.min;\n              yaxis[sI].max = yaxe.max === undefined ? max : yaxe.max;\n            } else {\n              yaxis[0].min = yaxe.min === undefined ? min : yaxe.min;\n              yaxis[0].max = yaxe.max === undefined ? max : yaxe.max;\n            }\n          });\n        });\n        return yaxis;\n      }\n    }]);\n\n    return Range;\n  }();\n\n  /**\n   * Range is used to generates values between min and max.\n   *\n   * @module Range\n   **/\n\n  var Range = /*#__PURE__*/function () {\n    function Range(ctx) {\n      _classCallCheck(this, Range);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.scales = new Range$1(ctx);\n    }\n\n    _createClass(Range, [{\n      key: \"init\",\n      value: function init() {\n        this.setYRange();\n        this.setXRange();\n        this.setZRange();\n      }\n    }, {\n      key: \"getMinYMaxY\",\n      value: function getMinYMaxY(startingIndex) {\n        var lowestY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;\n        var highestY = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : -Number.MAX_VALUE;\n        var len = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;\n        var cnf = this.w.config;\n        var gl = this.w.globals;\n        var maxY = -Number.MAX_VALUE;\n        var minY = Number.MIN_VALUE;\n\n        if (len === null) {\n          len = startingIndex + 1;\n        }\n\n        var series = gl.series;\n        var seriesMin = series;\n        var seriesMax = series;\n\n        if (cnf.chart.type === 'candlestick') {\n          seriesMin = gl.seriesCandleL;\n          seriesMax = gl.seriesCandleH;\n        } else if (cnf.chart.type === 'boxPlot') {\n          seriesMin = gl.seriesCandleO;\n          seriesMax = gl.seriesCandleC;\n        } else if (gl.isRangeData) {\n          seriesMin = gl.seriesRangeStart;\n          seriesMax = gl.seriesRangeEnd;\n        }\n\n        for (var i = startingIndex; i < len; i++) {\n          gl.dataPoints = Math.max(gl.dataPoints, series[i].length);\n\n          if (gl.categoryLabels.length) {\n            gl.dataPoints = gl.categoryLabels.filter(function (label) {\n              return typeof label !== 'undefined';\n            }).length;\n          }\n\n          for (var j = 0; j < gl.series[i].length; j++) {\n            var val = series[i][j];\n\n            if (val !== null && Utils$1.isNumber(val)) {\n              if (typeof seriesMax[i][j] !== 'undefined') {\n                maxY = Math.max(maxY, seriesMax[i][j]);\n                lowestY = Math.min(lowestY, seriesMax[i][j]);\n              }\n\n              if (typeof seriesMin[i][j] !== 'undefined') {\n                lowestY = Math.min(lowestY, seriesMin[i][j]);\n                highestY = Math.max(highestY, seriesMin[i][j]);\n              }\n\n              if (this.w.config.chart.type === 'candlestick' || this.w.config.chart.type === 'boxPlot' || this.w.config.chart.type !== 'rangeArea' || this.w.config.chart.type !== 'rangeBar') {\n                if (this.w.config.chart.type === 'candlestick' || this.w.config.chart.type === 'boxPlot') {\n                  if (typeof gl.seriesCandleC[i][j] !== 'undefined') {\n                    maxY = Math.max(maxY, gl.seriesCandleO[i][j]);\n                    maxY = Math.max(maxY, gl.seriesCandleH[i][j]);\n                    maxY = Math.max(maxY, gl.seriesCandleL[i][j]);\n                    maxY = Math.max(maxY, gl.seriesCandleC[i][j]);\n\n                    if (this.w.config.chart.type === 'boxPlot') {\n                      maxY = Math.max(maxY, gl.seriesCandleM[i][j]);\n                    }\n                  }\n                } // there is a combo chart and the specified series in not either candlestick, boxplot, or rangeArea/rangeBar; find the max there\n\n\n                if (cnf.series[i].type && (cnf.series[i].type !== 'candlestick' || cnf.series[i].type !== 'boxPlot' || cnf.series[i].type !== 'rangeArea' || cnf.series[i].type !== 'rangeBar')) {\n                  maxY = Math.max(maxY, gl.series[i][j]);\n                  lowestY = Math.min(lowestY, gl.series[i][j]);\n                }\n\n                highestY = maxY;\n              }\n\n              if (gl.seriesGoals[i] && gl.seriesGoals[i][j] && Array.isArray(gl.seriesGoals[i][j])) {\n                gl.seriesGoals[i][j].forEach(function (g) {\n                  if (minY !== Number.MIN_VALUE) {\n                    minY = Math.min(minY, g.value);\n                    lowestY = minY;\n                  }\n\n                  maxY = Math.max(maxY, g.value);\n                  highestY = maxY;\n                });\n              }\n\n              if (Utils$1.isFloat(val)) {\n                val = Utils$1.noExponents(val);\n                gl.yValueDecimal = Math.max(gl.yValueDecimal, val.toString().split('.')[1].length);\n              }\n\n              if (minY > seriesMin[i][j] && seriesMin[i][j] < 0) {\n                minY = seriesMin[i][j];\n              }\n            } else {\n              gl.hasNullValues = true;\n            }\n          }\n        }\n\n        if (cnf.chart.type === 'rangeBar' && gl.seriesRangeStart.length && gl.isBarHorizontal) {\n          minY = lowestY;\n        }\n\n        if (cnf.chart.type === 'bar') {\n          if (minY < 0 && maxY < 0) {\n            // all negative values in a bar chart, hence make the max to 0\n            maxY = 0;\n          }\n\n          if (minY === Number.MIN_VALUE) {\n            minY = 0;\n          }\n        }\n\n        return {\n          minY: minY,\n          maxY: maxY,\n          lowestY: lowestY,\n          highestY: highestY\n        };\n      }\n    }, {\n      key: \"setYRange\",\n      value: function setYRange() {\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n        gl.maxY = -Number.MAX_VALUE;\n        gl.minY = Number.MIN_VALUE;\n        var lowestYInAllSeries = Number.MAX_VALUE;\n\n        if (gl.isMultipleYAxis) {\n          // we need to get minY and maxY for multiple y axis\n          for (var i = 0; i < gl.series.length; i++) {\n            var minYMaxYArr = this.getMinYMaxY(i, lowestYInAllSeries, null, i + 1);\n            gl.minYArr.push(minYMaxYArr.minY);\n            gl.maxYArr.push(minYMaxYArr.maxY);\n            lowestYInAllSeries = minYMaxYArr.lowestY;\n          }\n        } // and then, get the minY and maxY from all series\n\n\n        var minYMaxY = this.getMinYMaxY(0, lowestYInAllSeries, null, gl.series.length);\n        gl.minY = minYMaxY.minY;\n        gl.maxY = minYMaxY.maxY;\n        lowestYInAllSeries = minYMaxY.lowestY;\n\n        if (cnf.chart.stacked) {\n          this._setStackedMinMax();\n        } // if the numbers are too big, reduce the range\n        // for eg, if number is between 100000-110000, putting 0 as the lowest value is not so good idea. So change the gl.minY for line/area/candlesticks/boxPlot\n\n\n        if (cnf.chart.type === 'line' || cnf.chart.type === 'area' || cnf.chart.type === 'candlestick' || cnf.chart.type === 'boxPlot' || cnf.chart.type === 'rangeBar' && !gl.isBarHorizontal) {\n          if (gl.minY === Number.MIN_VALUE && lowestYInAllSeries !== -Number.MAX_VALUE && lowestYInAllSeries !== gl.maxY // single value possibility\n          ) {\n            var diff = gl.maxY - lowestYInAllSeries;\n\n            if (lowestYInAllSeries >= 0 && lowestYInAllSeries <= 10 || cnf.yaxis[0].min !== undefined || cnf.yaxis[0].max !== undefined) {\n              // if minY is already 0/low value, we don't want to go negatives here - so this check is essential.\n              diff = 0;\n            }\n\n            gl.minY = lowestYInAllSeries - diff * 5 / 100;\n            /* fix https://github.com/apexcharts/apexcharts.js/issues/614 */\n\n            /* fix https://github.com/apexcharts/apexcharts.js/issues/968 */\n\n            if (lowestYInAllSeries > 0 && gl.minY < 0) {\n              gl.minY = 0;\n            }\n            /* fix https://github.com/apexcharts/apexcharts.js/issues/426 */\n\n\n            gl.maxY = gl.maxY + diff * 5 / 100;\n          }\n        }\n\n        cnf.yaxis.forEach(function (yaxe, index) {\n          // override all min/max values by user defined values (y axis)\n          if (yaxe.max !== undefined) {\n            if (typeof yaxe.max === 'number') {\n              gl.maxYArr[index] = yaxe.max;\n            } else if (typeof yaxe.max === 'function') {\n              // fixes apexcharts.js/issues/2098\n              gl.maxYArr[index] = yaxe.max(gl.isMultipleYAxis ? gl.maxYArr[index] : gl.maxY);\n            } // gl.maxY is for single y-axis chart, it will be ignored in multi-yaxis\n\n\n            gl.maxY = gl.maxYArr[index];\n          }\n\n          if (yaxe.min !== undefined) {\n            if (typeof yaxe.min === 'number') {\n              gl.minYArr[index] = yaxe.min;\n            } else if (typeof yaxe.min === 'function') {\n              // fixes apexcharts.js/issues/2098\n              gl.minYArr[index] = yaxe.min(gl.isMultipleYAxis ? gl.minYArr[index] === Number.MIN_VALUE ? 0 : gl.minYArr[index] : gl.minY);\n            } // gl.minY is for single y-axis chart, it will be ignored in multi-yaxis\n\n\n            gl.minY = gl.minYArr[index];\n          }\n        }); // for horizontal bar charts, we need to check xaxis min/max as user may have specified there\n\n        if (gl.isBarHorizontal) {\n          var minmax = ['min', 'max'];\n          minmax.forEach(function (m) {\n            if (cnf.xaxis[m] !== undefined && typeof cnf.xaxis[m] === 'number') {\n              m === 'min' ? gl.minY = cnf.xaxis[m] : gl.maxY = cnf.xaxis[m];\n            }\n          });\n        } // for multi y-axis we need different scales for each\n\n\n        if (gl.isMultipleYAxis) {\n          this.scales.setMultipleYScales();\n          gl.minY = lowestYInAllSeries;\n          gl.yAxisScale.forEach(function (scale, i) {\n            gl.minYArr[i] = scale.niceMin;\n            gl.maxYArr[i] = scale.niceMax;\n          });\n        } else {\n          this.scales.setYScaleForIndex(0, gl.minY, gl.maxY);\n          gl.minY = gl.yAxisScale[0].niceMin;\n          gl.maxY = gl.yAxisScale[0].niceMax;\n          gl.minYArr[0] = gl.yAxisScale[0].niceMin;\n          gl.maxYArr[0] = gl.yAxisScale[0].niceMax;\n        }\n\n        return {\n          minY: gl.minY,\n          maxY: gl.maxY,\n          minYArr: gl.minYArr,\n          maxYArr: gl.maxYArr,\n          yAxisScale: gl.yAxisScale\n        };\n      }\n    }, {\n      key: \"setXRange\",\n      value: function setXRange() {\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n        var isXNumeric = cnf.xaxis.type === 'numeric' || cnf.xaxis.type === 'datetime' || cnf.xaxis.type === 'category' && !gl.noLabelsProvided || gl.noLabelsProvided || gl.isXNumeric;\n\n        var getInitialMinXMaxX = function getInitialMinXMaxX() {\n          for (var i = 0; i < gl.series.length; i++) {\n            if (gl.labels[i]) {\n              for (var j = 0; j < gl.labels[i].length; j++) {\n                if (gl.labels[i][j] !== null && Utils$1.isNumber(gl.labels[i][j])) {\n                  gl.maxX = Math.max(gl.maxX, gl.labels[i][j]);\n                  gl.initialMaxX = Math.max(gl.maxX, gl.labels[i][j]);\n                  gl.minX = Math.min(gl.minX, gl.labels[i][j]);\n                  gl.initialMinX = Math.min(gl.minX, gl.labels[i][j]);\n                }\n              }\n            }\n          }\n        }; // minX maxX starts here\n\n\n        if (gl.isXNumeric) {\n          getInitialMinXMaxX();\n        }\n\n        if (gl.noLabelsProvided) {\n          if (cnf.xaxis.categories.length === 0) {\n            gl.maxX = gl.labels[gl.labels.length - 1];\n            gl.initialMaxX = gl.labels[gl.labels.length - 1];\n            gl.minX = 1;\n            gl.initialMinX = 1;\n          }\n        }\n\n        if (gl.isXNumeric || gl.noLabelsProvided || gl.dataFormatXNumeric) {\n          var ticks;\n\n          if (cnf.xaxis.tickAmount === undefined) {\n            ticks = Math.round(gl.svgWidth / 150); // no labels provided and total number of dataPoints is less than 30\n\n            if (cnf.xaxis.type === 'numeric' && gl.dataPoints < 30) {\n              ticks = gl.dataPoints - 1;\n            } // this check is for when ticks exceeds total datapoints and that would result in duplicate labels\n\n\n            if (ticks > gl.dataPoints && gl.dataPoints !== 0) {\n              ticks = gl.dataPoints - 1;\n            }\n          } else if (cnf.xaxis.tickAmount === 'dataPoints') {\n            if (gl.series.length > 1) {\n              ticks = gl.series[gl.maxValsInArrayIndex].length - 1;\n            }\n\n            if (gl.isXNumeric) {\n              ticks = gl.maxX - gl.minX - 1;\n            }\n          } else {\n            ticks = cnf.xaxis.tickAmount;\n          }\n\n          gl.xTickAmount = ticks; // override all min/max values by user defined values (x axis)\n\n          if (cnf.xaxis.max !== undefined && typeof cnf.xaxis.max === 'number') {\n            gl.maxX = cnf.xaxis.max;\n          }\n\n          if (cnf.xaxis.min !== undefined && typeof cnf.xaxis.min === 'number') {\n            gl.minX = cnf.xaxis.min;\n          } // if range is provided, adjust the new minX\n\n\n          if (cnf.xaxis.range !== undefined) {\n            gl.minX = gl.maxX - cnf.xaxis.range;\n          }\n\n          if (gl.minX !== Number.MAX_VALUE && gl.maxX !== -Number.MAX_VALUE) {\n            if (cnf.xaxis.convertedCatToNumeric && !gl.dataFormatXNumeric) {\n              var catScale = [];\n\n              for (var i = gl.minX - 1; i < gl.maxX; i++) {\n                catScale.push(i + 1);\n              }\n\n              gl.xAxisScale = {\n                result: catScale,\n                niceMin: catScale[0],\n                niceMax: catScale[catScale.length - 1]\n              };\n            } else {\n              gl.xAxisScale = this.scales.setXScale(gl.minX, gl.maxX);\n            }\n          } else {\n            gl.xAxisScale = this.scales.linearScale(1, ticks, ticks);\n\n            if (gl.noLabelsProvided && gl.labels.length > 0) {\n              gl.xAxisScale = this.scales.linearScale(1, gl.labels.length, ticks - 1); // this is the only place seriesX is again mutated\n\n              gl.seriesX = gl.labels.slice();\n            }\n          } // we will still store these labels as the count for this will be different (to draw grid and labels placement)\n\n\n          if (isXNumeric) {\n            gl.labels = gl.xAxisScale.result.slice();\n          }\n        }\n\n        if (gl.isBarHorizontal && gl.labels.length) {\n          gl.xTickAmount = gl.labels.length;\n        } // single dataPoint\n\n\n        this._handleSingleDataPoint(); // minimum x difference to calculate bar width in numeric bars\n\n\n        this._getMinXDiff();\n\n        return {\n          minX: gl.minX,\n          maxX: gl.maxX\n        };\n      }\n    }, {\n      key: \"setZRange\",\n      value: function setZRange() {\n        // minZ, maxZ starts here\n        var gl = this.w.globals;\n        if (!gl.isDataXYZ) return;\n\n        for (var i = 0; i < gl.series.length; i++) {\n          if (typeof gl.seriesZ[i] !== 'undefined') {\n            for (var j = 0; j < gl.seriesZ[i].length; j++) {\n              if (gl.seriesZ[i][j] !== null && Utils$1.isNumber(gl.seriesZ[i][j])) {\n                gl.maxZ = Math.max(gl.maxZ, gl.seriesZ[i][j]);\n                gl.minZ = Math.min(gl.minZ, gl.seriesZ[i][j]);\n              }\n            }\n          }\n        }\n      }\n    }, {\n      key: \"_handleSingleDataPoint\",\n      value: function _handleSingleDataPoint() {\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n\n        if (gl.minX === gl.maxX) {\n          var datetimeObj = new DateTime(this.ctx);\n\n          if (cnf.xaxis.type === 'datetime') {\n            var newMinX = datetimeObj.getDate(gl.minX);\n\n            if (cnf.xaxis.labels.datetimeUTC) {\n              newMinX.setUTCDate(newMinX.getUTCDate() - 2);\n            } else {\n              newMinX.setDate(newMinX.getDate() - 2);\n            }\n\n            gl.minX = new Date(newMinX).getTime();\n            var newMaxX = datetimeObj.getDate(gl.maxX);\n\n            if (cnf.xaxis.labels.datetimeUTC) {\n              newMaxX.setUTCDate(newMaxX.getUTCDate() + 2);\n            } else {\n              newMaxX.setDate(newMaxX.getDate() + 2);\n            }\n\n            gl.maxX = new Date(newMaxX).getTime();\n          } else if (cnf.xaxis.type === 'numeric' || cnf.xaxis.type === 'category' && !gl.noLabelsProvided) {\n            gl.minX = gl.minX - 2;\n            gl.initialMinX = gl.minX;\n            gl.maxX = gl.maxX + 2;\n            gl.initialMaxX = gl.maxX;\n          }\n        }\n      }\n    }, {\n      key: \"_getMinXDiff\",\n      value: function _getMinXDiff() {\n        var gl = this.w.globals;\n\n        if (gl.isXNumeric) {\n          // get the least x diff if numeric x axis is present\n          gl.seriesX.forEach(function (sX, i) {\n            if (sX.length === 1) {\n              // a small hack to prevent overlapping multiple bars when there is just 1 datapoint in bar series.\n              // fix #811\n              sX.push(gl.seriesX[gl.maxValsInArrayIndex][gl.seriesX[gl.maxValsInArrayIndex].length - 1]);\n            } // fix #983 (clone the array to avoid side effects)\n\n\n            var seriesX = sX.slice();\n            seriesX.sort(function (a, b) {\n              return a - b;\n            });\n            seriesX.forEach(function (s, j) {\n              if (j > 0) {\n                var xDiff = s - seriesX[j - 1];\n\n                if (xDiff > 0) {\n                  gl.minXDiff = Math.min(xDiff, gl.minXDiff);\n                }\n              }\n            });\n\n            if (gl.dataPoints === 1 || gl.minXDiff === Number.MAX_VALUE) {\n              // fixes apexcharts.js #1221\n              gl.minXDiff = 0.5;\n            }\n          });\n        }\n      }\n    }, {\n      key: \"_setStackedMinMax\",\n      value: function _setStackedMinMax() {\n        var gl = this.w.globals; // for stacked charts, we calculate each series's parallel values. i.e, series[0][j] + series[1][j] .... [series[i.length][j]] and get the max out of it\n\n        var stackedPoss = [];\n        var stackedNegs = [];\n\n        if (gl.series.length) {\n          for (var j = 0; j < gl.series[gl.maxValsInArrayIndex].length; j++) {\n            var poss = 0;\n            var negs = 0;\n\n            for (var i = 0; i < gl.series.length; i++) {\n              if (gl.series[i][j] !== null && Utils$1.isNumber(gl.series[i][j])) {\n                // 0.0001 fixes #185 when values are very small\n                gl.series[i][j] > 0 ? poss = poss + parseFloat(gl.series[i][j]) + 0.0001 : negs = negs + parseFloat(gl.series[i][j]);\n              }\n\n              if (i === gl.series.length - 1) {\n                // push all the totals to the array for future use\n                stackedPoss.push(poss);\n                stackedNegs.push(negs);\n              }\n            }\n          }\n        } // get the max/min out of the added parallel values\n\n\n        for (var z = 0; z < stackedPoss.length; z++) {\n          gl.maxY = Math.max(gl.maxY, stackedPoss[z]);\n          gl.minY = Math.min(gl.minY, stackedNegs[z]);\n        }\n      }\n    }]);\n\n    return Range;\n  }();\n\n  /**\n   * ApexCharts YAxis Class for drawing Y-Axis.\n   *\n   * @module YAxis\n   **/\n\n  var YAxis = /*#__PURE__*/function () {\n    function YAxis(ctx, elgrid) {\n      _classCallCheck(this, YAxis);\n\n      this.ctx = ctx;\n      this.elgrid = elgrid;\n      this.w = ctx.w;\n      var w = this.w;\n      this.xaxisFontSize = w.config.xaxis.labels.style.fontSize;\n      this.axisFontFamily = w.config.xaxis.labels.style.fontFamily;\n      this.xaxisForeColors = w.config.xaxis.labels.style.colors;\n      this.isCategoryBarHorizontal = w.config.chart.type === 'bar' && w.config.plotOptions.bar.horizontal;\n      this.xAxisoffX = 0;\n\n      if (w.config.xaxis.position === 'bottom') {\n        this.xAxisoffX = w.globals.gridHeight;\n      }\n\n      this.drawnLabels = [];\n      this.axesUtils = new AxesUtils(ctx);\n    }\n\n    _createClass(YAxis, [{\n      key: \"drawYaxis\",\n      value: function drawYaxis(realIndex) {\n        var _this = this;\n\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var yaxisStyle = w.config.yaxis[realIndex].labels.style;\n        var yaxisFontSize = yaxisStyle.fontSize;\n        var yaxisFontFamily = yaxisStyle.fontFamily;\n        var yaxisFontWeight = yaxisStyle.fontWeight;\n        var elYaxis = graphics.group({\n          class: 'apexcharts-yaxis',\n          rel: realIndex,\n          transform: 'translate(' + w.globals.translateYAxisX[realIndex] + ', 0)'\n        });\n\n        if (this.axesUtils.isYAxisHidden(realIndex)) {\n          return elYaxis;\n        }\n\n        var elYaxisTexts = graphics.group({\n          class: 'apexcharts-yaxis-texts-g'\n        });\n        elYaxis.add(elYaxisTexts);\n        var tickAmount = w.globals.yAxisScale[realIndex].result.length - 1; // labelsDivider is simply svg height/number of ticks\n\n        var labelsDivider = w.globals.gridHeight / tickAmount; // initial label position = 0;\n\n        var l = w.globals.translateY;\n        var lbFormatter = w.globals.yLabelFormatters[realIndex];\n        var labels = w.globals.yAxisScale[realIndex].result.slice();\n        labels = this.axesUtils.checkForReversedLabels(realIndex, labels);\n        var firstLabel = '';\n\n        if (w.config.yaxis[realIndex].labels.show) {\n          var _loop = function _loop(i) {\n            var val = labels[i];\n            val = lbFormatter(val, i, w);\n            var xPad = w.config.yaxis[realIndex].labels.padding;\n\n            if (w.config.yaxis[realIndex].opposite && w.config.yaxis.length !== 0) {\n              xPad = xPad * -1;\n            }\n\n            var textAnchor = 'end';\n\n            if (w.config.yaxis[realIndex].opposite) {\n              textAnchor = 'start';\n            }\n\n            if (w.config.yaxis[realIndex].labels.align === 'left') {\n              textAnchor = 'start';\n            } else if (w.config.yaxis[realIndex].labels.align === 'center') {\n              textAnchor = 'middle';\n            } else if (w.config.yaxis[realIndex].labels.align === 'right') {\n              textAnchor = 'end';\n            }\n\n            var yColors = _this.axesUtils.getYAxisForeColor(yaxisStyle.colors, realIndex);\n\n            var getForeColor = function getForeColor() {\n              return Array.isArray(yColors) ? yColors[i] : yColors;\n            };\n\n            var label = graphics.drawText({\n              x: xPad,\n              y: l + tickAmount / 10 + w.config.yaxis[realIndex].labels.offsetY + 1,\n              text: val,\n              textAnchor: textAnchor,\n              fontSize: yaxisFontSize,\n              fontFamily: yaxisFontFamily,\n              fontWeight: yaxisFontWeight,\n              maxWidth: w.config.yaxis[realIndex].labels.maxWidth,\n              foreColor: getForeColor(),\n              isPlainText: false,\n              cssClass: 'apexcharts-yaxis-label ' + yaxisStyle.cssClass\n            });\n\n            if (i === tickAmount) {\n              firstLabel = label;\n            }\n\n            elYaxisTexts.add(label);\n            var elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title');\n            elTooltipTitle.textContent = Array.isArray(val) ? val.join(' ') : val;\n            label.node.appendChild(elTooltipTitle);\n\n            if (w.config.yaxis[realIndex].labels.rotate !== 0) {\n              var firstabelRotatingCenter = graphics.rotateAroundCenter(firstLabel.node);\n              var labelRotatingCenter = graphics.rotateAroundCenter(label.node);\n              label.node.setAttribute('transform', \"rotate(\".concat(w.config.yaxis[realIndex].labels.rotate, \" \").concat(firstabelRotatingCenter.x, \" \").concat(labelRotatingCenter.y, \")\"));\n            }\n\n            l = l + labelsDivider;\n          };\n\n          for (var i = tickAmount; i >= 0; i--) {\n            _loop(i);\n          }\n        }\n\n        if (w.config.yaxis[realIndex].title.text !== undefined) {\n          var elYaxisTitle = graphics.group({\n            class: 'apexcharts-yaxis-title'\n          });\n          var _x = 0;\n\n          if (w.config.yaxis[realIndex].opposite) {\n            _x = w.globals.translateYAxisX[realIndex];\n          }\n\n          var elYAxisTitleText = graphics.drawText({\n            x: _x,\n            y: w.globals.gridHeight / 2 + w.globals.translateY + w.config.yaxis[realIndex].title.offsetY,\n            text: w.config.yaxis[realIndex].title.text,\n            textAnchor: 'end',\n            foreColor: w.config.yaxis[realIndex].title.style.color,\n            fontSize: w.config.yaxis[realIndex].title.style.fontSize,\n            fontWeight: w.config.yaxis[realIndex].title.style.fontWeight,\n            fontFamily: w.config.yaxis[realIndex].title.style.fontFamily,\n            cssClass: 'apexcharts-yaxis-title-text ' + w.config.yaxis[realIndex].title.style.cssClass\n          });\n          elYaxisTitle.add(elYAxisTitleText);\n          elYaxis.add(elYaxisTitle);\n        }\n\n        var axisBorder = w.config.yaxis[realIndex].axisBorder;\n        var x = 31 + axisBorder.offsetX;\n\n        if (w.config.yaxis[realIndex].opposite) {\n          x = -31 - axisBorder.offsetX;\n        }\n\n        if (axisBorder.show) {\n          var elVerticalLine = graphics.drawLine(x, w.globals.translateY + axisBorder.offsetY - 2, x, w.globals.gridHeight + w.globals.translateY + axisBorder.offsetY + 2, axisBorder.color, 0, axisBorder.width);\n          elYaxis.add(elVerticalLine);\n        }\n\n        if (w.config.yaxis[realIndex].axisTicks.show) {\n          this.axesUtils.drawYAxisTicks(x, tickAmount, axisBorder, w.config.yaxis[realIndex].axisTicks, realIndex, labelsDivider, elYaxis);\n        }\n\n        return elYaxis;\n      } // This actually becomes horizontal axis (for bar charts)\n\n    }, {\n      key: \"drawYaxisInversed\",\n      value: function drawYaxisInversed(realIndex) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var elXaxis = graphics.group({\n          class: 'apexcharts-xaxis apexcharts-yaxis-inversed'\n        });\n        var elXaxisTexts = graphics.group({\n          class: 'apexcharts-xaxis-texts-g',\n          transform: \"translate(\".concat(w.globals.translateXAxisX, \", \").concat(w.globals.translateXAxisY, \")\")\n        });\n        elXaxis.add(elXaxisTexts);\n        var tickAmount = w.globals.yAxisScale[realIndex].result.length - 1; // labelsDivider is simply svg width/number of ticks\n\n        var labelsDivider = w.globals.gridWidth / tickAmount + 0.1; // initial label position;\n\n        var l = labelsDivider + w.config.xaxis.labels.offsetX;\n        var lbFormatter = w.globals.xLabelFormatter;\n        var labels = w.globals.yAxisScale[realIndex].result.slice();\n        var timescaleLabels = w.globals.timescaleLabels;\n\n        if (timescaleLabels.length > 0) {\n          this.xaxisLabels = timescaleLabels.slice();\n          labels = timescaleLabels.slice();\n          tickAmount = labels.length;\n        }\n\n        labels = this.axesUtils.checkForReversedLabels(realIndex, labels);\n        var tl = timescaleLabels.length;\n\n        if (w.config.xaxis.labels.show) {\n          for (var i = tl ? 0 : tickAmount; tl ? i < tl : i >= 0; tl ? i++ : i--) {\n            var val = labels[i];\n            val = lbFormatter(val, i, w);\n            var x = w.globals.gridWidth + w.globals.padHorizontal - (l - labelsDivider + w.config.xaxis.labels.offsetX);\n\n            if (timescaleLabels.length) {\n              var label = this.axesUtils.getLabel(labels, timescaleLabels, x, i, this.drawnLabels, this.xaxisFontSize);\n              x = label.x;\n              val = label.text;\n              this.drawnLabels.push(label.text);\n\n              if (i === 0 && w.globals.skipFirstTimelinelabel) {\n                val = '';\n              }\n\n              if (i === labels.length - 1 && w.globals.skipLastTimelinelabel) {\n                val = '';\n              }\n            }\n\n            var elTick = graphics.drawText({\n              x: x,\n              y: this.xAxisoffX + w.config.xaxis.labels.offsetY + 30 - (w.config.xaxis.position === 'top' ? w.globals.xAxisHeight + w.config.xaxis.axisTicks.height - 2 : 0),\n              text: val,\n              textAnchor: 'middle',\n              foreColor: Array.isArray(this.xaxisForeColors) ? this.xaxisForeColors[realIndex] : this.xaxisForeColors,\n              fontSize: this.xaxisFontSize,\n              fontFamily: this.xaxisFontFamily,\n              fontWeight: w.config.xaxis.labels.style.fontWeight,\n              isPlainText: false,\n              cssClass: 'apexcharts-xaxis-label ' + w.config.xaxis.labels.style.cssClass\n            });\n            elXaxisTexts.add(elTick);\n            elTick.tspan(val);\n            var elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title');\n            elTooltipTitle.textContent = val;\n            elTick.node.appendChild(elTooltipTitle);\n            l = l + labelsDivider;\n          }\n        }\n\n        this.inversedYAxisTitleText(elXaxis);\n        this.inversedYAxisBorder(elXaxis);\n        return elXaxis;\n      }\n    }, {\n      key: \"inversedYAxisBorder\",\n      value: function inversedYAxisBorder(parent) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var axisBorder = w.config.xaxis.axisBorder;\n\n        if (axisBorder.show) {\n          var lineCorrection = 0;\n\n          if (w.config.chart.type === 'bar' && w.globals.isXNumeric) {\n            lineCorrection = lineCorrection - 15;\n          }\n\n          var elHorzLine = graphics.drawLine(w.globals.padHorizontal + lineCorrection + axisBorder.offsetX, this.xAxisoffX, w.globals.gridWidth, this.xAxisoffX, axisBorder.color, 0, axisBorder.height); // in horizontal bars, we append axisBorder to elGridBorders element to avoid z-index issues\n\n          if (this.elgrid && this.elgrid.elGridBorders) {\n            this.elgrid.elGridBorders.add(elHorzLine);\n          } else {\n            parent.add(elHorzLine);\n          }\n        }\n      }\n    }, {\n      key: \"inversedYAxisTitleText\",\n      value: function inversedYAxisTitleText(parent) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n\n        if (w.config.xaxis.title.text !== undefined) {\n          var elYaxisTitle = graphics.group({\n            class: 'apexcharts-xaxis-title apexcharts-yaxis-title-inversed'\n          });\n          var elYAxisTitleText = graphics.drawText({\n            x: w.globals.gridWidth / 2 + w.config.xaxis.title.offsetX,\n            y: this.xAxisoffX + parseFloat(this.xaxisFontSize) + parseFloat(w.config.xaxis.title.style.fontSize) + w.config.xaxis.title.offsetY + 20,\n            text: w.config.xaxis.title.text,\n            textAnchor: 'middle',\n            fontSize: w.config.xaxis.title.style.fontSize,\n            fontFamily: w.config.xaxis.title.style.fontFamily,\n            fontWeight: w.config.xaxis.title.style.fontWeight,\n            foreColor: w.config.xaxis.title.style.color,\n            cssClass: 'apexcharts-xaxis-title-text ' + w.config.xaxis.title.style.cssClass\n          });\n          elYaxisTitle.add(elYAxisTitleText);\n          parent.add(elYaxisTitle);\n        }\n      }\n    }, {\n      key: \"yAxisTitleRotate\",\n      value: function yAxisTitleRotate(realIndex, yAxisOpposite) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var yAxisLabelsCoord = {\n          width: 0,\n          height: 0\n        };\n        var yAxisTitleCoord = {\n          width: 0,\n          height: 0\n        };\n        var elYAxisLabelsWrap = w.globals.dom.baseEl.querySelector(\" .apexcharts-yaxis[rel='\".concat(realIndex, \"'] .apexcharts-yaxis-texts-g\"));\n\n        if (elYAxisLabelsWrap !== null) {\n          yAxisLabelsCoord = elYAxisLabelsWrap.getBoundingClientRect();\n        }\n\n        var yAxisTitle = w.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(realIndex, \"'] .apexcharts-yaxis-title text\"));\n\n        if (yAxisTitle !== null) {\n          yAxisTitleCoord = yAxisTitle.getBoundingClientRect();\n        }\n\n        if (yAxisTitle !== null) {\n          var x = this.xPaddingForYAxisTitle(realIndex, yAxisLabelsCoord, yAxisTitleCoord, yAxisOpposite);\n          yAxisTitle.setAttribute('x', x.xPos - (yAxisOpposite ? 10 : 0));\n        }\n\n        if (yAxisTitle !== null) {\n          var titleRotatingCenter = graphics.rotateAroundCenter(yAxisTitle);\n          yAxisTitle.setAttribute('transform', \"rotate(\".concat(yAxisOpposite ? w.config.yaxis[realIndex].title.rotate * -1 : w.config.yaxis[realIndex].title.rotate, \" \").concat(titleRotatingCenter.x, \" \").concat(titleRotatingCenter.y, \")\"));\n        }\n      }\n    }, {\n      key: \"xPaddingForYAxisTitle\",\n      value: function xPaddingForYAxisTitle(realIndex, yAxisLabelsCoord, yAxisTitleCoord, yAxisOpposite) {\n        var w = this.w;\n        var oppositeAxisCount = 0;\n        var x = 0;\n        var padd = 10;\n\n        if (w.config.yaxis[realIndex].title.text === undefined || realIndex < 0) {\n          return {\n            xPos: x,\n            padd: 0\n          };\n        }\n\n        if (yAxisOpposite) {\n          x = yAxisLabelsCoord.width + w.config.yaxis[realIndex].title.offsetX + yAxisTitleCoord.width / 2 + padd / 2;\n          oppositeAxisCount += 1;\n\n          if (oppositeAxisCount === 0) {\n            x = x - padd / 2;\n          }\n        } else {\n          x = yAxisLabelsCoord.width * -1 + w.config.yaxis[realIndex].title.offsetX + padd / 2 + yAxisTitleCoord.width / 2;\n\n          if (w.globals.isBarHorizontal) {\n            padd = 25;\n            x = yAxisLabelsCoord.width * -1 - w.config.yaxis[realIndex].title.offsetX - padd;\n          }\n        }\n\n        return {\n          xPos: x,\n          padd: padd\n        };\n      } // sets the x position of the y-axis by counting the labels width, title width and any offset\n\n    }, {\n      key: \"setYAxisXPosition\",\n      value: function setYAxisXPosition(yaxisLabelCoords, yTitleCoords) {\n        var w = this.w;\n        var xLeft = 0;\n        var xRight = 0;\n        var leftOffsetX = 18;\n        var rightOffsetX = 1;\n\n        if (w.config.yaxis.length > 1) {\n          this.multipleYs = true;\n        }\n\n        w.config.yaxis.map(function (yaxe, index) {\n          var shouldNotDrawAxis = w.globals.ignoreYAxisIndexes.indexOf(index) > -1 || !yaxe.show || yaxe.floating || yaxisLabelCoords[index].width === 0;\n          var axisWidth = yaxisLabelCoords[index].width + yTitleCoords[index].width;\n\n          if (!yaxe.opposite) {\n            xLeft = w.globals.translateX - leftOffsetX;\n\n            if (!shouldNotDrawAxis) {\n              leftOffsetX = leftOffsetX + axisWidth + 20;\n            }\n\n            w.globals.translateYAxisX[index] = xLeft + yaxe.labels.offsetX;\n          } else {\n            if (w.globals.isBarHorizontal) {\n              xRight = w.globals.gridWidth + w.globals.translateX - 1;\n              w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX;\n            } else {\n              xRight = w.globals.gridWidth + w.globals.translateX + rightOffsetX;\n\n              if (!shouldNotDrawAxis) {\n                rightOffsetX = rightOffsetX + axisWidth + 20;\n              }\n\n              w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX + 20;\n            }\n          }\n        });\n      }\n    }, {\n      key: \"setYAxisTextAlignments\",\n      value: function setYAxisTextAlignments() {\n        var w = this.w;\n        var yaxis = w.globals.dom.baseEl.getElementsByClassName(\"apexcharts-yaxis\");\n        yaxis = Utils$1.listToArray(yaxis);\n        yaxis.forEach(function (y, index) {\n          var yaxe = w.config.yaxis[index]; // proceed only if user has specified alignment\n\n          if (yaxe && !yaxe.floating && yaxe.labels.align !== undefined) {\n            var yAxisInner = w.globals.dom.baseEl.querySelector(\".apexcharts-yaxis[rel='\".concat(index, \"'] .apexcharts-yaxis-texts-g\"));\n            var yAxisTexts = w.globals.dom.baseEl.querySelectorAll(\".apexcharts-yaxis[rel='\".concat(index, \"'] .apexcharts-yaxis-label\"));\n            yAxisTexts = Utils$1.listToArray(yAxisTexts);\n            var rect = yAxisInner.getBoundingClientRect();\n\n            if (yaxe.labels.align === 'left') {\n              yAxisTexts.forEach(function (label, lI) {\n                label.setAttribute('text-anchor', 'start');\n              });\n\n              if (!yaxe.opposite) {\n                yAxisInner.setAttribute('transform', \"translate(-\".concat(rect.width, \", 0)\"));\n              }\n            } else if (yaxe.labels.align === 'center') {\n              yAxisTexts.forEach(function (label, lI) {\n                label.setAttribute('text-anchor', 'middle');\n              });\n              yAxisInner.setAttribute('transform', \"translate(\".concat(rect.width / 2 * (!yaxe.opposite ? -1 : 1), \", 0)\"));\n            } else if (yaxe.labels.align === 'right') {\n              yAxisTexts.forEach(function (label, lI) {\n                label.setAttribute('text-anchor', 'end');\n              });\n\n              if (yaxe.opposite) {\n                yAxisInner.setAttribute('transform', \"translate(\".concat(rect.width, \", 0)\"));\n              }\n            }\n          }\n        });\n      }\n    }]);\n\n    return YAxis;\n  }();\n\n  var Events = /*#__PURE__*/function () {\n    function Events(ctx) {\n      _classCallCheck(this, Events);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.documentEvent = Utils$1.bind(this.documentEvent, this);\n    }\n\n    _createClass(Events, [{\n      key: \"addEventListener\",\n      value: function addEventListener(name, handler) {\n        var w = this.w;\n\n        if (w.globals.events.hasOwnProperty(name)) {\n          w.globals.events[name].push(handler);\n        } else {\n          w.globals.events[name] = [handler];\n        }\n      }\n    }, {\n      key: \"removeEventListener\",\n      value: function removeEventListener(name, handler) {\n        var w = this.w;\n\n        if (!w.globals.events.hasOwnProperty(name)) {\n          return;\n        }\n\n        var index = w.globals.events[name].indexOf(handler);\n\n        if (index !== -1) {\n          w.globals.events[name].splice(index, 1);\n        }\n      }\n    }, {\n      key: \"fireEvent\",\n      value: function fireEvent(name, args) {\n        var w = this.w;\n\n        if (!w.globals.events.hasOwnProperty(name)) {\n          return;\n        }\n\n        if (!args || !args.length) {\n          args = [];\n        }\n\n        var evs = w.globals.events[name];\n        var l = evs.length;\n\n        for (var i = 0; i < l; i++) {\n          evs[i].apply(null, args);\n        }\n      }\n    }, {\n      key: \"setupEventHandlers\",\n      value: function setupEventHandlers() {\n        var _this = this;\n\n        var w = this.w;\n        var me = this.ctx;\n        var clickableArea = w.globals.dom.baseEl.querySelector(w.globals.chartClass);\n        this.ctx.eventList.forEach(function (event) {\n          clickableArea.addEventListener(event, function (e) {\n            var opts = Object.assign({}, w, {\n              seriesIndex: w.globals.capturedSeriesIndex,\n              dataPointIndex: w.globals.capturedDataPointIndex\n            });\n\n            if (e.type === 'mousemove' || e.type === 'touchmove') {\n              if (typeof w.config.chart.events.mouseMove === 'function') {\n                w.config.chart.events.mouseMove(e, me, opts);\n              }\n            } else if (e.type === 'mouseleave' || e.type === 'touchleave') {\n              if (typeof w.config.chart.events.mouseLeave === 'function') {\n                w.config.chart.events.mouseLeave(e, me, opts);\n              }\n            } else if (e.type === 'mouseup' && e.which === 1 || e.type === 'touchend') {\n              if (typeof w.config.chart.events.click === 'function') {\n                w.config.chart.events.click(e, me, opts);\n              }\n\n              me.ctx.events.fireEvent('click', [e, me, opts]);\n            }\n          }, {\n            capture: false,\n            passive: true\n          });\n        });\n        this.ctx.eventList.forEach(function (event) {\n          w.globals.dom.baseEl.addEventListener(event, _this.documentEvent, {\n            passive: true\n          });\n        });\n        this.ctx.core.setupBrushHandler();\n      }\n    }, {\n      key: \"documentEvent\",\n      value: function documentEvent(e) {\n        var w = this.w;\n        var target = e.target.className;\n\n        if (e.type === 'click') {\n          var elMenu = w.globals.dom.baseEl.querySelector('.apexcharts-menu');\n\n          if (elMenu && elMenu.classList.contains('apexcharts-menu-open') && target !== 'apexcharts-menu-icon') {\n            elMenu.classList.remove('apexcharts-menu-open');\n          }\n        }\n\n        w.globals.clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;\n        w.globals.clientY = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY;\n      }\n    }]);\n\n    return Events;\n  }();\n\n  var Localization = /*#__PURE__*/function () {\n    function Localization(ctx) {\n      _classCallCheck(this, Localization);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(Localization, [{\n      key: \"setCurrentLocaleValues\",\n      value: function setCurrentLocaleValues(localeName) {\n        var locales = this.w.config.chart.locales; // check if user has specified locales in global Apex variable\n        // if yes - then extend those with local chart's locale\n\n        if (window.Apex.chart && window.Apex.chart.locales && window.Apex.chart.locales.length > 0) {\n          locales = this.w.config.chart.locales.concat(window.Apex.chart.locales);\n        } // find the locale from the array of locales which user has set (either by chart.defaultLocale or by calling setLocale() method.)\n\n\n        var selectedLocale = locales.filter(function (c) {\n          return c.name === localeName;\n        })[0];\n\n        if (selectedLocale) {\n          // create a complete locale object by extending defaults so you don't get undefined errors.\n          var ret = Utils$1.extend(en, selectedLocale); // store these locale options in global var for ease access\n\n          this.w.globals.locale = ret.options;\n        } else {\n          throw new Error('Wrong locale name provided. Please make sure you set the correct locale name in options');\n        }\n      }\n    }]);\n\n    return Localization;\n  }();\n\n  var Axes = /*#__PURE__*/function () {\n    function Axes(ctx) {\n      _classCallCheck(this, Axes);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(Axes, [{\n      key: \"drawAxis\",\n      value: function drawAxis(type, elgrid) {\n        var _this = this;\n\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n        var xAxis = new XAxis(this.ctx, elgrid);\n        var yAxis = new YAxis(this.ctx, elgrid);\n\n        if (gl.axisCharts && type !== 'radar') {\n          var elXaxis, elYaxis;\n\n          if (gl.isBarHorizontal) {\n            elYaxis = yAxis.drawYaxisInversed(0);\n            elXaxis = xAxis.drawXaxisInversed(0);\n            gl.dom.elGraphical.add(elXaxis);\n            gl.dom.elGraphical.add(elYaxis);\n          } else {\n            elXaxis = xAxis.drawXaxis();\n            gl.dom.elGraphical.add(elXaxis);\n            cnf.yaxis.map(function (yaxe, index) {\n              if (gl.ignoreYAxisIndexes.indexOf(index) === -1) {\n                elYaxis = yAxis.drawYaxis(index);\n                gl.dom.Paper.add(elYaxis);\n\n                if (_this.w.config.grid.position === 'back') {\n                  var inner = gl.dom.Paper.children()[1];\n                  inner.remove();\n                  gl.dom.Paper.add(inner);\n                }\n              }\n            });\n          }\n        }\n      }\n    }]);\n\n    return Axes;\n  }();\n\n  var Crosshairs = /*#__PURE__*/function () {\n    function Crosshairs(ctx) {\n      _classCallCheck(this, Crosshairs);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(Crosshairs, [{\n      key: \"drawXCrosshairs\",\n      value: function drawXCrosshairs() {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var filters = new Filters(this.ctx);\n        var crosshairGradient = w.config.xaxis.crosshairs.fill.gradient;\n        var crosshairShadow = w.config.xaxis.crosshairs.dropShadow;\n        var fillType = w.config.xaxis.crosshairs.fill.type;\n        var gradientFrom = crosshairGradient.colorFrom;\n        var gradientTo = crosshairGradient.colorTo;\n        var opacityFrom = crosshairGradient.opacityFrom;\n        var opacityTo = crosshairGradient.opacityTo;\n        var stops = crosshairGradient.stops;\n        var shadow = 'none';\n        var dropShadow = crosshairShadow.enabled;\n        var shadowLeft = crosshairShadow.left;\n        var shadowTop = crosshairShadow.top;\n        var shadowBlur = crosshairShadow.blur;\n        var shadowColor = crosshairShadow.color;\n        var shadowOpacity = crosshairShadow.opacity;\n        var xcrosshairsFill = w.config.xaxis.crosshairs.fill.color;\n\n        if (w.config.xaxis.crosshairs.show) {\n          if (fillType === 'gradient') {\n            xcrosshairsFill = graphics.drawGradient('vertical', gradientFrom, gradientTo, opacityFrom, opacityTo, null, stops, null);\n          }\n\n          var xcrosshairs = graphics.drawRect();\n\n          if (w.config.xaxis.crosshairs.width === 1) {\n            // to prevent drawing 2 lines, convert rect to line\n            xcrosshairs = graphics.drawLine();\n          }\n\n          var gridHeight = w.globals.gridHeight;\n\n          if (!Utils$1.isNumber(gridHeight) || gridHeight < 0) {\n            gridHeight = 0;\n          }\n\n          var crosshairsWidth = w.config.xaxis.crosshairs.width;\n\n          if (!Utils$1.isNumber(crosshairsWidth) || crosshairsWidth < 0) {\n            crosshairsWidth = 0;\n          }\n\n          xcrosshairs.attr({\n            class: 'apexcharts-xcrosshairs',\n            x: 0,\n            y: 0,\n            y2: gridHeight,\n            width: crosshairsWidth,\n            height: gridHeight,\n            fill: xcrosshairsFill,\n            filter: shadow,\n            'fill-opacity': w.config.xaxis.crosshairs.opacity,\n            stroke: w.config.xaxis.crosshairs.stroke.color,\n            'stroke-width': w.config.xaxis.crosshairs.stroke.width,\n            'stroke-dasharray': w.config.xaxis.crosshairs.stroke.dashArray\n          });\n\n          if (dropShadow) {\n            xcrosshairs = filters.dropShadow(xcrosshairs, {\n              left: shadowLeft,\n              top: shadowTop,\n              blur: shadowBlur,\n              color: shadowColor,\n              opacity: shadowOpacity\n            });\n          }\n\n          w.globals.dom.elGraphical.add(xcrosshairs);\n        }\n      }\n    }, {\n      key: \"drawYCrosshairs\",\n      value: function drawYCrosshairs() {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var crosshair = w.config.yaxis[0].crosshairs;\n        var offX = w.globals.barPadForNumericAxis;\n\n        if (w.config.yaxis[0].crosshairs.show) {\n          var ycrosshairs = graphics.drawLine(-offX, 0, w.globals.gridWidth + offX, 0, crosshair.stroke.color, crosshair.stroke.dashArray, crosshair.stroke.width);\n          ycrosshairs.attr({\n            class: 'apexcharts-ycrosshairs'\n          });\n          w.globals.dom.elGraphical.add(ycrosshairs);\n        } // draw an invisible crosshair to help in positioning the yaxis tooltip\n\n\n        var ycrosshairsHidden = graphics.drawLine(-offX, 0, w.globals.gridWidth + offX, 0, crosshair.stroke.color, 0, 0);\n        ycrosshairsHidden.attr({\n          class: 'apexcharts-ycrosshairs-hidden'\n        });\n        w.globals.dom.elGraphical.add(ycrosshairsHidden);\n      }\n    }]);\n\n    return Crosshairs;\n  }();\n\n  /**\n   * ApexCharts Responsive Class to override options for different screen sizes.\n   *\n   * @module Responsive\n   **/\n\n  var Responsive = /*#__PURE__*/function () {\n    function Responsive(ctx) {\n      _classCallCheck(this, Responsive);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    } // the opts parameter if not null has to be set overriding everything\n    // as the opts is set by user externally\n\n\n    _createClass(Responsive, [{\n      key: \"checkResponsiveConfig\",\n      value: function checkResponsiveConfig(opts) {\n        var _this = this;\n\n        var w = this.w;\n        var cnf = w.config; // check if responsive config exists\n\n        if (cnf.responsive.length === 0) return;\n        var res = cnf.responsive.slice();\n        res.sort(function (a, b) {\n          return a.breakpoint > b.breakpoint ? 1 : b.breakpoint > a.breakpoint ? -1 : 0;\n        }).reverse();\n        var config = new Config({});\n\n        var iterateResponsiveOptions = function iterateResponsiveOptions() {\n          var newOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n          var largestBreakpoint = res[0].breakpoint;\n          var width = window.innerWidth > 0 ? window.innerWidth : screen.width;\n\n          if (width > largestBreakpoint) {\n            var options = CoreUtils.extendArrayProps(config, w.globals.initialConfig, w);\n            newOptions = Utils$1.extend(options, newOptions);\n            newOptions = Utils$1.extend(w.config, newOptions);\n\n            _this.overrideResponsiveOptions(newOptions);\n          } else {\n            for (var i = 0; i < res.length; i++) {\n              if (width < res[i].breakpoint) {\n                newOptions = CoreUtils.extendArrayProps(config, res[i].options, w);\n                newOptions = Utils$1.extend(w.config, newOptions);\n\n                _this.overrideResponsiveOptions(newOptions);\n              }\n            }\n          }\n        };\n\n        if (opts) {\n          var options = CoreUtils.extendArrayProps(config, opts, w);\n          options = Utils$1.extend(w.config, options);\n          options = Utils$1.extend(options, opts);\n          iterateResponsiveOptions(options);\n        } else {\n          iterateResponsiveOptions({});\n        }\n      }\n    }, {\n      key: \"overrideResponsiveOptions\",\n      value: function overrideResponsiveOptions(newOptions) {\n        var newConfig = new Config(newOptions).init({\n          responsiveOverride: true\n        });\n        this.w.config = newConfig;\n      }\n    }]);\n\n    return Responsive;\n  }();\n\n  /**\n   * ApexCharts Theme Class for setting the colors and palettes.\n   *\n   * @module Theme\n   **/\n\n  var Theme = /*#__PURE__*/function () {\n    function Theme(ctx) {\n      _classCallCheck(this, Theme);\n\n      this.ctx = ctx;\n      this.colors = [];\n      this.w = ctx.w;\n      var w = this.w;\n      this.isColorFn = false;\n      this.isHeatmapDistributed = w.config.chart.type === 'treemap' && w.config.plotOptions.treemap.distributed || w.config.chart.type === 'heatmap' && w.config.plotOptions.heatmap.distributed;\n      this.isBarDistributed = w.config.plotOptions.bar.distributed && (w.config.chart.type === 'bar' || w.config.chart.type === 'rangeBar');\n    }\n\n    _createClass(Theme, [{\n      key: \"init\",\n      value: function init() {\n        this.setDefaultColors();\n      }\n    }, {\n      key: \"setDefaultColors\",\n      value: function setDefaultColors() {\n        var _this = this;\n\n        var w = this.w;\n        var utils = new Utils$1();\n        w.globals.dom.elWrap.classList.add(\"apexcharts-theme-\".concat(w.config.theme.mode));\n\n        if (w.config.colors === undefined) {\n          w.globals.colors = this.predefined();\n        } else {\n          w.globals.colors = w.config.colors; // if user provided a function in colors, we need to eval here\n\n          if (Array.isArray(w.config.colors) && w.config.colors.length > 0 && typeof w.config.colors[0] === 'function') {\n            w.globals.colors = w.config.series.map(function (s, i) {\n              var c = w.config.colors[i];\n              if (!c) c = w.config.colors[0];\n\n              if (typeof c === 'function') {\n                _this.isColorFn = true;\n                return c({\n                  value: w.globals.axisCharts ? w.globals.series[i][0] ? w.globals.series[i][0] : 0 : w.globals.series[i],\n                  seriesIndex: i,\n                  dataPointIndex: i,\n                  w: w\n                });\n              }\n\n              return c;\n            });\n          }\n        } // user defined colors in series array\n\n\n        w.globals.seriesColors.map(function (c, i) {\n          if (c) {\n            w.globals.colors[i] = c;\n          }\n        });\n\n        if (w.config.theme.monochrome.enabled) {\n          var monoArr = [];\n          var glsCnt = w.globals.series.length;\n\n          if (this.isBarDistributed || this.isHeatmapDistributed) {\n            glsCnt = w.globals.series[0].length * w.globals.series.length;\n          }\n\n          var mainColor = w.config.theme.monochrome.color;\n          var part = 1 / (glsCnt / w.config.theme.monochrome.shadeIntensity);\n          var shade = w.config.theme.monochrome.shadeTo;\n          var percent = 0;\n\n          for (var gsl = 0; gsl < glsCnt; gsl++) {\n            var newColor = void 0;\n\n            if (shade === 'dark') {\n              newColor = utils.shadeColor(percent * -1, mainColor);\n              percent = percent + part;\n            } else {\n              newColor = utils.shadeColor(percent, mainColor);\n              percent = percent + part;\n            }\n\n            monoArr.push(newColor);\n          }\n\n          w.globals.colors = monoArr.slice();\n        }\n\n        var defaultColors = w.globals.colors.slice(); // if user specified fewer colors than no. of series, push the same colors again\n\n        this.pushExtraColors(w.globals.colors);\n        var colorTypes = ['fill', 'stroke'];\n        colorTypes.forEach(function (c) {\n          if (w.config[c].colors === undefined) {\n            w.globals[c].colors = _this.isColorFn ? w.config.colors : defaultColors;\n          } else {\n            w.globals[c].colors = w.config[c].colors.slice();\n          }\n\n          _this.pushExtraColors(w.globals[c].colors);\n        });\n\n        if (w.config.dataLabels.style.colors === undefined) {\n          w.globals.dataLabels.style.colors = defaultColors;\n        } else {\n          w.globals.dataLabels.style.colors = w.config.dataLabels.style.colors.slice();\n        }\n\n        this.pushExtraColors(w.globals.dataLabels.style.colors, 50);\n\n        if (w.config.plotOptions.radar.polygons.fill.colors === undefined) {\n          w.globals.radarPolygons.fill.colors = [w.config.theme.mode === 'dark' ? '#424242' : 'none'];\n        } else {\n          w.globals.radarPolygons.fill.colors = w.config.plotOptions.radar.polygons.fill.colors.slice();\n        }\n\n        this.pushExtraColors(w.globals.radarPolygons.fill.colors, 20); // The point colors\n\n        if (w.config.markers.colors === undefined) {\n          w.globals.markers.colors = defaultColors;\n        } else {\n          w.globals.markers.colors = w.config.markers.colors.slice();\n        }\n\n        this.pushExtraColors(w.globals.markers.colors);\n      } // When the number of colors provided is less than the number of series, this method\n      // will push same colors to the list\n      // params:\n      // distributed is only valid for distributed column/bar charts\n\n    }, {\n      key: \"pushExtraColors\",\n      value: function pushExtraColors(colorSeries, length) {\n        var distributed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n        var w = this.w;\n        var len = length || w.globals.series.length;\n\n        if (distributed === null) {\n          distributed = this.isBarDistributed || this.isHeatmapDistributed || w.config.chart.type === 'heatmap' && w.config.plotOptions.heatmap.colorScale.inverse;\n        }\n\n        if (distributed && w.globals.series.length) {\n          len = w.globals.series[w.globals.maxValsInArrayIndex].length * w.globals.series.length;\n        }\n\n        if (colorSeries.length < len) {\n          var diff = len - colorSeries.length;\n\n          for (var i = 0; i < diff; i++) {\n            colorSeries.push(colorSeries[i]);\n          }\n        }\n      }\n    }, {\n      key: \"updateThemeOptions\",\n      value: function updateThemeOptions(options) {\n        options.chart = options.chart || {};\n        options.tooltip = options.tooltip || {};\n        var mode = options.theme.mode || 'light';\n        var palette = options.theme.palette ? options.theme.palette : mode === 'dark' ? 'palette4' : 'palette1';\n        var foreColor = options.chart.foreColor ? options.chart.foreColor : mode === 'dark' ? '#f6f7f8' : '#373d3f';\n        options.tooltip.theme = mode;\n        options.chart.foreColor = foreColor;\n        options.theme.palette = palette;\n        return options;\n      }\n    }, {\n      key: \"predefined\",\n      value: function predefined() {\n        var palette = this.w.config.theme.palette; // D6E3F8, FCEFEF, DCE0D9, A5978B, EDDDD4, D6E3F8, FEF5EF\n\n        switch (palette) {\n          case 'palette1':\n            this.colors = ['#008FFB', '#00E396', '#FEB019', '#FF4560', '#775DD0'];\n            break;\n\n          case 'palette2':\n            this.colors = ['#3f51b5', '#03a9f4', '#4caf50', '#f9ce1d', '#FF9800'];\n            break;\n\n          case 'palette3':\n            this.colors = ['#33b2df', '#546E7A', '#d4526e', '#13d8aa', '#A5978B'];\n            break;\n\n          case 'palette4':\n            this.colors = ['#4ecdc4', '#c7f464', '#81D4FA', '#fd6a6a', '#546E7A'];\n            break;\n\n          case 'palette5':\n            this.colors = ['#2b908f', '#f9a3a4', '#90ee7e', '#fa4443', '#69d2e7'];\n            break;\n\n          case 'palette6':\n            this.colors = ['#449DD1', '#F86624', '#EA3546', '#662E9B', '#C5D86D'];\n            break;\n\n          case 'palette7':\n            this.colors = ['#D7263D', '#1B998B', '#2E294E', '#F46036', '#E2C044'];\n            break;\n\n          case 'palette8':\n            this.colors = ['#662E9B', '#F86624', '#F9C80E', '#EA3546', '#43BCCD'];\n            break;\n\n          case 'palette9':\n            this.colors = ['#5C4742', '#A5978B', '#8D5B4C', '#5A2A27', '#C4BBAF'];\n            break;\n\n          case 'palette10':\n            this.colors = ['#A300D6', '#7D02EB', '#5653FE', '#2983FF', '#00B1F2'];\n            break;\n\n          default:\n            this.colors = ['#008FFB', '#00E396', '#FEB019', '#FF4560', '#775DD0'];\n            break;\n        }\n\n        return this.colors;\n      }\n    }]);\n\n    return Theme;\n  }();\n\n  var TitleSubtitle = /*#__PURE__*/function () {\n    function TitleSubtitle(ctx) {\n      _classCallCheck(this, TitleSubtitle);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(TitleSubtitle, [{\n      key: \"draw\",\n      value: function draw() {\n        this.drawTitleSubtitle('title');\n        this.drawTitleSubtitle('subtitle');\n      }\n    }, {\n      key: \"drawTitleSubtitle\",\n      value: function drawTitleSubtitle(type) {\n        var w = this.w;\n        var tsConfig = type === 'title' ? w.config.title : w.config.subtitle;\n        var x = w.globals.svgWidth / 2;\n        var y = tsConfig.offsetY;\n        var textAnchor = 'middle';\n\n        if (tsConfig.align === 'left') {\n          x = 10;\n          textAnchor = 'start';\n        } else if (tsConfig.align === 'right') {\n          x = w.globals.svgWidth - 10;\n          textAnchor = 'end';\n        }\n\n        x = x + tsConfig.offsetX;\n        y = y + parseInt(tsConfig.style.fontSize, 10) + tsConfig.margin / 2;\n\n        if (tsConfig.text !== undefined) {\n          var graphics = new Graphics(this.ctx);\n          var titleText = graphics.drawText({\n            x: x,\n            y: y,\n            text: tsConfig.text,\n            textAnchor: textAnchor,\n            fontSize: tsConfig.style.fontSize,\n            fontFamily: tsConfig.style.fontFamily,\n            fontWeight: tsConfig.style.fontWeight,\n            foreColor: tsConfig.style.color,\n            opacity: 1\n          });\n          titleText.node.setAttribute('class', \"apexcharts-\".concat(type, \"-text\"));\n          w.globals.dom.Paper.add(titleText);\n        }\n      }\n    }]);\n\n    return TitleSubtitle;\n  }();\n\n  var Helpers$3 = /*#__PURE__*/function () {\n    function Helpers(dCtx) {\n      _classCallCheck(this, Helpers);\n\n      this.w = dCtx.w;\n      this.dCtx = dCtx;\n    }\n    /**\n     * Get Chart Title/Subtitle Dimensions\n     * @memberof Dimensions\n     * @return {{width, height}}\n     **/\n\n\n    _createClass(Helpers, [{\n      key: \"getTitleSubtitleCoords\",\n      value: function getTitleSubtitleCoords(type) {\n        var w = this.w;\n        var width = 0;\n        var height = 0;\n        var floating = type === 'title' ? w.config.title.floating : w.config.subtitle.floating;\n        var el = w.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(type, \"-text\"));\n\n        if (el !== null && !floating) {\n          var coord = el.getBoundingClientRect();\n          width = coord.width;\n          height = w.globals.axisCharts ? coord.height + 5 : coord.height;\n        }\n\n        return {\n          width: width,\n          height: height\n        };\n      }\n    }, {\n      key: \"getLegendsRect\",\n      value: function getLegendsRect() {\n        var w = this.w;\n        var elLegendWrap = w.globals.dom.baseEl.querySelector('.apexcharts-legend');\n\n        if (!w.config.legend.height && (w.config.legend.position === 'top' || w.config.legend.position === 'bottom')) {\n          // avoid legend to take up all the space\n          elLegendWrap.style.maxHeight = w.globals.svgHeight / 2 + 'px';\n        }\n\n        var lgRect = Object.assign({}, Utils$1.getBoundingClientRect(elLegendWrap));\n\n        if (elLegendWrap !== null && !w.config.legend.floating && w.config.legend.show) {\n          this.dCtx.lgRect = {\n            x: lgRect.x,\n            y: lgRect.y,\n            height: lgRect.height,\n            width: lgRect.height === 0 ? 0 : lgRect.width\n          };\n        } else {\n          this.dCtx.lgRect = {\n            x: 0,\n            y: 0,\n            height: 0,\n            width: 0\n          };\n        } // if legend takes up all of the chart space, we need to restrict it.\n\n\n        if (w.config.legend.position === 'left' || w.config.legend.position === 'right') {\n          if (this.dCtx.lgRect.width * 1.5 > w.globals.svgWidth) {\n            this.dCtx.lgRect.width = w.globals.svgWidth / 1.5;\n          }\n        }\n\n        return this.dCtx.lgRect;\n      }\n    }, {\n      key: \"getLargestStringFromMultiArr\",\n      value: function getLargestStringFromMultiArr(val, arr) {\n        var w = this.w;\n        var valArr = val;\n\n        if (w.globals.isMultiLineX) {\n          // if the xaxis labels has multiline texts (array)\n          var maxArrs = arr.map(function (xl, idx) {\n            return Array.isArray(xl) ? xl.length : 1;\n          });\n          var maxArrLen = Math.max.apply(Math, _toConsumableArray(maxArrs));\n          var maxArrIndex = maxArrs.indexOf(maxArrLen);\n          valArr = arr[maxArrIndex];\n        }\n\n        return valArr;\n      }\n    }]);\n\n    return Helpers;\n  }();\n\n  var DimXAxis = /*#__PURE__*/function () {\n    function DimXAxis(dCtx) {\n      _classCallCheck(this, DimXAxis);\n\n      this.w = dCtx.w;\n      this.dCtx = dCtx;\n    }\n    /**\n     * Get X Axis Dimensions\n     * @memberof Dimensions\n     * @return {{width, height}}\n     **/\n\n\n    _createClass(DimXAxis, [{\n      key: \"getxAxisLabelsCoords\",\n      value: function getxAxisLabelsCoords() {\n        var w = this.w;\n        var xaxisLabels = w.globals.labels.slice();\n\n        if (w.config.xaxis.convertedCatToNumeric && xaxisLabels.length === 0) {\n          xaxisLabels = w.globals.categoryLabels;\n        }\n\n        var rect;\n\n        if (w.globals.timescaleLabels.length > 0) {\n          var coords = this.getxAxisTimeScaleLabelsCoords();\n          rect = {\n            width: coords.width,\n            height: coords.height\n          };\n          w.globals.rotateXLabels = false;\n        } else {\n          this.dCtx.lgWidthForSideLegends = (w.config.legend.position === 'left' || w.config.legend.position === 'right') && !w.config.legend.floating ? this.dCtx.lgRect.width : 0; // get the longest string from the labels array and also apply label formatter\n\n          var xlbFormatter = w.globals.xLabelFormatter; // prevent changing xaxisLabels to avoid issues in multi-yaxes - fix #522\n\n          var val = Utils$1.getLargestStringFromArr(xaxisLabels);\n          var valArr = this.dCtx.dimHelpers.getLargestStringFromMultiArr(val, xaxisLabels); // the labels gets changed for bar charts\n\n          if (w.globals.isBarHorizontal) {\n            val = w.globals.yAxisScale[0].result.reduce(function (a, b) {\n              return a.length > b.length ? a : b;\n            }, 0);\n            valArr = val;\n          }\n\n          var xFormat = new Formatters(this.dCtx.ctx);\n          var timestamp = val;\n          val = xFormat.xLabelFormat(xlbFormatter, val, timestamp, {\n            i: undefined,\n            dateFormatter: new DateTime(this.dCtx.ctx).formatDate,\n            w: w\n          });\n          valArr = xFormat.xLabelFormat(xlbFormatter, valArr, timestamp, {\n            i: undefined,\n            dateFormatter: new DateTime(this.dCtx.ctx).formatDate,\n            w: w\n          });\n\n          if (w.config.xaxis.convertedCatToNumeric && typeof val === 'undefined' || String(val).trim() === '') {\n            val = '1';\n            valArr = val;\n          }\n\n          var graphics = new Graphics(this.dCtx.ctx);\n          var xLabelrect = graphics.getTextRects(val, w.config.xaxis.labels.style.fontSize);\n          var xArrLabelrect = xLabelrect;\n\n          if (val !== valArr) {\n            xArrLabelrect = graphics.getTextRects(valArr, w.config.xaxis.labels.style.fontSize);\n          }\n\n          rect = {\n            width: xLabelrect.width >= xArrLabelrect.width ? xLabelrect.width : xArrLabelrect.width,\n            height: xLabelrect.height >= xArrLabelrect.height ? xLabelrect.height : xArrLabelrect.height\n          };\n\n          if (rect.width * xaxisLabels.length > w.globals.svgWidth - this.dCtx.lgWidthForSideLegends - this.dCtx.yAxisWidth - this.dCtx.gridPad.left - this.dCtx.gridPad.right && w.config.xaxis.labels.rotate !== 0 || w.config.xaxis.labels.rotateAlways) {\n            if (!w.globals.isBarHorizontal) {\n              w.globals.rotateXLabels = true;\n\n              var getRotatedTextRects = function getRotatedTextRects(text) {\n                return graphics.getTextRects(text, w.config.xaxis.labels.style.fontSize, w.config.xaxis.labels.style.fontFamily, \"rotate(\".concat(w.config.xaxis.labels.rotate, \" 0 0)\"), false);\n              };\n\n              xLabelrect = getRotatedTextRects(val);\n\n              if (val !== valArr) {\n                xArrLabelrect = getRotatedTextRects(valArr);\n              }\n\n              rect.height = (xLabelrect.height > xArrLabelrect.height ? xLabelrect.height : xArrLabelrect.height) / 1.5;\n              rect.width = xLabelrect.width > xArrLabelrect.width ? xLabelrect.width : xArrLabelrect.width;\n            }\n          } else {\n            w.globals.rotateXLabels = false;\n          }\n        }\n\n        if (!w.config.xaxis.labels.show) {\n          rect = {\n            width: 0,\n            height: 0\n          };\n        }\n\n        return {\n          width: rect.width,\n          height: rect.height\n        };\n      }\n      /**\n       * Get X Axis Label Group height\n       * @memberof Dimensions\n       * @return {{width, height}}\n       */\n\n    }, {\n      key: \"getxAxisGroupLabelsCoords\",\n      value: function getxAxisGroupLabelsCoords() {\n        var _w$config$xaxis$group;\n\n        var w = this.w;\n\n        if (!w.globals.hasGroups) {\n          return {\n            width: 0,\n            height: 0\n          };\n        }\n\n        var fontSize = ((_w$config$xaxis$group = w.config.xaxis.group.style) === null || _w$config$xaxis$group === void 0 ? void 0 : _w$config$xaxis$group.fontSize) || w.config.xaxis.labels.style.fontSize;\n        var xaxisLabels = w.globals.groups.map(function (g) {\n          return g.title;\n        });\n        var rect; // prevent changing xaxisLabels to avoid issues in multi-yaxes - fix #522\n\n        var val = Utils$1.getLargestStringFromArr(xaxisLabels);\n        var valArr = this.dCtx.dimHelpers.getLargestStringFromMultiArr(val, xaxisLabels);\n        var graphics = new Graphics(this.dCtx.ctx);\n        var xLabelrect = graphics.getTextRects(val, fontSize);\n        var xArrLabelrect = xLabelrect;\n\n        if (val !== valArr) {\n          xArrLabelrect = graphics.getTextRects(valArr, fontSize);\n        }\n\n        rect = {\n          width: xLabelrect.width >= xArrLabelrect.width ? xLabelrect.width : xArrLabelrect.width,\n          height: xLabelrect.height >= xArrLabelrect.height ? xLabelrect.height : xArrLabelrect.height\n        };\n\n        if (!w.config.xaxis.labels.show) {\n          rect = {\n            width: 0,\n            height: 0\n          };\n        }\n\n        return {\n          width: rect.width,\n          height: rect.height\n        };\n      }\n      /**\n       * Get X Axis Title Dimensions\n       * @memberof Dimensions\n       * @return {{width, height}}\n       **/\n\n    }, {\n      key: \"getxAxisTitleCoords\",\n      value: function getxAxisTitleCoords() {\n        var w = this.w;\n        var width = 0;\n        var height = 0;\n\n        if (w.config.xaxis.title.text !== undefined) {\n          var graphics = new Graphics(this.dCtx.ctx);\n          var rect = graphics.getTextRects(w.config.xaxis.title.text, w.config.xaxis.title.style.fontSize);\n          width = rect.width;\n          height = rect.height;\n        }\n\n        return {\n          width: width,\n          height: height\n        };\n      }\n    }, {\n      key: \"getxAxisTimeScaleLabelsCoords\",\n      value: function getxAxisTimeScaleLabelsCoords() {\n        var w = this.w;\n        var rect;\n        this.dCtx.timescaleLabels = w.globals.timescaleLabels.slice();\n        var labels = this.dCtx.timescaleLabels.map(function (label) {\n          return label.value;\n        }); //  get the longest string from the labels array and also apply label formatter to it\n\n        var val = labels.reduce(function (a, b) {\n          // if undefined, maybe user didn't pass the datetime(x) values\n          if (typeof a === 'undefined') {\n            console.error('You have possibly supplied invalid Date format. Please supply a valid JavaScript Date');\n            return 0;\n          } else {\n            return a.length > b.length ? a : b;\n          }\n        }, 0);\n        var graphics = new Graphics(this.dCtx.ctx);\n        rect = graphics.getTextRects(val, w.config.xaxis.labels.style.fontSize);\n        var totalWidthRotated = rect.width * 1.05 * labels.length;\n\n        if (totalWidthRotated > w.globals.gridWidth && w.config.xaxis.labels.rotate !== 0) {\n          w.globals.overlappingXLabels = true;\n        }\n\n        return rect;\n      } // In certain cases, the last labels gets cropped in xaxis.\n      // Hence, we add some additional padding based on the label length to avoid the last label being cropped or we don't draw it at all\n\n    }, {\n      key: \"additionalPaddingXLabels\",\n      value: function additionalPaddingXLabels(xaxisLabelCoords) {\n        var _this = this;\n\n        var w = this.w;\n        var gl = w.globals;\n        var cnf = w.config;\n        var xtype = cnf.xaxis.type;\n        var lbWidth = xaxisLabelCoords.width;\n        gl.skipLastTimelinelabel = false;\n        gl.skipFirstTimelinelabel = false;\n        var isBarOpposite = w.config.yaxis[0].opposite && w.globals.isBarHorizontal;\n\n        var isCollapsed = function isCollapsed(i) {\n          return gl.collapsedSeriesIndices.indexOf(i) !== -1;\n        };\n\n        var rightPad = function rightPad(yaxe) {\n          if (_this.dCtx.timescaleLabels && _this.dCtx.timescaleLabels.length) {\n            // for timeline labels, we take the last label and check if it exceeds gridWidth\n            var firstimescaleLabel = _this.dCtx.timescaleLabels[0];\n            var lastTimescaleLabel = _this.dCtx.timescaleLabels[_this.dCtx.timescaleLabels.length - 1];\n            var lastLabelPosition = lastTimescaleLabel.position + lbWidth / 1.75 - _this.dCtx.yAxisWidthRight;\n            var firstLabelPosition = firstimescaleLabel.position - lbWidth / 1.75 + _this.dCtx.yAxisWidthLeft;\n            var lgRightRectWidth = w.config.legend.position === 'right' && _this.dCtx.lgRect.width > 0 ? _this.dCtx.lgRect.width : 0;\n\n            if (lastLabelPosition > gl.svgWidth - gl.translateX - lgRightRectWidth) {\n              gl.skipLastTimelinelabel = true;\n            }\n\n            if (firstLabelPosition < -((!yaxe.show || yaxe.floating) && (cnf.chart.type === 'bar' || cnf.chart.type === 'candlestick' || cnf.chart.type === 'rangeBar' || cnf.chart.type === 'boxPlot') ? lbWidth / 1.75 : 10)) {\n              gl.skipFirstTimelinelabel = true;\n            }\n          } else if (xtype === 'datetime') {\n            // If user has enabled DateTime, but uses own's formatter\n            if (_this.dCtx.gridPad.right < lbWidth && !gl.rotateXLabels) {\n              gl.skipLastTimelinelabel = true;\n            }\n          } else if (xtype !== 'datetime') {\n            if (_this.dCtx.gridPad.right < lbWidth / 2 - _this.dCtx.yAxisWidthRight && !gl.rotateXLabels && !w.config.xaxis.labels.trim && (w.config.xaxis.tickPlacement !== 'between' || w.globals.isBarHorizontal)) {\n              _this.dCtx.xPadRight = lbWidth / 2 + 1;\n            }\n          }\n        };\n\n        var padYAxe = function padYAxe(yaxe, i) {\n          if (isCollapsed(i)) return; // the code below causes issue apexcharts.js#1989\n          // after testing with other use-cases, this has no actual value, hence commented\n          // if (xtype !== 'datetime') {\n          //   if (\n          //     this.dCtx.gridPad.left < lbWidth / 2 - this.dCtx.yAxisWidthLeft &&\n          //     !gl.rotateXLabels &&\n          //     !cnf.xaxis.labels.trim\n          //   ) {\n          //     this.dCtx.xPadLeft = lbWidth / 2 + 1\n          //   }\n          // }\n\n          rightPad(yaxe);\n        };\n\n        cnf.yaxis.forEach(function (yaxe, i) {\n          if (isBarOpposite) {\n            if (_this.dCtx.gridPad.left < lbWidth) {\n              _this.dCtx.xPadLeft = lbWidth / 2 + 1;\n            }\n\n            _this.dCtx.xPadRight = lbWidth / 2 + 1;\n          } else {\n            padYAxe(yaxe, i);\n          }\n        });\n      }\n    }]);\n\n    return DimXAxis;\n  }();\n\n  var DimYAxis = /*#__PURE__*/function () {\n    function DimYAxis(dCtx) {\n      _classCallCheck(this, DimYAxis);\n\n      this.w = dCtx.w;\n      this.dCtx = dCtx;\n    }\n    /**\n     * Get Y Axis Dimensions\n     * @memberof Dimensions\n     * @return {{width, height}}\n     **/\n\n\n    _createClass(DimYAxis, [{\n      key: \"getyAxisLabelsCoords\",\n      value: function getyAxisLabelsCoords() {\n        var _this = this;\n\n        var w = this.w;\n        var width = 0;\n        var height = 0;\n        var ret = [];\n        var labelPad = 10;\n        var axesUtils = new AxesUtils(this.dCtx.ctx);\n        w.config.yaxis.map(function (yaxe, index) {\n          var yS = w.globals.yAxisScale[index];\n          var yAxisMinWidth = 0;\n          if (!axesUtils.isYAxisHidden(index) && yaxe.labels.show && yaxe.labels.minWidth !== undefined) yAxisMinWidth = yaxe.labels.minWidth;\n\n          if (!axesUtils.isYAxisHidden(index) && yaxe.labels.show && yS.result.length) {\n            var lbFormatter = w.globals.yLabelFormatters[index];\n            var minV = yS.niceMin === Number.MIN_VALUE ? 0 : yS.niceMin;\n            var longestStr = String(minV).length > String(yS.niceMax).length ? minV : yS.niceMax; // the second parameter -1 is the index of tick which user can use in the formatter\n\n            var val = lbFormatter(longestStr, {\n              seriesIndex: index,\n              dataPointIndex: -1,\n              w: w\n            });\n            var valArr = val; // if user has specified a custom formatter, and the result is null or empty, we need to discard the formatter and take the value as it is.\n\n            if (typeof val === 'undefined' || val.length === 0) {\n              val = longestStr;\n            }\n\n            if (w.globals.isBarHorizontal) {\n              labelPad = 0;\n              var barYaxisLabels = w.globals.labels.slice(); //  get the longest string from the labels array and also apply label formatter to it\n\n              val = Utils$1.getLargestStringFromArr(barYaxisLabels);\n              val = lbFormatter(val, {\n                seriesIndex: index,\n                dataPointIndex: -1,\n                w: w\n              });\n              valArr = _this.dCtx.dimHelpers.getLargestStringFromMultiArr(val, barYaxisLabels);\n            }\n\n            var graphics = new Graphics(_this.dCtx.ctx);\n            var rotateStr = 'rotate('.concat(yaxe.labels.rotate, ' 0 0)');\n            var rect = graphics.getTextRects(val, yaxe.labels.style.fontSize, yaxe.labels.style.fontFamily, rotateStr, false);\n            var arrLabelrect = rect;\n\n            if (val !== valArr) {\n              arrLabelrect = graphics.getTextRects(valArr, yaxe.labels.style.fontSize, yaxe.labels.style.fontFamily, rotateStr, false);\n            }\n\n            ret.push({\n              width: (yAxisMinWidth > arrLabelrect.width || yAxisMinWidth > rect.width ? yAxisMinWidth : arrLabelrect.width > rect.width ? arrLabelrect.width : rect.width) + labelPad,\n              height: arrLabelrect.height > rect.height ? arrLabelrect.height : rect.height\n            });\n          } else {\n            ret.push({\n              width: width,\n              height: height\n            });\n          }\n        });\n        return ret;\n      }\n      /**\n       * Get Y Axis Dimensions\n       * @memberof Dimensions\n       * @return {{width, height}}\n       **/\n\n    }, {\n      key: \"getyAxisTitleCoords\",\n      value: function getyAxisTitleCoords() {\n        var _this2 = this;\n\n        var w = this.w;\n        var ret = [];\n        w.config.yaxis.map(function (yaxe, index) {\n          if (yaxe.show && yaxe.title.text !== undefined) {\n            var graphics = new Graphics(_this2.dCtx.ctx);\n            var rotateStr = 'rotate('.concat(yaxe.title.rotate, ' 0 0)');\n            var rect = graphics.getTextRects(yaxe.title.text, yaxe.title.style.fontSize, yaxe.title.style.fontFamily, rotateStr, false);\n            ret.push({\n              width: rect.width,\n              height: rect.height\n            });\n          } else {\n            ret.push({\n              width: 0,\n              height: 0\n            });\n          }\n        });\n        return ret;\n      }\n    }, {\n      key: \"getTotalYAxisWidth\",\n      value: function getTotalYAxisWidth() {\n        var w = this.w;\n        var yAxisWidth = 0;\n        var yAxisWidthLeft = 0;\n        var yAxisWidthRight = 0;\n        var padding = w.globals.yAxisScale.length > 1 ? 10 : 0;\n        var axesUtils = new AxesUtils(this.dCtx.ctx);\n\n        var isHiddenYAxis = function isHiddenYAxis(index) {\n          return w.globals.ignoreYAxisIndexes.indexOf(index) > -1;\n        };\n\n        var padForLabelTitle = function padForLabelTitle(coord, index) {\n          var floating = w.config.yaxis[index].floating;\n          var width = 0;\n\n          if (coord.width > 0 && !floating) {\n            width = coord.width + padding;\n\n            if (isHiddenYAxis(index)) {\n              width = width - coord.width - padding;\n            }\n          } else {\n            width = floating || axesUtils.isYAxisHidden(index) ? 0 : 5;\n          }\n\n          w.config.yaxis[index].opposite ? yAxisWidthRight = yAxisWidthRight + width : yAxisWidthLeft = yAxisWidthLeft + width;\n          yAxisWidth = yAxisWidth + width;\n        };\n\n        w.globals.yLabelsCoords.map(function (yLabelCoord, index) {\n          padForLabelTitle(yLabelCoord, index);\n        });\n        w.globals.yTitleCoords.map(function (yTitleCoord, index) {\n          padForLabelTitle(yTitleCoord, index);\n        });\n\n        if (w.globals.isBarHorizontal && !w.config.yaxis[0].floating) {\n          yAxisWidth = w.globals.yLabelsCoords[0].width + w.globals.yTitleCoords[0].width + 15;\n        }\n\n        this.dCtx.yAxisWidthLeft = yAxisWidthLeft;\n        this.dCtx.yAxisWidthRight = yAxisWidthRight;\n        return yAxisWidth;\n      }\n    }]);\n\n    return DimYAxis;\n  }();\n\n  var DimGrid = /*#__PURE__*/function () {\n    function DimGrid(dCtx) {\n      _classCallCheck(this, DimGrid);\n\n      this.w = dCtx.w;\n      this.dCtx = dCtx;\n    }\n\n    _createClass(DimGrid, [{\n      key: \"gridPadForColumnsInNumericAxis\",\n      value: function gridPadForColumnsInNumericAxis(gridWidth) {\n        var w = this.w;\n\n        if (w.globals.noData || w.globals.allSeriesCollapsed) {\n          return 0;\n        }\n\n        var hasBar = function hasBar(type) {\n          return type === 'bar' || type === 'rangeBar' || type === 'candlestick' || type === 'boxPlot';\n        };\n\n        var type = w.config.chart.type;\n        var barWidth = 0;\n        var seriesLen = hasBar(type) ? w.config.series.length : 1;\n\n        if (w.globals.comboBarCount > 0) {\n          seriesLen = w.globals.comboBarCount;\n        }\n\n        w.globals.collapsedSeries.forEach(function (c) {\n          if (hasBar(c.type)) {\n            seriesLen = seriesLen - 1;\n          }\n        });\n\n        if (w.config.chart.stacked) {\n          seriesLen = 1;\n        }\n\n        var barsPresent = hasBar(type) || w.globals.comboBarCount > 0;\n\n        if (barsPresent && w.globals.isXNumeric && !w.globals.isBarHorizontal && seriesLen > 0) {\n          var xRatio = 0;\n          var xRange = Math.abs(w.globals.initialMaxX - w.globals.initialMinX);\n\n          if (xRange <= 3) {\n            xRange = w.globals.dataPoints;\n          }\n\n          xRatio = xRange / gridWidth;\n          var xDivision; // max barwidth should be equal to minXDiff to avoid overlap\n\n          if (w.globals.minXDiff && w.globals.minXDiff / xRatio > 0) {\n            xDivision = w.globals.minXDiff / xRatio;\n          }\n\n          if (xDivision > gridWidth / 2) {\n            xDivision = xDivision / 2;\n          }\n\n          barWidth = xDivision / seriesLen * parseInt(w.config.plotOptions.bar.columnWidth, 10) / 100;\n\n          if (barWidth < 1) {\n            barWidth = 1;\n          }\n\n          barWidth = barWidth / (seriesLen > 1 ? 1 : 1.5) + 5;\n          w.globals.barPadForNumericAxis = barWidth;\n        }\n\n        return barWidth;\n      }\n    }, {\n      key: \"gridPadFortitleSubtitle\",\n      value: function gridPadFortitleSubtitle() {\n        var _this = this;\n\n        var w = this.w;\n        var gl = w.globals;\n        var gridShrinkOffset = this.dCtx.isSparkline || !w.globals.axisCharts ? 0 : 10;\n        var titleSubtitle = ['title', 'subtitle'];\n        titleSubtitle.forEach(function (t) {\n          if (w.config[t].text !== undefined) {\n            gridShrinkOffset += w.config[t].margin;\n          } else {\n            gridShrinkOffset += _this.dCtx.isSparkline || !w.globals.axisCharts ? 0 : 5;\n          }\n        });\n\n        if (w.config.legend.show && w.config.legend.position === 'bottom' && !w.config.legend.floating && !w.globals.axisCharts) {\n          gridShrinkOffset += 10;\n        }\n\n        var titleCoords = this.dCtx.dimHelpers.getTitleSubtitleCoords('title');\n        var subtitleCoords = this.dCtx.dimHelpers.getTitleSubtitleCoords('subtitle');\n        gl.gridHeight = gl.gridHeight - titleCoords.height - subtitleCoords.height - gridShrinkOffset;\n        gl.translateY = gl.translateY + titleCoords.height + subtitleCoords.height + gridShrinkOffset;\n      }\n    }, {\n      key: \"setGridXPosForDualYAxis\",\n      value: function setGridXPosForDualYAxis(yTitleCoords, yaxisLabelCoords) {\n        var w = this.w;\n        var axesUtils = new AxesUtils(this.dCtx.ctx);\n        w.config.yaxis.map(function (yaxe, index) {\n          if (w.globals.ignoreYAxisIndexes.indexOf(index) === -1 && !yaxe.floating && !axesUtils.isYAxisHidden(index)) {\n            if (yaxe.opposite) {\n              w.globals.translateX = w.globals.translateX - (yaxisLabelCoords[index].width + yTitleCoords[index].width) - parseInt(w.config.yaxis[index].labels.style.fontSize, 10) / 1.2 - 12;\n            } // fixes apexcharts.js#1599\n\n\n            if (w.globals.translateX < 2) {\n              w.globals.translateX = 2;\n            }\n          }\n        });\n      }\n    }]);\n\n    return DimGrid;\n  }();\n\n  /**\n   * ApexCharts Dimensions Class for calculating rects of all elements that are drawn and will be drawn.\n   *\n   * @module Dimensions\n   **/\n\n  var Dimensions = /*#__PURE__*/function () {\n    function Dimensions(ctx) {\n      _classCallCheck(this, Dimensions);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.lgRect = {};\n      this.yAxisWidth = 0;\n      this.yAxisWidthLeft = 0;\n      this.yAxisWidthRight = 0;\n      this.xAxisHeight = 0;\n      this.isSparkline = this.w.config.chart.sparkline.enabled;\n      this.dimHelpers = new Helpers$3(this);\n      this.dimYAxis = new DimYAxis(this);\n      this.dimXAxis = new DimXAxis(this);\n      this.dimGrid = new DimGrid(this);\n      this.lgWidthForSideLegends = 0;\n      this.gridPad = this.w.config.grid.padding;\n      this.xPadRight = 0;\n      this.xPadLeft = 0;\n    }\n    /**\n     * @memberof Dimensions\n     * @param {object} w - chart context\n     **/\n\n\n    _createClass(Dimensions, [{\n      key: \"plotCoords\",\n      value: function plotCoords() {\n        var _this = this;\n\n        var w = this.w;\n        var gl = w.globals;\n        this.lgRect = this.dimHelpers.getLegendsRect();\n\n        if (this.isSparkline && (w.config.markers.discrete.length > 0 || w.config.markers.size > 0)) {\n          Object.entries(this.gridPad).forEach(function (_ref) {\n            var _ref2 = _slicedToArray(_ref, 2),\n                k = _ref2[0],\n                v = _ref2[1];\n\n            _this.gridPad[k] = Math.max(v, _this.w.globals.markers.largestSize / 1.5);\n          });\n        }\n\n        if (gl.axisCharts) {\n          // for line / area / scatter / column\n          this.setDimensionsForAxisCharts();\n        } else {\n          // for pie / donuts / circle\n          this.setDimensionsForNonAxisCharts();\n        }\n\n        this.dimGrid.gridPadFortitleSubtitle(); // after calculating everything, apply padding set by user\n\n        gl.gridHeight = gl.gridHeight - this.gridPad.top - this.gridPad.bottom;\n        gl.gridWidth = gl.gridWidth - this.gridPad.left - this.gridPad.right - this.xPadRight - this.xPadLeft;\n        var barWidth = this.dimGrid.gridPadForColumnsInNumericAxis(gl.gridWidth);\n        gl.gridWidth = gl.gridWidth - barWidth * 2;\n        gl.translateX = gl.translateX + this.gridPad.left + this.xPadLeft + (barWidth > 0 ? barWidth + 4 : 0);\n        gl.translateY = gl.translateY + this.gridPad.top;\n      }\n    }, {\n      key: \"setDimensionsForAxisCharts\",\n      value: function setDimensionsForAxisCharts() {\n        var _this2 = this;\n\n        var w = this.w;\n        var gl = w.globals;\n        var yaxisLabelCoords = this.dimYAxis.getyAxisLabelsCoords();\n        var yTitleCoords = this.dimYAxis.getyAxisTitleCoords();\n        w.globals.yLabelsCoords = [];\n        w.globals.yTitleCoords = [];\n        w.config.yaxis.map(function (yaxe, index) {\n          // store the labels and titles coords in global vars\n          w.globals.yLabelsCoords.push({\n            width: yaxisLabelCoords[index].width,\n            index: index\n          });\n          w.globals.yTitleCoords.push({\n            width: yTitleCoords[index].width,\n            index: index\n          });\n        });\n        this.yAxisWidth = this.dimYAxis.getTotalYAxisWidth();\n        var xaxisLabelCoords = this.dimXAxis.getxAxisLabelsCoords();\n        var xaxisGroupLabelCoords = this.dimXAxis.getxAxisGroupLabelsCoords();\n        var xtitleCoords = this.dimXAxis.getxAxisTitleCoords();\n        this.conditionalChecksForAxisCoords(xaxisLabelCoords, xtitleCoords, xaxisGroupLabelCoords);\n        gl.translateXAxisY = w.globals.rotateXLabels ? this.xAxisHeight / 8 : -4;\n        gl.translateXAxisX = w.globals.rotateXLabels && w.globals.isXNumeric && w.config.xaxis.labels.rotate <= -45 ? -this.xAxisWidth / 4 : 0;\n\n        if (w.globals.isBarHorizontal) {\n          gl.rotateXLabels = false;\n          gl.translateXAxisY = -1 * (parseInt(w.config.xaxis.labels.style.fontSize, 10) / 1.5);\n        }\n\n        gl.translateXAxisY = gl.translateXAxisY + w.config.xaxis.labels.offsetY;\n        gl.translateXAxisX = gl.translateXAxisX + w.config.xaxis.labels.offsetX;\n        var yAxisWidth = this.yAxisWidth;\n        var xAxisHeight = this.xAxisHeight;\n        gl.xAxisLabelsHeight = this.xAxisHeight - xtitleCoords.height;\n        gl.xAxisGroupLabelsHeight = gl.xAxisLabelsHeight - xaxisLabelCoords.height;\n        gl.xAxisLabelsWidth = this.xAxisWidth;\n        gl.xAxisHeight = this.xAxisHeight;\n        var translateY = 10;\n\n        if (w.config.chart.type === 'radar' || this.isSparkline) {\n          yAxisWidth = 0;\n          xAxisHeight = gl.goldenPadding;\n        }\n\n        if (this.isSparkline) {\n          this.lgRect = {\n            height: 0,\n            width: 0\n          };\n        }\n\n        if (this.isSparkline || w.config.chart.type === 'treemap') {\n          yAxisWidth = 0;\n          xAxisHeight = 0;\n          translateY = 0;\n        }\n\n        if (!this.isSparkline) {\n          this.dimXAxis.additionalPaddingXLabels(xaxisLabelCoords);\n        }\n\n        var legendTopBottom = function legendTopBottom() {\n          gl.translateX = yAxisWidth;\n          gl.gridHeight = gl.svgHeight - _this2.lgRect.height - xAxisHeight - (!_this2.isSparkline && w.config.chart.type !== 'treemap' ? w.globals.rotateXLabels ? 10 : 15 : 0);\n          gl.gridWidth = gl.svgWidth - yAxisWidth;\n        };\n\n        if (w.config.xaxis.position === 'top') translateY = gl.xAxisHeight - w.config.xaxis.axisTicks.height - 5;\n\n        switch (w.config.legend.position) {\n          case 'bottom':\n            gl.translateY = translateY;\n            legendTopBottom();\n            break;\n\n          case 'top':\n            gl.translateY = this.lgRect.height + translateY;\n            legendTopBottom();\n            break;\n\n          case 'left':\n            gl.translateY = translateY;\n            gl.translateX = this.lgRect.width + yAxisWidth;\n            gl.gridHeight = gl.svgHeight - xAxisHeight - 12;\n            gl.gridWidth = gl.svgWidth - this.lgRect.width - yAxisWidth;\n            break;\n\n          case 'right':\n            gl.translateY = translateY;\n            gl.translateX = yAxisWidth;\n            gl.gridHeight = gl.svgHeight - xAxisHeight - 12;\n            gl.gridWidth = gl.svgWidth - this.lgRect.width - yAxisWidth - 5;\n            break;\n\n          default:\n            throw new Error('Legend position not supported');\n        }\n\n        this.dimGrid.setGridXPosForDualYAxis(yTitleCoords, yaxisLabelCoords); // after drawing everything, set the Y axis positions\n\n        var objyAxis = new YAxis(this.ctx);\n        objyAxis.setYAxisXPosition(yaxisLabelCoords, yTitleCoords);\n      }\n    }, {\n      key: \"setDimensionsForNonAxisCharts\",\n      value: function setDimensionsForNonAxisCharts() {\n        var w = this.w;\n        var gl = w.globals;\n        var cnf = w.config;\n        var xPad = 0;\n\n        if (w.config.legend.show && !w.config.legend.floating) {\n          xPad = 20;\n        }\n\n        var type = cnf.chart.type === 'pie' || cnf.chart.type === 'polarArea' || cnf.chart.type === 'donut' ? 'pie' : 'radialBar';\n        var offY = cnf.plotOptions[type].offsetY;\n        var offX = cnf.plotOptions[type].offsetX;\n\n        if (!cnf.legend.show || cnf.legend.floating) {\n          gl.gridHeight = gl.svgHeight - cnf.grid.padding.left + cnf.grid.padding.right;\n          gl.gridWidth = gl.gridHeight;\n          gl.translateY = offY;\n          gl.translateX = offX + (gl.svgWidth - gl.gridWidth) / 2;\n          return;\n        }\n\n        switch (cnf.legend.position) {\n          case 'bottom':\n            gl.gridHeight = gl.svgHeight - this.lgRect.height - gl.goldenPadding;\n            gl.gridWidth = gl.svgWidth;\n            gl.translateY = offY - 10;\n            gl.translateX = offX + (gl.svgWidth - gl.gridWidth) / 2;\n            break;\n\n          case 'top':\n            gl.gridHeight = gl.svgHeight - this.lgRect.height - gl.goldenPadding;\n            gl.gridWidth = gl.svgWidth;\n            gl.translateY = this.lgRect.height + offY + 10;\n            gl.translateX = offX + (gl.svgWidth - gl.gridWidth) / 2;\n            break;\n\n          case 'left':\n            gl.gridWidth = gl.svgWidth - this.lgRect.width - xPad;\n            gl.gridHeight = cnf.chart.height !== 'auto' ? gl.svgHeight : gl.gridWidth;\n            gl.translateY = offY;\n            gl.translateX = offX + this.lgRect.width + xPad;\n            break;\n\n          case 'right':\n            gl.gridWidth = gl.svgWidth - this.lgRect.width - xPad - 5;\n            gl.gridHeight = cnf.chart.height !== 'auto' ? gl.svgHeight : gl.gridWidth;\n            gl.translateY = offY;\n            gl.translateX = offX + 10;\n            break;\n\n          default:\n            throw new Error('Legend position not supported');\n        }\n      }\n    }, {\n      key: \"conditionalChecksForAxisCoords\",\n      value: function conditionalChecksForAxisCoords(xaxisLabelCoords, xtitleCoords, xaxisGroupLabelCoords) {\n        var w = this.w;\n        var xAxisNum = w.globals.hasGroups ? 2 : 1;\n        var baseXAxisHeight = xaxisGroupLabelCoords.height + xaxisLabelCoords.height + xtitleCoords.height;\n        var xAxisHeightMultiplicate = w.globals.isMultiLineX ? 1.2 : w.globals.LINE_HEIGHT_RATIO;\n        var rotatedXAxisOffset = w.globals.rotateXLabels ? 22 : 10;\n        var rotatedXAxisLegendOffset = w.globals.rotateXLabels && w.config.legend.position === 'bottom';\n        var additionalOffset = rotatedXAxisLegendOffset ? 10 : 0;\n        this.xAxisHeight = baseXAxisHeight * xAxisHeightMultiplicate + xAxisNum * rotatedXAxisOffset + additionalOffset;\n        this.xAxisWidth = xaxisLabelCoords.width;\n\n        if (this.xAxisHeight - xtitleCoords.height > w.config.xaxis.labels.maxHeight) {\n          this.xAxisHeight = w.config.xaxis.labels.maxHeight;\n        }\n\n        if (w.config.xaxis.labels.minHeight && this.xAxisHeight < w.config.xaxis.labels.minHeight) {\n          this.xAxisHeight = w.config.xaxis.labels.minHeight;\n        }\n\n        if (w.config.xaxis.floating) {\n          this.xAxisHeight = 0;\n        }\n\n        var minYAxisWidth = 0;\n        var maxYAxisWidth = 0;\n        w.config.yaxis.forEach(function (y) {\n          minYAxisWidth += y.labels.minWidth;\n          maxYAxisWidth += y.labels.maxWidth;\n        });\n\n        if (this.yAxisWidth < minYAxisWidth) {\n          this.yAxisWidth = minYAxisWidth;\n        }\n\n        if (this.yAxisWidth > maxYAxisWidth) {\n          this.yAxisWidth = maxYAxisWidth;\n        }\n      }\n    }]);\n\n    return Dimensions;\n  }();\n\n  var Helpers$2 = /*#__PURE__*/function () {\n    function Helpers(lgCtx) {\n      _classCallCheck(this, Helpers);\n\n      this.w = lgCtx.w;\n      this.lgCtx = lgCtx;\n    }\n\n    _createClass(Helpers, [{\n      key: \"getLegendBBox\",\n      value: function getLegendBBox() {\n        var w = this.w;\n        var currLegendsWrap = w.globals.dom.baseEl.querySelector('.apexcharts-legend');\n        var currLegendsWrapRect = currLegendsWrap.getBoundingClientRect();\n        var currLegendsWrapWidth = currLegendsWrapRect.width;\n        var currLegendsWrapHeight = currLegendsWrapRect.height;\n        return {\n          clwh: currLegendsWrapHeight,\n          clww: currLegendsWrapWidth\n        };\n      }\n    }, {\n      key: \"toggleDataSeries\",\n      value: function toggleDataSeries(seriesCnt, isHidden) {\n        var _this = this;\n\n        var w = this.w;\n\n        if (w.globals.axisCharts || w.config.chart.type === 'radialBar') {\n          w.globals.resized = true; // we don't want initial animations again\n\n          var seriesEl = null;\n          var realIndex = null; // yes, make it null. 1 series will rise at a time\n\n          w.globals.risingSeries = [];\n\n          if (w.globals.axisCharts) {\n            seriesEl = w.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(seriesCnt, \"']\"));\n            realIndex = parseInt(seriesEl.getAttribute('data:realIndex'), 10);\n          } else {\n            seriesEl = w.globals.dom.baseEl.querySelector(\".apexcharts-series[rel='\".concat(seriesCnt + 1, \"']\"));\n            realIndex = parseInt(seriesEl.getAttribute('rel'), 10) - 1;\n          }\n\n          if (isHidden) {\n            var seriesToMakeVisible = [{\n              cs: w.globals.collapsedSeries,\n              csi: w.globals.collapsedSeriesIndices\n            }, {\n              cs: w.globals.ancillaryCollapsedSeries,\n              csi: w.globals.ancillaryCollapsedSeriesIndices\n            }];\n            seriesToMakeVisible.forEach(function (r) {\n              _this.riseCollapsedSeries(r.cs, r.csi, realIndex);\n            });\n          } else {\n            this.hideSeries({\n              seriesEl: seriesEl,\n              realIndex: realIndex\n            });\n          }\n        } else {\n          // for non-axis charts i.e pie / donuts\n          var _seriesEl = w.globals.dom.Paper.select(\" .apexcharts-series[rel='\".concat(seriesCnt + 1, \"'] path\"));\n\n          var type = w.config.chart.type;\n\n          if (type === 'pie' || type === 'polarArea' || type === 'donut') {\n            var dataLabels = w.config.plotOptions.pie.donut.labels;\n            var graphics = new Graphics(this.lgCtx.ctx);\n            graphics.pathMouseDown(_seriesEl.members[0], null);\n            this.lgCtx.ctx.pie.printDataLabelsInner(_seriesEl.members[0].node, dataLabels);\n          }\n\n          _seriesEl.fire('click');\n        }\n      }\n    }, {\n      key: \"hideSeries\",\n      value: function hideSeries(_ref) {\n        var seriesEl = _ref.seriesEl,\n            realIndex = _ref.realIndex;\n        var w = this.w;\n        var series = Utils$1.clone(w.config.series);\n\n        if (w.globals.axisCharts) {\n          var shouldNotHideYAxis = false;\n\n          if (w.config.yaxis[realIndex] && w.config.yaxis[realIndex].show && w.config.yaxis[realIndex].showAlways) {\n            shouldNotHideYAxis = true;\n\n            if (w.globals.ancillaryCollapsedSeriesIndices.indexOf(realIndex) < 0) {\n              w.globals.ancillaryCollapsedSeries.push({\n                index: realIndex,\n                data: series[realIndex].data.slice(),\n                type: seriesEl.parentNode.className.baseVal.split('-')[1]\n              });\n              w.globals.ancillaryCollapsedSeriesIndices.push(realIndex);\n            }\n          }\n\n          if (!shouldNotHideYAxis) {\n            w.globals.collapsedSeries.push({\n              index: realIndex,\n              data: series[realIndex].data.slice(),\n              type: seriesEl.parentNode.className.baseVal.split('-')[1]\n            });\n            w.globals.collapsedSeriesIndices.push(realIndex);\n            var removeIndexOfRising = w.globals.risingSeries.indexOf(realIndex);\n            w.globals.risingSeries.splice(removeIndexOfRising, 1);\n          }\n        } else {\n          w.globals.collapsedSeries.push({\n            index: realIndex,\n            data: series[realIndex]\n          });\n          w.globals.collapsedSeriesIndices.push(realIndex);\n        }\n\n        var seriesChildren = seriesEl.childNodes;\n\n        for (var sc = 0; sc < seriesChildren.length; sc++) {\n          if (seriesChildren[sc].classList.contains('apexcharts-series-markers-wrap')) {\n            if (seriesChildren[sc].classList.contains('apexcharts-hide')) {\n              seriesChildren[sc].classList.remove('apexcharts-hide');\n            } else {\n              seriesChildren[sc].classList.add('apexcharts-hide');\n            }\n          }\n        }\n\n        w.globals.allSeriesCollapsed = w.globals.collapsedSeries.length === w.config.series.length;\n        series = this._getSeriesBasedOnCollapsedState(series);\n\n        this.lgCtx.ctx.updateHelpers._updateSeries(series, w.config.chart.animations.dynamicAnimation.enabled);\n      }\n    }, {\n      key: \"riseCollapsedSeries\",\n      value: function riseCollapsedSeries(collapsedSeries, seriesIndices, realIndex) {\n        var w = this.w;\n        var series = Utils$1.clone(w.config.series);\n\n        if (collapsedSeries.length > 0) {\n          for (var c = 0; c < collapsedSeries.length; c++) {\n            if (collapsedSeries[c].index === realIndex) {\n              if (w.globals.axisCharts) {\n                series[realIndex].data = collapsedSeries[c].data.slice();\n                collapsedSeries.splice(c, 1);\n                seriesIndices.splice(c, 1);\n                w.globals.risingSeries.push(realIndex);\n              } else {\n                series[realIndex] = collapsedSeries[c].data;\n                collapsedSeries.splice(c, 1);\n                seriesIndices.splice(c, 1);\n                w.globals.risingSeries.push(realIndex);\n              }\n            }\n          }\n\n          series = this._getSeriesBasedOnCollapsedState(series);\n\n          this.lgCtx.ctx.updateHelpers._updateSeries(series, w.config.chart.animations.dynamicAnimation.enabled);\n        }\n      }\n    }, {\n      key: \"_getSeriesBasedOnCollapsedState\",\n      value: function _getSeriesBasedOnCollapsedState(series) {\n        var w = this.w;\n\n        if (w.globals.axisCharts) {\n          series.forEach(function (s, sI) {\n            if (w.globals.collapsedSeriesIndices.indexOf(sI) > -1) {\n              series[sI].data = [];\n            }\n          });\n        } else {\n          series.forEach(function (s, sI) {\n            if (w.globals.collapsedSeriesIndices.indexOf(sI) > -1) {\n              series[sI] = 0;\n            }\n          });\n        }\n\n        return series;\n      }\n    }]);\n\n    return Helpers;\n  }();\n\n  /**\n   * ApexCharts Legend Class to draw legend.\n   *\n   * @module Legend\n   **/\n\n  var Legend = /*#__PURE__*/function () {\n    function Legend(ctx) {\n      _classCallCheck(this, Legend);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.onLegendClick = this.onLegendClick.bind(this);\n      this.onLegendHovered = this.onLegendHovered.bind(this);\n      this.isBarsDistributed = this.w.config.chart.type === 'bar' && this.w.config.plotOptions.bar.distributed && this.w.config.series.length === 1;\n      this.legendHelpers = new Helpers$2(this);\n    }\n\n    _createClass(Legend, [{\n      key: \"init\",\n      value: function init() {\n        var w = this.w;\n        var gl = w.globals;\n        var cnf = w.config;\n        var showLegendAlways = cnf.legend.showForSingleSeries && gl.series.length === 1 || this.isBarsDistributed || gl.series.length > 1;\n\n        if ((showLegendAlways || !gl.axisCharts) && cnf.legend.show) {\n          while (gl.dom.elLegendWrap.firstChild) {\n            gl.dom.elLegendWrap.removeChild(gl.dom.elLegendWrap.firstChild);\n          }\n\n          this.drawLegends();\n\n          if (cnf.legend.position === 'bottom' || cnf.legend.position === 'top') {\n            this.legendAlignHorizontal();\n          } else if (cnf.legend.position === 'right' || cnf.legend.position === 'left') {\n            this.legendAlignVertical();\n          }\n        }\n      }\n    }, {\n      key: \"drawLegends\",\n      value: function drawLegends() {\n        var me = this;\n        var w = this.w;\n        var fontFamily = w.config.legend.fontFamily;\n        var legendNames = w.globals.seriesNames;\n        var fillcolor = w.globals.colors.slice();\n\n        if (w.config.chart.type === 'heatmap') {\n          var ranges = w.config.plotOptions.heatmap.colorScale.ranges;\n          legendNames = ranges.map(function (colorScale) {\n            return colorScale.name ? colorScale.name : colorScale.from + ' - ' + colorScale.to;\n          });\n          fillcolor = ranges.map(function (color) {\n            return color.color;\n          });\n        } else if (this.isBarsDistributed) {\n          legendNames = w.globals.labels.slice();\n        }\n\n        if (w.config.legend.customLegendItems.length) {\n          legendNames = w.config.legend.customLegendItems;\n        }\n\n        var legendFormatter = w.globals.legendFormatter;\n        var isLegendInversed = w.config.legend.inverseOrder;\n\n        for (var i = isLegendInversed ? legendNames.length - 1 : 0; isLegendInversed ? i >= 0 : i <= legendNames.length - 1; isLegendInversed ? i-- : i++) {\n          var text = legendFormatter(legendNames[i], {\n            seriesIndex: i,\n            w: w\n          });\n          var collapsedSeries = false;\n          var ancillaryCollapsedSeries = false;\n\n          if (w.globals.collapsedSeries.length > 0) {\n            for (var c = 0; c < w.globals.collapsedSeries.length; c++) {\n              if (w.globals.collapsedSeries[c].index === i) {\n                collapsedSeries = true;\n              }\n            }\n          }\n\n          if (w.globals.ancillaryCollapsedSeriesIndices.length > 0) {\n            for (var _c = 0; _c < w.globals.ancillaryCollapsedSeriesIndices.length; _c++) {\n              if (w.globals.ancillaryCollapsedSeriesIndices[_c] === i) {\n                ancillaryCollapsedSeries = true;\n              }\n            }\n          }\n\n          var elMarker = document.createElement('span');\n          elMarker.classList.add('apexcharts-legend-marker');\n          var mOffsetX = w.config.legend.markers.offsetX;\n          var mOffsetY = w.config.legend.markers.offsetY;\n          var mHeight = w.config.legend.markers.height;\n          var mWidth = w.config.legend.markers.width;\n          var mBorderWidth = w.config.legend.markers.strokeWidth;\n          var mBorderColor = w.config.legend.markers.strokeColor;\n          var mBorderRadius = w.config.legend.markers.radius;\n          var mStyle = elMarker.style;\n          mStyle.background = fillcolor[i];\n          mStyle.color = fillcolor[i];\n          mStyle.setProperty('background', fillcolor[i], 'important'); // override fill color with custom legend.markers.fillColors\n\n          if (w.config.legend.markers.fillColors && w.config.legend.markers.fillColors[i]) {\n            mStyle.background = w.config.legend.markers.fillColors[i];\n          } // override with data color\n\n\n          if (w.globals.seriesColors[i] !== undefined) {\n            mStyle.background = w.globals.seriesColors[i];\n            mStyle.color = w.globals.seriesColors[i];\n          }\n\n          mStyle.height = Array.isArray(mHeight) ? parseFloat(mHeight[i]) + 'px' : parseFloat(mHeight) + 'px';\n          mStyle.width = Array.isArray(mWidth) ? parseFloat(mWidth[i]) + 'px' : parseFloat(mWidth) + 'px';\n          mStyle.left = (Array.isArray(mOffsetX) ? parseFloat(mOffsetX[i]) : parseFloat(mOffsetX)) + 'px';\n          mStyle.top = (Array.isArray(mOffsetY) ? parseFloat(mOffsetY[i]) : parseFloat(mOffsetY)) + 'px';\n          mStyle.borderWidth = Array.isArray(mBorderWidth) ? mBorderWidth[i] : mBorderWidth;\n          mStyle.borderColor = Array.isArray(mBorderColor) ? mBorderColor[i] : mBorderColor;\n          mStyle.borderRadius = Array.isArray(mBorderRadius) ? parseFloat(mBorderRadius[i]) + 'px' : parseFloat(mBorderRadius) + 'px';\n\n          if (w.config.legend.markers.customHTML) {\n            if (Array.isArray(w.config.legend.markers.customHTML)) {\n              if (w.config.legend.markers.customHTML[i]) {\n                elMarker.innerHTML = w.config.legend.markers.customHTML[i]();\n              }\n            } else {\n              elMarker.innerHTML = w.config.legend.markers.customHTML();\n            }\n          }\n\n          Graphics.setAttrs(elMarker, {\n            rel: i + 1,\n            'data:collapsed': collapsedSeries || ancillaryCollapsedSeries\n          });\n\n          if (collapsedSeries || ancillaryCollapsedSeries) {\n            elMarker.classList.add('apexcharts-inactive-legend');\n          }\n\n          var elLegend = document.createElement('div');\n          var elLegendText = document.createElement('span');\n          elLegendText.classList.add('apexcharts-legend-text');\n          elLegendText.innerHTML = Array.isArray(text) ? text.join(' ') : text;\n          var textColor = w.config.legend.labels.useSeriesColors ? w.globals.colors[i] : w.config.legend.labels.colors;\n\n          if (!textColor) {\n            textColor = w.config.chart.foreColor;\n          }\n\n          elLegendText.style.color = textColor;\n          elLegendText.style.fontSize = parseFloat(w.config.legend.fontSize) + 'px';\n          elLegendText.style.fontWeight = w.config.legend.fontWeight;\n          elLegendText.style.fontFamily = fontFamily || w.config.chart.fontFamily;\n          Graphics.setAttrs(elLegendText, {\n            rel: i + 1,\n            i: i,\n            'data:default-text': encodeURIComponent(text),\n            'data:collapsed': collapsedSeries || ancillaryCollapsedSeries\n          });\n          elLegend.appendChild(elMarker);\n          elLegend.appendChild(elLegendText);\n          var coreUtils = new CoreUtils(this.ctx);\n\n          if (!w.config.legend.showForZeroSeries) {\n            var total = coreUtils.getSeriesTotalByIndex(i);\n\n            if (total === 0 && coreUtils.seriesHaveSameValues(i) && !coreUtils.isSeriesNull(i) && w.globals.collapsedSeriesIndices.indexOf(i) === -1 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(i) === -1) {\n              elLegend.classList.add('apexcharts-hidden-zero-series');\n            }\n          }\n\n          if (!w.config.legend.showForNullSeries) {\n            if (coreUtils.isSeriesNull(i) && w.globals.collapsedSeriesIndices.indexOf(i) === -1 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(i) === -1) {\n              elLegend.classList.add('apexcharts-hidden-null-series');\n            }\n          }\n\n          w.globals.dom.elLegendWrap.appendChild(elLegend);\n          w.globals.dom.elLegendWrap.classList.add(\"apexcharts-align-\".concat(w.config.legend.horizontalAlign));\n          w.globals.dom.elLegendWrap.classList.add('apx-legend-position-' + w.config.legend.position);\n          elLegend.classList.add('apexcharts-legend-series');\n          elLegend.style.margin = \"\".concat(w.config.legend.itemMargin.vertical, \"px \").concat(w.config.legend.itemMargin.horizontal, \"px\");\n          w.globals.dom.elLegendWrap.style.width = w.config.legend.width ? w.config.legend.width + 'px' : '';\n          w.globals.dom.elLegendWrap.style.height = w.config.legend.height ? w.config.legend.height + 'px' : '';\n          Graphics.setAttrs(elLegend, {\n            rel: i + 1,\n            seriesName: Utils$1.escapeString(legendNames[i]),\n            'data:collapsed': collapsedSeries || ancillaryCollapsedSeries\n          });\n\n          if (collapsedSeries || ancillaryCollapsedSeries) {\n            elLegend.classList.add('apexcharts-inactive-legend');\n          }\n\n          if (!w.config.legend.onItemClick.toggleDataSeries) {\n            elLegend.classList.add('apexcharts-no-click');\n          }\n        }\n\n        w.globals.dom.elWrap.addEventListener('click', me.onLegendClick, true);\n        w.globals.dom.elWrap.appendChild(w.globals.dom.elLegendWrap);\n\n        if (w.config.legend.onItemHover.highlightDataSeries && w.config.legend.customLegendItems.length === 0) {\n          w.globals.dom.elWrap.addEventListener('mousemove', me.onLegendHovered, true);\n          w.globals.dom.elWrap.addEventListener('mouseout', me.onLegendHovered, true);\n        }\n      }\n    }, {\n      key: \"setLegendWrapXY\",\n      value: function setLegendWrapXY(offsetX, offsetY) {\n        var w = this.w;\n        var elLegendWrap = w.globals.dom.elLegendWrap;\n        var legendRect = elLegendWrap.getBoundingClientRect();\n        var x = 0;\n        var y = 0;\n\n        if (w.config.legend.position === 'bottom') {\n          y = y + (w.globals.svgHeight - legendRect.height / 2);\n        } else if (w.config.legend.position === 'top') {\n          var dim = new Dimensions(this.ctx);\n          var titleH = dim.dimHelpers.getTitleSubtitleCoords('title').height;\n          var subtitleH = dim.dimHelpers.getTitleSubtitleCoords('subtitle').height;\n          y = y + (titleH > 0 ? titleH - 10 : 0) + (subtitleH > 0 ? subtitleH - 10 : 0);\n        }\n\n        elLegendWrap.style.position = 'absolute';\n        x = x + offsetX + w.config.legend.offsetX;\n        y = y + offsetY + w.config.legend.offsetY;\n        elLegendWrap.style.left = x + 'px';\n        elLegendWrap.style.top = y + 'px';\n\n        if (w.config.legend.position === 'bottom') {\n          elLegendWrap.style.top = 'auto';\n          elLegendWrap.style.bottom = 5 - w.config.legend.offsetY + 'px';\n        } else if (w.config.legend.position === 'right') {\n          elLegendWrap.style.left = 'auto';\n          elLegendWrap.style.right = 25 + w.config.legend.offsetX + 'px';\n        }\n\n        var fixedHeigthWidth = ['width', 'height'];\n        fixedHeigthWidth.forEach(function (hw) {\n          if (elLegendWrap.style[hw]) {\n            elLegendWrap.style[hw] = parseInt(w.config.legend[hw], 10) + 'px';\n          }\n        });\n      }\n    }, {\n      key: \"legendAlignHorizontal\",\n      value: function legendAlignHorizontal() {\n        var w = this.w;\n        var elLegendWrap = w.globals.dom.elLegendWrap;\n        elLegendWrap.style.right = 0;\n        var lRect = this.legendHelpers.getLegendBBox();\n        var dimensions = new Dimensions(this.ctx);\n        var titleRect = dimensions.dimHelpers.getTitleSubtitleCoords('title');\n        var subtitleRect = dimensions.dimHelpers.getTitleSubtitleCoords('subtitle');\n        var offsetX = 20;\n        var offsetY = 0; // the whole legend box is set to bottom\n\n        if (w.config.legend.position === 'bottom') {\n          offsetY = -lRect.clwh / 1.8;\n        } else if (w.config.legend.position === 'top') {\n          offsetY = titleRect.height + subtitleRect.height + w.config.title.margin + w.config.subtitle.margin - 10;\n        }\n\n        this.setLegendWrapXY(offsetX, offsetY);\n      }\n    }, {\n      key: \"legendAlignVertical\",\n      value: function legendAlignVertical() {\n        var w = this.w;\n        var lRect = this.legendHelpers.getLegendBBox();\n        var offsetY = 20;\n        var offsetX = 0;\n\n        if (w.config.legend.position === 'left') {\n          offsetX = 20;\n        }\n\n        if (w.config.legend.position === 'right') {\n          offsetX = w.globals.svgWidth - lRect.clww - 10;\n        }\n\n        this.setLegendWrapXY(offsetX, offsetY);\n      }\n    }, {\n      key: \"onLegendHovered\",\n      value: function onLegendHovered(e) {\n        var w = this.w;\n        var hoverOverLegend = e.target.classList.contains('apexcharts-legend-text') || e.target.classList.contains('apexcharts-legend-marker');\n\n        if (w.config.chart.type !== 'heatmap' && !this.isBarsDistributed) {\n          if (!e.target.classList.contains('apexcharts-inactive-legend') && hoverOverLegend) {\n            var series = new Series(this.ctx);\n            series.toggleSeriesOnHover(e, e.target);\n          }\n        } else {\n          // for heatmap handling\n          if (hoverOverLegend) {\n            var seriesCnt = parseInt(e.target.getAttribute('rel'), 10) - 1;\n            this.ctx.events.fireEvent('legendHover', [this.ctx, seriesCnt, this.w]);\n\n            var _series = new Series(this.ctx);\n\n            _series.highlightRangeInSeries(e, e.target);\n          }\n        }\n      }\n    }, {\n      key: \"onLegendClick\",\n      value: function onLegendClick(e) {\n        var w = this.w;\n        if (w.config.legend.customLegendItems.length) return;\n\n        if (e.target.classList.contains('apexcharts-legend-text') || e.target.classList.contains('apexcharts-legend-marker')) {\n          var seriesCnt = parseInt(e.target.getAttribute('rel'), 10) - 1;\n          var isHidden = e.target.getAttribute('data:collapsed') === 'true';\n          var legendClick = this.w.config.chart.events.legendClick;\n\n          if (typeof legendClick === 'function') {\n            legendClick(this.ctx, seriesCnt, this.w);\n          }\n\n          this.ctx.events.fireEvent('legendClick', [this.ctx, seriesCnt, this.w]);\n          var markerClick = this.w.config.legend.markers.onClick;\n\n          if (typeof markerClick === 'function' && e.target.classList.contains('apexcharts-legend-marker')) {\n            markerClick(this.ctx, seriesCnt, this.w);\n            this.ctx.events.fireEvent('legendMarkerClick', [this.ctx, seriesCnt, this.w]);\n          } // for now - just prevent click on heatmap legend - and allow hover only\n\n\n          var clickAllowed = w.config.chart.type !== 'treemap' && w.config.chart.type !== 'heatmap' && !this.isBarsDistributed;\n\n          if (clickAllowed && w.config.legend.onItemClick.toggleDataSeries) {\n            this.legendHelpers.toggleDataSeries(seriesCnt, isHidden);\n          }\n        }\n      }\n    }]);\n\n    return Legend;\n  }();\n\n  var icoPan = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" fill=\\\"#000000\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" width=\\\"24\\\">\\n    <defs>\\n        <path d=\\\"M0 0h24v24H0z\\\" id=\\\"a\\\"/>\\n    </defs>\\n    <clipPath id=\\\"b\\\">\\n        <use overflow=\\\"visible\\\" xlink:href=\\\"#a\\\"/>\\n    </clipPath>\\n    <path clip-path=\\\"url(#b)\\\" d=\\\"M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z\\\"/>\\n</svg>\";\n\n  var icoZoom = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" fill=\\\"#000000\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" width=\\\"24\\\">\\n    <path d=\\\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\\\"/>\\n    <path d=\\\"M0 0h24v24H0V0z\\\" fill=\\\"none\\\"/>\\n    <path d=\\\"M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z\\\"/>\\n</svg>\";\n\n  var icoReset = \"<svg fill=\\\"#000000\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" width=\\\"24\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\">\\n    <path d=\\\"M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z\\\"/>\\n    <path d=\\\"M0 0h24v24H0z\\\" fill=\\\"none\\\"/>\\n</svg>\";\n\n  var icoZoomIn = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\">\\n    <path d=\\\"M0 0h24v24H0z\\\" fill=\\\"none\\\"/>\\n    <path d=\\\"M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\\\"/>\\n</svg>\\n\";\n\n  var icoZoomOut = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\">\\n    <path d=\\\"M0 0h24v24H0z\\\" fill=\\\"none\\\"/>\\n    <path d=\\\"M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z\\\"/>\\n</svg>\\n\";\n\n  var icoSelect = \"<svg fill=\\\"#6E8192\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" width=\\\"24\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\">\\n    <path d=\\\"M0 0h24v24H0z\\\" fill=\\\"none\\\"/>\\n    <path d=\\\"M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2z\\\"/>\\n</svg>\";\n\n  var icoMenu = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\"><path fill=\\\"none\\\" d=\\\"M0 0h24v24H0V0z\\\"/><path d=\\\"M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z\\\"/></svg>\";\n\n  /**\n   * ApexCharts Toolbar Class for creating toolbar in axis based charts.\n   *\n   * @module Toolbar\n   **/\n\n  var Toolbar = /*#__PURE__*/function () {\n    function Toolbar(ctx) {\n      _classCallCheck(this, Toolbar);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      var w = this.w;\n      this.ev = this.w.config.chart.events;\n      this.selectedClass = 'apexcharts-selected';\n      this.localeValues = this.w.globals.locale.toolbar;\n      this.minX = w.globals.minX;\n      this.maxX = w.globals.maxX;\n    }\n\n    _createClass(Toolbar, [{\n      key: \"createToolbar\",\n      value: function createToolbar() {\n        var _this = this;\n\n        var w = this.w;\n\n        var createDiv = function createDiv() {\n          return document.createElement('div');\n        };\n\n        var elToolbarWrap = createDiv();\n        elToolbarWrap.setAttribute('class', 'apexcharts-toolbar');\n        elToolbarWrap.style.top = w.config.chart.toolbar.offsetY + 'px';\n        elToolbarWrap.style.right = -w.config.chart.toolbar.offsetX + 3 + 'px';\n        w.globals.dom.elWrap.appendChild(elToolbarWrap);\n        this.elZoom = createDiv();\n        this.elZoomIn = createDiv();\n        this.elZoomOut = createDiv();\n        this.elPan = createDiv();\n        this.elSelection = createDiv();\n        this.elZoomReset = createDiv();\n        this.elMenuIcon = createDiv();\n        this.elMenu = createDiv();\n        this.elCustomIcons = [];\n        this.t = w.config.chart.toolbar.tools;\n\n        if (Array.isArray(this.t.customIcons)) {\n          for (var i = 0; i < this.t.customIcons.length; i++) {\n            this.elCustomIcons.push(createDiv());\n          }\n        }\n\n        var toolbarControls = [];\n\n        var appendZoomControl = function appendZoomControl(type, el, ico) {\n          var tool = type.toLowerCase();\n\n          if (_this.t[tool] && w.config.chart.zoom.enabled) {\n            toolbarControls.push({\n              el: el,\n              icon: typeof _this.t[tool] === 'string' ? _this.t[tool] : ico,\n              title: _this.localeValues[type],\n              class: \"apexcharts-\".concat(tool, \"-icon\")\n            });\n          }\n        };\n\n        appendZoomControl('zoomIn', this.elZoomIn, icoZoomIn);\n        appendZoomControl('zoomOut', this.elZoomOut, icoZoomOut);\n\n        var zoomSelectionCtrls = function zoomSelectionCtrls(z) {\n          if (_this.t[z] && w.config.chart[z].enabled) {\n            toolbarControls.push({\n              el: z === 'zoom' ? _this.elZoom : _this.elSelection,\n              icon: typeof _this.t[z] === 'string' ? _this.t[z] : z === 'zoom' ? icoZoom : icoSelect,\n              title: _this.localeValues[z === 'zoom' ? 'selectionZoom' : 'selection'],\n              class: w.globals.isTouchDevice ? 'apexcharts-element-hidden' : \"apexcharts-\".concat(z, \"-icon\")\n            });\n          }\n        };\n\n        zoomSelectionCtrls('zoom');\n        zoomSelectionCtrls('selection');\n\n        if (this.t.pan && w.config.chart.zoom.enabled) {\n          toolbarControls.push({\n            el: this.elPan,\n            icon: typeof this.t.pan === 'string' ? this.t.pan : icoPan,\n            title: this.localeValues.pan,\n            class: w.globals.isTouchDevice ? 'apexcharts-element-hidden' : 'apexcharts-pan-icon'\n          });\n        }\n\n        appendZoomControl('reset', this.elZoomReset, icoReset);\n\n        if (this.t.download) {\n          toolbarControls.push({\n            el: this.elMenuIcon,\n            icon: typeof this.t.download === 'string' ? this.t.download : icoMenu,\n            title: this.localeValues.menu,\n            class: 'apexcharts-menu-icon'\n          });\n        }\n\n        for (var _i = 0; _i < this.elCustomIcons.length; _i++) {\n          toolbarControls.push({\n            el: this.elCustomIcons[_i],\n            icon: this.t.customIcons[_i].icon,\n            title: this.t.customIcons[_i].title,\n            index: this.t.customIcons[_i].index,\n            class: 'apexcharts-toolbar-custom-icon ' + this.t.customIcons[_i].class\n          });\n        }\n\n        toolbarControls.forEach(function (t, index) {\n          if (t.index) {\n            Utils$1.moveIndexInArray(toolbarControls, index, t.index);\n          }\n        });\n\n        for (var _i2 = 0; _i2 < toolbarControls.length; _i2++) {\n          Graphics.setAttrs(toolbarControls[_i2].el, {\n            class: toolbarControls[_i2].class,\n            title: toolbarControls[_i2].title\n          });\n          toolbarControls[_i2].el.innerHTML = toolbarControls[_i2].icon;\n          elToolbarWrap.appendChild(toolbarControls[_i2].el);\n        }\n\n        this._createHamburgerMenu(elToolbarWrap);\n\n        if (w.globals.zoomEnabled) {\n          this.elZoom.classList.add(this.selectedClass);\n        } else if (w.globals.panEnabled) {\n          this.elPan.classList.add(this.selectedClass);\n        } else if (w.globals.selectionEnabled) {\n          this.elSelection.classList.add(this.selectedClass);\n        }\n\n        this.addToolbarEventListeners();\n      }\n    }, {\n      key: \"_createHamburgerMenu\",\n      value: function _createHamburgerMenu(parent) {\n        this.elMenuItems = [];\n        parent.appendChild(this.elMenu);\n        Graphics.setAttrs(this.elMenu, {\n          class: 'apexcharts-menu'\n        });\n        var menuItems = [{\n          name: 'exportSVG',\n          title: this.localeValues.exportToSVG\n        }, {\n          name: 'exportPNG',\n          title: this.localeValues.exportToPNG\n        }, {\n          name: 'exportCSV',\n          title: this.localeValues.exportToCSV\n        }];\n\n        if (!this.w.globals.allSeriesHasEqualX) {\n          // if it is a multi series, and all series have variable x values, export CSV won't work\n          menuItems.splice(2, 1);\n        }\n\n        for (var i = 0; i < menuItems.length; i++) {\n          this.elMenuItems.push(document.createElement('div'));\n          this.elMenuItems[i].innerHTML = menuItems[i].title;\n          Graphics.setAttrs(this.elMenuItems[i], {\n            class: \"apexcharts-menu-item \".concat(menuItems[i].name),\n            title: menuItems[i].title\n          });\n          this.elMenu.appendChild(this.elMenuItems[i]);\n        }\n      }\n    }, {\n      key: \"addToolbarEventListeners\",\n      value: function addToolbarEventListeners() {\n        var _this2 = this;\n\n        this.elZoomReset.addEventListener('click', this.handleZoomReset.bind(this));\n        this.elSelection.addEventListener('click', this.toggleZoomSelection.bind(this, 'selection'));\n        this.elZoom.addEventListener('click', this.toggleZoomSelection.bind(this, 'zoom'));\n        this.elZoomIn.addEventListener('click', this.handleZoomIn.bind(this));\n        this.elZoomOut.addEventListener('click', this.handleZoomOut.bind(this));\n        this.elPan.addEventListener('click', this.togglePanning.bind(this));\n        this.elMenuIcon.addEventListener('click', this.toggleMenu.bind(this));\n        this.elMenuItems.forEach(function (m) {\n          if (m.classList.contains('exportSVG')) {\n            m.addEventListener('click', _this2.handleDownload.bind(_this2, 'svg'));\n          } else if (m.classList.contains('exportPNG')) {\n            m.addEventListener('click', _this2.handleDownload.bind(_this2, 'png'));\n          } else if (m.classList.contains('exportCSV')) {\n            m.addEventListener('click', _this2.handleDownload.bind(_this2, 'csv'));\n          }\n        });\n\n        for (var i = 0; i < this.t.customIcons.length; i++) {\n          this.elCustomIcons[i].addEventListener('click', this.t.customIcons[i].click.bind(this, this.ctx, this.ctx.w));\n        }\n      }\n    }, {\n      key: \"toggleZoomSelection\",\n      value: function toggleZoomSelection(type) {\n        var charts = this.ctx.getSyncedCharts();\n        charts.forEach(function (ch) {\n          ch.ctx.toolbar.toggleOtherControls();\n          var el = type === 'selection' ? ch.ctx.toolbar.elSelection : ch.ctx.toolbar.elZoom;\n          var enabledType = type === 'selection' ? 'selectionEnabled' : 'zoomEnabled';\n          ch.w.globals[enabledType] = !ch.w.globals[enabledType];\n\n          if (!el.classList.contains(ch.ctx.toolbar.selectedClass)) {\n            el.classList.add(ch.ctx.toolbar.selectedClass);\n          } else {\n            el.classList.remove(ch.ctx.toolbar.selectedClass);\n          }\n        });\n      }\n    }, {\n      key: \"getToolbarIconsReference\",\n      value: function getToolbarIconsReference() {\n        var w = this.w;\n\n        if (!this.elZoom) {\n          this.elZoom = w.globals.dom.baseEl.querySelector('.apexcharts-zoom-icon');\n        }\n\n        if (!this.elPan) {\n          this.elPan = w.globals.dom.baseEl.querySelector('.apexcharts-pan-icon');\n        }\n\n        if (!this.elSelection) {\n          this.elSelection = w.globals.dom.baseEl.querySelector('.apexcharts-selection-icon');\n        }\n      }\n    }, {\n      key: \"enableZoomPanFromToolbar\",\n      value: function enableZoomPanFromToolbar(type) {\n        this.toggleOtherControls();\n        type === 'pan' ? this.w.globals.panEnabled = true : this.w.globals.zoomEnabled = true;\n        var el = type === 'pan' ? this.elPan : this.elZoom;\n        var el2 = type === 'pan' ? this.elZoom : this.elPan;\n\n        if (el) {\n          el.classList.add(this.selectedClass);\n        }\n\n        if (el2) {\n          el2.classList.remove(this.selectedClass);\n        }\n      }\n    }, {\n      key: \"togglePanning\",\n      value: function togglePanning() {\n        var charts = this.ctx.getSyncedCharts();\n        charts.forEach(function (ch) {\n          ch.ctx.toolbar.toggleOtherControls();\n          ch.w.globals.panEnabled = !ch.w.globals.panEnabled;\n\n          if (!ch.ctx.toolbar.elPan.classList.contains(ch.ctx.toolbar.selectedClass)) {\n            ch.ctx.toolbar.elPan.classList.add(ch.ctx.toolbar.selectedClass);\n          } else {\n            ch.ctx.toolbar.elPan.classList.remove(ch.ctx.toolbar.selectedClass);\n          }\n        });\n      }\n    }, {\n      key: \"toggleOtherControls\",\n      value: function toggleOtherControls() {\n        var _this3 = this;\n\n        var w = this.w;\n        w.globals.panEnabled = false;\n        w.globals.zoomEnabled = false;\n        w.globals.selectionEnabled = false;\n        this.getToolbarIconsReference();\n        var toggleEls = [this.elPan, this.elSelection, this.elZoom];\n        toggleEls.forEach(function (el) {\n          if (el) {\n            el.classList.remove(_this3.selectedClass);\n          }\n        });\n      }\n    }, {\n      key: \"handleZoomIn\",\n      value: function handleZoomIn() {\n        var w = this.w;\n\n        if (w.globals.isRangeBar) {\n          this.minX = w.globals.minY;\n          this.maxX = w.globals.maxY;\n        }\n\n        var centerX = (this.minX + this.maxX) / 2;\n        var newMinX = (this.minX + centerX) / 2;\n        var newMaxX = (this.maxX + centerX) / 2;\n\n        var newMinXMaxX = this._getNewMinXMaxX(newMinX, newMaxX);\n\n        if (!w.globals.disableZoomIn) {\n          this.zoomUpdateOptions(newMinXMaxX.minX, newMinXMaxX.maxX);\n        }\n      }\n    }, {\n      key: \"handleZoomOut\",\n      value: function handleZoomOut() {\n        var w = this.w;\n\n        if (w.globals.isRangeBar) {\n          this.minX = w.globals.minY;\n          this.maxX = w.globals.maxY;\n        } // avoid zooming out beyond 1000 which may result in NaN values being printed on x-axis\n\n\n        if (w.config.xaxis.type === 'datetime' && new Date(this.minX).getUTCFullYear() < 1000) {\n          return;\n        }\n\n        var centerX = (this.minX + this.maxX) / 2;\n        var newMinX = this.minX - (centerX - this.minX);\n        var newMaxX = this.maxX - (centerX - this.maxX);\n\n        var newMinXMaxX = this._getNewMinXMaxX(newMinX, newMaxX);\n\n        if (!w.globals.disableZoomOut) {\n          this.zoomUpdateOptions(newMinXMaxX.minX, newMinXMaxX.maxX);\n        }\n      }\n    }, {\n      key: \"_getNewMinXMaxX\",\n      value: function _getNewMinXMaxX(newMinX, newMaxX) {\n        var shouldFloor = this.w.config.xaxis.convertedCatToNumeric;\n        return {\n          minX: shouldFloor ? Math.floor(newMinX) : newMinX,\n          maxX: shouldFloor ? Math.floor(newMaxX) : newMaxX\n        };\n      }\n    }, {\n      key: \"zoomUpdateOptions\",\n      value: function zoomUpdateOptions(newMinX, newMaxX) {\n        var w = this.w;\n\n        if (newMinX === undefined && newMaxX === undefined) {\n          this.handleZoomReset();\n          return;\n        }\n\n        if (w.config.xaxis.convertedCatToNumeric) {\n          // in category charts, avoid zooming out beyond min and max\n          if (newMinX < 1) {\n            newMinX = 1;\n            newMaxX = w.globals.dataPoints;\n          }\n\n          if (newMaxX - newMinX < 2) {\n            return;\n          }\n        }\n\n        var xaxis = {\n          min: newMinX,\n          max: newMaxX\n        };\n        var beforeZoomRange = this.getBeforeZoomRange(xaxis);\n\n        if (beforeZoomRange) {\n          xaxis = beforeZoomRange.xaxis;\n        }\n\n        var options = {\n          xaxis: xaxis\n        };\n        var yaxis = Utils$1.clone(w.globals.initialConfig.yaxis);\n\n        if (w.config.chart.zoom.autoScaleYaxis) {\n          var scale = new Range$1(this.ctx);\n          yaxis = scale.autoScaleY(this.ctx, yaxis, {\n            xaxis: xaxis\n          });\n        }\n\n        if (!w.config.chart.group) {\n          // if chart in a group, prevent yaxis update here\n          // fix issue #650\n          options.yaxis = yaxis;\n        }\n\n        this.w.globals.zoomed = true;\n\n        this.ctx.updateHelpers._updateOptions(options, false, this.w.config.chart.animations.dynamicAnimation.enabled);\n\n        this.zoomCallback(xaxis, yaxis);\n      }\n    }, {\n      key: \"zoomCallback\",\n      value: function zoomCallback(xaxis, yaxis) {\n        if (typeof this.ev.zoomed === 'function') {\n          this.ev.zoomed(this.ctx, {\n            xaxis: xaxis,\n            yaxis: yaxis\n          });\n        }\n      }\n    }, {\n      key: \"getBeforeZoomRange\",\n      value: function getBeforeZoomRange(xaxis, yaxis) {\n        var newRange = null;\n\n        if (typeof this.ev.beforeZoom === 'function') {\n          newRange = this.ev.beforeZoom(this, {\n            xaxis: xaxis,\n            yaxis: yaxis\n          });\n        }\n\n        return newRange;\n      }\n    }, {\n      key: \"toggleMenu\",\n      value: function toggleMenu() {\n        var _this4 = this;\n\n        window.setTimeout(function () {\n          if (_this4.elMenu.classList.contains('apexcharts-menu-open')) {\n            _this4.elMenu.classList.remove('apexcharts-menu-open');\n          } else {\n            _this4.elMenu.classList.add('apexcharts-menu-open');\n          }\n        }, 0);\n      }\n    }, {\n      key: \"handleDownload\",\n      value: function handleDownload(type) {\n        var w = this.w;\n        var exprt = new Exports(this.ctx);\n\n        switch (type) {\n          case 'svg':\n            exprt.exportToSVG(this.ctx);\n            break;\n\n          case 'png':\n            exprt.exportToPng(this.ctx);\n            break;\n\n          case 'csv':\n            exprt.exportToCSV({\n              series: w.config.series,\n              columnDelimiter: w.config.chart.toolbar.export.csv.columnDelimiter\n            });\n            break;\n        }\n      }\n    }, {\n      key: \"handleZoomReset\",\n      value: function handleZoomReset(e) {\n        var charts = this.ctx.getSyncedCharts();\n        charts.forEach(function (ch) {\n          var w = ch.w; // forget lastXAxis min/max as reset button isn't resetting the x-axis completely if zoomX is called before\n\n          w.globals.lastXAxis.min = undefined;\n          w.globals.lastXAxis.max = undefined;\n          ch.updateHelpers.revertDefaultAxisMinMax();\n\n          if (typeof w.config.chart.events.beforeResetZoom === 'function') {\n            // here, user get an option to control xaxis and yaxis when resetZoom is called\n            // at this point, whatever is returned from w.config.chart.events.beforeResetZoom\n            // is set as the new xaxis/yaxis min/max\n            var resetZoomRange = w.config.chart.events.beforeResetZoom(ch, w);\n\n            if (resetZoomRange) {\n              ch.updateHelpers.revertDefaultAxisMinMax(resetZoomRange);\n            }\n          }\n\n          if (typeof w.config.chart.events.zoomed === 'function') {\n            ch.ctx.toolbar.zoomCallback({\n              min: w.config.xaxis.min,\n              max: w.config.xaxis.max\n            });\n          }\n\n          w.globals.zoomed = false; // if user has some series collapsed before hitting zoom reset button,\n          // those series should stay collapsed\n\n          var series = ch.ctx.series.emptyCollapsedSeries(Utils$1.clone(w.globals.initialSeries));\n\n          ch.updateHelpers._updateSeries(series, w.config.chart.animations.dynamicAnimation.enabled);\n        });\n      }\n    }, {\n      key: \"destroy\",\n      value: function destroy() {\n        this.elZoom = null;\n        this.elZoomIn = null;\n        this.elZoomOut = null;\n        this.elPan = null;\n        this.elSelection = null;\n        this.elZoomReset = null;\n        this.elMenuIcon = null;\n      }\n    }]);\n\n    return Toolbar;\n  }();\n\n  /**\n   * ApexCharts Zoom Class for handling zooming and panning on axes based charts.\n   *\n   * @module ZoomPanSelection\n   **/\n\n  var ZoomPanSelection = /*#__PURE__*/function (_Toolbar) {\n    _inherits(ZoomPanSelection, _Toolbar);\n\n    var _super = _createSuper(ZoomPanSelection);\n\n    function ZoomPanSelection(ctx) {\n      var _this;\n\n      _classCallCheck(this, ZoomPanSelection);\n\n      _this = _super.call(this, ctx);\n      _this.ctx = ctx;\n      _this.w = ctx.w;\n      _this.dragged = false;\n      _this.graphics = new Graphics(_this.ctx);\n      _this.eventList = ['mousedown', 'mouseleave', 'mousemove', 'touchstart', 'touchmove', 'mouseup', 'touchend'];\n      _this.clientX = 0;\n      _this.clientY = 0;\n      _this.startX = 0;\n      _this.endX = 0;\n      _this.dragX = 0;\n      _this.startY = 0;\n      _this.endY = 0;\n      _this.dragY = 0;\n      _this.moveDirection = 'none';\n      return _this;\n    }\n\n    _createClass(ZoomPanSelection, [{\n      key: \"init\",\n      value: function init(_ref) {\n        var _this2 = this;\n\n        var xyRatios = _ref.xyRatios;\n        var w = this.w;\n        var me = this;\n        this.xyRatios = xyRatios;\n        this.zoomRect = this.graphics.drawRect(0, 0, 0, 0);\n        this.selectionRect = this.graphics.drawRect(0, 0, 0, 0);\n        this.gridRect = w.globals.dom.baseEl.querySelector('.apexcharts-grid');\n        this.zoomRect.node.classList.add('apexcharts-zoom-rect');\n        this.selectionRect.node.classList.add('apexcharts-selection-rect');\n        w.globals.dom.elGraphical.add(this.zoomRect);\n        w.globals.dom.elGraphical.add(this.selectionRect);\n\n        if (w.config.chart.selection.type === 'x') {\n          this.slDraggableRect = this.selectionRect.draggable({\n            minX: 0,\n            minY: 0,\n            maxX: w.globals.gridWidth,\n            maxY: w.globals.gridHeight\n          }).on('dragmove', this.selectionDragging.bind(this, 'dragging'));\n        } else if (w.config.chart.selection.type === 'y') {\n          this.slDraggableRect = this.selectionRect.draggable({\n            minX: 0,\n            maxX: w.globals.gridWidth\n          }).on('dragmove', this.selectionDragging.bind(this, 'dragging'));\n        } else {\n          this.slDraggableRect = this.selectionRect.draggable().on('dragmove', this.selectionDragging.bind(this, 'dragging'));\n        }\n\n        this.preselectedSelection();\n        this.hoverArea = w.globals.dom.baseEl.querySelector(\"\".concat(w.globals.chartClass, \" .apexcharts-svg\"));\n        this.hoverArea.classList.add('apexcharts-zoomable');\n        this.eventList.forEach(function (event) {\n          _this2.hoverArea.addEventListener(event, me.svgMouseEvents.bind(me, xyRatios), {\n            capture: false,\n            passive: true\n          });\n        });\n      } // remove the event listeners which were previously added on hover area\n\n    }, {\n      key: \"destroy\",\n      value: function destroy() {\n        if (this.slDraggableRect) {\n          this.slDraggableRect.draggable(false);\n          this.slDraggableRect.off();\n          this.selectionRect.off();\n        }\n\n        this.selectionRect = null;\n        this.zoomRect = null;\n        this.gridRect = null;\n      }\n    }, {\n      key: \"svgMouseEvents\",\n      value: function svgMouseEvents(xyRatios, e) {\n        var w = this.w;\n        var me = this;\n        var toolbar = this.ctx.toolbar;\n        var zoomtype = w.globals.zoomEnabled ? w.config.chart.zoom.type : w.config.chart.selection.type;\n        var autoSelected = w.config.chart.toolbar.autoSelected;\n\n        if (e.shiftKey) {\n          this.shiftWasPressed = true;\n          toolbar.enableZoomPanFromToolbar(autoSelected === 'pan' ? 'zoom' : 'pan');\n        } else {\n          if (this.shiftWasPressed) {\n            toolbar.enableZoomPanFromToolbar(autoSelected);\n            this.shiftWasPressed = false;\n          }\n        }\n\n        if (!e.target) return;\n        var tc = e.target.classList;\n        var pc;\n\n        if (e.target.parentNode && e.target.parentNode !== null) {\n          pc = e.target.parentNode.classList;\n        }\n\n        var falsePositives = tc.contains('apexcharts-selection-rect') || tc.contains('apexcharts-legend-marker') || tc.contains('apexcharts-legend-text') || pc && pc.contains('apexcharts-toolbar');\n        if (falsePositives) return;\n        me.clientX = e.type === 'touchmove' || e.type === 'touchstart' ? e.touches[0].clientX : e.type === 'touchend' ? e.changedTouches[0].clientX : e.clientX;\n        me.clientY = e.type === 'touchmove' || e.type === 'touchstart' ? e.touches[0].clientY : e.type === 'touchend' ? e.changedTouches[0].clientY : e.clientY;\n\n        if (e.type === 'mousedown' && e.which === 1) {\n          var gridRectDim = me.gridRect.getBoundingClientRect();\n          me.startX = me.clientX - gridRectDim.left;\n          me.startY = me.clientY - gridRectDim.top;\n          me.dragged = false;\n          me.w.globals.mousedown = true;\n        }\n\n        if (e.type === 'mousemove' && e.which === 1 || e.type === 'touchmove') {\n          me.dragged = true;\n\n          if (w.globals.panEnabled) {\n            w.globals.selection = null;\n\n            if (me.w.globals.mousedown) {\n              me.panDragging({\n                context: me,\n                zoomtype: zoomtype,\n                xyRatios: xyRatios\n              });\n            }\n          } else {\n            if (me.w.globals.mousedown && w.globals.zoomEnabled || me.w.globals.mousedown && w.globals.selectionEnabled) {\n              me.selection = me.selectionDrawing({\n                context: me,\n                zoomtype: zoomtype\n              });\n            }\n          }\n        }\n\n        if (e.type === 'mouseup' || e.type === 'touchend' || e.type === 'mouseleave') {\n          // we will be calling getBoundingClientRect on each mousedown/mousemove/mouseup\n          var _gridRectDim = me.gridRect.getBoundingClientRect();\n\n          if (me.w.globals.mousedown) {\n            // user released the drag, now do all the calculations\n            me.endX = me.clientX - _gridRectDim.left;\n            me.endY = me.clientY - _gridRectDim.top;\n            me.dragX = Math.abs(me.endX - me.startX);\n            me.dragY = Math.abs(me.endY - me.startY);\n\n            if (w.globals.zoomEnabled || w.globals.selectionEnabled) {\n              me.selectionDrawn({\n                context: me,\n                zoomtype: zoomtype\n              });\n            }\n\n            if (w.globals.panEnabled && w.config.xaxis.convertedCatToNumeric) {\n              me.delayedPanScrolled();\n            }\n          }\n\n          if (w.globals.zoomEnabled) {\n            me.hideSelectionRect(this.selectionRect);\n          }\n\n          me.dragged = false;\n          me.w.globals.mousedown = false;\n        }\n\n        this.makeSelectionRectDraggable();\n      }\n    }, {\n      key: \"makeSelectionRectDraggable\",\n      value: function makeSelectionRectDraggable() {\n        var w = this.w;\n        if (!this.selectionRect) return;\n        var rectDim = this.selectionRect.node.getBoundingClientRect();\n\n        if (rectDim.width > 0 && rectDim.height > 0) {\n          this.slDraggableRect.selectize({\n            points: 'l, r',\n            pointSize: 8,\n            pointType: 'rect'\n          }).resize({\n            constraint: {\n              minX: 0,\n              minY: 0,\n              maxX: w.globals.gridWidth,\n              maxY: w.globals.gridHeight\n            }\n          }).on('resizing', this.selectionDragging.bind(this, 'resizing'));\n        }\n      }\n    }, {\n      key: \"preselectedSelection\",\n      value: function preselectedSelection() {\n        var w = this.w;\n        var xyRatios = this.xyRatios;\n\n        if (!w.globals.zoomEnabled) {\n          if (typeof w.globals.selection !== 'undefined' && w.globals.selection !== null) {\n            this.drawSelectionRect(w.globals.selection);\n          } else {\n            if (w.config.chart.selection.xaxis.min !== undefined && w.config.chart.selection.xaxis.max !== undefined) {\n              var x = (w.config.chart.selection.xaxis.min - w.globals.minX) / xyRatios.xRatio;\n              var width = w.globals.gridWidth - (w.globals.maxX - w.config.chart.selection.xaxis.max) / xyRatios.xRatio - x;\n              var selectionRect = {\n                x: x,\n                y: 0,\n                width: width,\n                height: w.globals.gridHeight,\n                translateX: 0,\n                translateY: 0,\n                selectionEnabled: true\n              };\n              this.drawSelectionRect(selectionRect);\n              this.makeSelectionRectDraggable();\n\n              if (typeof w.config.chart.events.selection === 'function') {\n                w.config.chart.events.selection(this.ctx, {\n                  xaxis: {\n                    min: w.config.chart.selection.xaxis.min,\n                    max: w.config.chart.selection.xaxis.max\n                  },\n                  yaxis: {}\n                });\n              }\n            }\n          }\n        }\n      }\n    }, {\n      key: \"drawSelectionRect\",\n      value: function drawSelectionRect(_ref2) {\n        var x = _ref2.x,\n            y = _ref2.y,\n            width = _ref2.width,\n            height = _ref2.height,\n            _ref2$translateX = _ref2.translateX,\n            translateX = _ref2$translateX === void 0 ? 0 : _ref2$translateX,\n            _ref2$translateY = _ref2.translateY,\n            translateY = _ref2$translateY === void 0 ? 0 : _ref2$translateY;\n        var w = this.w;\n        var zoomRect = this.zoomRect;\n        var selectionRect = this.selectionRect;\n\n        if (this.dragged || w.globals.selection !== null) {\n          var scalingAttrs = {\n            transform: 'translate(' + translateX + ', ' + translateY + ')'\n          }; // change styles based on zoom or selection\n          // zoom is Enabled and user has dragged, so draw blue rect\n\n          if (w.globals.zoomEnabled && this.dragged) {\n            if (width < 0) width = 1; // fixes apexcharts.js#1168\n\n            zoomRect.attr({\n              x: x,\n              y: y,\n              width: width,\n              height: height,\n              fill: w.config.chart.zoom.zoomedArea.fill.color,\n              'fill-opacity': w.config.chart.zoom.zoomedArea.fill.opacity,\n              stroke: w.config.chart.zoom.zoomedArea.stroke.color,\n              'stroke-width': w.config.chart.zoom.zoomedArea.stroke.width,\n              'stroke-opacity': w.config.chart.zoom.zoomedArea.stroke.opacity\n            });\n            Graphics.setAttrs(zoomRect.node, scalingAttrs);\n          } // selection is enabled\n\n\n          if (w.globals.selectionEnabled) {\n            selectionRect.attr({\n              x: x,\n              y: y,\n              width: width > 0 ? width : 0,\n              height: height > 0 ? height : 0,\n              fill: w.config.chart.selection.fill.color,\n              'fill-opacity': w.config.chart.selection.fill.opacity,\n              stroke: w.config.chart.selection.stroke.color,\n              'stroke-width': w.config.chart.selection.stroke.width,\n              'stroke-dasharray': w.config.chart.selection.stroke.dashArray,\n              'stroke-opacity': w.config.chart.selection.stroke.opacity\n            });\n            Graphics.setAttrs(selectionRect.node, scalingAttrs);\n          }\n        }\n      }\n    }, {\n      key: \"hideSelectionRect\",\n      value: function hideSelectionRect(rect) {\n        if (rect) {\n          rect.attr({\n            x: 0,\n            y: 0,\n            width: 0,\n            height: 0\n          });\n        }\n      }\n    }, {\n      key: \"selectionDrawing\",\n      value: function selectionDrawing(_ref3) {\n        var context = _ref3.context,\n            zoomtype = _ref3.zoomtype;\n        var w = this.w;\n        var me = context;\n        var gridRectDim = this.gridRect.getBoundingClientRect();\n        var startX = me.startX - 1;\n        var startY = me.startY;\n        var inversedX = false;\n        var inversedY = false;\n        var selectionWidth = me.clientX - gridRectDim.left - startX;\n        var selectionHeight = me.clientY - gridRectDim.top - startY;\n        var selectionRect = {};\n\n        if (Math.abs(selectionWidth + startX) > w.globals.gridWidth) {\n          // user dragged the mouse outside drawing area to the right\n          selectionWidth = w.globals.gridWidth - startX;\n        } else if (me.clientX - gridRectDim.left < 0) {\n          // user dragged the mouse outside drawing area to the left\n          selectionWidth = startX;\n        } // inverse selection X\n\n\n        if (startX > me.clientX - gridRectDim.left) {\n          inversedX = true;\n          selectionWidth = Math.abs(selectionWidth);\n        } // inverse selection Y\n\n\n        if (startY > me.clientY - gridRectDim.top) {\n          inversedY = true;\n          selectionHeight = Math.abs(selectionHeight);\n        }\n\n        if (zoomtype === 'x') {\n          selectionRect = {\n            x: inversedX ? startX - selectionWidth : startX,\n            y: 0,\n            width: selectionWidth,\n            height: w.globals.gridHeight\n          };\n        } else if (zoomtype === 'y') {\n          selectionRect = {\n            x: 0,\n            y: inversedY ? startY - selectionHeight : startY,\n            width: w.globals.gridWidth,\n            height: selectionHeight\n          };\n        } else {\n          selectionRect = {\n            x: inversedX ? startX - selectionWidth : startX,\n            y: inversedY ? startY - selectionHeight : startY,\n            width: selectionWidth,\n            height: selectionHeight\n          };\n        }\n\n        me.drawSelectionRect(selectionRect);\n        me.selectionDragging('resizing');\n        return selectionRect;\n      }\n    }, {\n      key: \"selectionDragging\",\n      value: function selectionDragging(type, e) {\n        var _this3 = this;\n\n        var w = this.w;\n        var xyRatios = this.xyRatios;\n        var selRect = this.selectionRect;\n        var timerInterval = 0;\n\n        if (type === 'resizing') {\n          timerInterval = 30;\n        } // update selection when selection rect is dragged\n\n\n        var getSelAttr = function getSelAttr(attr) {\n          return parseFloat(selRect.node.getAttribute(attr));\n        };\n\n        var draggedProps = {\n          x: getSelAttr('x'),\n          y: getSelAttr('y'),\n          width: getSelAttr('width'),\n          height: getSelAttr('height')\n        };\n        w.globals.selection = draggedProps; // update selection ends\n\n        if (typeof w.config.chart.events.selection === 'function' && w.globals.selectionEnabled) {\n          // a small debouncer is required when resizing to avoid freezing the chart\n          clearTimeout(this.w.globals.selectionResizeTimer);\n          this.w.globals.selectionResizeTimer = window.setTimeout(function () {\n            var gridRectDim = _this3.gridRect.getBoundingClientRect();\n\n            var selectionRect = selRect.node.getBoundingClientRect();\n            var minX = w.globals.xAxisScale.niceMin + (selectionRect.left - gridRectDim.left) * xyRatios.xRatio;\n            var maxX = w.globals.xAxisScale.niceMin + (selectionRect.right - gridRectDim.left) * xyRatios.xRatio;\n            var minY = w.globals.yAxisScale[0].niceMin + (gridRectDim.bottom - selectionRect.bottom) * xyRatios.yRatio[0];\n            var maxY = w.globals.yAxisScale[0].niceMax - (selectionRect.top - gridRectDim.top) * xyRatios.yRatio[0];\n            var xyAxis = {\n              xaxis: {\n                min: minX,\n                max: maxX\n              },\n              yaxis: {\n                min: minY,\n                max: maxY\n              }\n            };\n            w.config.chart.events.selection(_this3.ctx, xyAxis);\n\n            if (w.config.chart.brush.enabled && w.config.chart.events.brushScrolled !== undefined) {\n              w.config.chart.events.brushScrolled(_this3.ctx, xyAxis);\n            }\n          }, timerInterval);\n        }\n      }\n    }, {\n      key: \"selectionDrawn\",\n      value: function selectionDrawn(_ref4) {\n        var context = _ref4.context,\n            zoomtype = _ref4.zoomtype;\n        var w = this.w;\n        var me = context;\n        var xyRatios = this.xyRatios;\n        var toolbar = this.ctx.toolbar;\n\n        if (me.startX > me.endX) {\n          var tempX = me.startX;\n          me.startX = me.endX;\n          me.endX = tempX;\n        }\n\n        if (me.startY > me.endY) {\n          var tempY = me.startY;\n          me.startY = me.endY;\n          me.endY = tempY;\n        }\n\n        var xLowestValue = undefined;\n        var xHighestValue = undefined;\n\n        if (!w.globals.isRangeBar) {\n          xLowestValue = w.globals.xAxisScale.niceMin + me.startX * xyRatios.xRatio;\n          xHighestValue = w.globals.xAxisScale.niceMin + me.endX * xyRatios.xRatio;\n        } else {\n          xLowestValue = w.globals.yAxisScale[0].niceMin + me.startX * xyRatios.invertedYRatio;\n          xHighestValue = w.globals.yAxisScale[0].niceMin + me.endX * xyRatios.invertedYRatio;\n        } // TODO: we will consider the 1st y axis values here for getting highest and lowest y\n\n\n        var yHighestValue = [];\n        var yLowestValue = [];\n        w.config.yaxis.forEach(function (yaxe, index) {\n          yHighestValue.push(w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[index] * me.startY);\n          yLowestValue.push(w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[index] * me.endY);\n        });\n\n        if (me.dragged && (me.dragX > 10 || me.dragY > 10) && xLowestValue !== xHighestValue) {\n          if (w.globals.zoomEnabled) {\n            var yaxis = Utils$1.clone(w.globals.initialConfig.yaxis);\n            var xaxis = Utils$1.clone(w.globals.initialConfig.xaxis);\n            w.globals.zoomed = true;\n\n            if (w.config.xaxis.convertedCatToNumeric) {\n              xLowestValue = Math.floor(xLowestValue);\n              xHighestValue = Math.floor(xHighestValue);\n\n              if (xLowestValue < 1) {\n                xLowestValue = 1;\n                xHighestValue = w.globals.dataPoints;\n              }\n\n              if (xHighestValue - xLowestValue < 2) {\n                xHighestValue = xLowestValue + 1;\n              }\n            }\n\n            if (zoomtype === 'xy' || zoomtype === 'x') {\n              xaxis = {\n                min: xLowestValue,\n                max: xHighestValue\n              };\n            }\n\n            if (zoomtype === 'xy' || zoomtype === 'y') {\n              yaxis.forEach(function (yaxe, index) {\n                yaxis[index].min = yLowestValue[index];\n                yaxis[index].max = yHighestValue[index];\n              });\n            }\n\n            if (w.config.chart.zoom.autoScaleYaxis) {\n              var scale = new Range$1(me.ctx);\n              yaxis = scale.autoScaleY(me.ctx, yaxis, {\n                xaxis: xaxis\n              });\n            }\n\n            if (toolbar) {\n              var beforeZoomRange = toolbar.getBeforeZoomRange(xaxis, yaxis);\n\n              if (beforeZoomRange) {\n                xaxis = beforeZoomRange.xaxis ? beforeZoomRange.xaxis : xaxis;\n                yaxis = beforeZoomRange.yaxis ? beforeZoomRange.yaxis : yaxis;\n              }\n            }\n\n            var options = {\n              xaxis: xaxis\n            };\n\n            if (!w.config.chart.group) {\n              // if chart in a group, prevent yaxis update here\n              // fix issue #650\n              options.yaxis = yaxis;\n            }\n\n            me.ctx.updateHelpers._updateOptions(options, false, me.w.config.chart.animations.dynamicAnimation.enabled);\n\n            if (typeof w.config.chart.events.zoomed === 'function') {\n              toolbar.zoomCallback(xaxis, yaxis);\n            }\n          } else if (w.globals.selectionEnabled) {\n            var _yaxis = null;\n            var _xaxis = null;\n            _xaxis = {\n              min: xLowestValue,\n              max: xHighestValue\n            };\n\n            if (zoomtype === 'xy' || zoomtype === 'y') {\n              _yaxis = Utils$1.clone(w.config.yaxis);\n\n              _yaxis.forEach(function (yaxe, index) {\n                _yaxis[index].min = yLowestValue[index];\n                _yaxis[index].max = yHighestValue[index];\n              });\n            }\n\n            w.globals.selection = me.selection;\n\n            if (typeof w.config.chart.events.selection === 'function') {\n              w.config.chart.events.selection(me.ctx, {\n                xaxis: _xaxis,\n                yaxis: _yaxis\n              });\n            }\n          }\n        }\n      }\n    }, {\n      key: \"panDragging\",\n      value: function panDragging(_ref5) {\n        var context = _ref5.context;\n        var w = this.w;\n        var me = context; // check to make sure there is data to compare against\n\n        if (typeof w.globals.lastClientPosition.x !== 'undefined') {\n          // get the change from last position to this position\n          var deltaX = w.globals.lastClientPosition.x - me.clientX;\n          var deltaY = w.globals.lastClientPosition.y - me.clientY; // check which direction had the highest amplitude and then figure out direction by checking if the value is greater or less than zero\n\n          if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX > 0) {\n            this.moveDirection = 'left';\n          } else if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX < 0) {\n            this.moveDirection = 'right';\n          } else if (Math.abs(deltaY) > Math.abs(deltaX) && deltaY > 0) {\n            this.moveDirection = 'up';\n          } else if (Math.abs(deltaY) > Math.abs(deltaX) && deltaY < 0) {\n            this.moveDirection = 'down';\n          }\n        } // set the new last position to the current for next time (to get the position of drag)\n\n\n        w.globals.lastClientPosition = {\n          x: me.clientX,\n          y: me.clientY\n        };\n        var xLowestValue = w.globals.isRangeBar ? w.globals.minY : w.globals.minX;\n        var xHighestValue = w.globals.isRangeBar ? w.globals.maxY : w.globals.maxX; // on a category, we don't pan continuosly as it causes bugs\n\n        if (!w.config.xaxis.convertedCatToNumeric) {\n          me.panScrolled(xLowestValue, xHighestValue);\n        }\n      }\n    }, {\n      key: \"delayedPanScrolled\",\n      value: function delayedPanScrolled() {\n        var w = this.w;\n        var newMinX = w.globals.minX;\n        var newMaxX = w.globals.maxX;\n        var centerX = (w.globals.maxX - w.globals.minX) / 2;\n\n        if (this.moveDirection === 'left') {\n          newMinX = w.globals.minX + centerX;\n          newMaxX = w.globals.maxX + centerX;\n        } else if (this.moveDirection === 'right') {\n          newMinX = w.globals.minX - centerX;\n          newMaxX = w.globals.maxX - centerX;\n        }\n\n        newMinX = Math.floor(newMinX);\n        newMaxX = Math.floor(newMaxX);\n        this.updateScrolledChart({\n          xaxis: {\n            min: newMinX,\n            max: newMaxX\n          }\n        }, newMinX, newMaxX);\n      }\n    }, {\n      key: \"panScrolled\",\n      value: function panScrolled(xLowestValue, xHighestValue) {\n        var w = this.w;\n        var xyRatios = this.xyRatios;\n        var yaxis = Utils$1.clone(w.globals.initialConfig.yaxis);\n        var xRatio = xyRatios.xRatio;\n        var minX = w.globals.minX;\n        var maxX = w.globals.maxX;\n\n        if (w.globals.isRangeBar) {\n          xRatio = xyRatios.invertedYRatio;\n          minX = w.globals.minY;\n          maxX = w.globals.maxY;\n        }\n\n        if (this.moveDirection === 'left') {\n          xLowestValue = minX + w.globals.gridWidth / 15 * xRatio;\n          xHighestValue = maxX + w.globals.gridWidth / 15 * xRatio;\n        } else if (this.moveDirection === 'right') {\n          xLowestValue = minX - w.globals.gridWidth / 15 * xRatio;\n          xHighestValue = maxX - w.globals.gridWidth / 15 * xRatio;\n        }\n\n        if (!w.globals.isRangeBar) {\n          if (xLowestValue < w.globals.initialMinX || xHighestValue > w.globals.initialMaxX) {\n            xLowestValue = minX;\n            xHighestValue = maxX;\n          }\n        }\n\n        var xaxis = {\n          min: xLowestValue,\n          max: xHighestValue\n        };\n\n        if (w.config.chart.zoom.autoScaleYaxis) {\n          var scale = new Range$1(this.ctx);\n          yaxis = scale.autoScaleY(this.ctx, yaxis, {\n            xaxis: xaxis\n          });\n        }\n\n        var options = {\n          xaxis: {\n            min: xLowestValue,\n            max: xHighestValue\n          }\n        };\n\n        if (!w.config.chart.group) {\n          // if chart in a group, prevent yaxis update here\n          // fix issue #650\n          options.yaxis = yaxis;\n        }\n\n        this.updateScrolledChart(options, xLowestValue, xHighestValue);\n      }\n    }, {\n      key: \"updateScrolledChart\",\n      value: function updateScrolledChart(options, xLowestValue, xHighestValue) {\n        var w = this.w;\n\n        this.ctx.updateHelpers._updateOptions(options, false, false);\n\n        if (typeof w.config.chart.events.scrolled === 'function') {\n          w.config.chart.events.scrolled(this.ctx, {\n            xaxis: {\n              min: xLowestValue,\n              max: xHighestValue\n            }\n          });\n        }\n      }\n    }]);\n\n    return ZoomPanSelection;\n  }(Toolbar);\n\n  /**\n   * ApexCharts Tooltip.Utils Class to support Tooltip functionality.\n   *\n   * @module Tooltip.Utils\n   **/\n\n  var Utils = /*#__PURE__*/function () {\n    function Utils(tooltipContext) {\n      _classCallCheck(this, Utils);\n\n      this.w = tooltipContext.w;\n      this.ttCtx = tooltipContext;\n      this.ctx = tooltipContext.ctx;\n    }\n    /**\n     ** When hovering over series, you need to capture which series is being hovered on.\n     ** This function will return both capturedseries index as well as inner index of that series\n     * @memberof Utils\n     * @param {object}\n     * - hoverArea = the rect on which user hovers\n     * - elGrid = dimensions of the hover rect (it can be different than hoverarea)\n     */\n\n\n    _createClass(Utils, [{\n      key: \"getNearestValues\",\n      value: function getNearestValues(_ref) {\n        var hoverArea = _ref.hoverArea,\n            elGrid = _ref.elGrid,\n            clientX = _ref.clientX,\n            clientY = _ref.clientY;\n        var w = this.w;\n        var seriesBound = elGrid.getBoundingClientRect();\n        var hoverWidth = seriesBound.width;\n        var hoverHeight = seriesBound.height;\n        var xDivisor = hoverWidth / (w.globals.dataPoints - 1);\n        var yDivisor = hoverHeight / w.globals.dataPoints;\n        var hasBars = this.hasBars();\n\n        if ((w.globals.comboCharts || hasBars) && !w.config.xaxis.convertedCatToNumeric) {\n          xDivisor = hoverWidth / w.globals.dataPoints;\n        }\n\n        var hoverX = clientX - seriesBound.left - w.globals.barPadForNumericAxis;\n        var hoverY = clientY - seriesBound.top;\n        var notInRect = hoverX < 0 || hoverY < 0 || hoverX > hoverWidth || hoverY > hoverHeight;\n\n        if (notInRect) {\n          hoverArea.classList.remove('hovering-zoom');\n          hoverArea.classList.remove('hovering-pan');\n        } else {\n          if (w.globals.zoomEnabled) {\n            hoverArea.classList.remove('hovering-pan');\n            hoverArea.classList.add('hovering-zoom');\n          } else if (w.globals.panEnabled) {\n            hoverArea.classList.remove('hovering-zoom');\n            hoverArea.classList.add('hovering-pan');\n          }\n        }\n\n        var j = Math.round(hoverX / xDivisor);\n        var jHorz = Math.floor(hoverY / yDivisor);\n\n        if (hasBars && !w.config.xaxis.convertedCatToNumeric) {\n          j = Math.ceil(hoverX / xDivisor);\n          j = j - 1;\n        }\n\n        var capturedSeries = null;\n        var closest = null;\n        var seriesXValArr = [];\n        var seriesYValArr = []; //add extra values to show markers for the first points. Included both axes to avoid incorrect positioning of the marker\n\n        w.globals.seriesXvalues.forEach(function (value) {\n          seriesXValArr.push([value[0] + 0.000001].concat(value));\n        });\n        w.globals.seriesYvalues.forEach(function (value) {\n          seriesYValArr.push([value[0] + 0.000001].concat(value));\n        });\n        seriesXValArr = seriesXValArr.map(function (seriesXVal) {\n          return seriesXVal.filter(function (s) {\n            return Utils$1.isNumber(s);\n          });\n        });\n        seriesYValArr = seriesYValArr.map(function (seriesYVal) {\n          return seriesYVal.filter(function (s) {\n            return Utils$1.isNumber(s);\n          });\n        }); // if X axis type is not category and tooltip is not shared, then we need to find the cursor position and get the nearest value\n\n        if (w.globals.isXNumeric) {\n          // Change origin of cursor position so that we can compute the relative nearest point to the cursor on our chart\n          // we only need to scale because all points are relative to the bounds.left and bounds.top => origin is virtually (0, 0)\n          var chartGridEl = this.ttCtx.getElGrid();\n          var chartGridElBoundingRect = chartGridEl.getBoundingClientRect();\n          var transformedHoverX = hoverX * (chartGridElBoundingRect.width / hoverWidth);\n          var transformedHoverY = hoverY * (chartGridElBoundingRect.height / hoverHeight);\n          closest = this.closestInMultiArray(transformedHoverX, transformedHoverY, seriesXValArr, seriesYValArr);\n          capturedSeries = closest.index;\n          j = closest.j;\n\n          if (capturedSeries !== null) {\n            // initial push, it should be a little smaller than the 1st val\n            seriesXValArr = w.globals.seriesXvalues[capturedSeries];\n            closest = this.closestInArray(transformedHoverX, seriesXValArr);\n            j = closest.index;\n          }\n        }\n\n        w.globals.capturedSeriesIndex = capturedSeries === null ? -1 : capturedSeries;\n        if (!j || j < 1) j = 0;\n\n        if (w.globals.isBarHorizontal) {\n          w.globals.capturedDataPointIndex = jHorz;\n        } else {\n          w.globals.capturedDataPointIndex = j;\n        }\n\n        return {\n          capturedSeries: capturedSeries,\n          j: w.globals.isBarHorizontal ? jHorz : j,\n          hoverX: hoverX,\n          hoverY: hoverY\n        };\n      }\n    }, {\n      key: \"closestInMultiArray\",\n      value: function closestInMultiArray(hoverX, hoverY, Xarrays, Yarrays) {\n        var w = this.w;\n        var activeIndex = 0;\n        var currIndex = null;\n        var j = -1;\n\n        if (w.globals.series.length > 1) {\n          activeIndex = this.getFirstActiveXArray(Xarrays);\n        } else {\n          currIndex = 0;\n        }\n\n        var currX = Xarrays[activeIndex][0];\n        var diffX = Math.abs(hoverX - currX); // find nearest point on x-axis\n\n        Xarrays.forEach(function (arrX) {\n          arrX.forEach(function (x, iX) {\n            var newDiff = Math.abs(hoverX - x);\n\n            if (newDiff < diffX) {\n              diffX = newDiff;\n              j = iX;\n            }\n          });\n        });\n\n        if (j !== -1) {\n          // find nearest graph on y-axis relevanted to nearest point on x-axis\n          var currY = Yarrays[activeIndex][j];\n          var diffY = Math.abs(hoverY - currY);\n          currIndex = activeIndex;\n          Yarrays.forEach(function (arrY, iAY) {\n            var newDiff = Math.abs(hoverY - arrY[j]);\n\n            if (newDiff < diffY) {\n              diffY = newDiff;\n              currIndex = iAY;\n            }\n          });\n        }\n\n        return {\n          index: currIndex,\n          j: j\n        };\n      }\n    }, {\n      key: \"getFirstActiveXArray\",\n      value: function getFirstActiveXArray(Xarrays) {\n        var w = this.w;\n        var activeIndex = 0;\n        var firstActiveSeriesIndex = Xarrays.map(function (xarr, index) {\n          return xarr.length > 0 ? index : -1;\n        });\n\n        for (var a = 0; a < firstActiveSeriesIndex.length; a++) {\n          if (firstActiveSeriesIndex[a] !== -1 && w.globals.collapsedSeriesIndices.indexOf(a) === -1 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(a) === -1) {\n            activeIndex = firstActiveSeriesIndex[a];\n            break;\n          }\n        }\n\n        return activeIndex;\n      }\n    }, {\n      key: \"closestInArray\",\n      value: function closestInArray(val, arr) {\n        var curr = arr[0];\n        var currIndex = null;\n        var diff = Math.abs(val - curr);\n\n        for (var i = 0; i < arr.length; i++) {\n          var newdiff = Math.abs(val - arr[i]);\n\n          if (newdiff < diff) {\n            diff = newdiff;\n            currIndex = i;\n          }\n        }\n\n        return {\n          index: currIndex\n        };\n      }\n      /**\n       * When there are multiple series, it is possible to have different x values for each series.\n       * But it may be possible in those multiple series, that there is same x value for 2 or more\n       * series.\n       * @memberof Utils\n       * @param {int}\n       * - j = is the inner index of series -> (series[i][j])\n       * @return {bool}\n       */\n\n    }, {\n      key: \"isXoverlap\",\n      value: function isXoverlap(j) {\n        var w = this.w;\n        var xSameForAllSeriesJArr = [];\n        var seriesX = w.globals.seriesX.filter(function (s) {\n          return typeof s[0] !== 'undefined';\n        });\n\n        if (seriesX.length > 0) {\n          for (var i = 0; i < seriesX.length - 1; i++) {\n            if (typeof seriesX[i][j] !== 'undefined' && typeof seriesX[i + 1][j] !== 'undefined') {\n              if (seriesX[i][j] !== seriesX[i + 1][j]) {\n                xSameForAllSeriesJArr.push('unEqual');\n              }\n            }\n          }\n        }\n\n        if (xSameForAllSeriesJArr.length === 0) {\n          return true;\n        }\n\n        return false;\n      }\n    }, {\n      key: \"isInitialSeriesSameLen\",\n      value: function isInitialSeriesSameLen() {\n        var sameLen = true;\n        var initialSeries = this.w.globals.initialSeries;\n\n        for (var i = 0; i < initialSeries.length - 1; i++) {\n          if (initialSeries[i].data.length !== initialSeries[i + 1].data.length) {\n            sameLen = false;\n            break;\n          }\n        }\n\n        return sameLen;\n      }\n    }, {\n      key: \"getBarsHeight\",\n      value: function getBarsHeight(allbars) {\n        var bars = _toConsumableArray(allbars);\n\n        var totalHeight = bars.reduce(function (acc, bar) {\n          return acc + bar.getBBox().height;\n        }, 0);\n        return totalHeight;\n      }\n    }, {\n      key: \"getElMarkers\",\n      value: function getElMarkers() {\n        return this.w.globals.dom.baseEl.querySelectorAll(' .apexcharts-series-markers');\n      }\n    }, {\n      key: \"getAllMarkers\",\n      value: function getAllMarkers() {\n        // first get all marker parents. This parent class contains series-index\n        // which helps to sort the markers as they are dynamic\n        var markersWraps = this.w.globals.dom.baseEl.querySelectorAll('.apexcharts-series-markers-wrap');\n        markersWraps = _toConsumableArray(markersWraps);\n        markersWraps.sort(function (a, b) {\n          var indexA = Number(a.getAttribute('data:realIndex'));\n          var indexB = Number(b.getAttribute('data:realIndex'));\n          return indexB < indexA ? 1 : indexB > indexA ? -1 : 0;\n        });\n        var markers = [];\n        markersWraps.forEach(function (m) {\n          markers.push(m.querySelector('.apexcharts-marker'));\n        });\n        return markers;\n      }\n    }, {\n      key: \"hasMarkers\",\n      value: function hasMarkers() {\n        var markers = this.getElMarkers();\n        return markers.length > 0;\n      }\n    }, {\n      key: \"getElBars\",\n      value: function getElBars() {\n        return this.w.globals.dom.baseEl.querySelectorAll('.apexcharts-bar-series,  .apexcharts-candlestick-series, .apexcharts-boxPlot-series, .apexcharts-rangebar-series');\n      }\n    }, {\n      key: \"hasBars\",\n      value: function hasBars() {\n        var bars = this.getElBars();\n        return bars.length > 0;\n      }\n    }, {\n      key: \"getHoverMarkerSize\",\n      value: function getHoverMarkerSize(index) {\n        var w = this.w;\n        var hoverSize = w.config.markers.hover.size;\n\n        if (hoverSize === undefined) {\n          hoverSize = w.globals.markers.size[index] + w.config.markers.hover.sizeOffset;\n        }\n\n        return hoverSize;\n      }\n    }, {\n      key: \"toggleAllTooltipSeriesGroups\",\n      value: function toggleAllTooltipSeriesGroups(state) {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n\n        if (ttCtx.allTooltipSeriesGroups.length === 0) {\n          ttCtx.allTooltipSeriesGroups = w.globals.dom.baseEl.querySelectorAll('.apexcharts-tooltip-series-group');\n        }\n\n        var allTooltipSeriesGroups = ttCtx.allTooltipSeriesGroups;\n\n        for (var i = 0; i < allTooltipSeriesGroups.length; i++) {\n          if (state === 'enable') {\n            allTooltipSeriesGroups[i].classList.add('apexcharts-active');\n            allTooltipSeriesGroups[i].style.display = w.config.tooltip.items.display;\n          } else {\n            allTooltipSeriesGroups[i].classList.remove('apexcharts-active');\n            allTooltipSeriesGroups[i].style.display = 'none';\n          }\n        }\n      }\n    }]);\n\n    return Utils;\n  }();\n\n  /**\n   * ApexCharts Tooltip.Labels Class to draw texts on the tooltip.\n   * This file deals with printing actual text on the tooltip.\n   *\n   * @module Tooltip.Labels\n   **/\n\n  var Labels = /*#__PURE__*/function () {\n    function Labels(tooltipContext) {\n      _classCallCheck(this, Labels);\n\n      this.w = tooltipContext.w;\n      this.ctx = tooltipContext.ctx;\n      this.ttCtx = tooltipContext;\n      this.tooltipUtil = new Utils(tooltipContext);\n    }\n\n    _createClass(Labels, [{\n      key: \"drawSeriesTexts\",\n      value: function drawSeriesTexts(_ref) {\n        var _ref$shared = _ref.shared,\n            shared = _ref$shared === void 0 ? true : _ref$shared,\n            ttItems = _ref.ttItems,\n            _ref$i = _ref.i,\n            i = _ref$i === void 0 ? 0 : _ref$i,\n            _ref$j = _ref.j,\n            j = _ref$j === void 0 ? null : _ref$j,\n            y1 = _ref.y1,\n            y2 = _ref.y2,\n            e = _ref.e;\n        var w = this.w;\n\n        if (w.config.tooltip.custom !== undefined) {\n          this.handleCustomTooltip({\n            i: i,\n            j: j,\n            y1: y1,\n            y2: y2,\n            w: w\n          });\n        } else {\n          this.toggleActiveInactiveSeries(shared);\n        }\n\n        var values = this.getValuesToPrint({\n          i: i,\n          j: j\n        });\n        this.printLabels({\n          i: i,\n          j: j,\n          values: values,\n          ttItems: ttItems,\n          shared: shared,\n          e: e\n        }); // Re-calculate tooltip dimensions now that we have drawn the text\n\n        var tooltipEl = this.ttCtx.getElTooltip();\n        this.ttCtx.tooltipRect.ttWidth = tooltipEl.getBoundingClientRect().width;\n        this.ttCtx.tooltipRect.ttHeight = tooltipEl.getBoundingClientRect().height;\n      }\n    }, {\n      key: \"printLabels\",\n      value: function printLabels(_ref2) {\n        var _this = this;\n\n        var i = _ref2.i,\n            j = _ref2.j,\n            values = _ref2.values,\n            ttItems = _ref2.ttItems,\n            shared = _ref2.shared,\n            e = _ref2.e;\n        var w = this.w;\n        var val;\n        var goalVals = [];\n\n        var hasGoalValues = function hasGoalValues(gi) {\n          return w.globals.seriesGoals[gi] && w.globals.seriesGoals[gi][j] && Array.isArray(w.globals.seriesGoals[gi][j]);\n        };\n\n        var xVal = values.xVal,\n            zVal = values.zVal,\n            xAxisTTVal = values.xAxisTTVal;\n        var seriesName = '';\n        var pColor = w.globals.colors[i]; // The pColor here is for the markers inside tooltip\n\n        if (j !== null && w.config.plotOptions.bar.distributed) {\n          pColor = w.globals.colors[j];\n        }\n\n        var _loop = function _loop(t, inverset) {\n          var f = _this.getFormatters(i);\n\n          seriesName = _this.getSeriesName({\n            fn: f.yLbTitleFormatter,\n            index: i,\n            seriesIndex: i,\n            j: j\n          });\n\n          if (w.config.chart.type === 'treemap') {\n            seriesName = f.yLbTitleFormatter(String(w.config.series[i].data[j].x), {\n              series: w.globals.series,\n              seriesIndex: i,\n              dataPointIndex: j,\n              w: w\n            });\n          }\n\n          var tIndex = w.config.tooltip.inverseOrder ? inverset : t;\n\n          if (w.globals.axisCharts) {\n            var getValBySeriesIndex = function getValBySeriesIndex(index) {\n              var _val = '';\n\n              if (w.globals.isRangeData) {\n                var _w$globals$seriesRang, _w$globals$seriesRang2;\n\n                _val += f.yLbFormatter((_w$globals$seriesRang = w.globals.seriesRangeStart) === null || _w$globals$seriesRang === void 0 ? void 0 : (_w$globals$seriesRang2 = _w$globals$seriesRang[index]) === null || _w$globals$seriesRang2 === void 0 ? void 0 : _w$globals$seriesRang2[j], {\n                  series: w.globals.seriesRangeStart,\n                  seriesIndex: index,\n                  dataPointIndex: j,\n                  w: w\n                }) + ' - ';\n              }\n\n              _val += f.yLbFormatter(w.globals.series[index][j], {\n                series: w.globals.series,\n                seriesIndex: index,\n                dataPointIndex: j,\n                w: w\n              });\n              return _val;\n            };\n\n            if (shared) {\n              f = _this.getFormatters(tIndex);\n              seriesName = _this.getSeriesName({\n                fn: f.yLbTitleFormatter,\n                index: tIndex,\n                seriesIndex: i,\n                j: j\n              });\n              pColor = w.globals.colors[tIndex];\n              val = getValBySeriesIndex(tIndex);\n\n              if (hasGoalValues(tIndex)) {\n                goalVals = w.globals.seriesGoals[tIndex][j].map(function (goal) {\n                  return {\n                    attrs: goal,\n                    val: f.yLbFormatter(goal.value, {\n                      seriesIndex: tIndex,\n                      dataPointIndex: j,\n                      w: w\n                    })\n                  };\n                });\n              }\n            } else {\n              var _e$target;\n\n              // get a color from a hover area (if it's a line pattern then get from a first line)\n              var targetFill = e === null || e === void 0 ? void 0 : (_e$target = e.target) === null || _e$target === void 0 ? void 0 : _e$target.getAttribute('fill');\n\n              if (targetFill) {\n                pColor = targetFill.indexOf('url') !== -1 ? document.querySelector(targetFill.substr(4).slice(0, -1)).childNodes[0].getAttribute('stroke') : targetFill;\n              }\n\n              val = getValBySeriesIndex(i);\n\n              if (hasGoalValues(i) && Array.isArray(w.globals.seriesGoals[i][j])) {\n                goalVals = w.globals.seriesGoals[i][j].map(function (goal) {\n                  return {\n                    attrs: goal,\n                    val: f.yLbFormatter(goal.value, {\n                      seriesIndex: i,\n                      dataPointIndex: j,\n                      w: w\n                    })\n                  };\n                });\n              }\n            }\n          } // for pie / donuts\n\n\n          if (j === null) {\n            val = f.yLbFormatter(w.globals.series[i], _objectSpread2(_objectSpread2({}, w), {}, {\n              seriesIndex: i,\n              dataPointIndex: i\n            }));\n          }\n\n          _this.DOMHandling({\n            i: i,\n            t: tIndex,\n            j: j,\n            ttItems: ttItems,\n            values: {\n              val: val,\n              goalVals: goalVals,\n              xVal: xVal,\n              xAxisTTVal: xAxisTTVal,\n              zVal: zVal\n            },\n            seriesName: seriesName,\n            shared: shared,\n            pColor: pColor\n          });\n        };\n\n        for (var t = 0, inverset = w.globals.series.length - 1; t < w.globals.series.length; t++, inverset--) {\n          _loop(t, inverset);\n        }\n      }\n    }, {\n      key: \"getFormatters\",\n      value: function getFormatters(i) {\n        var w = this.w;\n        var yLbFormatter = w.globals.yLabelFormatters[i];\n        var yLbTitleFormatter;\n\n        if (w.globals.ttVal !== undefined) {\n          if (Array.isArray(w.globals.ttVal)) {\n            yLbFormatter = w.globals.ttVal[i] && w.globals.ttVal[i].formatter;\n            yLbTitleFormatter = w.globals.ttVal[i] && w.globals.ttVal[i].title && w.globals.ttVal[i].title.formatter;\n          } else {\n            yLbFormatter = w.globals.ttVal.formatter;\n\n            if (typeof w.globals.ttVal.title.formatter === 'function') {\n              yLbTitleFormatter = w.globals.ttVal.title.formatter;\n            }\n          }\n        } else {\n          yLbTitleFormatter = w.config.tooltip.y.title.formatter;\n        }\n\n        if (typeof yLbFormatter !== 'function') {\n          if (w.globals.yLabelFormatters[0]) {\n            yLbFormatter = w.globals.yLabelFormatters[0];\n          } else {\n            yLbFormatter = function yLbFormatter(label) {\n              return label;\n            };\n          }\n        }\n\n        if (typeof yLbTitleFormatter !== 'function') {\n          yLbTitleFormatter = function yLbTitleFormatter(label) {\n            return label;\n          };\n        }\n\n        return {\n          yLbFormatter: yLbFormatter,\n          yLbTitleFormatter: yLbTitleFormatter\n        };\n      }\n    }, {\n      key: \"getSeriesName\",\n      value: function getSeriesName(_ref3) {\n        var fn = _ref3.fn,\n            index = _ref3.index,\n            seriesIndex = _ref3.seriesIndex,\n            j = _ref3.j;\n        var w = this.w;\n        return fn(String(w.globals.seriesNames[index]), {\n          series: w.globals.series,\n          seriesIndex: seriesIndex,\n          dataPointIndex: j,\n          w: w\n        });\n      }\n    }, {\n      key: \"DOMHandling\",\n      value: function DOMHandling(_ref4) {\n        _ref4.i;\n            var t = _ref4.t,\n            j = _ref4.j,\n            ttItems = _ref4.ttItems,\n            values = _ref4.values,\n            seriesName = _ref4.seriesName,\n            shared = _ref4.shared,\n            pColor = _ref4.pColor;\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n        var val = values.val,\n            goalVals = values.goalVals,\n            xVal = values.xVal,\n            xAxisTTVal = values.xAxisTTVal,\n            zVal = values.zVal;\n        var ttItemsChildren = null;\n        ttItemsChildren = ttItems[t].children;\n\n        if (w.config.tooltip.fillSeriesColor) {\n          ttItems[t].style.backgroundColor = pColor;\n          ttItemsChildren[0].style.display = 'none';\n        }\n\n        if (ttCtx.showTooltipTitle) {\n          if (ttCtx.tooltipTitle === null) {\n            // get it once if null, and store it in class property\n            ttCtx.tooltipTitle = w.globals.dom.baseEl.querySelector('.apexcharts-tooltip-title');\n          }\n\n          ttCtx.tooltipTitle.innerHTML = xVal;\n        } // if xaxis tooltip is constructed, we need to replace the innerHTML\n\n\n        if (ttCtx.isXAxisTooltipEnabled) {\n          ttCtx.xaxisTooltipText.innerHTML = xAxisTTVal !== '' ? xAxisTTVal : xVal;\n        }\n\n        var ttYLabel = ttItems[t].querySelector('.apexcharts-tooltip-text-y-label');\n\n        if (ttYLabel) {\n          ttYLabel.innerHTML = seriesName ? seriesName : '';\n        }\n\n        var ttYVal = ttItems[t].querySelector('.apexcharts-tooltip-text-y-value');\n\n        if (ttYVal) {\n          ttYVal.innerHTML = typeof val !== 'undefined' ? val : '';\n        }\n\n        if (ttItemsChildren[0] && ttItemsChildren[0].classList.contains('apexcharts-tooltip-marker')) {\n          if (w.config.tooltip.marker.fillColors && Array.isArray(w.config.tooltip.marker.fillColors)) {\n            pColor = w.config.tooltip.marker.fillColors[t];\n          }\n\n          ttItemsChildren[0].style.backgroundColor = pColor;\n        }\n\n        if (!w.config.tooltip.marker.show) {\n          ttItemsChildren[0].style.display = 'none';\n        }\n\n        var ttGLabel = ttItems[t].querySelector('.apexcharts-tooltip-text-goals-label');\n        var ttGVal = ttItems[t].querySelector('.apexcharts-tooltip-text-goals-value');\n\n        if (goalVals.length && w.globals.seriesGoals[t]) {\n          var createGoalsHtml = function createGoalsHtml() {\n            var gLabels = '<div >';\n            var gVals = '<div>';\n            goalVals.forEach(function (goal, gi) {\n              gLabels += \" <div style=\\\"display: flex\\\"><span class=\\\"apexcharts-tooltip-marker\\\" style=\\\"background-color: \".concat(goal.attrs.strokeColor, \"; height: 3px; border-radius: 0; top: 5px;\\\"></span> \").concat(goal.attrs.name, \"</div>\");\n              gVals += \"<div>\".concat(goal.val, \"</div>\");\n            });\n            ttGLabel.innerHTML = gLabels + \"</div>\";\n            ttGVal.innerHTML = gVals + \"</div>\";\n          };\n\n          if (shared) {\n            if (w.globals.seriesGoals[t][j] && Array.isArray(w.globals.seriesGoals[t][j])) {\n              createGoalsHtml();\n            } else {\n              ttGLabel.innerHTML = '';\n              ttGVal.innerHTML = '';\n            }\n          } else {\n            createGoalsHtml();\n          }\n        } else {\n          ttGLabel.innerHTML = '';\n          ttGVal.innerHTML = '';\n        }\n\n        if (zVal !== null) {\n          var ttZLabel = ttItems[t].querySelector('.apexcharts-tooltip-text-z-label');\n          ttZLabel.innerHTML = w.config.tooltip.z.title;\n          var ttZVal = ttItems[t].querySelector('.apexcharts-tooltip-text-z-value');\n          ttZVal.innerHTML = typeof zVal !== 'undefined' ? zVal : '';\n        }\n\n        if (shared && ttItemsChildren[0]) {\n          // hide when no Val or series collapsed\n          if (typeof val === 'undefined' || val === null || w.globals.ancillaryCollapsedSeriesIndices.indexOf(t) > -1 || w.globals.collapsedSeriesIndices.indexOf(t) > -1) {\n            ttItemsChildren[0].parentNode.style.display = 'none';\n          } else {\n            ttItemsChildren[0].parentNode.style.display = w.config.tooltip.items.display;\n          }\n        }\n      }\n    }, {\n      key: \"toggleActiveInactiveSeries\",\n      value: function toggleActiveInactiveSeries(shared) {\n        var w = this.w;\n\n        if (shared) {\n          // make all tooltips active\n          this.tooltipUtil.toggleAllTooltipSeriesGroups('enable');\n        } else {\n          // disable all tooltip text groups\n          this.tooltipUtil.toggleAllTooltipSeriesGroups('disable'); // enable the first tooltip text group\n\n          var firstTooltipSeriesGroup = w.globals.dom.baseEl.querySelector('.apexcharts-tooltip-series-group');\n\n          if (firstTooltipSeriesGroup) {\n            firstTooltipSeriesGroup.classList.add('apexcharts-active');\n            firstTooltipSeriesGroup.style.display = w.config.tooltip.items.display;\n          }\n        }\n      }\n    }, {\n      key: \"getValuesToPrint\",\n      value: function getValuesToPrint(_ref5) {\n        var i = _ref5.i,\n            j = _ref5.j;\n        var w = this.w;\n        var filteredSeriesX = this.ctx.series.filteredSeriesX();\n        var xVal = '';\n        var xAxisTTVal = '';\n        var zVal = null;\n        var val = null;\n        var customFormatterOpts = {\n          series: w.globals.series,\n          seriesIndex: i,\n          dataPointIndex: j,\n          w: w\n        };\n        var zFormatter = w.globals.ttZFormatter;\n\n        if (j === null) {\n          val = w.globals.series[i];\n        } else {\n          if (w.globals.isXNumeric && w.config.chart.type !== 'treemap') {\n            xVal = filteredSeriesX[i][j];\n\n            if (filteredSeriesX[i].length === 0) {\n              // a series (possibly the first one) might be collapsed, so get the next active index\n              var firstActiveSeriesIndex = this.tooltipUtil.getFirstActiveXArray(filteredSeriesX);\n              xVal = filteredSeriesX[firstActiveSeriesIndex][j];\n            }\n          } else {\n            xVal = typeof w.globals.labels[j] !== 'undefined' ? w.globals.labels[j] : '';\n          }\n        }\n\n        var bufferXVal = xVal;\n\n        if (w.globals.isXNumeric && w.config.xaxis.type === 'datetime') {\n          var xFormat = new Formatters(this.ctx);\n          xVal = xFormat.xLabelFormat(w.globals.ttKeyFormatter, bufferXVal, bufferXVal, {\n            i: undefined,\n            dateFormatter: new DateTime(this.ctx).formatDate,\n            w: this.w\n          });\n        } else {\n          if (w.globals.isBarHorizontal) {\n            xVal = w.globals.yLabelFormatters[0](bufferXVal, customFormatterOpts);\n          } else {\n            xVal = w.globals.xLabelFormatter(bufferXVal, customFormatterOpts);\n          }\n        } // override default x-axis formatter with tooltip formatter\n\n\n        if (w.config.tooltip.x.formatter !== undefined) {\n          xVal = w.globals.ttKeyFormatter(bufferXVal, customFormatterOpts);\n        }\n\n        if (w.globals.seriesZ.length > 0 && w.globals.seriesZ[i].length > 0) {\n          zVal = zFormatter(w.globals.seriesZ[i][j], w);\n        }\n\n        if (typeof w.config.xaxis.tooltip.formatter === 'function') {\n          xAxisTTVal = w.globals.xaxisTooltipFormatter(bufferXVal, customFormatterOpts);\n        } else {\n          xAxisTTVal = xVal;\n        }\n\n        return {\n          val: Array.isArray(val) ? val.join(' ') : val,\n          xVal: Array.isArray(xVal) ? xVal.join(' ') : xVal,\n          xAxisTTVal: Array.isArray(xAxisTTVal) ? xAxisTTVal.join(' ') : xAxisTTVal,\n          zVal: zVal\n        };\n      }\n    }, {\n      key: \"handleCustomTooltip\",\n      value: function handleCustomTooltip(_ref6) {\n        var i = _ref6.i,\n            j = _ref6.j,\n            y1 = _ref6.y1,\n            y2 = _ref6.y2,\n            w = _ref6.w;\n        var tooltipEl = this.ttCtx.getElTooltip();\n        var fn = w.config.tooltip.custom;\n\n        if (Array.isArray(fn) && fn[i]) {\n          fn = fn[i];\n        } // override everything with a custom html tooltip and replace it\n\n\n        tooltipEl.innerHTML = fn({\n          ctx: this.ctx,\n          series: w.globals.series,\n          seriesIndex: i,\n          dataPointIndex: j,\n          y1: y1,\n          y2: y2,\n          w: w\n        });\n      }\n    }]);\n\n    return Labels;\n  }();\n\n  /**\n   * ApexCharts Tooltip.Position Class to move the tooltip based on x and y position.\n   *\n   * @module Tooltip.Position\n   **/\n\n  var Position = /*#__PURE__*/function () {\n    function Position(tooltipContext) {\n      _classCallCheck(this, Position);\n\n      this.ttCtx = tooltipContext;\n      this.ctx = tooltipContext.ctx;\n      this.w = tooltipContext.w;\n    }\n    /**\n     * This will move the crosshair (the vertical/horz line that moves along with mouse)\n     * Along with this, this function also calls the xaxisMove function\n     * @memberof Position\n     * @param {int} - cx = point's x position, wherever point's x is, you need to move crosshair\n     */\n\n\n    _createClass(Position, [{\n      key: \"moveXCrosshairs\",\n      value: function moveXCrosshairs(cx) {\n        var j = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n        var ttCtx = this.ttCtx;\n        var w = this.w;\n        var xcrosshairs = ttCtx.getElXCrosshairs();\n        var x = cx - ttCtx.xcrosshairsWidth / 2;\n        var tickAmount = w.globals.labels.slice().length;\n\n        if (j !== null) {\n          x = w.globals.gridWidth / tickAmount * j;\n        }\n\n        if (xcrosshairs !== null && !w.globals.isBarHorizontal) {\n          xcrosshairs.setAttribute('x', x);\n          xcrosshairs.setAttribute('x1', x);\n          xcrosshairs.setAttribute('x2', x);\n          xcrosshairs.setAttribute('y2', w.globals.gridHeight);\n          xcrosshairs.classList.add('apexcharts-active');\n        }\n\n        if (x < 0) {\n          x = 0;\n        }\n\n        if (x > w.globals.gridWidth) {\n          x = w.globals.gridWidth;\n        }\n\n        if (ttCtx.isXAxisTooltipEnabled) {\n          var tx = x;\n\n          if (w.config.xaxis.crosshairs.width === 'tickWidth' || w.config.xaxis.crosshairs.width === 'barWidth') {\n            tx = x + ttCtx.xcrosshairsWidth / 2;\n          }\n\n          this.moveXAxisTooltip(tx);\n        }\n      }\n      /**\n       * This will move the crosshair (the vertical/horz line that moves along with mouse)\n       * Along with this, this function also calls the xaxisMove function\n       * @memberof Position\n       * @param {int} - cx = point's x position, wherever point's x is, you need to move crosshair\n       */\n\n    }, {\n      key: \"moveYCrosshairs\",\n      value: function moveYCrosshairs(cy) {\n        var ttCtx = this.ttCtx;\n\n        if (ttCtx.ycrosshairs !== null) {\n          Graphics.setAttrs(ttCtx.ycrosshairs, {\n            y1: cy,\n            y2: cy\n          });\n        }\n\n        if (ttCtx.ycrosshairsHidden !== null) {\n          Graphics.setAttrs(ttCtx.ycrosshairsHidden, {\n            y1: cy,\n            y2: cy\n          });\n        }\n      }\n      /**\n       ** AxisTooltip is the small rectangle which appears on x axis with x value, when user moves\n       * @memberof Position\n       * @param {int} - cx = point's x position, wherever point's x is, you need to move\n       */\n\n    }, {\n      key: \"moveXAxisTooltip\",\n      value: function moveXAxisTooltip(cx) {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n\n        if (ttCtx.xaxisTooltip !== null && ttCtx.xcrosshairsWidth !== 0) {\n          ttCtx.xaxisTooltip.classList.add('apexcharts-active');\n          var cy = ttCtx.xaxisOffY + w.config.xaxis.tooltip.offsetY + w.globals.translateY + 1 + w.config.xaxis.offsetY;\n          var xaxisTTText = ttCtx.xaxisTooltip.getBoundingClientRect();\n          var xaxisTTTextWidth = xaxisTTText.width;\n          cx = cx - xaxisTTTextWidth / 2;\n\n          if (!isNaN(cx)) {\n            cx = cx + w.globals.translateX;\n            var textRect = 0;\n            var graphics = new Graphics(this.ctx);\n            textRect = graphics.getTextRects(ttCtx.xaxisTooltipText.innerHTML);\n            ttCtx.xaxisTooltipText.style.minWidth = textRect.width + 'px';\n            ttCtx.xaxisTooltip.style.left = cx + 'px';\n            ttCtx.xaxisTooltip.style.top = cy + 'px';\n          }\n        }\n      }\n    }, {\n      key: \"moveYAxisTooltip\",\n      value: function moveYAxisTooltip(index) {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n\n        if (ttCtx.yaxisTTEls === null) {\n          ttCtx.yaxisTTEls = w.globals.dom.baseEl.querySelectorAll('.apexcharts-yaxistooltip');\n        }\n\n        var ycrosshairsHiddenRectY1 = parseInt(ttCtx.ycrosshairsHidden.getAttribute('y1'), 10);\n        var cy = w.globals.translateY + ycrosshairsHiddenRectY1;\n        var yAxisTTRect = ttCtx.yaxisTTEls[index].getBoundingClientRect();\n        var yAxisTTHeight = yAxisTTRect.height;\n        var cx = w.globals.translateYAxisX[index] - 2;\n\n        if (w.config.yaxis[index].opposite) {\n          cx = cx - 26;\n        }\n\n        cy = cy - yAxisTTHeight / 2;\n\n        if (w.globals.ignoreYAxisIndexes.indexOf(index) === -1) {\n          ttCtx.yaxisTTEls[index].classList.add('apexcharts-active');\n          ttCtx.yaxisTTEls[index].style.top = cy + 'px';\n          ttCtx.yaxisTTEls[index].style.left = cx + w.config.yaxis[index].tooltip.offsetX + 'px';\n        } else {\n          ttCtx.yaxisTTEls[index].classList.remove('apexcharts-active');\n        }\n      }\n      /**\n       ** moves the whole tooltip by changing x, y attrs\n       * @memberof Position\n       * @param {int} - cx = point's x position, wherever point's x is, you need to move tooltip\n       * @param {int} - cy = point's y position, wherever point's y is, you need to move tooltip\n       * @param {int} - r = point's radius\n       */\n\n    }, {\n      key: \"moveTooltip\",\n      value: function moveTooltip(cx, cy) {\n        var r = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n        var tooltipEl = ttCtx.getElTooltip();\n        var tooltipRect = ttCtx.tooltipRect;\n        var pointR = r !== null ? parseFloat(r) : 1;\n        var x = parseFloat(cx) + pointR + 5;\n        var y = parseFloat(cy) + pointR / 2; // - tooltipRect.ttHeight / 2\n\n        if (x > w.globals.gridWidth / 2) {\n          x = x - tooltipRect.ttWidth - pointR - 10;\n        }\n\n        if (x > w.globals.gridWidth - tooltipRect.ttWidth - 10) {\n          x = w.globals.gridWidth - tooltipRect.ttWidth;\n        }\n\n        if (x < -20) {\n          x = -20;\n        }\n\n        if (w.config.tooltip.followCursor) {\n          var elGrid = ttCtx.getElGrid();\n          var seriesBound = elGrid.getBoundingClientRect();\n          y = ttCtx.e.clientY + w.globals.translateY - seriesBound.top - tooltipRect.ttHeight / 2;\n        } else {\n          if (!w.globals.isBarHorizontal) {\n            if (tooltipRect.ttHeight / 2 + y > w.globals.gridHeight) {\n              y = w.globals.gridHeight - tooltipRect.ttHeight + w.globals.translateY;\n            }\n          }\n        }\n\n        if (!isNaN(x)) {\n          x = x + w.globals.translateX;\n          tooltipEl.style.left = x + 'px';\n          tooltipEl.style.top = y + 'px';\n        }\n      }\n    }, {\n      key: \"moveMarkers\",\n      value: function moveMarkers(i, j) {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n\n        if (w.globals.markers.size[i] > 0) {\n          var allPoints = w.globals.dom.baseEl.querySelectorAll(\" .apexcharts-series[data\\\\:realIndex='\".concat(i, \"'] .apexcharts-marker\"));\n\n          for (var p = 0; p < allPoints.length; p++) {\n            if (parseInt(allPoints[p].getAttribute('rel'), 10) === j) {\n              ttCtx.marker.resetPointsSize();\n              ttCtx.marker.enlargeCurrentPoint(j, allPoints[p]);\n            }\n          }\n        } else {\n          ttCtx.marker.resetPointsSize();\n          this.moveDynamicPointOnHover(j, i);\n        }\n      } // This function is used when you need to show markers/points only on hover -\n      // DIFFERENT X VALUES in multiple series\n\n    }, {\n      key: \"moveDynamicPointOnHover\",\n      value: function moveDynamicPointOnHover(j, capturedSeries) {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n        var cx = 0;\n        var cy = 0;\n        var pointsArr = w.globals.pointsArray;\n        var hoverSize = ttCtx.tooltipUtil.getHoverMarkerSize(capturedSeries);\n        var serType = w.config.series[capturedSeries].type;\n\n        if (serType && (serType === 'column' || serType === 'candlestick' || serType === 'boxPlot')) {\n          // fix error mentioned in #811\n          return;\n        }\n\n        cx = pointsArr[capturedSeries][j][0];\n        cy = pointsArr[capturedSeries][j][1] ? pointsArr[capturedSeries][j][1] : 0;\n        var point = w.globals.dom.baseEl.querySelector(\".apexcharts-series[data\\\\:realIndex='\".concat(capturedSeries, \"'] .apexcharts-series-markers circle\"));\n\n        if (point && cy < w.globals.gridHeight && cy > 0) {\n          point.setAttribute('r', hoverSize);\n          point.setAttribute('cx', cx);\n          point.setAttribute('cy', cy);\n        } // point.style.opacity = w.config.markers.hover.opacity\n\n\n        this.moveXCrosshairs(cx);\n\n        if (!ttCtx.fixedTooltip) {\n          this.moveTooltip(cx, cy, hoverSize);\n        }\n      } // This function is used when you need to show markers/points only on hover -\n      // SAME X VALUES in multiple series\n\n    }, {\n      key: \"moveDynamicPointsOnHover\",\n      value: function moveDynamicPointsOnHover(j) {\n        var ttCtx = this.ttCtx;\n        var w = ttCtx.w;\n        var cx = 0;\n        var cy = 0;\n        var activeSeries = 0;\n        var pointsArr = w.globals.pointsArray;\n        var series = new Series(this.ctx);\n        activeSeries = series.getActiveConfigSeriesIndex('asc', ['line', 'area', 'scatter', 'bubble']);\n        var hoverSize = ttCtx.tooltipUtil.getHoverMarkerSize(activeSeries);\n\n        if (pointsArr[activeSeries]) {\n          cx = pointsArr[activeSeries][j][0];\n          cy = pointsArr[activeSeries][j][1];\n        }\n\n        var points = ttCtx.tooltipUtil.getAllMarkers();\n\n        if (points !== null) {\n          for (var p = 0; p < w.globals.series.length; p++) {\n            var pointArr = pointsArr[p];\n\n            if (w.globals.comboCharts) {\n              // in a combo chart, if column charts are present, markers will not match with the number of series, hence this patch to push a null value in points array\n              if (typeof pointArr === 'undefined') {\n                // nodelist to array\n                points.splice(p, 0, null);\n              }\n            }\n\n            if (pointArr && pointArr.length) {\n              var pcy = pointsArr[p][j][1];\n              var pcy2 = void 0;\n              points[p].setAttribute('cx', cx);\n\n              if (w.config.chart.type === 'rangeArea' && !w.globals.comboCharts) {\n                var rangeStartIndex = j + w.globals.series[p].length;\n                pcy2 = pointsArr[p][rangeStartIndex][1];\n                var pcyDiff = Math.abs(pcy - pcy2) / 2;\n                pcy = pcy - pcyDiff;\n              }\n\n              if (pcy !== null && !isNaN(pcy) && pcy < w.globals.gridHeight + hoverSize && pcy + hoverSize > 0) {\n                points[p] && points[p].setAttribute('r', hoverSize);\n                points[p] && points[p].setAttribute('cy', pcy);\n              } else {\n                points[p] && points[p].setAttribute('r', 0);\n              }\n            }\n          }\n        }\n\n        this.moveXCrosshairs(cx);\n\n        if (!ttCtx.fixedTooltip) {\n          var tcy = cy || w.globals.gridHeight;\n          this.moveTooltip(cx, tcy, hoverSize);\n        }\n      }\n    }, {\n      key: \"moveStickyTooltipOverBars\",\n      value: function moveStickyTooltipOverBars(j) {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n        var barLen = w.globals.columnSeries ? w.globals.columnSeries.length : w.globals.series.length;\n        var i = barLen >= 2 && barLen % 2 === 0 ? Math.floor(barLen / 2) : Math.floor(barLen / 2) + 1;\n\n        if (w.globals.isBarHorizontal) {\n          var series = new Series(this.ctx);\n          i = series.getActiveConfigSeriesIndex('desc') + 1;\n        }\n\n        var jBar = w.globals.dom.baseEl.querySelector(\".apexcharts-bar-series .apexcharts-series[rel='\".concat(i, \"'] path[j='\").concat(j, \"'], .apexcharts-candlestick-series .apexcharts-series[rel='\").concat(i, \"'] path[j='\").concat(j, \"'], .apexcharts-boxPlot-series .apexcharts-series[rel='\").concat(i, \"'] path[j='\").concat(j, \"'], .apexcharts-rangebar-series .apexcharts-series[rel='\").concat(i, \"'] path[j='\").concat(j, \"']\"));\n        var bcx = jBar ? parseFloat(jBar.getAttribute('cx')) : 0;\n        var bcy = jBar ? parseFloat(jBar.getAttribute('cy')) : 0;\n        var bw = jBar ? parseFloat(jBar.getAttribute('barWidth')) : 0;\n        var elGrid = ttCtx.getElGrid();\n        var seriesBound = elGrid.getBoundingClientRect();\n        var isBoxOrCandle = jBar.classList.contains('apexcharts-candlestick-area') || jBar.classList.contains('apexcharts-boxPlot-area');\n\n        if (w.globals.isXNumeric) {\n          if (jBar && !isBoxOrCandle) {\n            bcx = bcx - (barLen % 2 !== 0 ? bw / 2 : 0);\n          }\n\n          if (jBar && // fixes apexcharts.js#2354\n          isBoxOrCandle && w.globals.comboCharts) {\n            bcx = bcx - bw / 2;\n          }\n        } else {\n          if (!w.globals.isBarHorizontal) {\n            bcx = ttCtx.xAxisTicksPositions[j - 1] + ttCtx.dataPointsDividedWidth / 2;\n\n            if (isNaN(bcx)) {\n              bcx = ttCtx.xAxisTicksPositions[j] - ttCtx.dataPointsDividedWidth / 2;\n            }\n          }\n        }\n\n        if (!w.globals.isBarHorizontal) {\n          if (w.config.tooltip.followCursor) {\n            bcy = ttCtx.e.clientY - seriesBound.top - ttCtx.tooltipRect.ttHeight / 2;\n          } else {\n            if (bcy + ttCtx.tooltipRect.ttHeight + 15 > w.globals.gridHeight) {\n              bcy = w.globals.gridHeight;\n            }\n          }\n        } else {\n          bcy = bcy - ttCtx.tooltipRect.ttHeight;\n        }\n\n        if (!w.globals.isBarHorizontal) {\n          this.moveXCrosshairs(bcx);\n        }\n\n        if (!ttCtx.fixedTooltip) {\n          var tcy = bcy || w.globals.gridHeight;\n          this.moveTooltip(bcx, tcy);\n        }\n      }\n    }]);\n\n    return Position;\n  }();\n\n  /**\n   * ApexCharts Tooltip.Marker Class to draw texts on the tooltip.\n   * This file deals with the markers that appear near tooltip in line/area charts.\n   * These markers helps the user to associate the data-points and the values\n   * that are shown in the tooltip\n   *\n   * @module Tooltip.Marker\n   **/\n\n  var Marker = /*#__PURE__*/function () {\n    function Marker(tooltipContext) {\n      _classCallCheck(this, Marker);\n\n      this.w = tooltipContext.w;\n      this.ttCtx = tooltipContext;\n      this.ctx = tooltipContext.ctx;\n      this.tooltipPosition = new Position(tooltipContext);\n    }\n\n    _createClass(Marker, [{\n      key: \"drawDynamicPoints\",\n      value: function drawDynamicPoints() {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var marker = new Markers(this.ctx);\n        var elsSeries = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series');\n        elsSeries = _toConsumableArray(elsSeries);\n\n        if (w.config.chart.stacked) {\n          elsSeries.sort(function (a, b) {\n            return parseFloat(a.getAttribute('data:realIndex')) - parseFloat(b.getAttribute('data:realIndex'));\n          });\n        }\n\n        for (var i = 0; i < elsSeries.length; i++) {\n          var pointsMain = elsSeries[i].querySelector(\".apexcharts-series-markers-wrap\");\n\n          if (pointsMain !== null) {\n            // it can be null as we have tooltips in donut/bar charts\n            var point = void 0;\n            var PointClasses = \"apexcharts-marker w\".concat((Math.random() + 1).toString(36).substring(4));\n\n            if ((w.config.chart.type === 'line' || w.config.chart.type === 'area') && !w.globals.comboCharts && !w.config.tooltip.intersect) {\n              PointClasses += ' no-pointer-events';\n            }\n\n            var elPointOptions = marker.getMarkerConfig({\n              cssClass: PointClasses,\n              seriesIndex: Number(pointsMain.getAttribute('data:realIndex')) // fixes apexcharts/apexcharts.js #1427\n\n            });\n            point = graphics.drawMarker(0, 0, elPointOptions);\n            point.node.setAttribute('default-marker-size', 0);\n            var elPointsG = document.createElementNS(w.globals.SVGNS, 'g');\n            elPointsG.classList.add('apexcharts-series-markers');\n            elPointsG.appendChild(point.node);\n            pointsMain.appendChild(elPointsG);\n          }\n        }\n      }\n    }, {\n      key: \"enlargeCurrentPoint\",\n      value: function enlargeCurrentPoint(rel, point) {\n        var x = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n        var y = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;\n        var w = this.w;\n\n        if (w.config.chart.type !== 'bubble') {\n          this.newPointSize(rel, point);\n        }\n\n        var cx = point.getAttribute('cx');\n        var cy = point.getAttribute('cy');\n\n        if (x !== null && y !== null) {\n          cx = x;\n          cy = y;\n        }\n\n        this.tooltipPosition.moveXCrosshairs(cx);\n\n        if (!this.fixedTooltip) {\n          if (w.config.chart.type === 'radar') {\n            var elGrid = this.ttCtx.getElGrid();\n            var seriesBound = elGrid.getBoundingClientRect();\n            cx = this.ttCtx.e.clientX - seriesBound.left;\n          }\n\n          this.tooltipPosition.moveTooltip(cx, cy, w.config.markers.hover.size);\n        }\n      }\n    }, {\n      key: \"enlargePoints\",\n      value: function enlargePoints(j) {\n        var w = this.w;\n        var me = this;\n        var ttCtx = this.ttCtx;\n        var col = j;\n        var points = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker');\n        var newSize = w.config.markers.hover.size;\n\n        for (var p = 0; p < points.length; p++) {\n          var rel = points[p].getAttribute('rel');\n          var index = points[p].getAttribute('index');\n\n          if (newSize === undefined) {\n            newSize = w.globals.markers.size[index] + w.config.markers.hover.sizeOffset;\n          }\n\n          if (col === parseInt(rel, 10)) {\n            me.newPointSize(col, points[p]);\n            var cx = points[p].getAttribute('cx');\n            var cy = points[p].getAttribute('cy');\n            me.tooltipPosition.moveXCrosshairs(cx);\n\n            if (!ttCtx.fixedTooltip) {\n              me.tooltipPosition.moveTooltip(cx, cy, newSize);\n            }\n          } else {\n            me.oldPointSize(points[p]);\n          }\n        }\n      }\n    }, {\n      key: \"newPointSize\",\n      value: function newPointSize(rel, point) {\n        var w = this.w;\n        var newSize = w.config.markers.hover.size;\n        var elPoint = rel === 0 ? point.parentNode.firstChild : point.parentNode.lastChild;\n\n        if (elPoint.getAttribute('default-marker-size') !== '0') {\n          var index = parseInt(elPoint.getAttribute('index'), 10);\n\n          if (newSize === undefined) {\n            newSize = w.globals.markers.size[index] + w.config.markers.hover.sizeOffset;\n          }\n\n          if (newSize < 0) newSize = 0;\n          elPoint.setAttribute('r', newSize);\n        }\n      }\n    }, {\n      key: \"oldPointSize\",\n      value: function oldPointSize(point) {\n        var size = parseFloat(point.getAttribute('default-marker-size'));\n        point.setAttribute('r', size);\n      }\n    }, {\n      key: \"resetPointsSize\",\n      value: function resetPointsSize() {\n        var w = this.w;\n        var points = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker');\n\n        for (var p = 0; p < points.length; p++) {\n          var size = parseFloat(points[p].getAttribute('default-marker-size'));\n\n          if (Utils$1.isNumber(size) && size >= 0) {\n            points[p].setAttribute('r', size);\n          } else {\n            points[p].setAttribute('r', 0);\n          }\n        }\n      }\n    }]);\n\n    return Marker;\n  }();\n\n  /**\n   * ApexCharts Tooltip.Intersect Class.\n   * This file deals with functions related to intersecting tooltips\n   * (tooltips that appear when user hovers directly over a data-point whether)\n   *\n   * @module Tooltip.Intersect\n   **/\n\n  var Intersect = /*#__PURE__*/function () {\n    function Intersect(tooltipContext) {\n      _classCallCheck(this, Intersect);\n\n      this.w = tooltipContext.w;\n      this.ttCtx = tooltipContext;\n    } // a helper function to get an element's attribute value\n\n\n    _createClass(Intersect, [{\n      key: \"getAttr\",\n      value: function getAttr(e, attr) {\n        return parseFloat(e.target.getAttribute(attr));\n      } // handle tooltip for heatmaps and treemaps\n\n    }, {\n      key: \"handleHeatTreeTooltip\",\n      value: function handleHeatTreeTooltip(_ref) {\n        var e = _ref.e,\n            opt = _ref.opt,\n            x = _ref.x,\n            y = _ref.y,\n            type = _ref.type;\n        var ttCtx = this.ttCtx;\n        var w = this.w;\n\n        if (e.target.classList.contains(\"apexcharts-\".concat(type, \"-rect\"))) {\n          var i = this.getAttr(e, 'i');\n          var j = this.getAttr(e, 'j');\n          var cx = this.getAttr(e, 'cx');\n          var cy = this.getAttr(e, 'cy');\n          var width = this.getAttr(e, 'width');\n          var height = this.getAttr(e, 'height');\n          ttCtx.tooltipLabels.drawSeriesTexts({\n            ttItems: opt.ttItems,\n            i: i,\n            j: j,\n            shared: false,\n            e: e\n          });\n          w.globals.capturedSeriesIndex = i;\n          w.globals.capturedDataPointIndex = j;\n          x = cx + ttCtx.tooltipRect.ttWidth / 2 + width;\n          y = cy + ttCtx.tooltipRect.ttHeight / 2 - height / 2;\n          ttCtx.tooltipPosition.moveXCrosshairs(cx + width / 2);\n\n          if (x > w.globals.gridWidth / 2) {\n            x = cx - ttCtx.tooltipRect.ttWidth / 2 + width;\n          }\n\n          if (ttCtx.w.config.tooltip.followCursor) {\n            var seriesBound = w.globals.dom.elWrap.getBoundingClientRect();\n            x = w.globals.clientX - seriesBound.left - (x > w.globals.gridWidth / 2 ? ttCtx.tooltipRect.ttWidth : 0);\n            y = w.globals.clientY - seriesBound.top - (y > w.globals.gridHeight / 2 ? ttCtx.tooltipRect.ttHeight : 0);\n          }\n        }\n\n        return {\n          x: x,\n          y: y\n        };\n      }\n      /**\n       * handle tooltips for line/area/scatter charts where tooltip.intersect is true\n       * when user hovers over the marker directly, this function is executed\n       */\n\n    }, {\n      key: \"handleMarkerTooltip\",\n      value: function handleMarkerTooltip(_ref2) {\n        var e = _ref2.e,\n            opt = _ref2.opt,\n            x = _ref2.x,\n            y = _ref2.y;\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n        var i;\n        var j;\n\n        if (e.target.classList.contains('apexcharts-marker')) {\n          var cx = parseInt(opt.paths.getAttribute('cx'), 10);\n          var cy = parseInt(opt.paths.getAttribute('cy'), 10);\n          var val = parseFloat(opt.paths.getAttribute('val'));\n          j = parseInt(opt.paths.getAttribute('rel'), 10);\n          i = parseInt(opt.paths.parentNode.parentNode.parentNode.getAttribute('rel'), 10) - 1;\n\n          if (ttCtx.intersect) {\n            var el = Utils$1.findAncestor(opt.paths, 'apexcharts-series');\n\n            if (el) {\n              i = parseInt(el.getAttribute('data:realIndex'), 10);\n            }\n          }\n\n          ttCtx.tooltipLabels.drawSeriesTexts({\n            ttItems: opt.ttItems,\n            i: i,\n            j: j,\n            shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,\n            e: e\n          });\n\n          if (e.type === 'mouseup') {\n            ttCtx.markerClick(e, i, j);\n          }\n\n          w.globals.capturedSeriesIndex = i;\n          w.globals.capturedDataPointIndex = j;\n          x = cx;\n          y = cy + w.globals.translateY - ttCtx.tooltipRect.ttHeight * 1.4;\n\n          if (ttCtx.w.config.tooltip.followCursor) {\n            var elGrid = ttCtx.getElGrid();\n            var seriesBound = elGrid.getBoundingClientRect();\n            y = ttCtx.e.clientY + w.globals.translateY - seriesBound.top;\n          }\n\n          if (val < 0) {\n            y = cy;\n          }\n\n          ttCtx.marker.enlargeCurrentPoint(j, opt.paths, x, y);\n        }\n\n        return {\n          x: x,\n          y: y\n        };\n      }\n      /**\n       * handle tooltips for bar/column charts\n       */\n\n    }, {\n      key: \"handleBarTooltip\",\n      value: function handleBarTooltip(_ref3) {\n        var e = _ref3.e,\n            opt = _ref3.opt;\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n        var tooltipEl = ttCtx.getElTooltip();\n        var bx = 0;\n        var x = 0;\n        var y = 0;\n        var i = 0;\n        var strokeWidth;\n        var barXY = this.getBarTooltipXY({\n          e: e,\n          opt: opt\n        });\n        i = barXY.i;\n        var barHeight = barXY.barHeight;\n        var j = barXY.j;\n        w.globals.capturedSeriesIndex = i;\n        w.globals.capturedDataPointIndex = j;\n\n        if (w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars() || !w.config.tooltip.shared) {\n          x = barXY.x;\n          y = barXY.y;\n          strokeWidth = Array.isArray(w.config.stroke.width) ? w.config.stroke.width[i] : w.config.stroke.width;\n          bx = x;\n        } else {\n          if (!w.globals.comboCharts && !w.config.tooltip.shared) {\n            // todo: re-check this condition as it's always 0\n            bx = bx / 2;\n          }\n        } // y is NaN, make it touch the bottom of grid area\n\n\n        if (isNaN(y)) {\n          y = w.globals.svgHeight - ttCtx.tooltipRect.ttHeight;\n        }\n\n        var seriesIndex = parseInt(opt.paths.parentNode.getAttribute('data:realIndex'), 10);\n        var isReversed = w.globals.isMultipleYAxis ? w.config.yaxis[seriesIndex] && w.config.yaxis[seriesIndex].reversed : w.config.yaxis[0].reversed;\n\n        if (x + ttCtx.tooltipRect.ttWidth > w.globals.gridWidth && !isReversed) {\n          x = x - ttCtx.tooltipRect.ttWidth;\n        } else if (x < 0) {\n          x = 0;\n        }\n\n        if (ttCtx.w.config.tooltip.followCursor) {\n          var elGrid = ttCtx.getElGrid();\n          var seriesBound = elGrid.getBoundingClientRect();\n          y = ttCtx.e.clientY - seriesBound.top;\n        } // if tooltip is still null, querySelector\n\n\n        if (ttCtx.tooltip === null) {\n          ttCtx.tooltip = w.globals.dom.baseEl.querySelector('.apexcharts-tooltip');\n        }\n\n        if (!w.config.tooltip.shared) {\n          if (w.globals.comboBarCount > 0) {\n            ttCtx.tooltipPosition.moveXCrosshairs(bx + strokeWidth / 2);\n          } else {\n            ttCtx.tooltipPosition.moveXCrosshairs(bx);\n          }\n        } // move tooltip here\n\n\n        if (!ttCtx.fixedTooltip && (!w.config.tooltip.shared || w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars())) {\n          if (isReversed) {\n            x = x - ttCtx.tooltipRect.ttWidth;\n\n            if (x < 0) {\n              x = 0;\n            }\n          }\n\n          if (isReversed && !(w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars())) {\n            y = y + barHeight - (w.globals.series[i][j] < 0 ? barHeight : 0) * 2;\n          }\n\n          y = y + w.globals.translateY - ttCtx.tooltipRect.ttHeight / 2;\n          tooltipEl.style.left = x + w.globals.translateX + 'px';\n          tooltipEl.style.top = y + 'px';\n        }\n      }\n    }, {\n      key: \"getBarTooltipXY\",\n      value: function getBarTooltipXY(_ref4) {\n        var e = _ref4.e,\n            opt = _ref4.opt;\n        var w = this.w;\n        var j = null;\n        var ttCtx = this.ttCtx;\n        var i = 0;\n        var x = 0;\n        var y = 0;\n        var barWidth = 0;\n        var barHeight = 0;\n        var cl = e.target.classList;\n\n        if (cl.contains('apexcharts-bar-area') || cl.contains('apexcharts-candlestick-area') || cl.contains('apexcharts-boxPlot-area') || cl.contains('apexcharts-rangebar-area')) {\n          var bar = e.target;\n          var barRect = bar.getBoundingClientRect();\n          var seriesBound = opt.elGrid.getBoundingClientRect();\n          var bh = barRect.height;\n          barHeight = barRect.height;\n          var bw = barRect.width;\n          var cx = parseInt(bar.getAttribute('cx'), 10);\n          var cy = parseInt(bar.getAttribute('cy'), 10);\n          barWidth = parseFloat(bar.getAttribute('barWidth'));\n          var clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;\n          j = parseInt(bar.getAttribute('j'), 10);\n          i = parseInt(bar.parentNode.getAttribute('rel'), 10) - 1;\n          var y1 = bar.getAttribute('data-range-y1');\n          var y2 = bar.getAttribute('data-range-y2');\n\n          if (w.globals.comboCharts) {\n            i = parseInt(bar.parentNode.getAttribute('data:realIndex'), 10);\n          } // if (w.config.tooltip.shared) {\n          // this check not needed  at the moment\n          //   const yDivisor = w.globals.gridHeight / (w.globals.series.length)\n          //   const hoverY = ttCtx.clientY - ttCtx.seriesBound.top\n          //   j = Math.ceil(hoverY / yDivisor)\n          // }\n\n\n          ttCtx.tooltipLabels.drawSeriesTexts({\n            ttItems: opt.ttItems,\n            i: i,\n            j: j,\n            y1: y1 ? parseInt(y1, 10) : null,\n            y2: y2 ? parseInt(y2, 10) : null,\n            shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,\n            e: e\n          });\n\n          if (w.config.tooltip.followCursor) {\n            if (w.globals.isBarHorizontal) {\n              x = clientX - seriesBound.left + 15;\n              y = cy - ttCtx.dataPointsDividedHeight + bh / 2 - ttCtx.tooltipRect.ttHeight / 2;\n            } else {\n              if (w.globals.isXNumeric) {\n                x = cx - bw / 2;\n              } else {\n                x = cx - ttCtx.dataPointsDividedWidth + bw / 2;\n              }\n\n              y = e.clientY - seriesBound.top - ttCtx.tooltipRect.ttHeight / 2 - 15;\n            }\n          } else {\n            if (w.globals.isBarHorizontal) {\n              x = cx;\n\n              if (x < ttCtx.xyRatios.baseLineInvertedY) {\n                x = cx - ttCtx.tooltipRect.ttWidth;\n              }\n\n              y = cy - ttCtx.dataPointsDividedHeight + bh / 2 - ttCtx.tooltipRect.ttHeight / 2;\n            } else {\n              // if columns\n              if (w.globals.isXNumeric) {\n                x = cx - bw / 2;\n              } else {\n                x = cx - ttCtx.dataPointsDividedWidth + bw / 2;\n              }\n\n              y = cy; // - ttCtx.tooltipRect.ttHeight / 2 + 10\n            }\n          }\n        }\n\n        return {\n          x: x,\n          y: y,\n          barHeight: barHeight,\n          barWidth: barWidth,\n          i: i,\n          j: j\n        };\n      }\n    }]);\n\n    return Intersect;\n  }();\n\n  /**\n   * ApexCharts Tooltip.AxesTooltip Class.\n   * This file deals with the x-axis and y-axis tooltips.\n   *\n   * @module Tooltip.AxesTooltip\n   **/\n  var AxesTooltip = /*#__PURE__*/function () {\n    function AxesTooltip(tooltipContext) {\n      _classCallCheck(this, AxesTooltip);\n\n      this.w = tooltipContext.w;\n      this.ttCtx = tooltipContext;\n    }\n    /**\n     * This method adds the secondary tooltip which appears below x axis\n     * @memberof Tooltip\n     **/\n\n\n    _createClass(AxesTooltip, [{\n      key: \"drawXaxisTooltip\",\n      value: function drawXaxisTooltip() {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n        var isBottom = w.config.xaxis.position === 'bottom';\n        ttCtx.xaxisOffY = isBottom ? w.globals.gridHeight + 1 : -w.globals.xAxisHeight - w.config.xaxis.axisTicks.height + 3;\n        var tooltipCssClass = isBottom ? 'apexcharts-xaxistooltip apexcharts-xaxistooltip-bottom' : 'apexcharts-xaxistooltip apexcharts-xaxistooltip-top';\n        var renderTo = w.globals.dom.elWrap;\n\n        if (ttCtx.isXAxisTooltipEnabled) {\n          var xaxisTooltip = w.globals.dom.baseEl.querySelector('.apexcharts-xaxistooltip');\n\n          if (xaxisTooltip === null) {\n            ttCtx.xaxisTooltip = document.createElement('div');\n            ttCtx.xaxisTooltip.setAttribute('class', tooltipCssClass + ' apexcharts-theme-' + w.config.tooltip.theme);\n            renderTo.appendChild(ttCtx.xaxisTooltip);\n            ttCtx.xaxisTooltipText = document.createElement('div');\n            ttCtx.xaxisTooltipText.classList.add('apexcharts-xaxistooltip-text');\n            ttCtx.xaxisTooltipText.style.fontFamily = w.config.xaxis.tooltip.style.fontFamily || w.config.chart.fontFamily;\n            ttCtx.xaxisTooltipText.style.fontSize = w.config.xaxis.tooltip.style.fontSize;\n            ttCtx.xaxisTooltip.appendChild(ttCtx.xaxisTooltipText);\n          }\n        }\n      }\n      /**\n       * This method adds the secondary tooltip which appears below x axis\n       * @memberof Tooltip\n       **/\n\n    }, {\n      key: \"drawYaxisTooltip\",\n      value: function drawYaxisTooltip() {\n        var w = this.w;\n        var ttCtx = this.ttCtx;\n\n        var _loop = function _loop(i) {\n          var isRight = w.config.yaxis[i].opposite || w.config.yaxis[i].crosshairs.opposite;\n          ttCtx.yaxisOffX = isRight ? w.globals.gridWidth + 1 : 1;\n          var tooltipCssClass = isRight ? \"apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i, \" apexcharts-yaxistooltip-right\") : \"apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i, \" apexcharts-yaxistooltip-left\");\n          w.globals.yAxisSameScaleIndices.map(function (samescales, ssi) {\n            samescales.map(function (s, si) {\n              if (si === i) {\n                tooltipCssClass += w.config.yaxis[si].show ? \" \" : \" apexcharts-yaxistooltip-hidden\";\n              }\n            });\n          });\n          var renderTo = w.globals.dom.elWrap;\n          var yaxisTooltip = w.globals.dom.baseEl.querySelector(\".apexcharts-yaxistooltip apexcharts-yaxistooltip-\".concat(i));\n\n          if (yaxisTooltip === null) {\n            ttCtx.yaxisTooltip = document.createElement('div');\n            ttCtx.yaxisTooltip.setAttribute('class', tooltipCssClass + ' apexcharts-theme-' + w.config.tooltip.theme);\n            renderTo.appendChild(ttCtx.yaxisTooltip);\n            if (i === 0) ttCtx.yaxisTooltipText = [];\n            ttCtx.yaxisTooltipText[i] = document.createElement('div');\n            ttCtx.yaxisTooltipText[i].classList.add('apexcharts-yaxistooltip-text');\n            ttCtx.yaxisTooltip.appendChild(ttCtx.yaxisTooltipText[i]);\n          }\n        };\n\n        for (var i = 0; i < w.config.yaxis.length; i++) {\n          _loop(i);\n        }\n      }\n      /**\n       * @memberof Tooltip\n       **/\n\n    }, {\n      key: \"setXCrosshairWidth\",\n      value: function setXCrosshairWidth() {\n        var w = this.w;\n        var ttCtx = this.ttCtx; // set xcrosshairs width\n\n        var xcrosshairs = ttCtx.getElXCrosshairs();\n        ttCtx.xcrosshairsWidth = parseInt(w.config.xaxis.crosshairs.width, 10);\n\n        if (!w.globals.comboCharts) {\n          if (w.config.xaxis.crosshairs.width === 'tickWidth') {\n            var count = w.globals.labels.length;\n            ttCtx.xcrosshairsWidth = w.globals.gridWidth / count;\n          } else if (w.config.xaxis.crosshairs.width === 'barWidth') {\n            var bar = w.globals.dom.baseEl.querySelector('.apexcharts-bar-area');\n\n            if (bar !== null) {\n              var barWidth = parseFloat(bar.getAttribute('barWidth'));\n              ttCtx.xcrosshairsWidth = barWidth;\n            } else {\n              ttCtx.xcrosshairsWidth = 1;\n            }\n          }\n        } else {\n          var _bar = w.globals.dom.baseEl.querySelector('.apexcharts-bar-area');\n\n          if (_bar !== null && w.config.xaxis.crosshairs.width === 'barWidth') {\n            var _barWidth = parseFloat(_bar.getAttribute('barWidth'));\n\n            ttCtx.xcrosshairsWidth = _barWidth;\n          } else {\n            if (w.config.xaxis.crosshairs.width === 'tickWidth') {\n              var _count = w.globals.labels.length;\n              ttCtx.xcrosshairsWidth = w.globals.gridWidth / _count;\n            }\n          }\n        }\n\n        if (w.globals.isBarHorizontal) {\n          ttCtx.xcrosshairsWidth = 0;\n        }\n\n        if (xcrosshairs !== null && ttCtx.xcrosshairsWidth > 0) {\n          xcrosshairs.setAttribute('width', ttCtx.xcrosshairsWidth);\n        }\n      }\n    }, {\n      key: \"handleYCrosshair\",\n      value: function handleYCrosshair() {\n        var w = this.w;\n        var ttCtx = this.ttCtx; // set ycrosshairs height\n\n        ttCtx.ycrosshairs = w.globals.dom.baseEl.querySelector('.apexcharts-ycrosshairs');\n        ttCtx.ycrosshairsHidden = w.globals.dom.baseEl.querySelector('.apexcharts-ycrosshairs-hidden');\n      }\n    }, {\n      key: \"drawYaxisTooltipText\",\n      value: function drawYaxisTooltipText(index, clientY, xyRatios) {\n        var ttCtx = this.ttCtx;\n        var w = this.w;\n        var lbFormatter = w.globals.yLabelFormatters[index];\n\n        if (ttCtx.yaxisTooltips[index]) {\n          var elGrid = ttCtx.getElGrid();\n          var seriesBound = elGrid.getBoundingClientRect();\n          var hoverY = (clientY - seriesBound.top) * xyRatios.yRatio[index];\n          var height = w.globals.maxYArr[index] - w.globals.minYArr[index];\n          var val = w.globals.minYArr[index] + (height - hoverY);\n          ttCtx.tooltipPosition.moveYCrosshairs(clientY - seriesBound.top);\n          ttCtx.yaxisTooltipText[index].innerHTML = lbFormatter(val);\n          ttCtx.tooltipPosition.moveYAxisTooltip(index);\n        }\n      }\n    }]);\n\n    return AxesTooltip;\n  }();\n\n  /**\n   * ApexCharts Core Tooltip Class to handle the tooltip generation.\n   *\n   * @module Tooltip\n   **/\n\n  var Tooltip = /*#__PURE__*/function () {\n    function Tooltip(ctx) {\n      _classCallCheck(this, Tooltip);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      var w = this.w;\n      this.tConfig = w.config.tooltip;\n      this.tooltipUtil = new Utils(this);\n      this.tooltipLabels = new Labels(this);\n      this.tooltipPosition = new Position(this);\n      this.marker = new Marker(this);\n      this.intersect = new Intersect(this);\n      this.axesTooltip = new AxesTooltip(this);\n      this.showOnIntersect = this.tConfig.intersect;\n      this.showTooltipTitle = this.tConfig.x.show;\n      this.fixedTooltip = this.tConfig.fixed.enabled;\n      this.xaxisTooltip = null;\n      this.yaxisTTEls = null;\n      this.isBarShared = !w.globals.isBarHorizontal && this.tConfig.shared;\n      this.lastHoverTime = Date.now();\n    }\n\n    _createClass(Tooltip, [{\n      key: \"getElTooltip\",\n      value: function getElTooltip(ctx) {\n        if (!ctx) ctx = this;\n        if (!ctx.w.globals.dom.baseEl) return null;\n        return ctx.w.globals.dom.baseEl.querySelector('.apexcharts-tooltip');\n      }\n    }, {\n      key: \"getElXCrosshairs\",\n      value: function getElXCrosshairs() {\n        return this.w.globals.dom.baseEl.querySelector('.apexcharts-xcrosshairs');\n      }\n    }, {\n      key: \"getElGrid\",\n      value: function getElGrid() {\n        return this.w.globals.dom.baseEl.querySelector('.apexcharts-grid');\n      }\n    }, {\n      key: \"drawTooltip\",\n      value: function drawTooltip(xyRatios) {\n        var w = this.w;\n        this.xyRatios = xyRatios;\n        this.isXAxisTooltipEnabled = w.config.xaxis.tooltip.enabled && w.globals.axisCharts;\n        this.yaxisTooltips = w.config.yaxis.map(function (y, i) {\n          return y.show && y.tooltip.enabled && w.globals.axisCharts ? true : false;\n        });\n        this.allTooltipSeriesGroups = [];\n\n        if (!w.globals.axisCharts) {\n          this.showTooltipTitle = false;\n        }\n\n        var tooltipEl = document.createElement('div');\n        tooltipEl.classList.add('apexcharts-tooltip');\n\n        if (w.config.tooltip.cssClass) {\n          tooltipEl.classList.add(w.config.tooltip.cssClass);\n        }\n\n        tooltipEl.classList.add(\"apexcharts-theme-\".concat(this.tConfig.theme));\n        w.globals.dom.elWrap.appendChild(tooltipEl);\n\n        if (w.globals.axisCharts) {\n          this.axesTooltip.drawXaxisTooltip();\n          this.axesTooltip.drawYaxisTooltip();\n          this.axesTooltip.setXCrosshairWidth();\n          this.axesTooltip.handleYCrosshair();\n          var xAxis = new XAxis(this.ctx);\n          this.xAxisTicksPositions = xAxis.getXAxisTicksPositions();\n        } // we forcefully set intersect true for these conditions\n\n\n        if ((w.globals.comboCharts || this.tConfig.intersect || w.config.chart.type === 'rangeBar') && !this.tConfig.shared) {\n          this.showOnIntersect = true;\n        }\n\n        if (w.config.markers.size === 0 || w.globals.markers.largestSize === 0) {\n          // when user don't want to show points all the time, but only on when hovering on series\n          this.marker.drawDynamicPoints(this);\n        } // no visible series, exit\n\n\n        if (w.globals.collapsedSeries.length === w.globals.series.length) return;\n        this.dataPointsDividedHeight = w.globals.gridHeight / w.globals.dataPoints;\n        this.dataPointsDividedWidth = w.globals.gridWidth / w.globals.dataPoints;\n\n        if (this.showTooltipTitle) {\n          this.tooltipTitle = document.createElement('div');\n          this.tooltipTitle.classList.add('apexcharts-tooltip-title');\n          this.tooltipTitle.style.fontFamily = this.tConfig.style.fontFamily || w.config.chart.fontFamily;\n          this.tooltipTitle.style.fontSize = this.tConfig.style.fontSize;\n          tooltipEl.appendChild(this.tooltipTitle);\n        }\n\n        var ttItemsCnt = w.globals.series.length; // whether shared or not, default is shared\n\n        if ((w.globals.xyCharts || w.globals.comboCharts) && this.tConfig.shared) {\n          if (!this.showOnIntersect) {\n            ttItemsCnt = w.globals.series.length;\n          } else {\n            ttItemsCnt = 1;\n          }\n        }\n\n        this.legendLabels = w.globals.dom.baseEl.querySelectorAll('.apexcharts-legend-text');\n        this.ttItems = this.createTTElements(ttItemsCnt);\n        this.addSVGEvents();\n      }\n    }, {\n      key: \"createTTElements\",\n      value: function createTTElements(ttItemsCnt) {\n        var _this = this;\n\n        var w = this.w;\n        var ttItems = [];\n        var tooltipEl = this.getElTooltip();\n\n        var _loop = function _loop(i) {\n          var gTxt = document.createElement('div');\n          gTxt.classList.add('apexcharts-tooltip-series-group');\n          gTxt.style.order = w.config.tooltip.inverseOrder ? ttItemsCnt - i : i + 1;\n\n          if (_this.tConfig.shared && _this.tConfig.enabledOnSeries && Array.isArray(_this.tConfig.enabledOnSeries)) {\n            if (_this.tConfig.enabledOnSeries.indexOf(i) < 0) {\n              gTxt.classList.add('apexcharts-tooltip-series-group-hidden');\n            }\n          }\n\n          var point = document.createElement('span');\n          point.classList.add('apexcharts-tooltip-marker');\n          point.style.backgroundColor = w.globals.colors[i];\n          gTxt.appendChild(point);\n          var gYZ = document.createElement('div');\n          gYZ.classList.add('apexcharts-tooltip-text');\n          gYZ.style.fontFamily = _this.tConfig.style.fontFamily || w.config.chart.fontFamily;\n          gYZ.style.fontSize = _this.tConfig.style.fontSize;\n          ['y', 'goals', 'z'].forEach(function (g) {\n            var gValText = document.createElement('div');\n            gValText.classList.add(\"apexcharts-tooltip-\".concat(g, \"-group\"));\n            var txtLabel = document.createElement('span');\n            txtLabel.classList.add(\"apexcharts-tooltip-text-\".concat(g, \"-label\"));\n            gValText.appendChild(txtLabel);\n            var txtValue = document.createElement('span');\n            txtValue.classList.add(\"apexcharts-tooltip-text-\".concat(g, \"-value\"));\n            gValText.appendChild(txtValue);\n            gYZ.appendChild(gValText);\n          });\n          gTxt.appendChild(gYZ);\n          tooltipEl.appendChild(gTxt);\n          ttItems.push(gTxt);\n        };\n\n        for (var i = 0; i < ttItemsCnt; i++) {\n          _loop(i);\n        }\n\n        return ttItems;\n      }\n    }, {\n      key: \"addSVGEvents\",\n      value: function addSVGEvents() {\n        var w = this.w;\n        var type = w.config.chart.type;\n        var tooltipEl = this.getElTooltip();\n        var commonBar = !!(type === 'bar' || type === 'candlestick' || type === 'boxPlot' || type === 'rangeBar');\n        var chartWithmarkers = type === 'area' || type === 'line' || type === 'scatter' || type === 'bubble' || type === 'radar';\n        var hoverArea = w.globals.dom.Paper.node;\n        var elGrid = this.getElGrid();\n\n        if (elGrid) {\n          this.seriesBound = elGrid.getBoundingClientRect();\n        }\n\n        var tooltipY = [];\n        var tooltipX = [];\n        var seriesHoverParams = {\n          hoverArea: hoverArea,\n          elGrid: elGrid,\n          tooltipEl: tooltipEl,\n          tooltipY: tooltipY,\n          tooltipX: tooltipX,\n          ttItems: this.ttItems\n        };\n        var points;\n\n        if (w.globals.axisCharts) {\n          if (chartWithmarkers) {\n            points = w.globals.dom.baseEl.querySelectorAll(\".apexcharts-series[data\\\\:longestSeries='true'] .apexcharts-marker\");\n          } else if (commonBar) {\n            points = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-boxPlot-area, .apexcharts-series .apexcharts-rangebar-area');\n          } else if (type === 'heatmap' || type === 'treemap') {\n            points = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series .apexcharts-heatmap, .apexcharts-series .apexcharts-treemap');\n          }\n\n          if (points && points.length) {\n            for (var p = 0; p < points.length; p++) {\n              tooltipY.push(points[p].getAttribute('cy'));\n              tooltipX.push(points[p].getAttribute('cx'));\n            }\n          }\n        }\n\n        var validSharedChartTypes = w.globals.xyCharts && !this.showOnIntersect || w.globals.comboCharts && !this.showOnIntersect || commonBar && this.tooltipUtil.hasBars() && this.tConfig.shared;\n\n        if (validSharedChartTypes) {\n          this.addPathsEventListeners([hoverArea], seriesHoverParams);\n        } else if (commonBar && !w.globals.comboCharts || chartWithmarkers && this.showOnIntersect) {\n          this.addDatapointEventsListeners(seriesHoverParams);\n        } else if (!w.globals.axisCharts || type === 'heatmap' || type === 'treemap') {\n          var seriesAll = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series');\n          this.addPathsEventListeners(seriesAll, seriesHoverParams);\n        }\n\n        if (this.showOnIntersect) {\n          var lineAreaPoints = w.globals.dom.baseEl.querySelectorAll('.apexcharts-line-series .apexcharts-marker, .apexcharts-area-series .apexcharts-marker');\n\n          if (lineAreaPoints.length > 0) {\n            // if we find any lineSeries, addEventListeners for them\n            this.addPathsEventListeners(lineAreaPoints, seriesHoverParams);\n          } // combo charts may have bars, so add event listeners here too\n\n\n          if (this.tooltipUtil.hasBars() && !this.tConfig.shared) {\n            this.addDatapointEventsListeners(seriesHoverParams);\n          }\n        }\n      }\n    }, {\n      key: \"drawFixedTooltipRect\",\n      value: function drawFixedTooltipRect() {\n        var w = this.w;\n        var tooltipEl = this.getElTooltip();\n        var tooltipRect = tooltipEl.getBoundingClientRect();\n        var ttWidth = tooltipRect.width + 10;\n        var ttHeight = tooltipRect.height + 10;\n        var x = this.tConfig.fixed.offsetX;\n        var y = this.tConfig.fixed.offsetY;\n        var fixed = this.tConfig.fixed.position.toLowerCase();\n\n        if (fixed.indexOf('right') > -1) {\n          x = x + w.globals.svgWidth - ttWidth + 10;\n        }\n\n        if (fixed.indexOf('bottom') > -1) {\n          y = y + w.globals.svgHeight - ttHeight - 10;\n        }\n\n        tooltipEl.style.left = x + 'px';\n        tooltipEl.style.top = y + 'px';\n        return {\n          x: x,\n          y: y,\n          ttWidth: ttWidth,\n          ttHeight: ttHeight\n        };\n      }\n    }, {\n      key: \"addDatapointEventsListeners\",\n      value: function addDatapointEventsListeners(seriesHoverParams) {\n        var w = this.w;\n        var points = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series-markers .apexcharts-marker, .apexcharts-bar-area, .apexcharts-candlestick-area, .apexcharts-boxPlot-area, .apexcharts-rangebar-area');\n        this.addPathsEventListeners(points, seriesHoverParams);\n      }\n    }, {\n      key: \"addPathsEventListeners\",\n      value: function addPathsEventListeners(paths, opts) {\n        var self = this;\n\n        var _loop2 = function _loop2(p) {\n          var extendedOpts = {\n            paths: paths[p],\n            tooltipEl: opts.tooltipEl,\n            tooltipY: opts.tooltipY,\n            tooltipX: opts.tooltipX,\n            elGrid: opts.elGrid,\n            hoverArea: opts.hoverArea,\n            ttItems: opts.ttItems\n          };\n          var events = ['mousemove', 'mouseup', 'touchmove', 'mouseout', 'touchend'];\n          events.map(function (ev) {\n            return paths[p].addEventListener(ev, self.onSeriesHover.bind(self, extendedOpts), {\n              capture: false,\n              passive: true\n            });\n          });\n        };\n\n        for (var p = 0; p < paths.length; p++) {\n          _loop2(p);\n        }\n      }\n      /*\n       ** Check to see if the tooltips should be updated based on a mouse / touch event\n       */\n\n    }, {\n      key: \"onSeriesHover\",\n      value: function onSeriesHover(opt, e) {\n        var _this2 = this;\n\n        // If a user is moving their mouse quickly, don't bother updating the tooltip every single frame\n        var targetDelay = 100;\n        var timeSinceLastUpdate = Date.now() - this.lastHoverTime;\n\n        if (timeSinceLastUpdate >= targetDelay) {\n          // The tooltip was last updated over 100ms ago - redraw it even if the user is still moving their\n          // mouse so they get some feedback that their moves are being registered\n          this.seriesHover(opt, e);\n        } else {\n          // The tooltip was last updated less than 100ms ago\n          // Cancel any other delayed draw, so we don't show stale data\n          clearTimeout(this.seriesHoverTimeout); // Schedule the next draw so that it happens about 100ms after the last update\n\n          this.seriesHoverTimeout = setTimeout(function () {\n            _this2.seriesHover(opt, e);\n          }, targetDelay - timeSinceLastUpdate);\n        }\n      }\n      /*\n       ** The actual series hover function\n       */\n\n    }, {\n      key: \"seriesHover\",\n      value: function seriesHover(opt, e) {\n        var _this3 = this;\n\n        this.lastHoverTime = Date.now();\n        var chartGroups = [];\n        var w = this.w; // if user has more than one charts in group, we need to sync\n\n        if (w.config.chart.group) {\n          chartGroups = this.ctx.getGroupedCharts();\n        }\n\n        if (w.globals.axisCharts && (w.globals.minX === -Infinity && w.globals.maxX === Infinity || w.globals.dataPoints === 0)) {\n          return;\n        }\n\n        if (chartGroups.length) {\n          chartGroups.forEach(function (ch) {\n            var tooltipEl = _this3.getElTooltip(ch);\n\n            var newOpts = {\n              paths: opt.paths,\n              tooltipEl: tooltipEl,\n              tooltipY: opt.tooltipY,\n              tooltipX: opt.tooltipX,\n              elGrid: opt.elGrid,\n              hoverArea: opt.hoverArea,\n              ttItems: ch.w.globals.tooltip.ttItems\n            }; // all the charts should have the same minX and maxX (same xaxis) for multiple tooltips to work correctly\n\n            if (ch.w.globals.minX === _this3.w.globals.minX && ch.w.globals.maxX === _this3.w.globals.maxX) {\n              ch.w.globals.tooltip.seriesHoverByContext({\n                chartCtx: ch,\n                ttCtx: ch.w.globals.tooltip,\n                opt: newOpts,\n                e: e\n              });\n            }\n          });\n        } else {\n          this.seriesHoverByContext({\n            chartCtx: this.ctx,\n            ttCtx: this.w.globals.tooltip,\n            opt: opt,\n            e: e\n          });\n        }\n      }\n    }, {\n      key: \"seriesHoverByContext\",\n      value: function seriesHoverByContext(_ref) {\n        var chartCtx = _ref.chartCtx,\n            ttCtx = _ref.ttCtx,\n            opt = _ref.opt,\n            e = _ref.e;\n        var w = chartCtx.w;\n        var tooltipEl = this.getElTooltip();\n        if (!tooltipEl) return; // tooltipRect is calculated on every mousemove, because the text is dynamic\n\n        ttCtx.tooltipRect = {\n          x: 0,\n          y: 0,\n          ttWidth: tooltipEl.getBoundingClientRect().width,\n          ttHeight: tooltipEl.getBoundingClientRect().height\n        };\n        ttCtx.e = e; // highlight the current hovered bars\n\n        if (ttCtx.tooltipUtil.hasBars() && !w.globals.comboCharts && !ttCtx.isBarShared) {\n          if (this.tConfig.onDatasetHover.highlightDataSeries) {\n            var series = new Series(chartCtx);\n            series.toggleSeriesOnHover(e, e.target.parentNode);\n          }\n        }\n\n        if (ttCtx.fixedTooltip) {\n          ttCtx.drawFixedTooltipRect();\n        }\n\n        if (w.globals.axisCharts) {\n          ttCtx.axisChartsTooltips({\n            e: e,\n            opt: opt,\n            tooltipRect: ttCtx.tooltipRect\n          });\n        } else {\n          // non-plot charts i.e pie/donut/circle\n          ttCtx.nonAxisChartsTooltips({\n            e: e,\n            opt: opt,\n            tooltipRect: ttCtx.tooltipRect\n          });\n        }\n      } // tooltip handling for line/area/bar/columns/scatter\n\n    }, {\n      key: \"axisChartsTooltips\",\n      value: function axisChartsTooltips(_ref2) {\n        var e = _ref2.e,\n            opt = _ref2.opt;\n        var w = this.w;\n        var x, y;\n        var seriesBound = opt.elGrid.getBoundingClientRect();\n        var clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;\n        var clientY = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY;\n        this.clientY = clientY;\n        this.clientX = clientX;\n        w.globals.capturedSeriesIndex = -1;\n        w.globals.capturedDataPointIndex = -1;\n\n        if (clientY < seriesBound.top || clientY > seriesBound.top + seriesBound.height) {\n          this.handleMouseOut(opt);\n          return;\n        }\n\n        if (Array.isArray(this.tConfig.enabledOnSeries) && !w.config.tooltip.shared) {\n          var index = parseInt(opt.paths.getAttribute('index'), 10);\n\n          if (this.tConfig.enabledOnSeries.indexOf(index) < 0) {\n            this.handleMouseOut(opt);\n            return;\n          }\n        }\n\n        var tooltipEl = this.getElTooltip();\n        var xcrosshairs = this.getElXCrosshairs();\n        var isStickyTooltip = w.globals.xyCharts || w.config.chart.type === 'bar' && !w.globals.isBarHorizontal && this.tooltipUtil.hasBars() && this.tConfig.shared || w.globals.comboCharts && this.tooltipUtil.hasBars();\n\n        if (e.type === 'mousemove' || e.type === 'touchmove' || e.type === 'mouseup') {\n          // there is no series to hover over\n          if (w.globals.collapsedSeries.length + w.globals.ancillaryCollapsedSeries.length === w.globals.series.length) {\n            return;\n          }\n\n          if (xcrosshairs !== null) {\n            xcrosshairs.classList.add('apexcharts-active');\n          }\n\n          var hasYAxisTooltip = this.yaxisTooltips.filter(function (b) {\n            return b === true;\n          });\n\n          if (this.ycrosshairs !== null && hasYAxisTooltip.length) {\n            this.ycrosshairs.classList.add('apexcharts-active');\n          }\n\n          if (isStickyTooltip && !this.showOnIntersect) {\n            this.handleStickyTooltip(e, clientX, clientY, opt);\n          } else {\n            if (w.config.chart.type === 'heatmap' || w.config.chart.type === 'treemap') {\n              var markerXY = this.intersect.handleHeatTreeTooltip({\n                e: e,\n                opt: opt,\n                x: x,\n                y: y,\n                type: w.config.chart.type\n              });\n              x = markerXY.x;\n              y = markerXY.y;\n              tooltipEl.style.left = x + 'px';\n              tooltipEl.style.top = y + 'px';\n            } else {\n              if (this.tooltipUtil.hasBars()) {\n                this.intersect.handleBarTooltip({\n                  e: e,\n                  opt: opt\n                });\n              }\n\n              if (this.tooltipUtil.hasMarkers()) {\n                // intersect - line/area/scatter/bubble\n                this.intersect.handleMarkerTooltip({\n                  e: e,\n                  opt: opt,\n                  x: x,\n                  y: y\n                });\n              }\n            }\n          }\n\n          if (this.yaxisTooltips.length) {\n            for (var yt = 0; yt < w.config.yaxis.length; yt++) {\n              this.axesTooltip.drawYaxisTooltipText(yt, clientY, this.xyRatios);\n            }\n          }\n\n          opt.tooltipEl.classList.add('apexcharts-active');\n        } else if (e.type === 'mouseout' || e.type === 'touchend') {\n          this.handleMouseOut(opt);\n        }\n      } // tooltip handling for pie/donuts\n\n    }, {\n      key: \"nonAxisChartsTooltips\",\n      value: function nonAxisChartsTooltips(_ref3) {\n        var e = _ref3.e,\n            opt = _ref3.opt,\n            tooltipRect = _ref3.tooltipRect;\n        var w = this.w;\n        var rel = opt.paths.getAttribute('rel');\n        var tooltipEl = this.getElTooltip();\n        var seriesBound = w.globals.dom.elWrap.getBoundingClientRect();\n\n        if (e.type === 'mousemove' || e.type === 'touchmove') {\n          tooltipEl.classList.add('apexcharts-active');\n          this.tooltipLabels.drawSeriesTexts({\n            ttItems: opt.ttItems,\n            i: parseInt(rel, 10) - 1,\n            shared: false\n          });\n          var x = w.globals.clientX - seriesBound.left - tooltipRect.ttWidth / 2;\n          var y = w.globals.clientY - seriesBound.top - tooltipRect.ttHeight - 10;\n          tooltipEl.style.left = x + 'px';\n          tooltipEl.style.top = y + 'px';\n\n          if (w.config.legend.tooltipHoverFormatter) {\n            var legendFormatter = w.config.legend.tooltipHoverFormatter;\n            var i = rel - 1;\n            var legendName = this.legendLabels[i].getAttribute('data:default-text');\n            var text = legendFormatter(legendName, {\n              seriesIndex: i,\n              dataPointIndex: i,\n              w: w\n            });\n            this.legendLabels[i].innerHTML = text;\n          }\n        } else if (e.type === 'mouseout' || e.type === 'touchend') {\n          tooltipEl.classList.remove('apexcharts-active');\n\n          if (w.config.legend.tooltipHoverFormatter) {\n            this.legendLabels.forEach(function (l) {\n              var defaultText = l.getAttribute('data:default-text');\n              l.innerHTML = decodeURIComponent(defaultText);\n            });\n          }\n        }\n      }\n    }, {\n      key: \"handleStickyTooltip\",\n      value: function handleStickyTooltip(e, clientX, clientY, opt) {\n        var w = this.w;\n        var capj = this.tooltipUtil.getNearestValues({\n          context: this,\n          hoverArea: opt.hoverArea,\n          elGrid: opt.elGrid,\n          clientX: clientX,\n          clientY: clientY\n        });\n        var j = capj.j;\n        var capturedSeries = capj.capturedSeries;\n        var bounds = opt.elGrid.getBoundingClientRect();\n\n        if (capj.hoverX < 0 || capj.hoverX > bounds.width) {\n          this.handleMouseOut(opt);\n          return;\n        }\n\n        if (capturedSeries !== null) {\n          this.handleStickyCapturedSeries(e, capturedSeries, opt, j);\n        } else {\n          // couldn't capture any series. check if shared X is same,\n          // if yes, draw a grouped tooltip\n          if (this.tooltipUtil.isXoverlap(j) || w.globals.isBarHorizontal) {\n            this.create(e, this, 0, j, opt.ttItems);\n          }\n        }\n      }\n    }, {\n      key: \"handleStickyCapturedSeries\",\n      value: function handleStickyCapturedSeries(e, capturedSeries, opt, j) {\n        var w = this.w;\n\n        if (!this.tConfig.shared) {\n          var ignoreNull = w.globals.series[capturedSeries][j] === null;\n\n          if (ignoreNull) {\n            this.handleMouseOut(opt);\n            return;\n          }\n        }\n\n        if (typeof w.globals.series[capturedSeries][j] !== 'undefined') {\n          if (this.tConfig.shared && this.tooltipUtil.isXoverlap(j) && this.tooltipUtil.isInitialSeriesSameLen()) {\n            this.create(e, this, capturedSeries, j, opt.ttItems);\n          } else {\n            this.create(e, this, capturedSeries, j, opt.ttItems, false);\n          }\n        } else {\n          if (this.tooltipUtil.isXoverlap(j)) {\n            this.create(e, this, 0, j, opt.ttItems);\n          }\n        }\n      }\n    }, {\n      key: \"deactivateHoverFilter\",\n      value: function deactivateHoverFilter() {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var allPaths = w.globals.dom.Paper.select(\".apexcharts-bar-area\");\n\n        for (var b = 0; b < allPaths.length; b++) {\n          graphics.pathMouseLeave(allPaths[b]);\n        }\n      }\n    }, {\n      key: \"handleMouseOut\",\n      value: function handleMouseOut(opt) {\n        var w = this.w;\n        var xcrosshairs = this.getElXCrosshairs();\n        opt.tooltipEl.classList.remove('apexcharts-active');\n        this.deactivateHoverFilter();\n\n        if (w.config.chart.type !== 'bubble') {\n          this.marker.resetPointsSize();\n        }\n\n        if (xcrosshairs !== null) {\n          xcrosshairs.classList.remove('apexcharts-active');\n        }\n\n        if (this.ycrosshairs !== null) {\n          this.ycrosshairs.classList.remove('apexcharts-active');\n        }\n\n        if (this.isXAxisTooltipEnabled) {\n          this.xaxisTooltip.classList.remove('apexcharts-active');\n        }\n\n        if (this.yaxisTooltips.length) {\n          if (this.yaxisTTEls === null) {\n            this.yaxisTTEls = w.globals.dom.baseEl.querySelectorAll('.apexcharts-yaxistooltip');\n          }\n\n          for (var i = 0; i < this.yaxisTTEls.length; i++) {\n            this.yaxisTTEls[i].classList.remove('apexcharts-active');\n          }\n        }\n\n        if (w.config.legend.tooltipHoverFormatter) {\n          this.legendLabels.forEach(function (l) {\n            var defaultText = l.getAttribute('data:default-text');\n            l.innerHTML = decodeURIComponent(defaultText);\n          });\n        }\n      }\n    }, {\n      key: \"markerClick\",\n      value: function markerClick(e, seriesIndex, dataPointIndex) {\n        var w = this.w;\n\n        if (typeof w.config.chart.events.markerClick === 'function') {\n          w.config.chart.events.markerClick(e, this.ctx, {\n            seriesIndex: seriesIndex,\n            dataPointIndex: dataPointIndex,\n            w: w\n          });\n        }\n\n        this.ctx.events.fireEvent('markerClick', [e, this.ctx, {\n          seriesIndex: seriesIndex,\n          dataPointIndex: dataPointIndex,\n          w: w\n        }]);\n      }\n    }, {\n      key: \"create\",\n      value: function create(e, context, capturedSeries, j, ttItems) {\n        var _w$globals$seriesRang, _w$globals$seriesRang2, _w$globals$seriesRang3, _w$globals$seriesRang4, _w$globals$seriesRang5, _w$globals$seriesRang6, _w$globals$seriesRang7, _w$globals$seriesRang8, _w$globals$seriesRang9, _w$globals$seriesRang10, _w$globals$seriesRang11, _w$globals$seriesRang12, _w$globals$seriesRang13, _w$globals$seriesRang14, _w$globals$seriesRang15, _w$globals$seriesRang16;\n\n        var shared = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;\n        var w = this.w;\n        var ttCtx = context;\n\n        if (e.type === 'mouseup') {\n          this.markerClick(e, capturedSeries, j);\n        }\n\n        if (shared === null) shared = this.tConfig.shared;\n        var hasMarkers = this.tooltipUtil.hasMarkers();\n        var bars = this.tooltipUtil.getElBars();\n\n        if (w.config.legend.tooltipHoverFormatter) {\n          var legendFormatter = w.config.legend.tooltipHoverFormatter;\n          var els = Array.from(this.legendLabels); // reset all legend values first\n\n          els.forEach(function (l) {\n            var legendName = l.getAttribute('data:default-text');\n            l.innerHTML = decodeURIComponent(legendName);\n          }); // for irregular time series\n\n          for (var i = 0; i < els.length; i++) {\n            var l = els[i];\n            var lsIndex = parseInt(l.getAttribute('i'), 10);\n            var legendName = decodeURIComponent(l.getAttribute('data:default-text'));\n            var text = legendFormatter(legendName, {\n              seriesIndex: shared ? lsIndex : capturedSeries,\n              dataPointIndex: j,\n              w: w\n            });\n\n            if (!shared) {\n              l.innerHTML = lsIndex === capturedSeries ? text : legendName;\n\n              if (capturedSeries === lsIndex) {\n                break;\n              }\n            } else {\n              l.innerHTML = w.globals.collapsedSeriesIndices.indexOf(lsIndex) < 0 ? text : legendName;\n            }\n          }\n        }\n\n        var commonSeriesTextsParams = _objectSpread2(_objectSpread2({\n          ttItems: ttItems,\n          i: capturedSeries,\n          j: j\n        }, typeof ((_w$globals$seriesRang = w.globals.seriesRange) === null || _w$globals$seriesRang === void 0 ? void 0 : (_w$globals$seriesRang2 = _w$globals$seriesRang[capturedSeries]) === null || _w$globals$seriesRang2 === void 0 ? void 0 : (_w$globals$seriesRang3 = _w$globals$seriesRang2[j]) === null || _w$globals$seriesRang3 === void 0 ? void 0 : (_w$globals$seriesRang4 = _w$globals$seriesRang3.y[0]) === null || _w$globals$seriesRang4 === void 0 ? void 0 : _w$globals$seriesRang4.y1) !== 'undefined' && {\n          y1: (_w$globals$seriesRang5 = w.globals.seriesRange) === null || _w$globals$seriesRang5 === void 0 ? void 0 : (_w$globals$seriesRang6 = _w$globals$seriesRang5[capturedSeries]) === null || _w$globals$seriesRang6 === void 0 ? void 0 : (_w$globals$seriesRang7 = _w$globals$seriesRang6[j]) === null || _w$globals$seriesRang7 === void 0 ? void 0 : (_w$globals$seriesRang8 = _w$globals$seriesRang7.y[0]) === null || _w$globals$seriesRang8 === void 0 ? void 0 : _w$globals$seriesRang8.y1\n        }), typeof ((_w$globals$seriesRang9 = w.globals.seriesRange) === null || _w$globals$seriesRang9 === void 0 ? void 0 : (_w$globals$seriesRang10 = _w$globals$seriesRang9[capturedSeries]) === null || _w$globals$seriesRang10 === void 0 ? void 0 : (_w$globals$seriesRang11 = _w$globals$seriesRang10[j]) === null || _w$globals$seriesRang11 === void 0 ? void 0 : (_w$globals$seriesRang12 = _w$globals$seriesRang11.y[0]) === null || _w$globals$seriesRang12 === void 0 ? void 0 : _w$globals$seriesRang12.y2) !== 'undefined' && {\n          y2: (_w$globals$seriesRang13 = w.globals.seriesRange) === null || _w$globals$seriesRang13 === void 0 ? void 0 : (_w$globals$seriesRang14 = _w$globals$seriesRang13[capturedSeries]) === null || _w$globals$seriesRang14 === void 0 ? void 0 : (_w$globals$seriesRang15 = _w$globals$seriesRang14[j]) === null || _w$globals$seriesRang15 === void 0 ? void 0 : (_w$globals$seriesRang16 = _w$globals$seriesRang15.y[0]) === null || _w$globals$seriesRang16 === void 0 ? void 0 : _w$globals$seriesRang16.y2\n        });\n\n        if (shared) {\n          ttCtx.tooltipLabels.drawSeriesTexts(_objectSpread2(_objectSpread2({}, commonSeriesTextsParams), {}, {\n            shared: this.showOnIntersect ? false : this.tConfig.shared\n          }));\n\n          if (hasMarkers) {\n            if (w.globals.markers.largestSize > 0) {\n              ttCtx.marker.enlargePoints(j);\n            } else {\n              ttCtx.tooltipPosition.moveDynamicPointsOnHover(j);\n            }\n          }\n\n          if (this.tooltipUtil.hasBars()) {\n            this.barSeriesHeight = this.tooltipUtil.getBarsHeight(bars);\n\n            if (this.barSeriesHeight > 0) {\n              // hover state, activate snap filter\n              var graphics = new Graphics(this.ctx);\n              var paths = w.globals.dom.Paper.select(\".apexcharts-bar-area[j='\".concat(j, \"']\")); // de-activate first\n\n              this.deactivateHoverFilter();\n              this.tooltipPosition.moveStickyTooltipOverBars(j);\n\n              for (var b = 0; b < paths.length; b++) {\n                graphics.pathMouseEnter(paths[b]);\n              }\n            }\n          }\n        } else {\n          ttCtx.tooltipLabels.drawSeriesTexts(_objectSpread2({\n            shared: false\n          }, commonSeriesTextsParams));\n\n          if (this.tooltipUtil.hasBars()) {\n            ttCtx.tooltipPosition.moveStickyTooltipOverBars(j);\n          }\n\n          if (hasMarkers) {\n            ttCtx.tooltipPosition.moveMarkers(capturedSeries, j);\n          }\n        }\n      }\n    }]);\n\n    return Tooltip;\n  }();\n\n  var BarDataLabels = /*#__PURE__*/function () {\n    function BarDataLabels(barCtx) {\n      _classCallCheck(this, BarDataLabels);\n\n      this.w = barCtx.w;\n      this.barCtx = barCtx;\n      this.totalFormatter = this.w.config.plotOptions.bar.dataLabels.total.formatter;\n\n      if (!this.totalFormatter) {\n        this.totalFormatter = this.w.config.dataLabels.formatter;\n      }\n    }\n    /** handleBarDataLabels is used to calculate the positions for the data-labels\n     * It also sets the element's data attr for bars and calls drawCalculatedBarDataLabels()\n     * After calculating, it also calls the function to draw data labels\n     * @memberof Bar\n     * @param {object} {barProps} most of the bar properties used throughout the bar\n     * drawing function\n     * @return {object} dataLabels node-element which you can append later\n     **/\n\n\n    _createClass(BarDataLabels, [{\n      key: \"handleBarDataLabels\",\n      value: function handleBarDataLabels(opts) {\n        var x = opts.x,\n            y = opts.y,\n            y1 = opts.y1,\n            y2 = opts.y2,\n            i = opts.i,\n            j = opts.j,\n            realIndex = opts.realIndex,\n            series = opts.series,\n            barHeight = opts.barHeight,\n            barWidth = opts.barWidth,\n            barYPosition = opts.barYPosition,\n            visibleSeries = opts.visibleSeries,\n            renderedPath = opts.renderedPath;\n        var w = this.w;\n        var graphics = new Graphics(this.barCtx.ctx);\n        var strokeWidth = Array.isArray(this.barCtx.strokeWidth) ? this.barCtx.strokeWidth[realIndex] : this.barCtx.strokeWidth;\n        var bcx = x + parseFloat(barWidth * visibleSeries);\n        var bcy = y + parseFloat(barHeight * visibleSeries);\n\n        if (w.globals.isXNumeric && !w.globals.isBarHorizontal) {\n          bcx = x + parseFloat(barWidth * (visibleSeries + 1));\n          bcy = y + parseFloat(barHeight * (visibleSeries + 1)) - strokeWidth;\n        }\n\n        var dataLabels = null;\n        var totalDataLabels = null;\n        var dataLabelsX = x;\n        var dataLabelsY = y;\n        var dataLabelsPos = {};\n        var dataLabelsConfig = w.config.dataLabels;\n        var barDataLabelsConfig = this.barCtx.barOptions.dataLabels;\n        var barTotalDataLabelsConfig = this.barCtx.barOptions.dataLabels.total;\n\n        if (typeof barYPosition !== 'undefined' && this.barCtx.isRangeBar) {\n          bcy = barYPosition;\n          dataLabelsY = barYPosition;\n        }\n\n        var offX = dataLabelsConfig.offsetX;\n        var offY = dataLabelsConfig.offsetY;\n        var textRects = {\n          width: 0,\n          height: 0\n        };\n\n        if (w.config.dataLabels.enabled) {\n          var yLabel = this.barCtx.series[i][j];\n          textRects = graphics.getTextRects(w.globals.yLabelFormatters[0](yLabel), parseFloat(dataLabelsConfig.style.fontSize));\n        }\n\n        var params = {\n          x: x,\n          y: y,\n          i: i,\n          j: j,\n          realIndex: realIndex,\n          renderedPath: renderedPath,\n          bcx: bcx,\n          bcy: bcy,\n          barHeight: barHeight,\n          barWidth: barWidth,\n          textRects: textRects,\n          strokeWidth: strokeWidth,\n          dataLabelsX: dataLabelsX,\n          dataLabelsY: dataLabelsY,\n          dataLabelsConfig: dataLabelsConfig,\n          barDataLabelsConfig: barDataLabelsConfig,\n          barTotalDataLabelsConfig: barTotalDataLabelsConfig,\n          offX: offX,\n          offY: offY\n        };\n\n        if (this.barCtx.isHorizontal) {\n          dataLabelsPos = this.calculateBarsDataLabelsPosition(params);\n        } else {\n          dataLabelsPos = this.calculateColumnsDataLabelsPosition(params);\n        }\n\n        renderedPath.attr({\n          cy: dataLabelsPos.bcy,\n          cx: dataLabelsPos.bcx,\n          j: j,\n          val: series[i][j],\n          barHeight: barHeight,\n          barWidth: barWidth\n        });\n        dataLabels = this.drawCalculatedDataLabels({\n          x: dataLabelsPos.dataLabelsX,\n          y: dataLabelsPos.dataLabelsY,\n          val: this.barCtx.isRangeBar ? [y1, y2] : series[i][j],\n          i: realIndex,\n          j: j,\n          barWidth: barWidth,\n          barHeight: barHeight,\n          textRects: textRects,\n          dataLabelsConfig: dataLabelsConfig\n        });\n\n        if (w.config.chart.stacked && barTotalDataLabelsConfig.enabled) {\n          totalDataLabels = this.drawTotalDataLabels({\n            x: dataLabelsPos.totalDataLabelsX,\n            y: dataLabelsPos.totalDataLabelsY,\n            realIndex: realIndex,\n            textAnchor: dataLabelsPos.totalDataLabelsAnchor,\n            val: this.getStackedTotalDataLabel({\n              realIndex: realIndex,\n              j: j\n            }),\n            dataLabelsConfig: dataLabelsConfig,\n            barTotalDataLabelsConfig: barTotalDataLabelsConfig\n          });\n        }\n\n        return {\n          dataLabels: dataLabels,\n          totalDataLabels: totalDataLabels\n        };\n      }\n    }, {\n      key: \"getStackedTotalDataLabel\",\n      value: function getStackedTotalDataLabel(_ref) {\n        var realIndex = _ref.realIndex,\n            j = _ref.j;\n        var w = this.w;\n        var val = this.barCtx.stackedSeriesTotals[j];\n\n        if (this.totalFormatter) {\n          val = this.totalFormatter(val, _objectSpread2(_objectSpread2({}, w), {}, {\n            seriesIndex: realIndex,\n            dataPointIndex: j,\n            w: w\n          }));\n        }\n\n        return val;\n      }\n    }, {\n      key: \"calculateColumnsDataLabelsPosition\",\n      value: function calculateColumnsDataLabelsPosition(opts) {\n        var w = this.w;\n        var i = opts.i,\n            j = opts.j,\n            realIndex = opts.realIndex,\n            y = opts.y,\n            bcx = opts.bcx,\n            barWidth = opts.barWidth,\n            barHeight = opts.barHeight,\n            textRects = opts.textRects,\n            dataLabelsY = opts.dataLabelsY,\n            dataLabelsConfig = opts.dataLabelsConfig,\n            barDataLabelsConfig = opts.barDataLabelsConfig,\n            barTotalDataLabelsConfig = opts.barTotalDataLabelsConfig,\n            strokeWidth = opts.strokeWidth,\n            offX = opts.offX,\n            offY = opts.offY;\n        var dataLabelsX;\n        var totalDataLabelsY;\n        var totalDataLabelsX;\n        var totalDataLabelsAnchor = 'middle';\n        barHeight = Math.abs(barHeight);\n        var vertical = w.config.plotOptions.bar.dataLabels.orientation === 'vertical';\n        bcx = bcx - strokeWidth / 2;\n        var dataPointsDividedWidth = w.globals.gridWidth / w.globals.dataPoints;\n\n        if (w.globals.isXNumeric) {\n          dataLabelsX = bcx - barWidth / 2 + offX;\n        } else {\n          dataLabelsX = bcx - dataPointsDividedWidth + barWidth / 2 + offX;\n        }\n\n        if (vertical) {\n          var offsetDLX = 2;\n          dataLabelsX = dataLabelsX + textRects.height / 2 - strokeWidth / 2 - offsetDLX;\n        }\n\n        var valIsNegative = this.barCtx.series[i][j] < 0;\n        var newY = y;\n\n        if (this.barCtx.isReversed) {\n          newY = y - barHeight + (valIsNegative ? barHeight * 2 : 0);\n          y = y - barHeight;\n        }\n\n        switch (barDataLabelsConfig.position) {\n          case 'center':\n            if (vertical) {\n              if (valIsNegative) {\n                dataLabelsY = newY + barHeight / 2 + offY;\n              } else {\n                dataLabelsY = newY + barHeight / 2 - offY;\n              }\n            } else {\n              if (valIsNegative) {\n                dataLabelsY = newY - barHeight / 2 + textRects.height / 2 + offY;\n              } else {\n                dataLabelsY = newY + barHeight / 2 + textRects.height / 2 - offY;\n              }\n            }\n\n            break;\n\n          case 'bottom':\n            if (vertical) {\n              if (valIsNegative) {\n                dataLabelsY = newY + barHeight + offY;\n              } else {\n                dataLabelsY = newY + barHeight - offY;\n              }\n            } else {\n              if (valIsNegative) {\n                dataLabelsY = newY - barHeight + textRects.height + strokeWidth + offY;\n              } else {\n                dataLabelsY = newY + barHeight - textRects.height / 2 + strokeWidth - offY;\n              }\n            }\n\n            break;\n\n          case 'top':\n            if (vertical) {\n              if (valIsNegative) {\n                dataLabelsY = newY + offY;\n              } else {\n                dataLabelsY = newY - offY;\n              }\n            } else {\n              if (valIsNegative) {\n                dataLabelsY = newY - textRects.height / 2 - offY;\n              } else {\n                dataLabelsY = newY + textRects.height + offY;\n              }\n            }\n\n            break;\n        }\n\n        if (this.barCtx.lastActiveBarSerieIndex === realIndex && barTotalDataLabelsConfig.enabled) {\n          var ADDITIONAL_OFFX = 18;\n          var graphics = new Graphics(this.barCtx.ctx);\n          var totalLabeltextRects = graphics.getTextRects(this.getStackedTotalDataLabel({\n            realIndex: realIndex,\n            j: j\n          }), dataLabelsConfig.fontSize);\n\n          if (valIsNegative) {\n            totalDataLabelsY = newY - totalLabeltextRects.height / 2 - offY - barTotalDataLabelsConfig.offsetY + ADDITIONAL_OFFX;\n          } else {\n            totalDataLabelsY = newY + totalLabeltextRects.height + offY + barTotalDataLabelsConfig.offsetY - ADDITIONAL_OFFX;\n          }\n\n          totalDataLabelsX = dataLabelsX + barTotalDataLabelsConfig.offsetX;\n        }\n\n        if (!w.config.chart.stacked) {\n          if (dataLabelsY < 0) {\n            dataLabelsY = 0 + strokeWidth;\n          } else if (dataLabelsY + textRects.height / 3 > w.globals.gridHeight) {\n            dataLabelsY = w.globals.gridHeight - strokeWidth;\n          }\n        }\n\n        return {\n          bcx: bcx,\n          bcy: y,\n          dataLabelsX: dataLabelsX,\n          dataLabelsY: dataLabelsY,\n          totalDataLabelsX: totalDataLabelsX,\n          totalDataLabelsY: totalDataLabelsY,\n          totalDataLabelsAnchor: totalDataLabelsAnchor\n        };\n      }\n    }, {\n      key: \"calculateBarsDataLabelsPosition\",\n      value: function calculateBarsDataLabelsPosition(opts) {\n        var w = this.w;\n        var x = opts.x,\n            i = opts.i,\n            j = opts.j,\n            realIndex = opts.realIndex,\n            bcy = opts.bcy,\n            barHeight = opts.barHeight,\n            barWidth = opts.barWidth,\n            textRects = opts.textRects,\n            dataLabelsX = opts.dataLabelsX,\n            strokeWidth = opts.strokeWidth,\n            dataLabelsConfig = opts.dataLabelsConfig,\n            barDataLabelsConfig = opts.barDataLabelsConfig,\n            barTotalDataLabelsConfig = opts.barTotalDataLabelsConfig,\n            offX = opts.offX,\n            offY = opts.offY;\n        var dataPointsDividedHeight = w.globals.gridHeight / w.globals.dataPoints;\n        barWidth = Math.abs(barWidth);\n        var dataLabelsY = bcy - (this.barCtx.isRangeBar ? 0 : dataPointsDividedHeight) + barHeight / 2 + textRects.height / 2 + offY - 3;\n        var totalDataLabelsX;\n        var totalDataLabelsY;\n        var totalDataLabelsAnchor = 'start';\n        var valIsNegative = this.barCtx.series[i][j] < 0;\n        var newX = x;\n\n        if (this.barCtx.isReversed) {\n          newX = x + barWidth - (valIsNegative ? barWidth * 2 : 0);\n          x = w.globals.gridWidth - barWidth;\n        }\n\n        switch (barDataLabelsConfig.position) {\n          case 'center':\n            if (valIsNegative) {\n              dataLabelsX = newX + barWidth / 2 - offX;\n            } else {\n              dataLabelsX = Math.max(textRects.width / 2, newX - barWidth / 2) + offX;\n            }\n\n            break;\n\n          case 'bottom':\n            if (valIsNegative) {\n              dataLabelsX = newX + barWidth - strokeWidth - Math.round(textRects.width / 2) - offX;\n            } else {\n              dataLabelsX = newX - barWidth + strokeWidth + Math.round(textRects.width / 2) + offX;\n            }\n\n            break;\n\n          case 'top':\n            if (valIsNegative) {\n              dataLabelsX = newX - strokeWidth + Math.round(textRects.width / 2) - offX;\n            } else {\n              dataLabelsX = newX - strokeWidth - Math.round(textRects.width / 2) + offX;\n            }\n\n            break;\n        }\n\n        if (this.barCtx.lastActiveBarSerieIndex === realIndex && barTotalDataLabelsConfig.enabled) {\n          var ADDITIONAL_OFFX = 15;\n          var graphics = new Graphics(this.barCtx.ctx);\n          var totalLabeltextRects = graphics.getTextRects(this.getStackedTotalDataLabel({\n            realIndex: realIndex,\n            j: j\n          }), dataLabelsConfig.fontSize);\n\n          if (valIsNegative) {\n            totalDataLabelsX = newX - strokeWidth + Math.round(totalLabeltextRects.width / 2) - offX - barTotalDataLabelsConfig.offsetX - ADDITIONAL_OFFX;\n            totalDataLabelsAnchor = 'end';\n          } else {\n            totalDataLabelsX = newX - strokeWidth - Math.round(totalLabeltextRects.width / 2) + offX + barTotalDataLabelsConfig.offsetX + ADDITIONAL_OFFX;\n          }\n\n          totalDataLabelsY = dataLabelsY + barTotalDataLabelsConfig.offsetY;\n        }\n\n        if (!w.config.chart.stacked) {\n          if (dataLabelsX < 0) {\n            dataLabelsX = dataLabelsX + textRects.width + strokeWidth;\n          } else if (dataLabelsX + textRects.width / 2 > w.globals.gridWidth) {\n            dataLabelsX = w.globals.gridWidth - textRects.width - strokeWidth;\n          }\n        }\n\n        return {\n          bcx: x,\n          bcy: bcy,\n          dataLabelsX: dataLabelsX,\n          dataLabelsY: dataLabelsY,\n          totalDataLabelsX: totalDataLabelsX,\n          totalDataLabelsY: totalDataLabelsY,\n          totalDataLabelsAnchor: totalDataLabelsAnchor\n        };\n      }\n    }, {\n      key: \"drawCalculatedDataLabels\",\n      value: function drawCalculatedDataLabels(_ref2) {\n        var x = _ref2.x,\n            y = _ref2.y,\n            val = _ref2.val,\n            i = _ref2.i,\n            j = _ref2.j,\n            textRects = _ref2.textRects,\n            barHeight = _ref2.barHeight,\n            barWidth = _ref2.barWidth,\n            dataLabelsConfig = _ref2.dataLabelsConfig;\n        var w = this.w;\n        var rotate = 'rotate(0)';\n        if (w.config.plotOptions.bar.dataLabels.orientation === 'vertical') rotate = \"rotate(-90, \".concat(x, \", \").concat(y, \")\");\n        var dataLabels = new DataLabels(this.barCtx.ctx);\n        var graphics = new Graphics(this.barCtx.ctx);\n        var formatter = dataLabelsConfig.formatter;\n        var elDataLabelsWrap = null;\n        var isSeriesNotCollapsed = w.globals.collapsedSeriesIndices.indexOf(i) > -1;\n\n        if (dataLabelsConfig.enabled && !isSeriesNotCollapsed) {\n          elDataLabelsWrap = graphics.group({\n            class: 'apexcharts-data-labels',\n            transform: rotate\n          });\n          var text = '';\n\n          if (typeof val !== 'undefined') {\n            text = formatter(val, _objectSpread2(_objectSpread2({}, w), {}, {\n              seriesIndex: i,\n              dataPointIndex: j,\n              w: w\n            }));\n          }\n\n          var valIsNegative = w.globals.series[i][j] < 0;\n          var position = w.config.plotOptions.bar.dataLabels.position;\n\n          if (w.config.plotOptions.bar.dataLabels.orientation === 'vertical') {\n            if (position === 'top') {\n              if (valIsNegative) dataLabelsConfig.textAnchor = 'end';else dataLabelsConfig.textAnchor = 'start';\n            }\n\n            if (position === 'center') {\n              dataLabelsConfig.textAnchor = 'middle';\n            }\n\n            if (position === 'bottom') {\n              if (valIsNegative) dataLabelsConfig.textAnchor = 'end';else dataLabelsConfig.textAnchor = 'start';\n            }\n          }\n\n          if (this.barCtx.isRangeBar && this.barCtx.barOptions.dataLabels.hideOverflowingLabels) {\n            // hide the datalabel if it cannot fit into the rect\n            var txRect = graphics.getTextRects(text, parseFloat(dataLabelsConfig.style.fontSize));\n\n            if (barWidth < txRect.width) {\n              text = '';\n            }\n          }\n\n          if (w.config.chart.stacked && this.barCtx.barOptions.dataLabels.hideOverflowingLabels) {\n            // if there is not enough space to draw the label in the bar/column rect, check hideOverflowingLabels property to prevent overflowing on wrong rect\n            // Note: This issue is only seen in stacked charts\n            if (this.barCtx.isHorizontal) {\n              if (textRects.width / 1.6 > Math.abs(barWidth)) {\n                text = '';\n              }\n            } else {\n              if (textRects.height / 1.6 > Math.abs(barHeight)) {\n                text = '';\n              }\n            }\n          }\n\n          var modifiedDataLabelsConfig = _objectSpread2({}, dataLabelsConfig);\n\n          if (this.barCtx.isHorizontal) {\n            if (val < 0) {\n              if (dataLabelsConfig.textAnchor === 'start') {\n                modifiedDataLabelsConfig.textAnchor = 'end';\n              } else if (dataLabelsConfig.textAnchor === 'end') {\n                modifiedDataLabelsConfig.textAnchor = 'start';\n              }\n            }\n          }\n\n          dataLabels.plotDataLabelsText({\n            x: x,\n            y: y,\n            text: text,\n            i: i,\n            j: j,\n            parent: elDataLabelsWrap,\n            dataLabelsConfig: modifiedDataLabelsConfig,\n            alwaysDrawDataLabel: true,\n            offsetCorrection: true\n          });\n        }\n\n        return elDataLabelsWrap;\n      }\n    }, {\n      key: \"drawTotalDataLabels\",\n      value: function drawTotalDataLabels(_ref3) {\n        var x = _ref3.x,\n            y = _ref3.y,\n            val = _ref3.val,\n            realIndex = _ref3.realIndex,\n            textAnchor = _ref3.textAnchor,\n            barTotalDataLabelsConfig = _ref3.barTotalDataLabelsConfig;\n        var graphics = new Graphics(this.barCtx.ctx);\n        var totalDataLabelText;\n\n        if (barTotalDataLabelsConfig.enabled && typeof x !== 'undefined' && typeof y !== 'undefined' && this.barCtx.lastActiveBarSerieIndex === realIndex) {\n          totalDataLabelText = graphics.drawText({\n            x: x,\n            y: y,\n            foreColor: barTotalDataLabelsConfig.style.color,\n            text: val,\n            textAnchor: textAnchor,\n            fontFamily: barTotalDataLabelsConfig.style.fontFamily,\n            fontSize: barTotalDataLabelsConfig.style.fontSize,\n            fontWeight: barTotalDataLabelsConfig.style.fontWeight\n          });\n        }\n\n        return totalDataLabelText;\n      }\n    }]);\n\n    return BarDataLabels;\n  }();\n\n  var Helpers$1 = /*#__PURE__*/function () {\n    function Helpers(barCtx) {\n      _classCallCheck(this, Helpers);\n\n      this.w = barCtx.w;\n      this.barCtx = barCtx;\n    }\n\n    _createClass(Helpers, [{\n      key: \"initVariables\",\n      value: function initVariables(series) {\n        var w = this.w;\n        this.barCtx.series = series;\n        this.barCtx.totalItems = 0;\n        this.barCtx.seriesLen = 0;\n        this.barCtx.visibleI = -1; // visible Series\n\n        this.barCtx.visibleItems = 1; // number of visible bars after user zoomed in/out\n\n        for (var sl = 0; sl < series.length; sl++) {\n          if (series[sl].length > 0) {\n            this.barCtx.seriesLen = this.barCtx.seriesLen + 1;\n            this.barCtx.totalItems += series[sl].length;\n          }\n\n          if (w.globals.isXNumeric) {\n            // get max visible items\n            for (var j = 0; j < series[sl].length; j++) {\n              if (w.globals.seriesX[sl][j] > w.globals.minX && w.globals.seriesX[sl][j] < w.globals.maxX) {\n                this.barCtx.visibleItems++;\n              }\n            }\n          } else {\n            this.barCtx.visibleItems = w.globals.dataPoints;\n          }\n        }\n\n        if (this.barCtx.seriesLen === 0) {\n          // A small adjustment when combo charts are used\n          this.barCtx.seriesLen = 1;\n        }\n\n        this.barCtx.zeroSerieses = [];\n        this.barCtx.radiusOnSeriesNumber = series.length - 1; // which series to draw ending shape on\n\n        if (!w.globals.comboCharts) {\n          this.checkZeroSeries({\n            series: series\n          });\n        }\n      }\n    }, {\n      key: \"initialPositions\",\n      value: function initialPositions() {\n        var w = this.w;\n        var x, y, yDivision, xDivision, barHeight, barWidth, zeroH, zeroW;\n        var dataPoints = w.globals.dataPoints;\n\n        if (this.barCtx.isRangeBar) {\n          // timeline rangebar chart\n          dataPoints = w.globals.labels.length;\n        }\n\n        var seriesLen = this.barCtx.seriesLen;\n\n        if (w.config.plotOptions.bar.rangeBarGroupRows) {\n          seriesLen = 1;\n        }\n\n        if (this.barCtx.isHorizontal) {\n          // height divided into equal parts\n          yDivision = w.globals.gridHeight / dataPoints;\n          barHeight = yDivision / seriesLen;\n\n          if (w.globals.isXNumeric) {\n            yDivision = w.globals.gridHeight / this.barCtx.totalItems;\n            barHeight = yDivision / this.barCtx.seriesLen;\n          }\n\n          barHeight = barHeight * parseInt(this.barCtx.barOptions.barHeight, 10) / 100;\n          zeroW = this.barCtx.baseLineInvertedY + w.globals.padHorizontal + (this.barCtx.isReversed ? w.globals.gridWidth : 0) - (this.barCtx.isReversed ? this.barCtx.baseLineInvertedY * 2 : 0);\n          y = (yDivision - barHeight * this.barCtx.seriesLen) / 2;\n        } else {\n          // width divided into equal parts\n          xDivision = w.globals.gridWidth / this.barCtx.visibleItems;\n\n          if (w.config.xaxis.convertedCatToNumeric) {\n            xDivision = w.globals.gridWidth / w.globals.dataPoints;\n          }\n\n          barWidth = xDivision / this.barCtx.seriesLen * parseInt(this.barCtx.barOptions.columnWidth, 10) / 100;\n\n          if (w.globals.isXNumeric) {\n            // max barwidth should be equal to minXDiff to avoid overlap\n            var xRatio = this.barCtx.xRatio;\n\n            if (w.config.xaxis.convertedCatToNumeric) {\n              xRatio = this.barCtx.initialXRatio;\n            }\n\n            if (w.globals.minXDiff && w.globals.minXDiff !== 0.5 && w.globals.minXDiff / xRatio > 0) {\n              xDivision = w.globals.minXDiff / xRatio;\n            }\n\n            barWidth = xDivision / this.barCtx.seriesLen * parseInt(this.barCtx.barOptions.columnWidth, 10) / 100;\n\n            if (barWidth < 1) {\n              barWidth = 1;\n            }\n          }\n\n          zeroH = w.globals.gridHeight - this.barCtx.baseLineY[this.barCtx.yaxisIndex] - (this.barCtx.isReversed ? w.globals.gridHeight : 0) + (this.barCtx.isReversed ? this.barCtx.baseLineY[this.barCtx.yaxisIndex] * 2 : 0);\n          x = w.globals.padHorizontal + (xDivision - barWidth * this.barCtx.seriesLen) / 2;\n        }\n\n        return {\n          x: x,\n          y: y,\n          yDivision: yDivision,\n          xDivision: xDivision,\n          barHeight: barHeight,\n          barWidth: barWidth,\n          zeroH: zeroH,\n          zeroW: zeroW\n        };\n      }\n    }, {\n      key: \"getPathFillColor\",\n      value: function getPathFillColor(series, i, j, realIndex) {\n        var _w$config$series$i$da, _w$config$series$i$da2, _w$config$series$i$da3, _w$config$series$i$da4;\n\n        var w = this.w;\n        var fill = new Fill(this.barCtx.ctx);\n        var fillColor = null;\n        var seriesNumber = this.barCtx.barOptions.distributed ? j : i;\n\n        if (this.barCtx.barOptions.colors.ranges.length > 0) {\n          var colorRange = this.barCtx.barOptions.colors.ranges;\n          colorRange.map(function (range) {\n            if (series[i][j] >= range.from && series[i][j] <= range.to) {\n              fillColor = range.color;\n            }\n          });\n        }\n\n        if (w.config.series[i].data[j] && w.config.series[i].data[j].fillColor) {\n          fillColor = w.config.series[i].data[j].fillColor;\n        }\n\n        var pathFill = fill.fillPath({\n          seriesNumber: this.barCtx.barOptions.distributed ? seriesNumber : realIndex,\n          dataPointIndex: j,\n          color: fillColor,\n          value: series[i][j],\n          fillConfig: (_w$config$series$i$da = w.config.series[i].data[j]) === null || _w$config$series$i$da === void 0 ? void 0 : _w$config$series$i$da.fill,\n          fillType: (_w$config$series$i$da2 = w.config.series[i].data[j]) !== null && _w$config$series$i$da2 !== void 0 && (_w$config$series$i$da3 = _w$config$series$i$da2.fill) !== null && _w$config$series$i$da3 !== void 0 && _w$config$series$i$da3.type ? (_w$config$series$i$da4 = w.config.series[i].data[j]) === null || _w$config$series$i$da4 === void 0 ? void 0 : _w$config$series$i$da4.fill.type : w.config.fill.type\n        });\n        return pathFill;\n      }\n    }, {\n      key: \"getStrokeWidth\",\n      value: function getStrokeWidth(i, j, realIndex) {\n        var strokeWidth = 0;\n        var w = this.w;\n\n        if (!this.barCtx.series[i][j]) {\n          this.barCtx.isNullValue = true;\n        } else {\n          this.barCtx.isNullValue = false;\n        }\n\n        if (w.config.stroke.show) {\n          if (!this.barCtx.isNullValue) {\n            strokeWidth = Array.isArray(this.barCtx.strokeWidth) ? this.barCtx.strokeWidth[realIndex] : this.barCtx.strokeWidth;\n          }\n        }\n\n        return strokeWidth;\n      }\n    }, {\n      key: \"shouldApplyRadius\",\n      value: function shouldApplyRadius(realIndex) {\n        var w = this.w;\n        var applyRadius = false;\n\n        if (w.config.plotOptions.bar.borderRadius > 0) {\n          if (w.config.chart.stacked) {\n            if (w.config.plotOptions.bar.borderRadiusWhenStacked === 'last') {\n              if (this.barCtx.lastActiveBarSerieIndex === realIndex) {\n                applyRadius = true;\n              }\n            } else {\n              applyRadius = true;\n            }\n          } else {\n            applyRadius = true;\n          }\n        }\n\n        return applyRadius;\n      }\n    }, {\n      key: \"barBackground\",\n      value: function barBackground(_ref) {\n        var j = _ref.j,\n            i = _ref.i,\n            x1 = _ref.x1,\n            x2 = _ref.x2,\n            y1 = _ref.y1,\n            y2 = _ref.y2,\n            elSeries = _ref.elSeries;\n        var w = this.w;\n        var graphics = new Graphics(this.barCtx.ctx);\n        var sr = new Series(this.barCtx.ctx);\n        var activeSeriesIndex = sr.getActiveConfigSeriesIndex();\n\n        if (this.barCtx.barOptions.colors.backgroundBarColors.length > 0 && activeSeriesIndex === i) {\n          if (j >= this.barCtx.barOptions.colors.backgroundBarColors.length) {\n            j %= this.barCtx.barOptions.colors.backgroundBarColors.length;\n          }\n\n          var bcolor = this.barCtx.barOptions.colors.backgroundBarColors[j];\n          var rect = graphics.drawRect(typeof x1 !== 'undefined' ? x1 : 0, typeof y1 !== 'undefined' ? y1 : 0, typeof x2 !== 'undefined' ? x2 : w.globals.gridWidth, typeof y2 !== 'undefined' ? y2 : w.globals.gridHeight, this.barCtx.barOptions.colors.backgroundBarRadius, bcolor, this.barCtx.barOptions.colors.backgroundBarOpacity);\n          elSeries.add(rect);\n          rect.node.classList.add('apexcharts-backgroundBar');\n        }\n      }\n    }, {\n      key: \"getColumnPaths\",\n      value: function getColumnPaths(_ref2) {\n        var _w$config$series$real;\n\n        var barWidth = _ref2.barWidth,\n            barXPosition = _ref2.barXPosition,\n            y1 = _ref2.y1,\n            y2 = _ref2.y2,\n            strokeWidth = _ref2.strokeWidth,\n            realIndex = _ref2.realIndex,\n            i = _ref2.i,\n            j = _ref2.j,\n            w = _ref2.w;\n        var graphics = new Graphics(this.barCtx.ctx);\n        strokeWidth = Array.isArray(strokeWidth) ? strokeWidth[realIndex] : strokeWidth;\n        if (!strokeWidth) strokeWidth = 0;\n        var bW = barWidth;\n        var bXP = barXPosition;\n\n        if ((_w$config$series$real = w.config.series[realIndex].data[j]) !== null && _w$config$series$real !== void 0 && _w$config$series$real.columnWidthOffset) {\n          bXP = barXPosition - w.config.series[realIndex].data[j].columnWidthOffset / 2;\n          bW = barWidth + w.config.series[realIndex].data[j].columnWidthOffset;\n        }\n\n        var x1 = bXP;\n        var x2 = bXP + bW; // append tiny pixels to avoid exponentials (which cause issues in border-radius)\n\n        y1 += 0.001;\n        y2 += 0.001;\n        var pathTo = graphics.move(x1, y1);\n        var pathFrom = graphics.move(x1, y1);\n        var sl = graphics.line(x2 - strokeWidth, y1);\n\n        if (w.globals.previousPaths.length > 0) {\n          pathFrom = this.barCtx.getPreviousPath(realIndex, j, false);\n        }\n\n        pathTo = pathTo + graphics.line(x1, y2) + graphics.line(x2 - strokeWidth, y2) + graphics.line(x2 - strokeWidth, y1) + (w.config.plotOptions.bar.borderRadiusApplication === 'around' ? ' Z' : ' z'); // the lines in pathFrom are repeated to equal it to the points of pathTo\n        // this is to avoid weird animation (bug in svg.js)\n\n        pathFrom = pathFrom + graphics.line(x1, y1) + sl + sl + sl + sl + sl + graphics.line(x1, y1) + (w.config.plotOptions.bar.borderRadiusApplication === 'around' ? ' Z' : ' z');\n\n        if (this.shouldApplyRadius(realIndex)) {\n          pathTo = graphics.roundPathCorners(pathTo, w.config.plotOptions.bar.borderRadius);\n        }\n\n        if (w.config.chart.stacked) {\n          this.barCtx.yArrj.push(y2);\n          this.barCtx.yArrjF.push(Math.abs(y1 - y2));\n          this.barCtx.yArrjVal.push(this.barCtx.series[i][j]);\n        }\n\n        return {\n          pathTo: pathTo,\n          pathFrom: pathFrom\n        };\n      }\n    }, {\n      key: \"getBarpaths\",\n      value: function getBarpaths(_ref3) {\n        var _w$config$series$real2;\n\n        var barYPosition = _ref3.barYPosition,\n            barHeight = _ref3.barHeight,\n            x1 = _ref3.x1,\n            x2 = _ref3.x2,\n            strokeWidth = _ref3.strokeWidth,\n            realIndex = _ref3.realIndex,\n            i = _ref3.i,\n            j = _ref3.j,\n            w = _ref3.w;\n        var graphics = new Graphics(this.barCtx.ctx);\n        strokeWidth = Array.isArray(strokeWidth) ? strokeWidth[realIndex] : strokeWidth;\n        if (!strokeWidth) strokeWidth = 0;\n        var bYP = barYPosition;\n        var bH = barHeight;\n\n        if ((_w$config$series$real2 = w.config.series[realIndex].data[j]) !== null && _w$config$series$real2 !== void 0 && _w$config$series$real2.barHeightOffset) {\n          bYP = barYPosition - w.config.series[realIndex].data[j].barHeightOffset / 2;\n          bH = barHeight + w.config.series[realIndex].data[j].barHeightOffset;\n        }\n\n        var y1 = bYP;\n        var y2 = bYP + bH; // append tiny pixels to avoid exponentials (which cause issues in border-radius)\n\n        x1 += 0.001;\n        x2 += 0.001;\n        var pathTo = graphics.move(x1, y1);\n        var pathFrom = graphics.move(x1, y1);\n\n        if (w.globals.previousPaths.length > 0) {\n          pathFrom = this.barCtx.getPreviousPath(realIndex, j, false);\n        }\n\n        var sl = graphics.line(x1, y2 - strokeWidth);\n        pathTo = pathTo + graphics.line(x2, y1) + graphics.line(x2, y2 - strokeWidth) + sl + (w.config.plotOptions.bar.borderRadiusApplication === 'around' ? ' Z' : ' z');\n        pathFrom = pathFrom + graphics.line(x1, y1) + sl + sl + sl + sl + sl + graphics.line(x1, y1) + (w.config.plotOptions.bar.borderRadiusApplication === 'around' ? ' Z' : ' z');\n\n        if (this.shouldApplyRadius(realIndex)) {\n          pathTo = graphics.roundPathCorners(pathTo, w.config.plotOptions.bar.borderRadius);\n        }\n\n        if (w.config.chart.stacked) {\n          this.barCtx.xArrj.push(x2);\n          this.barCtx.xArrjF.push(Math.abs(x1 - x2));\n          this.barCtx.xArrjVal.push(this.barCtx.series[i][j]);\n        }\n\n        return {\n          pathTo: pathTo,\n          pathFrom: pathFrom\n        };\n      }\n    }, {\n      key: \"checkZeroSeries\",\n      value: function checkZeroSeries(_ref4) {\n        var series = _ref4.series;\n        var w = this.w;\n\n        for (var zs = 0; zs < series.length; zs++) {\n          var total = 0;\n\n          for (var zsj = 0; zsj < series[w.globals.maxValsInArrayIndex].length; zsj++) {\n            total += series[zs][zsj];\n          }\n\n          if (total === 0) {\n            this.barCtx.zeroSerieses.push(zs);\n          }\n        } // After getting all zeroserieses, we need to ensure whether radiusOnSeriesNumber is not in that zeroseries array\n\n\n        for (var s = series.length - 1; s >= 0; s--) {\n          if (this.barCtx.zeroSerieses.indexOf(s) > -1 && s === this.radiusOnSeriesNumber) {\n            this.barCtx.radiusOnSeriesNumber -= 1;\n          }\n        }\n\n        for (var _s = series.length - 1; _s >= 0; _s--) {\n          if (w.globals.collapsedSeriesIndices.indexOf(this.barCtx.radiusOnSeriesNumber) > -1) {\n            this.barCtx.radiusOnSeriesNumber -= 1;\n          }\n        }\n      }\n    }, {\n      key: \"getXForValue\",\n      value: function getXForValue(value, zeroW) {\n        var zeroPositionForNull = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n        var xForVal = zeroPositionForNull ? zeroW : null;\n\n        if (typeof value !== 'undefined' && value !== null) {\n          xForVal = zeroW + value / this.barCtx.invertedYRatio - (this.barCtx.isReversed ? value / this.barCtx.invertedYRatio : 0) * 2;\n        }\n\n        return xForVal;\n      }\n    }, {\n      key: \"getYForValue\",\n      value: function getYForValue(value, zeroH) {\n        var zeroPositionForNull = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n        var yForVal = zeroPositionForNull ? zeroH : null;\n\n        if (typeof value !== 'undefined' && value !== null) {\n          yForVal = zeroH - value / this.barCtx.yRatio[this.barCtx.yaxisIndex] + (this.barCtx.isReversed ? value / this.barCtx.yRatio[this.barCtx.yaxisIndex] : 0) * 2;\n        }\n\n        return yForVal;\n      }\n    }, {\n      key: \"getGoalValues\",\n      value: function getGoalValues(type, zeroW, zeroH, i, j) {\n        var _this = this;\n\n        var w = this.w;\n        var goals = [];\n\n        if (w.globals.seriesGoals[i] && w.globals.seriesGoals[i][j] && Array.isArray(w.globals.seriesGoals[i][j])) {\n          w.globals.seriesGoals[i][j].forEach(function (goal) {\n            var _goals$push;\n\n            goals.push((_goals$push = {}, _defineProperty(_goals$push, type, type === 'x' ? _this.getXForValue(goal.value, zeroW, false) : _this.getYForValue(goal.value, zeroH, false)), _defineProperty(_goals$push, \"attrs\", goal), _goals$push));\n          });\n        }\n\n        return goals;\n      }\n    }, {\n      key: \"drawGoalLine\",\n      value: function drawGoalLine(_ref5) {\n        var barXPosition = _ref5.barXPosition,\n            barYPosition = _ref5.barYPosition,\n            goalX = _ref5.goalX,\n            goalY = _ref5.goalY,\n            barWidth = _ref5.barWidth,\n            barHeight = _ref5.barHeight;\n        var graphics = new Graphics(this.barCtx.ctx);\n        var lineGroup = graphics.group({\n          className: 'apexcharts-bar-goals-groups'\n        });\n        var line = null;\n\n        if (this.barCtx.isHorizontal) {\n          if (Array.isArray(goalX)) {\n            goalX.forEach(function (goal) {\n              var sHeight = typeof goal.attrs.strokeHeight !== 'undefined' ? goal.attrs.strokeHeight : barHeight / 2;\n              var y = barYPosition + sHeight + barHeight / 2;\n              line = graphics.drawLine(goal.x, y - sHeight * 2, goal.x, y, goal.attrs.strokeColor ? goal.attrs.strokeColor : undefined, goal.attrs.strokeDashArray, goal.attrs.strokeWidth ? goal.attrs.strokeWidth : 2, goal.attrs.strokeLineCap);\n              lineGroup.add(line);\n            });\n          }\n        } else {\n          if (Array.isArray(goalY)) {\n            goalY.forEach(function (goal) {\n              var sWidth = typeof goal.attrs.strokeWidth !== 'undefined' ? goal.attrs.strokeWidth : barWidth / 2;\n              var x = barXPosition + sWidth + barWidth / 2;\n              line = graphics.drawLine(x - sWidth * 2, goal.y, x, goal.y, goal.attrs.strokeColor ? goal.attrs.strokeColor : undefined, goal.attrs.strokeDashArray, goal.attrs.strokeHeight ? goal.attrs.strokeHeight : 2, goal.attrs.strokeLineCap);\n              lineGroup.add(line);\n            });\n          }\n        }\n\n        return lineGroup;\n      }\n    }]);\n\n    return Helpers;\n  }();\n\n  /**\n   * ApexCharts Bar Class responsible for drawing both Columns and Bars.\n   *\n   * @module Bar\n   **/\n\n  var Bar = /*#__PURE__*/function () {\n    function Bar(ctx, xyRatios) {\n      _classCallCheck(this, Bar);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      var w = this.w;\n      this.barOptions = w.config.plotOptions.bar;\n      this.isHorizontal = this.barOptions.horizontal;\n      this.strokeWidth = w.config.stroke.width;\n      this.isNullValue = false;\n      this.isRangeBar = w.globals.seriesRange.length && this.isHorizontal;\n      this.xyRatios = xyRatios;\n\n      if (this.xyRatios !== null) {\n        this.xRatio = xyRatios.xRatio;\n        this.initialXRatio = xyRatios.initialXRatio;\n        this.yRatio = xyRatios.yRatio;\n        this.invertedXRatio = xyRatios.invertedXRatio;\n        this.invertedYRatio = xyRatios.invertedYRatio;\n        this.baseLineY = xyRatios.baseLineY;\n        this.baseLineInvertedY = xyRatios.baseLineInvertedY;\n      }\n\n      this.yaxisIndex = 0;\n      this.seriesLen = 0;\n      var ser = new Series(this.ctx);\n      this.lastActiveBarSerieIndex = ser.getActiveConfigSeriesIndex('desc', ['bar', 'column']);\n      var barSeriesIndices = ser.getBarSeriesIndices();\n      var coreUtils = new CoreUtils(this.ctx);\n      this.stackedSeriesTotals = coreUtils.getStackedSeriesTotals(this.w.config.series.map(function (s, i) {\n        return barSeriesIndices.indexOf(i) === -1 ? i : -1;\n      }).filter(function (s) {\n        return s !== -1;\n      }));\n      this.barHelpers = new Helpers$1(this);\n    }\n    /** primary draw method which is called on bar object\n     * @memberof Bar\n     * @param {array} series - user supplied series values\n     * @param {int} seriesIndex - the index by which series will be drawn on the svg\n     * @return {node} element which is supplied to parent chart draw method for appending\n     **/\n\n\n    _createClass(Bar, [{\n      key: \"draw\",\n      value: function draw(series, seriesIndex) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var coreUtils = new CoreUtils(this.ctx, w);\n        series = coreUtils.getLogSeries(series);\n        this.series = series;\n        this.yRatio = coreUtils.getLogYRatios(this.yRatio);\n        this.barHelpers.initVariables(series);\n        var ret = graphics.group({\n          class: 'apexcharts-bar-series apexcharts-plot-series'\n        });\n\n        if (w.config.dataLabels.enabled) {\n          if (this.totalItems > this.barOptions.dataLabels.maxItems) {\n            console.warn('WARNING: DataLabels are enabled but there are too many to display. This may cause performance issue when rendering.');\n          }\n        }\n\n        for (var i = 0, bc = 0; i < series.length; i++, bc++) {\n          var x = void 0,\n              y = void 0,\n              xDivision = void 0,\n              // xDivision is the GRIDWIDTH divided by number of datapoints (columns)\n          yDivision = void 0,\n              // yDivision is the GRIDHEIGHT divided by number of datapoints (bars)\n          zeroH = void 0,\n              // zeroH is the baseline where 0 meets y axis\n          zeroW = void 0; // zeroW is the baseline where 0 meets x axis\n\n          var yArrj = []; // hold y values of current iterating series\n\n          var xArrj = []; // hold x values of current iterating series\n\n          var realIndex = w.globals.comboCharts ? seriesIndex[i] : i; // el to which series will be drawn\n\n          var elSeries = graphics.group({\n            class: \"apexcharts-series\",\n            rel: i + 1,\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[realIndex]),\n            'data:realIndex': realIndex\n          });\n          this.ctx.series.addCollapsedClassToSeries(elSeries, realIndex);\n\n          if (series[i].length > 0) {\n            this.visibleI = this.visibleI + 1;\n          }\n\n          var barHeight = 0;\n          var barWidth = 0;\n\n          if (this.yRatio.length > 1) {\n            this.yaxisIndex = realIndex;\n          }\n\n          this.isReversed = w.config.yaxis[this.yaxisIndex] && w.config.yaxis[this.yaxisIndex].reversed;\n          var initPositions = this.barHelpers.initialPositions();\n          y = initPositions.y;\n          barHeight = initPositions.barHeight;\n          yDivision = initPositions.yDivision;\n          zeroW = initPositions.zeroW;\n          x = initPositions.x;\n          barWidth = initPositions.barWidth;\n          xDivision = initPositions.xDivision;\n          zeroH = initPositions.zeroH;\n\n          if (!this.horizontal) {\n            xArrj.push(x + barWidth / 2);\n          } // eldatalabels\n\n\n          var elDataLabelsWrap = graphics.group({\n            class: 'apexcharts-datalabels',\n            'data:realIndex': realIndex\n          });\n          var elGoalsMarkers = graphics.group({\n            class: 'apexcharts-bar-goals-markers',\n            style: \"pointer-events: none\"\n          });\n\n          for (var j = 0; j < w.globals.dataPoints; j++) {\n            var strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex);\n            var paths = null;\n            var pathsParams = {\n              indexes: {\n                i: i,\n                j: j,\n                realIndex: realIndex,\n                bc: bc\n              },\n              x: x,\n              y: y,\n              strokeWidth: strokeWidth,\n              elSeries: elSeries\n            };\n\n            if (this.isHorizontal) {\n              paths = this.drawBarPaths(_objectSpread2(_objectSpread2({}, pathsParams), {}, {\n                barHeight: barHeight,\n                zeroW: zeroW,\n                yDivision: yDivision\n              }));\n              barWidth = this.series[i][j] / this.invertedYRatio;\n            } else {\n              paths = this.drawColumnPaths(_objectSpread2(_objectSpread2({}, pathsParams), {}, {\n                xDivision: xDivision,\n                barWidth: barWidth,\n                zeroH: zeroH\n              }));\n              barHeight = this.series[i][j] / this.yRatio[this.yaxisIndex];\n            }\n\n            var barGoalLine = this.barHelpers.drawGoalLine({\n              barXPosition: paths.barXPosition,\n              barYPosition: paths.barYPosition,\n              goalX: paths.goalX,\n              goalY: paths.goalY,\n              barHeight: barHeight,\n              barWidth: barWidth\n            });\n\n            if (barGoalLine) {\n              elGoalsMarkers.add(barGoalLine);\n            }\n\n            y = paths.y;\n            x = paths.x; // push current X\n\n            if (j > 0) {\n              xArrj.push(x + barWidth / 2);\n            }\n\n            yArrj.push(y);\n            var pathFill = this.barHelpers.getPathFillColor(series, i, j, realIndex);\n            this.renderSeries({\n              realIndex: realIndex,\n              pathFill: pathFill,\n              j: j,\n              i: i,\n              pathFrom: paths.pathFrom,\n              pathTo: paths.pathTo,\n              strokeWidth: strokeWidth,\n              elSeries: elSeries,\n              x: x,\n              y: y,\n              series: series,\n              barHeight: barHeight,\n              barWidth: barWidth,\n              elDataLabelsWrap: elDataLabelsWrap,\n              elGoalsMarkers: elGoalsMarkers,\n              visibleSeries: this.visibleI,\n              type: 'bar'\n            });\n          } // push all x val arrays into main xArr\n\n\n          w.globals.seriesXvalues[realIndex] = xArrj;\n          w.globals.seriesYvalues[realIndex] = yArrj;\n          ret.add(elSeries);\n        }\n\n        return ret;\n      }\n    }, {\n      key: \"renderSeries\",\n      value: function renderSeries(_ref) {\n        var realIndex = _ref.realIndex,\n            pathFill = _ref.pathFill,\n            lineFill = _ref.lineFill,\n            j = _ref.j,\n            i = _ref.i,\n            pathFrom = _ref.pathFrom,\n            pathTo = _ref.pathTo,\n            strokeWidth = _ref.strokeWidth,\n            elSeries = _ref.elSeries,\n            x = _ref.x,\n            y = _ref.y,\n            y1 = _ref.y1,\n            y2 = _ref.y2,\n            series = _ref.series,\n            barHeight = _ref.barHeight,\n            barWidth = _ref.barWidth,\n            barYPosition = _ref.barYPosition,\n            elDataLabelsWrap = _ref.elDataLabelsWrap,\n            elGoalsMarkers = _ref.elGoalsMarkers,\n            visibleSeries = _ref.visibleSeries,\n            type = _ref.type;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n\n        if (!lineFill) {\n          /* fix apexcharts#341 */\n          lineFill = this.barOptions.distributed ? w.globals.stroke.colors[j] : w.globals.stroke.colors[realIndex];\n        }\n\n        if (w.config.series[i].data[j] && w.config.series[i].data[j].strokeColor) {\n          lineFill = w.config.series[i].data[j].strokeColor;\n        }\n\n        if (this.isNullValue) {\n          pathFill = 'none';\n        }\n\n        var delay = j / w.config.chart.animations.animateGradually.delay * (w.config.chart.animations.speed / w.globals.dataPoints) / 2.4;\n        var renderedPath = graphics.renderPaths({\n          i: i,\n          j: j,\n          realIndex: realIndex,\n          pathFrom: pathFrom,\n          pathTo: pathTo,\n          stroke: lineFill,\n          strokeWidth: strokeWidth,\n          strokeLineCap: w.config.stroke.lineCap,\n          fill: pathFill,\n          animationDelay: delay,\n          initialSpeed: w.config.chart.animations.speed,\n          dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,\n          className: \"apexcharts-\".concat(type, \"-area\")\n        });\n        renderedPath.attr('clip-path', \"url(#gridRectMask\".concat(w.globals.cuid, \")\"));\n        var forecast = w.config.forecastDataPoints;\n\n        if (forecast.count > 0) {\n          if (j >= w.globals.dataPoints - forecast.count) {\n            renderedPath.node.setAttribute('stroke-dasharray', forecast.dashArray);\n            renderedPath.node.setAttribute('stroke-width', forecast.strokeWidth);\n            renderedPath.node.setAttribute('fill-opacity', forecast.fillOpacity);\n          }\n        }\n\n        if (typeof y1 !== 'undefined' && typeof y2 !== 'undefined') {\n          renderedPath.attr('data-range-y1', y1);\n          renderedPath.attr('data-range-y2', y2);\n        }\n\n        var filters = new Filters(this.ctx);\n        filters.setSelectionFilter(renderedPath, realIndex, j);\n        elSeries.add(renderedPath);\n        var barDataLabels = new BarDataLabels(this);\n        var dataLabelsObj = barDataLabels.handleBarDataLabels({\n          x: x,\n          y: y,\n          y1: y1,\n          y2: y2,\n          i: i,\n          j: j,\n          series: series,\n          realIndex: realIndex,\n          barHeight: barHeight,\n          barWidth: barWidth,\n          barYPosition: barYPosition,\n          renderedPath: renderedPath,\n          visibleSeries: visibleSeries\n        });\n\n        if (dataLabelsObj.dataLabels !== null) {\n          elDataLabelsWrap.add(dataLabelsObj.dataLabels);\n        }\n\n        if (dataLabelsObj.totalDataLabels) {\n          elDataLabelsWrap.add(dataLabelsObj.totalDataLabels);\n        }\n\n        elSeries.add(elDataLabelsWrap);\n\n        if (elGoalsMarkers) {\n          elSeries.add(elGoalsMarkers);\n        }\n\n        return elSeries;\n      }\n    }, {\n      key: \"drawBarPaths\",\n      value: function drawBarPaths(_ref2) {\n        var indexes = _ref2.indexes,\n            barHeight = _ref2.barHeight,\n            strokeWidth = _ref2.strokeWidth,\n            zeroW = _ref2.zeroW,\n            x = _ref2.x,\n            y = _ref2.y,\n            yDivision = _ref2.yDivision,\n            elSeries = _ref2.elSeries;\n        var w = this.w;\n        var i = indexes.i;\n        var j = indexes.j;\n        var barYPosition;\n\n        if (w.globals.isXNumeric) {\n          y = (w.globals.seriesX[i][j] - w.globals.minX) / this.invertedXRatio - barHeight;\n          barYPosition = y + barHeight * this.visibleI;\n        } else {\n          if (w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {\n            var nonZeroColumns = 0;\n            var zeroEncounters = 0;\n            w.globals.seriesPercent.forEach(function (_s, _si) {\n              if (_s[j]) {\n                nonZeroColumns++;\n              }\n\n              if (_si < i && _s[j] === 0) {\n                zeroEncounters++;\n              }\n            });\n            barHeight = this.seriesLen * barHeight / nonZeroColumns;\n            barYPosition = y + barHeight * this.visibleI;\n            barYPosition -= barHeight * zeroEncounters;\n          }\n        }\n\n        x = this.barHelpers.getXForValue(this.series[i][j], zeroW);\n        var paths = this.barHelpers.getBarpaths({\n          barYPosition: barYPosition,\n          barHeight: barHeight,\n          x1: zeroW,\n          x2: x,\n          strokeWidth: strokeWidth,\n          series: this.series,\n          realIndex: indexes.realIndex,\n          i: i,\n          j: j,\n          w: w\n        });\n\n        if (!w.globals.isXNumeric) {\n          y = y + yDivision;\n        }\n\n        this.barHelpers.barBackground({\n          j: j,\n          i: i,\n          y1: barYPosition - barHeight * this.visibleI,\n          y2: barHeight * this.seriesLen,\n          elSeries: elSeries\n        });\n        return {\n          pathTo: paths.pathTo,\n          pathFrom: paths.pathFrom,\n          x: x,\n          y: y,\n          goalX: this.barHelpers.getGoalValues('x', zeroW, null, i, j),\n          barYPosition: barYPosition\n        };\n      }\n    }, {\n      key: \"drawColumnPaths\",\n      value: function drawColumnPaths(_ref3) {\n        var indexes = _ref3.indexes,\n            x = _ref3.x,\n            y = _ref3.y,\n            xDivision = _ref3.xDivision,\n            barWidth = _ref3.barWidth,\n            zeroH = _ref3.zeroH,\n            strokeWidth = _ref3.strokeWidth,\n            elSeries = _ref3.elSeries;\n        var w = this.w;\n        var realIndex = indexes.realIndex;\n        var i = indexes.i;\n        var j = indexes.j;\n        var bc = indexes.bc;\n        var barXPosition;\n\n        if (w.globals.isXNumeric) {\n          var sxI = realIndex;\n\n          if (!w.globals.seriesX[realIndex].length) {\n            sxI = w.globals.maxValsInArrayIndex;\n          }\n\n          if (w.globals.seriesX[sxI][j]) {\n            x = (w.globals.seriesX[sxI][j] - w.globals.minX) / this.xRatio - barWidth * this.seriesLen / 2;\n          } // re-calc barXPosition as x changed\n\n\n          barXPosition = x + barWidth * this.visibleI;\n        } else {\n          if (w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {\n            var nonZeroColumns = 0;\n            var zeroEncounters = 0;\n            w.globals.seriesPercent.forEach(function (_s, _si) {\n              if (_s[j]) {\n                nonZeroColumns++;\n              }\n\n              if (_si < i && _s[j] === 0) {\n                zeroEncounters++;\n              }\n            });\n            barWidth = this.seriesLen * barWidth / nonZeroColumns;\n            barXPosition = x + barWidth * this.visibleI;\n            barXPosition -= barWidth * zeroEncounters;\n          }\n        }\n\n        y = this.barHelpers.getYForValue(this.series[i][j], zeroH);\n        var paths = this.barHelpers.getColumnPaths({\n          barXPosition: barXPosition,\n          barWidth: barWidth,\n          y1: zeroH,\n          y2: y,\n          strokeWidth: strokeWidth,\n          series: this.series,\n          realIndex: indexes.realIndex,\n          i: i,\n          j: j,\n          w: w\n        });\n\n        if (!w.globals.isXNumeric) {\n          x = x + xDivision;\n        }\n\n        this.barHelpers.barBackground({\n          bc: bc,\n          j: j,\n          i: i,\n          x1: barXPosition - strokeWidth / 2 - barWidth * this.visibleI,\n          x2: barWidth * this.seriesLen + strokeWidth / 2,\n          elSeries: elSeries\n        });\n        return {\n          pathTo: paths.pathTo,\n          pathFrom: paths.pathFrom,\n          x: x,\n          y: y,\n          goalY: this.barHelpers.getGoalValues('y', null, zeroH, i, j),\n          barXPosition: barXPosition\n        };\n      }\n      /** getPreviousPath is a common function for bars/columns which is used to get previous paths when data changes.\n       * @memberof Bar\n       * @param {int} realIndex - current iterating i\n       * @param {int} j - current iterating series's j index\n       * @return {string} pathFrom is the string which will be appended in animations\n       **/\n\n    }, {\n      key: \"getPreviousPath\",\n      value: function getPreviousPath(realIndex, j) {\n        var w = this.w;\n        var pathFrom;\n\n        for (var pp = 0; pp < w.globals.previousPaths.length; pp++) {\n          var gpp = w.globals.previousPaths[pp];\n\n          if (gpp.paths && gpp.paths.length > 0 && parseInt(gpp.realIndex, 10) === parseInt(realIndex, 10)) {\n            if (typeof w.globals.previousPaths[pp].paths[j] !== 'undefined') {\n              pathFrom = w.globals.previousPaths[pp].paths[j].d;\n            }\n          }\n        }\n\n        return pathFrom;\n      }\n    }]);\n\n    return Bar;\n  }();\n\n  /**\n   * ApexCharts BarStacked Class responsible for drawing both Stacked Columns and Bars.\n   *\n   * @module BarStacked\n   * The whole calculation for stacked bar/column is different from normal bar/column,\n   * hence it makes sense to derive a new class for it extending most of the props of Parent Bar\n   **/\n\n  var BarStacked = /*#__PURE__*/function (_Bar) {\n    _inherits(BarStacked, _Bar);\n\n    var _super = _createSuper(BarStacked);\n\n    function BarStacked() {\n      _classCallCheck(this, BarStacked);\n\n      return _super.apply(this, arguments);\n    }\n\n    _createClass(BarStacked, [{\n      key: \"draw\",\n      value: function draw(series, seriesIndex) {\n        var _this = this;\n\n        var w = this.w;\n        this.graphics = new Graphics(this.ctx);\n        this.bar = new Bar(this.ctx, this.xyRatios);\n        var coreUtils = new CoreUtils(this.ctx, w);\n        series = coreUtils.getLogSeries(series);\n        this.yRatio = coreUtils.getLogYRatios(this.yRatio);\n        this.barHelpers.initVariables(series);\n\n        if (w.config.chart.stackType === '100%') {\n          series = w.globals.seriesPercent.slice();\n        }\n\n        this.series = series;\n        this.totalItems = 0;\n        this.prevY = []; // y position on chart\n\n        this.prevX = []; // x position on chart\n\n        this.prevYF = []; // y position including shapes on chart\n\n        this.prevXF = []; // x position including shapes on chart\n\n        this.prevYVal = []; // y values (series[i][j]) in columns\n\n        this.prevXVal = []; // x values (series[i][j]) in bars\n\n        this.xArrj = []; // xj indicates x position on graph in bars\n\n        this.xArrjF = []; // xjF indicates bar's x position + roundedShape's positions in bars\n\n        this.xArrjVal = []; // x val means the actual series's y values in horizontal/bars\n\n        this.yArrj = []; // yj indicates y position on graph in columns\n\n        this.yArrjF = []; // yjF indicates bar's y position + roundedShape's positions in columns\n\n        this.yArrjVal = []; // y val means the actual series's y values in columns\n\n        for (var sl = 0; sl < series.length; sl++) {\n          if (series[sl].length > 0) {\n            this.totalItems += series[sl].length;\n          }\n        }\n\n        var ret = this.graphics.group({\n          class: 'apexcharts-bar-series apexcharts-plot-series'\n        });\n        var x = 0;\n        var y = 0;\n\n        var _loop = function _loop(i, bc) {\n          var xDivision = void 0; // xDivision is the GRIDWIDTH divided by number of datapoints (columns)\n\n          var yDivision = void 0; // yDivision is the GRIDHEIGHT divided by number of datapoints (bars)\n\n          var zeroH = void 0; // zeroH is the baseline where 0 meets y axis\n\n          var zeroW = void 0; // zeroW is the baseline where 0 meets x axis\n\n          var xArrValues = [];\n          var yArrValues = [];\n          var realIndex = w.globals.comboCharts ? seriesIndex[i] : i;\n\n          if (_this.yRatio.length > 1) {\n            _this.yaxisIndex = realIndex;\n          }\n\n          _this.isReversed = w.config.yaxis[_this.yaxisIndex] && w.config.yaxis[_this.yaxisIndex].reversed; // el to which series will be drawn\n\n          var elSeries = _this.graphics.group({\n            class: \"apexcharts-series\",\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[realIndex]),\n            rel: i + 1,\n            'data:realIndex': realIndex\n          });\n\n          _this.ctx.series.addCollapsedClassToSeries(elSeries, realIndex); // eldatalabels\n\n\n          var elDataLabelsWrap = _this.graphics.group({\n            class: 'apexcharts-datalabels',\n            'data:realIndex': realIndex\n          });\n\n          var elGoalsMarkers = _this.graphics.group({\n            class: 'apexcharts-bar-goals-markers',\n            style: \"pointer-events: none\"\n          });\n\n          var barHeight = 0;\n          var barWidth = 0;\n\n          var initPositions = _this.initialPositions(x, y, xDivision, yDivision, zeroH, zeroW);\n\n          y = initPositions.y;\n          barHeight = initPositions.barHeight;\n          yDivision = initPositions.yDivision;\n          zeroW = initPositions.zeroW;\n          x = initPositions.x;\n          barWidth = initPositions.barWidth;\n          xDivision = initPositions.xDivision;\n          zeroH = initPositions.zeroH;\n          _this.yArrj = [];\n          _this.yArrjF = [];\n          _this.yArrjVal = [];\n          _this.xArrj = [];\n          _this.xArrjF = [];\n          _this.xArrjVal = []; // if (!this.horizontal) {\n          // this.xArrj.push(x + barWidth / 2)\n          // }\n          // fix issue #1215;\n          // where all stack bar disappear after collapsing the first series\n          // sol: if only 1 arr in this.prevY(this.prevY.length === 1) and all are NaN\n\n          if (_this.prevY.length === 1 && _this.prevY[0].every(function (val) {\n            return isNaN(val);\n          })) {\n            // make this.prevY[0] all zeroH\n            _this.prevY[0] = _this.prevY[0].map(function (val) {\n              return zeroH;\n            }); // make this.prevYF[0] all 0\n\n            _this.prevYF[0] = _this.prevYF[0].map(function (val) {\n              return 0;\n            });\n          }\n\n          for (var j = 0; j < w.globals.dataPoints; j++) {\n            var strokeWidth = _this.barHelpers.getStrokeWidth(i, j, realIndex);\n\n            var commonPathOpts = {\n              indexes: {\n                i: i,\n                j: j,\n                realIndex: realIndex,\n                bc: bc\n              },\n              strokeWidth: strokeWidth,\n              x: x,\n              y: y,\n              elSeries: elSeries\n            };\n            var paths = null;\n\n            if (_this.isHorizontal) {\n              paths = _this.drawStackedBarPaths(_objectSpread2(_objectSpread2({}, commonPathOpts), {}, {\n                zeroW: zeroW,\n                barHeight: barHeight,\n                yDivision: yDivision\n              }));\n              barWidth = _this.series[i][j] / _this.invertedYRatio;\n            } else {\n              paths = _this.drawStackedColumnPaths(_objectSpread2(_objectSpread2({}, commonPathOpts), {}, {\n                xDivision: xDivision,\n                barWidth: barWidth,\n                zeroH: zeroH\n              }));\n              barHeight = _this.series[i][j] / _this.yRatio[_this.yaxisIndex];\n            }\n\n            var barGoalLine = _this.barHelpers.drawGoalLine({\n              barXPosition: paths.barXPosition,\n              barYPosition: paths.barYPosition,\n              goalX: paths.goalX,\n              goalY: paths.goalY,\n              barHeight: barHeight,\n              barWidth: barWidth\n            });\n\n            if (barGoalLine) {\n              elGoalsMarkers.add(barGoalLine);\n            }\n\n            y = paths.y;\n            x = paths.x;\n            xArrValues.push(x);\n            yArrValues.push(y);\n\n            var pathFill = _this.barHelpers.getPathFillColor(series, i, j, realIndex);\n\n            elSeries = _this.renderSeries({\n              realIndex: realIndex,\n              pathFill: pathFill,\n              j: j,\n              i: i,\n              pathFrom: paths.pathFrom,\n              pathTo: paths.pathTo,\n              strokeWidth: strokeWidth,\n              elSeries: elSeries,\n              x: x,\n              y: y,\n              series: series,\n              barHeight: barHeight,\n              barWidth: barWidth,\n              elDataLabelsWrap: elDataLabelsWrap,\n              elGoalsMarkers: elGoalsMarkers,\n              type: 'bar',\n              visibleSeries: 0\n            });\n          } // push all x val arrays into main xArr\n\n\n          w.globals.seriesXvalues[realIndex] = xArrValues;\n          w.globals.seriesYvalues[realIndex] = yArrValues; // push all current y values array to main PrevY Array\n\n          _this.prevY.push(_this.yArrj);\n\n          _this.prevYF.push(_this.yArrjF);\n\n          _this.prevYVal.push(_this.yArrjVal);\n\n          _this.prevX.push(_this.xArrj);\n\n          _this.prevXF.push(_this.xArrjF);\n\n          _this.prevXVal.push(_this.xArrjVal);\n\n          ret.add(elSeries);\n        };\n\n        for (var i = 0, bc = 0; i < series.length; i++, bc++) {\n          _loop(i, bc);\n        }\n\n        return ret;\n      }\n    }, {\n      key: \"initialPositions\",\n      value: function initialPositions(x, y, xDivision, yDivision, zeroH, zeroW) {\n        var w = this.w;\n        var barHeight, barWidth;\n\n        if (this.isHorizontal) {\n          // height divided into equal parts\n          yDivision = w.globals.gridHeight / w.globals.dataPoints;\n          barHeight = yDivision;\n          barHeight = barHeight * parseInt(w.config.plotOptions.bar.barHeight, 10) / 100;\n          zeroW = this.baseLineInvertedY + w.globals.padHorizontal + (this.isReversed ? w.globals.gridWidth : 0) - (this.isReversed ? this.baseLineInvertedY * 2 : 0); // initial y position is half of barHeight * half of number of Bars\n\n          y = (yDivision - barHeight) / 2;\n        } else {\n          // width divided into equal parts\n          xDivision = w.globals.gridWidth / w.globals.dataPoints;\n          barWidth = xDivision;\n\n          if (w.globals.isXNumeric && w.globals.dataPoints > 1) {\n            // the check (w.globals.dataPoints > 1) fixes apexcharts.js #1617\n            xDivision = w.globals.minXDiff / this.xRatio;\n            barWidth = xDivision * parseInt(this.barOptions.columnWidth, 10) / 100;\n          } else {\n            barWidth = barWidth * parseInt(w.config.plotOptions.bar.columnWidth, 10) / 100;\n          }\n\n          zeroH = w.globals.gridHeight - this.baseLineY[this.yaxisIndex] - (this.isReversed ? w.globals.gridHeight : 0) + (this.isReversed ? this.baseLineY[this.yaxisIndex] * 2 : 0); // initial x position is one third of barWidth\n\n          x = w.globals.padHorizontal + (xDivision - barWidth) / 2;\n        }\n\n        return {\n          x: x,\n          y: y,\n          yDivision: yDivision,\n          xDivision: xDivision,\n          barHeight: barHeight,\n          barWidth: barWidth,\n          zeroH: zeroH,\n          zeroW: zeroW\n        };\n      }\n    }, {\n      key: \"drawStackedBarPaths\",\n      value: function drawStackedBarPaths(_ref) {\n        var indexes = _ref.indexes,\n            barHeight = _ref.barHeight,\n            strokeWidth = _ref.strokeWidth,\n            zeroW = _ref.zeroW,\n            x = _ref.x,\n            y = _ref.y,\n            yDivision = _ref.yDivision,\n            elSeries = _ref.elSeries;\n        var w = this.w;\n        var barYPosition = y;\n        var barXPosition;\n        var i = indexes.i;\n        var j = indexes.j;\n        var prevBarW = 0;\n\n        for (var k = 0; k < this.prevXF.length; k++) {\n          prevBarW = prevBarW + this.prevXF[k][j];\n        }\n\n        if (i > 0) {\n          var bXP = zeroW;\n\n          if (this.prevXVal[i - 1][j] < 0) {\n            bXP = this.series[i][j] >= 0 ? this.prevX[i - 1][j] + prevBarW - (this.isReversed ? prevBarW : 0) * 2 : this.prevX[i - 1][j];\n          } else if (this.prevXVal[i - 1][j] >= 0) {\n            bXP = this.series[i][j] >= 0 ? this.prevX[i - 1][j] : this.prevX[i - 1][j] - prevBarW + (this.isReversed ? prevBarW : 0) * 2;\n          }\n\n          barXPosition = bXP;\n        } else {\n          // the first series will not have prevX values\n          barXPosition = zeroW;\n        }\n\n        if (this.series[i][j] === null) {\n          x = barXPosition;\n        } else {\n          x = barXPosition + this.series[i][j] / this.invertedYRatio - (this.isReversed ? this.series[i][j] / this.invertedYRatio : 0) * 2;\n        }\n\n        var paths = this.barHelpers.getBarpaths({\n          barYPosition: barYPosition,\n          barHeight: barHeight,\n          x1: barXPosition,\n          x2: x,\n          strokeWidth: strokeWidth,\n          series: this.series,\n          realIndex: indexes.realIndex,\n          i: i,\n          j: j,\n          w: w\n        });\n        this.barHelpers.barBackground({\n          j: j,\n          i: i,\n          y1: barYPosition,\n          y2: barHeight,\n          elSeries: elSeries\n        });\n        y = y + yDivision;\n        return {\n          pathTo: paths.pathTo,\n          pathFrom: paths.pathFrom,\n          goalX: this.barHelpers.getGoalValues('x', zeroW, null, i, j),\n          barYPosition: barYPosition,\n          x: x,\n          y: y\n        };\n      }\n    }, {\n      key: \"drawStackedColumnPaths\",\n      value: function drawStackedColumnPaths(_ref2) {\n        var indexes = _ref2.indexes,\n            x = _ref2.x,\n            y = _ref2.y,\n            xDivision = _ref2.xDivision,\n            barWidth = _ref2.barWidth,\n            zeroH = _ref2.zeroH;\n            _ref2.strokeWidth;\n            var elSeries = _ref2.elSeries;\n        var w = this.w;\n        var i = indexes.i;\n        var j = indexes.j;\n        var bc = indexes.bc;\n\n        if (w.globals.isXNumeric) {\n          var seriesVal = w.globals.seriesX[i][j];\n          if (!seriesVal) seriesVal = 0;\n          x = (seriesVal - w.globals.minX) / this.xRatio - barWidth / 2;\n        }\n\n        var barXPosition = x;\n        var barYPosition;\n        var prevBarH = 0;\n\n        for (var k = 0; k < this.prevYF.length; k++) {\n          // fix issue #1215\n          // in case where this.prevYF[k][j] is NaN, use 0 instead\n          prevBarH = prevBarH + (!isNaN(this.prevYF[k][j]) ? this.prevYF[k][j] : 0);\n        }\n\n        if (i > 0 && !w.globals.isXNumeric || i > 0 && w.globals.isXNumeric && w.globals.seriesX[i - 1][j] === w.globals.seriesX[i][j]) {\n          var bYP;\n          var prevYValue;\n          var p = Math.min(this.yRatio.length + 1, i + 1);\n\n          if (this.prevY[i - 1] !== undefined) {\n            for (var ii = 1; ii < p; ii++) {\n              if (!isNaN(this.prevY[i - ii][j])) {\n                // find the previous available value to give prevYValue\n                prevYValue = this.prevY[i - ii][j]; // if found it, break the loop\n\n                break;\n              }\n            }\n          }\n\n          for (var _ii = 1; _ii < p; _ii++) {\n            // find the previous available value(non-NaN) to give bYP\n            if (this.prevYVal[i - _ii][j] < 0) {\n              bYP = this.series[i][j] >= 0 ? prevYValue - prevBarH + (this.isReversed ? prevBarH : 0) * 2 : prevYValue; // found it? break the loop\n\n              break;\n            } else if (this.prevYVal[i - _ii][j] >= 0) {\n              bYP = this.series[i][j] >= 0 ? prevYValue : prevYValue + prevBarH - (this.isReversed ? prevBarH : 0) * 2; // found it? break the loop\n\n              break;\n            }\n          }\n\n          if (typeof bYP === 'undefined') bYP = w.globals.gridHeight; // if this.prevYF[0] is all 0 resulted from line #486\n          // AND every arr starting from the second only contains NaN\n\n          if (this.prevYF[0].every(function (val) {\n            return val === 0;\n          }) && this.prevYF.slice(1, i).every(function (arr) {\n            return arr.every(function (val) {\n              return isNaN(val);\n            });\n          })) {\n            barYPosition = zeroH;\n          } else {\n            // Nothing special\n            barYPosition = bYP;\n          }\n        } else {\n          // the first series will not have prevY values, also if the prev index's series X doesn't matches the current index's series X, then start from zero\n          barYPosition = zeroH;\n        }\n\n        if (this.series[i][j]) {\n          y = barYPosition - this.series[i][j] / this.yRatio[this.yaxisIndex] + (this.isReversed ? this.series[i][j] / this.yRatio[this.yaxisIndex] : 0) * 2;\n        } else {\n          // fixes #3610\n          y = barYPosition;\n        }\n\n        var paths = this.barHelpers.getColumnPaths({\n          barXPosition: barXPosition,\n          barWidth: barWidth,\n          y1: barYPosition,\n          y2: y,\n          yRatio: this.yRatio[this.yaxisIndex],\n          strokeWidth: this.strokeWidth,\n          series: this.series,\n          realIndex: indexes.realIndex,\n          i: i,\n          j: j,\n          w: w\n        });\n        this.barHelpers.barBackground({\n          bc: bc,\n          j: j,\n          i: i,\n          x1: barXPosition,\n          x2: barWidth,\n          elSeries: elSeries\n        });\n        x = x + xDivision;\n        return {\n          pathTo: paths.pathTo,\n          pathFrom: paths.pathFrom,\n          goalY: this.barHelpers.getGoalValues('y', null, zeroH, i, j),\n          barXPosition: barXPosition,\n          x: w.globals.isXNumeric ? x - xDivision : x,\n          y: y\n        };\n      }\n    }]);\n\n    return BarStacked;\n  }(Bar);\n\n  /**\n   * ApexCharts BoxCandleStick Class responsible for drawing both Stacked Columns and Bars.\n   *\n   * @module BoxCandleStick\n   **/\n\n  var BoxCandleStick = /*#__PURE__*/function (_Bar) {\n    _inherits(BoxCandleStick, _Bar);\n\n    var _super = _createSuper(BoxCandleStick);\n\n    function BoxCandleStick() {\n      _classCallCheck(this, BoxCandleStick);\n\n      return _super.apply(this, arguments);\n    }\n\n    _createClass(BoxCandleStick, [{\n      key: \"draw\",\n      value: function draw(series, seriesIndex) {\n        var _this = this;\n\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var fill = new Fill(this.ctx);\n        this.candlestickOptions = this.w.config.plotOptions.candlestick;\n        this.boxOptions = this.w.config.plotOptions.boxPlot;\n        this.isHorizontal = w.config.plotOptions.bar.horizontal;\n        var coreUtils = new CoreUtils(this.ctx, w);\n        series = coreUtils.getLogSeries(series);\n        this.series = series;\n        this.yRatio = coreUtils.getLogYRatios(this.yRatio);\n        this.barHelpers.initVariables(series);\n        var ret = graphics.group({\n          class: \"apexcharts-\".concat(w.config.chart.type, \"-series apexcharts-plot-series\")\n        });\n\n        var _loop = function _loop(i) {\n          _this.isBoxPlot = w.config.chart.type === 'boxPlot' || w.config.series[i].type === 'boxPlot';\n          var x = void 0,\n              y = void 0,\n              xDivision = void 0,\n              // xDivision is the GRIDWIDTH divided by number of datapoints (columns)\n          yDivision = void 0,\n              // yDivision is the GRIDHEIGHT divided by number of datapoints (bars)\n          zeroH = void 0,\n              // zeroH is the baseline where 0 meets y axis\n          zeroW = void 0; // zeroW is the baseline where 0 meets x axis\n\n          var yArrj = []; // hold y values of current iterating series\n\n          var xArrj = []; // hold x values of current iterating series\n\n          var realIndex = w.globals.comboCharts ? seriesIndex[i] : i; // el to which series will be drawn\n\n          var elSeries = graphics.group({\n            class: \"apexcharts-series\",\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[realIndex]),\n            rel: i + 1,\n            'data:realIndex': realIndex\n          });\n\n          _this.ctx.series.addCollapsedClassToSeries(elSeries, realIndex);\n\n          if (series[i].length > 0) {\n            _this.visibleI = _this.visibleI + 1;\n          }\n\n          var barHeight = 0;\n          var barWidth = 0;\n\n          if (_this.yRatio.length > 1) {\n            _this.yaxisIndex = realIndex;\n          }\n\n          var initPositions = _this.barHelpers.initialPositions();\n\n          y = initPositions.y;\n          barHeight = initPositions.barHeight;\n          yDivision = initPositions.yDivision;\n          zeroW = initPositions.zeroW;\n          x = initPositions.x;\n          barWidth = initPositions.barWidth;\n          xDivision = initPositions.xDivision;\n          zeroH = initPositions.zeroH;\n          xArrj.push(x + barWidth / 2); // eldatalabels\n\n          var elDataLabelsWrap = graphics.group({\n            class: 'apexcharts-datalabels',\n            'data:realIndex': realIndex\n          });\n\n          var _loop2 = function _loop2(j) {\n            var strokeWidth = _this.barHelpers.getStrokeWidth(i, j, realIndex);\n\n            var paths = null;\n            var pathsParams = {\n              indexes: {\n                i: i,\n                j: j,\n                realIndex: realIndex\n              },\n              x: x,\n              y: y,\n              strokeWidth: strokeWidth,\n              elSeries: elSeries\n            };\n\n            if (_this.isHorizontal) {\n              paths = _this.drawHorizontalBoxPaths(_objectSpread2(_objectSpread2({}, pathsParams), {}, {\n                yDivision: yDivision,\n                barHeight: barHeight,\n                zeroW: zeroW\n              }));\n            } else {\n              paths = _this.drawVerticalBoxPaths(_objectSpread2(_objectSpread2({}, pathsParams), {}, {\n                xDivision: xDivision,\n                barWidth: barWidth,\n                zeroH: zeroH\n              }));\n            }\n\n            y = paths.y;\n            x = paths.x; // push current X\n\n            if (j > 0) {\n              xArrj.push(x + barWidth / 2);\n            }\n\n            yArrj.push(y);\n            paths.pathTo.forEach(function (pathTo, pi) {\n              var lineFill = !_this.isBoxPlot && _this.candlestickOptions.wick.useFillColor ? paths.color[pi] : w.globals.stroke.colors[i];\n              var pathFill = fill.fillPath({\n                seriesNumber: realIndex,\n                dataPointIndex: j,\n                color: paths.color[pi],\n                value: series[i][j]\n              });\n\n              _this.renderSeries({\n                realIndex: realIndex,\n                pathFill: pathFill,\n                lineFill: lineFill,\n                j: j,\n                i: i,\n                pathFrom: paths.pathFrom,\n                pathTo: pathTo,\n                strokeWidth: strokeWidth,\n                elSeries: elSeries,\n                x: x,\n                y: y,\n                series: series,\n                barHeight: barHeight,\n                barWidth: barWidth,\n                elDataLabelsWrap: elDataLabelsWrap,\n                visibleSeries: _this.visibleI,\n                type: w.config.chart.type\n              });\n            });\n          };\n\n          for (var j = 0; j < w.globals.dataPoints; j++) {\n            _loop2(j);\n          } // push all x val arrays into main xArr\n\n\n          w.globals.seriesXvalues[realIndex] = xArrj;\n          w.globals.seriesYvalues[realIndex] = yArrj;\n          ret.add(elSeries);\n        };\n\n        for (var i = 0; i < series.length; i++) {\n          _loop(i);\n        }\n\n        return ret;\n      }\n    }, {\n      key: \"drawVerticalBoxPaths\",\n      value: function drawVerticalBoxPaths(_ref) {\n        var indexes = _ref.indexes,\n            x = _ref.x;\n            _ref.y;\n            var xDivision = _ref.xDivision,\n            barWidth = _ref.barWidth,\n            zeroH = _ref.zeroH,\n            strokeWidth = _ref.strokeWidth;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var i = indexes.i;\n        var j = indexes.j;\n        var isPositive = true;\n        var colorPos = w.config.plotOptions.candlestick.colors.upward;\n        var colorNeg = w.config.plotOptions.candlestick.colors.downward;\n        var color = '';\n\n        if (this.isBoxPlot) {\n          color = [this.boxOptions.colors.lower, this.boxOptions.colors.upper];\n        }\n\n        var yRatio = this.yRatio[this.yaxisIndex];\n        var realIndex = indexes.realIndex;\n        var ohlc = this.getOHLCValue(realIndex, j);\n        var l1 = zeroH;\n        var l2 = zeroH;\n\n        if (ohlc.o > ohlc.c) {\n          isPositive = false;\n        }\n\n        var y1 = Math.min(ohlc.o, ohlc.c);\n        var y2 = Math.max(ohlc.o, ohlc.c);\n        var m = ohlc.m;\n\n        if (w.globals.isXNumeric) {\n          x = (w.globals.seriesX[realIndex][j] - w.globals.minX) / this.xRatio - barWidth / 2;\n        }\n\n        var barXPosition = x + barWidth * this.visibleI;\n\n        if (typeof this.series[i][j] === 'undefined' || this.series[i][j] === null) {\n          y1 = zeroH;\n          y2 = zeroH;\n        } else {\n          y1 = zeroH - y1 / yRatio;\n          y2 = zeroH - y2 / yRatio;\n          l1 = zeroH - ohlc.h / yRatio;\n          l2 = zeroH - ohlc.l / yRatio;\n          m = zeroH - ohlc.m / yRatio;\n        }\n\n        var pathTo = graphics.move(barXPosition, zeroH);\n        var pathFrom = graphics.move(barXPosition + barWidth / 2, y1);\n\n        if (w.globals.previousPaths.length > 0) {\n          pathFrom = this.getPreviousPath(realIndex, j, true);\n        }\n\n        if (this.isBoxPlot) {\n          pathTo = [graphics.move(barXPosition, y1) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition + barWidth / 2, l1) + graphics.line(barXPosition + barWidth / 4, l1) + graphics.line(barXPosition + barWidth - barWidth / 4, l1) + graphics.line(barXPosition + barWidth / 2, l1) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition + barWidth, y1) + graphics.line(barXPosition + barWidth, m) + graphics.line(barXPosition, m) + graphics.line(barXPosition, y1 + strokeWidth / 2), graphics.move(barXPosition, m) + graphics.line(barXPosition + barWidth, m) + graphics.line(barXPosition + barWidth, y2) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition + barWidth / 2, l2) + graphics.line(barXPosition + barWidth - barWidth / 4, l2) + graphics.line(barXPosition + barWidth / 4, l2) + graphics.line(barXPosition + barWidth / 2, l2) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition, y2) + graphics.line(barXPosition, m) + 'z'];\n        } else {\n          // candlestick\n          pathTo = [graphics.move(barXPosition, y2) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition + barWidth / 2, l1) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition + barWidth, y2) + graphics.line(barXPosition + barWidth, y1) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition + barWidth / 2, l2) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition, y1) + graphics.line(barXPosition, y2 - strokeWidth / 2)];\n        }\n\n        pathFrom = pathFrom + graphics.move(barXPosition, y1);\n\n        if (!w.globals.isXNumeric) {\n          x = x + xDivision;\n        }\n\n        return {\n          pathTo: pathTo,\n          pathFrom: pathFrom,\n          x: x,\n          y: y2,\n          barXPosition: barXPosition,\n          color: this.isBoxPlot ? color : isPositive ? [colorPos] : [colorNeg]\n        };\n      }\n    }, {\n      key: \"drawHorizontalBoxPaths\",\n      value: function drawHorizontalBoxPaths(_ref2) {\n        var indexes = _ref2.indexes;\n            _ref2.x;\n            var y = _ref2.y,\n            yDivision = _ref2.yDivision,\n            barHeight = _ref2.barHeight,\n            zeroW = _ref2.zeroW,\n            strokeWidth = _ref2.strokeWidth;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var i = indexes.i;\n        var j = indexes.j;\n        var color = this.boxOptions.colors.lower;\n\n        if (this.isBoxPlot) {\n          color = [this.boxOptions.colors.lower, this.boxOptions.colors.upper];\n        }\n\n        var yRatio = this.invertedYRatio;\n        var realIndex = indexes.realIndex;\n        var ohlc = this.getOHLCValue(realIndex, j);\n        var l1 = zeroW;\n        var l2 = zeroW;\n        var x1 = Math.min(ohlc.o, ohlc.c);\n        var x2 = Math.max(ohlc.o, ohlc.c);\n        var m = ohlc.m;\n\n        if (w.globals.isXNumeric) {\n          y = (w.globals.seriesX[realIndex][j] - w.globals.minX) / this.invertedXRatio - barHeight / 2;\n        }\n\n        var barYPosition = y + barHeight * this.visibleI;\n\n        if (typeof this.series[i][j] === 'undefined' || this.series[i][j] === null) {\n          x1 = zeroW;\n          x2 = zeroW;\n        } else {\n          x1 = zeroW + x1 / yRatio;\n          x2 = zeroW + x2 / yRatio;\n          l1 = zeroW + ohlc.h / yRatio;\n          l2 = zeroW + ohlc.l / yRatio;\n          m = zeroW + ohlc.m / yRatio;\n        }\n\n        var pathTo = graphics.move(zeroW, barYPosition);\n        var pathFrom = graphics.move(x1, barYPosition + barHeight / 2);\n\n        if (w.globals.previousPaths.length > 0) {\n          pathFrom = this.getPreviousPath(realIndex, j, true);\n        }\n\n        pathTo = [graphics.move(x1, barYPosition) + graphics.line(x1, barYPosition + barHeight / 2) + graphics.line(l1, barYPosition + barHeight / 2) + graphics.line(l1, barYPosition + barHeight / 2 - barHeight / 4) + graphics.line(l1, barYPosition + barHeight / 2 + barHeight / 4) + graphics.line(l1, barYPosition + barHeight / 2) + graphics.line(x1, barYPosition + barHeight / 2) + graphics.line(x1, barYPosition + barHeight) + graphics.line(m, barYPosition + barHeight) + graphics.line(m, barYPosition) + graphics.line(x1 + strokeWidth / 2, barYPosition), graphics.move(m, barYPosition) + graphics.line(m, barYPosition + barHeight) + graphics.line(x2, barYPosition + barHeight) + graphics.line(x2, barYPosition + barHeight / 2) + graphics.line(l2, barYPosition + barHeight / 2) + graphics.line(l2, barYPosition + barHeight - barHeight / 4) + graphics.line(l2, barYPosition + barHeight / 4) + graphics.line(l2, barYPosition + barHeight / 2) + graphics.line(x2, barYPosition + barHeight / 2) + graphics.line(x2, barYPosition) + graphics.line(m, barYPosition) + 'z'];\n        pathFrom = pathFrom + graphics.move(x1, barYPosition);\n\n        if (!w.globals.isXNumeric) {\n          y = y + yDivision;\n        }\n\n        return {\n          pathTo: pathTo,\n          pathFrom: pathFrom,\n          x: x2,\n          y: y,\n          barYPosition: barYPosition,\n          color: color\n        };\n      }\n    }, {\n      key: \"getOHLCValue\",\n      value: function getOHLCValue(i, j) {\n        var w = this.w;\n        return {\n          o: this.isBoxPlot ? w.globals.seriesCandleH[i][j] : w.globals.seriesCandleO[i][j],\n          h: this.isBoxPlot ? w.globals.seriesCandleO[i][j] : w.globals.seriesCandleH[i][j],\n          m: w.globals.seriesCandleM[i][j],\n          l: this.isBoxPlot ? w.globals.seriesCandleC[i][j] : w.globals.seriesCandleL[i][j],\n          c: this.isBoxPlot ? w.globals.seriesCandleL[i][j] : w.globals.seriesCandleC[i][j]\n        };\n      }\n    }]);\n\n    return BoxCandleStick;\n  }(Bar);\n\n  var TreemapHelpers = /*#__PURE__*/function () {\n    function TreemapHelpers(ctx) {\n      _classCallCheck(this, TreemapHelpers);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(TreemapHelpers, [{\n      key: \"checkColorRange\",\n      value: function checkColorRange() {\n        var w = this.w;\n        var negRange = false;\n        var chartOpts = w.config.plotOptions[w.config.chart.type];\n\n        if (chartOpts.colorScale.ranges.length > 0) {\n          chartOpts.colorScale.ranges.map(function (range, index) {\n            if (range.from <= 0) {\n              negRange = true;\n            }\n          });\n        }\n\n        return negRange;\n      }\n    }, {\n      key: \"getShadeColor\",\n      value: function getShadeColor(chartType, i, j, negRange) {\n        var w = this.w;\n        var colorShadePercent = 1;\n        var shadeIntensity = w.config.plotOptions[chartType].shadeIntensity;\n        var colorProps = this.determineColor(chartType, i, j);\n\n        if (w.globals.hasNegs || negRange) {\n          if (w.config.plotOptions[chartType].reverseNegativeShade) {\n            if (colorProps.percent < 0) {\n              colorShadePercent = colorProps.percent / 100 * (shadeIntensity * 1.25);\n            } else {\n              colorShadePercent = (1 - colorProps.percent / 100) * (shadeIntensity * 1.25);\n            }\n          } else {\n            if (colorProps.percent <= 0) {\n              colorShadePercent = 1 - (1 + colorProps.percent / 100) * shadeIntensity;\n            } else {\n              colorShadePercent = (1 - colorProps.percent / 100) * shadeIntensity;\n            }\n          }\n        } else {\n          colorShadePercent = 1 - colorProps.percent / 100;\n\n          if (chartType === 'treemap') {\n            colorShadePercent = (1 - colorProps.percent / 100) * (shadeIntensity * 1.25);\n          }\n        }\n\n        var color = colorProps.color;\n        var utils = new Utils$1();\n\n        if (w.config.plotOptions[chartType].enableShades) {\n          if (this.w.config.theme.mode === 'dark') {\n            color = Utils$1.hexToRgba(utils.shadeColor(colorShadePercent * -1, colorProps.color), w.config.fill.opacity);\n          } else {\n            color = Utils$1.hexToRgba(utils.shadeColor(colorShadePercent, colorProps.color), w.config.fill.opacity);\n          }\n        }\n\n        return {\n          color: color,\n          colorProps: colorProps\n        };\n      }\n    }, {\n      key: \"determineColor\",\n      value: function determineColor(chartType, i, j) {\n        var w = this.w;\n        var val = w.globals.series[i][j];\n        var chartOpts = w.config.plotOptions[chartType];\n        var seriesNumber = chartOpts.colorScale.inverse ? j : i;\n\n        if (chartOpts.distributed && w.config.chart.type === 'treemap') {\n          seriesNumber = j;\n        }\n\n        var color = w.globals.colors[seriesNumber];\n        var foreColor = null;\n        var min = Math.min.apply(Math, _toConsumableArray(w.globals.series[i]));\n        var max = Math.max.apply(Math, _toConsumableArray(w.globals.series[i]));\n\n        if (!chartOpts.distributed && chartType === 'heatmap') {\n          min = w.globals.minY;\n          max = w.globals.maxY;\n        }\n\n        if (typeof chartOpts.colorScale.min !== 'undefined') {\n          min = chartOpts.colorScale.min < w.globals.minY ? chartOpts.colorScale.min : w.globals.minY;\n          max = chartOpts.colorScale.max > w.globals.maxY ? chartOpts.colorScale.max : w.globals.maxY;\n        }\n\n        var total = Math.abs(max) + Math.abs(min);\n        var percent = 100 * val / (total === 0 ? total - 0.000001 : total);\n\n        if (chartOpts.colorScale.ranges.length > 0) {\n          var colorRange = chartOpts.colorScale.ranges;\n          colorRange.map(function (range, index) {\n            if (val >= range.from && val <= range.to) {\n              color = range.color;\n              foreColor = range.foreColor ? range.foreColor : null;\n              min = range.from;\n              max = range.to;\n              var rTotal = Math.abs(max) + Math.abs(min);\n              percent = 100 * val / (rTotal === 0 ? rTotal - 0.000001 : rTotal);\n            }\n          });\n        }\n\n        return {\n          color: color,\n          foreColor: foreColor,\n          percent: percent\n        };\n      }\n    }, {\n      key: \"calculateDataLabels\",\n      value: function calculateDataLabels(_ref) {\n        var text = _ref.text,\n            x = _ref.x,\n            y = _ref.y,\n            i = _ref.i,\n            j = _ref.j,\n            colorProps = _ref.colorProps,\n            fontSize = _ref.fontSize;\n        var w = this.w;\n        var dataLabelsConfig = w.config.dataLabels;\n        var graphics = new Graphics(this.ctx);\n        var dataLabels = new DataLabels(this.ctx);\n        var elDataLabelsWrap = null;\n\n        if (dataLabelsConfig.enabled) {\n          elDataLabelsWrap = graphics.group({\n            class: 'apexcharts-data-labels'\n          });\n          var offX = dataLabelsConfig.offsetX;\n          var offY = dataLabelsConfig.offsetY;\n          var dataLabelsX = x + offX;\n          var dataLabelsY = y + parseFloat(dataLabelsConfig.style.fontSize) / 3 + offY;\n          dataLabels.plotDataLabelsText({\n            x: dataLabelsX,\n            y: dataLabelsY,\n            text: text,\n            i: i,\n            j: j,\n            color: colorProps.foreColor,\n            parent: elDataLabelsWrap,\n            fontSize: fontSize,\n            dataLabelsConfig: dataLabelsConfig\n          });\n        }\n\n        return elDataLabelsWrap;\n      }\n    }, {\n      key: \"addListeners\",\n      value: function addListeners(elRect) {\n        var graphics = new Graphics(this.ctx);\n        elRect.node.addEventListener('mouseenter', graphics.pathMouseEnter.bind(this, elRect));\n        elRect.node.addEventListener('mouseleave', graphics.pathMouseLeave.bind(this, elRect));\n        elRect.node.addEventListener('mousedown', graphics.pathMouseDown.bind(this, elRect));\n      }\n    }]);\n\n    return TreemapHelpers;\n  }();\n\n  /**\n   * ApexCharts HeatMap Class.\n   * @module HeatMap\n   **/\n\n  var HeatMap = /*#__PURE__*/function () {\n    function HeatMap(ctx, xyRatios) {\n      _classCallCheck(this, HeatMap);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.xRatio = xyRatios.xRatio;\n      this.yRatio = xyRatios.yRatio;\n      this.dynamicAnim = this.w.config.chart.animations.dynamicAnimation;\n      this.helpers = new TreemapHelpers(ctx);\n      this.rectRadius = this.w.config.plotOptions.heatmap.radius;\n      this.strokeWidth = this.w.config.stroke.show ? this.w.config.stroke.width : 0;\n    }\n\n    _createClass(HeatMap, [{\n      key: \"draw\",\n      value: function draw(series) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var ret = graphics.group({\n          class: 'apexcharts-heatmap'\n        });\n        ret.attr('clip-path', \"url(#gridRectMask\".concat(w.globals.cuid, \")\")); // width divided into equal parts\n\n        var xDivision = w.globals.gridWidth / w.globals.dataPoints;\n        var yDivision = w.globals.gridHeight / w.globals.series.length;\n        var y1 = 0;\n        var rev = false;\n        this.negRange = this.helpers.checkColorRange();\n        var heatSeries = series.slice();\n\n        if (w.config.yaxis[0].reversed) {\n          rev = true;\n          heatSeries.reverse();\n        }\n\n        for (var i = rev ? 0 : heatSeries.length - 1; rev ? i < heatSeries.length : i >= 0; rev ? i++ : i--) {\n          // el to which series will be drawn\n          var elSeries = graphics.group({\n            class: \"apexcharts-series apexcharts-heatmap-series\",\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[i]),\n            rel: i + 1,\n            'data:realIndex': i\n          });\n          this.ctx.series.addCollapsedClassToSeries(elSeries, i);\n\n          if (w.config.chart.dropShadow.enabled) {\n            var shadow = w.config.chart.dropShadow;\n            var filters = new Filters(this.ctx);\n            filters.dropShadow(elSeries, shadow, i);\n          }\n\n          var x1 = 0;\n          var shadeIntensity = w.config.plotOptions.heatmap.shadeIntensity;\n\n          for (var j = 0; j < heatSeries[i].length; j++) {\n            var heatColor = this.helpers.getShadeColor(w.config.chart.type, i, j, this.negRange);\n            var color = heatColor.color;\n            var heatColorProps = heatColor.colorProps;\n\n            if (w.config.fill.type === 'image') {\n              var fill = new Fill(this.ctx);\n              color = fill.fillPath({\n                seriesNumber: i,\n                dataPointIndex: j,\n                opacity: w.globals.hasNegs ? heatColorProps.percent < 0 ? 1 - (1 + heatColorProps.percent / 100) : shadeIntensity + heatColorProps.percent / 100 : heatColorProps.percent / 100,\n                patternID: Utils$1.randomId(),\n                width: w.config.fill.image.width ? w.config.fill.image.width : xDivision,\n                height: w.config.fill.image.height ? w.config.fill.image.height : yDivision\n              });\n            }\n\n            var radius = this.rectRadius;\n            var rect = graphics.drawRect(x1, y1, xDivision, yDivision, radius);\n            rect.attr({\n              cx: x1,\n              cy: y1\n            });\n            rect.node.classList.add('apexcharts-heatmap-rect');\n            elSeries.add(rect);\n            rect.attr({\n              fill: color,\n              i: i,\n              index: i,\n              j: j,\n              val: heatSeries[i][j],\n              'stroke-width': this.strokeWidth,\n              stroke: w.config.plotOptions.heatmap.useFillColorAsStroke ? color : w.globals.stroke.colors[0],\n              color: color\n            });\n            this.helpers.addListeners(rect);\n\n            if (w.config.chart.animations.enabled && !w.globals.dataChanged) {\n              var speed = 1;\n\n              if (!w.globals.resized) {\n                speed = w.config.chart.animations.speed;\n              }\n\n              this.animateHeatMap(rect, x1, y1, xDivision, yDivision, speed);\n            }\n\n            if (w.globals.dataChanged) {\n              var _speed = 1;\n\n              if (this.dynamicAnim.enabled && w.globals.shouldAnimate) {\n                _speed = this.dynamicAnim.speed;\n                var colorFrom = w.globals.previousPaths[i] && w.globals.previousPaths[i][j] && w.globals.previousPaths[i][j].color;\n                if (!colorFrom) colorFrom = 'rgba(255, 255, 255, 0)';\n                this.animateHeatColor(rect, Utils$1.isColorHex(colorFrom) ? colorFrom : Utils$1.rgb2hex(colorFrom), Utils$1.isColorHex(color) ? color : Utils$1.rgb2hex(color), _speed);\n              }\n            }\n\n            var formatter = w.config.dataLabels.formatter;\n            var formattedText = formatter(w.globals.series[i][j], {\n              value: w.globals.series[i][j],\n              seriesIndex: i,\n              dataPointIndex: j,\n              w: w\n            });\n            var dataLabels = this.helpers.calculateDataLabels({\n              text: formattedText,\n              x: x1 + xDivision / 2,\n              y: y1 + yDivision / 2,\n              i: i,\n              j: j,\n              colorProps: heatColorProps,\n              series: heatSeries\n            });\n\n            if (dataLabels !== null) {\n              elSeries.add(dataLabels);\n            }\n\n            x1 = x1 + xDivision;\n          }\n\n          y1 = y1 + yDivision;\n          ret.add(elSeries);\n        } // adjust yaxis labels for heatmap\n\n\n        var yAxisScale = w.globals.yAxisScale[0].result.slice();\n\n        if (w.config.yaxis[0].reversed) {\n          yAxisScale.unshift('');\n        } else {\n          yAxisScale.push('');\n        }\n\n        w.globals.yAxisScale[0].result = yAxisScale;\n        var divisor = w.globals.gridHeight / w.globals.series.length;\n        w.config.yaxis[0].labels.offsetY = -(divisor / 2);\n        return ret;\n      }\n    }, {\n      key: \"animateHeatMap\",\n      value: function animateHeatMap(el, x, y, width, height, speed) {\n        var animations = new Animations(this.ctx);\n        animations.animateRect(el, {\n          x: x + width / 2,\n          y: y + height / 2,\n          width: 0,\n          height: 0\n        }, {\n          x: x,\n          y: y,\n          width: width,\n          height: height\n        }, speed, function () {\n          animations.animationCompleted(el);\n        });\n      }\n    }, {\n      key: \"animateHeatColor\",\n      value: function animateHeatColor(el, colorFrom, colorTo, speed) {\n        el.attr({\n          fill: colorFrom\n        }).animate(speed).attr({\n          fill: colorTo\n        });\n      }\n    }]);\n\n    return HeatMap;\n  }();\n\n  var CircularChartsHelpers = /*#__PURE__*/function () {\n    function CircularChartsHelpers(ctx) {\n      _classCallCheck(this, CircularChartsHelpers);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(CircularChartsHelpers, [{\n      key: \"drawYAxisTexts\",\n      value: function drawYAxisTexts(x, y, i, text) {\n        var w = this.w;\n        var yaxisConfig = w.config.yaxis[0];\n        var formatter = w.globals.yLabelFormatters[0];\n        var graphics = new Graphics(this.ctx);\n        var yaxisLabel = graphics.drawText({\n          x: x + yaxisConfig.labels.offsetX,\n          y: y + yaxisConfig.labels.offsetY,\n          text: formatter(text, i),\n          textAnchor: 'middle',\n          fontSize: yaxisConfig.labels.style.fontSize,\n          fontFamily: yaxisConfig.labels.style.fontFamily,\n          foreColor: Array.isArray(yaxisConfig.labels.style.colors) ? yaxisConfig.labels.style.colors[i] : yaxisConfig.labels.style.colors\n        });\n        return yaxisLabel;\n      }\n    }]);\n\n    return CircularChartsHelpers;\n  }();\n\n  /**\n   * ApexCharts Pie Class for drawing Pie / Donut Charts.\n   * @module Pie\n   **/\n\n  var Pie = /*#__PURE__*/function () {\n    function Pie(ctx) {\n      _classCallCheck(this, Pie);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      var w = this.w;\n      this.chartType = this.w.config.chart.type;\n      this.initialAnim = this.w.config.chart.animations.enabled;\n      this.dynamicAnim = this.initialAnim && this.w.config.chart.animations.dynamicAnimation.enabled;\n      this.animBeginArr = [0];\n      this.animDur = 0;\n      this.donutDataLabels = this.w.config.plotOptions.pie.donut.labels;\n      this.lineColorArr = w.globals.stroke.colors !== undefined ? w.globals.stroke.colors : w.globals.colors;\n      this.defaultSize = Math.min(w.globals.gridWidth, w.globals.gridHeight);\n      this.centerY = this.defaultSize / 2;\n      this.centerX = w.globals.gridWidth / 2;\n\n      if (w.config.chart.type === 'radialBar') {\n        this.fullAngle = 360;\n      } else {\n        this.fullAngle = Math.abs(w.config.plotOptions.pie.endAngle - w.config.plotOptions.pie.startAngle);\n      }\n\n      this.initialAngle = w.config.plotOptions.pie.startAngle % this.fullAngle;\n      w.globals.radialSize = this.defaultSize / 2.05 - w.config.stroke.width - (!w.config.chart.sparkline.enabled ? w.config.chart.dropShadow.blur : 0);\n      this.donutSize = w.globals.radialSize * parseInt(w.config.plotOptions.pie.donut.size, 10) / 100;\n      this.maxY = 0;\n      this.sliceLabels = [];\n      this.sliceSizes = [];\n      this.prevSectorAngleArr = []; // for dynamic animations\n    }\n\n    _createClass(Pie, [{\n      key: \"draw\",\n      value: function draw(series) {\n        var _this = this;\n\n        var self = this;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        this.ret = graphics.group({\n          class: 'apexcharts-pie'\n        });\n        if (w.globals.noData) return this.ret;\n        var total = 0;\n\n        for (var k = 0; k < series.length; k++) {\n          // CALCULATE THE TOTAL\n          total += Utils$1.negToZero(series[k]);\n        }\n\n        var sectorAngleArr = []; // el to which series will be drawn\n\n        var elSeries = graphics.group(); // prevent division by zero error if there is no data\n\n        if (total === 0) {\n          total = 0.00001;\n        }\n\n        series.forEach(function (m) {\n          _this.maxY = Math.max(_this.maxY, m);\n        }); // override maxY if user provided in config\n\n        if (w.config.yaxis[0].max) {\n          this.maxY = w.config.yaxis[0].max;\n        }\n\n        if (w.config.grid.position === 'back' && this.chartType === 'polarArea') {\n          this.drawPolarElements(this.ret);\n        }\n\n        for (var i = 0; i < series.length; i++) {\n          // CALCULATE THE ANGLES\n          var angle = this.fullAngle * Utils$1.negToZero(series[i]) / total;\n          sectorAngleArr.push(angle);\n\n          if (this.chartType === 'polarArea') {\n            sectorAngleArr[i] = this.fullAngle / series.length;\n            this.sliceSizes.push(w.globals.radialSize * series[i] / this.maxY);\n          } else {\n            this.sliceSizes.push(w.globals.radialSize);\n          }\n        }\n\n        if (w.globals.dataChanged) {\n          var prevTotal = 0;\n\n          for (var _k = 0; _k < w.globals.previousPaths.length; _k++) {\n            // CALCULATE THE PREV TOTAL\n            prevTotal += Utils$1.negToZero(w.globals.previousPaths[_k]);\n          }\n\n          var previousAngle;\n\n          for (var _i = 0; _i < w.globals.previousPaths.length; _i++) {\n            // CALCULATE THE PREVIOUS ANGLES\n            previousAngle = this.fullAngle * Utils$1.negToZero(w.globals.previousPaths[_i]) / prevTotal;\n            this.prevSectorAngleArr.push(previousAngle);\n          }\n        } // on small chart size after few count of resizes browser window donutSize can be negative\n\n\n        if (this.donutSize < 0) {\n          this.donutSize = 0;\n        }\n\n        var scaleSize = w.config.plotOptions.pie.customScale;\n        var halfW = w.globals.gridWidth / 2;\n        var halfH = w.globals.gridHeight / 2;\n        var translateX = halfW - w.globals.gridWidth / 2 * scaleSize;\n        var translateY = halfH - w.globals.gridHeight / 2 * scaleSize;\n\n        if (this.chartType === 'donut') {\n          // draw the inner circle and add some text to it\n          var circle = graphics.drawCircle(this.donutSize);\n          circle.attr({\n            cx: this.centerX,\n            cy: this.centerY,\n            fill: w.config.plotOptions.pie.donut.background ? w.config.plotOptions.pie.donut.background : 'transparent'\n          });\n          elSeries.add(circle);\n        }\n\n        var elG = self.drawArcs(sectorAngleArr, series); // add slice dataLabels at the end\n\n        this.sliceLabels.forEach(function (s) {\n          elG.add(s);\n        });\n        elSeries.attr({\n          transform: \"translate(\".concat(translateX, \", \").concat(translateY, \") scale(\").concat(scaleSize, \")\")\n        });\n        elSeries.add(elG);\n        this.ret.add(elSeries);\n\n        if (this.donutDataLabels.show) {\n          var dataLabels = this.renderInnerDataLabels(this.donutDataLabels, {\n            hollowSize: this.donutSize,\n            centerX: this.centerX,\n            centerY: this.centerY,\n            opacity: this.donutDataLabels.show,\n            translateX: translateX,\n            translateY: translateY\n          });\n          this.ret.add(dataLabels);\n        }\n\n        if (w.config.grid.position === 'front' && this.chartType === 'polarArea') {\n          this.drawPolarElements(this.ret);\n        }\n\n        return this.ret;\n      } // core function for drawing pie arcs\n\n    }, {\n      key: \"drawArcs\",\n      value: function drawArcs(sectorAngleArr, series) {\n        var w = this.w;\n        var filters = new Filters(this.ctx);\n        var graphics = new Graphics(this.ctx);\n        var fill = new Fill(this.ctx);\n        var g = graphics.group({\n          class: 'apexcharts-slices'\n        });\n        var startAngle = this.initialAngle;\n        var prevStartAngle = this.initialAngle;\n        var endAngle = this.initialAngle;\n        var prevEndAngle = this.initialAngle;\n        this.strokeWidth = w.config.stroke.show ? w.config.stroke.width : 0;\n\n        for (var i = 0; i < sectorAngleArr.length; i++) {\n          var elPieArc = graphics.group({\n            class: \"apexcharts-series apexcharts-pie-series\",\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[i]),\n            rel: i + 1,\n            'data:realIndex': i\n          });\n          g.add(elPieArc);\n          startAngle = endAngle;\n          prevStartAngle = prevEndAngle;\n          endAngle = startAngle + sectorAngleArr[i];\n          prevEndAngle = prevStartAngle + this.prevSectorAngleArr[i];\n          var angle = endAngle < startAngle ? this.fullAngle + endAngle - startAngle : endAngle - startAngle;\n          var pathFill = fill.fillPath({\n            seriesNumber: i,\n            size: this.sliceSizes[i],\n            value: series[i]\n          }); // additionally, pass size for gradient drawing in the fillPath function\n\n          var path = this.getChangedPath(prevStartAngle, prevEndAngle);\n          var elPath = graphics.drawPath({\n            d: path,\n            stroke: Array.isArray(this.lineColorArr) ? this.lineColorArr[i] : this.lineColorArr,\n            strokeWidth: 0,\n            fill: pathFill,\n            fillOpacity: w.config.fill.opacity,\n            classes: \"apexcharts-pie-area apexcharts-\".concat(this.chartType.toLowerCase(), \"-slice-\").concat(i)\n          });\n          elPath.attr({\n            index: 0,\n            j: i\n          });\n          filters.setSelectionFilter(elPath, 0, i);\n\n          if (w.config.chart.dropShadow.enabled) {\n            var shadow = w.config.chart.dropShadow;\n            filters.dropShadow(elPath, shadow, i);\n          }\n\n          this.addListeners(elPath, this.donutDataLabels);\n          Graphics.setAttrs(elPath.node, {\n            'data:angle': angle,\n            'data:startAngle': startAngle,\n            'data:strokeWidth': this.strokeWidth,\n            'data:value': series[i]\n          });\n          var labelPosition = {\n            x: 0,\n            y: 0\n          };\n\n          if (this.chartType === 'pie' || this.chartType === 'polarArea') {\n            labelPosition = Utils$1.polarToCartesian(this.centerX, this.centerY, w.globals.radialSize / 1.25 + w.config.plotOptions.pie.dataLabels.offset, (startAngle + angle / 2) % this.fullAngle);\n          } else if (this.chartType === 'donut') {\n            labelPosition = Utils$1.polarToCartesian(this.centerX, this.centerY, (w.globals.radialSize + this.donutSize) / 2 + w.config.plotOptions.pie.dataLabels.offset, (startAngle + angle / 2) % this.fullAngle);\n          }\n\n          elPieArc.add(elPath); // Animation code starts\n\n          var dur = 0;\n\n          if (this.initialAnim && !w.globals.resized && !w.globals.dataChanged) {\n            dur = angle / this.fullAngle * w.config.chart.animations.speed;\n            if (dur === 0) dur = 1;\n            this.animDur = dur + this.animDur;\n            this.animBeginArr.push(this.animDur);\n          } else {\n            this.animBeginArr.push(0);\n          }\n\n          if (this.dynamicAnim && w.globals.dataChanged) {\n            this.animatePaths(elPath, {\n              size: this.sliceSizes[i],\n              endAngle: endAngle,\n              startAngle: startAngle,\n              prevStartAngle: prevStartAngle,\n              prevEndAngle: prevEndAngle,\n              animateStartingPos: true,\n              i: i,\n              animBeginArr: this.animBeginArr,\n              shouldSetPrevPaths: true,\n              dur: w.config.chart.animations.dynamicAnimation.speed\n            });\n          } else {\n            this.animatePaths(elPath, {\n              size: this.sliceSizes[i],\n              endAngle: endAngle,\n              startAngle: startAngle,\n              i: i,\n              totalItems: sectorAngleArr.length - 1,\n              animBeginArr: this.animBeginArr,\n              dur: dur\n            });\n          } // animation code ends\n\n\n          if (w.config.plotOptions.pie.expandOnClick && this.chartType !== 'polarArea') {\n            elPath.click(this.pieClicked.bind(this, i));\n          }\n\n          if (typeof w.globals.selectedDataPoints[0] !== 'undefined' && w.globals.selectedDataPoints[0].indexOf(i) > -1) {\n            this.pieClicked(i);\n          }\n\n          if (w.config.dataLabels.enabled) {\n            var xPos = labelPosition.x;\n            var yPos = labelPosition.y;\n            var text = 100 * angle / this.fullAngle + '%';\n\n            if (angle !== 0 && w.config.plotOptions.pie.dataLabels.minAngleToShowLabel < sectorAngleArr[i]) {\n              var formatter = w.config.dataLabels.formatter;\n\n              if (formatter !== undefined) {\n                text = formatter(w.globals.seriesPercent[i][0], {\n                  seriesIndex: i,\n                  w: w\n                });\n              }\n\n              var foreColor = w.globals.dataLabels.style.colors[i];\n              var elPieLabelWrap = graphics.group({\n                class: \"apexcharts-datalabels\"\n              });\n              var elPieLabel = graphics.drawText({\n                x: xPos,\n                y: yPos,\n                text: text,\n                textAnchor: 'middle',\n                fontSize: w.config.dataLabels.style.fontSize,\n                fontFamily: w.config.dataLabels.style.fontFamily,\n                fontWeight: w.config.dataLabels.style.fontWeight,\n                foreColor: foreColor\n              });\n              elPieLabelWrap.add(elPieLabel);\n\n              if (w.config.dataLabels.dropShadow.enabled) {\n                var textShadow = w.config.dataLabels.dropShadow;\n                filters.dropShadow(elPieLabel, textShadow);\n              }\n\n              elPieLabel.node.classList.add('apexcharts-pie-label');\n\n              if (w.config.chart.animations.animate && w.globals.resized === false) {\n                elPieLabel.node.classList.add('apexcharts-pie-label-delay');\n                elPieLabel.node.style.animationDelay = w.config.chart.animations.speed / 940 + 's';\n              }\n\n              this.sliceLabels.push(elPieLabelWrap);\n            }\n          }\n        }\n\n        return g;\n      }\n    }, {\n      key: \"addListeners\",\n      value: function addListeners(elPath, dataLabels) {\n        var graphics = new Graphics(this.ctx); // append filters on mouseenter and mouseleave\n\n        elPath.node.addEventListener('mouseenter', graphics.pathMouseEnter.bind(this, elPath));\n        elPath.node.addEventListener('mouseleave', graphics.pathMouseLeave.bind(this, elPath));\n        elPath.node.addEventListener('mouseleave', this.revertDataLabelsInner.bind(this, elPath.node, dataLabels));\n        elPath.node.addEventListener('mousedown', graphics.pathMouseDown.bind(this, elPath));\n\n        if (!this.donutDataLabels.total.showAlways) {\n          elPath.node.addEventListener('mouseenter', this.printDataLabelsInner.bind(this, elPath.node, dataLabels));\n          elPath.node.addEventListener('mousedown', this.printDataLabelsInner.bind(this, elPath.node, dataLabels));\n        }\n      } // This function can be used for other circle charts too\n\n    }, {\n      key: \"animatePaths\",\n      value: function animatePaths(el, opts) {\n        var w = this.w;\n        var me = this;\n        var angle = opts.endAngle < opts.startAngle ? this.fullAngle + opts.endAngle - opts.startAngle : opts.endAngle - opts.startAngle;\n        var prevAngle = angle;\n        var fromStartAngle = opts.startAngle;\n        var toStartAngle = opts.startAngle;\n\n        if (opts.prevStartAngle !== undefined && opts.prevEndAngle !== undefined) {\n          fromStartAngle = opts.prevEndAngle;\n          prevAngle = opts.prevEndAngle < opts.prevStartAngle ? this.fullAngle + opts.prevEndAngle - opts.prevStartAngle : opts.prevEndAngle - opts.prevStartAngle;\n        }\n\n        if (opts.i === w.config.series.length - 1) {\n          // some adjustments for the last overlapping paths\n          if (angle + toStartAngle > this.fullAngle) {\n            opts.endAngle = opts.endAngle - (angle + toStartAngle);\n          } else if (angle + toStartAngle < this.fullAngle) {\n            opts.endAngle = opts.endAngle + (this.fullAngle - (angle + toStartAngle));\n          }\n        }\n\n        if (angle === this.fullAngle) angle = this.fullAngle - 0.01;\n        me.animateArc(el, fromStartAngle, toStartAngle, angle, prevAngle, opts);\n      }\n    }, {\n      key: \"animateArc\",\n      value: function animateArc(el, fromStartAngle, toStartAngle, angle, prevAngle, opts) {\n        var me = this;\n        var w = this.w;\n        var animations = new Animations(this.ctx);\n        var size = opts.size;\n        var path;\n\n        if (isNaN(fromStartAngle) || isNaN(prevAngle)) {\n          fromStartAngle = toStartAngle;\n          prevAngle = angle;\n          opts.dur = 0;\n        }\n\n        var currAngle = angle;\n        var startAngle = toStartAngle;\n        var fromAngle = fromStartAngle < toStartAngle ? this.fullAngle + fromStartAngle - toStartAngle : fromStartAngle - toStartAngle;\n\n        if (w.globals.dataChanged && opts.shouldSetPrevPaths) {\n          // to avoid flicker when updating, set prev path first and then animate from there\n          if (opts.prevEndAngle) {\n            path = me.getPiePath({\n              me: me,\n              startAngle: opts.prevStartAngle,\n              angle: opts.prevEndAngle < opts.prevStartAngle ? this.fullAngle + opts.prevEndAngle - opts.prevStartAngle : opts.prevEndAngle - opts.prevStartAngle,\n              size: size\n            });\n            el.attr({\n              d: path\n            });\n          }\n        }\n\n        if (opts.dur !== 0) {\n          el.animate(opts.dur, w.globals.easing, opts.animBeginArr[opts.i]).afterAll(function () {\n            if (me.chartType === 'pie' || me.chartType === 'donut' || me.chartType === 'polarArea') {\n              this.animate(w.config.chart.animations.dynamicAnimation.speed).attr({\n                'stroke-width': me.strokeWidth\n              });\n            }\n\n            if (opts.i === w.config.series.length - 1) {\n              animations.animationCompleted(el);\n            }\n          }).during(function (pos) {\n            currAngle = fromAngle + (angle - fromAngle) * pos;\n\n            if (opts.animateStartingPos) {\n              currAngle = prevAngle + (angle - prevAngle) * pos;\n              startAngle = fromStartAngle - prevAngle + (toStartAngle - (fromStartAngle - prevAngle)) * pos;\n            }\n\n            path = me.getPiePath({\n              me: me,\n              startAngle: startAngle,\n              angle: currAngle,\n              size: size\n            });\n            el.node.setAttribute('data:pathOrig', path);\n            el.attr({\n              d: path\n            });\n          });\n        } else {\n          path = me.getPiePath({\n            me: me,\n            startAngle: startAngle,\n            angle: angle,\n            size: size\n          });\n\n          if (!opts.isTrack) {\n            w.globals.animationEnded = true;\n          }\n\n          el.node.setAttribute('data:pathOrig', path);\n          el.attr({\n            d: path,\n            'stroke-width': me.strokeWidth\n          });\n        }\n      }\n    }, {\n      key: \"pieClicked\",\n      value: function pieClicked(i) {\n        var w = this.w;\n        var me = this;\n        var path;\n        var size = me.sliceSizes[i] + (w.config.plotOptions.pie.expandOnClick ? 4 : 0);\n        var elPath = w.globals.dom.Paper.select(\".apexcharts-\".concat(me.chartType.toLowerCase(), \"-slice-\").concat(i)).members[0];\n\n        if (elPath.attr('data:pieClicked') === 'true') {\n          elPath.attr({\n            'data:pieClicked': 'false'\n          });\n          this.revertDataLabelsInner(elPath.node, this.donutDataLabels);\n          var origPath = elPath.attr('data:pathOrig');\n          elPath.attr({\n            d: origPath\n          });\n          return;\n        } else {\n          // reset all elems\n          var allEls = w.globals.dom.baseEl.getElementsByClassName('apexcharts-pie-area');\n          Array.prototype.forEach.call(allEls, function (pieSlice) {\n            pieSlice.setAttribute('data:pieClicked', 'false');\n            var origPath = pieSlice.getAttribute('data:pathOrig');\n            pieSlice.setAttribute('d', origPath);\n          });\n          elPath.attr('data:pieClicked', 'true');\n        }\n\n        var startAngle = parseInt(elPath.attr('data:startAngle'), 10);\n        var angle = parseInt(elPath.attr('data:angle'), 10);\n        path = me.getPiePath({\n          me: me,\n          startAngle: startAngle,\n          angle: angle,\n          size: size\n        });\n        if (angle === 360) return;\n        elPath.plot(path);\n      }\n    }, {\n      key: \"getChangedPath\",\n      value: function getChangedPath(prevStartAngle, prevEndAngle) {\n        var path = '';\n\n        if (this.dynamicAnim && this.w.globals.dataChanged) {\n          path = this.getPiePath({\n            me: this,\n            startAngle: prevStartAngle,\n            angle: prevEndAngle - prevStartAngle,\n            size: this.size\n          });\n        }\n\n        return path;\n      }\n    }, {\n      key: \"getPiePath\",\n      value: function getPiePath(_ref) {\n        var me = _ref.me,\n            startAngle = _ref.startAngle,\n            angle = _ref.angle,\n            size = _ref.size;\n        var path;\n        var startDeg = startAngle;\n        var startRadians = Math.PI * (startDeg - 90) / 180;\n        var endDeg = angle + startAngle; // prevent overlap\n\n        if (Math.ceil(endDeg) >= this.fullAngle + this.w.config.plotOptions.pie.startAngle % this.fullAngle) {\n          endDeg = this.fullAngle + this.w.config.plotOptions.pie.startAngle % this.fullAngle - 0.01;\n        }\n\n        if (Math.ceil(endDeg) > this.fullAngle) endDeg -= this.fullAngle;\n        var endRadians = Math.PI * (endDeg - 90) / 180;\n        var x1 = me.centerX + size * Math.cos(startRadians);\n        var y1 = me.centerY + size * Math.sin(startRadians);\n        var x2 = me.centerX + size * Math.cos(endRadians);\n        var y2 = me.centerY + size * Math.sin(endRadians);\n        var startInner = Utils$1.polarToCartesian(me.centerX, me.centerY, me.donutSize, endDeg);\n        var endInner = Utils$1.polarToCartesian(me.centerX, me.centerY, me.donutSize, startDeg);\n        var largeArc = angle > 180 ? 1 : 0;\n        var pathBeginning = ['M', x1, y1, 'A', size, size, 0, largeArc, 1, x2, y2];\n\n        if (me.chartType === 'donut') {\n          path = [].concat(pathBeginning, ['L', startInner.x, startInner.y, 'A', me.donutSize, me.donutSize, 0, largeArc, 0, endInner.x, endInner.y, 'L', x1, y1, 'z']).join(' ');\n        } else if (me.chartType === 'pie' || me.chartType === 'polarArea') {\n          path = [].concat(pathBeginning, ['L', me.centerX, me.centerY, 'L', x1, y1]).join(' ');\n        } else {\n          path = [].concat(pathBeginning).join(' ');\n        }\n\n        return path;\n      }\n    }, {\n      key: \"drawPolarElements\",\n      value: function drawPolarElements(parent) {\n        var w = this.w;\n        var scale = new Range$1(this.ctx);\n        var graphics = new Graphics(this.ctx);\n        var helpers = new CircularChartsHelpers(this.ctx);\n        var gCircles = graphics.group();\n        var gYAxis = graphics.group();\n        var yScale = scale.niceScale(0, Math.ceil(this.maxY), w.config.yaxis[0].tickAmount, 0, true);\n        var yTexts = yScale.result.reverse();\n        var len = yScale.result.length;\n        this.maxY = yScale.niceMax;\n        var circleSize = w.globals.radialSize;\n        var diff = circleSize / (len - 1);\n\n        for (var i = 0; i < len - 1; i++) {\n          var circle = graphics.drawCircle(circleSize);\n          circle.attr({\n            cx: this.centerX,\n            cy: this.centerY,\n            fill: 'none',\n            'stroke-width': w.config.plotOptions.polarArea.rings.strokeWidth,\n            stroke: w.config.plotOptions.polarArea.rings.strokeColor\n          });\n\n          if (w.config.yaxis[0].show) {\n            var yLabel = helpers.drawYAxisTexts(this.centerX, this.centerY - circleSize + parseInt(w.config.yaxis[0].labels.style.fontSize, 10) / 2, i, yTexts[i]);\n            gYAxis.add(yLabel);\n          }\n\n          gCircles.add(circle);\n          circleSize = circleSize - diff;\n        }\n\n        this.drawSpokes(parent);\n        parent.add(gCircles);\n        parent.add(gYAxis);\n      }\n    }, {\n      key: \"renderInnerDataLabels\",\n      value: function renderInnerDataLabels(dataLabelsConfig, opts) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var g = graphics.group({\n          class: 'apexcharts-datalabels-group',\n          transform: \"translate(\".concat(opts.translateX ? opts.translateX : 0, \", \").concat(opts.translateY ? opts.translateY : 0, \") scale(\").concat(w.config.plotOptions.pie.customScale, \")\")\n        });\n        var showTotal = dataLabelsConfig.total.show;\n        g.node.style.opacity = opts.opacity;\n        var x = opts.centerX;\n        var y = opts.centerY;\n        var labelColor, valueColor;\n\n        if (dataLabelsConfig.name.color === undefined) {\n          labelColor = w.globals.colors[0];\n        } else {\n          labelColor = dataLabelsConfig.name.color;\n        }\n\n        var labelFontSize = dataLabelsConfig.name.fontSize;\n        var labelFontFamily = dataLabelsConfig.name.fontFamily;\n        var labelFontWeight = dataLabelsConfig.name.fontWeight;\n\n        if (dataLabelsConfig.value.color === undefined) {\n          valueColor = w.config.chart.foreColor;\n        } else {\n          valueColor = dataLabelsConfig.value.color;\n        }\n\n        var lbFormatter = dataLabelsConfig.value.formatter;\n        var val = '';\n        var name = '';\n\n        if (showTotal) {\n          labelColor = dataLabelsConfig.total.color;\n          labelFontSize = dataLabelsConfig.total.fontSize;\n          labelFontFamily = dataLabelsConfig.total.fontFamily;\n          labelFontWeight = dataLabelsConfig.total.fontWeight;\n          name = dataLabelsConfig.total.label;\n          val = dataLabelsConfig.total.formatter(w);\n        } else {\n          if (w.globals.series.length === 1) {\n            val = lbFormatter(w.globals.series[0], w);\n            name = w.globals.seriesNames[0];\n          }\n        }\n\n        if (name) {\n          name = dataLabelsConfig.name.formatter(name, dataLabelsConfig.total.show, w);\n        }\n\n        if (dataLabelsConfig.name.show) {\n          var elLabel = graphics.drawText({\n            x: x,\n            y: y + parseFloat(dataLabelsConfig.name.offsetY),\n            text: name,\n            textAnchor: 'middle',\n            foreColor: labelColor,\n            fontSize: labelFontSize,\n            fontWeight: labelFontWeight,\n            fontFamily: labelFontFamily\n          });\n          elLabel.node.classList.add('apexcharts-datalabel-label');\n          g.add(elLabel);\n        }\n\n        if (dataLabelsConfig.value.show) {\n          var valOffset = dataLabelsConfig.name.show ? parseFloat(dataLabelsConfig.value.offsetY) + 16 : dataLabelsConfig.value.offsetY;\n          var elValue = graphics.drawText({\n            x: x,\n            y: y + valOffset,\n            text: val,\n            textAnchor: 'middle',\n            foreColor: valueColor,\n            fontWeight: dataLabelsConfig.value.fontWeight,\n            fontSize: dataLabelsConfig.value.fontSize,\n            fontFamily: dataLabelsConfig.value.fontFamily\n          });\n          elValue.node.classList.add('apexcharts-datalabel-value');\n          g.add(elValue);\n        } // for a multi-series circle chart, we need to show total value instead of first series labels\n\n\n        return g;\n      }\n      /**\n       *\n       * @param {string} name - The name of the series\n       * @param {string} val - The value of that series\n       * @param {object} el - Optional el (indicates which series was hovered/clicked). If this param is not present, means we need to show total\n       */\n\n    }, {\n      key: \"printInnerLabels\",\n      value: function printInnerLabels(labelsConfig, name, val, el) {\n        var w = this.w;\n        var labelColor;\n\n        if (el) {\n          if (labelsConfig.name.color === undefined) {\n            labelColor = w.globals.colors[parseInt(el.parentNode.getAttribute('rel'), 10) - 1];\n          } else {\n            labelColor = labelsConfig.name.color;\n          }\n        } else {\n          if (w.globals.series.length > 1 && labelsConfig.total.show) {\n            labelColor = labelsConfig.total.color;\n          }\n        }\n\n        var elLabel = w.globals.dom.baseEl.querySelector('.apexcharts-datalabel-label');\n        var elValue = w.globals.dom.baseEl.querySelector('.apexcharts-datalabel-value');\n        var lbFormatter = labelsConfig.value.formatter;\n        val = lbFormatter(val, w); // we need to show Total Val - so get the formatter of it\n\n        if (!el && typeof labelsConfig.total.formatter === 'function') {\n          val = labelsConfig.total.formatter(w);\n        }\n\n        var isTotal = name === labelsConfig.total.label;\n        name = labelsConfig.name.formatter(name, isTotal, w);\n\n        if (elLabel !== null) {\n          elLabel.textContent = name;\n        }\n\n        if (elValue !== null) {\n          elValue.textContent = val;\n        }\n\n        if (elLabel !== null) {\n          elLabel.style.fill = labelColor;\n        }\n      }\n    }, {\n      key: \"printDataLabelsInner\",\n      value: function printDataLabelsInner(el, dataLabelsConfig) {\n        var w = this.w;\n        var val = el.getAttribute('data:value');\n        var name = w.globals.seriesNames[parseInt(el.parentNode.getAttribute('rel'), 10) - 1];\n\n        if (w.globals.series.length > 1) {\n          this.printInnerLabels(dataLabelsConfig, name, val, el);\n        }\n\n        var dataLabelsGroup = w.globals.dom.baseEl.querySelector('.apexcharts-datalabels-group');\n\n        if (dataLabelsGroup !== null) {\n          dataLabelsGroup.style.opacity = 1;\n        }\n      }\n    }, {\n      key: \"drawSpokes\",\n      value: function drawSpokes(parent) {\n        var _this2 = this;\n\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var spokeConfig = w.config.plotOptions.polarArea.spokes;\n        if (spokeConfig.strokeWidth === 0) return;\n        var spokes = [];\n        var angleDivision = 360 / w.globals.series.length;\n\n        for (var i = 0; i < w.globals.series.length; i++) {\n          spokes.push(Utils$1.polarToCartesian(this.centerX, this.centerY, w.globals.radialSize, w.config.plotOptions.pie.startAngle + angleDivision * i));\n        }\n\n        spokes.forEach(function (p, i) {\n          var line = graphics.drawLine(p.x, p.y, _this2.centerX, _this2.centerY, Array.isArray(spokeConfig.connectorColors) ? spokeConfig.connectorColors[i] : spokeConfig.connectorColors);\n          parent.add(line);\n        });\n      }\n    }, {\n      key: \"revertDataLabelsInner\",\n      value: function revertDataLabelsInner(elem, dataLabelsConfig, event) {\n        var _this3 = this;\n\n        var w = this.w;\n        var dataLabelsGroup = w.globals.dom.baseEl.querySelector('.apexcharts-datalabels-group');\n        var sliceOut = false;\n        var slices = w.globals.dom.baseEl.getElementsByClassName(\"apexcharts-pie-area\");\n\n        var selectSlice = function selectSlice(_ref2) {\n          var makeSliceOut = _ref2.makeSliceOut,\n              printLabel = _ref2.printLabel;\n          Array.prototype.forEach.call(slices, function (s) {\n            if (s.getAttribute('data:pieClicked') === 'true') {\n              if (makeSliceOut) {\n                sliceOut = true;\n              }\n\n              if (printLabel) {\n                _this3.printDataLabelsInner(s, dataLabelsConfig);\n              }\n            }\n          });\n        };\n\n        selectSlice({\n          makeSliceOut: true,\n          printLabel: false\n        });\n\n        if (dataLabelsConfig.total.show && w.globals.series.length > 1) {\n          if (sliceOut && !dataLabelsConfig.total.showAlways) {\n            selectSlice({\n              makeSliceOut: false,\n              printLabel: true\n            });\n          } else {\n            this.printInnerLabels(dataLabelsConfig, dataLabelsConfig.total.label, dataLabelsConfig.total.formatter(w));\n          }\n        } else {\n          selectSlice({\n            makeSliceOut: false,\n            printLabel: true\n          });\n\n          if (!sliceOut) {\n            if (w.globals.selectedDataPoints.length && w.globals.series.length > 1) {\n              if (w.globals.selectedDataPoints[0].length > 0) {\n                var index = w.globals.selectedDataPoints[0];\n                var el = w.globals.dom.baseEl.querySelector(\".apexcharts-\".concat(this.chartType.toLowerCase(), \"-slice-\").concat(index));\n                this.printDataLabelsInner(el, dataLabelsConfig);\n              } else if (dataLabelsGroup && w.globals.selectedDataPoints.length && w.globals.selectedDataPoints[0].length === 0) {\n                dataLabelsGroup.style.opacity = 0;\n              }\n            } else {\n              if (dataLabelsGroup && w.globals.series.length > 1) {\n                dataLabelsGroup.style.opacity = 0;\n              }\n            }\n          }\n        }\n      }\n    }]);\n\n    return Pie;\n  }();\n\n  /**\n   * ApexCharts Radar Class for Spider/Radar Charts.\n   * @module Radar\n   **/\n\n  var Radar = /*#__PURE__*/function () {\n    function Radar(ctx) {\n      _classCallCheck(this, Radar);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.chartType = this.w.config.chart.type;\n      this.initialAnim = this.w.config.chart.animations.enabled;\n      this.dynamicAnim = this.initialAnim && this.w.config.chart.animations.dynamicAnimation.enabled;\n      this.animDur = 0;\n      var w = this.w;\n      this.graphics = new Graphics(this.ctx);\n      this.lineColorArr = w.globals.stroke.colors !== undefined ? w.globals.stroke.colors : w.globals.colors;\n      this.defaultSize = w.globals.svgHeight < w.globals.svgWidth ? w.globals.gridHeight + w.globals.goldenPadding * 1.5 : w.globals.gridWidth;\n      this.isLog = w.config.yaxis[0].logarithmic;\n      this.coreUtils = new CoreUtils(this.ctx);\n      this.maxValue = this.isLog ? this.coreUtils.getLogVal(w.globals.maxY, 0) : w.globals.maxY;\n      this.minValue = this.isLog ? this.coreUtils.getLogVal(this.w.globals.minY, 0) : w.globals.minY;\n      this.polygons = w.config.plotOptions.radar.polygons;\n      this.strokeWidth = w.config.stroke.show ? w.config.stroke.width : 0;\n      this.size = this.defaultSize / 2.1 - this.strokeWidth - w.config.chart.dropShadow.blur;\n\n      if (w.config.xaxis.labels.show) {\n        this.size = this.size - w.globals.xAxisLabelsWidth / 1.75;\n      }\n\n      if (w.config.plotOptions.radar.size !== undefined) {\n        this.size = w.config.plotOptions.radar.size;\n      }\n\n      this.dataRadiusOfPercent = [];\n      this.dataRadius = [];\n      this.angleArr = [];\n      this.yaxisLabelsTextsPos = [];\n    }\n\n    _createClass(Radar, [{\n      key: \"draw\",\n      value: function draw(series) {\n        var _this = this;\n\n        var w = this.w;\n        var fill = new Fill(this.ctx);\n        var allSeries = [];\n        var dataLabels = new DataLabels(this.ctx);\n\n        if (series.length) {\n          this.dataPointsLen = series[w.globals.maxValsInArrayIndex].length;\n        }\n\n        this.disAngle = Math.PI * 2 / this.dataPointsLen;\n        var halfW = w.globals.gridWidth / 2;\n        var halfH = w.globals.gridHeight / 2;\n        var translateX = halfW + w.config.plotOptions.radar.offsetX;\n        var translateY = halfH + w.config.plotOptions.radar.offsetY;\n        var ret = this.graphics.group({\n          class: 'apexcharts-radar-series apexcharts-plot-series',\n          transform: \"translate(\".concat(translateX || 0, \", \").concat(translateY || 0, \")\")\n        });\n        var dataPointsPos = [];\n        var elPointsMain = null;\n        var elDataPointsMain = null;\n        this.yaxisLabels = this.graphics.group({\n          class: 'apexcharts-yaxis'\n        });\n        series.forEach(function (s, i) {\n          var longestSeries = s.length === w.globals.dataPoints; // el to which series will be drawn\n\n          var elSeries = _this.graphics.group().attr({\n            class: \"apexcharts-series\",\n            'data:longestSeries': longestSeries,\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[i]),\n            rel: i + 1,\n            'data:realIndex': i\n          });\n\n          _this.dataRadiusOfPercent[i] = [];\n          _this.dataRadius[i] = [];\n          _this.angleArr[i] = [];\n          s.forEach(function (dv, j) {\n            var range = Math.abs(_this.maxValue - _this.minValue);\n            dv = dv + Math.abs(_this.minValue);\n\n            if (_this.isLog) {\n              dv = _this.coreUtils.getLogVal(dv, 0);\n            }\n\n            _this.dataRadiusOfPercent[i][j] = dv / range;\n            _this.dataRadius[i][j] = _this.dataRadiusOfPercent[i][j] * _this.size;\n            _this.angleArr[i][j] = j * _this.disAngle;\n          });\n          dataPointsPos = _this.getDataPointsPos(_this.dataRadius[i], _this.angleArr[i]);\n\n          var paths = _this.createPaths(dataPointsPos, {\n            x: 0,\n            y: 0\n          }); // points\n\n\n          elPointsMain = _this.graphics.group({\n            class: 'apexcharts-series-markers-wrap apexcharts-element-hidden'\n          }); // datapoints\n\n          elDataPointsMain = _this.graphics.group({\n            class: \"apexcharts-datalabels\",\n            'data:realIndex': i\n          });\n          w.globals.delayedElements.push({\n            el: elPointsMain.node,\n            index: i\n          });\n          var defaultRenderedPathOptions = {\n            i: i,\n            realIndex: i,\n            animationDelay: i,\n            initialSpeed: w.config.chart.animations.speed,\n            dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,\n            className: \"apexcharts-radar\",\n            shouldClipToGrid: false,\n            bindEventsOnPaths: false,\n            stroke: w.globals.stroke.colors[i],\n            strokeLineCap: w.config.stroke.lineCap\n          };\n          var pathFrom = null;\n\n          if (w.globals.previousPaths.length > 0) {\n            pathFrom = _this.getPreviousPath(i);\n          }\n\n          for (var p = 0; p < paths.linePathsTo.length; p++) {\n            var renderedLinePath = _this.graphics.renderPaths(_objectSpread2(_objectSpread2({}, defaultRenderedPathOptions), {}, {\n              pathFrom: pathFrom === null ? paths.linePathsFrom[p] : pathFrom,\n              pathTo: paths.linePathsTo[p],\n              strokeWidth: Array.isArray(_this.strokeWidth) ? _this.strokeWidth[i] : _this.strokeWidth,\n              fill: 'none',\n              drawShadow: false\n            }));\n\n            elSeries.add(renderedLinePath);\n            var pathFill = fill.fillPath({\n              seriesNumber: i\n            });\n\n            var renderedAreaPath = _this.graphics.renderPaths(_objectSpread2(_objectSpread2({}, defaultRenderedPathOptions), {}, {\n              pathFrom: pathFrom === null ? paths.areaPathsFrom[p] : pathFrom,\n              pathTo: paths.areaPathsTo[p],\n              strokeWidth: 0,\n              fill: pathFill,\n              drawShadow: false\n            }));\n\n            if (w.config.chart.dropShadow.enabled) {\n              var filters = new Filters(_this.ctx);\n              var shadow = w.config.chart.dropShadow;\n              filters.dropShadow(renderedAreaPath, Object.assign({}, shadow, {\n                noUserSpaceOnUse: true\n              }), i);\n            }\n\n            elSeries.add(renderedAreaPath);\n          }\n\n          s.forEach(function (sj, j) {\n            var markers = new Markers(_this.ctx);\n            var opts = markers.getMarkerConfig({\n              cssClass: 'apexcharts-marker',\n              seriesIndex: i,\n              dataPointIndex: j\n            });\n\n            var point = _this.graphics.drawMarker(dataPointsPos[j].x, dataPointsPos[j].y, opts);\n\n            point.attr('rel', j);\n            point.attr('j', j);\n            point.attr('index', i);\n            point.node.setAttribute('default-marker-size', opts.pSize);\n\n            var elPointsWrap = _this.graphics.group({\n              class: 'apexcharts-series-markers'\n            });\n\n            if (elPointsWrap) {\n              elPointsWrap.add(point);\n            }\n\n            elPointsMain.add(elPointsWrap);\n            elSeries.add(elPointsMain);\n            var dataLabelsConfig = w.config.dataLabels;\n\n            if (dataLabelsConfig.enabled) {\n              var text = dataLabelsConfig.formatter(w.globals.series[i][j], {\n                seriesIndex: i,\n                dataPointIndex: j,\n                w: w\n              });\n              dataLabels.plotDataLabelsText({\n                x: dataPointsPos[j].x,\n                y: dataPointsPos[j].y,\n                text: text,\n                textAnchor: 'middle',\n                i: i,\n                j: i,\n                parent: elDataPointsMain,\n                offsetCorrection: false,\n                dataLabelsConfig: _objectSpread2({}, dataLabelsConfig)\n              });\n            }\n\n            elSeries.add(elDataPointsMain);\n          });\n          allSeries.push(elSeries);\n        });\n        this.drawPolygons({\n          parent: ret\n        });\n\n        if (w.config.xaxis.labels.show) {\n          var xaxisTexts = this.drawXAxisTexts();\n          ret.add(xaxisTexts);\n        }\n\n        allSeries.forEach(function (elS) {\n          ret.add(elS);\n        });\n        ret.add(this.yaxisLabels);\n        return ret;\n      }\n    }, {\n      key: \"drawPolygons\",\n      value: function drawPolygons(opts) {\n        var _this2 = this;\n\n        var w = this.w;\n        var parent = opts.parent;\n        var helpers = new CircularChartsHelpers(this.ctx);\n        var yaxisTexts = w.globals.yAxisScale[0].result.reverse();\n        var layers = yaxisTexts.length;\n        var radiusSizes = [];\n        var layerDis = this.size / (layers - 1);\n\n        for (var i = 0; i < layers; i++) {\n          radiusSizes[i] = layerDis * i;\n        }\n\n        radiusSizes.reverse();\n        var polygonStrings = [];\n        var lines = [];\n        radiusSizes.forEach(function (radiusSize, r) {\n          var polygon = Utils$1.getPolygonPos(radiusSize, _this2.dataPointsLen);\n          var string = '';\n          polygon.forEach(function (p, i) {\n            if (r === 0) {\n              var line = _this2.graphics.drawLine(p.x, p.y, 0, 0, Array.isArray(_this2.polygons.connectorColors) ? _this2.polygons.connectorColors[i] : _this2.polygons.connectorColors);\n\n              lines.push(line);\n            }\n\n            if (i === 0) {\n              _this2.yaxisLabelsTextsPos.push({\n                x: p.x,\n                y: p.y\n              });\n            }\n\n            string += p.x + ',' + p.y + ' ';\n          });\n          polygonStrings.push(string);\n        });\n        polygonStrings.forEach(function (p, i) {\n          var strokeColors = _this2.polygons.strokeColors;\n          var strokeWidth = _this2.polygons.strokeWidth;\n\n          var polygon = _this2.graphics.drawPolygon(p, Array.isArray(strokeColors) ? strokeColors[i] : strokeColors, Array.isArray(strokeWidth) ? strokeWidth[i] : strokeWidth, w.globals.radarPolygons.fill.colors[i]);\n\n          parent.add(polygon);\n        });\n        lines.forEach(function (l) {\n          parent.add(l);\n        });\n\n        if (w.config.yaxis[0].show) {\n          this.yaxisLabelsTextsPos.forEach(function (p, i) {\n            var yText = helpers.drawYAxisTexts(p.x, p.y, i, yaxisTexts[i]);\n\n            _this2.yaxisLabels.add(yText);\n          });\n        }\n      }\n    }, {\n      key: \"drawXAxisTexts\",\n      value: function drawXAxisTexts() {\n        var _this3 = this;\n\n        var w = this.w;\n        var xaxisLabelsConfig = w.config.xaxis.labels;\n        var elXAxisWrap = this.graphics.group({\n          class: 'apexcharts-xaxis'\n        });\n        var polygonPos = Utils$1.getPolygonPos(this.size, this.dataPointsLen);\n        w.globals.labels.forEach(function (label, i) {\n          var formatter = w.config.xaxis.labels.formatter;\n          var dataLabels = new DataLabels(_this3.ctx);\n\n          if (polygonPos[i]) {\n            var textPos = _this3.getTextPos(polygonPos[i], _this3.size);\n\n            var text = formatter(label, {\n              seriesIndex: -1,\n              dataPointIndex: i,\n              w: w\n            });\n            dataLabels.plotDataLabelsText({\n              x: textPos.newX,\n              y: textPos.newY,\n              text: text,\n              textAnchor: textPos.textAnchor,\n              i: i,\n              j: i,\n              parent: elXAxisWrap,\n              color: Array.isArray(xaxisLabelsConfig.style.colors) && xaxisLabelsConfig.style.colors[i] ? xaxisLabelsConfig.style.colors[i] : '#a8a8a8',\n              dataLabelsConfig: _objectSpread2({\n                textAnchor: textPos.textAnchor,\n                dropShadow: {\n                  enabled: false\n                }\n              }, xaxisLabelsConfig),\n              offsetCorrection: false\n            });\n          }\n        });\n        return elXAxisWrap;\n      }\n    }, {\n      key: \"createPaths\",\n      value: function createPaths(pos, origin) {\n        var _this4 = this;\n\n        var linePathsTo = [];\n        var linePathsFrom = [];\n        var areaPathsTo = [];\n        var areaPathsFrom = [];\n\n        if (pos.length) {\n          linePathsFrom = [this.graphics.move(origin.x, origin.y)];\n          areaPathsFrom = [this.graphics.move(origin.x, origin.y)];\n          var linePathTo = this.graphics.move(pos[0].x, pos[0].y);\n          var areaPathTo = this.graphics.move(pos[0].x, pos[0].y);\n          pos.forEach(function (p, i) {\n            linePathTo += _this4.graphics.line(p.x, p.y);\n            areaPathTo += _this4.graphics.line(p.x, p.y);\n\n            if (i === pos.length - 1) {\n              linePathTo += 'Z';\n              areaPathTo += 'Z';\n            }\n          });\n          linePathsTo.push(linePathTo);\n          areaPathsTo.push(areaPathTo);\n        }\n\n        return {\n          linePathsFrom: linePathsFrom,\n          linePathsTo: linePathsTo,\n          areaPathsFrom: areaPathsFrom,\n          areaPathsTo: areaPathsTo\n        };\n      }\n    }, {\n      key: \"getTextPos\",\n      value: function getTextPos(pos, polygonSize) {\n        var limit = 10;\n        var textAnchor = 'middle';\n        var newX = pos.x;\n        var newY = pos.y;\n\n        if (Math.abs(pos.x) >= limit) {\n          if (pos.x > 0) {\n            textAnchor = 'start';\n            newX += 10;\n          } else if (pos.x < 0) {\n            textAnchor = 'end';\n            newX -= 10;\n          }\n        } else {\n          textAnchor = 'middle';\n        }\n\n        if (Math.abs(pos.y) >= polygonSize - limit) {\n          if (pos.y < 0) {\n            newY -= 10;\n          } else if (pos.y > 0) {\n            newY += 10;\n          }\n        }\n\n        return {\n          textAnchor: textAnchor,\n          newX: newX,\n          newY: newY\n        };\n      }\n    }, {\n      key: \"getPreviousPath\",\n      value: function getPreviousPath(realIndex) {\n        var w = this.w;\n        var pathFrom = null;\n\n        for (var pp = 0; pp < w.globals.previousPaths.length; pp++) {\n          var gpp = w.globals.previousPaths[pp];\n\n          if (gpp.paths.length > 0 && parseInt(gpp.realIndex, 10) === parseInt(realIndex, 10)) {\n            if (typeof w.globals.previousPaths[pp].paths[0] !== 'undefined') {\n              pathFrom = w.globals.previousPaths[pp].paths[0].d;\n            }\n          }\n        }\n\n        return pathFrom;\n      }\n    }, {\n      key: \"getDataPointsPos\",\n      value: function getDataPointsPos(dataRadiusArr, angleArr) {\n        var dataPointsLen = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.dataPointsLen;\n        dataRadiusArr = dataRadiusArr || [];\n        angleArr = angleArr || [];\n        var dataPointsPosArray = [];\n\n        for (var j = 0; j < dataPointsLen; j++) {\n          var curPointPos = {};\n          curPointPos.x = dataRadiusArr[j] * Math.sin(angleArr[j]);\n          curPointPos.y = -dataRadiusArr[j] * Math.cos(angleArr[j]);\n          dataPointsPosArray.push(curPointPos);\n        }\n\n        return dataPointsPosArray;\n      }\n    }]);\n\n    return Radar;\n  }();\n\n  /**\n   * ApexCharts Radial Class for drawing Circle / Semi Circle Charts.\n   * @module Radial\n   **/\n\n  var Radial = /*#__PURE__*/function (_Pie) {\n    _inherits(Radial, _Pie);\n\n    var _super = _createSuper(Radial);\n\n    function Radial(ctx) {\n      var _this;\n\n      _classCallCheck(this, Radial);\n\n      _this = _super.call(this, ctx);\n      _this.ctx = ctx;\n      _this.w = ctx.w;\n      _this.animBeginArr = [0];\n      _this.animDur = 0;\n      var w = _this.w;\n      _this.startAngle = w.config.plotOptions.radialBar.startAngle;\n      _this.endAngle = w.config.plotOptions.radialBar.endAngle;\n      _this.totalAngle = Math.abs(w.config.plotOptions.radialBar.endAngle - w.config.plotOptions.radialBar.startAngle);\n      _this.trackStartAngle = w.config.plotOptions.radialBar.track.startAngle;\n      _this.trackEndAngle = w.config.plotOptions.radialBar.track.endAngle;\n      _this.donutDataLabels = _this.w.config.plotOptions.radialBar.dataLabels;\n      _this.radialDataLabels = _this.donutDataLabels; // make a copy for easy reference\n\n      if (!_this.trackStartAngle) _this.trackStartAngle = _this.startAngle;\n      if (!_this.trackEndAngle) _this.trackEndAngle = _this.endAngle;\n      if (_this.endAngle === 360) _this.endAngle = 359.99;\n      _this.margin = parseInt(w.config.plotOptions.radialBar.track.margin, 10);\n      return _this;\n    }\n\n    _createClass(Radial, [{\n      key: \"draw\",\n      value: function draw(series) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var ret = graphics.group({\n          class: 'apexcharts-radialbar'\n        });\n        if (w.globals.noData) return ret;\n        var elSeries = graphics.group();\n        var centerY = this.defaultSize / 2;\n        var centerX = w.globals.gridWidth / 2;\n        var size = this.defaultSize / 2.05;\n\n        if (!w.config.chart.sparkline.enabled) {\n          size = size - w.config.stroke.width - w.config.chart.dropShadow.blur;\n        }\n\n        var colorArr = w.globals.fill.colors;\n\n        if (w.config.plotOptions.radialBar.track.show) {\n          var elTracks = this.drawTracks({\n            size: size,\n            centerX: centerX,\n            centerY: centerY,\n            colorArr: colorArr,\n            series: series\n          });\n          elSeries.add(elTracks);\n        }\n\n        var elG = this.drawArcs({\n          size: size,\n          centerX: centerX,\n          centerY: centerY,\n          colorArr: colorArr,\n          series: series\n        });\n        var totalAngle = 360;\n\n        if (w.config.plotOptions.radialBar.startAngle < 0) {\n          totalAngle = this.totalAngle;\n        }\n\n        var angleRatio = (360 - totalAngle) / 360;\n        w.globals.radialSize = size - size * angleRatio;\n\n        if (this.radialDataLabels.value.show) {\n          var offset = Math.max(this.radialDataLabels.value.offsetY, this.radialDataLabels.name.offsetY);\n          w.globals.radialSize += offset * angleRatio;\n        }\n\n        elSeries.add(elG.g);\n\n        if (w.config.plotOptions.radialBar.hollow.position === 'front') {\n          elG.g.add(elG.elHollow);\n\n          if (elG.dataLabels) {\n            elG.g.add(elG.dataLabels);\n          }\n        }\n\n        ret.add(elSeries);\n        return ret;\n      }\n    }, {\n      key: \"drawTracks\",\n      value: function drawTracks(opts) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var g = graphics.group({\n          class: 'apexcharts-tracks'\n        });\n        var filters = new Filters(this.ctx);\n        var fill = new Fill(this.ctx);\n        var strokeWidth = this.getStrokeWidth(opts);\n        opts.size = opts.size - strokeWidth / 2;\n\n        for (var i = 0; i < opts.series.length; i++) {\n          var elRadialBarTrack = graphics.group({\n            class: 'apexcharts-radialbar-track apexcharts-track'\n          });\n          g.add(elRadialBarTrack);\n          elRadialBarTrack.attr({\n            rel: i + 1\n          });\n          opts.size = opts.size - strokeWidth - this.margin;\n          var trackConfig = w.config.plotOptions.radialBar.track;\n          var pathFill = fill.fillPath({\n            seriesNumber: 0,\n            size: opts.size,\n            fillColors: Array.isArray(trackConfig.background) ? trackConfig.background[i] : trackConfig.background,\n            solid: true\n          });\n          var startAngle = this.trackStartAngle;\n          var endAngle = this.trackEndAngle;\n          if (Math.abs(endAngle) + Math.abs(startAngle) >= 360) endAngle = 360 - Math.abs(this.startAngle) - 0.1;\n          var elPath = graphics.drawPath({\n            d: '',\n            stroke: pathFill,\n            strokeWidth: strokeWidth * parseInt(trackConfig.strokeWidth, 10) / 100,\n            fill: 'none',\n            strokeOpacity: trackConfig.opacity,\n            classes: 'apexcharts-radialbar-area'\n          });\n\n          if (trackConfig.dropShadow.enabled) {\n            var shadow = trackConfig.dropShadow;\n            filters.dropShadow(elPath, shadow);\n          }\n\n          elRadialBarTrack.add(elPath);\n          elPath.attr('id', 'apexcharts-radialbarTrack-' + i);\n          this.animatePaths(elPath, {\n            centerX: opts.centerX,\n            centerY: opts.centerY,\n            endAngle: endAngle,\n            startAngle: startAngle,\n            size: opts.size,\n            i: i,\n            totalItems: 2,\n            animBeginArr: 0,\n            dur: 0,\n            isTrack: true,\n            easing: w.globals.easing\n          });\n        }\n\n        return g;\n      }\n    }, {\n      key: \"drawArcs\",\n      value: function drawArcs(opts) {\n        var w = this.w; // size, donutSize, centerX, centerY, colorArr, lineColorArr, sectorAngleArr, series\n\n        var graphics = new Graphics(this.ctx);\n        var fill = new Fill(this.ctx);\n        var filters = new Filters(this.ctx);\n        var g = graphics.group();\n        var strokeWidth = this.getStrokeWidth(opts);\n        opts.size = opts.size - strokeWidth / 2;\n        var hollowFillID = w.config.plotOptions.radialBar.hollow.background;\n        var hollowSize = opts.size - strokeWidth * opts.series.length - this.margin * opts.series.length - strokeWidth * parseInt(w.config.plotOptions.radialBar.track.strokeWidth, 10) / 100 / 2;\n        var hollowRadius = hollowSize - w.config.plotOptions.radialBar.hollow.margin;\n\n        if (w.config.plotOptions.radialBar.hollow.image !== undefined) {\n          hollowFillID = this.drawHollowImage(opts, g, hollowSize, hollowFillID);\n        }\n\n        var elHollow = this.drawHollow({\n          size: hollowRadius,\n          centerX: opts.centerX,\n          centerY: opts.centerY,\n          fill: hollowFillID ? hollowFillID : 'transparent'\n        });\n\n        if (w.config.plotOptions.radialBar.hollow.dropShadow.enabled) {\n          var shadow = w.config.plotOptions.radialBar.hollow.dropShadow;\n          filters.dropShadow(elHollow, shadow);\n        }\n\n        var shown = 1;\n\n        if (!this.radialDataLabels.total.show && w.globals.series.length > 1) {\n          shown = 0;\n        }\n\n        var dataLabels = null;\n\n        if (this.radialDataLabels.show) {\n          dataLabels = this.renderInnerDataLabels(this.radialDataLabels, {\n            hollowSize: hollowSize,\n            centerX: opts.centerX,\n            centerY: opts.centerY,\n            opacity: shown\n          });\n        }\n\n        if (w.config.plotOptions.radialBar.hollow.position === 'back') {\n          g.add(elHollow);\n\n          if (dataLabels) {\n            g.add(dataLabels);\n          }\n        }\n\n        var reverseLoop = false;\n\n        if (w.config.plotOptions.radialBar.inverseOrder) {\n          reverseLoop = true;\n        }\n\n        for (var i = reverseLoop ? opts.series.length - 1 : 0; reverseLoop ? i >= 0 : i < opts.series.length; reverseLoop ? i-- : i++) {\n          var elRadialBarArc = graphics.group({\n            class: \"apexcharts-series apexcharts-radial-series\",\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[i])\n          });\n          g.add(elRadialBarArc);\n          elRadialBarArc.attr({\n            rel: i + 1,\n            'data:realIndex': i\n          });\n          this.ctx.series.addCollapsedClassToSeries(elRadialBarArc, i);\n          opts.size = opts.size - strokeWidth - this.margin;\n          var pathFill = fill.fillPath({\n            seriesNumber: i,\n            size: opts.size,\n            value: opts.series[i]\n          });\n          var startAngle = this.startAngle;\n          var prevStartAngle = void 0; // if data exceeds 100, make it 100\n\n          var dataValue = Utils$1.negToZero(opts.series[i] > 100 ? 100 : opts.series[i]) / 100;\n          var endAngle = Math.round(this.totalAngle * dataValue) + this.startAngle;\n          var prevEndAngle = void 0;\n\n          if (w.globals.dataChanged) {\n            prevStartAngle = this.startAngle;\n            prevEndAngle = Math.round(this.totalAngle * Utils$1.negToZero(w.globals.previousPaths[i]) / 100) + prevStartAngle;\n          }\n\n          var currFullAngle = Math.abs(endAngle) + Math.abs(startAngle);\n\n          if (currFullAngle >= 360) {\n            endAngle = endAngle - 0.01;\n          }\n\n          var prevFullAngle = Math.abs(prevEndAngle) + Math.abs(prevStartAngle);\n\n          if (prevFullAngle >= 360) {\n            prevEndAngle = prevEndAngle - 0.01;\n          }\n\n          var angle = endAngle - startAngle;\n          var dashArray = Array.isArray(w.config.stroke.dashArray) ? w.config.stroke.dashArray[i] : w.config.stroke.dashArray;\n          var elPath = graphics.drawPath({\n            d: '',\n            stroke: pathFill,\n            strokeWidth: strokeWidth,\n            fill: 'none',\n            fillOpacity: w.config.fill.opacity,\n            classes: 'apexcharts-radialbar-area apexcharts-radialbar-slice-' + i,\n            strokeDashArray: dashArray\n          });\n          Graphics.setAttrs(elPath.node, {\n            'data:angle': angle,\n            'data:value': opts.series[i]\n          });\n\n          if (w.config.chart.dropShadow.enabled) {\n            var _shadow = w.config.chart.dropShadow;\n            filters.dropShadow(elPath, _shadow, i);\n          }\n\n          filters.setSelectionFilter(elPath, 0, i);\n          this.addListeners(elPath, this.radialDataLabels);\n          elRadialBarArc.add(elPath);\n          elPath.attr({\n            index: 0,\n            j: i\n          });\n          var dur = 0;\n\n          if (this.initialAnim && !w.globals.resized && !w.globals.dataChanged) {\n            dur = w.config.chart.animations.speed;\n          }\n\n          if (w.globals.dataChanged) {\n            dur = w.config.chart.animations.dynamicAnimation.speed;\n          }\n\n          this.animDur = dur / (opts.series.length * 1.2) + this.animDur;\n          this.animBeginArr.push(this.animDur);\n          this.animatePaths(elPath, {\n            centerX: opts.centerX,\n            centerY: opts.centerY,\n            endAngle: endAngle,\n            startAngle: startAngle,\n            prevEndAngle: prevEndAngle,\n            prevStartAngle: prevStartAngle,\n            size: opts.size,\n            i: i,\n            totalItems: 2,\n            animBeginArr: this.animBeginArr,\n            dur: dur,\n            shouldSetPrevPaths: true,\n            easing: w.globals.easing\n          });\n        }\n\n        return {\n          g: g,\n          elHollow: elHollow,\n          dataLabels: dataLabels\n        };\n      }\n    }, {\n      key: \"drawHollow\",\n      value: function drawHollow(opts) {\n        var graphics = new Graphics(this.ctx);\n        var circle = graphics.drawCircle(opts.size * 2);\n        circle.attr({\n          class: 'apexcharts-radialbar-hollow',\n          cx: opts.centerX,\n          cy: opts.centerY,\n          r: opts.size,\n          fill: opts.fill\n        });\n        return circle;\n      }\n    }, {\n      key: \"drawHollowImage\",\n      value: function drawHollowImage(opts, g, hollowSize, hollowFillID) {\n        var w = this.w;\n        var fill = new Fill(this.ctx);\n        var randID = Utils$1.randomId();\n        var hollowFillImg = w.config.plotOptions.radialBar.hollow.image;\n\n        if (w.config.plotOptions.radialBar.hollow.imageClipped) {\n          fill.clippedImgArea({\n            width: hollowSize,\n            height: hollowSize,\n            image: hollowFillImg,\n            patternID: \"pattern\".concat(w.globals.cuid).concat(randID)\n          });\n          hollowFillID = \"url(#pattern\".concat(w.globals.cuid).concat(randID, \")\");\n        } else {\n          var imgWidth = w.config.plotOptions.radialBar.hollow.imageWidth;\n          var imgHeight = w.config.plotOptions.radialBar.hollow.imageHeight;\n\n          if (imgWidth === undefined && imgHeight === undefined) {\n            var image = w.globals.dom.Paper.image(hollowFillImg).loaded(function (loader) {\n              this.move(opts.centerX - loader.width / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetX, opts.centerY - loader.height / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetY);\n            });\n            g.add(image);\n          } else {\n            var _image = w.globals.dom.Paper.image(hollowFillImg).loaded(function (loader) {\n              this.move(opts.centerX - imgWidth / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetX, opts.centerY - imgHeight / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetY);\n              this.size(imgWidth, imgHeight);\n            });\n\n            g.add(_image);\n          }\n        }\n\n        return hollowFillID;\n      }\n    }, {\n      key: \"getStrokeWidth\",\n      value: function getStrokeWidth(opts) {\n        var w = this.w;\n        return opts.size * (100 - parseInt(w.config.plotOptions.radialBar.hollow.size, 10)) / 100 / (opts.series.length + 1) - this.margin;\n      }\n    }]);\n\n    return Radial;\n  }(Pie);\n\n  /**\n   * ApexCharts RangeBar Class responsible for drawing Range/Timeline Bars.\n   *\n   * @module RangeBar\n   **/\n\n  var RangeBar = /*#__PURE__*/function (_Bar) {\n    _inherits(RangeBar, _Bar);\n\n    var _super = _createSuper(RangeBar);\n\n    function RangeBar() {\n      _classCallCheck(this, RangeBar);\n\n      return _super.apply(this, arguments);\n    }\n\n    _createClass(RangeBar, [{\n      key: \"draw\",\n      value: function draw(series, seriesIndex) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        this.rangeBarOptions = this.w.config.plotOptions.rangeBar;\n        this.series = series;\n        this.seriesRangeStart = w.globals.seriesRangeStart;\n        this.seriesRangeEnd = w.globals.seriesRangeEnd;\n        this.barHelpers.initVariables(series);\n        var ret = graphics.group({\n          class: 'apexcharts-rangebar-series apexcharts-plot-series'\n        });\n\n        for (var i = 0; i < series.length; i++) {\n          var x = void 0,\n              y = void 0,\n              xDivision = void 0,\n              // xDivision is the GRIDWIDTH divided by number of datapoints (columns)\n          yDivision = void 0,\n              // yDivision is the GRIDHEIGHT divided by number of datapoints (bars)\n          zeroH = void 0,\n              // zeroH is the baseline where 0 meets y axis\n          zeroW = void 0; // zeroW is the baseline where 0 meets x axis\n\n          var realIndex = w.globals.comboCharts ? seriesIndex[i] : i; // el to which series will be drawn\n\n          var elSeries = graphics.group({\n            class: \"apexcharts-series\",\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[realIndex]),\n            rel: i + 1,\n            'data:realIndex': realIndex\n          });\n          this.ctx.series.addCollapsedClassToSeries(elSeries, realIndex);\n\n          if (series[i].length > 0) {\n            this.visibleI = this.visibleI + 1;\n          }\n\n          var barHeight = 0;\n          var barWidth = 0;\n\n          if (this.yRatio.length > 1) {\n            this.yaxisIndex = realIndex;\n          }\n\n          var initPositions = this.barHelpers.initialPositions();\n          y = initPositions.y;\n          zeroW = initPositions.zeroW;\n          x = initPositions.x;\n          barWidth = initPositions.barWidth;\n          xDivision = initPositions.xDivision;\n          zeroH = initPositions.zeroH; // eldatalabels\n\n          var elDataLabelsWrap = graphics.group({\n            class: 'apexcharts-datalabels',\n            'data:realIndex': realIndex\n          });\n          var elGoalsMarkers = graphics.group({\n            class: 'apexcharts-rangebar-goals-markers',\n            style: \"pointer-events: none\"\n          });\n\n          for (var j = 0; j < w.globals.dataPoints; j++) {\n            var strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex);\n            var y1 = this.seriesRangeStart[i][j];\n            var y2 = this.seriesRangeEnd[i][j];\n            var paths = null;\n            var barYPosition = null;\n            var params = {\n              x: x,\n              y: y,\n              strokeWidth: strokeWidth,\n              elSeries: elSeries\n            };\n            yDivision = initPositions.yDivision;\n            barHeight = initPositions.barHeight;\n\n            if (this.isHorizontal) {\n              barYPosition = y + barHeight * this.visibleI;\n              var seriesLen = this.seriesLen;\n\n              if (w.config.plotOptions.bar.rangeBarGroupRows) {\n                seriesLen = 1;\n              }\n\n              var srty = (yDivision - barHeight * seriesLen) / 2;\n\n              if (typeof w.config.series[i].data[j] === 'undefined') {\n                // no data exists for further indexes, hence we need to get out the innr loop.\n                // As we are iterating over total datapoints, there is a possiblity the series might not have data for j index\n                break;\n              }\n\n              if (w.config.series[i].data[j].x) {\n                var positions = this.detectOverlappingBars({\n                  i: i,\n                  j: j,\n                  barYPosition: barYPosition,\n                  srty: srty,\n                  barHeight: barHeight,\n                  yDivision: yDivision,\n                  initPositions: initPositions\n                });\n                barHeight = positions.barHeight;\n                barYPosition = positions.barYPosition;\n              }\n\n              paths = this.drawRangeBarPaths(_objectSpread2({\n                indexes: {\n                  i: i,\n                  j: j,\n                  realIndex: realIndex\n                },\n                barHeight: barHeight,\n                barYPosition: barYPosition,\n                zeroW: zeroW,\n                yDivision: yDivision,\n                y1: y1,\n                y2: y2\n              }, params));\n              barWidth = paths.barWidth;\n            } else {\n              paths = this.drawRangeColumnPaths(_objectSpread2({\n                indexes: {\n                  i: i,\n                  j: j,\n                  realIndex: realIndex\n                },\n                zeroH: zeroH,\n                barWidth: barWidth,\n                xDivision: xDivision\n              }, params));\n              barHeight = paths.barHeight;\n            }\n\n            var barGoalLine = this.barHelpers.drawGoalLine({\n              barXPosition: paths.barXPosition,\n              barYPosition: barYPosition,\n              goalX: paths.goalX,\n              goalY: paths.goalY,\n              barHeight: barHeight,\n              barWidth: barWidth\n            });\n\n            if (barGoalLine) {\n              elGoalsMarkers.add(barGoalLine);\n            }\n\n            y = paths.y;\n            x = paths.x;\n            var pathFill = this.barHelpers.getPathFillColor(series, i, j, realIndex);\n            var lineFill = w.globals.stroke.colors[realIndex];\n            this.renderSeries({\n              realIndex: realIndex,\n              pathFill: pathFill,\n              lineFill: lineFill,\n              j: j,\n              i: i,\n              x: x,\n              y: y,\n              y1: y1,\n              y2: y2,\n              pathFrom: paths.pathFrom,\n              pathTo: paths.pathTo,\n              strokeWidth: strokeWidth,\n              elSeries: elSeries,\n              series: series,\n              barHeight: barHeight,\n              barYPosition: barYPosition,\n              barWidth: barWidth,\n              elDataLabelsWrap: elDataLabelsWrap,\n              elGoalsMarkers: elGoalsMarkers,\n              visibleSeries: this.visibleI,\n              type: 'rangebar'\n            });\n          }\n\n          ret.add(elSeries);\n        }\n\n        return ret;\n      }\n    }, {\n      key: \"detectOverlappingBars\",\n      value: function detectOverlappingBars(_ref) {\n        var i = _ref.i,\n            j = _ref.j,\n            barYPosition = _ref.barYPosition,\n            srty = _ref.srty,\n            barHeight = _ref.barHeight,\n            yDivision = _ref.yDivision,\n            initPositions = _ref.initPositions;\n        var w = this.w;\n        var overlaps = [];\n        var rangeName = w.config.series[i].data[j].rangeName;\n        var labelX = w.config.series[i].data[j].x;\n        var rowIndex = w.globals.labels.indexOf(labelX);\n        var overlappedIndex = w.globals.seriesRange[i].findIndex(function (tx) {\n          return tx.x === labelX && tx.overlaps.length > 0;\n        });\n\n        if (w.config.plotOptions.bar.rangeBarGroupRows) {\n          barYPosition = srty + yDivision * rowIndex;\n        } else {\n          barYPosition = srty + barHeight * this.visibleI + yDivision * rowIndex;\n        }\n\n        if (overlappedIndex > -1 && !w.config.plotOptions.bar.rangeBarOverlap) {\n          overlaps = w.globals.seriesRange[i][overlappedIndex].overlaps;\n\n          if (overlaps.indexOf(rangeName) > -1) {\n            barHeight = initPositions.barHeight / overlaps.length;\n            barYPosition = barHeight * this.visibleI + yDivision * (100 - parseInt(this.barOptions.barHeight, 10)) / 100 / 2 + barHeight * (this.visibleI + overlaps.indexOf(rangeName)) + yDivision * rowIndex;\n          }\n        }\n\n        return {\n          barYPosition: barYPosition,\n          barHeight: barHeight\n        };\n      }\n    }, {\n      key: \"drawRangeColumnPaths\",\n      value: function drawRangeColumnPaths(_ref2) {\n        var indexes = _ref2.indexes,\n            x = _ref2.x;\n            _ref2.strokeWidth;\n            var xDivision = _ref2.xDivision,\n            barWidth = _ref2.barWidth,\n            zeroH = _ref2.zeroH;\n        var w = this.w;\n        var i = indexes.i;\n        var j = indexes.j;\n        var yRatio = this.yRatio[this.yaxisIndex];\n        var realIndex = indexes.realIndex;\n        var range = this.getRangeValue(realIndex, j);\n        var y1 = Math.min(range.start, range.end);\n        var y2 = Math.max(range.start, range.end);\n\n        if (w.globals.isXNumeric) {\n          x = (w.globals.seriesX[i][j] - w.globals.minX) / this.xRatio - barWidth / 2;\n        }\n\n        var barXPosition = x + barWidth * this.visibleI;\n\n        if (typeof this.series[i][j] === 'undefined' || this.series[i][j] === null) {\n          y1 = zeroH;\n        } else {\n          y1 = zeroH - y1 / yRatio;\n          y2 = zeroH - y2 / yRatio;\n        }\n\n        var barHeight = Math.abs(y2 - y1);\n        var paths = this.barHelpers.getColumnPaths({\n          barXPosition: barXPosition,\n          barWidth: barWidth,\n          y1: y1,\n          y2: y2,\n          strokeWidth: this.strokeWidth,\n          series: this.seriesRangeEnd,\n          realIndex: indexes.realIndex,\n          i: realIndex,\n          j: j,\n          w: w\n        });\n\n        if (!w.globals.isXNumeric) {\n          x = x + xDivision;\n        }\n\n        return {\n          pathTo: paths.pathTo,\n          pathFrom: paths.pathFrom,\n          barHeight: barHeight,\n          x: x,\n          y: y2,\n          goalY: this.barHelpers.getGoalValues('y', null, zeroH, i, j),\n          barXPosition: barXPosition\n        };\n      }\n    }, {\n      key: \"drawRangeBarPaths\",\n      value: function drawRangeBarPaths(_ref3) {\n        var indexes = _ref3.indexes,\n            y = _ref3.y,\n            y1 = _ref3.y1,\n            y2 = _ref3.y2,\n            yDivision = _ref3.yDivision,\n            barHeight = _ref3.barHeight,\n            barYPosition = _ref3.barYPosition,\n            zeroW = _ref3.zeroW;\n        var w = this.w;\n        var x1 = zeroW + y1 / this.invertedYRatio;\n        var x2 = zeroW + y2 / this.invertedYRatio;\n        var barWidth = Math.abs(x2 - x1);\n        var paths = this.barHelpers.getBarpaths({\n          barYPosition: barYPosition,\n          barHeight: barHeight,\n          x1: x1,\n          x2: x2,\n          strokeWidth: this.strokeWidth,\n          series: this.seriesRangeEnd,\n          i: indexes.realIndex,\n          realIndex: indexes.realIndex,\n          j: indexes.j,\n          w: w\n        });\n\n        if (!w.globals.isXNumeric) {\n          y = y + yDivision;\n        }\n\n        return {\n          pathTo: paths.pathTo,\n          pathFrom: paths.pathFrom,\n          barWidth: barWidth,\n          x: x2,\n          goalX: this.barHelpers.getGoalValues('x', zeroW, null, indexes.realIndex, indexes.j),\n          y: y\n        };\n      }\n    }, {\n      key: \"getRangeValue\",\n      value: function getRangeValue(i, j) {\n        var w = this.w;\n        return {\n          start: w.globals.seriesRangeStart[i][j],\n          end: w.globals.seriesRangeEnd[i][j]\n        };\n      }\n    }]);\n\n    return RangeBar;\n  }(Bar);\n\n  var Helpers = /*#__PURE__*/function () {\n    function Helpers(lineCtx) {\n      _classCallCheck(this, Helpers);\n\n      this.w = lineCtx.w;\n      this.lineCtx = lineCtx;\n    }\n\n    _createClass(Helpers, [{\n      key: \"sameValueSeriesFix\",\n      value: function sameValueSeriesFix(i, series) {\n        var w = this.w;\n\n        if (w.config.fill.type === 'gradient' || w.config.fill.type[i] === 'gradient') {\n          var coreUtils = new CoreUtils(this.lineCtx.ctx, w); // applied only to LINE chart\n          // a small adjustment to allow gradient line to draw correctly for all same values\n\n          /* #fix https://github.com/apexcharts/apexcharts.js/issues/358 */\n\n          if (coreUtils.seriesHaveSameValues(i)) {\n            var gSeries = series[i].slice();\n            gSeries[gSeries.length - 1] = gSeries[gSeries.length - 1] + 0.000001;\n            series[i] = gSeries;\n          }\n        }\n\n        return series;\n      }\n    }, {\n      key: \"calculatePoints\",\n      value: function calculatePoints(_ref) {\n        var series = _ref.series,\n            realIndex = _ref.realIndex,\n            x = _ref.x,\n            y = _ref.y,\n            i = _ref.i,\n            j = _ref.j,\n            prevY = _ref.prevY;\n        var w = this.w;\n        var ptX = [];\n        var ptY = [];\n\n        if (j === 0) {\n          var xPT1st = this.lineCtx.categoryAxisCorrection + w.config.markers.offsetX; // the first point for line series\n          // we need to check whether it's not a time series, because a time series may\n          // start from the middle of the x axis\n\n          if (w.globals.isXNumeric) {\n            xPT1st = (w.globals.seriesX[realIndex][0] - w.globals.minX) / this.lineCtx.xRatio + w.config.markers.offsetX;\n          } // push 2 points for the first data values\n\n\n          ptX.push(xPT1st);\n          ptY.push(Utils$1.isNumber(series[i][0]) ? prevY + w.config.markers.offsetY : null);\n          ptX.push(x + w.config.markers.offsetX);\n          ptY.push(Utils$1.isNumber(series[i][j + 1]) ? y + w.config.markers.offsetY : null);\n        } else {\n          ptX.push(x + w.config.markers.offsetX);\n          ptY.push(Utils$1.isNumber(series[i][j + 1]) ? y + w.config.markers.offsetY : null);\n        }\n\n        var pointsPos = {\n          x: ptX,\n          y: ptY\n        };\n        return pointsPos;\n      }\n    }, {\n      key: \"checkPreviousPaths\",\n      value: function checkPreviousPaths(_ref2) {\n        var pathFromLine = _ref2.pathFromLine,\n            pathFromArea = _ref2.pathFromArea,\n            realIndex = _ref2.realIndex;\n        var w = this.w;\n\n        for (var pp = 0; pp < w.globals.previousPaths.length; pp++) {\n          var gpp = w.globals.previousPaths[pp];\n\n          if ((gpp.type === 'line' || gpp.type === 'area') && gpp.paths.length > 0 && parseInt(gpp.realIndex, 10) === parseInt(realIndex, 10)) {\n            if (gpp.type === 'line') {\n              this.lineCtx.appendPathFrom = false;\n              pathFromLine = w.globals.previousPaths[pp].paths[0].d;\n            } else if (gpp.type === 'area') {\n              this.lineCtx.appendPathFrom = false;\n              pathFromArea = w.globals.previousPaths[pp].paths[0].d;\n\n              if (w.config.stroke.show && w.globals.previousPaths[pp].paths[1]) {\n                pathFromLine = w.globals.previousPaths[pp].paths[1].d;\n              }\n            }\n          }\n        }\n\n        return {\n          pathFromLine: pathFromLine,\n          pathFromArea: pathFromArea\n        };\n      }\n    }, {\n      key: \"determineFirstPrevY\",\n      value: function determineFirstPrevY(_ref3) {\n        var _series$i;\n\n        var i = _ref3.i,\n            series = _ref3.series,\n            prevY = _ref3.prevY,\n            lineYPosition = _ref3.lineYPosition;\n        var w = this.w;\n\n        if (typeof ((_series$i = series[i]) === null || _series$i === void 0 ? void 0 : _series$i[0]) !== 'undefined') {\n          if (w.config.chart.stacked) {\n            if (i > 0) {\n              // 1st y value of previous series\n              lineYPosition = this.lineCtx.prevSeriesY[i - 1][0];\n            } else {\n              // the first series will not have prevY values\n              lineYPosition = this.lineCtx.zeroY;\n            }\n          } else {\n            lineYPosition = this.lineCtx.zeroY;\n          }\n\n          prevY = lineYPosition - series[i][0] / this.lineCtx.yRatio[this.lineCtx.yaxisIndex] + (this.lineCtx.isReversed ? series[i][0] / this.lineCtx.yRatio[this.lineCtx.yaxisIndex] : 0) * 2;\n        } else {\n          // the first value in the current series is null\n          if (w.config.chart.stacked && i > 0 && typeof series[i][0] === 'undefined') {\n            // check for undefined value (undefined value will occur when we clear the series while user clicks on legend to hide serieses)\n            for (var s = i - 1; s >= 0; s--) {\n              // for loop to get to 1st previous value until we get it\n              if (series[s][0] !== null && typeof series[s][0] !== 'undefined') {\n                lineYPosition = this.lineCtx.prevSeriesY[s][0];\n                prevY = lineYPosition;\n                break;\n              }\n            }\n          }\n        }\n\n        return {\n          prevY: prevY,\n          lineYPosition: lineYPosition\n        };\n      }\n    }]);\n\n    return Helpers;\n  }();\n\n  /**\n   * ApexCharts Line Class responsible for drawing Line / Area / RangeArea Charts.\n   * This class is also responsible for generating values for Bubble/Scatter charts, so need to rename it to Axis Charts to avoid confusions\n   * @module Line\n   **/\n\n  var Line = /*#__PURE__*/function () {\n    function Line(ctx, xyRatios, isPointsChart) {\n      _classCallCheck(this, Line);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.xyRatios = xyRatios;\n      this.pointsChart = !(this.w.config.chart.type !== 'bubble' && this.w.config.chart.type !== 'scatter') || isPointsChart;\n      this.scatter = new Scatter(this.ctx);\n      this.noNegatives = this.w.globals.minX === Number.MAX_VALUE;\n      this.lineHelpers = new Helpers(this);\n      this.markers = new Markers(this.ctx);\n      this.prevSeriesY = [];\n      this.categoryAxisCorrection = 0;\n      this.yaxisIndex = 0;\n    }\n\n    _createClass(Line, [{\n      key: \"draw\",\n      value: function draw(series, ctype, seriesIndex, seriesRangeEnd) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var type = w.globals.comboCharts ? ctype : w.config.chart.type;\n        var ret = graphics.group({\n          class: \"apexcharts-\".concat(type, \"-series apexcharts-plot-series\")\n        });\n        var coreUtils = new CoreUtils(this.ctx, w);\n        this.yRatio = this.xyRatios.yRatio;\n        this.zRatio = this.xyRatios.zRatio;\n        this.xRatio = this.xyRatios.xRatio;\n        this.baseLineY = this.xyRatios.baseLineY;\n        series = coreUtils.getLogSeries(series);\n        this.yRatio = coreUtils.getLogYRatios(this.yRatio); // push all series in an array, so we can draw in reverse order (for stacked charts)\n\n        var allSeries = [];\n\n        for (var i = 0; i < series.length; i++) {\n          series = this.lineHelpers.sameValueSeriesFix(i, series);\n          var realIndex = w.globals.comboCharts ? seriesIndex[i] : i;\n\n          this._initSerieVariables(series, i, realIndex);\n\n          var yArrj = []; // hold y values of current iterating series\n\n          var xArrj = []; // hold x values of current iterating series\n\n          var x = w.globals.padHorizontal + this.categoryAxisCorrection;\n          var y = 1;\n          var linePaths = [];\n          var areaPaths = [];\n          this.ctx.series.addCollapsedClassToSeries(this.elSeries, realIndex);\n\n          if (w.globals.isXNumeric && w.globals.seriesX.length > 0) {\n            x = (w.globals.seriesX[realIndex][0] - w.globals.minX) / this.xRatio;\n          }\n\n          xArrj.push(x);\n          var pX = x;\n          var pY = void 0;\n          var pY2 = void 0;\n          var prevX = pX;\n          var prevY = this.zeroY;\n          var prevY2 = this.zeroY;\n          var lineYPosition = 0; // the first value in the current series is not null or undefined\n\n          var firstPrevY = this.lineHelpers.determineFirstPrevY({\n            i: i,\n            series: series,\n            prevY: prevY,\n            lineYPosition: lineYPosition\n          });\n          prevY = firstPrevY.prevY;\n          yArrj.push(prevY);\n          pY = prevY; // y2 are needed for range-area charts\n\n          var firstPrevY2 = void 0;\n\n          if (type === 'rangeArea') {\n            firstPrevY2 = this.lineHelpers.determineFirstPrevY({\n              i: i,\n              series: seriesRangeEnd,\n              prevY: prevY2,\n              lineYPosition: lineYPosition\n            });\n            prevY2 = firstPrevY2.prevY;\n            pY2 = prevY2;\n          }\n\n          var pathsFrom = this._calculatePathsFrom({\n            type: type,\n            series: series,\n            i: i,\n            realIndex: realIndex,\n            prevX: prevX,\n            prevY: prevY,\n            prevY2: prevY2\n          });\n\n          var iteratingOpts = {\n            type: type,\n            series: series,\n            realIndex: realIndex,\n            i: i,\n            x: x,\n            y: y,\n            pX: pX,\n            pY: pY,\n            pathsFrom: pathsFrom,\n            linePaths: linePaths,\n            areaPaths: areaPaths,\n            seriesIndex: seriesIndex,\n            lineYPosition: lineYPosition,\n            xArrj: xArrj,\n            yArrj: yArrj,\n            seriesRangeEnd: seriesRangeEnd\n          };\n\n          var paths = this._iterateOverDataPoints(_objectSpread2(_objectSpread2({}, iteratingOpts), {}, {\n            iterations: type === 'rangeArea' ? series[i].length - 1 : undefined,\n            isRangeStart: true\n          }));\n\n          if (type === 'rangeArea') {\n            var pathsFrom2 = this._calculatePathsFrom({\n              series: seriesRangeEnd,\n              i: i,\n              realIndex: realIndex,\n              prevX: prevX,\n              prevY: prevY2\n            });\n\n            var rangePaths = this._iterateOverDataPoints(_objectSpread2(_objectSpread2({}, iteratingOpts), {}, {\n              series: seriesRangeEnd,\n              pY: pY2,\n              pathsFrom: pathsFrom2,\n              iterations: seriesRangeEnd[i].length - 1,\n              isRangeStart: false\n            }));\n\n            paths.linePaths[0] = rangePaths.linePath + paths.linePath;\n            paths.pathFromLine = rangePaths.pathFromLine + paths.pathFromLine;\n          }\n\n          this._handlePaths({\n            type: type,\n            realIndex: realIndex,\n            i: i,\n            paths: paths\n          });\n\n          this.elSeries.add(this.elPointsMain);\n          this.elSeries.add(this.elDataLabelsWrap);\n          allSeries.push(this.elSeries);\n        }\n\n        if (w.config.chart.stacked) {\n          for (var s = allSeries.length; s > 0; s--) {\n            ret.add(allSeries[s - 1]);\n          }\n        } else {\n          for (var _s = 0; _s < allSeries.length; _s++) {\n            ret.add(allSeries[_s]);\n          }\n        }\n\n        return ret;\n      }\n    }, {\n      key: \"_initSerieVariables\",\n      value: function _initSerieVariables(series, i, realIndex) {\n        var w = this.w;\n        var graphics = new Graphics(this.ctx); // width divided into equal parts\n\n        this.xDivision = w.globals.gridWidth / (w.globals.dataPoints - (w.config.xaxis.tickPlacement === 'on' ? 1 : 0));\n        this.strokeWidth = Array.isArray(w.config.stroke.width) ? w.config.stroke.width[realIndex] : w.config.stroke.width;\n\n        if (this.yRatio.length > 1) {\n          this.yaxisIndex = realIndex;\n        }\n\n        this.isReversed = w.config.yaxis[this.yaxisIndex] && w.config.yaxis[this.yaxisIndex].reversed; // zeroY is the 0 value in y series which can be used in negative charts\n\n        this.zeroY = w.globals.gridHeight - this.baseLineY[this.yaxisIndex] - (this.isReversed ? w.globals.gridHeight : 0) + (this.isReversed ? this.baseLineY[this.yaxisIndex] * 2 : 0);\n        this.areaBottomY = this.zeroY;\n\n        if (this.zeroY > w.globals.gridHeight || w.config.plotOptions.area.fillTo === 'end') {\n          this.areaBottomY = w.globals.gridHeight;\n        }\n\n        this.categoryAxisCorrection = this.xDivision / 2; // el to which series will be drawn\n\n        this.elSeries = graphics.group({\n          class: \"apexcharts-series\",\n          seriesName: Utils$1.escapeString(w.globals.seriesNames[realIndex])\n        }); // points\n\n        this.elPointsMain = graphics.group({\n          class: 'apexcharts-series-markers-wrap',\n          'data:realIndex': realIndex\n        }); // eldatalabels\n\n        this.elDataLabelsWrap = graphics.group({\n          class: 'apexcharts-datalabels',\n          'data:realIndex': realIndex\n        });\n        var longestSeries = series[i].length === w.globals.dataPoints;\n        this.elSeries.attr({\n          'data:longestSeries': longestSeries,\n          rel: i + 1,\n          'data:realIndex': realIndex\n        });\n        this.appendPathFrom = true;\n      }\n    }, {\n      key: \"_calculatePathsFrom\",\n      value: function _calculatePathsFrom(_ref) {\n        var type = _ref.type,\n            series = _ref.series,\n            i = _ref.i,\n            realIndex = _ref.realIndex,\n            prevX = _ref.prevX,\n            prevY = _ref.prevY,\n            prevY2 = _ref.prevY2;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var linePath, areaPath, pathFromLine, pathFromArea;\n\n        if (series[i][0] === null) {\n          // when the first value itself is null, we need to move the pointer to a location where a null value is not found\n          for (var s = 0; s < series[i].length; s++) {\n            if (series[i][s] !== null) {\n              prevX = this.xDivision * s;\n              prevY = this.zeroY - series[i][s] / this.yRatio[this.yaxisIndex];\n              linePath = graphics.move(prevX, prevY);\n              areaPath = graphics.move(prevX, this.areaBottomY);\n              break;\n            }\n          }\n        } else {\n          linePath = graphics.move(prevX, prevY);\n\n          if (type === 'rangeArea') {\n            linePath = graphics.move(prevX, prevY2) + graphics.line(prevX, prevY);\n          }\n\n          areaPath = graphics.move(prevX, this.areaBottomY) + graphics.line(prevX, prevY);\n        }\n\n        pathFromLine = graphics.move(-1, this.zeroY) + graphics.line(-1, this.zeroY);\n        pathFromArea = graphics.move(-1, this.zeroY) + graphics.line(-1, this.zeroY);\n\n        if (w.globals.previousPaths.length > 0) {\n          var pathFrom = this.lineHelpers.checkPreviousPaths({\n            pathFromLine: pathFromLine,\n            pathFromArea: pathFromArea,\n            realIndex: realIndex\n          });\n          pathFromLine = pathFrom.pathFromLine;\n          pathFromArea = pathFrom.pathFromArea;\n        }\n\n        return {\n          prevX: prevX,\n          prevY: prevY,\n          linePath: linePath,\n          areaPath: areaPath,\n          pathFromLine: pathFromLine,\n          pathFromArea: pathFromArea\n        };\n      }\n    }, {\n      key: \"_handlePaths\",\n      value: function _handlePaths(_ref2) {\n        var type = _ref2.type,\n            realIndex = _ref2.realIndex,\n            i = _ref2.i,\n            paths = _ref2.paths;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var fill = new Fill(this.ctx); // push all current y values array to main PrevY Array\n\n        this.prevSeriesY.push(paths.yArrj); // push all x val arrays into main xArr\n\n        w.globals.seriesXvalues[realIndex] = paths.xArrj;\n        w.globals.seriesYvalues[realIndex] = paths.yArrj;\n        var forecast = w.config.forecastDataPoints;\n\n        if (forecast.count > 0 && type !== 'rangeArea') {\n          var forecastCutoff = w.globals.seriesXvalues[realIndex][w.globals.seriesXvalues[realIndex].length - forecast.count - 1];\n          var elForecastMask = graphics.drawRect(forecastCutoff, 0, w.globals.gridWidth, w.globals.gridHeight, 0);\n          w.globals.dom.elForecastMask.appendChild(elForecastMask.node);\n          var elNonForecastMask = graphics.drawRect(0, 0, forecastCutoff, w.globals.gridHeight, 0);\n          w.globals.dom.elNonForecastMask.appendChild(elNonForecastMask.node);\n        } // these elements will be shown after area path animation completes\n\n\n        if (!this.pointsChart) {\n          w.globals.delayedElements.push({\n            el: this.elPointsMain.node,\n            index: realIndex\n          });\n        }\n\n        var defaultRenderedPathOptions = {\n          i: i,\n          realIndex: realIndex,\n          animationDelay: i,\n          initialSpeed: w.config.chart.animations.speed,\n          dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,\n          className: \"apexcharts-\".concat(type)\n        };\n\n        if (type === 'area') {\n          var pathFill = fill.fillPath({\n            seriesNumber: realIndex\n          });\n\n          for (var p = 0; p < paths.areaPaths.length; p++) {\n            var renderedPath = graphics.renderPaths(_objectSpread2(_objectSpread2({}, defaultRenderedPathOptions), {}, {\n              pathFrom: paths.pathFromArea,\n              pathTo: paths.areaPaths[p],\n              stroke: 'none',\n              strokeWidth: 0,\n              strokeLineCap: null,\n              fill: pathFill\n            }));\n            this.elSeries.add(renderedPath);\n          }\n        }\n\n        if (w.config.stroke.show && !this.pointsChart) {\n          var lineFill = null;\n\n          if (type === 'line') {\n            lineFill = fill.fillPath({\n              seriesNumber: realIndex,\n              i: i\n            });\n          } else {\n            if (w.config.stroke.fill.type === 'solid') {\n              lineFill = w.globals.stroke.colors[realIndex];\n            } else {\n              var prevFill = w.config.fill;\n              w.config.fill = w.config.stroke.fill;\n              lineFill = fill.fillPath({\n                seriesNumber: realIndex,\n                i: i\n              });\n              w.config.fill = prevFill;\n            }\n          } // range-area paths are drawn using linePaths\n\n\n          for (var _p = 0; _p < paths.linePaths.length; _p++) {\n            var _pathFill = lineFill;\n\n            if (type === 'rangeArea') {\n              _pathFill = fill.fillPath({\n                seriesNumber: realIndex\n              });\n            }\n\n            var linePathCommonOpts = _objectSpread2(_objectSpread2({}, defaultRenderedPathOptions), {}, {\n              pathFrom: paths.pathFromLine,\n              pathTo: paths.linePaths[_p],\n              stroke: lineFill,\n              strokeWidth: this.strokeWidth,\n              strokeLineCap: w.config.stroke.lineCap,\n              fill: type === 'rangeArea' ? _pathFill : 'none'\n            });\n\n            var _renderedPath = graphics.renderPaths(linePathCommonOpts);\n\n            this.elSeries.add(_renderedPath);\n\n            _renderedPath.attr('fill-rule', \"evenodd\");\n\n            if (forecast.count > 0 && type !== 'rangeArea') {\n              var renderedForecastPath = graphics.renderPaths(linePathCommonOpts);\n              renderedForecastPath.node.setAttribute('stroke-dasharray', forecast.dashArray);\n\n              if (forecast.strokeWidth) {\n                renderedForecastPath.node.setAttribute('stroke-width', forecast.strokeWidth);\n              }\n\n              this.elSeries.add(renderedForecastPath);\n              renderedForecastPath.attr('clip-path', \"url(#forecastMask\".concat(w.globals.cuid, \")\"));\n\n              _renderedPath.attr('clip-path', \"url(#nonForecastMask\".concat(w.globals.cuid, \")\"));\n            }\n          }\n        }\n      }\n    }, {\n      key: \"_iterateOverDataPoints\",\n      value: function _iterateOverDataPoints(_ref3) {\n        var type = _ref3.type,\n            series = _ref3.series,\n            iterations = _ref3.iterations,\n            realIndex = _ref3.realIndex,\n            i = _ref3.i,\n            x = _ref3.x,\n            y = _ref3.y,\n            pX = _ref3.pX,\n            pY = _ref3.pY,\n            pathsFrom = _ref3.pathsFrom,\n            linePaths = _ref3.linePaths,\n            areaPaths = _ref3.areaPaths,\n            seriesIndex = _ref3.seriesIndex,\n            lineYPosition = _ref3.lineYPosition,\n            xArrj = _ref3.xArrj,\n            yArrj = _ref3.yArrj,\n            isRangeStart = _ref3.isRangeStart,\n            seriesRangeEnd = _ref3.seriesRangeEnd;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var yRatio = this.yRatio;\n        var prevY = pathsFrom.prevY,\n            linePath = pathsFrom.linePath,\n            areaPath = pathsFrom.areaPath,\n            pathFromLine = pathsFrom.pathFromLine,\n            pathFromArea = pathsFrom.pathFromArea;\n        var minY = Utils$1.isNumber(w.globals.minYArr[realIndex]) ? w.globals.minYArr[realIndex] : w.globals.minY;\n\n        if (!iterations) {\n          iterations = w.globals.dataPoints > 1 ? w.globals.dataPoints - 1 : w.globals.dataPoints;\n        }\n\n        var y2 = y;\n\n        for (var j = 0; j < iterations; j++) {\n          var isNull = typeof series[i][j + 1] === 'undefined' || series[i][j + 1] === null;\n\n          if (w.globals.isXNumeric) {\n            var sX = w.globals.seriesX[realIndex][j + 1];\n\n            if (typeof w.globals.seriesX[realIndex][j + 1] === 'undefined') {\n              /* fix #374 */\n              sX = w.globals.seriesX[realIndex][iterations - 1];\n            }\n\n            x = (sX - w.globals.minX) / this.xRatio;\n          } else {\n            x = x + this.xDivision;\n          }\n\n          if (w.config.chart.stacked) {\n            if (i > 0 && w.globals.collapsedSeries.length < w.config.series.length - 1) {\n              // a collapsed series in a stacked bar chart may provide wrong result for the next series, hence find the prevIndex of prev series which is not collapsed - fixes apexcharts.js#1372\n              var prevIndex = function prevIndex(pi) {\n                var pii = pi;\n\n                for (var cpi = 0; cpi < w.globals.series.length; cpi++) {\n                  if (w.globals.collapsedSeriesIndices.indexOf(pi) > -1) {\n                    pii--;\n                    break;\n                  }\n                }\n\n                return pii >= 0 ? pii : 0;\n              };\n\n              lineYPosition = this.prevSeriesY[prevIndex(i - 1)][j + 1];\n            } else {\n              // the first series will not have prevY values\n              lineYPosition = this.zeroY;\n            }\n          } else {\n            lineYPosition = this.zeroY;\n          }\n\n          if (isNull) {\n            y = lineYPosition - minY / yRatio[this.yaxisIndex] + (this.isReversed ? minY / yRatio[this.yaxisIndex] : 0) * 2;\n          } else {\n            y = lineYPosition - series[i][j + 1] / yRatio[this.yaxisIndex] + (this.isReversed ? series[i][j + 1] / yRatio[this.yaxisIndex] : 0) * 2;\n\n            if (type === 'rangeArea') {\n              y2 = lineYPosition - seriesRangeEnd[i][j + 1] / yRatio[this.yaxisIndex] + (this.isReversed ? seriesRangeEnd[i][j + 1] / yRatio[this.yaxisIndex] : 0) * 2;\n            }\n          } // push current X\n\n\n          xArrj.push(x); // push current Y that will be used as next series's bottom position\n\n          yArrj.push(y);\n          var pointsPos = this.lineHelpers.calculatePoints({\n            series: series,\n            x: x,\n            y: y,\n            realIndex: realIndex,\n            i: i,\n            j: j,\n            prevY: prevY\n          });\n\n          var calculatedPaths = this._createPaths({\n            type: type,\n            series: series,\n            i: i,\n            realIndex: realIndex,\n            j: j,\n            x: x,\n            y: y,\n            y2: y2,\n            pX: pX,\n            pY: pY,\n            linePath: linePath,\n            areaPath: areaPath,\n            linePaths: linePaths,\n            areaPaths: areaPaths,\n            seriesIndex: seriesIndex,\n            isRangeStart: isRangeStart\n          });\n\n          areaPaths = calculatedPaths.areaPaths;\n          linePaths = calculatedPaths.linePaths;\n          pX = calculatedPaths.pX;\n          pY = calculatedPaths.pY;\n          areaPath = calculatedPaths.areaPath;\n          linePath = calculatedPaths.linePath;\n\n          if (this.appendPathFrom) {\n            pathFromLine = pathFromLine + graphics.line(x, this.zeroY);\n            pathFromArea = pathFromArea + graphics.line(x, this.zeroY);\n          }\n\n          this.handleNullDataPoints(series, pointsPos, i, j, realIndex);\n\n          this._handleMarkersAndLabels({\n            type: type,\n            pointsPos: pointsPos,\n            i: i,\n            j: j,\n            realIndex: realIndex,\n            isRangeStart: isRangeStart\n          });\n        }\n\n        return {\n          yArrj: yArrj,\n          xArrj: xArrj,\n          pathFromArea: pathFromArea,\n          areaPaths: areaPaths,\n          pathFromLine: pathFromLine,\n          linePaths: linePaths,\n          linePath: linePath,\n          areaPath: areaPath\n        };\n      }\n    }, {\n      key: \"_handleMarkersAndLabels\",\n      value: function _handleMarkersAndLabels(_ref4) {\n        var type = _ref4.type,\n            pointsPos = _ref4.pointsPos,\n            isRangeStart = _ref4.isRangeStart,\n            i = _ref4.i,\n            j = _ref4.j,\n            realIndex = _ref4.realIndex;\n        var w = this.w;\n        var dataLabels = new DataLabels(this.ctx);\n\n        if (!this.pointsChart) {\n          if (w.globals.series[i].length > 1) {\n            this.elPointsMain.node.classList.add('apexcharts-element-hidden');\n          }\n\n          var elPointsWrap = this.markers.plotChartMarkers(pointsPos, realIndex, j + 1);\n\n          if (elPointsWrap !== null) {\n            this.elPointsMain.add(elPointsWrap);\n          }\n        } else {\n          // scatter / bubble chart points creation\n          this.scatter.draw(this.elSeries, j, {\n            realIndex: realIndex,\n            pointsPos: pointsPos,\n            zRatio: this.zRatio,\n            elParent: this.elPointsMain\n          });\n        }\n\n        var drawnLabels = dataLabels.drawDataLabel({\n          type: type,\n          isRangeStart: isRangeStart,\n          pos: pointsPos,\n          i: realIndex,\n          j: j + 1\n        });\n\n        if (drawnLabels !== null) {\n          this.elDataLabelsWrap.add(drawnLabels);\n        }\n      }\n    }, {\n      key: \"_createPaths\",\n      value: function _createPaths(_ref5) {\n        var type = _ref5.type,\n            series = _ref5.series,\n            i = _ref5.i,\n            realIndex = _ref5.realIndex,\n            j = _ref5.j,\n            x = _ref5.x,\n            y = _ref5.y,\n            y2 = _ref5.y2,\n            pX = _ref5.pX,\n            pY = _ref5.pY,\n            linePath = _ref5.linePath,\n            areaPath = _ref5.areaPath,\n            linePaths = _ref5.linePaths,\n            areaPaths = _ref5.areaPaths,\n            seriesIndex = _ref5.seriesIndex,\n            isRangeStart = _ref5.isRangeStart;\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var curve = w.config.stroke.curve;\n        var areaBottomY = this.areaBottomY;\n\n        if (Array.isArray(w.config.stroke.curve)) {\n          if (Array.isArray(seriesIndex)) {\n            curve = w.config.stroke.curve[seriesIndex[i]];\n          } else {\n            curve = w.config.stroke.curve[i];\n          }\n        } // logic of smooth curve derived from chartist\n        // CREDITS: https://gionkunz.github.io/chartist-js/\n\n\n        if (curve === 'smooth') {\n          var length = (x - pX) * 0.35;\n\n          if (w.globals.hasNullValues) {\n            if (series[i][j] !== null) {\n              if (series[i][j + 1] !== null) {\n                linePath = graphics.move(pX, pY) + graphics.curve(pX + length, pY, x - length, y, x + 1, y);\n                areaPath = graphics.move(pX + 1, pY) + graphics.curve(pX + length, pY, x - length, y, x + 1, y) + graphics.line(x, areaBottomY) + graphics.line(pX, areaBottomY) + 'z';\n              } else {\n                linePath = graphics.move(pX, pY);\n                areaPath = graphics.move(pX, pY) + 'z';\n              }\n            }\n\n            linePaths.push(linePath);\n            areaPaths.push(areaPath);\n          } else {\n            linePath = linePath + graphics.curve(pX + length, pY, x - length, y, x, y);\n            areaPath = areaPath + graphics.curve(pX + length, pY, x - length, y, x, y);\n          }\n\n          pX = x;\n          pY = y;\n\n          if (j === series[i].length - 2) {\n            // last loop, close path\n            areaPath = areaPath + graphics.curve(pX, pY, x, y, x, areaBottomY) + graphics.move(x, y) + 'z';\n\n            if (type === 'rangeArea' && isRangeStart) {\n              linePath = linePath + graphics.curve(pX, pY, x, y, x, y2) + graphics.move(x, y2) + 'z';\n            } else {\n              if (!w.globals.hasNullValues) {\n                linePaths.push(linePath);\n                areaPaths.push(areaPath);\n              }\n            }\n          }\n        } else {\n          if (series[i][j + 1] === null) {\n            linePath = linePath + graphics.move(x, y);\n            var numericOrCatX = w.globals.isXNumeric ? (w.globals.seriesX[realIndex][j] - w.globals.minX) / this.xRatio : x - this.xDivision;\n            areaPath = areaPath + graphics.line(numericOrCatX, areaBottomY) + graphics.move(x, y) + 'z';\n          }\n\n          if (series[i][j] === null) {\n            linePath = linePath + graphics.move(x, y);\n            areaPath = areaPath + graphics.move(x, areaBottomY);\n          }\n\n          if (curve === 'stepline') {\n            linePath = linePath + graphics.line(x, null, 'H') + graphics.line(null, y, 'V');\n            areaPath = areaPath + graphics.line(x, null, 'H') + graphics.line(null, y, 'V');\n          } else if (curve === 'straight') {\n            linePath = linePath + graphics.line(x, y);\n            areaPath = areaPath + graphics.line(x, y);\n          }\n\n          if (j === series[i].length - 2) {\n            // last loop, close path\n            areaPath = areaPath + graphics.line(x, areaBottomY) + graphics.move(x, y) + 'z';\n\n            if (type === 'rangeArea' && isRangeStart) {\n              linePath = linePath + graphics.line(x, y2) + graphics.move(x, y2) + 'z';\n            } else {\n              linePaths.push(linePath);\n              areaPaths.push(areaPath);\n            }\n          }\n        }\n\n        return {\n          linePaths: linePaths,\n          areaPaths: areaPaths,\n          pX: pX,\n          pY: pY,\n          linePath: linePath,\n          areaPath: areaPath\n        };\n      }\n    }, {\n      key: \"handleNullDataPoints\",\n      value: function handleNullDataPoints(series, pointsPos, i, j, realIndex) {\n        var w = this.w;\n\n        if (series[i][j] === null && w.config.markers.showNullDataPoints || series[i].length === 1) {\n          // fixes apexcharts.js#1282, #1252\n          var elPointsWrap = this.markers.plotChartMarkers(pointsPos, realIndex, j + 1, this.strokeWidth - w.config.markers.strokeWidth / 2, true);\n\n          if (elPointsWrap !== null) {\n            this.elPointsMain.add(elPointsWrap);\n          }\n        }\n      }\n    }]);\n\n    return Line;\n  }();\n\n  /*\n   * treemap-squarify.js - open source implementation of squarified treemaps\n   *\n   * Treemap Squared 0.5 - Treemap Charting library\n   *\n   * https://github.com/imranghory/treemap-squared/\n   *\n   * Copyright (c) 2012 Imran Ghory (imranghory@gmail.com)\n   * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.\n   *\n   *\n   * Implementation of the squarify treemap algorithm described in:\n   *\n   * Bruls, Mark; Huizing, Kees; van Wijk, Jarke J. (2000), \"Squarified treemaps\"\n   * in de Leeuw, W.; van Liere, R., Data Visualization 2000:\n   * Proc. Joint Eurographics and IEEE TCVG Symp. on Visualization, Springer-Verlag, pp. 33–42.\n   *\n   * Paper is available online at: http://www.win.tue.nl/~vanwijk/stm.pdf\n   *\n   * The code in this file is completeley decoupled from the drawing code so it should be trivial\n   * to port it to any other vector drawing library. Given an array of datapoints this library returns\n   * an array of cartesian coordinates that represent the rectangles that make up the treemap.\n   *\n   * The library also supports multidimensional data (nested treemaps) and performs normalization on the data.\n   *\n   * See the README file for more details.\n   */\n  window.TreemapSquared = {};\n\n  (function () {\n\n    window.TreemapSquared.generate = function () {\n      function Container(xoffset, yoffset, width, height) {\n        this.xoffset = xoffset; // offset from the the top left hand corner\n\n        this.yoffset = yoffset; // ditto\n\n        this.height = height;\n        this.width = width;\n\n        this.shortestEdge = function () {\n          return Math.min(this.height, this.width);\n        }; // getCoordinates - for a row of boxes which we've placed\n        //                  return an array of their cartesian coordinates\n\n\n        this.getCoordinates = function (row) {\n          var coordinates = [];\n          var subxoffset = this.xoffset,\n              subyoffset = this.yoffset; //our offset within the container\n\n          var areawidth = sumArray(row) / this.height;\n          var areaheight = sumArray(row) / this.width;\n          var i;\n\n          if (this.width >= this.height) {\n            for (i = 0; i < row.length; i++) {\n              coordinates.push([subxoffset, subyoffset, subxoffset + areawidth, subyoffset + row[i] / areawidth]);\n              subyoffset = subyoffset + row[i] / areawidth;\n            }\n          } else {\n            for (i = 0; i < row.length; i++) {\n              coordinates.push([subxoffset, subyoffset, subxoffset + row[i] / areaheight, subyoffset + areaheight]);\n              subxoffset = subxoffset + row[i] / areaheight;\n            }\n          }\n\n          return coordinates;\n        }; // cutArea - once we've placed some boxes into an row we then need to identify the remaining area,\n        //           this function takes the area of the boxes we've placed and calculates the location and\n        //           dimensions of the remaining space and returns a container box defined by the remaining area\n\n\n        this.cutArea = function (area) {\n          var newcontainer;\n\n          if (this.width >= this.height) {\n            var areawidth = area / this.height;\n            var newwidth = this.width - areawidth;\n            newcontainer = new Container(this.xoffset + areawidth, this.yoffset, newwidth, this.height);\n          } else {\n            var areaheight = area / this.width;\n            var newheight = this.height - areaheight;\n            newcontainer = new Container(this.xoffset, this.yoffset + areaheight, this.width, newheight);\n          }\n\n          return newcontainer;\n        };\n      } // normalize - the Bruls algorithm assumes we're passing in areas that nicely fit into our\n      //             container box, this method takes our raw data and normalizes the data values into\n      //             area values so that this assumption is valid.\n\n\n      function normalize(data, area) {\n        var normalizeddata = [];\n        var sum = sumArray(data);\n        var multiplier = area / sum;\n        var i;\n\n        for (i = 0; i < data.length; i++) {\n          normalizeddata[i] = data[i] * multiplier;\n        }\n\n        return normalizeddata;\n      } // treemapMultidimensional - takes multidimensional data (aka [[23,11],[11,32]] - nested array)\n      //                           and recursively calls itself using treemapSingledimensional\n      //                           to create a patchwork of treemaps and merge them\n\n\n      function treemapMultidimensional(data, width, height, xoffset, yoffset) {\n        xoffset = typeof xoffset === 'undefined' ? 0 : xoffset;\n        yoffset = typeof yoffset === 'undefined' ? 0 : yoffset;\n        var mergeddata = [];\n        var mergedtreemap;\n        var results = [];\n        var i;\n\n        if (isArray(data[0])) {\n          // if we've got more dimensions of depth\n          for (i = 0; i < data.length; i++) {\n            mergeddata[i] = sumMultidimensionalArray(data[i]);\n          }\n\n          mergedtreemap = treemapSingledimensional(mergeddata, width, height, xoffset, yoffset);\n\n          for (i = 0; i < data.length; i++) {\n            results.push(treemapMultidimensional(data[i], mergedtreemap[i][2] - mergedtreemap[i][0], mergedtreemap[i][3] - mergedtreemap[i][1], mergedtreemap[i][0], mergedtreemap[i][1]));\n          }\n        } else {\n          results = treemapSingledimensional(data, width, height, xoffset, yoffset);\n        }\n\n        return results;\n      } // treemapSingledimensional - simple wrapper around squarify\n\n\n      function treemapSingledimensional(data, width, height, xoffset, yoffset) {\n        xoffset = typeof xoffset === 'undefined' ? 0 : xoffset;\n        yoffset = typeof yoffset === 'undefined' ? 0 : yoffset;\n        var rawtreemap = squarify(normalize(data, width * height), [], new Container(xoffset, yoffset, width, height), []);\n        return flattenTreemap(rawtreemap);\n      } // flattenTreemap - squarify implementation returns an array of arrays of coordinates\n      //                  because we have a new array everytime we switch to building a new row\n      //                  this converts it into an array of coordinates.\n\n\n      function flattenTreemap(rawtreemap) {\n        var flattreemap = [];\n        var i, j;\n\n        for (i = 0; i < rawtreemap.length; i++) {\n          for (j = 0; j < rawtreemap[i].length; j++) {\n            flattreemap.push(rawtreemap[i][j]);\n          }\n        }\n\n        return flattreemap;\n      } // squarify  - as per the Bruls paper\n      //             plus coordinates stack and containers so we get\n      //             usable data out of it\n\n\n      function squarify(data, currentrow, container, stack) {\n        var length;\n        var nextdatapoint;\n        var newcontainer;\n\n        if (data.length === 0) {\n          stack.push(container.getCoordinates(currentrow));\n          return;\n        }\n\n        length = container.shortestEdge();\n        nextdatapoint = data[0];\n\n        if (improvesRatio(currentrow, nextdatapoint, length)) {\n          currentrow.push(nextdatapoint);\n          squarify(data.slice(1), currentrow, container, stack);\n        } else {\n          newcontainer = container.cutArea(sumArray(currentrow), stack);\n          stack.push(container.getCoordinates(currentrow));\n          squarify(data, [], newcontainer, stack);\n        }\n\n        return stack;\n      } // improveRatio - implements the worse calculation and comparision as given in Bruls\n      //                (note the error in the original paper; fixed here)\n\n\n      function improvesRatio(currentrow, nextnode, length) {\n        var newrow;\n\n        if (currentrow.length === 0) {\n          return true;\n        }\n\n        newrow = currentrow.slice();\n        newrow.push(nextnode);\n        var currentratio = calculateRatio(currentrow, length);\n        var newratio = calculateRatio(newrow, length); // the pseudocode in the Bruls paper has the direction of the comparison\n        // wrong, this is the correct one.\n\n        return currentratio >= newratio;\n      } // calculateRatio - calculates the maximum width to height ratio of the\n      //                  boxes in this row\n\n\n      function calculateRatio(row, length) {\n        var min = Math.min.apply(Math, row);\n        var max = Math.max.apply(Math, row);\n        var sum = sumArray(row);\n        return Math.max(Math.pow(length, 2) * max / Math.pow(sum, 2), Math.pow(sum, 2) / (Math.pow(length, 2) * min));\n      } // isArray - checks if arr is an array\n\n\n      function isArray(arr) {\n        return arr && arr.constructor === Array;\n      } // sumArray - sums a single dimensional array\n\n\n      function sumArray(arr) {\n        var sum = 0;\n        var i;\n\n        for (i = 0; i < arr.length; i++) {\n          sum += arr[i];\n        }\n\n        return sum;\n      } // sumMultidimensionalArray - sums the values in a nested array (aka [[0,1],[[2,3]]])\n\n\n      function sumMultidimensionalArray(arr) {\n        var i,\n            total = 0;\n\n        if (isArray(arr[0])) {\n          for (i = 0; i < arr.length; i++) {\n            total += sumMultidimensionalArray(arr[i]);\n          }\n        } else {\n          total = sumArray(arr);\n        }\n\n        return total;\n      }\n\n      return treemapMultidimensional;\n    }();\n  })();\n\n  /**\n   * ApexCharts TreemapChart Class.\n   * @module TreemapChart\n   **/\n\n  var TreemapChart = /*#__PURE__*/function () {\n    function TreemapChart(ctx, xyRatios) {\n      _classCallCheck(this, TreemapChart);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.strokeWidth = this.w.config.stroke.width;\n      this.helpers = new TreemapHelpers(ctx);\n      this.dynamicAnim = this.w.config.chart.animations.dynamicAnimation;\n      this.labels = [];\n    }\n\n    _createClass(TreemapChart, [{\n      key: \"draw\",\n      value: function draw(series) {\n        var _this = this;\n\n        var w = this.w;\n        var graphics = new Graphics(this.ctx);\n        var fill = new Fill(this.ctx);\n        var ret = graphics.group({\n          class: 'apexcharts-treemap'\n        });\n        if (w.globals.noData) return ret;\n        var ser = [];\n        series.forEach(function (s) {\n          var d = s.map(function (v) {\n            return Math.abs(v);\n          });\n          ser.push(d);\n        });\n        this.negRange = this.helpers.checkColorRange();\n        w.config.series.forEach(function (s, i) {\n          s.data.forEach(function (l) {\n            if (!Array.isArray(_this.labels[i])) _this.labels[i] = [];\n\n            _this.labels[i].push(l.x);\n          });\n        });\n        var nodes = window.TreemapSquared.generate(ser, w.globals.gridWidth, w.globals.gridHeight);\n        nodes.forEach(function (node, i) {\n          var elSeries = graphics.group({\n            class: \"apexcharts-series apexcharts-treemap-series\",\n            seriesName: Utils$1.escapeString(w.globals.seriesNames[i]),\n            rel: i + 1,\n            'data:realIndex': i\n          });\n\n          if (w.config.chart.dropShadow.enabled) {\n            var shadow = w.config.chart.dropShadow;\n            var filters = new Filters(_this.ctx);\n            filters.dropShadow(ret, shadow, i);\n          }\n\n          var elDataLabelWrap = graphics.group({\n            class: 'apexcharts-data-labels'\n          });\n          node.forEach(function (r, j) {\n            var x1 = r[0];\n            var y1 = r[1];\n            var x2 = r[2];\n            var y2 = r[3];\n            var elRect = graphics.drawRect(x1, y1, x2 - x1, y2 - y1, 0, '#fff', 1, _this.strokeWidth, w.config.plotOptions.treemap.useFillColorAsStroke ? color : w.globals.stroke.colors[i]);\n            elRect.attr({\n              cx: x1,\n              cy: y1,\n              index: i,\n              i: i,\n              j: j,\n              width: x2 - x1,\n              height: y2 - y1\n            });\n\n            var colorProps = _this.helpers.getShadeColor(w.config.chart.type, i, j, _this.negRange);\n\n            var color = colorProps.color;\n\n            if (typeof w.config.series[i].data[j] !== 'undefined' && w.config.series[i].data[j].fillColor) {\n              color = w.config.series[i].data[j].fillColor;\n            }\n\n            var pathFill = fill.fillPath({\n              color: color,\n              seriesNumber: i,\n              dataPointIndex: j\n            });\n            elRect.node.classList.add('apexcharts-treemap-rect');\n            elRect.attr({\n              fill: pathFill\n            });\n\n            _this.helpers.addListeners(elRect);\n\n            var fromRect = {\n              x: x1 + (x2 - x1) / 2,\n              y: y1 + (y2 - y1) / 2,\n              width: 0,\n              height: 0\n            };\n            var toRect = {\n              x: x1,\n              y: y1,\n              width: x2 - x1,\n              height: y2 - y1\n            };\n\n            if (w.config.chart.animations.enabled && !w.globals.dataChanged) {\n              var speed = 1;\n\n              if (!w.globals.resized) {\n                speed = w.config.chart.animations.speed;\n              }\n\n              _this.animateTreemap(elRect, fromRect, toRect, speed);\n            }\n\n            if (w.globals.dataChanged) {\n              var _speed = 1;\n\n              if (_this.dynamicAnim.enabled && w.globals.shouldAnimate) {\n                _speed = _this.dynamicAnim.speed;\n\n                if (w.globals.previousPaths[i] && w.globals.previousPaths[i][j] && w.globals.previousPaths[i][j].rect) {\n                  fromRect = w.globals.previousPaths[i][j].rect;\n                }\n\n                _this.animateTreemap(elRect, fromRect, toRect, _speed);\n              }\n            }\n\n            var fontSize = _this.getFontSize(r);\n\n            var formattedText = w.config.dataLabels.formatter(_this.labels[i][j], {\n              value: w.globals.series[i][j],\n              seriesIndex: i,\n              dataPointIndex: j,\n              w: w\n            });\n\n            var dataLabels = _this.helpers.calculateDataLabels({\n              text: formattedText,\n              x: (x1 + x2) / 2,\n              y: (y1 + y2) / 2 + _this.strokeWidth / 2 + fontSize / 3,\n              i: i,\n              j: j,\n              colorProps: colorProps,\n              fontSize: fontSize,\n              series: series\n            });\n\n            if (w.config.dataLabels.enabled && dataLabels) {\n              _this.rotateToFitLabel(dataLabels, fontSize, formattedText, x1, y1, x2, y2);\n            }\n\n            elSeries.add(elRect);\n\n            if (dataLabels !== null) {\n              elSeries.add(dataLabels);\n            }\n          });\n          elSeries.add(elDataLabelWrap);\n          ret.add(elSeries);\n        });\n        return ret;\n      } // This calculates a font-size based upon\n      // average label length and the size of the box the label is\n      // going into. The maximum font size is set in chart config.\n\n    }, {\n      key: \"getFontSize\",\n      value: function getFontSize(coordinates) {\n        var w = this.w; // total length of labels (i.e [[\"Italy\"],[\"Spain\", \"Greece\"]] -> 16)\n\n        function totalLabelLength(arr) {\n          var i,\n              total = 0;\n\n          if (Array.isArray(arr[0])) {\n            for (i = 0; i < arr.length; i++) {\n              total += totalLabelLength(arr[i]);\n            }\n          } else {\n            for (i = 0; i < arr.length; i++) {\n              total += arr[i].length;\n            }\n          }\n\n          return total;\n        } // count of labels (i.e [[\"Italy\"],[\"Spain\", \"Greece\"]] -> 3)\n\n\n        function countLabels(arr) {\n          var i,\n              total = 0;\n\n          if (Array.isArray(arr[0])) {\n            for (i = 0; i < arr.length; i++) {\n              total += countLabels(arr[i]);\n            }\n          } else {\n            for (i = 0; i < arr.length; i++) {\n              total += 1;\n            }\n          }\n\n          return total;\n        }\n\n        var averagelabelsize = totalLabelLength(this.labels) / countLabels(this.labels);\n\n        function fontSize(width, height) {\n          // the font size should be proportional to the size of the box (and the value)\n          // otherwise you can end up creating a visual distortion where two boxes of identical\n          // size have different sized labels, and thus make it look as if the two boxes\n          // represent different sizes\n          var area = width * height;\n          var arearoot = Math.pow(area, 0.5);\n          return Math.min(arearoot / averagelabelsize, parseInt(w.config.dataLabels.style.fontSize, 10));\n        }\n\n        return fontSize(coordinates[2] - coordinates[0], coordinates[3] - coordinates[1]);\n      }\n    }, {\n      key: \"rotateToFitLabel\",\n      value: function rotateToFitLabel(elText, fontSize, text, x1, y1, x2, y2) {\n        var graphics = new Graphics(this.ctx);\n        var textRect = graphics.getTextRects(text, fontSize); //if the label fits better sideways then rotate it\n\n        if (textRect.width + this.w.config.stroke.width + 5 > x2 - x1 && textRect.width <= y2 - y1) {\n          var labelRotatingCenter = graphics.rotateAroundCenter(elText.node);\n          elText.node.setAttribute('transform', \"rotate(-90 \".concat(labelRotatingCenter.x, \" \").concat(labelRotatingCenter.y, \")\"));\n        }\n      }\n    }, {\n      key: \"animateTreemap\",\n      value: function animateTreemap(el, fromRect, toRect, speed) {\n        var animations = new Animations(this.ctx);\n        animations.animateRect(el, {\n          x: fromRect.x,\n          y: fromRect.y,\n          width: fromRect.width,\n          height: fromRect.height\n        }, {\n          x: toRect.x,\n          y: toRect.y,\n          width: toRect.width,\n          height: toRect.height\n        }, speed, function () {\n          animations.animationCompleted(el);\n        });\n      }\n    }]);\n\n    return TreemapChart;\n  }();\n\n  var MINUTES_IN_DAY = 24 * 60;\n  var SECONDS_IN_DAY = MINUTES_IN_DAY * 60;\n  var MIN_ZOOM_DAYS = 10 / SECONDS_IN_DAY;\n  /**\n   * ApexCharts TimeScale Class for generating time ticks for x-axis.\n   *\n   * @module TimeScale\n   **/\n\n  var TimeScale = /*#__PURE__*/function () {\n    function TimeScale(ctx) {\n      _classCallCheck(this, TimeScale);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.timeScaleArray = [];\n      this.utc = this.w.config.xaxis.labels.datetimeUTC;\n    }\n\n    _createClass(TimeScale, [{\n      key: \"calculateTimeScaleTicks\",\n      value: function calculateTimeScaleTicks(minX, maxX) {\n        var _this = this;\n\n        var w = this.w; // null check when no series to show\n\n        if (w.globals.allSeriesCollapsed) {\n          w.globals.labels = [];\n          w.globals.timescaleLabels = [];\n          return [];\n        }\n\n        var dt = new DateTime(this.ctx);\n        var daysDiff = (maxX - minX) / (1000 * SECONDS_IN_DAY);\n        this.determineInterval(daysDiff);\n        w.globals.disableZoomIn = false;\n        w.globals.disableZoomOut = false;\n\n        if (daysDiff < MIN_ZOOM_DAYS) {\n          w.globals.disableZoomIn = true;\n        } else if (daysDiff > 50000) {\n          w.globals.disableZoomOut = true;\n        }\n\n        var timeIntervals = dt.getTimeUnitsfromTimestamp(minX, maxX, this.utc);\n        var daysWidthOnXAxis = w.globals.gridWidth / daysDiff;\n        var hoursWidthOnXAxis = daysWidthOnXAxis / 24;\n        var minutesWidthOnXAxis = hoursWidthOnXAxis / 60;\n        var secondsWidthOnXAxis = minutesWidthOnXAxis / 60;\n        var numberOfHours = Math.floor(daysDiff * 24);\n        var numberOfMinutes = Math.floor(daysDiff * MINUTES_IN_DAY);\n        var numberOfSeconds = Math.floor(daysDiff * SECONDS_IN_DAY);\n        var numberOfDays = Math.floor(daysDiff);\n        var numberOfMonths = Math.floor(daysDiff / 30);\n        var numberOfYears = Math.floor(daysDiff / 365);\n        var firstVal = {\n          minMillisecond: timeIntervals.minMillisecond,\n          minSecond: timeIntervals.minSecond,\n          minMinute: timeIntervals.minMinute,\n          minHour: timeIntervals.minHour,\n          minDate: timeIntervals.minDate,\n          minMonth: timeIntervals.minMonth,\n          minYear: timeIntervals.minYear\n        };\n        var currentMillisecond = firstVal.minMillisecond;\n        var currentSecond = firstVal.minSecond;\n        var currentMinute = firstVal.minMinute;\n        var currentHour = firstVal.minHour;\n        var currentMonthDate = firstVal.minDate;\n        var currentDate = firstVal.minDate;\n        var currentMonth = firstVal.minMonth;\n        var currentYear = firstVal.minYear;\n        var params = {\n          firstVal: firstVal,\n          currentMillisecond: currentMillisecond,\n          currentSecond: currentSecond,\n          currentMinute: currentMinute,\n          currentHour: currentHour,\n          currentMonthDate: currentMonthDate,\n          currentDate: currentDate,\n          currentMonth: currentMonth,\n          currentYear: currentYear,\n          daysWidthOnXAxis: daysWidthOnXAxis,\n          hoursWidthOnXAxis: hoursWidthOnXAxis,\n          minutesWidthOnXAxis: minutesWidthOnXAxis,\n          secondsWidthOnXAxis: secondsWidthOnXAxis,\n          numberOfSeconds: numberOfSeconds,\n          numberOfMinutes: numberOfMinutes,\n          numberOfHours: numberOfHours,\n          numberOfDays: numberOfDays,\n          numberOfMonths: numberOfMonths,\n          numberOfYears: numberOfYears\n        };\n\n        switch (this.tickInterval) {\n          case 'years':\n            {\n              this.generateYearScale(params);\n              break;\n            }\n\n          case 'months':\n          case 'half_year':\n            {\n              this.generateMonthScale(params);\n              break;\n            }\n\n          case 'months_days':\n          case 'months_fortnight':\n          case 'days':\n          case 'week_days':\n            {\n              this.generateDayScale(params);\n              break;\n            }\n\n          case 'hours':\n            {\n              this.generateHourScale(params);\n              break;\n            }\n\n          case 'minutes_fives':\n          case 'minutes':\n            this.generateMinuteScale(params);\n            break;\n\n          case 'seconds_tens':\n          case 'seconds_fives':\n          case 'seconds':\n            this.generateSecondScale(params);\n            break;\n        } // first, we will adjust the month values index\n        // as in the upper function, it is starting from 0\n        // we will start them from 1\n\n\n        var adjustedMonthInTimeScaleArray = this.timeScaleArray.map(function (ts) {\n          var defaultReturn = {\n            position: ts.position,\n            unit: ts.unit,\n            year: ts.year,\n            day: ts.day ? ts.day : 1,\n            hour: ts.hour ? ts.hour : 0,\n            month: ts.month + 1\n          };\n\n          if (ts.unit === 'month') {\n            return _objectSpread2(_objectSpread2({}, defaultReturn), {}, {\n              day: 1,\n              value: ts.value + 1\n            });\n          } else if (ts.unit === 'day' || ts.unit === 'hour') {\n            return _objectSpread2(_objectSpread2({}, defaultReturn), {}, {\n              value: ts.value\n            });\n          } else if (ts.unit === 'minute') {\n            return _objectSpread2(_objectSpread2({}, defaultReturn), {}, {\n              value: ts.value,\n              minute: ts.value\n            });\n          } else if (ts.unit === 'second') {\n            return _objectSpread2(_objectSpread2({}, defaultReturn), {}, {\n              value: ts.value,\n              minute: ts.minute,\n              second: ts.second\n            });\n          }\n\n          return ts;\n        });\n        var filteredTimeScale = adjustedMonthInTimeScaleArray.filter(function (ts) {\n          var modulo = 1;\n          var ticks = Math.ceil(w.globals.gridWidth / 120);\n          var value = ts.value;\n\n          if (w.config.xaxis.tickAmount !== undefined) {\n            ticks = w.config.xaxis.tickAmount;\n          }\n\n          if (adjustedMonthInTimeScaleArray.length > ticks) {\n            modulo = Math.floor(adjustedMonthInTimeScaleArray.length / ticks);\n          }\n\n          var shouldNotSkipUnit = false; // there is a big change in unit i.e days to months\n\n          var shouldNotPrint = false; // should skip these values\n\n          switch (_this.tickInterval) {\n            case 'years':\n              // make years label denser\n              if (ts.unit === 'year') {\n                shouldNotSkipUnit = true;\n              }\n\n              break;\n\n            case 'half_year':\n              modulo = 7;\n\n              if (ts.unit === 'year') {\n                shouldNotSkipUnit = true;\n              }\n\n              break;\n\n            case 'months':\n              modulo = 1;\n\n              if (ts.unit === 'year') {\n                shouldNotSkipUnit = true;\n              }\n\n              break;\n\n            case 'months_fortnight':\n              modulo = 15;\n\n              if (ts.unit === 'year' || ts.unit === 'month') {\n                shouldNotSkipUnit = true;\n              }\n\n              if (value === 30) {\n                shouldNotPrint = true;\n              }\n\n              break;\n\n            case 'months_days':\n              modulo = 10;\n\n              if (ts.unit === 'month') {\n                shouldNotSkipUnit = true;\n              }\n\n              if (value === 30) {\n                shouldNotPrint = true;\n              }\n\n              break;\n\n            case 'week_days':\n              modulo = 8;\n\n              if (ts.unit === 'month') {\n                shouldNotSkipUnit = true;\n              }\n\n              break;\n\n            case 'days':\n              modulo = 1;\n\n              if (ts.unit === 'month') {\n                shouldNotSkipUnit = true;\n              }\n\n              break;\n\n            case 'hours':\n              if (ts.unit === 'day') {\n                shouldNotSkipUnit = true;\n              }\n\n              break;\n\n            case 'minutes_fives':\n              if (value % 5 !== 0) {\n                shouldNotPrint = true;\n              }\n\n              break;\n\n            case 'seconds_tens':\n              if (value % 10 !== 0) {\n                shouldNotPrint = true;\n              }\n\n              break;\n\n            case 'seconds_fives':\n              if (value % 5 !== 0) {\n                shouldNotPrint = true;\n              }\n\n              break;\n          }\n\n          if (_this.tickInterval === 'hours' || _this.tickInterval === 'minutes_fives' || _this.tickInterval === 'seconds_tens' || _this.tickInterval === 'seconds_fives') {\n            if (!shouldNotPrint) {\n              return true;\n            }\n          } else {\n            if ((value % modulo === 0 || shouldNotSkipUnit) && !shouldNotPrint) {\n              return true;\n            }\n          }\n        });\n        return filteredTimeScale;\n      }\n    }, {\n      key: \"recalcDimensionsBasedOnFormat\",\n      value: function recalcDimensionsBasedOnFormat(filteredTimeScale, inverted) {\n        var w = this.w;\n        var reformattedTimescaleArray = this.formatDates(filteredTimeScale);\n        var removedOverlappingTS = this.removeOverlappingTS(reformattedTimescaleArray);\n        w.globals.timescaleLabels = removedOverlappingTS.slice(); // at this stage, we need to re-calculate coords of the grid as timeline labels may have altered the xaxis labels coords\n        // The reason we can't do this prior to this stage is because timeline labels depends on gridWidth, and as the ticks are calculated based on available gridWidth, there can be unknown number of ticks generated for different minX and maxX\n        // Dependency on Dimensions(), need to refactor correctly\n        // TODO - find an alternate way to avoid calling this Heavy method twice\n\n        var dimensions = new Dimensions(this.ctx);\n        dimensions.plotCoords();\n      }\n    }, {\n      key: \"determineInterval\",\n      value: function determineInterval(daysDiff) {\n        var yearsDiff = daysDiff / 365;\n        var hoursDiff = daysDiff * 24;\n        var minutesDiff = hoursDiff * 60;\n        var secondsDiff = minutesDiff * 60;\n\n        switch (true) {\n          case yearsDiff > 5:\n            this.tickInterval = 'years';\n            break;\n\n          case daysDiff > 800:\n            this.tickInterval = 'half_year';\n            break;\n\n          case daysDiff > 180:\n            this.tickInterval = 'months';\n            break;\n\n          case daysDiff > 90:\n            this.tickInterval = 'months_fortnight';\n            break;\n\n          case daysDiff > 60:\n            this.tickInterval = 'months_days';\n            break;\n\n          case daysDiff > 30:\n            this.tickInterval = 'week_days';\n            break;\n\n          case daysDiff > 2:\n            this.tickInterval = 'days';\n            break;\n\n          case hoursDiff > 2.4:\n            this.tickInterval = 'hours';\n            break;\n\n          case minutesDiff > 15:\n            this.tickInterval = 'minutes_fives';\n            break;\n\n          case minutesDiff > 5:\n            this.tickInterval = 'minutes';\n            break;\n\n          case minutesDiff > 1:\n            this.tickInterval = 'seconds_tens';\n            break;\n\n          case secondsDiff > 20:\n            this.tickInterval = 'seconds_fives';\n            break;\n\n          default:\n            this.tickInterval = 'seconds';\n            break;\n        }\n      }\n    }, {\n      key: \"generateYearScale\",\n      value: function generateYearScale(_ref) {\n        var firstVal = _ref.firstVal,\n            currentMonth = _ref.currentMonth,\n            currentYear = _ref.currentYear,\n            daysWidthOnXAxis = _ref.daysWidthOnXAxis,\n            numberOfYears = _ref.numberOfYears;\n        var firstTickValue = firstVal.minYear;\n        var firstTickPosition = 0;\n        var dt = new DateTime(this.ctx);\n        var unit = 'year';\n\n        if (firstVal.minDate > 1 || firstVal.minMonth > 0) {\n          var remainingDays = dt.determineRemainingDaysOfYear(firstVal.minYear, firstVal.minMonth, firstVal.minDate); // remainingDaysofFirstMonth is used to reacht the 2nd tick position\n\n          var remainingDaysOfFirstYear = dt.determineDaysOfYear(firstVal.minYear) - remainingDays + 1; // calculate the first tick position\n\n          firstTickPosition = remainingDaysOfFirstYear * daysWidthOnXAxis;\n          firstTickValue = firstVal.minYear + 1; // push the first tick in the array\n\n          this.timeScaleArray.push({\n            position: firstTickPosition,\n            value: firstTickValue,\n            unit: unit,\n            year: firstTickValue,\n            month: Utils$1.monthMod(currentMonth + 1)\n          });\n        } else if (firstVal.minDate === 1 && firstVal.minMonth === 0) {\n          // push the first tick in the array\n          this.timeScaleArray.push({\n            position: firstTickPosition,\n            value: firstTickValue,\n            unit: unit,\n            year: currentYear,\n            month: Utils$1.monthMod(currentMonth + 1)\n          });\n        }\n\n        var year = firstTickValue;\n        var pos = firstTickPosition; // keep drawing rest of the ticks\n\n        for (var i = 0; i < numberOfYears; i++) {\n          year++;\n          pos = dt.determineDaysOfYear(year - 1) * daysWidthOnXAxis + pos;\n          this.timeScaleArray.push({\n            position: pos,\n            value: year,\n            unit: unit,\n            year: year,\n            month: 1\n          });\n        }\n      }\n    }, {\n      key: \"generateMonthScale\",\n      value: function generateMonthScale(_ref2) {\n        var firstVal = _ref2.firstVal,\n            currentMonthDate = _ref2.currentMonthDate,\n            currentMonth = _ref2.currentMonth,\n            currentYear = _ref2.currentYear,\n            daysWidthOnXAxis = _ref2.daysWidthOnXAxis,\n            numberOfMonths = _ref2.numberOfMonths;\n        var firstTickValue = currentMonth;\n        var firstTickPosition = 0;\n        var dt = new DateTime(this.ctx);\n        var unit = 'month';\n        var yrCounter = 0;\n\n        if (firstVal.minDate > 1) {\n          // remainingDaysofFirstMonth is used to reacht the 2nd tick position\n          var remainingDaysOfFirstMonth = dt.determineDaysOfMonths(currentMonth + 1, firstVal.minYear) - currentMonthDate + 1; // calculate the first tick position\n\n          firstTickPosition = remainingDaysOfFirstMonth * daysWidthOnXAxis;\n          firstTickValue = Utils$1.monthMod(currentMonth + 1);\n          var year = currentYear + yrCounter;\n\n          var _month = Utils$1.monthMod(firstTickValue);\n\n          var value = firstTickValue; // it's Jan, so update the year\n\n          if (firstTickValue === 0) {\n            unit = 'year';\n            value = year;\n            _month = 1;\n            yrCounter += 1;\n            year = year + yrCounter;\n          } // push the first tick in the array\n\n\n          this.timeScaleArray.push({\n            position: firstTickPosition,\n            value: value,\n            unit: unit,\n            year: year,\n            month: _month\n          });\n        } else {\n          // push the first tick in the array\n          this.timeScaleArray.push({\n            position: firstTickPosition,\n            value: firstTickValue,\n            unit: unit,\n            year: currentYear,\n            month: Utils$1.monthMod(currentMonth)\n          });\n        }\n\n        var month = firstTickValue + 1;\n        var pos = firstTickPosition; // keep drawing rest of the ticks\n\n        for (var i = 0, j = 1; i < numberOfMonths; i++, j++) {\n          month = Utils$1.monthMod(month);\n\n          if (month === 0) {\n            unit = 'year';\n            yrCounter += 1;\n          } else {\n            unit = 'month';\n          }\n\n          var _year = this._getYear(currentYear, month, yrCounter);\n\n          pos = dt.determineDaysOfMonths(month, _year) * daysWidthOnXAxis + pos;\n          var monthVal = month === 0 ? _year : month;\n          this.timeScaleArray.push({\n            position: pos,\n            value: monthVal,\n            unit: unit,\n            year: _year,\n            month: month === 0 ? 1 : month\n          });\n          month++;\n        }\n      }\n    }, {\n      key: \"generateDayScale\",\n      value: function generateDayScale(_ref3) {\n        var firstVal = _ref3.firstVal,\n            currentMonth = _ref3.currentMonth,\n            currentYear = _ref3.currentYear,\n            hoursWidthOnXAxis = _ref3.hoursWidthOnXAxis,\n            numberOfDays = _ref3.numberOfDays;\n        var dt = new DateTime(this.ctx);\n        var unit = 'day';\n        var firstTickValue = firstVal.minDate + 1;\n        var date = firstTickValue;\n\n        var changeMonth = function changeMonth(dateVal, month, year) {\n          var monthdays = dt.determineDaysOfMonths(month + 1, year);\n\n          if (dateVal > monthdays) {\n            month = month + 1;\n            date = 1;\n            unit = 'month';\n            val = month;\n            return month;\n          }\n\n          return month;\n        };\n\n        var remainingHours = 24 - firstVal.minHour;\n        var yrCounter = 0; // calculate the first tick position\n\n        var firstTickPosition = remainingHours * hoursWidthOnXAxis;\n        var val = firstTickValue;\n        var month = changeMonth(date, currentMonth, currentYear);\n\n        if (firstVal.minHour === 0 && firstVal.minDate === 1) {\n          // the first value is the first day of month\n          firstTickPosition = 0;\n          val = Utils$1.monthMod(firstVal.minMonth);\n          unit = 'month';\n          date = firstVal.minDate;\n          numberOfDays++;\n        } else if (firstVal.minDate !== 1 && firstVal.minHour === 0 && firstVal.minMinute === 0) {\n          // fixes apexcharts/apexcharts.js/issues/1730\n          firstTickPosition = 0;\n          firstTickValue = firstVal.minDate;\n          date = firstTickValue;\n          val = firstTickValue; // in case it's the last date of month, we need to check it\n\n          month = changeMonth(date, currentMonth, currentYear);\n        } // push the first tick in the array\n\n\n        this.timeScaleArray.push({\n          position: firstTickPosition,\n          value: val,\n          unit: unit,\n          year: this._getYear(currentYear, month, yrCounter),\n          month: Utils$1.monthMod(month),\n          day: date\n        });\n        var pos = firstTickPosition; // keep drawing rest of the ticks\n\n        for (var i = 0; i < numberOfDays; i++) {\n          date += 1;\n          unit = 'day';\n          month = changeMonth(date, month, this._getYear(currentYear, month, yrCounter));\n\n          var year = this._getYear(currentYear, month, yrCounter);\n\n          pos = 24 * hoursWidthOnXAxis + pos;\n          var value = date === 1 ? Utils$1.monthMod(month) : date;\n          this.timeScaleArray.push({\n            position: pos,\n            value: value,\n            unit: unit,\n            year: year,\n            month: Utils$1.monthMod(month),\n            day: value\n          });\n        }\n      }\n    }, {\n      key: \"generateHourScale\",\n      value: function generateHourScale(_ref4) {\n        var firstVal = _ref4.firstVal,\n            currentDate = _ref4.currentDate,\n            currentMonth = _ref4.currentMonth,\n            currentYear = _ref4.currentYear,\n            minutesWidthOnXAxis = _ref4.minutesWidthOnXAxis,\n            numberOfHours = _ref4.numberOfHours;\n        var dt = new DateTime(this.ctx);\n        var yrCounter = 0;\n        var unit = 'hour';\n\n        var changeDate = function changeDate(dateVal, month) {\n          var monthdays = dt.determineDaysOfMonths(month + 1, currentYear);\n\n          if (dateVal > monthdays) {\n            date = 1;\n            month = month + 1;\n          }\n\n          return {\n            month: month,\n            date: date\n          };\n        };\n\n        var changeMonth = function changeMonth(dateVal, month) {\n          var monthdays = dt.determineDaysOfMonths(month + 1, currentYear);\n\n          if (dateVal > monthdays) {\n            month = month + 1;\n            return month;\n          }\n\n          return month;\n        }; // factor in minSeconds as well\n\n\n        var remainingMins = 60 - (firstVal.minMinute + firstVal.minSecond / 60.0);\n        var firstTickPosition = remainingMins * minutesWidthOnXAxis;\n        var firstTickValue = firstVal.minHour + 1;\n        var hour = firstTickValue + 1;\n\n        if (remainingMins === 60) {\n          firstTickPosition = 0;\n          firstTickValue = firstVal.minHour;\n          hour = firstTickValue + 1;\n        }\n\n        var date = currentDate;\n        var month = changeMonth(date, currentMonth); // push the first tick in the array\n\n        this.timeScaleArray.push({\n          position: firstTickPosition,\n          value: firstTickValue,\n          unit: unit,\n          day: date,\n          hour: hour,\n          year: currentYear,\n          month: Utils$1.monthMod(month)\n        });\n        var pos = firstTickPosition; // keep drawing rest of the ticks\n\n        for (var i = 0; i < numberOfHours; i++) {\n          unit = 'hour';\n\n          if (hour >= 24) {\n            hour = 0;\n            date += 1;\n            unit = 'day';\n            var checkNextMonth = changeDate(date, month);\n            month = checkNextMonth.month;\n            month = changeMonth(date, month);\n          }\n\n          var year = this._getYear(currentYear, month, yrCounter);\n\n          pos = 60 * minutesWidthOnXAxis + pos;\n          var val = hour === 0 ? date : hour;\n          this.timeScaleArray.push({\n            position: pos,\n            value: val,\n            unit: unit,\n            hour: hour,\n            day: date,\n            year: year,\n            month: Utils$1.monthMod(month)\n          });\n          hour++;\n        }\n      }\n    }, {\n      key: \"generateMinuteScale\",\n      value: function generateMinuteScale(_ref5) {\n        var currentMillisecond = _ref5.currentMillisecond,\n            currentSecond = _ref5.currentSecond,\n            currentMinute = _ref5.currentMinute,\n            currentHour = _ref5.currentHour,\n            currentDate = _ref5.currentDate,\n            currentMonth = _ref5.currentMonth,\n            currentYear = _ref5.currentYear,\n            minutesWidthOnXAxis = _ref5.minutesWidthOnXAxis,\n            secondsWidthOnXAxis = _ref5.secondsWidthOnXAxis,\n            numberOfMinutes = _ref5.numberOfMinutes;\n        var yrCounter = 0;\n        var unit = 'minute';\n        var remainingSecs = 60 - currentSecond;\n        var firstTickPosition = (remainingSecs - currentMillisecond / 1000) * secondsWidthOnXAxis;\n        var minute = currentMinute + 1;\n        var date = currentDate;\n        var month = currentMonth;\n        var year = currentYear;\n        var hour = currentHour;\n        var pos = firstTickPosition;\n\n        for (var i = 0; i < numberOfMinutes; i++) {\n          if (minute >= 60) {\n            minute = 0;\n            hour += 1;\n\n            if (hour === 24) {\n              hour = 0;\n            }\n          }\n\n          this.timeScaleArray.push({\n            position: pos,\n            value: minute,\n            unit: unit,\n            hour: hour,\n            minute: minute,\n            day: date,\n            year: this._getYear(year, month, yrCounter),\n            month: Utils$1.monthMod(month)\n          });\n          pos += minutesWidthOnXAxis;\n          minute++;\n        }\n      }\n    }, {\n      key: \"generateSecondScale\",\n      value: function generateSecondScale(_ref6) {\n        var currentMillisecond = _ref6.currentMillisecond,\n            currentSecond = _ref6.currentSecond,\n            currentMinute = _ref6.currentMinute,\n            currentHour = _ref6.currentHour,\n            currentDate = _ref6.currentDate,\n            currentMonth = _ref6.currentMonth,\n            currentYear = _ref6.currentYear,\n            secondsWidthOnXAxis = _ref6.secondsWidthOnXAxis,\n            numberOfSeconds = _ref6.numberOfSeconds;\n        var yrCounter = 0;\n        var unit = 'second';\n        var remainingMillisecs = 1000 - currentMillisecond;\n        var firstTickPosition = remainingMillisecs / 1000 * secondsWidthOnXAxis;\n        var second = currentSecond + 1;\n        var minute = currentMinute;\n        var date = currentDate;\n        var month = currentMonth;\n        var year = currentYear;\n        var hour = currentHour;\n        var pos = firstTickPosition;\n\n        for (var i = 0; i < numberOfSeconds; i++) {\n          if (second >= 60) {\n            minute++;\n            second = 0;\n\n            if (minute >= 60) {\n              hour++;\n              minute = 0;\n\n              if (hour === 24) {\n                hour = 0;\n              }\n            }\n          }\n\n          this.timeScaleArray.push({\n            position: pos,\n            value: second,\n            unit: unit,\n            hour: hour,\n            minute: minute,\n            second: second,\n            day: date,\n            year: this._getYear(year, month, yrCounter),\n            month: Utils$1.monthMod(month)\n          });\n          pos += secondsWidthOnXAxis;\n          second++;\n        }\n      }\n    }, {\n      key: \"createRawDateString\",\n      value: function createRawDateString(ts, value) {\n        var raw = ts.year;\n\n        if (ts.month === 0) {\n          // invalid month, correct it\n          ts.month = 1;\n        }\n\n        raw += '-' + ('0' + ts.month.toString()).slice(-2); // unit is day\n\n        if (ts.unit === 'day') {\n          raw += ts.unit === 'day' ? '-' + ('0' + value).slice(-2) : '-01';\n        } else {\n          raw += '-' + ('0' + (ts.day ? ts.day : '1')).slice(-2);\n        } // unit is hour\n\n\n        if (ts.unit === 'hour') {\n          raw += ts.unit === 'hour' ? 'T' + ('0' + value).slice(-2) : 'T00';\n        } else {\n          raw += 'T' + ('0' + (ts.hour ? ts.hour : '0')).slice(-2);\n        }\n\n        if (ts.unit === 'minute') {\n          raw += ':' + ('0' + value).slice(-2);\n        } else {\n          raw += ':' + (ts.minute ? ('0' + ts.minute).slice(-2) : '00');\n        }\n\n        if (ts.unit === 'second') {\n          raw += ':' + ('0' + value).slice(-2);\n        } else {\n          raw += ':00';\n        }\n\n        if (this.utc) {\n          raw += '.000Z';\n        }\n\n        return raw;\n      }\n    }, {\n      key: \"formatDates\",\n      value: function formatDates(filteredTimeScale) {\n        var _this2 = this;\n\n        var w = this.w;\n        var reformattedTimescaleArray = filteredTimeScale.map(function (ts) {\n          var value = ts.value.toString();\n          var dt = new DateTime(_this2.ctx);\n\n          var raw = _this2.createRawDateString(ts, value);\n\n          var dateToFormat = dt.getDate(dt.parseDate(raw));\n\n          if (!_this2.utc) {\n            // Fixes #1726, #1544, #1485, #1255\n            dateToFormat = dt.getDate(dt.parseDateWithTimezone(raw));\n          }\n\n          if (w.config.xaxis.labels.format === undefined) {\n            var customFormat = 'dd MMM';\n            var dtFormatter = w.config.xaxis.labels.datetimeFormatter;\n            if (ts.unit === 'year') customFormat = dtFormatter.year;\n            if (ts.unit === 'month') customFormat = dtFormatter.month;\n            if (ts.unit === 'day') customFormat = dtFormatter.day;\n            if (ts.unit === 'hour') customFormat = dtFormatter.hour;\n            if (ts.unit === 'minute') customFormat = dtFormatter.minute;\n            if (ts.unit === 'second') customFormat = dtFormatter.second;\n            value = dt.formatDate(dateToFormat, customFormat);\n          } else {\n            value = dt.formatDate(dateToFormat, w.config.xaxis.labels.format);\n          }\n\n          return {\n            dateString: raw,\n            position: ts.position,\n            value: value,\n            unit: ts.unit,\n            year: ts.year,\n            month: ts.month\n          };\n        });\n        return reformattedTimescaleArray;\n      }\n    }, {\n      key: \"removeOverlappingTS\",\n      value: function removeOverlappingTS(arr) {\n        var _this3 = this;\n\n        var graphics = new Graphics(this.ctx);\n        var equalLabelLengthFlag = false; // These labels got same length?\n\n        var constantLabelWidth; // If true, what is the constant length to use\n\n        if (arr.length > 0 && // check arr length\n        arr[0].value && // check arr[0] contains value\n        arr.every(function (lb) {\n          return lb.value.length === arr[0].value.length;\n        }) // check every arr label value is the same as the first one\n        ) {\n          equalLabelLengthFlag = true; // These labels got same length\n\n          constantLabelWidth = graphics.getTextRects(arr[0].value).width; // The constant label width to use\n        }\n\n        var lastDrawnIndex = 0;\n        var filteredArray = arr.map(function (item, index) {\n          if (index > 0 && _this3.w.config.xaxis.labels.hideOverlappingLabels) {\n            var prevLabelWidth = !equalLabelLengthFlag // if vary in label length\n            ? graphics.getTextRects(arr[lastDrawnIndex].value).width // get individual length\n            : constantLabelWidth; // else: use constant length\n\n            var prevPos = arr[lastDrawnIndex].position;\n            var pos = item.position;\n\n            if (pos > prevPos + prevLabelWidth + 10) {\n              lastDrawnIndex = index;\n              return item;\n            } else {\n              return null;\n            }\n          } else {\n            return item;\n          }\n        });\n        filteredArray = filteredArray.filter(function (f) {\n          return f !== null;\n        });\n        return filteredArray;\n      }\n    }, {\n      key: \"_getYear\",\n      value: function _getYear(currentYear, month, yrCounter) {\n        return currentYear + Math.floor(month / 12) + yrCounter;\n      }\n    }]);\n\n    return TimeScale;\n  }();\n\n  /**\n   * ApexCharts Core Class responsible for major calculations and creating elements.\n   *\n   * @module Core\n   **/\n\n  var Core = /*#__PURE__*/function () {\n    function Core(el, ctx) {\n      _classCallCheck(this, Core);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n      this.el = el;\n    } // get data and store into appropriate vars\n\n\n    _createClass(Core, [{\n      key: \"setupElements\",\n      value: function setupElements() {\n        var gl = this.w.globals;\n        var cnf = this.w.config; // const graphics = new Graphics(this.ctx)\n\n        var ct = cnf.chart.type;\n        var axisChartsArrTypes = ['line', 'area', 'bar', 'rangeBar', 'rangeArea', 'candlestick', 'boxPlot', 'scatter', 'bubble', 'radar', 'heatmap', 'treemap'];\n        var xyChartsArrTypes = ['line', 'area', 'bar', 'rangeBar', 'rangeArea', 'candlestick', 'boxPlot', 'scatter', 'bubble'];\n        gl.axisCharts = axisChartsArrTypes.indexOf(ct) > -1;\n        gl.xyCharts = xyChartsArrTypes.indexOf(ct) > -1;\n        gl.isBarHorizontal = (cnf.chart.type === 'bar' || cnf.chart.type === 'rangeBar' || cnf.chart.type === 'boxPlot') && cnf.plotOptions.bar.horizontal;\n        gl.chartClass = '.apexcharts' + gl.chartID;\n        gl.dom.baseEl = this.el;\n        gl.dom.elWrap = document.createElement('div');\n        Graphics.setAttrs(gl.dom.elWrap, {\n          id: gl.chartClass.substring(1),\n          class: 'apexcharts-canvas ' + gl.chartClass.substring(1)\n        });\n        this.el.appendChild(gl.dom.elWrap);\n        gl.dom.Paper = new window.SVG.Doc(gl.dom.elWrap);\n        gl.dom.Paper.attr({\n          class: 'apexcharts-svg',\n          'xmlns:data': 'ApexChartsNS',\n          transform: \"translate(\".concat(cnf.chart.offsetX, \", \").concat(cnf.chart.offsetY, \")\")\n        });\n        gl.dom.Paper.node.style.background = cnf.chart.background;\n        this.setSVGDimensions();\n        gl.dom.elGraphical = gl.dom.Paper.group().attr({\n          class: 'apexcharts-inner apexcharts-graphical'\n        });\n        gl.dom.elAnnotations = gl.dom.Paper.group().attr({\n          class: 'apexcharts-annotations'\n        });\n        gl.dom.elDefs = gl.dom.Paper.defs();\n        gl.dom.elLegendWrap = document.createElement('div');\n        gl.dom.elLegendWrap.classList.add('apexcharts-legend');\n        gl.dom.elWrap.appendChild(gl.dom.elLegendWrap);\n        gl.dom.Paper.add(gl.dom.elGraphical);\n        gl.dom.elGraphical.add(gl.dom.elDefs);\n      }\n    }, {\n      key: \"plotChartType\",\n      value: function plotChartType(ser, xyRatios) {\n        var w = this.w;\n        var cnf = w.config;\n        var gl = w.globals;\n        var lineSeries = {\n          series: [],\n          i: []\n        };\n        var areaSeries = {\n          series: [],\n          i: []\n        };\n        var scatterSeries = {\n          series: [],\n          i: []\n        };\n        var bubbleSeries = {\n          series: [],\n          i: []\n        };\n        var columnSeries = {\n          series: [],\n          i: []\n        };\n        var candlestickSeries = {\n          series: [],\n          i: []\n        };\n        var boxplotSeries = {\n          series: [],\n          i: []\n        };\n        var rangeBarSeries = {\n          series: [],\n          i: []\n        };\n        var rangeAreaSeries = {\n          series: [],\n          seriesRangeEnd: [],\n          i: []\n        };\n        gl.series.map(function (serie, st) {\n          var comboCount = 0; // if user has specified a particular type for particular series\n\n          if (typeof ser[st].type !== 'undefined') {\n            if (ser[st].type === 'column' || ser[st].type === 'bar') {\n              if (gl.series.length > 1 && cnf.plotOptions.bar.horizontal) {\n                // horizontal bars not supported in mixed charts, hence show a warning\n                console.warn('Horizontal bars are not supported in a mixed/combo chart. Please turn off `plotOptions.bar.horizontal`');\n              }\n\n              columnSeries.series.push(serie);\n              columnSeries.i.push(st);\n              comboCount++;\n              w.globals.columnSeries = columnSeries.series;\n            } else if (ser[st].type === 'area') {\n              areaSeries.series.push(serie);\n              areaSeries.i.push(st);\n              comboCount++;\n            } else if (ser[st].type === 'line') {\n              lineSeries.series.push(serie);\n              lineSeries.i.push(st);\n              comboCount++;\n            } else if (ser[st].type === 'scatter') {\n              scatterSeries.series.push(serie);\n              scatterSeries.i.push(st);\n            } else if (ser[st].type === 'bubble') {\n              bubbleSeries.series.push(serie);\n              bubbleSeries.i.push(st);\n              comboCount++;\n            } else if (ser[st].type === 'candlestick') {\n              candlestickSeries.series.push(serie);\n              candlestickSeries.i.push(st);\n              comboCount++;\n            } else if (ser[st].type === 'boxPlot') {\n              boxplotSeries.series.push(serie);\n              boxplotSeries.i.push(st);\n              comboCount++;\n            } else if (ser[st].type === 'rangeBar') {\n              rangeBarSeries.series.push(serie);\n              rangeBarSeries.i.push(st);\n              comboCount++;\n            } else if (ser[st].type === 'rangeArea') {\n              rangeAreaSeries.series.push(gl.seriesRangeStart[st]);\n              rangeAreaSeries.seriesRangeEnd.push(gl.seriesRangeEnd[st]);\n              rangeAreaSeries.i.push(st);\n              comboCount++;\n            } else {\n              // user has specified type, but it is not valid (other than line/area/column)\n              console.warn('You have specified an unrecognized chart type. Available types for this property are line/area/column/bar/scatter/bubble');\n            }\n\n            if (comboCount > 1) {\n              gl.comboCharts = true;\n            }\n          } else {\n            lineSeries.series.push(serie);\n            lineSeries.i.push(st);\n          }\n        });\n        var line = new Line(this.ctx, xyRatios);\n        var boxCandlestick = new BoxCandleStick(this.ctx, xyRatios);\n        this.ctx.pie = new Pie(this.ctx);\n        var radialBar = new Radial(this.ctx);\n        this.ctx.rangeBar = new RangeBar(this.ctx, xyRatios);\n        var radar = new Radar(this.ctx);\n        var elGraph = [];\n\n        if (gl.comboCharts) {\n          if (areaSeries.series.length > 0) {\n            elGraph.push(line.draw(areaSeries.series, 'area', areaSeries.i));\n          }\n\n          if (columnSeries.series.length > 0) {\n            if (w.config.chart.stacked) {\n              var barStacked = new BarStacked(this.ctx, xyRatios);\n              elGraph.push(barStacked.draw(columnSeries.series, columnSeries.i));\n            } else {\n              this.ctx.bar = new Bar(this.ctx, xyRatios);\n              elGraph.push(this.ctx.bar.draw(columnSeries.series, columnSeries.i));\n            }\n          }\n\n          if (rangeAreaSeries.series.length > 0) {\n            elGraph.push(line.draw(rangeAreaSeries.series, 'rangeArea', rangeAreaSeries.i, rangeAreaSeries.seriesRangeEnd));\n          }\n\n          if (lineSeries.series.length > 0) {\n            elGraph.push(line.draw(lineSeries.series, 'line', lineSeries.i));\n          }\n\n          if (candlestickSeries.series.length > 0) {\n            elGraph.push(boxCandlestick.draw(candlestickSeries.series, candlestickSeries.i));\n          }\n\n          if (boxplotSeries.series.length > 0) {\n            elGraph.push(boxCandlestick.draw(boxplotSeries.series, boxplotSeries.i));\n          }\n\n          if (rangeBarSeries.series.length > 0) {\n            elGraph.push(this.ctx.rangeBar.draw(rangeBarSeries.series, rangeBarSeries.i));\n          }\n\n          if (scatterSeries.series.length > 0) {\n            var scatterLine = new Line(this.ctx, xyRatios, true);\n            elGraph.push(scatterLine.draw(scatterSeries.series, 'scatter', scatterSeries.i));\n          }\n\n          if (bubbleSeries.series.length > 0) {\n            var bubbleLine = new Line(this.ctx, xyRatios, true);\n            elGraph.push(bubbleLine.draw(bubbleSeries.series, 'bubble', bubbleSeries.i));\n          }\n        } else {\n          switch (cnf.chart.type) {\n            case 'line':\n              elGraph = line.draw(gl.series, 'line');\n              break;\n\n            case 'area':\n              elGraph = line.draw(gl.series, 'area');\n              break;\n\n            case 'bar':\n              if (cnf.chart.stacked) {\n                var _barStacked = new BarStacked(this.ctx, xyRatios);\n\n                elGraph = _barStacked.draw(gl.series);\n              } else {\n                this.ctx.bar = new Bar(this.ctx, xyRatios);\n                elGraph = this.ctx.bar.draw(gl.series);\n              }\n\n              break;\n\n            case 'candlestick':\n              var candleStick = new BoxCandleStick(this.ctx, xyRatios);\n              elGraph = candleStick.draw(gl.series);\n              break;\n\n            case 'boxPlot':\n              var boxPlot = new BoxCandleStick(this.ctx, xyRatios);\n              elGraph = boxPlot.draw(gl.series);\n              break;\n\n            case 'rangeBar':\n              elGraph = this.ctx.rangeBar.draw(gl.series);\n              break;\n\n            case 'rangeArea':\n              elGraph = line.draw(gl.seriesRangeStart, 'rangeArea', undefined, gl.seriesRangeEnd);\n              break;\n\n            case 'heatmap':\n              var heatmap = new HeatMap(this.ctx, xyRatios);\n              elGraph = heatmap.draw(gl.series);\n              break;\n\n            case 'treemap':\n              var treemap = new TreemapChart(this.ctx, xyRatios);\n              elGraph = treemap.draw(gl.series);\n              break;\n\n            case 'pie':\n            case 'donut':\n            case 'polarArea':\n              elGraph = this.ctx.pie.draw(gl.series);\n              break;\n\n            case 'radialBar':\n              elGraph = radialBar.draw(gl.series);\n              break;\n\n            case 'radar':\n              elGraph = radar.draw(gl.series);\n              break;\n\n            default:\n              elGraph = line.draw(gl.series);\n          }\n        }\n\n        return elGraph;\n      }\n    }, {\n      key: \"setSVGDimensions\",\n      value: function setSVGDimensions() {\n        var gl = this.w.globals;\n        var cnf = this.w.config;\n        gl.svgWidth = cnf.chart.width;\n        gl.svgHeight = cnf.chart.height;\n        var elDim = Utils$1.getDimensions(this.el);\n        var widthUnit = cnf.chart.width.toString().split(/[0-9]+/g).pop();\n\n        if (widthUnit === '%') {\n          if (Utils$1.isNumber(elDim[0])) {\n            if (elDim[0].width === 0) {\n              elDim = Utils$1.getDimensions(this.el.parentNode);\n            }\n\n            gl.svgWidth = elDim[0] * parseInt(cnf.chart.width, 10) / 100;\n          }\n        } else if (widthUnit === 'px' || widthUnit === '') {\n          gl.svgWidth = parseInt(cnf.chart.width, 10);\n        }\n\n        var heightUnit = cnf.chart.height.toString().split(/[0-9]+/g).pop();\n\n        if (gl.svgHeight !== 'auto' && gl.svgHeight !== '') {\n          if (heightUnit === '%') {\n            var elParentDim = Utils$1.getDimensions(this.el.parentNode);\n            gl.svgHeight = elParentDim[1] * parseInt(cnf.chart.height, 10) / 100;\n          } else {\n            gl.svgHeight = parseInt(cnf.chart.height, 10);\n          }\n        } else {\n          if (gl.axisCharts) {\n            gl.svgHeight = gl.svgWidth / 1.61;\n          } else {\n            gl.svgHeight = gl.svgWidth / 1.2;\n          }\n        }\n\n        if (gl.svgWidth < 0) gl.svgWidth = 0;\n        if (gl.svgHeight < 0) gl.svgHeight = 0;\n        Graphics.setAttrs(gl.dom.Paper.node, {\n          width: gl.svgWidth,\n          height: gl.svgHeight\n        });\n\n        if (heightUnit !== '%') {\n          // fixes https://github.com/apexcharts/apexcharts.js/issues/2059\n          var offsetY = cnf.chart.sparkline.enabled ? 0 : gl.axisCharts ? cnf.chart.parentHeightOffset : 0;\n          gl.dom.Paper.node.parentNode.parentNode.style.minHeight = gl.svgHeight + offsetY + 'px';\n        }\n\n        gl.dom.elWrap.style.width = gl.svgWidth + 'px';\n        gl.dom.elWrap.style.height = gl.svgHeight + 'px';\n      }\n    }, {\n      key: \"shiftGraphPosition\",\n      value: function shiftGraphPosition() {\n        var gl = this.w.globals;\n        var tY = gl.translateY;\n        var tX = gl.translateX;\n        var scalingAttrs = {\n          transform: 'translate(' + tX + ', ' + tY + ')'\n        };\n        Graphics.setAttrs(gl.dom.elGraphical.node, scalingAttrs);\n      } // To prevent extra spacings in the bottom of the chart, we need to recalculate the height for pie/donut/radialbar charts\n\n    }, {\n      key: \"resizeNonAxisCharts\",\n      value: function resizeNonAxisCharts() {\n        var w = this.w;\n        var gl = w.globals;\n        var legendHeight = 0;\n        var offY = w.config.chart.sparkline.enabled ? 1 : 15;\n        offY = offY + w.config.grid.padding.bottom;\n\n        if ((w.config.legend.position === 'top' || w.config.legend.position === 'bottom') && w.config.legend.show && !w.config.legend.floating) {\n          legendHeight = new Legend(this.ctx).legendHelpers.getLegendBBox().clwh + 10;\n        }\n\n        var el = w.globals.dom.baseEl.querySelector('.apexcharts-radialbar, .apexcharts-pie');\n        var chartInnerDimensions = w.globals.radialSize * 2.05;\n\n        if (el && !w.config.chart.sparkline.enabled && w.config.plotOptions.radialBar.startAngle !== 0) {\n          var elRadialRect = Utils$1.getBoundingClientRect(el);\n          chartInnerDimensions = elRadialRect.bottom;\n          var maxHeight = elRadialRect.bottom - elRadialRect.top;\n          chartInnerDimensions = Math.max(w.globals.radialSize * 2.05, maxHeight);\n        }\n\n        var newHeight = chartInnerDimensions + gl.translateY + legendHeight + offY;\n\n        if (gl.dom.elLegendForeign) {\n          gl.dom.elLegendForeign.setAttribute('height', newHeight);\n        } // fix apexcharts/apexcharts.js/issues/3105 (when % is provided in height, it keeps increasing)\n\n\n        if (w.config.chart.height && String(w.config.chart.height).indexOf('%') > 0) return;\n        gl.dom.elWrap.style.height = newHeight + 'px';\n        Graphics.setAttrs(gl.dom.Paper.node, {\n          height: newHeight\n        });\n        gl.dom.Paper.node.parentNode.parentNode.style.minHeight = newHeight + 'px';\n      }\n      /*\n       ** All the calculations for setting range in charts will be done here\n       */\n\n    }, {\n      key: \"coreCalculations\",\n      value: function coreCalculations() {\n        var range = new Range(this.ctx);\n        range.init();\n      }\n    }, {\n      key: \"resetGlobals\",\n      value: function resetGlobals() {\n        var _this = this;\n\n        var resetxyValues = function resetxyValues() {\n          return _this.w.config.series.map(function (s) {\n            return [];\n          });\n        };\n\n        var globalObj = new Globals();\n        var gl = this.w.globals;\n        globalObj.initGlobalVars(gl);\n        gl.seriesXvalues = resetxyValues();\n        gl.seriesYvalues = resetxyValues();\n      }\n    }, {\n      key: \"isMultipleY\",\n      value: function isMultipleY() {\n        // user has supplied an array in yaxis property. So, turn on multipleYAxis flag\n        if (this.w.config.yaxis.constructor === Array && this.w.config.yaxis.length > 1) {\n          this.w.globals.isMultipleYAxis = true;\n          return true;\n        }\n      }\n    }, {\n      key: \"xySettings\",\n      value: function xySettings() {\n        var xyRatios = null;\n        var w = this.w;\n\n        if (w.globals.axisCharts) {\n          if (w.config.xaxis.crosshairs.position === 'back') {\n            var crosshairs = new Crosshairs(this.ctx);\n            crosshairs.drawXCrosshairs();\n          }\n\n          if (w.config.yaxis[0].crosshairs.position === 'back') {\n            var _crosshairs = new Crosshairs(this.ctx);\n\n            _crosshairs.drawYCrosshairs();\n          }\n\n          if (w.config.xaxis.type === 'datetime' && w.config.xaxis.labels.formatter === undefined) {\n            this.ctx.timeScale = new TimeScale(this.ctx);\n            var formattedTimeScale = [];\n\n            if (isFinite(w.globals.minX) && isFinite(w.globals.maxX) && !w.globals.isBarHorizontal) {\n              formattedTimeScale = this.ctx.timeScale.calculateTimeScaleTicks(w.globals.minX, w.globals.maxX);\n            } else if (w.globals.isBarHorizontal) {\n              formattedTimeScale = this.ctx.timeScale.calculateTimeScaleTicks(w.globals.minY, w.globals.maxY);\n            }\n\n            this.ctx.timeScale.recalcDimensionsBasedOnFormat(formattedTimeScale);\n          }\n\n          var coreUtils = new CoreUtils(this.ctx);\n          xyRatios = coreUtils.getCalculatedRatios();\n        }\n\n        return xyRatios;\n      }\n    }, {\n      key: \"updateSourceChart\",\n      value: function updateSourceChart(targetChart) {\n        this.ctx.w.globals.selection = undefined;\n\n        this.ctx.updateHelpers._updateOptions({\n          chart: {\n            selection: {\n              xaxis: {\n                min: targetChart.w.globals.minX,\n                max: targetChart.w.globals.maxX\n              }\n            }\n          }\n        }, false, false);\n      }\n    }, {\n      key: \"setupBrushHandler\",\n      value: function setupBrushHandler() {\n        var _this2 = this;\n\n        var w = this.w; // only for brush charts\n\n        if (!w.config.chart.brush.enabled) {\n          return;\n        } // if user has not defined a custom function for selection - we handle the brush chart\n        // otherwise we leave it to the user to define the functionality for selection\n\n\n        if (typeof w.config.chart.events.selection !== 'function') {\n          var targets = w.config.chart.brush.targets || [w.config.chart.brush.target]; // retro compatibility with single target option\n\n          targets.forEach(function (target) {\n            var targetChart = ApexCharts.getChartByID(target);\n            targetChart.w.globals.brushSource = _this2.ctx;\n\n            if (typeof targetChart.w.config.chart.events.zoomed !== 'function') {\n              targetChart.w.config.chart.events.zoomed = function () {\n                _this2.updateSourceChart(targetChart);\n              };\n            }\n\n            if (typeof targetChart.w.config.chart.events.scrolled !== 'function') {\n              targetChart.w.config.chart.events.scrolled = function () {\n                _this2.updateSourceChart(targetChart);\n              };\n            }\n          });\n\n          w.config.chart.events.selection = function (chart, e) {\n            targets.forEach(function (target) {\n              var targetChart = ApexCharts.getChartByID(target);\n              var yaxis = Utils$1.clone(w.config.yaxis);\n\n              if (w.config.chart.brush.autoScaleYaxis && targetChart.w.globals.series.length === 1) {\n                var scale = new Range$1(targetChart);\n                yaxis = scale.autoScaleY(targetChart, yaxis, e);\n              }\n\n              var multipleYaxis = targetChart.w.config.yaxis.reduce(function (acc, curr, index) {\n                return [].concat(_toConsumableArray(acc), [_objectSpread2(_objectSpread2({}, targetChart.w.config.yaxis[index]), {}, {\n                  min: yaxis[0].min,\n                  max: yaxis[0].max\n                })]);\n              }, []);\n\n              targetChart.ctx.updateHelpers._updateOptions({\n                xaxis: {\n                  min: e.xaxis.min,\n                  max: e.xaxis.max\n                },\n                yaxis: multipleYaxis\n              }, false, false, false, false);\n            });\n          };\n        }\n      }\n    }]);\n\n    return Core;\n  }();\n\n  var UpdateHelpers = /*#__PURE__*/function () {\n    function UpdateHelpers(ctx) {\n      _classCallCheck(this, UpdateHelpers);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n    /**\n     * private method to update Options.\n     *\n     * @param {object} options - A new config object can be passed which will be merged with the existing config object\n     * @param {boolean} redraw - should redraw from beginning or should use existing paths and redraw from there\n     * @param {boolean} animate - should animate or not on updating Options\n     * @param {boolean} overwriteInitialConfig - should update the initial config or not\n     */\n\n\n    _createClass(UpdateHelpers, [{\n      key: \"_updateOptions\",\n      value: function _updateOptions(options) {\n        var _this = this;\n\n        var redraw = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n        var animate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n        var updateSyncedCharts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;\n        var overwriteInitialConfig = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n        return new Promise(function (resolve) {\n          var charts = [_this.ctx];\n\n          if (updateSyncedCharts) {\n            charts = _this.ctx.getSyncedCharts();\n          }\n\n          if (_this.ctx.w.globals.isExecCalled) {\n            // If the user called exec method, we don't want to get grouped charts as user specifically provided a chartID to update\n            charts = [_this.ctx];\n            _this.ctx.w.globals.isExecCalled = false;\n          }\n\n          charts.forEach(function (ch, chartIndex) {\n            var w = ch.w;\n            w.globals.shouldAnimate = animate;\n\n            if (!redraw) {\n              w.globals.resized = true;\n              w.globals.dataChanged = true;\n\n              if (animate) {\n                ch.series.getPreviousPaths();\n              }\n            }\n\n            if (options && _typeof(options) === 'object') {\n              ch.config = new Config(options);\n              options = CoreUtils.extendArrayProps(ch.config, options, w); // fixes #914, #623\n\n              if (ch.w.globals.chartID !== _this.ctx.w.globals.chartID) {\n                // don't overwrite series of synchronized charts\n                delete options.series;\n              }\n\n              w.config = Utils$1.extend(w.config, options);\n\n              if (overwriteInitialConfig) {\n                // we need to forget the lastXAxis and lastYAxis as user forcefully overwriteInitialConfig. If we do not do this, and next time when user zooms the chart after setting yaxis.min/max or xaxis.min/max - the stored lastXAxis will never allow the chart to use the updated min/max by user.\n                w.globals.lastXAxis = options.xaxis ? Utils$1.clone(options.xaxis) : [];\n                w.globals.lastYAxis = options.yaxis ? Utils$1.clone(options.yaxis) : []; // After forgetting lastAxes, we need to restore the new config in initialConfig/initialSeries\n\n                w.globals.initialConfig = Utils$1.extend({}, w.config);\n                w.globals.initialSeries = Utils$1.clone(w.config.series);\n\n                if (options.series) {\n                  // Replace the collapsed series data\n                  for (var i = 0; i < w.globals.collapsedSeriesIndices.length; i++) {\n                    var series = w.config.series[w.globals.collapsedSeriesIndices[i]];\n                    w.globals.collapsedSeries[i].data = w.globals.axisCharts ? series.data.slice() : series;\n                  }\n\n                  for (var _i = 0; _i < w.globals.ancillaryCollapsedSeriesIndices.length; _i++) {\n                    var _series = w.config.series[w.globals.ancillaryCollapsedSeriesIndices[_i]];\n                    w.globals.ancillaryCollapsedSeries[_i].data = w.globals.axisCharts ? _series.data.slice() : _series;\n                  } // Ensure that auto-generated axes are scaled to the visible data\n\n\n                  ch.series.emptyCollapsedSeries(w.config.series);\n                }\n              }\n            }\n\n            return ch.update(options).then(function () {\n              if (chartIndex === charts.length - 1) {\n                resolve(ch);\n              }\n            });\n          });\n        });\n      }\n      /**\n       * Private method to update Series.\n       *\n       * @param {array} series - New series which will override the existing\n       */\n\n    }, {\n      key: \"_updateSeries\",\n      value: function _updateSeries(newSeries, animate) {\n        var _this2 = this;\n\n        var overwriteInitialSeries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n        return new Promise(function (resolve) {\n          var w = _this2.w;\n          w.globals.shouldAnimate = animate;\n          w.globals.dataChanged = true;\n\n          if (animate) {\n            _this2.ctx.series.getPreviousPaths();\n          }\n\n          var existingSeries; // axis charts\n\n          if (w.globals.axisCharts) {\n            existingSeries = newSeries.map(function (s, i) {\n              return _this2._extendSeries(s, i);\n            });\n\n            if (existingSeries.length === 0) {\n              existingSeries = [{\n                data: []\n              }];\n            }\n\n            w.config.series = existingSeries;\n          } else {\n            // non-axis chart (pie/radialbar)\n            w.config.series = newSeries.slice();\n          }\n\n          if (overwriteInitialSeries) {\n            w.globals.initialConfig.series = Utils$1.clone(w.config.series);\n            w.globals.initialSeries = Utils$1.clone(w.config.series);\n          }\n\n          return _this2.ctx.update().then(function () {\n            resolve(_this2.ctx);\n          });\n        });\n      }\n    }, {\n      key: \"_extendSeries\",\n      value: function _extendSeries(s, i) {\n        var w = this.w;\n        var ser = w.config.series[i];\n        return _objectSpread2(_objectSpread2({}, w.config.series[i]), {}, {\n          name: s.name ? s.name : ser && ser.name,\n          color: s.color ? s.color : ser && ser.color,\n          type: s.type ? s.type : ser && ser.type,\n          data: s.data ? s.data : ser && ser.data\n        });\n      }\n    }, {\n      key: \"toggleDataPointSelection\",\n      value: function toggleDataPointSelection(seriesIndex, dataPointIndex) {\n        var w = this.w;\n        var elPath = null;\n        var parent = \".apexcharts-series[data\\\\:realIndex='\".concat(seriesIndex, \"']\");\n\n        if (w.globals.axisCharts) {\n          elPath = w.globals.dom.Paper.select(\"\".concat(parent, \" path[j='\").concat(dataPointIndex, \"'], \").concat(parent, \" circle[j='\").concat(dataPointIndex, \"'], \").concat(parent, \" rect[j='\").concat(dataPointIndex, \"']\")).members[0];\n        } else {\n          // dataPointIndex will be undefined here, hence using seriesIndex\n          if (typeof dataPointIndex === 'undefined') {\n            elPath = w.globals.dom.Paper.select(\"\".concat(parent, \" path[j='\").concat(seriesIndex, \"']\")).members[0];\n\n            if (w.config.chart.type === 'pie' || w.config.chart.type === 'polarArea' || w.config.chart.type === 'donut') {\n              this.ctx.pie.pieClicked(seriesIndex);\n            }\n          }\n        }\n\n        if (elPath) {\n          var graphics = new Graphics(this.ctx);\n          graphics.pathMouseDown(elPath, null);\n        } else {\n          console.warn('toggleDataPointSelection: Element not found');\n          return null;\n        }\n\n        return elPath.node ? elPath.node : null;\n      }\n    }, {\n      key: \"forceXAxisUpdate\",\n      value: function forceXAxisUpdate(options) {\n        var w = this.w;\n        var minmax = ['min', 'max'];\n        minmax.forEach(function (a) {\n          if (typeof options.xaxis[a] !== 'undefined') {\n            w.config.xaxis[a] = options.xaxis[a];\n            w.globals.lastXAxis[a] = options.xaxis[a];\n          }\n        });\n\n        if (options.xaxis.categories && options.xaxis.categories.length) {\n          w.config.xaxis.categories = options.xaxis.categories;\n        }\n\n        if (w.config.xaxis.convertedCatToNumeric) {\n          var defaults = new Defaults(options);\n          options = defaults.convertCatToNumericXaxis(options, this.ctx);\n        }\n\n        return options;\n      }\n    }, {\n      key: \"forceYAxisUpdate\",\n      value: function forceYAxisUpdate(options) {\n        if (options.chart && options.chart.stacked && options.chart.stackType === '100%') {\n          if (Array.isArray(options.yaxis)) {\n            options.yaxis.forEach(function (yaxe, index) {\n              options.yaxis[index].min = 0;\n              options.yaxis[index].max = 100;\n            });\n          } else {\n            options.yaxis.min = 0;\n            options.yaxis.max = 100;\n          }\n        }\n\n        return options;\n      }\n      /**\n       * This function reverts the yaxis and xaxis min/max values to what it was when the chart was defined.\n       * This function fixes an important bug where a user might load a new series after zooming in/out of previous series which resulted in wrong min/max\n       * Also, this should never be called internally on zoom/pan - the reset should only happen when user calls the updateSeries() function externally\n       * The function also accepts an object {xaxis, yaxis} which when present is set as the new xaxis/yaxis\n       */\n\n    }, {\n      key: \"revertDefaultAxisMinMax\",\n      value: function revertDefaultAxisMinMax(opts) {\n        var _this3 = this;\n\n        var w = this.w;\n        var xaxis = w.globals.lastXAxis;\n        var yaxis = w.globals.lastYAxis;\n\n        if (opts && opts.xaxis) {\n          xaxis = opts.xaxis;\n        }\n\n        if (opts && opts.yaxis) {\n          yaxis = opts.yaxis;\n        }\n\n        w.config.xaxis.min = xaxis.min;\n        w.config.xaxis.max = xaxis.max;\n\n        var getLastYAxis = function getLastYAxis(index) {\n          if (typeof yaxis[index] !== 'undefined') {\n            w.config.yaxis[index].min = yaxis[index].min;\n            w.config.yaxis[index].max = yaxis[index].max;\n          }\n        };\n\n        w.config.yaxis.map(function (yaxe, index) {\n          if (w.globals.zoomed) {\n            // user has zoomed, check the last yaxis\n            getLastYAxis(index);\n          } else {\n            // user hasn't zoomed, check the last yaxis first\n            if (typeof yaxis[index] !== 'undefined') {\n              getLastYAxis(index);\n            } else {\n              // if last y-axis don't exist, check the original yaxis\n              if (typeof _this3.ctx.opts.yaxis[index] !== 'undefined') {\n                yaxe.min = _this3.ctx.opts.yaxis[index].min;\n                yaxe.max = _this3.ctx.opts.yaxis[index].max;\n              }\n            }\n          }\n        });\n      }\n    }]);\n\n    return UpdateHelpers;\n  }();\n\n  (function (root, factory) {\n    /* istanbul ignore next */\n    if (typeof define === 'function' && define.amd) {\n      define(function () {\n        return factory(root, root.document);\n      });\n      /* below check fixes #412 */\n    } else if ((typeof exports === \"undefined\" ? \"undefined\" : _typeof(exports)) === 'object' && typeof module !== 'undefined') {\n      module.exports = root.document ? factory(root, root.document) : function (w) {\n        return factory(w, w.document);\n      };\n    } else {\n      root.SVG = factory(root, root.document);\n    }\n  })(typeof window !== 'undefined' ? window : undefined, function (window, document) {\n    // Find global reference - uses 'this' by default when available,\n    // falls back to 'window' otherwise (for bundlers like Webpack)\n    var globalRef = typeof this !== 'undefined' ? this : window; // The main wrapping element\n\n    var SVG = globalRef.SVG = function (element) {\n      if (SVG.supported) {\n        element = new SVG.Doc(element);\n\n        if (!SVG.parser.draw) {\n          SVG.prepare();\n        }\n\n        return element;\n      }\n    }; // Default namespaces\n\n\n    SVG.ns = 'http://www.w3.org/2000/svg';\n    SVG.xmlns = 'http://www.w3.org/2000/xmlns/';\n    SVG.xlink = 'http://www.w3.org/1999/xlink';\n    SVG.svgjs = 'http://svgjs.dev'; // Svg support test\n\n    SVG.supported = function () {\n      return true; // !!document.createElementNS &&\n      //     !! document.createElementNS(SVG.ns,'svg').createSVGRect\n    }(); // Don't bother to continue if SVG is not supported\n\n\n    if (!SVG.supported) return false; // Element id sequence\n\n    SVG.did = 1000; // Get next named element id\n\n    SVG.eid = function (name) {\n      return 'Svgjs' + capitalize(name) + SVG.did++;\n    }; // Method for element creation\n\n\n    SVG.create = function (name) {\n      // create element\n      var element = document.createElementNS(this.ns, name); // apply unique id\n\n      element.setAttribute('id', this.eid(name));\n      return element;\n    }; // Method for extending objects\n\n\n    SVG.extend = function () {\n      var modules, methods; // Get list of modules\n\n      modules = [].slice.call(arguments); // Get object with extensions\n\n      methods = modules.pop();\n\n      for (var i = modules.length - 1; i >= 0; i--) {\n        if (modules[i]) {\n          for (var key in methods) {\n            modules[i].prototype[key] = methods[key];\n          }\n        }\n      } // Make sure SVG.Set inherits any newly added methods\n\n\n      if (SVG.Set && SVG.Set.inherit) {\n        SVG.Set.inherit();\n      }\n    }; // Invent new element\n\n\n    SVG.invent = function (config) {\n      // Create element initializer\n      var initializer = typeof config.create === 'function' ? config.create : function () {\n        this.constructor.call(this, SVG.create(config.create));\n      }; // Inherit prototype\n\n      if (config.inherit) {\n        initializer.prototype = new config.inherit();\n      } // Extend with methods\n\n\n      if (config.extend) {\n        SVG.extend(initializer, config.extend);\n      } // Attach construct method to parent\n\n\n      if (config.construct) {\n        SVG.extend(config.parent || SVG.Container, config.construct);\n      }\n\n      return initializer;\n    }; // Adopt existing svg elements\n\n\n    SVG.adopt = function (node) {\n      // check for presence of node\n      if (!node) return null; // make sure a node isn't already adopted\n\n      if (node.instance) return node.instance; // initialize variables\n\n      var element; // adopt with element-specific settings\n\n      if (node.nodeName == 'svg') {\n        element = node.parentNode instanceof window.SVGElement ? new SVG.Nested() : new SVG.Doc();\n      } else if (node.nodeName == 'linearGradient') {\n        element = new SVG.Gradient('linear');\n      } else if (node.nodeName == 'radialGradient') {\n        element = new SVG.Gradient('radial');\n      } else if (SVG[capitalize(node.nodeName)]) {\n        element = new SVG[capitalize(node.nodeName)]();\n      } else {\n        element = new SVG.Element(node);\n      } // ensure references\n\n\n      element.type = node.nodeName;\n      element.node = node;\n      node.instance = element; // SVG.Class specific preparations\n\n      if (element instanceof SVG.Doc) {\n        element.namespace().defs();\n      } // pull svgjs data from the dom (getAttributeNS doesn't work in html5)\n\n\n      element.setData(JSON.parse(node.getAttribute('svgjs:data')) || {});\n      return element;\n    }; // Initialize parsing element\n\n\n    SVG.prepare = function () {\n      // Select document body and create invisible svg element\n      var body = document.getElementsByTagName('body')[0],\n          draw = (body ? new SVG.Doc(body) : SVG.adopt(document.documentElement).nested()).size(2, 0); // Create parser object\n\n      SVG.parser = {\n        body: body || document.documentElement,\n        draw: draw.style('opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden').node,\n        poly: draw.polyline().node,\n        path: draw.path().node,\n        native: SVG.create('svg')\n      };\n    };\n\n    SVG.parser = {\n      native: SVG.create('svg')\n    };\n    document.addEventListener('DOMContentLoaded', function () {\n      if (!SVG.parser.draw) {\n        SVG.prepare();\n      }\n    }, false); // Storage for regular expressions\n\n    SVG.regex = {\n      // Parse unit value\n      numberAndUnit: /^([+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?)([a-z%]*)$/i,\n      // Parse hex value\n      hex: /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i,\n      // Parse rgb value\n      rgb: /rgb\\((\\d+),(\\d+),(\\d+)\\)/,\n      // Parse reference id\n      reference: /#([a-z0-9\\-_]+)/i,\n      // splits a transformation chain\n      transforms: /\\)\\s*,?\\s*/,\n      // Whitespace\n      whitespace: /\\s/g,\n      // Test hex value\n      isHex: /^#[a-f0-9]{3,6}$/i,\n      // Test rgb value\n      isRgb: /^rgb\\(/,\n      // Test css declaration\n      isCss: /[^:]+:[^;]+;?/,\n      // Test for blank string\n      isBlank: /^(\\s+)?$/,\n      // Test for numeric string\n      isNumber: /^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?$/i,\n      // Test for percent value\n      isPercent: /^-?[\\d\\.]+%$/,\n      // Test for image url\n      isImage: /\\.(jpg|jpeg|png|gif|svg)(\\?[^=]+.*)?/i,\n      // split at whitespace and comma\n      delimiter: /[\\s,]+/,\n      // The following regex are used to parse the d attribute of a path\n      // Matches all hyphens which are not after an exponent\n      hyphen: /([^e])\\-/gi,\n      // Replaces and tests for all path letters\n      pathLetters: /[MLHVCSQTAZ]/gi,\n      // yes we need this one, too\n      isPathLetter: /[MLHVCSQTAZ]/i,\n      // matches 0.154.23.45\n      numbersWithDots: /((\\d?\\.\\d+(?:e[+-]?\\d+)?)((?:\\.\\d+(?:e[+-]?\\d+)?)+))+/gi,\n      // matches .\n      dots: /\\./g\n    };\n    SVG.utils = {\n      // Map function\n      map: function map(array, block) {\n        var il = array.length,\n            result = [];\n\n        for (var i = 0; i < il; i++) {\n          result.push(block(array[i]));\n        }\n\n        return result;\n      },\n      // Filter function\n      filter: function filter(array, block) {\n        var il = array.length,\n            result = [];\n\n        for (var i = 0; i < il; i++) {\n          if (block(array[i])) {\n            result.push(array[i]);\n          }\n        }\n\n        return result;\n      },\n      filterSVGElements: function filterSVGElements(nodes) {\n        return this.filter(nodes, function (el) {\n          return el instanceof window.SVGElement;\n        });\n      }\n    };\n    SVG.defaults = {\n      // Default attribute values\n      attrs: {\n        // fill and stroke\n        'fill-opacity': 1,\n        'stroke-opacity': 1,\n        'stroke-width': 0,\n        'stroke-linejoin': 'miter',\n        'stroke-linecap': 'butt',\n        fill: '#000000',\n        stroke: '#000000',\n        opacity: 1,\n        // position\n        x: 0,\n        y: 0,\n        cx: 0,\n        cy: 0,\n        // size\n        width: 0,\n        height: 0,\n        // radius\n        r: 0,\n        rx: 0,\n        ry: 0,\n        // gradient\n        offset: 0,\n        'stop-opacity': 1,\n        'stop-color': '#000000',\n        // text\n        'font-size': 16,\n        'font-family': 'Helvetica, Arial, sans-serif',\n        'text-anchor': 'start'\n      }\n    }; // Module for color convertions\n\n    SVG.Color = function (color) {\n      var match; // initialize defaults\n\n      this.r = 0;\n      this.g = 0;\n      this.b = 0;\n      if (!color) return; // parse color\n\n      if (typeof color === 'string') {\n        if (SVG.regex.isRgb.test(color)) {\n          // get rgb values\n          match = SVG.regex.rgb.exec(color.replace(SVG.regex.whitespace, '')); // parse numeric values\n\n          this.r = parseInt(match[1]);\n          this.g = parseInt(match[2]);\n          this.b = parseInt(match[3]);\n        } else if (SVG.regex.isHex.test(color)) {\n          // get hex values\n          match = SVG.regex.hex.exec(fullHex(color)); // parse numeric values\n\n          this.r = parseInt(match[1], 16);\n          this.g = parseInt(match[2], 16);\n          this.b = parseInt(match[3], 16);\n        }\n      } else if (_typeof(color) === 'object') {\n        this.r = color.r;\n        this.g = color.g;\n        this.b = color.b;\n      }\n    };\n\n    SVG.extend(SVG.Color, {\n      // Default to hex conversion\n      toString: function toString() {\n        return this.toHex();\n      },\n      // Build hex value\n      toHex: function toHex() {\n        return '#' + compToHex(this.r) + compToHex(this.g) + compToHex(this.b);\n      },\n      // Build rgb value\n      toRgb: function toRgb() {\n        return 'rgb(' + [this.r, this.g, this.b].join() + ')';\n      },\n      // Calculate true brightness\n      brightness: function brightness() {\n        return this.r / 255 * 0.30 + this.g / 255 * 0.59 + this.b / 255 * 0.11;\n      },\n      // Make color morphable\n      morph: function morph(color) {\n        this.destination = new SVG.Color(color);\n        return this;\n      },\n      // Get morphed color at given position\n      at: function at(pos) {\n        // make sure a destination is defined\n        if (!this.destination) return this; // normalise pos\n\n        pos = pos < 0 ? 0 : pos > 1 ? 1 : pos; // generate morphed color\n\n        return new SVG.Color({\n          r: ~~(this.r + (this.destination.r - this.r) * pos),\n          g: ~~(this.g + (this.destination.g - this.g) * pos),\n          b: ~~(this.b + (this.destination.b - this.b) * pos)\n        });\n      }\n    }); // Testers\n    // Test if given value is a color string\n\n    SVG.Color.test = function (color) {\n      color += '';\n      return SVG.regex.isHex.test(color) || SVG.regex.isRgb.test(color);\n    }; // Test if given value is a rgb object\n\n\n    SVG.Color.isRgb = function (color) {\n      return color && typeof color.r === 'number' && typeof color.g === 'number' && typeof color.b === 'number';\n    }; // Test if given value is a color\n\n\n    SVG.Color.isColor = function (color) {\n      return SVG.Color.isRgb(color) || SVG.Color.test(color);\n    }; // Module for array conversion\n\n\n    SVG.Array = function (array, fallback) {\n      array = (array || []).valueOf(); // if array is empty and fallback is provided, use fallback\n\n      if (array.length == 0 && fallback) {\n        array = fallback.valueOf();\n      } // parse array\n\n\n      this.value = this.parse(array);\n    };\n\n    SVG.extend(SVG.Array, {\n      // Convert array to string\n      toString: function toString() {\n        return this.value.join(' ');\n      },\n      // Real value\n      valueOf: function valueOf() {\n        return this.value;\n      },\n      // Parse whitespace separated string\n      parse: function parse(array) {\n        array = array.valueOf(); // if already is an array, no need to parse it\n\n        if (Array.isArray(array)) return array;\n        return this.split(array);\n      }\n    }); // Poly points array\n\n    SVG.PointArray = function (array, fallback) {\n      SVG.Array.call(this, array, fallback || [[0, 0]]);\n    }; // Inherit from SVG.Array\n\n\n    SVG.PointArray.prototype = new SVG.Array();\n    SVG.PointArray.prototype.constructor = SVG.PointArray;\n    var pathHandlers = {\n      M: function M(c, p, p0) {\n        p.x = p0.x = c[0];\n        p.y = p0.y = c[1];\n        return ['M', p.x, p.y];\n      },\n      L: function L(c, p) {\n        p.x = c[0];\n        p.y = c[1];\n        return ['L', c[0], c[1]];\n      },\n      H: function H(c, p) {\n        p.x = c[0];\n        return ['H', c[0]];\n      },\n      V: function V(c, p) {\n        p.y = c[0];\n        return ['V', c[0]];\n      },\n      C: function C(c, p) {\n        p.x = c[4];\n        p.y = c[5];\n        return ['C', c[0], c[1], c[2], c[3], c[4], c[5]];\n      },\n      Q: function Q(c, p) {\n        p.x = c[2];\n        p.y = c[3];\n        return ['Q', c[0], c[1], c[2], c[3]];\n      },\n      Z: function Z(c, p, p0) {\n        p.x = p0.x;\n        p.y = p0.y;\n        return ['Z'];\n      }\n    };\n    var mlhvqtcsa = 'mlhvqtcsaz'.split('');\n\n    for (var i = 0, il = mlhvqtcsa.length; i < il; ++i) {\n      pathHandlers[mlhvqtcsa[i]] = function (i) {\n        return function (c, p, p0) {\n          if (i == 'H') c[0] = c[0] + p.x;else if (i == 'V') c[0] = c[0] + p.y;else if (i == 'A') {\n            c[5] = c[5] + p.x, c[6] = c[6] + p.y;\n          } else {\n            for (var j = 0, jl = c.length; j < jl; ++j) {\n              c[j] = c[j] + (j % 2 ? p.y : p.x);\n            }\n          }\n\n          if (pathHandlers && typeof pathHandlers[i] === 'function') {\n            // this check fixes jest unit tests\n            return pathHandlers[i](c, p, p0);\n          }\n        };\n      }(mlhvqtcsa[i].toUpperCase());\n    } // Path points array\n\n\n    SVG.PathArray = function (array, fallback) {\n      SVG.Array.call(this, array, fallback || [['M', 0, 0]]);\n    }; // Inherit from SVG.Array\n\n\n    SVG.PathArray.prototype = new SVG.Array();\n    SVG.PathArray.prototype.constructor = SVG.PathArray;\n    SVG.extend(SVG.PathArray, {\n      // Convert array to string\n      toString: function toString() {\n        return arrayToString(this.value);\n      },\n      // Move path string\n      move: function move(x, y) {\n        // get bounding box of current situation\n        var box = this.bbox(); // get relative offset\n\n        x -= box.x;\n        y -= box.y;\n        return this;\n      },\n      // Get morphed path array at given position\n      at: function at(pos) {\n        // make sure a destination is defined\n        if (!this.destination) return this;\n        var sourceArray = this.value,\n            destinationArray = this.destination.value,\n            array = [],\n            pathArray = new SVG.PathArray(),\n            il,\n            jl; // Animate has specified in the SVG spec\n        // See: https://www.w3.org/TR/SVG11/paths.html#PathElement\n\n        for (var i = 0, il = sourceArray.length; i < il; i++) {\n          array[i] = [sourceArray[i][0]];\n\n          for (var j = 1, jl = sourceArray[i].length; j < jl; j++) {\n            array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos;\n          } // For the two flags of the elliptical arc command, the SVG spec say:\n          // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true\n          // Elliptical arc command as an array followed by corresponding indexes:\n          // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]\n          //   0    1   2        3                 4             5      6  7\n\n\n          if (array[i][0] === 'A') {\n            array[i][4] = +(array[i][4] != 0);\n            array[i][5] = +(array[i][5] != 0);\n          }\n        } // Directly modify the value of a path array, this is done this way for performance\n\n\n        pathArray.value = array;\n        return pathArray;\n      },\n      // Absolutize and parse path to array\n      parse: function parse(array) {\n        // if it's already a patharray, no need to parse it\n        if (array instanceof SVG.PathArray) return array.valueOf(); // prepare for parsing\n\n        var s,\n            arr,\n            paramCnt = {\n          'M': 2,\n          'L': 2,\n          'H': 1,\n          'V': 1,\n          'C': 6,\n          'S': 4,\n          'Q': 4,\n          'T': 2,\n          'A': 7,\n          'Z': 0\n        };\n\n        if (typeof array === 'string') {\n          array = array.replace(SVG.regex.numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123\n          .replace(SVG.regex.pathLetters, ' $& ') // put some room between letters and numbers\n          .replace(SVG.regex.hyphen, '$1 -') // add space before hyphen\n          .trim() // trim\n          .split(SVG.regex.delimiter); // split into array\n        } else {\n          array = array.reduce(function (prev, curr) {\n            return [].concat.call(prev, curr);\n          }, []);\n        } // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...]\n\n\n        var arr = [],\n            p = new SVG.Point(),\n            p0 = new SVG.Point(),\n            index = 0,\n            len = array.length;\n\n        do {\n          // Test if we have a path letter\n          if (SVG.regex.isPathLetter.test(array[index])) {\n            s = array[index];\n            ++index; // If last letter was a move command and we got no new, it defaults to [L]ine\n          } else if (s == 'M') {\n            s = 'L';\n          } else if (s == 'm') {\n            s = 'l';\n          }\n\n          arr.push(pathHandlers[s].call(null, array.slice(index, index = index + paramCnt[s.toUpperCase()]).map(parseFloat), p, p0));\n        } while (len > index);\n\n        return arr;\n      },\n      // Get bounding box of path\n      bbox: function bbox() {\n        if (!SVG.parser.draw) {\n          SVG.prepare();\n        }\n\n        SVG.parser.path.setAttribute('d', this.toString());\n        return SVG.parser.path.getBBox();\n      }\n    }); // Module for unit convertions\n\n    SVG.Number = SVG.invent({\n      // Initialize\n      create: function create(value, unit) {\n        // initialize defaults\n        this.value = 0;\n        this.unit = unit || ''; // parse value\n\n        if (typeof value === 'number') {\n          // ensure a valid numeric value\n          this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value;\n        } else if (typeof value === 'string') {\n          unit = value.match(SVG.regex.numberAndUnit);\n\n          if (unit) {\n            // make value numeric\n            this.value = parseFloat(unit[1]); // normalize\n\n            if (unit[5] == '%') {\n              this.value /= 100;\n            } else if (unit[5] == 's') {\n              this.value *= 1000;\n            } // store unit\n\n\n            this.unit = unit[5];\n          }\n        } else {\n          if (value instanceof SVG.Number) {\n            this.value = value.valueOf();\n            this.unit = value.unit;\n          }\n        }\n      },\n      // Add methods\n      extend: {\n        // Stringalize\n        toString: function toString() {\n          return (this.unit == '%' ? ~~(this.value * 1e8) / 1e6 : this.unit == 's' ? this.value / 1e3 : this.value) + this.unit;\n        },\n        toJSON: function toJSON() {\n          return this.toString();\n        },\n        // Convert to primitive\n        valueOf: function valueOf() {\n          return this.value;\n        },\n        // Add number\n        plus: function plus(number) {\n          number = new SVG.Number(number);\n          return new SVG.Number(this + number, this.unit || number.unit);\n        },\n        // Subtract number\n        minus: function minus(number) {\n          number = new SVG.Number(number);\n          return new SVG.Number(this - number, this.unit || number.unit);\n        },\n        // Multiply number\n        times: function times(number) {\n          number = new SVG.Number(number);\n          return new SVG.Number(this * number, this.unit || number.unit);\n        },\n        // Divide number\n        divide: function divide(number) {\n          number = new SVG.Number(number);\n          return new SVG.Number(this / number, this.unit || number.unit);\n        },\n        // Convert to different unit\n        to: function to(unit) {\n          var number = new SVG.Number(this);\n\n          if (typeof unit === 'string') {\n            number.unit = unit;\n          }\n\n          return number;\n        },\n        // Make number morphable\n        morph: function morph(number) {\n          this.destination = new SVG.Number(number);\n\n          if (number.relative) {\n            this.destination.value += this.value;\n          }\n\n          return this;\n        },\n        // Get morphed number at given position\n        at: function at(pos) {\n          // Make sure a destination is defined\n          if (!this.destination) return this; // Generate new morphed number\n\n          return new SVG.Number(this.destination).minus(this).times(pos).plus(this);\n        }\n      }\n    });\n    SVG.Element = SVG.invent({\n      // Initialize node\n      create: function create(node) {\n        // make stroke value accessible dynamically\n        this._stroke = SVG.defaults.attrs.stroke;\n        this._event = null; // initialize data object\n\n        this.dom = {}; // create circular reference\n\n        if (this.node = node) {\n          this.type = node.nodeName;\n          this.node.instance = this; // store current attribute value\n\n          this._stroke = node.getAttribute('stroke') || this._stroke;\n        }\n      },\n      // Add class methods\n      extend: {\n        // Move over x-axis\n        x: function x(_x) {\n          return this.attr('x', _x);\n        },\n        // Move over y-axis\n        y: function y(_y) {\n          return this.attr('y', _y);\n        },\n        // Move by center over x-axis\n        cx: function cx(x) {\n          return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2);\n        },\n        // Move by center over y-axis\n        cy: function cy(y) {\n          return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2);\n        },\n        // Move element to given x and y values\n        move: function move(x, y) {\n          return this.x(x).y(y);\n        },\n        // Move element by its center\n        center: function center(x, y) {\n          return this.cx(x).cy(y);\n        },\n        // Set width of element\n        width: function width(_width) {\n          return this.attr('width', _width);\n        },\n        // Set height of element\n        height: function height(_height) {\n          return this.attr('height', _height);\n        },\n        // Set element size to given width and height\n        size: function size(width, height) {\n          var p = proportionalSize(this, width, height);\n          return this.width(new SVG.Number(p.width)).height(new SVG.Number(p.height));\n        },\n        // Clone element\n        clone: function clone(parent) {\n          // write dom data to the dom so the clone can pickup the data\n          this.writeDataToDom(); // clone element and assign new id\n\n          var clone = assignNewId(this.node.cloneNode(true)); // insert the clone in the given parent or after myself\n\n          if (parent) parent.add(clone);else this.after(clone);\n          return clone;\n        },\n        // Remove element\n        remove: function remove() {\n          if (this.parent()) {\n            this.parent().removeElement(this);\n          }\n\n          return this;\n        },\n        // Replace element\n        replace: function replace(element) {\n          this.after(element).remove();\n          return element;\n        },\n        // Add element to given container and return self\n        addTo: function addTo(parent) {\n          return parent.put(this);\n        },\n        // Add element to given container and return container\n        putIn: function putIn(parent) {\n          return parent.add(this);\n        },\n        // Get / set id\n        id: function id(_id) {\n          return this.attr('id', _id);\n        },\n        // Show element\n        show: function show() {\n          return this.style('display', '');\n        },\n        // Hide element\n        hide: function hide() {\n          return this.style('display', 'none');\n        },\n        // Is element visible?\n        visible: function visible() {\n          return this.style('display') != 'none';\n        },\n        // Return id on string conversion\n        toString: function toString() {\n          return this.attr('id');\n        },\n        // Return array of classes on the node\n        classes: function classes() {\n          var attr = this.attr('class');\n          return attr == null ? [] : attr.trim().split(SVG.regex.delimiter);\n        },\n        // Return true if class exists on the node, false otherwise\n        hasClass: function hasClass(name) {\n          return this.classes().indexOf(name) != -1;\n        },\n        // Add class to the node\n        addClass: function addClass(name) {\n          if (!this.hasClass(name)) {\n            var array = this.classes();\n            array.push(name);\n            this.attr('class', array.join(' '));\n          }\n\n          return this;\n        },\n        // Remove class from the node\n        removeClass: function removeClass(name) {\n          if (this.hasClass(name)) {\n            this.attr('class', this.classes().filter(function (c) {\n              return c != name;\n            }).join(' '));\n          }\n\n          return this;\n        },\n        // Toggle the presence of a class on the node\n        toggleClass: function toggleClass(name) {\n          return this.hasClass(name) ? this.removeClass(name) : this.addClass(name);\n        },\n        // Get referenced element form attribute value\n        reference: function reference(attr) {\n          return SVG.get(this.attr(attr));\n        },\n        // Returns the parent element instance\n        parent: function parent(type) {\n          var parent = this; // check for parent\n\n          if (!parent.node.parentNode) return null; // get parent element\n\n          parent = SVG.adopt(parent.node.parentNode);\n          if (!type) return parent; // loop trough ancestors if type is given\n\n          while (parent && parent.node instanceof window.SVGElement) {\n            if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent;\n            if (!parent.node.parentNode || parent.node.parentNode.nodeName == '#document') return null; // #759, #720\n\n            parent = SVG.adopt(parent.node.parentNode);\n          }\n        },\n        // Get parent document\n        doc: function doc() {\n          return this instanceof SVG.Doc ? this : this.parent(SVG.Doc);\n        },\n        // return array of all ancestors of given type up to the root svg\n        parents: function parents(type) {\n          var parents = [],\n              parent = this;\n\n          do {\n            parent = parent.parent(type);\n            if (!parent || !parent.node) break;\n            parents.push(parent);\n          } while (parent.parent);\n\n          return parents;\n        },\n        // matches the element vs a css selector\n        matches: function matches(selector) {\n          return _matches(this.node, selector);\n        },\n        // Returns the svg node to call native svg methods on it\n        native: function native() {\n          return this.node;\n        },\n        // Import raw svg\n        svg: function svg(_svg) {\n          // create temporary holder\n          var well = document.createElement('svg'); // act as a setter if svg is given\n\n          if (_svg && this instanceof SVG.Parent) {\n            // dump raw svg\n            well.innerHTML = '<svg>' + _svg.replace(/\\n/, '').replace(/<([\\w:-]+)([^<]+?)\\/>/g, '<$1$2></$1>') + '</svg>'; // transplant nodes\n\n            for (var i = 0, il = well.firstChild.childNodes.length; i < il; i++) {\n              this.node.appendChild(well.firstChild.firstChild);\n            } // otherwise act as a getter\n\n          } else {\n            // create a wrapping svg element in case of partial content\n            well.appendChild(_svg = document.createElement('svg')); // write svgjs data to the dom\n\n            this.writeDataToDom(); // insert a copy of this node\n\n            _svg.appendChild(this.node.cloneNode(true)); // return target element\n\n\n            return well.innerHTML.replace(/^<svg>/, '').replace(/<\\/svg>$/, '');\n          }\n\n          return this;\n        },\n        // write svgjs data to the dom\n        writeDataToDom: function writeDataToDom() {\n          // dump variables recursively\n          if (this.each || this.lines) {\n            var fn = this.each ? this : this.lines();\n            fn.each(function () {\n              this.writeDataToDom();\n            });\n          } // remove previously set data\n\n\n          this.node.removeAttribute('svgjs:data');\n\n          if (Object.keys(this.dom).length) {\n            this.node.setAttribute('svgjs:data', JSON.stringify(this.dom));\n          } // see #428\n\n\n          return this;\n        },\n        // set given data to the elements data property\n        setData: function setData(o) {\n          this.dom = o;\n          return this;\n        },\n        is: function is(obj) {\n          return _is(this, obj);\n        }\n      }\n    });\n    SVG.easing = {\n      '-': function _(pos) {\n        return pos;\n      },\n      '<>': function _(pos) {\n        return -Math.cos(pos * Math.PI) / 2 + 0.5;\n      },\n      '>': function _(pos) {\n        return Math.sin(pos * Math.PI / 2);\n      },\n      '<': function _(pos) {\n        return -Math.cos(pos * Math.PI / 2) + 1;\n      }\n    };\n\n    SVG.morph = function (pos) {\n      return function (from, to) {\n        return new SVG.MorphObj(from, to).at(pos);\n      };\n    };\n\n    SVG.Situation = SVG.invent({\n      create: function create(o) {\n        this.init = false;\n        this.reversed = false;\n        this.reversing = false;\n        this.duration = new SVG.Number(o.duration).valueOf();\n        this.delay = new SVG.Number(o.delay).valueOf();\n        this.start = +new Date() + this.delay;\n        this.finish = this.start + this.duration;\n        this.ease = o.ease; // this.loop is incremented from 0 to this.loops\n        // it is also incremented when in an infinite loop (when this.loops is true)\n\n        this.loop = 0;\n        this.loops = false;\n        this.animations = {// functionToCall: [list of morphable objects]\n          // e.g. move: [SVG.Number, SVG.Number]\n        };\n        this.attrs = {// holds all attributes which are not represented from a function svg.js provides\n          // e.g. someAttr: SVG.Number\n        };\n        this.styles = {// holds all styles which should be animated\n          // e.g. fill-color: SVG.Color\n        };\n        this.transforms = [// holds all transformations as transformation objects\n          // e.g. [SVG.Rotate, SVG.Translate, SVG.Matrix]\n        ];\n        this.once = {// functions to fire at a specific position\n          // e.g. \"0.5\": function foo(){}\n        };\n      }\n    });\n    SVG.FX = SVG.invent({\n      create: function create(element) {\n        this._target = element;\n        this.situations = [];\n        this.active = false;\n        this.situation = null;\n        this.paused = false;\n        this.lastPos = 0;\n        this.pos = 0; // The absolute position of an animation is its position in the context of its complete duration (including delay and loops)\n        // When performing a delay, absPos is below 0 and when performing a loop, its value is above 1\n\n        this.absPos = 0;\n        this._speed = 1;\n      },\n      extend: {\n        /**\n         * sets or returns the target of this animation\n         * @param o object || number In case of Object it holds all parameters. In case of number its the duration of the animation\n         * @param ease function || string Function which should be used for easing or easing keyword\n         * @param delay Number indicating the delay before the animation starts\n         * @return target || this\n         */\n        animate: function animate(o, ease, delay) {\n          if (_typeof(o) === 'object') {\n            ease = o.ease;\n            delay = o.delay;\n            o = o.duration;\n          }\n\n          var situation = new SVG.Situation({\n            duration: o || 1000,\n            delay: delay || 0,\n            ease: SVG.easing[ease || '-'] || ease\n          });\n          this.queue(situation);\n          return this;\n        },\n\n        /**\n        * sets a delay before the next element of the queue is called\n        * @param delay Duration of delay in milliseconds\n        * @return this.target()\n        */\n\n        /**\n        * sets or returns the target of this animation\n        * @param null || target SVG.Element which should be set as new target\n        * @return target || this\n        */\n        target: function target(_target) {\n          if (_target && _target instanceof SVG.Element) {\n            this._target = _target;\n            return this;\n          }\n\n          return this._target;\n        },\n        // returns the absolute position at a given time\n        timeToAbsPos: function timeToAbsPos(timestamp) {\n          return (timestamp - this.situation.start) / (this.situation.duration / this._speed);\n        },\n        // returns the timestamp from a given absolute positon\n        absPosToTime: function absPosToTime(absPos) {\n          return this.situation.duration / this._speed * absPos + this.situation.start;\n        },\n        // starts the animationloop\n        startAnimFrame: function startAnimFrame() {\n          this.stopAnimFrame();\n          this.animationFrame = window.requestAnimationFrame(function () {\n            this.step();\n          }.bind(this));\n        },\n        // cancels the animationframe\n        stopAnimFrame: function stopAnimFrame() {\n          window.cancelAnimationFrame(this.animationFrame);\n        },\n        // kicks off the animation - only does something when the queue is currently not active and at least one situation is set\n        start: function start() {\n          // dont start if already started\n          if (!this.active && this.situation) {\n            this.active = true;\n            this.startCurrent();\n          }\n\n          return this;\n        },\n        // start the current situation\n        startCurrent: function startCurrent() {\n          this.situation.start = +new Date() + this.situation.delay / this._speed;\n          this.situation.finish = this.situation.start + this.situation.duration / this._speed;\n          return this.initAnimations().step();\n        },\n\n        /**\n        * adds a function / Situation to the animation queue\n        * @param fn function / situation to add\n        * @return this\n        */\n        queue: function queue(fn) {\n          if (typeof fn === 'function' || fn instanceof SVG.Situation) {\n            this.situations.push(fn);\n          }\n\n          if (!this.situation) this.situation = this.situations.shift();\n          return this;\n        },\n\n        /**\n        * pulls next element from the queue and execute it\n        * @return this\n        */\n        dequeue: function dequeue() {\n          // stop current animation\n          this.stop(); // get next animation from queue\n\n          this.situation = this.situations.shift();\n\n          if (this.situation) {\n            if (this.situation instanceof SVG.Situation) {\n              this.start();\n            } else {\n              // If it is not a SVG.Situation, then it is a function, we execute it\n              this.situation.call(this);\n            }\n          }\n\n          return this;\n        },\n        // updates all animations to the current state of the element\n        // this is important when one property could be changed from another property\n        initAnimations: function initAnimations() {\n          var source;\n          var s = this.situation;\n          if (s.init) return this;\n\n          for (var i in s.animations) {\n            source = this.target()[i]();\n\n            if (!Array.isArray(source)) {\n              source = [source];\n            }\n\n            if (!Array.isArray(s.animations[i])) {\n              s.animations[i] = [s.animations[i]];\n            } // if(s.animations[i].length > source.length) {\n            //  source.concat = source.concat(s.animations[i].slice(source.length, s.animations[i].length))\n            // }\n\n\n            for (var j = source.length; j--;) {\n              // The condition is because some methods return a normal number instead\n              // of a SVG.Number\n              if (s.animations[i][j] instanceof SVG.Number) {\n                source[j] = new SVG.Number(source[j]);\n              }\n\n              s.animations[i][j] = source[j].morph(s.animations[i][j]);\n            }\n          }\n\n          for (var i in s.attrs) {\n            s.attrs[i] = new SVG.MorphObj(this.target().attr(i), s.attrs[i]);\n          }\n\n          for (var i in s.styles) {\n            s.styles[i] = new SVG.MorphObj(this.target().style(i), s.styles[i]);\n          }\n\n          s.initialTransformation = this.target().matrixify();\n          s.init = true;\n          return this;\n        },\n        clearQueue: function clearQueue() {\n          this.situations = [];\n          return this;\n        },\n        clearCurrent: function clearCurrent() {\n          this.situation = null;\n          return this;\n        },\n\n        /** stops the animation immediately\n        * @param jumpToEnd A Boolean indicating whether to complete the current animation immediately.\n        * @param clearQueue A Boolean indicating whether to remove queued animation as well.\n        * @return this\n        */\n        stop: function stop(jumpToEnd, clearQueue) {\n          var active = this.active;\n          this.active = false;\n\n          if (clearQueue) {\n            this.clearQueue();\n          }\n\n          if (jumpToEnd && this.situation) {\n            // initialize the situation if it was not\n            !active && this.startCurrent();\n            this.atEnd();\n          }\n\n          this.stopAnimFrame();\n          return this.clearCurrent();\n        },\n        after: function after(fn) {\n          var c = this.last(),\n              wrapper = function wrapper(e) {\n            if (e.detail.situation == c) {\n              fn.call(this, c);\n              this.off('finished.fx', wrapper); // prevent memory leak\n            }\n          };\n\n          this.target().on('finished.fx', wrapper);\n          return this._callStart();\n        },\n        // adds a callback which is called whenever one animation step is performed\n        during: function during(fn) {\n          var c = this.last(),\n              wrapper = function wrapper(e) {\n            if (e.detail.situation == c) {\n              fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, c);\n            }\n          }; // see above\n\n\n          this.target().off('during.fx', wrapper).on('during.fx', wrapper);\n          this.after(function () {\n            this.off('during.fx', wrapper);\n          });\n          return this._callStart();\n        },\n        // calls after ALL animations in the queue are finished\n        afterAll: function afterAll(fn) {\n          var wrapper = function wrapper(e) {\n            fn.call(this);\n            this.off('allfinished.fx', wrapper);\n          }; // see above\n\n\n          this.target().off('allfinished.fx', wrapper).on('allfinished.fx', wrapper);\n          return this._callStart();\n        },\n        last: function last() {\n          return this.situations.length ? this.situations[this.situations.length - 1] : this.situation;\n        },\n        // adds one property to the animations\n        add: function add(method, args, type) {\n          this.last()[type || 'animations'][method] = args;\n          return this._callStart();\n        },\n\n        /** perform one step of the animation\n        *  @param ignoreTime Boolean indicating whether to ignore time and use position directly or recalculate position based on time\n        *  @return this\n        */\n        step: function step(ignoreTime) {\n          // convert current time to an absolute position\n          if (!ignoreTime) this.absPos = this.timeToAbsPos(+new Date()); // This part convert an absolute position to a position\n\n          if (this.situation.loops !== false) {\n            var absPos, absPosInt, lastLoop; // If the absolute position is below 0, we just treat it as if it was 0\n\n            absPos = Math.max(this.absPos, 0);\n            absPosInt = Math.floor(absPos);\n\n            if (this.situation.loops === true || absPosInt < this.situation.loops) {\n              this.pos = absPos - absPosInt;\n              lastLoop = this.situation.loop;\n              this.situation.loop = absPosInt;\n            } else {\n              this.absPos = this.situation.loops;\n              this.pos = 1; // The -1 here is because we don't want to toggle reversed when all the loops have been completed\n\n              lastLoop = this.situation.loop - 1;\n              this.situation.loop = this.situation.loops;\n            }\n\n            if (this.situation.reversing) {\n              // Toggle reversed if an odd number of loops as occured since the last call of step\n              this.situation.reversed = this.situation.reversed != Boolean((this.situation.loop - lastLoop) % 2);\n            }\n          } else {\n            // If there are no loop, the absolute position must not be above 1\n            this.absPos = Math.min(this.absPos, 1);\n            this.pos = this.absPos;\n          } // while the absolute position can be below 0, the position must not be below 0\n\n\n          if (this.pos < 0) this.pos = 0;\n          if (this.situation.reversed) this.pos = 1 - this.pos; // apply easing\n\n          var eased = this.situation.ease(this.pos); // call once-callbacks\n\n          for (var i in this.situation.once) {\n            if (i > this.lastPos && i <= eased) {\n              this.situation.once[i].call(this.target(), this.pos, eased);\n              delete this.situation.once[i];\n            }\n          } // fire during callback with position, eased position and current situation as parameter\n\n\n          if (this.active) this.target().fire('during', {\n            pos: this.pos,\n            eased: eased,\n            fx: this,\n            situation: this.situation\n          }); // the user may call stop or finish in the during callback\n          // so make sure that we still have a valid situation\n\n          if (!this.situation) {\n            return this;\n          } // apply the actual animation to every property\n\n\n          this.eachAt(); // do final code when situation is finished\n\n          if (this.pos == 1 && !this.situation.reversed || this.situation.reversed && this.pos == 0) {\n            // stop animation callback\n            this.stopAnimFrame(); // fire finished callback with current situation as parameter\n\n            this.target().fire('finished', {\n              fx: this,\n              situation: this.situation\n            });\n\n            if (!this.situations.length) {\n              this.target().fire('allfinished'); // Recheck the length since the user may call animate in the afterAll callback\n\n              if (!this.situations.length) {\n                this.target().off('.fx'); // there shouldnt be any binding left, but to make sure...\n\n                this.active = false;\n              }\n            } // start next animation\n\n\n            if (this.active) this.dequeue();else this.clearCurrent();\n          } else if (!this.paused && this.active) {\n            // we continue animating when we are not at the end\n            this.startAnimFrame();\n          } // save last eased position for once callback triggering\n\n\n          this.lastPos = eased;\n          return this;\n        },\n        // calculates the step for every property and calls block with it\n        eachAt: function eachAt() {\n          var len,\n              at,\n              self = this,\n              target = this.target(),\n              s = this.situation; // apply animations which can be called trough a method\n\n          for (var i in s.animations) {\n            at = [].concat(s.animations[i]).map(function (el) {\n              return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el;\n            });\n            target[i].apply(target, at);\n          } // apply animation which has to be applied with attr()\n\n\n          for (var i in s.attrs) {\n            at = [i].concat(s.attrs[i]).map(function (el) {\n              return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el;\n            });\n            target.attr.apply(target, at);\n          } // apply animation which has to be applied with style()\n\n\n          for (var i in s.styles) {\n            at = [i].concat(s.styles[i]).map(function (el) {\n              return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el;\n            });\n            target.style.apply(target, at);\n          } // animate initialTransformation which has to be chained\n\n\n          if (s.transforms.length) {\n            // get initial initialTransformation\n            at = s.initialTransformation;\n\n            for (var i = 0, len = s.transforms.length; i < len; i++) {\n              // get next transformation in chain\n              var a = s.transforms[i]; // multiply matrix directly\n\n              if (a instanceof SVG.Matrix) {\n                if (a.relative) {\n                  at = at.multiply(new SVG.Matrix().morph(a).at(s.ease(this.pos)));\n                } else {\n                  at = at.morph(a).at(s.ease(this.pos));\n                }\n\n                continue;\n              } // when transformation is absolute we have to reset the needed transformation first\n\n\n              if (!a.relative) {\n                a.undo(at.extract());\n              } // and reapply it after\n\n\n              at = at.multiply(a.at(s.ease(this.pos)));\n            } // set new matrix on element\n\n\n            target.matrix(at);\n          }\n\n          return this;\n        },\n        // adds an once-callback which is called at a specific position and never again\n        once: function once(pos, fn, isEased) {\n          var c = this.last();\n          if (!isEased) pos = c.ease(pos);\n          c.once[pos] = fn;\n          return this;\n        },\n        _callStart: function _callStart() {\n          setTimeout(function () {\n            this.start();\n          }.bind(this), 0);\n          return this;\n        }\n      },\n      parent: SVG.Element,\n      // Add method to parent elements\n      construct: {\n        // Get fx module or create a new one, then animate with given duration and ease\n        animate: function animate(o, ease, delay) {\n          return (this.fx || (this.fx = new SVG.FX(this))).animate(o, ease, delay);\n        },\n        delay: function delay(_delay) {\n          return (this.fx || (this.fx = new SVG.FX(this))).delay(_delay);\n        },\n        stop: function stop(jumpToEnd, clearQueue) {\n          if (this.fx) {\n            this.fx.stop(jumpToEnd, clearQueue);\n          }\n\n          return this;\n        },\n        finish: function finish() {\n          if (this.fx) {\n            this.fx.finish();\n          }\n\n          return this;\n        }\n      }\n    }); // MorphObj is used whenever no morphable object is given\n\n    SVG.MorphObj = SVG.invent({\n      create: function create(from, to) {\n        // prepare color for morphing\n        if (SVG.Color.isColor(to)) return new SVG.Color(from).morph(to); // check if we have a list of values\n\n        if (SVG.regex.delimiter.test(from)) {\n          // prepare path for morphing\n          if (SVG.regex.pathLetters.test(from)) return new SVG.PathArray(from).morph(to); // prepare value list for morphing\n          else return new SVG.Array(from).morph(to);\n        } // prepare number for morphing\n\n\n        if (SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to); // prepare for plain morphing\n\n        this.value = from;\n        this.destination = to;\n      },\n      extend: {\n        at: function at(pos, real) {\n          return real < 1 ? this.value : this.destination;\n        },\n        valueOf: function valueOf() {\n          return this.value;\n        }\n      }\n    });\n    SVG.extend(SVG.FX, {\n      // Add animatable attributes\n      attr: function attr(a, v, relative) {\n        // apply attributes individually\n        if (_typeof(a) === 'object') {\n          for (var key in a) {\n            this.attr(key, a[key]);\n          }\n        } else {\n          this.add(a, v, 'attrs');\n        }\n\n        return this;\n      },\n      // Add animatable plot\n      plot: function plot(a, b, c, d) {\n        // Lines can be plotted with 4 arguments\n        if (arguments.length == 4) {\n          return this.plot([a, b, c, d]);\n        }\n\n        return this.add('plot', new (this.target().morphArray)(a));\n      }\n    });\n    SVG.Box = SVG.invent({\n      create: function create(x, y, width, height) {\n        if (_typeof(x) === 'object' && !(x instanceof SVG.Element)) {\n          // chromes getBoundingClientRect has no x and y property\n          return SVG.Box.call(this, x.left != null ? x.left : x.x, x.top != null ? x.top : x.y, x.width, x.height);\n        } else if (arguments.length == 4) {\n          this.x = x;\n          this.y = y;\n          this.width = width;\n          this.height = height;\n        } // add center, right, bottom...\n\n\n        fullBox(this);\n      }\n    });\n    SVG.BBox = SVG.invent({\n      // Initialize\n      create: function create(element) {\n        SVG.Box.apply(this, [].slice.call(arguments)); // get values if element is given\n\n        if (element instanceof SVG.Element) {\n          var box; // yes this is ugly, but Firefox can be a pain when it comes to elements that are not yet rendered\n\n          try {\n            if (!document.documentElement.contains) {\n              // This is IE - it does not support contains() for top-level SVGs\n              var topParent = element.node;\n\n              while (topParent.parentNode) {\n                topParent = topParent.parentNode;\n              }\n\n              if (topParent != document) throw new Error('Element not in the dom');\n            } else {// the element is NOT in the dom, throw error\n              // disabling the check below which fixes issue #76\n              // if (!document.documentElement.contains(element.node)) throw new Exception('Element not in the dom')\n            } // find native bbox\n\n\n            box = element.node.getBBox();\n          } catch (e) {\n            if (element instanceof SVG.Shape) {\n              if (!SVG.parser.draw) {\n                // fixes apexcharts/vue-apexcharts #14\n                SVG.prepare();\n              }\n\n              var clone = element.clone(SVG.parser.draw.instance).show();\n\n              if (clone && clone.node && typeof clone.node.getBBox === 'function') {\n                // this check fixes jest unit tests\n                box = clone.node.getBBox();\n              }\n\n              if (clone && typeof clone.remove === 'function') {\n                clone.remove();\n              }\n            } else {\n              box = {\n                x: element.node.clientLeft,\n                y: element.node.clientTop,\n                width: element.node.clientWidth,\n                height: element.node.clientHeight\n              };\n            }\n          }\n\n          SVG.Box.call(this, box);\n        }\n      },\n      // Define ancestor\n      inherit: SVG.Box,\n      // Define Parent\n      parent: SVG.Element,\n      // Constructor\n      construct: {\n        // Get bounding box\n        bbox: function bbox() {\n          return new SVG.BBox(this);\n        }\n      }\n    });\n    SVG.BBox.prototype.constructor = SVG.BBox;\n    SVG.Matrix = SVG.invent({\n      // Initialize\n      create: function create(source) {\n        var base = arrayToMatrix([1, 0, 0, 1, 0, 0]); // ensure source as object\n\n        source = source === null ? base : source instanceof SVG.Element ? source.matrixify() : typeof source === 'string' ? arrayToMatrix(source.split(SVG.regex.delimiter).map(parseFloat)) : arguments.length == 6 ? arrayToMatrix([].slice.call(arguments)) : Array.isArray(source) ? arrayToMatrix(source) : source && _typeof(source) === 'object' ? source : base; // merge source\n\n        for (var i = abcdef.length - 1; i >= 0; --i) {\n          this[abcdef[i]] = source[abcdef[i]] != null ? source[abcdef[i]] : base[abcdef[i]];\n        }\n      },\n      // Add methods\n      extend: {\n        // Extract individual transformations\n        extract: function extract() {\n          // find delta transform points\n          var px = deltaTransformPoint(this, 0, 1);\n              deltaTransformPoint(this, 1, 0);\n              var skewX = 180 / Math.PI * Math.atan2(px.y, px.x) - 90;\n          return {\n            // translation\n            x: this.e,\n            y: this.f,\n            transformedX: (this.e * Math.cos(skewX * Math.PI / 180) + this.f * Math.sin(skewX * Math.PI / 180)) / Math.sqrt(this.a * this.a + this.b * this.b),\n            transformedY: (this.f * Math.cos(skewX * Math.PI / 180) + this.e * Math.sin(-skewX * Math.PI / 180)) / Math.sqrt(this.c * this.c + this.d * this.d),\n            // rotation\n            rotation: skewX,\n            a: this.a,\n            b: this.b,\n            c: this.c,\n            d: this.d,\n            e: this.e,\n            f: this.f,\n            matrix: new SVG.Matrix(this)\n          };\n        },\n        // Clone matrix\n        clone: function clone() {\n          return new SVG.Matrix(this);\n        },\n        // Morph one matrix into another\n        morph: function morph(matrix) {\n          // store new destination\n          this.destination = new SVG.Matrix(matrix);\n          return this;\n        },\n        // Multiplies by given matrix\n        multiply: function multiply(matrix) {\n          return new SVG.Matrix(this.native().multiply(parseMatrix(matrix).native()));\n        },\n        // Inverses matrix\n        inverse: function inverse() {\n          return new SVG.Matrix(this.native().inverse());\n        },\n        // Translate matrix\n        translate: function translate(x, y) {\n          return new SVG.Matrix(this.native().translate(x || 0, y || 0));\n        },\n        // Convert to native SVGMatrix\n        native: function native() {\n          // create new matrix\n          var matrix = SVG.parser.native.createSVGMatrix(); // update with current values\n\n          for (var i = abcdef.length - 1; i >= 0; i--) {\n            matrix[abcdef[i]] = this[abcdef[i]];\n          }\n\n          return matrix;\n        },\n        // Convert matrix to string\n        toString: function toString() {\n          // Construct the matrix directly, avoid values that are too small\n          return 'matrix(' + float32String(this.a) + ',' + float32String(this.b) + ',' + float32String(this.c) + ',' + float32String(this.d) + ',' + float32String(this.e) + ',' + float32String(this.f) + ')';\n        }\n      },\n      // Define parent\n      parent: SVG.Element,\n      // Add parent method\n      construct: {\n        // Get current matrix\n        ctm: function ctm() {\n          return new SVG.Matrix(this.node.getCTM());\n        },\n        // Get current screen matrix\n        screenCTM: function screenCTM() {\n          /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537\n             This is needed because FF does not return the transformation matrix\n             for the inner coordinate system when getScreenCTM() is called on nested svgs.\n             However all other Browsers do that */\n          if (this instanceof SVG.Nested) {\n            var rect = this.rect(1, 1);\n            var m = rect.node.getScreenCTM();\n            rect.remove();\n            return new SVG.Matrix(m);\n          }\n\n          return new SVG.Matrix(this.node.getScreenCTM());\n        }\n      }\n    });\n    SVG.Point = SVG.invent({\n      // Initialize\n      create: function create(x, y) {\n        var source,\n            base = {\n          x: 0,\n          y: 0\n        }; // ensure source as object\n\n        source = Array.isArray(x) ? {\n          x: x[0],\n          y: x[1]\n        } : _typeof(x) === 'object' ? {\n          x: x.x,\n          y: x.y\n        } : x != null ? {\n          x: x,\n          y: y != null ? y : x\n        } : base; // If y has no value, then x is used has its value\n        // merge source\n\n        this.x = source.x;\n        this.y = source.y;\n      },\n      // Add methods\n      extend: {\n        // Clone point\n        clone: function clone() {\n          return new SVG.Point(this);\n        },\n        // Morph one point into another\n        morph: function morph(x, y) {\n          // store new destination\n          this.destination = new SVG.Point(x, y);\n          return this;\n        }\n      }\n    });\n    SVG.extend(SVG.Element, {\n      // Get point\n      point: function point(x, y) {\n        return new SVG.Point(x, y).transform(this.screenCTM().inverse());\n      }\n    });\n    SVG.extend(SVG.Element, {\n      // Set svg element attribute\n      attr: function attr(a, v, n) {\n        // act as full getter\n        if (a == null) {\n          // get an object of attributes\n          a = {};\n          v = this.node.attributes;\n\n          for (var n = v.length - 1; n >= 0; n--) {\n            a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue;\n          }\n\n          return a;\n        } else if (_typeof(a) === 'object') {\n          // apply every attribute individually if an object is passed\n          for (var v_ in a) {\n            this.attr(v_, a[v_]);\n          }\n        } else if (v === null) {\n          // remove value\n          this.node.removeAttribute(a);\n        } else if (v == null) {\n          // act as a getter if the first and only argument is not an object\n          v = this.node.getAttribute(a);\n          return v == null ? SVG.defaults.attrs[a] : SVG.regex.isNumber.test(v) ? parseFloat(v) : v;\n        } else {\n          // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0\n          if (a == 'stroke-width') {\n            this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null);\n          } else if (a == 'stroke') {\n            this._stroke = v;\n          } // convert image fill and stroke to patterns\n\n\n          if (a == 'fill' || a == 'stroke') {\n            if (SVG.regex.isImage.test(v)) {\n              v = this.doc().defs().image(v, 0, 0);\n            }\n\n            if (v instanceof SVG.Image) {\n              v = this.doc().defs().pattern(0, 0, function () {\n                this.add(v);\n              });\n            }\n          } // ensure correct numeric values (also accepts NaN and Infinity)\n\n\n          if (typeof v === 'number') {\n            v = new SVG.Number(v);\n          } // ensure full hex color\n          else if (SVG.Color.isColor(v)) {\n            v = new SVG.Color(v);\n          } // parse array values\n          else if (Array.isArray(v)) {\n            v = new SVG.Array(v);\n          } // if the passed attribute is leading...\n\n\n          if (a == 'leading') {\n            // ... call the leading method instead\n            if (this.leading) {\n              this.leading(v);\n            }\n          } else {\n            // set given attribute on node\n            typeof n === 'string' ? this.node.setAttributeNS(n, a, v.toString()) : this.node.setAttribute(a, v.toString());\n          } // rebuild if required\n\n\n          if (this.rebuild && (a == 'font-size' || a == 'x')) {\n            this.rebuild(a, v);\n          }\n        }\n\n        return this;\n      }\n    });\n    SVG.extend(SVG.Element, {\n      // Add transformations\n      transform: function transform(o, relative) {\n        // get target in case of the fx module, otherwise reference this\n        var target = this,\n            matrix;\n   // act as a getter\n\n        if (_typeof(o) !== 'object') {\n          // get current matrix\n          matrix = new SVG.Matrix(target).extract();\n          return typeof o === 'string' ? matrix[o] : matrix;\n        } // get current matrix\n\n\n        matrix = new SVG.Matrix(target); // ensure relative flag\n\n        relative = !!relative || !!o.relative; // act on matrix\n\n        if (o.a != null) {\n          matrix = relative // relative\n          ? matrix.multiply(new SVG.Matrix(o)) // absolute\n          : new SVG.Matrix(o);\n        }\n\n        return this.attr('transform', matrix);\n      }\n    });\n    SVG.extend(SVG.Element, {\n      // Reset all transformations\n      untransform: function untransform() {\n        return this.attr('transform', null);\n      },\n      // merge the whole transformation chain into one matrix and returns it\n      matrixify: function matrixify() {\n        var matrix = (this.attr('transform') || '').split(SVG.regex.transforms).slice(0, -1).map(function (str) {\n          // generate key => value pairs\n          var kv = str.trim().split('(');\n          return [kv[0], kv[1].split(SVG.regex.delimiter).map(function (str) {\n            return parseFloat(str);\n          })];\n        }) // merge every transformation into one matrix\n        .reduce(function (matrix, transform) {\n          if (transform[0] == 'matrix') return matrix.multiply(arrayToMatrix(transform[1]));\n          return matrix[transform[0]].apply(matrix, transform[1]);\n        }, new SVG.Matrix());\n        return matrix;\n      },\n      // add an element to another parent without changing the visual representation on the screen\n      toParent: function toParent(parent) {\n        if (this == parent) return this;\n        var ctm = this.screenCTM();\n        var pCtm = parent.screenCTM().inverse();\n        this.addTo(parent).untransform().transform(pCtm.multiply(ctm));\n        return this;\n      },\n      // same as above with parent equals root-svg\n      toDoc: function toDoc() {\n        return this.toParent(this.doc());\n      }\n    });\n    SVG.Transformation = SVG.invent({\n      create: function create(source, inversed) {\n        if (arguments.length > 1 && typeof inversed !== 'boolean') {\n          return this.constructor.call(this, [].slice.call(arguments));\n        }\n\n        if (Array.isArray(source)) {\n          for (var i = 0, len = this.arguments.length; i < len; ++i) {\n            this[this.arguments[i]] = source[i];\n          }\n        } else if (source && _typeof(source) === 'object') {\n          for (var i = 0, len = this.arguments.length; i < len; ++i) {\n            this[this.arguments[i]] = source[this.arguments[i]];\n          }\n        }\n\n        this.inversed = false;\n\n        if (inversed === true) {\n          this.inversed = true;\n        }\n      }\n    });\n    SVG.Translate = SVG.invent({\n      parent: SVG.Matrix,\n      inherit: SVG.Transformation,\n      create: function create(source, inversed) {\n        this.constructor.apply(this, [].slice.call(arguments));\n      },\n      extend: {\n        arguments: ['transformedX', 'transformedY'],\n        method: 'translate'\n      }\n    });\n    SVG.extend(SVG.Element, {\n      // Dynamic style generator\n      style: function style(s, v) {\n        if (arguments.length == 0) {\n          // get full style\n          return this.node.style.cssText || '';\n        } else if (arguments.length < 2) {\n          // apply every style individually if an object is passed\n          if (_typeof(s) === 'object') {\n            for (var v_ in s) {\n              this.style(v_, s[v_]);\n            }\n          } else if (SVG.regex.isCss.test(s)) {\n            // parse css string\n            s = s.split(/\\s*;\\s*/) // filter out suffix ; and stuff like ;;\n            .filter(function (e) {\n              return !!e;\n            }).map(function (e) {\n              return e.split(/\\s*:\\s*/);\n            }); // apply every definition individually\n\n            while (v = s.pop()) {\n              this.style(v[0], v[1]);\n            }\n          } else {\n            // act as a getter if the first and only argument is not an object\n            return this.node.style[camelCase(s)];\n          }\n        } else {\n          this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v;\n        }\n\n        return this;\n      }\n    });\n    SVG.Parent = SVG.invent({\n      // Initialize node\n      create: function create(element) {\n        this.constructor.call(this, element);\n      },\n      // Inherit from\n      inherit: SVG.Element,\n      // Add class methods\n      extend: {\n        // Returns all child elements\n        children: function children() {\n          return SVG.utils.map(SVG.utils.filterSVGElements(this.node.childNodes), function (node) {\n            return SVG.adopt(node);\n          });\n        },\n        // Add given element at a position\n        add: function add(element, i) {\n          if (i == null) {\n            this.node.appendChild(element.node);\n          } else if (element.node != this.node.childNodes[i]) {\n            this.node.insertBefore(element.node, this.node.childNodes[i]);\n          }\n\n          return this;\n        },\n        // Basically does the same as `add()` but returns the added element instead\n        put: function put(element, i) {\n          this.add(element, i);\n          return element;\n        },\n        // Checks if the given element is a child\n        has: function has(element) {\n          return this.index(element) >= 0;\n        },\n        // Gets index of given element\n        index: function index(element) {\n          return [].slice.call(this.node.childNodes).indexOf(element.node);\n        },\n        // Get a element at the given index\n        get: function get(i) {\n          return SVG.adopt(this.node.childNodes[i]);\n        },\n        // Get first child\n        first: function first() {\n          return this.get(0);\n        },\n        // Get the last child\n        last: function last() {\n          return this.get(this.node.childNodes.length - 1);\n        },\n        // Iterates over all children and invokes a given block\n        each: function each(block, deep) {\n          var il,\n              children = this.children();\n\n          for (var i = 0, il = children.length; i < il; i++) {\n            if (children[i] instanceof SVG.Element) {\n              block.apply(children[i], [i, children]);\n            }\n\n            if (deep && children[i] instanceof SVG.Container) {\n              children[i].each(block, deep);\n            }\n          }\n\n          return this;\n        },\n        // Remove a given child\n        removeElement: function removeElement(element) {\n          this.node.removeChild(element.node);\n          return this;\n        },\n        // Remove all elements in this container\n        clear: function clear() {\n          // remove children\n          while (this.node.hasChildNodes()) {\n            this.node.removeChild(this.node.lastChild);\n          } // remove defs reference\n\n\n          delete this._defs;\n          return this;\n        },\n        // Get defs\n        defs: function defs() {\n          return this.doc().defs();\n        }\n      }\n    });\n    SVG.extend(SVG.Parent, {\n      ungroup: function ungroup(parent, depth) {\n        if (depth === 0 || this instanceof SVG.Defs || this.node == SVG.parser.draw) return this;\n        parent = parent || (this instanceof SVG.Doc ? this : this.parent(SVG.Parent));\n        depth = depth || Infinity;\n        this.each(function () {\n          if (this instanceof SVG.Defs) return this;\n          if (this instanceof SVG.Parent) return this.ungroup(parent, depth - 1);\n          return this.toParent(parent);\n        });\n        this.node.firstChild || this.remove();\n        return this;\n      },\n      flatten: function flatten(parent, depth) {\n        return this.ungroup(parent, depth);\n      }\n    });\n    SVG.Container = SVG.invent({\n      // Initialize node\n      create: function create(element) {\n        this.constructor.call(this, element);\n      },\n      // Inherit from\n      inherit: SVG.Parent\n    });\n    SVG.ViewBox = SVG.invent({\n      // Define parent\n      parent: SVG.Container,\n      // Add parent method\n      construct: {}\n    }) // Add events to elements\n    ;\n    ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', // , 'mouseenter' -> not supported by IE\n    // , 'mouseleave' -> not supported by IE\n    'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].forEach(function (event) {\n      // add event to SVG.Element\n      SVG.Element.prototype[event] = function (f) {\n        // bind event to element rather than element node\n        SVG.on(this.node, event, f);\n        return this;\n      };\n    }); // Initialize listeners stack\n\n    SVG.listeners = [];\n    SVG.handlerMap = [];\n    SVG.listenerId = 0; // Add event binder in the SVG namespace\n\n    SVG.on = function (node, event, listener, binding, options) {\n      // create listener, get object-index\n      var l = listener.bind(binding || node.instance || node),\n          index = (SVG.handlerMap.indexOf(node) + 1 || SVG.handlerMap.push(node)) - 1,\n          ev = event.split('.')[0],\n          ns = event.split('.')[1] || '*'; // ensure valid object\n\n      SVG.listeners[index] = SVG.listeners[index] || {};\n      SVG.listeners[index][ev] = SVG.listeners[index][ev] || {};\n      SVG.listeners[index][ev][ns] = SVG.listeners[index][ev][ns] || {};\n\n      if (!listener._svgjsListenerId) {\n        listener._svgjsListenerId = ++SVG.listenerId;\n      } // reference listener\n\n\n      SVG.listeners[index][ev][ns][listener._svgjsListenerId] = l; // add listener\n\n      node.addEventListener(ev, l, options || {\n        passive: true\n      });\n    }; // Add event unbinder in the SVG namespace\n\n\n    SVG.off = function (node, event, listener) {\n      var index = SVG.handlerMap.indexOf(node),\n          ev = event && event.split('.')[0],\n          ns = event && event.split('.')[1],\n          namespace = '';\n      if (index == -1) return;\n\n      if (listener) {\n        if (typeof listener === 'function') listener = listener._svgjsListenerId;\n        if (!listener) return; // remove listener reference\n\n        if (SVG.listeners[index][ev] && SVG.listeners[index][ev][ns || '*']) {\n          // remove listener\n          node.removeEventListener(ev, SVG.listeners[index][ev][ns || '*'][listener], false);\n          delete SVG.listeners[index][ev][ns || '*'][listener];\n        }\n      } else if (ns && ev) {\n        // remove all listeners for a namespaced event\n        if (SVG.listeners[index][ev] && SVG.listeners[index][ev][ns]) {\n          for (var listener_ in SVG.listeners[index][ev][ns]) {\n            SVG.off(node, [ev, ns].join('.'), listener_);\n          }\n\n          delete SVG.listeners[index][ev][ns];\n        }\n      } else if (ns) {\n        // remove all listeners for a specific namespace\n        for (var event_ in SVG.listeners[index]) {\n          for (var namespace in SVG.listeners[index][event_]) {\n            if (ns === namespace) {\n              SVG.off(node, [event_, ns].join('.'));\n            }\n          }\n        }\n      } else if (ev) {\n        // remove all listeners for the event\n        if (SVG.listeners[index][ev]) {\n          for (var namespace in SVG.listeners[index][ev]) {\n            SVG.off(node, [ev, namespace].join('.'));\n          }\n\n          delete SVG.listeners[index][ev];\n        }\n      } else {\n        // remove all listeners on a given node\n        for (var event_ in SVG.listeners[index]) {\n          SVG.off(node, event_);\n        }\n\n        delete SVG.listeners[index];\n        delete SVG.handlerMap[index];\n      }\n    }; //\n\n\n    SVG.extend(SVG.Element, {\n      // Bind given event to listener\n      on: function on(event, listener, binding, options) {\n        SVG.on(this.node, event, listener, binding, options);\n        return this;\n      },\n      // Unbind event from listener\n      off: function off(event, listener) {\n        SVG.off(this.node, event, listener);\n        return this;\n      },\n      // Fire given event\n      fire: function fire(event, data) {\n        // Dispatch event\n        if (event instanceof window.Event) {\n          this.node.dispatchEvent(event);\n        } else {\n          this.node.dispatchEvent(event = new SVG.CustomEvent(event, {\n            detail: data,\n            cancelable: true\n          }));\n        }\n\n        this._event = event;\n        return this;\n      },\n      event: function event() {\n        return this._event;\n      }\n    });\n    SVG.Defs = SVG.invent({\n      // Initialize node\n      create: 'defs',\n      // Inherit from\n      inherit: SVG.Container\n    });\n    SVG.G = SVG.invent({\n      // Initialize node\n      create: 'g',\n      // Inherit from\n      inherit: SVG.Container,\n      // Add class methods\n      extend: {\n        // Move over x-axis\n        x: function x(_x2) {\n          return _x2 == null ? this.transform('x') : this.transform({\n            x: _x2 - this.x()\n          }, true);\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create a group element\n        group: function group() {\n          return this.put(new SVG.G());\n        }\n      }\n    });\n    SVG.Doc = SVG.invent({\n      // Initialize node\n      create: function create(element) {\n        if (element) {\n          // ensure the presence of a dom element\n          element = typeof element === 'string' ? document.getElementById(element) : element; // If the target is an svg element, use that element as the main wrapper.\n          // This allows svg.js to work with svg documents as well.\n\n          if (element.nodeName == 'svg') {\n            this.constructor.call(this, element);\n          } else {\n            this.constructor.call(this, SVG.create('svg'));\n            element.appendChild(this.node);\n            this.size('100%', '100%');\n          } // set svg element attributes and ensure defs node\n\n\n          this.namespace().defs();\n        }\n      },\n      // Inherit from\n      inherit: SVG.Container,\n      // Add class methods\n      extend: {\n        // Add namespaces\n        namespace: function namespace() {\n          return this.attr({\n            xmlns: SVG.ns,\n            version: '1.1'\n          }).attr('xmlns:xlink', SVG.xlink, SVG.xmlns).attr('xmlns:svgjs', SVG.svgjs, SVG.xmlns);\n        },\n        // Creates and returns defs element\n        defs: function defs() {\n          if (!this._defs) {\n            var defs; // Find or create a defs element in this instance\n\n            if (defs = this.node.getElementsByTagName('defs')[0]) {\n              this._defs = SVG.adopt(defs);\n            } else {\n              this._defs = new SVG.Defs();\n            } // Make sure the defs node is at the end of the stack\n\n\n            this.node.appendChild(this._defs.node);\n          }\n\n          return this._defs;\n        },\n        // custom parent method\n        parent: function parent() {\n          if (!this.node.parentNode || this.node.parentNode.nodeName == '#document') return null;\n          return this.node.parentNode;\n        },\n        // Removes the doc from the DOM\n        remove: function remove() {\n          if (this.parent()) {\n            this.parent().removeChild(this.node);\n          }\n\n          return this;\n        },\n        clear: function clear() {\n          // remove children\n          while (this.node.hasChildNodes()) {\n            this.node.removeChild(this.node.lastChild);\n          } // remove defs reference\n\n\n          delete this._defs; // add back parser\n\n          if (SVG.parser.draw && !SVG.parser.draw.parentNode) {\n            this.node.appendChild(SVG.parser.draw);\n          }\n\n          return this;\n        },\n        clone: function clone(parent) {\n          // write dom data to the dom so the clone can pickup the data\n          this.writeDataToDom(); // get reference to node\n\n          var node = this.node; // clone element and assign new id\n\n          var clone = assignNewId(node.cloneNode(true)); // insert the clone in the given parent or after myself\n\n          if (parent) {\n            (parent.node || parent).appendChild(clone.node);\n          } else {\n            node.parentNode.insertBefore(clone.node, node.nextSibling);\n          }\n\n          return clone;\n        }\n      }\n    }); // ### This module adds backward / forward functionality to elements.\n    //\n\n    SVG.extend(SVG.Element, {// Get all siblings, including myself\n    });\n    SVG.Gradient = SVG.invent({\n      // Initialize node\n      create: function create(type) {\n        this.constructor.call(this, SVG.create(type + 'Gradient')); // store type\n\n        this.type = type;\n      },\n      // Inherit from\n      inherit: SVG.Container,\n      // Add class methods\n      extend: {\n        // Add a color stop\n        at: function at(offset, color, opacity) {\n          return this.put(new SVG.Stop()).update(offset, color, opacity);\n        },\n        // Update gradient\n        update: function update(block) {\n          // remove all stops\n          this.clear(); // invoke passed block\n\n          if (typeof block === 'function') {\n            block.call(this, this);\n          }\n\n          return this;\n        },\n        // Return the fill id\n        fill: function fill() {\n          return 'url(#' + this.id() + ')';\n        },\n        // Alias string convertion to fill\n        toString: function toString() {\n          return this.fill();\n        },\n        // custom attr to handle transform\n        attr: function attr(a, b, c) {\n          if (a == 'transform') a = 'gradientTransform';\n          return SVG.Container.prototype.attr.call(this, a, b, c);\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create gradient element in defs\n        gradient: function gradient(type, block) {\n          return this.defs().gradient(type, block);\n        }\n      }\n    }); // Add animatable methods to both gradient and fx module\n\n    SVG.extend(SVG.Gradient, SVG.FX, {\n      // From position\n      from: function from(x, y) {\n        return (this._target || this).type == 'radial' ? this.attr({\n          fx: new SVG.Number(x),\n          fy: new SVG.Number(y)\n        }) : this.attr({\n          x1: new SVG.Number(x),\n          y1: new SVG.Number(y)\n        });\n      },\n      // To position\n      to: function to(x, y) {\n        return (this._target || this).type == 'radial' ? this.attr({\n          cx: new SVG.Number(x),\n          cy: new SVG.Number(y)\n        }) : this.attr({\n          x2: new SVG.Number(x),\n          y2: new SVG.Number(y)\n        });\n      }\n    }); // Base gradient generation\n\n    SVG.extend(SVG.Defs, {\n      // define gradient\n      gradient: function gradient(type, block) {\n        return this.put(new SVG.Gradient(type)).update(block);\n      }\n    });\n    SVG.Stop = SVG.invent({\n      // Initialize node\n      create: 'stop',\n      // Inherit from\n      inherit: SVG.Element,\n      // Add class methods\n      extend: {\n        // add color stops\n        update: function update(o) {\n          if (typeof o === 'number' || o instanceof SVG.Number) {\n            o = {\n              offset: arguments[0],\n              color: arguments[1],\n              opacity: arguments[2]\n            };\n          } // set attributes\n\n\n          if (o.opacity != null) this.attr('stop-opacity', o.opacity);\n          if (o.color != null) this.attr('stop-color', o.color);\n          if (o.offset != null) this.attr('offset', new SVG.Number(o.offset));\n          return this;\n        }\n      }\n    });\n    SVG.Pattern = SVG.invent({\n      // Initialize node\n      create: 'pattern',\n      // Inherit from\n      inherit: SVG.Container,\n      // Add class methods\n      extend: {\n        // Return the fill id\n        fill: function fill() {\n          return 'url(#' + this.id() + ')';\n        },\n        // Update pattern by rebuilding\n        update: function update(block) {\n          // remove content\n          this.clear(); // invoke passed block\n\n          if (typeof block === 'function') {\n            block.call(this, this);\n          }\n\n          return this;\n        },\n        // Alias string convertion to fill\n        toString: function toString() {\n          return this.fill();\n        },\n        // custom attr to handle transform\n        attr: function attr(a, b, c) {\n          if (a == 'transform') a = 'patternTransform';\n          return SVG.Container.prototype.attr.call(this, a, b, c);\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create pattern element in defs\n        pattern: function pattern(width, height, block) {\n          return this.defs().pattern(width, height, block);\n        }\n      }\n    });\n    SVG.extend(SVG.Defs, {\n      // Define gradient\n      pattern: function pattern(width, height, block) {\n        return this.put(new SVG.Pattern()).update(block).attr({\n          x: 0,\n          y: 0,\n          width: width,\n          height: height,\n          patternUnits: 'userSpaceOnUse'\n        });\n      }\n    });\n    SVG.Shape = SVG.invent({\n      // Initialize node\n      create: function create(element) {\n        this.constructor.call(this, element);\n      },\n      // Inherit from\n      inherit: SVG.Element\n    });\n    SVG.Symbol = SVG.invent({\n      // Initialize node\n      create: 'symbol',\n      // Inherit from\n      inherit: SVG.Container,\n      construct: {\n        // create symbol\n        symbol: function symbol() {\n          return this.put(new SVG.Symbol());\n        }\n      }\n    });\n    SVG.Use = SVG.invent({\n      // Initialize node\n      create: 'use',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add class methods\n      extend: {\n        // Use element as a reference\n        element: function element(_element, file) {\n          // Set lined element\n          return this.attr('href', (file || '') + '#' + _element, SVG.xlink);\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create a use element\n        use: function use(element, file) {\n          return this.put(new SVG.Use()).element(element, file);\n        }\n      }\n    });\n    SVG.Rect = SVG.invent({\n      // Initialize node\n      create: 'rect',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add parent method\n      construct: {\n        // Create a rect element\n        rect: function rect(width, height) {\n          return this.put(new SVG.Rect()).size(width, height);\n        }\n      }\n    });\n    SVG.Circle = SVG.invent({\n      // Initialize node\n      create: 'circle',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add parent method\n      construct: {\n        // Create circle element, based on ellipse\n        circle: function circle(size) {\n          return this.put(new SVG.Circle()).rx(new SVG.Number(size).divide(2)).move(0, 0);\n        }\n      }\n    });\n    SVG.extend(SVG.Circle, SVG.FX, {\n      // Radius x value\n      rx: function rx(_rx) {\n        return this.attr('r', _rx);\n      },\n      // Alias radius x value\n      ry: function ry(_ry) {\n        return this.rx(_ry);\n      }\n    });\n    SVG.Ellipse = SVG.invent({\n      // Initialize node\n      create: 'ellipse',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add parent method\n      construct: {\n        // Create an ellipse\n        ellipse: function ellipse(width, height) {\n          return this.put(new SVG.Ellipse()).size(width, height).move(0, 0);\n        }\n      }\n    });\n    SVG.extend(SVG.Ellipse, SVG.Rect, SVG.FX, {\n      // Radius x value\n      rx: function rx(_rx2) {\n        return this.attr('rx', _rx2);\n      },\n      // Radius y value\n      ry: function ry(_ry2) {\n        return this.attr('ry', _ry2);\n      }\n    }); // Add common method\n\n    SVG.extend(SVG.Circle, SVG.Ellipse, {\n      // Move over x-axis\n      x: function x(_x3) {\n        return _x3 == null ? this.cx() - this.rx() : this.cx(_x3 + this.rx());\n      },\n      // Move over y-axis\n      y: function y(_y2) {\n        return _y2 == null ? this.cy() - this.ry() : this.cy(_y2 + this.ry());\n      },\n      // Move by center over x-axis\n      cx: function cx(x) {\n        return x == null ? this.attr('cx') : this.attr('cx', x);\n      },\n      // Move by center over y-axis\n      cy: function cy(y) {\n        return y == null ? this.attr('cy') : this.attr('cy', y);\n      },\n      // Set width of element\n      width: function width(_width2) {\n        return _width2 == null ? this.rx() * 2 : this.rx(new SVG.Number(_width2).divide(2));\n      },\n      // Set height of element\n      height: function height(_height2) {\n        return _height2 == null ? this.ry() * 2 : this.ry(new SVG.Number(_height2).divide(2));\n      },\n      // Custom size function\n      size: function size(width, height) {\n        var p = proportionalSize(this, width, height);\n        return this.rx(new SVG.Number(p.width).divide(2)).ry(new SVG.Number(p.height).divide(2));\n      }\n    });\n    SVG.Line = SVG.invent({\n      // Initialize node\n      create: 'line',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add class methods\n      extend: {\n        // Get array\n        array: function array() {\n          return new SVG.PointArray([[this.attr('x1'), this.attr('y1')], [this.attr('x2'), this.attr('y2')]]);\n        },\n        // Overwrite native plot() method\n        plot: function plot(x1, y1, x2, y2) {\n          if (x1 == null) {\n            return this.array();\n          } else if (typeof y1 !== 'undefined') {\n            x1 = {\n              x1: x1,\n              y1: y1,\n              x2: x2,\n              y2: y2\n            };\n          } else {\n            x1 = new SVG.PointArray(x1).toLine();\n          }\n\n          return this.attr(x1);\n        },\n        // Move by left top corner\n        move: function move(x, y) {\n          return this.attr(this.array().move(x, y).toLine());\n        },\n        // Set element size to given width and height\n        size: function size(width, height) {\n          var p = proportionalSize(this, width, height);\n          return this.attr(this.array().size(p.width, p.height).toLine());\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create a line element\n        line: function line(x1, y1, x2, y2) {\n          // make sure plot is called as a setter\n          // x1 is not necessarily a number, it can also be an array, a string and a SVG.PointArray\n          return SVG.Line.prototype.plot.apply(this.put(new SVG.Line()), x1 != null ? [x1, y1, x2, y2] : [0, 0, 0, 0]);\n        }\n      }\n    });\n    SVG.Polyline = SVG.invent({\n      // Initialize node\n      create: 'polyline',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add parent method\n      construct: {\n        // Create a wrapped polyline element\n        polyline: function polyline(p) {\n          // make sure plot is called as a setter\n          return this.put(new SVG.Polyline()).plot(p || new SVG.PointArray());\n        }\n      }\n    });\n    SVG.Polygon = SVG.invent({\n      // Initialize node\n      create: 'polygon',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add parent method\n      construct: {\n        // Create a wrapped polygon element\n        polygon: function polygon(p) {\n          // make sure plot is called as a setter\n          return this.put(new SVG.Polygon()).plot(p || new SVG.PointArray());\n        }\n      }\n    }); // Add polygon-specific functions\n\n    SVG.extend(SVG.Polyline, SVG.Polygon, {\n      // Get array\n      array: function array() {\n        return this._array || (this._array = new SVG.PointArray(this.attr('points')));\n      },\n      // Plot new path\n      plot: function plot(p) {\n        return p == null ? this.array() : this.clear().attr('points', typeof p === 'string' ? p : this._array = new SVG.PointArray(p));\n      },\n      // Clear array cache\n      clear: function clear() {\n        delete this._array;\n        return this;\n      },\n      // Move by left top corner\n      move: function move(x, y) {\n        return this.attr('points', this.array().move(x, y));\n      },\n      // Set element size to given width and height\n      size: function size(width, height) {\n        var p = proportionalSize(this, width, height);\n        return this.attr('points', this.array().size(p.width, p.height));\n      }\n    }); // unify all point to point elements\n\n    SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, {\n      // Define morphable array\n      morphArray: SVG.PointArray,\n      // Move by left top corner over x-axis\n      x: function x(_x4) {\n        return _x4 == null ? this.bbox().x : this.move(_x4, this.bbox().y);\n      },\n      // Move by left top corner over y-axis\n      y: function y(_y3) {\n        return _y3 == null ? this.bbox().y : this.move(this.bbox().x, _y3);\n      },\n      // Set width of element\n      width: function width(_width3) {\n        var b = this.bbox();\n        return _width3 == null ? b.width : this.size(_width3, b.height);\n      },\n      // Set height of element\n      height: function height(_height3) {\n        var b = this.bbox();\n        return _height3 == null ? b.height : this.size(b.width, _height3);\n      }\n    });\n    SVG.Path = SVG.invent({\n      // Initialize node\n      create: 'path',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add class methods\n      extend: {\n        // Define morphable array\n        morphArray: SVG.PathArray,\n        // Get array\n        array: function array() {\n          return this._array || (this._array = new SVG.PathArray(this.attr('d')));\n        },\n        // Plot new path\n        plot: function plot(d) {\n          return d == null ? this.array() : this.clear().attr('d', typeof d === 'string' ? d : this._array = new SVG.PathArray(d));\n        },\n        // Clear array cache\n        clear: function clear() {\n          delete this._array;\n          return this;\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create a wrapped path element\n        path: function path(d) {\n          // make sure plot is called as a setter\n          return this.put(new SVG.Path()).plot(d || new SVG.PathArray());\n        }\n      }\n    });\n    SVG.Image = SVG.invent({\n      // Initialize node\n      create: 'image',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add class methods\n      extend: {\n        // (re)load image\t\n        load: function load(url) {\n          if (!url) return this;\n          var self = this,\n              img = new window.Image(); // preload image\t\n\n          SVG.on(img, 'load', function () {\n            SVG.off(img);\n            var p = self.parent(SVG.Pattern);\n            if (p === null) return; // ensure image size\t\n\n            if (self.width() == 0 && self.height() == 0) {\n              self.size(img.width, img.height);\n            } // ensure pattern size if not set\t\n\n\n            if (p && p.width() == 0 && p.height() == 0) {\n              p.size(self.width(), self.height());\n            } // callback\t\n\n\n            if (typeof self._loaded === 'function') {\n              self._loaded.call(self, {\n                width: img.width,\n                height: img.height,\n                ratio: img.width / img.height,\n                url: url\n              });\n            }\n          });\n          SVG.on(img, 'error', function (e) {\n            SVG.off(img);\n\n            if (typeof self._error === 'function') {\n              self._error.call(self, e);\n            }\n          });\n          return this.attr('href', img.src = this.src = url, SVG.xlink);\n        },\n        // Add loaded callback\t\n        loaded: function loaded(_loaded) {\n          this._loaded = _loaded;\n          return this;\n        },\n        error: function error(_error) {\n          this._error = _error;\n          return this;\n        }\n      },\n      // Add parent method\n      construct: {\n        // create image element, load image and set its size\t\n        image: function image(source, width, height) {\n          return this.put(new SVG.Image()).load(source).size(width || 0, height || width || 0);\n        }\n      }\n    });\n    SVG.Text = SVG.invent({\n      // Initialize node\n      create: function create() {\n        this.constructor.call(this, SVG.create('text'));\n        this.dom.leading = new SVG.Number(1.3); // store leading value for rebuilding\n\n        this._rebuild = true; // enable automatic updating of dy values\n\n        this._build = false; // disable build mode for adding multiple lines\n        // set default font\n\n        this.attr('font-family', SVG.defaults.attrs['font-family']);\n      },\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add class methods\n      extend: {\n        // Move over x-axis\n        x: function x(_x5) {\n          // act as getter\n          if (_x5 == null) {\n            return this.attr('x');\n          }\n\n          return this.attr('x', _x5);\n        },\n        // Set the text content\n        text: function text(_text) {\n          // act as getter\n          if (typeof _text === 'undefined') {\n            var _text = '';\n            var children = this.node.childNodes;\n\n            for (var i = 0, len = children.length; i < len; ++i) {\n              // add newline if its not the first child and newLined is set to true\n              if (i != 0 && children[i].nodeType != 3 && SVG.adopt(children[i]).dom.newLined == true) {\n                _text += '\\n';\n              } // add content of this node\n\n\n              _text += children[i].textContent;\n            }\n\n            return _text;\n          } // remove existing content\n\n\n          this.clear().build(true);\n\n          if (typeof _text === 'function') {\n            // call block\n            _text.call(this, this);\n          } else {\n            // store text and make sure text is not blank\n            _text = _text.split('\\n'); // build new lines\n\n            for (var i = 0, il = _text.length; i < il; i++) {\n              this.tspan(_text[i]).newLine();\n            }\n          } // disable build mode and rebuild lines\n\n\n          return this.build(false).rebuild();\n        },\n        // Set font size\n        size: function size(_size) {\n          return this.attr('font-size', _size).rebuild();\n        },\n        // Set / get leading\n        leading: function leading(value) {\n          // act as getter\n          if (value == null) {\n            return this.dom.leading;\n          } // act as setter\n\n\n          this.dom.leading = new SVG.Number(value);\n          return this.rebuild();\n        },\n        // Get all the first level lines\n        lines: function lines() {\n          var node = (this.textPath && this.textPath() || this).node; // filter tspans and map them to SVG.js instances\n\n          var lines = SVG.utils.map(SVG.utils.filterSVGElements(node.childNodes), function (el) {\n            return SVG.adopt(el);\n          }); // return an instance of SVG.set\n\n          return new SVG.Set(lines);\n        },\n        // Rebuild appearance type\n        rebuild: function rebuild(_rebuild) {\n          // store new rebuild flag if given\n          if (typeof _rebuild === 'boolean') {\n            this._rebuild = _rebuild;\n          } // define position of all lines\n\n\n          if (this._rebuild) {\n            var self = this,\n                blankLineOffset = 0,\n                dy = this.dom.leading * new SVG.Number(this.attr('font-size'));\n            this.lines().each(function () {\n              if (this.dom.newLined) {\n                if (!self.textPath()) {\n                  this.attr('x', self.attr('x'));\n                }\n\n                if (this.text() == '\\n') {\n                  blankLineOffset += dy;\n                } else {\n                  this.attr('dy', dy + blankLineOffset);\n                  blankLineOffset = 0;\n                }\n              }\n            });\n            this.fire('rebuild');\n          }\n\n          return this;\n        },\n        // Enable / disable build mode\n        build: function build(_build) {\n          this._build = !!_build;\n          return this;\n        },\n        // overwrite method from parent to set data properly\n        setData: function setData(o) {\n          this.dom = o;\n          this.dom.leading = new SVG.Number(o.leading || 1.3);\n          return this;\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create text element\n        text: function text(_text2) {\n          return this.put(new SVG.Text()).text(_text2);\n        },\n        // Create plain text element\n        plain: function plain(text) {\n          return this.put(new SVG.Text()).plain(text);\n        }\n      }\n    });\n    SVG.Tspan = SVG.invent({\n      // Initialize node\n      create: 'tspan',\n      // Inherit from\n      inherit: SVG.Shape,\n      // Add class methods\n      extend: {\n        // Set text content\n        text: function text(_text3) {\n          if (_text3 == null) return this.node.textContent + (this.dom.newLined ? '\\n' : '');\n          typeof _text3 === 'function' ? _text3.call(this, this) : this.plain(_text3);\n          return this;\n        },\n        // Shortcut dx\n        dx: function dx(_dx) {\n          return this.attr('dx', _dx);\n        },\n        // Shortcut dy\n        dy: function dy(_dy) {\n          return this.attr('dy', _dy);\n        },\n        // Create new line\n        newLine: function newLine() {\n          // fetch text parent\n          var t = this.parent(SVG.Text); // mark new line\n\n          this.dom.newLined = true; // apply new hy¡n\n\n          return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x());\n        }\n      }\n    });\n    SVG.extend(SVG.Text, SVG.Tspan, {\n      // Create plain text node\n      plain: function plain(text) {\n        // clear if build mode is disabled\n        if (this._build === false) {\n          this.clear();\n        } // create text node\n\n\n        this.node.appendChild(document.createTextNode(text));\n        return this;\n      },\n      // Create a tspan\n      tspan: function tspan(text) {\n        var node = (this.textPath && this.textPath() || this).node,\n            tspan = new SVG.Tspan(); // clear if build mode is disabled\n\n        if (this._build === false) {\n          this.clear();\n        } // add new tspan\n\n\n        node.appendChild(tspan.node);\n        return tspan.text(text);\n      },\n      // Clear all lines\n      clear: function clear() {\n        var node = (this.textPath && this.textPath() || this).node; // remove existing child nodes\n\n        while (node.hasChildNodes()) {\n          node.removeChild(node.lastChild);\n        }\n\n        return this;\n      },\n      // Get length of text element\n      length: function length() {\n        return this.node.getComputedTextLength();\n      }\n    });\n    SVG.TextPath = SVG.invent({\n      // Initialize node\n      create: 'textPath',\n      // Inherit from\n      inherit: SVG.Parent,\n      // Define parent class\n      parent: SVG.Text,\n      // Add parent method\n      construct: {\n        morphArray: SVG.PathArray,\n        // return the array of the path track element\n        array: function array() {\n          var track = this.track();\n          return track ? track.array() : null;\n        },\n        // Plot path if any\n        plot: function plot(d) {\n          var track = this.track(),\n              pathArray = null;\n\n          if (track) {\n            pathArray = track.plot(d);\n          }\n\n          return d == null ? pathArray : this;\n        },\n        // Get the path track element\n        track: function track() {\n          var path = this.textPath();\n\n          if (path) {\n            return path.reference('href');\n          }\n        },\n        // Get the textPath child\n        textPath: function textPath() {\n          if (this.node.firstChild && this.node.firstChild.nodeName == 'textPath') {\n            return SVG.adopt(this.node.firstChild);\n          }\n        }\n      }\n    });\n    SVG.Nested = SVG.invent({\n      // Initialize node\n      create: function create() {\n        this.constructor.call(this, SVG.create('svg'));\n        this.style('overflow', 'visible');\n      },\n      // Inherit from\n      inherit: SVG.Container,\n      // Add parent method\n      construct: {\n        // Create nested svg document\n        nested: function nested() {\n          return this.put(new SVG.Nested());\n        }\n      }\n    }); // Define list of available attributes for stroke and fill\n\n    var sugar = {\n      stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],\n      fill: ['color', 'opacity', 'rule'],\n      prefix: function prefix(t, a) {\n        return a == 'color' ? t : t + '-' + a;\n      }\n    } // Add sugar for fill and stroke\n    ;\n    ['fill', 'stroke'].forEach(function (m) {\n      var extension = {};\n\n      extension[m] = function (o) {\n        if (typeof o === 'undefined') {\n          return this;\n        }\n\n        if (typeof o === 'string' || SVG.Color.isRgb(o) || o && typeof o.fill === 'function') {\n          this.attr(m, o);\n        } else // set all attributes from sugar.fill and sugar.stroke list\n          {\n            for (var i = sugar[m].length - 1; i >= 0; i--) {\n              if (o[sugar[m][i]] != null) {\n                this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);\n              }\n            }\n          }\n\n        return this;\n      };\n\n      SVG.extend(SVG.Element, SVG.FX, extension);\n    });\n    SVG.extend(SVG.Element, SVG.FX, {\n      // Map translate to transform\n      translate: function translate(x, y) {\n        return this.transform({\n          x: x,\n          y: y\n        });\n      },\n      // Map matrix to transform\n      matrix: function matrix(m) {\n        return this.attr('transform', new SVG.Matrix(arguments.length == 6 ? [].slice.call(arguments) : m));\n      },\n      // Opacity\n      opacity: function opacity(value) {\n        return this.attr('opacity', value);\n      },\n      // Relative move over x axis\n      dx: function dx(x) {\n        return this.x(new SVG.Number(x).plus(this instanceof SVG.FX ? 0 : this.x()), true);\n      },\n      // Relative move over y axis\n      dy: function dy(y) {\n        return this.y(new SVG.Number(y).plus(this instanceof SVG.FX ? 0 : this.y()), true);\n      }\n    });\n    SVG.extend(SVG.Path, {\n      // Get path length\n      length: function length() {\n        return this.node.getTotalLength();\n      },\n      // Get point at length\n      pointAt: function pointAt(length) {\n        return this.node.getPointAtLength(length);\n      }\n    });\n    SVG.Set = SVG.invent({\n      // Initialize\n      create: function create(members) {\n        // Set initial state\n        Array.isArray(members) ? this.members = members : this.clear();\n      },\n      // Add class methods\n      extend: {\n        // Add element to set\n        add: function add() {\n          var il,\n              elements = [].slice.call(arguments);\n\n          for (var i = 0, il = elements.length; i < il; i++) {\n            this.members.push(elements[i]);\n          }\n\n          return this;\n        },\n        // Remove element from set\n        remove: function remove(element) {\n          var i = this.index(element); // remove given child\n\n          if (i > -1) {\n            this.members.splice(i, 1);\n          }\n\n          return this;\n        },\n        // Iterate over all members\n        each: function each(block) {\n          for (var i = 0, il = this.members.length; i < il; i++) {\n            block.apply(this.members[i], [i, this.members]);\n          }\n\n          return this;\n        },\n        // Restore to defaults\n        clear: function clear() {\n          // initialize store\n          this.members = [];\n          return this;\n        },\n        // Get the length of a set\n        length: function length() {\n          return this.members.length;\n        },\n        // Checks if a given element is present in set\n        has: function has(element) {\n          return this.index(element) >= 0;\n        },\n        // retuns index of given element in set\n        index: function index(element) {\n          return this.members.indexOf(element);\n        },\n        // Get member at given index\n        get: function get(i) {\n          return this.members[i];\n        },\n        // Get first member\n        first: function first() {\n          return this.get(0);\n        },\n        // Get last member\n        last: function last() {\n          return this.get(this.members.length - 1);\n        },\n        // Default value\n        valueOf: function valueOf() {\n          return this.members;\n        }\n      },\n      // Add parent method\n      construct: {\n        // Create a new set\n        set: function set(members) {\n          return new SVG.Set(members);\n        }\n      }\n    });\n    SVG.FX.Set = SVG.invent({\n      // Initialize node\n      create: function create(set) {\n        // store reference to set\n        this.set = set;\n      }\n    }); // Alias methods\n\n    SVG.Set.inherit = function () {\n      var methods = []; // gather shape methods\n\n      for (var m in SVG.Shape.prototype) {\n        if (typeof SVG.Shape.prototype[m] === 'function' && typeof SVG.Set.prototype[m] !== 'function') {\n          methods.push(m);\n        }\n      } // apply shape aliasses\n\n\n      methods.forEach(function (method) {\n        SVG.Set.prototype[method] = function () {\n          for (var i = 0, il = this.members.length; i < il; i++) {\n            if (this.members[i] && typeof this.members[i][method] === 'function') {\n              this.members[i][method].apply(this.members[i], arguments);\n            }\n          }\n\n          return method == 'animate' ? this.fx || (this.fx = new SVG.FX.Set(this)) : this;\n        };\n      }); // clear methods for the next round\n\n      methods = []; // gather fx methods\n\n      for (var m in SVG.FX.prototype) {\n        if (typeof SVG.FX.prototype[m] === 'function' && typeof SVG.FX.Set.prototype[m] !== 'function') {\n          methods.push(m);\n        }\n      } // apply fx aliasses\n\n\n      methods.forEach(function (method) {\n        SVG.FX.Set.prototype[method] = function () {\n          for (var i = 0, il = this.set.members.length; i < il; i++) {\n            this.set.members[i].fx[method].apply(this.set.members[i].fx, arguments);\n          }\n\n          return this;\n        };\n      });\n    };\n\n    SVG.extend(SVG.Element, {});\n    SVG.extend(SVG.Element, {\n      // Remember arbitrary data\n      remember: function remember(k, v) {\n        // remember every item in an object individually\n        if (_typeof(arguments[0]) === 'object') {\n          for (var v_ in k) {\n            this.remember(v_, k[v_]);\n          }\n        } // retrieve memory\n        else if (arguments.length == 1) {\n          return this.memory()[k];\n        } // store memory\n        else {\n          this.memory()[k] = v;\n        }\n\n        return this;\n      },\n      // Erase a given memory\n      forget: function forget() {\n        if (arguments.length == 0) {\n          this._memory = {};\n        } else {\n          for (var i = arguments.length - 1; i >= 0; i--) {\n            delete this.memory()[arguments[i]];\n          }\n        }\n\n        return this;\n      },\n      // Initialize or return local memory object\n      memory: function memory() {\n        return this._memory || (this._memory = {});\n      }\n    }); // Method for getting an element by id\n\n    SVG.get = function (id) {\n      var node = document.getElementById(idFromReference(id) || id);\n      return SVG.adopt(node);\n    }; // Select elements by query string\n\n\n    SVG.select = function (query, parent) {\n      return new SVG.Set(SVG.utils.map((parent || document).querySelectorAll(query), function (node) {\n        return SVG.adopt(node);\n      }));\n    };\n\n    SVG.extend(SVG.Parent, {\n      // Scoped select method\n      select: function select(query) {\n        return SVG.select(query, this.node);\n      }\n    });\n\n    function pathRegReplace(a, b, c, d) {\n      return c + d.replace(SVG.regex.dots, ' .');\n    } // creates deep clone of array\n\n\n    function _is(el, obj) {\n      return el instanceof obj;\n    } // tests if a given selector matches an element\n\n\n    function _matches(el, selector) {\n      return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);\n    } // Convert dash-separated-string to camelCase\n\n\n    function camelCase(s) {\n      return s.toLowerCase().replace(/-(.)/g, function (m, g) {\n        return g.toUpperCase();\n      });\n    } // Capitalize first letter of a string\n\n\n    function capitalize(s) {\n      return s.charAt(0).toUpperCase() + s.slice(1);\n    } // Ensure to six-based hex\n\n\n    function fullHex(hex) {\n      return hex.length == 4 ? ['#', hex.substring(1, 2), hex.substring(1, 2), hex.substring(2, 3), hex.substring(2, 3), hex.substring(3, 4), hex.substring(3, 4)].join('') : hex;\n    } // Component to hex value\n\n\n    function compToHex(comp) {\n      var hex = comp.toString(16);\n      return hex.length == 1 ? '0' + hex : hex;\n    } // Calculate proportional width and height values when necessary\n\n\n    function proportionalSize(element, width, height) {\n      if (width == null || height == null) {\n        var box = element.bbox();\n\n        if (width == null) {\n          width = box.width / box.height * height;\n        } else if (height == null) {\n          height = box.height / box.width * width;\n        }\n      }\n\n      return {\n        width: width,\n        height: height\n      };\n    } // Delta transform point\n\n\n    function deltaTransformPoint(matrix, x, y) {\n      return {\n        x: x * matrix.a + y * matrix.c + 0,\n        y: x * matrix.b + y * matrix.d + 0\n      };\n    } // Map matrix array to object\n\n\n    function arrayToMatrix(a) {\n      return {\n        a: a[0],\n        b: a[1],\n        c: a[2],\n        d: a[3],\n        e: a[4],\n        f: a[5]\n      };\n    } // Parse matrix if required\n\n\n    function parseMatrix(matrix) {\n      if (!(matrix instanceof SVG.Matrix)) {\n        matrix = new SVG.Matrix(matrix);\n      }\n\n      return matrix;\n    } // Add centre point to transform object\n\n\n    function arrayToString(a) {\n      for (var i = 0, il = a.length, s = ''; i < il; i++) {\n        s += a[i][0];\n\n        if (a[i][1] != null) {\n          s += a[i][1];\n\n          if (a[i][2] != null) {\n            s += ' ';\n            s += a[i][2];\n\n            if (a[i][3] != null) {\n              s += ' ';\n              s += a[i][3];\n              s += ' ';\n              s += a[i][4];\n\n              if (a[i][5] != null) {\n                s += ' ';\n                s += a[i][5];\n                s += ' ';\n                s += a[i][6];\n\n                if (a[i][7] != null) {\n                  s += ' ';\n                  s += a[i][7];\n                }\n              }\n            }\n          }\n        }\n      }\n\n      return s + ' ';\n    } // Deep new id assignment\n\n\n    function assignNewId(node) {\n      // do the same for SVG child nodes as well\n      for (var i = node.childNodes.length - 1; i >= 0; i--) {\n        if (node.childNodes[i] instanceof window.SVGElement) {\n          assignNewId(node.childNodes[i]);\n        }\n      }\n\n      return SVG.adopt(node).id(SVG.eid(node.nodeName));\n    } // Add more bounding box properties\n\n\n    function fullBox(b) {\n      if (b.x == null) {\n        b.x = 0;\n        b.y = 0;\n        b.width = 0;\n        b.height = 0;\n      }\n\n      b.w = b.width;\n      b.h = b.height;\n      b.x2 = b.x + b.width;\n      b.y2 = b.y + b.height;\n      b.cx = b.x + b.width / 2;\n      b.cy = b.y + b.height / 2;\n      return b;\n    } // Get id from reference string\n\n\n    function idFromReference(url) {\n      var m = (url || '').toString().match(SVG.regex.reference);\n      if (m) return m[1];\n    } // If values like 1e-88 are passed, this is not a valid 32 bit float,\n    // but in those cases, we are so close to 0 that 0 works well!\n\n\n    function float32String(v) {\n      return Math.abs(v) > 1e-37 ? v : 0;\n    } // Create matrix array for looping\n\n\n    var abcdef = 'abcdef'.split(''); // Add CustomEvent to IE9 and IE10\t\n\n    if (typeof window.CustomEvent !== 'function') {\n      // Code from: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent\t\n      var CustomEventPoly = function CustomEventPoly(event, options) {\n        options = options || {\n          bubbles: false,\n          cancelable: false,\n          detail: undefined\n        };\n        var e = document.createEvent('CustomEvent');\n        e.initCustomEvent(event, options.bubbles, options.cancelable, options.detail);\n        return e;\n      };\n\n      CustomEventPoly.prototype = window.Event.prototype;\n      SVG.CustomEvent = CustomEventPoly;\n    } else {\n      SVG.CustomEvent = window.CustomEvent;\n    }\n\n    return SVG;\n  });\n\n  /*! svg.filter.js - v2.0.2 - 2016-02-24\n  * https://github.com/wout/svg.filter.js\n  * Copyright (c) 2016 Wout Fierens; Licensed MIT */\n  (function() {\n\n    // Main filter class\n    SVG.Filter = SVG.invent({\n      create: 'filter',\n      inherit: SVG.Parent,\n      extend: {\n        // Static strings\n        source:           'SourceGraphic',\n        sourceAlpha:      'SourceAlpha',\n        background:       'BackgroundImage',\n        backgroundAlpha:  'BackgroundAlpha',\n        fill:             'FillPaint',\n        stroke:           'StrokePaint',\n\n        autoSetIn: true,\n        // Custom put method for leaner code\n        put: function(element, i) {\n          this.add(element, i);\n\n          if(!element.attr('in') && this.autoSetIn){\n            element.attr('in',this.source);\n          }\n          if(!element.attr('result')){\n            element.attr('result',element);\n          }\n\n          return element\n        },\n        // Blend effect\n        blend: function(in1, in2, mode) {\n          return this.put(new SVG.BlendEffect(in1, in2, mode))\n        },\n        // ColorMatrix effect\n        colorMatrix: function(type, values) {\n          return this.put(new SVG.ColorMatrixEffect(type, values))\n        },\n        // ConvolveMatrix effect\n        convolveMatrix: function(matrix) {\n          return this.put(new SVG.ConvolveMatrixEffect(matrix))\n        },\n        // ComponentTransfer effect\n        componentTransfer: function(components) {\n          return this.put(new SVG.ComponentTransferEffect(components))\n        },\n        // Composite effect\n        composite: function(in1, in2, operator) {\n          return this.put(new SVG.CompositeEffect(in1, in2, operator))\n        },\n        // Flood effect\n        flood: function(color, opacity) {\n          return this.put(new SVG.FloodEffect(color, opacity))\n        },\n        // Offset effect\n        offset: function(x, y) {\n          return this.put(new SVG.OffsetEffect(x,y))\n        },\n        // Image effect\n        image: function(src) {\n          return this.put(new SVG.ImageEffect(src))\n        },\n        // Merge effect\n        merge: function() {\n          //pass the array of arguments to the constructor because we dont know if the user gave us an array as the first arguemnt or wether they listed the effects in the arguments\n          var args = [undefined];\n          for(var i in arguments) args.push(arguments[i]);\n          return this.put(new (SVG.MergeEffect.bind.apply(SVG.MergeEffect,args)))\n        },\n        // Gaussian Blur effect\n        gaussianBlur: function(x,y) {\n          return this.put(new SVG.GaussianBlurEffect(x,y))\n        },\n        // Morphology effect\n        morphology: function(operator,radius){\n          return this.put(new SVG.MorphologyEffect(operator,radius))\n        },\n        // DiffuseLighting effect\n        diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){\n          return this.put(new SVG.DiffuseLightingEffect(surfaceScale,diffuseConstant,kernelUnitLength))\n        },\n        // DisplacementMap effect\n        displacementMap: function(in1,in2,scale,xChannelSelector,yChannelSelector){\n          return this.put(new SVG.DisplacementMapEffect(in1,in2,scale,xChannelSelector,yChannelSelector))\n        },\n        // SpecularLighting effect\n        specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){\n          return this.put(new SVG.SpecularLightingEffect(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength))\n        },\n        // Tile effect\n        tile: function(){\n          return this.put(new SVG.TileEffect());\n        },\n        // Turbulence effect\n        turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){\n          return this.put(new SVG.TurbulenceEffect(baseFrequency,numOctaves,seed,stitchTiles,type))\n        },\n        // Default string value\n        toString: function() {\n          return 'url(#' + this.attr('id') + ')'\n        }\n      }\n    });\n\n    //add .filter function\n    SVG.extend(SVG.Defs, {\n      // Define filter\n      filter: function(block) {\n        var filter = this.put(new SVG.Filter);\n\n        /* invoke passed block */\n        if (typeof block === 'function')\n          block.call(filter, filter);\n\n        return filter\n      }\n    });\n    SVG.extend(SVG.Container, {\n      // Define filter on defs\n      filter: function(block) {\n        return this.defs().filter(block)\n      }\n    });\n    SVG.extend(SVG.Element, SVG.G, SVG.Nested, {\n      // Create filter element in defs and store reference\n      filter: function(block) {\n        this.filterer = block instanceof SVG.Element ?\n          block : this.doc().filter(block);\n\n        if(this.doc() && this.filterer.doc() !== this.doc()){\n          this.doc().defs().add(this.filterer);\n        }\n\n        this.attr('filter', this.filterer);\n\n        return this.filterer\n      },\n      // Remove filter\n      unfilter: function(remove) {\n        /* also remove the filter node */\n        if (this.filterer && remove === true)\n          this.filterer.remove();\n\n        /* delete reference to filterer */\n        delete this.filterer;\n\n        /* remove filter attribute */\n        return this.attr('filter', null)\n      }\n    });\n\n    // Create SVG.Effect class\n    SVG.Effect = SVG.invent({\n      create: function(){\n        this.constructor.call(this);\n      },\n      inherit: SVG.Element,\n      extend: {\n        // Set in attribute\n        in: function(effect) {\n          return effect == null? this.parent() && this.parent().select('[result=\"'+this.attr('in')+'\"]').get(0) || this.attr('in') : this.attr('in', effect)\n        },\n        // Named result\n        result: function(result) {\n          return result == null? this.attr('result') : this.attr('result',result)\n        },\n        // Stringification\n        toString: function() {\n          return this.result()\n        }\n      }\n    });\n\n    // create class for parent effects like merge\n    // Inherit from SVG.Parent\n    SVG.ParentEffect = SVG.invent({\n      create: function(){\n        this.constructor.call(this);\n      },\n      inherit: SVG.Parent,\n      extend: {\n        // Set in attribute\n        in: function(effect) {\n          return effect == null? this.parent() && this.parent().select('[result=\"'+this.attr('in')+'\"]').get(0) || this.attr('in') : this.attr('in', effect)\n        },\n        // Named result\n        result: function(result) {\n          return result == null? this.attr('result') : this.attr('result',result)\n        },\n        // Stringification\n        toString: function() {\n          return this.result()\n        }\n      }\n    });\n\n    //chaining\n    var chainingEffects = {\n      // Blend effect\n      blend: function(in2, mode) {\n        return this.parent() && this.parent().blend(this, in2, mode) //pass this as the first input\n      },\n      // ColorMatrix effect\n      colorMatrix: function(type, values) {\n        return this.parent() && this.parent().colorMatrix(type, values).in(this)\n      },\n      // ConvolveMatrix effect\n      convolveMatrix: function(matrix) {\n        return this.parent() && this.parent().convolveMatrix(matrix).in(this)\n      },\n      // ComponentTransfer effect\n      componentTransfer: function(components) {\n        return this.parent() && this.parent().componentTransfer(components).in(this)\n      },\n      // Composite effect\n      composite: function(in2, operator) {\n        return this.parent() && this.parent().composite(this, in2, operator) //pass this as the first input\n      },\n      // Flood effect\n      flood: function(color, opacity) {\n        return this.parent() && this.parent().flood(color, opacity) //this effect dont have inputs\n      },\n      // Offset effect\n      offset: function(x, y) {\n        return this.parent() && this.parent().offset(x,y).in(this)\n      },\n      // Image effect\n      image: function(src) {\n        return this.parent() && this.parent().image(src) //this effect dont have inputs\n      },\n      // Merge effect\n      merge: function() {\n        return this.parent() && this.parent().merge.apply(this.parent(),[this].concat(arguments)) //pass this as the first argument\n      },\n      // Gaussian Blur effect\n      gaussianBlur: function(x,y) {\n        return this.parent() && this.parent().gaussianBlur(x,y).in(this)\n      },\n      // Morphology effect\n      morphology: function(operator,radius){\n        return this.parent() && this.parent().morphology(operator,radius).in(this)\n      },\n      // DiffuseLighting effect\n      diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){\n        return this.parent() && this.parent().diffuseLighting(surfaceScale,diffuseConstant,kernelUnitLength).in(this)\n      },\n      // DisplacementMap effect\n      displacementMap: function(in2,scale,xChannelSelector,yChannelSelector){\n        return this.parent() && this.parent().displacementMap(this,in2,scale,xChannelSelector,yChannelSelector) //pass this as the first input\n      },\n      // SpecularLighting effect\n      specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){\n        return this.parent() && this.parent().specularLighting(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength).in(this)\n      },\n      // Tile effect\n      tile: function(){\n        return this.parent() && this.parent().tile().in(this)\n      },\n      // Turbulence effect\n      turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){\n        return this.parent() && this.parent().turbulence(baseFrequency,numOctaves,seed,stitchTiles,type).in(this)\n      }\n    };\n    SVG.extend(SVG.Effect,chainingEffects);\n    SVG.extend(SVG.ParentEffect,chainingEffects);\n\n    //crea class for child effects, like MergeNode, FuncR and lights\n    SVG.ChildEffect = SVG.invent({\n      create: function(){\n        this.constructor.call(this);\n      },\n      inherit: SVG.Element,\n      extend: {\n      in: function(effect){\n        this.attr('in',effect);\n      }\n      //dont include any \"result\" functions because these types of nodes dont have them\n      }\n    });\n\n    // Create all different effects\n    var effects = {\n      blend: function(in1,in2,mode){\n        this.attr({\n          in: in1,\n          in2: in2,\n          mode: mode || 'normal'\n        });\n      },\n      colorMatrix: function(type,values){\n        if (type == 'matrix')\n          values = normaliseMatrix(values);\n\n        this.attr({\n          type:   type\n        , values: typeof values == 'undefined' ? null : values\n        });\n      },\n      convolveMatrix: function(matrix){\n        matrix = normaliseMatrix(matrix);\n\n        this.attr({\n          order:        Math.sqrt(matrix.split(' ').length)\n        , kernelMatrix: matrix\n        });\n      },\n      composite: function(in1, in2, operator){\n        this.attr({\n          in: in1,\n          in2: in2,\n          operator: operator\n        });\n      },\n      flood: function(color,opacity){\n        this.attr('flood-color',color);\n        if(opacity != null) this.attr('flood-opacity',opacity);\n      },\n      offset: function(x,y){\n        this.attr({\n          dx: x,\n          dy: y\n        });\n      },\n      image: function(src){\n        this.attr('href', src, SVG.xlink);\n      },\n      displacementMap: function(in1,in2,scale,xChannelSelector,yChannelSelector){\n        this.attr({\n          in: in1,\n          in2: in2,\n          scale: scale,\n          xChannelSelector: xChannelSelector,\n          yChannelSelector: yChannelSelector\n        });\n      },\n      gaussianBlur: function(x,y){\n        if(x != null || y != null)\n          this.attr('stdDeviation', listString(Array.prototype.slice.call(arguments)));\n        else\n          this.attr('stdDeviation', '0 0');\n      },\n      morphology: function(operator,radius){\n        this.attr({\n          operator: operator,\n          radius: radius\n        });\n      },\n      tile: function(){\n\n      },\n      turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){\n        this.attr({\n          numOctaves: numOctaves,\n          seed: seed,\n          stitchTiles: stitchTiles,\n          baseFrequency: baseFrequency,\n          type: type\n        });\n      }\n    };\n\n    // Create all parent effects\n    var parentEffects = {\n      merge: function(){\n        var children;\n\n        //test to see if we have a set\n        if(arguments[0] instanceof SVG.Set){\n          var that = this;\n          arguments[0].each(function(i){\n            if(this instanceof SVG.MergeNode)\n              that.put(this);\n            else if(this instanceof SVG.Effect || this instanceof SVG.ParentEffect)\n              that.put(new SVG.MergeNode(this));\n          });\n        }\n        else {\n          //if the first argument is an array use it\n          if(Array.isArray(arguments[0]))\n            children = arguments[0];\n          else\n            children = arguments;\n\n          for(var i = 0; i < children.length; i++){\n            if(children[i] instanceof SVG.MergeNode){\n              this.put(children[i]);\n            }\n            else this.put(new SVG.MergeNode(children[i]));\n          }\n        }\n      },\n      componentTransfer: function(compontents){\n        /* create rgb set */\n        this.rgb = new SVG.Set\n\n        /* create components */\n        ;(['r', 'g', 'b', 'a']).forEach(function(c) {\n          /* create component */\n          this[c] = new SVG['Func' + c.toUpperCase()]('identity');\n\n          /* store component in set */\n          this.rgb.add(this[c]);\n\n          /* add component node */\n          this.node.appendChild(this[c].node);\n        }.bind(this)); //lost context in foreach\n\n        /* set components */\n        if (compontents) {\n          if (compontents.rgb) {\n  (['r', 'g', 'b']).forEach(function(c) {\n              this[c].attr(compontents.rgb);\n            }.bind(this));\n\n            delete compontents.rgb;\n          }\n\n          /* set individual components */\n          for (var c in compontents)\n            this[c].attr(compontents[c]);\n        }\n      },\n      diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){\n        this.attr({\n          surfaceScale: surfaceScale,\n          diffuseConstant: diffuseConstant,\n          kernelUnitLength: kernelUnitLength\n        });\n      },\n      specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){\n        this.attr({\n          surfaceScale: surfaceScale,\n          diffuseConstant: diffuseConstant,\n          specularExponent: specularExponent,\n          kernelUnitLength: kernelUnitLength\n        });\n      },\n    };\n\n    // Create child effects like PointLight and MergeNode\n    var childEffects = {\n      distantLight: function(azimuth, elevation){\n        this.attr({\n          azimuth: azimuth,\n          elevation: elevation\n        });\n      },\n      pointLight: function(x,y,z){\n        this.attr({\n          x: x,\n          y: y,\n          z: z\n        });\n      },\n      spotLight: function(x,y,z,pointsAtX,pointsAtY,pointsAtZ){\n        this.attr({\n          x: x,\n          y: y,\n          z: z,\n          pointsAtX: pointsAtX,\n          pointsAtY: pointsAtY,\n          pointsAtZ: pointsAtZ\n        });\n      },\n      mergeNode: function(in1){\n        this.attr('in',in1);\n      }\n    }\n\n    // Create compontent functions\n    ;(['r', 'g', 'b', 'a']).forEach(function(c) {\n      /* create class */\n      childEffects['Func' + c.toUpperCase()] = function(type) {\n        this.attr('type',type);\n\n        // take diffent arguments based on the type\n        switch(type){\n          case 'table':\n            this.attr('tableValues',arguments[1]);\n            break\n          case 'linear':\n            this.attr('slope',arguments[1]);\n            this.attr('intercept',arguments[2]);\n            break\n          case 'gamma':\n            this.attr('amplitude',arguments[1]);\n            this.attr('exponent',arguments[2]);\n            this.attr('offset',arguments[2]);\n            break\n        }\n      };\n    });\n\n    //create effects\n    foreach(effects,function(effect,i){\n\n      /* capitalize name */\n      var name = i.charAt(0).toUpperCase() + i.slice(1);\n      var proto = {};\n\n      /* create class */\n      SVG[name + 'Effect'] = SVG.invent({\n        create: function() {\n          //call super\n          this.constructor.call(this, SVG.create('fe' + name));\n\n          //call constructor for this effect\n          effect.apply(this,arguments);\n\n          //set the result\n          this.result(this.attr('id') + 'Out');\n        },\n        inherit: SVG.Effect,\n        extend: proto\n      });\n    });\n\n    //create parent effects\n    foreach(parentEffects,function(effect,i){\n\n      /* capitalize name */\n      var name = i.charAt(0).toUpperCase() + i.slice(1);\n      var proto = {};\n\n      /* create class */\n      SVG[name + 'Effect'] = SVG.invent({\n        create: function() {\n          //call super\n          this.constructor.call(this, SVG.create('fe' + name));\n\n          //call constructor for this effect\n          effect.apply(this,arguments);\n\n          //set the result\n          this.result(this.attr('id') + 'Out');\n        },\n        inherit: SVG.ParentEffect,\n        extend: proto\n      });\n    });\n\n    //create child effects\n    foreach(childEffects,function(effect,i){\n\n      /* capitalize name */\n      var name = i.charAt(0).toUpperCase() + i.slice(1);\n      var proto = {};\n\n      /* create class */\n      SVG[name] = SVG.invent({\n        create: function() {\n          //call super\n          this.constructor.call(this, SVG.create('fe' + name));\n\n          //call constructor for this effect\n          effect.apply(this,arguments);\n        },\n        inherit: SVG.ChildEffect,\n        extend: proto\n      });\n    });\n\n    // Effect-specific extensions\n    SVG.extend(SVG.MergeEffect,{\n      in: function(effect){\n        if(effect instanceof SVG.MergeNode)\n          this.add(effect,0);\n        else\n          this.add(new SVG.MergeNode(effect),0);\n\n        return this\n      }\n    });\n    SVG.extend(SVG.CompositeEffect,SVG.BlendEffect,SVG.DisplacementMapEffect,{\n      in2: function(effect){\n          return effect == null? this.parent() && this.parent().select('[result=\"'+this.attr('in2')+'\"]').get(0) || this.attr('in2') : this.attr('in2', effect)\n      }\n    });\n\n    // Presets\n    SVG.filter = {\n      sepiatone:  [ .343, .669, .119, 0, 0\n                  , .249, .626, .130, 0, 0\n                  , .172, .334, .111, 0, 0\n                  , .000, .000, .000, 1, 0 ]\n    };\n\n    // Helpers\n    function normaliseMatrix(matrix) {\n      /* convert possible array value to string */\n      if (Array.isArray(matrix))\n        matrix = new SVG.Array(matrix);\n\n      /* ensure there are no leading, tailing or double spaces */\n      return matrix.toString().replace(/^\\s+/, '').replace(/\\s+$/, '').replace(/\\s+/g, ' ')\n    }\n\n    function listString(list) {\n      if (!Array.isArray(list))\n        return list\n\n      for (var i = 0, l = list.length, s = []; i < l; i++)\n        s.push(list[i]);\n\n      return s.join(' ')\n    }\n\n    function foreach(){ //loops through mutiple objects\n      var fn = function(){};\n      if(typeof arguments[arguments.length-1] == 'function'){\n        fn = arguments[arguments.length-1];\n        Array.prototype.splice.call(arguments,arguments.length-1,1);\n      }\n      for(var k in arguments){\n        for(var i in arguments[k]){\n          fn(arguments[k][i],i,arguments[k]);\n        }\n      }\n    }\n\n  }).call(undefined);\n\n  (function() {\n\n  SVG.extend(SVG.PathArray, {\n    morph: function(array) {\n\n      var startArr = this.value\n        ,  destArr = this.parse(array);\n\n      var startOffsetM = 0\n        ,  destOffsetM = 0;\n\n      var startOffsetNextM = false\n        ,  destOffsetNextM = false;\n\n      while(true){\n        // stop if there is no M anymore\n        if(startOffsetM === false && destOffsetM === false) break\n\n        // find the next M in path array\n        startOffsetNextM = findNextM(startArr, startOffsetM === false ? false : startOffsetM+1);\n         destOffsetNextM = findNextM( destArr,  destOffsetM === false ? false :  destOffsetM+1);\n\n        // We have to add one M to the startArray\n        if(startOffsetM === false){\n          var bbox = new SVG.PathArray(result.start).bbox();\n\n          // when the last block had no bounding box we simply take the first M we got\n          if(bbox.height == 0 || bbox.width == 0){\n            startOffsetM =  startArr.push(startArr[0]) - 1;\n          }else {\n            // we take the middle of the bbox instead when we got one\n            startOffsetM = startArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1;\n          }\n        }\n\n        // We have to add one M to the destArray\n        if( destOffsetM === false){\n          var bbox = new SVG.PathArray(result.dest).bbox();\n\n          if(bbox.height == 0 || bbox.width == 0){\n            destOffsetM =  destArr.push(destArr[0]) - 1;\n          }else {\n            destOffsetM =  destArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1;\n          }\n        }\n\n        // handle block from M to next M\n        var result = handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM);\n\n        // update the arrays to their new values\n        startArr = startArr.slice(0, startOffsetM).concat(result.start, startOffsetNextM === false ? [] : startArr.slice(startOffsetNextM));\n         destArr =  destArr.slice(0,  destOffsetM).concat(result.dest ,  destOffsetNextM === false ? [] :  destArr.slice( destOffsetNextM));\n\n        // update offsets\n        startOffsetM = startOffsetNextM === false ? false : startOffsetM + result.start.length;\n         destOffsetM =  destOffsetNextM === false ? false :  destOffsetM + result.dest.length;\n\n      }\n\n      // copy back arrays\n      this.value = startArr;\n      this.destination = new SVG.PathArray();\n      this.destination.value = destArr;\n\n      return this\n    }\n  });\n\n\n\n  // sorry for the long declaration\n  // slices out one block (from M to M) and syncronize it so the types and length match\n  function handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM, undefined$1){\n\n    // slice out the block we need\n    var startArrTemp = startArr.slice(startOffsetM, startOffsetNextM || undefined$1)\n      ,  destArrTemp =  destArr.slice( destOffsetM,  destOffsetNextM || undefined$1);\n\n    var i = 0\n      , posStart = {pos:[0,0], start:[0,0]}\n      , posDest  = {pos:[0,0], start:[0,0]};\n\n    do{\n\n      // convert shorthand types to long form\n      startArrTemp[i] = simplyfy.call(posStart, startArrTemp[i]);\n       destArrTemp[i] = simplyfy.call(posDest ,  destArrTemp[i]);\n\n      // check if both shape types match\n      // 2 elliptical arc curve commands ('A'), are considered different if the\n      // flags (large-arc-flag, sweep-flag) don't match\n      if(startArrTemp[i][0] != destArrTemp[i][0] || startArrTemp[i][0] == 'M' ||\n          (startArrTemp[i][0] == 'A' &&\n            (startArrTemp[i][4] != destArrTemp[i][4] || startArrTemp[i][5] != destArrTemp[i][5])\n          )\n        ) {\n\n        // if not, convert shapes to beziere\n        Array.prototype.splice.apply(startArrTemp, [i, 1].concat(toBeziere.call(posStart, startArrTemp[i])));\n         Array.prototype.splice.apply(destArrTemp, [i, 1].concat(toBeziere.call(posDest, destArrTemp[i])));\n\n      } else {\n\n        // only update positions otherwise\n        startArrTemp[i] = setPosAndReflection.call(posStart, startArrTemp[i]);\n         destArrTemp[i] = setPosAndReflection.call(posDest ,  destArrTemp[i]);\n\n      }\n\n      // we are at the end at both arrays. stop here\n      if(++i == startArrTemp.length && i == destArrTemp.length) break\n\n      // destArray is longer. Add one element\n      if(i == startArrTemp.length){\n        startArrTemp.push([\n          'C',\n          posStart.pos[0],\n          posStart.pos[1],\n          posStart.pos[0],\n          posStart.pos[1],\n          posStart.pos[0],\n          posStart.pos[1],\n        ]);\n      }\n\n      // startArr is longer. Add one element\n      if(i == destArrTemp.length){\n        destArrTemp.push([\n          'C',\n          posDest.pos[0],\n          posDest.pos[1],\n          posDest.pos[0],\n          posDest.pos[1],\n          posDest.pos[0],\n          posDest.pos[1]\n        ]);\n      }\n\n\n    }while(true)\n\n    // return the updated block\n    return {start:startArrTemp, dest:destArrTemp}\n  }\n\n  // converts shorthand types to long form\n  function simplyfy(val){\n\n    switch(val[0]){\n      case 'z': // shorthand line to start\n      case 'Z':\n        val[0] = 'L';\n        val[1] = this.start[0];\n        val[2] = this.start[1];\n        break\n      case 'H': // shorthand horizontal line\n        val[0] = 'L';\n        val[2] = this.pos[1];\n        break\n      case 'V': // shorthand vertical line\n        val[0] = 'L';\n        val[2] = val[1];\n        val[1] = this.pos[0];\n        break\n      case 'T': // shorthand quadratic beziere\n        val[0] = 'Q';\n        val[3] = val[1];\n        val[4] = val[2];\n        val[1] = this.reflection[1];\n        val[2] = this.reflection[0];\n        break\n      case 'S': // shorthand cubic beziere\n        val[0] = 'C';\n        val[6] = val[4];\n        val[5] = val[3];\n        val[4] = val[2];\n        val[3] = val[1];\n        val[2] = this.reflection[1];\n        val[1] = this.reflection[0];\n        break\n    }\n\n    return val\n\n  }\n\n  // updates reflection point and current position\n  function setPosAndReflection(val){\n\n    var len = val.length;\n\n    this.pos = [ val[len-2], val[len-1] ];\n\n    if('SCQT'.indexOf(val[0]) != -1)\n      this.reflection = [ 2 * this.pos[0] - val[len-4], 2 * this.pos[1] - val[len-3] ];\n\n    return val\n  }\n\n  // converts all types to cubic beziere\n  function toBeziere(val){\n    var retVal = [val];\n\n    switch(val[0]){\n      case 'M': // special handling for M\n        this.pos = this.start = [val[1], val[2]];\n        return retVal\n      case 'L':\n        val[5] = val[3] = val[1];\n        val[6] = val[4] = val[2];\n        val[1] = this.pos[0];\n        val[2] = this.pos[1];\n        break\n      case 'Q':\n        val[6] = val[4];\n        val[5] = val[3];\n        val[4] = val[4] * 1/3 + val[2] * 2/3;\n        val[3] = val[3] * 1/3 + val[1] * 2/3;\n        val[2] = this.pos[1] * 1/3 + val[2] * 2/3;\n        val[1] = this.pos[0] * 1/3 + val[1] * 2/3;\n        break\n      case 'A':\n        retVal = arcToBeziere(this.pos, val);\n        val = retVal[0];\n        break\n    }\n\n    val[0] = 'C';\n    this.pos = [val[5], val[6]];\n    this.reflection = [2 * val[5] - val[3], 2 * val[6] - val[4]];\n\n    return retVal\n\n  }\n\n  // finds the next position of type M\n  function findNextM(arr, offset){\n\n    if(offset === false) return false\n\n    for(var i = offset, len = arr.length;i < len;++i){\n\n      if(arr[i][0] == 'M') return i\n\n    }\n\n    return false\n  }\n\n\n\n  // Convert an arc segment into equivalent cubic Bezier curves\n  // Depending on the arc, up to 4 curves might be used to represent it since a\n  // curve gives a good approximation for only a quarter of an ellipse\n  // The curves are returned as an array of SVG curve commands:\n  // [ ['C', x1, y1, x2, y2, x, y] ... ]\n  function arcToBeziere(pos, val) {\n      // Parameters extraction, handle out-of-range parameters as specified in the SVG spec\n      // See: https://www.w3.org/TR/SVG11/implnote.html#ArcOutOfRangeParameters\n      var rx = Math.abs(val[1]), ry = Math.abs(val[2]), xAxisRotation = val[3] % 360\n        , largeArcFlag = val[4], sweepFlag = val[5], x = val[6], y = val[7]\n        , A = new SVG.Point(pos), B = new SVG.Point(x, y)\n        , primedCoord, lambda, mat, k, c, cSquare, t, O, OA, OB, tetaStart, tetaEnd\n        , deltaTeta, nbSectors, f, arcSegPoints, angle, sinAngle, cosAngle, pt, i, il\n        , retVal = [], x1, y1, x2, y2;\n\n      // Ensure radii are non-zero\n      if(rx === 0 || ry === 0 || (A.x === B.x && A.y === B.y)) {\n        // treat this arc as a straight line segment\n        return [['C', A.x, A.y, B.x, B.y, B.x, B.y]]\n      }\n\n      // Ensure radii are large enough using the algorithm provided in the SVG spec\n      // See: https://www.w3.org/TR/SVG11/implnote.html#ArcCorrectionOutOfRangeRadii\n      primedCoord = new SVG.Point((A.x-B.x)/2, (A.y-B.y)/2).transform(new SVG.Matrix().rotate(xAxisRotation));\n      lambda = (primedCoord.x * primedCoord.x) / (rx * rx) + (primedCoord.y * primedCoord.y) / (ry * ry);\n      if(lambda > 1) {\n        lambda = Math.sqrt(lambda);\n        rx = lambda*rx;\n        ry = lambda*ry;\n      }\n\n      // To simplify calculations, we make the arc part of a unit circle (rayon is 1) instead of an ellipse\n      mat = new SVG.Matrix().rotate(xAxisRotation).scale(1/rx, 1/ry).rotate(-xAxisRotation);\n      A = A.transform(mat);\n      B = B.transform(mat);\n\n      // Calculate the horizontal and vertical distance between the initial and final point of the arc\n      k = [B.x-A.x, B.y-A.y];\n\n      // Find the length of the chord formed by A and B\n      cSquare = k[0]*k[0] + k[1]*k[1];\n      c = Math.sqrt(cSquare);\n\n      // Calculate the ratios of the horizontal and vertical distance on the length of the chord\n      k[0] /= c;\n      k[1] /= c;\n\n      // Calculate the distance between the circle center and the chord midpoint\n      // using this formula: t = sqrt(r^2 - c^2 / 4)\n      // where t is the distance between the cirle center and the chord midpoint,\n      //       r is the rayon of the circle and c is the chord length\n      // From: http://www.ajdesigner.com/phpcircle/circle_segment_chord_t.php\n      // Because of the imprecision of floating point numbers, cSquare might end\n      // up being slightly above 4 which would result in a negative radicand\n      // To prevent that, a test is made before computing the square root\n      t = (cSquare < 4) ? Math.sqrt(1 - cSquare/4) : 0;\n\n      // For most situations, there are actually two different ellipses that\n      // satisfy the constraints imposed by the points A and B, the radii rx and ry,\n      // and the xAxisRotation\n      // When the flags largeArcFlag and sweepFlag are equal, it means that the\n      // second ellipse is used as a solution\n      // See: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands\n      if(largeArcFlag === sweepFlag) {\n          t *= -1;\n      }\n\n      // Calculate the coordinates of the center of the circle from the midpoint of the chord\n      // This is done by multiplying the ratios calculated previously by the distance between\n      // the circle center and the chord midpoint and using these values to go from the midpoint\n      // to the center of the circle\n      // The negative of the vertical distance ratio is used to modify the x coordinate while\n      // the horizontal distance ratio is used to modify the y coordinate\n      // That is because the center of the circle is perpendicular to the chord and perpendicular\n      // lines are negative reciprocals\n      O = new SVG.Point((B.x+A.x)/2 + t*-k[1], (B.y+A.y)/2 + t*k[0]);\n      // Move the center of the circle at the origin\n      OA = new SVG.Point(A.x-O.x, A.y-O.y);\n      OB = new SVG.Point(B.x-O.x, B.y-O.y);\n\n      // Calculate the start and end angle\n      tetaStart = Math.acos(OA.x/Math.sqrt(OA.x*OA.x + OA.y*OA.y));\n      if (OA.y < 0) {\n        tetaStart *= -1;\n      }\n      tetaEnd = Math.acos(OB.x/Math.sqrt(OB.x*OB.x + OB.y*OB.y));\n      if (OB.y < 0) {\n        tetaEnd *= -1;\n      }\n\n      // If sweep-flag is '1', then the arc will be drawn in a \"positive-angle\" direction,\n      // make sure that the end angle is above the start angle\n      if (sweepFlag && tetaStart > tetaEnd) {\n        tetaEnd += 2*Math.PI;\n      }\n      // If sweep-flag is '0', then the arc will be drawn in a \"negative-angle\" direction,\n      // make sure that the end angle is below the start angle\n      if (!sweepFlag && tetaStart < tetaEnd) {\n        tetaEnd -= 2*Math.PI;\n      }\n\n      // Find the number of Bezier curves that are required to represent the arc\n      // A cubic Bezier curve gives a good enough approximation when representing at most a quarter of a circle\n      nbSectors = Math.ceil(Math.abs(tetaStart-tetaEnd) * 2/Math.PI);\n\n      // Calculate the coordinates of the points of all the Bezier curves required to represent the arc\n      // For an in-depth explanation of this part see: http://pomax.github.io/bezierinfo/#circles_cubic\n      arcSegPoints = [];\n      angle = tetaStart;\n      deltaTeta = (tetaEnd-tetaStart)/nbSectors;\n      f = 4*Math.tan(deltaTeta/4)/3;\n      for (i = 0; i <= nbSectors; i++) { // The <= is because a Bezier curve have a start and a endpoint\n        cosAngle = Math.cos(angle);\n        sinAngle = Math.sin(angle);\n\n        pt = new SVG.Point(O.x+cosAngle, O.y+sinAngle);\n        arcSegPoints[i] = [new SVG.Point(pt.x+f*sinAngle, pt.y-f*cosAngle), pt, new SVG.Point(pt.x-f*sinAngle, pt.y+f*cosAngle)];\n\n        angle += deltaTeta;\n      }\n\n      // Remove the first control point of the first segment point and remove the second control point of the last segment point\n      // These two control points are not used in the approximation of the arc, that is why they are removed\n      arcSegPoints[0][0] = arcSegPoints[0][1].clone();\n      arcSegPoints[arcSegPoints.length-1][2] = arcSegPoints[arcSegPoints.length-1][1].clone();\n\n      // Revert the transformation that was applied to make the arc part of a unit circle instead of an ellipse\n      mat = new SVG.Matrix().rotate(xAxisRotation).scale(rx, ry).rotate(-xAxisRotation);\n      for (i = 0, il = arcSegPoints.length; i < il; i++) {\n        arcSegPoints[i][0] = arcSegPoints[i][0].transform(mat);\n        arcSegPoints[i][1] = arcSegPoints[i][1].transform(mat);\n        arcSegPoints[i][2] = arcSegPoints[i][2].transform(mat);\n      }\n\n\n      // Convert the segments points to SVG curve commands\n      for (i = 1, il = arcSegPoints.length; i < il; i++) {\n        pt = arcSegPoints[i-1][2];\n        x1 = pt.x;\n        y1 = pt.y;\n\n        pt = arcSegPoints[i][0];\n        x2 = pt.x;\n        y2 = pt.y;\n\n        pt = arcSegPoints[i][1];\n        x = pt.x;\n        y = pt.y;\n\n        retVal.push(['C', x1, y1, x2, y2, x, y]);\n      }\n\n      return retVal\n  }\n  }());\n\n  /*! svg.draggable.js - v2.2.2 - 2019-01-08\n  * https://github.com/svgdotjs/svg.draggable.js\n  * Copyright (c) 2019 Wout Fierens; Licensed MIT */\n  (function() {\n\n    // creates handler, saves it\n    function DragHandler(el){\n      el.remember('_draggable', this);\n      this.el = el;\n    }\n\n\n    // Sets new parameter, starts dragging\n    DragHandler.prototype.init = function(constraint, val){\n      var _this = this;\n      this.constraint = constraint;\n      this.value = val;\n      this.el.on('mousedown.drag', function(e){ _this.start(e); });\n      this.el.on('touchstart.drag', function(e){ _this.start(e); });\n    };\n\n    // transforms one point from screen to user coords\n    DragHandler.prototype.transformPoint = function(event, offset){\n        event = event || window.event;\n        var touches = event.changedTouches && event.changedTouches[0] || event;\n        this.p.x = touches.clientX - (offset || 0);\n        this.p.y = touches.clientY;\n        return this.p.matrixTransform(this.m)\n    };\n\n    // gets elements bounding box with special handling of groups, nested and use\n    DragHandler.prototype.getBBox = function(){\n\n      var box = this.el.bbox();\n\n      if(this.el instanceof SVG.Nested) box = this.el.rbox();\n\n      if (this.el instanceof SVG.G || this.el instanceof SVG.Use || this.el instanceof SVG.Nested) {\n        box.x = this.el.x();\n        box.y = this.el.y();\n      }\n\n      return box\n    };\n\n    // start dragging\n    DragHandler.prototype.start = function(e){\n\n      // check for left button\n      if(e.type == 'click'|| e.type == 'mousedown' || e.type == 'mousemove'){\n        if((e.which || e.buttons) != 1){\n            return\n        }\n      }\n\n      var _this = this;\n\n      // fire beforedrag event\n      this.el.fire('beforedrag', { event: e, handler: this });\n      if(this.el.event().defaultPrevented) return;\n\n      // prevent browser drag behavior as soon as possible\n      e.preventDefault();\n\n      // prevent propagation to a parent that might also have dragging enabled\n      e.stopPropagation();\n\n      // search for parent on the fly to make sure we can call\n      // draggable() even when element is not in the dom currently\n      this.parent = this.parent || this.el.parent(SVG.Nested) || this.el.parent(SVG.Doc);\n      this.p = this.parent.node.createSVGPoint();\n\n      // save current transformation matrix\n      this.m = this.el.node.getScreenCTM().inverse();\n\n      var box = this.getBBox();\n\n      var anchorOffset;\n\n      // fix text-anchor in text-element (#37)\n      if(this.el instanceof SVG.Text){\n        anchorOffset = this.el.node.getComputedTextLength();\n\n        switch(this.el.attr('text-anchor')){\n          case 'middle':\n            anchorOffset /= 2;\n            break\n          case 'start':\n            anchorOffset = 0;\n            break;\n        }\n      }\n\n      this.startPoints = {\n        // We take absolute coordinates since we are just using a delta here\n        point: this.transformPoint(e, anchorOffset),\n        box:   box,\n        transform: this.el.transform()\n      };\n\n      // add drag and end events to window\n      SVG.on(window, 'mousemove.drag', function(e){ _this.drag(e); });\n      SVG.on(window, 'touchmove.drag', function(e){ _this.drag(e); });\n      SVG.on(window, 'mouseup.drag', function(e){ _this.end(e); });\n      SVG.on(window, 'touchend.drag', function(e){ _this.end(e); });\n\n      // fire dragstart event\n      this.el.fire('dragstart', {event: e, p: this.startPoints.point, m: this.m, handler: this});\n    };\n\n    // while dragging\n    DragHandler.prototype.drag = function(e){\n\n      var box = this.getBBox()\n        , p   = this.transformPoint(e)\n        , x   = this.startPoints.box.x + p.x - this.startPoints.point.x\n        , y   = this.startPoints.box.y + p.y - this.startPoints.point.y\n        , c   = this.constraint\n        , gx  = p.x - this.startPoints.point.x\n        , gy  = p.y - this.startPoints.point.y;\n\n      this.el.fire('dragmove', {\n          event: e\n        , p: p\n        , m: this.m\n        , handler: this\n      });\n\n      if(this.el.event().defaultPrevented) return p\n\n      // move the element to its new position, if possible by constraint\n      if (typeof c == 'function') {\n\n        var coord = c.call(this.el, x, y, this.m);\n\n        // bool, just show us if movement is allowed or not\n        if (typeof coord == 'boolean') {\n          coord = {\n            x: coord,\n            y: coord\n          };\n        }\n\n        // if true, we just move. If !false its a number and we move it there\n        if (coord.x === true) {\n          this.el.x(x);\n        } else if (coord.x !== false) {\n          this.el.x(coord.x);\n        }\n\n        if (coord.y === true) {\n          this.el.y(y);\n        } else if (coord.y !== false) {\n          this.el.y(coord.y);\n        }\n\n      } else if (typeof c == 'object') {\n\n        // keep element within constrained box\n        if (c.minX != null && x < c.minX) {\n          x = c.minX;\n          gx = x - this.startPoints.box.x;\n        } else if (c.maxX != null && x > c.maxX - box.width) {\n          x = c.maxX - box.width;\n          gx = x - this.startPoints.box.x;\n        } if (c.minY != null && y < c.minY) {\n          y = c.minY;\n          gy = y - this.startPoints.box.y;\n        } else if (c.maxY != null && y > c.maxY - box.height) {\n          y = c.maxY - box.height;\n          gy = y - this.startPoints.box.y;\n        }\n\n        if (c.snapToGrid != null) {\n          x = x - (x % c.snapToGrid);\n          y = y - (y % c.snapToGrid);\n          gx = gx - (gx % c.snapToGrid);\n          gy = gy - (gy % c.snapToGrid);\n        }\n\n        if(this.el instanceof SVG.G)\n          this.el.matrix(this.startPoints.transform).transform({x:gx, y: gy}, true);\n        else\n          this.el.move(x, y);\n      }\n\n      // so we can use it in the end-method, too\n      return p\n    };\n\n    DragHandler.prototype.end = function(e){\n\n      // final drag\n      var p = this.drag(e);\n\n      // fire dragend event\n      this.el.fire('dragend', { event: e, p: p, m: this.m, handler: this });\n\n      // unbind events\n      SVG.off(window, 'mousemove.drag');\n      SVG.off(window, 'touchmove.drag');\n      SVG.off(window, 'mouseup.drag');\n      SVG.off(window, 'touchend.drag');\n\n    };\n\n    SVG.extend(SVG.Element, {\n      // Make element draggable\n      // Constraint might be an object (as described in readme.md) or a function in the form \"function (x, y)\" that gets called before every move.\n      // The function can return a boolean or an object of the form {x, y}, to which the element will be moved. \"False\" skips moving, true moves to raw x, y.\n      draggable: function(value, constraint) {\n\n        // Check the parameters and reassign if needed\n        if (typeof value == 'function' || typeof value == 'object') {\n          constraint = value;\n          value = true;\n        }\n\n        var dragHandler = this.remember('_draggable') || new DragHandler(this);\n\n        // When no parameter is given, value is true\n        value = typeof value === 'undefined' ? true : value;\n\n        if(value) dragHandler.init(constraint || {}, value);\n        else {\n          this.off('mousedown.drag');\n          this.off('touchstart.drag');\n        }\n\n        return this\n      }\n\n    });\n\n  }).call(undefined);\n\n  (function() {\n\n  function SelectHandler(el) {\n\n      this.el = el;\n      el.remember('_selectHandler', this);\n      this.pointSelection = {isSelected: false};\n      this.rectSelection = {isSelected: false};\n\n      // helper list with position settings of each type of point\n      this.pointsList = {\n        lt: [ 0, 0 ],\n        rt: [ 'width', 0 ],\n        rb: [ 'width', 'height' ],\n        lb: [ 0, 'height' ],\n        t: [ 'width', 0 ],\n        r: [ 'width', 'height' ],\n        b: [ 'width', 'height' ],\n        l: [ 0, 'height' ]\n      };\n\n      // helper function to get point coordinates based on settings above and an object (bbox in our case)\n      this.pointCoord = function (setting, object, isPointCentered) {\n        var coord = typeof setting !== 'string' ? setting : object[setting];\n        // Top, bottom, right and left points are placed in the center of element width/height\n        return isPointCentered ? coord / 2 : coord\n      };\n\n      this.pointCoords = function (point, object) {\n        var settings = this.pointsList[point];\n\n        return {\n          x: this.pointCoord(settings[0], object, (point === 't' || point === 'b')),\n          y: this.pointCoord(settings[1], object, (point === 'r' || point === 'l'))\n        }\n      };\n  }\n\n  SelectHandler.prototype.init = function (value, options) {\n\n      var bbox = this.el.bbox();\n      this.options = {};\n\n      // store defaults list of points in order to verify users config\n      var points = this.el.selectize.defaults.points;\n\n      // Merging the defaults and the options-object together\n      for (var i in this.el.selectize.defaults) {\n          this.options[i] = this.el.selectize.defaults[i];\n          if (options[i] !== undefined) {\n              this.options[i] = options[i];\n          }\n      }\n\n      // prepare & validate list of points to be added (or excluded)\n      var pointsLists = ['points', 'pointsExclude'];\n\n      for (var i in pointsLists) {\n        var option = this.options[pointsLists[i]];\n\n        if (typeof option === 'string') {\n          if (option.length > 0) {\n            // if set as comma separated string list => convert it into an array\n            option = option.split(/\\s*,\\s*/i);\n          } else {\n            option = [];\n          }\n        } else if (typeof option === 'boolean' && pointsLists[i] === 'points') {\n          // this is not needed, but let's have it for legacy support\n          option = option ? points : [];\n        }\n\n        this.options[pointsLists[i]] = option;\n      }\n\n      // intersect correct all points options with users config (exclude unwanted points)\n      // ES5 -> NO arrow functions nor Array.includes()\n      this.options.points = [ points, this.options.points ].reduce(\n        function (a, b) {\n          return a.filter(\n            function (c) {\n              return b.indexOf(c) > -1;\n            }\n          )\n        }\n      );\n\n      // exclude pointsExclude, if wanted\n      this.options.points = [ this.options.points, this.options.pointsExclude ].reduce(\n        function (a, b) {\n          return a.filter(\n            function (c) {\n              return b.indexOf(c) < 0;\n            }\n          )\n        }\n      );\n\n      this.parent = this.el.parent();\n      this.nested = (this.nested || this.parent.group());\n      this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y));\n\n      // When deepSelect is enabled and the element is a line/polyline/polygon, draw only points for moving\n      if (this.options.deepSelect && ['line', 'polyline', 'polygon'].indexOf(this.el.type) !== -1) {\n          this.selectPoints(value);\n      } else {\n          this.selectRect(value);\n      }\n\n      this.observe();\n      this.cleanup();\n\n  };\n\n  SelectHandler.prototype.selectPoints = function (value) {\n\n      this.pointSelection.isSelected = value;\n\n      // When set is already there we dont have to create one\n      if (this.pointSelection.set) {\n          return this;\n      }\n\n      // Create our set of elements\n      this.pointSelection.set = this.parent.set();\n      // draw the points and mark the element as selected\n      this.drawPoints();\n\n      return this;\n\n  };\n\n  // create the point-array which contains the 2 points of a line or simply the points-array of polyline/polygon\n  SelectHandler.prototype.getPointArray = function () {\n      var bbox = this.el.bbox();\n\n      return this.el.array().valueOf().map(function (el) {\n          return [el[0] - bbox.x, el[1] - bbox.y];\n      });\n  };\n\n  // Draws a points\n  SelectHandler.prototype.drawPoints = function () {\n\n      var _this = this, array = this.getPointArray();\n\n      // go through the array of points\n      for (var i = 0, len = array.length; i < len; ++i) {\n\n          var curriedEvent = (function (k) {\n              return function (ev) {\n                  ev = ev || window.event;\n                  ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;\n                  ev.stopPropagation();\n\n                  var x = ev.pageX || ev.touches[0].pageX;\n                  var y = ev.pageY || ev.touches[0].pageY;\n                  _this.el.fire('point', {x: x, y: y, i: k, event: ev});\n              };\n          })(i);\n\n          // add every point to the set\n          // add css-classes and a touchstart-event which fires our event for moving points\n          var point = this.drawPoint(array[i][0], array[i][1])\n                          .addClass(this.options.classPoints)\n                          .addClass(this.options.classPoints + '_point')\n                          .on('touchstart', curriedEvent)\n                          .on('mousedown', curriedEvent);\n          this.pointSelection.set.add(point);\n      }\n  };\n\n  // The function to draw single point\n  SelectHandler.prototype.drawPoint = function (cx, cy) {\n      var pointType = this.options.pointType;\n\n      switch (pointType) {\n          case 'circle':\n              return this.drawCircle(cx, cy);\n          case 'rect':\n              return this.drawRect(cx, cy);\n          default:\n              if (typeof pointType === 'function') {\n                  return pointType.call(this, cx, cy);\n              }\n\n              throw new Error('Unknown ' + pointType + ' point type!');\n      }\n  };\n\n  // The function to draw the circle point\n  SelectHandler.prototype.drawCircle = function (cx, cy) {\n      return this.nested.circle(this.options.pointSize)\n                        .center(cx, cy);\n  };\n\n  // The function to draw the rect point\n  SelectHandler.prototype.drawRect = function (cx, cy) {\n      return this.nested.rect(this.options.pointSize, this.options.pointSize)\n                        .center(cx, cy);\n  };\n\n  // every time a point is moved, we have to update the positions of our point\n  SelectHandler.prototype.updatePointSelection = function () {\n      var array = this.getPointArray();\n\n      this.pointSelection.set.each(function (i) {\n          if (this.cx() === array[i][0] && this.cy() === array[i][1]) {\n              return;\n          }\n          this.center(array[i][0], array[i][1]);\n      });\n  };\n\n  SelectHandler.prototype.updateRectSelection = function () {\n      var _this = this, bbox = this.el.bbox();\n\n      this.rectSelection.set.get(0).attr({\n          width: bbox.width,\n          height: bbox.height\n      });\n\n      // set.get(1) is always in the upper left corner. no need to move it\n      if (this.options.points.length) {\n        this.options.points.map(function (point, index) {\n          var coords = _this.pointCoords(point, bbox);\n\n          _this.rectSelection.set.get(index + 1).center(coords.x, coords.y);\n        });\n      }\n\n      if (this.options.rotationPoint) {\n          var length = this.rectSelection.set.length();\n\n          this.rectSelection.set.get(length - 1).center(bbox.width / 2, 20);\n      }\n  };\n\n  SelectHandler.prototype.selectRect = function (value) {\n\n      var _this = this, bbox = this.el.bbox();\n\n      this.rectSelection.isSelected = value;\n\n      // when set is already p\n      this.rectSelection.set = this.rectSelection.set || this.parent.set();\n\n      // helperFunction to create a mouse-down function which triggers the event specified in `eventName`\n      function getMoseDownFunc(eventName) {\n          return function (ev) {\n              ev = ev || window.event;\n              ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;\n              ev.stopPropagation();\n\n              var x = ev.pageX || ev.touches[0].pageX;\n              var y = ev.pageY || ev.touches[0].pageY;\n              _this.el.fire(eventName, {x: x, y: y, event: ev});\n          };\n      }\n\n      // create the selection-rectangle and add the css-class\n      if (!this.rectSelection.set.get(0)) {\n          this.rectSelection.set.add(this.nested.rect(bbox.width, bbox.height).addClass(this.options.classRect));\n      }\n\n      // Draw Points at the edges, if enabled\n      if (this.options.points.length && this.rectSelection.set.length() < 2) {\n          var ename =\"touchstart\", mname = \"mousedown\";\n\n          this.options.points.map(function (point, index) {\n              var coords = _this.pointCoords(point, bbox);\n\n              var pointElement = _this.drawPoint(coords.x, coords.y)\n                                      .attr('class', _this.options.classPoints + '_' + point)\n                                      .on(mname, getMoseDownFunc(point))\n                                      .on(ename, getMoseDownFunc(point));\n              _this.rectSelection.set.add(pointElement);\n          });\n\n          this.rectSelection.set.each(function () {\n              this.addClass(_this.options.classPoints);\n          });\n      }\n\n      // draw rotationPint, if enabled\n      if (this.options.rotationPoint && ((this.options.points && !this.rectSelection.set.get(9)) || (!this.options.points && !this.rectSelection.set.get(1)))) {\n\n          var curriedEvent = function (ev) {\n              ev = ev || window.event;\n              ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;\n              ev.stopPropagation();\n\n              var x = ev.pageX || ev.touches[0].pageX;\n              var y = ev.pageY || ev.touches[0].pageY;\n              _this.el.fire('rot', {x: x, y: y, event: ev});\n          };\n\n          var pointElement = this.drawPoint(bbox.width / 2, 20)\n                                .attr('class', this.options.classPoints + '_rot')\n                                .on(\"touchstart\", curriedEvent)\n                                .on(\"mousedown\", curriedEvent);\n          this.rectSelection.set.add(pointElement);\n      }\n\n  };\n\n  SelectHandler.prototype.handler = function () {\n\n      var bbox = this.el.bbox();\n      this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y));\n\n      if (this.rectSelection.isSelected) {\n          this.updateRectSelection();\n      }\n\n      if (this.pointSelection.isSelected) {\n          this.updatePointSelection();\n      }\n\n  };\n\n  SelectHandler.prototype.observe = function () {\n      var _this = this;\n\n      if (MutationObserver) {\n          if (this.rectSelection.isSelected || this.pointSelection.isSelected) {\n              this.observerInst = this.observerInst || new MutationObserver(function () {\n                  _this.handler();\n              });\n              this.observerInst.observe(this.el.node, {attributes: true});\n          } else {\n              try {\n                  this.observerInst.disconnect();\n                  delete this.observerInst;\n              } catch (e) {\n              }\n          }\n      } else {\n          this.el.off('DOMAttrModified.select');\n\n          if (this.rectSelection.isSelected || this.pointSelection.isSelected) {\n              this.el.on('DOMAttrModified.select', function () {\n                  _this.handler();\n              });\n          }\n      }\n  };\n\n  SelectHandler.prototype.cleanup = function () {\n\n      //var _this = this;\n\n      if (!this.rectSelection.isSelected && this.rectSelection.set) {\n          // stop watching the element, remove the selection\n          this.rectSelection.set.each(function () {\n              this.remove();\n          });\n\n          this.rectSelection.set.clear();\n          delete this.rectSelection.set;\n      }\n\n      if (!this.pointSelection.isSelected && this.pointSelection.set) {\n          // Remove all points, clear the set, stop watching the element\n          this.pointSelection.set.each(function () {\n              this.remove();\n          });\n\n          this.pointSelection.set.clear();\n          delete this.pointSelection.set;\n      }\n\n      if (!this.pointSelection.isSelected && !this.rectSelection.isSelected) {\n          this.nested.remove();\n          delete this.nested;\n\n      }\n  };\n\n\n  SVG.extend(SVG.Element, {\n      // Select element with mouse\n      selectize: function (value, options) {\n\n          // Check the parameters and reassign if needed\n          if (typeof value === 'object') {\n              options = value;\n              value = true;\n          }\n\n          var selectHandler = this.remember('_selectHandler') || new SelectHandler(this);\n\n          selectHandler.init(value === undefined ? true : value, options || {});\n\n          return this;\n\n      }\n  });\n\n  SVG.Element.prototype.selectize.defaults = {\n      points: ['lt', 'rt', 'rb', 'lb', 't', 'r', 'b', 'l'],    // which points to draw, default all\n      pointsExclude: [],                       // easier option if to exclude few than rewrite all\n      classRect: 'svg_select_boundingRect',    // Css-class added to the rect\n      classPoints: 'svg_select_points',        // Css-class added to the points\n      pointSize: 7,                            // size of point\n      rotationPoint: true,                     // If true, rotation point is drawn. Needed for rotation!\n      deepSelect: false,                       // If true, moving of single points is possible (only line, polyline, polyon)\n      pointType: 'circle'                      // Point type: circle or rect, default circle\n  };\n  }());\n\n  (function() {\n  (function () {\n\n      function ResizeHandler(el) {\n\n          el.remember('_resizeHandler', this);\n\n          this.el = el;\n          this.parameters = {};\n          this.lastUpdateCall = null;\n          this.p = el.doc().node.createSVGPoint();\n      }\n\n      ResizeHandler.prototype.transformPoint = function(x, y, m){\n\n          this.p.x = x - (this.offset.x - window.pageXOffset);\n          this.p.y = y - (this.offset.y - window.pageYOffset);\n\n          return this.p.matrixTransform(m || this.m);\n\n      };\n\n      ResizeHandler.prototype._extractPosition = function(event) {\n          // Extract a position from a mouse/touch event.\n          // Returns { x: .., y: .. }\n          return {\n              x: event.clientX != null ? event.clientX : event.touches[0].clientX,\n              y: event.clientY != null ? event.clientY : event.touches[0].clientY\n          }\n      };\n\n      ResizeHandler.prototype.init = function (options) {\n\n          var _this = this;\n\n          this.stop();\n\n          if (options === 'stop') {\n              return;\n          }\n\n          this.options = {};\n\n          // Merge options and defaults\n          for (var i in this.el.resize.defaults) {\n              this.options[i] = this.el.resize.defaults[i];\n              if (typeof options[i] !== 'undefined') {\n                  this.options[i] = options[i];\n              }\n          }\n\n          // We listen to all these events which are specifying different edges\n          this.el.on('lt.resize', function(e){ _this.resize(e || window.event); });  // Left-Top\n          this.el.on('rt.resize', function(e){ _this.resize(e || window.event); });  // Right-Top\n          this.el.on('rb.resize', function(e){ _this.resize(e || window.event); });  // Right-Bottom\n          this.el.on('lb.resize', function(e){ _this.resize(e || window.event); });  // Left-Bottom\n\n          this.el.on('t.resize', function(e){ _this.resize(e || window.event); });   // Top\n          this.el.on('r.resize', function(e){ _this.resize(e || window.event); });   // Right\n          this.el.on('b.resize', function(e){ _this.resize(e || window.event); });   // Bottom\n          this.el.on('l.resize', function(e){ _this.resize(e || window.event); });   // Left\n\n          this.el.on('rot.resize', function(e){ _this.resize(e || window.event); }); // Rotation\n\n          this.el.on('point.resize', function(e){ _this.resize(e || window.event); }); // Point-Moving\n\n          // This call ensures, that the plugin reacts to a change of snapToGrid immediately\n          this.update();\n\n      };\n\n      ResizeHandler.prototype.stop = function(){\n          this.el.off('lt.resize');\n          this.el.off('rt.resize');\n          this.el.off('rb.resize');\n          this.el.off('lb.resize');\n\n          this.el.off('t.resize');\n          this.el.off('r.resize');\n          this.el.off('b.resize');\n          this.el.off('l.resize');\n\n          this.el.off('rot.resize');\n\n          this.el.off('point.resize');\n\n          return this;\n      };\n\n      ResizeHandler.prototype.resize = function (event) {\n\n          var _this = this;\n\n          this.m = this.el.node.getScreenCTM().inverse();\n          this.offset = { x: window.pageXOffset, y: window.pageYOffset };\n\n          var txPt = this._extractPosition(event.detail.event);\n          this.parameters = {\n              type: this.el.type, // the type of element\n              p: this.transformPoint(txPt.x, txPt.y),\n              x: event.detail.x,      // x-position of the mouse when resizing started\n              y: event.detail.y,      // y-position of the mouse when resizing started\n              box: this.el.bbox(),    // The bounding-box of the element\n              rotation: this.el.transform().rotation  // The current rotation of the element\n          };\n\n          // Add font-size parameter if the element type is text\n          if (this.el.type === \"text\") {\n              this.parameters.fontSize = this.el.attr()[\"font-size\"];\n          }\n\n          // the i-param in the event holds the index of the point which is moved, when using `deepSelect`\n          if (event.detail.i !== undefined) {\n\n              // get the point array\n              var array = this.el.array().valueOf();\n\n              // Save the index and the point which is moved\n              this.parameters.i = event.detail.i;\n              this.parameters.pointCoords = [array[event.detail.i][0], array[event.detail.i][1]];\n          }\n\n          // Lets check which edge of the bounding-box was clicked and resize the this.el according to this\n          switch (event.type) {\n\n              // Left-Top-Edge\n              case 'lt':\n                  // We build a calculating function for every case which gives us the new position of the this.el\n                  this.calc = function (diffX, diffY) {\n                      // The procedure is always the same\n                      // First we snap the edge to the given grid (snapping to 1px grid is normal resizing)\n                      var snap = this.snapToGrid(diffX, diffY);\n\n                      // Now we check if the new height and width still valid (> 0)\n                      if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height - snap[1] > 0) {\n                          // ...if valid, we resize the this.el (which can include moving because the coord-system starts at the left-top and this edge is moving sometimes when resized)\n\n                          /*\n                           * but first check if the element is text box, so we can change the font size instead of\n                           * the width and height\n                           */\n\n                          if (this.parameters.type === \"text\") {\n                              this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y);\n                              this.el.attr(\"font-size\", this.parameters.fontSize - snap[0]);\n                              return;\n                          }\n\n                          snap = this.checkAspectRatio(snap);\n\n                          this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y + snap[1]).size(this.parameters.box.width - snap[0], this.parameters.box.height - snap[1]);\n                      }\n                  };\n                  break;\n\n              // Right-Top\n              case 'rt':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n                      var snap = this.snapToGrid(diffX, diffY, 1 << 1);\n                      if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height - snap[1] > 0) {\n                          if (this.parameters.type === \"text\") {\n                              this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y);\n                              this.el.attr(\"font-size\", this.parameters.fontSize + snap[0]);\n                              return;\n                          }\n\n                          snap = this.checkAspectRatio(snap, true);\n\n                          this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).size(this.parameters.box.width + snap[0], this.parameters.box.height - snap[1]);\n                      }\n                  };\n                  break;\n\n              // Right-Bottom\n              case 'rb':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n                      var snap = this.snapToGrid(diffX, diffY, 0);\n                      if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height + snap[1] > 0) {\n                          if (this.parameters.type === \"text\") {\n                              this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y);\n                              this.el.attr(\"font-size\", this.parameters.fontSize + snap[0]);\n                              return;\n                          }\n\n                          snap = this.checkAspectRatio(snap);\n\n                          this.el.move(this.parameters.box.x, this.parameters.box.y).size(this.parameters.box.width + snap[0], this.parameters.box.height + snap[1]);\n                      }\n                  };\n                  break;\n\n              // Left-Bottom\n              case 'lb':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n                      var snap = this.snapToGrid(diffX, diffY, 1);\n                      if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height + snap[1] > 0) {\n                          if (this.parameters.type === \"text\") {\n                              this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y);\n                              this.el.attr(\"font-size\", this.parameters.fontSize - snap[0]);\n                              return;\n                          }\n\n                          snap = this.checkAspectRatio(snap, true);\n\n                          this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).size(this.parameters.box.width - snap[0], this.parameters.box.height + snap[1]);\n                      }\n                  };\n                  break;\n\n              // Top\n              case 't':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n                      var snap = this.snapToGrid(diffX, diffY, 1 << 1);\n                      if (this.parameters.box.height - snap[1] > 0) {\n                          // Disable the font-resizing if it is not from the corner of bounding-box\n                          if (this.parameters.type === \"text\") {\n                              return;\n                          }\n\n                          this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).height(this.parameters.box.height - snap[1]);\n                      }\n                  };\n                  break;\n\n              // Right\n              case 'r':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n                      var snap = this.snapToGrid(diffX, diffY, 0);\n                      if (this.parameters.box.width + snap[0] > 0) {\n                          if (this.parameters.type === \"text\") {\n                              return;\n                          }\n\n                          this.el.move(this.parameters.box.x, this.parameters.box.y).width(this.parameters.box.width + snap[0]);\n                      }\n                  };\n                  break;\n\n              // Bottom\n              case 'b':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n                      var snap = this.snapToGrid(diffX, diffY, 0);\n                      if (this.parameters.box.height + snap[1] > 0) {\n                          if (this.parameters.type === \"text\") {\n                              return;\n                          }\n\n                          this.el.move(this.parameters.box.x, this.parameters.box.y).height(this.parameters.box.height + snap[1]);\n                      }\n                  };\n                  break;\n\n              // Left\n              case 'l':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n                      var snap = this.snapToGrid(diffX, diffY, 1);\n                      if (this.parameters.box.width - snap[0] > 0) {\n                          if (this.parameters.type === \"text\") {\n                              return;\n                          }\n\n                          this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).width(this.parameters.box.width - snap[0]);\n                      }\n                  };\n                  break;\n\n              // Rotation\n              case 'rot':\n                  // s.a.\n                  this.calc = function (diffX, diffY) {\n\n                      // yes this is kinda stupid but we need the mouse coords back...\n                      var current = {x: diffX + this.parameters.p.x, y: diffY + this.parameters.p.y};\n\n                      // start minus middle\n                      var sAngle = Math.atan2((this.parameters.p.y - this.parameters.box.y - this.parameters.box.height / 2), (this.parameters.p.x - this.parameters.box.x - this.parameters.box.width / 2));\n\n                      // end minus middle\n                      var pAngle = Math.atan2((current.y - this.parameters.box.y - this.parameters.box.height / 2), (current.x - this.parameters.box.x - this.parameters.box.width / 2));\n\n                      var angle = this.parameters.rotation + (pAngle - sAngle) * 180 / Math.PI + this.options.snapToAngle / 2;\n\n                      // We have to move the element to the center of the box first and change the rotation afterwards\n                      // because rotation always works around a rotation-center, which is changed when moving the element\n                      // We also set the new rotation center to the center of the box.\n                      this.el.center(this.parameters.box.cx, this.parameters.box.cy).rotate(angle - (angle % this.options.snapToAngle), this.parameters.box.cx, this.parameters.box.cy);\n                  };\n                  break;\n\n              // Moving one single Point (needed when an element is deepSelected which means you can move every single point of the object)\n              case 'point':\n                  this.calc = function (diffX, diffY) {\n\n                      // Snapping the point to the grid\n                      var snap = this.snapToGrid(diffX, diffY, this.parameters.pointCoords[0], this.parameters.pointCoords[1]);\n\n                      // Get the point array\n                      var array = this.el.array().valueOf();\n\n                      // Changing the moved point in the array\n                      array[this.parameters.i][0] = this.parameters.pointCoords[0] + snap[0];\n                      array[this.parameters.i][1] = this.parameters.pointCoords[1] + snap[1];\n\n                      // And plot the new this.el\n                      this.el.plot(array);\n                  };\n          }\n\n          this.el.fire('resizestart', {dx: this.parameters.x, dy: this.parameters.y, event: event});\n          // When resizing started, we have to register events for...\n          // Touches.\n          SVG.on(window, 'touchmove.resize', function(e) {\n              _this.update(e || window.event);\n          });\n          SVG.on(window, 'touchend.resize', function() {\n              _this.done();\n          });\n          // Mouse.\n          SVG.on(window, 'mousemove.resize', function (e) {\n              _this.update(e || window.event);\n          });\n          SVG.on(window, 'mouseup.resize', function () {\n              _this.done();\n          });\n\n      };\n\n      // The update-function redraws the element every time the mouse is moving\n      ResizeHandler.prototype.update = function (event) {\n\n          if (!event) {\n              if (this.lastUpdateCall) {\n                  this.calc(this.lastUpdateCall[0], this.lastUpdateCall[1]);\n              }\n              return;\n          }\n\n          // Calculate the difference between the mouseposition at start and now\n          var txPt = this._extractPosition(event);\n          var p = this.transformPoint(txPt.x, txPt.y);\n\n          var diffX = p.x - this.parameters.p.x,\n              diffY = p.y - this.parameters.p.y;\n\n          this.lastUpdateCall = [diffX, diffY];\n\n          // Calculate the new position and height / width of the element\n          this.calc(diffX, diffY);\n\n         // Emit an event to say we have changed.\n          this.el.fire('resizing', {dx: diffX, dy: diffY, event: event});\n      };\n\n      // Is called on mouseup.\n      // Removes the update-function from the mousemove event\n      ResizeHandler.prototype.done = function () {\n          this.lastUpdateCall = null;\n          SVG.off(window, 'mousemove.resize');\n          SVG.off(window, 'mouseup.resize');\n          SVG.off(window, 'touchmove.resize');\n          SVG.off(window, 'touchend.resize');\n          this.el.fire('resizedone');\n      };\n\n      // The flag is used to determine whether the resizing is used with a left-Point (first bit) and top-point (second bit)\n      // In this cases the temp-values are calculated differently\n      ResizeHandler.prototype.snapToGrid = function (diffX, diffY, flag, pointCoordsY) {\n\n          var temp;\n\n          // If `pointCoordsY` is given, a single Point has to be snapped (deepSelect). That's why we need a different temp-value\n          if (typeof pointCoordsY !== 'undefined') {\n              // Note that flag = pointCoordsX in this case\n              temp = [(flag + diffX) % this.options.snapToGrid, (pointCoordsY + diffY) % this.options.snapToGrid];\n          } else {\n              // We check if the flag is set and if not we set a default-value (both bits set - which means upper-left-edge)\n              flag = flag == null ? 1 | 1 << 1 : flag;\n              temp = [(this.parameters.box.x + diffX + (flag & 1 ? 0 : this.parameters.box.width)) % this.options.snapToGrid, (this.parameters.box.y + diffY + (flag & (1 << 1) ? 0 : this.parameters.box.height)) % this.options.snapToGrid];\n          }\n\n          if(diffX < 0) {\n              temp[0] -= this.options.snapToGrid;\n          }\n          if(diffY < 0) {\n              temp[1] -= this.options.snapToGrid;\n          }\n\n          diffX -= (Math.abs(temp[0]) < this.options.snapToGrid / 2 ?\n                    temp[0] :\n                    temp[0] - (diffX < 0 ? -this.options.snapToGrid : this.options.snapToGrid));\n          diffY -= (Math.abs(temp[1]) < this.options.snapToGrid / 2 ?\n                    temp[1] :\n                    temp[1] - (diffY < 0 ? -this.options.snapToGrid : this.options.snapToGrid));\n\n          return this.constraintToBox(diffX, diffY, flag, pointCoordsY);\n\n      };\n\n      // keep element within constrained box\n      ResizeHandler.prototype.constraintToBox = function (diffX, diffY, flag, pointCoordsY) {\n          //return [diffX, diffY]\n          var c = this.options.constraint || {};\n          var orgX, orgY;\n\n          if (typeof pointCoordsY !== 'undefined') {\n            orgX = flag;\n            orgY = pointCoordsY;\n          } else {\n            orgX = this.parameters.box.x + (flag & 1 ? 0 : this.parameters.box.width);\n            orgY = this.parameters.box.y + (flag & (1<<1) ? 0 : this.parameters.box.height);\n          }\n\n          if (typeof c.minX !== 'undefined' && orgX + diffX < c.minX) {\n            diffX = c.minX - orgX;\n          }\n\n          if (typeof c.maxX !== 'undefined' && orgX + diffX > c.maxX) {\n            diffX = c.maxX - orgX;\n          }\n\n          if (typeof c.minY !== 'undefined' && orgY + diffY < c.minY) {\n            diffY = c.minY - orgY;\n          }\n\n          if (typeof c.maxY !== 'undefined' && orgY + diffY > c.maxY) {\n            diffY = c.maxY - orgY;\n          }\n\n          return [diffX, diffY];\n      };\n\n      ResizeHandler.prototype.checkAspectRatio = function (snap, isReverse) {\n          if (!this.options.saveAspectRatio) {\n              return snap;\n          }\n\n          var updatedSnap = snap.slice();\n          var aspectRatio = this.parameters.box.width / this.parameters.box.height;\n          var newW = this.parameters.box.width + snap[0];\n          var newH = this.parameters.box.height - snap[1];\n          var newAspectRatio = newW / newH;\n\n          if (newAspectRatio < aspectRatio) {\n              // Height is too big. Adapt it\n              updatedSnap[1] = newW / aspectRatio - this.parameters.box.height;\n              isReverse && (updatedSnap[1] = -updatedSnap[1]);\n          } else if (newAspectRatio > aspectRatio) {\n              // Width is too big. Adapt it\n              updatedSnap[0] = this.parameters.box.width - newH * aspectRatio;\n              isReverse && (updatedSnap[0] = -updatedSnap[0]);\n          }\n\n          return updatedSnap;\n      };\n\n      SVG.extend(SVG.Element, {\n          // Resize element with mouse\n          resize: function (options) {\n\n              (this.remember('_resizeHandler') || new ResizeHandler(this)).init(options || {});\n\n              return this;\n\n          }\n\n      });\n\n      SVG.Element.prototype.resize.defaults = {\n          snapToAngle: 0.1,       // Specifies the speed the rotation is happening when moving the mouse\n          snapToGrid: 1,          // Snaps to a grid of `snapToGrid` Pixels\n          constraint: {},         // keep element within constrained box\n          saveAspectRatio: false  // Save aspect ratio when resizing using lt, rt, rb or lb points\n      };\n\n  }).call(this);\n  }());\n\n  if (typeof window.Apex === 'undefined') {\n    window.Apex = {};\n  }\n\n  var InitCtxVariables = /*#__PURE__*/function () {\n    function InitCtxVariables(ctx) {\n      _classCallCheck(this, InitCtxVariables);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(InitCtxVariables, [{\n      key: \"initModules\",\n      value: function initModules() {\n        this.ctx.publicMethods = ['updateOptions', 'updateSeries', 'appendData', 'appendSeries', 'toggleSeries', 'showSeries', 'hideSeries', 'setLocale', 'resetSeries', 'zoomX', 'toggleDataPointSelection', 'dataURI', 'exportToCSV', 'addXaxisAnnotation', 'addYaxisAnnotation', 'addPointAnnotation', 'clearAnnotations', 'removeAnnotation', 'paper', 'destroy'];\n        this.ctx.eventList = ['click', 'mousedown', 'mousemove', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'mouseup', 'touchend'];\n        this.ctx.animations = new Animations(this.ctx);\n        this.ctx.axes = new Axes(this.ctx);\n        this.ctx.core = new Core(this.ctx.el, this.ctx);\n        this.ctx.config = new Config({});\n        this.ctx.data = new Data(this.ctx);\n        this.ctx.grid = new Grid(this.ctx);\n        this.ctx.graphics = new Graphics(this.ctx);\n        this.ctx.coreUtils = new CoreUtils(this.ctx);\n        this.ctx.crosshairs = new Crosshairs(this.ctx);\n        this.ctx.events = new Events(this.ctx);\n        this.ctx.exports = new Exports(this.ctx);\n        this.ctx.localization = new Localization(this.ctx);\n        this.ctx.options = new Options();\n        this.ctx.responsive = new Responsive(this.ctx);\n        this.ctx.series = new Series(this.ctx);\n        this.ctx.theme = new Theme(this.ctx);\n        this.ctx.formatters = new Formatters(this.ctx);\n        this.ctx.titleSubtitle = new TitleSubtitle(this.ctx);\n        this.ctx.legend = new Legend(this.ctx);\n        this.ctx.toolbar = new Toolbar(this.ctx);\n        this.ctx.tooltip = new Tooltip(this.ctx);\n        this.ctx.dimensions = new Dimensions(this.ctx);\n        this.ctx.updateHelpers = new UpdateHelpers(this.ctx);\n        this.ctx.zoomPanSelection = new ZoomPanSelection(this.ctx);\n        this.ctx.w.globals.tooltip = new Tooltip(this.ctx);\n      }\n    }]);\n\n    return InitCtxVariables;\n  }();\n\n  var Destroy = /*#__PURE__*/function () {\n    function Destroy(ctx) {\n      _classCallCheck(this, Destroy);\n\n      this.ctx = ctx;\n      this.w = ctx.w;\n    }\n\n    _createClass(Destroy, [{\n      key: \"clear\",\n      value: function clear(_ref) {\n        var isUpdating = _ref.isUpdating;\n\n        if (this.ctx.zoomPanSelection) {\n          this.ctx.zoomPanSelection.destroy();\n        }\n\n        if (this.ctx.toolbar) {\n          this.ctx.toolbar.destroy();\n        }\n\n        this.ctx.animations = null;\n        this.ctx.axes = null;\n        this.ctx.annotations = null;\n        this.ctx.core = null;\n        this.ctx.data = null;\n        this.ctx.grid = null;\n        this.ctx.series = null;\n        this.ctx.responsive = null;\n        this.ctx.theme = null;\n        this.ctx.formatters = null;\n        this.ctx.titleSubtitle = null;\n        this.ctx.legend = null;\n        this.ctx.dimensions = null;\n        this.ctx.options = null;\n        this.ctx.crosshairs = null;\n        this.ctx.zoomPanSelection = null;\n        this.ctx.updateHelpers = null;\n        this.ctx.toolbar = null;\n        this.ctx.localization = null;\n        this.ctx.w.globals.tooltip = null;\n        this.clearDomElements({\n          isUpdating: isUpdating\n        });\n      }\n    }, {\n      key: \"killSVG\",\n      value: function killSVG(draw) {\n        draw.each(function (i, children) {\n          this.removeClass('*');\n          this.off();\n          this.stop();\n        }, true);\n        draw.ungroup();\n        draw.clear();\n      }\n    }, {\n      key: \"clearDomElements\",\n      value: function clearDomElements(_ref2) {\n        var _this = this;\n\n        var isUpdating = _ref2.isUpdating;\n        var elSVG = this.w.globals.dom.Paper.node; // fixes apexcharts.js#1654 & vue-apexcharts#256\n\n        if (elSVG.parentNode && elSVG.parentNode.parentNode && !isUpdating) {\n          elSVG.parentNode.parentNode.style.minHeight = 'unset';\n        } // detach root event\n\n\n        var baseEl = this.w.globals.dom.baseEl;\n\n        if (baseEl) {\n          // see https://github.com/apexcharts/vue-apexcharts/issues/275\n          this.ctx.eventList.forEach(function (event) {\n            baseEl.removeEventListener(event, _this.ctx.events.documentEvent);\n          });\n        }\n\n        var domEls = this.w.globals.dom;\n\n        if (this.ctx.el !== null) {\n          // remove all child elements - resetting the whole chart\n          while (this.ctx.el.firstChild) {\n            this.ctx.el.removeChild(this.ctx.el.firstChild);\n          }\n        }\n\n        this.killSVG(domEls.Paper);\n        domEls.Paper.remove();\n        domEls.elWrap = null;\n        domEls.elGraphical = null;\n        domEls.elAnnotations = null;\n        domEls.elLegendWrap = null;\n        domEls.baseEl = null;\n        domEls.elGridRect = null;\n        domEls.elGridRectMask = null;\n        domEls.elGridRectMarkerMask = null;\n        domEls.elForecastMask = null;\n        domEls.elNonForecastMask = null;\n        domEls.elDefs = null;\n      }\n    }]);\n\n    return Destroy;\n  }();\n\n  // Helpers to react to element resizes, regardless of what caused them\n  // TODO Currently this creates a new ResizeObserver every time we want to observe an element for resizes\n  // Ideally, we should be able to use a single observer for all elements\n  var ros = new WeakMap(); // Map callbacks to ResizeObserver instances for easy removal\n\n  function addResizeListener(el, fn) {\n    var called = false;\n\n    if (el.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {\n      var elRect = el.getBoundingClientRect();\n\n      if (el.style.display === 'none' || elRect.width === 0) {\n        // if elRect.width=0, the chart is not rendered at all\n        // (it has either display none or hidden in a different tab)\n        // fixes https://github.com/apexcharts/apexcharts.js/issues/2825\n        // fixes https://github.com/apexcharts/apexcharts.js/issues/2991\n        // fixes https://github.com/apexcharts/apexcharts.js/issues/2992\n        called = true;\n      }\n    }\n\n    var ro = new ResizeObserver(function (r) {\n      // ROs fire immediately after being created,\n      // per spec: https://drafts.csswg.org/resize-observer/#ref-for-element%E2%91%A3\n      // we don't want that so we just discard the first run\n      if (called) {\n        fn.call(el, r);\n      }\n\n      called = true;\n    });\n\n    if (el.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\n      // Document fragment, observe children instead (needed for Shadow DOM, see #1332)\n      Array.from(el.children).forEach(function (c) {\n        return ro.observe(c);\n      });\n    } else {\n      ro.observe(el);\n    }\n\n    ros.set(fn, ro);\n  }\n  function removeResizeListener(el, fn) {\n    var ro = ros.get(fn);\n\n    if (ro) {\n      ro.disconnect();\n      ros.delete(fn);\n    }\n  }\n\n  var css_248z = \"@keyframes opaque {\\n  0% {\\n      opacity: 0\\n  }\\n\\n  to {\\n      opacity: 1\\n  }\\n}\\n\\n@keyframes resizeanim {\\n  0%,to {\\n      opacity: 0\\n  }\\n}\\n\\n.apexcharts-canvas {\\n  position: relative;\\n  user-select: none\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar {\\n  -webkit-appearance: none;\\n  width: 6px\\n}\\n\\n.apexcharts-canvas ::-webkit-scrollbar-thumb {\\n  border-radius: 4px;\\n  background-color: rgba(0,0,0,.5);\\n  box-shadow: 0 0 1px rgba(255,255,255,.5);\\n  -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5)\\n}\\n\\n.apexcharts-inner {\\n  position: relative\\n}\\n\\n.apexcharts-text tspan {\\n  font-family: inherit\\n}\\n\\n.legend-mouseover-inactive {\\n  transition: .15s ease all;\\n  opacity: .2\\n}\\n\\n.apexcharts-legend-text {\\n  padding-left: 15px;\\n  margin-left: -15px;\\n}\\n\\n.apexcharts-series-collapsed {\\n  opacity: 0\\n}\\n\\n.apexcharts-tooltip {\\n  border-radius: 5px;\\n  box-shadow: 2px 2px 6px -4px #999;\\n  cursor: default;\\n  font-size: 14px;\\n  left: 62px;\\n  opacity: 0;\\n  pointer-events: none;\\n  position: absolute;\\n  top: 20px;\\n  display: flex;\\n  flex-direction: column;\\n  overflow: hidden;\\n  white-space: nowrap;\\n  z-index: 12;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light {\\n  border: 1px solid #e3e3e3;\\n  background: rgba(255,255,255,.96)\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark {\\n  color: #fff;\\n  background: rgba(30,30,30,.8)\\n}\\n\\n.apexcharts-tooltip * {\\n  font-family: inherit\\n}\\n\\n.apexcharts-tooltip-title {\\n  padding: 6px;\\n  font-size: 15px;\\n  margin-bottom: 4px\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {\\n  background: #eceff1;\\n  border-bottom: 1px solid #ddd\\n}\\n\\n.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {\\n  background: rgba(0,0,0,.7);\\n  border-bottom: 1px solid #333\\n}\\n\\n.apexcharts-tooltip-text-goals-value,.apexcharts-tooltip-text-y-value,.apexcharts-tooltip-text-z-value {\\n  display: inline-block;\\n  margin-left: 5px;\\n  font-weight: 600\\n}\\n\\n.apexcharts-tooltip-text-goals-label:empty,.apexcharts-tooltip-text-goals-value:empty,.apexcharts-tooltip-text-y-label:empty,.apexcharts-tooltip-text-y-value:empty,.apexcharts-tooltip-text-z-value:empty,.apexcharts-tooltip-title:empty {\\n  display: none\\n}\\n\\n.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-tooltip-goals-group,.apexcharts-tooltip-text-goals-label,.apexcharts-tooltip-text-goals-value {\\n  display: flex\\n}\\n\\n.apexcharts-tooltip-text-goals-label:not(:empty),.apexcharts-tooltip-text-goals-value:not(:empty) {\\n  margin-top: -6px\\n}\\n\\n.apexcharts-tooltip-marker {\\n  width: 12px;\\n  height: 12px;\\n  position: relative;\\n  top: 0;\\n  margin-right: 10px;\\n  border-radius: 50%\\n}\\n\\n.apexcharts-tooltip-series-group {\\n  padding: 0 10px;\\n  display: none;\\n  text-align: left;\\n  justify-content: left;\\n  align-items: center\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {\\n  opacity: 1\\n}\\n\\n.apexcharts-tooltip-series-group.apexcharts-active,.apexcharts-tooltip-series-group:last-child {\\n  padding-bottom: 4px\\n}\\n\\n.apexcharts-tooltip-series-group-hidden {\\n  opacity: 0;\\n  height: 0;\\n  line-height: 0;\\n  padding: 0!important\\n}\\n\\n.apexcharts-tooltip-y-group {\\n  padding: 6px 0 5px\\n}\\n\\n.apexcharts-custom-tooltip,.apexcharts-tooltip-box {\\n  padding: 4px 8px\\n}\\n\\n.apexcharts-tooltip-boxPlot {\\n  display: flex;\\n  flex-direction: column-reverse\\n}\\n\\n.apexcharts-tooltip-box>div {\\n  margin: 4px 0\\n}\\n\\n.apexcharts-tooltip-box span.value {\\n  font-weight: 700\\n}\\n\\n.apexcharts-tooltip-rangebar {\\n  padding: 5px 8px\\n}\\n\\n.apexcharts-tooltip-rangebar .category {\\n  font-weight: 600;\\n  color: #777\\n}\\n\\n.apexcharts-tooltip-rangebar .series-name {\\n  font-weight: 700;\\n  display: block;\\n  margin-bottom: 5px\\n}\\n\\n.apexcharts-xaxistooltip,.apexcharts-yaxistooltip {\\n  opacity: 0;\\n  pointer-events: none;\\n  color: #373d3f;\\n  font-size: 13px;\\n  text-align: center;\\n  border-radius: 2px;\\n  position: absolute;\\n  z-index: 10;\\n  background: #eceff1;\\n  border: 1px solid #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip {\\n  padding: 9px 10px;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-xaxistooltip:after,.apexcharts-xaxistooltip:before {\\n  left: 50%;\\n  border: solid transparent;\\n  content: \\\" \\\";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-xaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-left: -6px\\n}\\n\\n.apexcharts-xaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-left: -7px\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after,.apexcharts-xaxistooltip-bottom:before {\\n  bottom: 100%\\n}\\n\\n.apexcharts-xaxistooltip-top:after,.apexcharts-xaxistooltip-top:before {\\n  top: 100%\\n}\\n\\n.apexcharts-xaxistooltip-bottom:after {\\n  border-bottom-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-bottom:before {\\n  border-bottom-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {\\n  border-bottom-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip-top:after {\\n  border-top-color: #eceff1\\n}\\n\\n.apexcharts-xaxistooltip-top:before {\\n  border-top-color: #90a4ae\\n}\\n\\n.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after,.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {\\n  border-top-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-xaxistooltip.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-yaxistooltip {\\n  padding: 4px 10px\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-theme-dark {\\n  background: rgba(0,0,0,.7);\\n  border: 1px solid rgba(0,0,0,.5);\\n  color: #fff\\n}\\n\\n.apexcharts-yaxistooltip:after,.apexcharts-yaxistooltip:before {\\n  top: 50%;\\n  border: solid transparent;\\n  content: \\\" \\\";\\n  height: 0;\\n  width: 0;\\n  position: absolute;\\n  pointer-events: none\\n}\\n\\n.apexcharts-yaxistooltip:after {\\n  border-color: transparent;\\n  border-width: 6px;\\n  margin-top: -6px\\n}\\n\\n.apexcharts-yaxistooltip:before {\\n  border-color: transparent;\\n  border-width: 7px;\\n  margin-top: -7px\\n}\\n\\n.apexcharts-yaxistooltip-left:after,.apexcharts-yaxistooltip-left:before {\\n  left: 100%\\n}\\n\\n.apexcharts-yaxistooltip-right:after,.apexcharts-yaxistooltip-right:before {\\n  right: 100%\\n}\\n\\n.apexcharts-yaxistooltip-left:after {\\n  border-left-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-left:before {\\n  border-left-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {\\n  border-left-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip-right:after {\\n  border-right-color: #eceff1\\n}\\n\\n.apexcharts-yaxistooltip-right:before {\\n  border-right-color: #90a4ae\\n}\\n\\n.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after,.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {\\n  border-right-color: rgba(0,0,0,.5)\\n}\\n\\n.apexcharts-yaxistooltip.apexcharts-active {\\n  opacity: 1\\n}\\n\\n.apexcharts-yaxistooltip-hidden {\\n  display: none\\n}\\n\\n.apexcharts-xcrosshairs,.apexcharts-ycrosshairs {\\n  pointer-events: none;\\n  opacity: 0;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-xcrosshairs.apexcharts-active,.apexcharts-ycrosshairs.apexcharts-active {\\n  opacity: 1;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-ycrosshairs-hidden {\\n  opacity: 0\\n}\\n\\n.apexcharts-selection-rect {\\n  cursor: move\\n}\\n\\n.svg_select_boundingRect,.svg_select_points_rot {\\n  pointer-events: none;\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_boundingRect,.apexcharts-selection-rect+g .svg_select_points_rot {\\n  opacity: 0;\\n  visibility: hidden\\n}\\n\\n.apexcharts-selection-rect+g .svg_select_points_l,.apexcharts-selection-rect+g .svg_select_points_r {\\n  cursor: ew-resize;\\n  opacity: 1;\\n  visibility: visible\\n}\\n\\n.svg_select_points {\\n  fill: #efefef;\\n  stroke: #333;\\n  rx: 2\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-zoom {\\n  cursor: crosshair\\n}\\n\\n.apexcharts-svg.apexcharts-zoomable.hovering-pan {\\n  cursor: move\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-pan-icon,.apexcharts-reset-icon,.apexcharts-selection-icon,.apexcharts-toolbar-custom-icon,.apexcharts-zoom-icon,.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  cursor: pointer;\\n  width: 20px;\\n  height: 20px;\\n  line-height: 24px;\\n  color: #6e8192;\\n  text-align: center\\n}\\n\\n.apexcharts-menu-icon svg,.apexcharts-reset-icon svg,.apexcharts-zoom-icon svg,.apexcharts-zoomin-icon svg,.apexcharts-zoomout-icon svg {\\n  fill: #6e8192\\n}\\n\\n.apexcharts-selection-icon svg {\\n  fill: #444;\\n  transform: scale(.76)\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu-icon svg,.apexcharts-theme-dark .apexcharts-pan-icon svg,.apexcharts-theme-dark .apexcharts-reset-icon svg,.apexcharts-theme-dark .apexcharts-selection-icon svg,.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg,.apexcharts-theme-dark .apexcharts-zoom-icon svg,.apexcharts-theme-dark .apexcharts-zoomin-icon svg,.apexcharts-theme-dark .apexcharts-zoomout-icon svg {\\n  fill: #f3f4f5\\n}\\n\\n.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg {\\n  fill: #008ffb\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-icon:hover svg,.apexcharts-theme-light .apexcharts-reset-icon:hover svg,.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg {\\n  fill: #333\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-selection-icon {\\n  position: relative\\n}\\n\\n.apexcharts-reset-icon {\\n  margin-left: 5px\\n}\\n\\n.apexcharts-menu-icon,.apexcharts-reset-icon,.apexcharts-zoom-icon {\\n  transform: scale(.85)\\n}\\n\\n.apexcharts-zoomin-icon,.apexcharts-zoomout-icon {\\n  transform: scale(.7)\\n}\\n\\n.apexcharts-zoomout-icon {\\n  margin-right: 3px\\n}\\n\\n.apexcharts-pan-icon {\\n  transform: scale(.62);\\n  position: relative;\\n  left: 1px;\\n  top: 0\\n}\\n\\n.apexcharts-pan-icon svg {\\n  fill: #fff;\\n  stroke: #6e8192;\\n  stroke-width: 2\\n}\\n\\n.apexcharts-pan-icon.apexcharts-selected svg {\\n  stroke: #008ffb\\n}\\n\\n.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {\\n  stroke: #333\\n}\\n\\n.apexcharts-toolbar {\\n  position: absolute;\\n  z-index: 11;\\n  max-width: 176px;\\n  text-align: right;\\n  border-radius: 3px;\\n  padding: 0 6px 2px;\\n  display: flex;\\n  justify-content: space-between;\\n  align-items: center\\n}\\n\\n.apexcharts-menu {\\n  background: #fff;\\n  position: absolute;\\n  top: 100%;\\n  border: 1px solid #ddd;\\n  border-radius: 3px;\\n  padding: 3px;\\n  right: 10px;\\n  opacity: 0;\\n  min-width: 110px;\\n  transition: .15s ease all;\\n  pointer-events: none\\n}\\n\\n.apexcharts-menu.apexcharts-menu-open {\\n  opacity: 1;\\n  pointer-events: all;\\n  transition: .15s ease all\\n}\\n\\n.apexcharts-menu-item {\\n  padding: 6px 7px;\\n  font-size: 12px;\\n  cursor: pointer\\n}\\n\\n.apexcharts-theme-light .apexcharts-menu-item:hover {\\n  background: #eee\\n}\\n\\n.apexcharts-theme-dark .apexcharts-menu {\\n  background: rgba(0,0,0,.7);\\n  color: #fff\\n}\\n\\n@media screen and (min-width:768px) {\\n  .apexcharts-canvas:hover .apexcharts-toolbar {\\n      opacity: 1\\n  }\\n}\\n\\n.apexcharts-canvas .apexcharts-element-hidden,.apexcharts-datalabel.apexcharts-element-hidden,.apexcharts-hide .apexcharts-series-points {\\n  opacity: 0\\n}\\n\\n.apexcharts-datalabel,.apexcharts-datalabel-label,.apexcharts-datalabel-value,.apexcharts-datalabels,.apexcharts-pie-label {\\n  cursor: default;\\n  pointer-events: none\\n}\\n\\n.apexcharts-pie-label-delay {\\n  opacity: 0;\\n  animation-name: opaque;\\n  animation-duration: .3s;\\n  animation-fill-mode: forwards;\\n  animation-timing-function: ease\\n}\\n\\n.apexcharts-legend {\\t\\n  display: flex;\\t\\n  overflow: auto;\\t\\n  padding: 0 10px;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom, .apexcharts-legend.apx-legend-position-top {\\t\\n  flex-wrap: wrap\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  flex-direction: column;\\t\\n  bottom: 0;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-left, .apexcharts-legend.apx-legend-position-top.apexcharts-align-left, .apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\\t\\n  justify-content: flex-start;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-center, .apexcharts-legend.apx-legend-position-top.apexcharts-align-center {\\t\\n  justify-content: center;  \\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-right, .apexcharts-legend.apx-legend-position-top.apexcharts-align-right {\\t\\n  justify-content: flex-end;\\t\\n}\\t\\n.apexcharts-legend-series {\\t\\n  cursor: pointer;\\t\\n  line-height: normal;\\t\\n}\\t\\n.apexcharts-legend.apx-legend-position-bottom .apexcharts-legend-series, .apexcharts-legend.apx-legend-position-top .apexcharts-legend-series{\\t\\n  display: flex;\\t\\n  align-items: center;\\t\\n}\\t\\n.apexcharts-legend-text {\\t\\n  position: relative;\\t\\n  font-size: 14px;\\t\\n}\\t\\n.apexcharts-legend-text *, .apexcharts-legend-marker * {\\t\\n  pointer-events: none;\\t\\n}\\t\\n.apexcharts-legend-marker {\\t\\n  position: relative;\\t\\n  display: inline-block;\\t\\n  cursor: pointer;\\t\\n  margin-right: 3px;\\t\\n  border-style: solid;\\n}\\t\\n  \\n.apexcharts-legend.apexcharts-align-right .apexcharts-legend-series, .apexcharts-legend.apexcharts-align-left .apexcharts-legend-series{\\t\\n  display: inline-block;\\t\\n}\\t\\n.apexcharts-legend-series.apexcharts-no-click {\\t\\n  cursor: auto;\\t\\n}\\t\\n.apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {\\t\\n  display: none !important;\\t\\n}\\t\\n.apexcharts-inactive-legend {\\t\\n  opacity: 0.45;\\t\\n}\\n\\n.apexcharts-annotation-rect,.apexcharts-area-series .apexcharts-area,.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-gridline,.apexcharts-line,.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,.apexcharts-point-annotation-label,.apexcharts-radar-series path,.apexcharts-radar-series polygon,.apexcharts-toolbar svg,.apexcharts-tooltip .apexcharts-marker,.apexcharts-xaxis-annotation-label,.apexcharts-yaxis-annotation-label,.apexcharts-zoom-rect {\\n  pointer-events: none\\n}\\n\\n.apexcharts-marker {\\n  transition: .15s ease all\\n}\\n\\n.resize-triggers {\\n  animation: 1ms resizeanim;\\n  visibility: hidden;\\n  opacity: 0;\\n  height: 100%;\\n  width: 100%;\\n  overflow: hidden\\n}\\n\\n.contract-trigger:before,.resize-triggers,.resize-triggers>div {\\n  content: \\\" \\\";\\n  display: block;\\n  position: absolute;\\n  top: 0;\\n  left: 0\\n}\\n\\n.resize-triggers>div {\\n  height: 100%;\\n  width: 100%;\\n  background: #eee;\\n  overflow: auto\\n}\\n\\n.contract-trigger:before {\\n  overflow: hidden;\\n  width: 200%;\\n  height: 200%\\n}\\n\";\n\n  /**\n   *\n   * @module ApexCharts\n   **/\n\n  var ApexCharts$1 = /*#__PURE__*/function () {\n    function ApexCharts(el, opts) {\n      _classCallCheck(this, ApexCharts);\n\n      this.opts = opts;\n      this.ctx = this; // Pass the user supplied options to the Base Class where these options will be extended with defaults. The returned object from Base Class will become the config object in the entire codebase.\n\n      this.w = new Base(opts).init();\n      this.el = el;\n      this.w.globals.cuid = Utils$1.randomId();\n      this.w.globals.chartID = this.w.config.chart.id ? Utils$1.escapeString(this.w.config.chart.id) : this.w.globals.cuid;\n      var initCtx = new InitCtxVariables(this);\n      initCtx.initModules();\n      this.create = Utils$1.bind(this.create, this);\n      this.windowResizeHandler = this._windowResizeHandler.bind(this);\n      this.parentResizeHandler = this._parentResizeCallback.bind(this);\n    }\n    /**\n     * The primary method user will call to render the chart.\n     */\n\n\n    _createClass(ApexCharts, [{\n      key: \"render\",\n      value: function render() {\n        var _this = this;\n\n        // main method\n        return new Promise(function (resolve, reject) {\n          // only draw chart, if element found\n          if (_this.el !== null) {\n            if (typeof Apex._chartInstances === 'undefined') {\n              Apex._chartInstances = [];\n            }\n\n            if (_this.w.config.chart.id) {\n              Apex._chartInstances.push({\n                id: _this.w.globals.chartID,\n                group: _this.w.config.chart.group,\n                chart: _this\n              });\n            } // set the locale here\n\n\n            _this.setLocale(_this.w.config.chart.defaultLocale);\n\n            var beforeMount = _this.w.config.chart.events.beforeMount;\n\n            if (typeof beforeMount === 'function') {\n              beforeMount(_this, _this.w);\n            }\n\n            _this.events.fireEvent('beforeMount', [_this, _this.w]);\n\n            window.addEventListener('resize', _this.windowResizeHandler);\n            addResizeListener(_this.el.parentNode, _this.parentResizeHandler); // Add CSS if not already added\n\n            if (!_this.css) {\n              var rootNode = _this.el.getRootNode && _this.el.getRootNode();\n\n              var inShadowRoot = Utils$1.is('ShadowRoot', rootNode);\n              var doc = _this.el.ownerDocument;\n              var globalCSS = doc.getElementById('apexcharts-css');\n\n              if (inShadowRoot || !globalCSS) {\n                _this.css = document.createElement('style');\n                _this.css.id = 'apexcharts-css';\n                _this.css.textContent = css_248z;\n\n                if (inShadowRoot) {\n                  // We are in Shadow DOM, add to shadow root\n                  rootNode.prepend(_this.css);\n                } else {\n                  // Add to <head> of element's document\n                  doc.head.appendChild(_this.css);\n                }\n              }\n            }\n\n            var graphData = _this.create(_this.w.config.series, {});\n\n            if (!graphData) return resolve(_this);\n\n            _this.mount(graphData).then(function () {\n              if (typeof _this.w.config.chart.events.mounted === 'function') {\n                _this.w.config.chart.events.mounted(_this, _this.w);\n              }\n\n              _this.events.fireEvent('mounted', [_this, _this.w]);\n\n              resolve(graphData);\n            }).catch(function (e) {\n              reject(e); // handle error in case no data or element not found\n            });\n          } else {\n            reject(new Error('Element not found'));\n          }\n        });\n      }\n    }, {\n      key: \"create\",\n      value: function create(ser, opts) {\n        var w = this.w;\n        var initCtx = new InitCtxVariables(this);\n        initCtx.initModules();\n        var gl = this.w.globals;\n        gl.noData = false;\n        gl.animationEnded = false;\n        this.responsive.checkResponsiveConfig(opts);\n\n        if (w.config.xaxis.convertedCatToNumeric) {\n          var defaults = new Defaults(w.config);\n          defaults.convertCatToNumericXaxis(w.config, this.ctx);\n        }\n\n        if (this.el === null) {\n          gl.animationEnded = true;\n          return null;\n        }\n\n        this.core.setupElements();\n\n        if (w.config.chart.type === 'treemap') {\n          w.config.grid.show = false;\n          w.config.yaxis[0].show = false;\n        }\n\n        if (gl.svgWidth === 0) {\n          // if the element is hidden, skip drawing\n          gl.animationEnded = true;\n          return null;\n        }\n\n        var combo = CoreUtils.checkComboSeries(ser);\n        gl.comboCharts = combo.comboCharts;\n        gl.comboBarCount = combo.comboBarCount;\n        var allSeriesAreEmpty = ser.every(function (s) {\n          return s.data && s.data.length === 0;\n        });\n\n        if (ser.length === 0 || allSeriesAreEmpty) {\n          this.series.handleNoData();\n        }\n\n        this.events.setupEventHandlers(); // Handle the data inputted by user and set some of the global variables (for eg, if data is datetime / numeric / category). Don't calculate the range / min / max at this time\n\n        this.data.parseData(ser); // this is a good time to set theme colors first\n\n        this.theme.init(); // as markers accepts array, we need to setup global markers for easier access\n\n        var markers = new Markers(this);\n        markers.setGlobalMarkerSize(); // labelFormatters should be called before dimensions as in dimensions we need text labels width\n\n        this.formatters.setLabelFormatters();\n        this.titleSubtitle.draw(); // legend is calculated here before coreCalculations because it affects the plottable area\n        // if there is some data to show or user collapsed all series, then proceed drawing legend\n\n        if (!gl.noData || gl.collapsedSeries.length === gl.series.length || w.config.legend.showForSingleSeries) {\n          this.legend.init();\n        } // check whether in multiple series, all series share the same X\n\n\n        this.series.hasAllSeriesEqualX(); // coreCalculations will give the min/max range and yaxis/axis values. It should be called here to set series variable from config to globals\n\n        if (gl.axisCharts) {\n          this.core.coreCalculations();\n\n          if (w.config.xaxis.type !== 'category') {\n            // as we have minX and maxX values, determine the default DateTimeFormat for time series\n            this.formatters.setLabelFormatters();\n          }\n\n          this.ctx.toolbar.minX = w.globals.minX;\n          this.ctx.toolbar.maxX = w.globals.maxX;\n        } // we need to generate yaxis for heatmap separately as we are not showing numerics there, but seriesNames. There are some tweaks which are required for heatmap to align labels correctly which are done in below function\n        // Also we need to do this before calculating Dimensions plotCoords() method of Dimensions\n\n\n        this.formatters.heatmapLabelFormatters(); // get the largest marker size which will be needed in dimensions calc\n\n        var coreUtils = new CoreUtils(this);\n        coreUtils.getLargestMarkerSize(); // We got plottable area here, next task would be to calculate axis areas\n\n        this.dimensions.plotCoords();\n        var xyRatios = this.core.xySettings();\n        this.grid.createGridMask();\n        var elGraph = this.core.plotChartType(ser, xyRatios);\n        var dataLabels = new DataLabels(this);\n        dataLabels.bringForward();\n\n        if (w.config.dataLabels.background.enabled) {\n          dataLabels.dataLabelsBackground();\n        } // after all the drawing calculations, shift the graphical area (actual charts/bars) excluding legends\n\n\n        this.core.shiftGraphPosition();\n        var dim = {\n          plot: {\n            left: w.globals.translateX,\n            top: w.globals.translateY,\n            width: w.globals.gridWidth,\n            height: w.globals.gridHeight\n          }\n        };\n        return {\n          elGraph: elGraph,\n          xyRatios: xyRatios,\n          elInner: w.globals.dom.elGraphical,\n          dimensions: dim\n        };\n      }\n    }, {\n      key: \"mount\",\n      value: function mount() {\n        var _this2 = this;\n\n        var graphData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n        var me = this;\n        var w = me.w;\n        return new Promise(function (resolve, reject) {\n          // no data to display\n          if (me.el === null) {\n            return reject(new Error('Not enough data to display or target element not found'));\n          } else if (graphData === null || w.globals.allSeriesCollapsed) {\n            me.series.handleNoData();\n          }\n\n          me.grid = new Grid(me);\n          var elgrid = me.grid.drawGrid();\n          me.annotations = new Annotations(me);\n          me.annotations.drawImageAnnos();\n          me.annotations.drawTextAnnos();\n\n          if (w.config.grid.position === 'back' && elgrid) {\n            w.globals.dom.elGraphical.add(elgrid.el);\n\n            if (elgrid && elgrid.elGridBorders && elgrid.elGridBorders.node) {\n              w.globals.dom.elGraphical.add(elgrid.elGridBorders);\n            }\n          }\n\n          if (Array.isArray(graphData.elGraph)) {\n            for (var g = 0; g < graphData.elGraph.length; g++) {\n              w.globals.dom.elGraphical.add(graphData.elGraph[g]);\n            }\n          } else {\n            w.globals.dom.elGraphical.add(graphData.elGraph);\n          }\n\n          if (w.config.grid.position === 'front' && elgrid) {\n            w.globals.dom.elGraphical.add(elgrid.el);\n\n            if (elgrid && elgrid.elGridBorders && elgrid.elGridBorders.node) {\n              w.globals.dom.elGraphical.add(elgrid.elGridBorders);\n            }\n          }\n\n          if (w.config.xaxis.crosshairs.position === 'front') {\n            me.crosshairs.drawXCrosshairs();\n          }\n\n          if (w.config.yaxis[0].crosshairs.position === 'front') {\n            me.crosshairs.drawYCrosshairs();\n          }\n\n          if (w.config.chart.type !== 'treemap') {\n            me.axes.drawAxis(w.config.chart.type, elgrid);\n          }\n\n          var xAxis = new XAxis(_this2.ctx, elgrid);\n          var yaxis = new YAxis(_this2.ctx, elgrid);\n\n          if (elgrid !== null) {\n            xAxis.xAxisLabelCorrections(elgrid.xAxisTickWidth);\n            yaxis.setYAxisTextAlignments();\n            w.config.yaxis.map(function (yaxe, index) {\n              if (w.globals.ignoreYAxisIndexes.indexOf(index) === -1) {\n                yaxis.yAxisTitleRotate(index, yaxe.opposite);\n              }\n            });\n          }\n\n          w.globals.dom.Paper.add(w.globals.dom.elAnnotations);\n          me.annotations.drawAxesAnnotations();\n\n          if (!w.globals.noData) {\n            // draw tooltips at the end\n            if (w.config.tooltip.enabled && !w.globals.noData) {\n              me.w.globals.tooltip.drawTooltip(graphData.xyRatios);\n            }\n\n            if (w.globals.axisCharts && (w.globals.isXNumeric || w.config.xaxis.convertedCatToNumeric || w.globals.isRangeBar)) {\n              if (w.config.chart.zoom.enabled || w.config.chart.selection && w.config.chart.selection.enabled || w.config.chart.pan && w.config.chart.pan.enabled) {\n                me.zoomPanSelection.init({\n                  xyRatios: graphData.xyRatios\n                });\n              }\n            } else {\n              var tools = w.config.chart.toolbar.tools;\n              var toolsArr = ['zoom', 'zoomin', 'zoomout', 'selection', 'pan', 'reset'];\n              toolsArr.forEach(function (t) {\n                tools[t] = false;\n              });\n            }\n\n            if (w.config.chart.toolbar.show && !w.globals.allSeriesCollapsed) {\n              me.toolbar.createToolbar();\n            }\n          }\n\n          if (w.globals.memory.methodsToExec.length > 0) {\n            w.globals.memory.methodsToExec.forEach(function (fn) {\n              fn.method(fn.params, false, fn.context);\n            });\n          }\n\n          if (!w.globals.axisCharts && !w.globals.noData) {\n            me.core.resizeNonAxisCharts();\n          }\n\n          resolve(me);\n        });\n      }\n      /**\n       * Destroy the chart instance by removing all elements which also clean up event listeners on those elements.\n       */\n\n    }, {\n      key: \"destroy\",\n      value: function destroy() {\n        window.removeEventListener('resize', this.windowResizeHandler);\n        removeResizeListener(this.el.parentNode, this.parentResizeHandler); // remove the chart's instance from the global Apex._chartInstances\n\n        var chartID = this.w.config.chart.id;\n\n        if (chartID) {\n          Apex._chartInstances.forEach(function (c, i) {\n            if (c.id === Utils$1.escapeString(chartID)) {\n              Apex._chartInstances.splice(i, 1);\n            }\n          });\n        }\n\n        new Destroy(this.ctx).clear({\n          isUpdating: false\n        });\n      }\n      /**\n       * Allows users to update Options after the chart has rendered.\n       *\n       * @param {object} options - A new config object can be passed which will be merged with the existing config object\n       * @param {boolean} redraw - should redraw from beginning or should use existing paths and redraw from there\n       * @param {boolean} animate - should animate or not on updating Options\n       */\n\n    }, {\n      key: \"updateOptions\",\n      value: function updateOptions(options) {\n        var _this3 = this;\n\n        var redraw = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n        var animate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n        var updateSyncedCharts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;\n        var overwriteInitialConfig = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;\n        var w = this.w; // when called externally, clear some global variables\n        // fixes apexcharts.js#1488\n\n        w.globals.selection = undefined;\n\n        if (options.series) {\n          this.series.resetSeries(false, true, false);\n\n          if (options.series.length && options.series[0].data) {\n            options.series = options.series.map(function (s, i) {\n              return _this3.updateHelpers._extendSeries(s, i);\n            });\n          } // user updated the series via updateOptions() function.\n          // Hence, we need to reset axis min/max to avoid zooming issues\n\n\n          this.updateHelpers.revertDefaultAxisMinMax();\n        } // user has set x-axis min/max externally - hence we need to forcefully set the xaxis min/max\n\n\n        if (options.xaxis) {\n          options = this.updateHelpers.forceXAxisUpdate(options);\n        }\n\n        if (options.yaxis) {\n          options = this.updateHelpers.forceYAxisUpdate(options);\n        }\n\n        if (w.globals.collapsedSeriesIndices.length > 0) {\n          this.series.clearPreviousPaths();\n        }\n        /* update theme mode#459 */\n\n\n        if (options.theme) {\n          options = this.theme.updateThemeOptions(options);\n        }\n\n        return this.updateHelpers._updateOptions(options, redraw, animate, updateSyncedCharts, overwriteInitialConfig);\n      }\n      /**\n       * Allows users to update Series after the chart has rendered.\n       *\n       * @param {array} series - New series which will override the existing\n       */\n\n    }, {\n      key: \"updateSeries\",\n      value: function updateSeries() {\n        var newSeries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n        var animate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        var overwriteInitialSeries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n        this.series.resetSeries(false);\n        this.updateHelpers.revertDefaultAxisMinMax();\n        return this.updateHelpers._updateSeries(newSeries, animate, overwriteInitialSeries);\n      }\n      /**\n       * Allows users to append a new series after the chart has rendered.\n       *\n       * @param {array} newSerie - New serie which will be appended to the existing series\n       */\n\n    }, {\n      key: \"appendSeries\",\n      value: function appendSeries(newSerie) {\n        var animate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        var overwriteInitialSeries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n        var newSeries = this.w.config.series.slice();\n        newSeries.push(newSerie);\n        this.series.resetSeries(false);\n        this.updateHelpers.revertDefaultAxisMinMax();\n        return this.updateHelpers._updateSeries(newSeries, animate, overwriteInitialSeries);\n      }\n      /**\n       * Allows users to append Data to series.\n       *\n       * @param {array} newData - New data in the same format as series\n       */\n\n    }, {\n      key: \"appendData\",\n      value: function appendData(newData) {\n        var overwriteInitialSeries = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        var me = this;\n        me.w.globals.dataChanged = true;\n        me.series.getPreviousPaths();\n        var newSeries = me.w.config.series.slice();\n\n        for (var i = 0; i < newSeries.length; i++) {\n          if (newData[i] !== null && typeof newData[i] !== 'undefined') {\n            for (var j = 0; j < newData[i].data.length; j++) {\n              newSeries[i].data.push(newData[i].data[j]);\n            }\n          }\n        }\n\n        me.w.config.series = newSeries;\n\n        if (overwriteInitialSeries) {\n          me.w.globals.initialSeries = Utils$1.clone(me.w.config.series);\n        }\n\n        return this.update();\n      }\n    }, {\n      key: \"update\",\n      value: function update(options) {\n        var _this4 = this;\n\n        return new Promise(function (resolve, reject) {\n          new Destroy(_this4.ctx).clear({\n            isUpdating: true\n          });\n\n          var graphData = _this4.create(_this4.w.config.series, options);\n\n          if (!graphData) return resolve(_this4);\n\n          _this4.mount(graphData).then(function () {\n            if (typeof _this4.w.config.chart.events.updated === 'function') {\n              _this4.w.config.chart.events.updated(_this4, _this4.w);\n            }\n\n            _this4.events.fireEvent('updated', [_this4, _this4.w]);\n\n            _this4.w.globals.isDirty = true;\n            resolve(_this4);\n          }).catch(function (e) {\n            reject(e);\n          });\n        });\n      }\n      /**\n       * Get all charts in the same \"group\" (including the instance which is called upon) to sync them when user zooms in/out or pan.\n       */\n\n    }, {\n      key: \"getSyncedCharts\",\n      value: function getSyncedCharts() {\n        var chartGroups = this.getGroupedCharts();\n        var allCharts = [this];\n\n        if (chartGroups.length) {\n          allCharts = [];\n          chartGroups.forEach(function (ch) {\n            allCharts.push(ch);\n          });\n        }\n\n        return allCharts;\n      }\n      /**\n       * Get charts in the same \"group\" (excluding the instance which is called upon) to perform operations on the other charts of the same group (eg., tooltip hovering)\n       */\n\n    }, {\n      key: \"getGroupedCharts\",\n      value: function getGroupedCharts() {\n        var _this5 = this;\n\n        return Apex._chartInstances.filter(function (ch) {\n          if (ch.group) {\n            return true;\n          }\n        }).map(function (ch) {\n          return _this5.w.config.chart.group === ch.group ? ch.chart : _this5;\n        });\n      }\n    }, {\n      key: \"toggleSeries\",\n      value: function toggleSeries(seriesName) {\n        return this.series.toggleSeries(seriesName);\n      }\n    }, {\n      key: \"highlightSeriesOnLegendHover\",\n      value: function highlightSeriesOnLegendHover(e, targetElement) {\n        return this.series.toggleSeriesOnHover(e, targetElement);\n      }\n    }, {\n      key: \"showSeries\",\n      value: function showSeries(seriesName) {\n        this.series.showSeries(seriesName);\n      }\n    }, {\n      key: \"hideSeries\",\n      value: function hideSeries(seriesName) {\n        this.series.hideSeries(seriesName);\n      }\n    }, {\n      key: \"resetSeries\",\n      value: function resetSeries() {\n        var shouldUpdateChart = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n        var shouldResetZoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        this.series.resetSeries(shouldUpdateChart, shouldResetZoom);\n      } // Public method to add event listener on chart context\n\n    }, {\n      key: \"addEventListener\",\n      value: function addEventListener(name, handler) {\n        this.events.addEventListener(name, handler);\n      } // Public method to remove event listener on chart context\n\n    }, {\n      key: \"removeEventListener\",\n      value: function removeEventListener(name, handler) {\n        this.events.removeEventListener(name, handler);\n      }\n    }, {\n      key: \"addXaxisAnnotation\",\n      value: function addXaxisAnnotation(opts) {\n        var pushToMemory = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n        var me = this;\n\n        if (context) {\n          me = context;\n        }\n\n        me.annotations.addXaxisAnnotationExternal(opts, pushToMemory, me);\n      }\n    }, {\n      key: \"addYaxisAnnotation\",\n      value: function addYaxisAnnotation(opts) {\n        var pushToMemory = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n        var me = this;\n\n        if (context) {\n          me = context;\n        }\n\n        me.annotations.addYaxisAnnotationExternal(opts, pushToMemory, me);\n      }\n    }, {\n      key: \"addPointAnnotation\",\n      value: function addPointAnnotation(opts) {\n        var pushToMemory = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n        var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n        var me = this;\n\n        if (context) {\n          me = context;\n        }\n\n        me.annotations.addPointAnnotationExternal(opts, pushToMemory, me);\n      }\n    }, {\n      key: \"clearAnnotations\",\n      value: function clearAnnotations() {\n        var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;\n        var me = this;\n\n        if (context) {\n          me = context;\n        }\n\n        me.annotations.clearAnnotations(me);\n      }\n    }, {\n      key: \"removeAnnotation\",\n      value: function removeAnnotation(id) {\n        var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;\n        var me = this;\n\n        if (context) {\n          me = context;\n        }\n\n        me.annotations.removeAnnotation(me, id);\n      }\n    }, {\n      key: \"getChartArea\",\n      value: function getChartArea() {\n        var el = this.w.globals.dom.baseEl.querySelector('.apexcharts-inner');\n        return el;\n      }\n    }, {\n      key: \"getSeriesTotalXRange\",\n      value: function getSeriesTotalXRange(minX, maxX) {\n        return this.coreUtils.getSeriesTotalsXRange(minX, maxX);\n      }\n    }, {\n      key: \"getHighestValueInSeries\",\n      value: function getHighestValueInSeries() {\n        var seriesIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n        var range = new Range(this.ctx);\n        return range.getMinYMaxY(seriesIndex).highestY;\n      }\n    }, {\n      key: \"getLowestValueInSeries\",\n      value: function getLowestValueInSeries() {\n        var seriesIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n        var range = new Range(this.ctx);\n        return range.getMinYMaxY(seriesIndex).lowestY;\n      }\n    }, {\n      key: \"getSeriesTotal\",\n      value: function getSeriesTotal() {\n        return this.w.globals.seriesTotals;\n      }\n    }, {\n      key: \"toggleDataPointSelection\",\n      value: function toggleDataPointSelection(seriesIndex, dataPointIndex) {\n        return this.updateHelpers.toggleDataPointSelection(seriesIndex, dataPointIndex);\n      }\n    }, {\n      key: \"zoomX\",\n      value: function zoomX(min, max) {\n        this.ctx.toolbar.zoomUpdateOptions(min, max);\n      }\n    }, {\n      key: \"setLocale\",\n      value: function setLocale(localeName) {\n        this.localization.setCurrentLocaleValues(localeName);\n      }\n    }, {\n      key: \"dataURI\",\n      value: function dataURI(options) {\n        var exp = new Exports(this.ctx);\n        return exp.dataURI(options);\n      }\n    }, {\n      key: \"exportToCSV\",\n      value: function exportToCSV() {\n        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n        var exp = new Exports(this.ctx);\n        return exp.exportToCSV(options);\n      }\n    }, {\n      key: \"paper\",\n      value: function paper() {\n        return this.w.globals.dom.Paper;\n      }\n    }, {\n      key: \"_parentResizeCallback\",\n      value: function _parentResizeCallback() {\n        if (this.w.globals.animationEnded && this.w.config.chart.redrawOnParentResize) {\n          this._windowResize();\n        }\n      }\n      /**\n       * Handle window resize and re-draw the whole chart.\n       */\n\n    }, {\n      key: \"_windowResize\",\n      value: function _windowResize() {\n        var _this6 = this;\n\n        clearTimeout(this.w.globals.resizeTimer);\n        this.w.globals.resizeTimer = window.setTimeout(function () {\n          _this6.w.globals.resized = true;\n          _this6.w.globals.dataChanged = false; // we need to redraw the whole chart on window resize (with a small delay).\n\n          _this6.ctx.update();\n        }, 150);\n      }\n    }, {\n      key: \"_windowResizeHandler\",\n      value: function _windowResizeHandler() {\n        var redraw = this.w.config.chart.redrawOnWindowResize;\n\n        if (typeof redraw === 'function') {\n          redraw = redraw();\n        }\n\n        redraw && this._windowResize();\n      }\n    }], [{\n      key: \"getChartByID\",\n      value: function getChartByID(id) {\n        var chartId = Utils$1.escapeString(id);\n\n        var c = Apex._chartInstances.filter(function (ch) {\n          return ch.id === chartId;\n        })[0];\n\n        return c && c.chart;\n      }\n      /**\n       * Allows the user to provide data attrs in the element and the chart will render automatically when this method is called by searching for the elements containing 'data-apexcharts' attribute\n       */\n\n    }, {\n      key: \"initOnLoad\",\n      value: function initOnLoad() {\n        var els = document.querySelectorAll('[data-apexcharts]');\n\n        for (var i = 0; i < els.length; i++) {\n          var el = els[i];\n          var options = JSON.parse(els[i].getAttribute('data-options'));\n          var apexChart = new ApexCharts(el, options);\n          apexChart.render();\n        }\n      }\n      /**\n       * This static method allows users to call chart methods without necessarily from the\n       * instance of the chart in case user has assigned chartID to the targeted chart.\n       * The chartID is used for mapping the instance stored in Apex._chartInstances global variable\n       *\n       * This is helpful in cases when you don't have reference of the chart instance\n       * easily and need to call the method from anywhere.\n       * For eg, in React/Vue applications when you have many parent/child components,\n       * and need easy reference to other charts for performing dynamic operations\n       *\n       * @param {string} chartID - The unique identifier which will be used to call methods\n       * on that chart instance\n       * @param {function} fn - The method name to call\n       * @param {object} opts - The parameters which are accepted in the original method will be passed here in the same order.\n       */\n\n    }, {\n      key: \"exec\",\n      value: function exec(chartID, fn) {\n        var chart = this.getChartByID(chartID);\n        if (!chart) return; // turn on the global exec flag to indicate this method was called\n\n        chart.w.globals.isExecCalled = true;\n        var ret = null;\n\n        if (chart.publicMethods.indexOf(fn) !== -1) {\n          for (var _len = arguments.length, opts = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n            opts[_key - 2] = arguments[_key];\n          }\n\n          ret = chart[fn].apply(chart, opts);\n        }\n\n        return ret;\n      }\n    }, {\n      key: \"merge\",\n      value: function merge(target, source) {\n        return Utils$1.extend(target, source);\n      }\n    }]);\n\n    return ApexCharts;\n  }();\n\n  return ApexCharts$1;\n\n})));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/ar.json",
    "content": "{\n\"name\": \"ar\",\n\"options\": {\n\"months\": [\n\"يناير\",\n\"فبراير\",\n\"مارس\",\n\"أبريل\",\n\"مايو\",\n\"يونيو\",\n\"يوليو\",\n\"أغسطس\",\n\"سبتمبر\",\n\"أكتوبر\",\n\"نوفمبر\",\n\"ديسمبر\"\n],\n\"shortMonths\": [\n\"يناير\",\n\"فبراير\",\n\"مارس\",\n\"أبريل\",\n\"مايو\",\n\"يونيو\",\n\"يوليو\",\n\"أغسطس\",\n\"سبتمبر\",\n\"أكتوبر\",\n\"نوفمبر\",\n\"ديسمبر\"\n],\n\"days\": [\n\"الأحد\",\n\"الإثنين\",\n\"الثلاثاء\",\n\"الأربعاء\",\n\"الخميس\",\n\"الجمعة\",\n\"السبت\"\n],\n\"shortDays\": [\n\"أحد\",\n\"إثنين\",\n\"ثلاثاء\",\n\"أربعاء\",\n\"خميس\",\n\"جمعة\",\n\"سبت\"\n],\n\"toolbar\": {\n\"exportToSVG\": \"تحميل بصيغة SVG\",\n\"exportToPNG\": \"تحميل بصيغة PNG\",\n\"exportToCSV\": \"تحميل بصيغة CSV\",\n\"menu\": \"القائمة\",\n\"selection\": \"تحديد\",\n\"selectionZoom\": \"تكبير التحديد\",\n\"zoomIn\": \"تكبير\",\n\"zoomOut\": \"تصغير\",\n\"pan\": \"تحريك\",\n\"reset\": \"إعادة التعيين\"\n}\n}\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/ca.json",
    "content": "{\n  \"name\": \"ca\",\n  \"options\": {\n    \"months\": [\n      \"Gener\",\n      \"Febrer\",\n      \"Març\",\n      \"Abril\",\n      \"Maig\",\n      \"Juny\",\n      \"Juliol\",\n      \"Agost\",\n      \"Setembre\",\n      \"Octubre\",\n      \"Novembre\",\n      \"Desembre\"\n    ],\n    \"shortMonths\": [\n      \"Gen.\",\n      \"Febr.\",\n      \"Març\",\n      \"Abr.\",\n      \"Maig\",\n      \"Juny\",\n      \"Jul.\",\n      \"Ag.\",\n      \"Set.\",\n      \"Oct.\",\n      \"Nov.\",\n      \"Des.\"\n    ],\n    \"days\": [\n      \"Diumenge\",\n      \"Dilluns\",\n      \"Dimarts\",\n      \"Dimecres\",\n      \"Dijous\",\n      \"Divendres\",\n      \"Dissabte\"\n    ],\n    \"shortDays\": [\"Dg\", \"Dl\", \"Dt\", \"Dc\", \"Dj\", \"Dv\", \"Ds\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Descarregar SVG\",\n      \"exportToPNG\": \"Descarregar PNG\",\n      \"exportToCSV\": \"Descarregar CSV\",\n      \"menu\": \"Menú\",\n      \"selection\": \"Seleccionar\",\n      \"selectionZoom\": \"Seleccionar Zoom\",\n      \"zoomIn\": \"Augmentar\",\n      \"zoomOut\": \"Disminuir\",\n      \"pan\": \"Navegació\",\n      \"reset\": \"Reiniciar Zoom\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/cs.json",
    "content": "{\n  \"name\": \"cs\",\n  \"options\": {\n    \"months\": [\n      \"Leden\",\n      \"Únor\",\n      \"Březen\",\n      \"Duben\",\n      \"Květen\",\n      \"Červen\",\n      \"Červenec\",\n      \"Srpen\",\n      \"Září\",\n      \"Říjen\",\n      \"Listopad\",\n      \"Prosinec\"\n    ],\n    \"shortMonths\": [\n      \"Led\",\n      \"Úno\",\n      \"Bře\",\n      \"Dub\",\n      \"Kvě\",\n      \"Čvn\",\n      \"Čvc\",\n      \"Srp\",\n      \"Zář\",\n      \"Říj\",\n      \"Lis\",\n      \"Pro\"\n    ],\n    \"days\": [\n      \"Neděle\",\n      \"Pondělí\",\n      \"Úterý\",\n      \"Středa\",\n      \"Čtvrtek\",\n      \"Pátek\",\n      \"Sobota\"\n    ],\n    \"shortDays\": [\"Ne\", \"Po\", \"Út\", \"St\", \"Čt\", \"Pá\", \"So\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Stáhnout SVG\",\n      \"exportToPNG\": \"Stáhnout PNG\",\n      \"exportToCSV\": \"Stáhnout CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Vybrat\",\n      \"selectionZoom\": \"Zoom: Vybrat\",\n      \"zoomIn\": \"Zoom: Přiblížit\",\n      \"zoomOut\": \"Zoom: Oddálit\",\n      \"pan\": \"Přesouvat\",\n      \"reset\": \"Resetovat\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/de.json",
    "content": "{\n  \"name\": \"de\",\n  \"options\": {\n    \"months\": [\n      \"Januar\",\n      \"Februar\",\n      \"März\",\n      \"April\",\n      \"Mai\",\n      \"Juni\",\n      \"Juli\",\n      \"August\",\n      \"September\",\n      \"Oktober\",\n      \"November\",\n      \"Dezember\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mär\",\n      \"Apr\",\n      \"Mai\",\n      \"Jun\",\n      \"Jul\",\n      \"Aug\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Dez\"\n    ],\n    \"days\": [\n      \"Sonntag\",\n      \"Montag\",\n      \"Dienstag\",\n      \"Mittwoch\",\n      \"Donnerstag\",\n      \"Freitag\",\n      \"Samstag\"\n    ],\n    \"shortDays\": [\"So\", \"Mo\", \"Di\", \"Mi\", \"Do\", \"Fr\", \"Sa\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"SVG speichern\",\n      \"exportToPNG\": \"PNG speichern\",\n      \"exportToCSV\": \"CSV speichern\",\n      \"menu\": \"Menü\",\n      \"selection\": \"Auswahl\",\n      \"selectionZoom\": \"Auswahl vergrößern\",\n      \"zoomIn\": \"Vergrößern\",\n      \"zoomOut\": \"Verkleinern\",\n      \"pan\": \"Verschieben\",\n      \"reset\": \"Zoom zurücksetzen\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/el.json",
    "content": "{\n  \"name\": \"el\",\n  \"options\": {\n    \"months\": [\n      \"Ιανουάριος\",\n      \"Φεβρουάριος\",\n      \"Μάρτιος\",\n      \"Απρίλιος\",\n      \"Μάιος\",\n      \"Ιούνιος\",\n      \"Ιούλιος\",\n      \"Αύγουστος\",\n      \"Σεπτέμβριος\",\n      \"Οκτώβριος\",\n      \"Νοέμβριος\",\n      \"Δεκέμβριος\"\n    ],\n    \"shortMonths\": [\n      \"Ιαν\",\n      \"Φευ\",\n      \"Μαρ\",\n      \"Απρ\",\n      \"Μάι\",\n      \"Ιουν\",\n      \"Ιουλ\",\n      \"Αυγ\",\n      \"Σεπ\",\n      \"Οκτ\",\n      \"Νοε\",\n      \"Δεκ\"\n    ],\n    \"days\": [\n      \"Κυριακή\",\n      \"Δευτέρα\",\n      \"Τρίτη\",\n      \"Τετάρτη\",\n      \"Πέμπτη\",\n      \"Παρασκευή\",\n      \"Σάββατο\"\n    ],\n    \"shortDays\": [\"Κυρ\", \"Δευ\", \"Τρι\", \"Τετ\", \"Πεμ\", \"Παρ\", \"Σαβ\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Λήψη SVG\",\n      \"exportToPNG\": \"Λήψη PNG\",\n      \"exportToCSV\": \"Λήψη CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Επιλογή\",\n      \"selectionZoom\": \"Μεγένθυση βάση επιλογής\",\n      \"zoomIn\": \"Μεγένθυνση\",\n      \"zoomOut\": \"Σμίκρυνση\",\n      \"pan\": \"Μετατόπιση\",\n      \"reset\": \"Επαναφορά μεγένθυνσης\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/en.json",
    "content": "{\n  \"name\": \"en\",\n  \"options\": {\n    \"months\": [\n      \"January\",\n      \"February\",\n      \"March\",\n      \"April\",\n      \"May\",\n      \"June\",\n      \"July\",\n      \"August\",\n      \"September\",\n      \"October\",\n      \"November\",\n      \"December\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"May\",\n      \"Jun\",\n      \"Jul\",\n      \"Aug\",\n      \"Sep\",\n      \"Oct\",\n      \"Nov\",\n      \"Dec\"\n    ],\n    \"days\": [\n      \"Sunday\",\n      \"Monday\",\n      \"Tuesday\",\n      \"Wednesday\",\n      \"Thursday\",\n      \"Friday\",\n      \"Saturday\"\n    ],\n    \"shortDays\": [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Download SVG\",\n      \"exportToPNG\": \"Download PNG\",\n      \"exportToCSV\": \"Download CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Selection\",\n      \"selectionZoom\": \"Selection Zoom\",\n      \"zoomIn\": \"Zoom In\",\n      \"zoomOut\": \"Zoom Out\",\n      \"pan\": \"Panning\",\n      \"reset\": \"Reset Zoom\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/es.json",
    "content": "{\n  \"name\": \"es\",\n  \"options\": {\n    \"months\": [\n      \"Enero\",\n      \"Febrero\",\n      \"Marzo\",\n      \"Abril\",\n      \"Mayo\",\n      \"Junio\",\n      \"Julio\",\n      \"Agosto\",\n      \"Septiembre\",\n      \"Octubre\",\n      \"Noviembre\",\n      \"Diciembre\"\n    ],\n    \"shortMonths\": [\n      \"Ene\",\n      \"Feb\",\n      \"Mar\",\n      \"Abr\",\n      \"May\",\n      \"Jun\",\n      \"Jul\",\n      \"Ago\",\n      \"Sep\",\n      \"Oct\",\n      \"Nov\",\n      \"Dic\"\n    ],\n    \"days\": [\n      \"Domingo\",\n      \"Lunes\",\n      \"Martes\",\n      \"Miércoles\",\n      \"Jueves\",\n      \"Viernes\",\n      \"Sábado\"\n    ],\n    \"shortDays\": [\"Dom\", \"Lun\", \"Mar\", \"Mie\", \"Jue\", \"Vie\", \"Sab\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Descargar SVG\",\n      \"exportToPNG\": \"Descargar PNG\",\n      \"exportToCSV\": \"Descargar CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Seleccionar\",\n      \"selectionZoom\": \"Seleccionar Zoom\",\n      \"zoomIn\": \"Aumentar\",\n      \"zoomOut\": \"Disminuir\",\n      \"pan\": \"Navegación\",\n      \"reset\": \"Reiniciar Zoom\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/et.json",
    "content": "{\n  \"name\": \"et\",\n  \"options\": {\n    \"months\": [\n      \"jaanuar\",\n      \"veebruar\",\n      \"märts\",\n      \"aprill\",\n      \"mai\",\n      \"juuni\",\n      \"juuli\",\n      \"august\",\n      \"september\",\n      \"oktoober\",\n      \"november\",\n      \"detsember\"\n    ],\n    \"shortMonths\": [\n      \"jaan\",\n      \"veebr\",\n      \"märts\",\n      \"apr\",\n      \"mai\",\n      \"juuni\",\n      \"juuli\",\n      \"aug\",\n      \"sept\",\n      \"okt\",\n      \"nov\",\n      \"dets\"\n    ],\n    \"days\": [\n      \"pühapäev\",\n      \"esmaspäev\",\n      \"teisipäev\",\n      \"kolmapäev\",\n      \"neljapäev\",\n      \"reede\",\n      \"laupäev\"\n    ],\n    \"shortDays\": [\n      \"P\",\n      \"E\",\n      \"T\",\n      \"K\",\n      \"N\",\n      \"R\",\n      \"L\"\n    ],\n    \"toolbar\": {\n      \"exportToSVG\": \"Lae alla SVG\",\n      \"exportToPNG\": \"Lae alla PNG\",\n      \"exportToCSV\": \"Lae alla CSV\",\n      \"menu\": \"Menüü\",\n      \"selection\": \"Valik\",\n      \"selectionZoom\": \"Valiku suum\",\n      \"zoomIn\": \"Suurenda\",\n      \"zoomOut\": \"Vähenda\",\n      \"pan\": \"Panoraamimine\",\n      \"reset\": \"Lähtesta suum\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/fa.json",
    "content": "{\n  \"name\": \"fa\",\n  \"options\": {\n    \"months\": [\n      \"فروردین\",\n      \"اردیبهشت\",\n      \"خرداد\",\n      \"تیر\",\n      \"مرداد\",\n      \"شهریور\",\n      \"مهر\",\n      \"آبان\",\n      \"آذر\",\n      \"دی\",\n      \"بهمن\",\n      \"اسفند\"\n    ],\n    \"shortMonths\": [\n      \"فرو\",\n      \"ارد\",\n      \"خرد\",\n      \"تیر\",\n      \"مرد\",\n      \"شهر\",\n      \"مهر\",\n      \"آبا\",\n      \"آذر\",\n      \"دی\",\n      \"بهمـ\",\n      \"اسفـ\"\n    ],\n    \"days\": [\n      \"یکشنبه\",\n      \"دوشنبه\",\n      \"سه شنبه\",\n      \"چهارشنبه\",\n      \"پنجشنبه\",\n      \"جمعه\",\n      \"شنبه\"\n    ],\n    \"shortDays\": [\"ی\", \"د\", \"س\", \"چ\", \"پ\", \"ج\", \"ش\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"دانلود SVG\",\n      \"exportToPNG\": \"دانلود PNG\",\n      \"exportToCSV\": \"دانلود CSV\",\n      \"menu\": \"منو\",\n      \"selection\": \"انتخاب\",\n      \"selectionZoom\": \"بزرگنمایی انتخابی\",\n      \"zoomIn\": \"بزرگنمایی\",\n      \"zoomOut\": \"کوچکنمایی\",\n      \"pan\": \"پیمایش\",\n      \"reset\": \"بازنشانی بزرگنمایی\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/fi.json",
    "content": "{\n  \"name\": \"fi\",\n  \"options\": {\n    \"months\": [\n      \"Tammikuu\",\n      \"Helmikuu\",\n      \"Maaliskuu\",\n      \"Huhtikuu\",\n      \"Toukokuu\",\n      \"Kesäkuu\",\n      \"Heinäkuu\",\n      \"Elokuu\",\n      \"Syyskuu\",\n      \"Lokakuu\",\n      \"Marraskuu\",\n      \"Joulukuu\"\n    ],\n    \"shortMonths\": [\n      \"Tammi\",\n      \"Helmi\",\n      \"Maalis\",\n      \"Huhti\",\n      \"Touko\",\n      \"Kesä\",\n      \"Heinä\",\n      \"Elo\",\n      \"Syys\",\n      \"Loka\",\n      \"Marras\",\n      \"Joulu\"\n    ],\n    \"days\": [\n      \"Sunnuntai\",\n      \"Maanantai\",\n      \"Tiistai\",\n      \"Keskiviikko\",\n      \"Torstai\",\n      \"Perjantai\",\n      \"Lauantai\"\n    ],\n    \"shortDays\": [\"Su\", \"Ma\", \"Ti\", \"Ke\", \"To\", \"Pe\", \"La\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Lataa SVG\",\n      \"exportToPNG\": \"Lataa PNG\",\n      \"exportToCSV\": \"Lataa CSV\",\n      \"menu\": \"Valikko\",\n      \"selection\": \"Valinta\",\n      \"selectionZoom\": \"Valinnan zoomaus\",\n      \"zoomIn\": \"Lähennä\",\n      \"zoomOut\": \"Loitonna\",\n      \"pan\": \"Panoroi\",\n      \"reset\": \"Nollaa zoomaus\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/fr.json",
    "content": "{\n  \"name\": \"fr\",\n  \"options\": {\n    \"months\": [\n      \"janvier\",\n      \"février\",\n      \"mars\",\n      \"avril\",\n      \"mai\",\n      \"juin\",\n      \"juillet\",\n      \"août\",\n      \"septembre\",\n      \"octobre\",\n      \"novembre\",\n      \"décembre\"\n    ],\n    \"shortMonths\": [\n      \"janv.\",\n      \"févr.\",\n      \"mars\",\n      \"avr.\",\n      \"mai\",\n      \"juin\",\n      \"juill.\",\n      \"août\",\n      \"sept.\",\n      \"oct.\",\n      \"nov.\",\n      \"déc.\"\n    ],\n    \"days\": [\n      \"dimanche\",\n      \"lundi\",\n      \"mardi\",\n      \"mercredi\",\n      \"jeudi\",\n      \"vendredi\",\n      \"samedi\"\n    ],\n    \"shortDays\": [\"dim.\", \"lun.\", \"mar.\", \"mer.\", \"jeu.\", \"ven.\", \"sam.\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Télécharger au format SVG\",\n      \"exportToPNG\": \"Télécharger au format PNG\",\n      \"exportToCSV\": \"Télécharger au format CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Sélection\",\n      \"selectionZoom\": \"Sélection et zoom\",\n      \"zoomIn\": \"Zoomer\",\n      \"zoomOut\": \"Dézoomer\",\n      \"pan\": \"Navigation\",\n      \"reset\": \"Réinitialiser le zoom\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/he.json",
    "content": "{\n  \"name\": \"he\",\n  \"options\": {\n    \"months\": [\n      \"ינואר\",\n      \"פברואר\",\n      \"מרץ\",\n      \"אפריל\",\n      \"מאי\",\n      \"יוני\",\n      \"יולי\",\n      \"אוגוסט\",\n      \"ספטמבר\",\n      \"אוקטובר\",\n      \"נובמבר\",\n      \"דצמבר\"\n    ],\n    \"shortMonths\": [\n      \"ינו׳\",\n      \"פבר׳\",\n      \"מרץ\",\n      \"אפר׳\",\n      \"מאי\",\n      \"יוני\",\n      \"יולי\",\n      \"אוג׳\",\n      \"ספט׳\",\n      \"אוק׳\",\n      \"נוב׳\",\n      \"דצמ׳\"\n    ],\n    \"days\": [\n      \"ראשון\",\n      \"שני\",\n      \"שלישי\",\n      \"רביעי\",\n      \"חמישי\",\n      \"שישי\",\n      \"שבת\"\n    ],\n    \"shortDays\": [\"א׳\", \"ב׳\", \"ג׳\", \"ד׳\", \"ה׳\", \"ו׳\", \"ש׳\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"הורד SVG\",\n      \"exportToPNG\": \"הורד PNG\",\n      \"exportToCSV\": \"הורד CSV\",\n      \"menu\": \"תפריט\",\n      \"selection\": \"בחירה\",\n      \"selectionZoom\": \"זום בחירה\",\n      \"zoomIn\": \"הגדלה\",\n      \"zoomOut\": \"הקטנה\",\n      \"pan\": \"הזזה\",\n      \"reset\": \"איפוס תצוגה\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/hi.json",
    "content": "{\n  \"name\": \"hi\",\n  \"options\": {\n    \"months\": [\n      \"जनवरी\",\n      \"फ़रवरी\",\n      \"मार्च\",\n      \"अप्रैल\",\n      \"मई\",\n      \"जून\",\n      \"जुलाई\",\n      \"अगस्त\",\n      \"सितंबर\",\n      \"अक्टूबर\",\n      \"नवंबर\",\n      \"दिसंबर\"\n    ],\n    \"shortMonths\": [\n      \"जनवरी\",\n      \"फ़रवरी\",\n      \"मार्च\",\n      \"अप्रैल\",\n      \"मई\",\n      \"जून\",\n      \"जुलाई\",\n      \"अगस्त\",\n      \"सितंबर\",\n      \"अक्टूबर\",\n      \"नवंबर\",\n      \"दिसंबर\"\n    ],\n    \"days\": [\n      \"रविवार\",\n      \"सोमवार\",\n      \"मंगलवार\",\n      \"बुधवार\",\n      \"गुरुवार\",\n      \"शुक्रवार\",\n      \"शनिवार\"\n    ],\n    \"shortDays\": [\"रवि\", \"सोम\", \"मंगल\", \"बुध\", \"गुरु\", \"शुक्र\", \"शनि\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"निर्यात SVG\",\n      \"exportToPNG\": \"निर्यात PNG\",\n      \"exportToCSV\": \"निर्यात CSV\",\n      \"menu\": \"सूची\",\n      \"selection\": \"चयन\",\n      \"selectionZoom\": \"ज़ूम करना\",\n      \"zoomIn\": \"ज़ूम इन\",\n      \"zoomOut\": \"ज़ूम आउट\",\n      \"pan\": \"पैनिंग\",\n      \"reset\": \"फिर से कायम करना\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/hr.json",
    "content": "{\n  \"name\": \"hr\",\n  \"options\": {\n    \"months\": [\n      \"Siječanj\",\n      \"Veljača\",\n      \"Ožujak\",\n      \"Travanj\",\n      \"Svibanj\",\n      \"Lipanj\",\n      \"Srpanj\",\n      \"Kolovoz\",\n      \"Rujan\",\n      \"Listopad\",\n      \"Studeni\",\n      \"Prosinac\"\n    ],\n    \"shortMonths\": [\n      \"Sij\",\n      \"Velj\",\n      \"Ožu\",\n      \"Tra\",\n      \"Svi\",\n      \"Lip\",\n      \"Srp\",\n      \"Kol\",\n      \"Ruj\",\n      \"Lis\",\n      \"Stu\",\n      \"Pro\"\n    ],\n    \"days\": [\n      \"Nedjelja\",\n      \"Ponedjeljak\",\n      \"Utorak\",\n      \"Srijeda\",\n      \"Četvrtak\",\n      \"Petak\",\n      \"Subota\"\n    ],\n    \"shortDays\": [\"Ned\", \"Pon\", \"Uto\", \"Sri\", \"Čet\", \"Pet\", \"Sub\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Preuzmi SVG\",\n      \"exportToPNG\": \"Preuzmi PNG\",\n      \"exportToCSV\": \"Preuzmi CSV\",\n      \"menu\": \"Izbornik\",\n      \"selection\": \"Odabir\",\n      \"selectionZoom\": \"Odabirno povećanje\",\n      \"zoomIn\": \"Uvećajte prikaz\",\n      \"zoomOut\": \"Umanjite prikaz\",\n      \"pan\": \"Pomicanje\",\n      \"reset\": \"Povratak na zadani prikaz\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/hu.json",
    "content": "{\n  \"name\": \"hu\",\n  \"options\": {\n    \"months\": [\n      \"január\",\n      \"február\",\n      \"március\",\n      \"április\",\n      \"május\",\n      \"június\",\n      \"július\",\n      \"augusztus\",\n      \"szeptember\",\n      \"október\",\n      \"november\",\n      \"december\"\n    ],\n    \"shortMonths\": [\n      \"jan\",\n      \"feb\",\n      \"mar\",\n      \"ápr\",\n      \"máj\",\n      \"jún\",\n      \"júl\",\n      \"aug\",\n      \"szept\",\n      \"okt\",\n      \"nov\",\n      \"dec\"\n    ],\n    \"days\": [\n      \"hétfő\",\n      \"kedd\",\n      \"szerda\",\n      \"csütörtök\",\n      \"péntek\",\n      \"szombat\",\n      \"vasárnap\"\n    ],\n    \"shortDays\": [\n      \"H\",\n      \"K\",\n      \"Sze\",\n      \"Cs\",\n      \"P\",\n      \"Szo\",\n      \"V\"\n    ],\n    \"toolbar\": {\n      \"exportToSVG\": \"Exportálás SVG-be\",\n      \"exportToPNG\": \"Exportálás PNG-be\",\n      \"exportToCSV\": \"Exportálás CSV-be\",\n      \"menu\": \"Fő ajánlat\",\n      \"download\": \"SVG letöltése\",\n      \"selection\": \"Kiválasztás\",\n      \"selectionZoom\": \"Nagyító kiválasztása\",\n      \"zoomIn\": \"Nagyítás\",\n      \"zoomOut\": \"Kicsinyítés\",\n      \"pan\": \"Képcsúsztatás\",\n      \"reset\": \"Nagyító visszaállítása\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/hy.json",
    "content": "{\n  \"name\": \"hy\",\n  \"options\": {\n    \"months\": [\n      \"Հունվար\",\n      \"Փետրվար\",\n      \"Մարտ\",\n      \"Ապրիլ\",\n      \"Մայիս\",\n      \"Հունիս\",\n      \"Հուլիս\",\n      \"Օգոստոս\",\n      \"Սեպտեմբեր\",\n      \"Հոկտեմբեր\",\n      \"Նոյեմբեր\",\n      \"Դեկտեմբեր\"\n    ],\n    \"shortMonths\": [\n      \"Հնվ\",\n      \"Փտվ\",\n      \"Մրտ\",\n      \"Ապր\",\n      \"Մյս\",\n      \"Հնս\",\n      \"Հլիս\",\n      \"Օգս\",\n      \"Սեպ\",\n      \"Հոկ\",\n      \"Նոյ\",\n      \"Դեկ\"\n    ],\n    \"days\": [\n      \"Կիրակի\",\n      \"Երկուշաբթի\",\n      \"Երեքշաբթի\",\n      \"Չորեքշաբթի\",\n      \"Հինգշաբթի\",\n      \"Ուրբաթ\",\n      \"Շաբաթ\"\n    ],\n    \"shortDays\": [\"Կիր\", \"Երկ\", \"Երք\", \"Չրք\", \"Հնգ\", \"Ուրբ\", \"Շբթ\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Բեռնել SVG\",\n      \"exportToPNG\": \"Բեռնել PNG\",\n      \"exportToCSV\": \"Բեռնել CSV\",\n      \"menu\": \"Մենյու\",\n      \"selection\": \"Ընտրված\",\n      \"selectionZoom\": \"Ընտրված հատվածի խոշորացում\",\n      \"zoomIn\": \"Խոշորացնել\",\n      \"zoomOut\": \"Մանրացնել\",\n      \"pan\": \"Տեղափոխում\",\n      \"reset\": \"Բերել սկզբնական վիճակի\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/id.json",
    "content": "{\n  \"name\": \"id\",\n  \"options\": {\n    \"months\": [\n      \"Januari\",\n      \"Februari\",\n      \"Maret\",\n      \"April\",\n      \"Mei\",\n      \"Juni\",\n      \"Juli\",\n      \"Agustus\",\n      \"September\",\n      \"Oktober\",\n      \"November\",\n      \"Desember\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"Mei\",\n      \"Jun\",\n      \"Jul\",\n      \"Agu\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Des\"\n    ],\n    \"days\": [\"Minggu\", \"Senin\", \"Selasa\", \"Rabu\", \"kamis\", \"Jumat\", \"Sabtu\"],\n    \"shortDays\": [\"Min\", \"Sen\", \"Sel\", \"Rab\", \"Kam\", \"Jum\", \"Sab\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Unduh SVG\",\n      \"exportToPNG\": \"Unduh PNG\",\n      \"exportToCSV\": \"Unduh CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Pilihan\",\n      \"selectionZoom\": \"Perbesar Pilihan\",\n      \"zoomIn\": \"Perbesar\",\n      \"zoomOut\": \"Perkecil\",\n      \"pan\": \"Geser\",\n      \"reset\": \"Atur Ulang Zoom\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/it.json",
    "content": "{\n  \"name\": \"it\",\n  \"options\": {\n    \"months\": [\n      \"Gennaio\",\n      \"Febbraio\",\n      \"Marzo\",\n      \"Aprile\",\n      \"Maggio\",\n      \"Giugno\",\n      \"Luglio\",\n      \"Agosto\",\n      \"Settembre\",\n      \"Ottobre\",\n      \"Novembre\",\n      \"Dicembre\"\n    ],\n    \"shortMonths\": [\n      \"Gen\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"Mag\",\n      \"Giu\",\n      \"Lug\",\n      \"Ago\",\n      \"Set\",\n      \"Ott\",\n      \"Nov\",\n      \"Dic\"\n    ],\n    \"days\": [\n      \"Domenica\",\n      \"Lunedì\",\n      \"Martedì\",\n      \"Mercoledì\",\n      \"Giovedì\",\n      \"Venerdì\",\n      \"Sabato\"\n    ],\n    \"shortDays\": [\"Dom\", \"Lun\", \"Mar\", \"Mer\", \"Gio\", \"Ven\", \"Sab\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Scarica SVG\",\n      \"exportToPNG\": \"Scarica PNG\",\n      \"exportToCSV\": \"Scarica CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Selezione\",\n      \"selectionZoom\": \"Seleziona Zoom\",\n      \"zoomIn\": \"Zoom In\",\n      \"zoomOut\": \"Zoom Out\",\n      \"pan\": \"Sposta\",\n      \"reset\": \"Reimposta Zoom\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/ja.json",
    "content": "{\n  \"name\": \"ja\",\n  \"options\": {\n    \"months\": [\n      \"1月\",\n      \"2月\",\n      \"3月\",\n      \"4月\",\n      \"5月\",\n      \"6月\",\n      \"7月\",\n      \"8月\",\n      \"9月\",\n      \"10月\",\n      \"11月\",\n      \"12月\"\n    ],\n    \"shortMonths\": [\n      \"1月\",\n      \"2月\",\n      \"3月\",\n      \"4月\",\n      \"5月\",\n      \"6月\",\n      \"7月\",\n      \"8月\",\n      \"9月\",\n      \"10月\",\n      \"11月\",\n      \"12月\"\n    ],\n    \"days\": [\n      \"日曜日\",\n      \"月曜日\",\n      \"火曜日\",\n      \"水曜日\",\n      \"木曜日\",\n      \"金曜日\",\n      \"土曜日\"\n    ],\n    \"shortDays\": [\"日\", \"月\", \"火\", \"水\", \"木\", \"金\", \"土\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"SVGダウンロード\",\n      \"exportToPNG\": \"PNGダウンロード\",\n      \"exportToCSV\": \"CSVダウンロード\",\n      \"menu\": \"メニュー\",\n      \"selection\": \"選択\",\n      \"selectionZoom\": \"選択ズーム\",\n      \"zoomIn\": \"拡大\",\n      \"zoomOut\": \"縮小\",\n      \"pan\": \"パン\",\n      \"reset\": \"ズームリセット\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/ka.json",
    "content": "{\n  \"name\": \"ka\",\n  \"options\": {\n    \"months\": [\n      \"იანვარი\",\n      \"თებერვალი\",\n      \"მარტი\",\n      \"აპრილი\",\n      \"მაისი\",\n      \"ივნისი\",\n      \"ივლისი\",\n      \"აგვისტო\",\n      \"სექტემბერი\",\n      \"ოქტომბერი\",\n      \"ნოემბერი\",\n      \"დეკემბერი\"\n    ],\n    \"shortMonths\": [\n      \"იან\",\n      \"თებ\",\n      \"მარ\",\n      \"აპრ\",\n      \"მაი\",\n      \"ივნ\",\n      \"ივლ\",\n      \"აგვ\",\n      \"სექ\",\n      \"ოქტ\",\n      \"ნოე\",\n      \"დეკ\"\n    ],\n    \"days\": [\n      \"კვირა\",\n      \"ორშაბათი\",\n      \"სამშაბათი\",\n      \"ოთხშაბათი\",\n      \"ხუთშაბათი\",\n      \"პარასკევი\",\n      \"შაბათი\"\n    ],\n    \"shortDays\": [\"კვი\", \"ორშ\", \"სამ\", \"ოთხ\", \"ხუთ\", \"პარ\", \"შაბ\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"გადმოქაჩე SVG\",\n      \"exportToPNG\": \"გადმოქაჩე PNG\",\n      \"exportToCSV\": \"გადმოქაჩე CSV\",\n      \"menu\": \"მენიუ\",\n      \"selection\": \"არჩევა\",\n      \"selectionZoom\": \"არჩეულის გადიდება\",\n      \"zoomIn\": \"გადიდება\",\n      \"zoomOut\": \"დაპატარაება\",\n      \"pan\": \"გადაჩოჩება\",\n      \"reset\": \"გადიდების გაუქმება\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/ko.json",
    "content": "{\n  \"name\": \"ko\",\n  \"options\": {\n    \"months\": [\n      \"1월\",\n      \"2월\",\n      \"3월\",\n      \"4월\",\n      \"5월\",\n      \"6월\",\n      \"7월\",\n      \"8월\",\n      \"9월\",\n      \"10월\",\n      \"11월\",\n      \"12월\"\n    ],\n    \"shortMonths\": [\n      \"1월\",\n      \"2월\",\n      \"3월\",\n      \"4월\",\n      \"5월\",\n      \"6월\",\n      \"7월\",\n      \"8월\",\n      \"9월\",\n      \"10월\",\n      \"11월\",\n      \"12월\"\n    ],\n    \"days\": [\n      \"일요일\",\n      \"월요일\",\n      \"화요일\",\n      \"수요일\",\n      \"목요일\",\n      \"금요일\",\n      \"토요일\"\n    ],\n    \"shortDays\": [\"일\", \"월\", \"화\", \"수\", \"목\", \"금\", \"토\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"SVG 다운로드\",\n      \"exportToPNG\": \"PNG 다운로드\",\n      \"exportToCSV\": \"CSV 다운로드\",\n      \"menu\": \"메뉴\",\n      \"selection\": \"선택\",\n      \"selectionZoom\": \"선택영역 확대\",\n      \"zoomIn\": \"확대\",\n      \"zoomOut\": \"축소\",\n      \"pan\": \"패닝\",\n      \"reset\": \"원래대로\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/lt.json",
    "content": "{\n  \"name\": \"lt\",\n  \"options\": {\n    \"months\": [\n      \"Sausis\",\n      \"Vasaris\",\n      \"Kovas\",\n      \"Balandis\",\n      \"Gegužė\",\n      \"Birželis\",\n      \"Liepa\",\n      \"Rugpjūtis\",\n      \"Rugsėjis\",\n      \"Spalis\",\n      \"Lapkritis\",\n      \"Gruodis\"\n    ],\n    \"shortMonths\": [\n      \"Sau\",\n      \"Vas\",\n      \"Kov\",\n      \"Bal\",\n      \"Geg\",\n      \"Bir\",\n      \"Lie\",\n      \"Rgp\",\n      \"Rgs\",\n      \"Spl\",\n      \"Lap\",\n      \"Grd\"\n    ],\n    \"days\": [\n      \"Sekmadienis\",\n      \"Pirmadienis\",\n      \"Antradienis\",\n      \"Trečiadienis\",\n      \"Ketvirtadienis\",\n      \"Penktadienis\",\n      \"Šeštadienis\"\n    ],\n    \"shortDays\": [\"Sk\", \"Per\", \"An\", \"Tr\", \"Kt\", \"Pn\", \"Št\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Atsisiųsti SVG\",\n      \"exportToPNG\": \"Atsisiųsti PNG\",\n      \"exportToCSV\": \"Atsisiųsti CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Pasirinkimas\",\n      \"selectionZoom\": \"Zoom: Pasirinkimas\",\n      \"zoomIn\": \"Zoom: Priartinti\",\n      \"zoomOut\": \"Zoom: Atitolinti\",\n      \"pan\": \"Perkėlimas\",\n      \"reset\": \"Atstatyti\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/lv.json",
    "content": "{\n  \"name\": \"lv\",\n  \"options\": {\n    \"months\": [\n      \"janvāris\",\n      \"februāris\",\n      \"marts\",\n      \"aprīlis\",\n      \"maijs\",\n      \"jūnijs\",\n      \"jūlijs\",\n      \"augusts\",\n      \"septembris\",\n      \"oktobris\",\n      \"novembris\",\n      \"decembris\"\n    ],\n    \"shortMonths\": [\n      \"janv\",\n      \"febr\",\n      \"marts\",\n      \"apr\",\n      \"maijs\",\n      \"jūn\",\n      \"jūl\",\n      \"aug\",\n      \"sept\",\n      \"okt\",\n      \"nov\",\n      \"dec\"\n    ],\n    \"days\": [\n      \"svētdiena\",\n      \"pirmdiena\",\n      \"otrdiena\",\n      \"trešdiena\",\n      \"ceturtdiena\",\n      \"piektdiena\",\n      \"sestdiena\"\n    ],\n    \"shortDays\": [\n      \"Sv\",\n      \"P\",\n      \"O\",\n      \"T\",\n      \"C\",\n      \"P\",\n      \"S\"\n    ],\n    \"toolbar\": {\n      \"exportToSVG\": \"Lejuplādēt SVG\",\n      \"exportToPNG\": \"Lejuplādēt PNG\",\n      \"exportToCSV\": \"Lejuplādēt CSV\",\n      \"menu\": \"Izvēlne\",\n      \"selection\": \"Atlase\",\n      \"selectionZoom\": \"Pietuvināt atlasi\",\n      \"zoomIn\": \"Pietuvināt\",\n      \"zoomOut\": \"Attālināt\",\n      \"pan\": \"Pārvietoties diagrammā\",\n      \"reset\": \"Atiestatīt pietuvinājumu\"\n    }\n  }\n}\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/nb.json",
    "content": "{\n  \"name\": \"nb\",\n  \"options\": {\n    \"months\": [\n      \"Januar\",\n      \"Februar\",\n      \"Mars\",\n      \"April\",\n      \"Mai\",\n      \"Juni\",\n      \"Juli\",\n      \"August\",\n      \"September\",\n      \"Oktober\",\n      \"November\",\n      \"Desember\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"Mai\",\n      \"Jun\",\n      \"Jul\",\n      \"Aug\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Des\"\n    ],\n    \"days\": [\n      \"Søndag\",\n      \"Mandag\",\n      \"Tirsdag\",\n      \"Onsdag\",\n      \"Torsdag\",\n      \"Fredag\",\n      \"Lørdag\"\n    ],\n    \"shortDays\": [\"Sø\", \"Ma\", \"Ti\", \"On\", \"To\", \"Fr\", \"Lø\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Last ned SVG\",\n      \"exportToPNG\": \"Last ned PNG\",\n      \"exportToCSV\": \"Last ned CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Velg\",\n      \"selectionZoom\": \"Zoom: Velg\",\n      \"zoomIn\": \"Zoome inn\",\n      \"zoomOut\": \"Zoome ut\",\n      \"pan\": \"Skyving\",\n      \"reset\": \"Start på nytt\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/nl.json",
    "content": "{\n  \"name\": \"nl\",\n  \"options\": {\n    \"months\": [\n      \"Januari\",\n      \"Februari\",\n      \"Maart\",\n      \"April\",\n      \"Mei\",\n      \"Juni\",\n      \"Juli\",\n      \"Augustus\",\n      \"September\",\n      \"Oktober\",\n      \"November\",\n      \"December\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mrt\",\n      \"Apr\",\n      \"Mei\",\n      \"Jun\",\n      \"Jul\",\n      \"Aug\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Dec\"\n    ],\n    \"days\": [\n      \"Zondag\",\n      \"Maandag\",\n      \"Dinsdag\",\n      \"Woensdag\",\n      \"Donderdag\",\n      \"Vrijdag\",\n      \"Zaterdag\"\n    ],\n    \"shortDays\": [\"Zo\", \"Ma\", \"Di\", \"Wo\", \"Do\", \"Vr\", \"Za\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Download SVG\",\n      \"exportToPNG\": \"Download PNG\",\n      \"exportToCSV\": \"Download CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Selectie\",\n      \"selectionZoom\": \"Zoom selectie\",\n      \"zoomIn\": \"Zoom in\",\n      \"zoomOut\": \"Zoom out\",\n      \"pan\": \"Verplaatsen\",\n      \"reset\": \"Standaardwaarden\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/pl.json",
    "content": "{\n  \"name\": \"pl\",\n  \"options\": {\n    \"months\": [\n      \"Styczeń\",\n      \"Luty\",\n      \"Marzec\",\n      \"Kwiecień\",\n      \"Maj\",\n      \"Czerwiec\",\n      \"Lipiec\",\n      \"Sierpień\",\n      \"Wrzesień\",\n      \"Październik\",\n      \"Listopad\",\n      \"Grudzień\"\n    ],\n    \"shortMonths\": [\n      \"Sty\",\n      \"Lut\",\n      \"Mar\",\n      \"Kwi\",\n      \"Maj\",\n      \"Cze\",\n      \"Lip\",\n      \"Sie\",\n      \"Wrz\",\n      \"Paź\",\n      \"Lis\",\n      \"Gru\"\n    ],\n    \"days\": [\n      \"Niedziela\",\n      \"Poniedziałek\",\n      \"Wtorek\",\n      \"Środa\",\n      \"Czwartek\",\n      \"Piątek\",\n      \"Sobota\"\n    ],\n    \"shortDays\": [\"Nd\", \"Pn\", \"Wt\", \"Śr\", \"Cz\", \"Pt\", \"Sb\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Pobierz SVG\",\n      \"exportToPNG\": \"Pobierz PNG\",\n      \"exportToCSV\": \"Pobierz CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Wybieranie\",\n      \"selectionZoom\": \"Zoom: Wybieranie\",\n      \"zoomIn\": \"Zoom: Przybliż\",\n      \"zoomOut\": \"Zoom: Oddal\",\n      \"pan\": \"Przesuwanie\",\n      \"reset\": \"Resetuj\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/pt-br.json",
    "content": "{\n  \"name\": \"pt-br\",\n  \"options\": {\n    \"months\": [\n      \"Janeiro\",\n      \"Fevereiro\",\n      \"Março\",\n      \"Abril\",\n      \"Maio\",\n      \"Junho\",\n      \"Julho\",\n      \"Agosto\",\n      \"Setembro\",\n      \"Outubro\",\n      \"Novembro\",\n      \"Dezembro\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Fev\",\n      \"Mar\",\n      \"Abr\",\n      \"Mai\",\n      \"Jun\",\n      \"Jul\",\n      \"Ago\",\n      \"Set\",\n      \"Out\",\n      \"Nov\",\n      \"Dez\"\n    ],\n    \"days\": [\n      \"Domingo\",\n      \"Segunda\",\n      \"Terça\",\n      \"Quarta\",\n      \"Quinta\",\n      \"Sexta\",\n      \"Sábado\"\n    ],\n    \"shortDays\": [\"Dom\", \"Seg\", \"Ter\", \"Qua\", \"Qui\", \"Sex\", \"Sab\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Baixar SVG\",\n      \"exportToPNG\": \"Baixar PNG\",\n      \"exportToCSV\": \"Baixar CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Selecionar\",\n      \"selectionZoom\": \"Selecionar Zoom\",\n      \"zoomIn\": \"Aumentar\",\n      \"zoomOut\": \"Diminuir\",\n      \"pan\": \"Navegação\",\n      \"reset\": \"Reiniciar Zoom\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/pt.json",
    "content": "{\n  \"name\": \"pt\",\n  \"options\": {\n    \"months\": [\n      \"Janeiro\",\n      \"Fevereiro\",\n      \"Março\",\n      \"Abril\",\n      \"Maio\",\n      \"Junho\",\n      \"Julho\",\n      \"Agosto\",\n      \"Setembro\",\n      \"Outubro\",\n      \"Novembro\",\n      \"Dezembro\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Fev\",\n      \"Mar\",\n      \"Abr\",\n      \"Mai\",\n      \"Jun\",\n      \"Jul\",\n      \"Ag\",\n      \"Set\",\n      \"Out\",\n      \"Nov\",\n      \"Dez\"\n    ],\n    \"days\": [\n      \"Domingo\",\n      \"Segunda-feira\",\n      \"Terça-feira\",\n      \"Quarta-feira\",\n      \"Quinta-feira\",\n      \"Sexta-feira\",\n      \"Sábado\"\n    ],\n    \"shortDays\": [\"Do\", \"Se\", \"Te\", \"Qa\", \"Qi\", \"Sx\", \"Sa\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Baixar SVG\",\n      \"exportToPNG\": \"Baixar PNG\",\n      \"exportToCSV\": \"Baixar CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Selecionar\",\n      \"selectionZoom\": \"Zoom: Selecionar\",\n      \"zoomIn\": \"Zoom: Aumentar\",\n      \"zoomOut\": \"Zoom: Diminuir\",\n      \"pan\": \"Deslocamento\",\n      \"reset\": \"Redefinir\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/rs.json",
    "content": "{\n  \"name\": \"rs\",\n  \"options\": {\n    \"months\": [\n      \"Januar\",\n      \"Februar\",\n      \"Mart\",\n      \"April\",\n      \"Maj\",\n      \"Jun\",\n      \"Jul\",\n      \"Avgust\",\n      \"Septembar\",\n      \"Oktobar\",\n      \"Novembar\",\n      \"Decembar\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"Maj\",\n      \"Jun\",\n      \"Jul\",\n      \"Avg\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Dec\"\n    ],\n    \"days\": [\n      \"Nedelja\",\n      \"Ponedeljak\",\n      \"Utorak\",\n      \"Sreda\",\n      \"Četvrtak\",\n      \"Petak\",\n      \"Subota\"\n    ],\n    \"shortDays\": [\"Ned\", \"Pon\", \"Uto\", \"Sre\", \"Čet\", \"Pet\", \"Sub\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Preuzmi SVG\",\n      \"exportToPNG\": \"Preuzmi PNG\",\n      \"exportToCSV\": \"Preuzmi CSV\",\n      \"menu\": \"Meni\",\n      \"selection\": \"Odabir\",\n      \"selectionZoom\": \"Odabirno povećanje\",\n      \"zoomIn\": \"Uvećajte prikaz\",\n      \"zoomOut\": \"Umanjite prikaz\",\n      \"pan\": \"Pomeranje\",\n      \"reset\": \"Resetuj prikaz\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/ru.json",
    "content": "{\n  \"name\": \"ru\",\n  \"options\": {\n    \"months\": [\n      \"Январь\",\n      \"Февраль\",\n      \"Март\",\n      \"Апрель\",\n      \"Май\",\n      \"Июнь\",\n      \"Июль\",\n      \"Август\",\n      \"Сентябрь\",\n      \"Октябрь\",\n      \"Ноябрь\",\n      \"Декабрь\"\n    ],\n    \"shortMonths\": [\n      \"Янв\",\n      \"Фев\",\n      \"Мар\",\n      \"Апр\",\n      \"Май\",\n      \"Июн\",\n      \"Июл\",\n      \"Авг\",\n      \"Сен\",\n      \"Окт\",\n      \"Ноя\",\n      \"Дек\"\n    ],\n    \"days\": [\n      \"Воскресенье\",\n      \"Понедельник\",\n      \"Вторник\",\n      \"Среда\",\n      \"Четверг\",\n      \"Пятница\",\n      \"Суббота\"\n    ],\n    \"shortDays\": [\"Вс\", \"Пн\", \"Вт\", \"Ср\", \"Чт\", \"Пт\", \"Сб\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Сохранить SVG\",\n      \"exportToPNG\": \"Сохранить PNG\",\n      \"exportToCSV\": \"Сохранить CSV\",\n      \"menu\": \"Меню\",\n      \"selection\": \"Выбор\",\n      \"selectionZoom\": \"Выбор с увеличением\",\n      \"zoomIn\": \"Увеличить\",\n      \"zoomOut\": \"Уменьшить\",\n      \"pan\": \"Перемещение\",\n      \"reset\": \"Сбросить увеличение\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/se.json",
    "content": "{\n  \"name\": \"se\",\n  \"options\": {\n    \"months\": [\n      \"Januari\",\n      \"Februari\",\n      \"Mars\",\n      \"April\",\n      \"Maj\",\n      \"Juni\",\n      \"Juli\",\n      \"Augusti\",\n      \"September\",\n      \"Oktober\",\n      \"November\",\n      \"December\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"Maj\",\n      \"Juni\",\n      \"Juli\",\n      \"Aug\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Dec\"\n    ],\n    \"days\": [\n      \"Söndag\",\n      \"Måndag\",\n      \"Tisdag\",\n      \"Onsdag\",\n      \"Torsdag\",\n      \"Fredag\",\n      \"Lördag\"\n    ],\n    \"shortDays\": [\"Sön\", \"Mån\", \"Tis\", \"Ons\", \"Tor\", \"Fre\", \"Lör\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Ladda SVG\",\n      \"exportToPNG\": \"Ladda PNG\",\n      \"exportToCSV\": \"Ladda CSV\",\n      \"menu\": \"Meny\",\n      \"selection\": \"Selektion\",\n      \"selectionZoom\": \"Val av zoom\",\n      \"zoomIn\": \"Zooma in\",\n      \"zoomOut\": \"Zooma ut\",\n      \"pan\": \"Panorering\",\n      \"reset\": \"Återställ zoomning\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/sk.json",
    "content": "{\n  \"name\": \"sk\",\n  \"options\": {\n    \"months\": [\n      \"Január\",\n      \"Február\",\n      \"Marec\",\n      \"Apríl\",\n      \"Máj\",\n      \"Jún\",\n      \"Júl\",\n      \"August\",\n      \"September\",\n      \"Október\",\n      \"November\",\n      \"December\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"Máj\",\n      \"Jún\",\n      \"Júl\",\n      \"Aug\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Dec\"\n    ],\n    \"days\": [\n      \"Nedeľa\",\n      \"Pondelok\",\n      \"Utorok\",\n      \"Streda\",\n      \"Štvrtok\",\n      \"Piatok\",\n      \"Sobota\"\n    ],\n    \"shortDays\": [\"Ne\", \"Po\", \"Ut\", \"St\", \"Št\", \"Pi\", \"So\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Stiahnuť SVG\",\n      \"exportToPNG\": \"Stiahnuť PNG\",\n      \"exportToCSV\": \"Stiahnuť CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Vyberanie\",\n      \"selectionZoom\": \"Zoom: Vyberanie\",\n      \"zoomIn\": \"Zoom: Priblížiť\",\n      \"zoomOut\": \"Zoom: Vzdialiť\",\n      \"pan\": \"Presúvanie\",\n      \"reset\": \"Resetovať\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/sl.json",
    "content": "{\n  \"name\": \"sl\",\n  \"options\": {\n    \"months\": [\n      \"Januar\",\n      \"Februar\",\n      \"Marec\",\n      \"April\",\n      \"Maj\",\n      \"Junij\",\n      \"Julij\",\n      \"Avgust\",\n      \"Septemer\",\n      \"Oktober\",\n      \"November\",\n      \"December\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Feb\",\n      \"Mar\",\n      \"Apr\",\n      \"Maj\",\n      \"Jun\",\n      \"Jul\",\n      \"Avg\",\n      \"Sep\",\n      \"Okt\",\n      \"Nov\",\n      \"Dec\"\n    ],\n    \"days\": [\n      \"Nedelja\",\n      \"Ponedeljek\",\n      \"Torek\",\n      \"Sreda\",\n      \"Četrtek\",\n      \"Petek\",\n      \"Sobota\"\n    ],\n    \"shortDays\": [\"Ne\", \"Po\", \"To\", \"Sr\", \"Če\", \"Pe\", \"So\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Prenesi SVG\",\n      \"exportToPNG\": \"Prenesi PNG\",\n      \"exportToCSV\": \"Prenesi CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Izbiranje\",\n      \"selectionZoom\": \"Zoom: Izbira\",\n      \"zoomIn\": \"Zoom: Približaj\",\n      \"zoomOut\": \"Zoom: Oddalji\",\n      \"pan\": \"Pomikanje\",\n      \"reset\": \"Resetiraj\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/sq.json",
    "content": "{\n  \"name\": \"sq\",\n  \"options\": {\n    \"months\": [\n      \"Janar\",\n      \"Shkurt\",\n      \"Mars\",\n      \"Prill\",\n      \"Maj\",\n      \"Qershor\",\n      \"Korrik\",\n      \"Gusht\",\n      \"Shtator\",\n      \"Tetor\",\n      \"Nëntor\",\n      \"Dhjetor\"\n    ],\n    \"shortMonths\": [\n      \"Jan\",\n      \"Shk\",\n      \"Mar\",\n      \"Pr\",\n      \"Maj\",\n      \"Qer\",\n      \"Korr\",\n      \"Gush\",\n      \"Sht\",\n      \"Tet\",\n      \"Nën\",\n      \"Dhj\"\n    ],\n    \"days\": [\n      \"e Dielë\",\n      \"e Hënë\",\n      \"e Martë\",\n      \"e Mërkurë\",\n      \"e Enjte\",\n      \"e Premte\",\n      \"e Shtunë\"\n    ],\n    \"shortDays\": [\"Die\", \"Hën\", \"Mar\", \"Mër\", \"Enj\", \"Pre\", \"Sht\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Shkarko SVG\",\n      \"exportToPNG\": \"Shkarko PNG\",\n      \"exportToCSV\": \"Shkarko CSV\",\n      \"menu\": \"Menu\",\n      \"selection\": \"Seleksiono\",\n      \"selectionZoom\": \"Seleksiono Zmadhim\",\n      \"zoomIn\": \"Zmadho\",\n      \"zoomOut\": \"Zvogëlo\",\n      \"pan\": \"Spostoje\",\n      \"reset\": \"Rikthe dimensionin\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/th.json",
    "content": "{\n  \"name\": \"th\",\n  \"options\": {\n    \"months\": [\n      \"มกราคม\",\n      \"กุมภาพันธ์\",\n      \"มีนาคม\",\n      \"เมษายน\",\n      \"พฤษภาคม\",\n      \"มิถุนายน\",\n      \"กรกฎาคม\",\n      \"สิงหาคม\",\n      \"กันยายน\",\n      \"ตุลาคม\",\n      \"พฤศจิกายน\",\n      \"ธันวาคม\"\n    ],\n    \"shortMonths\": [\n      \"ม.ค.\",\n      \"ก.พ.\",\n      \"มี.ค.\",\n      \"เม.ย.\",\n      \"พ.ค.\",\n      \"มิ.ย.\",\n      \"ก.ค.\",\n      \"ส.ค.\",\n      \"ก.ย.\",\n      \"ต.ค.\",\n      \"พ.ย.\",\n      \"ธ.ค.\"\n    ],\n    \"days\": [\n      \"อาทิตย์\",\n      \"จันทร์\",\n      \"อังคาร\",\n      \"พุธ\",\n      \"พฤหัสบดี\",\n      \"ศุกร์\",\n      \"เสาร์\"\n    ],\n    \"shortDays\": [\"อา\", \"จ\", \"อ\", \"พ\", \"พฤ\", \"ศ\", \"ส\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"ดาวน์โหลด SVG\",\n      \"exportToPNG\": \"ดาวน์โหลด PNG\",\n      \"exportToCSV\": \"ดาวน์โหลด CSV\",\n      \"menu\": \"เมนู\",\n      \"selection\": \"เลือก\",\n      \"selectionZoom\": \"เลือกจุดที่จะซูม\",\n      \"zoomIn\": \"ซูมเข้า\",\n      \"zoomOut\": \"ซูมออก\",\n      \"pan\": \"ปรากฎว่า\",\n      \"reset\": \"รีเซ็ตการซูม\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/tr.json",
    "content": "{\n  \"name\": \"tr\",\n  \"options\": {\n    \"months\": [\n      \"Ocak\",\n      \"Şubat\",\n      \"Mart\",\n      \"Nisan\",\n      \"Mayıs\",\n      \"Haziran\",\n      \"Temmuz\",\n      \"Ağustos\",\n      \"Eylül\",\n      \"Ekim\",\n      \"Kasım\",\n      \"Aralık\"\n    ],\n    \"shortMonths\": [\n      \"Oca\",\n      \"Şub\",\n      \"Mar\",\n      \"Nis\",\n      \"May\",\n      \"Haz\",\n      \"Tem\",\n      \"Ağu\",\n      \"Eyl\",\n      \"Eki\",\n      \"Kas\",\n      \"Ara\"\n    ],\n    \"days\": [\n      \"Pazar\",\n      \"Pazartesi\",\n      \"Salı\",\n      \"Çarşamba\",\n      \"Perşembe\",\n      \"Cuma\",\n      \"Cumartesi\"\n    ],\n    \"shortDays\": [\"Paz\", \"Pzt\", \"Sal\", \"Çar\", \"Per\", \"Cum\", \"Cmt\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"SVG İndir\",\n      \"exportToPNG\": \"PNG İndir\",\n      \"exportToCSV\": \"CSV İndir\",\n      \"menu\": \"Menü\",\n      \"selection\": \"Seçim\",\n      \"selectionZoom\": \"Seçim Yakınlaştır\",\n      \"zoomIn\": \"Yakınlaştır\",\n      \"zoomOut\": \"Uzaklaştır\",\n      \"pan\": \"Kaydır\",\n      \"reset\": \"Yakınlaştırmayı Sıfırla\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/ua.json",
    "content": "{\n  \"name\": \"ua\",\n  \"options\": {\n    \"months\": [\n      \"Січень\",\n      \"Лютий\",\n      \"Березень\",\n      \"Квітень\",\n      \"Травень\",\n      \"Червень\",\n      \"Липень\",\n      \"Серпень\",\n      \"Вересень\",\n      \"Жовтень\",\n      \"Листопад\",\n      \"Грудень\"\n    ],\n    \"shortMonths\": [\n      \"Січ\",\n      \"Лют\",\n      \"Бер\",\n      \"Кві\",\n      \"Тра\",\n      \"Чер\",\n      \"Лип\",\n      \"Сер\",\n      \"Вер\",\n      \"Жов\",\n      \"Лис\",\n      \"Гру\"\n    ],\n    \"days\": [\n      \"Неділя\",\n      \"Понеділок\",\n      \"Вівторок\",\n      \"Середа\",\n      \"Четвер\",\n      \"П'ятниця\",\n      \"Субота\"\n    ],\n    \"shortDays\": [\"Нд\", \"Пн\", \"Вт\", \"Ср\", \"Чт\", \"Пт\", \"Сб\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"Зберегти SVG\",\n      \"exportToPNG\": \"Зберегти PNG\",\n      \"exportToCSV\": \"Зберегти CSV\",\n      \"menu\": \"Меню\",\n      \"selection\": \"Вибір\",\n      \"selectionZoom\": \"Вибір із збільшенням\",\n      \"zoomIn\": \"Збільшити\",\n      \"zoomOut\": \"Зменшити\",\n      \"pan\": \"Переміщення\",\n      \"reset\": \"Скинути збільшення\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/zh-cn.json",
    "content": "{\n  \"name\": \"zh-cn\",\n  \"options\": {\n    \"months\": [\n      \"一月\",\n      \"二月\",\n      \"三月\",\n      \"四月\",\n      \"五月\",\n      \"六月\",\n      \"七月\",\n      \"八月\",\n      \"九月\",\n      \"十月\",\n      \"十一月\",\n      \"十二月\"\n    ],\n    \"shortMonths\": [\n      \"一月\",\n      \"二月\",\n      \"三月\",\n      \"四月\",\n      \"五月\",\n      \"六月\",\n      \"七月\",\n      \"八月\",\n      \"九月\",\n      \"十月\",\n      \"十一月\",\n      \"十二月\"\n    ],\n    \"days\": [\n      \"星期天\",\n      \"星期一\",\n      \"星期二\",\n      \"星期三\",\n      \"星期四\",\n      \"星期五\",\n      \"星期六\"\n    ],\n    \"shortDays\": [\"周日\", \"周一\", \"周二\", \"周三\", \"周四\", \"周五\", \"周六\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"下载 SVG\",\n      \"exportToPNG\": \"下载 PNG\",\n      \"exportToCSV\": \"下载 CSV\",\n      \"menu\": \"菜单\",\n      \"selection\": \"选择\",\n      \"selectionZoom\": \"选择缩放\",\n      \"zoomIn\": \"放大\",\n      \"zoomOut\": \"缩小\",\n      \"pan\": \"平移\",\n      \"reset\": \"重置缩放\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/apexcharts/locales/zh-tw.json",
    "content": "{\n  \"name\": \"zh-tw\",\n  \"options\": {\n    \"months\": [\n      \"一月\",\n      \"二月\",\n      \"三月\",\n      \"四月\",\n      \"五月\",\n      \"六月\",\n      \"七月\",\n      \"八月\",\n      \"九月\",\n      \"十月\",\n      \"十一月\",\n      \"十二月\"\n    ],\n    \"shortMonths\": [\n      \"一月\",\n      \"二月\",\n      \"三月\",\n      \"四月\",\n      \"五月\",\n      \"六月\",\n      \"七月\",\n      \"八月\",\n      \"九月\",\n      \"十月\",\n      \"十一月\",\n      \"十二月\"\n    ],\n    \"days\": [\n      \"星期日\",\n      \"星期一\",\n      \"星期二\",\n      \"星期三\",\n      \"星期四\",\n      \"星期五\",\n      \"星期六\"\n    ],\n    \"shortDays\": [\"週日\", \"週一\", \"週二\", \"週三\", \"週四\", \"週五\", \"週六\"],\n    \"toolbar\": {\n      \"exportToSVG\": \"下載 SVG\",\n      \"exportToPNG\": \"下載 PNG\",\n      \"exportToCSV\": \"下載 CSV\",\n      \"menu\": \"菜單\",\n      \"selection\": \"選擇\",\n      \"selectionZoom\": \"選擇縮放\",\n      \"zoomIn\": \"放大\",\n      \"zoomOut\": \"縮小\",\n      \"pan\": \"平移\",\n      \"reset\": \"重置縮放\"\n    }\n  }\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap-grid.css",
    "content": "/*!\n * Bootstrap Grid v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  width: 100%;\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  margin-right: auto;\n  margin-left: auto;\n}\n\n@media (min-width: 576px) {\n  .container-sm, .container {\n    max-width: 540px;\n  }\n}\n@media (min-width: 768px) {\n  .container-md, .container-sm, .container {\n    max-width: 720px;\n  }\n}\n@media (min-width: 992px) {\n  .container-lg, .container-md, .container-sm, .container {\n    max-width: 960px;\n  }\n}\n@media (min-width: 1200px) {\n  .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1140px;\n  }\n}\n@media (min-width: 1400px) {\n  .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1320px;\n  }\n}\n.row {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  display: flex;\n  flex-wrap: wrap;\n  margin-top: calc(-1 * var(--bs-gutter-y));\n  margin-right: calc(-0.5 * var(--bs-gutter-x));\n  margin-left: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n  box-sizing: border-box;\n  flex-shrink: 0;\n  width: 100%;\n  max-width: 100%;\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  margin-top: var(--bs-gutter-y);\n}\n\n.col {\n  flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.row-cols-1 > * {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.row-cols-2 > * {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.row-cols-3 > * {\n  flex: 0 0 auto;\n  width: 33.3333333333%;\n}\n\n.row-cols-4 > * {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.row-cols-5 > * {\n  flex: 0 0 auto;\n  width: 20%;\n}\n\n.row-cols-6 > * {\n  flex: 0 0 auto;\n  width: 16.6666666667%;\n}\n\n.col-auto {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.col-1 {\n  flex: 0 0 auto;\n  width: 8.33333333%;\n}\n\n.col-2 {\n  flex: 0 0 auto;\n  width: 16.66666667%;\n}\n\n.col-3 {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.col-4 {\n  flex: 0 0 auto;\n  width: 33.33333333%;\n}\n\n.col-5 {\n  flex: 0 0 auto;\n  width: 41.66666667%;\n}\n\n.col-6 {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.col-7 {\n  flex: 0 0 auto;\n  width: 58.33333333%;\n}\n\n.col-8 {\n  flex: 0 0 auto;\n  width: 66.66666667%;\n}\n\n.col-9 {\n  flex: 0 0 auto;\n  width: 75%;\n}\n\n.col-10 {\n  flex: 0 0 auto;\n  width: 83.33333333%;\n}\n\n.col-11 {\n  flex: 0 0 auto;\n  width: 91.66666667%;\n}\n\n.col-12 {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.offset-1 {\n  margin-left: 8.33333333%;\n}\n\n.offset-2 {\n  margin-left: 16.66666667%;\n}\n\n.offset-3 {\n  margin-left: 25%;\n}\n\n.offset-4 {\n  margin-left: 33.33333333%;\n}\n\n.offset-5 {\n  margin-left: 41.66666667%;\n}\n\n.offset-6 {\n  margin-left: 50%;\n}\n\n.offset-7 {\n  margin-left: 58.33333333%;\n}\n\n.offset-8 {\n  margin-left: 66.66666667%;\n}\n\n.offset-9 {\n  margin-left: 75%;\n}\n\n.offset-10 {\n  margin-left: 83.33333333%;\n}\n\n.offset-11 {\n  margin-left: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n  --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n  --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n  --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n  --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n  --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n  --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n  --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n  --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n  --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n  --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n  --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n  --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n  .col-sm {\n    flex: 1 0 0%;\n  }\n  .row-cols-sm-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-sm-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-sm-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-sm-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-sm-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-sm-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-sm-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-sm-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-sm-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-sm-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-sm-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-sm-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-sm-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-sm-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-sm-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-sm-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-sm-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-sm-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-sm-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-sm-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-sm-0 {\n    margin-left: 0;\n  }\n  .offset-sm-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-sm-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-sm-3 {\n    margin-left: 25%;\n  }\n  .offset-sm-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-sm-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-sm-6 {\n    margin-left: 50%;\n  }\n  .offset-sm-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-sm-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-sm-9 {\n    margin-left: 75%;\n  }\n  .offset-sm-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-sm-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-sm-0,\n.gx-sm-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-sm-0,\n.gy-sm-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-sm-1,\n.gx-sm-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-sm-1,\n.gy-sm-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-sm-2,\n.gx-sm-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-sm-2,\n.gy-sm-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-sm-3,\n.gx-sm-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-sm-3,\n.gy-sm-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-sm-4,\n.gx-sm-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-sm-4,\n.gy-sm-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-sm-5,\n.gx-sm-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-sm-5,\n.gy-sm-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 768px) {\n  .col-md {\n    flex: 1 0 0%;\n  }\n  .row-cols-md-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-md-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-md-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-md-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-md-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-md-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-md-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-md-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-md-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-md-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-md-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-md-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-md-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-md-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-md-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-md-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-md-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-md-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-md-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-md-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-md-0 {\n    margin-left: 0;\n  }\n  .offset-md-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-md-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-md-3 {\n    margin-left: 25%;\n  }\n  .offset-md-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-md-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-md-6 {\n    margin-left: 50%;\n  }\n  .offset-md-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-md-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-md-9 {\n    margin-left: 75%;\n  }\n  .offset-md-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-md-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-md-0,\n.gx-md-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-md-0,\n.gy-md-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-md-1,\n.gx-md-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-md-1,\n.gy-md-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-md-2,\n.gx-md-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-md-2,\n.gy-md-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-md-3,\n.gx-md-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-md-3,\n.gy-md-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-md-4,\n.gx-md-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-md-4,\n.gy-md-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-md-5,\n.gx-md-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-md-5,\n.gy-md-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 992px) {\n  .col-lg {\n    flex: 1 0 0%;\n  }\n  .row-cols-lg-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-lg-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-lg-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-lg-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-lg-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-lg-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-lg-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-lg-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-lg-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-lg-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-lg-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-lg-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-lg-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-lg-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-lg-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-lg-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-lg-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-lg-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-lg-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-lg-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-lg-0 {\n    margin-left: 0;\n  }\n  .offset-lg-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-lg-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-lg-3 {\n    margin-left: 25%;\n  }\n  .offset-lg-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-lg-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-lg-6 {\n    margin-left: 50%;\n  }\n  .offset-lg-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-lg-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-lg-9 {\n    margin-left: 75%;\n  }\n  .offset-lg-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-lg-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-lg-0,\n.gx-lg-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-lg-0,\n.gy-lg-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-lg-1,\n.gx-lg-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-lg-1,\n.gy-lg-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-lg-2,\n.gx-lg-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-lg-2,\n.gy-lg-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-lg-3,\n.gx-lg-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-lg-3,\n.gy-lg-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-lg-4,\n.gx-lg-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-lg-4,\n.gy-lg-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-lg-5,\n.gx-lg-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-lg-5,\n.gy-lg-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1200px) {\n  .col-xl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xl-0 {\n    margin-left: 0;\n  }\n  .offset-xl-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-xl-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-xl-3 {\n    margin-left: 25%;\n  }\n  .offset-xl-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-xl-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-xl-6 {\n    margin-left: 50%;\n  }\n  .offset-xl-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-xl-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-xl-9 {\n    margin-left: 75%;\n  }\n  .offset-xl-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-xl-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-xl-0,\n.gx-xl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xl-0,\n.gy-xl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xl-1,\n.gx-xl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xl-1,\n.gy-xl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xl-2,\n.gx-xl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xl-2,\n.gy-xl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xl-3,\n.gx-xl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xl-3,\n.gy-xl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xl-4,\n.gx-xl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xl-4,\n.gy-xl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xl-5,\n.gx-xl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xl-5,\n.gy-xl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1400px) {\n  .col-xxl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xxl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xxl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xxl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xxl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xxl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xxl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xxl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xxl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xxl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xxl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xxl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xxl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xxl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xxl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xxl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xxl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xxl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xxl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xxl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xxl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xxl-0 {\n    margin-left: 0;\n  }\n  .offset-xxl-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-xxl-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-xxl-3 {\n    margin-left: 25%;\n  }\n  .offset-xxl-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-xxl-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-xxl-6 {\n    margin-left: 50%;\n  }\n  .offset-xxl-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-xxl-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-xxl-9 {\n    margin-left: 75%;\n  }\n  .offset-xxl-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-xxl-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-xxl-0,\n.gx-xxl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xxl-0,\n.gy-xxl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xxl-1,\n.gx-xxl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xxl-1,\n.gy-xxl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xxl-2,\n.gx-xxl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xxl-2,\n.gy-xxl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xxl-3,\n.gx-xxl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xxl-3,\n.gy-xxl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xxl-4,\n.gx-xxl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xxl-4,\n.gy-xxl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xxl-5,\n.gx-xxl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xxl-5,\n.gy-xxl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-grid {\n  display: grid !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: inline-flex !important;\n}\n\n.d-none {\n  display: none !important;\n}\n\n.flex-fill {\n  flex: 1 1 auto !important;\n}\n\n.flex-row {\n  flex-direction: row !important;\n}\n\n.flex-column {\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n  justify-content: space-evenly !important;\n}\n\n.align-items-start {\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  align-content: center !important;\n}\n\n.align-content-between {\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  align-self: auto !important;\n}\n\n.align-self-start {\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  align-self: stretch !important;\n}\n\n.order-first {\n  order: -1 !important;\n}\n\n.order-0 {\n  order: 0 !important;\n}\n\n.order-1 {\n  order: 1 !important;\n}\n\n.order-2 {\n  order: 2 !important;\n}\n\n.order-3 {\n  order: 3 !important;\n}\n\n.order-4 {\n  order: 4 !important;\n}\n\n.order-5 {\n  order: 5 !important;\n}\n\n.order-last {\n  order: 6 !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mx-0 {\n  margin-right: 0 !important;\n  margin-left: 0 !important;\n}\n\n.mx-1 {\n  margin-right: 0.25rem !important;\n  margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n  margin-right: 0.5rem !important;\n  margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n  margin-right: 1rem !important;\n  margin-left: 1rem !important;\n}\n\n.mx-4 {\n  margin-right: 1.5rem !important;\n  margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n  margin-right: 3rem !important;\n  margin-left: 3rem !important;\n}\n\n.mx-auto {\n  margin-right: auto !important;\n  margin-left: auto !important;\n}\n\n.my-0 {\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\n\n.my-1 {\n  margin-top: 0.25rem !important;\n  margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n  margin-top: 0.5rem !important;\n  margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n  margin-top: 1rem !important;\n  margin-bottom: 1rem !important;\n}\n\n.my-4 {\n  margin-top: 1.5rem !important;\n  margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n  margin-top: 3rem !important;\n  margin-bottom: 3rem !important;\n}\n\n.my-auto {\n  margin-top: auto !important;\n  margin-bottom: auto !important;\n}\n\n.mt-0 {\n  margin-top: 0 !important;\n}\n\n.mt-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n  margin-top: 1rem !important;\n}\n\n.mt-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n  margin-top: 3rem !important;\n}\n\n.mt-auto {\n  margin-top: auto !important;\n}\n\n.me-0 {\n  margin-right: 0 !important;\n}\n\n.me-1 {\n  margin-right: 0.25rem !important;\n}\n\n.me-2 {\n  margin-right: 0.5rem !important;\n}\n\n.me-3 {\n  margin-right: 1rem !important;\n}\n\n.me-4 {\n  margin-right: 1.5rem !important;\n}\n\n.me-5 {\n  margin-right: 3rem !important;\n}\n\n.me-auto {\n  margin-right: auto !important;\n}\n\n.mb-0 {\n  margin-bottom: 0 !important;\n}\n\n.mb-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n  margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n  margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n  margin-bottom: auto !important;\n}\n\n.ms-0 {\n  margin-left: 0 !important;\n}\n\n.ms-1 {\n  margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n  margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n  margin-left: 1rem !important;\n}\n\n.ms-4 {\n  margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n  margin-left: 3rem !important;\n}\n\n.ms-auto {\n  margin-left: auto !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.px-0 {\n  padding-right: 0 !important;\n  padding-left: 0 !important;\n}\n\n.px-1 {\n  padding-right: 0.25rem !important;\n  padding-left: 0.25rem !important;\n}\n\n.px-2 {\n  padding-right: 0.5rem !important;\n  padding-left: 0.5rem !important;\n}\n\n.px-3 {\n  padding-right: 1rem !important;\n  padding-left: 1rem !important;\n}\n\n.px-4 {\n  padding-right: 1.5rem !important;\n  padding-left: 1.5rem !important;\n}\n\n.px-5 {\n  padding-right: 3rem !important;\n  padding-left: 3rem !important;\n}\n\n.py-0 {\n  padding-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\n.py-1 {\n  padding-top: 0.25rem !important;\n  padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n  padding-top: 0.5rem !important;\n  padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n  padding-top: 1rem !important;\n  padding-bottom: 1rem !important;\n}\n\n.py-4 {\n  padding-top: 1.5rem !important;\n  padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n  padding-top: 3rem !important;\n  padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n  padding-top: 0 !important;\n}\n\n.pt-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n  padding-top: 1rem !important;\n}\n\n.pt-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n  padding-top: 3rem !important;\n}\n\n.pe-0 {\n  padding-right: 0 !important;\n}\n\n.pe-1 {\n  padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n  padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n  padding-right: 1rem !important;\n}\n\n.pe-4 {\n  padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n  padding-right: 3rem !important;\n}\n\n.pb-0 {\n  padding-bottom: 0 !important;\n}\n\n.pb-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n  padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n  padding-left: 0 !important;\n}\n\n.ps-1 {\n  padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n  padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n  padding-left: 1rem !important;\n}\n\n.ps-4 {\n  padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n  padding-left: 3rem !important;\n}\n\n@media (min-width: 576px) {\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-grid {\n    display: grid !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-sm-none {\n    display: none !important;\n  }\n  .flex-sm-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-row {\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-sm-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-sm-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-sm-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-sm-start {\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    align-self: stretch !important;\n  }\n  .order-sm-first {\n    order: -1 !important;\n  }\n  .order-sm-0 {\n    order: 0 !important;\n  }\n  .order-sm-1 {\n    order: 1 !important;\n  }\n  .order-sm-2 {\n    order: 2 !important;\n  }\n  .order-sm-3 {\n    order: 3 !important;\n  }\n  .order-sm-4 {\n    order: 4 !important;\n  }\n  .order-sm-5 {\n    order: 5 !important;\n  }\n  .order-sm-last {\n    order: 6 !important;\n  }\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mx-sm-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-sm-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-sm-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-sm-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-sm-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-sm-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-sm-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-sm-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-sm-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-sm-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-sm-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mt-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-sm-auto {\n    margin-top: auto !important;\n  }\n  .me-sm-0 {\n    margin-right: 0 !important;\n  }\n  .me-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .me-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .me-sm-auto {\n    margin-right: auto !important;\n  }\n  .mb-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-sm-0 {\n    margin-left: 0 !important;\n  }\n  .ms-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-sm-auto {\n    margin-left: auto !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .px-sm-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-sm-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-sm-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-sm-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-sm-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-sm-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-sm-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-sm-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-sm-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pt-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-sm-0 {\n    padding-right: 0 !important;\n  }\n  .pe-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-sm-0 {\n    padding-left: 0 !important;\n  }\n  .ps-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-sm-5 {\n    padding-left: 3rem !important;\n  }\n}\n@media (min-width: 768px) {\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-grid {\n    display: grid !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-md-none {\n    display: none !important;\n  }\n  .flex-md-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-md-row {\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-md-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-md-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-md-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-md-start {\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    align-self: stretch !important;\n  }\n  .order-md-first {\n    order: -1 !important;\n  }\n  .order-md-0 {\n    order: 0 !important;\n  }\n  .order-md-1 {\n    order: 1 !important;\n  }\n  .order-md-2 {\n    order: 2 !important;\n  }\n  .order-md-3 {\n    order: 3 !important;\n  }\n  .order-md-4 {\n    order: 4 !important;\n  }\n  .order-md-5 {\n    order: 5 !important;\n  }\n  .order-md-last {\n    order: 6 !important;\n  }\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mx-md-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-md-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-md-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-md-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-md-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-md-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-md-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-md-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-md-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-md-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-md-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-md-0 {\n    margin-top: 0 !important;\n  }\n  .mt-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-md-auto {\n    margin-top: auto !important;\n  }\n  .me-md-0 {\n    margin-right: 0 !important;\n  }\n  .me-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-md-3 {\n    margin-right: 1rem !important;\n  }\n  .me-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-md-5 {\n    margin-right: 3rem !important;\n  }\n  .me-md-auto {\n    margin-right: auto !important;\n  }\n  .mb-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-md-0 {\n    margin-left: 0 !important;\n  }\n  .ms-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-md-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-md-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-md-auto {\n    margin-left: auto !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .px-md-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-md-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-md-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-md-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-md-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-md-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-md-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-md-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-md-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-md-0 {\n    padding-top: 0 !important;\n  }\n  .pt-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-md-0 {\n    padding-right: 0 !important;\n  }\n  .pe-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-md-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-md-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-md-0 {\n    padding-left: 0 !important;\n  }\n  .ps-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-md-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-md-5 {\n    padding-left: 3rem !important;\n  }\n}\n@media (min-width: 992px) {\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-grid {\n    display: grid !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-lg-none {\n    display: none !important;\n  }\n  .flex-lg-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-row {\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-lg-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-lg-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-lg-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-lg-start {\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    align-self: stretch !important;\n  }\n  .order-lg-first {\n    order: -1 !important;\n  }\n  .order-lg-0 {\n    order: 0 !important;\n  }\n  .order-lg-1 {\n    order: 1 !important;\n  }\n  .order-lg-2 {\n    order: 2 !important;\n  }\n  .order-lg-3 {\n    order: 3 !important;\n  }\n  .order-lg-4 {\n    order: 4 !important;\n  }\n  .order-lg-5 {\n    order: 5 !important;\n  }\n  .order-lg-last {\n    order: 6 !important;\n  }\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mx-lg-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-lg-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-lg-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-lg-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-lg-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-lg-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-lg-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-lg-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-lg-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-lg-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-lg-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mt-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-lg-auto {\n    margin-top: auto !important;\n  }\n  .me-lg-0 {\n    margin-right: 0 !important;\n  }\n  .me-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .me-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .me-lg-auto {\n    margin-right: auto !important;\n  }\n  .mb-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-lg-0 {\n    margin-left: 0 !important;\n  }\n  .ms-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-lg-auto {\n    margin-left: auto !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .px-lg-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-lg-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-lg-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-lg-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-lg-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-lg-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-lg-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-lg-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-lg-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pt-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-lg-0 {\n    padding-right: 0 !important;\n  }\n  .pe-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-lg-0 {\n    padding-left: 0 !important;\n  }\n  .ps-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-lg-5 {\n    padding-left: 3rem !important;\n  }\n}\n@media (min-width: 1200px) {\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-grid {\n    display: grid !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xl-none {\n    display: none !important;\n  }\n  .flex-xl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-row {\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xl-first {\n    order: -1 !important;\n  }\n  .order-xl-0 {\n    order: 0 !important;\n  }\n  .order-xl-1 {\n    order: 1 !important;\n  }\n  .order-xl-2 {\n    order: 2 !important;\n  }\n  .order-xl-3 {\n    order: 3 !important;\n  }\n  .order-xl-4 {\n    order: 4 !important;\n  }\n  .order-xl-5 {\n    order: 5 !important;\n  }\n  .order-xl-last {\n    order: 6 !important;\n  }\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mx-xl-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-xl-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-xl-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-xl-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-xl-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-xl-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-xl-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-xl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xl-auto {\n    margin-top: auto !important;\n  }\n  .me-xl-0 {\n    margin-right: 0 !important;\n  }\n  .me-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .me-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .me-xl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xl-0 {\n    margin-left: 0 !important;\n  }\n  .ms-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-xl-auto {\n    margin-left: auto !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .px-xl-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-xl-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-xl-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-xl-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-xl-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-xl-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-xl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xl-0 {\n    padding-right: 0 !important;\n  }\n  .pe-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xl-0 {\n    padding-left: 0 !important;\n  }\n  .ps-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-xl-5 {\n    padding-left: 3rem !important;\n  }\n}\n@media (min-width: 1400px) {\n  .d-xxl-inline {\n    display: inline !important;\n  }\n  .d-xxl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xxl-block {\n    display: block !important;\n  }\n  .d-xxl-grid {\n    display: grid !important;\n  }\n  .d-xxl-table {\n    display: table !important;\n  }\n  .d-xxl-table-row {\n    display: table-row !important;\n  }\n  .d-xxl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xxl-flex {\n    display: flex !important;\n  }\n  .d-xxl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xxl-none {\n    display: none !important;\n  }\n  .flex-xxl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xxl-row {\n    flex-direction: row !important;\n  }\n  .flex-xxl-column {\n    flex-direction: column !important;\n  }\n  .flex-xxl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xxl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xxl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xxl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xxl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xxl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xxl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xxl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xxl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xxl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xxl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xxl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xxl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xxl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xxl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xxl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xxl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xxl-center {\n    align-items: center !important;\n  }\n  .align-items-xxl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xxl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xxl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xxl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xxl-center {\n    align-content: center !important;\n  }\n  .align-content-xxl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xxl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xxl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xxl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xxl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xxl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xxl-center {\n    align-self: center !important;\n  }\n  .align-self-xxl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xxl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xxl-first {\n    order: -1 !important;\n  }\n  .order-xxl-0 {\n    order: 0 !important;\n  }\n  .order-xxl-1 {\n    order: 1 !important;\n  }\n  .order-xxl-2 {\n    order: 2 !important;\n  }\n  .order-xxl-3 {\n    order: 3 !important;\n  }\n  .order-xxl-4 {\n    order: 4 !important;\n  }\n  .order-xxl-5 {\n    order: 5 !important;\n  }\n  .order-xxl-last {\n    order: 6 !important;\n  }\n  .m-xxl-0 {\n    margin: 0 !important;\n  }\n  .m-xxl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xxl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xxl-3 {\n    margin: 1rem !important;\n  }\n  .m-xxl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xxl-5 {\n    margin: 3rem !important;\n  }\n  .m-xxl-auto {\n    margin: auto !important;\n  }\n  .mx-xxl-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-xxl-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-xxl-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-xxl-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-xxl-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-xxl-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-xxl-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-xxl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xxl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xxl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xxl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xxl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xxl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xxl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xxl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xxl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xxl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xxl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xxl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xxl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xxl-auto {\n    margin-top: auto !important;\n  }\n  .me-xxl-0 {\n    margin-right: 0 !important;\n  }\n  .me-xxl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-xxl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-xxl-3 {\n    margin-right: 1rem !important;\n  }\n  .me-xxl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-xxl-5 {\n    margin-right: 3rem !important;\n  }\n  .me-xxl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xxl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xxl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xxl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xxl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xxl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xxl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xxl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xxl-0 {\n    margin-left: 0 !important;\n  }\n  .ms-xxl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-xxl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-xxl-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-xxl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-xxl-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-xxl-auto {\n    margin-left: auto !important;\n  }\n  .p-xxl-0 {\n    padding: 0 !important;\n  }\n  .p-xxl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xxl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xxl-3 {\n    padding: 1rem !important;\n  }\n  .p-xxl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xxl-5 {\n    padding: 3rem !important;\n  }\n  .px-xxl-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-xxl-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-xxl-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-xxl-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-xxl-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-xxl-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-xxl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xxl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xxl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xxl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xxl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xxl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xxl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xxl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xxl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xxl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xxl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xxl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xxl-0 {\n    padding-right: 0 !important;\n  }\n  .pe-xxl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-xxl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-xxl-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-xxl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-xxl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xxl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xxl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xxl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xxl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xxl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xxl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xxl-0 {\n    padding-left: 0 !important;\n  }\n  .ps-xxl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-xxl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-xxl-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-xxl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-xxl-5 {\n    padding-left: 3rem !important;\n  }\n}\n@media print {\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-grid {\n    display: grid !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-print-none {\n    display: none !important;\n  }\n}\n\n/*# sourceMappingURL=bootstrap-grid.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap-grid.rtl.css",
    "content": "/*!\n * Bootstrap Grid v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  width: 100%;\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  margin-left: auto;\n  margin-right: auto;\n}\n\n@media (min-width: 576px) {\n  .container-sm, .container {\n    max-width: 540px;\n  }\n}\n@media (min-width: 768px) {\n  .container-md, .container-sm, .container {\n    max-width: 720px;\n  }\n}\n@media (min-width: 992px) {\n  .container-lg, .container-md, .container-sm, .container {\n    max-width: 960px;\n  }\n}\n@media (min-width: 1200px) {\n  .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1140px;\n  }\n}\n@media (min-width: 1400px) {\n  .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1320px;\n  }\n}\n.row {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  display: flex;\n  flex-wrap: wrap;\n  margin-top: calc(-1 * var(--bs-gutter-y));\n  margin-left: calc(-0.5 * var(--bs-gutter-x));\n  margin-right: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n  box-sizing: border-box;\n  flex-shrink: 0;\n  width: 100%;\n  max-width: 100%;\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  margin-top: var(--bs-gutter-y);\n}\n\n.col {\n  flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.row-cols-1 > * {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.row-cols-2 > * {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.row-cols-3 > * {\n  flex: 0 0 auto;\n  width: 33.3333333333%;\n}\n\n.row-cols-4 > * {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.row-cols-5 > * {\n  flex: 0 0 auto;\n  width: 20%;\n}\n\n.row-cols-6 > * {\n  flex: 0 0 auto;\n  width: 16.6666666667%;\n}\n\n.col-auto {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.col-1 {\n  flex: 0 0 auto;\n  width: 8.33333333%;\n}\n\n.col-2 {\n  flex: 0 0 auto;\n  width: 16.66666667%;\n}\n\n.col-3 {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.col-4 {\n  flex: 0 0 auto;\n  width: 33.33333333%;\n}\n\n.col-5 {\n  flex: 0 0 auto;\n  width: 41.66666667%;\n}\n\n.col-6 {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.col-7 {\n  flex: 0 0 auto;\n  width: 58.33333333%;\n}\n\n.col-8 {\n  flex: 0 0 auto;\n  width: 66.66666667%;\n}\n\n.col-9 {\n  flex: 0 0 auto;\n  width: 75%;\n}\n\n.col-10 {\n  flex: 0 0 auto;\n  width: 83.33333333%;\n}\n\n.col-11 {\n  flex: 0 0 auto;\n  width: 91.66666667%;\n}\n\n.col-12 {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.offset-1 {\n  margin-right: 8.33333333%;\n}\n\n.offset-2 {\n  margin-right: 16.66666667%;\n}\n\n.offset-3 {\n  margin-right: 25%;\n}\n\n.offset-4 {\n  margin-right: 33.33333333%;\n}\n\n.offset-5 {\n  margin-right: 41.66666667%;\n}\n\n.offset-6 {\n  margin-right: 50%;\n}\n\n.offset-7 {\n  margin-right: 58.33333333%;\n}\n\n.offset-8 {\n  margin-right: 66.66666667%;\n}\n\n.offset-9 {\n  margin-right: 75%;\n}\n\n.offset-10 {\n  margin-right: 83.33333333%;\n}\n\n.offset-11 {\n  margin-right: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n  --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n  --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n  --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n  --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n  --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n  --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n  --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n  --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n  --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n  --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n  --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n  --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n  .col-sm {\n    flex: 1 0 0%;\n  }\n  .row-cols-sm-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-sm-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-sm-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-sm-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-sm-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-sm-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-sm-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-sm-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-sm-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-sm-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-sm-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-sm-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-sm-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-sm-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-sm-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-sm-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-sm-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-sm-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-sm-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-sm-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-sm-0 {\n    margin-right: 0;\n  }\n  .offset-sm-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-sm-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-sm-3 {\n    margin-right: 25%;\n  }\n  .offset-sm-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-sm-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-sm-6 {\n    margin-right: 50%;\n  }\n  .offset-sm-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-sm-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-sm-9 {\n    margin-right: 75%;\n  }\n  .offset-sm-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-sm-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-sm-0,\n.gx-sm-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-sm-0,\n.gy-sm-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-sm-1,\n.gx-sm-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-sm-1,\n.gy-sm-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-sm-2,\n.gx-sm-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-sm-2,\n.gy-sm-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-sm-3,\n.gx-sm-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-sm-3,\n.gy-sm-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-sm-4,\n.gx-sm-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-sm-4,\n.gy-sm-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-sm-5,\n.gx-sm-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-sm-5,\n.gy-sm-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 768px) {\n  .col-md {\n    flex: 1 0 0%;\n  }\n  .row-cols-md-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-md-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-md-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-md-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-md-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-md-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-md-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-md-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-md-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-md-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-md-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-md-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-md-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-md-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-md-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-md-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-md-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-md-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-md-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-md-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-md-0 {\n    margin-right: 0;\n  }\n  .offset-md-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-md-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-md-3 {\n    margin-right: 25%;\n  }\n  .offset-md-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-md-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-md-6 {\n    margin-right: 50%;\n  }\n  .offset-md-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-md-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-md-9 {\n    margin-right: 75%;\n  }\n  .offset-md-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-md-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-md-0,\n.gx-md-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-md-0,\n.gy-md-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-md-1,\n.gx-md-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-md-1,\n.gy-md-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-md-2,\n.gx-md-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-md-2,\n.gy-md-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-md-3,\n.gx-md-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-md-3,\n.gy-md-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-md-4,\n.gx-md-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-md-4,\n.gy-md-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-md-5,\n.gx-md-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-md-5,\n.gy-md-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 992px) {\n  .col-lg {\n    flex: 1 0 0%;\n  }\n  .row-cols-lg-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-lg-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-lg-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-lg-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-lg-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-lg-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-lg-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-lg-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-lg-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-lg-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-lg-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-lg-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-lg-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-lg-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-lg-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-lg-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-lg-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-lg-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-lg-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-lg-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-lg-0 {\n    margin-right: 0;\n  }\n  .offset-lg-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-lg-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-lg-3 {\n    margin-right: 25%;\n  }\n  .offset-lg-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-lg-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-lg-6 {\n    margin-right: 50%;\n  }\n  .offset-lg-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-lg-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-lg-9 {\n    margin-right: 75%;\n  }\n  .offset-lg-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-lg-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-lg-0,\n.gx-lg-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-lg-0,\n.gy-lg-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-lg-1,\n.gx-lg-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-lg-1,\n.gy-lg-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-lg-2,\n.gx-lg-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-lg-2,\n.gy-lg-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-lg-3,\n.gx-lg-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-lg-3,\n.gy-lg-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-lg-4,\n.gx-lg-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-lg-4,\n.gy-lg-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-lg-5,\n.gx-lg-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-lg-5,\n.gy-lg-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1200px) {\n  .col-xl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xl-0 {\n    margin-right: 0;\n  }\n  .offset-xl-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-xl-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-xl-3 {\n    margin-right: 25%;\n  }\n  .offset-xl-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-xl-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-xl-6 {\n    margin-right: 50%;\n  }\n  .offset-xl-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-xl-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-xl-9 {\n    margin-right: 75%;\n  }\n  .offset-xl-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-xl-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-xl-0,\n.gx-xl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xl-0,\n.gy-xl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xl-1,\n.gx-xl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xl-1,\n.gy-xl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xl-2,\n.gx-xl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xl-2,\n.gy-xl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xl-3,\n.gx-xl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xl-3,\n.gy-xl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xl-4,\n.gx-xl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xl-4,\n.gy-xl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xl-5,\n.gx-xl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xl-5,\n.gy-xl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1400px) {\n  .col-xxl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xxl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xxl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xxl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xxl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xxl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xxl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xxl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xxl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xxl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xxl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xxl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xxl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xxl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xxl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xxl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xxl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xxl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xxl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xxl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xxl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xxl-0 {\n    margin-right: 0;\n  }\n  .offset-xxl-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-xxl-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-xxl-3 {\n    margin-right: 25%;\n  }\n  .offset-xxl-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-xxl-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-xxl-6 {\n    margin-right: 50%;\n  }\n  .offset-xxl-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-xxl-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-xxl-9 {\n    margin-right: 75%;\n  }\n  .offset-xxl-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-xxl-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-xxl-0,\n.gx-xxl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xxl-0,\n.gy-xxl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xxl-1,\n.gx-xxl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xxl-1,\n.gy-xxl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xxl-2,\n.gx-xxl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xxl-2,\n.gy-xxl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xxl-3,\n.gx-xxl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xxl-3,\n.gy-xxl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xxl-4,\n.gx-xxl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xxl-4,\n.gy-xxl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xxl-5,\n.gx-xxl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xxl-5,\n.gy-xxl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-grid {\n  display: grid !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: inline-flex !important;\n}\n\n.d-none {\n  display: none !important;\n}\n\n.flex-fill {\n  flex: 1 1 auto !important;\n}\n\n.flex-row {\n  flex-direction: row !important;\n}\n\n.flex-column {\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n  justify-content: space-evenly !important;\n}\n\n.align-items-start {\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  align-content: center !important;\n}\n\n.align-content-between {\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  align-self: auto !important;\n}\n\n.align-self-start {\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  align-self: stretch !important;\n}\n\n.order-first {\n  order: -1 !important;\n}\n\n.order-0 {\n  order: 0 !important;\n}\n\n.order-1 {\n  order: 1 !important;\n}\n\n.order-2 {\n  order: 2 !important;\n}\n\n.order-3 {\n  order: 3 !important;\n}\n\n.order-4 {\n  order: 4 !important;\n}\n\n.order-5 {\n  order: 5 !important;\n}\n\n.order-last {\n  order: 6 !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mx-0 {\n  margin-left: 0 !important;\n  margin-right: 0 !important;\n}\n\n.mx-1 {\n  margin-left: 0.25rem !important;\n  margin-right: 0.25rem !important;\n}\n\n.mx-2 {\n  margin-left: 0.5rem !important;\n  margin-right: 0.5rem !important;\n}\n\n.mx-3 {\n  margin-left: 1rem !important;\n  margin-right: 1rem !important;\n}\n\n.mx-4 {\n  margin-left: 1.5rem !important;\n  margin-right: 1.5rem !important;\n}\n\n.mx-5 {\n  margin-left: 3rem !important;\n  margin-right: 3rem !important;\n}\n\n.mx-auto {\n  margin-left: auto !important;\n  margin-right: auto !important;\n}\n\n.my-0 {\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\n\n.my-1 {\n  margin-top: 0.25rem !important;\n  margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n  margin-top: 0.5rem !important;\n  margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n  margin-top: 1rem !important;\n  margin-bottom: 1rem !important;\n}\n\n.my-4 {\n  margin-top: 1.5rem !important;\n  margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n  margin-top: 3rem !important;\n  margin-bottom: 3rem !important;\n}\n\n.my-auto {\n  margin-top: auto !important;\n  margin-bottom: auto !important;\n}\n\n.mt-0 {\n  margin-top: 0 !important;\n}\n\n.mt-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n  margin-top: 1rem !important;\n}\n\n.mt-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n  margin-top: 3rem !important;\n}\n\n.mt-auto {\n  margin-top: auto !important;\n}\n\n.me-0 {\n  margin-left: 0 !important;\n}\n\n.me-1 {\n  margin-left: 0.25rem !important;\n}\n\n.me-2 {\n  margin-left: 0.5rem !important;\n}\n\n.me-3 {\n  margin-left: 1rem !important;\n}\n\n.me-4 {\n  margin-left: 1.5rem !important;\n}\n\n.me-5 {\n  margin-left: 3rem !important;\n}\n\n.me-auto {\n  margin-left: auto !important;\n}\n\n.mb-0 {\n  margin-bottom: 0 !important;\n}\n\n.mb-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n  margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n  margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n  margin-bottom: auto !important;\n}\n\n.ms-0 {\n  margin-right: 0 !important;\n}\n\n.ms-1 {\n  margin-right: 0.25rem !important;\n}\n\n.ms-2 {\n  margin-right: 0.5rem !important;\n}\n\n.ms-3 {\n  margin-right: 1rem !important;\n}\n\n.ms-4 {\n  margin-right: 1.5rem !important;\n}\n\n.ms-5 {\n  margin-right: 3rem !important;\n}\n\n.ms-auto {\n  margin-right: auto !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.px-0 {\n  padding-left: 0 !important;\n  padding-right: 0 !important;\n}\n\n.px-1 {\n  padding-left: 0.25rem !important;\n  padding-right: 0.25rem !important;\n}\n\n.px-2 {\n  padding-left: 0.5rem !important;\n  padding-right: 0.5rem !important;\n}\n\n.px-3 {\n  padding-left: 1rem !important;\n  padding-right: 1rem !important;\n}\n\n.px-4 {\n  padding-left: 1.5rem !important;\n  padding-right: 1.5rem !important;\n}\n\n.px-5 {\n  padding-left: 3rem !important;\n  padding-right: 3rem !important;\n}\n\n.py-0 {\n  padding-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\n.py-1 {\n  padding-top: 0.25rem !important;\n  padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n  padding-top: 0.5rem !important;\n  padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n  padding-top: 1rem !important;\n  padding-bottom: 1rem !important;\n}\n\n.py-4 {\n  padding-top: 1.5rem !important;\n  padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n  padding-top: 3rem !important;\n  padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n  padding-top: 0 !important;\n}\n\n.pt-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n  padding-top: 1rem !important;\n}\n\n.pt-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n  padding-top: 3rem !important;\n}\n\n.pe-0 {\n  padding-left: 0 !important;\n}\n\n.pe-1 {\n  padding-left: 0.25rem !important;\n}\n\n.pe-2 {\n  padding-left: 0.5rem !important;\n}\n\n.pe-3 {\n  padding-left: 1rem !important;\n}\n\n.pe-4 {\n  padding-left: 1.5rem !important;\n}\n\n.pe-5 {\n  padding-left: 3rem !important;\n}\n\n.pb-0 {\n  padding-bottom: 0 !important;\n}\n\n.pb-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n  padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n  padding-right: 0 !important;\n}\n\n.ps-1 {\n  padding-right: 0.25rem !important;\n}\n\n.ps-2 {\n  padding-right: 0.5rem !important;\n}\n\n.ps-3 {\n  padding-right: 1rem !important;\n}\n\n.ps-4 {\n  padding-right: 1.5rem !important;\n}\n\n.ps-5 {\n  padding-right: 3rem !important;\n}\n\n@media (min-width: 576px) {\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-grid {\n    display: grid !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-sm-none {\n    display: none !important;\n  }\n  .flex-sm-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-row {\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-sm-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-sm-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-sm-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-sm-start {\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    align-self: stretch !important;\n  }\n  .order-sm-first {\n    order: -1 !important;\n  }\n  .order-sm-0 {\n    order: 0 !important;\n  }\n  .order-sm-1 {\n    order: 1 !important;\n  }\n  .order-sm-2 {\n    order: 2 !important;\n  }\n  .order-sm-3 {\n    order: 3 !important;\n  }\n  .order-sm-4 {\n    order: 4 !important;\n  }\n  .order-sm-5 {\n    order: 5 !important;\n  }\n  .order-sm-last {\n    order: 6 !important;\n  }\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mx-sm-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-sm-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-sm-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-sm-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-sm-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-sm-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-sm-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-sm-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-sm-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-sm-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-sm-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mt-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-sm-auto {\n    margin-top: auto !important;\n  }\n  .me-sm-0 {\n    margin-left: 0 !important;\n  }\n  .me-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .me-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .me-sm-auto {\n    margin-left: auto !important;\n  }\n  .mb-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-sm-0 {\n    margin-right: 0 !important;\n  }\n  .ms-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-sm-auto {\n    margin-right: auto !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .px-sm-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-sm-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-sm-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-sm-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-sm-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-sm-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-sm-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-sm-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-sm-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pt-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-sm-0 {\n    padding-left: 0 !important;\n  }\n  .pe-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-sm-0 {\n    padding-right: 0 !important;\n  }\n  .ps-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-sm-5 {\n    padding-right: 3rem !important;\n  }\n}\n@media (min-width: 768px) {\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-grid {\n    display: grid !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-md-none {\n    display: none !important;\n  }\n  .flex-md-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-md-row {\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-md-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-md-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-md-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-md-start {\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    align-self: stretch !important;\n  }\n  .order-md-first {\n    order: -1 !important;\n  }\n  .order-md-0 {\n    order: 0 !important;\n  }\n  .order-md-1 {\n    order: 1 !important;\n  }\n  .order-md-2 {\n    order: 2 !important;\n  }\n  .order-md-3 {\n    order: 3 !important;\n  }\n  .order-md-4 {\n    order: 4 !important;\n  }\n  .order-md-5 {\n    order: 5 !important;\n  }\n  .order-md-last {\n    order: 6 !important;\n  }\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mx-md-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-md-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-md-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-md-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-md-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-md-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-md-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-md-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-md-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-md-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-md-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-md-0 {\n    margin-top: 0 !important;\n  }\n  .mt-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-md-auto {\n    margin-top: auto !important;\n  }\n  .me-md-0 {\n    margin-left: 0 !important;\n  }\n  .me-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-md-3 {\n    margin-left: 1rem !important;\n  }\n  .me-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-md-5 {\n    margin-left: 3rem !important;\n  }\n  .me-md-auto {\n    margin-left: auto !important;\n  }\n  .mb-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-md-0 {\n    margin-right: 0 !important;\n  }\n  .ms-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-md-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-md-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-md-auto {\n    margin-right: auto !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .px-md-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-md-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-md-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-md-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-md-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-md-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-md-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-md-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-md-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-md-0 {\n    padding-top: 0 !important;\n  }\n  .pt-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-md-0 {\n    padding-left: 0 !important;\n  }\n  .pe-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-md-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-md-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-md-0 {\n    padding-right: 0 !important;\n  }\n  .ps-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-md-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-md-5 {\n    padding-right: 3rem !important;\n  }\n}\n@media (min-width: 992px) {\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-grid {\n    display: grid !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-lg-none {\n    display: none !important;\n  }\n  .flex-lg-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-row {\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-lg-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-lg-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-lg-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-lg-start {\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    align-self: stretch !important;\n  }\n  .order-lg-first {\n    order: -1 !important;\n  }\n  .order-lg-0 {\n    order: 0 !important;\n  }\n  .order-lg-1 {\n    order: 1 !important;\n  }\n  .order-lg-2 {\n    order: 2 !important;\n  }\n  .order-lg-3 {\n    order: 3 !important;\n  }\n  .order-lg-4 {\n    order: 4 !important;\n  }\n  .order-lg-5 {\n    order: 5 !important;\n  }\n  .order-lg-last {\n    order: 6 !important;\n  }\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mx-lg-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-lg-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-lg-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-lg-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-lg-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-lg-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-lg-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-lg-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-lg-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-lg-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-lg-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mt-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-lg-auto {\n    margin-top: auto !important;\n  }\n  .me-lg-0 {\n    margin-left: 0 !important;\n  }\n  .me-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .me-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .me-lg-auto {\n    margin-left: auto !important;\n  }\n  .mb-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-lg-0 {\n    margin-right: 0 !important;\n  }\n  .ms-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-lg-auto {\n    margin-right: auto !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .px-lg-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-lg-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-lg-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-lg-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-lg-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-lg-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-lg-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-lg-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-lg-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pt-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-lg-0 {\n    padding-left: 0 !important;\n  }\n  .pe-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-lg-0 {\n    padding-right: 0 !important;\n  }\n  .ps-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-lg-5 {\n    padding-right: 3rem !important;\n  }\n}\n@media (min-width: 1200px) {\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-grid {\n    display: grid !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xl-none {\n    display: none !important;\n  }\n  .flex-xl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-row {\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xl-first {\n    order: -1 !important;\n  }\n  .order-xl-0 {\n    order: 0 !important;\n  }\n  .order-xl-1 {\n    order: 1 !important;\n  }\n  .order-xl-2 {\n    order: 2 !important;\n  }\n  .order-xl-3 {\n    order: 3 !important;\n  }\n  .order-xl-4 {\n    order: 4 !important;\n  }\n  .order-xl-5 {\n    order: 5 !important;\n  }\n  .order-xl-last {\n    order: 6 !important;\n  }\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mx-xl-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-xl-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-xl-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-xl-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-xl-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-xl-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-xl-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-xl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xl-auto {\n    margin-top: auto !important;\n  }\n  .me-xl-0 {\n    margin-left: 0 !important;\n  }\n  .me-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .me-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .me-xl-auto {\n    margin-left: auto !important;\n  }\n  .mb-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xl-0 {\n    margin-right: 0 !important;\n  }\n  .ms-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-xl-auto {\n    margin-right: auto !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .px-xl-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-xl-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-xl-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-xl-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-xl-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-xl-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-xl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xl-0 {\n    padding-left: 0 !important;\n  }\n  .pe-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xl-0 {\n    padding-right: 0 !important;\n  }\n  .ps-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-xl-5 {\n    padding-right: 3rem !important;\n  }\n}\n@media (min-width: 1400px) {\n  .d-xxl-inline {\n    display: inline !important;\n  }\n  .d-xxl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xxl-block {\n    display: block !important;\n  }\n  .d-xxl-grid {\n    display: grid !important;\n  }\n  .d-xxl-table {\n    display: table !important;\n  }\n  .d-xxl-table-row {\n    display: table-row !important;\n  }\n  .d-xxl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xxl-flex {\n    display: flex !important;\n  }\n  .d-xxl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xxl-none {\n    display: none !important;\n  }\n  .flex-xxl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xxl-row {\n    flex-direction: row !important;\n  }\n  .flex-xxl-column {\n    flex-direction: column !important;\n  }\n  .flex-xxl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xxl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xxl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xxl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xxl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xxl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xxl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xxl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xxl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xxl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xxl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xxl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xxl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xxl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xxl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xxl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xxl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xxl-center {\n    align-items: center !important;\n  }\n  .align-items-xxl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xxl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xxl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xxl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xxl-center {\n    align-content: center !important;\n  }\n  .align-content-xxl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xxl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xxl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xxl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xxl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xxl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xxl-center {\n    align-self: center !important;\n  }\n  .align-self-xxl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xxl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xxl-first {\n    order: -1 !important;\n  }\n  .order-xxl-0 {\n    order: 0 !important;\n  }\n  .order-xxl-1 {\n    order: 1 !important;\n  }\n  .order-xxl-2 {\n    order: 2 !important;\n  }\n  .order-xxl-3 {\n    order: 3 !important;\n  }\n  .order-xxl-4 {\n    order: 4 !important;\n  }\n  .order-xxl-5 {\n    order: 5 !important;\n  }\n  .order-xxl-last {\n    order: 6 !important;\n  }\n  .m-xxl-0 {\n    margin: 0 !important;\n  }\n  .m-xxl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xxl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xxl-3 {\n    margin: 1rem !important;\n  }\n  .m-xxl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xxl-5 {\n    margin: 3rem !important;\n  }\n  .m-xxl-auto {\n    margin: auto !important;\n  }\n  .mx-xxl-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-xxl-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-xxl-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-xxl-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-xxl-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-xxl-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-xxl-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-xxl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xxl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xxl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xxl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xxl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xxl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xxl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xxl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xxl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xxl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xxl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xxl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xxl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xxl-auto {\n    margin-top: auto !important;\n  }\n  .me-xxl-0 {\n    margin-left: 0 !important;\n  }\n  .me-xxl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-xxl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-xxl-3 {\n    margin-left: 1rem !important;\n  }\n  .me-xxl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-xxl-5 {\n    margin-left: 3rem !important;\n  }\n  .me-xxl-auto {\n    margin-left: auto !important;\n  }\n  .mb-xxl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xxl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xxl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xxl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xxl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xxl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xxl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xxl-0 {\n    margin-right: 0 !important;\n  }\n  .ms-xxl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-xxl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-xxl-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-xxl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-xxl-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-xxl-auto {\n    margin-right: auto !important;\n  }\n  .p-xxl-0 {\n    padding: 0 !important;\n  }\n  .p-xxl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xxl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xxl-3 {\n    padding: 1rem !important;\n  }\n  .p-xxl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xxl-5 {\n    padding: 3rem !important;\n  }\n  .px-xxl-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-xxl-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-xxl-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-xxl-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-xxl-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-xxl-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-xxl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xxl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xxl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xxl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xxl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xxl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xxl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xxl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xxl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xxl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xxl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xxl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xxl-0 {\n    padding-left: 0 !important;\n  }\n  .pe-xxl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-xxl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-xxl-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-xxl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-xxl-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-xxl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xxl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xxl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xxl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xxl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xxl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xxl-0 {\n    padding-right: 0 !important;\n  }\n  .ps-xxl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-xxl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-xxl-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-xxl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-xxl-5 {\n    padding-right: 3rem !important;\n  }\n}\n@media print {\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-grid {\n    display: grid !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-print-none {\n    display: none !important;\n  }\n}\n/*# sourceMappingURL=bootstrap-grid.rtl.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap-reboot.css",
    "content": "/*!\n * Bootstrap Reboot v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  :root {\n    scroll-behavior: smooth;\n  }\n}\n\nbody {\n  margin: 0;\n  font-family: var(--bs-body-font-family);\n  font-size: var(--bs-body-font-size);\n  font-weight: var(--bs-body-font-weight);\n  line-height: var(--bs-body-line-height);\n  color: var(--bs-body-color);\n  text-align: var(--bs-body-text-align);\n  background-color: var(--bs-body-bg);\n  -webkit-text-size-adjust: 100%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nhr {\n  margin: 1rem 0;\n  color: inherit;\n  border: 0;\n  border-top: 1px solid;\n  opacity: 0.25;\n}\n\nh6, h5, h4, h3, h2, h1 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n  font-weight: 500;\n  line-height: 1.2;\n}\n\nh1 {\n  font-size: calc(1.375rem + 1.5vw);\n}\n@media (min-width: 1200px) {\n  h1 {\n    font-size: 2.5rem;\n  }\n}\n\nh2 {\n  font-size: calc(1.325rem + 0.9vw);\n}\n@media (min-width: 1200px) {\n  h2 {\n    font-size: 2rem;\n  }\n}\n\nh3 {\n  font-size: calc(1.3rem + 0.6vw);\n}\n@media (min-width: 1200px) {\n  h3 {\n    font-size: 1.75rem;\n  }\n}\n\nh4 {\n  font-size: calc(1.275rem + 0.3vw);\n}\n@media (min-width: 1200px) {\n  h4 {\n    font-size: 1.5rem;\n  }\n}\n\nh5 {\n  font-size: 1.25rem;\n}\n\nh6 {\n  font-size: 1rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title] {\n  -webkit-text-decoration: underline dotted;\n  text-decoration: underline dotted;\n  cursor: help;\n  -webkit-text-decoration-skip-ink: none;\n  text-decoration-skip-ink: none;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul {\n  padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: 700;\n}\n\ndd {\n  margin-bottom: 0.5rem;\n  margin-left: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall {\n  font-size: 0.875em;\n}\n\nmark {\n  padding: 0.1875em;\n  background-color: var(--bs-highlight-bg);\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 0.75em;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nsup {\n  top: -0.5em;\n}\n\na {\n  color: var(--bs-link-color);\n  text-decoration: underline;\n}\na:hover {\n  color: var(--bs-link-hover-color);\n}\n\na:not([href]):not([class]), a:not([href]):not([class]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: var(--bs-font-monospace);\n  font-size: 1em;\n}\n\npre {\n  display: block;\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n  font-size: 0.875em;\n}\npre code {\n  font-size: inherit;\n  color: inherit;\n  word-break: normal;\n}\n\ncode {\n  font-size: 0.875em;\n  color: var(--bs-code-color);\n  word-wrap: break-word;\n}\na > code {\n  color: inherit;\n}\n\nkbd {\n  padding: 0.1875rem 0.375rem;\n  font-size: 0.875em;\n  color: var(--bs-body-bg);\n  background-color: var(--bs-body-color);\n  border-radius: 0.25rem;\n}\nkbd kbd {\n  padding: 0;\n  font-size: 1em;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg,\nsvg {\n  vertical-align: middle;\n}\n\ntable {\n  caption-side: bottom;\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  color: #6c757d;\n  text-align: left;\n}\n\nth {\n  text-align: inherit;\n  text-align: -webkit-match-parent;\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n}\n\nlabel {\n  display: inline-block;\n}\n\nbutton {\n  border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n  outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n[role=button] {\n  cursor: pointer;\n}\n\nselect {\n  word-wrap: normal;\n}\nselect:disabled {\n  opacity: 1;\n}\n\n[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {\n  display: none !important;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n  -webkit-appearance: button;\n}\nbutton:not(:disabled),\n[type=button]:not(:disabled),\n[type=reset]:not(:disabled),\n[type=submit]:not(:disabled) {\n  cursor: pointer;\n}\n\n::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ntextarea {\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  float: left;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 0.5rem;\n  font-size: calc(1.275rem + 0.3vw);\n  line-height: inherit;\n}\n@media (min-width: 1200px) {\n  legend {\n    font-size: 1.5rem;\n  }\n}\nlegend + * {\n  clear: left;\n}\n\n::-webkit-datetime-edit-fields-wrapper,\n::-webkit-datetime-edit-text,\n::-webkit-datetime-edit-minute,\n::-webkit-datetime-edit-hour-field,\n::-webkit-datetime-edit-day-field,\n::-webkit-datetime-edit-month-field,\n::-webkit-datetime-edit-year-field {\n  padding: 0;\n}\n\n::-webkit-inner-spin-button {\n  height: auto;\n}\n\n[type=search] {\n  outline-offset: -2px;\n  -webkit-appearance: textfield;\n}\n\n/* rtl:raw:\n[type=\"tel\"],\n[type=\"url\"],\n[type=\"email\"],\n[type=\"number\"] {\n  direction: ltr;\n}\n*/\n::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-color-swatch-wrapper {\n  padding: 0;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\n::file-selector-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\niframe {\n  border: 0;\n}\n\nsummary {\n  display: list-item;\n  cursor: pointer;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[hidden] {\n  display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap-reboot.rtl.css",
    "content": "/*!\n * Bootstrap Reboot v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  :root {\n    scroll-behavior: smooth;\n  }\n}\n\nbody {\n  margin: 0;\n  font-family: var(--bs-body-font-family);\n  font-size: var(--bs-body-font-size);\n  font-weight: var(--bs-body-font-weight);\n  line-height: var(--bs-body-line-height);\n  color: var(--bs-body-color);\n  text-align: var(--bs-body-text-align);\n  background-color: var(--bs-body-bg);\n  -webkit-text-size-adjust: 100%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nhr {\n  margin: 1rem 0;\n  color: inherit;\n  border: 0;\n  border-top: 1px solid;\n  opacity: 0.25;\n}\n\nh6, h5, h4, h3, h2, h1 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n  font-weight: 500;\n  line-height: 1.2;\n}\n\nh1 {\n  font-size: calc(1.375rem + 1.5vw);\n}\n@media (min-width: 1200px) {\n  h1 {\n    font-size: 2.5rem;\n  }\n}\n\nh2 {\n  font-size: calc(1.325rem + 0.9vw);\n}\n@media (min-width: 1200px) {\n  h2 {\n    font-size: 2rem;\n  }\n}\n\nh3 {\n  font-size: calc(1.3rem + 0.6vw);\n}\n@media (min-width: 1200px) {\n  h3 {\n    font-size: 1.75rem;\n  }\n}\n\nh4 {\n  font-size: calc(1.275rem + 0.3vw);\n}\n@media (min-width: 1200px) {\n  h4 {\n    font-size: 1.5rem;\n  }\n}\n\nh5 {\n  font-size: 1.25rem;\n}\n\nh6 {\n  font-size: 1rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title] {\n  -webkit-text-decoration: underline dotted;\n  text-decoration: underline dotted;\n  cursor: help;\n  -webkit-text-decoration-skip-ink: none;\n  text-decoration-skip-ink: none;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul {\n  padding-right: 2rem;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: 700;\n}\n\ndd {\n  margin-bottom: 0.5rem;\n  margin-right: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall {\n  font-size: 0.875em;\n}\n\nmark {\n  padding: 0.1875em;\n  background-color: var(--bs-highlight-bg);\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 0.75em;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nsup {\n  top: -0.5em;\n}\n\na {\n  color: var(--bs-link-color);\n  text-decoration: underline;\n}\na:hover {\n  color: var(--bs-link-hover-color);\n}\n\na:not([href]):not([class]), a:not([href]):not([class]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: var(--bs-font-monospace);\n  font-size: 1em;\n}\n\npre {\n  display: block;\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n  font-size: 0.875em;\n}\npre code {\n  font-size: inherit;\n  color: inherit;\n  word-break: normal;\n}\n\ncode {\n  font-size: 0.875em;\n  color: var(--bs-code-color);\n  word-wrap: break-word;\n}\na > code {\n  color: inherit;\n}\n\nkbd {\n  padding: 0.1875rem 0.375rem;\n  font-size: 0.875em;\n  color: var(--bs-body-bg);\n  background-color: var(--bs-body-color);\n  border-radius: 0.25rem;\n}\nkbd kbd {\n  padding: 0;\n  font-size: 1em;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg,\nsvg {\n  vertical-align: middle;\n}\n\ntable {\n  caption-side: bottom;\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  color: #6c757d;\n  text-align: right;\n}\n\nth {\n  text-align: inherit;\n  text-align: -webkit-match-parent;\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n}\n\nlabel {\n  display: inline-block;\n}\n\nbutton {\n  border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n  outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n[role=button] {\n  cursor: pointer;\n}\n\nselect {\n  word-wrap: normal;\n}\nselect:disabled {\n  opacity: 1;\n}\n\n[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {\n  display: none !important;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n  -webkit-appearance: button;\n}\nbutton:not(:disabled),\n[type=button]:not(:disabled),\n[type=reset]:not(:disabled),\n[type=submit]:not(:disabled) {\n  cursor: pointer;\n}\n\n::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ntextarea {\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  float: right;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 0.5rem;\n  font-size: calc(1.275rem + 0.3vw);\n  line-height: inherit;\n}\n@media (min-width: 1200px) {\n  legend {\n    font-size: 1.5rem;\n  }\n}\nlegend + * {\n  clear: right;\n}\n\n::-webkit-datetime-edit-fields-wrapper,\n::-webkit-datetime-edit-text,\n::-webkit-datetime-edit-minute,\n::-webkit-datetime-edit-hour-field,\n::-webkit-datetime-edit-day-field,\n::-webkit-datetime-edit-month-field,\n::-webkit-datetime-edit-year-field {\n  padding: 0;\n}\n\n::-webkit-inner-spin-button {\n  height: auto;\n}\n\n[type=search] {\n  outline-offset: -2px;\n  -webkit-appearance: textfield;\n}\n\n[type=\"tel\"],\n[type=\"url\"],\n[type=\"email\"],\n[type=\"number\"] {\n  direction: ltr;\n}\n::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-color-swatch-wrapper {\n  padding: 0;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\n::file-selector-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\niframe {\n  border: 0;\n}\n\nsummary {\n  display: list-item;\n  cursor: pointer;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[hidden] {\n  display: none !important;\n}\n/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap-utilities.css",
    "content": "/*!\n * Bootstrap Utilities v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n.clearfix::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.text-bg-primary {\n  color: #fff !important;\n  background-color: RGBA(13, 110, 253, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-secondary {\n  color: #fff !important;\n  background-color: RGBA(108, 117, 125, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-success {\n  color: #fff !important;\n  background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-info {\n  color: #000 !important;\n  background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-warning {\n  color: #000 !important;\n  background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-danger {\n  color: #fff !important;\n  background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-light {\n  color: #000 !important;\n  background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-dark {\n  color: #fff !important;\n  background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important;\n}\n\n.link-primary {\n  color: #0d6efd !important;\n}\n.link-primary:hover, .link-primary:focus {\n  color: #0a58ca !important;\n}\n\n.link-secondary {\n  color: #6c757d !important;\n}\n.link-secondary:hover, .link-secondary:focus {\n  color: #565e64 !important;\n}\n\n.link-success {\n  color: #198754 !important;\n}\n.link-success:hover, .link-success:focus {\n  color: #146c43 !important;\n}\n\n.link-info {\n  color: #0dcaf0 !important;\n}\n.link-info:hover, .link-info:focus {\n  color: #3dd5f3 !important;\n}\n\n.link-warning {\n  color: #ffc107 !important;\n}\n.link-warning:hover, .link-warning:focus {\n  color: #ffcd39 !important;\n}\n\n.link-danger {\n  color: #dc3545 !important;\n}\n.link-danger:hover, .link-danger:focus {\n  color: #b02a37 !important;\n}\n\n.link-light {\n  color: #f8f9fa !important;\n}\n.link-light:hover, .link-light:focus {\n  color: #f9fafb !important;\n}\n\n.link-dark {\n  color: #212529 !important;\n}\n.link-dark:hover, .link-dark:focus {\n  color: #1a1e21 !important;\n}\n\n.ratio {\n  position: relative;\n  width: 100%;\n}\n.ratio::before {\n  display: block;\n  padding-top: var(--bs-aspect-ratio);\n  content: \"\";\n}\n.ratio > * {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.ratio-1x1 {\n  --bs-aspect-ratio: 100%;\n}\n\n.ratio-4x3 {\n  --bs-aspect-ratio: 75%;\n}\n\n.ratio-16x9 {\n  --bs-aspect-ratio: 56.25%;\n}\n\n.ratio-21x9 {\n  --bs-aspect-ratio: 42.8571428571%;\n}\n\n.fixed-top {\n  position: fixed;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n.fixed-bottom {\n  position: fixed;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n.sticky-top {\n  position: -webkit-sticky;\n  position: sticky;\n  top: 0;\n  z-index: 1020;\n}\n\n.sticky-bottom {\n  position: -webkit-sticky;\n  position: sticky;\n  bottom: 0;\n  z-index: 1020;\n}\n\n@media (min-width: 576px) {\n  .sticky-sm-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-sm-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 768px) {\n  .sticky-md-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-md-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 992px) {\n  .sticky-lg-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-lg-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1200px) {\n  .sticky-xl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1400px) {\n  .sticky-xxl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xxl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n.hstack {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  align-self: stretch;\n}\n\n.vstack {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  align-self: stretch;\n}\n\n.visually-hidden,\n.visually-hidden-focusable:not(:focus):not(:focus-within) {\n  position: absolute !important;\n  width: 1px !important;\n  height: 1px !important;\n  padding: 0 !important;\n  margin: -1px !important;\n  overflow: hidden !important;\n  clip: rect(0, 0, 0, 0) !important;\n  white-space: nowrap !important;\n  border: 0 !important;\n}\n\n.stretched-link::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1;\n  content: \"\";\n}\n\n.text-truncate {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.vr {\n  display: inline-block;\n  align-self: stretch;\n  width: 1px;\n  min-height: 1em;\n  background-color: currentcolor;\n  opacity: 0.25;\n}\n\n.align-baseline {\n  vertical-align: baseline !important;\n}\n\n.align-top {\n  vertical-align: top !important;\n}\n\n.align-middle {\n  vertical-align: middle !important;\n}\n\n.align-bottom {\n  vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n  vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n  vertical-align: text-top !important;\n}\n\n.float-start {\n  float: left !important;\n}\n\n.float-end {\n  float: right !important;\n}\n\n.float-none {\n  float: none !important;\n}\n\n.opacity-0 {\n  opacity: 0 !important;\n}\n\n.opacity-25 {\n  opacity: 0.25 !important;\n}\n\n.opacity-50 {\n  opacity: 0.5 !important;\n}\n\n.opacity-75 {\n  opacity: 0.75 !important;\n}\n\n.opacity-100 {\n  opacity: 1 !important;\n}\n\n.overflow-auto {\n  overflow: auto !important;\n}\n\n.overflow-hidden {\n  overflow: hidden !important;\n}\n\n.overflow-visible {\n  overflow: visible !important;\n}\n\n.overflow-scroll {\n  overflow: scroll !important;\n}\n\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-grid {\n  display: grid !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: inline-flex !important;\n}\n\n.d-none {\n  display: none !important;\n}\n\n.shadow {\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-sm {\n  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow-lg {\n  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n  box-shadow: none !important;\n}\n\n.position-static {\n  position: static !important;\n}\n\n.position-relative {\n  position: relative !important;\n}\n\n.position-absolute {\n  position: absolute !important;\n}\n\n.position-fixed {\n  position: fixed !important;\n}\n\n.position-sticky {\n  position: -webkit-sticky !important;\n  position: sticky !important;\n}\n\n.top-0 {\n  top: 0 !important;\n}\n\n.top-50 {\n  top: 50% !important;\n}\n\n.top-100 {\n  top: 100% !important;\n}\n\n.bottom-0 {\n  bottom: 0 !important;\n}\n\n.bottom-50 {\n  bottom: 50% !important;\n}\n\n.bottom-100 {\n  bottom: 100% !important;\n}\n\n.start-0 {\n  left: 0 !important;\n}\n\n.start-50 {\n  left: 50% !important;\n}\n\n.start-100 {\n  left: 100% !important;\n}\n\n.end-0 {\n  right: 0 !important;\n}\n\n.end-50 {\n  right: 50% !important;\n}\n\n.end-100 {\n  right: 100% !important;\n}\n\n.translate-middle {\n  transform: translate(-50%, -50%) !important;\n}\n\n.translate-middle-x {\n  transform: translateX(-50%) !important;\n}\n\n.translate-middle-y {\n  transform: translateY(-50%) !important;\n}\n\n.border {\n  border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-0 {\n  border: 0 !important;\n}\n\n.border-top {\n  border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-top-0 {\n  border-top: 0 !important;\n}\n\n.border-end {\n  border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-end-0 {\n  border-right: 0 !important;\n}\n\n.border-bottom {\n  border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-bottom-0 {\n  border-bottom: 0 !important;\n}\n\n.border-start {\n  border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-start-0 {\n  border-left: 0 !important;\n}\n\n.border-primary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-secondary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-success {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-info {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-warning {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-danger {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-light {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-dark {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-white {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-1 {\n  --bs-border-width: 1px;\n}\n\n.border-2 {\n  --bs-border-width: 2px;\n}\n\n.border-3 {\n  --bs-border-width: 3px;\n}\n\n.border-4 {\n  --bs-border-width: 4px;\n}\n\n.border-5 {\n  --bs-border-width: 5px;\n}\n\n.border-opacity-10 {\n  --bs-border-opacity: 0.1;\n}\n\n.border-opacity-25 {\n  --bs-border-opacity: 0.25;\n}\n\n.border-opacity-50 {\n  --bs-border-opacity: 0.5;\n}\n\n.border-opacity-75 {\n  --bs-border-opacity: 0.75;\n}\n\n.border-opacity-100 {\n  --bs-border-opacity: 1;\n}\n\n.w-25 {\n  width: 25% !important;\n}\n\n.w-50 {\n  width: 50% !important;\n}\n\n.w-75 {\n  width: 75% !important;\n}\n\n.w-100 {\n  width: 100% !important;\n}\n\n.w-auto {\n  width: auto !important;\n}\n\n.mw-100 {\n  max-width: 100% !important;\n}\n\n.vw-100 {\n  width: 100vw !important;\n}\n\n.min-vw-100 {\n  min-width: 100vw !important;\n}\n\n.h-25 {\n  height: 25% !important;\n}\n\n.h-50 {\n  height: 50% !important;\n}\n\n.h-75 {\n  height: 75% !important;\n}\n\n.h-100 {\n  height: 100% !important;\n}\n\n.h-auto {\n  height: auto !important;\n}\n\n.mh-100 {\n  max-height: 100% !important;\n}\n\n.vh-100 {\n  height: 100vh !important;\n}\n\n.min-vh-100 {\n  min-height: 100vh !important;\n}\n\n.flex-fill {\n  flex: 1 1 auto !important;\n}\n\n.flex-row {\n  flex-direction: row !important;\n}\n\n.flex-column {\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n  justify-content: space-evenly !important;\n}\n\n.align-items-start {\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  align-content: center !important;\n}\n\n.align-content-between {\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  align-self: auto !important;\n}\n\n.align-self-start {\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  align-self: stretch !important;\n}\n\n.order-first {\n  order: -1 !important;\n}\n\n.order-0 {\n  order: 0 !important;\n}\n\n.order-1 {\n  order: 1 !important;\n}\n\n.order-2 {\n  order: 2 !important;\n}\n\n.order-3 {\n  order: 3 !important;\n}\n\n.order-4 {\n  order: 4 !important;\n}\n\n.order-5 {\n  order: 5 !important;\n}\n\n.order-last {\n  order: 6 !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mx-0 {\n  margin-right: 0 !important;\n  margin-left: 0 !important;\n}\n\n.mx-1 {\n  margin-right: 0.25rem !important;\n  margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n  margin-right: 0.5rem !important;\n  margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n  margin-right: 1rem !important;\n  margin-left: 1rem !important;\n}\n\n.mx-4 {\n  margin-right: 1.5rem !important;\n  margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n  margin-right: 3rem !important;\n  margin-left: 3rem !important;\n}\n\n.mx-auto {\n  margin-right: auto !important;\n  margin-left: auto !important;\n}\n\n.my-0 {\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\n\n.my-1 {\n  margin-top: 0.25rem !important;\n  margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n  margin-top: 0.5rem !important;\n  margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n  margin-top: 1rem !important;\n  margin-bottom: 1rem !important;\n}\n\n.my-4 {\n  margin-top: 1.5rem !important;\n  margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n  margin-top: 3rem !important;\n  margin-bottom: 3rem !important;\n}\n\n.my-auto {\n  margin-top: auto !important;\n  margin-bottom: auto !important;\n}\n\n.mt-0 {\n  margin-top: 0 !important;\n}\n\n.mt-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n  margin-top: 1rem !important;\n}\n\n.mt-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n  margin-top: 3rem !important;\n}\n\n.mt-auto {\n  margin-top: auto !important;\n}\n\n.me-0 {\n  margin-right: 0 !important;\n}\n\n.me-1 {\n  margin-right: 0.25rem !important;\n}\n\n.me-2 {\n  margin-right: 0.5rem !important;\n}\n\n.me-3 {\n  margin-right: 1rem !important;\n}\n\n.me-4 {\n  margin-right: 1.5rem !important;\n}\n\n.me-5 {\n  margin-right: 3rem !important;\n}\n\n.me-auto {\n  margin-right: auto !important;\n}\n\n.mb-0 {\n  margin-bottom: 0 !important;\n}\n\n.mb-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n  margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n  margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n  margin-bottom: auto !important;\n}\n\n.ms-0 {\n  margin-left: 0 !important;\n}\n\n.ms-1 {\n  margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n  margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n  margin-left: 1rem !important;\n}\n\n.ms-4 {\n  margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n  margin-left: 3rem !important;\n}\n\n.ms-auto {\n  margin-left: auto !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.px-0 {\n  padding-right: 0 !important;\n  padding-left: 0 !important;\n}\n\n.px-1 {\n  padding-right: 0.25rem !important;\n  padding-left: 0.25rem !important;\n}\n\n.px-2 {\n  padding-right: 0.5rem !important;\n  padding-left: 0.5rem !important;\n}\n\n.px-3 {\n  padding-right: 1rem !important;\n  padding-left: 1rem !important;\n}\n\n.px-4 {\n  padding-right: 1.5rem !important;\n  padding-left: 1.5rem !important;\n}\n\n.px-5 {\n  padding-right: 3rem !important;\n  padding-left: 3rem !important;\n}\n\n.py-0 {\n  padding-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\n.py-1 {\n  padding-top: 0.25rem !important;\n  padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n  padding-top: 0.5rem !important;\n  padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n  padding-top: 1rem !important;\n  padding-bottom: 1rem !important;\n}\n\n.py-4 {\n  padding-top: 1.5rem !important;\n  padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n  padding-top: 3rem !important;\n  padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n  padding-top: 0 !important;\n}\n\n.pt-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n  padding-top: 1rem !important;\n}\n\n.pt-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n  padding-top: 3rem !important;\n}\n\n.pe-0 {\n  padding-right: 0 !important;\n}\n\n.pe-1 {\n  padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n  padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n  padding-right: 1rem !important;\n}\n\n.pe-4 {\n  padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n  padding-right: 3rem !important;\n}\n\n.pb-0 {\n  padding-bottom: 0 !important;\n}\n\n.pb-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n  padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n  padding-left: 0 !important;\n}\n\n.ps-1 {\n  padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n  padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n  padding-left: 1rem !important;\n}\n\n.ps-4 {\n  padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n  padding-left: 3rem !important;\n}\n\n.gap-0 {\n  gap: 0 !important;\n}\n\n.gap-1 {\n  gap: 0.25rem !important;\n}\n\n.gap-2 {\n  gap: 0.5rem !important;\n}\n\n.gap-3 {\n  gap: 1rem !important;\n}\n\n.gap-4 {\n  gap: 1.5rem !important;\n}\n\n.gap-5 {\n  gap: 3rem !important;\n}\n\n.font-monospace {\n  font-family: var(--bs-font-monospace) !important;\n}\n\n.fs-1 {\n  font-size: calc(1.375rem + 1.5vw) !important;\n}\n\n.fs-2 {\n  font-size: calc(1.325rem + 0.9vw) !important;\n}\n\n.fs-3 {\n  font-size: calc(1.3rem + 0.6vw) !important;\n}\n\n.fs-4 {\n  font-size: calc(1.275rem + 0.3vw) !important;\n}\n\n.fs-5 {\n  font-size: 1.25rem !important;\n}\n\n.fs-6 {\n  font-size: 1rem !important;\n}\n\n.fst-italic {\n  font-style: italic !important;\n}\n\n.fst-normal {\n  font-style: normal !important;\n}\n\n.fw-light {\n  font-weight: 300 !important;\n}\n\n.fw-lighter {\n  font-weight: lighter !important;\n}\n\n.fw-normal {\n  font-weight: 400 !important;\n}\n\n.fw-bold {\n  font-weight: 700 !important;\n}\n\n.fw-semibold {\n  font-weight: 600 !important;\n}\n\n.fw-bolder {\n  font-weight: bolder !important;\n}\n\n.lh-1 {\n  line-height: 1 !important;\n}\n\n.lh-sm {\n  line-height: 1.25 !important;\n}\n\n.lh-base {\n  line-height: 1.5 !important;\n}\n\n.lh-lg {\n  line-height: 2 !important;\n}\n\n.text-start {\n  text-align: left !important;\n}\n\n.text-end {\n  text-align: right !important;\n}\n\n.text-center {\n  text-align: center !important;\n}\n\n.text-decoration-none {\n  text-decoration: none !important;\n}\n\n.text-decoration-underline {\n  text-decoration: underline !important;\n}\n\n.text-decoration-line-through {\n  text-decoration: line-through !important;\n}\n\n.text-lowercase {\n  text-transform: lowercase !important;\n}\n\n.text-uppercase {\n  text-transform: uppercase !important;\n}\n\n.text-capitalize {\n  text-transform: capitalize !important;\n}\n\n.text-wrap {\n  white-space: normal !important;\n}\n\n.text-nowrap {\n  white-space: nowrap !important;\n}\n\n/* rtl:begin:remove */\n.text-break {\n  word-wrap: break-word !important;\n  word-break: break-word !important;\n}\n\n/* rtl:end:remove */\n.text-primary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-secondary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-success {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-info {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-warning {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-danger {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-light {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-dark {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-black {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-white {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-body {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-muted {\n  --bs-text-opacity: 1;\n  color: #6c757d !important;\n}\n\n.text-black-50 {\n  --bs-text-opacity: 1;\n  color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n  --bs-text-opacity: 1;\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-reset {\n  --bs-text-opacity: 1;\n  color: inherit !important;\n}\n\n.text-opacity-25 {\n  --bs-text-opacity: 0.25;\n}\n\n.text-opacity-50 {\n  --bs-text-opacity: 0.5;\n}\n\n.text-opacity-75 {\n  --bs-text-opacity: 0.75;\n}\n\n.text-opacity-100 {\n  --bs-text-opacity: 1;\n}\n\n.bg-primary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-secondary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-success {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-info {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-warning {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-danger {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-light {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-dark {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-black {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-white {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-body {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-transparent {\n  --bs-bg-opacity: 1;\n  background-color: transparent !important;\n}\n\n.bg-opacity-10 {\n  --bs-bg-opacity: 0.1;\n}\n\n.bg-opacity-25 {\n  --bs-bg-opacity: 0.25;\n}\n\n.bg-opacity-50 {\n  --bs-bg-opacity: 0.5;\n}\n\n.bg-opacity-75 {\n  --bs-bg-opacity: 0.75;\n}\n\n.bg-opacity-100 {\n  --bs-bg-opacity: 1;\n}\n\n.bg-gradient {\n  background-image: var(--bs-gradient) !important;\n}\n\n.user-select-all {\n  -webkit-user-select: all !important;\n  -moz-user-select: all !important;\n  user-select: all !important;\n}\n\n.user-select-auto {\n  -webkit-user-select: auto !important;\n  -moz-user-select: auto !important;\n  user-select: auto !important;\n}\n\n.user-select-none {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  user-select: none !important;\n}\n\n.pe-none {\n  pointer-events: none !important;\n}\n\n.pe-auto {\n  pointer-events: auto !important;\n}\n\n.rounded {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-0 {\n  border-radius: 0 !important;\n}\n\n.rounded-1 {\n  border-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-2 {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-3 {\n  border-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-4 {\n  border-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-5 {\n  border-radius: var(--bs-border-radius-2xl) !important;\n}\n\n.rounded-circle {\n  border-radius: 50% !important;\n}\n\n.rounded-pill {\n  border-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-top {\n  border-top-left-radius: var(--bs-border-radius) !important;\n  border-top-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-end {\n  border-top-right-radius: var(--bs-border-radius) !important;\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-bottom {\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-start {\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n  border-top-left-radius: var(--bs-border-radius) !important;\n}\n\n.visible {\n  visibility: visible !important;\n}\n\n.invisible {\n  visibility: hidden !important;\n}\n\n@media (min-width: 576px) {\n  .float-sm-start {\n    float: left !important;\n  }\n  .float-sm-end {\n    float: right !important;\n  }\n  .float-sm-none {\n    float: none !important;\n  }\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-grid {\n    display: grid !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-sm-none {\n    display: none !important;\n  }\n  .flex-sm-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-row {\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-sm-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-sm-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-sm-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-sm-start {\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    align-self: stretch !important;\n  }\n  .order-sm-first {\n    order: -1 !important;\n  }\n  .order-sm-0 {\n    order: 0 !important;\n  }\n  .order-sm-1 {\n    order: 1 !important;\n  }\n  .order-sm-2 {\n    order: 2 !important;\n  }\n  .order-sm-3 {\n    order: 3 !important;\n  }\n  .order-sm-4 {\n    order: 4 !important;\n  }\n  .order-sm-5 {\n    order: 5 !important;\n  }\n  .order-sm-last {\n    order: 6 !important;\n  }\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mx-sm-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-sm-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-sm-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-sm-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-sm-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-sm-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-sm-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-sm-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-sm-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-sm-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-sm-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mt-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-sm-auto {\n    margin-top: auto !important;\n  }\n  .me-sm-0 {\n    margin-right: 0 !important;\n  }\n  .me-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .me-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .me-sm-auto {\n    margin-right: auto !important;\n  }\n  .mb-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-sm-0 {\n    margin-left: 0 !important;\n  }\n  .ms-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-sm-auto {\n    margin-left: auto !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .px-sm-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-sm-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-sm-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-sm-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-sm-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-sm-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-sm-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-sm-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-sm-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pt-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-sm-0 {\n    padding-right: 0 !important;\n  }\n  .pe-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-sm-0 {\n    padding-left: 0 !important;\n  }\n  .ps-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-sm-0 {\n    gap: 0 !important;\n  }\n  .gap-sm-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-sm-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-sm-3 {\n    gap: 1rem !important;\n  }\n  .gap-sm-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-sm-5 {\n    gap: 3rem !important;\n  }\n  .text-sm-start {\n    text-align: left !important;\n  }\n  .text-sm-end {\n    text-align: right !important;\n  }\n  .text-sm-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 768px) {\n  .float-md-start {\n    float: left !important;\n  }\n  .float-md-end {\n    float: right !important;\n  }\n  .float-md-none {\n    float: none !important;\n  }\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-grid {\n    display: grid !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-md-none {\n    display: none !important;\n  }\n  .flex-md-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-md-row {\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-md-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-md-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-md-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-md-start {\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    align-self: stretch !important;\n  }\n  .order-md-first {\n    order: -1 !important;\n  }\n  .order-md-0 {\n    order: 0 !important;\n  }\n  .order-md-1 {\n    order: 1 !important;\n  }\n  .order-md-2 {\n    order: 2 !important;\n  }\n  .order-md-3 {\n    order: 3 !important;\n  }\n  .order-md-4 {\n    order: 4 !important;\n  }\n  .order-md-5 {\n    order: 5 !important;\n  }\n  .order-md-last {\n    order: 6 !important;\n  }\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mx-md-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-md-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-md-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-md-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-md-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-md-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-md-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-md-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-md-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-md-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-md-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-md-0 {\n    margin-top: 0 !important;\n  }\n  .mt-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-md-auto {\n    margin-top: auto !important;\n  }\n  .me-md-0 {\n    margin-right: 0 !important;\n  }\n  .me-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-md-3 {\n    margin-right: 1rem !important;\n  }\n  .me-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-md-5 {\n    margin-right: 3rem !important;\n  }\n  .me-md-auto {\n    margin-right: auto !important;\n  }\n  .mb-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-md-0 {\n    margin-left: 0 !important;\n  }\n  .ms-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-md-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-md-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-md-auto {\n    margin-left: auto !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .px-md-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-md-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-md-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-md-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-md-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-md-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-md-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-md-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-md-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-md-0 {\n    padding-top: 0 !important;\n  }\n  .pt-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-md-0 {\n    padding-right: 0 !important;\n  }\n  .pe-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-md-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-md-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-md-0 {\n    padding-left: 0 !important;\n  }\n  .ps-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-md-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-md-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-md-0 {\n    gap: 0 !important;\n  }\n  .gap-md-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-md-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-md-3 {\n    gap: 1rem !important;\n  }\n  .gap-md-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-md-5 {\n    gap: 3rem !important;\n  }\n  .text-md-start {\n    text-align: left !important;\n  }\n  .text-md-end {\n    text-align: right !important;\n  }\n  .text-md-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 992px) {\n  .float-lg-start {\n    float: left !important;\n  }\n  .float-lg-end {\n    float: right !important;\n  }\n  .float-lg-none {\n    float: none !important;\n  }\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-grid {\n    display: grid !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-lg-none {\n    display: none !important;\n  }\n  .flex-lg-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-row {\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-lg-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-lg-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-lg-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-lg-start {\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    align-self: stretch !important;\n  }\n  .order-lg-first {\n    order: -1 !important;\n  }\n  .order-lg-0 {\n    order: 0 !important;\n  }\n  .order-lg-1 {\n    order: 1 !important;\n  }\n  .order-lg-2 {\n    order: 2 !important;\n  }\n  .order-lg-3 {\n    order: 3 !important;\n  }\n  .order-lg-4 {\n    order: 4 !important;\n  }\n  .order-lg-5 {\n    order: 5 !important;\n  }\n  .order-lg-last {\n    order: 6 !important;\n  }\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mx-lg-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-lg-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-lg-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-lg-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-lg-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-lg-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-lg-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-lg-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-lg-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-lg-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-lg-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mt-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-lg-auto {\n    margin-top: auto !important;\n  }\n  .me-lg-0 {\n    margin-right: 0 !important;\n  }\n  .me-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .me-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .me-lg-auto {\n    margin-right: auto !important;\n  }\n  .mb-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-lg-0 {\n    margin-left: 0 !important;\n  }\n  .ms-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-lg-auto {\n    margin-left: auto !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .px-lg-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-lg-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-lg-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-lg-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-lg-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-lg-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-lg-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-lg-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-lg-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pt-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-lg-0 {\n    padding-right: 0 !important;\n  }\n  .pe-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-lg-0 {\n    padding-left: 0 !important;\n  }\n  .ps-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-lg-0 {\n    gap: 0 !important;\n  }\n  .gap-lg-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-lg-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-lg-3 {\n    gap: 1rem !important;\n  }\n  .gap-lg-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-lg-5 {\n    gap: 3rem !important;\n  }\n  .text-lg-start {\n    text-align: left !important;\n  }\n  .text-lg-end {\n    text-align: right !important;\n  }\n  .text-lg-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .float-xl-start {\n    float: left !important;\n  }\n  .float-xl-end {\n    float: right !important;\n  }\n  .float-xl-none {\n    float: none !important;\n  }\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-grid {\n    display: grid !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xl-none {\n    display: none !important;\n  }\n  .flex-xl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-row {\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xl-first {\n    order: -1 !important;\n  }\n  .order-xl-0 {\n    order: 0 !important;\n  }\n  .order-xl-1 {\n    order: 1 !important;\n  }\n  .order-xl-2 {\n    order: 2 !important;\n  }\n  .order-xl-3 {\n    order: 3 !important;\n  }\n  .order-xl-4 {\n    order: 4 !important;\n  }\n  .order-xl-5 {\n    order: 5 !important;\n  }\n  .order-xl-last {\n    order: 6 !important;\n  }\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mx-xl-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-xl-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-xl-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-xl-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-xl-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-xl-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-xl-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-xl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xl-auto {\n    margin-top: auto !important;\n  }\n  .me-xl-0 {\n    margin-right: 0 !important;\n  }\n  .me-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .me-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .me-xl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xl-0 {\n    margin-left: 0 !important;\n  }\n  .ms-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-xl-auto {\n    margin-left: auto !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .px-xl-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-xl-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-xl-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-xl-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-xl-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-xl-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-xl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xl-0 {\n    padding-right: 0 !important;\n  }\n  .pe-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xl-0 {\n    padding-left: 0 !important;\n  }\n  .ps-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-xl-0 {\n    gap: 0 !important;\n  }\n  .gap-xl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xl-5 {\n    gap: 3rem !important;\n  }\n  .text-xl-start {\n    text-align: left !important;\n  }\n  .text-xl-end {\n    text-align: right !important;\n  }\n  .text-xl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1400px) {\n  .float-xxl-start {\n    float: left !important;\n  }\n  .float-xxl-end {\n    float: right !important;\n  }\n  .float-xxl-none {\n    float: none !important;\n  }\n  .d-xxl-inline {\n    display: inline !important;\n  }\n  .d-xxl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xxl-block {\n    display: block !important;\n  }\n  .d-xxl-grid {\n    display: grid !important;\n  }\n  .d-xxl-table {\n    display: table !important;\n  }\n  .d-xxl-table-row {\n    display: table-row !important;\n  }\n  .d-xxl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xxl-flex {\n    display: flex !important;\n  }\n  .d-xxl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xxl-none {\n    display: none !important;\n  }\n  .flex-xxl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xxl-row {\n    flex-direction: row !important;\n  }\n  .flex-xxl-column {\n    flex-direction: column !important;\n  }\n  .flex-xxl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xxl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xxl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xxl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xxl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xxl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xxl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xxl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xxl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xxl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xxl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xxl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xxl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xxl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xxl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xxl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xxl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xxl-center {\n    align-items: center !important;\n  }\n  .align-items-xxl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xxl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xxl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xxl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xxl-center {\n    align-content: center !important;\n  }\n  .align-content-xxl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xxl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xxl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xxl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xxl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xxl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xxl-center {\n    align-self: center !important;\n  }\n  .align-self-xxl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xxl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xxl-first {\n    order: -1 !important;\n  }\n  .order-xxl-0 {\n    order: 0 !important;\n  }\n  .order-xxl-1 {\n    order: 1 !important;\n  }\n  .order-xxl-2 {\n    order: 2 !important;\n  }\n  .order-xxl-3 {\n    order: 3 !important;\n  }\n  .order-xxl-4 {\n    order: 4 !important;\n  }\n  .order-xxl-5 {\n    order: 5 !important;\n  }\n  .order-xxl-last {\n    order: 6 !important;\n  }\n  .m-xxl-0 {\n    margin: 0 !important;\n  }\n  .m-xxl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xxl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xxl-3 {\n    margin: 1rem !important;\n  }\n  .m-xxl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xxl-5 {\n    margin: 3rem !important;\n  }\n  .m-xxl-auto {\n    margin: auto !important;\n  }\n  .mx-xxl-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-xxl-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-xxl-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-xxl-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-xxl-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-xxl-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-xxl-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-xxl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xxl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xxl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xxl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xxl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xxl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xxl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xxl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xxl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xxl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xxl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xxl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xxl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xxl-auto {\n    margin-top: auto !important;\n  }\n  .me-xxl-0 {\n    margin-right: 0 !important;\n  }\n  .me-xxl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-xxl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-xxl-3 {\n    margin-right: 1rem !important;\n  }\n  .me-xxl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-xxl-5 {\n    margin-right: 3rem !important;\n  }\n  .me-xxl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xxl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xxl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xxl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xxl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xxl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xxl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xxl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xxl-0 {\n    margin-left: 0 !important;\n  }\n  .ms-xxl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-xxl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-xxl-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-xxl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-xxl-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-xxl-auto {\n    margin-left: auto !important;\n  }\n  .p-xxl-0 {\n    padding: 0 !important;\n  }\n  .p-xxl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xxl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xxl-3 {\n    padding: 1rem !important;\n  }\n  .p-xxl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xxl-5 {\n    padding: 3rem !important;\n  }\n  .px-xxl-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-xxl-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-xxl-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-xxl-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-xxl-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-xxl-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-xxl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xxl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xxl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xxl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xxl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xxl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xxl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xxl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xxl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xxl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xxl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xxl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xxl-0 {\n    padding-right: 0 !important;\n  }\n  .pe-xxl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-xxl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-xxl-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-xxl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-xxl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xxl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xxl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xxl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xxl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xxl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xxl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xxl-0 {\n    padding-left: 0 !important;\n  }\n  .ps-xxl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-xxl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-xxl-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-xxl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-xxl-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-xxl-0 {\n    gap: 0 !important;\n  }\n  .gap-xxl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xxl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xxl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xxl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xxl-5 {\n    gap: 3rem !important;\n  }\n  .text-xxl-start {\n    text-align: left !important;\n  }\n  .text-xxl-end {\n    text-align: right !important;\n  }\n  .text-xxl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .fs-1 {\n    font-size: 2.5rem !important;\n  }\n  .fs-2 {\n    font-size: 2rem !important;\n  }\n  .fs-3 {\n    font-size: 1.75rem !important;\n  }\n  .fs-4 {\n    font-size: 1.5rem !important;\n  }\n}\n@media print {\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-grid {\n    display: grid !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-print-none {\n    display: none !important;\n  }\n}\n\n/*# sourceMappingURL=bootstrap-utilities.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap-utilities.rtl.css",
    "content": "/*!\n * Bootstrap Utilities v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n.clearfix::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.text-bg-primary {\n  color: #fff !important;\n  background-color: RGBA(13, 110, 253, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-secondary {\n  color: #fff !important;\n  background-color: RGBA(108, 117, 125, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-success {\n  color: #fff !important;\n  background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-info {\n  color: #000 !important;\n  background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-warning {\n  color: #000 !important;\n  background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-danger {\n  color: #fff !important;\n  background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-light {\n  color: #000 !important;\n  background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-dark {\n  color: #fff !important;\n  background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important;\n}\n\n.link-primary {\n  color: #0d6efd !important;\n}\n.link-primary:hover, .link-primary:focus {\n  color: #0a58ca !important;\n}\n\n.link-secondary {\n  color: #6c757d !important;\n}\n.link-secondary:hover, .link-secondary:focus {\n  color: #565e64 !important;\n}\n\n.link-success {\n  color: #198754 !important;\n}\n.link-success:hover, .link-success:focus {\n  color: #146c43 !important;\n}\n\n.link-info {\n  color: #0dcaf0 !important;\n}\n.link-info:hover, .link-info:focus {\n  color: #3dd5f3 !important;\n}\n\n.link-warning {\n  color: #ffc107 !important;\n}\n.link-warning:hover, .link-warning:focus {\n  color: #ffcd39 !important;\n}\n\n.link-danger {\n  color: #dc3545 !important;\n}\n.link-danger:hover, .link-danger:focus {\n  color: #b02a37 !important;\n}\n\n.link-light {\n  color: #f8f9fa !important;\n}\n.link-light:hover, .link-light:focus {\n  color: #f9fafb !important;\n}\n\n.link-dark {\n  color: #212529 !important;\n}\n.link-dark:hover, .link-dark:focus {\n  color: #1a1e21 !important;\n}\n\n.ratio {\n  position: relative;\n  width: 100%;\n}\n.ratio::before {\n  display: block;\n  padding-top: var(--bs-aspect-ratio);\n  content: \"\";\n}\n.ratio > * {\n  position: absolute;\n  top: 0;\n  right: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.ratio-1x1 {\n  --bs-aspect-ratio: 100%;\n}\n\n.ratio-4x3 {\n  --bs-aspect-ratio: 75%;\n}\n\n.ratio-16x9 {\n  --bs-aspect-ratio: 56.25%;\n}\n\n.ratio-21x9 {\n  --bs-aspect-ratio: 42.8571428571%;\n}\n\n.fixed-top {\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: 1030;\n}\n\n.fixed-bottom {\n  position: fixed;\n  left: 0;\n  bottom: 0;\n  right: 0;\n  z-index: 1030;\n}\n\n.sticky-top {\n  position: -webkit-sticky;\n  position: sticky;\n  top: 0;\n  z-index: 1020;\n}\n\n.sticky-bottom {\n  position: -webkit-sticky;\n  position: sticky;\n  bottom: 0;\n  z-index: 1020;\n}\n\n@media (min-width: 576px) {\n  .sticky-sm-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-sm-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 768px) {\n  .sticky-md-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-md-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 992px) {\n  .sticky-lg-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-lg-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1200px) {\n  .sticky-xl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1400px) {\n  .sticky-xxl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xxl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n.hstack {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  align-self: stretch;\n}\n\n.vstack {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  align-self: stretch;\n}\n\n.visually-hidden,\n.visually-hidden-focusable:not(:focus):not(:focus-within) {\n  position: absolute !important;\n  width: 1px !important;\n  height: 1px !important;\n  padding: 0 !important;\n  margin: -1px !important;\n  overflow: hidden !important;\n  clip: rect(0, 0, 0, 0) !important;\n  white-space: nowrap !important;\n  border: 0 !important;\n}\n\n.stretched-link::after {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  right: 0;\n  z-index: 1;\n  content: \"\";\n}\n\n.text-truncate {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.vr {\n  display: inline-block;\n  align-self: stretch;\n  width: 1px;\n  min-height: 1em;\n  background-color: currentcolor;\n  opacity: 0.25;\n}\n\n.align-baseline {\n  vertical-align: baseline !important;\n}\n\n.align-top {\n  vertical-align: top !important;\n}\n\n.align-middle {\n  vertical-align: middle !important;\n}\n\n.align-bottom {\n  vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n  vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n  vertical-align: text-top !important;\n}\n\n.float-start {\n  float: right !important;\n}\n\n.float-end {\n  float: left !important;\n}\n\n.float-none {\n  float: none !important;\n}\n\n.opacity-0 {\n  opacity: 0 !important;\n}\n\n.opacity-25 {\n  opacity: 0.25 !important;\n}\n\n.opacity-50 {\n  opacity: 0.5 !important;\n}\n\n.opacity-75 {\n  opacity: 0.75 !important;\n}\n\n.opacity-100 {\n  opacity: 1 !important;\n}\n\n.overflow-auto {\n  overflow: auto !important;\n}\n\n.overflow-hidden {\n  overflow: hidden !important;\n}\n\n.overflow-visible {\n  overflow: visible !important;\n}\n\n.overflow-scroll {\n  overflow: scroll !important;\n}\n\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-grid {\n  display: grid !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: inline-flex !important;\n}\n\n.d-none {\n  display: none !important;\n}\n\n.shadow {\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-sm {\n  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow-lg {\n  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n  box-shadow: none !important;\n}\n\n.position-static {\n  position: static !important;\n}\n\n.position-relative {\n  position: relative !important;\n}\n\n.position-absolute {\n  position: absolute !important;\n}\n\n.position-fixed {\n  position: fixed !important;\n}\n\n.position-sticky {\n  position: -webkit-sticky !important;\n  position: sticky !important;\n}\n\n.top-0 {\n  top: 0 !important;\n}\n\n.top-50 {\n  top: 50% !important;\n}\n\n.top-100 {\n  top: 100% !important;\n}\n\n.bottom-0 {\n  bottom: 0 !important;\n}\n\n.bottom-50 {\n  bottom: 50% !important;\n}\n\n.bottom-100 {\n  bottom: 100% !important;\n}\n\n.start-0 {\n  right: 0 !important;\n}\n\n.start-50 {\n  right: 50% !important;\n}\n\n.start-100 {\n  right: 100% !important;\n}\n\n.end-0 {\n  left: 0 !important;\n}\n\n.end-50 {\n  left: 50% !important;\n}\n\n.end-100 {\n  left: 100% !important;\n}\n\n.translate-middle {\n  transform: translate(50%, -50%) !important;\n}\n\n.translate-middle-x {\n  transform: translateX(50%) !important;\n}\n\n.translate-middle-y {\n  transform: translateY(-50%) !important;\n}\n\n.border {\n  border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-0 {\n  border: 0 !important;\n}\n\n.border-top {\n  border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-top-0 {\n  border-top: 0 !important;\n}\n\n.border-end {\n  border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-end-0 {\n  border-left: 0 !important;\n}\n\n.border-bottom {\n  border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-bottom-0 {\n  border-bottom: 0 !important;\n}\n\n.border-start {\n  border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-start-0 {\n  border-right: 0 !important;\n}\n\n.border-primary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-secondary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-success {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-info {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-warning {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-danger {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-light {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-dark {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-white {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-1 {\n  --bs-border-width: 1px;\n}\n\n.border-2 {\n  --bs-border-width: 2px;\n}\n\n.border-3 {\n  --bs-border-width: 3px;\n}\n\n.border-4 {\n  --bs-border-width: 4px;\n}\n\n.border-5 {\n  --bs-border-width: 5px;\n}\n\n.border-opacity-10 {\n  --bs-border-opacity: 0.1;\n}\n\n.border-opacity-25 {\n  --bs-border-opacity: 0.25;\n}\n\n.border-opacity-50 {\n  --bs-border-opacity: 0.5;\n}\n\n.border-opacity-75 {\n  --bs-border-opacity: 0.75;\n}\n\n.border-opacity-100 {\n  --bs-border-opacity: 1;\n}\n\n.w-25 {\n  width: 25% !important;\n}\n\n.w-50 {\n  width: 50% !important;\n}\n\n.w-75 {\n  width: 75% !important;\n}\n\n.w-100 {\n  width: 100% !important;\n}\n\n.w-auto {\n  width: auto !important;\n}\n\n.mw-100 {\n  max-width: 100% !important;\n}\n\n.vw-100 {\n  width: 100vw !important;\n}\n\n.min-vw-100 {\n  min-width: 100vw !important;\n}\n\n.h-25 {\n  height: 25% !important;\n}\n\n.h-50 {\n  height: 50% !important;\n}\n\n.h-75 {\n  height: 75% !important;\n}\n\n.h-100 {\n  height: 100% !important;\n}\n\n.h-auto {\n  height: auto !important;\n}\n\n.mh-100 {\n  max-height: 100% !important;\n}\n\n.vh-100 {\n  height: 100vh !important;\n}\n\n.min-vh-100 {\n  min-height: 100vh !important;\n}\n\n.flex-fill {\n  flex: 1 1 auto !important;\n}\n\n.flex-row {\n  flex-direction: row !important;\n}\n\n.flex-column {\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n  justify-content: space-evenly !important;\n}\n\n.align-items-start {\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  align-content: center !important;\n}\n\n.align-content-between {\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  align-self: auto !important;\n}\n\n.align-self-start {\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  align-self: stretch !important;\n}\n\n.order-first {\n  order: -1 !important;\n}\n\n.order-0 {\n  order: 0 !important;\n}\n\n.order-1 {\n  order: 1 !important;\n}\n\n.order-2 {\n  order: 2 !important;\n}\n\n.order-3 {\n  order: 3 !important;\n}\n\n.order-4 {\n  order: 4 !important;\n}\n\n.order-5 {\n  order: 5 !important;\n}\n\n.order-last {\n  order: 6 !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mx-0 {\n  margin-left: 0 !important;\n  margin-right: 0 !important;\n}\n\n.mx-1 {\n  margin-left: 0.25rem !important;\n  margin-right: 0.25rem !important;\n}\n\n.mx-2 {\n  margin-left: 0.5rem !important;\n  margin-right: 0.5rem !important;\n}\n\n.mx-3 {\n  margin-left: 1rem !important;\n  margin-right: 1rem !important;\n}\n\n.mx-4 {\n  margin-left: 1.5rem !important;\n  margin-right: 1.5rem !important;\n}\n\n.mx-5 {\n  margin-left: 3rem !important;\n  margin-right: 3rem !important;\n}\n\n.mx-auto {\n  margin-left: auto !important;\n  margin-right: auto !important;\n}\n\n.my-0 {\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\n\n.my-1 {\n  margin-top: 0.25rem !important;\n  margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n  margin-top: 0.5rem !important;\n  margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n  margin-top: 1rem !important;\n  margin-bottom: 1rem !important;\n}\n\n.my-4 {\n  margin-top: 1.5rem !important;\n  margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n  margin-top: 3rem !important;\n  margin-bottom: 3rem !important;\n}\n\n.my-auto {\n  margin-top: auto !important;\n  margin-bottom: auto !important;\n}\n\n.mt-0 {\n  margin-top: 0 !important;\n}\n\n.mt-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n  margin-top: 1rem !important;\n}\n\n.mt-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n  margin-top: 3rem !important;\n}\n\n.mt-auto {\n  margin-top: auto !important;\n}\n\n.me-0 {\n  margin-left: 0 !important;\n}\n\n.me-1 {\n  margin-left: 0.25rem !important;\n}\n\n.me-2 {\n  margin-left: 0.5rem !important;\n}\n\n.me-3 {\n  margin-left: 1rem !important;\n}\n\n.me-4 {\n  margin-left: 1.5rem !important;\n}\n\n.me-5 {\n  margin-left: 3rem !important;\n}\n\n.me-auto {\n  margin-left: auto !important;\n}\n\n.mb-0 {\n  margin-bottom: 0 !important;\n}\n\n.mb-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n  margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n  margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n  margin-bottom: auto !important;\n}\n\n.ms-0 {\n  margin-right: 0 !important;\n}\n\n.ms-1 {\n  margin-right: 0.25rem !important;\n}\n\n.ms-2 {\n  margin-right: 0.5rem !important;\n}\n\n.ms-3 {\n  margin-right: 1rem !important;\n}\n\n.ms-4 {\n  margin-right: 1.5rem !important;\n}\n\n.ms-5 {\n  margin-right: 3rem !important;\n}\n\n.ms-auto {\n  margin-right: auto !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.px-0 {\n  padding-left: 0 !important;\n  padding-right: 0 !important;\n}\n\n.px-1 {\n  padding-left: 0.25rem !important;\n  padding-right: 0.25rem !important;\n}\n\n.px-2 {\n  padding-left: 0.5rem !important;\n  padding-right: 0.5rem !important;\n}\n\n.px-3 {\n  padding-left: 1rem !important;\n  padding-right: 1rem !important;\n}\n\n.px-4 {\n  padding-left: 1.5rem !important;\n  padding-right: 1.5rem !important;\n}\n\n.px-5 {\n  padding-left: 3rem !important;\n  padding-right: 3rem !important;\n}\n\n.py-0 {\n  padding-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\n.py-1 {\n  padding-top: 0.25rem !important;\n  padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n  padding-top: 0.5rem !important;\n  padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n  padding-top: 1rem !important;\n  padding-bottom: 1rem !important;\n}\n\n.py-4 {\n  padding-top: 1.5rem !important;\n  padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n  padding-top: 3rem !important;\n  padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n  padding-top: 0 !important;\n}\n\n.pt-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n  padding-top: 1rem !important;\n}\n\n.pt-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n  padding-top: 3rem !important;\n}\n\n.pe-0 {\n  padding-left: 0 !important;\n}\n\n.pe-1 {\n  padding-left: 0.25rem !important;\n}\n\n.pe-2 {\n  padding-left: 0.5rem !important;\n}\n\n.pe-3 {\n  padding-left: 1rem !important;\n}\n\n.pe-4 {\n  padding-left: 1.5rem !important;\n}\n\n.pe-5 {\n  padding-left: 3rem !important;\n}\n\n.pb-0 {\n  padding-bottom: 0 !important;\n}\n\n.pb-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n  padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n  padding-right: 0 !important;\n}\n\n.ps-1 {\n  padding-right: 0.25rem !important;\n}\n\n.ps-2 {\n  padding-right: 0.5rem !important;\n}\n\n.ps-3 {\n  padding-right: 1rem !important;\n}\n\n.ps-4 {\n  padding-right: 1.5rem !important;\n}\n\n.ps-5 {\n  padding-right: 3rem !important;\n}\n\n.gap-0 {\n  gap: 0 !important;\n}\n\n.gap-1 {\n  gap: 0.25rem !important;\n}\n\n.gap-2 {\n  gap: 0.5rem !important;\n}\n\n.gap-3 {\n  gap: 1rem !important;\n}\n\n.gap-4 {\n  gap: 1.5rem !important;\n}\n\n.gap-5 {\n  gap: 3rem !important;\n}\n\n.font-monospace {\n  font-family: var(--bs-font-monospace) !important;\n}\n\n.fs-1 {\n  font-size: calc(1.375rem + 1.5vw) !important;\n}\n\n.fs-2 {\n  font-size: calc(1.325rem + 0.9vw) !important;\n}\n\n.fs-3 {\n  font-size: calc(1.3rem + 0.6vw) !important;\n}\n\n.fs-4 {\n  font-size: calc(1.275rem + 0.3vw) !important;\n}\n\n.fs-5 {\n  font-size: 1.25rem !important;\n}\n\n.fs-6 {\n  font-size: 1rem !important;\n}\n\n.fst-italic {\n  font-style: italic !important;\n}\n\n.fst-normal {\n  font-style: normal !important;\n}\n\n.fw-light {\n  font-weight: 300 !important;\n}\n\n.fw-lighter {\n  font-weight: lighter !important;\n}\n\n.fw-normal {\n  font-weight: 400 !important;\n}\n\n.fw-bold {\n  font-weight: 700 !important;\n}\n\n.fw-semibold {\n  font-weight: 600 !important;\n}\n\n.fw-bolder {\n  font-weight: bolder !important;\n}\n\n.lh-1 {\n  line-height: 1 !important;\n}\n\n.lh-sm {\n  line-height: 1.25 !important;\n}\n\n.lh-base {\n  line-height: 1.5 !important;\n}\n\n.lh-lg {\n  line-height: 2 !important;\n}\n\n.text-start {\n  text-align: right !important;\n}\n\n.text-end {\n  text-align: left !important;\n}\n\n.text-center {\n  text-align: center !important;\n}\n\n.text-decoration-none {\n  text-decoration: none !important;\n}\n\n.text-decoration-underline {\n  text-decoration: underline !important;\n}\n\n.text-decoration-line-through {\n  text-decoration: line-through !important;\n}\n\n.text-lowercase {\n  text-transform: lowercase !important;\n}\n\n.text-uppercase {\n  text-transform: uppercase !important;\n}\n\n.text-capitalize {\n  text-transform: capitalize !important;\n}\n\n.text-wrap {\n  white-space: normal !important;\n}\n\n.text-nowrap {\n  white-space: nowrap !important;\n}\n.text-primary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-secondary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-success {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-info {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-warning {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-danger {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-light {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-dark {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-black {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-white {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-body {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-muted {\n  --bs-text-opacity: 1;\n  color: #6c757d !important;\n}\n\n.text-black-50 {\n  --bs-text-opacity: 1;\n  color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n  --bs-text-opacity: 1;\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-reset {\n  --bs-text-opacity: 1;\n  color: inherit !important;\n}\n\n.text-opacity-25 {\n  --bs-text-opacity: 0.25;\n}\n\n.text-opacity-50 {\n  --bs-text-opacity: 0.5;\n}\n\n.text-opacity-75 {\n  --bs-text-opacity: 0.75;\n}\n\n.text-opacity-100 {\n  --bs-text-opacity: 1;\n}\n\n.bg-primary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-secondary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-success {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-info {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-warning {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-danger {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-light {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-dark {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-black {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-white {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-body {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-transparent {\n  --bs-bg-opacity: 1;\n  background-color: transparent !important;\n}\n\n.bg-opacity-10 {\n  --bs-bg-opacity: 0.1;\n}\n\n.bg-opacity-25 {\n  --bs-bg-opacity: 0.25;\n}\n\n.bg-opacity-50 {\n  --bs-bg-opacity: 0.5;\n}\n\n.bg-opacity-75 {\n  --bs-bg-opacity: 0.75;\n}\n\n.bg-opacity-100 {\n  --bs-bg-opacity: 1;\n}\n\n.bg-gradient {\n  background-image: var(--bs-gradient) !important;\n}\n\n.user-select-all {\n  -webkit-user-select: all !important;\n  -moz-user-select: all !important;\n  user-select: all !important;\n}\n\n.user-select-auto {\n  -webkit-user-select: auto !important;\n  -moz-user-select: auto !important;\n  user-select: auto !important;\n}\n\n.user-select-none {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  user-select: none !important;\n}\n\n.pe-none {\n  pointer-events: none !important;\n}\n\n.pe-auto {\n  pointer-events: auto !important;\n}\n\n.rounded {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-0 {\n  border-radius: 0 !important;\n}\n\n.rounded-1 {\n  border-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-2 {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-3 {\n  border-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-4 {\n  border-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-5 {\n  border-radius: var(--bs-border-radius-2xl) !important;\n}\n\n.rounded-circle {\n  border-radius: 50% !important;\n}\n\n.rounded-pill {\n  border-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-top {\n  border-top-right-radius: var(--bs-border-radius) !important;\n  border-top-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-end {\n  border-top-left-radius: var(--bs-border-radius) !important;\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-bottom {\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-start {\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n  border-top-right-radius: var(--bs-border-radius) !important;\n}\n\n.visible {\n  visibility: visible !important;\n}\n\n.invisible {\n  visibility: hidden !important;\n}\n\n@media (min-width: 576px) {\n  .float-sm-start {\n    float: right !important;\n  }\n  .float-sm-end {\n    float: left !important;\n  }\n  .float-sm-none {\n    float: none !important;\n  }\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-grid {\n    display: grid !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-sm-none {\n    display: none !important;\n  }\n  .flex-sm-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-row {\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-sm-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-sm-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-sm-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-sm-start {\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    align-self: stretch !important;\n  }\n  .order-sm-first {\n    order: -1 !important;\n  }\n  .order-sm-0 {\n    order: 0 !important;\n  }\n  .order-sm-1 {\n    order: 1 !important;\n  }\n  .order-sm-2 {\n    order: 2 !important;\n  }\n  .order-sm-3 {\n    order: 3 !important;\n  }\n  .order-sm-4 {\n    order: 4 !important;\n  }\n  .order-sm-5 {\n    order: 5 !important;\n  }\n  .order-sm-last {\n    order: 6 !important;\n  }\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mx-sm-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-sm-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-sm-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-sm-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-sm-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-sm-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-sm-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-sm-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-sm-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-sm-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-sm-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mt-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-sm-auto {\n    margin-top: auto !important;\n  }\n  .me-sm-0 {\n    margin-left: 0 !important;\n  }\n  .me-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .me-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .me-sm-auto {\n    margin-left: auto !important;\n  }\n  .mb-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-sm-0 {\n    margin-right: 0 !important;\n  }\n  .ms-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-sm-auto {\n    margin-right: auto !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .px-sm-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-sm-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-sm-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-sm-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-sm-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-sm-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-sm-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-sm-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-sm-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pt-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-sm-0 {\n    padding-left: 0 !important;\n  }\n  .pe-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-sm-0 {\n    padding-right: 0 !important;\n  }\n  .ps-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-sm-0 {\n    gap: 0 !important;\n  }\n  .gap-sm-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-sm-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-sm-3 {\n    gap: 1rem !important;\n  }\n  .gap-sm-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-sm-5 {\n    gap: 3rem !important;\n  }\n  .text-sm-start {\n    text-align: right !important;\n  }\n  .text-sm-end {\n    text-align: left !important;\n  }\n  .text-sm-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 768px) {\n  .float-md-start {\n    float: right !important;\n  }\n  .float-md-end {\n    float: left !important;\n  }\n  .float-md-none {\n    float: none !important;\n  }\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-grid {\n    display: grid !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-md-none {\n    display: none !important;\n  }\n  .flex-md-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-md-row {\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-md-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-md-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-md-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-md-start {\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    align-self: stretch !important;\n  }\n  .order-md-first {\n    order: -1 !important;\n  }\n  .order-md-0 {\n    order: 0 !important;\n  }\n  .order-md-1 {\n    order: 1 !important;\n  }\n  .order-md-2 {\n    order: 2 !important;\n  }\n  .order-md-3 {\n    order: 3 !important;\n  }\n  .order-md-4 {\n    order: 4 !important;\n  }\n  .order-md-5 {\n    order: 5 !important;\n  }\n  .order-md-last {\n    order: 6 !important;\n  }\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mx-md-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-md-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-md-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-md-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-md-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-md-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-md-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-md-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-md-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-md-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-md-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-md-0 {\n    margin-top: 0 !important;\n  }\n  .mt-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-md-auto {\n    margin-top: auto !important;\n  }\n  .me-md-0 {\n    margin-left: 0 !important;\n  }\n  .me-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-md-3 {\n    margin-left: 1rem !important;\n  }\n  .me-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-md-5 {\n    margin-left: 3rem !important;\n  }\n  .me-md-auto {\n    margin-left: auto !important;\n  }\n  .mb-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-md-0 {\n    margin-right: 0 !important;\n  }\n  .ms-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-md-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-md-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-md-auto {\n    margin-right: auto !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .px-md-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-md-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-md-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-md-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-md-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-md-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-md-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-md-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-md-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-md-0 {\n    padding-top: 0 !important;\n  }\n  .pt-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-md-0 {\n    padding-left: 0 !important;\n  }\n  .pe-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-md-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-md-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-md-0 {\n    padding-right: 0 !important;\n  }\n  .ps-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-md-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-md-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-md-0 {\n    gap: 0 !important;\n  }\n  .gap-md-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-md-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-md-3 {\n    gap: 1rem !important;\n  }\n  .gap-md-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-md-5 {\n    gap: 3rem !important;\n  }\n  .text-md-start {\n    text-align: right !important;\n  }\n  .text-md-end {\n    text-align: left !important;\n  }\n  .text-md-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 992px) {\n  .float-lg-start {\n    float: right !important;\n  }\n  .float-lg-end {\n    float: left !important;\n  }\n  .float-lg-none {\n    float: none !important;\n  }\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-grid {\n    display: grid !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-lg-none {\n    display: none !important;\n  }\n  .flex-lg-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-row {\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-lg-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-lg-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-lg-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-lg-start {\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    align-self: stretch !important;\n  }\n  .order-lg-first {\n    order: -1 !important;\n  }\n  .order-lg-0 {\n    order: 0 !important;\n  }\n  .order-lg-1 {\n    order: 1 !important;\n  }\n  .order-lg-2 {\n    order: 2 !important;\n  }\n  .order-lg-3 {\n    order: 3 !important;\n  }\n  .order-lg-4 {\n    order: 4 !important;\n  }\n  .order-lg-5 {\n    order: 5 !important;\n  }\n  .order-lg-last {\n    order: 6 !important;\n  }\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mx-lg-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-lg-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-lg-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-lg-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-lg-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-lg-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-lg-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-lg-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-lg-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-lg-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-lg-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mt-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-lg-auto {\n    margin-top: auto !important;\n  }\n  .me-lg-0 {\n    margin-left: 0 !important;\n  }\n  .me-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .me-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .me-lg-auto {\n    margin-left: auto !important;\n  }\n  .mb-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-lg-0 {\n    margin-right: 0 !important;\n  }\n  .ms-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-lg-auto {\n    margin-right: auto !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .px-lg-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-lg-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-lg-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-lg-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-lg-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-lg-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-lg-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-lg-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-lg-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pt-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-lg-0 {\n    padding-left: 0 !important;\n  }\n  .pe-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-lg-0 {\n    padding-right: 0 !important;\n  }\n  .ps-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-lg-0 {\n    gap: 0 !important;\n  }\n  .gap-lg-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-lg-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-lg-3 {\n    gap: 1rem !important;\n  }\n  .gap-lg-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-lg-5 {\n    gap: 3rem !important;\n  }\n  .text-lg-start {\n    text-align: right !important;\n  }\n  .text-lg-end {\n    text-align: left !important;\n  }\n  .text-lg-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .float-xl-start {\n    float: right !important;\n  }\n  .float-xl-end {\n    float: left !important;\n  }\n  .float-xl-none {\n    float: none !important;\n  }\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-grid {\n    display: grid !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xl-none {\n    display: none !important;\n  }\n  .flex-xl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-row {\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xl-first {\n    order: -1 !important;\n  }\n  .order-xl-0 {\n    order: 0 !important;\n  }\n  .order-xl-1 {\n    order: 1 !important;\n  }\n  .order-xl-2 {\n    order: 2 !important;\n  }\n  .order-xl-3 {\n    order: 3 !important;\n  }\n  .order-xl-4 {\n    order: 4 !important;\n  }\n  .order-xl-5 {\n    order: 5 !important;\n  }\n  .order-xl-last {\n    order: 6 !important;\n  }\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mx-xl-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-xl-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-xl-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-xl-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-xl-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-xl-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-xl-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-xl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xl-auto {\n    margin-top: auto !important;\n  }\n  .me-xl-0 {\n    margin-left: 0 !important;\n  }\n  .me-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .me-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .me-xl-auto {\n    margin-left: auto !important;\n  }\n  .mb-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xl-0 {\n    margin-right: 0 !important;\n  }\n  .ms-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-xl-auto {\n    margin-right: auto !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .px-xl-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-xl-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-xl-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-xl-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-xl-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-xl-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-xl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xl-0 {\n    padding-left: 0 !important;\n  }\n  .pe-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xl-0 {\n    padding-right: 0 !important;\n  }\n  .ps-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-xl-0 {\n    gap: 0 !important;\n  }\n  .gap-xl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xl-5 {\n    gap: 3rem !important;\n  }\n  .text-xl-start {\n    text-align: right !important;\n  }\n  .text-xl-end {\n    text-align: left !important;\n  }\n  .text-xl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1400px) {\n  .float-xxl-start {\n    float: right !important;\n  }\n  .float-xxl-end {\n    float: left !important;\n  }\n  .float-xxl-none {\n    float: none !important;\n  }\n  .d-xxl-inline {\n    display: inline !important;\n  }\n  .d-xxl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xxl-block {\n    display: block !important;\n  }\n  .d-xxl-grid {\n    display: grid !important;\n  }\n  .d-xxl-table {\n    display: table !important;\n  }\n  .d-xxl-table-row {\n    display: table-row !important;\n  }\n  .d-xxl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xxl-flex {\n    display: flex !important;\n  }\n  .d-xxl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xxl-none {\n    display: none !important;\n  }\n  .flex-xxl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xxl-row {\n    flex-direction: row !important;\n  }\n  .flex-xxl-column {\n    flex-direction: column !important;\n  }\n  .flex-xxl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xxl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xxl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xxl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xxl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xxl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xxl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xxl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xxl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xxl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xxl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xxl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xxl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xxl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xxl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xxl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xxl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xxl-center {\n    align-items: center !important;\n  }\n  .align-items-xxl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xxl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xxl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xxl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xxl-center {\n    align-content: center !important;\n  }\n  .align-content-xxl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xxl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xxl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xxl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xxl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xxl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xxl-center {\n    align-self: center !important;\n  }\n  .align-self-xxl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xxl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xxl-first {\n    order: -1 !important;\n  }\n  .order-xxl-0 {\n    order: 0 !important;\n  }\n  .order-xxl-1 {\n    order: 1 !important;\n  }\n  .order-xxl-2 {\n    order: 2 !important;\n  }\n  .order-xxl-3 {\n    order: 3 !important;\n  }\n  .order-xxl-4 {\n    order: 4 !important;\n  }\n  .order-xxl-5 {\n    order: 5 !important;\n  }\n  .order-xxl-last {\n    order: 6 !important;\n  }\n  .m-xxl-0 {\n    margin: 0 !important;\n  }\n  .m-xxl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xxl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xxl-3 {\n    margin: 1rem !important;\n  }\n  .m-xxl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xxl-5 {\n    margin: 3rem !important;\n  }\n  .m-xxl-auto {\n    margin: auto !important;\n  }\n  .mx-xxl-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-xxl-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-xxl-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-xxl-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-xxl-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-xxl-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-xxl-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-xxl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xxl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xxl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xxl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xxl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xxl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xxl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xxl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xxl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xxl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xxl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xxl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xxl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xxl-auto {\n    margin-top: auto !important;\n  }\n  .me-xxl-0 {\n    margin-left: 0 !important;\n  }\n  .me-xxl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-xxl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-xxl-3 {\n    margin-left: 1rem !important;\n  }\n  .me-xxl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-xxl-5 {\n    margin-left: 3rem !important;\n  }\n  .me-xxl-auto {\n    margin-left: auto !important;\n  }\n  .mb-xxl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xxl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xxl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xxl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xxl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xxl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xxl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xxl-0 {\n    margin-right: 0 !important;\n  }\n  .ms-xxl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-xxl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-xxl-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-xxl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-xxl-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-xxl-auto {\n    margin-right: auto !important;\n  }\n  .p-xxl-0 {\n    padding: 0 !important;\n  }\n  .p-xxl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xxl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xxl-3 {\n    padding: 1rem !important;\n  }\n  .p-xxl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xxl-5 {\n    padding: 3rem !important;\n  }\n  .px-xxl-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-xxl-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-xxl-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-xxl-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-xxl-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-xxl-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-xxl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xxl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xxl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xxl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xxl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xxl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xxl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xxl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xxl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xxl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xxl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xxl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xxl-0 {\n    padding-left: 0 !important;\n  }\n  .pe-xxl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-xxl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-xxl-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-xxl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-xxl-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-xxl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xxl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xxl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xxl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xxl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xxl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xxl-0 {\n    padding-right: 0 !important;\n  }\n  .ps-xxl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-xxl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-xxl-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-xxl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-xxl-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-xxl-0 {\n    gap: 0 !important;\n  }\n  .gap-xxl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xxl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xxl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xxl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xxl-5 {\n    gap: 3rem !important;\n  }\n  .text-xxl-start {\n    text-align: right !important;\n  }\n  .text-xxl-end {\n    text-align: left !important;\n  }\n  .text-xxl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .fs-1 {\n    font-size: 2.5rem !important;\n  }\n  .fs-2 {\n    font-size: 2rem !important;\n  }\n  .fs-3 {\n    font-size: 1.75rem !important;\n  }\n  .fs-4 {\n    font-size: 1.5rem !important;\n  }\n}\n@media print {\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-grid {\n    display: grid !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-print-none {\n    display: none !important;\n  }\n}\n/*# sourceMappingURL=bootstrap-utilities.rtl.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap.css",
    "content": "@charset \"UTF-8\";\n/*!\n * Bootstrap  v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  :root {\n    scroll-behavior: smooth;\n  }\n}\n\nbody {\n  margin: 0;\n  font-family: var(--bs-body-font-family);\n  font-size: var(--bs-body-font-size);\n  font-weight: var(--bs-body-font-weight);\n  line-height: var(--bs-body-line-height);\n  color: var(--bs-body-color);\n  text-align: var(--bs-body-text-align);\n  background-color: var(--bs-body-bg);\n  -webkit-text-size-adjust: 100%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nhr {\n  margin: 1rem 0;\n  color: inherit;\n  border: 0;\n  border-top: 1px solid;\n  opacity: 0.25;\n}\n\nh6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n  font-weight: 500;\n  line-height: 1.2;\n}\n\nh1, .h1 {\n  font-size: calc(1.375rem + 1.5vw);\n}\n@media (min-width: 1200px) {\n  h1, .h1 {\n    font-size: 2.5rem;\n  }\n}\n\nh2, .h2 {\n  font-size: calc(1.325rem + 0.9vw);\n}\n@media (min-width: 1200px) {\n  h2, .h2 {\n    font-size: 2rem;\n  }\n}\n\nh3, .h3 {\n  font-size: calc(1.3rem + 0.6vw);\n}\n@media (min-width: 1200px) {\n  h3, .h3 {\n    font-size: 1.75rem;\n  }\n}\n\nh4, .h4 {\n  font-size: calc(1.275rem + 0.3vw);\n}\n@media (min-width: 1200px) {\n  h4, .h4 {\n    font-size: 1.5rem;\n  }\n}\n\nh5, .h5 {\n  font-size: 1.25rem;\n}\n\nh6, .h6 {\n  font-size: 1rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title] {\n  -webkit-text-decoration: underline dotted;\n  text-decoration: underline dotted;\n  cursor: help;\n  -webkit-text-decoration-skip-ink: none;\n  text-decoration-skip-ink: none;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul {\n  padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: 700;\n}\n\ndd {\n  margin-bottom: 0.5rem;\n  margin-left: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall, .small {\n  font-size: 0.875em;\n}\n\nmark, .mark {\n  padding: 0.1875em;\n  background-color: var(--bs-highlight-bg);\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 0.75em;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nsup {\n  top: -0.5em;\n}\n\na {\n  color: var(--bs-link-color);\n  text-decoration: underline;\n}\na:hover {\n  color: var(--bs-link-hover-color);\n}\n\na:not([href]):not([class]), a:not([href]):not([class]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: var(--bs-font-monospace);\n  font-size: 1em;\n}\n\npre {\n  display: block;\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n  font-size: 0.875em;\n}\npre code {\n  font-size: inherit;\n  color: inherit;\n  word-break: normal;\n}\n\ncode {\n  font-size: 0.875em;\n  color: var(--bs-code-color);\n  word-wrap: break-word;\n}\na > code {\n  color: inherit;\n}\n\nkbd {\n  padding: 0.1875rem 0.375rem;\n  font-size: 0.875em;\n  color: var(--bs-body-bg);\n  background-color: var(--bs-body-color);\n  border-radius: 0.25rem;\n}\nkbd kbd {\n  padding: 0;\n  font-size: 1em;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg,\nsvg {\n  vertical-align: middle;\n}\n\ntable {\n  caption-side: bottom;\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  color: #6c757d;\n  text-align: left;\n}\n\nth {\n  text-align: inherit;\n  text-align: -webkit-match-parent;\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n}\n\nlabel {\n  display: inline-block;\n}\n\nbutton {\n  border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n  outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n[role=button] {\n  cursor: pointer;\n}\n\nselect {\n  word-wrap: normal;\n}\nselect:disabled {\n  opacity: 1;\n}\n\n[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {\n  display: none !important;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n  -webkit-appearance: button;\n}\nbutton:not(:disabled),\n[type=button]:not(:disabled),\n[type=reset]:not(:disabled),\n[type=submit]:not(:disabled) {\n  cursor: pointer;\n}\n\n::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ntextarea {\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  float: left;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 0.5rem;\n  font-size: calc(1.275rem + 0.3vw);\n  line-height: inherit;\n}\n@media (min-width: 1200px) {\n  legend {\n    font-size: 1.5rem;\n  }\n}\nlegend + * {\n  clear: left;\n}\n\n::-webkit-datetime-edit-fields-wrapper,\n::-webkit-datetime-edit-text,\n::-webkit-datetime-edit-minute,\n::-webkit-datetime-edit-hour-field,\n::-webkit-datetime-edit-day-field,\n::-webkit-datetime-edit-month-field,\n::-webkit-datetime-edit-year-field {\n  padding: 0;\n}\n\n::-webkit-inner-spin-button {\n  height: auto;\n}\n\n[type=search] {\n  outline-offset: -2px;\n  -webkit-appearance: textfield;\n}\n\n/* rtl:raw:\n[type=\"tel\"],\n[type=\"url\"],\n[type=\"email\"],\n[type=\"number\"] {\n  direction: ltr;\n}\n*/\n::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-color-swatch-wrapper {\n  padding: 0;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\n::file-selector-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\niframe {\n  border: 0;\n}\n\nsummary {\n  display: list-item;\n  cursor: pointer;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[hidden] {\n  display: none !important;\n}\n\n.lead {\n  font-size: 1.25rem;\n  font-weight: 300;\n}\n\n.display-1 {\n  font-size: calc(1.625rem + 4.5vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-1 {\n    font-size: 5rem;\n  }\n}\n\n.display-2 {\n  font-size: calc(1.575rem + 3.9vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-2 {\n    font-size: 4.5rem;\n  }\n}\n\n.display-3 {\n  font-size: calc(1.525rem + 3.3vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-3 {\n    font-size: 4rem;\n  }\n}\n\n.display-4 {\n  font-size: calc(1.475rem + 2.7vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-4 {\n    font-size: 3.5rem;\n  }\n}\n\n.display-5 {\n  font-size: calc(1.425rem + 2.1vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-5 {\n    font-size: 3rem;\n  }\n}\n\n.display-6 {\n  font-size: calc(1.375rem + 1.5vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-6 {\n    font-size: 2.5rem;\n  }\n}\n\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline-item {\n  display: inline-block;\n}\n.list-inline-item:not(:last-child) {\n  margin-right: 0.5rem;\n}\n\n.initialism {\n  font-size: 0.875em;\n  text-transform: uppercase;\n}\n\n.blockquote {\n  margin-bottom: 1rem;\n  font-size: 1.25rem;\n}\n.blockquote > :last-child {\n  margin-bottom: 0;\n}\n\n.blockquote-footer {\n  margin-top: -1rem;\n  margin-bottom: 1rem;\n  font-size: 0.875em;\n  color: #6c757d;\n}\n.blockquote-footer::before {\n  content: \"— \";\n}\n\n.img-fluid {\n  max-width: 100%;\n  height: auto;\n}\n\n.img-thumbnail {\n  padding: 0.25rem;\n  background-color: #fff;\n  border: 1px solid var(--bs-border-color);\n  border-radius: 0.375rem;\n  max-width: 100%;\n  height: auto;\n}\n\n.figure {\n  display: inline-block;\n}\n\n.figure-img {\n  margin-bottom: 0.5rem;\n  line-height: 1;\n}\n\n.figure-caption {\n  font-size: 0.875em;\n  color: #6c757d;\n}\n\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  width: 100%;\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  margin-right: auto;\n  margin-left: auto;\n}\n\n@media (min-width: 576px) {\n  .container-sm, .container {\n    max-width: 540px;\n  }\n}\n@media (min-width: 768px) {\n  .container-md, .container-sm, .container {\n    max-width: 720px;\n  }\n}\n@media (min-width: 992px) {\n  .container-lg, .container-md, .container-sm, .container {\n    max-width: 960px;\n  }\n}\n@media (min-width: 1200px) {\n  .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1140px;\n  }\n}\n@media (min-width: 1400px) {\n  .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1320px;\n  }\n}\n.row {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  display: flex;\n  flex-wrap: wrap;\n  margin-top: calc(-1 * var(--bs-gutter-y));\n  margin-right: calc(-0.5 * var(--bs-gutter-x));\n  margin-left: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n  flex-shrink: 0;\n  width: 100%;\n  max-width: 100%;\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  margin-top: var(--bs-gutter-y);\n}\n\n.col {\n  flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.row-cols-1 > * {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.row-cols-2 > * {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.row-cols-3 > * {\n  flex: 0 0 auto;\n  width: 33.3333333333%;\n}\n\n.row-cols-4 > * {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.row-cols-5 > * {\n  flex: 0 0 auto;\n  width: 20%;\n}\n\n.row-cols-6 > * {\n  flex: 0 0 auto;\n  width: 16.6666666667%;\n}\n\n.col-auto {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.col-1 {\n  flex: 0 0 auto;\n  width: 8.33333333%;\n}\n\n.col-2 {\n  flex: 0 0 auto;\n  width: 16.66666667%;\n}\n\n.col-3 {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.col-4 {\n  flex: 0 0 auto;\n  width: 33.33333333%;\n}\n\n.col-5 {\n  flex: 0 0 auto;\n  width: 41.66666667%;\n}\n\n.col-6 {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.col-7 {\n  flex: 0 0 auto;\n  width: 58.33333333%;\n}\n\n.col-8 {\n  flex: 0 0 auto;\n  width: 66.66666667%;\n}\n\n.col-9 {\n  flex: 0 0 auto;\n  width: 75%;\n}\n\n.col-10 {\n  flex: 0 0 auto;\n  width: 83.33333333%;\n}\n\n.col-11 {\n  flex: 0 0 auto;\n  width: 91.66666667%;\n}\n\n.col-12 {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.offset-1 {\n  margin-left: 8.33333333%;\n}\n\n.offset-2 {\n  margin-left: 16.66666667%;\n}\n\n.offset-3 {\n  margin-left: 25%;\n}\n\n.offset-4 {\n  margin-left: 33.33333333%;\n}\n\n.offset-5 {\n  margin-left: 41.66666667%;\n}\n\n.offset-6 {\n  margin-left: 50%;\n}\n\n.offset-7 {\n  margin-left: 58.33333333%;\n}\n\n.offset-8 {\n  margin-left: 66.66666667%;\n}\n\n.offset-9 {\n  margin-left: 75%;\n}\n\n.offset-10 {\n  margin-left: 83.33333333%;\n}\n\n.offset-11 {\n  margin-left: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n  --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n  --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n  --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n  --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n  --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n  --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n  --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n  --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n  --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n  --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n  --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n  --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n  .col-sm {\n    flex: 1 0 0%;\n  }\n  .row-cols-sm-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-sm-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-sm-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-sm-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-sm-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-sm-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-sm-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-sm-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-sm-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-sm-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-sm-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-sm-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-sm-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-sm-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-sm-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-sm-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-sm-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-sm-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-sm-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-sm-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-sm-0 {\n    margin-left: 0;\n  }\n  .offset-sm-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-sm-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-sm-3 {\n    margin-left: 25%;\n  }\n  .offset-sm-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-sm-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-sm-6 {\n    margin-left: 50%;\n  }\n  .offset-sm-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-sm-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-sm-9 {\n    margin-left: 75%;\n  }\n  .offset-sm-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-sm-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-sm-0,\n.gx-sm-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-sm-0,\n.gy-sm-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-sm-1,\n.gx-sm-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-sm-1,\n.gy-sm-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-sm-2,\n.gx-sm-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-sm-2,\n.gy-sm-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-sm-3,\n.gx-sm-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-sm-3,\n.gy-sm-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-sm-4,\n.gx-sm-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-sm-4,\n.gy-sm-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-sm-5,\n.gx-sm-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-sm-5,\n.gy-sm-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 768px) {\n  .col-md {\n    flex: 1 0 0%;\n  }\n  .row-cols-md-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-md-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-md-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-md-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-md-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-md-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-md-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-md-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-md-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-md-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-md-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-md-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-md-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-md-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-md-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-md-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-md-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-md-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-md-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-md-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-md-0 {\n    margin-left: 0;\n  }\n  .offset-md-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-md-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-md-3 {\n    margin-left: 25%;\n  }\n  .offset-md-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-md-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-md-6 {\n    margin-left: 50%;\n  }\n  .offset-md-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-md-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-md-9 {\n    margin-left: 75%;\n  }\n  .offset-md-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-md-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-md-0,\n.gx-md-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-md-0,\n.gy-md-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-md-1,\n.gx-md-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-md-1,\n.gy-md-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-md-2,\n.gx-md-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-md-2,\n.gy-md-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-md-3,\n.gx-md-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-md-3,\n.gy-md-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-md-4,\n.gx-md-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-md-4,\n.gy-md-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-md-5,\n.gx-md-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-md-5,\n.gy-md-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 992px) {\n  .col-lg {\n    flex: 1 0 0%;\n  }\n  .row-cols-lg-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-lg-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-lg-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-lg-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-lg-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-lg-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-lg-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-lg-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-lg-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-lg-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-lg-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-lg-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-lg-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-lg-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-lg-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-lg-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-lg-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-lg-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-lg-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-lg-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-lg-0 {\n    margin-left: 0;\n  }\n  .offset-lg-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-lg-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-lg-3 {\n    margin-left: 25%;\n  }\n  .offset-lg-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-lg-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-lg-6 {\n    margin-left: 50%;\n  }\n  .offset-lg-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-lg-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-lg-9 {\n    margin-left: 75%;\n  }\n  .offset-lg-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-lg-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-lg-0,\n.gx-lg-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-lg-0,\n.gy-lg-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-lg-1,\n.gx-lg-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-lg-1,\n.gy-lg-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-lg-2,\n.gx-lg-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-lg-2,\n.gy-lg-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-lg-3,\n.gx-lg-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-lg-3,\n.gy-lg-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-lg-4,\n.gx-lg-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-lg-4,\n.gy-lg-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-lg-5,\n.gx-lg-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-lg-5,\n.gy-lg-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1200px) {\n  .col-xl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xl-0 {\n    margin-left: 0;\n  }\n  .offset-xl-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-xl-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-xl-3 {\n    margin-left: 25%;\n  }\n  .offset-xl-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-xl-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-xl-6 {\n    margin-left: 50%;\n  }\n  .offset-xl-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-xl-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-xl-9 {\n    margin-left: 75%;\n  }\n  .offset-xl-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-xl-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-xl-0,\n.gx-xl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xl-0,\n.gy-xl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xl-1,\n.gx-xl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xl-1,\n.gy-xl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xl-2,\n.gx-xl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xl-2,\n.gy-xl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xl-3,\n.gx-xl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xl-3,\n.gy-xl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xl-4,\n.gx-xl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xl-4,\n.gy-xl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xl-5,\n.gx-xl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xl-5,\n.gy-xl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1400px) {\n  .col-xxl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xxl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xxl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xxl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xxl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xxl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xxl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xxl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xxl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xxl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xxl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xxl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xxl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xxl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xxl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xxl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xxl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xxl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xxl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xxl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xxl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xxl-0 {\n    margin-left: 0;\n  }\n  .offset-xxl-1 {\n    margin-left: 8.33333333%;\n  }\n  .offset-xxl-2 {\n    margin-left: 16.66666667%;\n  }\n  .offset-xxl-3 {\n    margin-left: 25%;\n  }\n  .offset-xxl-4 {\n    margin-left: 33.33333333%;\n  }\n  .offset-xxl-5 {\n    margin-left: 41.66666667%;\n  }\n  .offset-xxl-6 {\n    margin-left: 50%;\n  }\n  .offset-xxl-7 {\n    margin-left: 58.33333333%;\n  }\n  .offset-xxl-8 {\n    margin-left: 66.66666667%;\n  }\n  .offset-xxl-9 {\n    margin-left: 75%;\n  }\n  .offset-xxl-10 {\n    margin-left: 83.33333333%;\n  }\n  .offset-xxl-11 {\n    margin-left: 91.66666667%;\n  }\n  .g-xxl-0,\n.gx-xxl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xxl-0,\n.gy-xxl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xxl-1,\n.gx-xxl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xxl-1,\n.gy-xxl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xxl-2,\n.gx-xxl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xxl-2,\n.gy-xxl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xxl-3,\n.gx-xxl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xxl-3,\n.gy-xxl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xxl-4,\n.gx-xxl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xxl-4,\n.gy-xxl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xxl-5,\n.gx-xxl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xxl-5,\n.gy-xxl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n.table {\n  --bs-table-color: var(--bs-body-color);\n  --bs-table-bg: transparent;\n  --bs-table-border-color: var(--bs-border-color);\n  --bs-table-accent-bg: transparent;\n  --bs-table-striped-color: var(--bs-body-color);\n  --bs-table-striped-bg: rgba(0, 0, 0, 0.05);\n  --bs-table-active-color: var(--bs-body-color);\n  --bs-table-active-bg: rgba(0, 0, 0, 0.1);\n  --bs-table-hover-color: var(--bs-body-color);\n  --bs-table-hover-bg: rgba(0, 0, 0, 0.075);\n  width: 100%;\n  margin-bottom: 1rem;\n  color: var(--bs-table-color);\n  vertical-align: top;\n  border-color: var(--bs-table-border-color);\n}\n.table > :not(caption) > * > * {\n  padding: 0.5rem 0.5rem;\n  background-color: var(--bs-table-bg);\n  border-bottom-width: 1px;\n  box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg);\n}\n.table > tbody {\n  vertical-align: inherit;\n}\n.table > thead {\n  vertical-align: bottom;\n}\n\n.table-group-divider {\n  border-top: 2px solid currentcolor;\n}\n\n.caption-top {\n  caption-side: top;\n}\n\n.table-sm > :not(caption) > * > * {\n  padding: 0.25rem 0.25rem;\n}\n\n.table-bordered > :not(caption) > * {\n  border-width: 1px 0;\n}\n.table-bordered > :not(caption) > * > * {\n  border-width: 0 1px;\n}\n\n.table-borderless > :not(caption) > * > * {\n  border-bottom-width: 0;\n}\n.table-borderless > :not(:first-child) {\n  border-top-width: 0;\n}\n\n.table-striped > tbody > tr:nth-of-type(odd) > * {\n  --bs-table-accent-bg: var(--bs-table-striped-bg);\n  color: var(--bs-table-striped-color);\n}\n\n.table-striped-columns > :not(caption) > tr > :nth-child(even) {\n  --bs-table-accent-bg: var(--bs-table-striped-bg);\n  color: var(--bs-table-striped-color);\n}\n\n.table-active {\n  --bs-table-accent-bg: var(--bs-table-active-bg);\n  color: var(--bs-table-active-color);\n}\n\n.table-hover > tbody > tr:hover > * {\n  --bs-table-accent-bg: var(--bs-table-hover-bg);\n  color: var(--bs-table-hover-color);\n}\n\n.table-primary {\n  --bs-table-color: #000;\n  --bs-table-bg: #cfe2ff;\n  --bs-table-border-color: #bacbe6;\n  --bs-table-striped-bg: #c5d7f2;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #bacbe6;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #bfd1ec;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-secondary {\n  --bs-table-color: #000;\n  --bs-table-bg: #e2e3e5;\n  --bs-table-border-color: #cbccce;\n  --bs-table-striped-bg: #d7d8da;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #cbccce;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #d1d2d4;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-success {\n  --bs-table-color: #000;\n  --bs-table-bg: #d1e7dd;\n  --bs-table-border-color: #bcd0c7;\n  --bs-table-striped-bg: #c7dbd2;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #bcd0c7;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #c1d6cc;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-info {\n  --bs-table-color: #000;\n  --bs-table-bg: #cff4fc;\n  --bs-table-border-color: #badce3;\n  --bs-table-striped-bg: #c5e8ef;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #badce3;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #bfe2e9;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-warning {\n  --bs-table-color: #000;\n  --bs-table-bg: #fff3cd;\n  --bs-table-border-color: #e6dbb9;\n  --bs-table-striped-bg: #f2e7c3;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #e6dbb9;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #ece1be;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-danger {\n  --bs-table-color: #000;\n  --bs-table-bg: #f8d7da;\n  --bs-table-border-color: #dfc2c4;\n  --bs-table-striped-bg: #eccccf;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #dfc2c4;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #e5c7ca;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-light {\n  --bs-table-color: #000;\n  --bs-table-bg: #f8f9fa;\n  --bs-table-border-color: #dfe0e1;\n  --bs-table-striped-bg: #ecedee;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #dfe0e1;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #e5e6e7;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-dark {\n  --bs-table-color: #fff;\n  --bs-table-bg: #212529;\n  --bs-table-border-color: #373b3e;\n  --bs-table-striped-bg: #2c3034;\n  --bs-table-striped-color: #fff;\n  --bs-table-active-bg: #373b3e;\n  --bs-table-active-color: #fff;\n  --bs-table-hover-bg: #323539;\n  --bs-table-hover-color: #fff;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-responsive {\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n}\n\n@media (max-width: 575.98px) {\n  .table-responsive-sm {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 767.98px) {\n  .table-responsive-md {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 991.98px) {\n  .table-responsive-lg {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 1199.98px) {\n  .table-responsive-xl {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 1399.98px) {\n  .table-responsive-xxl {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n.form-label {\n  margin-bottom: 0.5rem;\n}\n\n.col-form-label {\n  padding-top: calc(0.375rem + 1px);\n  padding-bottom: calc(0.375rem + 1px);\n  margin-bottom: 0;\n  font-size: inherit;\n  line-height: 1.5;\n}\n\n.col-form-label-lg {\n  padding-top: calc(0.5rem + 1px);\n  padding-bottom: calc(0.5rem + 1px);\n  font-size: 1.25rem;\n}\n\n.col-form-label-sm {\n  padding-top: calc(0.25rem + 1px);\n  padding-bottom: calc(0.25rem + 1px);\n  font-size: 0.875rem;\n}\n\n.form-text {\n  margin-top: 0.25rem;\n  font-size: 0.875em;\n  color: #6c757d;\n}\n\n.form-control {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid #ced4da;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n  border-radius: 0.375rem;\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-control {\n    transition: none;\n  }\n}\n.form-control[type=file] {\n  overflow: hidden;\n}\n.form-control[type=file]:not(:disabled):not([readonly]) {\n  cursor: pointer;\n}\n.form-control:focus {\n  color: #212529;\n  background-color: #fff;\n  border-color: #86b7fe;\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-control::-webkit-date-and-time-value {\n  height: 1.5em;\n}\n.form-control::-moz-placeholder {\n  color: #6c757d;\n  opacity: 1;\n}\n.form-control::placeholder {\n  color: #6c757d;\n  opacity: 1;\n}\n.form-control:disabled {\n  background-color: #e9ecef;\n  opacity: 1;\n}\n.form-control::-webkit-file-upload-button {\n  padding: 0.375rem 0.75rem;\n  margin: -0.375rem -0.75rem;\n  -webkit-margin-end: 0.75rem;\n  margin-inline-end: 0.75rem;\n  color: #212529;\n  background-color: #e9ecef;\n  pointer-events: none;\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n  border-inline-end-width: 1px;\n  border-radius: 0;\n  -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n.form-control::file-selector-button {\n  padding: 0.375rem 0.75rem;\n  margin: -0.375rem -0.75rem;\n  -webkit-margin-end: 0.75rem;\n  margin-inline-end: 0.75rem;\n  color: #212529;\n  background-color: #e9ecef;\n  pointer-events: none;\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n  border-inline-end-width: 1px;\n  border-radius: 0;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-control::-webkit-file-upload-button {\n    -webkit-transition: none;\n    transition: none;\n  }\n  .form-control::file-selector-button {\n    transition: none;\n  }\n}\n.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {\n  background-color: #dde0e3;\n}\n.form-control:hover:not(:disabled):not([readonly])::file-selector-button {\n  background-color: #dde0e3;\n}\n\n.form-control-plaintext {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0;\n  margin-bottom: 0;\n  line-height: 1.5;\n  color: #212529;\n  background-color: transparent;\n  border: solid transparent;\n  border-width: 1px 0;\n}\n.form-control-plaintext:focus {\n  outline: 0;\n}\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.form-control-sm {\n  min-height: calc(1.5em + 0.5rem + 2px);\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  border-radius: 0.25rem;\n}\n.form-control-sm::-webkit-file-upload-button {\n  padding: 0.25rem 0.5rem;\n  margin: -0.25rem -0.5rem;\n  -webkit-margin-end: 0.5rem;\n  margin-inline-end: 0.5rem;\n}\n.form-control-sm::file-selector-button {\n  padding: 0.25rem 0.5rem;\n  margin: -0.25rem -0.5rem;\n  -webkit-margin-end: 0.5rem;\n  margin-inline-end: 0.5rem;\n}\n\n.form-control-lg {\n  min-height: calc(1.5em + 1rem + 2px);\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  border-radius: 0.5rem;\n}\n.form-control-lg::-webkit-file-upload-button {\n  padding: 0.5rem 1rem;\n  margin: -0.5rem -1rem;\n  -webkit-margin-end: 1rem;\n  margin-inline-end: 1rem;\n}\n.form-control-lg::file-selector-button {\n  padding: 0.5rem 1rem;\n  margin: -0.5rem -1rem;\n  -webkit-margin-end: 1rem;\n  margin-inline-end: 1rem;\n}\n\ntextarea.form-control {\n  min-height: calc(1.5em + 0.75rem + 2px);\n}\ntextarea.form-control-sm {\n  min-height: calc(1.5em + 0.5rem + 2px);\n}\ntextarea.form-control-lg {\n  min-height: calc(1.5em + 1rem + 2px);\n}\n\n.form-control-color {\n  width: 3rem;\n  height: calc(1.5em + 0.75rem + 2px);\n  padding: 0.375rem;\n}\n.form-control-color:not(:disabled):not([readonly]) {\n  cursor: pointer;\n}\n.form-control-color::-moz-color-swatch {\n  border: 0 !important;\n  border-radius: 0.375rem;\n}\n.form-control-color::-webkit-color-swatch {\n  border-radius: 0.375rem;\n}\n.form-control-color.form-control-sm {\n  height: calc(1.5em + 0.5rem + 2px);\n}\n.form-control-color.form-control-lg {\n  height: calc(1.5em + 1rem + 2px);\n}\n\n.form-select {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 2.25rem 0.375rem 0.75rem;\n  -moz-padding-start: calc(0.75rem - 3px);\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  background-color: #fff;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right 0.75rem center;\n  background-size: 16px 12px;\n  border: 1px solid #ced4da;\n  border-radius: 0.375rem;\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-select {\n    transition: none;\n  }\n}\n.form-select:focus {\n  border-color: #86b7fe;\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-select[multiple], .form-select[size]:not([size=\"1\"]) {\n  padding-right: 0.75rem;\n  background-image: none;\n}\n.form-select:disabled {\n  background-color: #e9ecef;\n}\n.form-select:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #212529;\n}\n\n.form-select-sm {\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n  padding-left: 0.5rem;\n  font-size: 0.875rem;\n  border-radius: 0.25rem;\n}\n\n.form-select-lg {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  padding-left: 1rem;\n  font-size: 1.25rem;\n  border-radius: 0.5rem;\n}\n\n.form-check {\n  display: block;\n  min-height: 1.5rem;\n  padding-left: 1.5em;\n  margin-bottom: 0.125rem;\n}\n.form-check .form-check-input {\n  float: left;\n  margin-left: -1.5em;\n}\n\n.form-check-reverse {\n  padding-right: 1.5em;\n  padding-left: 0;\n  text-align: right;\n}\n.form-check-reverse .form-check-input {\n  float: right;\n  margin-right: -1.5em;\n  margin-left: 0;\n}\n\n.form-check-input {\n  width: 1em;\n  height: 1em;\n  margin-top: 0.25em;\n  vertical-align: top;\n  background-color: #fff;\n  background-repeat: no-repeat;\n  background-position: center;\n  background-size: contain;\n  border: 1px solid rgba(0, 0, 0, 0.25);\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n  -webkit-print-color-adjust: exact;\n  color-adjust: exact;\n  print-color-adjust: exact;\n}\n.form-check-input[type=checkbox] {\n  border-radius: 0.25em;\n}\n.form-check-input[type=radio] {\n  border-radius: 50%;\n}\n.form-check-input:active {\n  filter: brightness(90%);\n}\n.form-check-input:focus {\n  border-color: #86b7fe;\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-check-input:checked {\n  background-color: #0d6efd;\n  border-color: #0d6efd;\n}\n.form-check-input:checked[type=checkbox] {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e\");\n}\n.form-check-input:checked[type=radio] {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e\");\n}\n.form-check-input[type=checkbox]:indeterminate {\n  background-color: #0d6efd;\n  border-color: #0d6efd;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e\");\n}\n.form-check-input:disabled {\n  pointer-events: none;\n  filter: none;\n  opacity: 0.5;\n}\n.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label {\n  cursor: default;\n  opacity: 0.5;\n}\n\n.form-switch {\n  padding-left: 2.5em;\n}\n.form-switch .form-check-input {\n  width: 2em;\n  margin-left: -2.5em;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e\");\n  background-position: left center;\n  border-radius: 2em;\n  transition: background-position 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-switch .form-check-input {\n    transition: none;\n  }\n}\n.form-switch .form-check-input:focus {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e\");\n}\n.form-switch .form-check-input:checked {\n  background-position: right center;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e\");\n}\n.form-switch.form-check-reverse {\n  padding-right: 2.5em;\n  padding-left: 0;\n}\n.form-switch.form-check-reverse .form-check-input {\n  margin-right: -2.5em;\n  margin-left: 0;\n}\n\n.form-check-inline {\n  display: inline-block;\n  margin-right: 1rem;\n}\n\n.btn-check {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n.btn-check[disabled] + .btn, .btn-check:disabled + .btn {\n  pointer-events: none;\n  filter: none;\n  opacity: 0.65;\n}\n\n.form-range {\n  width: 100%;\n  height: 1.5rem;\n  padding: 0;\n  background-color: transparent;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n.form-range:focus {\n  outline: 0;\n}\n.form-range:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range::-moz-focus-outer {\n  border: 0;\n}\n.form-range::-webkit-slider-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: -0.25rem;\n  background-color: #0d6efd;\n  border: 0;\n  border-radius: 1rem;\n  -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -webkit-appearance: none;\n  appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-range::-webkit-slider-thumb {\n    -webkit-transition: none;\n    transition: none;\n  }\n}\n.form-range::-webkit-slider-thumb:active {\n  background-color: #b6d4fe;\n}\n.form-range::-webkit-slider-runnable-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n}\n.form-range::-moz-range-thumb {\n  width: 1rem;\n  height: 1rem;\n  background-color: #0d6efd;\n  border: 0;\n  border-radius: 1rem;\n  -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -moz-appearance: none;\n  appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-range::-moz-range-thumb {\n    -moz-transition: none;\n    transition: none;\n  }\n}\n.form-range::-moz-range-thumb:active {\n  background-color: #b6d4fe;\n}\n.form-range::-moz-range-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n}\n.form-range:disabled {\n  pointer-events: none;\n}\n.form-range:disabled::-webkit-slider-thumb {\n  background-color: #adb5bd;\n}\n.form-range:disabled::-moz-range-thumb {\n  background-color: #adb5bd;\n}\n\n.form-floating {\n  position: relative;\n}\n.form-floating > .form-control,\n.form-floating > .form-control-plaintext,\n.form-floating > .form-select {\n  height: calc(3.5rem + 2px);\n  line-height: 1.25;\n}\n.form-floating > label {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  padding: 1rem 0.75rem;\n  overflow: hidden;\n  text-align: start;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  pointer-events: none;\n  border: 1px solid transparent;\n  transform-origin: 0 0;\n  transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-floating > label {\n    transition: none;\n  }\n}\n.form-floating > .form-control,\n.form-floating > .form-control-plaintext {\n  padding: 1rem 0.75rem;\n}\n.form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder {\n  color: transparent;\n}\n.form-floating > .form-control::placeholder,\n.form-floating > .form-control-plaintext::placeholder {\n  color: transparent;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown),\n.form-floating > .form-control-plaintext:focus,\n.form-floating > .form-control-plaintext:not(:placeholder-shown) {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:-webkit-autofill,\n.form-floating > .form-control-plaintext:-webkit-autofill {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-select {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label {\n  opacity: 0.65;\n  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control:focus ~ label,\n.form-floating > .form-control:not(:placeholder-shown) ~ label,\n.form-floating > .form-control-plaintext ~ label,\n.form-floating > .form-select ~ label {\n  opacity: 0.65;\n  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control:-webkit-autofill ~ label {\n  opacity: 0.65;\n  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control-plaintext ~ label {\n  border-width: 1px 0;\n}\n\n.input-group {\n  position: relative;\n  display: flex;\n  flex-wrap: wrap;\n  align-items: stretch;\n  width: 100%;\n}\n.input-group > .form-control,\n.input-group > .form-select,\n.input-group > .form-floating {\n  position: relative;\n  flex: 1 1 auto;\n  width: 1%;\n  min-width: 0;\n}\n.input-group > .form-control:focus,\n.input-group > .form-select:focus,\n.input-group > .form-floating:focus-within {\n  z-index: 5;\n}\n.input-group .btn {\n  position: relative;\n  z-index: 2;\n}\n.input-group .btn:focus {\n  z-index: 5;\n}\n\n.input-group-text {\n  display: flex;\n  align-items: center;\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #e9ecef;\n  border: 1px solid #ced4da;\n  border-radius: 0.375rem;\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .form-select,\n.input-group-lg > .input-group-text,\n.input-group-lg > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  border-radius: 0.5rem;\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .form-select,\n.input-group-sm > .input-group-text,\n.input-group-sm > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  border-radius: 0.25rem;\n}\n\n.input-group-lg > .form-select,\n.input-group-sm > .form-select {\n  padding-right: 3rem;\n}\n\n.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),\n.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3),\n.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control,\n.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),\n.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4),\n.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-control,\n.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-select {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {\n  margin-left: -1px;\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.input-group > .form-floating:not(:first-child) > .form-control,\n.input-group > .form-floating:not(:first-child) > .form-select {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.valid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 0.875em;\n  color: #198754;\n}\n\n.valid-tooltip {\n  position: absolute;\n  top: 100%;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: 0.1rem;\n  font-size: 0.875rem;\n  color: #fff;\n  background-color: rgba(25, 135, 84, 0.9);\n  border-radius: 0.375rem;\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n  border-color: #198754;\n  padding-right: calc(1.5em + 0.75rem);\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n  border-color: #198754;\n  box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n  padding-right: calc(1.5em + 0.75rem);\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:valid, .form-select.is-valid {\n  border-color: #198754;\n}\n.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size=\"1\"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size=\"1\"] {\n  padding-right: 4.125rem;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n  background-position: right 0.75rem center, center right 2.25rem;\n  background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:valid:focus, .form-select.is-valid:focus {\n  border-color: #198754;\n  box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n\n.was-validated .form-control-color:valid, .form-control-color.is-valid {\n  width: calc(3rem + calc(1.5em + 0.75rem));\n}\n\n.was-validated .form-check-input:valid, .form-check-input.is-valid {\n  border-color: #198754;\n}\n.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked {\n  background-color: #198754;\n}\n.was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus {\n  box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n  color: #198754;\n}\n\n.form-check-inline .form-check-input ~ .valid-feedback {\n  margin-left: 0.5em;\n}\n\n.was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid,\n.was-validated .input-group > .form-select:not(:focus):valid,\n.input-group > .form-select:not(:focus).is-valid,\n.was-validated .input-group > .form-floating:not(:focus-within):valid,\n.input-group > .form-floating:not(:focus-within).is-valid {\n  z-index: 3;\n}\n\n.invalid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 0.875em;\n  color: #dc3545;\n}\n\n.invalid-tooltip {\n  position: absolute;\n  top: 100%;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: 0.1rem;\n  font-size: 0.875rem;\n  color: #fff;\n  background-color: rgba(220, 53, 69, 0.9);\n  border-radius: 0.375rem;\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n  border-color: #dc3545;\n  padding-right: calc(1.5em + 0.75rem);\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: right calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n  padding-right: calc(1.5em + 0.75rem);\n  background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:invalid, .form-select.is-invalid {\n  border-color: #dc3545;\n}\n.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size=\"1\"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size=\"1\"] {\n  padding-right: 4.125rem;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n  background-position: right 0.75rem center, center right 2.25rem;\n  background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control-color:invalid, .form-control-color.is-invalid {\n  width: calc(3rem + calc(1.5em + 0.75rem));\n}\n\n.was-validated .form-check-input:invalid, .form-check-input.is-invalid {\n  border-color: #dc3545;\n}\n.was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked {\n  background-color: #dc3545;\n}\n.was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus {\n  box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n  color: #dc3545;\n}\n\n.form-check-inline .form-check-input ~ .invalid-feedback {\n  margin-left: 0.5em;\n}\n\n.was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid,\n.was-validated .input-group > .form-select:not(:focus):invalid,\n.input-group > .form-select:not(:focus).is-invalid,\n.was-validated .input-group > .form-floating:not(:focus-within):invalid,\n.input-group > .form-floating:not(:focus-within).is-invalid {\n  z-index: 4;\n}\n\n.btn {\n  --bs-btn-padding-x: 0.75rem;\n  --bs-btn-padding-y: 0.375rem;\n  --bs-btn-font-family: ;\n  --bs-btn-font-size: 1rem;\n  --bs-btn-font-weight: 400;\n  --bs-btn-line-height: 1.5;\n  --bs-btn-color: #212529;\n  --bs-btn-bg: transparent;\n  --bs-btn-border-width: 1px;\n  --bs-btn-border-color: transparent;\n  --bs-btn-border-radius: 0.375rem;\n  --bs-btn-hover-border-color: transparent;\n  --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n  --bs-btn-disabled-opacity: 0.65;\n  --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);\n  display: inline-block;\n  padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);\n  font-family: var(--bs-btn-font-family);\n  font-size: var(--bs-btn-font-size);\n  font-weight: var(--bs-btn-font-weight);\n  line-height: var(--bs-btn-line-height);\n  color: var(--bs-btn-color);\n  text-align: center;\n  text-decoration: none;\n  vertical-align: middle;\n  cursor: pointer;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n  border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);\n  border-radius: var(--bs-btn-border-radius);\n  background-color: var(--bs-btn-bg);\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .btn {\n    transition: none;\n  }\n}\n.btn:hover {\n  color: var(--bs-btn-hover-color);\n  background-color: var(--bs-btn-hover-bg);\n  border-color: var(--bs-btn-hover-border-color);\n}\n.btn-check + .btn:hover {\n  color: var(--bs-btn-color);\n  background-color: var(--bs-btn-bg);\n  border-color: var(--bs-btn-border-color);\n}\n.btn:focus-visible {\n  color: var(--bs-btn-hover-color);\n  background-color: var(--bs-btn-hover-bg);\n  border-color: var(--bs-btn-hover-border-color);\n  outline: 0;\n  box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn-check:focus-visible + .btn {\n  border-color: var(--bs-btn-hover-border-color);\n  outline: 0;\n  box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show {\n  color: var(--bs-btn-active-color);\n  background-color: var(--bs-btn-active-bg);\n  border-color: var(--bs-btn-active-border-color);\n}\n.btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible {\n  box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn:disabled, .btn.disabled, fieldset:disabled .btn {\n  color: var(--bs-btn-disabled-color);\n  pointer-events: none;\n  background-color: var(--bs-btn-disabled-bg);\n  border-color: var(--bs-btn-disabled-border-color);\n  opacity: var(--bs-btn-disabled-opacity);\n}\n\n.btn-primary {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #0d6efd;\n  --bs-btn-border-color: #0d6efd;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #0b5ed7;\n  --bs-btn-hover-border-color: #0a58ca;\n  --bs-btn-focus-shadow-rgb: 49, 132, 253;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #0a58ca;\n  --bs-btn-active-border-color: #0a53be;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #0d6efd;\n  --bs-btn-disabled-border-color: #0d6efd;\n}\n\n.btn-secondary {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #6c757d;\n  --bs-btn-border-color: #6c757d;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #5c636a;\n  --bs-btn-hover-border-color: #565e64;\n  --bs-btn-focus-shadow-rgb: 130, 138, 145;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #565e64;\n  --bs-btn-active-border-color: #51585e;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #6c757d;\n  --bs-btn-disabled-border-color: #6c757d;\n}\n\n.btn-success {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #198754;\n  --bs-btn-border-color: #198754;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #157347;\n  --bs-btn-hover-border-color: #146c43;\n  --bs-btn-focus-shadow-rgb: 60, 153, 110;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #146c43;\n  --bs-btn-active-border-color: #13653f;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #198754;\n  --bs-btn-disabled-border-color: #198754;\n}\n\n.btn-info {\n  --bs-btn-color: #000;\n  --bs-btn-bg: #0dcaf0;\n  --bs-btn-border-color: #0dcaf0;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #31d2f2;\n  --bs-btn-hover-border-color: #25cff2;\n  --bs-btn-focus-shadow-rgb: 11, 172, 204;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #3dd5f3;\n  --bs-btn-active-border-color: #25cff2;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #000;\n  --bs-btn-disabled-bg: #0dcaf0;\n  --bs-btn-disabled-border-color: #0dcaf0;\n}\n\n.btn-warning {\n  --bs-btn-color: #000;\n  --bs-btn-bg: #ffc107;\n  --bs-btn-border-color: #ffc107;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #ffca2c;\n  --bs-btn-hover-border-color: #ffc720;\n  --bs-btn-focus-shadow-rgb: 217, 164, 6;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #ffcd39;\n  --bs-btn-active-border-color: #ffc720;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #000;\n  --bs-btn-disabled-bg: #ffc107;\n  --bs-btn-disabled-border-color: #ffc107;\n}\n\n.btn-danger {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #dc3545;\n  --bs-btn-border-color: #dc3545;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #bb2d3b;\n  --bs-btn-hover-border-color: #b02a37;\n  --bs-btn-focus-shadow-rgb: 225, 83, 97;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #b02a37;\n  --bs-btn-active-border-color: #a52834;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #dc3545;\n  --bs-btn-disabled-border-color: #dc3545;\n}\n\n.btn-light {\n  --bs-btn-color: #000;\n  --bs-btn-bg: #f8f9fa;\n  --bs-btn-border-color: #f8f9fa;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #d3d4d5;\n  --bs-btn-hover-border-color: #c6c7c8;\n  --bs-btn-focus-shadow-rgb: 211, 212, 213;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #c6c7c8;\n  --bs-btn-active-border-color: #babbbc;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #000;\n  --bs-btn-disabled-bg: #f8f9fa;\n  --bs-btn-disabled-border-color: #f8f9fa;\n}\n\n.btn-dark {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #212529;\n  --bs-btn-border-color: #212529;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #424649;\n  --bs-btn-hover-border-color: #373b3e;\n  --bs-btn-focus-shadow-rgb: 66, 70, 73;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #4d5154;\n  --bs-btn-active-border-color: #373b3e;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #212529;\n  --bs-btn-disabled-border-color: #212529;\n}\n\n.btn-outline-primary {\n  --bs-btn-color: #0d6efd;\n  --bs-btn-border-color: #0d6efd;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #0d6efd;\n  --bs-btn-hover-border-color: #0d6efd;\n  --bs-btn-focus-shadow-rgb: 13, 110, 253;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #0d6efd;\n  --bs-btn-active-border-color: #0d6efd;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #0d6efd;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #0d6efd;\n  --bs-gradient: none;\n}\n\n.btn-outline-secondary {\n  --bs-btn-color: #6c757d;\n  --bs-btn-border-color: #6c757d;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #6c757d;\n  --bs-btn-hover-border-color: #6c757d;\n  --bs-btn-focus-shadow-rgb: 108, 117, 125;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #6c757d;\n  --bs-btn-active-border-color: #6c757d;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #6c757d;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #6c757d;\n  --bs-gradient: none;\n}\n\n.btn-outline-success {\n  --bs-btn-color: #198754;\n  --bs-btn-border-color: #198754;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #198754;\n  --bs-btn-hover-border-color: #198754;\n  --bs-btn-focus-shadow-rgb: 25, 135, 84;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #198754;\n  --bs-btn-active-border-color: #198754;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #198754;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #198754;\n  --bs-gradient: none;\n}\n\n.btn-outline-info {\n  --bs-btn-color: #0dcaf0;\n  --bs-btn-border-color: #0dcaf0;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #0dcaf0;\n  --bs-btn-hover-border-color: #0dcaf0;\n  --bs-btn-focus-shadow-rgb: 13, 202, 240;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #0dcaf0;\n  --bs-btn-active-border-color: #0dcaf0;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #0dcaf0;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #0dcaf0;\n  --bs-gradient: none;\n}\n\n.btn-outline-warning {\n  --bs-btn-color: #ffc107;\n  --bs-btn-border-color: #ffc107;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #ffc107;\n  --bs-btn-hover-border-color: #ffc107;\n  --bs-btn-focus-shadow-rgb: 255, 193, 7;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #ffc107;\n  --bs-btn-active-border-color: #ffc107;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #ffc107;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #ffc107;\n  --bs-gradient: none;\n}\n\n.btn-outline-danger {\n  --bs-btn-color: #dc3545;\n  --bs-btn-border-color: #dc3545;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #dc3545;\n  --bs-btn-hover-border-color: #dc3545;\n  --bs-btn-focus-shadow-rgb: 220, 53, 69;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #dc3545;\n  --bs-btn-active-border-color: #dc3545;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #dc3545;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #dc3545;\n  --bs-gradient: none;\n}\n\n.btn-outline-light {\n  --bs-btn-color: #f8f9fa;\n  --bs-btn-border-color: #f8f9fa;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #f8f9fa;\n  --bs-btn-hover-border-color: #f8f9fa;\n  --bs-btn-focus-shadow-rgb: 248, 249, 250;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #f8f9fa;\n  --bs-btn-active-border-color: #f8f9fa;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #f8f9fa;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #f8f9fa;\n  --bs-gradient: none;\n}\n\n.btn-outline-dark {\n  --bs-btn-color: #212529;\n  --bs-btn-border-color: #212529;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #212529;\n  --bs-btn-hover-border-color: #212529;\n  --bs-btn-focus-shadow-rgb: 33, 37, 41;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #212529;\n  --bs-btn-active-border-color: #212529;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #212529;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #212529;\n  --bs-gradient: none;\n}\n\n.btn-link {\n  --bs-btn-font-weight: 400;\n  --bs-btn-color: var(--bs-link-color);\n  --bs-btn-bg: transparent;\n  --bs-btn-border-color: transparent;\n  --bs-btn-hover-color: var(--bs-link-hover-color);\n  --bs-btn-hover-border-color: transparent;\n  --bs-btn-active-color: var(--bs-link-hover-color);\n  --bs-btn-active-border-color: transparent;\n  --bs-btn-disabled-color: #6c757d;\n  --bs-btn-disabled-border-color: transparent;\n  --bs-btn-box-shadow: none;\n  --bs-btn-focus-shadow-rgb: 49, 132, 253;\n  text-decoration: underline;\n}\n.btn-link:focus-visible {\n  color: var(--bs-btn-color);\n}\n.btn-link:hover {\n  color: var(--bs-btn-hover-color);\n}\n\n.btn-lg, .btn-group-lg > .btn {\n  --bs-btn-padding-y: 0.5rem;\n  --bs-btn-padding-x: 1rem;\n  --bs-btn-font-size: 1.25rem;\n  --bs-btn-border-radius: 0.5rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n  --bs-btn-padding-y: 0.25rem;\n  --bs-btn-padding-x: 0.5rem;\n  --bs-btn-font-size: 0.875rem;\n  --bs-btn-border-radius: 0.25rem;\n}\n\n.fade {\n  transition: opacity 0.15s linear;\n}\n@media (prefers-reduced-motion: reduce) {\n  .fade {\n    transition: none;\n  }\n}\n.fade:not(.show) {\n  opacity: 0;\n}\n\n.collapse:not(.show) {\n  display: none;\n}\n\n.collapsing {\n  height: 0;\n  overflow: hidden;\n  transition: height 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .collapsing {\n    transition: none;\n  }\n}\n.collapsing.collapse-horizontal {\n  width: 0;\n  height: auto;\n  transition: width 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .collapsing.collapse-horizontal {\n    transition: none;\n  }\n}\n\n.dropup,\n.dropend,\n.dropdown,\n.dropstart,\n.dropup-center,\n.dropdown-center {\n  position: relative;\n}\n\n.dropdown-toggle {\n  white-space: nowrap;\n}\n.dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0;\n  border-left: 0.3em solid transparent;\n}\n.dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropdown-menu {\n  --bs-dropdown-zindex: 1000;\n  --bs-dropdown-min-width: 10rem;\n  --bs-dropdown-padding-x: 0;\n  --bs-dropdown-padding-y: 0.5rem;\n  --bs-dropdown-spacer: 0.125rem;\n  --bs-dropdown-font-size: 1rem;\n  --bs-dropdown-color: #212529;\n  --bs-dropdown-bg: #fff;\n  --bs-dropdown-border-color: var(--bs-border-color-translucent);\n  --bs-dropdown-border-radius: 0.375rem;\n  --bs-dropdown-border-width: 1px;\n  --bs-dropdown-inner-border-radius: calc(0.375rem - 1px);\n  --bs-dropdown-divider-bg: var(--bs-border-color-translucent);\n  --bs-dropdown-divider-margin-y: 0.5rem;\n  --bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  --bs-dropdown-link-color: #212529;\n  --bs-dropdown-link-hover-color: #1e2125;\n  --bs-dropdown-link-hover-bg: #e9ecef;\n  --bs-dropdown-link-active-color: #fff;\n  --bs-dropdown-link-active-bg: #0d6efd;\n  --bs-dropdown-link-disabled-color: #adb5bd;\n  --bs-dropdown-item-padding-x: 1rem;\n  --bs-dropdown-item-padding-y: 0.25rem;\n  --bs-dropdown-header-color: #6c757d;\n  --bs-dropdown-header-padding-x: 1rem;\n  --bs-dropdown-header-padding-y: 0.5rem;\n  position: absolute;\n  z-index: var(--bs-dropdown-zindex);\n  display: none;\n  min-width: var(--bs-dropdown-min-width);\n  padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);\n  margin: 0;\n  font-size: var(--bs-dropdown-font-size);\n  color: var(--bs-dropdown-color);\n  text-align: left;\n  list-style: none;\n  background-color: var(--bs-dropdown-bg);\n  background-clip: padding-box;\n  border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);\n  border-radius: var(--bs-dropdown-border-radius);\n}\n.dropdown-menu[data-bs-popper] {\n  top: 100%;\n  left: 0;\n  margin-top: var(--bs-dropdown-spacer);\n}\n\n.dropdown-menu-start {\n  --bs-position: start;\n}\n.dropdown-menu-start[data-bs-popper] {\n  right: auto;\n  left: 0;\n}\n\n.dropdown-menu-end {\n  --bs-position: end;\n}\n.dropdown-menu-end[data-bs-popper] {\n  right: 0;\n  left: auto;\n}\n\n@media (min-width: 576px) {\n  .dropdown-menu-sm-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-sm-start[data-bs-popper] {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-sm-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-sm-end[data-bs-popper] {\n    right: 0;\n    left: auto;\n  }\n}\n@media (min-width: 768px) {\n  .dropdown-menu-md-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-md-start[data-bs-popper] {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-md-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-md-end[data-bs-popper] {\n    right: 0;\n    left: auto;\n  }\n}\n@media (min-width: 992px) {\n  .dropdown-menu-lg-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-lg-start[data-bs-popper] {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-lg-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-lg-end[data-bs-popper] {\n    right: 0;\n    left: auto;\n  }\n}\n@media (min-width: 1200px) {\n  .dropdown-menu-xl-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-xl-start[data-bs-popper] {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-xl-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-xl-end[data-bs-popper] {\n    right: 0;\n    left: auto;\n  }\n}\n@media (min-width: 1400px) {\n  .dropdown-menu-xxl-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-xxl-start[data-bs-popper] {\n    right: auto;\n    left: 0;\n  }\n  .dropdown-menu-xxl-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-xxl-end[data-bs-popper] {\n    right: 0;\n    left: auto;\n  }\n}\n.dropup .dropdown-menu[data-bs-popper] {\n  top: auto;\n  bottom: 100%;\n  margin-top: 0;\n  margin-bottom: var(--bs-dropdown-spacer);\n}\n.dropup .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0;\n  border-right: 0.3em solid transparent;\n  border-bottom: 0.3em solid;\n  border-left: 0.3em solid transparent;\n}\n.dropup .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n\n.dropend .dropdown-menu[data-bs-popper] {\n  top: 0;\n  right: auto;\n  left: 100%;\n  margin-top: 0;\n  margin-left: var(--bs-dropdown-spacer);\n}\n.dropend .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0;\n  border-bottom: 0.3em solid transparent;\n  border-left: 0.3em solid;\n}\n.dropend .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n.dropend .dropdown-toggle::after {\n  vertical-align: 0;\n}\n\n.dropstart .dropdown-menu[data-bs-popper] {\n  top: 0;\n  right: 100%;\n  left: auto;\n  margin-top: 0;\n  margin-right: var(--bs-dropdown-spacer);\n}\n.dropstart .dropdown-toggle::after {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n}\n.dropstart .dropdown-toggle::after {\n  display: none;\n}\n.dropstart .dropdown-toggle::before {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-right: 0.3em solid;\n  border-bottom: 0.3em solid transparent;\n}\n.dropstart .dropdown-toggle:empty::after {\n  margin-left: 0;\n}\n.dropstart .dropdown-toggle::before {\n  vertical-align: 0;\n}\n\n.dropdown-divider {\n  height: 0;\n  margin: var(--bs-dropdown-divider-margin-y) 0;\n  overflow: hidden;\n  border-top: 1px solid var(--bs-dropdown-divider-bg);\n  opacity: 1;\n}\n\n.dropdown-item {\n  display: block;\n  width: 100%;\n  padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);\n  clear: both;\n  font-weight: 400;\n  color: var(--bs-dropdown-link-color);\n  text-align: inherit;\n  text-decoration: none;\n  white-space: nowrap;\n  background-color: transparent;\n  border: 0;\n}\n.dropdown-item:hover, .dropdown-item:focus {\n  color: var(--bs-dropdown-link-hover-color);\n  background-color: var(--bs-dropdown-link-hover-bg);\n}\n.dropdown-item.active, .dropdown-item:active {\n  color: var(--bs-dropdown-link-active-color);\n  text-decoration: none;\n  background-color: var(--bs-dropdown-link-active-bg);\n}\n.dropdown-item.disabled, .dropdown-item:disabled {\n  color: var(--bs-dropdown-link-disabled-color);\n  pointer-events: none;\n  background-color: transparent;\n}\n\n.dropdown-menu.show {\n  display: block;\n}\n\n.dropdown-header {\n  display: block;\n  padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);\n  margin-bottom: 0;\n  font-size: 0.875rem;\n  color: var(--bs-dropdown-header-color);\n  white-space: nowrap;\n}\n\n.dropdown-item-text {\n  display: block;\n  padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);\n  color: var(--bs-dropdown-link-color);\n}\n\n.dropdown-menu-dark {\n  --bs-dropdown-color: #dee2e6;\n  --bs-dropdown-bg: #343a40;\n  --bs-dropdown-border-color: var(--bs-border-color-translucent);\n  --bs-dropdown-box-shadow: ;\n  --bs-dropdown-link-color: #dee2e6;\n  --bs-dropdown-link-hover-color: #fff;\n  --bs-dropdown-divider-bg: var(--bs-border-color-translucent);\n  --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);\n  --bs-dropdown-link-active-color: #fff;\n  --bs-dropdown-link-active-bg: #0d6efd;\n  --bs-dropdown-link-disabled-color: #adb5bd;\n  --bs-dropdown-header-color: #adb5bd;\n}\n\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-flex;\n  vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  flex: 1 1 auto;\n}\n.btn-group > .btn-check:checked + .btn,\n.btn-group > .btn-check:focus + .btn,\n.btn-group > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn-check:checked + .btn,\n.btn-group-vertical > .btn-check:focus + .btn,\n.btn-group-vertical > .btn:hover,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n  z-index: 1;\n}\n\n.btn-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: flex-start;\n}\n.btn-toolbar .input-group {\n  width: auto;\n}\n\n.btn-group {\n  border-radius: 0.375rem;\n}\n.btn-group > :not(.btn-check:first-child) + .btn,\n.btn-group > .btn-group:not(:first-child) {\n  margin-left: -1px;\n}\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn.dropdown-toggle-split:first-child,\n.btn-group > .btn-group:not(:last-child) > .btn {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.btn-group > .btn:nth-child(n+3),\n.btn-group > :not(.btn-check) + .btn,\n.btn-group > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n  padding-right: 0.5625rem;\n  padding-left: 0.5625rem;\n}\n.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after {\n  margin-left: 0;\n}\n.dropstart .dropdown-toggle-split::before {\n  margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n  padding-right: 0.375rem;\n  padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n  padding-right: 0.75rem;\n  padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n  flex-direction: column;\n  align-items: flex-start;\n  justify-content: center;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n  width: 100%;\n}\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n  margin-top: -1px;\n}\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn ~ .btn,\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.nav {\n  --bs-nav-link-padding-x: 1rem;\n  --bs-nav-link-padding-y: 0.5rem;\n  --bs-nav-link-font-weight: ;\n  --bs-nav-link-color: var(--bs-link-color);\n  --bs-nav-link-hover-color: var(--bs-link-hover-color);\n  --bs-nav-link-disabled-color: #6c757d;\n  display: flex;\n  flex-wrap: wrap;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.nav-link {\n  display: block;\n  padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);\n  font-size: var(--bs-nav-link-font-size);\n  font-weight: var(--bs-nav-link-font-weight);\n  color: var(--bs-nav-link-color);\n  text-decoration: none;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .nav-link {\n    transition: none;\n  }\n}\n.nav-link:hover, .nav-link:focus {\n  color: var(--bs-nav-link-hover-color);\n}\n.nav-link.disabled {\n  color: var(--bs-nav-link-disabled-color);\n  pointer-events: none;\n  cursor: default;\n}\n\n.nav-tabs {\n  --bs-nav-tabs-border-width: 1px;\n  --bs-nav-tabs-border-color: #dee2e6;\n  --bs-nav-tabs-border-radius: 0.375rem;\n  --bs-nav-tabs-link-hover-border-color: #e9ecef #e9ecef #dee2e6;\n  --bs-nav-tabs-link-active-color: #495057;\n  --bs-nav-tabs-link-active-bg: #fff;\n  --bs-nav-tabs-link-active-border-color: #dee2e6 #dee2e6 #fff;\n  border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color);\n}\n.nav-tabs .nav-link {\n  margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width));\n  background: none;\n  border: var(--bs-nav-tabs-border-width) solid transparent;\n  border-top-left-radius: var(--bs-nav-tabs-border-radius);\n  border-top-right-radius: var(--bs-nav-tabs-border-radius);\n}\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n  isolation: isolate;\n  border-color: var(--bs-nav-tabs-link-hover-border-color);\n}\n.nav-tabs .nav-link.disabled, .nav-tabs .nav-link:disabled {\n  color: var(--bs-nav-link-disabled-color);\n  background-color: transparent;\n  border-color: transparent;\n}\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n  color: var(--bs-nav-tabs-link-active-color);\n  background-color: var(--bs-nav-tabs-link-active-bg);\n  border-color: var(--bs-nav-tabs-link-active-border-color);\n}\n.nav-tabs .dropdown-menu {\n  margin-top: calc(-1 * var(--bs-nav-tabs-border-width));\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n.nav-pills {\n  --bs-nav-pills-border-radius: 0.375rem;\n  --bs-nav-pills-link-active-color: #fff;\n  --bs-nav-pills-link-active-bg: #0d6efd;\n}\n.nav-pills .nav-link {\n  background: none;\n  border: 0;\n  border-radius: var(--bs-nav-pills-border-radius);\n}\n.nav-pills .nav-link:disabled {\n  color: var(--bs-nav-link-disabled-color);\n  background-color: transparent;\n  border-color: transparent;\n}\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n  color: var(--bs-nav-pills-link-active-color);\n  background-color: var(--bs-nav-pills-link-active-bg);\n}\n\n.nav-fill > .nav-link,\n.nav-fill .nav-item {\n  flex: 1 1 auto;\n  text-align: center;\n}\n\n.nav-justified > .nav-link,\n.nav-justified .nav-item {\n  flex-basis: 0;\n  flex-grow: 1;\n  text-align: center;\n}\n\n.nav-fill .nav-item .nav-link,\n.nav-justified .nav-item .nav-link {\n  width: 100%;\n}\n\n.tab-content > .tab-pane {\n  display: none;\n}\n.tab-content > .active {\n  display: block;\n}\n\n.navbar {\n  --bs-navbar-padding-x: 0;\n  --bs-navbar-padding-y: 0.5rem;\n  --bs-navbar-color: rgba(0, 0, 0, 0.55);\n  --bs-navbar-hover-color: rgba(0, 0, 0, 0.7);\n  --bs-navbar-disabled-color: rgba(0, 0, 0, 0.3);\n  --bs-navbar-active-color: rgba(0, 0, 0, 0.9);\n  --bs-navbar-brand-padding-y: 0.3125rem;\n  --bs-navbar-brand-margin-end: 1rem;\n  --bs-navbar-brand-font-size: 1.25rem;\n  --bs-navbar-brand-color: rgba(0, 0, 0, 0.9);\n  --bs-navbar-brand-hover-color: rgba(0, 0, 0, 0.9);\n  --bs-navbar-nav-link-padding-x: 0.5rem;\n  --bs-navbar-toggler-padding-y: 0.25rem;\n  --bs-navbar-toggler-padding-x: 0.75rem;\n  --bs-navbar-toggler-font-size: 1.25rem;\n  --bs-navbar-toggler-icon-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n  --bs-navbar-toggler-border-color: rgba(0, 0, 0, 0.1);\n  --bs-navbar-toggler-border-radius: 0.375rem;\n  --bs-navbar-toggler-focus-width: 0.25rem;\n  --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;\n  position: relative;\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  justify-content: space-between;\n  padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x);\n}\n.navbar > .container,\n.navbar > .container-fluid,\n.navbar > .container-sm,\n.navbar > .container-md,\n.navbar > .container-lg,\n.navbar > .container-xl,\n.navbar > .container-xxl {\n  display: flex;\n  flex-wrap: inherit;\n  align-items: center;\n  justify-content: space-between;\n}\n.navbar-brand {\n  padding-top: var(--bs-navbar-brand-padding-y);\n  padding-bottom: var(--bs-navbar-brand-padding-y);\n  margin-right: var(--bs-navbar-brand-margin-end);\n  font-size: var(--bs-navbar-brand-font-size);\n  color: var(--bs-navbar-brand-color);\n  text-decoration: none;\n  white-space: nowrap;\n}\n.navbar-brand:hover, .navbar-brand:focus {\n  color: var(--bs-navbar-brand-hover-color);\n}\n\n.navbar-nav {\n  --bs-nav-link-padding-x: 0;\n  --bs-nav-link-padding-y: 0.5rem;\n  --bs-nav-link-font-weight: ;\n  --bs-nav-link-color: var(--bs-navbar-color);\n  --bs-nav-link-hover-color: var(--bs-navbar-hover-color);\n  --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);\n  display: flex;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n.navbar-nav .show > .nav-link,\n.navbar-nav .nav-link.active {\n  color: var(--bs-navbar-active-color);\n}\n.navbar-nav .dropdown-menu {\n  position: static;\n}\n\n.navbar-text {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  color: var(--bs-navbar-color);\n}\n.navbar-text a,\n.navbar-text a:hover,\n.navbar-text a:focus {\n  color: var(--bs-navbar-active-color);\n}\n\n.navbar-collapse {\n  flex-basis: 100%;\n  flex-grow: 1;\n  align-items: center;\n}\n\n.navbar-toggler {\n  padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);\n  font-size: var(--bs-navbar-toggler-font-size);\n  line-height: 1;\n  color: var(--bs-navbar-color);\n  background-color: transparent;\n  border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);\n  border-radius: var(--bs-navbar-toggler-border-radius);\n  transition: var(--bs-navbar-toggler-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .navbar-toggler {\n    transition: none;\n  }\n}\n.navbar-toggler:hover {\n  text-decoration: none;\n}\n.navbar-toggler:focus {\n  text-decoration: none;\n  outline: 0;\n  box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width);\n}\n\n.navbar-toggler-icon {\n  display: inline-block;\n  width: 1.5em;\n  height: 1.5em;\n  vertical-align: middle;\n  background-image: var(--bs-navbar-toggler-icon-bg);\n  background-repeat: no-repeat;\n  background-position: center;\n  background-size: 100%;\n}\n\n.navbar-nav-scroll {\n  max-height: var(--bs-scroll-height, 75vh);\n  overflow-y: auto;\n}\n\n@media (min-width: 576px) {\n  .navbar-expand-sm {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-sm .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-sm .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-sm .navbar-nav .nav-link {\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-sm .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-sm .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-sm .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-sm .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-sm .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-sm .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-expand-md {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-md .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-md .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-md .navbar-nav .nav-link {\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-md .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-md .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-md .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-md .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-md .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-md .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 992px) {\n  .navbar-expand-lg {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-lg .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-lg .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-lg .navbar-nav .nav-link {\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-lg .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-lg .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-lg .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-lg .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-lg .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-lg .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 1200px) {\n  .navbar-expand-xl {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-xl .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-xl .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-xl .navbar-nav .nav-link {\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-xl .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-xl .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-xl .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-xl .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-xl .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-xl .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 1400px) {\n  .navbar-expand-xxl {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-xxl .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-xxl .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-xxl .navbar-nav .nav-link {\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-xxl .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-xxl .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-xxl .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-xxl .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-xxl .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-xxl .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n.navbar-expand {\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n}\n.navbar-expand .navbar-nav {\n  flex-direction: row;\n}\n.navbar-expand .navbar-nav .dropdown-menu {\n  position: absolute;\n}\n.navbar-expand .navbar-nav .nav-link {\n  padding-right: var(--bs-navbar-nav-link-padding-x);\n  padding-left: var(--bs-navbar-nav-link-padding-x);\n}\n.navbar-expand .navbar-nav-scroll {\n  overflow: visible;\n}\n.navbar-expand .navbar-collapse {\n  display: flex !important;\n  flex-basis: auto;\n}\n.navbar-expand .navbar-toggler {\n  display: none;\n}\n.navbar-expand .offcanvas {\n  position: static;\n  z-index: auto;\n  flex-grow: 1;\n  width: auto !important;\n  height: auto !important;\n  visibility: visible !important;\n  background-color: transparent !important;\n  border: 0 !important;\n  transform: none !important;\n  transition: none;\n}\n.navbar-expand .offcanvas .offcanvas-header {\n  display: none;\n}\n.navbar-expand .offcanvas .offcanvas-body {\n  display: flex;\n  flex-grow: 0;\n  padding: 0;\n  overflow-y: visible;\n}\n\n.navbar-dark {\n  --bs-navbar-color: rgba(255, 255, 255, 0.55);\n  --bs-navbar-hover-color: rgba(255, 255, 255, 0.75);\n  --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);\n  --bs-navbar-active-color: #fff;\n  --bs-navbar-brand-color: #fff;\n  --bs-navbar-brand-hover-color: #fff;\n  --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1);\n  --bs-navbar-toggler-icon-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.card {\n  --bs-card-spacer-y: 1rem;\n  --bs-card-spacer-x: 1rem;\n  --bs-card-title-spacer-y: 0.5rem;\n  --bs-card-border-width: 1px;\n  --bs-card-border-color: var(--bs-border-color-translucent);\n  --bs-card-border-radius: 0.375rem;\n  --bs-card-box-shadow: ;\n  --bs-card-inner-border-radius: calc(0.375rem - 1px);\n  --bs-card-cap-padding-y: 0.5rem;\n  --bs-card-cap-padding-x: 1rem;\n  --bs-card-cap-bg: rgba(0, 0, 0, 0.03);\n  --bs-card-cap-color: ;\n  --bs-card-height: ;\n  --bs-card-color: ;\n  --bs-card-bg: #fff;\n  --bs-card-img-overlay-padding: 1rem;\n  --bs-card-group-margin: 0.75rem;\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  min-width: 0;\n  height: var(--bs-card-height);\n  word-wrap: break-word;\n  background-color: var(--bs-card-bg);\n  background-clip: border-box;\n  border: var(--bs-card-border-width) solid var(--bs-card-border-color);\n  border-radius: var(--bs-card-border-radius);\n}\n.card > hr {\n  margin-right: 0;\n  margin-left: 0;\n}\n.card > .list-group {\n  border-top: inherit;\n  border-bottom: inherit;\n}\n.card > .list-group:first-child {\n  border-top-width: 0;\n  border-top-left-radius: var(--bs-card-inner-border-radius);\n  border-top-right-radius: var(--bs-card-inner-border-radius);\n}\n.card > .list-group:last-child {\n  border-bottom-width: 0;\n  border-bottom-right-radius: var(--bs-card-inner-border-radius);\n  border-bottom-left-radius: var(--bs-card-inner-border-radius);\n}\n.card > .card-header + .list-group,\n.card > .list-group + .card-footer {\n  border-top: 0;\n}\n\n.card-body {\n  flex: 1 1 auto;\n  padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x);\n  color: var(--bs-card-color);\n}\n\n.card-title {\n  margin-bottom: var(--bs-card-title-spacer-y);\n}\n\n.card-subtitle {\n  margin-top: calc(-0.5 * var(--bs-card-title-spacer-y));\n  margin-bottom: 0;\n}\n\n.card-text:last-child {\n  margin-bottom: 0;\n}\n\n.card-link + .card-link {\n  margin-left: var(--bs-card-spacer-x);\n}\n\n.card-header {\n  padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);\n  margin-bottom: 0;\n  color: var(--bs-card-cap-color);\n  background-color: var(--bs-card-cap-bg);\n  border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color);\n}\n.card-header:first-child {\n  border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0;\n}\n\n.card-footer {\n  padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);\n  color: var(--bs-card-cap-color);\n  background-color: var(--bs-card-cap-bg);\n  border-top: var(--bs-card-border-width) solid var(--bs-card-border-color);\n}\n.card-footer:last-child {\n  border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius);\n}\n\n.card-header-tabs {\n  margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));\n  margin-bottom: calc(-1 * var(--bs-card-cap-padding-y));\n  margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));\n  border-bottom: 0;\n}\n.card-header-tabs .nav-link.active {\n  background-color: var(--bs-card-bg);\n  border-bottom-color: var(--bs-card-bg);\n}\n\n.card-header-pills {\n  margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));\n  margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));\n}\n\n.card-img-overlay {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  padding: var(--bs-card-img-overlay-padding);\n  border-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n  width: 100%;\n}\n\n.card-img,\n.card-img-top {\n  border-top-left-radius: var(--bs-card-inner-border-radius);\n  border-top-right-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-img,\n.card-img-bottom {\n  border-bottom-right-radius: var(--bs-card-inner-border-radius);\n  border-bottom-left-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-group > .card {\n  margin-bottom: var(--bs-card-group-margin);\n}\n@media (min-width: 576px) {\n  .card-group {\n    display: flex;\n    flex-flow: row wrap;\n  }\n  .card-group > .card {\n    flex: 1 0 0%;\n    margin-bottom: 0;\n  }\n  .card-group > .card + .card {\n    margin-left: 0;\n    border-left: 0;\n  }\n  .card-group > .card:not(:last-child) {\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-top,\n.card-group > .card:not(:last-child) .card-header {\n    border-top-right-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-bottom,\n.card-group > .card:not(:last-child) .card-footer {\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:first-child) {\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-top,\n.card-group > .card:not(:first-child) .card-header {\n    border-top-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-bottom,\n.card-group > .card:not(:first-child) .card-footer {\n    border-bottom-left-radius: 0;\n  }\n}\n\n.accordion {\n  --bs-accordion-color: #212529;\n  --bs-accordion-bg: #fff;\n  --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;\n  --bs-accordion-border-color: var(--bs-border-color);\n  --bs-accordion-border-width: 1px;\n  --bs-accordion-border-radius: 0.375rem;\n  --bs-accordion-inner-border-radius: calc(0.375rem - 1px);\n  --bs-accordion-btn-padding-x: 1.25rem;\n  --bs-accordion-btn-padding-y: 1rem;\n  --bs-accordion-btn-color: #212529;\n  --bs-accordion-btn-bg: var(--bs-accordion-bg);\n  --bs-accordion-btn-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n  --bs-accordion-btn-icon-width: 1.25rem;\n  --bs-accordion-btn-icon-transform: rotate(-180deg);\n  --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;\n  --bs-accordion-btn-active-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n  --bs-accordion-btn-focus-border-color: #86b7fe;\n  --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n  --bs-accordion-body-padding-x: 1.25rem;\n  --bs-accordion-body-padding-y: 1rem;\n  --bs-accordion-active-color: #0c63e4;\n  --bs-accordion-active-bg: #e7f1ff;\n}\n\n.accordion-button {\n  position: relative;\n  display: flex;\n  align-items: center;\n  width: 100%;\n  padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);\n  font-size: 1rem;\n  color: var(--bs-accordion-btn-color);\n  text-align: left;\n  background-color: var(--bs-accordion-btn-bg);\n  border: 0;\n  border-radius: 0;\n  overflow-anchor: none;\n  transition: var(--bs-accordion-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .accordion-button {\n    transition: none;\n  }\n}\n.accordion-button:not(.collapsed) {\n  color: var(--bs-accordion-active-color);\n  background-color: var(--bs-accordion-active-bg);\n  box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color);\n}\n.accordion-button:not(.collapsed)::after {\n  background-image: var(--bs-accordion-btn-active-icon);\n  transform: var(--bs-accordion-btn-icon-transform);\n}\n.accordion-button::after {\n  flex-shrink: 0;\n  width: var(--bs-accordion-btn-icon-width);\n  height: var(--bs-accordion-btn-icon-width);\n  margin-left: auto;\n  content: \"\";\n  background-image: var(--bs-accordion-btn-icon);\n  background-repeat: no-repeat;\n  background-size: var(--bs-accordion-btn-icon-width);\n  transition: var(--bs-accordion-btn-icon-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .accordion-button::after {\n    transition: none;\n  }\n}\n.accordion-button:hover {\n  z-index: 2;\n}\n.accordion-button:focus {\n  z-index: 3;\n  border-color: var(--bs-accordion-btn-focus-border-color);\n  outline: 0;\n  box-shadow: var(--bs-accordion-btn-focus-box-shadow);\n}\n\n.accordion-header {\n  margin-bottom: 0;\n}\n\n.accordion-item {\n  color: var(--bs-accordion-color);\n  background-color: var(--bs-accordion-bg);\n  border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color);\n}\n.accordion-item:first-of-type {\n  border-top-left-radius: var(--bs-accordion-border-radius);\n  border-top-right-radius: var(--bs-accordion-border-radius);\n}\n.accordion-item:first-of-type .accordion-button {\n  border-top-left-radius: var(--bs-accordion-inner-border-radius);\n  border-top-right-radius: var(--bs-accordion-inner-border-radius);\n}\n.accordion-item:not(:first-of-type) {\n  border-top: 0;\n}\n.accordion-item:last-of-type {\n  border-bottom-right-radius: var(--bs-accordion-border-radius);\n  border-bottom-left-radius: var(--bs-accordion-border-radius);\n}\n.accordion-item:last-of-type .accordion-button.collapsed {\n  border-bottom-right-radius: var(--bs-accordion-inner-border-radius);\n  border-bottom-left-radius: var(--bs-accordion-inner-border-radius);\n}\n.accordion-item:last-of-type .accordion-collapse {\n  border-bottom-right-radius: var(--bs-accordion-border-radius);\n  border-bottom-left-radius: var(--bs-accordion-border-radius);\n}\n\n.accordion-body {\n  padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x);\n}\n\n.accordion-flush .accordion-collapse {\n  border-width: 0;\n}\n.accordion-flush .accordion-item {\n  border-right: 0;\n  border-left: 0;\n  border-radius: 0;\n}\n.accordion-flush .accordion-item:first-child {\n  border-top: 0;\n}\n.accordion-flush .accordion-item:last-child {\n  border-bottom: 0;\n}\n.accordion-flush .accordion-item .accordion-button, .accordion-flush .accordion-item .accordion-button.collapsed {\n  border-radius: 0;\n}\n\n.breadcrumb {\n  --bs-breadcrumb-padding-x: 0;\n  --bs-breadcrumb-padding-y: 0;\n  --bs-breadcrumb-margin-bottom: 1rem;\n  --bs-breadcrumb-bg: ;\n  --bs-breadcrumb-border-radius: ;\n  --bs-breadcrumb-divider-color: #6c757d;\n  --bs-breadcrumb-item-padding-x: 0.5rem;\n  --bs-breadcrumb-item-active-color: #6c757d;\n  display: flex;\n  flex-wrap: wrap;\n  padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);\n  margin-bottom: var(--bs-breadcrumb-margin-bottom);\n  font-size: var(--bs-breadcrumb-font-size);\n  list-style: none;\n  background-color: var(--bs-breadcrumb-bg);\n  border-radius: var(--bs-breadcrumb-border-radius);\n}\n\n.breadcrumb-item + .breadcrumb-item {\n  padding-left: var(--bs-breadcrumb-item-padding-x);\n}\n.breadcrumb-item + .breadcrumb-item::before {\n  float: left;\n  padding-right: var(--bs-breadcrumb-item-padding-x);\n  color: var(--bs-breadcrumb-divider-color);\n  content: var(--bs-breadcrumb-divider, \"/\") /* rtl: var(--bs-breadcrumb-divider, \"/\") */;\n}\n.breadcrumb-item.active {\n  color: var(--bs-breadcrumb-item-active-color);\n}\n\n.pagination {\n  --bs-pagination-padding-x: 0.75rem;\n  --bs-pagination-padding-y: 0.375rem;\n  --bs-pagination-font-size: 1rem;\n  --bs-pagination-color: var(--bs-link-color);\n  --bs-pagination-bg: #fff;\n  --bs-pagination-border-width: 1px;\n  --bs-pagination-border-color: #dee2e6;\n  --bs-pagination-border-radius: 0.375rem;\n  --bs-pagination-hover-color: var(--bs-link-hover-color);\n  --bs-pagination-hover-bg: #e9ecef;\n  --bs-pagination-hover-border-color: #dee2e6;\n  --bs-pagination-focus-color: var(--bs-link-hover-color);\n  --bs-pagination-focus-bg: #e9ecef;\n  --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n  --bs-pagination-active-color: #fff;\n  --bs-pagination-active-bg: #0d6efd;\n  --bs-pagination-active-border-color: #0d6efd;\n  --bs-pagination-disabled-color: #6c757d;\n  --bs-pagination-disabled-bg: #fff;\n  --bs-pagination-disabled-border-color: #dee2e6;\n  display: flex;\n  padding-left: 0;\n  list-style: none;\n}\n\n.page-link {\n  position: relative;\n  display: block;\n  padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);\n  font-size: var(--bs-pagination-font-size);\n  color: var(--bs-pagination-color);\n  text-decoration: none;\n  background-color: var(--bs-pagination-bg);\n  border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .page-link {\n    transition: none;\n  }\n}\n.page-link:hover {\n  z-index: 2;\n  color: var(--bs-pagination-hover-color);\n  background-color: var(--bs-pagination-hover-bg);\n  border-color: var(--bs-pagination-hover-border-color);\n}\n.page-link:focus {\n  z-index: 3;\n  color: var(--bs-pagination-focus-color);\n  background-color: var(--bs-pagination-focus-bg);\n  outline: 0;\n  box-shadow: var(--bs-pagination-focus-box-shadow);\n}\n.page-link.active, .active > .page-link {\n  z-index: 3;\n  color: var(--bs-pagination-active-color);\n  background-color: var(--bs-pagination-active-bg);\n  border-color: var(--bs-pagination-active-border-color);\n}\n.page-link.disabled, .disabled > .page-link {\n  color: var(--bs-pagination-disabled-color);\n  pointer-events: none;\n  background-color: var(--bs-pagination-disabled-bg);\n  border-color: var(--bs-pagination-disabled-border-color);\n}\n\n.page-item:not(:first-child) .page-link {\n  margin-left: -1px;\n}\n.page-item:first-child .page-link {\n  border-top-left-radius: var(--bs-pagination-border-radius);\n  border-bottom-left-radius: var(--bs-pagination-border-radius);\n}\n.page-item:last-child .page-link {\n  border-top-right-radius: var(--bs-pagination-border-radius);\n  border-bottom-right-radius: var(--bs-pagination-border-radius);\n}\n\n.pagination-lg {\n  --bs-pagination-padding-x: 1.5rem;\n  --bs-pagination-padding-y: 0.75rem;\n  --bs-pagination-font-size: 1.25rem;\n  --bs-pagination-border-radius: 0.5rem;\n}\n\n.pagination-sm {\n  --bs-pagination-padding-x: 0.5rem;\n  --bs-pagination-padding-y: 0.25rem;\n  --bs-pagination-font-size: 0.875rem;\n  --bs-pagination-border-radius: 0.25rem;\n}\n\n.badge {\n  --bs-badge-padding-x: 0.65em;\n  --bs-badge-padding-y: 0.35em;\n  --bs-badge-font-size: 0.75em;\n  --bs-badge-font-weight: 700;\n  --bs-badge-color: #fff;\n  --bs-badge-border-radius: 0.375rem;\n  display: inline-block;\n  padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x);\n  font-size: var(--bs-badge-font-size);\n  font-weight: var(--bs-badge-font-weight);\n  line-height: 1;\n  color: var(--bs-badge-color);\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: var(--bs-badge-border-radius);\n}\n.badge:empty {\n  display: none;\n}\n\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n\n.alert {\n  --bs-alert-bg: transparent;\n  --bs-alert-padding-x: 1rem;\n  --bs-alert-padding-y: 1rem;\n  --bs-alert-margin-bottom: 1rem;\n  --bs-alert-color: inherit;\n  --bs-alert-border-color: transparent;\n  --bs-alert-border: 1px solid var(--bs-alert-border-color);\n  --bs-alert-border-radius: 0.375rem;\n  position: relative;\n  padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);\n  margin-bottom: var(--bs-alert-margin-bottom);\n  color: var(--bs-alert-color);\n  background-color: var(--bs-alert-bg);\n  border: var(--bs-alert-border);\n  border-radius: var(--bs-alert-border-radius);\n}\n\n.alert-heading {\n  color: inherit;\n}\n\n.alert-link {\n  font-weight: 700;\n}\n\n.alert-dismissible {\n  padding-right: 3rem;\n}\n.alert-dismissible .btn-close {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  padding: 1.25rem 1rem;\n}\n\n.alert-primary {\n  --bs-alert-color: #084298;\n  --bs-alert-bg: #cfe2ff;\n  --bs-alert-border-color: #b6d4fe;\n}\n.alert-primary .alert-link {\n  color: #06357a;\n}\n\n.alert-secondary {\n  --bs-alert-color: #41464b;\n  --bs-alert-bg: #e2e3e5;\n  --bs-alert-border-color: #d3d6d8;\n}\n.alert-secondary .alert-link {\n  color: #34383c;\n}\n\n.alert-success {\n  --bs-alert-color: #0f5132;\n  --bs-alert-bg: #d1e7dd;\n  --bs-alert-border-color: #badbcc;\n}\n.alert-success .alert-link {\n  color: #0c4128;\n}\n\n.alert-info {\n  --bs-alert-color: #055160;\n  --bs-alert-bg: #cff4fc;\n  --bs-alert-border-color: #b6effb;\n}\n.alert-info .alert-link {\n  color: #04414d;\n}\n\n.alert-warning {\n  --bs-alert-color: #664d03;\n  --bs-alert-bg: #fff3cd;\n  --bs-alert-border-color: #ffecb5;\n}\n.alert-warning .alert-link {\n  color: #523e02;\n}\n\n.alert-danger {\n  --bs-alert-color: #842029;\n  --bs-alert-bg: #f8d7da;\n  --bs-alert-border-color: #f5c2c7;\n}\n.alert-danger .alert-link {\n  color: #6a1a21;\n}\n\n.alert-light {\n  --bs-alert-color: #636464;\n  --bs-alert-bg: #fefefe;\n  --bs-alert-border-color: #fdfdfe;\n}\n.alert-light .alert-link {\n  color: #4f5050;\n}\n\n.alert-dark {\n  --bs-alert-color: #141619;\n  --bs-alert-bg: #d3d3d4;\n  --bs-alert-border-color: #bcbebf;\n}\n.alert-dark .alert-link {\n  color: #101214;\n}\n\n@keyframes progress-bar-stripes {\n  0% {\n    background-position-x: 1rem;\n  }\n}\n.progress {\n  --bs-progress-height: 1rem;\n  --bs-progress-font-size: 0.75rem;\n  --bs-progress-bg: #e9ecef;\n  --bs-progress-border-radius: 0.375rem;\n  --bs-progress-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n  --bs-progress-bar-color: #fff;\n  --bs-progress-bar-bg: #0d6efd;\n  --bs-progress-bar-transition: width 0.6s ease;\n  display: flex;\n  height: var(--bs-progress-height);\n  overflow: hidden;\n  font-size: var(--bs-progress-font-size);\n  background-color: var(--bs-progress-bg);\n  border-radius: var(--bs-progress-border-radius);\n}\n\n.progress-bar {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  overflow: hidden;\n  color: var(--bs-progress-bar-color);\n  text-align: center;\n  white-space: nowrap;\n  background-color: var(--bs-progress-bar-bg);\n  transition: var(--bs-progress-bar-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar {\n    transition: none;\n  }\n}\n\n.progress-bar-striped {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: var(--bs-progress-height) var(--bs-progress-height);\n}\n\n.progress-bar-animated {\n  animation: 1s linear infinite progress-bar-stripes;\n}\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar-animated {\n    animation: none;\n  }\n}\n\n.list-group {\n  --bs-list-group-color: #212529;\n  --bs-list-group-bg: #fff;\n  --bs-list-group-border-color: rgba(0, 0, 0, 0.125);\n  --bs-list-group-border-width: 1px;\n  --bs-list-group-border-radius: 0.375rem;\n  --bs-list-group-item-padding-x: 1rem;\n  --bs-list-group-item-padding-y: 0.5rem;\n  --bs-list-group-action-color: #495057;\n  --bs-list-group-action-hover-color: #495057;\n  --bs-list-group-action-hover-bg: #f8f9fa;\n  --bs-list-group-action-active-color: #212529;\n  --bs-list-group-action-active-bg: #e9ecef;\n  --bs-list-group-disabled-color: #6c757d;\n  --bs-list-group-disabled-bg: #fff;\n  --bs-list-group-active-color: #fff;\n  --bs-list-group-active-bg: #0d6efd;\n  --bs-list-group-active-border-color: #0d6efd;\n  display: flex;\n  flex-direction: column;\n  padding-left: 0;\n  margin-bottom: 0;\n  border-radius: var(--bs-list-group-border-radius);\n}\n\n.list-group-numbered {\n  list-style-type: none;\n  counter-reset: section;\n}\n.list-group-numbered > .list-group-item::before {\n  content: counters(section, \".\") \". \";\n  counter-increment: section;\n}\n\n.list-group-item-action {\n  width: 100%;\n  color: var(--bs-list-group-action-color);\n  text-align: inherit;\n}\n.list-group-item-action:hover, .list-group-item-action:focus {\n  z-index: 1;\n  color: var(--bs-list-group-action-hover-color);\n  text-decoration: none;\n  background-color: var(--bs-list-group-action-hover-bg);\n}\n.list-group-item-action:active {\n  color: var(--bs-list-group-action-active-color);\n  background-color: var(--bs-list-group-action-active-bg);\n}\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);\n  color: var(--bs-list-group-color);\n  text-decoration: none;\n  background-color: var(--bs-list-group-bg);\n  border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color);\n}\n.list-group-item:first-child {\n  border-top-left-radius: inherit;\n  border-top-right-radius: inherit;\n}\n.list-group-item:last-child {\n  border-bottom-right-radius: inherit;\n  border-bottom-left-radius: inherit;\n}\n.list-group-item.disabled, .list-group-item:disabled {\n  color: var(--bs-list-group-disabled-color);\n  pointer-events: none;\n  background-color: var(--bs-list-group-disabled-bg);\n}\n.list-group-item.active {\n  z-index: 2;\n  color: var(--bs-list-group-active-color);\n  background-color: var(--bs-list-group-active-bg);\n  border-color: var(--bs-list-group-active-border-color);\n}\n.list-group-item + .list-group-item {\n  border-top-width: 0;\n}\n.list-group-item + .list-group-item.active {\n  margin-top: calc(-1 * var(--bs-list-group-border-width));\n  border-top-width: var(--bs-list-group-border-width);\n}\n\n.list-group-horizontal {\n  flex-direction: row;\n}\n.list-group-horizontal > .list-group-item:first-child:not(:last-child) {\n  border-bottom-left-radius: var(--bs-list-group-border-radius);\n  border-top-right-radius: 0;\n}\n.list-group-horizontal > .list-group-item:last-child:not(:first-child) {\n  border-top-right-radius: var(--bs-list-group-border-radius);\n  border-bottom-left-radius: 0;\n}\n.list-group-horizontal > .list-group-item.active {\n  margin-top: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item {\n  border-top-width: var(--bs-list-group-border-width);\n  border-left-width: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item.active {\n  margin-left: calc(-1 * var(--bs-list-group-border-width));\n  border-left-width: var(--bs-list-group-border-width);\n}\n\n@media (min-width: 576px) {\n  .list-group-horizontal-sm {\n    flex-direction: row;\n  }\n  .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) {\n    border-bottom-left-radius: var(--bs-list-group-border-radius);\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) {\n    border-top-right-radius: var(--bs-list-group-border-radius);\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-left-width: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item.active {\n    margin-left: calc(-1 * var(--bs-list-group-border-width));\n    border-left-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 768px) {\n  .list-group-horizontal-md {\n    flex-direction: row;\n  }\n  .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) {\n    border-bottom-left-radius: var(--bs-list-group-border-radius);\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) {\n    border-top-right-radius: var(--bs-list-group-border-radius);\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-left-width: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item.active {\n    margin-left: calc(-1 * var(--bs-list-group-border-width));\n    border-left-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 992px) {\n  .list-group-horizontal-lg {\n    flex-direction: row;\n  }\n  .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) {\n    border-bottom-left-radius: var(--bs-list-group-border-radius);\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) {\n    border-top-right-radius: var(--bs-list-group-border-radius);\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-left-width: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item.active {\n    margin-left: calc(-1 * var(--bs-list-group-border-width));\n    border-left-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 1200px) {\n  .list-group-horizontal-xl {\n    flex-direction: row;\n  }\n  .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) {\n    border-bottom-left-radius: var(--bs-list-group-border-radius);\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) {\n    border-top-right-radius: var(--bs-list-group-border-radius);\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-left-width: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item.active {\n    margin-left: calc(-1 * var(--bs-list-group-border-width));\n    border-left-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 1400px) {\n  .list-group-horizontal-xxl {\n    flex-direction: row;\n  }\n  .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) {\n    border-bottom-left-radius: var(--bs-list-group-border-radius);\n    border-top-right-radius: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) {\n    border-top-right-radius: var(--bs-list-group-border-radius);\n    border-bottom-left-radius: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-left-width: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item + .list-group-item.active {\n    margin-left: calc(-1 * var(--bs-list-group-border-width));\n    border-left-width: var(--bs-list-group-border-width);\n  }\n}\n.list-group-flush {\n  border-radius: 0;\n}\n.list-group-flush > .list-group-item {\n  border-width: 0 0 var(--bs-list-group-border-width);\n}\n.list-group-flush > .list-group-item:last-child {\n  border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n  color: #084298;\n  background-color: #cfe2ff;\n}\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n  color: #084298;\n  background-color: #bacbe6;\n}\n.list-group-item-primary.list-group-item-action.active {\n  color: #fff;\n  background-color: #084298;\n  border-color: #084298;\n}\n\n.list-group-item-secondary {\n  color: #41464b;\n  background-color: #e2e3e5;\n}\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n  color: #41464b;\n  background-color: #cbccce;\n}\n.list-group-item-secondary.list-group-item-action.active {\n  color: #fff;\n  background-color: #41464b;\n  border-color: #41464b;\n}\n\n.list-group-item-success {\n  color: #0f5132;\n  background-color: #d1e7dd;\n}\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n  color: #0f5132;\n  background-color: #bcd0c7;\n}\n.list-group-item-success.list-group-item-action.active {\n  color: #fff;\n  background-color: #0f5132;\n  border-color: #0f5132;\n}\n\n.list-group-item-info {\n  color: #055160;\n  background-color: #cff4fc;\n}\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n  color: #055160;\n  background-color: #badce3;\n}\n.list-group-item-info.list-group-item-action.active {\n  color: #fff;\n  background-color: #055160;\n  border-color: #055160;\n}\n\n.list-group-item-warning {\n  color: #664d03;\n  background-color: #fff3cd;\n}\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n  color: #664d03;\n  background-color: #e6dbb9;\n}\n.list-group-item-warning.list-group-item-action.active {\n  color: #fff;\n  background-color: #664d03;\n  border-color: #664d03;\n}\n\n.list-group-item-danger {\n  color: #842029;\n  background-color: #f8d7da;\n}\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n  color: #842029;\n  background-color: #dfc2c4;\n}\n.list-group-item-danger.list-group-item-action.active {\n  color: #fff;\n  background-color: #842029;\n  border-color: #842029;\n}\n\n.list-group-item-light {\n  color: #636464;\n  background-color: #fefefe;\n}\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n  color: #636464;\n  background-color: #e5e5e5;\n}\n.list-group-item-light.list-group-item-action.active {\n  color: #fff;\n  background-color: #636464;\n  border-color: #636464;\n}\n\n.list-group-item-dark {\n  color: #141619;\n  background-color: #d3d3d4;\n}\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n  color: #141619;\n  background-color: #bebebf;\n}\n.list-group-item-dark.list-group-item-action.active {\n  color: #fff;\n  background-color: #141619;\n  border-color: #141619;\n}\n\n.btn-close {\n  box-sizing: content-box;\n  width: 1em;\n  height: 1em;\n  padding: 0.25em 0.25em;\n  color: #000;\n  background: transparent url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e\") center/1em auto no-repeat;\n  border: 0;\n  border-radius: 0.375rem;\n  opacity: 0.5;\n}\n.btn-close:hover {\n  color: #000;\n  text-decoration: none;\n  opacity: 0.75;\n}\n.btn-close:focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n  opacity: 1;\n}\n.btn-close:disabled, .btn-close.disabled {\n  pointer-events: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n  opacity: 0.25;\n}\n\n.btn-close-white {\n  filter: invert(1) grayscale(100%) brightness(200%);\n}\n\n.toast {\n  --bs-toast-zindex: 1090;\n  --bs-toast-padding-x: 0.75rem;\n  --bs-toast-padding-y: 0.5rem;\n  --bs-toast-spacing: 1.5rem;\n  --bs-toast-max-width: 350px;\n  --bs-toast-font-size: 0.875rem;\n  --bs-toast-color: ;\n  --bs-toast-bg: rgba(255, 255, 255, 0.85);\n  --bs-toast-border-width: 1px;\n  --bs-toast-border-color: var(--bs-border-color-translucent);\n  --bs-toast-border-radius: 0.375rem;\n  --bs-toast-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  --bs-toast-header-color: #6c757d;\n  --bs-toast-header-bg: rgba(255, 255, 255, 0.85);\n  --bs-toast-header-border-color: rgba(0, 0, 0, 0.05);\n  width: var(--bs-toast-max-width);\n  max-width: 100%;\n  font-size: var(--bs-toast-font-size);\n  color: var(--bs-toast-color);\n  pointer-events: auto;\n  background-color: var(--bs-toast-bg);\n  background-clip: padding-box;\n  border: var(--bs-toast-border-width) solid var(--bs-toast-border-color);\n  box-shadow: var(--bs-toast-box-shadow);\n  border-radius: var(--bs-toast-border-radius);\n}\n.toast.showing {\n  opacity: 0;\n}\n.toast:not(.show) {\n  display: none;\n}\n\n.toast-container {\n  --bs-toast-zindex: 1090;\n  position: absolute;\n  z-index: var(--bs-toast-zindex);\n  width: -webkit-max-content;\n  width: -moz-max-content;\n  width: max-content;\n  max-width: 100%;\n  pointer-events: none;\n}\n.toast-container > :not(:last-child) {\n  margin-bottom: var(--bs-toast-spacing);\n}\n\n.toast-header {\n  display: flex;\n  align-items: center;\n  padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);\n  color: var(--bs-toast-header-color);\n  background-color: var(--bs-toast-header-bg);\n  background-clip: padding-box;\n  border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);\n  border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));\n  border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));\n}\n.toast-header .btn-close {\n  margin-right: calc(-0.5 * var(--bs-toast-padding-x));\n  margin-left: var(--bs-toast-padding-x);\n}\n\n.toast-body {\n  padding: var(--bs-toast-padding-x);\n  word-wrap: break-word;\n}\n\n.modal {\n  --bs-modal-zindex: 1055;\n  --bs-modal-width: 500px;\n  --bs-modal-padding: 1rem;\n  --bs-modal-margin: 0.5rem;\n  --bs-modal-color: ;\n  --bs-modal-bg: #fff;\n  --bs-modal-border-color: var(--bs-border-color-translucent);\n  --bs-modal-border-width: 1px;\n  --bs-modal-border-radius: 0.5rem;\n  --bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);\n  --bs-modal-inner-border-radius: calc(0.5rem - 1px);\n  --bs-modal-header-padding-x: 1rem;\n  --bs-modal-header-padding-y: 1rem;\n  --bs-modal-header-padding: 1rem 1rem;\n  --bs-modal-header-border-color: var(--bs-border-color);\n  --bs-modal-header-border-width: 1px;\n  --bs-modal-title-line-height: 1.5;\n  --bs-modal-footer-gap: 0.5rem;\n  --bs-modal-footer-bg: ;\n  --bs-modal-footer-border-color: var(--bs-border-color);\n  --bs-modal-footer-border-width: 1px;\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: var(--bs-modal-zindex);\n  display: none;\n  width: 100%;\n  height: 100%;\n  overflow-x: hidden;\n  overflow-y: auto;\n  outline: 0;\n}\n\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: var(--bs-modal-margin);\n  pointer-events: none;\n}\n.modal.fade .modal-dialog {\n  transition: transform 0.3s ease-out;\n  transform: translate(0, -50px);\n}\n@media (prefers-reduced-motion: reduce) {\n  .modal.fade .modal-dialog {\n    transition: none;\n  }\n}\n.modal.show .modal-dialog {\n  transform: none;\n}\n.modal.modal-static .modal-dialog {\n  transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n  height: calc(100% - var(--bs-modal-margin) * 2);\n}\n.modal-dialog-scrollable .modal-content {\n  max-height: 100%;\n  overflow: hidden;\n}\n.modal-dialog-scrollable .modal-body {\n  overflow-y: auto;\n}\n\n.modal-dialog-centered {\n  display: flex;\n  align-items: center;\n  min-height: calc(100% - var(--bs-modal-margin) * 2);\n}\n\n.modal-content {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  width: 100%;\n  color: var(--bs-modal-color);\n  pointer-events: auto;\n  background-color: var(--bs-modal-bg);\n  background-clip: padding-box;\n  border: var(--bs-modal-border-width) solid var(--bs-modal-border-color);\n  border-radius: var(--bs-modal-border-radius);\n  outline: 0;\n}\n\n.modal-backdrop {\n  --bs-backdrop-zindex: 1050;\n  --bs-backdrop-bg: #000;\n  --bs-backdrop-opacity: 0.5;\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: var(--bs-backdrop-zindex);\n  width: 100vw;\n  height: 100vh;\n  background-color: var(--bs-backdrop-bg);\n}\n.modal-backdrop.fade {\n  opacity: 0;\n}\n.modal-backdrop.show {\n  opacity: var(--bs-backdrop-opacity);\n}\n\n.modal-header {\n  display: flex;\n  flex-shrink: 0;\n  align-items: center;\n  justify-content: space-between;\n  padding: var(--bs-modal-header-padding);\n  border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);\n  border-top-left-radius: var(--bs-modal-inner-border-radius);\n  border-top-right-radius: var(--bs-modal-inner-border-radius);\n}\n.modal-header .btn-close {\n  padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5);\n  margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) auto;\n}\n\n.modal-title {\n  margin-bottom: 0;\n  line-height: var(--bs-modal-title-line-height);\n}\n\n.modal-body {\n  position: relative;\n  flex: 1 1 auto;\n  padding: var(--bs-modal-padding);\n}\n\n.modal-footer {\n  display: flex;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  align-items: center;\n  justify-content: flex-end;\n  padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5);\n  background-color: var(--bs-modal-footer-bg);\n  border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);\n  border-bottom-right-radius: var(--bs-modal-inner-border-radius);\n  border-bottom-left-radius: var(--bs-modal-inner-border-radius);\n}\n.modal-footer > * {\n  margin: calc(var(--bs-modal-footer-gap) * 0.5);\n}\n\n@media (min-width: 576px) {\n  .modal {\n    --bs-modal-margin: 1.75rem;\n    --bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  }\n  .modal-dialog {\n    max-width: var(--bs-modal-width);\n    margin-right: auto;\n    margin-left: auto;\n  }\n  .modal-sm {\n    --bs-modal-width: 300px;\n  }\n}\n@media (min-width: 992px) {\n  .modal-lg,\n.modal-xl {\n    --bs-modal-width: 800px;\n  }\n}\n@media (min-width: 1200px) {\n  .modal-xl {\n    --bs-modal-width: 1140px;\n  }\n}\n.modal-fullscreen {\n  width: 100vw;\n  max-width: none;\n  height: 100%;\n  margin: 0;\n}\n.modal-fullscreen .modal-content {\n  height: 100%;\n  border: 0;\n  border-radius: 0;\n}\n.modal-fullscreen .modal-header,\n.modal-fullscreen .modal-footer {\n  border-radius: 0;\n}\n.modal-fullscreen .modal-body {\n  overflow-y: auto;\n}\n\n@media (max-width: 575.98px) {\n  .modal-fullscreen-sm-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-sm-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-sm-down .modal-header,\n.modal-fullscreen-sm-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-sm-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 767.98px) {\n  .modal-fullscreen-md-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-md-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-md-down .modal-header,\n.modal-fullscreen-md-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-md-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 991.98px) {\n  .modal-fullscreen-lg-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-lg-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-lg-down .modal-header,\n.modal-fullscreen-lg-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-lg-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 1199.98px) {\n  .modal-fullscreen-xl-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-xl-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-xl-down .modal-header,\n.modal-fullscreen-xl-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-xl-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 1399.98px) {\n  .modal-fullscreen-xxl-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-xxl-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-xxl-down .modal-header,\n.modal-fullscreen-xxl-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-xxl-down .modal-body {\n    overflow-y: auto;\n  }\n}\n.tooltip {\n  --bs-tooltip-zindex: 1080;\n  --bs-tooltip-max-width: 200px;\n  --bs-tooltip-padding-x: 0.5rem;\n  --bs-tooltip-padding-y: 0.25rem;\n  --bs-tooltip-margin: ;\n  --bs-tooltip-font-size: 0.875rem;\n  --bs-tooltip-color: #fff;\n  --bs-tooltip-bg: #000;\n  --bs-tooltip-border-radius: 0.375rem;\n  --bs-tooltip-opacity: 0.9;\n  --bs-tooltip-arrow-width: 0.8rem;\n  --bs-tooltip-arrow-height: 0.4rem;\n  z-index: var(--bs-tooltip-zindex);\n  display: block;\n  padding: var(--bs-tooltip-arrow-height);\n  margin: var(--bs-tooltip-margin);\n  font-family: var(--bs-font-sans-serif);\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  white-space: normal;\n  word-spacing: normal;\n  line-break: auto;\n  font-size: var(--bs-tooltip-font-size);\n  word-wrap: break-word;\n  opacity: 0;\n}\n.tooltip.show {\n  opacity: var(--bs-tooltip-opacity);\n}\n.tooltip .tooltip-arrow {\n  display: block;\n  width: var(--bs-tooltip-arrow-width);\n  height: var(--bs-tooltip-arrow-height);\n}\n.tooltip .tooltip-arrow::before {\n  position: absolute;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {\n  bottom: 0;\n}\n.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {\n  top: -1px;\n  border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;\n  border-top-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {\n  left: 0;\n  width: var(--bs-tooltip-arrow-height);\n  height: var(--bs-tooltip-arrow-width);\n}\n.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {\n  right: -1px;\n  border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;\n  border-right-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:end:ignore */\n.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {\n  top: 0;\n}\n.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {\n  bottom: -1px;\n  border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);\n  border-bottom-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {\n  right: 0;\n  width: var(--bs-tooltip-arrow-height);\n  height: var(--bs-tooltip-arrow-width);\n}\n.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {\n  left: -1px;\n  border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);\n  border-left-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:end:ignore */\n.tooltip-inner {\n  max-width: var(--bs-tooltip-max-width);\n  padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);\n  color: var(--bs-tooltip-color);\n  text-align: center;\n  background-color: var(--bs-tooltip-bg);\n  border-radius: var(--bs-tooltip-border-radius);\n}\n\n.popover {\n  --bs-popover-zindex: 1070;\n  --bs-popover-max-width: 276px;\n  --bs-popover-font-size: 0.875rem;\n  --bs-popover-bg: #fff;\n  --bs-popover-border-width: 1px;\n  --bs-popover-border-color: var(--bs-border-color-translucent);\n  --bs-popover-border-radius: 0.5rem;\n  --bs-popover-inner-border-radius: calc(0.5rem - 1px);\n  --bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  --bs-popover-header-padding-x: 1rem;\n  --bs-popover-header-padding-y: 0.5rem;\n  --bs-popover-header-font-size: 1rem;\n  --bs-popover-header-color: ;\n  --bs-popover-header-bg: #f0f0f0;\n  --bs-popover-body-padding-x: 1rem;\n  --bs-popover-body-padding-y: 1rem;\n  --bs-popover-body-color: #212529;\n  --bs-popover-arrow-width: 1rem;\n  --bs-popover-arrow-height: 0.5rem;\n  --bs-popover-arrow-border: var(--bs-popover-border-color);\n  z-index: var(--bs-popover-zindex);\n  display: block;\n  max-width: var(--bs-popover-max-width);\n  font-family: var(--bs-font-sans-serif);\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: left;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  white-space: normal;\n  word-spacing: normal;\n  line-break: auto;\n  font-size: var(--bs-popover-font-size);\n  word-wrap: break-word;\n  background-color: var(--bs-popover-bg);\n  background-clip: padding-box;\n  border: var(--bs-popover-border-width) solid var(--bs-popover-border-color);\n  border-radius: var(--bs-popover-border-radius);\n}\n.popover .popover-arrow {\n  display: block;\n  width: var(--bs-popover-arrow-width);\n  height: var(--bs-popover-arrow-height);\n}\n.popover .popover-arrow::before, .popover .popover-arrow::after {\n  position: absolute;\n  display: block;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n  border-width: 0;\n}\n\n.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow {\n  bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n}\n.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {\n  border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;\n}\n.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before {\n  bottom: 0;\n  border-top-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {\n  bottom: var(--bs-popover-border-width);\n  border-top-color: var(--bs-popover-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow {\n  left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n  width: var(--bs-popover-arrow-height);\n  height: var(--bs-popover-arrow-width);\n}\n.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {\n  border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;\n}\n.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before {\n  left: 0;\n  border-right-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {\n  left: var(--bs-popover-border-width);\n  border-right-color: var(--bs-popover-bg);\n}\n\n/* rtl:end:ignore */\n.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow {\n  top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n}\n.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {\n  border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);\n}\n.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before {\n  top: 0;\n  border-bottom-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {\n  top: var(--bs-popover-border-width);\n  border-bottom-color: var(--bs-popover-bg);\n}\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before {\n  position: absolute;\n  top: 0;\n  left: 50%;\n  display: block;\n  width: var(--bs-popover-arrow-width);\n  margin-left: calc(-0.5 * var(--bs-popover-arrow-width));\n  content: \"\";\n  border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow {\n  right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n  width: var(--bs-popover-arrow-height);\n  height: var(--bs-popover-arrow-width);\n}\n.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {\n  border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);\n}\n.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before {\n  right: 0;\n  border-left-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {\n  right: var(--bs-popover-border-width);\n  border-left-color: var(--bs-popover-bg);\n}\n\n/* rtl:end:ignore */\n.popover-header {\n  padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);\n  margin-bottom: 0;\n  font-size: var(--bs-popover-header-font-size);\n  color: var(--bs-popover-header-color);\n  background-color: var(--bs-popover-header-bg);\n  border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color);\n  border-top-left-radius: var(--bs-popover-inner-border-radius);\n  border-top-right-radius: var(--bs-popover-inner-border-radius);\n}\n.popover-header:empty {\n  display: none;\n}\n\n.popover-body {\n  padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);\n  color: var(--bs-popover-body-color);\n}\n\n.carousel {\n  position: relative;\n}\n\n.carousel.pointer-event {\n  touch-action: pan-y;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n.carousel-inner::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.carousel-item {\n  position: relative;\n  display: none;\n  float: left;\n  width: 100%;\n  margin-right: -100%;\n  -webkit-backface-visibility: hidden;\n  backface-visibility: hidden;\n  transition: transform 0.6s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-item {\n    transition: none;\n  }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n  display: block;\n}\n\n.carousel-item-next:not(.carousel-item-start),\n.active.carousel-item-end {\n  transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-end),\n.active.carousel-item-start {\n  transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n  opacity: 0;\n  transition-property: opacity;\n  transform: none;\n}\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-start,\n.carousel-fade .carousel-item-prev.carousel-item-end {\n  z-index: 1;\n  opacity: 1;\n}\n.carousel-fade .active.carousel-item-start,\n.carousel-fade .active.carousel-item-end {\n  z-index: 0;\n  opacity: 0;\n  transition: opacity 0s 0.6s;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-fade .active.carousel-item-start,\n.carousel-fade .active.carousel-item-end {\n    transition: none;\n  }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  z-index: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 15%;\n  padding: 0;\n  color: #fff;\n  text-align: center;\n  background: none;\n  border: 0;\n  opacity: 0.5;\n  transition: opacity 0.15s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-control-prev,\n.carousel-control-next {\n    transition: none;\n  }\n}\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n  color: #fff;\n  text-decoration: none;\n  outline: 0;\n  opacity: 0.9;\n}\n\n.carousel-control-prev {\n  left: 0;\n}\n\n.carousel-control-next {\n  right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  background-repeat: no-repeat;\n  background-position: 50%;\n  background-size: 100% 100%;\n}\n\n/* rtl:options: {\n  \"autoRename\": true,\n  \"stringMap\":[ {\n    \"name\"    : \"prev-next\",\n    \"search\"  : \"prev\",\n    \"replace\" : \"next\"\n  } ]\n} */\n.carousel-control-prev-icon {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e\");\n}\n\n.carousel-control-next-icon {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n}\n\n.carousel-indicators {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 2;\n  display: flex;\n  justify-content: center;\n  padding: 0;\n  margin-right: 15%;\n  margin-bottom: 1rem;\n  margin-left: 15%;\n  list-style: none;\n}\n.carousel-indicators [data-bs-target] {\n  box-sizing: content-box;\n  flex: 0 1 auto;\n  width: 30px;\n  height: 3px;\n  padding: 0;\n  margin-right: 3px;\n  margin-left: 3px;\n  text-indent: -999px;\n  cursor: pointer;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 0;\n  border-top: 10px solid transparent;\n  border-bottom: 10px solid transparent;\n  opacity: 0.5;\n  transition: opacity 0.6s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-indicators [data-bs-target] {\n    transition: none;\n  }\n}\n.carousel-indicators .active {\n  opacity: 1;\n}\n\n.carousel-caption {\n  position: absolute;\n  right: 15%;\n  bottom: 1.25rem;\n  left: 15%;\n  padding-top: 1.25rem;\n  padding-bottom: 1.25rem;\n  color: #fff;\n  text-align: center;\n}\n\n.carousel-dark .carousel-control-prev-icon,\n.carousel-dark .carousel-control-next-icon {\n  filter: invert(1) grayscale(100);\n}\n.carousel-dark .carousel-indicators [data-bs-target] {\n  background-color: #000;\n}\n.carousel-dark .carousel-caption {\n  color: #000;\n}\n\n.spinner-grow,\n.spinner-border {\n  display: inline-block;\n  width: var(--bs-spinner-width);\n  height: var(--bs-spinner-height);\n  vertical-align: var(--bs-spinner-vertical-align);\n  border-radius: 50%;\n  animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name);\n}\n\n@keyframes spinner-border {\n  to {\n    transform: rotate(360deg) /* rtl:ignore */;\n  }\n}\n.spinner-border {\n  --bs-spinner-width: 2rem;\n  --bs-spinner-height: 2rem;\n  --bs-spinner-vertical-align: -0.125em;\n  --bs-spinner-border-width: 0.25em;\n  --bs-spinner-animation-speed: 0.75s;\n  --bs-spinner-animation-name: spinner-border;\n  border: var(--bs-spinner-border-width) solid currentcolor;\n  border-right-color: transparent;\n}\n\n.spinner-border-sm {\n  --bs-spinner-width: 1rem;\n  --bs-spinner-height: 1rem;\n  --bs-spinner-border-width: 0.2em;\n}\n\n@keyframes spinner-grow {\n  0% {\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    transform: none;\n  }\n}\n.spinner-grow {\n  --bs-spinner-width: 2rem;\n  --bs-spinner-height: 2rem;\n  --bs-spinner-vertical-align: -0.125em;\n  --bs-spinner-animation-speed: 0.75s;\n  --bs-spinner-animation-name: spinner-grow;\n  background-color: currentcolor;\n  opacity: 0;\n}\n\n.spinner-grow-sm {\n  --bs-spinner-width: 1rem;\n  --bs-spinner-height: 1rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .spinner-border,\n.spinner-grow {\n    --bs-spinner-animation-speed: 1.5s;\n  }\n}\n.offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm {\n  --bs-offcanvas-zindex: 1045;\n  --bs-offcanvas-width: 400px;\n  --bs-offcanvas-height: 30vh;\n  --bs-offcanvas-padding-x: 1rem;\n  --bs-offcanvas-padding-y: 1rem;\n  --bs-offcanvas-color: ;\n  --bs-offcanvas-bg: #fff;\n  --bs-offcanvas-border-width: 1px;\n  --bs-offcanvas-border-color: var(--bs-border-color-translucent);\n  --bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);\n}\n\n@media (max-width: 575.98px) {\n  .offcanvas-sm {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-sm {\n    transition: none;\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-start {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-end {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-top {\n    top: 0;\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-bottom {\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 576px) {\n  .offcanvas-sm {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-sm .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-sm .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .offcanvas-md {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-md {\n    transition: none;\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-start {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-end {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-top {\n    top: 0;\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-bottom {\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 768px) {\n  .offcanvas-md {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-md .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-md .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .offcanvas-lg {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-lg {\n    transition: none;\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-start {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-end {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-top {\n    top: 0;\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-bottom {\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 992px) {\n  .offcanvas-lg {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-lg .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-lg .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .offcanvas-xl {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-xl {\n    transition: none;\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-start {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-end {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-top {\n    top: 0;\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-bottom {\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 1200px) {\n  .offcanvas-xl {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-xl .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-xl .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-xxl {\n    transition: none;\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-start {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-end {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-top {\n    top: 0;\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-bottom {\n    right: 0;\n    left: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 1400px) {\n  .offcanvas-xxl {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-xxl .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-xxl .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n.offcanvas {\n  position: fixed;\n  bottom: 0;\n  z-index: var(--bs-offcanvas-zindex);\n  display: flex;\n  flex-direction: column;\n  max-width: 100%;\n  color: var(--bs-offcanvas-color);\n  visibility: hidden;\n  background-color: var(--bs-offcanvas-bg);\n  background-clip: padding-box;\n  outline: 0;\n  transition: transform 0.3s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .offcanvas {\n    transition: none;\n  }\n}\n.offcanvas.offcanvas-start {\n  top: 0;\n  left: 0;\n  width: var(--bs-offcanvas-width);\n  border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateX(-100%);\n}\n.offcanvas.offcanvas-end {\n  top: 0;\n  right: 0;\n  width: var(--bs-offcanvas-width);\n  border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateX(100%);\n}\n.offcanvas.offcanvas-top {\n  top: 0;\n  right: 0;\n  left: 0;\n  height: var(--bs-offcanvas-height);\n  max-height: 100%;\n  border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateY(-100%);\n}\n.offcanvas.offcanvas-bottom {\n  right: 0;\n  left: 0;\n  height: var(--bs-offcanvas-height);\n  max-height: 100%;\n  border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateY(100%);\n}\n.offcanvas.showing, .offcanvas.show:not(.hiding) {\n  transform: none;\n}\n.offcanvas.showing, .offcanvas.hiding, .offcanvas.show {\n  visibility: visible;\n}\n\n.offcanvas-backdrop {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 1040;\n  width: 100vw;\n  height: 100vh;\n  background-color: #000;\n}\n.offcanvas-backdrop.fade {\n  opacity: 0;\n}\n.offcanvas-backdrop.show {\n  opacity: 0.5;\n}\n\n.offcanvas-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);\n}\n.offcanvas-header .btn-close {\n  padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5);\n  margin-top: calc(-0.5 * var(--bs-offcanvas-padding-y));\n  margin-right: calc(-0.5 * var(--bs-offcanvas-padding-x));\n  margin-bottom: calc(-0.5 * var(--bs-offcanvas-padding-y));\n}\n\n.offcanvas-title {\n  margin-bottom: 0;\n  line-height: 1.5;\n}\n\n.offcanvas-body {\n  flex-grow: 1;\n  padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);\n  overflow-y: auto;\n}\n\n.placeholder {\n  display: inline-block;\n  min-height: 1em;\n  vertical-align: middle;\n  cursor: wait;\n  background-color: currentcolor;\n  opacity: 0.5;\n}\n.placeholder.btn::before {\n  display: inline-block;\n  content: \"\";\n}\n\n.placeholder-xs {\n  min-height: 0.6em;\n}\n\n.placeholder-sm {\n  min-height: 0.8em;\n}\n\n.placeholder-lg {\n  min-height: 1.2em;\n}\n\n.placeholder-glow .placeholder {\n  animation: placeholder-glow 2s ease-in-out infinite;\n}\n\n@keyframes placeholder-glow {\n  50% {\n    opacity: 0.2;\n  }\n}\n.placeholder-wave {\n  -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n  mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n  -webkit-mask-size: 200% 100%;\n  mask-size: 200% 100%;\n  animation: placeholder-wave 2s linear infinite;\n}\n\n@keyframes placeholder-wave {\n  100% {\n    -webkit-mask-position: -200% 0%;\n    mask-position: -200% 0%;\n  }\n}\n.clearfix::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.text-bg-primary {\n  color: #fff !important;\n  background-color: RGBA(13, 110, 253, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-secondary {\n  color: #fff !important;\n  background-color: RGBA(108, 117, 125, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-success {\n  color: #fff !important;\n  background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-info {\n  color: #000 !important;\n  background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-warning {\n  color: #000 !important;\n  background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-danger {\n  color: #fff !important;\n  background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-light {\n  color: #000 !important;\n  background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-dark {\n  color: #fff !important;\n  background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important;\n}\n\n.link-primary {\n  color: #0d6efd !important;\n}\n.link-primary:hover, .link-primary:focus {\n  color: #0a58ca !important;\n}\n\n.link-secondary {\n  color: #6c757d !important;\n}\n.link-secondary:hover, .link-secondary:focus {\n  color: #565e64 !important;\n}\n\n.link-success {\n  color: #198754 !important;\n}\n.link-success:hover, .link-success:focus {\n  color: #146c43 !important;\n}\n\n.link-info {\n  color: #0dcaf0 !important;\n}\n.link-info:hover, .link-info:focus {\n  color: #3dd5f3 !important;\n}\n\n.link-warning {\n  color: #ffc107 !important;\n}\n.link-warning:hover, .link-warning:focus {\n  color: #ffcd39 !important;\n}\n\n.link-danger {\n  color: #dc3545 !important;\n}\n.link-danger:hover, .link-danger:focus {\n  color: #b02a37 !important;\n}\n\n.link-light {\n  color: #f8f9fa !important;\n}\n.link-light:hover, .link-light:focus {\n  color: #f9fafb !important;\n}\n\n.link-dark {\n  color: #212529 !important;\n}\n.link-dark:hover, .link-dark:focus {\n  color: #1a1e21 !important;\n}\n\n.ratio {\n  position: relative;\n  width: 100%;\n}\n.ratio::before {\n  display: block;\n  padding-top: var(--bs-aspect-ratio);\n  content: \"\";\n}\n.ratio > * {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.ratio-1x1 {\n  --bs-aspect-ratio: 100%;\n}\n\n.ratio-4x3 {\n  --bs-aspect-ratio: 75%;\n}\n\n.ratio-16x9 {\n  --bs-aspect-ratio: 56.25%;\n}\n\n.ratio-21x9 {\n  --bs-aspect-ratio: 42.8571428571%;\n}\n\n.fixed-top {\n  position: fixed;\n  top: 0;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n.fixed-bottom {\n  position: fixed;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n.sticky-top {\n  position: -webkit-sticky;\n  position: sticky;\n  top: 0;\n  z-index: 1020;\n}\n\n.sticky-bottom {\n  position: -webkit-sticky;\n  position: sticky;\n  bottom: 0;\n  z-index: 1020;\n}\n\n@media (min-width: 576px) {\n  .sticky-sm-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-sm-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 768px) {\n  .sticky-md-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-md-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 992px) {\n  .sticky-lg-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-lg-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1200px) {\n  .sticky-xl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1400px) {\n  .sticky-xxl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xxl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n.hstack {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  align-self: stretch;\n}\n\n.vstack {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  align-self: stretch;\n}\n\n.visually-hidden,\n.visually-hidden-focusable:not(:focus):not(:focus-within) {\n  position: absolute !important;\n  width: 1px !important;\n  height: 1px !important;\n  padding: 0 !important;\n  margin: -1px !important;\n  overflow: hidden !important;\n  clip: rect(0, 0, 0, 0) !important;\n  white-space: nowrap !important;\n  border: 0 !important;\n}\n\n.stretched-link::after {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1;\n  content: \"\";\n}\n\n.text-truncate {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.vr {\n  display: inline-block;\n  align-self: stretch;\n  width: 1px;\n  min-height: 1em;\n  background-color: currentcolor;\n  opacity: 0.25;\n}\n\n.align-baseline {\n  vertical-align: baseline !important;\n}\n\n.align-top {\n  vertical-align: top !important;\n}\n\n.align-middle {\n  vertical-align: middle !important;\n}\n\n.align-bottom {\n  vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n  vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n  vertical-align: text-top !important;\n}\n\n.float-start {\n  float: left !important;\n}\n\n.float-end {\n  float: right !important;\n}\n\n.float-none {\n  float: none !important;\n}\n\n.opacity-0 {\n  opacity: 0 !important;\n}\n\n.opacity-25 {\n  opacity: 0.25 !important;\n}\n\n.opacity-50 {\n  opacity: 0.5 !important;\n}\n\n.opacity-75 {\n  opacity: 0.75 !important;\n}\n\n.opacity-100 {\n  opacity: 1 !important;\n}\n\n.overflow-auto {\n  overflow: auto !important;\n}\n\n.overflow-hidden {\n  overflow: hidden !important;\n}\n\n.overflow-visible {\n  overflow: visible !important;\n}\n\n.overflow-scroll {\n  overflow: scroll !important;\n}\n\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-grid {\n  display: grid !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: inline-flex !important;\n}\n\n.d-none {\n  display: none !important;\n}\n\n.shadow {\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-sm {\n  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow-lg {\n  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n  box-shadow: none !important;\n}\n\n.position-static {\n  position: static !important;\n}\n\n.position-relative {\n  position: relative !important;\n}\n\n.position-absolute {\n  position: absolute !important;\n}\n\n.position-fixed {\n  position: fixed !important;\n}\n\n.position-sticky {\n  position: -webkit-sticky !important;\n  position: sticky !important;\n}\n\n.top-0 {\n  top: 0 !important;\n}\n\n.top-50 {\n  top: 50% !important;\n}\n\n.top-100 {\n  top: 100% !important;\n}\n\n.bottom-0 {\n  bottom: 0 !important;\n}\n\n.bottom-50 {\n  bottom: 50% !important;\n}\n\n.bottom-100 {\n  bottom: 100% !important;\n}\n\n.start-0 {\n  left: 0 !important;\n}\n\n.start-50 {\n  left: 50% !important;\n}\n\n.start-100 {\n  left: 100% !important;\n}\n\n.end-0 {\n  right: 0 !important;\n}\n\n.end-50 {\n  right: 50% !important;\n}\n\n.end-100 {\n  right: 100% !important;\n}\n\n.translate-middle {\n  transform: translate(-50%, -50%) !important;\n}\n\n.translate-middle-x {\n  transform: translateX(-50%) !important;\n}\n\n.translate-middle-y {\n  transform: translateY(-50%) !important;\n}\n\n.border {\n  border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-0 {\n  border: 0 !important;\n}\n\n.border-top {\n  border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-top-0 {\n  border-top: 0 !important;\n}\n\n.border-end {\n  border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-end-0 {\n  border-right: 0 !important;\n}\n\n.border-bottom {\n  border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-bottom-0 {\n  border-bottom: 0 !important;\n}\n\n.border-start {\n  border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-start-0 {\n  border-left: 0 !important;\n}\n\n.border-primary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-secondary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-success {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-info {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-warning {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-danger {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-light {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-dark {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-white {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-1 {\n  --bs-border-width: 1px;\n}\n\n.border-2 {\n  --bs-border-width: 2px;\n}\n\n.border-3 {\n  --bs-border-width: 3px;\n}\n\n.border-4 {\n  --bs-border-width: 4px;\n}\n\n.border-5 {\n  --bs-border-width: 5px;\n}\n\n.border-opacity-10 {\n  --bs-border-opacity: 0.1;\n}\n\n.border-opacity-25 {\n  --bs-border-opacity: 0.25;\n}\n\n.border-opacity-50 {\n  --bs-border-opacity: 0.5;\n}\n\n.border-opacity-75 {\n  --bs-border-opacity: 0.75;\n}\n\n.border-opacity-100 {\n  --bs-border-opacity: 1;\n}\n\n.w-25 {\n  width: 25% !important;\n}\n\n.w-50 {\n  width: 50% !important;\n}\n\n.w-75 {\n  width: 75% !important;\n}\n\n.w-100 {\n  width: 100% !important;\n}\n\n.w-auto {\n  width: auto !important;\n}\n\n.mw-100 {\n  max-width: 100% !important;\n}\n\n.vw-100 {\n  width: 100vw !important;\n}\n\n.min-vw-100 {\n  min-width: 100vw !important;\n}\n\n.h-25 {\n  height: 25% !important;\n}\n\n.h-50 {\n  height: 50% !important;\n}\n\n.h-75 {\n  height: 75% !important;\n}\n\n.h-100 {\n  height: 100% !important;\n}\n\n.h-auto {\n  height: auto !important;\n}\n\n.mh-100 {\n  max-height: 100% !important;\n}\n\n.vh-100 {\n  height: 100vh !important;\n}\n\n.min-vh-100 {\n  min-height: 100vh !important;\n}\n\n.flex-fill {\n  flex: 1 1 auto !important;\n}\n\n.flex-row {\n  flex-direction: row !important;\n}\n\n.flex-column {\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n  justify-content: space-evenly !important;\n}\n\n.align-items-start {\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  align-content: center !important;\n}\n\n.align-content-between {\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  align-self: auto !important;\n}\n\n.align-self-start {\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  align-self: stretch !important;\n}\n\n.order-first {\n  order: -1 !important;\n}\n\n.order-0 {\n  order: 0 !important;\n}\n\n.order-1 {\n  order: 1 !important;\n}\n\n.order-2 {\n  order: 2 !important;\n}\n\n.order-3 {\n  order: 3 !important;\n}\n\n.order-4 {\n  order: 4 !important;\n}\n\n.order-5 {\n  order: 5 !important;\n}\n\n.order-last {\n  order: 6 !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mx-0 {\n  margin-right: 0 !important;\n  margin-left: 0 !important;\n}\n\n.mx-1 {\n  margin-right: 0.25rem !important;\n  margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n  margin-right: 0.5rem !important;\n  margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n  margin-right: 1rem !important;\n  margin-left: 1rem !important;\n}\n\n.mx-4 {\n  margin-right: 1.5rem !important;\n  margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n  margin-right: 3rem !important;\n  margin-left: 3rem !important;\n}\n\n.mx-auto {\n  margin-right: auto !important;\n  margin-left: auto !important;\n}\n\n.my-0 {\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\n\n.my-1 {\n  margin-top: 0.25rem !important;\n  margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n  margin-top: 0.5rem !important;\n  margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n  margin-top: 1rem !important;\n  margin-bottom: 1rem !important;\n}\n\n.my-4 {\n  margin-top: 1.5rem !important;\n  margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n  margin-top: 3rem !important;\n  margin-bottom: 3rem !important;\n}\n\n.my-auto {\n  margin-top: auto !important;\n  margin-bottom: auto !important;\n}\n\n.mt-0 {\n  margin-top: 0 !important;\n}\n\n.mt-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n  margin-top: 1rem !important;\n}\n\n.mt-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n  margin-top: 3rem !important;\n}\n\n.mt-auto {\n  margin-top: auto !important;\n}\n\n.me-0 {\n  margin-right: 0 !important;\n}\n\n.me-1 {\n  margin-right: 0.25rem !important;\n}\n\n.me-2 {\n  margin-right: 0.5rem !important;\n}\n\n.me-3 {\n  margin-right: 1rem !important;\n}\n\n.me-4 {\n  margin-right: 1.5rem !important;\n}\n\n.me-5 {\n  margin-right: 3rem !important;\n}\n\n.me-auto {\n  margin-right: auto !important;\n}\n\n.mb-0 {\n  margin-bottom: 0 !important;\n}\n\n.mb-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n  margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n  margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n  margin-bottom: auto !important;\n}\n\n.ms-0 {\n  margin-left: 0 !important;\n}\n\n.ms-1 {\n  margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n  margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n  margin-left: 1rem !important;\n}\n\n.ms-4 {\n  margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n  margin-left: 3rem !important;\n}\n\n.ms-auto {\n  margin-left: auto !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.px-0 {\n  padding-right: 0 !important;\n  padding-left: 0 !important;\n}\n\n.px-1 {\n  padding-right: 0.25rem !important;\n  padding-left: 0.25rem !important;\n}\n\n.px-2 {\n  padding-right: 0.5rem !important;\n  padding-left: 0.5rem !important;\n}\n\n.px-3 {\n  padding-right: 1rem !important;\n  padding-left: 1rem !important;\n}\n\n.px-4 {\n  padding-right: 1.5rem !important;\n  padding-left: 1.5rem !important;\n}\n\n.px-5 {\n  padding-right: 3rem !important;\n  padding-left: 3rem !important;\n}\n\n.py-0 {\n  padding-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\n.py-1 {\n  padding-top: 0.25rem !important;\n  padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n  padding-top: 0.5rem !important;\n  padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n  padding-top: 1rem !important;\n  padding-bottom: 1rem !important;\n}\n\n.py-4 {\n  padding-top: 1.5rem !important;\n  padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n  padding-top: 3rem !important;\n  padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n  padding-top: 0 !important;\n}\n\n.pt-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n  padding-top: 1rem !important;\n}\n\n.pt-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n  padding-top: 3rem !important;\n}\n\n.pe-0 {\n  padding-right: 0 !important;\n}\n\n.pe-1 {\n  padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n  padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n  padding-right: 1rem !important;\n}\n\n.pe-4 {\n  padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n  padding-right: 3rem !important;\n}\n\n.pb-0 {\n  padding-bottom: 0 !important;\n}\n\n.pb-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n  padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n  padding-left: 0 !important;\n}\n\n.ps-1 {\n  padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n  padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n  padding-left: 1rem !important;\n}\n\n.ps-4 {\n  padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n  padding-left: 3rem !important;\n}\n\n.gap-0 {\n  gap: 0 !important;\n}\n\n.gap-1 {\n  gap: 0.25rem !important;\n}\n\n.gap-2 {\n  gap: 0.5rem !important;\n}\n\n.gap-3 {\n  gap: 1rem !important;\n}\n\n.gap-4 {\n  gap: 1.5rem !important;\n}\n\n.gap-5 {\n  gap: 3rem !important;\n}\n\n.font-monospace {\n  font-family: var(--bs-font-monospace) !important;\n}\n\n.fs-1 {\n  font-size: calc(1.375rem + 1.5vw) !important;\n}\n\n.fs-2 {\n  font-size: calc(1.325rem + 0.9vw) !important;\n}\n\n.fs-3 {\n  font-size: calc(1.3rem + 0.6vw) !important;\n}\n\n.fs-4 {\n  font-size: calc(1.275rem + 0.3vw) !important;\n}\n\n.fs-5 {\n  font-size: 1.25rem !important;\n}\n\n.fs-6 {\n  font-size: 1rem !important;\n}\n\n.fst-italic {\n  font-style: italic !important;\n}\n\n.fst-normal {\n  font-style: normal !important;\n}\n\n.fw-light {\n  font-weight: 300 !important;\n}\n\n.fw-lighter {\n  font-weight: lighter !important;\n}\n\n.fw-normal {\n  font-weight: 400 !important;\n}\n\n.fw-bold {\n  font-weight: 700 !important;\n}\n\n.fw-semibold {\n  font-weight: 600 !important;\n}\n\n.fw-bolder {\n  font-weight: bolder !important;\n}\n\n.lh-1 {\n  line-height: 1 !important;\n}\n\n.lh-sm {\n  line-height: 1.25 !important;\n}\n\n.lh-base {\n  line-height: 1.5 !important;\n}\n\n.lh-lg {\n  line-height: 2 !important;\n}\n\n.text-start {\n  text-align: left !important;\n}\n\n.text-end {\n  text-align: right !important;\n}\n\n.text-center {\n  text-align: center !important;\n}\n\n.text-decoration-none {\n  text-decoration: none !important;\n}\n\n.text-decoration-underline {\n  text-decoration: underline !important;\n}\n\n.text-decoration-line-through {\n  text-decoration: line-through !important;\n}\n\n.text-lowercase {\n  text-transform: lowercase !important;\n}\n\n.text-uppercase {\n  text-transform: uppercase !important;\n}\n\n.text-capitalize {\n  text-transform: capitalize !important;\n}\n\n.text-wrap {\n  white-space: normal !important;\n}\n\n.text-nowrap {\n  white-space: nowrap !important;\n}\n\n/* rtl:begin:remove */\n.text-break {\n  word-wrap: break-word !important;\n  word-break: break-word !important;\n}\n\n/* rtl:end:remove */\n.text-primary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-secondary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-success {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-info {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-warning {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-danger {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-light {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-dark {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-black {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-white {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-body {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-muted {\n  --bs-text-opacity: 1;\n  color: #6c757d !important;\n}\n\n.text-black-50 {\n  --bs-text-opacity: 1;\n  color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n  --bs-text-opacity: 1;\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-reset {\n  --bs-text-opacity: 1;\n  color: inherit !important;\n}\n\n.text-opacity-25 {\n  --bs-text-opacity: 0.25;\n}\n\n.text-opacity-50 {\n  --bs-text-opacity: 0.5;\n}\n\n.text-opacity-75 {\n  --bs-text-opacity: 0.75;\n}\n\n.text-opacity-100 {\n  --bs-text-opacity: 1;\n}\n\n.bg-primary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-secondary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-success {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-info {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-warning {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-danger {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-light {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-dark {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-black {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-white {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-body {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-transparent {\n  --bs-bg-opacity: 1;\n  background-color: transparent !important;\n}\n\n.bg-opacity-10 {\n  --bs-bg-opacity: 0.1;\n}\n\n.bg-opacity-25 {\n  --bs-bg-opacity: 0.25;\n}\n\n.bg-opacity-50 {\n  --bs-bg-opacity: 0.5;\n}\n\n.bg-opacity-75 {\n  --bs-bg-opacity: 0.75;\n}\n\n.bg-opacity-100 {\n  --bs-bg-opacity: 1;\n}\n\n.bg-gradient {\n  background-image: var(--bs-gradient) !important;\n}\n\n.user-select-all {\n  -webkit-user-select: all !important;\n  -moz-user-select: all !important;\n  user-select: all !important;\n}\n\n.user-select-auto {\n  -webkit-user-select: auto !important;\n  -moz-user-select: auto !important;\n  user-select: auto !important;\n}\n\n.user-select-none {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  user-select: none !important;\n}\n\n.pe-none {\n  pointer-events: none !important;\n}\n\n.pe-auto {\n  pointer-events: auto !important;\n}\n\n.rounded {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-0 {\n  border-radius: 0 !important;\n}\n\n.rounded-1 {\n  border-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-2 {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-3 {\n  border-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-4 {\n  border-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-5 {\n  border-radius: var(--bs-border-radius-2xl) !important;\n}\n\n.rounded-circle {\n  border-radius: 50% !important;\n}\n\n.rounded-pill {\n  border-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-top {\n  border-top-left-radius: var(--bs-border-radius) !important;\n  border-top-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-end {\n  border-top-right-radius: var(--bs-border-radius) !important;\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-bottom {\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-start {\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n  border-top-left-radius: var(--bs-border-radius) !important;\n}\n\n.visible {\n  visibility: visible !important;\n}\n\n.invisible {\n  visibility: hidden !important;\n}\n\n@media (min-width: 576px) {\n  .float-sm-start {\n    float: left !important;\n  }\n  .float-sm-end {\n    float: right !important;\n  }\n  .float-sm-none {\n    float: none !important;\n  }\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-grid {\n    display: grid !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-sm-none {\n    display: none !important;\n  }\n  .flex-sm-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-row {\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-sm-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-sm-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-sm-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-sm-start {\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    align-self: stretch !important;\n  }\n  .order-sm-first {\n    order: -1 !important;\n  }\n  .order-sm-0 {\n    order: 0 !important;\n  }\n  .order-sm-1 {\n    order: 1 !important;\n  }\n  .order-sm-2 {\n    order: 2 !important;\n  }\n  .order-sm-3 {\n    order: 3 !important;\n  }\n  .order-sm-4 {\n    order: 4 !important;\n  }\n  .order-sm-5 {\n    order: 5 !important;\n  }\n  .order-sm-last {\n    order: 6 !important;\n  }\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mx-sm-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-sm-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-sm-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-sm-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-sm-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-sm-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-sm-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-sm-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-sm-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-sm-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-sm-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mt-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-sm-auto {\n    margin-top: auto !important;\n  }\n  .me-sm-0 {\n    margin-right: 0 !important;\n  }\n  .me-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .me-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .me-sm-auto {\n    margin-right: auto !important;\n  }\n  .mb-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-sm-0 {\n    margin-left: 0 !important;\n  }\n  .ms-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-sm-auto {\n    margin-left: auto !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .px-sm-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-sm-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-sm-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-sm-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-sm-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-sm-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-sm-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-sm-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-sm-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pt-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-sm-0 {\n    padding-right: 0 !important;\n  }\n  .pe-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-sm-0 {\n    padding-left: 0 !important;\n  }\n  .ps-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-sm-0 {\n    gap: 0 !important;\n  }\n  .gap-sm-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-sm-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-sm-3 {\n    gap: 1rem !important;\n  }\n  .gap-sm-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-sm-5 {\n    gap: 3rem !important;\n  }\n  .text-sm-start {\n    text-align: left !important;\n  }\n  .text-sm-end {\n    text-align: right !important;\n  }\n  .text-sm-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 768px) {\n  .float-md-start {\n    float: left !important;\n  }\n  .float-md-end {\n    float: right !important;\n  }\n  .float-md-none {\n    float: none !important;\n  }\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-grid {\n    display: grid !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-md-none {\n    display: none !important;\n  }\n  .flex-md-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-md-row {\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-md-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-md-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-md-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-md-start {\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    align-self: stretch !important;\n  }\n  .order-md-first {\n    order: -1 !important;\n  }\n  .order-md-0 {\n    order: 0 !important;\n  }\n  .order-md-1 {\n    order: 1 !important;\n  }\n  .order-md-2 {\n    order: 2 !important;\n  }\n  .order-md-3 {\n    order: 3 !important;\n  }\n  .order-md-4 {\n    order: 4 !important;\n  }\n  .order-md-5 {\n    order: 5 !important;\n  }\n  .order-md-last {\n    order: 6 !important;\n  }\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mx-md-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-md-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-md-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-md-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-md-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-md-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-md-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-md-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-md-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-md-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-md-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-md-0 {\n    margin-top: 0 !important;\n  }\n  .mt-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-md-auto {\n    margin-top: auto !important;\n  }\n  .me-md-0 {\n    margin-right: 0 !important;\n  }\n  .me-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-md-3 {\n    margin-right: 1rem !important;\n  }\n  .me-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-md-5 {\n    margin-right: 3rem !important;\n  }\n  .me-md-auto {\n    margin-right: auto !important;\n  }\n  .mb-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-md-0 {\n    margin-left: 0 !important;\n  }\n  .ms-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-md-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-md-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-md-auto {\n    margin-left: auto !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .px-md-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-md-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-md-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-md-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-md-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-md-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-md-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-md-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-md-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-md-0 {\n    padding-top: 0 !important;\n  }\n  .pt-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-md-0 {\n    padding-right: 0 !important;\n  }\n  .pe-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-md-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-md-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-md-0 {\n    padding-left: 0 !important;\n  }\n  .ps-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-md-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-md-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-md-0 {\n    gap: 0 !important;\n  }\n  .gap-md-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-md-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-md-3 {\n    gap: 1rem !important;\n  }\n  .gap-md-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-md-5 {\n    gap: 3rem !important;\n  }\n  .text-md-start {\n    text-align: left !important;\n  }\n  .text-md-end {\n    text-align: right !important;\n  }\n  .text-md-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 992px) {\n  .float-lg-start {\n    float: left !important;\n  }\n  .float-lg-end {\n    float: right !important;\n  }\n  .float-lg-none {\n    float: none !important;\n  }\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-grid {\n    display: grid !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-lg-none {\n    display: none !important;\n  }\n  .flex-lg-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-row {\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-lg-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-lg-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-lg-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-lg-start {\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    align-self: stretch !important;\n  }\n  .order-lg-first {\n    order: -1 !important;\n  }\n  .order-lg-0 {\n    order: 0 !important;\n  }\n  .order-lg-1 {\n    order: 1 !important;\n  }\n  .order-lg-2 {\n    order: 2 !important;\n  }\n  .order-lg-3 {\n    order: 3 !important;\n  }\n  .order-lg-4 {\n    order: 4 !important;\n  }\n  .order-lg-5 {\n    order: 5 !important;\n  }\n  .order-lg-last {\n    order: 6 !important;\n  }\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mx-lg-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-lg-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-lg-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-lg-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-lg-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-lg-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-lg-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-lg-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-lg-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-lg-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-lg-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mt-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-lg-auto {\n    margin-top: auto !important;\n  }\n  .me-lg-0 {\n    margin-right: 0 !important;\n  }\n  .me-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .me-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .me-lg-auto {\n    margin-right: auto !important;\n  }\n  .mb-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-lg-0 {\n    margin-left: 0 !important;\n  }\n  .ms-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-lg-auto {\n    margin-left: auto !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .px-lg-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-lg-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-lg-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-lg-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-lg-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-lg-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-lg-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-lg-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-lg-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pt-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-lg-0 {\n    padding-right: 0 !important;\n  }\n  .pe-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-lg-0 {\n    padding-left: 0 !important;\n  }\n  .ps-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-lg-0 {\n    gap: 0 !important;\n  }\n  .gap-lg-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-lg-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-lg-3 {\n    gap: 1rem !important;\n  }\n  .gap-lg-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-lg-5 {\n    gap: 3rem !important;\n  }\n  .text-lg-start {\n    text-align: left !important;\n  }\n  .text-lg-end {\n    text-align: right !important;\n  }\n  .text-lg-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .float-xl-start {\n    float: left !important;\n  }\n  .float-xl-end {\n    float: right !important;\n  }\n  .float-xl-none {\n    float: none !important;\n  }\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-grid {\n    display: grid !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xl-none {\n    display: none !important;\n  }\n  .flex-xl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-row {\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xl-first {\n    order: -1 !important;\n  }\n  .order-xl-0 {\n    order: 0 !important;\n  }\n  .order-xl-1 {\n    order: 1 !important;\n  }\n  .order-xl-2 {\n    order: 2 !important;\n  }\n  .order-xl-3 {\n    order: 3 !important;\n  }\n  .order-xl-4 {\n    order: 4 !important;\n  }\n  .order-xl-5 {\n    order: 5 !important;\n  }\n  .order-xl-last {\n    order: 6 !important;\n  }\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mx-xl-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-xl-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-xl-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-xl-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-xl-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-xl-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-xl-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-xl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xl-auto {\n    margin-top: auto !important;\n  }\n  .me-xl-0 {\n    margin-right: 0 !important;\n  }\n  .me-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .me-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .me-xl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xl-0 {\n    margin-left: 0 !important;\n  }\n  .ms-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-xl-auto {\n    margin-left: auto !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .px-xl-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-xl-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-xl-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-xl-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-xl-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-xl-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-xl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xl-0 {\n    padding-right: 0 !important;\n  }\n  .pe-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xl-0 {\n    padding-left: 0 !important;\n  }\n  .ps-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-xl-0 {\n    gap: 0 !important;\n  }\n  .gap-xl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xl-5 {\n    gap: 3rem !important;\n  }\n  .text-xl-start {\n    text-align: left !important;\n  }\n  .text-xl-end {\n    text-align: right !important;\n  }\n  .text-xl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1400px) {\n  .float-xxl-start {\n    float: left !important;\n  }\n  .float-xxl-end {\n    float: right !important;\n  }\n  .float-xxl-none {\n    float: none !important;\n  }\n  .d-xxl-inline {\n    display: inline !important;\n  }\n  .d-xxl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xxl-block {\n    display: block !important;\n  }\n  .d-xxl-grid {\n    display: grid !important;\n  }\n  .d-xxl-table {\n    display: table !important;\n  }\n  .d-xxl-table-row {\n    display: table-row !important;\n  }\n  .d-xxl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xxl-flex {\n    display: flex !important;\n  }\n  .d-xxl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xxl-none {\n    display: none !important;\n  }\n  .flex-xxl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xxl-row {\n    flex-direction: row !important;\n  }\n  .flex-xxl-column {\n    flex-direction: column !important;\n  }\n  .flex-xxl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xxl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xxl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xxl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xxl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xxl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xxl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xxl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xxl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xxl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xxl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xxl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xxl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xxl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xxl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xxl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xxl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xxl-center {\n    align-items: center !important;\n  }\n  .align-items-xxl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xxl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xxl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xxl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xxl-center {\n    align-content: center !important;\n  }\n  .align-content-xxl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xxl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xxl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xxl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xxl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xxl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xxl-center {\n    align-self: center !important;\n  }\n  .align-self-xxl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xxl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xxl-first {\n    order: -1 !important;\n  }\n  .order-xxl-0 {\n    order: 0 !important;\n  }\n  .order-xxl-1 {\n    order: 1 !important;\n  }\n  .order-xxl-2 {\n    order: 2 !important;\n  }\n  .order-xxl-3 {\n    order: 3 !important;\n  }\n  .order-xxl-4 {\n    order: 4 !important;\n  }\n  .order-xxl-5 {\n    order: 5 !important;\n  }\n  .order-xxl-last {\n    order: 6 !important;\n  }\n  .m-xxl-0 {\n    margin: 0 !important;\n  }\n  .m-xxl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xxl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xxl-3 {\n    margin: 1rem !important;\n  }\n  .m-xxl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xxl-5 {\n    margin: 3rem !important;\n  }\n  .m-xxl-auto {\n    margin: auto !important;\n  }\n  .mx-xxl-0 {\n    margin-right: 0 !important;\n    margin-left: 0 !important;\n  }\n  .mx-xxl-1 {\n    margin-right: 0.25rem !important;\n    margin-left: 0.25rem !important;\n  }\n  .mx-xxl-2 {\n    margin-right: 0.5rem !important;\n    margin-left: 0.5rem !important;\n  }\n  .mx-xxl-3 {\n    margin-right: 1rem !important;\n    margin-left: 1rem !important;\n  }\n  .mx-xxl-4 {\n    margin-right: 1.5rem !important;\n    margin-left: 1.5rem !important;\n  }\n  .mx-xxl-5 {\n    margin-right: 3rem !important;\n    margin-left: 3rem !important;\n  }\n  .mx-xxl-auto {\n    margin-right: auto !important;\n    margin-left: auto !important;\n  }\n  .my-xxl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xxl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xxl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xxl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xxl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xxl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xxl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xxl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xxl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xxl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xxl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xxl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xxl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xxl-auto {\n    margin-top: auto !important;\n  }\n  .me-xxl-0 {\n    margin-right: 0 !important;\n  }\n  .me-xxl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .me-xxl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .me-xxl-3 {\n    margin-right: 1rem !important;\n  }\n  .me-xxl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .me-xxl-5 {\n    margin-right: 3rem !important;\n  }\n  .me-xxl-auto {\n    margin-right: auto !important;\n  }\n  .mb-xxl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xxl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xxl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xxl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xxl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xxl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xxl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xxl-0 {\n    margin-left: 0 !important;\n  }\n  .ms-xxl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .ms-xxl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .ms-xxl-3 {\n    margin-left: 1rem !important;\n  }\n  .ms-xxl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .ms-xxl-5 {\n    margin-left: 3rem !important;\n  }\n  .ms-xxl-auto {\n    margin-left: auto !important;\n  }\n  .p-xxl-0 {\n    padding: 0 !important;\n  }\n  .p-xxl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xxl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xxl-3 {\n    padding: 1rem !important;\n  }\n  .p-xxl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xxl-5 {\n    padding: 3rem !important;\n  }\n  .px-xxl-0 {\n    padding-right: 0 !important;\n    padding-left: 0 !important;\n  }\n  .px-xxl-1 {\n    padding-right: 0.25rem !important;\n    padding-left: 0.25rem !important;\n  }\n  .px-xxl-2 {\n    padding-right: 0.5rem !important;\n    padding-left: 0.5rem !important;\n  }\n  .px-xxl-3 {\n    padding-right: 1rem !important;\n    padding-left: 1rem !important;\n  }\n  .px-xxl-4 {\n    padding-right: 1.5rem !important;\n    padding-left: 1.5rem !important;\n  }\n  .px-xxl-5 {\n    padding-right: 3rem !important;\n    padding-left: 3rem !important;\n  }\n  .py-xxl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xxl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xxl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xxl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xxl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xxl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xxl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xxl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xxl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xxl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xxl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xxl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xxl-0 {\n    padding-right: 0 !important;\n  }\n  .pe-xxl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .pe-xxl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .pe-xxl-3 {\n    padding-right: 1rem !important;\n  }\n  .pe-xxl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .pe-xxl-5 {\n    padding-right: 3rem !important;\n  }\n  .pb-xxl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xxl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xxl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xxl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xxl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xxl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xxl-0 {\n    padding-left: 0 !important;\n  }\n  .ps-xxl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .ps-xxl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .ps-xxl-3 {\n    padding-left: 1rem !important;\n  }\n  .ps-xxl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .ps-xxl-5 {\n    padding-left: 3rem !important;\n  }\n  .gap-xxl-0 {\n    gap: 0 !important;\n  }\n  .gap-xxl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xxl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xxl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xxl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xxl-5 {\n    gap: 3rem !important;\n  }\n  .text-xxl-start {\n    text-align: left !important;\n  }\n  .text-xxl-end {\n    text-align: right !important;\n  }\n  .text-xxl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .fs-1 {\n    font-size: 2.5rem !important;\n  }\n  .fs-2 {\n    font-size: 2rem !important;\n  }\n  .fs-3 {\n    font-size: 1.75rem !important;\n  }\n  .fs-4 {\n    font-size: 1.5rem !important;\n  }\n}\n@media print {\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-grid {\n    display: grid !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-print-none {\n    display: none !important;\n  }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/css/bootstrap.rtl.css",
    "content": "@charset \"UTF-8\";\n/*!\n * Bootstrap  v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n  --bs-blue: #0d6efd;\n  --bs-indigo: #6610f2;\n  --bs-purple: #6f42c1;\n  --bs-pink: #d63384;\n  --bs-red: #dc3545;\n  --bs-orange: #fd7e14;\n  --bs-yellow: #ffc107;\n  --bs-green: #198754;\n  --bs-teal: #20c997;\n  --bs-cyan: #0dcaf0;\n  --bs-black: #000;\n  --bs-white: #fff;\n  --bs-gray: #6c757d;\n  --bs-gray-dark: #343a40;\n  --bs-gray-100: #f8f9fa;\n  --bs-gray-200: #e9ecef;\n  --bs-gray-300: #dee2e6;\n  --bs-gray-400: #ced4da;\n  --bs-gray-500: #adb5bd;\n  --bs-gray-600: #6c757d;\n  --bs-gray-700: #495057;\n  --bs-gray-800: #343a40;\n  --bs-gray-900: #212529;\n  --bs-primary: #0d6efd;\n  --bs-secondary: #6c757d;\n  --bs-success: #198754;\n  --bs-info: #0dcaf0;\n  --bs-warning: #ffc107;\n  --bs-danger: #dc3545;\n  --bs-light: #f8f9fa;\n  --bs-dark: #212529;\n  --bs-primary-rgb: 13, 110, 253;\n  --bs-secondary-rgb: 108, 117, 125;\n  --bs-success-rgb: 25, 135, 84;\n  --bs-info-rgb: 13, 202, 240;\n  --bs-warning-rgb: 255, 193, 7;\n  --bs-danger-rgb: 220, 53, 69;\n  --bs-light-rgb: 248, 249, 250;\n  --bs-dark-rgb: 33, 37, 41;\n  --bs-white-rgb: 255, 255, 255;\n  --bs-black-rgb: 0, 0, 0;\n  --bs-body-color-rgb: 33, 37, 41;\n  --bs-body-bg-rgb: 255, 255, 255;\n  --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n  --bs-body-font-family: var(--bs-font-sans-serif);\n  --bs-body-font-size: 1rem;\n  --bs-body-font-weight: 400;\n  --bs-body-line-height: 1.5;\n  --bs-body-color: #212529;\n  --bs-body-bg: #fff;\n  --bs-border-width: 1px;\n  --bs-border-style: solid;\n  --bs-border-color: #dee2e6;\n  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n  --bs-border-radius: 0.375rem;\n  --bs-border-radius-sm: 0.25rem;\n  --bs-border-radius-lg: 0.5rem;\n  --bs-border-radius-xl: 1rem;\n  --bs-border-radius-2xl: 2rem;\n  --bs-border-radius-pill: 50rem;\n  --bs-link-color: #0d6efd;\n  --bs-link-hover-color: #0a58ca;\n  --bs-code-color: #d63384;\n  --bs-highlight-bg: #fff3cd;\n}\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  :root {\n    scroll-behavior: smooth;\n  }\n}\n\nbody {\n  margin: 0;\n  font-family: var(--bs-body-font-family);\n  font-size: var(--bs-body-font-size);\n  font-weight: var(--bs-body-font-weight);\n  line-height: var(--bs-body-line-height);\n  color: var(--bs-body-color);\n  text-align: var(--bs-body-text-align);\n  background-color: var(--bs-body-bg);\n  -webkit-text-size-adjust: 100%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nhr {\n  margin: 1rem 0;\n  color: inherit;\n  border: 0;\n  border-top: 1px solid;\n  opacity: 0.25;\n}\n\nh6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n  font-weight: 500;\n  line-height: 1.2;\n}\n\nh1, .h1 {\n  font-size: calc(1.375rem + 1.5vw);\n}\n@media (min-width: 1200px) {\n  h1, .h1 {\n    font-size: 2.5rem;\n  }\n}\n\nh2, .h2 {\n  font-size: calc(1.325rem + 0.9vw);\n}\n@media (min-width: 1200px) {\n  h2, .h2 {\n    font-size: 2rem;\n  }\n}\n\nh3, .h3 {\n  font-size: calc(1.3rem + 0.6vw);\n}\n@media (min-width: 1200px) {\n  h3, .h3 {\n    font-size: 1.75rem;\n  }\n}\n\nh4, .h4 {\n  font-size: calc(1.275rem + 0.3vw);\n}\n@media (min-width: 1200px) {\n  h4, .h4 {\n    font-size: 1.5rem;\n  }\n}\n\nh5, .h5 {\n  font-size: 1.25rem;\n}\n\nh6, .h6 {\n  font-size: 1rem;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nabbr[title] {\n  -webkit-text-decoration: underline dotted;\n  text-decoration: underline dotted;\n  cursor: help;\n  -webkit-text-decoration-skip-ink: none;\n  text-decoration-skip-ink: none;\n}\n\naddress {\n  margin-bottom: 1rem;\n  font-style: normal;\n  line-height: inherit;\n}\n\nol,\nul {\n  padding-right: 2rem;\n}\n\nol,\nul,\ndl {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n  margin-bottom: 0;\n}\n\ndt {\n  font-weight: 700;\n}\n\ndd {\n  margin-bottom: 0.5rem;\n  margin-right: 0;\n}\n\nblockquote {\n  margin: 0 0 1rem;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nsmall, .small {\n  font-size: 0.875em;\n}\n\nmark, .mark {\n  padding: 0.1875em;\n  background-color: var(--bs-highlight-bg);\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 0.75em;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nsup {\n  top: -0.5em;\n}\n\na {\n  color: var(--bs-link-color);\n  text-decoration: underline;\n}\na:hover {\n  color: var(--bs-link-hover-color);\n}\n\na:not([href]):not([class]), a:not([href]):not([class]):hover {\n  color: inherit;\n  text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: var(--bs-font-monospace);\n  font-size: 1em;\n}\n\npre {\n  display: block;\n  margin-top: 0;\n  margin-bottom: 1rem;\n  overflow: auto;\n  font-size: 0.875em;\n}\npre code {\n  font-size: inherit;\n  color: inherit;\n  word-break: normal;\n}\n\ncode {\n  font-size: 0.875em;\n  color: var(--bs-code-color);\n  word-wrap: break-word;\n}\na > code {\n  color: inherit;\n}\n\nkbd {\n  padding: 0.1875rem 0.375rem;\n  font-size: 0.875em;\n  color: var(--bs-body-bg);\n  background-color: var(--bs-body-color);\n  border-radius: 0.25rem;\n}\nkbd kbd {\n  padding: 0;\n  font-size: 1em;\n}\n\nfigure {\n  margin: 0 0 1rem;\n}\n\nimg,\nsvg {\n  vertical-align: middle;\n}\n\ntable {\n  caption-side: bottom;\n  border-collapse: collapse;\n}\n\ncaption {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  color: #6c757d;\n  text-align: right;\n}\n\nth {\n  text-align: inherit;\n  text-align: -webkit-match-parent;\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n}\n\nlabel {\n  display: inline-block;\n}\n\nbutton {\n  border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n  outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n[role=button] {\n  cursor: pointer;\n}\n\nselect {\n  word-wrap: normal;\n}\nselect:disabled {\n  opacity: 1;\n}\n\n[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {\n  display: none !important;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n  -webkit-appearance: button;\n}\nbutton:not(:disabled),\n[type=button]:not(:disabled),\n[type=reset]:not(:disabled),\n[type=submit]:not(:disabled) {\n  cursor: pointer;\n}\n\n::-moz-focus-inner {\n  padding: 0;\n  border-style: none;\n}\n\ntextarea {\n  resize: vertical;\n}\n\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  float: right;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 0.5rem;\n  font-size: calc(1.275rem + 0.3vw);\n  line-height: inherit;\n}\n@media (min-width: 1200px) {\n  legend {\n    font-size: 1.5rem;\n  }\n}\nlegend + * {\n  clear: right;\n}\n\n::-webkit-datetime-edit-fields-wrapper,\n::-webkit-datetime-edit-text,\n::-webkit-datetime-edit-minute,\n::-webkit-datetime-edit-hour-field,\n::-webkit-datetime-edit-day-field,\n::-webkit-datetime-edit-month-field,\n::-webkit-datetime-edit-year-field {\n  padding: 0;\n}\n\n::-webkit-inner-spin-button {\n  height: auto;\n}\n\n[type=search] {\n  outline-offset: -2px;\n  -webkit-appearance: textfield;\n}\n\n[type=\"tel\"],\n[type=\"url\"],\n[type=\"email\"],\n[type=\"number\"] {\n  direction: ltr;\n}\n::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n::-webkit-color-swatch-wrapper {\n  padding: 0;\n}\n\n::-webkit-file-upload-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\n::file-selector-button {\n  font: inherit;\n  -webkit-appearance: button;\n}\n\noutput {\n  display: inline-block;\n}\n\niframe {\n  border: 0;\n}\n\nsummary {\n  display: list-item;\n  cursor: pointer;\n}\n\nprogress {\n  vertical-align: baseline;\n}\n\n[hidden] {\n  display: none !important;\n}\n\n.lead {\n  font-size: 1.25rem;\n  font-weight: 300;\n}\n\n.display-1 {\n  font-size: calc(1.625rem + 4.5vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-1 {\n    font-size: 5rem;\n  }\n}\n\n.display-2 {\n  font-size: calc(1.575rem + 3.9vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-2 {\n    font-size: 4.5rem;\n  }\n}\n\n.display-3 {\n  font-size: calc(1.525rem + 3.3vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-3 {\n    font-size: 4rem;\n  }\n}\n\n.display-4 {\n  font-size: calc(1.475rem + 2.7vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-4 {\n    font-size: 3.5rem;\n  }\n}\n\n.display-5 {\n  font-size: calc(1.425rem + 2.1vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-5 {\n    font-size: 3rem;\n  }\n}\n\n.display-6 {\n  font-size: calc(1.375rem + 1.5vw);\n  font-weight: 300;\n  line-height: 1.2;\n}\n@media (min-width: 1200px) {\n  .display-6 {\n    font-size: 2.5rem;\n  }\n}\n\n.list-unstyled {\n  padding-right: 0;\n  list-style: none;\n}\n\n.list-inline {\n  padding-right: 0;\n  list-style: none;\n}\n\n.list-inline-item {\n  display: inline-block;\n}\n.list-inline-item:not(:last-child) {\n  margin-left: 0.5rem;\n}\n\n.initialism {\n  font-size: 0.875em;\n  text-transform: uppercase;\n}\n\n.blockquote {\n  margin-bottom: 1rem;\n  font-size: 1.25rem;\n}\n.blockquote > :last-child {\n  margin-bottom: 0;\n}\n\n.blockquote-footer {\n  margin-top: -1rem;\n  margin-bottom: 1rem;\n  font-size: 0.875em;\n  color: #6c757d;\n}\n.blockquote-footer::before {\n  content: \"— \";\n}\n\n.img-fluid {\n  max-width: 100%;\n  height: auto;\n}\n\n.img-thumbnail {\n  padding: 0.25rem;\n  background-color: #fff;\n  border: 1px solid var(--bs-border-color);\n  border-radius: 0.375rem;\n  max-width: 100%;\n  height: auto;\n}\n\n.figure {\n  display: inline-block;\n}\n\n.figure-img {\n  margin-bottom: 0.5rem;\n  line-height: 1;\n}\n\n.figure-caption {\n  font-size: 0.875em;\n  color: #6c757d;\n}\n\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  width: 100%;\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  margin-left: auto;\n  margin-right: auto;\n}\n\n@media (min-width: 576px) {\n  .container-sm, .container {\n    max-width: 540px;\n  }\n}\n@media (min-width: 768px) {\n  .container-md, .container-sm, .container {\n    max-width: 720px;\n  }\n}\n@media (min-width: 992px) {\n  .container-lg, .container-md, .container-sm, .container {\n    max-width: 960px;\n  }\n}\n@media (min-width: 1200px) {\n  .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1140px;\n  }\n}\n@media (min-width: 1400px) {\n  .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n    max-width: 1320px;\n  }\n}\n.row {\n  --bs-gutter-x: 1.5rem;\n  --bs-gutter-y: 0;\n  display: flex;\n  flex-wrap: wrap;\n  margin-top: calc(-1 * var(--bs-gutter-y));\n  margin-left: calc(-0.5 * var(--bs-gutter-x));\n  margin-right: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n  flex-shrink: 0;\n  width: 100%;\n  max-width: 100%;\n  padding-left: calc(var(--bs-gutter-x) * 0.5);\n  padding-right: calc(var(--bs-gutter-x) * 0.5);\n  margin-top: var(--bs-gutter-y);\n}\n\n.col {\n  flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.row-cols-1 > * {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.row-cols-2 > * {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.row-cols-3 > * {\n  flex: 0 0 auto;\n  width: 33.3333333333%;\n}\n\n.row-cols-4 > * {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.row-cols-5 > * {\n  flex: 0 0 auto;\n  width: 20%;\n}\n\n.row-cols-6 > * {\n  flex: 0 0 auto;\n  width: 16.6666666667%;\n}\n\n.col-auto {\n  flex: 0 0 auto;\n  width: auto;\n}\n\n.col-1 {\n  flex: 0 0 auto;\n  width: 8.33333333%;\n}\n\n.col-2 {\n  flex: 0 0 auto;\n  width: 16.66666667%;\n}\n\n.col-3 {\n  flex: 0 0 auto;\n  width: 25%;\n}\n\n.col-4 {\n  flex: 0 0 auto;\n  width: 33.33333333%;\n}\n\n.col-5 {\n  flex: 0 0 auto;\n  width: 41.66666667%;\n}\n\n.col-6 {\n  flex: 0 0 auto;\n  width: 50%;\n}\n\n.col-7 {\n  flex: 0 0 auto;\n  width: 58.33333333%;\n}\n\n.col-8 {\n  flex: 0 0 auto;\n  width: 66.66666667%;\n}\n\n.col-9 {\n  flex: 0 0 auto;\n  width: 75%;\n}\n\n.col-10 {\n  flex: 0 0 auto;\n  width: 83.33333333%;\n}\n\n.col-11 {\n  flex: 0 0 auto;\n  width: 91.66666667%;\n}\n\n.col-12 {\n  flex: 0 0 auto;\n  width: 100%;\n}\n\n.offset-1 {\n  margin-right: 8.33333333%;\n}\n\n.offset-2 {\n  margin-right: 16.66666667%;\n}\n\n.offset-3 {\n  margin-right: 25%;\n}\n\n.offset-4 {\n  margin-right: 33.33333333%;\n}\n\n.offset-5 {\n  margin-right: 41.66666667%;\n}\n\n.offset-6 {\n  margin-right: 50%;\n}\n\n.offset-7 {\n  margin-right: 58.33333333%;\n}\n\n.offset-8 {\n  margin-right: 66.66666667%;\n}\n\n.offset-9 {\n  margin-right: 75%;\n}\n\n.offset-10 {\n  margin-right: 83.33333333%;\n}\n\n.offset-11 {\n  margin-right: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n  --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n  --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n  --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n  --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n  --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n  --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n  --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n  --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n  --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n  --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n  --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n  --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n  .col-sm {\n    flex: 1 0 0%;\n  }\n  .row-cols-sm-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-sm-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-sm-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-sm-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-sm-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-sm-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-sm-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-sm-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-sm-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-sm-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-sm-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-sm-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-sm-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-sm-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-sm-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-sm-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-sm-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-sm-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-sm-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-sm-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-sm-0 {\n    margin-right: 0;\n  }\n  .offset-sm-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-sm-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-sm-3 {\n    margin-right: 25%;\n  }\n  .offset-sm-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-sm-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-sm-6 {\n    margin-right: 50%;\n  }\n  .offset-sm-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-sm-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-sm-9 {\n    margin-right: 75%;\n  }\n  .offset-sm-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-sm-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-sm-0,\n.gx-sm-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-sm-0,\n.gy-sm-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-sm-1,\n.gx-sm-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-sm-1,\n.gy-sm-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-sm-2,\n.gx-sm-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-sm-2,\n.gy-sm-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-sm-3,\n.gx-sm-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-sm-3,\n.gy-sm-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-sm-4,\n.gx-sm-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-sm-4,\n.gy-sm-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-sm-5,\n.gx-sm-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-sm-5,\n.gy-sm-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 768px) {\n  .col-md {\n    flex: 1 0 0%;\n  }\n  .row-cols-md-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-md-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-md-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-md-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-md-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-md-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-md-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-md-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-md-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-md-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-md-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-md-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-md-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-md-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-md-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-md-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-md-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-md-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-md-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-md-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-md-0 {\n    margin-right: 0;\n  }\n  .offset-md-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-md-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-md-3 {\n    margin-right: 25%;\n  }\n  .offset-md-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-md-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-md-6 {\n    margin-right: 50%;\n  }\n  .offset-md-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-md-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-md-9 {\n    margin-right: 75%;\n  }\n  .offset-md-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-md-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-md-0,\n.gx-md-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-md-0,\n.gy-md-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-md-1,\n.gx-md-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-md-1,\n.gy-md-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-md-2,\n.gx-md-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-md-2,\n.gy-md-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-md-3,\n.gx-md-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-md-3,\n.gy-md-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-md-4,\n.gx-md-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-md-4,\n.gy-md-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-md-5,\n.gx-md-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-md-5,\n.gy-md-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 992px) {\n  .col-lg {\n    flex: 1 0 0%;\n  }\n  .row-cols-lg-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-lg-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-lg-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-lg-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-lg-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-lg-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-lg-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-lg-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-lg-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-lg-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-lg-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-lg-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-lg-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-lg-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-lg-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-lg-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-lg-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-lg-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-lg-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-lg-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-lg-0 {\n    margin-right: 0;\n  }\n  .offset-lg-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-lg-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-lg-3 {\n    margin-right: 25%;\n  }\n  .offset-lg-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-lg-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-lg-6 {\n    margin-right: 50%;\n  }\n  .offset-lg-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-lg-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-lg-9 {\n    margin-right: 75%;\n  }\n  .offset-lg-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-lg-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-lg-0,\n.gx-lg-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-lg-0,\n.gy-lg-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-lg-1,\n.gx-lg-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-lg-1,\n.gy-lg-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-lg-2,\n.gx-lg-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-lg-2,\n.gy-lg-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-lg-3,\n.gx-lg-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-lg-3,\n.gy-lg-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-lg-4,\n.gx-lg-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-lg-4,\n.gy-lg-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-lg-5,\n.gx-lg-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-lg-5,\n.gy-lg-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1200px) {\n  .col-xl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xl-0 {\n    margin-right: 0;\n  }\n  .offset-xl-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-xl-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-xl-3 {\n    margin-right: 25%;\n  }\n  .offset-xl-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-xl-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-xl-6 {\n    margin-right: 50%;\n  }\n  .offset-xl-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-xl-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-xl-9 {\n    margin-right: 75%;\n  }\n  .offset-xl-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-xl-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-xl-0,\n.gx-xl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xl-0,\n.gy-xl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xl-1,\n.gx-xl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xl-1,\n.gy-xl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xl-2,\n.gx-xl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xl-2,\n.gy-xl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xl-3,\n.gx-xl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xl-3,\n.gy-xl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xl-4,\n.gx-xl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xl-4,\n.gy-xl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xl-5,\n.gx-xl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xl-5,\n.gy-xl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n@media (min-width: 1400px) {\n  .col-xxl {\n    flex: 1 0 0%;\n  }\n  .row-cols-xxl-auto > * {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .row-cols-xxl-1 > * {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .row-cols-xxl-2 > * {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .row-cols-xxl-3 > * {\n    flex: 0 0 auto;\n    width: 33.3333333333%;\n  }\n  .row-cols-xxl-4 > * {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .row-cols-xxl-5 > * {\n    flex: 0 0 auto;\n    width: 20%;\n  }\n  .row-cols-xxl-6 > * {\n    flex: 0 0 auto;\n    width: 16.6666666667%;\n  }\n  .col-xxl-auto {\n    flex: 0 0 auto;\n    width: auto;\n  }\n  .col-xxl-1 {\n    flex: 0 0 auto;\n    width: 8.33333333%;\n  }\n  .col-xxl-2 {\n    flex: 0 0 auto;\n    width: 16.66666667%;\n  }\n  .col-xxl-3 {\n    flex: 0 0 auto;\n    width: 25%;\n  }\n  .col-xxl-4 {\n    flex: 0 0 auto;\n    width: 33.33333333%;\n  }\n  .col-xxl-5 {\n    flex: 0 0 auto;\n    width: 41.66666667%;\n  }\n  .col-xxl-6 {\n    flex: 0 0 auto;\n    width: 50%;\n  }\n  .col-xxl-7 {\n    flex: 0 0 auto;\n    width: 58.33333333%;\n  }\n  .col-xxl-8 {\n    flex: 0 0 auto;\n    width: 66.66666667%;\n  }\n  .col-xxl-9 {\n    flex: 0 0 auto;\n    width: 75%;\n  }\n  .col-xxl-10 {\n    flex: 0 0 auto;\n    width: 83.33333333%;\n  }\n  .col-xxl-11 {\n    flex: 0 0 auto;\n    width: 91.66666667%;\n  }\n  .col-xxl-12 {\n    flex: 0 0 auto;\n    width: 100%;\n  }\n  .offset-xxl-0 {\n    margin-right: 0;\n  }\n  .offset-xxl-1 {\n    margin-right: 8.33333333%;\n  }\n  .offset-xxl-2 {\n    margin-right: 16.66666667%;\n  }\n  .offset-xxl-3 {\n    margin-right: 25%;\n  }\n  .offset-xxl-4 {\n    margin-right: 33.33333333%;\n  }\n  .offset-xxl-5 {\n    margin-right: 41.66666667%;\n  }\n  .offset-xxl-6 {\n    margin-right: 50%;\n  }\n  .offset-xxl-7 {\n    margin-right: 58.33333333%;\n  }\n  .offset-xxl-8 {\n    margin-right: 66.66666667%;\n  }\n  .offset-xxl-9 {\n    margin-right: 75%;\n  }\n  .offset-xxl-10 {\n    margin-right: 83.33333333%;\n  }\n  .offset-xxl-11 {\n    margin-right: 91.66666667%;\n  }\n  .g-xxl-0,\n.gx-xxl-0 {\n    --bs-gutter-x: 0;\n  }\n  .g-xxl-0,\n.gy-xxl-0 {\n    --bs-gutter-y: 0;\n  }\n  .g-xxl-1,\n.gx-xxl-1 {\n    --bs-gutter-x: 0.25rem;\n  }\n  .g-xxl-1,\n.gy-xxl-1 {\n    --bs-gutter-y: 0.25rem;\n  }\n  .g-xxl-2,\n.gx-xxl-2 {\n    --bs-gutter-x: 0.5rem;\n  }\n  .g-xxl-2,\n.gy-xxl-2 {\n    --bs-gutter-y: 0.5rem;\n  }\n  .g-xxl-3,\n.gx-xxl-3 {\n    --bs-gutter-x: 1rem;\n  }\n  .g-xxl-3,\n.gy-xxl-3 {\n    --bs-gutter-y: 1rem;\n  }\n  .g-xxl-4,\n.gx-xxl-4 {\n    --bs-gutter-x: 1.5rem;\n  }\n  .g-xxl-4,\n.gy-xxl-4 {\n    --bs-gutter-y: 1.5rem;\n  }\n  .g-xxl-5,\n.gx-xxl-5 {\n    --bs-gutter-x: 3rem;\n  }\n  .g-xxl-5,\n.gy-xxl-5 {\n    --bs-gutter-y: 3rem;\n  }\n}\n.table {\n  --bs-table-color: var(--bs-body-color);\n  --bs-table-bg: transparent;\n  --bs-table-border-color: var(--bs-border-color);\n  --bs-table-accent-bg: transparent;\n  --bs-table-striped-color: var(--bs-body-color);\n  --bs-table-striped-bg: rgba(0, 0, 0, 0.05);\n  --bs-table-active-color: var(--bs-body-color);\n  --bs-table-active-bg: rgba(0, 0, 0, 0.1);\n  --bs-table-hover-color: var(--bs-body-color);\n  --bs-table-hover-bg: rgba(0, 0, 0, 0.075);\n  width: 100%;\n  margin-bottom: 1rem;\n  color: var(--bs-table-color);\n  vertical-align: top;\n  border-color: var(--bs-table-border-color);\n}\n.table > :not(caption) > * > * {\n  padding: 0.5rem 0.5rem;\n  background-color: var(--bs-table-bg);\n  border-bottom-width: 1px;\n  box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg);\n}\n.table > tbody {\n  vertical-align: inherit;\n}\n.table > thead {\n  vertical-align: bottom;\n}\n\n.table-group-divider {\n  border-top: 2px solid currentcolor;\n}\n\n.caption-top {\n  caption-side: top;\n}\n\n.table-sm > :not(caption) > * > * {\n  padding: 0.25rem 0.25rem;\n}\n\n.table-bordered > :not(caption) > * {\n  border-width: 1px 0;\n}\n.table-bordered > :not(caption) > * > * {\n  border-width: 0 1px;\n}\n\n.table-borderless > :not(caption) > * > * {\n  border-bottom-width: 0;\n}\n.table-borderless > :not(:first-child) {\n  border-top-width: 0;\n}\n\n.table-striped > tbody > tr:nth-of-type(odd) > * {\n  --bs-table-accent-bg: var(--bs-table-striped-bg);\n  color: var(--bs-table-striped-color);\n}\n\n.table-striped-columns > :not(caption) > tr > :nth-child(even) {\n  --bs-table-accent-bg: var(--bs-table-striped-bg);\n  color: var(--bs-table-striped-color);\n}\n\n.table-active {\n  --bs-table-accent-bg: var(--bs-table-active-bg);\n  color: var(--bs-table-active-color);\n}\n\n.table-hover > tbody > tr:hover > * {\n  --bs-table-accent-bg: var(--bs-table-hover-bg);\n  color: var(--bs-table-hover-color);\n}\n\n.table-primary {\n  --bs-table-color: #000;\n  --bs-table-bg: #cfe2ff;\n  --bs-table-border-color: #bacbe6;\n  --bs-table-striped-bg: #c5d7f2;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #bacbe6;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #bfd1ec;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-secondary {\n  --bs-table-color: #000;\n  --bs-table-bg: #e2e3e5;\n  --bs-table-border-color: #cbccce;\n  --bs-table-striped-bg: #d7d8da;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #cbccce;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #d1d2d4;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-success {\n  --bs-table-color: #000;\n  --bs-table-bg: #d1e7dd;\n  --bs-table-border-color: #bcd0c7;\n  --bs-table-striped-bg: #c7dbd2;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #bcd0c7;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #c1d6cc;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-info {\n  --bs-table-color: #000;\n  --bs-table-bg: #cff4fc;\n  --bs-table-border-color: #badce3;\n  --bs-table-striped-bg: #c5e8ef;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #badce3;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #bfe2e9;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-warning {\n  --bs-table-color: #000;\n  --bs-table-bg: #fff3cd;\n  --bs-table-border-color: #e6dbb9;\n  --bs-table-striped-bg: #f2e7c3;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #e6dbb9;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #ece1be;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-danger {\n  --bs-table-color: #000;\n  --bs-table-bg: #f8d7da;\n  --bs-table-border-color: #dfc2c4;\n  --bs-table-striped-bg: #eccccf;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #dfc2c4;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #e5c7ca;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-light {\n  --bs-table-color: #000;\n  --bs-table-bg: #f8f9fa;\n  --bs-table-border-color: #dfe0e1;\n  --bs-table-striped-bg: #ecedee;\n  --bs-table-striped-color: #000;\n  --bs-table-active-bg: #dfe0e1;\n  --bs-table-active-color: #000;\n  --bs-table-hover-bg: #e5e6e7;\n  --bs-table-hover-color: #000;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-dark {\n  --bs-table-color: #fff;\n  --bs-table-bg: #212529;\n  --bs-table-border-color: #373b3e;\n  --bs-table-striped-bg: #2c3034;\n  --bs-table-striped-color: #fff;\n  --bs-table-active-bg: #373b3e;\n  --bs-table-active-color: #fff;\n  --bs-table-hover-bg: #323539;\n  --bs-table-hover-color: #fff;\n  color: var(--bs-table-color);\n  border-color: var(--bs-table-border-color);\n}\n\n.table-responsive {\n  overflow-x: auto;\n  -webkit-overflow-scrolling: touch;\n}\n\n@media (max-width: 575.98px) {\n  .table-responsive-sm {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 767.98px) {\n  .table-responsive-md {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 991.98px) {\n  .table-responsive-lg {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 1199.98px) {\n  .table-responsive-xl {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n@media (max-width: 1399.98px) {\n  .table-responsive-xxl {\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n  }\n}\n.form-label {\n  margin-bottom: 0.5rem;\n}\n\n.col-form-label {\n  padding-top: calc(0.375rem + 1px);\n  padding-bottom: calc(0.375rem + 1px);\n  margin-bottom: 0;\n  font-size: inherit;\n  line-height: 1.5;\n}\n\n.col-form-label-lg {\n  padding-top: calc(0.5rem + 1px);\n  padding-bottom: calc(0.5rem + 1px);\n  font-size: 1.25rem;\n}\n\n.col-form-label-sm {\n  padding-top: calc(0.25rem + 1px);\n  padding-bottom: calc(0.25rem + 1px);\n  font-size: 0.875rem;\n}\n\n.form-text {\n  margin-top: 0.25rem;\n  font-size: 0.875em;\n  color: #6c757d;\n}\n\n.form-control {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 1px solid #ced4da;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n  border-radius: 0.375rem;\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-control {\n    transition: none;\n  }\n}\n.form-control[type=file] {\n  overflow: hidden;\n}\n.form-control[type=file]:not(:disabled):not([readonly]) {\n  cursor: pointer;\n}\n.form-control:focus {\n  color: #212529;\n  background-color: #fff;\n  border-color: #86b7fe;\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-control::-webkit-date-and-time-value {\n  height: 1.5em;\n}\n.form-control::-moz-placeholder {\n  color: #6c757d;\n  opacity: 1;\n}\n.form-control::placeholder {\n  color: #6c757d;\n  opacity: 1;\n}\n.form-control:disabled {\n  background-color: #e9ecef;\n  opacity: 1;\n}\n.form-control::-webkit-file-upload-button {\n  padding: 0.375rem 0.75rem;\n  margin: -0.375rem -0.75rem;\n  -webkit-margin-end: 0.75rem;\n  margin-inline-end: 0.75rem;\n  color: #212529;\n  background-color: #e9ecef;\n  pointer-events: none;\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n  border-inline-end-width: 1px;\n  border-radius: 0;\n  -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n.form-control::file-selector-button {\n  padding: 0.375rem 0.75rem;\n  margin: -0.375rem -0.75rem;\n  -webkit-margin-end: 0.75rem;\n  margin-inline-end: 0.75rem;\n  color: #212529;\n  background-color: #e9ecef;\n  pointer-events: none;\n  border-color: inherit;\n  border-style: solid;\n  border-width: 0;\n  border-inline-end-width: 1px;\n  border-radius: 0;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-control::-webkit-file-upload-button {\n    -webkit-transition: none;\n    transition: none;\n  }\n  .form-control::file-selector-button {\n    transition: none;\n  }\n}\n.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {\n  background-color: #dde0e3;\n}\n.form-control:hover:not(:disabled):not([readonly])::file-selector-button {\n  background-color: #dde0e3;\n}\n\n.form-control-plaintext {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0;\n  margin-bottom: 0;\n  line-height: 1.5;\n  color: #212529;\n  background-color: transparent;\n  border: solid transparent;\n  border-width: 1px 0;\n}\n.form-control-plaintext:focus {\n  outline: 0;\n}\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.form-control-sm {\n  min-height: calc(1.5em + 0.5rem + 2px);\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  border-radius: 0.25rem;\n}\n.form-control-sm::-webkit-file-upload-button {\n  padding: 0.25rem 0.5rem;\n  margin: -0.25rem -0.5rem;\n  -webkit-margin-end: 0.5rem;\n  margin-inline-end: 0.5rem;\n}\n.form-control-sm::file-selector-button {\n  padding: 0.25rem 0.5rem;\n  margin: -0.25rem -0.5rem;\n  -webkit-margin-end: 0.5rem;\n  margin-inline-end: 0.5rem;\n}\n\n.form-control-lg {\n  min-height: calc(1.5em + 1rem + 2px);\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  border-radius: 0.5rem;\n}\n.form-control-lg::-webkit-file-upload-button {\n  padding: 0.5rem 1rem;\n  margin: -0.5rem -1rem;\n  -webkit-margin-end: 1rem;\n  margin-inline-end: 1rem;\n}\n.form-control-lg::file-selector-button {\n  padding: 0.5rem 1rem;\n  margin: -0.5rem -1rem;\n  -webkit-margin-end: 1rem;\n  margin-inline-end: 1rem;\n}\n\ntextarea.form-control {\n  min-height: calc(1.5em + 0.75rem + 2px);\n}\ntextarea.form-control-sm {\n  min-height: calc(1.5em + 0.5rem + 2px);\n}\ntextarea.form-control-lg {\n  min-height: calc(1.5em + 1rem + 2px);\n}\n\n.form-control-color {\n  width: 3rem;\n  height: calc(1.5em + 0.75rem + 2px);\n  padding: 0.375rem;\n}\n.form-control-color:not(:disabled):not([readonly]) {\n  cursor: pointer;\n}\n.form-control-color::-moz-color-swatch {\n  border: 0 !important;\n  border-radius: 0.375rem;\n}\n.form-control-color::-webkit-color-swatch {\n  border-radius: 0.375rem;\n}\n.form-control-color.form-control-sm {\n  height: calc(1.5em + 0.5rem + 2px);\n}\n.form-control-color.form-control-lg {\n  height: calc(1.5em + 1rem + 2px);\n}\n\n.form-select {\n  display: block;\n  width: 100%;\n  padding: 0.375rem 0.75rem 0.375rem 2.25rem;\n  -moz-padding-start: calc(0.75rem - 3px);\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  background-color: #fff;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: left 0.75rem center;\n  background-size: 16px 12px;\n  border: 1px solid #ced4da;\n  border-radius: 0.375rem;\n  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-select {\n    transition: none;\n  }\n}\n.form-select:focus {\n  border-color: #86b7fe;\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-select[multiple], .form-select[size]:not([size=\"1\"]) {\n  padding-left: 0.75rem;\n  background-image: none;\n}\n.form-select:disabled {\n  background-color: #e9ecef;\n}\n.form-select:-moz-focusring {\n  color: transparent;\n  text-shadow: 0 0 0 #212529;\n}\n\n.form-select-sm {\n  padding-top: 0.25rem;\n  padding-bottom: 0.25rem;\n  padding-right: 0.5rem;\n  font-size: 0.875rem;\n  border-radius: 0.25rem;\n}\n\n.form-select-lg {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  padding-right: 1rem;\n  font-size: 1.25rem;\n  border-radius: 0.5rem;\n}\n\n.form-check {\n  display: block;\n  min-height: 1.5rem;\n  padding-right: 1.5em;\n  margin-bottom: 0.125rem;\n}\n.form-check .form-check-input {\n  float: right;\n  margin-right: -1.5em;\n}\n\n.form-check-reverse {\n  padding-left: 1.5em;\n  padding-right: 0;\n  text-align: left;\n}\n.form-check-reverse .form-check-input {\n  float: left;\n  margin-left: -1.5em;\n  margin-right: 0;\n}\n\n.form-check-input {\n  width: 1em;\n  height: 1em;\n  margin-top: 0.25em;\n  vertical-align: top;\n  background-color: #fff;\n  background-repeat: no-repeat;\n  background-position: center;\n  background-size: contain;\n  border: 1px solid rgba(0, 0, 0, 0.25);\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n  -webkit-print-color-adjust: exact;\n  color-adjust: exact;\n  print-color-adjust: exact;\n}\n.form-check-input[type=checkbox] {\n  border-radius: 0.25em;\n}\n.form-check-input[type=radio] {\n  border-radius: 50%;\n}\n.form-check-input:active {\n  filter: brightness(90%);\n}\n.form-check-input:focus {\n  border-color: #86b7fe;\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-check-input:checked {\n  background-color: #0d6efd;\n  border-color: #0d6efd;\n}\n.form-check-input:checked[type=checkbox] {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e\");\n}\n.form-check-input:checked[type=radio] {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e\");\n}\n.form-check-input[type=checkbox]:indeterminate {\n  background-color: #0d6efd;\n  border-color: #0d6efd;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e\");\n}\n.form-check-input:disabled {\n  pointer-events: none;\n  filter: none;\n  opacity: 0.5;\n}\n.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label {\n  cursor: default;\n  opacity: 0.5;\n}\n\n.form-switch {\n  padding-right: 2.5em;\n}\n.form-switch .form-check-input {\n  width: 2em;\n  margin-right: -2.5em;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e\");\n  background-position: right center;\n  border-radius: 2em;\n  transition: background-position 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-switch .form-check-input {\n    transition: none;\n  }\n}\n.form-switch .form-check-input:focus {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e\");\n}\n.form-switch .form-check-input:checked {\n  background-position: left center;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e\");\n}\n.form-switch.form-check-reverse {\n  padding-left: 2.5em;\n  padding-right: 0;\n}\n.form-switch.form-check-reverse .form-check-input {\n  margin-left: -2.5em;\n  margin-right: 0;\n}\n\n.form-check-inline {\n  display: inline-block;\n  margin-left: 1rem;\n}\n\n.btn-check {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n.btn-check[disabled] + .btn, .btn-check:disabled + .btn {\n  pointer-events: none;\n  filter: none;\n  opacity: 0.65;\n}\n\n.form-range {\n  width: 100%;\n  height: 1.5rem;\n  padding: 0;\n  background-color: transparent;\n  -webkit-appearance: none;\n  -moz-appearance: none;\n  appearance: none;\n}\n.form-range:focus {\n  outline: 0;\n}\n.form-range:focus::-webkit-slider-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range:focus::-moz-range-thumb {\n  box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range::-moz-focus-outer {\n  border: 0;\n}\n.form-range::-webkit-slider-thumb {\n  width: 1rem;\n  height: 1rem;\n  margin-top: -0.25rem;\n  background-color: #0d6efd;\n  border: 0;\n  border-radius: 1rem;\n  -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -webkit-appearance: none;\n  appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-range::-webkit-slider-thumb {\n    -webkit-transition: none;\n    transition: none;\n  }\n}\n.form-range::-webkit-slider-thumb:active {\n  background-color: #b6d4fe;\n}\n.form-range::-webkit-slider-runnable-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n}\n.form-range::-moz-range-thumb {\n  width: 1rem;\n  height: 1rem;\n  background-color: #0d6efd;\n  border: 0;\n  border-radius: 1rem;\n  -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n  -moz-appearance: none;\n  appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-range::-moz-range-thumb {\n    -moz-transition: none;\n    transition: none;\n  }\n}\n.form-range::-moz-range-thumb:active {\n  background-color: #b6d4fe;\n}\n.form-range::-moz-range-track {\n  width: 100%;\n  height: 0.5rem;\n  color: transparent;\n  cursor: pointer;\n  background-color: #dee2e6;\n  border-color: transparent;\n  border-radius: 1rem;\n}\n.form-range:disabled {\n  pointer-events: none;\n}\n.form-range:disabled::-webkit-slider-thumb {\n  background-color: #adb5bd;\n}\n.form-range:disabled::-moz-range-thumb {\n  background-color: #adb5bd;\n}\n\n.form-floating {\n  position: relative;\n}\n.form-floating > .form-control,\n.form-floating > .form-control-plaintext,\n.form-floating > .form-select {\n  height: calc(3.5rem + 2px);\n  line-height: 1.25;\n}\n.form-floating > label {\n  position: absolute;\n  top: 0;\n  right: 0;\n  width: 100%;\n  height: 100%;\n  padding: 1rem 0.75rem;\n  overflow: hidden;\n  text-align: start;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  pointer-events: none;\n  border: 1px solid transparent;\n  transform-origin: 100% 0;\n  transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .form-floating > label {\n    transition: none;\n  }\n}\n.form-floating > .form-control,\n.form-floating > .form-control-plaintext {\n  padding: 1rem 0.75rem;\n}\n.form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder {\n  color: transparent;\n}\n.form-floating > .form-control::placeholder,\n.form-floating > .form-control-plaintext::placeholder {\n  color: transparent;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown),\n.form-floating > .form-control-plaintext:focus,\n.form-floating > .form-control-plaintext:not(:placeholder-shown) {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:-webkit-autofill,\n.form-floating > .form-control-plaintext:-webkit-autofill {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-select {\n  padding-top: 1.625rem;\n  padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label {\n  opacity: 0.65;\n  transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem);\n}\n.form-floating > .form-control:focus ~ label,\n.form-floating > .form-control:not(:placeholder-shown) ~ label,\n.form-floating > .form-control-plaintext ~ label,\n.form-floating > .form-select ~ label {\n  opacity: 0.65;\n  transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem);\n}\n.form-floating > .form-control:-webkit-autofill ~ label {\n  opacity: 0.65;\n  transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem);\n}\n.form-floating > .form-control-plaintext ~ label {\n  border-width: 1px 0;\n}\n\n.input-group {\n  position: relative;\n  display: flex;\n  flex-wrap: wrap;\n  align-items: stretch;\n  width: 100%;\n}\n.input-group > .form-control,\n.input-group > .form-select,\n.input-group > .form-floating {\n  position: relative;\n  flex: 1 1 auto;\n  width: 1%;\n  min-width: 0;\n}\n.input-group > .form-control:focus,\n.input-group > .form-select:focus,\n.input-group > .form-floating:focus-within {\n  z-index: 5;\n}\n.input-group .btn {\n  position: relative;\n  z-index: 2;\n}\n.input-group .btn:focus {\n  z-index: 5;\n}\n\n.input-group-text {\n  display: flex;\n  align-items: center;\n  padding: 0.375rem 0.75rem;\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  text-align: center;\n  white-space: nowrap;\n  background-color: #e9ecef;\n  border: 1px solid #ced4da;\n  border-radius: 0.375rem;\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .form-select,\n.input-group-lg > .input-group-text,\n.input-group-lg > .btn {\n  padding: 0.5rem 1rem;\n  font-size: 1.25rem;\n  border-radius: 0.5rem;\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .form-select,\n.input-group-sm > .input-group-text,\n.input-group-sm > .btn {\n  padding: 0.25rem 0.5rem;\n  font-size: 0.875rem;\n  border-radius: 0.25rem;\n}\n\n.input-group-lg > .form-select,\n.input-group-sm > .form-select {\n  padding-left: 3rem;\n}\n\n.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),\n.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3),\n.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control,\n.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),\n.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4),\n.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-control,\n.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-select {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {\n  margin-right: -1px;\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.input-group > .form-floating:not(:first-child) > .form-control,\n.input-group > .form-floating:not(:first-child) > .form-select {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.valid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 0.875em;\n  color: #198754;\n}\n\n.valid-tooltip {\n  position: absolute;\n  top: 100%;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: 0.1rem;\n  font-size: 0.875rem;\n  color: #fff;\n  background-color: rgba(25, 135, 84, 0.9);\n  border-radius: 0.375rem;\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n  border-color: #198754;\n  padding-left: calc(1.5em + 0.75rem);\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: left calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n  border-color: #198754;\n  box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n  padding-left: calc(1.5em + 0.75rem);\n  background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:valid, .form-select.is-valid {\n  border-color: #198754;\n}\n.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size=\"1\"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size=\"1\"] {\n  padding-left: 4.125rem;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n  background-position: left 0.75rem center, center left 2.25rem;\n  background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:valid:focus, .form-select.is-valid:focus {\n  border-color: #198754;\n  box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n\n.was-validated .form-control-color:valid, .form-control-color.is-valid {\n  width: calc(3rem + calc(1.5em + 0.75rem));\n}\n\n.was-validated .form-check-input:valid, .form-check-input.is-valid {\n  border-color: #198754;\n}\n.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked {\n  background-color: #198754;\n}\n.was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus {\n  box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n  color: #198754;\n}\n\n.form-check-inline .form-check-input ~ .valid-feedback {\n  margin-right: 0.5em;\n}\n\n.was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid,\n.was-validated .input-group > .form-select:not(:focus):valid,\n.input-group > .form-select:not(:focus).is-valid,\n.was-validated .input-group > .form-floating:not(:focus-within):valid,\n.input-group > .form-floating:not(:focus-within).is-valid {\n  z-index: 3;\n}\n\n.invalid-feedback {\n  display: none;\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 0.875em;\n  color: #dc3545;\n}\n\n.invalid-tooltip {\n  position: absolute;\n  top: 100%;\n  z-index: 5;\n  display: none;\n  max-width: 100%;\n  padding: 0.25rem 0.5rem;\n  margin-top: 0.1rem;\n  font-size: 0.875rem;\n  color: #fff;\n  background-color: rgba(220, 53, 69, 0.9);\n  border-radius: 0.375rem;\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n  display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n  border-color: #dc3545;\n  padding-left: calc(1.5em + 0.75rem);\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n  background-repeat: no-repeat;\n  background-position: left calc(0.375em + 0.1875rem) center;\n  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n  padding-left: calc(1.5em + 0.75rem);\n  background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:invalid, .form-select.is-invalid {\n  border-color: #dc3545;\n}\n.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size=\"1\"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size=\"1\"] {\n  padding-left: 4.125rem;\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n  background-position: left 0.75rem center, center left 2.25rem;\n  background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus {\n  border-color: #dc3545;\n  box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control-color:invalid, .form-control-color.is-invalid {\n  width: calc(3rem + calc(1.5em + 0.75rem));\n}\n\n.was-validated .form-check-input:invalid, .form-check-input.is-invalid {\n  border-color: #dc3545;\n}\n.was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked {\n  background-color: #dc3545;\n}\n.was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus {\n  box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n  color: #dc3545;\n}\n\n.form-check-inline .form-check-input ~ .invalid-feedback {\n  margin-right: 0.5em;\n}\n\n.was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid,\n.was-validated .input-group > .form-select:not(:focus):invalid,\n.input-group > .form-select:not(:focus).is-invalid,\n.was-validated .input-group > .form-floating:not(:focus-within):invalid,\n.input-group > .form-floating:not(:focus-within).is-invalid {\n  z-index: 4;\n}\n\n.btn {\n  --bs-btn-padding-x: 0.75rem;\n  --bs-btn-padding-y: 0.375rem;\n  --bs-btn-font-family: ;\n  --bs-btn-font-size: 1rem;\n  --bs-btn-font-weight: 400;\n  --bs-btn-line-height: 1.5;\n  --bs-btn-color: #212529;\n  --bs-btn-bg: transparent;\n  --bs-btn-border-width: 1px;\n  --bs-btn-border-color: transparent;\n  --bs-btn-border-radius: 0.375rem;\n  --bs-btn-hover-border-color: transparent;\n  --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n  --bs-btn-disabled-opacity: 0.65;\n  --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);\n  display: inline-block;\n  padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);\n  font-family: var(--bs-btn-font-family);\n  font-size: var(--bs-btn-font-size);\n  font-weight: var(--bs-btn-font-weight);\n  line-height: var(--bs-btn-line-height);\n  color: var(--bs-btn-color);\n  text-align: center;\n  text-decoration: none;\n  vertical-align: middle;\n  cursor: pointer;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n  border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);\n  border-radius: var(--bs-btn-border-radius);\n  background-color: var(--bs-btn-bg);\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .btn {\n    transition: none;\n  }\n}\n.btn:hover {\n  color: var(--bs-btn-hover-color);\n  background-color: var(--bs-btn-hover-bg);\n  border-color: var(--bs-btn-hover-border-color);\n}\n.btn-check + .btn:hover {\n  color: var(--bs-btn-color);\n  background-color: var(--bs-btn-bg);\n  border-color: var(--bs-btn-border-color);\n}\n.btn:focus-visible {\n  color: var(--bs-btn-hover-color);\n  background-color: var(--bs-btn-hover-bg);\n  border-color: var(--bs-btn-hover-border-color);\n  outline: 0;\n  box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn-check:focus-visible + .btn {\n  border-color: var(--bs-btn-hover-border-color);\n  outline: 0;\n  box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show {\n  color: var(--bs-btn-active-color);\n  background-color: var(--bs-btn-active-bg);\n  border-color: var(--bs-btn-active-border-color);\n}\n.btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible {\n  box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn:disabled, .btn.disabled, fieldset:disabled .btn {\n  color: var(--bs-btn-disabled-color);\n  pointer-events: none;\n  background-color: var(--bs-btn-disabled-bg);\n  border-color: var(--bs-btn-disabled-border-color);\n  opacity: var(--bs-btn-disabled-opacity);\n}\n\n.btn-primary {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #0d6efd;\n  --bs-btn-border-color: #0d6efd;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #0b5ed7;\n  --bs-btn-hover-border-color: #0a58ca;\n  --bs-btn-focus-shadow-rgb: 49, 132, 253;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #0a58ca;\n  --bs-btn-active-border-color: #0a53be;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #0d6efd;\n  --bs-btn-disabled-border-color: #0d6efd;\n}\n\n.btn-secondary {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #6c757d;\n  --bs-btn-border-color: #6c757d;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #5c636a;\n  --bs-btn-hover-border-color: #565e64;\n  --bs-btn-focus-shadow-rgb: 130, 138, 145;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #565e64;\n  --bs-btn-active-border-color: #51585e;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #6c757d;\n  --bs-btn-disabled-border-color: #6c757d;\n}\n\n.btn-success {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #198754;\n  --bs-btn-border-color: #198754;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #157347;\n  --bs-btn-hover-border-color: #146c43;\n  --bs-btn-focus-shadow-rgb: 60, 153, 110;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #146c43;\n  --bs-btn-active-border-color: #13653f;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #198754;\n  --bs-btn-disabled-border-color: #198754;\n}\n\n.btn-info {\n  --bs-btn-color: #000;\n  --bs-btn-bg: #0dcaf0;\n  --bs-btn-border-color: #0dcaf0;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #31d2f2;\n  --bs-btn-hover-border-color: #25cff2;\n  --bs-btn-focus-shadow-rgb: 11, 172, 204;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #3dd5f3;\n  --bs-btn-active-border-color: #25cff2;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #000;\n  --bs-btn-disabled-bg: #0dcaf0;\n  --bs-btn-disabled-border-color: #0dcaf0;\n}\n\n.btn-warning {\n  --bs-btn-color: #000;\n  --bs-btn-bg: #ffc107;\n  --bs-btn-border-color: #ffc107;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #ffca2c;\n  --bs-btn-hover-border-color: #ffc720;\n  --bs-btn-focus-shadow-rgb: 217, 164, 6;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #ffcd39;\n  --bs-btn-active-border-color: #ffc720;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #000;\n  --bs-btn-disabled-bg: #ffc107;\n  --bs-btn-disabled-border-color: #ffc107;\n}\n\n.btn-danger {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #dc3545;\n  --bs-btn-border-color: #dc3545;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #bb2d3b;\n  --bs-btn-hover-border-color: #b02a37;\n  --bs-btn-focus-shadow-rgb: 225, 83, 97;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #b02a37;\n  --bs-btn-active-border-color: #a52834;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #dc3545;\n  --bs-btn-disabled-border-color: #dc3545;\n}\n\n.btn-light {\n  --bs-btn-color: #000;\n  --bs-btn-bg: #f8f9fa;\n  --bs-btn-border-color: #f8f9fa;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #d3d4d5;\n  --bs-btn-hover-border-color: #c6c7c8;\n  --bs-btn-focus-shadow-rgb: 211, 212, 213;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #c6c7c8;\n  --bs-btn-active-border-color: #babbbc;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #000;\n  --bs-btn-disabled-bg: #f8f9fa;\n  --bs-btn-disabled-border-color: #f8f9fa;\n}\n\n.btn-dark {\n  --bs-btn-color: #fff;\n  --bs-btn-bg: #212529;\n  --bs-btn-border-color: #212529;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #424649;\n  --bs-btn-hover-border-color: #373b3e;\n  --bs-btn-focus-shadow-rgb: 66, 70, 73;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #4d5154;\n  --bs-btn-active-border-color: #373b3e;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #fff;\n  --bs-btn-disabled-bg: #212529;\n  --bs-btn-disabled-border-color: #212529;\n}\n\n.btn-outline-primary {\n  --bs-btn-color: #0d6efd;\n  --bs-btn-border-color: #0d6efd;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #0d6efd;\n  --bs-btn-hover-border-color: #0d6efd;\n  --bs-btn-focus-shadow-rgb: 13, 110, 253;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #0d6efd;\n  --bs-btn-active-border-color: #0d6efd;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #0d6efd;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #0d6efd;\n  --bs-gradient: none;\n}\n\n.btn-outline-secondary {\n  --bs-btn-color: #6c757d;\n  --bs-btn-border-color: #6c757d;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #6c757d;\n  --bs-btn-hover-border-color: #6c757d;\n  --bs-btn-focus-shadow-rgb: 108, 117, 125;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #6c757d;\n  --bs-btn-active-border-color: #6c757d;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #6c757d;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #6c757d;\n  --bs-gradient: none;\n}\n\n.btn-outline-success {\n  --bs-btn-color: #198754;\n  --bs-btn-border-color: #198754;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #198754;\n  --bs-btn-hover-border-color: #198754;\n  --bs-btn-focus-shadow-rgb: 25, 135, 84;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #198754;\n  --bs-btn-active-border-color: #198754;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #198754;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #198754;\n  --bs-gradient: none;\n}\n\n.btn-outline-info {\n  --bs-btn-color: #0dcaf0;\n  --bs-btn-border-color: #0dcaf0;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #0dcaf0;\n  --bs-btn-hover-border-color: #0dcaf0;\n  --bs-btn-focus-shadow-rgb: 13, 202, 240;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #0dcaf0;\n  --bs-btn-active-border-color: #0dcaf0;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #0dcaf0;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #0dcaf0;\n  --bs-gradient: none;\n}\n\n.btn-outline-warning {\n  --bs-btn-color: #ffc107;\n  --bs-btn-border-color: #ffc107;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #ffc107;\n  --bs-btn-hover-border-color: #ffc107;\n  --bs-btn-focus-shadow-rgb: 255, 193, 7;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #ffc107;\n  --bs-btn-active-border-color: #ffc107;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #ffc107;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #ffc107;\n  --bs-gradient: none;\n}\n\n.btn-outline-danger {\n  --bs-btn-color: #dc3545;\n  --bs-btn-border-color: #dc3545;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #dc3545;\n  --bs-btn-hover-border-color: #dc3545;\n  --bs-btn-focus-shadow-rgb: 220, 53, 69;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #dc3545;\n  --bs-btn-active-border-color: #dc3545;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #dc3545;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #dc3545;\n  --bs-gradient: none;\n}\n\n.btn-outline-light {\n  --bs-btn-color: #f8f9fa;\n  --bs-btn-border-color: #f8f9fa;\n  --bs-btn-hover-color: #000;\n  --bs-btn-hover-bg: #f8f9fa;\n  --bs-btn-hover-border-color: #f8f9fa;\n  --bs-btn-focus-shadow-rgb: 248, 249, 250;\n  --bs-btn-active-color: #000;\n  --bs-btn-active-bg: #f8f9fa;\n  --bs-btn-active-border-color: #f8f9fa;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #f8f9fa;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #f8f9fa;\n  --bs-gradient: none;\n}\n\n.btn-outline-dark {\n  --bs-btn-color: #212529;\n  --bs-btn-border-color: #212529;\n  --bs-btn-hover-color: #fff;\n  --bs-btn-hover-bg: #212529;\n  --bs-btn-hover-border-color: #212529;\n  --bs-btn-focus-shadow-rgb: 33, 37, 41;\n  --bs-btn-active-color: #fff;\n  --bs-btn-active-bg: #212529;\n  --bs-btn-active-border-color: #212529;\n  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n  --bs-btn-disabled-color: #212529;\n  --bs-btn-disabled-bg: transparent;\n  --bs-btn-disabled-border-color: #212529;\n  --bs-gradient: none;\n}\n\n.btn-link {\n  --bs-btn-font-weight: 400;\n  --bs-btn-color: var(--bs-link-color);\n  --bs-btn-bg: transparent;\n  --bs-btn-border-color: transparent;\n  --bs-btn-hover-color: var(--bs-link-hover-color);\n  --bs-btn-hover-border-color: transparent;\n  --bs-btn-active-color: var(--bs-link-hover-color);\n  --bs-btn-active-border-color: transparent;\n  --bs-btn-disabled-color: #6c757d;\n  --bs-btn-disabled-border-color: transparent;\n  --bs-btn-box-shadow: none;\n  --bs-btn-focus-shadow-rgb: 49, 132, 253;\n  text-decoration: underline;\n}\n.btn-link:focus-visible {\n  color: var(--bs-btn-color);\n}\n.btn-link:hover {\n  color: var(--bs-btn-hover-color);\n}\n\n.btn-lg, .btn-group-lg > .btn {\n  --bs-btn-padding-y: 0.5rem;\n  --bs-btn-padding-x: 1rem;\n  --bs-btn-font-size: 1.25rem;\n  --bs-btn-border-radius: 0.5rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n  --bs-btn-padding-y: 0.25rem;\n  --bs-btn-padding-x: 0.5rem;\n  --bs-btn-font-size: 0.875rem;\n  --bs-btn-border-radius: 0.25rem;\n}\n\n.fade {\n  transition: opacity 0.15s linear;\n}\n@media (prefers-reduced-motion: reduce) {\n  .fade {\n    transition: none;\n  }\n}\n.fade:not(.show) {\n  opacity: 0;\n}\n\n.collapse:not(.show) {\n  display: none;\n}\n\n.collapsing {\n  height: 0;\n  overflow: hidden;\n  transition: height 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .collapsing {\n    transition: none;\n  }\n}\n.collapsing.collapse-horizontal {\n  width: 0;\n  height: auto;\n  transition: width 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .collapsing.collapse-horizontal {\n    transition: none;\n  }\n}\n\n.dropup,\n.dropend,\n.dropdown,\n.dropstart,\n.dropup-center,\n.dropdown-center {\n  position: relative;\n}\n\n.dropdown-toggle {\n  white-space: nowrap;\n}\n.dropdown-toggle::after {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid;\n  border-left: 0.3em solid transparent;\n  border-bottom: 0;\n  border-right: 0.3em solid transparent;\n}\n.dropdown-toggle:empty::after {\n  margin-right: 0;\n}\n\n.dropdown-menu {\n  --bs-dropdown-zindex: 1000;\n  --bs-dropdown-min-width: 10rem;\n  --bs-dropdown-padding-x: 0;\n  --bs-dropdown-padding-y: 0.5rem;\n  --bs-dropdown-spacer: 0.125rem;\n  --bs-dropdown-font-size: 1rem;\n  --bs-dropdown-color: #212529;\n  --bs-dropdown-bg: #fff;\n  --bs-dropdown-border-color: var(--bs-border-color-translucent);\n  --bs-dropdown-border-radius: 0.375rem;\n  --bs-dropdown-border-width: 1px;\n  --bs-dropdown-inner-border-radius: calc(0.375rem - 1px);\n  --bs-dropdown-divider-bg: var(--bs-border-color-translucent);\n  --bs-dropdown-divider-margin-y: 0.5rem;\n  --bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  --bs-dropdown-link-color: #212529;\n  --bs-dropdown-link-hover-color: #1e2125;\n  --bs-dropdown-link-hover-bg: #e9ecef;\n  --bs-dropdown-link-active-color: #fff;\n  --bs-dropdown-link-active-bg: #0d6efd;\n  --bs-dropdown-link-disabled-color: #adb5bd;\n  --bs-dropdown-item-padding-x: 1rem;\n  --bs-dropdown-item-padding-y: 0.25rem;\n  --bs-dropdown-header-color: #6c757d;\n  --bs-dropdown-header-padding-x: 1rem;\n  --bs-dropdown-header-padding-y: 0.5rem;\n  position: absolute;\n  z-index: var(--bs-dropdown-zindex);\n  display: none;\n  min-width: var(--bs-dropdown-min-width);\n  padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);\n  margin: 0;\n  font-size: var(--bs-dropdown-font-size);\n  color: var(--bs-dropdown-color);\n  text-align: right;\n  list-style: none;\n  background-color: var(--bs-dropdown-bg);\n  background-clip: padding-box;\n  border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);\n  border-radius: var(--bs-dropdown-border-radius);\n}\n.dropdown-menu[data-bs-popper] {\n  top: 100%;\n  right: 0;\n  margin-top: var(--bs-dropdown-spacer);\n}\n\n.dropdown-menu-start {\n  --bs-position: start;\n}\n.dropdown-menu-start[data-bs-popper] {\n  left: auto;\n  right: 0;\n}\n\n.dropdown-menu-end {\n  --bs-position: end;\n}\n.dropdown-menu-end[data-bs-popper] {\n  left: 0;\n  right: auto;\n}\n\n@media (min-width: 576px) {\n  .dropdown-menu-sm-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-sm-start[data-bs-popper] {\n    left: auto;\n    right: 0;\n  }\n  .dropdown-menu-sm-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-sm-end[data-bs-popper] {\n    left: 0;\n    right: auto;\n  }\n}\n@media (min-width: 768px) {\n  .dropdown-menu-md-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-md-start[data-bs-popper] {\n    left: auto;\n    right: 0;\n  }\n  .dropdown-menu-md-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-md-end[data-bs-popper] {\n    left: 0;\n    right: auto;\n  }\n}\n@media (min-width: 992px) {\n  .dropdown-menu-lg-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-lg-start[data-bs-popper] {\n    left: auto;\n    right: 0;\n  }\n  .dropdown-menu-lg-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-lg-end[data-bs-popper] {\n    left: 0;\n    right: auto;\n  }\n}\n@media (min-width: 1200px) {\n  .dropdown-menu-xl-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-xl-start[data-bs-popper] {\n    left: auto;\n    right: 0;\n  }\n  .dropdown-menu-xl-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-xl-end[data-bs-popper] {\n    left: 0;\n    right: auto;\n  }\n}\n@media (min-width: 1400px) {\n  .dropdown-menu-xxl-start {\n    --bs-position: start;\n  }\n  .dropdown-menu-xxl-start[data-bs-popper] {\n    left: auto;\n    right: 0;\n  }\n  .dropdown-menu-xxl-end {\n    --bs-position: end;\n  }\n  .dropdown-menu-xxl-end[data-bs-popper] {\n    left: 0;\n    right: auto;\n  }\n}\n.dropup .dropdown-menu[data-bs-popper] {\n  top: auto;\n  bottom: 100%;\n  margin-top: 0;\n  margin-bottom: var(--bs-dropdown-spacer);\n}\n.dropup .dropdown-toggle::after {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0;\n  border-left: 0.3em solid transparent;\n  border-bottom: 0.3em solid;\n  border-right: 0.3em solid transparent;\n}\n.dropup .dropdown-toggle:empty::after {\n  margin-right: 0;\n}\n\n.dropend .dropdown-menu[data-bs-popper] {\n  top: 0;\n  left: auto;\n  right: 100%;\n  margin-top: 0;\n  margin-right: var(--bs-dropdown-spacer);\n}\n.dropend .dropdown-toggle::after {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-left: 0;\n  border-bottom: 0.3em solid transparent;\n  border-right: 0.3em solid;\n}\n.dropend .dropdown-toggle:empty::after {\n  margin-right: 0;\n}\n.dropend .dropdown-toggle::after {\n  vertical-align: 0;\n}\n\n.dropstart .dropdown-menu[data-bs-popper] {\n  top: 0;\n  left: 100%;\n  right: auto;\n  margin-top: 0;\n  margin-left: var(--bs-dropdown-spacer);\n}\n.dropstart .dropdown-toggle::after {\n  display: inline-block;\n  margin-right: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n}\n.dropstart .dropdown-toggle::after {\n  display: none;\n}\n.dropstart .dropdown-toggle::before {\n  display: inline-block;\n  margin-left: 0.255em;\n  vertical-align: 0.255em;\n  content: \"\";\n  border-top: 0.3em solid transparent;\n  border-left: 0.3em solid;\n  border-bottom: 0.3em solid transparent;\n}\n.dropstart .dropdown-toggle:empty::after {\n  margin-right: 0;\n}\n.dropstart .dropdown-toggle::before {\n  vertical-align: 0;\n}\n\n.dropdown-divider {\n  height: 0;\n  margin: var(--bs-dropdown-divider-margin-y) 0;\n  overflow: hidden;\n  border-top: 1px solid var(--bs-dropdown-divider-bg);\n  opacity: 1;\n}\n\n.dropdown-item {\n  display: block;\n  width: 100%;\n  padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);\n  clear: both;\n  font-weight: 400;\n  color: var(--bs-dropdown-link-color);\n  text-align: inherit;\n  text-decoration: none;\n  white-space: nowrap;\n  background-color: transparent;\n  border: 0;\n}\n.dropdown-item:hover, .dropdown-item:focus {\n  color: var(--bs-dropdown-link-hover-color);\n  background-color: var(--bs-dropdown-link-hover-bg);\n}\n.dropdown-item.active, .dropdown-item:active {\n  color: var(--bs-dropdown-link-active-color);\n  text-decoration: none;\n  background-color: var(--bs-dropdown-link-active-bg);\n}\n.dropdown-item.disabled, .dropdown-item:disabled {\n  color: var(--bs-dropdown-link-disabled-color);\n  pointer-events: none;\n  background-color: transparent;\n}\n\n.dropdown-menu.show {\n  display: block;\n}\n\n.dropdown-header {\n  display: block;\n  padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);\n  margin-bottom: 0;\n  font-size: 0.875rem;\n  color: var(--bs-dropdown-header-color);\n  white-space: nowrap;\n}\n\n.dropdown-item-text {\n  display: block;\n  padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);\n  color: var(--bs-dropdown-link-color);\n}\n\n.dropdown-menu-dark {\n  --bs-dropdown-color: #dee2e6;\n  --bs-dropdown-bg: #343a40;\n  --bs-dropdown-border-color: var(--bs-border-color-translucent);\n  --bs-dropdown-box-shadow: ;\n  --bs-dropdown-link-color: #dee2e6;\n  --bs-dropdown-link-hover-color: #fff;\n  --bs-dropdown-divider-bg: var(--bs-border-color-translucent);\n  --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);\n  --bs-dropdown-link-active-color: #fff;\n  --bs-dropdown-link-active-bg: #0d6efd;\n  --bs-dropdown-link-disabled-color: #adb5bd;\n  --bs-dropdown-header-color: #adb5bd;\n}\n\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-flex;\n  vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  flex: 1 1 auto;\n}\n.btn-group > .btn-check:checked + .btn,\n.btn-group > .btn-check:focus + .btn,\n.btn-group > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn-check:checked + .btn,\n.btn-group-vertical > .btn-check:focus + .btn,\n.btn-group-vertical > .btn:hover,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n  z-index: 1;\n}\n\n.btn-toolbar {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: flex-start;\n}\n.btn-toolbar .input-group {\n  width: auto;\n}\n\n.btn-group {\n  border-radius: 0.375rem;\n}\n.btn-group > :not(.btn-check:first-child) + .btn,\n.btn-group > .btn-group:not(:first-child) {\n  margin-right: -1px;\n}\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn.dropdown-toggle-split:first-child,\n.btn-group > .btn-group:not(:last-child) > .btn {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group > .btn:nth-child(n+3),\n.btn-group > :not(.btn-check) + .btn,\n.btn-group > .btn-group:not(:first-child) > .btn {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.dropdown-toggle-split {\n  padding-left: 0.5625rem;\n  padding-right: 0.5625rem;\n}\n.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after {\n  margin-right: 0;\n}\n.dropstart .dropdown-toggle-split::before {\n  margin-left: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n  padding-left: 0.375rem;\n  padding-right: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n  padding-left: 0.75rem;\n  padding-right: 0.75rem;\n}\n\n.btn-group-vertical {\n  flex-direction: column;\n  align-items: flex-start;\n  justify-content: center;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n  width: 100%;\n}\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n  margin-top: -1px;\n}\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n  border-bottom-left-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.btn-group-vertical > .btn ~ .btn,\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.nav {\n  --bs-nav-link-padding-x: 1rem;\n  --bs-nav-link-padding-y: 0.5rem;\n  --bs-nav-link-font-weight: ;\n  --bs-nav-link-color: var(--bs-link-color);\n  --bs-nav-link-hover-color: var(--bs-link-hover-color);\n  --bs-nav-link-disabled-color: #6c757d;\n  display: flex;\n  flex-wrap: wrap;\n  padding-right: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.nav-link {\n  display: block;\n  padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);\n  font-size: var(--bs-nav-link-font-size);\n  font-weight: var(--bs-nav-link-font-weight);\n  color: var(--bs-nav-link-color);\n  text-decoration: none;\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .nav-link {\n    transition: none;\n  }\n}\n.nav-link:hover, .nav-link:focus {\n  color: var(--bs-nav-link-hover-color);\n}\n.nav-link.disabled {\n  color: var(--bs-nav-link-disabled-color);\n  pointer-events: none;\n  cursor: default;\n}\n\n.nav-tabs {\n  --bs-nav-tabs-border-width: 1px;\n  --bs-nav-tabs-border-color: #dee2e6;\n  --bs-nav-tabs-border-radius: 0.375rem;\n  --bs-nav-tabs-link-hover-border-color: #e9ecef #e9ecef #dee2e6;\n  --bs-nav-tabs-link-active-color: #495057;\n  --bs-nav-tabs-link-active-bg: #fff;\n  --bs-nav-tabs-link-active-border-color: #dee2e6 #dee2e6 #fff;\n  border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color);\n}\n.nav-tabs .nav-link {\n  margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width));\n  background: none;\n  border: var(--bs-nav-tabs-border-width) solid transparent;\n  border-top-right-radius: var(--bs-nav-tabs-border-radius);\n  border-top-left-radius: var(--bs-nav-tabs-border-radius);\n}\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n  isolation: isolate;\n  border-color: var(--bs-nav-tabs-link-hover-border-color);\n}\n.nav-tabs .nav-link.disabled, .nav-tabs .nav-link:disabled {\n  color: var(--bs-nav-link-disabled-color);\n  background-color: transparent;\n  border-color: transparent;\n}\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n  color: var(--bs-nav-tabs-link-active-color);\n  background-color: var(--bs-nav-tabs-link-active-bg);\n  border-color: var(--bs-nav-tabs-link-active-border-color);\n}\n.nav-tabs .dropdown-menu {\n  margin-top: calc(-1 * var(--bs-nav-tabs-border-width));\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.nav-pills {\n  --bs-nav-pills-border-radius: 0.375rem;\n  --bs-nav-pills-link-active-color: #fff;\n  --bs-nav-pills-link-active-bg: #0d6efd;\n}\n.nav-pills .nav-link {\n  background: none;\n  border: 0;\n  border-radius: var(--bs-nav-pills-border-radius);\n}\n.nav-pills .nav-link:disabled {\n  color: var(--bs-nav-link-disabled-color);\n  background-color: transparent;\n  border-color: transparent;\n}\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n  color: var(--bs-nav-pills-link-active-color);\n  background-color: var(--bs-nav-pills-link-active-bg);\n}\n\n.nav-fill > .nav-link,\n.nav-fill .nav-item {\n  flex: 1 1 auto;\n  text-align: center;\n}\n\n.nav-justified > .nav-link,\n.nav-justified .nav-item {\n  flex-basis: 0;\n  flex-grow: 1;\n  text-align: center;\n}\n\n.nav-fill .nav-item .nav-link,\n.nav-justified .nav-item .nav-link {\n  width: 100%;\n}\n\n.tab-content > .tab-pane {\n  display: none;\n}\n.tab-content > .active {\n  display: block;\n}\n\n.navbar {\n  --bs-navbar-padding-x: 0;\n  --bs-navbar-padding-y: 0.5rem;\n  --bs-navbar-color: rgba(0, 0, 0, 0.55);\n  --bs-navbar-hover-color: rgba(0, 0, 0, 0.7);\n  --bs-navbar-disabled-color: rgba(0, 0, 0, 0.3);\n  --bs-navbar-active-color: rgba(0, 0, 0, 0.9);\n  --bs-navbar-brand-padding-y: 0.3125rem;\n  --bs-navbar-brand-margin-end: 1rem;\n  --bs-navbar-brand-font-size: 1.25rem;\n  --bs-navbar-brand-color: rgba(0, 0, 0, 0.9);\n  --bs-navbar-brand-hover-color: rgba(0, 0, 0, 0.9);\n  --bs-navbar-nav-link-padding-x: 0.5rem;\n  --bs-navbar-toggler-padding-y: 0.25rem;\n  --bs-navbar-toggler-padding-x: 0.75rem;\n  --bs-navbar-toggler-font-size: 1.25rem;\n  --bs-navbar-toggler-icon-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n  --bs-navbar-toggler-border-color: rgba(0, 0, 0, 0.1);\n  --bs-navbar-toggler-border-radius: 0.375rem;\n  --bs-navbar-toggler-focus-width: 0.25rem;\n  --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;\n  position: relative;\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  justify-content: space-between;\n  padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x);\n}\n.navbar > .container,\n.navbar > .container-fluid,\n.navbar > .container-sm,\n.navbar > .container-md,\n.navbar > .container-lg,\n.navbar > .container-xl,\n.navbar > .container-xxl {\n  display: flex;\n  flex-wrap: inherit;\n  align-items: center;\n  justify-content: space-between;\n}\n.navbar-brand {\n  padding-top: var(--bs-navbar-brand-padding-y);\n  padding-bottom: var(--bs-navbar-brand-padding-y);\n  margin-left: var(--bs-navbar-brand-margin-end);\n  font-size: var(--bs-navbar-brand-font-size);\n  color: var(--bs-navbar-brand-color);\n  text-decoration: none;\n  white-space: nowrap;\n}\n.navbar-brand:hover, .navbar-brand:focus {\n  color: var(--bs-navbar-brand-hover-color);\n}\n\n.navbar-nav {\n  --bs-nav-link-padding-x: 0;\n  --bs-nav-link-padding-y: 0.5rem;\n  --bs-nav-link-font-weight: ;\n  --bs-nav-link-color: var(--bs-navbar-color);\n  --bs-nav-link-hover-color: var(--bs-navbar-hover-color);\n  --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);\n  display: flex;\n  flex-direction: column;\n  padding-right: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n.navbar-nav .show > .nav-link,\n.navbar-nav .nav-link.active {\n  color: var(--bs-navbar-active-color);\n}\n.navbar-nav .dropdown-menu {\n  position: static;\n}\n\n.navbar-text {\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  color: var(--bs-navbar-color);\n}\n.navbar-text a,\n.navbar-text a:hover,\n.navbar-text a:focus {\n  color: var(--bs-navbar-active-color);\n}\n\n.navbar-collapse {\n  flex-basis: 100%;\n  flex-grow: 1;\n  align-items: center;\n}\n\n.navbar-toggler {\n  padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);\n  font-size: var(--bs-navbar-toggler-font-size);\n  line-height: 1;\n  color: var(--bs-navbar-color);\n  background-color: transparent;\n  border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);\n  border-radius: var(--bs-navbar-toggler-border-radius);\n  transition: var(--bs-navbar-toggler-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .navbar-toggler {\n    transition: none;\n  }\n}\n.navbar-toggler:hover {\n  text-decoration: none;\n}\n.navbar-toggler:focus {\n  text-decoration: none;\n  outline: 0;\n  box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width);\n}\n\n.navbar-toggler-icon {\n  display: inline-block;\n  width: 1.5em;\n  height: 1.5em;\n  vertical-align: middle;\n  background-image: var(--bs-navbar-toggler-icon-bg);\n  background-repeat: no-repeat;\n  background-position: center;\n  background-size: 100%;\n}\n\n.navbar-nav-scroll {\n  max-height: var(--bs-scroll-height, 75vh);\n  overflow-y: auto;\n}\n\n@media (min-width: 576px) {\n  .navbar-expand-sm {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-sm .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-sm .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-sm .navbar-nav .nav-link {\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-sm .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-sm .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-sm .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-sm .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-sm .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-sm .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-expand-md {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-md .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-md .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-md .navbar-nav .nav-link {\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-md .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-md .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-md .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-md .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-md .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-md .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 992px) {\n  .navbar-expand-lg {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-lg .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-lg .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-lg .navbar-nav .nav-link {\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-lg .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-lg .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-lg .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-lg .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-lg .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-lg .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 1200px) {\n  .navbar-expand-xl {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-xl .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-xl .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-xl .navbar-nav .nav-link {\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-xl .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-xl .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-xl .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-xl .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-xl .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-xl .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n@media (min-width: 1400px) {\n  .navbar-expand-xxl {\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n  }\n  .navbar-expand-xxl .navbar-nav {\n    flex-direction: row;\n  }\n  .navbar-expand-xxl .navbar-nav .dropdown-menu {\n    position: absolute;\n  }\n  .navbar-expand-xxl .navbar-nav .nav-link {\n    padding-left: var(--bs-navbar-nav-link-padding-x);\n    padding-right: var(--bs-navbar-nav-link-padding-x);\n  }\n  .navbar-expand-xxl .navbar-nav-scroll {\n    overflow: visible;\n  }\n  .navbar-expand-xxl .navbar-collapse {\n    display: flex !important;\n    flex-basis: auto;\n  }\n  .navbar-expand-xxl .navbar-toggler {\n    display: none;\n  }\n  .navbar-expand-xxl .offcanvas {\n    position: static;\n    z-index: auto;\n    flex-grow: 1;\n    width: auto !important;\n    height: auto !important;\n    visibility: visible !important;\n    background-color: transparent !important;\n    border: 0 !important;\n    transform: none !important;\n    transition: none;\n  }\n  .navbar-expand-xxl .offcanvas .offcanvas-header {\n    display: none;\n  }\n  .navbar-expand-xxl .offcanvas .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n  }\n}\n.navbar-expand {\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n}\n.navbar-expand .navbar-nav {\n  flex-direction: row;\n}\n.navbar-expand .navbar-nav .dropdown-menu {\n  position: absolute;\n}\n.navbar-expand .navbar-nav .nav-link {\n  padding-left: var(--bs-navbar-nav-link-padding-x);\n  padding-right: var(--bs-navbar-nav-link-padding-x);\n}\n.navbar-expand .navbar-nav-scroll {\n  overflow: visible;\n}\n.navbar-expand .navbar-collapse {\n  display: flex !important;\n  flex-basis: auto;\n}\n.navbar-expand .navbar-toggler {\n  display: none;\n}\n.navbar-expand .offcanvas {\n  position: static;\n  z-index: auto;\n  flex-grow: 1;\n  width: auto !important;\n  height: auto !important;\n  visibility: visible !important;\n  background-color: transparent !important;\n  border: 0 !important;\n  transform: none !important;\n  transition: none;\n}\n.navbar-expand .offcanvas .offcanvas-header {\n  display: none;\n}\n.navbar-expand .offcanvas .offcanvas-body {\n  display: flex;\n  flex-grow: 0;\n  padding: 0;\n  overflow-y: visible;\n}\n\n.navbar-dark {\n  --bs-navbar-color: rgba(255, 255, 255, 0.55);\n  --bs-navbar-hover-color: rgba(255, 255, 255, 0.75);\n  --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);\n  --bs-navbar-active-color: #fff;\n  --bs-navbar-brand-color: #fff;\n  --bs-navbar-brand-hover-color: #fff;\n  --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1);\n  --bs-navbar-toggler-icon-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.card {\n  --bs-card-spacer-y: 1rem;\n  --bs-card-spacer-x: 1rem;\n  --bs-card-title-spacer-y: 0.5rem;\n  --bs-card-border-width: 1px;\n  --bs-card-border-color: var(--bs-border-color-translucent);\n  --bs-card-border-radius: 0.375rem;\n  --bs-card-box-shadow: ;\n  --bs-card-inner-border-radius: calc(0.375rem - 1px);\n  --bs-card-cap-padding-y: 0.5rem;\n  --bs-card-cap-padding-x: 1rem;\n  --bs-card-cap-bg: rgba(0, 0, 0, 0.03);\n  --bs-card-cap-color: ;\n  --bs-card-height: ;\n  --bs-card-color: ;\n  --bs-card-bg: #fff;\n  --bs-card-img-overlay-padding: 1rem;\n  --bs-card-group-margin: 0.75rem;\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  min-width: 0;\n  height: var(--bs-card-height);\n  word-wrap: break-word;\n  background-color: var(--bs-card-bg);\n  background-clip: border-box;\n  border: var(--bs-card-border-width) solid var(--bs-card-border-color);\n  border-radius: var(--bs-card-border-radius);\n}\n.card > hr {\n  margin-left: 0;\n  margin-right: 0;\n}\n.card > .list-group {\n  border-top: inherit;\n  border-bottom: inherit;\n}\n.card > .list-group:first-child {\n  border-top-width: 0;\n  border-top-right-radius: var(--bs-card-inner-border-radius);\n  border-top-left-radius: var(--bs-card-inner-border-radius);\n}\n.card > .list-group:last-child {\n  border-bottom-width: 0;\n  border-bottom-left-radius: var(--bs-card-inner-border-radius);\n  border-bottom-right-radius: var(--bs-card-inner-border-radius);\n}\n.card > .card-header + .list-group,\n.card > .list-group + .card-footer {\n  border-top: 0;\n}\n\n.card-body {\n  flex: 1 1 auto;\n  padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x);\n  color: var(--bs-card-color);\n}\n\n.card-title {\n  margin-bottom: var(--bs-card-title-spacer-y);\n}\n\n.card-subtitle {\n  margin-top: calc(-0.5 * var(--bs-card-title-spacer-y));\n  margin-bottom: 0;\n}\n\n.card-text:last-child {\n  margin-bottom: 0;\n}\n\n.card-link + .card-link {\n  margin-right: var(--bs-card-spacer-x);\n}\n\n.card-header {\n  padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);\n  margin-bottom: 0;\n  color: var(--bs-card-cap-color);\n  background-color: var(--bs-card-cap-bg);\n  border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color);\n}\n.card-header:first-child {\n  border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0;\n}\n\n.card-footer {\n  padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);\n  color: var(--bs-card-cap-color);\n  background-color: var(--bs-card-cap-bg);\n  border-top: var(--bs-card-border-width) solid var(--bs-card-border-color);\n}\n.card-footer:last-child {\n  border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius);\n}\n\n.card-header-tabs {\n  margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));\n  margin-bottom: calc(-1 * var(--bs-card-cap-padding-y));\n  margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));\n  border-bottom: 0;\n}\n.card-header-tabs .nav-link.active {\n  background-color: var(--bs-card-bg);\n  border-bottom-color: var(--bs-card-bg);\n}\n\n.card-header-pills {\n  margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));\n  margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));\n}\n\n.card-img-overlay {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  right: 0;\n  padding: var(--bs-card-img-overlay-padding);\n  border-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n  width: 100%;\n}\n\n.card-img,\n.card-img-top {\n  border-top-right-radius: var(--bs-card-inner-border-radius);\n  border-top-left-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-img,\n.card-img-bottom {\n  border-bottom-left-radius: var(--bs-card-inner-border-radius);\n  border-bottom-right-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-group > .card {\n  margin-bottom: var(--bs-card-group-margin);\n}\n@media (min-width: 576px) {\n  .card-group {\n    display: flex;\n    flex-flow: row wrap;\n  }\n  .card-group > .card {\n    flex: 1 0 0%;\n    margin-bottom: 0;\n  }\n  .card-group > .card + .card {\n    margin-right: 0;\n    border-right: 0;\n  }\n  .card-group > .card:not(:last-child) {\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-top,\n.card-group > .card:not(:last-child) .card-header {\n    border-top-left-radius: 0;\n  }\n  .card-group > .card:not(:last-child) .card-img-bottom,\n.card-group > .card:not(:last-child) .card-footer {\n    border-bottom-left-radius: 0;\n  }\n  .card-group > .card:not(:first-child) {\n    border-top-right-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-top,\n.card-group > .card:not(:first-child) .card-header {\n    border-top-right-radius: 0;\n  }\n  .card-group > .card:not(:first-child) .card-img-bottom,\n.card-group > .card:not(:first-child) .card-footer {\n    border-bottom-right-radius: 0;\n  }\n}\n\n.accordion {\n  --bs-accordion-color: #212529;\n  --bs-accordion-bg: #fff;\n  --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;\n  --bs-accordion-border-color: var(--bs-border-color);\n  --bs-accordion-border-width: 1px;\n  --bs-accordion-border-radius: 0.375rem;\n  --bs-accordion-inner-border-radius: calc(0.375rem - 1px);\n  --bs-accordion-btn-padding-x: 1.25rem;\n  --bs-accordion-btn-padding-y: 1rem;\n  --bs-accordion-btn-color: #212529;\n  --bs-accordion-btn-bg: var(--bs-accordion-bg);\n  --bs-accordion-btn-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n  --bs-accordion-btn-icon-width: 1.25rem;\n  --bs-accordion-btn-icon-transform: rotate(-180deg);\n  --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;\n  --bs-accordion-btn-active-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n  --bs-accordion-btn-focus-border-color: #86b7fe;\n  --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n  --bs-accordion-body-padding-x: 1.25rem;\n  --bs-accordion-body-padding-y: 1rem;\n  --bs-accordion-active-color: #0c63e4;\n  --bs-accordion-active-bg: #e7f1ff;\n}\n\n.accordion-button {\n  position: relative;\n  display: flex;\n  align-items: center;\n  width: 100%;\n  padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);\n  font-size: 1rem;\n  color: var(--bs-accordion-btn-color);\n  text-align: right;\n  background-color: var(--bs-accordion-btn-bg);\n  border: 0;\n  border-radius: 0;\n  overflow-anchor: none;\n  transition: var(--bs-accordion-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .accordion-button {\n    transition: none;\n  }\n}\n.accordion-button:not(.collapsed) {\n  color: var(--bs-accordion-active-color);\n  background-color: var(--bs-accordion-active-bg);\n  box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color);\n}\n.accordion-button:not(.collapsed)::after {\n  background-image: var(--bs-accordion-btn-active-icon);\n  transform: var(--bs-accordion-btn-icon-transform);\n}\n.accordion-button::after {\n  flex-shrink: 0;\n  width: var(--bs-accordion-btn-icon-width);\n  height: var(--bs-accordion-btn-icon-width);\n  margin-right: auto;\n  content: \"\";\n  background-image: var(--bs-accordion-btn-icon);\n  background-repeat: no-repeat;\n  background-size: var(--bs-accordion-btn-icon-width);\n  transition: var(--bs-accordion-btn-icon-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .accordion-button::after {\n    transition: none;\n  }\n}\n.accordion-button:hover {\n  z-index: 2;\n}\n.accordion-button:focus {\n  z-index: 3;\n  border-color: var(--bs-accordion-btn-focus-border-color);\n  outline: 0;\n  box-shadow: var(--bs-accordion-btn-focus-box-shadow);\n}\n\n.accordion-header {\n  margin-bottom: 0;\n}\n\n.accordion-item {\n  color: var(--bs-accordion-color);\n  background-color: var(--bs-accordion-bg);\n  border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color);\n}\n.accordion-item:first-of-type {\n  border-top-right-radius: var(--bs-accordion-border-radius);\n  border-top-left-radius: var(--bs-accordion-border-radius);\n}\n.accordion-item:first-of-type .accordion-button {\n  border-top-right-radius: var(--bs-accordion-inner-border-radius);\n  border-top-left-radius: var(--bs-accordion-inner-border-radius);\n}\n.accordion-item:not(:first-of-type) {\n  border-top: 0;\n}\n.accordion-item:last-of-type {\n  border-bottom-left-radius: var(--bs-accordion-border-radius);\n  border-bottom-right-radius: var(--bs-accordion-border-radius);\n}\n.accordion-item:last-of-type .accordion-button.collapsed {\n  border-bottom-left-radius: var(--bs-accordion-inner-border-radius);\n  border-bottom-right-radius: var(--bs-accordion-inner-border-radius);\n}\n.accordion-item:last-of-type .accordion-collapse {\n  border-bottom-left-radius: var(--bs-accordion-border-radius);\n  border-bottom-right-radius: var(--bs-accordion-border-radius);\n}\n\n.accordion-body {\n  padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x);\n}\n\n.accordion-flush .accordion-collapse {\n  border-width: 0;\n}\n.accordion-flush .accordion-item {\n  border-left: 0;\n  border-right: 0;\n  border-radius: 0;\n}\n.accordion-flush .accordion-item:first-child {\n  border-top: 0;\n}\n.accordion-flush .accordion-item:last-child {\n  border-bottom: 0;\n}\n.accordion-flush .accordion-item .accordion-button, .accordion-flush .accordion-item .accordion-button.collapsed {\n  border-radius: 0;\n}\n\n.breadcrumb {\n  --bs-breadcrumb-padding-x: 0;\n  --bs-breadcrumb-padding-y: 0;\n  --bs-breadcrumb-margin-bottom: 1rem;\n  --bs-breadcrumb-bg: ;\n  --bs-breadcrumb-border-radius: ;\n  --bs-breadcrumb-divider-color: #6c757d;\n  --bs-breadcrumb-item-padding-x: 0.5rem;\n  --bs-breadcrumb-item-active-color: #6c757d;\n  display: flex;\n  flex-wrap: wrap;\n  padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);\n  margin-bottom: var(--bs-breadcrumb-margin-bottom);\n  font-size: var(--bs-breadcrumb-font-size);\n  list-style: none;\n  background-color: var(--bs-breadcrumb-bg);\n  border-radius: var(--bs-breadcrumb-border-radius);\n}\n\n.breadcrumb-item + .breadcrumb-item {\n  padding-right: var(--bs-breadcrumb-item-padding-x);\n}\n.breadcrumb-item + .breadcrumb-item::before {\n  float: right;\n  padding-left: var(--bs-breadcrumb-item-padding-x);\n  color: var(--bs-breadcrumb-divider-color);\n  content:  var(--bs-breadcrumb-divider, \"/\") ;\n}\n.breadcrumb-item.active {\n  color: var(--bs-breadcrumb-item-active-color);\n}\n\n.pagination {\n  --bs-pagination-padding-x: 0.75rem;\n  --bs-pagination-padding-y: 0.375rem;\n  --bs-pagination-font-size: 1rem;\n  --bs-pagination-color: var(--bs-link-color);\n  --bs-pagination-bg: #fff;\n  --bs-pagination-border-width: 1px;\n  --bs-pagination-border-color: #dee2e6;\n  --bs-pagination-border-radius: 0.375rem;\n  --bs-pagination-hover-color: var(--bs-link-hover-color);\n  --bs-pagination-hover-bg: #e9ecef;\n  --bs-pagination-hover-border-color: #dee2e6;\n  --bs-pagination-focus-color: var(--bs-link-hover-color);\n  --bs-pagination-focus-bg: #e9ecef;\n  --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n  --bs-pagination-active-color: #fff;\n  --bs-pagination-active-bg: #0d6efd;\n  --bs-pagination-active-border-color: #0d6efd;\n  --bs-pagination-disabled-color: #6c757d;\n  --bs-pagination-disabled-bg: #fff;\n  --bs-pagination-disabled-border-color: #dee2e6;\n  display: flex;\n  padding-right: 0;\n  list-style: none;\n}\n\n.page-link {\n  position: relative;\n  display: block;\n  padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);\n  font-size: var(--bs-pagination-font-size);\n  color: var(--bs-pagination-color);\n  text-decoration: none;\n  background-color: var(--bs-pagination-bg);\n  border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);\n  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .page-link {\n    transition: none;\n  }\n}\n.page-link:hover {\n  z-index: 2;\n  color: var(--bs-pagination-hover-color);\n  background-color: var(--bs-pagination-hover-bg);\n  border-color: var(--bs-pagination-hover-border-color);\n}\n.page-link:focus {\n  z-index: 3;\n  color: var(--bs-pagination-focus-color);\n  background-color: var(--bs-pagination-focus-bg);\n  outline: 0;\n  box-shadow: var(--bs-pagination-focus-box-shadow);\n}\n.page-link.active, .active > .page-link {\n  z-index: 3;\n  color: var(--bs-pagination-active-color);\n  background-color: var(--bs-pagination-active-bg);\n  border-color: var(--bs-pagination-active-border-color);\n}\n.page-link.disabled, .disabled > .page-link {\n  color: var(--bs-pagination-disabled-color);\n  pointer-events: none;\n  background-color: var(--bs-pagination-disabled-bg);\n  border-color: var(--bs-pagination-disabled-border-color);\n}\n\n.page-item:not(:first-child) .page-link {\n  margin-right: -1px;\n}\n.page-item:first-child .page-link {\n  border-top-right-radius: var(--bs-pagination-border-radius);\n  border-bottom-right-radius: var(--bs-pagination-border-radius);\n}\n.page-item:last-child .page-link {\n  border-top-left-radius: var(--bs-pagination-border-radius);\n  border-bottom-left-radius: var(--bs-pagination-border-radius);\n}\n\n.pagination-lg {\n  --bs-pagination-padding-x: 1.5rem;\n  --bs-pagination-padding-y: 0.75rem;\n  --bs-pagination-font-size: 1.25rem;\n  --bs-pagination-border-radius: 0.5rem;\n}\n\n.pagination-sm {\n  --bs-pagination-padding-x: 0.5rem;\n  --bs-pagination-padding-y: 0.25rem;\n  --bs-pagination-font-size: 0.875rem;\n  --bs-pagination-border-radius: 0.25rem;\n}\n\n.badge {\n  --bs-badge-padding-x: 0.65em;\n  --bs-badge-padding-y: 0.35em;\n  --bs-badge-font-size: 0.75em;\n  --bs-badge-font-weight: 700;\n  --bs-badge-color: #fff;\n  --bs-badge-border-radius: 0.375rem;\n  display: inline-block;\n  padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x);\n  font-size: var(--bs-badge-font-size);\n  font-weight: var(--bs-badge-font-weight);\n  line-height: 1;\n  color: var(--bs-badge-color);\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: var(--bs-badge-border-radius);\n}\n.badge:empty {\n  display: none;\n}\n\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n\n.alert {\n  --bs-alert-bg: transparent;\n  --bs-alert-padding-x: 1rem;\n  --bs-alert-padding-y: 1rem;\n  --bs-alert-margin-bottom: 1rem;\n  --bs-alert-color: inherit;\n  --bs-alert-border-color: transparent;\n  --bs-alert-border: 1px solid var(--bs-alert-border-color);\n  --bs-alert-border-radius: 0.375rem;\n  position: relative;\n  padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);\n  margin-bottom: var(--bs-alert-margin-bottom);\n  color: var(--bs-alert-color);\n  background-color: var(--bs-alert-bg);\n  border: var(--bs-alert-border);\n  border-radius: var(--bs-alert-border-radius);\n}\n\n.alert-heading {\n  color: inherit;\n}\n\n.alert-link {\n  font-weight: 700;\n}\n\n.alert-dismissible {\n  padding-left: 3rem;\n}\n.alert-dismissible .btn-close {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 2;\n  padding: 1.25rem 1rem;\n}\n\n.alert-primary {\n  --bs-alert-color: #084298;\n  --bs-alert-bg: #cfe2ff;\n  --bs-alert-border-color: #b6d4fe;\n}\n.alert-primary .alert-link {\n  color: #06357a;\n}\n\n.alert-secondary {\n  --bs-alert-color: #41464b;\n  --bs-alert-bg: #e2e3e5;\n  --bs-alert-border-color: #d3d6d8;\n}\n.alert-secondary .alert-link {\n  color: #34383c;\n}\n\n.alert-success {\n  --bs-alert-color: #0f5132;\n  --bs-alert-bg: #d1e7dd;\n  --bs-alert-border-color: #badbcc;\n}\n.alert-success .alert-link {\n  color: #0c4128;\n}\n\n.alert-info {\n  --bs-alert-color: #055160;\n  --bs-alert-bg: #cff4fc;\n  --bs-alert-border-color: #b6effb;\n}\n.alert-info .alert-link {\n  color: #04414d;\n}\n\n.alert-warning {\n  --bs-alert-color: #664d03;\n  --bs-alert-bg: #fff3cd;\n  --bs-alert-border-color: #ffecb5;\n}\n.alert-warning .alert-link {\n  color: #523e02;\n}\n\n.alert-danger {\n  --bs-alert-color: #842029;\n  --bs-alert-bg: #f8d7da;\n  --bs-alert-border-color: #f5c2c7;\n}\n.alert-danger .alert-link {\n  color: #6a1a21;\n}\n\n.alert-light {\n  --bs-alert-color: #636464;\n  --bs-alert-bg: #fefefe;\n  --bs-alert-border-color: #fdfdfe;\n}\n.alert-light .alert-link {\n  color: #4f5050;\n}\n\n.alert-dark {\n  --bs-alert-color: #141619;\n  --bs-alert-bg: #d3d3d4;\n  --bs-alert-border-color: #bcbebf;\n}\n.alert-dark .alert-link {\n  color: #101214;\n}\n\n@keyframes progress-bar-stripes {\n  0% {\n    background-position-x: 1rem;\n  }\n}\n.progress {\n  --bs-progress-height: 1rem;\n  --bs-progress-font-size: 0.75rem;\n  --bs-progress-bg: #e9ecef;\n  --bs-progress-border-radius: 0.375rem;\n  --bs-progress-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n  --bs-progress-bar-color: #fff;\n  --bs-progress-bar-bg: #0d6efd;\n  --bs-progress-bar-transition: width 0.6s ease;\n  display: flex;\n  height: var(--bs-progress-height);\n  overflow: hidden;\n  font-size: var(--bs-progress-font-size);\n  background-color: var(--bs-progress-bg);\n  border-radius: var(--bs-progress-border-radius);\n}\n\n.progress-bar {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  overflow: hidden;\n  color: var(--bs-progress-bar-color);\n  text-align: center;\n  white-space: nowrap;\n  background-color: var(--bs-progress-bar-bg);\n  transition: var(--bs-progress-bar-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar {\n    transition: none;\n  }\n}\n\n.progress-bar-striped {\n  background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: var(--bs-progress-height) var(--bs-progress-height);\n}\n\n.progress-bar-animated {\n  animation: 1s linear infinite progress-bar-stripes;\n}\n@media (prefers-reduced-motion: reduce) {\n  .progress-bar-animated {\n    animation: none;\n  }\n}\n\n.list-group {\n  --bs-list-group-color: #212529;\n  --bs-list-group-bg: #fff;\n  --bs-list-group-border-color: rgba(0, 0, 0, 0.125);\n  --bs-list-group-border-width: 1px;\n  --bs-list-group-border-radius: 0.375rem;\n  --bs-list-group-item-padding-x: 1rem;\n  --bs-list-group-item-padding-y: 0.5rem;\n  --bs-list-group-action-color: #495057;\n  --bs-list-group-action-hover-color: #495057;\n  --bs-list-group-action-hover-bg: #f8f9fa;\n  --bs-list-group-action-active-color: #212529;\n  --bs-list-group-action-active-bg: #e9ecef;\n  --bs-list-group-disabled-color: #6c757d;\n  --bs-list-group-disabled-bg: #fff;\n  --bs-list-group-active-color: #fff;\n  --bs-list-group-active-bg: #0d6efd;\n  --bs-list-group-active-border-color: #0d6efd;\n  display: flex;\n  flex-direction: column;\n  padding-right: 0;\n  margin-bottom: 0;\n  border-radius: var(--bs-list-group-border-radius);\n}\n\n.list-group-numbered {\n  list-style-type: none;\n  counter-reset: section;\n}\n.list-group-numbered > .list-group-item::before {\n  content: counters(section, \".\") \". \";\n  counter-increment: section;\n}\n\n.list-group-item-action {\n  width: 100%;\n  color: var(--bs-list-group-action-color);\n  text-align: inherit;\n}\n.list-group-item-action:hover, .list-group-item-action:focus {\n  z-index: 1;\n  color: var(--bs-list-group-action-hover-color);\n  text-decoration: none;\n  background-color: var(--bs-list-group-action-hover-bg);\n}\n.list-group-item-action:active {\n  color: var(--bs-list-group-action-active-color);\n  background-color: var(--bs-list-group-action-active-bg);\n}\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);\n  color: var(--bs-list-group-color);\n  text-decoration: none;\n  background-color: var(--bs-list-group-bg);\n  border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color);\n}\n.list-group-item:first-child {\n  border-top-right-radius: inherit;\n  border-top-left-radius: inherit;\n}\n.list-group-item:last-child {\n  border-bottom-left-radius: inherit;\n  border-bottom-right-radius: inherit;\n}\n.list-group-item.disabled, .list-group-item:disabled {\n  color: var(--bs-list-group-disabled-color);\n  pointer-events: none;\n  background-color: var(--bs-list-group-disabled-bg);\n}\n.list-group-item.active {\n  z-index: 2;\n  color: var(--bs-list-group-active-color);\n  background-color: var(--bs-list-group-active-bg);\n  border-color: var(--bs-list-group-active-border-color);\n}\n.list-group-item + .list-group-item {\n  border-top-width: 0;\n}\n.list-group-item + .list-group-item.active {\n  margin-top: calc(-1 * var(--bs-list-group-border-width));\n  border-top-width: var(--bs-list-group-border-width);\n}\n\n.list-group-horizontal {\n  flex-direction: row;\n}\n.list-group-horizontal > .list-group-item:first-child:not(:last-child) {\n  border-bottom-right-radius: var(--bs-list-group-border-radius);\n  border-top-left-radius: 0;\n}\n.list-group-horizontal > .list-group-item:last-child:not(:first-child) {\n  border-top-left-radius: var(--bs-list-group-border-radius);\n  border-bottom-right-radius: 0;\n}\n.list-group-horizontal > .list-group-item.active {\n  margin-top: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item {\n  border-top-width: var(--bs-list-group-border-width);\n  border-right-width: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item.active {\n  margin-right: calc(-1 * var(--bs-list-group-border-width));\n  border-right-width: var(--bs-list-group-border-width);\n}\n\n@media (min-width: 576px) {\n  .list-group-horizontal-sm {\n    flex-direction: row;\n  }\n  .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) {\n    border-bottom-right-radius: var(--bs-list-group-border-radius);\n    border-top-left-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) {\n    border-top-left-radius: var(--bs-list-group-border-radius);\n    border-bottom-right-radius: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-right-width: 0;\n  }\n  .list-group-horizontal-sm > .list-group-item + .list-group-item.active {\n    margin-right: calc(-1 * var(--bs-list-group-border-width));\n    border-right-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 768px) {\n  .list-group-horizontal-md {\n    flex-direction: row;\n  }\n  .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) {\n    border-bottom-right-radius: var(--bs-list-group-border-radius);\n    border-top-left-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) {\n    border-top-left-radius: var(--bs-list-group-border-radius);\n    border-bottom-right-radius: 0;\n  }\n  .list-group-horizontal-md > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-right-width: 0;\n  }\n  .list-group-horizontal-md > .list-group-item + .list-group-item.active {\n    margin-right: calc(-1 * var(--bs-list-group-border-width));\n    border-right-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 992px) {\n  .list-group-horizontal-lg {\n    flex-direction: row;\n  }\n  .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) {\n    border-bottom-right-radius: var(--bs-list-group-border-radius);\n    border-top-left-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) {\n    border-top-left-radius: var(--bs-list-group-border-radius);\n    border-bottom-right-radius: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-right-width: 0;\n  }\n  .list-group-horizontal-lg > .list-group-item + .list-group-item.active {\n    margin-right: calc(-1 * var(--bs-list-group-border-width));\n    border-right-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 1200px) {\n  .list-group-horizontal-xl {\n    flex-direction: row;\n  }\n  .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) {\n    border-bottom-right-radius: var(--bs-list-group-border-radius);\n    border-top-left-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) {\n    border-top-left-radius: var(--bs-list-group-border-radius);\n    border-bottom-right-radius: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-right-width: 0;\n  }\n  .list-group-horizontal-xl > .list-group-item + .list-group-item.active {\n    margin-right: calc(-1 * var(--bs-list-group-border-width));\n    border-right-width: var(--bs-list-group-border-width);\n  }\n}\n@media (min-width: 1400px) {\n  .list-group-horizontal-xxl {\n    flex-direction: row;\n  }\n  .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) {\n    border-bottom-right-radius: var(--bs-list-group-border-radius);\n    border-top-left-radius: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) {\n    border-top-left-radius: var(--bs-list-group-border-radius);\n    border-bottom-right-radius: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item.active {\n    margin-top: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item + .list-group-item {\n    border-top-width: var(--bs-list-group-border-width);\n    border-right-width: 0;\n  }\n  .list-group-horizontal-xxl > .list-group-item + .list-group-item.active {\n    margin-right: calc(-1 * var(--bs-list-group-border-width));\n    border-right-width: var(--bs-list-group-border-width);\n  }\n}\n.list-group-flush {\n  border-radius: 0;\n}\n.list-group-flush > .list-group-item {\n  border-width: 0 0 var(--bs-list-group-border-width);\n}\n.list-group-flush > .list-group-item:last-child {\n  border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n  color: #084298;\n  background-color: #cfe2ff;\n}\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n  color: #084298;\n  background-color: #bacbe6;\n}\n.list-group-item-primary.list-group-item-action.active {\n  color: #fff;\n  background-color: #084298;\n  border-color: #084298;\n}\n\n.list-group-item-secondary {\n  color: #41464b;\n  background-color: #e2e3e5;\n}\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n  color: #41464b;\n  background-color: #cbccce;\n}\n.list-group-item-secondary.list-group-item-action.active {\n  color: #fff;\n  background-color: #41464b;\n  border-color: #41464b;\n}\n\n.list-group-item-success {\n  color: #0f5132;\n  background-color: #d1e7dd;\n}\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n  color: #0f5132;\n  background-color: #bcd0c7;\n}\n.list-group-item-success.list-group-item-action.active {\n  color: #fff;\n  background-color: #0f5132;\n  border-color: #0f5132;\n}\n\n.list-group-item-info {\n  color: #055160;\n  background-color: #cff4fc;\n}\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n  color: #055160;\n  background-color: #badce3;\n}\n.list-group-item-info.list-group-item-action.active {\n  color: #fff;\n  background-color: #055160;\n  border-color: #055160;\n}\n\n.list-group-item-warning {\n  color: #664d03;\n  background-color: #fff3cd;\n}\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n  color: #664d03;\n  background-color: #e6dbb9;\n}\n.list-group-item-warning.list-group-item-action.active {\n  color: #fff;\n  background-color: #664d03;\n  border-color: #664d03;\n}\n\n.list-group-item-danger {\n  color: #842029;\n  background-color: #f8d7da;\n}\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n  color: #842029;\n  background-color: #dfc2c4;\n}\n.list-group-item-danger.list-group-item-action.active {\n  color: #fff;\n  background-color: #842029;\n  border-color: #842029;\n}\n\n.list-group-item-light {\n  color: #636464;\n  background-color: #fefefe;\n}\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n  color: #636464;\n  background-color: #e5e5e5;\n}\n.list-group-item-light.list-group-item-action.active {\n  color: #fff;\n  background-color: #636464;\n  border-color: #636464;\n}\n\n.list-group-item-dark {\n  color: #141619;\n  background-color: #d3d3d4;\n}\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n  color: #141619;\n  background-color: #bebebf;\n}\n.list-group-item-dark.list-group-item-action.active {\n  color: #fff;\n  background-color: #141619;\n  border-color: #141619;\n}\n\n.btn-close {\n  box-sizing: content-box;\n  width: 1em;\n  height: 1em;\n  padding: 0.25em 0.25em;\n  color: #000;\n  background: transparent url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e\") center/1em auto no-repeat;\n  border: 0;\n  border-radius: 0.375rem;\n  opacity: 0.5;\n}\n.btn-close:hover {\n  color: #000;\n  text-decoration: none;\n  opacity: 0.75;\n}\n.btn-close:focus {\n  outline: 0;\n  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n  opacity: 1;\n}\n.btn-close:disabled, .btn-close.disabled {\n  pointer-events: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n  opacity: 0.25;\n}\n\n.btn-close-white {\n  filter: invert(1) grayscale(100%) brightness(200%);\n}\n\n.toast {\n  --bs-toast-zindex: 1090;\n  --bs-toast-padding-x: 0.75rem;\n  --bs-toast-padding-y: 0.5rem;\n  --bs-toast-spacing: 1.5rem;\n  --bs-toast-max-width: 350px;\n  --bs-toast-font-size: 0.875rem;\n  --bs-toast-color: ;\n  --bs-toast-bg: rgba(255, 255, 255, 0.85);\n  --bs-toast-border-width: 1px;\n  --bs-toast-border-color: var(--bs-border-color-translucent);\n  --bs-toast-border-radius: 0.375rem;\n  --bs-toast-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  --bs-toast-header-color: #6c757d;\n  --bs-toast-header-bg: rgba(255, 255, 255, 0.85);\n  --bs-toast-header-border-color: rgba(0, 0, 0, 0.05);\n  width: var(--bs-toast-max-width);\n  max-width: 100%;\n  font-size: var(--bs-toast-font-size);\n  color: var(--bs-toast-color);\n  pointer-events: auto;\n  background-color: var(--bs-toast-bg);\n  background-clip: padding-box;\n  border: var(--bs-toast-border-width) solid var(--bs-toast-border-color);\n  box-shadow: var(--bs-toast-box-shadow);\n  border-radius: var(--bs-toast-border-radius);\n}\n.toast.showing {\n  opacity: 0;\n}\n.toast:not(.show) {\n  display: none;\n}\n\n.toast-container {\n  --bs-toast-zindex: 1090;\n  position: absolute;\n  z-index: var(--bs-toast-zindex);\n  width: -webkit-max-content;\n  width: -moz-max-content;\n  width: max-content;\n  max-width: 100%;\n  pointer-events: none;\n}\n.toast-container > :not(:last-child) {\n  margin-bottom: var(--bs-toast-spacing);\n}\n\n.toast-header {\n  display: flex;\n  align-items: center;\n  padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);\n  color: var(--bs-toast-header-color);\n  background-color: var(--bs-toast-header-bg);\n  background-clip: padding-box;\n  border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);\n  border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));\n  border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));\n}\n.toast-header .btn-close {\n  margin-left: calc(-0.5 * var(--bs-toast-padding-x));\n  margin-right: var(--bs-toast-padding-x);\n}\n\n.toast-body {\n  padding: var(--bs-toast-padding-x);\n  word-wrap: break-word;\n}\n\n.modal {\n  --bs-modal-zindex: 1055;\n  --bs-modal-width: 500px;\n  --bs-modal-padding: 1rem;\n  --bs-modal-margin: 0.5rem;\n  --bs-modal-color: ;\n  --bs-modal-bg: #fff;\n  --bs-modal-border-color: var(--bs-border-color-translucent);\n  --bs-modal-border-width: 1px;\n  --bs-modal-border-radius: 0.5rem;\n  --bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);\n  --bs-modal-inner-border-radius: calc(0.5rem - 1px);\n  --bs-modal-header-padding-x: 1rem;\n  --bs-modal-header-padding-y: 1rem;\n  --bs-modal-header-padding: 1rem 1rem;\n  --bs-modal-header-border-color: var(--bs-border-color);\n  --bs-modal-header-border-width: 1px;\n  --bs-modal-title-line-height: 1.5;\n  --bs-modal-footer-gap: 0.5rem;\n  --bs-modal-footer-bg: ;\n  --bs-modal-footer-border-color: var(--bs-border-color);\n  --bs-modal-footer-border-width: 1px;\n  position: fixed;\n  top: 0;\n  right: 0;\n  z-index: var(--bs-modal-zindex);\n  display: none;\n  width: 100%;\n  height: 100%;\n  overflow-x: hidden;\n  overflow-y: auto;\n  outline: 0;\n}\n\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: var(--bs-modal-margin);\n  pointer-events: none;\n}\n.modal.fade .modal-dialog {\n  transition: transform 0.3s ease-out;\n  transform: translate(0, -50px);\n}\n@media (prefers-reduced-motion: reduce) {\n  .modal.fade .modal-dialog {\n    transition: none;\n  }\n}\n.modal.show .modal-dialog {\n  transform: none;\n}\n.modal.modal-static .modal-dialog {\n  transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n  height: calc(100% - var(--bs-modal-margin) * 2);\n}\n.modal-dialog-scrollable .modal-content {\n  max-height: 100%;\n  overflow: hidden;\n}\n.modal-dialog-scrollable .modal-body {\n  overflow-y: auto;\n}\n\n.modal-dialog-centered {\n  display: flex;\n  align-items: center;\n  min-height: calc(100% - var(--bs-modal-margin) * 2);\n}\n\n.modal-content {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  width: 100%;\n  color: var(--bs-modal-color);\n  pointer-events: auto;\n  background-color: var(--bs-modal-bg);\n  background-clip: padding-box;\n  border: var(--bs-modal-border-width) solid var(--bs-modal-border-color);\n  border-radius: var(--bs-modal-border-radius);\n  outline: 0;\n}\n\n.modal-backdrop {\n  --bs-backdrop-zindex: 1050;\n  --bs-backdrop-bg: #000;\n  --bs-backdrop-opacity: 0.5;\n  position: fixed;\n  top: 0;\n  right: 0;\n  z-index: var(--bs-backdrop-zindex);\n  width: 100vw;\n  height: 100vh;\n  background-color: var(--bs-backdrop-bg);\n}\n.modal-backdrop.fade {\n  opacity: 0;\n}\n.modal-backdrop.show {\n  opacity: var(--bs-backdrop-opacity);\n}\n\n.modal-header {\n  display: flex;\n  flex-shrink: 0;\n  align-items: center;\n  justify-content: space-between;\n  padding: var(--bs-modal-header-padding);\n  border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);\n  border-top-right-radius: var(--bs-modal-inner-border-radius);\n  border-top-left-radius: var(--bs-modal-inner-border-radius);\n}\n.modal-header .btn-close {\n  padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5);\n  margin: calc(-0.5 * var(--bs-modal-header-padding-y)) auto calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x));\n}\n\n.modal-title {\n  margin-bottom: 0;\n  line-height: var(--bs-modal-title-line-height);\n}\n\n.modal-body {\n  position: relative;\n  flex: 1 1 auto;\n  padding: var(--bs-modal-padding);\n}\n\n.modal-footer {\n  display: flex;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  align-items: center;\n  justify-content: flex-end;\n  padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5);\n  background-color: var(--bs-modal-footer-bg);\n  border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);\n  border-bottom-left-radius: var(--bs-modal-inner-border-radius);\n  border-bottom-right-radius: var(--bs-modal-inner-border-radius);\n}\n.modal-footer > * {\n  margin: calc(var(--bs-modal-footer-gap) * 0.5);\n}\n\n@media (min-width: 576px) {\n  .modal {\n    --bs-modal-margin: 1.75rem;\n    --bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  }\n  .modal-dialog {\n    max-width: var(--bs-modal-width);\n    margin-left: auto;\n    margin-right: auto;\n  }\n  .modal-sm {\n    --bs-modal-width: 300px;\n  }\n}\n@media (min-width: 992px) {\n  .modal-lg,\n.modal-xl {\n    --bs-modal-width: 800px;\n  }\n}\n@media (min-width: 1200px) {\n  .modal-xl {\n    --bs-modal-width: 1140px;\n  }\n}\n.modal-fullscreen {\n  width: 100vw;\n  max-width: none;\n  height: 100%;\n  margin: 0;\n}\n.modal-fullscreen .modal-content {\n  height: 100%;\n  border: 0;\n  border-radius: 0;\n}\n.modal-fullscreen .modal-header,\n.modal-fullscreen .modal-footer {\n  border-radius: 0;\n}\n.modal-fullscreen .modal-body {\n  overflow-y: auto;\n}\n\n@media (max-width: 575.98px) {\n  .modal-fullscreen-sm-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-sm-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-sm-down .modal-header,\n.modal-fullscreen-sm-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-sm-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 767.98px) {\n  .modal-fullscreen-md-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-md-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-md-down .modal-header,\n.modal-fullscreen-md-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-md-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 991.98px) {\n  .modal-fullscreen-lg-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-lg-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-lg-down .modal-header,\n.modal-fullscreen-lg-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-lg-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 1199.98px) {\n  .modal-fullscreen-xl-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-xl-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-xl-down .modal-header,\n.modal-fullscreen-xl-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-xl-down .modal-body {\n    overflow-y: auto;\n  }\n}\n@media (max-width: 1399.98px) {\n  .modal-fullscreen-xxl-down {\n    width: 100vw;\n    max-width: none;\n    height: 100%;\n    margin: 0;\n  }\n  .modal-fullscreen-xxl-down .modal-content {\n    height: 100%;\n    border: 0;\n    border-radius: 0;\n  }\n  .modal-fullscreen-xxl-down .modal-header,\n.modal-fullscreen-xxl-down .modal-footer {\n    border-radius: 0;\n  }\n  .modal-fullscreen-xxl-down .modal-body {\n    overflow-y: auto;\n  }\n}\n.tooltip {\n  --bs-tooltip-zindex: 1080;\n  --bs-tooltip-max-width: 200px;\n  --bs-tooltip-padding-x: 0.5rem;\n  --bs-tooltip-padding-y: 0.25rem;\n  --bs-tooltip-margin: ;\n  --bs-tooltip-font-size: 0.875rem;\n  --bs-tooltip-color: #fff;\n  --bs-tooltip-bg: #000;\n  --bs-tooltip-border-radius: 0.375rem;\n  --bs-tooltip-opacity: 0.9;\n  --bs-tooltip-arrow-width: 0.8rem;\n  --bs-tooltip-arrow-height: 0.4rem;\n  z-index: var(--bs-tooltip-zindex);\n  display: block;\n  padding: var(--bs-tooltip-arrow-height);\n  margin: var(--bs-tooltip-margin);\n  font-family: var(--bs-font-sans-serif);\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: right;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  white-space: normal;\n  word-spacing: normal;\n  line-break: auto;\n  font-size: var(--bs-tooltip-font-size);\n  word-wrap: break-word;\n  opacity: 0;\n}\n.tooltip.show {\n  opacity: var(--bs-tooltip-opacity);\n}\n.tooltip .tooltip-arrow {\n  display: block;\n  width: var(--bs-tooltip-arrow-width);\n  height: var(--bs-tooltip-arrow-height);\n}\n.tooltip .tooltip-arrow::before {\n  position: absolute;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n}\n\n.bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {\n  bottom: 0;\n}\n.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {\n  top: -1px;\n  border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;\n  border-top-color: var(--bs-tooltip-bg);\n}\n.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {\n  left: 0;\n  width: var(--bs-tooltip-arrow-height);\n  height: var(--bs-tooltip-arrow-width);\n}\n.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {\n  right: -1px;\n  border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;\n  border-right-color: var(--bs-tooltip-bg);\n}\n.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {\n  top: 0;\n}\n.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {\n  bottom: -1px;\n  border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);\n  border-bottom-color: var(--bs-tooltip-bg);\n}\n.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {\n  right: 0;\n  width: var(--bs-tooltip-arrow-height);\n  height: var(--bs-tooltip-arrow-width);\n}\n.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {\n  left: -1px;\n  border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);\n  border-left-color: var(--bs-tooltip-bg);\n}\n.tooltip-inner {\n  max-width: var(--bs-tooltip-max-width);\n  padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);\n  color: var(--bs-tooltip-color);\n  text-align: center;\n  background-color: var(--bs-tooltip-bg);\n  border-radius: var(--bs-tooltip-border-radius);\n}\n\n.popover {\n  --bs-popover-zindex: 1070;\n  --bs-popover-max-width: 276px;\n  --bs-popover-font-size: 0.875rem;\n  --bs-popover-bg: #fff;\n  --bs-popover-border-width: 1px;\n  --bs-popover-border-color: var(--bs-border-color-translucent);\n  --bs-popover-border-radius: 0.5rem;\n  --bs-popover-inner-border-radius: calc(0.5rem - 1px);\n  --bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n  --bs-popover-header-padding-x: 1rem;\n  --bs-popover-header-padding-y: 0.5rem;\n  --bs-popover-header-font-size: 1rem;\n  --bs-popover-header-color: ;\n  --bs-popover-header-bg: #f0f0f0;\n  --bs-popover-body-padding-x: 1rem;\n  --bs-popover-body-padding-y: 1rem;\n  --bs-popover-body-color: #212529;\n  --bs-popover-arrow-width: 1rem;\n  --bs-popover-arrow-height: 0.5rem;\n  --bs-popover-arrow-border: var(--bs-popover-border-color);\n  z-index: var(--bs-popover-zindex);\n  display: block;\n  max-width: var(--bs-popover-max-width);\n  font-family: var(--bs-font-sans-serif);\n  font-style: normal;\n  font-weight: 400;\n  line-height: 1.5;\n  text-align: right;\n  text-align: start;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  letter-spacing: normal;\n  word-break: normal;\n  white-space: normal;\n  word-spacing: normal;\n  line-break: auto;\n  font-size: var(--bs-popover-font-size);\n  word-wrap: break-word;\n  background-color: var(--bs-popover-bg);\n  background-clip: padding-box;\n  border: var(--bs-popover-border-width) solid var(--bs-popover-border-color);\n  border-radius: var(--bs-popover-border-radius);\n}\n.popover .popover-arrow {\n  display: block;\n  width: var(--bs-popover-arrow-width);\n  height: var(--bs-popover-arrow-height);\n}\n.popover .popover-arrow::before, .popover .popover-arrow::after {\n  position: absolute;\n  display: block;\n  content: \"\";\n  border-color: transparent;\n  border-style: solid;\n  border-width: 0;\n}\n\n.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow {\n  bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n}\n.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {\n  border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;\n}\n.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before {\n  bottom: 0;\n  border-top-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {\n  bottom: var(--bs-popover-border-width);\n  border-top-color: var(--bs-popover-bg);\n}\n.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow {\n  left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n  width: var(--bs-popover-arrow-height);\n  height: var(--bs-popover-arrow-width);\n}\n.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {\n  border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;\n}\n.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before {\n  left: 0;\n  border-right-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {\n  left: var(--bs-popover-border-width);\n  border-right-color: var(--bs-popover-bg);\n}\n.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow {\n  top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n}\n.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {\n  border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);\n}\n.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before {\n  top: 0;\n  border-bottom-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {\n  top: var(--bs-popover-border-width);\n  border-bottom-color: var(--bs-popover-bg);\n}\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before {\n  position: absolute;\n  top: 0;\n  right: 50%;\n  display: block;\n  width: var(--bs-popover-arrow-width);\n  margin-right: calc(-0.5 * var(--bs-popover-arrow-width));\n  content: \"\";\n  border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg);\n}\n.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow {\n  right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n  width: var(--bs-popover-arrow-height);\n  height: var(--bs-popover-arrow-width);\n}\n.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {\n  border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);\n}\n.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before {\n  right: 0;\n  border-left-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {\n  right: var(--bs-popover-border-width);\n  border-left-color: var(--bs-popover-bg);\n}\n.popover-header {\n  padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);\n  margin-bottom: 0;\n  font-size: var(--bs-popover-header-font-size);\n  color: var(--bs-popover-header-color);\n  background-color: var(--bs-popover-header-bg);\n  border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color);\n  border-top-right-radius: var(--bs-popover-inner-border-radius);\n  border-top-left-radius: var(--bs-popover-inner-border-radius);\n}\n.popover-header:empty {\n  display: none;\n}\n\n.popover-body {\n  padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);\n  color: var(--bs-popover-body-color);\n}\n\n.carousel {\n  position: relative;\n}\n\n.carousel.pointer-event {\n  touch-action: pan-y;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n.carousel-inner::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.carousel-item {\n  position: relative;\n  display: none;\n  float: right;\n  width: 100%;\n  margin-left: -100%;\n  -webkit-backface-visibility: hidden;\n  backface-visibility: hidden;\n  transition: transform 0.6s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-item {\n    transition: none;\n  }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n  display: block;\n}\n\n.carousel-item-next:not(.carousel-item-start),\n.active.carousel-item-end {\n  transform: translateX(-100%);\n}\n\n.carousel-item-prev:not(.carousel-item-end),\n.active.carousel-item-start {\n  transform: translateX(100%);\n}\n\n.carousel-fade .carousel-item {\n  opacity: 0;\n  transition-property: opacity;\n  transform: none;\n}\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-start,\n.carousel-fade .carousel-item-prev.carousel-item-end {\n  z-index: 1;\n  opacity: 1;\n}\n.carousel-fade .active.carousel-item-start,\n.carousel-fade .active.carousel-item-end {\n  z-index: 0;\n  opacity: 0;\n  transition: opacity 0s 0.6s;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-fade .active.carousel-item-start,\n.carousel-fade .active.carousel-item-end {\n    transition: none;\n  }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  z-index: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 15%;\n  padding: 0;\n  color: #fff;\n  text-align: center;\n  background: none;\n  border: 0;\n  opacity: 0.5;\n  transition: opacity 0.15s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-control-prev,\n.carousel-control-next {\n    transition: none;\n  }\n}\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n  color: #fff;\n  text-decoration: none;\n  outline: 0;\n  opacity: 0.9;\n}\n\n.carousel-control-prev {\n  right: 0;\n}\n\n.carousel-control-next {\n  left: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n  display: inline-block;\n  width: 2rem;\n  height: 2rem;\n  background-repeat: no-repeat;\n  background-position: 50%;\n  background-size: 100% 100%;\n}\n.carousel-control-next-icon {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e\");\n}\n\n.carousel-control-prev-icon {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n}\n\n.carousel-indicators {\n  position: absolute;\n  left: 0;\n  bottom: 0;\n  right: 0;\n  z-index: 2;\n  display: flex;\n  justify-content: center;\n  padding: 0;\n  margin-left: 15%;\n  margin-bottom: 1rem;\n  margin-right: 15%;\n  list-style: none;\n}\n.carousel-indicators [data-bs-target] {\n  box-sizing: content-box;\n  flex: 0 1 auto;\n  width: 30px;\n  height: 3px;\n  padding: 0;\n  margin-left: 3px;\n  margin-right: 3px;\n  text-indent: -999px;\n  cursor: pointer;\n  background-color: #fff;\n  background-clip: padding-box;\n  border: 0;\n  border-top: 10px solid transparent;\n  border-bottom: 10px solid transparent;\n  opacity: 0.5;\n  transition: opacity 0.6s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n  .carousel-indicators [data-bs-target] {\n    transition: none;\n  }\n}\n.carousel-indicators .active {\n  opacity: 1;\n}\n\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  bottom: 1.25rem;\n  right: 15%;\n  padding-top: 1.25rem;\n  padding-bottom: 1.25rem;\n  color: #fff;\n  text-align: center;\n}\n\n.carousel-dark .carousel-control-next-icon,\n.carousel-dark .carousel-control-prev-icon {\n  filter: invert(1) grayscale(100);\n}\n.carousel-dark .carousel-indicators [data-bs-target] {\n  background-color: #000;\n}\n.carousel-dark .carousel-caption {\n  color: #000;\n}\n\n.spinner-grow,\n.spinner-border {\n  display: inline-block;\n  width: var(--bs-spinner-width);\n  height: var(--bs-spinner-height);\n  vertical-align: var(--bs-spinner-vertical-align);\n  border-radius: 50%;\n  animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name);\n}\n\n@keyframes spinner-border {\n  to {\n    transform: rotate(360deg) ;\n  }\n}\n.spinner-border {\n  --bs-spinner-width: 2rem;\n  --bs-spinner-height: 2rem;\n  --bs-spinner-vertical-align: -0.125em;\n  --bs-spinner-border-width: 0.25em;\n  --bs-spinner-animation-speed: 0.75s;\n  --bs-spinner-animation-name: spinner-border;\n  border: var(--bs-spinner-border-width) solid currentcolor;\n  border-left-color: transparent;\n}\n\n.spinner-border-sm {\n  --bs-spinner-width: 1rem;\n  --bs-spinner-height: 1rem;\n  --bs-spinner-border-width: 0.2em;\n}\n\n@keyframes spinner-grow {\n  0% {\n    transform: scale(0);\n  }\n  50% {\n    opacity: 1;\n    transform: none;\n  }\n}\n.spinner-grow {\n  --bs-spinner-width: 2rem;\n  --bs-spinner-height: 2rem;\n  --bs-spinner-vertical-align: -0.125em;\n  --bs-spinner-animation-speed: 0.75s;\n  --bs-spinner-animation-name: spinner-grow;\n  background-color: currentcolor;\n  opacity: 0;\n}\n\n.spinner-grow-sm {\n  --bs-spinner-width: 1rem;\n  --bs-spinner-height: 1rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .spinner-border,\n.spinner-grow {\n    --bs-spinner-animation-speed: 1.5s;\n  }\n}\n.offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm {\n  --bs-offcanvas-zindex: 1045;\n  --bs-offcanvas-width: 400px;\n  --bs-offcanvas-height: 30vh;\n  --bs-offcanvas-padding-x: 1rem;\n  --bs-offcanvas-padding-y: 1rem;\n  --bs-offcanvas-color: ;\n  --bs-offcanvas-bg: #fff;\n  --bs-offcanvas-border-width: 1px;\n  --bs-offcanvas-border-color: var(--bs-border-color-translucent);\n  --bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);\n}\n\n@media (max-width: 575.98px) {\n  .offcanvas-sm {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-sm {\n    transition: none;\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-start {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-end {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-top {\n    top: 0;\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.offcanvas-bottom {\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 575.98px) {\n  .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 576px) {\n  .offcanvas-sm {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-sm .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-sm .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 767.98px) {\n  .offcanvas-md {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-md {\n    transition: none;\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-start {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-end {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-top {\n    top: 0;\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.offcanvas-bottom {\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 767.98px) {\n  .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 768px) {\n  .offcanvas-md {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-md .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-md .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 991.98px) {\n  .offcanvas-lg {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-lg {\n    transition: none;\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-start {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-end {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-top {\n    top: 0;\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.offcanvas-bottom {\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 991.98px) {\n  .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 992px) {\n  .offcanvas-lg {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-lg .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-lg .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 1199.98px) {\n  .offcanvas-xl {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-xl {\n    transition: none;\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-start {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-end {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-top {\n    top: 0;\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.offcanvas-bottom {\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 1199.98px) {\n  .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 1200px) {\n  .offcanvas-xl {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-xl .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-xl .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl {\n    position: fixed;\n    bottom: 0;\n    z-index: var(--bs-offcanvas-zindex);\n    display: flex;\n    flex-direction: column;\n    max-width: 100%;\n    color: var(--bs-offcanvas-color);\n    visibility: hidden;\n    background-color: var(--bs-offcanvas-bg);\n    background-clip: padding-box;\n    outline: 0;\n    transition: transform 0.3s ease-in-out;\n  }\n}\n@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) {\n  .offcanvas-xxl {\n    transition: none;\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-start {\n    top: 0;\n    right: 0;\n    width: var(--bs-offcanvas-width);\n    border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-end {\n    top: 0;\n    left: 0;\n    width: var(--bs-offcanvas-width);\n    border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateX(-100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-top {\n    top: 0;\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(-100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.offcanvas-bottom {\n    left: 0;\n    right: 0;\n    height: var(--bs-offcanvas-height);\n    max-height: 100%;\n    border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n    transform: translateY(100%);\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) {\n    transform: none;\n  }\n}\n@media (max-width: 1399.98px) {\n  .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show {\n    visibility: visible;\n  }\n}\n@media (min-width: 1400px) {\n  .offcanvas-xxl {\n    --bs-offcanvas-height: auto;\n    --bs-offcanvas-border-width: 0;\n    background-color: transparent !important;\n  }\n  .offcanvas-xxl .offcanvas-header {\n    display: none;\n  }\n  .offcanvas-xxl .offcanvas-body {\n    display: flex;\n    flex-grow: 0;\n    padding: 0;\n    overflow-y: visible;\n    background-color: transparent !important;\n  }\n}\n\n.offcanvas {\n  position: fixed;\n  bottom: 0;\n  z-index: var(--bs-offcanvas-zindex);\n  display: flex;\n  flex-direction: column;\n  max-width: 100%;\n  color: var(--bs-offcanvas-color);\n  visibility: hidden;\n  background-color: var(--bs-offcanvas-bg);\n  background-clip: padding-box;\n  outline: 0;\n  transition: transform 0.3s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n  .offcanvas {\n    transition: none;\n  }\n}\n.offcanvas.offcanvas-start {\n  top: 0;\n  right: 0;\n  width: var(--bs-offcanvas-width);\n  border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateX(100%);\n}\n.offcanvas.offcanvas-end {\n  top: 0;\n  left: 0;\n  width: var(--bs-offcanvas-width);\n  border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateX(-100%);\n}\n.offcanvas.offcanvas-top {\n  top: 0;\n  left: 0;\n  right: 0;\n  height: var(--bs-offcanvas-height);\n  max-height: 100%;\n  border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateY(-100%);\n}\n.offcanvas.offcanvas-bottom {\n  left: 0;\n  right: 0;\n  height: var(--bs-offcanvas-height);\n  max-height: 100%;\n  border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n  transform: translateY(100%);\n}\n.offcanvas.showing, .offcanvas.show:not(.hiding) {\n  transform: none;\n}\n.offcanvas.showing, .offcanvas.hiding, .offcanvas.show {\n  visibility: visible;\n}\n\n.offcanvas-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  z-index: 1040;\n  width: 100vw;\n  height: 100vh;\n  background-color: #000;\n}\n.offcanvas-backdrop.fade {\n  opacity: 0;\n}\n.offcanvas-backdrop.show {\n  opacity: 0.5;\n}\n\n.offcanvas-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);\n}\n.offcanvas-header .btn-close {\n  padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5);\n  margin-top: calc(-0.5 * var(--bs-offcanvas-padding-y));\n  margin-left: calc(-0.5 * var(--bs-offcanvas-padding-x));\n  margin-bottom: calc(-0.5 * var(--bs-offcanvas-padding-y));\n}\n\n.offcanvas-title {\n  margin-bottom: 0;\n  line-height: 1.5;\n}\n\n.offcanvas-body {\n  flex-grow: 1;\n  padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);\n  overflow-y: auto;\n}\n\n.placeholder {\n  display: inline-block;\n  min-height: 1em;\n  vertical-align: middle;\n  cursor: wait;\n  background-color: currentcolor;\n  opacity: 0.5;\n}\n.placeholder.btn::before {\n  display: inline-block;\n  content: \"\";\n}\n\n.placeholder-xs {\n  min-height: 0.6em;\n}\n\n.placeholder-sm {\n  min-height: 0.8em;\n}\n\n.placeholder-lg {\n  min-height: 1.2em;\n}\n\n.placeholder-glow .placeholder {\n  animation: placeholder-glow 2s ease-in-out infinite;\n}\n\n@keyframes placeholder-glow {\n  50% {\n    opacity: 0.2;\n  }\n}\n.placeholder-wave {\n  -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n  mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n  -webkit-mask-size: 200% 100%;\n  mask-size: 200% 100%;\n  animation: placeholder-wave 2s linear infinite;\n}\n\n@keyframes placeholder-wave {\n  100% {\n    -webkit-mask-position: -200% 0%;\n    mask-position: -200% 0%;\n  }\n}\n.clearfix::after {\n  display: block;\n  clear: both;\n  content: \"\";\n}\n\n.text-bg-primary {\n  color: #fff !important;\n  background-color: RGBA(13, 110, 253, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-secondary {\n  color: #fff !important;\n  background-color: RGBA(108, 117, 125, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-success {\n  color: #fff !important;\n  background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-info {\n  color: #000 !important;\n  background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-warning {\n  color: #000 !important;\n  background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-danger {\n  color: #fff !important;\n  background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-light {\n  color: #000 !important;\n  background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-dark {\n  color: #fff !important;\n  background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important;\n}\n\n.link-primary {\n  color: #0d6efd !important;\n}\n.link-primary:hover, .link-primary:focus {\n  color: #0a58ca !important;\n}\n\n.link-secondary {\n  color: #6c757d !important;\n}\n.link-secondary:hover, .link-secondary:focus {\n  color: #565e64 !important;\n}\n\n.link-success {\n  color: #198754 !important;\n}\n.link-success:hover, .link-success:focus {\n  color: #146c43 !important;\n}\n\n.link-info {\n  color: #0dcaf0 !important;\n}\n.link-info:hover, .link-info:focus {\n  color: #3dd5f3 !important;\n}\n\n.link-warning {\n  color: #ffc107 !important;\n}\n.link-warning:hover, .link-warning:focus {\n  color: #ffcd39 !important;\n}\n\n.link-danger {\n  color: #dc3545 !important;\n}\n.link-danger:hover, .link-danger:focus {\n  color: #b02a37 !important;\n}\n\n.link-light {\n  color: #f8f9fa !important;\n}\n.link-light:hover, .link-light:focus {\n  color: #f9fafb !important;\n}\n\n.link-dark {\n  color: #212529 !important;\n}\n.link-dark:hover, .link-dark:focus {\n  color: #1a1e21 !important;\n}\n\n.ratio {\n  position: relative;\n  width: 100%;\n}\n.ratio::before {\n  display: block;\n  padding-top: var(--bs-aspect-ratio);\n  content: \"\";\n}\n.ratio > * {\n  position: absolute;\n  top: 0;\n  right: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.ratio-1x1 {\n  --bs-aspect-ratio: 100%;\n}\n\n.ratio-4x3 {\n  --bs-aspect-ratio: 75%;\n}\n\n.ratio-16x9 {\n  --bs-aspect-ratio: 56.25%;\n}\n\n.ratio-21x9 {\n  --bs-aspect-ratio: 42.8571428571%;\n}\n\n.fixed-top {\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: 1030;\n}\n\n.fixed-bottom {\n  position: fixed;\n  left: 0;\n  bottom: 0;\n  right: 0;\n  z-index: 1030;\n}\n\n.sticky-top {\n  position: -webkit-sticky;\n  position: sticky;\n  top: 0;\n  z-index: 1020;\n}\n\n.sticky-bottom {\n  position: -webkit-sticky;\n  position: sticky;\n  bottom: 0;\n  z-index: 1020;\n}\n\n@media (min-width: 576px) {\n  .sticky-sm-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-sm-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 768px) {\n  .sticky-md-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-md-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 992px) {\n  .sticky-lg-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-lg-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1200px) {\n  .sticky-xl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n@media (min-width: 1400px) {\n  .sticky-xxl-top {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 1020;\n  }\n  .sticky-xxl-bottom {\n    position: -webkit-sticky;\n    position: sticky;\n    bottom: 0;\n    z-index: 1020;\n  }\n}\n.hstack {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  align-self: stretch;\n}\n\n.vstack {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  align-self: stretch;\n}\n\n.visually-hidden,\n.visually-hidden-focusable:not(:focus):not(:focus-within) {\n  position: absolute !important;\n  width: 1px !important;\n  height: 1px !important;\n  padding: 0 !important;\n  margin: -1px !important;\n  overflow: hidden !important;\n  clip: rect(0, 0, 0, 0) !important;\n  white-space: nowrap !important;\n  border: 0 !important;\n}\n\n.stretched-link::after {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  right: 0;\n  z-index: 1;\n  content: \"\";\n}\n\n.text-truncate {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.vr {\n  display: inline-block;\n  align-self: stretch;\n  width: 1px;\n  min-height: 1em;\n  background-color: currentcolor;\n  opacity: 0.25;\n}\n\n.align-baseline {\n  vertical-align: baseline !important;\n}\n\n.align-top {\n  vertical-align: top !important;\n}\n\n.align-middle {\n  vertical-align: middle !important;\n}\n\n.align-bottom {\n  vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n  vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n  vertical-align: text-top !important;\n}\n\n.float-start {\n  float: right !important;\n}\n\n.float-end {\n  float: left !important;\n}\n\n.float-none {\n  float: none !important;\n}\n\n.opacity-0 {\n  opacity: 0 !important;\n}\n\n.opacity-25 {\n  opacity: 0.25 !important;\n}\n\n.opacity-50 {\n  opacity: 0.5 !important;\n}\n\n.opacity-75 {\n  opacity: 0.75 !important;\n}\n\n.opacity-100 {\n  opacity: 1 !important;\n}\n\n.overflow-auto {\n  overflow: auto !important;\n}\n\n.overflow-hidden {\n  overflow: hidden !important;\n}\n\n.overflow-visible {\n  overflow: visible !important;\n}\n\n.overflow-scroll {\n  overflow: scroll !important;\n}\n\n.d-inline {\n  display: inline !important;\n}\n\n.d-inline-block {\n  display: inline-block !important;\n}\n\n.d-block {\n  display: block !important;\n}\n\n.d-grid {\n  display: grid !important;\n}\n\n.d-table {\n  display: table !important;\n}\n\n.d-table-row {\n  display: table-row !important;\n}\n\n.d-table-cell {\n  display: table-cell !important;\n}\n\n.d-flex {\n  display: flex !important;\n}\n\n.d-inline-flex {\n  display: inline-flex !important;\n}\n\n.d-none {\n  display: none !important;\n}\n\n.shadow {\n  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-sm {\n  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow-lg {\n  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n  box-shadow: none !important;\n}\n\n.position-static {\n  position: static !important;\n}\n\n.position-relative {\n  position: relative !important;\n}\n\n.position-absolute {\n  position: absolute !important;\n}\n\n.position-fixed {\n  position: fixed !important;\n}\n\n.position-sticky {\n  position: -webkit-sticky !important;\n  position: sticky !important;\n}\n\n.top-0 {\n  top: 0 !important;\n}\n\n.top-50 {\n  top: 50% !important;\n}\n\n.top-100 {\n  top: 100% !important;\n}\n\n.bottom-0 {\n  bottom: 0 !important;\n}\n\n.bottom-50 {\n  bottom: 50% !important;\n}\n\n.bottom-100 {\n  bottom: 100% !important;\n}\n\n.start-0 {\n  right: 0 !important;\n}\n\n.start-50 {\n  right: 50% !important;\n}\n\n.start-100 {\n  right: 100% !important;\n}\n\n.end-0 {\n  left: 0 !important;\n}\n\n.end-50 {\n  left: 50% !important;\n}\n\n.end-100 {\n  left: 100% !important;\n}\n\n.translate-middle {\n  transform: translate(50%, -50%) !important;\n}\n\n.translate-middle-x {\n  transform: translateX(50%) !important;\n}\n\n.translate-middle-y {\n  transform: translateY(-50%) !important;\n}\n\n.border {\n  border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-0 {\n  border: 0 !important;\n}\n\n.border-top {\n  border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-top-0 {\n  border-top: 0 !important;\n}\n\n.border-end {\n  border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-end-0 {\n  border-left: 0 !important;\n}\n\n.border-bottom {\n  border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-bottom-0 {\n  border-bottom: 0 !important;\n}\n\n.border-start {\n  border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-start-0 {\n  border-right: 0 !important;\n}\n\n.border-primary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-secondary {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-success {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-info {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-warning {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-danger {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-light {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-dark {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-white {\n  --bs-border-opacity: 1;\n  border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-1 {\n  --bs-border-width: 1px;\n}\n\n.border-2 {\n  --bs-border-width: 2px;\n}\n\n.border-3 {\n  --bs-border-width: 3px;\n}\n\n.border-4 {\n  --bs-border-width: 4px;\n}\n\n.border-5 {\n  --bs-border-width: 5px;\n}\n\n.border-opacity-10 {\n  --bs-border-opacity: 0.1;\n}\n\n.border-opacity-25 {\n  --bs-border-opacity: 0.25;\n}\n\n.border-opacity-50 {\n  --bs-border-opacity: 0.5;\n}\n\n.border-opacity-75 {\n  --bs-border-opacity: 0.75;\n}\n\n.border-opacity-100 {\n  --bs-border-opacity: 1;\n}\n\n.w-25 {\n  width: 25% !important;\n}\n\n.w-50 {\n  width: 50% !important;\n}\n\n.w-75 {\n  width: 75% !important;\n}\n\n.w-100 {\n  width: 100% !important;\n}\n\n.w-auto {\n  width: auto !important;\n}\n\n.mw-100 {\n  max-width: 100% !important;\n}\n\n.vw-100 {\n  width: 100vw !important;\n}\n\n.min-vw-100 {\n  min-width: 100vw !important;\n}\n\n.h-25 {\n  height: 25% !important;\n}\n\n.h-50 {\n  height: 50% !important;\n}\n\n.h-75 {\n  height: 75% !important;\n}\n\n.h-100 {\n  height: 100% !important;\n}\n\n.h-auto {\n  height: auto !important;\n}\n\n.mh-100 {\n  max-height: 100% !important;\n}\n\n.vh-100 {\n  height: 100vh !important;\n}\n\n.min-vh-100 {\n  min-height: 100vh !important;\n}\n\n.flex-fill {\n  flex: 1 1 auto !important;\n}\n\n.flex-row {\n  flex-direction: row !important;\n}\n\n.flex-column {\n  flex-direction: column !important;\n}\n\n.flex-row-reverse {\n  flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n  flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n  flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n  flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n  flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n  flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n  flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n  flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n  flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n  justify-content: flex-start !important;\n}\n\n.justify-content-end {\n  justify-content: flex-end !important;\n}\n\n.justify-content-center {\n  justify-content: center !important;\n}\n\n.justify-content-between {\n  justify-content: space-between !important;\n}\n\n.justify-content-around {\n  justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n  justify-content: space-evenly !important;\n}\n\n.align-items-start {\n  align-items: flex-start !important;\n}\n\n.align-items-end {\n  align-items: flex-end !important;\n}\n\n.align-items-center {\n  align-items: center !important;\n}\n\n.align-items-baseline {\n  align-items: baseline !important;\n}\n\n.align-items-stretch {\n  align-items: stretch !important;\n}\n\n.align-content-start {\n  align-content: flex-start !important;\n}\n\n.align-content-end {\n  align-content: flex-end !important;\n}\n\n.align-content-center {\n  align-content: center !important;\n}\n\n.align-content-between {\n  align-content: space-between !important;\n}\n\n.align-content-around {\n  align-content: space-around !important;\n}\n\n.align-content-stretch {\n  align-content: stretch !important;\n}\n\n.align-self-auto {\n  align-self: auto !important;\n}\n\n.align-self-start {\n  align-self: flex-start !important;\n}\n\n.align-self-end {\n  align-self: flex-end !important;\n}\n\n.align-self-center {\n  align-self: center !important;\n}\n\n.align-self-baseline {\n  align-self: baseline !important;\n}\n\n.align-self-stretch {\n  align-self: stretch !important;\n}\n\n.order-first {\n  order: -1 !important;\n}\n\n.order-0 {\n  order: 0 !important;\n}\n\n.order-1 {\n  order: 1 !important;\n}\n\n.order-2 {\n  order: 2 !important;\n}\n\n.order-3 {\n  order: 3 !important;\n}\n\n.order-4 {\n  order: 4 !important;\n}\n\n.order-5 {\n  order: 5 !important;\n}\n\n.order-last {\n  order: 6 !important;\n}\n\n.m-0 {\n  margin: 0 !important;\n}\n\n.m-1 {\n  margin: 0.25rem !important;\n}\n\n.m-2 {\n  margin: 0.5rem !important;\n}\n\n.m-3 {\n  margin: 1rem !important;\n}\n\n.m-4 {\n  margin: 1.5rem !important;\n}\n\n.m-5 {\n  margin: 3rem !important;\n}\n\n.m-auto {\n  margin: auto !important;\n}\n\n.mx-0 {\n  margin-left: 0 !important;\n  margin-right: 0 !important;\n}\n\n.mx-1 {\n  margin-left: 0.25rem !important;\n  margin-right: 0.25rem !important;\n}\n\n.mx-2 {\n  margin-left: 0.5rem !important;\n  margin-right: 0.5rem !important;\n}\n\n.mx-3 {\n  margin-left: 1rem !important;\n  margin-right: 1rem !important;\n}\n\n.mx-4 {\n  margin-left: 1.5rem !important;\n  margin-right: 1.5rem !important;\n}\n\n.mx-5 {\n  margin-left: 3rem !important;\n  margin-right: 3rem !important;\n}\n\n.mx-auto {\n  margin-left: auto !important;\n  margin-right: auto !important;\n}\n\n.my-0 {\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\n\n.my-1 {\n  margin-top: 0.25rem !important;\n  margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n  margin-top: 0.5rem !important;\n  margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n  margin-top: 1rem !important;\n  margin-bottom: 1rem !important;\n}\n\n.my-4 {\n  margin-top: 1.5rem !important;\n  margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n  margin-top: 3rem !important;\n  margin-bottom: 3rem !important;\n}\n\n.my-auto {\n  margin-top: auto !important;\n  margin-bottom: auto !important;\n}\n\n.mt-0 {\n  margin-top: 0 !important;\n}\n\n.mt-1 {\n  margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n  margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n  margin-top: 1rem !important;\n}\n\n.mt-4 {\n  margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n  margin-top: 3rem !important;\n}\n\n.mt-auto {\n  margin-top: auto !important;\n}\n\n.me-0 {\n  margin-left: 0 !important;\n}\n\n.me-1 {\n  margin-left: 0.25rem !important;\n}\n\n.me-2 {\n  margin-left: 0.5rem !important;\n}\n\n.me-3 {\n  margin-left: 1rem !important;\n}\n\n.me-4 {\n  margin-left: 1.5rem !important;\n}\n\n.me-5 {\n  margin-left: 3rem !important;\n}\n\n.me-auto {\n  margin-left: auto !important;\n}\n\n.mb-0 {\n  margin-bottom: 0 !important;\n}\n\n.mb-1 {\n  margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n  margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n  margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n  margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n  margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n  margin-bottom: auto !important;\n}\n\n.ms-0 {\n  margin-right: 0 !important;\n}\n\n.ms-1 {\n  margin-right: 0.25rem !important;\n}\n\n.ms-2 {\n  margin-right: 0.5rem !important;\n}\n\n.ms-3 {\n  margin-right: 1rem !important;\n}\n\n.ms-4 {\n  margin-right: 1.5rem !important;\n}\n\n.ms-5 {\n  margin-right: 3rem !important;\n}\n\n.ms-auto {\n  margin-right: auto !important;\n}\n\n.p-0 {\n  padding: 0 !important;\n}\n\n.p-1 {\n  padding: 0.25rem !important;\n}\n\n.p-2 {\n  padding: 0.5rem !important;\n}\n\n.p-3 {\n  padding: 1rem !important;\n}\n\n.p-4 {\n  padding: 1.5rem !important;\n}\n\n.p-5 {\n  padding: 3rem !important;\n}\n\n.px-0 {\n  padding-left: 0 !important;\n  padding-right: 0 !important;\n}\n\n.px-1 {\n  padding-left: 0.25rem !important;\n  padding-right: 0.25rem !important;\n}\n\n.px-2 {\n  padding-left: 0.5rem !important;\n  padding-right: 0.5rem !important;\n}\n\n.px-3 {\n  padding-left: 1rem !important;\n  padding-right: 1rem !important;\n}\n\n.px-4 {\n  padding-left: 1.5rem !important;\n  padding-right: 1.5rem !important;\n}\n\n.px-5 {\n  padding-left: 3rem !important;\n  padding-right: 3rem !important;\n}\n\n.py-0 {\n  padding-top: 0 !important;\n  padding-bottom: 0 !important;\n}\n\n.py-1 {\n  padding-top: 0.25rem !important;\n  padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n  padding-top: 0.5rem !important;\n  padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n  padding-top: 1rem !important;\n  padding-bottom: 1rem !important;\n}\n\n.py-4 {\n  padding-top: 1.5rem !important;\n  padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n  padding-top: 3rem !important;\n  padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n  padding-top: 0 !important;\n}\n\n.pt-1 {\n  padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n  padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n  padding-top: 1rem !important;\n}\n\n.pt-4 {\n  padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n  padding-top: 3rem !important;\n}\n\n.pe-0 {\n  padding-left: 0 !important;\n}\n\n.pe-1 {\n  padding-left: 0.25rem !important;\n}\n\n.pe-2 {\n  padding-left: 0.5rem !important;\n}\n\n.pe-3 {\n  padding-left: 1rem !important;\n}\n\n.pe-4 {\n  padding-left: 1.5rem !important;\n}\n\n.pe-5 {\n  padding-left: 3rem !important;\n}\n\n.pb-0 {\n  padding-bottom: 0 !important;\n}\n\n.pb-1 {\n  padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n  padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n  padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n  padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n  padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n  padding-right: 0 !important;\n}\n\n.ps-1 {\n  padding-right: 0.25rem !important;\n}\n\n.ps-2 {\n  padding-right: 0.5rem !important;\n}\n\n.ps-3 {\n  padding-right: 1rem !important;\n}\n\n.ps-4 {\n  padding-right: 1.5rem !important;\n}\n\n.ps-5 {\n  padding-right: 3rem !important;\n}\n\n.gap-0 {\n  gap: 0 !important;\n}\n\n.gap-1 {\n  gap: 0.25rem !important;\n}\n\n.gap-2 {\n  gap: 0.5rem !important;\n}\n\n.gap-3 {\n  gap: 1rem !important;\n}\n\n.gap-4 {\n  gap: 1.5rem !important;\n}\n\n.gap-5 {\n  gap: 3rem !important;\n}\n\n.font-monospace {\n  font-family: var(--bs-font-monospace) !important;\n}\n\n.fs-1 {\n  font-size: calc(1.375rem + 1.5vw) !important;\n}\n\n.fs-2 {\n  font-size: calc(1.325rem + 0.9vw) !important;\n}\n\n.fs-3 {\n  font-size: calc(1.3rem + 0.6vw) !important;\n}\n\n.fs-4 {\n  font-size: calc(1.275rem + 0.3vw) !important;\n}\n\n.fs-5 {\n  font-size: 1.25rem !important;\n}\n\n.fs-6 {\n  font-size: 1rem !important;\n}\n\n.fst-italic {\n  font-style: italic !important;\n}\n\n.fst-normal {\n  font-style: normal !important;\n}\n\n.fw-light {\n  font-weight: 300 !important;\n}\n\n.fw-lighter {\n  font-weight: lighter !important;\n}\n\n.fw-normal {\n  font-weight: 400 !important;\n}\n\n.fw-bold {\n  font-weight: 700 !important;\n}\n\n.fw-semibold {\n  font-weight: 600 !important;\n}\n\n.fw-bolder {\n  font-weight: bolder !important;\n}\n\n.lh-1 {\n  line-height: 1 !important;\n}\n\n.lh-sm {\n  line-height: 1.25 !important;\n}\n\n.lh-base {\n  line-height: 1.5 !important;\n}\n\n.lh-lg {\n  line-height: 2 !important;\n}\n\n.text-start {\n  text-align: right !important;\n}\n\n.text-end {\n  text-align: left !important;\n}\n\n.text-center {\n  text-align: center !important;\n}\n\n.text-decoration-none {\n  text-decoration: none !important;\n}\n\n.text-decoration-underline {\n  text-decoration: underline !important;\n}\n\n.text-decoration-line-through {\n  text-decoration: line-through !important;\n}\n\n.text-lowercase {\n  text-transform: lowercase !important;\n}\n\n.text-uppercase {\n  text-transform: uppercase !important;\n}\n\n.text-capitalize {\n  text-transform: capitalize !important;\n}\n\n.text-wrap {\n  white-space: normal !important;\n}\n\n.text-nowrap {\n  white-space: nowrap !important;\n}\n.text-primary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-secondary {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-success {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-info {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-warning {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-danger {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-light {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-dark {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-black {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-white {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-body {\n  --bs-text-opacity: 1;\n  color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-muted {\n  --bs-text-opacity: 1;\n  color: #6c757d !important;\n}\n\n.text-black-50 {\n  --bs-text-opacity: 1;\n  color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n  --bs-text-opacity: 1;\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-reset {\n  --bs-text-opacity: 1;\n  color: inherit !important;\n}\n\n.text-opacity-25 {\n  --bs-text-opacity: 0.25;\n}\n\n.text-opacity-50 {\n  --bs-text-opacity: 0.5;\n}\n\n.text-opacity-75 {\n  --bs-text-opacity: 0.75;\n}\n\n.text-opacity-100 {\n  --bs-text-opacity: 1;\n}\n\n.bg-primary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-secondary {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-success {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-info {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-warning {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-danger {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-light {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-dark {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-black {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-white {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-body {\n  --bs-bg-opacity: 1;\n  background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-transparent {\n  --bs-bg-opacity: 1;\n  background-color: transparent !important;\n}\n\n.bg-opacity-10 {\n  --bs-bg-opacity: 0.1;\n}\n\n.bg-opacity-25 {\n  --bs-bg-opacity: 0.25;\n}\n\n.bg-opacity-50 {\n  --bs-bg-opacity: 0.5;\n}\n\n.bg-opacity-75 {\n  --bs-bg-opacity: 0.75;\n}\n\n.bg-opacity-100 {\n  --bs-bg-opacity: 1;\n}\n\n.bg-gradient {\n  background-image: var(--bs-gradient) !important;\n}\n\n.user-select-all {\n  -webkit-user-select: all !important;\n  -moz-user-select: all !important;\n  user-select: all !important;\n}\n\n.user-select-auto {\n  -webkit-user-select: auto !important;\n  -moz-user-select: auto !important;\n  user-select: auto !important;\n}\n\n.user-select-none {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  user-select: none !important;\n}\n\n.pe-none {\n  pointer-events: none !important;\n}\n\n.pe-auto {\n  pointer-events: auto !important;\n}\n\n.rounded {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-0 {\n  border-radius: 0 !important;\n}\n\n.rounded-1 {\n  border-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-2 {\n  border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-3 {\n  border-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-4 {\n  border-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-5 {\n  border-radius: var(--bs-border-radius-2xl) !important;\n}\n\n.rounded-circle {\n  border-radius: 50% !important;\n}\n\n.rounded-pill {\n  border-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-top {\n  border-top-right-radius: var(--bs-border-radius) !important;\n  border-top-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-end {\n  border-top-left-radius: var(--bs-border-radius) !important;\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-bottom {\n  border-bottom-left-radius: var(--bs-border-radius) !important;\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-start {\n  border-bottom-right-radius: var(--bs-border-radius) !important;\n  border-top-right-radius: var(--bs-border-radius) !important;\n}\n\n.visible {\n  visibility: visible !important;\n}\n\n.invisible {\n  visibility: hidden !important;\n}\n\n@media (min-width: 576px) {\n  .float-sm-start {\n    float: right !important;\n  }\n  .float-sm-end {\n    float: left !important;\n  }\n  .float-sm-none {\n    float: none !important;\n  }\n  .d-sm-inline {\n    display: inline !important;\n  }\n  .d-sm-inline-block {\n    display: inline-block !important;\n  }\n  .d-sm-block {\n    display: block !important;\n  }\n  .d-sm-grid {\n    display: grid !important;\n  }\n  .d-sm-table {\n    display: table !important;\n  }\n  .d-sm-table-row {\n    display: table-row !important;\n  }\n  .d-sm-table-cell {\n    display: table-cell !important;\n  }\n  .d-sm-flex {\n    display: flex !important;\n  }\n  .d-sm-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-sm-none {\n    display: none !important;\n  }\n  .flex-sm-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-sm-row {\n    flex-direction: row !important;\n  }\n  .flex-sm-column {\n    flex-direction: column !important;\n  }\n  .flex-sm-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-sm-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-sm-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-sm-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-sm-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-sm-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-sm-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-sm-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-sm-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-sm-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-sm-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-sm-center {\n    justify-content: center !important;\n  }\n  .justify-content-sm-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-sm-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-sm-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-sm-start {\n    align-items: flex-start !important;\n  }\n  .align-items-sm-end {\n    align-items: flex-end !important;\n  }\n  .align-items-sm-center {\n    align-items: center !important;\n  }\n  .align-items-sm-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-sm-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-sm-start {\n    align-content: flex-start !important;\n  }\n  .align-content-sm-end {\n    align-content: flex-end !important;\n  }\n  .align-content-sm-center {\n    align-content: center !important;\n  }\n  .align-content-sm-between {\n    align-content: space-between !important;\n  }\n  .align-content-sm-around {\n    align-content: space-around !important;\n  }\n  .align-content-sm-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-sm-auto {\n    align-self: auto !important;\n  }\n  .align-self-sm-start {\n    align-self: flex-start !important;\n  }\n  .align-self-sm-end {\n    align-self: flex-end !important;\n  }\n  .align-self-sm-center {\n    align-self: center !important;\n  }\n  .align-self-sm-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-sm-stretch {\n    align-self: stretch !important;\n  }\n  .order-sm-first {\n    order: -1 !important;\n  }\n  .order-sm-0 {\n    order: 0 !important;\n  }\n  .order-sm-1 {\n    order: 1 !important;\n  }\n  .order-sm-2 {\n    order: 2 !important;\n  }\n  .order-sm-3 {\n    order: 3 !important;\n  }\n  .order-sm-4 {\n    order: 4 !important;\n  }\n  .order-sm-5 {\n    order: 5 !important;\n  }\n  .order-sm-last {\n    order: 6 !important;\n  }\n  .m-sm-0 {\n    margin: 0 !important;\n  }\n  .m-sm-1 {\n    margin: 0.25rem !important;\n  }\n  .m-sm-2 {\n    margin: 0.5rem !important;\n  }\n  .m-sm-3 {\n    margin: 1rem !important;\n  }\n  .m-sm-4 {\n    margin: 1.5rem !important;\n  }\n  .m-sm-5 {\n    margin: 3rem !important;\n  }\n  .m-sm-auto {\n    margin: auto !important;\n  }\n  .mx-sm-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-sm-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-sm-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-sm-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-sm-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-sm-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-sm-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-sm-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-sm-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-sm-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-sm-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-sm-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-sm-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-sm-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-sm-0 {\n    margin-top: 0 !important;\n  }\n  .mt-sm-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-sm-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-sm-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-sm-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-sm-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-sm-auto {\n    margin-top: auto !important;\n  }\n  .me-sm-0 {\n    margin-left: 0 !important;\n  }\n  .me-sm-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-sm-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-sm-3 {\n    margin-left: 1rem !important;\n  }\n  .me-sm-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-sm-5 {\n    margin-left: 3rem !important;\n  }\n  .me-sm-auto {\n    margin-left: auto !important;\n  }\n  .mb-sm-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-sm-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-sm-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-sm-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-sm-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-sm-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-sm-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-sm-0 {\n    margin-right: 0 !important;\n  }\n  .ms-sm-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-sm-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-sm-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-sm-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-sm-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-sm-auto {\n    margin-right: auto !important;\n  }\n  .p-sm-0 {\n    padding: 0 !important;\n  }\n  .p-sm-1 {\n    padding: 0.25rem !important;\n  }\n  .p-sm-2 {\n    padding: 0.5rem !important;\n  }\n  .p-sm-3 {\n    padding: 1rem !important;\n  }\n  .p-sm-4 {\n    padding: 1.5rem !important;\n  }\n  .p-sm-5 {\n    padding: 3rem !important;\n  }\n  .px-sm-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-sm-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-sm-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-sm-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-sm-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-sm-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-sm-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-sm-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-sm-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-sm-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-sm-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-sm-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-sm-0 {\n    padding-top: 0 !important;\n  }\n  .pt-sm-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-sm-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-sm-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-sm-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-sm-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-sm-0 {\n    padding-left: 0 !important;\n  }\n  .pe-sm-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-sm-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-sm-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-sm-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-sm-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-sm-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-sm-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-sm-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-sm-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-sm-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-sm-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-sm-0 {\n    padding-right: 0 !important;\n  }\n  .ps-sm-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-sm-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-sm-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-sm-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-sm-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-sm-0 {\n    gap: 0 !important;\n  }\n  .gap-sm-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-sm-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-sm-3 {\n    gap: 1rem !important;\n  }\n  .gap-sm-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-sm-5 {\n    gap: 3rem !important;\n  }\n  .text-sm-start {\n    text-align: right !important;\n  }\n  .text-sm-end {\n    text-align: left !important;\n  }\n  .text-sm-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 768px) {\n  .float-md-start {\n    float: right !important;\n  }\n  .float-md-end {\n    float: left !important;\n  }\n  .float-md-none {\n    float: none !important;\n  }\n  .d-md-inline {\n    display: inline !important;\n  }\n  .d-md-inline-block {\n    display: inline-block !important;\n  }\n  .d-md-block {\n    display: block !important;\n  }\n  .d-md-grid {\n    display: grid !important;\n  }\n  .d-md-table {\n    display: table !important;\n  }\n  .d-md-table-row {\n    display: table-row !important;\n  }\n  .d-md-table-cell {\n    display: table-cell !important;\n  }\n  .d-md-flex {\n    display: flex !important;\n  }\n  .d-md-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-md-none {\n    display: none !important;\n  }\n  .flex-md-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-md-row {\n    flex-direction: row !important;\n  }\n  .flex-md-column {\n    flex-direction: column !important;\n  }\n  .flex-md-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-md-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-md-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-md-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-md-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-md-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-md-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-md-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-md-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-md-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-md-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-md-center {\n    justify-content: center !important;\n  }\n  .justify-content-md-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-md-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-md-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-md-start {\n    align-items: flex-start !important;\n  }\n  .align-items-md-end {\n    align-items: flex-end !important;\n  }\n  .align-items-md-center {\n    align-items: center !important;\n  }\n  .align-items-md-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-md-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-md-start {\n    align-content: flex-start !important;\n  }\n  .align-content-md-end {\n    align-content: flex-end !important;\n  }\n  .align-content-md-center {\n    align-content: center !important;\n  }\n  .align-content-md-between {\n    align-content: space-between !important;\n  }\n  .align-content-md-around {\n    align-content: space-around !important;\n  }\n  .align-content-md-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-md-auto {\n    align-self: auto !important;\n  }\n  .align-self-md-start {\n    align-self: flex-start !important;\n  }\n  .align-self-md-end {\n    align-self: flex-end !important;\n  }\n  .align-self-md-center {\n    align-self: center !important;\n  }\n  .align-self-md-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-md-stretch {\n    align-self: stretch !important;\n  }\n  .order-md-first {\n    order: -1 !important;\n  }\n  .order-md-0 {\n    order: 0 !important;\n  }\n  .order-md-1 {\n    order: 1 !important;\n  }\n  .order-md-2 {\n    order: 2 !important;\n  }\n  .order-md-3 {\n    order: 3 !important;\n  }\n  .order-md-4 {\n    order: 4 !important;\n  }\n  .order-md-5 {\n    order: 5 !important;\n  }\n  .order-md-last {\n    order: 6 !important;\n  }\n  .m-md-0 {\n    margin: 0 !important;\n  }\n  .m-md-1 {\n    margin: 0.25rem !important;\n  }\n  .m-md-2 {\n    margin: 0.5rem !important;\n  }\n  .m-md-3 {\n    margin: 1rem !important;\n  }\n  .m-md-4 {\n    margin: 1.5rem !important;\n  }\n  .m-md-5 {\n    margin: 3rem !important;\n  }\n  .m-md-auto {\n    margin: auto !important;\n  }\n  .mx-md-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-md-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-md-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-md-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-md-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-md-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-md-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-md-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-md-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-md-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-md-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-md-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-md-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-md-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-md-0 {\n    margin-top: 0 !important;\n  }\n  .mt-md-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-md-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-md-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-md-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-md-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-md-auto {\n    margin-top: auto !important;\n  }\n  .me-md-0 {\n    margin-left: 0 !important;\n  }\n  .me-md-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-md-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-md-3 {\n    margin-left: 1rem !important;\n  }\n  .me-md-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-md-5 {\n    margin-left: 3rem !important;\n  }\n  .me-md-auto {\n    margin-left: auto !important;\n  }\n  .mb-md-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-md-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-md-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-md-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-md-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-md-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-md-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-md-0 {\n    margin-right: 0 !important;\n  }\n  .ms-md-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-md-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-md-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-md-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-md-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-md-auto {\n    margin-right: auto !important;\n  }\n  .p-md-0 {\n    padding: 0 !important;\n  }\n  .p-md-1 {\n    padding: 0.25rem !important;\n  }\n  .p-md-2 {\n    padding: 0.5rem !important;\n  }\n  .p-md-3 {\n    padding: 1rem !important;\n  }\n  .p-md-4 {\n    padding: 1.5rem !important;\n  }\n  .p-md-5 {\n    padding: 3rem !important;\n  }\n  .px-md-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-md-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-md-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-md-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-md-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-md-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-md-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-md-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-md-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-md-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-md-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-md-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-md-0 {\n    padding-top: 0 !important;\n  }\n  .pt-md-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-md-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-md-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-md-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-md-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-md-0 {\n    padding-left: 0 !important;\n  }\n  .pe-md-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-md-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-md-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-md-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-md-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-md-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-md-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-md-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-md-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-md-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-md-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-md-0 {\n    padding-right: 0 !important;\n  }\n  .ps-md-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-md-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-md-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-md-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-md-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-md-0 {\n    gap: 0 !important;\n  }\n  .gap-md-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-md-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-md-3 {\n    gap: 1rem !important;\n  }\n  .gap-md-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-md-5 {\n    gap: 3rem !important;\n  }\n  .text-md-start {\n    text-align: right !important;\n  }\n  .text-md-end {\n    text-align: left !important;\n  }\n  .text-md-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 992px) {\n  .float-lg-start {\n    float: right !important;\n  }\n  .float-lg-end {\n    float: left !important;\n  }\n  .float-lg-none {\n    float: none !important;\n  }\n  .d-lg-inline {\n    display: inline !important;\n  }\n  .d-lg-inline-block {\n    display: inline-block !important;\n  }\n  .d-lg-block {\n    display: block !important;\n  }\n  .d-lg-grid {\n    display: grid !important;\n  }\n  .d-lg-table {\n    display: table !important;\n  }\n  .d-lg-table-row {\n    display: table-row !important;\n  }\n  .d-lg-table-cell {\n    display: table-cell !important;\n  }\n  .d-lg-flex {\n    display: flex !important;\n  }\n  .d-lg-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-lg-none {\n    display: none !important;\n  }\n  .flex-lg-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-lg-row {\n    flex-direction: row !important;\n  }\n  .flex-lg-column {\n    flex-direction: column !important;\n  }\n  .flex-lg-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-lg-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-lg-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-lg-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-lg-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-lg-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-lg-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-lg-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-lg-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-lg-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-lg-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-lg-center {\n    justify-content: center !important;\n  }\n  .justify-content-lg-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-lg-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-lg-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-lg-start {\n    align-items: flex-start !important;\n  }\n  .align-items-lg-end {\n    align-items: flex-end !important;\n  }\n  .align-items-lg-center {\n    align-items: center !important;\n  }\n  .align-items-lg-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-lg-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-lg-start {\n    align-content: flex-start !important;\n  }\n  .align-content-lg-end {\n    align-content: flex-end !important;\n  }\n  .align-content-lg-center {\n    align-content: center !important;\n  }\n  .align-content-lg-between {\n    align-content: space-between !important;\n  }\n  .align-content-lg-around {\n    align-content: space-around !important;\n  }\n  .align-content-lg-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-lg-auto {\n    align-self: auto !important;\n  }\n  .align-self-lg-start {\n    align-self: flex-start !important;\n  }\n  .align-self-lg-end {\n    align-self: flex-end !important;\n  }\n  .align-self-lg-center {\n    align-self: center !important;\n  }\n  .align-self-lg-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-lg-stretch {\n    align-self: stretch !important;\n  }\n  .order-lg-first {\n    order: -1 !important;\n  }\n  .order-lg-0 {\n    order: 0 !important;\n  }\n  .order-lg-1 {\n    order: 1 !important;\n  }\n  .order-lg-2 {\n    order: 2 !important;\n  }\n  .order-lg-3 {\n    order: 3 !important;\n  }\n  .order-lg-4 {\n    order: 4 !important;\n  }\n  .order-lg-5 {\n    order: 5 !important;\n  }\n  .order-lg-last {\n    order: 6 !important;\n  }\n  .m-lg-0 {\n    margin: 0 !important;\n  }\n  .m-lg-1 {\n    margin: 0.25rem !important;\n  }\n  .m-lg-2 {\n    margin: 0.5rem !important;\n  }\n  .m-lg-3 {\n    margin: 1rem !important;\n  }\n  .m-lg-4 {\n    margin: 1.5rem !important;\n  }\n  .m-lg-5 {\n    margin: 3rem !important;\n  }\n  .m-lg-auto {\n    margin: auto !important;\n  }\n  .mx-lg-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-lg-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-lg-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-lg-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-lg-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-lg-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-lg-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-lg-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-lg-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-lg-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-lg-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-lg-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-lg-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-lg-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-lg-0 {\n    margin-top: 0 !important;\n  }\n  .mt-lg-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-lg-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-lg-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-lg-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-lg-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-lg-auto {\n    margin-top: auto !important;\n  }\n  .me-lg-0 {\n    margin-left: 0 !important;\n  }\n  .me-lg-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-lg-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-lg-3 {\n    margin-left: 1rem !important;\n  }\n  .me-lg-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-lg-5 {\n    margin-left: 3rem !important;\n  }\n  .me-lg-auto {\n    margin-left: auto !important;\n  }\n  .mb-lg-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-lg-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-lg-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-lg-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-lg-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-lg-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-lg-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-lg-0 {\n    margin-right: 0 !important;\n  }\n  .ms-lg-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-lg-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-lg-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-lg-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-lg-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-lg-auto {\n    margin-right: auto !important;\n  }\n  .p-lg-0 {\n    padding: 0 !important;\n  }\n  .p-lg-1 {\n    padding: 0.25rem !important;\n  }\n  .p-lg-2 {\n    padding: 0.5rem !important;\n  }\n  .p-lg-3 {\n    padding: 1rem !important;\n  }\n  .p-lg-4 {\n    padding: 1.5rem !important;\n  }\n  .p-lg-5 {\n    padding: 3rem !important;\n  }\n  .px-lg-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-lg-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-lg-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-lg-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-lg-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-lg-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-lg-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-lg-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-lg-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-lg-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-lg-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-lg-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-lg-0 {\n    padding-top: 0 !important;\n  }\n  .pt-lg-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-lg-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-lg-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-lg-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-lg-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-lg-0 {\n    padding-left: 0 !important;\n  }\n  .pe-lg-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-lg-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-lg-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-lg-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-lg-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-lg-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-lg-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-lg-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-lg-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-lg-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-lg-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-lg-0 {\n    padding-right: 0 !important;\n  }\n  .ps-lg-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-lg-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-lg-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-lg-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-lg-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-lg-0 {\n    gap: 0 !important;\n  }\n  .gap-lg-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-lg-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-lg-3 {\n    gap: 1rem !important;\n  }\n  .gap-lg-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-lg-5 {\n    gap: 3rem !important;\n  }\n  .text-lg-start {\n    text-align: right !important;\n  }\n  .text-lg-end {\n    text-align: left !important;\n  }\n  .text-lg-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .float-xl-start {\n    float: right !important;\n  }\n  .float-xl-end {\n    float: left !important;\n  }\n  .float-xl-none {\n    float: none !important;\n  }\n  .d-xl-inline {\n    display: inline !important;\n  }\n  .d-xl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xl-block {\n    display: block !important;\n  }\n  .d-xl-grid {\n    display: grid !important;\n  }\n  .d-xl-table {\n    display: table !important;\n  }\n  .d-xl-table-row {\n    display: table-row !important;\n  }\n  .d-xl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xl-flex {\n    display: flex !important;\n  }\n  .d-xl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xl-none {\n    display: none !important;\n  }\n  .flex-xl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xl-row {\n    flex-direction: row !important;\n  }\n  .flex-xl-column {\n    flex-direction: column !important;\n  }\n  .flex-xl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xl-center {\n    align-items: center !important;\n  }\n  .align-items-xl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xl-center {\n    align-content: center !important;\n  }\n  .align-content-xl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xl-center {\n    align-self: center !important;\n  }\n  .align-self-xl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xl-first {\n    order: -1 !important;\n  }\n  .order-xl-0 {\n    order: 0 !important;\n  }\n  .order-xl-1 {\n    order: 1 !important;\n  }\n  .order-xl-2 {\n    order: 2 !important;\n  }\n  .order-xl-3 {\n    order: 3 !important;\n  }\n  .order-xl-4 {\n    order: 4 !important;\n  }\n  .order-xl-5 {\n    order: 5 !important;\n  }\n  .order-xl-last {\n    order: 6 !important;\n  }\n  .m-xl-0 {\n    margin: 0 !important;\n  }\n  .m-xl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xl-3 {\n    margin: 1rem !important;\n  }\n  .m-xl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xl-5 {\n    margin: 3rem !important;\n  }\n  .m-xl-auto {\n    margin: auto !important;\n  }\n  .mx-xl-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-xl-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-xl-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-xl-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-xl-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-xl-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-xl-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-xl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xl-auto {\n    margin-top: auto !important;\n  }\n  .me-xl-0 {\n    margin-left: 0 !important;\n  }\n  .me-xl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-xl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-xl-3 {\n    margin-left: 1rem !important;\n  }\n  .me-xl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-xl-5 {\n    margin-left: 3rem !important;\n  }\n  .me-xl-auto {\n    margin-left: auto !important;\n  }\n  .mb-xl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xl-0 {\n    margin-right: 0 !important;\n  }\n  .ms-xl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-xl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-xl-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-xl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-xl-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-xl-auto {\n    margin-right: auto !important;\n  }\n  .p-xl-0 {\n    padding: 0 !important;\n  }\n  .p-xl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xl-3 {\n    padding: 1rem !important;\n  }\n  .p-xl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xl-5 {\n    padding: 3rem !important;\n  }\n  .px-xl-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-xl-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-xl-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-xl-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-xl-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-xl-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-xl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xl-0 {\n    padding-left: 0 !important;\n  }\n  .pe-xl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-xl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-xl-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-xl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-xl-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-xl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xl-0 {\n    padding-right: 0 !important;\n  }\n  .ps-xl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-xl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-xl-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-xl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-xl-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-xl-0 {\n    gap: 0 !important;\n  }\n  .gap-xl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xl-5 {\n    gap: 3rem !important;\n  }\n  .text-xl-start {\n    text-align: right !important;\n  }\n  .text-xl-end {\n    text-align: left !important;\n  }\n  .text-xl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1400px) {\n  .float-xxl-start {\n    float: right !important;\n  }\n  .float-xxl-end {\n    float: left !important;\n  }\n  .float-xxl-none {\n    float: none !important;\n  }\n  .d-xxl-inline {\n    display: inline !important;\n  }\n  .d-xxl-inline-block {\n    display: inline-block !important;\n  }\n  .d-xxl-block {\n    display: block !important;\n  }\n  .d-xxl-grid {\n    display: grid !important;\n  }\n  .d-xxl-table {\n    display: table !important;\n  }\n  .d-xxl-table-row {\n    display: table-row !important;\n  }\n  .d-xxl-table-cell {\n    display: table-cell !important;\n  }\n  .d-xxl-flex {\n    display: flex !important;\n  }\n  .d-xxl-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-xxl-none {\n    display: none !important;\n  }\n  .flex-xxl-fill {\n    flex: 1 1 auto !important;\n  }\n  .flex-xxl-row {\n    flex-direction: row !important;\n  }\n  .flex-xxl-column {\n    flex-direction: column !important;\n  }\n  .flex-xxl-row-reverse {\n    flex-direction: row-reverse !important;\n  }\n  .flex-xxl-column-reverse {\n    flex-direction: column-reverse !important;\n  }\n  .flex-xxl-grow-0 {\n    flex-grow: 0 !important;\n  }\n  .flex-xxl-grow-1 {\n    flex-grow: 1 !important;\n  }\n  .flex-xxl-shrink-0 {\n    flex-shrink: 0 !important;\n  }\n  .flex-xxl-shrink-1 {\n    flex-shrink: 1 !important;\n  }\n  .flex-xxl-wrap {\n    flex-wrap: wrap !important;\n  }\n  .flex-xxl-nowrap {\n    flex-wrap: nowrap !important;\n  }\n  .flex-xxl-wrap-reverse {\n    flex-wrap: wrap-reverse !important;\n  }\n  .justify-content-xxl-start {\n    justify-content: flex-start !important;\n  }\n  .justify-content-xxl-end {\n    justify-content: flex-end !important;\n  }\n  .justify-content-xxl-center {\n    justify-content: center !important;\n  }\n  .justify-content-xxl-between {\n    justify-content: space-between !important;\n  }\n  .justify-content-xxl-around {\n    justify-content: space-around !important;\n  }\n  .justify-content-xxl-evenly {\n    justify-content: space-evenly !important;\n  }\n  .align-items-xxl-start {\n    align-items: flex-start !important;\n  }\n  .align-items-xxl-end {\n    align-items: flex-end !important;\n  }\n  .align-items-xxl-center {\n    align-items: center !important;\n  }\n  .align-items-xxl-baseline {\n    align-items: baseline !important;\n  }\n  .align-items-xxl-stretch {\n    align-items: stretch !important;\n  }\n  .align-content-xxl-start {\n    align-content: flex-start !important;\n  }\n  .align-content-xxl-end {\n    align-content: flex-end !important;\n  }\n  .align-content-xxl-center {\n    align-content: center !important;\n  }\n  .align-content-xxl-between {\n    align-content: space-between !important;\n  }\n  .align-content-xxl-around {\n    align-content: space-around !important;\n  }\n  .align-content-xxl-stretch {\n    align-content: stretch !important;\n  }\n  .align-self-xxl-auto {\n    align-self: auto !important;\n  }\n  .align-self-xxl-start {\n    align-self: flex-start !important;\n  }\n  .align-self-xxl-end {\n    align-self: flex-end !important;\n  }\n  .align-self-xxl-center {\n    align-self: center !important;\n  }\n  .align-self-xxl-baseline {\n    align-self: baseline !important;\n  }\n  .align-self-xxl-stretch {\n    align-self: stretch !important;\n  }\n  .order-xxl-first {\n    order: -1 !important;\n  }\n  .order-xxl-0 {\n    order: 0 !important;\n  }\n  .order-xxl-1 {\n    order: 1 !important;\n  }\n  .order-xxl-2 {\n    order: 2 !important;\n  }\n  .order-xxl-3 {\n    order: 3 !important;\n  }\n  .order-xxl-4 {\n    order: 4 !important;\n  }\n  .order-xxl-5 {\n    order: 5 !important;\n  }\n  .order-xxl-last {\n    order: 6 !important;\n  }\n  .m-xxl-0 {\n    margin: 0 !important;\n  }\n  .m-xxl-1 {\n    margin: 0.25rem !important;\n  }\n  .m-xxl-2 {\n    margin: 0.5rem !important;\n  }\n  .m-xxl-3 {\n    margin: 1rem !important;\n  }\n  .m-xxl-4 {\n    margin: 1.5rem !important;\n  }\n  .m-xxl-5 {\n    margin: 3rem !important;\n  }\n  .m-xxl-auto {\n    margin: auto !important;\n  }\n  .mx-xxl-0 {\n    margin-left: 0 !important;\n    margin-right: 0 !important;\n  }\n  .mx-xxl-1 {\n    margin-left: 0.25rem !important;\n    margin-right: 0.25rem !important;\n  }\n  .mx-xxl-2 {\n    margin-left: 0.5rem !important;\n    margin-right: 0.5rem !important;\n  }\n  .mx-xxl-3 {\n    margin-left: 1rem !important;\n    margin-right: 1rem !important;\n  }\n  .mx-xxl-4 {\n    margin-left: 1.5rem !important;\n    margin-right: 1.5rem !important;\n  }\n  .mx-xxl-5 {\n    margin-left: 3rem !important;\n    margin-right: 3rem !important;\n  }\n  .mx-xxl-auto {\n    margin-left: auto !important;\n    margin-right: auto !important;\n  }\n  .my-xxl-0 {\n    margin-top: 0 !important;\n    margin-bottom: 0 !important;\n  }\n  .my-xxl-1 {\n    margin-top: 0.25rem !important;\n    margin-bottom: 0.25rem !important;\n  }\n  .my-xxl-2 {\n    margin-top: 0.5rem !important;\n    margin-bottom: 0.5rem !important;\n  }\n  .my-xxl-3 {\n    margin-top: 1rem !important;\n    margin-bottom: 1rem !important;\n  }\n  .my-xxl-4 {\n    margin-top: 1.5rem !important;\n    margin-bottom: 1.5rem !important;\n  }\n  .my-xxl-5 {\n    margin-top: 3rem !important;\n    margin-bottom: 3rem !important;\n  }\n  .my-xxl-auto {\n    margin-top: auto !important;\n    margin-bottom: auto !important;\n  }\n  .mt-xxl-0 {\n    margin-top: 0 !important;\n  }\n  .mt-xxl-1 {\n    margin-top: 0.25rem !important;\n  }\n  .mt-xxl-2 {\n    margin-top: 0.5rem !important;\n  }\n  .mt-xxl-3 {\n    margin-top: 1rem !important;\n  }\n  .mt-xxl-4 {\n    margin-top: 1.5rem !important;\n  }\n  .mt-xxl-5 {\n    margin-top: 3rem !important;\n  }\n  .mt-xxl-auto {\n    margin-top: auto !important;\n  }\n  .me-xxl-0 {\n    margin-left: 0 !important;\n  }\n  .me-xxl-1 {\n    margin-left: 0.25rem !important;\n  }\n  .me-xxl-2 {\n    margin-left: 0.5rem !important;\n  }\n  .me-xxl-3 {\n    margin-left: 1rem !important;\n  }\n  .me-xxl-4 {\n    margin-left: 1.5rem !important;\n  }\n  .me-xxl-5 {\n    margin-left: 3rem !important;\n  }\n  .me-xxl-auto {\n    margin-left: auto !important;\n  }\n  .mb-xxl-0 {\n    margin-bottom: 0 !important;\n  }\n  .mb-xxl-1 {\n    margin-bottom: 0.25rem !important;\n  }\n  .mb-xxl-2 {\n    margin-bottom: 0.5rem !important;\n  }\n  .mb-xxl-3 {\n    margin-bottom: 1rem !important;\n  }\n  .mb-xxl-4 {\n    margin-bottom: 1.5rem !important;\n  }\n  .mb-xxl-5 {\n    margin-bottom: 3rem !important;\n  }\n  .mb-xxl-auto {\n    margin-bottom: auto !important;\n  }\n  .ms-xxl-0 {\n    margin-right: 0 !important;\n  }\n  .ms-xxl-1 {\n    margin-right: 0.25rem !important;\n  }\n  .ms-xxl-2 {\n    margin-right: 0.5rem !important;\n  }\n  .ms-xxl-3 {\n    margin-right: 1rem !important;\n  }\n  .ms-xxl-4 {\n    margin-right: 1.5rem !important;\n  }\n  .ms-xxl-5 {\n    margin-right: 3rem !important;\n  }\n  .ms-xxl-auto {\n    margin-right: auto !important;\n  }\n  .p-xxl-0 {\n    padding: 0 !important;\n  }\n  .p-xxl-1 {\n    padding: 0.25rem !important;\n  }\n  .p-xxl-2 {\n    padding: 0.5rem !important;\n  }\n  .p-xxl-3 {\n    padding: 1rem !important;\n  }\n  .p-xxl-4 {\n    padding: 1.5rem !important;\n  }\n  .p-xxl-5 {\n    padding: 3rem !important;\n  }\n  .px-xxl-0 {\n    padding-left: 0 !important;\n    padding-right: 0 !important;\n  }\n  .px-xxl-1 {\n    padding-left: 0.25rem !important;\n    padding-right: 0.25rem !important;\n  }\n  .px-xxl-2 {\n    padding-left: 0.5rem !important;\n    padding-right: 0.5rem !important;\n  }\n  .px-xxl-3 {\n    padding-left: 1rem !important;\n    padding-right: 1rem !important;\n  }\n  .px-xxl-4 {\n    padding-left: 1.5rem !important;\n    padding-right: 1.5rem !important;\n  }\n  .px-xxl-5 {\n    padding-left: 3rem !important;\n    padding-right: 3rem !important;\n  }\n  .py-xxl-0 {\n    padding-top: 0 !important;\n    padding-bottom: 0 !important;\n  }\n  .py-xxl-1 {\n    padding-top: 0.25rem !important;\n    padding-bottom: 0.25rem !important;\n  }\n  .py-xxl-2 {\n    padding-top: 0.5rem !important;\n    padding-bottom: 0.5rem !important;\n  }\n  .py-xxl-3 {\n    padding-top: 1rem !important;\n    padding-bottom: 1rem !important;\n  }\n  .py-xxl-4 {\n    padding-top: 1.5rem !important;\n    padding-bottom: 1.5rem !important;\n  }\n  .py-xxl-5 {\n    padding-top: 3rem !important;\n    padding-bottom: 3rem !important;\n  }\n  .pt-xxl-0 {\n    padding-top: 0 !important;\n  }\n  .pt-xxl-1 {\n    padding-top: 0.25rem !important;\n  }\n  .pt-xxl-2 {\n    padding-top: 0.5rem !important;\n  }\n  .pt-xxl-3 {\n    padding-top: 1rem !important;\n  }\n  .pt-xxl-4 {\n    padding-top: 1.5rem !important;\n  }\n  .pt-xxl-5 {\n    padding-top: 3rem !important;\n  }\n  .pe-xxl-0 {\n    padding-left: 0 !important;\n  }\n  .pe-xxl-1 {\n    padding-left: 0.25rem !important;\n  }\n  .pe-xxl-2 {\n    padding-left: 0.5rem !important;\n  }\n  .pe-xxl-3 {\n    padding-left: 1rem !important;\n  }\n  .pe-xxl-4 {\n    padding-left: 1.5rem !important;\n  }\n  .pe-xxl-5 {\n    padding-left: 3rem !important;\n  }\n  .pb-xxl-0 {\n    padding-bottom: 0 !important;\n  }\n  .pb-xxl-1 {\n    padding-bottom: 0.25rem !important;\n  }\n  .pb-xxl-2 {\n    padding-bottom: 0.5rem !important;\n  }\n  .pb-xxl-3 {\n    padding-bottom: 1rem !important;\n  }\n  .pb-xxl-4 {\n    padding-bottom: 1.5rem !important;\n  }\n  .pb-xxl-5 {\n    padding-bottom: 3rem !important;\n  }\n  .ps-xxl-0 {\n    padding-right: 0 !important;\n  }\n  .ps-xxl-1 {\n    padding-right: 0.25rem !important;\n  }\n  .ps-xxl-2 {\n    padding-right: 0.5rem !important;\n  }\n  .ps-xxl-3 {\n    padding-right: 1rem !important;\n  }\n  .ps-xxl-4 {\n    padding-right: 1.5rem !important;\n  }\n  .ps-xxl-5 {\n    padding-right: 3rem !important;\n  }\n  .gap-xxl-0 {\n    gap: 0 !important;\n  }\n  .gap-xxl-1 {\n    gap: 0.25rem !important;\n  }\n  .gap-xxl-2 {\n    gap: 0.5rem !important;\n  }\n  .gap-xxl-3 {\n    gap: 1rem !important;\n  }\n  .gap-xxl-4 {\n    gap: 1.5rem !important;\n  }\n  .gap-xxl-5 {\n    gap: 3rem !important;\n  }\n  .text-xxl-start {\n    text-align: right !important;\n  }\n  .text-xxl-end {\n    text-align: left !important;\n  }\n  .text-xxl-center {\n    text-align: center !important;\n  }\n}\n@media (min-width: 1200px) {\n  .fs-1 {\n    font-size: 2.5rem !important;\n  }\n  .fs-2 {\n    font-size: 2rem !important;\n  }\n  .fs-3 {\n    font-size: 1.75rem !important;\n  }\n  .fs-4 {\n    font-size: 1.5rem !important;\n  }\n}\n@media print {\n  .d-print-inline {\n    display: inline !important;\n  }\n  .d-print-inline-block {\n    display: inline-block !important;\n  }\n  .d-print-block {\n    display: block !important;\n  }\n  .d-print-grid {\n    display: grid !important;\n  }\n  .d-print-table {\n    display: table !important;\n  }\n  .d-print-table-row {\n    display: table-row !important;\n  }\n  .d-print-table-cell {\n    display: table-cell !important;\n  }\n  .d-print-flex {\n    display: flex !important;\n  }\n  .d-print-inline-flex {\n    display: inline-flex !important;\n  }\n  .d-print-none {\n    display: none !important;\n  }\n}\n/*# sourceMappingURL=bootstrap.rtl.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/js/bootstrap.bundle.js",
    "content": "/*!\n  * Bootstrap v5.2.3 (https://getbootstrap.com/)\n  * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory());\n})(this, (function () { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/index.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  const MAX_UID = 1000000;\n  const MILLISECONDS_MULTIPLIER = 1000;\n  const TRANSITION_END = 'transitionend'; // Shout-out Angus Croll (https://goo.gl/pxwQGp)\n\n  const toType = object => {\n    if (object === null || object === undefined) {\n      return `${object}`;\n    }\n\n    return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n  };\n  /**\n   * Public Util API\n   */\n\n\n  const getUID = prefix => {\n    do {\n      prefix += Math.floor(Math.random() * MAX_UID);\n    } while (document.getElementById(prefix));\n\n    return prefix;\n  };\n\n  const getSelector = element => {\n    let selector = element.getAttribute('data-bs-target');\n\n    if (!selector || selector === '#') {\n      let hrefAttribute = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,\n      // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n      // `document.querySelector` will rightfully complain it is invalid.\n      // See https://github.com/twbs/bootstrap/issues/32273\n\n      if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n        return null;\n      } // Just in case some CMS puts out a full URL with the anchor appended\n\n\n      if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n        hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n      }\n\n      selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n    }\n\n    return selector;\n  };\n\n  const getSelectorFromElement = element => {\n    const selector = getSelector(element);\n\n    if (selector) {\n      return document.querySelector(selector) ? selector : null;\n    }\n\n    return null;\n  };\n\n  const getElementFromSelector = element => {\n    const selector = getSelector(element);\n    return selector ? document.querySelector(selector) : null;\n  };\n\n  const getTransitionDurationFromElement = element => {\n    if (!element) {\n      return 0;\n    } // Get transition-duration of the element\n\n\n    let {\n      transitionDuration,\n      transitionDelay\n    } = window.getComputedStyle(element);\n    const floatTransitionDuration = Number.parseFloat(transitionDuration);\n    const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found\n\n    if (!floatTransitionDuration && !floatTransitionDelay) {\n      return 0;\n    } // If multiple durations are defined, take the first\n\n\n    transitionDuration = transitionDuration.split(',')[0];\n    transitionDelay = transitionDelay.split(',')[0];\n    return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n  };\n\n  const triggerTransitionEnd = element => {\n    element.dispatchEvent(new Event(TRANSITION_END));\n  };\n\n  const isElement$1 = object => {\n    if (!object || typeof object !== 'object') {\n      return false;\n    }\n\n    if (typeof object.jquery !== 'undefined') {\n      object = object[0];\n    }\n\n    return typeof object.nodeType !== 'undefined';\n  };\n\n  const getElement = object => {\n    // it's a jQuery object or a node element\n    if (isElement$1(object)) {\n      return object.jquery ? object[0] : object;\n    }\n\n    if (typeof object === 'string' && object.length > 0) {\n      return document.querySelector(object);\n    }\n\n    return null;\n  };\n\n  const isVisible = element => {\n    if (!isElement$1(element) || element.getClientRects().length === 0) {\n      return false;\n    }\n\n    const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; // Handle `details` element as its content may falsie appear visible when it is closed\n\n    const closedDetails = element.closest('details:not([open])');\n\n    if (!closedDetails) {\n      return elementIsVisible;\n    }\n\n    if (closedDetails !== element) {\n      const summary = element.closest('summary');\n\n      if (summary && summary.parentNode !== closedDetails) {\n        return false;\n      }\n\n      if (summary === null) {\n        return false;\n      }\n    }\n\n    return elementIsVisible;\n  };\n\n  const isDisabled = element => {\n    if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n      return true;\n    }\n\n    if (element.classList.contains('disabled')) {\n      return true;\n    }\n\n    if (typeof element.disabled !== 'undefined') {\n      return element.disabled;\n    }\n\n    return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n  };\n\n  const findShadowRoot = element => {\n    if (!document.documentElement.attachShadow) {\n      return null;\n    } // Can find the shadow root otherwise it'll return the document\n\n\n    if (typeof element.getRootNode === 'function') {\n      const root = element.getRootNode();\n      return root instanceof ShadowRoot ? root : null;\n    }\n\n    if (element instanceof ShadowRoot) {\n      return element;\n    } // when we don't find a shadow root\n\n\n    if (!element.parentNode) {\n      return null;\n    }\n\n    return findShadowRoot(element.parentNode);\n  };\n\n  const noop = () => {};\n  /**\n   * Trick to restart an element's animation\n   *\n   * @param {HTMLElement} element\n   * @return void\n   *\n   * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n   */\n\n\n  const reflow = element => {\n    element.offsetHeight; // eslint-disable-line no-unused-expressions\n  };\n\n  const getjQuery = () => {\n    if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n      return window.jQuery;\n    }\n\n    return null;\n  };\n\n  const DOMContentLoadedCallbacks = [];\n\n  const onDOMContentLoaded = callback => {\n    if (document.readyState === 'loading') {\n      // add listener on the first call when the document is in loading state\n      if (!DOMContentLoadedCallbacks.length) {\n        document.addEventListener('DOMContentLoaded', () => {\n          for (const callback of DOMContentLoadedCallbacks) {\n            callback();\n          }\n        });\n      }\n\n      DOMContentLoadedCallbacks.push(callback);\n    } else {\n      callback();\n    }\n  };\n\n  const isRTL = () => document.documentElement.dir === 'rtl';\n\n  const defineJQueryPlugin = plugin => {\n    onDOMContentLoaded(() => {\n      const $ = getjQuery();\n      /* istanbul ignore if */\n\n      if ($) {\n        const name = plugin.NAME;\n        const JQUERY_NO_CONFLICT = $.fn[name];\n        $.fn[name] = plugin.jQueryInterface;\n        $.fn[name].Constructor = plugin;\n\n        $.fn[name].noConflict = () => {\n          $.fn[name] = JQUERY_NO_CONFLICT;\n          return plugin.jQueryInterface;\n        };\n      }\n    });\n  };\n\n  const execute = callback => {\n    if (typeof callback === 'function') {\n      callback();\n    }\n  };\n\n  const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n    if (!waitForTransition) {\n      execute(callback);\n      return;\n    }\n\n    const durationPadding = 5;\n    const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n    let called = false;\n\n    const handler = ({\n      target\n    }) => {\n      if (target !== transitionElement) {\n        return;\n      }\n\n      called = true;\n      transitionElement.removeEventListener(TRANSITION_END, handler);\n      execute(callback);\n    };\n\n    transitionElement.addEventListener(TRANSITION_END, handler);\n    setTimeout(() => {\n      if (!called) {\n        triggerTransitionEnd(transitionElement);\n      }\n    }, emulatedDuration);\n  };\n  /**\n   * Return the previous/next element of a list.\n   *\n   * @param {array} list    The list of elements\n   * @param activeElement   The active element\n   * @param shouldGetNext   Choose to get next or previous element\n   * @param isCycleAllowed\n   * @return {Element|elem} The proper element\n   */\n\n\n  const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n    const listLength = list.length;\n    let index = list.indexOf(activeElement); // if the element does not exist in the list return an element\n    // depending on the direction and if cycle is allowed\n\n    if (index === -1) {\n      return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n    }\n\n    index += shouldGetNext ? 1 : -1;\n\n    if (isCycleAllowed) {\n      index = (index + listLength) % listLength;\n    }\n\n    return list[Math.max(0, Math.min(index, listLength - 1))];\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/event-handler.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\n  const stripNameRegex = /\\..*/;\n  const stripUidRegex = /::\\d+$/;\n  const eventRegistry = {}; // Events storage\n\n  let uidEvent = 1;\n  const customEvents = {\n    mouseenter: 'mouseover',\n    mouseleave: 'mouseout'\n  };\n  const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n  /**\n   * Private methods\n   */\n\n  function makeEventUid(element, uid) {\n    return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n  }\n\n  function getElementEvents(element) {\n    const uid = makeEventUid(element);\n    element.uidEvent = uid;\n    eventRegistry[uid] = eventRegistry[uid] || {};\n    return eventRegistry[uid];\n  }\n\n  function bootstrapHandler(element, fn) {\n    return function handler(event) {\n      hydrateObj(event, {\n        delegateTarget: element\n      });\n\n      if (handler.oneOff) {\n        EventHandler.off(element, event.type, fn);\n      }\n\n      return fn.apply(element, [event]);\n    };\n  }\n\n  function bootstrapDelegationHandler(element, selector, fn) {\n    return function handler(event) {\n      const domElements = element.querySelectorAll(selector);\n\n      for (let {\n        target\n      } = event; target && target !== this; target = target.parentNode) {\n        for (const domElement of domElements) {\n          if (domElement !== target) {\n            continue;\n          }\n\n          hydrateObj(event, {\n            delegateTarget: target\n          });\n\n          if (handler.oneOff) {\n            EventHandler.off(element, event.type, selector, fn);\n          }\n\n          return fn.apply(target, [event]);\n        }\n      }\n    };\n  }\n\n  function findHandler(events, callable, delegationSelector = null) {\n    return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n  }\n\n  function normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n    const isDelegated = typeof handler === 'string'; // todo: tooltip passes `false` instead of selector, so we need to check\n\n    const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n    let typeEvent = getTypeEvent(originalTypeEvent);\n\n    if (!nativeEvents.has(typeEvent)) {\n      typeEvent = originalTypeEvent;\n    }\n\n    return [isDelegated, callable, typeEvent];\n  }\n\n  function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n    if (typeof originalTypeEvent !== 'string' || !element) {\n      return;\n    }\n\n    let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n    // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n\n    if (originalTypeEvent in customEvents) {\n      const wrapFunction = fn => {\n        return function (event) {\n          if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n            return fn.call(this, event);\n          }\n        };\n      };\n\n      callable = wrapFunction(callable);\n    }\n\n    const events = getElementEvents(element);\n    const handlers = events[typeEvent] || (events[typeEvent] = {});\n    const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n\n    if (previousFunction) {\n      previousFunction.oneOff = previousFunction.oneOff && oneOff;\n      return;\n    }\n\n    const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n    const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n    fn.delegationSelector = isDelegated ? handler : null;\n    fn.callable = callable;\n    fn.oneOff = oneOff;\n    fn.uidEvent = uid;\n    handlers[uid] = fn;\n    element.addEventListener(typeEvent, fn, isDelegated);\n  }\n\n  function removeHandler(element, events, typeEvent, handler, delegationSelector) {\n    const fn = findHandler(events[typeEvent], handler, delegationSelector);\n\n    if (!fn) {\n      return;\n    }\n\n    element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n    delete events[typeEvent][fn.uidEvent];\n  }\n\n  function removeNamespacedHandlers(element, events, typeEvent, namespace) {\n    const storeElementEvent = events[typeEvent] || {};\n\n    for (const handlerKey of Object.keys(storeElementEvent)) {\n      if (handlerKey.includes(namespace)) {\n        const event = storeElementEvent[handlerKey];\n        removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n      }\n    }\n  }\n\n  function getTypeEvent(event) {\n    // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n    event = event.replace(stripNameRegex, '');\n    return customEvents[event] || event;\n  }\n\n  const EventHandler = {\n    on(element, event, handler, delegationFunction) {\n      addHandler(element, event, handler, delegationFunction, false);\n    },\n\n    one(element, event, handler, delegationFunction) {\n      addHandler(element, event, handler, delegationFunction, true);\n    },\n\n    off(element, originalTypeEvent, handler, delegationFunction) {\n      if (typeof originalTypeEvent !== 'string' || !element) {\n        return;\n      }\n\n      const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n      const inNamespace = typeEvent !== originalTypeEvent;\n      const events = getElementEvents(element);\n      const storeElementEvent = events[typeEvent] || {};\n      const isNamespace = originalTypeEvent.startsWith('.');\n\n      if (typeof callable !== 'undefined') {\n        // Simplest case: handler is passed, remove that listener ONLY.\n        if (!Object.keys(storeElementEvent).length) {\n          return;\n        }\n\n        removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n        return;\n      }\n\n      if (isNamespace) {\n        for (const elementEvent of Object.keys(events)) {\n          removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n        }\n      }\n\n      for (const keyHandlers of Object.keys(storeElementEvent)) {\n        const handlerKey = keyHandlers.replace(stripUidRegex, '');\n\n        if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n          const event = storeElementEvent[keyHandlers];\n          removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n        }\n      }\n    },\n\n    trigger(element, event, args) {\n      if (typeof event !== 'string' || !element) {\n        return null;\n      }\n\n      const $ = getjQuery();\n      const typeEvent = getTypeEvent(event);\n      const inNamespace = event !== typeEvent;\n      let jQueryEvent = null;\n      let bubbles = true;\n      let nativeDispatch = true;\n      let defaultPrevented = false;\n\n      if (inNamespace && $) {\n        jQueryEvent = $.Event(event, args);\n        $(element).trigger(jQueryEvent);\n        bubbles = !jQueryEvent.isPropagationStopped();\n        nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n        defaultPrevented = jQueryEvent.isDefaultPrevented();\n      }\n\n      let evt = new Event(event, {\n        bubbles,\n        cancelable: true\n      });\n      evt = hydrateObj(evt, args);\n\n      if (defaultPrevented) {\n        evt.preventDefault();\n      }\n\n      if (nativeDispatch) {\n        element.dispatchEvent(evt);\n      }\n\n      if (evt.defaultPrevented && jQueryEvent) {\n        jQueryEvent.preventDefault();\n      }\n\n      return evt;\n    }\n\n  };\n\n  function hydrateObj(obj, meta) {\n    for (const [key, value] of Object.entries(meta || {})) {\n      try {\n        obj[key] = value;\n      } catch (_unused) {\n        Object.defineProperty(obj, key, {\n          configurable: true,\n\n          get() {\n            return value;\n          }\n\n        });\n      }\n    }\n\n    return obj;\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/data.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  /**\n   * Constants\n   */\n  const elementMap = new Map();\n  const Data = {\n    set(element, key, instance) {\n      if (!elementMap.has(element)) {\n        elementMap.set(element, new Map());\n      }\n\n      const instanceMap = elementMap.get(element); // make it clear we only want one instance per element\n      // can be removed later when multiple key/instances are fine to be used\n\n      if (!instanceMap.has(key) && instanceMap.size !== 0) {\n        // eslint-disable-next-line no-console\n        console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n        return;\n      }\n\n      instanceMap.set(key, instance);\n    },\n\n    get(element, key) {\n      if (elementMap.has(element)) {\n        return elementMap.get(element).get(key) || null;\n      }\n\n      return null;\n    },\n\n    remove(element, key) {\n      if (!elementMap.has(element)) {\n        return;\n      }\n\n      const instanceMap = elementMap.get(element);\n      instanceMap.delete(key); // free up element references if there are no instances left for an element\n\n      if (instanceMap.size === 0) {\n        elementMap.delete(element);\n      }\n    }\n\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/manipulator.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  function normalizeData(value) {\n    if (value === 'true') {\n      return true;\n    }\n\n    if (value === 'false') {\n      return false;\n    }\n\n    if (value === Number(value).toString()) {\n      return Number(value);\n    }\n\n    if (value === '' || value === 'null') {\n      return null;\n    }\n\n    if (typeof value !== 'string') {\n      return value;\n    }\n\n    try {\n      return JSON.parse(decodeURIComponent(value));\n    } catch (_unused) {\n      return value;\n    }\n  }\n\n  function normalizeDataKey(key) {\n    return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n  }\n\n  const Manipulator = {\n    setDataAttribute(element, key, value) {\n      element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n    },\n\n    removeDataAttribute(element, key) {\n      element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n    },\n\n    getDataAttributes(element) {\n      if (!element) {\n        return {};\n      }\n\n      const attributes = {};\n      const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n\n      for (const key of bsKeys) {\n        let pureKey = key.replace(/^bs/, '');\n        pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n        attributes[pureKey] = normalizeData(element.dataset[key]);\n      }\n\n      return attributes;\n    },\n\n    getDataAttribute(element, key) {\n      return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n    }\n\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/config.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Class definition\n   */\n\n  class Config {\n    // Getters\n    static get Default() {\n      return {};\n    }\n\n    static get DefaultType() {\n      return {};\n    }\n\n    static get NAME() {\n      throw new Error('You have to implement the static method \"NAME\", for each component!');\n    }\n\n    _getConfig(config) {\n      config = this._mergeConfigObj(config);\n      config = this._configAfterMerge(config);\n\n      this._typeCheckConfig(config);\n\n      return config;\n    }\n\n    _configAfterMerge(config) {\n      return config;\n    }\n\n    _mergeConfigObj(config, element) {\n      const jsonConfig = isElement$1(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n      return { ...this.constructor.Default,\n        ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n        ...(isElement$1(element) ? Manipulator.getDataAttributes(element) : {}),\n        ...(typeof config === 'object' ? config : {})\n      };\n    }\n\n    _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n      for (const property of Object.keys(configTypes)) {\n        const expectedTypes = configTypes[property];\n        const value = config[property];\n        const valueType = isElement$1(value) ? 'element' : toType(value);\n\n        if (!new RegExp(expectedTypes).test(valueType)) {\n          throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n        }\n      }\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): base-component.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const VERSION = '5.2.3';\n  /**\n   * Class definition\n   */\n\n  class BaseComponent extends Config {\n    constructor(element, config) {\n      super();\n      element = getElement(element);\n\n      if (!element) {\n        return;\n      }\n\n      this._element = element;\n      this._config = this._getConfig(config);\n      Data.set(this._element, this.constructor.DATA_KEY, this);\n    } // Public\n\n\n    dispose() {\n      Data.remove(this._element, this.constructor.DATA_KEY);\n      EventHandler.off(this._element, this.constructor.EVENT_KEY);\n\n      for (const propertyName of Object.getOwnPropertyNames(this)) {\n        this[propertyName] = null;\n      }\n    }\n\n    _queueCallback(callback, element, isAnimated = true) {\n      executeAfterTransition(callback, element, isAnimated);\n    }\n\n    _getConfig(config) {\n      config = this._mergeConfigObj(config, this._element);\n      config = this._configAfterMerge(config);\n\n      this._typeCheckConfig(config);\n\n      return config;\n    } // Static\n\n\n    static getInstance(element) {\n      return Data.get(getElement(element), this.DATA_KEY);\n    }\n\n    static getOrCreateInstance(element, config = {}) {\n      return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n    }\n\n    static get VERSION() {\n      return VERSION;\n    }\n\n    static get DATA_KEY() {\n      return `bs.${this.NAME}`;\n    }\n\n    static get EVENT_KEY() {\n      return `.${this.DATA_KEY}`;\n    }\n\n    static eventName(name) {\n      return `${name}${this.EVENT_KEY}`;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/component-functions.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  const enableDismissTrigger = (component, method = 'hide') => {\n    const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n    const name = component.NAME;\n    EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n      if (['A', 'AREA'].includes(this.tagName)) {\n        event.preventDefault();\n      }\n\n      if (isDisabled(this)) {\n        return;\n      }\n\n      const target = getElementFromSelector(this) || this.closest(`.${name}`);\n      const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n\n      instance[method]();\n    });\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): alert.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$f = 'alert';\n  const DATA_KEY$a = 'bs.alert';\n  const EVENT_KEY$b = `.${DATA_KEY$a}`;\n  const EVENT_CLOSE = `close${EVENT_KEY$b}`;\n  const EVENT_CLOSED = `closed${EVENT_KEY$b}`;\n  const CLASS_NAME_FADE$5 = 'fade';\n  const CLASS_NAME_SHOW$8 = 'show';\n  /**\n   * Class definition\n   */\n\n  class Alert extends BaseComponent {\n    // Getters\n    static get NAME() {\n      return NAME$f;\n    } // Public\n\n\n    close() {\n      const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n\n      if (closeEvent.defaultPrevented) {\n        return;\n      }\n\n      this._element.classList.remove(CLASS_NAME_SHOW$8);\n\n      const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n\n      this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n    } // Private\n\n\n    _destroyElement() {\n      this._element.remove();\n\n      EventHandler.trigger(this._element, EVENT_CLOSED);\n      this.dispose();\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Alert.getOrCreateInstance(this);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config](this);\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  enableDismissTrigger(Alert, 'close');\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Alert);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): button.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$e = 'button';\n  const DATA_KEY$9 = 'bs.button';\n  const EVENT_KEY$a = `.${DATA_KEY$9}`;\n  const DATA_API_KEY$6 = '.data-api';\n  const CLASS_NAME_ACTIVE$3 = 'active';\n  const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\n  const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n  /**\n   * Class definition\n   */\n\n  class Button extends BaseComponent {\n    // Getters\n    static get NAME() {\n      return NAME$e;\n    } // Public\n\n\n    toggle() {\n      // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n      this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Button.getOrCreateInstance(this);\n\n        if (config === 'toggle') {\n          data[config]();\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n    event.preventDefault();\n    const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n    const data = Button.getOrCreateInstance(button);\n    data.toggle();\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Button);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/selector-engine.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const SelectorEngine = {\n    find(selector, element = document.documentElement) {\n      return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n    },\n\n    findOne(selector, element = document.documentElement) {\n      return Element.prototype.querySelector.call(element, selector);\n    },\n\n    children(element, selector) {\n      return [].concat(...element.children).filter(child => child.matches(selector));\n    },\n\n    parents(element, selector) {\n      const parents = [];\n      let ancestor = element.parentNode.closest(selector);\n\n      while (ancestor) {\n        parents.push(ancestor);\n        ancestor = ancestor.parentNode.closest(selector);\n      }\n\n      return parents;\n    },\n\n    prev(element, selector) {\n      let previous = element.previousElementSibling;\n\n      while (previous) {\n        if (previous.matches(selector)) {\n          return [previous];\n        }\n\n        previous = previous.previousElementSibling;\n      }\n\n      return [];\n    },\n\n    // TODO: this is now unused; remove later along with prev()\n    next(element, selector) {\n      let next = element.nextElementSibling;\n\n      while (next) {\n        if (next.matches(selector)) {\n          return [next];\n        }\n\n        next = next.nextElementSibling;\n      }\n\n      return [];\n    },\n\n    focusableChildren(element) {\n      const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n      return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n    }\n\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/swipe.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$d = 'swipe';\n  const EVENT_KEY$9 = '.bs.swipe';\n  const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\n  const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\n  const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\n  const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\n  const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\n  const POINTER_TYPE_TOUCH = 'touch';\n  const POINTER_TYPE_PEN = 'pen';\n  const CLASS_NAME_POINTER_EVENT = 'pointer-event';\n  const SWIPE_THRESHOLD = 40;\n  const Default$c = {\n    endCallback: null,\n    leftCallback: null,\n    rightCallback: null\n  };\n  const DefaultType$c = {\n    endCallback: '(function|null)',\n    leftCallback: '(function|null)',\n    rightCallback: '(function|null)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Swipe extends Config {\n    constructor(element, config) {\n      super();\n      this._element = element;\n\n      if (!element || !Swipe.isSupported()) {\n        return;\n      }\n\n      this._config = this._getConfig(config);\n      this._deltaX = 0;\n      this._supportPointerEvents = Boolean(window.PointerEvent);\n\n      this._initEvents();\n    } // Getters\n\n\n    static get Default() {\n      return Default$c;\n    }\n\n    static get DefaultType() {\n      return DefaultType$c;\n    }\n\n    static get NAME() {\n      return NAME$d;\n    } // Public\n\n\n    dispose() {\n      EventHandler.off(this._element, EVENT_KEY$9);\n    } // Private\n\n\n    _start(event) {\n      if (!this._supportPointerEvents) {\n        this._deltaX = event.touches[0].clientX;\n        return;\n      }\n\n      if (this._eventIsPointerPenTouch(event)) {\n        this._deltaX = event.clientX;\n      }\n    }\n\n    _end(event) {\n      if (this._eventIsPointerPenTouch(event)) {\n        this._deltaX = event.clientX - this._deltaX;\n      }\n\n      this._handleSwipe();\n\n      execute(this._config.endCallback);\n    }\n\n    _move(event) {\n      this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n    }\n\n    _handleSwipe() {\n      const absDeltaX = Math.abs(this._deltaX);\n\n      if (absDeltaX <= SWIPE_THRESHOLD) {\n        return;\n      }\n\n      const direction = absDeltaX / this._deltaX;\n      this._deltaX = 0;\n\n      if (!direction) {\n        return;\n      }\n\n      execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n    }\n\n    _initEvents() {\n      if (this._supportPointerEvents) {\n        EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n        EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n\n        this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n      } else {\n        EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n        EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n        EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n      }\n    }\n\n    _eventIsPointerPenTouch(event) {\n      return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n    } // Static\n\n\n    static isSupported() {\n      return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): carousel.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$c = 'carousel';\n  const DATA_KEY$8 = 'bs.carousel';\n  const EVENT_KEY$8 = `.${DATA_KEY$8}`;\n  const DATA_API_KEY$5 = '.data-api';\n  const ARROW_LEFT_KEY$1 = 'ArrowLeft';\n  const ARROW_RIGHT_KEY$1 = 'ArrowRight';\n  const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\n  const ORDER_NEXT = 'next';\n  const ORDER_PREV = 'prev';\n  const DIRECTION_LEFT = 'left';\n  const DIRECTION_RIGHT = 'right';\n  const EVENT_SLIDE = `slide${EVENT_KEY$8}`;\n  const EVENT_SLID = `slid${EVENT_KEY$8}`;\n  const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\n  const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\n  const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\n  const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\n  const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\n  const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\n  const CLASS_NAME_CAROUSEL = 'carousel';\n  const CLASS_NAME_ACTIVE$2 = 'active';\n  const CLASS_NAME_SLIDE = 'slide';\n  const CLASS_NAME_END = 'carousel-item-end';\n  const CLASS_NAME_START = 'carousel-item-start';\n  const CLASS_NAME_NEXT = 'carousel-item-next';\n  const CLASS_NAME_PREV = 'carousel-item-prev';\n  const SELECTOR_ACTIVE = '.active';\n  const SELECTOR_ITEM = '.carousel-item';\n  const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\n  const SELECTOR_ITEM_IMG = '.carousel-item img';\n  const SELECTOR_INDICATORS = '.carousel-indicators';\n  const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\n  const SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\n  const KEY_TO_DIRECTION = {\n    [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n    [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n  };\n  const Default$b = {\n    interval: 5000,\n    keyboard: true,\n    pause: 'hover',\n    ride: false,\n    touch: true,\n    wrap: true\n  };\n  const DefaultType$b = {\n    interval: '(number|boolean)',\n    // TODO:v6 remove boolean support\n    keyboard: 'boolean',\n    pause: '(string|boolean)',\n    ride: '(boolean|string)',\n    touch: 'boolean',\n    wrap: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Carousel extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._interval = null;\n      this._activeElement = null;\n      this._isSliding = false;\n      this.touchTimeout = null;\n      this._swipeHelper = null;\n      this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n\n      this._addEventListeners();\n\n      if (this._config.ride === CLASS_NAME_CAROUSEL) {\n        this.cycle();\n      }\n    } // Getters\n\n\n    static get Default() {\n      return Default$b;\n    }\n\n    static get DefaultType() {\n      return DefaultType$b;\n    }\n\n    static get NAME() {\n      return NAME$c;\n    } // Public\n\n\n    next() {\n      this._slide(ORDER_NEXT);\n    }\n\n    nextWhenVisible() {\n      // FIXME TODO use `document.visibilityState`\n      // Don't call next when the page isn't visible\n      // or the carousel or its parent isn't visible\n      if (!document.hidden && isVisible(this._element)) {\n        this.next();\n      }\n    }\n\n    prev() {\n      this._slide(ORDER_PREV);\n    }\n\n    pause() {\n      if (this._isSliding) {\n        triggerTransitionEnd(this._element);\n      }\n\n      this._clearInterval();\n    }\n\n    cycle() {\n      this._clearInterval();\n\n      this._updateInterval();\n\n      this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n    }\n\n    _maybeEnableCycle() {\n      if (!this._config.ride) {\n        return;\n      }\n\n      if (this._isSliding) {\n        EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n        return;\n      }\n\n      this.cycle();\n    }\n\n    to(index) {\n      const items = this._getItems();\n\n      if (index > items.length - 1 || index < 0) {\n        return;\n      }\n\n      if (this._isSliding) {\n        EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n        return;\n      }\n\n      const activeIndex = this._getItemIndex(this._getActive());\n\n      if (activeIndex === index) {\n        return;\n      }\n\n      const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n\n      this._slide(order, items[index]);\n    }\n\n    dispose() {\n      if (this._swipeHelper) {\n        this._swipeHelper.dispose();\n      }\n\n      super.dispose();\n    } // Private\n\n\n    _configAfterMerge(config) {\n      config.defaultInterval = config.interval;\n      return config;\n    }\n\n    _addEventListeners() {\n      if (this._config.keyboard) {\n        EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n      }\n\n      if (this._config.pause === 'hover') {\n        EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n        EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n      }\n\n      if (this._config.touch && Swipe.isSupported()) {\n        this._addTouchEventListeners();\n      }\n    }\n\n    _addTouchEventListeners() {\n      for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n        EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n      }\n\n      const endCallBack = () => {\n        if (this._config.pause !== 'hover') {\n          return;\n        } // If it's a touch-enabled device, mouseenter/leave are fired as\n        // part of the mouse compatibility events on first tap - the carousel\n        // would stop cycling until user tapped out of it;\n        // here, we listen for touchend, explicitly pause the carousel\n        // (as if it's the second time we tap on it, mouseenter compat event\n        // is NOT fired) and after a timeout (to allow for mouse compatibility\n        // events to fire) we explicitly restart cycling\n\n\n        this.pause();\n\n        if (this.touchTimeout) {\n          clearTimeout(this.touchTimeout);\n        }\n\n        this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n      };\n\n      const swipeConfig = {\n        leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n        rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n        endCallback: endCallBack\n      };\n      this._swipeHelper = new Swipe(this._element, swipeConfig);\n    }\n\n    _keydown(event) {\n      if (/input|textarea/i.test(event.target.tagName)) {\n        return;\n      }\n\n      const direction = KEY_TO_DIRECTION[event.key];\n\n      if (direction) {\n        event.preventDefault();\n\n        this._slide(this._directionToOrder(direction));\n      }\n    }\n\n    _getItemIndex(element) {\n      return this._getItems().indexOf(element);\n    }\n\n    _setActiveIndicatorElement(index) {\n      if (!this._indicatorsElement) {\n        return;\n      }\n\n      const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n      activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n      activeIndicator.removeAttribute('aria-current');\n      const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n\n      if (newActiveIndicator) {\n        newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n        newActiveIndicator.setAttribute('aria-current', 'true');\n      }\n    }\n\n    _updateInterval() {\n      const element = this._activeElement || this._getActive();\n\n      if (!element) {\n        return;\n      }\n\n      const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n      this._config.interval = elementInterval || this._config.defaultInterval;\n    }\n\n    _slide(order, element = null) {\n      if (this._isSliding) {\n        return;\n      }\n\n      const activeElement = this._getActive();\n\n      const isNext = order === ORDER_NEXT;\n      const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n\n      if (nextElement === activeElement) {\n        return;\n      }\n\n      const nextElementIndex = this._getItemIndex(nextElement);\n\n      const triggerEvent = eventName => {\n        return EventHandler.trigger(this._element, eventName, {\n          relatedTarget: nextElement,\n          direction: this._orderToDirection(order),\n          from: this._getItemIndex(activeElement),\n          to: nextElementIndex\n        });\n      };\n\n      const slideEvent = triggerEvent(EVENT_SLIDE);\n\n      if (slideEvent.defaultPrevented) {\n        return;\n      }\n\n      if (!activeElement || !nextElement) {\n        // Some weirdness is happening, so we bail\n        // todo: change tests that use empty divs to avoid this check\n        return;\n      }\n\n      const isCycling = Boolean(this._interval);\n      this.pause();\n      this._isSliding = true;\n\n      this._setActiveIndicatorElement(nextElementIndex);\n\n      this._activeElement = nextElement;\n      const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n      const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n      nextElement.classList.add(orderClassName);\n      reflow(nextElement);\n      activeElement.classList.add(directionalClassName);\n      nextElement.classList.add(directionalClassName);\n\n      const completeCallBack = () => {\n        nextElement.classList.remove(directionalClassName, orderClassName);\n        nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n        activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n        this._isSliding = false;\n        triggerEvent(EVENT_SLID);\n      };\n\n      this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n\n      if (isCycling) {\n        this.cycle();\n      }\n    }\n\n    _isAnimated() {\n      return this._element.classList.contains(CLASS_NAME_SLIDE);\n    }\n\n    _getActive() {\n      return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n    }\n\n    _getItems() {\n      return SelectorEngine.find(SELECTOR_ITEM, this._element);\n    }\n\n    _clearInterval() {\n      if (this._interval) {\n        clearInterval(this._interval);\n        this._interval = null;\n      }\n    }\n\n    _directionToOrder(direction) {\n      if (isRTL()) {\n        return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n      }\n\n      return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n    }\n\n    _orderToDirection(order) {\n      if (isRTL()) {\n        return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n      }\n\n      return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Carousel.getOrCreateInstance(this, config);\n\n        if (typeof config === 'number') {\n          data.to(config);\n          return;\n        }\n\n        if (typeof config === 'string') {\n          if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n\n          data[config]();\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n    const target = getElementFromSelector(this);\n\n    if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n      return;\n    }\n\n    event.preventDefault();\n    const carousel = Carousel.getOrCreateInstance(target);\n    const slideIndex = this.getAttribute('data-bs-slide-to');\n\n    if (slideIndex) {\n      carousel.to(slideIndex);\n\n      carousel._maybeEnableCycle();\n\n      return;\n    }\n\n    if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n      carousel.next();\n\n      carousel._maybeEnableCycle();\n\n      return;\n    }\n\n    carousel.prev();\n\n    carousel._maybeEnableCycle();\n  });\n  EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n    const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n\n    for (const carousel of carousels) {\n      Carousel.getOrCreateInstance(carousel);\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Carousel);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): collapse.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$b = 'collapse';\n  const DATA_KEY$7 = 'bs.collapse';\n  const EVENT_KEY$7 = `.${DATA_KEY$7}`;\n  const DATA_API_KEY$4 = '.data-api';\n  const EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\n  const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\n  const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\n  const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\n  const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\n  const CLASS_NAME_SHOW$7 = 'show';\n  const CLASS_NAME_COLLAPSE = 'collapse';\n  const CLASS_NAME_COLLAPSING = 'collapsing';\n  const CLASS_NAME_COLLAPSED = 'collapsed';\n  const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\n  const CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\n  const WIDTH = 'width';\n  const HEIGHT = 'height';\n  const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\n  const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\n  const Default$a = {\n    parent: null,\n    toggle: true\n  };\n  const DefaultType$a = {\n    parent: '(null|element)',\n    toggle: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Collapse extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._isTransitioning = false;\n      this._triggerArray = [];\n      const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n\n      for (const elem of toggleList) {\n        const selector = getSelectorFromElement(elem);\n        const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n\n        if (selector !== null && filterElement.length) {\n          this._triggerArray.push(elem);\n        }\n      }\n\n      this._initializeChildren();\n\n      if (!this._config.parent) {\n        this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n      }\n\n      if (this._config.toggle) {\n        this.toggle();\n      }\n    } // Getters\n\n\n    static get Default() {\n      return Default$a;\n    }\n\n    static get DefaultType() {\n      return DefaultType$a;\n    }\n\n    static get NAME() {\n      return NAME$b;\n    } // Public\n\n\n    toggle() {\n      if (this._isShown()) {\n        this.hide();\n      } else {\n        this.show();\n      }\n    }\n\n    show() {\n      if (this._isTransitioning || this._isShown()) {\n        return;\n      }\n\n      let activeChildren = []; // find active children\n\n      if (this._config.parent) {\n        activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n          toggle: false\n        }));\n      }\n\n      if (activeChildren.length && activeChildren[0]._isTransitioning) {\n        return;\n      }\n\n      const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n\n      if (startEvent.defaultPrevented) {\n        return;\n      }\n\n      for (const activeInstance of activeChildren) {\n        activeInstance.hide();\n      }\n\n      const dimension = this._getDimension();\n\n      this._element.classList.remove(CLASS_NAME_COLLAPSE);\n\n      this._element.classList.add(CLASS_NAME_COLLAPSING);\n\n      this._element.style[dimension] = 0;\n\n      this._addAriaAndCollapsedClass(this._triggerArray, true);\n\n      this._isTransitioning = true;\n\n      const complete = () => {\n        this._isTransitioning = false;\n\n        this._element.classList.remove(CLASS_NAME_COLLAPSING);\n\n        this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n\n        this._element.style[dimension] = '';\n        EventHandler.trigger(this._element, EVENT_SHOWN$6);\n      };\n\n      const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n      const scrollSize = `scroll${capitalizedDimension}`;\n\n      this._queueCallback(complete, this._element, true);\n\n      this._element.style[dimension] = `${this._element[scrollSize]}px`;\n    }\n\n    hide() {\n      if (this._isTransitioning || !this._isShown()) {\n        return;\n      }\n\n      const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n\n      if (startEvent.defaultPrevented) {\n        return;\n      }\n\n      const dimension = this._getDimension();\n\n      this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n      reflow(this._element);\n\n      this._element.classList.add(CLASS_NAME_COLLAPSING);\n\n      this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n\n      for (const trigger of this._triggerArray) {\n        const element = getElementFromSelector(trigger);\n\n        if (element && !this._isShown(element)) {\n          this._addAriaAndCollapsedClass([trigger], false);\n        }\n      }\n\n      this._isTransitioning = true;\n\n      const complete = () => {\n        this._isTransitioning = false;\n\n        this._element.classList.remove(CLASS_NAME_COLLAPSING);\n\n        this._element.classList.add(CLASS_NAME_COLLAPSE);\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n      };\n\n      this._element.style[dimension] = '';\n\n      this._queueCallback(complete, this._element, true);\n    }\n\n    _isShown(element = this._element) {\n      return element.classList.contains(CLASS_NAME_SHOW$7);\n    } // Private\n\n\n    _configAfterMerge(config) {\n      config.toggle = Boolean(config.toggle); // Coerce string values\n\n      config.parent = getElement(config.parent);\n      return config;\n    }\n\n    _getDimension() {\n      return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n    }\n\n    _initializeChildren() {\n      if (!this._config.parent) {\n        return;\n      }\n\n      const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n\n      for (const element of children) {\n        const selected = getElementFromSelector(element);\n\n        if (selected) {\n          this._addAriaAndCollapsedClass([element], this._isShown(selected));\n        }\n      }\n    }\n\n    _getFirstLevelChildren(selector) {\n      const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); // remove children if greater depth\n\n      return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n    }\n\n    _addAriaAndCollapsedClass(triggerArray, isOpen) {\n      if (!triggerArray.length) {\n        return;\n      }\n\n      for (const element of triggerArray) {\n        element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n        element.setAttribute('aria-expanded', isOpen);\n      }\n    } // Static\n\n\n    static jQueryInterface(config) {\n      const _config = {};\n\n      if (typeof config === 'string' && /show|hide/.test(config)) {\n        _config.toggle = false;\n      }\n\n      return this.each(function () {\n        const data = Collapse.getOrCreateInstance(this, _config);\n\n        if (typeof config === 'string') {\n          if (typeof data[config] === 'undefined') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n\n          data[config]();\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n    // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n    if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n      event.preventDefault();\n    }\n\n    const selector = getSelectorFromElement(this);\n    const selectorElements = SelectorEngine.find(selector);\n\n    for (const element of selectorElements) {\n      Collapse.getOrCreateInstance(element, {\n        toggle: false\n      }).toggle();\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Collapse);\n\n  var top = 'top';\n  var bottom = 'bottom';\n  var right = 'right';\n  var left = 'left';\n  var auto = 'auto';\n  var basePlacements = [top, bottom, right, left];\n  var start = 'start';\n  var end = 'end';\n  var clippingParents = 'clippingParents';\n  var viewport = 'viewport';\n  var popper = 'popper';\n  var reference = 'reference';\n  var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n    return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n  }, []);\n  var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n    return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n  }, []); // modifiers that need to read the DOM\n\n  var beforeRead = 'beforeRead';\n  var read = 'read';\n  var afterRead = 'afterRead'; // pure-logic modifiers\n\n  var beforeMain = 'beforeMain';\n  var main = 'main';\n  var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\n  var beforeWrite = 'beforeWrite';\n  var write = 'write';\n  var afterWrite = 'afterWrite';\n  var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];\n\n  function getNodeName(element) {\n    return element ? (element.nodeName || '').toLowerCase() : null;\n  }\n\n  function getWindow(node) {\n    if (node == null) {\n      return window;\n    }\n\n    if (node.toString() !== '[object Window]') {\n      var ownerDocument = node.ownerDocument;\n      return ownerDocument ? ownerDocument.defaultView || window : window;\n    }\n\n    return node;\n  }\n\n  function isElement(node) {\n    var OwnElement = getWindow(node).Element;\n    return node instanceof OwnElement || node instanceof Element;\n  }\n\n  function isHTMLElement(node) {\n    var OwnElement = getWindow(node).HTMLElement;\n    return node instanceof OwnElement || node instanceof HTMLElement;\n  }\n\n  function isShadowRoot(node) {\n    // IE 11 has no ShadowRoot\n    if (typeof ShadowRoot === 'undefined') {\n      return false;\n    }\n\n    var OwnElement = getWindow(node).ShadowRoot;\n    return node instanceof OwnElement || node instanceof ShadowRoot;\n  }\n\n  // and applies them to the HTMLElements such as popper and arrow\n\n  function applyStyles(_ref) {\n    var state = _ref.state;\n    Object.keys(state.elements).forEach(function (name) {\n      var style = state.styles[name] || {};\n      var attributes = state.attributes[name] || {};\n      var element = state.elements[name]; // arrow is optional + virtual elements\n\n      if (!isHTMLElement(element) || !getNodeName(element)) {\n        return;\n      } // Flow doesn't support to extend this property, but it's the most\n      // effective way to apply styles to an HTMLElement\n      // $FlowFixMe[cannot-write]\n\n\n      Object.assign(element.style, style);\n      Object.keys(attributes).forEach(function (name) {\n        var value = attributes[name];\n\n        if (value === false) {\n          element.removeAttribute(name);\n        } else {\n          element.setAttribute(name, value === true ? '' : value);\n        }\n      });\n    });\n  }\n\n  function effect$2(_ref2) {\n    var state = _ref2.state;\n    var initialStyles = {\n      popper: {\n        position: state.options.strategy,\n        left: '0',\n        top: '0',\n        margin: '0'\n      },\n      arrow: {\n        position: 'absolute'\n      },\n      reference: {}\n    };\n    Object.assign(state.elements.popper.style, initialStyles.popper);\n    state.styles = initialStyles;\n\n    if (state.elements.arrow) {\n      Object.assign(state.elements.arrow.style, initialStyles.arrow);\n    }\n\n    return function () {\n      Object.keys(state.elements).forEach(function (name) {\n        var element = state.elements[name];\n        var attributes = state.attributes[name] || {};\n        var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n        var style = styleProperties.reduce(function (style, property) {\n          style[property] = '';\n          return style;\n        }, {}); // arrow is optional + virtual elements\n\n        if (!isHTMLElement(element) || !getNodeName(element)) {\n          return;\n        }\n\n        Object.assign(element.style, style);\n        Object.keys(attributes).forEach(function (attribute) {\n          element.removeAttribute(attribute);\n        });\n      });\n    };\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const applyStyles$1 = {\n    name: 'applyStyles',\n    enabled: true,\n    phase: 'write',\n    fn: applyStyles,\n    effect: effect$2,\n    requires: ['computeStyles']\n  };\n\n  function getBasePlacement(placement) {\n    return placement.split('-')[0];\n  }\n\n  var max = Math.max;\n  var min = Math.min;\n  var round = Math.round;\n\n  function getUAString() {\n    var uaData = navigator.userAgentData;\n\n    if (uaData != null && uaData.brands) {\n      return uaData.brands.map(function (item) {\n        return item.brand + \"/\" + item.version;\n      }).join(' ');\n    }\n\n    return navigator.userAgent;\n  }\n\n  function isLayoutViewport() {\n    return !/^((?!chrome|android).)*safari/i.test(getUAString());\n  }\n\n  function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n    if (includeScale === void 0) {\n      includeScale = false;\n    }\n\n    if (isFixedStrategy === void 0) {\n      isFixedStrategy = false;\n    }\n\n    var clientRect = element.getBoundingClientRect();\n    var scaleX = 1;\n    var scaleY = 1;\n\n    if (includeScale && isHTMLElement(element)) {\n      scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n      scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n    }\n\n    var _ref = isElement(element) ? getWindow(element) : window,\n        visualViewport = _ref.visualViewport;\n\n    var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n    var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n    var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n    var width = clientRect.width / scaleX;\n    var height = clientRect.height / scaleY;\n    return {\n      width: width,\n      height: height,\n      top: y,\n      right: x + width,\n      bottom: y + height,\n      left: x,\n      x: x,\n      y: y\n    };\n  }\n\n  // means it doesn't take into account transforms.\n\n  function getLayoutRect(element) {\n    var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n    // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n    var width = element.offsetWidth;\n    var height = element.offsetHeight;\n\n    if (Math.abs(clientRect.width - width) <= 1) {\n      width = clientRect.width;\n    }\n\n    if (Math.abs(clientRect.height - height) <= 1) {\n      height = clientRect.height;\n    }\n\n    return {\n      x: element.offsetLeft,\n      y: element.offsetTop,\n      width: width,\n      height: height\n    };\n  }\n\n  function contains(parent, child) {\n    var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n    if (parent.contains(child)) {\n      return true;\n    } // then fallback to custom implementation with Shadow DOM support\n    else if (rootNode && isShadowRoot(rootNode)) {\n        var next = child;\n\n        do {\n          if (next && parent.isSameNode(next)) {\n            return true;\n          } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n          next = next.parentNode || next.host;\n        } while (next);\n      } // Give up, the result is false\n\n\n    return false;\n  }\n\n  function getComputedStyle$1(element) {\n    return getWindow(element).getComputedStyle(element);\n  }\n\n  function isTableElement(element) {\n    return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n  }\n\n  function getDocumentElement(element) {\n    // $FlowFixMe[incompatible-return]: assume body is always available\n    return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n    element.document) || window.document).documentElement;\n  }\n\n  function getParentNode(element) {\n    if (getNodeName(element) === 'html') {\n      return element;\n    }\n\n    return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n      // $FlowFixMe[incompatible-return]\n      // $FlowFixMe[prop-missing]\n      element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n      element.parentNode || ( // DOM Element detected\n      isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n      // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n      getDocumentElement(element) // fallback\n\n    );\n  }\n\n  function getTrueOffsetParent(element) {\n    if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n    getComputedStyle$1(element).position === 'fixed') {\n      return null;\n    }\n\n    return element.offsetParent;\n  } // `.offsetParent` reports `null` for fixed elements, while absolute elements\n  // return the containing block\n\n\n  function getContainingBlock(element) {\n    var isFirefox = /firefox/i.test(getUAString());\n    var isIE = /Trident/i.test(getUAString());\n\n    if (isIE && isHTMLElement(element)) {\n      // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n      var elementCss = getComputedStyle$1(element);\n\n      if (elementCss.position === 'fixed') {\n        return null;\n      }\n    }\n\n    var currentNode = getParentNode(element);\n\n    if (isShadowRoot(currentNode)) {\n      currentNode = currentNode.host;\n    }\n\n    while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n      var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n      // create a containing block.\n      // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n      if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n        return currentNode;\n      } else {\n        currentNode = currentNode.parentNode;\n      }\n    }\n\n    return null;\n  } // Gets the closest ancestor positioned element. Handles some edge cases,\n  // such as table ancestors and cross browser bugs.\n\n\n  function getOffsetParent(element) {\n    var window = getWindow(element);\n    var offsetParent = getTrueOffsetParent(element);\n\n    while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {\n      offsetParent = getTrueOffsetParent(offsetParent);\n    }\n\n    if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static')) {\n      return window;\n    }\n\n    return offsetParent || getContainingBlock(element) || window;\n  }\n\n  function getMainAxisFromPlacement(placement) {\n    return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n  }\n\n  function within(min$1, value, max$1) {\n    return max(min$1, min(value, max$1));\n  }\n  function withinMaxClamp(min, value, max) {\n    var v = within(min, value, max);\n    return v > max ? max : v;\n  }\n\n  function getFreshSideObject() {\n    return {\n      top: 0,\n      right: 0,\n      bottom: 0,\n      left: 0\n    };\n  }\n\n  function mergePaddingObject(paddingObject) {\n    return Object.assign({}, getFreshSideObject(), paddingObject);\n  }\n\n  function expandToHashMap(value, keys) {\n    return keys.reduce(function (hashMap, key) {\n      hashMap[key] = value;\n      return hashMap;\n    }, {});\n  }\n\n  var toPaddingObject = function toPaddingObject(padding, state) {\n    padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n      placement: state.placement\n    })) : padding;\n    return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n  };\n\n  function arrow(_ref) {\n    var _state$modifiersData$;\n\n    var state = _ref.state,\n        name = _ref.name,\n        options = _ref.options;\n    var arrowElement = state.elements.arrow;\n    var popperOffsets = state.modifiersData.popperOffsets;\n    var basePlacement = getBasePlacement(state.placement);\n    var axis = getMainAxisFromPlacement(basePlacement);\n    var isVertical = [left, right].indexOf(basePlacement) >= 0;\n    var len = isVertical ? 'height' : 'width';\n\n    if (!arrowElement || !popperOffsets) {\n      return;\n    }\n\n    var paddingObject = toPaddingObject(options.padding, state);\n    var arrowRect = getLayoutRect(arrowElement);\n    var minProp = axis === 'y' ? top : left;\n    var maxProp = axis === 'y' ? bottom : right;\n    var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n    var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n    var arrowOffsetParent = getOffsetParent(arrowElement);\n    var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n    var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n    // outside of the popper bounds\n\n    var min = paddingObject[minProp];\n    var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n    var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n    var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n    var axisProp = axis;\n    state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n  }\n\n  function effect$1(_ref2) {\n    var state = _ref2.state,\n        options = _ref2.options;\n    var _options$element = options.element,\n        arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n    if (arrowElement == null) {\n      return;\n    } // CSS selector\n\n\n    if (typeof arrowElement === 'string') {\n      arrowElement = state.elements.popper.querySelector(arrowElement);\n\n      if (!arrowElement) {\n        return;\n      }\n    }\n\n    if (!contains(state.elements.popper, arrowElement)) {\n\n      return;\n    }\n\n    state.elements.arrow = arrowElement;\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const arrow$1 = {\n    name: 'arrow',\n    enabled: true,\n    phase: 'main',\n    fn: arrow,\n    effect: effect$1,\n    requires: ['popperOffsets'],\n    requiresIfExists: ['preventOverflow']\n  };\n\n  function getVariation(placement) {\n    return placement.split('-')[1];\n  }\n\n  var unsetSides = {\n    top: 'auto',\n    right: 'auto',\n    bottom: 'auto',\n    left: 'auto'\n  }; // Round the offsets to the nearest suitable subpixel based on the DPR.\n  // Zooming can change the DPR, but it seems to report a value that will\n  // cleanly divide the values into the appropriate subpixels.\n\n  function roundOffsetsByDPR(_ref) {\n    var x = _ref.x,\n        y = _ref.y;\n    var win = window;\n    var dpr = win.devicePixelRatio || 1;\n    return {\n      x: round(x * dpr) / dpr || 0,\n      y: round(y * dpr) / dpr || 0\n    };\n  }\n\n  function mapToStyles(_ref2) {\n    var _Object$assign2;\n\n    var popper = _ref2.popper,\n        popperRect = _ref2.popperRect,\n        placement = _ref2.placement,\n        variation = _ref2.variation,\n        offsets = _ref2.offsets,\n        position = _ref2.position,\n        gpuAcceleration = _ref2.gpuAcceleration,\n        adaptive = _ref2.adaptive,\n        roundOffsets = _ref2.roundOffsets,\n        isFixed = _ref2.isFixed;\n    var _offsets$x = offsets.x,\n        x = _offsets$x === void 0 ? 0 : _offsets$x,\n        _offsets$y = offsets.y,\n        y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n    var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n      x: x,\n      y: y\n    }) : {\n      x: x,\n      y: y\n    };\n\n    x = _ref3.x;\n    y = _ref3.y;\n    var hasX = offsets.hasOwnProperty('x');\n    var hasY = offsets.hasOwnProperty('y');\n    var sideX = left;\n    var sideY = top;\n    var win = window;\n\n    if (adaptive) {\n      var offsetParent = getOffsetParent(popper);\n      var heightProp = 'clientHeight';\n      var widthProp = 'clientWidth';\n\n      if (offsetParent === getWindow(popper)) {\n        offsetParent = getDocumentElement(popper);\n\n        if (getComputedStyle$1(offsetParent).position !== 'static' && position === 'absolute') {\n          heightProp = 'scrollHeight';\n          widthProp = 'scrollWidth';\n        }\n      } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n      offsetParent = offsetParent;\n\n      if (placement === top || (placement === left || placement === right) && variation === end) {\n        sideY = bottom;\n        var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n        offsetParent[heightProp];\n        y -= offsetY - popperRect.height;\n        y *= gpuAcceleration ? 1 : -1;\n      }\n\n      if (placement === left || (placement === top || placement === bottom) && variation === end) {\n        sideX = right;\n        var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n        offsetParent[widthProp];\n        x -= offsetX - popperRect.width;\n        x *= gpuAcceleration ? 1 : -1;\n      }\n    }\n\n    var commonStyles = Object.assign({\n      position: position\n    }, adaptive && unsetSides);\n\n    var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n      x: x,\n      y: y\n    }) : {\n      x: x,\n      y: y\n    };\n\n    x = _ref4.x;\n    y = _ref4.y;\n\n    if (gpuAcceleration) {\n      var _Object$assign;\n\n      return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n    }\n\n    return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n  }\n\n  function computeStyles(_ref5) {\n    var state = _ref5.state,\n        options = _ref5.options;\n    var _options$gpuAccelerat = options.gpuAcceleration,\n        gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n        _options$adaptive = options.adaptive,\n        adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n        _options$roundOffsets = options.roundOffsets,\n        roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n\n    var commonStyles = {\n      placement: getBasePlacement(state.placement),\n      variation: getVariation(state.placement),\n      popper: state.elements.popper,\n      popperRect: state.rects.popper,\n      gpuAcceleration: gpuAcceleration,\n      isFixed: state.options.strategy === 'fixed'\n    };\n\n    if (state.modifiersData.popperOffsets != null) {\n      state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n        offsets: state.modifiersData.popperOffsets,\n        position: state.options.strategy,\n        adaptive: adaptive,\n        roundOffsets: roundOffsets\n      })));\n    }\n\n    if (state.modifiersData.arrow != null) {\n      state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n        offsets: state.modifiersData.arrow,\n        position: 'absolute',\n        adaptive: false,\n        roundOffsets: roundOffsets\n      })));\n    }\n\n    state.attributes.popper = Object.assign({}, state.attributes.popper, {\n      'data-popper-placement': state.placement\n    });\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const computeStyles$1 = {\n    name: 'computeStyles',\n    enabled: true,\n    phase: 'beforeWrite',\n    fn: computeStyles,\n    data: {}\n  };\n\n  var passive = {\n    passive: true\n  };\n\n  function effect(_ref) {\n    var state = _ref.state,\n        instance = _ref.instance,\n        options = _ref.options;\n    var _options$scroll = options.scroll,\n        scroll = _options$scroll === void 0 ? true : _options$scroll,\n        _options$resize = options.resize,\n        resize = _options$resize === void 0 ? true : _options$resize;\n    var window = getWindow(state.elements.popper);\n    var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n    if (scroll) {\n      scrollParents.forEach(function (scrollParent) {\n        scrollParent.addEventListener('scroll', instance.update, passive);\n      });\n    }\n\n    if (resize) {\n      window.addEventListener('resize', instance.update, passive);\n    }\n\n    return function () {\n      if (scroll) {\n        scrollParents.forEach(function (scrollParent) {\n          scrollParent.removeEventListener('scroll', instance.update, passive);\n        });\n      }\n\n      if (resize) {\n        window.removeEventListener('resize', instance.update, passive);\n      }\n    };\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const eventListeners = {\n    name: 'eventListeners',\n    enabled: true,\n    phase: 'write',\n    fn: function fn() {},\n    effect: effect,\n    data: {}\n  };\n\n  var hash$1 = {\n    left: 'right',\n    right: 'left',\n    bottom: 'top',\n    top: 'bottom'\n  };\n  function getOppositePlacement(placement) {\n    return placement.replace(/left|right|bottom|top/g, function (matched) {\n      return hash$1[matched];\n    });\n  }\n\n  var hash = {\n    start: 'end',\n    end: 'start'\n  };\n  function getOppositeVariationPlacement(placement) {\n    return placement.replace(/start|end/g, function (matched) {\n      return hash[matched];\n    });\n  }\n\n  function getWindowScroll(node) {\n    var win = getWindow(node);\n    var scrollLeft = win.pageXOffset;\n    var scrollTop = win.pageYOffset;\n    return {\n      scrollLeft: scrollLeft,\n      scrollTop: scrollTop\n    };\n  }\n\n  function getWindowScrollBarX(element) {\n    // If <html> has a CSS width greater than the viewport, then this will be\n    // incorrect for RTL.\n    // Popper 1 is broken in this case and never had a bug report so let's assume\n    // it's not an issue. I don't think anyone ever specifies width on <html>\n    // anyway.\n    // Browsers where the left scrollbar doesn't cause an issue report `0` for\n    // this (e.g. Edge 2019, IE11, Safari)\n    return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n  }\n\n  function getViewportRect(element, strategy) {\n    var win = getWindow(element);\n    var html = getDocumentElement(element);\n    var visualViewport = win.visualViewport;\n    var width = html.clientWidth;\n    var height = html.clientHeight;\n    var x = 0;\n    var y = 0;\n\n    if (visualViewport) {\n      width = visualViewport.width;\n      height = visualViewport.height;\n      var layoutViewport = isLayoutViewport();\n\n      if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n        x = visualViewport.offsetLeft;\n        y = visualViewport.offsetTop;\n      }\n    }\n\n    return {\n      width: width,\n      height: height,\n      x: x + getWindowScrollBarX(element),\n      y: y\n    };\n  }\n\n  // of the `<html>` and `<body>` rect bounds if horizontally scrollable\n\n  function getDocumentRect(element) {\n    var _element$ownerDocumen;\n\n    var html = getDocumentElement(element);\n    var winScroll = getWindowScroll(element);\n    var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n    var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n    var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n    var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n    var y = -winScroll.scrollTop;\n\n    if (getComputedStyle$1(body || html).direction === 'rtl') {\n      x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n    }\n\n    return {\n      width: width,\n      height: height,\n      x: x,\n      y: y\n    };\n  }\n\n  function isScrollParent(element) {\n    // Firefox wants us to check `-x` and `-y` variations as well\n    var _getComputedStyle = getComputedStyle$1(element),\n        overflow = _getComputedStyle.overflow,\n        overflowX = _getComputedStyle.overflowX,\n        overflowY = _getComputedStyle.overflowY;\n\n    return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n  }\n\n  function getScrollParent(node) {\n    if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n      // $FlowFixMe[incompatible-return]: assume body is always available\n      return node.ownerDocument.body;\n    }\n\n    if (isHTMLElement(node) && isScrollParent(node)) {\n      return node;\n    }\n\n    return getScrollParent(getParentNode(node));\n  }\n\n  /*\n  given a DOM element, return the list of all scroll parents, up the list of ancesors\n  until we get to the top window object. This list is what we attach scroll listeners\n  to, because if any of these parent elements scroll, we'll need to re-calculate the\n  reference element's position.\n  */\n\n  function listScrollParents(element, list) {\n    var _element$ownerDocumen;\n\n    if (list === void 0) {\n      list = [];\n    }\n\n    var scrollParent = getScrollParent(element);\n    var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n    var win = getWindow(scrollParent);\n    var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n    var updatedList = list.concat(target);\n    return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n    updatedList.concat(listScrollParents(getParentNode(target)));\n  }\n\n  function rectToClientRect(rect) {\n    return Object.assign({}, rect, {\n      left: rect.x,\n      top: rect.y,\n      right: rect.x + rect.width,\n      bottom: rect.y + rect.height\n    });\n  }\n\n  function getInnerBoundingClientRect(element, strategy) {\n    var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n    rect.top = rect.top + element.clientTop;\n    rect.left = rect.left + element.clientLeft;\n    rect.bottom = rect.top + element.clientHeight;\n    rect.right = rect.left + element.clientWidth;\n    rect.width = element.clientWidth;\n    rect.height = element.clientHeight;\n    rect.x = rect.left;\n    rect.y = rect.top;\n    return rect;\n  }\n\n  function getClientRectFromMixedType(element, clippingParent, strategy) {\n    return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n  } // A \"clipping parent\" is an overflowable container with the characteristic of\n  // clipping (or hiding) overflowing elements with a position different from\n  // `initial`\n\n\n  function getClippingParents(element) {\n    var clippingParents = listScrollParents(getParentNode(element));\n    var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle$1(element).position) >= 0;\n    var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n    if (!isElement(clipperElement)) {\n      return [];\n    } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n    return clippingParents.filter(function (clippingParent) {\n      return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n    });\n  } // Gets the maximum area that the element is visible in due to any number of\n  // clipping parents\n\n\n  function getClippingRect(element, boundary, rootBoundary, strategy) {\n    var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n    var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n    var firstClippingParent = clippingParents[0];\n    var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n      var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n      accRect.top = max(rect.top, accRect.top);\n      accRect.right = min(rect.right, accRect.right);\n      accRect.bottom = min(rect.bottom, accRect.bottom);\n      accRect.left = max(rect.left, accRect.left);\n      return accRect;\n    }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n    clippingRect.width = clippingRect.right - clippingRect.left;\n    clippingRect.height = clippingRect.bottom - clippingRect.top;\n    clippingRect.x = clippingRect.left;\n    clippingRect.y = clippingRect.top;\n    return clippingRect;\n  }\n\n  function computeOffsets(_ref) {\n    var reference = _ref.reference,\n        element = _ref.element,\n        placement = _ref.placement;\n    var basePlacement = placement ? getBasePlacement(placement) : null;\n    var variation = placement ? getVariation(placement) : null;\n    var commonX = reference.x + reference.width / 2 - element.width / 2;\n    var commonY = reference.y + reference.height / 2 - element.height / 2;\n    var offsets;\n\n    switch (basePlacement) {\n      case top:\n        offsets = {\n          x: commonX,\n          y: reference.y - element.height\n        };\n        break;\n\n      case bottom:\n        offsets = {\n          x: commonX,\n          y: reference.y + reference.height\n        };\n        break;\n\n      case right:\n        offsets = {\n          x: reference.x + reference.width,\n          y: commonY\n        };\n        break;\n\n      case left:\n        offsets = {\n          x: reference.x - element.width,\n          y: commonY\n        };\n        break;\n\n      default:\n        offsets = {\n          x: reference.x,\n          y: reference.y\n        };\n    }\n\n    var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n    if (mainAxis != null) {\n      var len = mainAxis === 'y' ? 'height' : 'width';\n\n      switch (variation) {\n        case start:\n          offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n          break;\n\n        case end:\n          offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n          break;\n      }\n    }\n\n    return offsets;\n  }\n\n  function detectOverflow(state, options) {\n    if (options === void 0) {\n      options = {};\n    }\n\n    var _options = options,\n        _options$placement = _options.placement,\n        placement = _options$placement === void 0 ? state.placement : _options$placement,\n        _options$strategy = _options.strategy,\n        strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n        _options$boundary = _options.boundary,\n        boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n        _options$rootBoundary = _options.rootBoundary,\n        rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n        _options$elementConte = _options.elementContext,\n        elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n        _options$altBoundary = _options.altBoundary,\n        altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n        _options$padding = _options.padding,\n        padding = _options$padding === void 0 ? 0 : _options$padding;\n    var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n    var altContext = elementContext === popper ? reference : popper;\n    var popperRect = state.rects.popper;\n    var element = state.elements[altBoundary ? altContext : elementContext];\n    var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n    var referenceClientRect = getBoundingClientRect(state.elements.reference);\n    var popperOffsets = computeOffsets({\n      reference: referenceClientRect,\n      element: popperRect,\n      strategy: 'absolute',\n      placement: placement\n    });\n    var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n    var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n    // 0 or negative = within the clipping rect\n\n    var overflowOffsets = {\n      top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n      bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n      left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n      right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n    };\n    var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n    if (elementContext === popper && offsetData) {\n      var offset = offsetData[placement];\n      Object.keys(overflowOffsets).forEach(function (key) {\n        var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n        var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n        overflowOffsets[key] += offset[axis] * multiply;\n      });\n    }\n\n    return overflowOffsets;\n  }\n\n  function computeAutoPlacement(state, options) {\n    if (options === void 0) {\n      options = {};\n    }\n\n    var _options = options,\n        placement = _options.placement,\n        boundary = _options.boundary,\n        rootBoundary = _options.rootBoundary,\n        padding = _options.padding,\n        flipVariations = _options.flipVariations,\n        _options$allowedAutoP = _options.allowedAutoPlacements,\n        allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP;\n    var variation = getVariation(placement);\n    var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n      return getVariation(placement) === variation;\n    }) : basePlacements;\n    var allowedPlacements = placements$1.filter(function (placement) {\n      return allowedAutoPlacements.indexOf(placement) >= 0;\n    });\n\n    if (allowedPlacements.length === 0) {\n      allowedPlacements = placements$1;\n    } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n    var overflows = allowedPlacements.reduce(function (acc, placement) {\n      acc[placement] = detectOverflow(state, {\n        placement: placement,\n        boundary: boundary,\n        rootBoundary: rootBoundary,\n        padding: padding\n      })[getBasePlacement(placement)];\n      return acc;\n    }, {});\n    return Object.keys(overflows).sort(function (a, b) {\n      return overflows[a] - overflows[b];\n    });\n  }\n\n  function getExpandedFallbackPlacements(placement) {\n    if (getBasePlacement(placement) === auto) {\n      return [];\n    }\n\n    var oppositePlacement = getOppositePlacement(placement);\n    return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n  }\n\n  function flip(_ref) {\n    var state = _ref.state,\n        options = _ref.options,\n        name = _ref.name;\n\n    if (state.modifiersData[name]._skip) {\n      return;\n    }\n\n    var _options$mainAxis = options.mainAxis,\n        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n        _options$altAxis = options.altAxis,\n        checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n        specifiedFallbackPlacements = options.fallbackPlacements,\n        padding = options.padding,\n        boundary = options.boundary,\n        rootBoundary = options.rootBoundary,\n        altBoundary = options.altBoundary,\n        _options$flipVariatio = options.flipVariations,\n        flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n        allowedAutoPlacements = options.allowedAutoPlacements;\n    var preferredPlacement = state.options.placement;\n    var basePlacement = getBasePlacement(preferredPlacement);\n    var isBasePlacement = basePlacement === preferredPlacement;\n    var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n    var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n      return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n        placement: placement,\n        boundary: boundary,\n        rootBoundary: rootBoundary,\n        padding: padding,\n        flipVariations: flipVariations,\n        allowedAutoPlacements: allowedAutoPlacements\n      }) : placement);\n    }, []);\n    var referenceRect = state.rects.reference;\n    var popperRect = state.rects.popper;\n    var checksMap = new Map();\n    var makeFallbackChecks = true;\n    var firstFittingPlacement = placements[0];\n\n    for (var i = 0; i < placements.length; i++) {\n      var placement = placements[i];\n\n      var _basePlacement = getBasePlacement(placement);\n\n      var isStartVariation = getVariation(placement) === start;\n      var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n      var len = isVertical ? 'width' : 'height';\n      var overflow = detectOverflow(state, {\n        placement: placement,\n        boundary: boundary,\n        rootBoundary: rootBoundary,\n        altBoundary: altBoundary,\n        padding: padding\n      });\n      var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n      if (referenceRect[len] > popperRect[len]) {\n        mainVariationSide = getOppositePlacement(mainVariationSide);\n      }\n\n      var altVariationSide = getOppositePlacement(mainVariationSide);\n      var checks = [];\n\n      if (checkMainAxis) {\n        checks.push(overflow[_basePlacement] <= 0);\n      }\n\n      if (checkAltAxis) {\n        checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n      }\n\n      if (checks.every(function (check) {\n        return check;\n      })) {\n        firstFittingPlacement = placement;\n        makeFallbackChecks = false;\n        break;\n      }\n\n      checksMap.set(placement, checks);\n    }\n\n    if (makeFallbackChecks) {\n      // `2` may be desired in some cases – research later\n      var numberOfChecks = flipVariations ? 3 : 1;\n\n      var _loop = function _loop(_i) {\n        var fittingPlacement = placements.find(function (placement) {\n          var checks = checksMap.get(placement);\n\n          if (checks) {\n            return checks.slice(0, _i).every(function (check) {\n              return check;\n            });\n          }\n        });\n\n        if (fittingPlacement) {\n          firstFittingPlacement = fittingPlacement;\n          return \"break\";\n        }\n      };\n\n      for (var _i = numberOfChecks; _i > 0; _i--) {\n        var _ret = _loop(_i);\n\n        if (_ret === \"break\") break;\n      }\n    }\n\n    if (state.placement !== firstFittingPlacement) {\n      state.modifiersData[name]._skip = true;\n      state.placement = firstFittingPlacement;\n      state.reset = true;\n    }\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const flip$1 = {\n    name: 'flip',\n    enabled: true,\n    phase: 'main',\n    fn: flip,\n    requiresIfExists: ['offset'],\n    data: {\n      _skip: false\n    }\n  };\n\n  function getSideOffsets(overflow, rect, preventedOffsets) {\n    if (preventedOffsets === void 0) {\n      preventedOffsets = {\n        x: 0,\n        y: 0\n      };\n    }\n\n    return {\n      top: overflow.top - rect.height - preventedOffsets.y,\n      right: overflow.right - rect.width + preventedOffsets.x,\n      bottom: overflow.bottom - rect.height + preventedOffsets.y,\n      left: overflow.left - rect.width - preventedOffsets.x\n    };\n  }\n\n  function isAnySideFullyClipped(overflow) {\n    return [top, right, bottom, left].some(function (side) {\n      return overflow[side] >= 0;\n    });\n  }\n\n  function hide(_ref) {\n    var state = _ref.state,\n        name = _ref.name;\n    var referenceRect = state.rects.reference;\n    var popperRect = state.rects.popper;\n    var preventedOffsets = state.modifiersData.preventOverflow;\n    var referenceOverflow = detectOverflow(state, {\n      elementContext: 'reference'\n    });\n    var popperAltOverflow = detectOverflow(state, {\n      altBoundary: true\n    });\n    var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n    var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n    var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n    var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n    state.modifiersData[name] = {\n      referenceClippingOffsets: referenceClippingOffsets,\n      popperEscapeOffsets: popperEscapeOffsets,\n      isReferenceHidden: isReferenceHidden,\n      hasPopperEscaped: hasPopperEscaped\n    };\n    state.attributes.popper = Object.assign({}, state.attributes.popper, {\n      'data-popper-reference-hidden': isReferenceHidden,\n      'data-popper-escaped': hasPopperEscaped\n    });\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const hide$1 = {\n    name: 'hide',\n    enabled: true,\n    phase: 'main',\n    requiresIfExists: ['preventOverflow'],\n    fn: hide\n  };\n\n  function distanceAndSkiddingToXY(placement, rects, offset) {\n    var basePlacement = getBasePlacement(placement);\n    var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n    var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n      placement: placement\n    })) : offset,\n        skidding = _ref[0],\n        distance = _ref[1];\n\n    skidding = skidding || 0;\n    distance = (distance || 0) * invertDistance;\n    return [left, right].indexOf(basePlacement) >= 0 ? {\n      x: distance,\n      y: skidding\n    } : {\n      x: skidding,\n      y: distance\n    };\n  }\n\n  function offset(_ref2) {\n    var state = _ref2.state,\n        options = _ref2.options,\n        name = _ref2.name;\n    var _options$offset = options.offset,\n        offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n    var data = placements.reduce(function (acc, placement) {\n      acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n      return acc;\n    }, {});\n    var _data$state$placement = data[state.placement],\n        x = _data$state$placement.x,\n        y = _data$state$placement.y;\n\n    if (state.modifiersData.popperOffsets != null) {\n      state.modifiersData.popperOffsets.x += x;\n      state.modifiersData.popperOffsets.y += y;\n    }\n\n    state.modifiersData[name] = data;\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const offset$1 = {\n    name: 'offset',\n    enabled: true,\n    phase: 'main',\n    requires: ['popperOffsets'],\n    fn: offset\n  };\n\n  function popperOffsets(_ref) {\n    var state = _ref.state,\n        name = _ref.name;\n    // Offsets are the actual position the popper needs to have to be\n    // properly positioned near its reference element\n    // This is the most basic placement, and will be adjusted by\n    // the modifiers in the next step\n    state.modifiersData[name] = computeOffsets({\n      reference: state.rects.reference,\n      element: state.rects.popper,\n      strategy: 'absolute',\n      placement: state.placement\n    });\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const popperOffsets$1 = {\n    name: 'popperOffsets',\n    enabled: true,\n    phase: 'read',\n    fn: popperOffsets,\n    data: {}\n  };\n\n  function getAltAxis(axis) {\n    return axis === 'x' ? 'y' : 'x';\n  }\n\n  function preventOverflow(_ref) {\n    var state = _ref.state,\n        options = _ref.options,\n        name = _ref.name;\n    var _options$mainAxis = options.mainAxis,\n        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n        _options$altAxis = options.altAxis,\n        checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n        boundary = options.boundary,\n        rootBoundary = options.rootBoundary,\n        altBoundary = options.altBoundary,\n        padding = options.padding,\n        _options$tether = options.tether,\n        tether = _options$tether === void 0 ? true : _options$tether,\n        _options$tetherOffset = options.tetherOffset,\n        tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n    var overflow = detectOverflow(state, {\n      boundary: boundary,\n      rootBoundary: rootBoundary,\n      padding: padding,\n      altBoundary: altBoundary\n    });\n    var basePlacement = getBasePlacement(state.placement);\n    var variation = getVariation(state.placement);\n    var isBasePlacement = !variation;\n    var mainAxis = getMainAxisFromPlacement(basePlacement);\n    var altAxis = getAltAxis(mainAxis);\n    var popperOffsets = state.modifiersData.popperOffsets;\n    var referenceRect = state.rects.reference;\n    var popperRect = state.rects.popper;\n    var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n      placement: state.placement\n    })) : tetherOffset;\n    var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n      mainAxis: tetherOffsetValue,\n      altAxis: tetherOffsetValue\n    } : Object.assign({\n      mainAxis: 0,\n      altAxis: 0\n    }, tetherOffsetValue);\n    var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n    var data = {\n      x: 0,\n      y: 0\n    };\n\n    if (!popperOffsets) {\n      return;\n    }\n\n    if (checkMainAxis) {\n      var _offsetModifierState$;\n\n      var mainSide = mainAxis === 'y' ? top : left;\n      var altSide = mainAxis === 'y' ? bottom : right;\n      var len = mainAxis === 'y' ? 'height' : 'width';\n      var offset = popperOffsets[mainAxis];\n      var min$1 = offset + overflow[mainSide];\n      var max$1 = offset - overflow[altSide];\n      var additive = tether ? -popperRect[len] / 2 : 0;\n      var minLen = variation === start ? referenceRect[len] : popperRect[len];\n      var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n      // outside the reference bounds\n\n      var arrowElement = state.elements.arrow;\n      var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n        width: 0,\n        height: 0\n      };\n      var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n      var arrowPaddingMin = arrowPaddingObject[mainSide];\n      var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n      // to include its full size in the calculation. If the reference is small\n      // and near the edge of a boundary, the popper can overflow even if the\n      // reference is not overflowing as well (e.g. virtual elements with no\n      // width or height)\n\n      var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n      var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n      var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n      var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n      var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n      var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n      var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n      var tetherMax = offset + maxOffset - offsetModifierValue;\n      var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1);\n      popperOffsets[mainAxis] = preventedOffset;\n      data[mainAxis] = preventedOffset - offset;\n    }\n\n    if (checkAltAxis) {\n      var _offsetModifierState$2;\n\n      var _mainSide = mainAxis === 'x' ? top : left;\n\n      var _altSide = mainAxis === 'x' ? bottom : right;\n\n      var _offset = popperOffsets[altAxis];\n\n      var _len = altAxis === 'y' ? 'height' : 'width';\n\n      var _min = _offset + overflow[_mainSide];\n\n      var _max = _offset - overflow[_altSide];\n\n      var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n      var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n      var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n      var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n      var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n      popperOffsets[altAxis] = _preventedOffset;\n      data[altAxis] = _preventedOffset - _offset;\n    }\n\n    state.modifiersData[name] = data;\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  const preventOverflow$1 = {\n    name: 'preventOverflow',\n    enabled: true,\n    phase: 'main',\n    fn: preventOverflow,\n    requiresIfExists: ['offset']\n  };\n\n  function getHTMLElementScroll(element) {\n    return {\n      scrollLeft: element.scrollLeft,\n      scrollTop: element.scrollTop\n    };\n  }\n\n  function getNodeScroll(node) {\n    if (node === getWindow(node) || !isHTMLElement(node)) {\n      return getWindowScroll(node);\n    } else {\n      return getHTMLElementScroll(node);\n    }\n  }\n\n  function isElementScaled(element) {\n    var rect = element.getBoundingClientRect();\n    var scaleX = round(rect.width) / element.offsetWidth || 1;\n    var scaleY = round(rect.height) / element.offsetHeight || 1;\n    return scaleX !== 1 || scaleY !== 1;\n  } // Returns the composite rect of an element relative to its offsetParent.\n  // Composite means it takes into account transforms as well as layout.\n\n\n  function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n    if (isFixed === void 0) {\n      isFixed = false;\n    }\n\n    var isOffsetParentAnElement = isHTMLElement(offsetParent);\n    var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n    var documentElement = getDocumentElement(offsetParent);\n    var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n    var scroll = {\n      scrollLeft: 0,\n      scrollTop: 0\n    };\n    var offsets = {\n      x: 0,\n      y: 0\n    };\n\n    if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n      if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n      isScrollParent(documentElement)) {\n        scroll = getNodeScroll(offsetParent);\n      }\n\n      if (isHTMLElement(offsetParent)) {\n        offsets = getBoundingClientRect(offsetParent, true);\n        offsets.x += offsetParent.clientLeft;\n        offsets.y += offsetParent.clientTop;\n      } else if (documentElement) {\n        offsets.x = getWindowScrollBarX(documentElement);\n      }\n    }\n\n    return {\n      x: rect.left + scroll.scrollLeft - offsets.x,\n      y: rect.top + scroll.scrollTop - offsets.y,\n      width: rect.width,\n      height: rect.height\n    };\n  }\n\n  function order(modifiers) {\n    var map = new Map();\n    var visited = new Set();\n    var result = [];\n    modifiers.forEach(function (modifier) {\n      map.set(modifier.name, modifier);\n    }); // On visiting object, check for its dependencies and visit them recursively\n\n    function sort(modifier) {\n      visited.add(modifier.name);\n      var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n      requires.forEach(function (dep) {\n        if (!visited.has(dep)) {\n          var depModifier = map.get(dep);\n\n          if (depModifier) {\n            sort(depModifier);\n          }\n        }\n      });\n      result.push(modifier);\n    }\n\n    modifiers.forEach(function (modifier) {\n      if (!visited.has(modifier.name)) {\n        // check for visited object\n        sort(modifier);\n      }\n    });\n    return result;\n  }\n\n  function orderModifiers(modifiers) {\n    // order based on dependencies\n    var orderedModifiers = order(modifiers); // order based on phase\n\n    return modifierPhases.reduce(function (acc, phase) {\n      return acc.concat(orderedModifiers.filter(function (modifier) {\n        return modifier.phase === phase;\n      }));\n    }, []);\n  }\n\n  function debounce(fn) {\n    var pending;\n    return function () {\n      if (!pending) {\n        pending = new Promise(function (resolve) {\n          Promise.resolve().then(function () {\n            pending = undefined;\n            resolve(fn());\n          });\n        });\n      }\n\n      return pending;\n    };\n  }\n\n  function mergeByName(modifiers) {\n    var merged = modifiers.reduce(function (merged, current) {\n      var existing = merged[current.name];\n      merged[current.name] = existing ? Object.assign({}, existing, current, {\n        options: Object.assign({}, existing.options, current.options),\n        data: Object.assign({}, existing.data, current.data)\n      }) : current;\n      return merged;\n    }, {}); // IE11 does not support Object.values\n\n    return Object.keys(merged).map(function (key) {\n      return merged[key];\n    });\n  }\n\n  var DEFAULT_OPTIONS = {\n    placement: 'bottom',\n    modifiers: [],\n    strategy: 'absolute'\n  };\n\n  function areValidElements() {\n    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n      args[_key] = arguments[_key];\n    }\n\n    return !args.some(function (element) {\n      return !(element && typeof element.getBoundingClientRect === 'function');\n    });\n  }\n\n  function popperGenerator(generatorOptions) {\n    if (generatorOptions === void 0) {\n      generatorOptions = {};\n    }\n\n    var _generatorOptions = generatorOptions,\n        _generatorOptions$def = _generatorOptions.defaultModifiers,\n        defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n        _generatorOptions$def2 = _generatorOptions.defaultOptions,\n        defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n    return function createPopper(reference, popper, options) {\n      if (options === void 0) {\n        options = defaultOptions;\n      }\n\n      var state = {\n        placement: 'bottom',\n        orderedModifiers: [],\n        options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n        modifiersData: {},\n        elements: {\n          reference: reference,\n          popper: popper\n        },\n        attributes: {},\n        styles: {}\n      };\n      var effectCleanupFns = [];\n      var isDestroyed = false;\n      var instance = {\n        state: state,\n        setOptions: function setOptions(setOptionsAction) {\n          var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n          cleanupModifierEffects();\n          state.options = Object.assign({}, defaultOptions, state.options, options);\n          state.scrollParents = {\n            reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n            popper: listScrollParents(popper)\n          }; // Orders the modifiers based on their dependencies and `phase`\n          // properties\n\n          var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n          state.orderedModifiers = orderedModifiers.filter(function (m) {\n            return m.enabled;\n          }); // Validate the provided modifiers so that the consumer will get warned\n\n          runModifierEffects();\n          return instance.update();\n        },\n        // Sync update – it will always be executed, even if not necessary. This\n        // is useful for low frequency updates where sync behavior simplifies the\n        // logic.\n        // For high frequency updates (e.g. `resize` and `scroll` events), always\n        // prefer the async Popper#update method\n        forceUpdate: function forceUpdate() {\n          if (isDestroyed) {\n            return;\n          }\n\n          var _state$elements = state.elements,\n              reference = _state$elements.reference,\n              popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n          // anymore\n\n          if (!areValidElements(reference, popper)) {\n\n            return;\n          } // Store the reference and popper rects to be read by modifiers\n\n\n          state.rects = {\n            reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n            popper: getLayoutRect(popper)\n          }; // Modifiers have the ability to reset the current update cycle. The\n          // most common use case for this is the `flip` modifier changing the\n          // placement, which then needs to re-run all the modifiers, because the\n          // logic was previously ran for the previous placement and is therefore\n          // stale/incorrect\n\n          state.reset = false;\n          state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n          // is filled with the initial data specified by the modifier. This means\n          // it doesn't persist and is fresh on each update.\n          // To ensure persistent data, use `${name}#persistent`\n\n          state.orderedModifiers.forEach(function (modifier) {\n            return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n          });\n\n          for (var index = 0; index < state.orderedModifiers.length; index++) {\n\n            if (state.reset === true) {\n              state.reset = false;\n              index = -1;\n              continue;\n            }\n\n            var _state$orderedModifie = state.orderedModifiers[index],\n                fn = _state$orderedModifie.fn,\n                _state$orderedModifie2 = _state$orderedModifie.options,\n                _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n                name = _state$orderedModifie.name;\n\n            if (typeof fn === 'function') {\n              state = fn({\n                state: state,\n                options: _options,\n                name: name,\n                instance: instance\n              }) || state;\n            }\n          }\n        },\n        // Async and optimistically optimized update – it will not be executed if\n        // not necessary (debounced to run at most once-per-tick)\n        update: debounce(function () {\n          return new Promise(function (resolve) {\n            instance.forceUpdate();\n            resolve(state);\n          });\n        }),\n        destroy: function destroy() {\n          cleanupModifierEffects();\n          isDestroyed = true;\n        }\n      };\n\n      if (!areValidElements(reference, popper)) {\n\n        return instance;\n      }\n\n      instance.setOptions(options).then(function (state) {\n        if (!isDestroyed && options.onFirstUpdate) {\n          options.onFirstUpdate(state);\n        }\n      }); // Modifiers have the ability to execute arbitrary code before the first\n      // update cycle runs. They will be executed in the same order as the update\n      // cycle. This is useful when a modifier adds some persistent data that\n      // other modifiers need to use, but the modifier is run after the dependent\n      // one.\n\n      function runModifierEffects() {\n        state.orderedModifiers.forEach(function (_ref3) {\n          var name = _ref3.name,\n              _ref3$options = _ref3.options,\n              options = _ref3$options === void 0 ? {} : _ref3$options,\n              effect = _ref3.effect;\n\n          if (typeof effect === 'function') {\n            var cleanupFn = effect({\n              state: state,\n              name: name,\n              instance: instance,\n              options: options\n            });\n\n            var noopFn = function noopFn() {};\n\n            effectCleanupFns.push(cleanupFn || noopFn);\n          }\n        });\n      }\n\n      function cleanupModifierEffects() {\n        effectCleanupFns.forEach(function (fn) {\n          return fn();\n        });\n        effectCleanupFns = [];\n      }\n\n      return instance;\n    };\n  }\n  var createPopper$2 = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\n  var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1];\n  var createPopper$1 = /*#__PURE__*/popperGenerator({\n    defaultModifiers: defaultModifiers$1\n  }); // eslint-disable-next-line import/no-unused-modules\n\n  var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1];\n  var createPopper = /*#__PURE__*/popperGenerator({\n    defaultModifiers: defaultModifiers\n  }); // eslint-disable-next-line import/no-unused-modules\n\n  const Popper = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({\n    __proto__: null,\n    popperGenerator,\n    detectOverflow,\n    createPopperBase: createPopper$2,\n    createPopper,\n    createPopperLite: createPopper$1,\n    top,\n    bottom,\n    right,\n    left,\n    auto,\n    basePlacements,\n    start,\n    end,\n    clippingParents,\n    viewport,\n    popper,\n    reference,\n    variationPlacements,\n    placements,\n    beforeRead,\n    read,\n    afterRead,\n    beforeMain,\n    main,\n    afterMain,\n    beforeWrite,\n    write,\n    afterWrite,\n    modifierPhases,\n    applyStyles: applyStyles$1,\n    arrow: arrow$1,\n    computeStyles: computeStyles$1,\n    eventListeners,\n    flip: flip$1,\n    hide: hide$1,\n    offset: offset$1,\n    popperOffsets: popperOffsets$1,\n    preventOverflow: preventOverflow$1\n  }, Symbol.toStringTag, { value: 'Module' }));\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dropdown.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$a = 'dropdown';\n  const DATA_KEY$6 = 'bs.dropdown';\n  const EVENT_KEY$6 = `.${DATA_KEY$6}`;\n  const DATA_API_KEY$3 = '.data-api';\n  const ESCAPE_KEY$2 = 'Escape';\n  const TAB_KEY$1 = 'Tab';\n  const ARROW_UP_KEY$1 = 'ArrowUp';\n  const ARROW_DOWN_KEY$1 = 'ArrowDown';\n  const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\n  const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\n  const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\n  const EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\n  const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\n  const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\n  const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\n  const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\n  const CLASS_NAME_SHOW$6 = 'show';\n  const CLASS_NAME_DROPUP = 'dropup';\n  const CLASS_NAME_DROPEND = 'dropend';\n  const CLASS_NAME_DROPSTART = 'dropstart';\n  const CLASS_NAME_DROPUP_CENTER = 'dropup-center';\n  const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\n  const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\n  const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\n  const SELECTOR_MENU = '.dropdown-menu';\n  const SELECTOR_NAVBAR = '.navbar';\n  const SELECTOR_NAVBAR_NAV = '.navbar-nav';\n  const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\n  const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\n  const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\n  const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\n  const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\n  const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\n  const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\n  const PLACEMENT_TOPCENTER = 'top';\n  const PLACEMENT_BOTTOMCENTER = 'bottom';\n  const Default$9 = {\n    autoClose: true,\n    boundary: 'clippingParents',\n    display: 'dynamic',\n    offset: [0, 2],\n    popperConfig: null,\n    reference: 'toggle'\n  };\n  const DefaultType$9 = {\n    autoClose: '(boolean|string)',\n    boundary: '(string|element)',\n    display: 'string',\n    offset: '(array|string|function)',\n    popperConfig: '(null|object|function)',\n    reference: '(string|element|object)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Dropdown extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._popper = null;\n      this._parent = this._element.parentNode; // dropdown wrapper\n      // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/\n\n      this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n      this._inNavbar = this._detectNavbar();\n    } // Getters\n\n\n    static get Default() {\n      return Default$9;\n    }\n\n    static get DefaultType() {\n      return DefaultType$9;\n    }\n\n    static get NAME() {\n      return NAME$a;\n    } // Public\n\n\n    toggle() {\n      return this._isShown() ? this.hide() : this.show();\n    }\n\n    show() {\n      if (isDisabled(this._element) || this._isShown()) {\n        return;\n      }\n\n      const relatedTarget = {\n        relatedTarget: this._element\n      };\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._createPopper(); // If this is a touch-enabled device we add extra\n      // empty mouseover listeners to the body's immediate children;\n      // only needed because of broken event delegation on iOS\n      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n\n\n      if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.on(element, 'mouseover', noop);\n        }\n      }\n\n      this._element.focus();\n\n      this._element.setAttribute('aria-expanded', true);\n\n      this._menu.classList.add(CLASS_NAME_SHOW$6);\n\n      this._element.classList.add(CLASS_NAME_SHOW$6);\n\n      EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n    }\n\n    hide() {\n      if (isDisabled(this._element) || !this._isShown()) {\n        return;\n      }\n\n      const relatedTarget = {\n        relatedTarget: this._element\n      };\n\n      this._completeHide(relatedTarget);\n    }\n\n    dispose() {\n      if (this._popper) {\n        this._popper.destroy();\n      }\n\n      super.dispose();\n    }\n\n    update() {\n      this._inNavbar = this._detectNavbar();\n\n      if (this._popper) {\n        this._popper.update();\n      }\n    } // Private\n\n\n    _completeHide(relatedTarget) {\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      } // If this is a touch-enabled device we remove the extra\n      // empty mouseover listeners we added for iOS support\n\n\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.off(element, 'mouseover', noop);\n        }\n      }\n\n      if (this._popper) {\n        this._popper.destroy();\n      }\n\n      this._menu.classList.remove(CLASS_NAME_SHOW$6);\n\n      this._element.classList.remove(CLASS_NAME_SHOW$6);\n\n      this._element.setAttribute('aria-expanded', 'false');\n\n      Manipulator.removeDataAttribute(this._menu, 'popper');\n      EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n    }\n\n    _getConfig(config) {\n      config = super._getConfig(config);\n\n      if (typeof config.reference === 'object' && !isElement$1(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n        // Popper virtual elements require a getBoundingClientRect method\n        throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n      }\n\n      return config;\n    }\n\n    _createPopper() {\n      if (typeof Popper === 'undefined') {\n        throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n      }\n\n      let referenceElement = this._element;\n\n      if (this._config.reference === 'parent') {\n        referenceElement = this._parent;\n      } else if (isElement$1(this._config.reference)) {\n        referenceElement = getElement(this._config.reference);\n      } else if (typeof this._config.reference === 'object') {\n        referenceElement = this._config.reference;\n      }\n\n      const popperConfig = this._getPopperConfig();\n\n      this._popper = createPopper(referenceElement, this._menu, popperConfig);\n    }\n\n    _isShown() {\n      return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n    }\n\n    _getPlacement() {\n      const parentDropdown = this._parent;\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n        return PLACEMENT_RIGHT;\n      }\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n        return PLACEMENT_LEFT;\n      }\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n        return PLACEMENT_TOPCENTER;\n      }\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n        return PLACEMENT_BOTTOMCENTER;\n      } // We need to trim the value because custom properties can also include spaces\n\n\n      const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n        return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n      }\n\n      return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n    }\n\n    _detectNavbar() {\n      return this._element.closest(SELECTOR_NAVBAR) !== null;\n    }\n\n    _getOffset() {\n      const {\n        offset\n      } = this._config;\n\n      if (typeof offset === 'string') {\n        return offset.split(',').map(value => Number.parseInt(value, 10));\n      }\n\n      if (typeof offset === 'function') {\n        return popperData => offset(popperData, this._element);\n      }\n\n      return offset;\n    }\n\n    _getPopperConfig() {\n      const defaultBsPopperConfig = {\n        placement: this._getPlacement(),\n        modifiers: [{\n          name: 'preventOverflow',\n          options: {\n            boundary: this._config.boundary\n          }\n        }, {\n          name: 'offset',\n          options: {\n            offset: this._getOffset()\n          }\n        }]\n      }; // Disable Popper if we have a static display or Dropdown is in Navbar\n\n      if (this._inNavbar || this._config.display === 'static') {\n        Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // todo:v6 remove\n\n        defaultBsPopperConfig.modifiers = [{\n          name: 'applyStyles',\n          enabled: false\n        }];\n      }\n\n      return { ...defaultBsPopperConfig,\n        ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n      };\n    }\n\n    _selectMenuItem({\n      key,\n      target\n    }) {\n      const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n\n      if (!items.length) {\n        return;\n      } // if target isn't included in items (e.g. when expanding the dropdown)\n      // allow cycling to get the last item in case key equals ARROW_UP_KEY\n\n\n      getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Dropdown.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n    static clearMenus(event) {\n      if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n        return;\n      }\n\n      const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n\n      for (const toggle of openToggles) {\n        const context = Dropdown.getInstance(toggle);\n\n        if (!context || context._config.autoClose === false) {\n          continue;\n        }\n\n        const composedPath = event.composedPath();\n        const isMenuTarget = composedPath.includes(context._menu);\n\n        if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n          continue;\n        } // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n\n\n        if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n          continue;\n        }\n\n        const relatedTarget = {\n          relatedTarget: context._element\n        };\n\n        if (event.type === 'click') {\n          relatedTarget.clickEvent = event;\n        }\n\n        context._completeHide(relatedTarget);\n      }\n    }\n\n    static dataApiKeydownHandler(event) {\n      // If not an UP | DOWN | ESCAPE key => not a dropdown command\n      // If input/textarea && if key is other than ESCAPE => not a dropdown command\n      const isInput = /input|textarea/i.test(event.target.tagName);\n      const isEscapeEvent = event.key === ESCAPE_KEY$2;\n      const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n\n      if (!isUpOrDownEvent && !isEscapeEvent) {\n        return;\n      }\n\n      if (isInput && !isEscapeEvent) {\n        return;\n      }\n\n      event.preventDefault(); // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/\n\n      const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n      const instance = Dropdown.getOrCreateInstance(getToggleButton);\n\n      if (isUpOrDownEvent) {\n        event.stopPropagation();\n        instance.show();\n\n        instance._selectMenuItem(event);\n\n        return;\n      }\n\n      if (instance._isShown()) {\n        // else is escape and we check if it is shown\n        event.stopPropagation();\n        instance.hide();\n        getToggleButton.focus();\n      }\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\n  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\n  EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\n  EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\n  EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n    event.preventDefault();\n    Dropdown.getOrCreateInstance(this).toggle();\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Dropdown);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/scrollBar.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\n  const SELECTOR_STICKY_CONTENT = '.sticky-top';\n  const PROPERTY_PADDING = 'padding-right';\n  const PROPERTY_MARGIN = 'margin-right';\n  /**\n   * Class definition\n   */\n\n  class ScrollBarHelper {\n    constructor() {\n      this._element = document.body;\n    } // Public\n\n\n    getWidth() {\n      // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n      const documentWidth = document.documentElement.clientWidth;\n      return Math.abs(window.innerWidth - documentWidth);\n    }\n\n    hide() {\n      const width = this.getWidth();\n\n      this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width\n\n\n      this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n\n\n      this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n\n      this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n    }\n\n    reset() {\n      this._resetElementAttributes(this._element, 'overflow');\n\n      this._resetElementAttributes(this._element, PROPERTY_PADDING);\n\n      this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n\n      this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n    }\n\n    isOverflowing() {\n      return this.getWidth() > 0;\n    } // Private\n\n\n    _disableOverFlow() {\n      this._saveInitialAttribute(this._element, 'overflow');\n\n      this._element.style.overflow = 'hidden';\n    }\n\n    _setElementAttributes(selector, styleProperty, callback) {\n      const scrollbarWidth = this.getWidth();\n\n      const manipulationCallBack = element => {\n        if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n          return;\n        }\n\n        this._saveInitialAttribute(element, styleProperty);\n\n        const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n        element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n      };\n\n      this._applyManipulationCallback(selector, manipulationCallBack);\n    }\n\n    _saveInitialAttribute(element, styleProperty) {\n      const actualValue = element.style.getPropertyValue(styleProperty);\n\n      if (actualValue) {\n        Manipulator.setDataAttribute(element, styleProperty, actualValue);\n      }\n    }\n\n    _resetElementAttributes(selector, styleProperty) {\n      const manipulationCallBack = element => {\n        const value = Manipulator.getDataAttribute(element, styleProperty); // We only want to remove the property if the value is `null`; the value can also be zero\n\n        if (value === null) {\n          element.style.removeProperty(styleProperty);\n          return;\n        }\n\n        Manipulator.removeDataAttribute(element, styleProperty);\n        element.style.setProperty(styleProperty, value);\n      };\n\n      this._applyManipulationCallback(selector, manipulationCallBack);\n    }\n\n    _applyManipulationCallback(selector, callBack) {\n      if (isElement$1(selector)) {\n        callBack(selector);\n        return;\n      }\n\n      for (const sel of SelectorEngine.find(selector, this._element)) {\n        callBack(sel);\n      }\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/backdrop.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$9 = 'backdrop';\n  const CLASS_NAME_FADE$4 = 'fade';\n  const CLASS_NAME_SHOW$5 = 'show';\n  const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\n  const Default$8 = {\n    className: 'modal-backdrop',\n    clickCallback: null,\n    isAnimated: false,\n    isVisible: true,\n    // if false, we use the backdrop helper without adding any element to the dom\n    rootElement: 'body' // give the choice to place backdrop under different elements\n\n  };\n  const DefaultType$8 = {\n    className: 'string',\n    clickCallback: '(function|null)',\n    isAnimated: 'boolean',\n    isVisible: 'boolean',\n    rootElement: '(element|string)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Backdrop extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n      this._isAppended = false;\n      this._element = null;\n    } // Getters\n\n\n    static get Default() {\n      return Default$8;\n    }\n\n    static get DefaultType() {\n      return DefaultType$8;\n    }\n\n    static get NAME() {\n      return NAME$9;\n    } // Public\n\n\n    show(callback) {\n      if (!this._config.isVisible) {\n        execute(callback);\n        return;\n      }\n\n      this._append();\n\n      const element = this._getElement();\n\n      if (this._config.isAnimated) {\n        reflow(element);\n      }\n\n      element.classList.add(CLASS_NAME_SHOW$5);\n\n      this._emulateAnimation(() => {\n        execute(callback);\n      });\n    }\n\n    hide(callback) {\n      if (!this._config.isVisible) {\n        execute(callback);\n        return;\n      }\n\n      this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n\n      this._emulateAnimation(() => {\n        this.dispose();\n        execute(callback);\n      });\n    }\n\n    dispose() {\n      if (!this._isAppended) {\n        return;\n      }\n\n      EventHandler.off(this._element, EVENT_MOUSEDOWN);\n\n      this._element.remove();\n\n      this._isAppended = false;\n    } // Private\n\n\n    _getElement() {\n      if (!this._element) {\n        const backdrop = document.createElement('div');\n        backdrop.className = this._config.className;\n\n        if (this._config.isAnimated) {\n          backdrop.classList.add(CLASS_NAME_FADE$4);\n        }\n\n        this._element = backdrop;\n      }\n\n      return this._element;\n    }\n\n    _configAfterMerge(config) {\n      // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n      config.rootElement = getElement(config.rootElement);\n      return config;\n    }\n\n    _append() {\n      if (this._isAppended) {\n        return;\n      }\n\n      const element = this._getElement();\n\n      this._config.rootElement.append(element);\n\n      EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n        execute(this._config.clickCallback);\n      });\n      this._isAppended = true;\n    }\n\n    _emulateAnimation(callback) {\n      executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/focustrap.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$8 = 'focustrap';\n  const DATA_KEY$5 = 'bs.focustrap';\n  const EVENT_KEY$5 = `.${DATA_KEY$5}`;\n  const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\n  const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\n  const TAB_KEY = 'Tab';\n  const TAB_NAV_FORWARD = 'forward';\n  const TAB_NAV_BACKWARD = 'backward';\n  const Default$7 = {\n    autofocus: true,\n    trapElement: null // The element to trap focus inside of\n\n  };\n  const DefaultType$7 = {\n    autofocus: 'boolean',\n    trapElement: 'element'\n  };\n  /**\n   * Class definition\n   */\n\n  class FocusTrap extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n      this._isActive = false;\n      this._lastTabNavDirection = null;\n    } // Getters\n\n\n    static get Default() {\n      return Default$7;\n    }\n\n    static get DefaultType() {\n      return DefaultType$7;\n    }\n\n    static get NAME() {\n      return NAME$8;\n    } // Public\n\n\n    activate() {\n      if (this._isActive) {\n        return;\n      }\n\n      if (this._config.autofocus) {\n        this._config.trapElement.focus();\n      }\n\n      EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n\n      EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n      EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n      this._isActive = true;\n    }\n\n    deactivate() {\n      if (!this._isActive) {\n        return;\n      }\n\n      this._isActive = false;\n      EventHandler.off(document, EVENT_KEY$5);\n    } // Private\n\n\n    _handleFocusin(event) {\n      const {\n        trapElement\n      } = this._config;\n\n      if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n        return;\n      }\n\n      const elements = SelectorEngine.focusableChildren(trapElement);\n\n      if (elements.length === 0) {\n        trapElement.focus();\n      } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n        elements[elements.length - 1].focus();\n      } else {\n        elements[0].focus();\n      }\n    }\n\n    _handleKeydown(event) {\n      if (event.key !== TAB_KEY) {\n        return;\n      }\n\n      this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): modal.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$7 = 'modal';\n  const DATA_KEY$4 = 'bs.modal';\n  const EVENT_KEY$4 = `.${DATA_KEY$4}`;\n  const DATA_API_KEY$2 = '.data-api';\n  const ESCAPE_KEY$1 = 'Escape';\n  const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\n  const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\n  const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\n  const EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\n  const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\n  const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\n  const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\n  const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\n  const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\n  const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\n  const CLASS_NAME_OPEN = 'modal-open';\n  const CLASS_NAME_FADE$3 = 'fade';\n  const CLASS_NAME_SHOW$4 = 'show';\n  const CLASS_NAME_STATIC = 'modal-static';\n  const OPEN_SELECTOR$1 = '.modal.show';\n  const SELECTOR_DIALOG = '.modal-dialog';\n  const SELECTOR_MODAL_BODY = '.modal-body';\n  const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\n  const Default$6 = {\n    backdrop: true,\n    focus: true,\n    keyboard: true\n  };\n  const DefaultType$6 = {\n    backdrop: '(boolean|string)',\n    focus: 'boolean',\n    keyboard: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Modal extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n      this._backdrop = this._initializeBackDrop();\n      this._focustrap = this._initializeFocusTrap();\n      this._isShown = false;\n      this._isTransitioning = false;\n      this._scrollBar = new ScrollBarHelper();\n\n      this._addEventListeners();\n    } // Getters\n\n\n    static get Default() {\n      return Default$6;\n    }\n\n    static get DefaultType() {\n      return DefaultType$6;\n    }\n\n    static get NAME() {\n      return NAME$7;\n    } // Public\n\n\n    toggle(relatedTarget) {\n      return this._isShown ? this.hide() : this.show(relatedTarget);\n    }\n\n    show(relatedTarget) {\n      if (this._isShown || this._isTransitioning) {\n        return;\n      }\n\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n        relatedTarget\n      });\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._isShown = true;\n      this._isTransitioning = true;\n\n      this._scrollBar.hide();\n\n      document.body.classList.add(CLASS_NAME_OPEN);\n\n      this._adjustDialog();\n\n      this._backdrop.show(() => this._showElement(relatedTarget));\n    }\n\n    hide() {\n      if (!this._isShown || this._isTransitioning) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      this._isShown = false;\n      this._isTransitioning = true;\n\n      this._focustrap.deactivate();\n\n      this._element.classList.remove(CLASS_NAME_SHOW$4);\n\n      this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n    }\n\n    dispose() {\n      for (const htmlElement of [window, this._dialog]) {\n        EventHandler.off(htmlElement, EVENT_KEY$4);\n      }\n\n      this._backdrop.dispose();\n\n      this._focustrap.deactivate();\n\n      super.dispose();\n    }\n\n    handleUpdate() {\n      this._adjustDialog();\n    } // Private\n\n\n    _initializeBackDrop() {\n      return new Backdrop({\n        isVisible: Boolean(this._config.backdrop),\n        // 'static' option will be translated to true, and booleans will keep their value,\n        isAnimated: this._isAnimated()\n      });\n    }\n\n    _initializeFocusTrap() {\n      return new FocusTrap({\n        trapElement: this._element\n      });\n    }\n\n    _showElement(relatedTarget) {\n      // try to append dynamic modal\n      if (!document.body.contains(this._element)) {\n        document.body.append(this._element);\n      }\n\n      this._element.style.display = 'block';\n\n      this._element.removeAttribute('aria-hidden');\n\n      this._element.setAttribute('aria-modal', true);\n\n      this._element.setAttribute('role', 'dialog');\n\n      this._element.scrollTop = 0;\n      const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n\n      if (modalBody) {\n        modalBody.scrollTop = 0;\n      }\n\n      reflow(this._element);\n\n      this._element.classList.add(CLASS_NAME_SHOW$4);\n\n      const transitionComplete = () => {\n        if (this._config.focus) {\n          this._focustrap.activate();\n        }\n\n        this._isTransitioning = false;\n        EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n          relatedTarget\n        });\n      };\n\n      this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n    }\n\n    _addEventListeners() {\n      EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n        if (event.key !== ESCAPE_KEY$1) {\n          return;\n        }\n\n        if (this._config.keyboard) {\n          event.preventDefault();\n          this.hide();\n          return;\n        }\n\n        this._triggerBackdropTransition();\n      });\n      EventHandler.on(window, EVENT_RESIZE$1, () => {\n        if (this._isShown && !this._isTransitioning) {\n          this._adjustDialog();\n        }\n      });\n      EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n        // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n        EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n          if (this._element !== event.target || this._element !== event2.target) {\n            return;\n          }\n\n          if (this._config.backdrop === 'static') {\n            this._triggerBackdropTransition();\n\n            return;\n          }\n\n          if (this._config.backdrop) {\n            this.hide();\n          }\n        });\n      });\n    }\n\n    _hideModal() {\n      this._element.style.display = 'none';\n\n      this._element.setAttribute('aria-hidden', true);\n\n      this._element.removeAttribute('aria-modal');\n\n      this._element.removeAttribute('role');\n\n      this._isTransitioning = false;\n\n      this._backdrop.hide(() => {\n        document.body.classList.remove(CLASS_NAME_OPEN);\n\n        this._resetAdjustments();\n\n        this._scrollBar.reset();\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n      });\n    }\n\n    _isAnimated() {\n      return this._element.classList.contains(CLASS_NAME_FADE$3);\n    }\n\n    _triggerBackdropTransition() {\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n      const initialOverflowY = this._element.style.overflowY; // return if the following background transition hasn't yet completed\n\n      if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n        return;\n      }\n\n      if (!isModalOverflowing) {\n        this._element.style.overflowY = 'hidden';\n      }\n\n      this._element.classList.add(CLASS_NAME_STATIC);\n\n      this._queueCallback(() => {\n        this._element.classList.remove(CLASS_NAME_STATIC);\n\n        this._queueCallback(() => {\n          this._element.style.overflowY = initialOverflowY;\n        }, this._dialog);\n      }, this._dialog);\n\n      this._element.focus();\n    }\n    /**\n     * The following methods are used to handle overflowing modals\n     */\n\n\n    _adjustDialog() {\n      const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n\n      const scrollbarWidth = this._scrollBar.getWidth();\n\n      const isBodyOverflowing = scrollbarWidth > 0;\n\n      if (isBodyOverflowing && !isModalOverflowing) {\n        const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n        this._element.style[property] = `${scrollbarWidth}px`;\n      }\n\n      if (!isBodyOverflowing && isModalOverflowing) {\n        const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n        this._element.style[property] = `${scrollbarWidth}px`;\n      }\n    }\n\n    _resetAdjustments() {\n      this._element.style.paddingLeft = '';\n      this._element.style.paddingRight = '';\n    } // Static\n\n\n    static jQueryInterface(config, relatedTarget) {\n      return this.each(function () {\n        const data = Modal.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config](relatedTarget);\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n    const target = getElementFromSelector(this);\n\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n\n    EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n      if (showEvent.defaultPrevented) {\n        // only register focus restorer if modal will actually get shown\n        return;\n      }\n\n      EventHandler.one(target, EVENT_HIDDEN$4, () => {\n        if (isVisible(this)) {\n          this.focus();\n        }\n      });\n    }); // avoid conflict when clicking modal toggler while another one is open\n\n    const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n\n    if (alreadyOpen) {\n      Modal.getInstance(alreadyOpen).hide();\n    }\n\n    const data = Modal.getOrCreateInstance(target);\n    data.toggle(this);\n  });\n  enableDismissTrigger(Modal);\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Modal);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): offcanvas.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$6 = 'offcanvas';\n  const DATA_KEY$3 = 'bs.offcanvas';\n  const EVENT_KEY$3 = `.${DATA_KEY$3}`;\n  const DATA_API_KEY$1 = '.data-api';\n  const EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\n  const ESCAPE_KEY = 'Escape';\n  const CLASS_NAME_SHOW$3 = 'show';\n  const CLASS_NAME_SHOWING$1 = 'showing';\n  const CLASS_NAME_HIDING = 'hiding';\n  const CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\n  const OPEN_SELECTOR = '.offcanvas.show';\n  const EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\n  const EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\n  const EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\n  const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\n  const EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\n  const EVENT_RESIZE = `resize${EVENT_KEY$3}`;\n  const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\n  const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\n  const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\n  const Default$5 = {\n    backdrop: true,\n    keyboard: true,\n    scroll: false\n  };\n  const DefaultType$5 = {\n    backdrop: '(boolean|string)',\n    keyboard: 'boolean',\n    scroll: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Offcanvas extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._isShown = false;\n      this._backdrop = this._initializeBackDrop();\n      this._focustrap = this._initializeFocusTrap();\n\n      this._addEventListeners();\n    } // Getters\n\n\n    static get Default() {\n      return Default$5;\n    }\n\n    static get DefaultType() {\n      return DefaultType$5;\n    }\n\n    static get NAME() {\n      return NAME$6;\n    } // Public\n\n\n    toggle(relatedTarget) {\n      return this._isShown ? this.hide() : this.show(relatedTarget);\n    }\n\n    show(relatedTarget) {\n      if (this._isShown) {\n        return;\n      }\n\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n        relatedTarget\n      });\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._isShown = true;\n\n      this._backdrop.show();\n\n      if (!this._config.scroll) {\n        new ScrollBarHelper().hide();\n      }\n\n      this._element.setAttribute('aria-modal', true);\n\n      this._element.setAttribute('role', 'dialog');\n\n      this._element.classList.add(CLASS_NAME_SHOWING$1);\n\n      const completeCallBack = () => {\n        if (!this._config.scroll || this._config.backdrop) {\n          this._focustrap.activate();\n        }\n\n        this._element.classList.add(CLASS_NAME_SHOW$3);\n\n        this._element.classList.remove(CLASS_NAME_SHOWING$1);\n\n        EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n          relatedTarget\n        });\n      };\n\n      this._queueCallback(completeCallBack, this._element, true);\n    }\n\n    hide() {\n      if (!this._isShown) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      this._focustrap.deactivate();\n\n      this._element.blur();\n\n      this._isShown = false;\n\n      this._element.classList.add(CLASS_NAME_HIDING);\n\n      this._backdrop.hide();\n\n      const completeCallback = () => {\n        this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n\n        this._element.removeAttribute('aria-modal');\n\n        this._element.removeAttribute('role');\n\n        if (!this._config.scroll) {\n          new ScrollBarHelper().reset();\n        }\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n      };\n\n      this._queueCallback(completeCallback, this._element, true);\n    }\n\n    dispose() {\n      this._backdrop.dispose();\n\n      this._focustrap.deactivate();\n\n      super.dispose();\n    } // Private\n\n\n    _initializeBackDrop() {\n      const clickCallback = () => {\n        if (this._config.backdrop === 'static') {\n          EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n          return;\n        }\n\n        this.hide();\n      }; // 'static' option will be translated to true, and booleans will keep their value\n\n\n      const isVisible = Boolean(this._config.backdrop);\n      return new Backdrop({\n        className: CLASS_NAME_BACKDROP,\n        isVisible,\n        isAnimated: true,\n        rootElement: this._element.parentNode,\n        clickCallback: isVisible ? clickCallback : null\n      });\n    }\n\n    _initializeFocusTrap() {\n      return new FocusTrap({\n        trapElement: this._element\n      });\n    }\n\n    _addEventListeners() {\n      EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n        if (event.key !== ESCAPE_KEY) {\n          return;\n        }\n\n        if (!this._config.keyboard) {\n          EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n          return;\n        }\n\n        this.hide();\n      });\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Offcanvas.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config](this);\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n    const target = getElementFromSelector(this);\n\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n\n    if (isDisabled(this)) {\n      return;\n    }\n\n    EventHandler.one(target, EVENT_HIDDEN$3, () => {\n      // focus on trigger when it is closed\n      if (isVisible(this)) {\n        this.focus();\n      }\n    }); // avoid conflict when clicking a toggler of an offcanvas, while another is open\n\n    const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n\n    if (alreadyOpen && alreadyOpen !== target) {\n      Offcanvas.getInstance(alreadyOpen).hide();\n    }\n\n    const data = Offcanvas.getOrCreateInstance(target);\n    data.toggle(this);\n  });\n  EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n    for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n      Offcanvas.getOrCreateInstance(selector).show();\n    }\n  });\n  EventHandler.on(window, EVENT_RESIZE, () => {\n    for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n      if (getComputedStyle(element).position !== 'fixed') {\n        Offcanvas.getOrCreateInstance(element).hide();\n      }\n    }\n  });\n  enableDismissTrigger(Offcanvas);\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Offcanvas);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/sanitizer.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n  const ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\n  /**\n   * A pattern that recognizes a commonly useful subset of URLs that are safe.\n   *\n   * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n   */\n\n  const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;\n  /**\n   * A pattern that matches safe data URLs. Only matches image, video and audio types.\n   *\n   * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n   */\n\n  const DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[\\d+/a-z]+=*$/i;\n\n  const allowedAttribute = (attribute, allowedAttributeList) => {\n    const attributeName = attribute.nodeName.toLowerCase();\n\n    if (allowedAttributeList.includes(attributeName)) {\n      if (uriAttributes.has(attributeName)) {\n        return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));\n      }\n\n      return true;\n    } // Check if a regular expression validates the attribute.\n\n\n    return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n  };\n\n  const DefaultAllowlist = {\n    // Global attributes allowed on any supplied element below.\n    '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n    a: ['target', 'href', 'title', 'rel'],\n    area: [],\n    b: [],\n    br: [],\n    col: [],\n    code: [],\n    div: [],\n    em: [],\n    hr: [],\n    h1: [],\n    h2: [],\n    h3: [],\n    h4: [],\n    h5: [],\n    h6: [],\n    i: [],\n    img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n    li: [],\n    ol: [],\n    p: [],\n    pre: [],\n    s: [],\n    small: [],\n    span: [],\n    sub: [],\n    sup: [],\n    strong: [],\n    u: [],\n    ul: []\n  };\n  function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n    if (!unsafeHtml.length) {\n      return unsafeHtml;\n    }\n\n    if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n      return sanitizeFunction(unsafeHtml);\n    }\n\n    const domParser = new window.DOMParser();\n    const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n    const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n\n    for (const element of elements) {\n      const elementName = element.nodeName.toLowerCase();\n\n      if (!Object.keys(allowList).includes(elementName)) {\n        element.remove();\n        continue;\n      }\n\n      const attributeList = [].concat(...element.attributes);\n      const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n\n      for (const attribute of attributeList) {\n        if (!allowedAttribute(attribute, allowedAttributes)) {\n          element.removeAttribute(attribute.nodeName);\n        }\n      }\n    }\n\n    return createdDocument.body.innerHTML;\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/template-factory.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$5 = 'TemplateFactory';\n  const Default$4 = {\n    allowList: DefaultAllowlist,\n    content: {},\n    // { selector : text ,  selector2 : text2 , }\n    extraClass: '',\n    html: false,\n    sanitize: true,\n    sanitizeFn: null,\n    template: '<div></div>'\n  };\n  const DefaultType$4 = {\n    allowList: 'object',\n    content: 'object',\n    extraClass: '(string|function)',\n    html: 'boolean',\n    sanitize: 'boolean',\n    sanitizeFn: '(null|function)',\n    template: 'string'\n  };\n  const DefaultContentType = {\n    entry: '(string|element|function|null)',\n    selector: '(string|element)'\n  };\n  /**\n   * Class definition\n   */\n\n  class TemplateFactory extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n    } // Getters\n\n\n    static get Default() {\n      return Default$4;\n    }\n\n    static get DefaultType() {\n      return DefaultType$4;\n    }\n\n    static get NAME() {\n      return NAME$5;\n    } // Public\n\n\n    getContent() {\n      return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n    }\n\n    hasContent() {\n      return this.getContent().length > 0;\n    }\n\n    changeContent(content) {\n      this._checkContent(content);\n\n      this._config.content = { ...this._config.content,\n        ...content\n      };\n      return this;\n    }\n\n    toHtml() {\n      const templateWrapper = document.createElement('div');\n      templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n\n      for (const [selector, text] of Object.entries(this._config.content)) {\n        this._setContent(templateWrapper, text, selector);\n      }\n\n      const template = templateWrapper.children[0];\n\n      const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n\n      if (extraClass) {\n        template.classList.add(...extraClass.split(' '));\n      }\n\n      return template;\n    } // Private\n\n\n    _typeCheckConfig(config) {\n      super._typeCheckConfig(config);\n\n      this._checkContent(config.content);\n    }\n\n    _checkContent(arg) {\n      for (const [selector, content] of Object.entries(arg)) {\n        super._typeCheckConfig({\n          selector,\n          entry: content\n        }, DefaultContentType);\n      }\n    }\n\n    _setContent(template, content, selector) {\n      const templateElement = SelectorEngine.findOne(selector, template);\n\n      if (!templateElement) {\n        return;\n      }\n\n      content = this._resolvePossibleFunction(content);\n\n      if (!content) {\n        templateElement.remove();\n        return;\n      }\n\n      if (isElement$1(content)) {\n        this._putElementInTemplate(getElement(content), templateElement);\n\n        return;\n      }\n\n      if (this._config.html) {\n        templateElement.innerHTML = this._maybeSanitize(content);\n        return;\n      }\n\n      templateElement.textContent = content;\n    }\n\n    _maybeSanitize(arg) {\n      return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n    }\n\n    _resolvePossibleFunction(arg) {\n      return typeof arg === 'function' ? arg(this) : arg;\n    }\n\n    _putElementInTemplate(element, templateElement) {\n      if (this._config.html) {\n        templateElement.innerHTML = '';\n        templateElement.append(element);\n        return;\n      }\n\n      templateElement.textContent = element.textContent;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): tooltip.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$4 = 'tooltip';\n  const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\n  const CLASS_NAME_FADE$2 = 'fade';\n  const CLASS_NAME_MODAL = 'modal';\n  const CLASS_NAME_SHOW$2 = 'show';\n  const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\n  const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\n  const EVENT_MODAL_HIDE = 'hide.bs.modal';\n  const TRIGGER_HOVER = 'hover';\n  const TRIGGER_FOCUS = 'focus';\n  const TRIGGER_CLICK = 'click';\n  const TRIGGER_MANUAL = 'manual';\n  const EVENT_HIDE$2 = 'hide';\n  const EVENT_HIDDEN$2 = 'hidden';\n  const EVENT_SHOW$2 = 'show';\n  const EVENT_SHOWN$2 = 'shown';\n  const EVENT_INSERTED = 'inserted';\n  const EVENT_CLICK$1 = 'click';\n  const EVENT_FOCUSIN$1 = 'focusin';\n  const EVENT_FOCUSOUT$1 = 'focusout';\n  const EVENT_MOUSEENTER = 'mouseenter';\n  const EVENT_MOUSELEAVE = 'mouseleave';\n  const AttachmentMap = {\n    AUTO: 'auto',\n    TOP: 'top',\n    RIGHT: isRTL() ? 'left' : 'right',\n    BOTTOM: 'bottom',\n    LEFT: isRTL() ? 'right' : 'left'\n  };\n  const Default$3 = {\n    allowList: DefaultAllowlist,\n    animation: true,\n    boundary: 'clippingParents',\n    container: false,\n    customClass: '',\n    delay: 0,\n    fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n    html: false,\n    offset: [0, 0],\n    placement: 'top',\n    popperConfig: null,\n    sanitize: true,\n    sanitizeFn: null,\n    selector: false,\n    template: '<div class=\"tooltip\" role=\"tooltip\">' + '<div class=\"tooltip-arrow\"></div>' + '<div class=\"tooltip-inner\"></div>' + '</div>',\n    title: '',\n    trigger: 'hover focus'\n  };\n  const DefaultType$3 = {\n    allowList: 'object',\n    animation: 'boolean',\n    boundary: '(string|element)',\n    container: '(string|element|boolean)',\n    customClass: '(string|function)',\n    delay: '(number|object)',\n    fallbackPlacements: 'array',\n    html: 'boolean',\n    offset: '(array|string|function)',\n    placement: '(string|function)',\n    popperConfig: '(null|object|function)',\n    sanitize: 'boolean',\n    sanitizeFn: '(null|function)',\n    selector: '(string|boolean)',\n    template: 'string',\n    title: '(string|element|function)',\n    trigger: 'string'\n  };\n  /**\n   * Class definition\n   */\n\n  class Tooltip extends BaseComponent {\n    constructor(element, config) {\n      if (typeof Popper === 'undefined') {\n        throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n      }\n\n      super(element, config); // Private\n\n      this._isEnabled = true;\n      this._timeout = 0;\n      this._isHovered = null;\n      this._activeTrigger = {};\n      this._popper = null;\n      this._templateFactory = null;\n      this._newContent = null; // Protected\n\n      this.tip = null;\n\n      this._setListeners();\n\n      if (!this._config.selector) {\n        this._fixTitle();\n      }\n    } // Getters\n\n\n    static get Default() {\n      return Default$3;\n    }\n\n    static get DefaultType() {\n      return DefaultType$3;\n    }\n\n    static get NAME() {\n      return NAME$4;\n    } // Public\n\n\n    enable() {\n      this._isEnabled = true;\n    }\n\n    disable() {\n      this._isEnabled = false;\n    }\n\n    toggleEnabled() {\n      this._isEnabled = !this._isEnabled;\n    }\n\n    toggle() {\n      if (!this._isEnabled) {\n        return;\n      }\n\n      this._activeTrigger.click = !this._activeTrigger.click;\n\n      if (this._isShown()) {\n        this._leave();\n\n        return;\n      }\n\n      this._enter();\n    }\n\n    dispose() {\n      clearTimeout(this._timeout);\n      EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n\n      if (this._element.getAttribute('data-bs-original-title')) {\n        this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n      }\n\n      this._disposePopper();\n\n      super.dispose();\n    }\n\n    show() {\n      if (this._element.style.display === 'none') {\n        throw new Error('Please use show on visible elements');\n      }\n\n      if (!(this._isWithContent() && this._isEnabled)) {\n        return;\n      }\n\n      const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n      const shadowRoot = findShadowRoot(this._element);\n\n      const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n\n      if (showEvent.defaultPrevented || !isInTheDom) {\n        return;\n      } // todo v6 remove this OR make it optional\n\n\n      this._disposePopper();\n\n      const tip = this._getTipElement();\n\n      this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n\n      const {\n        container\n      } = this._config;\n\n      if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n        container.append(tip);\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n      }\n\n      this._popper = this._createPopper(tip);\n      tip.classList.add(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we add extra\n      // empty mouseover listeners to the body's immediate children;\n      // only needed because of broken event delegation on iOS\n      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.on(element, 'mouseover', noop);\n        }\n      }\n\n      const complete = () => {\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n\n        if (this._isHovered === false) {\n          this._leave();\n        }\n\n        this._isHovered = false;\n      };\n\n      this._queueCallback(complete, this.tip, this._isAnimated());\n    }\n\n    hide() {\n      if (!this._isShown()) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      const tip = this._getTipElement();\n\n      tip.classList.remove(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we remove the extra\n      // empty mouseover listeners we added for iOS support\n\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.off(element, 'mouseover', noop);\n        }\n      }\n\n      this._activeTrigger[TRIGGER_CLICK] = false;\n      this._activeTrigger[TRIGGER_FOCUS] = false;\n      this._activeTrigger[TRIGGER_HOVER] = false;\n      this._isHovered = null; // it is a trick to support manual triggering\n\n      const complete = () => {\n        if (this._isWithActiveTrigger()) {\n          return;\n        }\n\n        if (!this._isHovered) {\n          this._disposePopper();\n        }\n\n        this._element.removeAttribute('aria-describedby');\n\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n      };\n\n      this._queueCallback(complete, this.tip, this._isAnimated());\n    }\n\n    update() {\n      if (this._popper) {\n        this._popper.update();\n      }\n    } // Protected\n\n\n    _isWithContent() {\n      return Boolean(this._getTitle());\n    }\n\n    _getTipElement() {\n      if (!this.tip) {\n        this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n      }\n\n      return this.tip;\n    }\n\n    _createTipElement(content) {\n      const tip = this._getTemplateFactory(content).toHtml(); // todo: remove this check on v6\n\n\n      if (!tip) {\n        return null;\n      }\n\n      tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); // todo: on v6 the following can be achieved with CSS only\n\n      tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n      const tipId = getUID(this.constructor.NAME).toString();\n      tip.setAttribute('id', tipId);\n\n      if (this._isAnimated()) {\n        tip.classList.add(CLASS_NAME_FADE$2);\n      }\n\n      return tip;\n    }\n\n    setContent(content) {\n      this._newContent = content;\n\n      if (this._isShown()) {\n        this._disposePopper();\n\n        this.show();\n      }\n    }\n\n    _getTemplateFactory(content) {\n      if (this._templateFactory) {\n        this._templateFactory.changeContent(content);\n      } else {\n        this._templateFactory = new TemplateFactory({ ...this._config,\n          // the `content` var has to be after `this._config`\n          // to override config.content in case of popover\n          content,\n          extraClass: this._resolvePossibleFunction(this._config.customClass)\n        });\n      }\n\n      return this._templateFactory;\n    }\n\n    _getContentForTemplate() {\n      return {\n        [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n      };\n    }\n\n    _getTitle() {\n      return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n    } // Private\n\n\n    _initializeOnDelegatedTarget(event) {\n      return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n    }\n\n    _isAnimated() {\n      return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n    }\n\n    _isShown() {\n      return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n    }\n\n    _createPopper(tip) {\n      const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;\n      const attachment = AttachmentMap[placement.toUpperCase()];\n      return createPopper(this._element, tip, this._getPopperConfig(attachment));\n    }\n\n    _getOffset() {\n      const {\n        offset\n      } = this._config;\n\n      if (typeof offset === 'string') {\n        return offset.split(',').map(value => Number.parseInt(value, 10));\n      }\n\n      if (typeof offset === 'function') {\n        return popperData => offset(popperData, this._element);\n      }\n\n      return offset;\n    }\n\n    _resolvePossibleFunction(arg) {\n      return typeof arg === 'function' ? arg.call(this._element) : arg;\n    }\n\n    _getPopperConfig(attachment) {\n      const defaultBsPopperConfig = {\n        placement: attachment,\n        modifiers: [{\n          name: 'flip',\n          options: {\n            fallbackPlacements: this._config.fallbackPlacements\n          }\n        }, {\n          name: 'offset',\n          options: {\n            offset: this._getOffset()\n          }\n        }, {\n          name: 'preventOverflow',\n          options: {\n            boundary: this._config.boundary\n          }\n        }, {\n          name: 'arrow',\n          options: {\n            element: `.${this.constructor.NAME}-arrow`\n          }\n        }, {\n          name: 'preSetPlacement',\n          enabled: true,\n          phase: 'beforeMain',\n          fn: data => {\n            // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n            // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n            this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n          }\n        }]\n      };\n      return { ...defaultBsPopperConfig,\n        ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n      };\n    }\n\n    _setListeners() {\n      const triggers = this._config.trigger.split(' ');\n\n      for (const trigger of triggers) {\n        if (trigger === 'click') {\n          EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n\n            context.toggle();\n          });\n        } else if (trigger !== TRIGGER_MANUAL) {\n          const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n          const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n          EventHandler.on(this._element, eventIn, this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n\n            context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n\n            context._enter();\n          });\n          EventHandler.on(this._element, eventOut, this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n\n            context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n\n            context._leave();\n          });\n        }\n      }\n\n      this._hideModalHandler = () => {\n        if (this._element) {\n          this.hide();\n        }\n      };\n\n      EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n    }\n\n    _fixTitle() {\n      const title = this._element.getAttribute('title');\n\n      if (!title) {\n        return;\n      }\n\n      if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n        this._element.setAttribute('aria-label', title);\n      }\n\n      this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n\n\n      this._element.removeAttribute('title');\n    }\n\n    _enter() {\n      if (this._isShown() || this._isHovered) {\n        this._isHovered = true;\n        return;\n      }\n\n      this._isHovered = true;\n\n      this._setTimeout(() => {\n        if (this._isHovered) {\n          this.show();\n        }\n      }, this._config.delay.show);\n    }\n\n    _leave() {\n      if (this._isWithActiveTrigger()) {\n        return;\n      }\n\n      this._isHovered = false;\n\n      this._setTimeout(() => {\n        if (!this._isHovered) {\n          this.hide();\n        }\n      }, this._config.delay.hide);\n    }\n\n    _setTimeout(handler, timeout) {\n      clearTimeout(this._timeout);\n      this._timeout = setTimeout(handler, timeout);\n    }\n\n    _isWithActiveTrigger() {\n      return Object.values(this._activeTrigger).includes(true);\n    }\n\n    _getConfig(config) {\n      const dataAttributes = Manipulator.getDataAttributes(this._element);\n\n      for (const dataAttribute of Object.keys(dataAttributes)) {\n        if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n          delete dataAttributes[dataAttribute];\n        }\n      }\n\n      config = { ...dataAttributes,\n        ...(typeof config === 'object' && config ? config : {})\n      };\n      config = this._mergeConfigObj(config);\n      config = this._configAfterMerge(config);\n\n      this._typeCheckConfig(config);\n\n      return config;\n    }\n\n    _configAfterMerge(config) {\n      config.container = config.container === false ? document.body : getElement(config.container);\n\n      if (typeof config.delay === 'number') {\n        config.delay = {\n          show: config.delay,\n          hide: config.delay\n        };\n      }\n\n      if (typeof config.title === 'number') {\n        config.title = config.title.toString();\n      }\n\n      if (typeof config.content === 'number') {\n        config.content = config.content.toString();\n      }\n\n      return config;\n    }\n\n    _getDelegateConfig() {\n      const config = {};\n\n      for (const key in this._config) {\n        if (this.constructor.Default[key] !== this._config[key]) {\n          config[key] = this._config[key];\n        }\n      }\n\n      config.selector = false;\n      config.trigger = 'manual'; // In the future can be replaced with:\n      // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n      // `Object.fromEntries(keysWithDifferentValues)`\n\n      return config;\n    }\n\n    _disposePopper() {\n      if (this._popper) {\n        this._popper.destroy();\n\n        this._popper = null;\n      }\n\n      if (this.tip) {\n        this.tip.remove();\n        this.tip = null;\n      }\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Tooltip.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * jQuery\n   */\n\n\n  defineJQueryPlugin(Tooltip);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): popover.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$3 = 'popover';\n  const SELECTOR_TITLE = '.popover-header';\n  const SELECTOR_CONTENT = '.popover-body';\n  const Default$2 = { ...Tooltip.Default,\n    content: '',\n    offset: [0, 8],\n    placement: 'right',\n    template: '<div class=\"popover\" role=\"tooltip\">' + '<div class=\"popover-arrow\"></div>' + '<h3 class=\"popover-header\"></h3>' + '<div class=\"popover-body\"></div>' + '</div>',\n    trigger: 'click'\n  };\n  const DefaultType$2 = { ...Tooltip.DefaultType,\n    content: '(null|string|element|function)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Popover extends Tooltip {\n    // Getters\n    static get Default() {\n      return Default$2;\n    }\n\n    static get DefaultType() {\n      return DefaultType$2;\n    }\n\n    static get NAME() {\n      return NAME$3;\n    } // Overrides\n\n\n    _isWithContent() {\n      return this._getTitle() || this._getContent();\n    } // Private\n\n\n    _getContentForTemplate() {\n      return {\n        [SELECTOR_TITLE]: this._getTitle(),\n        [SELECTOR_CONTENT]: this._getContent()\n      };\n    }\n\n    _getContent() {\n      return this._resolvePossibleFunction(this._config.content);\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Popover.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * jQuery\n   */\n\n\n  defineJQueryPlugin(Popover);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): scrollspy.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$2 = 'scrollspy';\n  const DATA_KEY$2 = 'bs.scrollspy';\n  const EVENT_KEY$2 = `.${DATA_KEY$2}`;\n  const DATA_API_KEY = '.data-api';\n  const EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\n  const EVENT_CLICK = `click${EVENT_KEY$2}`;\n  const EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\n  const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\n  const CLASS_NAME_ACTIVE$1 = 'active';\n  const SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\n  const SELECTOR_TARGET_LINKS = '[href]';\n  const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\n  const SELECTOR_NAV_LINKS = '.nav-link';\n  const SELECTOR_NAV_ITEMS = '.nav-item';\n  const SELECTOR_LIST_ITEMS = '.list-group-item';\n  const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\n  const SELECTOR_DROPDOWN = '.dropdown';\n  const SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\n  const Default$1 = {\n    offset: null,\n    // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n    rootMargin: '0px 0px -25%',\n    smoothScroll: false,\n    target: null,\n    threshold: [0.1, 0.5, 1]\n  };\n  const DefaultType$1 = {\n    offset: '(number|null)',\n    // TODO v6 @deprecated, keep it for backwards compatibility reasons\n    rootMargin: 'string',\n    smoothScroll: 'boolean',\n    target: 'element',\n    threshold: 'array'\n  };\n  /**\n   * Class definition\n   */\n\n  class ScrollSpy extends BaseComponent {\n    constructor(element, config) {\n      super(element, config); // this._element is the observablesContainer and config.target the menu links wrapper\n\n      this._targetLinks = new Map();\n      this._observableSections = new Map();\n      this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n      this._activeTarget = null;\n      this._observer = null;\n      this._previousScrollData = {\n        visibleEntryTop: 0,\n        parentScrollTop: 0\n      };\n      this.refresh(); // initialize\n    } // Getters\n\n\n    static get Default() {\n      return Default$1;\n    }\n\n    static get DefaultType() {\n      return DefaultType$1;\n    }\n\n    static get NAME() {\n      return NAME$2;\n    } // Public\n\n\n    refresh() {\n      this._initializeTargetsAndObservables();\n\n      this._maybeEnableSmoothScroll();\n\n      if (this._observer) {\n        this._observer.disconnect();\n      } else {\n        this._observer = this._getNewObserver();\n      }\n\n      for (const section of this._observableSections.values()) {\n        this._observer.observe(section);\n      }\n    }\n\n    dispose() {\n      this._observer.disconnect();\n\n      super.dispose();\n    } // Private\n\n\n    _configAfterMerge(config) {\n      // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n      config.target = getElement(config.target) || document.body; // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n\n      config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n\n      if (typeof config.threshold === 'string') {\n        config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n      }\n\n      return config;\n    }\n\n    _maybeEnableSmoothScroll() {\n      if (!this._config.smoothScroll) {\n        return;\n      } // unregister any previous listeners\n\n\n      EventHandler.off(this._config.target, EVENT_CLICK);\n      EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n        const observableSection = this._observableSections.get(event.target.hash);\n\n        if (observableSection) {\n          event.preventDefault();\n          const root = this._rootElement || window;\n          const height = observableSection.offsetTop - this._element.offsetTop;\n\n          if (root.scrollTo) {\n            root.scrollTo({\n              top: height,\n              behavior: 'smooth'\n            });\n            return;\n          } // Chrome 60 doesn't support `scrollTo`\n\n\n          root.scrollTop = height;\n        }\n      });\n    }\n\n    _getNewObserver() {\n      const options = {\n        root: this._rootElement,\n        threshold: this._config.threshold,\n        rootMargin: this._config.rootMargin\n      };\n      return new IntersectionObserver(entries => this._observerCallback(entries), options);\n    } // The logic of selection\n\n\n    _observerCallback(entries) {\n      const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n\n      const activate = entry => {\n        this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n\n        this._process(targetElement(entry));\n      };\n\n      const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n      const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n      this._previousScrollData.parentScrollTop = parentScrollTop;\n\n      for (const entry of entries) {\n        if (!entry.isIntersecting) {\n          this._activeTarget = null;\n\n          this._clearActiveClass(targetElement(entry));\n\n          continue;\n        }\n\n        const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; // if we are scrolling down, pick the bigger offsetTop\n\n        if (userScrollsDown && entryIsLowerThanPrevious) {\n          activate(entry); // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n\n          if (!parentScrollTop) {\n            return;\n          }\n\n          continue;\n        } // if we are scrolling up, pick the smallest offsetTop\n\n\n        if (!userScrollsDown && !entryIsLowerThanPrevious) {\n          activate(entry);\n        }\n      }\n    }\n\n    _initializeTargetsAndObservables() {\n      this._targetLinks = new Map();\n      this._observableSections = new Map();\n      const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n\n      for (const anchor of targetLinks) {\n        // ensure that the anchor has an id and is not disabled\n        if (!anchor.hash || isDisabled(anchor)) {\n          continue;\n        }\n\n        const observableSection = SelectorEngine.findOne(anchor.hash, this._element); // ensure that the observableSection exists & is visible\n\n        if (isVisible(observableSection)) {\n          this._targetLinks.set(anchor.hash, anchor);\n\n          this._observableSections.set(anchor.hash, observableSection);\n        }\n      }\n    }\n\n    _process(target) {\n      if (this._activeTarget === target) {\n        return;\n      }\n\n      this._clearActiveClass(this._config.target);\n\n      this._activeTarget = target;\n      target.classList.add(CLASS_NAME_ACTIVE$1);\n\n      this._activateParents(target);\n\n      EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n        relatedTarget: target\n      });\n    }\n\n    _activateParents(target) {\n      // Activate dropdown parents\n      if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n        SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n        return;\n      }\n\n      for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n        // Set triggered links parents as active\n        // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n        for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n          item.classList.add(CLASS_NAME_ACTIVE$1);\n        }\n      }\n    }\n\n    _clearActiveClass(parent) {\n      parent.classList.remove(CLASS_NAME_ACTIVE$1);\n      const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE$1}`, parent);\n\n      for (const node of activeNodes) {\n        node.classList.remove(CLASS_NAME_ACTIVE$1);\n      }\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = ScrollSpy.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(window, EVENT_LOAD_DATA_API$1, () => {\n    for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n      ScrollSpy.getOrCreateInstance(spy);\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(ScrollSpy);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): tab.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$1 = 'tab';\n  const DATA_KEY$1 = 'bs.tab';\n  const EVENT_KEY$1 = `.${DATA_KEY$1}`;\n  const EVENT_HIDE$1 = `hide${EVENT_KEY$1}`;\n  const EVENT_HIDDEN$1 = `hidden${EVENT_KEY$1}`;\n  const EVENT_SHOW$1 = `show${EVENT_KEY$1}`;\n  const EVENT_SHOWN$1 = `shown${EVENT_KEY$1}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY$1}`;\n  const EVENT_KEYDOWN = `keydown${EVENT_KEY$1}`;\n  const EVENT_LOAD_DATA_API = `load${EVENT_KEY$1}`;\n  const ARROW_LEFT_KEY = 'ArrowLeft';\n  const ARROW_RIGHT_KEY = 'ArrowRight';\n  const ARROW_UP_KEY = 'ArrowUp';\n  const ARROW_DOWN_KEY = 'ArrowDown';\n  const CLASS_NAME_ACTIVE = 'active';\n  const CLASS_NAME_FADE$1 = 'fade';\n  const CLASS_NAME_SHOW$1 = 'show';\n  const CLASS_DROPDOWN = 'dropdown';\n  const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';\n  const SELECTOR_DROPDOWN_MENU = '.dropdown-menu';\n  const NOT_SELECTOR_DROPDOWN_TOGGLE = ':not(.dropdown-toggle)';\n  const SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]';\n  const SELECTOR_OUTER = '.nav-item, .list-group-item';\n  const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`;\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]'; // todo:v6: could be only `tab`\n\n  const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`;\n  const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`;\n  /**\n   * Class definition\n   */\n\n  class Tab extends BaseComponent {\n    constructor(element) {\n      super(element);\n      this._parent = this._element.closest(SELECTOR_TAB_PANEL);\n\n      if (!this._parent) {\n        return; // todo: should Throw exception on v6\n        // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n      } // Set up initial aria attributes\n\n\n      this._setInitialAttributes(this._parent, this._getChildren());\n\n      EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));\n    } // Getters\n\n\n    static get NAME() {\n      return NAME$1;\n    } // Public\n\n\n    show() {\n      // Shows this elem and deactivate the active sibling if exists\n      const innerElem = this._element;\n\n      if (this._elemIsActive(innerElem)) {\n        return;\n      } // Search for active tab on same parent to deactivate it\n\n\n      const active = this._getActiveElem();\n\n      const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE$1, {\n        relatedTarget: innerElem\n      }) : null;\n      const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW$1, {\n        relatedTarget: active\n      });\n\n      if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) {\n        return;\n      }\n\n      this._deactivate(active, innerElem);\n\n      this._activate(innerElem, active);\n    } // Private\n\n\n    _activate(element, relatedElem) {\n      if (!element) {\n        return;\n      }\n\n      element.classList.add(CLASS_NAME_ACTIVE);\n\n      this._activate(getElementFromSelector(element)); // Search and activate/show the proper section\n\n\n      const complete = () => {\n        if (element.getAttribute('role') !== 'tab') {\n          element.classList.add(CLASS_NAME_SHOW$1);\n          return;\n        }\n\n        element.removeAttribute('tabindex');\n        element.setAttribute('aria-selected', true);\n\n        this._toggleDropDown(element, true);\n\n        EventHandler.trigger(element, EVENT_SHOWN$1, {\n          relatedTarget: relatedElem\n        });\n      };\n\n      this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1));\n    }\n\n    _deactivate(element, relatedElem) {\n      if (!element) {\n        return;\n      }\n\n      element.classList.remove(CLASS_NAME_ACTIVE);\n      element.blur();\n\n      this._deactivate(getElementFromSelector(element)); // Search and deactivate the shown section too\n\n\n      const complete = () => {\n        if (element.getAttribute('role') !== 'tab') {\n          element.classList.remove(CLASS_NAME_SHOW$1);\n          return;\n        }\n\n        element.setAttribute('aria-selected', false);\n        element.setAttribute('tabindex', '-1');\n\n        this._toggleDropDown(element, false);\n\n        EventHandler.trigger(element, EVENT_HIDDEN$1, {\n          relatedTarget: relatedElem\n        });\n      };\n\n      this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1));\n    }\n\n    _keydown(event) {\n      if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {\n        return;\n      }\n\n      event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n\n      event.preventDefault();\n      const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key);\n      const nextActiveElement = getNextActiveElement(this._getChildren().filter(element => !isDisabled(element)), event.target, isNext, true);\n\n      if (nextActiveElement) {\n        nextActiveElement.focus({\n          preventScroll: true\n        });\n        Tab.getOrCreateInstance(nextActiveElement).show();\n      }\n    }\n\n    _getChildren() {\n      // collection of inner elements\n      return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent);\n    }\n\n    _getActiveElem() {\n      return this._getChildren().find(child => this._elemIsActive(child)) || null;\n    }\n\n    _setInitialAttributes(parent, children) {\n      this._setAttributeIfNotExists(parent, 'role', 'tablist');\n\n      for (const child of children) {\n        this._setInitialAttributesOnChild(child);\n      }\n    }\n\n    _setInitialAttributesOnChild(child) {\n      child = this._getInnerElement(child);\n\n      const isActive = this._elemIsActive(child);\n\n      const outerElem = this._getOuterElement(child);\n\n      child.setAttribute('aria-selected', isActive);\n\n      if (outerElem !== child) {\n        this._setAttributeIfNotExists(outerElem, 'role', 'presentation');\n      }\n\n      if (!isActive) {\n        child.setAttribute('tabindex', '-1');\n      }\n\n      this._setAttributeIfNotExists(child, 'role', 'tab'); // set attributes to the related panel too\n\n\n      this._setInitialAttributesOnTargetPanel(child);\n    }\n\n    _setInitialAttributesOnTargetPanel(child) {\n      const target = getElementFromSelector(child);\n\n      if (!target) {\n        return;\n      }\n\n      this._setAttributeIfNotExists(target, 'role', 'tabpanel');\n\n      if (child.id) {\n        this._setAttributeIfNotExists(target, 'aria-labelledby', `#${child.id}`);\n      }\n    }\n\n    _toggleDropDown(element, open) {\n      const outerElem = this._getOuterElement(element);\n\n      if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n        return;\n      }\n\n      const toggle = (selector, className) => {\n        const element = SelectorEngine.findOne(selector, outerElem);\n\n        if (element) {\n          element.classList.toggle(className, open);\n        }\n      };\n\n      toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE);\n      toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW$1);\n      outerElem.setAttribute('aria-expanded', open);\n    }\n\n    _setAttributeIfNotExists(element, attribute, value) {\n      if (!element.hasAttribute(attribute)) {\n        element.setAttribute(attribute, value);\n      }\n    }\n\n    _elemIsActive(elem) {\n      return elem.classList.contains(CLASS_NAME_ACTIVE);\n    } // Try to get the inner element (usually the .nav-link)\n\n\n    _getInnerElement(elem) {\n      return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem);\n    } // Try to get the outer element (usually the .nav-item)\n\n\n    _getOuterElement(elem) {\n      return elem.closest(SELECTOR_OUTER) || elem;\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Tab.getOrCreateInstance(this);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n\n    if (isDisabled(this)) {\n      return;\n    }\n\n    Tab.getOrCreateInstance(this).show();\n  });\n  /**\n   * Initialize on focus\n   */\n\n  EventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n    for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n      Tab.getOrCreateInstance(element);\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Tab);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): toast.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME = 'toast';\n  const DATA_KEY = 'bs.toast';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`;\n  const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`;\n  const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;\n  const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`;\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility\n\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_NAME_SHOWING = 'showing';\n  const DefaultType = {\n    animation: 'boolean',\n    autohide: 'boolean',\n    delay: 'number'\n  };\n  const Default = {\n    animation: true,\n    autohide: true,\n    delay: 5000\n  };\n  /**\n   * Class definition\n   */\n\n  class Toast extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._timeout = null;\n      this._hasMouseInteraction = false;\n      this._hasKeyboardInteraction = false;\n\n      this._setListeners();\n    } // Getters\n\n\n    static get Default() {\n      return Default;\n    }\n\n    static get DefaultType() {\n      return DefaultType;\n    }\n\n    static get NAME() {\n      return NAME;\n    } // Public\n\n\n    show() {\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._clearTimeout();\n\n      if (this._config.animation) {\n        this._element.classList.add(CLASS_NAME_FADE);\n      }\n\n      const complete = () => {\n        this._element.classList.remove(CLASS_NAME_SHOWING);\n\n        EventHandler.trigger(this._element, EVENT_SHOWN);\n\n        this._maybeScheduleHide();\n      };\n\n      this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated\n\n\n      reflow(this._element);\n\n      this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING);\n\n      this._queueCallback(complete, this._element, this._config.animation);\n    }\n\n    hide() {\n      if (!this.isShown()) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      const complete = () => {\n        this._element.classList.add(CLASS_NAME_HIDE); // @deprecated\n\n\n        this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW);\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN);\n      };\n\n      this._element.classList.add(CLASS_NAME_SHOWING);\n\n      this._queueCallback(complete, this._element, this._config.animation);\n    }\n\n    dispose() {\n      this._clearTimeout();\n\n      if (this.isShown()) {\n        this._element.classList.remove(CLASS_NAME_SHOW);\n      }\n\n      super.dispose();\n    }\n\n    isShown() {\n      return this._element.classList.contains(CLASS_NAME_SHOW);\n    } // Private\n\n\n    _maybeScheduleHide() {\n      if (!this._config.autohide) {\n        return;\n      }\n\n      if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n        return;\n      }\n\n      this._timeout = setTimeout(() => {\n        this.hide();\n      }, this._config.delay);\n    }\n\n    _onInteraction(event, isInteracting) {\n      switch (event.type) {\n        case 'mouseover':\n        case 'mouseout':\n          {\n            this._hasMouseInteraction = isInteracting;\n            break;\n          }\n\n        case 'focusin':\n        case 'focusout':\n          {\n            this._hasKeyboardInteraction = isInteracting;\n            break;\n          }\n      }\n\n      if (isInteracting) {\n        this._clearTimeout();\n\n        return;\n      }\n\n      const nextElement = event.relatedTarget;\n\n      if (this._element === nextElement || this._element.contains(nextElement)) {\n        return;\n      }\n\n      this._maybeScheduleHide();\n    }\n\n    _setListeners() {\n      EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));\n      EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));\n      EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));\n      EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));\n    }\n\n    _clearTimeout() {\n      clearTimeout(this._timeout);\n      this._timeout = null;\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Toast.getOrCreateInstance(this, config);\n\n        if (typeof config === 'string') {\n          if (typeof data[config] === 'undefined') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n\n          data[config](this);\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  enableDismissTrigger(Toast);\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Toast);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): index.umd.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  const index_umd = {\n    Alert,\n    Button,\n    Carousel,\n    Collapse,\n    Dropdown,\n    Modal,\n    Offcanvas,\n    Popover,\n    ScrollSpy,\n    Tab,\n    Toast,\n    Tooltip\n  };\n\n  return index_umd;\n\n}));\n//# sourceMappingURL=bootstrap.bundle.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/js/bootstrap.esm.js",
    "content": "/*!\n  * Bootstrap v5.2.3 (https://getbootstrap.com/)\n  * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\nimport * as Popper from '@popperjs/core';\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\nconst MAX_UID = 1000000;\nconst MILLISECONDS_MULTIPLIER = 1000;\nconst TRANSITION_END = 'transitionend'; // Shout-out Angus Croll (https://goo.gl/pxwQGp)\n\nconst toType = object => {\n  if (object === null || object === undefined) {\n    return `${object}`;\n  }\n\n  return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n};\n/**\n * Public Util API\n */\n\n\nconst getUID = prefix => {\n  do {\n    prefix += Math.floor(Math.random() * MAX_UID);\n  } while (document.getElementById(prefix));\n\n  return prefix;\n};\n\nconst getSelector = element => {\n  let selector = element.getAttribute('data-bs-target');\n\n  if (!selector || selector === '#') {\n    let hrefAttribute = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,\n    // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n    // `document.querySelector` will rightfully complain it is invalid.\n    // See https://github.com/twbs/bootstrap/issues/32273\n\n    if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n      return null;\n    } // Just in case some CMS puts out a full URL with the anchor appended\n\n\n    if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n      hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n    }\n\n    selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n  }\n\n  return selector;\n};\n\nconst getSelectorFromElement = element => {\n  const selector = getSelector(element);\n\n  if (selector) {\n    return document.querySelector(selector) ? selector : null;\n  }\n\n  return null;\n};\n\nconst getElementFromSelector = element => {\n  const selector = getSelector(element);\n  return selector ? document.querySelector(selector) : null;\n};\n\nconst getTransitionDurationFromElement = element => {\n  if (!element) {\n    return 0;\n  } // Get transition-duration of the element\n\n\n  let {\n    transitionDuration,\n    transitionDelay\n  } = window.getComputedStyle(element);\n  const floatTransitionDuration = Number.parseFloat(transitionDuration);\n  const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found\n\n  if (!floatTransitionDuration && !floatTransitionDelay) {\n    return 0;\n  } // If multiple durations are defined, take the first\n\n\n  transitionDuration = transitionDuration.split(',')[0];\n  transitionDelay = transitionDelay.split(',')[0];\n  return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n};\n\nconst triggerTransitionEnd = element => {\n  element.dispatchEvent(new Event(TRANSITION_END));\n};\n\nconst isElement = object => {\n  if (!object || typeof object !== 'object') {\n    return false;\n  }\n\n  if (typeof object.jquery !== 'undefined') {\n    object = object[0];\n  }\n\n  return typeof object.nodeType !== 'undefined';\n};\n\nconst getElement = object => {\n  // it's a jQuery object or a node element\n  if (isElement(object)) {\n    return object.jquery ? object[0] : object;\n  }\n\n  if (typeof object === 'string' && object.length > 0) {\n    return document.querySelector(object);\n  }\n\n  return null;\n};\n\nconst isVisible = element => {\n  if (!isElement(element) || element.getClientRects().length === 0) {\n    return false;\n  }\n\n  const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; // Handle `details` element as its content may falsie appear visible when it is closed\n\n  const closedDetails = element.closest('details:not([open])');\n\n  if (!closedDetails) {\n    return elementIsVisible;\n  }\n\n  if (closedDetails !== element) {\n    const summary = element.closest('summary');\n\n    if (summary && summary.parentNode !== closedDetails) {\n      return false;\n    }\n\n    if (summary === null) {\n      return false;\n    }\n  }\n\n  return elementIsVisible;\n};\n\nconst isDisabled = element => {\n  if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n    return true;\n  }\n\n  if (element.classList.contains('disabled')) {\n    return true;\n  }\n\n  if (typeof element.disabled !== 'undefined') {\n    return element.disabled;\n  }\n\n  return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n};\n\nconst findShadowRoot = element => {\n  if (!document.documentElement.attachShadow) {\n    return null;\n  } // Can find the shadow root otherwise it'll return the document\n\n\n  if (typeof element.getRootNode === 'function') {\n    const root = element.getRootNode();\n    return root instanceof ShadowRoot ? root : null;\n  }\n\n  if (element instanceof ShadowRoot) {\n    return element;\n  } // when we don't find a shadow root\n\n\n  if (!element.parentNode) {\n    return null;\n  }\n\n  return findShadowRoot(element.parentNode);\n};\n\nconst noop = () => {};\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\n\n\nconst reflow = element => {\n  element.offsetHeight; // eslint-disable-line no-unused-expressions\n};\n\nconst getjQuery = () => {\n  if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n    return window.jQuery;\n  }\n\n  return null;\n};\n\nconst DOMContentLoadedCallbacks = [];\n\nconst onDOMContentLoaded = callback => {\n  if (document.readyState === 'loading') {\n    // add listener on the first call when the document is in loading state\n    if (!DOMContentLoadedCallbacks.length) {\n      document.addEventListener('DOMContentLoaded', () => {\n        for (const callback of DOMContentLoadedCallbacks) {\n          callback();\n        }\n      });\n    }\n\n    DOMContentLoadedCallbacks.push(callback);\n  } else {\n    callback();\n  }\n};\n\nconst isRTL = () => document.documentElement.dir === 'rtl';\n\nconst defineJQueryPlugin = plugin => {\n  onDOMContentLoaded(() => {\n    const $ = getjQuery();\n    /* istanbul ignore if */\n\n    if ($) {\n      const name = plugin.NAME;\n      const JQUERY_NO_CONFLICT = $.fn[name];\n      $.fn[name] = plugin.jQueryInterface;\n      $.fn[name].Constructor = plugin;\n\n      $.fn[name].noConflict = () => {\n        $.fn[name] = JQUERY_NO_CONFLICT;\n        return plugin.jQueryInterface;\n      };\n    }\n  });\n};\n\nconst execute = callback => {\n  if (typeof callback === 'function') {\n    callback();\n  }\n};\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n  if (!waitForTransition) {\n    execute(callback);\n    return;\n  }\n\n  const durationPadding = 5;\n  const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n  let called = false;\n\n  const handler = ({\n    target\n  }) => {\n    if (target !== transitionElement) {\n      return;\n    }\n\n    called = true;\n    transitionElement.removeEventListener(TRANSITION_END, handler);\n    execute(callback);\n  };\n\n  transitionElement.addEventListener(TRANSITION_END, handler);\n  setTimeout(() => {\n    if (!called) {\n      triggerTransitionEnd(transitionElement);\n    }\n  }, emulatedDuration);\n};\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list    The list of elements\n * @param activeElement   The active element\n * @param shouldGetNext   Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\n\n\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n  const listLength = list.length;\n  let index = list.indexOf(activeElement); // if the element does not exist in the list return an element\n  // depending on the direction and if cycle is allowed\n\n  if (index === -1) {\n    return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n  }\n\n  index += shouldGetNext ? 1 : -1;\n\n  if (isCycleAllowed) {\n    index = (index + listLength) % listLength;\n  }\n\n  return list[Math.max(0, Math.min(index, listLength - 1))];\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\nconst stripNameRegex = /\\..*/;\nconst stripUidRegex = /::\\d+$/;\nconst eventRegistry = {}; // Events storage\n\nlet uidEvent = 1;\nconst customEvents = {\n  mouseenter: 'mouseover',\n  mouseleave: 'mouseout'\n};\nconst nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n  return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n}\n\nfunction getElementEvents(element) {\n  const uid = makeEventUid(element);\n  element.uidEvent = uid;\n  eventRegistry[uid] = eventRegistry[uid] || {};\n  return eventRegistry[uid];\n}\n\nfunction bootstrapHandler(element, fn) {\n  return function handler(event) {\n    hydrateObj(event, {\n      delegateTarget: element\n    });\n\n    if (handler.oneOff) {\n      EventHandler.off(element, event.type, fn);\n    }\n\n    return fn.apply(element, [event]);\n  };\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n  return function handler(event) {\n    const domElements = element.querySelectorAll(selector);\n\n    for (let {\n      target\n    } = event; target && target !== this; target = target.parentNode) {\n      for (const domElement of domElements) {\n        if (domElement !== target) {\n          continue;\n        }\n\n        hydrateObj(event, {\n          delegateTarget: target\n        });\n\n        if (handler.oneOff) {\n          EventHandler.off(element, event.type, selector, fn);\n        }\n\n        return fn.apply(target, [event]);\n      }\n    }\n  };\n}\n\nfunction findHandler(events, callable, delegationSelector = null) {\n  return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n}\n\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n  const isDelegated = typeof handler === 'string'; // todo: tooltip passes `false` instead of selector, so we need to check\n\n  const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n  let typeEvent = getTypeEvent(originalTypeEvent);\n\n  if (!nativeEvents.has(typeEvent)) {\n    typeEvent = originalTypeEvent;\n  }\n\n  return [isDelegated, callable, typeEvent];\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n  if (typeof originalTypeEvent !== 'string' || !element) {\n    return;\n  }\n\n  let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n  // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n\n  if (originalTypeEvent in customEvents) {\n    const wrapFunction = fn => {\n      return function (event) {\n        if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n          return fn.call(this, event);\n        }\n      };\n    };\n\n    callable = wrapFunction(callable);\n  }\n\n  const events = getElementEvents(element);\n  const handlers = events[typeEvent] || (events[typeEvent] = {});\n  const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n\n  if (previousFunction) {\n    previousFunction.oneOff = previousFunction.oneOff && oneOff;\n    return;\n  }\n\n  const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n  const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n  fn.delegationSelector = isDelegated ? handler : null;\n  fn.callable = callable;\n  fn.oneOff = oneOff;\n  fn.uidEvent = uid;\n  handlers[uid] = fn;\n  element.addEventListener(typeEvent, fn, isDelegated);\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n  const fn = findHandler(events[typeEvent], handler, delegationSelector);\n\n  if (!fn) {\n    return;\n  }\n\n  element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n  delete events[typeEvent][fn.uidEvent];\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n  const storeElementEvent = events[typeEvent] || {};\n\n  for (const handlerKey of Object.keys(storeElementEvent)) {\n    if (handlerKey.includes(namespace)) {\n      const event = storeElementEvent[handlerKey];\n      removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n    }\n  }\n}\n\nfunction getTypeEvent(event) {\n  // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n  event = event.replace(stripNameRegex, '');\n  return customEvents[event] || event;\n}\n\nconst EventHandler = {\n  on(element, event, handler, delegationFunction) {\n    addHandler(element, event, handler, delegationFunction, false);\n  },\n\n  one(element, event, handler, delegationFunction) {\n    addHandler(element, event, handler, delegationFunction, true);\n  },\n\n  off(element, originalTypeEvent, handler, delegationFunction) {\n    if (typeof originalTypeEvent !== 'string' || !element) {\n      return;\n    }\n\n    const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n    const inNamespace = typeEvent !== originalTypeEvent;\n    const events = getElementEvents(element);\n    const storeElementEvent = events[typeEvent] || {};\n    const isNamespace = originalTypeEvent.startsWith('.');\n\n    if (typeof callable !== 'undefined') {\n      // Simplest case: handler is passed, remove that listener ONLY.\n      if (!Object.keys(storeElementEvent).length) {\n        return;\n      }\n\n      removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n      return;\n    }\n\n    if (isNamespace) {\n      for (const elementEvent of Object.keys(events)) {\n        removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n      }\n    }\n\n    for (const keyHandlers of Object.keys(storeElementEvent)) {\n      const handlerKey = keyHandlers.replace(stripUidRegex, '');\n\n      if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n        const event = storeElementEvent[keyHandlers];\n        removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n      }\n    }\n  },\n\n  trigger(element, event, args) {\n    if (typeof event !== 'string' || !element) {\n      return null;\n    }\n\n    const $ = getjQuery();\n    const typeEvent = getTypeEvent(event);\n    const inNamespace = event !== typeEvent;\n    let jQueryEvent = null;\n    let bubbles = true;\n    let nativeDispatch = true;\n    let defaultPrevented = false;\n\n    if (inNamespace && $) {\n      jQueryEvent = $.Event(event, args);\n      $(element).trigger(jQueryEvent);\n      bubbles = !jQueryEvent.isPropagationStopped();\n      nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n      defaultPrevented = jQueryEvent.isDefaultPrevented();\n    }\n\n    let evt = new Event(event, {\n      bubbles,\n      cancelable: true\n    });\n    evt = hydrateObj(evt, args);\n\n    if (defaultPrevented) {\n      evt.preventDefault();\n    }\n\n    if (nativeDispatch) {\n      element.dispatchEvent(evt);\n    }\n\n    if (evt.defaultPrevented && jQueryEvent) {\n      jQueryEvent.preventDefault();\n    }\n\n    return evt;\n  }\n\n};\n\nfunction hydrateObj(obj, meta) {\n  for (const [key, value] of Object.entries(meta || {})) {\n    try {\n      obj[key] = value;\n    } catch (_unused) {\n      Object.defineProperty(obj, key, {\n        configurable: true,\n\n        get() {\n          return value;\n        }\n\n      });\n    }\n  }\n\n  return obj;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\nconst elementMap = new Map();\nconst Data = {\n  set(element, key, instance) {\n    if (!elementMap.has(element)) {\n      elementMap.set(element, new Map());\n    }\n\n    const instanceMap = elementMap.get(element); // make it clear we only want one instance per element\n    // can be removed later when multiple key/instances are fine to be used\n\n    if (!instanceMap.has(key) && instanceMap.size !== 0) {\n      // eslint-disable-next-line no-console\n      console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n      return;\n    }\n\n    instanceMap.set(key, instance);\n  },\n\n  get(element, key) {\n    if (elementMap.has(element)) {\n      return elementMap.get(element).get(key) || null;\n    }\n\n    return null;\n  },\n\n  remove(element, key) {\n    if (!elementMap.has(element)) {\n      return;\n    }\n\n    const instanceMap = elementMap.get(element);\n    instanceMap.delete(key); // free up element references if there are no instances left for an element\n\n    if (instanceMap.size === 0) {\n      elementMap.delete(element);\n    }\n  }\n\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\nfunction normalizeData(value) {\n  if (value === 'true') {\n    return true;\n  }\n\n  if (value === 'false') {\n    return false;\n  }\n\n  if (value === Number(value).toString()) {\n    return Number(value);\n  }\n\n  if (value === '' || value === 'null') {\n    return null;\n  }\n\n  if (typeof value !== 'string') {\n    return value;\n  }\n\n  try {\n    return JSON.parse(decodeURIComponent(value));\n  } catch (_unused) {\n    return value;\n  }\n}\n\nfunction normalizeDataKey(key) {\n  return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n}\n\nconst Manipulator = {\n  setDataAttribute(element, key, value) {\n    element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n  },\n\n  removeDataAttribute(element, key) {\n    element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n  },\n\n  getDataAttributes(element) {\n    if (!element) {\n      return {};\n    }\n\n    const attributes = {};\n    const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n\n    for (const key of bsKeys) {\n      let pureKey = key.replace(/^bs/, '');\n      pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n      attributes[pureKey] = normalizeData(element.dataset[key]);\n    }\n\n    return attributes;\n  },\n\n  getDataAttribute(element, key) {\n    return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n  }\n\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Class definition\n */\n\nclass Config {\n  // Getters\n  static get Default() {\n    return {};\n  }\n\n  static get DefaultType() {\n    return {};\n  }\n\n  static get NAME() {\n    throw new Error('You have to implement the static method \"NAME\", for each component!');\n  }\n\n  _getConfig(config) {\n    config = this._mergeConfigObj(config);\n    config = this._configAfterMerge(config);\n\n    this._typeCheckConfig(config);\n\n    return config;\n  }\n\n  _configAfterMerge(config) {\n    return config;\n  }\n\n  _mergeConfigObj(config, element) {\n    const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n    return { ...this.constructor.Default,\n      ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n      ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n      ...(typeof config === 'object' ? config : {})\n    };\n  }\n\n  _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n    for (const property of Object.keys(configTypes)) {\n      const expectedTypes = configTypes[property];\n      const value = config[property];\n      const valueType = isElement(value) ? 'element' : toType(value);\n\n      if (!new RegExp(expectedTypes).test(valueType)) {\n        throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n      }\n    }\n  }\n\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst VERSION = '5.2.3';\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n  constructor(element, config) {\n    super();\n    element = getElement(element);\n\n    if (!element) {\n      return;\n    }\n\n    this._element = element;\n    this._config = this._getConfig(config);\n    Data.set(this._element, this.constructor.DATA_KEY, this);\n  } // Public\n\n\n  dispose() {\n    Data.remove(this._element, this.constructor.DATA_KEY);\n    EventHandler.off(this._element, this.constructor.EVENT_KEY);\n\n    for (const propertyName of Object.getOwnPropertyNames(this)) {\n      this[propertyName] = null;\n    }\n  }\n\n  _queueCallback(callback, element, isAnimated = true) {\n    executeAfterTransition(callback, element, isAnimated);\n  }\n\n  _getConfig(config) {\n    config = this._mergeConfigObj(config, this._element);\n    config = this._configAfterMerge(config);\n\n    this._typeCheckConfig(config);\n\n    return config;\n  } // Static\n\n\n  static getInstance(element) {\n    return Data.get(getElement(element), this.DATA_KEY);\n  }\n\n  static getOrCreateInstance(element, config = {}) {\n    return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n  }\n\n  static get VERSION() {\n    return VERSION;\n  }\n\n  static get DATA_KEY() {\n    return `bs.${this.NAME}`;\n  }\n\n  static get EVENT_KEY() {\n    return `.${this.DATA_KEY}`;\n  }\n\n  static eventName(name) {\n    return `${name}${this.EVENT_KEY}`;\n  }\n\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n  const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n  const name = component.NAME;\n  EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n\n    if (isDisabled(this)) {\n      return;\n    }\n\n    const target = getElementFromSelector(this) || this.closest(`.${name}`);\n    const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n\n    instance[method]();\n  });\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$f = 'alert';\nconst DATA_KEY$a = 'bs.alert';\nconst EVENT_KEY$b = `.${DATA_KEY$a}`;\nconst EVENT_CLOSE = `close${EVENT_KEY$b}`;\nconst EVENT_CLOSED = `closed${EVENT_KEY$b}`;\nconst CLASS_NAME_FADE$5 = 'fade';\nconst CLASS_NAME_SHOW$8 = 'show';\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n  // Getters\n  static get NAME() {\n    return NAME$f;\n  } // Public\n\n\n  close() {\n    const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n\n    if (closeEvent.defaultPrevented) {\n      return;\n    }\n\n    this._element.classList.remove(CLASS_NAME_SHOW$8);\n\n    const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n\n    this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n  } // Private\n\n\n  _destroyElement() {\n    this._element.remove();\n\n    EventHandler.trigger(this._element, EVENT_CLOSED);\n    this.dispose();\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Alert.getOrCreateInstance(this);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config](this);\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nenableDismissTrigger(Alert, 'close');\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$e = 'button';\nconst DATA_KEY$9 = 'bs.button';\nconst EVENT_KEY$a = `.${DATA_KEY$9}`;\nconst DATA_API_KEY$6 = '.data-api';\nconst CLASS_NAME_ACTIVE$3 = 'active';\nconst SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\nconst EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n  // Getters\n  static get NAME() {\n    return NAME$e;\n  } // Public\n\n\n  toggle() {\n    // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n    this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Button.getOrCreateInstance(this);\n\n      if (config === 'toggle') {\n        data[config]();\n      }\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n  event.preventDefault();\n  const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n  const data = Button.getOrCreateInstance(button);\n  data.toggle();\n});\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst SelectorEngine = {\n  find(selector, element = document.documentElement) {\n    return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n  },\n\n  findOne(selector, element = document.documentElement) {\n    return Element.prototype.querySelector.call(element, selector);\n  },\n\n  children(element, selector) {\n    return [].concat(...element.children).filter(child => child.matches(selector));\n  },\n\n  parents(element, selector) {\n    const parents = [];\n    let ancestor = element.parentNode.closest(selector);\n\n    while (ancestor) {\n      parents.push(ancestor);\n      ancestor = ancestor.parentNode.closest(selector);\n    }\n\n    return parents;\n  },\n\n  prev(element, selector) {\n    let previous = element.previousElementSibling;\n\n    while (previous) {\n      if (previous.matches(selector)) {\n        return [previous];\n      }\n\n      previous = previous.previousElementSibling;\n    }\n\n    return [];\n  },\n\n  // TODO: this is now unused; remove later along with prev()\n  next(element, selector) {\n    let next = element.nextElementSibling;\n\n    while (next) {\n      if (next.matches(selector)) {\n        return [next];\n      }\n\n      next = next.nextElementSibling;\n    }\n\n    return [];\n  },\n\n  focusableChildren(element) {\n    const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n    return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n  }\n\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$d = 'swipe';\nconst EVENT_KEY$9 = '.bs.swipe';\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\nconst POINTER_TYPE_TOUCH = 'touch';\nconst POINTER_TYPE_PEN = 'pen';\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event';\nconst SWIPE_THRESHOLD = 40;\nconst Default$c = {\n  endCallback: null,\n  leftCallback: null,\n  rightCallback: null\n};\nconst DefaultType$c = {\n  endCallback: '(function|null)',\n  leftCallback: '(function|null)',\n  rightCallback: '(function|null)'\n};\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n  constructor(element, config) {\n    super();\n    this._element = element;\n\n    if (!element || !Swipe.isSupported()) {\n      return;\n    }\n\n    this._config = this._getConfig(config);\n    this._deltaX = 0;\n    this._supportPointerEvents = Boolean(window.PointerEvent);\n\n    this._initEvents();\n  } // Getters\n\n\n  static get Default() {\n    return Default$c;\n  }\n\n  static get DefaultType() {\n    return DefaultType$c;\n  }\n\n  static get NAME() {\n    return NAME$d;\n  } // Public\n\n\n  dispose() {\n    EventHandler.off(this._element, EVENT_KEY$9);\n  } // Private\n\n\n  _start(event) {\n    if (!this._supportPointerEvents) {\n      this._deltaX = event.touches[0].clientX;\n      return;\n    }\n\n    if (this._eventIsPointerPenTouch(event)) {\n      this._deltaX = event.clientX;\n    }\n  }\n\n  _end(event) {\n    if (this._eventIsPointerPenTouch(event)) {\n      this._deltaX = event.clientX - this._deltaX;\n    }\n\n    this._handleSwipe();\n\n    execute(this._config.endCallback);\n  }\n\n  _move(event) {\n    this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n  }\n\n  _handleSwipe() {\n    const absDeltaX = Math.abs(this._deltaX);\n\n    if (absDeltaX <= SWIPE_THRESHOLD) {\n      return;\n    }\n\n    const direction = absDeltaX / this._deltaX;\n    this._deltaX = 0;\n\n    if (!direction) {\n      return;\n    }\n\n    execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n  }\n\n  _initEvents() {\n    if (this._supportPointerEvents) {\n      EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n      EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n\n      this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n    } else {\n      EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n      EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n      EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n    }\n  }\n\n  _eventIsPointerPenTouch(event) {\n    return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n  } // Static\n\n\n  static isSupported() {\n    return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n  }\n\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$c = 'carousel';\nconst DATA_KEY$8 = 'bs.carousel';\nconst EVENT_KEY$8 = `.${DATA_KEY$8}`;\nconst DATA_API_KEY$5 = '.data-api';\nconst ARROW_LEFT_KEY$1 = 'ArrowLeft';\nconst ARROW_RIGHT_KEY$1 = 'ArrowRight';\nconst TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next';\nconst ORDER_PREV = 'prev';\nconst DIRECTION_LEFT = 'left';\nconst DIRECTION_RIGHT = 'right';\nconst EVENT_SLIDE = `slide${EVENT_KEY$8}`;\nconst EVENT_SLID = `slid${EVENT_KEY$8}`;\nconst EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\nconst EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\nconst EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\nconst EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst CLASS_NAME_CAROUSEL = 'carousel';\nconst CLASS_NAME_ACTIVE$2 = 'active';\nconst CLASS_NAME_SLIDE = 'slide';\nconst CLASS_NAME_END = 'carousel-item-end';\nconst CLASS_NAME_START = 'carousel-item-start';\nconst CLASS_NAME_NEXT = 'carousel-item-next';\nconst CLASS_NAME_PREV = 'carousel-item-prev';\nconst SELECTOR_ACTIVE = '.active';\nconst SELECTOR_ITEM = '.carousel-item';\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\nconst SELECTOR_ITEM_IMG = '.carousel-item img';\nconst SELECTOR_INDICATORS = '.carousel-indicators';\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\nconst KEY_TO_DIRECTION = {\n  [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n  [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n};\nconst Default$b = {\n  interval: 5000,\n  keyboard: true,\n  pause: 'hover',\n  ride: false,\n  touch: true,\n  wrap: true\n};\nconst DefaultType$b = {\n  interval: '(number|boolean)',\n  // TODO:v6 remove boolean support\n  keyboard: 'boolean',\n  pause: '(string|boolean)',\n  ride: '(boolean|string)',\n  touch: 'boolean',\n  wrap: 'boolean'\n};\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n  constructor(element, config) {\n    super(element, config);\n    this._interval = null;\n    this._activeElement = null;\n    this._isSliding = false;\n    this.touchTimeout = null;\n    this._swipeHelper = null;\n    this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n\n    this._addEventListeners();\n\n    if (this._config.ride === CLASS_NAME_CAROUSEL) {\n      this.cycle();\n    }\n  } // Getters\n\n\n  static get Default() {\n    return Default$b;\n  }\n\n  static get DefaultType() {\n    return DefaultType$b;\n  }\n\n  static get NAME() {\n    return NAME$c;\n  } // Public\n\n\n  next() {\n    this._slide(ORDER_NEXT);\n  }\n\n  nextWhenVisible() {\n    // FIXME TODO use `document.visibilityState`\n    // Don't call next when the page isn't visible\n    // or the carousel or its parent isn't visible\n    if (!document.hidden && isVisible(this._element)) {\n      this.next();\n    }\n  }\n\n  prev() {\n    this._slide(ORDER_PREV);\n  }\n\n  pause() {\n    if (this._isSliding) {\n      triggerTransitionEnd(this._element);\n    }\n\n    this._clearInterval();\n  }\n\n  cycle() {\n    this._clearInterval();\n\n    this._updateInterval();\n\n    this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n  }\n\n  _maybeEnableCycle() {\n    if (!this._config.ride) {\n      return;\n    }\n\n    if (this._isSliding) {\n      EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n      return;\n    }\n\n    this.cycle();\n  }\n\n  to(index) {\n    const items = this._getItems();\n\n    if (index > items.length - 1 || index < 0) {\n      return;\n    }\n\n    if (this._isSliding) {\n      EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n      return;\n    }\n\n    const activeIndex = this._getItemIndex(this._getActive());\n\n    if (activeIndex === index) {\n      return;\n    }\n\n    const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n\n    this._slide(order, items[index]);\n  }\n\n  dispose() {\n    if (this._swipeHelper) {\n      this._swipeHelper.dispose();\n    }\n\n    super.dispose();\n  } // Private\n\n\n  _configAfterMerge(config) {\n    config.defaultInterval = config.interval;\n    return config;\n  }\n\n  _addEventListeners() {\n    if (this._config.keyboard) {\n      EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n    }\n\n    if (this._config.pause === 'hover') {\n      EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n      EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n    }\n\n    if (this._config.touch && Swipe.isSupported()) {\n      this._addTouchEventListeners();\n    }\n  }\n\n  _addTouchEventListeners() {\n    for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n      EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n    }\n\n    const endCallBack = () => {\n      if (this._config.pause !== 'hover') {\n        return;\n      } // If it's a touch-enabled device, mouseenter/leave are fired as\n      // part of the mouse compatibility events on first tap - the carousel\n      // would stop cycling until user tapped out of it;\n      // here, we listen for touchend, explicitly pause the carousel\n      // (as if it's the second time we tap on it, mouseenter compat event\n      // is NOT fired) and after a timeout (to allow for mouse compatibility\n      // events to fire) we explicitly restart cycling\n\n\n      this.pause();\n\n      if (this.touchTimeout) {\n        clearTimeout(this.touchTimeout);\n      }\n\n      this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n    };\n\n    const swipeConfig = {\n      leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n      rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n      endCallback: endCallBack\n    };\n    this._swipeHelper = new Swipe(this._element, swipeConfig);\n  }\n\n  _keydown(event) {\n    if (/input|textarea/i.test(event.target.tagName)) {\n      return;\n    }\n\n    const direction = KEY_TO_DIRECTION[event.key];\n\n    if (direction) {\n      event.preventDefault();\n\n      this._slide(this._directionToOrder(direction));\n    }\n  }\n\n  _getItemIndex(element) {\n    return this._getItems().indexOf(element);\n  }\n\n  _setActiveIndicatorElement(index) {\n    if (!this._indicatorsElement) {\n      return;\n    }\n\n    const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n    activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n    activeIndicator.removeAttribute('aria-current');\n    const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n\n    if (newActiveIndicator) {\n      newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n      newActiveIndicator.setAttribute('aria-current', 'true');\n    }\n  }\n\n  _updateInterval() {\n    const element = this._activeElement || this._getActive();\n\n    if (!element) {\n      return;\n    }\n\n    const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n    this._config.interval = elementInterval || this._config.defaultInterval;\n  }\n\n  _slide(order, element = null) {\n    if (this._isSliding) {\n      return;\n    }\n\n    const activeElement = this._getActive();\n\n    const isNext = order === ORDER_NEXT;\n    const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n\n    if (nextElement === activeElement) {\n      return;\n    }\n\n    const nextElementIndex = this._getItemIndex(nextElement);\n\n    const triggerEvent = eventName => {\n      return EventHandler.trigger(this._element, eventName, {\n        relatedTarget: nextElement,\n        direction: this._orderToDirection(order),\n        from: this._getItemIndex(activeElement),\n        to: nextElementIndex\n      });\n    };\n\n    const slideEvent = triggerEvent(EVENT_SLIDE);\n\n    if (slideEvent.defaultPrevented) {\n      return;\n    }\n\n    if (!activeElement || !nextElement) {\n      // Some weirdness is happening, so we bail\n      // todo: change tests that use empty divs to avoid this check\n      return;\n    }\n\n    const isCycling = Boolean(this._interval);\n    this.pause();\n    this._isSliding = true;\n\n    this._setActiveIndicatorElement(nextElementIndex);\n\n    this._activeElement = nextElement;\n    const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n    const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n    nextElement.classList.add(orderClassName);\n    reflow(nextElement);\n    activeElement.classList.add(directionalClassName);\n    nextElement.classList.add(directionalClassName);\n\n    const completeCallBack = () => {\n      nextElement.classList.remove(directionalClassName, orderClassName);\n      nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n      activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n      this._isSliding = false;\n      triggerEvent(EVENT_SLID);\n    };\n\n    this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n\n    if (isCycling) {\n      this.cycle();\n    }\n  }\n\n  _isAnimated() {\n    return this._element.classList.contains(CLASS_NAME_SLIDE);\n  }\n\n  _getActive() {\n    return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n  }\n\n  _getItems() {\n    return SelectorEngine.find(SELECTOR_ITEM, this._element);\n  }\n\n  _clearInterval() {\n    if (this._interval) {\n      clearInterval(this._interval);\n      this._interval = null;\n    }\n  }\n\n  _directionToOrder(direction) {\n    if (isRTL()) {\n      return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n    }\n\n    return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n  }\n\n  _orderToDirection(order) {\n    if (isRTL()) {\n      return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n    }\n\n    return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Carousel.getOrCreateInstance(this, config);\n\n      if (typeof config === 'number') {\n        data.to(config);\n        return;\n      }\n\n      if (typeof config === 'string') {\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      }\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n  const target = getElementFromSelector(this);\n\n  if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n    return;\n  }\n\n  event.preventDefault();\n  const carousel = Carousel.getOrCreateInstance(target);\n  const slideIndex = this.getAttribute('data-bs-slide-to');\n\n  if (slideIndex) {\n    carousel.to(slideIndex);\n\n    carousel._maybeEnableCycle();\n\n    return;\n  }\n\n  if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n    carousel.next();\n\n    carousel._maybeEnableCycle();\n\n    return;\n  }\n\n  carousel.prev();\n\n  carousel._maybeEnableCycle();\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n  const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n\n  for (const carousel of carousels) {\n    Carousel.getOrCreateInstance(carousel);\n  }\n});\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$b = 'collapse';\nconst DATA_KEY$7 = 'bs.collapse';\nconst EVENT_KEY$7 = `.${DATA_KEY$7}`;\nconst DATA_API_KEY$4 = '.data-api';\nconst EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\nconst EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\nconst EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\nconst EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\nconst EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\nconst CLASS_NAME_SHOW$7 = 'show';\nconst CLASS_NAME_COLLAPSE = 'collapse';\nconst CLASS_NAME_COLLAPSING = 'collapsing';\nconst CLASS_NAME_COLLAPSED = 'collapsed';\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\nconst WIDTH = 'width';\nconst HEIGHT = 'height';\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\nconst SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\nconst Default$a = {\n  parent: null,\n  toggle: true\n};\nconst DefaultType$a = {\n  parent: '(null|element)',\n  toggle: 'boolean'\n};\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n  constructor(element, config) {\n    super(element, config);\n    this._isTransitioning = false;\n    this._triggerArray = [];\n    const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n\n    for (const elem of toggleList) {\n      const selector = getSelectorFromElement(elem);\n      const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n\n      if (selector !== null && filterElement.length) {\n        this._triggerArray.push(elem);\n      }\n    }\n\n    this._initializeChildren();\n\n    if (!this._config.parent) {\n      this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n    }\n\n    if (this._config.toggle) {\n      this.toggle();\n    }\n  } // Getters\n\n\n  static get Default() {\n    return Default$a;\n  }\n\n  static get DefaultType() {\n    return DefaultType$a;\n  }\n\n  static get NAME() {\n    return NAME$b;\n  } // Public\n\n\n  toggle() {\n    if (this._isShown()) {\n      this.hide();\n    } else {\n      this.show();\n    }\n  }\n\n  show() {\n    if (this._isTransitioning || this._isShown()) {\n      return;\n    }\n\n    let activeChildren = []; // find active children\n\n    if (this._config.parent) {\n      activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n        toggle: false\n      }));\n    }\n\n    if (activeChildren.length && activeChildren[0]._isTransitioning) {\n      return;\n    }\n\n    const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n\n    if (startEvent.defaultPrevented) {\n      return;\n    }\n\n    for (const activeInstance of activeChildren) {\n      activeInstance.hide();\n    }\n\n    const dimension = this._getDimension();\n\n    this._element.classList.remove(CLASS_NAME_COLLAPSE);\n\n    this._element.classList.add(CLASS_NAME_COLLAPSING);\n\n    this._element.style[dimension] = 0;\n\n    this._addAriaAndCollapsedClass(this._triggerArray, true);\n\n    this._isTransitioning = true;\n\n    const complete = () => {\n      this._isTransitioning = false;\n\n      this._element.classList.remove(CLASS_NAME_COLLAPSING);\n\n      this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n\n      this._element.style[dimension] = '';\n      EventHandler.trigger(this._element, EVENT_SHOWN$6);\n    };\n\n    const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n    const scrollSize = `scroll${capitalizedDimension}`;\n\n    this._queueCallback(complete, this._element, true);\n\n    this._element.style[dimension] = `${this._element[scrollSize]}px`;\n  }\n\n  hide() {\n    if (this._isTransitioning || !this._isShown()) {\n      return;\n    }\n\n    const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n\n    if (startEvent.defaultPrevented) {\n      return;\n    }\n\n    const dimension = this._getDimension();\n\n    this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n    reflow(this._element);\n\n    this._element.classList.add(CLASS_NAME_COLLAPSING);\n\n    this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n\n    for (const trigger of this._triggerArray) {\n      const element = getElementFromSelector(trigger);\n\n      if (element && !this._isShown(element)) {\n        this._addAriaAndCollapsedClass([trigger], false);\n      }\n    }\n\n    this._isTransitioning = true;\n\n    const complete = () => {\n      this._isTransitioning = false;\n\n      this._element.classList.remove(CLASS_NAME_COLLAPSING);\n\n      this._element.classList.add(CLASS_NAME_COLLAPSE);\n\n      EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n    };\n\n    this._element.style[dimension] = '';\n\n    this._queueCallback(complete, this._element, true);\n  }\n\n  _isShown(element = this._element) {\n    return element.classList.contains(CLASS_NAME_SHOW$7);\n  } // Private\n\n\n  _configAfterMerge(config) {\n    config.toggle = Boolean(config.toggle); // Coerce string values\n\n    config.parent = getElement(config.parent);\n    return config;\n  }\n\n  _getDimension() {\n    return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n  }\n\n  _initializeChildren() {\n    if (!this._config.parent) {\n      return;\n    }\n\n    const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n\n    for (const element of children) {\n      const selected = getElementFromSelector(element);\n\n      if (selected) {\n        this._addAriaAndCollapsedClass([element], this._isShown(selected));\n      }\n    }\n  }\n\n  _getFirstLevelChildren(selector) {\n    const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); // remove children if greater depth\n\n    return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n  }\n\n  _addAriaAndCollapsedClass(triggerArray, isOpen) {\n    if (!triggerArray.length) {\n      return;\n    }\n\n    for (const element of triggerArray) {\n      element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n      element.setAttribute('aria-expanded', isOpen);\n    }\n  } // Static\n\n\n  static jQueryInterface(config) {\n    const _config = {};\n\n    if (typeof config === 'string' && /show|hide/.test(config)) {\n      _config.toggle = false;\n    }\n\n    return this.each(function () {\n      const data = Collapse.getOrCreateInstance(this, _config);\n\n      if (typeof config === 'string') {\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      }\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n  // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n  if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n    event.preventDefault();\n  }\n\n  const selector = getSelectorFromElement(this);\n  const selectorElements = SelectorEngine.find(selector);\n\n  for (const element of selectorElements) {\n    Collapse.getOrCreateInstance(element, {\n      toggle: false\n    }).toggle();\n  }\n});\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$a = 'dropdown';\nconst DATA_KEY$6 = 'bs.dropdown';\nconst EVENT_KEY$6 = `.${DATA_KEY$6}`;\nconst DATA_API_KEY$3 = '.data-api';\nconst ESCAPE_KEY$2 = 'Escape';\nconst TAB_KEY$1 = 'Tab';\nconst ARROW_UP_KEY$1 = 'ArrowUp';\nconst ARROW_DOWN_KEY$1 = 'ArrowDown';\nconst RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\nconst EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\nconst EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\nconst EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\nconst EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst CLASS_NAME_SHOW$6 = 'show';\nconst CLASS_NAME_DROPUP = 'dropup';\nconst CLASS_NAME_DROPEND = 'dropend';\nconst CLASS_NAME_DROPSTART = 'dropstart';\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center';\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\nconst SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\nconst SELECTOR_MENU = '.dropdown-menu';\nconst SELECTOR_NAVBAR = '.navbar';\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav';\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\nconst PLACEMENT_TOPCENTER = 'top';\nconst PLACEMENT_BOTTOMCENTER = 'bottom';\nconst Default$9 = {\n  autoClose: true,\n  boundary: 'clippingParents',\n  display: 'dynamic',\n  offset: [0, 2],\n  popperConfig: null,\n  reference: 'toggle'\n};\nconst DefaultType$9 = {\n  autoClose: '(boolean|string)',\n  boundary: '(string|element)',\n  display: 'string',\n  offset: '(array|string|function)',\n  popperConfig: '(null|object|function)',\n  reference: '(string|element|object)'\n};\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n  constructor(element, config) {\n    super(element, config);\n    this._popper = null;\n    this._parent = this._element.parentNode; // dropdown wrapper\n    // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/\n\n    this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n    this._inNavbar = this._detectNavbar();\n  } // Getters\n\n\n  static get Default() {\n    return Default$9;\n  }\n\n  static get DefaultType() {\n    return DefaultType$9;\n  }\n\n  static get NAME() {\n    return NAME$a;\n  } // Public\n\n\n  toggle() {\n    return this._isShown() ? this.hide() : this.show();\n  }\n\n  show() {\n    if (isDisabled(this._element) || this._isShown()) {\n      return;\n    }\n\n    const relatedTarget = {\n      relatedTarget: this._element\n    };\n    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n\n    if (showEvent.defaultPrevented) {\n      return;\n    }\n\n    this._createPopper(); // If this is a touch-enabled device we add extra\n    // empty mouseover listeners to the body's immediate children;\n    // only needed because of broken event delegation on iOS\n    // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n\n\n    if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n      for (const element of [].concat(...document.body.children)) {\n        EventHandler.on(element, 'mouseover', noop);\n      }\n    }\n\n    this._element.focus();\n\n    this._element.setAttribute('aria-expanded', true);\n\n    this._menu.classList.add(CLASS_NAME_SHOW$6);\n\n    this._element.classList.add(CLASS_NAME_SHOW$6);\n\n    EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n  }\n\n  hide() {\n    if (isDisabled(this._element) || !this._isShown()) {\n      return;\n    }\n\n    const relatedTarget = {\n      relatedTarget: this._element\n    };\n\n    this._completeHide(relatedTarget);\n  }\n\n  dispose() {\n    if (this._popper) {\n      this._popper.destroy();\n    }\n\n    super.dispose();\n  }\n\n  update() {\n    this._inNavbar = this._detectNavbar();\n\n    if (this._popper) {\n      this._popper.update();\n    }\n  } // Private\n\n\n  _completeHide(relatedTarget) {\n    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n\n    if (hideEvent.defaultPrevented) {\n      return;\n    } // If this is a touch-enabled device we remove the extra\n    // empty mouseover listeners we added for iOS support\n\n\n    if ('ontouchstart' in document.documentElement) {\n      for (const element of [].concat(...document.body.children)) {\n        EventHandler.off(element, 'mouseover', noop);\n      }\n    }\n\n    if (this._popper) {\n      this._popper.destroy();\n    }\n\n    this._menu.classList.remove(CLASS_NAME_SHOW$6);\n\n    this._element.classList.remove(CLASS_NAME_SHOW$6);\n\n    this._element.setAttribute('aria-expanded', 'false');\n\n    Manipulator.removeDataAttribute(this._menu, 'popper');\n    EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n  }\n\n  _getConfig(config) {\n    config = super._getConfig(config);\n\n    if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n      // Popper virtual elements require a getBoundingClientRect method\n      throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n    }\n\n    return config;\n  }\n\n  _createPopper() {\n    if (typeof Popper === 'undefined') {\n      throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n    }\n\n    let referenceElement = this._element;\n\n    if (this._config.reference === 'parent') {\n      referenceElement = this._parent;\n    } else if (isElement(this._config.reference)) {\n      referenceElement = getElement(this._config.reference);\n    } else if (typeof this._config.reference === 'object') {\n      referenceElement = this._config.reference;\n    }\n\n    const popperConfig = this._getPopperConfig();\n\n    this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig);\n  }\n\n  _isShown() {\n    return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n  }\n\n  _getPlacement() {\n    const parentDropdown = this._parent;\n\n    if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n      return PLACEMENT_RIGHT;\n    }\n\n    if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n      return PLACEMENT_LEFT;\n    }\n\n    if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n      return PLACEMENT_TOPCENTER;\n    }\n\n    if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n      return PLACEMENT_BOTTOMCENTER;\n    } // We need to trim the value because custom properties can also include spaces\n\n\n    const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n\n    if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n      return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n    }\n\n    return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n  }\n\n  _detectNavbar() {\n    return this._element.closest(SELECTOR_NAVBAR) !== null;\n  }\n\n  _getOffset() {\n    const {\n      offset\n    } = this._config;\n\n    if (typeof offset === 'string') {\n      return offset.split(',').map(value => Number.parseInt(value, 10));\n    }\n\n    if (typeof offset === 'function') {\n      return popperData => offset(popperData, this._element);\n    }\n\n    return offset;\n  }\n\n  _getPopperConfig() {\n    const defaultBsPopperConfig = {\n      placement: this._getPlacement(),\n      modifiers: [{\n        name: 'preventOverflow',\n        options: {\n          boundary: this._config.boundary\n        }\n      }, {\n        name: 'offset',\n        options: {\n          offset: this._getOffset()\n        }\n      }]\n    }; // Disable Popper if we have a static display or Dropdown is in Navbar\n\n    if (this._inNavbar || this._config.display === 'static') {\n      Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // todo:v6 remove\n\n      defaultBsPopperConfig.modifiers = [{\n        name: 'applyStyles',\n        enabled: false\n      }];\n    }\n\n    return { ...defaultBsPopperConfig,\n      ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n    };\n  }\n\n  _selectMenuItem({\n    key,\n    target\n  }) {\n    const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n\n    if (!items.length) {\n      return;\n    } // if target isn't included in items (e.g. when expanding the dropdown)\n    // allow cycling to get the last item in case key equals ARROW_UP_KEY\n\n\n    getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Dropdown.getOrCreateInstance(this, config);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (typeof data[config] === 'undefined') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config]();\n    });\n  }\n\n  static clearMenus(event) {\n    if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n      return;\n    }\n\n    const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n\n    for (const toggle of openToggles) {\n      const context = Dropdown.getInstance(toggle);\n\n      if (!context || context._config.autoClose === false) {\n        continue;\n      }\n\n      const composedPath = event.composedPath();\n      const isMenuTarget = composedPath.includes(context._menu);\n\n      if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n        continue;\n      } // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n\n\n      if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n        continue;\n      }\n\n      const relatedTarget = {\n        relatedTarget: context._element\n      };\n\n      if (event.type === 'click') {\n        relatedTarget.clickEvent = event;\n      }\n\n      context._completeHide(relatedTarget);\n    }\n  }\n\n  static dataApiKeydownHandler(event) {\n    // If not an UP | DOWN | ESCAPE key => not a dropdown command\n    // If input/textarea && if key is other than ESCAPE => not a dropdown command\n    const isInput = /input|textarea/i.test(event.target.tagName);\n    const isEscapeEvent = event.key === ESCAPE_KEY$2;\n    const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n\n    if (!isUpOrDownEvent && !isEscapeEvent) {\n      return;\n    }\n\n    if (isInput && !isEscapeEvent) {\n      return;\n    }\n\n    event.preventDefault(); // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/\n\n    const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n    const instance = Dropdown.getOrCreateInstance(getToggleButton);\n\n    if (isUpOrDownEvent) {\n      event.stopPropagation();\n      instance.show();\n\n      instance._selectMenuItem(event);\n\n      return;\n    }\n\n    if (instance._isShown()) {\n      // else is escape and we check if it is shown\n      event.stopPropagation();\n      instance.hide();\n      getToggleButton.focus();\n    }\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n  event.preventDefault();\n  Dropdown.getOrCreateInstance(this).toggle();\n});\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\nconst SELECTOR_STICKY_CONTENT = '.sticky-top';\nconst PROPERTY_PADDING = 'padding-right';\nconst PROPERTY_MARGIN = 'margin-right';\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n  constructor() {\n    this._element = document.body;\n  } // Public\n\n\n  getWidth() {\n    // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n    const documentWidth = document.documentElement.clientWidth;\n    return Math.abs(window.innerWidth - documentWidth);\n  }\n\n  hide() {\n    const width = this.getWidth();\n\n    this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width\n\n\n    this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n\n\n    this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n\n    this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n  }\n\n  reset() {\n    this._resetElementAttributes(this._element, 'overflow');\n\n    this._resetElementAttributes(this._element, PROPERTY_PADDING);\n\n    this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n\n    this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n  }\n\n  isOverflowing() {\n    return this.getWidth() > 0;\n  } // Private\n\n\n  _disableOverFlow() {\n    this._saveInitialAttribute(this._element, 'overflow');\n\n    this._element.style.overflow = 'hidden';\n  }\n\n  _setElementAttributes(selector, styleProperty, callback) {\n    const scrollbarWidth = this.getWidth();\n\n    const manipulationCallBack = element => {\n      if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n        return;\n      }\n\n      this._saveInitialAttribute(element, styleProperty);\n\n      const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n      element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n    };\n\n    this._applyManipulationCallback(selector, manipulationCallBack);\n  }\n\n  _saveInitialAttribute(element, styleProperty) {\n    const actualValue = element.style.getPropertyValue(styleProperty);\n\n    if (actualValue) {\n      Manipulator.setDataAttribute(element, styleProperty, actualValue);\n    }\n  }\n\n  _resetElementAttributes(selector, styleProperty) {\n    const manipulationCallBack = element => {\n      const value = Manipulator.getDataAttribute(element, styleProperty); // We only want to remove the property if the value is `null`; the value can also be zero\n\n      if (value === null) {\n        element.style.removeProperty(styleProperty);\n        return;\n      }\n\n      Manipulator.removeDataAttribute(element, styleProperty);\n      element.style.setProperty(styleProperty, value);\n    };\n\n    this._applyManipulationCallback(selector, manipulationCallBack);\n  }\n\n  _applyManipulationCallback(selector, callBack) {\n    if (isElement(selector)) {\n      callBack(selector);\n      return;\n    }\n\n    for (const sel of SelectorEngine.find(selector, this._element)) {\n      callBack(sel);\n    }\n  }\n\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$9 = 'backdrop';\nconst CLASS_NAME_FADE$4 = 'fade';\nconst CLASS_NAME_SHOW$5 = 'show';\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\nconst Default$8 = {\n  className: 'modal-backdrop',\n  clickCallback: null,\n  isAnimated: false,\n  isVisible: true,\n  // if false, we use the backdrop helper without adding any element to the dom\n  rootElement: 'body' // give the choice to place backdrop under different elements\n\n};\nconst DefaultType$8 = {\n  className: 'string',\n  clickCallback: '(function|null)',\n  isAnimated: 'boolean',\n  isVisible: 'boolean',\n  rootElement: '(element|string)'\n};\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n  constructor(config) {\n    super();\n    this._config = this._getConfig(config);\n    this._isAppended = false;\n    this._element = null;\n  } // Getters\n\n\n  static get Default() {\n    return Default$8;\n  }\n\n  static get DefaultType() {\n    return DefaultType$8;\n  }\n\n  static get NAME() {\n    return NAME$9;\n  } // Public\n\n\n  show(callback) {\n    if (!this._config.isVisible) {\n      execute(callback);\n      return;\n    }\n\n    this._append();\n\n    const element = this._getElement();\n\n    if (this._config.isAnimated) {\n      reflow(element);\n    }\n\n    element.classList.add(CLASS_NAME_SHOW$5);\n\n    this._emulateAnimation(() => {\n      execute(callback);\n    });\n  }\n\n  hide(callback) {\n    if (!this._config.isVisible) {\n      execute(callback);\n      return;\n    }\n\n    this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n\n    this._emulateAnimation(() => {\n      this.dispose();\n      execute(callback);\n    });\n  }\n\n  dispose() {\n    if (!this._isAppended) {\n      return;\n    }\n\n    EventHandler.off(this._element, EVENT_MOUSEDOWN);\n\n    this._element.remove();\n\n    this._isAppended = false;\n  } // Private\n\n\n  _getElement() {\n    if (!this._element) {\n      const backdrop = document.createElement('div');\n      backdrop.className = this._config.className;\n\n      if (this._config.isAnimated) {\n        backdrop.classList.add(CLASS_NAME_FADE$4);\n      }\n\n      this._element = backdrop;\n    }\n\n    return this._element;\n  }\n\n  _configAfterMerge(config) {\n    // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n    config.rootElement = getElement(config.rootElement);\n    return config;\n  }\n\n  _append() {\n    if (this._isAppended) {\n      return;\n    }\n\n    const element = this._getElement();\n\n    this._config.rootElement.append(element);\n\n    EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n      execute(this._config.clickCallback);\n    });\n    this._isAppended = true;\n  }\n\n  _emulateAnimation(callback) {\n    executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n  }\n\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$8 = 'focustrap';\nconst DATA_KEY$5 = 'bs.focustrap';\nconst EVENT_KEY$5 = `.${DATA_KEY$5}`;\nconst EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\nconst TAB_KEY = 'Tab';\nconst TAB_NAV_FORWARD = 'forward';\nconst TAB_NAV_BACKWARD = 'backward';\nconst Default$7 = {\n  autofocus: true,\n  trapElement: null // The element to trap focus inside of\n\n};\nconst DefaultType$7 = {\n  autofocus: 'boolean',\n  trapElement: 'element'\n};\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n  constructor(config) {\n    super();\n    this._config = this._getConfig(config);\n    this._isActive = false;\n    this._lastTabNavDirection = null;\n  } // Getters\n\n\n  static get Default() {\n    return Default$7;\n  }\n\n  static get DefaultType() {\n    return DefaultType$7;\n  }\n\n  static get NAME() {\n    return NAME$8;\n  } // Public\n\n\n  activate() {\n    if (this._isActive) {\n      return;\n    }\n\n    if (this._config.autofocus) {\n      this._config.trapElement.focus();\n    }\n\n    EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n\n    EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n    EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n    this._isActive = true;\n  }\n\n  deactivate() {\n    if (!this._isActive) {\n      return;\n    }\n\n    this._isActive = false;\n    EventHandler.off(document, EVENT_KEY$5);\n  } // Private\n\n\n  _handleFocusin(event) {\n    const {\n      trapElement\n    } = this._config;\n\n    if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n      return;\n    }\n\n    const elements = SelectorEngine.focusableChildren(trapElement);\n\n    if (elements.length === 0) {\n      trapElement.focus();\n    } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n      elements[elements.length - 1].focus();\n    } else {\n      elements[0].focus();\n    }\n  }\n\n  _handleKeydown(event) {\n    if (event.key !== TAB_KEY) {\n      return;\n    }\n\n    this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n  }\n\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$7 = 'modal';\nconst DATA_KEY$4 = 'bs.modal';\nconst EVENT_KEY$4 = `.${DATA_KEY$4}`;\nconst DATA_API_KEY$2 = '.data-api';\nconst ESCAPE_KEY$1 = 'Escape';\nconst EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\nconst EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\nconst EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\nconst EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\nconst EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\nconst EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\nconst EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\nconst EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\nconst CLASS_NAME_OPEN = 'modal-open';\nconst CLASS_NAME_FADE$3 = 'fade';\nconst CLASS_NAME_SHOW$4 = 'show';\nconst CLASS_NAME_STATIC = 'modal-static';\nconst OPEN_SELECTOR$1 = '.modal.show';\nconst SELECTOR_DIALOG = '.modal-dialog';\nconst SELECTOR_MODAL_BODY = '.modal-body';\nconst SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\nconst Default$6 = {\n  backdrop: true,\n  focus: true,\n  keyboard: true\n};\nconst DefaultType$6 = {\n  backdrop: '(boolean|string)',\n  focus: 'boolean',\n  keyboard: 'boolean'\n};\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n  constructor(element, config) {\n    super(element, config);\n    this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n    this._backdrop = this._initializeBackDrop();\n    this._focustrap = this._initializeFocusTrap();\n    this._isShown = false;\n    this._isTransitioning = false;\n    this._scrollBar = new ScrollBarHelper();\n\n    this._addEventListeners();\n  } // Getters\n\n\n  static get Default() {\n    return Default$6;\n  }\n\n  static get DefaultType() {\n    return DefaultType$6;\n  }\n\n  static get NAME() {\n    return NAME$7;\n  } // Public\n\n\n  toggle(relatedTarget) {\n    return this._isShown ? this.hide() : this.show(relatedTarget);\n  }\n\n  show(relatedTarget) {\n    if (this._isShown || this._isTransitioning) {\n      return;\n    }\n\n    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n      relatedTarget\n    });\n\n    if (showEvent.defaultPrevented) {\n      return;\n    }\n\n    this._isShown = true;\n    this._isTransitioning = true;\n\n    this._scrollBar.hide();\n\n    document.body.classList.add(CLASS_NAME_OPEN);\n\n    this._adjustDialog();\n\n    this._backdrop.show(() => this._showElement(relatedTarget));\n  }\n\n  hide() {\n    if (!this._isShown || this._isTransitioning) {\n      return;\n    }\n\n    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n\n    if (hideEvent.defaultPrevented) {\n      return;\n    }\n\n    this._isShown = false;\n    this._isTransitioning = true;\n\n    this._focustrap.deactivate();\n\n    this._element.classList.remove(CLASS_NAME_SHOW$4);\n\n    this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n  }\n\n  dispose() {\n    for (const htmlElement of [window, this._dialog]) {\n      EventHandler.off(htmlElement, EVENT_KEY$4);\n    }\n\n    this._backdrop.dispose();\n\n    this._focustrap.deactivate();\n\n    super.dispose();\n  }\n\n  handleUpdate() {\n    this._adjustDialog();\n  } // Private\n\n\n  _initializeBackDrop() {\n    return new Backdrop({\n      isVisible: Boolean(this._config.backdrop),\n      // 'static' option will be translated to true, and booleans will keep their value,\n      isAnimated: this._isAnimated()\n    });\n  }\n\n  _initializeFocusTrap() {\n    return new FocusTrap({\n      trapElement: this._element\n    });\n  }\n\n  _showElement(relatedTarget) {\n    // try to append dynamic modal\n    if (!document.body.contains(this._element)) {\n      document.body.append(this._element);\n    }\n\n    this._element.style.display = 'block';\n\n    this._element.removeAttribute('aria-hidden');\n\n    this._element.setAttribute('aria-modal', true);\n\n    this._element.setAttribute('role', 'dialog');\n\n    this._element.scrollTop = 0;\n    const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n\n    if (modalBody) {\n      modalBody.scrollTop = 0;\n    }\n\n    reflow(this._element);\n\n    this._element.classList.add(CLASS_NAME_SHOW$4);\n\n    const transitionComplete = () => {\n      if (this._config.focus) {\n        this._focustrap.activate();\n      }\n\n      this._isTransitioning = false;\n      EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n        relatedTarget\n      });\n    };\n\n    this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n  }\n\n  _addEventListeners() {\n    EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n      if (event.key !== ESCAPE_KEY$1) {\n        return;\n      }\n\n      if (this._config.keyboard) {\n        event.preventDefault();\n        this.hide();\n        return;\n      }\n\n      this._triggerBackdropTransition();\n    });\n    EventHandler.on(window, EVENT_RESIZE$1, () => {\n      if (this._isShown && !this._isTransitioning) {\n        this._adjustDialog();\n      }\n    });\n    EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n      // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n      EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n        if (this._element !== event.target || this._element !== event2.target) {\n          return;\n        }\n\n        if (this._config.backdrop === 'static') {\n          this._triggerBackdropTransition();\n\n          return;\n        }\n\n        if (this._config.backdrop) {\n          this.hide();\n        }\n      });\n    });\n  }\n\n  _hideModal() {\n    this._element.style.display = 'none';\n\n    this._element.setAttribute('aria-hidden', true);\n\n    this._element.removeAttribute('aria-modal');\n\n    this._element.removeAttribute('role');\n\n    this._isTransitioning = false;\n\n    this._backdrop.hide(() => {\n      document.body.classList.remove(CLASS_NAME_OPEN);\n\n      this._resetAdjustments();\n\n      this._scrollBar.reset();\n\n      EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n    });\n  }\n\n  _isAnimated() {\n    return this._element.classList.contains(CLASS_NAME_FADE$3);\n  }\n\n  _triggerBackdropTransition() {\n    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n\n    if (hideEvent.defaultPrevented) {\n      return;\n    }\n\n    const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n    const initialOverflowY = this._element.style.overflowY; // return if the following background transition hasn't yet completed\n\n    if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n      return;\n    }\n\n    if (!isModalOverflowing) {\n      this._element.style.overflowY = 'hidden';\n    }\n\n    this._element.classList.add(CLASS_NAME_STATIC);\n\n    this._queueCallback(() => {\n      this._element.classList.remove(CLASS_NAME_STATIC);\n\n      this._queueCallback(() => {\n        this._element.style.overflowY = initialOverflowY;\n      }, this._dialog);\n    }, this._dialog);\n\n    this._element.focus();\n  }\n  /**\n   * The following methods are used to handle overflowing modals\n   */\n\n\n  _adjustDialog() {\n    const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n\n    const scrollbarWidth = this._scrollBar.getWidth();\n\n    const isBodyOverflowing = scrollbarWidth > 0;\n\n    if (isBodyOverflowing && !isModalOverflowing) {\n      const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n      this._element.style[property] = `${scrollbarWidth}px`;\n    }\n\n    if (!isBodyOverflowing && isModalOverflowing) {\n      const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n      this._element.style[property] = `${scrollbarWidth}px`;\n    }\n  }\n\n  _resetAdjustments() {\n    this._element.style.paddingLeft = '';\n    this._element.style.paddingRight = '';\n  } // Static\n\n\n  static jQueryInterface(config, relatedTarget) {\n    return this.each(function () {\n      const data = Modal.getOrCreateInstance(this, config);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (typeof data[config] === 'undefined') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config](relatedTarget);\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n  const target = getElementFromSelector(this);\n\n  if (['A', 'AREA'].includes(this.tagName)) {\n    event.preventDefault();\n  }\n\n  EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n    if (showEvent.defaultPrevented) {\n      // only register focus restorer if modal will actually get shown\n      return;\n    }\n\n    EventHandler.one(target, EVENT_HIDDEN$4, () => {\n      if (isVisible(this)) {\n        this.focus();\n      }\n    });\n  }); // avoid conflict when clicking modal toggler while another one is open\n\n  const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n\n  if (alreadyOpen) {\n    Modal.getInstance(alreadyOpen).hide();\n  }\n\n  const data = Modal.getOrCreateInstance(target);\n  data.toggle(this);\n});\nenableDismissTrigger(Modal);\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$6 = 'offcanvas';\nconst DATA_KEY$3 = 'bs.offcanvas';\nconst EVENT_KEY$3 = `.${DATA_KEY$3}`;\nconst DATA_API_KEY$1 = '.data-api';\nconst EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst ESCAPE_KEY = 'Escape';\nconst CLASS_NAME_SHOW$3 = 'show';\nconst CLASS_NAME_SHOWING$1 = 'showing';\nconst CLASS_NAME_HIDING = 'hiding';\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\nconst OPEN_SELECTOR = '.offcanvas.show';\nconst EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\nconst EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\nconst EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\nconst EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\nconst EVENT_RESIZE = `resize${EVENT_KEY$3}`;\nconst EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\nconst SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\nconst Default$5 = {\n  backdrop: true,\n  keyboard: true,\n  scroll: false\n};\nconst DefaultType$5 = {\n  backdrop: '(boolean|string)',\n  keyboard: 'boolean',\n  scroll: 'boolean'\n};\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n  constructor(element, config) {\n    super(element, config);\n    this._isShown = false;\n    this._backdrop = this._initializeBackDrop();\n    this._focustrap = this._initializeFocusTrap();\n\n    this._addEventListeners();\n  } // Getters\n\n\n  static get Default() {\n    return Default$5;\n  }\n\n  static get DefaultType() {\n    return DefaultType$5;\n  }\n\n  static get NAME() {\n    return NAME$6;\n  } // Public\n\n\n  toggle(relatedTarget) {\n    return this._isShown ? this.hide() : this.show(relatedTarget);\n  }\n\n  show(relatedTarget) {\n    if (this._isShown) {\n      return;\n    }\n\n    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n      relatedTarget\n    });\n\n    if (showEvent.defaultPrevented) {\n      return;\n    }\n\n    this._isShown = true;\n\n    this._backdrop.show();\n\n    if (!this._config.scroll) {\n      new ScrollBarHelper().hide();\n    }\n\n    this._element.setAttribute('aria-modal', true);\n\n    this._element.setAttribute('role', 'dialog');\n\n    this._element.classList.add(CLASS_NAME_SHOWING$1);\n\n    const completeCallBack = () => {\n      if (!this._config.scroll || this._config.backdrop) {\n        this._focustrap.activate();\n      }\n\n      this._element.classList.add(CLASS_NAME_SHOW$3);\n\n      this._element.classList.remove(CLASS_NAME_SHOWING$1);\n\n      EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n        relatedTarget\n      });\n    };\n\n    this._queueCallback(completeCallBack, this._element, true);\n  }\n\n  hide() {\n    if (!this._isShown) {\n      return;\n    }\n\n    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n\n    if (hideEvent.defaultPrevented) {\n      return;\n    }\n\n    this._focustrap.deactivate();\n\n    this._element.blur();\n\n    this._isShown = false;\n\n    this._element.classList.add(CLASS_NAME_HIDING);\n\n    this._backdrop.hide();\n\n    const completeCallback = () => {\n      this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n\n      this._element.removeAttribute('aria-modal');\n\n      this._element.removeAttribute('role');\n\n      if (!this._config.scroll) {\n        new ScrollBarHelper().reset();\n      }\n\n      EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n    };\n\n    this._queueCallback(completeCallback, this._element, true);\n  }\n\n  dispose() {\n    this._backdrop.dispose();\n\n    this._focustrap.deactivate();\n\n    super.dispose();\n  } // Private\n\n\n  _initializeBackDrop() {\n    const clickCallback = () => {\n      if (this._config.backdrop === 'static') {\n        EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n        return;\n      }\n\n      this.hide();\n    }; // 'static' option will be translated to true, and booleans will keep their value\n\n\n    const isVisible = Boolean(this._config.backdrop);\n    return new Backdrop({\n      className: CLASS_NAME_BACKDROP,\n      isVisible,\n      isAnimated: true,\n      rootElement: this._element.parentNode,\n      clickCallback: isVisible ? clickCallback : null\n    });\n  }\n\n  _initializeFocusTrap() {\n    return new FocusTrap({\n      trapElement: this._element\n    });\n  }\n\n  _addEventListeners() {\n    EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n      if (event.key !== ESCAPE_KEY) {\n        return;\n      }\n\n      if (!this._config.keyboard) {\n        EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n        return;\n      }\n\n      this.hide();\n    });\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Offcanvas.getOrCreateInstance(this, config);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config](this);\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n  const target = getElementFromSelector(this);\n\n  if (['A', 'AREA'].includes(this.tagName)) {\n    event.preventDefault();\n  }\n\n  if (isDisabled(this)) {\n    return;\n  }\n\n  EventHandler.one(target, EVENT_HIDDEN$3, () => {\n    // focus on trigger when it is closed\n    if (isVisible(this)) {\n      this.focus();\n    }\n  }); // avoid conflict when clicking a toggler of an offcanvas, while another is open\n\n  const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n\n  if (alreadyOpen && alreadyOpen !== target) {\n    Offcanvas.getInstance(alreadyOpen).hide();\n  }\n\n  const data = Offcanvas.getOrCreateInstance(target);\n  data.toggle(this);\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n  for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n    Offcanvas.getOrCreateInstance(selector).show();\n  }\n});\nEventHandler.on(window, EVENT_RESIZE, () => {\n  for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n    if (getComputedStyle(element).position !== 'fixed') {\n      Offcanvas.getOrCreateInstance(element).hide();\n    }\n  }\n});\nenableDismissTrigger(Offcanvas);\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\nconst uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\n/**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n */\n\nconst SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;\n/**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n */\n\nconst DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[\\d+/a-z]+=*$/i;\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n  const attributeName = attribute.nodeName.toLowerCase();\n\n  if (allowedAttributeList.includes(attributeName)) {\n    if (uriAttributes.has(attributeName)) {\n      return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));\n    }\n\n    return true;\n  } // Check if a regular expression validates the attribute.\n\n\n  return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n};\n\nconst DefaultAllowlist = {\n  // Global attributes allowed on any supplied element below.\n  '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n  a: ['target', 'href', 'title', 'rel'],\n  area: [],\n  b: [],\n  br: [],\n  col: [],\n  code: [],\n  div: [],\n  em: [],\n  hr: [],\n  h1: [],\n  h2: [],\n  h3: [],\n  h4: [],\n  h5: [],\n  h6: [],\n  i: [],\n  img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n  li: [],\n  ol: [],\n  p: [],\n  pre: [],\n  s: [],\n  small: [],\n  span: [],\n  sub: [],\n  sup: [],\n  strong: [],\n  u: [],\n  ul: []\n};\nfunction sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n  if (!unsafeHtml.length) {\n    return unsafeHtml;\n  }\n\n  if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n    return sanitizeFunction(unsafeHtml);\n  }\n\n  const domParser = new window.DOMParser();\n  const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n  const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n\n  for (const element of elements) {\n    const elementName = element.nodeName.toLowerCase();\n\n    if (!Object.keys(allowList).includes(elementName)) {\n      element.remove();\n      continue;\n    }\n\n    const attributeList = [].concat(...element.attributes);\n    const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n\n    for (const attribute of attributeList) {\n      if (!allowedAttribute(attribute, allowedAttributes)) {\n        element.removeAttribute(attribute.nodeName);\n      }\n    }\n  }\n\n  return createdDocument.body.innerHTML;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$5 = 'TemplateFactory';\nconst Default$4 = {\n  allowList: DefaultAllowlist,\n  content: {},\n  // { selector : text ,  selector2 : text2 , }\n  extraClass: '',\n  html: false,\n  sanitize: true,\n  sanitizeFn: null,\n  template: '<div></div>'\n};\nconst DefaultType$4 = {\n  allowList: 'object',\n  content: 'object',\n  extraClass: '(string|function)',\n  html: 'boolean',\n  sanitize: 'boolean',\n  sanitizeFn: '(null|function)',\n  template: 'string'\n};\nconst DefaultContentType = {\n  entry: '(string|element|function|null)',\n  selector: '(string|element)'\n};\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n  constructor(config) {\n    super();\n    this._config = this._getConfig(config);\n  } // Getters\n\n\n  static get Default() {\n    return Default$4;\n  }\n\n  static get DefaultType() {\n    return DefaultType$4;\n  }\n\n  static get NAME() {\n    return NAME$5;\n  } // Public\n\n\n  getContent() {\n    return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n  }\n\n  hasContent() {\n    return this.getContent().length > 0;\n  }\n\n  changeContent(content) {\n    this._checkContent(content);\n\n    this._config.content = { ...this._config.content,\n      ...content\n    };\n    return this;\n  }\n\n  toHtml() {\n    const templateWrapper = document.createElement('div');\n    templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n\n    for (const [selector, text] of Object.entries(this._config.content)) {\n      this._setContent(templateWrapper, text, selector);\n    }\n\n    const template = templateWrapper.children[0];\n\n    const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n\n    if (extraClass) {\n      template.classList.add(...extraClass.split(' '));\n    }\n\n    return template;\n  } // Private\n\n\n  _typeCheckConfig(config) {\n    super._typeCheckConfig(config);\n\n    this._checkContent(config.content);\n  }\n\n  _checkContent(arg) {\n    for (const [selector, content] of Object.entries(arg)) {\n      super._typeCheckConfig({\n        selector,\n        entry: content\n      }, DefaultContentType);\n    }\n  }\n\n  _setContent(template, content, selector) {\n    const templateElement = SelectorEngine.findOne(selector, template);\n\n    if (!templateElement) {\n      return;\n    }\n\n    content = this._resolvePossibleFunction(content);\n\n    if (!content) {\n      templateElement.remove();\n      return;\n    }\n\n    if (isElement(content)) {\n      this._putElementInTemplate(getElement(content), templateElement);\n\n      return;\n    }\n\n    if (this._config.html) {\n      templateElement.innerHTML = this._maybeSanitize(content);\n      return;\n    }\n\n    templateElement.textContent = content;\n  }\n\n  _maybeSanitize(arg) {\n    return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n  }\n\n  _resolvePossibleFunction(arg) {\n    return typeof arg === 'function' ? arg(this) : arg;\n  }\n\n  _putElementInTemplate(element, templateElement) {\n    if (this._config.html) {\n      templateElement.innerHTML = '';\n      templateElement.append(element);\n      return;\n    }\n\n    templateElement.textContent = element.textContent;\n  }\n\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$4 = 'tooltip';\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\nconst CLASS_NAME_FADE$2 = 'fade';\nconst CLASS_NAME_MODAL = 'modal';\nconst CLASS_NAME_SHOW$2 = 'show';\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\nconst EVENT_MODAL_HIDE = 'hide.bs.modal';\nconst TRIGGER_HOVER = 'hover';\nconst TRIGGER_FOCUS = 'focus';\nconst TRIGGER_CLICK = 'click';\nconst TRIGGER_MANUAL = 'manual';\nconst EVENT_HIDE$2 = 'hide';\nconst EVENT_HIDDEN$2 = 'hidden';\nconst EVENT_SHOW$2 = 'show';\nconst EVENT_SHOWN$2 = 'shown';\nconst EVENT_INSERTED = 'inserted';\nconst EVENT_CLICK$1 = 'click';\nconst EVENT_FOCUSIN$1 = 'focusin';\nconst EVENT_FOCUSOUT$1 = 'focusout';\nconst EVENT_MOUSEENTER = 'mouseenter';\nconst EVENT_MOUSELEAVE = 'mouseleave';\nconst AttachmentMap = {\n  AUTO: 'auto',\n  TOP: 'top',\n  RIGHT: isRTL() ? 'left' : 'right',\n  BOTTOM: 'bottom',\n  LEFT: isRTL() ? 'right' : 'left'\n};\nconst Default$3 = {\n  allowList: DefaultAllowlist,\n  animation: true,\n  boundary: 'clippingParents',\n  container: false,\n  customClass: '',\n  delay: 0,\n  fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n  html: false,\n  offset: [0, 0],\n  placement: 'top',\n  popperConfig: null,\n  sanitize: true,\n  sanitizeFn: null,\n  selector: false,\n  template: '<div class=\"tooltip\" role=\"tooltip\">' + '<div class=\"tooltip-arrow\"></div>' + '<div class=\"tooltip-inner\"></div>' + '</div>',\n  title: '',\n  trigger: 'hover focus'\n};\nconst DefaultType$3 = {\n  allowList: 'object',\n  animation: 'boolean',\n  boundary: '(string|element)',\n  container: '(string|element|boolean)',\n  customClass: '(string|function)',\n  delay: '(number|object)',\n  fallbackPlacements: 'array',\n  html: 'boolean',\n  offset: '(array|string|function)',\n  placement: '(string|function)',\n  popperConfig: '(null|object|function)',\n  sanitize: 'boolean',\n  sanitizeFn: '(null|function)',\n  selector: '(string|boolean)',\n  template: 'string',\n  title: '(string|element|function)',\n  trigger: 'string'\n};\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n  constructor(element, config) {\n    if (typeof Popper === 'undefined') {\n      throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n    }\n\n    super(element, config); // Private\n\n    this._isEnabled = true;\n    this._timeout = 0;\n    this._isHovered = null;\n    this._activeTrigger = {};\n    this._popper = null;\n    this._templateFactory = null;\n    this._newContent = null; // Protected\n\n    this.tip = null;\n\n    this._setListeners();\n\n    if (!this._config.selector) {\n      this._fixTitle();\n    }\n  } // Getters\n\n\n  static get Default() {\n    return Default$3;\n  }\n\n  static get DefaultType() {\n    return DefaultType$3;\n  }\n\n  static get NAME() {\n    return NAME$4;\n  } // Public\n\n\n  enable() {\n    this._isEnabled = true;\n  }\n\n  disable() {\n    this._isEnabled = false;\n  }\n\n  toggleEnabled() {\n    this._isEnabled = !this._isEnabled;\n  }\n\n  toggle() {\n    if (!this._isEnabled) {\n      return;\n    }\n\n    this._activeTrigger.click = !this._activeTrigger.click;\n\n    if (this._isShown()) {\n      this._leave();\n\n      return;\n    }\n\n    this._enter();\n  }\n\n  dispose() {\n    clearTimeout(this._timeout);\n    EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n\n    if (this._element.getAttribute('data-bs-original-title')) {\n      this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n    }\n\n    this._disposePopper();\n\n    super.dispose();\n  }\n\n  show() {\n    if (this._element.style.display === 'none') {\n      throw new Error('Please use show on visible elements');\n    }\n\n    if (!(this._isWithContent() && this._isEnabled)) {\n      return;\n    }\n\n    const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n    const shadowRoot = findShadowRoot(this._element);\n\n    const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n\n    if (showEvent.defaultPrevented || !isInTheDom) {\n      return;\n    } // todo v6 remove this OR make it optional\n\n\n    this._disposePopper();\n\n    const tip = this._getTipElement();\n\n    this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n\n    const {\n      container\n    } = this._config;\n\n    if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n      container.append(tip);\n      EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n    }\n\n    this._popper = this._createPopper(tip);\n    tip.classList.add(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we add extra\n    // empty mouseover listeners to the body's immediate children;\n    // only needed because of broken event delegation on iOS\n    // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n\n    if ('ontouchstart' in document.documentElement) {\n      for (const element of [].concat(...document.body.children)) {\n        EventHandler.on(element, 'mouseover', noop);\n      }\n    }\n\n    const complete = () => {\n      EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n\n      if (this._isHovered === false) {\n        this._leave();\n      }\n\n      this._isHovered = false;\n    };\n\n    this._queueCallback(complete, this.tip, this._isAnimated());\n  }\n\n  hide() {\n    if (!this._isShown()) {\n      return;\n    }\n\n    const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n\n    if (hideEvent.defaultPrevented) {\n      return;\n    }\n\n    const tip = this._getTipElement();\n\n    tip.classList.remove(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we remove the extra\n    // empty mouseover listeners we added for iOS support\n\n    if ('ontouchstart' in document.documentElement) {\n      for (const element of [].concat(...document.body.children)) {\n        EventHandler.off(element, 'mouseover', noop);\n      }\n    }\n\n    this._activeTrigger[TRIGGER_CLICK] = false;\n    this._activeTrigger[TRIGGER_FOCUS] = false;\n    this._activeTrigger[TRIGGER_HOVER] = false;\n    this._isHovered = null; // it is a trick to support manual triggering\n\n    const complete = () => {\n      if (this._isWithActiveTrigger()) {\n        return;\n      }\n\n      if (!this._isHovered) {\n        this._disposePopper();\n      }\n\n      this._element.removeAttribute('aria-describedby');\n\n      EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n    };\n\n    this._queueCallback(complete, this.tip, this._isAnimated());\n  }\n\n  update() {\n    if (this._popper) {\n      this._popper.update();\n    }\n  } // Protected\n\n\n  _isWithContent() {\n    return Boolean(this._getTitle());\n  }\n\n  _getTipElement() {\n    if (!this.tip) {\n      this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n    }\n\n    return this.tip;\n  }\n\n  _createTipElement(content) {\n    const tip = this._getTemplateFactory(content).toHtml(); // todo: remove this check on v6\n\n\n    if (!tip) {\n      return null;\n    }\n\n    tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); // todo: on v6 the following can be achieved with CSS only\n\n    tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n    const tipId = getUID(this.constructor.NAME).toString();\n    tip.setAttribute('id', tipId);\n\n    if (this._isAnimated()) {\n      tip.classList.add(CLASS_NAME_FADE$2);\n    }\n\n    return tip;\n  }\n\n  setContent(content) {\n    this._newContent = content;\n\n    if (this._isShown()) {\n      this._disposePopper();\n\n      this.show();\n    }\n  }\n\n  _getTemplateFactory(content) {\n    if (this._templateFactory) {\n      this._templateFactory.changeContent(content);\n    } else {\n      this._templateFactory = new TemplateFactory({ ...this._config,\n        // the `content` var has to be after `this._config`\n        // to override config.content in case of popover\n        content,\n        extraClass: this._resolvePossibleFunction(this._config.customClass)\n      });\n    }\n\n    return this._templateFactory;\n  }\n\n  _getContentForTemplate() {\n    return {\n      [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n    };\n  }\n\n  _getTitle() {\n    return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n  } // Private\n\n\n  _initializeOnDelegatedTarget(event) {\n    return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n  }\n\n  _isAnimated() {\n    return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n  }\n\n  _isShown() {\n    return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n  }\n\n  _createPopper(tip) {\n    const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;\n    const attachment = AttachmentMap[placement.toUpperCase()];\n    return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment));\n  }\n\n  _getOffset() {\n    const {\n      offset\n    } = this._config;\n\n    if (typeof offset === 'string') {\n      return offset.split(',').map(value => Number.parseInt(value, 10));\n    }\n\n    if (typeof offset === 'function') {\n      return popperData => offset(popperData, this._element);\n    }\n\n    return offset;\n  }\n\n  _resolvePossibleFunction(arg) {\n    return typeof arg === 'function' ? arg.call(this._element) : arg;\n  }\n\n  _getPopperConfig(attachment) {\n    const defaultBsPopperConfig = {\n      placement: attachment,\n      modifiers: [{\n        name: 'flip',\n        options: {\n          fallbackPlacements: this._config.fallbackPlacements\n        }\n      }, {\n        name: 'offset',\n        options: {\n          offset: this._getOffset()\n        }\n      }, {\n        name: 'preventOverflow',\n        options: {\n          boundary: this._config.boundary\n        }\n      }, {\n        name: 'arrow',\n        options: {\n          element: `.${this.constructor.NAME}-arrow`\n        }\n      }, {\n        name: 'preSetPlacement',\n        enabled: true,\n        phase: 'beforeMain',\n        fn: data => {\n          // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n          // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n          this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n        }\n      }]\n    };\n    return { ...defaultBsPopperConfig,\n      ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n    };\n  }\n\n  _setListeners() {\n    const triggers = this._config.trigger.split(' ');\n\n    for (const trigger of triggers) {\n      if (trigger === 'click') {\n        EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n          const context = this._initializeOnDelegatedTarget(event);\n\n          context.toggle();\n        });\n      } else if (trigger !== TRIGGER_MANUAL) {\n        const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n        const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n        EventHandler.on(this._element, eventIn, this._config.selector, event => {\n          const context = this._initializeOnDelegatedTarget(event);\n\n          context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n\n          context._enter();\n        });\n        EventHandler.on(this._element, eventOut, this._config.selector, event => {\n          const context = this._initializeOnDelegatedTarget(event);\n\n          context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n\n          context._leave();\n        });\n      }\n    }\n\n    this._hideModalHandler = () => {\n      if (this._element) {\n        this.hide();\n      }\n    };\n\n    EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n  }\n\n  _fixTitle() {\n    const title = this._element.getAttribute('title');\n\n    if (!title) {\n      return;\n    }\n\n    if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n      this._element.setAttribute('aria-label', title);\n    }\n\n    this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n\n\n    this._element.removeAttribute('title');\n  }\n\n  _enter() {\n    if (this._isShown() || this._isHovered) {\n      this._isHovered = true;\n      return;\n    }\n\n    this._isHovered = true;\n\n    this._setTimeout(() => {\n      if (this._isHovered) {\n        this.show();\n      }\n    }, this._config.delay.show);\n  }\n\n  _leave() {\n    if (this._isWithActiveTrigger()) {\n      return;\n    }\n\n    this._isHovered = false;\n\n    this._setTimeout(() => {\n      if (!this._isHovered) {\n        this.hide();\n      }\n    }, this._config.delay.hide);\n  }\n\n  _setTimeout(handler, timeout) {\n    clearTimeout(this._timeout);\n    this._timeout = setTimeout(handler, timeout);\n  }\n\n  _isWithActiveTrigger() {\n    return Object.values(this._activeTrigger).includes(true);\n  }\n\n  _getConfig(config) {\n    const dataAttributes = Manipulator.getDataAttributes(this._element);\n\n    for (const dataAttribute of Object.keys(dataAttributes)) {\n      if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n        delete dataAttributes[dataAttribute];\n      }\n    }\n\n    config = { ...dataAttributes,\n      ...(typeof config === 'object' && config ? config : {})\n    };\n    config = this._mergeConfigObj(config);\n    config = this._configAfterMerge(config);\n\n    this._typeCheckConfig(config);\n\n    return config;\n  }\n\n  _configAfterMerge(config) {\n    config.container = config.container === false ? document.body : getElement(config.container);\n\n    if (typeof config.delay === 'number') {\n      config.delay = {\n        show: config.delay,\n        hide: config.delay\n      };\n    }\n\n    if (typeof config.title === 'number') {\n      config.title = config.title.toString();\n    }\n\n    if (typeof config.content === 'number') {\n      config.content = config.content.toString();\n    }\n\n    return config;\n  }\n\n  _getDelegateConfig() {\n    const config = {};\n\n    for (const key in this._config) {\n      if (this.constructor.Default[key] !== this._config[key]) {\n        config[key] = this._config[key];\n      }\n    }\n\n    config.selector = false;\n    config.trigger = 'manual'; // In the future can be replaced with:\n    // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n    // `Object.fromEntries(keysWithDifferentValues)`\n\n    return config;\n  }\n\n  _disposePopper() {\n    if (this._popper) {\n      this._popper.destroy();\n\n      this._popper = null;\n    }\n\n    if (this.tip) {\n      this.tip.remove();\n      this.tip = null;\n    }\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Tooltip.getOrCreateInstance(this, config);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (typeof data[config] === 'undefined') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config]();\n    });\n  }\n\n}\n/**\n * jQuery\n */\n\n\ndefineJQueryPlugin(Tooltip);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$3 = 'popover';\nconst SELECTOR_TITLE = '.popover-header';\nconst SELECTOR_CONTENT = '.popover-body';\nconst Default$2 = { ...Tooltip.Default,\n  content: '',\n  offset: [0, 8],\n  placement: 'right',\n  template: '<div class=\"popover\" role=\"tooltip\">' + '<div class=\"popover-arrow\"></div>' + '<h3 class=\"popover-header\"></h3>' + '<div class=\"popover-body\"></div>' + '</div>',\n  trigger: 'click'\n};\nconst DefaultType$2 = { ...Tooltip.DefaultType,\n  content: '(null|string|element|function)'\n};\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n  // Getters\n  static get Default() {\n    return Default$2;\n  }\n\n  static get DefaultType() {\n    return DefaultType$2;\n  }\n\n  static get NAME() {\n    return NAME$3;\n  } // Overrides\n\n\n  _isWithContent() {\n    return this._getTitle() || this._getContent();\n  } // Private\n\n\n  _getContentForTemplate() {\n    return {\n      [SELECTOR_TITLE]: this._getTitle(),\n      [SELECTOR_CONTENT]: this._getContent()\n    };\n  }\n\n  _getContent() {\n    return this._resolvePossibleFunction(this._config.content);\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Popover.getOrCreateInstance(this, config);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (typeof data[config] === 'undefined') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config]();\n    });\n  }\n\n}\n/**\n * jQuery\n */\n\n\ndefineJQueryPlugin(Popover);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$2 = 'scrollspy';\nconst DATA_KEY$2 = 'bs.scrollspy';\nconst EVENT_KEY$2 = `.${DATA_KEY$2}`;\nconst DATA_API_KEY = '.data-api';\nconst EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\nconst EVENT_CLICK = `click${EVENT_KEY$2}`;\nconst EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\nconst CLASS_NAME_ACTIVE$1 = 'active';\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\nconst SELECTOR_TARGET_LINKS = '[href]';\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\nconst SELECTOR_NAV_LINKS = '.nav-link';\nconst SELECTOR_NAV_ITEMS = '.nav-item';\nconst SELECTOR_LIST_ITEMS = '.list-group-item';\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\nconst SELECTOR_DROPDOWN = '.dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\nconst Default$1 = {\n  offset: null,\n  // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n  rootMargin: '0px 0px -25%',\n  smoothScroll: false,\n  target: null,\n  threshold: [0.1, 0.5, 1]\n};\nconst DefaultType$1 = {\n  offset: '(number|null)',\n  // TODO v6 @deprecated, keep it for backwards compatibility reasons\n  rootMargin: 'string',\n  smoothScroll: 'boolean',\n  target: 'element',\n  threshold: 'array'\n};\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n  constructor(element, config) {\n    super(element, config); // this._element is the observablesContainer and config.target the menu links wrapper\n\n    this._targetLinks = new Map();\n    this._observableSections = new Map();\n    this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n    this._activeTarget = null;\n    this._observer = null;\n    this._previousScrollData = {\n      visibleEntryTop: 0,\n      parentScrollTop: 0\n    };\n    this.refresh(); // initialize\n  } // Getters\n\n\n  static get Default() {\n    return Default$1;\n  }\n\n  static get DefaultType() {\n    return DefaultType$1;\n  }\n\n  static get NAME() {\n    return NAME$2;\n  } // Public\n\n\n  refresh() {\n    this._initializeTargetsAndObservables();\n\n    this._maybeEnableSmoothScroll();\n\n    if (this._observer) {\n      this._observer.disconnect();\n    } else {\n      this._observer = this._getNewObserver();\n    }\n\n    for (const section of this._observableSections.values()) {\n      this._observer.observe(section);\n    }\n  }\n\n  dispose() {\n    this._observer.disconnect();\n\n    super.dispose();\n  } // Private\n\n\n  _configAfterMerge(config) {\n    // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n    config.target = getElement(config.target) || document.body; // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n\n    config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n\n    if (typeof config.threshold === 'string') {\n      config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n    }\n\n    return config;\n  }\n\n  _maybeEnableSmoothScroll() {\n    if (!this._config.smoothScroll) {\n      return;\n    } // unregister any previous listeners\n\n\n    EventHandler.off(this._config.target, EVENT_CLICK);\n    EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n      const observableSection = this._observableSections.get(event.target.hash);\n\n      if (observableSection) {\n        event.preventDefault();\n        const root = this._rootElement || window;\n        const height = observableSection.offsetTop - this._element.offsetTop;\n\n        if (root.scrollTo) {\n          root.scrollTo({\n            top: height,\n            behavior: 'smooth'\n          });\n          return;\n        } // Chrome 60 doesn't support `scrollTo`\n\n\n        root.scrollTop = height;\n      }\n    });\n  }\n\n  _getNewObserver() {\n    const options = {\n      root: this._rootElement,\n      threshold: this._config.threshold,\n      rootMargin: this._config.rootMargin\n    };\n    return new IntersectionObserver(entries => this._observerCallback(entries), options);\n  } // The logic of selection\n\n\n  _observerCallback(entries) {\n    const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n\n    const activate = entry => {\n      this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n\n      this._process(targetElement(entry));\n    };\n\n    const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n    const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n    this._previousScrollData.parentScrollTop = parentScrollTop;\n\n    for (const entry of entries) {\n      if (!entry.isIntersecting) {\n        this._activeTarget = null;\n\n        this._clearActiveClass(targetElement(entry));\n\n        continue;\n      }\n\n      const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; // if we are scrolling down, pick the bigger offsetTop\n\n      if (userScrollsDown && entryIsLowerThanPrevious) {\n        activate(entry); // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n\n        if (!parentScrollTop) {\n          return;\n        }\n\n        continue;\n      } // if we are scrolling up, pick the smallest offsetTop\n\n\n      if (!userScrollsDown && !entryIsLowerThanPrevious) {\n        activate(entry);\n      }\n    }\n  }\n\n  _initializeTargetsAndObservables() {\n    this._targetLinks = new Map();\n    this._observableSections = new Map();\n    const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n\n    for (const anchor of targetLinks) {\n      // ensure that the anchor has an id and is not disabled\n      if (!anchor.hash || isDisabled(anchor)) {\n        continue;\n      }\n\n      const observableSection = SelectorEngine.findOne(anchor.hash, this._element); // ensure that the observableSection exists & is visible\n\n      if (isVisible(observableSection)) {\n        this._targetLinks.set(anchor.hash, anchor);\n\n        this._observableSections.set(anchor.hash, observableSection);\n      }\n    }\n  }\n\n  _process(target) {\n    if (this._activeTarget === target) {\n      return;\n    }\n\n    this._clearActiveClass(this._config.target);\n\n    this._activeTarget = target;\n    target.classList.add(CLASS_NAME_ACTIVE$1);\n\n    this._activateParents(target);\n\n    EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n      relatedTarget: target\n    });\n  }\n\n  _activateParents(target) {\n    // Activate dropdown parents\n    if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n      SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n      return;\n    }\n\n    for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n      // Set triggered links parents as active\n      // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n      for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n        item.classList.add(CLASS_NAME_ACTIVE$1);\n      }\n    }\n  }\n\n  _clearActiveClass(parent) {\n    parent.classList.remove(CLASS_NAME_ACTIVE$1);\n    const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE$1}`, parent);\n\n    for (const node of activeNodes) {\n      node.classList.remove(CLASS_NAME_ACTIVE$1);\n    }\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = ScrollSpy.getOrCreateInstance(this, config);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config]();\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(window, EVENT_LOAD_DATA_API$1, () => {\n  for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n    ScrollSpy.getOrCreateInstance(spy);\n  }\n});\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(ScrollSpy);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): tab.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME$1 = 'tab';\nconst DATA_KEY$1 = 'bs.tab';\nconst EVENT_KEY$1 = `.${DATA_KEY$1}`;\nconst EVENT_HIDE$1 = `hide${EVENT_KEY$1}`;\nconst EVENT_HIDDEN$1 = `hidden${EVENT_KEY$1}`;\nconst EVENT_SHOW$1 = `show${EVENT_KEY$1}`;\nconst EVENT_SHOWN$1 = `shown${EVENT_KEY$1}`;\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY$1}`;\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY$1}`;\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY$1}`;\nconst ARROW_LEFT_KEY = 'ArrowLeft';\nconst ARROW_RIGHT_KEY = 'ArrowRight';\nconst ARROW_UP_KEY = 'ArrowUp';\nconst ARROW_DOWN_KEY = 'ArrowDown';\nconst CLASS_NAME_ACTIVE = 'active';\nconst CLASS_NAME_FADE$1 = 'fade';\nconst CLASS_NAME_SHOW$1 = 'show';\nconst CLASS_DROPDOWN = 'dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';\nconst SELECTOR_DROPDOWN_MENU = '.dropdown-menu';\nconst NOT_SELECTOR_DROPDOWN_TOGGLE = ':not(.dropdown-toggle)';\nconst SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]';\nconst SELECTOR_OUTER = '.nav-item, .list-group-item';\nconst SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`;\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]'; // todo:v6: could be only `tab`\n\nconst SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`;\nconst SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`;\n/**\n * Class definition\n */\n\nclass Tab extends BaseComponent {\n  constructor(element) {\n    super(element);\n    this._parent = this._element.closest(SELECTOR_TAB_PANEL);\n\n    if (!this._parent) {\n      return; // todo: should Throw exception on v6\n      // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n    } // Set up initial aria attributes\n\n\n    this._setInitialAttributes(this._parent, this._getChildren());\n\n    EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));\n  } // Getters\n\n\n  static get NAME() {\n    return NAME$1;\n  } // Public\n\n\n  show() {\n    // Shows this elem and deactivate the active sibling if exists\n    const innerElem = this._element;\n\n    if (this._elemIsActive(innerElem)) {\n      return;\n    } // Search for active tab on same parent to deactivate it\n\n\n    const active = this._getActiveElem();\n\n    const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE$1, {\n      relatedTarget: innerElem\n    }) : null;\n    const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW$1, {\n      relatedTarget: active\n    });\n\n    if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) {\n      return;\n    }\n\n    this._deactivate(active, innerElem);\n\n    this._activate(innerElem, active);\n  } // Private\n\n\n  _activate(element, relatedElem) {\n    if (!element) {\n      return;\n    }\n\n    element.classList.add(CLASS_NAME_ACTIVE);\n\n    this._activate(getElementFromSelector(element)); // Search and activate/show the proper section\n\n\n    const complete = () => {\n      if (element.getAttribute('role') !== 'tab') {\n        element.classList.add(CLASS_NAME_SHOW$1);\n        return;\n      }\n\n      element.removeAttribute('tabindex');\n      element.setAttribute('aria-selected', true);\n\n      this._toggleDropDown(element, true);\n\n      EventHandler.trigger(element, EVENT_SHOWN$1, {\n        relatedTarget: relatedElem\n      });\n    };\n\n    this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1));\n  }\n\n  _deactivate(element, relatedElem) {\n    if (!element) {\n      return;\n    }\n\n    element.classList.remove(CLASS_NAME_ACTIVE);\n    element.blur();\n\n    this._deactivate(getElementFromSelector(element)); // Search and deactivate the shown section too\n\n\n    const complete = () => {\n      if (element.getAttribute('role') !== 'tab') {\n        element.classList.remove(CLASS_NAME_SHOW$1);\n        return;\n      }\n\n      element.setAttribute('aria-selected', false);\n      element.setAttribute('tabindex', '-1');\n\n      this._toggleDropDown(element, false);\n\n      EventHandler.trigger(element, EVENT_HIDDEN$1, {\n        relatedTarget: relatedElem\n      });\n    };\n\n    this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1));\n  }\n\n  _keydown(event) {\n    if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {\n      return;\n    }\n\n    event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n\n    event.preventDefault();\n    const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key);\n    const nextActiveElement = getNextActiveElement(this._getChildren().filter(element => !isDisabled(element)), event.target, isNext, true);\n\n    if (nextActiveElement) {\n      nextActiveElement.focus({\n        preventScroll: true\n      });\n      Tab.getOrCreateInstance(nextActiveElement).show();\n    }\n  }\n\n  _getChildren() {\n    // collection of inner elements\n    return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent);\n  }\n\n  _getActiveElem() {\n    return this._getChildren().find(child => this._elemIsActive(child)) || null;\n  }\n\n  _setInitialAttributes(parent, children) {\n    this._setAttributeIfNotExists(parent, 'role', 'tablist');\n\n    for (const child of children) {\n      this._setInitialAttributesOnChild(child);\n    }\n  }\n\n  _setInitialAttributesOnChild(child) {\n    child = this._getInnerElement(child);\n\n    const isActive = this._elemIsActive(child);\n\n    const outerElem = this._getOuterElement(child);\n\n    child.setAttribute('aria-selected', isActive);\n\n    if (outerElem !== child) {\n      this._setAttributeIfNotExists(outerElem, 'role', 'presentation');\n    }\n\n    if (!isActive) {\n      child.setAttribute('tabindex', '-1');\n    }\n\n    this._setAttributeIfNotExists(child, 'role', 'tab'); // set attributes to the related panel too\n\n\n    this._setInitialAttributesOnTargetPanel(child);\n  }\n\n  _setInitialAttributesOnTargetPanel(child) {\n    const target = getElementFromSelector(child);\n\n    if (!target) {\n      return;\n    }\n\n    this._setAttributeIfNotExists(target, 'role', 'tabpanel');\n\n    if (child.id) {\n      this._setAttributeIfNotExists(target, 'aria-labelledby', `#${child.id}`);\n    }\n  }\n\n  _toggleDropDown(element, open) {\n    const outerElem = this._getOuterElement(element);\n\n    if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n      return;\n    }\n\n    const toggle = (selector, className) => {\n      const element = SelectorEngine.findOne(selector, outerElem);\n\n      if (element) {\n        element.classList.toggle(className, open);\n      }\n    };\n\n    toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE);\n    toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW$1);\n    outerElem.setAttribute('aria-expanded', open);\n  }\n\n  _setAttributeIfNotExists(element, attribute, value) {\n    if (!element.hasAttribute(attribute)) {\n      element.setAttribute(attribute, value);\n    }\n  }\n\n  _elemIsActive(elem) {\n    return elem.classList.contains(CLASS_NAME_ACTIVE);\n  } // Try to get the inner element (usually the .nav-link)\n\n\n  _getInnerElement(elem) {\n    return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem);\n  } // Try to get the outer element (usually the .nav-item)\n\n\n  _getOuterElement(elem) {\n    return elem.closest(SELECTOR_OUTER) || elem;\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Tab.getOrCreateInstance(this);\n\n      if (typeof config !== 'string') {\n        return;\n      }\n\n      if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n        throw new TypeError(`No method named \"${config}\"`);\n      }\n\n      data[config]();\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n  if (['A', 'AREA'].includes(this.tagName)) {\n    event.preventDefault();\n  }\n\n  if (isDisabled(this)) {\n    return;\n  }\n\n  Tab.getOrCreateInstance(this).show();\n});\n/**\n * Initialize on focus\n */\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n  for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n    Tab.getOrCreateInstance(element);\n  }\n});\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tab);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.2.3): toast.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n/**\n * Constants\n */\n\nconst NAME = 'toast';\nconst DATA_KEY = 'bs.toast';\nconst EVENT_KEY = `.${DATA_KEY}`;\nconst EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`;\nconst EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`;\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`;\nconst EVENT_FOCUSOUT = `focusout${EVENT_KEY}`;\nconst EVENT_HIDE = `hide${EVENT_KEY}`;\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`;\nconst EVENT_SHOW = `show${EVENT_KEY}`;\nconst EVENT_SHOWN = `shown${EVENT_KEY}`;\nconst CLASS_NAME_FADE = 'fade';\nconst CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility\n\nconst CLASS_NAME_SHOW = 'show';\nconst CLASS_NAME_SHOWING = 'showing';\nconst DefaultType = {\n  animation: 'boolean',\n  autohide: 'boolean',\n  delay: 'number'\n};\nconst Default = {\n  animation: true,\n  autohide: true,\n  delay: 5000\n};\n/**\n * Class definition\n */\n\nclass Toast extends BaseComponent {\n  constructor(element, config) {\n    super(element, config);\n    this._timeout = null;\n    this._hasMouseInteraction = false;\n    this._hasKeyboardInteraction = false;\n\n    this._setListeners();\n  } // Getters\n\n\n  static get Default() {\n    return Default;\n  }\n\n  static get DefaultType() {\n    return DefaultType;\n  }\n\n  static get NAME() {\n    return NAME;\n  } // Public\n\n\n  show() {\n    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);\n\n    if (showEvent.defaultPrevented) {\n      return;\n    }\n\n    this._clearTimeout();\n\n    if (this._config.animation) {\n      this._element.classList.add(CLASS_NAME_FADE);\n    }\n\n    const complete = () => {\n      this._element.classList.remove(CLASS_NAME_SHOWING);\n\n      EventHandler.trigger(this._element, EVENT_SHOWN);\n\n      this._maybeScheduleHide();\n    };\n\n    this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated\n\n\n    reflow(this._element);\n\n    this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING);\n\n    this._queueCallback(complete, this._element, this._config.animation);\n  }\n\n  hide() {\n    if (!this.isShown()) {\n      return;\n    }\n\n    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);\n\n    if (hideEvent.defaultPrevented) {\n      return;\n    }\n\n    const complete = () => {\n      this._element.classList.add(CLASS_NAME_HIDE); // @deprecated\n\n\n      this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW);\n\n      EventHandler.trigger(this._element, EVENT_HIDDEN);\n    };\n\n    this._element.classList.add(CLASS_NAME_SHOWING);\n\n    this._queueCallback(complete, this._element, this._config.animation);\n  }\n\n  dispose() {\n    this._clearTimeout();\n\n    if (this.isShown()) {\n      this._element.classList.remove(CLASS_NAME_SHOW);\n    }\n\n    super.dispose();\n  }\n\n  isShown() {\n    return this._element.classList.contains(CLASS_NAME_SHOW);\n  } // Private\n\n\n  _maybeScheduleHide() {\n    if (!this._config.autohide) {\n      return;\n    }\n\n    if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n      return;\n    }\n\n    this._timeout = setTimeout(() => {\n      this.hide();\n    }, this._config.delay);\n  }\n\n  _onInteraction(event, isInteracting) {\n    switch (event.type) {\n      case 'mouseover':\n      case 'mouseout':\n        {\n          this._hasMouseInteraction = isInteracting;\n          break;\n        }\n\n      case 'focusin':\n      case 'focusout':\n        {\n          this._hasKeyboardInteraction = isInteracting;\n          break;\n        }\n    }\n\n    if (isInteracting) {\n      this._clearTimeout();\n\n      return;\n    }\n\n    const nextElement = event.relatedTarget;\n\n    if (this._element === nextElement || this._element.contains(nextElement)) {\n      return;\n    }\n\n    this._maybeScheduleHide();\n  }\n\n  _setListeners() {\n    EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));\n    EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));\n    EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));\n    EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));\n  }\n\n  _clearTimeout() {\n    clearTimeout(this._timeout);\n    this._timeout = null;\n  } // Static\n\n\n  static jQueryInterface(config) {\n    return this.each(function () {\n      const data = Toast.getOrCreateInstance(this, config);\n\n      if (typeof config === 'string') {\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config](this);\n      }\n    });\n  }\n\n}\n/**\n * Data API implementation\n */\n\n\nenableDismissTrigger(Toast);\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Toast);\n\nexport { Alert, Button, Carousel, Collapse, Dropdown, Modal, Offcanvas, Popover, ScrollSpy, Tab, Toast, Tooltip };\n//# sourceMappingURL=bootstrap.esm.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap/js/bootstrap.js",
    "content": "/*!\n  * Bootstrap v5.2.3 (https://getbootstrap.com/)\n  * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) :\n  typeof define === 'function' && define.amd ? define(['@popperjs/core'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory(global.Popper));\n})(this, (function (Popper) { 'use strict';\n\n  function _interopNamespace(e) {\n    if (e && e.__esModule) return e;\n    const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });\n    if (e) {\n      for (const k in e) {\n        if (k !== 'default') {\n          const d = Object.getOwnPropertyDescriptor(e, k);\n          Object.defineProperty(n, k, d.get ? d : {\n            enumerable: true,\n            get: () => e[k]\n          });\n        }\n      }\n    }\n    n.default = e;\n    return Object.freeze(n);\n  }\n\n  const Popper__namespace = /*#__PURE__*/_interopNamespace(Popper);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/index.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  const MAX_UID = 1000000;\n  const MILLISECONDS_MULTIPLIER = 1000;\n  const TRANSITION_END = 'transitionend'; // Shout-out Angus Croll (https://goo.gl/pxwQGp)\n\n  const toType = object => {\n    if (object === null || object === undefined) {\n      return `${object}`;\n    }\n\n    return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n  };\n  /**\n   * Public Util API\n   */\n\n\n  const getUID = prefix => {\n    do {\n      prefix += Math.floor(Math.random() * MAX_UID);\n    } while (document.getElementById(prefix));\n\n    return prefix;\n  };\n\n  const getSelector = element => {\n    let selector = element.getAttribute('data-bs-target');\n\n    if (!selector || selector === '#') {\n      let hrefAttribute = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,\n      // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n      // `document.querySelector` will rightfully complain it is invalid.\n      // See https://github.com/twbs/bootstrap/issues/32273\n\n      if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n        return null;\n      } // Just in case some CMS puts out a full URL with the anchor appended\n\n\n      if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n        hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n      }\n\n      selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n    }\n\n    return selector;\n  };\n\n  const getSelectorFromElement = element => {\n    const selector = getSelector(element);\n\n    if (selector) {\n      return document.querySelector(selector) ? selector : null;\n    }\n\n    return null;\n  };\n\n  const getElementFromSelector = element => {\n    const selector = getSelector(element);\n    return selector ? document.querySelector(selector) : null;\n  };\n\n  const getTransitionDurationFromElement = element => {\n    if (!element) {\n      return 0;\n    } // Get transition-duration of the element\n\n\n    let {\n      transitionDuration,\n      transitionDelay\n    } = window.getComputedStyle(element);\n    const floatTransitionDuration = Number.parseFloat(transitionDuration);\n    const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found\n\n    if (!floatTransitionDuration && !floatTransitionDelay) {\n      return 0;\n    } // If multiple durations are defined, take the first\n\n\n    transitionDuration = transitionDuration.split(',')[0];\n    transitionDelay = transitionDelay.split(',')[0];\n    return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n  };\n\n  const triggerTransitionEnd = element => {\n    element.dispatchEvent(new Event(TRANSITION_END));\n  };\n\n  const isElement = object => {\n    if (!object || typeof object !== 'object') {\n      return false;\n    }\n\n    if (typeof object.jquery !== 'undefined') {\n      object = object[0];\n    }\n\n    return typeof object.nodeType !== 'undefined';\n  };\n\n  const getElement = object => {\n    // it's a jQuery object or a node element\n    if (isElement(object)) {\n      return object.jquery ? object[0] : object;\n    }\n\n    if (typeof object === 'string' && object.length > 0) {\n      return document.querySelector(object);\n    }\n\n    return null;\n  };\n\n  const isVisible = element => {\n    if (!isElement(element) || element.getClientRects().length === 0) {\n      return false;\n    }\n\n    const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; // Handle `details` element as its content may falsie appear visible when it is closed\n\n    const closedDetails = element.closest('details:not([open])');\n\n    if (!closedDetails) {\n      return elementIsVisible;\n    }\n\n    if (closedDetails !== element) {\n      const summary = element.closest('summary');\n\n      if (summary && summary.parentNode !== closedDetails) {\n        return false;\n      }\n\n      if (summary === null) {\n        return false;\n      }\n    }\n\n    return elementIsVisible;\n  };\n\n  const isDisabled = element => {\n    if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n      return true;\n    }\n\n    if (element.classList.contains('disabled')) {\n      return true;\n    }\n\n    if (typeof element.disabled !== 'undefined') {\n      return element.disabled;\n    }\n\n    return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n  };\n\n  const findShadowRoot = element => {\n    if (!document.documentElement.attachShadow) {\n      return null;\n    } // Can find the shadow root otherwise it'll return the document\n\n\n    if (typeof element.getRootNode === 'function') {\n      const root = element.getRootNode();\n      return root instanceof ShadowRoot ? root : null;\n    }\n\n    if (element instanceof ShadowRoot) {\n      return element;\n    } // when we don't find a shadow root\n\n\n    if (!element.parentNode) {\n      return null;\n    }\n\n    return findShadowRoot(element.parentNode);\n  };\n\n  const noop = () => {};\n  /**\n   * Trick to restart an element's animation\n   *\n   * @param {HTMLElement} element\n   * @return void\n   *\n   * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n   */\n\n\n  const reflow = element => {\n    element.offsetHeight; // eslint-disable-line no-unused-expressions\n  };\n\n  const getjQuery = () => {\n    if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n      return window.jQuery;\n    }\n\n    return null;\n  };\n\n  const DOMContentLoadedCallbacks = [];\n\n  const onDOMContentLoaded = callback => {\n    if (document.readyState === 'loading') {\n      // add listener on the first call when the document is in loading state\n      if (!DOMContentLoadedCallbacks.length) {\n        document.addEventListener('DOMContentLoaded', () => {\n          for (const callback of DOMContentLoadedCallbacks) {\n            callback();\n          }\n        });\n      }\n\n      DOMContentLoadedCallbacks.push(callback);\n    } else {\n      callback();\n    }\n  };\n\n  const isRTL = () => document.documentElement.dir === 'rtl';\n\n  const defineJQueryPlugin = plugin => {\n    onDOMContentLoaded(() => {\n      const $ = getjQuery();\n      /* istanbul ignore if */\n\n      if ($) {\n        const name = plugin.NAME;\n        const JQUERY_NO_CONFLICT = $.fn[name];\n        $.fn[name] = plugin.jQueryInterface;\n        $.fn[name].Constructor = plugin;\n\n        $.fn[name].noConflict = () => {\n          $.fn[name] = JQUERY_NO_CONFLICT;\n          return plugin.jQueryInterface;\n        };\n      }\n    });\n  };\n\n  const execute = callback => {\n    if (typeof callback === 'function') {\n      callback();\n    }\n  };\n\n  const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n    if (!waitForTransition) {\n      execute(callback);\n      return;\n    }\n\n    const durationPadding = 5;\n    const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n    let called = false;\n\n    const handler = ({\n      target\n    }) => {\n      if (target !== transitionElement) {\n        return;\n      }\n\n      called = true;\n      transitionElement.removeEventListener(TRANSITION_END, handler);\n      execute(callback);\n    };\n\n    transitionElement.addEventListener(TRANSITION_END, handler);\n    setTimeout(() => {\n      if (!called) {\n        triggerTransitionEnd(transitionElement);\n      }\n    }, emulatedDuration);\n  };\n  /**\n   * Return the previous/next element of a list.\n   *\n   * @param {array} list    The list of elements\n   * @param activeElement   The active element\n   * @param shouldGetNext   Choose to get next or previous element\n   * @param isCycleAllowed\n   * @return {Element|elem} The proper element\n   */\n\n\n  const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n    const listLength = list.length;\n    let index = list.indexOf(activeElement); // if the element does not exist in the list return an element\n    // depending on the direction and if cycle is allowed\n\n    if (index === -1) {\n      return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n    }\n\n    index += shouldGetNext ? 1 : -1;\n\n    if (isCycleAllowed) {\n      index = (index + listLength) % listLength;\n    }\n\n    return list[Math.max(0, Math.min(index, listLength - 1))];\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/event-handler.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\n  const stripNameRegex = /\\..*/;\n  const stripUidRegex = /::\\d+$/;\n  const eventRegistry = {}; // Events storage\n\n  let uidEvent = 1;\n  const customEvents = {\n    mouseenter: 'mouseover',\n    mouseleave: 'mouseout'\n  };\n  const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n  /**\n   * Private methods\n   */\n\n  function makeEventUid(element, uid) {\n    return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n  }\n\n  function getElementEvents(element) {\n    const uid = makeEventUid(element);\n    element.uidEvent = uid;\n    eventRegistry[uid] = eventRegistry[uid] || {};\n    return eventRegistry[uid];\n  }\n\n  function bootstrapHandler(element, fn) {\n    return function handler(event) {\n      hydrateObj(event, {\n        delegateTarget: element\n      });\n\n      if (handler.oneOff) {\n        EventHandler.off(element, event.type, fn);\n      }\n\n      return fn.apply(element, [event]);\n    };\n  }\n\n  function bootstrapDelegationHandler(element, selector, fn) {\n    return function handler(event) {\n      const domElements = element.querySelectorAll(selector);\n\n      for (let {\n        target\n      } = event; target && target !== this; target = target.parentNode) {\n        for (const domElement of domElements) {\n          if (domElement !== target) {\n            continue;\n          }\n\n          hydrateObj(event, {\n            delegateTarget: target\n          });\n\n          if (handler.oneOff) {\n            EventHandler.off(element, event.type, selector, fn);\n          }\n\n          return fn.apply(target, [event]);\n        }\n      }\n    };\n  }\n\n  function findHandler(events, callable, delegationSelector = null) {\n    return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n  }\n\n  function normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n    const isDelegated = typeof handler === 'string'; // todo: tooltip passes `false` instead of selector, so we need to check\n\n    const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n    let typeEvent = getTypeEvent(originalTypeEvent);\n\n    if (!nativeEvents.has(typeEvent)) {\n      typeEvent = originalTypeEvent;\n    }\n\n    return [isDelegated, callable, typeEvent];\n  }\n\n  function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n    if (typeof originalTypeEvent !== 'string' || !element) {\n      return;\n    }\n\n    let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n    // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n\n    if (originalTypeEvent in customEvents) {\n      const wrapFunction = fn => {\n        return function (event) {\n          if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n            return fn.call(this, event);\n          }\n        };\n      };\n\n      callable = wrapFunction(callable);\n    }\n\n    const events = getElementEvents(element);\n    const handlers = events[typeEvent] || (events[typeEvent] = {});\n    const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n\n    if (previousFunction) {\n      previousFunction.oneOff = previousFunction.oneOff && oneOff;\n      return;\n    }\n\n    const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n    const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n    fn.delegationSelector = isDelegated ? handler : null;\n    fn.callable = callable;\n    fn.oneOff = oneOff;\n    fn.uidEvent = uid;\n    handlers[uid] = fn;\n    element.addEventListener(typeEvent, fn, isDelegated);\n  }\n\n  function removeHandler(element, events, typeEvent, handler, delegationSelector) {\n    const fn = findHandler(events[typeEvent], handler, delegationSelector);\n\n    if (!fn) {\n      return;\n    }\n\n    element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n    delete events[typeEvent][fn.uidEvent];\n  }\n\n  function removeNamespacedHandlers(element, events, typeEvent, namespace) {\n    const storeElementEvent = events[typeEvent] || {};\n\n    for (const handlerKey of Object.keys(storeElementEvent)) {\n      if (handlerKey.includes(namespace)) {\n        const event = storeElementEvent[handlerKey];\n        removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n      }\n    }\n  }\n\n  function getTypeEvent(event) {\n    // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n    event = event.replace(stripNameRegex, '');\n    return customEvents[event] || event;\n  }\n\n  const EventHandler = {\n    on(element, event, handler, delegationFunction) {\n      addHandler(element, event, handler, delegationFunction, false);\n    },\n\n    one(element, event, handler, delegationFunction) {\n      addHandler(element, event, handler, delegationFunction, true);\n    },\n\n    off(element, originalTypeEvent, handler, delegationFunction) {\n      if (typeof originalTypeEvent !== 'string' || !element) {\n        return;\n      }\n\n      const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n      const inNamespace = typeEvent !== originalTypeEvent;\n      const events = getElementEvents(element);\n      const storeElementEvent = events[typeEvent] || {};\n      const isNamespace = originalTypeEvent.startsWith('.');\n\n      if (typeof callable !== 'undefined') {\n        // Simplest case: handler is passed, remove that listener ONLY.\n        if (!Object.keys(storeElementEvent).length) {\n          return;\n        }\n\n        removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n        return;\n      }\n\n      if (isNamespace) {\n        for (const elementEvent of Object.keys(events)) {\n          removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n        }\n      }\n\n      for (const keyHandlers of Object.keys(storeElementEvent)) {\n        const handlerKey = keyHandlers.replace(stripUidRegex, '');\n\n        if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n          const event = storeElementEvent[keyHandlers];\n          removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n        }\n      }\n    },\n\n    trigger(element, event, args) {\n      if (typeof event !== 'string' || !element) {\n        return null;\n      }\n\n      const $ = getjQuery();\n      const typeEvent = getTypeEvent(event);\n      const inNamespace = event !== typeEvent;\n      let jQueryEvent = null;\n      let bubbles = true;\n      let nativeDispatch = true;\n      let defaultPrevented = false;\n\n      if (inNamespace && $) {\n        jQueryEvent = $.Event(event, args);\n        $(element).trigger(jQueryEvent);\n        bubbles = !jQueryEvent.isPropagationStopped();\n        nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n        defaultPrevented = jQueryEvent.isDefaultPrevented();\n      }\n\n      let evt = new Event(event, {\n        bubbles,\n        cancelable: true\n      });\n      evt = hydrateObj(evt, args);\n\n      if (defaultPrevented) {\n        evt.preventDefault();\n      }\n\n      if (nativeDispatch) {\n        element.dispatchEvent(evt);\n      }\n\n      if (evt.defaultPrevented && jQueryEvent) {\n        jQueryEvent.preventDefault();\n      }\n\n      return evt;\n    }\n\n  };\n\n  function hydrateObj(obj, meta) {\n    for (const [key, value] of Object.entries(meta || {})) {\n      try {\n        obj[key] = value;\n      } catch (_unused) {\n        Object.defineProperty(obj, key, {\n          configurable: true,\n\n          get() {\n            return value;\n          }\n\n        });\n      }\n    }\n\n    return obj;\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/data.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  /**\n   * Constants\n   */\n  const elementMap = new Map();\n  const Data = {\n    set(element, key, instance) {\n      if (!elementMap.has(element)) {\n        elementMap.set(element, new Map());\n      }\n\n      const instanceMap = elementMap.get(element); // make it clear we only want one instance per element\n      // can be removed later when multiple key/instances are fine to be used\n\n      if (!instanceMap.has(key) && instanceMap.size !== 0) {\n        // eslint-disable-next-line no-console\n        console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n        return;\n      }\n\n      instanceMap.set(key, instance);\n    },\n\n    get(element, key) {\n      if (elementMap.has(element)) {\n        return elementMap.get(element).get(key) || null;\n      }\n\n      return null;\n    },\n\n    remove(element, key) {\n      if (!elementMap.has(element)) {\n        return;\n      }\n\n      const instanceMap = elementMap.get(element);\n      instanceMap.delete(key); // free up element references if there are no instances left for an element\n\n      if (instanceMap.size === 0) {\n        elementMap.delete(element);\n      }\n    }\n\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/manipulator.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  function normalizeData(value) {\n    if (value === 'true') {\n      return true;\n    }\n\n    if (value === 'false') {\n      return false;\n    }\n\n    if (value === Number(value).toString()) {\n      return Number(value);\n    }\n\n    if (value === '' || value === 'null') {\n      return null;\n    }\n\n    if (typeof value !== 'string') {\n      return value;\n    }\n\n    try {\n      return JSON.parse(decodeURIComponent(value));\n    } catch (_unused) {\n      return value;\n    }\n  }\n\n  function normalizeDataKey(key) {\n    return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n  }\n\n  const Manipulator = {\n    setDataAttribute(element, key, value) {\n      element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n    },\n\n    removeDataAttribute(element, key) {\n      element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n    },\n\n    getDataAttributes(element) {\n      if (!element) {\n        return {};\n      }\n\n      const attributes = {};\n      const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n\n      for (const key of bsKeys) {\n        let pureKey = key.replace(/^bs/, '');\n        pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n        attributes[pureKey] = normalizeData(element.dataset[key]);\n      }\n\n      return attributes;\n    },\n\n    getDataAttribute(element, key) {\n      return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n    }\n\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/config.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Class definition\n   */\n\n  class Config {\n    // Getters\n    static get Default() {\n      return {};\n    }\n\n    static get DefaultType() {\n      return {};\n    }\n\n    static get NAME() {\n      throw new Error('You have to implement the static method \"NAME\", for each component!');\n    }\n\n    _getConfig(config) {\n      config = this._mergeConfigObj(config);\n      config = this._configAfterMerge(config);\n\n      this._typeCheckConfig(config);\n\n      return config;\n    }\n\n    _configAfterMerge(config) {\n      return config;\n    }\n\n    _mergeConfigObj(config, element) {\n      const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n      return { ...this.constructor.Default,\n        ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n        ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n        ...(typeof config === 'object' ? config : {})\n      };\n    }\n\n    _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n      for (const property of Object.keys(configTypes)) {\n        const expectedTypes = configTypes[property];\n        const value = config[property];\n        const valueType = isElement(value) ? 'element' : toType(value);\n\n        if (!new RegExp(expectedTypes).test(valueType)) {\n          throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n        }\n      }\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): base-component.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const VERSION = '5.2.3';\n  /**\n   * Class definition\n   */\n\n  class BaseComponent extends Config {\n    constructor(element, config) {\n      super();\n      element = getElement(element);\n\n      if (!element) {\n        return;\n      }\n\n      this._element = element;\n      this._config = this._getConfig(config);\n      Data.set(this._element, this.constructor.DATA_KEY, this);\n    } // Public\n\n\n    dispose() {\n      Data.remove(this._element, this.constructor.DATA_KEY);\n      EventHandler.off(this._element, this.constructor.EVENT_KEY);\n\n      for (const propertyName of Object.getOwnPropertyNames(this)) {\n        this[propertyName] = null;\n      }\n    }\n\n    _queueCallback(callback, element, isAnimated = true) {\n      executeAfterTransition(callback, element, isAnimated);\n    }\n\n    _getConfig(config) {\n      config = this._mergeConfigObj(config, this._element);\n      config = this._configAfterMerge(config);\n\n      this._typeCheckConfig(config);\n\n      return config;\n    } // Static\n\n\n    static getInstance(element) {\n      return Data.get(getElement(element), this.DATA_KEY);\n    }\n\n    static getOrCreateInstance(element, config = {}) {\n      return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n    }\n\n    static get VERSION() {\n      return VERSION;\n    }\n\n    static get DATA_KEY() {\n      return `bs.${this.NAME}`;\n    }\n\n    static get EVENT_KEY() {\n      return `.${this.DATA_KEY}`;\n    }\n\n    static eventName(name) {\n      return `${name}${this.EVENT_KEY}`;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/component-functions.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  const enableDismissTrigger = (component, method = 'hide') => {\n    const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n    const name = component.NAME;\n    EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n      if (['A', 'AREA'].includes(this.tagName)) {\n        event.preventDefault();\n      }\n\n      if (isDisabled(this)) {\n        return;\n      }\n\n      const target = getElementFromSelector(this) || this.closest(`.${name}`);\n      const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n\n      instance[method]();\n    });\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): alert.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$f = 'alert';\n  const DATA_KEY$a = 'bs.alert';\n  const EVENT_KEY$b = `.${DATA_KEY$a}`;\n  const EVENT_CLOSE = `close${EVENT_KEY$b}`;\n  const EVENT_CLOSED = `closed${EVENT_KEY$b}`;\n  const CLASS_NAME_FADE$5 = 'fade';\n  const CLASS_NAME_SHOW$8 = 'show';\n  /**\n   * Class definition\n   */\n\n  class Alert extends BaseComponent {\n    // Getters\n    static get NAME() {\n      return NAME$f;\n    } // Public\n\n\n    close() {\n      const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n\n      if (closeEvent.defaultPrevented) {\n        return;\n      }\n\n      this._element.classList.remove(CLASS_NAME_SHOW$8);\n\n      const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n\n      this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n    } // Private\n\n\n    _destroyElement() {\n      this._element.remove();\n\n      EventHandler.trigger(this._element, EVENT_CLOSED);\n      this.dispose();\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Alert.getOrCreateInstance(this);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config](this);\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  enableDismissTrigger(Alert, 'close');\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Alert);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): button.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$e = 'button';\n  const DATA_KEY$9 = 'bs.button';\n  const EVENT_KEY$a = `.${DATA_KEY$9}`;\n  const DATA_API_KEY$6 = '.data-api';\n  const CLASS_NAME_ACTIVE$3 = 'active';\n  const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\n  const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n  /**\n   * Class definition\n   */\n\n  class Button extends BaseComponent {\n    // Getters\n    static get NAME() {\n      return NAME$e;\n    } // Public\n\n\n    toggle() {\n      // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n      this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Button.getOrCreateInstance(this);\n\n        if (config === 'toggle') {\n          data[config]();\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n    event.preventDefault();\n    const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n    const data = Button.getOrCreateInstance(button);\n    data.toggle();\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Button);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dom/selector-engine.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const SelectorEngine = {\n    find(selector, element = document.documentElement) {\n      return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n    },\n\n    findOne(selector, element = document.documentElement) {\n      return Element.prototype.querySelector.call(element, selector);\n    },\n\n    children(element, selector) {\n      return [].concat(...element.children).filter(child => child.matches(selector));\n    },\n\n    parents(element, selector) {\n      const parents = [];\n      let ancestor = element.parentNode.closest(selector);\n\n      while (ancestor) {\n        parents.push(ancestor);\n        ancestor = ancestor.parentNode.closest(selector);\n      }\n\n      return parents;\n    },\n\n    prev(element, selector) {\n      let previous = element.previousElementSibling;\n\n      while (previous) {\n        if (previous.matches(selector)) {\n          return [previous];\n        }\n\n        previous = previous.previousElementSibling;\n      }\n\n      return [];\n    },\n\n    // TODO: this is now unused; remove later along with prev()\n    next(element, selector) {\n      let next = element.nextElementSibling;\n\n      while (next) {\n        if (next.matches(selector)) {\n          return [next];\n        }\n\n        next = next.nextElementSibling;\n      }\n\n      return [];\n    },\n\n    focusableChildren(element) {\n      const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n      return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n    }\n\n  };\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/swipe.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$d = 'swipe';\n  const EVENT_KEY$9 = '.bs.swipe';\n  const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\n  const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\n  const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\n  const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\n  const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\n  const POINTER_TYPE_TOUCH = 'touch';\n  const POINTER_TYPE_PEN = 'pen';\n  const CLASS_NAME_POINTER_EVENT = 'pointer-event';\n  const SWIPE_THRESHOLD = 40;\n  const Default$c = {\n    endCallback: null,\n    leftCallback: null,\n    rightCallback: null\n  };\n  const DefaultType$c = {\n    endCallback: '(function|null)',\n    leftCallback: '(function|null)',\n    rightCallback: '(function|null)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Swipe extends Config {\n    constructor(element, config) {\n      super();\n      this._element = element;\n\n      if (!element || !Swipe.isSupported()) {\n        return;\n      }\n\n      this._config = this._getConfig(config);\n      this._deltaX = 0;\n      this._supportPointerEvents = Boolean(window.PointerEvent);\n\n      this._initEvents();\n    } // Getters\n\n\n    static get Default() {\n      return Default$c;\n    }\n\n    static get DefaultType() {\n      return DefaultType$c;\n    }\n\n    static get NAME() {\n      return NAME$d;\n    } // Public\n\n\n    dispose() {\n      EventHandler.off(this._element, EVENT_KEY$9);\n    } // Private\n\n\n    _start(event) {\n      if (!this._supportPointerEvents) {\n        this._deltaX = event.touches[0].clientX;\n        return;\n      }\n\n      if (this._eventIsPointerPenTouch(event)) {\n        this._deltaX = event.clientX;\n      }\n    }\n\n    _end(event) {\n      if (this._eventIsPointerPenTouch(event)) {\n        this._deltaX = event.clientX - this._deltaX;\n      }\n\n      this._handleSwipe();\n\n      execute(this._config.endCallback);\n    }\n\n    _move(event) {\n      this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n    }\n\n    _handleSwipe() {\n      const absDeltaX = Math.abs(this._deltaX);\n\n      if (absDeltaX <= SWIPE_THRESHOLD) {\n        return;\n      }\n\n      const direction = absDeltaX / this._deltaX;\n      this._deltaX = 0;\n\n      if (!direction) {\n        return;\n      }\n\n      execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n    }\n\n    _initEvents() {\n      if (this._supportPointerEvents) {\n        EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n        EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n\n        this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n      } else {\n        EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n        EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n        EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n      }\n    }\n\n    _eventIsPointerPenTouch(event) {\n      return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n    } // Static\n\n\n    static isSupported() {\n      return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): carousel.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$c = 'carousel';\n  const DATA_KEY$8 = 'bs.carousel';\n  const EVENT_KEY$8 = `.${DATA_KEY$8}`;\n  const DATA_API_KEY$5 = '.data-api';\n  const ARROW_LEFT_KEY$1 = 'ArrowLeft';\n  const ARROW_RIGHT_KEY$1 = 'ArrowRight';\n  const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\n  const ORDER_NEXT = 'next';\n  const ORDER_PREV = 'prev';\n  const DIRECTION_LEFT = 'left';\n  const DIRECTION_RIGHT = 'right';\n  const EVENT_SLIDE = `slide${EVENT_KEY$8}`;\n  const EVENT_SLID = `slid${EVENT_KEY$8}`;\n  const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\n  const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\n  const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\n  const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\n  const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\n  const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\n  const CLASS_NAME_CAROUSEL = 'carousel';\n  const CLASS_NAME_ACTIVE$2 = 'active';\n  const CLASS_NAME_SLIDE = 'slide';\n  const CLASS_NAME_END = 'carousel-item-end';\n  const CLASS_NAME_START = 'carousel-item-start';\n  const CLASS_NAME_NEXT = 'carousel-item-next';\n  const CLASS_NAME_PREV = 'carousel-item-prev';\n  const SELECTOR_ACTIVE = '.active';\n  const SELECTOR_ITEM = '.carousel-item';\n  const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\n  const SELECTOR_ITEM_IMG = '.carousel-item img';\n  const SELECTOR_INDICATORS = '.carousel-indicators';\n  const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\n  const SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\n  const KEY_TO_DIRECTION = {\n    [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n    [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n  };\n  const Default$b = {\n    interval: 5000,\n    keyboard: true,\n    pause: 'hover',\n    ride: false,\n    touch: true,\n    wrap: true\n  };\n  const DefaultType$b = {\n    interval: '(number|boolean)',\n    // TODO:v6 remove boolean support\n    keyboard: 'boolean',\n    pause: '(string|boolean)',\n    ride: '(boolean|string)',\n    touch: 'boolean',\n    wrap: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Carousel extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._interval = null;\n      this._activeElement = null;\n      this._isSliding = false;\n      this.touchTimeout = null;\n      this._swipeHelper = null;\n      this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n\n      this._addEventListeners();\n\n      if (this._config.ride === CLASS_NAME_CAROUSEL) {\n        this.cycle();\n      }\n    } // Getters\n\n\n    static get Default() {\n      return Default$b;\n    }\n\n    static get DefaultType() {\n      return DefaultType$b;\n    }\n\n    static get NAME() {\n      return NAME$c;\n    } // Public\n\n\n    next() {\n      this._slide(ORDER_NEXT);\n    }\n\n    nextWhenVisible() {\n      // FIXME TODO use `document.visibilityState`\n      // Don't call next when the page isn't visible\n      // or the carousel or its parent isn't visible\n      if (!document.hidden && isVisible(this._element)) {\n        this.next();\n      }\n    }\n\n    prev() {\n      this._slide(ORDER_PREV);\n    }\n\n    pause() {\n      if (this._isSliding) {\n        triggerTransitionEnd(this._element);\n      }\n\n      this._clearInterval();\n    }\n\n    cycle() {\n      this._clearInterval();\n\n      this._updateInterval();\n\n      this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n    }\n\n    _maybeEnableCycle() {\n      if (!this._config.ride) {\n        return;\n      }\n\n      if (this._isSliding) {\n        EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n        return;\n      }\n\n      this.cycle();\n    }\n\n    to(index) {\n      const items = this._getItems();\n\n      if (index > items.length - 1 || index < 0) {\n        return;\n      }\n\n      if (this._isSliding) {\n        EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n        return;\n      }\n\n      const activeIndex = this._getItemIndex(this._getActive());\n\n      if (activeIndex === index) {\n        return;\n      }\n\n      const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n\n      this._slide(order, items[index]);\n    }\n\n    dispose() {\n      if (this._swipeHelper) {\n        this._swipeHelper.dispose();\n      }\n\n      super.dispose();\n    } // Private\n\n\n    _configAfterMerge(config) {\n      config.defaultInterval = config.interval;\n      return config;\n    }\n\n    _addEventListeners() {\n      if (this._config.keyboard) {\n        EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n      }\n\n      if (this._config.pause === 'hover') {\n        EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n        EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n      }\n\n      if (this._config.touch && Swipe.isSupported()) {\n        this._addTouchEventListeners();\n      }\n    }\n\n    _addTouchEventListeners() {\n      for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n        EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n      }\n\n      const endCallBack = () => {\n        if (this._config.pause !== 'hover') {\n          return;\n        } // If it's a touch-enabled device, mouseenter/leave are fired as\n        // part of the mouse compatibility events on first tap - the carousel\n        // would stop cycling until user tapped out of it;\n        // here, we listen for touchend, explicitly pause the carousel\n        // (as if it's the second time we tap on it, mouseenter compat event\n        // is NOT fired) and after a timeout (to allow for mouse compatibility\n        // events to fire) we explicitly restart cycling\n\n\n        this.pause();\n\n        if (this.touchTimeout) {\n          clearTimeout(this.touchTimeout);\n        }\n\n        this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n      };\n\n      const swipeConfig = {\n        leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n        rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n        endCallback: endCallBack\n      };\n      this._swipeHelper = new Swipe(this._element, swipeConfig);\n    }\n\n    _keydown(event) {\n      if (/input|textarea/i.test(event.target.tagName)) {\n        return;\n      }\n\n      const direction = KEY_TO_DIRECTION[event.key];\n\n      if (direction) {\n        event.preventDefault();\n\n        this._slide(this._directionToOrder(direction));\n      }\n    }\n\n    _getItemIndex(element) {\n      return this._getItems().indexOf(element);\n    }\n\n    _setActiveIndicatorElement(index) {\n      if (!this._indicatorsElement) {\n        return;\n      }\n\n      const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n      activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n      activeIndicator.removeAttribute('aria-current');\n      const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n\n      if (newActiveIndicator) {\n        newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n        newActiveIndicator.setAttribute('aria-current', 'true');\n      }\n    }\n\n    _updateInterval() {\n      const element = this._activeElement || this._getActive();\n\n      if (!element) {\n        return;\n      }\n\n      const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n      this._config.interval = elementInterval || this._config.defaultInterval;\n    }\n\n    _slide(order, element = null) {\n      if (this._isSliding) {\n        return;\n      }\n\n      const activeElement = this._getActive();\n\n      const isNext = order === ORDER_NEXT;\n      const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n\n      if (nextElement === activeElement) {\n        return;\n      }\n\n      const nextElementIndex = this._getItemIndex(nextElement);\n\n      const triggerEvent = eventName => {\n        return EventHandler.trigger(this._element, eventName, {\n          relatedTarget: nextElement,\n          direction: this._orderToDirection(order),\n          from: this._getItemIndex(activeElement),\n          to: nextElementIndex\n        });\n      };\n\n      const slideEvent = triggerEvent(EVENT_SLIDE);\n\n      if (slideEvent.defaultPrevented) {\n        return;\n      }\n\n      if (!activeElement || !nextElement) {\n        // Some weirdness is happening, so we bail\n        // todo: change tests that use empty divs to avoid this check\n        return;\n      }\n\n      const isCycling = Boolean(this._interval);\n      this.pause();\n      this._isSliding = true;\n\n      this._setActiveIndicatorElement(nextElementIndex);\n\n      this._activeElement = nextElement;\n      const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n      const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n      nextElement.classList.add(orderClassName);\n      reflow(nextElement);\n      activeElement.classList.add(directionalClassName);\n      nextElement.classList.add(directionalClassName);\n\n      const completeCallBack = () => {\n        nextElement.classList.remove(directionalClassName, orderClassName);\n        nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n        activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n        this._isSliding = false;\n        triggerEvent(EVENT_SLID);\n      };\n\n      this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n\n      if (isCycling) {\n        this.cycle();\n      }\n    }\n\n    _isAnimated() {\n      return this._element.classList.contains(CLASS_NAME_SLIDE);\n    }\n\n    _getActive() {\n      return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n    }\n\n    _getItems() {\n      return SelectorEngine.find(SELECTOR_ITEM, this._element);\n    }\n\n    _clearInterval() {\n      if (this._interval) {\n        clearInterval(this._interval);\n        this._interval = null;\n      }\n    }\n\n    _directionToOrder(direction) {\n      if (isRTL()) {\n        return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n      }\n\n      return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n    }\n\n    _orderToDirection(order) {\n      if (isRTL()) {\n        return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n      }\n\n      return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Carousel.getOrCreateInstance(this, config);\n\n        if (typeof config === 'number') {\n          data.to(config);\n          return;\n        }\n\n        if (typeof config === 'string') {\n          if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n\n          data[config]();\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n    const target = getElementFromSelector(this);\n\n    if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n      return;\n    }\n\n    event.preventDefault();\n    const carousel = Carousel.getOrCreateInstance(target);\n    const slideIndex = this.getAttribute('data-bs-slide-to');\n\n    if (slideIndex) {\n      carousel.to(slideIndex);\n\n      carousel._maybeEnableCycle();\n\n      return;\n    }\n\n    if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n      carousel.next();\n\n      carousel._maybeEnableCycle();\n\n      return;\n    }\n\n    carousel.prev();\n\n    carousel._maybeEnableCycle();\n  });\n  EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n    const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n\n    for (const carousel of carousels) {\n      Carousel.getOrCreateInstance(carousel);\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Carousel);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): collapse.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$b = 'collapse';\n  const DATA_KEY$7 = 'bs.collapse';\n  const EVENT_KEY$7 = `.${DATA_KEY$7}`;\n  const DATA_API_KEY$4 = '.data-api';\n  const EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\n  const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\n  const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\n  const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\n  const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\n  const CLASS_NAME_SHOW$7 = 'show';\n  const CLASS_NAME_COLLAPSE = 'collapse';\n  const CLASS_NAME_COLLAPSING = 'collapsing';\n  const CLASS_NAME_COLLAPSED = 'collapsed';\n  const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\n  const CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\n  const WIDTH = 'width';\n  const HEIGHT = 'height';\n  const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\n  const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\n  const Default$a = {\n    parent: null,\n    toggle: true\n  };\n  const DefaultType$a = {\n    parent: '(null|element)',\n    toggle: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Collapse extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._isTransitioning = false;\n      this._triggerArray = [];\n      const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n\n      for (const elem of toggleList) {\n        const selector = getSelectorFromElement(elem);\n        const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n\n        if (selector !== null && filterElement.length) {\n          this._triggerArray.push(elem);\n        }\n      }\n\n      this._initializeChildren();\n\n      if (!this._config.parent) {\n        this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n      }\n\n      if (this._config.toggle) {\n        this.toggle();\n      }\n    } // Getters\n\n\n    static get Default() {\n      return Default$a;\n    }\n\n    static get DefaultType() {\n      return DefaultType$a;\n    }\n\n    static get NAME() {\n      return NAME$b;\n    } // Public\n\n\n    toggle() {\n      if (this._isShown()) {\n        this.hide();\n      } else {\n        this.show();\n      }\n    }\n\n    show() {\n      if (this._isTransitioning || this._isShown()) {\n        return;\n      }\n\n      let activeChildren = []; // find active children\n\n      if (this._config.parent) {\n        activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n          toggle: false\n        }));\n      }\n\n      if (activeChildren.length && activeChildren[0]._isTransitioning) {\n        return;\n      }\n\n      const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n\n      if (startEvent.defaultPrevented) {\n        return;\n      }\n\n      for (const activeInstance of activeChildren) {\n        activeInstance.hide();\n      }\n\n      const dimension = this._getDimension();\n\n      this._element.classList.remove(CLASS_NAME_COLLAPSE);\n\n      this._element.classList.add(CLASS_NAME_COLLAPSING);\n\n      this._element.style[dimension] = 0;\n\n      this._addAriaAndCollapsedClass(this._triggerArray, true);\n\n      this._isTransitioning = true;\n\n      const complete = () => {\n        this._isTransitioning = false;\n\n        this._element.classList.remove(CLASS_NAME_COLLAPSING);\n\n        this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n\n        this._element.style[dimension] = '';\n        EventHandler.trigger(this._element, EVENT_SHOWN$6);\n      };\n\n      const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n      const scrollSize = `scroll${capitalizedDimension}`;\n\n      this._queueCallback(complete, this._element, true);\n\n      this._element.style[dimension] = `${this._element[scrollSize]}px`;\n    }\n\n    hide() {\n      if (this._isTransitioning || !this._isShown()) {\n        return;\n      }\n\n      const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n\n      if (startEvent.defaultPrevented) {\n        return;\n      }\n\n      const dimension = this._getDimension();\n\n      this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n      reflow(this._element);\n\n      this._element.classList.add(CLASS_NAME_COLLAPSING);\n\n      this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n\n      for (const trigger of this._triggerArray) {\n        const element = getElementFromSelector(trigger);\n\n        if (element && !this._isShown(element)) {\n          this._addAriaAndCollapsedClass([trigger], false);\n        }\n      }\n\n      this._isTransitioning = true;\n\n      const complete = () => {\n        this._isTransitioning = false;\n\n        this._element.classList.remove(CLASS_NAME_COLLAPSING);\n\n        this._element.classList.add(CLASS_NAME_COLLAPSE);\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n      };\n\n      this._element.style[dimension] = '';\n\n      this._queueCallback(complete, this._element, true);\n    }\n\n    _isShown(element = this._element) {\n      return element.classList.contains(CLASS_NAME_SHOW$7);\n    } // Private\n\n\n    _configAfterMerge(config) {\n      config.toggle = Boolean(config.toggle); // Coerce string values\n\n      config.parent = getElement(config.parent);\n      return config;\n    }\n\n    _getDimension() {\n      return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n    }\n\n    _initializeChildren() {\n      if (!this._config.parent) {\n        return;\n      }\n\n      const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n\n      for (const element of children) {\n        const selected = getElementFromSelector(element);\n\n        if (selected) {\n          this._addAriaAndCollapsedClass([element], this._isShown(selected));\n        }\n      }\n    }\n\n    _getFirstLevelChildren(selector) {\n      const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); // remove children if greater depth\n\n      return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n    }\n\n    _addAriaAndCollapsedClass(triggerArray, isOpen) {\n      if (!triggerArray.length) {\n        return;\n      }\n\n      for (const element of triggerArray) {\n        element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n        element.setAttribute('aria-expanded', isOpen);\n      }\n    } // Static\n\n\n    static jQueryInterface(config) {\n      const _config = {};\n\n      if (typeof config === 'string' && /show|hide/.test(config)) {\n        _config.toggle = false;\n      }\n\n      return this.each(function () {\n        const data = Collapse.getOrCreateInstance(this, _config);\n\n        if (typeof config === 'string') {\n          if (typeof data[config] === 'undefined') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n\n          data[config]();\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n    // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n    if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n      event.preventDefault();\n    }\n\n    const selector = getSelectorFromElement(this);\n    const selectorElements = SelectorEngine.find(selector);\n\n    for (const element of selectorElements) {\n      Collapse.getOrCreateInstance(element, {\n        toggle: false\n      }).toggle();\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Collapse);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): dropdown.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$a = 'dropdown';\n  const DATA_KEY$6 = 'bs.dropdown';\n  const EVENT_KEY$6 = `.${DATA_KEY$6}`;\n  const DATA_API_KEY$3 = '.data-api';\n  const ESCAPE_KEY$2 = 'Escape';\n  const TAB_KEY$1 = 'Tab';\n  const ARROW_UP_KEY$1 = 'ArrowUp';\n  const ARROW_DOWN_KEY$1 = 'ArrowDown';\n  const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\n  const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\n  const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\n  const EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\n  const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\n  const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\n  const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\n  const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\n  const CLASS_NAME_SHOW$6 = 'show';\n  const CLASS_NAME_DROPUP = 'dropup';\n  const CLASS_NAME_DROPEND = 'dropend';\n  const CLASS_NAME_DROPSTART = 'dropstart';\n  const CLASS_NAME_DROPUP_CENTER = 'dropup-center';\n  const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\n  const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\n  const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\n  const SELECTOR_MENU = '.dropdown-menu';\n  const SELECTOR_NAVBAR = '.navbar';\n  const SELECTOR_NAVBAR_NAV = '.navbar-nav';\n  const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\n  const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\n  const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\n  const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\n  const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\n  const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\n  const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\n  const PLACEMENT_TOPCENTER = 'top';\n  const PLACEMENT_BOTTOMCENTER = 'bottom';\n  const Default$9 = {\n    autoClose: true,\n    boundary: 'clippingParents',\n    display: 'dynamic',\n    offset: [0, 2],\n    popperConfig: null,\n    reference: 'toggle'\n  };\n  const DefaultType$9 = {\n    autoClose: '(boolean|string)',\n    boundary: '(string|element)',\n    display: 'string',\n    offset: '(array|string|function)',\n    popperConfig: '(null|object|function)',\n    reference: '(string|element|object)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Dropdown extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._popper = null;\n      this._parent = this._element.parentNode; // dropdown wrapper\n      // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/\n\n      this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n      this._inNavbar = this._detectNavbar();\n    } // Getters\n\n\n    static get Default() {\n      return Default$9;\n    }\n\n    static get DefaultType() {\n      return DefaultType$9;\n    }\n\n    static get NAME() {\n      return NAME$a;\n    } // Public\n\n\n    toggle() {\n      return this._isShown() ? this.hide() : this.show();\n    }\n\n    show() {\n      if (isDisabled(this._element) || this._isShown()) {\n        return;\n      }\n\n      const relatedTarget = {\n        relatedTarget: this._element\n      };\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._createPopper(); // If this is a touch-enabled device we add extra\n      // empty mouseover listeners to the body's immediate children;\n      // only needed because of broken event delegation on iOS\n      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n\n\n      if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.on(element, 'mouseover', noop);\n        }\n      }\n\n      this._element.focus();\n\n      this._element.setAttribute('aria-expanded', true);\n\n      this._menu.classList.add(CLASS_NAME_SHOW$6);\n\n      this._element.classList.add(CLASS_NAME_SHOW$6);\n\n      EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n    }\n\n    hide() {\n      if (isDisabled(this._element) || !this._isShown()) {\n        return;\n      }\n\n      const relatedTarget = {\n        relatedTarget: this._element\n      };\n\n      this._completeHide(relatedTarget);\n    }\n\n    dispose() {\n      if (this._popper) {\n        this._popper.destroy();\n      }\n\n      super.dispose();\n    }\n\n    update() {\n      this._inNavbar = this._detectNavbar();\n\n      if (this._popper) {\n        this._popper.update();\n      }\n    } // Private\n\n\n    _completeHide(relatedTarget) {\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      } // If this is a touch-enabled device we remove the extra\n      // empty mouseover listeners we added for iOS support\n\n\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.off(element, 'mouseover', noop);\n        }\n      }\n\n      if (this._popper) {\n        this._popper.destroy();\n      }\n\n      this._menu.classList.remove(CLASS_NAME_SHOW$6);\n\n      this._element.classList.remove(CLASS_NAME_SHOW$6);\n\n      this._element.setAttribute('aria-expanded', 'false');\n\n      Manipulator.removeDataAttribute(this._menu, 'popper');\n      EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n    }\n\n    _getConfig(config) {\n      config = super._getConfig(config);\n\n      if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n        // Popper virtual elements require a getBoundingClientRect method\n        throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n      }\n\n      return config;\n    }\n\n    _createPopper() {\n      if (typeof Popper__namespace === 'undefined') {\n        throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n      }\n\n      let referenceElement = this._element;\n\n      if (this._config.reference === 'parent') {\n        referenceElement = this._parent;\n      } else if (isElement(this._config.reference)) {\n        referenceElement = getElement(this._config.reference);\n      } else if (typeof this._config.reference === 'object') {\n        referenceElement = this._config.reference;\n      }\n\n      const popperConfig = this._getPopperConfig();\n\n      this._popper = Popper__namespace.createPopper(referenceElement, this._menu, popperConfig);\n    }\n\n    _isShown() {\n      return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n    }\n\n    _getPlacement() {\n      const parentDropdown = this._parent;\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n        return PLACEMENT_RIGHT;\n      }\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n        return PLACEMENT_LEFT;\n      }\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n        return PLACEMENT_TOPCENTER;\n      }\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n        return PLACEMENT_BOTTOMCENTER;\n      } // We need to trim the value because custom properties can also include spaces\n\n\n      const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n        return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n      }\n\n      return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n    }\n\n    _detectNavbar() {\n      return this._element.closest(SELECTOR_NAVBAR) !== null;\n    }\n\n    _getOffset() {\n      const {\n        offset\n      } = this._config;\n\n      if (typeof offset === 'string') {\n        return offset.split(',').map(value => Number.parseInt(value, 10));\n      }\n\n      if (typeof offset === 'function') {\n        return popperData => offset(popperData, this._element);\n      }\n\n      return offset;\n    }\n\n    _getPopperConfig() {\n      const defaultBsPopperConfig = {\n        placement: this._getPlacement(),\n        modifiers: [{\n          name: 'preventOverflow',\n          options: {\n            boundary: this._config.boundary\n          }\n        }, {\n          name: 'offset',\n          options: {\n            offset: this._getOffset()\n          }\n        }]\n      }; // Disable Popper if we have a static display or Dropdown is in Navbar\n\n      if (this._inNavbar || this._config.display === 'static') {\n        Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // todo:v6 remove\n\n        defaultBsPopperConfig.modifiers = [{\n          name: 'applyStyles',\n          enabled: false\n        }];\n      }\n\n      return { ...defaultBsPopperConfig,\n        ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n      };\n    }\n\n    _selectMenuItem({\n      key,\n      target\n    }) {\n      const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n\n      if (!items.length) {\n        return;\n      } // if target isn't included in items (e.g. when expanding the dropdown)\n      // allow cycling to get the last item in case key equals ARROW_UP_KEY\n\n\n      getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Dropdown.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n    static clearMenus(event) {\n      if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n        return;\n      }\n\n      const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n\n      for (const toggle of openToggles) {\n        const context = Dropdown.getInstance(toggle);\n\n        if (!context || context._config.autoClose === false) {\n          continue;\n        }\n\n        const composedPath = event.composedPath();\n        const isMenuTarget = composedPath.includes(context._menu);\n\n        if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n          continue;\n        } // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n\n\n        if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n          continue;\n        }\n\n        const relatedTarget = {\n          relatedTarget: context._element\n        };\n\n        if (event.type === 'click') {\n          relatedTarget.clickEvent = event;\n        }\n\n        context._completeHide(relatedTarget);\n      }\n    }\n\n    static dataApiKeydownHandler(event) {\n      // If not an UP | DOWN | ESCAPE key => not a dropdown command\n      // If input/textarea && if key is other than ESCAPE => not a dropdown command\n      const isInput = /input|textarea/i.test(event.target.tagName);\n      const isEscapeEvent = event.key === ESCAPE_KEY$2;\n      const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n\n      if (!isUpOrDownEvent && !isEscapeEvent) {\n        return;\n      }\n\n      if (isInput && !isEscapeEvent) {\n        return;\n      }\n\n      event.preventDefault(); // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/\n\n      const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n      const instance = Dropdown.getOrCreateInstance(getToggleButton);\n\n      if (isUpOrDownEvent) {\n        event.stopPropagation();\n        instance.show();\n\n        instance._selectMenuItem(event);\n\n        return;\n      }\n\n      if (instance._isShown()) {\n        // else is escape and we check if it is shown\n        event.stopPropagation();\n        instance.hide();\n        getToggleButton.focus();\n      }\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\n  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\n  EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\n  EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\n  EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n    event.preventDefault();\n    Dropdown.getOrCreateInstance(this).toggle();\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Dropdown);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/scrollBar.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\n  const SELECTOR_STICKY_CONTENT = '.sticky-top';\n  const PROPERTY_PADDING = 'padding-right';\n  const PROPERTY_MARGIN = 'margin-right';\n  /**\n   * Class definition\n   */\n\n  class ScrollBarHelper {\n    constructor() {\n      this._element = document.body;\n    } // Public\n\n\n    getWidth() {\n      // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n      const documentWidth = document.documentElement.clientWidth;\n      return Math.abs(window.innerWidth - documentWidth);\n    }\n\n    hide() {\n      const width = this.getWidth();\n\n      this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width\n\n\n      this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n\n\n      this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n\n      this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n    }\n\n    reset() {\n      this._resetElementAttributes(this._element, 'overflow');\n\n      this._resetElementAttributes(this._element, PROPERTY_PADDING);\n\n      this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n\n      this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n    }\n\n    isOverflowing() {\n      return this.getWidth() > 0;\n    } // Private\n\n\n    _disableOverFlow() {\n      this._saveInitialAttribute(this._element, 'overflow');\n\n      this._element.style.overflow = 'hidden';\n    }\n\n    _setElementAttributes(selector, styleProperty, callback) {\n      const scrollbarWidth = this.getWidth();\n\n      const manipulationCallBack = element => {\n        if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n          return;\n        }\n\n        this._saveInitialAttribute(element, styleProperty);\n\n        const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n        element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n      };\n\n      this._applyManipulationCallback(selector, manipulationCallBack);\n    }\n\n    _saveInitialAttribute(element, styleProperty) {\n      const actualValue = element.style.getPropertyValue(styleProperty);\n\n      if (actualValue) {\n        Manipulator.setDataAttribute(element, styleProperty, actualValue);\n      }\n    }\n\n    _resetElementAttributes(selector, styleProperty) {\n      const manipulationCallBack = element => {\n        const value = Manipulator.getDataAttribute(element, styleProperty); // We only want to remove the property if the value is `null`; the value can also be zero\n\n        if (value === null) {\n          element.style.removeProperty(styleProperty);\n          return;\n        }\n\n        Manipulator.removeDataAttribute(element, styleProperty);\n        element.style.setProperty(styleProperty, value);\n      };\n\n      this._applyManipulationCallback(selector, manipulationCallBack);\n    }\n\n    _applyManipulationCallback(selector, callBack) {\n      if (isElement(selector)) {\n        callBack(selector);\n        return;\n      }\n\n      for (const sel of SelectorEngine.find(selector, this._element)) {\n        callBack(sel);\n      }\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/backdrop.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$9 = 'backdrop';\n  const CLASS_NAME_FADE$4 = 'fade';\n  const CLASS_NAME_SHOW$5 = 'show';\n  const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\n  const Default$8 = {\n    className: 'modal-backdrop',\n    clickCallback: null,\n    isAnimated: false,\n    isVisible: true,\n    // if false, we use the backdrop helper without adding any element to the dom\n    rootElement: 'body' // give the choice to place backdrop under different elements\n\n  };\n  const DefaultType$8 = {\n    className: 'string',\n    clickCallback: '(function|null)',\n    isAnimated: 'boolean',\n    isVisible: 'boolean',\n    rootElement: '(element|string)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Backdrop extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n      this._isAppended = false;\n      this._element = null;\n    } // Getters\n\n\n    static get Default() {\n      return Default$8;\n    }\n\n    static get DefaultType() {\n      return DefaultType$8;\n    }\n\n    static get NAME() {\n      return NAME$9;\n    } // Public\n\n\n    show(callback) {\n      if (!this._config.isVisible) {\n        execute(callback);\n        return;\n      }\n\n      this._append();\n\n      const element = this._getElement();\n\n      if (this._config.isAnimated) {\n        reflow(element);\n      }\n\n      element.classList.add(CLASS_NAME_SHOW$5);\n\n      this._emulateAnimation(() => {\n        execute(callback);\n      });\n    }\n\n    hide(callback) {\n      if (!this._config.isVisible) {\n        execute(callback);\n        return;\n      }\n\n      this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n\n      this._emulateAnimation(() => {\n        this.dispose();\n        execute(callback);\n      });\n    }\n\n    dispose() {\n      if (!this._isAppended) {\n        return;\n      }\n\n      EventHandler.off(this._element, EVENT_MOUSEDOWN);\n\n      this._element.remove();\n\n      this._isAppended = false;\n    } // Private\n\n\n    _getElement() {\n      if (!this._element) {\n        const backdrop = document.createElement('div');\n        backdrop.className = this._config.className;\n\n        if (this._config.isAnimated) {\n          backdrop.classList.add(CLASS_NAME_FADE$4);\n        }\n\n        this._element = backdrop;\n      }\n\n      return this._element;\n    }\n\n    _configAfterMerge(config) {\n      // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n      config.rootElement = getElement(config.rootElement);\n      return config;\n    }\n\n    _append() {\n      if (this._isAppended) {\n        return;\n      }\n\n      const element = this._getElement();\n\n      this._config.rootElement.append(element);\n\n      EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n        execute(this._config.clickCallback);\n      });\n      this._isAppended = true;\n    }\n\n    _emulateAnimation(callback) {\n      executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/focustrap.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$8 = 'focustrap';\n  const DATA_KEY$5 = 'bs.focustrap';\n  const EVENT_KEY$5 = `.${DATA_KEY$5}`;\n  const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\n  const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\n  const TAB_KEY = 'Tab';\n  const TAB_NAV_FORWARD = 'forward';\n  const TAB_NAV_BACKWARD = 'backward';\n  const Default$7 = {\n    autofocus: true,\n    trapElement: null // The element to trap focus inside of\n\n  };\n  const DefaultType$7 = {\n    autofocus: 'boolean',\n    trapElement: 'element'\n  };\n  /**\n   * Class definition\n   */\n\n  class FocusTrap extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n      this._isActive = false;\n      this._lastTabNavDirection = null;\n    } // Getters\n\n\n    static get Default() {\n      return Default$7;\n    }\n\n    static get DefaultType() {\n      return DefaultType$7;\n    }\n\n    static get NAME() {\n      return NAME$8;\n    } // Public\n\n\n    activate() {\n      if (this._isActive) {\n        return;\n      }\n\n      if (this._config.autofocus) {\n        this._config.trapElement.focus();\n      }\n\n      EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n\n      EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n      EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n      this._isActive = true;\n    }\n\n    deactivate() {\n      if (!this._isActive) {\n        return;\n      }\n\n      this._isActive = false;\n      EventHandler.off(document, EVENT_KEY$5);\n    } // Private\n\n\n    _handleFocusin(event) {\n      const {\n        trapElement\n      } = this._config;\n\n      if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n        return;\n      }\n\n      const elements = SelectorEngine.focusableChildren(trapElement);\n\n      if (elements.length === 0) {\n        trapElement.focus();\n      } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n        elements[elements.length - 1].focus();\n      } else {\n        elements[0].focus();\n      }\n    }\n\n    _handleKeydown(event) {\n      if (event.key !== TAB_KEY) {\n        return;\n      }\n\n      this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): modal.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$7 = 'modal';\n  const DATA_KEY$4 = 'bs.modal';\n  const EVENT_KEY$4 = `.${DATA_KEY$4}`;\n  const DATA_API_KEY$2 = '.data-api';\n  const ESCAPE_KEY$1 = 'Escape';\n  const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\n  const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\n  const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\n  const EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\n  const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\n  const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\n  const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\n  const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\n  const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\n  const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\n  const CLASS_NAME_OPEN = 'modal-open';\n  const CLASS_NAME_FADE$3 = 'fade';\n  const CLASS_NAME_SHOW$4 = 'show';\n  const CLASS_NAME_STATIC = 'modal-static';\n  const OPEN_SELECTOR$1 = '.modal.show';\n  const SELECTOR_DIALOG = '.modal-dialog';\n  const SELECTOR_MODAL_BODY = '.modal-body';\n  const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\n  const Default$6 = {\n    backdrop: true,\n    focus: true,\n    keyboard: true\n  };\n  const DefaultType$6 = {\n    backdrop: '(boolean|string)',\n    focus: 'boolean',\n    keyboard: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Modal extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n      this._backdrop = this._initializeBackDrop();\n      this._focustrap = this._initializeFocusTrap();\n      this._isShown = false;\n      this._isTransitioning = false;\n      this._scrollBar = new ScrollBarHelper();\n\n      this._addEventListeners();\n    } // Getters\n\n\n    static get Default() {\n      return Default$6;\n    }\n\n    static get DefaultType() {\n      return DefaultType$6;\n    }\n\n    static get NAME() {\n      return NAME$7;\n    } // Public\n\n\n    toggle(relatedTarget) {\n      return this._isShown ? this.hide() : this.show(relatedTarget);\n    }\n\n    show(relatedTarget) {\n      if (this._isShown || this._isTransitioning) {\n        return;\n      }\n\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n        relatedTarget\n      });\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._isShown = true;\n      this._isTransitioning = true;\n\n      this._scrollBar.hide();\n\n      document.body.classList.add(CLASS_NAME_OPEN);\n\n      this._adjustDialog();\n\n      this._backdrop.show(() => this._showElement(relatedTarget));\n    }\n\n    hide() {\n      if (!this._isShown || this._isTransitioning) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      this._isShown = false;\n      this._isTransitioning = true;\n\n      this._focustrap.deactivate();\n\n      this._element.classList.remove(CLASS_NAME_SHOW$4);\n\n      this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n    }\n\n    dispose() {\n      for (const htmlElement of [window, this._dialog]) {\n        EventHandler.off(htmlElement, EVENT_KEY$4);\n      }\n\n      this._backdrop.dispose();\n\n      this._focustrap.deactivate();\n\n      super.dispose();\n    }\n\n    handleUpdate() {\n      this._adjustDialog();\n    } // Private\n\n\n    _initializeBackDrop() {\n      return new Backdrop({\n        isVisible: Boolean(this._config.backdrop),\n        // 'static' option will be translated to true, and booleans will keep their value,\n        isAnimated: this._isAnimated()\n      });\n    }\n\n    _initializeFocusTrap() {\n      return new FocusTrap({\n        trapElement: this._element\n      });\n    }\n\n    _showElement(relatedTarget) {\n      // try to append dynamic modal\n      if (!document.body.contains(this._element)) {\n        document.body.append(this._element);\n      }\n\n      this._element.style.display = 'block';\n\n      this._element.removeAttribute('aria-hidden');\n\n      this._element.setAttribute('aria-modal', true);\n\n      this._element.setAttribute('role', 'dialog');\n\n      this._element.scrollTop = 0;\n      const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n\n      if (modalBody) {\n        modalBody.scrollTop = 0;\n      }\n\n      reflow(this._element);\n\n      this._element.classList.add(CLASS_NAME_SHOW$4);\n\n      const transitionComplete = () => {\n        if (this._config.focus) {\n          this._focustrap.activate();\n        }\n\n        this._isTransitioning = false;\n        EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n          relatedTarget\n        });\n      };\n\n      this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n    }\n\n    _addEventListeners() {\n      EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n        if (event.key !== ESCAPE_KEY$1) {\n          return;\n        }\n\n        if (this._config.keyboard) {\n          event.preventDefault();\n          this.hide();\n          return;\n        }\n\n        this._triggerBackdropTransition();\n      });\n      EventHandler.on(window, EVENT_RESIZE$1, () => {\n        if (this._isShown && !this._isTransitioning) {\n          this._adjustDialog();\n        }\n      });\n      EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n        // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n        EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n          if (this._element !== event.target || this._element !== event2.target) {\n            return;\n          }\n\n          if (this._config.backdrop === 'static') {\n            this._triggerBackdropTransition();\n\n            return;\n          }\n\n          if (this._config.backdrop) {\n            this.hide();\n          }\n        });\n      });\n    }\n\n    _hideModal() {\n      this._element.style.display = 'none';\n\n      this._element.setAttribute('aria-hidden', true);\n\n      this._element.removeAttribute('aria-modal');\n\n      this._element.removeAttribute('role');\n\n      this._isTransitioning = false;\n\n      this._backdrop.hide(() => {\n        document.body.classList.remove(CLASS_NAME_OPEN);\n\n        this._resetAdjustments();\n\n        this._scrollBar.reset();\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n      });\n    }\n\n    _isAnimated() {\n      return this._element.classList.contains(CLASS_NAME_FADE$3);\n    }\n\n    _triggerBackdropTransition() {\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n      const initialOverflowY = this._element.style.overflowY; // return if the following background transition hasn't yet completed\n\n      if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n        return;\n      }\n\n      if (!isModalOverflowing) {\n        this._element.style.overflowY = 'hidden';\n      }\n\n      this._element.classList.add(CLASS_NAME_STATIC);\n\n      this._queueCallback(() => {\n        this._element.classList.remove(CLASS_NAME_STATIC);\n\n        this._queueCallback(() => {\n          this._element.style.overflowY = initialOverflowY;\n        }, this._dialog);\n      }, this._dialog);\n\n      this._element.focus();\n    }\n    /**\n     * The following methods are used to handle overflowing modals\n     */\n\n\n    _adjustDialog() {\n      const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n\n      const scrollbarWidth = this._scrollBar.getWidth();\n\n      const isBodyOverflowing = scrollbarWidth > 0;\n\n      if (isBodyOverflowing && !isModalOverflowing) {\n        const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n        this._element.style[property] = `${scrollbarWidth}px`;\n      }\n\n      if (!isBodyOverflowing && isModalOverflowing) {\n        const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n        this._element.style[property] = `${scrollbarWidth}px`;\n      }\n    }\n\n    _resetAdjustments() {\n      this._element.style.paddingLeft = '';\n      this._element.style.paddingRight = '';\n    } // Static\n\n\n    static jQueryInterface(config, relatedTarget) {\n      return this.each(function () {\n        const data = Modal.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config](relatedTarget);\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n    const target = getElementFromSelector(this);\n\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n\n    EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n      if (showEvent.defaultPrevented) {\n        // only register focus restorer if modal will actually get shown\n        return;\n      }\n\n      EventHandler.one(target, EVENT_HIDDEN$4, () => {\n        if (isVisible(this)) {\n          this.focus();\n        }\n      });\n    }); // avoid conflict when clicking modal toggler while another one is open\n\n    const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n\n    if (alreadyOpen) {\n      Modal.getInstance(alreadyOpen).hide();\n    }\n\n    const data = Modal.getOrCreateInstance(target);\n    data.toggle(this);\n  });\n  enableDismissTrigger(Modal);\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Modal);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): offcanvas.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$6 = 'offcanvas';\n  const DATA_KEY$3 = 'bs.offcanvas';\n  const EVENT_KEY$3 = `.${DATA_KEY$3}`;\n  const DATA_API_KEY$1 = '.data-api';\n  const EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\n  const ESCAPE_KEY = 'Escape';\n  const CLASS_NAME_SHOW$3 = 'show';\n  const CLASS_NAME_SHOWING$1 = 'showing';\n  const CLASS_NAME_HIDING = 'hiding';\n  const CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\n  const OPEN_SELECTOR = '.offcanvas.show';\n  const EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\n  const EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\n  const EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\n  const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\n  const EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\n  const EVENT_RESIZE = `resize${EVENT_KEY$3}`;\n  const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\n  const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\n  const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\n  const Default$5 = {\n    backdrop: true,\n    keyboard: true,\n    scroll: false\n  };\n  const DefaultType$5 = {\n    backdrop: '(boolean|string)',\n    keyboard: 'boolean',\n    scroll: 'boolean'\n  };\n  /**\n   * Class definition\n   */\n\n  class Offcanvas extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._isShown = false;\n      this._backdrop = this._initializeBackDrop();\n      this._focustrap = this._initializeFocusTrap();\n\n      this._addEventListeners();\n    } // Getters\n\n\n    static get Default() {\n      return Default$5;\n    }\n\n    static get DefaultType() {\n      return DefaultType$5;\n    }\n\n    static get NAME() {\n      return NAME$6;\n    } // Public\n\n\n    toggle(relatedTarget) {\n      return this._isShown ? this.hide() : this.show(relatedTarget);\n    }\n\n    show(relatedTarget) {\n      if (this._isShown) {\n        return;\n      }\n\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n        relatedTarget\n      });\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._isShown = true;\n\n      this._backdrop.show();\n\n      if (!this._config.scroll) {\n        new ScrollBarHelper().hide();\n      }\n\n      this._element.setAttribute('aria-modal', true);\n\n      this._element.setAttribute('role', 'dialog');\n\n      this._element.classList.add(CLASS_NAME_SHOWING$1);\n\n      const completeCallBack = () => {\n        if (!this._config.scroll || this._config.backdrop) {\n          this._focustrap.activate();\n        }\n\n        this._element.classList.add(CLASS_NAME_SHOW$3);\n\n        this._element.classList.remove(CLASS_NAME_SHOWING$1);\n\n        EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n          relatedTarget\n        });\n      };\n\n      this._queueCallback(completeCallBack, this._element, true);\n    }\n\n    hide() {\n      if (!this._isShown) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      this._focustrap.deactivate();\n\n      this._element.blur();\n\n      this._isShown = false;\n\n      this._element.classList.add(CLASS_NAME_HIDING);\n\n      this._backdrop.hide();\n\n      const completeCallback = () => {\n        this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n\n        this._element.removeAttribute('aria-modal');\n\n        this._element.removeAttribute('role');\n\n        if (!this._config.scroll) {\n          new ScrollBarHelper().reset();\n        }\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n      };\n\n      this._queueCallback(completeCallback, this._element, true);\n    }\n\n    dispose() {\n      this._backdrop.dispose();\n\n      this._focustrap.deactivate();\n\n      super.dispose();\n    } // Private\n\n\n    _initializeBackDrop() {\n      const clickCallback = () => {\n        if (this._config.backdrop === 'static') {\n          EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n          return;\n        }\n\n        this.hide();\n      }; // 'static' option will be translated to true, and booleans will keep their value\n\n\n      const isVisible = Boolean(this._config.backdrop);\n      return new Backdrop({\n        className: CLASS_NAME_BACKDROP,\n        isVisible,\n        isAnimated: true,\n        rootElement: this._element.parentNode,\n        clickCallback: isVisible ? clickCallback : null\n      });\n    }\n\n    _initializeFocusTrap() {\n      return new FocusTrap({\n        trapElement: this._element\n      });\n    }\n\n    _addEventListeners() {\n      EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n        if (event.key !== ESCAPE_KEY) {\n          return;\n        }\n\n        if (!this._config.keyboard) {\n          EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n          return;\n        }\n\n        this.hide();\n      });\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Offcanvas.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config](this);\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n    const target = getElementFromSelector(this);\n\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n\n    if (isDisabled(this)) {\n      return;\n    }\n\n    EventHandler.one(target, EVENT_HIDDEN$3, () => {\n      // focus on trigger when it is closed\n      if (isVisible(this)) {\n        this.focus();\n      }\n    }); // avoid conflict when clicking a toggler of an offcanvas, while another is open\n\n    const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n\n    if (alreadyOpen && alreadyOpen !== target) {\n      Offcanvas.getInstance(alreadyOpen).hide();\n    }\n\n    const data = Offcanvas.getOrCreateInstance(target);\n    data.toggle(this);\n  });\n  EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n    for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n      Offcanvas.getOrCreateInstance(selector).show();\n    }\n  });\n  EventHandler.on(window, EVENT_RESIZE, () => {\n    for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n      if (getComputedStyle(element).position !== 'fixed') {\n        Offcanvas.getOrCreateInstance(element).hide();\n      }\n    }\n  });\n  enableDismissTrigger(Offcanvas);\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Offcanvas);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/sanitizer.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n  const ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\n  /**\n   * A pattern that recognizes a commonly useful subset of URLs that are safe.\n   *\n   * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n   */\n\n  const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;\n  /**\n   * A pattern that matches safe data URLs. Only matches image, video and audio types.\n   *\n   * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n   */\n\n  const DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[\\d+/a-z]+=*$/i;\n\n  const allowedAttribute = (attribute, allowedAttributeList) => {\n    const attributeName = attribute.nodeName.toLowerCase();\n\n    if (allowedAttributeList.includes(attributeName)) {\n      if (uriAttributes.has(attributeName)) {\n        return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));\n      }\n\n      return true;\n    } // Check if a regular expression validates the attribute.\n\n\n    return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n  };\n\n  const DefaultAllowlist = {\n    // Global attributes allowed on any supplied element below.\n    '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n    a: ['target', 'href', 'title', 'rel'],\n    area: [],\n    b: [],\n    br: [],\n    col: [],\n    code: [],\n    div: [],\n    em: [],\n    hr: [],\n    h1: [],\n    h2: [],\n    h3: [],\n    h4: [],\n    h5: [],\n    h6: [],\n    i: [],\n    img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n    li: [],\n    ol: [],\n    p: [],\n    pre: [],\n    s: [],\n    small: [],\n    span: [],\n    sub: [],\n    sup: [],\n    strong: [],\n    u: [],\n    ul: []\n  };\n  function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n    if (!unsafeHtml.length) {\n      return unsafeHtml;\n    }\n\n    if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n      return sanitizeFunction(unsafeHtml);\n    }\n\n    const domParser = new window.DOMParser();\n    const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n    const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n\n    for (const element of elements) {\n      const elementName = element.nodeName.toLowerCase();\n\n      if (!Object.keys(allowList).includes(elementName)) {\n        element.remove();\n        continue;\n      }\n\n      const attributeList = [].concat(...element.attributes);\n      const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n\n      for (const attribute of attributeList) {\n        if (!allowedAttribute(attribute, allowedAttributes)) {\n          element.removeAttribute(attribute.nodeName);\n        }\n      }\n    }\n\n    return createdDocument.body.innerHTML;\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): util/template-factory.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$5 = 'TemplateFactory';\n  const Default$4 = {\n    allowList: DefaultAllowlist,\n    content: {},\n    // { selector : text ,  selector2 : text2 , }\n    extraClass: '',\n    html: false,\n    sanitize: true,\n    sanitizeFn: null,\n    template: '<div></div>'\n  };\n  const DefaultType$4 = {\n    allowList: 'object',\n    content: 'object',\n    extraClass: '(string|function)',\n    html: 'boolean',\n    sanitize: 'boolean',\n    sanitizeFn: '(null|function)',\n    template: 'string'\n  };\n  const DefaultContentType = {\n    entry: '(string|element|function|null)',\n    selector: '(string|element)'\n  };\n  /**\n   * Class definition\n   */\n\n  class TemplateFactory extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n    } // Getters\n\n\n    static get Default() {\n      return Default$4;\n    }\n\n    static get DefaultType() {\n      return DefaultType$4;\n    }\n\n    static get NAME() {\n      return NAME$5;\n    } // Public\n\n\n    getContent() {\n      return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n    }\n\n    hasContent() {\n      return this.getContent().length > 0;\n    }\n\n    changeContent(content) {\n      this._checkContent(content);\n\n      this._config.content = { ...this._config.content,\n        ...content\n      };\n      return this;\n    }\n\n    toHtml() {\n      const templateWrapper = document.createElement('div');\n      templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n\n      for (const [selector, text] of Object.entries(this._config.content)) {\n        this._setContent(templateWrapper, text, selector);\n      }\n\n      const template = templateWrapper.children[0];\n\n      const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n\n      if (extraClass) {\n        template.classList.add(...extraClass.split(' '));\n      }\n\n      return template;\n    } // Private\n\n\n    _typeCheckConfig(config) {\n      super._typeCheckConfig(config);\n\n      this._checkContent(config.content);\n    }\n\n    _checkContent(arg) {\n      for (const [selector, content] of Object.entries(arg)) {\n        super._typeCheckConfig({\n          selector,\n          entry: content\n        }, DefaultContentType);\n      }\n    }\n\n    _setContent(template, content, selector) {\n      const templateElement = SelectorEngine.findOne(selector, template);\n\n      if (!templateElement) {\n        return;\n      }\n\n      content = this._resolvePossibleFunction(content);\n\n      if (!content) {\n        templateElement.remove();\n        return;\n      }\n\n      if (isElement(content)) {\n        this._putElementInTemplate(getElement(content), templateElement);\n\n        return;\n      }\n\n      if (this._config.html) {\n        templateElement.innerHTML = this._maybeSanitize(content);\n        return;\n      }\n\n      templateElement.textContent = content;\n    }\n\n    _maybeSanitize(arg) {\n      return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n    }\n\n    _resolvePossibleFunction(arg) {\n      return typeof arg === 'function' ? arg(this) : arg;\n    }\n\n    _putElementInTemplate(element, templateElement) {\n      if (this._config.html) {\n        templateElement.innerHTML = '';\n        templateElement.append(element);\n        return;\n      }\n\n      templateElement.textContent = element.textContent;\n    }\n\n  }\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): tooltip.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$4 = 'tooltip';\n  const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\n  const CLASS_NAME_FADE$2 = 'fade';\n  const CLASS_NAME_MODAL = 'modal';\n  const CLASS_NAME_SHOW$2 = 'show';\n  const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\n  const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\n  const EVENT_MODAL_HIDE = 'hide.bs.modal';\n  const TRIGGER_HOVER = 'hover';\n  const TRIGGER_FOCUS = 'focus';\n  const TRIGGER_CLICK = 'click';\n  const TRIGGER_MANUAL = 'manual';\n  const EVENT_HIDE$2 = 'hide';\n  const EVENT_HIDDEN$2 = 'hidden';\n  const EVENT_SHOW$2 = 'show';\n  const EVENT_SHOWN$2 = 'shown';\n  const EVENT_INSERTED = 'inserted';\n  const EVENT_CLICK$1 = 'click';\n  const EVENT_FOCUSIN$1 = 'focusin';\n  const EVENT_FOCUSOUT$1 = 'focusout';\n  const EVENT_MOUSEENTER = 'mouseenter';\n  const EVENT_MOUSELEAVE = 'mouseleave';\n  const AttachmentMap = {\n    AUTO: 'auto',\n    TOP: 'top',\n    RIGHT: isRTL() ? 'left' : 'right',\n    BOTTOM: 'bottom',\n    LEFT: isRTL() ? 'right' : 'left'\n  };\n  const Default$3 = {\n    allowList: DefaultAllowlist,\n    animation: true,\n    boundary: 'clippingParents',\n    container: false,\n    customClass: '',\n    delay: 0,\n    fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n    html: false,\n    offset: [0, 0],\n    placement: 'top',\n    popperConfig: null,\n    sanitize: true,\n    sanitizeFn: null,\n    selector: false,\n    template: '<div class=\"tooltip\" role=\"tooltip\">' + '<div class=\"tooltip-arrow\"></div>' + '<div class=\"tooltip-inner\"></div>' + '</div>',\n    title: '',\n    trigger: 'hover focus'\n  };\n  const DefaultType$3 = {\n    allowList: 'object',\n    animation: 'boolean',\n    boundary: '(string|element)',\n    container: '(string|element|boolean)',\n    customClass: '(string|function)',\n    delay: '(number|object)',\n    fallbackPlacements: 'array',\n    html: 'boolean',\n    offset: '(array|string|function)',\n    placement: '(string|function)',\n    popperConfig: '(null|object|function)',\n    sanitize: 'boolean',\n    sanitizeFn: '(null|function)',\n    selector: '(string|boolean)',\n    template: 'string',\n    title: '(string|element|function)',\n    trigger: 'string'\n  };\n  /**\n   * Class definition\n   */\n\n  class Tooltip extends BaseComponent {\n    constructor(element, config) {\n      if (typeof Popper__namespace === 'undefined') {\n        throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n      }\n\n      super(element, config); // Private\n\n      this._isEnabled = true;\n      this._timeout = 0;\n      this._isHovered = null;\n      this._activeTrigger = {};\n      this._popper = null;\n      this._templateFactory = null;\n      this._newContent = null; // Protected\n\n      this.tip = null;\n\n      this._setListeners();\n\n      if (!this._config.selector) {\n        this._fixTitle();\n      }\n    } // Getters\n\n\n    static get Default() {\n      return Default$3;\n    }\n\n    static get DefaultType() {\n      return DefaultType$3;\n    }\n\n    static get NAME() {\n      return NAME$4;\n    } // Public\n\n\n    enable() {\n      this._isEnabled = true;\n    }\n\n    disable() {\n      this._isEnabled = false;\n    }\n\n    toggleEnabled() {\n      this._isEnabled = !this._isEnabled;\n    }\n\n    toggle() {\n      if (!this._isEnabled) {\n        return;\n      }\n\n      this._activeTrigger.click = !this._activeTrigger.click;\n\n      if (this._isShown()) {\n        this._leave();\n\n        return;\n      }\n\n      this._enter();\n    }\n\n    dispose() {\n      clearTimeout(this._timeout);\n      EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n\n      if (this._element.getAttribute('data-bs-original-title')) {\n        this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n      }\n\n      this._disposePopper();\n\n      super.dispose();\n    }\n\n    show() {\n      if (this._element.style.display === 'none') {\n        throw new Error('Please use show on visible elements');\n      }\n\n      if (!(this._isWithContent() && this._isEnabled)) {\n        return;\n      }\n\n      const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n      const shadowRoot = findShadowRoot(this._element);\n\n      const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n\n      if (showEvent.defaultPrevented || !isInTheDom) {\n        return;\n      } // todo v6 remove this OR make it optional\n\n\n      this._disposePopper();\n\n      const tip = this._getTipElement();\n\n      this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n\n      const {\n        container\n      } = this._config;\n\n      if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n        container.append(tip);\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n      }\n\n      this._popper = this._createPopper(tip);\n      tip.classList.add(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we add extra\n      // empty mouseover listeners to the body's immediate children;\n      // only needed because of broken event delegation on iOS\n      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.on(element, 'mouseover', noop);\n        }\n      }\n\n      const complete = () => {\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n\n        if (this._isHovered === false) {\n          this._leave();\n        }\n\n        this._isHovered = false;\n      };\n\n      this._queueCallback(complete, this.tip, this._isAnimated());\n    }\n\n    hide() {\n      if (!this._isShown()) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      const tip = this._getTipElement();\n\n      tip.classList.remove(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we remove the extra\n      // empty mouseover listeners we added for iOS support\n\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.off(element, 'mouseover', noop);\n        }\n      }\n\n      this._activeTrigger[TRIGGER_CLICK] = false;\n      this._activeTrigger[TRIGGER_FOCUS] = false;\n      this._activeTrigger[TRIGGER_HOVER] = false;\n      this._isHovered = null; // it is a trick to support manual triggering\n\n      const complete = () => {\n        if (this._isWithActiveTrigger()) {\n          return;\n        }\n\n        if (!this._isHovered) {\n          this._disposePopper();\n        }\n\n        this._element.removeAttribute('aria-describedby');\n\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n      };\n\n      this._queueCallback(complete, this.tip, this._isAnimated());\n    }\n\n    update() {\n      if (this._popper) {\n        this._popper.update();\n      }\n    } // Protected\n\n\n    _isWithContent() {\n      return Boolean(this._getTitle());\n    }\n\n    _getTipElement() {\n      if (!this.tip) {\n        this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n      }\n\n      return this.tip;\n    }\n\n    _createTipElement(content) {\n      const tip = this._getTemplateFactory(content).toHtml(); // todo: remove this check on v6\n\n\n      if (!tip) {\n        return null;\n      }\n\n      tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); // todo: on v6 the following can be achieved with CSS only\n\n      tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n      const tipId = getUID(this.constructor.NAME).toString();\n      tip.setAttribute('id', tipId);\n\n      if (this._isAnimated()) {\n        tip.classList.add(CLASS_NAME_FADE$2);\n      }\n\n      return tip;\n    }\n\n    setContent(content) {\n      this._newContent = content;\n\n      if (this._isShown()) {\n        this._disposePopper();\n\n        this.show();\n      }\n    }\n\n    _getTemplateFactory(content) {\n      if (this._templateFactory) {\n        this._templateFactory.changeContent(content);\n      } else {\n        this._templateFactory = new TemplateFactory({ ...this._config,\n          // the `content` var has to be after `this._config`\n          // to override config.content in case of popover\n          content,\n          extraClass: this._resolvePossibleFunction(this._config.customClass)\n        });\n      }\n\n      return this._templateFactory;\n    }\n\n    _getContentForTemplate() {\n      return {\n        [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n      };\n    }\n\n    _getTitle() {\n      return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n    } // Private\n\n\n    _initializeOnDelegatedTarget(event) {\n      return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n    }\n\n    _isAnimated() {\n      return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n    }\n\n    _isShown() {\n      return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n    }\n\n    _createPopper(tip) {\n      const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;\n      const attachment = AttachmentMap[placement.toUpperCase()];\n      return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));\n    }\n\n    _getOffset() {\n      const {\n        offset\n      } = this._config;\n\n      if (typeof offset === 'string') {\n        return offset.split(',').map(value => Number.parseInt(value, 10));\n      }\n\n      if (typeof offset === 'function') {\n        return popperData => offset(popperData, this._element);\n      }\n\n      return offset;\n    }\n\n    _resolvePossibleFunction(arg) {\n      return typeof arg === 'function' ? arg.call(this._element) : arg;\n    }\n\n    _getPopperConfig(attachment) {\n      const defaultBsPopperConfig = {\n        placement: attachment,\n        modifiers: [{\n          name: 'flip',\n          options: {\n            fallbackPlacements: this._config.fallbackPlacements\n          }\n        }, {\n          name: 'offset',\n          options: {\n            offset: this._getOffset()\n          }\n        }, {\n          name: 'preventOverflow',\n          options: {\n            boundary: this._config.boundary\n          }\n        }, {\n          name: 'arrow',\n          options: {\n            element: `.${this.constructor.NAME}-arrow`\n          }\n        }, {\n          name: 'preSetPlacement',\n          enabled: true,\n          phase: 'beforeMain',\n          fn: data => {\n            // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n            // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n            this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n          }\n        }]\n      };\n      return { ...defaultBsPopperConfig,\n        ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n      };\n    }\n\n    _setListeners() {\n      const triggers = this._config.trigger.split(' ');\n\n      for (const trigger of triggers) {\n        if (trigger === 'click') {\n          EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n\n            context.toggle();\n          });\n        } else if (trigger !== TRIGGER_MANUAL) {\n          const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n          const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n          EventHandler.on(this._element, eventIn, this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n\n            context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n\n            context._enter();\n          });\n          EventHandler.on(this._element, eventOut, this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n\n            context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n\n            context._leave();\n          });\n        }\n      }\n\n      this._hideModalHandler = () => {\n        if (this._element) {\n          this.hide();\n        }\n      };\n\n      EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n    }\n\n    _fixTitle() {\n      const title = this._element.getAttribute('title');\n\n      if (!title) {\n        return;\n      }\n\n      if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n        this._element.setAttribute('aria-label', title);\n      }\n\n      this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n\n\n      this._element.removeAttribute('title');\n    }\n\n    _enter() {\n      if (this._isShown() || this._isHovered) {\n        this._isHovered = true;\n        return;\n      }\n\n      this._isHovered = true;\n\n      this._setTimeout(() => {\n        if (this._isHovered) {\n          this.show();\n        }\n      }, this._config.delay.show);\n    }\n\n    _leave() {\n      if (this._isWithActiveTrigger()) {\n        return;\n      }\n\n      this._isHovered = false;\n\n      this._setTimeout(() => {\n        if (!this._isHovered) {\n          this.hide();\n        }\n      }, this._config.delay.hide);\n    }\n\n    _setTimeout(handler, timeout) {\n      clearTimeout(this._timeout);\n      this._timeout = setTimeout(handler, timeout);\n    }\n\n    _isWithActiveTrigger() {\n      return Object.values(this._activeTrigger).includes(true);\n    }\n\n    _getConfig(config) {\n      const dataAttributes = Manipulator.getDataAttributes(this._element);\n\n      for (const dataAttribute of Object.keys(dataAttributes)) {\n        if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n          delete dataAttributes[dataAttribute];\n        }\n      }\n\n      config = { ...dataAttributes,\n        ...(typeof config === 'object' && config ? config : {})\n      };\n      config = this._mergeConfigObj(config);\n      config = this._configAfterMerge(config);\n\n      this._typeCheckConfig(config);\n\n      return config;\n    }\n\n    _configAfterMerge(config) {\n      config.container = config.container === false ? document.body : getElement(config.container);\n\n      if (typeof config.delay === 'number') {\n        config.delay = {\n          show: config.delay,\n          hide: config.delay\n        };\n      }\n\n      if (typeof config.title === 'number') {\n        config.title = config.title.toString();\n      }\n\n      if (typeof config.content === 'number') {\n        config.content = config.content.toString();\n      }\n\n      return config;\n    }\n\n    _getDelegateConfig() {\n      const config = {};\n\n      for (const key in this._config) {\n        if (this.constructor.Default[key] !== this._config[key]) {\n          config[key] = this._config[key];\n        }\n      }\n\n      config.selector = false;\n      config.trigger = 'manual'; // In the future can be replaced with:\n      // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n      // `Object.fromEntries(keysWithDifferentValues)`\n\n      return config;\n    }\n\n    _disposePopper() {\n      if (this._popper) {\n        this._popper.destroy();\n\n        this._popper = null;\n      }\n\n      if (this.tip) {\n        this.tip.remove();\n        this.tip = null;\n      }\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Tooltip.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * jQuery\n   */\n\n\n  defineJQueryPlugin(Tooltip);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): popover.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$3 = 'popover';\n  const SELECTOR_TITLE = '.popover-header';\n  const SELECTOR_CONTENT = '.popover-body';\n  const Default$2 = { ...Tooltip.Default,\n    content: '',\n    offset: [0, 8],\n    placement: 'right',\n    template: '<div class=\"popover\" role=\"tooltip\">' + '<div class=\"popover-arrow\"></div>' + '<h3 class=\"popover-header\"></h3>' + '<div class=\"popover-body\"></div>' + '</div>',\n    trigger: 'click'\n  };\n  const DefaultType$2 = { ...Tooltip.DefaultType,\n    content: '(null|string|element|function)'\n  };\n  /**\n   * Class definition\n   */\n\n  class Popover extends Tooltip {\n    // Getters\n    static get Default() {\n      return Default$2;\n    }\n\n    static get DefaultType() {\n      return DefaultType$2;\n    }\n\n    static get NAME() {\n      return NAME$3;\n    } // Overrides\n\n\n    _isWithContent() {\n      return this._getTitle() || this._getContent();\n    } // Private\n\n\n    _getContentForTemplate() {\n      return {\n        [SELECTOR_TITLE]: this._getTitle(),\n        [SELECTOR_CONTENT]: this._getContent()\n      };\n    }\n\n    _getContent() {\n      return this._resolvePossibleFunction(this._config.content);\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Popover.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * jQuery\n   */\n\n\n  defineJQueryPlugin(Popover);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): scrollspy.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$2 = 'scrollspy';\n  const DATA_KEY$2 = 'bs.scrollspy';\n  const EVENT_KEY$2 = `.${DATA_KEY$2}`;\n  const DATA_API_KEY = '.data-api';\n  const EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\n  const EVENT_CLICK = `click${EVENT_KEY$2}`;\n  const EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\n  const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\n  const CLASS_NAME_ACTIVE$1 = 'active';\n  const SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\n  const SELECTOR_TARGET_LINKS = '[href]';\n  const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\n  const SELECTOR_NAV_LINKS = '.nav-link';\n  const SELECTOR_NAV_ITEMS = '.nav-item';\n  const SELECTOR_LIST_ITEMS = '.list-group-item';\n  const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\n  const SELECTOR_DROPDOWN = '.dropdown';\n  const SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\n  const Default$1 = {\n    offset: null,\n    // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n    rootMargin: '0px 0px -25%',\n    smoothScroll: false,\n    target: null,\n    threshold: [0.1, 0.5, 1]\n  };\n  const DefaultType$1 = {\n    offset: '(number|null)',\n    // TODO v6 @deprecated, keep it for backwards compatibility reasons\n    rootMargin: 'string',\n    smoothScroll: 'boolean',\n    target: 'element',\n    threshold: 'array'\n  };\n  /**\n   * Class definition\n   */\n\n  class ScrollSpy extends BaseComponent {\n    constructor(element, config) {\n      super(element, config); // this._element is the observablesContainer and config.target the menu links wrapper\n\n      this._targetLinks = new Map();\n      this._observableSections = new Map();\n      this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n      this._activeTarget = null;\n      this._observer = null;\n      this._previousScrollData = {\n        visibleEntryTop: 0,\n        parentScrollTop: 0\n      };\n      this.refresh(); // initialize\n    } // Getters\n\n\n    static get Default() {\n      return Default$1;\n    }\n\n    static get DefaultType() {\n      return DefaultType$1;\n    }\n\n    static get NAME() {\n      return NAME$2;\n    } // Public\n\n\n    refresh() {\n      this._initializeTargetsAndObservables();\n\n      this._maybeEnableSmoothScroll();\n\n      if (this._observer) {\n        this._observer.disconnect();\n      } else {\n        this._observer = this._getNewObserver();\n      }\n\n      for (const section of this._observableSections.values()) {\n        this._observer.observe(section);\n      }\n    }\n\n    dispose() {\n      this._observer.disconnect();\n\n      super.dispose();\n    } // Private\n\n\n    _configAfterMerge(config) {\n      // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n      config.target = getElement(config.target) || document.body; // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n\n      config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n\n      if (typeof config.threshold === 'string') {\n        config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n      }\n\n      return config;\n    }\n\n    _maybeEnableSmoothScroll() {\n      if (!this._config.smoothScroll) {\n        return;\n      } // unregister any previous listeners\n\n\n      EventHandler.off(this._config.target, EVENT_CLICK);\n      EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n        const observableSection = this._observableSections.get(event.target.hash);\n\n        if (observableSection) {\n          event.preventDefault();\n          const root = this._rootElement || window;\n          const height = observableSection.offsetTop - this._element.offsetTop;\n\n          if (root.scrollTo) {\n            root.scrollTo({\n              top: height,\n              behavior: 'smooth'\n            });\n            return;\n          } // Chrome 60 doesn't support `scrollTo`\n\n\n          root.scrollTop = height;\n        }\n      });\n    }\n\n    _getNewObserver() {\n      const options = {\n        root: this._rootElement,\n        threshold: this._config.threshold,\n        rootMargin: this._config.rootMargin\n      };\n      return new IntersectionObserver(entries => this._observerCallback(entries), options);\n    } // The logic of selection\n\n\n    _observerCallback(entries) {\n      const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n\n      const activate = entry => {\n        this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n\n        this._process(targetElement(entry));\n      };\n\n      const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n      const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n      this._previousScrollData.parentScrollTop = parentScrollTop;\n\n      for (const entry of entries) {\n        if (!entry.isIntersecting) {\n          this._activeTarget = null;\n\n          this._clearActiveClass(targetElement(entry));\n\n          continue;\n        }\n\n        const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; // if we are scrolling down, pick the bigger offsetTop\n\n        if (userScrollsDown && entryIsLowerThanPrevious) {\n          activate(entry); // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n\n          if (!parentScrollTop) {\n            return;\n          }\n\n          continue;\n        } // if we are scrolling up, pick the smallest offsetTop\n\n\n        if (!userScrollsDown && !entryIsLowerThanPrevious) {\n          activate(entry);\n        }\n      }\n    }\n\n    _initializeTargetsAndObservables() {\n      this._targetLinks = new Map();\n      this._observableSections = new Map();\n      const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n\n      for (const anchor of targetLinks) {\n        // ensure that the anchor has an id and is not disabled\n        if (!anchor.hash || isDisabled(anchor)) {\n          continue;\n        }\n\n        const observableSection = SelectorEngine.findOne(anchor.hash, this._element); // ensure that the observableSection exists & is visible\n\n        if (isVisible(observableSection)) {\n          this._targetLinks.set(anchor.hash, anchor);\n\n          this._observableSections.set(anchor.hash, observableSection);\n        }\n      }\n    }\n\n    _process(target) {\n      if (this._activeTarget === target) {\n        return;\n      }\n\n      this._clearActiveClass(this._config.target);\n\n      this._activeTarget = target;\n      target.classList.add(CLASS_NAME_ACTIVE$1);\n\n      this._activateParents(target);\n\n      EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n        relatedTarget: target\n      });\n    }\n\n    _activateParents(target) {\n      // Activate dropdown parents\n      if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n        SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n        return;\n      }\n\n      for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n        // Set triggered links parents as active\n        // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n        for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n          item.classList.add(CLASS_NAME_ACTIVE$1);\n        }\n      }\n    }\n\n    _clearActiveClass(parent) {\n      parent.classList.remove(CLASS_NAME_ACTIVE$1);\n      const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE$1}`, parent);\n\n      for (const node of activeNodes) {\n        node.classList.remove(CLASS_NAME_ACTIVE$1);\n      }\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = ScrollSpy.getOrCreateInstance(this, config);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(window, EVENT_LOAD_DATA_API$1, () => {\n    for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n      ScrollSpy.getOrCreateInstance(spy);\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(ScrollSpy);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): tab.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME$1 = 'tab';\n  const DATA_KEY$1 = 'bs.tab';\n  const EVENT_KEY$1 = `.${DATA_KEY$1}`;\n  const EVENT_HIDE$1 = `hide${EVENT_KEY$1}`;\n  const EVENT_HIDDEN$1 = `hidden${EVENT_KEY$1}`;\n  const EVENT_SHOW$1 = `show${EVENT_KEY$1}`;\n  const EVENT_SHOWN$1 = `shown${EVENT_KEY$1}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY$1}`;\n  const EVENT_KEYDOWN = `keydown${EVENT_KEY$1}`;\n  const EVENT_LOAD_DATA_API = `load${EVENT_KEY$1}`;\n  const ARROW_LEFT_KEY = 'ArrowLeft';\n  const ARROW_RIGHT_KEY = 'ArrowRight';\n  const ARROW_UP_KEY = 'ArrowUp';\n  const ARROW_DOWN_KEY = 'ArrowDown';\n  const CLASS_NAME_ACTIVE = 'active';\n  const CLASS_NAME_FADE$1 = 'fade';\n  const CLASS_NAME_SHOW$1 = 'show';\n  const CLASS_DROPDOWN = 'dropdown';\n  const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';\n  const SELECTOR_DROPDOWN_MENU = '.dropdown-menu';\n  const NOT_SELECTOR_DROPDOWN_TOGGLE = ':not(.dropdown-toggle)';\n  const SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]';\n  const SELECTOR_OUTER = '.nav-item, .list-group-item';\n  const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`;\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]'; // todo:v6: could be only `tab`\n\n  const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`;\n  const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`;\n  /**\n   * Class definition\n   */\n\n  class Tab extends BaseComponent {\n    constructor(element) {\n      super(element);\n      this._parent = this._element.closest(SELECTOR_TAB_PANEL);\n\n      if (!this._parent) {\n        return; // todo: should Throw exception on v6\n        // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n      } // Set up initial aria attributes\n\n\n      this._setInitialAttributes(this._parent, this._getChildren());\n\n      EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));\n    } // Getters\n\n\n    static get NAME() {\n      return NAME$1;\n    } // Public\n\n\n    show() {\n      // Shows this elem and deactivate the active sibling if exists\n      const innerElem = this._element;\n\n      if (this._elemIsActive(innerElem)) {\n        return;\n      } // Search for active tab on same parent to deactivate it\n\n\n      const active = this._getActiveElem();\n\n      const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE$1, {\n        relatedTarget: innerElem\n      }) : null;\n      const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW$1, {\n        relatedTarget: active\n      });\n\n      if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) {\n        return;\n      }\n\n      this._deactivate(active, innerElem);\n\n      this._activate(innerElem, active);\n    } // Private\n\n\n    _activate(element, relatedElem) {\n      if (!element) {\n        return;\n      }\n\n      element.classList.add(CLASS_NAME_ACTIVE);\n\n      this._activate(getElementFromSelector(element)); // Search and activate/show the proper section\n\n\n      const complete = () => {\n        if (element.getAttribute('role') !== 'tab') {\n          element.classList.add(CLASS_NAME_SHOW$1);\n          return;\n        }\n\n        element.removeAttribute('tabindex');\n        element.setAttribute('aria-selected', true);\n\n        this._toggleDropDown(element, true);\n\n        EventHandler.trigger(element, EVENT_SHOWN$1, {\n          relatedTarget: relatedElem\n        });\n      };\n\n      this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1));\n    }\n\n    _deactivate(element, relatedElem) {\n      if (!element) {\n        return;\n      }\n\n      element.classList.remove(CLASS_NAME_ACTIVE);\n      element.blur();\n\n      this._deactivate(getElementFromSelector(element)); // Search and deactivate the shown section too\n\n\n      const complete = () => {\n        if (element.getAttribute('role') !== 'tab') {\n          element.classList.remove(CLASS_NAME_SHOW$1);\n          return;\n        }\n\n        element.setAttribute('aria-selected', false);\n        element.setAttribute('tabindex', '-1');\n\n        this._toggleDropDown(element, false);\n\n        EventHandler.trigger(element, EVENT_HIDDEN$1, {\n          relatedTarget: relatedElem\n        });\n      };\n\n      this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1));\n    }\n\n    _keydown(event) {\n      if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {\n        return;\n      }\n\n      event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n\n      event.preventDefault();\n      const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key);\n      const nextActiveElement = getNextActiveElement(this._getChildren().filter(element => !isDisabled(element)), event.target, isNext, true);\n\n      if (nextActiveElement) {\n        nextActiveElement.focus({\n          preventScroll: true\n        });\n        Tab.getOrCreateInstance(nextActiveElement).show();\n      }\n    }\n\n    _getChildren() {\n      // collection of inner elements\n      return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent);\n    }\n\n    _getActiveElem() {\n      return this._getChildren().find(child => this._elemIsActive(child)) || null;\n    }\n\n    _setInitialAttributes(parent, children) {\n      this._setAttributeIfNotExists(parent, 'role', 'tablist');\n\n      for (const child of children) {\n        this._setInitialAttributesOnChild(child);\n      }\n    }\n\n    _setInitialAttributesOnChild(child) {\n      child = this._getInnerElement(child);\n\n      const isActive = this._elemIsActive(child);\n\n      const outerElem = this._getOuterElement(child);\n\n      child.setAttribute('aria-selected', isActive);\n\n      if (outerElem !== child) {\n        this._setAttributeIfNotExists(outerElem, 'role', 'presentation');\n      }\n\n      if (!isActive) {\n        child.setAttribute('tabindex', '-1');\n      }\n\n      this._setAttributeIfNotExists(child, 'role', 'tab'); // set attributes to the related panel too\n\n\n      this._setInitialAttributesOnTargetPanel(child);\n    }\n\n    _setInitialAttributesOnTargetPanel(child) {\n      const target = getElementFromSelector(child);\n\n      if (!target) {\n        return;\n      }\n\n      this._setAttributeIfNotExists(target, 'role', 'tabpanel');\n\n      if (child.id) {\n        this._setAttributeIfNotExists(target, 'aria-labelledby', `#${child.id}`);\n      }\n    }\n\n    _toggleDropDown(element, open) {\n      const outerElem = this._getOuterElement(element);\n\n      if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n        return;\n      }\n\n      const toggle = (selector, className) => {\n        const element = SelectorEngine.findOne(selector, outerElem);\n\n        if (element) {\n          element.classList.toggle(className, open);\n        }\n      };\n\n      toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE);\n      toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW$1);\n      outerElem.setAttribute('aria-expanded', open);\n    }\n\n    _setAttributeIfNotExists(element, attribute, value) {\n      if (!element.hasAttribute(attribute)) {\n        element.setAttribute(attribute, value);\n      }\n    }\n\n    _elemIsActive(elem) {\n      return elem.classList.contains(CLASS_NAME_ACTIVE);\n    } // Try to get the inner element (usually the .nav-link)\n\n\n    _getInnerElement(elem) {\n      return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem);\n    } // Try to get the outer element (usually the .nav-item)\n\n\n    _getOuterElement(elem) {\n      return elem.closest(SELECTOR_OUTER) || elem;\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Tab.getOrCreateInstance(this);\n\n        if (typeof config !== 'string') {\n          return;\n        }\n\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n\n        data[config]();\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n\n    if (isDisabled(this)) {\n      return;\n    }\n\n    Tab.getOrCreateInstance(this).show();\n  });\n  /**\n   * Initialize on focus\n   */\n\n  EventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n    for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n      Tab.getOrCreateInstance(element);\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Tab);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): toast.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  /**\n   * Constants\n   */\n\n  const NAME = 'toast';\n  const DATA_KEY = 'bs.toast';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`;\n  const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`;\n  const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;\n  const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`;\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility\n\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_NAME_SHOWING = 'showing';\n  const DefaultType = {\n    animation: 'boolean',\n    autohide: 'boolean',\n    delay: 'number'\n  };\n  const Default = {\n    animation: true,\n    autohide: true,\n    delay: 5000\n  };\n  /**\n   * Class definition\n   */\n\n  class Toast extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._timeout = null;\n      this._hasMouseInteraction = false;\n      this._hasKeyboardInteraction = false;\n\n      this._setListeners();\n    } // Getters\n\n\n    static get Default() {\n      return Default;\n    }\n\n    static get DefaultType() {\n      return DefaultType;\n    }\n\n    static get NAME() {\n      return NAME;\n    } // Public\n\n\n    show() {\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);\n\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n\n      this._clearTimeout();\n\n      if (this._config.animation) {\n        this._element.classList.add(CLASS_NAME_FADE);\n      }\n\n      const complete = () => {\n        this._element.classList.remove(CLASS_NAME_SHOWING);\n\n        EventHandler.trigger(this._element, EVENT_SHOWN);\n\n        this._maybeScheduleHide();\n      };\n\n      this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated\n\n\n      reflow(this._element);\n\n      this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING);\n\n      this._queueCallback(complete, this._element, this._config.animation);\n    }\n\n    hide() {\n      if (!this.isShown()) {\n        return;\n      }\n\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);\n\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      const complete = () => {\n        this._element.classList.add(CLASS_NAME_HIDE); // @deprecated\n\n\n        this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW);\n\n        EventHandler.trigger(this._element, EVENT_HIDDEN);\n      };\n\n      this._element.classList.add(CLASS_NAME_SHOWING);\n\n      this._queueCallback(complete, this._element, this._config.animation);\n    }\n\n    dispose() {\n      this._clearTimeout();\n\n      if (this.isShown()) {\n        this._element.classList.remove(CLASS_NAME_SHOW);\n      }\n\n      super.dispose();\n    }\n\n    isShown() {\n      return this._element.classList.contains(CLASS_NAME_SHOW);\n    } // Private\n\n\n    _maybeScheduleHide() {\n      if (!this._config.autohide) {\n        return;\n      }\n\n      if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n        return;\n      }\n\n      this._timeout = setTimeout(() => {\n        this.hide();\n      }, this._config.delay);\n    }\n\n    _onInteraction(event, isInteracting) {\n      switch (event.type) {\n        case 'mouseover':\n        case 'mouseout':\n          {\n            this._hasMouseInteraction = isInteracting;\n            break;\n          }\n\n        case 'focusin':\n        case 'focusout':\n          {\n            this._hasKeyboardInteraction = isInteracting;\n            break;\n          }\n      }\n\n      if (isInteracting) {\n        this._clearTimeout();\n\n        return;\n      }\n\n      const nextElement = event.relatedTarget;\n\n      if (this._element === nextElement || this._element.contains(nextElement)) {\n        return;\n      }\n\n      this._maybeScheduleHide();\n    }\n\n    _setListeners() {\n      EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));\n      EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));\n      EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));\n      EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));\n    }\n\n    _clearTimeout() {\n      clearTimeout(this._timeout);\n      this._timeout = null;\n    } // Static\n\n\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Toast.getOrCreateInstance(this, config);\n\n        if (typeof config === 'string') {\n          if (typeof data[config] === 'undefined') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n\n          data[config](this);\n        }\n      });\n    }\n\n  }\n  /**\n   * Data API implementation\n   */\n\n\n  enableDismissTrigger(Toast);\n  /**\n   * jQuery\n   */\n\n  defineJQueryPlugin(Toast);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap (v5.2.3): index.umd.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n  const index_umd = {\n    Alert,\n    Button,\n    Carousel,\n    Collapse,\n    Dropdown,\n    Modal,\n    Offcanvas,\n    Popover,\n    ScrollSpy,\n    Tab,\n    Toast,\n    Tooltip\n  };\n\n  return index_umd;\n\n}));\n//# sourceMappingURL=bootstrap.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap-icons/bootstrap-icons.css",
    "content": "@font-face {\n  font-display: block;\n  font-family: \"bootstrap-icons\";\n  src: url(\"./fonts/bootstrap-icons.woff2?24e3eb84d0bcaf83d77f904c78ac1f47\") format(\"woff2\"),\nurl(\"./fonts/bootstrap-icons.woff?24e3eb84d0bcaf83d77f904c78ac1f47\") format(\"woff\");\n}\n\n.bi::before,\n[class^=\"bi-\"]::before,\n[class*=\" bi-\"]::before {\n  display: inline-block;\n  font-family: bootstrap-icons !important;\n  font-style: normal;\n  font-weight: normal !important;\n  font-variant: normal;\n  text-transform: none;\n  line-height: 1;\n  vertical-align: -.125em;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.bi-123::before { content: \"\\f67f\"; }\n.bi-alarm-fill::before { content: \"\\f101\"; }\n.bi-alarm::before { content: \"\\f102\"; }\n.bi-align-bottom::before { content: \"\\f103\"; }\n.bi-align-center::before { content: \"\\f104\"; }\n.bi-align-end::before { content: \"\\f105\"; }\n.bi-align-middle::before { content: \"\\f106\"; }\n.bi-align-start::before { content: \"\\f107\"; }\n.bi-align-top::before { content: \"\\f108\"; }\n.bi-alt::before { content: \"\\f109\"; }\n.bi-app-indicator::before { content: \"\\f10a\"; }\n.bi-app::before { content: \"\\f10b\"; }\n.bi-archive-fill::before { content: \"\\f10c\"; }\n.bi-archive::before { content: \"\\f10d\"; }\n.bi-arrow-90deg-down::before { content: \"\\f10e\"; }\n.bi-arrow-90deg-left::before { content: \"\\f10f\"; }\n.bi-arrow-90deg-right::before { content: \"\\f110\"; }\n.bi-arrow-90deg-up::before { content: \"\\f111\"; }\n.bi-arrow-bar-down::before { content: \"\\f112\"; }\n.bi-arrow-bar-left::before { content: \"\\f113\"; }\n.bi-arrow-bar-right::before { content: \"\\f114\"; }\n.bi-arrow-bar-up::before { content: \"\\f115\"; }\n.bi-arrow-clockwise::before { content: \"\\f116\"; }\n.bi-arrow-counterclockwise::before { content: \"\\f117\"; }\n.bi-arrow-down-circle-fill::before { content: \"\\f118\"; }\n.bi-arrow-down-circle::before { content: \"\\f119\"; }\n.bi-arrow-down-left-circle-fill::before { content: \"\\f11a\"; }\n.bi-arrow-down-left-circle::before { content: \"\\f11b\"; }\n.bi-arrow-down-left-square-fill::before { content: \"\\f11c\"; }\n.bi-arrow-down-left-square::before { content: \"\\f11d\"; }\n.bi-arrow-down-left::before { content: \"\\f11e\"; }\n.bi-arrow-down-right-circle-fill::before { content: \"\\f11f\"; }\n.bi-arrow-down-right-circle::before { content: \"\\f120\"; }\n.bi-arrow-down-right-square-fill::before { content: \"\\f121\"; }\n.bi-arrow-down-right-square::before { content: \"\\f122\"; }\n.bi-arrow-down-right::before { content: \"\\f123\"; }\n.bi-arrow-down-short::before { content: \"\\f124\"; }\n.bi-arrow-down-square-fill::before { content: \"\\f125\"; }\n.bi-arrow-down-square::before { content: \"\\f126\"; }\n.bi-arrow-down-up::before { content: \"\\f127\"; }\n.bi-arrow-down::before { content: \"\\f128\"; }\n.bi-arrow-left-circle-fill::before { content: \"\\f129\"; }\n.bi-arrow-left-circle::before { content: \"\\f12a\"; }\n.bi-arrow-left-right::before { content: \"\\f12b\"; }\n.bi-arrow-left-short::before { content: \"\\f12c\"; }\n.bi-arrow-left-square-fill::before { content: \"\\f12d\"; }\n.bi-arrow-left-square::before { content: \"\\f12e\"; }\n.bi-arrow-left::before { content: \"\\f12f\"; }\n.bi-arrow-repeat::before { content: \"\\f130\"; }\n.bi-arrow-return-left::before { content: \"\\f131\"; }\n.bi-arrow-return-right::before { content: \"\\f132\"; }\n.bi-arrow-right-circle-fill::before { content: \"\\f133\"; }\n.bi-arrow-right-circle::before { content: \"\\f134\"; }\n.bi-arrow-right-short::before { content: \"\\f135\"; }\n.bi-arrow-right-square-fill::before { content: \"\\f136\"; }\n.bi-arrow-right-square::before { content: \"\\f137\"; }\n.bi-arrow-right::before { content: \"\\f138\"; }\n.bi-arrow-up-circle-fill::before { content: \"\\f139\"; }\n.bi-arrow-up-circle::before { content: \"\\f13a\"; }\n.bi-arrow-up-left-circle-fill::before { content: \"\\f13b\"; }\n.bi-arrow-up-left-circle::before { content: \"\\f13c\"; }\n.bi-arrow-up-left-square-fill::before { content: \"\\f13d\"; }\n.bi-arrow-up-left-square::before { content: \"\\f13e\"; }\n.bi-arrow-up-left::before { content: \"\\f13f\"; }\n.bi-arrow-up-right-circle-fill::before { content: \"\\f140\"; }\n.bi-arrow-up-right-circle::before { content: \"\\f141\"; }\n.bi-arrow-up-right-square-fill::before { content: \"\\f142\"; }\n.bi-arrow-up-right-square::before { content: \"\\f143\"; }\n.bi-arrow-up-right::before { content: \"\\f144\"; }\n.bi-arrow-up-short::before { content: \"\\f145\"; }\n.bi-arrow-up-square-fill::before { content: \"\\f146\"; }\n.bi-arrow-up-square::before { content: \"\\f147\"; }\n.bi-arrow-up::before { content: \"\\f148\"; }\n.bi-arrows-angle-contract::before { content: \"\\f149\"; }\n.bi-arrows-angle-expand::before { content: \"\\f14a\"; }\n.bi-arrows-collapse::before { content: \"\\f14b\"; }\n.bi-arrows-expand::before { content: \"\\f14c\"; }\n.bi-arrows-fullscreen::before { content: \"\\f14d\"; }\n.bi-arrows-move::before { content: \"\\f14e\"; }\n.bi-aspect-ratio-fill::before { content: \"\\f14f\"; }\n.bi-aspect-ratio::before { content: \"\\f150\"; }\n.bi-asterisk::before { content: \"\\f151\"; }\n.bi-at::before { content: \"\\f152\"; }\n.bi-award-fill::before { content: \"\\f153\"; }\n.bi-award::before { content: \"\\f154\"; }\n.bi-back::before { content: \"\\f155\"; }\n.bi-backspace-fill::before { content: \"\\f156\"; }\n.bi-backspace-reverse-fill::before { content: \"\\f157\"; }\n.bi-backspace-reverse::before { content: \"\\f158\"; }\n.bi-backspace::before { content: \"\\f159\"; }\n.bi-badge-3d-fill::before { content: \"\\f15a\"; }\n.bi-badge-3d::before { content: \"\\f15b\"; }\n.bi-badge-4k-fill::before { content: \"\\f15c\"; }\n.bi-badge-4k::before { content: \"\\f15d\"; }\n.bi-badge-8k-fill::before { content: \"\\f15e\"; }\n.bi-badge-8k::before { content: \"\\f15f\"; }\n.bi-badge-ad-fill::before { content: \"\\f160\"; }\n.bi-badge-ad::before { content: \"\\f161\"; }\n.bi-badge-ar-fill::before { content: \"\\f162\"; }\n.bi-badge-ar::before { content: \"\\f163\"; }\n.bi-badge-cc-fill::before { content: \"\\f164\"; }\n.bi-badge-cc::before { content: \"\\f165\"; }\n.bi-badge-hd-fill::before { content: \"\\f166\"; }\n.bi-badge-hd::before { content: \"\\f167\"; }\n.bi-badge-tm-fill::before { content: \"\\f168\"; }\n.bi-badge-tm::before { content: \"\\f169\"; }\n.bi-badge-vo-fill::before { content: \"\\f16a\"; }\n.bi-badge-vo::before { content: \"\\f16b\"; }\n.bi-badge-vr-fill::before { content: \"\\f16c\"; }\n.bi-badge-vr::before { content: \"\\f16d\"; }\n.bi-badge-wc-fill::before { content: \"\\f16e\"; }\n.bi-badge-wc::before { content: \"\\f16f\"; }\n.bi-bag-check-fill::before { content: \"\\f170\"; }\n.bi-bag-check::before { content: \"\\f171\"; }\n.bi-bag-dash-fill::before { content: \"\\f172\"; }\n.bi-bag-dash::before { content: \"\\f173\"; }\n.bi-bag-fill::before { content: \"\\f174\"; }\n.bi-bag-plus-fill::before { content: \"\\f175\"; }\n.bi-bag-plus::before { content: \"\\f176\"; }\n.bi-bag-x-fill::before { content: \"\\f177\"; }\n.bi-bag-x::before { content: \"\\f178\"; }\n.bi-bag::before { content: \"\\f179\"; }\n.bi-bar-chart-fill::before { content: \"\\f17a\"; }\n.bi-bar-chart-line-fill::before { content: \"\\f17b\"; }\n.bi-bar-chart-line::before { content: \"\\f17c\"; }\n.bi-bar-chart-steps::before { content: \"\\f17d\"; }\n.bi-bar-chart::before { content: \"\\f17e\"; }\n.bi-basket-fill::before { content: \"\\f17f\"; }\n.bi-basket::before { content: \"\\f180\"; }\n.bi-basket2-fill::before { content: \"\\f181\"; }\n.bi-basket2::before { content: \"\\f182\"; }\n.bi-basket3-fill::before { content: \"\\f183\"; }\n.bi-basket3::before { content: \"\\f184\"; }\n.bi-battery-charging::before { content: \"\\f185\"; }\n.bi-battery-full::before { content: \"\\f186\"; }\n.bi-battery-half::before { content: \"\\f187\"; }\n.bi-battery::before { content: \"\\f188\"; }\n.bi-bell-fill::before { content: \"\\f189\"; }\n.bi-bell::before { content: \"\\f18a\"; }\n.bi-bezier::before { content: \"\\f18b\"; }\n.bi-bezier2::before { content: \"\\f18c\"; }\n.bi-bicycle::before { content: \"\\f18d\"; }\n.bi-binoculars-fill::before { content: \"\\f18e\"; }\n.bi-binoculars::before { content: \"\\f18f\"; }\n.bi-blockquote-left::before { content: \"\\f190\"; }\n.bi-blockquote-right::before { content: \"\\f191\"; }\n.bi-book-fill::before { content: \"\\f192\"; }\n.bi-book-half::before { content: \"\\f193\"; }\n.bi-book::before { content: \"\\f194\"; }\n.bi-bookmark-check-fill::before { content: \"\\f195\"; }\n.bi-bookmark-check::before { content: \"\\f196\"; }\n.bi-bookmark-dash-fill::before { content: \"\\f197\"; }\n.bi-bookmark-dash::before { content: \"\\f198\"; }\n.bi-bookmark-fill::before { content: \"\\f199\"; }\n.bi-bookmark-heart-fill::before { content: \"\\f19a\"; }\n.bi-bookmark-heart::before { content: \"\\f19b\"; }\n.bi-bookmark-plus-fill::before { content: \"\\f19c\"; }\n.bi-bookmark-plus::before { content: \"\\f19d\"; }\n.bi-bookmark-star-fill::before { content: \"\\f19e\"; }\n.bi-bookmark-star::before { content: \"\\f19f\"; }\n.bi-bookmark-x-fill::before { content: \"\\f1a0\"; }\n.bi-bookmark-x::before { content: \"\\f1a1\"; }\n.bi-bookmark::before { content: \"\\f1a2\"; }\n.bi-bookmarks-fill::before { content: \"\\f1a3\"; }\n.bi-bookmarks::before { content: \"\\f1a4\"; }\n.bi-bookshelf::before { content: \"\\f1a5\"; }\n.bi-bootstrap-fill::before { content: \"\\f1a6\"; }\n.bi-bootstrap-reboot::before { content: \"\\f1a7\"; }\n.bi-bootstrap::before { content: \"\\f1a8\"; }\n.bi-border-all::before { content: \"\\f1a9\"; }\n.bi-border-bottom::before { content: \"\\f1aa\"; }\n.bi-border-center::before { content: \"\\f1ab\"; }\n.bi-border-inner::before { content: \"\\f1ac\"; }\n.bi-border-left::before { content: \"\\f1ad\"; }\n.bi-border-middle::before { content: \"\\f1ae\"; }\n.bi-border-outer::before { content: \"\\f1af\"; }\n.bi-border-right::before { content: \"\\f1b0\"; }\n.bi-border-style::before { content: \"\\f1b1\"; }\n.bi-border-top::before { content: \"\\f1b2\"; }\n.bi-border-width::before { content: \"\\f1b3\"; }\n.bi-border::before { content: \"\\f1b4\"; }\n.bi-bounding-box-circles::before { content: \"\\f1b5\"; }\n.bi-bounding-box::before { content: \"\\f1b6\"; }\n.bi-box-arrow-down-left::before { content: \"\\f1b7\"; }\n.bi-box-arrow-down-right::before { content: \"\\f1b8\"; }\n.bi-box-arrow-down::before { content: \"\\f1b9\"; }\n.bi-box-arrow-in-down-left::before { content: \"\\f1ba\"; }\n.bi-box-arrow-in-down-right::before { content: \"\\f1bb\"; }\n.bi-box-arrow-in-down::before { content: \"\\f1bc\"; }\n.bi-box-arrow-in-left::before { content: \"\\f1bd\"; }\n.bi-box-arrow-in-right::before { content: \"\\f1be\"; }\n.bi-box-arrow-in-up-left::before { content: \"\\f1bf\"; }\n.bi-box-arrow-in-up-right::before { content: \"\\f1c0\"; }\n.bi-box-arrow-in-up::before { content: \"\\f1c1\"; }\n.bi-box-arrow-left::before { content: \"\\f1c2\"; }\n.bi-box-arrow-right::before { content: \"\\f1c3\"; }\n.bi-box-arrow-up-left::before { content: \"\\f1c4\"; }\n.bi-box-arrow-up-right::before { content: \"\\f1c5\"; }\n.bi-box-arrow-up::before { content: \"\\f1c6\"; }\n.bi-box-seam::before { content: \"\\f1c7\"; }\n.bi-box::before { content: \"\\f1c8\"; }\n.bi-braces::before { content: \"\\f1c9\"; }\n.bi-bricks::before { content: \"\\f1ca\"; }\n.bi-briefcase-fill::before { content: \"\\f1cb\"; }\n.bi-briefcase::before { content: \"\\f1cc\"; }\n.bi-brightness-alt-high-fill::before { content: \"\\f1cd\"; }\n.bi-brightness-alt-high::before { content: \"\\f1ce\"; }\n.bi-brightness-alt-low-fill::before { content: \"\\f1cf\"; }\n.bi-brightness-alt-low::before { content: \"\\f1d0\"; }\n.bi-brightness-high-fill::before { content: \"\\f1d1\"; }\n.bi-brightness-high::before { content: \"\\f1d2\"; }\n.bi-brightness-low-fill::before { content: \"\\f1d3\"; }\n.bi-brightness-low::before { content: \"\\f1d4\"; }\n.bi-broadcast-pin::before { content: \"\\f1d5\"; }\n.bi-broadcast::before { content: \"\\f1d6\"; }\n.bi-brush-fill::before { content: \"\\f1d7\"; }\n.bi-brush::before { content: \"\\f1d8\"; }\n.bi-bucket-fill::before { content: \"\\f1d9\"; }\n.bi-bucket::before { content: \"\\f1da\"; }\n.bi-bug-fill::before { content: \"\\f1db\"; }\n.bi-bug::before { content: \"\\f1dc\"; }\n.bi-building::before { content: \"\\f1dd\"; }\n.bi-bullseye::before { content: \"\\f1de\"; }\n.bi-calculator-fill::before { content: \"\\f1df\"; }\n.bi-calculator::before { content: \"\\f1e0\"; }\n.bi-calendar-check-fill::before { content: \"\\f1e1\"; }\n.bi-calendar-check::before { content: \"\\f1e2\"; }\n.bi-calendar-date-fill::before { content: \"\\f1e3\"; }\n.bi-calendar-date::before { content: \"\\f1e4\"; }\n.bi-calendar-day-fill::before { content: \"\\f1e5\"; }\n.bi-calendar-day::before { content: \"\\f1e6\"; }\n.bi-calendar-event-fill::before { content: \"\\f1e7\"; }\n.bi-calendar-event::before { content: \"\\f1e8\"; }\n.bi-calendar-fill::before { content: \"\\f1e9\"; }\n.bi-calendar-minus-fill::before { content: \"\\f1ea\"; }\n.bi-calendar-minus::before { content: \"\\f1eb\"; }\n.bi-calendar-month-fill::before { content: \"\\f1ec\"; }\n.bi-calendar-month::before { content: \"\\f1ed\"; }\n.bi-calendar-plus-fill::before { content: \"\\f1ee\"; }\n.bi-calendar-plus::before { content: \"\\f1ef\"; }\n.bi-calendar-range-fill::before { content: \"\\f1f0\"; }\n.bi-calendar-range::before { content: \"\\f1f1\"; }\n.bi-calendar-week-fill::before { content: \"\\f1f2\"; }\n.bi-calendar-week::before { content: \"\\f1f3\"; }\n.bi-calendar-x-fill::before { content: \"\\f1f4\"; }\n.bi-calendar-x::before { content: \"\\f1f5\"; }\n.bi-calendar::before { content: \"\\f1f6\"; }\n.bi-calendar2-check-fill::before { content: \"\\f1f7\"; }\n.bi-calendar2-check::before { content: \"\\f1f8\"; }\n.bi-calendar2-date-fill::before { content: \"\\f1f9\"; }\n.bi-calendar2-date::before { content: \"\\f1fa\"; }\n.bi-calendar2-day-fill::before { content: \"\\f1fb\"; }\n.bi-calendar2-day::before { content: \"\\f1fc\"; }\n.bi-calendar2-event-fill::before { content: \"\\f1fd\"; }\n.bi-calendar2-event::before { content: \"\\f1fe\"; }\n.bi-calendar2-fill::before { content: \"\\f1ff\"; }\n.bi-calendar2-minus-fill::before { content: \"\\f200\"; }\n.bi-calendar2-minus::before { content: \"\\f201\"; }\n.bi-calendar2-month-fill::before { content: \"\\f202\"; }\n.bi-calendar2-month::before { content: \"\\f203\"; }\n.bi-calendar2-plus-fill::before { content: \"\\f204\"; }\n.bi-calendar2-plus::before { content: \"\\f205\"; }\n.bi-calendar2-range-fill::before { content: \"\\f206\"; }\n.bi-calendar2-range::before { content: \"\\f207\"; }\n.bi-calendar2-week-fill::before { content: \"\\f208\"; }\n.bi-calendar2-week::before { content: \"\\f209\"; }\n.bi-calendar2-x-fill::before { content: \"\\f20a\"; }\n.bi-calendar2-x::before { content: \"\\f20b\"; }\n.bi-calendar2::before { content: \"\\f20c\"; }\n.bi-calendar3-event-fill::before { content: \"\\f20d\"; }\n.bi-calendar3-event::before { content: \"\\f20e\"; }\n.bi-calendar3-fill::before { content: \"\\f20f\"; }\n.bi-calendar3-range-fill::before { content: \"\\f210\"; }\n.bi-calendar3-range::before { content: \"\\f211\"; }\n.bi-calendar3-week-fill::before { content: \"\\f212\"; }\n.bi-calendar3-week::before { content: \"\\f213\"; }\n.bi-calendar3::before { content: \"\\f214\"; }\n.bi-calendar4-event::before { content: \"\\f215\"; }\n.bi-calendar4-range::before { content: \"\\f216\"; }\n.bi-calendar4-week::before { content: \"\\f217\"; }\n.bi-calendar4::before { content: \"\\f218\"; }\n.bi-camera-fill::before { content: \"\\f219\"; }\n.bi-camera-reels-fill::before { content: \"\\f21a\"; }\n.bi-camera-reels::before { content: \"\\f21b\"; }\n.bi-camera-video-fill::before { content: \"\\f21c\"; }\n.bi-camera-video-off-fill::before { content: \"\\f21d\"; }\n.bi-camera-video-off::before { content: \"\\f21e\"; }\n.bi-camera-video::before { content: \"\\f21f\"; }\n.bi-camera::before { content: \"\\f220\"; }\n.bi-camera2::before { content: \"\\f221\"; }\n.bi-capslock-fill::before { content: \"\\f222\"; }\n.bi-capslock::before { content: \"\\f223\"; }\n.bi-card-checklist::before { content: \"\\f224\"; }\n.bi-card-heading::before { content: \"\\f225\"; }\n.bi-card-image::before { content: \"\\f226\"; }\n.bi-card-list::before { content: \"\\f227\"; }\n.bi-card-text::before { content: \"\\f228\"; }\n.bi-caret-down-fill::before { content: \"\\f229\"; }\n.bi-caret-down-square-fill::before { content: \"\\f22a\"; }\n.bi-caret-down-square::before { content: \"\\f22b\"; }\n.bi-caret-down::before { content: \"\\f22c\"; }\n.bi-caret-left-fill::before { content: \"\\f22d\"; }\n.bi-caret-left-square-fill::before { content: \"\\f22e\"; }\n.bi-caret-left-square::before { content: \"\\f22f\"; }\n.bi-caret-left::before { content: \"\\f230\"; }\n.bi-caret-right-fill::before { content: \"\\f231\"; }\n.bi-caret-right-square-fill::before { content: \"\\f232\"; }\n.bi-caret-right-square::before { content: \"\\f233\"; }\n.bi-caret-right::before { content: \"\\f234\"; }\n.bi-caret-up-fill::before { content: \"\\f235\"; }\n.bi-caret-up-square-fill::before { content: \"\\f236\"; }\n.bi-caret-up-square::before { content: \"\\f237\"; }\n.bi-caret-up::before { content: \"\\f238\"; }\n.bi-cart-check-fill::before { content: \"\\f239\"; }\n.bi-cart-check::before { content: \"\\f23a\"; }\n.bi-cart-dash-fill::before { content: \"\\f23b\"; }\n.bi-cart-dash::before { content: \"\\f23c\"; }\n.bi-cart-fill::before { content: \"\\f23d\"; }\n.bi-cart-plus-fill::before { content: \"\\f23e\"; }\n.bi-cart-plus::before { content: \"\\f23f\"; }\n.bi-cart-x-fill::before { content: \"\\f240\"; }\n.bi-cart-x::before { content: \"\\f241\"; }\n.bi-cart::before { content: \"\\f242\"; }\n.bi-cart2::before { content: \"\\f243\"; }\n.bi-cart3::before { content: \"\\f244\"; }\n.bi-cart4::before { content: \"\\f245\"; }\n.bi-cash-stack::before { content: \"\\f246\"; }\n.bi-cash::before { content: \"\\f247\"; }\n.bi-cast::before { content: \"\\f248\"; }\n.bi-chat-dots-fill::before { content: \"\\f249\"; }\n.bi-chat-dots::before { content: \"\\f24a\"; }\n.bi-chat-fill::before { content: \"\\f24b\"; }\n.bi-chat-left-dots-fill::before { content: \"\\f24c\"; }\n.bi-chat-left-dots::before { content: \"\\f24d\"; }\n.bi-chat-left-fill::before { content: \"\\f24e\"; }\n.bi-chat-left-quote-fill::before { content: \"\\f24f\"; }\n.bi-chat-left-quote::before { content: \"\\f250\"; }\n.bi-chat-left-text-fill::before { content: \"\\f251\"; }\n.bi-chat-left-text::before { content: \"\\f252\"; }\n.bi-chat-left::before { content: \"\\f253\"; }\n.bi-chat-quote-fill::before { content: \"\\f254\"; }\n.bi-chat-quote::before { content: \"\\f255\"; }\n.bi-chat-right-dots-fill::before { content: \"\\f256\"; }\n.bi-chat-right-dots::before { content: \"\\f257\"; }\n.bi-chat-right-fill::before { content: \"\\f258\"; }\n.bi-chat-right-quote-fill::before { content: \"\\f259\"; }\n.bi-chat-right-quote::before { content: \"\\f25a\"; }\n.bi-chat-right-text-fill::before { content: \"\\f25b\"; }\n.bi-chat-right-text::before { content: \"\\f25c\"; }\n.bi-chat-right::before { content: \"\\f25d\"; }\n.bi-chat-square-dots-fill::before { content: \"\\f25e\"; }\n.bi-chat-square-dots::before { content: \"\\f25f\"; }\n.bi-chat-square-fill::before { content: \"\\f260\"; }\n.bi-chat-square-quote-fill::before { content: \"\\f261\"; }\n.bi-chat-square-quote::before { content: \"\\f262\"; }\n.bi-chat-square-text-fill::before { content: \"\\f263\"; }\n.bi-chat-square-text::before { content: \"\\f264\"; }\n.bi-chat-square::before { content: \"\\f265\"; }\n.bi-chat-text-fill::before { content: \"\\f266\"; }\n.bi-chat-text::before { content: \"\\f267\"; }\n.bi-chat::before { content: \"\\f268\"; }\n.bi-check-all::before { content: \"\\f269\"; }\n.bi-check-circle-fill::before { content: \"\\f26a\"; }\n.bi-check-circle::before { content: \"\\f26b\"; }\n.bi-check-square-fill::before { content: \"\\f26c\"; }\n.bi-check-square::before { content: \"\\f26d\"; }\n.bi-check::before { content: \"\\f26e\"; }\n.bi-check2-all::before { content: \"\\f26f\"; }\n.bi-check2-circle::before { content: \"\\f270\"; }\n.bi-check2-square::before { content: \"\\f271\"; }\n.bi-check2::before { content: \"\\f272\"; }\n.bi-chevron-bar-contract::before { content: \"\\f273\"; }\n.bi-chevron-bar-down::before { content: \"\\f274\"; }\n.bi-chevron-bar-expand::before { content: \"\\f275\"; }\n.bi-chevron-bar-left::before { content: \"\\f276\"; }\n.bi-chevron-bar-right::before { content: \"\\f277\"; }\n.bi-chevron-bar-up::before { content: \"\\f278\"; }\n.bi-chevron-compact-down::before { content: \"\\f279\"; }\n.bi-chevron-compact-left::before { content: \"\\f27a\"; }\n.bi-chevron-compact-right::before { content: \"\\f27b\"; }\n.bi-chevron-compact-up::before { content: \"\\f27c\"; }\n.bi-chevron-contract::before { content: \"\\f27d\"; }\n.bi-chevron-double-down::before { content: \"\\f27e\"; }\n.bi-chevron-double-left::before { content: \"\\f27f\"; }\n.bi-chevron-double-right::before { content: \"\\f280\"; }\n.bi-chevron-double-up::before { content: \"\\f281\"; }\n.bi-chevron-down::before { content: \"\\f282\"; }\n.bi-chevron-expand::before { content: \"\\f283\"; }\n.bi-chevron-left::before { content: \"\\f284\"; }\n.bi-chevron-right::before { content: \"\\f285\"; }\n.bi-chevron-up::before { content: \"\\f286\"; }\n.bi-circle-fill::before { content: \"\\f287\"; }\n.bi-circle-half::before { content: \"\\f288\"; }\n.bi-circle-square::before { content: \"\\f289\"; }\n.bi-circle::before { content: \"\\f28a\"; }\n.bi-clipboard-check::before { content: \"\\f28b\"; }\n.bi-clipboard-data::before { content: \"\\f28c\"; }\n.bi-clipboard-minus::before { content: \"\\f28d\"; }\n.bi-clipboard-plus::before { content: \"\\f28e\"; }\n.bi-clipboard-x::before { content: \"\\f28f\"; }\n.bi-clipboard::before { content: \"\\f290\"; }\n.bi-clock-fill::before { content: \"\\f291\"; }\n.bi-clock-history::before { content: \"\\f292\"; }\n.bi-clock::before { content: \"\\f293\"; }\n.bi-cloud-arrow-down-fill::before { content: \"\\f294\"; }\n.bi-cloud-arrow-down::before { content: \"\\f295\"; }\n.bi-cloud-arrow-up-fill::before { content: \"\\f296\"; }\n.bi-cloud-arrow-up::before { content: \"\\f297\"; }\n.bi-cloud-check-fill::before { content: \"\\f298\"; }\n.bi-cloud-check::before { content: \"\\f299\"; }\n.bi-cloud-download-fill::before { content: \"\\f29a\"; }\n.bi-cloud-download::before { content: \"\\f29b\"; }\n.bi-cloud-drizzle-fill::before { content: \"\\f29c\"; }\n.bi-cloud-drizzle::before { content: \"\\f29d\"; }\n.bi-cloud-fill::before { content: \"\\f29e\"; }\n.bi-cloud-fog-fill::before { content: \"\\f29f\"; }\n.bi-cloud-fog::before { content: \"\\f2a0\"; }\n.bi-cloud-fog2-fill::before { content: \"\\f2a1\"; }\n.bi-cloud-fog2::before { content: \"\\f2a2\"; }\n.bi-cloud-hail-fill::before { content: \"\\f2a3\"; }\n.bi-cloud-hail::before { content: \"\\f2a4\"; }\n.bi-cloud-haze-1::before { content: \"\\f2a5\"; }\n.bi-cloud-haze-fill::before { content: \"\\f2a6\"; }\n.bi-cloud-haze::before { content: \"\\f2a7\"; }\n.bi-cloud-haze2-fill::before { content: \"\\f2a8\"; }\n.bi-cloud-lightning-fill::before { content: \"\\f2a9\"; }\n.bi-cloud-lightning-rain-fill::before { content: \"\\f2aa\"; }\n.bi-cloud-lightning-rain::before { content: \"\\f2ab\"; }\n.bi-cloud-lightning::before { content: \"\\f2ac\"; }\n.bi-cloud-minus-fill::before { content: \"\\f2ad\"; }\n.bi-cloud-minus::before { content: \"\\f2ae\"; }\n.bi-cloud-moon-fill::before { content: \"\\f2af\"; }\n.bi-cloud-moon::before { content: \"\\f2b0\"; }\n.bi-cloud-plus-fill::before { content: \"\\f2b1\"; }\n.bi-cloud-plus::before { content: \"\\f2b2\"; }\n.bi-cloud-rain-fill::before { content: \"\\f2b3\"; }\n.bi-cloud-rain-heavy-fill::before { content: \"\\f2b4\"; }\n.bi-cloud-rain-heavy::before { content: \"\\f2b5\"; }\n.bi-cloud-rain::before { content: \"\\f2b6\"; }\n.bi-cloud-slash-fill::before { content: \"\\f2b7\"; }\n.bi-cloud-slash::before { content: \"\\f2b8\"; }\n.bi-cloud-sleet-fill::before { content: \"\\f2b9\"; }\n.bi-cloud-sleet::before { content: \"\\f2ba\"; }\n.bi-cloud-snow-fill::before { content: \"\\f2bb\"; }\n.bi-cloud-snow::before { content: \"\\f2bc\"; }\n.bi-cloud-sun-fill::before { content: \"\\f2bd\"; }\n.bi-cloud-sun::before { content: \"\\f2be\"; }\n.bi-cloud-upload-fill::before { content: \"\\f2bf\"; }\n.bi-cloud-upload::before { content: \"\\f2c0\"; }\n.bi-cloud::before { content: \"\\f2c1\"; }\n.bi-clouds-fill::before { content: \"\\f2c2\"; }\n.bi-clouds::before { content: \"\\f2c3\"; }\n.bi-cloudy-fill::before { content: \"\\f2c4\"; }\n.bi-cloudy::before { content: \"\\f2c5\"; }\n.bi-code-slash::before { content: \"\\f2c6\"; }\n.bi-code-square::before { content: \"\\f2c7\"; }\n.bi-code::before { content: \"\\f2c8\"; }\n.bi-collection-fill::before { content: \"\\f2c9\"; }\n.bi-collection-play-fill::before { content: \"\\f2ca\"; }\n.bi-collection-play::before { content: \"\\f2cb\"; }\n.bi-collection::before { content: \"\\f2cc\"; }\n.bi-columns-gap::before { content: \"\\f2cd\"; }\n.bi-columns::before { content: \"\\f2ce\"; }\n.bi-command::before { content: \"\\f2cf\"; }\n.bi-compass-fill::before { content: \"\\f2d0\"; }\n.bi-compass::before { content: \"\\f2d1\"; }\n.bi-cone-striped::before { content: \"\\f2d2\"; }\n.bi-cone::before { content: \"\\f2d3\"; }\n.bi-controller::before { content: \"\\f2d4\"; }\n.bi-cpu-fill::before { content: \"\\f2d5\"; }\n.bi-cpu::before { content: \"\\f2d6\"; }\n.bi-credit-card-2-back-fill::before { content: \"\\f2d7\"; }\n.bi-credit-card-2-back::before { content: \"\\f2d8\"; }\n.bi-credit-card-2-front-fill::before { content: \"\\f2d9\"; }\n.bi-credit-card-2-front::before { content: \"\\f2da\"; }\n.bi-credit-card-fill::before { content: \"\\f2db\"; }\n.bi-credit-card::before { content: \"\\f2dc\"; }\n.bi-crop::before { content: \"\\f2dd\"; }\n.bi-cup-fill::before { content: \"\\f2de\"; }\n.bi-cup-straw::before { content: \"\\f2df\"; }\n.bi-cup::before { content: \"\\f2e0\"; }\n.bi-cursor-fill::before { content: \"\\f2e1\"; }\n.bi-cursor-text::before { content: \"\\f2e2\"; }\n.bi-cursor::before { content: \"\\f2e3\"; }\n.bi-dash-circle-dotted::before { content: \"\\f2e4\"; }\n.bi-dash-circle-fill::before { content: \"\\f2e5\"; }\n.bi-dash-circle::before { content: \"\\f2e6\"; }\n.bi-dash-square-dotted::before { content: \"\\f2e7\"; }\n.bi-dash-square-fill::before { content: \"\\f2e8\"; }\n.bi-dash-square::before { content: \"\\f2e9\"; }\n.bi-dash::before { content: \"\\f2ea\"; }\n.bi-diagram-2-fill::before { content: \"\\f2eb\"; }\n.bi-diagram-2::before { content: \"\\f2ec\"; }\n.bi-diagram-3-fill::before { content: \"\\f2ed\"; }\n.bi-diagram-3::before { content: \"\\f2ee\"; }\n.bi-diamond-fill::before { content: \"\\f2ef\"; }\n.bi-diamond-half::before { content: \"\\f2f0\"; }\n.bi-diamond::before { content: \"\\f2f1\"; }\n.bi-dice-1-fill::before { content: \"\\f2f2\"; }\n.bi-dice-1::before { content: \"\\f2f3\"; }\n.bi-dice-2-fill::before { content: \"\\f2f4\"; }\n.bi-dice-2::before { content: \"\\f2f5\"; }\n.bi-dice-3-fill::before { content: \"\\f2f6\"; }\n.bi-dice-3::before { content: \"\\f2f7\"; }\n.bi-dice-4-fill::before { content: \"\\f2f8\"; }\n.bi-dice-4::before { content: \"\\f2f9\"; }\n.bi-dice-5-fill::before { content: \"\\f2fa\"; }\n.bi-dice-5::before { content: \"\\f2fb\"; }\n.bi-dice-6-fill::before { content: \"\\f2fc\"; }\n.bi-dice-6::before { content: \"\\f2fd\"; }\n.bi-disc-fill::before { content: \"\\f2fe\"; }\n.bi-disc::before { content: \"\\f2ff\"; }\n.bi-discord::before { content: \"\\f300\"; }\n.bi-display-fill::before { content: \"\\f301\"; }\n.bi-display::before { content: \"\\f302\"; }\n.bi-distribute-horizontal::before { content: \"\\f303\"; }\n.bi-distribute-vertical::before { content: \"\\f304\"; }\n.bi-door-closed-fill::before { content: \"\\f305\"; }\n.bi-door-closed::before { content: \"\\f306\"; }\n.bi-door-open-fill::before { content: \"\\f307\"; }\n.bi-door-open::before { content: \"\\f308\"; }\n.bi-dot::before { content: \"\\f309\"; }\n.bi-download::before { content: \"\\f30a\"; }\n.bi-droplet-fill::before { content: \"\\f30b\"; }\n.bi-droplet-half::before { content: \"\\f30c\"; }\n.bi-droplet::before { content: \"\\f30d\"; }\n.bi-earbuds::before { content: \"\\f30e\"; }\n.bi-easel-fill::before { content: \"\\f30f\"; }\n.bi-easel::before { content: \"\\f310\"; }\n.bi-egg-fill::before { content: \"\\f311\"; }\n.bi-egg-fried::before { content: \"\\f312\"; }\n.bi-egg::before { content: \"\\f313\"; }\n.bi-eject-fill::before { content: \"\\f314\"; }\n.bi-eject::before { content: \"\\f315\"; }\n.bi-emoji-angry-fill::before { content: \"\\f316\"; }\n.bi-emoji-angry::before { content: \"\\f317\"; }\n.bi-emoji-dizzy-fill::before { content: \"\\f318\"; }\n.bi-emoji-dizzy::before { content: \"\\f319\"; }\n.bi-emoji-expressionless-fill::before { content: \"\\f31a\"; }\n.bi-emoji-expressionless::before { content: \"\\f31b\"; }\n.bi-emoji-frown-fill::before { content: \"\\f31c\"; }\n.bi-emoji-frown::before { content: \"\\f31d\"; }\n.bi-emoji-heart-eyes-fill::before { content: \"\\f31e\"; }\n.bi-emoji-heart-eyes::before { content: \"\\f31f\"; }\n.bi-emoji-laughing-fill::before { content: \"\\f320\"; }\n.bi-emoji-laughing::before { content: \"\\f321\"; }\n.bi-emoji-neutral-fill::before { content: \"\\f322\"; }\n.bi-emoji-neutral::before { content: \"\\f323\"; }\n.bi-emoji-smile-fill::before { content: \"\\f324\"; }\n.bi-emoji-smile-upside-down-fill::before { content: \"\\f325\"; }\n.bi-emoji-smile-upside-down::before { content: \"\\f326\"; }\n.bi-emoji-smile::before { content: \"\\f327\"; }\n.bi-emoji-sunglasses-fill::before { content: \"\\f328\"; }\n.bi-emoji-sunglasses::before { content: \"\\f329\"; }\n.bi-emoji-wink-fill::before { content: \"\\f32a\"; }\n.bi-emoji-wink::before { content: \"\\f32b\"; }\n.bi-envelope-fill::before { content: \"\\f32c\"; }\n.bi-envelope-open-fill::before { content: \"\\f32d\"; }\n.bi-envelope-open::before { content: \"\\f32e\"; }\n.bi-envelope::before { content: \"\\f32f\"; }\n.bi-eraser-fill::before { content: \"\\f330\"; }\n.bi-eraser::before { content: \"\\f331\"; }\n.bi-exclamation-circle-fill::before { content: \"\\f332\"; }\n.bi-exclamation-circle::before { content: \"\\f333\"; }\n.bi-exclamation-diamond-fill::before { content: \"\\f334\"; }\n.bi-exclamation-diamond::before { content: \"\\f335\"; }\n.bi-exclamation-octagon-fill::before { content: \"\\f336\"; }\n.bi-exclamation-octagon::before { content: \"\\f337\"; }\n.bi-exclamation-square-fill::before { content: \"\\f338\"; }\n.bi-exclamation-square::before { content: \"\\f339\"; }\n.bi-exclamation-triangle-fill::before { content: \"\\f33a\"; }\n.bi-exclamation-triangle::before { content: \"\\f33b\"; }\n.bi-exclamation::before { content: \"\\f33c\"; }\n.bi-exclude::before { content: \"\\f33d\"; }\n.bi-eye-fill::before { content: \"\\f33e\"; }\n.bi-eye-slash-fill::before { content: \"\\f33f\"; }\n.bi-eye-slash::before { content: \"\\f340\"; }\n.bi-eye::before { content: \"\\f341\"; }\n.bi-eyedropper::before { content: \"\\f342\"; }\n.bi-eyeglasses::before { content: \"\\f343\"; }\n.bi-facebook::before { content: \"\\f344\"; }\n.bi-file-arrow-down-fill::before { content: \"\\f345\"; }\n.bi-file-arrow-down::before { content: \"\\f346\"; }\n.bi-file-arrow-up-fill::before { content: \"\\f347\"; }\n.bi-file-arrow-up::before { content: \"\\f348\"; }\n.bi-file-bar-graph-fill::before { content: \"\\f349\"; }\n.bi-file-bar-graph::before { content: \"\\f34a\"; }\n.bi-file-binary-fill::before { content: \"\\f34b\"; }\n.bi-file-binary::before { content: \"\\f34c\"; }\n.bi-file-break-fill::before { content: \"\\f34d\"; }\n.bi-file-break::before { content: \"\\f34e\"; }\n.bi-file-check-fill::before { content: \"\\f34f\"; }\n.bi-file-check::before { content: \"\\f350\"; }\n.bi-file-code-fill::before { content: \"\\f351\"; }\n.bi-file-code::before { content: \"\\f352\"; }\n.bi-file-diff-fill::before { content: \"\\f353\"; }\n.bi-file-diff::before { content: \"\\f354\"; }\n.bi-file-earmark-arrow-down-fill::before { content: \"\\f355\"; }\n.bi-file-earmark-arrow-down::before { content: \"\\f356\"; }\n.bi-file-earmark-arrow-up-fill::before { content: \"\\f357\"; }\n.bi-file-earmark-arrow-up::before { content: \"\\f358\"; }\n.bi-file-earmark-bar-graph-fill::before { content: \"\\f359\"; }\n.bi-file-earmark-bar-graph::before { content: \"\\f35a\"; }\n.bi-file-earmark-binary-fill::before { content: \"\\f35b\"; }\n.bi-file-earmark-binary::before { content: \"\\f35c\"; }\n.bi-file-earmark-break-fill::before { content: \"\\f35d\"; }\n.bi-file-earmark-break::before { content: \"\\f35e\"; }\n.bi-file-earmark-check-fill::before { content: \"\\f35f\"; }\n.bi-file-earmark-check::before { content: \"\\f360\"; }\n.bi-file-earmark-code-fill::before { content: \"\\f361\"; }\n.bi-file-earmark-code::before { content: \"\\f362\"; }\n.bi-file-earmark-diff-fill::before { content: \"\\f363\"; }\n.bi-file-earmark-diff::before { content: \"\\f364\"; }\n.bi-file-earmark-easel-fill::before { content: \"\\f365\"; }\n.bi-file-earmark-easel::before { content: \"\\f366\"; }\n.bi-file-earmark-excel-fill::before { content: \"\\f367\"; }\n.bi-file-earmark-excel::before { content: \"\\f368\"; }\n.bi-file-earmark-fill::before { content: \"\\f369\"; }\n.bi-file-earmark-font-fill::before { content: \"\\f36a\"; }\n.bi-file-earmark-font::before { content: \"\\f36b\"; }\n.bi-file-earmark-image-fill::before { content: \"\\f36c\"; }\n.bi-file-earmark-image::before { content: \"\\f36d\"; }\n.bi-file-earmark-lock-fill::before { content: \"\\f36e\"; }\n.bi-file-earmark-lock::before { content: \"\\f36f\"; }\n.bi-file-earmark-lock2-fill::before { content: \"\\f370\"; }\n.bi-file-earmark-lock2::before { content: \"\\f371\"; }\n.bi-file-earmark-medical-fill::before { content: \"\\f372\"; }\n.bi-file-earmark-medical::before { content: \"\\f373\"; }\n.bi-file-earmark-minus-fill::before { content: \"\\f374\"; }\n.bi-file-earmark-minus::before { content: \"\\f375\"; }\n.bi-file-earmark-music-fill::before { content: \"\\f376\"; }\n.bi-file-earmark-music::before { content: \"\\f377\"; }\n.bi-file-earmark-person-fill::before { content: \"\\f378\"; }\n.bi-file-earmark-person::before { content: \"\\f379\"; }\n.bi-file-earmark-play-fill::before { content: \"\\f37a\"; }\n.bi-file-earmark-play::before { content: \"\\f37b\"; }\n.bi-file-earmark-plus-fill::before { content: \"\\f37c\"; }\n.bi-file-earmark-plus::before { content: \"\\f37d\"; }\n.bi-file-earmark-post-fill::before { content: \"\\f37e\"; }\n.bi-file-earmark-post::before { content: \"\\f37f\"; }\n.bi-file-earmark-ppt-fill::before { content: \"\\f380\"; }\n.bi-file-earmark-ppt::before { content: \"\\f381\"; }\n.bi-file-earmark-richtext-fill::before { content: \"\\f382\"; }\n.bi-file-earmark-richtext::before { content: \"\\f383\"; }\n.bi-file-earmark-ruled-fill::before { content: \"\\f384\"; }\n.bi-file-earmark-ruled::before { content: \"\\f385\"; }\n.bi-file-earmark-slides-fill::before { content: \"\\f386\"; }\n.bi-file-earmark-slides::before { content: \"\\f387\"; }\n.bi-file-earmark-spreadsheet-fill::before { content: \"\\f388\"; }\n.bi-file-earmark-spreadsheet::before { content: \"\\f389\"; }\n.bi-file-earmark-text-fill::before { content: \"\\f38a\"; }\n.bi-file-earmark-text::before { content: \"\\f38b\"; }\n.bi-file-earmark-word-fill::before { content: \"\\f38c\"; }\n.bi-file-earmark-word::before { content: \"\\f38d\"; }\n.bi-file-earmark-x-fill::before { content: \"\\f38e\"; }\n.bi-file-earmark-x::before { content: \"\\f38f\"; }\n.bi-file-earmark-zip-fill::before { content: \"\\f390\"; }\n.bi-file-earmark-zip::before { content: \"\\f391\"; }\n.bi-file-earmark::before { content: \"\\f392\"; }\n.bi-file-easel-fill::before { content: \"\\f393\"; }\n.bi-file-easel::before { content: \"\\f394\"; }\n.bi-file-excel-fill::before { content: \"\\f395\"; }\n.bi-file-excel::before { content: \"\\f396\"; }\n.bi-file-fill::before { content: \"\\f397\"; }\n.bi-file-font-fill::before { content: \"\\f398\"; }\n.bi-file-font::before { content: \"\\f399\"; }\n.bi-file-image-fill::before { content: \"\\f39a\"; }\n.bi-file-image::before { content: \"\\f39b\"; }\n.bi-file-lock-fill::before { content: \"\\f39c\"; }\n.bi-file-lock::before { content: \"\\f39d\"; }\n.bi-file-lock2-fill::before { content: \"\\f39e\"; }\n.bi-file-lock2::before { content: \"\\f39f\"; }\n.bi-file-medical-fill::before { content: \"\\f3a0\"; }\n.bi-file-medical::before { content: \"\\f3a1\"; }\n.bi-file-minus-fill::before { content: \"\\f3a2\"; }\n.bi-file-minus::before { content: \"\\f3a3\"; }\n.bi-file-music-fill::before { content: \"\\f3a4\"; }\n.bi-file-music::before { content: \"\\f3a5\"; }\n.bi-file-person-fill::before { content: \"\\f3a6\"; }\n.bi-file-person::before { content: \"\\f3a7\"; }\n.bi-file-play-fill::before { content: \"\\f3a8\"; }\n.bi-file-play::before { content: \"\\f3a9\"; }\n.bi-file-plus-fill::before { content: \"\\f3aa\"; }\n.bi-file-plus::before { content: \"\\f3ab\"; }\n.bi-file-post-fill::before { content: \"\\f3ac\"; }\n.bi-file-post::before { content: \"\\f3ad\"; }\n.bi-file-ppt-fill::before { content: \"\\f3ae\"; }\n.bi-file-ppt::before { content: \"\\f3af\"; }\n.bi-file-richtext-fill::before { content: \"\\f3b0\"; }\n.bi-file-richtext::before { content: \"\\f3b1\"; }\n.bi-file-ruled-fill::before { content: \"\\f3b2\"; }\n.bi-file-ruled::before { content: \"\\f3b3\"; }\n.bi-file-slides-fill::before { content: \"\\f3b4\"; }\n.bi-file-slides::before { content: \"\\f3b5\"; }\n.bi-file-spreadsheet-fill::before { content: \"\\f3b6\"; }\n.bi-file-spreadsheet::before { content: \"\\f3b7\"; }\n.bi-file-text-fill::before { content: \"\\f3b8\"; }\n.bi-file-text::before { content: \"\\f3b9\"; }\n.bi-file-word-fill::before { content: \"\\f3ba\"; }\n.bi-file-word::before { content: \"\\f3bb\"; }\n.bi-file-x-fill::before { content: \"\\f3bc\"; }\n.bi-file-x::before { content: \"\\f3bd\"; }\n.bi-file-zip-fill::before { content: \"\\f3be\"; }\n.bi-file-zip::before { content: \"\\f3bf\"; }\n.bi-file::before { content: \"\\f3c0\"; }\n.bi-files-alt::before { content: \"\\f3c1\"; }\n.bi-files::before { content: \"\\f3c2\"; }\n.bi-film::before { content: \"\\f3c3\"; }\n.bi-filter-circle-fill::before { content: \"\\f3c4\"; }\n.bi-filter-circle::before { content: \"\\f3c5\"; }\n.bi-filter-left::before { content: \"\\f3c6\"; }\n.bi-filter-right::before { content: \"\\f3c7\"; }\n.bi-filter-square-fill::before { content: \"\\f3c8\"; }\n.bi-filter-square::before { content: \"\\f3c9\"; }\n.bi-filter::before { content: \"\\f3ca\"; }\n.bi-flag-fill::before { content: \"\\f3cb\"; }\n.bi-flag::before { content: \"\\f3cc\"; }\n.bi-flower1::before { content: \"\\f3cd\"; }\n.bi-flower2::before { content: \"\\f3ce\"; }\n.bi-flower3::before { content: \"\\f3cf\"; }\n.bi-folder-check::before { content: \"\\f3d0\"; }\n.bi-folder-fill::before { content: \"\\f3d1\"; }\n.bi-folder-minus::before { content: \"\\f3d2\"; }\n.bi-folder-plus::before { content: \"\\f3d3\"; }\n.bi-folder-symlink-fill::before { content: \"\\f3d4\"; }\n.bi-folder-symlink::before { content: \"\\f3d5\"; }\n.bi-folder-x::before { content: \"\\f3d6\"; }\n.bi-folder::before { content: \"\\f3d7\"; }\n.bi-folder2-open::before { content: \"\\f3d8\"; }\n.bi-folder2::before { content: \"\\f3d9\"; }\n.bi-fonts::before { content: \"\\f3da\"; }\n.bi-forward-fill::before { content: \"\\f3db\"; }\n.bi-forward::before { content: \"\\f3dc\"; }\n.bi-front::before { content: \"\\f3dd\"; }\n.bi-fullscreen-exit::before { content: \"\\f3de\"; }\n.bi-fullscreen::before { content: \"\\f3df\"; }\n.bi-funnel-fill::before { content: \"\\f3e0\"; }\n.bi-funnel::before { content: \"\\f3e1\"; }\n.bi-gear-fill::before { content: \"\\f3e2\"; }\n.bi-gear-wide-connected::before { content: \"\\f3e3\"; }\n.bi-gear-wide::before { content: \"\\f3e4\"; }\n.bi-gear::before { content: \"\\f3e5\"; }\n.bi-gem::before { content: \"\\f3e6\"; }\n.bi-geo-alt-fill::before { content: \"\\f3e7\"; }\n.bi-geo-alt::before { content: \"\\f3e8\"; }\n.bi-geo-fill::before { content: \"\\f3e9\"; }\n.bi-geo::before { content: \"\\f3ea\"; }\n.bi-gift-fill::before { content: \"\\f3eb\"; }\n.bi-gift::before { content: \"\\f3ec\"; }\n.bi-github::before { content: \"\\f3ed\"; }\n.bi-globe::before { content: \"\\f3ee\"; }\n.bi-globe2::before { content: \"\\f3ef\"; }\n.bi-google::before { content: \"\\f3f0\"; }\n.bi-graph-down::before { content: \"\\f3f1\"; }\n.bi-graph-up::before { content: \"\\f3f2\"; }\n.bi-grid-1x2-fill::before { content: \"\\f3f3\"; }\n.bi-grid-1x2::before { content: \"\\f3f4\"; }\n.bi-grid-3x2-gap-fill::before { content: \"\\f3f5\"; }\n.bi-grid-3x2-gap::before { content: \"\\f3f6\"; }\n.bi-grid-3x2::before { content: \"\\f3f7\"; }\n.bi-grid-3x3-gap-fill::before { content: \"\\f3f8\"; }\n.bi-grid-3x3-gap::before { content: \"\\f3f9\"; }\n.bi-grid-3x3::before { content: \"\\f3fa\"; }\n.bi-grid-fill::before { content: \"\\f3fb\"; }\n.bi-grid::before { content: \"\\f3fc\"; }\n.bi-grip-horizontal::before { content: \"\\f3fd\"; }\n.bi-grip-vertical::before { content: \"\\f3fe\"; }\n.bi-hammer::before { content: \"\\f3ff\"; }\n.bi-hand-index-fill::before { content: \"\\f400\"; }\n.bi-hand-index-thumb-fill::before { content: \"\\f401\"; }\n.bi-hand-index-thumb::before { content: \"\\f402\"; }\n.bi-hand-index::before { content: \"\\f403\"; }\n.bi-hand-thumbs-down-fill::before { content: \"\\f404\"; }\n.bi-hand-thumbs-down::before { content: \"\\f405\"; }\n.bi-hand-thumbs-up-fill::before { content: \"\\f406\"; }\n.bi-hand-thumbs-up::before { content: \"\\f407\"; }\n.bi-handbag-fill::before { content: \"\\f408\"; }\n.bi-handbag::before { content: \"\\f409\"; }\n.bi-hash::before { content: \"\\f40a\"; }\n.bi-hdd-fill::before { content: \"\\f40b\"; }\n.bi-hdd-network-fill::before { content: \"\\f40c\"; }\n.bi-hdd-network::before { content: \"\\f40d\"; }\n.bi-hdd-rack-fill::before { content: \"\\f40e\"; }\n.bi-hdd-rack::before { content: \"\\f40f\"; }\n.bi-hdd-stack-fill::before { content: \"\\f410\"; }\n.bi-hdd-stack::before { content: \"\\f411\"; }\n.bi-hdd::before { content: \"\\f412\"; }\n.bi-headphones::before { content: \"\\f413\"; }\n.bi-headset::before { content: \"\\f414\"; }\n.bi-heart-fill::before { content: \"\\f415\"; }\n.bi-heart-half::before { content: \"\\f416\"; }\n.bi-heart::before { content: \"\\f417\"; }\n.bi-heptagon-fill::before { content: \"\\f418\"; }\n.bi-heptagon-half::before { content: \"\\f419\"; }\n.bi-heptagon::before { content: \"\\f41a\"; }\n.bi-hexagon-fill::before { content: \"\\f41b\"; }\n.bi-hexagon-half::before { content: \"\\f41c\"; }\n.bi-hexagon::before { content: \"\\f41d\"; }\n.bi-hourglass-bottom::before { content: \"\\f41e\"; }\n.bi-hourglass-split::before { content: \"\\f41f\"; }\n.bi-hourglass-top::before { content: \"\\f420\"; }\n.bi-hourglass::before { content: \"\\f421\"; }\n.bi-house-door-fill::before { content: \"\\f422\"; }\n.bi-house-door::before { content: \"\\f423\"; }\n.bi-house-fill::before { content: \"\\f424\"; }\n.bi-house::before { content: \"\\f425\"; }\n.bi-hr::before { content: \"\\f426\"; }\n.bi-hurricane::before { content: \"\\f427\"; }\n.bi-image-alt::before { content: \"\\f428\"; }\n.bi-image-fill::before { content: \"\\f429\"; }\n.bi-image::before { content: \"\\f42a\"; }\n.bi-images::before { content: \"\\f42b\"; }\n.bi-inbox-fill::before { content: \"\\f42c\"; }\n.bi-inbox::before { content: \"\\f42d\"; }\n.bi-inboxes-fill::before { content: \"\\f42e\"; }\n.bi-inboxes::before { content: \"\\f42f\"; }\n.bi-info-circle-fill::before { content: \"\\f430\"; }\n.bi-info-circle::before { content: \"\\f431\"; }\n.bi-info-square-fill::before { content: \"\\f432\"; }\n.bi-info-square::before { content: \"\\f433\"; }\n.bi-info::before { content: \"\\f434\"; }\n.bi-input-cursor-text::before { content: \"\\f435\"; }\n.bi-input-cursor::before { content: \"\\f436\"; }\n.bi-instagram::before { content: \"\\f437\"; }\n.bi-intersect::before { content: \"\\f438\"; }\n.bi-journal-album::before { content: \"\\f439\"; }\n.bi-journal-arrow-down::before { content: \"\\f43a\"; }\n.bi-journal-arrow-up::before { content: \"\\f43b\"; }\n.bi-journal-bookmark-fill::before { content: \"\\f43c\"; }\n.bi-journal-bookmark::before { content: \"\\f43d\"; }\n.bi-journal-check::before { content: \"\\f43e\"; }\n.bi-journal-code::before { content: \"\\f43f\"; }\n.bi-journal-medical::before { content: \"\\f440\"; }\n.bi-journal-minus::before { content: \"\\f441\"; }\n.bi-journal-plus::before { content: \"\\f442\"; }\n.bi-journal-richtext::before { content: \"\\f443\"; }\n.bi-journal-text::before { content: \"\\f444\"; }\n.bi-journal-x::before { content: \"\\f445\"; }\n.bi-journal::before { content: \"\\f446\"; }\n.bi-journals::before { content: \"\\f447\"; }\n.bi-joystick::before { content: \"\\f448\"; }\n.bi-justify-left::before { content: \"\\f449\"; }\n.bi-justify-right::before { content: \"\\f44a\"; }\n.bi-justify::before { content: \"\\f44b\"; }\n.bi-kanban-fill::before { content: \"\\f44c\"; }\n.bi-kanban::before { content: \"\\f44d\"; }\n.bi-key-fill::before { content: \"\\f44e\"; }\n.bi-key::before { content: \"\\f44f\"; }\n.bi-keyboard-fill::before { content: \"\\f450\"; }\n.bi-keyboard::before { content: \"\\f451\"; }\n.bi-ladder::before { content: \"\\f452\"; }\n.bi-lamp-fill::before { content: \"\\f453\"; }\n.bi-lamp::before { content: \"\\f454\"; }\n.bi-laptop-fill::before { content: \"\\f455\"; }\n.bi-laptop::before { content: \"\\f456\"; }\n.bi-layer-backward::before { content: \"\\f457\"; }\n.bi-layer-forward::before { content: \"\\f458\"; }\n.bi-layers-fill::before { content: \"\\f459\"; }\n.bi-layers-half::before { content: \"\\f45a\"; }\n.bi-layers::before { content: \"\\f45b\"; }\n.bi-layout-sidebar-inset-reverse::before { content: \"\\f45c\"; }\n.bi-layout-sidebar-inset::before { content: \"\\f45d\"; }\n.bi-layout-sidebar-reverse::before { content: \"\\f45e\"; }\n.bi-layout-sidebar::before { content: \"\\f45f\"; }\n.bi-layout-split::before { content: \"\\f460\"; }\n.bi-layout-text-sidebar-reverse::before { content: \"\\f461\"; }\n.bi-layout-text-sidebar::before { content: \"\\f462\"; }\n.bi-layout-text-window-reverse::before { content: \"\\f463\"; }\n.bi-layout-text-window::before { content: \"\\f464\"; }\n.bi-layout-three-columns::before { content: \"\\f465\"; }\n.bi-layout-wtf::before { content: \"\\f466\"; }\n.bi-life-preserver::before { content: \"\\f467\"; }\n.bi-lightbulb-fill::before { content: \"\\f468\"; }\n.bi-lightbulb-off-fill::before { content: \"\\f469\"; }\n.bi-lightbulb-off::before { content: \"\\f46a\"; }\n.bi-lightbulb::before { content: \"\\f46b\"; }\n.bi-lightning-charge-fill::before { content: \"\\f46c\"; }\n.bi-lightning-charge::before { content: \"\\f46d\"; }\n.bi-lightning-fill::before { content: \"\\f46e\"; }\n.bi-lightning::before { content: \"\\f46f\"; }\n.bi-link-45deg::before { content: \"\\f470\"; }\n.bi-link::before { content: \"\\f471\"; }\n.bi-linkedin::before { content: \"\\f472\"; }\n.bi-list-check::before { content: \"\\f473\"; }\n.bi-list-nested::before { content: \"\\f474\"; }\n.bi-list-ol::before { content: \"\\f475\"; }\n.bi-list-stars::before { content: \"\\f476\"; }\n.bi-list-task::before { content: \"\\f477\"; }\n.bi-list-ul::before { content: \"\\f478\"; }\n.bi-list::before { content: \"\\f479\"; }\n.bi-lock-fill::before { content: \"\\f47a\"; }\n.bi-lock::before { content: \"\\f47b\"; }\n.bi-mailbox::before { content: \"\\f47c\"; }\n.bi-mailbox2::before { content: \"\\f47d\"; }\n.bi-map-fill::before { content: \"\\f47e\"; }\n.bi-map::before { content: \"\\f47f\"; }\n.bi-markdown-fill::before { content: \"\\f480\"; }\n.bi-markdown::before { content: \"\\f481\"; }\n.bi-mask::before { content: \"\\f482\"; }\n.bi-megaphone-fill::before { content: \"\\f483\"; }\n.bi-megaphone::before { content: \"\\f484\"; }\n.bi-menu-app-fill::before { content: \"\\f485\"; }\n.bi-menu-app::before { content: \"\\f486\"; }\n.bi-menu-button-fill::before { content: \"\\f487\"; }\n.bi-menu-button-wide-fill::before { content: \"\\f488\"; }\n.bi-menu-button-wide::before { content: \"\\f489\"; }\n.bi-menu-button::before { content: \"\\f48a\"; }\n.bi-menu-down::before { content: \"\\f48b\"; }\n.bi-menu-up::before { content: \"\\f48c\"; }\n.bi-mic-fill::before { content: \"\\f48d\"; }\n.bi-mic-mute-fill::before { content: \"\\f48e\"; }\n.bi-mic-mute::before { content: \"\\f48f\"; }\n.bi-mic::before { content: \"\\f490\"; }\n.bi-minecart-loaded::before { content: \"\\f491\"; }\n.bi-minecart::before { content: \"\\f492\"; }\n.bi-moisture::before { content: \"\\f493\"; }\n.bi-moon-fill::before { content: \"\\f494\"; }\n.bi-moon-stars-fill::before { content: \"\\f495\"; }\n.bi-moon-stars::before { content: \"\\f496\"; }\n.bi-moon::before { content: \"\\f497\"; }\n.bi-mouse-fill::before { content: \"\\f498\"; }\n.bi-mouse::before { content: \"\\f499\"; }\n.bi-mouse2-fill::before { content: \"\\f49a\"; }\n.bi-mouse2::before { content: \"\\f49b\"; }\n.bi-mouse3-fill::before { content: \"\\f49c\"; }\n.bi-mouse3::before { content: \"\\f49d\"; }\n.bi-music-note-beamed::before { content: \"\\f49e\"; }\n.bi-music-note-list::before { content: \"\\f49f\"; }\n.bi-music-note::before { content: \"\\f4a0\"; }\n.bi-music-player-fill::before { content: \"\\f4a1\"; }\n.bi-music-player::before { content: \"\\f4a2\"; }\n.bi-newspaper::before { content: \"\\f4a3\"; }\n.bi-node-minus-fill::before { content: \"\\f4a4\"; }\n.bi-node-minus::before { content: \"\\f4a5\"; }\n.bi-node-plus-fill::before { content: \"\\f4a6\"; }\n.bi-node-plus::before { content: \"\\f4a7\"; }\n.bi-nut-fill::before { content: \"\\f4a8\"; }\n.bi-nut::before { content: \"\\f4a9\"; }\n.bi-octagon-fill::before { content: \"\\f4aa\"; }\n.bi-octagon-half::before { content: \"\\f4ab\"; }\n.bi-octagon::before { content: \"\\f4ac\"; }\n.bi-option::before { content: \"\\f4ad\"; }\n.bi-outlet::before { content: \"\\f4ae\"; }\n.bi-paint-bucket::before { content: \"\\f4af\"; }\n.bi-palette-fill::before { content: \"\\f4b0\"; }\n.bi-palette::before { content: \"\\f4b1\"; }\n.bi-palette2::before { content: \"\\f4b2\"; }\n.bi-paperclip::before { content: \"\\f4b3\"; }\n.bi-paragraph::before { content: \"\\f4b4\"; }\n.bi-patch-check-fill::before { content: \"\\f4b5\"; }\n.bi-patch-check::before { content: \"\\f4b6\"; }\n.bi-patch-exclamation-fill::before { content: \"\\f4b7\"; }\n.bi-patch-exclamation::before { content: \"\\f4b8\"; }\n.bi-patch-minus-fill::before { content: \"\\f4b9\"; }\n.bi-patch-minus::before { content: \"\\f4ba\"; }\n.bi-patch-plus-fill::before { content: \"\\f4bb\"; }\n.bi-patch-plus::before { content: \"\\f4bc\"; }\n.bi-patch-question-fill::before { content: \"\\f4bd\"; }\n.bi-patch-question::before { content: \"\\f4be\"; }\n.bi-pause-btn-fill::before { content: \"\\f4bf\"; }\n.bi-pause-btn::before { content: \"\\f4c0\"; }\n.bi-pause-circle-fill::before { content: \"\\f4c1\"; }\n.bi-pause-circle::before { content: \"\\f4c2\"; }\n.bi-pause-fill::before { content: \"\\f4c3\"; }\n.bi-pause::before { content: \"\\f4c4\"; }\n.bi-peace-fill::before { content: \"\\f4c5\"; }\n.bi-peace::before { content: \"\\f4c6\"; }\n.bi-pen-fill::before { content: \"\\f4c7\"; }\n.bi-pen::before { content: \"\\f4c8\"; }\n.bi-pencil-fill::before { content: \"\\f4c9\"; }\n.bi-pencil-square::before { content: \"\\f4ca\"; }\n.bi-pencil::before { content: \"\\f4cb\"; }\n.bi-pentagon-fill::before { content: \"\\f4cc\"; }\n.bi-pentagon-half::before { content: \"\\f4cd\"; }\n.bi-pentagon::before { content: \"\\f4ce\"; }\n.bi-people-fill::before { content: \"\\f4cf\"; }\n.bi-people::before { content: \"\\f4d0\"; }\n.bi-percent::before { content: \"\\f4d1\"; }\n.bi-person-badge-fill::before { content: \"\\f4d2\"; }\n.bi-person-badge::before { content: \"\\f4d3\"; }\n.bi-person-bounding-box::before { content: \"\\f4d4\"; }\n.bi-person-check-fill::before { content: \"\\f4d5\"; }\n.bi-person-check::before { content: \"\\f4d6\"; }\n.bi-person-circle::before { content: \"\\f4d7\"; }\n.bi-person-dash-fill::before { content: \"\\f4d8\"; }\n.bi-person-dash::before { content: \"\\f4d9\"; }\n.bi-person-fill::before { content: \"\\f4da\"; }\n.bi-person-lines-fill::before { content: \"\\f4db\"; }\n.bi-person-plus-fill::before { content: \"\\f4dc\"; }\n.bi-person-plus::before { content: \"\\f4dd\"; }\n.bi-person-square::before { content: \"\\f4de\"; }\n.bi-person-x-fill::before { content: \"\\f4df\"; }\n.bi-person-x::before { content: \"\\f4e0\"; }\n.bi-person::before { content: \"\\f4e1\"; }\n.bi-phone-fill::before { content: \"\\f4e2\"; }\n.bi-phone-landscape-fill::before { content: \"\\f4e3\"; }\n.bi-phone-landscape::before { content: \"\\f4e4\"; }\n.bi-phone-vibrate-fill::before { content: \"\\f4e5\"; }\n.bi-phone-vibrate::before { content: \"\\f4e6\"; }\n.bi-phone::before { content: \"\\f4e7\"; }\n.bi-pie-chart-fill::before { content: \"\\f4e8\"; }\n.bi-pie-chart::before { content: \"\\f4e9\"; }\n.bi-pin-angle-fill::before { content: \"\\f4ea\"; }\n.bi-pin-angle::before { content: \"\\f4eb\"; }\n.bi-pin-fill::before { content: \"\\f4ec\"; }\n.bi-pin::before { content: \"\\f4ed\"; }\n.bi-pip-fill::before { content: \"\\f4ee\"; }\n.bi-pip::before { content: \"\\f4ef\"; }\n.bi-play-btn-fill::before { content: \"\\f4f0\"; }\n.bi-play-btn::before { content: \"\\f4f1\"; }\n.bi-play-circle-fill::before { content: \"\\f4f2\"; }\n.bi-play-circle::before { content: \"\\f4f3\"; }\n.bi-play-fill::before { content: \"\\f4f4\"; }\n.bi-play::before { content: \"\\f4f5\"; }\n.bi-plug-fill::before { content: \"\\f4f6\"; }\n.bi-plug::before { content: \"\\f4f7\"; }\n.bi-plus-circle-dotted::before { content: \"\\f4f8\"; }\n.bi-plus-circle-fill::before { content: \"\\f4f9\"; }\n.bi-plus-circle::before { content: \"\\f4fa\"; }\n.bi-plus-square-dotted::before { content: \"\\f4fb\"; }\n.bi-plus-square-fill::before { content: \"\\f4fc\"; }\n.bi-plus-square::before { content: \"\\f4fd\"; }\n.bi-plus::before { content: \"\\f4fe\"; }\n.bi-power::before { content: \"\\f4ff\"; }\n.bi-printer-fill::before { content: \"\\f500\"; }\n.bi-printer::before { content: \"\\f501\"; }\n.bi-puzzle-fill::before { content: \"\\f502\"; }\n.bi-puzzle::before { content: \"\\f503\"; }\n.bi-question-circle-fill::before { content: \"\\f504\"; }\n.bi-question-circle::before { content: \"\\f505\"; }\n.bi-question-diamond-fill::before { content: \"\\f506\"; }\n.bi-question-diamond::before { content: \"\\f507\"; }\n.bi-question-octagon-fill::before { content: \"\\f508\"; }\n.bi-question-octagon::before { content: \"\\f509\"; }\n.bi-question-square-fill::before { content: \"\\f50a\"; }\n.bi-question-square::before { content: \"\\f50b\"; }\n.bi-question::before { content: \"\\f50c\"; }\n.bi-rainbow::before { content: \"\\f50d\"; }\n.bi-receipt-cutoff::before { content: \"\\f50e\"; }\n.bi-receipt::before { content: \"\\f50f\"; }\n.bi-reception-0::before { content: \"\\f510\"; }\n.bi-reception-1::before { content: \"\\f511\"; }\n.bi-reception-2::before { content: \"\\f512\"; }\n.bi-reception-3::before { content: \"\\f513\"; }\n.bi-reception-4::before { content: \"\\f514\"; }\n.bi-record-btn-fill::before { content: \"\\f515\"; }\n.bi-record-btn::before { content: \"\\f516\"; }\n.bi-record-circle-fill::before { content: \"\\f517\"; }\n.bi-record-circle::before { content: \"\\f518\"; }\n.bi-record-fill::before { content: \"\\f519\"; }\n.bi-record::before { content: \"\\f51a\"; }\n.bi-record2-fill::before { content: \"\\f51b\"; }\n.bi-record2::before { content: \"\\f51c\"; }\n.bi-reply-all-fill::before { content: \"\\f51d\"; }\n.bi-reply-all::before { content: \"\\f51e\"; }\n.bi-reply-fill::before { content: \"\\f51f\"; }\n.bi-reply::before { content: \"\\f520\"; }\n.bi-rss-fill::before { content: \"\\f521\"; }\n.bi-rss::before { content: \"\\f522\"; }\n.bi-rulers::before { content: \"\\f523\"; }\n.bi-save-fill::before { content: \"\\f524\"; }\n.bi-save::before { content: \"\\f525\"; }\n.bi-save2-fill::before { content: \"\\f526\"; }\n.bi-save2::before { content: \"\\f527\"; }\n.bi-scissors::before { content: \"\\f528\"; }\n.bi-screwdriver::before { content: \"\\f529\"; }\n.bi-search::before { content: \"\\f52a\"; }\n.bi-segmented-nav::before { content: \"\\f52b\"; }\n.bi-server::before { content: \"\\f52c\"; }\n.bi-share-fill::before { content: \"\\f52d\"; }\n.bi-share::before { content: \"\\f52e\"; }\n.bi-shield-check::before { content: \"\\f52f\"; }\n.bi-shield-exclamation::before { content: \"\\f530\"; }\n.bi-shield-fill-check::before { content: \"\\f531\"; }\n.bi-shield-fill-exclamation::before { content: \"\\f532\"; }\n.bi-shield-fill-minus::before { content: \"\\f533\"; }\n.bi-shield-fill-plus::before { content: \"\\f534\"; }\n.bi-shield-fill-x::before { content: \"\\f535\"; }\n.bi-shield-fill::before { content: \"\\f536\"; }\n.bi-shield-lock-fill::before { content: \"\\f537\"; }\n.bi-shield-lock::before { content: \"\\f538\"; }\n.bi-shield-minus::before { content: \"\\f539\"; }\n.bi-shield-plus::before { content: \"\\f53a\"; }\n.bi-shield-shaded::before { content: \"\\f53b\"; }\n.bi-shield-slash-fill::before { content: \"\\f53c\"; }\n.bi-shield-slash::before { content: \"\\f53d\"; }\n.bi-shield-x::before { content: \"\\f53e\"; }\n.bi-shield::before { content: \"\\f53f\"; }\n.bi-shift-fill::before { content: \"\\f540\"; }\n.bi-shift::before { content: \"\\f541\"; }\n.bi-shop-window::before { content: \"\\f542\"; }\n.bi-shop::before { content: \"\\f543\"; }\n.bi-shuffle::before { content: \"\\f544\"; }\n.bi-signpost-2-fill::before { content: \"\\f545\"; }\n.bi-signpost-2::before { content: \"\\f546\"; }\n.bi-signpost-fill::before { content: \"\\f547\"; }\n.bi-signpost-split-fill::before { content: \"\\f548\"; }\n.bi-signpost-split::before { content: \"\\f549\"; }\n.bi-signpost::before { content: \"\\f54a\"; }\n.bi-sim-fill::before { content: \"\\f54b\"; }\n.bi-sim::before { content: \"\\f54c\"; }\n.bi-skip-backward-btn-fill::before { content: \"\\f54d\"; }\n.bi-skip-backward-btn::before { content: \"\\f54e\"; }\n.bi-skip-backward-circle-fill::before { content: \"\\f54f\"; }\n.bi-skip-backward-circle::before { content: \"\\f550\"; }\n.bi-skip-backward-fill::before { content: \"\\f551\"; }\n.bi-skip-backward::before { content: \"\\f552\"; }\n.bi-skip-end-btn-fill::before { content: \"\\f553\"; }\n.bi-skip-end-btn::before { content: \"\\f554\"; }\n.bi-skip-end-circle-fill::before { content: \"\\f555\"; }\n.bi-skip-end-circle::before { content: \"\\f556\"; }\n.bi-skip-end-fill::before { content: \"\\f557\"; }\n.bi-skip-end::before { content: \"\\f558\"; }\n.bi-skip-forward-btn-fill::before { content: \"\\f559\"; }\n.bi-skip-forward-btn::before { content: \"\\f55a\"; }\n.bi-skip-forward-circle-fill::before { content: \"\\f55b\"; }\n.bi-skip-forward-circle::before { content: \"\\f55c\"; }\n.bi-skip-forward-fill::before { content: \"\\f55d\"; }\n.bi-skip-forward::before { content: \"\\f55e\"; }\n.bi-skip-start-btn-fill::before { content: \"\\f55f\"; }\n.bi-skip-start-btn::before { content: \"\\f560\"; }\n.bi-skip-start-circle-fill::before { content: \"\\f561\"; }\n.bi-skip-start-circle::before { content: \"\\f562\"; }\n.bi-skip-start-fill::before { content: \"\\f563\"; }\n.bi-skip-start::before { content: \"\\f564\"; }\n.bi-slack::before { content: \"\\f565\"; }\n.bi-slash-circle-fill::before { content: \"\\f566\"; }\n.bi-slash-circle::before { content: \"\\f567\"; }\n.bi-slash-square-fill::before { content: \"\\f568\"; }\n.bi-slash-square::before { content: \"\\f569\"; }\n.bi-slash::before { content: \"\\f56a\"; }\n.bi-sliders::before { content: \"\\f56b\"; }\n.bi-smartwatch::before { content: \"\\f56c\"; }\n.bi-snow::before { content: \"\\f56d\"; }\n.bi-snow2::before { content: \"\\f56e\"; }\n.bi-snow3::before { content: \"\\f56f\"; }\n.bi-sort-alpha-down-alt::before { content: \"\\f570\"; }\n.bi-sort-alpha-down::before { content: \"\\f571\"; }\n.bi-sort-alpha-up-alt::before { content: \"\\f572\"; }\n.bi-sort-alpha-up::before { content: \"\\f573\"; }\n.bi-sort-down-alt::before { content: \"\\f574\"; }\n.bi-sort-down::before { content: \"\\f575\"; }\n.bi-sort-numeric-down-alt::before { content: \"\\f576\"; }\n.bi-sort-numeric-down::before { content: \"\\f577\"; }\n.bi-sort-numeric-up-alt::before { content: \"\\f578\"; }\n.bi-sort-numeric-up::before { content: \"\\f579\"; }\n.bi-sort-up-alt::before { content: \"\\f57a\"; }\n.bi-sort-up::before { content: \"\\f57b\"; }\n.bi-soundwave::before { content: \"\\f57c\"; }\n.bi-speaker-fill::before { content: \"\\f57d\"; }\n.bi-speaker::before { content: \"\\f57e\"; }\n.bi-speedometer::before { content: \"\\f57f\"; }\n.bi-speedometer2::before { content: \"\\f580\"; }\n.bi-spellcheck::before { content: \"\\f581\"; }\n.bi-square-fill::before { content: \"\\f582\"; }\n.bi-square-half::before { content: \"\\f583\"; }\n.bi-square::before { content: \"\\f584\"; }\n.bi-stack::before { content: \"\\f585\"; }\n.bi-star-fill::before { content: \"\\f586\"; }\n.bi-star-half::before { content: \"\\f587\"; }\n.bi-star::before { content: \"\\f588\"; }\n.bi-stars::before { content: \"\\f589\"; }\n.bi-stickies-fill::before { content: \"\\f58a\"; }\n.bi-stickies::before { content: \"\\f58b\"; }\n.bi-sticky-fill::before { content: \"\\f58c\"; }\n.bi-sticky::before { content: \"\\f58d\"; }\n.bi-stop-btn-fill::before { content: \"\\f58e\"; }\n.bi-stop-btn::before { content: \"\\f58f\"; }\n.bi-stop-circle-fill::before { content: \"\\f590\"; }\n.bi-stop-circle::before { content: \"\\f591\"; }\n.bi-stop-fill::before { content: \"\\f592\"; }\n.bi-stop::before { content: \"\\f593\"; }\n.bi-stoplights-fill::before { content: \"\\f594\"; }\n.bi-stoplights::before { content: \"\\f595\"; }\n.bi-stopwatch-fill::before { content: \"\\f596\"; }\n.bi-stopwatch::before { content: \"\\f597\"; }\n.bi-subtract::before { content: \"\\f598\"; }\n.bi-suit-club-fill::before { content: \"\\f599\"; }\n.bi-suit-club::before { content: \"\\f59a\"; }\n.bi-suit-diamond-fill::before { content: \"\\f59b\"; }\n.bi-suit-diamond::before { content: \"\\f59c\"; }\n.bi-suit-heart-fill::before { content: \"\\f59d\"; }\n.bi-suit-heart::before { content: \"\\f59e\"; }\n.bi-suit-spade-fill::before { content: \"\\f59f\"; }\n.bi-suit-spade::before { content: \"\\f5a0\"; }\n.bi-sun-fill::before { content: \"\\f5a1\"; }\n.bi-sun::before { content: \"\\f5a2\"; }\n.bi-sunglasses::before { content: \"\\f5a3\"; }\n.bi-sunrise-fill::before { content: \"\\f5a4\"; }\n.bi-sunrise::before { content: \"\\f5a5\"; }\n.bi-sunset-fill::before { content: \"\\f5a6\"; }\n.bi-sunset::before { content: \"\\f5a7\"; }\n.bi-symmetry-horizontal::before { content: \"\\f5a8\"; }\n.bi-symmetry-vertical::before { content: \"\\f5a9\"; }\n.bi-table::before { content: \"\\f5aa\"; }\n.bi-tablet-fill::before { content: \"\\f5ab\"; }\n.bi-tablet-landscape-fill::before { content: \"\\f5ac\"; }\n.bi-tablet-landscape::before { content: \"\\f5ad\"; }\n.bi-tablet::before { content: \"\\f5ae\"; }\n.bi-tag-fill::before { content: \"\\f5af\"; }\n.bi-tag::before { content: \"\\f5b0\"; }\n.bi-tags-fill::before { content: \"\\f5b1\"; }\n.bi-tags::before { content: \"\\f5b2\"; }\n.bi-telegram::before { content: \"\\f5b3\"; }\n.bi-telephone-fill::before { content: \"\\f5b4\"; }\n.bi-telephone-forward-fill::before { content: \"\\f5b5\"; }\n.bi-telephone-forward::before { content: \"\\f5b6\"; }\n.bi-telephone-inbound-fill::before { content: \"\\f5b7\"; }\n.bi-telephone-inbound::before { content: \"\\f5b8\"; }\n.bi-telephone-minus-fill::before { content: \"\\f5b9\"; }\n.bi-telephone-minus::before { content: \"\\f5ba\"; }\n.bi-telephone-outbound-fill::before { content: \"\\f5bb\"; }\n.bi-telephone-outbound::before { content: \"\\f5bc\"; }\n.bi-telephone-plus-fill::before { content: \"\\f5bd\"; }\n.bi-telephone-plus::before { content: \"\\f5be\"; }\n.bi-telephone-x-fill::before { content: \"\\f5bf\"; }\n.bi-telephone-x::before { content: \"\\f5c0\"; }\n.bi-telephone::before { content: \"\\f5c1\"; }\n.bi-terminal-fill::before { content: \"\\f5c2\"; }\n.bi-terminal::before { content: \"\\f5c3\"; }\n.bi-text-center::before { content: \"\\f5c4\"; }\n.bi-text-indent-left::before { content: \"\\f5c5\"; }\n.bi-text-indent-right::before { content: \"\\f5c6\"; }\n.bi-text-left::before { content: \"\\f5c7\"; }\n.bi-text-paragraph::before { content: \"\\f5c8\"; }\n.bi-text-right::before { content: \"\\f5c9\"; }\n.bi-textarea-resize::before { content: \"\\f5ca\"; }\n.bi-textarea-t::before { content: \"\\f5cb\"; }\n.bi-textarea::before { content: \"\\f5cc\"; }\n.bi-thermometer-half::before { content: \"\\f5cd\"; }\n.bi-thermometer-high::before { content: \"\\f5ce\"; }\n.bi-thermometer-low::before { content: \"\\f5cf\"; }\n.bi-thermometer-snow::before { content: \"\\f5d0\"; }\n.bi-thermometer-sun::before { content: \"\\f5d1\"; }\n.bi-thermometer::before { content: \"\\f5d2\"; }\n.bi-three-dots-vertical::before { content: \"\\f5d3\"; }\n.bi-three-dots::before { content: \"\\f5d4\"; }\n.bi-toggle-off::before { content: \"\\f5d5\"; }\n.bi-toggle-on::before { content: \"\\f5d6\"; }\n.bi-toggle2-off::before { content: \"\\f5d7\"; }\n.bi-toggle2-on::before { content: \"\\f5d8\"; }\n.bi-toggles::before { content: \"\\f5d9\"; }\n.bi-toggles2::before { content: \"\\f5da\"; }\n.bi-tools::before { content: \"\\f5db\"; }\n.bi-tornado::before { content: \"\\f5dc\"; }\n.bi-trash-fill::before { content: \"\\f5dd\"; }\n.bi-trash::before { content: \"\\f5de\"; }\n.bi-trash2-fill::before { content: \"\\f5df\"; }\n.bi-trash2::before { content: \"\\f5e0\"; }\n.bi-tree-fill::before { content: \"\\f5e1\"; }\n.bi-tree::before { content: \"\\f5e2\"; }\n.bi-triangle-fill::before { content: \"\\f5e3\"; }\n.bi-triangle-half::before { content: \"\\f5e4\"; }\n.bi-triangle::before { content: \"\\f5e5\"; }\n.bi-trophy-fill::before { content: \"\\f5e6\"; }\n.bi-trophy::before { content: \"\\f5e7\"; }\n.bi-tropical-storm::before { content: \"\\f5e8\"; }\n.bi-truck-flatbed::before { content: \"\\f5e9\"; }\n.bi-truck::before { content: \"\\f5ea\"; }\n.bi-tsunami::before { content: \"\\f5eb\"; }\n.bi-tv-fill::before { content: \"\\f5ec\"; }\n.bi-tv::before { content: \"\\f5ed\"; }\n.bi-twitch::before { content: \"\\f5ee\"; }\n.bi-twitter::before { content: \"\\f5ef\"; }\n.bi-type-bold::before { content: \"\\f5f0\"; }\n.bi-type-h1::before { content: \"\\f5f1\"; }\n.bi-type-h2::before { content: \"\\f5f2\"; }\n.bi-type-h3::before { content: \"\\f5f3\"; }\n.bi-type-italic::before { content: \"\\f5f4\"; }\n.bi-type-strikethrough::before { content: \"\\f5f5\"; }\n.bi-type-underline::before { content: \"\\f5f6\"; }\n.bi-type::before { content: \"\\f5f7\"; }\n.bi-ui-checks-grid::before { content: \"\\f5f8\"; }\n.bi-ui-checks::before { content: \"\\f5f9\"; }\n.bi-ui-radios-grid::before { content: \"\\f5fa\"; }\n.bi-ui-radios::before { content: \"\\f5fb\"; }\n.bi-umbrella-fill::before { content: \"\\f5fc\"; }\n.bi-umbrella::before { content: \"\\f5fd\"; }\n.bi-union::before { content: \"\\f5fe\"; }\n.bi-unlock-fill::before { content: \"\\f5ff\"; }\n.bi-unlock::before { content: \"\\f600\"; }\n.bi-upc-scan::before { content: \"\\f601\"; }\n.bi-upc::before { content: \"\\f602\"; }\n.bi-upload::before { content: \"\\f603\"; }\n.bi-vector-pen::before { content: \"\\f604\"; }\n.bi-view-list::before { content: \"\\f605\"; }\n.bi-view-stacked::before { content: \"\\f606\"; }\n.bi-vinyl-fill::before { content: \"\\f607\"; }\n.bi-vinyl::before { content: \"\\f608\"; }\n.bi-voicemail::before { content: \"\\f609\"; }\n.bi-volume-down-fill::before { content: \"\\f60a\"; }\n.bi-volume-down::before { content: \"\\f60b\"; }\n.bi-volume-mute-fill::before { content: \"\\f60c\"; }\n.bi-volume-mute::before { content: \"\\f60d\"; }\n.bi-volume-off-fill::before { content: \"\\f60e\"; }\n.bi-volume-off::before { content: \"\\f60f\"; }\n.bi-volume-up-fill::before { content: \"\\f610\"; }\n.bi-volume-up::before { content: \"\\f611\"; }\n.bi-vr::before { content: \"\\f612\"; }\n.bi-wallet-fill::before { content: \"\\f613\"; }\n.bi-wallet::before { content: \"\\f614\"; }\n.bi-wallet2::before { content: \"\\f615\"; }\n.bi-watch::before { content: \"\\f616\"; }\n.bi-water::before { content: \"\\f617\"; }\n.bi-whatsapp::before { content: \"\\f618\"; }\n.bi-wifi-1::before { content: \"\\f619\"; }\n.bi-wifi-2::before { content: \"\\f61a\"; }\n.bi-wifi-off::before { content: \"\\f61b\"; }\n.bi-wifi::before { content: \"\\f61c\"; }\n.bi-wind::before { content: \"\\f61d\"; }\n.bi-window-dock::before { content: \"\\f61e\"; }\n.bi-window-sidebar::before { content: \"\\f61f\"; }\n.bi-window::before { content: \"\\f620\"; }\n.bi-wrench::before { content: \"\\f621\"; }\n.bi-x-circle-fill::before { content: \"\\f622\"; }\n.bi-x-circle::before { content: \"\\f623\"; }\n.bi-x-diamond-fill::before { content: \"\\f624\"; }\n.bi-x-diamond::before { content: \"\\f625\"; }\n.bi-x-octagon-fill::before { content: \"\\f626\"; }\n.bi-x-octagon::before { content: \"\\f627\"; }\n.bi-x-square-fill::before { content: \"\\f628\"; }\n.bi-x-square::before { content: \"\\f629\"; }\n.bi-x::before { content: \"\\f62a\"; }\n.bi-youtube::before { content: \"\\f62b\"; }\n.bi-zoom-in::before { content: \"\\f62c\"; }\n.bi-zoom-out::before { content: \"\\f62d\"; }\n.bi-bank::before { content: \"\\f62e\"; }\n.bi-bank2::before { content: \"\\f62f\"; }\n.bi-bell-slash-fill::before { content: \"\\f630\"; }\n.bi-bell-slash::before { content: \"\\f631\"; }\n.bi-cash-coin::before { content: \"\\f632\"; }\n.bi-check-lg::before { content: \"\\f633\"; }\n.bi-coin::before { content: \"\\f634\"; }\n.bi-currency-bitcoin::before { content: \"\\f635\"; }\n.bi-currency-dollar::before { content: \"\\f636\"; }\n.bi-currency-euro::before { content: \"\\f637\"; }\n.bi-currency-exchange::before { content: \"\\f638\"; }\n.bi-currency-pound::before { content: \"\\f639\"; }\n.bi-currency-yen::before { content: \"\\f63a\"; }\n.bi-dash-lg::before { content: \"\\f63b\"; }\n.bi-exclamation-lg::before { content: \"\\f63c\"; }\n.bi-file-earmark-pdf-fill::before { content: \"\\f63d\"; }\n.bi-file-earmark-pdf::before { content: \"\\f63e\"; }\n.bi-file-pdf-fill::before { content: \"\\f63f\"; }\n.bi-file-pdf::before { content: \"\\f640\"; }\n.bi-gender-ambiguous::before { content: \"\\f641\"; }\n.bi-gender-female::before { content: \"\\f642\"; }\n.bi-gender-male::before { content: \"\\f643\"; }\n.bi-gender-trans::before { content: \"\\f644\"; }\n.bi-headset-vr::before { content: \"\\f645\"; }\n.bi-info-lg::before { content: \"\\f646\"; }\n.bi-mastodon::before { content: \"\\f647\"; }\n.bi-messenger::before { content: \"\\f648\"; }\n.bi-piggy-bank-fill::before { content: \"\\f649\"; }\n.bi-piggy-bank::before { content: \"\\f64a\"; }\n.bi-pin-map-fill::before { content: \"\\f64b\"; }\n.bi-pin-map::before { content: \"\\f64c\"; }\n.bi-plus-lg::before { content: \"\\f64d\"; }\n.bi-question-lg::before { content: \"\\f64e\"; }\n.bi-recycle::before { content: \"\\f64f\"; }\n.bi-reddit::before { content: \"\\f650\"; }\n.bi-safe-fill::before { content: \"\\f651\"; }\n.bi-safe2-fill::before { content: \"\\f652\"; }\n.bi-safe2::before { content: \"\\f653\"; }\n.bi-sd-card-fill::before { content: \"\\f654\"; }\n.bi-sd-card::before { content: \"\\f655\"; }\n.bi-skype::before { content: \"\\f656\"; }\n.bi-slash-lg::before { content: \"\\f657\"; }\n.bi-translate::before { content: \"\\f658\"; }\n.bi-x-lg::before { content: \"\\f659\"; }\n.bi-safe::before { content: \"\\f65a\"; }\n.bi-apple::before { content: \"\\f65b\"; }\n.bi-microsoft::before { content: \"\\f65d\"; }\n.bi-windows::before { content: \"\\f65e\"; }\n.bi-behance::before { content: \"\\f65c\"; }\n.bi-dribbble::before { content: \"\\f65f\"; }\n.bi-line::before { content: \"\\f660\"; }\n.bi-medium::before { content: \"\\f661\"; }\n.bi-paypal::before { content: \"\\f662\"; }\n.bi-pinterest::before { content: \"\\f663\"; }\n.bi-signal::before { content: \"\\f664\"; }\n.bi-snapchat::before { content: \"\\f665\"; }\n.bi-spotify::before { content: \"\\f666\"; }\n.bi-stack-overflow::before { content: \"\\f667\"; }\n.bi-strava::before { content: \"\\f668\"; }\n.bi-wordpress::before { content: \"\\f669\"; }\n.bi-vimeo::before { content: \"\\f66a\"; }\n.bi-activity::before { content: \"\\f66b\"; }\n.bi-easel2-fill::before { content: \"\\f66c\"; }\n.bi-easel2::before { content: \"\\f66d\"; }\n.bi-easel3-fill::before { content: \"\\f66e\"; }\n.bi-easel3::before { content: \"\\f66f\"; }\n.bi-fan::before { content: \"\\f670\"; }\n.bi-fingerprint::before { content: \"\\f671\"; }\n.bi-graph-down-arrow::before { content: \"\\f672\"; }\n.bi-graph-up-arrow::before { content: \"\\f673\"; }\n.bi-hypnotize::before { content: \"\\f674\"; }\n.bi-magic::before { content: \"\\f675\"; }\n.bi-person-rolodex::before { content: \"\\f676\"; }\n.bi-person-video::before { content: \"\\f677\"; }\n.bi-person-video2::before { content: \"\\f678\"; }\n.bi-person-video3::before { content: \"\\f679\"; }\n.bi-person-workspace::before { content: \"\\f67a\"; }\n.bi-radioactive::before { content: \"\\f67b\"; }\n.bi-webcam-fill::before { content: \"\\f67c\"; }\n.bi-webcam::before { content: \"\\f67d\"; }\n.bi-yin-yang::before { content: \"\\f67e\"; }\n.bi-bandaid-fill::before { content: \"\\f680\"; }\n.bi-bandaid::before { content: \"\\f681\"; }\n.bi-bluetooth::before { content: \"\\f682\"; }\n.bi-body-text::before { content: \"\\f683\"; }\n.bi-boombox::before { content: \"\\f684\"; }\n.bi-boxes::before { content: \"\\f685\"; }\n.bi-dpad-fill::before { content: \"\\f686\"; }\n.bi-dpad::before { content: \"\\f687\"; }\n.bi-ear-fill::before { content: \"\\f688\"; }\n.bi-ear::before { content: \"\\f689\"; }\n.bi-envelope-check-1::before { content: \"\\f68a\"; }\n.bi-envelope-check-fill::before { content: \"\\f68b\"; }\n.bi-envelope-check::before { content: \"\\f68c\"; }\n.bi-envelope-dash-1::before { content: \"\\f68d\"; }\n.bi-envelope-dash-fill::before { content: \"\\f68e\"; }\n.bi-envelope-dash::before { content: \"\\f68f\"; }\n.bi-envelope-exclamation-1::before { content: \"\\f690\"; }\n.bi-envelope-exclamation-fill::before { content: \"\\f691\"; }\n.bi-envelope-exclamation::before { content: \"\\f692\"; }\n.bi-envelope-plus-fill::before { content: \"\\f693\"; }\n.bi-envelope-plus::before { content: \"\\f694\"; }\n.bi-envelope-slash-1::before { content: \"\\f695\"; }\n.bi-envelope-slash-fill::before { content: \"\\f696\"; }\n.bi-envelope-slash::before { content: \"\\f697\"; }\n.bi-envelope-x-1::before { content: \"\\f698\"; }\n.bi-envelope-x-fill::before { content: \"\\f699\"; }\n.bi-envelope-x::before { content: \"\\f69a\"; }\n.bi-explicit-fill::before { content: \"\\f69b\"; }\n.bi-explicit::before { content: \"\\f69c\"; }\n.bi-git::before { content: \"\\f69d\"; }\n.bi-infinity::before { content: \"\\f69e\"; }\n.bi-list-columns-reverse::before { content: \"\\f69f\"; }\n.bi-list-columns::before { content: \"\\f6a0\"; }\n.bi-meta::before { content: \"\\f6a1\"; }\n.bi-mortorboard-fill::before { content: \"\\f6a2\"; }\n.bi-mortorboard::before { content: \"\\f6a3\"; }\n.bi-nintendo-switch::before { content: \"\\f6a4\"; }\n.bi-pc-display-horizontal::before { content: \"\\f6a5\"; }\n.bi-pc-display::before { content: \"\\f6a6\"; }\n.bi-pc-horizontal::before { content: \"\\f6a7\"; }\n.bi-pc::before { content: \"\\f6a8\"; }\n.bi-playstation::before { content: \"\\f6a9\"; }\n.bi-plus-slash-minus::before { content: \"\\f6aa\"; }\n.bi-projector-fill::before { content: \"\\f6ab\"; }\n.bi-projector::before { content: \"\\f6ac\"; }\n.bi-qr-code-scan::before { content: \"\\f6ad\"; }\n.bi-qr-code::before { content: \"\\f6ae\"; }\n.bi-quora::before { content: \"\\f6af\"; }\n.bi-quote::before { content: \"\\f6b0\"; }\n.bi-robot::before { content: \"\\f6b1\"; }\n.bi-send-check-fill::before { content: \"\\f6b2\"; }\n.bi-send-check::before { content: \"\\f6b3\"; }\n.bi-send-dash-fill::before { content: \"\\f6b4\"; }\n.bi-send-dash::before { content: \"\\f6b5\"; }\n.bi-send-exclamation-1::before { content: \"\\f6b6\"; }\n.bi-send-exclamation-fill::before { content: \"\\f6b7\"; }\n.bi-send-exclamation::before { content: \"\\f6b8\"; }\n.bi-send-fill::before { content: \"\\f6b9\"; }\n.bi-send-plus-fill::before { content: \"\\f6ba\"; }\n.bi-send-plus::before { content: \"\\f6bb\"; }\n.bi-send-slash-fill::before { content: \"\\f6bc\"; }\n.bi-send-slash::before { content: \"\\f6bd\"; }\n.bi-send-x-fill::before { content: \"\\f6be\"; }\n.bi-send-x::before { content: \"\\f6bf\"; }\n.bi-send::before { content: \"\\f6c0\"; }\n.bi-steam::before { content: \"\\f6c1\"; }\n.bi-terminal-dash-1::before { content: \"\\f6c2\"; }\n.bi-terminal-dash::before { content: \"\\f6c3\"; }\n.bi-terminal-plus::before { content: \"\\f6c4\"; }\n.bi-terminal-split::before { content: \"\\f6c5\"; }\n.bi-ticket-detailed-fill::before { content: \"\\f6c6\"; }\n.bi-ticket-detailed::before { content: \"\\f6c7\"; }\n.bi-ticket-fill::before { content: \"\\f6c8\"; }\n.bi-ticket-perforated-fill::before { content: \"\\f6c9\"; }\n.bi-ticket-perforated::before { content: \"\\f6ca\"; }\n.bi-ticket::before { content: \"\\f6cb\"; }\n.bi-tiktok::before { content: \"\\f6cc\"; }\n.bi-window-dash::before { content: \"\\f6cd\"; }\n.bi-window-desktop::before { content: \"\\f6ce\"; }\n.bi-window-fullscreen::before { content: \"\\f6cf\"; }\n.bi-window-plus::before { content: \"\\f6d0\"; }\n.bi-window-split::before { content: \"\\f6d1\"; }\n.bi-window-stack::before { content: \"\\f6d2\"; }\n.bi-window-x::before { content: \"\\f6d3\"; }\n.bi-xbox::before { content: \"\\f6d4\"; }\n.bi-ethernet::before { content: \"\\f6d5\"; }\n.bi-hdmi-fill::before { content: \"\\f6d6\"; }\n.bi-hdmi::before { content: \"\\f6d7\"; }\n.bi-usb-c-fill::before { content: \"\\f6d8\"; }\n.bi-usb-c::before { content: \"\\f6d9\"; }\n.bi-usb-fill::before { content: \"\\f6da\"; }\n.bi-usb-plug-fill::before { content: \"\\f6db\"; }\n.bi-usb-plug::before { content: \"\\f6dc\"; }\n.bi-usb-symbol::before { content: \"\\f6dd\"; }\n.bi-usb::before { content: \"\\f6de\"; }\n.bi-boombox-fill::before { content: \"\\f6df\"; }\n.bi-displayport-1::before { content: \"\\f6e0\"; }\n.bi-displayport::before { content: \"\\f6e1\"; }\n.bi-gpu-card::before { content: \"\\f6e2\"; }\n.bi-memory::before { content: \"\\f6e3\"; }\n.bi-modem-fill::before { content: \"\\f6e4\"; }\n.bi-modem::before { content: \"\\f6e5\"; }\n.bi-motherboard-fill::before { content: \"\\f6e6\"; }\n.bi-motherboard::before { content: \"\\f6e7\"; }\n.bi-optical-audio-fill::before { content: \"\\f6e8\"; }\n.bi-optical-audio::before { content: \"\\f6e9\"; }\n.bi-pci-card::before { content: \"\\f6ea\"; }\n.bi-router-fill::before { content: \"\\f6eb\"; }\n.bi-router::before { content: \"\\f6ec\"; }\n.bi-ssd-fill::before { content: \"\\f6ed\"; }\n.bi-ssd::before { content: \"\\f6ee\"; }\n.bi-thunderbolt-fill::before { content: \"\\f6ef\"; }\n.bi-thunderbolt::before { content: \"\\f6f0\"; }\n.bi-usb-drive-fill::before { content: \"\\f6f1\"; }\n.bi-usb-drive::before { content: \"\\f6f2\"; }\n.bi-usb-micro-fill::before { content: \"\\f6f3\"; }\n.bi-usb-micro::before { content: \"\\f6f4\"; }\n.bi-usb-mini-fill::before { content: \"\\f6f5\"; }\n.bi-usb-mini::before { content: \"\\f6f6\"; }\n.bi-cloud-haze2::before { content: \"\\f6f7\"; }\n.bi-device-hdd-fill::before { content: \"\\f6f8\"; }\n.bi-device-hdd::before { content: \"\\f6f9\"; }\n.bi-device-ssd-fill::before { content: \"\\f6fa\"; }\n.bi-device-ssd::before { content: \"\\f6fb\"; }\n.bi-displayport-fill::before { content: \"\\f6fc\"; }\n.bi-mortarboard-fill::before { content: \"\\f6fd\"; }\n.bi-mortarboard::before { content: \"\\f6fe\"; }\n.bi-terminal-x::before { content: \"\\f6ff\"; }\n.bi-arrow-through-heart-fill::before { content: \"\\f700\"; }\n.bi-arrow-through-heart::before { content: \"\\f701\"; }\n.bi-badge-sd-fill::before { content: \"\\f702\"; }\n.bi-badge-sd::before { content: \"\\f703\"; }\n.bi-bag-heart-fill::before { content: \"\\f704\"; }\n.bi-bag-heart::before { content: \"\\f705\"; }\n.bi-balloon-fill::before { content: \"\\f706\"; }\n.bi-balloon-heart-fill::before { content: \"\\f707\"; }\n.bi-balloon-heart::before { content: \"\\f708\"; }\n.bi-balloon::before { content: \"\\f709\"; }\n.bi-box2-fill::before { content: \"\\f70a\"; }\n.bi-box2-heart-fill::before { content: \"\\f70b\"; }\n.bi-box2-heart::before { content: \"\\f70c\"; }\n.bi-box2::before { content: \"\\f70d\"; }\n.bi-braces-asterisk::before { content: \"\\f70e\"; }\n.bi-calendar-heart-fill::before { content: \"\\f70f\"; }\n.bi-calendar-heart::before { content: \"\\f710\"; }\n.bi-calendar2-heart-fill::before { content: \"\\f711\"; }\n.bi-calendar2-heart::before { content: \"\\f712\"; }\n.bi-chat-heart-fill::before { content: \"\\f713\"; }\n.bi-chat-heart::before { content: \"\\f714\"; }\n.bi-chat-left-heart-fill::before { content: \"\\f715\"; }\n.bi-chat-left-heart::before { content: \"\\f716\"; }\n.bi-chat-right-heart-fill::before { content: \"\\f717\"; }\n.bi-chat-right-heart::before { content: \"\\f718\"; }\n.bi-chat-square-heart-fill::before { content: \"\\f719\"; }\n.bi-chat-square-heart::before { content: \"\\f71a\"; }\n.bi-clipboard-check-fill::before { content: \"\\f71b\"; }\n.bi-clipboard-data-fill::before { content: \"\\f71c\"; }\n.bi-clipboard-fill::before { content: \"\\f71d\"; }\n.bi-clipboard-heart-fill::before { content: \"\\f71e\"; }\n.bi-clipboard-heart::before { content: \"\\f71f\"; }\n.bi-clipboard-minus-fill::before { content: \"\\f720\"; }\n.bi-clipboard-plus-fill::before { content: \"\\f721\"; }\n.bi-clipboard-pulse::before { content: \"\\f722\"; }\n.bi-clipboard-x-fill::before { content: \"\\f723\"; }\n.bi-clipboard2-check-fill::before { content: \"\\f724\"; }\n.bi-clipboard2-check::before { content: \"\\f725\"; }\n.bi-clipboard2-data-fill::before { content: \"\\f726\"; }\n.bi-clipboard2-data::before { content: \"\\f727\"; }\n.bi-clipboard2-fill::before { content: \"\\f728\"; }\n.bi-clipboard2-heart-fill::before { content: \"\\f729\"; }\n.bi-clipboard2-heart::before { content: \"\\f72a\"; }\n.bi-clipboard2-minus-fill::before { content: \"\\f72b\"; }\n.bi-clipboard2-minus::before { content: \"\\f72c\"; }\n.bi-clipboard2-plus-fill::before { content: \"\\f72d\"; }\n.bi-clipboard2-plus::before { content: \"\\f72e\"; }\n.bi-clipboard2-pulse-fill::before { content: \"\\f72f\"; }\n.bi-clipboard2-pulse::before { content: \"\\f730\"; }\n.bi-clipboard2-x-fill::before { content: \"\\f731\"; }\n.bi-clipboard2-x::before { content: \"\\f732\"; }\n.bi-clipboard2::before { content: \"\\f733\"; }\n.bi-emoji-kiss-fill::before { content: \"\\f734\"; }\n.bi-emoji-kiss::before { content: \"\\f735\"; }\n.bi-envelope-heart-fill::before { content: \"\\f736\"; }\n.bi-envelope-heart::before { content: \"\\f737\"; }\n.bi-envelope-open-heart-fill::before { content: \"\\f738\"; }\n.bi-envelope-open-heart::before { content: \"\\f739\"; }\n.bi-envelope-paper-fill::before { content: \"\\f73a\"; }\n.bi-envelope-paper-heart-fill::before { content: \"\\f73b\"; }\n.bi-envelope-paper-heart::before { content: \"\\f73c\"; }\n.bi-envelope-paper::before { content: \"\\f73d\"; }\n.bi-filetype-aac::before { content: \"\\f73e\"; }\n.bi-filetype-ai::before { content: \"\\f73f\"; }\n.bi-filetype-bmp::before { content: \"\\f740\"; }\n.bi-filetype-cs::before { content: \"\\f741\"; }\n.bi-filetype-css::before { content: \"\\f742\"; }\n.bi-filetype-csv::before { content: \"\\f743\"; }\n.bi-filetype-doc::before { content: \"\\f744\"; }\n.bi-filetype-docx::before { content: \"\\f745\"; }\n.bi-filetype-exe::before { content: \"\\f746\"; }\n.bi-filetype-gif::before { content: \"\\f747\"; }\n.bi-filetype-heic::before { content: \"\\f748\"; }\n.bi-filetype-html::before { content: \"\\f749\"; }\n.bi-filetype-java::before { content: \"\\f74a\"; }\n.bi-filetype-jpg::before { content: \"\\f74b\"; }\n.bi-filetype-js::before { content: \"\\f74c\"; }\n.bi-filetype-jsx::before { content: \"\\f74d\"; }\n.bi-filetype-key::before { content: \"\\f74e\"; }\n.bi-filetype-m4p::before { content: \"\\f74f\"; }\n.bi-filetype-md::before { content: \"\\f750\"; }\n.bi-filetype-mdx::before { content: \"\\f751\"; }\n.bi-filetype-mov::before { content: \"\\f752\"; }\n.bi-filetype-mp3::before { content: \"\\f753\"; }\n.bi-filetype-mp4::before { content: \"\\f754\"; }\n.bi-filetype-otf::before { content: \"\\f755\"; }\n.bi-filetype-pdf::before { content: \"\\f756\"; }\n.bi-filetype-php::before { content: \"\\f757\"; }\n.bi-filetype-png::before { content: \"\\f758\"; }\n.bi-filetype-ppt-1::before { content: \"\\f759\"; }\n.bi-filetype-ppt::before { content: \"\\f75a\"; }\n.bi-filetype-psd::before { content: \"\\f75b\"; }\n.bi-filetype-py::before { content: \"\\f75c\"; }\n.bi-filetype-raw::before { content: \"\\f75d\"; }\n.bi-filetype-rb::before { content: \"\\f75e\"; }\n.bi-filetype-sass::before { content: \"\\f75f\"; }\n.bi-filetype-scss::before { content: \"\\f760\"; }\n.bi-filetype-sh::before { content: \"\\f761\"; }\n.bi-filetype-svg::before { content: \"\\f762\"; }\n.bi-filetype-tiff::before { content: \"\\f763\"; }\n.bi-filetype-tsx::before { content: \"\\f764\"; }\n.bi-filetype-ttf::before { content: \"\\f765\"; }\n.bi-filetype-txt::before { content: \"\\f766\"; }\n.bi-filetype-wav::before { content: \"\\f767\"; }\n.bi-filetype-woff::before { content: \"\\f768\"; }\n.bi-filetype-xls-1::before { content: \"\\f769\"; }\n.bi-filetype-xls::before { content: \"\\f76a\"; }\n.bi-filetype-xml::before { content: \"\\f76b\"; }\n.bi-filetype-yml::before { content: \"\\f76c\"; }\n.bi-heart-arrow::before { content: \"\\f76d\"; }\n.bi-heart-pulse-fill::before { content: \"\\f76e\"; }\n.bi-heart-pulse::before { content: \"\\f76f\"; }\n.bi-heartbreak-fill::before { content: \"\\f770\"; }\n.bi-heartbreak::before { content: \"\\f771\"; }\n.bi-hearts::before { content: \"\\f772\"; }\n.bi-hospital-fill::before { content: \"\\f773\"; }\n.bi-hospital::before { content: \"\\f774\"; }\n.bi-house-heart-fill::before { content: \"\\f775\"; }\n.bi-house-heart::before { content: \"\\f776\"; }\n.bi-incognito::before { content: \"\\f777\"; }\n.bi-magnet-fill::before { content: \"\\f778\"; }\n.bi-magnet::before { content: \"\\f779\"; }\n.bi-person-heart::before { content: \"\\f77a\"; }\n.bi-person-hearts::before { content: \"\\f77b\"; }\n.bi-phone-flip::before { content: \"\\f77c\"; }\n.bi-plugin::before { content: \"\\f77d\"; }\n.bi-postage-fill::before { content: \"\\f77e\"; }\n.bi-postage-heart-fill::before { content: \"\\f77f\"; }\n.bi-postage-heart::before { content: \"\\f780\"; }\n.bi-postage::before { content: \"\\f781\"; }\n.bi-postcard-fill::before { content: \"\\f782\"; }\n.bi-postcard-heart-fill::before { content: \"\\f783\"; }\n.bi-postcard-heart::before { content: \"\\f784\"; }\n.bi-postcard::before { content: \"\\f785\"; }\n.bi-search-heart-fill::before { content: \"\\f786\"; }\n.bi-search-heart::before { content: \"\\f787\"; }\n.bi-sliders2-vertical::before { content: \"\\f788\"; }\n.bi-sliders2::before { content: \"\\f789\"; }\n.bi-trash3-fill::before { content: \"\\f78a\"; }\n.bi-trash3::before { content: \"\\f78b\"; }\n.bi-valentine::before { content: \"\\f78c\"; }\n.bi-valentine2::before { content: \"\\f78d\"; }\n.bi-wrench-adjustable-circle-fill::before { content: \"\\f78e\"; }\n.bi-wrench-adjustable-circle::before { content: \"\\f78f\"; }\n.bi-wrench-adjustable::before { content: \"\\f790\"; }\n.bi-filetype-json::before { content: \"\\f791\"; }\n.bi-filetype-pptx::before { content: \"\\f792\"; }\n.bi-filetype-xlsx::before { content: \"\\f793\"; }\n.bi-1-circle-1::before { content: \"\\f794\"; }\n.bi-1-circle-fill-1::before { content: \"\\f795\"; }\n.bi-1-circle-fill::before { content: \"\\f796\"; }\n.bi-1-circle::before { content: \"\\f797\"; }\n.bi-1-square-fill::before { content: \"\\f798\"; }\n.bi-1-square::before { content: \"\\f799\"; }\n.bi-2-circle-1::before { content: \"\\f79a\"; }\n.bi-2-circle-fill-1::before { content: \"\\f79b\"; }\n.bi-2-circle-fill::before { content: \"\\f79c\"; }\n.bi-2-circle::before { content: \"\\f79d\"; }\n.bi-2-square-fill::before { content: \"\\f79e\"; }\n.bi-2-square::before { content: \"\\f79f\"; }\n.bi-3-circle-1::before { content: \"\\f7a0\"; }\n.bi-3-circle-fill-1::before { content: \"\\f7a1\"; }\n.bi-3-circle-fill::before { content: \"\\f7a2\"; }\n.bi-3-circle::before { content: \"\\f7a3\"; }\n.bi-3-square-fill::before { content: \"\\f7a4\"; }\n.bi-3-square::before { content: \"\\f7a5\"; }\n.bi-4-circle-1::before { content: \"\\f7a6\"; }\n.bi-4-circle-fill-1::before { content: \"\\f7a7\"; }\n.bi-4-circle-fill::before { content: \"\\f7a8\"; }\n.bi-4-circle::before { content: \"\\f7a9\"; }\n.bi-4-square-fill::before { content: \"\\f7aa\"; }\n.bi-4-square::before { content: \"\\f7ab\"; }\n.bi-5-circle-1::before { content: \"\\f7ac\"; }\n.bi-5-circle-fill-1::before { content: \"\\f7ad\"; }\n.bi-5-circle-fill::before { content: \"\\f7ae\"; }\n.bi-5-circle::before { content: \"\\f7af\"; }\n.bi-5-square-fill::before { content: \"\\f7b0\"; }\n.bi-5-square::before { content: \"\\f7b1\"; }\n.bi-6-circle-1::before { content: \"\\f7b2\"; }\n.bi-6-circle-fill-1::before { content: \"\\f7b3\"; }\n.bi-6-circle-fill::before { content: \"\\f7b4\"; }\n.bi-6-circle::before { content: \"\\f7b5\"; }\n.bi-6-square-fill::before { content: \"\\f7b6\"; }\n.bi-6-square::before { content: \"\\f7b7\"; }\n.bi-7-circle-1::before { content: \"\\f7b8\"; }\n.bi-7-circle-fill-1::before { content: \"\\f7b9\"; }\n.bi-7-circle-fill::before { content: \"\\f7ba\"; }\n.bi-7-circle::before { content: \"\\f7bb\"; }\n.bi-7-square-fill::before { content: \"\\f7bc\"; }\n.bi-7-square::before { content: \"\\f7bd\"; }\n.bi-8-circle-1::before { content: \"\\f7be\"; }\n.bi-8-circle-fill-1::before { content: \"\\f7bf\"; }\n.bi-8-circle-fill::before { content: \"\\f7c0\"; }\n.bi-8-circle::before { content: \"\\f7c1\"; }\n.bi-8-square-fill::before { content: \"\\f7c2\"; }\n.bi-8-square::before { content: \"\\f7c3\"; }\n.bi-9-circle-1::before { content: \"\\f7c4\"; }\n.bi-9-circle-fill-1::before { content: \"\\f7c5\"; }\n.bi-9-circle-fill::before { content: \"\\f7c6\"; }\n.bi-9-circle::before { content: \"\\f7c7\"; }\n.bi-9-square-fill::before { content: \"\\f7c8\"; }\n.bi-9-square::before { content: \"\\f7c9\"; }\n.bi-airplane-engines-fill::before { content: \"\\f7ca\"; }\n.bi-airplane-engines::before { content: \"\\f7cb\"; }\n.bi-airplane-fill::before { content: \"\\f7cc\"; }\n.bi-airplane::before { content: \"\\f7cd\"; }\n.bi-alexa::before { content: \"\\f7ce\"; }\n.bi-alipay::before { content: \"\\f7cf\"; }\n.bi-android::before { content: \"\\f7d0\"; }\n.bi-android2::before { content: \"\\f7d1\"; }\n.bi-box-fill::before { content: \"\\f7d2\"; }\n.bi-box-seam-fill::before { content: \"\\f7d3\"; }\n.bi-browser-chrome::before { content: \"\\f7d4\"; }\n.bi-browser-edge::before { content: \"\\f7d5\"; }\n.bi-browser-firefox::before { content: \"\\f7d6\"; }\n.bi-browser-safari::before { content: \"\\f7d7\"; }\n.bi-c-circle-1::before { content: \"\\f7d8\"; }\n.bi-c-circle-fill-1::before { content: \"\\f7d9\"; }\n.bi-c-circle-fill::before { content: \"\\f7da\"; }\n.bi-c-circle::before { content: \"\\f7db\"; }\n.bi-c-square-fill::before { content: \"\\f7dc\"; }\n.bi-c-square::before { content: \"\\f7dd\"; }\n.bi-capsule-pill::before { content: \"\\f7de\"; }\n.bi-capsule::before { content: \"\\f7df\"; }\n.bi-car-front-fill::before { content: \"\\f7e0\"; }\n.bi-car-front::before { content: \"\\f7e1\"; }\n.bi-cassette-fill::before { content: \"\\f7e2\"; }\n.bi-cassette::before { content: \"\\f7e3\"; }\n.bi-cc-circle-1::before { content: \"\\f7e4\"; }\n.bi-cc-circle-fill-1::before { content: \"\\f7e5\"; }\n.bi-cc-circle-fill::before { content: \"\\f7e6\"; }\n.bi-cc-circle::before { content: \"\\f7e7\"; }\n.bi-cc-square-fill::before { content: \"\\f7e8\"; }\n.bi-cc-square::before { content: \"\\f7e9\"; }\n.bi-cup-hot-fill::before { content: \"\\f7ea\"; }\n.bi-cup-hot::before { content: \"\\f7eb\"; }\n.bi-currency-rupee::before { content: \"\\f7ec\"; }\n.bi-dropbox::before { content: \"\\f7ed\"; }\n.bi-escape::before { content: \"\\f7ee\"; }\n.bi-fast-forward-btn-fill::before { content: \"\\f7ef\"; }\n.bi-fast-forward-btn::before { content: \"\\f7f0\"; }\n.bi-fast-forward-circle-fill::before { content: \"\\f7f1\"; }\n.bi-fast-forward-circle::before { content: \"\\f7f2\"; }\n.bi-fast-forward-fill::before { content: \"\\f7f3\"; }\n.bi-fast-forward::before { content: \"\\f7f4\"; }\n.bi-filetype-sql::before { content: \"\\f7f5\"; }\n.bi-fire::before { content: \"\\f7f6\"; }\n.bi-google-play::before { content: \"\\f7f7\"; }\n.bi-h-circle-1::before { content: \"\\f7f8\"; }\n.bi-h-circle-fill-1::before { content: \"\\f7f9\"; }\n.bi-h-circle-fill::before { content: \"\\f7fa\"; }\n.bi-h-circle::before { content: \"\\f7fb\"; }\n.bi-h-square-fill::before { content: \"\\f7fc\"; }\n.bi-h-square::before { content: \"\\f7fd\"; }\n.bi-indent::before { content: \"\\f7fe\"; }\n.bi-lungs-fill::before { content: \"\\f7ff\"; }\n.bi-lungs::before { content: \"\\f800\"; }\n.bi-microsoft-teams::before { content: \"\\f801\"; }\n.bi-p-circle-1::before { content: \"\\f802\"; }\n.bi-p-circle-fill-1::before { content: \"\\f803\"; }\n.bi-p-circle-fill::before { content: \"\\f804\"; }\n.bi-p-circle::before { content: \"\\f805\"; }\n.bi-p-square-fill::before { content: \"\\f806\"; }\n.bi-p-square::before { content: \"\\f807\"; }\n.bi-pass-fill::before { content: \"\\f808\"; }\n.bi-pass::before { content: \"\\f809\"; }\n.bi-prescription::before { content: \"\\f80a\"; }\n.bi-prescription2::before { content: \"\\f80b\"; }\n.bi-r-circle-1::before { content: \"\\f80c\"; }\n.bi-r-circle-fill-1::before { content: \"\\f80d\"; }\n.bi-r-circle-fill::before { content: \"\\f80e\"; }\n.bi-r-circle::before { content: \"\\f80f\"; }\n.bi-r-square-fill::before { content: \"\\f810\"; }\n.bi-r-square::before { content: \"\\f811\"; }\n.bi-repeat-1::before { content: \"\\f812\"; }\n.bi-repeat::before { content: \"\\f813\"; }\n.bi-rewind-btn-fill::before { content: \"\\f814\"; }\n.bi-rewind-btn::before { content: \"\\f815\"; }\n.bi-rewind-circle-fill::before { content: \"\\f816\"; }\n.bi-rewind-circle::before { content: \"\\f817\"; }\n.bi-rewind-fill::before { content: \"\\f818\"; }\n.bi-rewind::before { content: \"\\f819\"; }\n.bi-train-freight-front-fill::before { content: \"\\f81a\"; }\n.bi-train-freight-front::before { content: \"\\f81b\"; }\n.bi-train-front-fill::before { content: \"\\f81c\"; }\n.bi-train-front::before { content: \"\\f81d\"; }\n.bi-train-lightrail-front-fill::before { content: \"\\f81e\"; }\n.bi-train-lightrail-front::before { content: \"\\f81f\"; }\n.bi-truck-front-fill::before { content: \"\\f820\"; }\n.bi-truck-front::before { content: \"\\f821\"; }\n.bi-ubuntu::before { content: \"\\f822\"; }\n.bi-unindent::before { content: \"\\f823\"; }\n.bi-unity::before { content: \"\\f824\"; }\n.bi-universal-access-circle::before { content: \"\\f825\"; }\n.bi-universal-access::before { content: \"\\f826\"; }\n.bi-virus::before { content: \"\\f827\"; }\n.bi-virus2::before { content: \"\\f828\"; }\n.bi-wechat::before { content: \"\\f829\"; }\n.bi-yelp::before { content: \"\\f82a\"; }\n.bi-sign-stop-fill::before { content: \"\\f82b\"; }\n.bi-sign-stop-lights-fill::before { content: \"\\f82c\"; }\n.bi-sign-stop-lights::before { content: \"\\f82d\"; }\n.bi-sign-stop::before { content: \"\\f82e\"; }\n.bi-sign-turn-left-fill::before { content: \"\\f82f\"; }\n.bi-sign-turn-left::before { content: \"\\f830\"; }\n.bi-sign-turn-right-fill::before { content: \"\\f831\"; }\n.bi-sign-turn-right::before { content: \"\\f832\"; }\n.bi-sign-turn-slight-left-fill::before { content: \"\\f833\"; }\n.bi-sign-turn-slight-left::before { content: \"\\f834\"; }\n.bi-sign-turn-slight-right-fill::before { content: \"\\f835\"; }\n.bi-sign-turn-slight-right::before { content: \"\\f836\"; }\n.bi-sign-yield-fill::before { content: \"\\f837\"; }\n.bi-sign-yield::before { content: \"\\f838\"; }\n.bi-ev-station-fill::before { content: \"\\f839\"; }\n.bi-ev-station::before { content: \"\\f83a\"; }\n.bi-fuel-pump-diesel-fill::before { content: \"\\f83b\"; }\n.bi-fuel-pump-diesel::before { content: \"\\f83c\"; }\n.bi-fuel-pump-fill::before { content: \"\\f83d\"; }\n.bi-fuel-pump::before { content: \"\\f83e\"; }\n.bi-0-circle-fill::before { content: \"\\f83f\"; }\n.bi-0-circle::before { content: \"\\f840\"; }\n.bi-0-square-fill::before { content: \"\\f841\"; }\n.bi-0-square::before { content: \"\\f842\"; }\n.bi-rocket-fill::before { content: \"\\f843\"; }\n.bi-rocket-takeoff-fill::before { content: \"\\f844\"; }\n.bi-rocket-takeoff::before { content: \"\\f845\"; }\n.bi-rocket::before { content: \"\\f846\"; }\n.bi-stripe::before { content: \"\\f847\"; }\n.bi-subscript::before { content: \"\\f848\"; }\n.bi-superscript::before { content: \"\\f849\"; }\n.bi-trello::before { content: \"\\f84a\"; }\n.bi-envelope-at-fill::before { content: \"\\f84b\"; }\n.bi-envelope-at::before { content: \"\\f84c\"; }\n.bi-regex::before { content: \"\\f84d\"; }\n.bi-text-wrap::before { content: \"\\f84e\"; }\n.bi-sign-dead-end-fill::before { content: \"\\f84f\"; }\n.bi-sign-dead-end::before { content: \"\\f850\"; }\n.bi-sign-do-not-enter-fill::before { content: \"\\f851\"; }\n.bi-sign-do-not-enter::before { content: \"\\f852\"; }\n.bi-sign-intersection-fill::before { content: \"\\f853\"; }\n.bi-sign-intersection-side-fill::before { content: \"\\f854\"; }\n.bi-sign-intersection-side::before { content: \"\\f855\"; }\n.bi-sign-intersection-t-fill::before { content: \"\\f856\"; }\n.bi-sign-intersection-t::before { content: \"\\f857\"; }\n.bi-sign-intersection-y-fill::before { content: \"\\f858\"; }\n.bi-sign-intersection-y::before { content: \"\\f859\"; }\n.bi-sign-intersection::before { content: \"\\f85a\"; }\n.bi-sign-merge-left-fill::before { content: \"\\f85b\"; }\n.bi-sign-merge-left::before { content: \"\\f85c\"; }\n.bi-sign-merge-right-fill::before { content: \"\\f85d\"; }\n.bi-sign-merge-right::before { content: \"\\f85e\"; }\n.bi-sign-no-left-turn-fill::before { content: \"\\f85f\"; }\n.bi-sign-no-left-turn::before { content: \"\\f860\"; }\n.bi-sign-no-parking-fill::before { content: \"\\f861\"; }\n.bi-sign-no-parking::before { content: \"\\f862\"; }\n.bi-sign-no-right-turn-fill::before { content: \"\\f863\"; }\n.bi-sign-no-right-turn::before { content: \"\\f864\"; }\n.bi-sign-railroad-fill::before { content: \"\\f865\"; }\n.bi-sign-railroad::before { content: \"\\f866\"; }\n.bi-building-add::before { content: \"\\f867\"; }\n.bi-building-check::before { content: \"\\f868\"; }\n.bi-building-dash::before { content: \"\\f869\"; }\n.bi-building-down::before { content: \"\\f86a\"; }\n.bi-building-exclamation::before { content: \"\\f86b\"; }\n.bi-building-fill-add::before { content: \"\\f86c\"; }\n.bi-building-fill-check::before { content: \"\\f86d\"; }\n.bi-building-fill-dash::before { content: \"\\f86e\"; }\n.bi-building-fill-down::before { content: \"\\f86f\"; }\n.bi-building-fill-exclamation::before { content: \"\\f870\"; }\n.bi-building-fill-gear::before { content: \"\\f871\"; }\n.bi-building-fill-lock::before { content: \"\\f872\"; }\n.bi-building-fill-slash::before { content: \"\\f873\"; }\n.bi-building-fill-up::before { content: \"\\f874\"; }\n.bi-building-fill-x::before { content: \"\\f875\"; }\n.bi-building-fill::before { content: \"\\f876\"; }\n.bi-building-gear::before { content: \"\\f877\"; }\n.bi-building-lock::before { content: \"\\f878\"; }\n.bi-building-slash::before { content: \"\\f879\"; }\n.bi-building-up::before { content: \"\\f87a\"; }\n.bi-building-x::before { content: \"\\f87b\"; }\n.bi-buildings-fill::before { content: \"\\f87c\"; }\n.bi-buildings::before { content: \"\\f87d\"; }\n.bi-bus-front-fill::before { content: \"\\f87e\"; }\n.bi-bus-front::before { content: \"\\f87f\"; }\n.bi-ev-front-fill::before { content: \"\\f880\"; }\n.bi-ev-front::before { content: \"\\f881\"; }\n.bi-globe-americas::before { content: \"\\f882\"; }\n.bi-globe-asia-australia::before { content: \"\\f883\"; }\n.bi-globe-central-south-asia::before { content: \"\\f884\"; }\n.bi-globe-europe-africa::before { content: \"\\f885\"; }\n.bi-house-add-fill::before { content: \"\\f886\"; }\n.bi-house-add::before { content: \"\\f887\"; }\n.bi-house-check-fill::before { content: \"\\f888\"; }\n.bi-house-check::before { content: \"\\f889\"; }\n.bi-house-dash-fill::before { content: \"\\f88a\"; }\n.bi-house-dash::before { content: \"\\f88b\"; }\n.bi-house-down-fill::before { content: \"\\f88c\"; }\n.bi-house-down::before { content: \"\\f88d\"; }\n.bi-house-exclamation-fill::before { content: \"\\f88e\"; }\n.bi-house-exclamation::before { content: \"\\f88f\"; }\n.bi-house-gear-fill::before { content: \"\\f890\"; }\n.bi-house-gear::before { content: \"\\f891\"; }\n.bi-house-lock-fill::before { content: \"\\f892\"; }\n.bi-house-lock::before { content: \"\\f893\"; }\n.bi-house-slash-fill::before { content: \"\\f894\"; }\n.bi-house-slash::before { content: \"\\f895\"; }\n.bi-house-up-fill::before { content: \"\\f896\"; }\n.bi-house-up::before { content: \"\\f897\"; }\n.bi-house-x-fill::before { content: \"\\f898\"; }\n.bi-house-x::before { content: \"\\f899\"; }\n.bi-person-add::before { content: \"\\f89a\"; }\n.bi-person-down::before { content: \"\\f89b\"; }\n.bi-person-exclamation::before { content: \"\\f89c\"; }\n.bi-person-fill-add::before { content: \"\\f89d\"; }\n.bi-person-fill-check::before { content: \"\\f89e\"; }\n.bi-person-fill-dash::before { content: \"\\f89f\"; }\n.bi-person-fill-down::before { content: \"\\f8a0\"; }\n.bi-person-fill-exclamation::before { content: \"\\f8a1\"; }\n.bi-person-fill-gear::before { content: \"\\f8a2\"; }\n.bi-person-fill-lock::before { content: \"\\f8a3\"; }\n.bi-person-fill-slash::before { content: \"\\f8a4\"; }\n.bi-person-fill-up::before { content: \"\\f8a5\"; }\n.bi-person-fill-x::before { content: \"\\f8a6\"; }\n.bi-person-gear::before { content: \"\\f8a7\"; }\n.bi-person-lock::before { content: \"\\f8a8\"; }\n.bi-person-slash::before { content: \"\\f8a9\"; }\n.bi-person-up::before { content: \"\\f8aa\"; }\n.bi-scooter::before { content: \"\\f8ab\"; }\n.bi-taxi-front-fill::before { content: \"\\f8ac\"; }\n.bi-taxi-front::before { content: \"\\f8ad\"; }\n.bi-amd::before { content: \"\\f8ae\"; }\n.bi-database-add::before { content: \"\\f8af\"; }\n.bi-database-check::before { content: \"\\f8b0\"; }\n.bi-database-dash::before { content: \"\\f8b1\"; }\n.bi-database-down::before { content: \"\\f8b2\"; }\n.bi-database-exclamation::before { content: \"\\f8b3\"; }\n.bi-database-fill-add::before { content: \"\\f8b4\"; }\n.bi-database-fill-check::before { content: \"\\f8b5\"; }\n.bi-database-fill-dash::before { content: \"\\f8b6\"; }\n.bi-database-fill-down::before { content: \"\\f8b7\"; }\n.bi-database-fill-exclamation::before { content: \"\\f8b8\"; }\n.bi-database-fill-gear::before { content: \"\\f8b9\"; }\n.bi-database-fill-lock::before { content: \"\\f8ba\"; }\n.bi-database-fill-slash::before { content: \"\\f8bb\"; }\n.bi-database-fill-up::before { content: \"\\f8bc\"; }\n.bi-database-fill-x::before { content: \"\\f8bd\"; }\n.bi-database-fill::before { content: \"\\f8be\"; }\n.bi-database-gear::before { content: \"\\f8bf\"; }\n.bi-database-lock::before { content: \"\\f8c0\"; }\n.bi-database-slash::before { content: \"\\f8c1\"; }\n.bi-database-up::before { content: \"\\f8c2\"; }\n.bi-database-x::before { content: \"\\f8c3\"; }\n.bi-database::before { content: \"\\f8c4\"; }\n.bi-houses-fill::before { content: \"\\f8c5\"; }\n.bi-houses::before { content: \"\\f8c6\"; }\n.bi-nvidia::before { content: \"\\f8c7\"; }\n.bi-person-vcard-fill::before { content: \"\\f8c8\"; }\n.bi-person-vcard::before { content: \"\\f8c9\"; }\n.bi-sina-weibo::before { content: \"\\f8ca\"; }\n.bi-tencent-qq::before { content: \"\\f8cb\"; }\n.bi-wikipedia::before { content: \"\\f8cc\"; }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap-icons/bootstrap-icons.json",
    "content": "{\n  \"123\": 63103,\n  \"alarm-fill\": 61697,\n  \"alarm\": 61698,\n  \"align-bottom\": 61699,\n  \"align-center\": 61700,\n  \"align-end\": 61701,\n  \"align-middle\": 61702,\n  \"align-start\": 61703,\n  \"align-top\": 61704,\n  \"alt\": 61705,\n  \"app-indicator\": 61706,\n  \"app\": 61707,\n  \"archive-fill\": 61708,\n  \"archive\": 61709,\n  \"arrow-90deg-down\": 61710,\n  \"arrow-90deg-left\": 61711,\n  \"arrow-90deg-right\": 61712,\n  \"arrow-90deg-up\": 61713,\n  \"arrow-bar-down\": 61714,\n  \"arrow-bar-left\": 61715,\n  \"arrow-bar-right\": 61716,\n  \"arrow-bar-up\": 61717,\n  \"arrow-clockwise\": 61718,\n  \"arrow-counterclockwise\": 61719,\n  \"arrow-down-circle-fill\": 61720,\n  \"arrow-down-circle\": 61721,\n  \"arrow-down-left-circle-fill\": 61722,\n  \"arrow-down-left-circle\": 61723,\n  \"arrow-down-left-square-fill\": 61724,\n  \"arrow-down-left-square\": 61725,\n  \"arrow-down-left\": 61726,\n  \"arrow-down-right-circle-fill\": 61727,\n  \"arrow-down-right-circle\": 61728,\n  \"arrow-down-right-square-fill\": 61729,\n  \"arrow-down-right-square\": 61730,\n  \"arrow-down-right\": 61731,\n  \"arrow-down-short\": 61732,\n  \"arrow-down-square-fill\": 61733,\n  \"arrow-down-square\": 61734,\n  \"arrow-down-up\": 61735,\n  \"arrow-down\": 61736,\n  \"arrow-left-circle-fill\": 61737,\n  \"arrow-left-circle\": 61738,\n  \"arrow-left-right\": 61739,\n  \"arrow-left-short\": 61740,\n  \"arrow-left-square-fill\": 61741,\n  \"arrow-left-square\": 61742,\n  \"arrow-left\": 61743,\n  \"arrow-repeat\": 61744,\n  \"arrow-return-left\": 61745,\n  \"arrow-return-right\": 61746,\n  \"arrow-right-circle-fill\": 61747,\n  \"arrow-right-circle\": 61748,\n  \"arrow-right-short\": 61749,\n  \"arrow-right-square-fill\": 61750,\n  \"arrow-right-square\": 61751,\n  \"arrow-right\": 61752,\n  \"arrow-up-circle-fill\": 61753,\n  \"arrow-up-circle\": 61754,\n  \"arrow-up-left-circle-fill\": 61755,\n  \"arrow-up-left-circle\": 61756,\n  \"arrow-up-left-square-fill\": 61757,\n  \"arrow-up-left-square\": 61758,\n  \"arrow-up-left\": 61759,\n  \"arrow-up-right-circle-fill\": 61760,\n  \"arrow-up-right-circle\": 61761,\n  \"arrow-up-right-square-fill\": 61762,\n  \"arrow-up-right-square\": 61763,\n  \"arrow-up-right\": 61764,\n  \"arrow-up-short\": 61765,\n  \"arrow-up-square-fill\": 61766,\n  \"arrow-up-square\": 61767,\n  \"arrow-up\": 61768,\n  \"arrows-angle-contract\": 61769,\n  \"arrows-angle-expand\": 61770,\n  \"arrows-collapse\": 61771,\n  \"arrows-expand\": 61772,\n  \"arrows-fullscreen\": 61773,\n  \"arrows-move\": 61774,\n  \"aspect-ratio-fill\": 61775,\n  \"aspect-ratio\": 61776,\n  \"asterisk\": 61777,\n  \"at\": 61778,\n  \"award-fill\": 61779,\n  \"award\": 61780,\n  \"back\": 61781,\n  \"backspace-fill\": 61782,\n  \"backspace-reverse-fill\": 61783,\n  \"backspace-reverse\": 61784,\n  \"backspace\": 61785,\n  \"badge-3d-fill\": 61786,\n  \"badge-3d\": 61787,\n  \"badge-4k-fill\": 61788,\n  \"badge-4k\": 61789,\n  \"badge-8k-fill\": 61790,\n  \"badge-8k\": 61791,\n  \"badge-ad-fill\": 61792,\n  \"badge-ad\": 61793,\n  \"badge-ar-fill\": 61794,\n  \"badge-ar\": 61795,\n  \"badge-cc-fill\": 61796,\n  \"badge-cc\": 61797,\n  \"badge-hd-fill\": 61798,\n  \"badge-hd\": 61799,\n  \"badge-tm-fill\": 61800,\n  \"badge-tm\": 61801,\n  \"badge-vo-fill\": 61802,\n  \"badge-vo\": 61803,\n  \"badge-vr-fill\": 61804,\n  \"badge-vr\": 61805,\n  \"badge-wc-fill\": 61806,\n  \"badge-wc\": 61807,\n  \"bag-check-fill\": 61808,\n  \"bag-check\": 61809,\n  \"bag-dash-fill\": 61810,\n  \"bag-dash\": 61811,\n  \"bag-fill\": 61812,\n  \"bag-plus-fill\": 61813,\n  \"bag-plus\": 61814,\n  \"bag-x-fill\": 61815,\n  \"bag-x\": 61816,\n  \"bag\": 61817,\n  \"bar-chart-fill\": 61818,\n  \"bar-chart-line-fill\": 61819,\n  \"bar-chart-line\": 61820,\n  \"bar-chart-steps\": 61821,\n  \"bar-chart\": 61822,\n  \"basket-fill\": 61823,\n  \"basket\": 61824,\n  \"basket2-fill\": 61825,\n  \"basket2\": 61826,\n  \"basket3-fill\": 61827,\n  \"basket3\": 61828,\n  \"battery-charging\": 61829,\n  \"battery-full\": 61830,\n  \"battery-half\": 61831,\n  \"battery\": 61832,\n  \"bell-fill\": 61833,\n  \"bell\": 61834,\n  \"bezier\": 61835,\n  \"bezier2\": 61836,\n  \"bicycle\": 61837,\n  \"binoculars-fill\": 61838,\n  \"binoculars\": 61839,\n  \"blockquote-left\": 61840,\n  \"blockquote-right\": 61841,\n  \"book-fill\": 61842,\n  \"book-half\": 61843,\n  \"book\": 61844,\n  \"bookmark-check-fill\": 61845,\n  \"bookmark-check\": 61846,\n  \"bookmark-dash-fill\": 61847,\n  \"bookmark-dash\": 61848,\n  \"bookmark-fill\": 61849,\n  \"bookmark-heart-fill\": 61850,\n  \"bookmark-heart\": 61851,\n  \"bookmark-plus-fill\": 61852,\n  \"bookmark-plus\": 61853,\n  \"bookmark-star-fill\": 61854,\n  \"bookmark-star\": 61855,\n  \"bookmark-x-fill\": 61856,\n  \"bookmark-x\": 61857,\n  \"bookmark\": 61858,\n  \"bookmarks-fill\": 61859,\n  \"bookmarks\": 61860,\n  \"bookshelf\": 61861,\n  \"bootstrap-fill\": 61862,\n  \"bootstrap-reboot\": 61863,\n  \"bootstrap\": 61864,\n  \"border-all\": 61865,\n  \"border-bottom\": 61866,\n  \"border-center\": 61867,\n  \"border-inner\": 61868,\n  \"border-left\": 61869,\n  \"border-middle\": 61870,\n  \"border-outer\": 61871,\n  \"border-right\": 61872,\n  \"border-style\": 61873,\n  \"border-top\": 61874,\n  \"border-width\": 61875,\n  \"border\": 61876,\n  \"bounding-box-circles\": 61877,\n  \"bounding-box\": 61878,\n  \"box-arrow-down-left\": 61879,\n  \"box-arrow-down-right\": 61880,\n  \"box-arrow-down\": 61881,\n  \"box-arrow-in-down-left\": 61882,\n  \"box-arrow-in-down-right\": 61883,\n  \"box-arrow-in-down\": 61884,\n  \"box-arrow-in-left\": 61885,\n  \"box-arrow-in-right\": 61886,\n  \"box-arrow-in-up-left\": 61887,\n  \"box-arrow-in-up-right\": 61888,\n  \"box-arrow-in-up\": 61889,\n  \"box-arrow-left\": 61890,\n  \"box-arrow-right\": 61891,\n  \"box-arrow-up-left\": 61892,\n  \"box-arrow-up-right\": 61893,\n  \"box-arrow-up\": 61894,\n  \"box-seam\": 61895,\n  \"box\": 61896,\n  \"braces\": 61897,\n  \"bricks\": 61898,\n  \"briefcase-fill\": 61899,\n  \"briefcase\": 61900,\n  \"brightness-alt-high-fill\": 61901,\n  \"brightness-alt-high\": 61902,\n  \"brightness-alt-low-fill\": 61903,\n  \"brightness-alt-low\": 61904,\n  \"brightness-high-fill\": 61905,\n  \"brightness-high\": 61906,\n  \"brightness-low-fill\": 61907,\n  \"brightness-low\": 61908,\n  \"broadcast-pin\": 61909,\n  \"broadcast\": 61910,\n  \"brush-fill\": 61911,\n  \"brush\": 61912,\n  \"bucket-fill\": 61913,\n  \"bucket\": 61914,\n  \"bug-fill\": 61915,\n  \"bug\": 61916,\n  \"building\": 61917,\n  \"bullseye\": 61918,\n  \"calculator-fill\": 61919,\n  \"calculator\": 61920,\n  \"calendar-check-fill\": 61921,\n  \"calendar-check\": 61922,\n  \"calendar-date-fill\": 61923,\n  \"calendar-date\": 61924,\n  \"calendar-day-fill\": 61925,\n  \"calendar-day\": 61926,\n  \"calendar-event-fill\": 61927,\n  \"calendar-event\": 61928,\n  \"calendar-fill\": 61929,\n  \"calendar-minus-fill\": 61930,\n  \"calendar-minus\": 61931,\n  \"calendar-month-fill\": 61932,\n  \"calendar-month\": 61933,\n  \"calendar-plus-fill\": 61934,\n  \"calendar-plus\": 61935,\n  \"calendar-range-fill\": 61936,\n  \"calendar-range\": 61937,\n  \"calendar-week-fill\": 61938,\n  \"calendar-week\": 61939,\n  \"calendar-x-fill\": 61940,\n  \"calendar-x\": 61941,\n  \"calendar\": 61942,\n  \"calendar2-check-fill\": 61943,\n  \"calendar2-check\": 61944,\n  \"calendar2-date-fill\": 61945,\n  \"calendar2-date\": 61946,\n  \"calendar2-day-fill\": 61947,\n  \"calendar2-day\": 61948,\n  \"calendar2-event-fill\": 61949,\n  \"calendar2-event\": 61950,\n  \"calendar2-fill\": 61951,\n  \"calendar2-minus-fill\": 61952,\n  \"calendar2-minus\": 61953,\n  \"calendar2-month-fill\": 61954,\n  \"calendar2-month\": 61955,\n  \"calendar2-plus-fill\": 61956,\n  \"calendar2-plus\": 61957,\n  \"calendar2-range-fill\": 61958,\n  \"calendar2-range\": 61959,\n  \"calendar2-week-fill\": 61960,\n  \"calendar2-week\": 61961,\n  \"calendar2-x-fill\": 61962,\n  \"calendar2-x\": 61963,\n  \"calendar2\": 61964,\n  \"calendar3-event-fill\": 61965,\n  \"calendar3-event\": 61966,\n  \"calendar3-fill\": 61967,\n  \"calendar3-range-fill\": 61968,\n  \"calendar3-range\": 61969,\n  \"calendar3-week-fill\": 61970,\n  \"calendar3-week\": 61971,\n  \"calendar3\": 61972,\n  \"calendar4-event\": 61973,\n  \"calendar4-range\": 61974,\n  \"calendar4-week\": 61975,\n  \"calendar4\": 61976,\n  \"camera-fill\": 61977,\n  \"camera-reels-fill\": 61978,\n  \"camera-reels\": 61979,\n  \"camera-video-fill\": 61980,\n  \"camera-video-off-fill\": 61981,\n  \"camera-video-off\": 61982,\n  \"camera-video\": 61983,\n  \"camera\": 61984,\n  \"camera2\": 61985,\n  \"capslock-fill\": 61986,\n  \"capslock\": 61987,\n  \"card-checklist\": 61988,\n  \"card-heading\": 61989,\n  \"card-image\": 61990,\n  \"card-list\": 61991,\n  \"card-text\": 61992,\n  \"caret-down-fill\": 61993,\n  \"caret-down-square-fill\": 61994,\n  \"caret-down-square\": 61995,\n  \"caret-down\": 61996,\n  \"caret-left-fill\": 61997,\n  \"caret-left-square-fill\": 61998,\n  \"caret-left-square\": 61999,\n  \"caret-left\": 62000,\n  \"caret-right-fill\": 62001,\n  \"caret-right-square-fill\": 62002,\n  \"caret-right-square\": 62003,\n  \"caret-right\": 62004,\n  \"caret-up-fill\": 62005,\n  \"caret-up-square-fill\": 62006,\n  \"caret-up-square\": 62007,\n  \"caret-up\": 62008,\n  \"cart-check-fill\": 62009,\n  \"cart-check\": 62010,\n  \"cart-dash-fill\": 62011,\n  \"cart-dash\": 62012,\n  \"cart-fill\": 62013,\n  \"cart-plus-fill\": 62014,\n  \"cart-plus\": 62015,\n  \"cart-x-fill\": 62016,\n  \"cart-x\": 62017,\n  \"cart\": 62018,\n  \"cart2\": 62019,\n  \"cart3\": 62020,\n  \"cart4\": 62021,\n  \"cash-stack\": 62022,\n  \"cash\": 62023,\n  \"cast\": 62024,\n  \"chat-dots-fill\": 62025,\n  \"chat-dots\": 62026,\n  \"chat-fill\": 62027,\n  \"chat-left-dots-fill\": 62028,\n  \"chat-left-dots\": 62029,\n  \"chat-left-fill\": 62030,\n  \"chat-left-quote-fill\": 62031,\n  \"chat-left-quote\": 62032,\n  \"chat-left-text-fill\": 62033,\n  \"chat-left-text\": 62034,\n  \"chat-left\": 62035,\n  \"chat-quote-fill\": 62036,\n  \"chat-quote\": 62037,\n  \"chat-right-dots-fill\": 62038,\n  \"chat-right-dots\": 62039,\n  \"chat-right-fill\": 62040,\n  \"chat-right-quote-fill\": 62041,\n  \"chat-right-quote\": 62042,\n  \"chat-right-text-fill\": 62043,\n  \"chat-right-text\": 62044,\n  \"chat-right\": 62045,\n  \"chat-square-dots-fill\": 62046,\n  \"chat-square-dots\": 62047,\n  \"chat-square-fill\": 62048,\n  \"chat-square-quote-fill\": 62049,\n  \"chat-square-quote\": 62050,\n  \"chat-square-text-fill\": 62051,\n  \"chat-square-text\": 62052,\n  \"chat-square\": 62053,\n  \"chat-text-fill\": 62054,\n  \"chat-text\": 62055,\n  \"chat\": 62056,\n  \"check-all\": 62057,\n  \"check-circle-fill\": 62058,\n  \"check-circle\": 62059,\n  \"check-square-fill\": 62060,\n  \"check-square\": 62061,\n  \"check\": 62062,\n  \"check2-all\": 62063,\n  \"check2-circle\": 62064,\n  \"check2-square\": 62065,\n  \"check2\": 62066,\n  \"chevron-bar-contract\": 62067,\n  \"chevron-bar-down\": 62068,\n  \"chevron-bar-expand\": 62069,\n  \"chevron-bar-left\": 62070,\n  \"chevron-bar-right\": 62071,\n  \"chevron-bar-up\": 62072,\n  \"chevron-compact-down\": 62073,\n  \"chevron-compact-left\": 62074,\n  \"chevron-compact-right\": 62075,\n  \"chevron-compact-up\": 62076,\n  \"chevron-contract\": 62077,\n  \"chevron-double-down\": 62078,\n  \"chevron-double-left\": 62079,\n  \"chevron-double-right\": 62080,\n  \"chevron-double-up\": 62081,\n  \"chevron-down\": 62082,\n  \"chevron-expand\": 62083,\n  \"chevron-left\": 62084,\n  \"chevron-right\": 62085,\n  \"chevron-up\": 62086,\n  \"circle-fill\": 62087,\n  \"circle-half\": 62088,\n  \"circle-square\": 62089,\n  \"circle\": 62090,\n  \"clipboard-check\": 62091,\n  \"clipboard-data\": 62092,\n  \"clipboard-minus\": 62093,\n  \"clipboard-plus\": 62094,\n  \"clipboard-x\": 62095,\n  \"clipboard\": 62096,\n  \"clock-fill\": 62097,\n  \"clock-history\": 62098,\n  \"clock\": 62099,\n  \"cloud-arrow-down-fill\": 62100,\n  \"cloud-arrow-down\": 62101,\n  \"cloud-arrow-up-fill\": 62102,\n  \"cloud-arrow-up\": 62103,\n  \"cloud-check-fill\": 62104,\n  \"cloud-check\": 62105,\n  \"cloud-download-fill\": 62106,\n  \"cloud-download\": 62107,\n  \"cloud-drizzle-fill\": 62108,\n  \"cloud-drizzle\": 62109,\n  \"cloud-fill\": 62110,\n  \"cloud-fog-fill\": 62111,\n  \"cloud-fog\": 62112,\n  \"cloud-fog2-fill\": 62113,\n  \"cloud-fog2\": 62114,\n  \"cloud-hail-fill\": 62115,\n  \"cloud-hail\": 62116,\n  \"cloud-haze-1\": 62117,\n  \"cloud-haze-fill\": 62118,\n  \"cloud-haze\": 62119,\n  \"cloud-haze2-fill\": 62120,\n  \"cloud-lightning-fill\": 62121,\n  \"cloud-lightning-rain-fill\": 62122,\n  \"cloud-lightning-rain\": 62123,\n  \"cloud-lightning\": 62124,\n  \"cloud-minus-fill\": 62125,\n  \"cloud-minus\": 62126,\n  \"cloud-moon-fill\": 62127,\n  \"cloud-moon\": 62128,\n  \"cloud-plus-fill\": 62129,\n  \"cloud-plus\": 62130,\n  \"cloud-rain-fill\": 62131,\n  \"cloud-rain-heavy-fill\": 62132,\n  \"cloud-rain-heavy\": 62133,\n  \"cloud-rain\": 62134,\n  \"cloud-slash-fill\": 62135,\n  \"cloud-slash\": 62136,\n  \"cloud-sleet-fill\": 62137,\n  \"cloud-sleet\": 62138,\n  \"cloud-snow-fill\": 62139,\n  \"cloud-snow\": 62140,\n  \"cloud-sun-fill\": 62141,\n  \"cloud-sun\": 62142,\n  \"cloud-upload-fill\": 62143,\n  \"cloud-upload\": 62144,\n  \"cloud\": 62145,\n  \"clouds-fill\": 62146,\n  \"clouds\": 62147,\n  \"cloudy-fill\": 62148,\n  \"cloudy\": 62149,\n  \"code-slash\": 62150,\n  \"code-square\": 62151,\n  \"code\": 62152,\n  \"collection-fill\": 62153,\n  \"collection-play-fill\": 62154,\n  \"collection-play\": 62155,\n  \"collection\": 62156,\n  \"columns-gap\": 62157,\n  \"columns\": 62158,\n  \"command\": 62159,\n  \"compass-fill\": 62160,\n  \"compass\": 62161,\n  \"cone-striped\": 62162,\n  \"cone\": 62163,\n  \"controller\": 62164,\n  \"cpu-fill\": 62165,\n  \"cpu\": 62166,\n  \"credit-card-2-back-fill\": 62167,\n  \"credit-card-2-back\": 62168,\n  \"credit-card-2-front-fill\": 62169,\n  \"credit-card-2-front\": 62170,\n  \"credit-card-fill\": 62171,\n  \"credit-card\": 62172,\n  \"crop\": 62173,\n  \"cup-fill\": 62174,\n  \"cup-straw\": 62175,\n  \"cup\": 62176,\n  \"cursor-fill\": 62177,\n  \"cursor-text\": 62178,\n  \"cursor\": 62179,\n  \"dash-circle-dotted\": 62180,\n  \"dash-circle-fill\": 62181,\n  \"dash-circle\": 62182,\n  \"dash-square-dotted\": 62183,\n  \"dash-square-fill\": 62184,\n  \"dash-square\": 62185,\n  \"dash\": 62186,\n  \"diagram-2-fill\": 62187,\n  \"diagram-2\": 62188,\n  \"diagram-3-fill\": 62189,\n  \"diagram-3\": 62190,\n  \"diamond-fill\": 62191,\n  \"diamond-half\": 62192,\n  \"diamond\": 62193,\n  \"dice-1-fill\": 62194,\n  \"dice-1\": 62195,\n  \"dice-2-fill\": 62196,\n  \"dice-2\": 62197,\n  \"dice-3-fill\": 62198,\n  \"dice-3\": 62199,\n  \"dice-4-fill\": 62200,\n  \"dice-4\": 62201,\n  \"dice-5-fill\": 62202,\n  \"dice-5\": 62203,\n  \"dice-6-fill\": 62204,\n  \"dice-6\": 62205,\n  \"disc-fill\": 62206,\n  \"disc\": 62207,\n  \"discord\": 62208,\n  \"display-fill\": 62209,\n  \"display\": 62210,\n  \"distribute-horizontal\": 62211,\n  \"distribute-vertical\": 62212,\n  \"door-closed-fill\": 62213,\n  \"door-closed\": 62214,\n  \"door-open-fill\": 62215,\n  \"door-open\": 62216,\n  \"dot\": 62217,\n  \"download\": 62218,\n  \"droplet-fill\": 62219,\n  \"droplet-half\": 62220,\n  \"droplet\": 62221,\n  \"earbuds\": 62222,\n  \"easel-fill\": 62223,\n  \"easel\": 62224,\n  \"egg-fill\": 62225,\n  \"egg-fried\": 62226,\n  \"egg\": 62227,\n  \"eject-fill\": 62228,\n  \"eject\": 62229,\n  \"emoji-angry-fill\": 62230,\n  \"emoji-angry\": 62231,\n  \"emoji-dizzy-fill\": 62232,\n  \"emoji-dizzy\": 62233,\n  \"emoji-expressionless-fill\": 62234,\n  \"emoji-expressionless\": 62235,\n  \"emoji-frown-fill\": 62236,\n  \"emoji-frown\": 62237,\n  \"emoji-heart-eyes-fill\": 62238,\n  \"emoji-heart-eyes\": 62239,\n  \"emoji-laughing-fill\": 62240,\n  \"emoji-laughing\": 62241,\n  \"emoji-neutral-fill\": 62242,\n  \"emoji-neutral\": 62243,\n  \"emoji-smile-fill\": 62244,\n  \"emoji-smile-upside-down-fill\": 62245,\n  \"emoji-smile-upside-down\": 62246,\n  \"emoji-smile\": 62247,\n  \"emoji-sunglasses-fill\": 62248,\n  \"emoji-sunglasses\": 62249,\n  \"emoji-wink-fill\": 62250,\n  \"emoji-wink\": 62251,\n  \"envelope-fill\": 62252,\n  \"envelope-open-fill\": 62253,\n  \"envelope-open\": 62254,\n  \"envelope\": 62255,\n  \"eraser-fill\": 62256,\n  \"eraser\": 62257,\n  \"exclamation-circle-fill\": 62258,\n  \"exclamation-circle\": 62259,\n  \"exclamation-diamond-fill\": 62260,\n  \"exclamation-diamond\": 62261,\n  \"exclamation-octagon-fill\": 62262,\n  \"exclamation-octagon\": 62263,\n  \"exclamation-square-fill\": 62264,\n  \"exclamation-square\": 62265,\n  \"exclamation-triangle-fill\": 62266,\n  \"exclamation-triangle\": 62267,\n  \"exclamation\": 62268,\n  \"exclude\": 62269,\n  \"eye-fill\": 62270,\n  \"eye-slash-fill\": 62271,\n  \"eye-slash\": 62272,\n  \"eye\": 62273,\n  \"eyedropper\": 62274,\n  \"eyeglasses\": 62275,\n  \"facebook\": 62276,\n  \"file-arrow-down-fill\": 62277,\n  \"file-arrow-down\": 62278,\n  \"file-arrow-up-fill\": 62279,\n  \"file-arrow-up\": 62280,\n  \"file-bar-graph-fill\": 62281,\n  \"file-bar-graph\": 62282,\n  \"file-binary-fill\": 62283,\n  \"file-binary\": 62284,\n  \"file-break-fill\": 62285,\n  \"file-break\": 62286,\n  \"file-check-fill\": 62287,\n  \"file-check\": 62288,\n  \"file-code-fill\": 62289,\n  \"file-code\": 62290,\n  \"file-diff-fill\": 62291,\n  \"file-diff\": 62292,\n  \"file-earmark-arrow-down-fill\": 62293,\n  \"file-earmark-arrow-down\": 62294,\n  \"file-earmark-arrow-up-fill\": 62295,\n  \"file-earmark-arrow-up\": 62296,\n  \"file-earmark-bar-graph-fill\": 62297,\n  \"file-earmark-bar-graph\": 62298,\n  \"file-earmark-binary-fill\": 62299,\n  \"file-earmark-binary\": 62300,\n  \"file-earmark-break-fill\": 62301,\n  \"file-earmark-break\": 62302,\n  \"file-earmark-check-fill\": 62303,\n  \"file-earmark-check\": 62304,\n  \"file-earmark-code-fill\": 62305,\n  \"file-earmark-code\": 62306,\n  \"file-earmark-diff-fill\": 62307,\n  \"file-earmark-diff\": 62308,\n  \"file-earmark-easel-fill\": 62309,\n  \"file-earmark-easel\": 62310,\n  \"file-earmark-excel-fill\": 62311,\n  \"file-earmark-excel\": 62312,\n  \"file-earmark-fill\": 62313,\n  \"file-earmark-font-fill\": 62314,\n  \"file-earmark-font\": 62315,\n  \"file-earmark-image-fill\": 62316,\n  \"file-earmark-image\": 62317,\n  \"file-earmark-lock-fill\": 62318,\n  \"file-earmark-lock\": 62319,\n  \"file-earmark-lock2-fill\": 62320,\n  \"file-earmark-lock2\": 62321,\n  \"file-earmark-medical-fill\": 62322,\n  \"file-earmark-medical\": 62323,\n  \"file-earmark-minus-fill\": 62324,\n  \"file-earmark-minus\": 62325,\n  \"file-earmark-music-fill\": 62326,\n  \"file-earmark-music\": 62327,\n  \"file-earmark-person-fill\": 62328,\n  \"file-earmark-person\": 62329,\n  \"file-earmark-play-fill\": 62330,\n  \"file-earmark-play\": 62331,\n  \"file-earmark-plus-fill\": 62332,\n  \"file-earmark-plus\": 62333,\n  \"file-earmark-post-fill\": 62334,\n  \"file-earmark-post\": 62335,\n  \"file-earmark-ppt-fill\": 62336,\n  \"file-earmark-ppt\": 62337,\n  \"file-earmark-richtext-fill\": 62338,\n  \"file-earmark-richtext\": 62339,\n  \"file-earmark-ruled-fill\": 62340,\n  \"file-earmark-ruled\": 62341,\n  \"file-earmark-slides-fill\": 62342,\n  \"file-earmark-slides\": 62343,\n  \"file-earmark-spreadsheet-fill\": 62344,\n  \"file-earmark-spreadsheet\": 62345,\n  \"file-earmark-text-fill\": 62346,\n  \"file-earmark-text\": 62347,\n  \"file-earmark-word-fill\": 62348,\n  \"file-earmark-word\": 62349,\n  \"file-earmark-x-fill\": 62350,\n  \"file-earmark-x\": 62351,\n  \"file-earmark-zip-fill\": 62352,\n  \"file-earmark-zip\": 62353,\n  \"file-earmark\": 62354,\n  \"file-easel-fill\": 62355,\n  \"file-easel\": 62356,\n  \"file-excel-fill\": 62357,\n  \"file-excel\": 62358,\n  \"file-fill\": 62359,\n  \"file-font-fill\": 62360,\n  \"file-font\": 62361,\n  \"file-image-fill\": 62362,\n  \"file-image\": 62363,\n  \"file-lock-fill\": 62364,\n  \"file-lock\": 62365,\n  \"file-lock2-fill\": 62366,\n  \"file-lock2\": 62367,\n  \"file-medical-fill\": 62368,\n  \"file-medical\": 62369,\n  \"file-minus-fill\": 62370,\n  \"file-minus\": 62371,\n  \"file-music-fill\": 62372,\n  \"file-music\": 62373,\n  \"file-person-fill\": 62374,\n  \"file-person\": 62375,\n  \"file-play-fill\": 62376,\n  \"file-play\": 62377,\n  \"file-plus-fill\": 62378,\n  \"file-plus\": 62379,\n  \"file-post-fill\": 62380,\n  \"file-post\": 62381,\n  \"file-ppt-fill\": 62382,\n  \"file-ppt\": 62383,\n  \"file-richtext-fill\": 62384,\n  \"file-richtext\": 62385,\n  \"file-ruled-fill\": 62386,\n  \"file-ruled\": 62387,\n  \"file-slides-fill\": 62388,\n  \"file-slides\": 62389,\n  \"file-spreadsheet-fill\": 62390,\n  \"file-spreadsheet\": 62391,\n  \"file-text-fill\": 62392,\n  \"file-text\": 62393,\n  \"file-word-fill\": 62394,\n  \"file-word\": 62395,\n  \"file-x-fill\": 62396,\n  \"file-x\": 62397,\n  \"file-zip-fill\": 62398,\n  \"file-zip\": 62399,\n  \"file\": 62400,\n  \"files-alt\": 62401,\n  \"files\": 62402,\n  \"film\": 62403,\n  \"filter-circle-fill\": 62404,\n  \"filter-circle\": 62405,\n  \"filter-left\": 62406,\n  \"filter-right\": 62407,\n  \"filter-square-fill\": 62408,\n  \"filter-square\": 62409,\n  \"filter\": 62410,\n  \"flag-fill\": 62411,\n  \"flag\": 62412,\n  \"flower1\": 62413,\n  \"flower2\": 62414,\n  \"flower3\": 62415,\n  \"folder-check\": 62416,\n  \"folder-fill\": 62417,\n  \"folder-minus\": 62418,\n  \"folder-plus\": 62419,\n  \"folder-symlink-fill\": 62420,\n  \"folder-symlink\": 62421,\n  \"folder-x\": 62422,\n  \"folder\": 62423,\n  \"folder2-open\": 62424,\n  \"folder2\": 62425,\n  \"fonts\": 62426,\n  \"forward-fill\": 62427,\n  \"forward\": 62428,\n  \"front\": 62429,\n  \"fullscreen-exit\": 62430,\n  \"fullscreen\": 62431,\n  \"funnel-fill\": 62432,\n  \"funnel\": 62433,\n  \"gear-fill\": 62434,\n  \"gear-wide-connected\": 62435,\n  \"gear-wide\": 62436,\n  \"gear\": 62437,\n  \"gem\": 62438,\n  \"geo-alt-fill\": 62439,\n  \"geo-alt\": 62440,\n  \"geo-fill\": 62441,\n  \"geo\": 62442,\n  \"gift-fill\": 62443,\n  \"gift\": 62444,\n  \"github\": 62445,\n  \"globe\": 62446,\n  \"globe2\": 62447,\n  \"google\": 62448,\n  \"graph-down\": 62449,\n  \"graph-up\": 62450,\n  \"grid-1x2-fill\": 62451,\n  \"grid-1x2\": 62452,\n  \"grid-3x2-gap-fill\": 62453,\n  \"grid-3x2-gap\": 62454,\n  \"grid-3x2\": 62455,\n  \"grid-3x3-gap-fill\": 62456,\n  \"grid-3x3-gap\": 62457,\n  \"grid-3x3\": 62458,\n  \"grid-fill\": 62459,\n  \"grid\": 62460,\n  \"grip-horizontal\": 62461,\n  \"grip-vertical\": 62462,\n  \"hammer\": 62463,\n  \"hand-index-fill\": 62464,\n  \"hand-index-thumb-fill\": 62465,\n  \"hand-index-thumb\": 62466,\n  \"hand-index\": 62467,\n  \"hand-thumbs-down-fill\": 62468,\n  \"hand-thumbs-down\": 62469,\n  \"hand-thumbs-up-fill\": 62470,\n  \"hand-thumbs-up\": 62471,\n  \"handbag-fill\": 62472,\n  \"handbag\": 62473,\n  \"hash\": 62474,\n  \"hdd-fill\": 62475,\n  \"hdd-network-fill\": 62476,\n  \"hdd-network\": 62477,\n  \"hdd-rack-fill\": 62478,\n  \"hdd-rack\": 62479,\n  \"hdd-stack-fill\": 62480,\n  \"hdd-stack\": 62481,\n  \"hdd\": 62482,\n  \"headphones\": 62483,\n  \"headset\": 62484,\n  \"heart-fill\": 62485,\n  \"heart-half\": 62486,\n  \"heart\": 62487,\n  \"heptagon-fill\": 62488,\n  \"heptagon-half\": 62489,\n  \"heptagon\": 62490,\n  \"hexagon-fill\": 62491,\n  \"hexagon-half\": 62492,\n  \"hexagon\": 62493,\n  \"hourglass-bottom\": 62494,\n  \"hourglass-split\": 62495,\n  \"hourglass-top\": 62496,\n  \"hourglass\": 62497,\n  \"house-door-fill\": 62498,\n  \"house-door\": 62499,\n  \"house-fill\": 62500,\n  \"house\": 62501,\n  \"hr\": 62502,\n  \"hurricane\": 62503,\n  \"image-alt\": 62504,\n  \"image-fill\": 62505,\n  \"image\": 62506,\n  \"images\": 62507,\n  \"inbox-fill\": 62508,\n  \"inbox\": 62509,\n  \"inboxes-fill\": 62510,\n  \"inboxes\": 62511,\n  \"info-circle-fill\": 62512,\n  \"info-circle\": 62513,\n  \"info-square-fill\": 62514,\n  \"info-square\": 62515,\n  \"info\": 62516,\n  \"input-cursor-text\": 62517,\n  \"input-cursor\": 62518,\n  \"instagram\": 62519,\n  \"intersect\": 62520,\n  \"journal-album\": 62521,\n  \"journal-arrow-down\": 62522,\n  \"journal-arrow-up\": 62523,\n  \"journal-bookmark-fill\": 62524,\n  \"journal-bookmark\": 62525,\n  \"journal-check\": 62526,\n  \"journal-code\": 62527,\n  \"journal-medical\": 62528,\n  \"journal-minus\": 62529,\n  \"journal-plus\": 62530,\n  \"journal-richtext\": 62531,\n  \"journal-text\": 62532,\n  \"journal-x\": 62533,\n  \"journal\": 62534,\n  \"journals\": 62535,\n  \"joystick\": 62536,\n  \"justify-left\": 62537,\n  \"justify-right\": 62538,\n  \"justify\": 62539,\n  \"kanban-fill\": 62540,\n  \"kanban\": 62541,\n  \"key-fill\": 62542,\n  \"key\": 62543,\n  \"keyboard-fill\": 62544,\n  \"keyboard\": 62545,\n  \"ladder\": 62546,\n  \"lamp-fill\": 62547,\n  \"lamp\": 62548,\n  \"laptop-fill\": 62549,\n  \"laptop\": 62550,\n  \"layer-backward\": 62551,\n  \"layer-forward\": 62552,\n  \"layers-fill\": 62553,\n  \"layers-half\": 62554,\n  \"layers\": 62555,\n  \"layout-sidebar-inset-reverse\": 62556,\n  \"layout-sidebar-inset\": 62557,\n  \"layout-sidebar-reverse\": 62558,\n  \"layout-sidebar\": 62559,\n  \"layout-split\": 62560,\n  \"layout-text-sidebar-reverse\": 62561,\n  \"layout-text-sidebar\": 62562,\n  \"layout-text-window-reverse\": 62563,\n  \"layout-text-window\": 62564,\n  \"layout-three-columns\": 62565,\n  \"layout-wtf\": 62566,\n  \"life-preserver\": 62567,\n  \"lightbulb-fill\": 62568,\n  \"lightbulb-off-fill\": 62569,\n  \"lightbulb-off\": 62570,\n  \"lightbulb\": 62571,\n  \"lightning-charge-fill\": 62572,\n  \"lightning-charge\": 62573,\n  \"lightning-fill\": 62574,\n  \"lightning\": 62575,\n  \"link-45deg\": 62576,\n  \"link\": 62577,\n  \"linkedin\": 62578,\n  \"list-check\": 62579,\n  \"list-nested\": 62580,\n  \"list-ol\": 62581,\n  \"list-stars\": 62582,\n  \"list-task\": 62583,\n  \"list-ul\": 62584,\n  \"list\": 62585,\n  \"lock-fill\": 62586,\n  \"lock\": 62587,\n  \"mailbox\": 62588,\n  \"mailbox2\": 62589,\n  \"map-fill\": 62590,\n  \"map\": 62591,\n  \"markdown-fill\": 62592,\n  \"markdown\": 62593,\n  \"mask\": 62594,\n  \"megaphone-fill\": 62595,\n  \"megaphone\": 62596,\n  \"menu-app-fill\": 62597,\n  \"menu-app\": 62598,\n  \"menu-button-fill\": 62599,\n  \"menu-button-wide-fill\": 62600,\n  \"menu-button-wide\": 62601,\n  \"menu-button\": 62602,\n  \"menu-down\": 62603,\n  \"menu-up\": 62604,\n  \"mic-fill\": 62605,\n  \"mic-mute-fill\": 62606,\n  \"mic-mute\": 62607,\n  \"mic\": 62608,\n  \"minecart-loaded\": 62609,\n  \"minecart\": 62610,\n  \"moisture\": 62611,\n  \"moon-fill\": 62612,\n  \"moon-stars-fill\": 62613,\n  \"moon-stars\": 62614,\n  \"moon\": 62615,\n  \"mouse-fill\": 62616,\n  \"mouse\": 62617,\n  \"mouse2-fill\": 62618,\n  \"mouse2\": 62619,\n  \"mouse3-fill\": 62620,\n  \"mouse3\": 62621,\n  \"music-note-beamed\": 62622,\n  \"music-note-list\": 62623,\n  \"music-note\": 62624,\n  \"music-player-fill\": 62625,\n  \"music-player\": 62626,\n  \"newspaper\": 62627,\n  \"node-minus-fill\": 62628,\n  \"node-minus\": 62629,\n  \"node-plus-fill\": 62630,\n  \"node-plus\": 62631,\n  \"nut-fill\": 62632,\n  \"nut\": 62633,\n  \"octagon-fill\": 62634,\n  \"octagon-half\": 62635,\n  \"octagon\": 62636,\n  \"option\": 62637,\n  \"outlet\": 62638,\n  \"paint-bucket\": 62639,\n  \"palette-fill\": 62640,\n  \"palette\": 62641,\n  \"palette2\": 62642,\n  \"paperclip\": 62643,\n  \"paragraph\": 62644,\n  \"patch-check-fill\": 62645,\n  \"patch-check\": 62646,\n  \"patch-exclamation-fill\": 62647,\n  \"patch-exclamation\": 62648,\n  \"patch-minus-fill\": 62649,\n  \"patch-minus\": 62650,\n  \"patch-plus-fill\": 62651,\n  \"patch-plus\": 62652,\n  \"patch-question-fill\": 62653,\n  \"patch-question\": 62654,\n  \"pause-btn-fill\": 62655,\n  \"pause-btn\": 62656,\n  \"pause-circle-fill\": 62657,\n  \"pause-circle\": 62658,\n  \"pause-fill\": 62659,\n  \"pause\": 62660,\n  \"peace-fill\": 62661,\n  \"peace\": 62662,\n  \"pen-fill\": 62663,\n  \"pen\": 62664,\n  \"pencil-fill\": 62665,\n  \"pencil-square\": 62666,\n  \"pencil\": 62667,\n  \"pentagon-fill\": 62668,\n  \"pentagon-half\": 62669,\n  \"pentagon\": 62670,\n  \"people-fill\": 62671,\n  \"people\": 62672,\n  \"percent\": 62673,\n  \"person-badge-fill\": 62674,\n  \"person-badge\": 62675,\n  \"person-bounding-box\": 62676,\n  \"person-check-fill\": 62677,\n  \"person-check\": 62678,\n  \"person-circle\": 62679,\n  \"person-dash-fill\": 62680,\n  \"person-dash\": 62681,\n  \"person-fill\": 62682,\n  \"person-lines-fill\": 62683,\n  \"person-plus-fill\": 62684,\n  \"person-plus\": 62685,\n  \"person-square\": 62686,\n  \"person-x-fill\": 62687,\n  \"person-x\": 62688,\n  \"person\": 62689,\n  \"phone-fill\": 62690,\n  \"phone-landscape-fill\": 62691,\n  \"phone-landscape\": 62692,\n  \"phone-vibrate-fill\": 62693,\n  \"phone-vibrate\": 62694,\n  \"phone\": 62695,\n  \"pie-chart-fill\": 62696,\n  \"pie-chart\": 62697,\n  \"pin-angle-fill\": 62698,\n  \"pin-angle\": 62699,\n  \"pin-fill\": 62700,\n  \"pin\": 62701,\n  \"pip-fill\": 62702,\n  \"pip\": 62703,\n  \"play-btn-fill\": 62704,\n  \"play-btn\": 62705,\n  \"play-circle-fill\": 62706,\n  \"play-circle\": 62707,\n  \"play-fill\": 62708,\n  \"play\": 62709,\n  \"plug-fill\": 62710,\n  \"plug\": 62711,\n  \"plus-circle-dotted\": 62712,\n  \"plus-circle-fill\": 62713,\n  \"plus-circle\": 62714,\n  \"plus-square-dotted\": 62715,\n  \"plus-square-fill\": 62716,\n  \"plus-square\": 62717,\n  \"plus\": 62718,\n  \"power\": 62719,\n  \"printer-fill\": 62720,\n  \"printer\": 62721,\n  \"puzzle-fill\": 62722,\n  \"puzzle\": 62723,\n  \"question-circle-fill\": 62724,\n  \"question-circle\": 62725,\n  \"question-diamond-fill\": 62726,\n  \"question-diamond\": 62727,\n  \"question-octagon-fill\": 62728,\n  \"question-octagon\": 62729,\n  \"question-square-fill\": 62730,\n  \"question-square\": 62731,\n  \"question\": 62732,\n  \"rainbow\": 62733,\n  \"receipt-cutoff\": 62734,\n  \"receipt\": 62735,\n  \"reception-0\": 62736,\n  \"reception-1\": 62737,\n  \"reception-2\": 62738,\n  \"reception-3\": 62739,\n  \"reception-4\": 62740,\n  \"record-btn-fill\": 62741,\n  \"record-btn\": 62742,\n  \"record-circle-fill\": 62743,\n  \"record-circle\": 62744,\n  \"record-fill\": 62745,\n  \"record\": 62746,\n  \"record2-fill\": 62747,\n  \"record2\": 62748,\n  \"reply-all-fill\": 62749,\n  \"reply-all\": 62750,\n  \"reply-fill\": 62751,\n  \"reply\": 62752,\n  \"rss-fill\": 62753,\n  \"rss\": 62754,\n  \"rulers\": 62755,\n  \"save-fill\": 62756,\n  \"save\": 62757,\n  \"save2-fill\": 62758,\n  \"save2\": 62759,\n  \"scissors\": 62760,\n  \"screwdriver\": 62761,\n  \"search\": 62762,\n  \"segmented-nav\": 62763,\n  \"server\": 62764,\n  \"share-fill\": 62765,\n  \"share\": 62766,\n  \"shield-check\": 62767,\n  \"shield-exclamation\": 62768,\n  \"shield-fill-check\": 62769,\n  \"shield-fill-exclamation\": 62770,\n  \"shield-fill-minus\": 62771,\n  \"shield-fill-plus\": 62772,\n  \"shield-fill-x\": 62773,\n  \"shield-fill\": 62774,\n  \"shield-lock-fill\": 62775,\n  \"shield-lock\": 62776,\n  \"shield-minus\": 62777,\n  \"shield-plus\": 62778,\n  \"shield-shaded\": 62779,\n  \"shield-slash-fill\": 62780,\n  \"shield-slash\": 62781,\n  \"shield-x\": 62782,\n  \"shield\": 62783,\n  \"shift-fill\": 62784,\n  \"shift\": 62785,\n  \"shop-window\": 62786,\n  \"shop\": 62787,\n  \"shuffle\": 62788,\n  \"signpost-2-fill\": 62789,\n  \"signpost-2\": 62790,\n  \"signpost-fill\": 62791,\n  \"signpost-split-fill\": 62792,\n  \"signpost-split\": 62793,\n  \"signpost\": 62794,\n  \"sim-fill\": 62795,\n  \"sim\": 62796,\n  \"skip-backward-btn-fill\": 62797,\n  \"skip-backward-btn\": 62798,\n  \"skip-backward-circle-fill\": 62799,\n  \"skip-backward-circle\": 62800,\n  \"skip-backward-fill\": 62801,\n  \"skip-backward\": 62802,\n  \"skip-end-btn-fill\": 62803,\n  \"skip-end-btn\": 62804,\n  \"skip-end-circle-fill\": 62805,\n  \"skip-end-circle\": 62806,\n  \"skip-end-fill\": 62807,\n  \"skip-end\": 62808,\n  \"skip-forward-btn-fill\": 62809,\n  \"skip-forward-btn\": 62810,\n  \"skip-forward-circle-fill\": 62811,\n  \"skip-forward-circle\": 62812,\n  \"skip-forward-fill\": 62813,\n  \"skip-forward\": 62814,\n  \"skip-start-btn-fill\": 62815,\n  \"skip-start-btn\": 62816,\n  \"skip-start-circle-fill\": 62817,\n  \"skip-start-circle\": 62818,\n  \"skip-start-fill\": 62819,\n  \"skip-start\": 62820,\n  \"slack\": 62821,\n  \"slash-circle-fill\": 62822,\n  \"slash-circle\": 62823,\n  \"slash-square-fill\": 62824,\n  \"slash-square\": 62825,\n  \"slash\": 62826,\n  \"sliders\": 62827,\n  \"smartwatch\": 62828,\n  \"snow\": 62829,\n  \"snow2\": 62830,\n  \"snow3\": 62831,\n  \"sort-alpha-down-alt\": 62832,\n  \"sort-alpha-down\": 62833,\n  \"sort-alpha-up-alt\": 62834,\n  \"sort-alpha-up\": 62835,\n  \"sort-down-alt\": 62836,\n  \"sort-down\": 62837,\n  \"sort-numeric-down-alt\": 62838,\n  \"sort-numeric-down\": 62839,\n  \"sort-numeric-up-alt\": 62840,\n  \"sort-numeric-up\": 62841,\n  \"sort-up-alt\": 62842,\n  \"sort-up\": 62843,\n  \"soundwave\": 62844,\n  \"speaker-fill\": 62845,\n  \"speaker\": 62846,\n  \"speedometer\": 62847,\n  \"speedometer2\": 62848,\n  \"spellcheck\": 62849,\n  \"square-fill\": 62850,\n  \"square-half\": 62851,\n  \"square\": 62852,\n  \"stack\": 62853,\n  \"star-fill\": 62854,\n  \"star-half\": 62855,\n  \"star\": 62856,\n  \"stars\": 62857,\n  \"stickies-fill\": 62858,\n  \"stickies\": 62859,\n  \"sticky-fill\": 62860,\n  \"sticky\": 62861,\n  \"stop-btn-fill\": 62862,\n  \"stop-btn\": 62863,\n  \"stop-circle-fill\": 62864,\n  \"stop-circle\": 62865,\n  \"stop-fill\": 62866,\n  \"stop\": 62867,\n  \"stoplights-fill\": 62868,\n  \"stoplights\": 62869,\n  \"stopwatch-fill\": 62870,\n  \"stopwatch\": 62871,\n  \"subtract\": 62872,\n  \"suit-club-fill\": 62873,\n  \"suit-club\": 62874,\n  \"suit-diamond-fill\": 62875,\n  \"suit-diamond\": 62876,\n  \"suit-heart-fill\": 62877,\n  \"suit-heart\": 62878,\n  \"suit-spade-fill\": 62879,\n  \"suit-spade\": 62880,\n  \"sun-fill\": 62881,\n  \"sun\": 62882,\n  \"sunglasses\": 62883,\n  \"sunrise-fill\": 62884,\n  \"sunrise\": 62885,\n  \"sunset-fill\": 62886,\n  \"sunset\": 62887,\n  \"symmetry-horizontal\": 62888,\n  \"symmetry-vertical\": 62889,\n  \"table\": 62890,\n  \"tablet-fill\": 62891,\n  \"tablet-landscape-fill\": 62892,\n  \"tablet-landscape\": 62893,\n  \"tablet\": 62894,\n  \"tag-fill\": 62895,\n  \"tag\": 62896,\n  \"tags-fill\": 62897,\n  \"tags\": 62898,\n  \"telegram\": 62899,\n  \"telephone-fill\": 62900,\n  \"telephone-forward-fill\": 62901,\n  \"telephone-forward\": 62902,\n  \"telephone-inbound-fill\": 62903,\n  \"telephone-inbound\": 62904,\n  \"telephone-minus-fill\": 62905,\n  \"telephone-minus\": 62906,\n  \"telephone-outbound-fill\": 62907,\n  \"telephone-outbound\": 62908,\n  \"telephone-plus-fill\": 62909,\n  \"telephone-plus\": 62910,\n  \"telephone-x-fill\": 62911,\n  \"telephone-x\": 62912,\n  \"telephone\": 62913,\n  \"terminal-fill\": 62914,\n  \"terminal\": 62915,\n  \"text-center\": 62916,\n  \"text-indent-left\": 62917,\n  \"text-indent-right\": 62918,\n  \"text-left\": 62919,\n  \"text-paragraph\": 62920,\n  \"text-right\": 62921,\n  \"textarea-resize\": 62922,\n  \"textarea-t\": 62923,\n  \"textarea\": 62924,\n  \"thermometer-half\": 62925,\n  \"thermometer-high\": 62926,\n  \"thermometer-low\": 62927,\n  \"thermometer-snow\": 62928,\n  \"thermometer-sun\": 62929,\n  \"thermometer\": 62930,\n  \"three-dots-vertical\": 62931,\n  \"three-dots\": 62932,\n  \"toggle-off\": 62933,\n  \"toggle-on\": 62934,\n  \"toggle2-off\": 62935,\n  \"toggle2-on\": 62936,\n  \"toggles\": 62937,\n  \"toggles2\": 62938,\n  \"tools\": 62939,\n  \"tornado\": 62940,\n  \"trash-fill\": 62941,\n  \"trash\": 62942,\n  \"trash2-fill\": 62943,\n  \"trash2\": 62944,\n  \"tree-fill\": 62945,\n  \"tree\": 62946,\n  \"triangle-fill\": 62947,\n  \"triangle-half\": 62948,\n  \"triangle\": 62949,\n  \"trophy-fill\": 62950,\n  \"trophy\": 62951,\n  \"tropical-storm\": 62952,\n  \"truck-flatbed\": 62953,\n  \"truck\": 62954,\n  \"tsunami\": 62955,\n  \"tv-fill\": 62956,\n  \"tv\": 62957,\n  \"twitch\": 62958,\n  \"twitter\": 62959,\n  \"type-bold\": 62960,\n  \"type-h1\": 62961,\n  \"type-h2\": 62962,\n  \"type-h3\": 62963,\n  \"type-italic\": 62964,\n  \"type-strikethrough\": 62965,\n  \"type-underline\": 62966,\n  \"type\": 62967,\n  \"ui-checks-grid\": 62968,\n  \"ui-checks\": 62969,\n  \"ui-radios-grid\": 62970,\n  \"ui-radios\": 62971,\n  \"umbrella-fill\": 62972,\n  \"umbrella\": 62973,\n  \"union\": 62974,\n  \"unlock-fill\": 62975,\n  \"unlock\": 62976,\n  \"upc-scan\": 62977,\n  \"upc\": 62978,\n  \"upload\": 62979,\n  \"vector-pen\": 62980,\n  \"view-list\": 62981,\n  \"view-stacked\": 62982,\n  \"vinyl-fill\": 62983,\n  \"vinyl\": 62984,\n  \"voicemail\": 62985,\n  \"volume-down-fill\": 62986,\n  \"volume-down\": 62987,\n  \"volume-mute-fill\": 62988,\n  \"volume-mute\": 62989,\n  \"volume-off-fill\": 62990,\n  \"volume-off\": 62991,\n  \"volume-up-fill\": 62992,\n  \"volume-up\": 62993,\n  \"vr\": 62994,\n  \"wallet-fill\": 62995,\n  \"wallet\": 62996,\n  \"wallet2\": 62997,\n  \"watch\": 62998,\n  \"water\": 62999,\n  \"whatsapp\": 63000,\n  \"wifi-1\": 63001,\n  \"wifi-2\": 63002,\n  \"wifi-off\": 63003,\n  \"wifi\": 63004,\n  \"wind\": 63005,\n  \"window-dock\": 63006,\n  \"window-sidebar\": 63007,\n  \"window\": 63008,\n  \"wrench\": 63009,\n  \"x-circle-fill\": 63010,\n  \"x-circle\": 63011,\n  \"x-diamond-fill\": 63012,\n  \"x-diamond\": 63013,\n  \"x-octagon-fill\": 63014,\n  \"x-octagon\": 63015,\n  \"x-square-fill\": 63016,\n  \"x-square\": 63017,\n  \"x\": 63018,\n  \"youtube\": 63019,\n  \"zoom-in\": 63020,\n  \"zoom-out\": 63021,\n  \"bank\": 63022,\n  \"bank2\": 63023,\n  \"bell-slash-fill\": 63024,\n  \"bell-slash\": 63025,\n  \"cash-coin\": 63026,\n  \"check-lg\": 63027,\n  \"coin\": 63028,\n  \"currency-bitcoin\": 63029,\n  \"currency-dollar\": 63030,\n  \"currency-euro\": 63031,\n  \"currency-exchange\": 63032,\n  \"currency-pound\": 63033,\n  \"currency-yen\": 63034,\n  \"dash-lg\": 63035,\n  \"exclamation-lg\": 63036,\n  \"file-earmark-pdf-fill\": 63037,\n  \"file-earmark-pdf\": 63038,\n  \"file-pdf-fill\": 63039,\n  \"file-pdf\": 63040,\n  \"gender-ambiguous\": 63041,\n  \"gender-female\": 63042,\n  \"gender-male\": 63043,\n  \"gender-trans\": 63044,\n  \"headset-vr\": 63045,\n  \"info-lg\": 63046,\n  \"mastodon\": 63047,\n  \"messenger\": 63048,\n  \"piggy-bank-fill\": 63049,\n  \"piggy-bank\": 63050,\n  \"pin-map-fill\": 63051,\n  \"pin-map\": 63052,\n  \"plus-lg\": 63053,\n  \"question-lg\": 63054,\n  \"recycle\": 63055,\n  \"reddit\": 63056,\n  \"safe-fill\": 63057,\n  \"safe2-fill\": 63058,\n  \"safe2\": 63059,\n  \"sd-card-fill\": 63060,\n  \"sd-card\": 63061,\n  \"skype\": 63062,\n  \"slash-lg\": 63063,\n  \"translate\": 63064,\n  \"x-lg\": 63065,\n  \"safe\": 63066,\n  \"apple\": 63067,\n  \"microsoft\": 63069,\n  \"windows\": 63070,\n  \"behance\": 63068,\n  \"dribbble\": 63071,\n  \"line\": 63072,\n  \"medium\": 63073,\n  \"paypal\": 63074,\n  \"pinterest\": 63075,\n  \"signal\": 63076,\n  \"snapchat\": 63077,\n  \"spotify\": 63078,\n  \"stack-overflow\": 63079,\n  \"strava\": 63080,\n  \"wordpress\": 63081,\n  \"vimeo\": 63082,\n  \"activity\": 63083,\n  \"easel2-fill\": 63084,\n  \"easel2\": 63085,\n  \"easel3-fill\": 63086,\n  \"easel3\": 63087,\n  \"fan\": 63088,\n  \"fingerprint\": 63089,\n  \"graph-down-arrow\": 63090,\n  \"graph-up-arrow\": 63091,\n  \"hypnotize\": 63092,\n  \"magic\": 63093,\n  \"person-rolodex\": 63094,\n  \"person-video\": 63095,\n  \"person-video2\": 63096,\n  \"person-video3\": 63097,\n  \"person-workspace\": 63098,\n  \"radioactive\": 63099,\n  \"webcam-fill\": 63100,\n  \"webcam\": 63101,\n  \"yin-yang\": 63102,\n  \"bandaid-fill\": 63104,\n  \"bandaid\": 63105,\n  \"bluetooth\": 63106,\n  \"body-text\": 63107,\n  \"boombox\": 63108,\n  \"boxes\": 63109,\n  \"dpad-fill\": 63110,\n  \"dpad\": 63111,\n  \"ear-fill\": 63112,\n  \"ear\": 63113,\n  \"envelope-check-1\": 63114,\n  \"envelope-check-fill\": 63115,\n  \"envelope-check\": 63116,\n  \"envelope-dash-1\": 63117,\n  \"envelope-dash-fill\": 63118,\n  \"envelope-dash\": 63119,\n  \"envelope-exclamation-1\": 63120,\n  \"envelope-exclamation-fill\": 63121,\n  \"envelope-exclamation\": 63122,\n  \"envelope-plus-fill\": 63123,\n  \"envelope-plus\": 63124,\n  \"envelope-slash-1\": 63125,\n  \"envelope-slash-fill\": 63126,\n  \"envelope-slash\": 63127,\n  \"envelope-x-1\": 63128,\n  \"envelope-x-fill\": 63129,\n  \"envelope-x\": 63130,\n  \"explicit-fill\": 63131,\n  \"explicit\": 63132,\n  \"git\": 63133,\n  \"infinity\": 63134,\n  \"list-columns-reverse\": 63135,\n  \"list-columns\": 63136,\n  \"meta\": 63137,\n  \"mortorboard-fill\": 63138,\n  \"mortorboard\": 63139,\n  \"nintendo-switch\": 63140,\n  \"pc-display-horizontal\": 63141,\n  \"pc-display\": 63142,\n  \"pc-horizontal\": 63143,\n  \"pc\": 63144,\n  \"playstation\": 63145,\n  \"plus-slash-minus\": 63146,\n  \"projector-fill\": 63147,\n  \"projector\": 63148,\n  \"qr-code-scan\": 63149,\n  \"qr-code\": 63150,\n  \"quora\": 63151,\n  \"quote\": 63152,\n  \"robot\": 63153,\n  \"send-check-fill\": 63154,\n  \"send-check\": 63155,\n  \"send-dash-fill\": 63156,\n  \"send-dash\": 63157,\n  \"send-exclamation-1\": 63158,\n  \"send-exclamation-fill\": 63159,\n  \"send-exclamation\": 63160,\n  \"send-fill\": 63161,\n  \"send-plus-fill\": 63162,\n  \"send-plus\": 63163,\n  \"send-slash-fill\": 63164,\n  \"send-slash\": 63165,\n  \"send-x-fill\": 63166,\n  \"send-x\": 63167,\n  \"send\": 63168,\n  \"steam\": 63169,\n  \"terminal-dash-1\": 63170,\n  \"terminal-dash\": 63171,\n  \"terminal-plus\": 63172,\n  \"terminal-split\": 63173,\n  \"ticket-detailed-fill\": 63174,\n  \"ticket-detailed\": 63175,\n  \"ticket-fill\": 63176,\n  \"ticket-perforated-fill\": 63177,\n  \"ticket-perforated\": 63178,\n  \"ticket\": 63179,\n  \"tiktok\": 63180,\n  \"window-dash\": 63181,\n  \"window-desktop\": 63182,\n  \"window-fullscreen\": 63183,\n  \"window-plus\": 63184,\n  \"window-split\": 63185,\n  \"window-stack\": 63186,\n  \"window-x\": 63187,\n  \"xbox\": 63188,\n  \"ethernet\": 63189,\n  \"hdmi-fill\": 63190,\n  \"hdmi\": 63191,\n  \"usb-c-fill\": 63192,\n  \"usb-c\": 63193,\n  \"usb-fill\": 63194,\n  \"usb-plug-fill\": 63195,\n  \"usb-plug\": 63196,\n  \"usb-symbol\": 63197,\n  \"usb\": 63198,\n  \"boombox-fill\": 63199,\n  \"displayport-1\": 63200,\n  \"displayport\": 63201,\n  \"gpu-card\": 63202,\n  \"memory\": 63203,\n  \"modem-fill\": 63204,\n  \"modem\": 63205,\n  \"motherboard-fill\": 63206,\n  \"motherboard\": 63207,\n  \"optical-audio-fill\": 63208,\n  \"optical-audio\": 63209,\n  \"pci-card\": 63210,\n  \"router-fill\": 63211,\n  \"router\": 63212,\n  \"ssd-fill\": 63213,\n  \"ssd\": 63214,\n  \"thunderbolt-fill\": 63215,\n  \"thunderbolt\": 63216,\n  \"usb-drive-fill\": 63217,\n  \"usb-drive\": 63218,\n  \"usb-micro-fill\": 63219,\n  \"usb-micro\": 63220,\n  \"usb-mini-fill\": 63221,\n  \"usb-mini\": 63222,\n  \"cloud-haze2\": 63223,\n  \"device-hdd-fill\": 63224,\n  \"device-hdd\": 63225,\n  \"device-ssd-fill\": 63226,\n  \"device-ssd\": 63227,\n  \"displayport-fill\": 63228,\n  \"mortarboard-fill\": 63229,\n  \"mortarboard\": 63230,\n  \"terminal-x\": 63231,\n  \"arrow-through-heart-fill\": 63232,\n  \"arrow-through-heart\": 63233,\n  \"badge-sd-fill\": 63234,\n  \"badge-sd\": 63235,\n  \"bag-heart-fill\": 63236,\n  \"bag-heart\": 63237,\n  \"balloon-fill\": 63238,\n  \"balloon-heart-fill\": 63239,\n  \"balloon-heart\": 63240,\n  \"balloon\": 63241,\n  \"box2-fill\": 63242,\n  \"box2-heart-fill\": 63243,\n  \"box2-heart\": 63244,\n  \"box2\": 63245,\n  \"braces-asterisk\": 63246,\n  \"calendar-heart-fill\": 63247,\n  \"calendar-heart\": 63248,\n  \"calendar2-heart-fill\": 63249,\n  \"calendar2-heart\": 63250,\n  \"chat-heart-fill\": 63251,\n  \"chat-heart\": 63252,\n  \"chat-left-heart-fill\": 63253,\n  \"chat-left-heart\": 63254,\n  \"chat-right-heart-fill\": 63255,\n  \"chat-right-heart\": 63256,\n  \"chat-square-heart-fill\": 63257,\n  \"chat-square-heart\": 63258,\n  \"clipboard-check-fill\": 63259,\n  \"clipboard-data-fill\": 63260,\n  \"clipboard-fill\": 63261,\n  \"clipboard-heart-fill\": 63262,\n  \"clipboard-heart\": 63263,\n  \"clipboard-minus-fill\": 63264,\n  \"clipboard-plus-fill\": 63265,\n  \"clipboard-pulse\": 63266,\n  \"clipboard-x-fill\": 63267,\n  \"clipboard2-check-fill\": 63268,\n  \"clipboard2-check\": 63269,\n  \"clipboard2-data-fill\": 63270,\n  \"clipboard2-data\": 63271,\n  \"clipboard2-fill\": 63272,\n  \"clipboard2-heart-fill\": 63273,\n  \"clipboard2-heart\": 63274,\n  \"clipboard2-minus-fill\": 63275,\n  \"clipboard2-minus\": 63276,\n  \"clipboard2-plus-fill\": 63277,\n  \"clipboard2-plus\": 63278,\n  \"clipboard2-pulse-fill\": 63279,\n  \"clipboard2-pulse\": 63280,\n  \"clipboard2-x-fill\": 63281,\n  \"clipboard2-x\": 63282,\n  \"clipboard2\": 63283,\n  \"emoji-kiss-fill\": 63284,\n  \"emoji-kiss\": 63285,\n  \"envelope-heart-fill\": 63286,\n  \"envelope-heart\": 63287,\n  \"envelope-open-heart-fill\": 63288,\n  \"envelope-open-heart\": 63289,\n  \"envelope-paper-fill\": 63290,\n  \"envelope-paper-heart-fill\": 63291,\n  \"envelope-paper-heart\": 63292,\n  \"envelope-paper\": 63293,\n  \"filetype-aac\": 63294,\n  \"filetype-ai\": 63295,\n  \"filetype-bmp\": 63296,\n  \"filetype-cs\": 63297,\n  \"filetype-css\": 63298,\n  \"filetype-csv\": 63299,\n  \"filetype-doc\": 63300,\n  \"filetype-docx\": 63301,\n  \"filetype-exe\": 63302,\n  \"filetype-gif\": 63303,\n  \"filetype-heic\": 63304,\n  \"filetype-html\": 63305,\n  \"filetype-java\": 63306,\n  \"filetype-jpg\": 63307,\n  \"filetype-js\": 63308,\n  \"filetype-jsx\": 63309,\n  \"filetype-key\": 63310,\n  \"filetype-m4p\": 63311,\n  \"filetype-md\": 63312,\n  \"filetype-mdx\": 63313,\n  \"filetype-mov\": 63314,\n  \"filetype-mp3\": 63315,\n  \"filetype-mp4\": 63316,\n  \"filetype-otf\": 63317,\n  \"filetype-pdf\": 63318,\n  \"filetype-php\": 63319,\n  \"filetype-png\": 63320,\n  \"filetype-ppt-1\": 63321,\n  \"filetype-ppt\": 63322,\n  \"filetype-psd\": 63323,\n  \"filetype-py\": 63324,\n  \"filetype-raw\": 63325,\n  \"filetype-rb\": 63326,\n  \"filetype-sass\": 63327,\n  \"filetype-scss\": 63328,\n  \"filetype-sh\": 63329,\n  \"filetype-svg\": 63330,\n  \"filetype-tiff\": 63331,\n  \"filetype-tsx\": 63332,\n  \"filetype-ttf\": 63333,\n  \"filetype-txt\": 63334,\n  \"filetype-wav\": 63335,\n  \"filetype-woff\": 63336,\n  \"filetype-xls-1\": 63337,\n  \"filetype-xls\": 63338,\n  \"filetype-xml\": 63339,\n  \"filetype-yml\": 63340,\n  \"heart-arrow\": 63341,\n  \"heart-pulse-fill\": 63342,\n  \"heart-pulse\": 63343,\n  \"heartbreak-fill\": 63344,\n  \"heartbreak\": 63345,\n  \"hearts\": 63346,\n  \"hospital-fill\": 63347,\n  \"hospital\": 63348,\n  \"house-heart-fill\": 63349,\n  \"house-heart\": 63350,\n  \"incognito\": 63351,\n  \"magnet-fill\": 63352,\n  \"magnet\": 63353,\n  \"person-heart\": 63354,\n  \"person-hearts\": 63355,\n  \"phone-flip\": 63356,\n  \"plugin\": 63357,\n  \"postage-fill\": 63358,\n  \"postage-heart-fill\": 63359,\n  \"postage-heart\": 63360,\n  \"postage\": 63361,\n  \"postcard-fill\": 63362,\n  \"postcard-heart-fill\": 63363,\n  \"postcard-heart\": 63364,\n  \"postcard\": 63365,\n  \"search-heart-fill\": 63366,\n  \"search-heart\": 63367,\n  \"sliders2-vertical\": 63368,\n  \"sliders2\": 63369,\n  \"trash3-fill\": 63370,\n  \"trash3\": 63371,\n  \"valentine\": 63372,\n  \"valentine2\": 63373,\n  \"wrench-adjustable-circle-fill\": 63374,\n  \"wrench-adjustable-circle\": 63375,\n  \"wrench-adjustable\": 63376,\n  \"filetype-json\": 63377,\n  \"filetype-pptx\": 63378,\n  \"filetype-xlsx\": 63379,\n  \"1-circle-1\": 63380,\n  \"1-circle-fill-1\": 63381,\n  \"1-circle-fill\": 63382,\n  \"1-circle\": 63383,\n  \"1-square-fill\": 63384,\n  \"1-square\": 63385,\n  \"2-circle-1\": 63386,\n  \"2-circle-fill-1\": 63387,\n  \"2-circle-fill\": 63388,\n  \"2-circle\": 63389,\n  \"2-square-fill\": 63390,\n  \"2-square\": 63391,\n  \"3-circle-1\": 63392,\n  \"3-circle-fill-1\": 63393,\n  \"3-circle-fill\": 63394,\n  \"3-circle\": 63395,\n  \"3-square-fill\": 63396,\n  \"3-square\": 63397,\n  \"4-circle-1\": 63398,\n  \"4-circle-fill-1\": 63399,\n  \"4-circle-fill\": 63400,\n  \"4-circle\": 63401,\n  \"4-square-fill\": 63402,\n  \"4-square\": 63403,\n  \"5-circle-1\": 63404,\n  \"5-circle-fill-1\": 63405,\n  \"5-circle-fill\": 63406,\n  \"5-circle\": 63407,\n  \"5-square-fill\": 63408,\n  \"5-square\": 63409,\n  \"6-circle-1\": 63410,\n  \"6-circle-fill-1\": 63411,\n  \"6-circle-fill\": 63412,\n  \"6-circle\": 63413,\n  \"6-square-fill\": 63414,\n  \"6-square\": 63415,\n  \"7-circle-1\": 63416,\n  \"7-circle-fill-1\": 63417,\n  \"7-circle-fill\": 63418,\n  \"7-circle\": 63419,\n  \"7-square-fill\": 63420,\n  \"7-square\": 63421,\n  \"8-circle-1\": 63422,\n  \"8-circle-fill-1\": 63423,\n  \"8-circle-fill\": 63424,\n  \"8-circle\": 63425,\n  \"8-square-fill\": 63426,\n  \"8-square\": 63427,\n  \"9-circle-1\": 63428,\n  \"9-circle-fill-1\": 63429,\n  \"9-circle-fill\": 63430,\n  \"9-circle\": 63431,\n  \"9-square-fill\": 63432,\n  \"9-square\": 63433,\n  \"airplane-engines-fill\": 63434,\n  \"airplane-engines\": 63435,\n  \"airplane-fill\": 63436,\n  \"airplane\": 63437,\n  \"alexa\": 63438,\n  \"alipay\": 63439,\n  \"android\": 63440,\n  \"android2\": 63441,\n  \"box-fill\": 63442,\n  \"box-seam-fill\": 63443,\n  \"browser-chrome\": 63444,\n  \"browser-edge\": 63445,\n  \"browser-firefox\": 63446,\n  \"browser-safari\": 63447,\n  \"c-circle-1\": 63448,\n  \"c-circle-fill-1\": 63449,\n  \"c-circle-fill\": 63450,\n  \"c-circle\": 63451,\n  \"c-square-fill\": 63452,\n  \"c-square\": 63453,\n  \"capsule-pill\": 63454,\n  \"capsule\": 63455,\n  \"car-front-fill\": 63456,\n  \"car-front\": 63457,\n  \"cassette-fill\": 63458,\n  \"cassette\": 63459,\n  \"cc-circle-1\": 63460,\n  \"cc-circle-fill-1\": 63461,\n  \"cc-circle-fill\": 63462,\n  \"cc-circle\": 63463,\n  \"cc-square-fill\": 63464,\n  \"cc-square\": 63465,\n  \"cup-hot-fill\": 63466,\n  \"cup-hot\": 63467,\n  \"currency-rupee\": 63468,\n  \"dropbox\": 63469,\n  \"escape\": 63470,\n  \"fast-forward-btn-fill\": 63471,\n  \"fast-forward-btn\": 63472,\n  \"fast-forward-circle-fill\": 63473,\n  \"fast-forward-circle\": 63474,\n  \"fast-forward-fill\": 63475,\n  \"fast-forward\": 63476,\n  \"filetype-sql\": 63477,\n  \"fire\": 63478,\n  \"google-play\": 63479,\n  \"h-circle-1\": 63480,\n  \"h-circle-fill-1\": 63481,\n  \"h-circle-fill\": 63482,\n  \"h-circle\": 63483,\n  \"h-square-fill\": 63484,\n  \"h-square\": 63485,\n  \"indent\": 63486,\n  \"lungs-fill\": 63487,\n  \"lungs\": 63488,\n  \"microsoft-teams\": 63489,\n  \"p-circle-1\": 63490,\n  \"p-circle-fill-1\": 63491,\n  \"p-circle-fill\": 63492,\n  \"p-circle\": 63493,\n  \"p-square-fill\": 63494,\n  \"p-square\": 63495,\n  \"pass-fill\": 63496,\n  \"pass\": 63497,\n  \"prescription\": 63498,\n  \"prescription2\": 63499,\n  \"r-circle-1\": 63500,\n  \"r-circle-fill-1\": 63501,\n  \"r-circle-fill\": 63502,\n  \"r-circle\": 63503,\n  \"r-square-fill\": 63504,\n  \"r-square\": 63505,\n  \"repeat-1\": 63506,\n  \"repeat\": 63507,\n  \"rewind-btn-fill\": 63508,\n  \"rewind-btn\": 63509,\n  \"rewind-circle-fill\": 63510,\n  \"rewind-circle\": 63511,\n  \"rewind-fill\": 63512,\n  \"rewind\": 63513,\n  \"train-freight-front-fill\": 63514,\n  \"train-freight-front\": 63515,\n  \"train-front-fill\": 63516,\n  \"train-front\": 63517,\n  \"train-lightrail-front-fill\": 63518,\n  \"train-lightrail-front\": 63519,\n  \"truck-front-fill\": 63520,\n  \"truck-front\": 63521,\n  \"ubuntu\": 63522,\n  \"unindent\": 63523,\n  \"unity\": 63524,\n  \"universal-access-circle\": 63525,\n  \"universal-access\": 63526,\n  \"virus\": 63527,\n  \"virus2\": 63528,\n  \"wechat\": 63529,\n  \"yelp\": 63530,\n  \"sign-stop-fill\": 63531,\n  \"sign-stop-lights-fill\": 63532,\n  \"sign-stop-lights\": 63533,\n  \"sign-stop\": 63534,\n  \"sign-turn-left-fill\": 63535,\n  \"sign-turn-left\": 63536,\n  \"sign-turn-right-fill\": 63537,\n  \"sign-turn-right\": 63538,\n  \"sign-turn-slight-left-fill\": 63539,\n  \"sign-turn-slight-left\": 63540,\n  \"sign-turn-slight-right-fill\": 63541,\n  \"sign-turn-slight-right\": 63542,\n  \"sign-yield-fill\": 63543,\n  \"sign-yield\": 63544,\n  \"ev-station-fill\": 63545,\n  \"ev-station\": 63546,\n  \"fuel-pump-diesel-fill\": 63547,\n  \"fuel-pump-diesel\": 63548,\n  \"fuel-pump-fill\": 63549,\n  \"fuel-pump\": 63550,\n  \"0-circle-fill\": 63551,\n  \"0-circle\": 63552,\n  \"0-square-fill\": 63553,\n  \"0-square\": 63554,\n  \"rocket-fill\": 63555,\n  \"rocket-takeoff-fill\": 63556,\n  \"rocket-takeoff\": 63557,\n  \"rocket\": 63558,\n  \"stripe\": 63559,\n  \"subscript\": 63560,\n  \"superscript\": 63561,\n  \"trello\": 63562,\n  \"envelope-at-fill\": 63563,\n  \"envelope-at\": 63564,\n  \"regex\": 63565,\n  \"text-wrap\": 63566,\n  \"sign-dead-end-fill\": 63567,\n  \"sign-dead-end\": 63568,\n  \"sign-do-not-enter-fill\": 63569,\n  \"sign-do-not-enter\": 63570,\n  \"sign-intersection-fill\": 63571,\n  \"sign-intersection-side-fill\": 63572,\n  \"sign-intersection-side\": 63573,\n  \"sign-intersection-t-fill\": 63574,\n  \"sign-intersection-t\": 63575,\n  \"sign-intersection-y-fill\": 63576,\n  \"sign-intersection-y\": 63577,\n  \"sign-intersection\": 63578,\n  \"sign-merge-left-fill\": 63579,\n  \"sign-merge-left\": 63580,\n  \"sign-merge-right-fill\": 63581,\n  \"sign-merge-right\": 63582,\n  \"sign-no-left-turn-fill\": 63583,\n  \"sign-no-left-turn\": 63584,\n  \"sign-no-parking-fill\": 63585,\n  \"sign-no-parking\": 63586,\n  \"sign-no-right-turn-fill\": 63587,\n  \"sign-no-right-turn\": 63588,\n  \"sign-railroad-fill\": 63589,\n  \"sign-railroad\": 63590,\n  \"building-add\": 63591,\n  \"building-check\": 63592,\n  \"building-dash\": 63593,\n  \"building-down\": 63594,\n  \"building-exclamation\": 63595,\n  \"building-fill-add\": 63596,\n  \"building-fill-check\": 63597,\n  \"building-fill-dash\": 63598,\n  \"building-fill-down\": 63599,\n  \"building-fill-exclamation\": 63600,\n  \"building-fill-gear\": 63601,\n  \"building-fill-lock\": 63602,\n  \"building-fill-slash\": 63603,\n  \"building-fill-up\": 63604,\n  \"building-fill-x\": 63605,\n  \"building-fill\": 63606,\n  \"building-gear\": 63607,\n  \"building-lock\": 63608,\n  \"building-slash\": 63609,\n  \"building-up\": 63610,\n  \"building-x\": 63611,\n  \"buildings-fill\": 63612,\n  \"buildings\": 63613,\n  \"bus-front-fill\": 63614,\n  \"bus-front\": 63615,\n  \"ev-front-fill\": 63616,\n  \"ev-front\": 63617,\n  \"globe-americas\": 63618,\n  \"globe-asia-australia\": 63619,\n  \"globe-central-south-asia\": 63620,\n  \"globe-europe-africa\": 63621,\n  \"house-add-fill\": 63622,\n  \"house-add\": 63623,\n  \"house-check-fill\": 63624,\n  \"house-check\": 63625,\n  \"house-dash-fill\": 63626,\n  \"house-dash\": 63627,\n  \"house-down-fill\": 63628,\n  \"house-down\": 63629,\n  \"house-exclamation-fill\": 63630,\n  \"house-exclamation\": 63631,\n  \"house-gear-fill\": 63632,\n  \"house-gear\": 63633,\n  \"house-lock-fill\": 63634,\n  \"house-lock\": 63635,\n  \"house-slash-fill\": 63636,\n  \"house-slash\": 63637,\n  \"house-up-fill\": 63638,\n  \"house-up\": 63639,\n  \"house-x-fill\": 63640,\n  \"house-x\": 63641,\n  \"person-add\": 63642,\n  \"person-down\": 63643,\n  \"person-exclamation\": 63644,\n  \"person-fill-add\": 63645,\n  \"person-fill-check\": 63646,\n  \"person-fill-dash\": 63647,\n  \"person-fill-down\": 63648,\n  \"person-fill-exclamation\": 63649,\n  \"person-fill-gear\": 63650,\n  \"person-fill-lock\": 63651,\n  \"person-fill-slash\": 63652,\n  \"person-fill-up\": 63653,\n  \"person-fill-x\": 63654,\n  \"person-gear\": 63655,\n  \"person-lock\": 63656,\n  \"person-slash\": 63657,\n  \"person-up\": 63658,\n  \"scooter\": 63659,\n  \"taxi-front-fill\": 63660,\n  \"taxi-front\": 63661,\n  \"amd\": 63662,\n  \"database-add\": 63663,\n  \"database-check\": 63664,\n  \"database-dash\": 63665,\n  \"database-down\": 63666,\n  \"database-exclamation\": 63667,\n  \"database-fill-add\": 63668,\n  \"database-fill-check\": 63669,\n  \"database-fill-dash\": 63670,\n  \"database-fill-down\": 63671,\n  \"database-fill-exclamation\": 63672,\n  \"database-fill-gear\": 63673,\n  \"database-fill-lock\": 63674,\n  \"database-fill-slash\": 63675,\n  \"database-fill-up\": 63676,\n  \"database-fill-x\": 63677,\n  \"database-fill\": 63678,\n  \"database-gear\": 63679,\n  \"database-lock\": 63680,\n  \"database-slash\": 63681,\n  \"database-up\": 63682,\n  \"database-x\": 63683,\n  \"database\": 63684,\n  \"houses-fill\": 63685,\n  \"houses\": 63686,\n  \"nvidia\": 63687,\n  \"person-vcard-fill\": 63688,\n  \"person-vcard\": 63689,\n  \"sina-weibo\": 63690,\n  \"tencent-qq\": 63691,\n  \"wikipedia\": 63692\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap-icons/bootstrap-icons.scss",
    "content": "$bootstrap-icons-font: \"bootstrap-icons\" !default;\n$bootstrap-icons-font-dir: \"./fonts\" !default;\n$bootstrap-icons-font-file: #{$bootstrap-icons-font-dir}/#{$bootstrap-icons-font} !default;\n$bootstrap-icons-font-hash: \"24e3eb84d0bcaf83d77f904c78ac1f47\" !default;\n$bootstrap-icons-font-src: url(\"#{$bootstrap-icons-font-file}.woff2?#{$bootstrap-icons-font-hash}\") format(\"woff2\"), url(\"#{$bootstrap-icons-font-file}.woff?#{$bootstrap-icons-font-hash}\") format(\"woff\") !default;\n\n@font-face {\n  font-display: block;\n  font-family: $bootstrap-icons-font;\n  src: $bootstrap-icons-font-src;\n}\n\n.bi::before,\n[class^=\"bi-\"]::before,\n[class*=\" bi-\"]::before {\n  display: inline-block;\n  font-family: $bootstrap-icons-font !important;\n  font-style: normal;\n  font-weight: normal !important;\n  font-variant: normal;\n  text-transform: none;\n  line-height: 1;\n  vertical-align: -.125em;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n$bootstrap-icons-map: (\n  \"123\": \"\\f67f\",\n  \"alarm-fill\": \"\\f101\",\n  \"alarm\": \"\\f102\",\n  \"align-bottom\": \"\\f103\",\n  \"align-center\": \"\\f104\",\n  \"align-end\": \"\\f105\",\n  \"align-middle\": \"\\f106\",\n  \"align-start\": \"\\f107\",\n  \"align-top\": \"\\f108\",\n  \"alt\": \"\\f109\",\n  \"app-indicator\": \"\\f10a\",\n  \"app\": \"\\f10b\",\n  \"archive-fill\": \"\\f10c\",\n  \"archive\": \"\\f10d\",\n  \"arrow-90deg-down\": \"\\f10e\",\n  \"arrow-90deg-left\": \"\\f10f\",\n  \"arrow-90deg-right\": \"\\f110\",\n  \"arrow-90deg-up\": \"\\f111\",\n  \"arrow-bar-down\": \"\\f112\",\n  \"arrow-bar-left\": \"\\f113\",\n  \"arrow-bar-right\": \"\\f114\",\n  \"arrow-bar-up\": \"\\f115\",\n  \"arrow-clockwise\": \"\\f116\",\n  \"arrow-counterclockwise\": \"\\f117\",\n  \"arrow-down-circle-fill\": \"\\f118\",\n  \"arrow-down-circle\": \"\\f119\",\n  \"arrow-down-left-circle-fill\": \"\\f11a\",\n  \"arrow-down-left-circle\": \"\\f11b\",\n  \"arrow-down-left-square-fill\": \"\\f11c\",\n  \"arrow-down-left-square\": \"\\f11d\",\n  \"arrow-down-left\": \"\\f11e\",\n  \"arrow-down-right-circle-fill\": \"\\f11f\",\n  \"arrow-down-right-circle\": \"\\f120\",\n  \"arrow-down-right-square-fill\": \"\\f121\",\n  \"arrow-down-right-square\": \"\\f122\",\n  \"arrow-down-right\": \"\\f123\",\n  \"arrow-down-short\": \"\\f124\",\n  \"arrow-down-square-fill\": \"\\f125\",\n  \"arrow-down-square\": \"\\f126\",\n  \"arrow-down-up\": \"\\f127\",\n  \"arrow-down\": \"\\f128\",\n  \"arrow-left-circle-fill\": \"\\f129\",\n  \"arrow-left-circle\": \"\\f12a\",\n  \"arrow-left-right\": \"\\f12b\",\n  \"arrow-left-short\": \"\\f12c\",\n  \"arrow-left-square-fill\": \"\\f12d\",\n  \"arrow-left-square\": \"\\f12e\",\n  \"arrow-left\": \"\\f12f\",\n  \"arrow-repeat\": \"\\f130\",\n  \"arrow-return-left\": \"\\f131\",\n  \"arrow-return-right\": \"\\f132\",\n  \"arrow-right-circle-fill\": \"\\f133\",\n  \"arrow-right-circle\": \"\\f134\",\n  \"arrow-right-short\": \"\\f135\",\n  \"arrow-right-square-fill\": \"\\f136\",\n  \"arrow-right-square\": \"\\f137\",\n  \"arrow-right\": \"\\f138\",\n  \"arrow-up-circle-fill\": \"\\f139\",\n  \"arrow-up-circle\": \"\\f13a\",\n  \"arrow-up-left-circle-fill\": \"\\f13b\",\n  \"arrow-up-left-circle\": \"\\f13c\",\n  \"arrow-up-left-square-fill\": \"\\f13d\",\n  \"arrow-up-left-square\": \"\\f13e\",\n  \"arrow-up-left\": \"\\f13f\",\n  \"arrow-up-right-circle-fill\": \"\\f140\",\n  \"arrow-up-right-circle\": \"\\f141\",\n  \"arrow-up-right-square-fill\": \"\\f142\",\n  \"arrow-up-right-square\": \"\\f143\",\n  \"arrow-up-right\": \"\\f144\",\n  \"arrow-up-short\": \"\\f145\",\n  \"arrow-up-square-fill\": \"\\f146\",\n  \"arrow-up-square\": \"\\f147\",\n  \"arrow-up\": \"\\f148\",\n  \"arrows-angle-contract\": \"\\f149\",\n  \"arrows-angle-expand\": \"\\f14a\",\n  \"arrows-collapse\": \"\\f14b\",\n  \"arrows-expand\": \"\\f14c\",\n  \"arrows-fullscreen\": \"\\f14d\",\n  \"arrows-move\": \"\\f14e\",\n  \"aspect-ratio-fill\": \"\\f14f\",\n  \"aspect-ratio\": \"\\f150\",\n  \"asterisk\": \"\\f151\",\n  \"at\": \"\\f152\",\n  \"award-fill\": \"\\f153\",\n  \"award\": \"\\f154\",\n  \"back\": \"\\f155\",\n  \"backspace-fill\": \"\\f156\",\n  \"backspace-reverse-fill\": \"\\f157\",\n  \"backspace-reverse\": \"\\f158\",\n  \"backspace\": \"\\f159\",\n  \"badge-3d-fill\": \"\\f15a\",\n  \"badge-3d\": \"\\f15b\",\n  \"badge-4k-fill\": \"\\f15c\",\n  \"badge-4k\": \"\\f15d\",\n  \"badge-8k-fill\": \"\\f15e\",\n  \"badge-8k\": \"\\f15f\",\n  \"badge-ad-fill\": \"\\f160\",\n  \"badge-ad\": \"\\f161\",\n  \"badge-ar-fill\": \"\\f162\",\n  \"badge-ar\": \"\\f163\",\n  \"badge-cc-fill\": \"\\f164\",\n  \"badge-cc\": \"\\f165\",\n  \"badge-hd-fill\": \"\\f166\",\n  \"badge-hd\": \"\\f167\",\n  \"badge-tm-fill\": \"\\f168\",\n  \"badge-tm\": \"\\f169\",\n  \"badge-vo-fill\": \"\\f16a\",\n  \"badge-vo\": \"\\f16b\",\n  \"badge-vr-fill\": \"\\f16c\",\n  \"badge-vr\": \"\\f16d\",\n  \"badge-wc-fill\": \"\\f16e\",\n  \"badge-wc\": \"\\f16f\",\n  \"bag-check-fill\": \"\\f170\",\n  \"bag-check\": \"\\f171\",\n  \"bag-dash-fill\": \"\\f172\",\n  \"bag-dash\": \"\\f173\",\n  \"bag-fill\": \"\\f174\",\n  \"bag-plus-fill\": \"\\f175\",\n  \"bag-plus\": \"\\f176\",\n  \"bag-x-fill\": \"\\f177\",\n  \"bag-x\": \"\\f178\",\n  \"bag\": \"\\f179\",\n  \"bar-chart-fill\": \"\\f17a\",\n  \"bar-chart-line-fill\": \"\\f17b\",\n  \"bar-chart-line\": \"\\f17c\",\n  \"bar-chart-steps\": \"\\f17d\",\n  \"bar-chart\": \"\\f17e\",\n  \"basket-fill\": \"\\f17f\",\n  \"basket\": \"\\f180\",\n  \"basket2-fill\": \"\\f181\",\n  \"basket2\": \"\\f182\",\n  \"basket3-fill\": \"\\f183\",\n  \"basket3\": \"\\f184\",\n  \"battery-charging\": \"\\f185\",\n  \"battery-full\": \"\\f186\",\n  \"battery-half\": \"\\f187\",\n  \"battery\": \"\\f188\",\n  \"bell-fill\": \"\\f189\",\n  \"bell\": \"\\f18a\",\n  \"bezier\": \"\\f18b\",\n  \"bezier2\": \"\\f18c\",\n  \"bicycle\": \"\\f18d\",\n  \"binoculars-fill\": \"\\f18e\",\n  \"binoculars\": \"\\f18f\",\n  \"blockquote-left\": \"\\f190\",\n  \"blockquote-right\": \"\\f191\",\n  \"book-fill\": \"\\f192\",\n  \"book-half\": \"\\f193\",\n  \"book\": \"\\f194\",\n  \"bookmark-check-fill\": \"\\f195\",\n  \"bookmark-check\": \"\\f196\",\n  \"bookmark-dash-fill\": \"\\f197\",\n  \"bookmark-dash\": \"\\f198\",\n  \"bookmark-fill\": \"\\f199\",\n  \"bookmark-heart-fill\": \"\\f19a\",\n  \"bookmark-heart\": \"\\f19b\",\n  \"bookmark-plus-fill\": \"\\f19c\",\n  \"bookmark-plus\": \"\\f19d\",\n  \"bookmark-star-fill\": \"\\f19e\",\n  \"bookmark-star\": \"\\f19f\",\n  \"bookmark-x-fill\": \"\\f1a0\",\n  \"bookmark-x\": \"\\f1a1\",\n  \"bookmark\": \"\\f1a2\",\n  \"bookmarks-fill\": \"\\f1a3\",\n  \"bookmarks\": \"\\f1a4\",\n  \"bookshelf\": \"\\f1a5\",\n  \"bootstrap-fill\": \"\\f1a6\",\n  \"bootstrap-reboot\": \"\\f1a7\",\n  \"bootstrap\": \"\\f1a8\",\n  \"border-all\": \"\\f1a9\",\n  \"border-bottom\": \"\\f1aa\",\n  \"border-center\": \"\\f1ab\",\n  \"border-inner\": \"\\f1ac\",\n  \"border-left\": \"\\f1ad\",\n  \"border-middle\": \"\\f1ae\",\n  \"border-outer\": \"\\f1af\",\n  \"border-right\": \"\\f1b0\",\n  \"border-style\": \"\\f1b1\",\n  \"border-top\": \"\\f1b2\",\n  \"border-width\": \"\\f1b3\",\n  \"border\": \"\\f1b4\",\n  \"bounding-box-circles\": \"\\f1b5\",\n  \"bounding-box\": \"\\f1b6\",\n  \"box-arrow-down-left\": \"\\f1b7\",\n  \"box-arrow-down-right\": \"\\f1b8\",\n  \"box-arrow-down\": \"\\f1b9\",\n  \"box-arrow-in-down-left\": \"\\f1ba\",\n  \"box-arrow-in-down-right\": \"\\f1bb\",\n  \"box-arrow-in-down\": \"\\f1bc\",\n  \"box-arrow-in-left\": \"\\f1bd\",\n  \"box-arrow-in-right\": \"\\f1be\",\n  \"box-arrow-in-up-left\": \"\\f1bf\",\n  \"box-arrow-in-up-right\": \"\\f1c0\",\n  \"box-arrow-in-up\": \"\\f1c1\",\n  \"box-arrow-left\": \"\\f1c2\",\n  \"box-arrow-right\": \"\\f1c3\",\n  \"box-arrow-up-left\": \"\\f1c4\",\n  \"box-arrow-up-right\": \"\\f1c5\",\n  \"box-arrow-up\": \"\\f1c6\",\n  \"box-seam\": \"\\f1c7\",\n  \"box\": \"\\f1c8\",\n  \"braces\": \"\\f1c9\",\n  \"bricks\": \"\\f1ca\",\n  \"briefcase-fill\": \"\\f1cb\",\n  \"briefcase\": \"\\f1cc\",\n  \"brightness-alt-high-fill\": \"\\f1cd\",\n  \"brightness-alt-high\": \"\\f1ce\",\n  \"brightness-alt-low-fill\": \"\\f1cf\",\n  \"brightness-alt-low\": \"\\f1d0\",\n  \"brightness-high-fill\": \"\\f1d1\",\n  \"brightness-high\": \"\\f1d2\",\n  \"brightness-low-fill\": \"\\f1d3\",\n  \"brightness-low\": \"\\f1d4\",\n  \"broadcast-pin\": \"\\f1d5\",\n  \"broadcast\": \"\\f1d6\",\n  \"brush-fill\": \"\\f1d7\",\n  \"brush\": \"\\f1d8\",\n  \"bucket-fill\": \"\\f1d9\",\n  \"bucket\": \"\\f1da\",\n  \"bug-fill\": \"\\f1db\",\n  \"bug\": \"\\f1dc\",\n  \"building\": \"\\f1dd\",\n  \"bullseye\": \"\\f1de\",\n  \"calculator-fill\": \"\\f1df\",\n  \"calculator\": \"\\f1e0\",\n  \"calendar-check-fill\": \"\\f1e1\",\n  \"calendar-check\": \"\\f1e2\",\n  \"calendar-date-fill\": \"\\f1e3\",\n  \"calendar-date\": \"\\f1e4\",\n  \"calendar-day-fill\": \"\\f1e5\",\n  \"calendar-day\": \"\\f1e6\",\n  \"calendar-event-fill\": \"\\f1e7\",\n  \"calendar-event\": \"\\f1e8\",\n  \"calendar-fill\": \"\\f1e9\",\n  \"calendar-minus-fill\": \"\\f1ea\",\n  \"calendar-minus\": \"\\f1eb\",\n  \"calendar-month-fill\": \"\\f1ec\",\n  \"calendar-month\": \"\\f1ed\",\n  \"calendar-plus-fill\": \"\\f1ee\",\n  \"calendar-plus\": \"\\f1ef\",\n  \"calendar-range-fill\": \"\\f1f0\",\n  \"calendar-range\": \"\\f1f1\",\n  \"calendar-week-fill\": \"\\f1f2\",\n  \"calendar-week\": \"\\f1f3\",\n  \"calendar-x-fill\": \"\\f1f4\",\n  \"calendar-x\": \"\\f1f5\",\n  \"calendar\": \"\\f1f6\",\n  \"calendar2-check-fill\": \"\\f1f7\",\n  \"calendar2-check\": \"\\f1f8\",\n  \"calendar2-date-fill\": \"\\f1f9\",\n  \"calendar2-date\": \"\\f1fa\",\n  \"calendar2-day-fill\": \"\\f1fb\",\n  \"calendar2-day\": \"\\f1fc\",\n  \"calendar2-event-fill\": \"\\f1fd\",\n  \"calendar2-event\": \"\\f1fe\",\n  \"calendar2-fill\": \"\\f1ff\",\n  \"calendar2-minus-fill\": \"\\f200\",\n  \"calendar2-minus\": \"\\f201\",\n  \"calendar2-month-fill\": \"\\f202\",\n  \"calendar2-month\": \"\\f203\",\n  \"calendar2-plus-fill\": \"\\f204\",\n  \"calendar2-plus\": \"\\f205\",\n  \"calendar2-range-fill\": \"\\f206\",\n  \"calendar2-range\": \"\\f207\",\n  \"calendar2-week-fill\": \"\\f208\",\n  \"calendar2-week\": \"\\f209\",\n  \"calendar2-x-fill\": \"\\f20a\",\n  \"calendar2-x\": \"\\f20b\",\n  \"calendar2\": \"\\f20c\",\n  \"calendar3-event-fill\": \"\\f20d\",\n  \"calendar3-event\": \"\\f20e\",\n  \"calendar3-fill\": \"\\f20f\",\n  \"calendar3-range-fill\": \"\\f210\",\n  \"calendar3-range\": \"\\f211\",\n  \"calendar3-week-fill\": \"\\f212\",\n  \"calendar3-week\": \"\\f213\",\n  \"calendar3\": \"\\f214\",\n  \"calendar4-event\": \"\\f215\",\n  \"calendar4-range\": \"\\f216\",\n  \"calendar4-week\": \"\\f217\",\n  \"calendar4\": \"\\f218\",\n  \"camera-fill\": \"\\f219\",\n  \"camera-reels-fill\": \"\\f21a\",\n  \"camera-reels\": \"\\f21b\",\n  \"camera-video-fill\": \"\\f21c\",\n  \"camera-video-off-fill\": \"\\f21d\",\n  \"camera-video-off\": \"\\f21e\",\n  \"camera-video\": \"\\f21f\",\n  \"camera\": \"\\f220\",\n  \"camera2\": \"\\f221\",\n  \"capslock-fill\": \"\\f222\",\n  \"capslock\": \"\\f223\",\n  \"card-checklist\": \"\\f224\",\n  \"card-heading\": \"\\f225\",\n  \"card-image\": \"\\f226\",\n  \"card-list\": \"\\f227\",\n  \"card-text\": \"\\f228\",\n  \"caret-down-fill\": \"\\f229\",\n  \"caret-down-square-fill\": \"\\f22a\",\n  \"caret-down-square\": \"\\f22b\",\n  \"caret-down\": \"\\f22c\",\n  \"caret-left-fill\": \"\\f22d\",\n  \"caret-left-square-fill\": \"\\f22e\",\n  \"caret-left-square\": \"\\f22f\",\n  \"caret-left\": \"\\f230\",\n  \"caret-right-fill\": \"\\f231\",\n  \"caret-right-square-fill\": \"\\f232\",\n  \"caret-right-square\": \"\\f233\",\n  \"caret-right\": \"\\f234\",\n  \"caret-up-fill\": \"\\f235\",\n  \"caret-up-square-fill\": \"\\f236\",\n  \"caret-up-square\": \"\\f237\",\n  \"caret-up\": \"\\f238\",\n  \"cart-check-fill\": \"\\f239\",\n  \"cart-check\": \"\\f23a\",\n  \"cart-dash-fill\": \"\\f23b\",\n  \"cart-dash\": \"\\f23c\",\n  \"cart-fill\": \"\\f23d\",\n  \"cart-plus-fill\": \"\\f23e\",\n  \"cart-plus\": \"\\f23f\",\n  \"cart-x-fill\": \"\\f240\",\n  \"cart-x\": \"\\f241\",\n  \"cart\": \"\\f242\",\n  \"cart2\": \"\\f243\",\n  \"cart3\": \"\\f244\",\n  \"cart4\": \"\\f245\",\n  \"cash-stack\": \"\\f246\",\n  \"cash\": \"\\f247\",\n  \"cast\": \"\\f248\",\n  \"chat-dots-fill\": \"\\f249\",\n  \"chat-dots\": \"\\f24a\",\n  \"chat-fill\": \"\\f24b\",\n  \"chat-left-dots-fill\": \"\\f24c\",\n  \"chat-left-dots\": \"\\f24d\",\n  \"chat-left-fill\": \"\\f24e\",\n  \"chat-left-quote-fill\": \"\\f24f\",\n  \"chat-left-quote\": \"\\f250\",\n  \"chat-left-text-fill\": \"\\f251\",\n  \"chat-left-text\": \"\\f252\",\n  \"chat-left\": \"\\f253\",\n  \"chat-quote-fill\": \"\\f254\",\n  \"chat-quote\": \"\\f255\",\n  \"chat-right-dots-fill\": \"\\f256\",\n  \"chat-right-dots\": \"\\f257\",\n  \"chat-right-fill\": \"\\f258\",\n  \"chat-right-quote-fill\": \"\\f259\",\n  \"chat-right-quote\": \"\\f25a\",\n  \"chat-right-text-fill\": \"\\f25b\",\n  \"chat-right-text\": \"\\f25c\",\n  \"chat-right\": \"\\f25d\",\n  \"chat-square-dots-fill\": \"\\f25e\",\n  \"chat-square-dots\": \"\\f25f\",\n  \"chat-square-fill\": \"\\f260\",\n  \"chat-square-quote-fill\": \"\\f261\",\n  \"chat-square-quote\": \"\\f262\",\n  \"chat-square-text-fill\": \"\\f263\",\n  \"chat-square-text\": \"\\f264\",\n  \"chat-square\": \"\\f265\",\n  \"chat-text-fill\": \"\\f266\",\n  \"chat-text\": \"\\f267\",\n  \"chat\": \"\\f268\",\n  \"check-all\": \"\\f269\",\n  \"check-circle-fill\": \"\\f26a\",\n  \"check-circle\": \"\\f26b\",\n  \"check-square-fill\": \"\\f26c\",\n  \"check-square\": \"\\f26d\",\n  \"check\": \"\\f26e\",\n  \"check2-all\": \"\\f26f\",\n  \"check2-circle\": \"\\f270\",\n  \"check2-square\": \"\\f271\",\n  \"check2\": \"\\f272\",\n  \"chevron-bar-contract\": \"\\f273\",\n  \"chevron-bar-down\": \"\\f274\",\n  \"chevron-bar-expand\": \"\\f275\",\n  \"chevron-bar-left\": \"\\f276\",\n  \"chevron-bar-right\": \"\\f277\",\n  \"chevron-bar-up\": \"\\f278\",\n  \"chevron-compact-down\": \"\\f279\",\n  \"chevron-compact-left\": \"\\f27a\",\n  \"chevron-compact-right\": \"\\f27b\",\n  \"chevron-compact-up\": \"\\f27c\",\n  \"chevron-contract\": \"\\f27d\",\n  \"chevron-double-down\": \"\\f27e\",\n  \"chevron-double-left\": \"\\f27f\",\n  \"chevron-double-right\": \"\\f280\",\n  \"chevron-double-up\": \"\\f281\",\n  \"chevron-down\": \"\\f282\",\n  \"chevron-expand\": \"\\f283\",\n  \"chevron-left\": \"\\f284\",\n  \"chevron-right\": \"\\f285\",\n  \"chevron-up\": \"\\f286\",\n  \"circle-fill\": \"\\f287\",\n  \"circle-half\": \"\\f288\",\n  \"circle-square\": \"\\f289\",\n  \"circle\": \"\\f28a\",\n  \"clipboard-check\": \"\\f28b\",\n  \"clipboard-data\": \"\\f28c\",\n  \"clipboard-minus\": \"\\f28d\",\n  \"clipboard-plus\": \"\\f28e\",\n  \"clipboard-x\": \"\\f28f\",\n  \"clipboard\": \"\\f290\",\n  \"clock-fill\": \"\\f291\",\n  \"clock-history\": \"\\f292\",\n  \"clock\": \"\\f293\",\n  \"cloud-arrow-down-fill\": \"\\f294\",\n  \"cloud-arrow-down\": \"\\f295\",\n  \"cloud-arrow-up-fill\": \"\\f296\",\n  \"cloud-arrow-up\": \"\\f297\",\n  \"cloud-check-fill\": \"\\f298\",\n  \"cloud-check\": \"\\f299\",\n  \"cloud-download-fill\": \"\\f29a\",\n  \"cloud-download\": \"\\f29b\",\n  \"cloud-drizzle-fill\": \"\\f29c\",\n  \"cloud-drizzle\": \"\\f29d\",\n  \"cloud-fill\": \"\\f29e\",\n  \"cloud-fog-fill\": \"\\f29f\",\n  \"cloud-fog\": \"\\f2a0\",\n  \"cloud-fog2-fill\": \"\\f2a1\",\n  \"cloud-fog2\": \"\\f2a2\",\n  \"cloud-hail-fill\": \"\\f2a3\",\n  \"cloud-hail\": \"\\f2a4\",\n  \"cloud-haze-1\": \"\\f2a5\",\n  \"cloud-haze-fill\": \"\\f2a6\",\n  \"cloud-haze\": \"\\f2a7\",\n  \"cloud-haze2-fill\": \"\\f2a8\",\n  \"cloud-lightning-fill\": \"\\f2a9\",\n  \"cloud-lightning-rain-fill\": \"\\f2aa\",\n  \"cloud-lightning-rain\": \"\\f2ab\",\n  \"cloud-lightning\": \"\\f2ac\",\n  \"cloud-minus-fill\": \"\\f2ad\",\n  \"cloud-minus\": \"\\f2ae\",\n  \"cloud-moon-fill\": \"\\f2af\",\n  \"cloud-moon\": \"\\f2b0\",\n  \"cloud-plus-fill\": \"\\f2b1\",\n  \"cloud-plus\": \"\\f2b2\",\n  \"cloud-rain-fill\": \"\\f2b3\",\n  \"cloud-rain-heavy-fill\": \"\\f2b4\",\n  \"cloud-rain-heavy\": \"\\f2b5\",\n  \"cloud-rain\": \"\\f2b6\",\n  \"cloud-slash-fill\": \"\\f2b7\",\n  \"cloud-slash\": \"\\f2b8\",\n  \"cloud-sleet-fill\": \"\\f2b9\",\n  \"cloud-sleet\": \"\\f2ba\",\n  \"cloud-snow-fill\": \"\\f2bb\",\n  \"cloud-snow\": \"\\f2bc\",\n  \"cloud-sun-fill\": \"\\f2bd\",\n  \"cloud-sun\": \"\\f2be\",\n  \"cloud-upload-fill\": \"\\f2bf\",\n  \"cloud-upload\": \"\\f2c0\",\n  \"cloud\": \"\\f2c1\",\n  \"clouds-fill\": \"\\f2c2\",\n  \"clouds\": \"\\f2c3\",\n  \"cloudy-fill\": \"\\f2c4\",\n  \"cloudy\": \"\\f2c5\",\n  \"code-slash\": \"\\f2c6\",\n  \"code-square\": \"\\f2c7\",\n  \"code\": \"\\f2c8\",\n  \"collection-fill\": \"\\f2c9\",\n  \"collection-play-fill\": \"\\f2ca\",\n  \"collection-play\": \"\\f2cb\",\n  \"collection\": \"\\f2cc\",\n  \"columns-gap\": \"\\f2cd\",\n  \"columns\": \"\\f2ce\",\n  \"command\": \"\\f2cf\",\n  \"compass-fill\": \"\\f2d0\",\n  \"compass\": \"\\f2d1\",\n  \"cone-striped\": \"\\f2d2\",\n  \"cone\": \"\\f2d3\",\n  \"controller\": \"\\f2d4\",\n  \"cpu-fill\": \"\\f2d5\",\n  \"cpu\": \"\\f2d6\",\n  \"credit-card-2-back-fill\": \"\\f2d7\",\n  \"credit-card-2-back\": \"\\f2d8\",\n  \"credit-card-2-front-fill\": \"\\f2d9\",\n  \"credit-card-2-front\": \"\\f2da\",\n  \"credit-card-fill\": \"\\f2db\",\n  \"credit-card\": \"\\f2dc\",\n  \"crop\": \"\\f2dd\",\n  \"cup-fill\": \"\\f2de\",\n  \"cup-straw\": \"\\f2df\",\n  \"cup\": \"\\f2e0\",\n  \"cursor-fill\": \"\\f2e1\",\n  \"cursor-text\": \"\\f2e2\",\n  \"cursor\": \"\\f2e3\",\n  \"dash-circle-dotted\": \"\\f2e4\",\n  \"dash-circle-fill\": \"\\f2e5\",\n  \"dash-circle\": \"\\f2e6\",\n  \"dash-square-dotted\": \"\\f2e7\",\n  \"dash-square-fill\": \"\\f2e8\",\n  \"dash-square\": \"\\f2e9\",\n  \"dash\": \"\\f2ea\",\n  \"diagram-2-fill\": \"\\f2eb\",\n  \"diagram-2\": \"\\f2ec\",\n  \"diagram-3-fill\": \"\\f2ed\",\n  \"diagram-3\": \"\\f2ee\",\n  \"diamond-fill\": \"\\f2ef\",\n  \"diamond-half\": \"\\f2f0\",\n  \"diamond\": \"\\f2f1\",\n  \"dice-1-fill\": \"\\f2f2\",\n  \"dice-1\": \"\\f2f3\",\n  \"dice-2-fill\": \"\\f2f4\",\n  \"dice-2\": \"\\f2f5\",\n  \"dice-3-fill\": \"\\f2f6\",\n  \"dice-3\": \"\\f2f7\",\n  \"dice-4-fill\": \"\\f2f8\",\n  \"dice-4\": \"\\f2f9\",\n  \"dice-5-fill\": \"\\f2fa\",\n  \"dice-5\": \"\\f2fb\",\n  \"dice-6-fill\": \"\\f2fc\",\n  \"dice-6\": \"\\f2fd\",\n  \"disc-fill\": \"\\f2fe\",\n  \"disc\": \"\\f2ff\",\n  \"discord\": \"\\f300\",\n  \"display-fill\": \"\\f301\",\n  \"display\": \"\\f302\",\n  \"distribute-horizontal\": \"\\f303\",\n  \"distribute-vertical\": \"\\f304\",\n  \"door-closed-fill\": \"\\f305\",\n  \"door-closed\": \"\\f306\",\n  \"door-open-fill\": \"\\f307\",\n  \"door-open\": \"\\f308\",\n  \"dot\": \"\\f309\",\n  \"download\": \"\\f30a\",\n  \"droplet-fill\": \"\\f30b\",\n  \"droplet-half\": \"\\f30c\",\n  \"droplet\": \"\\f30d\",\n  \"earbuds\": \"\\f30e\",\n  \"easel-fill\": \"\\f30f\",\n  \"easel\": \"\\f310\",\n  \"egg-fill\": \"\\f311\",\n  \"egg-fried\": \"\\f312\",\n  \"egg\": \"\\f313\",\n  \"eject-fill\": \"\\f314\",\n  \"eject\": \"\\f315\",\n  \"emoji-angry-fill\": \"\\f316\",\n  \"emoji-angry\": \"\\f317\",\n  \"emoji-dizzy-fill\": \"\\f318\",\n  \"emoji-dizzy\": \"\\f319\",\n  \"emoji-expressionless-fill\": \"\\f31a\",\n  \"emoji-expressionless\": \"\\f31b\",\n  \"emoji-frown-fill\": \"\\f31c\",\n  \"emoji-frown\": \"\\f31d\",\n  \"emoji-heart-eyes-fill\": \"\\f31e\",\n  \"emoji-heart-eyes\": \"\\f31f\",\n  \"emoji-laughing-fill\": \"\\f320\",\n  \"emoji-laughing\": \"\\f321\",\n  \"emoji-neutral-fill\": \"\\f322\",\n  \"emoji-neutral\": \"\\f323\",\n  \"emoji-smile-fill\": \"\\f324\",\n  \"emoji-smile-upside-down-fill\": \"\\f325\",\n  \"emoji-smile-upside-down\": \"\\f326\",\n  \"emoji-smile\": \"\\f327\",\n  \"emoji-sunglasses-fill\": \"\\f328\",\n  \"emoji-sunglasses\": \"\\f329\",\n  \"emoji-wink-fill\": \"\\f32a\",\n  \"emoji-wink\": \"\\f32b\",\n  \"envelope-fill\": \"\\f32c\",\n  \"envelope-open-fill\": \"\\f32d\",\n  \"envelope-open\": \"\\f32e\",\n  \"envelope\": \"\\f32f\",\n  \"eraser-fill\": \"\\f330\",\n  \"eraser\": \"\\f331\",\n  \"exclamation-circle-fill\": \"\\f332\",\n  \"exclamation-circle\": \"\\f333\",\n  \"exclamation-diamond-fill\": \"\\f334\",\n  \"exclamation-diamond\": \"\\f335\",\n  \"exclamation-octagon-fill\": \"\\f336\",\n  \"exclamation-octagon\": \"\\f337\",\n  \"exclamation-square-fill\": \"\\f338\",\n  \"exclamation-square\": \"\\f339\",\n  \"exclamation-triangle-fill\": \"\\f33a\",\n  \"exclamation-triangle\": \"\\f33b\",\n  \"exclamation\": \"\\f33c\",\n  \"exclude\": \"\\f33d\",\n  \"eye-fill\": \"\\f33e\",\n  \"eye-slash-fill\": \"\\f33f\",\n  \"eye-slash\": \"\\f340\",\n  \"eye\": \"\\f341\",\n  \"eyedropper\": \"\\f342\",\n  \"eyeglasses\": \"\\f343\",\n  \"facebook\": \"\\f344\",\n  \"file-arrow-down-fill\": \"\\f345\",\n  \"file-arrow-down\": \"\\f346\",\n  \"file-arrow-up-fill\": \"\\f347\",\n  \"file-arrow-up\": \"\\f348\",\n  \"file-bar-graph-fill\": \"\\f349\",\n  \"file-bar-graph\": \"\\f34a\",\n  \"file-binary-fill\": \"\\f34b\",\n  \"file-binary\": \"\\f34c\",\n  \"file-break-fill\": \"\\f34d\",\n  \"file-break\": \"\\f34e\",\n  \"file-check-fill\": \"\\f34f\",\n  \"file-check\": \"\\f350\",\n  \"file-code-fill\": \"\\f351\",\n  \"file-code\": \"\\f352\",\n  \"file-diff-fill\": \"\\f353\",\n  \"file-diff\": \"\\f354\",\n  \"file-earmark-arrow-down-fill\": \"\\f355\",\n  \"file-earmark-arrow-down\": \"\\f356\",\n  \"file-earmark-arrow-up-fill\": \"\\f357\",\n  \"file-earmark-arrow-up\": \"\\f358\",\n  \"file-earmark-bar-graph-fill\": \"\\f359\",\n  \"file-earmark-bar-graph\": \"\\f35a\",\n  \"file-earmark-binary-fill\": \"\\f35b\",\n  \"file-earmark-binary\": \"\\f35c\",\n  \"file-earmark-break-fill\": \"\\f35d\",\n  \"file-earmark-break\": \"\\f35e\",\n  \"file-earmark-check-fill\": \"\\f35f\",\n  \"file-earmark-check\": \"\\f360\",\n  \"file-earmark-code-fill\": \"\\f361\",\n  \"file-earmark-code\": \"\\f362\",\n  \"file-earmark-diff-fill\": \"\\f363\",\n  \"file-earmark-diff\": \"\\f364\",\n  \"file-earmark-easel-fill\": \"\\f365\",\n  \"file-earmark-easel\": \"\\f366\",\n  \"file-earmark-excel-fill\": \"\\f367\",\n  \"file-earmark-excel\": \"\\f368\",\n  \"file-earmark-fill\": \"\\f369\",\n  \"file-earmark-font-fill\": \"\\f36a\",\n  \"file-earmark-font\": \"\\f36b\",\n  \"file-earmark-image-fill\": \"\\f36c\",\n  \"file-earmark-image\": \"\\f36d\",\n  \"file-earmark-lock-fill\": \"\\f36e\",\n  \"file-earmark-lock\": \"\\f36f\",\n  \"file-earmark-lock2-fill\": \"\\f370\",\n  \"file-earmark-lock2\": \"\\f371\",\n  \"file-earmark-medical-fill\": \"\\f372\",\n  \"file-earmark-medical\": \"\\f373\",\n  \"file-earmark-minus-fill\": \"\\f374\",\n  \"file-earmark-minus\": \"\\f375\",\n  \"file-earmark-music-fill\": \"\\f376\",\n  \"file-earmark-music\": \"\\f377\",\n  \"file-earmark-person-fill\": \"\\f378\",\n  \"file-earmark-person\": \"\\f379\",\n  \"file-earmark-play-fill\": \"\\f37a\",\n  \"file-earmark-play\": \"\\f37b\",\n  \"file-earmark-plus-fill\": \"\\f37c\",\n  \"file-earmark-plus\": \"\\f37d\",\n  \"file-earmark-post-fill\": \"\\f37e\",\n  \"file-earmark-post\": \"\\f37f\",\n  \"file-earmark-ppt-fill\": \"\\f380\",\n  \"file-earmark-ppt\": \"\\f381\",\n  \"file-earmark-richtext-fill\": \"\\f382\",\n  \"file-earmark-richtext\": \"\\f383\",\n  \"file-earmark-ruled-fill\": \"\\f384\",\n  \"file-earmark-ruled\": \"\\f385\",\n  \"file-earmark-slides-fill\": \"\\f386\",\n  \"file-earmark-slides\": \"\\f387\",\n  \"file-earmark-spreadsheet-fill\": \"\\f388\",\n  \"file-earmark-spreadsheet\": \"\\f389\",\n  \"file-earmark-text-fill\": \"\\f38a\",\n  \"file-earmark-text\": \"\\f38b\",\n  \"file-earmark-word-fill\": \"\\f38c\",\n  \"file-earmark-word\": \"\\f38d\",\n  \"file-earmark-x-fill\": \"\\f38e\",\n  \"file-earmark-x\": \"\\f38f\",\n  \"file-earmark-zip-fill\": \"\\f390\",\n  \"file-earmark-zip\": \"\\f391\",\n  \"file-earmark\": \"\\f392\",\n  \"file-easel-fill\": \"\\f393\",\n  \"file-easel\": \"\\f394\",\n  \"file-excel-fill\": \"\\f395\",\n  \"file-excel\": \"\\f396\",\n  \"file-fill\": \"\\f397\",\n  \"file-font-fill\": \"\\f398\",\n  \"file-font\": \"\\f399\",\n  \"file-image-fill\": \"\\f39a\",\n  \"file-image\": \"\\f39b\",\n  \"file-lock-fill\": \"\\f39c\",\n  \"file-lock\": \"\\f39d\",\n  \"file-lock2-fill\": \"\\f39e\",\n  \"file-lock2\": \"\\f39f\",\n  \"file-medical-fill\": \"\\f3a0\",\n  \"file-medical\": \"\\f3a1\",\n  \"file-minus-fill\": \"\\f3a2\",\n  \"file-minus\": \"\\f3a3\",\n  \"file-music-fill\": \"\\f3a4\",\n  \"file-music\": \"\\f3a5\",\n  \"file-person-fill\": \"\\f3a6\",\n  \"file-person\": \"\\f3a7\",\n  \"file-play-fill\": \"\\f3a8\",\n  \"file-play\": \"\\f3a9\",\n  \"file-plus-fill\": \"\\f3aa\",\n  \"file-plus\": \"\\f3ab\",\n  \"file-post-fill\": \"\\f3ac\",\n  \"file-post\": \"\\f3ad\",\n  \"file-ppt-fill\": \"\\f3ae\",\n  \"file-ppt\": \"\\f3af\",\n  \"file-richtext-fill\": \"\\f3b0\",\n  \"file-richtext\": \"\\f3b1\",\n  \"file-ruled-fill\": \"\\f3b2\",\n  \"file-ruled\": \"\\f3b3\",\n  \"file-slides-fill\": \"\\f3b4\",\n  \"file-slides\": \"\\f3b5\",\n  \"file-spreadsheet-fill\": \"\\f3b6\",\n  \"file-spreadsheet\": \"\\f3b7\",\n  \"file-text-fill\": \"\\f3b8\",\n  \"file-text\": \"\\f3b9\",\n  \"file-word-fill\": \"\\f3ba\",\n  \"file-word\": \"\\f3bb\",\n  \"file-x-fill\": \"\\f3bc\",\n  \"file-x\": \"\\f3bd\",\n  \"file-zip-fill\": \"\\f3be\",\n  \"file-zip\": \"\\f3bf\",\n  \"file\": \"\\f3c0\",\n  \"files-alt\": \"\\f3c1\",\n  \"files\": \"\\f3c2\",\n  \"film\": \"\\f3c3\",\n  \"filter-circle-fill\": \"\\f3c4\",\n  \"filter-circle\": \"\\f3c5\",\n  \"filter-left\": \"\\f3c6\",\n  \"filter-right\": \"\\f3c7\",\n  \"filter-square-fill\": \"\\f3c8\",\n  \"filter-square\": \"\\f3c9\",\n  \"filter\": \"\\f3ca\",\n  \"flag-fill\": \"\\f3cb\",\n  \"flag\": \"\\f3cc\",\n  \"flower1\": \"\\f3cd\",\n  \"flower2\": \"\\f3ce\",\n  \"flower3\": \"\\f3cf\",\n  \"folder-check\": \"\\f3d0\",\n  \"folder-fill\": \"\\f3d1\",\n  \"folder-minus\": \"\\f3d2\",\n  \"folder-plus\": \"\\f3d3\",\n  \"folder-symlink-fill\": \"\\f3d4\",\n  \"folder-symlink\": \"\\f3d5\",\n  \"folder-x\": \"\\f3d6\",\n  \"folder\": \"\\f3d7\",\n  \"folder2-open\": \"\\f3d8\",\n  \"folder2\": \"\\f3d9\",\n  \"fonts\": \"\\f3da\",\n  \"forward-fill\": \"\\f3db\",\n  \"forward\": \"\\f3dc\",\n  \"front\": \"\\f3dd\",\n  \"fullscreen-exit\": \"\\f3de\",\n  \"fullscreen\": \"\\f3df\",\n  \"funnel-fill\": \"\\f3e0\",\n  \"funnel\": \"\\f3e1\",\n  \"gear-fill\": \"\\f3e2\",\n  \"gear-wide-connected\": \"\\f3e3\",\n  \"gear-wide\": \"\\f3e4\",\n  \"gear\": \"\\f3e5\",\n  \"gem\": \"\\f3e6\",\n  \"geo-alt-fill\": \"\\f3e7\",\n  \"geo-alt\": \"\\f3e8\",\n  \"geo-fill\": \"\\f3e9\",\n  \"geo\": \"\\f3ea\",\n  \"gift-fill\": \"\\f3eb\",\n  \"gift\": \"\\f3ec\",\n  \"github\": \"\\f3ed\",\n  \"globe\": \"\\f3ee\",\n  \"globe2\": \"\\f3ef\",\n  \"google\": \"\\f3f0\",\n  \"graph-down\": \"\\f3f1\",\n  \"graph-up\": \"\\f3f2\",\n  \"grid-1x2-fill\": \"\\f3f3\",\n  \"grid-1x2\": \"\\f3f4\",\n  \"grid-3x2-gap-fill\": \"\\f3f5\",\n  \"grid-3x2-gap\": \"\\f3f6\",\n  \"grid-3x2\": \"\\f3f7\",\n  \"grid-3x3-gap-fill\": \"\\f3f8\",\n  \"grid-3x3-gap\": \"\\f3f9\",\n  \"grid-3x3\": \"\\f3fa\",\n  \"grid-fill\": \"\\f3fb\",\n  \"grid\": \"\\f3fc\",\n  \"grip-horizontal\": \"\\f3fd\",\n  \"grip-vertical\": \"\\f3fe\",\n  \"hammer\": \"\\f3ff\",\n  \"hand-index-fill\": \"\\f400\",\n  \"hand-index-thumb-fill\": \"\\f401\",\n  \"hand-index-thumb\": \"\\f402\",\n  \"hand-index\": \"\\f403\",\n  \"hand-thumbs-down-fill\": \"\\f404\",\n  \"hand-thumbs-down\": \"\\f405\",\n  \"hand-thumbs-up-fill\": \"\\f406\",\n  \"hand-thumbs-up\": \"\\f407\",\n  \"handbag-fill\": \"\\f408\",\n  \"handbag\": \"\\f409\",\n  \"hash\": \"\\f40a\",\n  \"hdd-fill\": \"\\f40b\",\n  \"hdd-network-fill\": \"\\f40c\",\n  \"hdd-network\": \"\\f40d\",\n  \"hdd-rack-fill\": \"\\f40e\",\n  \"hdd-rack\": \"\\f40f\",\n  \"hdd-stack-fill\": \"\\f410\",\n  \"hdd-stack\": \"\\f411\",\n  \"hdd\": \"\\f412\",\n  \"headphones\": \"\\f413\",\n  \"headset\": \"\\f414\",\n  \"heart-fill\": \"\\f415\",\n  \"heart-half\": \"\\f416\",\n  \"heart\": \"\\f417\",\n  \"heptagon-fill\": \"\\f418\",\n  \"heptagon-half\": \"\\f419\",\n  \"heptagon\": \"\\f41a\",\n  \"hexagon-fill\": \"\\f41b\",\n  \"hexagon-half\": \"\\f41c\",\n  \"hexagon\": \"\\f41d\",\n  \"hourglass-bottom\": \"\\f41e\",\n  \"hourglass-split\": \"\\f41f\",\n  \"hourglass-top\": \"\\f420\",\n  \"hourglass\": \"\\f421\",\n  \"house-door-fill\": \"\\f422\",\n  \"house-door\": \"\\f423\",\n  \"house-fill\": \"\\f424\",\n  \"house\": \"\\f425\",\n  \"hr\": \"\\f426\",\n  \"hurricane\": \"\\f427\",\n  \"image-alt\": \"\\f428\",\n  \"image-fill\": \"\\f429\",\n  \"image\": \"\\f42a\",\n  \"images\": \"\\f42b\",\n  \"inbox-fill\": \"\\f42c\",\n  \"inbox\": \"\\f42d\",\n  \"inboxes-fill\": \"\\f42e\",\n  \"inboxes\": \"\\f42f\",\n  \"info-circle-fill\": \"\\f430\",\n  \"info-circle\": \"\\f431\",\n  \"info-square-fill\": \"\\f432\",\n  \"info-square\": \"\\f433\",\n  \"info\": \"\\f434\",\n  \"input-cursor-text\": \"\\f435\",\n  \"input-cursor\": \"\\f436\",\n  \"instagram\": \"\\f437\",\n  \"intersect\": \"\\f438\",\n  \"journal-album\": \"\\f439\",\n  \"journal-arrow-down\": \"\\f43a\",\n  \"journal-arrow-up\": \"\\f43b\",\n  \"journal-bookmark-fill\": \"\\f43c\",\n  \"journal-bookmark\": \"\\f43d\",\n  \"journal-check\": \"\\f43e\",\n  \"journal-code\": \"\\f43f\",\n  \"journal-medical\": \"\\f440\",\n  \"journal-minus\": \"\\f441\",\n  \"journal-plus\": \"\\f442\",\n  \"journal-richtext\": \"\\f443\",\n  \"journal-text\": \"\\f444\",\n  \"journal-x\": \"\\f445\",\n  \"journal\": \"\\f446\",\n  \"journals\": \"\\f447\",\n  \"joystick\": \"\\f448\",\n  \"justify-left\": \"\\f449\",\n  \"justify-right\": \"\\f44a\",\n  \"justify\": \"\\f44b\",\n  \"kanban-fill\": \"\\f44c\",\n  \"kanban\": \"\\f44d\",\n  \"key-fill\": \"\\f44e\",\n  \"key\": \"\\f44f\",\n  \"keyboard-fill\": \"\\f450\",\n  \"keyboard\": \"\\f451\",\n  \"ladder\": \"\\f452\",\n  \"lamp-fill\": \"\\f453\",\n  \"lamp\": \"\\f454\",\n  \"laptop-fill\": \"\\f455\",\n  \"laptop\": \"\\f456\",\n  \"layer-backward\": \"\\f457\",\n  \"layer-forward\": \"\\f458\",\n  \"layers-fill\": \"\\f459\",\n  \"layers-half\": \"\\f45a\",\n  \"layers\": \"\\f45b\",\n  \"layout-sidebar-inset-reverse\": \"\\f45c\",\n  \"layout-sidebar-inset\": \"\\f45d\",\n  \"layout-sidebar-reverse\": \"\\f45e\",\n  \"layout-sidebar\": \"\\f45f\",\n  \"layout-split\": \"\\f460\",\n  \"layout-text-sidebar-reverse\": \"\\f461\",\n  \"layout-text-sidebar\": \"\\f462\",\n  \"layout-text-window-reverse\": \"\\f463\",\n  \"layout-text-window\": \"\\f464\",\n  \"layout-three-columns\": \"\\f465\",\n  \"layout-wtf\": \"\\f466\",\n  \"life-preserver\": \"\\f467\",\n  \"lightbulb-fill\": \"\\f468\",\n  \"lightbulb-off-fill\": \"\\f469\",\n  \"lightbulb-off\": \"\\f46a\",\n  \"lightbulb\": \"\\f46b\",\n  \"lightning-charge-fill\": \"\\f46c\",\n  \"lightning-charge\": \"\\f46d\",\n  \"lightning-fill\": \"\\f46e\",\n  \"lightning\": \"\\f46f\",\n  \"link-45deg\": \"\\f470\",\n  \"link\": \"\\f471\",\n  \"linkedin\": \"\\f472\",\n  \"list-check\": \"\\f473\",\n  \"list-nested\": \"\\f474\",\n  \"list-ol\": \"\\f475\",\n  \"list-stars\": \"\\f476\",\n  \"list-task\": \"\\f477\",\n  \"list-ul\": \"\\f478\",\n  \"list\": \"\\f479\",\n  \"lock-fill\": \"\\f47a\",\n  \"lock\": \"\\f47b\",\n  \"mailbox\": \"\\f47c\",\n  \"mailbox2\": \"\\f47d\",\n  \"map-fill\": \"\\f47e\",\n  \"map\": \"\\f47f\",\n  \"markdown-fill\": \"\\f480\",\n  \"markdown\": \"\\f481\",\n  \"mask\": \"\\f482\",\n  \"megaphone-fill\": \"\\f483\",\n  \"megaphone\": \"\\f484\",\n  \"menu-app-fill\": \"\\f485\",\n  \"menu-app\": \"\\f486\",\n  \"menu-button-fill\": \"\\f487\",\n  \"menu-button-wide-fill\": \"\\f488\",\n  \"menu-button-wide\": \"\\f489\",\n  \"menu-button\": \"\\f48a\",\n  \"menu-down\": \"\\f48b\",\n  \"menu-up\": \"\\f48c\",\n  \"mic-fill\": \"\\f48d\",\n  \"mic-mute-fill\": \"\\f48e\",\n  \"mic-mute\": \"\\f48f\",\n  \"mic\": \"\\f490\",\n  \"minecart-loaded\": \"\\f491\",\n  \"minecart\": \"\\f492\",\n  \"moisture\": \"\\f493\",\n  \"moon-fill\": \"\\f494\",\n  \"moon-stars-fill\": \"\\f495\",\n  \"moon-stars\": \"\\f496\",\n  \"moon\": \"\\f497\",\n  \"mouse-fill\": \"\\f498\",\n  \"mouse\": \"\\f499\",\n  \"mouse2-fill\": \"\\f49a\",\n  \"mouse2\": \"\\f49b\",\n  \"mouse3-fill\": \"\\f49c\",\n  \"mouse3\": \"\\f49d\",\n  \"music-note-beamed\": \"\\f49e\",\n  \"music-note-list\": \"\\f49f\",\n  \"music-note\": \"\\f4a0\",\n  \"music-player-fill\": \"\\f4a1\",\n  \"music-player\": \"\\f4a2\",\n  \"newspaper\": \"\\f4a3\",\n  \"node-minus-fill\": \"\\f4a4\",\n  \"node-minus\": \"\\f4a5\",\n  \"node-plus-fill\": \"\\f4a6\",\n  \"node-plus\": \"\\f4a7\",\n  \"nut-fill\": \"\\f4a8\",\n  \"nut\": \"\\f4a9\",\n  \"octagon-fill\": \"\\f4aa\",\n  \"octagon-half\": \"\\f4ab\",\n  \"octagon\": \"\\f4ac\",\n  \"option\": \"\\f4ad\",\n  \"outlet\": \"\\f4ae\",\n  \"paint-bucket\": \"\\f4af\",\n  \"palette-fill\": \"\\f4b0\",\n  \"palette\": \"\\f4b1\",\n  \"palette2\": \"\\f4b2\",\n  \"paperclip\": \"\\f4b3\",\n  \"paragraph\": \"\\f4b4\",\n  \"patch-check-fill\": \"\\f4b5\",\n  \"patch-check\": \"\\f4b6\",\n  \"patch-exclamation-fill\": \"\\f4b7\",\n  \"patch-exclamation\": \"\\f4b8\",\n  \"patch-minus-fill\": \"\\f4b9\",\n  \"patch-minus\": \"\\f4ba\",\n  \"patch-plus-fill\": \"\\f4bb\",\n  \"patch-plus\": \"\\f4bc\",\n  \"patch-question-fill\": \"\\f4bd\",\n  \"patch-question\": \"\\f4be\",\n  \"pause-btn-fill\": \"\\f4bf\",\n  \"pause-btn\": \"\\f4c0\",\n  \"pause-circle-fill\": \"\\f4c1\",\n  \"pause-circle\": \"\\f4c2\",\n  \"pause-fill\": \"\\f4c3\",\n  \"pause\": \"\\f4c4\",\n  \"peace-fill\": \"\\f4c5\",\n  \"peace\": \"\\f4c6\",\n  \"pen-fill\": \"\\f4c7\",\n  \"pen\": \"\\f4c8\",\n  \"pencil-fill\": \"\\f4c9\",\n  \"pencil-square\": \"\\f4ca\",\n  \"pencil\": \"\\f4cb\",\n  \"pentagon-fill\": \"\\f4cc\",\n  \"pentagon-half\": \"\\f4cd\",\n  \"pentagon\": \"\\f4ce\",\n  \"people-fill\": \"\\f4cf\",\n  \"people\": \"\\f4d0\",\n  \"percent\": \"\\f4d1\",\n  \"person-badge-fill\": \"\\f4d2\",\n  \"person-badge\": \"\\f4d3\",\n  \"person-bounding-box\": \"\\f4d4\",\n  \"person-check-fill\": \"\\f4d5\",\n  \"person-check\": \"\\f4d6\",\n  \"person-circle\": \"\\f4d7\",\n  \"person-dash-fill\": \"\\f4d8\",\n  \"person-dash\": \"\\f4d9\",\n  \"person-fill\": \"\\f4da\",\n  \"person-lines-fill\": \"\\f4db\",\n  \"person-plus-fill\": \"\\f4dc\",\n  \"person-plus\": \"\\f4dd\",\n  \"person-square\": \"\\f4de\",\n  \"person-x-fill\": \"\\f4df\",\n  \"person-x\": \"\\f4e0\",\n  \"person\": \"\\f4e1\",\n  \"phone-fill\": \"\\f4e2\",\n  \"phone-landscape-fill\": \"\\f4e3\",\n  \"phone-landscape\": \"\\f4e4\",\n  \"phone-vibrate-fill\": \"\\f4e5\",\n  \"phone-vibrate\": \"\\f4e6\",\n  \"phone\": \"\\f4e7\",\n  \"pie-chart-fill\": \"\\f4e8\",\n  \"pie-chart\": \"\\f4e9\",\n  \"pin-angle-fill\": \"\\f4ea\",\n  \"pin-angle\": \"\\f4eb\",\n  \"pin-fill\": \"\\f4ec\",\n  \"pin\": \"\\f4ed\",\n  \"pip-fill\": \"\\f4ee\",\n  \"pip\": \"\\f4ef\",\n  \"play-btn-fill\": \"\\f4f0\",\n  \"play-btn\": \"\\f4f1\",\n  \"play-circle-fill\": \"\\f4f2\",\n  \"play-circle\": \"\\f4f3\",\n  \"play-fill\": \"\\f4f4\",\n  \"play\": \"\\f4f5\",\n  \"plug-fill\": \"\\f4f6\",\n  \"plug\": \"\\f4f7\",\n  \"plus-circle-dotted\": \"\\f4f8\",\n  \"plus-circle-fill\": \"\\f4f9\",\n  \"plus-circle\": \"\\f4fa\",\n  \"plus-square-dotted\": \"\\f4fb\",\n  \"plus-square-fill\": \"\\f4fc\",\n  \"plus-square\": \"\\f4fd\",\n  \"plus\": \"\\f4fe\",\n  \"power\": \"\\f4ff\",\n  \"printer-fill\": \"\\f500\",\n  \"printer\": \"\\f501\",\n  \"puzzle-fill\": \"\\f502\",\n  \"puzzle\": \"\\f503\",\n  \"question-circle-fill\": \"\\f504\",\n  \"question-circle\": \"\\f505\",\n  \"question-diamond-fill\": \"\\f506\",\n  \"question-diamond\": \"\\f507\",\n  \"question-octagon-fill\": \"\\f508\",\n  \"question-octagon\": \"\\f509\",\n  \"question-square-fill\": \"\\f50a\",\n  \"question-square\": \"\\f50b\",\n  \"question\": \"\\f50c\",\n  \"rainbow\": \"\\f50d\",\n  \"receipt-cutoff\": \"\\f50e\",\n  \"receipt\": \"\\f50f\",\n  \"reception-0\": \"\\f510\",\n  \"reception-1\": \"\\f511\",\n  \"reception-2\": \"\\f512\",\n  \"reception-3\": \"\\f513\",\n  \"reception-4\": \"\\f514\",\n  \"record-btn-fill\": \"\\f515\",\n  \"record-btn\": \"\\f516\",\n  \"record-circle-fill\": \"\\f517\",\n  \"record-circle\": \"\\f518\",\n  \"record-fill\": \"\\f519\",\n  \"record\": \"\\f51a\",\n  \"record2-fill\": \"\\f51b\",\n  \"record2\": \"\\f51c\",\n  \"reply-all-fill\": \"\\f51d\",\n  \"reply-all\": \"\\f51e\",\n  \"reply-fill\": \"\\f51f\",\n  \"reply\": \"\\f520\",\n  \"rss-fill\": \"\\f521\",\n  \"rss\": \"\\f522\",\n  \"rulers\": \"\\f523\",\n  \"save-fill\": \"\\f524\",\n  \"save\": \"\\f525\",\n  \"save2-fill\": \"\\f526\",\n  \"save2\": \"\\f527\",\n  \"scissors\": \"\\f528\",\n  \"screwdriver\": \"\\f529\",\n  \"search\": \"\\f52a\",\n  \"segmented-nav\": \"\\f52b\",\n  \"server\": \"\\f52c\",\n  \"share-fill\": \"\\f52d\",\n  \"share\": \"\\f52e\",\n  \"shield-check\": \"\\f52f\",\n  \"shield-exclamation\": \"\\f530\",\n  \"shield-fill-check\": \"\\f531\",\n  \"shield-fill-exclamation\": \"\\f532\",\n  \"shield-fill-minus\": \"\\f533\",\n  \"shield-fill-plus\": \"\\f534\",\n  \"shield-fill-x\": \"\\f535\",\n  \"shield-fill\": \"\\f536\",\n  \"shield-lock-fill\": \"\\f537\",\n  \"shield-lock\": \"\\f538\",\n  \"shield-minus\": \"\\f539\",\n  \"shield-plus\": \"\\f53a\",\n  \"shield-shaded\": \"\\f53b\",\n  \"shield-slash-fill\": \"\\f53c\",\n  \"shield-slash\": \"\\f53d\",\n  \"shield-x\": \"\\f53e\",\n  \"shield\": \"\\f53f\",\n  \"shift-fill\": \"\\f540\",\n  \"shift\": \"\\f541\",\n  \"shop-window\": \"\\f542\",\n  \"shop\": \"\\f543\",\n  \"shuffle\": \"\\f544\",\n  \"signpost-2-fill\": \"\\f545\",\n  \"signpost-2\": \"\\f546\",\n  \"signpost-fill\": \"\\f547\",\n  \"signpost-split-fill\": \"\\f548\",\n  \"signpost-split\": \"\\f549\",\n  \"signpost\": \"\\f54a\",\n  \"sim-fill\": \"\\f54b\",\n  \"sim\": \"\\f54c\",\n  \"skip-backward-btn-fill\": \"\\f54d\",\n  \"skip-backward-btn\": \"\\f54e\",\n  \"skip-backward-circle-fill\": \"\\f54f\",\n  \"skip-backward-circle\": \"\\f550\",\n  \"skip-backward-fill\": \"\\f551\",\n  \"skip-backward\": \"\\f552\",\n  \"skip-end-btn-fill\": \"\\f553\",\n  \"skip-end-btn\": \"\\f554\",\n  \"skip-end-circle-fill\": \"\\f555\",\n  \"skip-end-circle\": \"\\f556\",\n  \"skip-end-fill\": \"\\f557\",\n  \"skip-end\": \"\\f558\",\n  \"skip-forward-btn-fill\": \"\\f559\",\n  \"skip-forward-btn\": \"\\f55a\",\n  \"skip-forward-circle-fill\": \"\\f55b\",\n  \"skip-forward-circle\": \"\\f55c\",\n  \"skip-forward-fill\": \"\\f55d\",\n  \"skip-forward\": \"\\f55e\",\n  \"skip-start-btn-fill\": \"\\f55f\",\n  \"skip-start-btn\": \"\\f560\",\n  \"skip-start-circle-fill\": \"\\f561\",\n  \"skip-start-circle\": \"\\f562\",\n  \"skip-start-fill\": \"\\f563\",\n  \"skip-start\": \"\\f564\",\n  \"slack\": \"\\f565\",\n  \"slash-circle-fill\": \"\\f566\",\n  \"slash-circle\": \"\\f567\",\n  \"slash-square-fill\": \"\\f568\",\n  \"slash-square\": \"\\f569\",\n  \"slash\": \"\\f56a\",\n  \"sliders\": \"\\f56b\",\n  \"smartwatch\": \"\\f56c\",\n  \"snow\": \"\\f56d\",\n  \"snow2\": \"\\f56e\",\n  \"snow3\": \"\\f56f\",\n  \"sort-alpha-down-alt\": \"\\f570\",\n  \"sort-alpha-down\": \"\\f571\",\n  \"sort-alpha-up-alt\": \"\\f572\",\n  \"sort-alpha-up\": \"\\f573\",\n  \"sort-down-alt\": \"\\f574\",\n  \"sort-down\": \"\\f575\",\n  \"sort-numeric-down-alt\": \"\\f576\",\n  \"sort-numeric-down\": \"\\f577\",\n  \"sort-numeric-up-alt\": \"\\f578\",\n  \"sort-numeric-up\": \"\\f579\",\n  \"sort-up-alt\": \"\\f57a\",\n  \"sort-up\": \"\\f57b\",\n  \"soundwave\": \"\\f57c\",\n  \"speaker-fill\": \"\\f57d\",\n  \"speaker\": \"\\f57e\",\n  \"speedometer\": \"\\f57f\",\n  \"speedometer2\": \"\\f580\",\n  \"spellcheck\": \"\\f581\",\n  \"square-fill\": \"\\f582\",\n  \"square-half\": \"\\f583\",\n  \"square\": \"\\f584\",\n  \"stack\": \"\\f585\",\n  \"star-fill\": \"\\f586\",\n  \"star-half\": \"\\f587\",\n  \"star\": \"\\f588\",\n  \"stars\": \"\\f589\",\n  \"stickies-fill\": \"\\f58a\",\n  \"stickies\": \"\\f58b\",\n  \"sticky-fill\": \"\\f58c\",\n  \"sticky\": \"\\f58d\",\n  \"stop-btn-fill\": \"\\f58e\",\n  \"stop-btn\": \"\\f58f\",\n  \"stop-circle-fill\": \"\\f590\",\n  \"stop-circle\": \"\\f591\",\n  \"stop-fill\": \"\\f592\",\n  \"stop\": \"\\f593\",\n  \"stoplights-fill\": \"\\f594\",\n  \"stoplights\": \"\\f595\",\n  \"stopwatch-fill\": \"\\f596\",\n  \"stopwatch\": \"\\f597\",\n  \"subtract\": \"\\f598\",\n  \"suit-club-fill\": \"\\f599\",\n  \"suit-club\": \"\\f59a\",\n  \"suit-diamond-fill\": \"\\f59b\",\n  \"suit-diamond\": \"\\f59c\",\n  \"suit-heart-fill\": \"\\f59d\",\n  \"suit-heart\": \"\\f59e\",\n  \"suit-spade-fill\": \"\\f59f\",\n  \"suit-spade\": \"\\f5a0\",\n  \"sun-fill\": \"\\f5a1\",\n  \"sun\": \"\\f5a2\",\n  \"sunglasses\": \"\\f5a3\",\n  \"sunrise-fill\": \"\\f5a4\",\n  \"sunrise\": \"\\f5a5\",\n  \"sunset-fill\": \"\\f5a6\",\n  \"sunset\": \"\\f5a7\",\n  \"symmetry-horizontal\": \"\\f5a8\",\n  \"symmetry-vertical\": \"\\f5a9\",\n  \"table\": \"\\f5aa\",\n  \"tablet-fill\": \"\\f5ab\",\n  \"tablet-landscape-fill\": \"\\f5ac\",\n  \"tablet-landscape\": \"\\f5ad\",\n  \"tablet\": \"\\f5ae\",\n  \"tag-fill\": \"\\f5af\",\n  \"tag\": \"\\f5b0\",\n  \"tags-fill\": \"\\f5b1\",\n  \"tags\": \"\\f5b2\",\n  \"telegram\": \"\\f5b3\",\n  \"telephone-fill\": \"\\f5b4\",\n  \"telephone-forward-fill\": \"\\f5b5\",\n  \"telephone-forward\": \"\\f5b6\",\n  \"telephone-inbound-fill\": \"\\f5b7\",\n  \"telephone-inbound\": \"\\f5b8\",\n  \"telephone-minus-fill\": \"\\f5b9\",\n  \"telephone-minus\": \"\\f5ba\",\n  \"telephone-outbound-fill\": \"\\f5bb\",\n  \"telephone-outbound\": \"\\f5bc\",\n  \"telephone-plus-fill\": \"\\f5bd\",\n  \"telephone-plus\": \"\\f5be\",\n  \"telephone-x-fill\": \"\\f5bf\",\n  \"telephone-x\": \"\\f5c0\",\n  \"telephone\": \"\\f5c1\",\n  \"terminal-fill\": \"\\f5c2\",\n  \"terminal\": \"\\f5c3\",\n  \"text-center\": \"\\f5c4\",\n  \"text-indent-left\": \"\\f5c5\",\n  \"text-indent-right\": \"\\f5c6\",\n  \"text-left\": \"\\f5c7\",\n  \"text-paragraph\": \"\\f5c8\",\n  \"text-right\": \"\\f5c9\",\n  \"textarea-resize\": \"\\f5ca\",\n  \"textarea-t\": \"\\f5cb\",\n  \"textarea\": \"\\f5cc\",\n  \"thermometer-half\": \"\\f5cd\",\n  \"thermometer-high\": \"\\f5ce\",\n  \"thermometer-low\": \"\\f5cf\",\n  \"thermometer-snow\": \"\\f5d0\",\n  \"thermometer-sun\": \"\\f5d1\",\n  \"thermometer\": \"\\f5d2\",\n  \"three-dots-vertical\": \"\\f5d3\",\n  \"three-dots\": \"\\f5d4\",\n  \"toggle-off\": \"\\f5d5\",\n  \"toggle-on\": \"\\f5d6\",\n  \"toggle2-off\": \"\\f5d7\",\n  \"toggle2-on\": \"\\f5d8\",\n  \"toggles\": \"\\f5d9\",\n  \"toggles2\": \"\\f5da\",\n  \"tools\": \"\\f5db\",\n  \"tornado\": \"\\f5dc\",\n  \"trash-fill\": \"\\f5dd\",\n  \"trash\": \"\\f5de\",\n  \"trash2-fill\": \"\\f5df\",\n  \"trash2\": \"\\f5e0\",\n  \"tree-fill\": \"\\f5e1\",\n  \"tree\": \"\\f5e2\",\n  \"triangle-fill\": \"\\f5e3\",\n  \"triangle-half\": \"\\f5e4\",\n  \"triangle\": \"\\f5e5\",\n  \"trophy-fill\": \"\\f5e6\",\n  \"trophy\": \"\\f5e7\",\n  \"tropical-storm\": \"\\f5e8\",\n  \"truck-flatbed\": \"\\f5e9\",\n  \"truck\": \"\\f5ea\",\n  \"tsunami\": \"\\f5eb\",\n  \"tv-fill\": \"\\f5ec\",\n  \"tv\": \"\\f5ed\",\n  \"twitch\": \"\\f5ee\",\n  \"twitter\": \"\\f5ef\",\n  \"type-bold\": \"\\f5f0\",\n  \"type-h1\": \"\\f5f1\",\n  \"type-h2\": \"\\f5f2\",\n  \"type-h3\": \"\\f5f3\",\n  \"type-italic\": \"\\f5f4\",\n  \"type-strikethrough\": \"\\f5f5\",\n  \"type-underline\": \"\\f5f6\",\n  \"type\": \"\\f5f7\",\n  \"ui-checks-grid\": \"\\f5f8\",\n  \"ui-checks\": \"\\f5f9\",\n  \"ui-radios-grid\": \"\\f5fa\",\n  \"ui-radios\": \"\\f5fb\",\n  \"umbrella-fill\": \"\\f5fc\",\n  \"umbrella\": \"\\f5fd\",\n  \"union\": \"\\f5fe\",\n  \"unlock-fill\": \"\\f5ff\",\n  \"unlock\": \"\\f600\",\n  \"upc-scan\": \"\\f601\",\n  \"upc\": \"\\f602\",\n  \"upload\": \"\\f603\",\n  \"vector-pen\": \"\\f604\",\n  \"view-list\": \"\\f605\",\n  \"view-stacked\": \"\\f606\",\n  \"vinyl-fill\": \"\\f607\",\n  \"vinyl\": \"\\f608\",\n  \"voicemail\": \"\\f609\",\n  \"volume-down-fill\": \"\\f60a\",\n  \"volume-down\": \"\\f60b\",\n  \"volume-mute-fill\": \"\\f60c\",\n  \"volume-mute\": \"\\f60d\",\n  \"volume-off-fill\": \"\\f60e\",\n  \"volume-off\": \"\\f60f\",\n  \"volume-up-fill\": \"\\f610\",\n  \"volume-up\": \"\\f611\",\n  \"vr\": \"\\f612\",\n  \"wallet-fill\": \"\\f613\",\n  \"wallet\": \"\\f614\",\n  \"wallet2\": \"\\f615\",\n  \"watch\": \"\\f616\",\n  \"water\": \"\\f617\",\n  \"whatsapp\": \"\\f618\",\n  \"wifi-1\": \"\\f619\",\n  \"wifi-2\": \"\\f61a\",\n  \"wifi-off\": \"\\f61b\",\n  \"wifi\": \"\\f61c\",\n  \"wind\": \"\\f61d\",\n  \"window-dock\": \"\\f61e\",\n  \"window-sidebar\": \"\\f61f\",\n  \"window\": \"\\f620\",\n  \"wrench\": \"\\f621\",\n  \"x-circle-fill\": \"\\f622\",\n  \"x-circle\": \"\\f623\",\n  \"x-diamond-fill\": \"\\f624\",\n  \"x-diamond\": \"\\f625\",\n  \"x-octagon-fill\": \"\\f626\",\n  \"x-octagon\": \"\\f627\",\n  \"x-square-fill\": \"\\f628\",\n  \"x-square\": \"\\f629\",\n  \"x\": \"\\f62a\",\n  \"youtube\": \"\\f62b\",\n  \"zoom-in\": \"\\f62c\",\n  \"zoom-out\": \"\\f62d\",\n  \"bank\": \"\\f62e\",\n  \"bank2\": \"\\f62f\",\n  \"bell-slash-fill\": \"\\f630\",\n  \"bell-slash\": \"\\f631\",\n  \"cash-coin\": \"\\f632\",\n  \"check-lg\": \"\\f633\",\n  \"coin\": \"\\f634\",\n  \"currency-bitcoin\": \"\\f635\",\n  \"currency-dollar\": \"\\f636\",\n  \"currency-euro\": \"\\f637\",\n  \"currency-exchange\": \"\\f638\",\n  \"currency-pound\": \"\\f639\",\n  \"currency-yen\": \"\\f63a\",\n  \"dash-lg\": \"\\f63b\",\n  \"exclamation-lg\": \"\\f63c\",\n  \"file-earmark-pdf-fill\": \"\\f63d\",\n  \"file-earmark-pdf\": \"\\f63e\",\n  \"file-pdf-fill\": \"\\f63f\",\n  \"file-pdf\": \"\\f640\",\n  \"gender-ambiguous\": \"\\f641\",\n  \"gender-female\": \"\\f642\",\n  \"gender-male\": \"\\f643\",\n  \"gender-trans\": \"\\f644\",\n  \"headset-vr\": \"\\f645\",\n  \"info-lg\": \"\\f646\",\n  \"mastodon\": \"\\f647\",\n  \"messenger\": \"\\f648\",\n  \"piggy-bank-fill\": \"\\f649\",\n  \"piggy-bank\": \"\\f64a\",\n  \"pin-map-fill\": \"\\f64b\",\n  \"pin-map\": \"\\f64c\",\n  \"plus-lg\": \"\\f64d\",\n  \"question-lg\": \"\\f64e\",\n  \"recycle\": \"\\f64f\",\n  \"reddit\": \"\\f650\",\n  \"safe-fill\": \"\\f651\",\n  \"safe2-fill\": \"\\f652\",\n  \"safe2\": \"\\f653\",\n  \"sd-card-fill\": \"\\f654\",\n  \"sd-card\": \"\\f655\",\n  \"skype\": \"\\f656\",\n  \"slash-lg\": \"\\f657\",\n  \"translate\": \"\\f658\",\n  \"x-lg\": \"\\f659\",\n  \"safe\": \"\\f65a\",\n  \"apple\": \"\\f65b\",\n  \"microsoft\": \"\\f65d\",\n  \"windows\": \"\\f65e\",\n  \"behance\": \"\\f65c\",\n  \"dribbble\": \"\\f65f\",\n  \"line\": \"\\f660\",\n  \"medium\": \"\\f661\",\n  \"paypal\": \"\\f662\",\n  \"pinterest\": \"\\f663\",\n  \"signal\": \"\\f664\",\n  \"snapchat\": \"\\f665\",\n  \"spotify\": \"\\f666\",\n  \"stack-overflow\": \"\\f667\",\n  \"strava\": \"\\f668\",\n  \"wordpress\": \"\\f669\",\n  \"vimeo\": \"\\f66a\",\n  \"activity\": \"\\f66b\",\n  \"easel2-fill\": \"\\f66c\",\n  \"easel2\": \"\\f66d\",\n  \"easel3-fill\": \"\\f66e\",\n  \"easel3\": \"\\f66f\",\n  \"fan\": \"\\f670\",\n  \"fingerprint\": \"\\f671\",\n  \"graph-down-arrow\": \"\\f672\",\n  \"graph-up-arrow\": \"\\f673\",\n  \"hypnotize\": \"\\f674\",\n  \"magic\": \"\\f675\",\n  \"person-rolodex\": \"\\f676\",\n  \"person-video\": \"\\f677\",\n  \"person-video2\": \"\\f678\",\n  \"person-video3\": \"\\f679\",\n  \"person-workspace\": \"\\f67a\",\n  \"radioactive\": \"\\f67b\",\n  \"webcam-fill\": \"\\f67c\",\n  \"webcam\": \"\\f67d\",\n  \"yin-yang\": \"\\f67e\",\n  \"bandaid-fill\": \"\\f680\",\n  \"bandaid\": \"\\f681\",\n  \"bluetooth\": \"\\f682\",\n  \"body-text\": \"\\f683\",\n  \"boombox\": \"\\f684\",\n  \"boxes\": \"\\f685\",\n  \"dpad-fill\": \"\\f686\",\n  \"dpad\": \"\\f687\",\n  \"ear-fill\": \"\\f688\",\n  \"ear\": \"\\f689\",\n  \"envelope-check-1\": \"\\f68a\",\n  \"envelope-check-fill\": \"\\f68b\",\n  \"envelope-check\": \"\\f68c\",\n  \"envelope-dash-1\": \"\\f68d\",\n  \"envelope-dash-fill\": \"\\f68e\",\n  \"envelope-dash\": \"\\f68f\",\n  \"envelope-exclamation-1\": \"\\f690\",\n  \"envelope-exclamation-fill\": \"\\f691\",\n  \"envelope-exclamation\": \"\\f692\",\n  \"envelope-plus-fill\": \"\\f693\",\n  \"envelope-plus\": \"\\f694\",\n  \"envelope-slash-1\": \"\\f695\",\n  \"envelope-slash-fill\": \"\\f696\",\n  \"envelope-slash\": \"\\f697\",\n  \"envelope-x-1\": \"\\f698\",\n  \"envelope-x-fill\": \"\\f699\",\n  \"envelope-x\": \"\\f69a\",\n  \"explicit-fill\": \"\\f69b\",\n  \"explicit\": \"\\f69c\",\n  \"git\": \"\\f69d\",\n  \"infinity\": \"\\f69e\",\n  \"list-columns-reverse\": \"\\f69f\",\n  \"list-columns\": \"\\f6a0\",\n  \"meta\": \"\\f6a1\",\n  \"mortorboard-fill\": \"\\f6a2\",\n  \"mortorboard\": \"\\f6a3\",\n  \"nintendo-switch\": \"\\f6a4\",\n  \"pc-display-horizontal\": \"\\f6a5\",\n  \"pc-display\": \"\\f6a6\",\n  \"pc-horizontal\": \"\\f6a7\",\n  \"pc\": \"\\f6a8\",\n  \"playstation\": \"\\f6a9\",\n  \"plus-slash-minus\": \"\\f6aa\",\n  \"projector-fill\": \"\\f6ab\",\n  \"projector\": \"\\f6ac\",\n  \"qr-code-scan\": \"\\f6ad\",\n  \"qr-code\": \"\\f6ae\",\n  \"quora\": \"\\f6af\",\n  \"quote\": \"\\f6b0\",\n  \"robot\": \"\\f6b1\",\n  \"send-check-fill\": \"\\f6b2\",\n  \"send-check\": \"\\f6b3\",\n  \"send-dash-fill\": \"\\f6b4\",\n  \"send-dash\": \"\\f6b5\",\n  \"send-exclamation-1\": \"\\f6b6\",\n  \"send-exclamation-fill\": \"\\f6b7\",\n  \"send-exclamation\": \"\\f6b8\",\n  \"send-fill\": \"\\f6b9\",\n  \"send-plus-fill\": \"\\f6ba\",\n  \"send-plus\": \"\\f6bb\",\n  \"send-slash-fill\": \"\\f6bc\",\n  \"send-slash\": \"\\f6bd\",\n  \"send-x-fill\": \"\\f6be\",\n  \"send-x\": \"\\f6bf\",\n  \"send\": \"\\f6c0\",\n  \"steam\": \"\\f6c1\",\n  \"terminal-dash-1\": \"\\f6c2\",\n  \"terminal-dash\": \"\\f6c3\",\n  \"terminal-plus\": \"\\f6c4\",\n  \"terminal-split\": \"\\f6c5\",\n  \"ticket-detailed-fill\": \"\\f6c6\",\n  \"ticket-detailed\": \"\\f6c7\",\n  \"ticket-fill\": \"\\f6c8\",\n  \"ticket-perforated-fill\": \"\\f6c9\",\n  \"ticket-perforated\": \"\\f6ca\",\n  \"ticket\": \"\\f6cb\",\n  \"tiktok\": \"\\f6cc\",\n  \"window-dash\": \"\\f6cd\",\n  \"window-desktop\": \"\\f6ce\",\n  \"window-fullscreen\": \"\\f6cf\",\n  \"window-plus\": \"\\f6d0\",\n  \"window-split\": \"\\f6d1\",\n  \"window-stack\": \"\\f6d2\",\n  \"window-x\": \"\\f6d3\",\n  \"xbox\": \"\\f6d4\",\n  \"ethernet\": \"\\f6d5\",\n  \"hdmi-fill\": \"\\f6d6\",\n  \"hdmi\": \"\\f6d7\",\n  \"usb-c-fill\": \"\\f6d8\",\n  \"usb-c\": \"\\f6d9\",\n  \"usb-fill\": \"\\f6da\",\n  \"usb-plug-fill\": \"\\f6db\",\n  \"usb-plug\": \"\\f6dc\",\n  \"usb-symbol\": \"\\f6dd\",\n  \"usb\": \"\\f6de\",\n  \"boombox-fill\": \"\\f6df\",\n  \"displayport-1\": \"\\f6e0\",\n  \"displayport\": \"\\f6e1\",\n  \"gpu-card\": \"\\f6e2\",\n  \"memory\": \"\\f6e3\",\n  \"modem-fill\": \"\\f6e4\",\n  \"modem\": \"\\f6e5\",\n  \"motherboard-fill\": \"\\f6e6\",\n  \"motherboard\": \"\\f6e7\",\n  \"optical-audio-fill\": \"\\f6e8\",\n  \"optical-audio\": \"\\f6e9\",\n  \"pci-card\": \"\\f6ea\",\n  \"router-fill\": \"\\f6eb\",\n  \"router\": \"\\f6ec\",\n  \"ssd-fill\": \"\\f6ed\",\n  \"ssd\": \"\\f6ee\",\n  \"thunderbolt-fill\": \"\\f6ef\",\n  \"thunderbolt\": \"\\f6f0\",\n  \"usb-drive-fill\": \"\\f6f1\",\n  \"usb-drive\": \"\\f6f2\",\n  \"usb-micro-fill\": \"\\f6f3\",\n  \"usb-micro\": \"\\f6f4\",\n  \"usb-mini-fill\": \"\\f6f5\",\n  \"usb-mini\": \"\\f6f6\",\n  \"cloud-haze2\": \"\\f6f7\",\n  \"device-hdd-fill\": \"\\f6f8\",\n  \"device-hdd\": \"\\f6f9\",\n  \"device-ssd-fill\": \"\\f6fa\",\n  \"device-ssd\": \"\\f6fb\",\n  \"displayport-fill\": \"\\f6fc\",\n  \"mortarboard-fill\": \"\\f6fd\",\n  \"mortarboard\": \"\\f6fe\",\n  \"terminal-x\": \"\\f6ff\",\n  \"arrow-through-heart-fill\": \"\\f700\",\n  \"arrow-through-heart\": \"\\f701\",\n  \"badge-sd-fill\": \"\\f702\",\n  \"badge-sd\": \"\\f703\",\n  \"bag-heart-fill\": \"\\f704\",\n  \"bag-heart\": \"\\f705\",\n  \"balloon-fill\": \"\\f706\",\n  \"balloon-heart-fill\": \"\\f707\",\n  \"balloon-heart\": \"\\f708\",\n  \"balloon\": \"\\f709\",\n  \"box2-fill\": \"\\f70a\",\n  \"box2-heart-fill\": \"\\f70b\",\n  \"box2-heart\": \"\\f70c\",\n  \"box2\": \"\\f70d\",\n  \"braces-asterisk\": \"\\f70e\",\n  \"calendar-heart-fill\": \"\\f70f\",\n  \"calendar-heart\": \"\\f710\",\n  \"calendar2-heart-fill\": \"\\f711\",\n  \"calendar2-heart\": \"\\f712\",\n  \"chat-heart-fill\": \"\\f713\",\n  \"chat-heart\": \"\\f714\",\n  \"chat-left-heart-fill\": \"\\f715\",\n  \"chat-left-heart\": \"\\f716\",\n  \"chat-right-heart-fill\": \"\\f717\",\n  \"chat-right-heart\": \"\\f718\",\n  \"chat-square-heart-fill\": \"\\f719\",\n  \"chat-square-heart\": \"\\f71a\",\n  \"clipboard-check-fill\": \"\\f71b\",\n  \"clipboard-data-fill\": \"\\f71c\",\n  \"clipboard-fill\": \"\\f71d\",\n  \"clipboard-heart-fill\": \"\\f71e\",\n  \"clipboard-heart\": \"\\f71f\",\n  \"clipboard-minus-fill\": \"\\f720\",\n  \"clipboard-plus-fill\": \"\\f721\",\n  \"clipboard-pulse\": \"\\f722\",\n  \"clipboard-x-fill\": \"\\f723\",\n  \"clipboard2-check-fill\": \"\\f724\",\n  \"clipboard2-check\": \"\\f725\",\n  \"clipboard2-data-fill\": \"\\f726\",\n  \"clipboard2-data\": \"\\f727\",\n  \"clipboard2-fill\": \"\\f728\",\n  \"clipboard2-heart-fill\": \"\\f729\",\n  \"clipboard2-heart\": \"\\f72a\",\n  \"clipboard2-minus-fill\": \"\\f72b\",\n  \"clipboard2-minus\": \"\\f72c\",\n  \"clipboard2-plus-fill\": \"\\f72d\",\n  \"clipboard2-plus\": \"\\f72e\",\n  \"clipboard2-pulse-fill\": \"\\f72f\",\n  \"clipboard2-pulse\": \"\\f730\",\n  \"clipboard2-x-fill\": \"\\f731\",\n  \"clipboard2-x\": \"\\f732\",\n  \"clipboard2\": \"\\f733\",\n  \"emoji-kiss-fill\": \"\\f734\",\n  \"emoji-kiss\": \"\\f735\",\n  \"envelope-heart-fill\": \"\\f736\",\n  \"envelope-heart\": \"\\f737\",\n  \"envelope-open-heart-fill\": \"\\f738\",\n  \"envelope-open-heart\": \"\\f739\",\n  \"envelope-paper-fill\": \"\\f73a\",\n  \"envelope-paper-heart-fill\": \"\\f73b\",\n  \"envelope-paper-heart\": \"\\f73c\",\n  \"envelope-paper\": \"\\f73d\",\n  \"filetype-aac\": \"\\f73e\",\n  \"filetype-ai\": \"\\f73f\",\n  \"filetype-bmp\": \"\\f740\",\n  \"filetype-cs\": \"\\f741\",\n  \"filetype-css\": \"\\f742\",\n  \"filetype-csv\": \"\\f743\",\n  \"filetype-doc\": \"\\f744\",\n  \"filetype-docx\": \"\\f745\",\n  \"filetype-exe\": \"\\f746\",\n  \"filetype-gif\": \"\\f747\",\n  \"filetype-heic\": \"\\f748\",\n  \"filetype-html\": \"\\f749\",\n  \"filetype-java\": \"\\f74a\",\n  \"filetype-jpg\": \"\\f74b\",\n  \"filetype-js\": \"\\f74c\",\n  \"filetype-jsx\": \"\\f74d\",\n  \"filetype-key\": \"\\f74e\",\n  \"filetype-m4p\": \"\\f74f\",\n  \"filetype-md\": \"\\f750\",\n  \"filetype-mdx\": \"\\f751\",\n  \"filetype-mov\": \"\\f752\",\n  \"filetype-mp3\": \"\\f753\",\n  \"filetype-mp4\": \"\\f754\",\n  \"filetype-otf\": \"\\f755\",\n  \"filetype-pdf\": \"\\f756\",\n  \"filetype-php\": \"\\f757\",\n  \"filetype-png\": \"\\f758\",\n  \"filetype-ppt-1\": \"\\f759\",\n  \"filetype-ppt\": \"\\f75a\",\n  \"filetype-psd\": \"\\f75b\",\n  \"filetype-py\": \"\\f75c\",\n  \"filetype-raw\": \"\\f75d\",\n  \"filetype-rb\": \"\\f75e\",\n  \"filetype-sass\": \"\\f75f\",\n  \"filetype-scss\": \"\\f760\",\n  \"filetype-sh\": \"\\f761\",\n  \"filetype-svg\": \"\\f762\",\n  \"filetype-tiff\": \"\\f763\",\n  \"filetype-tsx\": \"\\f764\",\n  \"filetype-ttf\": \"\\f765\",\n  \"filetype-txt\": \"\\f766\",\n  \"filetype-wav\": \"\\f767\",\n  \"filetype-woff\": \"\\f768\",\n  \"filetype-xls-1\": \"\\f769\",\n  \"filetype-xls\": \"\\f76a\",\n  \"filetype-xml\": \"\\f76b\",\n  \"filetype-yml\": \"\\f76c\",\n  \"heart-arrow\": \"\\f76d\",\n  \"heart-pulse-fill\": \"\\f76e\",\n  \"heart-pulse\": \"\\f76f\",\n  \"heartbreak-fill\": \"\\f770\",\n  \"heartbreak\": \"\\f771\",\n  \"hearts\": \"\\f772\",\n  \"hospital-fill\": \"\\f773\",\n  \"hospital\": \"\\f774\",\n  \"house-heart-fill\": \"\\f775\",\n  \"house-heart\": \"\\f776\",\n  \"incognito\": \"\\f777\",\n  \"magnet-fill\": \"\\f778\",\n  \"magnet\": \"\\f779\",\n  \"person-heart\": \"\\f77a\",\n  \"person-hearts\": \"\\f77b\",\n  \"phone-flip\": \"\\f77c\",\n  \"plugin\": \"\\f77d\",\n  \"postage-fill\": \"\\f77e\",\n  \"postage-heart-fill\": \"\\f77f\",\n  \"postage-heart\": \"\\f780\",\n  \"postage\": \"\\f781\",\n  \"postcard-fill\": \"\\f782\",\n  \"postcard-heart-fill\": \"\\f783\",\n  \"postcard-heart\": \"\\f784\",\n  \"postcard\": \"\\f785\",\n  \"search-heart-fill\": \"\\f786\",\n  \"search-heart\": \"\\f787\",\n  \"sliders2-vertical\": \"\\f788\",\n  \"sliders2\": \"\\f789\",\n  \"trash3-fill\": \"\\f78a\",\n  \"trash3\": \"\\f78b\",\n  \"valentine\": \"\\f78c\",\n  \"valentine2\": \"\\f78d\",\n  \"wrench-adjustable-circle-fill\": \"\\f78e\",\n  \"wrench-adjustable-circle\": \"\\f78f\",\n  \"wrench-adjustable\": \"\\f790\",\n  \"filetype-json\": \"\\f791\",\n  \"filetype-pptx\": \"\\f792\",\n  \"filetype-xlsx\": \"\\f793\",\n  \"1-circle-1\": \"\\f794\",\n  \"1-circle-fill-1\": \"\\f795\",\n  \"1-circle-fill\": \"\\f796\",\n  \"1-circle\": \"\\f797\",\n  \"1-square-fill\": \"\\f798\",\n  \"1-square\": \"\\f799\",\n  \"2-circle-1\": \"\\f79a\",\n  \"2-circle-fill-1\": \"\\f79b\",\n  \"2-circle-fill\": \"\\f79c\",\n  \"2-circle\": \"\\f79d\",\n  \"2-square-fill\": \"\\f79e\",\n  \"2-square\": \"\\f79f\",\n  \"3-circle-1\": \"\\f7a0\",\n  \"3-circle-fill-1\": \"\\f7a1\",\n  \"3-circle-fill\": \"\\f7a2\",\n  \"3-circle\": \"\\f7a3\",\n  \"3-square-fill\": \"\\f7a4\",\n  \"3-square\": \"\\f7a5\",\n  \"4-circle-1\": \"\\f7a6\",\n  \"4-circle-fill-1\": \"\\f7a7\",\n  \"4-circle-fill\": \"\\f7a8\",\n  \"4-circle\": \"\\f7a9\",\n  \"4-square-fill\": \"\\f7aa\",\n  \"4-square\": \"\\f7ab\",\n  \"5-circle-1\": \"\\f7ac\",\n  \"5-circle-fill-1\": \"\\f7ad\",\n  \"5-circle-fill\": \"\\f7ae\",\n  \"5-circle\": \"\\f7af\",\n  \"5-square-fill\": \"\\f7b0\",\n  \"5-square\": \"\\f7b1\",\n  \"6-circle-1\": \"\\f7b2\",\n  \"6-circle-fill-1\": \"\\f7b3\",\n  \"6-circle-fill\": \"\\f7b4\",\n  \"6-circle\": \"\\f7b5\",\n  \"6-square-fill\": \"\\f7b6\",\n  \"6-square\": \"\\f7b7\",\n  \"7-circle-1\": \"\\f7b8\",\n  \"7-circle-fill-1\": \"\\f7b9\",\n  \"7-circle-fill\": \"\\f7ba\",\n  \"7-circle\": \"\\f7bb\",\n  \"7-square-fill\": \"\\f7bc\",\n  \"7-square\": \"\\f7bd\",\n  \"8-circle-1\": \"\\f7be\",\n  \"8-circle-fill-1\": \"\\f7bf\",\n  \"8-circle-fill\": \"\\f7c0\",\n  \"8-circle\": \"\\f7c1\",\n  \"8-square-fill\": \"\\f7c2\",\n  \"8-square\": \"\\f7c3\",\n  \"9-circle-1\": \"\\f7c4\",\n  \"9-circle-fill-1\": \"\\f7c5\",\n  \"9-circle-fill\": \"\\f7c6\",\n  \"9-circle\": \"\\f7c7\",\n  \"9-square-fill\": \"\\f7c8\",\n  \"9-square\": \"\\f7c9\",\n  \"airplane-engines-fill\": \"\\f7ca\",\n  \"airplane-engines\": \"\\f7cb\",\n  \"airplane-fill\": \"\\f7cc\",\n  \"airplane\": \"\\f7cd\",\n  \"alexa\": \"\\f7ce\",\n  \"alipay\": \"\\f7cf\",\n  \"android\": \"\\f7d0\",\n  \"android2\": \"\\f7d1\",\n  \"box-fill\": \"\\f7d2\",\n  \"box-seam-fill\": \"\\f7d3\",\n  \"browser-chrome\": \"\\f7d4\",\n  \"browser-edge\": \"\\f7d5\",\n  \"browser-firefox\": \"\\f7d6\",\n  \"browser-safari\": \"\\f7d7\",\n  \"c-circle-1\": \"\\f7d8\",\n  \"c-circle-fill-1\": \"\\f7d9\",\n  \"c-circle-fill\": \"\\f7da\",\n  \"c-circle\": \"\\f7db\",\n  \"c-square-fill\": \"\\f7dc\",\n  \"c-square\": \"\\f7dd\",\n  \"capsule-pill\": \"\\f7de\",\n  \"capsule\": \"\\f7df\",\n  \"car-front-fill\": \"\\f7e0\",\n  \"car-front\": \"\\f7e1\",\n  \"cassette-fill\": \"\\f7e2\",\n  \"cassette\": \"\\f7e3\",\n  \"cc-circle-1\": \"\\f7e4\",\n  \"cc-circle-fill-1\": \"\\f7e5\",\n  \"cc-circle-fill\": \"\\f7e6\",\n  \"cc-circle\": \"\\f7e7\",\n  \"cc-square-fill\": \"\\f7e8\",\n  \"cc-square\": \"\\f7e9\",\n  \"cup-hot-fill\": \"\\f7ea\",\n  \"cup-hot\": \"\\f7eb\",\n  \"currency-rupee\": \"\\f7ec\",\n  \"dropbox\": \"\\f7ed\",\n  \"escape\": \"\\f7ee\",\n  \"fast-forward-btn-fill\": \"\\f7ef\",\n  \"fast-forward-btn\": \"\\f7f0\",\n  \"fast-forward-circle-fill\": \"\\f7f1\",\n  \"fast-forward-circle\": \"\\f7f2\",\n  \"fast-forward-fill\": \"\\f7f3\",\n  \"fast-forward\": \"\\f7f4\",\n  \"filetype-sql\": \"\\f7f5\",\n  \"fire\": \"\\f7f6\",\n  \"google-play\": \"\\f7f7\",\n  \"h-circle-1\": \"\\f7f8\",\n  \"h-circle-fill-1\": \"\\f7f9\",\n  \"h-circle-fill\": \"\\f7fa\",\n  \"h-circle\": \"\\f7fb\",\n  \"h-square-fill\": \"\\f7fc\",\n  \"h-square\": \"\\f7fd\",\n  \"indent\": \"\\f7fe\",\n  \"lungs-fill\": \"\\f7ff\",\n  \"lungs\": \"\\f800\",\n  \"microsoft-teams\": \"\\f801\",\n  \"p-circle-1\": \"\\f802\",\n  \"p-circle-fill-1\": \"\\f803\",\n  \"p-circle-fill\": \"\\f804\",\n  \"p-circle\": \"\\f805\",\n  \"p-square-fill\": \"\\f806\",\n  \"p-square\": \"\\f807\",\n  \"pass-fill\": \"\\f808\",\n  \"pass\": \"\\f809\",\n  \"prescription\": \"\\f80a\",\n  \"prescription2\": \"\\f80b\",\n  \"r-circle-1\": \"\\f80c\",\n  \"r-circle-fill-1\": \"\\f80d\",\n  \"r-circle-fill\": \"\\f80e\",\n  \"r-circle\": \"\\f80f\",\n  \"r-square-fill\": \"\\f810\",\n  \"r-square\": \"\\f811\",\n  \"repeat-1\": \"\\f812\",\n  \"repeat\": \"\\f813\",\n  \"rewind-btn-fill\": \"\\f814\",\n  \"rewind-btn\": \"\\f815\",\n  \"rewind-circle-fill\": \"\\f816\",\n  \"rewind-circle\": \"\\f817\",\n  \"rewind-fill\": \"\\f818\",\n  \"rewind\": \"\\f819\",\n  \"train-freight-front-fill\": \"\\f81a\",\n  \"train-freight-front\": \"\\f81b\",\n  \"train-front-fill\": \"\\f81c\",\n  \"train-front\": \"\\f81d\",\n  \"train-lightrail-front-fill\": \"\\f81e\",\n  \"train-lightrail-front\": \"\\f81f\",\n  \"truck-front-fill\": \"\\f820\",\n  \"truck-front\": \"\\f821\",\n  \"ubuntu\": \"\\f822\",\n  \"unindent\": \"\\f823\",\n  \"unity\": \"\\f824\",\n  \"universal-access-circle\": \"\\f825\",\n  \"universal-access\": \"\\f826\",\n  \"virus\": \"\\f827\",\n  \"virus2\": \"\\f828\",\n  \"wechat\": \"\\f829\",\n  \"yelp\": \"\\f82a\",\n  \"sign-stop-fill\": \"\\f82b\",\n  \"sign-stop-lights-fill\": \"\\f82c\",\n  \"sign-stop-lights\": \"\\f82d\",\n  \"sign-stop\": \"\\f82e\",\n  \"sign-turn-left-fill\": \"\\f82f\",\n  \"sign-turn-left\": \"\\f830\",\n  \"sign-turn-right-fill\": \"\\f831\",\n  \"sign-turn-right\": \"\\f832\",\n  \"sign-turn-slight-left-fill\": \"\\f833\",\n  \"sign-turn-slight-left\": \"\\f834\",\n  \"sign-turn-slight-right-fill\": \"\\f835\",\n  \"sign-turn-slight-right\": \"\\f836\",\n  \"sign-yield-fill\": \"\\f837\",\n  \"sign-yield\": \"\\f838\",\n  \"ev-station-fill\": \"\\f839\",\n  \"ev-station\": \"\\f83a\",\n  \"fuel-pump-diesel-fill\": \"\\f83b\",\n  \"fuel-pump-diesel\": \"\\f83c\",\n  \"fuel-pump-fill\": \"\\f83d\",\n  \"fuel-pump\": \"\\f83e\",\n  \"0-circle-fill\": \"\\f83f\",\n  \"0-circle\": \"\\f840\",\n  \"0-square-fill\": \"\\f841\",\n  \"0-square\": \"\\f842\",\n  \"rocket-fill\": \"\\f843\",\n  \"rocket-takeoff-fill\": \"\\f844\",\n  \"rocket-takeoff\": \"\\f845\",\n  \"rocket\": \"\\f846\",\n  \"stripe\": \"\\f847\",\n  \"subscript\": \"\\f848\",\n  \"superscript\": \"\\f849\",\n  \"trello\": \"\\f84a\",\n  \"envelope-at-fill\": \"\\f84b\",\n  \"envelope-at\": \"\\f84c\",\n  \"regex\": \"\\f84d\",\n  \"text-wrap\": \"\\f84e\",\n  \"sign-dead-end-fill\": \"\\f84f\",\n  \"sign-dead-end\": \"\\f850\",\n  \"sign-do-not-enter-fill\": \"\\f851\",\n  \"sign-do-not-enter\": \"\\f852\",\n  \"sign-intersection-fill\": \"\\f853\",\n  \"sign-intersection-side-fill\": \"\\f854\",\n  \"sign-intersection-side\": \"\\f855\",\n  \"sign-intersection-t-fill\": \"\\f856\",\n  \"sign-intersection-t\": \"\\f857\",\n  \"sign-intersection-y-fill\": \"\\f858\",\n  \"sign-intersection-y\": \"\\f859\",\n  \"sign-intersection\": \"\\f85a\",\n  \"sign-merge-left-fill\": \"\\f85b\",\n  \"sign-merge-left\": \"\\f85c\",\n  \"sign-merge-right-fill\": \"\\f85d\",\n  \"sign-merge-right\": \"\\f85e\",\n  \"sign-no-left-turn-fill\": \"\\f85f\",\n  \"sign-no-left-turn\": \"\\f860\",\n  \"sign-no-parking-fill\": \"\\f861\",\n  \"sign-no-parking\": \"\\f862\",\n  \"sign-no-right-turn-fill\": \"\\f863\",\n  \"sign-no-right-turn\": \"\\f864\",\n  \"sign-railroad-fill\": \"\\f865\",\n  \"sign-railroad\": \"\\f866\",\n  \"building-add\": \"\\f867\",\n  \"building-check\": \"\\f868\",\n  \"building-dash\": \"\\f869\",\n  \"building-down\": \"\\f86a\",\n  \"building-exclamation\": \"\\f86b\",\n  \"building-fill-add\": \"\\f86c\",\n  \"building-fill-check\": \"\\f86d\",\n  \"building-fill-dash\": \"\\f86e\",\n  \"building-fill-down\": \"\\f86f\",\n  \"building-fill-exclamation\": \"\\f870\",\n  \"building-fill-gear\": \"\\f871\",\n  \"building-fill-lock\": \"\\f872\",\n  \"building-fill-slash\": \"\\f873\",\n  \"building-fill-up\": \"\\f874\",\n  \"building-fill-x\": \"\\f875\",\n  \"building-fill\": \"\\f876\",\n  \"building-gear\": \"\\f877\",\n  \"building-lock\": \"\\f878\",\n  \"building-slash\": \"\\f879\",\n  \"building-up\": \"\\f87a\",\n  \"building-x\": \"\\f87b\",\n  \"buildings-fill\": \"\\f87c\",\n  \"buildings\": \"\\f87d\",\n  \"bus-front-fill\": \"\\f87e\",\n  \"bus-front\": \"\\f87f\",\n  \"ev-front-fill\": \"\\f880\",\n  \"ev-front\": \"\\f881\",\n  \"globe-americas\": \"\\f882\",\n  \"globe-asia-australia\": \"\\f883\",\n  \"globe-central-south-asia\": \"\\f884\",\n  \"globe-europe-africa\": \"\\f885\",\n  \"house-add-fill\": \"\\f886\",\n  \"house-add\": \"\\f887\",\n  \"house-check-fill\": \"\\f888\",\n  \"house-check\": \"\\f889\",\n  \"house-dash-fill\": \"\\f88a\",\n  \"house-dash\": \"\\f88b\",\n  \"house-down-fill\": \"\\f88c\",\n  \"house-down\": \"\\f88d\",\n  \"house-exclamation-fill\": \"\\f88e\",\n  \"house-exclamation\": \"\\f88f\",\n  \"house-gear-fill\": \"\\f890\",\n  \"house-gear\": \"\\f891\",\n  \"house-lock-fill\": \"\\f892\",\n  \"house-lock\": \"\\f893\",\n  \"house-slash-fill\": \"\\f894\",\n  \"house-slash\": \"\\f895\",\n  \"house-up-fill\": \"\\f896\",\n  \"house-up\": \"\\f897\",\n  \"house-x-fill\": \"\\f898\",\n  \"house-x\": \"\\f899\",\n  \"person-add\": \"\\f89a\",\n  \"person-down\": \"\\f89b\",\n  \"person-exclamation\": \"\\f89c\",\n  \"person-fill-add\": \"\\f89d\",\n  \"person-fill-check\": \"\\f89e\",\n  \"person-fill-dash\": \"\\f89f\",\n  \"person-fill-down\": \"\\f8a0\",\n  \"person-fill-exclamation\": \"\\f8a1\",\n  \"person-fill-gear\": \"\\f8a2\",\n  \"person-fill-lock\": \"\\f8a3\",\n  \"person-fill-slash\": \"\\f8a4\",\n  \"person-fill-up\": \"\\f8a5\",\n  \"person-fill-x\": \"\\f8a6\",\n  \"person-gear\": \"\\f8a7\",\n  \"person-lock\": \"\\f8a8\",\n  \"person-slash\": \"\\f8a9\",\n  \"person-up\": \"\\f8aa\",\n  \"scooter\": \"\\f8ab\",\n  \"taxi-front-fill\": \"\\f8ac\",\n  \"taxi-front\": \"\\f8ad\",\n  \"amd\": \"\\f8ae\",\n  \"database-add\": \"\\f8af\",\n  \"database-check\": \"\\f8b0\",\n  \"database-dash\": \"\\f8b1\",\n  \"database-down\": \"\\f8b2\",\n  \"database-exclamation\": \"\\f8b3\",\n  \"database-fill-add\": \"\\f8b4\",\n  \"database-fill-check\": \"\\f8b5\",\n  \"database-fill-dash\": \"\\f8b6\",\n  \"database-fill-down\": \"\\f8b7\",\n  \"database-fill-exclamation\": \"\\f8b8\",\n  \"database-fill-gear\": \"\\f8b9\",\n  \"database-fill-lock\": \"\\f8ba\",\n  \"database-fill-slash\": \"\\f8bb\",\n  \"database-fill-up\": \"\\f8bc\",\n  \"database-fill-x\": \"\\f8bd\",\n  \"database-fill\": \"\\f8be\",\n  \"database-gear\": \"\\f8bf\",\n  \"database-lock\": \"\\f8c0\",\n  \"database-slash\": \"\\f8c1\",\n  \"database-up\": \"\\f8c2\",\n  \"database-x\": \"\\f8c3\",\n  \"database\": \"\\f8c4\",\n  \"houses-fill\": \"\\f8c5\",\n  \"houses\": \"\\f8c6\",\n  \"nvidia\": \"\\f8c7\",\n  \"person-vcard-fill\": \"\\f8c8\",\n  \"person-vcard\": \"\\f8c9\",\n  \"sina-weibo\": \"\\f8ca\",\n  \"tencent-qq\": \"\\f8cb\",\n  \"wikipedia\": \"\\f8cc\",\n);\n\n.bi-123::before { content: map-get($bootstrap-icons-map, \"123\"); }\n.bi-alarm-fill::before { content: map-get($bootstrap-icons-map, \"alarm-fill\"); }\n.bi-alarm::before { content: map-get($bootstrap-icons-map, \"alarm\"); }\n.bi-align-bottom::before { content: map-get($bootstrap-icons-map, \"align-bottom\"); }\n.bi-align-center::before { content: map-get($bootstrap-icons-map, \"align-center\"); }\n.bi-align-end::before { content: map-get($bootstrap-icons-map, \"align-end\"); }\n.bi-align-middle::before { content: map-get($bootstrap-icons-map, \"align-middle\"); }\n.bi-align-start::before { content: map-get($bootstrap-icons-map, \"align-start\"); }\n.bi-align-top::before { content: map-get($bootstrap-icons-map, \"align-top\"); }\n.bi-alt::before { content: map-get($bootstrap-icons-map, \"alt\"); }\n.bi-app-indicator::before { content: map-get($bootstrap-icons-map, \"app-indicator\"); }\n.bi-app::before { content: map-get($bootstrap-icons-map, \"app\"); }\n.bi-archive-fill::before { content: map-get($bootstrap-icons-map, \"archive-fill\"); }\n.bi-archive::before { content: map-get($bootstrap-icons-map, \"archive\"); }\n.bi-arrow-90deg-down::before { content: map-get($bootstrap-icons-map, \"arrow-90deg-down\"); }\n.bi-arrow-90deg-left::before { content: map-get($bootstrap-icons-map, \"arrow-90deg-left\"); }\n.bi-arrow-90deg-right::before { content: map-get($bootstrap-icons-map, \"arrow-90deg-right\"); }\n.bi-arrow-90deg-up::before { content: map-get($bootstrap-icons-map, \"arrow-90deg-up\"); }\n.bi-arrow-bar-down::before { content: map-get($bootstrap-icons-map, \"arrow-bar-down\"); }\n.bi-arrow-bar-left::before { content: map-get($bootstrap-icons-map, \"arrow-bar-left\"); }\n.bi-arrow-bar-right::before { content: map-get($bootstrap-icons-map, \"arrow-bar-right\"); }\n.bi-arrow-bar-up::before { content: map-get($bootstrap-icons-map, \"arrow-bar-up\"); }\n.bi-arrow-clockwise::before { content: map-get($bootstrap-icons-map, \"arrow-clockwise\"); }\n.bi-arrow-counterclockwise::before { content: map-get($bootstrap-icons-map, \"arrow-counterclockwise\"); }\n.bi-arrow-down-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-down-circle-fill\"); }\n.bi-arrow-down-circle::before { content: map-get($bootstrap-icons-map, \"arrow-down-circle\"); }\n.bi-arrow-down-left-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-down-left-circle-fill\"); }\n.bi-arrow-down-left-circle::before { content: map-get($bootstrap-icons-map, \"arrow-down-left-circle\"); }\n.bi-arrow-down-left-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-down-left-square-fill\"); }\n.bi-arrow-down-left-square::before { content: map-get($bootstrap-icons-map, \"arrow-down-left-square\"); }\n.bi-arrow-down-left::before { content: map-get($bootstrap-icons-map, \"arrow-down-left\"); }\n.bi-arrow-down-right-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-down-right-circle-fill\"); }\n.bi-arrow-down-right-circle::before { content: map-get($bootstrap-icons-map, \"arrow-down-right-circle\"); }\n.bi-arrow-down-right-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-down-right-square-fill\"); }\n.bi-arrow-down-right-square::before { content: map-get($bootstrap-icons-map, \"arrow-down-right-square\"); }\n.bi-arrow-down-right::before { content: map-get($bootstrap-icons-map, \"arrow-down-right\"); }\n.bi-arrow-down-short::before { content: map-get($bootstrap-icons-map, \"arrow-down-short\"); }\n.bi-arrow-down-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-down-square-fill\"); }\n.bi-arrow-down-square::before { content: map-get($bootstrap-icons-map, \"arrow-down-square\"); }\n.bi-arrow-down-up::before { content: map-get($bootstrap-icons-map, \"arrow-down-up\"); }\n.bi-arrow-down::before { content: map-get($bootstrap-icons-map, \"arrow-down\"); }\n.bi-arrow-left-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-left-circle-fill\"); }\n.bi-arrow-left-circle::before { content: map-get($bootstrap-icons-map, \"arrow-left-circle\"); }\n.bi-arrow-left-right::before { content: map-get($bootstrap-icons-map, \"arrow-left-right\"); }\n.bi-arrow-left-short::before { content: map-get($bootstrap-icons-map, \"arrow-left-short\"); }\n.bi-arrow-left-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-left-square-fill\"); }\n.bi-arrow-left-square::before { content: map-get($bootstrap-icons-map, \"arrow-left-square\"); }\n.bi-arrow-left::before { content: map-get($bootstrap-icons-map, \"arrow-left\"); }\n.bi-arrow-repeat::before { content: map-get($bootstrap-icons-map, \"arrow-repeat\"); }\n.bi-arrow-return-left::before { content: map-get($bootstrap-icons-map, \"arrow-return-left\"); }\n.bi-arrow-return-right::before { content: map-get($bootstrap-icons-map, \"arrow-return-right\"); }\n.bi-arrow-right-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-right-circle-fill\"); }\n.bi-arrow-right-circle::before { content: map-get($bootstrap-icons-map, \"arrow-right-circle\"); }\n.bi-arrow-right-short::before { content: map-get($bootstrap-icons-map, \"arrow-right-short\"); }\n.bi-arrow-right-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-right-square-fill\"); }\n.bi-arrow-right-square::before { content: map-get($bootstrap-icons-map, \"arrow-right-square\"); }\n.bi-arrow-right::before { content: map-get($bootstrap-icons-map, \"arrow-right\"); }\n.bi-arrow-up-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-up-circle-fill\"); }\n.bi-arrow-up-circle::before { content: map-get($bootstrap-icons-map, \"arrow-up-circle\"); }\n.bi-arrow-up-left-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-up-left-circle-fill\"); }\n.bi-arrow-up-left-circle::before { content: map-get($bootstrap-icons-map, \"arrow-up-left-circle\"); }\n.bi-arrow-up-left-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-up-left-square-fill\"); }\n.bi-arrow-up-left-square::before { content: map-get($bootstrap-icons-map, \"arrow-up-left-square\"); }\n.bi-arrow-up-left::before { content: map-get($bootstrap-icons-map, \"arrow-up-left\"); }\n.bi-arrow-up-right-circle-fill::before { content: map-get($bootstrap-icons-map, \"arrow-up-right-circle-fill\"); }\n.bi-arrow-up-right-circle::before { content: map-get($bootstrap-icons-map, \"arrow-up-right-circle\"); }\n.bi-arrow-up-right-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-up-right-square-fill\"); }\n.bi-arrow-up-right-square::before { content: map-get($bootstrap-icons-map, \"arrow-up-right-square\"); }\n.bi-arrow-up-right::before { content: map-get($bootstrap-icons-map, \"arrow-up-right\"); }\n.bi-arrow-up-short::before { content: map-get($bootstrap-icons-map, \"arrow-up-short\"); }\n.bi-arrow-up-square-fill::before { content: map-get($bootstrap-icons-map, \"arrow-up-square-fill\"); }\n.bi-arrow-up-square::before { content: map-get($bootstrap-icons-map, \"arrow-up-square\"); }\n.bi-arrow-up::before { content: map-get($bootstrap-icons-map, \"arrow-up\"); }\n.bi-arrows-angle-contract::before { content: map-get($bootstrap-icons-map, \"arrows-angle-contract\"); }\n.bi-arrows-angle-expand::before { content: map-get($bootstrap-icons-map, \"arrows-angle-expand\"); }\n.bi-arrows-collapse::before { content: map-get($bootstrap-icons-map, \"arrows-collapse\"); }\n.bi-arrows-expand::before { content: map-get($bootstrap-icons-map, \"arrows-expand\"); }\n.bi-arrows-fullscreen::before { content: map-get($bootstrap-icons-map, \"arrows-fullscreen\"); }\n.bi-arrows-move::before { content: map-get($bootstrap-icons-map, \"arrows-move\"); }\n.bi-aspect-ratio-fill::before { content: map-get($bootstrap-icons-map, \"aspect-ratio-fill\"); }\n.bi-aspect-ratio::before { content: map-get($bootstrap-icons-map, \"aspect-ratio\"); }\n.bi-asterisk::before { content: map-get($bootstrap-icons-map, \"asterisk\"); }\n.bi-at::before { content: map-get($bootstrap-icons-map, \"at\"); }\n.bi-award-fill::before { content: map-get($bootstrap-icons-map, \"award-fill\"); }\n.bi-award::before { content: map-get($bootstrap-icons-map, \"award\"); }\n.bi-back::before { content: map-get($bootstrap-icons-map, \"back\"); }\n.bi-backspace-fill::before { content: map-get($bootstrap-icons-map, \"backspace-fill\"); }\n.bi-backspace-reverse-fill::before { content: map-get($bootstrap-icons-map, \"backspace-reverse-fill\"); }\n.bi-backspace-reverse::before { content: map-get($bootstrap-icons-map, \"backspace-reverse\"); }\n.bi-backspace::before { content: map-get($bootstrap-icons-map, \"backspace\"); }\n.bi-badge-3d-fill::before { content: map-get($bootstrap-icons-map, \"badge-3d-fill\"); }\n.bi-badge-3d::before { content: map-get($bootstrap-icons-map, \"badge-3d\"); }\n.bi-badge-4k-fill::before { content: map-get($bootstrap-icons-map, \"badge-4k-fill\"); }\n.bi-badge-4k::before { content: map-get($bootstrap-icons-map, \"badge-4k\"); }\n.bi-badge-8k-fill::before { content: map-get($bootstrap-icons-map, \"badge-8k-fill\"); }\n.bi-badge-8k::before { content: map-get($bootstrap-icons-map, \"badge-8k\"); }\n.bi-badge-ad-fill::before { content: map-get($bootstrap-icons-map, \"badge-ad-fill\"); }\n.bi-badge-ad::before { content: map-get($bootstrap-icons-map, \"badge-ad\"); }\n.bi-badge-ar-fill::before { content: map-get($bootstrap-icons-map, \"badge-ar-fill\"); }\n.bi-badge-ar::before { content: map-get($bootstrap-icons-map, \"badge-ar\"); }\n.bi-badge-cc-fill::before { content: map-get($bootstrap-icons-map, \"badge-cc-fill\"); }\n.bi-badge-cc::before { content: map-get($bootstrap-icons-map, \"badge-cc\"); }\n.bi-badge-hd-fill::before { content: map-get($bootstrap-icons-map, \"badge-hd-fill\"); }\n.bi-badge-hd::before { content: map-get($bootstrap-icons-map, \"badge-hd\"); }\n.bi-badge-tm-fill::before { content: map-get($bootstrap-icons-map, \"badge-tm-fill\"); }\n.bi-badge-tm::before { content: map-get($bootstrap-icons-map, \"badge-tm\"); }\n.bi-badge-vo-fill::before { content: map-get($bootstrap-icons-map, \"badge-vo-fill\"); }\n.bi-badge-vo::before { content: map-get($bootstrap-icons-map, \"badge-vo\"); }\n.bi-badge-vr-fill::before { content: map-get($bootstrap-icons-map, \"badge-vr-fill\"); }\n.bi-badge-vr::before { content: map-get($bootstrap-icons-map, \"badge-vr\"); }\n.bi-badge-wc-fill::before { content: map-get($bootstrap-icons-map, \"badge-wc-fill\"); }\n.bi-badge-wc::before { content: map-get($bootstrap-icons-map, \"badge-wc\"); }\n.bi-bag-check-fill::before { content: map-get($bootstrap-icons-map, \"bag-check-fill\"); }\n.bi-bag-check::before { content: map-get($bootstrap-icons-map, \"bag-check\"); }\n.bi-bag-dash-fill::before { content: map-get($bootstrap-icons-map, \"bag-dash-fill\"); }\n.bi-bag-dash::before { content: map-get($bootstrap-icons-map, \"bag-dash\"); }\n.bi-bag-fill::before { content: map-get($bootstrap-icons-map, \"bag-fill\"); }\n.bi-bag-plus-fill::before { content: map-get($bootstrap-icons-map, \"bag-plus-fill\"); }\n.bi-bag-plus::before { content: map-get($bootstrap-icons-map, \"bag-plus\"); }\n.bi-bag-x-fill::before { content: map-get($bootstrap-icons-map, \"bag-x-fill\"); }\n.bi-bag-x::before { content: map-get($bootstrap-icons-map, \"bag-x\"); }\n.bi-bag::before { content: map-get($bootstrap-icons-map, \"bag\"); }\n.bi-bar-chart-fill::before { content: map-get($bootstrap-icons-map, \"bar-chart-fill\"); }\n.bi-bar-chart-line-fill::before { content: map-get($bootstrap-icons-map, \"bar-chart-line-fill\"); }\n.bi-bar-chart-line::before { content: map-get($bootstrap-icons-map, \"bar-chart-line\"); }\n.bi-bar-chart-steps::before { content: map-get($bootstrap-icons-map, \"bar-chart-steps\"); }\n.bi-bar-chart::before { content: map-get($bootstrap-icons-map, \"bar-chart\"); }\n.bi-basket-fill::before { content: map-get($bootstrap-icons-map, \"basket-fill\"); }\n.bi-basket::before { content: map-get($bootstrap-icons-map, \"basket\"); }\n.bi-basket2-fill::before { content: map-get($bootstrap-icons-map, \"basket2-fill\"); }\n.bi-basket2::before { content: map-get($bootstrap-icons-map, \"basket2\"); }\n.bi-basket3-fill::before { content: map-get($bootstrap-icons-map, \"basket3-fill\"); }\n.bi-basket3::before { content: map-get($bootstrap-icons-map, \"basket3\"); }\n.bi-battery-charging::before { content: map-get($bootstrap-icons-map, \"battery-charging\"); }\n.bi-battery-full::before { content: map-get($bootstrap-icons-map, \"battery-full\"); }\n.bi-battery-half::before { content: map-get($bootstrap-icons-map, \"battery-half\"); }\n.bi-battery::before { content: map-get($bootstrap-icons-map, \"battery\"); }\n.bi-bell-fill::before { content: map-get($bootstrap-icons-map, \"bell-fill\"); }\n.bi-bell::before { content: map-get($bootstrap-icons-map, \"bell\"); }\n.bi-bezier::before { content: map-get($bootstrap-icons-map, \"bezier\"); }\n.bi-bezier2::before { content: map-get($bootstrap-icons-map, \"bezier2\"); }\n.bi-bicycle::before { content: map-get($bootstrap-icons-map, \"bicycle\"); }\n.bi-binoculars-fill::before { content: map-get($bootstrap-icons-map, \"binoculars-fill\"); }\n.bi-binoculars::before { content: map-get($bootstrap-icons-map, \"binoculars\"); }\n.bi-blockquote-left::before { content: map-get($bootstrap-icons-map, \"blockquote-left\"); }\n.bi-blockquote-right::before { content: map-get($bootstrap-icons-map, \"blockquote-right\"); }\n.bi-book-fill::before { content: map-get($bootstrap-icons-map, \"book-fill\"); }\n.bi-book-half::before { content: map-get($bootstrap-icons-map, \"book-half\"); }\n.bi-book::before { content: map-get($bootstrap-icons-map, \"book\"); }\n.bi-bookmark-check-fill::before { content: map-get($bootstrap-icons-map, \"bookmark-check-fill\"); }\n.bi-bookmark-check::before { content: map-get($bootstrap-icons-map, \"bookmark-check\"); }\n.bi-bookmark-dash-fill::before { content: map-get($bootstrap-icons-map, \"bookmark-dash-fill\"); }\n.bi-bookmark-dash::before { content: map-get($bootstrap-icons-map, \"bookmark-dash\"); }\n.bi-bookmark-fill::before { content: map-get($bootstrap-icons-map, \"bookmark-fill\"); }\n.bi-bookmark-heart-fill::before { content: map-get($bootstrap-icons-map, \"bookmark-heart-fill\"); }\n.bi-bookmark-heart::before { content: map-get($bootstrap-icons-map, \"bookmark-heart\"); }\n.bi-bookmark-plus-fill::before { content: map-get($bootstrap-icons-map, \"bookmark-plus-fill\"); }\n.bi-bookmark-plus::before { content: map-get($bootstrap-icons-map, \"bookmark-plus\"); }\n.bi-bookmark-star-fill::before { content: map-get($bootstrap-icons-map, \"bookmark-star-fill\"); }\n.bi-bookmark-star::before { content: map-get($bootstrap-icons-map, \"bookmark-star\"); }\n.bi-bookmark-x-fill::before { content: map-get($bootstrap-icons-map, \"bookmark-x-fill\"); }\n.bi-bookmark-x::before { content: map-get($bootstrap-icons-map, \"bookmark-x\"); }\n.bi-bookmark::before { content: map-get($bootstrap-icons-map, \"bookmark\"); }\n.bi-bookmarks-fill::before { content: map-get($bootstrap-icons-map, \"bookmarks-fill\"); }\n.bi-bookmarks::before { content: map-get($bootstrap-icons-map, \"bookmarks\"); }\n.bi-bookshelf::before { content: map-get($bootstrap-icons-map, \"bookshelf\"); }\n.bi-bootstrap-fill::before { content: map-get($bootstrap-icons-map, \"bootstrap-fill\"); }\n.bi-bootstrap-reboot::before { content: map-get($bootstrap-icons-map, \"bootstrap-reboot\"); }\n.bi-bootstrap::before { content: map-get($bootstrap-icons-map, \"bootstrap\"); }\n.bi-border-all::before { content: map-get($bootstrap-icons-map, \"border-all\"); }\n.bi-border-bottom::before { content: map-get($bootstrap-icons-map, \"border-bottom\"); }\n.bi-border-center::before { content: map-get($bootstrap-icons-map, \"border-center\"); }\n.bi-border-inner::before { content: map-get($bootstrap-icons-map, \"border-inner\"); }\n.bi-border-left::before { content: map-get($bootstrap-icons-map, \"border-left\"); }\n.bi-border-middle::before { content: map-get($bootstrap-icons-map, \"border-middle\"); }\n.bi-border-outer::before { content: map-get($bootstrap-icons-map, \"border-outer\"); }\n.bi-border-right::before { content: map-get($bootstrap-icons-map, \"border-right\"); }\n.bi-border-style::before { content: map-get($bootstrap-icons-map, \"border-style\"); }\n.bi-border-top::before { content: map-get($bootstrap-icons-map, \"border-top\"); }\n.bi-border-width::before { content: map-get($bootstrap-icons-map, \"border-width\"); }\n.bi-border::before { content: map-get($bootstrap-icons-map, \"border\"); }\n.bi-bounding-box-circles::before { content: map-get($bootstrap-icons-map, \"bounding-box-circles\"); }\n.bi-bounding-box::before { content: map-get($bootstrap-icons-map, \"bounding-box\"); }\n.bi-box-arrow-down-left::before { content: map-get($bootstrap-icons-map, \"box-arrow-down-left\"); }\n.bi-box-arrow-down-right::before { content: map-get($bootstrap-icons-map, \"box-arrow-down-right\"); }\n.bi-box-arrow-down::before { content: map-get($bootstrap-icons-map, \"box-arrow-down\"); }\n.bi-box-arrow-in-down-left::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-down-left\"); }\n.bi-box-arrow-in-down-right::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-down-right\"); }\n.bi-box-arrow-in-down::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-down\"); }\n.bi-box-arrow-in-left::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-left\"); }\n.bi-box-arrow-in-right::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-right\"); }\n.bi-box-arrow-in-up-left::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-up-left\"); }\n.bi-box-arrow-in-up-right::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-up-right\"); }\n.bi-box-arrow-in-up::before { content: map-get($bootstrap-icons-map, \"box-arrow-in-up\"); }\n.bi-box-arrow-left::before { content: map-get($bootstrap-icons-map, \"box-arrow-left\"); }\n.bi-box-arrow-right::before { content: map-get($bootstrap-icons-map, \"box-arrow-right\"); }\n.bi-box-arrow-up-left::before { content: map-get($bootstrap-icons-map, \"box-arrow-up-left\"); }\n.bi-box-arrow-up-right::before { content: map-get($bootstrap-icons-map, \"box-arrow-up-right\"); }\n.bi-box-arrow-up::before { content: map-get($bootstrap-icons-map, \"box-arrow-up\"); }\n.bi-box-seam::before { content: map-get($bootstrap-icons-map, \"box-seam\"); }\n.bi-box::before { content: map-get($bootstrap-icons-map, \"box\"); }\n.bi-braces::before { content: map-get($bootstrap-icons-map, \"braces\"); }\n.bi-bricks::before { content: map-get($bootstrap-icons-map, \"bricks\"); }\n.bi-briefcase-fill::before { content: map-get($bootstrap-icons-map, \"briefcase-fill\"); }\n.bi-briefcase::before { content: map-get($bootstrap-icons-map, \"briefcase\"); }\n.bi-brightness-alt-high-fill::before { content: map-get($bootstrap-icons-map, \"brightness-alt-high-fill\"); }\n.bi-brightness-alt-high::before { content: map-get($bootstrap-icons-map, \"brightness-alt-high\"); }\n.bi-brightness-alt-low-fill::before { content: map-get($bootstrap-icons-map, \"brightness-alt-low-fill\"); }\n.bi-brightness-alt-low::before { content: map-get($bootstrap-icons-map, \"brightness-alt-low\"); }\n.bi-brightness-high-fill::before { content: map-get($bootstrap-icons-map, \"brightness-high-fill\"); }\n.bi-brightness-high::before { content: map-get($bootstrap-icons-map, \"brightness-high\"); }\n.bi-brightness-low-fill::before { content: map-get($bootstrap-icons-map, \"brightness-low-fill\"); }\n.bi-brightness-low::before { content: map-get($bootstrap-icons-map, \"brightness-low\"); }\n.bi-broadcast-pin::before { content: map-get($bootstrap-icons-map, \"broadcast-pin\"); }\n.bi-broadcast::before { content: map-get($bootstrap-icons-map, \"broadcast\"); }\n.bi-brush-fill::before { content: map-get($bootstrap-icons-map, \"brush-fill\"); }\n.bi-brush::before { content: map-get($bootstrap-icons-map, \"brush\"); }\n.bi-bucket-fill::before { content: map-get($bootstrap-icons-map, \"bucket-fill\"); }\n.bi-bucket::before { content: map-get($bootstrap-icons-map, \"bucket\"); }\n.bi-bug-fill::before { content: map-get($bootstrap-icons-map, \"bug-fill\"); }\n.bi-bug::before { content: map-get($bootstrap-icons-map, \"bug\"); }\n.bi-building::before { content: map-get($bootstrap-icons-map, \"building\"); }\n.bi-bullseye::before { content: map-get($bootstrap-icons-map, \"bullseye\"); }\n.bi-calculator-fill::before { content: map-get($bootstrap-icons-map, \"calculator-fill\"); }\n.bi-calculator::before { content: map-get($bootstrap-icons-map, \"calculator\"); }\n.bi-calendar-check-fill::before { content: map-get($bootstrap-icons-map, \"calendar-check-fill\"); }\n.bi-calendar-check::before { content: map-get($bootstrap-icons-map, \"calendar-check\"); }\n.bi-calendar-date-fill::before { content: map-get($bootstrap-icons-map, \"calendar-date-fill\"); }\n.bi-calendar-date::before { content: map-get($bootstrap-icons-map, \"calendar-date\"); }\n.bi-calendar-day-fill::before { content: map-get($bootstrap-icons-map, \"calendar-day-fill\"); }\n.bi-calendar-day::before { content: map-get($bootstrap-icons-map, \"calendar-day\"); }\n.bi-calendar-event-fill::before { content: map-get($bootstrap-icons-map, \"calendar-event-fill\"); }\n.bi-calendar-event::before { content: map-get($bootstrap-icons-map, \"calendar-event\"); }\n.bi-calendar-fill::before { content: map-get($bootstrap-icons-map, \"calendar-fill\"); }\n.bi-calendar-minus-fill::before { content: map-get($bootstrap-icons-map, \"calendar-minus-fill\"); }\n.bi-calendar-minus::before { content: map-get($bootstrap-icons-map, \"calendar-minus\"); }\n.bi-calendar-month-fill::before { content: map-get($bootstrap-icons-map, \"calendar-month-fill\"); }\n.bi-calendar-month::before { content: map-get($bootstrap-icons-map, \"calendar-month\"); }\n.bi-calendar-plus-fill::before { content: map-get($bootstrap-icons-map, \"calendar-plus-fill\"); }\n.bi-calendar-plus::before { content: map-get($bootstrap-icons-map, \"calendar-plus\"); }\n.bi-calendar-range-fill::before { content: map-get($bootstrap-icons-map, \"calendar-range-fill\"); }\n.bi-calendar-range::before { content: map-get($bootstrap-icons-map, \"calendar-range\"); }\n.bi-calendar-week-fill::before { content: map-get($bootstrap-icons-map, \"calendar-week-fill\"); }\n.bi-calendar-week::before { content: map-get($bootstrap-icons-map, \"calendar-week\"); }\n.bi-calendar-x-fill::before { content: map-get($bootstrap-icons-map, \"calendar-x-fill\"); }\n.bi-calendar-x::before { content: map-get($bootstrap-icons-map, \"calendar-x\"); }\n.bi-calendar::before { content: map-get($bootstrap-icons-map, \"calendar\"); }\n.bi-calendar2-check-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-check-fill\"); }\n.bi-calendar2-check::before { content: map-get($bootstrap-icons-map, \"calendar2-check\"); }\n.bi-calendar2-date-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-date-fill\"); }\n.bi-calendar2-date::before { content: map-get($bootstrap-icons-map, \"calendar2-date\"); }\n.bi-calendar2-day-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-day-fill\"); }\n.bi-calendar2-day::before { content: map-get($bootstrap-icons-map, \"calendar2-day\"); }\n.bi-calendar2-event-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-event-fill\"); }\n.bi-calendar2-event::before { content: map-get($bootstrap-icons-map, \"calendar2-event\"); }\n.bi-calendar2-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-fill\"); }\n.bi-calendar2-minus-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-minus-fill\"); }\n.bi-calendar2-minus::before { content: map-get($bootstrap-icons-map, \"calendar2-minus\"); }\n.bi-calendar2-month-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-month-fill\"); }\n.bi-calendar2-month::before { content: map-get($bootstrap-icons-map, \"calendar2-month\"); }\n.bi-calendar2-plus-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-plus-fill\"); }\n.bi-calendar2-plus::before { content: map-get($bootstrap-icons-map, \"calendar2-plus\"); }\n.bi-calendar2-range-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-range-fill\"); }\n.bi-calendar2-range::before { content: map-get($bootstrap-icons-map, \"calendar2-range\"); }\n.bi-calendar2-week-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-week-fill\"); }\n.bi-calendar2-week::before { content: map-get($bootstrap-icons-map, \"calendar2-week\"); }\n.bi-calendar2-x-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-x-fill\"); }\n.bi-calendar2-x::before { content: map-get($bootstrap-icons-map, \"calendar2-x\"); }\n.bi-calendar2::before { content: map-get($bootstrap-icons-map, \"calendar2\"); }\n.bi-calendar3-event-fill::before { content: map-get($bootstrap-icons-map, \"calendar3-event-fill\"); }\n.bi-calendar3-event::before { content: map-get($bootstrap-icons-map, \"calendar3-event\"); }\n.bi-calendar3-fill::before { content: map-get($bootstrap-icons-map, \"calendar3-fill\"); }\n.bi-calendar3-range-fill::before { content: map-get($bootstrap-icons-map, \"calendar3-range-fill\"); }\n.bi-calendar3-range::before { content: map-get($bootstrap-icons-map, \"calendar3-range\"); }\n.bi-calendar3-week-fill::before { content: map-get($bootstrap-icons-map, \"calendar3-week-fill\"); }\n.bi-calendar3-week::before { content: map-get($bootstrap-icons-map, \"calendar3-week\"); }\n.bi-calendar3::before { content: map-get($bootstrap-icons-map, \"calendar3\"); }\n.bi-calendar4-event::before { content: map-get($bootstrap-icons-map, \"calendar4-event\"); }\n.bi-calendar4-range::before { content: map-get($bootstrap-icons-map, \"calendar4-range\"); }\n.bi-calendar4-week::before { content: map-get($bootstrap-icons-map, \"calendar4-week\"); }\n.bi-calendar4::before { content: map-get($bootstrap-icons-map, \"calendar4\"); }\n.bi-camera-fill::before { content: map-get($bootstrap-icons-map, \"camera-fill\"); }\n.bi-camera-reels-fill::before { content: map-get($bootstrap-icons-map, \"camera-reels-fill\"); }\n.bi-camera-reels::before { content: map-get($bootstrap-icons-map, \"camera-reels\"); }\n.bi-camera-video-fill::before { content: map-get($bootstrap-icons-map, \"camera-video-fill\"); }\n.bi-camera-video-off-fill::before { content: map-get($bootstrap-icons-map, \"camera-video-off-fill\"); }\n.bi-camera-video-off::before { content: map-get($bootstrap-icons-map, \"camera-video-off\"); }\n.bi-camera-video::before { content: map-get($bootstrap-icons-map, \"camera-video\"); }\n.bi-camera::before { content: map-get($bootstrap-icons-map, \"camera\"); }\n.bi-camera2::before { content: map-get($bootstrap-icons-map, \"camera2\"); }\n.bi-capslock-fill::before { content: map-get($bootstrap-icons-map, \"capslock-fill\"); }\n.bi-capslock::before { content: map-get($bootstrap-icons-map, \"capslock\"); }\n.bi-card-checklist::before { content: map-get($bootstrap-icons-map, \"card-checklist\"); }\n.bi-card-heading::before { content: map-get($bootstrap-icons-map, \"card-heading\"); }\n.bi-card-image::before { content: map-get($bootstrap-icons-map, \"card-image\"); }\n.bi-card-list::before { content: map-get($bootstrap-icons-map, \"card-list\"); }\n.bi-card-text::before { content: map-get($bootstrap-icons-map, \"card-text\"); }\n.bi-caret-down-fill::before { content: map-get($bootstrap-icons-map, \"caret-down-fill\"); }\n.bi-caret-down-square-fill::before { content: map-get($bootstrap-icons-map, \"caret-down-square-fill\"); }\n.bi-caret-down-square::before { content: map-get($bootstrap-icons-map, \"caret-down-square\"); }\n.bi-caret-down::before { content: map-get($bootstrap-icons-map, \"caret-down\"); }\n.bi-caret-left-fill::before { content: map-get($bootstrap-icons-map, \"caret-left-fill\"); }\n.bi-caret-left-square-fill::before { content: map-get($bootstrap-icons-map, \"caret-left-square-fill\"); }\n.bi-caret-left-square::before { content: map-get($bootstrap-icons-map, \"caret-left-square\"); }\n.bi-caret-left::before { content: map-get($bootstrap-icons-map, \"caret-left\"); }\n.bi-caret-right-fill::before { content: map-get($bootstrap-icons-map, \"caret-right-fill\"); }\n.bi-caret-right-square-fill::before { content: map-get($bootstrap-icons-map, \"caret-right-square-fill\"); }\n.bi-caret-right-square::before { content: map-get($bootstrap-icons-map, \"caret-right-square\"); }\n.bi-caret-right::before { content: map-get($bootstrap-icons-map, \"caret-right\"); }\n.bi-caret-up-fill::before { content: map-get($bootstrap-icons-map, \"caret-up-fill\"); }\n.bi-caret-up-square-fill::before { content: map-get($bootstrap-icons-map, \"caret-up-square-fill\"); }\n.bi-caret-up-square::before { content: map-get($bootstrap-icons-map, \"caret-up-square\"); }\n.bi-caret-up::before { content: map-get($bootstrap-icons-map, \"caret-up\"); }\n.bi-cart-check-fill::before { content: map-get($bootstrap-icons-map, \"cart-check-fill\"); }\n.bi-cart-check::before { content: map-get($bootstrap-icons-map, \"cart-check\"); }\n.bi-cart-dash-fill::before { content: map-get($bootstrap-icons-map, \"cart-dash-fill\"); }\n.bi-cart-dash::before { content: map-get($bootstrap-icons-map, \"cart-dash\"); }\n.bi-cart-fill::before { content: map-get($bootstrap-icons-map, \"cart-fill\"); }\n.bi-cart-plus-fill::before { content: map-get($bootstrap-icons-map, \"cart-plus-fill\"); }\n.bi-cart-plus::before { content: map-get($bootstrap-icons-map, \"cart-plus\"); }\n.bi-cart-x-fill::before { content: map-get($bootstrap-icons-map, \"cart-x-fill\"); }\n.bi-cart-x::before { content: map-get($bootstrap-icons-map, \"cart-x\"); }\n.bi-cart::before { content: map-get($bootstrap-icons-map, \"cart\"); }\n.bi-cart2::before { content: map-get($bootstrap-icons-map, \"cart2\"); }\n.bi-cart3::before { content: map-get($bootstrap-icons-map, \"cart3\"); }\n.bi-cart4::before { content: map-get($bootstrap-icons-map, \"cart4\"); }\n.bi-cash-stack::before { content: map-get($bootstrap-icons-map, \"cash-stack\"); }\n.bi-cash::before { content: map-get($bootstrap-icons-map, \"cash\"); }\n.bi-cast::before { content: map-get($bootstrap-icons-map, \"cast\"); }\n.bi-chat-dots-fill::before { content: map-get($bootstrap-icons-map, \"chat-dots-fill\"); }\n.bi-chat-dots::before { content: map-get($bootstrap-icons-map, \"chat-dots\"); }\n.bi-chat-fill::before { content: map-get($bootstrap-icons-map, \"chat-fill\"); }\n.bi-chat-left-dots-fill::before { content: map-get($bootstrap-icons-map, \"chat-left-dots-fill\"); }\n.bi-chat-left-dots::before { content: map-get($bootstrap-icons-map, \"chat-left-dots\"); }\n.bi-chat-left-fill::before { content: map-get($bootstrap-icons-map, \"chat-left-fill\"); }\n.bi-chat-left-quote-fill::before { content: map-get($bootstrap-icons-map, \"chat-left-quote-fill\"); }\n.bi-chat-left-quote::before { content: map-get($bootstrap-icons-map, \"chat-left-quote\"); }\n.bi-chat-left-text-fill::before { content: map-get($bootstrap-icons-map, \"chat-left-text-fill\"); }\n.bi-chat-left-text::before { content: map-get($bootstrap-icons-map, \"chat-left-text\"); }\n.bi-chat-left::before { content: map-get($bootstrap-icons-map, \"chat-left\"); }\n.bi-chat-quote-fill::before { content: map-get($bootstrap-icons-map, \"chat-quote-fill\"); }\n.bi-chat-quote::before { content: map-get($bootstrap-icons-map, \"chat-quote\"); }\n.bi-chat-right-dots-fill::before { content: map-get($bootstrap-icons-map, \"chat-right-dots-fill\"); }\n.bi-chat-right-dots::before { content: map-get($bootstrap-icons-map, \"chat-right-dots\"); }\n.bi-chat-right-fill::before { content: map-get($bootstrap-icons-map, \"chat-right-fill\"); }\n.bi-chat-right-quote-fill::before { content: map-get($bootstrap-icons-map, \"chat-right-quote-fill\"); }\n.bi-chat-right-quote::before { content: map-get($bootstrap-icons-map, \"chat-right-quote\"); }\n.bi-chat-right-text-fill::before { content: map-get($bootstrap-icons-map, \"chat-right-text-fill\"); }\n.bi-chat-right-text::before { content: map-get($bootstrap-icons-map, \"chat-right-text\"); }\n.bi-chat-right::before { content: map-get($bootstrap-icons-map, \"chat-right\"); }\n.bi-chat-square-dots-fill::before { content: map-get($bootstrap-icons-map, \"chat-square-dots-fill\"); }\n.bi-chat-square-dots::before { content: map-get($bootstrap-icons-map, \"chat-square-dots\"); }\n.bi-chat-square-fill::before { content: map-get($bootstrap-icons-map, \"chat-square-fill\"); }\n.bi-chat-square-quote-fill::before { content: map-get($bootstrap-icons-map, \"chat-square-quote-fill\"); }\n.bi-chat-square-quote::before { content: map-get($bootstrap-icons-map, \"chat-square-quote\"); }\n.bi-chat-square-text-fill::before { content: map-get($bootstrap-icons-map, \"chat-square-text-fill\"); }\n.bi-chat-square-text::before { content: map-get($bootstrap-icons-map, \"chat-square-text\"); }\n.bi-chat-square::before { content: map-get($bootstrap-icons-map, \"chat-square\"); }\n.bi-chat-text-fill::before { content: map-get($bootstrap-icons-map, \"chat-text-fill\"); }\n.bi-chat-text::before { content: map-get($bootstrap-icons-map, \"chat-text\"); }\n.bi-chat::before { content: map-get($bootstrap-icons-map, \"chat\"); }\n.bi-check-all::before { content: map-get($bootstrap-icons-map, \"check-all\"); }\n.bi-check-circle-fill::before { content: map-get($bootstrap-icons-map, \"check-circle-fill\"); }\n.bi-check-circle::before { content: map-get($bootstrap-icons-map, \"check-circle\"); }\n.bi-check-square-fill::before { content: map-get($bootstrap-icons-map, \"check-square-fill\"); }\n.bi-check-square::before { content: map-get($bootstrap-icons-map, \"check-square\"); }\n.bi-check::before { content: map-get($bootstrap-icons-map, \"check\"); }\n.bi-check2-all::before { content: map-get($bootstrap-icons-map, \"check2-all\"); }\n.bi-check2-circle::before { content: map-get($bootstrap-icons-map, \"check2-circle\"); }\n.bi-check2-square::before { content: map-get($bootstrap-icons-map, \"check2-square\"); }\n.bi-check2::before { content: map-get($bootstrap-icons-map, \"check2\"); }\n.bi-chevron-bar-contract::before { content: map-get($bootstrap-icons-map, \"chevron-bar-contract\"); }\n.bi-chevron-bar-down::before { content: map-get($bootstrap-icons-map, \"chevron-bar-down\"); }\n.bi-chevron-bar-expand::before { content: map-get($bootstrap-icons-map, \"chevron-bar-expand\"); }\n.bi-chevron-bar-left::before { content: map-get($bootstrap-icons-map, \"chevron-bar-left\"); }\n.bi-chevron-bar-right::before { content: map-get($bootstrap-icons-map, \"chevron-bar-right\"); }\n.bi-chevron-bar-up::before { content: map-get($bootstrap-icons-map, \"chevron-bar-up\"); }\n.bi-chevron-compact-down::before { content: map-get($bootstrap-icons-map, \"chevron-compact-down\"); }\n.bi-chevron-compact-left::before { content: map-get($bootstrap-icons-map, \"chevron-compact-left\"); }\n.bi-chevron-compact-right::before { content: map-get($bootstrap-icons-map, \"chevron-compact-right\"); }\n.bi-chevron-compact-up::before { content: map-get($bootstrap-icons-map, \"chevron-compact-up\"); }\n.bi-chevron-contract::before { content: map-get($bootstrap-icons-map, \"chevron-contract\"); }\n.bi-chevron-double-down::before { content: map-get($bootstrap-icons-map, \"chevron-double-down\"); }\n.bi-chevron-double-left::before { content: map-get($bootstrap-icons-map, \"chevron-double-left\"); }\n.bi-chevron-double-right::before { content: map-get($bootstrap-icons-map, \"chevron-double-right\"); }\n.bi-chevron-double-up::before { content: map-get($bootstrap-icons-map, \"chevron-double-up\"); }\n.bi-chevron-down::before { content: map-get($bootstrap-icons-map, \"chevron-down\"); }\n.bi-chevron-expand::before { content: map-get($bootstrap-icons-map, \"chevron-expand\"); }\n.bi-chevron-left::before { content: map-get($bootstrap-icons-map, \"chevron-left\"); }\n.bi-chevron-right::before { content: map-get($bootstrap-icons-map, \"chevron-right\"); }\n.bi-chevron-up::before { content: map-get($bootstrap-icons-map, \"chevron-up\"); }\n.bi-circle-fill::before { content: map-get($bootstrap-icons-map, \"circle-fill\"); }\n.bi-circle-half::before { content: map-get($bootstrap-icons-map, \"circle-half\"); }\n.bi-circle-square::before { content: map-get($bootstrap-icons-map, \"circle-square\"); }\n.bi-circle::before { content: map-get($bootstrap-icons-map, \"circle\"); }\n.bi-clipboard-check::before { content: map-get($bootstrap-icons-map, \"clipboard-check\"); }\n.bi-clipboard-data::before { content: map-get($bootstrap-icons-map, \"clipboard-data\"); }\n.bi-clipboard-minus::before { content: map-get($bootstrap-icons-map, \"clipboard-minus\"); }\n.bi-clipboard-plus::before { content: map-get($bootstrap-icons-map, \"clipboard-plus\"); }\n.bi-clipboard-x::before { content: map-get($bootstrap-icons-map, \"clipboard-x\"); }\n.bi-clipboard::before { content: map-get($bootstrap-icons-map, \"clipboard\"); }\n.bi-clock-fill::before { content: map-get($bootstrap-icons-map, \"clock-fill\"); }\n.bi-clock-history::before { content: map-get($bootstrap-icons-map, \"clock-history\"); }\n.bi-clock::before { content: map-get($bootstrap-icons-map, \"clock\"); }\n.bi-cloud-arrow-down-fill::before { content: map-get($bootstrap-icons-map, \"cloud-arrow-down-fill\"); }\n.bi-cloud-arrow-down::before { content: map-get($bootstrap-icons-map, \"cloud-arrow-down\"); }\n.bi-cloud-arrow-up-fill::before { content: map-get($bootstrap-icons-map, \"cloud-arrow-up-fill\"); }\n.bi-cloud-arrow-up::before { content: map-get($bootstrap-icons-map, \"cloud-arrow-up\"); }\n.bi-cloud-check-fill::before { content: map-get($bootstrap-icons-map, \"cloud-check-fill\"); }\n.bi-cloud-check::before { content: map-get($bootstrap-icons-map, \"cloud-check\"); }\n.bi-cloud-download-fill::before { content: map-get($bootstrap-icons-map, \"cloud-download-fill\"); }\n.bi-cloud-download::before { content: map-get($bootstrap-icons-map, \"cloud-download\"); }\n.bi-cloud-drizzle-fill::before { content: map-get($bootstrap-icons-map, \"cloud-drizzle-fill\"); }\n.bi-cloud-drizzle::before { content: map-get($bootstrap-icons-map, \"cloud-drizzle\"); }\n.bi-cloud-fill::before { content: map-get($bootstrap-icons-map, \"cloud-fill\"); }\n.bi-cloud-fog-fill::before { content: map-get($bootstrap-icons-map, \"cloud-fog-fill\"); }\n.bi-cloud-fog::before { content: map-get($bootstrap-icons-map, \"cloud-fog\"); }\n.bi-cloud-fog2-fill::before { content: map-get($bootstrap-icons-map, \"cloud-fog2-fill\"); }\n.bi-cloud-fog2::before { content: map-get($bootstrap-icons-map, \"cloud-fog2\"); }\n.bi-cloud-hail-fill::before { content: map-get($bootstrap-icons-map, \"cloud-hail-fill\"); }\n.bi-cloud-hail::before { content: map-get($bootstrap-icons-map, \"cloud-hail\"); }\n.bi-cloud-haze-1::before { content: map-get($bootstrap-icons-map, \"cloud-haze-1\"); }\n.bi-cloud-haze-fill::before { content: map-get($bootstrap-icons-map, \"cloud-haze-fill\"); }\n.bi-cloud-haze::before { content: map-get($bootstrap-icons-map, \"cloud-haze\"); }\n.bi-cloud-haze2-fill::before { content: map-get($bootstrap-icons-map, \"cloud-haze2-fill\"); }\n.bi-cloud-lightning-fill::before { content: map-get($bootstrap-icons-map, \"cloud-lightning-fill\"); }\n.bi-cloud-lightning-rain-fill::before { content: map-get($bootstrap-icons-map, \"cloud-lightning-rain-fill\"); }\n.bi-cloud-lightning-rain::before { content: map-get($bootstrap-icons-map, \"cloud-lightning-rain\"); }\n.bi-cloud-lightning::before { content: map-get($bootstrap-icons-map, \"cloud-lightning\"); }\n.bi-cloud-minus-fill::before { content: map-get($bootstrap-icons-map, \"cloud-minus-fill\"); }\n.bi-cloud-minus::before { content: map-get($bootstrap-icons-map, \"cloud-minus\"); }\n.bi-cloud-moon-fill::before { content: map-get($bootstrap-icons-map, \"cloud-moon-fill\"); }\n.bi-cloud-moon::before { content: map-get($bootstrap-icons-map, \"cloud-moon\"); }\n.bi-cloud-plus-fill::before { content: map-get($bootstrap-icons-map, \"cloud-plus-fill\"); }\n.bi-cloud-plus::before { content: map-get($bootstrap-icons-map, \"cloud-plus\"); }\n.bi-cloud-rain-fill::before { content: map-get($bootstrap-icons-map, \"cloud-rain-fill\"); }\n.bi-cloud-rain-heavy-fill::before { content: map-get($bootstrap-icons-map, \"cloud-rain-heavy-fill\"); }\n.bi-cloud-rain-heavy::before { content: map-get($bootstrap-icons-map, \"cloud-rain-heavy\"); }\n.bi-cloud-rain::before { content: map-get($bootstrap-icons-map, \"cloud-rain\"); }\n.bi-cloud-slash-fill::before { content: map-get($bootstrap-icons-map, \"cloud-slash-fill\"); }\n.bi-cloud-slash::before { content: map-get($bootstrap-icons-map, \"cloud-slash\"); }\n.bi-cloud-sleet-fill::before { content: map-get($bootstrap-icons-map, \"cloud-sleet-fill\"); }\n.bi-cloud-sleet::before { content: map-get($bootstrap-icons-map, \"cloud-sleet\"); }\n.bi-cloud-snow-fill::before { content: map-get($bootstrap-icons-map, \"cloud-snow-fill\"); }\n.bi-cloud-snow::before { content: map-get($bootstrap-icons-map, \"cloud-snow\"); }\n.bi-cloud-sun-fill::before { content: map-get($bootstrap-icons-map, \"cloud-sun-fill\"); }\n.bi-cloud-sun::before { content: map-get($bootstrap-icons-map, \"cloud-sun\"); }\n.bi-cloud-upload-fill::before { content: map-get($bootstrap-icons-map, \"cloud-upload-fill\"); }\n.bi-cloud-upload::before { content: map-get($bootstrap-icons-map, \"cloud-upload\"); }\n.bi-cloud::before { content: map-get($bootstrap-icons-map, \"cloud\"); }\n.bi-clouds-fill::before { content: map-get($bootstrap-icons-map, \"clouds-fill\"); }\n.bi-clouds::before { content: map-get($bootstrap-icons-map, \"clouds\"); }\n.bi-cloudy-fill::before { content: map-get($bootstrap-icons-map, \"cloudy-fill\"); }\n.bi-cloudy::before { content: map-get($bootstrap-icons-map, \"cloudy\"); }\n.bi-code-slash::before { content: map-get($bootstrap-icons-map, \"code-slash\"); }\n.bi-code-square::before { content: map-get($bootstrap-icons-map, \"code-square\"); }\n.bi-code::before { content: map-get($bootstrap-icons-map, \"code\"); }\n.bi-collection-fill::before { content: map-get($bootstrap-icons-map, \"collection-fill\"); }\n.bi-collection-play-fill::before { content: map-get($bootstrap-icons-map, \"collection-play-fill\"); }\n.bi-collection-play::before { content: map-get($bootstrap-icons-map, \"collection-play\"); }\n.bi-collection::before { content: map-get($bootstrap-icons-map, \"collection\"); }\n.bi-columns-gap::before { content: map-get($bootstrap-icons-map, \"columns-gap\"); }\n.bi-columns::before { content: map-get($bootstrap-icons-map, \"columns\"); }\n.bi-command::before { content: map-get($bootstrap-icons-map, \"command\"); }\n.bi-compass-fill::before { content: map-get($bootstrap-icons-map, \"compass-fill\"); }\n.bi-compass::before { content: map-get($bootstrap-icons-map, \"compass\"); }\n.bi-cone-striped::before { content: map-get($bootstrap-icons-map, \"cone-striped\"); }\n.bi-cone::before { content: map-get($bootstrap-icons-map, \"cone\"); }\n.bi-controller::before { content: map-get($bootstrap-icons-map, \"controller\"); }\n.bi-cpu-fill::before { content: map-get($bootstrap-icons-map, \"cpu-fill\"); }\n.bi-cpu::before { content: map-get($bootstrap-icons-map, \"cpu\"); }\n.bi-credit-card-2-back-fill::before { content: map-get($bootstrap-icons-map, \"credit-card-2-back-fill\"); }\n.bi-credit-card-2-back::before { content: map-get($bootstrap-icons-map, \"credit-card-2-back\"); }\n.bi-credit-card-2-front-fill::before { content: map-get($bootstrap-icons-map, \"credit-card-2-front-fill\"); }\n.bi-credit-card-2-front::before { content: map-get($bootstrap-icons-map, \"credit-card-2-front\"); }\n.bi-credit-card-fill::before { content: map-get($bootstrap-icons-map, \"credit-card-fill\"); }\n.bi-credit-card::before { content: map-get($bootstrap-icons-map, \"credit-card\"); }\n.bi-crop::before { content: map-get($bootstrap-icons-map, \"crop\"); }\n.bi-cup-fill::before { content: map-get($bootstrap-icons-map, \"cup-fill\"); }\n.bi-cup-straw::before { content: map-get($bootstrap-icons-map, \"cup-straw\"); }\n.bi-cup::before { content: map-get($bootstrap-icons-map, \"cup\"); }\n.bi-cursor-fill::before { content: map-get($bootstrap-icons-map, \"cursor-fill\"); }\n.bi-cursor-text::before { content: map-get($bootstrap-icons-map, \"cursor-text\"); }\n.bi-cursor::before { content: map-get($bootstrap-icons-map, \"cursor\"); }\n.bi-dash-circle-dotted::before { content: map-get($bootstrap-icons-map, \"dash-circle-dotted\"); }\n.bi-dash-circle-fill::before { content: map-get($bootstrap-icons-map, \"dash-circle-fill\"); }\n.bi-dash-circle::before { content: map-get($bootstrap-icons-map, \"dash-circle\"); }\n.bi-dash-square-dotted::before { content: map-get($bootstrap-icons-map, \"dash-square-dotted\"); }\n.bi-dash-square-fill::before { content: map-get($bootstrap-icons-map, \"dash-square-fill\"); }\n.bi-dash-square::before { content: map-get($bootstrap-icons-map, \"dash-square\"); }\n.bi-dash::before { content: map-get($bootstrap-icons-map, \"dash\"); }\n.bi-diagram-2-fill::before { content: map-get($bootstrap-icons-map, \"diagram-2-fill\"); }\n.bi-diagram-2::before { content: map-get($bootstrap-icons-map, \"diagram-2\"); }\n.bi-diagram-3-fill::before { content: map-get($bootstrap-icons-map, \"diagram-3-fill\"); }\n.bi-diagram-3::before { content: map-get($bootstrap-icons-map, \"diagram-3\"); }\n.bi-diamond-fill::before { content: map-get($bootstrap-icons-map, \"diamond-fill\"); }\n.bi-diamond-half::before { content: map-get($bootstrap-icons-map, \"diamond-half\"); }\n.bi-diamond::before { content: map-get($bootstrap-icons-map, \"diamond\"); }\n.bi-dice-1-fill::before { content: map-get($bootstrap-icons-map, \"dice-1-fill\"); }\n.bi-dice-1::before { content: map-get($bootstrap-icons-map, \"dice-1\"); }\n.bi-dice-2-fill::before { content: map-get($bootstrap-icons-map, \"dice-2-fill\"); }\n.bi-dice-2::before { content: map-get($bootstrap-icons-map, \"dice-2\"); }\n.bi-dice-3-fill::before { content: map-get($bootstrap-icons-map, \"dice-3-fill\"); }\n.bi-dice-3::before { content: map-get($bootstrap-icons-map, \"dice-3\"); }\n.bi-dice-4-fill::before { content: map-get($bootstrap-icons-map, \"dice-4-fill\"); }\n.bi-dice-4::before { content: map-get($bootstrap-icons-map, \"dice-4\"); }\n.bi-dice-5-fill::before { content: map-get($bootstrap-icons-map, \"dice-5-fill\"); }\n.bi-dice-5::before { content: map-get($bootstrap-icons-map, \"dice-5\"); }\n.bi-dice-6-fill::before { content: map-get($bootstrap-icons-map, \"dice-6-fill\"); }\n.bi-dice-6::before { content: map-get($bootstrap-icons-map, \"dice-6\"); }\n.bi-disc-fill::before { content: map-get($bootstrap-icons-map, \"disc-fill\"); }\n.bi-disc::before { content: map-get($bootstrap-icons-map, \"disc\"); }\n.bi-discord::before { content: map-get($bootstrap-icons-map, \"discord\"); }\n.bi-display-fill::before { content: map-get($bootstrap-icons-map, \"display-fill\"); }\n.bi-display::before { content: map-get($bootstrap-icons-map, \"display\"); }\n.bi-distribute-horizontal::before { content: map-get($bootstrap-icons-map, \"distribute-horizontal\"); }\n.bi-distribute-vertical::before { content: map-get($bootstrap-icons-map, \"distribute-vertical\"); }\n.bi-door-closed-fill::before { content: map-get($bootstrap-icons-map, \"door-closed-fill\"); }\n.bi-door-closed::before { content: map-get($bootstrap-icons-map, \"door-closed\"); }\n.bi-door-open-fill::before { content: map-get($bootstrap-icons-map, \"door-open-fill\"); }\n.bi-door-open::before { content: map-get($bootstrap-icons-map, \"door-open\"); }\n.bi-dot::before { content: map-get($bootstrap-icons-map, \"dot\"); }\n.bi-download::before { content: map-get($bootstrap-icons-map, \"download\"); }\n.bi-droplet-fill::before { content: map-get($bootstrap-icons-map, \"droplet-fill\"); }\n.bi-droplet-half::before { content: map-get($bootstrap-icons-map, \"droplet-half\"); }\n.bi-droplet::before { content: map-get($bootstrap-icons-map, \"droplet\"); }\n.bi-earbuds::before { content: map-get($bootstrap-icons-map, \"earbuds\"); }\n.bi-easel-fill::before { content: map-get($bootstrap-icons-map, \"easel-fill\"); }\n.bi-easel::before { content: map-get($bootstrap-icons-map, \"easel\"); }\n.bi-egg-fill::before { content: map-get($bootstrap-icons-map, \"egg-fill\"); }\n.bi-egg-fried::before { content: map-get($bootstrap-icons-map, \"egg-fried\"); }\n.bi-egg::before { content: map-get($bootstrap-icons-map, \"egg\"); }\n.bi-eject-fill::before { content: map-get($bootstrap-icons-map, \"eject-fill\"); }\n.bi-eject::before { content: map-get($bootstrap-icons-map, \"eject\"); }\n.bi-emoji-angry-fill::before { content: map-get($bootstrap-icons-map, \"emoji-angry-fill\"); }\n.bi-emoji-angry::before { content: map-get($bootstrap-icons-map, \"emoji-angry\"); }\n.bi-emoji-dizzy-fill::before { content: map-get($bootstrap-icons-map, \"emoji-dizzy-fill\"); }\n.bi-emoji-dizzy::before { content: map-get($bootstrap-icons-map, \"emoji-dizzy\"); }\n.bi-emoji-expressionless-fill::before { content: map-get($bootstrap-icons-map, \"emoji-expressionless-fill\"); }\n.bi-emoji-expressionless::before { content: map-get($bootstrap-icons-map, \"emoji-expressionless\"); }\n.bi-emoji-frown-fill::before { content: map-get($bootstrap-icons-map, \"emoji-frown-fill\"); }\n.bi-emoji-frown::before { content: map-get($bootstrap-icons-map, \"emoji-frown\"); }\n.bi-emoji-heart-eyes-fill::before { content: map-get($bootstrap-icons-map, \"emoji-heart-eyes-fill\"); }\n.bi-emoji-heart-eyes::before { content: map-get($bootstrap-icons-map, \"emoji-heart-eyes\"); }\n.bi-emoji-laughing-fill::before { content: map-get($bootstrap-icons-map, \"emoji-laughing-fill\"); }\n.bi-emoji-laughing::before { content: map-get($bootstrap-icons-map, \"emoji-laughing\"); }\n.bi-emoji-neutral-fill::before { content: map-get($bootstrap-icons-map, \"emoji-neutral-fill\"); }\n.bi-emoji-neutral::before { content: map-get($bootstrap-icons-map, \"emoji-neutral\"); }\n.bi-emoji-smile-fill::before { content: map-get($bootstrap-icons-map, \"emoji-smile-fill\"); }\n.bi-emoji-smile-upside-down-fill::before { content: map-get($bootstrap-icons-map, \"emoji-smile-upside-down-fill\"); }\n.bi-emoji-smile-upside-down::before { content: map-get($bootstrap-icons-map, \"emoji-smile-upside-down\"); }\n.bi-emoji-smile::before { content: map-get($bootstrap-icons-map, \"emoji-smile\"); }\n.bi-emoji-sunglasses-fill::before { content: map-get($bootstrap-icons-map, \"emoji-sunglasses-fill\"); }\n.bi-emoji-sunglasses::before { content: map-get($bootstrap-icons-map, \"emoji-sunglasses\"); }\n.bi-emoji-wink-fill::before { content: map-get($bootstrap-icons-map, \"emoji-wink-fill\"); }\n.bi-emoji-wink::before { content: map-get($bootstrap-icons-map, \"emoji-wink\"); }\n.bi-envelope-fill::before { content: map-get($bootstrap-icons-map, \"envelope-fill\"); }\n.bi-envelope-open-fill::before { content: map-get($bootstrap-icons-map, \"envelope-open-fill\"); }\n.bi-envelope-open::before { content: map-get($bootstrap-icons-map, \"envelope-open\"); }\n.bi-envelope::before { content: map-get($bootstrap-icons-map, \"envelope\"); }\n.bi-eraser-fill::before { content: map-get($bootstrap-icons-map, \"eraser-fill\"); }\n.bi-eraser::before { content: map-get($bootstrap-icons-map, \"eraser\"); }\n.bi-exclamation-circle-fill::before { content: map-get($bootstrap-icons-map, \"exclamation-circle-fill\"); }\n.bi-exclamation-circle::before { content: map-get($bootstrap-icons-map, \"exclamation-circle\"); }\n.bi-exclamation-diamond-fill::before { content: map-get($bootstrap-icons-map, \"exclamation-diamond-fill\"); }\n.bi-exclamation-diamond::before { content: map-get($bootstrap-icons-map, \"exclamation-diamond\"); }\n.bi-exclamation-octagon-fill::before { content: map-get($bootstrap-icons-map, \"exclamation-octagon-fill\"); }\n.bi-exclamation-octagon::before { content: map-get($bootstrap-icons-map, \"exclamation-octagon\"); }\n.bi-exclamation-square-fill::before { content: map-get($bootstrap-icons-map, \"exclamation-square-fill\"); }\n.bi-exclamation-square::before { content: map-get($bootstrap-icons-map, \"exclamation-square\"); }\n.bi-exclamation-triangle-fill::before { content: map-get($bootstrap-icons-map, \"exclamation-triangle-fill\"); }\n.bi-exclamation-triangle::before { content: map-get($bootstrap-icons-map, \"exclamation-triangle\"); }\n.bi-exclamation::before { content: map-get($bootstrap-icons-map, \"exclamation\"); }\n.bi-exclude::before { content: map-get($bootstrap-icons-map, \"exclude\"); }\n.bi-eye-fill::before { content: map-get($bootstrap-icons-map, \"eye-fill\"); }\n.bi-eye-slash-fill::before { content: map-get($bootstrap-icons-map, \"eye-slash-fill\"); }\n.bi-eye-slash::before { content: map-get($bootstrap-icons-map, \"eye-slash\"); }\n.bi-eye::before { content: map-get($bootstrap-icons-map, \"eye\"); }\n.bi-eyedropper::before { content: map-get($bootstrap-icons-map, \"eyedropper\"); }\n.bi-eyeglasses::before { content: map-get($bootstrap-icons-map, \"eyeglasses\"); }\n.bi-facebook::before { content: map-get($bootstrap-icons-map, \"facebook\"); }\n.bi-file-arrow-down-fill::before { content: map-get($bootstrap-icons-map, \"file-arrow-down-fill\"); }\n.bi-file-arrow-down::before { content: map-get($bootstrap-icons-map, \"file-arrow-down\"); }\n.bi-file-arrow-up-fill::before { content: map-get($bootstrap-icons-map, \"file-arrow-up-fill\"); }\n.bi-file-arrow-up::before { content: map-get($bootstrap-icons-map, \"file-arrow-up\"); }\n.bi-file-bar-graph-fill::before { content: map-get($bootstrap-icons-map, \"file-bar-graph-fill\"); }\n.bi-file-bar-graph::before { content: map-get($bootstrap-icons-map, \"file-bar-graph\"); }\n.bi-file-binary-fill::before { content: map-get($bootstrap-icons-map, \"file-binary-fill\"); }\n.bi-file-binary::before { content: map-get($bootstrap-icons-map, \"file-binary\"); }\n.bi-file-break-fill::before { content: map-get($bootstrap-icons-map, \"file-break-fill\"); }\n.bi-file-break::before { content: map-get($bootstrap-icons-map, \"file-break\"); }\n.bi-file-check-fill::before { content: map-get($bootstrap-icons-map, \"file-check-fill\"); }\n.bi-file-check::before { content: map-get($bootstrap-icons-map, \"file-check\"); }\n.bi-file-code-fill::before { content: map-get($bootstrap-icons-map, \"file-code-fill\"); }\n.bi-file-code::before { content: map-get($bootstrap-icons-map, \"file-code\"); }\n.bi-file-diff-fill::before { content: map-get($bootstrap-icons-map, \"file-diff-fill\"); }\n.bi-file-diff::before { content: map-get($bootstrap-icons-map, \"file-diff\"); }\n.bi-file-earmark-arrow-down-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-arrow-down-fill\"); }\n.bi-file-earmark-arrow-down::before { content: map-get($bootstrap-icons-map, \"file-earmark-arrow-down\"); }\n.bi-file-earmark-arrow-up-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-arrow-up-fill\"); }\n.bi-file-earmark-arrow-up::before { content: map-get($bootstrap-icons-map, \"file-earmark-arrow-up\"); }\n.bi-file-earmark-bar-graph-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-bar-graph-fill\"); }\n.bi-file-earmark-bar-graph::before { content: map-get($bootstrap-icons-map, \"file-earmark-bar-graph\"); }\n.bi-file-earmark-binary-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-binary-fill\"); }\n.bi-file-earmark-binary::before { content: map-get($bootstrap-icons-map, \"file-earmark-binary\"); }\n.bi-file-earmark-break-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-break-fill\"); }\n.bi-file-earmark-break::before { content: map-get($bootstrap-icons-map, \"file-earmark-break\"); }\n.bi-file-earmark-check-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-check-fill\"); }\n.bi-file-earmark-check::before { content: map-get($bootstrap-icons-map, \"file-earmark-check\"); }\n.bi-file-earmark-code-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-code-fill\"); }\n.bi-file-earmark-code::before { content: map-get($bootstrap-icons-map, \"file-earmark-code\"); }\n.bi-file-earmark-diff-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-diff-fill\"); }\n.bi-file-earmark-diff::before { content: map-get($bootstrap-icons-map, \"file-earmark-diff\"); }\n.bi-file-earmark-easel-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-easel-fill\"); }\n.bi-file-earmark-easel::before { content: map-get($bootstrap-icons-map, \"file-earmark-easel\"); }\n.bi-file-earmark-excel-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-excel-fill\"); }\n.bi-file-earmark-excel::before { content: map-get($bootstrap-icons-map, \"file-earmark-excel\"); }\n.bi-file-earmark-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-fill\"); }\n.bi-file-earmark-font-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-font-fill\"); }\n.bi-file-earmark-font::before { content: map-get($bootstrap-icons-map, \"file-earmark-font\"); }\n.bi-file-earmark-image-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-image-fill\"); }\n.bi-file-earmark-image::before { content: map-get($bootstrap-icons-map, \"file-earmark-image\"); }\n.bi-file-earmark-lock-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-lock-fill\"); }\n.bi-file-earmark-lock::before { content: map-get($bootstrap-icons-map, \"file-earmark-lock\"); }\n.bi-file-earmark-lock2-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-lock2-fill\"); }\n.bi-file-earmark-lock2::before { content: map-get($bootstrap-icons-map, \"file-earmark-lock2\"); }\n.bi-file-earmark-medical-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-medical-fill\"); }\n.bi-file-earmark-medical::before { content: map-get($bootstrap-icons-map, \"file-earmark-medical\"); }\n.bi-file-earmark-minus-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-minus-fill\"); }\n.bi-file-earmark-minus::before { content: map-get($bootstrap-icons-map, \"file-earmark-minus\"); }\n.bi-file-earmark-music-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-music-fill\"); }\n.bi-file-earmark-music::before { content: map-get($bootstrap-icons-map, \"file-earmark-music\"); }\n.bi-file-earmark-person-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-person-fill\"); }\n.bi-file-earmark-person::before { content: map-get($bootstrap-icons-map, \"file-earmark-person\"); }\n.bi-file-earmark-play-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-play-fill\"); }\n.bi-file-earmark-play::before { content: map-get($bootstrap-icons-map, \"file-earmark-play\"); }\n.bi-file-earmark-plus-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-plus-fill\"); }\n.bi-file-earmark-plus::before { content: map-get($bootstrap-icons-map, \"file-earmark-plus\"); }\n.bi-file-earmark-post-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-post-fill\"); }\n.bi-file-earmark-post::before { content: map-get($bootstrap-icons-map, \"file-earmark-post\"); }\n.bi-file-earmark-ppt-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-ppt-fill\"); }\n.bi-file-earmark-ppt::before { content: map-get($bootstrap-icons-map, \"file-earmark-ppt\"); }\n.bi-file-earmark-richtext-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-richtext-fill\"); }\n.bi-file-earmark-richtext::before { content: map-get($bootstrap-icons-map, \"file-earmark-richtext\"); }\n.bi-file-earmark-ruled-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-ruled-fill\"); }\n.bi-file-earmark-ruled::before { content: map-get($bootstrap-icons-map, \"file-earmark-ruled\"); }\n.bi-file-earmark-slides-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-slides-fill\"); }\n.bi-file-earmark-slides::before { content: map-get($bootstrap-icons-map, \"file-earmark-slides\"); }\n.bi-file-earmark-spreadsheet-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-spreadsheet-fill\"); }\n.bi-file-earmark-spreadsheet::before { content: map-get($bootstrap-icons-map, \"file-earmark-spreadsheet\"); }\n.bi-file-earmark-text-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-text-fill\"); }\n.bi-file-earmark-text::before { content: map-get($bootstrap-icons-map, \"file-earmark-text\"); }\n.bi-file-earmark-word-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-word-fill\"); }\n.bi-file-earmark-word::before { content: map-get($bootstrap-icons-map, \"file-earmark-word\"); }\n.bi-file-earmark-x-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-x-fill\"); }\n.bi-file-earmark-x::before { content: map-get($bootstrap-icons-map, \"file-earmark-x\"); }\n.bi-file-earmark-zip-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-zip-fill\"); }\n.bi-file-earmark-zip::before { content: map-get($bootstrap-icons-map, \"file-earmark-zip\"); }\n.bi-file-earmark::before { content: map-get($bootstrap-icons-map, \"file-earmark\"); }\n.bi-file-easel-fill::before { content: map-get($bootstrap-icons-map, \"file-easel-fill\"); }\n.bi-file-easel::before { content: map-get($bootstrap-icons-map, \"file-easel\"); }\n.bi-file-excel-fill::before { content: map-get($bootstrap-icons-map, \"file-excel-fill\"); }\n.bi-file-excel::before { content: map-get($bootstrap-icons-map, \"file-excel\"); }\n.bi-file-fill::before { content: map-get($bootstrap-icons-map, \"file-fill\"); }\n.bi-file-font-fill::before { content: map-get($bootstrap-icons-map, \"file-font-fill\"); }\n.bi-file-font::before { content: map-get($bootstrap-icons-map, \"file-font\"); }\n.bi-file-image-fill::before { content: map-get($bootstrap-icons-map, \"file-image-fill\"); }\n.bi-file-image::before { content: map-get($bootstrap-icons-map, \"file-image\"); }\n.bi-file-lock-fill::before { content: map-get($bootstrap-icons-map, \"file-lock-fill\"); }\n.bi-file-lock::before { content: map-get($bootstrap-icons-map, \"file-lock\"); }\n.bi-file-lock2-fill::before { content: map-get($bootstrap-icons-map, \"file-lock2-fill\"); }\n.bi-file-lock2::before { content: map-get($bootstrap-icons-map, \"file-lock2\"); }\n.bi-file-medical-fill::before { content: map-get($bootstrap-icons-map, \"file-medical-fill\"); }\n.bi-file-medical::before { content: map-get($bootstrap-icons-map, \"file-medical\"); }\n.bi-file-minus-fill::before { content: map-get($bootstrap-icons-map, \"file-minus-fill\"); }\n.bi-file-minus::before { content: map-get($bootstrap-icons-map, \"file-minus\"); }\n.bi-file-music-fill::before { content: map-get($bootstrap-icons-map, \"file-music-fill\"); }\n.bi-file-music::before { content: map-get($bootstrap-icons-map, \"file-music\"); }\n.bi-file-person-fill::before { content: map-get($bootstrap-icons-map, \"file-person-fill\"); }\n.bi-file-person::before { content: map-get($bootstrap-icons-map, \"file-person\"); }\n.bi-file-play-fill::before { content: map-get($bootstrap-icons-map, \"file-play-fill\"); }\n.bi-file-play::before { content: map-get($bootstrap-icons-map, \"file-play\"); }\n.bi-file-plus-fill::before { content: map-get($bootstrap-icons-map, \"file-plus-fill\"); }\n.bi-file-plus::before { content: map-get($bootstrap-icons-map, \"file-plus\"); }\n.bi-file-post-fill::before { content: map-get($bootstrap-icons-map, \"file-post-fill\"); }\n.bi-file-post::before { content: map-get($bootstrap-icons-map, \"file-post\"); }\n.bi-file-ppt-fill::before { content: map-get($bootstrap-icons-map, \"file-ppt-fill\"); }\n.bi-file-ppt::before { content: map-get($bootstrap-icons-map, \"file-ppt\"); }\n.bi-file-richtext-fill::before { content: map-get($bootstrap-icons-map, \"file-richtext-fill\"); }\n.bi-file-richtext::before { content: map-get($bootstrap-icons-map, \"file-richtext\"); }\n.bi-file-ruled-fill::before { content: map-get($bootstrap-icons-map, \"file-ruled-fill\"); }\n.bi-file-ruled::before { content: map-get($bootstrap-icons-map, \"file-ruled\"); }\n.bi-file-slides-fill::before { content: map-get($bootstrap-icons-map, \"file-slides-fill\"); }\n.bi-file-slides::before { content: map-get($bootstrap-icons-map, \"file-slides\"); }\n.bi-file-spreadsheet-fill::before { content: map-get($bootstrap-icons-map, \"file-spreadsheet-fill\"); }\n.bi-file-spreadsheet::before { content: map-get($bootstrap-icons-map, \"file-spreadsheet\"); }\n.bi-file-text-fill::before { content: map-get($bootstrap-icons-map, \"file-text-fill\"); }\n.bi-file-text::before { content: map-get($bootstrap-icons-map, \"file-text\"); }\n.bi-file-word-fill::before { content: map-get($bootstrap-icons-map, \"file-word-fill\"); }\n.bi-file-word::before { content: map-get($bootstrap-icons-map, \"file-word\"); }\n.bi-file-x-fill::before { content: map-get($bootstrap-icons-map, \"file-x-fill\"); }\n.bi-file-x::before { content: map-get($bootstrap-icons-map, \"file-x\"); }\n.bi-file-zip-fill::before { content: map-get($bootstrap-icons-map, \"file-zip-fill\"); }\n.bi-file-zip::before { content: map-get($bootstrap-icons-map, \"file-zip\"); }\n.bi-file::before { content: map-get($bootstrap-icons-map, \"file\"); }\n.bi-files-alt::before { content: map-get($bootstrap-icons-map, \"files-alt\"); }\n.bi-files::before { content: map-get($bootstrap-icons-map, \"files\"); }\n.bi-film::before { content: map-get($bootstrap-icons-map, \"film\"); }\n.bi-filter-circle-fill::before { content: map-get($bootstrap-icons-map, \"filter-circle-fill\"); }\n.bi-filter-circle::before { content: map-get($bootstrap-icons-map, \"filter-circle\"); }\n.bi-filter-left::before { content: map-get($bootstrap-icons-map, \"filter-left\"); }\n.bi-filter-right::before { content: map-get($bootstrap-icons-map, \"filter-right\"); }\n.bi-filter-square-fill::before { content: map-get($bootstrap-icons-map, \"filter-square-fill\"); }\n.bi-filter-square::before { content: map-get($bootstrap-icons-map, \"filter-square\"); }\n.bi-filter::before { content: map-get($bootstrap-icons-map, \"filter\"); }\n.bi-flag-fill::before { content: map-get($bootstrap-icons-map, \"flag-fill\"); }\n.bi-flag::before { content: map-get($bootstrap-icons-map, \"flag\"); }\n.bi-flower1::before { content: map-get($bootstrap-icons-map, \"flower1\"); }\n.bi-flower2::before { content: map-get($bootstrap-icons-map, \"flower2\"); }\n.bi-flower3::before { content: map-get($bootstrap-icons-map, \"flower3\"); }\n.bi-folder-check::before { content: map-get($bootstrap-icons-map, \"folder-check\"); }\n.bi-folder-fill::before { content: map-get($bootstrap-icons-map, \"folder-fill\"); }\n.bi-folder-minus::before { content: map-get($bootstrap-icons-map, \"folder-minus\"); }\n.bi-folder-plus::before { content: map-get($bootstrap-icons-map, \"folder-plus\"); }\n.bi-folder-symlink-fill::before { content: map-get($bootstrap-icons-map, \"folder-symlink-fill\"); }\n.bi-folder-symlink::before { content: map-get($bootstrap-icons-map, \"folder-symlink\"); }\n.bi-folder-x::before { content: map-get($bootstrap-icons-map, \"folder-x\"); }\n.bi-folder::before { content: map-get($bootstrap-icons-map, \"folder\"); }\n.bi-folder2-open::before { content: map-get($bootstrap-icons-map, \"folder2-open\"); }\n.bi-folder2::before { content: map-get($bootstrap-icons-map, \"folder2\"); }\n.bi-fonts::before { content: map-get($bootstrap-icons-map, \"fonts\"); }\n.bi-forward-fill::before { content: map-get($bootstrap-icons-map, \"forward-fill\"); }\n.bi-forward::before { content: map-get($bootstrap-icons-map, \"forward\"); }\n.bi-front::before { content: map-get($bootstrap-icons-map, \"front\"); }\n.bi-fullscreen-exit::before { content: map-get($bootstrap-icons-map, \"fullscreen-exit\"); }\n.bi-fullscreen::before { content: map-get($bootstrap-icons-map, \"fullscreen\"); }\n.bi-funnel-fill::before { content: map-get($bootstrap-icons-map, \"funnel-fill\"); }\n.bi-funnel::before { content: map-get($bootstrap-icons-map, \"funnel\"); }\n.bi-gear-fill::before { content: map-get($bootstrap-icons-map, \"gear-fill\"); }\n.bi-gear-wide-connected::before { content: map-get($bootstrap-icons-map, \"gear-wide-connected\"); }\n.bi-gear-wide::before { content: map-get($bootstrap-icons-map, \"gear-wide\"); }\n.bi-gear::before { content: map-get($bootstrap-icons-map, \"gear\"); }\n.bi-gem::before { content: map-get($bootstrap-icons-map, \"gem\"); }\n.bi-geo-alt-fill::before { content: map-get($bootstrap-icons-map, \"geo-alt-fill\"); }\n.bi-geo-alt::before { content: map-get($bootstrap-icons-map, \"geo-alt\"); }\n.bi-geo-fill::before { content: map-get($bootstrap-icons-map, \"geo-fill\"); }\n.bi-geo::before { content: map-get($bootstrap-icons-map, \"geo\"); }\n.bi-gift-fill::before { content: map-get($bootstrap-icons-map, \"gift-fill\"); }\n.bi-gift::before { content: map-get($bootstrap-icons-map, \"gift\"); }\n.bi-github::before { content: map-get($bootstrap-icons-map, \"github\"); }\n.bi-globe::before { content: map-get($bootstrap-icons-map, \"globe\"); }\n.bi-globe2::before { content: map-get($bootstrap-icons-map, \"globe2\"); }\n.bi-google::before { content: map-get($bootstrap-icons-map, \"google\"); }\n.bi-graph-down::before { content: map-get($bootstrap-icons-map, \"graph-down\"); }\n.bi-graph-up::before { content: map-get($bootstrap-icons-map, \"graph-up\"); }\n.bi-grid-1x2-fill::before { content: map-get($bootstrap-icons-map, \"grid-1x2-fill\"); }\n.bi-grid-1x2::before { content: map-get($bootstrap-icons-map, \"grid-1x2\"); }\n.bi-grid-3x2-gap-fill::before { content: map-get($bootstrap-icons-map, \"grid-3x2-gap-fill\"); }\n.bi-grid-3x2-gap::before { content: map-get($bootstrap-icons-map, \"grid-3x2-gap\"); }\n.bi-grid-3x2::before { content: map-get($bootstrap-icons-map, \"grid-3x2\"); }\n.bi-grid-3x3-gap-fill::before { content: map-get($bootstrap-icons-map, \"grid-3x3-gap-fill\"); }\n.bi-grid-3x3-gap::before { content: map-get($bootstrap-icons-map, \"grid-3x3-gap\"); }\n.bi-grid-3x3::before { content: map-get($bootstrap-icons-map, \"grid-3x3\"); }\n.bi-grid-fill::before { content: map-get($bootstrap-icons-map, \"grid-fill\"); }\n.bi-grid::before { content: map-get($bootstrap-icons-map, \"grid\"); }\n.bi-grip-horizontal::before { content: map-get($bootstrap-icons-map, \"grip-horizontal\"); }\n.bi-grip-vertical::before { content: map-get($bootstrap-icons-map, \"grip-vertical\"); }\n.bi-hammer::before { content: map-get($bootstrap-icons-map, \"hammer\"); }\n.bi-hand-index-fill::before { content: map-get($bootstrap-icons-map, \"hand-index-fill\"); }\n.bi-hand-index-thumb-fill::before { content: map-get($bootstrap-icons-map, \"hand-index-thumb-fill\"); }\n.bi-hand-index-thumb::before { content: map-get($bootstrap-icons-map, \"hand-index-thumb\"); }\n.bi-hand-index::before { content: map-get($bootstrap-icons-map, \"hand-index\"); }\n.bi-hand-thumbs-down-fill::before { content: map-get($bootstrap-icons-map, \"hand-thumbs-down-fill\"); }\n.bi-hand-thumbs-down::before { content: map-get($bootstrap-icons-map, \"hand-thumbs-down\"); }\n.bi-hand-thumbs-up-fill::before { content: map-get($bootstrap-icons-map, \"hand-thumbs-up-fill\"); }\n.bi-hand-thumbs-up::before { content: map-get($bootstrap-icons-map, \"hand-thumbs-up\"); }\n.bi-handbag-fill::before { content: map-get($bootstrap-icons-map, \"handbag-fill\"); }\n.bi-handbag::before { content: map-get($bootstrap-icons-map, \"handbag\"); }\n.bi-hash::before { content: map-get($bootstrap-icons-map, \"hash\"); }\n.bi-hdd-fill::before { content: map-get($bootstrap-icons-map, \"hdd-fill\"); }\n.bi-hdd-network-fill::before { content: map-get($bootstrap-icons-map, \"hdd-network-fill\"); }\n.bi-hdd-network::before { content: map-get($bootstrap-icons-map, \"hdd-network\"); }\n.bi-hdd-rack-fill::before { content: map-get($bootstrap-icons-map, \"hdd-rack-fill\"); }\n.bi-hdd-rack::before { content: map-get($bootstrap-icons-map, \"hdd-rack\"); }\n.bi-hdd-stack-fill::before { content: map-get($bootstrap-icons-map, \"hdd-stack-fill\"); }\n.bi-hdd-stack::before { content: map-get($bootstrap-icons-map, \"hdd-stack\"); }\n.bi-hdd::before { content: map-get($bootstrap-icons-map, \"hdd\"); }\n.bi-headphones::before { content: map-get($bootstrap-icons-map, \"headphones\"); }\n.bi-headset::before { content: map-get($bootstrap-icons-map, \"headset\"); }\n.bi-heart-fill::before { content: map-get($bootstrap-icons-map, \"heart-fill\"); }\n.bi-heart-half::before { content: map-get($bootstrap-icons-map, \"heart-half\"); }\n.bi-heart::before { content: map-get($bootstrap-icons-map, \"heart\"); }\n.bi-heptagon-fill::before { content: map-get($bootstrap-icons-map, \"heptagon-fill\"); }\n.bi-heptagon-half::before { content: map-get($bootstrap-icons-map, \"heptagon-half\"); }\n.bi-heptagon::before { content: map-get($bootstrap-icons-map, \"heptagon\"); }\n.bi-hexagon-fill::before { content: map-get($bootstrap-icons-map, \"hexagon-fill\"); }\n.bi-hexagon-half::before { content: map-get($bootstrap-icons-map, \"hexagon-half\"); }\n.bi-hexagon::before { content: map-get($bootstrap-icons-map, \"hexagon\"); }\n.bi-hourglass-bottom::before { content: map-get($bootstrap-icons-map, \"hourglass-bottom\"); }\n.bi-hourglass-split::before { content: map-get($bootstrap-icons-map, \"hourglass-split\"); }\n.bi-hourglass-top::before { content: map-get($bootstrap-icons-map, \"hourglass-top\"); }\n.bi-hourglass::before { content: map-get($bootstrap-icons-map, \"hourglass\"); }\n.bi-house-door-fill::before { content: map-get($bootstrap-icons-map, \"house-door-fill\"); }\n.bi-house-door::before { content: map-get($bootstrap-icons-map, \"house-door\"); }\n.bi-house-fill::before { content: map-get($bootstrap-icons-map, \"house-fill\"); }\n.bi-house::before { content: map-get($bootstrap-icons-map, \"house\"); }\n.bi-hr::before { content: map-get($bootstrap-icons-map, \"hr\"); }\n.bi-hurricane::before { content: map-get($bootstrap-icons-map, \"hurricane\"); }\n.bi-image-alt::before { content: map-get($bootstrap-icons-map, \"image-alt\"); }\n.bi-image-fill::before { content: map-get($bootstrap-icons-map, \"image-fill\"); }\n.bi-image::before { content: map-get($bootstrap-icons-map, \"image\"); }\n.bi-images::before { content: map-get($bootstrap-icons-map, \"images\"); }\n.bi-inbox-fill::before { content: map-get($bootstrap-icons-map, \"inbox-fill\"); }\n.bi-inbox::before { content: map-get($bootstrap-icons-map, \"inbox\"); }\n.bi-inboxes-fill::before { content: map-get($bootstrap-icons-map, \"inboxes-fill\"); }\n.bi-inboxes::before { content: map-get($bootstrap-icons-map, \"inboxes\"); }\n.bi-info-circle-fill::before { content: map-get($bootstrap-icons-map, \"info-circle-fill\"); }\n.bi-info-circle::before { content: map-get($bootstrap-icons-map, \"info-circle\"); }\n.bi-info-square-fill::before { content: map-get($bootstrap-icons-map, \"info-square-fill\"); }\n.bi-info-square::before { content: map-get($bootstrap-icons-map, \"info-square\"); }\n.bi-info::before { content: map-get($bootstrap-icons-map, \"info\"); }\n.bi-input-cursor-text::before { content: map-get($bootstrap-icons-map, \"input-cursor-text\"); }\n.bi-input-cursor::before { content: map-get($bootstrap-icons-map, \"input-cursor\"); }\n.bi-instagram::before { content: map-get($bootstrap-icons-map, \"instagram\"); }\n.bi-intersect::before { content: map-get($bootstrap-icons-map, \"intersect\"); }\n.bi-journal-album::before { content: map-get($bootstrap-icons-map, \"journal-album\"); }\n.bi-journal-arrow-down::before { content: map-get($bootstrap-icons-map, \"journal-arrow-down\"); }\n.bi-journal-arrow-up::before { content: map-get($bootstrap-icons-map, \"journal-arrow-up\"); }\n.bi-journal-bookmark-fill::before { content: map-get($bootstrap-icons-map, \"journal-bookmark-fill\"); }\n.bi-journal-bookmark::before { content: map-get($bootstrap-icons-map, \"journal-bookmark\"); }\n.bi-journal-check::before { content: map-get($bootstrap-icons-map, \"journal-check\"); }\n.bi-journal-code::before { content: map-get($bootstrap-icons-map, \"journal-code\"); }\n.bi-journal-medical::before { content: map-get($bootstrap-icons-map, \"journal-medical\"); }\n.bi-journal-minus::before { content: map-get($bootstrap-icons-map, \"journal-minus\"); }\n.bi-journal-plus::before { content: map-get($bootstrap-icons-map, \"journal-plus\"); }\n.bi-journal-richtext::before { content: map-get($bootstrap-icons-map, \"journal-richtext\"); }\n.bi-journal-text::before { content: map-get($bootstrap-icons-map, \"journal-text\"); }\n.bi-journal-x::before { content: map-get($bootstrap-icons-map, \"journal-x\"); }\n.bi-journal::before { content: map-get($bootstrap-icons-map, \"journal\"); }\n.bi-journals::before { content: map-get($bootstrap-icons-map, \"journals\"); }\n.bi-joystick::before { content: map-get($bootstrap-icons-map, \"joystick\"); }\n.bi-justify-left::before { content: map-get($bootstrap-icons-map, \"justify-left\"); }\n.bi-justify-right::before { content: map-get($bootstrap-icons-map, \"justify-right\"); }\n.bi-justify::before { content: map-get($bootstrap-icons-map, \"justify\"); }\n.bi-kanban-fill::before { content: map-get($bootstrap-icons-map, \"kanban-fill\"); }\n.bi-kanban::before { content: map-get($bootstrap-icons-map, \"kanban\"); }\n.bi-key-fill::before { content: map-get($bootstrap-icons-map, \"key-fill\"); }\n.bi-key::before { content: map-get($bootstrap-icons-map, \"key\"); }\n.bi-keyboard-fill::before { content: map-get($bootstrap-icons-map, \"keyboard-fill\"); }\n.bi-keyboard::before { content: map-get($bootstrap-icons-map, \"keyboard\"); }\n.bi-ladder::before { content: map-get($bootstrap-icons-map, \"ladder\"); }\n.bi-lamp-fill::before { content: map-get($bootstrap-icons-map, \"lamp-fill\"); }\n.bi-lamp::before { content: map-get($bootstrap-icons-map, \"lamp\"); }\n.bi-laptop-fill::before { content: map-get($bootstrap-icons-map, \"laptop-fill\"); }\n.bi-laptop::before { content: map-get($bootstrap-icons-map, \"laptop\"); }\n.bi-layer-backward::before { content: map-get($bootstrap-icons-map, \"layer-backward\"); }\n.bi-layer-forward::before { content: map-get($bootstrap-icons-map, \"layer-forward\"); }\n.bi-layers-fill::before { content: map-get($bootstrap-icons-map, \"layers-fill\"); }\n.bi-layers-half::before { content: map-get($bootstrap-icons-map, \"layers-half\"); }\n.bi-layers::before { content: map-get($bootstrap-icons-map, \"layers\"); }\n.bi-layout-sidebar-inset-reverse::before { content: map-get($bootstrap-icons-map, \"layout-sidebar-inset-reverse\"); }\n.bi-layout-sidebar-inset::before { content: map-get($bootstrap-icons-map, \"layout-sidebar-inset\"); }\n.bi-layout-sidebar-reverse::before { content: map-get($bootstrap-icons-map, \"layout-sidebar-reverse\"); }\n.bi-layout-sidebar::before { content: map-get($bootstrap-icons-map, \"layout-sidebar\"); }\n.bi-layout-split::before { content: map-get($bootstrap-icons-map, \"layout-split\"); }\n.bi-layout-text-sidebar-reverse::before { content: map-get($bootstrap-icons-map, \"layout-text-sidebar-reverse\"); }\n.bi-layout-text-sidebar::before { content: map-get($bootstrap-icons-map, \"layout-text-sidebar\"); }\n.bi-layout-text-window-reverse::before { content: map-get($bootstrap-icons-map, \"layout-text-window-reverse\"); }\n.bi-layout-text-window::before { content: map-get($bootstrap-icons-map, \"layout-text-window\"); }\n.bi-layout-three-columns::before { content: map-get($bootstrap-icons-map, \"layout-three-columns\"); }\n.bi-layout-wtf::before { content: map-get($bootstrap-icons-map, \"layout-wtf\"); }\n.bi-life-preserver::before { content: map-get($bootstrap-icons-map, \"life-preserver\"); }\n.bi-lightbulb-fill::before { content: map-get($bootstrap-icons-map, \"lightbulb-fill\"); }\n.bi-lightbulb-off-fill::before { content: map-get($bootstrap-icons-map, \"lightbulb-off-fill\"); }\n.bi-lightbulb-off::before { content: map-get($bootstrap-icons-map, \"lightbulb-off\"); }\n.bi-lightbulb::before { content: map-get($bootstrap-icons-map, \"lightbulb\"); }\n.bi-lightning-charge-fill::before { content: map-get($bootstrap-icons-map, \"lightning-charge-fill\"); }\n.bi-lightning-charge::before { content: map-get($bootstrap-icons-map, \"lightning-charge\"); }\n.bi-lightning-fill::before { content: map-get($bootstrap-icons-map, \"lightning-fill\"); }\n.bi-lightning::before { content: map-get($bootstrap-icons-map, \"lightning\"); }\n.bi-link-45deg::before { content: map-get($bootstrap-icons-map, \"link-45deg\"); }\n.bi-link::before { content: map-get($bootstrap-icons-map, \"link\"); }\n.bi-linkedin::before { content: map-get($bootstrap-icons-map, \"linkedin\"); }\n.bi-list-check::before { content: map-get($bootstrap-icons-map, \"list-check\"); }\n.bi-list-nested::before { content: map-get($bootstrap-icons-map, \"list-nested\"); }\n.bi-list-ol::before { content: map-get($bootstrap-icons-map, \"list-ol\"); }\n.bi-list-stars::before { content: map-get($bootstrap-icons-map, \"list-stars\"); }\n.bi-list-task::before { content: map-get($bootstrap-icons-map, \"list-task\"); }\n.bi-list-ul::before { content: map-get($bootstrap-icons-map, \"list-ul\"); }\n.bi-list::before { content: map-get($bootstrap-icons-map, \"list\"); }\n.bi-lock-fill::before { content: map-get($bootstrap-icons-map, \"lock-fill\"); }\n.bi-lock::before { content: map-get($bootstrap-icons-map, \"lock\"); }\n.bi-mailbox::before { content: map-get($bootstrap-icons-map, \"mailbox\"); }\n.bi-mailbox2::before { content: map-get($bootstrap-icons-map, \"mailbox2\"); }\n.bi-map-fill::before { content: map-get($bootstrap-icons-map, \"map-fill\"); }\n.bi-map::before { content: map-get($bootstrap-icons-map, \"map\"); }\n.bi-markdown-fill::before { content: map-get($bootstrap-icons-map, \"markdown-fill\"); }\n.bi-markdown::before { content: map-get($bootstrap-icons-map, \"markdown\"); }\n.bi-mask::before { content: map-get($bootstrap-icons-map, \"mask\"); }\n.bi-megaphone-fill::before { content: map-get($bootstrap-icons-map, \"megaphone-fill\"); }\n.bi-megaphone::before { content: map-get($bootstrap-icons-map, \"megaphone\"); }\n.bi-menu-app-fill::before { content: map-get($bootstrap-icons-map, \"menu-app-fill\"); }\n.bi-menu-app::before { content: map-get($bootstrap-icons-map, \"menu-app\"); }\n.bi-menu-button-fill::before { content: map-get($bootstrap-icons-map, \"menu-button-fill\"); }\n.bi-menu-button-wide-fill::before { content: map-get($bootstrap-icons-map, \"menu-button-wide-fill\"); }\n.bi-menu-button-wide::before { content: map-get($bootstrap-icons-map, \"menu-button-wide\"); }\n.bi-menu-button::before { content: map-get($bootstrap-icons-map, \"menu-button\"); }\n.bi-menu-down::before { content: map-get($bootstrap-icons-map, \"menu-down\"); }\n.bi-menu-up::before { content: map-get($bootstrap-icons-map, \"menu-up\"); }\n.bi-mic-fill::before { content: map-get($bootstrap-icons-map, \"mic-fill\"); }\n.bi-mic-mute-fill::before { content: map-get($bootstrap-icons-map, \"mic-mute-fill\"); }\n.bi-mic-mute::before { content: map-get($bootstrap-icons-map, \"mic-mute\"); }\n.bi-mic::before { content: map-get($bootstrap-icons-map, \"mic\"); }\n.bi-minecart-loaded::before { content: map-get($bootstrap-icons-map, \"minecart-loaded\"); }\n.bi-minecart::before { content: map-get($bootstrap-icons-map, \"minecart\"); }\n.bi-moisture::before { content: map-get($bootstrap-icons-map, \"moisture\"); }\n.bi-moon-fill::before { content: map-get($bootstrap-icons-map, \"moon-fill\"); }\n.bi-moon-stars-fill::before { content: map-get($bootstrap-icons-map, \"moon-stars-fill\"); }\n.bi-moon-stars::before { content: map-get($bootstrap-icons-map, \"moon-stars\"); }\n.bi-moon::before { content: map-get($bootstrap-icons-map, \"moon\"); }\n.bi-mouse-fill::before { content: map-get($bootstrap-icons-map, \"mouse-fill\"); }\n.bi-mouse::before { content: map-get($bootstrap-icons-map, \"mouse\"); }\n.bi-mouse2-fill::before { content: map-get($bootstrap-icons-map, \"mouse2-fill\"); }\n.bi-mouse2::before { content: map-get($bootstrap-icons-map, \"mouse2\"); }\n.bi-mouse3-fill::before { content: map-get($bootstrap-icons-map, \"mouse3-fill\"); }\n.bi-mouse3::before { content: map-get($bootstrap-icons-map, \"mouse3\"); }\n.bi-music-note-beamed::before { content: map-get($bootstrap-icons-map, \"music-note-beamed\"); }\n.bi-music-note-list::before { content: map-get($bootstrap-icons-map, \"music-note-list\"); }\n.bi-music-note::before { content: map-get($bootstrap-icons-map, \"music-note\"); }\n.bi-music-player-fill::before { content: map-get($bootstrap-icons-map, \"music-player-fill\"); }\n.bi-music-player::before { content: map-get($bootstrap-icons-map, \"music-player\"); }\n.bi-newspaper::before { content: map-get($bootstrap-icons-map, \"newspaper\"); }\n.bi-node-minus-fill::before { content: map-get($bootstrap-icons-map, \"node-minus-fill\"); }\n.bi-node-minus::before { content: map-get($bootstrap-icons-map, \"node-minus\"); }\n.bi-node-plus-fill::before { content: map-get($bootstrap-icons-map, \"node-plus-fill\"); }\n.bi-node-plus::before { content: map-get($bootstrap-icons-map, \"node-plus\"); }\n.bi-nut-fill::before { content: map-get($bootstrap-icons-map, \"nut-fill\"); }\n.bi-nut::before { content: map-get($bootstrap-icons-map, \"nut\"); }\n.bi-octagon-fill::before { content: map-get($bootstrap-icons-map, \"octagon-fill\"); }\n.bi-octagon-half::before { content: map-get($bootstrap-icons-map, \"octagon-half\"); }\n.bi-octagon::before { content: map-get($bootstrap-icons-map, \"octagon\"); }\n.bi-option::before { content: map-get($bootstrap-icons-map, \"option\"); }\n.bi-outlet::before { content: map-get($bootstrap-icons-map, \"outlet\"); }\n.bi-paint-bucket::before { content: map-get($bootstrap-icons-map, \"paint-bucket\"); }\n.bi-palette-fill::before { content: map-get($bootstrap-icons-map, \"palette-fill\"); }\n.bi-palette::before { content: map-get($bootstrap-icons-map, \"palette\"); }\n.bi-palette2::before { content: map-get($bootstrap-icons-map, \"palette2\"); }\n.bi-paperclip::before { content: map-get($bootstrap-icons-map, \"paperclip\"); }\n.bi-paragraph::before { content: map-get($bootstrap-icons-map, \"paragraph\"); }\n.bi-patch-check-fill::before { content: map-get($bootstrap-icons-map, \"patch-check-fill\"); }\n.bi-patch-check::before { content: map-get($bootstrap-icons-map, \"patch-check\"); }\n.bi-patch-exclamation-fill::before { content: map-get($bootstrap-icons-map, \"patch-exclamation-fill\"); }\n.bi-patch-exclamation::before { content: map-get($bootstrap-icons-map, \"patch-exclamation\"); }\n.bi-patch-minus-fill::before { content: map-get($bootstrap-icons-map, \"patch-minus-fill\"); }\n.bi-patch-minus::before { content: map-get($bootstrap-icons-map, \"patch-minus\"); }\n.bi-patch-plus-fill::before { content: map-get($bootstrap-icons-map, \"patch-plus-fill\"); }\n.bi-patch-plus::before { content: map-get($bootstrap-icons-map, \"patch-plus\"); }\n.bi-patch-question-fill::before { content: map-get($bootstrap-icons-map, \"patch-question-fill\"); }\n.bi-patch-question::before { content: map-get($bootstrap-icons-map, \"patch-question\"); }\n.bi-pause-btn-fill::before { content: map-get($bootstrap-icons-map, \"pause-btn-fill\"); }\n.bi-pause-btn::before { content: map-get($bootstrap-icons-map, \"pause-btn\"); }\n.bi-pause-circle-fill::before { content: map-get($bootstrap-icons-map, \"pause-circle-fill\"); }\n.bi-pause-circle::before { content: map-get($bootstrap-icons-map, \"pause-circle\"); }\n.bi-pause-fill::before { content: map-get($bootstrap-icons-map, \"pause-fill\"); }\n.bi-pause::before { content: map-get($bootstrap-icons-map, \"pause\"); }\n.bi-peace-fill::before { content: map-get($bootstrap-icons-map, \"peace-fill\"); }\n.bi-peace::before { content: map-get($bootstrap-icons-map, \"peace\"); }\n.bi-pen-fill::before { content: map-get($bootstrap-icons-map, \"pen-fill\"); }\n.bi-pen::before { content: map-get($bootstrap-icons-map, \"pen\"); }\n.bi-pencil-fill::before { content: map-get($bootstrap-icons-map, \"pencil-fill\"); }\n.bi-pencil-square::before { content: map-get($bootstrap-icons-map, \"pencil-square\"); }\n.bi-pencil::before { content: map-get($bootstrap-icons-map, \"pencil\"); }\n.bi-pentagon-fill::before { content: map-get($bootstrap-icons-map, \"pentagon-fill\"); }\n.bi-pentagon-half::before { content: map-get($bootstrap-icons-map, \"pentagon-half\"); }\n.bi-pentagon::before { content: map-get($bootstrap-icons-map, \"pentagon\"); }\n.bi-people-fill::before { content: map-get($bootstrap-icons-map, \"people-fill\"); }\n.bi-people::before { content: map-get($bootstrap-icons-map, \"people\"); }\n.bi-percent::before { content: map-get($bootstrap-icons-map, \"percent\"); }\n.bi-person-badge-fill::before { content: map-get($bootstrap-icons-map, \"person-badge-fill\"); }\n.bi-person-badge::before { content: map-get($bootstrap-icons-map, \"person-badge\"); }\n.bi-person-bounding-box::before { content: map-get($bootstrap-icons-map, \"person-bounding-box\"); }\n.bi-person-check-fill::before { content: map-get($bootstrap-icons-map, \"person-check-fill\"); }\n.bi-person-check::before { content: map-get($bootstrap-icons-map, \"person-check\"); }\n.bi-person-circle::before { content: map-get($bootstrap-icons-map, \"person-circle\"); }\n.bi-person-dash-fill::before { content: map-get($bootstrap-icons-map, \"person-dash-fill\"); }\n.bi-person-dash::before { content: map-get($bootstrap-icons-map, \"person-dash\"); }\n.bi-person-fill::before { content: map-get($bootstrap-icons-map, \"person-fill\"); }\n.bi-person-lines-fill::before { content: map-get($bootstrap-icons-map, \"person-lines-fill\"); }\n.bi-person-plus-fill::before { content: map-get($bootstrap-icons-map, \"person-plus-fill\"); }\n.bi-person-plus::before { content: map-get($bootstrap-icons-map, \"person-plus\"); }\n.bi-person-square::before { content: map-get($bootstrap-icons-map, \"person-square\"); }\n.bi-person-x-fill::before { content: map-get($bootstrap-icons-map, \"person-x-fill\"); }\n.bi-person-x::before { content: map-get($bootstrap-icons-map, \"person-x\"); }\n.bi-person::before { content: map-get($bootstrap-icons-map, \"person\"); }\n.bi-phone-fill::before { content: map-get($bootstrap-icons-map, \"phone-fill\"); }\n.bi-phone-landscape-fill::before { content: map-get($bootstrap-icons-map, \"phone-landscape-fill\"); }\n.bi-phone-landscape::before { content: map-get($bootstrap-icons-map, \"phone-landscape\"); }\n.bi-phone-vibrate-fill::before { content: map-get($bootstrap-icons-map, \"phone-vibrate-fill\"); }\n.bi-phone-vibrate::before { content: map-get($bootstrap-icons-map, \"phone-vibrate\"); }\n.bi-phone::before { content: map-get($bootstrap-icons-map, \"phone\"); }\n.bi-pie-chart-fill::before { content: map-get($bootstrap-icons-map, \"pie-chart-fill\"); }\n.bi-pie-chart::before { content: map-get($bootstrap-icons-map, \"pie-chart\"); }\n.bi-pin-angle-fill::before { content: map-get($bootstrap-icons-map, \"pin-angle-fill\"); }\n.bi-pin-angle::before { content: map-get($bootstrap-icons-map, \"pin-angle\"); }\n.bi-pin-fill::before { content: map-get($bootstrap-icons-map, \"pin-fill\"); }\n.bi-pin::before { content: map-get($bootstrap-icons-map, \"pin\"); }\n.bi-pip-fill::before { content: map-get($bootstrap-icons-map, \"pip-fill\"); }\n.bi-pip::before { content: map-get($bootstrap-icons-map, \"pip\"); }\n.bi-play-btn-fill::before { content: map-get($bootstrap-icons-map, \"play-btn-fill\"); }\n.bi-play-btn::before { content: map-get($bootstrap-icons-map, \"play-btn\"); }\n.bi-play-circle-fill::before { content: map-get($bootstrap-icons-map, \"play-circle-fill\"); }\n.bi-play-circle::before { content: map-get($bootstrap-icons-map, \"play-circle\"); }\n.bi-play-fill::before { content: map-get($bootstrap-icons-map, \"play-fill\"); }\n.bi-play::before { content: map-get($bootstrap-icons-map, \"play\"); }\n.bi-plug-fill::before { content: map-get($bootstrap-icons-map, \"plug-fill\"); }\n.bi-plug::before { content: map-get($bootstrap-icons-map, \"plug\"); }\n.bi-plus-circle-dotted::before { content: map-get($bootstrap-icons-map, \"plus-circle-dotted\"); }\n.bi-plus-circle-fill::before { content: map-get($bootstrap-icons-map, \"plus-circle-fill\"); }\n.bi-plus-circle::before { content: map-get($bootstrap-icons-map, \"plus-circle\"); }\n.bi-plus-square-dotted::before { content: map-get($bootstrap-icons-map, \"plus-square-dotted\"); }\n.bi-plus-square-fill::before { content: map-get($bootstrap-icons-map, \"plus-square-fill\"); }\n.bi-plus-square::before { content: map-get($bootstrap-icons-map, \"plus-square\"); }\n.bi-plus::before { content: map-get($bootstrap-icons-map, \"plus\"); }\n.bi-power::before { content: map-get($bootstrap-icons-map, \"power\"); }\n.bi-printer-fill::before { content: map-get($bootstrap-icons-map, \"printer-fill\"); }\n.bi-printer::before { content: map-get($bootstrap-icons-map, \"printer\"); }\n.bi-puzzle-fill::before { content: map-get($bootstrap-icons-map, \"puzzle-fill\"); }\n.bi-puzzle::before { content: map-get($bootstrap-icons-map, \"puzzle\"); }\n.bi-question-circle-fill::before { content: map-get($bootstrap-icons-map, \"question-circle-fill\"); }\n.bi-question-circle::before { content: map-get($bootstrap-icons-map, \"question-circle\"); }\n.bi-question-diamond-fill::before { content: map-get($bootstrap-icons-map, \"question-diamond-fill\"); }\n.bi-question-diamond::before { content: map-get($bootstrap-icons-map, \"question-diamond\"); }\n.bi-question-octagon-fill::before { content: map-get($bootstrap-icons-map, \"question-octagon-fill\"); }\n.bi-question-octagon::before { content: map-get($bootstrap-icons-map, \"question-octagon\"); }\n.bi-question-square-fill::before { content: map-get($bootstrap-icons-map, \"question-square-fill\"); }\n.bi-question-square::before { content: map-get($bootstrap-icons-map, \"question-square\"); }\n.bi-question::before { content: map-get($bootstrap-icons-map, \"question\"); }\n.bi-rainbow::before { content: map-get($bootstrap-icons-map, \"rainbow\"); }\n.bi-receipt-cutoff::before { content: map-get($bootstrap-icons-map, \"receipt-cutoff\"); }\n.bi-receipt::before { content: map-get($bootstrap-icons-map, \"receipt\"); }\n.bi-reception-0::before { content: map-get($bootstrap-icons-map, \"reception-0\"); }\n.bi-reception-1::before { content: map-get($bootstrap-icons-map, \"reception-1\"); }\n.bi-reception-2::before { content: map-get($bootstrap-icons-map, \"reception-2\"); }\n.bi-reception-3::before { content: map-get($bootstrap-icons-map, \"reception-3\"); }\n.bi-reception-4::before { content: map-get($bootstrap-icons-map, \"reception-4\"); }\n.bi-record-btn-fill::before { content: map-get($bootstrap-icons-map, \"record-btn-fill\"); }\n.bi-record-btn::before { content: map-get($bootstrap-icons-map, \"record-btn\"); }\n.bi-record-circle-fill::before { content: map-get($bootstrap-icons-map, \"record-circle-fill\"); }\n.bi-record-circle::before { content: map-get($bootstrap-icons-map, \"record-circle\"); }\n.bi-record-fill::before { content: map-get($bootstrap-icons-map, \"record-fill\"); }\n.bi-record::before { content: map-get($bootstrap-icons-map, \"record\"); }\n.bi-record2-fill::before { content: map-get($bootstrap-icons-map, \"record2-fill\"); }\n.bi-record2::before { content: map-get($bootstrap-icons-map, \"record2\"); }\n.bi-reply-all-fill::before { content: map-get($bootstrap-icons-map, \"reply-all-fill\"); }\n.bi-reply-all::before { content: map-get($bootstrap-icons-map, \"reply-all\"); }\n.bi-reply-fill::before { content: map-get($bootstrap-icons-map, \"reply-fill\"); }\n.bi-reply::before { content: map-get($bootstrap-icons-map, \"reply\"); }\n.bi-rss-fill::before { content: map-get($bootstrap-icons-map, \"rss-fill\"); }\n.bi-rss::before { content: map-get($bootstrap-icons-map, \"rss\"); }\n.bi-rulers::before { content: map-get($bootstrap-icons-map, \"rulers\"); }\n.bi-save-fill::before { content: map-get($bootstrap-icons-map, \"save-fill\"); }\n.bi-save::before { content: map-get($bootstrap-icons-map, \"save\"); }\n.bi-save2-fill::before { content: map-get($bootstrap-icons-map, \"save2-fill\"); }\n.bi-save2::before { content: map-get($bootstrap-icons-map, \"save2\"); }\n.bi-scissors::before { content: map-get($bootstrap-icons-map, \"scissors\"); }\n.bi-screwdriver::before { content: map-get($bootstrap-icons-map, \"screwdriver\"); }\n.bi-search::before { content: map-get($bootstrap-icons-map, \"search\"); }\n.bi-segmented-nav::before { content: map-get($bootstrap-icons-map, \"segmented-nav\"); }\n.bi-server::before { content: map-get($bootstrap-icons-map, \"server\"); }\n.bi-share-fill::before { content: map-get($bootstrap-icons-map, \"share-fill\"); }\n.bi-share::before { content: map-get($bootstrap-icons-map, \"share\"); }\n.bi-shield-check::before { content: map-get($bootstrap-icons-map, \"shield-check\"); }\n.bi-shield-exclamation::before { content: map-get($bootstrap-icons-map, \"shield-exclamation\"); }\n.bi-shield-fill-check::before { content: map-get($bootstrap-icons-map, \"shield-fill-check\"); }\n.bi-shield-fill-exclamation::before { content: map-get($bootstrap-icons-map, \"shield-fill-exclamation\"); }\n.bi-shield-fill-minus::before { content: map-get($bootstrap-icons-map, \"shield-fill-minus\"); }\n.bi-shield-fill-plus::before { content: map-get($bootstrap-icons-map, \"shield-fill-plus\"); }\n.bi-shield-fill-x::before { content: map-get($bootstrap-icons-map, \"shield-fill-x\"); }\n.bi-shield-fill::before { content: map-get($bootstrap-icons-map, \"shield-fill\"); }\n.bi-shield-lock-fill::before { content: map-get($bootstrap-icons-map, \"shield-lock-fill\"); }\n.bi-shield-lock::before { content: map-get($bootstrap-icons-map, \"shield-lock\"); }\n.bi-shield-minus::before { content: map-get($bootstrap-icons-map, \"shield-minus\"); }\n.bi-shield-plus::before { content: map-get($bootstrap-icons-map, \"shield-plus\"); }\n.bi-shield-shaded::before { content: map-get($bootstrap-icons-map, \"shield-shaded\"); }\n.bi-shield-slash-fill::before { content: map-get($bootstrap-icons-map, \"shield-slash-fill\"); }\n.bi-shield-slash::before { content: map-get($bootstrap-icons-map, \"shield-slash\"); }\n.bi-shield-x::before { content: map-get($bootstrap-icons-map, \"shield-x\"); }\n.bi-shield::before { content: map-get($bootstrap-icons-map, \"shield\"); }\n.bi-shift-fill::before { content: map-get($bootstrap-icons-map, \"shift-fill\"); }\n.bi-shift::before { content: map-get($bootstrap-icons-map, \"shift\"); }\n.bi-shop-window::before { content: map-get($bootstrap-icons-map, \"shop-window\"); }\n.bi-shop::before { content: map-get($bootstrap-icons-map, \"shop\"); }\n.bi-shuffle::before { content: map-get($bootstrap-icons-map, \"shuffle\"); }\n.bi-signpost-2-fill::before { content: map-get($bootstrap-icons-map, \"signpost-2-fill\"); }\n.bi-signpost-2::before { content: map-get($bootstrap-icons-map, \"signpost-2\"); }\n.bi-signpost-fill::before { content: map-get($bootstrap-icons-map, \"signpost-fill\"); }\n.bi-signpost-split-fill::before { content: map-get($bootstrap-icons-map, \"signpost-split-fill\"); }\n.bi-signpost-split::before { content: map-get($bootstrap-icons-map, \"signpost-split\"); }\n.bi-signpost::before { content: map-get($bootstrap-icons-map, \"signpost\"); }\n.bi-sim-fill::before { content: map-get($bootstrap-icons-map, \"sim-fill\"); }\n.bi-sim::before { content: map-get($bootstrap-icons-map, \"sim\"); }\n.bi-skip-backward-btn-fill::before { content: map-get($bootstrap-icons-map, \"skip-backward-btn-fill\"); }\n.bi-skip-backward-btn::before { content: map-get($bootstrap-icons-map, \"skip-backward-btn\"); }\n.bi-skip-backward-circle-fill::before { content: map-get($bootstrap-icons-map, \"skip-backward-circle-fill\"); }\n.bi-skip-backward-circle::before { content: map-get($bootstrap-icons-map, \"skip-backward-circle\"); }\n.bi-skip-backward-fill::before { content: map-get($bootstrap-icons-map, \"skip-backward-fill\"); }\n.bi-skip-backward::before { content: map-get($bootstrap-icons-map, \"skip-backward\"); }\n.bi-skip-end-btn-fill::before { content: map-get($bootstrap-icons-map, \"skip-end-btn-fill\"); }\n.bi-skip-end-btn::before { content: map-get($bootstrap-icons-map, \"skip-end-btn\"); }\n.bi-skip-end-circle-fill::before { content: map-get($bootstrap-icons-map, \"skip-end-circle-fill\"); }\n.bi-skip-end-circle::before { content: map-get($bootstrap-icons-map, \"skip-end-circle\"); }\n.bi-skip-end-fill::before { content: map-get($bootstrap-icons-map, \"skip-end-fill\"); }\n.bi-skip-end::before { content: map-get($bootstrap-icons-map, \"skip-end\"); }\n.bi-skip-forward-btn-fill::before { content: map-get($bootstrap-icons-map, \"skip-forward-btn-fill\"); }\n.bi-skip-forward-btn::before { content: map-get($bootstrap-icons-map, \"skip-forward-btn\"); }\n.bi-skip-forward-circle-fill::before { content: map-get($bootstrap-icons-map, \"skip-forward-circle-fill\"); }\n.bi-skip-forward-circle::before { content: map-get($bootstrap-icons-map, \"skip-forward-circle\"); }\n.bi-skip-forward-fill::before { content: map-get($bootstrap-icons-map, \"skip-forward-fill\"); }\n.bi-skip-forward::before { content: map-get($bootstrap-icons-map, \"skip-forward\"); }\n.bi-skip-start-btn-fill::before { content: map-get($bootstrap-icons-map, \"skip-start-btn-fill\"); }\n.bi-skip-start-btn::before { content: map-get($bootstrap-icons-map, \"skip-start-btn\"); }\n.bi-skip-start-circle-fill::before { content: map-get($bootstrap-icons-map, \"skip-start-circle-fill\"); }\n.bi-skip-start-circle::before { content: map-get($bootstrap-icons-map, \"skip-start-circle\"); }\n.bi-skip-start-fill::before { content: map-get($bootstrap-icons-map, \"skip-start-fill\"); }\n.bi-skip-start::before { content: map-get($bootstrap-icons-map, \"skip-start\"); }\n.bi-slack::before { content: map-get($bootstrap-icons-map, \"slack\"); }\n.bi-slash-circle-fill::before { content: map-get($bootstrap-icons-map, \"slash-circle-fill\"); }\n.bi-slash-circle::before { content: map-get($bootstrap-icons-map, \"slash-circle\"); }\n.bi-slash-square-fill::before { content: map-get($bootstrap-icons-map, \"slash-square-fill\"); }\n.bi-slash-square::before { content: map-get($bootstrap-icons-map, \"slash-square\"); }\n.bi-slash::before { content: map-get($bootstrap-icons-map, \"slash\"); }\n.bi-sliders::before { content: map-get($bootstrap-icons-map, \"sliders\"); }\n.bi-smartwatch::before { content: map-get($bootstrap-icons-map, \"smartwatch\"); }\n.bi-snow::before { content: map-get($bootstrap-icons-map, \"snow\"); }\n.bi-snow2::before { content: map-get($bootstrap-icons-map, \"snow2\"); }\n.bi-snow3::before { content: map-get($bootstrap-icons-map, \"snow3\"); }\n.bi-sort-alpha-down-alt::before { content: map-get($bootstrap-icons-map, \"sort-alpha-down-alt\"); }\n.bi-sort-alpha-down::before { content: map-get($bootstrap-icons-map, \"sort-alpha-down\"); }\n.bi-sort-alpha-up-alt::before { content: map-get($bootstrap-icons-map, \"sort-alpha-up-alt\"); }\n.bi-sort-alpha-up::before { content: map-get($bootstrap-icons-map, \"sort-alpha-up\"); }\n.bi-sort-down-alt::before { content: map-get($bootstrap-icons-map, \"sort-down-alt\"); }\n.bi-sort-down::before { content: map-get($bootstrap-icons-map, \"sort-down\"); }\n.bi-sort-numeric-down-alt::before { content: map-get($bootstrap-icons-map, \"sort-numeric-down-alt\"); }\n.bi-sort-numeric-down::before { content: map-get($bootstrap-icons-map, \"sort-numeric-down\"); }\n.bi-sort-numeric-up-alt::before { content: map-get($bootstrap-icons-map, \"sort-numeric-up-alt\"); }\n.bi-sort-numeric-up::before { content: map-get($bootstrap-icons-map, \"sort-numeric-up\"); }\n.bi-sort-up-alt::before { content: map-get($bootstrap-icons-map, \"sort-up-alt\"); }\n.bi-sort-up::before { content: map-get($bootstrap-icons-map, \"sort-up\"); }\n.bi-soundwave::before { content: map-get($bootstrap-icons-map, \"soundwave\"); }\n.bi-speaker-fill::before { content: map-get($bootstrap-icons-map, \"speaker-fill\"); }\n.bi-speaker::before { content: map-get($bootstrap-icons-map, \"speaker\"); }\n.bi-speedometer::before { content: map-get($bootstrap-icons-map, \"speedometer\"); }\n.bi-speedometer2::before { content: map-get($bootstrap-icons-map, \"speedometer2\"); }\n.bi-spellcheck::before { content: map-get($bootstrap-icons-map, \"spellcheck\"); }\n.bi-square-fill::before { content: map-get($bootstrap-icons-map, \"square-fill\"); }\n.bi-square-half::before { content: map-get($bootstrap-icons-map, \"square-half\"); }\n.bi-square::before { content: map-get($bootstrap-icons-map, \"square\"); }\n.bi-stack::before { content: map-get($bootstrap-icons-map, \"stack\"); }\n.bi-star-fill::before { content: map-get($bootstrap-icons-map, \"star-fill\"); }\n.bi-star-half::before { content: map-get($bootstrap-icons-map, \"star-half\"); }\n.bi-star::before { content: map-get($bootstrap-icons-map, \"star\"); }\n.bi-stars::before { content: map-get($bootstrap-icons-map, \"stars\"); }\n.bi-stickies-fill::before { content: map-get($bootstrap-icons-map, \"stickies-fill\"); }\n.bi-stickies::before { content: map-get($bootstrap-icons-map, \"stickies\"); }\n.bi-sticky-fill::before { content: map-get($bootstrap-icons-map, \"sticky-fill\"); }\n.bi-sticky::before { content: map-get($bootstrap-icons-map, \"sticky\"); }\n.bi-stop-btn-fill::before { content: map-get($bootstrap-icons-map, \"stop-btn-fill\"); }\n.bi-stop-btn::before { content: map-get($bootstrap-icons-map, \"stop-btn\"); }\n.bi-stop-circle-fill::before { content: map-get($bootstrap-icons-map, \"stop-circle-fill\"); }\n.bi-stop-circle::before { content: map-get($bootstrap-icons-map, \"stop-circle\"); }\n.bi-stop-fill::before { content: map-get($bootstrap-icons-map, \"stop-fill\"); }\n.bi-stop::before { content: map-get($bootstrap-icons-map, \"stop\"); }\n.bi-stoplights-fill::before { content: map-get($bootstrap-icons-map, \"stoplights-fill\"); }\n.bi-stoplights::before { content: map-get($bootstrap-icons-map, \"stoplights\"); }\n.bi-stopwatch-fill::before { content: map-get($bootstrap-icons-map, \"stopwatch-fill\"); }\n.bi-stopwatch::before { content: map-get($bootstrap-icons-map, \"stopwatch\"); }\n.bi-subtract::before { content: map-get($bootstrap-icons-map, \"subtract\"); }\n.bi-suit-club-fill::before { content: map-get($bootstrap-icons-map, \"suit-club-fill\"); }\n.bi-suit-club::before { content: map-get($bootstrap-icons-map, \"suit-club\"); }\n.bi-suit-diamond-fill::before { content: map-get($bootstrap-icons-map, \"suit-diamond-fill\"); }\n.bi-suit-diamond::before { content: map-get($bootstrap-icons-map, \"suit-diamond\"); }\n.bi-suit-heart-fill::before { content: map-get($bootstrap-icons-map, \"suit-heart-fill\"); }\n.bi-suit-heart::before { content: map-get($bootstrap-icons-map, \"suit-heart\"); }\n.bi-suit-spade-fill::before { content: map-get($bootstrap-icons-map, \"suit-spade-fill\"); }\n.bi-suit-spade::before { content: map-get($bootstrap-icons-map, \"suit-spade\"); }\n.bi-sun-fill::before { content: map-get($bootstrap-icons-map, \"sun-fill\"); }\n.bi-sun::before { content: map-get($bootstrap-icons-map, \"sun\"); }\n.bi-sunglasses::before { content: map-get($bootstrap-icons-map, \"sunglasses\"); }\n.bi-sunrise-fill::before { content: map-get($bootstrap-icons-map, \"sunrise-fill\"); }\n.bi-sunrise::before { content: map-get($bootstrap-icons-map, \"sunrise\"); }\n.bi-sunset-fill::before { content: map-get($bootstrap-icons-map, \"sunset-fill\"); }\n.bi-sunset::before { content: map-get($bootstrap-icons-map, \"sunset\"); }\n.bi-symmetry-horizontal::before { content: map-get($bootstrap-icons-map, \"symmetry-horizontal\"); }\n.bi-symmetry-vertical::before { content: map-get($bootstrap-icons-map, \"symmetry-vertical\"); }\n.bi-table::before { content: map-get($bootstrap-icons-map, \"table\"); }\n.bi-tablet-fill::before { content: map-get($bootstrap-icons-map, \"tablet-fill\"); }\n.bi-tablet-landscape-fill::before { content: map-get($bootstrap-icons-map, \"tablet-landscape-fill\"); }\n.bi-tablet-landscape::before { content: map-get($bootstrap-icons-map, \"tablet-landscape\"); }\n.bi-tablet::before { content: map-get($bootstrap-icons-map, \"tablet\"); }\n.bi-tag-fill::before { content: map-get($bootstrap-icons-map, \"tag-fill\"); }\n.bi-tag::before { content: map-get($bootstrap-icons-map, \"tag\"); }\n.bi-tags-fill::before { content: map-get($bootstrap-icons-map, \"tags-fill\"); }\n.bi-tags::before { content: map-get($bootstrap-icons-map, \"tags\"); }\n.bi-telegram::before { content: map-get($bootstrap-icons-map, \"telegram\"); }\n.bi-telephone-fill::before { content: map-get($bootstrap-icons-map, \"telephone-fill\"); }\n.bi-telephone-forward-fill::before { content: map-get($bootstrap-icons-map, \"telephone-forward-fill\"); }\n.bi-telephone-forward::before { content: map-get($bootstrap-icons-map, \"telephone-forward\"); }\n.bi-telephone-inbound-fill::before { content: map-get($bootstrap-icons-map, \"telephone-inbound-fill\"); }\n.bi-telephone-inbound::before { content: map-get($bootstrap-icons-map, \"telephone-inbound\"); }\n.bi-telephone-minus-fill::before { content: map-get($bootstrap-icons-map, \"telephone-minus-fill\"); }\n.bi-telephone-minus::before { content: map-get($bootstrap-icons-map, \"telephone-minus\"); }\n.bi-telephone-outbound-fill::before { content: map-get($bootstrap-icons-map, \"telephone-outbound-fill\"); }\n.bi-telephone-outbound::before { content: map-get($bootstrap-icons-map, \"telephone-outbound\"); }\n.bi-telephone-plus-fill::before { content: map-get($bootstrap-icons-map, \"telephone-plus-fill\"); }\n.bi-telephone-plus::before { content: map-get($bootstrap-icons-map, \"telephone-plus\"); }\n.bi-telephone-x-fill::before { content: map-get($bootstrap-icons-map, \"telephone-x-fill\"); }\n.bi-telephone-x::before { content: map-get($bootstrap-icons-map, \"telephone-x\"); }\n.bi-telephone::before { content: map-get($bootstrap-icons-map, \"telephone\"); }\n.bi-terminal-fill::before { content: map-get($bootstrap-icons-map, \"terminal-fill\"); }\n.bi-terminal::before { content: map-get($bootstrap-icons-map, \"terminal\"); }\n.bi-text-center::before { content: map-get($bootstrap-icons-map, \"text-center\"); }\n.bi-text-indent-left::before { content: map-get($bootstrap-icons-map, \"text-indent-left\"); }\n.bi-text-indent-right::before { content: map-get($bootstrap-icons-map, \"text-indent-right\"); }\n.bi-text-left::before { content: map-get($bootstrap-icons-map, \"text-left\"); }\n.bi-text-paragraph::before { content: map-get($bootstrap-icons-map, \"text-paragraph\"); }\n.bi-text-right::before { content: map-get($bootstrap-icons-map, \"text-right\"); }\n.bi-textarea-resize::before { content: map-get($bootstrap-icons-map, \"textarea-resize\"); }\n.bi-textarea-t::before { content: map-get($bootstrap-icons-map, \"textarea-t\"); }\n.bi-textarea::before { content: map-get($bootstrap-icons-map, \"textarea\"); }\n.bi-thermometer-half::before { content: map-get($bootstrap-icons-map, \"thermometer-half\"); }\n.bi-thermometer-high::before { content: map-get($bootstrap-icons-map, \"thermometer-high\"); }\n.bi-thermometer-low::before { content: map-get($bootstrap-icons-map, \"thermometer-low\"); }\n.bi-thermometer-snow::before { content: map-get($bootstrap-icons-map, \"thermometer-snow\"); }\n.bi-thermometer-sun::before { content: map-get($bootstrap-icons-map, \"thermometer-sun\"); }\n.bi-thermometer::before { content: map-get($bootstrap-icons-map, \"thermometer\"); }\n.bi-three-dots-vertical::before { content: map-get($bootstrap-icons-map, \"three-dots-vertical\"); }\n.bi-three-dots::before { content: map-get($bootstrap-icons-map, \"three-dots\"); }\n.bi-toggle-off::before { content: map-get($bootstrap-icons-map, \"toggle-off\"); }\n.bi-toggle-on::before { content: map-get($bootstrap-icons-map, \"toggle-on\"); }\n.bi-toggle2-off::before { content: map-get($bootstrap-icons-map, \"toggle2-off\"); }\n.bi-toggle2-on::before { content: map-get($bootstrap-icons-map, \"toggle2-on\"); }\n.bi-toggles::before { content: map-get($bootstrap-icons-map, \"toggles\"); }\n.bi-toggles2::before { content: map-get($bootstrap-icons-map, \"toggles2\"); }\n.bi-tools::before { content: map-get($bootstrap-icons-map, \"tools\"); }\n.bi-tornado::before { content: map-get($bootstrap-icons-map, \"tornado\"); }\n.bi-trash-fill::before { content: map-get($bootstrap-icons-map, \"trash-fill\"); }\n.bi-trash::before { content: map-get($bootstrap-icons-map, \"trash\"); }\n.bi-trash2-fill::before { content: map-get($bootstrap-icons-map, \"trash2-fill\"); }\n.bi-trash2::before { content: map-get($bootstrap-icons-map, \"trash2\"); }\n.bi-tree-fill::before { content: map-get($bootstrap-icons-map, \"tree-fill\"); }\n.bi-tree::before { content: map-get($bootstrap-icons-map, \"tree\"); }\n.bi-triangle-fill::before { content: map-get($bootstrap-icons-map, \"triangle-fill\"); }\n.bi-triangle-half::before { content: map-get($bootstrap-icons-map, \"triangle-half\"); }\n.bi-triangle::before { content: map-get($bootstrap-icons-map, \"triangle\"); }\n.bi-trophy-fill::before { content: map-get($bootstrap-icons-map, \"trophy-fill\"); }\n.bi-trophy::before { content: map-get($bootstrap-icons-map, \"trophy\"); }\n.bi-tropical-storm::before { content: map-get($bootstrap-icons-map, \"tropical-storm\"); }\n.bi-truck-flatbed::before { content: map-get($bootstrap-icons-map, \"truck-flatbed\"); }\n.bi-truck::before { content: map-get($bootstrap-icons-map, \"truck\"); }\n.bi-tsunami::before { content: map-get($bootstrap-icons-map, \"tsunami\"); }\n.bi-tv-fill::before { content: map-get($bootstrap-icons-map, \"tv-fill\"); }\n.bi-tv::before { content: map-get($bootstrap-icons-map, \"tv\"); }\n.bi-twitch::before { content: map-get($bootstrap-icons-map, \"twitch\"); }\n.bi-twitter::before { content: map-get($bootstrap-icons-map, \"twitter\"); }\n.bi-type-bold::before { content: map-get($bootstrap-icons-map, \"type-bold\"); }\n.bi-type-h1::before { content: map-get($bootstrap-icons-map, \"type-h1\"); }\n.bi-type-h2::before { content: map-get($bootstrap-icons-map, \"type-h2\"); }\n.bi-type-h3::before { content: map-get($bootstrap-icons-map, \"type-h3\"); }\n.bi-type-italic::before { content: map-get($bootstrap-icons-map, \"type-italic\"); }\n.bi-type-strikethrough::before { content: map-get($bootstrap-icons-map, \"type-strikethrough\"); }\n.bi-type-underline::before { content: map-get($bootstrap-icons-map, \"type-underline\"); }\n.bi-type::before { content: map-get($bootstrap-icons-map, \"type\"); }\n.bi-ui-checks-grid::before { content: map-get($bootstrap-icons-map, \"ui-checks-grid\"); }\n.bi-ui-checks::before { content: map-get($bootstrap-icons-map, \"ui-checks\"); }\n.bi-ui-radios-grid::before { content: map-get($bootstrap-icons-map, \"ui-radios-grid\"); }\n.bi-ui-radios::before { content: map-get($bootstrap-icons-map, \"ui-radios\"); }\n.bi-umbrella-fill::before { content: map-get($bootstrap-icons-map, \"umbrella-fill\"); }\n.bi-umbrella::before { content: map-get($bootstrap-icons-map, \"umbrella\"); }\n.bi-union::before { content: map-get($bootstrap-icons-map, \"union\"); }\n.bi-unlock-fill::before { content: map-get($bootstrap-icons-map, \"unlock-fill\"); }\n.bi-unlock::before { content: map-get($bootstrap-icons-map, \"unlock\"); }\n.bi-upc-scan::before { content: map-get($bootstrap-icons-map, \"upc-scan\"); }\n.bi-upc::before { content: map-get($bootstrap-icons-map, \"upc\"); }\n.bi-upload::before { content: map-get($bootstrap-icons-map, \"upload\"); }\n.bi-vector-pen::before { content: map-get($bootstrap-icons-map, \"vector-pen\"); }\n.bi-view-list::before { content: map-get($bootstrap-icons-map, \"view-list\"); }\n.bi-view-stacked::before { content: map-get($bootstrap-icons-map, \"view-stacked\"); }\n.bi-vinyl-fill::before { content: map-get($bootstrap-icons-map, \"vinyl-fill\"); }\n.bi-vinyl::before { content: map-get($bootstrap-icons-map, \"vinyl\"); }\n.bi-voicemail::before { content: map-get($bootstrap-icons-map, \"voicemail\"); }\n.bi-volume-down-fill::before { content: map-get($bootstrap-icons-map, \"volume-down-fill\"); }\n.bi-volume-down::before { content: map-get($bootstrap-icons-map, \"volume-down\"); }\n.bi-volume-mute-fill::before { content: map-get($bootstrap-icons-map, \"volume-mute-fill\"); }\n.bi-volume-mute::before { content: map-get($bootstrap-icons-map, \"volume-mute\"); }\n.bi-volume-off-fill::before { content: map-get($bootstrap-icons-map, \"volume-off-fill\"); }\n.bi-volume-off::before { content: map-get($bootstrap-icons-map, \"volume-off\"); }\n.bi-volume-up-fill::before { content: map-get($bootstrap-icons-map, \"volume-up-fill\"); }\n.bi-volume-up::before { content: map-get($bootstrap-icons-map, \"volume-up\"); }\n.bi-vr::before { content: map-get($bootstrap-icons-map, \"vr\"); }\n.bi-wallet-fill::before { content: map-get($bootstrap-icons-map, \"wallet-fill\"); }\n.bi-wallet::before { content: map-get($bootstrap-icons-map, \"wallet\"); }\n.bi-wallet2::before { content: map-get($bootstrap-icons-map, \"wallet2\"); }\n.bi-watch::before { content: map-get($bootstrap-icons-map, \"watch\"); }\n.bi-water::before { content: map-get($bootstrap-icons-map, \"water\"); }\n.bi-whatsapp::before { content: map-get($bootstrap-icons-map, \"whatsapp\"); }\n.bi-wifi-1::before { content: map-get($bootstrap-icons-map, \"wifi-1\"); }\n.bi-wifi-2::before { content: map-get($bootstrap-icons-map, \"wifi-2\"); }\n.bi-wifi-off::before { content: map-get($bootstrap-icons-map, \"wifi-off\"); }\n.bi-wifi::before { content: map-get($bootstrap-icons-map, \"wifi\"); }\n.bi-wind::before { content: map-get($bootstrap-icons-map, \"wind\"); }\n.bi-window-dock::before { content: map-get($bootstrap-icons-map, \"window-dock\"); }\n.bi-window-sidebar::before { content: map-get($bootstrap-icons-map, \"window-sidebar\"); }\n.bi-window::before { content: map-get($bootstrap-icons-map, \"window\"); }\n.bi-wrench::before { content: map-get($bootstrap-icons-map, \"wrench\"); }\n.bi-x-circle-fill::before { content: map-get($bootstrap-icons-map, \"x-circle-fill\"); }\n.bi-x-circle::before { content: map-get($bootstrap-icons-map, \"x-circle\"); }\n.bi-x-diamond-fill::before { content: map-get($bootstrap-icons-map, \"x-diamond-fill\"); }\n.bi-x-diamond::before { content: map-get($bootstrap-icons-map, \"x-diamond\"); }\n.bi-x-octagon-fill::before { content: map-get($bootstrap-icons-map, \"x-octagon-fill\"); }\n.bi-x-octagon::before { content: map-get($bootstrap-icons-map, \"x-octagon\"); }\n.bi-x-square-fill::before { content: map-get($bootstrap-icons-map, \"x-square-fill\"); }\n.bi-x-square::before { content: map-get($bootstrap-icons-map, \"x-square\"); }\n.bi-x::before { content: map-get($bootstrap-icons-map, \"x\"); }\n.bi-youtube::before { content: map-get($bootstrap-icons-map, \"youtube\"); }\n.bi-zoom-in::before { content: map-get($bootstrap-icons-map, \"zoom-in\"); }\n.bi-zoom-out::before { content: map-get($bootstrap-icons-map, \"zoom-out\"); }\n.bi-bank::before { content: map-get($bootstrap-icons-map, \"bank\"); }\n.bi-bank2::before { content: map-get($bootstrap-icons-map, \"bank2\"); }\n.bi-bell-slash-fill::before { content: map-get($bootstrap-icons-map, \"bell-slash-fill\"); }\n.bi-bell-slash::before { content: map-get($bootstrap-icons-map, \"bell-slash\"); }\n.bi-cash-coin::before { content: map-get($bootstrap-icons-map, \"cash-coin\"); }\n.bi-check-lg::before { content: map-get($bootstrap-icons-map, \"check-lg\"); }\n.bi-coin::before { content: map-get($bootstrap-icons-map, \"coin\"); }\n.bi-currency-bitcoin::before { content: map-get($bootstrap-icons-map, \"currency-bitcoin\"); }\n.bi-currency-dollar::before { content: map-get($bootstrap-icons-map, \"currency-dollar\"); }\n.bi-currency-euro::before { content: map-get($bootstrap-icons-map, \"currency-euro\"); }\n.bi-currency-exchange::before { content: map-get($bootstrap-icons-map, \"currency-exchange\"); }\n.bi-currency-pound::before { content: map-get($bootstrap-icons-map, \"currency-pound\"); }\n.bi-currency-yen::before { content: map-get($bootstrap-icons-map, \"currency-yen\"); }\n.bi-dash-lg::before { content: map-get($bootstrap-icons-map, \"dash-lg\"); }\n.bi-exclamation-lg::before { content: map-get($bootstrap-icons-map, \"exclamation-lg\"); }\n.bi-file-earmark-pdf-fill::before { content: map-get($bootstrap-icons-map, \"file-earmark-pdf-fill\"); }\n.bi-file-earmark-pdf::before { content: map-get($bootstrap-icons-map, \"file-earmark-pdf\"); }\n.bi-file-pdf-fill::before { content: map-get($bootstrap-icons-map, \"file-pdf-fill\"); }\n.bi-file-pdf::before { content: map-get($bootstrap-icons-map, \"file-pdf\"); }\n.bi-gender-ambiguous::before { content: map-get($bootstrap-icons-map, \"gender-ambiguous\"); }\n.bi-gender-female::before { content: map-get($bootstrap-icons-map, \"gender-female\"); }\n.bi-gender-male::before { content: map-get($bootstrap-icons-map, \"gender-male\"); }\n.bi-gender-trans::before { content: map-get($bootstrap-icons-map, \"gender-trans\"); }\n.bi-headset-vr::before { content: map-get($bootstrap-icons-map, \"headset-vr\"); }\n.bi-info-lg::before { content: map-get($bootstrap-icons-map, \"info-lg\"); }\n.bi-mastodon::before { content: map-get($bootstrap-icons-map, \"mastodon\"); }\n.bi-messenger::before { content: map-get($bootstrap-icons-map, \"messenger\"); }\n.bi-piggy-bank-fill::before { content: map-get($bootstrap-icons-map, \"piggy-bank-fill\"); }\n.bi-piggy-bank::before { content: map-get($bootstrap-icons-map, \"piggy-bank\"); }\n.bi-pin-map-fill::before { content: map-get($bootstrap-icons-map, \"pin-map-fill\"); }\n.bi-pin-map::before { content: map-get($bootstrap-icons-map, \"pin-map\"); }\n.bi-plus-lg::before { content: map-get($bootstrap-icons-map, \"plus-lg\"); }\n.bi-question-lg::before { content: map-get($bootstrap-icons-map, \"question-lg\"); }\n.bi-recycle::before { content: map-get($bootstrap-icons-map, \"recycle\"); }\n.bi-reddit::before { content: map-get($bootstrap-icons-map, \"reddit\"); }\n.bi-safe-fill::before { content: map-get($bootstrap-icons-map, \"safe-fill\"); }\n.bi-safe2-fill::before { content: map-get($bootstrap-icons-map, \"safe2-fill\"); }\n.bi-safe2::before { content: map-get($bootstrap-icons-map, \"safe2\"); }\n.bi-sd-card-fill::before { content: map-get($bootstrap-icons-map, \"sd-card-fill\"); }\n.bi-sd-card::before { content: map-get($bootstrap-icons-map, \"sd-card\"); }\n.bi-skype::before { content: map-get($bootstrap-icons-map, \"skype\"); }\n.bi-slash-lg::before { content: map-get($bootstrap-icons-map, \"slash-lg\"); }\n.bi-translate::before { content: map-get($bootstrap-icons-map, \"translate\"); }\n.bi-x-lg::before { content: map-get($bootstrap-icons-map, \"x-lg\"); }\n.bi-safe::before { content: map-get($bootstrap-icons-map, \"safe\"); }\n.bi-apple::before { content: map-get($bootstrap-icons-map, \"apple\"); }\n.bi-microsoft::before { content: map-get($bootstrap-icons-map, \"microsoft\"); }\n.bi-windows::before { content: map-get($bootstrap-icons-map, \"windows\"); }\n.bi-behance::before { content: map-get($bootstrap-icons-map, \"behance\"); }\n.bi-dribbble::before { content: map-get($bootstrap-icons-map, \"dribbble\"); }\n.bi-line::before { content: map-get($bootstrap-icons-map, \"line\"); }\n.bi-medium::before { content: map-get($bootstrap-icons-map, \"medium\"); }\n.bi-paypal::before { content: map-get($bootstrap-icons-map, \"paypal\"); }\n.bi-pinterest::before { content: map-get($bootstrap-icons-map, \"pinterest\"); }\n.bi-signal::before { content: map-get($bootstrap-icons-map, \"signal\"); }\n.bi-snapchat::before { content: map-get($bootstrap-icons-map, \"snapchat\"); }\n.bi-spotify::before { content: map-get($bootstrap-icons-map, \"spotify\"); }\n.bi-stack-overflow::before { content: map-get($bootstrap-icons-map, \"stack-overflow\"); }\n.bi-strava::before { content: map-get($bootstrap-icons-map, \"strava\"); }\n.bi-wordpress::before { content: map-get($bootstrap-icons-map, \"wordpress\"); }\n.bi-vimeo::before { content: map-get($bootstrap-icons-map, \"vimeo\"); }\n.bi-activity::before { content: map-get($bootstrap-icons-map, \"activity\"); }\n.bi-easel2-fill::before { content: map-get($bootstrap-icons-map, \"easel2-fill\"); }\n.bi-easel2::before { content: map-get($bootstrap-icons-map, \"easel2\"); }\n.bi-easel3-fill::before { content: map-get($bootstrap-icons-map, \"easel3-fill\"); }\n.bi-easel3::before { content: map-get($bootstrap-icons-map, \"easel3\"); }\n.bi-fan::before { content: map-get($bootstrap-icons-map, \"fan\"); }\n.bi-fingerprint::before { content: map-get($bootstrap-icons-map, \"fingerprint\"); }\n.bi-graph-down-arrow::before { content: map-get($bootstrap-icons-map, \"graph-down-arrow\"); }\n.bi-graph-up-arrow::before { content: map-get($bootstrap-icons-map, \"graph-up-arrow\"); }\n.bi-hypnotize::before { content: map-get($bootstrap-icons-map, \"hypnotize\"); }\n.bi-magic::before { content: map-get($bootstrap-icons-map, \"magic\"); }\n.bi-person-rolodex::before { content: map-get($bootstrap-icons-map, \"person-rolodex\"); }\n.bi-person-video::before { content: map-get($bootstrap-icons-map, \"person-video\"); }\n.bi-person-video2::before { content: map-get($bootstrap-icons-map, \"person-video2\"); }\n.bi-person-video3::before { content: map-get($bootstrap-icons-map, \"person-video3\"); }\n.bi-person-workspace::before { content: map-get($bootstrap-icons-map, \"person-workspace\"); }\n.bi-radioactive::before { content: map-get($bootstrap-icons-map, \"radioactive\"); }\n.bi-webcam-fill::before { content: map-get($bootstrap-icons-map, \"webcam-fill\"); }\n.bi-webcam::before { content: map-get($bootstrap-icons-map, \"webcam\"); }\n.bi-yin-yang::before { content: map-get($bootstrap-icons-map, \"yin-yang\"); }\n.bi-bandaid-fill::before { content: map-get($bootstrap-icons-map, \"bandaid-fill\"); }\n.bi-bandaid::before { content: map-get($bootstrap-icons-map, \"bandaid\"); }\n.bi-bluetooth::before { content: map-get($bootstrap-icons-map, \"bluetooth\"); }\n.bi-body-text::before { content: map-get($bootstrap-icons-map, \"body-text\"); }\n.bi-boombox::before { content: map-get($bootstrap-icons-map, \"boombox\"); }\n.bi-boxes::before { content: map-get($bootstrap-icons-map, \"boxes\"); }\n.bi-dpad-fill::before { content: map-get($bootstrap-icons-map, \"dpad-fill\"); }\n.bi-dpad::before { content: map-get($bootstrap-icons-map, \"dpad\"); }\n.bi-ear-fill::before { content: map-get($bootstrap-icons-map, \"ear-fill\"); }\n.bi-ear::before { content: map-get($bootstrap-icons-map, \"ear\"); }\n.bi-envelope-check-1::before { content: map-get($bootstrap-icons-map, \"envelope-check-1\"); }\n.bi-envelope-check-fill::before { content: map-get($bootstrap-icons-map, \"envelope-check-fill\"); }\n.bi-envelope-check::before { content: map-get($bootstrap-icons-map, \"envelope-check\"); }\n.bi-envelope-dash-1::before { content: map-get($bootstrap-icons-map, \"envelope-dash-1\"); }\n.bi-envelope-dash-fill::before { content: map-get($bootstrap-icons-map, \"envelope-dash-fill\"); }\n.bi-envelope-dash::before { content: map-get($bootstrap-icons-map, \"envelope-dash\"); }\n.bi-envelope-exclamation-1::before { content: map-get($bootstrap-icons-map, \"envelope-exclamation-1\"); }\n.bi-envelope-exclamation-fill::before { content: map-get($bootstrap-icons-map, \"envelope-exclamation-fill\"); }\n.bi-envelope-exclamation::before { content: map-get($bootstrap-icons-map, \"envelope-exclamation\"); }\n.bi-envelope-plus-fill::before { content: map-get($bootstrap-icons-map, \"envelope-plus-fill\"); }\n.bi-envelope-plus::before { content: map-get($bootstrap-icons-map, \"envelope-plus\"); }\n.bi-envelope-slash-1::before { content: map-get($bootstrap-icons-map, \"envelope-slash-1\"); }\n.bi-envelope-slash-fill::before { content: map-get($bootstrap-icons-map, \"envelope-slash-fill\"); }\n.bi-envelope-slash::before { content: map-get($bootstrap-icons-map, \"envelope-slash\"); }\n.bi-envelope-x-1::before { content: map-get($bootstrap-icons-map, \"envelope-x-1\"); }\n.bi-envelope-x-fill::before { content: map-get($bootstrap-icons-map, \"envelope-x-fill\"); }\n.bi-envelope-x::before { content: map-get($bootstrap-icons-map, \"envelope-x\"); }\n.bi-explicit-fill::before { content: map-get($bootstrap-icons-map, \"explicit-fill\"); }\n.bi-explicit::before { content: map-get($bootstrap-icons-map, \"explicit\"); }\n.bi-git::before { content: map-get($bootstrap-icons-map, \"git\"); }\n.bi-infinity::before { content: map-get($bootstrap-icons-map, \"infinity\"); }\n.bi-list-columns-reverse::before { content: map-get($bootstrap-icons-map, \"list-columns-reverse\"); }\n.bi-list-columns::before { content: map-get($bootstrap-icons-map, \"list-columns\"); }\n.bi-meta::before { content: map-get($bootstrap-icons-map, \"meta\"); }\n.bi-mortorboard-fill::before { content: map-get($bootstrap-icons-map, \"mortorboard-fill\"); }\n.bi-mortorboard::before { content: map-get($bootstrap-icons-map, \"mortorboard\"); }\n.bi-nintendo-switch::before { content: map-get($bootstrap-icons-map, \"nintendo-switch\"); }\n.bi-pc-display-horizontal::before { content: map-get($bootstrap-icons-map, \"pc-display-horizontal\"); }\n.bi-pc-display::before { content: map-get($bootstrap-icons-map, \"pc-display\"); }\n.bi-pc-horizontal::before { content: map-get($bootstrap-icons-map, \"pc-horizontal\"); }\n.bi-pc::before { content: map-get($bootstrap-icons-map, \"pc\"); }\n.bi-playstation::before { content: map-get($bootstrap-icons-map, \"playstation\"); }\n.bi-plus-slash-minus::before { content: map-get($bootstrap-icons-map, \"plus-slash-minus\"); }\n.bi-projector-fill::before { content: map-get($bootstrap-icons-map, \"projector-fill\"); }\n.bi-projector::before { content: map-get($bootstrap-icons-map, \"projector\"); }\n.bi-qr-code-scan::before { content: map-get($bootstrap-icons-map, \"qr-code-scan\"); }\n.bi-qr-code::before { content: map-get($bootstrap-icons-map, \"qr-code\"); }\n.bi-quora::before { content: map-get($bootstrap-icons-map, \"quora\"); }\n.bi-quote::before { content: map-get($bootstrap-icons-map, \"quote\"); }\n.bi-robot::before { content: map-get($bootstrap-icons-map, \"robot\"); }\n.bi-send-check-fill::before { content: map-get($bootstrap-icons-map, \"send-check-fill\"); }\n.bi-send-check::before { content: map-get($bootstrap-icons-map, \"send-check\"); }\n.bi-send-dash-fill::before { content: map-get($bootstrap-icons-map, \"send-dash-fill\"); }\n.bi-send-dash::before { content: map-get($bootstrap-icons-map, \"send-dash\"); }\n.bi-send-exclamation-1::before { content: map-get($bootstrap-icons-map, \"send-exclamation-1\"); }\n.bi-send-exclamation-fill::before { content: map-get($bootstrap-icons-map, \"send-exclamation-fill\"); }\n.bi-send-exclamation::before { content: map-get($bootstrap-icons-map, \"send-exclamation\"); }\n.bi-send-fill::before { content: map-get($bootstrap-icons-map, \"send-fill\"); }\n.bi-send-plus-fill::before { content: map-get($bootstrap-icons-map, \"send-plus-fill\"); }\n.bi-send-plus::before { content: map-get($bootstrap-icons-map, \"send-plus\"); }\n.bi-send-slash-fill::before { content: map-get($bootstrap-icons-map, \"send-slash-fill\"); }\n.bi-send-slash::before { content: map-get($bootstrap-icons-map, \"send-slash\"); }\n.bi-send-x-fill::before { content: map-get($bootstrap-icons-map, \"send-x-fill\"); }\n.bi-send-x::before { content: map-get($bootstrap-icons-map, \"send-x\"); }\n.bi-send::before { content: map-get($bootstrap-icons-map, \"send\"); }\n.bi-steam::before { content: map-get($bootstrap-icons-map, \"steam\"); }\n.bi-terminal-dash-1::before { content: map-get($bootstrap-icons-map, \"terminal-dash-1\"); }\n.bi-terminal-dash::before { content: map-get($bootstrap-icons-map, \"terminal-dash\"); }\n.bi-terminal-plus::before { content: map-get($bootstrap-icons-map, \"terminal-plus\"); }\n.bi-terminal-split::before { content: map-get($bootstrap-icons-map, \"terminal-split\"); }\n.bi-ticket-detailed-fill::before { content: map-get($bootstrap-icons-map, \"ticket-detailed-fill\"); }\n.bi-ticket-detailed::before { content: map-get($bootstrap-icons-map, \"ticket-detailed\"); }\n.bi-ticket-fill::before { content: map-get($bootstrap-icons-map, \"ticket-fill\"); }\n.bi-ticket-perforated-fill::before { content: map-get($bootstrap-icons-map, \"ticket-perforated-fill\"); }\n.bi-ticket-perforated::before { content: map-get($bootstrap-icons-map, \"ticket-perforated\"); }\n.bi-ticket::before { content: map-get($bootstrap-icons-map, \"ticket\"); }\n.bi-tiktok::before { content: map-get($bootstrap-icons-map, \"tiktok\"); }\n.bi-window-dash::before { content: map-get($bootstrap-icons-map, \"window-dash\"); }\n.bi-window-desktop::before { content: map-get($bootstrap-icons-map, \"window-desktop\"); }\n.bi-window-fullscreen::before { content: map-get($bootstrap-icons-map, \"window-fullscreen\"); }\n.bi-window-plus::before { content: map-get($bootstrap-icons-map, \"window-plus\"); }\n.bi-window-split::before { content: map-get($bootstrap-icons-map, \"window-split\"); }\n.bi-window-stack::before { content: map-get($bootstrap-icons-map, \"window-stack\"); }\n.bi-window-x::before { content: map-get($bootstrap-icons-map, \"window-x\"); }\n.bi-xbox::before { content: map-get($bootstrap-icons-map, \"xbox\"); }\n.bi-ethernet::before { content: map-get($bootstrap-icons-map, \"ethernet\"); }\n.bi-hdmi-fill::before { content: map-get($bootstrap-icons-map, \"hdmi-fill\"); }\n.bi-hdmi::before { content: map-get($bootstrap-icons-map, \"hdmi\"); }\n.bi-usb-c-fill::before { content: map-get($bootstrap-icons-map, \"usb-c-fill\"); }\n.bi-usb-c::before { content: map-get($bootstrap-icons-map, \"usb-c\"); }\n.bi-usb-fill::before { content: map-get($bootstrap-icons-map, \"usb-fill\"); }\n.bi-usb-plug-fill::before { content: map-get($bootstrap-icons-map, \"usb-plug-fill\"); }\n.bi-usb-plug::before { content: map-get($bootstrap-icons-map, \"usb-plug\"); }\n.bi-usb-symbol::before { content: map-get($bootstrap-icons-map, \"usb-symbol\"); }\n.bi-usb::before { content: map-get($bootstrap-icons-map, \"usb\"); }\n.bi-boombox-fill::before { content: map-get($bootstrap-icons-map, \"boombox-fill\"); }\n.bi-displayport-1::before { content: map-get($bootstrap-icons-map, \"displayport-1\"); }\n.bi-displayport::before { content: map-get($bootstrap-icons-map, \"displayport\"); }\n.bi-gpu-card::before { content: map-get($bootstrap-icons-map, \"gpu-card\"); }\n.bi-memory::before { content: map-get($bootstrap-icons-map, \"memory\"); }\n.bi-modem-fill::before { content: map-get($bootstrap-icons-map, \"modem-fill\"); }\n.bi-modem::before { content: map-get($bootstrap-icons-map, \"modem\"); }\n.bi-motherboard-fill::before { content: map-get($bootstrap-icons-map, \"motherboard-fill\"); }\n.bi-motherboard::before { content: map-get($bootstrap-icons-map, \"motherboard\"); }\n.bi-optical-audio-fill::before { content: map-get($bootstrap-icons-map, \"optical-audio-fill\"); }\n.bi-optical-audio::before { content: map-get($bootstrap-icons-map, \"optical-audio\"); }\n.bi-pci-card::before { content: map-get($bootstrap-icons-map, \"pci-card\"); }\n.bi-router-fill::before { content: map-get($bootstrap-icons-map, \"router-fill\"); }\n.bi-router::before { content: map-get($bootstrap-icons-map, \"router\"); }\n.bi-ssd-fill::before { content: map-get($bootstrap-icons-map, \"ssd-fill\"); }\n.bi-ssd::before { content: map-get($bootstrap-icons-map, \"ssd\"); }\n.bi-thunderbolt-fill::before { content: map-get($bootstrap-icons-map, \"thunderbolt-fill\"); }\n.bi-thunderbolt::before { content: map-get($bootstrap-icons-map, \"thunderbolt\"); }\n.bi-usb-drive-fill::before { content: map-get($bootstrap-icons-map, \"usb-drive-fill\"); }\n.bi-usb-drive::before { content: map-get($bootstrap-icons-map, \"usb-drive\"); }\n.bi-usb-micro-fill::before { content: map-get($bootstrap-icons-map, \"usb-micro-fill\"); }\n.bi-usb-micro::before { content: map-get($bootstrap-icons-map, \"usb-micro\"); }\n.bi-usb-mini-fill::before { content: map-get($bootstrap-icons-map, \"usb-mini-fill\"); }\n.bi-usb-mini::before { content: map-get($bootstrap-icons-map, \"usb-mini\"); }\n.bi-cloud-haze2::before { content: map-get($bootstrap-icons-map, \"cloud-haze2\"); }\n.bi-device-hdd-fill::before { content: map-get($bootstrap-icons-map, \"device-hdd-fill\"); }\n.bi-device-hdd::before { content: map-get($bootstrap-icons-map, \"device-hdd\"); }\n.bi-device-ssd-fill::before { content: map-get($bootstrap-icons-map, \"device-ssd-fill\"); }\n.bi-device-ssd::before { content: map-get($bootstrap-icons-map, \"device-ssd\"); }\n.bi-displayport-fill::before { content: map-get($bootstrap-icons-map, \"displayport-fill\"); }\n.bi-mortarboard-fill::before { content: map-get($bootstrap-icons-map, \"mortarboard-fill\"); }\n.bi-mortarboard::before { content: map-get($bootstrap-icons-map, \"mortarboard\"); }\n.bi-terminal-x::before { content: map-get($bootstrap-icons-map, \"terminal-x\"); }\n.bi-arrow-through-heart-fill::before { content: map-get($bootstrap-icons-map, \"arrow-through-heart-fill\"); }\n.bi-arrow-through-heart::before { content: map-get($bootstrap-icons-map, \"arrow-through-heart\"); }\n.bi-badge-sd-fill::before { content: map-get($bootstrap-icons-map, \"badge-sd-fill\"); }\n.bi-badge-sd::before { content: map-get($bootstrap-icons-map, \"badge-sd\"); }\n.bi-bag-heart-fill::before { content: map-get($bootstrap-icons-map, \"bag-heart-fill\"); }\n.bi-bag-heart::before { content: map-get($bootstrap-icons-map, \"bag-heart\"); }\n.bi-balloon-fill::before { content: map-get($bootstrap-icons-map, \"balloon-fill\"); }\n.bi-balloon-heart-fill::before { content: map-get($bootstrap-icons-map, \"balloon-heart-fill\"); }\n.bi-balloon-heart::before { content: map-get($bootstrap-icons-map, \"balloon-heart\"); }\n.bi-balloon::before { content: map-get($bootstrap-icons-map, \"balloon\"); }\n.bi-box2-fill::before { content: map-get($bootstrap-icons-map, \"box2-fill\"); }\n.bi-box2-heart-fill::before { content: map-get($bootstrap-icons-map, \"box2-heart-fill\"); }\n.bi-box2-heart::before { content: map-get($bootstrap-icons-map, \"box2-heart\"); }\n.bi-box2::before { content: map-get($bootstrap-icons-map, \"box2\"); }\n.bi-braces-asterisk::before { content: map-get($bootstrap-icons-map, \"braces-asterisk\"); }\n.bi-calendar-heart-fill::before { content: map-get($bootstrap-icons-map, \"calendar-heart-fill\"); }\n.bi-calendar-heart::before { content: map-get($bootstrap-icons-map, \"calendar-heart\"); }\n.bi-calendar2-heart-fill::before { content: map-get($bootstrap-icons-map, \"calendar2-heart-fill\"); }\n.bi-calendar2-heart::before { content: map-get($bootstrap-icons-map, \"calendar2-heart\"); }\n.bi-chat-heart-fill::before { content: map-get($bootstrap-icons-map, \"chat-heart-fill\"); }\n.bi-chat-heart::before { content: map-get($bootstrap-icons-map, \"chat-heart\"); }\n.bi-chat-left-heart-fill::before { content: map-get($bootstrap-icons-map, \"chat-left-heart-fill\"); }\n.bi-chat-left-heart::before { content: map-get($bootstrap-icons-map, \"chat-left-heart\"); }\n.bi-chat-right-heart-fill::before { content: map-get($bootstrap-icons-map, \"chat-right-heart-fill\"); }\n.bi-chat-right-heart::before { content: map-get($bootstrap-icons-map, \"chat-right-heart\"); }\n.bi-chat-square-heart-fill::before { content: map-get($bootstrap-icons-map, \"chat-square-heart-fill\"); }\n.bi-chat-square-heart::before { content: map-get($bootstrap-icons-map, \"chat-square-heart\"); }\n.bi-clipboard-check-fill::before { content: map-get($bootstrap-icons-map, \"clipboard-check-fill\"); }\n.bi-clipboard-data-fill::before { content: map-get($bootstrap-icons-map, \"clipboard-data-fill\"); }\n.bi-clipboard-fill::before { content: map-get($bootstrap-icons-map, \"clipboard-fill\"); }\n.bi-clipboard-heart-fill::before { content: map-get($bootstrap-icons-map, \"clipboard-heart-fill\"); }\n.bi-clipboard-heart::before { content: map-get($bootstrap-icons-map, \"clipboard-heart\"); }\n.bi-clipboard-minus-fill::before { content: map-get($bootstrap-icons-map, \"clipboard-minus-fill\"); }\n.bi-clipboard-plus-fill::before { content: map-get($bootstrap-icons-map, \"clipboard-plus-fill\"); }\n.bi-clipboard-pulse::before { content: map-get($bootstrap-icons-map, \"clipboard-pulse\"); }\n.bi-clipboard-x-fill::before { content: map-get($bootstrap-icons-map, \"clipboard-x-fill\"); }\n.bi-clipboard2-check-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-check-fill\"); }\n.bi-clipboard2-check::before { content: map-get($bootstrap-icons-map, \"clipboard2-check\"); }\n.bi-clipboard2-data-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-data-fill\"); }\n.bi-clipboard2-data::before { content: map-get($bootstrap-icons-map, \"clipboard2-data\"); }\n.bi-clipboard2-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-fill\"); }\n.bi-clipboard2-heart-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-heart-fill\"); }\n.bi-clipboard2-heart::before { content: map-get($bootstrap-icons-map, \"clipboard2-heart\"); }\n.bi-clipboard2-minus-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-minus-fill\"); }\n.bi-clipboard2-minus::before { content: map-get($bootstrap-icons-map, \"clipboard2-minus\"); }\n.bi-clipboard2-plus-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-plus-fill\"); }\n.bi-clipboard2-plus::before { content: map-get($bootstrap-icons-map, \"clipboard2-plus\"); }\n.bi-clipboard2-pulse-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-pulse-fill\"); }\n.bi-clipboard2-pulse::before { content: map-get($bootstrap-icons-map, \"clipboard2-pulse\"); }\n.bi-clipboard2-x-fill::before { content: map-get($bootstrap-icons-map, \"clipboard2-x-fill\"); }\n.bi-clipboard2-x::before { content: map-get($bootstrap-icons-map, \"clipboard2-x\"); }\n.bi-clipboard2::before { content: map-get($bootstrap-icons-map, \"clipboard2\"); }\n.bi-emoji-kiss-fill::before { content: map-get($bootstrap-icons-map, \"emoji-kiss-fill\"); }\n.bi-emoji-kiss::before { content: map-get($bootstrap-icons-map, \"emoji-kiss\"); }\n.bi-envelope-heart-fill::before { content: map-get($bootstrap-icons-map, \"envelope-heart-fill\"); }\n.bi-envelope-heart::before { content: map-get($bootstrap-icons-map, \"envelope-heart\"); }\n.bi-envelope-open-heart-fill::before { content: map-get($bootstrap-icons-map, \"envelope-open-heart-fill\"); }\n.bi-envelope-open-heart::before { content: map-get($bootstrap-icons-map, \"envelope-open-heart\"); }\n.bi-envelope-paper-fill::before { content: map-get($bootstrap-icons-map, \"envelope-paper-fill\"); }\n.bi-envelope-paper-heart-fill::before { content: map-get($bootstrap-icons-map, \"envelope-paper-heart-fill\"); }\n.bi-envelope-paper-heart::before { content: map-get($bootstrap-icons-map, \"envelope-paper-heart\"); }\n.bi-envelope-paper::before { content: map-get($bootstrap-icons-map, \"envelope-paper\"); }\n.bi-filetype-aac::before { content: map-get($bootstrap-icons-map, \"filetype-aac\"); }\n.bi-filetype-ai::before { content: map-get($bootstrap-icons-map, \"filetype-ai\"); }\n.bi-filetype-bmp::before { content: map-get($bootstrap-icons-map, \"filetype-bmp\"); }\n.bi-filetype-cs::before { content: map-get($bootstrap-icons-map, \"filetype-cs\"); }\n.bi-filetype-css::before { content: map-get($bootstrap-icons-map, \"filetype-css\"); }\n.bi-filetype-csv::before { content: map-get($bootstrap-icons-map, \"filetype-csv\"); }\n.bi-filetype-doc::before { content: map-get($bootstrap-icons-map, \"filetype-doc\"); }\n.bi-filetype-docx::before { content: map-get($bootstrap-icons-map, \"filetype-docx\"); }\n.bi-filetype-exe::before { content: map-get($bootstrap-icons-map, \"filetype-exe\"); }\n.bi-filetype-gif::before { content: map-get($bootstrap-icons-map, \"filetype-gif\"); }\n.bi-filetype-heic::before { content: map-get($bootstrap-icons-map, \"filetype-heic\"); }\n.bi-filetype-html::before { content: map-get($bootstrap-icons-map, \"filetype-html\"); }\n.bi-filetype-java::before { content: map-get($bootstrap-icons-map, \"filetype-java\"); }\n.bi-filetype-jpg::before { content: map-get($bootstrap-icons-map, \"filetype-jpg\"); }\n.bi-filetype-js::before { content: map-get($bootstrap-icons-map, \"filetype-js\"); }\n.bi-filetype-jsx::before { content: map-get($bootstrap-icons-map, \"filetype-jsx\"); }\n.bi-filetype-key::before { content: map-get($bootstrap-icons-map, \"filetype-key\"); }\n.bi-filetype-m4p::before { content: map-get($bootstrap-icons-map, \"filetype-m4p\"); }\n.bi-filetype-md::before { content: map-get($bootstrap-icons-map, \"filetype-md\"); }\n.bi-filetype-mdx::before { content: map-get($bootstrap-icons-map, \"filetype-mdx\"); }\n.bi-filetype-mov::before { content: map-get($bootstrap-icons-map, \"filetype-mov\"); }\n.bi-filetype-mp3::before { content: map-get($bootstrap-icons-map, \"filetype-mp3\"); }\n.bi-filetype-mp4::before { content: map-get($bootstrap-icons-map, \"filetype-mp4\"); }\n.bi-filetype-otf::before { content: map-get($bootstrap-icons-map, \"filetype-otf\"); }\n.bi-filetype-pdf::before { content: map-get($bootstrap-icons-map, \"filetype-pdf\"); }\n.bi-filetype-php::before { content: map-get($bootstrap-icons-map, \"filetype-php\"); }\n.bi-filetype-png::before { content: map-get($bootstrap-icons-map, \"filetype-png\"); }\n.bi-filetype-ppt-1::before { content: map-get($bootstrap-icons-map, \"filetype-ppt-1\"); }\n.bi-filetype-ppt::before { content: map-get($bootstrap-icons-map, \"filetype-ppt\"); }\n.bi-filetype-psd::before { content: map-get($bootstrap-icons-map, \"filetype-psd\"); }\n.bi-filetype-py::before { content: map-get($bootstrap-icons-map, \"filetype-py\"); }\n.bi-filetype-raw::before { content: map-get($bootstrap-icons-map, \"filetype-raw\"); }\n.bi-filetype-rb::before { content: map-get($bootstrap-icons-map, \"filetype-rb\"); }\n.bi-filetype-sass::before { content: map-get($bootstrap-icons-map, \"filetype-sass\"); }\n.bi-filetype-scss::before { content: map-get($bootstrap-icons-map, \"filetype-scss\"); }\n.bi-filetype-sh::before { content: map-get($bootstrap-icons-map, \"filetype-sh\"); }\n.bi-filetype-svg::before { content: map-get($bootstrap-icons-map, \"filetype-svg\"); }\n.bi-filetype-tiff::before { content: map-get($bootstrap-icons-map, \"filetype-tiff\"); }\n.bi-filetype-tsx::before { content: map-get($bootstrap-icons-map, \"filetype-tsx\"); }\n.bi-filetype-ttf::before { content: map-get($bootstrap-icons-map, \"filetype-ttf\"); }\n.bi-filetype-txt::before { content: map-get($bootstrap-icons-map, \"filetype-txt\"); }\n.bi-filetype-wav::before { content: map-get($bootstrap-icons-map, \"filetype-wav\"); }\n.bi-filetype-woff::before { content: map-get($bootstrap-icons-map, \"filetype-woff\"); }\n.bi-filetype-xls-1::before { content: map-get($bootstrap-icons-map, \"filetype-xls-1\"); }\n.bi-filetype-xls::before { content: map-get($bootstrap-icons-map, \"filetype-xls\"); }\n.bi-filetype-xml::before { content: map-get($bootstrap-icons-map, \"filetype-xml\"); }\n.bi-filetype-yml::before { content: map-get($bootstrap-icons-map, \"filetype-yml\"); }\n.bi-heart-arrow::before { content: map-get($bootstrap-icons-map, \"heart-arrow\"); }\n.bi-heart-pulse-fill::before { content: map-get($bootstrap-icons-map, \"heart-pulse-fill\"); }\n.bi-heart-pulse::before { content: map-get($bootstrap-icons-map, \"heart-pulse\"); }\n.bi-heartbreak-fill::before { content: map-get($bootstrap-icons-map, \"heartbreak-fill\"); }\n.bi-heartbreak::before { content: map-get($bootstrap-icons-map, \"heartbreak\"); }\n.bi-hearts::before { content: map-get($bootstrap-icons-map, \"hearts\"); }\n.bi-hospital-fill::before { content: map-get($bootstrap-icons-map, \"hospital-fill\"); }\n.bi-hospital::before { content: map-get($bootstrap-icons-map, \"hospital\"); }\n.bi-house-heart-fill::before { content: map-get($bootstrap-icons-map, \"house-heart-fill\"); }\n.bi-house-heart::before { content: map-get($bootstrap-icons-map, \"house-heart\"); }\n.bi-incognito::before { content: map-get($bootstrap-icons-map, \"incognito\"); }\n.bi-magnet-fill::before { content: map-get($bootstrap-icons-map, \"magnet-fill\"); }\n.bi-magnet::before { content: map-get($bootstrap-icons-map, \"magnet\"); }\n.bi-person-heart::before { content: map-get($bootstrap-icons-map, \"person-heart\"); }\n.bi-person-hearts::before { content: map-get($bootstrap-icons-map, \"person-hearts\"); }\n.bi-phone-flip::before { content: map-get($bootstrap-icons-map, \"phone-flip\"); }\n.bi-plugin::before { content: map-get($bootstrap-icons-map, \"plugin\"); }\n.bi-postage-fill::before { content: map-get($bootstrap-icons-map, \"postage-fill\"); }\n.bi-postage-heart-fill::before { content: map-get($bootstrap-icons-map, \"postage-heart-fill\"); }\n.bi-postage-heart::before { content: map-get($bootstrap-icons-map, \"postage-heart\"); }\n.bi-postage::before { content: map-get($bootstrap-icons-map, \"postage\"); }\n.bi-postcard-fill::before { content: map-get($bootstrap-icons-map, \"postcard-fill\"); }\n.bi-postcard-heart-fill::before { content: map-get($bootstrap-icons-map, \"postcard-heart-fill\"); }\n.bi-postcard-heart::before { content: map-get($bootstrap-icons-map, \"postcard-heart\"); }\n.bi-postcard::before { content: map-get($bootstrap-icons-map, \"postcard\"); }\n.bi-search-heart-fill::before { content: map-get($bootstrap-icons-map, \"search-heart-fill\"); }\n.bi-search-heart::before { content: map-get($bootstrap-icons-map, \"search-heart\"); }\n.bi-sliders2-vertical::before { content: map-get($bootstrap-icons-map, \"sliders2-vertical\"); }\n.bi-sliders2::before { content: map-get($bootstrap-icons-map, \"sliders2\"); }\n.bi-trash3-fill::before { content: map-get($bootstrap-icons-map, \"trash3-fill\"); }\n.bi-trash3::before { content: map-get($bootstrap-icons-map, \"trash3\"); }\n.bi-valentine::before { content: map-get($bootstrap-icons-map, \"valentine\"); }\n.bi-valentine2::before { content: map-get($bootstrap-icons-map, \"valentine2\"); }\n.bi-wrench-adjustable-circle-fill::before { content: map-get($bootstrap-icons-map, \"wrench-adjustable-circle-fill\"); }\n.bi-wrench-adjustable-circle::before { content: map-get($bootstrap-icons-map, \"wrench-adjustable-circle\"); }\n.bi-wrench-adjustable::before { content: map-get($bootstrap-icons-map, \"wrench-adjustable\"); }\n.bi-filetype-json::before { content: map-get($bootstrap-icons-map, \"filetype-json\"); }\n.bi-filetype-pptx::before { content: map-get($bootstrap-icons-map, \"filetype-pptx\"); }\n.bi-filetype-xlsx::before { content: map-get($bootstrap-icons-map, \"filetype-xlsx\"); }\n.bi-1-circle-1::before { content: map-get($bootstrap-icons-map, \"1-circle-1\"); }\n.bi-1-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"1-circle-fill-1\"); }\n.bi-1-circle-fill::before { content: map-get($bootstrap-icons-map, \"1-circle-fill\"); }\n.bi-1-circle::before { content: map-get($bootstrap-icons-map, \"1-circle\"); }\n.bi-1-square-fill::before { content: map-get($bootstrap-icons-map, \"1-square-fill\"); }\n.bi-1-square::before { content: map-get($bootstrap-icons-map, \"1-square\"); }\n.bi-2-circle-1::before { content: map-get($bootstrap-icons-map, \"2-circle-1\"); }\n.bi-2-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"2-circle-fill-1\"); }\n.bi-2-circle-fill::before { content: map-get($bootstrap-icons-map, \"2-circle-fill\"); }\n.bi-2-circle::before { content: map-get($bootstrap-icons-map, \"2-circle\"); }\n.bi-2-square-fill::before { content: map-get($bootstrap-icons-map, \"2-square-fill\"); }\n.bi-2-square::before { content: map-get($bootstrap-icons-map, \"2-square\"); }\n.bi-3-circle-1::before { content: map-get($bootstrap-icons-map, \"3-circle-1\"); }\n.bi-3-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"3-circle-fill-1\"); }\n.bi-3-circle-fill::before { content: map-get($bootstrap-icons-map, \"3-circle-fill\"); }\n.bi-3-circle::before { content: map-get($bootstrap-icons-map, \"3-circle\"); }\n.bi-3-square-fill::before { content: map-get($bootstrap-icons-map, \"3-square-fill\"); }\n.bi-3-square::before { content: map-get($bootstrap-icons-map, \"3-square\"); }\n.bi-4-circle-1::before { content: map-get($bootstrap-icons-map, \"4-circle-1\"); }\n.bi-4-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"4-circle-fill-1\"); }\n.bi-4-circle-fill::before { content: map-get($bootstrap-icons-map, \"4-circle-fill\"); }\n.bi-4-circle::before { content: map-get($bootstrap-icons-map, \"4-circle\"); }\n.bi-4-square-fill::before { content: map-get($bootstrap-icons-map, \"4-square-fill\"); }\n.bi-4-square::before { content: map-get($bootstrap-icons-map, \"4-square\"); }\n.bi-5-circle-1::before { content: map-get($bootstrap-icons-map, \"5-circle-1\"); }\n.bi-5-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"5-circle-fill-1\"); }\n.bi-5-circle-fill::before { content: map-get($bootstrap-icons-map, \"5-circle-fill\"); }\n.bi-5-circle::before { content: map-get($bootstrap-icons-map, \"5-circle\"); }\n.bi-5-square-fill::before { content: map-get($bootstrap-icons-map, \"5-square-fill\"); }\n.bi-5-square::before { content: map-get($bootstrap-icons-map, \"5-square\"); }\n.bi-6-circle-1::before { content: map-get($bootstrap-icons-map, \"6-circle-1\"); }\n.bi-6-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"6-circle-fill-1\"); }\n.bi-6-circle-fill::before { content: map-get($bootstrap-icons-map, \"6-circle-fill\"); }\n.bi-6-circle::before { content: map-get($bootstrap-icons-map, \"6-circle\"); }\n.bi-6-square-fill::before { content: map-get($bootstrap-icons-map, \"6-square-fill\"); }\n.bi-6-square::before { content: map-get($bootstrap-icons-map, \"6-square\"); }\n.bi-7-circle-1::before { content: map-get($bootstrap-icons-map, \"7-circle-1\"); }\n.bi-7-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"7-circle-fill-1\"); }\n.bi-7-circle-fill::before { content: map-get($bootstrap-icons-map, \"7-circle-fill\"); }\n.bi-7-circle::before { content: map-get($bootstrap-icons-map, \"7-circle\"); }\n.bi-7-square-fill::before { content: map-get($bootstrap-icons-map, \"7-square-fill\"); }\n.bi-7-square::before { content: map-get($bootstrap-icons-map, \"7-square\"); }\n.bi-8-circle-1::before { content: map-get($bootstrap-icons-map, \"8-circle-1\"); }\n.bi-8-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"8-circle-fill-1\"); }\n.bi-8-circle-fill::before { content: map-get($bootstrap-icons-map, \"8-circle-fill\"); }\n.bi-8-circle::before { content: map-get($bootstrap-icons-map, \"8-circle\"); }\n.bi-8-square-fill::before { content: map-get($bootstrap-icons-map, \"8-square-fill\"); }\n.bi-8-square::before { content: map-get($bootstrap-icons-map, \"8-square\"); }\n.bi-9-circle-1::before { content: map-get($bootstrap-icons-map, \"9-circle-1\"); }\n.bi-9-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"9-circle-fill-1\"); }\n.bi-9-circle-fill::before { content: map-get($bootstrap-icons-map, \"9-circle-fill\"); }\n.bi-9-circle::before { content: map-get($bootstrap-icons-map, \"9-circle\"); }\n.bi-9-square-fill::before { content: map-get($bootstrap-icons-map, \"9-square-fill\"); }\n.bi-9-square::before { content: map-get($bootstrap-icons-map, \"9-square\"); }\n.bi-airplane-engines-fill::before { content: map-get($bootstrap-icons-map, \"airplane-engines-fill\"); }\n.bi-airplane-engines::before { content: map-get($bootstrap-icons-map, \"airplane-engines\"); }\n.bi-airplane-fill::before { content: map-get($bootstrap-icons-map, \"airplane-fill\"); }\n.bi-airplane::before { content: map-get($bootstrap-icons-map, \"airplane\"); }\n.bi-alexa::before { content: map-get($bootstrap-icons-map, \"alexa\"); }\n.bi-alipay::before { content: map-get($bootstrap-icons-map, \"alipay\"); }\n.bi-android::before { content: map-get($bootstrap-icons-map, \"android\"); }\n.bi-android2::before { content: map-get($bootstrap-icons-map, \"android2\"); }\n.bi-box-fill::before { content: map-get($bootstrap-icons-map, \"box-fill\"); }\n.bi-box-seam-fill::before { content: map-get($bootstrap-icons-map, \"box-seam-fill\"); }\n.bi-browser-chrome::before { content: map-get($bootstrap-icons-map, \"browser-chrome\"); }\n.bi-browser-edge::before { content: map-get($bootstrap-icons-map, \"browser-edge\"); }\n.bi-browser-firefox::before { content: map-get($bootstrap-icons-map, \"browser-firefox\"); }\n.bi-browser-safari::before { content: map-get($bootstrap-icons-map, \"browser-safari\"); }\n.bi-c-circle-1::before { content: map-get($bootstrap-icons-map, \"c-circle-1\"); }\n.bi-c-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"c-circle-fill-1\"); }\n.bi-c-circle-fill::before { content: map-get($bootstrap-icons-map, \"c-circle-fill\"); }\n.bi-c-circle::before { content: map-get($bootstrap-icons-map, \"c-circle\"); }\n.bi-c-square-fill::before { content: map-get($bootstrap-icons-map, \"c-square-fill\"); }\n.bi-c-square::before { content: map-get($bootstrap-icons-map, \"c-square\"); }\n.bi-capsule-pill::before { content: map-get($bootstrap-icons-map, \"capsule-pill\"); }\n.bi-capsule::before { content: map-get($bootstrap-icons-map, \"capsule\"); }\n.bi-car-front-fill::before { content: map-get($bootstrap-icons-map, \"car-front-fill\"); }\n.bi-car-front::before { content: map-get($bootstrap-icons-map, \"car-front\"); }\n.bi-cassette-fill::before { content: map-get($bootstrap-icons-map, \"cassette-fill\"); }\n.bi-cassette::before { content: map-get($bootstrap-icons-map, \"cassette\"); }\n.bi-cc-circle-1::before { content: map-get($bootstrap-icons-map, \"cc-circle-1\"); }\n.bi-cc-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"cc-circle-fill-1\"); }\n.bi-cc-circle-fill::before { content: map-get($bootstrap-icons-map, \"cc-circle-fill\"); }\n.bi-cc-circle::before { content: map-get($bootstrap-icons-map, \"cc-circle\"); }\n.bi-cc-square-fill::before { content: map-get($bootstrap-icons-map, \"cc-square-fill\"); }\n.bi-cc-square::before { content: map-get($bootstrap-icons-map, \"cc-square\"); }\n.bi-cup-hot-fill::before { content: map-get($bootstrap-icons-map, \"cup-hot-fill\"); }\n.bi-cup-hot::before { content: map-get($bootstrap-icons-map, \"cup-hot\"); }\n.bi-currency-rupee::before { content: map-get($bootstrap-icons-map, \"currency-rupee\"); }\n.bi-dropbox::before { content: map-get($bootstrap-icons-map, \"dropbox\"); }\n.bi-escape::before { content: map-get($bootstrap-icons-map, \"escape\"); }\n.bi-fast-forward-btn-fill::before { content: map-get($bootstrap-icons-map, \"fast-forward-btn-fill\"); }\n.bi-fast-forward-btn::before { content: map-get($bootstrap-icons-map, \"fast-forward-btn\"); }\n.bi-fast-forward-circle-fill::before { content: map-get($bootstrap-icons-map, \"fast-forward-circle-fill\"); }\n.bi-fast-forward-circle::before { content: map-get($bootstrap-icons-map, \"fast-forward-circle\"); }\n.bi-fast-forward-fill::before { content: map-get($bootstrap-icons-map, \"fast-forward-fill\"); }\n.bi-fast-forward::before { content: map-get($bootstrap-icons-map, \"fast-forward\"); }\n.bi-filetype-sql::before { content: map-get($bootstrap-icons-map, \"filetype-sql\"); }\n.bi-fire::before { content: map-get($bootstrap-icons-map, \"fire\"); }\n.bi-google-play::before { content: map-get($bootstrap-icons-map, \"google-play\"); }\n.bi-h-circle-1::before { content: map-get($bootstrap-icons-map, \"h-circle-1\"); }\n.bi-h-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"h-circle-fill-1\"); }\n.bi-h-circle-fill::before { content: map-get($bootstrap-icons-map, \"h-circle-fill\"); }\n.bi-h-circle::before { content: map-get($bootstrap-icons-map, \"h-circle\"); }\n.bi-h-square-fill::before { content: map-get($bootstrap-icons-map, \"h-square-fill\"); }\n.bi-h-square::before { content: map-get($bootstrap-icons-map, \"h-square\"); }\n.bi-indent::before { content: map-get($bootstrap-icons-map, \"indent\"); }\n.bi-lungs-fill::before { content: map-get($bootstrap-icons-map, \"lungs-fill\"); }\n.bi-lungs::before { content: map-get($bootstrap-icons-map, \"lungs\"); }\n.bi-microsoft-teams::before { content: map-get($bootstrap-icons-map, \"microsoft-teams\"); }\n.bi-p-circle-1::before { content: map-get($bootstrap-icons-map, \"p-circle-1\"); }\n.bi-p-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"p-circle-fill-1\"); }\n.bi-p-circle-fill::before { content: map-get($bootstrap-icons-map, \"p-circle-fill\"); }\n.bi-p-circle::before { content: map-get($bootstrap-icons-map, \"p-circle\"); }\n.bi-p-square-fill::before { content: map-get($bootstrap-icons-map, \"p-square-fill\"); }\n.bi-p-square::before { content: map-get($bootstrap-icons-map, \"p-square\"); }\n.bi-pass-fill::before { content: map-get($bootstrap-icons-map, \"pass-fill\"); }\n.bi-pass::before { content: map-get($bootstrap-icons-map, \"pass\"); }\n.bi-prescription::before { content: map-get($bootstrap-icons-map, \"prescription\"); }\n.bi-prescription2::before { content: map-get($bootstrap-icons-map, \"prescription2\"); }\n.bi-r-circle-1::before { content: map-get($bootstrap-icons-map, \"r-circle-1\"); }\n.bi-r-circle-fill-1::before { content: map-get($bootstrap-icons-map, \"r-circle-fill-1\"); }\n.bi-r-circle-fill::before { content: map-get($bootstrap-icons-map, \"r-circle-fill\"); }\n.bi-r-circle::before { content: map-get($bootstrap-icons-map, \"r-circle\"); }\n.bi-r-square-fill::before { content: map-get($bootstrap-icons-map, \"r-square-fill\"); }\n.bi-r-square::before { content: map-get($bootstrap-icons-map, \"r-square\"); }\n.bi-repeat-1::before { content: map-get($bootstrap-icons-map, \"repeat-1\"); }\n.bi-repeat::before { content: map-get($bootstrap-icons-map, \"repeat\"); }\n.bi-rewind-btn-fill::before { content: map-get($bootstrap-icons-map, \"rewind-btn-fill\"); }\n.bi-rewind-btn::before { content: map-get($bootstrap-icons-map, \"rewind-btn\"); }\n.bi-rewind-circle-fill::before { content: map-get($bootstrap-icons-map, \"rewind-circle-fill\"); }\n.bi-rewind-circle::before { content: map-get($bootstrap-icons-map, \"rewind-circle\"); }\n.bi-rewind-fill::before { content: map-get($bootstrap-icons-map, \"rewind-fill\"); }\n.bi-rewind::before { content: map-get($bootstrap-icons-map, \"rewind\"); }\n.bi-train-freight-front-fill::before { content: map-get($bootstrap-icons-map, \"train-freight-front-fill\"); }\n.bi-train-freight-front::before { content: map-get($bootstrap-icons-map, \"train-freight-front\"); }\n.bi-train-front-fill::before { content: map-get($bootstrap-icons-map, \"train-front-fill\"); }\n.bi-train-front::before { content: map-get($bootstrap-icons-map, \"train-front\"); }\n.bi-train-lightrail-front-fill::before { content: map-get($bootstrap-icons-map, \"train-lightrail-front-fill\"); }\n.bi-train-lightrail-front::before { content: map-get($bootstrap-icons-map, \"train-lightrail-front\"); }\n.bi-truck-front-fill::before { content: map-get($bootstrap-icons-map, \"truck-front-fill\"); }\n.bi-truck-front::before { content: map-get($bootstrap-icons-map, \"truck-front\"); }\n.bi-ubuntu::before { content: map-get($bootstrap-icons-map, \"ubuntu\"); }\n.bi-unindent::before { content: map-get($bootstrap-icons-map, \"unindent\"); }\n.bi-unity::before { content: map-get($bootstrap-icons-map, \"unity\"); }\n.bi-universal-access-circle::before { content: map-get($bootstrap-icons-map, \"universal-access-circle\"); }\n.bi-universal-access::before { content: map-get($bootstrap-icons-map, \"universal-access\"); }\n.bi-virus::before { content: map-get($bootstrap-icons-map, \"virus\"); }\n.bi-virus2::before { content: map-get($bootstrap-icons-map, \"virus2\"); }\n.bi-wechat::before { content: map-get($bootstrap-icons-map, \"wechat\"); }\n.bi-yelp::before { content: map-get($bootstrap-icons-map, \"yelp\"); }\n.bi-sign-stop-fill::before { content: map-get($bootstrap-icons-map, \"sign-stop-fill\"); }\n.bi-sign-stop-lights-fill::before { content: map-get($bootstrap-icons-map, \"sign-stop-lights-fill\"); }\n.bi-sign-stop-lights::before { content: map-get($bootstrap-icons-map, \"sign-stop-lights\"); }\n.bi-sign-stop::before { content: map-get($bootstrap-icons-map, \"sign-stop\"); }\n.bi-sign-turn-left-fill::before { content: map-get($bootstrap-icons-map, \"sign-turn-left-fill\"); }\n.bi-sign-turn-left::before { content: map-get($bootstrap-icons-map, \"sign-turn-left\"); }\n.bi-sign-turn-right-fill::before { content: map-get($bootstrap-icons-map, \"sign-turn-right-fill\"); }\n.bi-sign-turn-right::before { content: map-get($bootstrap-icons-map, \"sign-turn-right\"); }\n.bi-sign-turn-slight-left-fill::before { content: map-get($bootstrap-icons-map, \"sign-turn-slight-left-fill\"); }\n.bi-sign-turn-slight-left::before { content: map-get($bootstrap-icons-map, \"sign-turn-slight-left\"); }\n.bi-sign-turn-slight-right-fill::before { content: map-get($bootstrap-icons-map, \"sign-turn-slight-right-fill\"); }\n.bi-sign-turn-slight-right::before { content: map-get($bootstrap-icons-map, \"sign-turn-slight-right\"); }\n.bi-sign-yield-fill::before { content: map-get($bootstrap-icons-map, \"sign-yield-fill\"); }\n.bi-sign-yield::before { content: map-get($bootstrap-icons-map, \"sign-yield\"); }\n.bi-ev-station-fill::before { content: map-get($bootstrap-icons-map, \"ev-station-fill\"); }\n.bi-ev-station::before { content: map-get($bootstrap-icons-map, \"ev-station\"); }\n.bi-fuel-pump-diesel-fill::before { content: map-get($bootstrap-icons-map, \"fuel-pump-diesel-fill\"); }\n.bi-fuel-pump-diesel::before { content: map-get($bootstrap-icons-map, \"fuel-pump-diesel\"); }\n.bi-fuel-pump-fill::before { content: map-get($bootstrap-icons-map, \"fuel-pump-fill\"); }\n.bi-fuel-pump::before { content: map-get($bootstrap-icons-map, \"fuel-pump\"); }\n.bi-0-circle-fill::before { content: map-get($bootstrap-icons-map, \"0-circle-fill\"); }\n.bi-0-circle::before { content: map-get($bootstrap-icons-map, \"0-circle\"); }\n.bi-0-square-fill::before { content: map-get($bootstrap-icons-map, \"0-square-fill\"); }\n.bi-0-square::before { content: map-get($bootstrap-icons-map, \"0-square\"); }\n.bi-rocket-fill::before { content: map-get($bootstrap-icons-map, \"rocket-fill\"); }\n.bi-rocket-takeoff-fill::before { content: map-get($bootstrap-icons-map, \"rocket-takeoff-fill\"); }\n.bi-rocket-takeoff::before { content: map-get($bootstrap-icons-map, \"rocket-takeoff\"); }\n.bi-rocket::before { content: map-get($bootstrap-icons-map, \"rocket\"); }\n.bi-stripe::before { content: map-get($bootstrap-icons-map, \"stripe\"); }\n.bi-subscript::before { content: map-get($bootstrap-icons-map, \"subscript\"); }\n.bi-superscript::before { content: map-get($bootstrap-icons-map, \"superscript\"); }\n.bi-trello::before { content: map-get($bootstrap-icons-map, \"trello\"); }\n.bi-envelope-at-fill::before { content: map-get($bootstrap-icons-map, \"envelope-at-fill\"); }\n.bi-envelope-at::before { content: map-get($bootstrap-icons-map, \"envelope-at\"); }\n.bi-regex::before { content: map-get($bootstrap-icons-map, \"regex\"); }\n.bi-text-wrap::before { content: map-get($bootstrap-icons-map, \"text-wrap\"); }\n.bi-sign-dead-end-fill::before { content: map-get($bootstrap-icons-map, \"sign-dead-end-fill\"); }\n.bi-sign-dead-end::before { content: map-get($bootstrap-icons-map, \"sign-dead-end\"); }\n.bi-sign-do-not-enter-fill::before { content: map-get($bootstrap-icons-map, \"sign-do-not-enter-fill\"); }\n.bi-sign-do-not-enter::before { content: map-get($bootstrap-icons-map, \"sign-do-not-enter\"); }\n.bi-sign-intersection-fill::before { content: map-get($bootstrap-icons-map, \"sign-intersection-fill\"); }\n.bi-sign-intersection-side-fill::before { content: map-get($bootstrap-icons-map, \"sign-intersection-side-fill\"); }\n.bi-sign-intersection-side::before { content: map-get($bootstrap-icons-map, \"sign-intersection-side\"); }\n.bi-sign-intersection-t-fill::before { content: map-get($bootstrap-icons-map, \"sign-intersection-t-fill\"); }\n.bi-sign-intersection-t::before { content: map-get($bootstrap-icons-map, \"sign-intersection-t\"); }\n.bi-sign-intersection-y-fill::before { content: map-get($bootstrap-icons-map, \"sign-intersection-y-fill\"); }\n.bi-sign-intersection-y::before { content: map-get($bootstrap-icons-map, \"sign-intersection-y\"); }\n.bi-sign-intersection::before { content: map-get($bootstrap-icons-map, \"sign-intersection\"); }\n.bi-sign-merge-left-fill::before { content: map-get($bootstrap-icons-map, \"sign-merge-left-fill\"); }\n.bi-sign-merge-left::before { content: map-get($bootstrap-icons-map, \"sign-merge-left\"); }\n.bi-sign-merge-right-fill::before { content: map-get($bootstrap-icons-map, \"sign-merge-right-fill\"); }\n.bi-sign-merge-right::before { content: map-get($bootstrap-icons-map, \"sign-merge-right\"); }\n.bi-sign-no-left-turn-fill::before { content: map-get($bootstrap-icons-map, \"sign-no-left-turn-fill\"); }\n.bi-sign-no-left-turn::before { content: map-get($bootstrap-icons-map, \"sign-no-left-turn\"); }\n.bi-sign-no-parking-fill::before { content: map-get($bootstrap-icons-map, \"sign-no-parking-fill\"); }\n.bi-sign-no-parking::before { content: map-get($bootstrap-icons-map, \"sign-no-parking\"); }\n.bi-sign-no-right-turn-fill::before { content: map-get($bootstrap-icons-map, \"sign-no-right-turn-fill\"); }\n.bi-sign-no-right-turn::before { content: map-get($bootstrap-icons-map, \"sign-no-right-turn\"); }\n.bi-sign-railroad-fill::before { content: map-get($bootstrap-icons-map, \"sign-railroad-fill\"); }\n.bi-sign-railroad::before { content: map-get($bootstrap-icons-map, \"sign-railroad\"); }\n.bi-building-add::before { content: map-get($bootstrap-icons-map, \"building-add\"); }\n.bi-building-check::before { content: map-get($bootstrap-icons-map, \"building-check\"); }\n.bi-building-dash::before { content: map-get($bootstrap-icons-map, \"building-dash\"); }\n.bi-building-down::before { content: map-get($bootstrap-icons-map, \"building-down\"); }\n.bi-building-exclamation::before { content: map-get($bootstrap-icons-map, \"building-exclamation\"); }\n.bi-building-fill-add::before { content: map-get($bootstrap-icons-map, \"building-fill-add\"); }\n.bi-building-fill-check::before { content: map-get($bootstrap-icons-map, \"building-fill-check\"); }\n.bi-building-fill-dash::before { content: map-get($bootstrap-icons-map, \"building-fill-dash\"); }\n.bi-building-fill-down::before { content: map-get($bootstrap-icons-map, \"building-fill-down\"); }\n.bi-building-fill-exclamation::before { content: map-get($bootstrap-icons-map, \"building-fill-exclamation\"); }\n.bi-building-fill-gear::before { content: map-get($bootstrap-icons-map, \"building-fill-gear\"); }\n.bi-building-fill-lock::before { content: map-get($bootstrap-icons-map, \"building-fill-lock\"); }\n.bi-building-fill-slash::before { content: map-get($bootstrap-icons-map, \"building-fill-slash\"); }\n.bi-building-fill-up::before { content: map-get($bootstrap-icons-map, \"building-fill-up\"); }\n.bi-building-fill-x::before { content: map-get($bootstrap-icons-map, \"building-fill-x\"); }\n.bi-building-fill::before { content: map-get($bootstrap-icons-map, \"building-fill\"); }\n.bi-building-gear::before { content: map-get($bootstrap-icons-map, \"building-gear\"); }\n.bi-building-lock::before { content: map-get($bootstrap-icons-map, \"building-lock\"); }\n.bi-building-slash::before { content: map-get($bootstrap-icons-map, \"building-slash\"); }\n.bi-building-up::before { content: map-get($bootstrap-icons-map, \"building-up\"); }\n.bi-building-x::before { content: map-get($bootstrap-icons-map, \"building-x\"); }\n.bi-buildings-fill::before { content: map-get($bootstrap-icons-map, \"buildings-fill\"); }\n.bi-buildings::before { content: map-get($bootstrap-icons-map, \"buildings\"); }\n.bi-bus-front-fill::before { content: map-get($bootstrap-icons-map, \"bus-front-fill\"); }\n.bi-bus-front::before { content: map-get($bootstrap-icons-map, \"bus-front\"); }\n.bi-ev-front-fill::before { content: map-get($bootstrap-icons-map, \"ev-front-fill\"); }\n.bi-ev-front::before { content: map-get($bootstrap-icons-map, \"ev-front\"); }\n.bi-globe-americas::before { content: map-get($bootstrap-icons-map, \"globe-americas\"); }\n.bi-globe-asia-australia::before { content: map-get($bootstrap-icons-map, \"globe-asia-australia\"); }\n.bi-globe-central-south-asia::before { content: map-get($bootstrap-icons-map, \"globe-central-south-asia\"); }\n.bi-globe-europe-africa::before { content: map-get($bootstrap-icons-map, \"globe-europe-africa\"); }\n.bi-house-add-fill::before { content: map-get($bootstrap-icons-map, \"house-add-fill\"); }\n.bi-house-add::before { content: map-get($bootstrap-icons-map, \"house-add\"); }\n.bi-house-check-fill::before { content: map-get($bootstrap-icons-map, \"house-check-fill\"); }\n.bi-house-check::before { content: map-get($bootstrap-icons-map, \"house-check\"); }\n.bi-house-dash-fill::before { content: map-get($bootstrap-icons-map, \"house-dash-fill\"); }\n.bi-house-dash::before { content: map-get($bootstrap-icons-map, \"house-dash\"); }\n.bi-house-down-fill::before { content: map-get($bootstrap-icons-map, \"house-down-fill\"); }\n.bi-house-down::before { content: map-get($bootstrap-icons-map, \"house-down\"); }\n.bi-house-exclamation-fill::before { content: map-get($bootstrap-icons-map, \"house-exclamation-fill\"); }\n.bi-house-exclamation::before { content: map-get($bootstrap-icons-map, \"house-exclamation\"); }\n.bi-house-gear-fill::before { content: map-get($bootstrap-icons-map, \"house-gear-fill\"); }\n.bi-house-gear::before { content: map-get($bootstrap-icons-map, \"house-gear\"); }\n.bi-house-lock-fill::before { content: map-get($bootstrap-icons-map, \"house-lock-fill\"); }\n.bi-house-lock::before { content: map-get($bootstrap-icons-map, \"house-lock\"); }\n.bi-house-slash-fill::before { content: map-get($bootstrap-icons-map, \"house-slash-fill\"); }\n.bi-house-slash::before { content: map-get($bootstrap-icons-map, \"house-slash\"); }\n.bi-house-up-fill::before { content: map-get($bootstrap-icons-map, \"house-up-fill\"); }\n.bi-house-up::before { content: map-get($bootstrap-icons-map, \"house-up\"); }\n.bi-house-x-fill::before { content: map-get($bootstrap-icons-map, \"house-x-fill\"); }\n.bi-house-x::before { content: map-get($bootstrap-icons-map, \"house-x\"); }\n.bi-person-add::before { content: map-get($bootstrap-icons-map, \"person-add\"); }\n.bi-person-down::before { content: map-get($bootstrap-icons-map, \"person-down\"); }\n.bi-person-exclamation::before { content: map-get($bootstrap-icons-map, \"person-exclamation\"); }\n.bi-person-fill-add::before { content: map-get($bootstrap-icons-map, \"person-fill-add\"); }\n.bi-person-fill-check::before { content: map-get($bootstrap-icons-map, \"person-fill-check\"); }\n.bi-person-fill-dash::before { content: map-get($bootstrap-icons-map, \"person-fill-dash\"); }\n.bi-person-fill-down::before { content: map-get($bootstrap-icons-map, \"person-fill-down\"); }\n.bi-person-fill-exclamation::before { content: map-get($bootstrap-icons-map, \"person-fill-exclamation\"); }\n.bi-person-fill-gear::before { content: map-get($bootstrap-icons-map, \"person-fill-gear\"); }\n.bi-person-fill-lock::before { content: map-get($bootstrap-icons-map, \"person-fill-lock\"); }\n.bi-person-fill-slash::before { content: map-get($bootstrap-icons-map, \"person-fill-slash\"); }\n.bi-person-fill-up::before { content: map-get($bootstrap-icons-map, \"person-fill-up\"); }\n.bi-person-fill-x::before { content: map-get($bootstrap-icons-map, \"person-fill-x\"); }\n.bi-person-gear::before { content: map-get($bootstrap-icons-map, \"person-gear\"); }\n.bi-person-lock::before { content: map-get($bootstrap-icons-map, \"person-lock\"); }\n.bi-person-slash::before { content: map-get($bootstrap-icons-map, \"person-slash\"); }\n.bi-person-up::before { content: map-get($bootstrap-icons-map, \"person-up\"); }\n.bi-scooter::before { content: map-get($bootstrap-icons-map, \"scooter\"); }\n.bi-taxi-front-fill::before { content: map-get($bootstrap-icons-map, \"taxi-front-fill\"); }\n.bi-taxi-front::before { content: map-get($bootstrap-icons-map, \"taxi-front\"); }\n.bi-amd::before { content: map-get($bootstrap-icons-map, \"amd\"); }\n.bi-database-add::before { content: map-get($bootstrap-icons-map, \"database-add\"); }\n.bi-database-check::before { content: map-get($bootstrap-icons-map, \"database-check\"); }\n.bi-database-dash::before { content: map-get($bootstrap-icons-map, \"database-dash\"); }\n.bi-database-down::before { content: map-get($bootstrap-icons-map, \"database-down\"); }\n.bi-database-exclamation::before { content: map-get($bootstrap-icons-map, \"database-exclamation\"); }\n.bi-database-fill-add::before { content: map-get($bootstrap-icons-map, \"database-fill-add\"); }\n.bi-database-fill-check::before { content: map-get($bootstrap-icons-map, \"database-fill-check\"); }\n.bi-database-fill-dash::before { content: map-get($bootstrap-icons-map, \"database-fill-dash\"); }\n.bi-database-fill-down::before { content: map-get($bootstrap-icons-map, \"database-fill-down\"); }\n.bi-database-fill-exclamation::before { content: map-get($bootstrap-icons-map, \"database-fill-exclamation\"); }\n.bi-database-fill-gear::before { content: map-get($bootstrap-icons-map, \"database-fill-gear\"); }\n.bi-database-fill-lock::before { content: map-get($bootstrap-icons-map, \"database-fill-lock\"); }\n.bi-database-fill-slash::before { content: map-get($bootstrap-icons-map, \"database-fill-slash\"); }\n.bi-database-fill-up::before { content: map-get($bootstrap-icons-map, \"database-fill-up\"); }\n.bi-database-fill-x::before { content: map-get($bootstrap-icons-map, \"database-fill-x\"); }\n.bi-database-fill::before { content: map-get($bootstrap-icons-map, \"database-fill\"); }\n.bi-database-gear::before { content: map-get($bootstrap-icons-map, \"database-gear\"); }\n.bi-database-lock::before { content: map-get($bootstrap-icons-map, \"database-lock\"); }\n.bi-database-slash::before { content: map-get($bootstrap-icons-map, \"database-slash\"); }\n.bi-database-up::before { content: map-get($bootstrap-icons-map, \"database-up\"); }\n.bi-database-x::before { content: map-get($bootstrap-icons-map, \"database-x\"); }\n.bi-database::before { content: map-get($bootstrap-icons-map, \"database\"); }\n.bi-houses-fill::before { content: map-get($bootstrap-icons-map, \"houses-fill\"); }\n.bi-houses::before { content: map-get($bootstrap-icons-map, \"houses\"); }\n.bi-nvidia::before { content: map-get($bootstrap-icons-map, \"nvidia\"); }\n.bi-person-vcard-fill::before { content: map-get($bootstrap-icons-map, \"person-vcard-fill\"); }\n.bi-person-vcard::before { content: map-get($bootstrap-icons-map, \"person-vcard\"); }\n.bi-sina-weibo::before { content: map-get($bootstrap-icons-map, \"sina-weibo\"); }\n.bi-tencent-qq::before { content: map-get($bootstrap-icons-map, \"tencent-qq\"); }\n.bi-wikipedia::before { content: map-get($bootstrap-icons-map, \"wikipedia\"); }\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap-select/bootstrap-select.css",
    "content": "/*!\n * Bootstrap-select v1.13.9 (https://developer.snapappointments.com/bootstrap-select)\n *\n * Copyright 2012-2019 SnapAppointments, LLC\n * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)\n */\n\nselect.bs-select-hidden,\n.bootstrap-select > select.bs-select-hidden,\nselect.selectpicker {\n  display: none !important;\n}\n.bootstrap-select {\n  width: 220px \\0;\n  /*IE9 and below*/\n  vertical-align: middle;\n}\n.bootstrap-select > .dropdown-toggle {\n  position: relative;\n  width: 100%;\n  text-align: right;\n  white-space: nowrap;\n  display: -webkit-inline-box;\n  display: -webkit-inline-flex;\n  display: -ms-inline-flexbox;\n  display: inline-flex;\n  -webkit-box-align: center;\n  -webkit-align-items: center;\n      -ms-flex-align: center;\n          align-items: center;\n  -webkit-box-pack: justify;\n  -webkit-justify-content: space-between;\n      -ms-flex-pack: justify;\n          justify-content: space-between;\n}\n.bootstrap-select > .dropdown-toggle:after {\n  margin-top: -1px;\n}\n.bootstrap-select > .dropdown-toggle.bs-placeholder,\n.bootstrap-select > .dropdown-toggle.bs-placeholder:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder:active {\n  color: #999;\n}\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:active {\n  color: rgba(255, 255, 255, 0.5);\n}\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-invalid {\n  border: 1px solid #dc3545;\n}\n.bootstrap-select > select {\n  position: absolute !important;\n  bottom: 0;\n  left: 50%;\n  display: block !important;\n  width: 0.5px !important;\n  height: 100% !important;\n  padding: 0 !important;\n  opacity: 0 !important;\n  border: none;\n  z-index: 0 !important;\n}\n.bootstrap-select > select.mobile-device {\n  top: 0;\n  left: 0;\n  display: block !important;\n  width: 100% !important;\n  z-index: 2 !important;\n}\n.has-error .bootstrap-select .dropdown-toggle,\n.error .bootstrap-select .dropdown-toggle,\n.bootstrap-select.is-invalid .dropdown-toggle,\n.was-validated .bootstrap-select .selectpicker:invalid + .dropdown-toggle {\n  border-color: #b94a48;\n}\n.bootstrap-select.is-valid .dropdown-toggle,\n.was-validated .bootstrap-select .selectpicker:valid + .dropdown-toggle {\n  border-color: #28a745;\n}\n.bootstrap-select.fit-width {\n  width: auto !important;\n}\n.bootstrap-select:not([class*=\"col-\"]):not([class*=\"form-control\"]):not(.input-group-btn) {\n  width: 220px;\n}\n.bootstrap-select > select.mobile-device:focus + .dropdown-toggle,\n.bootstrap-select .dropdown-toggle:focus {\n  outline: thin dotted #333333 !important;\n  outline: 5px auto -webkit-focus-ring-color !important;\n  outline-offset: -2px;\n}\n.bootstrap-select.form-control {\n  margin-bottom: 0;\n  padding: 0;\n  border: none;\n}\n:not(.input-group) > .bootstrap-select.form-control:not([class*=\"col-\"]) {\n  width: 100%;\n}\n.bootstrap-select.form-control.input-group-btn {\n  float: none;\n  z-index: auto;\n}\n.form-inline .bootstrap-select,\n.form-inline .bootstrap-select.form-control:not([class*=\"col-\"]) {\n  width: auto;\n}\n.bootstrap-select:not(.input-group-btn),\n.bootstrap-select[class*=\"col-\"] {\n  float: none;\n  display: inline-block;\n  margin-left: 0;\n}\n.bootstrap-select.dropdown-menu-right,\n.bootstrap-select[class*=\"col-\"].dropdown-menu-right,\n.row .bootstrap-select[class*=\"col-\"].dropdown-menu-right {\n  float: right;\n}\n.form-inline .bootstrap-select,\n.form-horizontal .bootstrap-select,\n.form-group .bootstrap-select {\n  margin-bottom: 0;\n}\n.form-group-lg .bootstrap-select.form-control,\n.form-group-sm .bootstrap-select.form-control {\n  padding: 0;\n}\n.form-group-lg .bootstrap-select.form-control .dropdown-toggle,\n.form-group-sm .bootstrap-select.form-control .dropdown-toggle {\n  height: 100%;\n  font-size: inherit;\n  line-height: inherit;\n  border-radius: inherit;\n}\n.bootstrap-select.form-control-sm .dropdown-toggle,\n.bootstrap-select.form-control-lg .dropdown-toggle {\n  font-size: inherit;\n  line-height: inherit;\n  border-radius: inherit;\n}\n.bootstrap-select.form-control-sm .dropdown-toggle {\n  padding: 0.25rem 0.5rem;\n}\n.bootstrap-select.form-control-lg .dropdown-toggle {\n  padding: 0.5rem 1rem;\n}\n.form-inline .bootstrap-select .form-control {\n  width: 100%;\n}\n.bootstrap-select.disabled,\n.bootstrap-select > .disabled {\n  cursor: not-allowed;\n}\n.bootstrap-select.disabled:focus,\n.bootstrap-select > .disabled:focus {\n  outline: none !important;\n}\n.bootstrap-select.bs-container {\n  position: absolute;\n  top: 0;\n  left: 0;\n  height: 0 !important;\n  padding: 0 !important;\n}\n.bootstrap-select.bs-container .dropdown-menu {\n  z-index: 1060;\n}\n.bootstrap-select .dropdown-toggle .filter-option {\n  position: static;\n  top: 0;\n  left: 0;\n  float: left;\n  height: 100%;\n  width: 100%;\n  text-align: left;\n  overflow: hidden;\n  -webkit-box-flex: 0;\n  -webkit-flex: 0 1 auto;\n      -ms-flex: 0 1 auto;\n          flex: 0 1 auto;\n}\n.bs3.bootstrap-select .dropdown-toggle .filter-option {\n  padding-right: inherit;\n}\n.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option {\n  position: absolute;\n  padding-top: inherit;\n  padding-bottom: inherit;\n  padding-left: inherit;\n  float: none;\n}\n.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner {\n  padding-right: inherit;\n}\n.bootstrap-select .dropdown-toggle .filter-option-inner-inner {\n  overflow: hidden;\n}\n.bootstrap-select .dropdown-toggle .filter-expand {\n  width: 0 !important;\n  float: left;\n  opacity: 0 !important;\n  overflow: hidden;\n}\n.bootstrap-select .dropdown-toggle .caret {\n  position: absolute;\n  top: 50%;\n  right: 12px;\n  margin-top: -2px;\n  vertical-align: middle;\n}\n.input-group .bootstrap-select.form-control .dropdown-toggle {\n  border-radius: inherit;\n}\n.bootstrap-select[class*=\"col-\"] .dropdown-toggle {\n  width: 100%;\n}\n.bootstrap-select .dropdown-menu {\n  min-width: 100%;\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n.bootstrap-select .dropdown-menu > .inner:focus {\n  outline: none !important;\n}\n.bootstrap-select .dropdown-menu.inner {\n  position: static;\n  float: none;\n  border: 0;\n  padding: 0;\n  margin: 0;\n  border-radius: 0;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n.bootstrap-select .dropdown-menu li {\n  position: relative;\n}\n.bootstrap-select .dropdown-menu li.active small {\n  color: rgba(255, 255, 255, 0.5) !important;\n}\n.bootstrap-select .dropdown-menu li.disabled a {\n  cursor: not-allowed;\n}\n.bootstrap-select .dropdown-menu li a {\n  cursor: pointer;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n          user-select: none;\n}\n.bootstrap-select .dropdown-menu li a.opt {\n  position: relative;\n  padding-left: 2.25em;\n}\n.bootstrap-select .dropdown-menu li a span.check-mark {\n  display: none;\n}\n.bootstrap-select .dropdown-menu li a span.text {\n  display: inline-block;\n}\n.bootstrap-select .dropdown-menu li small {\n  padding-left: 0.5em;\n}\n.bootstrap-select .dropdown-menu .notify {\n  position: absolute;\n  bottom: 5px;\n  width: 96%;\n  margin: 0 2%;\n  min-height: 26px;\n  padding: 3px 5px;\n  background: #f5f5f5;\n  border: 1px solid #e3e3e3;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n  pointer-events: none;\n  opacity: 0.9;\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n.bootstrap-select .no-results {\n  padding: 3px;\n  background: #f5f5f5;\n  margin: 0 5px;\n  white-space: nowrap;\n}\n.bootstrap-select.fit-width .dropdown-toggle .filter-option {\n  position: static;\n  display: inline;\n  padding: 0;\n  width: auto;\n}\n.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,\n.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner {\n  display: inline;\n}\n.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before {\n  content: '\\00a0';\n}\n.bootstrap-select.fit-width .dropdown-toggle .caret {\n  position: static;\n  top: auto;\n  margin-top: -1px;\n}\n.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark {\n  position: absolute;\n  display: inline-block;\n  right: 15px;\n  top: 5px;\n}\n.bootstrap-select.show-tick .dropdown-menu li a span.text {\n  margin-right: 34px;\n}\n.bootstrap-select .bs-ok-default:after {\n  content: '';\n  display: block;\n  width: 0.5em;\n  height: 1em;\n  border-style: solid;\n  border-width: 0 0.26em 0.26em 0;\n  -webkit-transform: rotate(45deg);\n      -ms-transform: rotate(45deg);\n       -o-transform: rotate(45deg);\n          transform: rotate(45deg);\n}\n.bootstrap-select.show-menu-arrow.open > .dropdown-toggle,\n.bootstrap-select.show-menu-arrow.show > .dropdown-toggle {\n  z-index: 1061;\n}\n.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before {\n  content: '';\n  border-left: 7px solid transparent;\n  border-right: 7px solid transparent;\n  border-bottom: 7px solid rgba(204, 204, 204, 0.2);\n  position: absolute;\n  bottom: -4px;\n  left: 9px;\n  display: none;\n}\n.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after {\n  content: '';\n  border-left: 6px solid transparent;\n  border-right: 6px solid transparent;\n  border-bottom: 6px solid white;\n  position: absolute;\n  bottom: -4px;\n  left: 10px;\n  display: none;\n}\n.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before {\n  bottom: auto;\n  top: -4px;\n  border-top: 7px solid rgba(204, 204, 204, 0.2);\n  border-bottom: 0;\n}\n.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after {\n  bottom: auto;\n  top: -4px;\n  border-top: 6px solid white;\n  border-bottom: 0;\n}\n.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before {\n  right: 12px;\n  left: auto;\n}\n.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after {\n  right: 13px;\n  left: auto;\n}\n.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:before,\n.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:before,\n.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:after,\n.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:after {\n  display: block;\n}\n.bs-searchbox,\n.bs-actionsbox,\n.bs-donebutton {\n  padding: 4px 8px;\n}\n.bs-actionsbox {\n  width: 100%;\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n.bs-actionsbox .btn-group button {\n  width: 50%;\n}\n.bs-donebutton {\n  float: left;\n  width: 100%;\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n.bs-donebutton .btn-group button {\n  width: 100%;\n}\n.bs-searchbox + .bs-actionsbox {\n  padding: 0 8px 4px;\n}\n.bs-searchbox .form-control {\n  margin-bottom: 0;\n  width: 100%;\n  float: none;\n}\n/*# sourceMappingURL=bootstrap-select.css.map */"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/bootstrap-select/bootstrap-select.js",
    "content": "(function ($) {\n  'use strict';\n\n  var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'];\n\n  var uriAttrs = [\n    'background',\n    'cite',\n    'href',\n    'itemtype',\n    'longdesc',\n    'poster',\n    'src',\n    'xlink:href'\n  ];\n\n  var ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\n\n  var DefaultWhitelist = {\n    // Global attributes allowed on any supplied element below.\n    '*': ['class', 'dir', 'id', 'lang', 'role', 'tabindex', 'style', ARIA_ATTRIBUTE_PATTERN],\n    a: ['target', 'href', 'title', 'rel'],\n    area: [],\n    b: [],\n    br: [],\n    col: [],\n    code: [],\n    div: [],\n    em: [],\n    hr: [],\n    h1: [],\n    h2: [],\n    h3: [],\n    h4: [],\n    h5: [],\n    h6: [],\n    i: [],\n    img: ['src', 'alt', 'title', 'width', 'height'],\n    li: [],\n    ol: [],\n    p: [],\n    pre: [],\n    s: [],\n    small: [],\n    span: [],\n    sub: [],\n    sup: [],\n    strong: [],\n    u: [],\n    ul: []\n  };\n\n  /**\n   * A pattern that recognizes a commonly useful subset of URLs that are safe.\n   *\n   * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n   */\n  var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi;\n\n  /**\n   * A pattern that matches safe data URLs. Only matches image, video and audio types.\n   *\n   * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n   */\n  var DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;\n\n  var ParseableAttributes = ['title', 'placeholder']; // attributes to use as settings, can add others in the future\n\n  function allowedAttribute (attr, allowedAttributeList) {\n    var attrName = attr.nodeName.toLowerCase();\n\n    if ($.inArray(attrName, allowedAttributeList) !== -1) {\n      if ($.inArray(attrName, uriAttrs) !== -1) {\n        return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN));\n      }\n\n      return true;\n    }\n\n    var regExp = $(allowedAttributeList).filter(function (index, value) {\n      return value instanceof RegExp;\n    });\n\n    // Check if a regular expression validates the attribute.\n    for (var i = 0, l = regExp.length; i < l; i++) {\n      if (attrName.match(regExp[i])) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  function sanitizeHtml (unsafeElements, whiteList, sanitizeFn) {\n    if (sanitizeFn && typeof sanitizeFn === 'function') {\n      return sanitizeFn(unsafeElements);\n    }\n\n    var whitelistKeys = Object.keys(whiteList);\n\n    for (var i = 0, len = unsafeElements.length; i < len; i++) {\n      var elements = unsafeElements[i].querySelectorAll('*');\n\n      for (var j = 0, len2 = elements.length; j < len2; j++) {\n        var el = elements[j];\n        var elName = el.nodeName.toLowerCase();\n\n        if (whitelistKeys.indexOf(elName) === -1) {\n          el.parentNode.removeChild(el);\n\n          continue;\n        }\n\n        var attributeList = [].slice.call(el.attributes);\n        var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []);\n\n        for (var k = 0, len3 = attributeList.length; k < len3; k++) {\n          var attr = attributeList[k];\n\n          if (!allowedAttribute(attr, whitelistedAttributes)) {\n            el.removeAttribute(attr.nodeName);\n          }\n        }\n      }\n    }\n  }\n\n  function getAttributesObject ($select) {\n    var attributesObject = {},\n        attrVal;\n\n    ParseableAttributes.forEach(function (item) {\n      attrVal = $select.attr(item);\n      if (attrVal) attributesObject[item] = attrVal;\n    });\n\n    // for backwards compatibility\n    // (using title as placeholder is deprecated - remove in v2.0.0)\n    if (!attributesObject.placeholder && attributesObject.title) {\n      attributesObject.placeholder = attributesObject.title;\n    }\n\n    return attributesObject;\n  }\n\n  // Polyfill for browsers with no classList support\n  // Remove in v2\n  if (!('classList' in document.createElement('_'))) {\n    (function (view) {\n      if (!('Element' in view)) return;\n\n      var classListProp = 'classList',\n          protoProp = 'prototype',\n          elemCtrProto = view.Element[protoProp],\n          objCtr = Object,\n          classListGetter = function () {\n            var $elem = $(this);\n\n            return {\n              add: function (classes) {\n                classes = Array.prototype.slice.call(arguments).join(' ');\n                return $elem.addClass(classes);\n              },\n              remove: function (classes) {\n                classes = Array.prototype.slice.call(arguments).join(' ');\n                return $elem.removeClass(classes);\n              },\n              toggle: function (classes, force) {\n                return $elem.toggleClass(classes, force);\n              },\n              contains: function (classes) {\n                return $elem.hasClass(classes);\n              }\n            };\n          };\n\n      if (objCtr.defineProperty) {\n        var classListPropDesc = {\n          get: classListGetter,\n          enumerable: true,\n          configurable: true\n        };\n        try {\n          objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);\n        } catch (ex) { // IE 8 doesn't support enumerable:true\n          // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36\n          // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected\n          if (ex.number === undefined || ex.number === -0x7FF5EC54) {\n            classListPropDesc.enumerable = false;\n            objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);\n          }\n        }\n      } else if (objCtr[protoProp].__defineGetter__) {\n        elemCtrProto.__defineGetter__(classListProp, classListGetter);\n      }\n    }(window));\n  }\n\n  var testElement = document.createElement('_');\n\n  testElement.classList.add('c1', 'c2');\n\n  if (!testElement.classList.contains('c2')) {\n    var _add = DOMTokenList.prototype.add,\n        _remove = DOMTokenList.prototype.remove;\n\n    DOMTokenList.prototype.add = function () {\n      Array.prototype.forEach.call(arguments, _add.bind(this));\n    };\n\n    DOMTokenList.prototype.remove = function () {\n      Array.prototype.forEach.call(arguments, _remove.bind(this));\n    };\n  }\n\n  testElement.classList.toggle('c3', false);\n\n  // Polyfill for IE 10 and Firefox <24, where classList.toggle does not\n  // support the second argument.\n  if (testElement.classList.contains('c3')) {\n    var _toggle = DOMTokenList.prototype.toggle;\n\n    DOMTokenList.prototype.toggle = function (token, force) {\n      if (1 in arguments && !this.contains(token) === !force) {\n        return force;\n      } else {\n        return _toggle.call(this, token);\n      }\n    };\n  }\n\n  testElement = null;\n\n  // Polyfill for IE (remove in v2)\n  Object.values = typeof Object.values === 'function' ? Object.values : function (obj) {\n    return Object.keys(obj).map(function (key) {\n      return obj[key];\n    });\n  };\n\n  // shallow array comparison\n  function isEqual (array1, array2) {\n    return array1.length === array2.length && array1.every(function (element, index) {\n      return element === array2[index];\n    });\n  };\n\n  // <editor-fold desc=\"Shims\">\n  if (!String.prototype.startsWith) {\n    (function () {\n      'use strict'; // needed to support `apply`/`call` with `undefined`/`null`\n      var toString = {}.toString;\n      var startsWith = function (search) {\n        if (this == null) {\n          throw new TypeError();\n        }\n        var string = String(this);\n        if (search && toString.call(search) == '[object RegExp]') {\n          throw new TypeError();\n        }\n        var stringLength = string.length;\n        var searchString = String(search);\n        var searchLength = searchString.length;\n        var position = arguments.length > 1 ? arguments[1] : undefined;\n        // `ToInteger`\n        var pos = position ? Number(position) : 0;\n        if (pos != pos) { // better `isNaN`\n          pos = 0;\n        }\n        var start = Math.min(Math.max(pos, 0), stringLength);\n        // Avoid the `indexOf` call if no match is possible\n        if (searchLength + start > stringLength) {\n          return false;\n        }\n        var index = -1;\n        while (++index < searchLength) {\n          if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {\n            return false;\n          }\n        }\n        return true;\n      };\n      if (Object.defineProperty) {\n        Object.defineProperty(String.prototype, 'startsWith', {\n          'value': startsWith,\n          'configurable': true,\n          'writable': true\n        });\n      } else {\n        String.prototype.startsWith = startsWith;\n      }\n    }());\n  }\n\n  function toKebabCase (str) {\n    return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, function ($, ofs) {\n      return (ofs ? '-' : '') + $.toLowerCase();\n    });\n  }\n\n  function getSelectedOptions () {\n    var options = this.selectpicker.main.data;\n\n    if (this.options.source.data || this.options.source.search) {\n      options = Object.values(this.selectpicker.optionValuesDataMap);\n    }\n\n    var selectedOptions = options.filter(function (item) {\n      if (item.selected) {\n        if (this.options.hideDisabled && item.disabled) return false;\n        return true;\n      }\n\n      return false;\n    }, this);\n\n    // ensure only 1 option is selected if multiple are set in the data source\n    if (this.options.source.data && !this.multiple && selectedOptions.length > 1) {\n      for (var i = 0; i < selectedOptions.length - 1; i++) {\n        selectedOptions[i].selected = false;\n      }\n\n      selectedOptions = [ selectedOptions[selectedOptions.length - 1] ];\n    }\n\n    return selectedOptions;\n  }\n\n  // much faster than $.val()\n  function getSelectValues (selectedOptions) {\n    var value = [],\n        options = selectedOptions || getSelectedOptions.call(this),\n        opt;\n\n    for (var i = 0, len = options.length; i < len; i++) {\n      opt = options[i];\n\n      if (!opt.disabled) {\n        value.push(opt.value === undefined ? opt.text : opt.value);\n      }\n    }\n\n    if (!this.multiple) {\n      return !value.length ? null : value[0];\n    }\n\n    return value;\n  }\n\n  // set data-selected on select element if the value has been programmatically selected\n  // prior to initialization of bootstrap-select\n  // * consider removing or replacing an alternative method *\n  var valHooks = {\n    useDefault: false,\n    _set: $.valHooks.select.set\n  };\n\n  $.valHooks.select.set = function (elem, value) {\n    if (value && !valHooks.useDefault) $(elem).data('selected', true);\n\n    return valHooks._set.apply(this, arguments);\n  };\n\n  var changedArguments = null;\n\n  var EventIsSupported = (function () {\n    try {\n      new Event('change');\n      return true;\n    } catch (e) {\n      return false;\n    }\n  })();\n\n  $.fn.triggerNative = function (eventName) {\n    var el = this[0],\n        event;\n\n    if (el.dispatchEvent) { // for modern browsers & IE9+\n      if (EventIsSupported) {\n        // For modern browsers\n        event = new Event(eventName, {\n          bubbles: true\n        });\n      } else {\n        // For IE since it doesn't support Event constructor\n        event = document.createEvent('Event');\n        event.initEvent(eventName, true, false);\n      }\n\n      el.dispatchEvent(event);\n    }\n  };\n  // </editor-fold>\n\n  function stringSearch (li, searchString, method, normalize) {\n    var stringTypes = [\n          'display',\n          'subtext',\n          'tokens'\n        ],\n        searchSuccess = false;\n\n    for (var i = 0; i < stringTypes.length; i++) {\n      var stringType = stringTypes[i],\n          string = li[stringType];\n\n      if (string) {\n        string = string.toString();\n\n        // Strip HTML tags. This isn't perfect, but it's much faster than any other method\n        if (stringType === 'display') {\n          string = string.replace(/<[^>]+>/g, '');\n        }\n\n        if (normalize) string = normalizeToBase(string);\n        string = string.toUpperCase();\n\n        if (typeof method === 'function') {\n          searchSuccess = method(string, searchString);\n        } else if (method === 'contains') {\n          searchSuccess = string.indexOf(searchString) >= 0;\n        } else {\n          searchSuccess = string.startsWith(searchString);\n        }\n\n        if (searchSuccess) break;\n      }\n    }\n\n    return searchSuccess;\n  }\n\n  function toInteger (value) {\n    return parseInt(value, 10) || 0;\n  }\n\n  // Borrowed from Lodash (_.deburr)\n  /** Used to map Latin Unicode letters to basic Latin letters. */\n  var deburredLetters = {\n    // Latin-1 Supplement block.\n    '\\xc0': 'A',  '\\xc1': 'A', '\\xc2': 'A', '\\xc3': 'A', '\\xc4': 'A', '\\xc5': 'A',\n    '\\xe0': 'a',  '\\xe1': 'a', '\\xe2': 'a', '\\xe3': 'a', '\\xe4': 'a', '\\xe5': 'a',\n    '\\xc7': 'C',  '\\xe7': 'c',\n    '\\xd0': 'D',  '\\xf0': 'd',\n    '\\xc8': 'E',  '\\xc9': 'E', '\\xca': 'E', '\\xcb': 'E',\n    '\\xe8': 'e',  '\\xe9': 'e', '\\xea': 'e', '\\xeb': 'e',\n    '\\xcc': 'I',  '\\xcd': 'I', '\\xce': 'I', '\\xcf': 'I',\n    '\\xec': 'i',  '\\xed': 'i', '\\xee': 'i', '\\xef': 'i',\n    '\\xd1': 'N',  '\\xf1': 'n',\n    '\\xd2': 'O',  '\\xd3': 'O', '\\xd4': 'O', '\\xd5': 'O', '\\xd6': 'O', '\\xd8': 'O',\n    '\\xf2': 'o',  '\\xf3': 'o', '\\xf4': 'o', '\\xf5': 'o', '\\xf6': 'o', '\\xf8': 'o',\n    '\\xd9': 'U',  '\\xda': 'U', '\\xdb': 'U', '\\xdc': 'U',\n    '\\xf9': 'u',  '\\xfa': 'u', '\\xfb': 'u', '\\xfc': 'u',\n    '\\xdd': 'Y',  '\\xfd': 'y', '\\xff': 'y',\n    '\\xc6': 'Ae', '\\xe6': 'ae',\n    '\\xde': 'Th', '\\xfe': 'th',\n    '\\xdf': 'ss',\n    // Latin Extended-A block.\n    '\\u0100': 'A',  '\\u0102': 'A', '\\u0104': 'A',\n    '\\u0101': 'a',  '\\u0103': 'a', '\\u0105': 'a',\n    '\\u0106': 'C',  '\\u0108': 'C', '\\u010a': 'C', '\\u010c': 'C',\n    '\\u0107': 'c',  '\\u0109': 'c', '\\u010b': 'c', '\\u010d': 'c',\n    '\\u010e': 'D',  '\\u0110': 'D', '\\u010f': 'd', '\\u0111': 'd',\n    '\\u0112': 'E',  '\\u0114': 'E', '\\u0116': 'E', '\\u0118': 'E', '\\u011a': 'E',\n    '\\u0113': 'e',  '\\u0115': 'e', '\\u0117': 'e', '\\u0119': 'e', '\\u011b': 'e',\n    '\\u011c': 'G',  '\\u011e': 'G', '\\u0120': 'G', '\\u0122': 'G',\n    '\\u011d': 'g',  '\\u011f': 'g', '\\u0121': 'g', '\\u0123': 'g',\n    '\\u0124': 'H',  '\\u0126': 'H', '\\u0125': 'h', '\\u0127': 'h',\n    '\\u0128': 'I',  '\\u012a': 'I', '\\u012c': 'I', '\\u012e': 'I', '\\u0130': 'I',\n    '\\u0129': 'i',  '\\u012b': 'i', '\\u012d': 'i', '\\u012f': 'i', '\\u0131': 'i',\n    '\\u0134': 'J',  '\\u0135': 'j',\n    '\\u0136': 'K',  '\\u0137': 'k', '\\u0138': 'k',\n    '\\u0139': 'L',  '\\u013b': 'L', '\\u013d': 'L', '\\u013f': 'L', '\\u0141': 'L',\n    '\\u013a': 'l',  '\\u013c': 'l', '\\u013e': 'l', '\\u0140': 'l', '\\u0142': 'l',\n    '\\u0143': 'N',  '\\u0145': 'N', '\\u0147': 'N', '\\u014a': 'N',\n    '\\u0144': 'n',  '\\u0146': 'n', '\\u0148': 'n', '\\u014b': 'n',\n    '\\u014c': 'O',  '\\u014e': 'O', '\\u0150': 'O',\n    '\\u014d': 'o',  '\\u014f': 'o', '\\u0151': 'o',\n    '\\u0154': 'R',  '\\u0156': 'R', '\\u0158': 'R',\n    '\\u0155': 'r',  '\\u0157': 'r', '\\u0159': 'r',\n    '\\u015a': 'S',  '\\u015c': 'S', '\\u015e': 'S', '\\u0160': 'S',\n    '\\u015b': 's',  '\\u015d': 's', '\\u015f': 's', '\\u0161': 's',\n    '\\u0162': 'T',  '\\u0164': 'T', '\\u0166': 'T',\n    '\\u0163': 't',  '\\u0165': 't', '\\u0167': 't',\n    '\\u0168': 'U',  '\\u016a': 'U', '\\u016c': 'U', '\\u016e': 'U', '\\u0170': 'U', '\\u0172': 'U',\n    '\\u0169': 'u',  '\\u016b': 'u', '\\u016d': 'u', '\\u016f': 'u', '\\u0171': 'u', '\\u0173': 'u',\n    '\\u0174': 'W',  '\\u0175': 'w',\n    '\\u0176': 'Y',  '\\u0177': 'y', '\\u0178': 'Y',\n    '\\u0179': 'Z',  '\\u017b': 'Z', '\\u017d': 'Z',\n    '\\u017a': 'z',  '\\u017c': 'z', '\\u017e': 'z',\n    '\\u0132': 'IJ', '\\u0133': 'ij',\n    '\\u0152': 'Oe', '\\u0153': 'oe',\n    '\\u0149': \"'n\", '\\u017f': 's'\n  };\n\n  /** Used to match Latin Unicode letters (excluding mathematical operators). */\n  var reLatin = /[\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\xff\\u0100-\\u017f]/g;\n\n  /** Used to compose unicode character classes. */\n  var rsComboMarksRange = '\\\\u0300-\\\\u036f',\n      reComboHalfMarksRange = '\\\\ufe20-\\\\ufe2f',\n      rsComboSymbolsRange = '\\\\u20d0-\\\\u20ff',\n      rsComboMarksExtendedRange = '\\\\u1ab0-\\\\u1aff',\n      rsComboMarksSupplementRange = '\\\\u1dc0-\\\\u1dff',\n      rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange;\n\n  /** Used to compose unicode capture groups. */\n  var rsCombo = '[' + rsComboRange + ']';\n\n  /**\n   * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and\n   * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).\n   */\n  var reComboMark = RegExp(rsCombo, 'g');\n\n  function deburrLetter (key) {\n    return deburredLetters[key];\n  };\n\n  function normalizeToBase (string) {\n    string = string.toString();\n    return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');\n  }\n\n  // List of HTML entities for escaping.\n  var escapeMap = {\n    '&': '&amp;',\n    '<': '&lt;',\n    '>': '&gt;',\n    '\"': '&quot;',\n    \"'\": '&#x27;',\n    '`': '&#x60;'\n  };\n\n  // Functions for escaping and unescaping strings to/from HTML interpolation.\n  var createEscaper = function (map) {\n    var escaper = function (match) {\n      return map[match];\n    };\n    // Regexes for identifying a key that needs to be escaped.\n    var source = '(?:' + Object.keys(map).join('|') + ')';\n    var testRegexp = RegExp(source);\n    var replaceRegexp = RegExp(source, 'g');\n    return function (string) {\n      string = string == null ? '' : '' + string;\n      return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;\n    };\n  };\n\n  var htmlEscape = createEscaper(escapeMap);\n\n  /**\n   * ------------------------------------------------------------------------\n   * Constants\n   * ------------------------------------------------------------------------\n   */\n\n  var keyCodeMap = {\n    32: ' ',\n    48: '0',\n    49: '1',\n    50: '2',\n    51: '3',\n    52: '4',\n    53: '5',\n    54: '6',\n    55: '7',\n    56: '8',\n    57: '9',\n    59: ';',\n    65: 'A',\n    66: 'B',\n    67: 'C',\n    68: 'D',\n    69: 'E',\n    70: 'F',\n    71: 'G',\n    72: 'H',\n    73: 'I',\n    74: 'J',\n    75: 'K',\n    76: 'L',\n    77: 'M',\n    78: 'N',\n    79: 'O',\n    80: 'P',\n    81: 'Q',\n    82: 'R',\n    83: 'S',\n    84: 'T',\n    85: 'U',\n    86: 'V',\n    87: 'W',\n    88: 'X',\n    89: 'Y',\n    90: 'Z',\n    96: '0',\n    97: '1',\n    98: '2',\n    99: '3',\n    100: '4',\n    101: '5',\n    102: '6',\n    103: '7',\n    104: '8',\n    105: '9'\n  };\n\n  var keyCodes = {\n    ESCAPE: 27, // KeyboardEvent.which value for Escape (Esc) key\n    ENTER: 13, // KeyboardEvent.which value for Enter key\n    SPACE: 32, // KeyboardEvent.which value for space key\n    TAB: 9, // KeyboardEvent.which value for tab key\n    ARROW_UP: 38, // KeyboardEvent.which value for up arrow key\n    ARROW_DOWN: 40 // KeyboardEvent.which value for down arrow key\n  };\n\n  // eslint-disable-next-line no-undef\n  var Dropdown = window.Dropdown || bootstrap.Dropdown;\n\n  function getVersion () {\n    var version;\n\n    try {\n      version = $.fn.dropdown.Constructor.VERSION;\n    } catch (err) {\n      version = Dropdown.VERSION;\n    }\n\n    return version;\n  }\n\n  var version = {\n    success: false,\n    major: '3'\n  };\n\n  try {\n    version.full = (getVersion() || '').split(' ')[0].split('.');\n    version.major = version.full[0];\n    version.success = true;\n  } catch (err) {\n    // do nothing\n  }\n\n  var selectId = 0;\n\n  var EVENT_KEY = '.bs.select';\n\n  var classNames = {\n    DISABLED: 'disabled',\n    DIVIDER: 'divider',\n    SHOW: 'open',\n    DROPUP: 'dropup',\n    MENU: 'dropdown-menu',\n    MENURIGHT: 'dropdown-menu-right',\n    MENULEFT: 'dropdown-menu-left',\n    // to-do: replace with more advanced template/customization options\n    BUTTONCLASS: 'btn-default',\n    POPOVERHEADER: 'popover-title',\n    ICONBASE: 'glyphicon',\n    TICKICON: 'glyphicon-ok'\n  };\n\n  var Selector = {\n    MENU: '.' + classNames.MENU,\n    DATA_TOGGLE: 'data-toggle=\"dropdown\"'\n  };\n\n  var elementTemplates = {\n    div: document.createElement('div'),\n    span: document.createElement('span'),\n    i: document.createElement('i'),\n    subtext: document.createElement('small'),\n    a: document.createElement('a'),\n    li: document.createElement('li'),\n    whitespace: document.createTextNode('\\u00A0'),\n    fragment: document.createDocumentFragment(),\n    option: document.createElement('option')\n  };\n\n  elementTemplates.selectedOption = elementTemplates.option.cloneNode(false);\n  elementTemplates.selectedOption.setAttribute('selected', true);\n\n  elementTemplates.noResults = elementTemplates.li.cloneNode(false);\n  elementTemplates.noResults.className = 'no-results';\n\n  elementTemplates.a.setAttribute('role', 'option');\n  elementTemplates.a.className = 'dropdown-item';\n\n  elementTemplates.subtext.className = 'text-muted';\n\n  elementTemplates.text = elementTemplates.span.cloneNode(false);\n  elementTemplates.text.className = 'text';\n\n  elementTemplates.checkMark = elementTemplates.span.cloneNode(false);\n\n  var REGEXP_ARROW = new RegExp(keyCodes.ARROW_UP + '|' + keyCodes.ARROW_DOWN);\n  var REGEXP_TAB_OR_ESCAPE = new RegExp('^' + keyCodes.TAB + '$|' + keyCodes.ESCAPE);\n\n  var generateOption = {\n    li: function (content, classes, optgroup) {\n      var li = elementTemplates.li.cloneNode(false);\n\n      if (content) {\n        if (content.nodeType === 1 || content.nodeType === 11) {\n          li.appendChild(content);\n        } else {\n          li.innerHTML = content;\n        }\n      }\n\n      if (typeof classes !== 'undefined' && classes !== '') li.className = classes;\n      if (typeof optgroup !== 'undefined' && optgroup !== null) li.classList.add('optgroup-' + optgroup);\n\n      return li;\n    },\n\n    a: function (text, classes, inline) {\n      var a = elementTemplates.a.cloneNode(true);\n\n      if (text) {\n        if (text.nodeType === 11) {\n          a.appendChild(text);\n        } else {\n          a.insertAdjacentHTML('beforeend', text);\n        }\n      }\n\n      if (typeof classes !== 'undefined' && classes !== '') a.classList.add.apply(a.classList, classes.split(/\\s+/));\n      if (inline) a.setAttribute('style', inline);\n\n      return a;\n    },\n\n    text: function (options, useFragment) {\n      var textElement = elementTemplates.text.cloneNode(false),\n          subtextElement,\n          iconElement;\n\n      if (options.content) {\n        textElement.innerHTML = options.content;\n      } else {\n        textElement.textContent = options.text;\n\n        if (options.icon) {\n          var whitespace = elementTemplates.whitespace.cloneNode(false);\n\n          // need to use <i> for icons in the button to prevent a breaking change\n          // note: switch to span in next major release\n          iconElement = (useFragment === true ? elementTemplates.i : elementTemplates.span).cloneNode(false);\n          iconElement.className = this.options.iconBase + ' ' + options.icon;\n\n          elementTemplates.fragment.appendChild(iconElement);\n          elementTemplates.fragment.appendChild(whitespace);\n        }\n\n        if (options.subtext) {\n          subtextElement = elementTemplates.subtext.cloneNode(false);\n          subtextElement.textContent = options.subtext;\n          textElement.appendChild(subtextElement);\n        }\n      }\n\n      if (useFragment === true) {\n        while (textElement.childNodes.length > 0) {\n          elementTemplates.fragment.appendChild(textElement.childNodes[0]);\n        }\n      } else {\n        elementTemplates.fragment.appendChild(textElement);\n      }\n\n      return elementTemplates.fragment;\n    },\n\n    label: function (options) {\n      var textElement = elementTemplates.text.cloneNode(false),\n          subtextElement,\n          iconElement;\n\n      textElement.innerHTML = options.display;\n\n      if (options.icon) {\n        var whitespace = elementTemplates.whitespace.cloneNode(false);\n\n        iconElement = elementTemplates.span.cloneNode(false);\n        iconElement.className = this.options.iconBase + ' ' + options.icon;\n\n        elementTemplates.fragment.appendChild(iconElement);\n        elementTemplates.fragment.appendChild(whitespace);\n      }\n\n      if (options.subtext) {\n        subtextElement = elementTemplates.subtext.cloneNode(false);\n        subtextElement.textContent = options.subtext;\n        textElement.appendChild(subtextElement);\n      }\n\n      elementTemplates.fragment.appendChild(textElement);\n\n      return elementTemplates.fragment;\n    }\n  };\n\n  var getOptionData = {\n    fromOption: function (option, type) {\n      var value;\n\n      switch (type) {\n        case 'divider':\n          value = option.getAttribute('data-divider') === 'true';\n          break;\n\n        case 'text':\n          value = option.textContent;\n          break;\n\n        case 'label':\n          value = option.label;\n          break;\n\n        case 'style':\n          value = option.style.cssText;\n          break;\n\n        case 'title':\n          value = option.title;\n          break;\n\n        default:\n          value = option.getAttribute('data-' + toKebabCase(type));\n          break;\n      }\n\n      return value;\n    },\n    fromDataSource: function (option, type) {\n      var value;\n\n      switch (type) {\n        case 'text':\n        case 'label':\n          value = option.text || option.value || '';\n          break;\n\n        default:\n          value = option[type];\n          break;\n      }\n\n      return value;\n    }\n  };\n\n  function showNoResults (searchMatch, searchValue) {\n    if (!searchMatch.length) {\n      elementTemplates.noResults.innerHTML = this.options.noneResultsText.replace('{0}', '\"' + htmlEscape(searchValue) + '\"');\n      this.$menuInner[0].firstChild.appendChild(elementTemplates.noResults);\n    }\n  }\n\n  function filterHidden (item) {\n    return !(item.hidden || this.options.hideDisabled && item.disabled);\n  }\n\n  var Selectpicker = function (element, options) {\n    var that = this;\n\n    // bootstrap-select has been initialized - revert valHooks.select.set back to its original function\n    if (!valHooks.useDefault) {\n      $.valHooks.select.set = valHooks._set;\n      valHooks.useDefault = true;\n    }\n\n    this.$element = $(element);\n    this.$newElement = null;\n    this.$button = null;\n    this.$menu = null;\n    this.options = options;\n    this.selectpicker = {\n      main: {\n        data: [],\n        optionQueue: elementTemplates.fragment.cloneNode(false),\n        hasMore: false\n      },\n      search: {\n        data: [],\n        hasMore: false\n      },\n      current: {}, // current is either equal to main or search depending on if a search is in progress\n      view: {},\n      // map of option values and their respective data (only used in conjunction with options.source)\n      optionValuesDataMap: {},\n      isSearching: false,\n      keydown: {\n        keyHistory: '',\n        resetKeyHistory: {\n          start: function () {\n            return setTimeout(function () {\n              that.selectpicker.keydown.keyHistory = '';\n            }, 800);\n          }\n        }\n      }\n    };\n\n    this.sizeInfo = {};\n\n    // Format window padding\n    var winPad = this.options.windowPadding;\n    if (typeof winPad === 'number') {\n      this.options.windowPadding = [winPad, winPad, winPad, winPad];\n    }\n\n    // Expose public methods\n    this.val = Selectpicker.prototype.val;\n    this.render = Selectpicker.prototype.render;\n    this.refresh = Selectpicker.prototype.refresh;\n    this.setStyle = Selectpicker.prototype.setStyle;\n    this.selectAll = Selectpicker.prototype.selectAll;\n    this.deselectAll = Selectpicker.prototype.deselectAll;\n    this.destroy = Selectpicker.prototype.destroy;\n    this.remove = Selectpicker.prototype.remove;\n    this.show = Selectpicker.prototype.show;\n    this.hide = Selectpicker.prototype.hide;\n\n    this.init();\n  };\n\n  Selectpicker.VERSION = '1.14.0-beta3';\n\n  // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both.\n  Selectpicker.DEFAULTS = {\n    noneSelectedText: 'Nothing selected',\n    noneResultsText: 'No results matched {0}',\n    countSelectedText: function (numSelected, numTotal) {\n      return (numSelected == 1) ? '{0} item selected' : '{0} items selected';\n    },\n    maxOptionsText: function (numAll, numGroup) {\n      return [\n        (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)',\n        (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)'\n      ];\n    },\n    selectAllText: 'Select All',\n    deselectAllText: 'Deselect All',\n    source: {\n      pageSize: 40\n    },\n    chunkSize: 40,\n    doneButton: false,\n    doneButtonText: 'Close',\n    multipleSeparator: ', ',\n    styleBase: 'btn',\n    style: classNames.BUTTONCLASS,\n    size: 'auto',\n    title: null,\n    placeholder: null,\n    allowClear: false,\n    selectedTextFormat: 'values',\n    width: false,\n    container: false,\n    hideDisabled: false,\n    showSubtext: false,\n    showIcon: true,\n    showContent: true,\n    dropupAuto: true,\n    header: false,\n    liveSearch: false,\n    liveSearchPlaceholder: null,\n    liveSearchNormalize: false,\n    liveSearchStyle: 'contains',\n    actionsBox: false,\n    iconBase: classNames.ICONBASE,\n    tickIcon: classNames.TICKICON,\n    showTick: false,\n    template: {\n      caret: '<span class=\"caret\"></span>'\n    },\n    maxOptions: false,\n    mobile: false,\n    selectOnTab: true,\n    dropdownAlignRight: false,\n    windowPadding: 0,\n    virtualScroll: 600,\n    display: false,\n    sanitize: true,\n    sanitizeFn: null,\n    whiteList: DefaultWhitelist\n  };\n\n  Selectpicker.prototype = {\n\n    constructor: Selectpicker,\n\n    init: function () {\n      var that = this,\n          id = this.$element.attr('id'),\n          element = this.$element[0],\n          form = element.form;\n\n      selectId++;\n      this.selectId = 'bs-select-' + selectId;\n\n      element.classList.add('bs-select-hidden');\n\n      this.multiple = this.$element.prop('multiple');\n      this.autofocus = this.$element.prop('autofocus');\n\n      if (element.classList.contains('show-tick')) {\n        this.options.showTick = true;\n      }\n\n      this.$newElement = this.createDropdown();\n\n      this.$element\n          .after(this.$newElement)\n          .prependTo(this.$newElement);\n\n      // ensure select is associated with form element if it got unlinked after moving it inside newElement\n      if (form && element.form === null) {\n        if (!form.id) form.id = 'form-' + this.selectId;\n        element.setAttribute('form', form.id);\n      }\n\n      this.$button = this.$newElement.children('button');\n      if (this.options.allowClear) this.$clearButton = this.$button.children('.bs-select-clear-selected');\n      this.$menu = this.$newElement.children(Selector.MENU);\n      this.$menuInner = this.$menu.children('.inner');\n      this.$searchbox = this.$menu.find('input');\n\n      element.classList.remove('bs-select-hidden');\n\n      this.fetchData(function () {\n        that.render(true);\n        that.buildList();\n\n        requestAnimationFrame(function () {\n          that.$element.trigger('loaded' + EVENT_KEY);\n        });\n      });\n\n      if (this.options.dropdownAlignRight === true) this.$menu[0].classList.add(classNames.MENURIGHT);\n\n      if (typeof id !== 'undefined') {\n        this.$button.attr('data-id', id);\n      }\n\n      this.checkDisabled();\n      this.clickListener();\n\n      if (version.major > 4) this.dropdown = new Dropdown(this.$button[0]);\n\n      if (this.options.liveSearch) {\n        this.liveSearchListener();\n        this.focusedParent = this.$searchbox[0];\n      } else {\n        this.focusedParent = this.$menuInner[0];\n      }\n\n      this.setStyle();\n      this.setWidth();\n      if (this.options.container) {\n        this.selectPosition();\n      } else {\n        this.$element.on('hide' + EVENT_KEY, function () {\n          if (that.isVirtual()) {\n            // empty menu on close\n            var menuInner = that.$menuInner[0],\n                emptyMenu = menuInner.firstChild.cloneNode(false);\n\n            // replace the existing UL with an empty one - this is faster than $.empty() or innerHTML = ''\n            menuInner.replaceChild(emptyMenu, menuInner.firstChild);\n            menuInner.scrollTop = 0;\n          }\n        });\n      }\n      this.$menu.data('this', this);\n      this.$newElement.data('this', this);\n      if (this.options.mobile) this.mobile();\n\n      this.$newElement.on({\n        'hide.bs.dropdown': function (e) {\n          that.$element.trigger('hide' + EVENT_KEY, e);\n        },\n        'hidden.bs.dropdown': function (e) {\n          that.$element.trigger('hidden' + EVENT_KEY, e);\n        },\n        'show.bs.dropdown': function (e) {\n          that.$element.trigger('show' + EVENT_KEY, e);\n        },\n        'shown.bs.dropdown': function (e) {\n          that.$element.trigger('shown' + EVENT_KEY, e);\n        }\n      });\n\n      if (element.hasAttribute('required')) {\n        this.$element.on('invalid' + EVENT_KEY, function () {\n          that.$button[0].classList.add('bs-invalid');\n\n          that.$element\n              .on('shown' + EVENT_KEY + '.invalid', function () {\n                that.$element\n                    .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened\n                    .off('shown' + EVENT_KEY + '.invalid');\n              })\n              .on('rendered' + EVENT_KEY, function () {\n                // if select is no longer invalid, remove the bs-invalid class\n                if (this.validity.valid) that.$button[0].classList.remove('bs-invalid');\n                that.$element.off('rendered' + EVENT_KEY);\n              });\n\n          that.$button.on('blur' + EVENT_KEY, function () {\n            that.$element.trigger('focus').trigger('blur');\n            that.$button.off('blur' + EVENT_KEY);\n          });\n        });\n      }\n\n      if (form) {\n        $(form).on('reset' + EVENT_KEY, function () {\n          requestAnimationFrame(function () {\n            that.render();\n          });\n        });\n      }\n    },\n\n    createDropdown: function () {\n      // Options\n      // If we are multiple or showTick option is set, then add the show-tick class\n      var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '',\n          multiselectable = this.multiple ? ' aria-multiselectable=\"true\"' : '',\n          inputGroup = '',\n          autofocus = this.autofocus ? ' autofocus' : '';\n\n      if (version.major < 4 && this.$element.parent().hasClass('input-group')) {\n        inputGroup = ' input-group-btn';\n      }\n\n      // Elements\n      var drop,\n          header = '',\n          searchbox = '',\n          actionsbox = '',\n          donebutton = '',\n          clearButton = '';\n\n      if (this.options.header) {\n        header =\n            '<div class=\"' + classNames.POPOVERHEADER + '\">' +\n            '<button type=\"button\" class=\"close\" aria-hidden=\"true\">&times;</button>' +\n            this.options.header +\n            '</div>';\n      }\n\n      if (this.options.liveSearch) {\n        searchbox =\n            '<div class=\"bs-searchbox\">' +\n            '<input type=\"search\" class=\"form-control\" autocomplete=\"off\"' +\n            (\n                this.options.liveSearchPlaceholder === null ? ''\n                    :\n                    ' placeholder=\"' + htmlEscape(this.options.liveSearchPlaceholder) + '\"'\n            ) +\n            ' role=\"combobox\" aria-label=\"Search\" aria-controls=\"' + this.selectId + '\" aria-autocomplete=\"list\">' +\n            '</div>';\n      }\n\n      if (this.multiple && this.options.actionsBox) {\n        actionsbox =\n            '<div class=\"bs-actionsbox\">' +\n            '<div class=\"btn-group btn-group-sm\">' +\n            '<button type=\"button\" class=\"actions-btn bs-select-all btn ' + classNames.BUTTONCLASS + '\">' +\n            this.options.selectAllText +\n            '</button>' +\n            '<button type=\"button\" class=\"actions-btn bs-deselect-all btn ' + classNames.BUTTONCLASS + '\">' +\n            this.options.deselectAllText +\n            '</button>' +\n            '</div>' +\n            '</div>';\n      }\n\n      if (this.multiple && this.options.doneButton) {\n        donebutton =\n            '<div class=\"bs-donebutton\">' +\n            '<div class=\"btn-group\">' +\n            '<button type=\"button\" class=\"btn btn-sm ' + classNames.BUTTONCLASS + '\">' +\n            this.options.doneButtonText +\n            '</button>' +\n            '</div>' +\n            '</div>';\n      }\n\n      if (this.options.allowClear) {\n        clearButton = '<span class=\"close bs-select-clear-selected\" title=\"' + this.options.deselectAllText + '\"><span>&times;</span>';\n      }\n\n      drop =\n          '<div class=\"dropdown bootstrap-select' + showTick + inputGroup + '\">' +\n          '<button type=\"button\" tabindex=\"-1\" class=\"' +\n          this.options.styleBase +\n          ' dropdown-toggle\" ' +\n          (this.options.display === 'static' ? 'data-display=\"static\"' : '') +\n          Selector.DATA_TOGGLE +\n          autofocus +\n          ' role=\"combobox\" aria-owns=\"' +\n          this.selectId +\n          '\" aria-haspopup=\"listbox\" aria-expanded=\"false\">' +\n          '<div class=\"filter-option\">' +\n          '<div class=\"filter-option-inner\">' +\n          '<div class=\"filter-option-inner-inner\">&nbsp;</div>' +\n          '</div> ' +\n          '</div>' +\n          clearButton +\n          '</span>' +\n          (\n              version.major >= '4' ? ''\n                  :\n                  '<span class=\"bs-caret\">' +\n                  this.options.template.caret +\n                  '</span>'\n          ) +\n          '</button>' +\n          '<div class=\"' + classNames.MENU + ' ' + (version.major >= '4' ? '' : classNames.SHOW) + '\">' +\n          header +\n          searchbox +\n          actionsbox +\n          '<div class=\"inner ' + classNames.SHOW + '\" role=\"listbox\" id=\"' + this.selectId + '\" tabindex=\"-1\" ' + multiselectable + '>' +\n          '<ul class=\"' + classNames.MENU + ' inner ' + (version.major >= '4' ? classNames.SHOW : '') + '\" role=\"presentation\">' +\n          '</ul>' +\n          '</div>' +\n          donebutton +\n          '</div>' +\n          '</div>';\n\n      return $(drop);\n    },\n\n    setPositionData: function () {\n      this.selectpicker.view.canHighlight = [];\n      this.selectpicker.view.size = 0;\n      this.selectpicker.view.firstHighlightIndex = false;\n\n      for (var i = 0; i < this.selectpicker.current.data.length; i++) {\n        var li = this.selectpicker.current.data[i],\n            canHighlight = true;\n\n        if (li.type === 'divider') {\n          canHighlight = false;\n          li.height = this.sizeInfo.dividerHeight;\n        } else if (li.type === 'optgroup-label') {\n          canHighlight = false;\n          li.height = this.sizeInfo.dropdownHeaderHeight;\n        } else {\n          li.height = this.sizeInfo.liHeight;\n        }\n\n        if (li.disabled) canHighlight = false;\n\n        this.selectpicker.view.canHighlight.push(canHighlight);\n\n        if (canHighlight) {\n          this.selectpicker.view.size++;\n          li.posinset = this.selectpicker.view.size;\n          if (this.selectpicker.view.firstHighlightIndex === false) this.selectpicker.view.firstHighlightIndex = i;\n        }\n\n        li.position = (i === 0 ? 0 : this.selectpicker.current.data[i - 1].position) + li.height;\n      }\n    },\n\n    isVirtual: function () {\n      return (this.options.virtualScroll !== false) && (this.selectpicker.main.data.length >= this.options.virtualScroll) || this.options.virtualScroll === true;\n    },\n\n    createView: function (isSearching, setSize, refresh) {\n      var that = this,\n          scrollTop = 0;\n\n      this.selectpicker.isSearching = isSearching;\n      this.selectpicker.current = isSearching ? this.selectpicker.search : this.selectpicker.main;\n\n      this.setPositionData();\n\n      if (setSize) {\n        if (refresh) {\n          scrollTop = this.$menuInner[0].scrollTop;\n        } else if (!that.multiple) {\n          var element = that.$element[0],\n              selectedIndex = (element.options[element.selectedIndex] || {}).liIndex;\n\n          if (typeof selectedIndex === 'number' && that.options.size !== false) {\n            var selectedData = that.selectpicker.main.data[selectedIndex],\n                position = selectedData && selectedData.position;\n\n            if (position) {\n              scrollTop = position - ((that.sizeInfo.menuInnerHeight + that.sizeInfo.liHeight) / 2);\n            }\n          }\n        }\n      }\n\n      scroll(scrollTop, true);\n\n      this.$menuInner.off('scroll.createView').on('scroll.createView', function (e, updateValue) {\n        if (!that.noScroll) scroll(this.scrollTop, updateValue);\n        that.noScroll = false;\n      });\n\n      function scroll (scrollTop, init) {\n        var size = that.selectpicker.current.data.length,\n            chunks = [],\n            chunkSize,\n            chunkCount,\n            firstChunk,\n            lastChunk,\n            currentChunk,\n            prevPositions,\n            positionIsDifferent,\n            previousElements,\n            menuIsDifferent = true,\n            isVirtual = that.isVirtual();\n\n        that.selectpicker.view.scrollTop = scrollTop;\n\n        chunkSize = that.options.chunkSize; // number of options in a chunk\n        chunkCount = Math.ceil(size / chunkSize) || 1; // number of chunks\n\n        for (var i = 0; i < chunkCount; i++) {\n          var endOfChunk = (i + 1) * chunkSize;\n\n          if (i === chunkCount - 1) {\n            endOfChunk = size;\n          }\n\n          chunks[i] = [\n            (i) * chunkSize + (!i ? 0 : 1),\n            endOfChunk\n          ];\n\n          if (!size) break;\n\n          if (currentChunk === undefined && scrollTop - 1 <= that.selectpicker.current.data[endOfChunk - 1].position - that.sizeInfo.menuInnerHeight) {\n            currentChunk = i;\n          }\n        }\n\n        if (currentChunk === undefined) currentChunk = 0;\n\n        prevPositions = [that.selectpicker.view.position0, that.selectpicker.view.position1];\n\n        // always display previous, current, and next chunks\n        firstChunk = Math.max(0, currentChunk - 1);\n        lastChunk = Math.min(chunkCount - 1, currentChunk + 1);\n\n        that.selectpicker.view.position0 = isVirtual === false ? 0 : (Math.max(0, chunks[firstChunk][0]) || 0);\n        that.selectpicker.view.position1 = isVirtual === false ? size : (Math.min(size, chunks[lastChunk][1]) || 0);\n\n        positionIsDifferent = prevPositions[0] !== that.selectpicker.view.position0 || prevPositions[1] !== that.selectpicker.view.position1;\n\n        if (that.activeElement !== undefined) {\n          if (init) {\n            if (that.activeElement !== that.selectedElement) {\n              that.defocusItem(that.activeElement);\n            }\n            that.activeElement = undefined;\n          }\n\n          if (that.activeElement !== that.selectedElement) {\n            that.defocusItem(that.selectedElement);\n          }\n        }\n\n        if (that.prevActiveElement !== undefined && that.prevActiveElement !== that.activeElement && that.prevActiveElement !== that.selectedElement) {\n          that.defocusItem(that.prevActiveElement);\n        }\n\n        if (init || positionIsDifferent || that.selectpicker.current.hasMore) {\n          previousElements = that.selectpicker.view.visibleElements ? that.selectpicker.view.visibleElements.slice() : [];\n\n          if (isVirtual === false) {\n            that.selectpicker.view.visibleElements = that.selectpicker.current.elements;\n          } else {\n            that.selectpicker.view.visibleElements = that.selectpicker.current.elements.slice(that.selectpicker.view.position0, that.selectpicker.view.position1);\n          }\n\n          that.setOptionStatus();\n\n          // if searching, check to make sure the list has actually been updated before updating DOM\n          // this prevents unnecessary repaints\n          if (isSearching || (isVirtual === false && init)) menuIsDifferent = !isEqual(previousElements, that.selectpicker.view.visibleElements);\n\n          // if virtual scroll is disabled and not searching,\n          // menu should never need to be updated more than once\n          if ((init || isVirtual === true) && menuIsDifferent) {\n            var menuInner = that.$menuInner[0],\n                menuFragment = document.createDocumentFragment(),\n                emptyMenu = menuInner.firstChild.cloneNode(false),\n                marginTop,\n                marginBottom,\n                elements = that.selectpicker.view.visibleElements,\n                toSanitize = [];\n\n            // replace the existing UL with an empty one - this is faster than $.empty()\n            menuInner.replaceChild(emptyMenu, menuInner.firstChild);\n\n            for (var i = 0, visibleElementsLen = elements.length; i < visibleElementsLen; i++) {\n              var element = elements[i],\n                  elText,\n                  elementData;\n\n              if (that.options.sanitize) {\n                elText = element.lastChild;\n\n                if (elText) {\n                  elementData = that.selectpicker.current.data[i + that.selectpicker.view.position0];\n\n                  if (elementData && elementData.content && !elementData.sanitized) {\n                    toSanitize.push(elText);\n                    elementData.sanitized = true;\n                  }\n                }\n              }\n\n              menuFragment.appendChild(element);\n            }\n\n            if (that.options.sanitize && toSanitize.length) {\n              sanitizeHtml(toSanitize, that.options.whiteList, that.options.sanitizeFn);\n            }\n\n            if (isVirtual === true) {\n              marginTop = (that.selectpicker.view.position0 === 0 ? 0 : that.selectpicker.current.data[that.selectpicker.view.position0 - 1].position);\n              marginBottom = (that.selectpicker.view.position1 > size - 1 ? 0 : that.selectpicker.current.data[size - 1].position - that.selectpicker.current.data[that.selectpicker.view.position1 - 1].position);\n\n              menuInner.firstChild.style.marginTop = marginTop + 'px';\n              menuInner.firstChild.style.marginBottom = marginBottom + 'px';\n            } else {\n              menuInner.firstChild.style.marginTop = 0;\n              menuInner.firstChild.style.marginBottom = 0;\n            }\n\n            menuInner.firstChild.appendChild(menuFragment);\n\n            // if an option is encountered that is wider than the current menu width, update the menu width accordingly\n            // switch to ResizeObserver with increased browser support\n            if (isVirtual === true && that.sizeInfo.hasScrollBar) {\n              var menuInnerInnerWidth = menuInner.firstChild.offsetWidth;\n\n              if (init && menuInnerInnerWidth < that.sizeInfo.menuInnerInnerWidth && that.sizeInfo.totalMenuWidth > that.sizeInfo.selectWidth) {\n                menuInner.firstChild.style.minWidth = that.sizeInfo.menuInnerInnerWidth + 'px';\n              } else if (menuInnerInnerWidth > that.sizeInfo.menuInnerInnerWidth) {\n                // set to 0 to get actual width of menu\n                that.$menu[0].style.minWidth = 0;\n\n                var actualMenuWidth = menuInner.firstChild.offsetWidth;\n\n                if (actualMenuWidth > that.sizeInfo.menuInnerInnerWidth) {\n                  that.sizeInfo.menuInnerInnerWidth = actualMenuWidth;\n                  menuInner.firstChild.style.minWidth = that.sizeInfo.menuInnerInnerWidth + 'px';\n                }\n\n                // reset to default CSS styling\n                that.$menu[0].style.minWidth = '';\n              }\n            }\n          }\n\n          if ((!isSearching && that.options.source.data || isSearching && that.options.source.search) && that.selectpicker.current.hasMore && currentChunk === chunkCount - 1) {\n            // Don't load the next chunk until scrolling has started\n            // This prevents unnecessary requests while the user is typing if pageSize is <= chunkSize\n            if (scrollTop > 0) {\n              // Chunks use 0-based indexing, but pages use 1-based. Add 1 to convert and add 1 again to get next page\n              var page = Math.floor((currentChunk * that.options.chunkSize) / that.options.source.pageSize) + 2;\n\n              that.fetchData(function () {\n                that.render();\n                that.buildList(size, isSearching);\n                that.setPositionData();\n                scroll(scrollTop);\n              }, isSearching ? 'search' : 'data', page, isSearching ? that.selectpicker.search.previousValue : undefined);\n            }\n          }\n        }\n\n        that.prevActiveElement = that.activeElement;\n\n        if (!that.options.liveSearch) {\n          that.$menuInner.trigger('focus');\n        } else if (isSearching && init) {\n          var index = 0,\n              newActive;\n\n          if (!that.selectpicker.view.canHighlight[index]) {\n            index = 1 + that.selectpicker.view.canHighlight.slice(1).indexOf(true);\n          }\n\n          newActive = that.selectpicker.view.visibleElements[index];\n\n          that.defocusItem(that.selectpicker.view.currentActive);\n\n          that.activeElement = (that.selectpicker.current.data[index] || {}).element;\n\n          that.focusItem(newActive);\n        }\n      }\n\n      $(window)\n          .off('resize' + EVENT_KEY + '.' + this.selectId + '.createView')\n          .on('resize' + EVENT_KEY + '.' + this.selectId + '.createView', function () {\n            var isActive = that.$newElement.hasClass(classNames.SHOW);\n\n            if (isActive) scroll(that.$menuInner[0].scrollTop);\n          });\n    },\n\n    focusItem: function (li, liData, noStyle) {\n      if (li) {\n        liData = liData || this.selectpicker.current.data[this.selectpicker.current.elements.indexOf(this.activeElement)];\n        var a = li.firstChild;\n\n        if (a) {\n          a.setAttribute('aria-setsize', this.selectpicker.view.size);\n          a.setAttribute('aria-posinset', liData.posinset);\n\n          if (noStyle !== true) {\n            this.focusedParent.setAttribute('aria-activedescendant', a.id);\n            li.classList.add('active');\n            a.classList.add('active');\n          }\n        }\n      }\n    },\n\n    defocusItem: function (li) {\n      if (li) {\n        li.classList.remove('active');\n        if (li.firstChild) li.firstChild.classList.remove('active');\n      }\n    },\n\n    setPlaceholder: function () {\n      var that = this,\n          updateIndex = false;\n\n      if ((this.options.placeholder || this.options.allowClear) && !this.multiple) {\n        if (!this.selectpicker.view.titleOption) this.selectpicker.view.titleOption = document.createElement('option');\n\n        // this option doesn't create a new <li> element, but does add a new option at the start,\n        // so startIndex should increase to prevent having to check every option for the bs-title-option class\n        updateIndex = true;\n\n        var element = this.$element[0],\n            selectTitleOption = false,\n            titleNotAppended = !this.selectpicker.view.titleOption.parentNode,\n            selectedIndex = element.selectedIndex,\n            selectedOption = element.options[selectedIndex],\n            firstSelectable = element.querySelector('select > *:not(:disabled)'),\n            firstSelectableIndex = firstSelectable ? firstSelectable.index : 0,\n            navigation = window.performance && window.performance.getEntriesByType('navigation'),\n            // Safari doesn't support getEntriesByType('navigation') - fall back to performance.navigation\n            isNotBackForward = (navigation && navigation.length) ? navigation[0].type !== 'back_forward' : window.performance.navigation.type !== 2;\n\n        if (titleNotAppended) {\n          // Use native JS to prepend option (faster)\n          this.selectpicker.view.titleOption.className = 'bs-title-option';\n          this.selectpicker.view.titleOption.value = '';\n\n          // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option.\n          // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs,\n          // if so, the select will have the data-selected attribute\n          selectTitleOption = !selectedOption || (selectedIndex === firstSelectableIndex && selectedOption.defaultSelected === false && this.$element.data('selected') === undefined);\n        }\n\n        if (titleNotAppended || this.selectpicker.view.titleOption.index !== 0) {\n          element.insertBefore(this.selectpicker.view.titleOption, element.firstChild);\n        }\n\n        // Set selected *after* appending to select,\n        // otherwise the option doesn't get selected in IE\n        // set using selectedIndex, as setting the selected attr to true here doesn't work in IE11\n        if (selectTitleOption && isNotBackForward) {\n          element.selectedIndex = 0;\n        } else if (document.readyState !== 'complete') {\n          // if navigation type is back_forward, there's a chance the select will have its value set by BFCache\n          // wait for that value to be set, then run render again\n          window.addEventListener('pageshow', function () {\n            if (that.selectpicker.view.displayedValue !== element.value) that.render();\n          });\n        }\n      }\n\n      return updateIndex;\n    },\n\n    fetchData: function (callback, type, page, searchValue) {\n      page = page || 1;\n      type = type || 'data';\n\n      var that = this,\n          data = this.options.source[type],\n          builtData;\n\n      if (data) {\n        this.options.virtualScroll = true;\n\n        if (typeof data === 'function') {\n          data.call(\n              this,\n              function (data, more, totalItems) {\n                var current = that.selectpicker[type === 'search' ? 'search' : 'main'];\n                current.hasMore = more;\n                current.totalItems = totalItems;\n                builtData = that.buildData(data, type);\n                callback.call(that, builtData);\n                that.$element.trigger('fetched' + EVENT_KEY);\n              },\n              page,\n              searchValue\n          );\n        } else if (Array.isArray(data)) {\n          builtData = that.buildData(data, type);\n          callback.call(that, builtData);\n        }\n      } else {\n        builtData = this.buildData(false, type);\n        callback.call(that, builtData);\n      }\n    },\n\n    buildData: function (data, type) {\n      var that = this;\n      var dataGetter = data === false ? getOptionData.fromOption : getOptionData.fromDataSource;\n\n      var optionSelector = ':not([hidden]):not([data-hidden=\"true\"]):not([style*=\"display: none\"])',\n          mainData = [],\n          startLen = this.selectpicker.main.data ? this.selectpicker.main.data.length : 0,\n          optID = 0,\n          startIndex = this.setPlaceholder() && !data ? 1 : 0; // append the titleOption if necessary and skip the first option in the loop\n\n      if (type === 'search') {\n        startLen = this.selectpicker.search.data.length;\n      }\n\n      if (this.options.hideDisabled) optionSelector += ':not(:disabled)';\n\n      var selectOptions = data ? data.filter(filterHidden, this) : this.$element[0].querySelectorAll('select > *' + optionSelector);\n\n      function addDivider (config) {\n        var previousData = mainData[mainData.length - 1];\n\n        // ensure optgroup doesn't create back-to-back dividers\n        if (\n            previousData &&\n            previousData.type === 'divider' &&\n            (previousData.optID || config.optID)\n        ) {\n          return;\n        }\n\n        config = config || {};\n        config.type = 'divider';\n\n        mainData.push(config);\n      }\n\n      function addOption (item, config) {\n        config = config || {};\n\n        config.divider = dataGetter(item, 'divider');\n\n        if (config.divider === true) {\n          addDivider({\n            optID: config.optID\n          });\n        } else {\n          var liIndex = mainData.length + startLen,\n              cssText = dataGetter(item, 'style'),\n              inlineStyle = cssText ? htmlEscape(cssText) : '',\n              optionClass = (item.className || '') + (config.optgroupClass || '');\n\n          if (config.optID) optionClass = 'opt ' + optionClass;\n\n          config.optionClass = optionClass.trim();\n          config.inlineStyle = inlineStyle;\n\n          config.text = dataGetter(item, 'text');\n          config.title = dataGetter(item, 'title');\n          config.content = dataGetter(item, 'content');\n          config.tokens = dataGetter(item, 'tokens');\n          config.subtext = dataGetter(item, 'subtext');\n          config.icon = dataGetter(item, 'icon');\n\n          config.display = config.content || config.text;\n          config.value = item.value === undefined ? item.text : item.value;\n          config.type = 'option';\n          config.index = liIndex;\n\n          config.option = !item.option ? item : item.option; // reference option element if it exists\n          config.option.liIndex = liIndex;\n          config.selected = !!item.selected;\n          config.disabled = config.disabled || !!item.disabled;\n\n          if (data !== false) {\n            if (that.selectpicker.optionValuesDataMap[config.value]) {\n              config = $.extend(that.selectpicker.optionValuesDataMap[config.value], config);\n            } else {\n              that.selectpicker.optionValuesDataMap[config.value] = config;\n            }\n          }\n\n          mainData.push(config);\n        }\n      }\n\n      function addOptgroup (index, selectOptions) {\n        var optgroup = selectOptions[index],\n            // skip placeholder option\n            previous = index - 1 < startIndex ? false : selectOptions[index - 1],\n            next = selectOptions[index + 1],\n            options = data ? optgroup.children.filter(filterHidden, this) : optgroup.querySelectorAll('option' + optionSelector);\n\n        if (!options.length) return;\n\n        var config = {\n              display: htmlEscape(dataGetter(item, 'label')),\n              subtext: dataGetter(optgroup, 'subtext'),\n              icon: dataGetter(optgroup, 'icon'),\n              type: 'optgroup-label',\n              optgroupClass: ' ' + (optgroup.className || ''),\n              optgroup: optgroup\n            },\n            headerIndex,\n            lastIndex;\n\n        optID++;\n\n        if (previous) {\n          addDivider({ optID: optID });\n        }\n\n        config.optID = optID;\n\n        mainData.push(config);\n\n        for (var j = 0, len = options.length; j < len; j++) {\n          var option = options[j];\n\n          if (j === 0) {\n            headerIndex = mainData.length - 1;\n            lastIndex = headerIndex + len;\n          }\n\n          addOption(option, {\n            headerIndex: headerIndex,\n            lastIndex: lastIndex,\n            optID: config.optID,\n            optgroupClass: config.optgroupClass,\n            disabled: optgroup.disabled\n          });\n        }\n\n        if (next) {\n          addDivider({ optID: optID });\n        }\n      }\n\n      for (var len = selectOptions.length, i = startIndex; i < len; i++) {\n        var item = selectOptions[i],\n            children = item.children;\n\n        if (children && children.length) {\n          addOptgroup.call(this, i, selectOptions);\n        } else {\n          addOption.call(this, item, {});\n        }\n      }\n\n      switch (type) {\n        case 'data': {\n          if (!this.selectpicker.main.data) {\n            this.selectpicker.main.data = [];\n          }\n          Array.prototype.push.apply(this.selectpicker.main.data, mainData);\n          this.selectpicker.current.data = this.selectpicker.main.data;\n          break;\n        }\n        case 'search': {\n          Array.prototype.push.apply(this.selectpicker.search.data, mainData);\n          break;\n        }\n      }\n\n      return mainData;\n    },\n\n    buildList: function (size, searching) {\n      var that = this,\n          selectData = searching ? this.selectpicker.search.data : this.selectpicker.main.data,\n          mainElements = [],\n          widestOptionLength = 0;\n\n      if ((that.options.showTick || that.multiple) && !elementTemplates.checkMark.parentNode) {\n        elementTemplates.checkMark.className = this.options.iconBase + ' ' + that.options.tickIcon + ' check-mark';\n        elementTemplates.a.appendChild(elementTemplates.checkMark);\n      }\n\n      function buildElement (mainElements, item) {\n        var liElement,\n            combinedLength = 0;\n\n        switch (item.type) {\n          case 'divider':\n            liElement = generateOption.li(\n                false,\n                classNames.DIVIDER,\n                (item.optID ? item.optID + 'div' : undefined)\n            );\n\n            break;\n\n          case 'option':\n            liElement = generateOption.li(\n                generateOption.a(\n                    generateOption.text.call(that, item),\n                    item.optionClass,\n                    item.inlineStyle\n                ),\n                '',\n                item.optID\n            );\n\n            if (liElement.firstChild) {\n              liElement.firstChild.id = that.selectId + '-' + item.index;\n            }\n\n            break;\n\n          case 'optgroup-label':\n            liElement = generateOption.li(\n                generateOption.label.call(that, item),\n                'dropdown-header' + item.optgroupClass,\n                item.optID\n            );\n\n            break;\n        }\n\n        if (!item.element) {\n          item.element = liElement;\n        } else {\n          item.element.innerHTML = liElement.innerHTML;\n        }\n        mainElements.push(item.element);\n\n        // count the number of characters in the option - not perfect, but should work in most cases\n        if (item.display) combinedLength += item.display.length;\n        if (item.subtext) combinedLength += item.subtext.length;\n        // if there is an icon, ensure this option's width is checked\n        if (item.icon) combinedLength += 1;\n\n        if (combinedLength > widestOptionLength) {\n          widestOptionLength = combinedLength;\n\n          // guess which option is the widest\n          // use this when calculating menu width\n          // not perfect, but it's fast, and the width will be updating accordingly when scrolling\n          that.selectpicker.view.widestOption = mainElements[mainElements.length - 1];\n        }\n      }\n\n      var startIndex = size || 0;\n\n      for (var len = selectData.length, i = startIndex; i < len; i++) {\n        var item = selectData[i];\n\n        buildElement(mainElements, item);\n      }\n\n      if (size) {\n        if (searching) {\n          Array.prototype.push.apply(this.selectpicker.search.elements, mainElements);\n        } else {\n          Array.prototype.push.apply(this.selectpicker.main.elements, mainElements);\n          this.selectpicker.current.elements = this.selectpicker.main.elements;\n        }\n      } else {\n        if (searching) {\n          this.selectpicker.search.elements = mainElements;\n        } else {\n          this.selectpicker.main.elements = this.selectpicker.current.elements = mainElements;\n        }\n      }\n    },\n\n    findLis: function () {\n      return this.$menuInner.find('.inner > li');\n    },\n\n    render: function (init) {\n      var that = this,\n          element = this.$element[0],\n          // ensure titleOption is appended and selected (if necessary) before getting selectedOptions\n          placeholderSelected = this.setPlaceholder() && element.selectedIndex === 0,\n          selectedOptions = getSelectedOptions.call(this),\n          selectedCount = selectedOptions.length,\n          selectedValues = getSelectValues.call(this, selectedOptions),\n          button = this.$button[0],\n          buttonInner = button.querySelector('.filter-option-inner-inner'),\n          multipleSeparator = document.createTextNode(this.options.multipleSeparator),\n          titleFragment = elementTemplates.fragment.cloneNode(false),\n          showCount,\n          countMax,\n          hasContent = false;\n\n      function createSelected (item) {\n        if (item.selected) {\n          that.createOption(item, true);\n        } else if (item.children && item.children.length) {\n          item.children.map(createSelected);\n        }\n      }\n\n      // create selected option elements to ensure select value is correct\n      if (this.options.source.data && init) {\n        selectedOptions.map(createSelected);\n        element.appendChild(this.selectpicker.main.optionQueue);\n\n        if (placeholderSelected) placeholderSelected = element.selectedIndex === 0;\n      }\n\n      button.classList.toggle('bs-placeholder', that.multiple ? !selectedCount : !selectedValues && selectedValues !== 0);\n\n      if (!that.multiple && selectedOptions.length === 1) {\n        that.selectpicker.view.displayedValue = selectedValues;\n      }\n\n      if (this.options.selectedTextFormat === 'static') {\n        titleFragment = generateOption.text.call(this, { text: this.options.placeholder }, true);\n      } else {\n        showCount = this.multiple && this.options.selectedTextFormat.indexOf('count') !== -1 && selectedCount > 0;\n\n        // determine if the number of selected options will be shown (showCount === true)\n        if (showCount) {\n          countMax = this.options.selectedTextFormat.split('>');\n          showCount = (countMax.length > 1 && selectedCount > countMax[1]) || (countMax.length === 1 && selectedCount >= 2);\n        }\n\n        // only loop through all selected options if the count won't be shown\n        if (showCount === false) {\n          if (!placeholderSelected) {\n            for (var selectedIndex = 0; selectedIndex < selectedCount; selectedIndex++) {\n              if (selectedIndex < 50) {\n                var option = selectedOptions[selectedIndex],\n                    titleOptions = {};\n\n                if (option) {\n                  if (this.multiple && selectedIndex > 0) {\n                    titleFragment.appendChild(multipleSeparator.cloneNode(false));\n                  }\n\n                  if (option.title) {\n                    titleOptions.text = option.title;\n                  } else if (option.content && that.options.showContent) {\n                    titleOptions.content = option.content.toString();\n                    hasContent = true;\n                  } else {\n                    if (that.options.showIcon) {\n                      titleOptions.icon = option.icon;\n                    }\n                    if (that.options.showSubtext && !that.multiple && option.subtext) titleOptions.subtext = ' ' + option.subtext;\n                    titleOptions.text = option.text.trim();\n                  }\n\n                  titleFragment.appendChild(generateOption.text.call(this, titleOptions, true));\n                }\n              } else {\n                break;\n              }\n            }\n\n            // add ellipsis\n            if (selectedCount > 49) {\n              titleFragment.appendChild(document.createTextNode('...'));\n            }\n          }\n        } else {\n          var optionSelector = ':not([hidden]):not([data-hidden=\"true\"]):not([data-divider=\"true\"]):not([style*=\"display: none\"])';\n          if (this.options.hideDisabled) optionSelector += ':not(:disabled)';\n\n          // If this is a multiselect, and selectedTextFormat is count, then show 1 of 2 selected, etc.\n          var totalCount = this.$element[0].querySelectorAll('select > option' + optionSelector + ', optgroup' + optionSelector + ' option' + optionSelector).length,\n              tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedCount, totalCount) : this.options.countSelectedText;\n\n          titleFragment = generateOption.text.call(this, {\n            text: tr8nText.replace('{0}', selectedCount.toString()).replace('{1}', totalCount.toString())\n          }, true);\n        }\n      }\n\n      // If the select doesn't have a title, then use the default, or if nothing is set at all, use noneSelectedText\n      if (!titleFragment.childNodes.length) {\n        titleFragment = generateOption.text.call(this, {\n          text: this.options.placeholder ? this.options.placeholder : this.options.noneSelectedText\n        }, true);\n      }\n\n      // if the select has a title, apply it to the button, and if not, apply titleFragment text\n      // strip all HTML tags and trim the result, then unescape any escaped tags\n      button.title = titleFragment.textContent.replace(/<[^>]*>?/g, '').trim();\n\n      if (this.options.sanitize && hasContent) {\n        sanitizeHtml([titleFragment], that.options.whiteList, that.options.sanitizeFn);\n      }\n\n      buttonInner.innerHTML = '';\n      buttonInner.appendChild(titleFragment);\n\n      if (version.major < 4 && this.$newElement[0].classList.contains('bs3-has-addon')) {\n        var filterExpand = button.querySelector('.filter-expand'),\n            clone = buttonInner.cloneNode(true);\n\n        clone.className = 'filter-expand';\n\n        if (filterExpand) {\n          button.replaceChild(clone, filterExpand);\n        } else {\n          button.appendChild(clone);\n        }\n      }\n\n      this.$element.trigger('rendered' + EVENT_KEY);\n    },\n\n    /**\n     * @param [style]\n     * @param [status]\n     */\n    setStyle: function (newStyle, status) {\n      var button = this.$button[0],\n          newElement = this.$newElement[0],\n          style = this.options.style.trim(),\n          buttonClass;\n\n      if (this.$element.attr('class')) {\n        this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\\[.*\\]/gi, ''));\n      }\n\n      if (version.major < 4) {\n        newElement.classList.add('bs3');\n\n        if (newElement.parentNode.classList && newElement.parentNode.classList.contains('input-group') &&\n            (newElement.previousElementSibling || newElement.nextElementSibling) &&\n            (newElement.previousElementSibling || newElement.nextElementSibling).classList.contains('input-group-addon')\n        ) {\n          newElement.classList.add('bs3-has-addon');\n        }\n      }\n\n      if (newStyle) {\n        buttonClass = newStyle.trim();\n      } else {\n        buttonClass = style;\n      }\n\n      if (status == 'add') {\n        if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' '));\n      } else if (status == 'remove') {\n        if (buttonClass) button.classList.remove.apply(button.classList, buttonClass.split(' '));\n      } else {\n        if (style) button.classList.remove.apply(button.classList, style.split(' '));\n        if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' '));\n      }\n    },\n\n    liHeight: function (refresh) {\n      if (!refresh && (this.options.size === false || Object.keys(this.sizeInfo).length)) return;\n\n      var newElement = elementTemplates.div.cloneNode(false),\n          menu = elementTemplates.div.cloneNode(false),\n          menuInner = elementTemplates.div.cloneNode(false),\n          menuInnerInner = document.createElement('ul'),\n          divider = elementTemplates.li.cloneNode(false),\n          dropdownHeader = elementTemplates.li.cloneNode(false),\n          li,\n          a = elementTemplates.a.cloneNode(false),\n          text = elementTemplates.span.cloneNode(false),\n          header = this.options.header && this.$menu.find('.' + classNames.POPOVERHEADER).length > 0 ? this.$menu.find('.' + classNames.POPOVERHEADER)[0].cloneNode(true) : null,\n          search = this.options.liveSearch ? elementTemplates.div.cloneNode(false) : null,\n          actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null,\n          doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null,\n          firstOption = this.$element[0].options[0];\n\n      this.sizeInfo.selectWidth = this.$newElement[0].offsetWidth;\n\n      text.className = 'text';\n      a.className = 'dropdown-item ' + (firstOption ? firstOption.className : '');\n      newElement.className = this.$menu[0].parentNode.className + ' ' + classNames.SHOW;\n      newElement.style.width = 0; // ensure button width doesn't affect natural width of menu when calculating\n      if (this.options.width === 'auto') menu.style.minWidth = 0;\n      menu.className = classNames.MENU + ' ' + classNames.SHOW;\n      menuInner.className = 'inner ' + classNames.SHOW;\n      menuInnerInner.className = classNames.MENU + ' inner ' + (version.major >= '4' ? classNames.SHOW : '');\n      divider.className = classNames.DIVIDER;\n      dropdownHeader.className = 'dropdown-header';\n\n      text.appendChild(document.createTextNode('\\u200b'));\n\n      if (this.selectpicker.current.data.length) {\n        for (var i = 0; i < this.selectpicker.current.data.length; i++) {\n          var data = this.selectpicker.current.data[i];\n          if (data.type === 'option' && $(data.element.firstChild).css('display') !== 'none') {\n            li = data.element;\n            break;\n          }\n        }\n      } else {\n        li = elementTemplates.li.cloneNode(false);\n        a.appendChild(text);\n        li.appendChild(a);\n      }\n\n      dropdownHeader.appendChild(text.cloneNode(true));\n\n      if (this.selectpicker.view.widestOption) {\n        menuInnerInner.appendChild(this.selectpicker.view.widestOption.cloneNode(true));\n      }\n\n      menuInnerInner.appendChild(li);\n      menuInnerInner.appendChild(divider);\n      menuInnerInner.appendChild(dropdownHeader);\n      if (header) menu.appendChild(header);\n      if (search) {\n        var input = document.createElement('input');\n        search.className = 'bs-searchbox';\n        input.className = 'form-control';\n        search.appendChild(input);\n        menu.appendChild(search);\n      }\n      if (actions) menu.appendChild(actions);\n      menuInner.appendChild(menuInnerInner);\n      menu.appendChild(menuInner);\n      if (doneButton) menu.appendChild(doneButton);\n      newElement.appendChild(menu);\n\n      document.body.appendChild(newElement);\n\n      var liHeight = li.offsetHeight,\n          dropdownHeaderHeight = dropdownHeader ? dropdownHeader.offsetHeight : 0,\n          headerHeight = header ? header.offsetHeight : 0,\n          searchHeight = search ? search.offsetHeight : 0,\n          actionsHeight = actions ? actions.offsetHeight : 0,\n          doneButtonHeight = doneButton ? doneButton.offsetHeight : 0,\n          dividerHeight = $(divider).outerHeight(true),\n          menuStyle = window.getComputedStyle(menu),\n          menuWidth = menu.offsetWidth,\n          menuPadding = {\n            vert: toInteger(menuStyle.paddingTop) +\n                toInteger(menuStyle.paddingBottom) +\n                toInteger(menuStyle.borderTopWidth) +\n                toInteger(menuStyle.borderBottomWidth),\n            horiz: toInteger(menuStyle.paddingLeft) +\n                toInteger(menuStyle.paddingRight) +\n                toInteger(menuStyle.borderLeftWidth) +\n                toInteger(menuStyle.borderRightWidth)\n          },\n          menuExtras = {\n            vert: menuPadding.vert +\n                toInteger(menuStyle.marginTop) +\n                toInteger(menuStyle.marginBottom) + 2,\n            horiz: menuPadding.horiz +\n                toInteger(menuStyle.marginLeft) +\n                toInteger(menuStyle.marginRight) + 2\n          },\n          scrollBarWidth;\n\n      menuInner.style.overflowY = 'scroll';\n\n      scrollBarWidth = menu.offsetWidth - menuWidth;\n\n      document.body.removeChild(newElement);\n\n      this.sizeInfo.liHeight = liHeight;\n      this.sizeInfo.dropdownHeaderHeight = dropdownHeaderHeight;\n      this.sizeInfo.headerHeight = headerHeight;\n      this.sizeInfo.searchHeight = searchHeight;\n      this.sizeInfo.actionsHeight = actionsHeight;\n      this.sizeInfo.doneButtonHeight = doneButtonHeight;\n      this.sizeInfo.dividerHeight = dividerHeight;\n      this.sizeInfo.menuPadding = menuPadding;\n      this.sizeInfo.menuExtras = menuExtras;\n      this.sizeInfo.menuWidth = menuWidth;\n      this.sizeInfo.menuInnerInnerWidth = menuWidth - menuPadding.horiz;\n      this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth;\n      this.sizeInfo.scrollBarWidth = scrollBarWidth;\n      this.sizeInfo.selectHeight = this.$newElement[0].offsetHeight;\n\n      this.setPositionData();\n    },\n\n    getSelectPosition: function () {\n      var that = this,\n          $window = $(window),\n          pos = that.$newElement.offset(),\n          $container = $(that.options.container),\n          containerPos;\n\n      if (that.options.container && $container.length && !$container.is('body')) {\n        containerPos = $container.offset();\n        containerPos.top += parseInt($container.css('borderTopWidth'));\n        containerPos.left += parseInt($container.css('borderLeftWidth'));\n      } else {\n        containerPos = { top: 0, left: 0 };\n      }\n\n      var winPad = that.options.windowPadding;\n\n      this.sizeInfo.selectOffsetTop = pos.top - containerPos.top - $window.scrollTop();\n      this.sizeInfo.selectOffsetBot = $window.height() - this.sizeInfo.selectOffsetTop - this.sizeInfo.selectHeight - containerPos.top - winPad[2];\n      this.sizeInfo.selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft();\n      this.sizeInfo.selectOffsetRight = $window.width() - this.sizeInfo.selectOffsetLeft - this.sizeInfo.selectWidth - containerPos.left - winPad[1];\n      this.sizeInfo.selectOffsetTop -= winPad[0];\n      this.sizeInfo.selectOffsetLeft -= winPad[3];\n    },\n\n    setMenuSize: function (isAuto) {\n      this.getSelectPosition();\n\n      var selectWidth = this.sizeInfo.selectWidth,\n          liHeight = this.sizeInfo.liHeight,\n          headerHeight = this.sizeInfo.headerHeight,\n          searchHeight = this.sizeInfo.searchHeight,\n          actionsHeight = this.sizeInfo.actionsHeight,\n          doneButtonHeight = this.sizeInfo.doneButtonHeight,\n          divHeight = this.sizeInfo.dividerHeight,\n          menuPadding = this.sizeInfo.menuPadding,\n          menuInnerHeight,\n          menuHeight,\n          divLength = 0,\n          minHeight,\n          _minHeight,\n          maxHeight,\n          menuInnerMinHeight,\n          estimate,\n          isDropup;\n\n      if (this.options.dropupAuto) {\n        // Get the estimated height of the menu without scrollbars.\n        // This is useful for smaller menus, where there might be plenty of room\n        // below the button without setting dropup, but we can't know\n        // the exact height of the menu until createView is called later\n        estimate = liHeight * this.selectpicker.current.data.length + menuPadding.vert;\n\n        isDropup = this.sizeInfo.selectOffsetTop - this.sizeInfo.selectOffsetBot > this.sizeInfo.menuExtras.vert && estimate + this.sizeInfo.menuExtras.vert + 50 > this.sizeInfo.selectOffsetBot;\n\n        // ensure dropup doesn't change while searching (so menu doesn't bounce back and forth)\n        if (this.selectpicker.isSearching === true) {\n          isDropup = this.selectpicker.dropup;\n        }\n\n        this.$newElement.toggleClass(classNames.DROPUP, isDropup);\n        this.selectpicker.dropup = isDropup;\n      }\n\n      if (this.options.size === 'auto') {\n        _minHeight = this.selectpicker.current.data.length > 3 ? this.sizeInfo.liHeight * 3 + this.sizeInfo.menuExtras.vert - 2 : 0;\n        menuHeight = this.sizeInfo.selectOffsetBot - this.sizeInfo.menuExtras.vert;\n        minHeight = _minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight;\n        menuInnerMinHeight = Math.max(_minHeight - menuPadding.vert, 0);\n\n        if (this.$newElement.hasClass(classNames.DROPUP)) {\n          menuHeight = this.sizeInfo.selectOffsetTop - this.sizeInfo.menuExtras.vert;\n        }\n\n        maxHeight = menuHeight;\n        menuInnerHeight = menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert;\n      } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) {\n        for (var i = 0; i < this.options.size; i++) {\n          if (this.selectpicker.current.data[i].type === 'divider') divLength++;\n        }\n\n        menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert;\n        menuInnerHeight = menuHeight - menuPadding.vert;\n        maxHeight = menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight;\n        minHeight = menuInnerMinHeight = '';\n      }\n\n      this.$menu.css({\n        'max-height': maxHeight + 'px',\n        'overflow': 'hidden',\n        'min-height': minHeight + 'px'\n      });\n\n      this.$menuInner.css({\n        'max-height': menuInnerHeight + 'px',\n        'overflow': 'hidden auto',\n        'min-height': menuInnerMinHeight + 'px'\n      });\n\n      // ensure menuInnerHeight is always a positive number to prevent issues calculating chunkSize in createView\n      this.sizeInfo.menuInnerHeight = Math.max(menuInnerHeight, 1);\n\n      if (this.selectpicker.current.data.length && this.selectpicker.current.data[this.selectpicker.current.data.length - 1].position > this.sizeInfo.menuInnerHeight) {\n        this.sizeInfo.hasScrollBar = true;\n        this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth + this.sizeInfo.scrollBarWidth;\n      }\n\n      if (this.options.dropdownAlignRight === 'auto') {\n        this.$menu.toggleClass(classNames.MENURIGHT, this.sizeInfo.selectOffsetLeft > this.sizeInfo.selectOffsetRight && this.sizeInfo.selectOffsetRight < (this.sizeInfo.totalMenuWidth - selectWidth));\n      }\n\n      if (this.dropdown && this.dropdown._popper) this.dropdown._popper.update();\n    },\n\n    setSize: function (refresh) {\n      this.liHeight(refresh);\n\n      if (this.options.header) this.$menu.css('padding-top', 0);\n\n      if (this.options.size !== false) {\n        var that = this,\n            $window = $(window);\n\n        this.setMenuSize();\n\n        if (this.options.liveSearch) {\n          this.$searchbox\n              .off('input.setMenuSize propertychange.setMenuSize')\n              .on('input.setMenuSize propertychange.setMenuSize', function () {\n                return that.setMenuSize();\n              });\n        }\n\n        if (this.options.size === 'auto') {\n          $window\n              .off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize')\n              .on('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize', function () {\n                return that.setMenuSize();\n              });\n        } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) {\n          $window.off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize');\n        }\n      }\n\n      this.createView(false, true, refresh);\n    },\n\n    setWidth: function () {\n      var that = this;\n\n      if (this.options.width === 'auto') {\n        requestAnimationFrame(function () {\n          that.$menu.css('min-width', '0');\n\n          that.$element.on('loaded' + EVENT_KEY, function () {\n            that.liHeight();\n            that.setMenuSize();\n\n            // Get correct width if element is hidden\n            var $selectClone = that.$newElement.clone().appendTo('body'),\n                btnWidth = $selectClone.css('width', 'auto').children('button').outerWidth();\n\n            $selectClone.remove();\n\n            // Set width to whatever's larger, button title or longest option\n            that.sizeInfo.selectWidth = Math.max(that.sizeInfo.totalMenuWidth, btnWidth);\n            that.$newElement.css('width', that.sizeInfo.selectWidth + 'px');\n          });\n        });\n      } else if (this.options.width === 'fit') {\n        // Remove inline min-width so width can be changed from 'auto'\n        this.$menu.css('min-width', '');\n        this.$newElement.css('width', '').addClass('fit-width');\n      } else if (this.options.width) {\n        // Remove inline min-width so width can be changed from 'auto'\n        this.$menu.css('min-width', '');\n        this.$newElement.css('width', this.options.width);\n      } else {\n        // Remove inline min-width/width so width can be changed\n        this.$menu.css('min-width', '');\n        this.$newElement.css('width', '');\n      }\n      // Remove fit-width class if width is changed programmatically\n      if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') {\n        this.$newElement[0].classList.remove('fit-width');\n      }\n    },\n\n    selectPosition: function () {\n      this.$bsContainer = $('<div class=\"bs-container\" />');\n\n      var that = this,\n          $container = $(this.options.container),\n          pos,\n          containerPos,\n          actualHeight,\n          getPlacement = function ($element) {\n            var containerPosition = {},\n                // fall back to dropdown's default display setting if display is not manually set\n                display = that.options.display || (\n                    // Bootstrap 3 doesn't have $.fn.dropdown.Constructor.Default\n                    $.fn.dropdown.Constructor.Default ? $.fn.dropdown.Constructor.Default.display\n                        : false\n                );\n\n            that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass(classNames.DROPUP, $element.hasClass(classNames.DROPUP));\n            pos = $element.offset();\n\n            if (!$container.is('body')) {\n              containerPos = $container.offset();\n              containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop();\n              containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft();\n            } else {\n              containerPos = { top: 0, left: 0 };\n            }\n\n            actualHeight = $element.hasClass(classNames.DROPUP) ? 0 : $element[0].offsetHeight;\n\n            // Bootstrap 4+ uses Popper for menu positioning\n            if (version.major < 4 || display === 'static') {\n              containerPosition.top = pos.top - containerPos.top + actualHeight;\n              containerPosition.left = pos.left - containerPos.left;\n            }\n\n            containerPosition.width = $element[0].offsetWidth;\n\n            that.$bsContainer.css(containerPosition);\n          };\n\n      this.$button.on('click.bs.dropdown.data-api', function () {\n        if (that.isDisabled()) {\n          return;\n        }\n\n        getPlacement(that.$newElement);\n\n        that.$bsContainer\n            .appendTo(that.options.container)\n            .toggleClass(classNames.SHOW, !that.$button.hasClass(classNames.SHOW))\n            .append(that.$menu);\n      });\n\n      $(window)\n          .off('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId)\n          .on('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId, function () {\n            var isActive = that.$newElement.hasClass(classNames.SHOW);\n\n            if (isActive) getPlacement(that.$newElement);\n          });\n\n      this.$element.on('hide' + EVENT_KEY, function () {\n        that.$menu.data('height', that.$menu.height());\n        that.$bsContainer.detach();\n      });\n    },\n\n    createOption: function (data, init) {\n      var optionData = !data.option ? data : data.option;\n\n      if (optionData && optionData.nodeType !== 1) {\n        var option = (init ? elementTemplates.selectedOption : elementTemplates.option).cloneNode(true);\n        if (optionData.value !== undefined) option.value = optionData.value;\n        option.textContent = optionData.text;\n\n        option.selected = true;\n\n        if (optionData.liIndex !== undefined) {\n          option.liIndex = optionData.liIndex;\n        } else if (!init) {\n          option.liIndex = data.index;\n        }\n\n        data.option = option;\n\n        this.selectpicker.main.optionQueue.appendChild(option);\n      }\n    },\n\n    setOptionStatus: function (selectedOnly) {\n      var that = this;\n\n      that.noScroll = false;\n\n      if (that.selectpicker.view.visibleElements && that.selectpicker.view.visibleElements.length) {\n        for (var i = 0; i < that.selectpicker.view.visibleElements.length; i++) {\n          var liData = that.selectpicker.current.data[i + that.selectpicker.view.position0],\n              option = liData.option;\n\n          if (option) {\n            if (selectedOnly !== true) {\n              that.setDisabled(liData);\n            }\n\n            that.setSelected(liData);\n          }\n        }\n\n        // append optionQueue (documentFragment with option elements for select options)\n        if (this.options.source.data) this.$element[0].appendChild(this.selectpicker.main.optionQueue);\n      }\n    },\n\n    /**\n     * @param {Object} liData - the option object that is being changed\n     * @param {boolean} selected - true if the option is being selected, false if being deselected\n     */\n    setSelected: function (liData, selected) {\n      selected = selected === undefined ? liData.selected : selected;\n\n      var li = liData.element,\n          activeElementIsSet = this.activeElement !== undefined,\n          thisIsActive = this.activeElement === li,\n          prevActive,\n          a,\n          // if current option is already active\n          // OR\n          // if the current option is being selected, it's NOT multiple, and\n          // activeElement is undefined:\n          //  - when the menu is first being opened, OR\n          //  - after a search has been performed, OR\n          //  - when retainActive is false when selecting a new option (i.e. index of the newly selected option is not the same as the current activeElement)\n          keepActive = thisIsActive || (selected && !this.multiple && !activeElementIsSet);\n\n      if (!li) return;\n\n      if (selected !== undefined) {\n        liData.selected = selected;\n        if (liData.option) liData.option.selected = selected;\n      }\n\n      if (selected && this.options.source.data) {\n        this.createOption(liData, false);\n      }\n\n      a = li.firstChild;\n\n      if (selected) {\n        this.selectedElement = li;\n      }\n\n      li.classList.toggle('selected', selected);\n\n      if (keepActive) {\n        this.focusItem(li, liData);\n        this.selectpicker.view.currentActive = li;\n        this.activeElement = li;\n      } else {\n        this.defocusItem(li);\n      }\n\n      if (a) {\n        a.classList.toggle('selected', selected);\n\n        if (selected) {\n          a.setAttribute('aria-selected', true);\n        } else {\n          if (this.multiple) {\n            a.setAttribute('aria-selected', false);\n          } else {\n            a.removeAttribute('aria-selected');\n          }\n        }\n      }\n\n      if (!keepActive && !activeElementIsSet && selected && this.prevActiveElement !== undefined) {\n        prevActive = this.prevActiveElement;\n\n        this.defocusItem(prevActive);\n      }\n    },\n\n    /**\n     * @param {number} index - the index of the option that is being disabled\n     * @param {boolean} disabled - true if the option is being disabled, false if being enabled\n     */\n    setDisabled: function (liData) {\n      var disabled = liData.disabled,\n          li = liData.element,\n          a;\n\n      if (!li) return;\n\n      a = li.firstChild;\n\n      li.classList.toggle(classNames.DISABLED, disabled);\n\n      if (a) {\n        if (version.major >= '4') a.classList.toggle(classNames.DISABLED, disabled);\n\n        if (disabled) {\n          a.setAttribute('aria-disabled', disabled);\n          a.setAttribute('tabindex', -1);\n        } else {\n          a.removeAttribute('aria-disabled');\n          a.setAttribute('tabindex', 0);\n        }\n      }\n    },\n\n    isDisabled: function () {\n      return this.$element[0].disabled;\n    },\n\n    checkDisabled: function () {\n      if (this.isDisabled()) {\n        this.$newElement[0].classList.add(classNames.DISABLED);\n        this.$button.addClass(classNames.DISABLED).attr('aria-disabled', true);\n      } else {\n        if (this.$button[0].classList.contains(classNames.DISABLED)) {\n          this.$newElement[0].classList.remove(classNames.DISABLED);\n          this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false);\n        }\n      }\n    },\n\n    clickListener: function () {\n      var that = this,\n          $document = $(document);\n\n      $document.data('spaceSelect', false);\n\n      this.$button.on('keyup', function (e) {\n        if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) {\n          e.preventDefault();\n          $document.data('spaceSelect', false);\n        }\n      });\n\n      this.$newElement.on('show.bs.dropdown', function () {\n        if (!that.dropdown && version.major === '4') {\n          that.dropdown = that.$button.data('bs.dropdown');\n          that.dropdown._menu = that.$menu[0];\n        }\n      });\n\n      function clearSelection (e) {\n        if (that.multiple) {\n          that.deselectAll();\n        } else {\n          var element = that.$element[0],\n              prevValue = element.value,\n              prevIndex = element.selectedIndex,\n              prevOption = element.options[prevIndex],\n              prevData = prevOption ? that.selectpicker.main.data[prevOption.liIndex] : false;\n\n          if (prevData) {\n            that.setSelected(prevData, false);\n          }\n\n          element.selectedIndex = 0;\n\n          changedArguments = [prevIndex, false, prevValue];\n          that.$element.triggerNative('change');\n        }\n\n        // remove selected styling if menu is open\n        if (that.$newElement.hasClass(classNames.SHOW)) {\n          if (that.options.liveSearch) {\n            that.$searchbox.trigger('focus');\n          }\n\n          that.createView(false);\n        }\n      }\n\n      this.$button.on('click.bs.dropdown.data-api', function (e) {\n        if (that.options.allowClear) {\n          var target = e.target,\n              clearButton = that.$clearButton[0];\n\n          // IE doesn't support event listeners on child elements of buttons\n          if (/MSIE|Trident/.test(window.navigator.userAgent)) {\n            target = document.elementFromPoint(e.clientX, e.clientY);\n          }\n\n          if (target === clearButton || target.parentElement === clearButton) {\n            e.stopImmediatePropagation();\n            clearSelection(e);\n          }\n        }\n\n        if (!that.$newElement.hasClass(classNames.SHOW)) {\n          that.setSize();\n        }\n      });\n\n      function setFocus () {\n        if (that.options.liveSearch) {\n          that.$searchbox.trigger('focus');\n        } else {\n          that.$menuInner.trigger('focus');\n        }\n      }\n\n      function checkPopperExists () {\n        if (that.dropdown && that.dropdown._popper && that.dropdown._popper.state) {\n          setFocus();\n        } else {\n          requestAnimationFrame(checkPopperExists);\n        }\n      }\n\n      this.$element.on('shown' + EVENT_KEY, function () {\n        if (that.$menuInner[0].scrollTop !== that.selectpicker.view.scrollTop) {\n          that.$menuInner[0].scrollTop = that.selectpicker.view.scrollTop;\n        }\n\n        if (version.major > 3) {\n          requestAnimationFrame(checkPopperExists);\n        } else {\n          setFocus();\n        }\n      });\n\n      // ensure posinset and setsize are correct before selecting an option via a click\n      this.$menuInner.on('mouseenter', 'li a', function (e) {\n        var hoverLi = this.parentElement,\n            position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0,\n            index = Array.prototype.indexOf.call(hoverLi.parentElement.children, hoverLi),\n            hoverData = that.selectpicker.current.data[index + position0];\n\n        that.focusItem(hoverLi, hoverData, true);\n      });\n\n      this.$menuInner.on('click', 'li a', function (e, retainActive) {\n        var $this = $(this),\n            element = that.$element[0],\n            position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0,\n            clickedData = that.selectpicker.current.data[$this.parent().index() + position0],\n            clickedElement = clickedData.element,\n            prevValue = getSelectValues.call(that),\n            prevIndex = element.selectedIndex,\n            prevOption = element.options[prevIndex],\n            prevData = prevOption ? that.selectpicker.main.data[prevOption.liIndex] : false,\n            triggerChange = true;\n\n        // Don't close on multi choice menu\n        if (that.multiple && that.options.maxOptions !== 1) {\n          e.stopPropagation();\n        }\n\n        e.preventDefault();\n\n        // Don't run if the select is disabled\n        if (!that.isDisabled() && !$this.parent().hasClass(classNames.DISABLED)) {\n          var option = clickedData.option,\n              $option = $(option),\n              state = option.selected,\n              optgroupData = that.selectpicker.current.data.find(function (datum) {\n                return datum.optID === clickedData.optID && datum.type === 'optgroup-label';\n              }),\n              optgroup = optgroupData ? optgroupData.optgroup : undefined,\n              dataGetter = optgroup instanceof Element ? getOptionData.fromOption : getOptionData.fromDataSource,\n              optgroupOptions = optgroup && optgroup.children,\n              maxOptions = parseInt(that.options.maxOptions),\n              maxOptionsGrp = optgroup && parseInt(dataGetter(optgroup, 'maxOptions')) || false;\n\n          if (clickedElement === that.activeElement) retainActive = true;\n\n          if (!retainActive) {\n            that.prevActiveElement = that.activeElement;\n            that.activeElement = undefined;\n          }\n\n          if (!that.multiple || maxOptions === 1) { // Deselect previous option if not multi select\n            if (prevData) that.setSelected(prevData, false);\n            that.setSelected(clickedData, true);\n          } else { // Toggle the clicked option if multi select.\n            that.setSelected(clickedData, !state);\n            that.focusedParent.focus();\n\n            if (maxOptions !== false || maxOptionsGrp !== false) {\n              var maxReached = maxOptions < getSelectedOptions.call(that).length,\n                  selectedGroupOptions = 0;\n\n              if (optgroup && optgroup.children) {\n                for (var i = 0; i < optgroup.children.length; i++) {\n                  if (optgroup.children[i].selected) selectedGroupOptions++;\n                }\n              }\n\n              var maxReachedGrp = maxOptionsGrp < selectedGroupOptions;\n\n              if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) {\n                if (maxOptions && maxOptions === 1) {\n                  element.selectedIndex = -1;\n                  that.setOptionStatus(true);\n                } else if (maxOptionsGrp && maxOptionsGrp === 1) {\n                  for (var i = 0; i < optgroupOptions.length; i++) {\n                    var _option = optgroupOptions[i];\n                    that.setSelected(that.selectpicker.current.data[_option.liIndex], false);\n                  }\n\n                  that.setSelected(clickedData, true);\n                } else {\n                  var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText,\n                      maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText,\n                      maxTxt = maxOptionsArr[0].replace('{n}', maxOptions),\n                      maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp),\n                      $notify = $('<div class=\"notify\"></div>');\n                  // If {var} is set in array, replace it\n                  /** @deprecated */\n                  if (maxOptionsArr[2]) {\n                    maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]);\n                    maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]);\n                  }\n\n                  that.$menu.append($notify);\n\n                  if (maxOptions && maxReached) {\n                    $notify.append($('<div>' + maxTxt + '</div>'));\n                    triggerChange = false;\n                    that.$element.trigger('maxReached' + EVENT_KEY);\n                  }\n\n                  if (maxOptionsGrp && maxReachedGrp) {\n                    $notify.append($('<div>' + maxTxtGrp + '</div>'));\n                    triggerChange = false;\n                    that.$element.trigger('maxReachedGrp' + EVENT_KEY);\n                  }\n\n                  setTimeout(function () {\n                    that.setSelected(clickedData, false);\n                  }, 10);\n\n                  $notify[0].classList.add('fadeOut');\n\n                  setTimeout(function () {\n                    $notify.remove();\n                  }, 1050);\n                }\n              }\n            }\n          }\n\n          if (that.options.source.data) that.$element[0].appendChild(that.selectpicker.main.optionQueue);\n\n          if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) {\n            that.$button.trigger('focus');\n          } else if (that.options.liveSearch) {\n            that.$searchbox.trigger('focus');\n          }\n\n          // Trigger select 'change'\n          if (triggerChange) {\n            if (that.multiple || prevIndex !== element.selectedIndex) {\n              // $option.prop('selected') is current option state (selected/unselected). prevValue is the value of the select prior to being changed.\n              changedArguments = [option.index, $option.prop('selected'), prevValue];\n              that.$element\n                  .triggerNative('change');\n            }\n          }\n        }\n      });\n\n      this.$menu.on('click', 'li.' + classNames.DISABLED + ' a, .' + classNames.POPOVERHEADER + ', .' + classNames.POPOVERHEADER + ' :not(.close)', function (e) {\n        if (e.currentTarget == this) {\n          e.preventDefault();\n          e.stopPropagation();\n          if (that.options.liveSearch && !$(e.target).hasClass('close')) {\n            that.$searchbox.trigger('focus');\n          } else {\n            that.$button.trigger('focus');\n          }\n        }\n      });\n\n      this.$menuInner.on('click', '.divider, .dropdown-header', function (e) {\n        e.preventDefault();\n        e.stopPropagation();\n        if (that.options.liveSearch) {\n          that.$searchbox.trigger('focus');\n        } else {\n          that.$button.trigger('focus');\n        }\n      });\n\n      this.$menu.on('click', '.' + classNames.POPOVERHEADER + ' .close', function () {\n        that.$button.trigger('click');\n      });\n\n      this.$searchbox.on('click', function (e) {\n        e.stopPropagation();\n      });\n\n      this.$menu.on('click', '.actions-btn', function (e) {\n        if (that.options.liveSearch) {\n          that.$searchbox.trigger('focus');\n        } else {\n          that.$button.trigger('focus');\n        }\n\n        e.preventDefault();\n        e.stopPropagation();\n\n        if ($(this).hasClass('bs-select-all')) {\n          that.selectAll();\n        } else {\n          that.deselectAll();\n        }\n      });\n\n      this.$button\n          .on('focus' + EVENT_KEY, function (e) {\n            var tabindex = that.$element[0].getAttribute('tabindex');\n\n            // only change when button is actually focused\n            if (tabindex !== undefined && e.originalEvent && e.originalEvent.isTrusted) {\n              // apply select element's tabindex to ensure correct order is followed when tabbing to the next element\n              this.setAttribute('tabindex', tabindex);\n              // set element's tabindex to -1 to allow for reverse tabbing\n              that.$element[0].setAttribute('tabindex', -1);\n              that.selectpicker.view.tabindex = tabindex;\n            }\n          })\n          .on('blur' + EVENT_KEY, function (e) {\n            // revert everything to original tabindex\n            if (that.selectpicker.view.tabindex !== undefined && e.originalEvent && e.originalEvent.isTrusted) {\n              that.$element[0].setAttribute('tabindex', that.selectpicker.view.tabindex);\n              this.setAttribute('tabindex', -1);\n              that.selectpicker.view.tabindex = undefined;\n            }\n          });\n\n      this.$element\n          .on('change' + EVENT_KEY, function () {\n            that.render();\n            that.$element.trigger('changed' + EVENT_KEY, changedArguments);\n            changedArguments = null;\n          })\n          .on('focus' + EVENT_KEY, function () {\n            if (!that.options.mobile) that.$button[0].focus();\n          });\n    },\n\n    liveSearchListener: function () {\n      var that = this;\n\n      this.$button.on('click.bs.dropdown.data-api', function () {\n        if (!!that.$searchbox.val()) {\n          that.$searchbox.val('');\n          that.selectpicker.search.previousValue = undefined;\n        }\n      });\n\n      this.$searchbox.on('click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api', function (e) {\n        e.stopPropagation();\n      });\n\n      this.$searchbox.on('input propertychange', function () {\n        var searchValue = that.$searchbox[0].value;\n\n        that.selectpicker.search.elements = [];\n        that.selectpicker.search.data = [];\n\n        if (searchValue) {\n          that.selectpicker.search.previousValue = searchValue;\n\n          if (that.options.source.search) {\n            that.fetchData(function (builtData) {\n              that.render();\n              that.buildList(undefined, true);\n              that.noScroll = true;\n              that.$menuInner.scrollTop(0);\n              that.createView(true);\n              showNoResults.call(that, builtData, searchValue);\n            }, 'search', 0, searchValue);\n          } else {\n            var i,\n                searchMatch = [],\n                q = searchValue.toUpperCase(),\n                cache = {},\n                cacheArr = [],\n                searchStyle = that._searchStyle(),\n                normalizeSearch = that.options.liveSearchNormalize;\n\n            if (normalizeSearch) q = normalizeToBase(q);\n\n            for (var i = 0; i < that.selectpicker.main.data.length; i++) {\n              var li = that.selectpicker.main.data[i];\n\n              if (!cache[i]) {\n                cache[i] = stringSearch(li, q, searchStyle, normalizeSearch);\n              }\n\n              if (cache[i] && li.headerIndex !== undefined && cacheArr.indexOf(li.headerIndex) === -1) {\n                if (li.headerIndex > 0) {\n                  cache[li.headerIndex - 1] = true;\n                  cacheArr.push(li.headerIndex - 1);\n                }\n\n                cache[li.headerIndex] = true;\n                cacheArr.push(li.headerIndex);\n\n                cache[li.lastIndex + 1] = true;\n              }\n\n              if (cache[i] && li.type !== 'optgroup-label') cacheArr.push(i);\n            }\n\n            for (var i = 0, cacheLen = cacheArr.length; i < cacheLen; i++) {\n              var index = cacheArr[i],\n                  prevIndex = cacheArr[i - 1],\n                  li = that.selectpicker.main.data[index],\n                  liPrev = that.selectpicker.main.data[prevIndex];\n\n              if (li.type !== 'divider' || (li.type === 'divider' && liPrev && liPrev.type !== 'divider' && cacheLen - 1 !== i)) {\n                that.selectpicker.search.data.push(li);\n                searchMatch.push(that.selectpicker.main.elements[index]);\n              }\n            }\n\n            that.activeElement = undefined;\n            that.noScroll = true;\n            that.$menuInner.scrollTop(0);\n            that.selectpicker.search.elements = searchMatch;\n            that.createView(true);\n            showNoResults.call(that, searchMatch, searchValue);\n          }\n        } else if (that.selectpicker.search.previousValue) { // for IE11 (#2402)\n          that.$menuInner.scrollTop(0);\n          that.createView(false);\n        }\n      });\n    },\n\n    _searchStyle: function () {\n      return this.options.liveSearchStyle || 'contains';\n    },\n\n    val: function (value) {\n      var element = this.$element[0];\n\n      if (typeof value !== 'undefined') {\n        var selectedOptions = getSelectedOptions.call(this),\n            prevValue = getSelectValues.call(this, selectedOptions);\n\n        changedArguments = [null, null, prevValue];\n\n        if (!Array.isArray(value)) value = [ value ];\n\n        value.map(String);\n\n        for (var i = 0; i < selectedOptions.length; i++) {\n          var item = selectedOptions[i];\n\n          if (item && value.indexOf(String(item.value)) === -1) {\n            this.setSelected(item, false);\n          }\n        }\n\n        // only update selected value if it matches an existing option\n        this.selectpicker.main.data.filter(function (item) {\n          if (value.indexOf(String(item.value)) !== -1) {\n            this.setSelected(item, true);\n            return true;\n          }\n\n          return false;\n        }, this);\n\n        if (this.options.source.data) element.appendChild(this.selectpicker.main.optionQueue);\n\n        this.$element.trigger('changed' + EVENT_KEY, changedArguments);\n\n        if (this.$newElement.hasClass(classNames.SHOW)) {\n          if (this.multiple) {\n            this.setOptionStatus(true);\n          } else {\n            var liSelectedIndex = (element.options[element.selectedIndex] || {}).liIndex;\n\n            if (typeof liSelectedIndex === 'number') {\n              this.setSelected(this.selectpicker.current.data[liSelectedIndex], true);\n            }\n          }\n        }\n\n        this.render();\n\n        changedArguments = null;\n\n        return this.$element;\n      } else {\n        return this.$element.val();\n      }\n    },\n\n    changeAll: function (status) {\n      if (!this.multiple) return;\n      if (typeof status === 'undefined') status = true;\n\n      var element = this.$element[0],\n          previousSelected = 0,\n          currentSelected = 0,\n          prevValue = getSelectValues.call(this);\n\n      element.classList.add('bs-select-hidden');\n\n      for (var i = 0, data = this.selectpicker.current.data, len = data.length; i < len; i++) {\n        var liData = data[i],\n            option = liData.option;\n\n        if (option && !liData.disabled && liData.type !== 'divider') {\n          if (liData.selected) previousSelected++;\n          option.selected = status;\n          liData.selected = status;\n          if (status === true) currentSelected++;\n        }\n      }\n\n      element.classList.remove('bs-select-hidden');\n\n      if (previousSelected === currentSelected) return;\n\n      this.setOptionStatus();\n\n      changedArguments = [null, null, prevValue];\n\n      this.$element\n          .triggerNative('change');\n    },\n\n    selectAll: function () {\n      return this.changeAll(true);\n    },\n\n    deselectAll: function () {\n      return this.changeAll(false);\n    },\n\n    toggle: function (e, state) {\n      var isActive,\n          triggerClick = state === undefined;\n\n      e = e || window.event;\n\n      if (e) e.stopPropagation();\n\n      if (triggerClick === false) {\n        isActive = this.$newElement[0].classList.contains(classNames.SHOW);\n        triggerClick = state === true && isActive === false || state === false && isActive === true;\n      }\n\n      if (triggerClick) this.$button.trigger('click.bs.dropdown.data-api');\n    },\n\n    open: function (e) {\n      this.toggle(e, true);\n    },\n\n    close: function (e) {\n      this.toggle(e, false);\n    },\n\n    keydown: function (e) {\n      var $this = $(this),\n          isToggle = $this.hasClass('dropdown-toggle'),\n          $parent = isToggle ? $this.closest('.dropdown') : $this.closest(Selector.MENU),\n          that = $parent.data('this'),\n          $items = that.findLis(),\n          index,\n          isActive,\n          liActive,\n          activeLi,\n          offset,\n          updateScroll = false,\n          downOnTab = e.which === keyCodes.TAB && !isToggle && !that.options.selectOnTab,\n          isArrowKey = REGEXP_ARROW.test(e.which) || downOnTab,\n          scrollTop = that.$menuInner[0].scrollTop,\n          isVirtual = that.isVirtual(),\n          position0 = isVirtual === true ? that.selectpicker.view.position0 : 0;\n\n      // do nothing if a function key is pressed\n      if (e.which >= 112 && e.which <= 123) return;\n\n      isActive = that.$menu.hasClass(classNames.SHOW);\n\n      if (\n          !isActive &&\n          (\n              isArrowKey ||\n              (e.which >= 48 && e.which <= 57) ||\n              (e.which >= 96 && e.which <= 105) ||\n              (e.which >= 65 && e.which <= 90)\n          )\n      ) {\n        that.$button.trigger('click.bs.dropdown.data-api');\n\n        if (that.options.liveSearch) {\n          that.$searchbox.trigger('focus');\n          return;\n        }\n      }\n\n      if (e.which === keyCodes.ESCAPE && isActive) {\n        e.preventDefault();\n        that.$button.trigger('click.bs.dropdown.data-api').trigger('focus');\n      }\n\n      if (isArrowKey) { // if up or down\n        if (!$items.length) return;\n\n        liActive = that.activeElement;\n        index = liActive ? Array.prototype.indexOf.call(liActive.parentElement.children, liActive) : -1;\n\n        if (index !== -1) {\n          that.defocusItem(liActive);\n        }\n\n        if (e.which === keyCodes.ARROW_UP) { // up\n          if (index !== -1) index--;\n          if (index + position0 < 0) index += $items.length;\n\n          if (!that.selectpicker.view.canHighlight[index + position0]) {\n            index = that.selectpicker.view.canHighlight.slice(0, index + position0).lastIndexOf(true) - position0;\n            if (index === -1) index = $items.length - 1;\n          }\n        } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down\n          index++;\n          if (index + position0 >= that.selectpicker.view.canHighlight.length) index = that.selectpicker.view.firstHighlightIndex;\n\n          if (!that.selectpicker.view.canHighlight[index + position0]) {\n            index = index + 1 + that.selectpicker.view.canHighlight.slice(index + position0 + 1).indexOf(true);\n          }\n        }\n\n        e.preventDefault();\n\n        var liActiveIndex = position0 + index;\n\n        if (e.which === keyCodes.ARROW_UP) { // up\n          // scroll to bottom and highlight last option\n          if (position0 === 0 && index === $items.length - 1) {\n            that.$menuInner[0].scrollTop = that.$menuInner[0].scrollHeight;\n\n            liActiveIndex = that.selectpicker.current.elements.length - 1;\n          } else {\n            activeLi = that.selectpicker.current.data[liActiveIndex];\n\n            // could be undefined if no results exist\n            if (activeLi) {\n              offset = activeLi.position - activeLi.height;\n\n              updateScroll = offset < scrollTop;\n            }\n          }\n        } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down\n          // scroll to top and highlight first option\n          if (index === that.selectpicker.view.firstHighlightIndex) {\n            that.$menuInner[0].scrollTop = 0;\n\n            liActiveIndex = that.selectpicker.view.firstHighlightIndex;\n          } else {\n            activeLi = that.selectpicker.current.data[liActiveIndex];\n\n            // could be undefined if no results exist\n            if (activeLi) {\n              offset = activeLi.position - that.sizeInfo.menuInnerHeight;\n\n              updateScroll = offset > scrollTop;\n            }\n          }\n        }\n\n        liActive = that.selectpicker.current.elements[liActiveIndex];\n\n        that.activeElement = (that.selectpicker.current.data[liActiveIndex] || {}).element;\n\n        that.focusItem(liActive);\n\n        that.selectpicker.view.currentActive = liActive;\n\n        if (updateScroll) that.$menuInner[0].scrollTop = offset;\n\n        if (that.options.liveSearch) {\n          that.$searchbox.trigger('focus');\n        } else {\n          $this.trigger('focus');\n        }\n      } else if (\n          (!$this.is('input') && !REGEXP_TAB_OR_ESCAPE.test(e.which)) ||\n          (e.which === keyCodes.SPACE && that.selectpicker.keydown.keyHistory)\n      ) {\n        var searchMatch,\n            matches = [],\n            keyHistory;\n\n        e.preventDefault();\n\n        that.selectpicker.keydown.keyHistory += keyCodeMap[e.which];\n\n        if (that.selectpicker.keydown.resetKeyHistory.cancel) clearTimeout(that.selectpicker.keydown.resetKeyHistory.cancel);\n        that.selectpicker.keydown.resetKeyHistory.cancel = that.selectpicker.keydown.resetKeyHistory.start();\n\n        keyHistory = that.selectpicker.keydown.keyHistory;\n\n        // if all letters are the same, set keyHistory to just the first character when searching\n        if (/^(.)\\1+$/.test(keyHistory)) {\n          keyHistory = keyHistory.charAt(0);\n        }\n\n        // find matches\n        for (var i = 0; i < that.selectpicker.current.data.length; i++) {\n          var li = that.selectpicker.current.data[i],\n              hasMatch;\n\n          hasMatch = stringSearch(li, keyHistory, 'startsWith', true);\n\n          if (hasMatch && that.selectpicker.view.canHighlight[i]) {\n            matches.push(li.element);\n          }\n        }\n\n        if (matches.length) {\n          var matchIndex = 0;\n\n          $items.removeClass('active').find('a').removeClass('active');\n\n          // either only one key has been pressed or they are all the same key\n          if (keyHistory.length === 1) {\n            matchIndex = matches.indexOf(that.activeElement);\n\n            if (matchIndex === -1 || matchIndex === matches.length - 1) {\n              matchIndex = 0;\n            } else {\n              matchIndex++;\n            }\n          }\n\n          searchMatch = matches[matchIndex];\n\n          activeLi = that.selectpicker.main.data[searchMatch];\n\n          if (scrollTop - activeLi.position > 0) {\n            offset = activeLi.position - activeLi.height;\n            updateScroll = true;\n          } else {\n            offset = activeLi.position - that.sizeInfo.menuInnerHeight;\n            // if the option is already visible at the current scroll position, just keep it the same\n            updateScroll = activeLi.position > scrollTop + that.sizeInfo.menuInnerHeight;\n          }\n\n          liActive = that.selectpicker.main.elements[searchMatch];\n\n          that.activeElement = liActive;\n\n          that.focusItem(liActive);\n\n          if (liActive) liActive.firstChild.focus();\n\n          if (updateScroll) that.$menuInner[0].scrollTop = offset;\n\n          $this.trigger('focus');\n        }\n      }\n\n      // Select focused option if \"Enter\", \"Spacebar\" or \"Tab\" (when selectOnTab is true) are pressed inside the menu.\n      if (\n          isActive &&\n          (\n              (e.which === keyCodes.SPACE && !that.selectpicker.keydown.keyHistory) ||\n              e.which === keyCodes.ENTER ||\n              (e.which === keyCodes.TAB && that.options.selectOnTab)\n          )\n      ) {\n        if (e.which !== keyCodes.SPACE) e.preventDefault();\n\n        if (!that.options.liveSearch || e.which !== keyCodes.SPACE) {\n          that.$menuInner.find('.active a').trigger('click', true); // retain active class\n          $this.trigger('focus');\n\n          if (!that.options.liveSearch) {\n            // Prevent screen from scrolling if the user hits the spacebar\n            e.preventDefault();\n            // Fixes spacebar selection of dropdown items in FF & IE\n            $(document).data('spaceSelect', true);\n          }\n        }\n      }\n    },\n\n    mobile: function () {\n      // ensure mobile is set to true if mobile function is called after init\n      this.options.mobile = true;\n      this.$element[0].classList.add('mobile-device');\n    },\n\n    refresh: function () {\n      var that = this;\n      // update options if data attributes have been changed\n      var config = $.extend({}, this.options, getAttributesObject(this.$element), this.$element.data()); // in this order on refresh, as user may change attributes on select, and options object is not passed on refresh\n      this.options = config;\n\n      if (this.options.source.data) {\n        this.render();\n        this.buildList();\n      } else {\n        this.fetchData(function () {\n          that.render();\n          that.buildList();\n        });\n      }\n\n      this.checkDisabled();\n      this.setStyle();\n      this.setWidth();\n\n      this.setSize(true);\n\n      this.$element.trigger('refreshed' + EVENT_KEY);\n    },\n\n    hide: function () {\n      this.$newElement.hide();\n    },\n\n    show: function () {\n      this.$newElement.show();\n    },\n\n    remove: function () {\n      this.$newElement.remove();\n      this.$element.remove();\n    },\n\n    destroy: function () {\n      this.$newElement.before(this.$element).remove();\n\n      if (this.$bsContainer) {\n        this.$bsContainer.remove();\n      } else {\n        this.$menu.remove();\n      }\n\n      if (this.selectpicker.view.titleOption && this.selectpicker.view.titleOption.parentNode) {\n        this.selectpicker.view.titleOption.parentNode.removeChild(this.selectpicker.view.titleOption);\n      }\n\n      this.$element\n          .off(EVENT_KEY)\n          .removeData('selectpicker')\n          .removeClass('bs-select-hidden selectpicker mobile-device');\n\n      $(window).off(EVENT_KEY + '.' + this.selectId);\n    }\n  };\n\n  // SELECTPICKER PLUGIN DEFINITION\n  // ==============================\n  function Plugin (option) {\n    // get the args of the outer function..\n    var args = arguments;\n    // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them\n    // to get lost/corrupted in android 2.3 and IE9 #715 #775\n    var _option = option;\n\n    [].shift.apply(args);\n\n    // if the version was not set successfully\n    if (!version.success) {\n      // try to retreive it again\n      try {\n        version.full = (getVersion() || '').split(' ')[0].split('.');\n      } catch (err) {\n        // fall back to use BootstrapVersion if set\n        if (Selectpicker.BootstrapVersion) {\n          version.full = Selectpicker.BootstrapVersion.split(' ')[0].split('.');\n        } else {\n          version.full = [version.major, '0', '0'];\n\n          console.warn(\n              'There was an issue retrieving Bootstrap\\'s version. ' +\n              'Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. ' +\n              'If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.',\n              err\n          );\n        }\n      }\n\n      version.major = version.full[0];\n      version.success = true;\n    }\n\n    if (version.major >= '4') {\n      // some defaults need to be changed if using Bootstrap 4\n      // check to see if they have already been manually changed before forcing them to update\n      var toUpdate = [];\n\n      if (Selectpicker.DEFAULTS.style === classNames.BUTTONCLASS) toUpdate.push({ name: 'style', className: 'BUTTONCLASS' });\n      if (Selectpicker.DEFAULTS.iconBase === classNames.ICONBASE) toUpdate.push({ name: 'iconBase', className: 'ICONBASE' });\n      if (Selectpicker.DEFAULTS.tickIcon === classNames.TICKICON) toUpdate.push({ name: 'tickIcon', className: 'TICKICON' });\n\n      classNames.DIVIDER = 'dropdown-divider';\n      classNames.SHOW = 'show';\n      classNames.BUTTONCLASS = 'btn-light';\n      classNames.POPOVERHEADER = 'popover-header';\n      classNames.ICONBASE = '';\n      classNames.TICKICON = 'bs-ok-default';\n\n      for (var i = 0; i < toUpdate.length; i++) {\n        var option = toUpdate[i];\n        Selectpicker.DEFAULTS[option.name] = classNames[option.className];\n      }\n    }\n\n    if (version.major > '4') {\n      Selector.DATA_TOGGLE = 'data-bs-toggle=\"dropdown\"';\n    }\n\n    var value;\n    var chain = this.each(function () {\n      var $this = $(this);\n      if ($this.is('select')) {\n        var data = $this.data('selectpicker'),\n            options = typeof _option == 'object' && _option;\n\n        // for backwards compatibility\n        // (using title as placeholder is deprecated - remove in v2.0.0)\n        if (options.title) options.placeholder = options.title;\n\n        if (!data) {\n          var dataAttributes = $this.data();\n\n          for (var dataAttr in dataAttributes) {\n            if (Object.prototype.hasOwnProperty.call(dataAttributes, dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) {\n              delete dataAttributes[dataAttr];\n            }\n          }\n\n          var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, getAttributesObject($this), dataAttributes, options); // this is correct order on initial render\n          config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), dataAttributes.template, options.template);\n          config.source = $.extend({}, Selectpicker.DEFAULTS.source, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.source : {}), options.source);\n          $this.data('selectpicker', (data = new Selectpicker(this, config)));\n        } else if (options) {\n          for (var i in options) {\n            if (Object.prototype.hasOwnProperty.call(options, i)) {\n              data.options[i] = options[i];\n            }\n          }\n        }\n\n        if (typeof _option == 'string') {\n          if (data[_option] instanceof Function) {\n            value = data[_option].apply(data, args);\n          } else {\n            value = data.options[_option];\n          }\n        }\n      }\n    });\n\n    if (typeof value !== 'undefined') {\n      // noinspection JSUnusedAssignment\n      return value;\n    } else {\n      return chain;\n    }\n  }\n\n  var old = $.fn.selectpicker;\n  $.fn.selectpicker = Plugin;\n  $.fn.selectpicker.Constructor = Selectpicker;\n\n  // SELECTPICKER NO CONFLICT\n  // ========================\n  $.fn.selectpicker.noConflict = function () {\n    $.fn.selectpicker = old;\n    return this;\n  };\n\n  // get Bootstrap's keydown event handler for either Bootstrap 4 or Bootstrap 3\n  function keydownHandler () {\n    if (version.major < 5) {\n      if ($.fn.dropdown) {\n        // wait to define until function is called in case Bootstrap isn't loaded yet\n        var bootstrapKeydown = $.fn.dropdown.Constructor._dataApiKeydownHandler || $.fn.dropdown.Constructor.prototype.keydown;\n        return bootstrapKeydown.apply(this, arguments);\n      }\n    } else {\n      return Dropdown.dataApiKeydownHandler;\n    }\n  }\n\n  $(document)\n    .off('keydown.bs.dropdown.data-api')\n    .on('keydown.bs.dropdown.data-api', ':not(.bootstrap-select) > [' + Selector.DATA_TOGGLE + ']', keydownHandler)\n    .on('keydown.bs.dropdown.data-api', ':not(.bootstrap-select) > .dropdown-menu', keydownHandler)\n    .on('keydown' + EVENT_KEY, '.bootstrap-select [' + Selector.DATA_TOGGLE + '], .bootstrap-select [role=\"listbox\"], .bootstrap-select .bs-searchbox input', Selectpicker.prototype.keydown)\n    .on('focusin.modal', '.bootstrap-select [' + Selector.DATA_TOGGLE + '], .bootstrap-select [role=\"listbox\"], .bootstrap-select .bs-searchbox input', function (e) {\n      e.stopPropagation();\n    });\n\n  // SELECTPICKER DATA-API\n  // =====================\n  document.addEventListener('DOMContentLoaded', function () {\n    $('.selectpicker').each(function () {\n      var $selectpicker = $(this);\n      Plugin.call($selectpicker, $selectpicker.data());\n    });\n  });\n})(jQuery);\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/boxicons/css/animations.css",
    "content": "@-webkit-keyframes spin\n{\n    0%\n    {\n        -webkit-transform: rotate(0);\n                transform: rotate(0);\n    }\n    100%\n    {\n        -webkit-transform: rotate(359deg);\n                transform: rotate(359deg);\n    }\n}\n@keyframes spin\n{\n    0%\n    {\n        -webkit-transform: rotate(0);\n                transform: rotate(0);\n    }\n    100%\n    {\n        -webkit-transform: rotate(359deg);\n                transform: rotate(359deg);\n    }\n}\n@-webkit-keyframes burst\n{\n    0%\n    {\n        -webkit-transform: scale(1);\n                transform: scale(1);\n\n        opacity: 1;\n    }\n    90%\n    {\n        -webkit-transform: scale(1.5);\n                transform: scale(1.5);\n\n        opacity: 0;\n    }\n}\n@keyframes burst\n{\n    0%\n    {\n        -webkit-transform: scale(1);\n                transform: scale(1);\n\n        opacity: 1;\n    }\n    90%\n    {\n        -webkit-transform: scale(1.5);\n                transform: scale(1.5);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes flashing\n{\n    0%\n    {\n        opacity: 1;\n    }\n    45%\n    {\n        opacity: 0;\n    }\n    90%\n    {\n        opacity: 1;\n    }\n}\n@keyframes flashing\n{\n    0%\n    {\n        opacity: 1;\n    }\n    45%\n    {\n        opacity: 0;\n    }\n    90%\n    {\n        opacity: 1;\n    }\n}\n@-webkit-keyframes fade-left\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(-20px);\n                transform: translateX(-20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-left\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(-20px);\n                transform: translateX(-20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes fade-right\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(20px);\n                transform: translateX(20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-right\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(20px);\n                transform: translateX(20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes fade-up\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(-20px);\n                transform: translateY(-20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-up\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(-20px);\n                transform: translateY(-20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes fade-down\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(20px);\n                transform: translateY(20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-down\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(20px);\n                transform: translateY(20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes tada\n{\n    from\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n\n    10%,\n    20%\n    {\n        -webkit-transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n                transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n    }\n\n    30%,\n    50%,\n    70%,\n    90%\n    {\n        -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n                transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n    }\n\n    40%,\n    60%,\n    80%\n    {\n        -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, -10deg);\n                transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, -10deg);\n    }\n\n    to\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n}\n\n@keyframes tada\n{\n    from\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n\n    10%,\n    20%\n    {\n        -webkit-transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n                transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n    }\n\n    30%,\n    50%,\n    70%,\n    90%\n    {\n        -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n                transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n    }\n\n    40%,\n    60%,\n    80%\n    {\n        -webkit-transform: rotate3d(0, 0, 1, -10deg);\n                transform: rotate3d(0, 0, 1, -10deg);\n    }\n\n    to\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n}\n.bx-spin\n{\n    -webkit-animation: spin 2s linear infinite;\n            animation: spin 2s linear infinite;\n}\n.bx-spin-hover:hover\n{\n    -webkit-animation: spin 2s linear infinite;\n            animation: spin 2s linear infinite;\n}\n\n.bx-tada\n{\n    -webkit-animation: tada 1.5s ease infinite;\n            animation: tada 1.5s ease infinite;\n}\n.bx-tada-hover:hover\n{\n    -webkit-animation: tada 1.5s ease infinite;\n            animation: tada 1.5s ease infinite;\n}\n\n.bx-flashing\n{\n    -webkit-animation: flashing 1.5s infinite linear;\n            animation: flashing 1.5s infinite linear;\n}\n.bx-flashing-hover:hover\n{\n    -webkit-animation: flashing 1.5s infinite linear;\n            animation: flashing 1.5s infinite linear;\n}\n\n.bx-burst\n{\n    -webkit-animation: burst 1.5s infinite linear;\n            animation: burst 1.5s infinite linear;\n}\n.bx-burst-hover:hover\n{\n    -webkit-animation: burst 1.5s infinite linear;\n            animation: burst 1.5s infinite linear;\n}\n.bx-fade-up\n{\n    -webkit-animation: fade-up 1.5s infinite linear;\n            animation: fade-up 1.5s infinite linear;\n}\n.bx-fade-up-hover:hover\n{\n    -webkit-animation: fade-up 1.5s infinite linear;\n            animation: fade-up 1.5s infinite linear;\n}\n.bx-fade-down\n{\n    -webkit-animation: fade-down 1.5s infinite linear;\n            animation: fade-down 1.5s infinite linear;\n}\n.bx-fade-down-hover:hover\n{\n    -webkit-animation: fade-down 1.5s infinite linear;\n            animation: fade-down 1.5s infinite linear;\n}\n.bx-fade-left\n{\n    -webkit-animation: fade-left 1.5s infinite linear;\n            animation: fade-left 1.5s infinite linear;\n}\n.bx-fade-left-hover:hover\n{\n    -webkit-animation: fade-left 1.5s infinite linear;\n            animation: fade-left 1.5s infinite linear;\n}\n.bx-fade-right\n{\n    -webkit-animation: fade-right 1.5s infinite linear;\n            animation: fade-right 1.5s infinite linear;\n}\n.bx-fade-right-hover:hover\n{\n    -webkit-animation: fade-right 1.5s infinite linear;\n            animation: fade-right 1.5s infinite linear;\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/boxicons/css/boxicons.css",
    "content": "@font-face\n{\n    font-family: 'boxicons';\n    font-weight: normal;\n    font-style: normal;\n\n    src: url('../fonts/boxicons.eot');\n    src: url('../fonts/boxicons.eot') format('embedded-opentype'),\n    url('../fonts/boxicons.woff2') format('woff2'),\n    url('../fonts/boxicons.woff') format('woff'),\n    url('../fonts/boxicons.ttf') format('truetype'),\n    url('../fonts/boxicons.svg?#boxicons') format('svg');\n}\n.bx\n{\n    font-family: 'boxicons' !important;\n    font-weight: normal;\n    font-style: normal;\n    font-variant: normal;\n    line-height: 1;\n    text-rendering: auto;\n    display: inline-block;\n\n    text-transform: none;\n\n    speak: none;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n.bx-ul\n{\n    margin-left: 2em;\n    padding-left: 0;\n\n    list-style: none;\n}\n.bx-ul > li\n{\n    position: relative;\n}\n.bx-ul .bx\n{\n    font-size: inherit;\n    line-height: inherit;\n\n    position: absolute;\n    left: -2em;\n\n    width: 2em;\n\n    text-align: center;\n}\n@-webkit-keyframes spin\n{\n    0%\n    {\n        -webkit-transform: rotate(0);\n                transform: rotate(0);\n    }\n    100%\n    {\n        -webkit-transform: rotate(359deg);\n                transform: rotate(359deg);\n    }\n}\n@keyframes spin\n{\n    0%\n    {\n        -webkit-transform: rotate(0);\n                transform: rotate(0);\n    }\n    100%\n    {\n        -webkit-transform: rotate(359deg);\n                transform: rotate(359deg);\n    }\n}\n@-webkit-keyframes burst\n{\n    0%\n    {\n        -webkit-transform: scale(1);\n                transform: scale(1);\n\n        opacity: 1;\n    }\n    90%\n    {\n        -webkit-transform: scale(1.5);\n                transform: scale(1.5);\n\n        opacity: 0;\n    }\n}\n@keyframes burst\n{\n    0%\n    {\n        -webkit-transform: scale(1);\n                transform: scale(1);\n\n        opacity: 1;\n    }\n    90%\n    {\n        -webkit-transform: scale(1.5);\n                transform: scale(1.5);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes flashing\n{\n    0%\n    {\n        opacity: 1;\n    }\n    45%\n    {\n        opacity: 0;\n    }\n    90%\n    {\n        opacity: 1;\n    }\n}\n@keyframes flashing\n{\n    0%\n    {\n        opacity: 1;\n    }\n    45%\n    {\n        opacity: 0;\n    }\n    90%\n    {\n        opacity: 1;\n    }\n}\n@-webkit-keyframes fade-left\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(-20px);\n                transform: translateX(-20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-left\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(-20px);\n                transform: translateX(-20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes fade-right\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(20px);\n                transform: translateX(20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-right\n{\n    0%\n    {\n        -webkit-transform: translateX(0);\n                transform: translateX(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateX(20px);\n                transform: translateX(20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes fade-up\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(-20px);\n                transform: translateY(-20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-up\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(-20px);\n                transform: translateY(-20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes fade-down\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(20px);\n                transform: translateY(20px);\n\n        opacity: 0;\n    }\n}\n@keyframes fade-down\n{\n    0%\n    {\n        -webkit-transform: translateY(0);\n                transform: translateY(0);\n\n        opacity: 1;\n    }\n    75%\n    {\n        -webkit-transform: translateY(20px);\n                transform: translateY(20px);\n\n        opacity: 0;\n    }\n}\n@-webkit-keyframes tada\n{\n    from\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n\n    10%,\n    20%\n    {\n        -webkit-transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n                transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n    }\n\n    30%,\n    50%,\n    70%,\n    90%\n    {\n        -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n                transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n    }\n\n    40%,\n    60%,\n    80%\n    {\n        -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, -10deg);\n                transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, -10deg);\n    }\n\n    to\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n}\n\n@keyframes tada\n{\n    from\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n\n    10%,\n    20%\n    {\n        -webkit-transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n                transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg);\n    }\n\n    30%,\n    50%,\n    70%,\n    90%\n    {\n        -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n                transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg);\n    }\n\n    40%,\n    60%,\n    80%\n    {\n        -webkit-transform: rotate3d(0, 0, 1, -10deg);\n                transform: rotate3d(0, 0, 1, -10deg);\n    }\n\n    to\n    {\n        -webkit-transform: scale3d(1, 1, 1);\n                transform: scale3d(1, 1, 1);\n    }\n}\n.bx-spin\n{\n    -webkit-animation: spin 2s linear infinite;\n            animation: spin 2s linear infinite;\n}\n.bx-spin-hover:hover\n{\n    -webkit-animation: spin 2s linear infinite;\n            animation: spin 2s linear infinite;\n}\n\n.bx-tada\n{\n    -webkit-animation: tada 1.5s ease infinite;\n            animation: tada 1.5s ease infinite;\n}\n.bx-tada-hover:hover\n{\n    -webkit-animation: tada 1.5s ease infinite;\n            animation: tada 1.5s ease infinite;\n}\n\n.bx-flashing\n{\n    -webkit-animation: flashing 1.5s infinite linear;\n            animation: flashing 1.5s infinite linear;\n}\n.bx-flashing-hover:hover\n{\n    -webkit-animation: flashing 1.5s infinite linear;\n            animation: flashing 1.5s infinite linear;\n}\n\n.bx-burst\n{\n    -webkit-animation: burst 1.5s infinite linear;\n            animation: burst 1.5s infinite linear;\n}\n.bx-burst-hover:hover\n{\n    -webkit-animation: burst 1.5s infinite linear;\n            animation: burst 1.5s infinite linear;\n}\n.bx-fade-up\n{\n    -webkit-animation: fade-up 1.5s infinite linear;\n            animation: fade-up 1.5s infinite linear;\n}\n.bx-fade-up-hover:hover\n{\n    -webkit-animation: fade-up 1.5s infinite linear;\n            animation: fade-up 1.5s infinite linear;\n}\n.bx-fade-down\n{\n    -webkit-animation: fade-down 1.5s infinite linear;\n            animation: fade-down 1.5s infinite linear;\n}\n.bx-fade-down-hover:hover\n{\n    -webkit-animation: fade-down 1.5s infinite linear;\n            animation: fade-down 1.5s infinite linear;\n}\n.bx-fade-left\n{\n    -webkit-animation: fade-left 1.5s infinite linear;\n            animation: fade-left 1.5s infinite linear;\n}\n.bx-fade-left-hover:hover\n{\n    -webkit-animation: fade-left 1.5s infinite linear;\n            animation: fade-left 1.5s infinite linear;\n}\n.bx-fade-right\n{\n    -webkit-animation: fade-right 1.5s infinite linear;\n            animation: fade-right 1.5s infinite linear;\n}\n.bx-fade-right-hover:hover\n{\n    -webkit-animation: fade-right 1.5s infinite linear;\n            animation: fade-right 1.5s infinite linear;\n}\n.bx-xs\n{\n    font-size: 1rem!important;\n}\n.bx-sm\n{\n    font-size: 1.55rem!important;\n}\n.bx-md\n{\n    font-size: 2.25rem!important;\n}\n.bx-lg\n{\n    font-size: 3.0rem!important;\n}\n.bx-fw\n{\n    font-size: 1.2857142857em;\n    line-height: .8em;\n\n    width: 1.2857142857em;\n    height: .8em;\n    margin-top: -.2em!important;\n\n    vertical-align: middle;\n}\n.bx-pull-left\n{\n    float: left;\n\n    margin-right: .3em!important;\n}\n.bx-pull-right\n{\n    float: right;\n\n    margin-left: .3em!important;\n}\n.bx-rotate-90\n{\n    transform: rotate(90deg);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)';\n}\n.bx-rotate-180\n{\n    transform: rotate(180deg);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)';\n}\n.bx-rotate-270\n{\n    transform: rotate(270deg);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)';\n}\n.bx-flip-horizontal\n{\n    transform: scaleX(-1);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)';\n}\n.bx-flip-vertical\n{\n    transform: scaleY(-1);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)';\n}\n.bx-border\n{\n    padding: .25em;\n\n    border: .07em solid rgba(0,0,0,.1);\n    border-radius: .25em;\n}\n.bx-border-circle\n{\n    padding: .25em;\n\n    border: .07em solid rgba(0,0,0,.1);\n    border-radius: 50%;\n}\n\n  .bxs-balloon:before {\n    content: \"\\eb60\";\n  }\n  .bxs-castle:before {\n    content: \"\\eb79\";\n  }\n  .bxs-coffee-bean:before {\n    content: \"\\eb92\";\n  }\n  .bxs-objects-horizontal-center:before {\n    content: \"\\ebab\";\n  }\n  .bxs-objects-horizontal-left:before {\n    content: \"\\ebc4\";\n  }\n  .bxs-objects-horizontal-right:before {\n    content: \"\\ebdd\";\n  }\n  .bxs-objects-vertical-bottom:before {\n    content: \"\\ebf6\";\n  }\n  .bxs-objects-vertical-center:before {\n    content: \"\\ef40\";\n  }\n  .bxs-objects-vertical-top:before {\n    content: \"\\ef41\";\n  }\n  .bxs-pear:before {\n    content: \"\\ef42\";\n  }\n  .bxs-shield-minus:before {\n    content: \"\\ef43\";\n  }\n  .bxs-shield-plus:before {\n    content: \"\\ef44\";\n  }\n  .bxs-shower:before {\n    content: \"\\ef45\";\n  }\n  .bxs-sushi:before {\n    content: \"\\ef46\";\n  }\n  .bxs-universal-access:before {\n    content: \"\\ef47\";\n  }\n  .bx-child:before {\n    content: \"\\ef48\";\n  }\n  .bx-horizontal-left:before {\n    content: \"\\ef49\";\n  }\n  .bx-horizontal-right:before {\n    content: \"\\ef4a\";\n  }\n  .bx-objects-horizontal-center:before {\n    content: \"\\ef4b\";\n  }\n  .bx-objects-horizontal-left:before {\n    content: \"\\ef4c\";\n  }\n  .bx-objects-horizontal-right:before {\n    content: \"\\ef4d\";\n  }\n  .bx-objects-vertical-bottom:before {\n    content: \"\\ef4e\";\n  }\n  .bx-objects-vertical-center:before {\n    content: \"\\ef4f\";\n  }\n  .bx-objects-vertical-top:before {\n    content: \"\\ef50\";\n  }\n  .bx-rfid:before {\n    content: \"\\ef51\";\n  }\n  .bx-shield-minus:before {\n    content: \"\\ef52\";\n  }\n  .bx-shield-plus:before {\n    content: \"\\ef53\";\n  }\n  .bx-shower:before {\n    content: \"\\ef54\";\n  }\n  .bx-sushi:before {\n    content: \"\\ef55\";\n  }\n  .bx-universal-access:before {\n    content: \"\\ef56\";\n  }\n  .bx-vertical-bottom:before {\n    content: \"\\ef57\";\n  }\n  .bx-vertical-top:before {\n    content: \"\\ef58\";\n  }\n  .bxl-graphql:before {\n    content: \"\\ef59\";\n  }\n  .bxl-typescript:before {\n    content: \"\\ef5a\";\n  }\n  .bxs-color:before {\n    content: \"\\ef39\";\n  }\n  .bx-reflect-horizontal:before {\n    content: \"\\ef3a\";\n  }\n  .bx-reflect-vertical:before {\n    content: \"\\ef3b\";\n  }\n  .bx-color:before {\n    content: \"\\ef3c\";\n  }\n  .bxl-mongodb:before {\n    content: \"\\ef3d\";\n  }\n  .bxl-postgresql:before {\n    content: \"\\ef3e\";\n  }\n  .bxl-deezer:before {\n    content: \"\\ef3f\";\n  }\n  .bxs-hard-hat:before {\n    content: \"\\ef2a\";\n  }\n  .bxs-home-alt-2:before {\n    content: \"\\ef2b\";\n  }\n  .bxs-cheese:before {\n    content: \"\\ef2c\";\n  }\n  .bx-home-alt-2:before {\n    content: \"\\ef2d\";\n  }\n  .bx-hard-hat:before {\n    content: \"\\ef2e\";\n  }\n  .bx-cheese:before {\n    content: \"\\ef2f\";\n  }\n  .bx-cart-add:before {\n    content: \"\\ef30\";\n  }\n  .bx-cart-download:before {\n    content: \"\\ef31\";\n  }\n  .bx-no-signal:before {\n    content: \"\\ef32\";\n  }\n  .bx-signal-1:before {\n    content: \"\\ef33\";\n  }\n  .bx-signal-2:before {\n    content: \"\\ef34\";\n  }\n  .bx-signal-3:before {\n    content: \"\\ef35\";\n  }\n  .bx-signal-4:before {\n    content: \"\\ef36\";\n  }\n  .bx-signal-5:before {\n    content: \"\\ef37\";\n  }\n  .bxl-xing:before {\n    content: \"\\ef38\";\n  }\n  .bxl-meta:before {\n    content: \"\\ef27\";\n  }\n  .bx-lemon:before {\n    content: \"\\ef28\";\n  }\n  .bxs-lemon:before {\n    content: \"\\ef29\";\n  }\n  .bx-cricket-ball:before {\n    content: \"\\ef0c\";\n  }\n  .bx-baguette:before {\n    content: \"\\ef0d\";\n  }\n  .bx-bowl-hot:before {\n    content: \"\\ef0e\";\n  }\n  .bx-bowl-rice:before {\n    content: \"\\ef0f\";\n  }\n  .bx-cable-car:before {\n    content: \"\\ef10\";\n  }\n  .bx-candles:before {\n    content: \"\\ef11\";\n  }\n  .bx-circle-half:before {\n    content: \"\\ef12\";\n  }\n  .bx-circle-quarter:before {\n    content: \"\\ef13\";\n  }\n  .bx-circle-three-quarter:before {\n    content: \"\\ef14\";\n  }\n  .bx-cross:before {\n    content: \"\\ef15\";\n  }\n  .bx-fork:before {\n    content: \"\\ef16\";\n  }\n  .bx-knife:before {\n    content: \"\\ef17\";\n  }\n  .bx-money-withdraw:before {\n    content: \"\\ef18\";\n  }\n  .bx-popsicle:before {\n    content: \"\\ef19\";\n  }\n  .bx-scatter-chart:before {\n    content: \"\\ef1a\";\n  }\n  .bxs-baguette:before {\n    content: \"\\ef1b\";\n  }\n  .bxs-bowl-hot:before {\n    content: \"\\ef1c\";\n  }\n  .bxs-bowl-rice:before {\n    content: \"\\ef1d\";\n  }\n  .bxs-cable-car:before {\n    content: \"\\ef1e\";\n  }\n  .bxs-circle-half:before {\n    content: \"\\ef1f\";\n  }\n  .bxs-circle-quarter:before {\n    content: \"\\ef20\";\n  }\n  .bxs-circle-three-quarter:before {\n    content: \"\\ef21\";\n  }\n  .bxs-cricket-ball:before {\n    content: \"\\ef22\";\n  }\n  .bxs-invader:before {\n    content: \"\\ef23\";\n  }\n  .bx-male-female:before {\n    content: \"\\ef24\";\n  }\n  .bxs-popsicle:before {\n    content: \"\\ef25\";\n  }\n  .bxs-tree-alt:before {\n    content: \"\\ef26\";\n  }\n  .bxl-venmo:before {\n    content: \"\\e900\";\n  }\n  .bxl-upwork:before {\n    content: \"\\e901\";\n  }\n  .bxl-netlify:before {\n    content: \"\\e902\";\n  }\n  .bxl-java:before {\n    content: \"\\e903\";\n  }\n  .bxl-heroku:before {\n    content: \"\\e904\";\n  }\n  .bxl-go-lang:before {\n    content: \"\\e905\";\n  }\n  .bxl-gmail:before {\n    content: \"\\e906\";\n  }\n  .bxl-flask:before {\n    content: \"\\e907\";\n  }\n  .bxl-99designs:before {\n    content: \"\\e908\";\n  }\n  .bxl-500px:before {\n    content: \"\\e909\";\n  }\n  .bxl-adobe:before {\n    content: \"\\e90a\";\n  }\n  .bxl-airbnb:before {\n    content: \"\\e90b\";\n  }\n  .bxl-algolia:before {\n    content: \"\\e90c\";\n  }\n  .bxl-amazon:before {\n    content: \"\\e90d\";\n  }\n  .bxl-android:before {\n    content: \"\\e90e\";\n  }\n  .bxl-angular:before {\n    content: \"\\e90f\";\n  }\n  .bxl-apple:before {\n    content: \"\\e910\";\n  }\n  .bxl-audible:before {\n    content: \"\\e911\";\n  }\n  .bxl-aws:before {\n    content: \"\\e912\";\n  }\n  .bxl-baidu:before {\n    content: \"\\e913\";\n  }\n  .bxl-behance:before {\n    content: \"\\e914\";\n  }\n  .bxl-bing:before {\n    content: \"\\e915\";\n  }\n  .bxl-bitcoin:before {\n    content: \"\\e916\";\n  }\n  .bxl-blender:before {\n    content: \"\\e917\";\n  }\n  .bxl-blogger:before {\n    content: \"\\e918\";\n  }\n  .bxl-bootstrap:before {\n    content: \"\\e919\";\n  }\n  .bxl-chrome:before {\n    content: \"\\e91a\";\n  }\n  .bxl-codepen:before {\n    content: \"\\e91b\";\n  }\n  .bxl-c-plus-plus:before {\n    content: \"\\e91c\";\n  }\n  .bxl-creative-commons:before {\n    content: \"\\e91d\";\n  }\n  .bxl-css3:before {\n    content: \"\\e91e\";\n  }\n  .bxl-dailymotion:before {\n    content: \"\\e91f\";\n  }\n  .bxl-deviantart:before {\n    content: \"\\e920\";\n  }\n  .bxl-dev-to:before {\n    content: \"\\e921\";\n  }\n  .bxl-digg:before {\n    content: \"\\e922\";\n  }\n  .bxl-digitalocean:before {\n    content: \"\\e923\";\n  }\n  .bxl-discord:before {\n    content: \"\\e924\";\n  }\n  .bxl-discord-alt:before {\n    content: \"\\e925\";\n  }\n  .bxl-discourse:before {\n    content: \"\\e926\";\n  }\n  .bxl-django:before {\n    content: \"\\e927\";\n  }\n  .bxl-docker:before {\n    content: \"\\e928\";\n  }\n  .bxl-dribbble:before {\n    content: \"\\e929\";\n  }\n  .bxl-dropbox:before {\n    content: \"\\e92a\";\n  }\n  .bxl-drupal:before {\n    content: \"\\e92b\";\n  }\n  .bxl-ebay:before {\n    content: \"\\e92c\";\n  }\n  .bxl-edge:before {\n    content: \"\\e92d\";\n  }\n  .bxl-etsy:before {\n    content: \"\\e92e\";\n  }\n  .bxl-facebook:before {\n    content: \"\\e92f\";\n  }\n  .bxl-facebook-circle:before {\n    content: \"\\e930\";\n  }\n  .bxl-facebook-square:before {\n    content: \"\\e931\";\n  }\n  .bxl-figma:before {\n    content: \"\\e932\";\n  }\n  .bxl-firebase:before {\n    content: \"\\e933\";\n  }\n  .bxl-firefox:before {\n    content: \"\\e934\";\n  }\n  .bxl-flickr:before {\n    content: \"\\e935\";\n  }\n  .bxl-flickr-square:before {\n    content: \"\\e936\";\n  }\n  .bxl-flutter:before {\n    content: \"\\e937\";\n  }\n  .bxl-foursquare:before {\n    content: \"\\e938\";\n  }\n  .bxl-git:before {\n    content: \"\\e939\";\n  }\n  .bxl-github:before {\n    content: \"\\e93a\";\n  }\n  .bxl-gitlab:before {\n    content: \"\\e93b\";\n  }\n  .bxl-google:before {\n    content: \"\\e93c\";\n  }\n  .bxl-google-cloud:before {\n    content: \"\\e93d\";\n  }\n  .bxl-google-plus:before {\n    content: \"\\e93e\";\n  }\n  .bxl-google-plus-circle:before {\n    content: \"\\e93f\";\n  }\n  .bxl-html5:before {\n    content: \"\\e940\";\n  }\n  .bxl-imdb:before {\n    content: \"\\e941\";\n  }\n  .bxl-instagram:before {\n    content: \"\\e942\";\n  }\n  .bxl-instagram-alt:before {\n    content: \"\\e943\";\n  }\n  .bxl-internet-explorer:before {\n    content: \"\\e944\";\n  }\n  .bxl-invision:before {\n    content: \"\\e945\";\n  }\n  .bxl-javascript:before {\n    content: \"\\e946\";\n  }\n  .bxl-joomla:before {\n    content: \"\\e947\";\n  }\n  .bxl-jquery:before {\n    content: \"\\e948\";\n  }\n  .bxl-jsfiddle:before {\n    content: \"\\e949\";\n  }\n  .bxl-kickstarter:before {\n    content: \"\\e94a\";\n  }\n  .bxl-kubernetes:before {\n    content: \"\\e94b\";\n  }\n  .bxl-less:before {\n    content: \"\\e94c\";\n  }\n  .bxl-linkedin:before {\n    content: \"\\e94d\";\n  }\n  .bxl-linkedin-square:before {\n    content: \"\\e94e\";\n  }\n  .bxl-magento:before {\n    content: \"\\e94f\";\n  }\n  .bxl-mailchimp:before {\n    content: \"\\e950\";\n  }\n  .bxl-markdown:before {\n    content: \"\\e951\";\n  }\n  .bxl-mastercard:before {\n    content: \"\\e952\";\n  }\n  .bxl-mastodon:before {\n    content: \"\\e953\";\n  }\n  .bxl-medium:before {\n    content: \"\\e954\";\n  }\n  .bxl-medium-old:before {\n    content: \"\\e955\";\n  }\n  .bxl-medium-square:before {\n    content: \"\\e956\";\n  }\n  .bxl-messenger:before {\n    content: \"\\e957\";\n  }\n  .bxl-microsoft:before {\n    content: \"\\e958\";\n  }\n  .bxl-microsoft-teams:before {\n    content: \"\\e959\";\n  }\n  .bxl-nodejs:before {\n    content: \"\\e95a\";\n  }\n  .bxl-ok-ru:before {\n    content: \"\\e95b\";\n  }\n  .bxl-opera:before {\n    content: \"\\e95c\";\n  }\n  .bxl-patreon:before {\n    content: \"\\e95d\";\n  }\n  .bxl-paypal:before {\n    content: \"\\e95e\";\n  }\n  .bxl-periscope:before {\n    content: \"\\e95f\";\n  }\n  .bxl-php:before {\n    content: \"\\e960\";\n  }\n  .bxl-pinterest:before {\n    content: \"\\e961\";\n  }\n  .bxl-pinterest-alt:before {\n    content: \"\\e962\";\n  }\n  .bxl-play-store:before {\n    content: \"\\e963\";\n  }\n  .bxl-pocket:before {\n    content: \"\\e964\";\n  }\n  .bxl-product-hunt:before {\n    content: \"\\e965\";\n  }\n  .bxl-python:before {\n    content: \"\\e966\";\n  }\n  .bxl-quora:before {\n    content: \"\\e967\";\n  }\n  .bxl-react:before {\n    content: \"\\e968\";\n  }\n  .bxl-redbubble:before {\n    content: \"\\e969\";\n  }\n  .bxl-reddit:before {\n    content: \"\\e96a\";\n  }\n  .bxl-redux:before {\n    content: \"\\e96b\";\n  }\n  .bxl-sass:before {\n    content: \"\\e96c\";\n  }\n  .bxl-shopify:before {\n    content: \"\\e96d\";\n  }\n  .bxl-sketch:before {\n    content: \"\\e96e\";\n  }\n  .bxl-skype:before {\n    content: \"\\e96f\";\n  }\n  .bxl-slack:before {\n    content: \"\\e970\";\n  }\n  .bxl-slack-old:before {\n    content: \"\\e971\";\n  }\n  .bxl-snapchat:before {\n    content: \"\\e972\";\n  }\n  .bxl-soundcloud:before {\n    content: \"\\e973\";\n  }\n  .bxl-spotify:before {\n    content: \"\\e974\";\n  }\n  .bxl-spring-boot:before {\n    content: \"\\e975\";\n  }\n  .bxl-squarespace:before {\n    content: \"\\e976\";\n  }\n  .bxl-stack-overflow:before {\n    content: \"\\e977\";\n  }\n  .bxl-steam:before {\n    content: \"\\e978\";\n  }\n  .bxl-stripe:before {\n    content: \"\\e979\";\n  }\n  .bxl-tailwind-css:before {\n    content: \"\\e97a\";\n  }\n  .bxl-telegram:before {\n    content: \"\\e97b\";\n  }\n  .bxl-tiktok:before {\n    content: \"\\e97c\";\n  }\n  .bxl-trello:before {\n    content: \"\\e97d\";\n  }\n  .bxl-trip-advisor:before {\n    content: \"\\e97e\";\n  }\n  .bxl-tumblr:before {\n    content: \"\\e97f\";\n  }\n  .bxl-tux:before {\n    content: \"\\e980\";\n  }\n  .bxl-twitch:before {\n    content: \"\\e981\";\n  }\n  .bxl-twitter:before {\n    content: \"\\e982\";\n  }\n  .bxl-unity:before {\n    content: \"\\e983\";\n  }\n  .bxl-unsplash:before {\n    content: \"\\e984\";\n  }\n  .bxl-vimeo:before {\n    content: \"\\e985\";\n  }\n  .bxl-visa:before {\n    content: \"\\e986\";\n  }\n  .bxl-visual-studio:before {\n    content: \"\\e987\";\n  }\n  .bxl-vk:before {\n    content: \"\\e988\";\n  }\n  .bxl-vuejs:before {\n    content: \"\\e989\";\n  }\n  .bxl-whatsapp:before {\n    content: \"\\e98a\";\n  }\n  .bxl-whatsapp-square:before {\n    content: \"\\e98b\";\n  }\n  .bxl-wikipedia:before {\n    content: \"\\e98c\";\n  }\n  .bxl-windows:before {\n    content: \"\\e98d\";\n  }\n  .bxl-wix:before {\n    content: \"\\e98e\";\n  }\n  .bxl-wordpress:before {\n    content: \"\\e98f\";\n  }\n  .bxl-yahoo:before {\n    content: \"\\e990\";\n  }\n  .bxl-yelp:before {\n    content: \"\\e991\";\n  }\n  .bxl-youtube:before {\n    content: \"\\e992\";\n  }\n  .bxl-zoom:before {\n    content: \"\\e993\";\n  }\n  .bx-collapse-alt:before {\n    content: \"\\e994\";\n  }\n  .bx-collapse-horizontal:before {\n    content: \"\\e995\";\n  }\n  .bx-collapse-vertical:before {\n    content: \"\\e996\";\n  }\n  .bx-expand-horizontal:before {\n    content: \"\\e997\";\n  }\n  .bx-expand-vertical:before {\n    content: \"\\e998\";\n  }\n  .bx-injection:before {\n    content: \"\\e999\";\n  }\n  .bx-leaf:before {\n    content: \"\\e99a\";\n  }\n  .bx-math:before {\n    content: \"\\e99b\";\n  }\n  .bx-party:before {\n    content: \"\\e99c\";\n  }\n  .bx-abacus:before {\n    content: \"\\e99d\";\n  }\n  .bx-accessibility:before {\n    content: \"\\e99e\";\n  }\n  .bx-add-to-queue:before {\n    content: \"\\e99f\";\n  }\n  .bx-adjust:before {\n    content: \"\\e9a0\";\n  }\n  .bx-alarm:before {\n    content: \"\\e9a1\";\n  }\n  .bx-alarm-add:before {\n    content: \"\\e9a2\";\n  }\n  .bx-alarm-exclamation:before {\n    content: \"\\e9a3\";\n  }\n  .bx-alarm-off:before {\n    content: \"\\e9a4\";\n  }\n  .bx-alarm-snooze:before {\n    content: \"\\e9a5\";\n  }\n  .bx-album:before {\n    content: \"\\e9a6\";\n  }\n  .bx-align-justify:before {\n    content: \"\\e9a7\";\n  }\n  .bx-align-left:before {\n    content: \"\\e9a8\";\n  }\n  .bx-align-middle:before {\n    content: \"\\e9a9\";\n  }\n  .bx-align-right:before {\n    content: \"\\e9aa\";\n  }\n  .bx-analyse:before {\n    content: \"\\e9ab\";\n  }\n  .bx-anchor:before {\n    content: \"\\e9ac\";\n  }\n  .bx-angry:before {\n    content: \"\\e9ad\";\n  }\n  .bx-aperture:before {\n    content: \"\\e9ae\";\n  }\n  .bx-arch:before {\n    content: \"\\e9af\";\n  }\n  .bx-archive:before {\n    content: \"\\e9b0\";\n  }\n  .bx-archive-in:before {\n    content: \"\\e9b1\";\n  }\n  .bx-archive-out:before {\n    content: \"\\e9b2\";\n  }\n  .bx-area:before {\n    content: \"\\e9b3\";\n  }\n  .bx-arrow-back:before {\n    content: \"\\e9b4\";\n  }\n  .bx-arrow-from-bottom:before {\n    content: \"\\e9b5\";\n  }\n  .bx-arrow-from-left:before {\n    content: \"\\e9b6\";\n  }\n  .bx-arrow-from-right:before {\n    content: \"\\e9b7\";\n  }\n  .bx-arrow-from-top:before {\n    content: \"\\e9b8\";\n  }\n  .bx-arrow-to-bottom:before {\n    content: \"\\e9b9\";\n  }\n  .bx-arrow-to-left:before {\n    content: \"\\e9ba\";\n  }\n  .bx-arrow-to-right:before {\n    content: \"\\e9bb\";\n  }\n  .bx-arrow-to-top:before {\n    content: \"\\e9bc\";\n  }\n  .bx-at:before {\n    content: \"\\e9bd\";\n  }\n  .bx-atom:before {\n    content: \"\\e9be\";\n  }\n  .bx-award:before {\n    content: \"\\e9bf\";\n  }\n  .bx-badge:before {\n    content: \"\\e9c0\";\n  }\n  .bx-badge-check:before {\n    content: \"\\e9c1\";\n  }\n  .bx-ball:before {\n    content: \"\\e9c2\";\n  }\n  .bx-band-aid:before {\n    content: \"\\e9c3\";\n  }\n  .bx-bar-chart:before {\n    content: \"\\e9c4\";\n  }\n  .bx-bar-chart-alt:before {\n    content: \"\\e9c5\";\n  }\n  .bx-bar-chart-alt-2:before {\n    content: \"\\e9c6\";\n  }\n  .bx-bar-chart-square:before {\n    content: \"\\e9c7\";\n  }\n  .bx-barcode:before {\n    content: \"\\e9c8\";\n  }\n  .bx-barcode-reader:before {\n    content: \"\\e9c9\";\n  }\n  .bx-baseball:before {\n    content: \"\\e9ca\";\n  }\n  .bx-basket:before {\n    content: \"\\e9cb\";\n  }\n  .bx-basketball:before {\n    content: \"\\e9cc\";\n  }\n  .bx-bath:before {\n    content: \"\\e9cd\";\n  }\n  .bx-battery:before {\n    content: \"\\e9ce\";\n  }\n  .bx-bed:before {\n    content: \"\\e9cf\";\n  }\n  .bx-been-here:before {\n    content: \"\\e9d0\";\n  }\n  .bx-beer:before {\n    content: \"\\e9d1\";\n  }\n  .bx-bell:before {\n    content: \"\\e9d2\";\n  }\n  .bx-bell-minus:before {\n    content: \"\\e9d3\";\n  }\n  .bx-bell-off:before {\n    content: \"\\e9d4\";\n  }\n  .bx-bell-plus:before {\n    content: \"\\e9d5\";\n  }\n  .bx-bible:before {\n    content: \"\\e9d6\";\n  }\n  .bx-bitcoin:before {\n    content: \"\\e9d7\";\n  }\n  .bx-blanket:before {\n    content: \"\\e9d8\";\n  }\n  .bx-block:before {\n    content: \"\\e9d9\";\n  }\n  .bx-bluetooth:before {\n    content: \"\\e9da\";\n  }\n  .bx-body:before {\n    content: \"\\e9db\";\n  }\n  .bx-bold:before {\n    content: \"\\e9dc\";\n  }\n  .bx-bolt-circle:before {\n    content: \"\\e9dd\";\n  }\n  .bx-bomb:before {\n    content: \"\\e9de\";\n  }\n  .bx-bone:before {\n    content: \"\\e9df\";\n  }\n  .bx-bong:before {\n    content: \"\\e9e0\";\n  }\n  .bx-book:before {\n    content: \"\\e9e1\";\n  }\n  .bx-book-add:before {\n    content: \"\\e9e2\";\n  }\n  .bx-book-alt:before {\n    content: \"\\e9e3\";\n  }\n  .bx-book-bookmark:before {\n    content: \"\\e9e4\";\n  }\n  .bx-book-content:before {\n    content: \"\\e9e5\";\n  }\n  .bx-book-heart:before {\n    content: \"\\e9e6\";\n  }\n  .bx-bookmark:before {\n    content: \"\\e9e7\";\n  }\n  .bx-bookmark-alt:before {\n    content: \"\\e9e8\";\n  }\n  .bx-bookmark-alt-minus:before {\n    content: \"\\e9e9\";\n  }\n  .bx-bookmark-alt-plus:before {\n    content: \"\\e9ea\";\n  }\n  .bx-bookmark-heart:before {\n    content: \"\\e9eb\";\n  }\n  .bx-bookmark-minus:before {\n    content: \"\\e9ec\";\n  }\n  .bx-bookmark-plus:before {\n    content: \"\\e9ed\";\n  }\n  .bx-bookmarks:before {\n    content: \"\\e9ee\";\n  }\n  .bx-book-open:before {\n    content: \"\\e9ef\";\n  }\n  .bx-book-reader:before {\n    content: \"\\e9f0\";\n  }\n  .bx-border-all:before {\n    content: \"\\e9f1\";\n  }\n  .bx-border-bottom:before {\n    content: \"\\e9f2\";\n  }\n  .bx-border-inner:before {\n    content: \"\\e9f3\";\n  }\n  .bx-border-left:before {\n    content: \"\\e9f4\";\n  }\n  .bx-border-none:before {\n    content: \"\\e9f5\";\n  }\n  .bx-border-outer:before {\n    content: \"\\e9f6\";\n  }\n  .bx-border-radius:before {\n    content: \"\\e9f7\";\n  }\n  .bx-border-right:before {\n    content: \"\\e9f8\";\n  }\n  .bx-border-top:before {\n    content: \"\\e9f9\";\n  }\n  .bx-bot:before {\n    content: \"\\e9fa\";\n  }\n  .bx-bowling-ball:before {\n    content: \"\\e9fb\";\n  }\n  .bx-box:before {\n    content: \"\\e9fc\";\n  }\n  .bx-bracket:before {\n    content: \"\\e9fd\";\n  }\n  .bx-braille:before {\n    content: \"\\e9fe\";\n  }\n  .bx-brain:before {\n    content: \"\\e9ff\";\n  }\n  .bx-briefcase:before {\n    content: \"\\ea00\";\n  }\n  .bx-briefcase-alt:before {\n    content: \"\\ea01\";\n  }\n  .bx-briefcase-alt-2:before {\n    content: \"\\ea02\";\n  }\n  .bx-brightness:before {\n    content: \"\\ea03\";\n  }\n  .bx-brightness-half:before {\n    content: \"\\ea04\";\n  }\n  .bx-broadcast:before {\n    content: \"\\ea05\";\n  }\n  .bx-brush:before {\n    content: \"\\ea06\";\n  }\n  .bx-brush-alt:before {\n    content: \"\\ea07\";\n  }\n  .bx-bug:before {\n    content: \"\\ea08\";\n  }\n  .bx-bug-alt:before {\n    content: \"\\ea09\";\n  }\n  .bx-building:before {\n    content: \"\\ea0a\";\n  }\n  .bx-building-house:before {\n    content: \"\\ea0b\";\n  }\n  .bx-buildings:before {\n    content: \"\\ea0c\";\n  }\n  .bx-bulb:before {\n    content: \"\\ea0d\";\n  }\n  .bx-bullseye:before {\n    content: \"\\ea0e\";\n  }\n  .bx-buoy:before {\n    content: \"\\ea0f\";\n  }\n  .bx-bus:before {\n    content: \"\\ea10\";\n  }\n  .bx-bus-school:before {\n    content: \"\\ea11\";\n  }\n  .bx-cabinet:before {\n    content: \"\\ea12\";\n  }\n  .bx-cake:before {\n    content: \"\\ea13\";\n  }\n  .bx-calculator:before {\n    content: \"\\ea14\";\n  }\n  .bx-calendar:before {\n    content: \"\\ea15\";\n  }\n  .bx-calendar-alt:before {\n    content: \"\\ea16\";\n  }\n  .bx-calendar-check:before {\n    content: \"\\ea17\";\n  }\n  .bx-calendar-edit:before {\n    content: \"\\ea18\";\n  }\n  .bx-calendar-event:before {\n    content: \"\\ea19\";\n  }\n  .bx-calendar-exclamation:before {\n    content: \"\\ea1a\";\n  }\n  .bx-calendar-heart:before {\n    content: \"\\ea1b\";\n  }\n  .bx-calendar-minus:before {\n    content: \"\\ea1c\";\n  }\n  .bx-calendar-plus:before {\n    content: \"\\ea1d\";\n  }\n  .bx-calendar-star:before {\n    content: \"\\ea1e\";\n  }\n  .bx-calendar-week:before {\n    content: \"\\ea1f\";\n  }\n  .bx-calendar-x:before {\n    content: \"\\ea20\";\n  }\n  .bx-camera:before {\n    content: \"\\ea21\";\n  }\n  .bx-camera-home:before {\n    content: \"\\ea22\";\n  }\n  .bx-camera-movie:before {\n    content: \"\\ea23\";\n  }\n  .bx-camera-off:before {\n    content: \"\\ea24\";\n  }\n  .bx-capsule:before {\n    content: \"\\ea25\";\n  }\n  .bx-captions:before {\n    content: \"\\ea26\";\n  }\n  .bx-car:before {\n    content: \"\\ea27\";\n  }\n  .bx-card:before {\n    content: \"\\ea28\";\n  }\n  .bx-caret-down:before {\n    content: \"\\ea29\";\n  }\n  .bx-caret-down-circle:before {\n    content: \"\\ea2a\";\n  }\n  .bx-caret-down-square:before {\n    content: \"\\ea2b\";\n  }\n  .bx-caret-left:before {\n    content: \"\\ea2c\";\n  }\n  .bx-caret-left-circle:before {\n    content: \"\\ea2d\";\n  }\n  .bx-caret-left-square:before {\n    content: \"\\ea2e\";\n  }\n  .bx-caret-right:before {\n    content: \"\\ea2f\";\n  }\n  .bx-caret-right-circle:before {\n    content: \"\\ea30\";\n  }\n  .bx-caret-right-square:before {\n    content: \"\\ea31\";\n  }\n  .bx-caret-up:before {\n    content: \"\\ea32\";\n  }\n  .bx-caret-up-circle:before {\n    content: \"\\ea33\";\n  }\n  .bx-caret-up-square:before {\n    content: \"\\ea34\";\n  }\n  .bx-carousel:before {\n    content: \"\\ea35\";\n  }\n  .bx-cart:before {\n    content: \"\\ea36\";\n  }\n  .bx-cart-alt:before {\n    content: \"\\ea37\";\n  }\n  .bx-cast:before {\n    content: \"\\ea38\";\n  }\n  .bx-category:before {\n    content: \"\\ea39\";\n  }\n  .bx-category-alt:before {\n    content: \"\\ea3a\";\n  }\n  .bx-cctv:before {\n    content: \"\\ea3b\";\n  }\n  .bx-certification:before {\n    content: \"\\ea3c\";\n  }\n  .bx-chair:before {\n    content: \"\\ea3d\";\n  }\n  .bx-chalkboard:before {\n    content: \"\\ea3e\";\n  }\n  .bx-chart:before {\n    content: \"\\ea3f\";\n  }\n  .bx-chat:before {\n    content: \"\\ea40\";\n  }\n  .bx-check:before {\n    content: \"\\ea41\";\n  }\n  .bx-checkbox:before {\n    content: \"\\ea42\";\n  }\n  .bx-checkbox-checked:before {\n    content: \"\\ea43\";\n  }\n  .bx-checkbox-minus:before {\n    content: \"\\ea44\";\n  }\n  .bx-checkbox-square:before {\n    content: \"\\ea45\";\n  }\n  .bx-check-circle:before {\n    content: \"\\ea46\";\n  }\n  .bx-check-double:before {\n    content: \"\\ea47\";\n  }\n  .bx-check-shield:before {\n    content: \"\\ea48\";\n  }\n  .bx-check-square:before {\n    content: \"\\ea49\";\n  }\n  .bx-chevron-down:before {\n    content: \"\\ea4a\";\n  }\n  .bx-chevron-down-circle:before {\n    content: \"\\ea4b\";\n  }\n  .bx-chevron-down-square:before {\n    content: \"\\ea4c\";\n  }\n  .bx-chevron-left:before {\n    content: \"\\ea4d\";\n  }\n  .bx-chevron-left-circle:before {\n    content: \"\\ea4e\";\n  }\n  .bx-chevron-left-square:before {\n    content: \"\\ea4f\";\n  }\n  .bx-chevron-right:before {\n    content: \"\\ea50\";\n  }\n  .bx-chevron-right-circle:before {\n    content: \"\\ea51\";\n  }\n  .bx-chevron-right-square:before {\n    content: \"\\ea52\";\n  }\n  .bx-chevrons-down:before {\n    content: \"\\ea53\";\n  }\n  .bx-chevrons-left:before {\n    content: \"\\ea54\";\n  }\n  .bx-chevrons-right:before {\n    content: \"\\ea55\";\n  }\n  .bx-chevrons-up:before {\n    content: \"\\ea56\";\n  }\n  .bx-chevron-up:before {\n    content: \"\\ea57\";\n  }\n  .bx-chevron-up-circle:before {\n    content: \"\\ea58\";\n  }\n  .bx-chevron-up-square:before {\n    content: \"\\ea59\";\n  }\n  .bx-chip:before {\n    content: \"\\ea5a\";\n  }\n  .bx-church:before {\n    content: \"\\ea5b\";\n  }\n  .bx-circle:before {\n    content: \"\\ea5c\";\n  }\n  .bx-clinic:before {\n    content: \"\\ea5d\";\n  }\n  .bx-clipboard:before {\n    content: \"\\ea5e\";\n  }\n  .bx-closet:before {\n    content: \"\\ea5f\";\n  }\n  .bx-cloud:before {\n    content: \"\\ea60\";\n  }\n  .bx-cloud-download:before {\n    content: \"\\ea61\";\n  }\n  .bx-cloud-drizzle:before {\n    content: \"\\ea62\";\n  }\n  .bx-cloud-lightning:before {\n    content: \"\\ea63\";\n  }\n  .bx-cloud-light-rain:before {\n    content: \"\\ea64\";\n  }\n  .bx-cloud-rain:before {\n    content: \"\\ea65\";\n  }\n  .bx-cloud-snow:before {\n    content: \"\\ea66\";\n  }\n  .bx-cloud-upload:before {\n    content: \"\\ea67\";\n  }\n  .bx-code:before {\n    content: \"\\ea68\";\n  }\n  .bx-code-alt:before {\n    content: \"\\ea69\";\n  }\n  .bx-code-block:before {\n    content: \"\\ea6a\";\n  }\n  .bx-code-curly:before {\n    content: \"\\ea6b\";\n  }\n  .bx-coffee:before {\n    content: \"\\ea6c\";\n  }\n  .bx-coffee-togo:before {\n    content: \"\\ea6d\";\n  }\n  .bx-cog:before {\n    content: \"\\ea6e\";\n  }\n  .bx-coin:before {\n    content: \"\\ea6f\";\n  }\n  .bx-coin-stack:before {\n    content: \"\\ea70\";\n  }\n  .bx-collapse:before {\n    content: \"\\ea71\";\n  }\n  .bx-collection:before {\n    content: \"\\ea72\";\n  }\n  .bx-color-fill:before {\n    content: \"\\ea73\";\n  }\n  .bx-columns:before {\n    content: \"\\ea74\";\n  }\n  .bx-command:before {\n    content: \"\\ea75\";\n  }\n  .bx-comment:before {\n    content: \"\\ea76\";\n  }\n  .bx-comment-add:before {\n    content: \"\\ea77\";\n  }\n  .bx-comment-check:before {\n    content: \"\\ea78\";\n  }\n  .bx-comment-detail:before {\n    content: \"\\ea79\";\n  }\n  .bx-comment-dots:before {\n    content: \"\\ea7a\";\n  }\n  .bx-comment-edit:before {\n    content: \"\\ea7b\";\n  }\n  .bx-comment-error:before {\n    content: \"\\ea7c\";\n  }\n  .bx-comment-minus:before {\n    content: \"\\ea7d\";\n  }\n  .bx-comment-x:before {\n    content: \"\\ea7e\";\n  }\n  .bx-compass:before {\n    content: \"\\ea7f\";\n  }\n  .bx-confused:before {\n    content: \"\\ea80\";\n  }\n  .bx-conversation:before {\n    content: \"\\ea81\";\n  }\n  .bx-cookie:before {\n    content: \"\\ea82\";\n  }\n  .bx-cool:before {\n    content: \"\\ea83\";\n  }\n  .bx-copy:before {\n    content: \"\\ea84\";\n  }\n  .bx-copy-alt:before {\n    content: \"\\ea85\";\n  }\n  .bx-copyright:before {\n    content: \"\\ea86\";\n  }\n  .bx-credit-card:before {\n    content: \"\\ea87\";\n  }\n  .bx-credit-card-alt:before {\n    content: \"\\ea88\";\n  }\n  .bx-credit-card-front:before {\n    content: \"\\ea89\";\n  }\n  .bx-crop:before {\n    content: \"\\ea8a\";\n  }\n  .bx-crosshair:before {\n    content: \"\\ea8b\";\n  }\n  .bx-crown:before {\n    content: \"\\ea8c\";\n  }\n  .bx-cube:before {\n    content: \"\\ea8d\";\n  }\n  .bx-cube-alt:before {\n    content: \"\\ea8e\";\n  }\n  .bx-cuboid:before {\n    content: \"\\ea8f\";\n  }\n  .bx-current-location:before {\n    content: \"\\ea90\";\n  }\n  .bx-customize:before {\n    content: \"\\ea91\";\n  }\n  .bx-cut:before {\n    content: \"\\ea92\";\n  }\n  .bx-cycling:before {\n    content: \"\\ea93\";\n  }\n  .bx-cylinder:before {\n    content: \"\\ea94\";\n  }\n  .bx-data:before {\n    content: \"\\ea95\";\n  }\n  .bx-desktop:before {\n    content: \"\\ea96\";\n  }\n  .bx-detail:before {\n    content: \"\\ea97\";\n  }\n  .bx-devices:before {\n    content: \"\\ea98\";\n  }\n  .bx-dialpad:before {\n    content: \"\\ea99\";\n  }\n  .bx-dialpad-alt:before {\n    content: \"\\ea9a\";\n  }\n  .bx-diamond:before {\n    content: \"\\ea9b\";\n  }\n  .bx-dice-1:before {\n    content: \"\\ea9c\";\n  }\n  .bx-dice-2:before {\n    content: \"\\ea9d\";\n  }\n  .bx-dice-3:before {\n    content: \"\\ea9e\";\n  }\n  .bx-dice-4:before {\n    content: \"\\ea9f\";\n  }\n  .bx-dice-5:before {\n    content: \"\\eaa0\";\n  }\n  .bx-dice-6:before {\n    content: \"\\eaa1\";\n  }\n  .bx-directions:before {\n    content: \"\\eaa2\";\n  }\n  .bx-disc:before {\n    content: \"\\eaa3\";\n  }\n  .bx-dish:before {\n    content: \"\\eaa4\";\n  }\n  .bx-dislike:before {\n    content: \"\\eaa5\";\n  }\n  .bx-dizzy:before {\n    content: \"\\eaa6\";\n  }\n  .bx-dna:before {\n    content: \"\\eaa7\";\n  }\n  .bx-dock-bottom:before {\n    content: \"\\eaa8\";\n  }\n  .bx-dock-left:before {\n    content: \"\\eaa9\";\n  }\n  .bx-dock-right:before {\n    content: \"\\eaaa\";\n  }\n  .bx-dock-top:before {\n    content: \"\\eaab\";\n  }\n  .bx-dollar:before {\n    content: \"\\eaac\";\n  }\n  .bx-dollar-circle:before {\n    content: \"\\eaad\";\n  }\n  .bx-donate-blood:before {\n    content: \"\\eaae\";\n  }\n  .bx-donate-heart:before {\n    content: \"\\eaaf\";\n  }\n  .bx-door-open:before {\n    content: \"\\eab0\";\n  }\n  .bx-dots-horizontal:before {\n    content: \"\\eab1\";\n  }\n  .bx-dots-horizontal-rounded:before {\n    content: \"\\eab2\";\n  }\n  .bx-dots-vertical:before {\n    content: \"\\eab3\";\n  }\n  .bx-dots-vertical-rounded:before {\n    content: \"\\eab4\";\n  }\n  .bx-doughnut-chart:before {\n    content: \"\\eab5\";\n  }\n  .bx-down-arrow:before {\n    content: \"\\eab6\";\n  }\n  .bx-down-arrow-alt:before {\n    content: \"\\eab7\";\n  }\n  .bx-down-arrow-circle:before {\n    content: \"\\eab8\";\n  }\n  .bx-download:before {\n    content: \"\\eab9\";\n  }\n  .bx-downvote:before {\n    content: \"\\eaba\";\n  }\n  .bx-drink:before {\n    content: \"\\eabb\";\n  }\n  .bx-droplet:before {\n    content: \"\\eabc\";\n  }\n  .bx-dumbbell:before {\n    content: \"\\eabd\";\n  }\n  .bx-duplicate:before {\n    content: \"\\eabe\";\n  }\n  .bx-edit:before {\n    content: \"\\eabf\";\n  }\n  .bx-edit-alt:before {\n    content: \"\\eac0\";\n  }\n  .bx-envelope:before {\n    content: \"\\eac1\";\n  }\n  .bx-envelope-open:before {\n    content: \"\\eac2\";\n  }\n  .bx-equalizer:before {\n    content: \"\\eac3\";\n  }\n  .bx-eraser:before {\n    content: \"\\eac4\";\n  }\n  .bx-error:before {\n    content: \"\\eac5\";\n  }\n  .bx-error-alt:before {\n    content: \"\\eac6\";\n  }\n  .bx-error-circle:before {\n    content: \"\\eac7\";\n  }\n  .bx-euro:before {\n    content: \"\\eac8\";\n  }\n  .bx-exclude:before {\n    content: \"\\eac9\";\n  }\n  .bx-exit:before {\n    content: \"\\eaca\";\n  }\n  .bx-exit-fullscreen:before {\n    content: \"\\eacb\";\n  }\n  .bx-expand:before {\n    content: \"\\eacc\";\n  }\n  .bx-expand-alt:before {\n    content: \"\\eacd\";\n  }\n  .bx-export:before {\n    content: \"\\eace\";\n  }\n  .bx-extension:before {\n    content: \"\\eacf\";\n  }\n  .bx-face:before {\n    content: \"\\ead0\";\n  }\n  .bx-fast-forward:before {\n    content: \"\\ead1\";\n  }\n  .bx-fast-forward-circle:before {\n    content: \"\\ead2\";\n  }\n  .bx-female:before {\n    content: \"\\ead3\";\n  }\n  .bx-female-sign:before {\n    content: \"\\ead4\";\n  }\n  .bx-file:before {\n    content: \"\\ead5\";\n  }\n  .bx-file-blank:before {\n    content: \"\\ead6\";\n  }\n  .bx-file-find:before {\n    content: \"\\ead7\";\n  }\n  .bx-film:before {\n    content: \"\\ead8\";\n  }\n  .bx-filter:before {\n    content: \"\\ead9\";\n  }\n  .bx-filter-alt:before {\n    content: \"\\eada\";\n  }\n  .bx-fingerprint:before {\n    content: \"\\eadb\";\n  }\n  .bx-first-aid:before {\n    content: \"\\eadc\";\n  }\n  .bx-first-page:before {\n    content: \"\\eadd\";\n  }\n  .bx-flag:before {\n    content: \"\\eade\";\n  }\n  .bx-folder:before {\n    content: \"\\eadf\";\n  }\n  .bx-folder-minus:before {\n    content: \"\\eae0\";\n  }\n  .bx-folder-open:before {\n    content: \"\\eae1\";\n  }\n  .bx-folder-plus:before {\n    content: \"\\eae2\";\n  }\n  .bx-font:before {\n    content: \"\\eae3\";\n  }\n  .bx-font-color:before {\n    content: \"\\eae4\";\n  }\n  .bx-font-family:before {\n    content: \"\\eae5\";\n  }\n  .bx-font-size:before {\n    content: \"\\eae6\";\n  }\n  .bx-food-menu:before {\n    content: \"\\eae7\";\n  }\n  .bx-food-tag:before {\n    content: \"\\eae8\";\n  }\n  .bx-football:before {\n    content: \"\\eae9\";\n  }\n  .bx-fridge:before {\n    content: \"\\eaea\";\n  }\n  .bx-fullscreen:before {\n    content: \"\\eaeb\";\n  }\n  .bx-game:before {\n    content: \"\\eaec\";\n  }\n  .bx-gas-pump:before {\n    content: \"\\eaed\";\n  }\n  .bx-ghost:before {\n    content: \"\\eaee\";\n  }\n  .bx-gift:before {\n    content: \"\\eaef\";\n  }\n  .bx-git-branch:before {\n    content: \"\\eaf0\";\n  }\n  .bx-git-commit:before {\n    content: \"\\eaf1\";\n  }\n  .bx-git-compare:before {\n    content: \"\\eaf2\";\n  }\n  .bx-git-merge:before {\n    content: \"\\eaf3\";\n  }\n  .bx-git-pull-request:before {\n    content: \"\\eaf4\";\n  }\n  .bx-git-repo-forked:before {\n    content: \"\\eaf5\";\n  }\n  .bx-glasses:before {\n    content: \"\\eaf6\";\n  }\n  .bx-glasses-alt:before {\n    content: \"\\eaf7\";\n  }\n  .bx-globe:before {\n    content: \"\\eaf8\";\n  }\n  .bx-globe-alt:before {\n    content: \"\\eaf9\";\n  }\n  .bx-grid:before {\n    content: \"\\eafa\";\n  }\n  .bx-grid-alt:before {\n    content: \"\\eafb\";\n  }\n  .bx-grid-horizontal:before {\n    content: \"\\eafc\";\n  }\n  .bx-grid-small:before {\n    content: \"\\eafd\";\n  }\n  .bx-grid-vertical:before {\n    content: \"\\eafe\";\n  }\n  .bx-group:before {\n    content: \"\\eaff\";\n  }\n  .bx-handicap:before {\n    content: \"\\eb00\";\n  }\n  .bx-happy:before {\n    content: \"\\eb01\";\n  }\n  .bx-happy-alt:before {\n    content: \"\\eb02\";\n  }\n  .bx-happy-beaming:before {\n    content: \"\\eb03\";\n  }\n  .bx-happy-heart-eyes:before {\n    content: \"\\eb04\";\n  }\n  .bx-hash:before {\n    content: \"\\eb05\";\n  }\n  .bx-hdd:before {\n    content: \"\\eb06\";\n  }\n  .bx-heading:before {\n    content: \"\\eb07\";\n  }\n  .bx-headphone:before {\n    content: \"\\eb08\";\n  }\n  .bx-health:before {\n    content: \"\\eb09\";\n  }\n  .bx-heart:before {\n    content: \"\\eb0a\";\n  }\n  .bx-heart-circle:before {\n    content: \"\\eb0b\";\n  }\n  .bx-heart-square:before {\n    content: \"\\eb0c\";\n  }\n  .bx-help-circle:before {\n    content: \"\\eb0d\";\n  }\n  .bx-hide:before {\n    content: \"\\eb0e\";\n  }\n  .bx-highlight:before {\n    content: \"\\eb0f\";\n  }\n  .bx-history:before {\n    content: \"\\eb10\";\n  }\n  .bx-hive:before {\n    content: \"\\eb11\";\n  }\n  .bx-home:before {\n    content: \"\\eb12\";\n  }\n  .bx-home-alt:before {\n    content: \"\\eb13\";\n  }\n  .bx-home-circle:before {\n    content: \"\\eb14\";\n  }\n  .bx-home-heart:before {\n    content: \"\\eb15\";\n  }\n  .bx-home-smile:before {\n    content: \"\\eb16\";\n  }\n  .bx-horizontal-center:before {\n    content: \"\\eb17\";\n  }\n  .bx-hotel:before {\n    content: \"\\eb18\";\n  }\n  .bx-hourglass:before {\n    content: \"\\eb19\";\n  }\n  .bx-id-card:before {\n    content: \"\\eb1a\";\n  }\n  .bx-image:before {\n    content: \"\\eb1b\";\n  }\n  .bx-image-add:before {\n    content: \"\\eb1c\";\n  }\n  .bx-image-alt:before {\n    content: \"\\eb1d\";\n  }\n  .bx-images:before {\n    content: \"\\eb1e\";\n  }\n  .bx-import:before {\n    content: \"\\eb1f\";\n  }\n  .bx-infinite:before {\n    content: \"\\eb20\";\n  }\n  .bx-info-circle:before {\n    content: \"\\eb21\";\n  }\n  .bx-info-square:before {\n    content: \"\\eb22\";\n  }\n  .bx-intersect:before {\n    content: \"\\eb23\";\n  }\n  .bx-italic:before {\n    content: \"\\eb24\";\n  }\n  .bx-joystick:before {\n    content: \"\\eb25\";\n  }\n  .bx-joystick-alt:before {\n    content: \"\\eb26\";\n  }\n  .bx-joystick-button:before {\n    content: \"\\eb27\";\n  }\n  .bx-key:before {\n    content: \"\\eb28\";\n  }\n  .bx-label:before {\n    content: \"\\eb29\";\n  }\n  .bx-landscape:before {\n    content: \"\\eb2a\";\n  }\n  .bx-laptop:before {\n    content: \"\\eb2b\";\n  }\n  .bx-last-page:before {\n    content: \"\\eb2c\";\n  }\n  .bx-laugh:before {\n    content: \"\\eb2d\";\n  }\n  .bx-layer:before {\n    content: \"\\eb2e\";\n  }\n  .bx-layer-minus:before {\n    content: \"\\eb2f\";\n  }\n  .bx-layer-plus:before {\n    content: \"\\eb30\";\n  }\n  .bx-layout:before {\n    content: \"\\eb31\";\n  }\n  .bx-left-arrow:before {\n    content: \"\\eb32\";\n  }\n  .bx-left-arrow-alt:before {\n    content: \"\\eb33\";\n  }\n  .bx-left-arrow-circle:before {\n    content: \"\\eb34\";\n  }\n  .bx-left-down-arrow-circle:before {\n    content: \"\\eb35\";\n  }\n  .bx-left-indent:before {\n    content: \"\\eb36\";\n  }\n  .bx-left-top-arrow-circle:before {\n    content: \"\\eb37\";\n  }\n  .bx-library:before {\n    content: \"\\eb38\";\n  }\n  .bx-like:before {\n    content: \"\\eb39\";\n  }\n  .bx-line-chart:before {\n    content: \"\\eb3a\";\n  }\n  .bx-line-chart-down:before {\n    content: \"\\eb3b\";\n  }\n  .bx-link:before {\n    content: \"\\eb3c\";\n  }\n  .bx-link-alt:before {\n    content: \"\\eb3d\";\n  }\n  .bx-link-external:before {\n    content: \"\\eb3e\";\n  }\n  .bx-lira:before {\n    content: \"\\eb3f\";\n  }\n  .bx-list-check:before {\n    content: \"\\eb40\";\n  }\n  .bx-list-minus:before {\n    content: \"\\eb41\";\n  }\n  .bx-list-ol:before {\n    content: \"\\eb42\";\n  }\n  .bx-list-plus:before {\n    content: \"\\eb43\";\n  }\n  .bx-list-ul:before {\n    content: \"\\eb44\";\n  }\n  .bx-loader:before {\n    content: \"\\eb45\";\n  }\n  .bx-loader-alt:before {\n    content: \"\\eb46\";\n  }\n  .bx-loader-circle:before {\n    content: \"\\eb47\";\n  }\n  .bx-location-plus:before {\n    content: \"\\eb48\";\n  }\n  .bx-lock:before {\n    content: \"\\eb49\";\n  }\n  .bx-lock-alt:before {\n    content: \"\\eb4a\";\n  }\n  .bx-lock-open:before {\n    content: \"\\eb4b\";\n  }\n  .bx-lock-open-alt:before {\n    content: \"\\eb4c\";\n  }\n  .bx-log-in:before {\n    content: \"\\eb4d\";\n  }\n  .bx-log-in-circle:before {\n    content: \"\\eb4e\";\n  }\n  .bx-log-out:before {\n    content: \"\\eb4f\";\n  }\n  .bx-log-out-circle:before {\n    content: \"\\eb50\";\n  }\n  .bx-low-vision:before {\n    content: \"\\eb51\";\n  }\n  .bx-magnet:before {\n    content: \"\\eb52\";\n  }\n  .bx-mail-send:before {\n    content: \"\\eb53\";\n  }\n  .bx-male:before {\n    content: \"\\eb54\";\n  }\n  .bx-male-sign:before {\n    content: \"\\eb55\";\n  }\n  .bx-map:before {\n    content: \"\\eb56\";\n  }\n  .bx-map-alt:before {\n    content: \"\\eb57\";\n  }\n  .bx-map-pin:before {\n    content: \"\\eb58\";\n  }\n  .bx-mask:before {\n    content: \"\\eb59\";\n  }\n  .bx-medal:before {\n    content: \"\\eb5a\";\n  }\n  .bx-meh:before {\n    content: \"\\eb5b\";\n  }\n  .bx-meh-alt:before {\n    content: \"\\eb5c\";\n  }\n  .bx-meh-blank:before {\n    content: \"\\eb5d\";\n  }\n  .bx-memory-card:before {\n    content: \"\\eb5e\";\n  }\n  .bx-menu:before {\n    content: \"\\eb5f\";\n  }\n  .bx-menu-alt-left:before {\n    content: \"\\ef5b\";\n  }\n  .bx-menu-alt-right:before {\n    content: \"\\eb61\";\n  }\n  .bx-merge:before {\n    content: \"\\eb62\";\n  }\n  .bx-message:before {\n    content: \"\\eb63\";\n  }\n  .bx-message-add:before {\n    content: \"\\eb64\";\n  }\n  .bx-message-alt:before {\n    content: \"\\eb65\";\n  }\n  .bx-message-alt-add:before {\n    content: \"\\eb66\";\n  }\n  .bx-message-alt-check:before {\n    content: \"\\eb67\";\n  }\n  .bx-message-alt-detail:before {\n    content: \"\\eb68\";\n  }\n  .bx-message-alt-dots:before {\n    content: \"\\eb69\";\n  }\n  .bx-message-alt-edit:before {\n    content: \"\\eb6a\";\n  }\n  .bx-message-alt-error:before {\n    content: \"\\eb6b\";\n  }\n  .bx-message-alt-minus:before {\n    content: \"\\eb6c\";\n  }\n  .bx-message-alt-x:before {\n    content: \"\\eb6d\";\n  }\n  .bx-message-check:before {\n    content: \"\\eb6e\";\n  }\n  .bx-message-detail:before {\n    content: \"\\eb6f\";\n  }\n  .bx-message-dots:before {\n    content: \"\\eb70\";\n  }\n  .bx-message-edit:before {\n    content: \"\\eb71\";\n  }\n  .bx-message-error:before {\n    content: \"\\eb72\";\n  }\n  .bx-message-minus:before {\n    content: \"\\eb73\";\n  }\n  .bx-message-rounded:before {\n    content: \"\\eb74\";\n  }\n  .bx-message-rounded-add:before {\n    content: \"\\eb75\";\n  }\n  .bx-message-rounded-check:before {\n    content: \"\\eb76\";\n  }\n  .bx-message-rounded-detail:before {\n    content: \"\\eb77\";\n  }\n  .bx-message-rounded-dots:before {\n    content: \"\\eb78\";\n  }\n  .bx-message-rounded-edit:before {\n    content: \"\\ef5c\";\n  }\n  .bx-message-rounded-error:before {\n    content: \"\\eb7a\";\n  }\n  .bx-message-rounded-minus:before {\n    content: \"\\eb7b\";\n  }\n  .bx-message-rounded-x:before {\n    content: \"\\eb7c\";\n  }\n  .bx-message-square:before {\n    content: \"\\eb7d\";\n  }\n  .bx-message-square-add:before {\n    content: \"\\eb7e\";\n  }\n  .bx-message-square-check:before {\n    content: \"\\eb7f\";\n  }\n  .bx-message-square-detail:before {\n    content: \"\\eb80\";\n  }\n  .bx-message-square-dots:before {\n    content: \"\\eb81\";\n  }\n  .bx-message-square-edit:before {\n    content: \"\\eb82\";\n  }\n  .bx-message-square-error:before {\n    content: \"\\eb83\";\n  }\n  .bx-message-square-minus:before {\n    content: \"\\eb84\";\n  }\n  .bx-message-square-x:before {\n    content: \"\\eb85\";\n  }\n  .bx-message-x:before {\n    content: \"\\eb86\";\n  }\n  .bx-meteor:before {\n    content: \"\\eb87\";\n  }\n  .bx-microchip:before {\n    content: \"\\eb88\";\n  }\n  .bx-microphone:before {\n    content: \"\\eb89\";\n  }\n  .bx-microphone-off:before {\n    content: \"\\eb8a\";\n  }\n  .bx-minus:before {\n    content: \"\\eb8b\";\n  }\n  .bx-minus-back:before {\n    content: \"\\eb8c\";\n  }\n  .bx-minus-circle:before {\n    content: \"\\eb8d\";\n  }\n  .bx-minus-front:before {\n    content: \"\\eb8e\";\n  }\n  .bx-mobile:before {\n    content: \"\\eb8f\";\n  }\n  .bx-mobile-alt:before {\n    content: \"\\eb90\";\n  }\n  .bx-mobile-landscape:before {\n    content: \"\\eb91\";\n  }\n  .bx-mobile-vibration:before {\n    content: \"\\ef5d\";\n  }\n  .bx-money:before {\n    content: \"\\eb93\";\n  }\n  .bx-moon:before {\n    content: \"\\eb94\";\n  }\n  .bx-mouse:before {\n    content: \"\\eb95\";\n  }\n  .bx-mouse-alt:before {\n    content: \"\\eb96\";\n  }\n  .bx-move:before {\n    content: \"\\eb97\";\n  }\n  .bx-move-horizontal:before {\n    content: \"\\eb98\";\n  }\n  .bx-move-vertical:before {\n    content: \"\\eb99\";\n  }\n  .bx-movie:before {\n    content: \"\\eb9a\";\n  }\n  .bx-movie-play:before {\n    content: \"\\eb9b\";\n  }\n  .bx-music:before {\n    content: \"\\eb9c\";\n  }\n  .bx-navigation:before {\n    content: \"\\eb9d\";\n  }\n  .bx-network-chart:before {\n    content: \"\\eb9e\";\n  }\n  .bx-news:before {\n    content: \"\\eb9f\";\n  }\n  .bx-no-entry:before {\n    content: \"\\eba0\";\n  }\n  .bx-note:before {\n    content: \"\\eba1\";\n  }\n  .bx-notepad:before {\n    content: \"\\eba2\";\n  }\n  .bx-notification:before {\n    content: \"\\eba3\";\n  }\n  .bx-notification-off:before {\n    content: \"\\eba4\";\n  }\n  .bx-outline:before {\n    content: \"\\eba5\";\n  }\n  .bx-package:before {\n    content: \"\\eba6\";\n  }\n  .bx-paint:before {\n    content: \"\\eba7\";\n  }\n  .bx-paint-roll:before {\n    content: \"\\eba8\";\n  }\n  .bx-palette:before {\n    content: \"\\eba9\";\n  }\n  .bx-paperclip:before {\n    content: \"\\ebaa\";\n  }\n  .bx-paper-plane:before {\n    content: \"\\ef61\";\n  }\n  .bx-paragraph:before {\n    content: \"\\ebac\";\n  }\n  .bx-paste:before {\n    content: \"\\ebad\";\n  }\n  .bx-pause:before {\n    content: \"\\ebae\";\n  }\n  .bx-pause-circle:before {\n    content: \"\\ebaf\";\n  }\n  .bx-pen:before {\n    content: \"\\ebb0\";\n  }\n  .bx-pencil:before {\n    content: \"\\ebb1\";\n  }\n  .bx-phone:before {\n    content: \"\\ebb2\";\n  }\n  .bx-phone-call:before {\n    content: \"\\ebb3\";\n  }\n  .bx-phone-incoming:before {\n    content: \"\\ebb4\";\n  }\n  .bx-phone-off:before {\n    content: \"\\ebb5\";\n  }\n  .bx-phone-outgoing:before {\n    content: \"\\ebb6\";\n  }\n  .bx-photo-album:before {\n    content: \"\\ebb7\";\n  }\n  .bx-pie-chart:before {\n    content: \"\\ebb8\";\n  }\n  .bx-pie-chart-alt:before {\n    content: \"\\ebb9\";\n  }\n  .bx-pie-chart-alt-2:before {\n    content: \"\\ebba\";\n  }\n  .bx-pin:before {\n    content: \"\\ebbb\";\n  }\n  .bx-planet:before {\n    content: \"\\ebbc\";\n  }\n  .bx-play:before {\n    content: \"\\ebbd\";\n  }\n  .bx-play-circle:before {\n    content: \"\\ebbe\";\n  }\n  .bx-plug:before {\n    content: \"\\ebbf\";\n  }\n  .bx-plus:before {\n    content: \"\\ebc0\";\n  }\n  .bx-plus-circle:before {\n    content: \"\\ebc1\";\n  }\n  .bx-plus-medical:before {\n    content: \"\\ebc2\";\n  }\n  .bx-podcast:before {\n    content: \"\\ebc3\";\n  }\n  .bx-pointer:before {\n    content: \"\\ef5e\";\n  }\n  .bx-poll:before {\n    content: \"\\ebc5\";\n  }\n  .bx-polygon:before {\n    content: \"\\ebc6\";\n  }\n  .bx-pound:before {\n    content: \"\\ebc7\";\n  }\n  .bx-power-off:before {\n    content: \"\\ebc8\";\n  }\n  .bx-printer:before {\n    content: \"\\ebc9\";\n  }\n  .bx-pulse:before {\n    content: \"\\ebca\";\n  }\n  .bx-purchase-tag:before {\n    content: \"\\ebcb\";\n  }\n  .bx-purchase-tag-alt:before {\n    content: \"\\ebcc\";\n  }\n  .bx-pyramid:before {\n    content: \"\\ebcd\";\n  }\n  .bx-qr:before {\n    content: \"\\ebce\";\n  }\n  .bx-qr-scan:before {\n    content: \"\\ebcf\";\n  }\n  .bx-question-mark:before {\n    content: \"\\ebd0\";\n  }\n  .bx-radar:before {\n    content: \"\\ebd1\";\n  }\n  .bx-radio:before {\n    content: \"\\ebd2\";\n  }\n  .bx-radio-circle:before {\n    content: \"\\ebd3\";\n  }\n  .bx-radio-circle-marked:before {\n    content: \"\\ebd4\";\n  }\n  .bx-receipt:before {\n    content: \"\\ebd5\";\n  }\n  .bx-rectangle:before {\n    content: \"\\ebd6\";\n  }\n  .bx-recycle:before {\n    content: \"\\ebd7\";\n  }\n  .bx-redo:before {\n    content: \"\\ebd8\";\n  }\n  .bx-refresh:before {\n    content: \"\\ebd9\";\n  }\n  .bx-registered:before {\n    content: \"\\ebda\";\n  }\n  .bx-rename:before {\n    content: \"\\ebdb\";\n  }\n  .bx-repeat:before {\n    content: \"\\ebdc\";\n  }\n  .bx-reply:before {\n    content: \"\\ef5f\";\n  }\n  .bx-reply-all:before {\n    content: \"\\ebde\";\n  }\n  .bx-repost:before {\n    content: \"\\ebdf\";\n  }\n  .bx-reset:before {\n    content: \"\\ebe0\";\n  }\n  .bx-restaurant:before {\n    content: \"\\ebe1\";\n  }\n  .bx-revision:before {\n    content: \"\\ebe2\";\n  }\n  .bx-rewind:before {\n    content: \"\\ebe3\";\n  }\n  .bx-rewind-circle:before {\n    content: \"\\ebe4\";\n  }\n  .bx-right-arrow:before {\n    content: \"\\ebe5\";\n  }\n  .bx-right-arrow-alt:before {\n    content: \"\\ebe6\";\n  }\n  .bx-right-arrow-circle:before {\n    content: \"\\ebe7\";\n  }\n  .bx-right-down-arrow-circle:before {\n    content: \"\\ebe8\";\n  }\n  .bx-right-indent:before {\n    content: \"\\ebe9\";\n  }\n  .bx-right-top-arrow-circle:before {\n    content: \"\\ebea\";\n  }\n  .bx-rocket:before {\n    content: \"\\ebeb\";\n  }\n  .bx-rotate-left:before {\n    content: \"\\ebec\";\n  }\n  .bx-rotate-right:before {\n    content: \"\\ebed\";\n  }\n  .bx-rss:before {\n    content: \"\\ebee\";\n  }\n  .bx-ruble:before {\n    content: \"\\ebef\";\n  }\n  .bx-ruler:before {\n    content: \"\\ebf0\";\n  }\n  .bx-run:before {\n    content: \"\\ebf1\";\n  }\n  .bx-rupee:before {\n    content: \"\\ebf2\";\n  }\n  .bx-sad:before {\n    content: \"\\ebf3\";\n  }\n  .bx-save:before {\n    content: \"\\ebf4\";\n  }\n  .bx-scan:before {\n    content: \"\\ebf5\";\n  }\n  .bx-screenshot:before {\n    content: \"\\ef60\";\n  }\n  .bx-search:before {\n    content: \"\\ebf7\";\n  }\n  .bx-search-alt:before {\n    content: \"\\ebf8\";\n  }\n  .bx-search-alt-2:before {\n    content: \"\\ebf9\";\n  }\n  .bx-selection:before {\n    content: \"\\ebfa\";\n  }\n  .bx-select-multiple:before {\n    content: \"\\ebfb\";\n  }\n  .bx-send:before {\n    content: \"\\ebfc\";\n  }\n  .bx-server:before {\n    content: \"\\ebfd\";\n  }\n  .bx-shape-circle:before {\n    content: \"\\ebfe\";\n  }\n  .bx-shape-polygon:before {\n    content: \"\\ebff\";\n  }\n  .bx-shape-square:before {\n    content: \"\\ec00\";\n  }\n  .bx-shape-triangle:before {\n    content: \"\\ec01\";\n  }\n  .bx-share:before {\n    content: \"\\ec02\";\n  }\n  .bx-share-alt:before {\n    content: \"\\ec03\";\n  }\n  .bx-shekel:before {\n    content: \"\\ec04\";\n  }\n  .bx-shield:before {\n    content: \"\\ec05\";\n  }\n  .bx-shield-alt:before {\n    content: \"\\ec06\";\n  }\n  .bx-shield-alt-2:before {\n    content: \"\\ec07\";\n  }\n  .bx-shield-quarter:before {\n    content: \"\\ec08\";\n  }\n  .bx-shield-x:before {\n    content: \"\\ec09\";\n  }\n  .bx-shocked:before {\n    content: \"\\ec0a\";\n  }\n  .bx-shopping-bag:before {\n    content: \"\\ec0b\";\n  }\n  .bx-show:before {\n    content: \"\\ec0c\";\n  }\n  .bx-show-alt:before {\n    content: \"\\ec0d\";\n  }\n  .bx-shuffle:before {\n    content: \"\\ec0e\";\n  }\n  .bx-sidebar:before {\n    content: \"\\ec0f\";\n  }\n  .bx-sitemap:before {\n    content: \"\\ec10\";\n  }\n  .bx-skip-next:before {\n    content: \"\\ec11\";\n  }\n  .bx-skip-next-circle:before {\n    content: \"\\ec12\";\n  }\n  .bx-skip-previous:before {\n    content: \"\\ec13\";\n  }\n  .bx-skip-previous-circle:before {\n    content: \"\\ec14\";\n  }\n  .bx-sleepy:before {\n    content: \"\\ec15\";\n  }\n  .bx-slider:before {\n    content: \"\\ec16\";\n  }\n  .bx-slider-alt:before {\n    content: \"\\ec17\";\n  }\n  .bx-slideshow:before {\n    content: \"\\ec18\";\n  }\n  .bx-smile:before {\n    content: \"\\ec19\";\n  }\n  .bx-sort:before {\n    content: \"\\ec1a\";\n  }\n  .bx-sort-alt-2:before {\n    content: \"\\ec1b\";\n  }\n  .bx-sort-a-z:before {\n    content: \"\\ec1c\";\n  }\n  .bx-sort-down:before {\n    content: \"\\ec1d\";\n  }\n  .bx-sort-up:before {\n    content: \"\\ec1e\";\n  }\n  .bx-sort-z-a:before {\n    content: \"\\ec1f\";\n  }\n  .bx-spa:before {\n    content: \"\\ec20\";\n  }\n  .bx-space-bar:before {\n    content: \"\\ec21\";\n  }\n  .bx-speaker:before {\n    content: \"\\ec22\";\n  }\n  .bx-spray-can:before {\n    content: \"\\ec23\";\n  }\n  .bx-spreadsheet:before {\n    content: \"\\ec24\";\n  }\n  .bx-square:before {\n    content: \"\\ec25\";\n  }\n  .bx-square-rounded:before {\n    content: \"\\ec26\";\n  }\n  .bx-star:before {\n    content: \"\\ec27\";\n  }\n  .bx-station:before {\n    content: \"\\ec28\";\n  }\n  .bx-stats:before {\n    content: \"\\ec29\";\n  }\n  .bx-sticker:before {\n    content: \"\\ec2a\";\n  }\n  .bx-stop:before {\n    content: \"\\ec2b\";\n  }\n  .bx-stop-circle:before {\n    content: \"\\ec2c\";\n  }\n  .bx-stopwatch:before {\n    content: \"\\ec2d\";\n  }\n  .bx-store:before {\n    content: \"\\ec2e\";\n  }\n  .bx-store-alt:before {\n    content: \"\\ec2f\";\n  }\n  .bx-street-view:before {\n    content: \"\\ec30\";\n  }\n  .bx-strikethrough:before {\n    content: \"\\ec31\";\n  }\n  .bx-subdirectory-left:before {\n    content: \"\\ec32\";\n  }\n  .bx-subdirectory-right:before {\n    content: \"\\ec33\";\n  }\n  .bx-sun:before {\n    content: \"\\ec34\";\n  }\n  .bx-support:before {\n    content: \"\\ec35\";\n  }\n  .bx-swim:before {\n    content: \"\\ec36\";\n  }\n  .bx-sync:before {\n    content: \"\\ec37\";\n  }\n  .bx-tab:before {\n    content: \"\\ec38\";\n  }\n  .bx-table:before {\n    content: \"\\ec39\";\n  }\n  .bx-tachometer:before {\n    content: \"\\ec3a\";\n  }\n  .bx-tag:before {\n    content: \"\\ec3b\";\n  }\n  .bx-tag-alt:before {\n    content: \"\\ec3c\";\n  }\n  .bx-target-lock:before {\n    content: \"\\ec3d\";\n  }\n  .bx-task:before {\n    content: \"\\ec3e\";\n  }\n  .bx-task-x:before {\n    content: \"\\ec3f\";\n  }\n  .bx-taxi:before {\n    content: \"\\ec40\";\n  }\n  .bx-tennis-ball:before {\n    content: \"\\ec41\";\n  }\n  .bx-terminal:before {\n    content: \"\\ec42\";\n  }\n  .bx-test-tube:before {\n    content: \"\\ec43\";\n  }\n  .bx-text:before {\n    content: \"\\ec44\";\n  }\n  .bx-time:before {\n    content: \"\\ec45\";\n  }\n  .bx-time-five:before {\n    content: \"\\ec46\";\n  }\n  .bx-timer:before {\n    content: \"\\ec47\";\n  }\n  .bx-tired:before {\n    content: \"\\ec48\";\n  }\n  .bx-toggle-left:before {\n    content: \"\\ec49\";\n  }\n  .bx-toggle-right:before {\n    content: \"\\ec4a\";\n  }\n  .bx-tone:before {\n    content: \"\\ec4b\";\n  }\n  .bx-traffic-cone:before {\n    content: \"\\ec4c\";\n  }\n  .bx-train:before {\n    content: \"\\ec4d\";\n  }\n  .bx-transfer:before {\n    content: \"\\ec4e\";\n  }\n  .bx-transfer-alt:before {\n    content: \"\\ec4f\";\n  }\n  .bx-trash:before {\n    content: \"\\ec50\";\n  }\n  .bx-trash-alt:before {\n    content: \"\\ec51\";\n  }\n  .bx-trending-down:before {\n    content: \"\\ec52\";\n  }\n  .bx-trending-up:before {\n    content: \"\\ec53\";\n  }\n  .bx-trim:before {\n    content: \"\\ec54\";\n  }\n  .bx-trip:before {\n    content: \"\\ec55\";\n  }\n  .bx-trophy:before {\n    content: \"\\ec56\";\n  }\n  .bx-tv:before {\n    content: \"\\ec57\";\n  }\n  .bx-underline:before {\n    content: \"\\ec58\";\n  }\n  .bx-undo:before {\n    content: \"\\ec59\";\n  }\n  .bx-unite:before {\n    content: \"\\ec5a\";\n  }\n  .bx-unlink:before {\n    content: \"\\ec5b\";\n  }\n  .bx-up-arrow:before {\n    content: \"\\ec5c\";\n  }\n  .bx-up-arrow-alt:before {\n    content: \"\\ec5d\";\n  }\n  .bx-up-arrow-circle:before {\n    content: \"\\ec5e\";\n  }\n  .bx-upload:before {\n    content: \"\\ec5f\";\n  }\n  .bx-upside-down:before {\n    content: \"\\ec60\";\n  }\n  .bx-upvote:before {\n    content: \"\\ec61\";\n  }\n  .bx-usb:before {\n    content: \"\\ec62\";\n  }\n  .bx-user:before {\n    content: \"\\ec63\";\n  }\n  .bx-user-check:before {\n    content: \"\\ec64\";\n  }\n  .bx-user-circle:before {\n    content: \"\\ec65\";\n  }\n  .bx-user-minus:before {\n    content: \"\\ec66\";\n  }\n  .bx-user-pin:before {\n    content: \"\\ec67\";\n  }\n  .bx-user-plus:before {\n    content: \"\\ec68\";\n  }\n  .bx-user-voice:before {\n    content: \"\\ec69\";\n  }\n  .bx-user-x:before {\n    content: \"\\ec6a\";\n  }\n  .bx-vector:before {\n    content: \"\\ec6b\";\n  }\n  .bx-vertical-center:before {\n    content: \"\\ec6c\";\n  }\n  .bx-vial:before {\n    content: \"\\ec6d\";\n  }\n  .bx-video:before {\n    content: \"\\ec6e\";\n  }\n  .bx-video-off:before {\n    content: \"\\ec6f\";\n  }\n  .bx-video-plus:before {\n    content: \"\\ec70\";\n  }\n  .bx-video-recording:before {\n    content: \"\\ec71\";\n  }\n  .bx-voicemail:before {\n    content: \"\\ec72\";\n  }\n  .bx-volume:before {\n    content: \"\\ec73\";\n  }\n  .bx-volume-full:before {\n    content: \"\\ec74\";\n  }\n  .bx-volume-low:before {\n    content: \"\\ec75\";\n  }\n  .bx-volume-mute:before {\n    content: \"\\ec76\";\n  }\n  .bx-walk:before {\n    content: \"\\ec77\";\n  }\n  .bx-wallet:before {\n    content: \"\\ec78\";\n  }\n  .bx-wallet-alt:before {\n    content: \"\\ec79\";\n  }\n  .bx-water:before {\n    content: \"\\ec7a\";\n  }\n  .bx-webcam:before {\n    content: \"\\ec7b\";\n  }\n  .bx-wifi:before {\n    content: \"\\ec7c\";\n  }\n  .bx-wifi-0:before {\n    content: \"\\ec7d\";\n  }\n  .bx-wifi-1:before {\n    content: \"\\ec7e\";\n  }\n  .bx-wifi-2:before {\n    content: \"\\ec7f\";\n  }\n  .bx-wifi-off:before {\n    content: \"\\ec80\";\n  }\n  .bx-wind:before {\n    content: \"\\ec81\";\n  }\n  .bx-window:before {\n    content: \"\\ec82\";\n  }\n  .bx-window-alt:before {\n    content: \"\\ec83\";\n  }\n  .bx-window-close:before {\n    content: \"\\ec84\";\n  }\n  .bx-window-open:before {\n    content: \"\\ec85\";\n  }\n  .bx-windows:before {\n    content: \"\\ec86\";\n  }\n  .bx-wine:before {\n    content: \"\\ec87\";\n  }\n  .bx-wink-smile:before {\n    content: \"\\ec88\";\n  }\n  .bx-wink-tongue:before {\n    content: \"\\ec89\";\n  }\n  .bx-won:before {\n    content: \"\\ec8a\";\n  }\n  .bx-world:before {\n    content: \"\\ec8b\";\n  }\n  .bx-wrench:before {\n    content: \"\\ec8c\";\n  }\n  .bx-x:before {\n    content: \"\\ec8d\";\n  }\n  .bx-x-circle:before {\n    content: \"\\ec8e\";\n  }\n  .bx-yen:before {\n    content: \"\\ec8f\";\n  }\n  .bx-zoom-in:before {\n    content: \"\\ec90\";\n  }\n  .bx-zoom-out:before {\n    content: \"\\ec91\";\n  }\n  .bxs-party:before {\n    content: \"\\ec92\";\n  }\n  .bxs-hot:before {\n    content: \"\\ec93\";\n  }\n  .bxs-droplet:before {\n    content: \"\\ec94\";\n  }\n  .bxs-cat:before {\n    content: \"\\ec95\";\n  }\n  .bxs-dog:before {\n    content: \"\\ec96\";\n  }\n  .bxs-injection:before {\n    content: \"\\ec97\";\n  }\n  .bxs-leaf:before {\n    content: \"\\ec98\";\n  }\n  .bxs-add-to-queue:before {\n    content: \"\\ec99\";\n  }\n  .bxs-adjust:before {\n    content: \"\\ec9a\";\n  }\n  .bxs-adjust-alt:before {\n    content: \"\\ec9b\";\n  }\n  .bxs-alarm:before {\n    content: \"\\ec9c\";\n  }\n  .bxs-alarm-add:before {\n    content: \"\\ec9d\";\n  }\n  .bxs-alarm-exclamation:before {\n    content: \"\\ec9e\";\n  }\n  .bxs-alarm-off:before {\n    content: \"\\ec9f\";\n  }\n  .bxs-alarm-snooze:before {\n    content: \"\\eca0\";\n  }\n  .bxs-album:before {\n    content: \"\\eca1\";\n  }\n  .bxs-ambulance:before {\n    content: \"\\eca2\";\n  }\n  .bxs-analyse:before {\n    content: \"\\eca3\";\n  }\n  .bxs-angry:before {\n    content: \"\\eca4\";\n  }\n  .bxs-arch:before {\n    content: \"\\eca5\";\n  }\n  .bxs-archive:before {\n    content: \"\\eca6\";\n  }\n  .bxs-archive-in:before {\n    content: \"\\eca7\";\n  }\n  .bxs-archive-out:before {\n    content: \"\\eca8\";\n  }\n  .bxs-area:before {\n    content: \"\\eca9\";\n  }\n  .bxs-arrow-from-bottom:before {\n    content: \"\\ecaa\";\n  }\n  .bxs-arrow-from-left:before {\n    content: \"\\ecab\";\n  }\n  .bxs-arrow-from-right:before {\n    content: \"\\ecac\";\n  }\n  .bxs-arrow-from-top:before {\n    content: \"\\ecad\";\n  }\n  .bxs-arrow-to-bottom:before {\n    content: \"\\ecae\";\n  }\n  .bxs-arrow-to-left:before {\n    content: \"\\ecaf\";\n  }\n  .bxs-arrow-to-right:before {\n    content: \"\\ecb0\";\n  }\n  .bxs-arrow-to-top:before {\n    content: \"\\ecb1\";\n  }\n  .bxs-award:before {\n    content: \"\\ecb2\";\n  }\n  .bxs-baby-carriage:before {\n    content: \"\\ecb3\";\n  }\n  .bxs-backpack:before {\n    content: \"\\ecb4\";\n  }\n  .bxs-badge:before {\n    content: \"\\ecb5\";\n  }\n  .bxs-badge-check:before {\n    content: \"\\ecb6\";\n  }\n  .bxs-badge-dollar:before {\n    content: \"\\ecb7\";\n  }\n  .bxs-ball:before {\n    content: \"\\ecb8\";\n  }\n  .bxs-band-aid:before {\n    content: \"\\ecb9\";\n  }\n  .bxs-bank:before {\n    content: \"\\ecba\";\n  }\n  .bxs-bar-chart-alt-2:before {\n    content: \"\\ecbb\";\n  }\n  .bxs-bar-chart-square:before {\n    content: \"\\ecbc\";\n  }\n  .bxs-barcode:before {\n    content: \"\\ecbd\";\n  }\n  .bxs-baseball:before {\n    content: \"\\ecbe\";\n  }\n  .bxs-basket:before {\n    content: \"\\ecbf\";\n  }\n  .bxs-basketball:before {\n    content: \"\\ecc0\";\n  }\n  .bxs-bath:before {\n    content: \"\\ecc1\";\n  }\n  .bxs-battery:before {\n    content: \"\\ecc2\";\n  }\n  .bxs-battery-charging:before {\n    content: \"\\ecc3\";\n  }\n  .bxs-battery-full:before {\n    content: \"\\ecc4\";\n  }\n  .bxs-battery-low:before {\n    content: \"\\ecc5\";\n  }\n  .bxs-bed:before {\n    content: \"\\ecc6\";\n  }\n  .bxs-been-here:before {\n    content: \"\\ecc7\";\n  }\n  .bxs-beer:before {\n    content: \"\\ecc8\";\n  }\n  .bxs-bell:before {\n    content: \"\\ecc9\";\n  }\n  .bxs-bell-minus:before {\n    content: \"\\ecca\";\n  }\n  .bxs-bell-off:before {\n    content: \"\\eccb\";\n  }\n  .bxs-bell-plus:before {\n    content: \"\\eccc\";\n  }\n  .bxs-bell-ring:before {\n    content: \"\\eccd\";\n  }\n  .bxs-bible:before {\n    content: \"\\ecce\";\n  }\n  .bxs-binoculars:before {\n    content: \"\\eccf\";\n  }\n  .bxs-blanket:before {\n    content: \"\\ecd0\";\n  }\n  .bxs-bolt:before {\n    content: \"\\ecd1\";\n  }\n  .bxs-bolt-circle:before {\n    content: \"\\ecd2\";\n  }\n  .bxs-bomb:before {\n    content: \"\\ecd3\";\n  }\n  .bxs-bone:before {\n    content: \"\\ecd4\";\n  }\n  .bxs-bong:before {\n    content: \"\\ecd5\";\n  }\n  .bxs-book:before {\n    content: \"\\ecd6\";\n  }\n  .bxs-book-add:before {\n    content: \"\\ecd7\";\n  }\n  .bxs-book-alt:before {\n    content: \"\\ecd8\";\n  }\n  .bxs-book-bookmark:before {\n    content: \"\\ecd9\";\n  }\n  .bxs-book-content:before {\n    content: \"\\ecda\";\n  }\n  .bxs-book-heart:before {\n    content: \"\\ecdb\";\n  }\n  .bxs-bookmark:before {\n    content: \"\\ecdc\";\n  }\n  .bxs-bookmark-alt:before {\n    content: \"\\ecdd\";\n  }\n  .bxs-bookmark-alt-minus:before {\n    content: \"\\ecde\";\n  }\n  .bxs-bookmark-alt-plus:before {\n    content: \"\\ecdf\";\n  }\n  .bxs-bookmark-heart:before {\n    content: \"\\ece0\";\n  }\n  .bxs-bookmark-minus:before {\n    content: \"\\ece1\";\n  }\n  .bxs-bookmark-plus:before {\n    content: \"\\ece2\";\n  }\n  .bxs-bookmarks:before {\n    content: \"\\ece3\";\n  }\n  .bxs-bookmark-star:before {\n    content: \"\\ece4\";\n  }\n  .bxs-book-open:before {\n    content: \"\\ece5\";\n  }\n  .bxs-book-reader:before {\n    content: \"\\ece6\";\n  }\n  .bxs-bot:before {\n    content: \"\\ece7\";\n  }\n  .bxs-bowling-ball:before {\n    content: \"\\ece8\";\n  }\n  .bxs-box:before {\n    content: \"\\ece9\";\n  }\n  .bxs-brain:before {\n    content: \"\\ecea\";\n  }\n  .bxs-briefcase:before {\n    content: \"\\eceb\";\n  }\n  .bxs-briefcase-alt:before {\n    content: \"\\ecec\";\n  }\n  .bxs-briefcase-alt-2:before {\n    content: \"\\eced\";\n  }\n  .bxs-brightness:before {\n    content: \"\\ecee\";\n  }\n  .bxs-brightness-half:before {\n    content: \"\\ecef\";\n  }\n  .bxs-brush:before {\n    content: \"\\ecf0\";\n  }\n  .bxs-brush-alt:before {\n    content: \"\\ecf1\";\n  }\n  .bxs-bug:before {\n    content: \"\\ecf2\";\n  }\n  .bxs-bug-alt:before {\n    content: \"\\ecf3\";\n  }\n  .bxs-building:before {\n    content: \"\\ecf4\";\n  }\n  .bxs-building-house:before {\n    content: \"\\ecf5\";\n  }\n  .bxs-buildings:before {\n    content: \"\\ecf6\";\n  }\n  .bxs-bulb:before {\n    content: \"\\ecf7\";\n  }\n  .bxs-bullseye:before {\n    content: \"\\ecf8\";\n  }\n  .bxs-buoy:before {\n    content: \"\\ecf9\";\n  }\n  .bxs-bus:before {\n    content: \"\\ecfa\";\n  }\n  .bxs-business:before {\n    content: \"\\ecfb\";\n  }\n  .bxs-bus-school:before {\n    content: \"\\ecfc\";\n  }\n  .bxs-cabinet:before {\n    content: \"\\ecfd\";\n  }\n  .bxs-cake:before {\n    content: \"\\ecfe\";\n  }\n  .bxs-calculator:before {\n    content: \"\\ecff\";\n  }\n  .bxs-calendar:before {\n    content: \"\\ed00\";\n  }\n  .bxs-calendar-alt:before {\n    content: \"\\ed01\";\n  }\n  .bxs-calendar-check:before {\n    content: \"\\ed02\";\n  }\n  .bxs-calendar-edit:before {\n    content: \"\\ed03\";\n  }\n  .bxs-calendar-event:before {\n    content: \"\\ed04\";\n  }\n  .bxs-calendar-exclamation:before {\n    content: \"\\ed05\";\n  }\n  .bxs-calendar-heart:before {\n    content: \"\\ed06\";\n  }\n  .bxs-calendar-minus:before {\n    content: \"\\ed07\";\n  }\n  .bxs-calendar-plus:before {\n    content: \"\\ed08\";\n  }\n  .bxs-calendar-star:before {\n    content: \"\\ed09\";\n  }\n  .bxs-calendar-week:before {\n    content: \"\\ed0a\";\n  }\n  .bxs-calendar-x:before {\n    content: \"\\ed0b\";\n  }\n  .bxs-camera:before {\n    content: \"\\ed0c\";\n  }\n  .bxs-camera-home:before {\n    content: \"\\ed0d\";\n  }\n  .bxs-camera-movie:before {\n    content: \"\\ed0e\";\n  }\n  .bxs-camera-off:before {\n    content: \"\\ed0f\";\n  }\n  .bxs-camera-plus:before {\n    content: \"\\ed10\";\n  }\n  .bxs-capsule:before {\n    content: \"\\ed11\";\n  }\n  .bxs-captions:before {\n    content: \"\\ed12\";\n  }\n  .bxs-car:before {\n    content: \"\\ed13\";\n  }\n  .bxs-car-battery:before {\n    content: \"\\ed14\";\n  }\n  .bxs-car-crash:before {\n    content: \"\\ed15\";\n  }\n  .bxs-card:before {\n    content: \"\\ed16\";\n  }\n  .bxs-caret-down-circle:before {\n    content: \"\\ed17\";\n  }\n  .bxs-caret-down-square:before {\n    content: \"\\ed18\";\n  }\n  .bxs-caret-left-circle:before {\n    content: \"\\ed19\";\n  }\n  .bxs-caret-left-square:before {\n    content: \"\\ed1a\";\n  }\n  .bxs-caret-right-circle:before {\n    content: \"\\ed1b\";\n  }\n  .bxs-caret-right-square:before {\n    content: \"\\ed1c\";\n  }\n  .bxs-caret-up-circle:before {\n    content: \"\\ed1d\";\n  }\n  .bxs-caret-up-square:before {\n    content: \"\\ed1e\";\n  }\n  .bxs-car-garage:before {\n    content: \"\\ed1f\";\n  }\n  .bxs-car-mechanic:before {\n    content: \"\\ed20\";\n  }\n  .bxs-carousel:before {\n    content: \"\\ed21\";\n  }\n  .bxs-cart:before {\n    content: \"\\ed22\";\n  }\n  .bxs-cart-add:before {\n    content: \"\\ed23\";\n  }\n  .bxs-cart-alt:before {\n    content: \"\\ed24\";\n  }\n  .bxs-cart-download:before {\n    content: \"\\ed25\";\n  }\n  .bxs-car-wash:before {\n    content: \"\\ed26\";\n  }\n  .bxs-category:before {\n    content: \"\\ed27\";\n  }\n  .bxs-category-alt:before {\n    content: \"\\ed28\";\n  }\n  .bxs-cctv:before {\n    content: \"\\ed29\";\n  }\n  .bxs-certification:before {\n    content: \"\\ed2a\";\n  }\n  .bxs-chalkboard:before {\n    content: \"\\ed2b\";\n  }\n  .bxs-chart:before {\n    content: \"\\ed2c\";\n  }\n  .bxs-chat:before {\n    content: \"\\ed2d\";\n  }\n  .bxs-checkbox:before {\n    content: \"\\ed2e\";\n  }\n  .bxs-checkbox-checked:before {\n    content: \"\\ed2f\";\n  }\n  .bxs-checkbox-minus:before {\n    content: \"\\ed30\";\n  }\n  .bxs-check-circle:before {\n    content: \"\\ed31\";\n  }\n  .bxs-check-shield:before {\n    content: \"\\ed32\";\n  }\n  .bxs-check-square:before {\n    content: \"\\ed33\";\n  }\n  .bxs-chess:before {\n    content: \"\\ed34\";\n  }\n  .bxs-chevron-down:before {\n    content: \"\\ed35\";\n  }\n  .bxs-chevron-down-circle:before {\n    content: \"\\ed36\";\n  }\n  .bxs-chevron-down-square:before {\n    content: \"\\ed37\";\n  }\n  .bxs-chevron-left:before {\n    content: \"\\ed38\";\n  }\n  .bxs-chevron-left-circle:before {\n    content: \"\\ed39\";\n  }\n  .bxs-chevron-left-square:before {\n    content: \"\\ed3a\";\n  }\n  .bxs-chevron-right:before {\n    content: \"\\ed3b\";\n  }\n  .bxs-chevron-right-circle:before {\n    content: \"\\ed3c\";\n  }\n  .bxs-chevron-right-square:before {\n    content: \"\\ed3d\";\n  }\n  .bxs-chevrons-down:before {\n    content: \"\\ed3e\";\n  }\n  .bxs-chevrons-left:before {\n    content: \"\\ed3f\";\n  }\n  .bxs-chevrons-right:before {\n    content: \"\\ed40\";\n  }\n  .bxs-chevrons-up:before {\n    content: \"\\ed41\";\n  }\n  .bxs-chevron-up:before {\n    content: \"\\ed42\";\n  }\n  .bxs-chevron-up-circle:before {\n    content: \"\\ed43\";\n  }\n  .bxs-chevron-up-square:before {\n    content: \"\\ed44\";\n  }\n  .bxs-chip:before {\n    content: \"\\ed45\";\n  }\n  .bxs-church:before {\n    content: \"\\ed46\";\n  }\n  .bxs-circle:before {\n    content: \"\\ed47\";\n  }\n  .bxs-city:before {\n    content: \"\\ed48\";\n  }\n  .bxs-clinic:before {\n    content: \"\\ed49\";\n  }\n  .bxs-cloud:before {\n    content: \"\\ed4a\";\n  }\n  .bxs-cloud-download:before {\n    content: \"\\ed4b\";\n  }\n  .bxs-cloud-lightning:before {\n    content: \"\\ed4c\";\n  }\n  .bxs-cloud-rain:before {\n    content: \"\\ed4d\";\n  }\n  .bxs-cloud-upload:before {\n    content: \"\\ed4e\";\n  }\n  .bxs-coffee:before {\n    content: \"\\ed4f\";\n  }\n  .bxs-coffee-alt:before {\n    content: \"\\ed50\";\n  }\n  .bxs-coffee-togo:before {\n    content: \"\\ed51\";\n  }\n  .bxs-cog:before {\n    content: \"\\ed52\";\n  }\n  .bxs-coin:before {\n    content: \"\\ed53\";\n  }\n  .bxs-coin-stack:before {\n    content: \"\\ed54\";\n  }\n  .bxs-collection:before {\n    content: \"\\ed55\";\n  }\n  .bxs-color-fill:before {\n    content: \"\\ed56\";\n  }\n  .bxs-comment:before {\n    content: \"\\ed57\";\n  }\n  .bxs-comment-add:before {\n    content: \"\\ed58\";\n  }\n  .bxs-comment-check:before {\n    content: \"\\ed59\";\n  }\n  .bxs-comment-detail:before {\n    content: \"\\ed5a\";\n  }\n  .bxs-comment-dots:before {\n    content: \"\\ed5b\";\n  }\n  .bxs-comment-edit:before {\n    content: \"\\ed5c\";\n  }\n  .bxs-comment-error:before {\n    content: \"\\ed5d\";\n  }\n  .bxs-comment-minus:before {\n    content: \"\\ed5e\";\n  }\n  .bxs-comment-x:before {\n    content: \"\\ed5f\";\n  }\n  .bxs-compass:before {\n    content: \"\\ed60\";\n  }\n  .bxs-component:before {\n    content: \"\\ed61\";\n  }\n  .bxs-confused:before {\n    content: \"\\ed62\";\n  }\n  .bxs-contact:before {\n    content: \"\\ed63\";\n  }\n  .bxs-conversation:before {\n    content: \"\\ed64\";\n  }\n  .bxs-cookie:before {\n    content: \"\\ed65\";\n  }\n  .bxs-cool:before {\n    content: \"\\ed66\";\n  }\n  .bxs-copy:before {\n    content: \"\\ed67\";\n  }\n  .bxs-copy-alt:before {\n    content: \"\\ed68\";\n  }\n  .bxs-copyright:before {\n    content: \"\\ed69\";\n  }\n  .bxs-coupon:before {\n    content: \"\\ed6a\";\n  }\n  .bxs-credit-card:before {\n    content: \"\\ed6b\";\n  }\n  .bxs-credit-card-alt:before {\n    content: \"\\ed6c\";\n  }\n  .bxs-credit-card-front:before {\n    content: \"\\ed6d\";\n  }\n  .bxs-crop:before {\n    content: \"\\ed6e\";\n  }\n  .bxs-crown:before {\n    content: \"\\ed6f\";\n  }\n  .bxs-cube:before {\n    content: \"\\ed70\";\n  }\n  .bxs-cube-alt:before {\n    content: \"\\ed71\";\n  }\n  .bxs-cuboid:before {\n    content: \"\\ed72\";\n  }\n  .bxs-customize:before {\n    content: \"\\ed73\";\n  }\n  .bxs-cylinder:before {\n    content: \"\\ed74\";\n  }\n  .bxs-dashboard:before {\n    content: \"\\ed75\";\n  }\n  .bxs-data:before {\n    content: \"\\ed76\";\n  }\n  .bxs-detail:before {\n    content: \"\\ed77\";\n  }\n  .bxs-devices:before {\n    content: \"\\ed78\";\n  }\n  .bxs-diamond:before {\n    content: \"\\ed79\";\n  }\n  .bxs-dice-1:before {\n    content: \"\\ed7a\";\n  }\n  .bxs-dice-2:before {\n    content: \"\\ed7b\";\n  }\n  .bxs-dice-3:before {\n    content: \"\\ed7c\";\n  }\n  .bxs-dice-4:before {\n    content: \"\\ed7d\";\n  }\n  .bxs-dice-5:before {\n    content: \"\\ed7e\";\n  }\n  .bxs-dice-6:before {\n    content: \"\\ed7f\";\n  }\n  .bxs-direction-left:before {\n    content: \"\\ed80\";\n  }\n  .bxs-direction-right:before {\n    content: \"\\ed81\";\n  }\n  .bxs-directions:before {\n    content: \"\\ed82\";\n  }\n  .bxs-disc:before {\n    content: \"\\ed83\";\n  }\n  .bxs-discount:before {\n    content: \"\\ed84\";\n  }\n  .bxs-dish:before {\n    content: \"\\ed85\";\n  }\n  .bxs-dislike:before {\n    content: \"\\ed86\";\n  }\n  .bxs-dizzy:before {\n    content: \"\\ed87\";\n  }\n  .bxs-dock-bottom:before {\n    content: \"\\ed88\";\n  }\n  .bxs-dock-left:before {\n    content: \"\\ed89\";\n  }\n  .bxs-dock-right:before {\n    content: \"\\ed8a\";\n  }\n  .bxs-dock-top:before {\n    content: \"\\ed8b\";\n  }\n  .bxs-dollar-circle:before {\n    content: \"\\ed8c\";\n  }\n  .bxs-donate-blood:before {\n    content: \"\\ed8d\";\n  }\n  .bxs-donate-heart:before {\n    content: \"\\ed8e\";\n  }\n  .bxs-door-open:before {\n    content: \"\\ed8f\";\n  }\n  .bxs-doughnut-chart:before {\n    content: \"\\ed90\";\n  }\n  .bxs-down-arrow:before {\n    content: \"\\ed91\";\n  }\n  .bxs-down-arrow-alt:before {\n    content: \"\\ed92\";\n  }\n  .bxs-down-arrow-circle:before {\n    content: \"\\ed93\";\n  }\n  .bxs-down-arrow-square:before {\n    content: \"\\ed94\";\n  }\n  .bxs-download:before {\n    content: \"\\ed95\";\n  }\n  .bxs-downvote:before {\n    content: \"\\ed96\";\n  }\n  .bxs-drink:before {\n    content: \"\\ed97\";\n  }\n  .bxs-droplet-half:before {\n    content: \"\\ed98\";\n  }\n  .bxs-dryer:before {\n    content: \"\\ed99\";\n  }\n  .bxs-duplicate:before {\n    content: \"\\ed9a\";\n  }\n  .bxs-edit:before {\n    content: \"\\ed9b\";\n  }\n  .bxs-edit-alt:before {\n    content: \"\\ed9c\";\n  }\n  .bxs-edit-location:before {\n    content: \"\\ed9d\";\n  }\n  .bxs-eject:before {\n    content: \"\\ed9e\";\n  }\n  .bxs-envelope:before {\n    content: \"\\ed9f\";\n  }\n  .bxs-envelope-open:before {\n    content: \"\\eda0\";\n  }\n  .bxs-eraser:before {\n    content: \"\\eda1\";\n  }\n  .bxs-error:before {\n    content: \"\\eda2\";\n  }\n  .bxs-error-alt:before {\n    content: \"\\eda3\";\n  }\n  .bxs-error-circle:before {\n    content: \"\\eda4\";\n  }\n  .bxs-ev-station:before {\n    content: \"\\eda5\";\n  }\n  .bxs-exit:before {\n    content: \"\\eda6\";\n  }\n  .bxs-extension:before {\n    content: \"\\eda7\";\n  }\n  .bxs-eyedropper:before {\n    content: \"\\eda8\";\n  }\n  .bxs-face:before {\n    content: \"\\eda9\";\n  }\n  .bxs-face-mask:before {\n    content: \"\\edaa\";\n  }\n  .bxs-factory:before {\n    content: \"\\edab\";\n  }\n  .bxs-fast-forward-circle:before {\n    content: \"\\edac\";\n  }\n  .bxs-file:before {\n    content: \"\\edad\";\n  }\n  .bxs-file-archive:before {\n    content: \"\\edae\";\n  }\n  .bxs-file-blank:before {\n    content: \"\\edaf\";\n  }\n  .bxs-file-css:before {\n    content: \"\\edb0\";\n  }\n  .bxs-file-doc:before {\n    content: \"\\edb1\";\n  }\n  .bxs-file-export:before {\n    content: \"\\edb2\";\n  }\n  .bxs-file-find:before {\n    content: \"\\edb3\";\n  }\n  .bxs-file-gif:before {\n    content: \"\\edb4\";\n  }\n  .bxs-file-html:before {\n    content: \"\\edb5\";\n  }\n  .bxs-file-image:before {\n    content: \"\\edb6\";\n  }\n  .bxs-file-import:before {\n    content: \"\\edb7\";\n  }\n  .bxs-file-jpg:before {\n    content: \"\\edb8\";\n  }\n  .bxs-file-js:before {\n    content: \"\\edb9\";\n  }\n  .bxs-file-json:before {\n    content: \"\\edba\";\n  }\n  .bxs-file-md:before {\n    content: \"\\edbb\";\n  }\n  .bxs-file-pdf:before {\n    content: \"\\edbc\";\n  }\n  .bxs-file-plus:before {\n    content: \"\\edbd\";\n  }\n  .bxs-file-png:before {\n    content: \"\\edbe\";\n  }\n  .bxs-file-txt:before {\n    content: \"\\edbf\";\n  }\n  .bxs-film:before {\n    content: \"\\edc0\";\n  }\n  .bxs-filter-alt:before {\n    content: \"\\edc1\";\n  }\n  .bxs-first-aid:before {\n    content: \"\\edc2\";\n  }\n  .bxs-flag:before {\n    content: \"\\edc3\";\n  }\n  .bxs-flag-alt:before {\n    content: \"\\edc4\";\n  }\n  .bxs-flag-checkered:before {\n    content: \"\\edc5\";\n  }\n  .bxs-flame:before {\n    content: \"\\edc6\";\n  }\n  .bxs-flask:before {\n    content: \"\\edc7\";\n  }\n  .bxs-florist:before {\n    content: \"\\edc8\";\n  }\n  .bxs-folder:before {\n    content: \"\\edc9\";\n  }\n  .bxs-folder-minus:before {\n    content: \"\\edca\";\n  }\n  .bxs-folder-open:before {\n    content: \"\\edcb\";\n  }\n  .bxs-folder-plus:before {\n    content: \"\\edcc\";\n  }\n  .bxs-food-menu:before {\n    content: \"\\edcd\";\n  }\n  .bxs-fridge:before {\n    content: \"\\edce\";\n  }\n  .bxs-game:before {\n    content: \"\\edcf\";\n  }\n  .bxs-gas-pump:before {\n    content: \"\\edd0\";\n  }\n  .bxs-ghost:before {\n    content: \"\\edd1\";\n  }\n  .bxs-gift:before {\n    content: \"\\edd2\";\n  }\n  .bxs-graduation:before {\n    content: \"\\edd3\";\n  }\n  .bxs-grid:before {\n    content: \"\\edd4\";\n  }\n  .bxs-grid-alt:before {\n    content: \"\\edd5\";\n  }\n  .bxs-group:before {\n    content: \"\\edd6\";\n  }\n  .bxs-guitar-amp:before {\n    content: \"\\edd7\";\n  }\n  .bxs-hand:before {\n    content: \"\\edd8\";\n  }\n  .bxs-hand-down:before {\n    content: \"\\edd9\";\n  }\n  .bxs-hand-left:before {\n    content: \"\\edda\";\n  }\n  .bxs-hand-right:before {\n    content: \"\\eddb\";\n  }\n  .bxs-hand-up:before {\n    content: \"\\eddc\";\n  }\n  .bxs-happy:before {\n    content: \"\\eddd\";\n  }\n  .bxs-happy-alt:before {\n    content: \"\\edde\";\n  }\n  .bxs-happy-beaming:before {\n    content: \"\\eddf\";\n  }\n  .bxs-happy-heart-eyes:before {\n    content: \"\\ede0\";\n  }\n  .bxs-hdd:before {\n    content: \"\\ede1\";\n  }\n  .bxs-heart:before {\n    content: \"\\ede2\";\n  }\n  .bxs-heart-circle:before {\n    content: \"\\ede3\";\n  }\n  .bxs-heart-square:before {\n    content: \"\\ede4\";\n  }\n  .bxs-help-circle:before {\n    content: \"\\ede5\";\n  }\n  .bxs-hide:before {\n    content: \"\\ede6\";\n  }\n  .bxs-home:before {\n    content: \"\\ede7\";\n  }\n  .bxs-home-circle:before {\n    content: \"\\ede8\";\n  }\n  .bxs-home-heart:before {\n    content: \"\\ede9\";\n  }\n  .bxs-home-smile:before {\n    content: \"\\edea\";\n  }\n  .bxs-hotel:before {\n    content: \"\\edeb\";\n  }\n  .bxs-hourglass:before {\n    content: \"\\edec\";\n  }\n  .bxs-hourglass-bottom:before {\n    content: \"\\eded\";\n  }\n  .bxs-hourglass-top:before {\n    content: \"\\edee\";\n  }\n  .bxs-id-card:before {\n    content: \"\\edef\";\n  }\n  .bxs-image:before {\n    content: \"\\edf0\";\n  }\n  .bxs-image-add:before {\n    content: \"\\edf1\";\n  }\n  .bxs-image-alt:before {\n    content: \"\\edf2\";\n  }\n  .bxs-inbox:before {\n    content: \"\\edf3\";\n  }\n  .bxs-info-circle:before {\n    content: \"\\edf4\";\n  }\n  .bxs-info-square:before {\n    content: \"\\edf5\";\n  }\n  .bxs-institution:before {\n    content: \"\\edf6\";\n  }\n  .bxs-joystick:before {\n    content: \"\\edf7\";\n  }\n  .bxs-joystick-alt:before {\n    content: \"\\edf8\";\n  }\n  .bxs-joystick-button:before {\n    content: \"\\edf9\";\n  }\n  .bxs-key:before {\n    content: \"\\edfa\";\n  }\n  .bxs-keyboard:before {\n    content: \"\\edfb\";\n  }\n  .bxs-label:before {\n    content: \"\\edfc\";\n  }\n  .bxs-landmark:before {\n    content: \"\\edfd\";\n  }\n  .bxs-landscape:before {\n    content: \"\\edfe\";\n  }\n  .bxs-laugh:before {\n    content: \"\\edff\";\n  }\n  .bxs-layer:before {\n    content: \"\\ee00\";\n  }\n  .bxs-layer-minus:before {\n    content: \"\\ee01\";\n  }\n  .bxs-layer-plus:before {\n    content: \"\\ee02\";\n  }\n  .bxs-layout:before {\n    content: \"\\ee03\";\n  }\n  .bxs-left-arrow:before {\n    content: \"\\ee04\";\n  }\n  .bxs-left-arrow-alt:before {\n    content: \"\\ee05\";\n  }\n  .bxs-left-arrow-circle:before {\n    content: \"\\ee06\";\n  }\n  .bxs-left-arrow-square:before {\n    content: \"\\ee07\";\n  }\n  .bxs-left-down-arrow-circle:before {\n    content: \"\\ee08\";\n  }\n  .bxs-left-top-arrow-circle:before {\n    content: \"\\ee09\";\n  }\n  .bxs-like:before {\n    content: \"\\ee0a\";\n  }\n  .bxs-location-plus:before {\n    content: \"\\ee0b\";\n  }\n  .bxs-lock:before {\n    content: \"\\ee0c\";\n  }\n  .bxs-lock-alt:before {\n    content: \"\\ee0d\";\n  }\n  .bxs-lock-open:before {\n    content: \"\\ee0e\";\n  }\n  .bxs-lock-open-alt:before {\n    content: \"\\ee0f\";\n  }\n  .bxs-log-in:before {\n    content: \"\\ee10\";\n  }\n  .bxs-log-in-circle:before {\n    content: \"\\ee11\";\n  }\n  .bxs-log-out:before {\n    content: \"\\ee12\";\n  }\n  .bxs-log-out-circle:before {\n    content: \"\\ee13\";\n  }\n  .bxs-low-vision:before {\n    content: \"\\ee14\";\n  }\n  .bxs-magic-wand:before {\n    content: \"\\ee15\";\n  }\n  .bxs-magnet:before {\n    content: \"\\ee16\";\n  }\n  .bxs-map:before {\n    content: \"\\ee17\";\n  }\n  .bxs-map-alt:before {\n    content: \"\\ee18\";\n  }\n  .bxs-map-pin:before {\n    content: \"\\ee19\";\n  }\n  .bxs-mask:before {\n    content: \"\\ee1a\";\n  }\n  .bxs-medal:before {\n    content: \"\\ee1b\";\n  }\n  .bxs-megaphone:before {\n    content: \"\\ee1c\";\n  }\n  .bxs-meh:before {\n    content: \"\\ee1d\";\n  }\n  .bxs-meh-alt:before {\n    content: \"\\ee1e\";\n  }\n  .bxs-meh-blank:before {\n    content: \"\\ee1f\";\n  }\n  .bxs-memory-card:before {\n    content: \"\\ee20\";\n  }\n  .bxs-message:before {\n    content: \"\\ee21\";\n  }\n  .bxs-message-add:before {\n    content: \"\\ee22\";\n  }\n  .bxs-message-alt:before {\n    content: \"\\ee23\";\n  }\n  .bxs-message-alt-add:before {\n    content: \"\\ee24\";\n  }\n  .bxs-message-alt-check:before {\n    content: \"\\ee25\";\n  }\n  .bxs-message-alt-detail:before {\n    content: \"\\ee26\";\n  }\n  .bxs-message-alt-dots:before {\n    content: \"\\ee27\";\n  }\n  .bxs-message-alt-edit:before {\n    content: \"\\ee28\";\n  }\n  .bxs-message-alt-error:before {\n    content: \"\\ee29\";\n  }\n  .bxs-message-alt-minus:before {\n    content: \"\\ee2a\";\n  }\n  .bxs-message-alt-x:before {\n    content: \"\\ee2b\";\n  }\n  .bxs-message-check:before {\n    content: \"\\ee2c\";\n  }\n  .bxs-message-detail:before {\n    content: \"\\ee2d\";\n  }\n  .bxs-message-dots:before {\n    content: \"\\ee2e\";\n  }\n  .bxs-message-edit:before {\n    content: \"\\ee2f\";\n  }\n  .bxs-message-error:before {\n    content: \"\\ee30\";\n  }\n  .bxs-message-minus:before {\n    content: \"\\ee31\";\n  }\n  .bxs-message-rounded:before {\n    content: \"\\ee32\";\n  }\n  .bxs-message-rounded-add:before {\n    content: \"\\ee33\";\n  }\n  .bxs-message-rounded-check:before {\n    content: \"\\ee34\";\n  }\n  .bxs-message-rounded-detail:before {\n    content: \"\\ee35\";\n  }\n  .bxs-message-rounded-dots:before {\n    content: \"\\ee36\";\n  }\n  .bxs-message-rounded-edit:before {\n    content: \"\\ee37\";\n  }\n  .bxs-message-rounded-error:before {\n    content: \"\\ee38\";\n  }\n  .bxs-message-rounded-minus:before {\n    content: \"\\ee39\";\n  }\n  .bxs-message-rounded-x:before {\n    content: \"\\ee3a\";\n  }\n  .bxs-message-square:before {\n    content: \"\\ee3b\";\n  }\n  .bxs-message-square-add:before {\n    content: \"\\ee3c\";\n  }\n  .bxs-message-square-check:before {\n    content: \"\\ee3d\";\n  }\n  .bxs-message-square-detail:before {\n    content: \"\\ee3e\";\n  }\n  .bxs-message-square-dots:before {\n    content: \"\\ee3f\";\n  }\n  .bxs-message-square-edit:before {\n    content: \"\\ee40\";\n  }\n  .bxs-message-square-error:before {\n    content: \"\\ee41\";\n  }\n  .bxs-message-square-minus:before {\n    content: \"\\ee42\";\n  }\n  .bxs-message-square-x:before {\n    content: \"\\ee43\";\n  }\n  .bxs-message-x:before {\n    content: \"\\ee44\";\n  }\n  .bxs-meteor:before {\n    content: \"\\ee45\";\n  }\n  .bxs-microchip:before {\n    content: \"\\ee46\";\n  }\n  .bxs-microphone:before {\n    content: \"\\ee47\";\n  }\n  .bxs-microphone-alt:before {\n    content: \"\\ee48\";\n  }\n  .bxs-microphone-off:before {\n    content: \"\\ee49\";\n  }\n  .bxs-minus-circle:before {\n    content: \"\\ee4a\";\n  }\n  .bxs-minus-square:before {\n    content: \"\\ee4b\";\n  }\n  .bxs-mobile:before {\n    content: \"\\ee4c\";\n  }\n  .bxs-mobile-vibration:before {\n    content: \"\\ee4d\";\n  }\n  .bxs-moon:before {\n    content: \"\\ee4e\";\n  }\n  .bxs-mouse:before {\n    content: \"\\ee4f\";\n  }\n  .bxs-mouse-alt:before {\n    content: \"\\ee50\";\n  }\n  .bxs-movie:before {\n    content: \"\\ee51\";\n  }\n  .bxs-movie-play:before {\n    content: \"\\ee52\";\n  }\n  .bxs-music:before {\n    content: \"\\ee53\";\n  }\n  .bxs-navigation:before {\n    content: \"\\ee54\";\n  }\n  .bxs-network-chart:before {\n    content: \"\\ee55\";\n  }\n  .bxs-news:before {\n    content: \"\\ee56\";\n  }\n  .bxs-no-entry:before {\n    content: \"\\ee57\";\n  }\n  .bxs-note:before {\n    content: \"\\ee58\";\n  }\n  .bxs-notepad:before {\n    content: \"\\ee59\";\n  }\n  .bxs-notification:before {\n    content: \"\\ee5a\";\n  }\n  .bxs-notification-off:before {\n    content: \"\\ee5b\";\n  }\n  .bxs-offer:before {\n    content: \"\\ee5c\";\n  }\n  .bxs-package:before {\n    content: \"\\ee5d\";\n  }\n  .bxs-paint:before {\n    content: \"\\ee5e\";\n  }\n  .bxs-paint-roll:before {\n    content: \"\\ee5f\";\n  }\n  .bxs-palette:before {\n    content: \"\\ee60\";\n  }\n  .bxs-paper-plane:before {\n    content: \"\\ee61\";\n  }\n  .bxs-parking:before {\n    content: \"\\ee62\";\n  }\n  .bxs-paste:before {\n    content: \"\\ee63\";\n  }\n  .bxs-pen:before {\n    content: \"\\ee64\";\n  }\n  .bxs-pencil:before {\n    content: \"\\ee65\";\n  }\n  .bxs-phone:before {\n    content: \"\\ee66\";\n  }\n  .bxs-phone-call:before {\n    content: \"\\ee67\";\n  }\n  .bxs-phone-incoming:before {\n    content: \"\\ee68\";\n  }\n  .bxs-phone-off:before {\n    content: \"\\ee69\";\n  }\n  .bxs-phone-outgoing:before {\n    content: \"\\ee6a\";\n  }\n  .bxs-photo-album:before {\n    content: \"\\ee6b\";\n  }\n  .bxs-piano:before {\n    content: \"\\ee6c\";\n  }\n  .bxs-pie-chart:before {\n    content: \"\\ee6d\";\n  }\n  .bxs-pie-chart-alt:before {\n    content: \"\\ee6e\";\n  }\n  .bxs-pie-chart-alt-2:before {\n    content: \"\\ee6f\";\n  }\n  .bxs-pin:before {\n    content: \"\\ee70\";\n  }\n  .bxs-pizza:before {\n    content: \"\\ee71\";\n  }\n  .bxs-plane:before {\n    content: \"\\ee72\";\n  }\n  .bxs-plane-alt:before {\n    content: \"\\ee73\";\n  }\n  .bxs-plane-land:before {\n    content: \"\\ee74\";\n  }\n  .bxs-planet:before {\n    content: \"\\ee75\";\n  }\n  .bxs-plane-take-off:before {\n    content: \"\\ee76\";\n  }\n  .bxs-playlist:before {\n    content: \"\\ee77\";\n  }\n  .bxs-plug:before {\n    content: \"\\ee78\";\n  }\n  .bxs-plus-circle:before {\n    content: \"\\ee79\";\n  }\n  .bxs-plus-square:before {\n    content: \"\\ee7a\";\n  }\n  .bxs-pointer:before {\n    content: \"\\ee7b\";\n  }\n  .bxs-polygon:before {\n    content: \"\\ee7c\";\n  }\n  .bxs-printer:before {\n    content: \"\\ee7d\";\n  }\n  .bxs-purchase-tag:before {\n    content: \"\\ee7e\";\n  }\n  .bxs-purchase-tag-alt:before {\n    content: \"\\ee7f\";\n  }\n  .bxs-pyramid:before {\n    content: \"\\ee80\";\n  }\n  .bxs-quote-alt-left:before {\n    content: \"\\ee81\";\n  }\n  .bxs-quote-alt-right:before {\n    content: \"\\ee82\";\n  }\n  .bxs-quote-left:before {\n    content: \"\\ee83\";\n  }\n  .bxs-quote-right:before {\n    content: \"\\ee84\";\n  }\n  .bxs-quote-single-left:before {\n    content: \"\\ee85\";\n  }\n  .bxs-quote-single-right:before {\n    content: \"\\ee86\";\n  }\n  .bxs-radiation:before {\n    content: \"\\ee87\";\n  }\n  .bxs-radio:before {\n    content: \"\\ee88\";\n  }\n  .bxs-receipt:before {\n    content: \"\\ee89\";\n  }\n  .bxs-rectangle:before {\n    content: \"\\ee8a\";\n  }\n  .bxs-registered:before {\n    content: \"\\ee8b\";\n  }\n  .bxs-rename:before {\n    content: \"\\ee8c\";\n  }\n  .bxs-report:before {\n    content: \"\\ee8d\";\n  }\n  .bxs-rewind-circle:before {\n    content: \"\\ee8e\";\n  }\n  .bxs-right-arrow:before {\n    content: \"\\ee8f\";\n  }\n  .bxs-right-arrow-alt:before {\n    content: \"\\ee90\";\n  }\n  .bxs-right-arrow-circle:before {\n    content: \"\\ee91\";\n  }\n  .bxs-right-arrow-square:before {\n    content: \"\\ee92\";\n  }\n  .bxs-right-down-arrow-circle:before {\n    content: \"\\ee93\";\n  }\n  .bxs-right-top-arrow-circle:before {\n    content: \"\\ee94\";\n  }\n  .bxs-rocket:before {\n    content: \"\\ee95\";\n  }\n  .bxs-ruler:before {\n    content: \"\\ee96\";\n  }\n  .bxs-sad:before {\n    content: \"\\ee97\";\n  }\n  .bxs-save:before {\n    content: \"\\ee98\";\n  }\n  .bxs-school:before {\n    content: \"\\ee99\";\n  }\n  .bxs-search:before {\n    content: \"\\ee9a\";\n  }\n  .bxs-search-alt-2:before {\n    content: \"\\ee9b\";\n  }\n  .bxs-select-multiple:before {\n    content: \"\\ee9c\";\n  }\n  .bxs-send:before {\n    content: \"\\ee9d\";\n  }\n  .bxs-server:before {\n    content: \"\\ee9e\";\n  }\n  .bxs-shapes:before {\n    content: \"\\ee9f\";\n  }\n  .bxs-share:before {\n    content: \"\\eea0\";\n  }\n  .bxs-share-alt:before {\n    content: \"\\eea1\";\n  }\n  .bxs-shield:before {\n    content: \"\\eea2\";\n  }\n  .bxs-shield-alt-2:before {\n    content: \"\\eea3\";\n  }\n  .bxs-shield-x:before {\n    content: \"\\eea4\";\n  }\n  .bxs-ship:before {\n    content: \"\\eea5\";\n  }\n  .bxs-shocked:before {\n    content: \"\\eea6\";\n  }\n  .bxs-shopping-bag:before {\n    content: \"\\eea7\";\n  }\n  .bxs-shopping-bag-alt:before {\n    content: \"\\eea8\";\n  }\n  .bxs-shopping-bags:before {\n    content: \"\\eea9\";\n  }\n  .bxs-show:before {\n    content: \"\\eeaa\";\n  }\n  .bxs-skip-next-circle:before {\n    content: \"\\eeab\";\n  }\n  .bxs-skip-previous-circle:before {\n    content: \"\\eeac\";\n  }\n  .bxs-skull:before {\n    content: \"\\eead\";\n  }\n  .bxs-sleepy:before {\n    content: \"\\eeae\";\n  }\n  .bxs-slideshow:before {\n    content: \"\\eeaf\";\n  }\n  .bxs-smile:before {\n    content: \"\\eeb0\";\n  }\n  .bxs-sort-alt:before {\n    content: \"\\eeb1\";\n  }\n  .bxs-spa:before {\n    content: \"\\eeb2\";\n  }\n  .bxs-speaker:before {\n    content: \"\\eeb3\";\n  }\n  .bxs-spray-can:before {\n    content: \"\\eeb4\";\n  }\n  .bxs-spreadsheet:before {\n    content: \"\\eeb5\";\n  }\n  .bxs-square:before {\n    content: \"\\eeb6\";\n  }\n  .bxs-square-rounded:before {\n    content: \"\\eeb7\";\n  }\n  .bxs-star:before {\n    content: \"\\eeb8\";\n  }\n  .bxs-star-half:before {\n    content: \"\\eeb9\";\n  }\n  .bxs-sticker:before {\n    content: \"\\eeba\";\n  }\n  .bxs-stopwatch:before {\n    content: \"\\eebb\";\n  }\n  .bxs-store:before {\n    content: \"\\eebc\";\n  }\n  .bxs-store-alt:before {\n    content: \"\\eebd\";\n  }\n  .bxs-sun:before {\n    content: \"\\eebe\";\n  }\n  .bxs-tachometer:before {\n    content: \"\\eebf\";\n  }\n  .bxs-tag:before {\n    content: \"\\eec0\";\n  }\n  .bxs-tag-alt:before {\n    content: \"\\eec1\";\n  }\n  .bxs-tag-x:before {\n    content: \"\\eec2\";\n  }\n  .bxs-taxi:before {\n    content: \"\\eec3\";\n  }\n  .bxs-tennis-ball:before {\n    content: \"\\eec4\";\n  }\n  .bxs-terminal:before {\n    content: \"\\eec5\";\n  }\n  .bxs-thermometer:before {\n    content: \"\\eec6\";\n  }\n  .bxs-time:before {\n    content: \"\\eec7\";\n  }\n  .bxs-time-five:before {\n    content: \"\\eec8\";\n  }\n  .bxs-timer:before {\n    content: \"\\eec9\";\n  }\n  .bxs-tired:before {\n    content: \"\\eeca\";\n  }\n  .bxs-toggle-left:before {\n    content: \"\\eecb\";\n  }\n  .bxs-toggle-right:before {\n    content: \"\\eecc\";\n  }\n  .bxs-tone:before {\n    content: \"\\eecd\";\n  }\n  .bxs-torch:before {\n    content: \"\\eece\";\n  }\n  .bxs-to-top:before {\n    content: \"\\eecf\";\n  }\n  .bxs-traffic:before {\n    content: \"\\eed0\";\n  }\n  .bxs-traffic-barrier:before {\n    content: \"\\eed1\";\n  }\n  .bxs-traffic-cone:before {\n    content: \"\\eed2\";\n  }\n  .bxs-train:before {\n    content: \"\\eed3\";\n  }\n  .bxs-trash:before {\n    content: \"\\eed4\";\n  }\n  .bxs-trash-alt:before {\n    content: \"\\eed5\";\n  }\n  .bxs-tree:before {\n    content: \"\\eed6\";\n  }\n  .bxs-trophy:before {\n    content: \"\\eed7\";\n  }\n  .bxs-truck:before {\n    content: \"\\eed8\";\n  }\n  .bxs-t-shirt:before {\n    content: \"\\eed9\";\n  }\n  .bxs-tv:before {\n    content: \"\\eeda\";\n  }\n  .bxs-up-arrow:before {\n    content: \"\\eedb\";\n  }\n  .bxs-up-arrow-alt:before {\n    content: \"\\eedc\";\n  }\n  .bxs-up-arrow-circle:before {\n    content: \"\\eedd\";\n  }\n  .bxs-up-arrow-square:before {\n    content: \"\\eede\";\n  }\n  .bxs-upside-down:before {\n    content: \"\\eedf\";\n  }\n  .bxs-upvote:before {\n    content: \"\\eee0\";\n  }\n  .bxs-user:before {\n    content: \"\\eee1\";\n  }\n  .bxs-user-account:before {\n    content: \"\\eee2\";\n  }\n  .bxs-user-badge:before {\n    content: \"\\eee3\";\n  }\n  .bxs-user-check:before {\n    content: \"\\eee4\";\n  }\n  .bxs-user-circle:before {\n    content: \"\\eee5\";\n  }\n  .bxs-user-detail:before {\n    content: \"\\eee6\";\n  }\n  .bxs-user-minus:before {\n    content: \"\\eee7\";\n  }\n  .bxs-user-pin:before {\n    content: \"\\eee8\";\n  }\n  .bxs-user-plus:before {\n    content: \"\\eee9\";\n  }\n  .bxs-user-rectangle:before {\n    content: \"\\eeea\";\n  }\n  .bxs-user-voice:before {\n    content: \"\\eeeb\";\n  }\n  .bxs-user-x:before {\n    content: \"\\eeec\";\n  }\n  .bxs-vector:before {\n    content: \"\\eeed\";\n  }\n  .bxs-vial:before {\n    content: \"\\eeee\";\n  }\n  .bxs-video:before {\n    content: \"\\eeef\";\n  }\n  .bxs-video-off:before {\n    content: \"\\eef0\";\n  }\n  .bxs-video-plus:before {\n    content: \"\\eef1\";\n  }\n  .bxs-video-recording:before {\n    content: \"\\eef2\";\n  }\n  .bxs-videos:before {\n    content: \"\\eef3\";\n  }\n  .bxs-virus:before {\n    content: \"\\eef4\";\n  }\n  .bxs-virus-block:before {\n    content: \"\\eef5\";\n  }\n  .bxs-volume:before {\n    content: \"\\eef6\";\n  }\n  .bxs-volume-full:before {\n    content: \"\\eef7\";\n  }\n  .bxs-volume-low:before {\n    content: \"\\eef8\";\n  }\n  .bxs-volume-mute:before {\n    content: \"\\eef9\";\n  }\n  .bxs-wallet:before {\n    content: \"\\eefa\";\n  }\n  .bxs-wallet-alt:before {\n    content: \"\\eefb\";\n  }\n  .bxs-washer:before {\n    content: \"\\eefc\";\n  }\n  .bxs-watch:before {\n    content: \"\\eefd\";\n  }\n  .bxs-watch-alt:before {\n    content: \"\\eefe\";\n  }\n  .bxs-webcam:before {\n    content: \"\\eeff\";\n  }\n  .bxs-widget:before {\n    content: \"\\ef00\";\n  }\n  .bxs-window-alt:before {\n    content: \"\\ef01\";\n  }\n  .bxs-wine:before {\n    content: \"\\ef02\";\n  }\n  .bxs-wink-smile:before {\n    content: \"\\ef03\";\n  }\n  .bxs-wink-tongue:before {\n    content: \"\\ef04\";\n  }\n  .bxs-wrench:before {\n    content: \"\\ef05\";\n  }\n  .bxs-x-circle:before {\n    content: \"\\ef06\";\n  }\n  .bxs-x-square:before {\n    content: \"\\ef07\";\n  }\n  .bxs-yin-yang:before {\n    content: \"\\ef08\";\n  }\n  .bxs-zap:before {\n    content: \"\\ef09\";\n  }\n  .bxs-zoom-in:before {\n    content: \"\\ef0a\";\n  }\n  .bxs-zoom-out:before {\n    content: \"\\ef0b\";\n  }\n  "
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/boxicons/css/transformations.css",
    "content": ".bx-rotate-90\n{\n    transform: rotate(90deg);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)';\n}\n.bx-rotate-180\n{\n    transform: rotate(180deg);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)';\n}\n.bx-rotate-270\n{\n    transform: rotate(270deg);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)';\n}\n.bx-flip-horizontal\n{\n    transform: scaleX(-1);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)';\n}\n.bx-flip-vertical\n{\n    transform: scaleY(-1);\n\n    -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)';\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/chart.cjs",
    "content": "/*!\n * Chart.js v4.2.1\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\n'use strict';\n\nvar helpers_segment = require('./chunks/helpers.segment.cjs');\nrequire('@kurkle/color');\n\nclass Animator {\n    constructor(){\n        this._request = null;\n        this._charts = new Map();\n        this._running = false;\n        this._lastDate = undefined;\n    }\n _notify(chart, anims, date, type) {\n        const callbacks = anims.listeners[type];\n        const numSteps = anims.duration;\n        callbacks.forEach((fn)=>fn({\n                chart,\n                initial: anims.initial,\n                numSteps,\n                currentStep: Math.min(date - anims.start, numSteps)\n            }));\n    }\n _refresh() {\n        if (this._request) {\n            return;\n        }\n        this._running = true;\n        this._request = helpers_segment.requestAnimFrame.call(window, ()=>{\n            this._update();\n            this._request = null;\n            if (this._running) {\n                this._refresh();\n            }\n        });\n    }\n _update(date = Date.now()) {\n        let remaining = 0;\n        this._charts.forEach((anims, chart)=>{\n            if (!anims.running || !anims.items.length) {\n                return;\n            }\n            const items = anims.items;\n            let i = items.length - 1;\n            let draw = false;\n            let item;\n            for(; i >= 0; --i){\n                item = items[i];\n                if (item._active) {\n                    if (item._total > anims.duration) {\n                        anims.duration = item._total;\n                    }\n                    item.tick(date);\n                    draw = true;\n                } else {\n                    items[i] = items[items.length - 1];\n                    items.pop();\n                }\n            }\n            if (draw) {\n                chart.draw();\n                this._notify(chart, anims, date, 'progress');\n            }\n            if (!items.length) {\n                anims.running = false;\n                this._notify(chart, anims, date, 'complete');\n                anims.initial = false;\n            }\n            remaining += items.length;\n        });\n        this._lastDate = date;\n        if (remaining === 0) {\n            this._running = false;\n        }\n    }\n _getAnims(chart) {\n        const charts = this._charts;\n        let anims = charts.get(chart);\n        if (!anims) {\n            anims = {\n                running: false,\n                initial: true,\n                items: [],\n                listeners: {\n                    complete: [],\n                    progress: []\n                }\n            };\n            charts.set(chart, anims);\n        }\n        return anims;\n    }\n listen(chart, event, cb) {\n        this._getAnims(chart).listeners[event].push(cb);\n    }\n add(chart, items) {\n        if (!items || !items.length) {\n            return;\n        }\n        this._getAnims(chart).items.push(...items);\n    }\n has(chart) {\n        return this._getAnims(chart).items.length > 0;\n    }\n start(chart) {\n        const anims = this._charts.get(chart);\n        if (!anims) {\n            return;\n        }\n        anims.running = true;\n        anims.start = Date.now();\n        anims.duration = anims.items.reduce((acc, cur)=>Math.max(acc, cur._duration), 0);\n        this._refresh();\n    }\n    running(chart) {\n        if (!this._running) {\n            return false;\n        }\n        const anims = this._charts.get(chart);\n        if (!anims || !anims.running || !anims.items.length) {\n            return false;\n        }\n        return true;\n    }\n stop(chart) {\n        const anims = this._charts.get(chart);\n        if (!anims || !anims.items.length) {\n            return;\n        }\n        const items = anims.items;\n        let i = items.length - 1;\n        for(; i >= 0; --i){\n            items[i].cancel();\n        }\n        anims.items = [];\n        this._notify(chart, anims, Date.now(), 'complete');\n    }\n remove(chart) {\n        return this._charts.delete(chart);\n    }\n}\nvar animator = /* #__PURE__ */ new Animator();\n\nconst transparent = 'transparent';\nconst interpolators = {\n    boolean (from, to, factor) {\n        return factor > 0.5 ? to : from;\n    },\n color (from, to, factor) {\n        const c0 = helpers_segment.color(from || transparent);\n        const c1 = c0.valid && helpers_segment.color(to || transparent);\n        return c1 && c1.valid ? c1.mix(c0, factor).hexString() : to;\n    },\n    number (from, to, factor) {\n        return from + (to - from) * factor;\n    }\n};\nclass Animation {\n    constructor(cfg, target, prop, to){\n        const currentValue = target[prop];\n        to = helpers_segment.resolve([\n            cfg.to,\n            to,\n            currentValue,\n            cfg.from\n        ]);\n        const from = helpers_segment.resolve([\n            cfg.from,\n            currentValue,\n            to\n        ]);\n        this._active = true;\n        this._fn = cfg.fn || interpolators[cfg.type || typeof from];\n        this._easing = helpers_segment.effects[cfg.easing] || helpers_segment.effects.linear;\n        this._start = Math.floor(Date.now() + (cfg.delay || 0));\n        this._duration = this._total = Math.floor(cfg.duration);\n        this._loop = !!cfg.loop;\n        this._target = target;\n        this._prop = prop;\n        this._from = from;\n        this._to = to;\n        this._promises = undefined;\n    }\n    active() {\n        return this._active;\n    }\n    update(cfg, to, date) {\n        if (this._active) {\n            this._notify(false);\n            const currentValue = this._target[this._prop];\n            const elapsed = date - this._start;\n            const remain = this._duration - elapsed;\n            this._start = date;\n            this._duration = Math.floor(Math.max(remain, cfg.duration));\n            this._total += elapsed;\n            this._loop = !!cfg.loop;\n            this._to = helpers_segment.resolve([\n                cfg.to,\n                to,\n                currentValue,\n                cfg.from\n            ]);\n            this._from = helpers_segment.resolve([\n                cfg.from,\n                currentValue,\n                to\n            ]);\n        }\n    }\n    cancel() {\n        if (this._active) {\n            this.tick(Date.now());\n            this._active = false;\n            this._notify(false);\n        }\n    }\n    tick(date) {\n        const elapsed = date - this._start;\n        const duration = this._duration;\n        const prop = this._prop;\n        const from = this._from;\n        const loop = this._loop;\n        const to = this._to;\n        let factor;\n        this._active = from !== to && (loop || elapsed < duration);\n        if (!this._active) {\n            this._target[prop] = to;\n            this._notify(true);\n            return;\n        }\n        if (elapsed < 0) {\n            this._target[prop] = from;\n            return;\n        }\n        factor = elapsed / duration % 2;\n        factor = loop && factor > 1 ? 2 - factor : factor;\n        factor = this._easing(Math.min(1, Math.max(0, factor)));\n        this._target[prop] = this._fn(from, to, factor);\n    }\n    wait() {\n        const promises = this._promises || (this._promises = []);\n        return new Promise((res, rej)=>{\n            promises.push({\n                res,\n                rej\n            });\n        });\n    }\n    _notify(resolved) {\n        const method = resolved ? 'res' : 'rej';\n        const promises = this._promises || [];\n        for(let i = 0; i < promises.length; i++){\n            promises[i][method]();\n        }\n    }\n}\n\nclass Animations {\n    constructor(chart, config){\n        this._chart = chart;\n        this._properties = new Map();\n        this.configure(config);\n    }\n    configure(config) {\n        if (!helpers_segment.isObject(config)) {\n            return;\n        }\n        const animationOptions = Object.keys(helpers_segment.defaults.animation);\n        const animatedProps = this._properties;\n        Object.getOwnPropertyNames(config).forEach((key)=>{\n            const cfg = config[key];\n            if (!helpers_segment.isObject(cfg)) {\n                return;\n            }\n            const resolved = {};\n            for (const option of animationOptions){\n                resolved[option] = cfg[option];\n            }\n            (helpers_segment.isArray(cfg.properties) && cfg.properties || [\n                key\n            ]).forEach((prop)=>{\n                if (prop === key || !animatedProps.has(prop)) {\n                    animatedProps.set(prop, resolved);\n                }\n            });\n        });\n    }\n _animateOptions(target, values) {\n        const newOptions = values.options;\n        const options = resolveTargetOptions(target, newOptions);\n        if (!options) {\n            return [];\n        }\n        const animations = this._createAnimations(options, newOptions);\n        if (newOptions.$shared) {\n            awaitAll(target.options.$animations, newOptions).then(()=>{\n                target.options = newOptions;\n            }, ()=>{\n            });\n        }\n        return animations;\n    }\n _createAnimations(target, values) {\n        const animatedProps = this._properties;\n        const animations = [];\n        const running = target.$animations || (target.$animations = {});\n        const props = Object.keys(values);\n        const date = Date.now();\n        let i;\n        for(i = props.length - 1; i >= 0; --i){\n            const prop = props[i];\n            if (prop.charAt(0) === '$') {\n                continue;\n            }\n            if (prop === 'options') {\n                animations.push(...this._animateOptions(target, values));\n                continue;\n            }\n            const value = values[prop];\n            let animation = running[prop];\n            const cfg = animatedProps.get(prop);\n            if (animation) {\n                if (cfg && animation.active()) {\n                    animation.update(cfg, value, date);\n                    continue;\n                } else {\n                    animation.cancel();\n                }\n            }\n            if (!cfg || !cfg.duration) {\n                target[prop] = value;\n                continue;\n            }\n            running[prop] = animation = new Animation(cfg, target, prop, value);\n            animations.push(animation);\n        }\n        return animations;\n    }\n update(target, values) {\n        if (this._properties.size === 0) {\n            Object.assign(target, values);\n            return;\n        }\n        const animations = this._createAnimations(target, values);\n        if (animations.length) {\n            animator.add(this._chart, animations);\n            return true;\n        }\n    }\n}\nfunction awaitAll(animations, properties) {\n    const running = [];\n    const keys = Object.keys(properties);\n    for(let i = 0; i < keys.length; i++){\n        const anim = animations[keys[i]];\n        if (anim && anim.active()) {\n            running.push(anim.wait());\n        }\n    }\n    return Promise.all(running);\n}\nfunction resolveTargetOptions(target, newOptions) {\n    if (!newOptions) {\n        return;\n    }\n    let options = target.options;\n    if (!options) {\n        target.options = newOptions;\n        return;\n    }\n    if (options.$shared) {\n        target.options = options = Object.assign({}, options, {\n            $shared: false,\n            $animations: {}\n        });\n    }\n    return options;\n}\n\nfunction scaleClip(scale, allowedOverflow) {\n    const opts = scale && scale.options || {};\n    const reverse = opts.reverse;\n    const min = opts.min === undefined ? allowedOverflow : 0;\n    const max = opts.max === undefined ? allowedOverflow : 0;\n    return {\n        start: reverse ? max : min,\n        end: reverse ? min : max\n    };\n}\nfunction defaultClip(xScale, yScale, allowedOverflow) {\n    if (allowedOverflow === false) {\n        return false;\n    }\n    const x = scaleClip(xScale, allowedOverflow);\n    const y = scaleClip(yScale, allowedOverflow);\n    return {\n        top: y.end,\n        right: x.end,\n        bottom: y.start,\n        left: x.start\n    };\n}\nfunction toClip(value) {\n    let t, r, b, l;\n    if (helpers_segment.isObject(value)) {\n        t = value.top;\n        r = value.right;\n        b = value.bottom;\n        l = value.left;\n    } else {\n        t = r = b = l = value;\n    }\n    return {\n        top: t,\n        right: r,\n        bottom: b,\n        left: l,\n        disabled: value === false\n    };\n}\nfunction getSortedDatasetIndices(chart, filterVisible) {\n    const keys = [];\n    const metasets = chart._getSortedDatasetMetas(filterVisible);\n    let i, ilen;\n    for(i = 0, ilen = metasets.length; i < ilen; ++i){\n        keys.push(metasets[i].index);\n    }\n    return keys;\n}\nfunction applyStack(stack, value, dsIndex, options = {}) {\n    const keys = stack.keys;\n    const singleMode = options.mode === 'single';\n    let i, ilen, datasetIndex, otherValue;\n    if (value === null) {\n        return;\n    }\n    for(i = 0, ilen = keys.length; i < ilen; ++i){\n        datasetIndex = +keys[i];\n        if (datasetIndex === dsIndex) {\n            if (options.all) {\n                continue;\n            }\n            break;\n        }\n        otherValue = stack.values[datasetIndex];\n        if (helpers_segment.isNumberFinite(otherValue) && (singleMode || value === 0 || helpers_segment.sign(value) === helpers_segment.sign(otherValue))) {\n            value += otherValue;\n        }\n    }\n    return value;\n}\nfunction convertObjectDataToArray(data) {\n    const keys = Object.keys(data);\n    const adata = new Array(keys.length);\n    let i, ilen, key;\n    for(i = 0, ilen = keys.length; i < ilen; ++i){\n        key = keys[i];\n        adata[i] = {\n            x: key,\n            y: data[key]\n        };\n    }\n    return adata;\n}\nfunction isStacked(scale, meta) {\n    const stacked = scale && scale.options.stacked;\n    return stacked || stacked === undefined && meta.stack !== undefined;\n}\nfunction getStackKey(indexScale, valueScale, meta) {\n    return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;\n}\nfunction getUserBounds(scale) {\n    const { min , max , minDefined , maxDefined  } = scale.getUserBounds();\n    return {\n        min: minDefined ? min : Number.NEGATIVE_INFINITY,\n        max: maxDefined ? max : Number.POSITIVE_INFINITY\n    };\n}\nfunction getOrCreateStack(stacks, stackKey, indexValue) {\n    const subStack = stacks[stackKey] || (stacks[stackKey] = {});\n    return subStack[indexValue] || (subStack[indexValue] = {});\n}\nfunction getLastIndexInStack(stack, vScale, positive, type) {\n    for (const meta of vScale.getMatchingVisibleMetas(type).reverse()){\n        const value = stack[meta.index];\n        if (positive && value > 0 || !positive && value < 0) {\n            return meta.index;\n        }\n    }\n    return null;\n}\nfunction updateStacks(controller, parsed) {\n    const { chart , _cachedMeta: meta  } = controller;\n    const stacks = chart._stacks || (chart._stacks = {});\n    const { iScale , vScale , index: datasetIndex  } = meta;\n    const iAxis = iScale.axis;\n    const vAxis = vScale.axis;\n    const key = getStackKey(iScale, vScale, meta);\n    const ilen = parsed.length;\n    let stack;\n    for(let i = 0; i < ilen; ++i){\n        const item = parsed[i];\n        const { [iAxis]: index , [vAxis]: value  } = item;\n        const itemStacks = item._stacks || (item._stacks = {});\n        stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);\n        stack[datasetIndex] = value;\n        stack._top = getLastIndexInStack(stack, vScale, true, meta.type);\n        stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);\n        const visualValues = stack._visualValues || (stack._visualValues = {});\n        visualValues[datasetIndex] = value;\n    }\n}\nfunction getFirstScaleId(chart, axis) {\n    const scales = chart.scales;\n    return Object.keys(scales).filter((key)=>scales[key].axis === axis).shift();\n}\nfunction createDatasetContext(parent, index) {\n    return helpers_segment.createContext(parent, {\n        active: false,\n        dataset: undefined,\n        datasetIndex: index,\n        index,\n        mode: 'default',\n        type: 'dataset'\n    });\n}\nfunction createDataContext(parent, index, element) {\n    return helpers_segment.createContext(parent, {\n        active: false,\n        dataIndex: index,\n        parsed: undefined,\n        raw: undefined,\n        element,\n        index,\n        mode: 'default',\n        type: 'data'\n    });\n}\nfunction clearStacks(meta, items) {\n    const datasetIndex = meta.controller.index;\n    const axis = meta.vScale && meta.vScale.axis;\n    if (!axis) {\n        return;\n    }\n    items = items || meta._parsed;\n    for (const parsed of items){\n        const stacks = parsed._stacks;\n        if (!stacks || stacks[axis] === undefined || stacks[axis][datasetIndex] === undefined) {\n            return;\n        }\n        delete stacks[axis][datasetIndex];\n        if (stacks[axis]._visualValues !== undefined && stacks[axis]._visualValues[datasetIndex] !== undefined) {\n            delete stacks[axis]._visualValues[datasetIndex];\n        }\n    }\n}\nconst isDirectUpdateMode = (mode)=>mode === 'reset' || mode === 'none';\nconst cloneIfNotShared = (cached, shared)=>shared ? cached : Object.assign({}, cached);\nconst createStack = (canStack, meta, chart)=>canStack && !meta.hidden && meta._stacked && {\n        keys: getSortedDatasetIndices(chart, true),\n        values: null\n    };\nclass DatasetController {\n static defaults = {};\n static datasetElementType = null;\n static dataElementType = null;\n constructor(chart, datasetIndex){\n        this.chart = chart;\n        this._ctx = chart.ctx;\n        this.index = datasetIndex;\n        this._cachedDataOpts = {};\n        this._cachedMeta = this.getMeta();\n        this._type = this._cachedMeta.type;\n        this.options = undefined;\n         this._parsing = false;\n        this._data = undefined;\n        this._objectData = undefined;\n        this._sharedOptions = undefined;\n        this._drawStart = undefined;\n        this._drawCount = undefined;\n        this.enableOptionSharing = false;\n        this.supportsDecimation = false;\n        this.$context = undefined;\n        this._syncList = [];\n        this.datasetElementType = new.target.datasetElementType;\n        this.dataElementType = new.target.dataElementType;\n        this.initialize();\n    }\n    initialize() {\n        const meta = this._cachedMeta;\n        this.configure();\n        this.linkScales();\n        meta._stacked = isStacked(meta.vScale, meta);\n        this.addElements();\n        if (this.options.fill && !this.chart.isPluginEnabled('filler')) {\n            console.warn(\"Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options\");\n        }\n    }\n    updateIndex(datasetIndex) {\n        if (this.index !== datasetIndex) {\n            clearStacks(this._cachedMeta);\n        }\n        this.index = datasetIndex;\n    }\n    linkScales() {\n        const chart = this.chart;\n        const meta = this._cachedMeta;\n        const dataset = this.getDataset();\n        const chooseId = (axis, x, y, r)=>axis === 'x' ? x : axis === 'r' ? r : y;\n        const xid = meta.xAxisID = helpers_segment.valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x'));\n        const yid = meta.yAxisID = helpers_segment.valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y'));\n        const rid = meta.rAxisID = helpers_segment.valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, 'r'));\n        const indexAxis = meta.indexAxis;\n        const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);\n        const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);\n        meta.xScale = this.getScaleForId(xid);\n        meta.yScale = this.getScaleForId(yid);\n        meta.rScale = this.getScaleForId(rid);\n        meta.iScale = this.getScaleForId(iid);\n        meta.vScale = this.getScaleForId(vid);\n    }\n    getDataset() {\n        return this.chart.data.datasets[this.index];\n    }\n    getMeta() {\n        return this.chart.getDatasetMeta(this.index);\n    }\n getScaleForId(scaleID) {\n        return this.chart.scales[scaleID];\n    }\n _getOtherScale(scale) {\n        const meta = this._cachedMeta;\n        return scale === meta.iScale ? meta.vScale : meta.iScale;\n    }\n    reset() {\n        this._update('reset');\n    }\n _destroy() {\n        const meta = this._cachedMeta;\n        if (this._data) {\n            helpers_segment.unlistenArrayEvents(this._data, this);\n        }\n        if (meta._stacked) {\n            clearStacks(meta);\n        }\n    }\n _dataCheck() {\n        const dataset = this.getDataset();\n        const data = dataset.data || (dataset.data = []);\n        const _data = this._data;\n        if (helpers_segment.isObject(data)) {\n            this._data = convertObjectDataToArray(data);\n        } else if (_data !== data) {\n            if (_data) {\n                helpers_segment.unlistenArrayEvents(_data, this);\n                const meta = this._cachedMeta;\n                clearStacks(meta);\n                meta._parsed = [];\n            }\n            if (data && Object.isExtensible(data)) {\n                helpers_segment.listenArrayEvents(data, this);\n            }\n            this._syncList = [];\n            this._data = data;\n        }\n    }\n    addElements() {\n        const meta = this._cachedMeta;\n        this._dataCheck();\n        if (this.datasetElementType) {\n            meta.dataset = new this.datasetElementType();\n        }\n    }\n    buildOrUpdateElements(resetNewElements) {\n        const meta = this._cachedMeta;\n        const dataset = this.getDataset();\n        let stackChanged = false;\n        this._dataCheck();\n        const oldStacked = meta._stacked;\n        meta._stacked = isStacked(meta.vScale, meta);\n        if (meta.stack !== dataset.stack) {\n            stackChanged = true;\n            clearStacks(meta);\n            meta.stack = dataset.stack;\n        }\n        this._resyncElements(resetNewElements);\n        if (stackChanged || oldStacked !== meta._stacked) {\n            updateStacks(this, meta._parsed);\n        }\n    }\n configure() {\n        const config = this.chart.config;\n        const scopeKeys = config.datasetScopeKeys(this._type);\n        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);\n        this.options = config.createResolver(scopes, this.getContext());\n        this._parsing = this.options.parsing;\n        this._cachedDataOpts = {};\n    }\n parse(start, count) {\n        const { _cachedMeta: meta , _data: data  } = this;\n        const { iScale , _stacked  } = meta;\n        const iAxis = iScale.axis;\n        let sorted = start === 0 && count === data.length ? true : meta._sorted;\n        let prev = start > 0 && meta._parsed[start - 1];\n        let i, cur, parsed;\n        if (this._parsing === false) {\n            meta._parsed = data;\n            meta._sorted = true;\n            parsed = data;\n        } else {\n            if (helpers_segment.isArray(data[start])) {\n                parsed = this.parseArrayData(meta, data, start, count);\n            } else if (helpers_segment.isObject(data[start])) {\n                parsed = this.parseObjectData(meta, data, start, count);\n            } else {\n                parsed = this.parsePrimitiveData(meta, data, start, count);\n            }\n            const isNotInOrderComparedToPrev = ()=>cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis];\n            for(i = 0; i < count; ++i){\n                meta._parsed[i + start] = cur = parsed[i];\n                if (sorted) {\n                    if (isNotInOrderComparedToPrev()) {\n                        sorted = false;\n                    }\n                    prev = cur;\n                }\n            }\n            meta._sorted = sorted;\n        }\n        if (_stacked) {\n            updateStacks(this, parsed);\n        }\n    }\n parsePrimitiveData(meta, data, start, count) {\n        const { iScale , vScale  } = meta;\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        const labels = iScale.getLabels();\n        const singleScale = iScale === vScale;\n        const parsed = new Array(count);\n        let i, ilen, index;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            index = i + start;\n            parsed[i] = {\n                [iAxis]: singleScale || iScale.parse(labels[index], index),\n                [vAxis]: vScale.parse(data[index], index)\n            };\n        }\n        return parsed;\n    }\n parseArrayData(meta, data, start, count) {\n        const { xScale , yScale  } = meta;\n        const parsed = new Array(count);\n        let i, ilen, index, item;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            index = i + start;\n            item = data[index];\n            parsed[i] = {\n                x: xScale.parse(item[0], index),\n                y: yScale.parse(item[1], index)\n            };\n        }\n        return parsed;\n    }\n parseObjectData(meta, data, start, count) {\n        const { xScale , yScale  } = meta;\n        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;\n        const parsed = new Array(count);\n        let i, ilen, index, item;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            index = i + start;\n            item = data[index];\n            parsed[i] = {\n                x: xScale.parse(helpers_segment.resolveObjectKey(item, xAxisKey), index),\n                y: yScale.parse(helpers_segment.resolveObjectKey(item, yAxisKey), index)\n            };\n        }\n        return parsed;\n    }\n getParsed(index) {\n        return this._cachedMeta._parsed[index];\n    }\n getDataElement(index) {\n        return this._cachedMeta.data[index];\n    }\n applyStack(scale, parsed, mode) {\n        const chart = this.chart;\n        const meta = this._cachedMeta;\n        const value = parsed[scale.axis];\n        const stack = {\n            keys: getSortedDatasetIndices(chart, true),\n            values: parsed._stacks[scale.axis]._visualValues\n        };\n        return applyStack(stack, value, meta.index, {\n            mode\n        });\n    }\n updateRangeFromParsed(range, scale, parsed, stack) {\n        const parsedValue = parsed[scale.axis];\n        let value = parsedValue === null ? NaN : parsedValue;\n        const values = stack && parsed._stacks[scale.axis];\n        if (stack && values) {\n            stack.values = values;\n            value = applyStack(stack, parsedValue, this._cachedMeta.index);\n        }\n        range.min = Math.min(range.min, value);\n        range.max = Math.max(range.max, value);\n    }\n getMinMax(scale, canStack) {\n        const meta = this._cachedMeta;\n        const _parsed = meta._parsed;\n        const sorted = meta._sorted && scale === meta.iScale;\n        const ilen = _parsed.length;\n        const otherScale = this._getOtherScale(scale);\n        const stack = createStack(canStack, meta, this.chart);\n        const range = {\n            min: Number.POSITIVE_INFINITY,\n            max: Number.NEGATIVE_INFINITY\n        };\n        const { min: otherMin , max: otherMax  } = getUserBounds(otherScale);\n        let i, parsed;\n        function _skip() {\n            parsed = _parsed[i];\n            const otherValue = parsed[otherScale.axis];\n            return !helpers_segment.isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue;\n        }\n        for(i = 0; i < ilen; ++i){\n            if (_skip()) {\n                continue;\n            }\n            this.updateRangeFromParsed(range, scale, parsed, stack);\n            if (sorted) {\n                break;\n            }\n        }\n        if (sorted) {\n            for(i = ilen - 1; i >= 0; --i){\n                if (_skip()) {\n                    continue;\n                }\n                this.updateRangeFromParsed(range, scale, parsed, stack);\n                break;\n            }\n        }\n        return range;\n    }\n    getAllParsedValues(scale) {\n        const parsed = this._cachedMeta._parsed;\n        const values = [];\n        let i, ilen, value;\n        for(i = 0, ilen = parsed.length; i < ilen; ++i){\n            value = parsed[i][scale.axis];\n            if (helpers_segment.isNumberFinite(value)) {\n                values.push(value);\n            }\n        }\n        return values;\n    }\n getMaxOverflow() {\n        return false;\n    }\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const iScale = meta.iScale;\n        const vScale = meta.vScale;\n        const parsed = this.getParsed(index);\n        return {\n            label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',\n            value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''\n        };\n    }\n _update(mode) {\n        const meta = this._cachedMeta;\n        this.update(mode || 'default');\n        meta._clip = toClip(helpers_segment.valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));\n    }\n update(mode) {}\n    draw() {\n        const ctx = this._ctx;\n        const chart = this.chart;\n        const meta = this._cachedMeta;\n        const elements = meta.data || [];\n        const area = chart.chartArea;\n        const active = [];\n        const start = this._drawStart || 0;\n        const count = this._drawCount || elements.length - start;\n        const drawActiveElementsOnTop = this.options.drawActiveElementsOnTop;\n        let i;\n        if (meta.dataset) {\n            meta.dataset.draw(ctx, area, start, count);\n        }\n        for(i = start; i < start + count; ++i){\n            const element = elements[i];\n            if (element.hidden) {\n                continue;\n            }\n            if (element.active && drawActiveElementsOnTop) {\n                active.push(element);\n            } else {\n                element.draw(ctx, area);\n            }\n        }\n        for(i = 0; i < active.length; ++i){\n            active[i].draw(ctx, area);\n        }\n    }\n getStyle(index, active) {\n        const mode = active ? 'active' : 'default';\n        return index === undefined && this._cachedMeta.dataset ? this.resolveDatasetElementOptions(mode) : this.resolveDataElementOptions(index || 0, mode);\n    }\n getContext(index, active, mode) {\n        const dataset = this.getDataset();\n        let context;\n        if (index >= 0 && index < this._cachedMeta.data.length) {\n            const element = this._cachedMeta.data[index];\n            context = element.$context || (element.$context = createDataContext(this.getContext(), index, element));\n            context.parsed = this.getParsed(index);\n            context.raw = dataset.data[index];\n            context.index = context.dataIndex = index;\n        } else {\n            context = this.$context || (this.$context = createDatasetContext(this.chart.getContext(), this.index));\n            context.dataset = dataset;\n            context.index = context.datasetIndex = this.index;\n        }\n        context.active = !!active;\n        context.mode = mode;\n        return context;\n    }\n resolveDatasetElementOptions(mode) {\n        return this._resolveElementOptions(this.datasetElementType.id, mode);\n    }\n resolveDataElementOptions(index, mode) {\n        return this._resolveElementOptions(this.dataElementType.id, mode, index);\n    }\n _resolveElementOptions(elementType, mode = 'default', index) {\n        const active = mode === 'active';\n        const cache = this._cachedDataOpts;\n        const cacheKey = elementType + '-' + mode;\n        const cached = cache[cacheKey];\n        const sharing = this.enableOptionSharing && helpers_segment.defined(index);\n        if (cached) {\n            return cloneIfNotShared(cached, sharing);\n        }\n        const config = this.chart.config;\n        const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);\n        const prefixes = active ? [\n            `${elementType}Hover`,\n            'hover',\n            elementType,\n            ''\n        ] : [\n            elementType,\n            ''\n        ];\n        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);\n        const names = Object.keys(helpers_segment.defaults.elements[elementType]);\n        const context = ()=>this.getContext(index, active, mode);\n        const values = config.resolveNamedOptions(scopes, names, context, prefixes);\n        if (values.$shared) {\n            values.$shared = sharing;\n            cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing));\n        }\n        return values;\n    }\n _resolveAnimations(index, transition, active) {\n        const chart = this.chart;\n        const cache = this._cachedDataOpts;\n        const cacheKey = `animation-${transition}`;\n        const cached = cache[cacheKey];\n        if (cached) {\n            return cached;\n        }\n        let options;\n        if (chart.options.animation !== false) {\n            const config = this.chart.config;\n            const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);\n            const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);\n            options = config.createResolver(scopes, this.getContext(index, active, transition));\n        }\n        const animations = new Animations(chart, options && options.animations);\n        if (options && options._cacheable) {\n            cache[cacheKey] = Object.freeze(animations);\n        }\n        return animations;\n    }\n getSharedOptions(options) {\n        if (!options.$shared) {\n            return;\n        }\n        return this._sharedOptions || (this._sharedOptions = Object.assign({}, options));\n    }\n includeOptions(mode, sharedOptions) {\n        return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled;\n    }\n _getSharedOptions(start, mode) {\n        const firstOpts = this.resolveDataElementOptions(start, mode);\n        const previouslySharedOptions = this._sharedOptions;\n        const sharedOptions = this.getSharedOptions(firstOpts);\n        const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions;\n        this.updateSharedOptions(sharedOptions, mode, firstOpts);\n        return {\n            sharedOptions,\n            includeOptions\n        };\n    }\n updateElement(element, index, properties, mode) {\n        if (isDirectUpdateMode(mode)) {\n            Object.assign(element, properties);\n        } else {\n            this._resolveAnimations(index, mode).update(element, properties);\n        }\n    }\n updateSharedOptions(sharedOptions, mode, newOptions) {\n        if (sharedOptions && !isDirectUpdateMode(mode)) {\n            this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions);\n        }\n    }\n _setStyle(element, index, mode, active) {\n        element.active = active;\n        const options = this.getStyle(index, active);\n        this._resolveAnimations(index, mode, active).update(element, {\n            options: !active && this.getSharedOptions(options) || options\n        });\n    }\n    removeHoverStyle(element, datasetIndex, index) {\n        this._setStyle(element, index, 'active', false);\n    }\n    setHoverStyle(element, datasetIndex, index) {\n        this._setStyle(element, index, 'active', true);\n    }\n _removeDatasetHoverStyle() {\n        const element = this._cachedMeta.dataset;\n        if (element) {\n            this._setStyle(element, undefined, 'active', false);\n        }\n    }\n _setDatasetHoverStyle() {\n        const element = this._cachedMeta.dataset;\n        if (element) {\n            this._setStyle(element, undefined, 'active', true);\n        }\n    }\n _resyncElements(resetNewElements) {\n        const data = this._data;\n        const elements = this._cachedMeta.data;\n        for (const [method, arg1, arg2] of this._syncList){\n            this[method](arg1, arg2);\n        }\n        this._syncList = [];\n        const numMeta = elements.length;\n        const numData = data.length;\n        const count = Math.min(numData, numMeta);\n        if (count) {\n            this.parse(0, count);\n        }\n        if (numData > numMeta) {\n            this._insertElements(numMeta, numData - numMeta, resetNewElements);\n        } else if (numData < numMeta) {\n            this._removeElements(numData, numMeta - numData);\n        }\n    }\n _insertElements(start, count, resetNewElements = true) {\n        const meta = this._cachedMeta;\n        const data = meta.data;\n        const end = start + count;\n        let i;\n        const move = (arr)=>{\n            arr.length += count;\n            for(i = arr.length - 1; i >= end; i--){\n                arr[i] = arr[i - count];\n            }\n        };\n        move(data);\n        for(i = start; i < end; ++i){\n            data[i] = new this.dataElementType();\n        }\n        if (this._parsing) {\n            move(meta._parsed);\n        }\n        this.parse(start, count);\n        if (resetNewElements) {\n            this.updateElements(data, start, count, 'reset');\n        }\n    }\n    updateElements(element, start, count, mode) {}\n _removeElements(start, count) {\n        const meta = this._cachedMeta;\n        if (this._parsing) {\n            const removed = meta._parsed.splice(start, count);\n            if (meta._stacked) {\n                clearStacks(meta, removed);\n            }\n        }\n        meta.data.splice(start, count);\n    }\n _sync(args) {\n        if (this._parsing) {\n            this._syncList.push(args);\n        } else {\n            const [method, arg1, arg2] = args;\n            this[method](arg1, arg2);\n        }\n        this.chart._dataChanges.push([\n            this.index,\n            ...args\n        ]);\n    }\n    _onDataPush() {\n        const count = arguments.length;\n        this._sync([\n            '_insertElements',\n            this.getDataset().data.length - count,\n            count\n        ]);\n    }\n    _onDataPop() {\n        this._sync([\n            '_removeElements',\n            this._cachedMeta.data.length - 1,\n            1\n        ]);\n    }\n    _onDataShift() {\n        this._sync([\n            '_removeElements',\n            0,\n            1\n        ]);\n    }\n    _onDataSplice(start, count) {\n        if (count) {\n            this._sync([\n                '_removeElements',\n                start,\n                count\n            ]);\n        }\n        const newCount = arguments.length - 2;\n        if (newCount) {\n            this._sync([\n                '_insertElements',\n                start,\n                newCount\n            ]);\n        }\n    }\n    _onDataUnshift() {\n        this._sync([\n            '_insertElements',\n            0,\n            arguments.length\n        ]);\n    }\n}\n\nfunction getAllScaleValues(scale, type) {\n    if (!scale._cache.$bar) {\n        const visibleMetas = scale.getMatchingVisibleMetas(type);\n        let values = [];\n        for(let i = 0, ilen = visibleMetas.length; i < ilen; i++){\n            values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale));\n        }\n        scale._cache.$bar = helpers_segment._arrayUnique(values.sort((a, b)=>a - b));\n    }\n    return scale._cache.$bar;\n}\n function computeMinSampleSize(meta) {\n    const scale = meta.iScale;\n    const values = getAllScaleValues(scale, meta.type);\n    let min = scale._length;\n    let i, ilen, curr, prev;\n    const updateMinAndPrev = ()=>{\n        if (curr === 32767 || curr === -32768) {\n            return;\n        }\n        if (helpers_segment.defined(prev)) {\n            min = Math.min(min, Math.abs(curr - prev) || min);\n        }\n        prev = curr;\n    };\n    for(i = 0, ilen = values.length; i < ilen; ++i){\n        curr = scale.getPixelForValue(values[i]);\n        updateMinAndPrev();\n    }\n    prev = undefined;\n    for(i = 0, ilen = scale.ticks.length; i < ilen; ++i){\n        curr = scale.getPixelForTick(i);\n        updateMinAndPrev();\n    }\n    return min;\n}\n function computeFitCategoryTraits(index, ruler, options, stackCount) {\n    const thickness = options.barThickness;\n    let size, ratio;\n    if (helpers_segment.isNullOrUndef(thickness)) {\n        size = ruler.min * options.categoryPercentage;\n        ratio = options.barPercentage;\n    } else {\n        size = thickness * stackCount;\n        ratio = 1;\n    }\n    return {\n        chunk: size / stackCount,\n        ratio,\n        start: ruler.pixels[index] - size / 2\n    };\n}\n function computeFlexCategoryTraits(index, ruler, options, stackCount) {\n    const pixels = ruler.pixels;\n    const curr = pixels[index];\n    let prev = index > 0 ? pixels[index - 1] : null;\n    let next = index < pixels.length - 1 ? pixels[index + 1] : null;\n    const percent = options.categoryPercentage;\n    if (prev === null) {\n        prev = curr - (next === null ? ruler.end - ruler.start : next - curr);\n    }\n    if (next === null) {\n        next = curr + curr - prev;\n    }\n    const start = curr - (curr - Math.min(prev, next)) / 2 * percent;\n    const size = Math.abs(next - prev) / 2 * percent;\n    return {\n        chunk: size / stackCount,\n        ratio: options.barPercentage,\n        start\n    };\n}\nfunction parseFloatBar(entry, item, vScale, i) {\n    const startValue = vScale.parse(entry[0], i);\n    const endValue = vScale.parse(entry[1], i);\n    const min = Math.min(startValue, endValue);\n    const max = Math.max(startValue, endValue);\n    let barStart = min;\n    let barEnd = max;\n    if (Math.abs(min) > Math.abs(max)) {\n        barStart = max;\n        barEnd = min;\n    }\n    item[vScale.axis] = barEnd;\n    item._custom = {\n        barStart,\n        barEnd,\n        start: startValue,\n        end: endValue,\n        min,\n        max\n    };\n}\nfunction parseValue(entry, item, vScale, i) {\n    if (helpers_segment.isArray(entry)) {\n        parseFloatBar(entry, item, vScale, i);\n    } else {\n        item[vScale.axis] = vScale.parse(entry, i);\n    }\n    return item;\n}\nfunction parseArrayOrPrimitive(meta, data, start, count) {\n    const iScale = meta.iScale;\n    const vScale = meta.vScale;\n    const labels = iScale.getLabels();\n    const singleScale = iScale === vScale;\n    const parsed = [];\n    let i, ilen, item, entry;\n    for(i = start, ilen = start + count; i < ilen; ++i){\n        entry = data[i];\n        item = {};\n        item[iScale.axis] = singleScale || iScale.parse(labels[i], i);\n        parsed.push(parseValue(entry, item, vScale, i));\n    }\n    return parsed;\n}\nfunction isFloatBar(custom) {\n    return custom && custom.barStart !== undefined && custom.barEnd !== undefined;\n}\nfunction barSign(size, vScale, actualBase) {\n    if (size !== 0) {\n        return helpers_segment.sign(size);\n    }\n    return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1);\n}\nfunction borderProps(properties) {\n    let reverse, start, end, top, bottom;\n    if (properties.horizontal) {\n        reverse = properties.base > properties.x;\n        start = 'left';\n        end = 'right';\n    } else {\n        reverse = properties.base < properties.y;\n        start = 'bottom';\n        end = 'top';\n    }\n    if (reverse) {\n        top = 'end';\n        bottom = 'start';\n    } else {\n        top = 'start';\n        bottom = 'end';\n    }\n    return {\n        start,\n        end,\n        reverse,\n        top,\n        bottom\n    };\n}\nfunction setBorderSkipped(properties, options, stack, index) {\n    let edge = options.borderSkipped;\n    const res = {};\n    if (!edge) {\n        properties.borderSkipped = res;\n        return;\n    }\n    if (edge === true) {\n        properties.borderSkipped = {\n            top: true,\n            right: true,\n            bottom: true,\n            left: true\n        };\n        return;\n    }\n    const { start , end , reverse , top , bottom  } = borderProps(properties);\n    if (edge === 'middle' && stack) {\n        properties.enableBorderRadius = true;\n        if ((stack._top || 0) === index) {\n            edge = top;\n        } else if ((stack._bottom || 0) === index) {\n            edge = bottom;\n        } else {\n            res[parseEdge(bottom, start, end, reverse)] = true;\n            edge = top;\n        }\n    }\n    res[parseEdge(edge, start, end, reverse)] = true;\n    properties.borderSkipped = res;\n}\nfunction parseEdge(edge, a, b, reverse) {\n    if (reverse) {\n        edge = swap(edge, a, b);\n        edge = startEnd(edge, b, a);\n    } else {\n        edge = startEnd(edge, a, b);\n    }\n    return edge;\n}\nfunction swap(orig, v1, v2) {\n    return orig === v1 ? v2 : orig === v2 ? v1 : orig;\n}\nfunction startEnd(v, start, end) {\n    return v === 'start' ? start : v === 'end' ? end : v;\n}\nfunction setInflateAmount(properties, { inflateAmount  }, ratio) {\n    properties.inflateAmount = inflateAmount === 'auto' ? ratio === 1 ? 0.33 : 0 : inflateAmount;\n}\nclass BarController extends DatasetController {\n    static id = 'bar';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'bar',\n        categoryPercentage: 0.8,\n        barPercentage: 0.9,\n        grouped: true,\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'base',\n                    'width',\n                    'height'\n                ]\n            }\n        }\n    };\n static overrides = {\n        scales: {\n            _index_: {\n                type: 'category',\n                offset: true,\n                grid: {\n                    offset: true\n                }\n            },\n            _value_: {\n                type: 'linear',\n                beginAtZero: true\n            }\n        }\n    };\n parsePrimitiveData(meta, data, start, count) {\n        return parseArrayOrPrimitive(meta, data, start, count);\n    }\n parseArrayData(meta, data, start, count) {\n        return parseArrayOrPrimitive(meta, data, start, count);\n    }\n parseObjectData(meta, data, start, count) {\n        const { iScale , vScale  } = meta;\n        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;\n        const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey;\n        const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey;\n        const parsed = [];\n        let i, ilen, item, obj;\n        for(i = start, ilen = start + count; i < ilen; ++i){\n            obj = data[i];\n            item = {};\n            item[iScale.axis] = iScale.parse(helpers_segment.resolveObjectKey(obj, iAxisKey), i);\n            parsed.push(parseValue(helpers_segment.resolveObjectKey(obj, vAxisKey), item, vScale, i));\n        }\n        return parsed;\n    }\n updateRangeFromParsed(range, scale, parsed, stack) {\n        super.updateRangeFromParsed(range, scale, parsed, stack);\n        const custom = parsed._custom;\n        if (custom && scale === this._cachedMeta.vScale) {\n            range.min = Math.min(range.min, custom.min);\n            range.max = Math.max(range.max, custom.max);\n        }\n    }\n getMaxOverflow() {\n        return 0;\n    }\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const { iScale , vScale  } = meta;\n        const parsed = this.getParsed(index);\n        const custom = parsed._custom;\n        const value = isFloatBar(custom) ? '[' + custom.start + ', ' + custom.end + ']' : '' + vScale.getLabelForValue(parsed[vScale.axis]);\n        return {\n            label: '' + iScale.getLabelForValue(parsed[iScale.axis]),\n            value\n        };\n    }\n    initialize() {\n        this.enableOptionSharing = true;\n        super.initialize();\n        const meta = this._cachedMeta;\n        meta.stack = this.getDataset().stack;\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        this.updateElements(meta.data, 0, meta.data.length, mode);\n    }\n    updateElements(bars, start, count, mode) {\n        const reset = mode === 'reset';\n        const { index , _cachedMeta: { vScale  }  } = this;\n        const base = vScale.getBasePixel();\n        const horizontal = vScale.isHorizontal();\n        const ruler = this._getRuler();\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        for(let i = start; i < start + count; i++){\n            const parsed = this.getParsed(i);\n            const vpixels = reset || helpers_segment.isNullOrUndef(parsed[vScale.axis]) ? {\n                base,\n                head: base\n            } : this._calculateBarValuePixels(i);\n            const ipixels = this._calculateBarIndexPixels(i, ruler);\n            const stack = (parsed._stacks || {})[vScale.axis];\n            const properties = {\n                horizontal,\n                base: vpixels.base,\n                enableBorderRadius: !stack || isFloatBar(parsed._custom) || index === stack._top || index === stack._bottom,\n                x: horizontal ? vpixels.head : ipixels.center,\n                y: horizontal ? ipixels.center : vpixels.head,\n                height: horizontal ? ipixels.size : Math.abs(vpixels.size),\n                width: horizontal ? Math.abs(vpixels.size) : ipixels.size\n            };\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);\n            }\n            const options = properties.options || bars[i].options;\n            setBorderSkipped(properties, options, stack, index);\n            setInflateAmount(properties, options, ruler.ratio);\n            this.updateElement(bars[i], i, properties, mode);\n        }\n    }\n _getStacks(last, dataIndex) {\n        const { iScale  } = this._cachedMeta;\n        const metasets = iScale.getMatchingVisibleMetas(this._type).filter((meta)=>meta.controller.options.grouped);\n        const stacked = iScale.options.stacked;\n        const stacks = [];\n        const skipNull = (meta)=>{\n            const parsed = meta.controller.getParsed(dataIndex);\n            const val = parsed && parsed[meta.vScale.axis];\n            if (helpers_segment.isNullOrUndef(val) || isNaN(val)) {\n                return true;\n            }\n        };\n        for (const meta of metasets){\n            if (dataIndex !== undefined && skipNull(meta)) {\n                continue;\n            }\n            if (stacked === false || stacks.indexOf(meta.stack) === -1 || stacked === undefined && meta.stack === undefined) {\n                stacks.push(meta.stack);\n            }\n            if (meta.index === last) {\n                break;\n            }\n        }\n        if (!stacks.length) {\n            stacks.push(undefined);\n        }\n        return stacks;\n    }\n _getStackCount(index) {\n        return this._getStacks(undefined, index).length;\n    }\n _getStackIndex(datasetIndex, name, dataIndex) {\n        const stacks = this._getStacks(datasetIndex, dataIndex);\n        const index = name !== undefined ? stacks.indexOf(name) : -1;\n        return index === -1 ? stacks.length - 1 : index;\n    }\n _getRuler() {\n        const opts = this.options;\n        const meta = this._cachedMeta;\n        const iScale = meta.iScale;\n        const pixels = [];\n        let i, ilen;\n        for(i = 0, ilen = meta.data.length; i < ilen; ++i){\n            pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i));\n        }\n        const barThickness = opts.barThickness;\n        const min = barThickness || computeMinSampleSize(meta);\n        return {\n            min,\n            pixels,\n            start: iScale._startPixel,\n            end: iScale._endPixel,\n            stackCount: this._getStackCount(),\n            scale: iScale,\n            grouped: opts.grouped,\n            ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage\n        };\n    }\n _calculateBarValuePixels(index) {\n        const { _cachedMeta: { vScale , _stacked , index: datasetIndex  } , options: { base: baseValue , minBarLength  }  } = this;\n        const actualBase = baseValue || 0;\n        const parsed = this.getParsed(index);\n        const custom = parsed._custom;\n        const floating = isFloatBar(custom);\n        let value = parsed[vScale.axis];\n        let start = 0;\n        let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value;\n        let head, size;\n        if (length !== value) {\n            start = length - value;\n            length = value;\n        }\n        if (floating) {\n            value = custom.barStart;\n            length = custom.barEnd - custom.barStart;\n            if (value !== 0 && helpers_segment.sign(value) !== helpers_segment.sign(custom.barEnd)) {\n                start = 0;\n            }\n            start += value;\n        }\n        const startValue = !helpers_segment.isNullOrUndef(baseValue) && !floating ? baseValue : start;\n        let base = vScale.getPixelForValue(startValue);\n        if (this.chart.getDataVisibility(index)) {\n            head = vScale.getPixelForValue(start + length);\n        } else {\n            head = base;\n        }\n        size = head - base;\n        if (Math.abs(size) < minBarLength) {\n            size = barSign(size, vScale, actualBase) * minBarLength;\n            if (value === actualBase) {\n                base -= size / 2;\n            }\n            const startPixel = vScale.getPixelForDecimal(0);\n            const endPixel = vScale.getPixelForDecimal(1);\n            const min = Math.min(startPixel, endPixel);\n            const max = Math.max(startPixel, endPixel);\n            base = Math.max(Math.min(base, max), min);\n            head = base + size;\n            if (_stacked && !floating) {\n                parsed._stacks[vScale.axis]._visualValues[datasetIndex] = vScale.getValueForPixel(head) - vScale.getValueForPixel(base);\n            }\n        }\n        if (base === vScale.getPixelForValue(actualBase)) {\n            const halfGrid = helpers_segment.sign(size) * vScale.getLineWidthForValue(actualBase) / 2;\n            base += halfGrid;\n            size -= halfGrid;\n        }\n        return {\n            size,\n            base,\n            head,\n            center: head + size / 2\n        };\n    }\n _calculateBarIndexPixels(index, ruler) {\n        const scale = ruler.scale;\n        const options = this.options;\n        const skipNull = options.skipNull;\n        const maxBarThickness = helpers_segment.valueOrDefault(options.maxBarThickness, Infinity);\n        let center, size;\n        if (ruler.grouped) {\n            const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;\n            const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount) : computeFitCategoryTraits(index, ruler, options, stackCount);\n            const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);\n            center = range.start + range.chunk * stackIndex + range.chunk / 2;\n            size = Math.min(maxBarThickness, range.chunk * range.ratio);\n        } else {\n            center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index);\n            size = Math.min(maxBarThickness, ruler.min * ruler.ratio);\n        }\n        return {\n            base: center - size / 2,\n            head: center + size / 2,\n            center,\n            size\n        };\n    }\n    draw() {\n        const meta = this._cachedMeta;\n        const vScale = meta.vScale;\n        const rects = meta.data;\n        const ilen = rects.length;\n        let i = 0;\n        for(; i < ilen; ++i){\n            if (this.getParsed(i)[vScale.axis] !== null) {\n                rects[i].draw(this._ctx);\n            }\n        }\n    }\n}\n\nclass BubbleController extends DatasetController {\n    static id = 'bubble';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'point',\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'borderWidth',\n                    'radius'\n                ]\n            }\n        }\n    };\n static overrides = {\n        scales: {\n            x: {\n                type: 'linear'\n            },\n            y: {\n                type: 'linear'\n            }\n        }\n    };\n    initialize() {\n        this.enableOptionSharing = true;\n        super.initialize();\n    }\n parsePrimitiveData(meta, data, start, count) {\n        const parsed = super.parsePrimitiveData(meta, data, start, count);\n        for(let i = 0; i < parsed.length; i++){\n            parsed[i]._custom = this.resolveDataElementOptions(i + start).radius;\n        }\n        return parsed;\n    }\n parseArrayData(meta, data, start, count) {\n        const parsed = super.parseArrayData(meta, data, start, count);\n        for(let i = 0; i < parsed.length; i++){\n            const item = data[start + i];\n            parsed[i]._custom = helpers_segment.valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius);\n        }\n        return parsed;\n    }\n parseObjectData(meta, data, start, count) {\n        const parsed = super.parseObjectData(meta, data, start, count);\n        for(let i = 0; i < parsed.length; i++){\n            const item = data[start + i];\n            parsed[i]._custom = helpers_segment.valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius);\n        }\n        return parsed;\n    }\n getMaxOverflow() {\n        const data = this._cachedMeta.data;\n        let max = 0;\n        for(let i = data.length - 1; i >= 0; --i){\n            max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);\n        }\n        return max > 0 && max;\n    }\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const labels = this.chart.data.labels || [];\n        const { xScale , yScale  } = meta;\n        const parsed = this.getParsed(index);\n        const x = xScale.getLabelForValue(parsed.x);\n        const y = yScale.getLabelForValue(parsed.y);\n        const r = parsed._custom;\n        return {\n            label: labels[index] || '',\n            value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')'\n        };\n    }\n    update(mode) {\n        const points = this._cachedMeta.data;\n        this.updateElements(points, 0, points.length, mode);\n    }\n    updateElements(points, start, count, mode) {\n        const reset = mode === 'reset';\n        const { iScale , vScale  } = this._cachedMeta;\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        for(let i = start; i < start + count; i++){\n            const point = points[i];\n            const parsed = !reset && this.getParsed(i);\n            const properties = {};\n            const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);\n            const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);\n            properties.skip = isNaN(iPixel) || isNaN(vPixel);\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n                if (reset) {\n                    properties.options.radius = 0;\n                }\n            }\n            this.updateElement(point, i, properties, mode);\n        }\n    }\n resolveDataElementOptions(index, mode) {\n        const parsed = this.getParsed(index);\n        let values = super.resolveDataElementOptions(index, mode);\n        if (values.$shared) {\n            values = Object.assign({}, values, {\n                $shared: false\n            });\n        }\n        const radius = values.radius;\n        if (mode !== 'active') {\n            values.radius = 0;\n        }\n        values.radius += helpers_segment.valueOrDefault(parsed && parsed._custom, radius);\n        return values;\n    }\n}\n\nfunction getRatioAndOffset(rotation, circumference, cutout) {\n    let ratioX = 1;\n    let ratioY = 1;\n    let offsetX = 0;\n    let offsetY = 0;\n    if (circumference < helpers_segment.TAU) {\n        const startAngle = rotation;\n        const endAngle = startAngle + circumference;\n        const startX = Math.cos(startAngle);\n        const startY = Math.sin(startAngle);\n        const endX = Math.cos(endAngle);\n        const endY = Math.sin(endAngle);\n        const calcMax = (angle, a, b)=>helpers_segment._angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout);\n        const calcMin = (angle, a, b)=>helpers_segment._angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout);\n        const maxX = calcMax(0, startX, endX);\n        const maxY = calcMax(helpers_segment.HALF_PI, startY, endY);\n        const minX = calcMin(helpers_segment.PI, startX, endX);\n        const minY = calcMin(helpers_segment.PI + helpers_segment.HALF_PI, startY, endY);\n        ratioX = (maxX - minX) / 2;\n        ratioY = (maxY - minY) / 2;\n        offsetX = -(maxX + minX) / 2;\n        offsetY = -(maxY + minY) / 2;\n    }\n    return {\n        ratioX,\n        ratioY,\n        offsetX,\n        offsetY\n    };\n}\nclass DoughnutController extends DatasetController {\n    static id = 'doughnut';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'arc',\n        animation: {\n            animateRotate: true,\n            animateScale: false\n        },\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'circumference',\n                    'endAngle',\n                    'innerRadius',\n                    'outerRadius',\n                    'startAngle',\n                    'x',\n                    'y',\n                    'offset',\n                    'borderWidth',\n                    'spacing'\n                ]\n            }\n        },\n        cutout: '50%',\n        rotation: 0,\n        circumference: 360,\n        radius: '100%',\n        spacing: 0,\n        indexAxis: 'r'\n    };\n    static descriptors = {\n        _scriptable: (name)=>name !== 'spacing',\n        _indexable: (name)=>name !== 'spacing'\n    };\n static overrides = {\n        aspectRatio: 1,\n        plugins: {\n            legend: {\n                labels: {\n                    generateLabels (chart) {\n                        const data = chart.data;\n                        if (data.labels.length && data.datasets.length) {\n                            const { labels: { pointStyle , color  }  } = chart.legend.options;\n                            return data.labels.map((label, i)=>{\n                                const meta = chart.getDatasetMeta(0);\n                                const style = meta.controller.getStyle(i);\n                                return {\n                                    text: label,\n                                    fillStyle: style.backgroundColor,\n                                    strokeStyle: style.borderColor,\n                                    fontColor: color,\n                                    lineWidth: style.borderWidth,\n                                    pointStyle: pointStyle,\n                                    hidden: !chart.getDataVisibility(i),\n                                    index: i\n                                };\n                            });\n                        }\n                        return [];\n                    }\n                },\n                onClick (e, legendItem, legend) {\n                    legend.chart.toggleDataVisibility(legendItem.index);\n                    legend.chart.update();\n                }\n            }\n        }\n    };\n    constructor(chart, datasetIndex){\n        super(chart, datasetIndex);\n        this.enableOptionSharing = true;\n        this.innerRadius = undefined;\n        this.outerRadius = undefined;\n        this.offsetX = undefined;\n        this.offsetY = undefined;\n    }\n    linkScales() {}\n parse(start, count) {\n        const data = this.getDataset().data;\n        const meta = this._cachedMeta;\n        if (this._parsing === false) {\n            meta._parsed = data;\n        } else {\n            let getter = (i)=>+data[i];\n            if (helpers_segment.isObject(data[start])) {\n                const { key ='value'  } = this._parsing;\n                getter = (i)=>+helpers_segment.resolveObjectKey(data[i], key);\n            }\n            let i, ilen;\n            for(i = start, ilen = start + count; i < ilen; ++i){\n                meta._parsed[i] = getter(i);\n            }\n        }\n    }\n _getRotation() {\n        return helpers_segment.toRadians(this.options.rotation - 90);\n    }\n _getCircumference() {\n        return helpers_segment.toRadians(this.options.circumference);\n    }\n _getRotationExtents() {\n        let min = helpers_segment.TAU;\n        let max = -helpers_segment.TAU;\n        for(let i = 0; i < this.chart.data.datasets.length; ++i){\n            if (this.chart.isDatasetVisible(i) && this.chart.getDatasetMeta(i).type === this._type) {\n                const controller = this.chart.getDatasetMeta(i).controller;\n                const rotation = controller._getRotation();\n                const circumference = controller._getCircumference();\n                min = Math.min(min, rotation);\n                max = Math.max(max, rotation + circumference);\n            }\n        }\n        return {\n            rotation: min,\n            circumference: max - min\n        };\n    }\n update(mode) {\n        const chart = this.chart;\n        const { chartArea  } = chart;\n        const meta = this._cachedMeta;\n        const arcs = meta.data;\n        const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing;\n        const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);\n        const cutout = Math.min(helpers_segment.toPercentage(this.options.cutout, maxSize), 1);\n        const chartWeight = this._getRingWeight(this.index);\n        const { circumference , rotation  } = this._getRotationExtents();\n        const { ratioX , ratioY , offsetX , offsetY  } = getRatioAndOffset(rotation, circumference, cutout);\n        const maxWidth = (chartArea.width - spacing) / ratioX;\n        const maxHeight = (chartArea.height - spacing) / ratioY;\n        const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);\n        const outerRadius = helpers_segment.toDimension(this.options.radius, maxRadius);\n        const innerRadius = Math.max(outerRadius * cutout, 0);\n        const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal();\n        this.offsetX = offsetX * outerRadius;\n        this.offsetY = offsetY * outerRadius;\n        meta.total = this.calculateTotal();\n        this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);\n        this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);\n        this.updateElements(arcs, 0, arcs.length, mode);\n    }\n _circumference(i, reset) {\n        const opts = this.options;\n        const meta = this._cachedMeta;\n        const circumference = this._getCircumference();\n        if (reset && opts.animation.animateRotate || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {\n            return 0;\n        }\n        return this.calculateCircumference(meta._parsed[i] * circumference / helpers_segment.TAU);\n    }\n    updateElements(arcs, start, count, mode) {\n        const reset = mode === 'reset';\n        const chart = this.chart;\n        const chartArea = chart.chartArea;\n        const opts = chart.options;\n        const animationOpts = opts.animation;\n        const centerX = (chartArea.left + chartArea.right) / 2;\n        const centerY = (chartArea.top + chartArea.bottom) / 2;\n        const animateScale = reset && animationOpts.animateScale;\n        const innerRadius = animateScale ? 0 : this.innerRadius;\n        const outerRadius = animateScale ? 0 : this.outerRadius;\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        let startAngle = this._getRotation();\n        let i;\n        for(i = 0; i < start; ++i){\n            startAngle += this._circumference(i, reset);\n        }\n        for(i = start; i < start + count; ++i){\n            const circumference = this._circumference(i, reset);\n            const arc = arcs[i];\n            const properties = {\n                x: centerX + this.offsetX,\n                y: centerY + this.offsetY,\n                startAngle,\n                endAngle: startAngle + circumference,\n                circumference,\n                outerRadius,\n                innerRadius\n            };\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode);\n            }\n            startAngle += circumference;\n            this.updateElement(arc, i, properties, mode);\n        }\n    }\n    calculateTotal() {\n        const meta = this._cachedMeta;\n        const metaData = meta.data;\n        let total = 0;\n        let i;\n        for(i = 0; i < metaData.length; i++){\n            const value = meta._parsed[i];\n            if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) {\n                total += Math.abs(value);\n            }\n        }\n        return total;\n    }\n    calculateCircumference(value) {\n        const total = this._cachedMeta.total;\n        if (total > 0 && !isNaN(value)) {\n            return helpers_segment.TAU * (Math.abs(value) / total);\n        }\n        return 0;\n    }\n    getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const chart = this.chart;\n        const labels = chart.data.labels || [];\n        const value = helpers_segment.formatNumber(meta._parsed[index], chart.options.locale);\n        return {\n            label: labels[index] || '',\n            value\n        };\n    }\n    getMaxBorderWidth(arcs) {\n        let max = 0;\n        const chart = this.chart;\n        let i, ilen, meta, controller, options;\n        if (!arcs) {\n            for(i = 0, ilen = chart.data.datasets.length; i < ilen; ++i){\n                if (chart.isDatasetVisible(i)) {\n                    meta = chart.getDatasetMeta(i);\n                    arcs = meta.data;\n                    controller = meta.controller;\n                    break;\n                }\n            }\n        }\n        if (!arcs) {\n            return 0;\n        }\n        for(i = 0, ilen = arcs.length; i < ilen; ++i){\n            options = controller.resolveDataElementOptions(i);\n            if (options.borderAlign !== 'inner') {\n                max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0);\n            }\n        }\n        return max;\n    }\n    getMaxOffset(arcs) {\n        let max = 0;\n        for(let i = 0, ilen = arcs.length; i < ilen; ++i){\n            const options = this.resolveDataElementOptions(i);\n            max = Math.max(max, options.offset || 0, options.hoverOffset || 0);\n        }\n        return max;\n    }\n _getRingWeightOffset(datasetIndex) {\n        let ringWeightOffset = 0;\n        for(let i = 0; i < datasetIndex; ++i){\n            if (this.chart.isDatasetVisible(i)) {\n                ringWeightOffset += this._getRingWeight(i);\n            }\n        }\n        return ringWeightOffset;\n    }\n _getRingWeight(datasetIndex) {\n        return Math.max(helpers_segment.valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0);\n    }\n _getVisibleDatasetWeightTotal() {\n        return this._getRingWeightOffset(this.chart.data.datasets.length) || 1;\n    }\n}\n\nclass LineController extends DatasetController {\n    static id = 'line';\n static defaults = {\n        datasetElementType: 'line',\n        dataElementType: 'point',\n        showLine: true,\n        spanGaps: false\n    };\n static overrides = {\n        scales: {\n            _index_: {\n                type: 'category'\n            },\n            _value_: {\n                type: 'linear'\n            }\n        }\n    };\n    initialize() {\n        this.enableOptionSharing = true;\n        this.supportsDecimation = true;\n        super.initialize();\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        const { dataset: line , data: points = [] , _dataset  } = meta;\n        const animationsDisabled = this.chart._animationsDisabled;\n        let { start , count  } = helpers_segment._getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);\n        this._drawStart = start;\n        this._drawCount = count;\n        if (helpers_segment._scaleRangesChanged(meta)) {\n            start = 0;\n            count = points.length;\n        }\n        line._chart = this.chart;\n        line._datasetIndex = this.index;\n        line._decimated = !!_dataset._decimated;\n        line.points = points;\n        const options = this.resolveDatasetElementOptions(mode);\n        if (!this.options.showLine) {\n            options.borderWidth = 0;\n        }\n        options.segment = this.options.segment;\n        this.updateElement(line, undefined, {\n            animated: !animationsDisabled,\n            options\n        }, mode);\n        this.updateElements(points, start, count, mode);\n    }\n    updateElements(points, start, count, mode) {\n        const reset = mode === 'reset';\n        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        const { spanGaps , segment  } = this.options;\n        const maxGapLength = helpers_segment.isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;\n        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';\n        const end = start + count;\n        const pointsCount = points.length;\n        let prevParsed = start > 0 && this.getParsed(start - 1);\n        for(let i = 0; i < pointsCount; ++i){\n            const point = points[i];\n            const properties = directUpdate ? point : {};\n            if (i < start || i >= end) {\n                properties.skip = true;\n                continue;\n            }\n            const parsed = this.getParsed(i);\n            const nullData = helpers_segment.isNullOrUndef(parsed[vAxis]);\n            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);\n            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);\n            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;\n            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;\n            if (segment) {\n                properties.parsed = parsed;\n                properties.raw = _dataset.data[i];\n            }\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n            }\n            if (!directUpdate) {\n                this.updateElement(point, i, properties, mode);\n            }\n            prevParsed = parsed;\n        }\n    }\n getMaxOverflow() {\n        const meta = this._cachedMeta;\n        const dataset = meta.dataset;\n        const border = dataset.options && dataset.options.borderWidth || 0;\n        const data = meta.data || [];\n        if (!data.length) {\n            return border;\n        }\n        const firstPoint = data[0].size(this.resolveDataElementOptions(0));\n        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));\n        return Math.max(border, firstPoint, lastPoint) / 2;\n    }\n    draw() {\n        const meta = this._cachedMeta;\n        meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis);\n        super.draw();\n    }\n}\n\nclass PolarAreaController extends DatasetController {\n    static id = 'polarArea';\n static defaults = {\n        dataElementType: 'arc',\n        animation: {\n            animateRotate: true,\n            animateScale: true\n        },\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'startAngle',\n                    'endAngle',\n                    'innerRadius',\n                    'outerRadius'\n                ]\n            }\n        },\n        indexAxis: 'r',\n        startAngle: 0\n    };\n static overrides = {\n        aspectRatio: 1,\n        plugins: {\n            legend: {\n                labels: {\n                    generateLabels (chart) {\n                        const data = chart.data;\n                        if (data.labels.length && data.datasets.length) {\n                            const { labels: { pointStyle , color  }  } = chart.legend.options;\n                            return data.labels.map((label, i)=>{\n                                const meta = chart.getDatasetMeta(0);\n                                const style = meta.controller.getStyle(i);\n                                return {\n                                    text: label,\n                                    fillStyle: style.backgroundColor,\n                                    strokeStyle: style.borderColor,\n                                    fontColor: color,\n                                    lineWidth: style.borderWidth,\n                                    pointStyle: pointStyle,\n                                    hidden: !chart.getDataVisibility(i),\n                                    index: i\n                                };\n                            });\n                        }\n                        return [];\n                    }\n                },\n                onClick (e, legendItem, legend) {\n                    legend.chart.toggleDataVisibility(legendItem.index);\n                    legend.chart.update();\n                }\n            }\n        },\n        scales: {\n            r: {\n                type: 'radialLinear',\n                angleLines: {\n                    display: false\n                },\n                beginAtZero: true,\n                grid: {\n                    circular: true\n                },\n                pointLabels: {\n                    display: false\n                },\n                startAngle: 0\n            }\n        }\n    };\n    constructor(chart, datasetIndex){\n        super(chart, datasetIndex);\n        this.innerRadius = undefined;\n        this.outerRadius = undefined;\n    }\n    getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const chart = this.chart;\n        const labels = chart.data.labels || [];\n        const value = helpers_segment.formatNumber(meta._parsed[index].r, chart.options.locale);\n        return {\n            label: labels[index] || '',\n            value\n        };\n    }\n    parseObjectData(meta, data, start, count) {\n        return helpers_segment._parseObjectDataRadialScale.bind(this)(meta, data, start, count);\n    }\n    update(mode) {\n        const arcs = this._cachedMeta.data;\n        this._updateRadius();\n        this.updateElements(arcs, 0, arcs.length, mode);\n    }\n getMinMax() {\n        const meta = this._cachedMeta;\n        const range = {\n            min: Number.POSITIVE_INFINITY,\n            max: Number.NEGATIVE_INFINITY\n        };\n        meta.data.forEach((element, index)=>{\n            const parsed = this.getParsed(index).r;\n            if (!isNaN(parsed) && this.chart.getDataVisibility(index)) {\n                if (parsed < range.min) {\n                    range.min = parsed;\n                }\n                if (parsed > range.max) {\n                    range.max = parsed;\n                }\n            }\n        });\n        return range;\n    }\n _updateRadius() {\n        const chart = this.chart;\n        const chartArea = chart.chartArea;\n        const opts = chart.options;\n        const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);\n        const outerRadius = Math.max(minSize / 2, 0);\n        const innerRadius = Math.max(opts.cutoutPercentage ? outerRadius / 100 * opts.cutoutPercentage : 1, 0);\n        const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();\n        this.outerRadius = outerRadius - radiusLength * this.index;\n        this.innerRadius = this.outerRadius - radiusLength;\n    }\n    updateElements(arcs, start, count, mode) {\n        const reset = mode === 'reset';\n        const chart = this.chart;\n        const opts = chart.options;\n        const animationOpts = opts.animation;\n        const scale = this._cachedMeta.rScale;\n        const centerX = scale.xCenter;\n        const centerY = scale.yCenter;\n        const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * helpers_segment.PI;\n        let angle = datasetStartAngle;\n        let i;\n        const defaultAngle = 360 / this.countVisibleElements();\n        for(i = 0; i < start; ++i){\n            angle += this._computeAngle(i, mode, defaultAngle);\n        }\n        for(i = start; i < start + count; i++){\n            const arc = arcs[i];\n            let startAngle = angle;\n            let endAngle = angle + this._computeAngle(i, mode, defaultAngle);\n            let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(this.getParsed(i).r) : 0;\n            angle = endAngle;\n            if (reset) {\n                if (animationOpts.animateScale) {\n                    outerRadius = 0;\n                }\n                if (animationOpts.animateRotate) {\n                    startAngle = endAngle = datasetStartAngle;\n                }\n            }\n            const properties = {\n                x: centerX,\n                y: centerY,\n                innerRadius: 0,\n                outerRadius,\n                startAngle,\n                endAngle,\n                options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode)\n            };\n            this.updateElement(arc, i, properties, mode);\n        }\n    }\n    countVisibleElements() {\n        const meta = this._cachedMeta;\n        let count = 0;\n        meta.data.forEach((element, index)=>{\n            if (!isNaN(this.getParsed(index).r) && this.chart.getDataVisibility(index)) {\n                count++;\n            }\n        });\n        return count;\n    }\n _computeAngle(index, mode, defaultAngle) {\n        return this.chart.getDataVisibility(index) ? helpers_segment.toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) : 0;\n    }\n}\n\nclass PieController extends DoughnutController {\n    static id = 'pie';\n static defaults = {\n        cutout: 0,\n        rotation: 0,\n        circumference: 360,\n        radius: '100%'\n    };\n}\n\nclass RadarController extends DatasetController {\n    static id = 'radar';\n static defaults = {\n        datasetElementType: 'line',\n        dataElementType: 'point',\n        indexAxis: 'r',\n        showLine: true,\n        elements: {\n            line: {\n                fill: 'start'\n            }\n        }\n    };\n static overrides = {\n        aspectRatio: 1,\n        scales: {\n            r: {\n                type: 'radialLinear'\n            }\n        }\n    };\n getLabelAndValue(index) {\n        const vScale = this._cachedMeta.vScale;\n        const parsed = this.getParsed(index);\n        return {\n            label: vScale.getLabels()[index],\n            value: '' + vScale.getLabelForValue(parsed[vScale.axis])\n        };\n    }\n    parseObjectData(meta, data, start, count) {\n        return helpers_segment._parseObjectDataRadialScale.bind(this)(meta, data, start, count);\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        const line = meta.dataset;\n        const points = meta.data || [];\n        const labels = meta.iScale.getLabels();\n        line.points = points;\n        if (mode !== 'resize') {\n            const options = this.resolveDatasetElementOptions(mode);\n            if (!this.options.showLine) {\n                options.borderWidth = 0;\n            }\n            const properties = {\n                _loop: true,\n                _fullLoop: labels.length === points.length,\n                options\n            };\n            this.updateElement(line, undefined, properties, mode);\n        }\n        this.updateElements(points, 0, points.length, mode);\n    }\n    updateElements(points, start, count, mode) {\n        const scale = this._cachedMeta.rScale;\n        const reset = mode === 'reset';\n        for(let i = start; i < start + count; i++){\n            const point = points[i];\n            const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n            const pointPosition = scale.getPointPositionForValue(i, this.getParsed(i).r);\n            const x = reset ? scale.xCenter : pointPosition.x;\n            const y = reset ? scale.yCenter : pointPosition.y;\n            const properties = {\n                x,\n                y,\n                angle: pointPosition.angle,\n                skip: isNaN(x) || isNaN(y),\n                options\n            };\n            this.updateElement(point, i, properties, mode);\n        }\n    }\n}\n\nclass ScatterController extends DatasetController {\n    static id = 'scatter';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'point',\n        showLine: false,\n        fill: false\n    };\n static overrides = {\n        interaction: {\n            mode: 'point'\n        },\n        scales: {\n            x: {\n                type: 'linear'\n            },\n            y: {\n                type: 'linear'\n            }\n        }\n    };\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const labels = this.chart.data.labels || [];\n        const { xScale , yScale  } = meta;\n        const parsed = this.getParsed(index);\n        const x = xScale.getLabelForValue(parsed.x);\n        const y = yScale.getLabelForValue(parsed.y);\n        return {\n            label: labels[index] || '',\n            value: '(' + x + ', ' + y + ')'\n        };\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        const { data: points = []  } = meta;\n        const animationsDisabled = this.chart._animationsDisabled;\n        let { start , count  } = helpers_segment._getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);\n        this._drawStart = start;\n        this._drawCount = count;\n        if (helpers_segment._scaleRangesChanged(meta)) {\n            start = 0;\n            count = points.length;\n        }\n        if (this.options.showLine) {\n            const { dataset: line , _dataset  } = meta;\n            line._chart = this.chart;\n            line._datasetIndex = this.index;\n            line._decimated = !!_dataset._decimated;\n            line.points = points;\n            const options = this.resolveDatasetElementOptions(mode);\n            options.segment = this.options.segment;\n            this.updateElement(line, undefined, {\n                animated: !animationsDisabled,\n                options\n            }, mode);\n        }\n        this.updateElements(points, start, count, mode);\n    }\n    addElements() {\n        const { showLine  } = this.options;\n        if (!this.datasetElementType && showLine) {\n            this.datasetElementType = this.chart.registry.getElement('line');\n        }\n        super.addElements();\n    }\n    updateElements(points, start, count, mode) {\n        const reset = mode === 'reset';\n        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;\n        const firstOpts = this.resolveDataElementOptions(start, mode);\n        const sharedOptions = this.getSharedOptions(firstOpts);\n        const includeOptions = this.includeOptions(mode, sharedOptions);\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        const { spanGaps , segment  } = this.options;\n        const maxGapLength = helpers_segment.isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;\n        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';\n        let prevParsed = start > 0 && this.getParsed(start - 1);\n        for(let i = start; i < start + count; ++i){\n            const point = points[i];\n            const parsed = this.getParsed(i);\n            const properties = directUpdate ? point : {};\n            const nullData = helpers_segment.isNullOrUndef(parsed[vAxis]);\n            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);\n            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);\n            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;\n            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;\n            if (segment) {\n                properties.parsed = parsed;\n                properties.raw = _dataset.data[i];\n            }\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n            }\n            if (!directUpdate) {\n                this.updateElement(point, i, properties, mode);\n            }\n            prevParsed = parsed;\n        }\n        this.updateSharedOptions(sharedOptions, mode, firstOpts);\n    }\n getMaxOverflow() {\n        const meta = this._cachedMeta;\n        const data = meta.data || [];\n        if (!this.options.showLine) {\n            let max = 0;\n            for(let i = data.length - 1; i >= 0; --i){\n                max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);\n            }\n            return max > 0 && max;\n        }\n        const dataset = meta.dataset;\n        const border = dataset.options && dataset.options.borderWidth || 0;\n        if (!data.length) {\n            return border;\n        }\n        const firstPoint = data[0].size(this.resolveDataElementOptions(0));\n        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));\n        return Math.max(border, firstPoint, lastPoint) / 2;\n    }\n}\n\nvar controllers = /*#__PURE__*/Object.freeze({\n__proto__: null,\nBarController: BarController,\nBubbleController: BubbleController,\nDoughnutController: DoughnutController,\nLineController: LineController,\nPolarAreaController: PolarAreaController,\nPieController: PieController,\nRadarController: RadarController,\nScatterController: ScatterController\n});\n\n/**\n * @namespace Chart._adapters\n * @since 2.8.0\n * @private\n */ function abstract() {\n    throw new Error('This method is not implemented: Check that a complete date adapter is provided.');\n}\n/**\n * Date adapter (current used by the time scale)\n * @namespace Chart._adapters._date\n * @memberof Chart._adapters\n * @private\n */ class DateAdapterBase {\n    /**\n   * Override default date adapter methods.\n   * Accepts type parameter to define options type.\n   * @example\n   * Chart._adapters._date.override<{myAdapterOption: string}>({\n   *   init() {\n   *     console.log(this.options.myAdapterOption);\n   *   }\n   * })\n   */ static override(members) {\n        Object.assign(DateAdapterBase.prototype, members);\n    }\n    constructor(options){\n        this.options = options || {};\n    }\n    // eslint-disable-next-line @typescript-eslint/no-empty-function\n    init() {}\n    formats() {\n        return abstract();\n    }\n    parse() {\n        return abstract();\n    }\n    format() {\n        return abstract();\n    }\n    add() {\n        return abstract();\n    }\n    diff() {\n        return abstract();\n    }\n    startOf() {\n        return abstract();\n    }\n    endOf() {\n        return abstract();\n    }\n}\nvar adapters = {\n    _date: DateAdapterBase\n};\n\nfunction binarySearch(metaset, axis, value, intersect) {\n    const { controller , data , _sorted  } = metaset;\n    const iScale = controller._cachedMeta.iScale;\n    if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) {\n        const lookupMethod = iScale._reversePixels ? helpers_segment._rlookupByKey : helpers_segment._lookupByKey;\n        if (!intersect) {\n            return lookupMethod(data, axis, value);\n        } else if (controller._sharedOptions) {\n            const el = data[0];\n            const range = typeof el.getRange === 'function' && el.getRange(axis);\n            if (range) {\n                const start = lookupMethod(data, axis, value - range);\n                const end = lookupMethod(data, axis, value + range);\n                return {\n                    lo: start.lo,\n                    hi: end.hi\n                };\n            }\n        }\n    }\n    return {\n        lo: 0,\n        hi: data.length - 1\n    };\n}\n function evaluateInteractionItems(chart, axis, position, handler, intersect) {\n    const metasets = chart.getSortedVisibleDatasetMetas();\n    const value = position[axis];\n    for(let i = 0, ilen = metasets.length; i < ilen; ++i){\n        const { index , data  } = metasets[i];\n        const { lo , hi  } = binarySearch(metasets[i], axis, value, intersect);\n        for(let j = lo; j <= hi; ++j){\n            const element = data[j];\n            if (!element.skip) {\n                handler(element, index, j);\n            }\n        }\n    }\n}\n function getDistanceMetricForAxis(axis) {\n    const useX = axis.indexOf('x') !== -1;\n    const useY = axis.indexOf('y') !== -1;\n    return function(pt1, pt2) {\n        const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;\n        const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;\n        return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));\n    };\n}\n function getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) {\n    const items = [];\n    if (!includeInvisible && !chart.isPointInArea(position)) {\n        return items;\n    }\n    const evaluationFunc = function(element, datasetIndex, index) {\n        if (!includeInvisible && !helpers_segment._isPointInArea(element, chart.chartArea, 0)) {\n            return;\n        }\n        if (element.inRange(position.x, position.y, useFinalPosition)) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n        }\n    };\n    evaluateInteractionItems(chart, axis, position, evaluationFunc, true);\n    return items;\n}\n function getNearestRadialItems(chart, position, axis, useFinalPosition) {\n    let items = [];\n    function evaluationFunc(element, datasetIndex, index) {\n        const { startAngle , endAngle  } = element.getProps([\n            'startAngle',\n            'endAngle'\n        ], useFinalPosition);\n        const { angle  } = helpers_segment.getAngleFromPoint(element, {\n            x: position.x,\n            y: position.y\n        });\n        if (helpers_segment._angleBetween(angle, startAngle, endAngle)) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n        }\n    }\n    evaluateInteractionItems(chart, axis, position, evaluationFunc);\n    return items;\n}\n function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {\n    let items = [];\n    const distanceMetric = getDistanceMetricForAxis(axis);\n    let minDistance = Number.POSITIVE_INFINITY;\n    function evaluationFunc(element, datasetIndex, index) {\n        const inRange = element.inRange(position.x, position.y, useFinalPosition);\n        if (intersect && !inRange) {\n            return;\n        }\n        const center = element.getCenterPoint(useFinalPosition);\n        const pointInArea = !!includeInvisible || chart.isPointInArea(center);\n        if (!pointInArea && !inRange) {\n            return;\n        }\n        const distance = distanceMetric(position, center);\n        if (distance < minDistance) {\n            items = [\n                {\n                    element,\n                    datasetIndex,\n                    index\n                }\n            ];\n            minDistance = distance;\n        } else if (distance === minDistance) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n        }\n    }\n    evaluateInteractionItems(chart, axis, position, evaluationFunc);\n    return items;\n}\n function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {\n    if (!includeInvisible && !chart.isPointInArea(position)) {\n        return [];\n    }\n    return axis === 'r' && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible);\n}\n function getAxisItems(chart, position, axis, intersect, useFinalPosition) {\n    const items = [];\n    const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange';\n    let intersectsItem = false;\n    evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index)=>{\n        if (element[rangeMethod](position[axis], useFinalPosition)) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n            intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition);\n        }\n    });\n    if (intersect && !intersectsItem) {\n        return [];\n    }\n    return items;\n}\n var Interaction = {\n    evaluateInteractionItems,\n    modes: {\n index (chart, e, options, useFinalPosition) {\n            const position = helpers_segment.getRelativePosition(e, chart);\n            const axis = options.axis || 'x';\n            const includeInvisible = options.includeInvisible || false;\n            const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);\n            const elements = [];\n            if (!items.length) {\n                return [];\n            }\n            chart.getSortedVisibleDatasetMetas().forEach((meta)=>{\n                const index = items[0].index;\n                const element = meta.data[index];\n                if (element && !element.skip) {\n                    elements.push({\n                        element,\n                        datasetIndex: meta.index,\n                        index\n                    });\n                }\n            });\n            return elements;\n        },\n dataset (chart, e, options, useFinalPosition) {\n            const position = helpers_segment.getRelativePosition(e, chart);\n            const axis = options.axis || 'xy';\n            const includeInvisible = options.includeInvisible || false;\n            let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);\n            if (items.length > 0) {\n                const datasetIndex = items[0].datasetIndex;\n                const data = chart.getDatasetMeta(datasetIndex).data;\n                items = [];\n                for(let i = 0; i < data.length; ++i){\n                    items.push({\n                        element: data[i],\n                        datasetIndex,\n                        index: i\n                    });\n                }\n            }\n            return items;\n        },\n point (chart, e, options, useFinalPosition) {\n            const position = helpers_segment.getRelativePosition(e, chart);\n            const axis = options.axis || 'xy';\n            const includeInvisible = options.includeInvisible || false;\n            return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible);\n        },\n nearest (chart, e, options, useFinalPosition) {\n            const position = helpers_segment.getRelativePosition(e, chart);\n            const axis = options.axis || 'xy';\n            const includeInvisible = options.includeInvisible || false;\n            return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible);\n        },\n x (chart, e, options, useFinalPosition) {\n            const position = helpers_segment.getRelativePosition(e, chart);\n            return getAxisItems(chart, position, 'x', options.intersect, useFinalPosition);\n        },\n y (chart, e, options, useFinalPosition) {\n            const position = helpers_segment.getRelativePosition(e, chart);\n            return getAxisItems(chart, position, 'y', options.intersect, useFinalPosition);\n        }\n    }\n};\n\nconst STATIC_POSITIONS = [\n    'left',\n    'top',\n    'right',\n    'bottom'\n];\nfunction filterByPosition(array, position) {\n    return array.filter((v)=>v.pos === position);\n}\nfunction filterDynamicPositionByAxis(array, axis) {\n    return array.filter((v)=>STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);\n}\nfunction sortByWeight(array, reverse) {\n    return array.sort((a, b)=>{\n        const v0 = reverse ? b : a;\n        const v1 = reverse ? a : b;\n        return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight;\n    });\n}\nfunction wrapBoxes(boxes) {\n    const layoutBoxes = [];\n    let i, ilen, box, pos, stack, stackWeight;\n    for(i = 0, ilen = (boxes || []).length; i < ilen; ++i){\n        box = boxes[i];\n        ({ position: pos , options: { stack , stackWeight =1  }  } = box);\n        layoutBoxes.push({\n            index: i,\n            box,\n            pos,\n            horizontal: box.isHorizontal(),\n            weight: box.weight,\n            stack: stack && pos + stack,\n            stackWeight\n        });\n    }\n    return layoutBoxes;\n}\nfunction buildStacks(layouts) {\n    const stacks = {};\n    for (const wrap of layouts){\n        const { stack , pos , stackWeight  } = wrap;\n        if (!stack || !STATIC_POSITIONS.includes(pos)) {\n            continue;\n        }\n        const _stack = stacks[stack] || (stacks[stack] = {\n            count: 0,\n            placed: 0,\n            weight: 0,\n            size: 0\n        });\n        _stack.count++;\n        _stack.weight += stackWeight;\n    }\n    return stacks;\n}\n function setLayoutDims(layouts, params) {\n    const stacks = buildStacks(layouts);\n    const { vBoxMaxWidth , hBoxMaxHeight  } = params;\n    let i, ilen, layout;\n    for(i = 0, ilen = layouts.length; i < ilen; ++i){\n        layout = layouts[i];\n        const { fullSize  } = layout.box;\n        const stack = stacks[layout.stack];\n        const factor = stack && layout.stackWeight / stack.weight;\n        if (layout.horizontal) {\n            layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth;\n            layout.height = hBoxMaxHeight;\n        } else {\n            layout.width = vBoxMaxWidth;\n            layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight;\n        }\n    }\n    return stacks;\n}\nfunction buildLayoutBoxes(boxes) {\n    const layoutBoxes = wrapBoxes(boxes);\n    const fullSize = sortByWeight(layoutBoxes.filter((wrap)=>wrap.box.fullSize), true);\n    const left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);\n    const right = sortByWeight(filterByPosition(layoutBoxes, 'right'));\n    const top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);\n    const bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));\n    const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, 'x');\n    const centerVertical = filterDynamicPositionByAxis(layoutBoxes, 'y');\n    return {\n        fullSize,\n        leftAndTop: left.concat(top),\n        rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),\n        chartArea: filterByPosition(layoutBoxes, 'chartArea'),\n        vertical: left.concat(right).concat(centerVertical),\n        horizontal: top.concat(bottom).concat(centerHorizontal)\n    };\n}\nfunction getCombinedMax(maxPadding, chartArea, a, b) {\n    return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);\n}\nfunction updateMaxPadding(maxPadding, boxPadding) {\n    maxPadding.top = Math.max(maxPadding.top, boxPadding.top);\n    maxPadding.left = Math.max(maxPadding.left, boxPadding.left);\n    maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);\n    maxPadding.right = Math.max(maxPadding.right, boxPadding.right);\n}\nfunction updateDims(chartArea, params, layout, stacks) {\n    const { pos , box  } = layout;\n    const maxPadding = chartArea.maxPadding;\n    if (!helpers_segment.isObject(pos)) {\n        if (layout.size) {\n            chartArea[pos] -= layout.size;\n        }\n        const stack = stacks[layout.stack] || {\n            size: 0,\n            count: 1\n        };\n        stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width);\n        layout.size = stack.size / stack.count;\n        chartArea[pos] += layout.size;\n    }\n    if (box.getPadding) {\n        updateMaxPadding(maxPadding, box.getPadding());\n    }\n    const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'));\n    const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'));\n    const widthChanged = newWidth !== chartArea.w;\n    const heightChanged = newHeight !== chartArea.h;\n    chartArea.w = newWidth;\n    chartArea.h = newHeight;\n    return layout.horizontal ? {\n        same: widthChanged,\n        other: heightChanged\n    } : {\n        same: heightChanged,\n        other: widthChanged\n    };\n}\nfunction handleMaxPadding(chartArea) {\n    const maxPadding = chartArea.maxPadding;\n    function updatePos(pos) {\n        const change = Math.max(maxPadding[pos] - chartArea[pos], 0);\n        chartArea[pos] += change;\n        return change;\n    }\n    chartArea.y += updatePos('top');\n    chartArea.x += updatePos('left');\n    updatePos('right');\n    updatePos('bottom');\n}\nfunction getMargins(horizontal, chartArea) {\n    const maxPadding = chartArea.maxPadding;\n    function marginForPositions(positions) {\n        const margin = {\n            left: 0,\n            top: 0,\n            right: 0,\n            bottom: 0\n        };\n        positions.forEach((pos)=>{\n            margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);\n        });\n        return margin;\n    }\n    return horizontal ? marginForPositions([\n        'left',\n        'right'\n    ]) : marginForPositions([\n        'top',\n        'bottom'\n    ]);\n}\nfunction fitBoxes(boxes, chartArea, params, stacks) {\n    const refitBoxes = [];\n    let i, ilen, layout, box, refit, changed;\n    for(i = 0, ilen = boxes.length, refit = 0; i < ilen; ++i){\n        layout = boxes[i];\n        box = layout.box;\n        box.update(layout.width || chartArea.w, layout.height || chartArea.h, getMargins(layout.horizontal, chartArea));\n        const { same , other  } = updateDims(chartArea, params, layout, stacks);\n        refit |= same && refitBoxes.length;\n        changed = changed || other;\n        if (!box.fullSize) {\n            refitBoxes.push(layout);\n        }\n    }\n    return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed;\n}\nfunction setBoxDims(box, left, top, width, height) {\n    box.top = top;\n    box.left = left;\n    box.right = left + width;\n    box.bottom = top + height;\n    box.width = width;\n    box.height = height;\n}\nfunction placeBoxes(boxes, chartArea, params, stacks) {\n    const userPadding = params.padding;\n    let { x , y  } = chartArea;\n    for (const layout of boxes){\n        const box = layout.box;\n        const stack = stacks[layout.stack] || {\n            count: 1,\n            placed: 0,\n            weight: 1\n        };\n        const weight = layout.stackWeight / stack.weight || 1;\n        if (layout.horizontal) {\n            const width = chartArea.w * weight;\n            const height = stack.size || box.height;\n            if (helpers_segment.defined(stack.start)) {\n                y = stack.start;\n            }\n            if (box.fullSize) {\n                setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height);\n            } else {\n                setBoxDims(box, chartArea.left + stack.placed, y, width, height);\n            }\n            stack.start = y;\n            stack.placed += width;\n            y = box.bottom;\n        } else {\n            const height1 = chartArea.h * weight;\n            const width1 = stack.size || box.width;\n            if (helpers_segment.defined(stack.start)) {\n                x = stack.start;\n            }\n            if (box.fullSize) {\n                setBoxDims(box, x, userPadding.top, width1, params.outerHeight - userPadding.bottom - userPadding.top);\n            } else {\n                setBoxDims(box, x, chartArea.top + stack.placed, width1, height1);\n            }\n            stack.start = x;\n            stack.placed += height1;\n            x = box.right;\n        }\n    }\n    chartArea.x = x;\n    chartArea.y = y;\n}\nvar layouts = {\n addBox (chart, item) {\n        if (!chart.boxes) {\n            chart.boxes = [];\n        }\n        item.fullSize = item.fullSize || false;\n        item.position = item.position || 'top';\n        item.weight = item.weight || 0;\n        item._layers = item._layers || function() {\n            return [\n                {\n                    z: 0,\n                    draw (chartArea) {\n                        item.draw(chartArea);\n                    }\n                }\n            ];\n        };\n        chart.boxes.push(item);\n    },\n removeBox (chart, layoutItem) {\n        const index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;\n        if (index !== -1) {\n            chart.boxes.splice(index, 1);\n        }\n    },\n configure (chart, item, options) {\n        item.fullSize = options.fullSize;\n        item.position = options.position;\n        item.weight = options.weight;\n    },\n update (chart, width, height, minPadding) {\n        if (!chart) {\n            return;\n        }\n        const padding = helpers_segment.toPadding(chart.options.layout.padding);\n        const availableWidth = Math.max(width - padding.width, 0);\n        const availableHeight = Math.max(height - padding.height, 0);\n        const boxes = buildLayoutBoxes(chart.boxes);\n        const verticalBoxes = boxes.vertical;\n        const horizontalBoxes = boxes.horizontal;\n        helpers_segment.each(chart.boxes, (box)=>{\n            if (typeof box.beforeLayout === 'function') {\n                box.beforeLayout();\n            }\n        });\n        const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap)=>wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1;\n        const params = Object.freeze({\n            outerWidth: width,\n            outerHeight: height,\n            padding,\n            availableWidth,\n            availableHeight,\n            vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount,\n            hBoxMaxHeight: availableHeight / 2\n        });\n        const maxPadding = Object.assign({}, padding);\n        updateMaxPadding(maxPadding, helpers_segment.toPadding(minPadding));\n        const chartArea = Object.assign({\n            maxPadding,\n            w: availableWidth,\n            h: availableHeight,\n            x: padding.left,\n            y: padding.top\n        }, padding);\n        const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);\n        fitBoxes(boxes.fullSize, chartArea, params, stacks);\n        fitBoxes(verticalBoxes, chartArea, params, stacks);\n        if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) {\n            fitBoxes(verticalBoxes, chartArea, params, stacks);\n        }\n        handleMaxPadding(chartArea);\n        placeBoxes(boxes.leftAndTop, chartArea, params, stacks);\n        chartArea.x += chartArea.w;\n        chartArea.y += chartArea.h;\n        placeBoxes(boxes.rightAndBottom, chartArea, params, stacks);\n        chart.chartArea = {\n            left: chartArea.left,\n            top: chartArea.top,\n            right: chartArea.left + chartArea.w,\n            bottom: chartArea.top + chartArea.h,\n            height: chartArea.h,\n            width: chartArea.w\n        };\n        helpers_segment.each(boxes.chartArea, (layout)=>{\n            const box = layout.box;\n            Object.assign(box, chart.chartArea);\n            box.update(chartArea.w, chartArea.h, {\n                left: 0,\n                top: 0,\n                right: 0,\n                bottom: 0\n            });\n        });\n    }\n};\n\nclass BasePlatform {\n acquireContext(canvas, aspectRatio) {}\n releaseContext(context) {\n        return false;\n    }\n addEventListener(chart, type, listener) {}\n removeEventListener(chart, type, listener) {}\n getDevicePixelRatio() {\n        return 1;\n    }\n getMaximumSize(element, width, height, aspectRatio) {\n        width = Math.max(0, width || element.width);\n        height = height || element.height;\n        return {\n            width,\n            height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)\n        };\n    }\n isAttached(canvas) {\n        return true;\n    }\n updateConfig(config) {\n    }\n}\n\nclass BasicPlatform extends BasePlatform {\n    acquireContext(item) {\n        return item && item.getContext && item.getContext('2d') || null;\n    }\n    updateConfig(config) {\n        config.options.animation = false;\n    }\n}\n\nconst EXPANDO_KEY = '$chartjs';\n const EVENT_TYPES = {\n    touchstart: 'mousedown',\n    touchmove: 'mousemove',\n    touchend: 'mouseup',\n    pointerenter: 'mouseenter',\n    pointerdown: 'mousedown',\n    pointermove: 'mousemove',\n    pointerup: 'mouseup',\n    pointerleave: 'mouseout',\n    pointerout: 'mouseout'\n};\nconst isNullOrEmpty = (value)=>value === null || value === '';\n function initCanvas(canvas, aspectRatio) {\n    const style = canvas.style;\n    const renderHeight = canvas.getAttribute('height');\n    const renderWidth = canvas.getAttribute('width');\n    canvas[EXPANDO_KEY] = {\n        initial: {\n            height: renderHeight,\n            width: renderWidth,\n            style: {\n                display: style.display,\n                height: style.height,\n                width: style.width\n            }\n        }\n    };\n    style.display = style.display || 'block';\n    style.boxSizing = style.boxSizing || 'border-box';\n    if (isNullOrEmpty(renderWidth)) {\n        const displayWidth = helpers_segment.readUsedSize(canvas, 'width');\n        if (displayWidth !== undefined) {\n            canvas.width = displayWidth;\n        }\n    }\n    if (isNullOrEmpty(renderHeight)) {\n        if (canvas.style.height === '') {\n            canvas.height = canvas.width / (aspectRatio || 2);\n        } else {\n            const displayHeight = helpers_segment.readUsedSize(canvas, 'height');\n            if (displayHeight !== undefined) {\n                canvas.height = displayHeight;\n            }\n        }\n    }\n    return canvas;\n}\nconst eventListenerOptions = helpers_segment.supportsEventListenerOptions ? {\n    passive: true\n} : false;\nfunction addListener(node, type, listener) {\n    node.addEventListener(type, listener, eventListenerOptions);\n}\nfunction removeListener(chart, type, listener) {\n    chart.canvas.removeEventListener(type, listener, eventListenerOptions);\n}\nfunction fromNativeEvent(event, chart) {\n    const type = EVENT_TYPES[event.type] || event.type;\n    const { x , y  } = helpers_segment.getRelativePosition(event, chart);\n    return {\n        type,\n        chart,\n        native: event,\n        x: x !== undefined ? x : null,\n        y: y !== undefined ? y : null\n    };\n}\nfunction nodeListContains(nodeList, canvas) {\n    for (const node of nodeList){\n        if (node === canvas || node.contains(canvas)) {\n            return true;\n        }\n    }\n}\nfunction createAttachObserver(chart, type, listener) {\n    const canvas = chart.canvas;\n    const observer = new MutationObserver((entries)=>{\n        let trigger = false;\n        for (const entry of entries){\n            trigger = trigger || nodeListContains(entry.addedNodes, canvas);\n            trigger = trigger && !nodeListContains(entry.removedNodes, canvas);\n        }\n        if (trigger) {\n            listener();\n        }\n    });\n    observer.observe(document, {\n        childList: true,\n        subtree: true\n    });\n    return observer;\n}\nfunction createDetachObserver(chart, type, listener) {\n    const canvas = chart.canvas;\n    const observer = new MutationObserver((entries)=>{\n        let trigger = false;\n        for (const entry of entries){\n            trigger = trigger || nodeListContains(entry.removedNodes, canvas);\n            trigger = trigger && !nodeListContains(entry.addedNodes, canvas);\n        }\n        if (trigger) {\n            listener();\n        }\n    });\n    observer.observe(document, {\n        childList: true,\n        subtree: true\n    });\n    return observer;\n}\nconst drpListeningCharts = new Map();\nlet oldDevicePixelRatio = 0;\nfunction onWindowResize() {\n    const dpr = window.devicePixelRatio;\n    if (dpr === oldDevicePixelRatio) {\n        return;\n    }\n    oldDevicePixelRatio = dpr;\n    drpListeningCharts.forEach((resize, chart)=>{\n        if (chart.currentDevicePixelRatio !== dpr) {\n            resize();\n        }\n    });\n}\nfunction listenDevicePixelRatioChanges(chart, resize) {\n    if (!drpListeningCharts.size) {\n        window.addEventListener('resize', onWindowResize);\n    }\n    drpListeningCharts.set(chart, resize);\n}\nfunction unlistenDevicePixelRatioChanges(chart) {\n    drpListeningCharts.delete(chart);\n    if (!drpListeningCharts.size) {\n        window.removeEventListener('resize', onWindowResize);\n    }\n}\nfunction createResizeObserver(chart, type, listener) {\n    const canvas = chart.canvas;\n    const container = canvas && helpers_segment._getParentNode(canvas);\n    if (!container) {\n        return;\n    }\n    const resize = helpers_segment.throttled((width, height)=>{\n        const w = container.clientWidth;\n        listener(width, height);\n        if (w < container.clientWidth) {\n            listener();\n        }\n    }, window);\n    const observer = new ResizeObserver((entries)=>{\n        const entry = entries[0];\n        const width = entry.contentRect.width;\n        const height = entry.contentRect.height;\n        if (width === 0 && height === 0) {\n            return;\n        }\n        resize(width, height);\n    });\n    observer.observe(container);\n    listenDevicePixelRatioChanges(chart, resize);\n    return observer;\n}\nfunction releaseObserver(chart, type, observer) {\n    if (observer) {\n        observer.disconnect();\n    }\n    if (type === 'resize') {\n        unlistenDevicePixelRatioChanges(chart);\n    }\n}\nfunction createProxyAndListen(chart, type, listener) {\n    const canvas = chart.canvas;\n    const proxy = helpers_segment.throttled((event)=>{\n        if (chart.ctx !== null) {\n            listener(fromNativeEvent(event, chart));\n        }\n    }, chart);\n    addListener(canvas, type, proxy);\n    return proxy;\n}\n class DomPlatform extends BasePlatform {\n acquireContext(canvas, aspectRatio) {\n        const context = canvas && canvas.getContext && canvas.getContext('2d');\n        if (context && context.canvas === canvas) {\n            initCanvas(canvas, aspectRatio);\n            return context;\n        }\n        return null;\n    }\n releaseContext(context) {\n        const canvas = context.canvas;\n        if (!canvas[EXPANDO_KEY]) {\n            return false;\n        }\n        const initial = canvas[EXPANDO_KEY].initial;\n        [\n            'height',\n            'width'\n        ].forEach((prop)=>{\n            const value = initial[prop];\n            if (helpers_segment.isNullOrUndef(value)) {\n                canvas.removeAttribute(prop);\n            } else {\n                canvas.setAttribute(prop, value);\n            }\n        });\n        const style = initial.style || {};\n        Object.keys(style).forEach((key)=>{\n            canvas.style[key] = style[key];\n        });\n        canvas.width = canvas.width;\n        delete canvas[EXPANDO_KEY];\n        return true;\n    }\n addEventListener(chart, type, listener) {\n        this.removeEventListener(chart, type);\n        const proxies = chart.$proxies || (chart.$proxies = {});\n        const handlers = {\n            attach: createAttachObserver,\n            detach: createDetachObserver,\n            resize: createResizeObserver\n        };\n        const handler = handlers[type] || createProxyAndListen;\n        proxies[type] = handler(chart, type, listener);\n    }\n removeEventListener(chart, type) {\n        const proxies = chart.$proxies || (chart.$proxies = {});\n        const proxy = proxies[type];\n        if (!proxy) {\n            return;\n        }\n        const handlers = {\n            attach: releaseObserver,\n            detach: releaseObserver,\n            resize: releaseObserver\n        };\n        const handler = handlers[type] || removeListener;\n        handler(chart, type, proxy);\n        proxies[type] = undefined;\n    }\n    getDevicePixelRatio() {\n        return window.devicePixelRatio;\n    }\n getMaximumSize(canvas, width, height, aspectRatio) {\n        return helpers_segment.getMaximumSize(canvas, width, height, aspectRatio);\n    }\n isAttached(canvas) {\n        const container = helpers_segment._getParentNode(canvas);\n        return !!(container && container.isConnected);\n    }\n}\n\nfunction _detectPlatform(canvas) {\n    if (!helpers_segment._isDomSupported() || typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {\n        return BasicPlatform;\n    }\n    return DomPlatform;\n}\n\nclass Element {\n    static defaults = {};\n    static defaultRoutes = undefined;\n    active = false;\n    tooltipPosition(useFinalPosition) {\n        const { x , y  } = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        return {\n            x,\n            y\n        };\n    }\n    hasValue() {\n        return helpers_segment.isNumber(this.x) && helpers_segment.isNumber(this.y);\n    }\n    getProps(props, final) {\n        const anims = this.$animations;\n        if (!final || !anims) {\n            // let's not create an object, if not needed\n            return this;\n        }\n        const ret = {};\n        props.forEach((prop)=>{\n            ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];\n        });\n        return ret;\n    }\n}\n\nfunction autoSkip(scale, ticks) {\n    const tickOpts = scale.options.ticks;\n    const determinedMaxTicks = determineMaxTicks(scale);\n    const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks);\n    const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];\n    const numMajorIndices = majorIndices.length;\n    const first = majorIndices[0];\n    const last = majorIndices[numMajorIndices - 1];\n    const newTicks = [];\n    if (numMajorIndices > ticksLimit) {\n        skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);\n        return newTicks;\n    }\n    const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);\n    if (numMajorIndices > 0) {\n        let i, ilen;\n        const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;\n        skip(ticks, newTicks, spacing, helpers_segment.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);\n        for(i = 0, ilen = numMajorIndices - 1; i < ilen; i++){\n            skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);\n        }\n        skip(ticks, newTicks, spacing, last, helpers_segment.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);\n        return newTicks;\n    }\n    skip(ticks, newTicks, spacing);\n    return newTicks;\n}\nfunction determineMaxTicks(scale) {\n    const offset = scale.options.offset;\n    const tickLength = scale._tickSize();\n    const maxScale = scale._length / tickLength + (offset ? 0 : 1);\n    const maxChart = scale._maxLength / tickLength;\n    return Math.floor(Math.min(maxScale, maxChart));\n}\n function calculateSpacing(majorIndices, ticks, ticksLimit) {\n    const evenMajorSpacing = getEvenSpacing(majorIndices);\n    const spacing = ticks.length / ticksLimit;\n    if (!evenMajorSpacing) {\n        return Math.max(spacing, 1);\n    }\n    const factors = helpers_segment._factorize(evenMajorSpacing);\n    for(let i = 0, ilen = factors.length - 1; i < ilen; i++){\n        const factor = factors[i];\n        if (factor > spacing) {\n            return factor;\n        }\n    }\n    return Math.max(spacing, 1);\n}\n function getMajorIndices(ticks) {\n    const result = [];\n    let i, ilen;\n    for(i = 0, ilen = ticks.length; i < ilen; i++){\n        if (ticks[i].major) {\n            result.push(i);\n        }\n    }\n    return result;\n}\n function skipMajors(ticks, newTicks, majorIndices, spacing) {\n    let count = 0;\n    let next = majorIndices[0];\n    let i;\n    spacing = Math.ceil(spacing);\n    for(i = 0; i < ticks.length; i++){\n        if (i === next) {\n            newTicks.push(ticks[i]);\n            count++;\n            next = majorIndices[count * spacing];\n        }\n    }\n}\n function skip(ticks, newTicks, spacing, majorStart, majorEnd) {\n    const start = helpers_segment.valueOrDefault(majorStart, 0);\n    const end = Math.min(helpers_segment.valueOrDefault(majorEnd, ticks.length), ticks.length);\n    let count = 0;\n    let length, i, next;\n    spacing = Math.ceil(spacing);\n    if (majorEnd) {\n        length = majorEnd - majorStart;\n        spacing = length / Math.floor(length / spacing);\n    }\n    next = start;\n    while(next < 0){\n        count++;\n        next = Math.round(start + count * spacing);\n    }\n    for(i = Math.max(start, 0); i < end; i++){\n        if (i === next) {\n            newTicks.push(ticks[i]);\n            count++;\n            next = Math.round(start + count * spacing);\n        }\n    }\n}\n function getEvenSpacing(arr) {\n    const len = arr.length;\n    let i, diff;\n    if (len < 2) {\n        return false;\n    }\n    for(diff = arr[0], i = 1; i < len; ++i){\n        if (arr[i] - arr[i - 1] !== diff) {\n            return false;\n        }\n    }\n    return diff;\n}\n\nconst reverseAlign = (align)=>align === 'left' ? 'right' : align === 'right' ? 'left' : align;\nconst offsetFromEdge = (scale, edge, offset)=>edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset;\nconst getTicksLimit = (ticksLength, maxTicksLimit)=>Math.min(maxTicksLimit || ticksLength, ticksLength);\n function sample(arr, numItems) {\n    const result = [];\n    const increment = arr.length / numItems;\n    const len = arr.length;\n    let i = 0;\n    for(; i < len; i += increment){\n        result.push(arr[Math.floor(i)]);\n    }\n    return result;\n}\n function getPixelForGridLine(scale, index, offsetGridLines) {\n    const length = scale.ticks.length;\n    const validIndex = Math.min(index, length - 1);\n    const start = scale._startPixel;\n    const end = scale._endPixel;\n    const epsilon = 1e-6;\n    let lineValue = scale.getPixelForTick(validIndex);\n    let offset;\n    if (offsetGridLines) {\n        if (length === 1) {\n            offset = Math.max(lineValue - start, end - lineValue);\n        } else if (index === 0) {\n            offset = (scale.getPixelForTick(1) - lineValue) / 2;\n        } else {\n            offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;\n        }\n        lineValue += validIndex < index ? offset : -offset;\n        if (lineValue < start - epsilon || lineValue > end + epsilon) {\n            return;\n        }\n    }\n    return lineValue;\n}\n function garbageCollect(caches, length) {\n    helpers_segment.each(caches, (cache)=>{\n        const gc = cache.gc;\n        const gcLen = gc.length / 2;\n        let i;\n        if (gcLen > length) {\n            for(i = 0; i < gcLen; ++i){\n                delete cache.data[gc[i]];\n            }\n            gc.splice(0, gcLen);\n        }\n    });\n}\n function getTickMarkLength(options) {\n    return options.drawTicks ? options.tickLength : 0;\n}\n function getTitleHeight(options, fallback) {\n    if (!options.display) {\n        return 0;\n    }\n    const font = helpers_segment.toFont(options.font, fallback);\n    const padding = helpers_segment.toPadding(options.padding);\n    const lines = helpers_segment.isArray(options.text) ? options.text.length : 1;\n    return lines * font.lineHeight + padding.height;\n}\nfunction createScaleContext(parent, scale) {\n    return helpers_segment.createContext(parent, {\n        scale,\n        type: 'scale'\n    });\n}\nfunction createTickContext(parent, index, tick) {\n    return helpers_segment.createContext(parent, {\n        tick,\n        index,\n        type: 'tick'\n    });\n}\nfunction titleAlign(align, position, reverse) {\n    let ret = helpers_segment._toLeftRightCenter(align);\n    if (reverse && position !== 'right' || !reverse && position === 'right') {\n        ret = reverseAlign(ret);\n    }\n    return ret;\n}\nfunction titleArgs(scale, offset, position, align) {\n    const { top , left , bottom , right , chart  } = scale;\n    const { chartArea , scales  } = chart;\n    let rotation = 0;\n    let maxWidth, titleX, titleY;\n    const height = bottom - top;\n    const width = right - left;\n    if (scale.isHorizontal()) {\n        titleX = helpers_segment._alignStartEnd(align, left, right);\n        if (helpers_segment.isObject(position)) {\n            const positionAxisID = Object.keys(position)[0];\n            const value = position[positionAxisID];\n            titleY = scales[positionAxisID].getPixelForValue(value) + height - offset;\n        } else if (position === 'center') {\n            titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset;\n        } else {\n            titleY = offsetFromEdge(scale, position, offset);\n        }\n        maxWidth = right - left;\n    } else {\n        if (helpers_segment.isObject(position)) {\n            const positionAxisID1 = Object.keys(position)[0];\n            const value1 = position[positionAxisID1];\n            titleX = scales[positionAxisID1].getPixelForValue(value1) - width + offset;\n        } else if (position === 'center') {\n            titleX = (chartArea.left + chartArea.right) / 2 - width + offset;\n        } else {\n            titleX = offsetFromEdge(scale, position, offset);\n        }\n        titleY = helpers_segment._alignStartEnd(align, bottom, top);\n        rotation = position === 'left' ? -helpers_segment.HALF_PI : helpers_segment.HALF_PI;\n    }\n    return {\n        titleX,\n        titleY,\n        maxWidth,\n        rotation\n    };\n}\nclass Scale extends Element {\n    constructor(cfg){\n        super();\n         this.id = cfg.id;\n         this.type = cfg.type;\n         this.options = undefined;\n         this.ctx = cfg.ctx;\n         this.chart = cfg.chart;\n         this.top = undefined;\n         this.bottom = undefined;\n         this.left = undefined;\n         this.right = undefined;\n         this.width = undefined;\n         this.height = undefined;\n        this._margins = {\n            left: 0,\n            right: 0,\n            top: 0,\n            bottom: 0\n        };\n         this.maxWidth = undefined;\n         this.maxHeight = undefined;\n         this.paddingTop = undefined;\n         this.paddingBottom = undefined;\n         this.paddingLeft = undefined;\n         this.paddingRight = undefined;\n         this.axis = undefined;\n         this.labelRotation = undefined;\n        this.min = undefined;\n        this.max = undefined;\n        this._range = undefined;\n         this.ticks = [];\n         this._gridLineItems = null;\n         this._labelItems = null;\n         this._labelSizes = null;\n        this._length = 0;\n        this._maxLength = 0;\n        this._longestTextCache = {};\n         this._startPixel = undefined;\n         this._endPixel = undefined;\n        this._reversePixels = false;\n        this._userMax = undefined;\n        this._userMin = undefined;\n        this._suggestedMax = undefined;\n        this._suggestedMin = undefined;\n        this._ticksLength = 0;\n        this._borderValue = 0;\n        this._cache = {};\n        this._dataLimitsCached = false;\n        this.$context = undefined;\n    }\n init(options) {\n        this.options = options.setContext(this.getContext());\n        this.axis = options.axis;\n        this._userMin = this.parse(options.min);\n        this._userMax = this.parse(options.max);\n        this._suggestedMin = this.parse(options.suggestedMin);\n        this._suggestedMax = this.parse(options.suggestedMax);\n    }\n parse(raw, index) {\n        return raw;\n    }\n getUserBounds() {\n        let { _userMin , _userMax , _suggestedMin , _suggestedMax  } = this;\n        _userMin = helpers_segment.finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);\n        _userMax = helpers_segment.finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);\n        _suggestedMin = helpers_segment.finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);\n        _suggestedMax = helpers_segment.finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);\n        return {\n            min: helpers_segment.finiteOrDefault(_userMin, _suggestedMin),\n            max: helpers_segment.finiteOrDefault(_userMax, _suggestedMax),\n            minDefined: helpers_segment.isNumberFinite(_userMin),\n            maxDefined: helpers_segment.isNumberFinite(_userMax)\n        };\n    }\n getMinMax(canStack) {\n        let { min , max , minDefined , maxDefined  } = this.getUserBounds();\n        let range;\n        if (minDefined && maxDefined) {\n            return {\n                min,\n                max\n            };\n        }\n        const metas = this.getMatchingVisibleMetas();\n        for(let i = 0, ilen = metas.length; i < ilen; ++i){\n            range = metas[i].controller.getMinMax(this, canStack);\n            if (!minDefined) {\n                min = Math.min(min, range.min);\n            }\n            if (!maxDefined) {\n                max = Math.max(max, range.max);\n            }\n        }\n        min = maxDefined && min > max ? max : min;\n        max = minDefined && min > max ? min : max;\n        return {\n            min: helpers_segment.finiteOrDefault(min, helpers_segment.finiteOrDefault(max, min)),\n            max: helpers_segment.finiteOrDefault(max, helpers_segment.finiteOrDefault(min, max))\n        };\n    }\n getPadding() {\n        return {\n            left: this.paddingLeft || 0,\n            top: this.paddingTop || 0,\n            right: this.paddingRight || 0,\n            bottom: this.paddingBottom || 0\n        };\n    }\n getTicks() {\n        return this.ticks;\n    }\n getLabels() {\n        const data = this.chart.data;\n        return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];\n    }\n getLabelItems(chartArea = this.chart.chartArea) {\n        const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea));\n        return items;\n    }\n    beforeLayout() {\n        this._cache = {};\n        this._dataLimitsCached = false;\n    }\n    beforeUpdate() {\n        helpers_segment.callback(this.options.beforeUpdate, [\n            this\n        ]);\n    }\n update(maxWidth, maxHeight, margins) {\n        const { beginAtZero , grace , ticks: tickOpts  } = this.options;\n        const sampleSize = tickOpts.sampleSize;\n        this.beforeUpdate();\n        this.maxWidth = maxWidth;\n        this.maxHeight = maxHeight;\n        this._margins = margins = Object.assign({\n            left: 0,\n            right: 0,\n            top: 0,\n            bottom: 0\n        }, margins);\n        this.ticks = null;\n        this._labelSizes = null;\n        this._gridLineItems = null;\n        this._labelItems = null;\n        this.beforeSetDimensions();\n        this.setDimensions();\n        this.afterSetDimensions();\n        this._maxLength = this.isHorizontal() ? this.width + margins.left + margins.right : this.height + margins.top + margins.bottom;\n        if (!this._dataLimitsCached) {\n            this.beforeDataLimits();\n            this.determineDataLimits();\n            this.afterDataLimits();\n            this._range = helpers_segment._addGrace(this, grace, beginAtZero);\n            this._dataLimitsCached = true;\n        }\n        this.beforeBuildTicks();\n        this.ticks = this.buildTicks() || [];\n        this.afterBuildTicks();\n        const samplingEnabled = sampleSize < this.ticks.length;\n        this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks);\n        this.configure();\n        this.beforeCalculateLabelRotation();\n        this.calculateLabelRotation();\n        this.afterCalculateLabelRotation();\n        if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {\n            this.ticks = autoSkip(this, this.ticks);\n            this._labelSizes = null;\n            this.afterAutoSkip();\n        }\n        if (samplingEnabled) {\n            this._convertTicksToLabels(this.ticks);\n        }\n        this.beforeFit();\n        this.fit();\n        this.afterFit();\n        this.afterUpdate();\n    }\n configure() {\n        let reversePixels = this.options.reverse;\n        let startPixel, endPixel;\n        if (this.isHorizontal()) {\n            startPixel = this.left;\n            endPixel = this.right;\n        } else {\n            startPixel = this.top;\n            endPixel = this.bottom;\n            reversePixels = !reversePixels;\n        }\n        this._startPixel = startPixel;\n        this._endPixel = endPixel;\n        this._reversePixels = reversePixels;\n        this._length = endPixel - startPixel;\n        this._alignToPixels = this.options.alignToPixels;\n    }\n    afterUpdate() {\n        helpers_segment.callback(this.options.afterUpdate, [\n            this\n        ]);\n    }\n    beforeSetDimensions() {\n        helpers_segment.callback(this.options.beforeSetDimensions, [\n            this\n        ]);\n    }\n    setDimensions() {\n        if (this.isHorizontal()) {\n            this.width = this.maxWidth;\n            this.left = 0;\n            this.right = this.width;\n        } else {\n            this.height = this.maxHeight;\n            this.top = 0;\n            this.bottom = this.height;\n        }\n        this.paddingLeft = 0;\n        this.paddingTop = 0;\n        this.paddingRight = 0;\n        this.paddingBottom = 0;\n    }\n    afterSetDimensions() {\n        helpers_segment.callback(this.options.afterSetDimensions, [\n            this\n        ]);\n    }\n    _callHooks(name) {\n        this.chart.notifyPlugins(name, this.getContext());\n        helpers_segment.callback(this.options[name], [\n            this\n        ]);\n    }\n    beforeDataLimits() {\n        this._callHooks('beforeDataLimits');\n    }\n    determineDataLimits() {}\n    afterDataLimits() {\n        this._callHooks('afterDataLimits');\n    }\n    beforeBuildTicks() {\n        this._callHooks('beforeBuildTicks');\n    }\n buildTicks() {\n        return [];\n    }\n    afterBuildTicks() {\n        this._callHooks('afterBuildTicks');\n    }\n    beforeTickToLabelConversion() {\n        helpers_segment.callback(this.options.beforeTickToLabelConversion, [\n            this\n        ]);\n    }\n generateTickLabels(ticks) {\n        const tickOpts = this.options.ticks;\n        let i, ilen, tick;\n        for(i = 0, ilen = ticks.length; i < ilen; i++){\n            tick = ticks[i];\n            tick.label = helpers_segment.callback(tickOpts.callback, [\n                tick.value,\n                i,\n                ticks\n            ], this);\n        }\n    }\n    afterTickToLabelConversion() {\n        helpers_segment.callback(this.options.afterTickToLabelConversion, [\n            this\n        ]);\n    }\n    beforeCalculateLabelRotation() {\n        helpers_segment.callback(this.options.beforeCalculateLabelRotation, [\n            this\n        ]);\n    }\n    calculateLabelRotation() {\n        const options = this.options;\n        const tickOpts = options.ticks;\n        const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit);\n        const minRotation = tickOpts.minRotation || 0;\n        const maxRotation = tickOpts.maxRotation;\n        let labelRotation = minRotation;\n        let tickWidth, maxHeight, maxLabelDiagonal;\n        if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) {\n            this.labelRotation = minRotation;\n            return;\n        }\n        const labelSizes = this._getLabelSizes();\n        const maxLabelWidth = labelSizes.widest.width;\n        const maxLabelHeight = labelSizes.highest.height;\n        const maxWidth = helpers_segment._limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth);\n        tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1);\n        if (maxLabelWidth + 6 > tickWidth) {\n            tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));\n            maxHeight = this.maxHeight - getTickMarkLength(options.grid) - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font);\n            maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);\n            labelRotation = helpers_segment.toDegrees(Math.min(Math.asin(helpers_segment._limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)), Math.asin(helpers_segment._limitValue(maxHeight / maxLabelDiagonal, -1, 1)) - Math.asin(helpers_segment._limitValue(maxLabelHeight / maxLabelDiagonal, -1, 1))));\n            labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));\n        }\n        this.labelRotation = labelRotation;\n    }\n    afterCalculateLabelRotation() {\n        helpers_segment.callback(this.options.afterCalculateLabelRotation, [\n            this\n        ]);\n    }\n    afterAutoSkip() {}\n    beforeFit() {\n        helpers_segment.callback(this.options.beforeFit, [\n            this\n        ]);\n    }\n    fit() {\n        const minSize = {\n            width: 0,\n            height: 0\n        };\n        const { chart , options: { ticks: tickOpts , title: titleOpts , grid: gridOpts  }  } = this;\n        const display = this._isVisible();\n        const isHorizontal = this.isHorizontal();\n        if (display) {\n            const titleHeight = getTitleHeight(titleOpts, chart.options.font);\n            if (isHorizontal) {\n                minSize.width = this.maxWidth;\n                minSize.height = getTickMarkLength(gridOpts) + titleHeight;\n            } else {\n                minSize.height = this.maxHeight;\n                minSize.width = getTickMarkLength(gridOpts) + titleHeight;\n            }\n            if (tickOpts.display && this.ticks.length) {\n                const { first , last , widest , highest  } = this._getLabelSizes();\n                const tickPadding = tickOpts.padding * 2;\n                const angleRadians = helpers_segment.toRadians(this.labelRotation);\n                const cos = Math.cos(angleRadians);\n                const sin = Math.sin(angleRadians);\n                if (isHorizontal) {\n                    const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;\n                    minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding);\n                } else {\n                    const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;\n                    minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding);\n                }\n                this._calculatePadding(first, last, sin, cos);\n            }\n        }\n        this._handleMargins();\n        if (isHorizontal) {\n            this.width = this._length = chart.width - this._margins.left - this._margins.right;\n            this.height = minSize.height;\n        } else {\n            this.width = minSize.width;\n            this.height = this._length = chart.height - this._margins.top - this._margins.bottom;\n        }\n    }\n    _calculatePadding(first, last, sin, cos) {\n        const { ticks: { align , padding  } , position  } = this.options;\n        const isRotated = this.labelRotation !== 0;\n        const labelsBelowTicks = position !== 'top' && this.axis === 'x';\n        if (this.isHorizontal()) {\n            const offsetLeft = this.getPixelForTick(0) - this.left;\n            const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1);\n            let paddingLeft = 0;\n            let paddingRight = 0;\n            if (isRotated) {\n                if (labelsBelowTicks) {\n                    paddingLeft = cos * first.width;\n                    paddingRight = sin * last.height;\n                } else {\n                    paddingLeft = sin * first.height;\n                    paddingRight = cos * last.width;\n                }\n            } else if (align === 'start') {\n                paddingRight = last.width;\n            } else if (align === 'end') {\n                paddingLeft = first.width;\n            } else if (align !== 'inner') {\n                paddingLeft = first.width / 2;\n                paddingRight = last.width / 2;\n            }\n            this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0);\n            this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0);\n        } else {\n            let paddingTop = last.height / 2;\n            let paddingBottom = first.height / 2;\n            if (align === 'start') {\n                paddingTop = 0;\n                paddingBottom = first.height;\n            } else if (align === 'end') {\n                paddingTop = last.height;\n                paddingBottom = 0;\n            }\n            this.paddingTop = paddingTop + padding;\n            this.paddingBottom = paddingBottom + padding;\n        }\n    }\n _handleMargins() {\n        if (this._margins) {\n            this._margins.left = Math.max(this.paddingLeft, this._margins.left);\n            this._margins.top = Math.max(this.paddingTop, this._margins.top);\n            this._margins.right = Math.max(this.paddingRight, this._margins.right);\n            this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom);\n        }\n    }\n    afterFit() {\n        helpers_segment.callback(this.options.afterFit, [\n            this\n        ]);\n    }\n isHorizontal() {\n        const { axis , position  } = this.options;\n        return position === 'top' || position === 'bottom' || axis === 'x';\n    }\n isFullSize() {\n        return this.options.fullSize;\n    }\n _convertTicksToLabels(ticks) {\n        this.beforeTickToLabelConversion();\n        this.generateTickLabels(ticks);\n        let i, ilen;\n        for(i = 0, ilen = ticks.length; i < ilen; i++){\n            if (helpers_segment.isNullOrUndef(ticks[i].label)) {\n                ticks.splice(i, 1);\n                ilen--;\n                i--;\n            }\n        }\n        this.afterTickToLabelConversion();\n    }\n _getLabelSizes() {\n        let labelSizes = this._labelSizes;\n        if (!labelSizes) {\n            const sampleSize = this.options.ticks.sampleSize;\n            let ticks = this.ticks;\n            if (sampleSize < ticks.length) {\n                ticks = sample(ticks, sampleSize);\n            }\n            this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit);\n        }\n        return labelSizes;\n    }\n _computeLabelSizes(ticks, length, maxTicksLimit) {\n        const { ctx , _longestTextCache: caches  } = this;\n        const widths = [];\n        const heights = [];\n        const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit));\n        let widestLabelSize = 0;\n        let highestLabelSize = 0;\n        let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel;\n        for(i = 0; i < length; i += increment){\n            label = ticks[i].label;\n            tickFont = this._resolveTickFontOptions(i);\n            ctx.font = fontString = tickFont.string;\n            cache = caches[fontString] = caches[fontString] || {\n                data: {},\n                gc: []\n            };\n            lineHeight = tickFont.lineHeight;\n            width = height = 0;\n            if (!helpers_segment.isNullOrUndef(label) && !helpers_segment.isArray(label)) {\n                width = helpers_segment._measureText(ctx, cache.data, cache.gc, width, label);\n                height = lineHeight;\n            } else if (helpers_segment.isArray(label)) {\n                for(j = 0, jlen = label.length; j < jlen; ++j){\n                    nestedLabel = label[j];\n                    if (!helpers_segment.isNullOrUndef(nestedLabel) && !helpers_segment.isArray(nestedLabel)) {\n                        width = helpers_segment._measureText(ctx, cache.data, cache.gc, width, nestedLabel);\n                        height += lineHeight;\n                    }\n                }\n            }\n            widths.push(width);\n            heights.push(height);\n            widestLabelSize = Math.max(width, widestLabelSize);\n            highestLabelSize = Math.max(height, highestLabelSize);\n        }\n        garbageCollect(caches, length);\n        const widest = widths.indexOf(widestLabelSize);\n        const highest = heights.indexOf(highestLabelSize);\n        const valueAt = (idx)=>({\n                width: widths[idx] || 0,\n                height: heights[idx] || 0\n            });\n        return {\n            first: valueAt(0),\n            last: valueAt(length - 1),\n            widest: valueAt(widest),\n            highest: valueAt(highest),\n            widths,\n            heights\n        };\n    }\n getLabelForValue(value) {\n        return value;\n    }\n getPixelForValue(value, index) {\n        return NaN;\n    }\n getValueForPixel(pixel) {}\n getPixelForTick(index) {\n        const ticks = this.ticks;\n        if (index < 0 || index > ticks.length - 1) {\n            return null;\n        }\n        return this.getPixelForValue(ticks[index].value);\n    }\n getPixelForDecimal(decimal) {\n        if (this._reversePixels) {\n            decimal = 1 - decimal;\n        }\n        const pixel = this._startPixel + decimal * this._length;\n        return helpers_segment._int16Range(this._alignToPixels ? helpers_segment._alignPixel(this.chart, pixel, 0) : pixel);\n    }\n getDecimalForPixel(pixel) {\n        const decimal = (pixel - this._startPixel) / this._length;\n        return this._reversePixels ? 1 - decimal : decimal;\n    }\n getBasePixel() {\n        return this.getPixelForValue(this.getBaseValue());\n    }\n getBaseValue() {\n        const { min , max  } = this;\n        return min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0;\n    }\n getContext(index) {\n        const ticks = this.ticks || [];\n        if (index >= 0 && index < ticks.length) {\n            const tick = ticks[index];\n            return tick.$context || (tick.$context = createTickContext(this.getContext(), index, tick));\n        }\n        return this.$context || (this.$context = createScaleContext(this.chart.getContext(), this));\n    }\n _tickSize() {\n        const optionTicks = this.options.ticks;\n        const rot = helpers_segment.toRadians(this.labelRotation);\n        const cos = Math.abs(Math.cos(rot));\n        const sin = Math.abs(Math.sin(rot));\n        const labelSizes = this._getLabelSizes();\n        const padding = optionTicks.autoSkipPadding || 0;\n        const w = labelSizes ? labelSizes.widest.width + padding : 0;\n        const h = labelSizes ? labelSizes.highest.height + padding : 0;\n        return this.isHorizontal() ? h * cos > w * sin ? w / cos : h / sin : h * sin < w * cos ? h / cos : w / sin;\n    }\n _isVisible() {\n        const display = this.options.display;\n        if (display !== 'auto') {\n            return !!display;\n        }\n        return this.getMatchingVisibleMetas().length > 0;\n    }\n _computeGridLineItems(chartArea) {\n        const axis = this.axis;\n        const chart = this.chart;\n        const options = this.options;\n        const { grid , position , border  } = options;\n        const offset = grid.offset;\n        const isHorizontal = this.isHorizontal();\n        const ticks = this.ticks;\n        const ticksLength = ticks.length + (offset ? 1 : 0);\n        const tl = getTickMarkLength(grid);\n        const items = [];\n        const borderOpts = border.setContext(this.getContext());\n        const axisWidth = borderOpts.display ? borderOpts.width : 0;\n        const axisHalfWidth = axisWidth / 2;\n        const alignBorderValue = function(pixel) {\n            return helpers_segment._alignPixel(chart, pixel, axisWidth);\n        };\n        let borderValue, i, lineValue, alignedLineValue;\n        let tx1, ty1, tx2, ty2, x1, y1, x2, y2;\n        if (position === 'top') {\n            borderValue = alignBorderValue(this.bottom);\n            ty1 = this.bottom - tl;\n            ty2 = borderValue - axisHalfWidth;\n            y1 = alignBorderValue(chartArea.top) + axisHalfWidth;\n            y2 = chartArea.bottom;\n        } else if (position === 'bottom') {\n            borderValue = alignBorderValue(this.top);\n            y1 = chartArea.top;\n            y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;\n            ty1 = borderValue + axisHalfWidth;\n            ty2 = this.top + tl;\n        } else if (position === 'left') {\n            borderValue = alignBorderValue(this.right);\n            tx1 = this.right - tl;\n            tx2 = borderValue - axisHalfWidth;\n            x1 = alignBorderValue(chartArea.left) + axisHalfWidth;\n            x2 = chartArea.right;\n        } else if (position === 'right') {\n            borderValue = alignBorderValue(this.left);\n            x1 = chartArea.left;\n            x2 = alignBorderValue(chartArea.right) - axisHalfWidth;\n            tx1 = borderValue + axisHalfWidth;\n            tx2 = this.left + tl;\n        } else if (axis === 'x') {\n            if (position === 'center') {\n                borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);\n            } else if (helpers_segment.isObject(position)) {\n                const positionAxisID = Object.keys(position)[0];\n                const value = position[positionAxisID];\n                borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));\n            }\n            y1 = chartArea.top;\n            y2 = chartArea.bottom;\n            ty1 = borderValue + axisHalfWidth;\n            ty2 = ty1 + tl;\n        } else if (axis === 'y') {\n            if (position === 'center') {\n                borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2);\n            } else if (helpers_segment.isObject(position)) {\n                const positionAxisID1 = Object.keys(position)[0];\n                const value1 = position[positionAxisID1];\n                borderValue = alignBorderValue(this.chart.scales[positionAxisID1].getPixelForValue(value1));\n            }\n            tx1 = borderValue - axisHalfWidth;\n            tx2 = tx1 - tl;\n            x1 = chartArea.left;\n            x2 = chartArea.right;\n        }\n        const limit = helpers_segment.valueOrDefault(options.ticks.maxTicksLimit, ticksLength);\n        const step = Math.max(1, Math.ceil(ticksLength / limit));\n        for(i = 0; i < ticksLength; i += step){\n            const context = this.getContext(i);\n            const optsAtIndex = grid.setContext(context);\n            const optsAtIndexBorder = border.setContext(context);\n            const lineWidth = optsAtIndex.lineWidth;\n            const lineColor = optsAtIndex.color;\n            const borderDash = optsAtIndexBorder.dash || [];\n            const borderDashOffset = optsAtIndexBorder.dashOffset;\n            const tickWidth = optsAtIndex.tickWidth;\n            const tickColor = optsAtIndex.tickColor;\n            const tickBorderDash = optsAtIndex.tickBorderDash || [];\n            const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;\n            lineValue = getPixelForGridLine(this, i, offset);\n            if (lineValue === undefined) {\n                continue;\n            }\n            alignedLineValue = helpers_segment._alignPixel(chart, lineValue, lineWidth);\n            if (isHorizontal) {\n                tx1 = tx2 = x1 = x2 = alignedLineValue;\n            } else {\n                ty1 = ty2 = y1 = y2 = alignedLineValue;\n            }\n            items.push({\n                tx1,\n                ty1,\n                tx2,\n                ty2,\n                x1,\n                y1,\n                x2,\n                y2,\n                width: lineWidth,\n                color: lineColor,\n                borderDash,\n                borderDashOffset,\n                tickWidth,\n                tickColor,\n                tickBorderDash,\n                tickBorderDashOffset\n            });\n        }\n        this._ticksLength = ticksLength;\n        this._borderValue = borderValue;\n        return items;\n    }\n _computeLabelItems(chartArea) {\n        const axis = this.axis;\n        const options = this.options;\n        const { position , ticks: optionTicks  } = options;\n        const isHorizontal = this.isHorizontal();\n        const ticks = this.ticks;\n        const { align , crossAlign , padding , mirror  } = optionTicks;\n        const tl = getTickMarkLength(options.grid);\n        const tickAndPadding = tl + padding;\n        const hTickAndPadding = mirror ? -padding : tickAndPadding;\n        const rotation = -helpers_segment.toRadians(this.labelRotation);\n        const items = [];\n        let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;\n        let textBaseline = 'middle';\n        if (position === 'top') {\n            y = this.bottom - hTickAndPadding;\n            textAlign = this._getXAxisLabelAlignment();\n        } else if (position === 'bottom') {\n            y = this.top + hTickAndPadding;\n            textAlign = this._getXAxisLabelAlignment();\n        } else if (position === 'left') {\n            const ret = this._getYAxisLabelAlignment(tl);\n            textAlign = ret.textAlign;\n            x = ret.x;\n        } else if (position === 'right') {\n            const ret1 = this._getYAxisLabelAlignment(tl);\n            textAlign = ret1.textAlign;\n            x = ret1.x;\n        } else if (axis === 'x') {\n            if (position === 'center') {\n                y = (chartArea.top + chartArea.bottom) / 2 + tickAndPadding;\n            } else if (helpers_segment.isObject(position)) {\n                const positionAxisID = Object.keys(position)[0];\n                const value = position[positionAxisID];\n                y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;\n            }\n            textAlign = this._getXAxisLabelAlignment();\n        } else if (axis === 'y') {\n            if (position === 'center') {\n                x = (chartArea.left + chartArea.right) / 2 - tickAndPadding;\n            } else if (helpers_segment.isObject(position)) {\n                const positionAxisID1 = Object.keys(position)[0];\n                const value1 = position[positionAxisID1];\n                x = this.chart.scales[positionAxisID1].getPixelForValue(value1);\n            }\n            textAlign = this._getYAxisLabelAlignment(tl).textAlign;\n        }\n        if (axis === 'y') {\n            if (align === 'start') {\n                textBaseline = 'top';\n            } else if (align === 'end') {\n                textBaseline = 'bottom';\n            }\n        }\n        const labelSizes = this._getLabelSizes();\n        for(i = 0, ilen = ticks.length; i < ilen; ++i){\n            tick = ticks[i];\n            label = tick.label;\n            const optsAtIndex = optionTicks.setContext(this.getContext(i));\n            pixel = this.getPixelForTick(i) + optionTicks.labelOffset;\n            font = this._resolveTickFontOptions(i);\n            lineHeight = font.lineHeight;\n            lineCount = helpers_segment.isArray(label) ? label.length : 1;\n            const halfCount = lineCount / 2;\n            const color = optsAtIndex.color;\n            const strokeColor = optsAtIndex.textStrokeColor;\n            const strokeWidth = optsAtIndex.textStrokeWidth;\n            let tickTextAlign = textAlign;\n            if (isHorizontal) {\n                x = pixel;\n                if (textAlign === 'inner') {\n                    if (i === ilen - 1) {\n                        tickTextAlign = !this.options.reverse ? 'right' : 'left';\n                    } else if (i === 0) {\n                        tickTextAlign = !this.options.reverse ? 'left' : 'right';\n                    } else {\n                        tickTextAlign = 'center';\n                    }\n                }\n                if (position === 'top') {\n                    if (crossAlign === 'near' || rotation !== 0) {\n                        textOffset = -lineCount * lineHeight + lineHeight / 2;\n                    } else if (crossAlign === 'center') {\n                        textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight;\n                    } else {\n                        textOffset = -labelSizes.highest.height + lineHeight / 2;\n                    }\n                } else {\n                    if (crossAlign === 'near' || rotation !== 0) {\n                        textOffset = lineHeight / 2;\n                    } else if (crossAlign === 'center') {\n                        textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight;\n                    } else {\n                        textOffset = labelSizes.highest.height - lineCount * lineHeight;\n                    }\n                }\n                if (mirror) {\n                    textOffset *= -1;\n                }\n                if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) {\n                    x += lineHeight / 2 * Math.sin(rotation);\n                }\n            } else {\n                y = pixel;\n                textOffset = (1 - lineCount) * lineHeight / 2;\n            }\n            let backdrop;\n            if (optsAtIndex.showLabelBackdrop) {\n                const labelPadding = helpers_segment.toPadding(optsAtIndex.backdropPadding);\n                const height = labelSizes.heights[i];\n                const width = labelSizes.widths[i];\n                let top = textOffset - labelPadding.top;\n                let left = 0 - labelPadding.left;\n                switch(textBaseline){\n                    case 'middle':\n                        top -= height / 2;\n                        break;\n                    case 'bottom':\n                        top -= height;\n                        break;\n                }\n                switch(textAlign){\n                    case 'center':\n                        left -= width / 2;\n                        break;\n                    case 'right':\n                        left -= width;\n                        break;\n                }\n                backdrop = {\n                    left,\n                    top,\n                    width: width + labelPadding.width,\n                    height: height + labelPadding.height,\n                    color: optsAtIndex.backdropColor\n                };\n            }\n            items.push({\n                label,\n                font,\n                textOffset,\n                options: {\n                    rotation,\n                    color,\n                    strokeColor,\n                    strokeWidth,\n                    textAlign: tickTextAlign,\n                    textBaseline,\n                    translation: [\n                        x,\n                        y\n                    ],\n                    backdrop\n                }\n            });\n        }\n        return items;\n    }\n    _getXAxisLabelAlignment() {\n        const { position , ticks  } = this.options;\n        const rotation = -helpers_segment.toRadians(this.labelRotation);\n        if (rotation) {\n            return position === 'top' ? 'left' : 'right';\n        }\n        let align = 'center';\n        if (ticks.align === 'start') {\n            align = 'left';\n        } else if (ticks.align === 'end') {\n            align = 'right';\n        } else if (ticks.align === 'inner') {\n            align = 'inner';\n        }\n        return align;\n    }\n    _getYAxisLabelAlignment(tl) {\n        const { position , ticks: { crossAlign , mirror , padding  }  } = this.options;\n        const labelSizes = this._getLabelSizes();\n        const tickAndPadding = tl + padding;\n        const widest = labelSizes.widest.width;\n        let textAlign;\n        let x;\n        if (position === 'left') {\n            if (mirror) {\n                x = this.right + padding;\n                if (crossAlign === 'near') {\n                    textAlign = 'left';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x += widest / 2;\n                } else {\n                    textAlign = 'right';\n                    x += widest;\n                }\n            } else {\n                x = this.right - tickAndPadding;\n                if (crossAlign === 'near') {\n                    textAlign = 'right';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x -= widest / 2;\n                } else {\n                    textAlign = 'left';\n                    x = this.left;\n                }\n            }\n        } else if (position === 'right') {\n            if (mirror) {\n                x = this.left + padding;\n                if (crossAlign === 'near') {\n                    textAlign = 'right';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x -= widest / 2;\n                } else {\n                    textAlign = 'left';\n                    x -= widest;\n                }\n            } else {\n                x = this.left + tickAndPadding;\n                if (crossAlign === 'near') {\n                    textAlign = 'left';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x += widest / 2;\n                } else {\n                    textAlign = 'right';\n                    x = this.right;\n                }\n            }\n        } else {\n            textAlign = 'right';\n        }\n        return {\n            textAlign,\n            x\n        };\n    }\n _computeLabelArea() {\n        if (this.options.ticks.mirror) {\n            return;\n        }\n        const chart = this.chart;\n        const position = this.options.position;\n        if (position === 'left' || position === 'right') {\n            return {\n                top: 0,\n                left: this.left,\n                bottom: chart.height,\n                right: this.right\n            };\n        }\n        if (position === 'top' || position === 'bottom') {\n            return {\n                top: this.top,\n                left: 0,\n                bottom: this.bottom,\n                right: chart.width\n            };\n        }\n    }\n drawBackground() {\n        const { ctx , options: { backgroundColor  } , left , top , width , height  } = this;\n        if (backgroundColor) {\n            ctx.save();\n            ctx.fillStyle = backgroundColor;\n            ctx.fillRect(left, top, width, height);\n            ctx.restore();\n        }\n    }\n    getLineWidthForValue(value) {\n        const grid = this.options.grid;\n        if (!this._isVisible() || !grid.display) {\n            return 0;\n        }\n        const ticks = this.ticks;\n        const index = ticks.findIndex((t)=>t.value === value);\n        if (index >= 0) {\n            const opts = grid.setContext(this.getContext(index));\n            return opts.lineWidth;\n        }\n        return 0;\n    }\n drawGrid(chartArea) {\n        const grid = this.options.grid;\n        const ctx = this.ctx;\n        const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea));\n        let i, ilen;\n        const drawLine = (p1, p2, style)=>{\n            if (!style.width || !style.color) {\n                return;\n            }\n            ctx.save();\n            ctx.lineWidth = style.width;\n            ctx.strokeStyle = style.color;\n            ctx.setLineDash(style.borderDash || []);\n            ctx.lineDashOffset = style.borderDashOffset;\n            ctx.beginPath();\n            ctx.moveTo(p1.x, p1.y);\n            ctx.lineTo(p2.x, p2.y);\n            ctx.stroke();\n            ctx.restore();\n        };\n        if (grid.display) {\n            for(i = 0, ilen = items.length; i < ilen; ++i){\n                const item = items[i];\n                if (grid.drawOnChartArea) {\n                    drawLine({\n                        x: item.x1,\n                        y: item.y1\n                    }, {\n                        x: item.x2,\n                        y: item.y2\n                    }, item);\n                }\n                if (grid.drawTicks) {\n                    drawLine({\n                        x: item.tx1,\n                        y: item.ty1\n                    }, {\n                        x: item.tx2,\n                        y: item.ty2\n                    }, {\n                        color: item.tickColor,\n                        width: item.tickWidth,\n                        borderDash: item.tickBorderDash,\n                        borderDashOffset: item.tickBorderDashOffset\n                    });\n                }\n            }\n        }\n    }\n drawBorder() {\n        const { chart , ctx , options: { border , grid  }  } = this;\n        const borderOpts = border.setContext(this.getContext());\n        const axisWidth = border.display ? borderOpts.width : 0;\n        if (!axisWidth) {\n            return;\n        }\n        const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth;\n        const borderValue = this._borderValue;\n        let x1, x2, y1, y2;\n        if (this.isHorizontal()) {\n            x1 = helpers_segment._alignPixel(chart, this.left, axisWidth) - axisWidth / 2;\n            x2 = helpers_segment._alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2;\n            y1 = y2 = borderValue;\n        } else {\n            y1 = helpers_segment._alignPixel(chart, this.top, axisWidth) - axisWidth / 2;\n            y2 = helpers_segment._alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2;\n            x1 = x2 = borderValue;\n        }\n        ctx.save();\n        ctx.lineWidth = borderOpts.width;\n        ctx.strokeStyle = borderOpts.color;\n        ctx.beginPath();\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n        ctx.stroke();\n        ctx.restore();\n    }\n drawLabels(chartArea) {\n        const optionTicks = this.options.ticks;\n        if (!optionTicks.display) {\n            return;\n        }\n        const ctx = this.ctx;\n        const area = this._computeLabelArea();\n        if (area) {\n            helpers_segment.clipArea(ctx, area);\n        }\n        const items = this.getLabelItems(chartArea);\n        for (const item of items){\n            const renderTextOptions = item.options;\n            const tickFont = item.font;\n            const label = item.label;\n            const y = item.textOffset;\n            helpers_segment.renderText(ctx, label, 0, y, tickFont, renderTextOptions);\n        }\n        if (area) {\n            helpers_segment.unclipArea(ctx);\n        }\n    }\n drawTitle() {\n        const { ctx , options: { position , title , reverse  }  } = this;\n        if (!title.display) {\n            return;\n        }\n        const font = helpers_segment.toFont(title.font);\n        const padding = helpers_segment.toPadding(title.padding);\n        const align = title.align;\n        let offset = font.lineHeight / 2;\n        if (position === 'bottom' || position === 'center' || helpers_segment.isObject(position)) {\n            offset += padding.bottom;\n            if (helpers_segment.isArray(title.text)) {\n                offset += font.lineHeight * (title.text.length - 1);\n            }\n        } else {\n            offset += padding.top;\n        }\n        const { titleX , titleY , maxWidth , rotation  } = titleArgs(this, offset, position, align);\n        helpers_segment.renderText(ctx, title.text, 0, 0, font, {\n            color: title.color,\n            maxWidth,\n            rotation,\n            textAlign: titleAlign(align, position, reverse),\n            textBaseline: 'middle',\n            translation: [\n                titleX,\n                titleY\n            ]\n        });\n    }\n    draw(chartArea) {\n        if (!this._isVisible()) {\n            return;\n        }\n        this.drawBackground();\n        this.drawGrid(chartArea);\n        this.drawBorder();\n        this.drawTitle();\n        this.drawLabels(chartArea);\n    }\n _layers() {\n        const opts = this.options;\n        const tz = opts.ticks && opts.ticks.z || 0;\n        const gz = helpers_segment.valueOrDefault(opts.grid && opts.grid.z, -1);\n        const bz = helpers_segment.valueOrDefault(opts.border && opts.border.z, 0);\n        if (!this._isVisible() || this.draw !== Scale.prototype.draw) {\n            return [\n                {\n                    z: tz,\n                    draw: (chartArea)=>{\n                        this.draw(chartArea);\n                    }\n                }\n            ];\n        }\n        return [\n            {\n                z: gz,\n                draw: (chartArea)=>{\n                    this.drawBackground();\n                    this.drawGrid(chartArea);\n                    this.drawTitle();\n                }\n            },\n            {\n                z: bz,\n                draw: ()=>{\n                    this.drawBorder();\n                }\n            },\n            {\n                z: tz,\n                draw: (chartArea)=>{\n                    this.drawLabels(chartArea);\n                }\n            }\n        ];\n    }\n getMatchingVisibleMetas(type) {\n        const metas = this.chart.getSortedVisibleDatasetMetas();\n        const axisID = this.axis + 'AxisID';\n        const result = [];\n        let i, ilen;\n        for(i = 0, ilen = metas.length; i < ilen; ++i){\n            const meta = metas[i];\n            if (meta[axisID] === this.id && (!type || meta.type === type)) {\n                result.push(meta);\n            }\n        }\n        return result;\n    }\n _resolveTickFontOptions(index) {\n        const opts = this.options.ticks.setContext(this.getContext(index));\n        return helpers_segment.toFont(opts.font);\n    }\n _maxDigits() {\n        const fontSize = this._resolveTickFontOptions(0).lineHeight;\n        return (this.isHorizontal() ? this.width : this.height) / fontSize;\n    }\n}\n\nclass TypedRegistry {\n    constructor(type, scope, override){\n        this.type = type;\n        this.scope = scope;\n        this.override = override;\n        this.items = Object.create(null);\n    }\n    isForType(type) {\n        return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);\n    }\n register(item) {\n        const proto = Object.getPrototypeOf(item);\n        let parentScope;\n        if (isIChartComponent(proto)) {\n            parentScope = this.register(proto);\n        }\n        const items = this.items;\n        const id = item.id;\n        const scope = this.scope + '.' + id;\n        if (!id) {\n            throw new Error('class does not have id: ' + item);\n        }\n        if (id in items) {\n            return scope;\n        }\n        items[id] = item;\n        registerDefaults(item, scope, parentScope);\n        if (this.override) {\n            helpers_segment.defaults.override(item.id, item.overrides);\n        }\n        return scope;\n    }\n get(id) {\n        return this.items[id];\n    }\n unregister(item) {\n        const items = this.items;\n        const id = item.id;\n        const scope = this.scope;\n        if (id in items) {\n            delete items[id];\n        }\n        if (scope && id in helpers_segment.defaults[scope]) {\n            delete helpers_segment.defaults[scope][id];\n            if (this.override) {\n                delete helpers_segment.overrides[id];\n            }\n        }\n    }\n}\nfunction registerDefaults(item, scope, parentScope) {\n    const itemDefaults = helpers_segment.merge(Object.create(null), [\n        parentScope ? helpers_segment.defaults.get(parentScope) : {},\n        helpers_segment.defaults.get(scope),\n        item.defaults\n    ]);\n    helpers_segment.defaults.set(scope, itemDefaults);\n    if (item.defaultRoutes) {\n        routeDefaults(scope, item.defaultRoutes);\n    }\n    if (item.descriptors) {\n        helpers_segment.defaults.describe(scope, item.descriptors);\n    }\n}\nfunction routeDefaults(scope, routes) {\n    Object.keys(routes).forEach((property)=>{\n        const propertyParts = property.split('.');\n        const sourceName = propertyParts.pop();\n        const sourceScope = [\n            scope\n        ].concat(propertyParts).join('.');\n        const parts = routes[property].split('.');\n        const targetName = parts.pop();\n        const targetScope = parts.join('.');\n        helpers_segment.defaults.route(sourceScope, sourceName, targetScope, targetName);\n    });\n}\nfunction isIChartComponent(proto) {\n    return 'id' in proto && 'defaults' in proto;\n}\n\nclass Registry {\n    constructor(){\n        this.controllers = new TypedRegistry(DatasetController, 'datasets', true);\n        this.elements = new TypedRegistry(Element, 'elements');\n        this.plugins = new TypedRegistry(Object, 'plugins');\n        this.scales = new TypedRegistry(Scale, 'scales');\n        this._typedRegistries = [\n            this.controllers,\n            this.scales,\n            this.elements\n        ];\n    }\n add(...args) {\n        this._each('register', args);\n    }\n    remove(...args) {\n        this._each('unregister', args);\n    }\n addControllers(...args) {\n        this._each('register', args, this.controllers);\n    }\n addElements(...args) {\n        this._each('register', args, this.elements);\n    }\n addPlugins(...args) {\n        this._each('register', args, this.plugins);\n    }\n addScales(...args) {\n        this._each('register', args, this.scales);\n    }\n getController(id) {\n        return this._get(id, this.controllers, 'controller');\n    }\n getElement(id) {\n        return this._get(id, this.elements, 'element');\n    }\n getPlugin(id) {\n        return this._get(id, this.plugins, 'plugin');\n    }\n getScale(id) {\n        return this._get(id, this.scales, 'scale');\n    }\n removeControllers(...args) {\n        this._each('unregister', args, this.controllers);\n    }\n removeElements(...args) {\n        this._each('unregister', args, this.elements);\n    }\n removePlugins(...args) {\n        this._each('unregister', args, this.plugins);\n    }\n removeScales(...args) {\n        this._each('unregister', args, this.scales);\n    }\n _each(method, args, typedRegistry) {\n        [\n            ...args\n        ].forEach((arg)=>{\n            const reg = typedRegistry || this._getRegistryForType(arg);\n            if (typedRegistry || reg.isForType(arg) || reg === this.plugins && arg.id) {\n                this._exec(method, reg, arg);\n            } else {\n                helpers_segment.each(arg, (item)=>{\n                    const itemReg = typedRegistry || this._getRegistryForType(item);\n                    this._exec(method, itemReg, item);\n                });\n            }\n        });\n    }\n _exec(method, registry, component) {\n        const camelMethod = helpers_segment._capitalize(method);\n        helpers_segment.callback(component['before' + camelMethod], [], component);\n        registry[method](component);\n        helpers_segment.callback(component['after' + camelMethod], [], component);\n    }\n _getRegistryForType(type) {\n        for(let i = 0; i < this._typedRegistries.length; i++){\n            const reg = this._typedRegistries[i];\n            if (reg.isForType(type)) {\n                return reg;\n            }\n        }\n        return this.plugins;\n    }\n _get(id, typedRegistry, type) {\n        const item = typedRegistry.get(id);\n        if (item === undefined) {\n            throw new Error('\"' + id + '\" is not a registered ' + type + '.');\n        }\n        return item;\n    }\n}\nvar registry = /* #__PURE__ */ new Registry();\n\nclass PluginService {\n    constructor(){\n        this._init = [];\n    }\n notify(chart, hook, args, filter) {\n        if (hook === 'beforeInit') {\n            this._init = this._createDescriptors(chart, true);\n            this._notify(this._init, chart, 'install');\n        }\n        const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);\n        const result = this._notify(descriptors, chart, hook, args);\n        if (hook === 'afterDestroy') {\n            this._notify(descriptors, chart, 'stop');\n            this._notify(this._init, chart, 'uninstall');\n        }\n        return result;\n    }\n _notify(descriptors, chart, hook, args) {\n        args = args || {};\n        for (const descriptor of descriptors){\n            const plugin = descriptor.plugin;\n            const method = plugin[hook];\n            const params = [\n                chart,\n                args,\n                descriptor.options\n            ];\n            if (helpers_segment.callback(method, params, plugin) === false && args.cancelable) {\n                return false;\n            }\n        }\n        return true;\n    }\n    invalidate() {\n        if (!helpers_segment.isNullOrUndef(this._cache)) {\n            this._oldCache = this._cache;\n            this._cache = undefined;\n        }\n    }\n _descriptors(chart) {\n        if (this._cache) {\n            return this._cache;\n        }\n        const descriptors = this._cache = this._createDescriptors(chart);\n        this._notifyStateChanges(chart);\n        return descriptors;\n    }\n    _createDescriptors(chart, all) {\n        const config = chart && chart.config;\n        const options = helpers_segment.valueOrDefault(config.options && config.options.plugins, {});\n        const plugins = allPlugins(config);\n        return options === false && !all ? [] : createDescriptors(chart, plugins, options, all);\n    }\n _notifyStateChanges(chart) {\n        const previousDescriptors = this._oldCache || [];\n        const descriptors = this._cache;\n        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.plugin.id === y.plugin.id));\n        this._notify(diff(previousDescriptors, descriptors), chart, 'stop');\n        this._notify(diff(descriptors, previousDescriptors), chart, 'start');\n    }\n}\n function allPlugins(config) {\n    const localIds = {};\n    const plugins = [];\n    const keys = Object.keys(registry.plugins.items);\n    for(let i = 0; i < keys.length; i++){\n        plugins.push(registry.getPlugin(keys[i]));\n    }\n    const local = config.plugins || [];\n    for(let i1 = 0; i1 < local.length; i1++){\n        const plugin = local[i1];\n        if (plugins.indexOf(plugin) === -1) {\n            plugins.push(plugin);\n            localIds[plugin.id] = true;\n        }\n    }\n    return {\n        plugins,\n        localIds\n    };\n}\nfunction getOpts(options, all) {\n    if (!all && options === false) {\n        return null;\n    }\n    if (options === true) {\n        return {};\n    }\n    return options;\n}\nfunction createDescriptors(chart, { plugins , localIds  }, options, all) {\n    const result = [];\n    const context = chart.getContext();\n    for (const plugin of plugins){\n        const id = plugin.id;\n        const opts = getOpts(options[id], all);\n        if (opts === null) {\n            continue;\n        }\n        result.push({\n            plugin,\n            options: pluginOpts(chart.config, {\n                plugin,\n                local: localIds[id]\n            }, opts, context)\n        });\n    }\n    return result;\n}\nfunction pluginOpts(config, { plugin , local  }, opts, context) {\n    const keys = config.pluginScopeKeys(plugin);\n    const scopes = config.getOptionScopes(opts, keys);\n    if (local && plugin.defaults) {\n        scopes.push(plugin.defaults);\n    }\n    return config.createResolver(scopes, context, [\n        ''\n    ], {\n        scriptable: false,\n        indexable: false,\n        allKeys: true\n    });\n}\n\nfunction getIndexAxis(type, options) {\n    const datasetDefaults = helpers_segment.defaults.datasets[type] || {};\n    const datasetOptions = (options.datasets || {})[type] || {};\n    return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';\n}\nfunction getAxisFromDefaultScaleID(id, indexAxis) {\n    let axis = id;\n    if (id === '_index_') {\n        axis = indexAxis;\n    } else if (id === '_value_') {\n        axis = indexAxis === 'x' ? 'y' : 'x';\n    }\n    return axis;\n}\nfunction getDefaultScaleIDFromAxis(axis, indexAxis) {\n    return axis === indexAxis ? '_index_' : '_value_';\n}\nfunction axisFromPosition(position) {\n    if (position === 'top' || position === 'bottom') {\n        return 'x';\n    }\n    if (position === 'left' || position === 'right') {\n        return 'y';\n    }\n}\nfunction determineAxis(id, scaleOptions) {\n    if (id === 'x' || id === 'y' || id === 'r') {\n        return id;\n    }\n    id = scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.length > 1 && determineAxis(id[0].toLowerCase(), scaleOptions);\n    if (id) {\n        return id;\n    }\n    throw new Error(`Cannot determine type of '${name}' axis. Please provide 'axis' or 'position' option.`);\n}\nfunction mergeScaleConfig(config, options) {\n    const chartDefaults = helpers_segment.overrides[config.type] || {\n        scales: {}\n    };\n    const configScales = options.scales || {};\n    const chartIndexAxis = getIndexAxis(config.type, options);\n    const scales = Object.create(null);\n    Object.keys(configScales).forEach((id)=>{\n        const scaleConf = configScales[id];\n        if (!helpers_segment.isObject(scaleConf)) {\n            return console.error(`Invalid scale configuration for scale: ${id}`);\n        }\n        if (scaleConf._proxy) {\n            return console.warn(`Ignoring resolver passed as options for scale: ${id}`);\n        }\n        const axis = determineAxis(id, scaleConf);\n        const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);\n        const defaultScaleOptions = chartDefaults.scales || {};\n        scales[id] = helpers_segment.mergeIf(Object.create(null), [\n            {\n                axis\n            },\n            scaleConf,\n            defaultScaleOptions[axis],\n            defaultScaleOptions[defaultId]\n        ]);\n    });\n    config.data.datasets.forEach((dataset)=>{\n        const type = dataset.type || config.type;\n        const indexAxis = dataset.indexAxis || getIndexAxis(type, options);\n        const datasetDefaults = helpers_segment.overrides[type] || {};\n        const defaultScaleOptions = datasetDefaults.scales || {};\n        Object.keys(defaultScaleOptions).forEach((defaultID)=>{\n            const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);\n            const id = dataset[axis + 'AxisID'] || axis;\n            scales[id] = scales[id] || Object.create(null);\n            helpers_segment.mergeIf(scales[id], [\n                {\n                    axis\n                },\n                configScales[id],\n                defaultScaleOptions[defaultID]\n            ]);\n        });\n    });\n    Object.keys(scales).forEach((key)=>{\n        const scale = scales[key];\n        helpers_segment.mergeIf(scale, [\n            helpers_segment.defaults.scales[scale.type],\n            helpers_segment.defaults.scale\n        ]);\n    });\n    return scales;\n}\nfunction initOptions(config) {\n    const options = config.options || (config.options = {});\n    options.plugins = helpers_segment.valueOrDefault(options.plugins, {});\n    options.scales = mergeScaleConfig(config, options);\n}\nfunction initData(data) {\n    data = data || {};\n    data.datasets = data.datasets || [];\n    data.labels = data.labels || [];\n    return data;\n}\nfunction initConfig(config) {\n    config = config || {};\n    config.data = initData(config.data);\n    initOptions(config);\n    return config;\n}\nconst keyCache = new Map();\nconst keysCached = new Set();\nfunction cachedKeys(cacheKey, generate) {\n    let keys = keyCache.get(cacheKey);\n    if (!keys) {\n        keys = generate();\n        keyCache.set(cacheKey, keys);\n        keysCached.add(keys);\n    }\n    return keys;\n}\nconst addIfFound = (set, obj, key)=>{\n    const opts = helpers_segment.resolveObjectKey(obj, key);\n    if (opts !== undefined) {\n        set.add(opts);\n    }\n};\nclass Config {\n    constructor(config){\n        this._config = initConfig(config);\n        this._scopeCache = new Map();\n        this._resolverCache = new Map();\n    }\n    get platform() {\n        return this._config.platform;\n    }\n    get type() {\n        return this._config.type;\n    }\n    set type(type) {\n        this._config.type = type;\n    }\n    get data() {\n        return this._config.data;\n    }\n    set data(data) {\n        this._config.data = initData(data);\n    }\n    get options() {\n        return this._config.options;\n    }\n    set options(options) {\n        this._config.options = options;\n    }\n    get plugins() {\n        return this._config.plugins;\n    }\n    update() {\n        const config = this._config;\n        this.clearCache();\n        initOptions(config);\n    }\n    clearCache() {\n        this._scopeCache.clear();\n        this._resolverCache.clear();\n    }\n datasetScopeKeys(datasetType) {\n        return cachedKeys(datasetType, ()=>[\n                [\n                    `datasets.${datasetType}`,\n                    ''\n                ]\n            ]);\n    }\n datasetAnimationScopeKeys(datasetType, transition) {\n        return cachedKeys(`${datasetType}.transition.${transition}`, ()=>[\n                [\n                    `datasets.${datasetType}.transitions.${transition}`,\n                    `transitions.${transition}`\n                ],\n                [\n                    `datasets.${datasetType}`,\n                    ''\n                ]\n            ]);\n    }\n datasetElementScopeKeys(datasetType, elementType) {\n        return cachedKeys(`${datasetType}-${elementType}`, ()=>[\n                [\n                    `datasets.${datasetType}.elements.${elementType}`,\n                    `datasets.${datasetType}`,\n                    `elements.${elementType}`,\n                    ''\n                ]\n            ]);\n    }\n pluginScopeKeys(plugin) {\n        const id = plugin.id;\n        const type = this.type;\n        return cachedKeys(`${type}-plugin-${id}`, ()=>[\n                [\n                    `plugins.${id}`,\n                    ...plugin.additionalOptionScopes || []\n                ]\n            ]);\n    }\n _cachedScopes(mainScope, resetCache) {\n        const _scopeCache = this._scopeCache;\n        let cache = _scopeCache.get(mainScope);\n        if (!cache || resetCache) {\n            cache = new Map();\n            _scopeCache.set(mainScope, cache);\n        }\n        return cache;\n    }\n getOptionScopes(mainScope, keyLists, resetCache) {\n        const { options , type  } = this;\n        const cache = this._cachedScopes(mainScope, resetCache);\n        const cached = cache.get(keyLists);\n        if (cached) {\n            return cached;\n        }\n        const scopes = new Set();\n        keyLists.forEach((keys)=>{\n            if (mainScope) {\n                scopes.add(mainScope);\n                keys.forEach((key)=>addIfFound(scopes, mainScope, key));\n            }\n            keys.forEach((key)=>addIfFound(scopes, options, key));\n            keys.forEach((key)=>addIfFound(scopes, helpers_segment.overrides[type] || {}, key));\n            keys.forEach((key)=>addIfFound(scopes, helpers_segment.defaults, key));\n            keys.forEach((key)=>addIfFound(scopes, helpers_segment.descriptors, key));\n        });\n        const array = Array.from(scopes);\n        if (array.length === 0) {\n            array.push(Object.create(null));\n        }\n        if (keysCached.has(keyLists)) {\n            cache.set(keyLists, array);\n        }\n        return array;\n    }\n chartOptionScopes() {\n        const { options , type  } = this;\n        return [\n            options,\n            helpers_segment.overrides[type] || {},\n            helpers_segment.defaults.datasets[type] || {},\n            {\n                type\n            },\n            helpers_segment.defaults,\n            helpers_segment.descriptors\n        ];\n    }\n resolveNamedOptions(scopes, names, context, prefixes = [\n        ''\n    ]) {\n        const result = {\n            $shared: true\n        };\n        const { resolver , subPrefixes  } = getResolver(this._resolverCache, scopes, prefixes);\n        let options = resolver;\n        if (needContext(resolver, names)) {\n            result.$shared = false;\n            context = helpers_segment.isFunction(context) ? context() : context;\n            const subResolver = this.createResolver(scopes, context, subPrefixes);\n            options = helpers_segment._attachContext(resolver, context, subResolver);\n        }\n        for (const prop of names){\n            result[prop] = options[prop];\n        }\n        return result;\n    }\n createResolver(scopes, context, prefixes = [\n        ''\n    ], descriptorDefaults) {\n        const { resolver  } = getResolver(this._resolverCache, scopes, prefixes);\n        return helpers_segment.isObject(context) ? helpers_segment._attachContext(resolver, context, undefined, descriptorDefaults) : resolver;\n    }\n}\nfunction getResolver(resolverCache, scopes, prefixes) {\n    let cache = resolverCache.get(scopes);\n    if (!cache) {\n        cache = new Map();\n        resolverCache.set(scopes, cache);\n    }\n    const cacheKey = prefixes.join();\n    let cached = cache.get(cacheKey);\n    if (!cached) {\n        const resolver = helpers_segment._createResolver(scopes, prefixes);\n        cached = {\n            resolver,\n            subPrefixes: prefixes.filter((p)=>!p.toLowerCase().includes('hover'))\n        };\n        cache.set(cacheKey, cached);\n    }\n    return cached;\n}\nconst hasFunction = (value)=>helpers_segment.isObject(value) && Object.getOwnPropertyNames(value).reduce((acc, key)=>acc || helpers_segment.isFunction(value[key]), false);\nfunction needContext(proxy, names) {\n    const { isScriptable , isIndexable  } = helpers_segment._descriptors(proxy);\n    for (const prop of names){\n        const scriptable = isScriptable(prop);\n        const indexable = isIndexable(prop);\n        const value = (indexable || scriptable) && proxy[prop];\n        if (scriptable && (helpers_segment.isFunction(value) || hasFunction(value)) || indexable && helpers_segment.isArray(value)) {\n            return true;\n        }\n    }\n    return false;\n}\n\nvar version = \"4.2.1\";\n\nconst KNOWN_POSITIONS = [\n    'top',\n    'bottom',\n    'left',\n    'right',\n    'chartArea'\n];\nfunction positionIsHorizontal(position, axis) {\n    return position === 'top' || position === 'bottom' || KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x';\n}\nfunction compare2Level(l1, l2) {\n    return function(a, b) {\n        return a[l1] === b[l1] ? a[l2] - b[l2] : a[l1] - b[l1];\n    };\n}\nfunction onAnimationsComplete(context) {\n    const chart = context.chart;\n    const animationOptions = chart.options.animation;\n    chart.notifyPlugins('afterRender');\n    helpers_segment.callback(animationOptions && animationOptions.onComplete, [\n        context\n    ], chart);\n}\nfunction onAnimationProgress(context) {\n    const chart = context.chart;\n    const animationOptions = chart.options.animation;\n    helpers_segment.callback(animationOptions && animationOptions.onProgress, [\n        context\n    ], chart);\n}\n function getCanvas(item) {\n    if (helpers_segment._isDomSupported() && typeof item === 'string') {\n        item = document.getElementById(item);\n    } else if (item && item.length) {\n        item = item[0];\n    }\n    if (item && item.canvas) {\n        item = item.canvas;\n    }\n    return item;\n}\nconst instances = {};\nconst getChart = (key)=>{\n    const canvas = getCanvas(key);\n    return Object.values(instances).filter((c)=>c.canvas === canvas).pop();\n};\nfunction moveNumericKeys(obj, start, move) {\n    const keys = Object.keys(obj);\n    for (const key of keys){\n        const intKey = +key;\n        if (intKey >= start) {\n            const value = obj[key];\n            delete obj[key];\n            if (move > 0 || intKey > start) {\n                obj[intKey + move] = value;\n            }\n        }\n    }\n}\n function determineLastEvent(e, lastEvent, inChartArea, isClick) {\n    if (!inChartArea || e.type === 'mouseout') {\n        return null;\n    }\n    if (isClick) {\n        return lastEvent;\n    }\n    return e;\n}\nfunction getDatasetArea(meta) {\n    const { xScale , yScale  } = meta;\n    if (xScale && yScale) {\n        return {\n            left: xScale.left,\n            right: xScale.right,\n            top: yScale.top,\n            bottom: yScale.bottom\n        };\n    }\n}\nclass Chart {\n    static defaults = helpers_segment.defaults;\n    static instances = instances;\n    static overrides = helpers_segment.overrides;\n    static registry = registry;\n    static version = version;\n    static getChart = getChart;\n    static register(...items) {\n        registry.add(...items);\n        invalidatePlugins();\n    }\n    static unregister(...items) {\n        registry.remove(...items);\n        invalidatePlugins();\n    }\n    constructor(item, userConfig){\n        const config = this.config = new Config(userConfig);\n        const initialCanvas = getCanvas(item);\n        const existingChart = getChart(initialCanvas);\n        if (existingChart) {\n            throw new Error('Canvas is already in use. Chart with ID \\'' + existingChart.id + '\\'' + ' must be destroyed before the canvas with ID \\'' + existingChart.canvas.id + '\\' can be reused.');\n        }\n        const options = config.createResolver(config.chartOptionScopes(), this.getContext());\n        this.platform = new (config.platform || _detectPlatform(initialCanvas))();\n        this.platform.updateConfig(config);\n        const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);\n        const canvas = context && context.canvas;\n        const height = canvas && canvas.height;\n        const width = canvas && canvas.width;\n        this.id = helpers_segment.uid();\n        this.ctx = context;\n        this.canvas = canvas;\n        this.width = width;\n        this.height = height;\n        this._options = options;\n        this._aspectRatio = this.aspectRatio;\n        this._layers = [];\n        this._metasets = [];\n        this._stacks = undefined;\n        this.boxes = [];\n        this.currentDevicePixelRatio = undefined;\n        this.chartArea = undefined;\n        this._active = [];\n        this._lastEvent = undefined;\n        this._listeners = {};\n         this._responsiveListeners = undefined;\n        this._sortedMetasets = [];\n        this.scales = {};\n        this._plugins = new PluginService();\n        this.$proxies = {};\n        this._hiddenIndices = {};\n        this.attached = false;\n        this._animationsDisabled = undefined;\n        this.$context = undefined;\n        this._doResize = helpers_segment.debounce((mode)=>this.update(mode), options.resizeDelay || 0);\n        this._dataChanges = [];\n        instances[this.id] = this;\n        if (!context || !canvas) {\n            console.error(\"Failed to create chart: can't acquire context from the given item\");\n            return;\n        }\n        animator.listen(this, 'complete', onAnimationsComplete);\n        animator.listen(this, 'progress', onAnimationProgress);\n        this._initialize();\n        if (this.attached) {\n            this.update();\n        }\n    }\n    get aspectRatio() {\n        const { options: { aspectRatio , maintainAspectRatio  } , width , height , _aspectRatio  } = this;\n        if (!helpers_segment.isNullOrUndef(aspectRatio)) {\n            return aspectRatio;\n        }\n        if (maintainAspectRatio && _aspectRatio) {\n            return _aspectRatio;\n        }\n        return height ? width / height : null;\n    }\n    get data() {\n        return this.config.data;\n    }\n    set data(data) {\n        this.config.data = data;\n    }\n    get options() {\n        return this._options;\n    }\n    set options(options) {\n        this.config.options = options;\n    }\n    get registry() {\n        return registry;\n    }\n _initialize() {\n        this.notifyPlugins('beforeInit');\n        if (this.options.responsive) {\n            this.resize();\n        } else {\n            helpers_segment.retinaScale(this, this.options.devicePixelRatio);\n        }\n        this.bindEvents();\n        this.notifyPlugins('afterInit');\n        return this;\n    }\n    clear() {\n        helpers_segment.clearCanvas(this.canvas, this.ctx);\n        return this;\n    }\n    stop() {\n        animator.stop(this);\n        return this;\n    }\n resize(width, height) {\n        if (!animator.running(this)) {\n            this._resize(width, height);\n        } else {\n            this._resizeBeforeDraw = {\n                width,\n                height\n            };\n        }\n    }\n    _resize(width, height) {\n        const options = this.options;\n        const canvas = this.canvas;\n        const aspectRatio = options.maintainAspectRatio && this.aspectRatio;\n        const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);\n        const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();\n        const mode = this.width ? 'resize' : 'attach';\n        this.width = newSize.width;\n        this.height = newSize.height;\n        this._aspectRatio = this.aspectRatio;\n        if (!helpers_segment.retinaScale(this, newRatio, true)) {\n            return;\n        }\n        this.notifyPlugins('resize', {\n            size: newSize\n        });\n        helpers_segment.callback(options.onResize, [\n            this,\n            newSize\n        ], this);\n        if (this.attached) {\n            if (this._doResize(mode)) {\n                this.render();\n            }\n        }\n    }\n    ensureScalesHaveIDs() {\n        const options = this.options;\n        const scalesOptions = options.scales || {};\n        helpers_segment.each(scalesOptions, (axisOptions, axisID)=>{\n            axisOptions.id = axisID;\n        });\n    }\n buildOrUpdateScales() {\n        const options = this.options;\n        const scaleOpts = options.scales;\n        const scales = this.scales;\n        const updated = Object.keys(scales).reduce((obj, id)=>{\n            obj[id] = false;\n            return obj;\n        }, {});\n        let items = [];\n        if (scaleOpts) {\n            items = items.concat(Object.keys(scaleOpts).map((id)=>{\n                const scaleOptions = scaleOpts[id];\n                const axis = determineAxis(id, scaleOptions);\n                const isRadial = axis === 'r';\n                const isHorizontal = axis === 'x';\n                return {\n                    options: scaleOptions,\n                    dposition: isRadial ? 'chartArea' : isHorizontal ? 'bottom' : 'left',\n                    dtype: isRadial ? 'radialLinear' : isHorizontal ? 'category' : 'linear'\n                };\n            }));\n        }\n        helpers_segment.each(items, (item)=>{\n            const scaleOptions = item.options;\n            const id = scaleOptions.id;\n            const axis = determineAxis(id, scaleOptions);\n            const scaleType = helpers_segment.valueOrDefault(scaleOptions.type, item.dtype);\n            if (scaleOptions.position === undefined || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) {\n                scaleOptions.position = item.dposition;\n            }\n            updated[id] = true;\n            let scale = null;\n            if (id in scales && scales[id].type === scaleType) {\n                scale = scales[id];\n            } else {\n                const scaleClass = registry.getScale(scaleType);\n                scale = new scaleClass({\n                    id,\n                    type: scaleType,\n                    ctx: this.ctx,\n                    chart: this\n                });\n                scales[scale.id] = scale;\n            }\n            scale.init(scaleOptions, options);\n        });\n        helpers_segment.each(updated, (hasUpdated, id)=>{\n            if (!hasUpdated) {\n                delete scales[id];\n            }\n        });\n        helpers_segment.each(scales, (scale)=>{\n            layouts.configure(this, scale, scale.options);\n            layouts.addBox(this, scale);\n        });\n    }\n _updateMetasets() {\n        const metasets = this._metasets;\n        const numData = this.data.datasets.length;\n        const numMeta = metasets.length;\n        metasets.sort((a, b)=>a.index - b.index);\n        if (numMeta > numData) {\n            for(let i = numData; i < numMeta; ++i){\n                this._destroyDatasetMeta(i);\n            }\n            metasets.splice(numData, numMeta - numData);\n        }\n        this._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));\n    }\n _removeUnreferencedMetasets() {\n        const { _metasets: metasets , data: { datasets  }  } = this;\n        if (metasets.length > datasets.length) {\n            delete this._stacks;\n        }\n        metasets.forEach((meta, index)=>{\n            if (datasets.filter((x)=>x === meta._dataset).length === 0) {\n                this._destroyDatasetMeta(index);\n            }\n        });\n    }\n    buildOrUpdateControllers() {\n        const newControllers = [];\n        const datasets = this.data.datasets;\n        let i, ilen;\n        this._removeUnreferencedMetasets();\n        for(i = 0, ilen = datasets.length; i < ilen; i++){\n            const dataset = datasets[i];\n            let meta = this.getDatasetMeta(i);\n            const type = dataset.type || this.config.type;\n            if (meta.type && meta.type !== type) {\n                this._destroyDatasetMeta(i);\n                meta = this.getDatasetMeta(i);\n            }\n            meta.type = type;\n            meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);\n            meta.order = dataset.order || 0;\n            meta.index = i;\n            meta.label = '' + dataset.label;\n            meta.visible = this.isDatasetVisible(i);\n            if (meta.controller) {\n                meta.controller.updateIndex(i);\n                meta.controller.linkScales();\n            } else {\n                const ControllerClass = registry.getController(type);\n                const { datasetElementType , dataElementType  } = helpers_segment.defaults.datasets[type];\n                Object.assign(ControllerClass, {\n                    dataElementType: registry.getElement(dataElementType),\n                    datasetElementType: datasetElementType && registry.getElement(datasetElementType)\n                });\n                meta.controller = new ControllerClass(this, i);\n                newControllers.push(meta.controller);\n            }\n        }\n        this._updateMetasets();\n        return newControllers;\n    }\n _resetElements() {\n        helpers_segment.each(this.data.datasets, (dataset, datasetIndex)=>{\n            this.getDatasetMeta(datasetIndex).controller.reset();\n        }, this);\n    }\n reset() {\n        this._resetElements();\n        this.notifyPlugins('reset');\n    }\n    update(mode) {\n        const config = this.config;\n        config.update();\n        const options = this._options = config.createResolver(config.chartOptionScopes(), this.getContext());\n        const animsDisabled = this._animationsDisabled = !options.animation;\n        this._updateScales();\n        this._checkEventBindings();\n        this._updateHiddenIndices();\n        this._plugins.invalidate();\n        if (this.notifyPlugins('beforeUpdate', {\n            mode,\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        const newControllers = this.buildOrUpdateControllers();\n        this.notifyPlugins('beforeElementsUpdate');\n        let minPadding = 0;\n        for(let i = 0, ilen = this.data.datasets.length; i < ilen; i++){\n            const { controller  } = this.getDatasetMeta(i);\n            const reset = !animsDisabled && newControllers.indexOf(controller) === -1;\n            controller.buildOrUpdateElements(reset);\n            minPadding = Math.max(+controller.getMaxOverflow(), minPadding);\n        }\n        minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0;\n        this._updateLayout(minPadding);\n        if (!animsDisabled) {\n            helpers_segment.each(newControllers, (controller)=>{\n                controller.reset();\n            });\n        }\n        this._updateDatasets(mode);\n        this.notifyPlugins('afterUpdate', {\n            mode\n        });\n        this._layers.sort(compare2Level('z', '_idx'));\n        const { _active , _lastEvent  } = this;\n        if (_lastEvent) {\n            this._eventHandler(_lastEvent, true);\n        } else if (_active.length) {\n            this._updateHoverStyles(_active, _active, true);\n        }\n        this.render();\n    }\n _updateScales() {\n        helpers_segment.each(this.scales, (scale)=>{\n            layouts.removeBox(this, scale);\n        });\n        this.ensureScalesHaveIDs();\n        this.buildOrUpdateScales();\n    }\n _checkEventBindings() {\n        const options = this.options;\n        const existingEvents = new Set(Object.keys(this._listeners));\n        const newEvents = new Set(options.events);\n        if (!helpers_segment.setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) {\n            this.unbindEvents();\n            this.bindEvents();\n        }\n    }\n _updateHiddenIndices() {\n        const { _hiddenIndices  } = this;\n        const changes = this._getUniformDataChanges() || [];\n        for (const { method , start , count  } of changes){\n            const move = method === '_removeElements' ? -count : count;\n            moveNumericKeys(_hiddenIndices, start, move);\n        }\n    }\n _getUniformDataChanges() {\n        const _dataChanges = this._dataChanges;\n        if (!_dataChanges || !_dataChanges.length) {\n            return;\n        }\n        this._dataChanges = [];\n        const datasetCount = this.data.datasets.length;\n        const makeSet = (idx)=>new Set(_dataChanges.filter((c)=>c[0] === idx).map((c, i)=>i + ',' + c.splice(1).join(',')));\n        const changeSet = makeSet(0);\n        for(let i = 1; i < datasetCount; i++){\n            if (!helpers_segment.setsEqual(changeSet, makeSet(i))) {\n                return;\n            }\n        }\n        return Array.from(changeSet).map((c)=>c.split(',')).map((a)=>({\n                method: a[1],\n                start: +a[2],\n                count: +a[3]\n            }));\n    }\n _updateLayout(minPadding) {\n        if (this.notifyPlugins('beforeLayout', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        layouts.update(this, this.width, this.height, minPadding);\n        const area = this.chartArea;\n        const noArea = area.width <= 0 || area.height <= 0;\n        this._layers = [];\n        helpers_segment.each(this.boxes, (box)=>{\n            if (noArea && box.position === 'chartArea') {\n                return;\n            }\n            if (box.configure) {\n                box.configure();\n            }\n            this._layers.push(...box._layers());\n        }, this);\n        this._layers.forEach((item, index)=>{\n            item._idx = index;\n        });\n        this.notifyPlugins('afterLayout');\n    }\n _updateDatasets(mode) {\n        if (this.notifyPlugins('beforeDatasetsUpdate', {\n            mode,\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){\n            this.getDatasetMeta(i).controller.configure();\n        }\n        for(let i1 = 0, ilen1 = this.data.datasets.length; i1 < ilen1; ++i1){\n            this._updateDataset(i1, helpers_segment.isFunction(mode) ? mode({\n                datasetIndex: i1\n            }) : mode);\n        }\n        this.notifyPlugins('afterDatasetsUpdate', {\n            mode\n        });\n    }\n _updateDataset(index, mode) {\n        const meta = this.getDatasetMeta(index);\n        const args = {\n            meta,\n            index,\n            mode,\n            cancelable: true\n        };\n        if (this.notifyPlugins('beforeDatasetUpdate', args) === false) {\n            return;\n        }\n        meta.controller._update(mode);\n        args.cancelable = false;\n        this.notifyPlugins('afterDatasetUpdate', args);\n    }\n    render() {\n        if (this.notifyPlugins('beforeRender', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        if (animator.has(this)) {\n            if (this.attached && !animator.running(this)) {\n                animator.start(this);\n            }\n        } else {\n            this.draw();\n            onAnimationsComplete({\n                chart: this\n            });\n        }\n    }\n    draw() {\n        let i;\n        if (this._resizeBeforeDraw) {\n            const { width , height  } = this._resizeBeforeDraw;\n            this._resize(width, height);\n            this._resizeBeforeDraw = null;\n        }\n        this.clear();\n        if (this.width <= 0 || this.height <= 0) {\n            return;\n        }\n        if (this.notifyPlugins('beforeDraw', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        const layers = this._layers;\n        for(i = 0; i < layers.length && layers[i].z <= 0; ++i){\n            layers[i].draw(this.chartArea);\n        }\n        this._drawDatasets();\n        for(; i < layers.length; ++i){\n            layers[i].draw(this.chartArea);\n        }\n        this.notifyPlugins('afterDraw');\n    }\n _getSortedDatasetMetas(filterVisible) {\n        const metasets = this._sortedMetasets;\n        const result = [];\n        let i, ilen;\n        for(i = 0, ilen = metasets.length; i < ilen; ++i){\n            const meta = metasets[i];\n            if (!filterVisible || meta.visible) {\n                result.push(meta);\n            }\n        }\n        return result;\n    }\n getSortedVisibleDatasetMetas() {\n        return this._getSortedDatasetMetas(true);\n    }\n _drawDatasets() {\n        if (this.notifyPlugins('beforeDatasetsDraw', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        const metasets = this.getSortedVisibleDatasetMetas();\n        for(let i = metasets.length - 1; i >= 0; --i){\n            this._drawDataset(metasets[i]);\n        }\n        this.notifyPlugins('afterDatasetsDraw');\n    }\n _drawDataset(meta) {\n        const ctx = this.ctx;\n        const clip = meta._clip;\n        const useClip = !clip.disabled;\n        const area = getDatasetArea(meta) || this.chartArea;\n        const args = {\n            meta,\n            index: meta.index,\n            cancelable: true\n        };\n        if (this.notifyPlugins('beforeDatasetDraw', args) === false) {\n            return;\n        }\n        if (useClip) {\n            helpers_segment.clipArea(ctx, {\n                left: clip.left === false ? 0 : area.left - clip.left,\n                right: clip.right === false ? this.width : area.right + clip.right,\n                top: clip.top === false ? 0 : area.top - clip.top,\n                bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom\n            });\n        }\n        meta.controller.draw();\n        if (useClip) {\n            helpers_segment.unclipArea(ctx);\n        }\n        args.cancelable = false;\n        this.notifyPlugins('afterDatasetDraw', args);\n    }\n isPointInArea(point) {\n        return helpers_segment._isPointInArea(point, this.chartArea, this._minPadding);\n    }\n    getElementsAtEventForMode(e, mode, options, useFinalPosition) {\n        const method = Interaction.modes[mode];\n        if (typeof method === 'function') {\n            return method(this, e, options, useFinalPosition);\n        }\n        return [];\n    }\n    getDatasetMeta(datasetIndex) {\n        const dataset = this.data.datasets[datasetIndex];\n        const metasets = this._metasets;\n        let meta = metasets.filter((x)=>x && x._dataset === dataset).pop();\n        if (!meta) {\n            meta = {\n                type: null,\n                data: [],\n                dataset: null,\n                controller: null,\n                hidden: null,\n                xAxisID: null,\n                yAxisID: null,\n                order: dataset && dataset.order || 0,\n                index: datasetIndex,\n                _dataset: dataset,\n                _parsed: [],\n                _sorted: false\n            };\n            metasets.push(meta);\n        }\n        return meta;\n    }\n    getContext() {\n        return this.$context || (this.$context = helpers_segment.createContext(null, {\n            chart: this,\n            type: 'chart'\n        }));\n    }\n    getVisibleDatasetCount() {\n        return this.getSortedVisibleDatasetMetas().length;\n    }\n    isDatasetVisible(datasetIndex) {\n        const dataset = this.data.datasets[datasetIndex];\n        if (!dataset) {\n            return false;\n        }\n        const meta = this.getDatasetMeta(datasetIndex);\n        return typeof meta.hidden === 'boolean' ? !meta.hidden : !dataset.hidden;\n    }\n    setDatasetVisibility(datasetIndex, visible) {\n        const meta = this.getDatasetMeta(datasetIndex);\n        meta.hidden = !visible;\n    }\n    toggleDataVisibility(index) {\n        this._hiddenIndices[index] = !this._hiddenIndices[index];\n    }\n    getDataVisibility(index) {\n        return !this._hiddenIndices[index];\n    }\n _updateVisibility(datasetIndex, dataIndex, visible) {\n        const mode = visible ? 'show' : 'hide';\n        const meta = this.getDatasetMeta(datasetIndex);\n        const anims = meta.controller._resolveAnimations(undefined, mode);\n        if (helpers_segment.defined(dataIndex)) {\n            meta.data[dataIndex].hidden = !visible;\n            this.update();\n        } else {\n            this.setDatasetVisibility(datasetIndex, visible);\n            anims.update(meta, {\n                visible\n            });\n            this.update((ctx)=>ctx.datasetIndex === datasetIndex ? mode : undefined);\n        }\n    }\n    hide(datasetIndex, dataIndex) {\n        this._updateVisibility(datasetIndex, dataIndex, false);\n    }\n    show(datasetIndex, dataIndex) {\n        this._updateVisibility(datasetIndex, dataIndex, true);\n    }\n _destroyDatasetMeta(datasetIndex) {\n        const meta = this._metasets[datasetIndex];\n        if (meta && meta.controller) {\n            meta.controller._destroy();\n        }\n        delete this._metasets[datasetIndex];\n    }\n    _stop() {\n        let i, ilen;\n        this.stop();\n        animator.remove(this);\n        for(i = 0, ilen = this.data.datasets.length; i < ilen; ++i){\n            this._destroyDatasetMeta(i);\n        }\n    }\n    destroy() {\n        this.notifyPlugins('beforeDestroy');\n        const { canvas , ctx  } = this;\n        this._stop();\n        this.config.clearCache();\n        if (canvas) {\n            this.unbindEvents();\n            helpers_segment.clearCanvas(canvas, ctx);\n            this.platform.releaseContext(ctx);\n            this.canvas = null;\n            this.ctx = null;\n        }\n        delete instances[this.id];\n        this.notifyPlugins('afterDestroy');\n    }\n    toBase64Image(...args) {\n        return this.canvas.toDataURL(...args);\n    }\n bindEvents() {\n        this.bindUserEvents();\n        if (this.options.responsive) {\n            this.bindResponsiveEvents();\n        } else {\n            this.attached = true;\n        }\n    }\n bindUserEvents() {\n        const listeners = this._listeners;\n        const platform = this.platform;\n        const _add = (type, listener)=>{\n            platform.addEventListener(this, type, listener);\n            listeners[type] = listener;\n        };\n        const listener = (e, x, y)=>{\n            e.offsetX = x;\n            e.offsetY = y;\n            this._eventHandler(e);\n        };\n        helpers_segment.each(this.options.events, (type)=>_add(type, listener));\n    }\n bindResponsiveEvents() {\n        if (!this._responsiveListeners) {\n            this._responsiveListeners = {};\n        }\n        const listeners = this._responsiveListeners;\n        const platform = this.platform;\n        const _add = (type, listener)=>{\n            platform.addEventListener(this, type, listener);\n            listeners[type] = listener;\n        };\n        const _remove = (type, listener)=>{\n            if (listeners[type]) {\n                platform.removeEventListener(this, type, listener);\n                delete listeners[type];\n            }\n        };\n        const listener = (width, height)=>{\n            if (this.canvas) {\n                this.resize(width, height);\n            }\n        };\n        let detached;\n        const attached = ()=>{\n            _remove('attach', attached);\n            this.attached = true;\n            this.resize();\n            _add('resize', listener);\n            _add('detach', detached);\n        };\n        detached = ()=>{\n            this.attached = false;\n            _remove('resize', listener);\n            this._stop();\n            this._resize(0, 0);\n            _add('attach', attached);\n        };\n        if (platform.isAttached(this.canvas)) {\n            attached();\n        } else {\n            detached();\n        }\n    }\n unbindEvents() {\n        helpers_segment.each(this._listeners, (listener, type)=>{\n            this.platform.removeEventListener(this, type, listener);\n        });\n        this._listeners = {};\n        helpers_segment.each(this._responsiveListeners, (listener, type)=>{\n            this.platform.removeEventListener(this, type, listener);\n        });\n        this._responsiveListeners = undefined;\n    }\n    updateHoverStyle(items, mode, enabled) {\n        const prefix = enabled ? 'set' : 'remove';\n        let meta, item, i, ilen;\n        if (mode === 'dataset') {\n            meta = this.getDatasetMeta(items[0].datasetIndex);\n            meta.controller['_' + prefix + 'DatasetHoverStyle']();\n        }\n        for(i = 0, ilen = items.length; i < ilen; ++i){\n            item = items[i];\n            const controller = item && this.getDatasetMeta(item.datasetIndex).controller;\n            if (controller) {\n                controller[prefix + 'HoverStyle'](item.element, item.datasetIndex, item.index);\n            }\n        }\n    }\n getActiveElements() {\n        return this._active || [];\n    }\n setActiveElements(activeElements) {\n        const lastActive = this._active || [];\n        const active = activeElements.map(({ datasetIndex , index  })=>{\n            const meta = this.getDatasetMeta(datasetIndex);\n            if (!meta) {\n                throw new Error('No dataset found at index ' + datasetIndex);\n            }\n            return {\n                datasetIndex,\n                element: meta.data[index],\n                index\n            };\n        });\n        const changed = !helpers_segment._elementsEqual(active, lastActive);\n        if (changed) {\n            this._active = active;\n            this._lastEvent = null;\n            this._updateHoverStyles(active, lastActive);\n        }\n    }\n notifyPlugins(hook, args, filter) {\n        return this._plugins.notify(this, hook, args, filter);\n    }\n isPluginEnabled(pluginId) {\n        return this._plugins._cache.filter((p)=>p.plugin.id === pluginId).length === 1;\n    }\n _updateHoverStyles(active, lastActive, replay) {\n        const hoverOptions = this.options.hover;\n        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.datasetIndex === y.datasetIndex && x.index === y.index));\n        const deactivated = diff(lastActive, active);\n        const activated = replay ? active : diff(active, lastActive);\n        if (deactivated.length) {\n            this.updateHoverStyle(deactivated, hoverOptions.mode, false);\n        }\n        if (activated.length && hoverOptions.mode) {\n            this.updateHoverStyle(activated, hoverOptions.mode, true);\n        }\n    }\n _eventHandler(e, replay) {\n        const args = {\n            event: e,\n            replay,\n            cancelable: true,\n            inChartArea: this.isPointInArea(e)\n        };\n        const eventFilter = (plugin)=>(plugin.options.events || this.options.events).includes(e.native.type);\n        if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) {\n            return;\n        }\n        const changed = this._handleEvent(e, replay, args.inChartArea);\n        args.cancelable = false;\n        this.notifyPlugins('afterEvent', args, eventFilter);\n        if (changed || args.changed) {\n            this.render();\n        }\n        return this;\n    }\n _handleEvent(e, replay, inChartArea) {\n        const { _active: lastActive = [] , options  } = this;\n        const useFinalPosition = replay;\n        const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition);\n        const isClick = helpers_segment._isClickEvent(e);\n        const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick);\n        if (inChartArea) {\n            this._lastEvent = null;\n            helpers_segment.callback(options.onHover, [\n                e,\n                active,\n                this\n            ], this);\n            if (isClick) {\n                helpers_segment.callback(options.onClick, [\n                    e,\n                    active,\n                    this\n                ], this);\n            }\n        }\n        const changed = !helpers_segment._elementsEqual(active, lastActive);\n        if (changed || replay) {\n            this._active = active;\n            this._updateHoverStyles(active, lastActive, replay);\n        }\n        this._lastEvent = lastEvent;\n        return changed;\n    }\n _getActiveElements(e, lastActive, inChartArea, useFinalPosition) {\n        if (e.type === 'mouseout') {\n            return [];\n        }\n        if (!inChartArea) {\n            return lastActive;\n        }\n        const hoverOptions = this.options.hover;\n        return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);\n    }\n}\nfunction invalidatePlugins() {\n    return helpers_segment.each(Chart.instances, (chart)=>chart._plugins.invalidate());\n}\n\nfunction clipArc(ctx, element, endAngle) {\n    const { startAngle , pixelMargin , x , y , outerRadius , innerRadius  } = element;\n    let angleMargin = pixelMargin / outerRadius;\n    // Draw an inner border by clipping the arc and drawing a double-width border\n    // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders\n    ctx.beginPath();\n    ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin);\n    if (innerRadius > pixelMargin) {\n        angleMargin = pixelMargin / innerRadius;\n        ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true);\n    } else {\n        ctx.arc(x, y, pixelMargin, endAngle + helpers_segment.HALF_PI, startAngle - helpers_segment.HALF_PI);\n    }\n    ctx.closePath();\n    ctx.clip();\n}\nfunction toRadiusCorners(value) {\n    return helpers_segment._readValueToProps(value, [\n        'outerStart',\n        'outerEnd',\n        'innerStart',\n        'innerEnd'\n    ]);\n}\n/**\n * Parse border radius from the provided options\n */ function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) {\n    const o = toRadiusCorners(arc.options.borderRadius);\n    const halfThickness = (outerRadius - innerRadius) / 2;\n    const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2);\n    // Outer limits are complicated. We want to compute the available angular distance at\n    // a radius of outerRadius - borderRadius because for small angular distances, this term limits.\n    // We compute at r = outerRadius - borderRadius because this circle defines the center of the border corners.\n    //\n    // If the borderRadius is large, that value can become negative.\n    // This causes the outer borders to lose their radius entirely, which is rather unexpected. To solve that, if borderRadius > outerRadius\n    // we know that the thickness term will dominate and compute the limits at that point\n    const computeOuterLimit = (val)=>{\n        const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2;\n        return helpers_segment._limitValue(val, 0, Math.min(halfThickness, outerArcLimit));\n    };\n    return {\n        outerStart: computeOuterLimit(o.outerStart),\n        outerEnd: computeOuterLimit(o.outerEnd),\n        innerStart: helpers_segment._limitValue(o.innerStart, 0, innerLimit),\n        innerEnd: helpers_segment._limitValue(o.innerEnd, 0, innerLimit)\n    };\n}\n/**\n * Convert (r, 𝜃) to (x, y)\n */ function rThetaToXY(r, theta, x, y) {\n    return {\n        x: x + r * Math.cos(theta),\n        y: y + r * Math.sin(theta)\n    };\n}\n/**\n * Path the arc, respecting border radius by separating into left and right halves.\n *\n *   Start      End\n *\n *    1--->a--->2    Outer\n *   /           \\\n *   8           3\n *   |           |\n *   |           |\n *   7           4\n *   \\           /\n *    6<---b<---5    Inner\n */ function pathArc(ctx, element, offset, spacing, end, circular) {\n    const { x , y , startAngle: start , pixelMargin , innerRadius: innerR  } = element;\n    const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);\n    const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0;\n    let spacingOffset = 0;\n    const alpha = end - start;\n    if (spacing) {\n        // When spacing is present, it is the same for all items\n        // So we adjust the start and end angle of the arc such that\n        // the distance is the same as it would be without the spacing\n        const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0;\n        const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0;\n        const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2;\n        const adjustedAngle = avNogSpacingRadius !== 0 ? alpha * avNogSpacingRadius / (avNogSpacingRadius + spacing) : alpha;\n        spacingOffset = (alpha - adjustedAngle) / 2;\n    }\n    const beta = Math.max(0.001, alpha * outerRadius - offset / helpers_segment.PI) / outerRadius;\n    const angleOffset = (alpha - beta) / 2;\n    const startAngle = start + angleOffset + spacingOffset;\n    const endAngle = end - angleOffset - spacingOffset;\n    const { outerStart , outerEnd , innerStart , innerEnd  } = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle);\n    const outerStartAdjustedRadius = outerRadius - outerStart;\n    const outerEndAdjustedRadius = outerRadius - outerEnd;\n    const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius;\n    const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius;\n    const innerStartAdjustedRadius = innerRadius + innerStart;\n    const innerEndAdjustedRadius = innerRadius + innerEnd;\n    const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius;\n    const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius;\n    ctx.beginPath();\n    if (circular) {\n        // The first arc segments from point 1 to point a to point 2\n        const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2;\n        ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle);\n        ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle);\n        // The corner segment from point 2 to point 3\n        if (outerEnd > 0) {\n            const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);\n            ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + helpers_segment.HALF_PI);\n        }\n        // The line from point 3 to point 4\n        const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);\n        ctx.lineTo(p4.x, p4.y);\n        // The corner segment from point 4 to point 5\n        if (innerEnd > 0) {\n            const pCenter1 = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);\n            ctx.arc(pCenter1.x, pCenter1.y, innerEnd, endAngle + helpers_segment.HALF_PI, innerEndAdjustedAngle + Math.PI);\n        }\n        // The inner arc from point 5 to point b to point 6\n        const innerMidAdjustedAngle = (endAngle - innerEnd / innerRadius + (startAngle + innerStart / innerRadius)) / 2;\n        ctx.arc(x, y, innerRadius, endAngle - innerEnd / innerRadius, innerMidAdjustedAngle, true);\n        ctx.arc(x, y, innerRadius, innerMidAdjustedAngle, startAngle + innerStart / innerRadius, true);\n        // The corner segment from point 6 to point 7\n        if (innerStart > 0) {\n            const pCenter2 = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);\n            ctx.arc(pCenter2.x, pCenter2.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - helpers_segment.HALF_PI);\n        }\n        // The line from point 7 to point 8\n        const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);\n        ctx.lineTo(p8.x, p8.y);\n        // The corner segment from point 8 to point 1\n        if (outerStart > 0) {\n            const pCenter3 = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);\n            ctx.arc(pCenter3.x, pCenter3.y, outerStart, startAngle - helpers_segment.HALF_PI, outerStartAdjustedAngle);\n        }\n    } else {\n        ctx.moveTo(x, y);\n        const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x;\n        const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y;\n        ctx.lineTo(outerStartX, outerStartY);\n        const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x;\n        const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y;\n        ctx.lineTo(outerEndX, outerEndY);\n    }\n    ctx.closePath();\n}\nfunction drawArc(ctx, element, offset, spacing, circular) {\n    const { fullCircles , startAngle , circumference  } = element;\n    let endAngle = element.endAngle;\n    if (fullCircles) {\n        pathArc(ctx, element, offset, spacing, endAngle, circular);\n        for(let i = 0; i < fullCircles; ++i){\n            ctx.fill();\n        }\n        if (!isNaN(circumference)) {\n            endAngle = startAngle + (circumference % helpers_segment.TAU || helpers_segment.TAU);\n        }\n    }\n    pathArc(ctx, element, offset, spacing, endAngle, circular);\n    ctx.fill();\n    return endAngle;\n}\nfunction drawBorder(ctx, element, offset, spacing, circular) {\n    const { fullCircles , startAngle , circumference , options  } = element;\n    const { borderWidth , borderJoinStyle  } = options;\n    const inner = options.borderAlign === 'inner';\n    if (!borderWidth) {\n        return;\n    }\n    if (inner) {\n        ctx.lineWidth = borderWidth * 2;\n        ctx.lineJoin = borderJoinStyle || 'round';\n    } else {\n        ctx.lineWidth = borderWidth;\n        ctx.lineJoin = borderJoinStyle || 'bevel';\n    }\n    let endAngle = element.endAngle;\n    if (fullCircles) {\n        pathArc(ctx, element, offset, spacing, endAngle, circular);\n        for(let i = 0; i < fullCircles; ++i){\n            ctx.stroke();\n        }\n        if (!isNaN(circumference)) {\n            endAngle = startAngle + (circumference % helpers_segment.TAU || helpers_segment.TAU);\n        }\n    }\n    if (inner) {\n        clipArc(ctx, element, endAngle);\n    }\n    if (!fullCircles) {\n        pathArc(ctx, element, offset, spacing, endAngle, circular);\n        ctx.stroke();\n    }\n}\nclass ArcElement extends Element {\n    static id = 'arc';\n    static defaults = {\n        borderAlign: 'center',\n        borderColor: '#fff',\n        borderJoinStyle: undefined,\n        borderRadius: 0,\n        borderWidth: 2,\n        offset: 0,\n        spacing: 0,\n        angle: undefined,\n        circular: true\n    };\n    static defaultRoutes = {\n        backgroundColor: 'backgroundColor'\n    };\n    constructor(cfg){\n        super();\n        this.options = undefined;\n        this.circumference = undefined;\n        this.startAngle = undefined;\n        this.endAngle = undefined;\n        this.innerRadius = undefined;\n        this.outerRadius = undefined;\n        this.pixelMargin = 0;\n        this.fullCircles = 0;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    inRange(chartX, chartY, useFinalPosition) {\n        const point = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        const { angle , distance  } = helpers_segment.getAngleFromPoint(point, {\n            x: chartX,\n            y: chartY\n        });\n        const { startAngle , endAngle , innerRadius , outerRadius , circumference  } = this.getProps([\n            'startAngle',\n            'endAngle',\n            'innerRadius',\n            'outerRadius',\n            'circumference'\n        ], useFinalPosition);\n        const rAdjust = this.options.spacing / 2;\n        const _circumference = helpers_segment.valueOrDefault(circumference, endAngle - startAngle);\n        const betweenAngles = _circumference >= helpers_segment.TAU || helpers_segment._angleBetween(angle, startAngle, endAngle);\n        const withinRadius = helpers_segment._isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust);\n        return betweenAngles && withinRadius;\n    }\n    getCenterPoint(useFinalPosition) {\n        const { x , y , startAngle , endAngle , innerRadius , outerRadius  } = this.getProps([\n            'x',\n            'y',\n            'startAngle',\n            'endAngle',\n            'innerRadius',\n            'outerRadius'\n        ], useFinalPosition);\n        const { offset , spacing  } = this.options;\n        const halfAngle = (startAngle + endAngle) / 2;\n        const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2;\n        return {\n            x: x + Math.cos(halfAngle) * halfRadius,\n            y: y + Math.sin(halfAngle) * halfRadius\n        };\n    }\n    tooltipPosition(useFinalPosition) {\n        return this.getCenterPoint(useFinalPosition);\n    }\n    draw(ctx) {\n        const { options , circumference  } = this;\n        const offset = (options.offset || 0) / 4;\n        const spacing = (options.spacing || 0) / 2;\n        const circular = options.circular;\n        this.pixelMargin = options.borderAlign === 'inner' ? 0.33 : 0;\n        this.fullCircles = circumference > helpers_segment.TAU ? Math.floor(circumference / helpers_segment.TAU) : 0;\n        if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) {\n            return;\n        }\n        ctx.save();\n        const halfAngle = (this.startAngle + this.endAngle) / 2;\n        ctx.translate(Math.cos(halfAngle) * offset, Math.sin(halfAngle) * offset);\n        const fix = 1 - Math.sin(Math.min(helpers_segment.PI, circumference || 0));\n        const radiusOffset = offset * fix;\n        ctx.fillStyle = options.backgroundColor;\n        ctx.strokeStyle = options.borderColor;\n        drawArc(ctx, this, radiusOffset, spacing, circular);\n        drawBorder(ctx, this, radiusOffset, spacing, circular);\n        ctx.restore();\n    }\n}\n\nfunction setStyle(ctx, options, style = options) {\n    ctx.lineCap = helpers_segment.valueOrDefault(style.borderCapStyle, options.borderCapStyle);\n    ctx.setLineDash(helpers_segment.valueOrDefault(style.borderDash, options.borderDash));\n    ctx.lineDashOffset = helpers_segment.valueOrDefault(style.borderDashOffset, options.borderDashOffset);\n    ctx.lineJoin = helpers_segment.valueOrDefault(style.borderJoinStyle, options.borderJoinStyle);\n    ctx.lineWidth = helpers_segment.valueOrDefault(style.borderWidth, options.borderWidth);\n    ctx.strokeStyle = helpers_segment.valueOrDefault(style.borderColor, options.borderColor);\n}\nfunction lineTo(ctx, previous, target) {\n    ctx.lineTo(target.x, target.y);\n}\nfunction getLineMethod(options) {\n    if (options.stepped) {\n        return helpers_segment._steppedLineTo;\n    }\n    if (options.tension || options.cubicInterpolationMode === 'monotone') {\n        return helpers_segment._bezierCurveTo;\n    }\n    return lineTo;\n}\nfunction pathVars(points, segment, params = {}) {\n    const count = points.length;\n    const { start: paramsStart = 0 , end: paramsEnd = count - 1  } = params;\n    const { start: segmentStart , end: segmentEnd  } = segment;\n    const start = Math.max(paramsStart, segmentStart);\n    const end = Math.min(paramsEnd, segmentEnd);\n    const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd;\n    return {\n        count,\n        start,\n        loop: segment.loop,\n        ilen: end < start && !outside ? count + end - start : end - start\n    };\n}\n function pathSegment(ctx, line, segment, params) {\n    const { points , options  } = line;\n    const { count , start , loop , ilen  } = pathVars(points, segment, params);\n    const lineMethod = getLineMethod(options);\n    let { move =true , reverse  } = params || {};\n    let i, point, prev;\n    for(i = 0; i <= ilen; ++i){\n        point = points[(start + (reverse ? ilen - i : i)) % count];\n        if (point.skip) {\n            continue;\n        } else if (move) {\n            ctx.moveTo(point.x, point.y);\n            move = false;\n        } else {\n            lineMethod(ctx, prev, point, reverse, options.stepped);\n        }\n        prev = point;\n    }\n    if (loop) {\n        point = points[(start + (reverse ? ilen : 0)) % count];\n        lineMethod(ctx, prev, point, reverse, options.stepped);\n    }\n    return !!loop;\n}\n function fastPathSegment(ctx, line, segment, params) {\n    const points = line.points;\n    const { count , start , ilen  } = pathVars(points, segment, params);\n    const { move =true , reverse  } = params || {};\n    let avgX = 0;\n    let countX = 0;\n    let i, point, prevX, minY, maxY, lastY;\n    const pointIndex = (index)=>(start + (reverse ? ilen - index : index)) % count;\n    const drawX = ()=>{\n        if (minY !== maxY) {\n            ctx.lineTo(avgX, maxY);\n            ctx.lineTo(avgX, minY);\n            ctx.lineTo(avgX, lastY);\n        }\n    };\n    if (move) {\n        point = points[pointIndex(0)];\n        ctx.moveTo(point.x, point.y);\n    }\n    for(i = 0; i <= ilen; ++i){\n        point = points[pointIndex(i)];\n        if (point.skip) {\n            continue;\n        }\n        const x = point.x;\n        const y = point.y;\n        const truncX = x | 0;\n        if (truncX === prevX) {\n            if (y < minY) {\n                minY = y;\n            } else if (y > maxY) {\n                maxY = y;\n            }\n            avgX = (countX * avgX + x) / ++countX;\n        } else {\n            drawX();\n            ctx.lineTo(x, y);\n            prevX = truncX;\n            countX = 0;\n            minY = maxY = y;\n        }\n        lastY = y;\n    }\n    drawX();\n}\n function _getSegmentMethod(line) {\n    const opts = line.options;\n    const borderDash = opts.borderDash && opts.borderDash.length;\n    const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== 'monotone' && !opts.stepped && !borderDash;\n    return useFastPath ? fastPathSegment : pathSegment;\n}\n function _getInterpolationMethod(options) {\n    if (options.stepped) {\n        return helpers_segment._steppedInterpolation;\n    }\n    if (options.tension || options.cubicInterpolationMode === 'monotone') {\n        return helpers_segment._bezierInterpolation;\n    }\n    return helpers_segment._pointInLine;\n}\nfunction strokePathWithCache(ctx, line, start, count) {\n    let path = line._path;\n    if (!path) {\n        path = line._path = new Path2D();\n        if (line.path(path, start, count)) {\n            path.closePath();\n        }\n    }\n    setStyle(ctx, line.options);\n    ctx.stroke(path);\n}\nfunction strokePathDirect(ctx, line, start, count) {\n    const { segments , options  } = line;\n    const segmentMethod = _getSegmentMethod(line);\n    for (const segment of segments){\n        setStyle(ctx, options, segment.style);\n        ctx.beginPath();\n        if (segmentMethod(ctx, line, segment, {\n            start,\n            end: start + count - 1\n        })) {\n            ctx.closePath();\n        }\n        ctx.stroke();\n    }\n}\nconst usePath2D = typeof Path2D === 'function';\nfunction draw(ctx, line, start, count) {\n    if (usePath2D && !line.options.segment) {\n        strokePathWithCache(ctx, line, start, count);\n    } else {\n        strokePathDirect(ctx, line, start, count);\n    }\n}\nclass LineElement extends Element {\n    static id = 'line';\n static defaults = {\n        borderCapStyle: 'butt',\n        borderDash: [],\n        borderDashOffset: 0,\n        borderJoinStyle: 'miter',\n        borderWidth: 3,\n        capBezierPoints: true,\n        cubicInterpolationMode: 'default',\n        fill: false,\n        spanGaps: false,\n        stepped: false,\n        tension: 0\n    };\n static defaultRoutes = {\n        backgroundColor: 'backgroundColor',\n        borderColor: 'borderColor'\n    };\n    static descriptors = {\n        _scriptable: true,\n        _indexable: (name)=>name !== 'borderDash' && name !== 'fill'\n    };\n    constructor(cfg){\n        super();\n        this.animated = true;\n        this.options = undefined;\n        this._chart = undefined;\n        this._loop = undefined;\n        this._fullLoop = undefined;\n        this._path = undefined;\n        this._points = undefined;\n        this._segments = undefined;\n        this._decimated = false;\n        this._pointsUpdated = false;\n        this._datasetIndex = undefined;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    updateControlPoints(chartArea, indexAxis) {\n        const options = this.options;\n        if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !this._pointsUpdated) {\n            const loop = options.spanGaps ? this._loop : this._fullLoop;\n            helpers_segment._updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);\n            this._pointsUpdated = true;\n        }\n    }\n    set points(points) {\n        this._points = points;\n        delete this._segments;\n        delete this._path;\n        this._pointsUpdated = false;\n    }\n    get points() {\n        return this._points;\n    }\n    get segments() {\n        return this._segments || (this._segments = helpers_segment._computeSegments(this, this.options.segment));\n    }\n first() {\n        const segments = this.segments;\n        const points = this.points;\n        return segments.length && points[segments[0].start];\n    }\n last() {\n        const segments = this.segments;\n        const points = this.points;\n        const count = segments.length;\n        return count && points[segments[count - 1].end];\n    }\n interpolate(point, property) {\n        const options = this.options;\n        const value = point[property];\n        const points = this.points;\n        const segments = helpers_segment._boundSegments(this, {\n            property,\n            start: value,\n            end: value\n        });\n        if (!segments.length) {\n            return;\n        }\n        const result = [];\n        const _interpolate = _getInterpolationMethod(options);\n        let i, ilen;\n        for(i = 0, ilen = segments.length; i < ilen; ++i){\n            const { start , end  } = segments[i];\n            const p1 = points[start];\n            const p2 = points[end];\n            if (p1 === p2) {\n                result.push(p1);\n                continue;\n            }\n            const t = Math.abs((value - p1[property]) / (p2[property] - p1[property]));\n            const interpolated = _interpolate(p1, p2, t, options.stepped);\n            interpolated[property] = point[property];\n            result.push(interpolated);\n        }\n        return result.length === 1 ? result[0] : result;\n    }\n pathSegment(ctx, segment, params) {\n        const segmentMethod = _getSegmentMethod(this);\n        return segmentMethod(ctx, this, segment, params);\n    }\n path(ctx, start, count) {\n        const segments = this.segments;\n        const segmentMethod = _getSegmentMethod(this);\n        let loop = this._loop;\n        start = start || 0;\n        count = count || this.points.length - start;\n        for (const segment of segments){\n            loop &= segmentMethod(ctx, this, segment, {\n                start,\n                end: start + count - 1\n            });\n        }\n        return !!loop;\n    }\n draw(ctx, chartArea, start, count) {\n        const options = this.options || {};\n        const points = this.points || [];\n        if (points.length && options.borderWidth) {\n            ctx.save();\n            draw(ctx, this, start, count);\n            ctx.restore();\n        }\n        if (this.animated) {\n            this._pointsUpdated = false;\n            this._path = undefined;\n        }\n    }\n}\n\nfunction inRange$1(el, pos, axis, useFinalPosition) {\n    const options = el.options;\n    const { [axis]: value  } = el.getProps([\n        axis\n    ], useFinalPosition);\n    return Math.abs(pos - value) < options.radius + options.hitRadius;\n}\nclass PointElement extends Element {\n    static id = 'point';\n    /**\n   * @type {any}\n   */ static defaults = {\n        borderWidth: 1,\n        hitRadius: 1,\n        hoverBorderWidth: 1,\n        hoverRadius: 4,\n        pointStyle: 'circle',\n        radius: 3,\n        rotation: 0\n    };\n    /**\n   * @type {any}\n   */ static defaultRoutes = {\n        backgroundColor: 'backgroundColor',\n        borderColor: 'borderColor'\n    };\n    constructor(cfg){\n        super();\n        this.options = undefined;\n        this.parsed = undefined;\n        this.skip = undefined;\n        this.stop = undefined;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    inRange(mouseX, mouseY, useFinalPosition) {\n        const options = this.options;\n        const { x , y  } = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        return Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2) < Math.pow(options.hitRadius + options.radius, 2);\n    }\n    inXRange(mouseX, useFinalPosition) {\n        return inRange$1(this, mouseX, 'x', useFinalPosition);\n    }\n    inYRange(mouseY, useFinalPosition) {\n        return inRange$1(this, mouseY, 'y', useFinalPosition);\n    }\n    getCenterPoint(useFinalPosition) {\n        const { x , y  } = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        return {\n            x,\n            y\n        };\n    }\n    size(options) {\n        options = options || this.options || {};\n        let radius = options.radius || 0;\n        radius = Math.max(radius, radius && options.hoverRadius || 0);\n        const borderWidth = radius && options.borderWidth || 0;\n        return (radius + borderWidth) * 2;\n    }\n    draw(ctx, area) {\n        const options = this.options;\n        if (this.skip || options.radius < 0.1 || !helpers_segment._isPointInArea(this, area, this.size(options) / 2)) {\n            return;\n        }\n        ctx.strokeStyle = options.borderColor;\n        ctx.lineWidth = options.borderWidth;\n        ctx.fillStyle = options.backgroundColor;\n        helpers_segment.drawPoint(ctx, options, this.x, this.y);\n    }\n    getRange() {\n        const options = this.options || {};\n        // @ts-expect-error Fallbacks should never be hit in practice\n        return options.radius + options.hitRadius;\n    }\n}\n\nfunction getBarBounds(bar, useFinalPosition) {\n    const { x , y , base , width , height  } =  bar.getProps([\n        'x',\n        'y',\n        'base',\n        'width',\n        'height'\n    ], useFinalPosition);\n    let left, right, top, bottom, half;\n    if (bar.horizontal) {\n        half = height / 2;\n        left = Math.min(x, base);\n        right = Math.max(x, base);\n        top = y - half;\n        bottom = y + half;\n    } else {\n        half = width / 2;\n        left = x - half;\n        right = x + half;\n        top = Math.min(y, base);\n        bottom = Math.max(y, base);\n    }\n    return {\n        left,\n        top,\n        right,\n        bottom\n    };\n}\nfunction skipOrLimit(skip, value, min, max) {\n    return skip ? 0 : helpers_segment._limitValue(value, min, max);\n}\nfunction parseBorderWidth(bar, maxW, maxH) {\n    const value = bar.options.borderWidth;\n    const skip = bar.borderSkipped;\n    const o = helpers_segment.toTRBL(value);\n    return {\n        t: skipOrLimit(skip.top, o.top, 0, maxH),\n        r: skipOrLimit(skip.right, o.right, 0, maxW),\n        b: skipOrLimit(skip.bottom, o.bottom, 0, maxH),\n        l: skipOrLimit(skip.left, o.left, 0, maxW)\n    };\n}\nfunction parseBorderRadius(bar, maxW, maxH) {\n    const { enableBorderRadius  } = bar.getProps([\n        'enableBorderRadius'\n    ]);\n    const value = bar.options.borderRadius;\n    const o = helpers_segment.toTRBLCorners(value);\n    const maxR = Math.min(maxW, maxH);\n    const skip = bar.borderSkipped;\n    const enableBorder = enableBorderRadius || helpers_segment.isObject(value);\n    return {\n        topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),\n        topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),\n        bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),\n        bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)\n    };\n}\nfunction boundingRects(bar) {\n    const bounds = getBarBounds(bar);\n    const width = bounds.right - bounds.left;\n    const height = bounds.bottom - bounds.top;\n    const border = parseBorderWidth(bar, width / 2, height / 2);\n    const radius = parseBorderRadius(bar, width / 2, height / 2);\n    return {\n        outer: {\n            x: bounds.left,\n            y: bounds.top,\n            w: width,\n            h: height,\n            radius\n        },\n        inner: {\n            x: bounds.left + border.l,\n            y: bounds.top + border.t,\n            w: width - border.l - border.r,\n            h: height - border.t - border.b,\n            radius: {\n                topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),\n                topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),\n                bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),\n                bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r))\n            }\n        }\n    };\n}\nfunction inRange(bar, x, y, useFinalPosition) {\n    const skipX = x === null;\n    const skipY = y === null;\n    const skipBoth = skipX && skipY;\n    const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);\n    return bounds && (skipX || helpers_segment._isBetween(x, bounds.left, bounds.right)) && (skipY || helpers_segment._isBetween(y, bounds.top, bounds.bottom));\n}\nfunction hasRadius(radius) {\n    return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;\n}\n function addNormalRectPath(ctx, rect) {\n    ctx.rect(rect.x, rect.y, rect.w, rect.h);\n}\nfunction inflateRect(rect, amount, refRect = {}) {\n    const x = rect.x !== refRect.x ? -amount : 0;\n    const y = rect.y !== refRect.y ? -amount : 0;\n    const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x;\n    const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y;\n    return {\n        x: rect.x + x,\n        y: rect.y + y,\n        w: rect.w + w,\n        h: rect.h + h,\n        radius: rect.radius\n    };\n}\nclass BarElement extends Element {\n    static id = 'bar';\n static defaults = {\n        borderSkipped: 'start',\n        borderWidth: 0,\n        borderRadius: 0,\n        inflateAmount: 'auto',\n        pointStyle: undefined\n    };\n static defaultRoutes = {\n        backgroundColor: 'backgroundColor',\n        borderColor: 'borderColor'\n    };\n    constructor(cfg){\n        super();\n        this.options = undefined;\n        this.horizontal = undefined;\n        this.base = undefined;\n        this.width = undefined;\n        this.height = undefined;\n        this.inflateAmount = undefined;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    draw(ctx) {\n        const { inflateAmount , options: { borderColor , backgroundColor  }  } = this;\n        const { inner , outer  } = boundingRects(this);\n        const addRectPath = hasRadius(outer.radius) ? helpers_segment.addRoundedRectPath : addNormalRectPath;\n        ctx.save();\n        if (outer.w !== inner.w || outer.h !== inner.h) {\n            ctx.beginPath();\n            addRectPath(ctx, inflateRect(outer, inflateAmount, inner));\n            ctx.clip();\n            addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));\n            ctx.fillStyle = borderColor;\n            ctx.fill('evenodd');\n        }\n        ctx.beginPath();\n        addRectPath(ctx, inflateRect(inner, inflateAmount));\n        ctx.fillStyle = backgroundColor;\n        ctx.fill();\n        ctx.restore();\n    }\n    inRange(mouseX, mouseY, useFinalPosition) {\n        return inRange(this, mouseX, mouseY, useFinalPosition);\n    }\n    inXRange(mouseX, useFinalPosition) {\n        return inRange(this, mouseX, null, useFinalPosition);\n    }\n    inYRange(mouseY, useFinalPosition) {\n        return inRange(this, null, mouseY, useFinalPosition);\n    }\n    getCenterPoint(useFinalPosition) {\n        const { x , y , base , horizontal  } =  this.getProps([\n            'x',\n            'y',\n            'base',\n            'horizontal'\n        ], useFinalPosition);\n        return {\n            x: horizontal ? (x + base) / 2 : x,\n            y: horizontal ? y : (y + base) / 2\n        };\n    }\n    getRange(axis) {\n        return axis === 'x' ? this.width / 2 : this.height / 2;\n    }\n}\n\nvar elements = /*#__PURE__*/Object.freeze({\n__proto__: null,\nArcElement: ArcElement,\nLineElement: LineElement,\nPointElement: PointElement,\nBarElement: BarElement\n});\n\nconst BORDER_COLORS = [\n    'rgb(54, 162, 235)',\n    'rgb(255, 99, 132)',\n    'rgb(255, 159, 64)',\n    'rgb(255, 205, 86)',\n    'rgb(75, 192, 192)',\n    'rgb(153, 102, 255)',\n    'rgb(201, 203, 207)' // grey\n];\n// Border colors with 50% transparency\nconst BACKGROUND_COLORS = /* #__PURE__ */ BORDER_COLORS.map((color)=>color.replace('rgb(', 'rgba(').replace(')', ', 0.5)'));\nfunction getBorderColor(i) {\n    return BORDER_COLORS[i % BORDER_COLORS.length];\n}\nfunction getBackgroundColor(i) {\n    return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length];\n}\nfunction colorizeDefaultDataset(dataset, i) {\n    dataset.borderColor = getBorderColor(i);\n    dataset.backgroundColor = getBackgroundColor(i);\n    return ++i;\n}\nfunction colorizeDoughnutDataset(dataset, i) {\n    dataset.backgroundColor = dataset.data.map(()=>getBorderColor(i++));\n    return i;\n}\nfunction colorizePolarAreaDataset(dataset, i) {\n    dataset.backgroundColor = dataset.data.map(()=>getBackgroundColor(i++));\n    return i;\n}\nfunction getColorizer(chart) {\n    let i = 0;\n    return (dataset, datasetIndex)=>{\n        const controller = chart.getDatasetMeta(datasetIndex).controller;\n        if (controller instanceof DoughnutController) {\n            i = colorizeDoughnutDataset(dataset, i);\n        } else if (controller instanceof PolarAreaController) {\n            i = colorizePolarAreaDataset(dataset, i);\n        } else if (controller) {\n            i = colorizeDefaultDataset(dataset, i);\n        }\n    };\n}\nfunction containsColorsDefinitions(descriptors) {\n    let k;\n    for(k in descriptors){\n        if (descriptors[k].borderColor || descriptors[k].backgroundColor) {\n            return true;\n        }\n    }\n    return false;\n}\nfunction containsColorsDefinition(descriptor) {\n    return descriptor && (descriptor.borderColor || descriptor.backgroundColor);\n}\nvar plugin_colors = {\n    id: 'colors',\n    defaults: {\n        enabled: true,\n        forceOverride: false\n    },\n    beforeLayout (chart, _args, options) {\n        if (!options.enabled) {\n            return;\n        }\n        const { data: { datasets  } , options: chartOptions  } = chart.config;\n        const { elements  } = chartOptions;\n        if (!options.forceOverride && (containsColorsDefinitions(datasets) || containsColorsDefinition(chartOptions) || elements && containsColorsDefinitions(elements))) {\n            return;\n        }\n        const colorizer = getColorizer(chart);\n        datasets.forEach(colorizer);\n    }\n};\n\nfunction lttbDecimation(data, start, count, availableWidth, options) {\n const samples = options.samples || availableWidth;\n    if (samples >= count) {\n        return data.slice(start, start + count);\n    }\n    const decimated = [];\n    const bucketWidth = (count - 2) / (samples - 2);\n    let sampledIndex = 0;\n    const endIndex = start + count - 1;\n    let a = start;\n    let i, maxAreaPoint, maxArea, area, nextA;\n    decimated[sampledIndex++] = data[a];\n    for(i = 0; i < samples - 2; i++){\n        let avgX = 0;\n        let avgY = 0;\n        let j;\n        const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start;\n        const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start;\n        const avgRangeLength = avgRangeEnd - avgRangeStart;\n        for(j = avgRangeStart; j < avgRangeEnd; j++){\n            avgX += data[j].x;\n            avgY += data[j].y;\n        }\n        avgX /= avgRangeLength;\n        avgY /= avgRangeLength;\n        const rangeOffs = Math.floor(i * bucketWidth) + 1 + start;\n        const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start;\n        const { x: pointAx , y: pointAy  } = data[a];\n        maxArea = area = -1;\n        for(j = rangeOffs; j < rangeTo; j++){\n            area = 0.5 * Math.abs((pointAx - avgX) * (data[j].y - pointAy) - (pointAx - data[j].x) * (avgY - pointAy));\n            if (area > maxArea) {\n                maxArea = area;\n                maxAreaPoint = data[j];\n                nextA = j;\n            }\n        }\n        decimated[sampledIndex++] = maxAreaPoint;\n        a = nextA;\n    }\n    decimated[sampledIndex++] = data[endIndex];\n    return decimated;\n}\nfunction minMaxDecimation(data, start, count, availableWidth) {\n    let avgX = 0;\n    let countX = 0;\n    let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY;\n    const decimated = [];\n    const endIndex = start + count - 1;\n    const xMin = data[start].x;\n    const xMax = data[endIndex].x;\n    const dx = xMax - xMin;\n    for(i = start; i < start + count; ++i){\n        point = data[i];\n        x = (point.x - xMin) / dx * availableWidth;\n        y = point.y;\n        const truncX = x | 0;\n        if (truncX === prevX) {\n            if (y < minY) {\n                minY = y;\n                minIndex = i;\n            } else if (y > maxY) {\n                maxY = y;\n                maxIndex = i;\n            }\n            avgX = (countX * avgX + point.x) / ++countX;\n        } else {\n            const lastIndex = i - 1;\n            if (!helpers_segment.isNullOrUndef(minIndex) && !helpers_segment.isNullOrUndef(maxIndex)) {\n                const intermediateIndex1 = Math.min(minIndex, maxIndex);\n                const intermediateIndex2 = Math.max(minIndex, maxIndex);\n                if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) {\n                    decimated.push({\n                        ...data[intermediateIndex1],\n                        x: avgX\n                    });\n                }\n                if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) {\n                    decimated.push({\n                        ...data[intermediateIndex2],\n                        x: avgX\n                    });\n                }\n            }\n            if (i > 0 && lastIndex !== startIndex) {\n                decimated.push(data[lastIndex]);\n            }\n            decimated.push(point);\n            prevX = truncX;\n            countX = 0;\n            minY = maxY = y;\n            minIndex = maxIndex = startIndex = i;\n        }\n    }\n    return decimated;\n}\nfunction cleanDecimatedDataset(dataset) {\n    if (dataset._decimated) {\n        const data = dataset._data;\n        delete dataset._decimated;\n        delete dataset._data;\n        Object.defineProperty(dataset, 'data', {\n            configurable: true,\n            enumerable: true,\n            writable: true,\n            value: data\n        });\n    }\n}\nfunction cleanDecimatedData(chart) {\n    chart.data.datasets.forEach((dataset)=>{\n        cleanDecimatedDataset(dataset);\n    });\n}\nfunction getStartAndCountOfVisiblePointsSimplified(meta, points) {\n    const pointCount = points.length;\n    let start = 0;\n    let count;\n    const { iScale  } = meta;\n    const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();\n    if (minDefined) {\n        start = helpers_segment._limitValue(helpers_segment._lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1);\n    }\n    if (maxDefined) {\n        count = helpers_segment._limitValue(helpers_segment._lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start;\n    } else {\n        count = pointCount - start;\n    }\n    return {\n        start,\n        count\n    };\n}\nvar plugin_decimation = {\n    id: 'decimation',\n    defaults: {\n        algorithm: 'min-max',\n        enabled: false\n    },\n    beforeElementsUpdate: (chart, args, options)=>{\n        if (!options.enabled) {\n            cleanDecimatedData(chart);\n            return;\n        }\n        const availableWidth = chart.width;\n        chart.data.datasets.forEach((dataset, datasetIndex)=>{\n            const { _data , indexAxis  } = dataset;\n            const meta = chart.getDatasetMeta(datasetIndex);\n            const data = _data || dataset.data;\n            if (helpers_segment.resolve([\n                indexAxis,\n                chart.options.indexAxis\n            ]) === 'y') {\n                return;\n            }\n            if (!meta.controller.supportsDecimation) {\n                return;\n            }\n            const xAxis = chart.scales[meta.xAxisID];\n            if (xAxis.type !== 'linear' && xAxis.type !== 'time') {\n                return;\n            }\n            if (chart.options.parsing) {\n                return;\n            }\n            let { start , count  } = getStartAndCountOfVisiblePointsSimplified(meta, data);\n            const threshold = options.threshold || 4 * availableWidth;\n            if (count <= threshold) {\n                cleanDecimatedDataset(dataset);\n                return;\n            }\n            if (helpers_segment.isNullOrUndef(_data)) {\n                dataset._data = data;\n                delete dataset.data;\n                Object.defineProperty(dataset, 'data', {\n                    configurable: true,\n                    enumerable: true,\n                    get: function() {\n                        return this._decimated;\n                    },\n                    set: function(d) {\n                        this._data = d;\n                    }\n                });\n            }\n            let decimated;\n            switch(options.algorithm){\n                case 'lttb':\n                    decimated = lttbDecimation(data, start, count, availableWidth, options);\n                    break;\n                case 'min-max':\n                    decimated = minMaxDecimation(data, start, count, availableWidth);\n                    break;\n                default:\n                    throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`);\n            }\n            dataset._decimated = decimated;\n        });\n    },\n    destroy (chart) {\n        cleanDecimatedData(chart);\n    }\n};\n\nfunction _segments(line, target, property) {\n    const segments = line.segments;\n    const points = line.points;\n    const tpoints = target.points;\n    const parts = [];\n    for (const segment of segments){\n        let { start , end  } = segment;\n        end = _findSegmentEnd(start, end, points);\n        const bounds = _getBounds(property, points[start], points[end], segment.loop);\n        if (!target.segments) {\n            parts.push({\n                source: segment,\n                target: bounds,\n                start: points[start],\n                end: points[end]\n            });\n            continue;\n        }\n        const targetSegments = helpers_segment._boundSegments(target, bounds);\n        for (const tgt of targetSegments){\n            const subBounds = _getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop);\n            const fillSources = helpers_segment._boundSegment(segment, points, subBounds);\n            for (const fillSource of fillSources){\n                parts.push({\n                    source: fillSource,\n                    target: tgt,\n                    start: {\n                        [property]: _getEdge(bounds, subBounds, 'start', Math.max)\n                    },\n                    end: {\n                        [property]: _getEdge(bounds, subBounds, 'end', Math.min)\n                    }\n                });\n            }\n        }\n    }\n    return parts;\n}\nfunction _getBounds(property, first, last, loop) {\n    if (loop) {\n        return;\n    }\n    let start = first[property];\n    let end = last[property];\n    if (property === 'angle') {\n        start = helpers_segment._normalizeAngle(start);\n        end = helpers_segment._normalizeAngle(end);\n    }\n    return {\n        property,\n        start,\n        end\n    };\n}\nfunction _pointsFromSegments(boundary, line) {\n    const { x =null , y =null  } = boundary || {};\n    const linePoints = line.points;\n    const points = [];\n    line.segments.forEach(({ start , end  })=>{\n        end = _findSegmentEnd(start, end, linePoints);\n        const first = linePoints[start];\n        const last = linePoints[end];\n        if (y !== null) {\n            points.push({\n                x: first.x,\n                y\n            });\n            points.push({\n                x: last.x,\n                y\n            });\n        } else if (x !== null) {\n            points.push({\n                x,\n                y: first.y\n            });\n            points.push({\n                x,\n                y: last.y\n            });\n        }\n    });\n    return points;\n}\nfunction _findSegmentEnd(start, end, points) {\n    for(; end > start; end--){\n        const point = points[end];\n        if (!isNaN(point.x) && !isNaN(point.y)) {\n            break;\n        }\n    }\n    return end;\n}\nfunction _getEdge(a, b, prop, fn) {\n    if (a && b) {\n        return fn(a[prop], b[prop]);\n    }\n    return a ? a[prop] : b ? b[prop] : 0;\n}\n\nfunction _createBoundaryLine(boundary, line) {\n    let points = [];\n    let _loop = false;\n    if (helpers_segment.isArray(boundary)) {\n        _loop = true;\n        points = boundary;\n    } else {\n        points = _pointsFromSegments(boundary, line);\n    }\n    return points.length ? new LineElement({\n        points,\n        options: {\n            tension: 0\n        },\n        _loop,\n        _fullLoop: _loop\n    }) : null;\n}\nfunction _shouldApplyFill(source) {\n    return source && source.fill !== false;\n}\n\nfunction _resolveTarget(sources, index, propagate) {\n    const source = sources[index];\n    let fill = source.fill;\n    const visited = [\n        index\n    ];\n    let target;\n    if (!propagate) {\n        return fill;\n    }\n    while(fill !== false && visited.indexOf(fill) === -1){\n        if (!helpers_segment.isNumberFinite(fill)) {\n            return fill;\n        }\n        target = sources[fill];\n        if (!target) {\n            return false;\n        }\n        if (target.visible) {\n            return fill;\n        }\n        visited.push(fill);\n        fill = target.fill;\n    }\n    return false;\n}\n function _decodeFill(line, index, count) {\n     const fill = parseFillOption(line);\n    if (helpers_segment.isObject(fill)) {\n        return isNaN(fill.value) ? false : fill;\n    }\n    let target = parseFloat(fill);\n    if (helpers_segment.isNumberFinite(target) && Math.floor(target) === target) {\n        return decodeTargetIndex(fill[0], index, target, count);\n    }\n    return [\n        'origin',\n        'start',\n        'end',\n        'stack',\n        'shape'\n    ].indexOf(fill) >= 0 && fill;\n}\nfunction decodeTargetIndex(firstCh, index, target, count) {\n    if (firstCh === '-' || firstCh === '+') {\n        target = index + target;\n    }\n    if (target === index || target < 0 || target >= count) {\n        return false;\n    }\n    return target;\n}\n function _getTargetPixel(fill, scale) {\n    let pixel = null;\n    if (fill === 'start') {\n        pixel = scale.bottom;\n    } else if (fill === 'end') {\n        pixel = scale.top;\n    } else if (helpers_segment.isObject(fill)) {\n        pixel = scale.getPixelForValue(fill.value);\n    } else if (scale.getBasePixel) {\n        pixel = scale.getBasePixel();\n    }\n    return pixel;\n}\n function _getTargetValue(fill, scale, startValue) {\n    let value;\n    if (fill === 'start') {\n        value = startValue;\n    } else if (fill === 'end') {\n        value = scale.options.reverse ? scale.min : scale.max;\n    } else if (helpers_segment.isObject(fill)) {\n        value = fill.value;\n    } else {\n        value = scale.getBaseValue();\n    }\n    return value;\n}\n function parseFillOption(line) {\n    const options = line.options;\n    const fillOption = options.fill;\n    let fill = helpers_segment.valueOrDefault(fillOption && fillOption.target, fillOption);\n    if (fill === undefined) {\n        fill = !!options.backgroundColor;\n    }\n    if (fill === false || fill === null) {\n        return false;\n    }\n    if (fill === true) {\n        return 'origin';\n    }\n    return fill;\n}\n\nfunction _buildStackLine(source) {\n    const { scale , index , line  } = source;\n    const points = [];\n    const segments = line.segments;\n    const sourcePoints = line.points;\n    const linesBelow = getLinesBelow(scale, index);\n    linesBelow.push(_createBoundaryLine({\n        x: null,\n        y: scale.bottom\n    }, line));\n    for(let i = 0; i < segments.length; i++){\n        const segment = segments[i];\n        for(let j = segment.start; j <= segment.end; j++){\n            addPointsBelow(points, sourcePoints[j], linesBelow);\n        }\n    }\n    return new LineElement({\n        points,\n        options: {}\n    });\n}\n function getLinesBelow(scale, index) {\n    const below = [];\n    const metas = scale.getMatchingVisibleMetas('line');\n    for(let i = 0; i < metas.length; i++){\n        const meta = metas[i];\n        if (meta.index === index) {\n            break;\n        }\n        if (!meta.hidden) {\n            below.unshift(meta.dataset);\n        }\n    }\n    return below;\n}\n function addPointsBelow(points, sourcePoint, linesBelow) {\n    const postponed = [];\n    for(let j = 0; j < linesBelow.length; j++){\n        const line = linesBelow[j];\n        const { first , last , point  } = findPoint(line, sourcePoint, 'x');\n        if (!point || first && last) {\n            continue;\n        }\n        if (first) {\n            postponed.unshift(point);\n        } else {\n            points.push(point);\n            if (!last) {\n                break;\n            }\n        }\n    }\n    points.push(...postponed);\n}\n function findPoint(line, sourcePoint, property) {\n    const point = line.interpolate(sourcePoint, property);\n    if (!point) {\n        return {};\n    }\n    const pointValue = point[property];\n    const segments = line.segments;\n    const linePoints = line.points;\n    let first = false;\n    let last = false;\n    for(let i = 0; i < segments.length; i++){\n        const segment = segments[i];\n        const firstValue = linePoints[segment.start][property];\n        const lastValue = linePoints[segment.end][property];\n        if (helpers_segment._isBetween(pointValue, firstValue, lastValue)) {\n            first = pointValue === firstValue;\n            last = pointValue === lastValue;\n            break;\n        }\n    }\n    return {\n        first,\n        last,\n        point\n    };\n}\n\nclass simpleArc {\n    constructor(opts){\n        this.x = opts.x;\n        this.y = opts.y;\n        this.radius = opts.radius;\n    }\n    pathSegment(ctx, bounds, opts) {\n        const { x , y , radius  } = this;\n        bounds = bounds || {\n            start: 0,\n            end: helpers_segment.TAU\n        };\n        ctx.arc(x, y, radius, bounds.end, bounds.start, true);\n        return !opts.bounds;\n    }\n    interpolate(point) {\n        const { x , y , radius  } = this;\n        const angle = point.angle;\n        return {\n            x: x + Math.cos(angle) * radius,\n            y: y + Math.sin(angle) * radius,\n            angle\n        };\n    }\n}\n\nfunction _getTarget(source) {\n    const { chart , fill , line  } = source;\n    if (helpers_segment.isNumberFinite(fill)) {\n        return getLineByIndex(chart, fill);\n    }\n    if (fill === 'stack') {\n        return _buildStackLine(source);\n    }\n    if (fill === 'shape') {\n        return true;\n    }\n    const boundary = computeBoundary(source);\n    if (boundary instanceof simpleArc) {\n        return boundary;\n    }\n    return _createBoundaryLine(boundary, line);\n}\n function getLineByIndex(chart, index) {\n    const meta = chart.getDatasetMeta(index);\n    const visible = meta && chart.isDatasetVisible(index);\n    return visible ? meta.dataset : null;\n}\nfunction computeBoundary(source) {\n    const scale = source.scale || {};\n    if (scale.getPointPositionForValue) {\n        return computeCircularBoundary(source);\n    }\n    return computeLinearBoundary(source);\n}\nfunction computeLinearBoundary(source) {\n    const { scale ={} , fill  } = source;\n    const pixel = _getTargetPixel(fill, scale);\n    if (helpers_segment.isNumberFinite(pixel)) {\n        const horizontal = scale.isHorizontal();\n        return {\n            x: horizontal ? pixel : null,\n            y: horizontal ? null : pixel\n        };\n    }\n    return null;\n}\nfunction computeCircularBoundary(source) {\n    const { scale , fill  } = source;\n    const options = scale.options;\n    const length = scale.getLabels().length;\n    const start = options.reverse ? scale.max : scale.min;\n    const value = _getTargetValue(fill, scale, start);\n    const target = [];\n    if (options.grid.circular) {\n        const center = scale.getPointPositionForValue(0, start);\n        return new simpleArc({\n            x: center.x,\n            y: center.y,\n            radius: scale.getDistanceFromCenterForValue(value)\n        });\n    }\n    for(let i = 0; i < length; ++i){\n        target.push(scale.getPointPositionForValue(i, value));\n    }\n    return target;\n}\n\nfunction _drawfill(ctx, source, area) {\n    const target = _getTarget(source);\n    const { line , scale , axis  } = source;\n    const lineOpts = line.options;\n    const fillOption = lineOpts.fill;\n    const color = lineOpts.backgroundColor;\n    const { above =color , below =color  } = fillOption || {};\n    if (target && line.points.length) {\n        helpers_segment.clipArea(ctx, area);\n        doFill(ctx, {\n            line,\n            target,\n            above,\n            below,\n            area,\n            scale,\n            axis\n        });\n        helpers_segment.unclipArea(ctx);\n    }\n}\nfunction doFill(ctx, cfg) {\n    const { line , target , above , below , area , scale  } = cfg;\n    const property = line._loop ? 'angle' : cfg.axis;\n    ctx.save();\n    if (property === 'x' && below !== above) {\n        clipVertical(ctx, target, area.top);\n        fill(ctx, {\n            line,\n            target,\n            color: above,\n            scale,\n            property\n        });\n        ctx.restore();\n        ctx.save();\n        clipVertical(ctx, target, area.bottom);\n    }\n    fill(ctx, {\n        line,\n        target,\n        color: below,\n        scale,\n        property\n    });\n    ctx.restore();\n}\nfunction clipVertical(ctx, target, clipY) {\n    const { segments , points  } = target;\n    let first = true;\n    let lineLoop = false;\n    ctx.beginPath();\n    for (const segment of segments){\n        const { start , end  } = segment;\n        const firstPoint = points[start];\n        const lastPoint = points[_findSegmentEnd(start, end, points)];\n        if (first) {\n            ctx.moveTo(firstPoint.x, firstPoint.y);\n            first = false;\n        } else {\n            ctx.lineTo(firstPoint.x, clipY);\n            ctx.lineTo(firstPoint.x, firstPoint.y);\n        }\n        lineLoop = !!target.pathSegment(ctx, segment, {\n            move: lineLoop\n        });\n        if (lineLoop) {\n            ctx.closePath();\n        } else {\n            ctx.lineTo(lastPoint.x, clipY);\n        }\n    }\n    ctx.lineTo(target.first().x, clipY);\n    ctx.closePath();\n    ctx.clip();\n}\nfunction fill(ctx, cfg) {\n    const { line , target , property , color , scale  } = cfg;\n    const segments = _segments(line, target, property);\n    for (const { source: src , target: tgt , start , end  } of segments){\n        const { style: { backgroundColor =color  } = {}  } = src;\n        const notShape = target !== true;\n        ctx.save();\n        ctx.fillStyle = backgroundColor;\n        clipBounds(ctx, scale, notShape && _getBounds(property, start, end));\n        ctx.beginPath();\n        const lineLoop = !!line.pathSegment(ctx, src);\n        let loop;\n        if (notShape) {\n            if (lineLoop) {\n                ctx.closePath();\n            } else {\n                interpolatedLineTo(ctx, target, end, property);\n            }\n            const targetLoop = !!target.pathSegment(ctx, tgt, {\n                move: lineLoop,\n                reverse: true\n            });\n            loop = lineLoop && targetLoop;\n            if (!loop) {\n                interpolatedLineTo(ctx, target, start, property);\n            }\n        }\n        ctx.closePath();\n        ctx.fill(loop ? 'evenodd' : 'nonzero');\n        ctx.restore();\n    }\n}\nfunction clipBounds(ctx, scale, bounds) {\n    const { top , bottom  } = scale.chart.chartArea;\n    const { property , start , end  } = bounds || {};\n    if (property === 'x') {\n        ctx.beginPath();\n        ctx.rect(start, top, end - start, bottom - top);\n        ctx.clip();\n    }\n}\nfunction interpolatedLineTo(ctx, target, point, property) {\n    const interpolatedPoint = target.interpolate(point, property);\n    if (interpolatedPoint) {\n        ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y);\n    }\n}\n\nvar index = {\n    id: 'filler',\n    afterDatasetsUpdate (chart, _args, options) {\n        const count = (chart.data.datasets || []).length;\n        const sources = [];\n        let meta, i, line, source;\n        for(i = 0; i < count; ++i){\n            meta = chart.getDatasetMeta(i);\n            line = meta.dataset;\n            source = null;\n            if (line && line.options && line instanceof LineElement) {\n                source = {\n                    visible: chart.isDatasetVisible(i),\n                    index: i,\n                    fill: _decodeFill(line, i, count),\n                    chart,\n                    axis: meta.controller.options.indexAxis,\n                    scale: meta.vScale,\n                    line\n                };\n            }\n            meta.$filler = source;\n            sources.push(source);\n        }\n        for(i = 0; i < count; ++i){\n            source = sources[i];\n            if (!source || source.fill === false) {\n                continue;\n            }\n            source.fill = _resolveTarget(sources, i, options.propagate);\n        }\n    },\n    beforeDraw (chart, _args, options) {\n        const draw = options.drawTime === 'beforeDraw';\n        const metasets = chart.getSortedVisibleDatasetMetas();\n        const area = chart.chartArea;\n        for(let i = metasets.length - 1; i >= 0; --i){\n            const source = metasets[i].$filler;\n            if (!source) {\n                continue;\n            }\n            source.line.updateControlPoints(area, source.axis);\n            if (draw && source.fill) {\n                _drawfill(chart.ctx, source, area);\n            }\n        }\n    },\n    beforeDatasetsDraw (chart, _args, options) {\n        if (options.drawTime !== 'beforeDatasetsDraw') {\n            return;\n        }\n        const metasets = chart.getSortedVisibleDatasetMetas();\n        for(let i = metasets.length - 1; i >= 0; --i){\n            const source = metasets[i].$filler;\n            if (_shouldApplyFill(source)) {\n                _drawfill(chart.ctx, source, chart.chartArea);\n            }\n        }\n    },\n    beforeDatasetDraw (chart, args, options) {\n        const source = args.meta.$filler;\n        if (!_shouldApplyFill(source) || options.drawTime !== 'beforeDatasetDraw') {\n            return;\n        }\n        _drawfill(chart.ctx, source, chart.chartArea);\n    },\n    defaults: {\n        propagate: true,\n        drawTime: 'beforeDatasetDraw'\n    }\n};\n\nconst getBoxSize = (labelOpts, fontSize)=>{\n    let { boxHeight =fontSize , boxWidth =fontSize  } = labelOpts;\n    if (labelOpts.usePointStyle) {\n        boxHeight = Math.min(boxHeight, fontSize);\n        boxWidth = labelOpts.pointStyleWidth || Math.min(boxWidth, fontSize);\n    }\n    return {\n        boxWidth,\n        boxHeight,\n        itemHeight: Math.max(fontSize, boxHeight)\n    };\n};\nconst itemsEqual = (a, b)=>a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index;\nclass Legend extends Element {\n constructor(config){\n        super();\n        this._added = false;\n        this.legendHitBoxes = [];\n this._hoveredItem = null;\n        this.doughnutMode = false;\n        this.chart = config.chart;\n        this.options = config.options;\n        this.ctx = config.ctx;\n        this.legendItems = undefined;\n        this.columnSizes = undefined;\n        this.lineWidths = undefined;\n        this.maxHeight = undefined;\n        this.maxWidth = undefined;\n        this.top = undefined;\n        this.bottom = undefined;\n        this.left = undefined;\n        this.right = undefined;\n        this.height = undefined;\n        this.width = undefined;\n        this._margins = undefined;\n        this.position = undefined;\n        this.weight = undefined;\n        this.fullSize = undefined;\n    }\n    update(maxWidth, maxHeight, margins) {\n        this.maxWidth = maxWidth;\n        this.maxHeight = maxHeight;\n        this._margins = margins;\n        this.setDimensions();\n        this.buildLabels();\n        this.fit();\n    }\n    setDimensions() {\n        if (this.isHorizontal()) {\n            this.width = this.maxWidth;\n            this.left = this._margins.left;\n            this.right = this.width;\n        } else {\n            this.height = this.maxHeight;\n            this.top = this._margins.top;\n            this.bottom = this.height;\n        }\n    }\n    buildLabels() {\n        const labelOpts = this.options.labels || {};\n        let legendItems = helpers_segment.callback(labelOpts.generateLabels, [\n            this.chart\n        ], this) || [];\n        if (labelOpts.filter) {\n            legendItems = legendItems.filter((item)=>labelOpts.filter(item, this.chart.data));\n        }\n        if (labelOpts.sort) {\n            legendItems = legendItems.sort((a, b)=>labelOpts.sort(a, b, this.chart.data));\n        }\n        if (this.options.reverse) {\n            legendItems.reverse();\n        }\n        this.legendItems = legendItems;\n    }\n    fit() {\n        const { options , ctx  } = this;\n        if (!options.display) {\n            this.width = this.height = 0;\n            return;\n        }\n        const labelOpts = options.labels;\n        const labelFont = helpers_segment.toFont(labelOpts.font);\n        const fontSize = labelFont.size;\n        const titleHeight = this._computeTitleHeight();\n        const { boxWidth , itemHeight  } = getBoxSize(labelOpts, fontSize);\n        let width, height;\n        ctx.font = labelFont.string;\n        if (this.isHorizontal()) {\n            width = this.maxWidth;\n            height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;\n        } else {\n            height = this.maxHeight;\n            width = this._fitCols(titleHeight, labelFont, boxWidth, itemHeight) + 10;\n        }\n        this.width = Math.min(width, options.maxWidth || this.maxWidth);\n        this.height = Math.min(height, options.maxHeight || this.maxHeight);\n    }\n _fitRows(titleHeight, fontSize, boxWidth, itemHeight) {\n        const { ctx , maxWidth , options: { labels: { padding  }  }  } = this;\n        const hitboxes = this.legendHitBoxes = [];\n        const lineWidths = this.lineWidths = [\n            0\n        ];\n        const lineHeight = itemHeight + padding;\n        let totalHeight = titleHeight;\n        ctx.textAlign = 'left';\n        ctx.textBaseline = 'middle';\n        let row = -1;\n        let top = -lineHeight;\n        this.legendItems.forEach((legendItem, i)=>{\n            const itemWidth = boxWidth + fontSize / 2 + ctx.measureText(legendItem.text).width;\n            if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {\n                totalHeight += lineHeight;\n                lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;\n                top += lineHeight;\n                row++;\n            }\n            hitboxes[i] = {\n                left: 0,\n                top,\n                row,\n                width: itemWidth,\n                height: itemHeight\n            };\n            lineWidths[lineWidths.length - 1] += itemWidth + padding;\n        });\n        return totalHeight;\n    }\n    _fitCols(titleHeight, labelFont, boxWidth, _itemHeight) {\n        const { ctx , maxHeight , options: { labels: { padding  }  }  } = this;\n        const hitboxes = this.legendHitBoxes = [];\n        const columnSizes = this.columnSizes = [];\n        const heightLimit = maxHeight - titleHeight;\n        let totalWidth = padding;\n        let currentColWidth = 0;\n        let currentColHeight = 0;\n        let left = 0;\n        let col = 0;\n        this.legendItems.forEach((legendItem, i)=>{\n            const { itemWidth , itemHeight  } = calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight);\n            if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) {\n                totalWidth += currentColWidth + padding;\n                columnSizes.push({\n                    width: currentColWidth,\n                    height: currentColHeight\n                });\n                left += currentColWidth + padding;\n                col++;\n                currentColWidth = currentColHeight = 0;\n            }\n            hitboxes[i] = {\n                left,\n                top: currentColHeight,\n                col,\n                width: itemWidth,\n                height: itemHeight\n            };\n            currentColWidth = Math.max(currentColWidth, itemWidth);\n            currentColHeight += itemHeight + padding;\n        });\n        totalWidth += currentColWidth;\n        columnSizes.push({\n            width: currentColWidth,\n            height: currentColHeight\n        });\n        return totalWidth;\n    }\n    adjustHitBoxes() {\n        if (!this.options.display) {\n            return;\n        }\n        const titleHeight = this._computeTitleHeight();\n        const { legendHitBoxes: hitboxes , options: { align , labels: { padding  } , rtl  }  } = this;\n        const rtlHelper = helpers_segment.getRtlAdapter(rtl, this.left, this.width);\n        if (this.isHorizontal()) {\n            let row = 0;\n            let left = helpers_segment._alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);\n            for (const hitbox of hitboxes){\n                if (row !== hitbox.row) {\n                    row = hitbox.row;\n                    left = helpers_segment._alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);\n                }\n                hitbox.top += this.top + titleHeight + padding;\n                hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);\n                left += hitbox.width + padding;\n            }\n        } else {\n            let col = 0;\n            let top = helpers_segment._alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);\n            for (const hitbox1 of hitboxes){\n                if (hitbox1.col !== col) {\n                    col = hitbox1.col;\n                    top = helpers_segment._alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);\n                }\n                hitbox1.top = top;\n                hitbox1.left += this.left + padding;\n                hitbox1.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox1.left), hitbox1.width);\n                top += hitbox1.height + padding;\n            }\n        }\n    }\n    isHorizontal() {\n        return this.options.position === 'top' || this.options.position === 'bottom';\n    }\n    draw() {\n        if (this.options.display) {\n            const ctx = this.ctx;\n            helpers_segment.clipArea(ctx, this);\n            this._draw();\n            helpers_segment.unclipArea(ctx);\n        }\n    }\n _draw() {\n        const { options: opts , columnSizes , lineWidths , ctx  } = this;\n        const { align , labels: labelOpts  } = opts;\n        const defaultColor = helpers_segment.defaults.color;\n        const rtlHelper = helpers_segment.getRtlAdapter(opts.rtl, this.left, this.width);\n        const labelFont = helpers_segment.toFont(labelOpts.font);\n        const { padding  } = labelOpts;\n        const fontSize = labelFont.size;\n        const halfFontSize = fontSize / 2;\n        let cursor;\n        this.drawTitle();\n        ctx.textAlign = rtlHelper.textAlign('left');\n        ctx.textBaseline = 'middle';\n        ctx.lineWidth = 0.5;\n        ctx.font = labelFont.string;\n        const { boxWidth , boxHeight , itemHeight  } = getBoxSize(labelOpts, fontSize);\n        const drawLegendBox = function(x, y, legendItem) {\n            if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) {\n                return;\n            }\n            ctx.save();\n            const lineWidth = helpers_segment.valueOrDefault(legendItem.lineWidth, 1);\n            ctx.fillStyle = helpers_segment.valueOrDefault(legendItem.fillStyle, defaultColor);\n            ctx.lineCap = helpers_segment.valueOrDefault(legendItem.lineCap, 'butt');\n            ctx.lineDashOffset = helpers_segment.valueOrDefault(legendItem.lineDashOffset, 0);\n            ctx.lineJoin = helpers_segment.valueOrDefault(legendItem.lineJoin, 'miter');\n            ctx.lineWidth = lineWidth;\n            ctx.strokeStyle = helpers_segment.valueOrDefault(legendItem.strokeStyle, defaultColor);\n            ctx.setLineDash(helpers_segment.valueOrDefault(legendItem.lineDash, []));\n            if (labelOpts.usePointStyle) {\n                const drawOptions = {\n                    radius: boxHeight * Math.SQRT2 / 2,\n                    pointStyle: legendItem.pointStyle,\n                    rotation: legendItem.rotation,\n                    borderWidth: lineWidth\n                };\n                const centerX = rtlHelper.xPlus(x, boxWidth / 2);\n                const centerY = y + halfFontSize;\n                helpers_segment.drawPointLegend(ctx, drawOptions, centerX, centerY, labelOpts.pointStyleWidth && boxWidth);\n            } else {\n                const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0);\n                const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth);\n                const borderRadius = helpers_segment.toTRBLCorners(legendItem.borderRadius);\n                ctx.beginPath();\n                if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                    helpers_segment.addRoundedRectPath(ctx, {\n                        x: xBoxLeft,\n                        y: yBoxTop,\n                        w: boxWidth,\n                        h: boxHeight,\n                        radius: borderRadius\n                    });\n                } else {\n                    ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight);\n                }\n                ctx.fill();\n                if (lineWidth !== 0) {\n                    ctx.stroke();\n                }\n            }\n            ctx.restore();\n        };\n        const fillText = function(x, y, legendItem) {\n            helpers_segment.renderText(ctx, legendItem.text, x, y + itemHeight / 2, labelFont, {\n                strikethrough: legendItem.hidden,\n                textAlign: rtlHelper.textAlign(legendItem.textAlign)\n            });\n        };\n        const isHorizontal = this.isHorizontal();\n        const titleHeight = this._computeTitleHeight();\n        if (isHorizontal) {\n            cursor = {\n                x: helpers_segment._alignStartEnd(align, this.left + padding, this.right - lineWidths[0]),\n                y: this.top + padding + titleHeight,\n                line: 0\n            };\n        } else {\n            cursor = {\n                x: this.left + padding,\n                y: helpers_segment._alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height),\n                line: 0\n            };\n        }\n        helpers_segment.overrideTextDirection(this.ctx, opts.textDirection);\n        const lineHeight = itemHeight + padding;\n        this.legendItems.forEach((legendItem, i)=>{\n            ctx.strokeStyle = legendItem.fontColor;\n            ctx.fillStyle = legendItem.fontColor;\n            const textWidth = ctx.measureText(legendItem.text).width;\n            const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign));\n            const width = boxWidth + halfFontSize + textWidth;\n            let x = cursor.x;\n            let y = cursor.y;\n            rtlHelper.setWidth(this.width);\n            if (isHorizontal) {\n                if (i > 0 && x + width + padding > this.right) {\n                    y = cursor.y += lineHeight;\n                    cursor.line++;\n                    x = cursor.x = helpers_segment._alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]);\n                }\n            } else if (i > 0 && y + lineHeight > this.bottom) {\n                x = cursor.x = x + columnSizes[cursor.line].width + padding;\n                cursor.line++;\n                y = cursor.y = helpers_segment._alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height);\n            }\n            const realX = rtlHelper.x(x);\n            drawLegendBox(realX, y, legendItem);\n            x = helpers_segment._textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl);\n            fillText(rtlHelper.x(x), y, legendItem);\n            if (isHorizontal) {\n                cursor.x += width + padding;\n            } else if (typeof legendItem.text !== 'string') {\n                const fontLineHeight = labelFont.lineHeight;\n                cursor.y += calculateLegendItemHeight(legendItem, fontLineHeight);\n            } else {\n                cursor.y += lineHeight;\n            }\n        });\n        helpers_segment.restoreTextDirection(this.ctx, opts.textDirection);\n    }\n drawTitle() {\n        const opts = this.options;\n        const titleOpts = opts.title;\n        const titleFont = helpers_segment.toFont(titleOpts.font);\n        const titlePadding = helpers_segment.toPadding(titleOpts.padding);\n        if (!titleOpts.display) {\n            return;\n        }\n        const rtlHelper = helpers_segment.getRtlAdapter(opts.rtl, this.left, this.width);\n        const ctx = this.ctx;\n        const position = titleOpts.position;\n        const halfFontSize = titleFont.size / 2;\n        const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;\n        let y;\n        let left = this.left;\n        let maxWidth = this.width;\n        if (this.isHorizontal()) {\n            maxWidth = Math.max(...this.lineWidths);\n            y = this.top + topPaddingPlusHalfFontSize;\n            left = helpers_segment._alignStartEnd(opts.align, left, this.right - maxWidth);\n        } else {\n            const maxHeight = this.columnSizes.reduce((acc, size)=>Math.max(acc, size.height), 0);\n            y = topPaddingPlusHalfFontSize + helpers_segment._alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight());\n        }\n        const x = helpers_segment._alignStartEnd(position, left, left + maxWidth);\n        ctx.textAlign = rtlHelper.textAlign(helpers_segment._toLeftRightCenter(position));\n        ctx.textBaseline = 'middle';\n        ctx.strokeStyle = titleOpts.color;\n        ctx.fillStyle = titleOpts.color;\n        ctx.font = titleFont.string;\n        helpers_segment.renderText(ctx, titleOpts.text, x, y, titleFont);\n    }\n _computeTitleHeight() {\n        const titleOpts = this.options.title;\n        const titleFont = helpers_segment.toFont(titleOpts.font);\n        const titlePadding = helpers_segment.toPadding(titleOpts.padding);\n        return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;\n    }\n _getLegendItemAt(x, y) {\n        let i, hitBox, lh;\n        if (helpers_segment._isBetween(x, this.left, this.right) && helpers_segment._isBetween(y, this.top, this.bottom)) {\n            lh = this.legendHitBoxes;\n            for(i = 0; i < lh.length; ++i){\n                hitBox = lh[i];\n                if (helpers_segment._isBetween(x, hitBox.left, hitBox.left + hitBox.width) && helpers_segment._isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {\n                    return this.legendItems[i];\n                }\n            }\n        }\n        return null;\n    }\n handleEvent(e) {\n        const opts = this.options;\n        if (!isListened(e.type, opts)) {\n            return;\n        }\n        const hoveredItem = this._getLegendItemAt(e.x, e.y);\n        if (e.type === 'mousemove' || e.type === 'mouseout') {\n            const previous = this._hoveredItem;\n            const sameItem = itemsEqual(previous, hoveredItem);\n            if (previous && !sameItem) {\n                helpers_segment.callback(opts.onLeave, [\n                    e,\n                    previous,\n                    this\n                ], this);\n            }\n            this._hoveredItem = hoveredItem;\n            if (hoveredItem && !sameItem) {\n                helpers_segment.callback(opts.onHover, [\n                    e,\n                    hoveredItem,\n                    this\n                ], this);\n            }\n        } else if (hoveredItem) {\n            helpers_segment.callback(opts.onClick, [\n                e,\n                hoveredItem,\n                this\n            ], this);\n        }\n    }\n}\nfunction calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight) {\n    const itemWidth = calculateItemWidth(legendItem, boxWidth, labelFont, ctx);\n    const itemHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight);\n    return {\n        itemWidth,\n        itemHeight\n    };\n}\nfunction calculateItemWidth(legendItem, boxWidth, labelFont, ctx) {\n    let legendItemText = legendItem.text;\n    if (legendItemText && typeof legendItemText !== 'string') {\n        legendItemText = legendItemText.reduce((a, b)=>a.length > b.length ? a : b);\n    }\n    return boxWidth + labelFont.size / 2 + ctx.measureText(legendItemText).width;\n}\nfunction calculateItemHeight(_itemHeight, legendItem, fontLineHeight) {\n    let itemHeight = _itemHeight;\n    if (typeof legendItem.text !== 'string') {\n        itemHeight = calculateLegendItemHeight(legendItem, fontLineHeight);\n    }\n    return itemHeight;\n}\nfunction calculateLegendItemHeight(legendItem, fontLineHeight) {\n    const labelHeight = legendItem.text ? legendItem.text.length + 0.5 : 0;\n    return fontLineHeight * labelHeight;\n}\nfunction isListened(type, opts) {\n    if ((type === 'mousemove' || type === 'mouseout') && (opts.onHover || opts.onLeave)) {\n        return true;\n    }\n    if (opts.onClick && (type === 'click' || type === 'mouseup')) {\n        return true;\n    }\n    return false;\n}\nvar plugin_legend = {\n    id: 'legend',\n _element: Legend,\n    start (chart, _args, options) {\n        const legend = chart.legend = new Legend({\n            ctx: chart.ctx,\n            options,\n            chart\n        });\n        layouts.configure(chart, legend, options);\n        layouts.addBox(chart, legend);\n    },\n    stop (chart) {\n        layouts.removeBox(chart, chart.legend);\n        delete chart.legend;\n    },\n    beforeUpdate (chart, _args, options) {\n        const legend = chart.legend;\n        layouts.configure(chart, legend, options);\n        legend.options = options;\n    },\n    afterUpdate (chart) {\n        const legend = chart.legend;\n        legend.buildLabels();\n        legend.adjustHitBoxes();\n    },\n    afterEvent (chart, args) {\n        if (!args.replay) {\n            chart.legend.handleEvent(args.event);\n        }\n    },\n    defaults: {\n        display: true,\n        position: 'top',\n        align: 'center',\n        fullSize: true,\n        reverse: false,\n        weight: 1000,\n        onClick (e, legendItem, legend) {\n            const index = legendItem.datasetIndex;\n            const ci = legend.chart;\n            if (ci.isDatasetVisible(index)) {\n                ci.hide(index);\n                legendItem.hidden = true;\n            } else {\n                ci.show(index);\n                legendItem.hidden = false;\n            }\n        },\n        onHover: null,\n        onLeave: null,\n        labels: {\n            color: (ctx)=>ctx.chart.options.color,\n            boxWidth: 40,\n            padding: 10,\n            generateLabels (chart) {\n                const datasets = chart.data.datasets;\n                const { labels: { usePointStyle , pointStyle , textAlign , color , useBorderRadius , borderRadius  }  } = chart.legend.options;\n                return chart._getSortedDatasetMetas().map((meta)=>{\n                    const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);\n                    const borderWidth = helpers_segment.toPadding(style.borderWidth);\n                    return {\n                        text: datasets[meta.index].label,\n                        fillStyle: style.backgroundColor,\n                        fontColor: color,\n                        hidden: !meta.visible,\n                        lineCap: style.borderCapStyle,\n                        lineDash: style.borderDash,\n                        lineDashOffset: style.borderDashOffset,\n                        lineJoin: style.borderJoinStyle,\n                        lineWidth: (borderWidth.width + borderWidth.height) / 4,\n                        strokeStyle: style.borderColor,\n                        pointStyle: pointStyle || style.pointStyle,\n                        rotation: style.rotation,\n                        textAlign: textAlign || style.textAlign,\n                        borderRadius: useBorderRadius && (borderRadius || style.borderRadius),\n                        datasetIndex: meta.index\n                    };\n                }, this);\n            }\n        },\n        title: {\n            color: (ctx)=>ctx.chart.options.color,\n            display: false,\n            position: 'center',\n            text: ''\n        }\n    },\n    descriptors: {\n        _scriptable: (name)=>!name.startsWith('on'),\n        labels: {\n            _scriptable: (name)=>![\n                    'generateLabels',\n                    'filter',\n                    'sort'\n                ].includes(name)\n        }\n    }\n};\n\nclass Title extends Element {\n constructor(config){\n        super();\n        this.chart = config.chart;\n        this.options = config.options;\n        this.ctx = config.ctx;\n        this._padding = undefined;\n        this.top = undefined;\n        this.bottom = undefined;\n        this.left = undefined;\n        this.right = undefined;\n        this.width = undefined;\n        this.height = undefined;\n        this.position = undefined;\n        this.weight = undefined;\n        this.fullSize = undefined;\n    }\n    update(maxWidth, maxHeight) {\n        const opts = this.options;\n        this.left = 0;\n        this.top = 0;\n        if (!opts.display) {\n            this.width = this.height = this.right = this.bottom = 0;\n            return;\n        }\n        this.width = this.right = maxWidth;\n        this.height = this.bottom = maxHeight;\n        const lineCount = helpers_segment.isArray(opts.text) ? opts.text.length : 1;\n        this._padding = helpers_segment.toPadding(opts.padding);\n        const textSize = lineCount * helpers_segment.toFont(opts.font).lineHeight + this._padding.height;\n        if (this.isHorizontal()) {\n            this.height = textSize;\n        } else {\n            this.width = textSize;\n        }\n    }\n    isHorizontal() {\n        const pos = this.options.position;\n        return pos === 'top' || pos === 'bottom';\n    }\n    _drawArgs(offset) {\n        const { top , left , bottom , right , options  } = this;\n        const align = options.align;\n        let rotation = 0;\n        let maxWidth, titleX, titleY;\n        if (this.isHorizontal()) {\n            titleX = helpers_segment._alignStartEnd(align, left, right);\n            titleY = top + offset;\n            maxWidth = right - left;\n        } else {\n            if (options.position === 'left') {\n                titleX = left + offset;\n                titleY = helpers_segment._alignStartEnd(align, bottom, top);\n                rotation = helpers_segment.PI * -0.5;\n            } else {\n                titleX = right - offset;\n                titleY = helpers_segment._alignStartEnd(align, top, bottom);\n                rotation = helpers_segment.PI * 0.5;\n            }\n            maxWidth = bottom - top;\n        }\n        return {\n            titleX,\n            titleY,\n            maxWidth,\n            rotation\n        };\n    }\n    draw() {\n        const ctx = this.ctx;\n        const opts = this.options;\n        if (!opts.display) {\n            return;\n        }\n        const fontOpts = helpers_segment.toFont(opts.font);\n        const lineHeight = fontOpts.lineHeight;\n        const offset = lineHeight / 2 + this._padding.top;\n        const { titleX , titleY , maxWidth , rotation  } = this._drawArgs(offset);\n        helpers_segment.renderText(ctx, opts.text, 0, 0, fontOpts, {\n            color: opts.color,\n            maxWidth,\n            rotation,\n            textAlign: helpers_segment._toLeftRightCenter(opts.align),\n            textBaseline: 'middle',\n            translation: [\n                titleX,\n                titleY\n            ]\n        });\n    }\n}\nfunction createTitle(chart, titleOpts) {\n    const title = new Title({\n        ctx: chart.ctx,\n        options: titleOpts,\n        chart\n    });\n    layouts.configure(chart, title, titleOpts);\n    layouts.addBox(chart, title);\n    chart.titleBlock = title;\n}\nvar plugin_title = {\n    id: 'title',\n _element: Title,\n    start (chart, _args, options) {\n        createTitle(chart, options);\n    },\n    stop (chart) {\n        const titleBlock = chart.titleBlock;\n        layouts.removeBox(chart, titleBlock);\n        delete chart.titleBlock;\n    },\n    beforeUpdate (chart, _args, options) {\n        const title = chart.titleBlock;\n        layouts.configure(chart, title, options);\n        title.options = options;\n    },\n    defaults: {\n        align: 'center',\n        display: false,\n        font: {\n            weight: 'bold'\n        },\n        fullSize: true,\n        padding: 10,\n        position: 'top',\n        text: '',\n        weight: 2000\n    },\n    defaultRoutes: {\n        color: 'color'\n    },\n    descriptors: {\n        _scriptable: true,\n        _indexable: false\n    }\n};\n\nconst map = new WeakMap();\nvar plugin_subtitle = {\n    id: 'subtitle',\n    start (chart, _args, options) {\n        const title = new Title({\n            ctx: chart.ctx,\n            options,\n            chart\n        });\n        layouts.configure(chart, title, options);\n        layouts.addBox(chart, title);\n        map.set(chart, title);\n    },\n    stop (chart) {\n        layouts.removeBox(chart, map.get(chart));\n        map.delete(chart);\n    },\n    beforeUpdate (chart, _args, options) {\n        const title = map.get(chart);\n        layouts.configure(chart, title, options);\n        title.options = options;\n    },\n    defaults: {\n        align: 'center',\n        display: false,\n        font: {\n            weight: 'normal'\n        },\n        fullSize: true,\n        padding: 0,\n        position: 'top',\n        text: '',\n        weight: 1500\n    },\n    defaultRoutes: {\n        color: 'color'\n    },\n    descriptors: {\n        _scriptable: true,\n        _indexable: false\n    }\n};\n\nconst positioners = {\n average (items) {\n        if (!items.length) {\n            return false;\n        }\n        let i, len;\n        let x = 0;\n        let y = 0;\n        let count = 0;\n        for(i = 0, len = items.length; i < len; ++i){\n            const el = items[i].element;\n            if (el && el.hasValue()) {\n                const pos = el.tooltipPosition();\n                x += pos.x;\n                y += pos.y;\n                ++count;\n            }\n        }\n        return {\n            x: x / count,\n            y: y / count\n        };\n    },\n nearest (items, eventPosition) {\n        if (!items.length) {\n            return false;\n        }\n        let x = eventPosition.x;\n        let y = eventPosition.y;\n        let minDistance = Number.POSITIVE_INFINITY;\n        let i, len, nearestElement;\n        for(i = 0, len = items.length; i < len; ++i){\n            const el = items[i].element;\n            if (el && el.hasValue()) {\n                const center = el.getCenterPoint();\n                const d = helpers_segment.distanceBetweenPoints(eventPosition, center);\n                if (d < minDistance) {\n                    minDistance = d;\n                    nearestElement = el;\n                }\n            }\n        }\n        if (nearestElement) {\n            const tp = nearestElement.tooltipPosition();\n            x = tp.x;\n            y = tp.y;\n        }\n        return {\n            x,\n            y\n        };\n    }\n};\nfunction pushOrConcat(base, toPush) {\n    if (toPush) {\n        if (helpers_segment.isArray(toPush)) {\n            Array.prototype.push.apply(base, toPush);\n        } else {\n            base.push(toPush);\n        }\n    }\n    return base;\n}\n function splitNewlines(str) {\n    if ((typeof str === 'string' || str instanceof String) && str.indexOf('\\n') > -1) {\n        return str.split('\\n');\n    }\n    return str;\n}\n function createTooltipItem(chart, item) {\n    const { element , datasetIndex , index  } = item;\n    const controller = chart.getDatasetMeta(datasetIndex).controller;\n    const { label , value  } = controller.getLabelAndValue(index);\n    return {\n        chart,\n        label,\n        parsed: controller.getParsed(index),\n        raw: chart.data.datasets[datasetIndex].data[index],\n        formattedValue: value,\n        dataset: controller.getDataset(),\n        dataIndex: index,\n        datasetIndex,\n        element\n    };\n}\n function getTooltipSize(tooltip, options) {\n    const ctx = tooltip.chart.ctx;\n    const { body , footer , title  } = tooltip;\n    const { boxWidth , boxHeight  } = options;\n    const bodyFont = helpers_segment.toFont(options.bodyFont);\n    const titleFont = helpers_segment.toFont(options.titleFont);\n    const footerFont = helpers_segment.toFont(options.footerFont);\n    const titleLineCount = title.length;\n    const footerLineCount = footer.length;\n    const bodyLineItemCount = body.length;\n    const padding = helpers_segment.toPadding(options.padding);\n    let height = padding.height;\n    let width = 0;\n    let combinedBodyLength = body.reduce((count, bodyItem)=>count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length, 0);\n    combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length;\n    if (titleLineCount) {\n        height += titleLineCount * titleFont.lineHeight + (titleLineCount - 1) * options.titleSpacing + options.titleMarginBottom;\n    }\n    if (combinedBodyLength) {\n        const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight;\n        height += bodyLineItemCount * bodyLineHeight + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight + (combinedBodyLength - 1) * options.bodySpacing;\n    }\n    if (footerLineCount) {\n        height += options.footerMarginTop + footerLineCount * footerFont.lineHeight + (footerLineCount - 1) * options.footerSpacing;\n    }\n    let widthPadding = 0;\n    const maxLineWidth = function(line) {\n        width = Math.max(width, ctx.measureText(line).width + widthPadding);\n    };\n    ctx.save();\n    ctx.font = titleFont.string;\n    helpers_segment.each(tooltip.title, maxLineWidth);\n    ctx.font = bodyFont.string;\n    helpers_segment.each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);\n    widthPadding = options.displayColors ? boxWidth + 2 + options.boxPadding : 0;\n    helpers_segment.each(body, (bodyItem)=>{\n        helpers_segment.each(bodyItem.before, maxLineWidth);\n        helpers_segment.each(bodyItem.lines, maxLineWidth);\n        helpers_segment.each(bodyItem.after, maxLineWidth);\n    });\n    widthPadding = 0;\n    ctx.font = footerFont.string;\n    helpers_segment.each(tooltip.footer, maxLineWidth);\n    ctx.restore();\n    width += padding.width;\n    return {\n        width,\n        height\n    };\n}\nfunction determineYAlign(chart, size) {\n    const { y , height  } = size;\n    if (y < height / 2) {\n        return 'top';\n    } else if (y > chart.height - height / 2) {\n        return 'bottom';\n    }\n    return 'center';\n}\nfunction doesNotFitWithAlign(xAlign, chart, options, size) {\n    const { x , width  } = size;\n    const caret = options.caretSize + options.caretPadding;\n    if (xAlign === 'left' && x + width + caret > chart.width) {\n        return true;\n    }\n    if (xAlign === 'right' && x - width - caret < 0) {\n        return true;\n    }\n}\nfunction determineXAlign(chart, options, size, yAlign) {\n    const { x , width  } = size;\n    const { width: chartWidth , chartArea: { left , right  }  } = chart;\n    let xAlign = 'center';\n    if (yAlign === 'center') {\n        xAlign = x <= (left + right) / 2 ? 'left' : 'right';\n    } else if (x <= width / 2) {\n        xAlign = 'left';\n    } else if (x >= chartWidth - width / 2) {\n        xAlign = 'right';\n    }\n    if (doesNotFitWithAlign(xAlign, chart, options, size)) {\n        xAlign = 'center';\n    }\n    return xAlign;\n}\n function determineAlignment(chart, options, size) {\n    const yAlign = size.yAlign || options.yAlign || determineYAlign(chart, size);\n    return {\n        xAlign: size.xAlign || options.xAlign || determineXAlign(chart, options, size, yAlign),\n        yAlign\n    };\n}\nfunction alignX(size, xAlign) {\n    let { x , width  } = size;\n    if (xAlign === 'right') {\n        x -= width;\n    } else if (xAlign === 'center') {\n        x -= width / 2;\n    }\n    return x;\n}\nfunction alignY(size, yAlign, paddingAndSize) {\n    let { y , height  } = size;\n    if (yAlign === 'top') {\n        y += paddingAndSize;\n    } else if (yAlign === 'bottom') {\n        y -= height + paddingAndSize;\n    } else {\n        y -= height / 2;\n    }\n    return y;\n}\n function getBackgroundPoint(options, size, alignment, chart) {\n    const { caretSize , caretPadding , cornerRadius  } = options;\n    const { xAlign , yAlign  } = alignment;\n    const paddingAndSize = caretSize + caretPadding;\n    const { topLeft , topRight , bottomLeft , bottomRight  } = helpers_segment.toTRBLCorners(cornerRadius);\n    let x = alignX(size, xAlign);\n    const y = alignY(size, yAlign, paddingAndSize);\n    if (yAlign === 'center') {\n        if (xAlign === 'left') {\n            x += paddingAndSize;\n        } else if (xAlign === 'right') {\n            x -= paddingAndSize;\n        }\n    } else if (xAlign === 'left') {\n        x -= Math.max(topLeft, bottomLeft) + caretSize;\n    } else if (xAlign === 'right') {\n        x += Math.max(topRight, bottomRight) + caretSize;\n    }\n    return {\n        x: helpers_segment._limitValue(x, 0, chart.width - size.width),\n        y: helpers_segment._limitValue(y, 0, chart.height - size.height)\n    };\n}\nfunction getAlignedX(tooltip, align, options) {\n    const padding = helpers_segment.toPadding(options.padding);\n    return align === 'center' ? tooltip.x + tooltip.width / 2 : align === 'right' ? tooltip.x + tooltip.width - padding.right : tooltip.x + padding.left;\n}\n function getBeforeAfterBodyLines(callback) {\n    return pushOrConcat([], splitNewlines(callback));\n}\nfunction createTooltipContext(parent, tooltip, tooltipItems) {\n    return helpers_segment.createContext(parent, {\n        tooltip,\n        tooltipItems,\n        type: 'tooltip'\n    });\n}\nfunction overrideCallbacks(callbacks, context) {\n    const override = context && context.dataset && context.dataset.tooltip && context.dataset.tooltip.callbacks;\n    return override ? callbacks.override(override) : callbacks;\n}\nconst defaultCallbacks = {\n    beforeTitle: helpers_segment.noop,\n    title (tooltipItems) {\n        if (tooltipItems.length > 0) {\n            const item = tooltipItems[0];\n            const labels = item.chart.data.labels;\n            const labelCount = labels ? labels.length : 0;\n            if (this && this.options && this.options.mode === 'dataset') {\n                return item.dataset.label || '';\n            } else if (item.label) {\n                return item.label;\n            } else if (labelCount > 0 && item.dataIndex < labelCount) {\n                return labels[item.dataIndex];\n            }\n        }\n        return '';\n    },\n    afterTitle: helpers_segment.noop,\n    beforeBody: helpers_segment.noop,\n    beforeLabel: helpers_segment.noop,\n    label (tooltipItem) {\n        if (this && this.options && this.options.mode === 'dataset') {\n            return tooltipItem.label + ': ' + tooltipItem.formattedValue || tooltipItem.formattedValue;\n        }\n        let label = tooltipItem.dataset.label || '';\n        if (label) {\n            label += ': ';\n        }\n        const value = tooltipItem.formattedValue;\n        if (!helpers_segment.isNullOrUndef(value)) {\n            label += value;\n        }\n        return label;\n    },\n    labelColor (tooltipItem) {\n        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);\n        const options = meta.controller.getStyle(tooltipItem.dataIndex);\n        return {\n            borderColor: options.borderColor,\n            backgroundColor: options.backgroundColor,\n            borderWidth: options.borderWidth,\n            borderDash: options.borderDash,\n            borderDashOffset: options.borderDashOffset,\n            borderRadius: 0\n        };\n    },\n    labelTextColor () {\n        return this.options.bodyColor;\n    },\n    labelPointStyle (tooltipItem) {\n        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);\n        const options = meta.controller.getStyle(tooltipItem.dataIndex);\n        return {\n            pointStyle: options.pointStyle,\n            rotation: options.rotation\n        };\n    },\n    afterLabel: helpers_segment.noop,\n    afterBody: helpers_segment.noop,\n    beforeFooter: helpers_segment.noop,\n    footer: helpers_segment.noop,\n    afterFooter: helpers_segment.noop\n};\n function invokeCallbackWithFallback(callbacks, name, ctx, arg) {\n    const result = callbacks[name].call(ctx, arg);\n    if (typeof result === 'undefined') {\n        return defaultCallbacks[name].call(ctx, arg);\n    }\n    return result;\n}\nclass Tooltip extends Element {\n static positioners = positioners;\n    constructor(config){\n        super();\n        this.opacity = 0;\n        this._active = [];\n        this._eventPosition = undefined;\n        this._size = undefined;\n        this._cachedAnimations = undefined;\n        this._tooltipItems = [];\n        this.$animations = undefined;\n        this.$context = undefined;\n        this.chart = config.chart;\n        this.options = config.options;\n        this.dataPoints = undefined;\n        this.title = undefined;\n        this.beforeBody = undefined;\n        this.body = undefined;\n        this.afterBody = undefined;\n        this.footer = undefined;\n        this.xAlign = undefined;\n        this.yAlign = undefined;\n        this.x = undefined;\n        this.y = undefined;\n        this.height = undefined;\n        this.width = undefined;\n        this.caretX = undefined;\n        this.caretY = undefined;\n        this.labelColors = undefined;\n        this.labelPointStyles = undefined;\n        this.labelTextColors = undefined;\n    }\n    initialize(options) {\n        this.options = options;\n        this._cachedAnimations = undefined;\n        this.$context = undefined;\n    }\n _resolveAnimations() {\n        const cached = this._cachedAnimations;\n        if (cached) {\n            return cached;\n        }\n        const chart = this.chart;\n        const options = this.options.setContext(this.getContext());\n        const opts = options.enabled && chart.options.animation && options.animations;\n        const animations = new Animations(this.chart, opts);\n        if (opts._cacheable) {\n            this._cachedAnimations = Object.freeze(animations);\n        }\n        return animations;\n    }\n getContext() {\n        return this.$context || (this.$context = createTooltipContext(this.chart.getContext(), this, this._tooltipItems));\n    }\n    getTitle(context, options) {\n        const { callbacks  } = options;\n        const beforeTitle = invokeCallbackWithFallback(callbacks, 'beforeTitle', this, context);\n        const title = invokeCallbackWithFallback(callbacks, 'title', this, context);\n        const afterTitle = invokeCallbackWithFallback(callbacks, 'afterTitle', this, context);\n        let lines = [];\n        lines = pushOrConcat(lines, splitNewlines(beforeTitle));\n        lines = pushOrConcat(lines, splitNewlines(title));\n        lines = pushOrConcat(lines, splitNewlines(afterTitle));\n        return lines;\n    }\n    getBeforeBody(tooltipItems, options) {\n        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'beforeBody', this, tooltipItems));\n    }\n    getBody(tooltipItems, options) {\n        const { callbacks  } = options;\n        const bodyItems = [];\n        helpers_segment.each(tooltipItems, (context)=>{\n            const bodyItem = {\n                before: [],\n                lines: [],\n                after: []\n            };\n            const scoped = overrideCallbacks(callbacks, context);\n            pushOrConcat(bodyItem.before, splitNewlines(invokeCallbackWithFallback(scoped, 'beforeLabel', this, context)));\n            pushOrConcat(bodyItem.lines, invokeCallbackWithFallback(scoped, 'label', this, context));\n            pushOrConcat(bodyItem.after, splitNewlines(invokeCallbackWithFallback(scoped, 'afterLabel', this, context)));\n            bodyItems.push(bodyItem);\n        });\n        return bodyItems;\n    }\n    getAfterBody(tooltipItems, options) {\n        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'afterBody', this, tooltipItems));\n    }\n    getFooter(tooltipItems, options) {\n        const { callbacks  } = options;\n        const beforeFooter = invokeCallbackWithFallback(callbacks, 'beforeFooter', this, tooltipItems);\n        const footer = invokeCallbackWithFallback(callbacks, 'footer', this, tooltipItems);\n        const afterFooter = invokeCallbackWithFallback(callbacks, 'afterFooter', this, tooltipItems);\n        let lines = [];\n        lines = pushOrConcat(lines, splitNewlines(beforeFooter));\n        lines = pushOrConcat(lines, splitNewlines(footer));\n        lines = pushOrConcat(lines, splitNewlines(afterFooter));\n        return lines;\n    }\n _createItems(options) {\n        const active = this._active;\n        const data = this.chart.data;\n        const labelColors = [];\n        const labelPointStyles = [];\n        const labelTextColors = [];\n        let tooltipItems = [];\n        let i, len;\n        for(i = 0, len = active.length; i < len; ++i){\n            tooltipItems.push(createTooltipItem(this.chart, active[i]));\n        }\n        if (options.filter) {\n            tooltipItems = tooltipItems.filter((element, index, array)=>options.filter(element, index, array, data));\n        }\n        if (options.itemSort) {\n            tooltipItems = tooltipItems.sort((a, b)=>options.itemSort(a, b, data));\n        }\n        helpers_segment.each(tooltipItems, (context)=>{\n            const scoped = overrideCallbacks(options.callbacks, context);\n            labelColors.push(invokeCallbackWithFallback(scoped, 'labelColor', this, context));\n            labelPointStyles.push(invokeCallbackWithFallback(scoped, 'labelPointStyle', this, context));\n            labelTextColors.push(invokeCallbackWithFallback(scoped, 'labelTextColor', this, context));\n        });\n        this.labelColors = labelColors;\n        this.labelPointStyles = labelPointStyles;\n        this.labelTextColors = labelTextColors;\n        this.dataPoints = tooltipItems;\n        return tooltipItems;\n    }\n    update(changed, replay) {\n        const options = this.options.setContext(this.getContext());\n        const active = this._active;\n        let properties;\n        let tooltipItems = [];\n        if (!active.length) {\n            if (this.opacity !== 0) {\n                properties = {\n                    opacity: 0\n                };\n            }\n        } else {\n            const position = positioners[options.position].call(this, active, this._eventPosition);\n            tooltipItems = this._createItems(options);\n            this.title = this.getTitle(tooltipItems, options);\n            this.beforeBody = this.getBeforeBody(tooltipItems, options);\n            this.body = this.getBody(tooltipItems, options);\n            this.afterBody = this.getAfterBody(tooltipItems, options);\n            this.footer = this.getFooter(tooltipItems, options);\n            const size = this._size = getTooltipSize(this, options);\n            const positionAndSize = Object.assign({}, position, size);\n            const alignment = determineAlignment(this.chart, options, positionAndSize);\n            const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this.chart);\n            this.xAlign = alignment.xAlign;\n            this.yAlign = alignment.yAlign;\n            properties = {\n                opacity: 1,\n                x: backgroundPoint.x,\n                y: backgroundPoint.y,\n                width: size.width,\n                height: size.height,\n                caretX: position.x,\n                caretY: position.y\n            };\n        }\n        this._tooltipItems = tooltipItems;\n        this.$context = undefined;\n        if (properties) {\n            this._resolveAnimations().update(this, properties);\n        }\n        if (changed && options.external) {\n            options.external.call(this, {\n                chart: this.chart,\n                tooltip: this,\n                replay\n            });\n        }\n    }\n    drawCaret(tooltipPoint, ctx, size, options) {\n        const caretPosition = this.getCaretPosition(tooltipPoint, size, options);\n        ctx.lineTo(caretPosition.x1, caretPosition.y1);\n        ctx.lineTo(caretPosition.x2, caretPosition.y2);\n        ctx.lineTo(caretPosition.x3, caretPosition.y3);\n    }\n    getCaretPosition(tooltipPoint, size, options) {\n        const { xAlign , yAlign  } = this;\n        const { caretSize , cornerRadius  } = options;\n        const { topLeft , topRight , bottomLeft , bottomRight  } = helpers_segment.toTRBLCorners(cornerRadius);\n        const { x: ptX , y: ptY  } = tooltipPoint;\n        const { width , height  } = size;\n        let x1, x2, x3, y1, y2, y3;\n        if (yAlign === 'center') {\n            y2 = ptY + height / 2;\n            if (xAlign === 'left') {\n                x1 = ptX;\n                x2 = x1 - caretSize;\n                y1 = y2 + caretSize;\n                y3 = y2 - caretSize;\n            } else {\n                x1 = ptX + width;\n                x2 = x1 + caretSize;\n                y1 = y2 - caretSize;\n                y3 = y2 + caretSize;\n            }\n            x3 = x1;\n        } else {\n            if (xAlign === 'left') {\n                x2 = ptX + Math.max(topLeft, bottomLeft) + caretSize;\n            } else if (xAlign === 'right') {\n                x2 = ptX + width - Math.max(topRight, bottomRight) - caretSize;\n            } else {\n                x2 = this.caretX;\n            }\n            if (yAlign === 'top') {\n                y1 = ptY;\n                y2 = y1 - caretSize;\n                x1 = x2 - caretSize;\n                x3 = x2 + caretSize;\n            } else {\n                y1 = ptY + height;\n                y2 = y1 + caretSize;\n                x1 = x2 + caretSize;\n                x3 = x2 - caretSize;\n            }\n            y3 = y1;\n        }\n        return {\n            x1,\n            x2,\n            x3,\n            y1,\n            y2,\n            y3\n        };\n    }\n    drawTitle(pt, ctx, options) {\n        const title = this.title;\n        const length = title.length;\n        let titleFont, titleSpacing, i;\n        if (length) {\n            const rtlHelper = helpers_segment.getRtlAdapter(options.rtl, this.x, this.width);\n            pt.x = getAlignedX(this, options.titleAlign, options);\n            ctx.textAlign = rtlHelper.textAlign(options.titleAlign);\n            ctx.textBaseline = 'middle';\n            titleFont = helpers_segment.toFont(options.titleFont);\n            titleSpacing = options.titleSpacing;\n            ctx.fillStyle = options.titleColor;\n            ctx.font = titleFont.string;\n            for(i = 0; i < length; ++i){\n                ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2);\n                pt.y += titleFont.lineHeight + titleSpacing;\n                if (i + 1 === length) {\n                    pt.y += options.titleMarginBottom - titleSpacing;\n                }\n            }\n        }\n    }\n _drawColorBox(ctx, pt, i, rtlHelper, options) {\n        const labelColors = this.labelColors[i];\n        const labelPointStyle = this.labelPointStyles[i];\n        const { boxHeight , boxWidth , boxPadding  } = options;\n        const bodyFont = helpers_segment.toFont(options.bodyFont);\n        const colorX = getAlignedX(this, 'left', options);\n        const rtlColorX = rtlHelper.x(colorX);\n        const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;\n        const colorY = pt.y + yOffSet;\n        if (options.usePointStyle) {\n            const drawOptions = {\n                radius: Math.min(boxWidth, boxHeight) / 2,\n                pointStyle: labelPointStyle.pointStyle,\n                rotation: labelPointStyle.rotation,\n                borderWidth: 1\n            };\n            const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2;\n            const centerY = colorY + boxHeight / 2;\n            ctx.strokeStyle = options.multiKeyBackground;\n            ctx.fillStyle = options.multiKeyBackground;\n            helpers_segment.drawPoint(ctx, drawOptions, centerX, centerY);\n            ctx.strokeStyle = labelColors.borderColor;\n            ctx.fillStyle = labelColors.backgroundColor;\n            helpers_segment.drawPoint(ctx, drawOptions, centerX, centerY);\n        } else {\n            ctx.lineWidth = helpers_segment.isObject(labelColors.borderWidth) ? Math.max(...Object.values(labelColors.borderWidth)) : labelColors.borderWidth || 1;\n            ctx.strokeStyle = labelColors.borderColor;\n            ctx.setLineDash(labelColors.borderDash || []);\n            ctx.lineDashOffset = labelColors.borderDashOffset || 0;\n            const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth - boxPadding);\n            const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - boxPadding - 2);\n            const borderRadius = helpers_segment.toTRBLCorners(labelColors.borderRadius);\n            if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                ctx.beginPath();\n                ctx.fillStyle = options.multiKeyBackground;\n                helpers_segment.addRoundedRectPath(ctx, {\n                    x: outerX,\n                    y: colorY,\n                    w: boxWidth,\n                    h: boxHeight,\n                    radius: borderRadius\n                });\n                ctx.fill();\n                ctx.stroke();\n                ctx.fillStyle = labelColors.backgroundColor;\n                ctx.beginPath();\n                helpers_segment.addRoundedRectPath(ctx, {\n                    x: innerX,\n                    y: colorY + 1,\n                    w: boxWidth - 2,\n                    h: boxHeight - 2,\n                    radius: borderRadius\n                });\n                ctx.fill();\n            } else {\n                ctx.fillStyle = options.multiKeyBackground;\n                ctx.fillRect(outerX, colorY, boxWidth, boxHeight);\n                ctx.strokeRect(outerX, colorY, boxWidth, boxHeight);\n                ctx.fillStyle = labelColors.backgroundColor;\n                ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2);\n            }\n        }\n        ctx.fillStyle = this.labelTextColors[i];\n    }\n    drawBody(pt, ctx, options) {\n        const { body  } = this;\n        const { bodySpacing , bodyAlign , displayColors , boxHeight , boxWidth , boxPadding  } = options;\n        const bodyFont = helpers_segment.toFont(options.bodyFont);\n        let bodyLineHeight = bodyFont.lineHeight;\n        let xLinePadding = 0;\n        const rtlHelper = helpers_segment.getRtlAdapter(options.rtl, this.x, this.width);\n        const fillLineOfText = function(line) {\n            ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);\n            pt.y += bodyLineHeight + bodySpacing;\n        };\n        const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);\n        let bodyItem, textColor, lines, i, j, ilen, jlen;\n        ctx.textAlign = bodyAlign;\n        ctx.textBaseline = 'middle';\n        ctx.font = bodyFont.string;\n        pt.x = getAlignedX(this, bodyAlignForCalculation, options);\n        ctx.fillStyle = options.bodyColor;\n        helpers_segment.each(this.beforeBody, fillLineOfText);\n        xLinePadding = displayColors && bodyAlignForCalculation !== 'right' ? bodyAlign === 'center' ? boxWidth / 2 + boxPadding : boxWidth + 2 + boxPadding : 0;\n        for(i = 0, ilen = body.length; i < ilen; ++i){\n            bodyItem = body[i];\n            textColor = this.labelTextColors[i];\n            ctx.fillStyle = textColor;\n            helpers_segment.each(bodyItem.before, fillLineOfText);\n            lines = bodyItem.lines;\n            if (displayColors && lines.length) {\n                this._drawColorBox(ctx, pt, i, rtlHelper, options);\n                bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);\n            }\n            for(j = 0, jlen = lines.length; j < jlen; ++j){\n                fillLineOfText(lines[j]);\n                bodyLineHeight = bodyFont.lineHeight;\n            }\n            helpers_segment.each(bodyItem.after, fillLineOfText);\n        }\n        xLinePadding = 0;\n        bodyLineHeight = bodyFont.lineHeight;\n        helpers_segment.each(this.afterBody, fillLineOfText);\n        pt.y -= bodySpacing;\n    }\n    drawFooter(pt, ctx, options) {\n        const footer = this.footer;\n        const length = footer.length;\n        let footerFont, i;\n        if (length) {\n            const rtlHelper = helpers_segment.getRtlAdapter(options.rtl, this.x, this.width);\n            pt.x = getAlignedX(this, options.footerAlign, options);\n            pt.y += options.footerMarginTop;\n            ctx.textAlign = rtlHelper.textAlign(options.footerAlign);\n            ctx.textBaseline = 'middle';\n            footerFont = helpers_segment.toFont(options.footerFont);\n            ctx.fillStyle = options.footerColor;\n            ctx.font = footerFont.string;\n            for(i = 0; i < length; ++i){\n                ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2);\n                pt.y += footerFont.lineHeight + options.footerSpacing;\n            }\n        }\n    }\n    drawBackground(pt, ctx, tooltipSize, options) {\n        const { xAlign , yAlign  } = this;\n        const { x , y  } = pt;\n        const { width , height  } = tooltipSize;\n        const { topLeft , topRight , bottomLeft , bottomRight  } = helpers_segment.toTRBLCorners(options.cornerRadius);\n        ctx.fillStyle = options.backgroundColor;\n        ctx.strokeStyle = options.borderColor;\n        ctx.lineWidth = options.borderWidth;\n        ctx.beginPath();\n        ctx.moveTo(x + topLeft, y);\n        if (yAlign === 'top') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x + width - topRight, y);\n        ctx.quadraticCurveTo(x + width, y, x + width, y + topRight);\n        if (yAlign === 'center' && xAlign === 'right') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x + width, y + height - bottomRight);\n        ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height);\n        if (yAlign === 'bottom') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x + bottomLeft, y + height);\n        ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft);\n        if (yAlign === 'center' && xAlign === 'left') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x, y + topLeft);\n        ctx.quadraticCurveTo(x, y, x + topLeft, y);\n        ctx.closePath();\n        ctx.fill();\n        if (options.borderWidth > 0) {\n            ctx.stroke();\n        }\n    }\n _updateAnimationTarget(options) {\n        const chart = this.chart;\n        const anims = this.$animations;\n        const animX = anims && anims.x;\n        const animY = anims && anims.y;\n        if (animX || animY) {\n            const position = positioners[options.position].call(this, this._active, this._eventPosition);\n            if (!position) {\n                return;\n            }\n            const size = this._size = getTooltipSize(this, options);\n            const positionAndSize = Object.assign({}, position, this._size);\n            const alignment = determineAlignment(chart, options, positionAndSize);\n            const point = getBackgroundPoint(options, positionAndSize, alignment, chart);\n            if (animX._to !== point.x || animY._to !== point.y) {\n                this.xAlign = alignment.xAlign;\n                this.yAlign = alignment.yAlign;\n                this.width = size.width;\n                this.height = size.height;\n                this.caretX = position.x;\n                this.caretY = position.y;\n                this._resolveAnimations().update(this, point);\n            }\n        }\n    }\n _willRender() {\n        return !!this.opacity;\n    }\n    draw(ctx) {\n        const options = this.options.setContext(this.getContext());\n        let opacity = this.opacity;\n        if (!opacity) {\n            return;\n        }\n        this._updateAnimationTarget(options);\n        const tooltipSize = {\n            width: this.width,\n            height: this.height\n        };\n        const pt = {\n            x: this.x,\n            y: this.y\n        };\n        opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity;\n        const padding = helpers_segment.toPadding(options.padding);\n        const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length;\n        if (options.enabled && hasTooltipContent) {\n            ctx.save();\n            ctx.globalAlpha = opacity;\n            this.drawBackground(pt, ctx, tooltipSize, options);\n            helpers_segment.overrideTextDirection(ctx, options.textDirection);\n            pt.y += padding.top;\n            this.drawTitle(pt, ctx, options);\n            this.drawBody(pt, ctx, options);\n            this.drawFooter(pt, ctx, options);\n            helpers_segment.restoreTextDirection(ctx, options.textDirection);\n            ctx.restore();\n        }\n    }\n getActiveElements() {\n        return this._active || [];\n    }\n setActiveElements(activeElements, eventPosition) {\n        const lastActive = this._active;\n        const active = activeElements.map(({ datasetIndex , index  })=>{\n            const meta = this.chart.getDatasetMeta(datasetIndex);\n            if (!meta) {\n                throw new Error('Cannot find a dataset at index ' + datasetIndex);\n            }\n            return {\n                datasetIndex,\n                element: meta.data[index],\n                index\n            };\n        });\n        const changed = !helpers_segment._elementsEqual(lastActive, active);\n        const positionChanged = this._positionChanged(active, eventPosition);\n        if (changed || positionChanged) {\n            this._active = active;\n            this._eventPosition = eventPosition;\n            this._ignoreReplayEvents = true;\n            this.update(true);\n        }\n    }\n handleEvent(e, replay, inChartArea = true) {\n        if (replay && this._ignoreReplayEvents) {\n            return false;\n        }\n        this._ignoreReplayEvents = false;\n        const options = this.options;\n        const lastActive = this._active || [];\n        const active = this._getActiveElements(e, lastActive, replay, inChartArea);\n        const positionChanged = this._positionChanged(active, e);\n        const changed = replay || !helpers_segment._elementsEqual(active, lastActive) || positionChanged;\n        if (changed) {\n            this._active = active;\n            if (options.enabled || options.external) {\n                this._eventPosition = {\n                    x: e.x,\n                    y: e.y\n                };\n                this.update(true, replay);\n            }\n        }\n        return changed;\n    }\n _getActiveElements(e, lastActive, replay, inChartArea) {\n        const options = this.options;\n        if (e.type === 'mouseout') {\n            return [];\n        }\n        if (!inChartArea) {\n            return lastActive;\n        }\n        const active = this.chart.getElementsAtEventForMode(e, options.mode, options, replay);\n        if (options.reverse) {\n            active.reverse();\n        }\n        return active;\n    }\n _positionChanged(active, e) {\n        const { caretX , caretY , options  } = this;\n        const position = positioners[options.position].call(this, active, e);\n        return position !== false && (caretX !== position.x || caretY !== position.y);\n    }\n}\nvar plugin_tooltip = {\n    id: 'tooltip',\n    _element: Tooltip,\n    positioners,\n    afterInit (chart, _args, options) {\n        if (options) {\n            chart.tooltip = new Tooltip({\n                chart,\n                options\n            });\n        }\n    },\n    beforeUpdate (chart, _args, options) {\n        if (chart.tooltip) {\n            chart.tooltip.initialize(options);\n        }\n    },\n    reset (chart, _args, options) {\n        if (chart.tooltip) {\n            chart.tooltip.initialize(options);\n        }\n    },\n    afterDraw (chart) {\n        const tooltip = chart.tooltip;\n        if (tooltip && tooltip._willRender()) {\n            const args = {\n                tooltip\n            };\n            if (chart.notifyPlugins('beforeTooltipDraw', {\n                ...args,\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            tooltip.draw(chart.ctx);\n            chart.notifyPlugins('afterTooltipDraw', args);\n        }\n    },\n    afterEvent (chart, args) {\n        if (chart.tooltip) {\n            const useFinalPosition = args.replay;\n            if (chart.tooltip.handleEvent(args.event, useFinalPosition, args.inChartArea)) {\n                args.changed = true;\n            }\n        }\n    },\n    defaults: {\n        enabled: true,\n        external: null,\n        position: 'average',\n        backgroundColor: 'rgba(0,0,0,0.8)',\n        titleColor: '#fff',\n        titleFont: {\n            weight: 'bold'\n        },\n        titleSpacing: 2,\n        titleMarginBottom: 6,\n        titleAlign: 'left',\n        bodyColor: '#fff',\n        bodySpacing: 2,\n        bodyFont: {},\n        bodyAlign: 'left',\n        footerColor: '#fff',\n        footerSpacing: 2,\n        footerMarginTop: 6,\n        footerFont: {\n            weight: 'bold'\n        },\n        footerAlign: 'left',\n        padding: 6,\n        caretPadding: 2,\n        caretSize: 5,\n        cornerRadius: 6,\n        boxHeight: (ctx, opts)=>opts.bodyFont.size,\n        boxWidth: (ctx, opts)=>opts.bodyFont.size,\n        multiKeyBackground: '#fff',\n        displayColors: true,\n        boxPadding: 0,\n        borderColor: 'rgba(0,0,0,0)',\n        borderWidth: 0,\n        animation: {\n            duration: 400,\n            easing: 'easeOutQuart'\n        },\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'width',\n                    'height',\n                    'caretX',\n                    'caretY'\n                ]\n            },\n            opacity: {\n                easing: 'linear',\n                duration: 200\n            }\n        },\n        callbacks: defaultCallbacks\n    },\n    defaultRoutes: {\n        bodyFont: 'font',\n        footerFont: 'font',\n        titleFont: 'font'\n    },\n    descriptors: {\n        _scriptable: (name)=>name !== 'filter' && name !== 'itemSort' && name !== 'external',\n        _indexable: false,\n        callbacks: {\n            _scriptable: false,\n            _indexable: false\n        },\n        animation: {\n            _fallback: false\n        },\n        animations: {\n            _fallback: 'animation'\n        }\n    },\n    additionalOptionScopes: [\n        'interaction'\n    ]\n};\n\nvar plugins = /*#__PURE__*/Object.freeze({\n__proto__: null,\nColors: plugin_colors,\nDecimation: plugin_decimation,\nFiller: index,\nLegend: plugin_legend,\nSubTitle: plugin_subtitle,\nTitle: plugin_title,\nTooltip: plugin_tooltip\n});\n\nconst addIfString = (labels, raw, index, addedLabels)=>{\n    if (typeof raw === 'string') {\n        index = labels.push(raw) - 1;\n        addedLabels.unshift({\n            index,\n            label: raw\n        });\n    } else if (isNaN(raw)) {\n        index = null;\n    }\n    return index;\n};\nfunction findOrAddLabel(labels, raw, index, addedLabels) {\n    const first = labels.indexOf(raw);\n    if (first === -1) {\n        return addIfString(labels, raw, index, addedLabels);\n    }\n    const last = labels.lastIndexOf(raw);\n    return first !== last ? index : first;\n}\nconst validIndex = (index, max)=>index === null ? null : helpers_segment._limitValue(Math.round(index), 0, max);\nfunction _getLabelForValue(value) {\n    const labels = this.getLabels();\n    if (value >= 0 && value < labels.length) {\n        return labels[value];\n    }\n    return value;\n}\nclass CategoryScale extends Scale {\n    static id = 'category';\n static defaults = {\n        ticks: {\n            callback: _getLabelForValue\n        }\n    };\n    constructor(cfg){\n        super(cfg);\n         this._startValue = undefined;\n        this._valueRange = 0;\n        this._addedLabels = [];\n    }\n    init(scaleOptions) {\n        const added = this._addedLabels;\n        if (added.length) {\n            const labels = this.getLabels();\n            for (const { index , label  } of added){\n                if (labels[index] === label) {\n                    labels.splice(index, 1);\n                }\n            }\n            this._addedLabels = [];\n        }\n        super.init(scaleOptions);\n    }\n    parse(raw, index) {\n        if (helpers_segment.isNullOrUndef(raw)) {\n            return null;\n        }\n        const labels = this.getLabels();\n        index = isFinite(index) && labels[index] === raw ? index : findOrAddLabel(labels, raw, helpers_segment.valueOrDefault(index, raw), this._addedLabels);\n        return validIndex(index, labels.length - 1);\n    }\n    determineDataLimits() {\n        const { minDefined , maxDefined  } = this.getUserBounds();\n        let { min , max  } = this.getMinMax(true);\n        if (this.options.bounds === 'ticks') {\n            if (!minDefined) {\n                min = 0;\n            }\n            if (!maxDefined) {\n                max = this.getLabels().length - 1;\n            }\n        }\n        this.min = min;\n        this.max = max;\n    }\n    buildTicks() {\n        const min = this.min;\n        const max = this.max;\n        const offset = this.options.offset;\n        const ticks = [];\n        let labels = this.getLabels();\n        labels = min === 0 && max === labels.length - 1 ? labels : labels.slice(min, max + 1);\n        this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);\n        this._startValue = this.min - (offset ? 0.5 : 0);\n        for(let value = min; value <= max; value++){\n            ticks.push({\n                value\n            });\n        }\n        return ticks;\n    }\n    getLabelForValue(value) {\n        return _getLabelForValue.call(this, value);\n    }\n configure() {\n        super.configure();\n        if (!this.isHorizontal()) {\n            this._reversePixels = !this._reversePixels;\n        }\n    }\n    getPixelForValue(value) {\n        if (typeof value !== 'number') {\n            value = this.parse(value);\n        }\n        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);\n    }\n    getPixelForTick(index) {\n        const ticks = this.ticks;\n        if (index < 0 || index > ticks.length - 1) {\n            return null;\n        }\n        return this.getPixelForValue(ticks[index].value);\n    }\n    getValueForPixel(pixel) {\n        return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);\n    }\n    getBasePixel() {\n        return this.bottom;\n    }\n}\n\nfunction generateTicks$1(generationOptions, dataRange) {\n    const ticks = [];\n    const MIN_SPACING = 1e-14;\n    const { bounds , step , min , max , precision , count , maxTicks , maxDigits , includeBounds  } = generationOptions;\n    const unit = step || 1;\n    const maxSpaces = maxTicks - 1;\n    const { min: rmin , max: rmax  } = dataRange;\n    const minDefined = !helpers_segment.isNullOrUndef(min);\n    const maxDefined = !helpers_segment.isNullOrUndef(max);\n    const countDefined = !helpers_segment.isNullOrUndef(count);\n    const minSpacing = (rmax - rmin) / (maxDigits + 1);\n    let spacing = helpers_segment.niceNum((rmax - rmin) / maxSpaces / unit) * unit;\n    let factor, niceMin, niceMax, numSpaces;\n    if (spacing < MIN_SPACING && !minDefined && !maxDefined) {\n        return [\n            {\n                value: rmin\n            },\n            {\n                value: rmax\n            }\n        ];\n    }\n    numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);\n    if (numSpaces > maxSpaces) {\n        spacing = helpers_segment.niceNum(numSpaces * spacing / maxSpaces / unit) * unit;\n    }\n    if (!helpers_segment.isNullOrUndef(precision)) {\n        factor = Math.pow(10, precision);\n        spacing = Math.ceil(spacing * factor) / factor;\n    }\n    if (bounds === 'ticks') {\n        niceMin = Math.floor(rmin / spacing) * spacing;\n        niceMax = Math.ceil(rmax / spacing) * spacing;\n    } else {\n        niceMin = rmin;\n        niceMax = rmax;\n    }\n    if (minDefined && maxDefined && step && helpers_segment.almostWhole((max - min) / step, spacing / 1000)) {\n        numSpaces = Math.round(Math.min((max - min) / spacing, maxTicks));\n        spacing = (max - min) / numSpaces;\n        niceMin = min;\n        niceMax = max;\n    } else if (countDefined) {\n        niceMin = minDefined ? min : niceMin;\n        niceMax = maxDefined ? max : niceMax;\n        numSpaces = count - 1;\n        spacing = (niceMax - niceMin) / numSpaces;\n    } else {\n        numSpaces = (niceMax - niceMin) / spacing;\n        if (helpers_segment.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {\n            numSpaces = Math.round(numSpaces);\n        } else {\n            numSpaces = Math.ceil(numSpaces);\n        }\n    }\n    const decimalPlaces = Math.max(helpers_segment._decimalPlaces(spacing), helpers_segment._decimalPlaces(niceMin));\n    factor = Math.pow(10, helpers_segment.isNullOrUndef(precision) ? decimalPlaces : precision);\n    niceMin = Math.round(niceMin * factor) / factor;\n    niceMax = Math.round(niceMax * factor) / factor;\n    let j = 0;\n    if (minDefined) {\n        if (includeBounds && niceMin !== min) {\n            ticks.push({\n                value: min\n            });\n            if (niceMin < min) {\n                j++;\n            }\n            if (helpers_segment.almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) {\n                j++;\n            }\n        } else if (niceMin < min) {\n            j++;\n        }\n    }\n    for(; j < numSpaces; ++j){\n        ticks.push({\n            value: Math.round((niceMin + j * spacing) * factor) / factor\n        });\n    }\n    if (maxDefined && includeBounds && niceMax !== max) {\n        if (ticks.length && helpers_segment.almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) {\n            ticks[ticks.length - 1].value = max;\n        } else {\n            ticks.push({\n                value: max\n            });\n        }\n    } else if (!maxDefined || niceMax === max) {\n        ticks.push({\n            value: niceMax\n        });\n    }\n    return ticks;\n}\nfunction relativeLabelSize(value, minSpacing, { horizontal , minRotation  }) {\n    const rad = helpers_segment.toRadians(minRotation);\n    const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 0.001;\n    const length = 0.75 * minSpacing * ('' + value).length;\n    return Math.min(minSpacing / ratio, length);\n}\nclass LinearScaleBase extends Scale {\n    constructor(cfg){\n        super(cfg);\n         this.start = undefined;\n         this.end = undefined;\n         this._startValue = undefined;\n         this._endValue = undefined;\n        this._valueRange = 0;\n    }\n    parse(raw, index) {\n        if (helpers_segment.isNullOrUndef(raw)) {\n            return null;\n        }\n        if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(+raw)) {\n            return null;\n        }\n        return +raw;\n    }\n    handleTickRangeOptions() {\n        const { beginAtZero  } = this.options;\n        const { minDefined , maxDefined  } = this.getUserBounds();\n        let { min , max  } = this;\n        const setMin = (v)=>min = minDefined ? min : v;\n        const setMax = (v)=>max = maxDefined ? max : v;\n        if (beginAtZero) {\n            const minSign = helpers_segment.sign(min);\n            const maxSign = helpers_segment.sign(max);\n            if (minSign < 0 && maxSign < 0) {\n                setMax(0);\n            } else if (minSign > 0 && maxSign > 0) {\n                setMin(0);\n            }\n        }\n        if (min === max) {\n            let offset = max === 0 ? 1 : Math.abs(max * 0.05);\n            setMax(max + offset);\n            if (!beginAtZero) {\n                setMin(min - offset);\n            }\n        }\n        this.min = min;\n        this.max = max;\n    }\n    getTickLimit() {\n        const tickOpts = this.options.ticks;\n        let { maxTicksLimit , stepSize  } = tickOpts;\n        let maxTicks;\n        if (stepSize) {\n            maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;\n            if (maxTicks > 1000) {\n                console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);\n                maxTicks = 1000;\n            }\n        } else {\n            maxTicks = this.computeTickLimit();\n            maxTicksLimit = maxTicksLimit || 11;\n        }\n        if (maxTicksLimit) {\n            maxTicks = Math.min(maxTicksLimit, maxTicks);\n        }\n        return maxTicks;\n    }\n computeTickLimit() {\n        return Number.POSITIVE_INFINITY;\n    }\n    buildTicks() {\n        const opts = this.options;\n        const tickOpts = opts.ticks;\n        let maxTicks = this.getTickLimit();\n        maxTicks = Math.max(2, maxTicks);\n        const numericGeneratorOptions = {\n            maxTicks,\n            bounds: opts.bounds,\n            min: opts.min,\n            max: opts.max,\n            precision: tickOpts.precision,\n            step: tickOpts.stepSize,\n            count: tickOpts.count,\n            maxDigits: this._maxDigits(),\n            horizontal: this.isHorizontal(),\n            minRotation: tickOpts.minRotation || 0,\n            includeBounds: tickOpts.includeBounds !== false\n        };\n        const dataRange = this._range || this;\n        const ticks = generateTicks$1(numericGeneratorOptions, dataRange);\n        if (opts.bounds === 'ticks') {\n            helpers_segment._setMinAndMaxByKey(ticks, this, 'value');\n        }\n        if (opts.reverse) {\n            ticks.reverse();\n            this.start = this.max;\n            this.end = this.min;\n        } else {\n            this.start = this.min;\n            this.end = this.max;\n        }\n        return ticks;\n    }\n configure() {\n        const ticks = this.ticks;\n        let start = this.min;\n        let end = this.max;\n        super.configure();\n        if (this.options.offset && ticks.length) {\n            const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;\n            start -= offset;\n            end += offset;\n        }\n        this._startValue = start;\n        this._endValue = end;\n        this._valueRange = end - start;\n    }\n    getLabelForValue(value) {\n        return helpers_segment.formatNumber(value, this.chart.options.locale, this.options.ticks.format);\n    }\n}\n\nclass LinearScale extends LinearScaleBase {\n    static id = 'linear';\n static defaults = {\n        ticks: {\n            callback: helpers_segment.Ticks.formatters.numeric\n        }\n    };\n    determineDataLimits() {\n        const { min , max  } = this.getMinMax(true);\n        this.min = helpers_segment.isNumberFinite(min) ? min : 0;\n        this.max = helpers_segment.isNumberFinite(max) ? max : 1;\n        this.handleTickRangeOptions();\n    }\n computeTickLimit() {\n        const horizontal = this.isHorizontal();\n        const length = horizontal ? this.width : this.height;\n        const minRotation = helpers_segment.toRadians(this.options.ticks.minRotation);\n        const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;\n        const tickFont = this._resolveTickFontOptions(0);\n        return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));\n    }\n    getPixelForValue(value) {\n        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);\n    }\n    getValueForPixel(pixel) {\n        return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;\n    }\n}\n\nconst log10Floor = (v)=>Math.floor(helpers_segment.log10(v));\nconst changeExponent = (v, m)=>Math.pow(10, log10Floor(v) + m);\nfunction isMajor(tickVal) {\n    const remain = tickVal / Math.pow(10, log10Floor(tickVal));\n    return remain === 1;\n}\nfunction steps(min, max, rangeExp) {\n    const rangeStep = Math.pow(10, rangeExp);\n    const start = Math.floor(min / rangeStep);\n    const end = Math.ceil(max / rangeStep);\n    return end - start;\n}\nfunction startExp(min, max) {\n    const range = max - min;\n    let rangeExp = log10Floor(range);\n    while(steps(min, max, rangeExp) > 10){\n        rangeExp++;\n    }\n    while(steps(min, max, rangeExp) < 10){\n        rangeExp--;\n    }\n    return Math.min(rangeExp, log10Floor(min));\n}\n function generateTicks(generationOptions, { min , max  }) {\n    min = helpers_segment.finiteOrDefault(generationOptions.min, min);\n    const ticks = [];\n    const minExp = log10Floor(min);\n    let exp = startExp(min, max);\n    let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;\n    const stepSize = Math.pow(10, exp);\n    const base = minExp > exp ? Math.pow(10, minExp) : 0;\n    const start = Math.round((min - base) * precision) / precision;\n    const offset = Math.floor((min - base) / stepSize / 10) * stepSize * 10;\n    let significand = Math.floor((start - offset) / Math.pow(10, exp));\n    let value = helpers_segment.finiteOrDefault(generationOptions.min, Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision);\n    while(value < max){\n        ticks.push({\n            value,\n            major: isMajor(value),\n            significand\n        });\n        if (significand >= 10) {\n            significand = significand < 15 ? 15 : 20;\n        } else {\n            significand++;\n        }\n        if (significand >= 20) {\n            exp++;\n            significand = 2;\n            precision = exp >= 0 ? 1 : precision;\n        }\n        value = Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision;\n    }\n    const lastTick = helpers_segment.finiteOrDefault(generationOptions.max, value);\n    ticks.push({\n        value: lastTick,\n        major: isMajor(lastTick),\n        significand\n    });\n    return ticks;\n}\nclass LogarithmicScale extends Scale {\n    static id = 'logarithmic';\n static defaults = {\n        ticks: {\n            callback: helpers_segment.Ticks.formatters.logarithmic,\n            major: {\n                enabled: true\n            }\n        }\n    };\n    constructor(cfg){\n        super(cfg);\n         this.start = undefined;\n         this.end = undefined;\n         this._startValue = undefined;\n        this._valueRange = 0;\n    }\n    parse(raw, index) {\n        const value = LinearScaleBase.prototype.parse.apply(this, [\n            raw,\n            index\n        ]);\n        if (value === 0) {\n            this._zero = true;\n            return undefined;\n        }\n        return helpers_segment.isNumberFinite(value) && value > 0 ? value : null;\n    }\n    determineDataLimits() {\n        const { min , max  } = this.getMinMax(true);\n        this.min = helpers_segment.isNumberFinite(min) ? Math.max(0, min) : null;\n        this.max = helpers_segment.isNumberFinite(max) ? Math.max(0, max) : null;\n        if (this.options.beginAtZero) {\n            this._zero = true;\n        }\n        if (this._zero && this.min !== this._suggestedMin && !helpers_segment.isNumberFinite(this._userMin)) {\n            this.min = min === changeExponent(this.min, 0) ? changeExponent(this.min, -1) : changeExponent(this.min, 0);\n        }\n        this.handleTickRangeOptions();\n    }\n    handleTickRangeOptions() {\n        const { minDefined , maxDefined  } = this.getUserBounds();\n        let min = this.min;\n        let max = this.max;\n        const setMin = (v)=>min = minDefined ? min : v;\n        const setMax = (v)=>max = maxDefined ? max : v;\n        if (min === max) {\n            if (min <= 0) {\n                setMin(1);\n                setMax(10);\n            } else {\n                setMin(changeExponent(min, -1));\n                setMax(changeExponent(max, +1));\n            }\n        }\n        if (min <= 0) {\n            setMin(changeExponent(max, -1));\n        }\n        if (max <= 0) {\n            setMax(changeExponent(min, +1));\n        }\n        this.min = min;\n        this.max = max;\n    }\n    buildTicks() {\n        const opts = this.options;\n        const generationOptions = {\n            min: this._userMin,\n            max: this._userMax\n        };\n        const ticks = generateTicks(generationOptions, this);\n        if (opts.bounds === 'ticks') {\n            helpers_segment._setMinAndMaxByKey(ticks, this, 'value');\n        }\n        if (opts.reverse) {\n            ticks.reverse();\n            this.start = this.max;\n            this.end = this.min;\n        } else {\n            this.start = this.min;\n            this.end = this.max;\n        }\n        return ticks;\n    }\n getLabelForValue(value) {\n        return value === undefined ? '0' : helpers_segment.formatNumber(value, this.chart.options.locale, this.options.ticks.format);\n    }\n configure() {\n        const start = this.min;\n        super.configure();\n        this._startValue = helpers_segment.log10(start);\n        this._valueRange = helpers_segment.log10(this.max) - helpers_segment.log10(start);\n    }\n    getPixelForValue(value) {\n        if (value === undefined || value === 0) {\n            value = this.min;\n        }\n        if (value === null || isNaN(value)) {\n            return NaN;\n        }\n        return this.getPixelForDecimal(value === this.min ? 0 : (helpers_segment.log10(value) - this._startValue) / this._valueRange);\n    }\n    getValueForPixel(pixel) {\n        const decimal = this.getDecimalForPixel(pixel);\n        return Math.pow(10, this._startValue + decimal * this._valueRange);\n    }\n}\n\nfunction getTickBackdropHeight(opts) {\n    const tickOpts = opts.ticks;\n    if (tickOpts.display && opts.display) {\n        const padding = helpers_segment.toPadding(tickOpts.backdropPadding);\n        return helpers_segment.valueOrDefault(tickOpts.font && tickOpts.font.size, helpers_segment.defaults.font.size) + padding.height;\n    }\n    return 0;\n}\nfunction measureLabelSize(ctx, font, label) {\n    label = helpers_segment.isArray(label) ? label : [\n        label\n    ];\n    return {\n        w: helpers_segment._longestText(ctx, font.string, label),\n        h: label.length * font.lineHeight\n    };\n}\nfunction determineLimits(angle, pos, size, min, max) {\n    if (angle === min || angle === max) {\n        return {\n            start: pos - size / 2,\n            end: pos + size / 2\n        };\n    } else if (angle < min || angle > max) {\n        return {\n            start: pos - size,\n            end: pos\n        };\n    }\n    return {\n        start: pos,\n        end: pos + size\n    };\n}\n function fitWithPointLabels(scale) {\n    const orig = {\n        l: scale.left + scale._padding.left,\n        r: scale.right - scale._padding.right,\n        t: scale.top + scale._padding.top,\n        b: scale.bottom - scale._padding.bottom\n    };\n    const limits = Object.assign({}, orig);\n    const labelSizes = [];\n    const padding = [];\n    const valueCount = scale._pointLabels.length;\n    const pointLabelOpts = scale.options.pointLabels;\n    const additionalAngle = pointLabelOpts.centerPointLabels ? helpers_segment.PI / valueCount : 0;\n    for(let i = 0; i < valueCount; i++){\n        const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i));\n        padding[i] = opts.padding;\n        const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle);\n        const plFont = helpers_segment.toFont(opts.font);\n        const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);\n        labelSizes[i] = textSize;\n        const angleRadians = helpers_segment._normalizeAngle(scale.getIndexAngle(i) + additionalAngle);\n        const angle = Math.round(helpers_segment.toDegrees(angleRadians));\n        const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);\n        const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);\n        updateLimits(limits, orig, angleRadians, hLimits, vLimits);\n    }\n    scale.setCenterPoint(orig.l - limits.l, limits.r - orig.r, orig.t - limits.t, limits.b - orig.b);\n    scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);\n}\nfunction updateLimits(limits, orig, angle, hLimits, vLimits) {\n    const sin = Math.abs(Math.sin(angle));\n    const cos = Math.abs(Math.cos(angle));\n    let x = 0;\n    let y = 0;\n    if (hLimits.start < orig.l) {\n        x = (orig.l - hLimits.start) / sin;\n        limits.l = Math.min(limits.l, orig.l - x);\n    } else if (hLimits.end > orig.r) {\n        x = (hLimits.end - orig.r) / sin;\n        limits.r = Math.max(limits.r, orig.r + x);\n    }\n    if (vLimits.start < orig.t) {\n        y = (orig.t - vLimits.start) / cos;\n        limits.t = Math.min(limits.t, orig.t - y);\n    } else if (vLimits.end > orig.b) {\n        y = (vLimits.end - orig.b) / cos;\n        limits.b = Math.max(limits.b, orig.b + y);\n    }\n}\nfunction buildPointLabelItems(scale, labelSizes, padding) {\n    const items = [];\n    const valueCount = scale._pointLabels.length;\n    const opts = scale.options;\n    const extra = getTickBackdropHeight(opts) / 2;\n    const outerDistance = scale.drawingArea;\n    const additionalAngle = opts.pointLabels.centerPointLabels ? helpers_segment.PI / valueCount : 0;\n    for(let i = 0; i < valueCount; i++){\n        const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], additionalAngle);\n        const angle = Math.round(helpers_segment.toDegrees(helpers_segment._normalizeAngle(pointLabelPosition.angle + helpers_segment.HALF_PI)));\n        const size = labelSizes[i];\n        const y = yForAngle(pointLabelPosition.y, size.h, angle);\n        const textAlign = getTextAlignForAngle(angle);\n        const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign);\n        items.push({\n            x: pointLabelPosition.x,\n            y,\n            textAlign,\n            left,\n            top: y,\n            right: left + size.w,\n            bottom: y + size.h\n        });\n    }\n    return items;\n}\nfunction getTextAlignForAngle(angle) {\n    if (angle === 0 || angle === 180) {\n        return 'center';\n    } else if (angle < 180) {\n        return 'left';\n    }\n    return 'right';\n}\nfunction leftForTextAlign(x, w, align) {\n    if (align === 'right') {\n        x -= w;\n    } else if (align === 'center') {\n        x -= w / 2;\n    }\n    return x;\n}\nfunction yForAngle(y, h, angle) {\n    if (angle === 90 || angle === 270) {\n        y -= h / 2;\n    } else if (angle > 270 || angle < 90) {\n        y -= h;\n    }\n    return y;\n}\nfunction drawPointLabels(scale, labelCount) {\n    const { ctx , options: { pointLabels  }  } = scale;\n    for(let i = labelCount - 1; i >= 0; i--){\n        const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i));\n        const plFont = helpers_segment.toFont(optsAtIndex.font);\n        const { x , y , textAlign , left , top , right , bottom  } = scale._pointLabelItems[i];\n        const { backdropColor  } = optsAtIndex;\n        if (!helpers_segment.isNullOrUndef(backdropColor)) {\n            const borderRadius = helpers_segment.toTRBLCorners(optsAtIndex.borderRadius);\n            const padding = helpers_segment.toPadding(optsAtIndex.backdropPadding);\n            ctx.fillStyle = backdropColor;\n            const backdropLeft = left - padding.left;\n            const backdropTop = top - padding.top;\n            const backdropWidth = right - left + padding.width;\n            const backdropHeight = bottom - top + padding.height;\n            if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                ctx.beginPath();\n                helpers_segment.addRoundedRectPath(ctx, {\n                    x: backdropLeft,\n                    y: backdropTop,\n                    w: backdropWidth,\n                    h: backdropHeight,\n                    radius: borderRadius\n                });\n                ctx.fill();\n            } else {\n                ctx.fillRect(backdropLeft, backdropTop, backdropWidth, backdropHeight);\n            }\n        }\n        helpers_segment.renderText(ctx, scale._pointLabels[i], x, y + plFont.lineHeight / 2, plFont, {\n            color: optsAtIndex.color,\n            textAlign: textAlign,\n            textBaseline: 'middle'\n        });\n    }\n}\nfunction pathRadiusLine(scale, radius, circular, labelCount) {\n    const { ctx  } = scale;\n    if (circular) {\n        ctx.arc(scale.xCenter, scale.yCenter, radius, 0, helpers_segment.TAU);\n    } else {\n        let pointPosition = scale.getPointPosition(0, radius);\n        ctx.moveTo(pointPosition.x, pointPosition.y);\n        for(let i = 1; i < labelCount; i++){\n            pointPosition = scale.getPointPosition(i, radius);\n            ctx.lineTo(pointPosition.x, pointPosition.y);\n        }\n    }\n}\nfunction drawRadiusLine(scale, gridLineOpts, radius, labelCount, borderOpts) {\n    const ctx = scale.ctx;\n    const circular = gridLineOpts.circular;\n    const { color , lineWidth  } = gridLineOpts;\n    if (!circular && !labelCount || !color || !lineWidth || radius < 0) {\n        return;\n    }\n    ctx.save();\n    ctx.strokeStyle = color;\n    ctx.lineWidth = lineWidth;\n    ctx.setLineDash(borderOpts.dash);\n    ctx.lineDashOffset = borderOpts.dashOffset;\n    ctx.beginPath();\n    pathRadiusLine(scale, radius, circular, labelCount);\n    ctx.closePath();\n    ctx.stroke();\n    ctx.restore();\n}\nfunction createPointLabelContext(parent, index, label) {\n    return helpers_segment.createContext(parent, {\n        label,\n        index,\n        type: 'pointLabel'\n    });\n}\nclass RadialLinearScale extends LinearScaleBase {\n    static id = 'radialLinear';\n static defaults = {\n        display: true,\n        animate: true,\n        position: 'chartArea',\n        angleLines: {\n            display: true,\n            lineWidth: 1,\n            borderDash: [],\n            borderDashOffset: 0.0\n        },\n        grid: {\n            circular: false\n        },\n        startAngle: 0,\n        ticks: {\n            showLabelBackdrop: true,\n            callback: helpers_segment.Ticks.formatters.numeric\n        },\n        pointLabels: {\n            backdropColor: undefined,\n            backdropPadding: 2,\n            display: true,\n            font: {\n                size: 10\n            },\n            callback (label) {\n                return label;\n            },\n            padding: 5,\n            centerPointLabels: false\n        }\n    };\n    static defaultRoutes = {\n        'angleLines.color': 'borderColor',\n        'pointLabels.color': 'color',\n        'ticks.color': 'color'\n    };\n    static descriptors = {\n        angleLines: {\n            _fallback: 'grid'\n        }\n    };\n    constructor(cfg){\n        super(cfg);\n         this.xCenter = undefined;\n         this.yCenter = undefined;\n         this.drawingArea = undefined;\n         this._pointLabels = [];\n        this._pointLabelItems = [];\n    }\n    setDimensions() {\n        const padding = this._padding = helpers_segment.toPadding(getTickBackdropHeight(this.options) / 2);\n        const w = this.width = this.maxWidth - padding.width;\n        const h = this.height = this.maxHeight - padding.height;\n        this.xCenter = Math.floor(this.left + w / 2 + padding.left);\n        this.yCenter = Math.floor(this.top + h / 2 + padding.top);\n        this.drawingArea = Math.floor(Math.min(w, h) / 2);\n    }\n    determineDataLimits() {\n        const { min , max  } = this.getMinMax(false);\n        this.min = helpers_segment.isNumberFinite(min) && !isNaN(min) ? min : 0;\n        this.max = helpers_segment.isNumberFinite(max) && !isNaN(max) ? max : 0;\n        this.handleTickRangeOptions();\n    }\n computeTickLimit() {\n        return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));\n    }\n    generateTickLabels(ticks) {\n        LinearScaleBase.prototype.generateTickLabels.call(this, ticks);\n        this._pointLabels = this.getLabels().map((value, index)=>{\n            const label = helpers_segment.callback(this.options.pointLabels.callback, [\n                value,\n                index\n            ], this);\n            return label || label === 0 ? label : '';\n        }).filter((v, i)=>this.chart.getDataVisibility(i));\n    }\n    fit() {\n        const opts = this.options;\n        if (opts.display && opts.pointLabels.display) {\n            fitWithPointLabels(this);\n        } else {\n            this.setCenterPoint(0, 0, 0, 0);\n        }\n    }\n    setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {\n        this.xCenter += Math.floor((leftMovement - rightMovement) / 2);\n        this.yCenter += Math.floor((topMovement - bottomMovement) / 2);\n        this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement));\n    }\n    getIndexAngle(index) {\n        const angleMultiplier = helpers_segment.TAU / (this._pointLabels.length || 1);\n        const startAngle = this.options.startAngle || 0;\n        return helpers_segment._normalizeAngle(index * angleMultiplier + helpers_segment.toRadians(startAngle));\n    }\n    getDistanceFromCenterForValue(value) {\n        if (helpers_segment.isNullOrUndef(value)) {\n            return NaN;\n        }\n        const scalingFactor = this.drawingArea / (this.max - this.min);\n        if (this.options.reverse) {\n            return (this.max - value) * scalingFactor;\n        }\n        return (value - this.min) * scalingFactor;\n    }\n    getValueForDistanceFromCenter(distance) {\n        if (helpers_segment.isNullOrUndef(distance)) {\n            return NaN;\n        }\n        const scaledDistance = distance / (this.drawingArea / (this.max - this.min));\n        return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance;\n    }\n    getPointLabelContext(index) {\n        const pointLabels = this._pointLabels || [];\n        if (index >= 0 && index < pointLabels.length) {\n            const pointLabel = pointLabels[index];\n            return createPointLabelContext(this.getContext(), index, pointLabel);\n        }\n    }\n    getPointPosition(index, distanceFromCenter, additionalAngle = 0) {\n        const angle = this.getIndexAngle(index) - helpers_segment.HALF_PI + additionalAngle;\n        return {\n            x: Math.cos(angle) * distanceFromCenter + this.xCenter,\n            y: Math.sin(angle) * distanceFromCenter + this.yCenter,\n            angle\n        };\n    }\n    getPointPositionForValue(index, value) {\n        return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));\n    }\n    getBasePosition(index) {\n        return this.getPointPositionForValue(index || 0, this.getBaseValue());\n    }\n    getPointLabelPosition(index) {\n        const { left , top , right , bottom  } = this._pointLabelItems[index];\n        return {\n            left,\n            top,\n            right,\n            bottom\n        };\n    }\n drawBackground() {\n        const { backgroundColor , grid: { circular  }  } = this.options;\n        if (backgroundColor) {\n            const ctx = this.ctx;\n            ctx.save();\n            ctx.beginPath();\n            pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length);\n            ctx.closePath();\n            ctx.fillStyle = backgroundColor;\n            ctx.fill();\n            ctx.restore();\n        }\n    }\n drawGrid() {\n        const ctx = this.ctx;\n        const opts = this.options;\n        const { angleLines , grid , border  } = opts;\n        const labelCount = this._pointLabels.length;\n        let i, offset, position;\n        if (opts.pointLabels.display) {\n            drawPointLabels(this, labelCount);\n        }\n        if (grid.display) {\n            this.ticks.forEach((tick, index)=>{\n                if (index !== 0) {\n                    offset = this.getDistanceFromCenterForValue(tick.value);\n                    const context = this.getContext(index);\n                    const optsAtIndex = grid.setContext(context);\n                    const optsAtIndexBorder = border.setContext(context);\n                    drawRadiusLine(this, optsAtIndex, offset, labelCount, optsAtIndexBorder);\n                }\n            });\n        }\n        if (angleLines.display) {\n            ctx.save();\n            for(i = labelCount - 1; i >= 0; i--){\n                const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i));\n                const { color , lineWidth  } = optsAtIndex;\n                if (!lineWidth || !color) {\n                    continue;\n                }\n                ctx.lineWidth = lineWidth;\n                ctx.strokeStyle = color;\n                ctx.setLineDash(optsAtIndex.borderDash);\n                ctx.lineDashOffset = optsAtIndex.borderDashOffset;\n                offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max);\n                position = this.getPointPosition(i, offset);\n                ctx.beginPath();\n                ctx.moveTo(this.xCenter, this.yCenter);\n                ctx.lineTo(position.x, position.y);\n                ctx.stroke();\n            }\n            ctx.restore();\n        }\n    }\n drawBorder() {}\n drawLabels() {\n        const ctx = this.ctx;\n        const opts = this.options;\n        const tickOpts = opts.ticks;\n        if (!tickOpts.display) {\n            return;\n        }\n        const startAngle = this.getIndexAngle(0);\n        let offset, width;\n        ctx.save();\n        ctx.translate(this.xCenter, this.yCenter);\n        ctx.rotate(startAngle);\n        ctx.textAlign = 'center';\n        ctx.textBaseline = 'middle';\n        this.ticks.forEach((tick, index)=>{\n            if (index === 0 && !opts.reverse) {\n                return;\n            }\n            const optsAtIndex = tickOpts.setContext(this.getContext(index));\n            const tickFont = helpers_segment.toFont(optsAtIndex.font);\n            offset = this.getDistanceFromCenterForValue(this.ticks[index].value);\n            if (optsAtIndex.showLabelBackdrop) {\n                ctx.font = tickFont.string;\n                width = ctx.measureText(tick.label).width;\n                ctx.fillStyle = optsAtIndex.backdropColor;\n                const padding = helpers_segment.toPadding(optsAtIndex.backdropPadding);\n                ctx.fillRect(-width / 2 - padding.left, -offset - tickFont.size / 2 - padding.top, width + padding.width, tickFont.size + padding.height);\n            }\n            helpers_segment.renderText(ctx, tick.label, 0, -offset, tickFont, {\n                color: optsAtIndex.color\n            });\n        });\n        ctx.restore();\n    }\n drawTitle() {}\n}\n\nconst INTERVALS = {\n    millisecond: {\n        common: true,\n        size: 1,\n        steps: 1000\n    },\n    second: {\n        common: true,\n        size: 1000,\n        steps: 60\n    },\n    minute: {\n        common: true,\n        size: 60000,\n        steps: 60\n    },\n    hour: {\n        common: true,\n        size: 3600000,\n        steps: 24\n    },\n    day: {\n        common: true,\n        size: 86400000,\n        steps: 30\n    },\n    week: {\n        common: false,\n        size: 604800000,\n        steps: 4\n    },\n    month: {\n        common: true,\n        size: 2.628e9,\n        steps: 12\n    },\n    quarter: {\n        common: false,\n        size: 7.884e9,\n        steps: 4\n    },\n    year: {\n        common: true,\n        size: 3.154e10\n    }\n};\n const UNITS =  /* #__PURE__ */ Object.keys(INTERVALS);\n function sorter(a, b) {\n    return a - b;\n}\n function parse(scale, input) {\n    if (helpers_segment.isNullOrUndef(input)) {\n        return null;\n    }\n    const adapter = scale._adapter;\n    const { parser , round , isoWeekday  } = scale._parseOpts;\n    let value = input;\n    if (typeof parser === 'function') {\n        value = parser(value);\n    }\n    if (!helpers_segment.isNumberFinite(value)) {\n        value = typeof parser === 'string' ? adapter.parse(value,  parser) : adapter.parse(value);\n    }\n    if (value === null) {\n        return null;\n    }\n    if (round) {\n        value = round === 'week' && (helpers_segment.isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, 'isoWeek', isoWeekday) : adapter.startOf(value, round);\n    }\n    return +value;\n}\n function determineUnitForAutoTicks(minUnit, min, max, capacity) {\n    const ilen = UNITS.length;\n    for(let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i){\n        const interval = INTERVALS[UNITS[i]];\n        const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER;\n        if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {\n            return UNITS[i];\n        }\n    }\n    return UNITS[ilen - 1];\n}\n function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {\n    for(let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--){\n        const unit = UNITS[i];\n        if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {\n            return unit;\n        }\n    }\n    return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];\n}\n function determineMajorUnit(unit) {\n    for(let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i){\n        if (INTERVALS[UNITS[i]].common) {\n            return UNITS[i];\n        }\n    }\n}\n function addTick(ticks, time, timestamps) {\n    if (!timestamps) {\n        ticks[time] = true;\n    } else if (timestamps.length) {\n        const { lo , hi  } = helpers_segment._lookup(timestamps, time);\n        const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];\n        ticks[timestamp] = true;\n    }\n}\n function setMajorTicks(scale, ticks, map, majorUnit) {\n    const adapter = scale._adapter;\n    const first = +adapter.startOf(ticks[0].value, majorUnit);\n    const last = ticks[ticks.length - 1].value;\n    let major, index;\n    for(major = first; major <= last; major = +adapter.add(major, 1, majorUnit)){\n        index = map[major];\n        if (index >= 0) {\n            ticks[index].major = true;\n        }\n    }\n    return ticks;\n}\n function ticksFromTimestamps(scale, values, majorUnit) {\n    const ticks = [];\n     const map = {};\n    const ilen = values.length;\n    let i, value;\n    for(i = 0; i < ilen; ++i){\n        value = values[i];\n        map[value] = i;\n        ticks.push({\n            value,\n            major: false\n        });\n    }\n    return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map, majorUnit);\n}\nclass TimeScale extends Scale {\n    static id = 'time';\n static defaults = {\n bounds: 'data',\n        adapters: {},\n        time: {\n            parser: false,\n            unit: false,\n            round: false,\n            isoWeekday: false,\n            minUnit: 'millisecond',\n            displayFormats: {}\n        },\n        ticks: {\n source: 'auto',\n            callback: false,\n            major: {\n                enabled: false\n            }\n        }\n    };\n constructor(props){\n        super(props);\n         this._cache = {\n            data: [],\n            labels: [],\n            all: []\n        };\n         this._unit = 'day';\n         this._majorUnit = undefined;\n        this._offsets = {};\n        this._normalized = false;\n        this._parseOpts = undefined;\n    }\n    init(scaleOpts, opts = {}) {\n        const time = scaleOpts.time || (scaleOpts.time = {});\n         const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date);\n        adapter.init(opts);\n        helpers_segment.mergeIf(time.displayFormats, adapter.formats());\n        this._parseOpts = {\n            parser: time.parser,\n            round: time.round,\n            isoWeekday: time.isoWeekday\n        };\n        super.init(scaleOpts);\n        this._normalized = opts.normalized;\n    }\n parse(raw, index) {\n        if (raw === undefined) {\n            return null;\n        }\n        return parse(this, raw);\n    }\n    beforeLayout() {\n        super.beforeLayout();\n        this._cache = {\n            data: [],\n            labels: [],\n            all: []\n        };\n    }\n    determineDataLimits() {\n        const options = this.options;\n        const adapter = this._adapter;\n        const unit = options.time.unit || 'day';\n        let { min , max , minDefined , maxDefined  } = this.getUserBounds();\n function _applyBounds(bounds) {\n            if (!minDefined && !isNaN(bounds.min)) {\n                min = Math.min(min, bounds.min);\n            }\n            if (!maxDefined && !isNaN(bounds.max)) {\n                max = Math.max(max, bounds.max);\n            }\n        }\n        if (!minDefined || !maxDefined) {\n            _applyBounds(this._getLabelBounds());\n            if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {\n                _applyBounds(this.getMinMax(false));\n            }\n        }\n        min = helpers_segment.isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);\n        max = helpers_segment.isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;\n        this.min = Math.min(min, max - 1);\n        this.max = Math.max(min + 1, max);\n    }\n _getLabelBounds() {\n        const arr = this.getLabelTimestamps();\n        let min = Number.POSITIVE_INFINITY;\n        let max = Number.NEGATIVE_INFINITY;\n        if (arr.length) {\n            min = arr[0];\n            max = arr[arr.length - 1];\n        }\n        return {\n            min,\n            max\n        };\n    }\n buildTicks() {\n        const options = this.options;\n        const timeOpts = options.time;\n        const tickOpts = options.ticks;\n        const timestamps = tickOpts.source === 'labels' ? this.getLabelTimestamps() : this._generate();\n        if (options.bounds === 'ticks' && timestamps.length) {\n            this.min = this._userMin || timestamps[0];\n            this.max = this._userMax || timestamps[timestamps.length - 1];\n        }\n        const min = this.min;\n        const max = this.max;\n        const ticks = helpers_segment._filterBetween(timestamps, min, max);\n        this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max));\n        this._majorUnit = !tickOpts.major.enabled || this._unit === 'year' ? undefined : determineMajorUnit(this._unit);\n        this.initOffsets(timestamps);\n        if (options.reverse) {\n            ticks.reverse();\n        }\n        return ticksFromTimestamps(this, ticks, this._majorUnit);\n    }\n    afterAutoSkip() {\n        if (this.options.offsetAfterAutoskip) {\n            this.initOffsets(this.ticks.map((tick)=>+tick.value));\n        }\n    }\n initOffsets(timestamps = []) {\n        let start = 0;\n        let end = 0;\n        let first, last;\n        if (this.options.offset && timestamps.length) {\n            first = this.getDecimalForValue(timestamps[0]);\n            if (timestamps.length === 1) {\n                start = 1 - first;\n            } else {\n                start = (this.getDecimalForValue(timestamps[1]) - first) / 2;\n            }\n            last = this.getDecimalForValue(timestamps[timestamps.length - 1]);\n            if (timestamps.length === 1) {\n                end = last;\n            } else {\n                end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;\n            }\n        }\n        const limit = timestamps.length < 3 ? 0.5 : 0.25;\n        start = helpers_segment._limitValue(start, 0, limit);\n        end = helpers_segment._limitValue(end, 0, limit);\n        this._offsets = {\n            start,\n            end,\n            factor: 1 / (start + 1 + end)\n        };\n    }\n _generate() {\n        const adapter = this._adapter;\n        const min = this.min;\n        const max = this.max;\n        const options = this.options;\n        const timeOpts = options.time;\n        const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));\n        const stepSize = helpers_segment.valueOrDefault(options.ticks.stepSize, 1);\n        const weekday = minor === 'week' ? timeOpts.isoWeekday : false;\n        const hasWeekday = helpers_segment.isNumber(weekday) || weekday === true;\n        const ticks = {};\n        let first = min;\n        let time, count;\n        if (hasWeekday) {\n            first = +adapter.startOf(first, 'isoWeek', weekday);\n        }\n        first = +adapter.startOf(first, hasWeekday ? 'day' : minor);\n        if (adapter.diff(max, min, minor) > 100000 * stepSize) {\n            throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);\n        }\n        const timestamps = options.ticks.source === 'data' && this.getDataTimestamps();\n        for(time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++){\n            addTick(ticks, time, timestamps);\n        }\n        if (time === max || options.bounds === 'ticks' || count === 1) {\n            addTick(ticks, time, timestamps);\n        }\n        return Object.keys(ticks).sort((a, b)=>a - b).map((x)=>+x);\n    }\n getLabelForValue(value) {\n        const adapter = this._adapter;\n        const timeOpts = this.options.time;\n        if (timeOpts.tooltipFormat) {\n            return adapter.format(value, timeOpts.tooltipFormat);\n        }\n        return adapter.format(value, timeOpts.displayFormats.datetime);\n    }\n format(value, format) {\n        const options = this.options;\n        const formats = options.time.displayFormats;\n        const unit = this._unit;\n        const fmt = format || formats[unit];\n        return this._adapter.format(value, fmt);\n    }\n _tickFormatFunction(time, index, ticks, format) {\n        const options = this.options;\n        const formatter = options.ticks.callback;\n        if (formatter) {\n            return helpers_segment.callback(formatter, [\n                time,\n                index,\n                ticks\n            ], this);\n        }\n        const formats = options.time.displayFormats;\n        const unit = this._unit;\n        const majorUnit = this._majorUnit;\n        const minorFormat = unit && formats[unit];\n        const majorFormat = majorUnit && formats[majorUnit];\n        const tick = ticks[index];\n        const major = majorUnit && majorFormat && tick && tick.major;\n        return this._adapter.format(time, format || (major ? majorFormat : minorFormat));\n    }\n generateTickLabels(ticks) {\n        let i, ilen, tick;\n        for(i = 0, ilen = ticks.length; i < ilen; ++i){\n            tick = ticks[i];\n            tick.label = this._tickFormatFunction(tick.value, i, ticks);\n        }\n    }\n getDecimalForValue(value) {\n        return value === null ? NaN : (value - this.min) / (this.max - this.min);\n    }\n getPixelForValue(value) {\n        const offsets = this._offsets;\n        const pos = this.getDecimalForValue(value);\n        return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);\n    }\n getValueForPixel(pixel) {\n        const offsets = this._offsets;\n        const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;\n        return this.min + pos * (this.max - this.min);\n    }\n _getLabelSize(label) {\n        const ticksOpts = this.options.ticks;\n        const tickLabelWidth = this.ctx.measureText(label).width;\n        const angle = helpers_segment.toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);\n        const cosRotation = Math.cos(angle);\n        const sinRotation = Math.sin(angle);\n        const tickFontSize = this._resolveTickFontOptions(0).size;\n        return {\n            w: tickLabelWidth * cosRotation + tickFontSize * sinRotation,\n            h: tickLabelWidth * sinRotation + tickFontSize * cosRotation\n        };\n    }\n _getLabelCapacity(exampleTime) {\n        const timeOpts = this.options.time;\n        const displayFormats = timeOpts.displayFormats;\n        const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;\n        const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [\n            exampleTime\n        ], this._majorUnit), format);\n        const size = this._getLabelSize(exampleLabel);\n        const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;\n        return capacity > 0 ? capacity : 1;\n    }\n getDataTimestamps() {\n        let timestamps = this._cache.data || [];\n        let i, ilen;\n        if (timestamps.length) {\n            return timestamps;\n        }\n        const metas = this.getMatchingVisibleMetas();\n        if (this._normalized && metas.length) {\n            return this._cache.data = metas[0].controller.getAllParsedValues(this);\n        }\n        for(i = 0, ilen = metas.length; i < ilen; ++i){\n            timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));\n        }\n        return this._cache.data = this.normalize(timestamps);\n    }\n getLabelTimestamps() {\n        const timestamps = this._cache.labels || [];\n        let i, ilen;\n        if (timestamps.length) {\n            return timestamps;\n        }\n        const labels = this.getLabels();\n        for(i = 0, ilen = labels.length; i < ilen; ++i){\n            timestamps.push(parse(this, labels[i]));\n        }\n        return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps);\n    }\n normalize(values) {\n        return helpers_segment._arrayUnique(values.sort(sorter));\n    }\n}\n\nfunction interpolate(table, val, reverse) {\n    let lo = 0;\n    let hi = table.length - 1;\n    let prevSource, nextSource, prevTarget, nextTarget;\n    if (reverse) {\n        if (val >= table[lo].pos && val <= table[hi].pos) {\n            ({ lo , hi  } = helpers_segment._lookupByKey(table, 'pos', val));\n        }\n        ({ pos: prevSource , time: prevTarget  } = table[lo]);\n        ({ pos: nextSource , time: nextTarget  } = table[hi]);\n    } else {\n        if (val >= table[lo].time && val <= table[hi].time) {\n            ({ lo , hi  } = helpers_segment._lookupByKey(table, 'time', val));\n        }\n        ({ time: prevSource , pos: prevTarget  } = table[lo]);\n        ({ time: nextSource , pos: nextTarget  } = table[hi]);\n    }\n    const span = nextSource - prevSource;\n    return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget;\n}\nclass TimeSeriesScale extends TimeScale {\n    static id = 'timeseries';\n static defaults = TimeScale.defaults;\n constructor(props){\n        super(props);\n         this._table = [];\n         this._minPos = undefined;\n         this._tableRange = undefined;\n    }\n initOffsets() {\n        const timestamps = this._getTimestampsForTable();\n        const table = this._table = this.buildLookupTable(timestamps);\n        this._minPos = interpolate(table, this.min);\n        this._tableRange = interpolate(table, this.max) - this._minPos;\n        super.initOffsets(timestamps);\n    }\n buildLookupTable(timestamps) {\n        const { min , max  } = this;\n        const items = [];\n        const table = [];\n        let i, ilen, prev, curr, next;\n        for(i = 0, ilen = timestamps.length; i < ilen; ++i){\n            curr = timestamps[i];\n            if (curr >= min && curr <= max) {\n                items.push(curr);\n            }\n        }\n        if (items.length < 2) {\n            return [\n                {\n                    time: min,\n                    pos: 0\n                },\n                {\n                    time: max,\n                    pos: 1\n                }\n            ];\n        }\n        for(i = 0, ilen = items.length; i < ilen; ++i){\n            next = items[i + 1];\n            prev = items[i - 1];\n            curr = items[i];\n            if (Math.round((next + prev) / 2) !== curr) {\n                table.push({\n                    time: curr,\n                    pos: i / (ilen - 1)\n                });\n            }\n        }\n        return table;\n    }\n _getTimestampsForTable() {\n        let timestamps = this._cache.all || [];\n        if (timestamps.length) {\n            return timestamps;\n        }\n        const data = this.getDataTimestamps();\n        const label = this.getLabelTimestamps();\n        if (data.length && label.length) {\n            timestamps = this.normalize(data.concat(label));\n        } else {\n            timestamps = data.length ? data : label;\n        }\n        timestamps = this._cache.all = timestamps;\n        return timestamps;\n    }\n getDecimalForValue(value) {\n        return (interpolate(this._table, value) - this._minPos) / this._tableRange;\n    }\n getValueForPixel(pixel) {\n        const offsets = this._offsets;\n        const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;\n        return interpolate(this._table, decimal * this._tableRange + this._minPos, true);\n    }\n}\n\nvar scales = /*#__PURE__*/Object.freeze({\n__proto__: null,\nCategoryScale: CategoryScale,\nLinearScale: LinearScale,\nLogarithmicScale: LogarithmicScale,\nRadialLinearScale: RadialLinearScale,\nTimeScale: TimeScale,\nTimeSeriesScale: TimeSeriesScale\n});\n\nconst registerables = [\n    controllers,\n    elements,\n    plugins,\n    scales\n];\n\nexports.Ticks = helpers_segment.Ticks;\nexports.defaults = helpers_segment.defaults;\nexports.Animation = Animation;\nexports.Animations = Animations;\nexports.ArcElement = ArcElement;\nexports.BarController = BarController;\nexports.BarElement = BarElement;\nexports.BasePlatform = BasePlatform;\nexports.BasicPlatform = BasicPlatform;\nexports.BubbleController = BubbleController;\nexports.CategoryScale = CategoryScale;\nexports.Chart = Chart;\nexports.Colors = plugin_colors;\nexports.DatasetController = DatasetController;\nexports.Decimation = plugin_decimation;\nexports.DomPlatform = DomPlatform;\nexports.DoughnutController = DoughnutController;\nexports.Element = Element;\nexports.Filler = index;\nexports.Interaction = Interaction;\nexports.Legend = plugin_legend;\nexports.LineController = LineController;\nexports.LineElement = LineElement;\nexports.LinearScale = LinearScale;\nexports.LogarithmicScale = LogarithmicScale;\nexports.PieController = PieController;\nexports.PointElement = PointElement;\nexports.PolarAreaController = PolarAreaController;\nexports.RadarController = RadarController;\nexports.RadialLinearScale = RadialLinearScale;\nexports.Scale = Scale;\nexports.ScatterController = ScatterController;\nexports.SubTitle = plugin_subtitle;\nexports.TimeScale = TimeScale;\nexports.TimeSeriesScale = TimeSeriesScale;\nexports.Title = plugin_title;\nexports.Tooltip = plugin_tooltip;\nexports._adapters = adapters;\nexports._detectPlatform = _detectPlatform;\nexports.animator = animator;\nexports.controllers = controllers;\nexports.elements = elements;\nexports.layouts = layouts;\nexports.plugins = plugins;\nexports.registerables = registerables;\nexports.registry = registry;\nexports.scales = scales;\n//# sourceMappingURL=chart.cjs.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/chart.js",
    "content": "/*!\n * Chart.js v4.2.1\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\nimport { r as requestAnimFrame, a as resolve, e as effects, c as color, i as isObject, d as defaults, b as isArray, v as valueOrDefault, u as unlistenArrayEvents, l as listenArrayEvents, f as resolveObjectKey, g as isNumberFinite, h as defined, s as sign, j as createContext, k as isNullOrUndef, _ as _arrayUnique, t as toRadians, m as toPercentage, n as toDimension, T as TAU, o as formatNumber, p as _angleBetween, H as HALF_PI, P as PI, q as _getStartAndCountOfVisiblePoints, w as _scaleRangesChanged, x as isNumber, y as _parseObjectDataRadialScale, z as getRelativePosition, A as _rlookupByKey, B as _lookupByKey, C as _isPointInArea, D as getAngleFromPoint, E as toPadding, F as each, G as getMaximumSize, I as _getParentNode, J as readUsedSize, K as supportsEventListenerOptions, L as throttled, M as _isDomSupported, N as _factorize, O as finiteOrDefault, Q as callback, R as _addGrace, S as _limitValue, U as toDegrees, V as _measureText, W as _int16Range, X as _alignPixel, Y as clipArea, Z as renderText, $ as unclipArea, a0 as toFont, a1 as _toLeftRightCenter, a2 as _alignStartEnd, a3 as overrides, a4 as merge, a5 as _capitalize, a6 as descriptors, a7 as isFunction, a8 as _attachContext, a9 as _createResolver, aa as _descriptors, ab as mergeIf, ac as uid, ad as debounce, ae as retinaScale, af as clearCanvas, ag as setsEqual, ah as _elementsEqual, ai as _isClickEvent, aj as _isBetween, ak as _readValueToProps, al as _updateBezierControlPoints, am as _computeSegments, an as _boundSegments, ao as _steppedInterpolation, ap as _bezierInterpolation, aq as _pointInLine, ar as _steppedLineTo, as as _bezierCurveTo, at as drawPoint, au as addRoundedRectPath, av as toTRBL, aw as toTRBLCorners, ax as _boundSegment, ay as _normalizeAngle, az as getRtlAdapter, aA as overrideTextDirection, aB as _textX, aC as restoreTextDirection, aD as drawPointLegend, aE as distanceBetweenPoints, aF as noop, aG as _setMinAndMaxByKey, aH as niceNum, aI as almostWhole, aJ as almostEquals, aK as _decimalPlaces, aL as Ticks, aM as log10, aN as _longestText, aO as _filterBetween, aP as _lookup } from './chunks/helpers.segment.js';\nexport { aL as Ticks, d as defaults } from './chunks/helpers.segment.js';\nimport '@kurkle/color';\n\nclass Animator {\n    constructor(){\n        this._request = null;\n        this._charts = new Map();\n        this._running = false;\n        this._lastDate = undefined;\n    }\n _notify(chart, anims, date, type) {\n        const callbacks = anims.listeners[type];\n        const numSteps = anims.duration;\n        callbacks.forEach((fn)=>fn({\n                chart,\n                initial: anims.initial,\n                numSteps,\n                currentStep: Math.min(date - anims.start, numSteps)\n            }));\n    }\n _refresh() {\n        if (this._request) {\n            return;\n        }\n        this._running = true;\n        this._request = requestAnimFrame.call(window, ()=>{\n            this._update();\n            this._request = null;\n            if (this._running) {\n                this._refresh();\n            }\n        });\n    }\n _update(date = Date.now()) {\n        let remaining = 0;\n        this._charts.forEach((anims, chart)=>{\n            if (!anims.running || !anims.items.length) {\n                return;\n            }\n            const items = anims.items;\n            let i = items.length - 1;\n            let draw = false;\n            let item;\n            for(; i >= 0; --i){\n                item = items[i];\n                if (item._active) {\n                    if (item._total > anims.duration) {\n                        anims.duration = item._total;\n                    }\n                    item.tick(date);\n                    draw = true;\n                } else {\n                    items[i] = items[items.length - 1];\n                    items.pop();\n                }\n            }\n            if (draw) {\n                chart.draw();\n                this._notify(chart, anims, date, 'progress');\n            }\n            if (!items.length) {\n                anims.running = false;\n                this._notify(chart, anims, date, 'complete');\n                anims.initial = false;\n            }\n            remaining += items.length;\n        });\n        this._lastDate = date;\n        if (remaining === 0) {\n            this._running = false;\n        }\n    }\n _getAnims(chart) {\n        const charts = this._charts;\n        let anims = charts.get(chart);\n        if (!anims) {\n            anims = {\n                running: false,\n                initial: true,\n                items: [],\n                listeners: {\n                    complete: [],\n                    progress: []\n                }\n            };\n            charts.set(chart, anims);\n        }\n        return anims;\n    }\n listen(chart, event, cb) {\n        this._getAnims(chart).listeners[event].push(cb);\n    }\n add(chart, items) {\n        if (!items || !items.length) {\n            return;\n        }\n        this._getAnims(chart).items.push(...items);\n    }\n has(chart) {\n        return this._getAnims(chart).items.length > 0;\n    }\n start(chart) {\n        const anims = this._charts.get(chart);\n        if (!anims) {\n            return;\n        }\n        anims.running = true;\n        anims.start = Date.now();\n        anims.duration = anims.items.reduce((acc, cur)=>Math.max(acc, cur._duration), 0);\n        this._refresh();\n    }\n    running(chart) {\n        if (!this._running) {\n            return false;\n        }\n        const anims = this._charts.get(chart);\n        if (!anims || !anims.running || !anims.items.length) {\n            return false;\n        }\n        return true;\n    }\n stop(chart) {\n        const anims = this._charts.get(chart);\n        if (!anims || !anims.items.length) {\n            return;\n        }\n        const items = anims.items;\n        let i = items.length - 1;\n        for(; i >= 0; --i){\n            items[i].cancel();\n        }\n        anims.items = [];\n        this._notify(chart, anims, Date.now(), 'complete');\n    }\n remove(chart) {\n        return this._charts.delete(chart);\n    }\n}\nvar animator = /* #__PURE__ */ new Animator();\n\nconst transparent = 'transparent';\nconst interpolators = {\n    boolean (from, to, factor) {\n        return factor > 0.5 ? to : from;\n    },\n color (from, to, factor) {\n        const c0 = color(from || transparent);\n        const c1 = c0.valid && color(to || transparent);\n        return c1 && c1.valid ? c1.mix(c0, factor).hexString() : to;\n    },\n    number (from, to, factor) {\n        return from + (to - from) * factor;\n    }\n};\nclass Animation {\n    constructor(cfg, target, prop, to){\n        const currentValue = target[prop];\n        to = resolve([\n            cfg.to,\n            to,\n            currentValue,\n            cfg.from\n        ]);\n        const from = resolve([\n            cfg.from,\n            currentValue,\n            to\n        ]);\n        this._active = true;\n        this._fn = cfg.fn || interpolators[cfg.type || typeof from];\n        this._easing = effects[cfg.easing] || effects.linear;\n        this._start = Math.floor(Date.now() + (cfg.delay || 0));\n        this._duration = this._total = Math.floor(cfg.duration);\n        this._loop = !!cfg.loop;\n        this._target = target;\n        this._prop = prop;\n        this._from = from;\n        this._to = to;\n        this._promises = undefined;\n    }\n    active() {\n        return this._active;\n    }\n    update(cfg, to, date) {\n        if (this._active) {\n            this._notify(false);\n            const currentValue = this._target[this._prop];\n            const elapsed = date - this._start;\n            const remain = this._duration - elapsed;\n            this._start = date;\n            this._duration = Math.floor(Math.max(remain, cfg.duration));\n            this._total += elapsed;\n            this._loop = !!cfg.loop;\n            this._to = resolve([\n                cfg.to,\n                to,\n                currentValue,\n                cfg.from\n            ]);\n            this._from = resolve([\n                cfg.from,\n                currentValue,\n                to\n            ]);\n        }\n    }\n    cancel() {\n        if (this._active) {\n            this.tick(Date.now());\n            this._active = false;\n            this._notify(false);\n        }\n    }\n    tick(date) {\n        const elapsed = date - this._start;\n        const duration = this._duration;\n        const prop = this._prop;\n        const from = this._from;\n        const loop = this._loop;\n        const to = this._to;\n        let factor;\n        this._active = from !== to && (loop || elapsed < duration);\n        if (!this._active) {\n            this._target[prop] = to;\n            this._notify(true);\n            return;\n        }\n        if (elapsed < 0) {\n            this._target[prop] = from;\n            return;\n        }\n        factor = elapsed / duration % 2;\n        factor = loop && factor > 1 ? 2 - factor : factor;\n        factor = this._easing(Math.min(1, Math.max(0, factor)));\n        this._target[prop] = this._fn(from, to, factor);\n    }\n    wait() {\n        const promises = this._promises || (this._promises = []);\n        return new Promise((res, rej)=>{\n            promises.push({\n                res,\n                rej\n            });\n        });\n    }\n    _notify(resolved) {\n        const method = resolved ? 'res' : 'rej';\n        const promises = this._promises || [];\n        for(let i = 0; i < promises.length; i++){\n            promises[i][method]();\n        }\n    }\n}\n\nclass Animations {\n    constructor(chart, config){\n        this._chart = chart;\n        this._properties = new Map();\n        this.configure(config);\n    }\n    configure(config) {\n        if (!isObject(config)) {\n            return;\n        }\n        const animationOptions = Object.keys(defaults.animation);\n        const animatedProps = this._properties;\n        Object.getOwnPropertyNames(config).forEach((key)=>{\n            const cfg = config[key];\n            if (!isObject(cfg)) {\n                return;\n            }\n            const resolved = {};\n            for (const option of animationOptions){\n                resolved[option] = cfg[option];\n            }\n            (isArray(cfg.properties) && cfg.properties || [\n                key\n            ]).forEach((prop)=>{\n                if (prop === key || !animatedProps.has(prop)) {\n                    animatedProps.set(prop, resolved);\n                }\n            });\n        });\n    }\n _animateOptions(target, values) {\n        const newOptions = values.options;\n        const options = resolveTargetOptions(target, newOptions);\n        if (!options) {\n            return [];\n        }\n        const animations = this._createAnimations(options, newOptions);\n        if (newOptions.$shared) {\n            awaitAll(target.options.$animations, newOptions).then(()=>{\n                target.options = newOptions;\n            }, ()=>{\n            });\n        }\n        return animations;\n    }\n _createAnimations(target, values) {\n        const animatedProps = this._properties;\n        const animations = [];\n        const running = target.$animations || (target.$animations = {});\n        const props = Object.keys(values);\n        const date = Date.now();\n        let i;\n        for(i = props.length - 1; i >= 0; --i){\n            const prop = props[i];\n            if (prop.charAt(0) === '$') {\n                continue;\n            }\n            if (prop === 'options') {\n                animations.push(...this._animateOptions(target, values));\n                continue;\n            }\n            const value = values[prop];\n            let animation = running[prop];\n            const cfg = animatedProps.get(prop);\n            if (animation) {\n                if (cfg && animation.active()) {\n                    animation.update(cfg, value, date);\n                    continue;\n                } else {\n                    animation.cancel();\n                }\n            }\n            if (!cfg || !cfg.duration) {\n                target[prop] = value;\n                continue;\n            }\n            running[prop] = animation = new Animation(cfg, target, prop, value);\n            animations.push(animation);\n        }\n        return animations;\n    }\n update(target, values) {\n        if (this._properties.size === 0) {\n            Object.assign(target, values);\n            return;\n        }\n        const animations = this._createAnimations(target, values);\n        if (animations.length) {\n            animator.add(this._chart, animations);\n            return true;\n        }\n    }\n}\nfunction awaitAll(animations, properties) {\n    const running = [];\n    const keys = Object.keys(properties);\n    for(let i = 0; i < keys.length; i++){\n        const anim = animations[keys[i]];\n        if (anim && anim.active()) {\n            running.push(anim.wait());\n        }\n    }\n    return Promise.all(running);\n}\nfunction resolveTargetOptions(target, newOptions) {\n    if (!newOptions) {\n        return;\n    }\n    let options = target.options;\n    if (!options) {\n        target.options = newOptions;\n        return;\n    }\n    if (options.$shared) {\n        target.options = options = Object.assign({}, options, {\n            $shared: false,\n            $animations: {}\n        });\n    }\n    return options;\n}\n\nfunction scaleClip(scale, allowedOverflow) {\n    const opts = scale && scale.options || {};\n    const reverse = opts.reverse;\n    const min = opts.min === undefined ? allowedOverflow : 0;\n    const max = opts.max === undefined ? allowedOverflow : 0;\n    return {\n        start: reverse ? max : min,\n        end: reverse ? min : max\n    };\n}\nfunction defaultClip(xScale, yScale, allowedOverflow) {\n    if (allowedOverflow === false) {\n        return false;\n    }\n    const x = scaleClip(xScale, allowedOverflow);\n    const y = scaleClip(yScale, allowedOverflow);\n    return {\n        top: y.end,\n        right: x.end,\n        bottom: y.start,\n        left: x.start\n    };\n}\nfunction toClip(value) {\n    let t, r, b, l;\n    if (isObject(value)) {\n        t = value.top;\n        r = value.right;\n        b = value.bottom;\n        l = value.left;\n    } else {\n        t = r = b = l = value;\n    }\n    return {\n        top: t,\n        right: r,\n        bottom: b,\n        left: l,\n        disabled: value === false\n    };\n}\nfunction getSortedDatasetIndices(chart, filterVisible) {\n    const keys = [];\n    const metasets = chart._getSortedDatasetMetas(filterVisible);\n    let i, ilen;\n    for(i = 0, ilen = metasets.length; i < ilen; ++i){\n        keys.push(metasets[i].index);\n    }\n    return keys;\n}\nfunction applyStack(stack, value, dsIndex, options = {}) {\n    const keys = stack.keys;\n    const singleMode = options.mode === 'single';\n    let i, ilen, datasetIndex, otherValue;\n    if (value === null) {\n        return;\n    }\n    for(i = 0, ilen = keys.length; i < ilen; ++i){\n        datasetIndex = +keys[i];\n        if (datasetIndex === dsIndex) {\n            if (options.all) {\n                continue;\n            }\n            break;\n        }\n        otherValue = stack.values[datasetIndex];\n        if (isNumberFinite(otherValue) && (singleMode || value === 0 || sign(value) === sign(otherValue))) {\n            value += otherValue;\n        }\n    }\n    return value;\n}\nfunction convertObjectDataToArray(data) {\n    const keys = Object.keys(data);\n    const adata = new Array(keys.length);\n    let i, ilen, key;\n    for(i = 0, ilen = keys.length; i < ilen; ++i){\n        key = keys[i];\n        adata[i] = {\n            x: key,\n            y: data[key]\n        };\n    }\n    return adata;\n}\nfunction isStacked(scale, meta) {\n    const stacked = scale && scale.options.stacked;\n    return stacked || stacked === undefined && meta.stack !== undefined;\n}\nfunction getStackKey(indexScale, valueScale, meta) {\n    return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;\n}\nfunction getUserBounds(scale) {\n    const { min , max , minDefined , maxDefined  } = scale.getUserBounds();\n    return {\n        min: minDefined ? min : Number.NEGATIVE_INFINITY,\n        max: maxDefined ? max : Number.POSITIVE_INFINITY\n    };\n}\nfunction getOrCreateStack(stacks, stackKey, indexValue) {\n    const subStack = stacks[stackKey] || (stacks[stackKey] = {});\n    return subStack[indexValue] || (subStack[indexValue] = {});\n}\nfunction getLastIndexInStack(stack, vScale, positive, type) {\n    for (const meta of vScale.getMatchingVisibleMetas(type).reverse()){\n        const value = stack[meta.index];\n        if (positive && value > 0 || !positive && value < 0) {\n            return meta.index;\n        }\n    }\n    return null;\n}\nfunction updateStacks(controller, parsed) {\n    const { chart , _cachedMeta: meta  } = controller;\n    const stacks = chart._stacks || (chart._stacks = {});\n    const { iScale , vScale , index: datasetIndex  } = meta;\n    const iAxis = iScale.axis;\n    const vAxis = vScale.axis;\n    const key = getStackKey(iScale, vScale, meta);\n    const ilen = parsed.length;\n    let stack;\n    for(let i = 0; i < ilen; ++i){\n        const item = parsed[i];\n        const { [iAxis]: index , [vAxis]: value  } = item;\n        const itemStacks = item._stacks || (item._stacks = {});\n        stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);\n        stack[datasetIndex] = value;\n        stack._top = getLastIndexInStack(stack, vScale, true, meta.type);\n        stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);\n        const visualValues = stack._visualValues || (stack._visualValues = {});\n        visualValues[datasetIndex] = value;\n    }\n}\nfunction getFirstScaleId(chart, axis) {\n    const scales = chart.scales;\n    return Object.keys(scales).filter((key)=>scales[key].axis === axis).shift();\n}\nfunction createDatasetContext(parent, index) {\n    return createContext(parent, {\n        active: false,\n        dataset: undefined,\n        datasetIndex: index,\n        index,\n        mode: 'default',\n        type: 'dataset'\n    });\n}\nfunction createDataContext(parent, index, element) {\n    return createContext(parent, {\n        active: false,\n        dataIndex: index,\n        parsed: undefined,\n        raw: undefined,\n        element,\n        index,\n        mode: 'default',\n        type: 'data'\n    });\n}\nfunction clearStacks(meta, items) {\n    const datasetIndex = meta.controller.index;\n    const axis = meta.vScale && meta.vScale.axis;\n    if (!axis) {\n        return;\n    }\n    items = items || meta._parsed;\n    for (const parsed of items){\n        const stacks = parsed._stacks;\n        if (!stacks || stacks[axis] === undefined || stacks[axis][datasetIndex] === undefined) {\n            return;\n        }\n        delete stacks[axis][datasetIndex];\n        if (stacks[axis]._visualValues !== undefined && stacks[axis]._visualValues[datasetIndex] !== undefined) {\n            delete stacks[axis]._visualValues[datasetIndex];\n        }\n    }\n}\nconst isDirectUpdateMode = (mode)=>mode === 'reset' || mode === 'none';\nconst cloneIfNotShared = (cached, shared)=>shared ? cached : Object.assign({}, cached);\nconst createStack = (canStack, meta, chart)=>canStack && !meta.hidden && meta._stacked && {\n        keys: getSortedDatasetIndices(chart, true),\n        values: null\n    };\nclass DatasetController {\n static defaults = {};\n static datasetElementType = null;\n static dataElementType = null;\n constructor(chart, datasetIndex){\n        this.chart = chart;\n        this._ctx = chart.ctx;\n        this.index = datasetIndex;\n        this._cachedDataOpts = {};\n        this._cachedMeta = this.getMeta();\n        this._type = this._cachedMeta.type;\n        this.options = undefined;\n         this._parsing = false;\n        this._data = undefined;\n        this._objectData = undefined;\n        this._sharedOptions = undefined;\n        this._drawStart = undefined;\n        this._drawCount = undefined;\n        this.enableOptionSharing = false;\n        this.supportsDecimation = false;\n        this.$context = undefined;\n        this._syncList = [];\n        this.datasetElementType = new.target.datasetElementType;\n        this.dataElementType = new.target.dataElementType;\n        this.initialize();\n    }\n    initialize() {\n        const meta = this._cachedMeta;\n        this.configure();\n        this.linkScales();\n        meta._stacked = isStacked(meta.vScale, meta);\n        this.addElements();\n        if (this.options.fill && !this.chart.isPluginEnabled('filler')) {\n            console.warn(\"Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options\");\n        }\n    }\n    updateIndex(datasetIndex) {\n        if (this.index !== datasetIndex) {\n            clearStacks(this._cachedMeta);\n        }\n        this.index = datasetIndex;\n    }\n    linkScales() {\n        const chart = this.chart;\n        const meta = this._cachedMeta;\n        const dataset = this.getDataset();\n        const chooseId = (axis, x, y, r)=>axis === 'x' ? x : axis === 'r' ? r : y;\n        const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x'));\n        const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y'));\n        const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, 'r'));\n        const indexAxis = meta.indexAxis;\n        const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);\n        const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);\n        meta.xScale = this.getScaleForId(xid);\n        meta.yScale = this.getScaleForId(yid);\n        meta.rScale = this.getScaleForId(rid);\n        meta.iScale = this.getScaleForId(iid);\n        meta.vScale = this.getScaleForId(vid);\n    }\n    getDataset() {\n        return this.chart.data.datasets[this.index];\n    }\n    getMeta() {\n        return this.chart.getDatasetMeta(this.index);\n    }\n getScaleForId(scaleID) {\n        return this.chart.scales[scaleID];\n    }\n _getOtherScale(scale) {\n        const meta = this._cachedMeta;\n        return scale === meta.iScale ? meta.vScale : meta.iScale;\n    }\n    reset() {\n        this._update('reset');\n    }\n _destroy() {\n        const meta = this._cachedMeta;\n        if (this._data) {\n            unlistenArrayEvents(this._data, this);\n        }\n        if (meta._stacked) {\n            clearStacks(meta);\n        }\n    }\n _dataCheck() {\n        const dataset = this.getDataset();\n        const data = dataset.data || (dataset.data = []);\n        const _data = this._data;\n        if (isObject(data)) {\n            this._data = convertObjectDataToArray(data);\n        } else if (_data !== data) {\n            if (_data) {\n                unlistenArrayEvents(_data, this);\n                const meta = this._cachedMeta;\n                clearStacks(meta);\n                meta._parsed = [];\n            }\n            if (data && Object.isExtensible(data)) {\n                listenArrayEvents(data, this);\n            }\n            this._syncList = [];\n            this._data = data;\n        }\n    }\n    addElements() {\n        const meta = this._cachedMeta;\n        this._dataCheck();\n        if (this.datasetElementType) {\n            meta.dataset = new this.datasetElementType();\n        }\n    }\n    buildOrUpdateElements(resetNewElements) {\n        const meta = this._cachedMeta;\n        const dataset = this.getDataset();\n        let stackChanged = false;\n        this._dataCheck();\n        const oldStacked = meta._stacked;\n        meta._stacked = isStacked(meta.vScale, meta);\n        if (meta.stack !== dataset.stack) {\n            stackChanged = true;\n            clearStacks(meta);\n            meta.stack = dataset.stack;\n        }\n        this._resyncElements(resetNewElements);\n        if (stackChanged || oldStacked !== meta._stacked) {\n            updateStacks(this, meta._parsed);\n        }\n    }\n configure() {\n        const config = this.chart.config;\n        const scopeKeys = config.datasetScopeKeys(this._type);\n        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);\n        this.options = config.createResolver(scopes, this.getContext());\n        this._parsing = this.options.parsing;\n        this._cachedDataOpts = {};\n    }\n parse(start, count) {\n        const { _cachedMeta: meta , _data: data  } = this;\n        const { iScale , _stacked  } = meta;\n        const iAxis = iScale.axis;\n        let sorted = start === 0 && count === data.length ? true : meta._sorted;\n        let prev = start > 0 && meta._parsed[start - 1];\n        let i, cur, parsed;\n        if (this._parsing === false) {\n            meta._parsed = data;\n            meta._sorted = true;\n            parsed = data;\n        } else {\n            if (isArray(data[start])) {\n                parsed = this.parseArrayData(meta, data, start, count);\n            } else if (isObject(data[start])) {\n                parsed = this.parseObjectData(meta, data, start, count);\n            } else {\n                parsed = this.parsePrimitiveData(meta, data, start, count);\n            }\n            const isNotInOrderComparedToPrev = ()=>cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis];\n            for(i = 0; i < count; ++i){\n                meta._parsed[i + start] = cur = parsed[i];\n                if (sorted) {\n                    if (isNotInOrderComparedToPrev()) {\n                        sorted = false;\n                    }\n                    prev = cur;\n                }\n            }\n            meta._sorted = sorted;\n        }\n        if (_stacked) {\n            updateStacks(this, parsed);\n        }\n    }\n parsePrimitiveData(meta, data, start, count) {\n        const { iScale , vScale  } = meta;\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        const labels = iScale.getLabels();\n        const singleScale = iScale === vScale;\n        const parsed = new Array(count);\n        let i, ilen, index;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            index = i + start;\n            parsed[i] = {\n                [iAxis]: singleScale || iScale.parse(labels[index], index),\n                [vAxis]: vScale.parse(data[index], index)\n            };\n        }\n        return parsed;\n    }\n parseArrayData(meta, data, start, count) {\n        const { xScale , yScale  } = meta;\n        const parsed = new Array(count);\n        let i, ilen, index, item;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            index = i + start;\n            item = data[index];\n            parsed[i] = {\n                x: xScale.parse(item[0], index),\n                y: yScale.parse(item[1], index)\n            };\n        }\n        return parsed;\n    }\n parseObjectData(meta, data, start, count) {\n        const { xScale , yScale  } = meta;\n        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;\n        const parsed = new Array(count);\n        let i, ilen, index, item;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            index = i + start;\n            item = data[index];\n            parsed[i] = {\n                x: xScale.parse(resolveObjectKey(item, xAxisKey), index),\n                y: yScale.parse(resolveObjectKey(item, yAxisKey), index)\n            };\n        }\n        return parsed;\n    }\n getParsed(index) {\n        return this._cachedMeta._parsed[index];\n    }\n getDataElement(index) {\n        return this._cachedMeta.data[index];\n    }\n applyStack(scale, parsed, mode) {\n        const chart = this.chart;\n        const meta = this._cachedMeta;\n        const value = parsed[scale.axis];\n        const stack = {\n            keys: getSortedDatasetIndices(chart, true),\n            values: parsed._stacks[scale.axis]._visualValues\n        };\n        return applyStack(stack, value, meta.index, {\n            mode\n        });\n    }\n updateRangeFromParsed(range, scale, parsed, stack) {\n        const parsedValue = parsed[scale.axis];\n        let value = parsedValue === null ? NaN : parsedValue;\n        const values = stack && parsed._stacks[scale.axis];\n        if (stack && values) {\n            stack.values = values;\n            value = applyStack(stack, parsedValue, this._cachedMeta.index);\n        }\n        range.min = Math.min(range.min, value);\n        range.max = Math.max(range.max, value);\n    }\n getMinMax(scale, canStack) {\n        const meta = this._cachedMeta;\n        const _parsed = meta._parsed;\n        const sorted = meta._sorted && scale === meta.iScale;\n        const ilen = _parsed.length;\n        const otherScale = this._getOtherScale(scale);\n        const stack = createStack(canStack, meta, this.chart);\n        const range = {\n            min: Number.POSITIVE_INFINITY,\n            max: Number.NEGATIVE_INFINITY\n        };\n        const { min: otherMin , max: otherMax  } = getUserBounds(otherScale);\n        let i, parsed;\n        function _skip() {\n            parsed = _parsed[i];\n            const otherValue = parsed[otherScale.axis];\n            return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue;\n        }\n        for(i = 0; i < ilen; ++i){\n            if (_skip()) {\n                continue;\n            }\n            this.updateRangeFromParsed(range, scale, parsed, stack);\n            if (sorted) {\n                break;\n            }\n        }\n        if (sorted) {\n            for(i = ilen - 1; i >= 0; --i){\n                if (_skip()) {\n                    continue;\n                }\n                this.updateRangeFromParsed(range, scale, parsed, stack);\n                break;\n            }\n        }\n        return range;\n    }\n    getAllParsedValues(scale) {\n        const parsed = this._cachedMeta._parsed;\n        const values = [];\n        let i, ilen, value;\n        for(i = 0, ilen = parsed.length; i < ilen; ++i){\n            value = parsed[i][scale.axis];\n            if (isNumberFinite(value)) {\n                values.push(value);\n            }\n        }\n        return values;\n    }\n getMaxOverflow() {\n        return false;\n    }\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const iScale = meta.iScale;\n        const vScale = meta.vScale;\n        const parsed = this.getParsed(index);\n        return {\n            label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',\n            value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''\n        };\n    }\n _update(mode) {\n        const meta = this._cachedMeta;\n        this.update(mode || 'default');\n        meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));\n    }\n update(mode) {}\n    draw() {\n        const ctx = this._ctx;\n        const chart = this.chart;\n        const meta = this._cachedMeta;\n        const elements = meta.data || [];\n        const area = chart.chartArea;\n        const active = [];\n        const start = this._drawStart || 0;\n        const count = this._drawCount || elements.length - start;\n        const drawActiveElementsOnTop = this.options.drawActiveElementsOnTop;\n        let i;\n        if (meta.dataset) {\n            meta.dataset.draw(ctx, area, start, count);\n        }\n        for(i = start; i < start + count; ++i){\n            const element = elements[i];\n            if (element.hidden) {\n                continue;\n            }\n            if (element.active && drawActiveElementsOnTop) {\n                active.push(element);\n            } else {\n                element.draw(ctx, area);\n            }\n        }\n        for(i = 0; i < active.length; ++i){\n            active[i].draw(ctx, area);\n        }\n    }\n getStyle(index, active) {\n        const mode = active ? 'active' : 'default';\n        return index === undefined && this._cachedMeta.dataset ? this.resolveDatasetElementOptions(mode) : this.resolveDataElementOptions(index || 0, mode);\n    }\n getContext(index, active, mode) {\n        const dataset = this.getDataset();\n        let context;\n        if (index >= 0 && index < this._cachedMeta.data.length) {\n            const element = this._cachedMeta.data[index];\n            context = element.$context || (element.$context = createDataContext(this.getContext(), index, element));\n            context.parsed = this.getParsed(index);\n            context.raw = dataset.data[index];\n            context.index = context.dataIndex = index;\n        } else {\n            context = this.$context || (this.$context = createDatasetContext(this.chart.getContext(), this.index));\n            context.dataset = dataset;\n            context.index = context.datasetIndex = this.index;\n        }\n        context.active = !!active;\n        context.mode = mode;\n        return context;\n    }\n resolveDatasetElementOptions(mode) {\n        return this._resolveElementOptions(this.datasetElementType.id, mode);\n    }\n resolveDataElementOptions(index, mode) {\n        return this._resolveElementOptions(this.dataElementType.id, mode, index);\n    }\n _resolveElementOptions(elementType, mode = 'default', index) {\n        const active = mode === 'active';\n        const cache = this._cachedDataOpts;\n        const cacheKey = elementType + '-' + mode;\n        const cached = cache[cacheKey];\n        const sharing = this.enableOptionSharing && defined(index);\n        if (cached) {\n            return cloneIfNotShared(cached, sharing);\n        }\n        const config = this.chart.config;\n        const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);\n        const prefixes = active ? [\n            `${elementType}Hover`,\n            'hover',\n            elementType,\n            ''\n        ] : [\n            elementType,\n            ''\n        ];\n        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);\n        const names = Object.keys(defaults.elements[elementType]);\n        const context = ()=>this.getContext(index, active, mode);\n        const values = config.resolveNamedOptions(scopes, names, context, prefixes);\n        if (values.$shared) {\n            values.$shared = sharing;\n            cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing));\n        }\n        return values;\n    }\n _resolveAnimations(index, transition, active) {\n        const chart = this.chart;\n        const cache = this._cachedDataOpts;\n        const cacheKey = `animation-${transition}`;\n        const cached = cache[cacheKey];\n        if (cached) {\n            return cached;\n        }\n        let options;\n        if (chart.options.animation !== false) {\n            const config = this.chart.config;\n            const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);\n            const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);\n            options = config.createResolver(scopes, this.getContext(index, active, transition));\n        }\n        const animations = new Animations(chart, options && options.animations);\n        if (options && options._cacheable) {\n            cache[cacheKey] = Object.freeze(animations);\n        }\n        return animations;\n    }\n getSharedOptions(options) {\n        if (!options.$shared) {\n            return;\n        }\n        return this._sharedOptions || (this._sharedOptions = Object.assign({}, options));\n    }\n includeOptions(mode, sharedOptions) {\n        return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled;\n    }\n _getSharedOptions(start, mode) {\n        const firstOpts = this.resolveDataElementOptions(start, mode);\n        const previouslySharedOptions = this._sharedOptions;\n        const sharedOptions = this.getSharedOptions(firstOpts);\n        const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions;\n        this.updateSharedOptions(sharedOptions, mode, firstOpts);\n        return {\n            sharedOptions,\n            includeOptions\n        };\n    }\n updateElement(element, index, properties, mode) {\n        if (isDirectUpdateMode(mode)) {\n            Object.assign(element, properties);\n        } else {\n            this._resolveAnimations(index, mode).update(element, properties);\n        }\n    }\n updateSharedOptions(sharedOptions, mode, newOptions) {\n        if (sharedOptions && !isDirectUpdateMode(mode)) {\n            this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions);\n        }\n    }\n _setStyle(element, index, mode, active) {\n        element.active = active;\n        const options = this.getStyle(index, active);\n        this._resolveAnimations(index, mode, active).update(element, {\n            options: !active && this.getSharedOptions(options) || options\n        });\n    }\n    removeHoverStyle(element, datasetIndex, index) {\n        this._setStyle(element, index, 'active', false);\n    }\n    setHoverStyle(element, datasetIndex, index) {\n        this._setStyle(element, index, 'active', true);\n    }\n _removeDatasetHoverStyle() {\n        const element = this._cachedMeta.dataset;\n        if (element) {\n            this._setStyle(element, undefined, 'active', false);\n        }\n    }\n _setDatasetHoverStyle() {\n        const element = this._cachedMeta.dataset;\n        if (element) {\n            this._setStyle(element, undefined, 'active', true);\n        }\n    }\n _resyncElements(resetNewElements) {\n        const data = this._data;\n        const elements = this._cachedMeta.data;\n        for (const [method, arg1, arg2] of this._syncList){\n            this[method](arg1, arg2);\n        }\n        this._syncList = [];\n        const numMeta = elements.length;\n        const numData = data.length;\n        const count = Math.min(numData, numMeta);\n        if (count) {\n            this.parse(0, count);\n        }\n        if (numData > numMeta) {\n            this._insertElements(numMeta, numData - numMeta, resetNewElements);\n        } else if (numData < numMeta) {\n            this._removeElements(numData, numMeta - numData);\n        }\n    }\n _insertElements(start, count, resetNewElements = true) {\n        const meta = this._cachedMeta;\n        const data = meta.data;\n        const end = start + count;\n        let i;\n        const move = (arr)=>{\n            arr.length += count;\n            for(i = arr.length - 1; i >= end; i--){\n                arr[i] = arr[i - count];\n            }\n        };\n        move(data);\n        for(i = start; i < end; ++i){\n            data[i] = new this.dataElementType();\n        }\n        if (this._parsing) {\n            move(meta._parsed);\n        }\n        this.parse(start, count);\n        if (resetNewElements) {\n            this.updateElements(data, start, count, 'reset');\n        }\n    }\n    updateElements(element, start, count, mode) {}\n _removeElements(start, count) {\n        const meta = this._cachedMeta;\n        if (this._parsing) {\n            const removed = meta._parsed.splice(start, count);\n            if (meta._stacked) {\n                clearStacks(meta, removed);\n            }\n        }\n        meta.data.splice(start, count);\n    }\n _sync(args) {\n        if (this._parsing) {\n            this._syncList.push(args);\n        } else {\n            const [method, arg1, arg2] = args;\n            this[method](arg1, arg2);\n        }\n        this.chart._dataChanges.push([\n            this.index,\n            ...args\n        ]);\n    }\n    _onDataPush() {\n        const count = arguments.length;\n        this._sync([\n            '_insertElements',\n            this.getDataset().data.length - count,\n            count\n        ]);\n    }\n    _onDataPop() {\n        this._sync([\n            '_removeElements',\n            this._cachedMeta.data.length - 1,\n            1\n        ]);\n    }\n    _onDataShift() {\n        this._sync([\n            '_removeElements',\n            0,\n            1\n        ]);\n    }\n    _onDataSplice(start, count) {\n        if (count) {\n            this._sync([\n                '_removeElements',\n                start,\n                count\n            ]);\n        }\n        const newCount = arguments.length - 2;\n        if (newCount) {\n            this._sync([\n                '_insertElements',\n                start,\n                newCount\n            ]);\n        }\n    }\n    _onDataUnshift() {\n        this._sync([\n            '_insertElements',\n            0,\n            arguments.length\n        ]);\n    }\n}\n\nfunction getAllScaleValues(scale, type) {\n    if (!scale._cache.$bar) {\n        const visibleMetas = scale.getMatchingVisibleMetas(type);\n        let values = [];\n        for(let i = 0, ilen = visibleMetas.length; i < ilen; i++){\n            values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale));\n        }\n        scale._cache.$bar = _arrayUnique(values.sort((a, b)=>a - b));\n    }\n    return scale._cache.$bar;\n}\n function computeMinSampleSize(meta) {\n    const scale = meta.iScale;\n    const values = getAllScaleValues(scale, meta.type);\n    let min = scale._length;\n    let i, ilen, curr, prev;\n    const updateMinAndPrev = ()=>{\n        if (curr === 32767 || curr === -32768) {\n            return;\n        }\n        if (defined(prev)) {\n            min = Math.min(min, Math.abs(curr - prev) || min);\n        }\n        prev = curr;\n    };\n    for(i = 0, ilen = values.length; i < ilen; ++i){\n        curr = scale.getPixelForValue(values[i]);\n        updateMinAndPrev();\n    }\n    prev = undefined;\n    for(i = 0, ilen = scale.ticks.length; i < ilen; ++i){\n        curr = scale.getPixelForTick(i);\n        updateMinAndPrev();\n    }\n    return min;\n}\n function computeFitCategoryTraits(index, ruler, options, stackCount) {\n    const thickness = options.barThickness;\n    let size, ratio;\n    if (isNullOrUndef(thickness)) {\n        size = ruler.min * options.categoryPercentage;\n        ratio = options.barPercentage;\n    } else {\n        size = thickness * stackCount;\n        ratio = 1;\n    }\n    return {\n        chunk: size / stackCount,\n        ratio,\n        start: ruler.pixels[index] - size / 2\n    };\n}\n function computeFlexCategoryTraits(index, ruler, options, stackCount) {\n    const pixels = ruler.pixels;\n    const curr = pixels[index];\n    let prev = index > 0 ? pixels[index - 1] : null;\n    let next = index < pixels.length - 1 ? pixels[index + 1] : null;\n    const percent = options.categoryPercentage;\n    if (prev === null) {\n        prev = curr - (next === null ? ruler.end - ruler.start : next - curr);\n    }\n    if (next === null) {\n        next = curr + curr - prev;\n    }\n    const start = curr - (curr - Math.min(prev, next)) / 2 * percent;\n    const size = Math.abs(next - prev) / 2 * percent;\n    return {\n        chunk: size / stackCount,\n        ratio: options.barPercentage,\n        start\n    };\n}\nfunction parseFloatBar(entry, item, vScale, i) {\n    const startValue = vScale.parse(entry[0], i);\n    const endValue = vScale.parse(entry[1], i);\n    const min = Math.min(startValue, endValue);\n    const max = Math.max(startValue, endValue);\n    let barStart = min;\n    let barEnd = max;\n    if (Math.abs(min) > Math.abs(max)) {\n        barStart = max;\n        barEnd = min;\n    }\n    item[vScale.axis] = barEnd;\n    item._custom = {\n        barStart,\n        barEnd,\n        start: startValue,\n        end: endValue,\n        min,\n        max\n    };\n}\nfunction parseValue(entry, item, vScale, i) {\n    if (isArray(entry)) {\n        parseFloatBar(entry, item, vScale, i);\n    } else {\n        item[vScale.axis] = vScale.parse(entry, i);\n    }\n    return item;\n}\nfunction parseArrayOrPrimitive(meta, data, start, count) {\n    const iScale = meta.iScale;\n    const vScale = meta.vScale;\n    const labels = iScale.getLabels();\n    const singleScale = iScale === vScale;\n    const parsed = [];\n    let i, ilen, item, entry;\n    for(i = start, ilen = start + count; i < ilen; ++i){\n        entry = data[i];\n        item = {};\n        item[iScale.axis] = singleScale || iScale.parse(labels[i], i);\n        parsed.push(parseValue(entry, item, vScale, i));\n    }\n    return parsed;\n}\nfunction isFloatBar(custom) {\n    return custom && custom.barStart !== undefined && custom.barEnd !== undefined;\n}\nfunction barSign(size, vScale, actualBase) {\n    if (size !== 0) {\n        return sign(size);\n    }\n    return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1);\n}\nfunction borderProps(properties) {\n    let reverse, start, end, top, bottom;\n    if (properties.horizontal) {\n        reverse = properties.base > properties.x;\n        start = 'left';\n        end = 'right';\n    } else {\n        reverse = properties.base < properties.y;\n        start = 'bottom';\n        end = 'top';\n    }\n    if (reverse) {\n        top = 'end';\n        bottom = 'start';\n    } else {\n        top = 'start';\n        bottom = 'end';\n    }\n    return {\n        start,\n        end,\n        reverse,\n        top,\n        bottom\n    };\n}\nfunction setBorderSkipped(properties, options, stack, index) {\n    let edge = options.borderSkipped;\n    const res = {};\n    if (!edge) {\n        properties.borderSkipped = res;\n        return;\n    }\n    if (edge === true) {\n        properties.borderSkipped = {\n            top: true,\n            right: true,\n            bottom: true,\n            left: true\n        };\n        return;\n    }\n    const { start , end , reverse , top , bottom  } = borderProps(properties);\n    if (edge === 'middle' && stack) {\n        properties.enableBorderRadius = true;\n        if ((stack._top || 0) === index) {\n            edge = top;\n        } else if ((stack._bottom || 0) === index) {\n            edge = bottom;\n        } else {\n            res[parseEdge(bottom, start, end, reverse)] = true;\n            edge = top;\n        }\n    }\n    res[parseEdge(edge, start, end, reverse)] = true;\n    properties.borderSkipped = res;\n}\nfunction parseEdge(edge, a, b, reverse) {\n    if (reverse) {\n        edge = swap(edge, a, b);\n        edge = startEnd(edge, b, a);\n    } else {\n        edge = startEnd(edge, a, b);\n    }\n    return edge;\n}\nfunction swap(orig, v1, v2) {\n    return orig === v1 ? v2 : orig === v2 ? v1 : orig;\n}\nfunction startEnd(v, start, end) {\n    return v === 'start' ? start : v === 'end' ? end : v;\n}\nfunction setInflateAmount(properties, { inflateAmount  }, ratio) {\n    properties.inflateAmount = inflateAmount === 'auto' ? ratio === 1 ? 0.33 : 0 : inflateAmount;\n}\nclass BarController extends DatasetController {\n    static id = 'bar';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'bar',\n        categoryPercentage: 0.8,\n        barPercentage: 0.9,\n        grouped: true,\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'base',\n                    'width',\n                    'height'\n                ]\n            }\n        }\n    };\n static overrides = {\n        scales: {\n            _index_: {\n                type: 'category',\n                offset: true,\n                grid: {\n                    offset: true\n                }\n            },\n            _value_: {\n                type: 'linear',\n                beginAtZero: true\n            }\n        }\n    };\n parsePrimitiveData(meta, data, start, count) {\n        return parseArrayOrPrimitive(meta, data, start, count);\n    }\n parseArrayData(meta, data, start, count) {\n        return parseArrayOrPrimitive(meta, data, start, count);\n    }\n parseObjectData(meta, data, start, count) {\n        const { iScale , vScale  } = meta;\n        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;\n        const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey;\n        const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey;\n        const parsed = [];\n        let i, ilen, item, obj;\n        for(i = start, ilen = start + count; i < ilen; ++i){\n            obj = data[i];\n            item = {};\n            item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i);\n            parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i));\n        }\n        return parsed;\n    }\n updateRangeFromParsed(range, scale, parsed, stack) {\n        super.updateRangeFromParsed(range, scale, parsed, stack);\n        const custom = parsed._custom;\n        if (custom && scale === this._cachedMeta.vScale) {\n            range.min = Math.min(range.min, custom.min);\n            range.max = Math.max(range.max, custom.max);\n        }\n    }\n getMaxOverflow() {\n        return 0;\n    }\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const { iScale , vScale  } = meta;\n        const parsed = this.getParsed(index);\n        const custom = parsed._custom;\n        const value = isFloatBar(custom) ? '[' + custom.start + ', ' + custom.end + ']' : '' + vScale.getLabelForValue(parsed[vScale.axis]);\n        return {\n            label: '' + iScale.getLabelForValue(parsed[iScale.axis]),\n            value\n        };\n    }\n    initialize() {\n        this.enableOptionSharing = true;\n        super.initialize();\n        const meta = this._cachedMeta;\n        meta.stack = this.getDataset().stack;\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        this.updateElements(meta.data, 0, meta.data.length, mode);\n    }\n    updateElements(bars, start, count, mode) {\n        const reset = mode === 'reset';\n        const { index , _cachedMeta: { vScale  }  } = this;\n        const base = vScale.getBasePixel();\n        const horizontal = vScale.isHorizontal();\n        const ruler = this._getRuler();\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        for(let i = start; i < start + count; i++){\n            const parsed = this.getParsed(i);\n            const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {\n                base,\n                head: base\n            } : this._calculateBarValuePixels(i);\n            const ipixels = this._calculateBarIndexPixels(i, ruler);\n            const stack = (parsed._stacks || {})[vScale.axis];\n            const properties = {\n                horizontal,\n                base: vpixels.base,\n                enableBorderRadius: !stack || isFloatBar(parsed._custom) || index === stack._top || index === stack._bottom,\n                x: horizontal ? vpixels.head : ipixels.center,\n                y: horizontal ? ipixels.center : vpixels.head,\n                height: horizontal ? ipixels.size : Math.abs(vpixels.size),\n                width: horizontal ? Math.abs(vpixels.size) : ipixels.size\n            };\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);\n            }\n            const options = properties.options || bars[i].options;\n            setBorderSkipped(properties, options, stack, index);\n            setInflateAmount(properties, options, ruler.ratio);\n            this.updateElement(bars[i], i, properties, mode);\n        }\n    }\n _getStacks(last, dataIndex) {\n        const { iScale  } = this._cachedMeta;\n        const metasets = iScale.getMatchingVisibleMetas(this._type).filter((meta)=>meta.controller.options.grouped);\n        const stacked = iScale.options.stacked;\n        const stacks = [];\n        const skipNull = (meta)=>{\n            const parsed = meta.controller.getParsed(dataIndex);\n            const val = parsed && parsed[meta.vScale.axis];\n            if (isNullOrUndef(val) || isNaN(val)) {\n                return true;\n            }\n        };\n        for (const meta of metasets){\n            if (dataIndex !== undefined && skipNull(meta)) {\n                continue;\n            }\n            if (stacked === false || stacks.indexOf(meta.stack) === -1 || stacked === undefined && meta.stack === undefined) {\n                stacks.push(meta.stack);\n            }\n            if (meta.index === last) {\n                break;\n            }\n        }\n        if (!stacks.length) {\n            stacks.push(undefined);\n        }\n        return stacks;\n    }\n _getStackCount(index) {\n        return this._getStacks(undefined, index).length;\n    }\n _getStackIndex(datasetIndex, name, dataIndex) {\n        const stacks = this._getStacks(datasetIndex, dataIndex);\n        const index = name !== undefined ? stacks.indexOf(name) : -1;\n        return index === -1 ? stacks.length - 1 : index;\n    }\n _getRuler() {\n        const opts = this.options;\n        const meta = this._cachedMeta;\n        const iScale = meta.iScale;\n        const pixels = [];\n        let i, ilen;\n        for(i = 0, ilen = meta.data.length; i < ilen; ++i){\n            pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i));\n        }\n        const barThickness = opts.barThickness;\n        const min = barThickness || computeMinSampleSize(meta);\n        return {\n            min,\n            pixels,\n            start: iScale._startPixel,\n            end: iScale._endPixel,\n            stackCount: this._getStackCount(),\n            scale: iScale,\n            grouped: opts.grouped,\n            ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage\n        };\n    }\n _calculateBarValuePixels(index) {\n        const { _cachedMeta: { vScale , _stacked , index: datasetIndex  } , options: { base: baseValue , minBarLength  }  } = this;\n        const actualBase = baseValue || 0;\n        const parsed = this.getParsed(index);\n        const custom = parsed._custom;\n        const floating = isFloatBar(custom);\n        let value = parsed[vScale.axis];\n        let start = 0;\n        let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value;\n        let head, size;\n        if (length !== value) {\n            start = length - value;\n            length = value;\n        }\n        if (floating) {\n            value = custom.barStart;\n            length = custom.barEnd - custom.barStart;\n            if (value !== 0 && sign(value) !== sign(custom.barEnd)) {\n                start = 0;\n            }\n            start += value;\n        }\n        const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start;\n        let base = vScale.getPixelForValue(startValue);\n        if (this.chart.getDataVisibility(index)) {\n            head = vScale.getPixelForValue(start + length);\n        } else {\n            head = base;\n        }\n        size = head - base;\n        if (Math.abs(size) < minBarLength) {\n            size = barSign(size, vScale, actualBase) * minBarLength;\n            if (value === actualBase) {\n                base -= size / 2;\n            }\n            const startPixel = vScale.getPixelForDecimal(0);\n            const endPixel = vScale.getPixelForDecimal(1);\n            const min = Math.min(startPixel, endPixel);\n            const max = Math.max(startPixel, endPixel);\n            base = Math.max(Math.min(base, max), min);\n            head = base + size;\n            if (_stacked && !floating) {\n                parsed._stacks[vScale.axis]._visualValues[datasetIndex] = vScale.getValueForPixel(head) - vScale.getValueForPixel(base);\n            }\n        }\n        if (base === vScale.getPixelForValue(actualBase)) {\n            const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2;\n            base += halfGrid;\n            size -= halfGrid;\n        }\n        return {\n            size,\n            base,\n            head,\n            center: head + size / 2\n        };\n    }\n _calculateBarIndexPixels(index, ruler) {\n        const scale = ruler.scale;\n        const options = this.options;\n        const skipNull = options.skipNull;\n        const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);\n        let center, size;\n        if (ruler.grouped) {\n            const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;\n            const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount) : computeFitCategoryTraits(index, ruler, options, stackCount);\n            const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);\n            center = range.start + range.chunk * stackIndex + range.chunk / 2;\n            size = Math.min(maxBarThickness, range.chunk * range.ratio);\n        } else {\n            center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index);\n            size = Math.min(maxBarThickness, ruler.min * ruler.ratio);\n        }\n        return {\n            base: center - size / 2,\n            head: center + size / 2,\n            center,\n            size\n        };\n    }\n    draw() {\n        const meta = this._cachedMeta;\n        const vScale = meta.vScale;\n        const rects = meta.data;\n        const ilen = rects.length;\n        let i = 0;\n        for(; i < ilen; ++i){\n            if (this.getParsed(i)[vScale.axis] !== null) {\n                rects[i].draw(this._ctx);\n            }\n        }\n    }\n}\n\nclass BubbleController extends DatasetController {\n    static id = 'bubble';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'point',\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'borderWidth',\n                    'radius'\n                ]\n            }\n        }\n    };\n static overrides = {\n        scales: {\n            x: {\n                type: 'linear'\n            },\n            y: {\n                type: 'linear'\n            }\n        }\n    };\n    initialize() {\n        this.enableOptionSharing = true;\n        super.initialize();\n    }\n parsePrimitiveData(meta, data, start, count) {\n        const parsed = super.parsePrimitiveData(meta, data, start, count);\n        for(let i = 0; i < parsed.length; i++){\n            parsed[i]._custom = this.resolveDataElementOptions(i + start).radius;\n        }\n        return parsed;\n    }\n parseArrayData(meta, data, start, count) {\n        const parsed = super.parseArrayData(meta, data, start, count);\n        for(let i = 0; i < parsed.length; i++){\n            const item = data[start + i];\n            parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius);\n        }\n        return parsed;\n    }\n parseObjectData(meta, data, start, count) {\n        const parsed = super.parseObjectData(meta, data, start, count);\n        for(let i = 0; i < parsed.length; i++){\n            const item = data[start + i];\n            parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius);\n        }\n        return parsed;\n    }\n getMaxOverflow() {\n        const data = this._cachedMeta.data;\n        let max = 0;\n        for(let i = data.length - 1; i >= 0; --i){\n            max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);\n        }\n        return max > 0 && max;\n    }\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const labels = this.chart.data.labels || [];\n        const { xScale , yScale  } = meta;\n        const parsed = this.getParsed(index);\n        const x = xScale.getLabelForValue(parsed.x);\n        const y = yScale.getLabelForValue(parsed.y);\n        const r = parsed._custom;\n        return {\n            label: labels[index] || '',\n            value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')'\n        };\n    }\n    update(mode) {\n        const points = this._cachedMeta.data;\n        this.updateElements(points, 0, points.length, mode);\n    }\n    updateElements(points, start, count, mode) {\n        const reset = mode === 'reset';\n        const { iScale , vScale  } = this._cachedMeta;\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        for(let i = start; i < start + count; i++){\n            const point = points[i];\n            const parsed = !reset && this.getParsed(i);\n            const properties = {};\n            const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);\n            const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);\n            properties.skip = isNaN(iPixel) || isNaN(vPixel);\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n                if (reset) {\n                    properties.options.radius = 0;\n                }\n            }\n            this.updateElement(point, i, properties, mode);\n        }\n    }\n resolveDataElementOptions(index, mode) {\n        const parsed = this.getParsed(index);\n        let values = super.resolveDataElementOptions(index, mode);\n        if (values.$shared) {\n            values = Object.assign({}, values, {\n                $shared: false\n            });\n        }\n        const radius = values.radius;\n        if (mode !== 'active') {\n            values.radius = 0;\n        }\n        values.radius += valueOrDefault(parsed && parsed._custom, radius);\n        return values;\n    }\n}\n\nfunction getRatioAndOffset(rotation, circumference, cutout) {\n    let ratioX = 1;\n    let ratioY = 1;\n    let offsetX = 0;\n    let offsetY = 0;\n    if (circumference < TAU) {\n        const startAngle = rotation;\n        const endAngle = startAngle + circumference;\n        const startX = Math.cos(startAngle);\n        const startY = Math.sin(startAngle);\n        const endX = Math.cos(endAngle);\n        const endY = Math.sin(endAngle);\n        const calcMax = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout);\n        const calcMin = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout);\n        const maxX = calcMax(0, startX, endX);\n        const maxY = calcMax(HALF_PI, startY, endY);\n        const minX = calcMin(PI, startX, endX);\n        const minY = calcMin(PI + HALF_PI, startY, endY);\n        ratioX = (maxX - minX) / 2;\n        ratioY = (maxY - minY) / 2;\n        offsetX = -(maxX + minX) / 2;\n        offsetY = -(maxY + minY) / 2;\n    }\n    return {\n        ratioX,\n        ratioY,\n        offsetX,\n        offsetY\n    };\n}\nclass DoughnutController extends DatasetController {\n    static id = 'doughnut';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'arc',\n        animation: {\n            animateRotate: true,\n            animateScale: false\n        },\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'circumference',\n                    'endAngle',\n                    'innerRadius',\n                    'outerRadius',\n                    'startAngle',\n                    'x',\n                    'y',\n                    'offset',\n                    'borderWidth',\n                    'spacing'\n                ]\n            }\n        },\n        cutout: '50%',\n        rotation: 0,\n        circumference: 360,\n        radius: '100%',\n        spacing: 0,\n        indexAxis: 'r'\n    };\n    static descriptors = {\n        _scriptable: (name)=>name !== 'spacing',\n        _indexable: (name)=>name !== 'spacing'\n    };\n static overrides = {\n        aspectRatio: 1,\n        plugins: {\n            legend: {\n                labels: {\n                    generateLabels (chart) {\n                        const data = chart.data;\n                        if (data.labels.length && data.datasets.length) {\n                            const { labels: { pointStyle , color  }  } = chart.legend.options;\n                            return data.labels.map((label, i)=>{\n                                const meta = chart.getDatasetMeta(0);\n                                const style = meta.controller.getStyle(i);\n                                return {\n                                    text: label,\n                                    fillStyle: style.backgroundColor,\n                                    strokeStyle: style.borderColor,\n                                    fontColor: color,\n                                    lineWidth: style.borderWidth,\n                                    pointStyle: pointStyle,\n                                    hidden: !chart.getDataVisibility(i),\n                                    index: i\n                                };\n                            });\n                        }\n                        return [];\n                    }\n                },\n                onClick (e, legendItem, legend) {\n                    legend.chart.toggleDataVisibility(legendItem.index);\n                    legend.chart.update();\n                }\n            }\n        }\n    };\n    constructor(chart, datasetIndex){\n        super(chart, datasetIndex);\n        this.enableOptionSharing = true;\n        this.innerRadius = undefined;\n        this.outerRadius = undefined;\n        this.offsetX = undefined;\n        this.offsetY = undefined;\n    }\n    linkScales() {}\n parse(start, count) {\n        const data = this.getDataset().data;\n        const meta = this._cachedMeta;\n        if (this._parsing === false) {\n            meta._parsed = data;\n        } else {\n            let getter = (i)=>+data[i];\n            if (isObject(data[start])) {\n                const { key ='value'  } = this._parsing;\n                getter = (i)=>+resolveObjectKey(data[i], key);\n            }\n            let i, ilen;\n            for(i = start, ilen = start + count; i < ilen; ++i){\n                meta._parsed[i] = getter(i);\n            }\n        }\n    }\n _getRotation() {\n        return toRadians(this.options.rotation - 90);\n    }\n _getCircumference() {\n        return toRadians(this.options.circumference);\n    }\n _getRotationExtents() {\n        let min = TAU;\n        let max = -TAU;\n        for(let i = 0; i < this.chart.data.datasets.length; ++i){\n            if (this.chart.isDatasetVisible(i) && this.chart.getDatasetMeta(i).type === this._type) {\n                const controller = this.chart.getDatasetMeta(i).controller;\n                const rotation = controller._getRotation();\n                const circumference = controller._getCircumference();\n                min = Math.min(min, rotation);\n                max = Math.max(max, rotation + circumference);\n            }\n        }\n        return {\n            rotation: min,\n            circumference: max - min\n        };\n    }\n update(mode) {\n        const chart = this.chart;\n        const { chartArea  } = chart;\n        const meta = this._cachedMeta;\n        const arcs = meta.data;\n        const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing;\n        const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);\n        const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1);\n        const chartWeight = this._getRingWeight(this.index);\n        const { circumference , rotation  } = this._getRotationExtents();\n        const { ratioX , ratioY , offsetX , offsetY  } = getRatioAndOffset(rotation, circumference, cutout);\n        const maxWidth = (chartArea.width - spacing) / ratioX;\n        const maxHeight = (chartArea.height - spacing) / ratioY;\n        const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);\n        const outerRadius = toDimension(this.options.radius, maxRadius);\n        const innerRadius = Math.max(outerRadius * cutout, 0);\n        const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal();\n        this.offsetX = offsetX * outerRadius;\n        this.offsetY = offsetY * outerRadius;\n        meta.total = this.calculateTotal();\n        this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);\n        this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);\n        this.updateElements(arcs, 0, arcs.length, mode);\n    }\n _circumference(i, reset) {\n        const opts = this.options;\n        const meta = this._cachedMeta;\n        const circumference = this._getCircumference();\n        if (reset && opts.animation.animateRotate || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {\n            return 0;\n        }\n        return this.calculateCircumference(meta._parsed[i] * circumference / TAU);\n    }\n    updateElements(arcs, start, count, mode) {\n        const reset = mode === 'reset';\n        const chart = this.chart;\n        const chartArea = chart.chartArea;\n        const opts = chart.options;\n        const animationOpts = opts.animation;\n        const centerX = (chartArea.left + chartArea.right) / 2;\n        const centerY = (chartArea.top + chartArea.bottom) / 2;\n        const animateScale = reset && animationOpts.animateScale;\n        const innerRadius = animateScale ? 0 : this.innerRadius;\n        const outerRadius = animateScale ? 0 : this.outerRadius;\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        let startAngle = this._getRotation();\n        let i;\n        for(i = 0; i < start; ++i){\n            startAngle += this._circumference(i, reset);\n        }\n        for(i = start; i < start + count; ++i){\n            const circumference = this._circumference(i, reset);\n            const arc = arcs[i];\n            const properties = {\n                x: centerX + this.offsetX,\n                y: centerY + this.offsetY,\n                startAngle,\n                endAngle: startAngle + circumference,\n                circumference,\n                outerRadius,\n                innerRadius\n            };\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode);\n            }\n            startAngle += circumference;\n            this.updateElement(arc, i, properties, mode);\n        }\n    }\n    calculateTotal() {\n        const meta = this._cachedMeta;\n        const metaData = meta.data;\n        let total = 0;\n        let i;\n        for(i = 0; i < metaData.length; i++){\n            const value = meta._parsed[i];\n            if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) {\n                total += Math.abs(value);\n            }\n        }\n        return total;\n    }\n    calculateCircumference(value) {\n        const total = this._cachedMeta.total;\n        if (total > 0 && !isNaN(value)) {\n            return TAU * (Math.abs(value) / total);\n        }\n        return 0;\n    }\n    getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const chart = this.chart;\n        const labels = chart.data.labels || [];\n        const value = formatNumber(meta._parsed[index], chart.options.locale);\n        return {\n            label: labels[index] || '',\n            value\n        };\n    }\n    getMaxBorderWidth(arcs) {\n        let max = 0;\n        const chart = this.chart;\n        let i, ilen, meta, controller, options;\n        if (!arcs) {\n            for(i = 0, ilen = chart.data.datasets.length; i < ilen; ++i){\n                if (chart.isDatasetVisible(i)) {\n                    meta = chart.getDatasetMeta(i);\n                    arcs = meta.data;\n                    controller = meta.controller;\n                    break;\n                }\n            }\n        }\n        if (!arcs) {\n            return 0;\n        }\n        for(i = 0, ilen = arcs.length; i < ilen; ++i){\n            options = controller.resolveDataElementOptions(i);\n            if (options.borderAlign !== 'inner') {\n                max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0);\n            }\n        }\n        return max;\n    }\n    getMaxOffset(arcs) {\n        let max = 0;\n        for(let i = 0, ilen = arcs.length; i < ilen; ++i){\n            const options = this.resolveDataElementOptions(i);\n            max = Math.max(max, options.offset || 0, options.hoverOffset || 0);\n        }\n        return max;\n    }\n _getRingWeightOffset(datasetIndex) {\n        let ringWeightOffset = 0;\n        for(let i = 0; i < datasetIndex; ++i){\n            if (this.chart.isDatasetVisible(i)) {\n                ringWeightOffset += this._getRingWeight(i);\n            }\n        }\n        return ringWeightOffset;\n    }\n _getRingWeight(datasetIndex) {\n        return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0);\n    }\n _getVisibleDatasetWeightTotal() {\n        return this._getRingWeightOffset(this.chart.data.datasets.length) || 1;\n    }\n}\n\nclass LineController extends DatasetController {\n    static id = 'line';\n static defaults = {\n        datasetElementType: 'line',\n        dataElementType: 'point',\n        showLine: true,\n        spanGaps: false\n    };\n static overrides = {\n        scales: {\n            _index_: {\n                type: 'category'\n            },\n            _value_: {\n                type: 'linear'\n            }\n        }\n    };\n    initialize() {\n        this.enableOptionSharing = true;\n        this.supportsDecimation = true;\n        super.initialize();\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        const { dataset: line , data: points = [] , _dataset  } = meta;\n        const animationsDisabled = this.chart._animationsDisabled;\n        let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);\n        this._drawStart = start;\n        this._drawCount = count;\n        if (_scaleRangesChanged(meta)) {\n            start = 0;\n            count = points.length;\n        }\n        line._chart = this.chart;\n        line._datasetIndex = this.index;\n        line._decimated = !!_dataset._decimated;\n        line.points = points;\n        const options = this.resolveDatasetElementOptions(mode);\n        if (!this.options.showLine) {\n            options.borderWidth = 0;\n        }\n        options.segment = this.options.segment;\n        this.updateElement(line, undefined, {\n            animated: !animationsDisabled,\n            options\n        }, mode);\n        this.updateElements(points, start, count, mode);\n    }\n    updateElements(points, start, count, mode) {\n        const reset = mode === 'reset';\n        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;\n        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        const { spanGaps , segment  } = this.options;\n        const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;\n        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';\n        const end = start + count;\n        const pointsCount = points.length;\n        let prevParsed = start > 0 && this.getParsed(start - 1);\n        for(let i = 0; i < pointsCount; ++i){\n            const point = points[i];\n            const properties = directUpdate ? point : {};\n            if (i < start || i >= end) {\n                properties.skip = true;\n                continue;\n            }\n            const parsed = this.getParsed(i);\n            const nullData = isNullOrUndef(parsed[vAxis]);\n            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);\n            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);\n            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;\n            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;\n            if (segment) {\n                properties.parsed = parsed;\n                properties.raw = _dataset.data[i];\n            }\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n            }\n            if (!directUpdate) {\n                this.updateElement(point, i, properties, mode);\n            }\n            prevParsed = parsed;\n        }\n    }\n getMaxOverflow() {\n        const meta = this._cachedMeta;\n        const dataset = meta.dataset;\n        const border = dataset.options && dataset.options.borderWidth || 0;\n        const data = meta.data || [];\n        if (!data.length) {\n            return border;\n        }\n        const firstPoint = data[0].size(this.resolveDataElementOptions(0));\n        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));\n        return Math.max(border, firstPoint, lastPoint) / 2;\n    }\n    draw() {\n        const meta = this._cachedMeta;\n        meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis);\n        super.draw();\n    }\n}\n\nclass PolarAreaController extends DatasetController {\n    static id = 'polarArea';\n static defaults = {\n        dataElementType: 'arc',\n        animation: {\n            animateRotate: true,\n            animateScale: true\n        },\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'startAngle',\n                    'endAngle',\n                    'innerRadius',\n                    'outerRadius'\n                ]\n            }\n        },\n        indexAxis: 'r',\n        startAngle: 0\n    };\n static overrides = {\n        aspectRatio: 1,\n        plugins: {\n            legend: {\n                labels: {\n                    generateLabels (chart) {\n                        const data = chart.data;\n                        if (data.labels.length && data.datasets.length) {\n                            const { labels: { pointStyle , color  }  } = chart.legend.options;\n                            return data.labels.map((label, i)=>{\n                                const meta = chart.getDatasetMeta(0);\n                                const style = meta.controller.getStyle(i);\n                                return {\n                                    text: label,\n                                    fillStyle: style.backgroundColor,\n                                    strokeStyle: style.borderColor,\n                                    fontColor: color,\n                                    lineWidth: style.borderWidth,\n                                    pointStyle: pointStyle,\n                                    hidden: !chart.getDataVisibility(i),\n                                    index: i\n                                };\n                            });\n                        }\n                        return [];\n                    }\n                },\n                onClick (e, legendItem, legend) {\n                    legend.chart.toggleDataVisibility(legendItem.index);\n                    legend.chart.update();\n                }\n            }\n        },\n        scales: {\n            r: {\n                type: 'radialLinear',\n                angleLines: {\n                    display: false\n                },\n                beginAtZero: true,\n                grid: {\n                    circular: true\n                },\n                pointLabels: {\n                    display: false\n                },\n                startAngle: 0\n            }\n        }\n    };\n    constructor(chart, datasetIndex){\n        super(chart, datasetIndex);\n        this.innerRadius = undefined;\n        this.outerRadius = undefined;\n    }\n    getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const chart = this.chart;\n        const labels = chart.data.labels || [];\n        const value = formatNumber(meta._parsed[index].r, chart.options.locale);\n        return {\n            label: labels[index] || '',\n            value\n        };\n    }\n    parseObjectData(meta, data, start, count) {\n        return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);\n    }\n    update(mode) {\n        const arcs = this._cachedMeta.data;\n        this._updateRadius();\n        this.updateElements(arcs, 0, arcs.length, mode);\n    }\n getMinMax() {\n        const meta = this._cachedMeta;\n        const range = {\n            min: Number.POSITIVE_INFINITY,\n            max: Number.NEGATIVE_INFINITY\n        };\n        meta.data.forEach((element, index)=>{\n            const parsed = this.getParsed(index).r;\n            if (!isNaN(parsed) && this.chart.getDataVisibility(index)) {\n                if (parsed < range.min) {\n                    range.min = parsed;\n                }\n                if (parsed > range.max) {\n                    range.max = parsed;\n                }\n            }\n        });\n        return range;\n    }\n _updateRadius() {\n        const chart = this.chart;\n        const chartArea = chart.chartArea;\n        const opts = chart.options;\n        const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);\n        const outerRadius = Math.max(minSize / 2, 0);\n        const innerRadius = Math.max(opts.cutoutPercentage ? outerRadius / 100 * opts.cutoutPercentage : 1, 0);\n        const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();\n        this.outerRadius = outerRadius - radiusLength * this.index;\n        this.innerRadius = this.outerRadius - radiusLength;\n    }\n    updateElements(arcs, start, count, mode) {\n        const reset = mode === 'reset';\n        const chart = this.chart;\n        const opts = chart.options;\n        const animationOpts = opts.animation;\n        const scale = this._cachedMeta.rScale;\n        const centerX = scale.xCenter;\n        const centerY = scale.yCenter;\n        const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;\n        let angle = datasetStartAngle;\n        let i;\n        const defaultAngle = 360 / this.countVisibleElements();\n        for(i = 0; i < start; ++i){\n            angle += this._computeAngle(i, mode, defaultAngle);\n        }\n        for(i = start; i < start + count; i++){\n            const arc = arcs[i];\n            let startAngle = angle;\n            let endAngle = angle + this._computeAngle(i, mode, defaultAngle);\n            let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(this.getParsed(i).r) : 0;\n            angle = endAngle;\n            if (reset) {\n                if (animationOpts.animateScale) {\n                    outerRadius = 0;\n                }\n                if (animationOpts.animateRotate) {\n                    startAngle = endAngle = datasetStartAngle;\n                }\n            }\n            const properties = {\n                x: centerX,\n                y: centerY,\n                innerRadius: 0,\n                outerRadius,\n                startAngle,\n                endAngle,\n                options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode)\n            };\n            this.updateElement(arc, i, properties, mode);\n        }\n    }\n    countVisibleElements() {\n        const meta = this._cachedMeta;\n        let count = 0;\n        meta.data.forEach((element, index)=>{\n            if (!isNaN(this.getParsed(index).r) && this.chart.getDataVisibility(index)) {\n                count++;\n            }\n        });\n        return count;\n    }\n _computeAngle(index, mode, defaultAngle) {\n        return this.chart.getDataVisibility(index) ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) : 0;\n    }\n}\n\nclass PieController extends DoughnutController {\n    static id = 'pie';\n static defaults = {\n        cutout: 0,\n        rotation: 0,\n        circumference: 360,\n        radius: '100%'\n    };\n}\n\nclass RadarController extends DatasetController {\n    static id = 'radar';\n static defaults = {\n        datasetElementType: 'line',\n        dataElementType: 'point',\n        indexAxis: 'r',\n        showLine: true,\n        elements: {\n            line: {\n                fill: 'start'\n            }\n        }\n    };\n static overrides = {\n        aspectRatio: 1,\n        scales: {\n            r: {\n                type: 'radialLinear'\n            }\n        }\n    };\n getLabelAndValue(index) {\n        const vScale = this._cachedMeta.vScale;\n        const parsed = this.getParsed(index);\n        return {\n            label: vScale.getLabels()[index],\n            value: '' + vScale.getLabelForValue(parsed[vScale.axis])\n        };\n    }\n    parseObjectData(meta, data, start, count) {\n        return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        const line = meta.dataset;\n        const points = meta.data || [];\n        const labels = meta.iScale.getLabels();\n        line.points = points;\n        if (mode !== 'resize') {\n            const options = this.resolveDatasetElementOptions(mode);\n            if (!this.options.showLine) {\n                options.borderWidth = 0;\n            }\n            const properties = {\n                _loop: true,\n                _fullLoop: labels.length === points.length,\n                options\n            };\n            this.updateElement(line, undefined, properties, mode);\n        }\n        this.updateElements(points, 0, points.length, mode);\n    }\n    updateElements(points, start, count, mode) {\n        const scale = this._cachedMeta.rScale;\n        const reset = mode === 'reset';\n        for(let i = start; i < start + count; i++){\n            const point = points[i];\n            const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n            const pointPosition = scale.getPointPositionForValue(i, this.getParsed(i).r);\n            const x = reset ? scale.xCenter : pointPosition.x;\n            const y = reset ? scale.yCenter : pointPosition.y;\n            const properties = {\n                x,\n                y,\n                angle: pointPosition.angle,\n                skip: isNaN(x) || isNaN(y),\n                options\n            };\n            this.updateElement(point, i, properties, mode);\n        }\n    }\n}\n\nclass ScatterController extends DatasetController {\n    static id = 'scatter';\n static defaults = {\n        datasetElementType: false,\n        dataElementType: 'point',\n        showLine: false,\n        fill: false\n    };\n static overrides = {\n        interaction: {\n            mode: 'point'\n        },\n        scales: {\n            x: {\n                type: 'linear'\n            },\n            y: {\n                type: 'linear'\n            }\n        }\n    };\n getLabelAndValue(index) {\n        const meta = this._cachedMeta;\n        const labels = this.chart.data.labels || [];\n        const { xScale , yScale  } = meta;\n        const parsed = this.getParsed(index);\n        const x = xScale.getLabelForValue(parsed.x);\n        const y = yScale.getLabelForValue(parsed.y);\n        return {\n            label: labels[index] || '',\n            value: '(' + x + ', ' + y + ')'\n        };\n    }\n    update(mode) {\n        const meta = this._cachedMeta;\n        const { data: points = []  } = meta;\n        const animationsDisabled = this.chart._animationsDisabled;\n        let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);\n        this._drawStart = start;\n        this._drawCount = count;\n        if (_scaleRangesChanged(meta)) {\n            start = 0;\n            count = points.length;\n        }\n        if (this.options.showLine) {\n            const { dataset: line , _dataset  } = meta;\n            line._chart = this.chart;\n            line._datasetIndex = this.index;\n            line._decimated = !!_dataset._decimated;\n            line.points = points;\n            const options = this.resolveDatasetElementOptions(mode);\n            options.segment = this.options.segment;\n            this.updateElement(line, undefined, {\n                animated: !animationsDisabled,\n                options\n            }, mode);\n        }\n        this.updateElements(points, start, count, mode);\n    }\n    addElements() {\n        const { showLine  } = this.options;\n        if (!this.datasetElementType && showLine) {\n            this.datasetElementType = this.chart.registry.getElement('line');\n        }\n        super.addElements();\n    }\n    updateElements(points, start, count, mode) {\n        const reset = mode === 'reset';\n        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;\n        const firstOpts = this.resolveDataElementOptions(start, mode);\n        const sharedOptions = this.getSharedOptions(firstOpts);\n        const includeOptions = this.includeOptions(mode, sharedOptions);\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        const { spanGaps , segment  } = this.options;\n        const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;\n        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';\n        let prevParsed = start > 0 && this.getParsed(start - 1);\n        for(let i = start; i < start + count; ++i){\n            const point = points[i];\n            const parsed = this.getParsed(i);\n            const properties = directUpdate ? point : {};\n            const nullData = isNullOrUndef(parsed[vAxis]);\n            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);\n            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);\n            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;\n            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;\n            if (segment) {\n                properties.parsed = parsed;\n                properties.raw = _dataset.data[i];\n            }\n            if (includeOptions) {\n                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n            }\n            if (!directUpdate) {\n                this.updateElement(point, i, properties, mode);\n            }\n            prevParsed = parsed;\n        }\n        this.updateSharedOptions(sharedOptions, mode, firstOpts);\n    }\n getMaxOverflow() {\n        const meta = this._cachedMeta;\n        const data = meta.data || [];\n        if (!this.options.showLine) {\n            let max = 0;\n            for(let i = data.length - 1; i >= 0; --i){\n                max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);\n            }\n            return max > 0 && max;\n        }\n        const dataset = meta.dataset;\n        const border = dataset.options && dataset.options.borderWidth || 0;\n        if (!data.length) {\n            return border;\n        }\n        const firstPoint = data[0].size(this.resolveDataElementOptions(0));\n        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));\n        return Math.max(border, firstPoint, lastPoint) / 2;\n    }\n}\n\nvar controllers = /*#__PURE__*/Object.freeze({\n__proto__: null,\nBarController: BarController,\nBubbleController: BubbleController,\nDoughnutController: DoughnutController,\nLineController: LineController,\nPolarAreaController: PolarAreaController,\nPieController: PieController,\nRadarController: RadarController,\nScatterController: ScatterController\n});\n\n/**\n * @namespace Chart._adapters\n * @since 2.8.0\n * @private\n */ function abstract() {\n    throw new Error('This method is not implemented: Check that a complete date adapter is provided.');\n}\n/**\n * Date adapter (current used by the time scale)\n * @namespace Chart._adapters._date\n * @memberof Chart._adapters\n * @private\n */ class DateAdapterBase {\n    /**\n   * Override default date adapter methods.\n   * Accepts type parameter to define options type.\n   * @example\n   * Chart._adapters._date.override<{myAdapterOption: string}>({\n   *   init() {\n   *     console.log(this.options.myAdapterOption);\n   *   }\n   * })\n   */ static override(members) {\n        Object.assign(DateAdapterBase.prototype, members);\n    }\n    constructor(options){\n        this.options = options || {};\n    }\n    // eslint-disable-next-line @typescript-eslint/no-empty-function\n    init() {}\n    formats() {\n        return abstract();\n    }\n    parse() {\n        return abstract();\n    }\n    format() {\n        return abstract();\n    }\n    add() {\n        return abstract();\n    }\n    diff() {\n        return abstract();\n    }\n    startOf() {\n        return abstract();\n    }\n    endOf() {\n        return abstract();\n    }\n}\nvar adapters = {\n    _date: DateAdapterBase\n};\n\nfunction binarySearch(metaset, axis, value, intersect) {\n    const { controller , data , _sorted  } = metaset;\n    const iScale = controller._cachedMeta.iScale;\n    if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) {\n        const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey;\n        if (!intersect) {\n            return lookupMethod(data, axis, value);\n        } else if (controller._sharedOptions) {\n            const el = data[0];\n            const range = typeof el.getRange === 'function' && el.getRange(axis);\n            if (range) {\n                const start = lookupMethod(data, axis, value - range);\n                const end = lookupMethod(data, axis, value + range);\n                return {\n                    lo: start.lo,\n                    hi: end.hi\n                };\n            }\n        }\n    }\n    return {\n        lo: 0,\n        hi: data.length - 1\n    };\n}\n function evaluateInteractionItems(chart, axis, position, handler, intersect) {\n    const metasets = chart.getSortedVisibleDatasetMetas();\n    const value = position[axis];\n    for(let i = 0, ilen = metasets.length; i < ilen; ++i){\n        const { index , data  } = metasets[i];\n        const { lo , hi  } = binarySearch(metasets[i], axis, value, intersect);\n        for(let j = lo; j <= hi; ++j){\n            const element = data[j];\n            if (!element.skip) {\n                handler(element, index, j);\n            }\n        }\n    }\n}\n function getDistanceMetricForAxis(axis) {\n    const useX = axis.indexOf('x') !== -1;\n    const useY = axis.indexOf('y') !== -1;\n    return function(pt1, pt2) {\n        const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;\n        const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;\n        return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));\n    };\n}\n function getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) {\n    const items = [];\n    if (!includeInvisible && !chart.isPointInArea(position)) {\n        return items;\n    }\n    const evaluationFunc = function(element, datasetIndex, index) {\n        if (!includeInvisible && !_isPointInArea(element, chart.chartArea, 0)) {\n            return;\n        }\n        if (element.inRange(position.x, position.y, useFinalPosition)) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n        }\n    };\n    evaluateInteractionItems(chart, axis, position, evaluationFunc, true);\n    return items;\n}\n function getNearestRadialItems(chart, position, axis, useFinalPosition) {\n    let items = [];\n    function evaluationFunc(element, datasetIndex, index) {\n        const { startAngle , endAngle  } = element.getProps([\n            'startAngle',\n            'endAngle'\n        ], useFinalPosition);\n        const { angle  } = getAngleFromPoint(element, {\n            x: position.x,\n            y: position.y\n        });\n        if (_angleBetween(angle, startAngle, endAngle)) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n        }\n    }\n    evaluateInteractionItems(chart, axis, position, evaluationFunc);\n    return items;\n}\n function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {\n    let items = [];\n    const distanceMetric = getDistanceMetricForAxis(axis);\n    let minDistance = Number.POSITIVE_INFINITY;\n    function evaluationFunc(element, datasetIndex, index) {\n        const inRange = element.inRange(position.x, position.y, useFinalPosition);\n        if (intersect && !inRange) {\n            return;\n        }\n        const center = element.getCenterPoint(useFinalPosition);\n        const pointInArea = !!includeInvisible || chart.isPointInArea(center);\n        if (!pointInArea && !inRange) {\n            return;\n        }\n        const distance = distanceMetric(position, center);\n        if (distance < minDistance) {\n            items = [\n                {\n                    element,\n                    datasetIndex,\n                    index\n                }\n            ];\n            minDistance = distance;\n        } else if (distance === minDistance) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n        }\n    }\n    evaluateInteractionItems(chart, axis, position, evaluationFunc);\n    return items;\n}\n function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {\n    if (!includeInvisible && !chart.isPointInArea(position)) {\n        return [];\n    }\n    return axis === 'r' && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible);\n}\n function getAxisItems(chart, position, axis, intersect, useFinalPosition) {\n    const items = [];\n    const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange';\n    let intersectsItem = false;\n    evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index)=>{\n        if (element[rangeMethod](position[axis], useFinalPosition)) {\n            items.push({\n                element,\n                datasetIndex,\n                index\n            });\n            intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition);\n        }\n    });\n    if (intersect && !intersectsItem) {\n        return [];\n    }\n    return items;\n}\n var Interaction = {\n    evaluateInteractionItems,\n    modes: {\n index (chart, e, options, useFinalPosition) {\n            const position = getRelativePosition(e, chart);\n            const axis = options.axis || 'x';\n            const includeInvisible = options.includeInvisible || false;\n            const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);\n            const elements = [];\n            if (!items.length) {\n                return [];\n            }\n            chart.getSortedVisibleDatasetMetas().forEach((meta)=>{\n                const index = items[0].index;\n                const element = meta.data[index];\n                if (element && !element.skip) {\n                    elements.push({\n                        element,\n                        datasetIndex: meta.index,\n                        index\n                    });\n                }\n            });\n            return elements;\n        },\n dataset (chart, e, options, useFinalPosition) {\n            const position = getRelativePosition(e, chart);\n            const axis = options.axis || 'xy';\n            const includeInvisible = options.includeInvisible || false;\n            let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);\n            if (items.length > 0) {\n                const datasetIndex = items[0].datasetIndex;\n                const data = chart.getDatasetMeta(datasetIndex).data;\n                items = [];\n                for(let i = 0; i < data.length; ++i){\n                    items.push({\n                        element: data[i],\n                        datasetIndex,\n                        index: i\n                    });\n                }\n            }\n            return items;\n        },\n point (chart, e, options, useFinalPosition) {\n            const position = getRelativePosition(e, chart);\n            const axis = options.axis || 'xy';\n            const includeInvisible = options.includeInvisible || false;\n            return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible);\n        },\n nearest (chart, e, options, useFinalPosition) {\n            const position = getRelativePosition(e, chart);\n            const axis = options.axis || 'xy';\n            const includeInvisible = options.includeInvisible || false;\n            return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible);\n        },\n x (chart, e, options, useFinalPosition) {\n            const position = getRelativePosition(e, chart);\n            return getAxisItems(chart, position, 'x', options.intersect, useFinalPosition);\n        },\n y (chart, e, options, useFinalPosition) {\n            const position = getRelativePosition(e, chart);\n            return getAxisItems(chart, position, 'y', options.intersect, useFinalPosition);\n        }\n    }\n};\n\nconst STATIC_POSITIONS = [\n    'left',\n    'top',\n    'right',\n    'bottom'\n];\nfunction filterByPosition(array, position) {\n    return array.filter((v)=>v.pos === position);\n}\nfunction filterDynamicPositionByAxis(array, axis) {\n    return array.filter((v)=>STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);\n}\nfunction sortByWeight(array, reverse) {\n    return array.sort((a, b)=>{\n        const v0 = reverse ? b : a;\n        const v1 = reverse ? a : b;\n        return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight;\n    });\n}\nfunction wrapBoxes(boxes) {\n    const layoutBoxes = [];\n    let i, ilen, box, pos, stack, stackWeight;\n    for(i = 0, ilen = (boxes || []).length; i < ilen; ++i){\n        box = boxes[i];\n        ({ position: pos , options: { stack , stackWeight =1  }  } = box);\n        layoutBoxes.push({\n            index: i,\n            box,\n            pos,\n            horizontal: box.isHorizontal(),\n            weight: box.weight,\n            stack: stack && pos + stack,\n            stackWeight\n        });\n    }\n    return layoutBoxes;\n}\nfunction buildStacks(layouts) {\n    const stacks = {};\n    for (const wrap of layouts){\n        const { stack , pos , stackWeight  } = wrap;\n        if (!stack || !STATIC_POSITIONS.includes(pos)) {\n            continue;\n        }\n        const _stack = stacks[stack] || (stacks[stack] = {\n            count: 0,\n            placed: 0,\n            weight: 0,\n            size: 0\n        });\n        _stack.count++;\n        _stack.weight += stackWeight;\n    }\n    return stacks;\n}\n function setLayoutDims(layouts, params) {\n    const stacks = buildStacks(layouts);\n    const { vBoxMaxWidth , hBoxMaxHeight  } = params;\n    let i, ilen, layout;\n    for(i = 0, ilen = layouts.length; i < ilen; ++i){\n        layout = layouts[i];\n        const { fullSize  } = layout.box;\n        const stack = stacks[layout.stack];\n        const factor = stack && layout.stackWeight / stack.weight;\n        if (layout.horizontal) {\n            layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth;\n            layout.height = hBoxMaxHeight;\n        } else {\n            layout.width = vBoxMaxWidth;\n            layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight;\n        }\n    }\n    return stacks;\n}\nfunction buildLayoutBoxes(boxes) {\n    const layoutBoxes = wrapBoxes(boxes);\n    const fullSize = sortByWeight(layoutBoxes.filter((wrap)=>wrap.box.fullSize), true);\n    const left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);\n    const right = sortByWeight(filterByPosition(layoutBoxes, 'right'));\n    const top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);\n    const bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));\n    const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, 'x');\n    const centerVertical = filterDynamicPositionByAxis(layoutBoxes, 'y');\n    return {\n        fullSize,\n        leftAndTop: left.concat(top),\n        rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),\n        chartArea: filterByPosition(layoutBoxes, 'chartArea'),\n        vertical: left.concat(right).concat(centerVertical),\n        horizontal: top.concat(bottom).concat(centerHorizontal)\n    };\n}\nfunction getCombinedMax(maxPadding, chartArea, a, b) {\n    return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);\n}\nfunction updateMaxPadding(maxPadding, boxPadding) {\n    maxPadding.top = Math.max(maxPadding.top, boxPadding.top);\n    maxPadding.left = Math.max(maxPadding.left, boxPadding.left);\n    maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);\n    maxPadding.right = Math.max(maxPadding.right, boxPadding.right);\n}\nfunction updateDims(chartArea, params, layout, stacks) {\n    const { pos , box  } = layout;\n    const maxPadding = chartArea.maxPadding;\n    if (!isObject(pos)) {\n        if (layout.size) {\n            chartArea[pos] -= layout.size;\n        }\n        const stack = stacks[layout.stack] || {\n            size: 0,\n            count: 1\n        };\n        stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width);\n        layout.size = stack.size / stack.count;\n        chartArea[pos] += layout.size;\n    }\n    if (box.getPadding) {\n        updateMaxPadding(maxPadding, box.getPadding());\n    }\n    const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'));\n    const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'));\n    const widthChanged = newWidth !== chartArea.w;\n    const heightChanged = newHeight !== chartArea.h;\n    chartArea.w = newWidth;\n    chartArea.h = newHeight;\n    return layout.horizontal ? {\n        same: widthChanged,\n        other: heightChanged\n    } : {\n        same: heightChanged,\n        other: widthChanged\n    };\n}\nfunction handleMaxPadding(chartArea) {\n    const maxPadding = chartArea.maxPadding;\n    function updatePos(pos) {\n        const change = Math.max(maxPadding[pos] - chartArea[pos], 0);\n        chartArea[pos] += change;\n        return change;\n    }\n    chartArea.y += updatePos('top');\n    chartArea.x += updatePos('left');\n    updatePos('right');\n    updatePos('bottom');\n}\nfunction getMargins(horizontal, chartArea) {\n    const maxPadding = chartArea.maxPadding;\n    function marginForPositions(positions) {\n        const margin = {\n            left: 0,\n            top: 0,\n            right: 0,\n            bottom: 0\n        };\n        positions.forEach((pos)=>{\n            margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);\n        });\n        return margin;\n    }\n    return horizontal ? marginForPositions([\n        'left',\n        'right'\n    ]) : marginForPositions([\n        'top',\n        'bottom'\n    ]);\n}\nfunction fitBoxes(boxes, chartArea, params, stacks) {\n    const refitBoxes = [];\n    let i, ilen, layout, box, refit, changed;\n    for(i = 0, ilen = boxes.length, refit = 0; i < ilen; ++i){\n        layout = boxes[i];\n        box = layout.box;\n        box.update(layout.width || chartArea.w, layout.height || chartArea.h, getMargins(layout.horizontal, chartArea));\n        const { same , other  } = updateDims(chartArea, params, layout, stacks);\n        refit |= same && refitBoxes.length;\n        changed = changed || other;\n        if (!box.fullSize) {\n            refitBoxes.push(layout);\n        }\n    }\n    return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed;\n}\nfunction setBoxDims(box, left, top, width, height) {\n    box.top = top;\n    box.left = left;\n    box.right = left + width;\n    box.bottom = top + height;\n    box.width = width;\n    box.height = height;\n}\nfunction placeBoxes(boxes, chartArea, params, stacks) {\n    const userPadding = params.padding;\n    let { x , y  } = chartArea;\n    for (const layout of boxes){\n        const box = layout.box;\n        const stack = stacks[layout.stack] || {\n            count: 1,\n            placed: 0,\n            weight: 1\n        };\n        const weight = layout.stackWeight / stack.weight || 1;\n        if (layout.horizontal) {\n            const width = chartArea.w * weight;\n            const height = stack.size || box.height;\n            if (defined(stack.start)) {\n                y = stack.start;\n            }\n            if (box.fullSize) {\n                setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height);\n            } else {\n                setBoxDims(box, chartArea.left + stack.placed, y, width, height);\n            }\n            stack.start = y;\n            stack.placed += width;\n            y = box.bottom;\n        } else {\n            const height1 = chartArea.h * weight;\n            const width1 = stack.size || box.width;\n            if (defined(stack.start)) {\n                x = stack.start;\n            }\n            if (box.fullSize) {\n                setBoxDims(box, x, userPadding.top, width1, params.outerHeight - userPadding.bottom - userPadding.top);\n            } else {\n                setBoxDims(box, x, chartArea.top + stack.placed, width1, height1);\n            }\n            stack.start = x;\n            stack.placed += height1;\n            x = box.right;\n        }\n    }\n    chartArea.x = x;\n    chartArea.y = y;\n}\nvar layouts = {\n addBox (chart, item) {\n        if (!chart.boxes) {\n            chart.boxes = [];\n        }\n        item.fullSize = item.fullSize || false;\n        item.position = item.position || 'top';\n        item.weight = item.weight || 0;\n        item._layers = item._layers || function() {\n            return [\n                {\n                    z: 0,\n                    draw (chartArea) {\n                        item.draw(chartArea);\n                    }\n                }\n            ];\n        };\n        chart.boxes.push(item);\n    },\n removeBox (chart, layoutItem) {\n        const index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;\n        if (index !== -1) {\n            chart.boxes.splice(index, 1);\n        }\n    },\n configure (chart, item, options) {\n        item.fullSize = options.fullSize;\n        item.position = options.position;\n        item.weight = options.weight;\n    },\n update (chart, width, height, minPadding) {\n        if (!chart) {\n            return;\n        }\n        const padding = toPadding(chart.options.layout.padding);\n        const availableWidth = Math.max(width - padding.width, 0);\n        const availableHeight = Math.max(height - padding.height, 0);\n        const boxes = buildLayoutBoxes(chart.boxes);\n        const verticalBoxes = boxes.vertical;\n        const horizontalBoxes = boxes.horizontal;\n        each(chart.boxes, (box)=>{\n            if (typeof box.beforeLayout === 'function') {\n                box.beforeLayout();\n            }\n        });\n        const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap)=>wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1;\n        const params = Object.freeze({\n            outerWidth: width,\n            outerHeight: height,\n            padding,\n            availableWidth,\n            availableHeight,\n            vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount,\n            hBoxMaxHeight: availableHeight / 2\n        });\n        const maxPadding = Object.assign({}, padding);\n        updateMaxPadding(maxPadding, toPadding(minPadding));\n        const chartArea = Object.assign({\n            maxPadding,\n            w: availableWidth,\n            h: availableHeight,\n            x: padding.left,\n            y: padding.top\n        }, padding);\n        const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);\n        fitBoxes(boxes.fullSize, chartArea, params, stacks);\n        fitBoxes(verticalBoxes, chartArea, params, stacks);\n        if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) {\n            fitBoxes(verticalBoxes, chartArea, params, stacks);\n        }\n        handleMaxPadding(chartArea);\n        placeBoxes(boxes.leftAndTop, chartArea, params, stacks);\n        chartArea.x += chartArea.w;\n        chartArea.y += chartArea.h;\n        placeBoxes(boxes.rightAndBottom, chartArea, params, stacks);\n        chart.chartArea = {\n            left: chartArea.left,\n            top: chartArea.top,\n            right: chartArea.left + chartArea.w,\n            bottom: chartArea.top + chartArea.h,\n            height: chartArea.h,\n            width: chartArea.w\n        };\n        each(boxes.chartArea, (layout)=>{\n            const box = layout.box;\n            Object.assign(box, chart.chartArea);\n            box.update(chartArea.w, chartArea.h, {\n                left: 0,\n                top: 0,\n                right: 0,\n                bottom: 0\n            });\n        });\n    }\n};\n\nclass BasePlatform {\n acquireContext(canvas, aspectRatio) {}\n releaseContext(context) {\n        return false;\n    }\n addEventListener(chart, type, listener) {}\n removeEventListener(chart, type, listener) {}\n getDevicePixelRatio() {\n        return 1;\n    }\n getMaximumSize(element, width, height, aspectRatio) {\n        width = Math.max(0, width || element.width);\n        height = height || element.height;\n        return {\n            width,\n            height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)\n        };\n    }\n isAttached(canvas) {\n        return true;\n    }\n updateConfig(config) {\n    }\n}\n\nclass BasicPlatform extends BasePlatform {\n    acquireContext(item) {\n        return item && item.getContext && item.getContext('2d') || null;\n    }\n    updateConfig(config) {\n        config.options.animation = false;\n    }\n}\n\nconst EXPANDO_KEY = '$chartjs';\n const EVENT_TYPES = {\n    touchstart: 'mousedown',\n    touchmove: 'mousemove',\n    touchend: 'mouseup',\n    pointerenter: 'mouseenter',\n    pointerdown: 'mousedown',\n    pointermove: 'mousemove',\n    pointerup: 'mouseup',\n    pointerleave: 'mouseout',\n    pointerout: 'mouseout'\n};\nconst isNullOrEmpty = (value)=>value === null || value === '';\n function initCanvas(canvas, aspectRatio) {\n    const style = canvas.style;\n    const renderHeight = canvas.getAttribute('height');\n    const renderWidth = canvas.getAttribute('width');\n    canvas[EXPANDO_KEY] = {\n        initial: {\n            height: renderHeight,\n            width: renderWidth,\n            style: {\n                display: style.display,\n                height: style.height,\n                width: style.width\n            }\n        }\n    };\n    style.display = style.display || 'block';\n    style.boxSizing = style.boxSizing || 'border-box';\n    if (isNullOrEmpty(renderWidth)) {\n        const displayWidth = readUsedSize(canvas, 'width');\n        if (displayWidth !== undefined) {\n            canvas.width = displayWidth;\n        }\n    }\n    if (isNullOrEmpty(renderHeight)) {\n        if (canvas.style.height === '') {\n            canvas.height = canvas.width / (aspectRatio || 2);\n        } else {\n            const displayHeight = readUsedSize(canvas, 'height');\n            if (displayHeight !== undefined) {\n                canvas.height = displayHeight;\n            }\n        }\n    }\n    return canvas;\n}\nconst eventListenerOptions = supportsEventListenerOptions ? {\n    passive: true\n} : false;\nfunction addListener(node, type, listener) {\n    node.addEventListener(type, listener, eventListenerOptions);\n}\nfunction removeListener(chart, type, listener) {\n    chart.canvas.removeEventListener(type, listener, eventListenerOptions);\n}\nfunction fromNativeEvent(event, chart) {\n    const type = EVENT_TYPES[event.type] || event.type;\n    const { x , y  } = getRelativePosition(event, chart);\n    return {\n        type,\n        chart,\n        native: event,\n        x: x !== undefined ? x : null,\n        y: y !== undefined ? y : null\n    };\n}\nfunction nodeListContains(nodeList, canvas) {\n    for (const node of nodeList){\n        if (node === canvas || node.contains(canvas)) {\n            return true;\n        }\n    }\n}\nfunction createAttachObserver(chart, type, listener) {\n    const canvas = chart.canvas;\n    const observer = new MutationObserver((entries)=>{\n        let trigger = false;\n        for (const entry of entries){\n            trigger = trigger || nodeListContains(entry.addedNodes, canvas);\n            trigger = trigger && !nodeListContains(entry.removedNodes, canvas);\n        }\n        if (trigger) {\n            listener();\n        }\n    });\n    observer.observe(document, {\n        childList: true,\n        subtree: true\n    });\n    return observer;\n}\nfunction createDetachObserver(chart, type, listener) {\n    const canvas = chart.canvas;\n    const observer = new MutationObserver((entries)=>{\n        let trigger = false;\n        for (const entry of entries){\n            trigger = trigger || nodeListContains(entry.removedNodes, canvas);\n            trigger = trigger && !nodeListContains(entry.addedNodes, canvas);\n        }\n        if (trigger) {\n            listener();\n        }\n    });\n    observer.observe(document, {\n        childList: true,\n        subtree: true\n    });\n    return observer;\n}\nconst drpListeningCharts = new Map();\nlet oldDevicePixelRatio = 0;\nfunction onWindowResize() {\n    const dpr = window.devicePixelRatio;\n    if (dpr === oldDevicePixelRatio) {\n        return;\n    }\n    oldDevicePixelRatio = dpr;\n    drpListeningCharts.forEach((resize, chart)=>{\n        if (chart.currentDevicePixelRatio !== dpr) {\n            resize();\n        }\n    });\n}\nfunction listenDevicePixelRatioChanges(chart, resize) {\n    if (!drpListeningCharts.size) {\n        window.addEventListener('resize', onWindowResize);\n    }\n    drpListeningCharts.set(chart, resize);\n}\nfunction unlistenDevicePixelRatioChanges(chart) {\n    drpListeningCharts.delete(chart);\n    if (!drpListeningCharts.size) {\n        window.removeEventListener('resize', onWindowResize);\n    }\n}\nfunction createResizeObserver(chart, type, listener) {\n    const canvas = chart.canvas;\n    const container = canvas && _getParentNode(canvas);\n    if (!container) {\n        return;\n    }\n    const resize = throttled((width, height)=>{\n        const w = container.clientWidth;\n        listener(width, height);\n        if (w < container.clientWidth) {\n            listener();\n        }\n    }, window);\n    const observer = new ResizeObserver((entries)=>{\n        const entry = entries[0];\n        const width = entry.contentRect.width;\n        const height = entry.contentRect.height;\n        if (width === 0 && height === 0) {\n            return;\n        }\n        resize(width, height);\n    });\n    observer.observe(container);\n    listenDevicePixelRatioChanges(chart, resize);\n    return observer;\n}\nfunction releaseObserver(chart, type, observer) {\n    if (observer) {\n        observer.disconnect();\n    }\n    if (type === 'resize') {\n        unlistenDevicePixelRatioChanges(chart);\n    }\n}\nfunction createProxyAndListen(chart, type, listener) {\n    const canvas = chart.canvas;\n    const proxy = throttled((event)=>{\n        if (chart.ctx !== null) {\n            listener(fromNativeEvent(event, chart));\n        }\n    }, chart);\n    addListener(canvas, type, proxy);\n    return proxy;\n}\n class DomPlatform extends BasePlatform {\n acquireContext(canvas, aspectRatio) {\n        const context = canvas && canvas.getContext && canvas.getContext('2d');\n        if (context && context.canvas === canvas) {\n            initCanvas(canvas, aspectRatio);\n            return context;\n        }\n        return null;\n    }\n releaseContext(context) {\n        const canvas = context.canvas;\n        if (!canvas[EXPANDO_KEY]) {\n            return false;\n        }\n        const initial = canvas[EXPANDO_KEY].initial;\n        [\n            'height',\n            'width'\n        ].forEach((prop)=>{\n            const value = initial[prop];\n            if (isNullOrUndef(value)) {\n                canvas.removeAttribute(prop);\n            } else {\n                canvas.setAttribute(prop, value);\n            }\n        });\n        const style = initial.style || {};\n        Object.keys(style).forEach((key)=>{\n            canvas.style[key] = style[key];\n        });\n        canvas.width = canvas.width;\n        delete canvas[EXPANDO_KEY];\n        return true;\n    }\n addEventListener(chart, type, listener) {\n        this.removeEventListener(chart, type);\n        const proxies = chart.$proxies || (chart.$proxies = {});\n        const handlers = {\n            attach: createAttachObserver,\n            detach: createDetachObserver,\n            resize: createResizeObserver\n        };\n        const handler = handlers[type] || createProxyAndListen;\n        proxies[type] = handler(chart, type, listener);\n    }\n removeEventListener(chart, type) {\n        const proxies = chart.$proxies || (chart.$proxies = {});\n        const proxy = proxies[type];\n        if (!proxy) {\n            return;\n        }\n        const handlers = {\n            attach: releaseObserver,\n            detach: releaseObserver,\n            resize: releaseObserver\n        };\n        const handler = handlers[type] || removeListener;\n        handler(chart, type, proxy);\n        proxies[type] = undefined;\n    }\n    getDevicePixelRatio() {\n        return window.devicePixelRatio;\n    }\n getMaximumSize(canvas, width, height, aspectRatio) {\n        return getMaximumSize(canvas, width, height, aspectRatio);\n    }\n isAttached(canvas) {\n        const container = _getParentNode(canvas);\n        return !!(container && container.isConnected);\n    }\n}\n\nfunction _detectPlatform(canvas) {\n    if (!_isDomSupported() || typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {\n        return BasicPlatform;\n    }\n    return DomPlatform;\n}\n\nclass Element {\n    static defaults = {};\n    static defaultRoutes = undefined;\n    active = false;\n    tooltipPosition(useFinalPosition) {\n        const { x , y  } = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        return {\n            x,\n            y\n        };\n    }\n    hasValue() {\n        return isNumber(this.x) && isNumber(this.y);\n    }\n    getProps(props, final) {\n        const anims = this.$animations;\n        if (!final || !anims) {\n            // let's not create an object, if not needed\n            return this;\n        }\n        const ret = {};\n        props.forEach((prop)=>{\n            ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];\n        });\n        return ret;\n    }\n}\n\nfunction autoSkip(scale, ticks) {\n    const tickOpts = scale.options.ticks;\n    const determinedMaxTicks = determineMaxTicks(scale);\n    const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks);\n    const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];\n    const numMajorIndices = majorIndices.length;\n    const first = majorIndices[0];\n    const last = majorIndices[numMajorIndices - 1];\n    const newTicks = [];\n    if (numMajorIndices > ticksLimit) {\n        skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);\n        return newTicks;\n    }\n    const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);\n    if (numMajorIndices > 0) {\n        let i, ilen;\n        const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;\n        skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);\n        for(i = 0, ilen = numMajorIndices - 1; i < ilen; i++){\n            skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);\n        }\n        skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);\n        return newTicks;\n    }\n    skip(ticks, newTicks, spacing);\n    return newTicks;\n}\nfunction determineMaxTicks(scale) {\n    const offset = scale.options.offset;\n    const tickLength = scale._tickSize();\n    const maxScale = scale._length / tickLength + (offset ? 0 : 1);\n    const maxChart = scale._maxLength / tickLength;\n    return Math.floor(Math.min(maxScale, maxChart));\n}\n function calculateSpacing(majorIndices, ticks, ticksLimit) {\n    const evenMajorSpacing = getEvenSpacing(majorIndices);\n    const spacing = ticks.length / ticksLimit;\n    if (!evenMajorSpacing) {\n        return Math.max(spacing, 1);\n    }\n    const factors = _factorize(evenMajorSpacing);\n    for(let i = 0, ilen = factors.length - 1; i < ilen; i++){\n        const factor = factors[i];\n        if (factor > spacing) {\n            return factor;\n        }\n    }\n    return Math.max(spacing, 1);\n}\n function getMajorIndices(ticks) {\n    const result = [];\n    let i, ilen;\n    for(i = 0, ilen = ticks.length; i < ilen; i++){\n        if (ticks[i].major) {\n            result.push(i);\n        }\n    }\n    return result;\n}\n function skipMajors(ticks, newTicks, majorIndices, spacing) {\n    let count = 0;\n    let next = majorIndices[0];\n    let i;\n    spacing = Math.ceil(spacing);\n    for(i = 0; i < ticks.length; i++){\n        if (i === next) {\n            newTicks.push(ticks[i]);\n            count++;\n            next = majorIndices[count * spacing];\n        }\n    }\n}\n function skip(ticks, newTicks, spacing, majorStart, majorEnd) {\n    const start = valueOrDefault(majorStart, 0);\n    const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);\n    let count = 0;\n    let length, i, next;\n    spacing = Math.ceil(spacing);\n    if (majorEnd) {\n        length = majorEnd - majorStart;\n        spacing = length / Math.floor(length / spacing);\n    }\n    next = start;\n    while(next < 0){\n        count++;\n        next = Math.round(start + count * spacing);\n    }\n    for(i = Math.max(start, 0); i < end; i++){\n        if (i === next) {\n            newTicks.push(ticks[i]);\n            count++;\n            next = Math.round(start + count * spacing);\n        }\n    }\n}\n function getEvenSpacing(arr) {\n    const len = arr.length;\n    let i, diff;\n    if (len < 2) {\n        return false;\n    }\n    for(diff = arr[0], i = 1; i < len; ++i){\n        if (arr[i] - arr[i - 1] !== diff) {\n            return false;\n        }\n    }\n    return diff;\n}\n\nconst reverseAlign = (align)=>align === 'left' ? 'right' : align === 'right' ? 'left' : align;\nconst offsetFromEdge = (scale, edge, offset)=>edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset;\nconst getTicksLimit = (ticksLength, maxTicksLimit)=>Math.min(maxTicksLimit || ticksLength, ticksLength);\n function sample(arr, numItems) {\n    const result = [];\n    const increment = arr.length / numItems;\n    const len = arr.length;\n    let i = 0;\n    for(; i < len; i += increment){\n        result.push(arr[Math.floor(i)]);\n    }\n    return result;\n}\n function getPixelForGridLine(scale, index, offsetGridLines) {\n    const length = scale.ticks.length;\n    const validIndex = Math.min(index, length - 1);\n    const start = scale._startPixel;\n    const end = scale._endPixel;\n    const epsilon = 1e-6;\n    let lineValue = scale.getPixelForTick(validIndex);\n    let offset;\n    if (offsetGridLines) {\n        if (length === 1) {\n            offset = Math.max(lineValue - start, end - lineValue);\n        } else if (index === 0) {\n            offset = (scale.getPixelForTick(1) - lineValue) / 2;\n        } else {\n            offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;\n        }\n        lineValue += validIndex < index ? offset : -offset;\n        if (lineValue < start - epsilon || lineValue > end + epsilon) {\n            return;\n        }\n    }\n    return lineValue;\n}\n function garbageCollect(caches, length) {\n    each(caches, (cache)=>{\n        const gc = cache.gc;\n        const gcLen = gc.length / 2;\n        let i;\n        if (gcLen > length) {\n            for(i = 0; i < gcLen; ++i){\n                delete cache.data[gc[i]];\n            }\n            gc.splice(0, gcLen);\n        }\n    });\n}\n function getTickMarkLength(options) {\n    return options.drawTicks ? options.tickLength : 0;\n}\n function getTitleHeight(options, fallback) {\n    if (!options.display) {\n        return 0;\n    }\n    const font = toFont(options.font, fallback);\n    const padding = toPadding(options.padding);\n    const lines = isArray(options.text) ? options.text.length : 1;\n    return lines * font.lineHeight + padding.height;\n}\nfunction createScaleContext(parent, scale) {\n    return createContext(parent, {\n        scale,\n        type: 'scale'\n    });\n}\nfunction createTickContext(parent, index, tick) {\n    return createContext(parent, {\n        tick,\n        index,\n        type: 'tick'\n    });\n}\nfunction titleAlign(align, position, reverse) {\n    let ret = _toLeftRightCenter(align);\n    if (reverse && position !== 'right' || !reverse && position === 'right') {\n        ret = reverseAlign(ret);\n    }\n    return ret;\n}\nfunction titleArgs(scale, offset, position, align) {\n    const { top , left , bottom , right , chart  } = scale;\n    const { chartArea , scales  } = chart;\n    let rotation = 0;\n    let maxWidth, titleX, titleY;\n    const height = bottom - top;\n    const width = right - left;\n    if (scale.isHorizontal()) {\n        titleX = _alignStartEnd(align, left, right);\n        if (isObject(position)) {\n            const positionAxisID = Object.keys(position)[0];\n            const value = position[positionAxisID];\n            titleY = scales[positionAxisID].getPixelForValue(value) + height - offset;\n        } else if (position === 'center') {\n            titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset;\n        } else {\n            titleY = offsetFromEdge(scale, position, offset);\n        }\n        maxWidth = right - left;\n    } else {\n        if (isObject(position)) {\n            const positionAxisID1 = Object.keys(position)[0];\n            const value1 = position[positionAxisID1];\n            titleX = scales[positionAxisID1].getPixelForValue(value1) - width + offset;\n        } else if (position === 'center') {\n            titleX = (chartArea.left + chartArea.right) / 2 - width + offset;\n        } else {\n            titleX = offsetFromEdge(scale, position, offset);\n        }\n        titleY = _alignStartEnd(align, bottom, top);\n        rotation = position === 'left' ? -HALF_PI : HALF_PI;\n    }\n    return {\n        titleX,\n        titleY,\n        maxWidth,\n        rotation\n    };\n}\nclass Scale extends Element {\n    constructor(cfg){\n        super();\n         this.id = cfg.id;\n         this.type = cfg.type;\n         this.options = undefined;\n         this.ctx = cfg.ctx;\n         this.chart = cfg.chart;\n         this.top = undefined;\n         this.bottom = undefined;\n         this.left = undefined;\n         this.right = undefined;\n         this.width = undefined;\n         this.height = undefined;\n        this._margins = {\n            left: 0,\n            right: 0,\n            top: 0,\n            bottom: 0\n        };\n         this.maxWidth = undefined;\n         this.maxHeight = undefined;\n         this.paddingTop = undefined;\n         this.paddingBottom = undefined;\n         this.paddingLeft = undefined;\n         this.paddingRight = undefined;\n         this.axis = undefined;\n         this.labelRotation = undefined;\n        this.min = undefined;\n        this.max = undefined;\n        this._range = undefined;\n         this.ticks = [];\n         this._gridLineItems = null;\n         this._labelItems = null;\n         this._labelSizes = null;\n        this._length = 0;\n        this._maxLength = 0;\n        this._longestTextCache = {};\n         this._startPixel = undefined;\n         this._endPixel = undefined;\n        this._reversePixels = false;\n        this._userMax = undefined;\n        this._userMin = undefined;\n        this._suggestedMax = undefined;\n        this._suggestedMin = undefined;\n        this._ticksLength = 0;\n        this._borderValue = 0;\n        this._cache = {};\n        this._dataLimitsCached = false;\n        this.$context = undefined;\n    }\n init(options) {\n        this.options = options.setContext(this.getContext());\n        this.axis = options.axis;\n        this._userMin = this.parse(options.min);\n        this._userMax = this.parse(options.max);\n        this._suggestedMin = this.parse(options.suggestedMin);\n        this._suggestedMax = this.parse(options.suggestedMax);\n    }\n parse(raw, index) {\n        return raw;\n    }\n getUserBounds() {\n        let { _userMin , _userMax , _suggestedMin , _suggestedMax  } = this;\n        _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);\n        _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);\n        _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);\n        _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);\n        return {\n            min: finiteOrDefault(_userMin, _suggestedMin),\n            max: finiteOrDefault(_userMax, _suggestedMax),\n            minDefined: isNumberFinite(_userMin),\n            maxDefined: isNumberFinite(_userMax)\n        };\n    }\n getMinMax(canStack) {\n        let { min , max , minDefined , maxDefined  } = this.getUserBounds();\n        let range;\n        if (minDefined && maxDefined) {\n            return {\n                min,\n                max\n            };\n        }\n        const metas = this.getMatchingVisibleMetas();\n        for(let i = 0, ilen = metas.length; i < ilen; ++i){\n            range = metas[i].controller.getMinMax(this, canStack);\n            if (!minDefined) {\n                min = Math.min(min, range.min);\n            }\n            if (!maxDefined) {\n                max = Math.max(max, range.max);\n            }\n        }\n        min = maxDefined && min > max ? max : min;\n        max = minDefined && min > max ? min : max;\n        return {\n            min: finiteOrDefault(min, finiteOrDefault(max, min)),\n            max: finiteOrDefault(max, finiteOrDefault(min, max))\n        };\n    }\n getPadding() {\n        return {\n            left: this.paddingLeft || 0,\n            top: this.paddingTop || 0,\n            right: this.paddingRight || 0,\n            bottom: this.paddingBottom || 0\n        };\n    }\n getTicks() {\n        return this.ticks;\n    }\n getLabels() {\n        const data = this.chart.data;\n        return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];\n    }\n getLabelItems(chartArea = this.chart.chartArea) {\n        const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea));\n        return items;\n    }\n    beforeLayout() {\n        this._cache = {};\n        this._dataLimitsCached = false;\n    }\n    beforeUpdate() {\n        callback(this.options.beforeUpdate, [\n            this\n        ]);\n    }\n update(maxWidth, maxHeight, margins) {\n        const { beginAtZero , grace , ticks: tickOpts  } = this.options;\n        const sampleSize = tickOpts.sampleSize;\n        this.beforeUpdate();\n        this.maxWidth = maxWidth;\n        this.maxHeight = maxHeight;\n        this._margins = margins = Object.assign({\n            left: 0,\n            right: 0,\n            top: 0,\n            bottom: 0\n        }, margins);\n        this.ticks = null;\n        this._labelSizes = null;\n        this._gridLineItems = null;\n        this._labelItems = null;\n        this.beforeSetDimensions();\n        this.setDimensions();\n        this.afterSetDimensions();\n        this._maxLength = this.isHorizontal() ? this.width + margins.left + margins.right : this.height + margins.top + margins.bottom;\n        if (!this._dataLimitsCached) {\n            this.beforeDataLimits();\n            this.determineDataLimits();\n            this.afterDataLimits();\n            this._range = _addGrace(this, grace, beginAtZero);\n            this._dataLimitsCached = true;\n        }\n        this.beforeBuildTicks();\n        this.ticks = this.buildTicks() || [];\n        this.afterBuildTicks();\n        const samplingEnabled = sampleSize < this.ticks.length;\n        this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks);\n        this.configure();\n        this.beforeCalculateLabelRotation();\n        this.calculateLabelRotation();\n        this.afterCalculateLabelRotation();\n        if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {\n            this.ticks = autoSkip(this, this.ticks);\n            this._labelSizes = null;\n            this.afterAutoSkip();\n        }\n        if (samplingEnabled) {\n            this._convertTicksToLabels(this.ticks);\n        }\n        this.beforeFit();\n        this.fit();\n        this.afterFit();\n        this.afterUpdate();\n    }\n configure() {\n        let reversePixels = this.options.reverse;\n        let startPixel, endPixel;\n        if (this.isHorizontal()) {\n            startPixel = this.left;\n            endPixel = this.right;\n        } else {\n            startPixel = this.top;\n            endPixel = this.bottom;\n            reversePixels = !reversePixels;\n        }\n        this._startPixel = startPixel;\n        this._endPixel = endPixel;\n        this._reversePixels = reversePixels;\n        this._length = endPixel - startPixel;\n        this._alignToPixels = this.options.alignToPixels;\n    }\n    afterUpdate() {\n        callback(this.options.afterUpdate, [\n            this\n        ]);\n    }\n    beforeSetDimensions() {\n        callback(this.options.beforeSetDimensions, [\n            this\n        ]);\n    }\n    setDimensions() {\n        if (this.isHorizontal()) {\n            this.width = this.maxWidth;\n            this.left = 0;\n            this.right = this.width;\n        } else {\n            this.height = this.maxHeight;\n            this.top = 0;\n            this.bottom = this.height;\n        }\n        this.paddingLeft = 0;\n        this.paddingTop = 0;\n        this.paddingRight = 0;\n        this.paddingBottom = 0;\n    }\n    afterSetDimensions() {\n        callback(this.options.afterSetDimensions, [\n            this\n        ]);\n    }\n    _callHooks(name) {\n        this.chart.notifyPlugins(name, this.getContext());\n        callback(this.options[name], [\n            this\n        ]);\n    }\n    beforeDataLimits() {\n        this._callHooks('beforeDataLimits');\n    }\n    determineDataLimits() {}\n    afterDataLimits() {\n        this._callHooks('afterDataLimits');\n    }\n    beforeBuildTicks() {\n        this._callHooks('beforeBuildTicks');\n    }\n buildTicks() {\n        return [];\n    }\n    afterBuildTicks() {\n        this._callHooks('afterBuildTicks');\n    }\n    beforeTickToLabelConversion() {\n        callback(this.options.beforeTickToLabelConversion, [\n            this\n        ]);\n    }\n generateTickLabels(ticks) {\n        const tickOpts = this.options.ticks;\n        let i, ilen, tick;\n        for(i = 0, ilen = ticks.length; i < ilen; i++){\n            tick = ticks[i];\n            tick.label = callback(tickOpts.callback, [\n                tick.value,\n                i,\n                ticks\n            ], this);\n        }\n    }\n    afterTickToLabelConversion() {\n        callback(this.options.afterTickToLabelConversion, [\n            this\n        ]);\n    }\n    beforeCalculateLabelRotation() {\n        callback(this.options.beforeCalculateLabelRotation, [\n            this\n        ]);\n    }\n    calculateLabelRotation() {\n        const options = this.options;\n        const tickOpts = options.ticks;\n        const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit);\n        const minRotation = tickOpts.minRotation || 0;\n        const maxRotation = tickOpts.maxRotation;\n        let labelRotation = minRotation;\n        let tickWidth, maxHeight, maxLabelDiagonal;\n        if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) {\n            this.labelRotation = minRotation;\n            return;\n        }\n        const labelSizes = this._getLabelSizes();\n        const maxLabelWidth = labelSizes.widest.width;\n        const maxLabelHeight = labelSizes.highest.height;\n        const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth);\n        tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1);\n        if (maxLabelWidth + 6 > tickWidth) {\n            tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));\n            maxHeight = this.maxHeight - getTickMarkLength(options.grid) - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font);\n            maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);\n            labelRotation = toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)), Math.asin(_limitValue(maxHeight / maxLabelDiagonal, -1, 1)) - Math.asin(_limitValue(maxLabelHeight / maxLabelDiagonal, -1, 1))));\n            labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));\n        }\n        this.labelRotation = labelRotation;\n    }\n    afterCalculateLabelRotation() {\n        callback(this.options.afterCalculateLabelRotation, [\n            this\n        ]);\n    }\n    afterAutoSkip() {}\n    beforeFit() {\n        callback(this.options.beforeFit, [\n            this\n        ]);\n    }\n    fit() {\n        const minSize = {\n            width: 0,\n            height: 0\n        };\n        const { chart , options: { ticks: tickOpts , title: titleOpts , grid: gridOpts  }  } = this;\n        const display = this._isVisible();\n        const isHorizontal = this.isHorizontal();\n        if (display) {\n            const titleHeight = getTitleHeight(titleOpts, chart.options.font);\n            if (isHorizontal) {\n                minSize.width = this.maxWidth;\n                minSize.height = getTickMarkLength(gridOpts) + titleHeight;\n            } else {\n                minSize.height = this.maxHeight;\n                minSize.width = getTickMarkLength(gridOpts) + titleHeight;\n            }\n            if (tickOpts.display && this.ticks.length) {\n                const { first , last , widest , highest  } = this._getLabelSizes();\n                const tickPadding = tickOpts.padding * 2;\n                const angleRadians = toRadians(this.labelRotation);\n                const cos = Math.cos(angleRadians);\n                const sin = Math.sin(angleRadians);\n                if (isHorizontal) {\n                    const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;\n                    minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding);\n                } else {\n                    const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;\n                    minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding);\n                }\n                this._calculatePadding(first, last, sin, cos);\n            }\n        }\n        this._handleMargins();\n        if (isHorizontal) {\n            this.width = this._length = chart.width - this._margins.left - this._margins.right;\n            this.height = minSize.height;\n        } else {\n            this.width = minSize.width;\n            this.height = this._length = chart.height - this._margins.top - this._margins.bottom;\n        }\n    }\n    _calculatePadding(first, last, sin, cos) {\n        const { ticks: { align , padding  } , position  } = this.options;\n        const isRotated = this.labelRotation !== 0;\n        const labelsBelowTicks = position !== 'top' && this.axis === 'x';\n        if (this.isHorizontal()) {\n            const offsetLeft = this.getPixelForTick(0) - this.left;\n            const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1);\n            let paddingLeft = 0;\n            let paddingRight = 0;\n            if (isRotated) {\n                if (labelsBelowTicks) {\n                    paddingLeft = cos * first.width;\n                    paddingRight = sin * last.height;\n                } else {\n                    paddingLeft = sin * first.height;\n                    paddingRight = cos * last.width;\n                }\n            } else if (align === 'start') {\n                paddingRight = last.width;\n            } else if (align === 'end') {\n                paddingLeft = first.width;\n            } else if (align !== 'inner') {\n                paddingLeft = first.width / 2;\n                paddingRight = last.width / 2;\n            }\n            this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0);\n            this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0);\n        } else {\n            let paddingTop = last.height / 2;\n            let paddingBottom = first.height / 2;\n            if (align === 'start') {\n                paddingTop = 0;\n                paddingBottom = first.height;\n            } else if (align === 'end') {\n                paddingTop = last.height;\n                paddingBottom = 0;\n            }\n            this.paddingTop = paddingTop + padding;\n            this.paddingBottom = paddingBottom + padding;\n        }\n    }\n _handleMargins() {\n        if (this._margins) {\n            this._margins.left = Math.max(this.paddingLeft, this._margins.left);\n            this._margins.top = Math.max(this.paddingTop, this._margins.top);\n            this._margins.right = Math.max(this.paddingRight, this._margins.right);\n            this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom);\n        }\n    }\n    afterFit() {\n        callback(this.options.afterFit, [\n            this\n        ]);\n    }\n isHorizontal() {\n        const { axis , position  } = this.options;\n        return position === 'top' || position === 'bottom' || axis === 'x';\n    }\n isFullSize() {\n        return this.options.fullSize;\n    }\n _convertTicksToLabels(ticks) {\n        this.beforeTickToLabelConversion();\n        this.generateTickLabels(ticks);\n        let i, ilen;\n        for(i = 0, ilen = ticks.length; i < ilen; i++){\n            if (isNullOrUndef(ticks[i].label)) {\n                ticks.splice(i, 1);\n                ilen--;\n                i--;\n            }\n        }\n        this.afterTickToLabelConversion();\n    }\n _getLabelSizes() {\n        let labelSizes = this._labelSizes;\n        if (!labelSizes) {\n            const sampleSize = this.options.ticks.sampleSize;\n            let ticks = this.ticks;\n            if (sampleSize < ticks.length) {\n                ticks = sample(ticks, sampleSize);\n            }\n            this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit);\n        }\n        return labelSizes;\n    }\n _computeLabelSizes(ticks, length, maxTicksLimit) {\n        const { ctx , _longestTextCache: caches  } = this;\n        const widths = [];\n        const heights = [];\n        const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit));\n        let widestLabelSize = 0;\n        let highestLabelSize = 0;\n        let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel;\n        for(i = 0; i < length; i += increment){\n            label = ticks[i].label;\n            tickFont = this._resolveTickFontOptions(i);\n            ctx.font = fontString = tickFont.string;\n            cache = caches[fontString] = caches[fontString] || {\n                data: {},\n                gc: []\n            };\n            lineHeight = tickFont.lineHeight;\n            width = height = 0;\n            if (!isNullOrUndef(label) && !isArray(label)) {\n                width = _measureText(ctx, cache.data, cache.gc, width, label);\n                height = lineHeight;\n            } else if (isArray(label)) {\n                for(j = 0, jlen = label.length; j < jlen; ++j){\n                    nestedLabel = label[j];\n                    if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {\n                        width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);\n                        height += lineHeight;\n                    }\n                }\n            }\n            widths.push(width);\n            heights.push(height);\n            widestLabelSize = Math.max(width, widestLabelSize);\n            highestLabelSize = Math.max(height, highestLabelSize);\n        }\n        garbageCollect(caches, length);\n        const widest = widths.indexOf(widestLabelSize);\n        const highest = heights.indexOf(highestLabelSize);\n        const valueAt = (idx)=>({\n                width: widths[idx] || 0,\n                height: heights[idx] || 0\n            });\n        return {\n            first: valueAt(0),\n            last: valueAt(length - 1),\n            widest: valueAt(widest),\n            highest: valueAt(highest),\n            widths,\n            heights\n        };\n    }\n getLabelForValue(value) {\n        return value;\n    }\n getPixelForValue(value, index) {\n        return NaN;\n    }\n getValueForPixel(pixel) {}\n getPixelForTick(index) {\n        const ticks = this.ticks;\n        if (index < 0 || index > ticks.length - 1) {\n            return null;\n        }\n        return this.getPixelForValue(ticks[index].value);\n    }\n getPixelForDecimal(decimal) {\n        if (this._reversePixels) {\n            decimal = 1 - decimal;\n        }\n        const pixel = this._startPixel + decimal * this._length;\n        return _int16Range(this._alignToPixels ? _alignPixel(this.chart, pixel, 0) : pixel);\n    }\n getDecimalForPixel(pixel) {\n        const decimal = (pixel - this._startPixel) / this._length;\n        return this._reversePixels ? 1 - decimal : decimal;\n    }\n getBasePixel() {\n        return this.getPixelForValue(this.getBaseValue());\n    }\n getBaseValue() {\n        const { min , max  } = this;\n        return min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0;\n    }\n getContext(index) {\n        const ticks = this.ticks || [];\n        if (index >= 0 && index < ticks.length) {\n            const tick = ticks[index];\n            return tick.$context || (tick.$context = createTickContext(this.getContext(), index, tick));\n        }\n        return this.$context || (this.$context = createScaleContext(this.chart.getContext(), this));\n    }\n _tickSize() {\n        const optionTicks = this.options.ticks;\n        const rot = toRadians(this.labelRotation);\n        const cos = Math.abs(Math.cos(rot));\n        const sin = Math.abs(Math.sin(rot));\n        const labelSizes = this._getLabelSizes();\n        const padding = optionTicks.autoSkipPadding || 0;\n        const w = labelSizes ? labelSizes.widest.width + padding : 0;\n        const h = labelSizes ? labelSizes.highest.height + padding : 0;\n        return this.isHorizontal() ? h * cos > w * sin ? w / cos : h / sin : h * sin < w * cos ? h / cos : w / sin;\n    }\n _isVisible() {\n        const display = this.options.display;\n        if (display !== 'auto') {\n            return !!display;\n        }\n        return this.getMatchingVisibleMetas().length > 0;\n    }\n _computeGridLineItems(chartArea) {\n        const axis = this.axis;\n        const chart = this.chart;\n        const options = this.options;\n        const { grid , position , border  } = options;\n        const offset = grid.offset;\n        const isHorizontal = this.isHorizontal();\n        const ticks = this.ticks;\n        const ticksLength = ticks.length + (offset ? 1 : 0);\n        const tl = getTickMarkLength(grid);\n        const items = [];\n        const borderOpts = border.setContext(this.getContext());\n        const axisWidth = borderOpts.display ? borderOpts.width : 0;\n        const axisHalfWidth = axisWidth / 2;\n        const alignBorderValue = function(pixel) {\n            return _alignPixel(chart, pixel, axisWidth);\n        };\n        let borderValue, i, lineValue, alignedLineValue;\n        let tx1, ty1, tx2, ty2, x1, y1, x2, y2;\n        if (position === 'top') {\n            borderValue = alignBorderValue(this.bottom);\n            ty1 = this.bottom - tl;\n            ty2 = borderValue - axisHalfWidth;\n            y1 = alignBorderValue(chartArea.top) + axisHalfWidth;\n            y2 = chartArea.bottom;\n        } else if (position === 'bottom') {\n            borderValue = alignBorderValue(this.top);\n            y1 = chartArea.top;\n            y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;\n            ty1 = borderValue + axisHalfWidth;\n            ty2 = this.top + tl;\n        } else if (position === 'left') {\n            borderValue = alignBorderValue(this.right);\n            tx1 = this.right - tl;\n            tx2 = borderValue - axisHalfWidth;\n            x1 = alignBorderValue(chartArea.left) + axisHalfWidth;\n            x2 = chartArea.right;\n        } else if (position === 'right') {\n            borderValue = alignBorderValue(this.left);\n            x1 = chartArea.left;\n            x2 = alignBorderValue(chartArea.right) - axisHalfWidth;\n            tx1 = borderValue + axisHalfWidth;\n            tx2 = this.left + tl;\n        } else if (axis === 'x') {\n            if (position === 'center') {\n                borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);\n            } else if (isObject(position)) {\n                const positionAxisID = Object.keys(position)[0];\n                const value = position[positionAxisID];\n                borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));\n            }\n            y1 = chartArea.top;\n            y2 = chartArea.bottom;\n            ty1 = borderValue + axisHalfWidth;\n            ty2 = ty1 + tl;\n        } else if (axis === 'y') {\n            if (position === 'center') {\n                borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2);\n            } else if (isObject(position)) {\n                const positionAxisID1 = Object.keys(position)[0];\n                const value1 = position[positionAxisID1];\n                borderValue = alignBorderValue(this.chart.scales[positionAxisID1].getPixelForValue(value1));\n            }\n            tx1 = borderValue - axisHalfWidth;\n            tx2 = tx1 - tl;\n            x1 = chartArea.left;\n            x2 = chartArea.right;\n        }\n        const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength);\n        const step = Math.max(1, Math.ceil(ticksLength / limit));\n        for(i = 0; i < ticksLength; i += step){\n            const context = this.getContext(i);\n            const optsAtIndex = grid.setContext(context);\n            const optsAtIndexBorder = border.setContext(context);\n            const lineWidth = optsAtIndex.lineWidth;\n            const lineColor = optsAtIndex.color;\n            const borderDash = optsAtIndexBorder.dash || [];\n            const borderDashOffset = optsAtIndexBorder.dashOffset;\n            const tickWidth = optsAtIndex.tickWidth;\n            const tickColor = optsAtIndex.tickColor;\n            const tickBorderDash = optsAtIndex.tickBorderDash || [];\n            const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;\n            lineValue = getPixelForGridLine(this, i, offset);\n            if (lineValue === undefined) {\n                continue;\n            }\n            alignedLineValue = _alignPixel(chart, lineValue, lineWidth);\n            if (isHorizontal) {\n                tx1 = tx2 = x1 = x2 = alignedLineValue;\n            } else {\n                ty1 = ty2 = y1 = y2 = alignedLineValue;\n            }\n            items.push({\n                tx1,\n                ty1,\n                tx2,\n                ty2,\n                x1,\n                y1,\n                x2,\n                y2,\n                width: lineWidth,\n                color: lineColor,\n                borderDash,\n                borderDashOffset,\n                tickWidth,\n                tickColor,\n                tickBorderDash,\n                tickBorderDashOffset\n            });\n        }\n        this._ticksLength = ticksLength;\n        this._borderValue = borderValue;\n        return items;\n    }\n _computeLabelItems(chartArea) {\n        const axis = this.axis;\n        const options = this.options;\n        const { position , ticks: optionTicks  } = options;\n        const isHorizontal = this.isHorizontal();\n        const ticks = this.ticks;\n        const { align , crossAlign , padding , mirror  } = optionTicks;\n        const tl = getTickMarkLength(options.grid);\n        const tickAndPadding = tl + padding;\n        const hTickAndPadding = mirror ? -padding : tickAndPadding;\n        const rotation = -toRadians(this.labelRotation);\n        const items = [];\n        let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;\n        let textBaseline = 'middle';\n        if (position === 'top') {\n            y = this.bottom - hTickAndPadding;\n            textAlign = this._getXAxisLabelAlignment();\n        } else if (position === 'bottom') {\n            y = this.top + hTickAndPadding;\n            textAlign = this._getXAxisLabelAlignment();\n        } else if (position === 'left') {\n            const ret = this._getYAxisLabelAlignment(tl);\n            textAlign = ret.textAlign;\n            x = ret.x;\n        } else if (position === 'right') {\n            const ret1 = this._getYAxisLabelAlignment(tl);\n            textAlign = ret1.textAlign;\n            x = ret1.x;\n        } else if (axis === 'x') {\n            if (position === 'center') {\n                y = (chartArea.top + chartArea.bottom) / 2 + tickAndPadding;\n            } else if (isObject(position)) {\n                const positionAxisID = Object.keys(position)[0];\n                const value = position[positionAxisID];\n                y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;\n            }\n            textAlign = this._getXAxisLabelAlignment();\n        } else if (axis === 'y') {\n            if (position === 'center') {\n                x = (chartArea.left + chartArea.right) / 2 - tickAndPadding;\n            } else if (isObject(position)) {\n                const positionAxisID1 = Object.keys(position)[0];\n                const value1 = position[positionAxisID1];\n                x = this.chart.scales[positionAxisID1].getPixelForValue(value1);\n            }\n            textAlign = this._getYAxisLabelAlignment(tl).textAlign;\n        }\n        if (axis === 'y') {\n            if (align === 'start') {\n                textBaseline = 'top';\n            } else if (align === 'end') {\n                textBaseline = 'bottom';\n            }\n        }\n        const labelSizes = this._getLabelSizes();\n        for(i = 0, ilen = ticks.length; i < ilen; ++i){\n            tick = ticks[i];\n            label = tick.label;\n            const optsAtIndex = optionTicks.setContext(this.getContext(i));\n            pixel = this.getPixelForTick(i) + optionTicks.labelOffset;\n            font = this._resolveTickFontOptions(i);\n            lineHeight = font.lineHeight;\n            lineCount = isArray(label) ? label.length : 1;\n            const halfCount = lineCount / 2;\n            const color = optsAtIndex.color;\n            const strokeColor = optsAtIndex.textStrokeColor;\n            const strokeWidth = optsAtIndex.textStrokeWidth;\n            let tickTextAlign = textAlign;\n            if (isHorizontal) {\n                x = pixel;\n                if (textAlign === 'inner') {\n                    if (i === ilen - 1) {\n                        tickTextAlign = !this.options.reverse ? 'right' : 'left';\n                    } else if (i === 0) {\n                        tickTextAlign = !this.options.reverse ? 'left' : 'right';\n                    } else {\n                        tickTextAlign = 'center';\n                    }\n                }\n                if (position === 'top') {\n                    if (crossAlign === 'near' || rotation !== 0) {\n                        textOffset = -lineCount * lineHeight + lineHeight / 2;\n                    } else if (crossAlign === 'center') {\n                        textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight;\n                    } else {\n                        textOffset = -labelSizes.highest.height + lineHeight / 2;\n                    }\n                } else {\n                    if (crossAlign === 'near' || rotation !== 0) {\n                        textOffset = lineHeight / 2;\n                    } else if (crossAlign === 'center') {\n                        textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight;\n                    } else {\n                        textOffset = labelSizes.highest.height - lineCount * lineHeight;\n                    }\n                }\n                if (mirror) {\n                    textOffset *= -1;\n                }\n                if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) {\n                    x += lineHeight / 2 * Math.sin(rotation);\n                }\n            } else {\n                y = pixel;\n                textOffset = (1 - lineCount) * lineHeight / 2;\n            }\n            let backdrop;\n            if (optsAtIndex.showLabelBackdrop) {\n                const labelPadding = toPadding(optsAtIndex.backdropPadding);\n                const height = labelSizes.heights[i];\n                const width = labelSizes.widths[i];\n                let top = textOffset - labelPadding.top;\n                let left = 0 - labelPadding.left;\n                switch(textBaseline){\n                    case 'middle':\n                        top -= height / 2;\n                        break;\n                    case 'bottom':\n                        top -= height;\n                        break;\n                }\n                switch(textAlign){\n                    case 'center':\n                        left -= width / 2;\n                        break;\n                    case 'right':\n                        left -= width;\n                        break;\n                }\n                backdrop = {\n                    left,\n                    top,\n                    width: width + labelPadding.width,\n                    height: height + labelPadding.height,\n                    color: optsAtIndex.backdropColor\n                };\n            }\n            items.push({\n                label,\n                font,\n                textOffset,\n                options: {\n                    rotation,\n                    color,\n                    strokeColor,\n                    strokeWidth,\n                    textAlign: tickTextAlign,\n                    textBaseline,\n                    translation: [\n                        x,\n                        y\n                    ],\n                    backdrop\n                }\n            });\n        }\n        return items;\n    }\n    _getXAxisLabelAlignment() {\n        const { position , ticks  } = this.options;\n        const rotation = -toRadians(this.labelRotation);\n        if (rotation) {\n            return position === 'top' ? 'left' : 'right';\n        }\n        let align = 'center';\n        if (ticks.align === 'start') {\n            align = 'left';\n        } else if (ticks.align === 'end') {\n            align = 'right';\n        } else if (ticks.align === 'inner') {\n            align = 'inner';\n        }\n        return align;\n    }\n    _getYAxisLabelAlignment(tl) {\n        const { position , ticks: { crossAlign , mirror , padding  }  } = this.options;\n        const labelSizes = this._getLabelSizes();\n        const tickAndPadding = tl + padding;\n        const widest = labelSizes.widest.width;\n        let textAlign;\n        let x;\n        if (position === 'left') {\n            if (mirror) {\n                x = this.right + padding;\n                if (crossAlign === 'near') {\n                    textAlign = 'left';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x += widest / 2;\n                } else {\n                    textAlign = 'right';\n                    x += widest;\n                }\n            } else {\n                x = this.right - tickAndPadding;\n                if (crossAlign === 'near') {\n                    textAlign = 'right';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x -= widest / 2;\n                } else {\n                    textAlign = 'left';\n                    x = this.left;\n                }\n            }\n        } else if (position === 'right') {\n            if (mirror) {\n                x = this.left + padding;\n                if (crossAlign === 'near') {\n                    textAlign = 'right';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x -= widest / 2;\n                } else {\n                    textAlign = 'left';\n                    x -= widest;\n                }\n            } else {\n                x = this.left + tickAndPadding;\n                if (crossAlign === 'near') {\n                    textAlign = 'left';\n                } else if (crossAlign === 'center') {\n                    textAlign = 'center';\n                    x += widest / 2;\n                } else {\n                    textAlign = 'right';\n                    x = this.right;\n                }\n            }\n        } else {\n            textAlign = 'right';\n        }\n        return {\n            textAlign,\n            x\n        };\n    }\n _computeLabelArea() {\n        if (this.options.ticks.mirror) {\n            return;\n        }\n        const chart = this.chart;\n        const position = this.options.position;\n        if (position === 'left' || position === 'right') {\n            return {\n                top: 0,\n                left: this.left,\n                bottom: chart.height,\n                right: this.right\n            };\n        }\n        if (position === 'top' || position === 'bottom') {\n            return {\n                top: this.top,\n                left: 0,\n                bottom: this.bottom,\n                right: chart.width\n            };\n        }\n    }\n drawBackground() {\n        const { ctx , options: { backgroundColor  } , left , top , width , height  } = this;\n        if (backgroundColor) {\n            ctx.save();\n            ctx.fillStyle = backgroundColor;\n            ctx.fillRect(left, top, width, height);\n            ctx.restore();\n        }\n    }\n    getLineWidthForValue(value) {\n        const grid = this.options.grid;\n        if (!this._isVisible() || !grid.display) {\n            return 0;\n        }\n        const ticks = this.ticks;\n        const index = ticks.findIndex((t)=>t.value === value);\n        if (index >= 0) {\n            const opts = grid.setContext(this.getContext(index));\n            return opts.lineWidth;\n        }\n        return 0;\n    }\n drawGrid(chartArea) {\n        const grid = this.options.grid;\n        const ctx = this.ctx;\n        const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea));\n        let i, ilen;\n        const drawLine = (p1, p2, style)=>{\n            if (!style.width || !style.color) {\n                return;\n            }\n            ctx.save();\n            ctx.lineWidth = style.width;\n            ctx.strokeStyle = style.color;\n            ctx.setLineDash(style.borderDash || []);\n            ctx.lineDashOffset = style.borderDashOffset;\n            ctx.beginPath();\n            ctx.moveTo(p1.x, p1.y);\n            ctx.lineTo(p2.x, p2.y);\n            ctx.stroke();\n            ctx.restore();\n        };\n        if (grid.display) {\n            for(i = 0, ilen = items.length; i < ilen; ++i){\n                const item = items[i];\n                if (grid.drawOnChartArea) {\n                    drawLine({\n                        x: item.x1,\n                        y: item.y1\n                    }, {\n                        x: item.x2,\n                        y: item.y2\n                    }, item);\n                }\n                if (grid.drawTicks) {\n                    drawLine({\n                        x: item.tx1,\n                        y: item.ty1\n                    }, {\n                        x: item.tx2,\n                        y: item.ty2\n                    }, {\n                        color: item.tickColor,\n                        width: item.tickWidth,\n                        borderDash: item.tickBorderDash,\n                        borderDashOffset: item.tickBorderDashOffset\n                    });\n                }\n            }\n        }\n    }\n drawBorder() {\n        const { chart , ctx , options: { border , grid  }  } = this;\n        const borderOpts = border.setContext(this.getContext());\n        const axisWidth = border.display ? borderOpts.width : 0;\n        if (!axisWidth) {\n            return;\n        }\n        const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth;\n        const borderValue = this._borderValue;\n        let x1, x2, y1, y2;\n        if (this.isHorizontal()) {\n            x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2;\n            x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2;\n            y1 = y2 = borderValue;\n        } else {\n            y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2;\n            y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2;\n            x1 = x2 = borderValue;\n        }\n        ctx.save();\n        ctx.lineWidth = borderOpts.width;\n        ctx.strokeStyle = borderOpts.color;\n        ctx.beginPath();\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n        ctx.stroke();\n        ctx.restore();\n    }\n drawLabels(chartArea) {\n        const optionTicks = this.options.ticks;\n        if (!optionTicks.display) {\n            return;\n        }\n        const ctx = this.ctx;\n        const area = this._computeLabelArea();\n        if (area) {\n            clipArea(ctx, area);\n        }\n        const items = this.getLabelItems(chartArea);\n        for (const item of items){\n            const renderTextOptions = item.options;\n            const tickFont = item.font;\n            const label = item.label;\n            const y = item.textOffset;\n            renderText(ctx, label, 0, y, tickFont, renderTextOptions);\n        }\n        if (area) {\n            unclipArea(ctx);\n        }\n    }\n drawTitle() {\n        const { ctx , options: { position , title , reverse  }  } = this;\n        if (!title.display) {\n            return;\n        }\n        const font = toFont(title.font);\n        const padding = toPadding(title.padding);\n        const align = title.align;\n        let offset = font.lineHeight / 2;\n        if (position === 'bottom' || position === 'center' || isObject(position)) {\n            offset += padding.bottom;\n            if (isArray(title.text)) {\n                offset += font.lineHeight * (title.text.length - 1);\n            }\n        } else {\n            offset += padding.top;\n        }\n        const { titleX , titleY , maxWidth , rotation  } = titleArgs(this, offset, position, align);\n        renderText(ctx, title.text, 0, 0, font, {\n            color: title.color,\n            maxWidth,\n            rotation,\n            textAlign: titleAlign(align, position, reverse),\n            textBaseline: 'middle',\n            translation: [\n                titleX,\n                titleY\n            ]\n        });\n    }\n    draw(chartArea) {\n        if (!this._isVisible()) {\n            return;\n        }\n        this.drawBackground();\n        this.drawGrid(chartArea);\n        this.drawBorder();\n        this.drawTitle();\n        this.drawLabels(chartArea);\n    }\n _layers() {\n        const opts = this.options;\n        const tz = opts.ticks && opts.ticks.z || 0;\n        const gz = valueOrDefault(opts.grid && opts.grid.z, -1);\n        const bz = valueOrDefault(opts.border && opts.border.z, 0);\n        if (!this._isVisible() || this.draw !== Scale.prototype.draw) {\n            return [\n                {\n                    z: tz,\n                    draw: (chartArea)=>{\n                        this.draw(chartArea);\n                    }\n                }\n            ];\n        }\n        return [\n            {\n                z: gz,\n                draw: (chartArea)=>{\n                    this.drawBackground();\n                    this.drawGrid(chartArea);\n                    this.drawTitle();\n                }\n            },\n            {\n                z: bz,\n                draw: ()=>{\n                    this.drawBorder();\n                }\n            },\n            {\n                z: tz,\n                draw: (chartArea)=>{\n                    this.drawLabels(chartArea);\n                }\n            }\n        ];\n    }\n getMatchingVisibleMetas(type) {\n        const metas = this.chart.getSortedVisibleDatasetMetas();\n        const axisID = this.axis + 'AxisID';\n        const result = [];\n        let i, ilen;\n        for(i = 0, ilen = metas.length; i < ilen; ++i){\n            const meta = metas[i];\n            if (meta[axisID] === this.id && (!type || meta.type === type)) {\n                result.push(meta);\n            }\n        }\n        return result;\n    }\n _resolveTickFontOptions(index) {\n        const opts = this.options.ticks.setContext(this.getContext(index));\n        return toFont(opts.font);\n    }\n _maxDigits() {\n        const fontSize = this._resolveTickFontOptions(0).lineHeight;\n        return (this.isHorizontal() ? this.width : this.height) / fontSize;\n    }\n}\n\nclass TypedRegistry {\n    constructor(type, scope, override){\n        this.type = type;\n        this.scope = scope;\n        this.override = override;\n        this.items = Object.create(null);\n    }\n    isForType(type) {\n        return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);\n    }\n register(item) {\n        const proto = Object.getPrototypeOf(item);\n        let parentScope;\n        if (isIChartComponent(proto)) {\n            parentScope = this.register(proto);\n        }\n        const items = this.items;\n        const id = item.id;\n        const scope = this.scope + '.' + id;\n        if (!id) {\n            throw new Error('class does not have id: ' + item);\n        }\n        if (id in items) {\n            return scope;\n        }\n        items[id] = item;\n        registerDefaults(item, scope, parentScope);\n        if (this.override) {\n            defaults.override(item.id, item.overrides);\n        }\n        return scope;\n    }\n get(id) {\n        return this.items[id];\n    }\n unregister(item) {\n        const items = this.items;\n        const id = item.id;\n        const scope = this.scope;\n        if (id in items) {\n            delete items[id];\n        }\n        if (scope && id in defaults[scope]) {\n            delete defaults[scope][id];\n            if (this.override) {\n                delete overrides[id];\n            }\n        }\n    }\n}\nfunction registerDefaults(item, scope, parentScope) {\n    const itemDefaults = merge(Object.create(null), [\n        parentScope ? defaults.get(parentScope) : {},\n        defaults.get(scope),\n        item.defaults\n    ]);\n    defaults.set(scope, itemDefaults);\n    if (item.defaultRoutes) {\n        routeDefaults(scope, item.defaultRoutes);\n    }\n    if (item.descriptors) {\n        defaults.describe(scope, item.descriptors);\n    }\n}\nfunction routeDefaults(scope, routes) {\n    Object.keys(routes).forEach((property)=>{\n        const propertyParts = property.split('.');\n        const sourceName = propertyParts.pop();\n        const sourceScope = [\n            scope\n        ].concat(propertyParts).join('.');\n        const parts = routes[property].split('.');\n        const targetName = parts.pop();\n        const targetScope = parts.join('.');\n        defaults.route(sourceScope, sourceName, targetScope, targetName);\n    });\n}\nfunction isIChartComponent(proto) {\n    return 'id' in proto && 'defaults' in proto;\n}\n\nclass Registry {\n    constructor(){\n        this.controllers = new TypedRegistry(DatasetController, 'datasets', true);\n        this.elements = new TypedRegistry(Element, 'elements');\n        this.plugins = new TypedRegistry(Object, 'plugins');\n        this.scales = new TypedRegistry(Scale, 'scales');\n        this._typedRegistries = [\n            this.controllers,\n            this.scales,\n            this.elements\n        ];\n    }\n add(...args) {\n        this._each('register', args);\n    }\n    remove(...args) {\n        this._each('unregister', args);\n    }\n addControllers(...args) {\n        this._each('register', args, this.controllers);\n    }\n addElements(...args) {\n        this._each('register', args, this.elements);\n    }\n addPlugins(...args) {\n        this._each('register', args, this.plugins);\n    }\n addScales(...args) {\n        this._each('register', args, this.scales);\n    }\n getController(id) {\n        return this._get(id, this.controllers, 'controller');\n    }\n getElement(id) {\n        return this._get(id, this.elements, 'element');\n    }\n getPlugin(id) {\n        return this._get(id, this.plugins, 'plugin');\n    }\n getScale(id) {\n        return this._get(id, this.scales, 'scale');\n    }\n removeControllers(...args) {\n        this._each('unregister', args, this.controllers);\n    }\n removeElements(...args) {\n        this._each('unregister', args, this.elements);\n    }\n removePlugins(...args) {\n        this._each('unregister', args, this.plugins);\n    }\n removeScales(...args) {\n        this._each('unregister', args, this.scales);\n    }\n _each(method, args, typedRegistry) {\n        [\n            ...args\n        ].forEach((arg)=>{\n            const reg = typedRegistry || this._getRegistryForType(arg);\n            if (typedRegistry || reg.isForType(arg) || reg === this.plugins && arg.id) {\n                this._exec(method, reg, arg);\n            } else {\n                each(arg, (item)=>{\n                    const itemReg = typedRegistry || this._getRegistryForType(item);\n                    this._exec(method, itemReg, item);\n                });\n            }\n        });\n    }\n _exec(method, registry, component) {\n        const camelMethod = _capitalize(method);\n        callback(component['before' + camelMethod], [], component);\n        registry[method](component);\n        callback(component['after' + camelMethod], [], component);\n    }\n _getRegistryForType(type) {\n        for(let i = 0; i < this._typedRegistries.length; i++){\n            const reg = this._typedRegistries[i];\n            if (reg.isForType(type)) {\n                return reg;\n            }\n        }\n        return this.plugins;\n    }\n _get(id, typedRegistry, type) {\n        const item = typedRegistry.get(id);\n        if (item === undefined) {\n            throw new Error('\"' + id + '\" is not a registered ' + type + '.');\n        }\n        return item;\n    }\n}\nvar registry = /* #__PURE__ */ new Registry();\n\nclass PluginService {\n    constructor(){\n        this._init = [];\n    }\n notify(chart, hook, args, filter) {\n        if (hook === 'beforeInit') {\n            this._init = this._createDescriptors(chart, true);\n            this._notify(this._init, chart, 'install');\n        }\n        const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);\n        const result = this._notify(descriptors, chart, hook, args);\n        if (hook === 'afterDestroy') {\n            this._notify(descriptors, chart, 'stop');\n            this._notify(this._init, chart, 'uninstall');\n        }\n        return result;\n    }\n _notify(descriptors, chart, hook, args) {\n        args = args || {};\n        for (const descriptor of descriptors){\n            const plugin = descriptor.plugin;\n            const method = plugin[hook];\n            const params = [\n                chart,\n                args,\n                descriptor.options\n            ];\n            if (callback(method, params, plugin) === false && args.cancelable) {\n                return false;\n            }\n        }\n        return true;\n    }\n    invalidate() {\n        if (!isNullOrUndef(this._cache)) {\n            this._oldCache = this._cache;\n            this._cache = undefined;\n        }\n    }\n _descriptors(chart) {\n        if (this._cache) {\n            return this._cache;\n        }\n        const descriptors = this._cache = this._createDescriptors(chart);\n        this._notifyStateChanges(chart);\n        return descriptors;\n    }\n    _createDescriptors(chart, all) {\n        const config = chart && chart.config;\n        const options = valueOrDefault(config.options && config.options.plugins, {});\n        const plugins = allPlugins(config);\n        return options === false && !all ? [] : createDescriptors(chart, plugins, options, all);\n    }\n _notifyStateChanges(chart) {\n        const previousDescriptors = this._oldCache || [];\n        const descriptors = this._cache;\n        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.plugin.id === y.plugin.id));\n        this._notify(diff(previousDescriptors, descriptors), chart, 'stop');\n        this._notify(diff(descriptors, previousDescriptors), chart, 'start');\n    }\n}\n function allPlugins(config) {\n    const localIds = {};\n    const plugins = [];\n    const keys = Object.keys(registry.plugins.items);\n    for(let i = 0; i < keys.length; i++){\n        plugins.push(registry.getPlugin(keys[i]));\n    }\n    const local = config.plugins || [];\n    for(let i1 = 0; i1 < local.length; i1++){\n        const plugin = local[i1];\n        if (plugins.indexOf(plugin) === -1) {\n            plugins.push(plugin);\n            localIds[plugin.id] = true;\n        }\n    }\n    return {\n        plugins,\n        localIds\n    };\n}\nfunction getOpts(options, all) {\n    if (!all && options === false) {\n        return null;\n    }\n    if (options === true) {\n        return {};\n    }\n    return options;\n}\nfunction createDescriptors(chart, { plugins , localIds  }, options, all) {\n    const result = [];\n    const context = chart.getContext();\n    for (const plugin of plugins){\n        const id = plugin.id;\n        const opts = getOpts(options[id], all);\n        if (opts === null) {\n            continue;\n        }\n        result.push({\n            plugin,\n            options: pluginOpts(chart.config, {\n                plugin,\n                local: localIds[id]\n            }, opts, context)\n        });\n    }\n    return result;\n}\nfunction pluginOpts(config, { plugin , local  }, opts, context) {\n    const keys = config.pluginScopeKeys(plugin);\n    const scopes = config.getOptionScopes(opts, keys);\n    if (local && plugin.defaults) {\n        scopes.push(plugin.defaults);\n    }\n    return config.createResolver(scopes, context, [\n        ''\n    ], {\n        scriptable: false,\n        indexable: false,\n        allKeys: true\n    });\n}\n\nfunction getIndexAxis(type, options) {\n    const datasetDefaults = defaults.datasets[type] || {};\n    const datasetOptions = (options.datasets || {})[type] || {};\n    return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';\n}\nfunction getAxisFromDefaultScaleID(id, indexAxis) {\n    let axis = id;\n    if (id === '_index_') {\n        axis = indexAxis;\n    } else if (id === '_value_') {\n        axis = indexAxis === 'x' ? 'y' : 'x';\n    }\n    return axis;\n}\nfunction getDefaultScaleIDFromAxis(axis, indexAxis) {\n    return axis === indexAxis ? '_index_' : '_value_';\n}\nfunction axisFromPosition(position) {\n    if (position === 'top' || position === 'bottom') {\n        return 'x';\n    }\n    if (position === 'left' || position === 'right') {\n        return 'y';\n    }\n}\nfunction determineAxis(id, scaleOptions) {\n    if (id === 'x' || id === 'y' || id === 'r') {\n        return id;\n    }\n    id = scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.length > 1 && determineAxis(id[0].toLowerCase(), scaleOptions);\n    if (id) {\n        return id;\n    }\n    throw new Error(`Cannot determine type of '${name}' axis. Please provide 'axis' or 'position' option.`);\n}\nfunction mergeScaleConfig(config, options) {\n    const chartDefaults = overrides[config.type] || {\n        scales: {}\n    };\n    const configScales = options.scales || {};\n    const chartIndexAxis = getIndexAxis(config.type, options);\n    const scales = Object.create(null);\n    Object.keys(configScales).forEach((id)=>{\n        const scaleConf = configScales[id];\n        if (!isObject(scaleConf)) {\n            return console.error(`Invalid scale configuration for scale: ${id}`);\n        }\n        if (scaleConf._proxy) {\n            return console.warn(`Ignoring resolver passed as options for scale: ${id}`);\n        }\n        const axis = determineAxis(id, scaleConf);\n        const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);\n        const defaultScaleOptions = chartDefaults.scales || {};\n        scales[id] = mergeIf(Object.create(null), [\n            {\n                axis\n            },\n            scaleConf,\n            defaultScaleOptions[axis],\n            defaultScaleOptions[defaultId]\n        ]);\n    });\n    config.data.datasets.forEach((dataset)=>{\n        const type = dataset.type || config.type;\n        const indexAxis = dataset.indexAxis || getIndexAxis(type, options);\n        const datasetDefaults = overrides[type] || {};\n        const defaultScaleOptions = datasetDefaults.scales || {};\n        Object.keys(defaultScaleOptions).forEach((defaultID)=>{\n            const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);\n            const id = dataset[axis + 'AxisID'] || axis;\n            scales[id] = scales[id] || Object.create(null);\n            mergeIf(scales[id], [\n                {\n                    axis\n                },\n                configScales[id],\n                defaultScaleOptions[defaultID]\n            ]);\n        });\n    });\n    Object.keys(scales).forEach((key)=>{\n        const scale = scales[key];\n        mergeIf(scale, [\n            defaults.scales[scale.type],\n            defaults.scale\n        ]);\n    });\n    return scales;\n}\nfunction initOptions(config) {\n    const options = config.options || (config.options = {});\n    options.plugins = valueOrDefault(options.plugins, {});\n    options.scales = mergeScaleConfig(config, options);\n}\nfunction initData(data) {\n    data = data || {};\n    data.datasets = data.datasets || [];\n    data.labels = data.labels || [];\n    return data;\n}\nfunction initConfig(config) {\n    config = config || {};\n    config.data = initData(config.data);\n    initOptions(config);\n    return config;\n}\nconst keyCache = new Map();\nconst keysCached = new Set();\nfunction cachedKeys(cacheKey, generate) {\n    let keys = keyCache.get(cacheKey);\n    if (!keys) {\n        keys = generate();\n        keyCache.set(cacheKey, keys);\n        keysCached.add(keys);\n    }\n    return keys;\n}\nconst addIfFound = (set, obj, key)=>{\n    const opts = resolveObjectKey(obj, key);\n    if (opts !== undefined) {\n        set.add(opts);\n    }\n};\nclass Config {\n    constructor(config){\n        this._config = initConfig(config);\n        this._scopeCache = new Map();\n        this._resolverCache = new Map();\n    }\n    get platform() {\n        return this._config.platform;\n    }\n    get type() {\n        return this._config.type;\n    }\n    set type(type) {\n        this._config.type = type;\n    }\n    get data() {\n        return this._config.data;\n    }\n    set data(data) {\n        this._config.data = initData(data);\n    }\n    get options() {\n        return this._config.options;\n    }\n    set options(options) {\n        this._config.options = options;\n    }\n    get plugins() {\n        return this._config.plugins;\n    }\n    update() {\n        const config = this._config;\n        this.clearCache();\n        initOptions(config);\n    }\n    clearCache() {\n        this._scopeCache.clear();\n        this._resolverCache.clear();\n    }\n datasetScopeKeys(datasetType) {\n        return cachedKeys(datasetType, ()=>[\n                [\n                    `datasets.${datasetType}`,\n                    ''\n                ]\n            ]);\n    }\n datasetAnimationScopeKeys(datasetType, transition) {\n        return cachedKeys(`${datasetType}.transition.${transition}`, ()=>[\n                [\n                    `datasets.${datasetType}.transitions.${transition}`,\n                    `transitions.${transition}`\n                ],\n                [\n                    `datasets.${datasetType}`,\n                    ''\n                ]\n            ]);\n    }\n datasetElementScopeKeys(datasetType, elementType) {\n        return cachedKeys(`${datasetType}-${elementType}`, ()=>[\n                [\n                    `datasets.${datasetType}.elements.${elementType}`,\n                    `datasets.${datasetType}`,\n                    `elements.${elementType}`,\n                    ''\n                ]\n            ]);\n    }\n pluginScopeKeys(plugin) {\n        const id = plugin.id;\n        const type = this.type;\n        return cachedKeys(`${type}-plugin-${id}`, ()=>[\n                [\n                    `plugins.${id}`,\n                    ...plugin.additionalOptionScopes || []\n                ]\n            ]);\n    }\n _cachedScopes(mainScope, resetCache) {\n        const _scopeCache = this._scopeCache;\n        let cache = _scopeCache.get(mainScope);\n        if (!cache || resetCache) {\n            cache = new Map();\n            _scopeCache.set(mainScope, cache);\n        }\n        return cache;\n    }\n getOptionScopes(mainScope, keyLists, resetCache) {\n        const { options , type  } = this;\n        const cache = this._cachedScopes(mainScope, resetCache);\n        const cached = cache.get(keyLists);\n        if (cached) {\n            return cached;\n        }\n        const scopes = new Set();\n        keyLists.forEach((keys)=>{\n            if (mainScope) {\n                scopes.add(mainScope);\n                keys.forEach((key)=>addIfFound(scopes, mainScope, key));\n            }\n            keys.forEach((key)=>addIfFound(scopes, options, key));\n            keys.forEach((key)=>addIfFound(scopes, overrides[type] || {}, key));\n            keys.forEach((key)=>addIfFound(scopes, defaults, key));\n            keys.forEach((key)=>addIfFound(scopes, descriptors, key));\n        });\n        const array = Array.from(scopes);\n        if (array.length === 0) {\n            array.push(Object.create(null));\n        }\n        if (keysCached.has(keyLists)) {\n            cache.set(keyLists, array);\n        }\n        return array;\n    }\n chartOptionScopes() {\n        const { options , type  } = this;\n        return [\n            options,\n            overrides[type] || {},\n            defaults.datasets[type] || {},\n            {\n                type\n            },\n            defaults,\n            descriptors\n        ];\n    }\n resolveNamedOptions(scopes, names, context, prefixes = [\n        ''\n    ]) {\n        const result = {\n            $shared: true\n        };\n        const { resolver , subPrefixes  } = getResolver(this._resolverCache, scopes, prefixes);\n        let options = resolver;\n        if (needContext(resolver, names)) {\n            result.$shared = false;\n            context = isFunction(context) ? context() : context;\n            const subResolver = this.createResolver(scopes, context, subPrefixes);\n            options = _attachContext(resolver, context, subResolver);\n        }\n        for (const prop of names){\n            result[prop] = options[prop];\n        }\n        return result;\n    }\n createResolver(scopes, context, prefixes = [\n        ''\n    ], descriptorDefaults) {\n        const { resolver  } = getResolver(this._resolverCache, scopes, prefixes);\n        return isObject(context) ? _attachContext(resolver, context, undefined, descriptorDefaults) : resolver;\n    }\n}\nfunction getResolver(resolverCache, scopes, prefixes) {\n    let cache = resolverCache.get(scopes);\n    if (!cache) {\n        cache = new Map();\n        resolverCache.set(scopes, cache);\n    }\n    const cacheKey = prefixes.join();\n    let cached = cache.get(cacheKey);\n    if (!cached) {\n        const resolver = _createResolver(scopes, prefixes);\n        cached = {\n            resolver,\n            subPrefixes: prefixes.filter((p)=>!p.toLowerCase().includes('hover'))\n        };\n        cache.set(cacheKey, cached);\n    }\n    return cached;\n}\nconst hasFunction = (value)=>isObject(value) && Object.getOwnPropertyNames(value).reduce((acc, key)=>acc || isFunction(value[key]), false);\nfunction needContext(proxy, names) {\n    const { isScriptable , isIndexable  } = _descriptors(proxy);\n    for (const prop of names){\n        const scriptable = isScriptable(prop);\n        const indexable = isIndexable(prop);\n        const value = (indexable || scriptable) && proxy[prop];\n        if (scriptable && (isFunction(value) || hasFunction(value)) || indexable && isArray(value)) {\n            return true;\n        }\n    }\n    return false;\n}\n\nvar version = \"4.2.1\";\n\nconst KNOWN_POSITIONS = [\n    'top',\n    'bottom',\n    'left',\n    'right',\n    'chartArea'\n];\nfunction positionIsHorizontal(position, axis) {\n    return position === 'top' || position === 'bottom' || KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x';\n}\nfunction compare2Level(l1, l2) {\n    return function(a, b) {\n        return a[l1] === b[l1] ? a[l2] - b[l2] : a[l1] - b[l1];\n    };\n}\nfunction onAnimationsComplete(context) {\n    const chart = context.chart;\n    const animationOptions = chart.options.animation;\n    chart.notifyPlugins('afterRender');\n    callback(animationOptions && animationOptions.onComplete, [\n        context\n    ], chart);\n}\nfunction onAnimationProgress(context) {\n    const chart = context.chart;\n    const animationOptions = chart.options.animation;\n    callback(animationOptions && animationOptions.onProgress, [\n        context\n    ], chart);\n}\n function getCanvas(item) {\n    if (_isDomSupported() && typeof item === 'string') {\n        item = document.getElementById(item);\n    } else if (item && item.length) {\n        item = item[0];\n    }\n    if (item && item.canvas) {\n        item = item.canvas;\n    }\n    return item;\n}\nconst instances = {};\nconst getChart = (key)=>{\n    const canvas = getCanvas(key);\n    return Object.values(instances).filter((c)=>c.canvas === canvas).pop();\n};\nfunction moveNumericKeys(obj, start, move) {\n    const keys = Object.keys(obj);\n    for (const key of keys){\n        const intKey = +key;\n        if (intKey >= start) {\n            const value = obj[key];\n            delete obj[key];\n            if (move > 0 || intKey > start) {\n                obj[intKey + move] = value;\n            }\n        }\n    }\n}\n function determineLastEvent(e, lastEvent, inChartArea, isClick) {\n    if (!inChartArea || e.type === 'mouseout') {\n        return null;\n    }\n    if (isClick) {\n        return lastEvent;\n    }\n    return e;\n}\nfunction getDatasetArea(meta) {\n    const { xScale , yScale  } = meta;\n    if (xScale && yScale) {\n        return {\n            left: xScale.left,\n            right: xScale.right,\n            top: yScale.top,\n            bottom: yScale.bottom\n        };\n    }\n}\nclass Chart {\n    static defaults = defaults;\n    static instances = instances;\n    static overrides = overrides;\n    static registry = registry;\n    static version = version;\n    static getChart = getChart;\n    static register(...items) {\n        registry.add(...items);\n        invalidatePlugins();\n    }\n    static unregister(...items) {\n        registry.remove(...items);\n        invalidatePlugins();\n    }\n    constructor(item, userConfig){\n        const config = this.config = new Config(userConfig);\n        const initialCanvas = getCanvas(item);\n        const existingChart = getChart(initialCanvas);\n        if (existingChart) {\n            throw new Error('Canvas is already in use. Chart with ID \\'' + existingChart.id + '\\'' + ' must be destroyed before the canvas with ID \\'' + existingChart.canvas.id + '\\' can be reused.');\n        }\n        const options = config.createResolver(config.chartOptionScopes(), this.getContext());\n        this.platform = new (config.platform || _detectPlatform(initialCanvas))();\n        this.platform.updateConfig(config);\n        const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);\n        const canvas = context && context.canvas;\n        const height = canvas && canvas.height;\n        const width = canvas && canvas.width;\n        this.id = uid();\n        this.ctx = context;\n        this.canvas = canvas;\n        this.width = width;\n        this.height = height;\n        this._options = options;\n        this._aspectRatio = this.aspectRatio;\n        this._layers = [];\n        this._metasets = [];\n        this._stacks = undefined;\n        this.boxes = [];\n        this.currentDevicePixelRatio = undefined;\n        this.chartArea = undefined;\n        this._active = [];\n        this._lastEvent = undefined;\n        this._listeners = {};\n         this._responsiveListeners = undefined;\n        this._sortedMetasets = [];\n        this.scales = {};\n        this._plugins = new PluginService();\n        this.$proxies = {};\n        this._hiddenIndices = {};\n        this.attached = false;\n        this._animationsDisabled = undefined;\n        this.$context = undefined;\n        this._doResize = debounce((mode)=>this.update(mode), options.resizeDelay || 0);\n        this._dataChanges = [];\n        instances[this.id] = this;\n        if (!context || !canvas) {\n            console.error(\"Failed to create chart: can't acquire context from the given item\");\n            return;\n        }\n        animator.listen(this, 'complete', onAnimationsComplete);\n        animator.listen(this, 'progress', onAnimationProgress);\n        this._initialize();\n        if (this.attached) {\n            this.update();\n        }\n    }\n    get aspectRatio() {\n        const { options: { aspectRatio , maintainAspectRatio  } , width , height , _aspectRatio  } = this;\n        if (!isNullOrUndef(aspectRatio)) {\n            return aspectRatio;\n        }\n        if (maintainAspectRatio && _aspectRatio) {\n            return _aspectRatio;\n        }\n        return height ? width / height : null;\n    }\n    get data() {\n        return this.config.data;\n    }\n    set data(data) {\n        this.config.data = data;\n    }\n    get options() {\n        return this._options;\n    }\n    set options(options) {\n        this.config.options = options;\n    }\n    get registry() {\n        return registry;\n    }\n _initialize() {\n        this.notifyPlugins('beforeInit');\n        if (this.options.responsive) {\n            this.resize();\n        } else {\n            retinaScale(this, this.options.devicePixelRatio);\n        }\n        this.bindEvents();\n        this.notifyPlugins('afterInit');\n        return this;\n    }\n    clear() {\n        clearCanvas(this.canvas, this.ctx);\n        return this;\n    }\n    stop() {\n        animator.stop(this);\n        return this;\n    }\n resize(width, height) {\n        if (!animator.running(this)) {\n            this._resize(width, height);\n        } else {\n            this._resizeBeforeDraw = {\n                width,\n                height\n            };\n        }\n    }\n    _resize(width, height) {\n        const options = this.options;\n        const canvas = this.canvas;\n        const aspectRatio = options.maintainAspectRatio && this.aspectRatio;\n        const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);\n        const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();\n        const mode = this.width ? 'resize' : 'attach';\n        this.width = newSize.width;\n        this.height = newSize.height;\n        this._aspectRatio = this.aspectRatio;\n        if (!retinaScale(this, newRatio, true)) {\n            return;\n        }\n        this.notifyPlugins('resize', {\n            size: newSize\n        });\n        callback(options.onResize, [\n            this,\n            newSize\n        ], this);\n        if (this.attached) {\n            if (this._doResize(mode)) {\n                this.render();\n            }\n        }\n    }\n    ensureScalesHaveIDs() {\n        const options = this.options;\n        const scalesOptions = options.scales || {};\n        each(scalesOptions, (axisOptions, axisID)=>{\n            axisOptions.id = axisID;\n        });\n    }\n buildOrUpdateScales() {\n        const options = this.options;\n        const scaleOpts = options.scales;\n        const scales = this.scales;\n        const updated = Object.keys(scales).reduce((obj, id)=>{\n            obj[id] = false;\n            return obj;\n        }, {});\n        let items = [];\n        if (scaleOpts) {\n            items = items.concat(Object.keys(scaleOpts).map((id)=>{\n                const scaleOptions = scaleOpts[id];\n                const axis = determineAxis(id, scaleOptions);\n                const isRadial = axis === 'r';\n                const isHorizontal = axis === 'x';\n                return {\n                    options: scaleOptions,\n                    dposition: isRadial ? 'chartArea' : isHorizontal ? 'bottom' : 'left',\n                    dtype: isRadial ? 'radialLinear' : isHorizontal ? 'category' : 'linear'\n                };\n            }));\n        }\n        each(items, (item)=>{\n            const scaleOptions = item.options;\n            const id = scaleOptions.id;\n            const axis = determineAxis(id, scaleOptions);\n            const scaleType = valueOrDefault(scaleOptions.type, item.dtype);\n            if (scaleOptions.position === undefined || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) {\n                scaleOptions.position = item.dposition;\n            }\n            updated[id] = true;\n            let scale = null;\n            if (id in scales && scales[id].type === scaleType) {\n                scale = scales[id];\n            } else {\n                const scaleClass = registry.getScale(scaleType);\n                scale = new scaleClass({\n                    id,\n                    type: scaleType,\n                    ctx: this.ctx,\n                    chart: this\n                });\n                scales[scale.id] = scale;\n            }\n            scale.init(scaleOptions, options);\n        });\n        each(updated, (hasUpdated, id)=>{\n            if (!hasUpdated) {\n                delete scales[id];\n            }\n        });\n        each(scales, (scale)=>{\n            layouts.configure(this, scale, scale.options);\n            layouts.addBox(this, scale);\n        });\n    }\n _updateMetasets() {\n        const metasets = this._metasets;\n        const numData = this.data.datasets.length;\n        const numMeta = metasets.length;\n        metasets.sort((a, b)=>a.index - b.index);\n        if (numMeta > numData) {\n            for(let i = numData; i < numMeta; ++i){\n                this._destroyDatasetMeta(i);\n            }\n            metasets.splice(numData, numMeta - numData);\n        }\n        this._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));\n    }\n _removeUnreferencedMetasets() {\n        const { _metasets: metasets , data: { datasets  }  } = this;\n        if (metasets.length > datasets.length) {\n            delete this._stacks;\n        }\n        metasets.forEach((meta, index)=>{\n            if (datasets.filter((x)=>x === meta._dataset).length === 0) {\n                this._destroyDatasetMeta(index);\n            }\n        });\n    }\n    buildOrUpdateControllers() {\n        const newControllers = [];\n        const datasets = this.data.datasets;\n        let i, ilen;\n        this._removeUnreferencedMetasets();\n        for(i = 0, ilen = datasets.length; i < ilen; i++){\n            const dataset = datasets[i];\n            let meta = this.getDatasetMeta(i);\n            const type = dataset.type || this.config.type;\n            if (meta.type && meta.type !== type) {\n                this._destroyDatasetMeta(i);\n                meta = this.getDatasetMeta(i);\n            }\n            meta.type = type;\n            meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);\n            meta.order = dataset.order || 0;\n            meta.index = i;\n            meta.label = '' + dataset.label;\n            meta.visible = this.isDatasetVisible(i);\n            if (meta.controller) {\n                meta.controller.updateIndex(i);\n                meta.controller.linkScales();\n            } else {\n                const ControllerClass = registry.getController(type);\n                const { datasetElementType , dataElementType  } = defaults.datasets[type];\n                Object.assign(ControllerClass, {\n                    dataElementType: registry.getElement(dataElementType),\n                    datasetElementType: datasetElementType && registry.getElement(datasetElementType)\n                });\n                meta.controller = new ControllerClass(this, i);\n                newControllers.push(meta.controller);\n            }\n        }\n        this._updateMetasets();\n        return newControllers;\n    }\n _resetElements() {\n        each(this.data.datasets, (dataset, datasetIndex)=>{\n            this.getDatasetMeta(datasetIndex).controller.reset();\n        }, this);\n    }\n reset() {\n        this._resetElements();\n        this.notifyPlugins('reset');\n    }\n    update(mode) {\n        const config = this.config;\n        config.update();\n        const options = this._options = config.createResolver(config.chartOptionScopes(), this.getContext());\n        const animsDisabled = this._animationsDisabled = !options.animation;\n        this._updateScales();\n        this._checkEventBindings();\n        this._updateHiddenIndices();\n        this._plugins.invalidate();\n        if (this.notifyPlugins('beforeUpdate', {\n            mode,\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        const newControllers = this.buildOrUpdateControllers();\n        this.notifyPlugins('beforeElementsUpdate');\n        let minPadding = 0;\n        for(let i = 0, ilen = this.data.datasets.length; i < ilen; i++){\n            const { controller  } = this.getDatasetMeta(i);\n            const reset = !animsDisabled && newControllers.indexOf(controller) === -1;\n            controller.buildOrUpdateElements(reset);\n            minPadding = Math.max(+controller.getMaxOverflow(), minPadding);\n        }\n        minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0;\n        this._updateLayout(minPadding);\n        if (!animsDisabled) {\n            each(newControllers, (controller)=>{\n                controller.reset();\n            });\n        }\n        this._updateDatasets(mode);\n        this.notifyPlugins('afterUpdate', {\n            mode\n        });\n        this._layers.sort(compare2Level('z', '_idx'));\n        const { _active , _lastEvent  } = this;\n        if (_lastEvent) {\n            this._eventHandler(_lastEvent, true);\n        } else if (_active.length) {\n            this._updateHoverStyles(_active, _active, true);\n        }\n        this.render();\n    }\n _updateScales() {\n        each(this.scales, (scale)=>{\n            layouts.removeBox(this, scale);\n        });\n        this.ensureScalesHaveIDs();\n        this.buildOrUpdateScales();\n    }\n _checkEventBindings() {\n        const options = this.options;\n        const existingEvents = new Set(Object.keys(this._listeners));\n        const newEvents = new Set(options.events);\n        if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) {\n            this.unbindEvents();\n            this.bindEvents();\n        }\n    }\n _updateHiddenIndices() {\n        const { _hiddenIndices  } = this;\n        const changes = this._getUniformDataChanges() || [];\n        for (const { method , start , count  } of changes){\n            const move = method === '_removeElements' ? -count : count;\n            moveNumericKeys(_hiddenIndices, start, move);\n        }\n    }\n _getUniformDataChanges() {\n        const _dataChanges = this._dataChanges;\n        if (!_dataChanges || !_dataChanges.length) {\n            return;\n        }\n        this._dataChanges = [];\n        const datasetCount = this.data.datasets.length;\n        const makeSet = (idx)=>new Set(_dataChanges.filter((c)=>c[0] === idx).map((c, i)=>i + ',' + c.splice(1).join(',')));\n        const changeSet = makeSet(0);\n        for(let i = 1; i < datasetCount; i++){\n            if (!setsEqual(changeSet, makeSet(i))) {\n                return;\n            }\n        }\n        return Array.from(changeSet).map((c)=>c.split(',')).map((a)=>({\n                method: a[1],\n                start: +a[2],\n                count: +a[3]\n            }));\n    }\n _updateLayout(minPadding) {\n        if (this.notifyPlugins('beforeLayout', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        layouts.update(this, this.width, this.height, minPadding);\n        const area = this.chartArea;\n        const noArea = area.width <= 0 || area.height <= 0;\n        this._layers = [];\n        each(this.boxes, (box)=>{\n            if (noArea && box.position === 'chartArea') {\n                return;\n            }\n            if (box.configure) {\n                box.configure();\n            }\n            this._layers.push(...box._layers());\n        }, this);\n        this._layers.forEach((item, index)=>{\n            item._idx = index;\n        });\n        this.notifyPlugins('afterLayout');\n    }\n _updateDatasets(mode) {\n        if (this.notifyPlugins('beforeDatasetsUpdate', {\n            mode,\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){\n            this.getDatasetMeta(i).controller.configure();\n        }\n        for(let i1 = 0, ilen1 = this.data.datasets.length; i1 < ilen1; ++i1){\n            this._updateDataset(i1, isFunction(mode) ? mode({\n                datasetIndex: i1\n            }) : mode);\n        }\n        this.notifyPlugins('afterDatasetsUpdate', {\n            mode\n        });\n    }\n _updateDataset(index, mode) {\n        const meta = this.getDatasetMeta(index);\n        const args = {\n            meta,\n            index,\n            mode,\n            cancelable: true\n        };\n        if (this.notifyPlugins('beforeDatasetUpdate', args) === false) {\n            return;\n        }\n        meta.controller._update(mode);\n        args.cancelable = false;\n        this.notifyPlugins('afterDatasetUpdate', args);\n    }\n    render() {\n        if (this.notifyPlugins('beforeRender', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        if (animator.has(this)) {\n            if (this.attached && !animator.running(this)) {\n                animator.start(this);\n            }\n        } else {\n            this.draw();\n            onAnimationsComplete({\n                chart: this\n            });\n        }\n    }\n    draw() {\n        let i;\n        if (this._resizeBeforeDraw) {\n            const { width , height  } = this._resizeBeforeDraw;\n            this._resize(width, height);\n            this._resizeBeforeDraw = null;\n        }\n        this.clear();\n        if (this.width <= 0 || this.height <= 0) {\n            return;\n        }\n        if (this.notifyPlugins('beforeDraw', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        const layers = this._layers;\n        for(i = 0; i < layers.length && layers[i].z <= 0; ++i){\n            layers[i].draw(this.chartArea);\n        }\n        this._drawDatasets();\n        for(; i < layers.length; ++i){\n            layers[i].draw(this.chartArea);\n        }\n        this.notifyPlugins('afterDraw');\n    }\n _getSortedDatasetMetas(filterVisible) {\n        const metasets = this._sortedMetasets;\n        const result = [];\n        let i, ilen;\n        for(i = 0, ilen = metasets.length; i < ilen; ++i){\n            const meta = metasets[i];\n            if (!filterVisible || meta.visible) {\n                result.push(meta);\n            }\n        }\n        return result;\n    }\n getSortedVisibleDatasetMetas() {\n        return this._getSortedDatasetMetas(true);\n    }\n _drawDatasets() {\n        if (this.notifyPlugins('beforeDatasetsDraw', {\n            cancelable: true\n        }) === false) {\n            return;\n        }\n        const metasets = this.getSortedVisibleDatasetMetas();\n        for(let i = metasets.length - 1; i >= 0; --i){\n            this._drawDataset(metasets[i]);\n        }\n        this.notifyPlugins('afterDatasetsDraw');\n    }\n _drawDataset(meta) {\n        const ctx = this.ctx;\n        const clip = meta._clip;\n        const useClip = !clip.disabled;\n        const area = getDatasetArea(meta) || this.chartArea;\n        const args = {\n            meta,\n            index: meta.index,\n            cancelable: true\n        };\n        if (this.notifyPlugins('beforeDatasetDraw', args) === false) {\n            return;\n        }\n        if (useClip) {\n            clipArea(ctx, {\n                left: clip.left === false ? 0 : area.left - clip.left,\n                right: clip.right === false ? this.width : area.right + clip.right,\n                top: clip.top === false ? 0 : area.top - clip.top,\n                bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom\n            });\n        }\n        meta.controller.draw();\n        if (useClip) {\n            unclipArea(ctx);\n        }\n        args.cancelable = false;\n        this.notifyPlugins('afterDatasetDraw', args);\n    }\n isPointInArea(point) {\n        return _isPointInArea(point, this.chartArea, this._minPadding);\n    }\n    getElementsAtEventForMode(e, mode, options, useFinalPosition) {\n        const method = Interaction.modes[mode];\n        if (typeof method === 'function') {\n            return method(this, e, options, useFinalPosition);\n        }\n        return [];\n    }\n    getDatasetMeta(datasetIndex) {\n        const dataset = this.data.datasets[datasetIndex];\n        const metasets = this._metasets;\n        let meta = metasets.filter((x)=>x && x._dataset === dataset).pop();\n        if (!meta) {\n            meta = {\n                type: null,\n                data: [],\n                dataset: null,\n                controller: null,\n                hidden: null,\n                xAxisID: null,\n                yAxisID: null,\n                order: dataset && dataset.order || 0,\n                index: datasetIndex,\n                _dataset: dataset,\n                _parsed: [],\n                _sorted: false\n            };\n            metasets.push(meta);\n        }\n        return meta;\n    }\n    getContext() {\n        return this.$context || (this.$context = createContext(null, {\n            chart: this,\n            type: 'chart'\n        }));\n    }\n    getVisibleDatasetCount() {\n        return this.getSortedVisibleDatasetMetas().length;\n    }\n    isDatasetVisible(datasetIndex) {\n        const dataset = this.data.datasets[datasetIndex];\n        if (!dataset) {\n            return false;\n        }\n        const meta = this.getDatasetMeta(datasetIndex);\n        return typeof meta.hidden === 'boolean' ? !meta.hidden : !dataset.hidden;\n    }\n    setDatasetVisibility(datasetIndex, visible) {\n        const meta = this.getDatasetMeta(datasetIndex);\n        meta.hidden = !visible;\n    }\n    toggleDataVisibility(index) {\n        this._hiddenIndices[index] = !this._hiddenIndices[index];\n    }\n    getDataVisibility(index) {\n        return !this._hiddenIndices[index];\n    }\n _updateVisibility(datasetIndex, dataIndex, visible) {\n        const mode = visible ? 'show' : 'hide';\n        const meta = this.getDatasetMeta(datasetIndex);\n        const anims = meta.controller._resolveAnimations(undefined, mode);\n        if (defined(dataIndex)) {\n            meta.data[dataIndex].hidden = !visible;\n            this.update();\n        } else {\n            this.setDatasetVisibility(datasetIndex, visible);\n            anims.update(meta, {\n                visible\n            });\n            this.update((ctx)=>ctx.datasetIndex === datasetIndex ? mode : undefined);\n        }\n    }\n    hide(datasetIndex, dataIndex) {\n        this._updateVisibility(datasetIndex, dataIndex, false);\n    }\n    show(datasetIndex, dataIndex) {\n        this._updateVisibility(datasetIndex, dataIndex, true);\n    }\n _destroyDatasetMeta(datasetIndex) {\n        const meta = this._metasets[datasetIndex];\n        if (meta && meta.controller) {\n            meta.controller._destroy();\n        }\n        delete this._metasets[datasetIndex];\n    }\n    _stop() {\n        let i, ilen;\n        this.stop();\n        animator.remove(this);\n        for(i = 0, ilen = this.data.datasets.length; i < ilen; ++i){\n            this._destroyDatasetMeta(i);\n        }\n    }\n    destroy() {\n        this.notifyPlugins('beforeDestroy');\n        const { canvas , ctx  } = this;\n        this._stop();\n        this.config.clearCache();\n        if (canvas) {\n            this.unbindEvents();\n            clearCanvas(canvas, ctx);\n            this.platform.releaseContext(ctx);\n            this.canvas = null;\n            this.ctx = null;\n        }\n        delete instances[this.id];\n        this.notifyPlugins('afterDestroy');\n    }\n    toBase64Image(...args) {\n        return this.canvas.toDataURL(...args);\n    }\n bindEvents() {\n        this.bindUserEvents();\n        if (this.options.responsive) {\n            this.bindResponsiveEvents();\n        } else {\n            this.attached = true;\n        }\n    }\n bindUserEvents() {\n        const listeners = this._listeners;\n        const platform = this.platform;\n        const _add = (type, listener)=>{\n            platform.addEventListener(this, type, listener);\n            listeners[type] = listener;\n        };\n        const listener = (e, x, y)=>{\n            e.offsetX = x;\n            e.offsetY = y;\n            this._eventHandler(e);\n        };\n        each(this.options.events, (type)=>_add(type, listener));\n    }\n bindResponsiveEvents() {\n        if (!this._responsiveListeners) {\n            this._responsiveListeners = {};\n        }\n        const listeners = this._responsiveListeners;\n        const platform = this.platform;\n        const _add = (type, listener)=>{\n            platform.addEventListener(this, type, listener);\n            listeners[type] = listener;\n        };\n        const _remove = (type, listener)=>{\n            if (listeners[type]) {\n                platform.removeEventListener(this, type, listener);\n                delete listeners[type];\n            }\n        };\n        const listener = (width, height)=>{\n            if (this.canvas) {\n                this.resize(width, height);\n            }\n        };\n        let detached;\n        const attached = ()=>{\n            _remove('attach', attached);\n            this.attached = true;\n            this.resize();\n            _add('resize', listener);\n            _add('detach', detached);\n        };\n        detached = ()=>{\n            this.attached = false;\n            _remove('resize', listener);\n            this._stop();\n            this._resize(0, 0);\n            _add('attach', attached);\n        };\n        if (platform.isAttached(this.canvas)) {\n            attached();\n        } else {\n            detached();\n        }\n    }\n unbindEvents() {\n        each(this._listeners, (listener, type)=>{\n            this.platform.removeEventListener(this, type, listener);\n        });\n        this._listeners = {};\n        each(this._responsiveListeners, (listener, type)=>{\n            this.platform.removeEventListener(this, type, listener);\n        });\n        this._responsiveListeners = undefined;\n    }\n    updateHoverStyle(items, mode, enabled) {\n        const prefix = enabled ? 'set' : 'remove';\n        let meta, item, i, ilen;\n        if (mode === 'dataset') {\n            meta = this.getDatasetMeta(items[0].datasetIndex);\n            meta.controller['_' + prefix + 'DatasetHoverStyle']();\n        }\n        for(i = 0, ilen = items.length; i < ilen; ++i){\n            item = items[i];\n            const controller = item && this.getDatasetMeta(item.datasetIndex).controller;\n            if (controller) {\n                controller[prefix + 'HoverStyle'](item.element, item.datasetIndex, item.index);\n            }\n        }\n    }\n getActiveElements() {\n        return this._active || [];\n    }\n setActiveElements(activeElements) {\n        const lastActive = this._active || [];\n        const active = activeElements.map(({ datasetIndex , index  })=>{\n            const meta = this.getDatasetMeta(datasetIndex);\n            if (!meta) {\n                throw new Error('No dataset found at index ' + datasetIndex);\n            }\n            return {\n                datasetIndex,\n                element: meta.data[index],\n                index\n            };\n        });\n        const changed = !_elementsEqual(active, lastActive);\n        if (changed) {\n            this._active = active;\n            this._lastEvent = null;\n            this._updateHoverStyles(active, lastActive);\n        }\n    }\n notifyPlugins(hook, args, filter) {\n        return this._plugins.notify(this, hook, args, filter);\n    }\n isPluginEnabled(pluginId) {\n        return this._plugins._cache.filter((p)=>p.plugin.id === pluginId).length === 1;\n    }\n _updateHoverStyles(active, lastActive, replay) {\n        const hoverOptions = this.options.hover;\n        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.datasetIndex === y.datasetIndex && x.index === y.index));\n        const deactivated = diff(lastActive, active);\n        const activated = replay ? active : diff(active, lastActive);\n        if (deactivated.length) {\n            this.updateHoverStyle(deactivated, hoverOptions.mode, false);\n        }\n        if (activated.length && hoverOptions.mode) {\n            this.updateHoverStyle(activated, hoverOptions.mode, true);\n        }\n    }\n _eventHandler(e, replay) {\n        const args = {\n            event: e,\n            replay,\n            cancelable: true,\n            inChartArea: this.isPointInArea(e)\n        };\n        const eventFilter = (plugin)=>(plugin.options.events || this.options.events).includes(e.native.type);\n        if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) {\n            return;\n        }\n        const changed = this._handleEvent(e, replay, args.inChartArea);\n        args.cancelable = false;\n        this.notifyPlugins('afterEvent', args, eventFilter);\n        if (changed || args.changed) {\n            this.render();\n        }\n        return this;\n    }\n _handleEvent(e, replay, inChartArea) {\n        const { _active: lastActive = [] , options  } = this;\n        const useFinalPosition = replay;\n        const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition);\n        const isClick = _isClickEvent(e);\n        const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick);\n        if (inChartArea) {\n            this._lastEvent = null;\n            callback(options.onHover, [\n                e,\n                active,\n                this\n            ], this);\n            if (isClick) {\n                callback(options.onClick, [\n                    e,\n                    active,\n                    this\n                ], this);\n            }\n        }\n        const changed = !_elementsEqual(active, lastActive);\n        if (changed || replay) {\n            this._active = active;\n            this._updateHoverStyles(active, lastActive, replay);\n        }\n        this._lastEvent = lastEvent;\n        return changed;\n    }\n _getActiveElements(e, lastActive, inChartArea, useFinalPosition) {\n        if (e.type === 'mouseout') {\n            return [];\n        }\n        if (!inChartArea) {\n            return lastActive;\n        }\n        const hoverOptions = this.options.hover;\n        return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);\n    }\n}\nfunction invalidatePlugins() {\n    return each(Chart.instances, (chart)=>chart._plugins.invalidate());\n}\n\nfunction clipArc(ctx, element, endAngle) {\n    const { startAngle , pixelMargin , x , y , outerRadius , innerRadius  } = element;\n    let angleMargin = pixelMargin / outerRadius;\n    // Draw an inner border by clipping the arc and drawing a double-width border\n    // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders\n    ctx.beginPath();\n    ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin);\n    if (innerRadius > pixelMargin) {\n        angleMargin = pixelMargin / innerRadius;\n        ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true);\n    } else {\n        ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI);\n    }\n    ctx.closePath();\n    ctx.clip();\n}\nfunction toRadiusCorners(value) {\n    return _readValueToProps(value, [\n        'outerStart',\n        'outerEnd',\n        'innerStart',\n        'innerEnd'\n    ]);\n}\n/**\n * Parse border radius from the provided options\n */ function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) {\n    const o = toRadiusCorners(arc.options.borderRadius);\n    const halfThickness = (outerRadius - innerRadius) / 2;\n    const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2);\n    // Outer limits are complicated. We want to compute the available angular distance at\n    // a radius of outerRadius - borderRadius because for small angular distances, this term limits.\n    // We compute at r = outerRadius - borderRadius because this circle defines the center of the border corners.\n    //\n    // If the borderRadius is large, that value can become negative.\n    // This causes the outer borders to lose their radius entirely, which is rather unexpected. To solve that, if borderRadius > outerRadius\n    // we know that the thickness term will dominate and compute the limits at that point\n    const computeOuterLimit = (val)=>{\n        const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2;\n        return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit));\n    };\n    return {\n        outerStart: computeOuterLimit(o.outerStart),\n        outerEnd: computeOuterLimit(o.outerEnd),\n        innerStart: _limitValue(o.innerStart, 0, innerLimit),\n        innerEnd: _limitValue(o.innerEnd, 0, innerLimit)\n    };\n}\n/**\n * Convert (r, 𝜃) to (x, y)\n */ function rThetaToXY(r, theta, x, y) {\n    return {\n        x: x + r * Math.cos(theta),\n        y: y + r * Math.sin(theta)\n    };\n}\n/**\n * Path the arc, respecting border radius by separating into left and right halves.\n *\n *   Start      End\n *\n *    1--->a--->2    Outer\n *   /           \\\n *   8           3\n *   |           |\n *   |           |\n *   7           4\n *   \\           /\n *    6<---b<---5    Inner\n */ function pathArc(ctx, element, offset, spacing, end, circular) {\n    const { x , y , startAngle: start , pixelMargin , innerRadius: innerR  } = element;\n    const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);\n    const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0;\n    let spacingOffset = 0;\n    const alpha = end - start;\n    if (spacing) {\n        // When spacing is present, it is the same for all items\n        // So we adjust the start and end angle of the arc such that\n        // the distance is the same as it would be without the spacing\n        const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0;\n        const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0;\n        const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2;\n        const adjustedAngle = avNogSpacingRadius !== 0 ? alpha * avNogSpacingRadius / (avNogSpacingRadius + spacing) : alpha;\n        spacingOffset = (alpha - adjustedAngle) / 2;\n    }\n    const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius;\n    const angleOffset = (alpha - beta) / 2;\n    const startAngle = start + angleOffset + spacingOffset;\n    const endAngle = end - angleOffset - spacingOffset;\n    const { outerStart , outerEnd , innerStart , innerEnd  } = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle);\n    const outerStartAdjustedRadius = outerRadius - outerStart;\n    const outerEndAdjustedRadius = outerRadius - outerEnd;\n    const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius;\n    const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius;\n    const innerStartAdjustedRadius = innerRadius + innerStart;\n    const innerEndAdjustedRadius = innerRadius + innerEnd;\n    const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius;\n    const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius;\n    ctx.beginPath();\n    if (circular) {\n        // The first arc segments from point 1 to point a to point 2\n        const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2;\n        ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle);\n        ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle);\n        // The corner segment from point 2 to point 3\n        if (outerEnd > 0) {\n            const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);\n            ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);\n        }\n        // The line from point 3 to point 4\n        const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);\n        ctx.lineTo(p4.x, p4.y);\n        // The corner segment from point 4 to point 5\n        if (innerEnd > 0) {\n            const pCenter1 = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);\n            ctx.arc(pCenter1.x, pCenter1.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);\n        }\n        // The inner arc from point 5 to point b to point 6\n        const innerMidAdjustedAngle = (endAngle - innerEnd / innerRadius + (startAngle + innerStart / innerRadius)) / 2;\n        ctx.arc(x, y, innerRadius, endAngle - innerEnd / innerRadius, innerMidAdjustedAngle, true);\n        ctx.arc(x, y, innerRadius, innerMidAdjustedAngle, startAngle + innerStart / innerRadius, true);\n        // The corner segment from point 6 to point 7\n        if (innerStart > 0) {\n            const pCenter2 = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);\n            ctx.arc(pCenter2.x, pCenter2.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);\n        }\n        // The line from point 7 to point 8\n        const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);\n        ctx.lineTo(p8.x, p8.y);\n        // The corner segment from point 8 to point 1\n        if (outerStart > 0) {\n            const pCenter3 = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);\n            ctx.arc(pCenter3.x, pCenter3.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);\n        }\n    } else {\n        ctx.moveTo(x, y);\n        const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x;\n        const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y;\n        ctx.lineTo(outerStartX, outerStartY);\n        const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x;\n        const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y;\n        ctx.lineTo(outerEndX, outerEndY);\n    }\n    ctx.closePath();\n}\nfunction drawArc(ctx, element, offset, spacing, circular) {\n    const { fullCircles , startAngle , circumference  } = element;\n    let endAngle = element.endAngle;\n    if (fullCircles) {\n        pathArc(ctx, element, offset, spacing, endAngle, circular);\n        for(let i = 0; i < fullCircles; ++i){\n            ctx.fill();\n        }\n        if (!isNaN(circumference)) {\n            endAngle = startAngle + (circumference % TAU || TAU);\n        }\n    }\n    pathArc(ctx, element, offset, spacing, endAngle, circular);\n    ctx.fill();\n    return endAngle;\n}\nfunction drawBorder(ctx, element, offset, spacing, circular) {\n    const { fullCircles , startAngle , circumference , options  } = element;\n    const { borderWidth , borderJoinStyle  } = options;\n    const inner = options.borderAlign === 'inner';\n    if (!borderWidth) {\n        return;\n    }\n    if (inner) {\n        ctx.lineWidth = borderWidth * 2;\n        ctx.lineJoin = borderJoinStyle || 'round';\n    } else {\n        ctx.lineWidth = borderWidth;\n        ctx.lineJoin = borderJoinStyle || 'bevel';\n    }\n    let endAngle = element.endAngle;\n    if (fullCircles) {\n        pathArc(ctx, element, offset, spacing, endAngle, circular);\n        for(let i = 0; i < fullCircles; ++i){\n            ctx.stroke();\n        }\n        if (!isNaN(circumference)) {\n            endAngle = startAngle + (circumference % TAU || TAU);\n        }\n    }\n    if (inner) {\n        clipArc(ctx, element, endAngle);\n    }\n    if (!fullCircles) {\n        pathArc(ctx, element, offset, spacing, endAngle, circular);\n        ctx.stroke();\n    }\n}\nclass ArcElement extends Element {\n    static id = 'arc';\n    static defaults = {\n        borderAlign: 'center',\n        borderColor: '#fff',\n        borderJoinStyle: undefined,\n        borderRadius: 0,\n        borderWidth: 2,\n        offset: 0,\n        spacing: 0,\n        angle: undefined,\n        circular: true\n    };\n    static defaultRoutes = {\n        backgroundColor: 'backgroundColor'\n    };\n    constructor(cfg){\n        super();\n        this.options = undefined;\n        this.circumference = undefined;\n        this.startAngle = undefined;\n        this.endAngle = undefined;\n        this.innerRadius = undefined;\n        this.outerRadius = undefined;\n        this.pixelMargin = 0;\n        this.fullCircles = 0;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    inRange(chartX, chartY, useFinalPosition) {\n        const point = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        const { angle , distance  } = getAngleFromPoint(point, {\n            x: chartX,\n            y: chartY\n        });\n        const { startAngle , endAngle , innerRadius , outerRadius , circumference  } = this.getProps([\n            'startAngle',\n            'endAngle',\n            'innerRadius',\n            'outerRadius',\n            'circumference'\n        ], useFinalPosition);\n        const rAdjust = this.options.spacing / 2;\n        const _circumference = valueOrDefault(circumference, endAngle - startAngle);\n        const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle);\n        const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust);\n        return betweenAngles && withinRadius;\n    }\n    getCenterPoint(useFinalPosition) {\n        const { x , y , startAngle , endAngle , innerRadius , outerRadius  } = this.getProps([\n            'x',\n            'y',\n            'startAngle',\n            'endAngle',\n            'innerRadius',\n            'outerRadius'\n        ], useFinalPosition);\n        const { offset , spacing  } = this.options;\n        const halfAngle = (startAngle + endAngle) / 2;\n        const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2;\n        return {\n            x: x + Math.cos(halfAngle) * halfRadius,\n            y: y + Math.sin(halfAngle) * halfRadius\n        };\n    }\n    tooltipPosition(useFinalPosition) {\n        return this.getCenterPoint(useFinalPosition);\n    }\n    draw(ctx) {\n        const { options , circumference  } = this;\n        const offset = (options.offset || 0) / 4;\n        const spacing = (options.spacing || 0) / 2;\n        const circular = options.circular;\n        this.pixelMargin = options.borderAlign === 'inner' ? 0.33 : 0;\n        this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;\n        if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) {\n            return;\n        }\n        ctx.save();\n        const halfAngle = (this.startAngle + this.endAngle) / 2;\n        ctx.translate(Math.cos(halfAngle) * offset, Math.sin(halfAngle) * offset);\n        const fix = 1 - Math.sin(Math.min(PI, circumference || 0));\n        const radiusOffset = offset * fix;\n        ctx.fillStyle = options.backgroundColor;\n        ctx.strokeStyle = options.borderColor;\n        drawArc(ctx, this, radiusOffset, spacing, circular);\n        drawBorder(ctx, this, radiusOffset, spacing, circular);\n        ctx.restore();\n    }\n}\n\nfunction setStyle(ctx, options, style = options) {\n    ctx.lineCap = valueOrDefault(style.borderCapStyle, options.borderCapStyle);\n    ctx.setLineDash(valueOrDefault(style.borderDash, options.borderDash));\n    ctx.lineDashOffset = valueOrDefault(style.borderDashOffset, options.borderDashOffset);\n    ctx.lineJoin = valueOrDefault(style.borderJoinStyle, options.borderJoinStyle);\n    ctx.lineWidth = valueOrDefault(style.borderWidth, options.borderWidth);\n    ctx.strokeStyle = valueOrDefault(style.borderColor, options.borderColor);\n}\nfunction lineTo(ctx, previous, target) {\n    ctx.lineTo(target.x, target.y);\n}\nfunction getLineMethod(options) {\n    if (options.stepped) {\n        return _steppedLineTo;\n    }\n    if (options.tension || options.cubicInterpolationMode === 'monotone') {\n        return _bezierCurveTo;\n    }\n    return lineTo;\n}\nfunction pathVars(points, segment, params = {}) {\n    const count = points.length;\n    const { start: paramsStart = 0 , end: paramsEnd = count - 1  } = params;\n    const { start: segmentStart , end: segmentEnd  } = segment;\n    const start = Math.max(paramsStart, segmentStart);\n    const end = Math.min(paramsEnd, segmentEnd);\n    const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd;\n    return {\n        count,\n        start,\n        loop: segment.loop,\n        ilen: end < start && !outside ? count + end - start : end - start\n    };\n}\n function pathSegment(ctx, line, segment, params) {\n    const { points , options  } = line;\n    const { count , start , loop , ilen  } = pathVars(points, segment, params);\n    const lineMethod = getLineMethod(options);\n    let { move =true , reverse  } = params || {};\n    let i, point, prev;\n    for(i = 0; i <= ilen; ++i){\n        point = points[(start + (reverse ? ilen - i : i)) % count];\n        if (point.skip) {\n            continue;\n        } else if (move) {\n            ctx.moveTo(point.x, point.y);\n            move = false;\n        } else {\n            lineMethod(ctx, prev, point, reverse, options.stepped);\n        }\n        prev = point;\n    }\n    if (loop) {\n        point = points[(start + (reverse ? ilen : 0)) % count];\n        lineMethod(ctx, prev, point, reverse, options.stepped);\n    }\n    return !!loop;\n}\n function fastPathSegment(ctx, line, segment, params) {\n    const points = line.points;\n    const { count , start , ilen  } = pathVars(points, segment, params);\n    const { move =true , reverse  } = params || {};\n    let avgX = 0;\n    let countX = 0;\n    let i, point, prevX, minY, maxY, lastY;\n    const pointIndex = (index)=>(start + (reverse ? ilen - index : index)) % count;\n    const drawX = ()=>{\n        if (minY !== maxY) {\n            ctx.lineTo(avgX, maxY);\n            ctx.lineTo(avgX, minY);\n            ctx.lineTo(avgX, lastY);\n        }\n    };\n    if (move) {\n        point = points[pointIndex(0)];\n        ctx.moveTo(point.x, point.y);\n    }\n    for(i = 0; i <= ilen; ++i){\n        point = points[pointIndex(i)];\n        if (point.skip) {\n            continue;\n        }\n        const x = point.x;\n        const y = point.y;\n        const truncX = x | 0;\n        if (truncX === prevX) {\n            if (y < minY) {\n                minY = y;\n            } else if (y > maxY) {\n                maxY = y;\n            }\n            avgX = (countX * avgX + x) / ++countX;\n        } else {\n            drawX();\n            ctx.lineTo(x, y);\n            prevX = truncX;\n            countX = 0;\n            minY = maxY = y;\n        }\n        lastY = y;\n    }\n    drawX();\n}\n function _getSegmentMethod(line) {\n    const opts = line.options;\n    const borderDash = opts.borderDash && opts.borderDash.length;\n    const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== 'monotone' && !opts.stepped && !borderDash;\n    return useFastPath ? fastPathSegment : pathSegment;\n}\n function _getInterpolationMethod(options) {\n    if (options.stepped) {\n        return _steppedInterpolation;\n    }\n    if (options.tension || options.cubicInterpolationMode === 'monotone') {\n        return _bezierInterpolation;\n    }\n    return _pointInLine;\n}\nfunction strokePathWithCache(ctx, line, start, count) {\n    let path = line._path;\n    if (!path) {\n        path = line._path = new Path2D();\n        if (line.path(path, start, count)) {\n            path.closePath();\n        }\n    }\n    setStyle(ctx, line.options);\n    ctx.stroke(path);\n}\nfunction strokePathDirect(ctx, line, start, count) {\n    const { segments , options  } = line;\n    const segmentMethod = _getSegmentMethod(line);\n    for (const segment of segments){\n        setStyle(ctx, options, segment.style);\n        ctx.beginPath();\n        if (segmentMethod(ctx, line, segment, {\n            start,\n            end: start + count - 1\n        })) {\n            ctx.closePath();\n        }\n        ctx.stroke();\n    }\n}\nconst usePath2D = typeof Path2D === 'function';\nfunction draw(ctx, line, start, count) {\n    if (usePath2D && !line.options.segment) {\n        strokePathWithCache(ctx, line, start, count);\n    } else {\n        strokePathDirect(ctx, line, start, count);\n    }\n}\nclass LineElement extends Element {\n    static id = 'line';\n static defaults = {\n        borderCapStyle: 'butt',\n        borderDash: [],\n        borderDashOffset: 0,\n        borderJoinStyle: 'miter',\n        borderWidth: 3,\n        capBezierPoints: true,\n        cubicInterpolationMode: 'default',\n        fill: false,\n        spanGaps: false,\n        stepped: false,\n        tension: 0\n    };\n static defaultRoutes = {\n        backgroundColor: 'backgroundColor',\n        borderColor: 'borderColor'\n    };\n    static descriptors = {\n        _scriptable: true,\n        _indexable: (name)=>name !== 'borderDash' && name !== 'fill'\n    };\n    constructor(cfg){\n        super();\n        this.animated = true;\n        this.options = undefined;\n        this._chart = undefined;\n        this._loop = undefined;\n        this._fullLoop = undefined;\n        this._path = undefined;\n        this._points = undefined;\n        this._segments = undefined;\n        this._decimated = false;\n        this._pointsUpdated = false;\n        this._datasetIndex = undefined;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    updateControlPoints(chartArea, indexAxis) {\n        const options = this.options;\n        if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !this._pointsUpdated) {\n            const loop = options.spanGaps ? this._loop : this._fullLoop;\n            _updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);\n            this._pointsUpdated = true;\n        }\n    }\n    set points(points) {\n        this._points = points;\n        delete this._segments;\n        delete this._path;\n        this._pointsUpdated = false;\n    }\n    get points() {\n        return this._points;\n    }\n    get segments() {\n        return this._segments || (this._segments = _computeSegments(this, this.options.segment));\n    }\n first() {\n        const segments = this.segments;\n        const points = this.points;\n        return segments.length && points[segments[0].start];\n    }\n last() {\n        const segments = this.segments;\n        const points = this.points;\n        const count = segments.length;\n        return count && points[segments[count - 1].end];\n    }\n interpolate(point, property) {\n        const options = this.options;\n        const value = point[property];\n        const points = this.points;\n        const segments = _boundSegments(this, {\n            property,\n            start: value,\n            end: value\n        });\n        if (!segments.length) {\n            return;\n        }\n        const result = [];\n        const _interpolate = _getInterpolationMethod(options);\n        let i, ilen;\n        for(i = 0, ilen = segments.length; i < ilen; ++i){\n            const { start , end  } = segments[i];\n            const p1 = points[start];\n            const p2 = points[end];\n            if (p1 === p2) {\n                result.push(p1);\n                continue;\n            }\n            const t = Math.abs((value - p1[property]) / (p2[property] - p1[property]));\n            const interpolated = _interpolate(p1, p2, t, options.stepped);\n            interpolated[property] = point[property];\n            result.push(interpolated);\n        }\n        return result.length === 1 ? result[0] : result;\n    }\n pathSegment(ctx, segment, params) {\n        const segmentMethod = _getSegmentMethod(this);\n        return segmentMethod(ctx, this, segment, params);\n    }\n path(ctx, start, count) {\n        const segments = this.segments;\n        const segmentMethod = _getSegmentMethod(this);\n        let loop = this._loop;\n        start = start || 0;\n        count = count || this.points.length - start;\n        for (const segment of segments){\n            loop &= segmentMethod(ctx, this, segment, {\n                start,\n                end: start + count - 1\n            });\n        }\n        return !!loop;\n    }\n draw(ctx, chartArea, start, count) {\n        const options = this.options || {};\n        const points = this.points || [];\n        if (points.length && options.borderWidth) {\n            ctx.save();\n            draw(ctx, this, start, count);\n            ctx.restore();\n        }\n        if (this.animated) {\n            this._pointsUpdated = false;\n            this._path = undefined;\n        }\n    }\n}\n\nfunction inRange$1(el, pos, axis, useFinalPosition) {\n    const options = el.options;\n    const { [axis]: value  } = el.getProps([\n        axis\n    ], useFinalPosition);\n    return Math.abs(pos - value) < options.radius + options.hitRadius;\n}\nclass PointElement extends Element {\n    static id = 'point';\n    /**\n   * @type {any}\n   */ static defaults = {\n        borderWidth: 1,\n        hitRadius: 1,\n        hoverBorderWidth: 1,\n        hoverRadius: 4,\n        pointStyle: 'circle',\n        radius: 3,\n        rotation: 0\n    };\n    /**\n   * @type {any}\n   */ static defaultRoutes = {\n        backgroundColor: 'backgroundColor',\n        borderColor: 'borderColor'\n    };\n    constructor(cfg){\n        super();\n        this.options = undefined;\n        this.parsed = undefined;\n        this.skip = undefined;\n        this.stop = undefined;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    inRange(mouseX, mouseY, useFinalPosition) {\n        const options = this.options;\n        const { x , y  } = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        return Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2) < Math.pow(options.hitRadius + options.radius, 2);\n    }\n    inXRange(mouseX, useFinalPosition) {\n        return inRange$1(this, mouseX, 'x', useFinalPosition);\n    }\n    inYRange(mouseY, useFinalPosition) {\n        return inRange$1(this, mouseY, 'y', useFinalPosition);\n    }\n    getCenterPoint(useFinalPosition) {\n        const { x , y  } = this.getProps([\n            'x',\n            'y'\n        ], useFinalPosition);\n        return {\n            x,\n            y\n        };\n    }\n    size(options) {\n        options = options || this.options || {};\n        let radius = options.radius || 0;\n        radius = Math.max(radius, radius && options.hoverRadius || 0);\n        const borderWidth = radius && options.borderWidth || 0;\n        return (radius + borderWidth) * 2;\n    }\n    draw(ctx, area) {\n        const options = this.options;\n        if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) {\n            return;\n        }\n        ctx.strokeStyle = options.borderColor;\n        ctx.lineWidth = options.borderWidth;\n        ctx.fillStyle = options.backgroundColor;\n        drawPoint(ctx, options, this.x, this.y);\n    }\n    getRange() {\n        const options = this.options || {};\n        // @ts-expect-error Fallbacks should never be hit in practice\n        return options.radius + options.hitRadius;\n    }\n}\n\nfunction getBarBounds(bar, useFinalPosition) {\n    const { x , y , base , width , height  } =  bar.getProps([\n        'x',\n        'y',\n        'base',\n        'width',\n        'height'\n    ], useFinalPosition);\n    let left, right, top, bottom, half;\n    if (bar.horizontal) {\n        half = height / 2;\n        left = Math.min(x, base);\n        right = Math.max(x, base);\n        top = y - half;\n        bottom = y + half;\n    } else {\n        half = width / 2;\n        left = x - half;\n        right = x + half;\n        top = Math.min(y, base);\n        bottom = Math.max(y, base);\n    }\n    return {\n        left,\n        top,\n        right,\n        bottom\n    };\n}\nfunction skipOrLimit(skip, value, min, max) {\n    return skip ? 0 : _limitValue(value, min, max);\n}\nfunction parseBorderWidth(bar, maxW, maxH) {\n    const value = bar.options.borderWidth;\n    const skip = bar.borderSkipped;\n    const o = toTRBL(value);\n    return {\n        t: skipOrLimit(skip.top, o.top, 0, maxH),\n        r: skipOrLimit(skip.right, o.right, 0, maxW),\n        b: skipOrLimit(skip.bottom, o.bottom, 0, maxH),\n        l: skipOrLimit(skip.left, o.left, 0, maxW)\n    };\n}\nfunction parseBorderRadius(bar, maxW, maxH) {\n    const { enableBorderRadius  } = bar.getProps([\n        'enableBorderRadius'\n    ]);\n    const value = bar.options.borderRadius;\n    const o = toTRBLCorners(value);\n    const maxR = Math.min(maxW, maxH);\n    const skip = bar.borderSkipped;\n    const enableBorder = enableBorderRadius || isObject(value);\n    return {\n        topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),\n        topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),\n        bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),\n        bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)\n    };\n}\nfunction boundingRects(bar) {\n    const bounds = getBarBounds(bar);\n    const width = bounds.right - bounds.left;\n    const height = bounds.bottom - bounds.top;\n    const border = parseBorderWidth(bar, width / 2, height / 2);\n    const radius = parseBorderRadius(bar, width / 2, height / 2);\n    return {\n        outer: {\n            x: bounds.left,\n            y: bounds.top,\n            w: width,\n            h: height,\n            radius\n        },\n        inner: {\n            x: bounds.left + border.l,\n            y: bounds.top + border.t,\n            w: width - border.l - border.r,\n            h: height - border.t - border.b,\n            radius: {\n                topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),\n                topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),\n                bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),\n                bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r))\n            }\n        }\n    };\n}\nfunction inRange(bar, x, y, useFinalPosition) {\n    const skipX = x === null;\n    const skipY = y === null;\n    const skipBoth = skipX && skipY;\n    const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);\n    return bounds && (skipX || _isBetween(x, bounds.left, bounds.right)) && (skipY || _isBetween(y, bounds.top, bounds.bottom));\n}\nfunction hasRadius(radius) {\n    return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;\n}\n function addNormalRectPath(ctx, rect) {\n    ctx.rect(rect.x, rect.y, rect.w, rect.h);\n}\nfunction inflateRect(rect, amount, refRect = {}) {\n    const x = rect.x !== refRect.x ? -amount : 0;\n    const y = rect.y !== refRect.y ? -amount : 0;\n    const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x;\n    const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y;\n    return {\n        x: rect.x + x,\n        y: rect.y + y,\n        w: rect.w + w,\n        h: rect.h + h,\n        radius: rect.radius\n    };\n}\nclass BarElement extends Element {\n    static id = 'bar';\n static defaults = {\n        borderSkipped: 'start',\n        borderWidth: 0,\n        borderRadius: 0,\n        inflateAmount: 'auto',\n        pointStyle: undefined\n    };\n static defaultRoutes = {\n        backgroundColor: 'backgroundColor',\n        borderColor: 'borderColor'\n    };\n    constructor(cfg){\n        super();\n        this.options = undefined;\n        this.horizontal = undefined;\n        this.base = undefined;\n        this.width = undefined;\n        this.height = undefined;\n        this.inflateAmount = undefined;\n        if (cfg) {\n            Object.assign(this, cfg);\n        }\n    }\n    draw(ctx) {\n        const { inflateAmount , options: { borderColor , backgroundColor  }  } = this;\n        const { inner , outer  } = boundingRects(this);\n        const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath;\n        ctx.save();\n        if (outer.w !== inner.w || outer.h !== inner.h) {\n            ctx.beginPath();\n            addRectPath(ctx, inflateRect(outer, inflateAmount, inner));\n            ctx.clip();\n            addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));\n            ctx.fillStyle = borderColor;\n            ctx.fill('evenodd');\n        }\n        ctx.beginPath();\n        addRectPath(ctx, inflateRect(inner, inflateAmount));\n        ctx.fillStyle = backgroundColor;\n        ctx.fill();\n        ctx.restore();\n    }\n    inRange(mouseX, mouseY, useFinalPosition) {\n        return inRange(this, mouseX, mouseY, useFinalPosition);\n    }\n    inXRange(mouseX, useFinalPosition) {\n        return inRange(this, mouseX, null, useFinalPosition);\n    }\n    inYRange(mouseY, useFinalPosition) {\n        return inRange(this, null, mouseY, useFinalPosition);\n    }\n    getCenterPoint(useFinalPosition) {\n        const { x , y , base , horizontal  } =  this.getProps([\n            'x',\n            'y',\n            'base',\n            'horizontal'\n        ], useFinalPosition);\n        return {\n            x: horizontal ? (x + base) / 2 : x,\n            y: horizontal ? y : (y + base) / 2\n        };\n    }\n    getRange(axis) {\n        return axis === 'x' ? this.width / 2 : this.height / 2;\n    }\n}\n\nvar elements = /*#__PURE__*/Object.freeze({\n__proto__: null,\nArcElement: ArcElement,\nLineElement: LineElement,\nPointElement: PointElement,\nBarElement: BarElement\n});\n\nconst BORDER_COLORS = [\n    'rgb(54, 162, 235)',\n    'rgb(255, 99, 132)',\n    'rgb(255, 159, 64)',\n    'rgb(255, 205, 86)',\n    'rgb(75, 192, 192)',\n    'rgb(153, 102, 255)',\n    'rgb(201, 203, 207)' // grey\n];\n// Border colors with 50% transparency\nconst BACKGROUND_COLORS = /* #__PURE__ */ BORDER_COLORS.map((color)=>color.replace('rgb(', 'rgba(').replace(')', ', 0.5)'));\nfunction getBorderColor(i) {\n    return BORDER_COLORS[i % BORDER_COLORS.length];\n}\nfunction getBackgroundColor(i) {\n    return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length];\n}\nfunction colorizeDefaultDataset(dataset, i) {\n    dataset.borderColor = getBorderColor(i);\n    dataset.backgroundColor = getBackgroundColor(i);\n    return ++i;\n}\nfunction colorizeDoughnutDataset(dataset, i) {\n    dataset.backgroundColor = dataset.data.map(()=>getBorderColor(i++));\n    return i;\n}\nfunction colorizePolarAreaDataset(dataset, i) {\n    dataset.backgroundColor = dataset.data.map(()=>getBackgroundColor(i++));\n    return i;\n}\nfunction getColorizer(chart) {\n    let i = 0;\n    return (dataset, datasetIndex)=>{\n        const controller = chart.getDatasetMeta(datasetIndex).controller;\n        if (controller instanceof DoughnutController) {\n            i = colorizeDoughnutDataset(dataset, i);\n        } else if (controller instanceof PolarAreaController) {\n            i = colorizePolarAreaDataset(dataset, i);\n        } else if (controller) {\n            i = colorizeDefaultDataset(dataset, i);\n        }\n    };\n}\nfunction containsColorsDefinitions(descriptors) {\n    let k;\n    for(k in descriptors){\n        if (descriptors[k].borderColor || descriptors[k].backgroundColor) {\n            return true;\n        }\n    }\n    return false;\n}\nfunction containsColorsDefinition(descriptor) {\n    return descriptor && (descriptor.borderColor || descriptor.backgroundColor);\n}\nvar plugin_colors = {\n    id: 'colors',\n    defaults: {\n        enabled: true,\n        forceOverride: false\n    },\n    beforeLayout (chart, _args, options) {\n        if (!options.enabled) {\n            return;\n        }\n        const { data: { datasets  } , options: chartOptions  } = chart.config;\n        const { elements  } = chartOptions;\n        if (!options.forceOverride && (containsColorsDefinitions(datasets) || containsColorsDefinition(chartOptions) || elements && containsColorsDefinitions(elements))) {\n            return;\n        }\n        const colorizer = getColorizer(chart);\n        datasets.forEach(colorizer);\n    }\n};\n\nfunction lttbDecimation(data, start, count, availableWidth, options) {\n const samples = options.samples || availableWidth;\n    if (samples >= count) {\n        return data.slice(start, start + count);\n    }\n    const decimated = [];\n    const bucketWidth = (count - 2) / (samples - 2);\n    let sampledIndex = 0;\n    const endIndex = start + count - 1;\n    let a = start;\n    let i, maxAreaPoint, maxArea, area, nextA;\n    decimated[sampledIndex++] = data[a];\n    for(i = 0; i < samples - 2; i++){\n        let avgX = 0;\n        let avgY = 0;\n        let j;\n        const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start;\n        const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start;\n        const avgRangeLength = avgRangeEnd - avgRangeStart;\n        for(j = avgRangeStart; j < avgRangeEnd; j++){\n            avgX += data[j].x;\n            avgY += data[j].y;\n        }\n        avgX /= avgRangeLength;\n        avgY /= avgRangeLength;\n        const rangeOffs = Math.floor(i * bucketWidth) + 1 + start;\n        const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start;\n        const { x: pointAx , y: pointAy  } = data[a];\n        maxArea = area = -1;\n        for(j = rangeOffs; j < rangeTo; j++){\n            area = 0.5 * Math.abs((pointAx - avgX) * (data[j].y - pointAy) - (pointAx - data[j].x) * (avgY - pointAy));\n            if (area > maxArea) {\n                maxArea = area;\n                maxAreaPoint = data[j];\n                nextA = j;\n            }\n        }\n        decimated[sampledIndex++] = maxAreaPoint;\n        a = nextA;\n    }\n    decimated[sampledIndex++] = data[endIndex];\n    return decimated;\n}\nfunction minMaxDecimation(data, start, count, availableWidth) {\n    let avgX = 0;\n    let countX = 0;\n    let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY;\n    const decimated = [];\n    const endIndex = start + count - 1;\n    const xMin = data[start].x;\n    const xMax = data[endIndex].x;\n    const dx = xMax - xMin;\n    for(i = start; i < start + count; ++i){\n        point = data[i];\n        x = (point.x - xMin) / dx * availableWidth;\n        y = point.y;\n        const truncX = x | 0;\n        if (truncX === prevX) {\n            if (y < minY) {\n                minY = y;\n                minIndex = i;\n            } else if (y > maxY) {\n                maxY = y;\n                maxIndex = i;\n            }\n            avgX = (countX * avgX + point.x) / ++countX;\n        } else {\n            const lastIndex = i - 1;\n            if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) {\n                const intermediateIndex1 = Math.min(minIndex, maxIndex);\n                const intermediateIndex2 = Math.max(minIndex, maxIndex);\n                if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) {\n                    decimated.push({\n                        ...data[intermediateIndex1],\n                        x: avgX\n                    });\n                }\n                if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) {\n                    decimated.push({\n                        ...data[intermediateIndex2],\n                        x: avgX\n                    });\n                }\n            }\n            if (i > 0 && lastIndex !== startIndex) {\n                decimated.push(data[lastIndex]);\n            }\n            decimated.push(point);\n            prevX = truncX;\n            countX = 0;\n            minY = maxY = y;\n            minIndex = maxIndex = startIndex = i;\n        }\n    }\n    return decimated;\n}\nfunction cleanDecimatedDataset(dataset) {\n    if (dataset._decimated) {\n        const data = dataset._data;\n        delete dataset._decimated;\n        delete dataset._data;\n        Object.defineProperty(dataset, 'data', {\n            configurable: true,\n            enumerable: true,\n            writable: true,\n            value: data\n        });\n    }\n}\nfunction cleanDecimatedData(chart) {\n    chart.data.datasets.forEach((dataset)=>{\n        cleanDecimatedDataset(dataset);\n    });\n}\nfunction getStartAndCountOfVisiblePointsSimplified(meta, points) {\n    const pointCount = points.length;\n    let start = 0;\n    let count;\n    const { iScale  } = meta;\n    const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();\n    if (minDefined) {\n        start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1);\n    }\n    if (maxDefined) {\n        count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start;\n    } else {\n        count = pointCount - start;\n    }\n    return {\n        start,\n        count\n    };\n}\nvar plugin_decimation = {\n    id: 'decimation',\n    defaults: {\n        algorithm: 'min-max',\n        enabled: false\n    },\n    beforeElementsUpdate: (chart, args, options)=>{\n        if (!options.enabled) {\n            cleanDecimatedData(chart);\n            return;\n        }\n        const availableWidth = chart.width;\n        chart.data.datasets.forEach((dataset, datasetIndex)=>{\n            const { _data , indexAxis  } = dataset;\n            const meta = chart.getDatasetMeta(datasetIndex);\n            const data = _data || dataset.data;\n            if (resolve([\n                indexAxis,\n                chart.options.indexAxis\n            ]) === 'y') {\n                return;\n            }\n            if (!meta.controller.supportsDecimation) {\n                return;\n            }\n            const xAxis = chart.scales[meta.xAxisID];\n            if (xAxis.type !== 'linear' && xAxis.type !== 'time') {\n                return;\n            }\n            if (chart.options.parsing) {\n                return;\n            }\n            let { start , count  } = getStartAndCountOfVisiblePointsSimplified(meta, data);\n            const threshold = options.threshold || 4 * availableWidth;\n            if (count <= threshold) {\n                cleanDecimatedDataset(dataset);\n                return;\n            }\n            if (isNullOrUndef(_data)) {\n                dataset._data = data;\n                delete dataset.data;\n                Object.defineProperty(dataset, 'data', {\n                    configurable: true,\n                    enumerable: true,\n                    get: function() {\n                        return this._decimated;\n                    },\n                    set: function(d) {\n                        this._data = d;\n                    }\n                });\n            }\n            let decimated;\n            switch(options.algorithm){\n                case 'lttb':\n                    decimated = lttbDecimation(data, start, count, availableWidth, options);\n                    break;\n                case 'min-max':\n                    decimated = minMaxDecimation(data, start, count, availableWidth);\n                    break;\n                default:\n                    throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`);\n            }\n            dataset._decimated = decimated;\n        });\n    },\n    destroy (chart) {\n        cleanDecimatedData(chart);\n    }\n};\n\nfunction _segments(line, target, property) {\n    const segments = line.segments;\n    const points = line.points;\n    const tpoints = target.points;\n    const parts = [];\n    for (const segment of segments){\n        let { start , end  } = segment;\n        end = _findSegmentEnd(start, end, points);\n        const bounds = _getBounds(property, points[start], points[end], segment.loop);\n        if (!target.segments) {\n            parts.push({\n                source: segment,\n                target: bounds,\n                start: points[start],\n                end: points[end]\n            });\n            continue;\n        }\n        const targetSegments = _boundSegments(target, bounds);\n        for (const tgt of targetSegments){\n            const subBounds = _getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop);\n            const fillSources = _boundSegment(segment, points, subBounds);\n            for (const fillSource of fillSources){\n                parts.push({\n                    source: fillSource,\n                    target: tgt,\n                    start: {\n                        [property]: _getEdge(bounds, subBounds, 'start', Math.max)\n                    },\n                    end: {\n                        [property]: _getEdge(bounds, subBounds, 'end', Math.min)\n                    }\n                });\n            }\n        }\n    }\n    return parts;\n}\nfunction _getBounds(property, first, last, loop) {\n    if (loop) {\n        return;\n    }\n    let start = first[property];\n    let end = last[property];\n    if (property === 'angle') {\n        start = _normalizeAngle(start);\n        end = _normalizeAngle(end);\n    }\n    return {\n        property,\n        start,\n        end\n    };\n}\nfunction _pointsFromSegments(boundary, line) {\n    const { x =null , y =null  } = boundary || {};\n    const linePoints = line.points;\n    const points = [];\n    line.segments.forEach(({ start , end  })=>{\n        end = _findSegmentEnd(start, end, linePoints);\n        const first = linePoints[start];\n        const last = linePoints[end];\n        if (y !== null) {\n            points.push({\n                x: first.x,\n                y\n            });\n            points.push({\n                x: last.x,\n                y\n            });\n        } else if (x !== null) {\n            points.push({\n                x,\n                y: first.y\n            });\n            points.push({\n                x,\n                y: last.y\n            });\n        }\n    });\n    return points;\n}\nfunction _findSegmentEnd(start, end, points) {\n    for(; end > start; end--){\n        const point = points[end];\n        if (!isNaN(point.x) && !isNaN(point.y)) {\n            break;\n        }\n    }\n    return end;\n}\nfunction _getEdge(a, b, prop, fn) {\n    if (a && b) {\n        return fn(a[prop], b[prop]);\n    }\n    return a ? a[prop] : b ? b[prop] : 0;\n}\n\nfunction _createBoundaryLine(boundary, line) {\n    let points = [];\n    let _loop = false;\n    if (isArray(boundary)) {\n        _loop = true;\n        points = boundary;\n    } else {\n        points = _pointsFromSegments(boundary, line);\n    }\n    return points.length ? new LineElement({\n        points,\n        options: {\n            tension: 0\n        },\n        _loop,\n        _fullLoop: _loop\n    }) : null;\n}\nfunction _shouldApplyFill(source) {\n    return source && source.fill !== false;\n}\n\nfunction _resolveTarget(sources, index, propagate) {\n    const source = sources[index];\n    let fill = source.fill;\n    const visited = [\n        index\n    ];\n    let target;\n    if (!propagate) {\n        return fill;\n    }\n    while(fill !== false && visited.indexOf(fill) === -1){\n        if (!isNumberFinite(fill)) {\n            return fill;\n        }\n        target = sources[fill];\n        if (!target) {\n            return false;\n        }\n        if (target.visible) {\n            return fill;\n        }\n        visited.push(fill);\n        fill = target.fill;\n    }\n    return false;\n}\n function _decodeFill(line, index, count) {\n     const fill = parseFillOption(line);\n    if (isObject(fill)) {\n        return isNaN(fill.value) ? false : fill;\n    }\n    let target = parseFloat(fill);\n    if (isNumberFinite(target) && Math.floor(target) === target) {\n        return decodeTargetIndex(fill[0], index, target, count);\n    }\n    return [\n        'origin',\n        'start',\n        'end',\n        'stack',\n        'shape'\n    ].indexOf(fill) >= 0 && fill;\n}\nfunction decodeTargetIndex(firstCh, index, target, count) {\n    if (firstCh === '-' || firstCh === '+') {\n        target = index + target;\n    }\n    if (target === index || target < 0 || target >= count) {\n        return false;\n    }\n    return target;\n}\n function _getTargetPixel(fill, scale) {\n    let pixel = null;\n    if (fill === 'start') {\n        pixel = scale.bottom;\n    } else if (fill === 'end') {\n        pixel = scale.top;\n    } else if (isObject(fill)) {\n        pixel = scale.getPixelForValue(fill.value);\n    } else if (scale.getBasePixel) {\n        pixel = scale.getBasePixel();\n    }\n    return pixel;\n}\n function _getTargetValue(fill, scale, startValue) {\n    let value;\n    if (fill === 'start') {\n        value = startValue;\n    } else if (fill === 'end') {\n        value = scale.options.reverse ? scale.min : scale.max;\n    } else if (isObject(fill)) {\n        value = fill.value;\n    } else {\n        value = scale.getBaseValue();\n    }\n    return value;\n}\n function parseFillOption(line) {\n    const options = line.options;\n    const fillOption = options.fill;\n    let fill = valueOrDefault(fillOption && fillOption.target, fillOption);\n    if (fill === undefined) {\n        fill = !!options.backgroundColor;\n    }\n    if (fill === false || fill === null) {\n        return false;\n    }\n    if (fill === true) {\n        return 'origin';\n    }\n    return fill;\n}\n\nfunction _buildStackLine(source) {\n    const { scale , index , line  } = source;\n    const points = [];\n    const segments = line.segments;\n    const sourcePoints = line.points;\n    const linesBelow = getLinesBelow(scale, index);\n    linesBelow.push(_createBoundaryLine({\n        x: null,\n        y: scale.bottom\n    }, line));\n    for(let i = 0; i < segments.length; i++){\n        const segment = segments[i];\n        for(let j = segment.start; j <= segment.end; j++){\n            addPointsBelow(points, sourcePoints[j], linesBelow);\n        }\n    }\n    return new LineElement({\n        points,\n        options: {}\n    });\n}\n function getLinesBelow(scale, index) {\n    const below = [];\n    const metas = scale.getMatchingVisibleMetas('line');\n    for(let i = 0; i < metas.length; i++){\n        const meta = metas[i];\n        if (meta.index === index) {\n            break;\n        }\n        if (!meta.hidden) {\n            below.unshift(meta.dataset);\n        }\n    }\n    return below;\n}\n function addPointsBelow(points, sourcePoint, linesBelow) {\n    const postponed = [];\n    for(let j = 0; j < linesBelow.length; j++){\n        const line = linesBelow[j];\n        const { first , last , point  } = findPoint(line, sourcePoint, 'x');\n        if (!point || first && last) {\n            continue;\n        }\n        if (first) {\n            postponed.unshift(point);\n        } else {\n            points.push(point);\n            if (!last) {\n                break;\n            }\n        }\n    }\n    points.push(...postponed);\n}\n function findPoint(line, sourcePoint, property) {\n    const point = line.interpolate(sourcePoint, property);\n    if (!point) {\n        return {};\n    }\n    const pointValue = point[property];\n    const segments = line.segments;\n    const linePoints = line.points;\n    let first = false;\n    let last = false;\n    for(let i = 0; i < segments.length; i++){\n        const segment = segments[i];\n        const firstValue = linePoints[segment.start][property];\n        const lastValue = linePoints[segment.end][property];\n        if (_isBetween(pointValue, firstValue, lastValue)) {\n            first = pointValue === firstValue;\n            last = pointValue === lastValue;\n            break;\n        }\n    }\n    return {\n        first,\n        last,\n        point\n    };\n}\n\nclass simpleArc {\n    constructor(opts){\n        this.x = opts.x;\n        this.y = opts.y;\n        this.radius = opts.radius;\n    }\n    pathSegment(ctx, bounds, opts) {\n        const { x , y , radius  } = this;\n        bounds = bounds || {\n            start: 0,\n            end: TAU\n        };\n        ctx.arc(x, y, radius, bounds.end, bounds.start, true);\n        return !opts.bounds;\n    }\n    interpolate(point) {\n        const { x , y , radius  } = this;\n        const angle = point.angle;\n        return {\n            x: x + Math.cos(angle) * radius,\n            y: y + Math.sin(angle) * radius,\n            angle\n        };\n    }\n}\n\nfunction _getTarget(source) {\n    const { chart , fill , line  } = source;\n    if (isNumberFinite(fill)) {\n        return getLineByIndex(chart, fill);\n    }\n    if (fill === 'stack') {\n        return _buildStackLine(source);\n    }\n    if (fill === 'shape') {\n        return true;\n    }\n    const boundary = computeBoundary(source);\n    if (boundary instanceof simpleArc) {\n        return boundary;\n    }\n    return _createBoundaryLine(boundary, line);\n}\n function getLineByIndex(chart, index) {\n    const meta = chart.getDatasetMeta(index);\n    const visible = meta && chart.isDatasetVisible(index);\n    return visible ? meta.dataset : null;\n}\nfunction computeBoundary(source) {\n    const scale = source.scale || {};\n    if (scale.getPointPositionForValue) {\n        return computeCircularBoundary(source);\n    }\n    return computeLinearBoundary(source);\n}\nfunction computeLinearBoundary(source) {\n    const { scale ={} , fill  } = source;\n    const pixel = _getTargetPixel(fill, scale);\n    if (isNumberFinite(pixel)) {\n        const horizontal = scale.isHorizontal();\n        return {\n            x: horizontal ? pixel : null,\n            y: horizontal ? null : pixel\n        };\n    }\n    return null;\n}\nfunction computeCircularBoundary(source) {\n    const { scale , fill  } = source;\n    const options = scale.options;\n    const length = scale.getLabels().length;\n    const start = options.reverse ? scale.max : scale.min;\n    const value = _getTargetValue(fill, scale, start);\n    const target = [];\n    if (options.grid.circular) {\n        const center = scale.getPointPositionForValue(0, start);\n        return new simpleArc({\n            x: center.x,\n            y: center.y,\n            radius: scale.getDistanceFromCenterForValue(value)\n        });\n    }\n    for(let i = 0; i < length; ++i){\n        target.push(scale.getPointPositionForValue(i, value));\n    }\n    return target;\n}\n\nfunction _drawfill(ctx, source, area) {\n    const target = _getTarget(source);\n    const { line , scale , axis  } = source;\n    const lineOpts = line.options;\n    const fillOption = lineOpts.fill;\n    const color = lineOpts.backgroundColor;\n    const { above =color , below =color  } = fillOption || {};\n    if (target && line.points.length) {\n        clipArea(ctx, area);\n        doFill(ctx, {\n            line,\n            target,\n            above,\n            below,\n            area,\n            scale,\n            axis\n        });\n        unclipArea(ctx);\n    }\n}\nfunction doFill(ctx, cfg) {\n    const { line , target , above , below , area , scale  } = cfg;\n    const property = line._loop ? 'angle' : cfg.axis;\n    ctx.save();\n    if (property === 'x' && below !== above) {\n        clipVertical(ctx, target, area.top);\n        fill(ctx, {\n            line,\n            target,\n            color: above,\n            scale,\n            property\n        });\n        ctx.restore();\n        ctx.save();\n        clipVertical(ctx, target, area.bottom);\n    }\n    fill(ctx, {\n        line,\n        target,\n        color: below,\n        scale,\n        property\n    });\n    ctx.restore();\n}\nfunction clipVertical(ctx, target, clipY) {\n    const { segments , points  } = target;\n    let first = true;\n    let lineLoop = false;\n    ctx.beginPath();\n    for (const segment of segments){\n        const { start , end  } = segment;\n        const firstPoint = points[start];\n        const lastPoint = points[_findSegmentEnd(start, end, points)];\n        if (first) {\n            ctx.moveTo(firstPoint.x, firstPoint.y);\n            first = false;\n        } else {\n            ctx.lineTo(firstPoint.x, clipY);\n            ctx.lineTo(firstPoint.x, firstPoint.y);\n        }\n        lineLoop = !!target.pathSegment(ctx, segment, {\n            move: lineLoop\n        });\n        if (lineLoop) {\n            ctx.closePath();\n        } else {\n            ctx.lineTo(lastPoint.x, clipY);\n        }\n    }\n    ctx.lineTo(target.first().x, clipY);\n    ctx.closePath();\n    ctx.clip();\n}\nfunction fill(ctx, cfg) {\n    const { line , target , property , color , scale  } = cfg;\n    const segments = _segments(line, target, property);\n    for (const { source: src , target: tgt , start , end  } of segments){\n        const { style: { backgroundColor =color  } = {}  } = src;\n        const notShape = target !== true;\n        ctx.save();\n        ctx.fillStyle = backgroundColor;\n        clipBounds(ctx, scale, notShape && _getBounds(property, start, end));\n        ctx.beginPath();\n        const lineLoop = !!line.pathSegment(ctx, src);\n        let loop;\n        if (notShape) {\n            if (lineLoop) {\n                ctx.closePath();\n            } else {\n                interpolatedLineTo(ctx, target, end, property);\n            }\n            const targetLoop = !!target.pathSegment(ctx, tgt, {\n                move: lineLoop,\n                reverse: true\n            });\n            loop = lineLoop && targetLoop;\n            if (!loop) {\n                interpolatedLineTo(ctx, target, start, property);\n            }\n        }\n        ctx.closePath();\n        ctx.fill(loop ? 'evenodd' : 'nonzero');\n        ctx.restore();\n    }\n}\nfunction clipBounds(ctx, scale, bounds) {\n    const { top , bottom  } = scale.chart.chartArea;\n    const { property , start , end  } = bounds || {};\n    if (property === 'x') {\n        ctx.beginPath();\n        ctx.rect(start, top, end - start, bottom - top);\n        ctx.clip();\n    }\n}\nfunction interpolatedLineTo(ctx, target, point, property) {\n    const interpolatedPoint = target.interpolate(point, property);\n    if (interpolatedPoint) {\n        ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y);\n    }\n}\n\nvar index = {\n    id: 'filler',\n    afterDatasetsUpdate (chart, _args, options) {\n        const count = (chart.data.datasets || []).length;\n        const sources = [];\n        let meta, i, line, source;\n        for(i = 0; i < count; ++i){\n            meta = chart.getDatasetMeta(i);\n            line = meta.dataset;\n            source = null;\n            if (line && line.options && line instanceof LineElement) {\n                source = {\n                    visible: chart.isDatasetVisible(i),\n                    index: i,\n                    fill: _decodeFill(line, i, count),\n                    chart,\n                    axis: meta.controller.options.indexAxis,\n                    scale: meta.vScale,\n                    line\n                };\n            }\n            meta.$filler = source;\n            sources.push(source);\n        }\n        for(i = 0; i < count; ++i){\n            source = sources[i];\n            if (!source || source.fill === false) {\n                continue;\n            }\n            source.fill = _resolveTarget(sources, i, options.propagate);\n        }\n    },\n    beforeDraw (chart, _args, options) {\n        const draw = options.drawTime === 'beforeDraw';\n        const metasets = chart.getSortedVisibleDatasetMetas();\n        const area = chart.chartArea;\n        for(let i = metasets.length - 1; i >= 0; --i){\n            const source = metasets[i].$filler;\n            if (!source) {\n                continue;\n            }\n            source.line.updateControlPoints(area, source.axis);\n            if (draw && source.fill) {\n                _drawfill(chart.ctx, source, area);\n            }\n        }\n    },\n    beforeDatasetsDraw (chart, _args, options) {\n        if (options.drawTime !== 'beforeDatasetsDraw') {\n            return;\n        }\n        const metasets = chart.getSortedVisibleDatasetMetas();\n        for(let i = metasets.length - 1; i >= 0; --i){\n            const source = metasets[i].$filler;\n            if (_shouldApplyFill(source)) {\n                _drawfill(chart.ctx, source, chart.chartArea);\n            }\n        }\n    },\n    beforeDatasetDraw (chart, args, options) {\n        const source = args.meta.$filler;\n        if (!_shouldApplyFill(source) || options.drawTime !== 'beforeDatasetDraw') {\n            return;\n        }\n        _drawfill(chart.ctx, source, chart.chartArea);\n    },\n    defaults: {\n        propagate: true,\n        drawTime: 'beforeDatasetDraw'\n    }\n};\n\nconst getBoxSize = (labelOpts, fontSize)=>{\n    let { boxHeight =fontSize , boxWidth =fontSize  } = labelOpts;\n    if (labelOpts.usePointStyle) {\n        boxHeight = Math.min(boxHeight, fontSize);\n        boxWidth = labelOpts.pointStyleWidth || Math.min(boxWidth, fontSize);\n    }\n    return {\n        boxWidth,\n        boxHeight,\n        itemHeight: Math.max(fontSize, boxHeight)\n    };\n};\nconst itemsEqual = (a, b)=>a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index;\nclass Legend extends Element {\n constructor(config){\n        super();\n        this._added = false;\n        this.legendHitBoxes = [];\n this._hoveredItem = null;\n        this.doughnutMode = false;\n        this.chart = config.chart;\n        this.options = config.options;\n        this.ctx = config.ctx;\n        this.legendItems = undefined;\n        this.columnSizes = undefined;\n        this.lineWidths = undefined;\n        this.maxHeight = undefined;\n        this.maxWidth = undefined;\n        this.top = undefined;\n        this.bottom = undefined;\n        this.left = undefined;\n        this.right = undefined;\n        this.height = undefined;\n        this.width = undefined;\n        this._margins = undefined;\n        this.position = undefined;\n        this.weight = undefined;\n        this.fullSize = undefined;\n    }\n    update(maxWidth, maxHeight, margins) {\n        this.maxWidth = maxWidth;\n        this.maxHeight = maxHeight;\n        this._margins = margins;\n        this.setDimensions();\n        this.buildLabels();\n        this.fit();\n    }\n    setDimensions() {\n        if (this.isHorizontal()) {\n            this.width = this.maxWidth;\n            this.left = this._margins.left;\n            this.right = this.width;\n        } else {\n            this.height = this.maxHeight;\n            this.top = this._margins.top;\n            this.bottom = this.height;\n        }\n    }\n    buildLabels() {\n        const labelOpts = this.options.labels || {};\n        let legendItems = callback(labelOpts.generateLabels, [\n            this.chart\n        ], this) || [];\n        if (labelOpts.filter) {\n            legendItems = legendItems.filter((item)=>labelOpts.filter(item, this.chart.data));\n        }\n        if (labelOpts.sort) {\n            legendItems = legendItems.sort((a, b)=>labelOpts.sort(a, b, this.chart.data));\n        }\n        if (this.options.reverse) {\n            legendItems.reverse();\n        }\n        this.legendItems = legendItems;\n    }\n    fit() {\n        const { options , ctx  } = this;\n        if (!options.display) {\n            this.width = this.height = 0;\n            return;\n        }\n        const labelOpts = options.labels;\n        const labelFont = toFont(labelOpts.font);\n        const fontSize = labelFont.size;\n        const titleHeight = this._computeTitleHeight();\n        const { boxWidth , itemHeight  } = getBoxSize(labelOpts, fontSize);\n        let width, height;\n        ctx.font = labelFont.string;\n        if (this.isHorizontal()) {\n            width = this.maxWidth;\n            height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;\n        } else {\n            height = this.maxHeight;\n            width = this._fitCols(titleHeight, labelFont, boxWidth, itemHeight) + 10;\n        }\n        this.width = Math.min(width, options.maxWidth || this.maxWidth);\n        this.height = Math.min(height, options.maxHeight || this.maxHeight);\n    }\n _fitRows(titleHeight, fontSize, boxWidth, itemHeight) {\n        const { ctx , maxWidth , options: { labels: { padding  }  }  } = this;\n        const hitboxes = this.legendHitBoxes = [];\n        const lineWidths = this.lineWidths = [\n            0\n        ];\n        const lineHeight = itemHeight + padding;\n        let totalHeight = titleHeight;\n        ctx.textAlign = 'left';\n        ctx.textBaseline = 'middle';\n        let row = -1;\n        let top = -lineHeight;\n        this.legendItems.forEach((legendItem, i)=>{\n            const itemWidth = boxWidth + fontSize / 2 + ctx.measureText(legendItem.text).width;\n            if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {\n                totalHeight += lineHeight;\n                lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;\n                top += lineHeight;\n                row++;\n            }\n            hitboxes[i] = {\n                left: 0,\n                top,\n                row,\n                width: itemWidth,\n                height: itemHeight\n            };\n            lineWidths[lineWidths.length - 1] += itemWidth + padding;\n        });\n        return totalHeight;\n    }\n    _fitCols(titleHeight, labelFont, boxWidth, _itemHeight) {\n        const { ctx , maxHeight , options: { labels: { padding  }  }  } = this;\n        const hitboxes = this.legendHitBoxes = [];\n        const columnSizes = this.columnSizes = [];\n        const heightLimit = maxHeight - titleHeight;\n        let totalWidth = padding;\n        let currentColWidth = 0;\n        let currentColHeight = 0;\n        let left = 0;\n        let col = 0;\n        this.legendItems.forEach((legendItem, i)=>{\n            const { itemWidth , itemHeight  } = calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight);\n            if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) {\n                totalWidth += currentColWidth + padding;\n                columnSizes.push({\n                    width: currentColWidth,\n                    height: currentColHeight\n                });\n                left += currentColWidth + padding;\n                col++;\n                currentColWidth = currentColHeight = 0;\n            }\n            hitboxes[i] = {\n                left,\n                top: currentColHeight,\n                col,\n                width: itemWidth,\n                height: itemHeight\n            };\n            currentColWidth = Math.max(currentColWidth, itemWidth);\n            currentColHeight += itemHeight + padding;\n        });\n        totalWidth += currentColWidth;\n        columnSizes.push({\n            width: currentColWidth,\n            height: currentColHeight\n        });\n        return totalWidth;\n    }\n    adjustHitBoxes() {\n        if (!this.options.display) {\n            return;\n        }\n        const titleHeight = this._computeTitleHeight();\n        const { legendHitBoxes: hitboxes , options: { align , labels: { padding  } , rtl  }  } = this;\n        const rtlHelper = getRtlAdapter(rtl, this.left, this.width);\n        if (this.isHorizontal()) {\n            let row = 0;\n            let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);\n            for (const hitbox of hitboxes){\n                if (row !== hitbox.row) {\n                    row = hitbox.row;\n                    left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);\n                }\n                hitbox.top += this.top + titleHeight + padding;\n                hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);\n                left += hitbox.width + padding;\n            }\n        } else {\n            let col = 0;\n            let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);\n            for (const hitbox1 of hitboxes){\n                if (hitbox1.col !== col) {\n                    col = hitbox1.col;\n                    top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);\n                }\n                hitbox1.top = top;\n                hitbox1.left += this.left + padding;\n                hitbox1.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox1.left), hitbox1.width);\n                top += hitbox1.height + padding;\n            }\n        }\n    }\n    isHorizontal() {\n        return this.options.position === 'top' || this.options.position === 'bottom';\n    }\n    draw() {\n        if (this.options.display) {\n            const ctx = this.ctx;\n            clipArea(ctx, this);\n            this._draw();\n            unclipArea(ctx);\n        }\n    }\n _draw() {\n        const { options: opts , columnSizes , lineWidths , ctx  } = this;\n        const { align , labels: labelOpts  } = opts;\n        const defaultColor = defaults.color;\n        const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);\n        const labelFont = toFont(labelOpts.font);\n        const { padding  } = labelOpts;\n        const fontSize = labelFont.size;\n        const halfFontSize = fontSize / 2;\n        let cursor;\n        this.drawTitle();\n        ctx.textAlign = rtlHelper.textAlign('left');\n        ctx.textBaseline = 'middle';\n        ctx.lineWidth = 0.5;\n        ctx.font = labelFont.string;\n        const { boxWidth , boxHeight , itemHeight  } = getBoxSize(labelOpts, fontSize);\n        const drawLegendBox = function(x, y, legendItem) {\n            if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) {\n                return;\n            }\n            ctx.save();\n            const lineWidth = valueOrDefault(legendItem.lineWidth, 1);\n            ctx.fillStyle = valueOrDefault(legendItem.fillStyle, defaultColor);\n            ctx.lineCap = valueOrDefault(legendItem.lineCap, 'butt');\n            ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, 0);\n            ctx.lineJoin = valueOrDefault(legendItem.lineJoin, 'miter');\n            ctx.lineWidth = lineWidth;\n            ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, defaultColor);\n            ctx.setLineDash(valueOrDefault(legendItem.lineDash, []));\n            if (labelOpts.usePointStyle) {\n                const drawOptions = {\n                    radius: boxHeight * Math.SQRT2 / 2,\n                    pointStyle: legendItem.pointStyle,\n                    rotation: legendItem.rotation,\n                    borderWidth: lineWidth\n                };\n                const centerX = rtlHelper.xPlus(x, boxWidth / 2);\n                const centerY = y + halfFontSize;\n                drawPointLegend(ctx, drawOptions, centerX, centerY, labelOpts.pointStyleWidth && boxWidth);\n            } else {\n                const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0);\n                const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth);\n                const borderRadius = toTRBLCorners(legendItem.borderRadius);\n                ctx.beginPath();\n                if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                    addRoundedRectPath(ctx, {\n                        x: xBoxLeft,\n                        y: yBoxTop,\n                        w: boxWidth,\n                        h: boxHeight,\n                        radius: borderRadius\n                    });\n                } else {\n                    ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight);\n                }\n                ctx.fill();\n                if (lineWidth !== 0) {\n                    ctx.stroke();\n                }\n            }\n            ctx.restore();\n        };\n        const fillText = function(x, y, legendItem) {\n            renderText(ctx, legendItem.text, x, y + itemHeight / 2, labelFont, {\n                strikethrough: legendItem.hidden,\n                textAlign: rtlHelper.textAlign(legendItem.textAlign)\n            });\n        };\n        const isHorizontal = this.isHorizontal();\n        const titleHeight = this._computeTitleHeight();\n        if (isHorizontal) {\n            cursor = {\n                x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]),\n                y: this.top + padding + titleHeight,\n                line: 0\n            };\n        } else {\n            cursor = {\n                x: this.left + padding,\n                y: _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height),\n                line: 0\n            };\n        }\n        overrideTextDirection(this.ctx, opts.textDirection);\n        const lineHeight = itemHeight + padding;\n        this.legendItems.forEach((legendItem, i)=>{\n            ctx.strokeStyle = legendItem.fontColor;\n            ctx.fillStyle = legendItem.fontColor;\n            const textWidth = ctx.measureText(legendItem.text).width;\n            const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign));\n            const width = boxWidth + halfFontSize + textWidth;\n            let x = cursor.x;\n            let y = cursor.y;\n            rtlHelper.setWidth(this.width);\n            if (isHorizontal) {\n                if (i > 0 && x + width + padding > this.right) {\n                    y = cursor.y += lineHeight;\n                    cursor.line++;\n                    x = cursor.x = _alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]);\n                }\n            } else if (i > 0 && y + lineHeight > this.bottom) {\n                x = cursor.x = x + columnSizes[cursor.line].width + padding;\n                cursor.line++;\n                y = cursor.y = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height);\n            }\n            const realX = rtlHelper.x(x);\n            drawLegendBox(realX, y, legendItem);\n            x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl);\n            fillText(rtlHelper.x(x), y, legendItem);\n            if (isHorizontal) {\n                cursor.x += width + padding;\n            } else if (typeof legendItem.text !== 'string') {\n                const fontLineHeight = labelFont.lineHeight;\n                cursor.y += calculateLegendItemHeight(legendItem, fontLineHeight);\n            } else {\n                cursor.y += lineHeight;\n            }\n        });\n        restoreTextDirection(this.ctx, opts.textDirection);\n    }\n drawTitle() {\n        const opts = this.options;\n        const titleOpts = opts.title;\n        const titleFont = toFont(titleOpts.font);\n        const titlePadding = toPadding(titleOpts.padding);\n        if (!titleOpts.display) {\n            return;\n        }\n        const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);\n        const ctx = this.ctx;\n        const position = titleOpts.position;\n        const halfFontSize = titleFont.size / 2;\n        const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;\n        let y;\n        let left = this.left;\n        let maxWidth = this.width;\n        if (this.isHorizontal()) {\n            maxWidth = Math.max(...this.lineWidths);\n            y = this.top + topPaddingPlusHalfFontSize;\n            left = _alignStartEnd(opts.align, left, this.right - maxWidth);\n        } else {\n            const maxHeight = this.columnSizes.reduce((acc, size)=>Math.max(acc, size.height), 0);\n            y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight());\n        }\n        const x = _alignStartEnd(position, left, left + maxWidth);\n        ctx.textAlign = rtlHelper.textAlign(_toLeftRightCenter(position));\n        ctx.textBaseline = 'middle';\n        ctx.strokeStyle = titleOpts.color;\n        ctx.fillStyle = titleOpts.color;\n        ctx.font = titleFont.string;\n        renderText(ctx, titleOpts.text, x, y, titleFont);\n    }\n _computeTitleHeight() {\n        const titleOpts = this.options.title;\n        const titleFont = toFont(titleOpts.font);\n        const titlePadding = toPadding(titleOpts.padding);\n        return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;\n    }\n _getLegendItemAt(x, y) {\n        let i, hitBox, lh;\n        if (_isBetween(x, this.left, this.right) && _isBetween(y, this.top, this.bottom)) {\n            lh = this.legendHitBoxes;\n            for(i = 0; i < lh.length; ++i){\n                hitBox = lh[i];\n                if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width) && _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {\n                    return this.legendItems[i];\n                }\n            }\n        }\n        return null;\n    }\n handleEvent(e) {\n        const opts = this.options;\n        if (!isListened(e.type, opts)) {\n            return;\n        }\n        const hoveredItem = this._getLegendItemAt(e.x, e.y);\n        if (e.type === 'mousemove' || e.type === 'mouseout') {\n            const previous = this._hoveredItem;\n            const sameItem = itemsEqual(previous, hoveredItem);\n            if (previous && !sameItem) {\n                callback(opts.onLeave, [\n                    e,\n                    previous,\n                    this\n                ], this);\n            }\n            this._hoveredItem = hoveredItem;\n            if (hoveredItem && !sameItem) {\n                callback(opts.onHover, [\n                    e,\n                    hoveredItem,\n                    this\n                ], this);\n            }\n        } else if (hoveredItem) {\n            callback(opts.onClick, [\n                e,\n                hoveredItem,\n                this\n            ], this);\n        }\n    }\n}\nfunction calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight) {\n    const itemWidth = calculateItemWidth(legendItem, boxWidth, labelFont, ctx);\n    const itemHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight);\n    return {\n        itemWidth,\n        itemHeight\n    };\n}\nfunction calculateItemWidth(legendItem, boxWidth, labelFont, ctx) {\n    let legendItemText = legendItem.text;\n    if (legendItemText && typeof legendItemText !== 'string') {\n        legendItemText = legendItemText.reduce((a, b)=>a.length > b.length ? a : b);\n    }\n    return boxWidth + labelFont.size / 2 + ctx.measureText(legendItemText).width;\n}\nfunction calculateItemHeight(_itemHeight, legendItem, fontLineHeight) {\n    let itemHeight = _itemHeight;\n    if (typeof legendItem.text !== 'string') {\n        itemHeight = calculateLegendItemHeight(legendItem, fontLineHeight);\n    }\n    return itemHeight;\n}\nfunction calculateLegendItemHeight(legendItem, fontLineHeight) {\n    const labelHeight = legendItem.text ? legendItem.text.length + 0.5 : 0;\n    return fontLineHeight * labelHeight;\n}\nfunction isListened(type, opts) {\n    if ((type === 'mousemove' || type === 'mouseout') && (opts.onHover || opts.onLeave)) {\n        return true;\n    }\n    if (opts.onClick && (type === 'click' || type === 'mouseup')) {\n        return true;\n    }\n    return false;\n}\nvar plugin_legend = {\n    id: 'legend',\n _element: Legend,\n    start (chart, _args, options) {\n        const legend = chart.legend = new Legend({\n            ctx: chart.ctx,\n            options,\n            chart\n        });\n        layouts.configure(chart, legend, options);\n        layouts.addBox(chart, legend);\n    },\n    stop (chart) {\n        layouts.removeBox(chart, chart.legend);\n        delete chart.legend;\n    },\n    beforeUpdate (chart, _args, options) {\n        const legend = chart.legend;\n        layouts.configure(chart, legend, options);\n        legend.options = options;\n    },\n    afterUpdate (chart) {\n        const legend = chart.legend;\n        legend.buildLabels();\n        legend.adjustHitBoxes();\n    },\n    afterEvent (chart, args) {\n        if (!args.replay) {\n            chart.legend.handleEvent(args.event);\n        }\n    },\n    defaults: {\n        display: true,\n        position: 'top',\n        align: 'center',\n        fullSize: true,\n        reverse: false,\n        weight: 1000,\n        onClick (e, legendItem, legend) {\n            const index = legendItem.datasetIndex;\n            const ci = legend.chart;\n            if (ci.isDatasetVisible(index)) {\n                ci.hide(index);\n                legendItem.hidden = true;\n            } else {\n                ci.show(index);\n                legendItem.hidden = false;\n            }\n        },\n        onHover: null,\n        onLeave: null,\n        labels: {\n            color: (ctx)=>ctx.chart.options.color,\n            boxWidth: 40,\n            padding: 10,\n            generateLabels (chart) {\n                const datasets = chart.data.datasets;\n                const { labels: { usePointStyle , pointStyle , textAlign , color , useBorderRadius , borderRadius  }  } = chart.legend.options;\n                return chart._getSortedDatasetMetas().map((meta)=>{\n                    const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);\n                    const borderWidth = toPadding(style.borderWidth);\n                    return {\n                        text: datasets[meta.index].label,\n                        fillStyle: style.backgroundColor,\n                        fontColor: color,\n                        hidden: !meta.visible,\n                        lineCap: style.borderCapStyle,\n                        lineDash: style.borderDash,\n                        lineDashOffset: style.borderDashOffset,\n                        lineJoin: style.borderJoinStyle,\n                        lineWidth: (borderWidth.width + borderWidth.height) / 4,\n                        strokeStyle: style.borderColor,\n                        pointStyle: pointStyle || style.pointStyle,\n                        rotation: style.rotation,\n                        textAlign: textAlign || style.textAlign,\n                        borderRadius: useBorderRadius && (borderRadius || style.borderRadius),\n                        datasetIndex: meta.index\n                    };\n                }, this);\n            }\n        },\n        title: {\n            color: (ctx)=>ctx.chart.options.color,\n            display: false,\n            position: 'center',\n            text: ''\n        }\n    },\n    descriptors: {\n        _scriptable: (name)=>!name.startsWith('on'),\n        labels: {\n            _scriptable: (name)=>![\n                    'generateLabels',\n                    'filter',\n                    'sort'\n                ].includes(name)\n        }\n    }\n};\n\nclass Title extends Element {\n constructor(config){\n        super();\n        this.chart = config.chart;\n        this.options = config.options;\n        this.ctx = config.ctx;\n        this._padding = undefined;\n        this.top = undefined;\n        this.bottom = undefined;\n        this.left = undefined;\n        this.right = undefined;\n        this.width = undefined;\n        this.height = undefined;\n        this.position = undefined;\n        this.weight = undefined;\n        this.fullSize = undefined;\n    }\n    update(maxWidth, maxHeight) {\n        const opts = this.options;\n        this.left = 0;\n        this.top = 0;\n        if (!opts.display) {\n            this.width = this.height = this.right = this.bottom = 0;\n            return;\n        }\n        this.width = this.right = maxWidth;\n        this.height = this.bottom = maxHeight;\n        const lineCount = isArray(opts.text) ? opts.text.length : 1;\n        this._padding = toPadding(opts.padding);\n        const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height;\n        if (this.isHorizontal()) {\n            this.height = textSize;\n        } else {\n            this.width = textSize;\n        }\n    }\n    isHorizontal() {\n        const pos = this.options.position;\n        return pos === 'top' || pos === 'bottom';\n    }\n    _drawArgs(offset) {\n        const { top , left , bottom , right , options  } = this;\n        const align = options.align;\n        let rotation = 0;\n        let maxWidth, titleX, titleY;\n        if (this.isHorizontal()) {\n            titleX = _alignStartEnd(align, left, right);\n            titleY = top + offset;\n            maxWidth = right - left;\n        } else {\n            if (options.position === 'left') {\n                titleX = left + offset;\n                titleY = _alignStartEnd(align, bottom, top);\n                rotation = PI * -0.5;\n            } else {\n                titleX = right - offset;\n                titleY = _alignStartEnd(align, top, bottom);\n                rotation = PI * 0.5;\n            }\n            maxWidth = bottom - top;\n        }\n        return {\n            titleX,\n            titleY,\n            maxWidth,\n            rotation\n        };\n    }\n    draw() {\n        const ctx = this.ctx;\n        const opts = this.options;\n        if (!opts.display) {\n            return;\n        }\n        const fontOpts = toFont(opts.font);\n        const lineHeight = fontOpts.lineHeight;\n        const offset = lineHeight / 2 + this._padding.top;\n        const { titleX , titleY , maxWidth , rotation  } = this._drawArgs(offset);\n        renderText(ctx, opts.text, 0, 0, fontOpts, {\n            color: opts.color,\n            maxWidth,\n            rotation,\n            textAlign: _toLeftRightCenter(opts.align),\n            textBaseline: 'middle',\n            translation: [\n                titleX,\n                titleY\n            ]\n        });\n    }\n}\nfunction createTitle(chart, titleOpts) {\n    const title = new Title({\n        ctx: chart.ctx,\n        options: titleOpts,\n        chart\n    });\n    layouts.configure(chart, title, titleOpts);\n    layouts.addBox(chart, title);\n    chart.titleBlock = title;\n}\nvar plugin_title = {\n    id: 'title',\n _element: Title,\n    start (chart, _args, options) {\n        createTitle(chart, options);\n    },\n    stop (chart) {\n        const titleBlock = chart.titleBlock;\n        layouts.removeBox(chart, titleBlock);\n        delete chart.titleBlock;\n    },\n    beforeUpdate (chart, _args, options) {\n        const title = chart.titleBlock;\n        layouts.configure(chart, title, options);\n        title.options = options;\n    },\n    defaults: {\n        align: 'center',\n        display: false,\n        font: {\n            weight: 'bold'\n        },\n        fullSize: true,\n        padding: 10,\n        position: 'top',\n        text: '',\n        weight: 2000\n    },\n    defaultRoutes: {\n        color: 'color'\n    },\n    descriptors: {\n        _scriptable: true,\n        _indexable: false\n    }\n};\n\nconst map = new WeakMap();\nvar plugin_subtitle = {\n    id: 'subtitle',\n    start (chart, _args, options) {\n        const title = new Title({\n            ctx: chart.ctx,\n            options,\n            chart\n        });\n        layouts.configure(chart, title, options);\n        layouts.addBox(chart, title);\n        map.set(chart, title);\n    },\n    stop (chart) {\n        layouts.removeBox(chart, map.get(chart));\n        map.delete(chart);\n    },\n    beforeUpdate (chart, _args, options) {\n        const title = map.get(chart);\n        layouts.configure(chart, title, options);\n        title.options = options;\n    },\n    defaults: {\n        align: 'center',\n        display: false,\n        font: {\n            weight: 'normal'\n        },\n        fullSize: true,\n        padding: 0,\n        position: 'top',\n        text: '',\n        weight: 1500\n    },\n    defaultRoutes: {\n        color: 'color'\n    },\n    descriptors: {\n        _scriptable: true,\n        _indexable: false\n    }\n};\n\nconst positioners = {\n average (items) {\n        if (!items.length) {\n            return false;\n        }\n        let i, len;\n        let x = 0;\n        let y = 0;\n        let count = 0;\n        for(i = 0, len = items.length; i < len; ++i){\n            const el = items[i].element;\n            if (el && el.hasValue()) {\n                const pos = el.tooltipPosition();\n                x += pos.x;\n                y += pos.y;\n                ++count;\n            }\n        }\n        return {\n            x: x / count,\n            y: y / count\n        };\n    },\n nearest (items, eventPosition) {\n        if (!items.length) {\n            return false;\n        }\n        let x = eventPosition.x;\n        let y = eventPosition.y;\n        let minDistance = Number.POSITIVE_INFINITY;\n        let i, len, nearestElement;\n        for(i = 0, len = items.length; i < len; ++i){\n            const el = items[i].element;\n            if (el && el.hasValue()) {\n                const center = el.getCenterPoint();\n                const d = distanceBetweenPoints(eventPosition, center);\n                if (d < minDistance) {\n                    minDistance = d;\n                    nearestElement = el;\n                }\n            }\n        }\n        if (nearestElement) {\n            const tp = nearestElement.tooltipPosition();\n            x = tp.x;\n            y = tp.y;\n        }\n        return {\n            x,\n            y\n        };\n    }\n};\nfunction pushOrConcat(base, toPush) {\n    if (toPush) {\n        if (isArray(toPush)) {\n            Array.prototype.push.apply(base, toPush);\n        } else {\n            base.push(toPush);\n        }\n    }\n    return base;\n}\n function splitNewlines(str) {\n    if ((typeof str === 'string' || str instanceof String) && str.indexOf('\\n') > -1) {\n        return str.split('\\n');\n    }\n    return str;\n}\n function createTooltipItem(chart, item) {\n    const { element , datasetIndex , index  } = item;\n    const controller = chart.getDatasetMeta(datasetIndex).controller;\n    const { label , value  } = controller.getLabelAndValue(index);\n    return {\n        chart,\n        label,\n        parsed: controller.getParsed(index),\n        raw: chart.data.datasets[datasetIndex].data[index],\n        formattedValue: value,\n        dataset: controller.getDataset(),\n        dataIndex: index,\n        datasetIndex,\n        element\n    };\n}\n function getTooltipSize(tooltip, options) {\n    const ctx = tooltip.chart.ctx;\n    const { body , footer , title  } = tooltip;\n    const { boxWidth , boxHeight  } = options;\n    const bodyFont = toFont(options.bodyFont);\n    const titleFont = toFont(options.titleFont);\n    const footerFont = toFont(options.footerFont);\n    const titleLineCount = title.length;\n    const footerLineCount = footer.length;\n    const bodyLineItemCount = body.length;\n    const padding = toPadding(options.padding);\n    let height = padding.height;\n    let width = 0;\n    let combinedBodyLength = body.reduce((count, bodyItem)=>count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length, 0);\n    combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length;\n    if (titleLineCount) {\n        height += titleLineCount * titleFont.lineHeight + (titleLineCount - 1) * options.titleSpacing + options.titleMarginBottom;\n    }\n    if (combinedBodyLength) {\n        const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight;\n        height += bodyLineItemCount * bodyLineHeight + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight + (combinedBodyLength - 1) * options.bodySpacing;\n    }\n    if (footerLineCount) {\n        height += options.footerMarginTop + footerLineCount * footerFont.lineHeight + (footerLineCount - 1) * options.footerSpacing;\n    }\n    let widthPadding = 0;\n    const maxLineWidth = function(line) {\n        width = Math.max(width, ctx.measureText(line).width + widthPadding);\n    };\n    ctx.save();\n    ctx.font = titleFont.string;\n    each(tooltip.title, maxLineWidth);\n    ctx.font = bodyFont.string;\n    each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);\n    widthPadding = options.displayColors ? boxWidth + 2 + options.boxPadding : 0;\n    each(body, (bodyItem)=>{\n        each(bodyItem.before, maxLineWidth);\n        each(bodyItem.lines, maxLineWidth);\n        each(bodyItem.after, maxLineWidth);\n    });\n    widthPadding = 0;\n    ctx.font = footerFont.string;\n    each(tooltip.footer, maxLineWidth);\n    ctx.restore();\n    width += padding.width;\n    return {\n        width,\n        height\n    };\n}\nfunction determineYAlign(chart, size) {\n    const { y , height  } = size;\n    if (y < height / 2) {\n        return 'top';\n    } else if (y > chart.height - height / 2) {\n        return 'bottom';\n    }\n    return 'center';\n}\nfunction doesNotFitWithAlign(xAlign, chart, options, size) {\n    const { x , width  } = size;\n    const caret = options.caretSize + options.caretPadding;\n    if (xAlign === 'left' && x + width + caret > chart.width) {\n        return true;\n    }\n    if (xAlign === 'right' && x - width - caret < 0) {\n        return true;\n    }\n}\nfunction determineXAlign(chart, options, size, yAlign) {\n    const { x , width  } = size;\n    const { width: chartWidth , chartArea: { left , right  }  } = chart;\n    let xAlign = 'center';\n    if (yAlign === 'center') {\n        xAlign = x <= (left + right) / 2 ? 'left' : 'right';\n    } else if (x <= width / 2) {\n        xAlign = 'left';\n    } else if (x >= chartWidth - width / 2) {\n        xAlign = 'right';\n    }\n    if (doesNotFitWithAlign(xAlign, chart, options, size)) {\n        xAlign = 'center';\n    }\n    return xAlign;\n}\n function determineAlignment(chart, options, size) {\n    const yAlign = size.yAlign || options.yAlign || determineYAlign(chart, size);\n    return {\n        xAlign: size.xAlign || options.xAlign || determineXAlign(chart, options, size, yAlign),\n        yAlign\n    };\n}\nfunction alignX(size, xAlign) {\n    let { x , width  } = size;\n    if (xAlign === 'right') {\n        x -= width;\n    } else if (xAlign === 'center') {\n        x -= width / 2;\n    }\n    return x;\n}\nfunction alignY(size, yAlign, paddingAndSize) {\n    let { y , height  } = size;\n    if (yAlign === 'top') {\n        y += paddingAndSize;\n    } else if (yAlign === 'bottom') {\n        y -= height + paddingAndSize;\n    } else {\n        y -= height / 2;\n    }\n    return y;\n}\n function getBackgroundPoint(options, size, alignment, chart) {\n    const { caretSize , caretPadding , cornerRadius  } = options;\n    const { xAlign , yAlign  } = alignment;\n    const paddingAndSize = caretSize + caretPadding;\n    const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);\n    let x = alignX(size, xAlign);\n    const y = alignY(size, yAlign, paddingAndSize);\n    if (yAlign === 'center') {\n        if (xAlign === 'left') {\n            x += paddingAndSize;\n        } else if (xAlign === 'right') {\n            x -= paddingAndSize;\n        }\n    } else if (xAlign === 'left') {\n        x -= Math.max(topLeft, bottomLeft) + caretSize;\n    } else if (xAlign === 'right') {\n        x += Math.max(topRight, bottomRight) + caretSize;\n    }\n    return {\n        x: _limitValue(x, 0, chart.width - size.width),\n        y: _limitValue(y, 0, chart.height - size.height)\n    };\n}\nfunction getAlignedX(tooltip, align, options) {\n    const padding = toPadding(options.padding);\n    return align === 'center' ? tooltip.x + tooltip.width / 2 : align === 'right' ? tooltip.x + tooltip.width - padding.right : tooltip.x + padding.left;\n}\n function getBeforeAfterBodyLines(callback) {\n    return pushOrConcat([], splitNewlines(callback));\n}\nfunction createTooltipContext(parent, tooltip, tooltipItems) {\n    return createContext(parent, {\n        tooltip,\n        tooltipItems,\n        type: 'tooltip'\n    });\n}\nfunction overrideCallbacks(callbacks, context) {\n    const override = context && context.dataset && context.dataset.tooltip && context.dataset.tooltip.callbacks;\n    return override ? callbacks.override(override) : callbacks;\n}\nconst defaultCallbacks = {\n    beforeTitle: noop,\n    title (tooltipItems) {\n        if (tooltipItems.length > 0) {\n            const item = tooltipItems[0];\n            const labels = item.chart.data.labels;\n            const labelCount = labels ? labels.length : 0;\n            if (this && this.options && this.options.mode === 'dataset') {\n                return item.dataset.label || '';\n            } else if (item.label) {\n                return item.label;\n            } else if (labelCount > 0 && item.dataIndex < labelCount) {\n                return labels[item.dataIndex];\n            }\n        }\n        return '';\n    },\n    afterTitle: noop,\n    beforeBody: noop,\n    beforeLabel: noop,\n    label (tooltipItem) {\n        if (this && this.options && this.options.mode === 'dataset') {\n            return tooltipItem.label + ': ' + tooltipItem.formattedValue || tooltipItem.formattedValue;\n        }\n        let label = tooltipItem.dataset.label || '';\n        if (label) {\n            label += ': ';\n        }\n        const value = tooltipItem.formattedValue;\n        if (!isNullOrUndef(value)) {\n            label += value;\n        }\n        return label;\n    },\n    labelColor (tooltipItem) {\n        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);\n        const options = meta.controller.getStyle(tooltipItem.dataIndex);\n        return {\n            borderColor: options.borderColor,\n            backgroundColor: options.backgroundColor,\n            borderWidth: options.borderWidth,\n            borderDash: options.borderDash,\n            borderDashOffset: options.borderDashOffset,\n            borderRadius: 0\n        };\n    },\n    labelTextColor () {\n        return this.options.bodyColor;\n    },\n    labelPointStyle (tooltipItem) {\n        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);\n        const options = meta.controller.getStyle(tooltipItem.dataIndex);\n        return {\n            pointStyle: options.pointStyle,\n            rotation: options.rotation\n        };\n    },\n    afterLabel: noop,\n    afterBody: noop,\n    beforeFooter: noop,\n    footer: noop,\n    afterFooter: noop\n};\n function invokeCallbackWithFallback(callbacks, name, ctx, arg) {\n    const result = callbacks[name].call(ctx, arg);\n    if (typeof result === 'undefined') {\n        return defaultCallbacks[name].call(ctx, arg);\n    }\n    return result;\n}\nclass Tooltip extends Element {\n static positioners = positioners;\n    constructor(config){\n        super();\n        this.opacity = 0;\n        this._active = [];\n        this._eventPosition = undefined;\n        this._size = undefined;\n        this._cachedAnimations = undefined;\n        this._tooltipItems = [];\n        this.$animations = undefined;\n        this.$context = undefined;\n        this.chart = config.chart;\n        this.options = config.options;\n        this.dataPoints = undefined;\n        this.title = undefined;\n        this.beforeBody = undefined;\n        this.body = undefined;\n        this.afterBody = undefined;\n        this.footer = undefined;\n        this.xAlign = undefined;\n        this.yAlign = undefined;\n        this.x = undefined;\n        this.y = undefined;\n        this.height = undefined;\n        this.width = undefined;\n        this.caretX = undefined;\n        this.caretY = undefined;\n        this.labelColors = undefined;\n        this.labelPointStyles = undefined;\n        this.labelTextColors = undefined;\n    }\n    initialize(options) {\n        this.options = options;\n        this._cachedAnimations = undefined;\n        this.$context = undefined;\n    }\n _resolveAnimations() {\n        const cached = this._cachedAnimations;\n        if (cached) {\n            return cached;\n        }\n        const chart = this.chart;\n        const options = this.options.setContext(this.getContext());\n        const opts = options.enabled && chart.options.animation && options.animations;\n        const animations = new Animations(this.chart, opts);\n        if (opts._cacheable) {\n            this._cachedAnimations = Object.freeze(animations);\n        }\n        return animations;\n    }\n getContext() {\n        return this.$context || (this.$context = createTooltipContext(this.chart.getContext(), this, this._tooltipItems));\n    }\n    getTitle(context, options) {\n        const { callbacks  } = options;\n        const beforeTitle = invokeCallbackWithFallback(callbacks, 'beforeTitle', this, context);\n        const title = invokeCallbackWithFallback(callbacks, 'title', this, context);\n        const afterTitle = invokeCallbackWithFallback(callbacks, 'afterTitle', this, context);\n        let lines = [];\n        lines = pushOrConcat(lines, splitNewlines(beforeTitle));\n        lines = pushOrConcat(lines, splitNewlines(title));\n        lines = pushOrConcat(lines, splitNewlines(afterTitle));\n        return lines;\n    }\n    getBeforeBody(tooltipItems, options) {\n        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'beforeBody', this, tooltipItems));\n    }\n    getBody(tooltipItems, options) {\n        const { callbacks  } = options;\n        const bodyItems = [];\n        each(tooltipItems, (context)=>{\n            const bodyItem = {\n                before: [],\n                lines: [],\n                after: []\n            };\n            const scoped = overrideCallbacks(callbacks, context);\n            pushOrConcat(bodyItem.before, splitNewlines(invokeCallbackWithFallback(scoped, 'beforeLabel', this, context)));\n            pushOrConcat(bodyItem.lines, invokeCallbackWithFallback(scoped, 'label', this, context));\n            pushOrConcat(bodyItem.after, splitNewlines(invokeCallbackWithFallback(scoped, 'afterLabel', this, context)));\n            bodyItems.push(bodyItem);\n        });\n        return bodyItems;\n    }\n    getAfterBody(tooltipItems, options) {\n        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'afterBody', this, tooltipItems));\n    }\n    getFooter(tooltipItems, options) {\n        const { callbacks  } = options;\n        const beforeFooter = invokeCallbackWithFallback(callbacks, 'beforeFooter', this, tooltipItems);\n        const footer = invokeCallbackWithFallback(callbacks, 'footer', this, tooltipItems);\n        const afterFooter = invokeCallbackWithFallback(callbacks, 'afterFooter', this, tooltipItems);\n        let lines = [];\n        lines = pushOrConcat(lines, splitNewlines(beforeFooter));\n        lines = pushOrConcat(lines, splitNewlines(footer));\n        lines = pushOrConcat(lines, splitNewlines(afterFooter));\n        return lines;\n    }\n _createItems(options) {\n        const active = this._active;\n        const data = this.chart.data;\n        const labelColors = [];\n        const labelPointStyles = [];\n        const labelTextColors = [];\n        let tooltipItems = [];\n        let i, len;\n        for(i = 0, len = active.length; i < len; ++i){\n            tooltipItems.push(createTooltipItem(this.chart, active[i]));\n        }\n        if (options.filter) {\n            tooltipItems = tooltipItems.filter((element, index, array)=>options.filter(element, index, array, data));\n        }\n        if (options.itemSort) {\n            tooltipItems = tooltipItems.sort((a, b)=>options.itemSort(a, b, data));\n        }\n        each(tooltipItems, (context)=>{\n            const scoped = overrideCallbacks(options.callbacks, context);\n            labelColors.push(invokeCallbackWithFallback(scoped, 'labelColor', this, context));\n            labelPointStyles.push(invokeCallbackWithFallback(scoped, 'labelPointStyle', this, context));\n            labelTextColors.push(invokeCallbackWithFallback(scoped, 'labelTextColor', this, context));\n        });\n        this.labelColors = labelColors;\n        this.labelPointStyles = labelPointStyles;\n        this.labelTextColors = labelTextColors;\n        this.dataPoints = tooltipItems;\n        return tooltipItems;\n    }\n    update(changed, replay) {\n        const options = this.options.setContext(this.getContext());\n        const active = this._active;\n        let properties;\n        let tooltipItems = [];\n        if (!active.length) {\n            if (this.opacity !== 0) {\n                properties = {\n                    opacity: 0\n                };\n            }\n        } else {\n            const position = positioners[options.position].call(this, active, this._eventPosition);\n            tooltipItems = this._createItems(options);\n            this.title = this.getTitle(tooltipItems, options);\n            this.beforeBody = this.getBeforeBody(tooltipItems, options);\n            this.body = this.getBody(tooltipItems, options);\n            this.afterBody = this.getAfterBody(tooltipItems, options);\n            this.footer = this.getFooter(tooltipItems, options);\n            const size = this._size = getTooltipSize(this, options);\n            const positionAndSize = Object.assign({}, position, size);\n            const alignment = determineAlignment(this.chart, options, positionAndSize);\n            const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this.chart);\n            this.xAlign = alignment.xAlign;\n            this.yAlign = alignment.yAlign;\n            properties = {\n                opacity: 1,\n                x: backgroundPoint.x,\n                y: backgroundPoint.y,\n                width: size.width,\n                height: size.height,\n                caretX: position.x,\n                caretY: position.y\n            };\n        }\n        this._tooltipItems = tooltipItems;\n        this.$context = undefined;\n        if (properties) {\n            this._resolveAnimations().update(this, properties);\n        }\n        if (changed && options.external) {\n            options.external.call(this, {\n                chart: this.chart,\n                tooltip: this,\n                replay\n            });\n        }\n    }\n    drawCaret(tooltipPoint, ctx, size, options) {\n        const caretPosition = this.getCaretPosition(tooltipPoint, size, options);\n        ctx.lineTo(caretPosition.x1, caretPosition.y1);\n        ctx.lineTo(caretPosition.x2, caretPosition.y2);\n        ctx.lineTo(caretPosition.x3, caretPosition.y3);\n    }\n    getCaretPosition(tooltipPoint, size, options) {\n        const { xAlign , yAlign  } = this;\n        const { caretSize , cornerRadius  } = options;\n        const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);\n        const { x: ptX , y: ptY  } = tooltipPoint;\n        const { width , height  } = size;\n        let x1, x2, x3, y1, y2, y3;\n        if (yAlign === 'center') {\n            y2 = ptY + height / 2;\n            if (xAlign === 'left') {\n                x1 = ptX;\n                x2 = x1 - caretSize;\n                y1 = y2 + caretSize;\n                y3 = y2 - caretSize;\n            } else {\n                x1 = ptX + width;\n                x2 = x1 + caretSize;\n                y1 = y2 - caretSize;\n                y3 = y2 + caretSize;\n            }\n            x3 = x1;\n        } else {\n            if (xAlign === 'left') {\n                x2 = ptX + Math.max(topLeft, bottomLeft) + caretSize;\n            } else if (xAlign === 'right') {\n                x2 = ptX + width - Math.max(topRight, bottomRight) - caretSize;\n            } else {\n                x2 = this.caretX;\n            }\n            if (yAlign === 'top') {\n                y1 = ptY;\n                y2 = y1 - caretSize;\n                x1 = x2 - caretSize;\n                x3 = x2 + caretSize;\n            } else {\n                y1 = ptY + height;\n                y2 = y1 + caretSize;\n                x1 = x2 + caretSize;\n                x3 = x2 - caretSize;\n            }\n            y3 = y1;\n        }\n        return {\n            x1,\n            x2,\n            x3,\n            y1,\n            y2,\n            y3\n        };\n    }\n    drawTitle(pt, ctx, options) {\n        const title = this.title;\n        const length = title.length;\n        let titleFont, titleSpacing, i;\n        if (length) {\n            const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);\n            pt.x = getAlignedX(this, options.titleAlign, options);\n            ctx.textAlign = rtlHelper.textAlign(options.titleAlign);\n            ctx.textBaseline = 'middle';\n            titleFont = toFont(options.titleFont);\n            titleSpacing = options.titleSpacing;\n            ctx.fillStyle = options.titleColor;\n            ctx.font = titleFont.string;\n            for(i = 0; i < length; ++i){\n                ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2);\n                pt.y += titleFont.lineHeight + titleSpacing;\n                if (i + 1 === length) {\n                    pt.y += options.titleMarginBottom - titleSpacing;\n                }\n            }\n        }\n    }\n _drawColorBox(ctx, pt, i, rtlHelper, options) {\n        const labelColors = this.labelColors[i];\n        const labelPointStyle = this.labelPointStyles[i];\n        const { boxHeight , boxWidth , boxPadding  } = options;\n        const bodyFont = toFont(options.bodyFont);\n        const colorX = getAlignedX(this, 'left', options);\n        const rtlColorX = rtlHelper.x(colorX);\n        const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;\n        const colorY = pt.y + yOffSet;\n        if (options.usePointStyle) {\n            const drawOptions = {\n                radius: Math.min(boxWidth, boxHeight) / 2,\n                pointStyle: labelPointStyle.pointStyle,\n                rotation: labelPointStyle.rotation,\n                borderWidth: 1\n            };\n            const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2;\n            const centerY = colorY + boxHeight / 2;\n            ctx.strokeStyle = options.multiKeyBackground;\n            ctx.fillStyle = options.multiKeyBackground;\n            drawPoint(ctx, drawOptions, centerX, centerY);\n            ctx.strokeStyle = labelColors.borderColor;\n            ctx.fillStyle = labelColors.backgroundColor;\n            drawPoint(ctx, drawOptions, centerX, centerY);\n        } else {\n            ctx.lineWidth = isObject(labelColors.borderWidth) ? Math.max(...Object.values(labelColors.borderWidth)) : labelColors.borderWidth || 1;\n            ctx.strokeStyle = labelColors.borderColor;\n            ctx.setLineDash(labelColors.borderDash || []);\n            ctx.lineDashOffset = labelColors.borderDashOffset || 0;\n            const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth - boxPadding);\n            const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - boxPadding - 2);\n            const borderRadius = toTRBLCorners(labelColors.borderRadius);\n            if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                ctx.beginPath();\n                ctx.fillStyle = options.multiKeyBackground;\n                addRoundedRectPath(ctx, {\n                    x: outerX,\n                    y: colorY,\n                    w: boxWidth,\n                    h: boxHeight,\n                    radius: borderRadius\n                });\n                ctx.fill();\n                ctx.stroke();\n                ctx.fillStyle = labelColors.backgroundColor;\n                ctx.beginPath();\n                addRoundedRectPath(ctx, {\n                    x: innerX,\n                    y: colorY + 1,\n                    w: boxWidth - 2,\n                    h: boxHeight - 2,\n                    radius: borderRadius\n                });\n                ctx.fill();\n            } else {\n                ctx.fillStyle = options.multiKeyBackground;\n                ctx.fillRect(outerX, colorY, boxWidth, boxHeight);\n                ctx.strokeRect(outerX, colorY, boxWidth, boxHeight);\n                ctx.fillStyle = labelColors.backgroundColor;\n                ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2);\n            }\n        }\n        ctx.fillStyle = this.labelTextColors[i];\n    }\n    drawBody(pt, ctx, options) {\n        const { body  } = this;\n        const { bodySpacing , bodyAlign , displayColors , boxHeight , boxWidth , boxPadding  } = options;\n        const bodyFont = toFont(options.bodyFont);\n        let bodyLineHeight = bodyFont.lineHeight;\n        let xLinePadding = 0;\n        const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);\n        const fillLineOfText = function(line) {\n            ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);\n            pt.y += bodyLineHeight + bodySpacing;\n        };\n        const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);\n        let bodyItem, textColor, lines, i, j, ilen, jlen;\n        ctx.textAlign = bodyAlign;\n        ctx.textBaseline = 'middle';\n        ctx.font = bodyFont.string;\n        pt.x = getAlignedX(this, bodyAlignForCalculation, options);\n        ctx.fillStyle = options.bodyColor;\n        each(this.beforeBody, fillLineOfText);\n        xLinePadding = displayColors && bodyAlignForCalculation !== 'right' ? bodyAlign === 'center' ? boxWidth / 2 + boxPadding : boxWidth + 2 + boxPadding : 0;\n        for(i = 0, ilen = body.length; i < ilen; ++i){\n            bodyItem = body[i];\n            textColor = this.labelTextColors[i];\n            ctx.fillStyle = textColor;\n            each(bodyItem.before, fillLineOfText);\n            lines = bodyItem.lines;\n            if (displayColors && lines.length) {\n                this._drawColorBox(ctx, pt, i, rtlHelper, options);\n                bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);\n            }\n            for(j = 0, jlen = lines.length; j < jlen; ++j){\n                fillLineOfText(lines[j]);\n                bodyLineHeight = bodyFont.lineHeight;\n            }\n            each(bodyItem.after, fillLineOfText);\n        }\n        xLinePadding = 0;\n        bodyLineHeight = bodyFont.lineHeight;\n        each(this.afterBody, fillLineOfText);\n        pt.y -= bodySpacing;\n    }\n    drawFooter(pt, ctx, options) {\n        const footer = this.footer;\n        const length = footer.length;\n        let footerFont, i;\n        if (length) {\n            const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);\n            pt.x = getAlignedX(this, options.footerAlign, options);\n            pt.y += options.footerMarginTop;\n            ctx.textAlign = rtlHelper.textAlign(options.footerAlign);\n            ctx.textBaseline = 'middle';\n            footerFont = toFont(options.footerFont);\n            ctx.fillStyle = options.footerColor;\n            ctx.font = footerFont.string;\n            for(i = 0; i < length; ++i){\n                ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2);\n                pt.y += footerFont.lineHeight + options.footerSpacing;\n            }\n        }\n    }\n    drawBackground(pt, ctx, tooltipSize, options) {\n        const { xAlign , yAlign  } = this;\n        const { x , y  } = pt;\n        const { width , height  } = tooltipSize;\n        const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(options.cornerRadius);\n        ctx.fillStyle = options.backgroundColor;\n        ctx.strokeStyle = options.borderColor;\n        ctx.lineWidth = options.borderWidth;\n        ctx.beginPath();\n        ctx.moveTo(x + topLeft, y);\n        if (yAlign === 'top') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x + width - topRight, y);\n        ctx.quadraticCurveTo(x + width, y, x + width, y + topRight);\n        if (yAlign === 'center' && xAlign === 'right') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x + width, y + height - bottomRight);\n        ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height);\n        if (yAlign === 'bottom') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x + bottomLeft, y + height);\n        ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft);\n        if (yAlign === 'center' && xAlign === 'left') {\n            this.drawCaret(pt, ctx, tooltipSize, options);\n        }\n        ctx.lineTo(x, y + topLeft);\n        ctx.quadraticCurveTo(x, y, x + topLeft, y);\n        ctx.closePath();\n        ctx.fill();\n        if (options.borderWidth > 0) {\n            ctx.stroke();\n        }\n    }\n _updateAnimationTarget(options) {\n        const chart = this.chart;\n        const anims = this.$animations;\n        const animX = anims && anims.x;\n        const animY = anims && anims.y;\n        if (animX || animY) {\n            const position = positioners[options.position].call(this, this._active, this._eventPosition);\n            if (!position) {\n                return;\n            }\n            const size = this._size = getTooltipSize(this, options);\n            const positionAndSize = Object.assign({}, position, this._size);\n            const alignment = determineAlignment(chart, options, positionAndSize);\n            const point = getBackgroundPoint(options, positionAndSize, alignment, chart);\n            if (animX._to !== point.x || animY._to !== point.y) {\n                this.xAlign = alignment.xAlign;\n                this.yAlign = alignment.yAlign;\n                this.width = size.width;\n                this.height = size.height;\n                this.caretX = position.x;\n                this.caretY = position.y;\n                this._resolveAnimations().update(this, point);\n            }\n        }\n    }\n _willRender() {\n        return !!this.opacity;\n    }\n    draw(ctx) {\n        const options = this.options.setContext(this.getContext());\n        let opacity = this.opacity;\n        if (!opacity) {\n            return;\n        }\n        this._updateAnimationTarget(options);\n        const tooltipSize = {\n            width: this.width,\n            height: this.height\n        };\n        const pt = {\n            x: this.x,\n            y: this.y\n        };\n        opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity;\n        const padding = toPadding(options.padding);\n        const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length;\n        if (options.enabled && hasTooltipContent) {\n            ctx.save();\n            ctx.globalAlpha = opacity;\n            this.drawBackground(pt, ctx, tooltipSize, options);\n            overrideTextDirection(ctx, options.textDirection);\n            pt.y += padding.top;\n            this.drawTitle(pt, ctx, options);\n            this.drawBody(pt, ctx, options);\n            this.drawFooter(pt, ctx, options);\n            restoreTextDirection(ctx, options.textDirection);\n            ctx.restore();\n        }\n    }\n getActiveElements() {\n        return this._active || [];\n    }\n setActiveElements(activeElements, eventPosition) {\n        const lastActive = this._active;\n        const active = activeElements.map(({ datasetIndex , index  })=>{\n            const meta = this.chart.getDatasetMeta(datasetIndex);\n            if (!meta) {\n                throw new Error('Cannot find a dataset at index ' + datasetIndex);\n            }\n            return {\n                datasetIndex,\n                element: meta.data[index],\n                index\n            };\n        });\n        const changed = !_elementsEqual(lastActive, active);\n        const positionChanged = this._positionChanged(active, eventPosition);\n        if (changed || positionChanged) {\n            this._active = active;\n            this._eventPosition = eventPosition;\n            this._ignoreReplayEvents = true;\n            this.update(true);\n        }\n    }\n handleEvent(e, replay, inChartArea = true) {\n        if (replay && this._ignoreReplayEvents) {\n            return false;\n        }\n        this._ignoreReplayEvents = false;\n        const options = this.options;\n        const lastActive = this._active || [];\n        const active = this._getActiveElements(e, lastActive, replay, inChartArea);\n        const positionChanged = this._positionChanged(active, e);\n        const changed = replay || !_elementsEqual(active, lastActive) || positionChanged;\n        if (changed) {\n            this._active = active;\n            if (options.enabled || options.external) {\n                this._eventPosition = {\n                    x: e.x,\n                    y: e.y\n                };\n                this.update(true, replay);\n            }\n        }\n        return changed;\n    }\n _getActiveElements(e, lastActive, replay, inChartArea) {\n        const options = this.options;\n        if (e.type === 'mouseout') {\n            return [];\n        }\n        if (!inChartArea) {\n            return lastActive;\n        }\n        const active = this.chart.getElementsAtEventForMode(e, options.mode, options, replay);\n        if (options.reverse) {\n            active.reverse();\n        }\n        return active;\n    }\n _positionChanged(active, e) {\n        const { caretX , caretY , options  } = this;\n        const position = positioners[options.position].call(this, active, e);\n        return position !== false && (caretX !== position.x || caretY !== position.y);\n    }\n}\nvar plugin_tooltip = {\n    id: 'tooltip',\n    _element: Tooltip,\n    positioners,\n    afterInit (chart, _args, options) {\n        if (options) {\n            chart.tooltip = new Tooltip({\n                chart,\n                options\n            });\n        }\n    },\n    beforeUpdate (chart, _args, options) {\n        if (chart.tooltip) {\n            chart.tooltip.initialize(options);\n        }\n    },\n    reset (chart, _args, options) {\n        if (chart.tooltip) {\n            chart.tooltip.initialize(options);\n        }\n    },\n    afterDraw (chart) {\n        const tooltip = chart.tooltip;\n        if (tooltip && tooltip._willRender()) {\n            const args = {\n                tooltip\n            };\n            if (chart.notifyPlugins('beforeTooltipDraw', {\n                ...args,\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            tooltip.draw(chart.ctx);\n            chart.notifyPlugins('afterTooltipDraw', args);\n        }\n    },\n    afterEvent (chart, args) {\n        if (chart.tooltip) {\n            const useFinalPosition = args.replay;\n            if (chart.tooltip.handleEvent(args.event, useFinalPosition, args.inChartArea)) {\n                args.changed = true;\n            }\n        }\n    },\n    defaults: {\n        enabled: true,\n        external: null,\n        position: 'average',\n        backgroundColor: 'rgba(0,0,0,0.8)',\n        titleColor: '#fff',\n        titleFont: {\n            weight: 'bold'\n        },\n        titleSpacing: 2,\n        titleMarginBottom: 6,\n        titleAlign: 'left',\n        bodyColor: '#fff',\n        bodySpacing: 2,\n        bodyFont: {},\n        bodyAlign: 'left',\n        footerColor: '#fff',\n        footerSpacing: 2,\n        footerMarginTop: 6,\n        footerFont: {\n            weight: 'bold'\n        },\n        footerAlign: 'left',\n        padding: 6,\n        caretPadding: 2,\n        caretSize: 5,\n        cornerRadius: 6,\n        boxHeight: (ctx, opts)=>opts.bodyFont.size,\n        boxWidth: (ctx, opts)=>opts.bodyFont.size,\n        multiKeyBackground: '#fff',\n        displayColors: true,\n        boxPadding: 0,\n        borderColor: 'rgba(0,0,0,0)',\n        borderWidth: 0,\n        animation: {\n            duration: 400,\n            easing: 'easeOutQuart'\n        },\n        animations: {\n            numbers: {\n                type: 'number',\n                properties: [\n                    'x',\n                    'y',\n                    'width',\n                    'height',\n                    'caretX',\n                    'caretY'\n                ]\n            },\n            opacity: {\n                easing: 'linear',\n                duration: 200\n            }\n        },\n        callbacks: defaultCallbacks\n    },\n    defaultRoutes: {\n        bodyFont: 'font',\n        footerFont: 'font',\n        titleFont: 'font'\n    },\n    descriptors: {\n        _scriptable: (name)=>name !== 'filter' && name !== 'itemSort' && name !== 'external',\n        _indexable: false,\n        callbacks: {\n            _scriptable: false,\n            _indexable: false\n        },\n        animation: {\n            _fallback: false\n        },\n        animations: {\n            _fallback: 'animation'\n        }\n    },\n    additionalOptionScopes: [\n        'interaction'\n    ]\n};\n\nvar plugins = /*#__PURE__*/Object.freeze({\n__proto__: null,\nColors: plugin_colors,\nDecimation: plugin_decimation,\nFiller: index,\nLegend: plugin_legend,\nSubTitle: plugin_subtitle,\nTitle: plugin_title,\nTooltip: plugin_tooltip\n});\n\nconst addIfString = (labels, raw, index, addedLabels)=>{\n    if (typeof raw === 'string') {\n        index = labels.push(raw) - 1;\n        addedLabels.unshift({\n            index,\n            label: raw\n        });\n    } else if (isNaN(raw)) {\n        index = null;\n    }\n    return index;\n};\nfunction findOrAddLabel(labels, raw, index, addedLabels) {\n    const first = labels.indexOf(raw);\n    if (first === -1) {\n        return addIfString(labels, raw, index, addedLabels);\n    }\n    const last = labels.lastIndexOf(raw);\n    return first !== last ? index : first;\n}\nconst validIndex = (index, max)=>index === null ? null : _limitValue(Math.round(index), 0, max);\nfunction _getLabelForValue(value) {\n    const labels = this.getLabels();\n    if (value >= 0 && value < labels.length) {\n        return labels[value];\n    }\n    return value;\n}\nclass CategoryScale extends Scale {\n    static id = 'category';\n static defaults = {\n        ticks: {\n            callback: _getLabelForValue\n        }\n    };\n    constructor(cfg){\n        super(cfg);\n         this._startValue = undefined;\n        this._valueRange = 0;\n        this._addedLabels = [];\n    }\n    init(scaleOptions) {\n        const added = this._addedLabels;\n        if (added.length) {\n            const labels = this.getLabels();\n            for (const { index , label  } of added){\n                if (labels[index] === label) {\n                    labels.splice(index, 1);\n                }\n            }\n            this._addedLabels = [];\n        }\n        super.init(scaleOptions);\n    }\n    parse(raw, index) {\n        if (isNullOrUndef(raw)) {\n            return null;\n        }\n        const labels = this.getLabels();\n        index = isFinite(index) && labels[index] === raw ? index : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels);\n        return validIndex(index, labels.length - 1);\n    }\n    determineDataLimits() {\n        const { minDefined , maxDefined  } = this.getUserBounds();\n        let { min , max  } = this.getMinMax(true);\n        if (this.options.bounds === 'ticks') {\n            if (!minDefined) {\n                min = 0;\n            }\n            if (!maxDefined) {\n                max = this.getLabels().length - 1;\n            }\n        }\n        this.min = min;\n        this.max = max;\n    }\n    buildTicks() {\n        const min = this.min;\n        const max = this.max;\n        const offset = this.options.offset;\n        const ticks = [];\n        let labels = this.getLabels();\n        labels = min === 0 && max === labels.length - 1 ? labels : labels.slice(min, max + 1);\n        this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);\n        this._startValue = this.min - (offset ? 0.5 : 0);\n        for(let value = min; value <= max; value++){\n            ticks.push({\n                value\n            });\n        }\n        return ticks;\n    }\n    getLabelForValue(value) {\n        return _getLabelForValue.call(this, value);\n    }\n configure() {\n        super.configure();\n        if (!this.isHorizontal()) {\n            this._reversePixels = !this._reversePixels;\n        }\n    }\n    getPixelForValue(value) {\n        if (typeof value !== 'number') {\n            value = this.parse(value);\n        }\n        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);\n    }\n    getPixelForTick(index) {\n        const ticks = this.ticks;\n        if (index < 0 || index > ticks.length - 1) {\n            return null;\n        }\n        return this.getPixelForValue(ticks[index].value);\n    }\n    getValueForPixel(pixel) {\n        return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);\n    }\n    getBasePixel() {\n        return this.bottom;\n    }\n}\n\nfunction generateTicks$1(generationOptions, dataRange) {\n    const ticks = [];\n    const MIN_SPACING = 1e-14;\n    const { bounds , step , min , max , precision , count , maxTicks , maxDigits , includeBounds  } = generationOptions;\n    const unit = step || 1;\n    const maxSpaces = maxTicks - 1;\n    const { min: rmin , max: rmax  } = dataRange;\n    const minDefined = !isNullOrUndef(min);\n    const maxDefined = !isNullOrUndef(max);\n    const countDefined = !isNullOrUndef(count);\n    const minSpacing = (rmax - rmin) / (maxDigits + 1);\n    let spacing = niceNum((rmax - rmin) / maxSpaces / unit) * unit;\n    let factor, niceMin, niceMax, numSpaces;\n    if (spacing < MIN_SPACING && !minDefined && !maxDefined) {\n        return [\n            {\n                value: rmin\n            },\n            {\n                value: rmax\n            }\n        ];\n    }\n    numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);\n    if (numSpaces > maxSpaces) {\n        spacing = niceNum(numSpaces * spacing / maxSpaces / unit) * unit;\n    }\n    if (!isNullOrUndef(precision)) {\n        factor = Math.pow(10, precision);\n        spacing = Math.ceil(spacing * factor) / factor;\n    }\n    if (bounds === 'ticks') {\n        niceMin = Math.floor(rmin / spacing) * spacing;\n        niceMax = Math.ceil(rmax / spacing) * spacing;\n    } else {\n        niceMin = rmin;\n        niceMax = rmax;\n    }\n    if (minDefined && maxDefined && step && almostWhole((max - min) / step, spacing / 1000)) {\n        numSpaces = Math.round(Math.min((max - min) / spacing, maxTicks));\n        spacing = (max - min) / numSpaces;\n        niceMin = min;\n        niceMax = max;\n    } else if (countDefined) {\n        niceMin = minDefined ? min : niceMin;\n        niceMax = maxDefined ? max : niceMax;\n        numSpaces = count - 1;\n        spacing = (niceMax - niceMin) / numSpaces;\n    } else {\n        numSpaces = (niceMax - niceMin) / spacing;\n        if (almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {\n            numSpaces = Math.round(numSpaces);\n        } else {\n            numSpaces = Math.ceil(numSpaces);\n        }\n    }\n    const decimalPlaces = Math.max(_decimalPlaces(spacing), _decimalPlaces(niceMin));\n    factor = Math.pow(10, isNullOrUndef(precision) ? decimalPlaces : precision);\n    niceMin = Math.round(niceMin * factor) / factor;\n    niceMax = Math.round(niceMax * factor) / factor;\n    let j = 0;\n    if (minDefined) {\n        if (includeBounds && niceMin !== min) {\n            ticks.push({\n                value: min\n            });\n            if (niceMin < min) {\n                j++;\n            }\n            if (almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) {\n                j++;\n            }\n        } else if (niceMin < min) {\n            j++;\n        }\n    }\n    for(; j < numSpaces; ++j){\n        ticks.push({\n            value: Math.round((niceMin + j * spacing) * factor) / factor\n        });\n    }\n    if (maxDefined && includeBounds && niceMax !== max) {\n        if (ticks.length && almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) {\n            ticks[ticks.length - 1].value = max;\n        } else {\n            ticks.push({\n                value: max\n            });\n        }\n    } else if (!maxDefined || niceMax === max) {\n        ticks.push({\n            value: niceMax\n        });\n    }\n    return ticks;\n}\nfunction relativeLabelSize(value, minSpacing, { horizontal , minRotation  }) {\n    const rad = toRadians(minRotation);\n    const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 0.001;\n    const length = 0.75 * minSpacing * ('' + value).length;\n    return Math.min(minSpacing / ratio, length);\n}\nclass LinearScaleBase extends Scale {\n    constructor(cfg){\n        super(cfg);\n         this.start = undefined;\n         this.end = undefined;\n         this._startValue = undefined;\n         this._endValue = undefined;\n        this._valueRange = 0;\n    }\n    parse(raw, index) {\n        if (isNullOrUndef(raw)) {\n            return null;\n        }\n        if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(+raw)) {\n            return null;\n        }\n        return +raw;\n    }\n    handleTickRangeOptions() {\n        const { beginAtZero  } = this.options;\n        const { minDefined , maxDefined  } = this.getUserBounds();\n        let { min , max  } = this;\n        const setMin = (v)=>min = minDefined ? min : v;\n        const setMax = (v)=>max = maxDefined ? max : v;\n        if (beginAtZero) {\n            const minSign = sign(min);\n            const maxSign = sign(max);\n            if (minSign < 0 && maxSign < 0) {\n                setMax(0);\n            } else if (minSign > 0 && maxSign > 0) {\n                setMin(0);\n            }\n        }\n        if (min === max) {\n            let offset = max === 0 ? 1 : Math.abs(max * 0.05);\n            setMax(max + offset);\n            if (!beginAtZero) {\n                setMin(min - offset);\n            }\n        }\n        this.min = min;\n        this.max = max;\n    }\n    getTickLimit() {\n        const tickOpts = this.options.ticks;\n        let { maxTicksLimit , stepSize  } = tickOpts;\n        let maxTicks;\n        if (stepSize) {\n            maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;\n            if (maxTicks > 1000) {\n                console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);\n                maxTicks = 1000;\n            }\n        } else {\n            maxTicks = this.computeTickLimit();\n            maxTicksLimit = maxTicksLimit || 11;\n        }\n        if (maxTicksLimit) {\n            maxTicks = Math.min(maxTicksLimit, maxTicks);\n        }\n        return maxTicks;\n    }\n computeTickLimit() {\n        return Number.POSITIVE_INFINITY;\n    }\n    buildTicks() {\n        const opts = this.options;\n        const tickOpts = opts.ticks;\n        let maxTicks = this.getTickLimit();\n        maxTicks = Math.max(2, maxTicks);\n        const numericGeneratorOptions = {\n            maxTicks,\n            bounds: opts.bounds,\n            min: opts.min,\n            max: opts.max,\n            precision: tickOpts.precision,\n            step: tickOpts.stepSize,\n            count: tickOpts.count,\n            maxDigits: this._maxDigits(),\n            horizontal: this.isHorizontal(),\n            minRotation: tickOpts.minRotation || 0,\n            includeBounds: tickOpts.includeBounds !== false\n        };\n        const dataRange = this._range || this;\n        const ticks = generateTicks$1(numericGeneratorOptions, dataRange);\n        if (opts.bounds === 'ticks') {\n            _setMinAndMaxByKey(ticks, this, 'value');\n        }\n        if (opts.reverse) {\n            ticks.reverse();\n            this.start = this.max;\n            this.end = this.min;\n        } else {\n            this.start = this.min;\n            this.end = this.max;\n        }\n        return ticks;\n    }\n configure() {\n        const ticks = this.ticks;\n        let start = this.min;\n        let end = this.max;\n        super.configure();\n        if (this.options.offset && ticks.length) {\n            const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;\n            start -= offset;\n            end += offset;\n        }\n        this._startValue = start;\n        this._endValue = end;\n        this._valueRange = end - start;\n    }\n    getLabelForValue(value) {\n        return formatNumber(value, this.chart.options.locale, this.options.ticks.format);\n    }\n}\n\nclass LinearScale extends LinearScaleBase {\n    static id = 'linear';\n static defaults = {\n        ticks: {\n            callback: Ticks.formatters.numeric\n        }\n    };\n    determineDataLimits() {\n        const { min , max  } = this.getMinMax(true);\n        this.min = isNumberFinite(min) ? min : 0;\n        this.max = isNumberFinite(max) ? max : 1;\n        this.handleTickRangeOptions();\n    }\n computeTickLimit() {\n        const horizontal = this.isHorizontal();\n        const length = horizontal ? this.width : this.height;\n        const minRotation = toRadians(this.options.ticks.minRotation);\n        const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;\n        const tickFont = this._resolveTickFontOptions(0);\n        return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));\n    }\n    getPixelForValue(value) {\n        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);\n    }\n    getValueForPixel(pixel) {\n        return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;\n    }\n}\n\nconst log10Floor = (v)=>Math.floor(log10(v));\nconst changeExponent = (v, m)=>Math.pow(10, log10Floor(v) + m);\nfunction isMajor(tickVal) {\n    const remain = tickVal / Math.pow(10, log10Floor(tickVal));\n    return remain === 1;\n}\nfunction steps(min, max, rangeExp) {\n    const rangeStep = Math.pow(10, rangeExp);\n    const start = Math.floor(min / rangeStep);\n    const end = Math.ceil(max / rangeStep);\n    return end - start;\n}\nfunction startExp(min, max) {\n    const range = max - min;\n    let rangeExp = log10Floor(range);\n    while(steps(min, max, rangeExp) > 10){\n        rangeExp++;\n    }\n    while(steps(min, max, rangeExp) < 10){\n        rangeExp--;\n    }\n    return Math.min(rangeExp, log10Floor(min));\n}\n function generateTicks(generationOptions, { min , max  }) {\n    min = finiteOrDefault(generationOptions.min, min);\n    const ticks = [];\n    const minExp = log10Floor(min);\n    let exp = startExp(min, max);\n    let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;\n    const stepSize = Math.pow(10, exp);\n    const base = minExp > exp ? Math.pow(10, minExp) : 0;\n    const start = Math.round((min - base) * precision) / precision;\n    const offset = Math.floor((min - base) / stepSize / 10) * stepSize * 10;\n    let significand = Math.floor((start - offset) / Math.pow(10, exp));\n    let value = finiteOrDefault(generationOptions.min, Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision);\n    while(value < max){\n        ticks.push({\n            value,\n            major: isMajor(value),\n            significand\n        });\n        if (significand >= 10) {\n            significand = significand < 15 ? 15 : 20;\n        } else {\n            significand++;\n        }\n        if (significand >= 20) {\n            exp++;\n            significand = 2;\n            precision = exp >= 0 ? 1 : precision;\n        }\n        value = Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision;\n    }\n    const lastTick = finiteOrDefault(generationOptions.max, value);\n    ticks.push({\n        value: lastTick,\n        major: isMajor(lastTick),\n        significand\n    });\n    return ticks;\n}\nclass LogarithmicScale extends Scale {\n    static id = 'logarithmic';\n static defaults = {\n        ticks: {\n            callback: Ticks.formatters.logarithmic,\n            major: {\n                enabled: true\n            }\n        }\n    };\n    constructor(cfg){\n        super(cfg);\n         this.start = undefined;\n         this.end = undefined;\n         this._startValue = undefined;\n        this._valueRange = 0;\n    }\n    parse(raw, index) {\n        const value = LinearScaleBase.prototype.parse.apply(this, [\n            raw,\n            index\n        ]);\n        if (value === 0) {\n            this._zero = true;\n            return undefined;\n        }\n        return isNumberFinite(value) && value > 0 ? value : null;\n    }\n    determineDataLimits() {\n        const { min , max  } = this.getMinMax(true);\n        this.min = isNumberFinite(min) ? Math.max(0, min) : null;\n        this.max = isNumberFinite(max) ? Math.max(0, max) : null;\n        if (this.options.beginAtZero) {\n            this._zero = true;\n        }\n        if (this._zero && this.min !== this._suggestedMin && !isNumberFinite(this._userMin)) {\n            this.min = min === changeExponent(this.min, 0) ? changeExponent(this.min, -1) : changeExponent(this.min, 0);\n        }\n        this.handleTickRangeOptions();\n    }\n    handleTickRangeOptions() {\n        const { minDefined , maxDefined  } = this.getUserBounds();\n        let min = this.min;\n        let max = this.max;\n        const setMin = (v)=>min = minDefined ? min : v;\n        const setMax = (v)=>max = maxDefined ? max : v;\n        if (min === max) {\n            if (min <= 0) {\n                setMin(1);\n                setMax(10);\n            } else {\n                setMin(changeExponent(min, -1));\n                setMax(changeExponent(max, +1));\n            }\n        }\n        if (min <= 0) {\n            setMin(changeExponent(max, -1));\n        }\n        if (max <= 0) {\n            setMax(changeExponent(min, +1));\n        }\n        this.min = min;\n        this.max = max;\n    }\n    buildTicks() {\n        const opts = this.options;\n        const generationOptions = {\n            min: this._userMin,\n            max: this._userMax\n        };\n        const ticks = generateTicks(generationOptions, this);\n        if (opts.bounds === 'ticks') {\n            _setMinAndMaxByKey(ticks, this, 'value');\n        }\n        if (opts.reverse) {\n            ticks.reverse();\n            this.start = this.max;\n            this.end = this.min;\n        } else {\n            this.start = this.min;\n            this.end = this.max;\n        }\n        return ticks;\n    }\n getLabelForValue(value) {\n        return value === undefined ? '0' : formatNumber(value, this.chart.options.locale, this.options.ticks.format);\n    }\n configure() {\n        const start = this.min;\n        super.configure();\n        this._startValue = log10(start);\n        this._valueRange = log10(this.max) - log10(start);\n    }\n    getPixelForValue(value) {\n        if (value === undefined || value === 0) {\n            value = this.min;\n        }\n        if (value === null || isNaN(value)) {\n            return NaN;\n        }\n        return this.getPixelForDecimal(value === this.min ? 0 : (log10(value) - this._startValue) / this._valueRange);\n    }\n    getValueForPixel(pixel) {\n        const decimal = this.getDecimalForPixel(pixel);\n        return Math.pow(10, this._startValue + decimal * this._valueRange);\n    }\n}\n\nfunction getTickBackdropHeight(opts) {\n    const tickOpts = opts.ticks;\n    if (tickOpts.display && opts.display) {\n        const padding = toPadding(tickOpts.backdropPadding);\n        return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height;\n    }\n    return 0;\n}\nfunction measureLabelSize(ctx, font, label) {\n    label = isArray(label) ? label : [\n        label\n    ];\n    return {\n        w: _longestText(ctx, font.string, label),\n        h: label.length * font.lineHeight\n    };\n}\nfunction determineLimits(angle, pos, size, min, max) {\n    if (angle === min || angle === max) {\n        return {\n            start: pos - size / 2,\n            end: pos + size / 2\n        };\n    } else if (angle < min || angle > max) {\n        return {\n            start: pos - size,\n            end: pos\n        };\n    }\n    return {\n        start: pos,\n        end: pos + size\n    };\n}\n function fitWithPointLabels(scale) {\n    const orig = {\n        l: scale.left + scale._padding.left,\n        r: scale.right - scale._padding.right,\n        t: scale.top + scale._padding.top,\n        b: scale.bottom - scale._padding.bottom\n    };\n    const limits = Object.assign({}, orig);\n    const labelSizes = [];\n    const padding = [];\n    const valueCount = scale._pointLabels.length;\n    const pointLabelOpts = scale.options.pointLabels;\n    const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0;\n    for(let i = 0; i < valueCount; i++){\n        const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i));\n        padding[i] = opts.padding;\n        const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle);\n        const plFont = toFont(opts.font);\n        const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);\n        labelSizes[i] = textSize;\n        const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle);\n        const angle = Math.round(toDegrees(angleRadians));\n        const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);\n        const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);\n        updateLimits(limits, orig, angleRadians, hLimits, vLimits);\n    }\n    scale.setCenterPoint(orig.l - limits.l, limits.r - orig.r, orig.t - limits.t, limits.b - orig.b);\n    scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);\n}\nfunction updateLimits(limits, orig, angle, hLimits, vLimits) {\n    const sin = Math.abs(Math.sin(angle));\n    const cos = Math.abs(Math.cos(angle));\n    let x = 0;\n    let y = 0;\n    if (hLimits.start < orig.l) {\n        x = (orig.l - hLimits.start) / sin;\n        limits.l = Math.min(limits.l, orig.l - x);\n    } else if (hLimits.end > orig.r) {\n        x = (hLimits.end - orig.r) / sin;\n        limits.r = Math.max(limits.r, orig.r + x);\n    }\n    if (vLimits.start < orig.t) {\n        y = (orig.t - vLimits.start) / cos;\n        limits.t = Math.min(limits.t, orig.t - y);\n    } else if (vLimits.end > orig.b) {\n        y = (vLimits.end - orig.b) / cos;\n        limits.b = Math.max(limits.b, orig.b + y);\n    }\n}\nfunction buildPointLabelItems(scale, labelSizes, padding) {\n    const items = [];\n    const valueCount = scale._pointLabels.length;\n    const opts = scale.options;\n    const extra = getTickBackdropHeight(opts) / 2;\n    const outerDistance = scale.drawingArea;\n    const additionalAngle = opts.pointLabels.centerPointLabels ? PI / valueCount : 0;\n    for(let i = 0; i < valueCount; i++){\n        const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i], additionalAngle);\n        const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI)));\n        const size = labelSizes[i];\n        const y = yForAngle(pointLabelPosition.y, size.h, angle);\n        const textAlign = getTextAlignForAngle(angle);\n        const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign);\n        items.push({\n            x: pointLabelPosition.x,\n            y,\n            textAlign,\n            left,\n            top: y,\n            right: left + size.w,\n            bottom: y + size.h\n        });\n    }\n    return items;\n}\nfunction getTextAlignForAngle(angle) {\n    if (angle === 0 || angle === 180) {\n        return 'center';\n    } else if (angle < 180) {\n        return 'left';\n    }\n    return 'right';\n}\nfunction leftForTextAlign(x, w, align) {\n    if (align === 'right') {\n        x -= w;\n    } else if (align === 'center') {\n        x -= w / 2;\n    }\n    return x;\n}\nfunction yForAngle(y, h, angle) {\n    if (angle === 90 || angle === 270) {\n        y -= h / 2;\n    } else if (angle > 270 || angle < 90) {\n        y -= h;\n    }\n    return y;\n}\nfunction drawPointLabels(scale, labelCount) {\n    const { ctx , options: { pointLabels  }  } = scale;\n    for(let i = labelCount - 1; i >= 0; i--){\n        const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i));\n        const plFont = toFont(optsAtIndex.font);\n        const { x , y , textAlign , left , top , right , bottom  } = scale._pointLabelItems[i];\n        const { backdropColor  } = optsAtIndex;\n        if (!isNullOrUndef(backdropColor)) {\n            const borderRadius = toTRBLCorners(optsAtIndex.borderRadius);\n            const padding = toPadding(optsAtIndex.backdropPadding);\n            ctx.fillStyle = backdropColor;\n            const backdropLeft = left - padding.left;\n            const backdropTop = top - padding.top;\n            const backdropWidth = right - left + padding.width;\n            const backdropHeight = bottom - top + padding.height;\n            if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                ctx.beginPath();\n                addRoundedRectPath(ctx, {\n                    x: backdropLeft,\n                    y: backdropTop,\n                    w: backdropWidth,\n                    h: backdropHeight,\n                    radius: borderRadius\n                });\n                ctx.fill();\n            } else {\n                ctx.fillRect(backdropLeft, backdropTop, backdropWidth, backdropHeight);\n            }\n        }\n        renderText(ctx, scale._pointLabels[i], x, y + plFont.lineHeight / 2, plFont, {\n            color: optsAtIndex.color,\n            textAlign: textAlign,\n            textBaseline: 'middle'\n        });\n    }\n}\nfunction pathRadiusLine(scale, radius, circular, labelCount) {\n    const { ctx  } = scale;\n    if (circular) {\n        ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU);\n    } else {\n        let pointPosition = scale.getPointPosition(0, radius);\n        ctx.moveTo(pointPosition.x, pointPosition.y);\n        for(let i = 1; i < labelCount; i++){\n            pointPosition = scale.getPointPosition(i, radius);\n            ctx.lineTo(pointPosition.x, pointPosition.y);\n        }\n    }\n}\nfunction drawRadiusLine(scale, gridLineOpts, radius, labelCount, borderOpts) {\n    const ctx = scale.ctx;\n    const circular = gridLineOpts.circular;\n    const { color , lineWidth  } = gridLineOpts;\n    if (!circular && !labelCount || !color || !lineWidth || radius < 0) {\n        return;\n    }\n    ctx.save();\n    ctx.strokeStyle = color;\n    ctx.lineWidth = lineWidth;\n    ctx.setLineDash(borderOpts.dash);\n    ctx.lineDashOffset = borderOpts.dashOffset;\n    ctx.beginPath();\n    pathRadiusLine(scale, radius, circular, labelCount);\n    ctx.closePath();\n    ctx.stroke();\n    ctx.restore();\n}\nfunction createPointLabelContext(parent, index, label) {\n    return createContext(parent, {\n        label,\n        index,\n        type: 'pointLabel'\n    });\n}\nclass RadialLinearScale extends LinearScaleBase {\n    static id = 'radialLinear';\n static defaults = {\n        display: true,\n        animate: true,\n        position: 'chartArea',\n        angleLines: {\n            display: true,\n            lineWidth: 1,\n            borderDash: [],\n            borderDashOffset: 0.0\n        },\n        grid: {\n            circular: false\n        },\n        startAngle: 0,\n        ticks: {\n            showLabelBackdrop: true,\n            callback: Ticks.formatters.numeric\n        },\n        pointLabels: {\n            backdropColor: undefined,\n            backdropPadding: 2,\n            display: true,\n            font: {\n                size: 10\n            },\n            callback (label) {\n                return label;\n            },\n            padding: 5,\n            centerPointLabels: false\n        }\n    };\n    static defaultRoutes = {\n        'angleLines.color': 'borderColor',\n        'pointLabels.color': 'color',\n        'ticks.color': 'color'\n    };\n    static descriptors = {\n        angleLines: {\n            _fallback: 'grid'\n        }\n    };\n    constructor(cfg){\n        super(cfg);\n         this.xCenter = undefined;\n         this.yCenter = undefined;\n         this.drawingArea = undefined;\n         this._pointLabels = [];\n        this._pointLabelItems = [];\n    }\n    setDimensions() {\n        const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2);\n        const w = this.width = this.maxWidth - padding.width;\n        const h = this.height = this.maxHeight - padding.height;\n        this.xCenter = Math.floor(this.left + w / 2 + padding.left);\n        this.yCenter = Math.floor(this.top + h / 2 + padding.top);\n        this.drawingArea = Math.floor(Math.min(w, h) / 2);\n    }\n    determineDataLimits() {\n        const { min , max  } = this.getMinMax(false);\n        this.min = isNumberFinite(min) && !isNaN(min) ? min : 0;\n        this.max = isNumberFinite(max) && !isNaN(max) ? max : 0;\n        this.handleTickRangeOptions();\n    }\n computeTickLimit() {\n        return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));\n    }\n    generateTickLabels(ticks) {\n        LinearScaleBase.prototype.generateTickLabels.call(this, ticks);\n        this._pointLabels = this.getLabels().map((value, index)=>{\n            const label = callback(this.options.pointLabels.callback, [\n                value,\n                index\n            ], this);\n            return label || label === 0 ? label : '';\n        }).filter((v, i)=>this.chart.getDataVisibility(i));\n    }\n    fit() {\n        const opts = this.options;\n        if (opts.display && opts.pointLabels.display) {\n            fitWithPointLabels(this);\n        } else {\n            this.setCenterPoint(0, 0, 0, 0);\n        }\n    }\n    setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {\n        this.xCenter += Math.floor((leftMovement - rightMovement) / 2);\n        this.yCenter += Math.floor((topMovement - bottomMovement) / 2);\n        this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement));\n    }\n    getIndexAngle(index) {\n        const angleMultiplier = TAU / (this._pointLabels.length || 1);\n        const startAngle = this.options.startAngle || 0;\n        return _normalizeAngle(index * angleMultiplier + toRadians(startAngle));\n    }\n    getDistanceFromCenterForValue(value) {\n        if (isNullOrUndef(value)) {\n            return NaN;\n        }\n        const scalingFactor = this.drawingArea / (this.max - this.min);\n        if (this.options.reverse) {\n            return (this.max - value) * scalingFactor;\n        }\n        return (value - this.min) * scalingFactor;\n    }\n    getValueForDistanceFromCenter(distance) {\n        if (isNullOrUndef(distance)) {\n            return NaN;\n        }\n        const scaledDistance = distance / (this.drawingArea / (this.max - this.min));\n        return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance;\n    }\n    getPointLabelContext(index) {\n        const pointLabels = this._pointLabels || [];\n        if (index >= 0 && index < pointLabels.length) {\n            const pointLabel = pointLabels[index];\n            return createPointLabelContext(this.getContext(), index, pointLabel);\n        }\n    }\n    getPointPosition(index, distanceFromCenter, additionalAngle = 0) {\n        const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle;\n        return {\n            x: Math.cos(angle) * distanceFromCenter + this.xCenter,\n            y: Math.sin(angle) * distanceFromCenter + this.yCenter,\n            angle\n        };\n    }\n    getPointPositionForValue(index, value) {\n        return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));\n    }\n    getBasePosition(index) {\n        return this.getPointPositionForValue(index || 0, this.getBaseValue());\n    }\n    getPointLabelPosition(index) {\n        const { left , top , right , bottom  } = this._pointLabelItems[index];\n        return {\n            left,\n            top,\n            right,\n            bottom\n        };\n    }\n drawBackground() {\n        const { backgroundColor , grid: { circular  }  } = this.options;\n        if (backgroundColor) {\n            const ctx = this.ctx;\n            ctx.save();\n            ctx.beginPath();\n            pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length);\n            ctx.closePath();\n            ctx.fillStyle = backgroundColor;\n            ctx.fill();\n            ctx.restore();\n        }\n    }\n drawGrid() {\n        const ctx = this.ctx;\n        const opts = this.options;\n        const { angleLines , grid , border  } = opts;\n        const labelCount = this._pointLabels.length;\n        let i, offset, position;\n        if (opts.pointLabels.display) {\n            drawPointLabels(this, labelCount);\n        }\n        if (grid.display) {\n            this.ticks.forEach((tick, index)=>{\n                if (index !== 0) {\n                    offset = this.getDistanceFromCenterForValue(tick.value);\n                    const context = this.getContext(index);\n                    const optsAtIndex = grid.setContext(context);\n                    const optsAtIndexBorder = border.setContext(context);\n                    drawRadiusLine(this, optsAtIndex, offset, labelCount, optsAtIndexBorder);\n                }\n            });\n        }\n        if (angleLines.display) {\n            ctx.save();\n            for(i = labelCount - 1; i >= 0; i--){\n                const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i));\n                const { color , lineWidth  } = optsAtIndex;\n                if (!lineWidth || !color) {\n                    continue;\n                }\n                ctx.lineWidth = lineWidth;\n                ctx.strokeStyle = color;\n                ctx.setLineDash(optsAtIndex.borderDash);\n                ctx.lineDashOffset = optsAtIndex.borderDashOffset;\n                offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max);\n                position = this.getPointPosition(i, offset);\n                ctx.beginPath();\n                ctx.moveTo(this.xCenter, this.yCenter);\n                ctx.lineTo(position.x, position.y);\n                ctx.stroke();\n            }\n            ctx.restore();\n        }\n    }\n drawBorder() {}\n drawLabels() {\n        const ctx = this.ctx;\n        const opts = this.options;\n        const tickOpts = opts.ticks;\n        if (!tickOpts.display) {\n            return;\n        }\n        const startAngle = this.getIndexAngle(0);\n        let offset, width;\n        ctx.save();\n        ctx.translate(this.xCenter, this.yCenter);\n        ctx.rotate(startAngle);\n        ctx.textAlign = 'center';\n        ctx.textBaseline = 'middle';\n        this.ticks.forEach((tick, index)=>{\n            if (index === 0 && !opts.reverse) {\n                return;\n            }\n            const optsAtIndex = tickOpts.setContext(this.getContext(index));\n            const tickFont = toFont(optsAtIndex.font);\n            offset = this.getDistanceFromCenterForValue(this.ticks[index].value);\n            if (optsAtIndex.showLabelBackdrop) {\n                ctx.font = tickFont.string;\n                width = ctx.measureText(tick.label).width;\n                ctx.fillStyle = optsAtIndex.backdropColor;\n                const padding = toPadding(optsAtIndex.backdropPadding);\n                ctx.fillRect(-width / 2 - padding.left, -offset - tickFont.size / 2 - padding.top, width + padding.width, tickFont.size + padding.height);\n            }\n            renderText(ctx, tick.label, 0, -offset, tickFont, {\n                color: optsAtIndex.color\n            });\n        });\n        ctx.restore();\n    }\n drawTitle() {}\n}\n\nconst INTERVALS = {\n    millisecond: {\n        common: true,\n        size: 1,\n        steps: 1000\n    },\n    second: {\n        common: true,\n        size: 1000,\n        steps: 60\n    },\n    minute: {\n        common: true,\n        size: 60000,\n        steps: 60\n    },\n    hour: {\n        common: true,\n        size: 3600000,\n        steps: 24\n    },\n    day: {\n        common: true,\n        size: 86400000,\n        steps: 30\n    },\n    week: {\n        common: false,\n        size: 604800000,\n        steps: 4\n    },\n    month: {\n        common: true,\n        size: 2.628e9,\n        steps: 12\n    },\n    quarter: {\n        common: false,\n        size: 7.884e9,\n        steps: 4\n    },\n    year: {\n        common: true,\n        size: 3.154e10\n    }\n};\n const UNITS =  /* #__PURE__ */ Object.keys(INTERVALS);\n function sorter(a, b) {\n    return a - b;\n}\n function parse(scale, input) {\n    if (isNullOrUndef(input)) {\n        return null;\n    }\n    const adapter = scale._adapter;\n    const { parser , round , isoWeekday  } = scale._parseOpts;\n    let value = input;\n    if (typeof parser === 'function') {\n        value = parser(value);\n    }\n    if (!isNumberFinite(value)) {\n        value = typeof parser === 'string' ? adapter.parse(value,  parser) : adapter.parse(value);\n    }\n    if (value === null) {\n        return null;\n    }\n    if (round) {\n        value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, 'isoWeek', isoWeekday) : adapter.startOf(value, round);\n    }\n    return +value;\n}\n function determineUnitForAutoTicks(minUnit, min, max, capacity) {\n    const ilen = UNITS.length;\n    for(let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i){\n        const interval = INTERVALS[UNITS[i]];\n        const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER;\n        if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {\n            return UNITS[i];\n        }\n    }\n    return UNITS[ilen - 1];\n}\n function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {\n    for(let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--){\n        const unit = UNITS[i];\n        if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {\n            return unit;\n        }\n    }\n    return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];\n}\n function determineMajorUnit(unit) {\n    for(let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i){\n        if (INTERVALS[UNITS[i]].common) {\n            return UNITS[i];\n        }\n    }\n}\n function addTick(ticks, time, timestamps) {\n    if (!timestamps) {\n        ticks[time] = true;\n    } else if (timestamps.length) {\n        const { lo , hi  } = _lookup(timestamps, time);\n        const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];\n        ticks[timestamp] = true;\n    }\n}\n function setMajorTicks(scale, ticks, map, majorUnit) {\n    const adapter = scale._adapter;\n    const first = +adapter.startOf(ticks[0].value, majorUnit);\n    const last = ticks[ticks.length - 1].value;\n    let major, index;\n    for(major = first; major <= last; major = +adapter.add(major, 1, majorUnit)){\n        index = map[major];\n        if (index >= 0) {\n            ticks[index].major = true;\n        }\n    }\n    return ticks;\n}\n function ticksFromTimestamps(scale, values, majorUnit) {\n    const ticks = [];\n     const map = {};\n    const ilen = values.length;\n    let i, value;\n    for(i = 0; i < ilen; ++i){\n        value = values[i];\n        map[value] = i;\n        ticks.push({\n            value,\n            major: false\n        });\n    }\n    return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map, majorUnit);\n}\nclass TimeScale extends Scale {\n    static id = 'time';\n static defaults = {\n bounds: 'data',\n        adapters: {},\n        time: {\n            parser: false,\n            unit: false,\n            round: false,\n            isoWeekday: false,\n            minUnit: 'millisecond',\n            displayFormats: {}\n        },\n        ticks: {\n source: 'auto',\n            callback: false,\n            major: {\n                enabled: false\n            }\n        }\n    };\n constructor(props){\n        super(props);\n         this._cache = {\n            data: [],\n            labels: [],\n            all: []\n        };\n         this._unit = 'day';\n         this._majorUnit = undefined;\n        this._offsets = {};\n        this._normalized = false;\n        this._parseOpts = undefined;\n    }\n    init(scaleOpts, opts = {}) {\n        const time = scaleOpts.time || (scaleOpts.time = {});\n         const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date);\n        adapter.init(opts);\n        mergeIf(time.displayFormats, adapter.formats());\n        this._parseOpts = {\n            parser: time.parser,\n            round: time.round,\n            isoWeekday: time.isoWeekday\n        };\n        super.init(scaleOpts);\n        this._normalized = opts.normalized;\n    }\n parse(raw, index) {\n        if (raw === undefined) {\n            return null;\n        }\n        return parse(this, raw);\n    }\n    beforeLayout() {\n        super.beforeLayout();\n        this._cache = {\n            data: [],\n            labels: [],\n            all: []\n        };\n    }\n    determineDataLimits() {\n        const options = this.options;\n        const adapter = this._adapter;\n        const unit = options.time.unit || 'day';\n        let { min , max , minDefined , maxDefined  } = this.getUserBounds();\n function _applyBounds(bounds) {\n            if (!minDefined && !isNaN(bounds.min)) {\n                min = Math.min(min, bounds.min);\n            }\n            if (!maxDefined && !isNaN(bounds.max)) {\n                max = Math.max(max, bounds.max);\n            }\n        }\n        if (!minDefined || !maxDefined) {\n            _applyBounds(this._getLabelBounds());\n            if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {\n                _applyBounds(this.getMinMax(false));\n            }\n        }\n        min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);\n        max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;\n        this.min = Math.min(min, max - 1);\n        this.max = Math.max(min + 1, max);\n    }\n _getLabelBounds() {\n        const arr = this.getLabelTimestamps();\n        let min = Number.POSITIVE_INFINITY;\n        let max = Number.NEGATIVE_INFINITY;\n        if (arr.length) {\n            min = arr[0];\n            max = arr[arr.length - 1];\n        }\n        return {\n            min,\n            max\n        };\n    }\n buildTicks() {\n        const options = this.options;\n        const timeOpts = options.time;\n        const tickOpts = options.ticks;\n        const timestamps = tickOpts.source === 'labels' ? this.getLabelTimestamps() : this._generate();\n        if (options.bounds === 'ticks' && timestamps.length) {\n            this.min = this._userMin || timestamps[0];\n            this.max = this._userMax || timestamps[timestamps.length - 1];\n        }\n        const min = this.min;\n        const max = this.max;\n        const ticks = _filterBetween(timestamps, min, max);\n        this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max));\n        this._majorUnit = !tickOpts.major.enabled || this._unit === 'year' ? undefined : determineMajorUnit(this._unit);\n        this.initOffsets(timestamps);\n        if (options.reverse) {\n            ticks.reverse();\n        }\n        return ticksFromTimestamps(this, ticks, this._majorUnit);\n    }\n    afterAutoSkip() {\n        if (this.options.offsetAfterAutoskip) {\n            this.initOffsets(this.ticks.map((tick)=>+tick.value));\n        }\n    }\n initOffsets(timestamps = []) {\n        let start = 0;\n        let end = 0;\n        let first, last;\n        if (this.options.offset && timestamps.length) {\n            first = this.getDecimalForValue(timestamps[0]);\n            if (timestamps.length === 1) {\n                start = 1 - first;\n            } else {\n                start = (this.getDecimalForValue(timestamps[1]) - first) / 2;\n            }\n            last = this.getDecimalForValue(timestamps[timestamps.length - 1]);\n            if (timestamps.length === 1) {\n                end = last;\n            } else {\n                end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;\n            }\n        }\n        const limit = timestamps.length < 3 ? 0.5 : 0.25;\n        start = _limitValue(start, 0, limit);\n        end = _limitValue(end, 0, limit);\n        this._offsets = {\n            start,\n            end,\n            factor: 1 / (start + 1 + end)\n        };\n    }\n _generate() {\n        const adapter = this._adapter;\n        const min = this.min;\n        const max = this.max;\n        const options = this.options;\n        const timeOpts = options.time;\n        const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));\n        const stepSize = valueOrDefault(options.ticks.stepSize, 1);\n        const weekday = minor === 'week' ? timeOpts.isoWeekday : false;\n        const hasWeekday = isNumber(weekday) || weekday === true;\n        const ticks = {};\n        let first = min;\n        let time, count;\n        if (hasWeekday) {\n            first = +adapter.startOf(first, 'isoWeek', weekday);\n        }\n        first = +adapter.startOf(first, hasWeekday ? 'day' : minor);\n        if (adapter.diff(max, min, minor) > 100000 * stepSize) {\n            throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);\n        }\n        const timestamps = options.ticks.source === 'data' && this.getDataTimestamps();\n        for(time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++){\n            addTick(ticks, time, timestamps);\n        }\n        if (time === max || options.bounds === 'ticks' || count === 1) {\n            addTick(ticks, time, timestamps);\n        }\n        return Object.keys(ticks).sort((a, b)=>a - b).map((x)=>+x);\n    }\n getLabelForValue(value) {\n        const adapter = this._adapter;\n        const timeOpts = this.options.time;\n        if (timeOpts.tooltipFormat) {\n            return adapter.format(value, timeOpts.tooltipFormat);\n        }\n        return adapter.format(value, timeOpts.displayFormats.datetime);\n    }\n format(value, format) {\n        const options = this.options;\n        const formats = options.time.displayFormats;\n        const unit = this._unit;\n        const fmt = format || formats[unit];\n        return this._adapter.format(value, fmt);\n    }\n _tickFormatFunction(time, index, ticks, format) {\n        const options = this.options;\n        const formatter = options.ticks.callback;\n        if (formatter) {\n            return callback(formatter, [\n                time,\n                index,\n                ticks\n            ], this);\n        }\n        const formats = options.time.displayFormats;\n        const unit = this._unit;\n        const majorUnit = this._majorUnit;\n        const minorFormat = unit && formats[unit];\n        const majorFormat = majorUnit && formats[majorUnit];\n        const tick = ticks[index];\n        const major = majorUnit && majorFormat && tick && tick.major;\n        return this._adapter.format(time, format || (major ? majorFormat : minorFormat));\n    }\n generateTickLabels(ticks) {\n        let i, ilen, tick;\n        for(i = 0, ilen = ticks.length; i < ilen; ++i){\n            tick = ticks[i];\n            tick.label = this._tickFormatFunction(tick.value, i, ticks);\n        }\n    }\n getDecimalForValue(value) {\n        return value === null ? NaN : (value - this.min) / (this.max - this.min);\n    }\n getPixelForValue(value) {\n        const offsets = this._offsets;\n        const pos = this.getDecimalForValue(value);\n        return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);\n    }\n getValueForPixel(pixel) {\n        const offsets = this._offsets;\n        const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;\n        return this.min + pos * (this.max - this.min);\n    }\n _getLabelSize(label) {\n        const ticksOpts = this.options.ticks;\n        const tickLabelWidth = this.ctx.measureText(label).width;\n        const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);\n        const cosRotation = Math.cos(angle);\n        const sinRotation = Math.sin(angle);\n        const tickFontSize = this._resolveTickFontOptions(0).size;\n        return {\n            w: tickLabelWidth * cosRotation + tickFontSize * sinRotation,\n            h: tickLabelWidth * sinRotation + tickFontSize * cosRotation\n        };\n    }\n _getLabelCapacity(exampleTime) {\n        const timeOpts = this.options.time;\n        const displayFormats = timeOpts.displayFormats;\n        const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;\n        const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [\n            exampleTime\n        ], this._majorUnit), format);\n        const size = this._getLabelSize(exampleLabel);\n        const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;\n        return capacity > 0 ? capacity : 1;\n    }\n getDataTimestamps() {\n        let timestamps = this._cache.data || [];\n        let i, ilen;\n        if (timestamps.length) {\n            return timestamps;\n        }\n        const metas = this.getMatchingVisibleMetas();\n        if (this._normalized && metas.length) {\n            return this._cache.data = metas[0].controller.getAllParsedValues(this);\n        }\n        for(i = 0, ilen = metas.length; i < ilen; ++i){\n            timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));\n        }\n        return this._cache.data = this.normalize(timestamps);\n    }\n getLabelTimestamps() {\n        const timestamps = this._cache.labels || [];\n        let i, ilen;\n        if (timestamps.length) {\n            return timestamps;\n        }\n        const labels = this.getLabels();\n        for(i = 0, ilen = labels.length; i < ilen; ++i){\n            timestamps.push(parse(this, labels[i]));\n        }\n        return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps);\n    }\n normalize(values) {\n        return _arrayUnique(values.sort(sorter));\n    }\n}\n\nfunction interpolate(table, val, reverse) {\n    let lo = 0;\n    let hi = table.length - 1;\n    let prevSource, nextSource, prevTarget, nextTarget;\n    if (reverse) {\n        if (val >= table[lo].pos && val <= table[hi].pos) {\n            ({ lo , hi  } = _lookupByKey(table, 'pos', val));\n        }\n        ({ pos: prevSource , time: prevTarget  } = table[lo]);\n        ({ pos: nextSource , time: nextTarget  } = table[hi]);\n    } else {\n        if (val >= table[lo].time && val <= table[hi].time) {\n            ({ lo , hi  } = _lookupByKey(table, 'time', val));\n        }\n        ({ time: prevSource , pos: prevTarget  } = table[lo]);\n        ({ time: nextSource , pos: nextTarget  } = table[hi]);\n    }\n    const span = nextSource - prevSource;\n    return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget;\n}\nclass TimeSeriesScale extends TimeScale {\n    static id = 'timeseries';\n static defaults = TimeScale.defaults;\n constructor(props){\n        super(props);\n         this._table = [];\n         this._minPos = undefined;\n         this._tableRange = undefined;\n    }\n initOffsets() {\n        const timestamps = this._getTimestampsForTable();\n        const table = this._table = this.buildLookupTable(timestamps);\n        this._minPos = interpolate(table, this.min);\n        this._tableRange = interpolate(table, this.max) - this._minPos;\n        super.initOffsets(timestamps);\n    }\n buildLookupTable(timestamps) {\n        const { min , max  } = this;\n        const items = [];\n        const table = [];\n        let i, ilen, prev, curr, next;\n        for(i = 0, ilen = timestamps.length; i < ilen; ++i){\n            curr = timestamps[i];\n            if (curr >= min && curr <= max) {\n                items.push(curr);\n            }\n        }\n        if (items.length < 2) {\n            return [\n                {\n                    time: min,\n                    pos: 0\n                },\n                {\n                    time: max,\n                    pos: 1\n                }\n            ];\n        }\n        for(i = 0, ilen = items.length; i < ilen; ++i){\n            next = items[i + 1];\n            prev = items[i - 1];\n            curr = items[i];\n            if (Math.round((next + prev) / 2) !== curr) {\n                table.push({\n                    time: curr,\n                    pos: i / (ilen - 1)\n                });\n            }\n        }\n        return table;\n    }\n _getTimestampsForTable() {\n        let timestamps = this._cache.all || [];\n        if (timestamps.length) {\n            return timestamps;\n        }\n        const data = this.getDataTimestamps();\n        const label = this.getLabelTimestamps();\n        if (data.length && label.length) {\n            timestamps = this.normalize(data.concat(label));\n        } else {\n            timestamps = data.length ? data : label;\n        }\n        timestamps = this._cache.all = timestamps;\n        return timestamps;\n    }\n getDecimalForValue(value) {\n        return (interpolate(this._table, value) - this._minPos) / this._tableRange;\n    }\n getValueForPixel(pixel) {\n        const offsets = this._offsets;\n        const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;\n        return interpolate(this._table, decimal * this._tableRange + this._minPos, true);\n    }\n}\n\nvar scales = /*#__PURE__*/Object.freeze({\n__proto__: null,\nCategoryScale: CategoryScale,\nLinearScale: LinearScale,\nLogarithmicScale: LogarithmicScale,\nRadialLinearScale: RadialLinearScale,\nTimeScale: TimeScale,\nTimeSeriesScale: TimeSeriesScale\n});\n\nconst registerables = [\n    controllers,\n    elements,\n    plugins,\n    scales\n];\n\nexport { Animation, Animations, ArcElement, BarController, BarElement, BasePlatform, BasicPlatform, BubbleController, CategoryScale, Chart, plugin_colors as Colors, DatasetController, plugin_decimation as Decimation, DomPlatform, DoughnutController, Element, index as Filler, Interaction, plugin_legend as Legend, LineController, LineElement, LinearScale, LogarithmicScale, PieController, PointElement, PolarAreaController, RadarController, RadialLinearScale, Scale, ScatterController, plugin_subtitle as SubTitle, TimeScale, TimeSeriesScale, plugin_title as Title, plugin_tooltip as Tooltip, adapters as _adapters, _detectPlatform, animator, controllers, elements, layouts, plugins, registerables, registry, scales };\n//# sourceMappingURL=chart.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/chart.umd.js",
    "content": "/*!\n * Chart.js v4.2.1\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\n!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define(e):(t=\"undefined\"!=typeof globalThis?globalThis:t||self).Chart=e()}(this,(function(){\"use strict\";var t=Object.freeze({__proto__:null,get Colors(){return jo},get Decimation(){return Uo},get Filler(){return ha},get Legend(){return fa},get SubTitle(){return ba},get Title(){return pa},get Tooltip(){return La}});function e(){}const i=(()=>{let t=0;return()=>t++})();function s(t){return null==t}function n(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return\"[object\"===e.slice(0,7)&&\"Array]\"===e.slice(-6)}function o(t){return null!==t&&\"[object Object]\"===Object.prototype.toString.call(t)}function a(t){return(\"number\"==typeof t||t instanceof Number)&&isFinite(+t)}function r(t,e){return a(t)?t:e}function l(t,e){return void 0===t?e:t}const h=(t,e)=>\"string\"==typeof t&&t.endsWith(\"%\")?parseFloat(t)/100:+t/e,c=(t,e)=>\"string\"==typeof t&&t.endsWith(\"%\")?parseFloat(t)/100*e:+t;function d(t,e,i){if(t&&\"function\"==typeof t.call)return t.apply(i,e)}function u(t,e,i,s){let a,r,l;if(n(t))if(r=t.length,s)for(a=r-1;a>=0;a--)e.call(i,t[a],a);else for(a=0;a<r;a++)e.call(i,t[a],a);else if(o(t))for(l=Object.keys(t),r=l.length,a=0;a<r;a++)e.call(i,t[l[a]],l[a])}function f(t,e){let i,s,n,o;if(!t||!e||t.length!==e.length)return!1;for(i=0,s=t.length;i<s;++i)if(n=t[i],o=e[i],n.datasetIndex!==o.datasetIndex||n.index!==o.index)return!1;return!0}function g(t){if(n(t))return t.map(g);if(o(t)){const e=Object.create(null),i=Object.keys(t),s=i.length;let n=0;for(;n<s;++n)e[i[n]]=g(t[i[n]]);return e}return t}function p(t){return-1===[\"__proto__\",\"prototype\",\"constructor\"].indexOf(t)}function m(t,e,i,s){if(!p(t))return;const n=e[t],a=i[t];o(n)&&o(a)?b(n,a,s):e[t]=g(a)}function b(t,e,i){const s=n(e)?e:[e],a=s.length;if(!o(t))return t;const r=(i=i||{}).merger||m;let l;for(let e=0;e<a;++e){if(l=s[e],!o(l))continue;const n=Object.keys(l);for(let e=0,s=n.length;e<s;++e)r(n[e],t,l,i)}return t}function x(t,e){return b(t,e,{merger:_})}function _(t,e,i){if(!p(t))return;const s=e[t],n=i[t];o(s)&&o(n)?x(s,n):Object.prototype.hasOwnProperty.call(e,t)||(e[t]=g(n))}const y={\"\":t=>t,x:t=>t.x,y:t=>t.y};function v(t){const e=t.split(\".\"),i=[];let s=\"\";for(const t of e)s+=t,s.endsWith(\"\\\\\")?s=s.slice(0,-1)+\".\":(i.push(s),s=\"\");return i}function M(t,e){const i=y[e]||(y[e]=function(t){const e=v(t);return t=>{for(const i of e){if(\"\"===i)break;t=t&&t[i]}return t}}(e));return i(t)}function w(t){return t.charAt(0).toUpperCase()+t.slice(1)}const k=t=>void 0!==t,S=t=>\"function\"==typeof t,P=(t,e)=>{if(t.size!==e.size)return!1;for(const i of t)if(!e.has(i))return!1;return!0};function D(t){return\"mouseup\"===t.type||\"click\"===t.type||\"contextmenu\"===t.type}const C=Math.PI,O=2*C,A=O+C,T=Number.POSITIVE_INFINITY,L=C/180,E=C/2,R=C/4,I=2*C/3,z=Math.log10,F=Math.sign;function V(t,e,i){return Math.abs(t-e)<i}function B(t){const e=Math.round(t);t=V(t,e,t/1e3)?e:t;const i=Math.pow(10,Math.floor(z(t))),s=t/i;return(s<=1?1:s<=2?2:s<=5?5:10)*i}function N(t){const e=[],i=Math.sqrt(t);let s;for(s=1;s<i;s++)t%s==0&&(e.push(s),e.push(t/s));return i===(0|i)&&e.push(i),e.sort(((t,e)=>t-e)).pop(),e}function W(t){return!isNaN(parseFloat(t))&&isFinite(t)}function H(t,e){const i=Math.round(t);return i-e<=t&&i+e>=t}function j(t,e,i){let s,n,o;for(s=0,n=t.length;s<n;s++)o=t[s][i],isNaN(o)||(e.min=Math.min(e.min,o),e.max=Math.max(e.max,o))}function $(t){return t*(C/180)}function Y(t){return t*(180/C)}function U(t){if(!a(t))return;let e=1,i=0;for(;Math.round(t*e)/e!==t;)e*=10,i++;return i}function X(t,e){const i=e.x-t.x,s=e.y-t.y,n=Math.sqrt(i*i+s*s);let o=Math.atan2(s,i);return o<-.5*C&&(o+=O),{angle:o,distance:n}}function q(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function K(t,e){return(t-e+A)%O-C}function G(t){return(t%O+O)%O}function Z(t,e,i,s){const n=G(t),o=G(e),a=G(i),r=G(o-n),l=G(a-n),h=G(n-o),c=G(n-a);return n===o||n===a||s&&o===a||r>l&&h<c}function J(t,e,i){return Math.max(e,Math.min(i,t))}function Q(t){return J(t,-32768,32767)}function tt(t,e,i,s=1e-6){return t>=Math.min(e,i)-s&&t<=Math.max(e,i)+s}function et(t,e,i){i=i||(i=>t[i]<e);let s,n=t.length-1,o=0;for(;n-o>1;)s=o+n>>1,i(s)?o=s:n=s;return{lo:o,hi:n}}const it=(t,e,i,s)=>et(t,i,s?s=>{const n=t[s][e];return n<i||n===i&&t[s+1][e]===i}:s=>t[s][e]<i),st=(t,e,i)=>et(t,i,(s=>t[s][e]>=i));function nt(t,e,i){let s=0,n=t.length;for(;s<n&&t[s]<e;)s++;for(;n>s&&t[n-1]>i;)n--;return s>0||n<t.length?t.slice(s,n):t}const ot=[\"push\",\"pop\",\"shift\",\"splice\",\"unshift\"];function at(t,e){t._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,\"_chartjs\",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),ot.forEach((e=>{const i=\"_onData\"+w(e),s=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value(...e){const n=s.apply(this,e);return t._chartjs.listeners.forEach((t=>{\"function\"==typeof t[i]&&t[i](...e)})),n}})})))}function rt(t,e){const i=t._chartjs;if(!i)return;const s=i.listeners,n=s.indexOf(e);-1!==n&&s.splice(n,1),s.length>0||(ot.forEach((e=>{delete t[e]})),delete t._chartjs)}function lt(t){const e=new Set;let i,s;for(i=0,s=t.length;i<s;++i)e.add(t[i]);return e.size===s?t:Array.from(e)}const ht=\"undefined\"==typeof window?function(t){return t()}:window.requestAnimationFrame;function ct(t,e){let i=[],s=!1;return function(...n){i=n,s||(s=!0,ht.call(window,(()=>{s=!1,t.apply(e,i)})))}}function dt(t,e){let i;return function(...s){return e?(clearTimeout(i),i=setTimeout(t,e,s)):t.apply(this,s),e}}const ut=t=>\"start\"===t?\"left\":\"end\"===t?\"right\":\"center\",ft=(t,e,i)=>\"start\"===t?e:\"end\"===t?i:(e+i)/2,gt=(t,e,i,s)=>t===(s?\"left\":\"right\")?i:\"center\"===t?(e+i)/2:e;function pt(t,e,i){const s=e.length;let n=0,o=s;if(t._sorted){const{iScale:a,_parsed:r}=t,l=a.axis,{min:h,max:c,minDefined:d,maxDefined:u}=a.getUserBounds();d&&(n=J(Math.min(it(r,a.axis,h).lo,i?s:it(e,l,a.getPixelForValue(h)).lo),0,s-1)),o=u?J(Math.max(it(r,a.axis,c,!0).hi+1,i?0:it(e,l,a.getPixelForValue(c),!0).hi+1),n,s)-n:s-n}return{start:n,count:o}}function mt(t){const{xScale:e,yScale:i,_scaleRanges:s}=t,n={xmin:e.min,xmax:e.max,ymin:i.min,ymax:i.max};if(!s)return t._scaleRanges=n,!0;const o=s.xmin!==e.min||s.xmax!==e.max||s.ymin!==i.min||s.ymax!==i.max;return Object.assign(s,n),o}class bt{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(t,e,i,s){const n=e.listeners[s],o=e.duration;n.forEach((s=>s({chart:t,initial:e.initial,numSteps:o,currentStep:Math.min(i-e.start,o)})))}_refresh(){this._request||(this._running=!0,this._request=ht.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(t=Date.now()){let e=0;this._charts.forEach(((i,s)=>{if(!i.running||!i.items.length)return;const n=i.items;let o,a=n.length-1,r=!1;for(;a>=0;--a)o=n[a],o._active?(o._total>i.duration&&(i.duration=o._total),o.tick(t),r=!0):(n[a]=n[n.length-1],n.pop());r&&(s.draw(),this._notify(s,i,t,\"progress\")),n.length||(i.running=!1,this._notify(s,i,t,\"complete\"),i.initial=!1),e+=n.length})),this._lastDate=t,0===e&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){e&&e.length&&this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce(((t,e)=>Math.max(t,e._duration)),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!!(e&&e.running&&e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),\"complete\")}remove(t){return this._charts.delete(t)}}var xt=new bt;\n/*!\n * @kurkle/color v0.3.0\n * https://github.com/kurkle/color#readme\n * (c) 2022 Jukka Kurkela\n * Released under the MIT License\n */function _t(t){return t+.5|0}const yt=(t,e,i)=>Math.max(Math.min(t,i),e);function vt(t){return yt(_t(2.55*t),0,255)}function Mt(t){return yt(_t(255*t),0,255)}function wt(t){return yt(_t(t/2.55)/100,0,1)}function kt(t){return yt(_t(100*t),0,100)}const St={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Pt=[...\"0123456789ABCDEF\"],Dt=t=>Pt[15&t],Ct=t=>Pt[(240&t)>>4]+Pt[15&t],Ot=t=>(240&t)>>4==(15&t);function At(t){var e=(t=>Ot(t.r)&&Ot(t.g)&&Ot(t.b)&&Ot(t.a))(t)?Dt:Ct;return t?\"#\"+e(t.r)+e(t.g)+e(t.b)+((t,e)=>t<255?e(t):\"\")(t.a,e):void 0}const Tt=/^(hsla?|hwb|hsv)\\(\\s*([-+.e\\d]+)(?:deg)?[\\s,]+([-+.e\\d]+)%[\\s,]+([-+.e\\d]+)%(?:[\\s,]+([-+.e\\d]+)(%)?)?\\s*\\)$/;function Lt(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function Et(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function Rt(t,e,i){const s=Lt(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function It(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=function(t,e,i,s,n){return t===n?(e-i)/s+(e<i?6:0):e===n?(i-t)/s+2:(t-e)/s+4}(e,i,s,h,n),r=60*r+.5),[0|r,l||0,a]}function zt(t,e,i,s){return(Array.isArray(e)?t(e[0],e[1],e[2]):t(e,i,s)).map(Mt)}function Ft(t,e,i){return zt(Lt,t,e,i)}function Vt(t){return(t%360+360)%360}function Bt(t){const e=Tt.exec(t);let i,s=255;if(!e)return;e[5]!==i&&(s=e[6]?vt(+e[5]):Mt(+e[5]));const n=Vt(+e[2]),o=+e[3]/100,a=+e[4]/100;return i=\"hwb\"===e[1]?function(t,e,i){return zt(Rt,t,e,i)}(n,o,a):\"hsv\"===e[1]?function(t,e,i){return zt(Et,t,e,i)}(n,o,a):Ft(n,o,a),{r:i[0],g:i[1],b:i[2],a:s}}const Nt={x:\"dark\",Z:\"light\",Y:\"re\",X:\"blu\",W:\"gr\",V:\"medium\",U:\"slate\",A:\"ee\",T:\"ol\",S:\"or\",B:\"ra\",C:\"lateg\",D:\"ights\",R:\"in\",Q:\"turquois\",E:\"hi\",P:\"ro\",O:\"al\",N:\"le\",M:\"de\",L:\"yello\",F:\"en\",K:\"ch\",G:\"arks\",H:\"ea\",I:\"ightg\",J:\"wh\"},Wt={OiceXe:\"f0f8ff\",antiquewEte:\"faebd7\",aqua:\"ffff\",aquamarRe:\"7fffd4\",azuY:\"f0ffff\",beige:\"f5f5dc\",bisque:\"ffe4c4\",black:\"0\",blanKedOmond:\"ffebcd\",Xe:\"ff\",XeviTet:\"8a2be2\",bPwn:\"a52a2a\",burlywood:\"deb887\",caMtXe:\"5f9ea0\",KartYuse:\"7fff00\",KocTate:\"d2691e\",cSO:\"ff7f50\",cSnflowerXe:\"6495ed\",cSnsilk:\"fff8dc\",crimson:\"dc143c\",cyan:\"ffff\",xXe:\"8b\",xcyan:\"8b8b\",xgTMnPd:\"b8860b\",xWay:\"a9a9a9\",xgYF:\"6400\",xgYy:\"a9a9a9\",xkhaki:\"bdb76b\",xmagFta:\"8b008b\",xTivegYF:\"556b2f\",xSange:\"ff8c00\",xScEd:\"9932cc\",xYd:\"8b0000\",xsOmon:\"e9967a\",xsHgYF:\"8fbc8f\",xUXe:\"483d8b\",xUWay:\"2f4f4f\",xUgYy:\"2f4f4f\",xQe:\"ced1\",xviTet:\"9400d3\",dAppRk:\"ff1493\",dApskyXe:\"bfff\",dimWay:\"696969\",dimgYy:\"696969\",dodgerXe:\"1e90ff\",fiYbrick:\"b22222\",flSOwEte:\"fffaf0\",foYstWAn:\"228b22\",fuKsia:\"ff00ff\",gaRsbSo:\"dcdcdc\",ghostwEte:\"f8f8ff\",gTd:\"ffd700\",gTMnPd:\"daa520\",Way:\"808080\",gYF:\"8000\",gYFLw:\"adff2f\",gYy:\"808080\",honeyMw:\"f0fff0\",hotpRk:\"ff69b4\",RdianYd:\"cd5c5c\",Rdigo:\"4b0082\",ivSy:\"fffff0\",khaki:\"f0e68c\",lavFMr:\"e6e6fa\",lavFMrXsh:\"fff0f5\",lawngYF:\"7cfc00\",NmoncEffon:\"fffacd\",ZXe:\"add8e6\",ZcSO:\"f08080\",Zcyan:\"e0ffff\",ZgTMnPdLw:\"fafad2\",ZWay:\"d3d3d3\",ZgYF:\"90ee90\",ZgYy:\"d3d3d3\",ZpRk:\"ffb6c1\",ZsOmon:\"ffa07a\",ZsHgYF:\"20b2aa\",ZskyXe:\"87cefa\",ZUWay:\"778899\",ZUgYy:\"778899\",ZstAlXe:\"b0c4de\",ZLw:\"ffffe0\",lime:\"ff00\",limegYF:\"32cd32\",lRF:\"faf0e6\",magFta:\"ff00ff\",maPon:\"800000\",VaquamarRe:\"66cdaa\",VXe:\"cd\",VScEd:\"ba55d3\",VpurpN:\"9370db\",VsHgYF:\"3cb371\",VUXe:\"7b68ee\",VsprRggYF:\"fa9a\",VQe:\"48d1cc\",VviTetYd:\"c71585\",midnightXe:\"191970\",mRtcYam:\"f5fffa\",mistyPse:\"ffe4e1\",moccasR:\"ffe4b5\",navajowEte:\"ffdead\",navy:\"80\",Tdlace:\"fdf5e6\",Tive:\"808000\",TivedBb:\"6b8e23\",Sange:\"ffa500\",SangeYd:\"ff4500\",ScEd:\"da70d6\",pOegTMnPd:\"eee8aa\",pOegYF:\"98fb98\",pOeQe:\"afeeee\",pOeviTetYd:\"db7093\",papayawEp:\"ffefd5\",pHKpuff:\"ffdab9\",peru:\"cd853f\",pRk:\"ffc0cb\",plum:\"dda0dd\",powMrXe:\"b0e0e6\",purpN:\"800080\",YbeccapurpN:\"663399\",Yd:\"ff0000\",Psybrown:\"bc8f8f\",PyOXe:\"4169e1\",saddNbPwn:\"8b4513\",sOmon:\"fa8072\",sandybPwn:\"f4a460\",sHgYF:\"2e8b57\",sHshell:\"fff5ee\",siFna:\"a0522d\",silver:\"c0c0c0\",skyXe:\"87ceeb\",UXe:\"6a5acd\",UWay:\"708090\",UgYy:\"708090\",snow:\"fffafa\",sprRggYF:\"ff7f\",stAlXe:\"4682b4\",tan:\"d2b48c\",teO:\"8080\",tEstN:\"d8bfd8\",tomato:\"ff6347\",Qe:\"40e0d0\",viTet:\"ee82ee\",JHt:\"f5deb3\",wEte:\"ffffff\",wEtesmoke:\"f5f5f5\",Lw:\"ffff00\",LwgYF:\"9acd32\"};let Ht;function jt(t){Ht||(Ht=function(){const t={},e=Object.keys(Wt),i=Object.keys(Nt);let s,n,o,a,r;for(s=0;s<e.length;s++){for(a=r=e[s],n=0;n<i.length;n++)o=i[n],r=r.replace(o,Nt[o]);o=parseInt(Wt[a],16),t[r]=[o>>16&255,o>>8&255,255&o]}return t}(),Ht.transparent=[0,0,0,0]);const e=Ht[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const $t=/^rgba?\\(\\s*([-+.\\d]+)(%)?[\\s,]+([-+.e\\d]+)(%)?[\\s,]+([-+.e\\d]+)(%)?(?:[\\s,/]+([-+.e\\d]+)(%)?)?\\s*\\)$/;const Yt=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ut=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Xt(t,e,i){if(t){let s=It(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=Ft(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function qt(t,e){return t?Object.assign(e||{},t):t}function Kt(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=Mt(t[3]))):(e=qt(t,{r:0,g:0,b:0,a:1})).a=Mt(e.a),e}function Gt(t){return\"r\"===t.charAt(0)?function(t){const e=$t.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=e[8]?vt(t):yt(255*t,0,255)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?vt(i):yt(i,0,255)),s=255&(e[4]?vt(s):yt(s,0,255)),n=255&(e[6]?vt(n):yt(n,0,255)),{r:i,g:s,b:n,a:o}}}(t):Bt(t)}class Zt{constructor(t){if(t instanceof Zt)return t;const e=typeof t;let i;var s,n,o;\"object\"===e?i=Kt(t):\"string\"===e&&(o=(s=t).length,\"#\"===s[0]&&(4===o||5===o?n={r:255&17*St[s[1]],g:255&17*St[s[2]],b:255&17*St[s[3]],a:5===o?17*St[s[4]]:255}:7!==o&&9!==o||(n={r:St[s[1]]<<4|St[s[2]],g:St[s[3]]<<4|St[s[4]],b:St[s[5]]<<4|St[s[6]],a:9===o?St[s[7]]<<4|St[s[8]]:255})),i=n||jt(t)||Gt(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=qt(this._rgb);return t&&(t.a=wt(t.a)),t}set rgb(t){this._rgb=Kt(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${wt(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):void 0;var t}hexString(){return this._valid?At(this._rgb):void 0}hslString(){return this._valid?function(t){if(!t)return;const e=It(t),i=e[0],s=kt(e[1]),n=kt(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${wt(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let n;const o=e===n?.5:e,a=2*o-1,r=i.a-s.a,l=((a*r==-1?a:(a+r)/(1+a*r))+1)/2;n=1-l,i.r=255&l*i.r+n*s.r+.5,i.g=255&l*i.g+n*s.g+.5,i.b=255&l*i.b+n*s.b+.5,i.a=o*i.a+(1-o)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=function(t,e,i){const s=Ut(wt(t.r)),n=Ut(wt(t.g)),o=Ut(wt(t.b));return{r:Mt(Yt(s+i*(Ut(wt(e.r))-s))),g:Mt(Yt(n+i*(Ut(wt(e.g))-n))),b:Mt(Yt(o+i*(Ut(wt(e.b))-o))),a:t.a+i*(e.a-t.a)}}(this._rgb,t._rgb,e)),this}clone(){return new Zt(this.rgb)}alpha(t){return this._rgb.a=Mt(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=_t(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Xt(this._rgb,2,t),this}darken(t){return Xt(this._rgb,2,-t),this}saturate(t){return Xt(this._rgb,1,t),this}desaturate(t){return Xt(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=It(t);i[0]=Vt(i[0]+e),i=Ft(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function Jt(t){if(t&&\"object\"==typeof t){const e=t.toString();return\"[object CanvasPattern]\"===e||\"[object CanvasGradient]\"===e}return!1}function Qt(t){return Jt(t)?t:new Zt(t)}function te(t){return Jt(t)?t:new Zt(t).saturate(.5).darken(.1).hexString()}const ee=[\"x\",\"y\",\"borderWidth\",\"radius\",\"tension\"],ie=[\"color\",\"borderColor\",\"backgroundColor\"];const se=new Map;function ne(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=se.get(i);return s||(s=new Intl.NumberFormat(t,e),se.set(i,s)),s}(e,i).format(t)}const oe={values:t=>n(t)?t:\"\"+t,numeric(t,e,i){if(0===t)return\"0\";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n=\"scientific\"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=z(Math.abs(o)),r=Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),ne(t,s,l)},logarithmic(t,e,i){if(0===t)return\"0\";const s=i[e].significand||t/Math.pow(10,Math.floor(z(t)));return[1,2,3,5,10,15].includes(s)||e>.8*i.length?oe.numeric.call(this,t,e,i):\"\"}};var ae={formatters:oe};const re=Object.create(null),le=Object.create(null);function he(t,e){if(!e)return t;const i=e.split(\".\");for(let e=0,s=i.length;e<s;++e){const s=i[e];t=t[s]||(t[s]=Object.create(null))}return t}function ce(t,e,i){return\"string\"==typeof e?b(he(t,e),i):b(he(t,\"\"),e)}class de{constructor(t,e){this.animation=void 0,this.backgroundColor=\"rgba(0,0,0,0.1)\",this.borderColor=\"rgba(0,0,0,0.1)\",this.color=\"#666\",this.datasets={},this.devicePixelRatio=t=>t.chart.platform.getDevicePixelRatio(),this.elements={},this.events=[\"mousemove\",\"mouseout\",\"click\",\"touchstart\",\"touchmove\"],this.font={family:\"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",size:12,style:\"normal\",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>te(e.backgroundColor),this.hoverBorderColor=(t,e)=>te(e.borderColor),this.hoverColor=(t,e)=>te(e.color),this.indexAxis=\"x\",this.interaction={mode:\"nearest\",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ce(this,t,e)}get(t){return he(this,t)}describe(t,e){return ce(le,t,e)}override(t,e){return ce(re,t,e)}route(t,e,i,s){const n=he(this,t),a=he(this,i),r=\"_\"+e;Object.defineProperties(n,{[r]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[r],e=a[s];return o(t)?Object.assign({},e,t):l(t,e)},set(t){this[r]=t}}})}apply(t){t.forEach((t=>t(this)))}}var ue=new de({_scriptable:t=>!t.startsWith(\"on\"),_indexable:t=>\"events\"!==t,hover:{_fallback:\"interaction\"},interaction:{_scriptable:!1,_indexable:!1}},[function(t){t.set(\"animation\",{delay:void 0,duration:1e3,easing:\"easeOutQuart\",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe(\"animation\",{_fallback:!1,_indexable:!1,_scriptable:t=>\"onProgress\"!==t&&\"onComplete\"!==t&&\"fn\"!==t}),t.set(\"animations\",{colors:{type:\"color\",properties:ie},numbers:{type:\"number\",properties:ee}}),t.describe(\"animations\",{_fallback:\"animation\"}),t.set(\"transitions\",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:\"transparent\"},visible:{type:\"boolean\",duration:0}}},hide:{animations:{colors:{to:\"transparent\"},visible:{type:\"boolean\",easing:\"linear\",fn:t=>0|t}}}})},function(t){t.set(\"layout\",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})},function(t){t.set(\"scale\",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:\"ticks\",grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:\"\",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:\"\",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:ae.formatters.values,minor:{},major:{},align:\"center\",crossAlign:\"near\",showLabelBackdrop:!1,backdropColor:\"rgba(255, 255, 255, 0.75)\",backdropPadding:2}}),t.route(\"scale.ticks\",\"color\",\"\",\"color\"),t.route(\"scale.grid\",\"color\",\"\",\"borderColor\"),t.route(\"scale.border\",\"color\",\"\",\"borderColor\"),t.route(\"scale.title\",\"color\",\"\",\"color\"),t.describe(\"scale\",{_fallback:!1,_scriptable:t=>!t.startsWith(\"before\")&&!t.startsWith(\"after\")&&\"callback\"!==t&&\"parser\"!==t,_indexable:t=>\"borderDash\"!==t&&\"tickBorderDash\"!==t&&\"dash\"!==t}),t.describe(\"scales\",{_fallback:\"scale\"}),t.describe(\"scale.ticks\",{_scriptable:t=>\"backdropPadding\"!==t&&\"callback\"!==t,_indexable:t=>\"backdropPadding\"!==t})}]);function fe(){return\"undefined\"!=typeof window&&\"undefined\"!=typeof document}function ge(t){let e=t.parentNode;return e&&\"[object ShadowRoot]\"===e.toString()&&(e=e.host),e}function pe(t,e,i){let s;return\"string\"==typeof t?(s=parseInt(t,10),-1!==t.indexOf(\"%\")&&(s=s/100*e.parentNode[i])):s=t,s}const me=t=>t.ownerDocument.defaultView.getComputedStyle(t,null);function be(t,e){return me(t).getPropertyValue(e)}const xe=[\"top\",\"right\",\"bottom\",\"left\"];function _e(t,e,i){const s={};i=i?\"-\"+i:\"\";for(let n=0;n<4;n++){const o=xe[n];s[o]=parseFloat(t[e+\"-\"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}function ye(t,e){if(\"native\"in t)return t;const{canvas:i,currentDevicePixelRatio:s}=e,n=me(i),o=\"border-box\"===n.boxSizing,a=_e(n,\"padding\"),r=_e(n,\"border\",\"width\"),{x:l,y:h,box:c}=function(t,e){const i=t.touches,s=i&&i.length?i[0]:t,{offsetX:n,offsetY:o}=s;let a,r,l=!1;if(((t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot))(n,o,t.target))a=n,r=o;else{const t=e.getBoundingClientRect();a=s.clientX-t.left,r=s.clientY-t.top,l=!0}return{x:a,y:r,box:l}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const ve=t=>Math.round(10*t)/10;function Me(t,e,i,s){const n=me(t),o=_e(n,\"margin\"),a=pe(n.maxWidth,t,\"clientWidth\")||T,r=pe(n.maxHeight,t,\"clientHeight\")||T,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=ge(t);if(o){const t=o.getBoundingClientRect(),a=me(o),r=_e(a,\"border\",\"width\"),l=_e(a,\"padding\");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=pe(a.maxWidth,o,\"clientWidth\"),n=pe(a.maxHeight,o,\"clientHeight\")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||T,maxHeight:n||T}}(t,e,i);let{width:h,height:c}=l;if(\"content-box\"===n.boxSizing){const t=_e(n,\"border\",\"width\"),e=_e(n,\"padding\");h-=e.width+t.width,c-=e.height+t.height}h=Math.max(0,h-o.width),c=Math.max(0,s?h/s:c-o.height),h=ve(Math.min(h,a,l.maxWidth)),c=ve(Math.min(c,r,l.maxHeight)),h&&!c&&(c=ve(h/2));return(void 0!==e||void 0!==i)&&s&&l.height&&c>l.height&&(c=l.height,h=ve(Math.floor(c*s))),{width:h,height:c}}function we(t,e,i){const s=e||1,n=Math.floor(t.height*s),o=Math.floor(t.width*s);t.height=Math.floor(t.height),t.width=Math.floor(t.width);const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const ke=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};window.addEventListener(\"test\",null,e),window.removeEventListener(\"test\",null,e)}catch(t){}return t}();function Se(t,e){const i=be(t,e),s=i&&i.match(/^(\\d+)(\\.\\d+)?px$/);return s?+s[1]:void 0}function Pe(t){return!t||s(t.size)||s(t.family)?null:(t.style?t.style+\" \":\"\")+(t.weight?t.weight+\" \":\"\")+t.size+\"px \"+t.family}function De(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function Ce(t,e,i,s){let o=(s=s||{}).data=s.data||{},a=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(o=s.data={},a=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let r=0;const l=i.length;let h,c,d,u,f;for(h=0;h<l;h++)if(u=i[h],null!=u&&!0!==n(u))r=De(t,o,a,r,u);else if(n(u))for(c=0,d=u.length;c<d;c++)f=u[c],null==f||n(f)||(r=De(t,o,a,r,f));t.restore();const g=a.length/2;if(g>i.length){for(h=0;h<g;h++)delete o[a[h]];a.splice(0,g)}return r}function Oe(t,e,i){const s=t.currentDevicePixelRatio,n=0!==i?Math.max(i/2,.5):0;return Math.round((e-n)*s)/s+n}function Ae(t,e){(e=e||t.getContext(\"2d\")).save(),e.resetTransform(),e.clearRect(0,0,t.width,t.height),e.restore()}function Te(t,e,i,s){Le(t,e,i,s,null)}function Le(t,e,i,s,n){let o,a,r,l,h,c,d,u;const f=e.pointStyle,g=e.rotation,p=e.radius;let m=(g||0)*L;if(f&&\"object\"==typeof f&&(o=f.toString(),\"[object HTMLImageElement]\"===o||\"[object HTMLCanvasElement]\"===o))return t.save(),t.translate(i,s),t.rotate(m),t.drawImage(f,-f.width/2,-f.height/2,f.width,f.height),void t.restore();if(!(isNaN(p)||p<=0)){switch(t.beginPath(),f){default:n?t.ellipse(i,s,n/2,p,0,0,O):t.arc(i,s,p,0,O),t.closePath();break;case\"triangle\":c=n?n/2:p,t.moveTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),t.closePath();break;case\"rectRounded\":h=.516*p,l=p-h,a=Math.cos(m+R)*l,d=Math.cos(m+R)*(n?n/2-h:l),r=Math.sin(m+R)*l,u=Math.sin(m+R)*(n?n/2-h:l),t.arc(i-d,s-r,h,m-C,m-E),t.arc(i+u,s-a,h,m-E,m),t.arc(i+d,s+r,h,m,m+E),t.arc(i-u,s+a,h,m+E,m+C),t.closePath();break;case\"rect\":if(!g){l=Math.SQRT1_2*p,c=n?n/2:l,t.rect(i-c,s-l,2*c,2*l);break}m+=R;case\"rectRot\":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+u,s-a),t.lineTo(i+d,s+r),t.lineTo(i-u,s+a),t.closePath();break;case\"crossRot\":m+=R;case\"cross\":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case\"star\":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a),m+=R,d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case\"line\":a=n?n/2:Math.cos(m)*p,r=Math.sin(m)*p,t.moveTo(i-a,s-r),t.lineTo(i+a,s+r);break;case\"dash\":t.moveTo(i,s),t.lineTo(i+Math.cos(m)*(n?n/2:p),s+Math.sin(m)*p);break;case!1:t.closePath()}t.fill(),e.borderWidth>0&&t.stroke()}}function Ee(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.x<e.right+i&&t.y>e.top-i&&t.y<e.bottom+i}function Re(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()}function Ie(t){t.restore()}function ze(t,e,i,s,n){if(!e)return t.lineTo(i.x,i.y);if(\"middle\"===n){const s=(e.x+i.x)/2;t.lineTo(s,e.y),t.lineTo(s,i.y)}else\"after\"===n!=!!s?t.lineTo(e.x,i.y):t.lineTo(i.x,e.y);t.lineTo(i.x,i.y)}function Fe(t,e,i,s){if(!e)return t.lineTo(i.x,i.y);t.bezierCurveTo(s?e.cp1x:e.cp2x,s?e.cp1y:e.cp2y,s?i.cp2x:i.cp1x,s?i.cp2y:i.cp1y,i.x,i.y)}function Ve(t,e,i,o,a,r={}){const l=n(e)?e:[e],h=r.strokeWidth>0&&\"\"!==r.strokeColor;let c,d;for(t.save(),t.font=a.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]);s(e.rotation)||t.rotate(e.rotation);e.color&&(t.fillStyle=e.color);e.textAlign&&(t.textAlign=e.textAlign);e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,r),c=0;c<l.length;++c)d=l[c],r.backdrop&&Ne(t,r.backdrop),h&&(r.strokeColor&&(t.strokeStyle=r.strokeColor),s(r.strokeWidth)||(t.lineWidth=r.strokeWidth),t.strokeText(d,i,o,r.maxWidth)),t.fillText(d,i,o,r.maxWidth),Be(t,i,o,d,r),o+=a.lineHeight;t.restore()}function Be(t,e,i,s,n){if(n.strikethrough||n.underline){const o=t.measureText(s),a=e-o.actualBoundingBoxLeft,r=e+o.actualBoundingBoxRight,l=i-o.actualBoundingBoxAscent,h=i+o.actualBoundingBoxDescent,c=n.strikethrough?(l+h)/2:h;t.strokeStyle=t.fillStyle,t.beginPath(),t.lineWidth=n.decorationWidth||2,t.moveTo(a,c),t.lineTo(r,c),t.stroke()}}function Ne(t,e){const i=t.fillStyle;t.fillStyle=e.color,t.fillRect(e.left,e.top,e.width,e.height),t.fillStyle=i}function We(t,e){const{x:i,y:s,w:n,h:o,radius:a}=e;t.arc(i+a.topLeft,s+a.topLeft,a.topLeft,-E,C,!0),t.lineTo(i,s+o-a.bottomLeft),t.arc(i+a.bottomLeft,s+o-a.bottomLeft,a.bottomLeft,C,E,!0),t.lineTo(i+n-a.bottomRight,s+o),t.arc(i+n-a.bottomRight,s+o-a.bottomRight,a.bottomRight,E,0,!0),t.lineTo(i+n,s+a.topRight),t.arc(i+n-a.topRight,s+a.topRight,a.topRight,0,-E,!0),t.lineTo(i+a.topLeft,s)}function He(t,e=[\"\"],i=t,s,n=(()=>t[0])){k(s)||(s=Qe(\"_fallback\",t));const o={[Symbol.toStringTag]:\"Object\",_cacheable:!0,_scopes:t,_rootScopes:i,_fallback:s,_getTarget:n,override:n=>He([n,...t],e,i,s)};return new Proxy(o,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>Xe(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=Qe(Ye(o,t),i),k(n))return Ue(t,n)?Ze(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>ti(t).includes(e),ownKeys:t=>ti(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function je(t,e,i,s){const a={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:$e(t,s),setContext:e=>je(t,e,i,s),override:n=>je(t.override(n),e,i,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>Xe(t,e,(()=>function(t,e,i){const{_proxy:s,_context:a,_subProxy:r,_descriptors:l}=t;let h=s[e];S(h)&&l.isScriptable(e)&&(h=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error(\"Recursion detected: \"+Array.from(r).join(\"->\")+\"->\"+t);r.add(t),e=e(o,a||s),r.delete(t),Ue(t,e)&&(e=Ze(n._scopes,n,t,e));return e}(e,h,t,i));n(h)&&h.length&&(h=function(t,e,i,s){const{_proxy:n,_context:a,_subProxy:r,_descriptors:l}=i;if(k(a.index)&&s(t))e=e[a.index%e.length];else if(o(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const o of i){const i=Ze(s,n,t,o);e.push(je(i,a,r&&r[t],l))}}return e}(e,h,t,l.isIndexable));Ue(e,h)&&(h=je(h,a,r&&r[e],l));return h}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function $e(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:S(i)?i:()=>i,isIndexable:S(s)?s:()=>s}}const Ye=(t,e)=>t?t+w(e):e,Ue=(t,e)=>o(e)&&\"adapters\"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function Xe(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e))return t[e];const s=i();return t[e]=s,s}function qe(t,e,i){return S(t)?t(e,i):t}const Ke=(t,e)=>!0===t?e:\"string\"==typeof t?M(e,t):void 0;function Ge(t,e,i,s,n){for(const o of e){const e=Ke(i,o);if(e){t.add(e);const o=qe(e._fallback,i,n);if(k(o)&&o!==i&&o!==s)return o}else if(!1===e&&k(s)&&i!==s)return null}return!1}function Ze(t,e,i,s){const a=e._rootScopes,r=qe(e._fallback,i,s),l=[...t,...a],h=new Set;h.add(s);let c=Je(h,l,i,r||i,s);return null!==c&&((!k(r)||r===i||(c=Je(h,l,r,c,s),null!==c))&&He(Array.from(h),[\"\"],a,r,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const a=s[e];if(n(a)&&o(i))return i;return a||{}}(e,i,s))))}function Je(t,e,i,s,n){for(;i;)i=Ge(t,e,i,s,n);return i}function Qe(t,e){for(const i of e){if(!i)continue;const e=i[t];if(k(e))return e}}function ti(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith(\"_\"))))e.add(t);return Array.from(e)}(t._scopes)),e}function ei(t,e,i,s){const{iScale:n}=t,{key:o=\"r\"}=this._parsing,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={r:n.parse(M(c,o),h)};return a}const ii=Number.EPSILON||1e-14,si=(t,e)=>e<t.length&&!t[e].skip&&t[e],ni=t=>\"x\"===t?\"y\":\"x\";function oi(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=q(o,n),l=q(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function ai(t,e=\"x\"){const i=ni(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=si(t,0);for(a=0;a<s;++a)if(r=l,l=h,h=si(t,a+1),l){if(h){const t=h[e]-l[e];n[a]=0!==t?(h[i]-l[i])/t:0}o[a]=r?h?F(n[a-1])!==F(n[a])?0:(n[a-1]+n[a])/2:n[a-1]:n[a]}!function(t,e,i){const s=t.length;let n,o,a,r,l,h=si(t,0);for(let c=0;c<s-1;++c)l=h,h=si(t,c+1),l&&h&&(V(e[c],0,ii)?i[c]=i[c+1]=0:(n=i[c]/e[c],o=i[c+1]/e[c],r=Math.pow(n,2)+Math.pow(o,2),r<=9||(a=3/Math.sqrt(r),i[c]=n*a*e[c],i[c+1]=o*a*e[c])))}(t,n,o),function(t,e,i=\"x\"){const s=ni(i),n=t.length;let o,a,r,l=si(t,0);for(let h=0;h<n;++h){if(a=r,r=l,l=si(t,h+1),!r)continue;const n=r[i],c=r[s];a&&(o=(n-a[i])/3,r[`cp1${i}`]=n-o,r[`cp1${s}`]=c-o*e[h]),l&&(o=(l[i]-n)/3,r[`cp2${i}`]=n+o,r[`cp2${s}`]=c+o*e[h])}}(t,o,e)}function ri(t,e,i){return Math.max(Math.min(t,i),e)}function li(t,e,i,s,n){let o,a,r,l;if(e.spanGaps&&(t=t.filter((t=>!t.skip))),\"monotone\"===e.cubicInterpolationMode)ai(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o<a;++o)r=t[o],l=oi(i,r,t[Math.min(o+1,a-(s?0:1))%a],e.tension),r.cp1x=l.previous.x,r.cp1y=l.previous.y,r.cp2x=l.next.x,r.cp2y=l.next.y,i=r}e.capBezierPoints&&function(t,e){let i,s,n,o,a,r=Ee(t[0],e);for(i=0,s=t.length;i<s;++i)a=o,o=r,r=i<s-1&&Ee(t[i+1],e),o&&(n=t[i],a&&(n.cp1x=ri(n.cp1x,e.left,e.right),n.cp1y=ri(n.cp1y,e.top,e.bottom)),r&&(n.cp2x=ri(n.cp2x,e.left,e.right),n.cp2y=ri(n.cp2y,e.top,e.bottom)))}(t,i)}const hi=t=>0===t||1===t,ci=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*O/i),di=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*O/i)+1,ui={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*E),easeOutSine:t=>Math.sin(t*E),easeInOutSine:t=>-.5*(Math.cos(C*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>hi(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>hi(t)?t:ci(t,.075,.3),easeOutElastic:t=>hi(t)?t:di(t,.075,.3),easeInOutElastic(t){const e=.1125;return hi(t)?t:t<.5?.5*ci(2*t,e,.45):.5+.5*di(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-ui.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*ui.easeInBounce(2*t):.5*ui.easeOutBounce(2*t-1)+.5};function fi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function gi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:\"middle\"===s?i<.5?t.y:e.y:\"after\"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function pi(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=fi(t,n,i),r=fi(n,o,i),l=fi(o,e,i),h=fi(a,r,i),c=fi(r,l,i);return fi(h,c,i)}const mi=/^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/,bi=/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;function xi(t,e){const i=(\"\"+t).match(mi);if(!i||\"normal\"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case\"px\":return t;case\"%\":t/=100}return e*t}function _i(t,e){const i={},s=o(e),n=s?Object.keys(e):e,a=o(t)?s?i=>l(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=+a(t)||0;return i}function yi(t){return _i(t,{top:\"y\",right:\"x\",bottom:\"y\",left:\"x\"})}function vi(t){return _i(t,[\"topLeft\",\"topRight\",\"bottomLeft\",\"bottomRight\"])}function Mi(t){const e=yi(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function wi(t,e){t=t||{},e=e||ue.font;let i=l(t.size,e.size);\"string\"==typeof i&&(i=parseInt(i,10));let s=l(t.style,e.style);s&&!(\"\"+s).match(bi)&&(console.warn('Invalid font style specified: \"'+s+'\"'),s=void 0);const n={family:l(t.family,e.family),lineHeight:xi(l(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:l(t.weight,e.weight),string:\"\"};return n.string=Pe(n),n}function ki(t,e,i,s){let o,a,r,l=!0;for(o=0,a=t.length;o<a;++o)if(r=t[o],void 0!==r&&(void 0!==e&&\"function\"==typeof r&&(r=r(e),l=!1),void 0!==i&&n(r)&&(r=r[i%r.length],l=!1),void 0!==r))return s&&!l&&(s.cacheable=!1),r}function Si(t,e,i){const{min:s,max:n}=t,o=c(e,(n-s)/2),a=(t,e)=>i&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Pi(t,e){return Object.assign(Object.create(t),e)}function Di(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>\"center\"===t?t:\"right\"===t?\"left\":\"right\",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ci(t,e){let i,s;\"ltr\"!==e&&\"rtl\"!==e||(i=t.canvas.style,s=[i.getPropertyValue(\"direction\"),i.getPropertyPriority(\"direction\")],i.setProperty(\"direction\",e,\"important\"),t.prevTextDirection=s)}function Oi(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty(\"direction\",e[0],e[1]))}function Ai(t){return\"angle\"===t?{between:Z,compare:K,normalize:G}:{between:tt,compare:(t,e)=>t-e,normalize:t=>t}}function Ti({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Li(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Ai(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Ai(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;h<c&&a(r(e[d%l][s]),n,o);++h)d--,u--;d%=l,u%=l}return u<d&&(u+=l),{start:d,end:u,loop:f,style:t.style}}(t,e,i),g=[];let p,m,b,x=!1,_=null;const y=()=>x||l(n,b,p)&&0!==r(n,b),v=()=>!x||0===r(o,p)||l(o,b,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==b&&(x=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Ti({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,b=p));return null!==_&&g.push(Ti({start:_,end:d,loop:u,count:a,style:f})),g}function Ei(t,e){const i=[],s=t.segments;for(let n=0;n<s.length;n++){const o=Li(s[n],t.points,e);o.length&&i.push(...o)}return i}function Ri(t,e){const i=t.points,s=t.options.spanGaps,n=i.length;if(!n)return[];const o=!!t._loop,{start:a,end:r}=function(t,e,i,s){let n=0,o=e-1;if(i&&!s)for(;n<e&&!t[n].skip;)n++;for(;n<e&&t[n].skip;)n++;for(n%=e,i&&(o+=n);o>n&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Ii(t,[{start:a,end:r,loop:o}],i,e);return Ii(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,r<a?r+n:r,!!t._fullLoop&&0===a&&r===n-1),i,e)}function Ii(t,e,i,s){return s&&s.setContext&&i?function(t,e,i,s){const n=t._chart.getContext(),o=zi(t.options),{_datasetIndex:a,options:{spanGaps:r}}=t,l=i.length,h=[];let c=o,d=e[0].start,u=d;function f(t,e,s,n){const o=r?-1:1;if(t!==e){for(t+=l;i[t%l].skip;)t-=o;for(;i[e%l].skip;)e+=o;t%l!=e%l&&(h.push({start:t%l,end:e%l,loop:s,style:n}),c=n,d=e%l)}}for(const t of e){d=r?d:t.start;let e,o=i[d%l];for(u=d+1;u<=t.end;u++){const r=i[u%l];e=zi(s.setContext(Pi(n,{type:\"segment\",p0:o,p1:r,p0DataIndex:(u-1)%l,p1DataIndex:u%l,datasetIndex:a}))),Fi(e,c)&&f(d,u-1,t.loop,c),o=r,c=e}d<u-1&&f(d,u-1,t.loop,c)}return h}(t,e,i,s):e}function zi(t){return{backgroundColor:t.backgroundColor,borderCapStyle:t.borderCapStyle,borderDash:t.borderDash,borderDashOffset:t.borderDashOffset,borderJoinStyle:t.borderJoinStyle,borderWidth:t.borderWidth,borderColor:t.borderColor}}function Fi(t,e){return e&&JSON.stringify(t)!==JSON.stringify(e)}var Vi=Object.freeze({__proto__:null,easingEffects:ui,isPatternOrGradient:Jt,color:Qt,getHoverColor:te,noop:e,uid:i,isNullOrUndef:s,isArray:n,isObject:o,isFinite:a,finiteOrDefault:r,valueOrDefault:l,toPercentage:h,toDimension:c,callback:d,each:u,_elementsEqual:f,clone:g,_merger:m,merge:b,mergeIf:x,_mergerIf:_,_deprecated:function(t,e,i,s){void 0!==e&&console.warn(t+': \"'+i+'\" is deprecated. Please use \"'+s+'\" instead')},_splitKey:v,resolveObjectKey:M,_capitalize:w,defined:k,isFunction:S,setsEqual:P,_isClickEvent:D,toFontString:Pe,_measureText:De,_longestText:Ce,_alignPixel:Oe,clearCanvas:Ae,drawPoint:Te,drawPointLegend:Le,_isPointInArea:Ee,clipArea:Re,unclipArea:Ie,_steppedLineTo:ze,_bezierCurveTo:Fe,renderText:Ve,addRoundedRectPath:We,_lookup:et,_lookupByKey:it,_rlookupByKey:st,_filterBetween:nt,listenArrayEvents:at,unlistenArrayEvents:rt,_arrayUnique:lt,_createResolver:He,_attachContext:je,_descriptors:$e,_parseObjectDataRadialScale:ei,splineCurve:oi,splineCurveMonotone:ai,_updateBezierControlPoints:li,_isDomSupported:fe,_getParentNode:ge,getStyle:be,getRelativePosition:ye,getMaximumSize:Me,retinaScale:we,supportsEventListenerOptions:ke,readUsedSize:Se,fontString:function(t,e,i){return e+\" \"+t+\"px \"+i},requestAnimFrame:ht,throttled:ct,debounce:dt,_toLeftRightCenter:ut,_alignStartEnd:ft,_textX:gt,_getStartAndCountOfVisiblePoints:pt,_scaleRangesChanged:mt,_pointInLine:fi,_steppedInterpolation:gi,_bezierInterpolation:pi,formatNumber:ne,toLineHeight:xi,_readValueToProps:_i,toTRBL:yi,toTRBLCorners:vi,toPadding:Mi,toFont:wi,resolve:ki,_addGrace:Si,createContext:Pi,PI:C,TAU:O,PITAU:A,INFINITY:T,RAD_PER_DEG:L,HALF_PI:E,QUARTER_PI:R,TWO_THIRDS_PI:I,log10:z,sign:F,almostEquals:V,niceNum:B,_factorize:N,isNumber:W,almostWhole:H,_setMinAndMaxByKey:j,toRadians:$,toDegrees:Y,_decimalPlaces:U,getAngleFromPoint:X,distanceBetweenPoints:q,_angleDiff:K,_normalizeAngle:G,_angleBetween:Z,_limitValue:J,_int16Range:Q,_isBetween:tt,getRtlAdapter:Di,overrideTextDirection:Ci,restoreTextDirection:Oi,_boundSegment:Li,_boundSegments:Ei,_computeSegments:Ri});function Bi(t,e,i,s){const{controller:n,data:o,_sorted:a}=t,r=n._cachedMeta.iScale;if(r&&e===r.axis&&\"r\"!==e&&a&&o.length){const t=r._reversePixels?st:it;if(!s)return t(o,e,i);if(n._sharedOptions){const s=o[0],n=\"function\"==typeof s.getRange&&s.getRange(e);if(n){const s=t(o,e,i-n),a=t(o,e,i+n);return{lo:s.lo,hi:a.hi}}}}return{lo:0,hi:o.length-1}}function Ni(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t<i;++t){const{index:i,data:r}=o[t],{lo:l,hi:h}=Bi(o[t],e,a,n);for(let t=l;t<=h;++t){const e=r[t];e.skip||s(e,i,t)}}}function Wi(t,e,i,s,n){const o=[];if(!n&&!t.isPointInArea(e))return o;return Ni(t,i,e,(function(i,a,r){(n||Ee(i,t.chartArea,0))&&i.inRange(e.x,e.y,s)&&o.push({element:i,datasetIndex:a,index:r})}),!0),o}function Hi(t,e,i,s,n,o){let a=[];const r=function(t){const e=-1!==t.indexOf(\"x\"),i=-1!==t.indexOf(\"y\");return function(t,s){const n=e?Math.abs(t.x-s.x):0,o=i?Math.abs(t.y-s.y):0;return Math.sqrt(Math.pow(n,2)+Math.pow(o,2))}}(i);let l=Number.POSITIVE_INFINITY;return Ni(t,i,e,(function(i,h,c){const d=i.inRange(e.x,e.y,n);if(s&&!d)return;const u=i.getCenterPoint(n);if(!(!!o||t.isPointInArea(u))&&!d)return;const f=r(e,u);f<l?(a=[{element:i,datasetIndex:h,index:c}],l=f):f===l&&a.push({element:i,datasetIndex:h,index:c})})),a}function ji(t,e,i,s,n,o){return o||t.isPointInArea(e)?\"r\"!==i||s?Hi(t,e,i,s,n,o):function(t,e,i,s){let n=[];return Ni(t,i,e,(function(t,i,o){const{startAngle:a,endAngle:r}=t.getProps([\"startAngle\",\"endAngle\"],s),{angle:l}=X(t,{x:e.x,y:e.y});Z(l,a,r)&&n.push({element:t,datasetIndex:i,index:o})})),n}(t,e,i,n):[]}function $i(t,e,i,s,n){const o=[],a=\"x\"===i?\"inXRange\":\"inYRange\";let r=!1;return Ni(t,i,e,((t,s,l)=>{t[a](e[i],n)&&(o.push({element:t,datasetIndex:s,index:l}),r=r||t.inRange(e.x,e.y,n))})),s&&!r?[]:o}var Yi={evaluateInteractionItems:Ni,modes:{index(t,e,i,s){const n=ye(e,t),o=i.axis||\"x\",a=i.includeInvisible||!1,r=i.intersect?Wi(t,n,o,s,a):ji(t,n,o,!1,s,a),l=[];return r.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=r[0].index,i=t.data[e];i&&!i.skip&&l.push({element:i,datasetIndex:t.index,index:e})})),l):[]},dataset(t,e,i,s){const n=ye(e,t),o=i.axis||\"xy\",a=i.includeInvisible||!1;let r=i.intersect?Wi(t,n,o,s,a):ji(t,n,o,!1,s,a);if(r.length>0){const e=r[0].datasetIndex,i=t.getDatasetMeta(e).data;r=[];for(let t=0;t<i.length;++t)r.push({element:i[t],datasetIndex:e,index:t})}return r},point:(t,e,i,s)=>Wi(t,ye(e,t),i.axis||\"xy\",s,i.includeInvisible||!1),nearest(t,e,i,s){const n=ye(e,t),o=i.axis||\"xy\",a=i.includeInvisible||!1;return ji(t,n,o,i.intersect,s,a)},x:(t,e,i,s)=>$i(t,ye(e,t),\"x\",i.intersect,s),y:(t,e,i,s)=>$i(t,ye(e,t),\"y\",i.intersect,s)}};const Ui=[\"left\",\"top\",\"right\",\"bottom\"];function Xi(t,e){return t.filter((t=>t.pos===e))}function qi(t,e){return t.filter((t=>-1===Ui.indexOf(t.pos)&&t.box.axis===e))}function Ki(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function Gi(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Ui.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o<a;++o){r=t[o];const{fullSize:a}=r.box,l=i[r.stack],h=l&&r.stackWeight/l.weight;r.horizontal?(r.width=h?h*s:a&&e.availableWidth,r.height=n):(r.width=s,r.height=h?h*n:a&&e.availableHeight)}return i}function Zi(t,e,i,s){return Math.max(t[i],e[i])+Math.max(t[s],e[s])}function Ji(t,e){t.top=Math.max(t.top,e.top),t.left=Math.max(t.left,e.left),t.bottom=Math.max(t.bottom,e.bottom),t.right=Math.max(t.right,e.right)}function Qi(t,e,i,s){const{pos:n,box:a}=i,r=t.maxPadding;if(!o(n)){i.size&&(t[n]-=i.size);const e=s[i.stack]||{size:0,count:1};e.size=Math.max(e.size,i.horizontal?a.height:a.width),i.size=e.size/e.count,t[n]+=i.size}a.getPadding&&Ji(r,a.getPadding());const l=Math.max(0,e.outerWidth-Zi(r,t,\"left\",\"right\")),h=Math.max(0,e.outerHeight-Zi(r,t,\"top\",\"bottom\")),c=l!==t.w,d=h!==t.h;return t.w=l,t.h=h,i.horizontal?{same:c,other:d}:{same:d,other:c}}function ts(t,e){const i=e.maxPadding;function s(t){const s={left:0,top:0,right:0,bottom:0};return t.forEach((t=>{s[t]=Math.max(e[t],i[t])})),s}return s(t?[\"left\",\"right\"]:[\"top\",\"bottom\"])}function es(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;o<a;++o){r=t[o],l=r.box,l.update(r.width||e.w,r.height||e.h,ts(r.horizontal,e));const{same:a,other:d}=Qi(e,i,r,s);h|=a&&n.length,c=c||d,l.fullSize||n.push(r)}return h&&es(n,e,i,s)||c}function is(t,e,i,s,n){t.top=i,t.left=e,t.right=e+s,t.bottom=i+n,t.width=s,t.height=n}function ss(t,e,i,s){const n=i.padding;let{x:o,y:a}=e;for(const r of t){const t=r.box,l=s[r.stack]||{count:1,placed:0,weight:1},h=r.stackWeight/l.weight||1;if(r.horizontal){const s=e.w*h,o=l.size||t.height;k(l.start)&&(a=l.start),t.fullSize?is(t,n.left,a,i.outerWidth-n.right-n.left,o):is(t,e.left+l.placed,a,s,o),l.start=a,l.placed+=s,a=t.bottom}else{const s=e.h*h,a=l.size||t.width;k(l.start)&&(o=l.start),t.fullSize?is(t,o,n.top,a,i.outerHeight-n.bottom-n.top):is(t,o,e.top+l.placed,a,s),l.start=o,l.placed+=s,o=t.right}}e.x=o,e.y=a}var ns={addBox(t,e){t.boxes||(t.boxes=[]),e.fullSize=e.fullSize||!1,e.position=e.position||\"top\",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw(t){e.draw(t)}}]},t.boxes.push(e)},removeBox(t,e){const i=t.boxes?t.boxes.indexOf(e):-1;-1!==i&&t.boxes.splice(i,1)},configure(t,e,i){e.fullSize=i.fullSize,e.position=i.position,e.weight=i.weight},update(t,e,i,s){if(!t)return;const n=Mi(t.options.layout.padding),o=Math.max(e-n.width,0),a=Math.max(i-n.height,0),r=function(t){const e=function(t){const e=[];let i,s,n,o,a,r;for(i=0,s=(t||[]).length;i<s;++i)n=t[i],({position:o,options:{stack:a,stackWeight:r=1}}=n),e.push({index:i,box:n,pos:o,horizontal:n.isHorizontal(),weight:n.weight,stack:a&&o+a,stackWeight:r});return e}(t),i=Ki(e.filter((t=>t.box.fullSize)),!0),s=Ki(Xi(e,\"left\"),!0),n=Ki(Xi(e,\"right\")),o=Ki(Xi(e,\"top\"),!0),a=Ki(Xi(e,\"bottom\")),r=qi(e,\"x\"),l=qi(e,\"y\");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Xi(e,\"chartArea\"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;u(t.boxes,(t=>{\"function\"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),f=Object.assign({},n);Ji(f,Mi(s));const g=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),p=Gi(l.concat(h),d);es(r.fullSize,g,d,p),es(l,g,d,p),es(h,g,d,p)&&es(l,g,d,p),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i(\"top\"),t.x+=i(\"left\"),i(\"right\"),i(\"bottom\")}(g),ss(r.leftAndTop,g,d,p),g.x+=g.w,g.y+=g.h,ss(r.rightAndBottom,g,d,p),t.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},u(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})}))}};class os{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class as extends os{acquireContext(t){return t&&t.getContext&&t.getContext(\"2d\")||null}updateConfig(t){t.options.animation=!1}}const rs={touchstart:\"mousedown\",touchmove:\"mousemove\",touchend:\"mouseup\",pointerenter:\"mouseenter\",pointerdown:\"mousedown\",pointermove:\"mousemove\",pointerup:\"mouseup\",pointerleave:\"mouseout\",pointerout:\"mouseout\"},ls=t=>null===t||\"\"===t;const hs=!!ke&&{passive:!0};function cs(t,e,i){t.canvas.removeEventListener(e,i,hs)}function ds(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function us(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ds(i.addedNodes,s),e=e&&!ds(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function fs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ds(i.removedNodes,s),e=e&&!ds(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const gs=new Map;let ps=0;function ms(){const t=window.devicePixelRatio;t!==ps&&(ps=t,gs.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function bs(t,e,i){const s=t.canvas,n=s&&ge(s);if(!n)return;const o=ct(((t,e)=>{const s=n.clientWidth;i(t,e),s<n.clientWidth&&i()}),window),a=new ResizeObserver((t=>{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||o(i,s)}));return a.observe(n),function(t,e){gs.size||window.addEventListener(\"resize\",ms),gs.set(t,e)}(t,o),a}function xs(t,e,i){i&&i.disconnect(),\"resize\"===e&&function(t){gs.delete(t),gs.size||window.removeEventListener(\"resize\",ms)}(t)}function _s(t,e,i){const s=t.canvas,n=ct((e=>{null!==t.ctx&&i(function(t,e){const i=rs[t.type]||t.type,{x:s,y:n}=ye(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t);return function(t,e,i){t.addEventListener(e,i,hs)}(s,e,n),n}class ys extends os{acquireContext(t,e){const i=t&&t.getContext&&t.getContext(\"2d\");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute(\"height\"),n=t.getAttribute(\"width\");if(t.$chartjs={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||\"block\",i.boxSizing=i.boxSizing||\"border-box\",ls(n)){const e=Se(t,\"width\");void 0!==e&&(t.width=e)}if(ls(s))if(\"\"===t.style.height)t.height=t.width/(e||2);else{const e=Se(t,\"height\");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e.$chartjs)return!1;const i=e.$chartjs.initial;[\"height\",\"width\"].forEach((t=>{const n=i[t];s(n)?e.removeAttribute(t):e.setAttribute(t,n)}));const n=i.style||{};return Object.keys(n).forEach((t=>{e.style[t]=n[t]})),e.width=e.width,delete e.$chartjs,!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:us,detach:fs,resize:bs}[e]||_s;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:xs,detach:xs,resize:xs}[e]||cs)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return Me(t,e,i,s)}isAttached(t){const e=ge(t);return!(!e||!e.isConnected)}}function vs(t){return!fe()||\"undefined\"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?as:ys}var Ms=Object.freeze({__proto__:null,_detectPlatform:vs,BasePlatform:os,BasicPlatform:as,DomPlatform:ys});const ws=\"transparent\",ks={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=Qt(t||ws),n=s.valid&&Qt(e||ws);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class Ss{constructor(t,e,i,s){const n=e[i];s=ki([t.to,s,n,t.from]);const o=ki([t.from,n,s]);this._active=!0,this._fn=t.fn||ks[t.type||typeof o],this._easing=ui[t.easing]||ui.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=ki([t.to,e,s,t.from]),this._from=ki([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e<i),!this._active)return this._target[s]=a,void this._notify(!0);e<0?this._target[s]=n:(r=e/i%2,r=o&&r>1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?\"res\":\"rej\",i=this._promises||[];for(let t=0;t<i.length;t++)i[t][e]()}}class Ps{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!o(t))return;const e=Object.keys(ue.animation),i=this._properties;Object.getOwnPropertyNames(t).forEach((s=>{const a=t[s];if(!o(a))return;const r={};for(const t of e)r[t]=a[t];(n(a.properties)&&a.properties||[s]).forEach((t=>{t!==s&&i.has(t)||i.set(t,r)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e<s.length;e++){const n=t[s[e]];n&&n.active()&&i.push(n.wait())}return Promise.all(i)}(t.options.$animations,i).then((()=>{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if(\"$\"===l.charAt(0))continue;if(\"options\"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new Ss(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(xt.add(this._chart,i),!0):void 0}}function Ds(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function Cs(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n<o;++n)i.push(s[n].index);return i}function Os(t,e,i,s={}){const n=t.keys,o=\"single\"===s.mode;let r,l,h,c;if(null!==e){for(r=0,l=n.length;r<l;++r){if(h=+n[r],h===i){if(s.all)continue;break}c=t.values[h],a(c)&&(o||0===e||F(e)===F(c))&&(e+=c)}return e}}function As(t,e){const i=t&&t.options.stacked;return i||void 0===i&&void 0!==e.stack}function Ts(t,e,i){const s=t[e]||(t[e]={});return s[i]||(s[i]={})}function Ls(t,e,i,s){for(const n of e.getMatchingVisibleMetas(s).reverse()){const e=t[n.index];if(i&&e>0||!i&&e<0)return n.index}return null}function Es(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;t<d;++t){const i=e[t],{[l]:o,[h]:d}=i;u=(i._stacks||(i._stacks={}))[h]=Ts(n,c,o),u[r]=d,u._top=Ls(u,a,!0,s.type),u._bottom=Ls(u,a,!1,s.type);(u._visualValues||(u._visualValues={}))[r]=d}}function Rs(t,e){const i=t.scales;return Object.keys(i).filter((t=>i[t].axis===e)).shift()}function Is(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i],void 0!==e[s]._visualValues&&void 0!==e[s]._visualValues[i]&&delete e[s]._visualValues[i]}}}const zs=t=>\"reset\"===t||\"none\"===t,Fs=(t,e)=>e?t:Object.assign({},t);class Vs{static defaults={};static datasetElementType=null;static dataElementType=null;constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=As(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled(\"filler\")&&console.warn(\"Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options\")}updateIndex(t){this.index!==t&&Is(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>\"x\"===t?e:\"r\"===t?s:i,n=e.xAxisID=l(i.xAxisID,Rs(t,\"x\")),o=e.yAxisID=l(i.yAxisID,Rs(t,\"y\")),a=e.rAxisID=l(i.rAxisID,Rs(t,\"r\")),r=e.indexAxis,h=e.iAxisID=s(r,n,o,a),c=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(h),e.vScale=this.getScaleForId(c)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update(\"reset\")}_destroy(){const t=this._cachedMeta;this._data&&rt(this._data,this),t._stacked&&Is(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(o(e))this._data=function(t){const e=Object.keys(t),i=new Array(e.length);let s,n,o;for(s=0,n=e.length;s<n;++s)o=e[s],i[s]={x:o,y:t[o]};return i}(e);else if(i!==e){if(i){rt(i,this);const t=this._cachedMeta;Is(t),t._parsed=[]}e&&Object.isExtensible(e)&&at(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const n=e._stacked;e._stacked=As(e.vScale,e),e.stack!==i.stack&&(s=!0,Is(e),e.stack=i.stack),this._resyncElements(t),(s||n!==e._stacked)&&Es(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:a,_stacked:r}=i,l=a.axis;let h,c,d,u=0===t&&e===s.length||i._sorted,f=t>0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,d=s;else{d=n(s[t])?this.parseArrayData(i,s,t,e):o(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const a=()=>null===c[l]||f&&c[l]<f[l];for(h=0;h<e;++h)i._parsed[h+t]=c=d[h],u&&(a()&&(u=!1),f=c);i._sorted=u}r&&Es(this,d)}parsePrimitiveData(t,e,i,s){const{iScale:n,vScale:o}=t,a=n.axis,r=o.axis,l=n.getLabels(),h=n===o,c=new Array(s);let d,u,f;for(d=0,u=s;d<u;++d)f=d+i,c[d]={[a]:h||n.parse(l[f],f),[r]:o.parse(e[f],f)};return c}parseArrayData(t,e,i,s){const{xScale:n,yScale:o}=t,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={x:n.parse(c[0],h),y:o.parse(c[1],h)};return a}parseObjectData(t,e,i,s){const{xScale:n,yScale:o}=t,{xAxisKey:a=\"x\",yAxisKey:r=\"y\"}=this._parsing,l=new Array(s);let h,c,d,u;for(h=0,c=s;h<c;++h)d=h+i,u=e[d],l[h]={x:n.parse(M(u,a),d),y:o.parse(M(u,r),d)};return l}getParsed(t){return this._cachedMeta._parsed[t]}getDataElement(t){return this._cachedMeta.data[t]}applyStack(t,e,i){const s=this.chart,n=this._cachedMeta,o=e[t.axis];return Os({keys:Cs(s,!0),values:e._stacks[t.axis]._visualValues},o,n.index,{mode:i})}updateRangeFromParsed(t,e,i,s){const n=i[e.axis];let o=null===n?NaN:n;const a=s&&i._stacks[e.axis];s&&a&&(s.values=a,o=Os(s,n,this._cachedMeta.index)),t.min=Math.min(t.min,o),t.max=Math.max(t.max,o)}getMinMax(t,e){const i=this._cachedMeta,s=i._parsed,n=i._sorted&&t===i.iScale,o=s.length,r=this._getOtherScale(t),l=((t,e,i)=>t&&!e.hidden&&e._stacked&&{keys:Cs(i,!0),values:null})(e,i,this.chart),h={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:c,max:d}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(r);let u,f;function g(){f=s[u];const e=f[r.axis];return!a(f[t.axis])||c>e||d<e}for(u=0;u<o&&(g()||(this.updateRangeFromParsed(h,t,f,l),!n));++u);if(n)for(u=o-1;u>=0;--u)if(!g()){this.updateRangeFromParsed(h,t,f,l);break}return h}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s<n;++s)o=e[s][t.axis],a(o)&&i.push(o);return i}getMaxOverflow(){return!1}getLabelAndValue(t){const e=this._cachedMeta,i=e.iScale,s=e.vScale,n=this.getParsed(t);return{label:i?\"\"+i.getLabelForValue(n[i.axis]):\"\",value:s?\"\"+s.getLabelForValue(n[s.axis]):\"\"}}_update(t){const e=this._cachedMeta;this.update(t||\"default\"),e._clip=function(t){let e,i,s,n;return o(t)?(e=t.top,i=t.right,s=t.bottom,n=t.left):e=i=s=n=t,{top:e,right:i,bottom:s,left:n,disabled:!1===t}}(l(this.options.clip,function(t,e,i){if(!1===i)return!1;const s=Ds(t,i),n=Ds(e,i);return{top:n.end,right:s.end,bottom:n.start,left:s.start}}(e.xScale,e.yScale,this.getMaxOverflow())))}update(t){}draw(){const t=this._ctx,e=this.chart,i=this._cachedMeta,s=i.data||[],n=e.chartArea,o=[],a=this._drawStart||0,r=this._drawCount||s.length-a,l=this.options.drawActiveElementsOnTop;let h;for(i.dataset&&i.dataset.draw(t,n,a,r),h=a;h<a+r;++h){const e=s[h];e.hidden||(e.active&&l?o.push(e):e.draw(t,n))}for(h=0;h<o.length;++h)o[h].draw(t,n)}getStyle(t,e){const i=e?\"active\":\"default\";return void 0===t&&this._cachedMeta.dataset?this.resolveDatasetElementOptions(i):this.resolveDataElementOptions(t||0,i)}getContext(t,e,i){const s=this.getDataset();let n;if(t>=0&&t<this._cachedMeta.data.length){const e=this._cachedMeta.data[t];n=e.$context||(e.$context=function(t,e,i){return Pi(t,{active:!1,dataIndex:e,parsed:void 0,raw:void 0,element:i,index:e,mode:\"default\",type:\"data\"})}(this.getContext(),t,e)),n.parsed=this.getParsed(t),n.raw=s.data[t],n.index=n.dataIndex=t}else n=this.$context||(this.$context=function(t,e){return Pi(t,{active:!1,dataset:void 0,datasetIndex:e,index:e,mode:\"default\",type:\"dataset\"})}(this.chart.getContext(),this.index)),n.dataset=s,n.index=n.datasetIndex=this.index;return n.active=!!e,n.mode=i,n}resolveDatasetElementOptions(t){return this._resolveElementOptions(this.datasetElementType.id,t)}resolveDataElementOptions(t,e){return this._resolveElementOptions(this.dataElementType.id,e,t)}_resolveElementOptions(t,e=\"default\",i){const s=\"active\"===e,n=this._cachedDataOpts,o=t+\"-\"+e,a=n[o],r=this.enableOptionSharing&&k(i);if(a)return Fs(a,r);const l=this.chart.config,h=l.datasetElementScopeKeys(this._type,t),c=s?[`${t}Hover`,\"hover\",t,\"\"]:[t,\"\"],d=l.getOptionScopes(this.getDataset(),h),u=Object.keys(ue.elements[t]),f=l.resolveNamedOptions(d,u,(()=>this.getContext(i,s,e)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Fs(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new Ps(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||zs(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,n=this.getSharedOptions(i),o=this.includeOptions(e,n)||n!==s;return this.updateSharedOptions(n,e,i),{sharedOptions:n,includeOptions:o}}updateElement(t,e,i,s){zs(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!zs(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,\"active\",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,\"active\",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,\"active\",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,\"active\",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n<s&&this._removeElements(n,s-n)}_insertElements(t,e,i=!0){const s=this._cachedMeta,n=s.data,o=t+e;let a;const r=t=>{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a<o;++a)n[a]=new this.dataElementType;this._parsing&&r(s._parsed),this.parse(t,e),i&&this.updateElements(n,t,e,\"reset\")}updateElements(t,e,i,s){}_removeElements(t,e){const i=this._cachedMeta;if(this._parsing){const s=i._parsed.splice(t,e);i._stacked&&Is(i,s)}i.data.splice(t,e)}_sync(t){if(this._parsing)this._syncList.push(t);else{const[e,i,s]=t;this[e](i,s)}this.chart._dataChanges.push([this.index,...t])}_onDataPush(){const t=arguments.length;this._sync([\"_insertElements\",this.getDataset().data.length-t,t])}_onDataPop(){this._sync([\"_removeElements\",this._cachedMeta.data.length-1,1])}_onDataShift(){this._sync([\"_removeElements\",0,1])}_onDataSplice(t,e){e&&this._sync([\"_removeElements\",t,e]);const i=arguments.length-2;i&&this._sync([\"_insertElements\",t,i])}_onDataUnshift(){this._sync([\"_insertElements\",0,arguments.length])}}class Bs{static defaults={};static defaultRoutes=void 0;active=!1;tooltipPosition(t){const{x:e,y:i}=this.getProps([\"x\",\"y\"],t);return{x:e,y:i}}hasValue(){return W(this.x)&&W(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach((t=>{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}function Ns(t,e){const i=t.options.ticks,n=function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),o=Math.min(i.maxTicksLimit||n,n),a=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;i<s;i++)t[i].major&&e.push(i);return e}(e):[],r=a.length,l=a[0],h=a[r-1],c=[];if(r>o)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;n<t.length;n++)n===a&&(e.push(t[n]),o++,a=i[o*s])}(e,c,a,r/o),c;const d=function(t,e,i){const s=function(t){const e=t.length;let i,s;if(e<2)return!1;for(s=t[0],i=1;i<e;++i)if(t[i]-t[i-1]!==s)return!1;return s}(t),n=e.length/i;if(!s)return Math.max(n,1);const o=N(s);for(let t=0,e=o.length-1;t<e;t++){const e=o[t];if(e>n)return e}return Math.max(n,1)}(a,e,o);if(r>0){let t,i;const n=r>1?Math.round((h-l)/(r-1)):null;for(Ws(e,c,d,s(n)?0:l-n,l),t=0,i=r-1;t<i;t++)Ws(e,c,d,a[t],a[t+1]);return Ws(e,c,d,h,s(n)?e.length:h+n),c}return Ws(e,c,d),c}function Ws(t,e,i,s,n){const o=l(s,0),a=Math.min(l(n,t.length),t.length);let r,h,c,d=0;for(i=Math.ceil(i),n&&(r=n-s,i=r/Math.floor(r/i)),c=o;c<0;)d++,c=Math.round(o+d*i);for(h=Math.max(o,0);h<a;h++)h===c&&(e.push(t[h]),d++,c=Math.round(o+d*i))}const Hs=(t,e,i)=>\"top\"===e||\"left\"===e?t[e]+i:t[e]-i,js=(t,e)=>Math.min(e||t,t);function $s(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;o<n;o+=s)i.push(t[Math.floor(o)]);return i}function Ys(t,e,i){const s=t.ticks.length,n=Math.min(e,s-1),o=t._startPixel,a=t._endPixel,r=1e-6;let l,h=t.getPixelForTick(n);if(!(i&&(l=1===s?Math.max(h-o,a-h):0===e?(t.getPixelForTick(1)-h)/2:(h-t.getPixelForTick(n-1))/2,h+=n<e?l:-l,h<o-r||h>a+r)))return h}function Us(t){return t.drawTicks?t.tickLength:0}function Xs(t,e){if(!t.display)return 0;const i=wi(t.font,e),s=Mi(t.padding);return(n(t.text)?t.text.length:1)*i.lineHeight+s.height}function qs(t,e,i){let s=ut(t);return(i&&\"right\"!==e||!i&&\"right\"===e)&&(s=(t=>\"left\"===t?\"right\":\"right\"===t?\"left\":t)(s)),s}class Ks extends Bs{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=r(t,Number.POSITIVE_INFINITY),e=r(e,Number.NEGATIVE_INFINITY),i=r(i,Number.POSITIVE_INFINITY),s=r(s,Number.NEGATIVE_INFINITY),{min:r(t,i),max:r(e,s),minDefined:a(t),maxDefined:a(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;r<l;++r)e=a[r].controller.getMinMax(this,t),n||(i=Math.min(i,e.min)),o||(s=Math.max(s,e.max));return i=o&&i>s?s:i,s=n&&i>s?i:s,{min:r(i,r(s,i)),max:r(s,r(i,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){d(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Si(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a<this.ticks.length;this._convertTicksToLabels(r?$s(this.ticks,a):this.ticks),this.configure(),this.beforeCalculateLabelRotation(),this.calculateLabelRotation(),this.afterCalculateLabelRotation(),o.display&&(o.autoSkip||\"auto\"===o.source)&&(this.ticks=Ns(this,this.ticks),this._labelSizes=null,this.afterAutoSkip()),r&&this._convertTicksToLabels(this.ticks),this.beforeFit(),this.fit(),this.afterFit(),this.afterUpdate()}configure(){let t,e,i=this.options.reverse;this.isHorizontal()?(t=this.left,e=this.right):(t=this.top,e=this.bottom,i=!i),this._startPixel=t,this._endPixel=e,this._reversePixels=i,this._length=e-t,this._alignToPixels=this.options.alignToPixels}afterUpdate(){d(this.options.afterUpdate,[this])}beforeSetDimensions(){d(this.options.beforeSetDimensions,[this])}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=0,this.right=this.width):(this.height=this.maxHeight,this.top=0,this.bottom=this.height),this.paddingLeft=0,this.paddingTop=0,this.paddingRight=0,this.paddingBottom=0}afterSetDimensions(){d(this.options.afterSetDimensions,[this])}_callHooks(t){this.chart.notifyPlugins(t,this.getContext()),d(this.options[t],[this])}beforeDataLimits(){this._callHooks(\"beforeDataLimits\")}determineDataLimits(){}afterDataLimits(){this._callHooks(\"afterDataLimits\")}beforeBuildTicks(){this._callHooks(\"beforeBuildTicks\")}buildTicks(){return[]}afterBuildTicks(){this._callHooks(\"afterBuildTicks\")}beforeTickToLabelConversion(){d(this.options.beforeTickToLabelConversion,[this])}generateTickLabels(t){const e=this.options.ticks;let i,s,n;for(i=0,s=t.length;i<s;i++)n=t[i],n.label=d(e.callback,[n.value,i,t],this)}afterTickToLabelConversion(){d(this.options.afterTickToLabelConversion,[this])}beforeCalculateLabelRotation(){d(this.options.beforeCalculateLabelRotation,[this])}calculateLabelRotation(){const t=this.options,e=t.ticks,i=js(this.ticks.length,t.ticks.maxTicksLimit),s=e.minRotation||0,n=e.maxRotation;let o,a,r,l=s;if(!this._isVisible()||!e.display||s>=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=J(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Us(t.grid)-e.padding-Xs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=Y(Math.min(Math.asin(J((h.highest.height+6)/o,-1,1)),Math.asin(J(a/r,-1,1))-Math.asin(J(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){d(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){d(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=Xs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Us(n)+o):(t.height=this.maxHeight,t.width=Us(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=$(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l=\"top\"!==a&&\"x\"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):\"start\"===n?d=e.width:\"end\"===n?c=t.width:\"inner\"!==n&&(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;\"start\"===n?(i=0,s=t.height):\"end\"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){d(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return\"top\"===e||\"bottom\"===e||\"x\"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e<i;e++)s(t[e].label)&&(t.splice(e,1),i--,e--);this.afterTickToLabelConversion()}_getLabelSizes(){let t=this._labelSizes;if(!t){const e=this.options.ticks.sampleSize;let i=this.ticks;e<i.length&&(i=$s(i,e)),this._labelSizes=t=this._computeLabelSizes(i,i.length,this.options.ticks.maxTicksLimit)}return t}_computeLabelSizes(t,e,i){const{ctx:o,_longestTextCache:a}=this,r=[],l=[],h=Math.floor(e/js(e,i));let c,d,f,g,p,m,b,x,_,y,v,M=0,w=0;for(c=0;c<e;c+=h){if(g=t[c].label,p=this._resolveTickFontOptions(c),o.font=m=p.string,b=a[m]=a[m]||{data:{},gc:[]},x=p.lineHeight,_=y=0,s(g)||n(g)){if(n(g))for(d=0,f=g.length;d<f;++d)v=g[d],s(v)||n(v)||(_=De(o,b.data,b.gc,_,v),y+=x)}else _=De(o,b.data,b.gc,_,g),y=x;r.push(_),l.push(y),M=Math.max(_,M),w=Math.max(y,w)}!function(t,e){u(t,(t=>{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n<s;++n)delete t.data[i[n]];i.splice(0,s)}}))}(a,e);const k=r.indexOf(M),S=l.indexOf(w),P=t=>({width:r[t]||0,height:l[t]||0});return{first:P(0),last:P(e-1),widest:P(k),highest:P(S),widths:r,heights:l}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Q(this._alignToPixels?Oe(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&t<e.length){const i=e[t];return i.$context||(i.$context=function(t,e,i){return Pi(t,{tick:i,index:e,type:\"tick\"})}(this.getContext(),t,i))}return this.$context||(this.$context=Pi(this.chart.getContext(),{scale:this,type:\"scale\"}))}_tickSize(){const t=this.options.ticks,e=$(this.labelRotation),i=Math.abs(Math.cos(e)),s=Math.abs(Math.sin(e)),n=this._getLabelSizes(),o=t.autoSkipPadding||0,a=n?n.widest.width+o:0,r=n?n.highest.height+o:0;return this.isHorizontal()?r*i>a*s?a/i:r/s:r*s<a*i?r/i:a/s}_isVisible(){const t=this.options.display;return\"auto\"!==t?!!t:this.getMatchingVisibleMetas().length>0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:a,border:r}=s,h=n.offset,c=this.isHorizontal(),d=this.ticks.length+(h?1:0),u=Us(n),f=[],g=r.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,b=function(t){return Oe(i,t,p)};let x,_,y,v,M,w,k,S,P,D,C,O;if(\"top\"===a)x=b(this.bottom),w=this.bottom-u,S=x-m,D=b(t.top)+m,O=t.bottom;else if(\"bottom\"===a)x=b(this.top),D=t.top,O=b(t.bottom)-m,w=x+m,S=this.top+u;else if(\"left\"===a)x=b(this.right),M=this.right-u,k=x-m,P=b(t.left)+m,C=t.right;else if(\"right\"===a)x=b(this.left),P=t.left,C=b(t.right)-m,M=x+m,k=this.left+u;else if(\"x\"===e){if(\"center\"===a)x=b((t.top+t.bottom)/2+.5);else if(o(a)){const t=Object.keys(a)[0],e=a[t];x=b(this.chart.scales[t].getPixelForValue(e))}D=t.top,O=t.bottom,w=x+m,S=w+u}else if(\"y\"===e){if(\"center\"===a)x=b((t.left+t.right)/2);else if(o(a)){const t=Object.keys(a)[0],e=a[t];x=b(this.chart.scales[t].getPixelForValue(e))}M=x-m,k=M-u,P=t.left,C=t.right}const A=l(s.ticks.maxTicksLimit,d),T=Math.max(1,Math.ceil(d/A));for(_=0;_<d;_+=T){const t=this.getContext(_),e=n.setContext(t),s=r.setContext(t),o=e.lineWidth,a=e.color,l=s.dash||[],d=s.dashOffset,u=e.tickWidth,g=e.tickColor,p=e.tickBorderDash||[],m=e.tickBorderDashOffset;y=Ys(this,_,h),void 0!==y&&(v=Oe(i,y,o),c?M=k=P=C=v:w=S=D=O=v,f.push({tx1:M,ty1:w,tx2:k,ty2:S,x1:P,y1:D,x2:C,y2:O,width:o,color:a,borderDash:l,borderDashOffset:d,tickWidth:u,tickColor:g,tickBorderDash:p,tickBorderDashOffset:m}))}return this._ticksLength=d,this._borderValue=x,f}_computeLabelItems(t){const e=this.axis,i=this.options,{position:s,ticks:a}=i,r=this.isHorizontal(),l=this.ticks,{align:h,crossAlign:c,padding:d,mirror:u}=a,f=Us(i.grid),g=f+d,p=u?-d:g,m=-$(this.labelRotation),b=[];let x,_,y,v,M,w,k,S,P,D,C,O,A=\"middle\";if(\"top\"===s)w=this.bottom-p,k=this._getXAxisLabelAlignment();else if(\"bottom\"===s)w=this.top+p,k=this._getXAxisLabelAlignment();else if(\"left\"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if(\"right\"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if(\"x\"===e){if(\"center\"===s)w=(t.top+t.bottom)/2+g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];w=this.chart.scales[t].getPixelForValue(e)+g}k=this._getXAxisLabelAlignment()}else if(\"y\"===e){if(\"center\"===s)M=(t.left+t.right)/2-g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];M=this.chart.scales[t].getPixelForValue(e)}k=this._getYAxisLabelAlignment(f).textAlign}\"y\"===e&&(\"start\"===h?A=\"top\":\"end\"===h&&(A=\"bottom\"));const T=this._getLabelSizes();for(x=0,_=l.length;x<_;++x){y=l[x],v=y.label;const t=a.setContext(this.getContext(x));S=this.getPixelForTick(x)+a.labelOffset,P=this._resolveTickFontOptions(x),D=P.lineHeight,C=n(v)?v.length:1;const e=C/2,i=t.color,o=t.textStrokeColor,h=t.textStrokeWidth;let d,f=k;if(r?(M=S,\"inner\"===k&&(f=x===_-1?this.options.reverse?\"left\":\"right\":0===x?this.options.reverse?\"right\":\"left\":\"center\"),O=\"top\"===s?\"near\"===c||0!==m?-C*D+D/2:\"center\"===c?-T.highest.height/2-e*D+D:-T.highest.height+D/2:\"near\"===c||0!==m?D/2:\"center\"===c?T.highest.height/2-e*D:T.highest.height-C*D,u&&(O*=-1),0===m||t.showLabelBackdrop||(M+=D/2*Math.sin(m))):(w=S,O=(1-C)*D/2),t.showLabelBackdrop){const e=Mi(t.backdropPadding),i=T.heights[x],s=T.widths[x];let n=O-e.top,o=0-e.left;switch(A){case\"middle\":n-=i/2;break;case\"bottom\":n-=i}switch(k){case\"center\":o-=s/2;break;case\"right\":o-=s}d={left:o,top:n,width:s+e.width,height:i+e.height,color:t.backdropColor}}b.push({label:v,font:P,textOffset:O,options:{rotation:m,color:i,strokeColor:o,strokeWidth:h,textAlign:f,textBaseline:A,translation:[M,w],backdrop:d}})}return b}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-$(this.labelRotation))return\"top\"===t?\"left\":\"right\";let i=\"center\";return\"start\"===e.align?i=\"left\":\"end\"===e.align?i=\"right\":\"inner\"===e.align&&(i=\"inner\"),i}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:n}}=this.options,o=t+n,a=this._getLabelSizes().widest.width;let r,l;return\"left\"===e?s?(l=this.right+n,\"near\"===i?r=\"left\":\"center\"===i?(r=\"center\",l+=a/2):(r=\"right\",l+=a)):(l=this.right-o,\"near\"===i?r=\"right\":\"center\"===i?(r=\"center\",l-=a/2):(r=\"left\",l=this.left)):\"right\"===e?s?(l=this.left+n,\"near\"===i?r=\"right\":\"center\"===i?(r=\"center\",l-=a/2):(r=\"left\",l-=a)):(l=this.left+o,\"near\"===i?r=\"left\":\"center\"===i?(r=\"center\",l+=a/2):(r=\"right\",l=this.right)):r=\"right\",{textAlign:r,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;return\"left\"===e||\"right\"===e?{top:0,left:this.left,bottom:t.height,right:this.right}:\"top\"===e||\"bottom\"===e?{top:this.top,left:0,bottom:this.bottom,right:t.width}:void 0}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:n,height:o}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,n,o),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const i=this.ticks.findIndex((e=>e.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n<o;++n){const t=s[n];e.drawOnChartArea&&a({x:t.x1,y:t.y1},{x:t.x2,y:t.y2},t),e.drawTicks&&a({x:t.tx1,y:t.ty1},{x:t.tx2,y:t.ty2},{color:t.tickColor,width:t.tickWidth,borderDash:t.tickBorderDash,borderDashOffset:t.tickBorderDashOffset})}}drawBorder(){const{chart:t,ctx:e,options:{border:i,grid:s}}=this,n=i.setContext(this.getContext()),o=i.display?n.width:0;if(!o)return;const a=s.setContext(this.getContext(0)).lineWidth,r=this._borderValue;let l,h,c,d;this.isHorizontal()?(l=Oe(t,this.left,o)-o/2,h=Oe(t,this.right,a)+a/2,c=d=r):(c=Oe(t,this.top,o)-o/2,d=Oe(t,this.bottom,a)+a/2,l=h=r),e.save(),e.lineWidth=n.width,e.strokeStyle=n.color,e.beginPath(),e.moveTo(l,c),e.lineTo(h,d),e.stroke(),e.restore()}drawLabels(t){if(!this.options.ticks.display)return;const e=this.ctx,i=this._computeLabelArea();i&&Re(e,i);const s=this.getLabelItems(t);for(const t of s){const i=t.options,s=t.font;Ve(e,t.label,0,t.textOffset,s,i)}i&&Ie(e)}drawTitle(){const{ctx:t,options:{position:e,title:i,reverse:s}}=this;if(!i.display)return;const a=wi(i.font),r=Mi(i.padding),l=i.align;let h=a.lineHeight/2;\"bottom\"===e||\"center\"===e||o(e)?(h+=r.bottom,n(i.text)&&(h+=a.lineHeight*(i.text.length-1))):h+=r.top;const{titleX:c,titleY:d,maxWidth:u,rotation:f}=function(t,e,i,s){const{top:n,left:a,bottom:r,right:l,chart:h}=t,{chartArea:c,scales:d}=h;let u,f,g,p=0;const m=r-n,b=l-a;if(t.isHorizontal()){if(f=ft(s,a,l),o(i)){const t=Object.keys(i)[0],s=i[t];g=d[t].getPixelForValue(s)+m-e}else g=\"center\"===i?(c.bottom+c.top)/2+m-e:Hs(t,i,e);u=l-a}else{if(o(i)){const t=Object.keys(i)[0],s=i[t];f=d[t].getPixelForValue(s)-b+e}else f=\"center\"===i?(c.left+c.right)/2-b+e:Hs(t,i,e);g=ft(s,r,n),p=\"left\"===i?-E:E}return{titleX:f,titleY:g,maxWidth:u,rotation:p}}(this,h,e,l);Ve(t,i.text,0,0,a,{color:i.color,maxWidth:u,rotation:f,textAlign:qs(l,e,s),textBaseline:\"middle\",translation:[c,d]})}draw(t){this._isVisible()&&(this.drawBackground(),this.drawGrid(t),this.drawBorder(),this.drawTitle(),this.drawLabels(t))}_layers(){const t=this.options,e=t.ticks&&t.ticks.z||0,i=l(t.grid&&t.grid.z,-1),s=l(t.border&&t.border.z,0);return this._isVisible()&&this.draw===Ks.prototype.draw?[{z:i,draw:t=>{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+\"AxisID\",s=[];let n,o;for(n=0,o=e.length;n<o;++n){const o=e[n];o[i]!==this.id||t&&o.type!==t||s.push(o)}return s}_resolveTickFontOptions(t){return wi(this.options.ticks.setContext(this.getContext(t)).font)}_maxDigits(){const t=this._resolveTickFontOptions(0).lineHeight;return(this.isHorizontal()?this.width:this.height)/t}}class Gs{constructor(t,e,i){this.type=t,this.scope=e,this.override=i,this.items=Object.create(null)}isForType(t){return Object.prototype.isPrototypeOf.call(this.type.prototype,t.prototype)}register(t){const e=Object.getPrototypeOf(t);let i;(function(t){return\"id\"in t&&\"defaults\"in t})(e)&&(i=this.register(e));const s=this.items,n=t.id,o=this.scope+\".\"+n;if(!n)throw new Error(\"class does not have id: \"+t);return n in s||(s[n]=t,function(t,e,i){const s=b(Object.create(null),[i?ue.get(i):{},ue.get(e),t.defaults]);ue.set(e,s),t.defaultRoutes&&function(t,e){Object.keys(e).forEach((i=>{const s=i.split(\".\"),n=s.pop(),o=[t].concat(s).join(\".\"),a=e[i].split(\".\"),r=a.pop(),l=a.join(\".\");ue.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&ue.describe(e,t.descriptors)}(t,o,i),this.override&&ue.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in ue[s]&&(delete ue[s][i],this.override&&delete re[i])}}class Zs{constructor(){this.controllers=new Gs(Vs,\"datasets\",!0),this.elements=new Gs(Bs,\"elements\"),this.plugins=new Gs(Object,\"plugins\"),this.scales=new Gs(Ks,\"scales\"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each(\"register\",t)}remove(...t){this._each(\"unregister\",t)}addControllers(...t){this._each(\"register\",t,this.controllers)}addElements(...t){this._each(\"register\",t,this.elements)}addPlugins(...t){this._each(\"register\",t,this.plugins)}addScales(...t){this._each(\"register\",t,this.scales)}getController(t){return this._get(t,this.controllers,\"controller\")}getElement(t){return this._get(t,this.elements,\"element\")}getPlugin(t){return this._get(t,this.plugins,\"plugin\")}getScale(t){return this._get(t,this.scales,\"scale\")}removeControllers(...t){this._each(\"unregister\",t,this.controllers)}removeElements(...t){this._each(\"unregister\",t,this.elements)}removePlugins(...t){this._each(\"unregister\",t,this.plugins)}removeScales(...t){this._each(\"unregister\",t,this.scales)}_each(t,e,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):u(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=w(t);d(i[\"before\"+s],[],i),e[t](i),d(i[\"after\"+s],[],i)}_getRegistryForType(t){for(let e=0;e<this._typedRegistries.length;e++){const i=this._typedRegistries[e];if(i.isForType(t))return i}return this.plugins}_get(t,e,i){const s=e.get(t);if(void 0===s)throw new Error('\"'+t+'\" is not a registered '+i+\".\");return s}}var Js=new Zs;class Qs{constructor(){this._init=[]}notify(t,e,i,s){\"beforeInit\"===e&&(this._init=this._createDescriptors(t,!0),this._notify(this._init,t,\"install\"));const n=s?this._descriptors(t).filter(s):this._descriptors(t),o=this._notify(n,t,e,i);return\"afterDestroy\"===e&&(this._notify(n,t,\"stop\"),this._notify(this._init,t,\"uninstall\")),o}_notify(t,e,i,s){s=s||{};for(const n of t){const t=n.plugin;if(!1===d(t[i],[e,s,n.options],t)&&s.cancelable)return!1}return!0}invalidate(){s(this._cache)||(this._oldCache=this._cache,this._cache=void 0)}_descriptors(t){if(this._cache)return this._cache;const e=this._cache=this._createDescriptors(t);return this._notifyStateChanges(t),e}_createDescriptors(t,e){const i=t&&t.config,s=l(i.options&&i.options.plugins,{}),n=function(t){const e={},i=[],s=Object.keys(Js.plugins.items);for(let t=0;t<s.length;t++)i.push(Js.getPlugin(s[t]));const n=t.plugins||[];for(let t=0;t<n.length;t++){const s=n[t];-1===i.indexOf(s)&&(i.push(s),e[s.id]=!0)}return{plugins:i,localIds:e}}(i);return!1!==s||e?function(t,{plugins:e,localIds:i},s,n){const o=[],a=t.getContext();for(const r of e){const e=r.id,l=tn(s[e],n);null!==l&&o.push({plugin:r,options:en(t.config,{plugin:r,local:i[e]},l,a)})}return o}(t,n,s,e):[]}_notifyStateChanges(t){const e=this._oldCache||[],i=this._cache,s=(t,e)=>t.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,\"stop\"),this._notify(s(i,e),t,\"start\")}}function tn(t,e){return e||!1!==t?!0===t?{}:t:null}function en(t,{plugin:e,local:i},s,n){const o=t.pluginScopeKeys(e),a=t.getOptionScopes(s,o);return i&&e.defaults&&a.push(e.defaults),t.createResolver(a,n,[\"\"],{scriptable:!1,indexable:!1,allKeys:!0})}function sn(t,e){const i=ue.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||\"x\"}function nn(t,e){if(\"x\"===t||\"y\"===t||\"r\"===t)return t;var i;if(t=e.axis||(\"top\"===(i=e.position)||\"bottom\"===i?\"x\":\"left\"===i||\"right\"===i?\"y\":void 0)||t.length>1&&nn(t[0].toLowerCase(),e))return t;throw new Error(`Cannot determine type of '${name}' axis. Please provide 'axis' or 'position' option.`)}function on(t){const e=t.options||(t.options={});e.plugins=l(e.plugins,{}),e.scales=function(t,e){const i=re[t.type]||{scales:{}},s=e.scales||{},n=sn(t.type,e),a=Object.create(null);return Object.keys(s).forEach((t=>{const e=s[t];if(!o(e))return console.error(`Invalid scale configuration for scale: ${t}`);if(e._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${t}`);const r=nn(t,e),l=function(t,e){return t===e?\"_index_\":\"_value_\"}(r,n),h=i.scales||{};a[t]=x(Object.create(null),[{axis:r},e,h[r],h[l]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,o=i.indexAxis||sn(n,e),r=(re[n]||{}).scales||{};Object.keys(r).forEach((t=>{const e=function(t,e){let i=t;return\"_index_\"===t?i=e:\"_value_\"===t&&(i=\"x\"===e?\"y\":\"x\"),i}(t,o),n=i[e+\"AxisID\"]||e;a[n]=a[n]||Object.create(null),x(a[n],[{axis:e},s[n],r[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];x(e,[ue.scales[e.type],ue.scale])})),a}(t,e)}function an(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const rn=new Map,ln=new Set;function hn(t,e){let i=rn.get(t);return i||(i=e(),rn.set(t,i),ln.add(i)),i}const cn=(t,e,i)=>{const s=M(e,i);void 0!==s&&t.add(s)};class dn{constructor(t){this._config=function(t){return(t=t||{}).data=an(t.data),on(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=an(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),on(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return hn(t,(()=>[[`datasets.${t}`,\"\"]]))}datasetAnimationScopeKeys(t,e){return hn(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,\"\"]]))}datasetElementScopeKeys(t,e){return hn(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,\"\"]]))}pluginScopeKeys(t){const e=t.id;return hn(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>cn(r,t,e)))),e.forEach((t=>cn(r,s,t))),e.forEach((t=>cn(r,re[n]||{},t))),e.forEach((t=>cn(r,ue,t))),e.forEach((t=>cn(r,le,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),ln.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,re[e]||{},ue.datasets[e]||{},{type:e},ue,le]}resolveNamedOptions(t,e,i,s=[\"\"]){const o={$shared:!0},{resolver:a,subPrefixes:r}=un(this._resolverCache,t,s);let l=a;if(function(t,e){const{isScriptable:i,isIndexable:s}=$e(t);for(const o of e){const e=i(o),a=s(o),r=(a||e)&&t[o];if(e&&(S(r)||fn(r))||a&&n(r))return!0}return!1}(a,e)){o.$shared=!1;l=je(a,i=S(i)?i():i,this.createResolver(t,i,r))}for(const t of e)o[t]=l[t];return o}createResolver(t,e,i=[\"\"],s){const{resolver:n}=un(this._resolverCache,t,i);return o(e)?je(n,e,void 0,s):n}}function un(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:He(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes(\"hover\")))},s.set(n,o)}return o}const fn=t=>o(t)&&Object.getOwnPropertyNames(t).reduce(((e,i)=>e||S(t[i])),!1);const gn=[\"top\",\"bottom\",\"left\",\"right\",\"chartArea\"];function pn(t,e){return\"top\"===t||\"bottom\"===t||-1===gn.indexOf(t)&&\"x\"===e}function mn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function bn(t){const e=t.chart,i=e.options.animation;e.notifyPlugins(\"afterRender\"),d(i&&i.onComplete,[t],e)}function xn(t){const e=t.chart,i=e.options.animation;d(i&&i.onProgress,[t],e)}function _n(t){return fe()&&\"string\"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const yn={},vn=t=>{const e=_n(t);return Object.values(yn).filter((t=>t.canvas===e)).pop()};function Mn(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class wn{static defaults=ue;static instances=yn;static overrides=re;static registry=Js;static version=\"4.2.1\";static getChart=vn;static register(...t){Js.add(...t),kn()}static unregister(...t){Js.remove(...t),kn()}constructor(t,e){const s=this.config=new dn(e),n=_n(t),o=vn(n);if(o)throw new Error(\"Canvas is already in use. Chart with ID '\"+o.id+\"' must be destroyed before the canvas with ID '\"+o.canvas.id+\"' can be reused.\");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||vs(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,h=l&&l.height,c=l&&l.width;this.id=i(),this.ctx=r,this.canvas=l,this.width=c,this.height=h,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new Qs,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=dt((t=>this.update(t)),a.resizeDelay||0),this._dataChanges=[],yn[this.id]=this,r&&l?(xt.listen(this,\"complete\",bn),xt.listen(this,\"progress\",xn),this._initialize(),this.attached&&this.update()):console.error(\"Failed to create chart: can't acquire context from the given item\")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:n,_aspectRatio:o}=this;return s(t)?e&&o?o:n?i/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return Js}_initialize(){return this.notifyPlugins(\"beforeInit\"),this.options.responsive?this.resize():we(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins(\"afterInit\"),this}clear(){return Ae(this.canvas,this.ctx),this}stop(){return xt.stop(this),this}resize(t,e){xt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?\"resize\":\"attach\";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,we(this,a,!0)&&(this.notifyPlugins(\"resize\",{size:o}),d(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){u(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=nn(t,i),n=\"r\"===s,o=\"x\"===s;return{options:i,dposition:n?\"chartArea\":o?\"bottom\":\"left\",dtype:n?\"radialLinear\":o?\"category\":\"linear\"}})))),u(n,(e=>{const n=e.options,o=n.id,a=nn(o,n),r=l(n.type,e.dtype);void 0!==n.position&&pn(n.position,a)===pn(e.dposition)||(n.position=e.dposition),s[o]=!0;let h=null;if(o in i&&i[o].type===r)h=i[o];else{h=new(Js.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(n,t)})),u(s,((t,e)=>{t||delete i[e]})),u(i,(t=>{ns.configure(this,t,t.options),ns.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;t<i;++t)this._destroyDatasetMeta(t);t.splice(e,i-e)}this._sortedMetasets=t.slice(0).sort(mn(\"order\",\"index\"))}_removeUnreferencedMetasets(){const{_metasets:t,data:{datasets:e}}=this;t.length>e.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i<s;i++){const s=e[i];let n=this.getDatasetMeta(i);const o=s.type||this.config.type;if(n.type&&n.type!==o&&(this._destroyDatasetMeta(i),n=this.getDatasetMeta(i)),n.type=o,n.indexAxis=s.indexAxis||sn(o,this.options),n.order=s.order||0,n.index=i,n.label=\"\"+s.label,n.visible=this.isDatasetVisible(i),n.controller)n.controller.updateIndex(i),n.controller.linkScales();else{const e=Js.getController(o),{datasetElementType:s,dataElementType:a}=ue.datasets[o];Object.assign(e,{dataElementType:Js.getElement(a),datasetElementType:s&&Js.getElement(s)}),n.controller=new e(this,i),t.push(n.controller)}}return this._updateMetasets(),t}_resetElements(){u(this.data.datasets,((t,e)=>{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins(\"reset\")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins(\"beforeUpdate\",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins(\"beforeElementsUpdate\");let o=0;for(let t=0,e=this.data.datasets.length;t<e;t++){const{controller:e}=this.getDatasetMeta(t),i=!s&&-1===n.indexOf(e);e.buildOrUpdateElements(i),o=Math.max(+e.getMaxOverflow(),o)}o=this._minPadding=i.layout.autoPadding?o:0,this._updateLayout(o),s||u(n,(t=>{t.reset()})),this._updateDatasets(t),this.notifyPlugins(\"afterUpdate\",{mode:t}),this._layers.sort(mn(\"z\",\"_idx\"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){u(this.scales,(t=>{ns.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);P(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){Mn(t,s,\"_removeElements\"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+\",\"+t.splice(1).join(\",\")))),s=i(0);for(let t=1;t<e;t++)if(!P(s,i(t)))return;return Array.from(s).map((t=>t.split(\",\"))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins(\"beforeLayout\",{cancelable:!0}))return;ns.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],u(this.boxes,(t=>{i&&\"chartArea\"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins(\"afterLayout\")}_updateDatasets(t){if(!1!==this.notifyPlugins(\"beforeDatasetsUpdate\",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t<e;++t)this.getDatasetMeta(t).controller.configure();for(let e=0,i=this.data.datasets.length;e<i;++e)this._updateDataset(e,S(t)?t({datasetIndex:e}):t);this.notifyPlugins(\"afterDatasetsUpdate\",{mode:t})}}_updateDataset(t,e){const i=this.getDatasetMeta(t),s={meta:i,index:t,mode:e,cancelable:!0};!1!==this.notifyPlugins(\"beforeDatasetUpdate\",s)&&(i.controller._update(e),s.cancelable=!1,this.notifyPlugins(\"afterDatasetUpdate\",s))}render(){!1!==this.notifyPlugins(\"beforeRender\",{cancelable:!0})&&(xt.has(this)?this.attached&&!xt.running(this)&&xt.start(this):(this.draw(),bn({chart:this})))}draw(){let t;if(this._resizeBeforeDraw){const{width:t,height:e}=this._resizeBeforeDraw;this._resize(t,e),this._resizeBeforeDraw=null}if(this.clear(),this.width<=0||this.height<=0)return;if(!1===this.notifyPlugins(\"beforeDraw\",{cancelable:!0}))return;const e=this._layers;for(t=0;t<e.length&&e[t].z<=0;++t)e[t].draw(this.chartArea);for(this._drawDatasets();t<e.length;++t)e[t].draw(this.chartArea);this.notifyPlugins(\"afterDraw\")}_getSortedDatasetMetas(t){const e=this._sortedMetasets,i=[];let s,n;for(s=0,n=e.length;s<n;++s){const n=e[s];t&&!n.visible||i.push(n)}return i}getSortedVisibleDatasetMetas(){return this._getSortedDatasetMetas(!0)}_drawDatasets(){if(!1===this.notifyPlugins(\"beforeDatasetsDraw\",{cancelable:!0}))return;const t=this.getSortedVisibleDatasetMetas();for(let e=t.length-1;e>=0;--e)this._drawDataset(t[e]);this.notifyPlugins(\"afterDatasetsDraw\")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,n=function(t){const{xScale:e,yScale:i}=t;if(e&&i)return{left:e.left,right:e.right,top:i.top,bottom:i.bottom}}(t)||this.chartArea,o={meta:t,index:t.index,cancelable:!0};!1!==this.notifyPlugins(\"beforeDatasetDraw\",o)&&(s&&Re(e,{left:!1===i.left?0:n.left-i.left,right:!1===i.right?this.width:n.right+i.right,top:!1===i.top?0:n.top-i.top,bottom:!1===i.bottom?this.height:n.bottom+i.bottom}),t.controller.draw(),s&&Ie(e),o.cancelable=!1,this.notifyPlugins(\"afterDatasetDraw\",o))}isPointInArea(t){return Ee(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const n=Yi.modes[e];return\"function\"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Pi(null,{chart:this,type:\"chart\"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return\"boolean\"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?\"show\":\"hide\",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);k(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),xt.remove(this),t=0,e=this.data.datasets.length;t<e;++t)this._destroyDatasetMeta(t)}destroy(){this.notifyPlugins(\"beforeDestroy\");const{canvas:t,ctx:e}=this;this._stop(),this.config.clearCache(),t&&(this.unbindEvents(),Ae(t,e),this.platform.releaseContext(e),this.canvas=null,this.ctx=null),delete yn[this.id],this.notifyPlugins(\"afterDestroy\")}toBase64Image(...t){return this.canvas.toDataURL(...t)}bindEvents(){this.bindUserEvents(),this.options.responsive?this.bindResponsiveEvents():this.attached=!0}bindUserEvents(){const t=this._listeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};u(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s(\"attach\",a),this.attached=!0,this.resize(),i(\"resize\",n),i(\"detach\",o)};o=()=>{this.attached=!1,s(\"resize\",n),this._stop(),this._resize(0,0),i(\"attach\",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){u(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},u(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?\"set\":\"remove\";let n,o,a,r;for(\"dataset\"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller[\"_\"+s+\"DatasetHoverStyle\"]()),a=0,r=t.length;a<r;++a){o=t[a];const e=o&&this.getDatasetMeta(o.datasetIndex).controller;e&&e[s+\"HoverStyle\"](o.element,o.datasetIndex,o.index)}}getActiveElements(){return this._active||[]}setActiveElements(t){const e=this._active||[],i=t.map((({datasetIndex:t,index:e})=>{const i=this.getDatasetMeta(t);if(!i)throw new Error(\"No dataset found at index \"+t);return{datasetIndex:t,element:i.data[e],index:e}}));!f(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return 1===this._plugins._cache.filter((e=>e.plugin.id===t)).length}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins(\"beforeEvent\",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins(\"afterEvent\",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=D(t),l=function(t,e,i,s){return i&&\"mouseout\"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,d(n.onHover,[t,a,this],this),r&&d(n.onClick,[t,a,this],this));const h=!f(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if(\"mouseout\"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}function kn(){return u(wn.instances,(t=>t._plugins.invalidate()))}function Sn(){throw new Error(\"This method is not implemented: Check that a complete date adapter is provided.\")}class Pn{static override(t){Object.assign(Pn.prototype,t)}constructor(t){this.options=t||{}}init(){}formats(){return Sn()}parse(){return Sn()}format(){return Sn()}add(){return Sn()}diff(){return Sn()}startOf(){return Sn()}endOf(){return Sn()}}var Dn={_date:Pn};function Cn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;e<n;e++)s=s.concat(i[e].controller.getAllParsedValues(t));t._cache.$bar=lt(s.sort(((t,e)=>t-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(k(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;s<n;++s)o=e.getPixelForValue(i[s]),l();for(a=void 0,s=0,n=e.ticks.length;s<n;++s)o=e.getPixelForTick(s),l();return r}function On(t,e,i,s){return n(t)?function(t,e,i,s){const n=i.parse(t[0],s),o=i.parse(t[1],s),a=Math.min(n,o),r=Math.max(n,o);let l=a,h=r;Math.abs(a)>Math.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function An(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;h<c;++h)u=e[h],d={},d[n.axis]=r||n.parse(a[h],h),l.push(On(u,d,o,h));return l}function Tn(t){return t&&void 0!==t.barStart&&void 0!==t.barEnd}function Ln(t,e,i,s){let n=e.borderSkipped;const o={};if(!n)return void(t.borderSkipped=o);if(!0===n)return void(t.borderSkipped={top:!0,right:!0,bottom:!0,left:!0});const{start:a,end:r,reverse:l,top:h,bottom:c}=function(t){let e,i,s,n,o;return t.horizontal?(e=t.base>t.x,i=\"left\",s=\"right\"):(e=t.base<t.y,i=\"bottom\",s=\"top\"),e?(n=\"end\",o=\"start\"):(n=\"start\",o=\"end\"),{start:i,end:s,reverse:e,top:n,bottom:o}}(t);\"middle\"===n&&i&&(t.enableBorderRadius=!0,(i._top||0)===s?n=h:(i._bottom||0)===s?n=c:(o[En(c,a,r,l)]=!0,n=h)),o[En(n,a,r,l)]=!0,t.borderSkipped=o}function En(t,e,i,s){var n,o,a;return s?(a=i,t=Rn(t=(n=t)===(o=e)?a:n===a?o:n,i,e)):t=Rn(t,e,i),t}function Rn(t,e,i){return\"start\"===t?e:\"end\"===t?i:t}function In(t,{inflateAmount:e},i){t.inflateAmount=\"auto\"===e?1===i?.33:0:e}class zn extends Vs{static id=\"doughnut\";static defaults={datasetElementType:!1,dataElementType:\"arc\",animation:{animateRotate:!0,animateScale:!1},animations:{numbers:{type:\"number\",properties:[\"circumference\",\"endAngle\",\"innerRadius\",\"outerRadius\",\"startAngle\",\"x\",\"y\",\"offset\",\"borderWidth\",\"spacing\"]}},cutout:\"50%\",rotation:0,circumference:360,radius:\"100%\",spacing:0,indexAxis:\"r\"};static descriptors={_scriptable:t=>\"spacing\"!==t,_indexable:t=>\"spacing\"!==t};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map(((e,n)=>{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}};constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,a,r=t=>+i[t];if(o(i[t])){const{key:t=\"value\"}=this._parsing;r=e=>+M(i[e],t)}for(n=t,a=t+e;n<a;++n)s._parsed[n]=r(n)}}_getRotation(){return $(this.options.rotation-90)}_getCircumference(){return $(this.options.circumference)}_getRotationExtents(){let t=O,e=-O;for(let i=0;i<this.chart.data.datasets.length;++i)if(this.chart.isDatasetVisible(i)&&this.chart.getDatasetMeta(i).type===this._type){const s=this.chart.getDatasetMeta(i).controller,n=s._getRotation(),o=s._getCircumference();t=Math.min(t,n),e=Math.max(e,n+o)}return{rotation:t,circumference:e-t}}update(t){const e=this.chart,{chartArea:i}=e,s=this._cachedMeta,n=s.data,o=this.getMaxBorderWidth()+this.getMaxOffset(n)+this.options.spacing,a=Math.max((Math.min(i.width,i.height)-o)/2,0),r=Math.min(h(this.options.cutout,a),1),l=this._getRingWeight(this.index),{circumference:d,rotation:u}=this._getRotationExtents(),{ratioX:f,ratioY:g,offsetX:p,offsetY:m}=function(t,e,i){let s=1,n=1,o=0,a=0;if(e<O){const r=t,l=r+e,h=Math.cos(r),c=Math.sin(r),d=Math.cos(l),u=Math.sin(l),f=(t,e,s)=>Z(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>Z(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(E,c,u),b=g(C,h,d),x=g(C+E,c,u);s=(p-b)/2,n=(m-x)/2,o=-(p+b)/2,a=-(m+x)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(u,d,r),b=(i.width-o)/f,x=(i.height-o)/g,_=Math.max(Math.min(b,x)/2,0),y=c(this.options.radius,_),v=(y-Math.max(y*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=p*y,this.offsetY=m*y,s.total=this.calculateTotal(),this.outerRadius=y-v*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-v*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/O)}updateElements(t,e,i,s){const n=\"reset\"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,{sharedOptions:f,includeOptions:g}=this._getSharedOptions(e,s);let p,m=this._getRotation();for(p=0;p<e;++p)m+=this._circumference(p,n);for(p=e;p<e+i;++p){const e=this._circumference(p,n),i=t[p],o={x:l+this.offsetX,y:h+this.offsetY,startAngle:m,endAngle:m+e,circumference:e,outerRadius:u,innerRadius:d};g&&(o.options=f||this.resolveDataElementOptions(p,i.active?\"active\":s)),m+=e,this.updateElement(i,p,o,s)}}calculateTotal(){const t=this._cachedMeta,e=t.data;let i,s=0;for(i=0;i<e.length;i++){const n=t._parsed[i];null===n||isNaN(n)||!this.chart.getDataVisibility(i)||e[i].hidden||(s+=Math.abs(n))}return s}calculateCircumference(t){const e=this._cachedMeta.total;return e>0&&!isNaN(t)?O*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t],i.options.locale);return{label:s[t]||\"\",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s<n;++s)if(i.isDatasetVisible(s)){o=i.getDatasetMeta(s),t=o.data,a=o.controller;break}if(!t)return 0;for(s=0,n=t.length;s<n;++s)r=a.resolveDataElementOptions(s),\"inner\"!==r.borderAlign&&(e=Math.max(e,r.borderWidth||0,r.hoverBorderWidth||0));return e}getMaxOffset(t){let e=0;for(let i=0,s=t.length;i<s;++i){const t=this.resolveDataElementOptions(i);e=Math.max(e,t.offset||0,t.hoverOffset||0)}return e}_getRingWeightOffset(t){let e=0;for(let i=0;i<t;++i)this.chart.isDatasetVisible(i)&&(e+=this._getRingWeight(i));return e}_getRingWeight(t){return Math.max(l(this.chart.data.datasets[t].weight,1),0)}_getVisibleDatasetWeightTotal(){return this._getRingWeightOffset(this.chart.data.datasets.length)||1}}class Fn extends Vs{static id=\"polarArea\";static defaults={dataElementType:\"arc\",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"startAngle\",\"endAngle\",\"innerRadius\",\"outerRadius\"]}},indexAxis:\"r\",startAngle:0};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map(((e,n)=>{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:\"radialLinear\",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t].r,i.options.locale);return{label:s[t]||\"\",value:n}}parseObjectData(t,e,i,s){return ei.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach(((t,i)=>{const s=this.getParsed(i).r;!isNaN(s)&&this.chart.getDataVisibility(i)&&(s<e.min&&(e.min=s),s>e.max&&(e.max=s))})),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n=\"reset\"===s,o=this.chart,a=o.options.animation,r=this._cachedMeta.rScale,l=r.xCenter,h=r.yCenter,c=r.getIndexAngle(0)-.5*C;let d,u=c;const f=360/this.countVisibleElements();for(d=0;d<e;++d)u+=this._computeAngle(d,s,f);for(d=e;d<e+i;d++){const e=t[d];let i=u,g=u+this._computeAngle(d,s,f),p=o.getDataVisibility(d)?r.getDistanceFromCenterForValue(this.getParsed(d).r):0;u=g,n&&(a.animateScale&&(p=0),a.animateRotate&&(i=g=c));const m={x:l,y:h,innerRadius:0,outerRadius:p,startAngle:i,endAngle:g,options:this.resolveDataElementOptions(d,e.active?\"active\":s)};this.updateElement(e,d,m,s)}}countVisibleElements(){const t=this._cachedMeta;let e=0;return t.data.forEach(((t,i)=>{!isNaN(this.getParsed(i).r)&&this.chart.getDataVisibility(i)&&e++})),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?$(this.resolveDataElementOptions(t,e).angle||i):0}}var Vn=Object.freeze({__proto__:null,BarController:class extends Vs{static id=\"bar\";static defaults={datasetElementType:!1,dataElementType:\"bar\",categoryPercentage:.8,barPercentage:.9,grouped:!0,animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"base\",\"width\",\"height\"]}}};static overrides={scales:{_index_:{type:\"category\",offset:!0,grid:{offset:!0}},_value_:{type:\"linear\",beginAtZero:!0}}};parsePrimitiveData(t,e,i,s){return An(t,e,i,s)}parseArrayData(t,e,i,s){return An(t,e,i,s)}parseObjectData(t,e,i,s){const{iScale:n,vScale:o}=t,{xAxisKey:a=\"x\",yAxisKey:r=\"y\"}=this._parsing,l=\"x\"===n.axis?a:r,h=\"x\"===o.axis?a:r,c=[];let d,u,f,g;for(d=i,u=i+s;d<u;++d)g=e[d],f={},f[n.axis]=n.parse(M(g,l),d),c.push(On(M(g,h),f,o,d));return c}updateRangeFromParsed(t,e,i,s){super.updateRangeFromParsed(t,e,i,s);const n=i._custom;n&&e===this._cachedMeta.vScale&&(t.min=Math.min(t.min,n.min),t.max=Math.max(t.max,n.max))}getMaxOverflow(){return 0}getLabelAndValue(t){const e=this._cachedMeta,{iScale:i,vScale:s}=e,n=this.getParsed(t),o=n._custom,a=Tn(o)?\"[\"+o.start+\", \"+o.end+\"]\":\"\"+s.getLabelForValue(n[s.axis]);return{label:\"\"+i.getLabelForValue(n[i.axis]),value:a}}initialize(){this.enableOptionSharing=!0,super.initialize();this._cachedMeta.stack=this.getDataset().stack}update(t){const e=this._cachedMeta;this.updateElements(e.data,0,e.data.length,t)}updateElements(t,e,i,n){const o=\"reset\"===n,{index:a,_cachedMeta:{vScale:r}}=this,l=r.getBasePixel(),h=r.isHorizontal(),c=this._getRuler(),{sharedOptions:d,includeOptions:u}=this._getSharedOptions(e,n);for(let f=e;f<e+i;f++){const e=this.getParsed(f),i=o||s(e[r.axis])?{base:l,head:l}:this._calculateBarValuePixels(f),g=this._calculateBarIndexPixels(f,c),p=(e._stacks||{})[r.axis],m={horizontal:h,base:i.base,enableBorderRadius:!p||Tn(e._custom)||a===p._top||a===p._bottom,x:h?i.head:g.center,y:h?g.center:i.head,height:h?g.size:Math.abs(i.size),width:h?Math.abs(i.size):g.size};u&&(m.options=d||this.resolveDataElementOptions(f,t[f].active?\"active\":n));const b=m.options||t[f].options;Ln(m,b,p,a),In(m,b,c.ratio),this.updateElement(t[f],f,m,n)}}_getStacks(t,e){const{iScale:i}=this._cachedMeta,n=i.getMatchingVisibleMetas(this._type).filter((t=>t.controller.options.grouped)),o=i.options.stacked,a=[],r=t=>{const i=t.controller.getParsed(e),n=i&&i[t.vScale.axis];if(s(n)||isNaN(n))return!0};for(const i of n)if((void 0===e||!r(i))&&((!1===o||-1===a.indexOf(i.stack)||void 0===o&&void 0===i.stack)&&a.push(i.stack),i.index===t))break;return a.length||a.push(void 0),a}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,i){const s=this._getStacks(t,i),n=void 0!==e?s.indexOf(e):-1;return-1===n?s.length-1:n}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let n,o;for(n=0,o=e.data.length;n<o;++n)s.push(i.getPixelForValue(this.getParsed(n)[i.axis],n));const a=t.barThickness;return{min:a||Cn(e),pixels:s,start:i._startPixel,end:i._endPixel,stackCount:this._getStackCount(),scale:i,grouped:t.grouped,ratio:a?1:t.categoryPercentage*t.barPercentage}}_calculateBarValuePixels(t){const{_cachedMeta:{vScale:e,_stacked:i,index:n},options:{base:o,minBarLength:a}}=this,r=o||0,l=this.getParsed(t),h=l._custom,c=Tn(h);let d,u,f=l[e.axis],g=0,p=i?this.applyStack(e,l,i):f;p!==f&&(g=p-f,p=f),c&&(f=h.barStart,p=h.barEnd-h.barStart,0!==f&&F(f)!==F(h.barEnd)&&(g=0),g+=f);const m=s(o)||c?g:o;let b=e.getPixelForValue(m);if(d=this.chart.getDataVisibility(t)?e.getPixelForValue(g+p):b,u=d-b,Math.abs(u)<a){u=function(t,e,i){return 0!==t?F(t):(e.isHorizontal()?1:-1)*(e.min>=i?1:-1)}(u,e,r)*a,f===r&&(b-=u/2);const t=e.getPixelForDecimal(0),s=e.getPixelForDecimal(1),o=Math.min(t,s),h=Math.max(t,s);b=Math.max(Math.min(b,h),o),d=b+u,i&&!c&&(l._stacks[e.axis]._visualValues[n]=e.getValueForPixel(d)-e.getValueForPixel(b))}if(b===e.getPixelForValue(r)){const t=F(u)*e.getLineWidthForValue(r)/2;b+=t,u-=t}return{size:u,base:b,head:d,center:d+u/2}}_calculateBarIndexPixels(t,e){const i=e.scale,n=this.options,o=n.skipNull,a=l(n.maxBarThickness,1/0);let r,h;if(e.grouped){const i=o?this._getStackCount(t):e.stackCount,l=\"flex\"===n.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t<n.length-1?n[t+1]:null;const l=i.categoryPercentage;null===a&&(a=o-(null===r?e.end-e.start:r-o)),null===r&&(r=o+o-a);const h=o-(o-Math.min(a,r))/2*l;return{chunk:Math.abs(r-a)/2*l/s,ratio:i.barPercentage,start:h}}(t,e,n,i):function(t,e,i,n){const o=i.barThickness;let a,r;return s(o)?(a=e.min*i.categoryPercentage,r=i.barPercentage):(a=o*n,r=1),{chunk:a/n,ratio:r,start:e.pixels[t]-a/2}}(t,e,n,i),c=this._getStackIndex(this.index,this._cachedMeta.stack,o?t:void 0);r=l.start+l.chunk*c+l.chunk/2,h=Math.min(a,l.chunk*l.ratio)}else r=i.getPixelForValue(this.getParsed(t)[i.axis],t),h=Math.min(a,e.min*e.ratio);return{base:r-h/2,head:r+h/2,center:r,size:h}}draw(){const t=this._cachedMeta,e=t.vScale,i=t.data,s=i.length;let n=0;for(;n<s;++n)null!==this.getParsed(n)[e.axis]&&i[n].draw(this._ctx)}},BubbleController:class extends Vs{static id=\"bubble\";static defaults={datasetElementType:!1,dataElementType:\"point\",animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"borderWidth\",\"radius\"]}}};static overrides={scales:{x:{type:\"linear\"},y:{type:\"linear\"}}};initialize(){this.enableOptionSharing=!0,super.initialize()}parsePrimitiveData(t,e,i,s){const n=super.parsePrimitiveData(t,e,i,s);for(let t=0;t<n.length;t++)n[t]._custom=this.resolveDataElementOptions(t+i).radius;return n}parseArrayData(t,e,i,s){const n=super.parseArrayData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s[2],this.resolveDataElementOptions(t+i).radius)}return n}parseObjectData(t,e,i,s){const n=super.parseObjectData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s&&s.r&&+s.r,this.resolveDataElementOptions(t+i).radius)}return n}getMaxOverflow(){const t=this._cachedMeta.data;let e=0;for(let i=t.length-1;i>=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y),l=o._custom;return{label:i[t]||\"\",value:\"(\"+a+\", \"+r+(l?\", \"+l:\"\")+\")\"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n=\"reset\"===s,{iScale:o,vScale:a}=this._cachedMeta,{sharedOptions:r,includeOptions:l}=this._getSharedOptions(e,s),h=o.axis,c=a.axis;for(let d=e;d<e+i;d++){const e=t[d],i=!n&&this.getParsed(d),u={},f=u[h]=n?o.getPixelForDecimal(.5):o.getPixelForValue(i[h]),g=u[c]=n?a.getBasePixel():a.getPixelForValue(i[c]);u.skip=isNaN(f)||isNaN(g),l&&(u.options=r||this.resolveDataElementOptions(d,e.active?\"active\":s),n&&(u.options.radius=0)),this.updateElement(e,d,u,s)}}resolveDataElementOptions(t,e){const i=this.getParsed(t);let s=super.resolveDataElementOptions(t,e);s.$shared&&(s=Object.assign({},s,{$shared:!1}));const n=s.radius;return\"active\"!==e&&(s.radius=0),s.radius+=l(i&&i._custom,n),s}},DoughnutController:zn,LineController:class extends Vs{static id=\"line\";static defaults={datasetElementType:\"line\",dataElementType:\"point\",showLine:!0,spanGaps:!1};static overrides={scales:{_index_:{type:\"category\"},_value_:{type:\"linear\"}}};initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:n}=e,o=this.chart._animationsDisabled;let{start:a,count:r}=pt(e,s,o);this._drawStart=a,this._drawCount=r,mt(e)&&(a=0,r=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!n._decimated,i.points=s;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(i,void 0,{animated:!o,options:l},t),this.updateElements(s,a,r,t)}updateElements(t,e,i,n){const o=\"reset\"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,{sharedOptions:c,includeOptions:d}=this._getSharedOptions(e,n),u=a.axis,f=r.axis,{spanGaps:g,segment:p}=this.options,m=W(g)?g:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||\"none\"===n,x=e+i,_=t.length;let y=e>0&&this.getParsed(e-1);for(let i=0;i<_;++i){const g=t[i],_=b?g:{};if(i<e||i>=x){_.skip=!0;continue}const v=this.getParsed(i),M=s(v[f]),w=_[u]=a.getPixelForValue(v[u],i),k=_[f]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,v,l):v[f],i);_.skip=isNaN(w)||isNaN(k)||M,_.stop=i>0&&Math.abs(v[u]-y[u])>m,p&&(_.parsed=v,_.raw=h.data[i]),d&&(_.options=c||this.resolveDataElementOptions(i,g.active?\"active\":n)),b||this.updateElement(g,i,_,n),y=v}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}},PolarAreaController:Fn,PieController:class extends zn{static id=\"pie\";static defaults={cutout:0,rotation:0,circumference:360,radius:\"100%\"}},RadarController:class extends Vs{static id=\"radar\";static defaults={datasetElementType:\"line\",dataElementType:\"point\",indexAxis:\"r\",showLine:!0,elements:{line:{fill:\"start\"}}};static overrides={aspectRatio:1,scales:{r:{type:\"radialLinear\"}}};getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:\"\"+e.getLabelForValue(i[e.axis])}}parseObjectData(t,e,i,s){return ei.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,\"resize\"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this._cachedMeta.rScale,o=\"reset\"===s;for(let a=e;a<e+i;a++){const e=t[a],i=this.resolveDataElementOptions(a,e.active?\"active\":s),r=n.getPointPositionForValue(a,this.getParsed(a).r),l=o?n.xCenter:r.x,h=o?n.yCenter:r.y,c={x:l,y:h,angle:r.angle,skip:isNaN(l)||isNaN(h),options:i};this.updateElement(e,a,c,s)}}},ScatterController:class extends Vs{static id=\"scatter\";static defaults={datasetElementType:!1,dataElementType:\"point\",showLine:!1,fill:!1};static overrides={interaction:{mode:\"point\"},scales:{x:{type:\"linear\"},y:{type:\"linear\"}}};getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y);return{label:i[t]||\"\",value:\"(\"+a+\", \"+r+\")\"}}update(t){const e=this._cachedMeta,{data:i=[]}=e,s=this.chart._animationsDisabled;let{start:n,count:o}=pt(e,i,s);if(this._drawStart=n,this._drawCount=o,mt(e)&&(n=0,o=i.length),this.options.showLine){const{dataset:n,_dataset:o}=e;n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!o._decimated,n.points=i;const a=this.resolveDatasetElementOptions(t);a.segment=this.options.segment,this.updateElement(n,void 0,{animated:!s,options:a},t)}this.updateElements(i,n,o,t)}addElements(){const{showLine:t}=this.options;!this.datasetElementType&&t&&(this.datasetElementType=this.chart.registry.getElement(\"line\")),super.addElements()}updateElements(t,e,i,n){const o=\"reset\"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,c=this.resolveDataElementOptions(e,n),d=this.getSharedOptions(c),u=this.includeOptions(n,d),f=a.axis,g=r.axis,{spanGaps:p,segment:m}=this.options,b=W(p)?p:Number.POSITIVE_INFINITY,x=this.chart._animationsDisabled||o||\"none\"===n;let _=e>0&&this.getParsed(e-1);for(let c=e;c<e+i;++c){const e=t[c],i=this.getParsed(c),p=x?e:{},y=s(i[g]),v=p[f]=a.getPixelForValue(i[f],c),M=p[g]=o||y?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,i,l):i[g],c);p.skip=isNaN(v)||isNaN(M)||y,p.stop=c>0&&Math.abs(i[f]-_[f])>b,m&&(p.parsed=i,p.raw=h.data[c]),u&&(p.options=d||this.resolveDataElementOptions(c,e.active?\"active\":n)),x||this.updateElement(e,c,p,n),_=i}this.updateSharedOptions(d,n,c)}getMaxOverflow(){const t=this._cachedMeta,e=t.data||[];if(!this.options.showLine){let t=0;for(let i=e.length-1;i>=0;--i)t=Math.max(t,e[i].size(this.resolveDataElementOptions(i))/2);return t>0&&t}const i=t.dataset,s=i.options&&i.options.borderWidth||0;if(!e.length)return s;const n=e[0].size(this.resolveDataElementOptions(0)),o=e[e.length-1].size(this.resolveDataElementOptions(e.length-1));return Math.max(s,n,o)/2}}});function Bn(t,e,i,s){const n=_i(t.options.borderRadius,[\"outerStart\",\"outerEnd\",\"innerStart\",\"innerEnd\"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return J(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:J(n.innerStart,0,a),innerEnd:J(n.innerEnd,0,a)}}function Nn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Wn(t,e,i,s,n,o){const{x:a,y:r,startAngle:l,pixelMargin:h,innerRadius:c}=e,d=Math.max(e.outerRadius+s+i-h,0),u=c>0?c+s+i+h:0;let f=0;const g=n-l;if(s){const t=((c>0?c-s:0)+(d>0?d-s:0))/2;f=(g-(0!==t?g*t/(t+s):g))/2}const p=(g-Math.max(.001,g*d-i/C)/d)/2,m=l+p+f,b=n-p-f,{outerStart:x,outerEnd:_,innerStart:y,innerEnd:v}=Bn(e,u,d,b-m),M=d-x,w=d-_,k=m+x/M,S=b-_/w,P=u+y,D=u+v,O=m+y/P,A=b-v/D;if(t.beginPath(),o){const e=(k+S)/2;if(t.arc(a,r,d,k,e),t.arc(a,r,d,e,S),_>0){const e=Nn(w,S,a,r);t.arc(e.x,e.y,_,S,b+E)}const i=Nn(D,b,a,r);if(t.lineTo(i.x,i.y),v>0){const e=Nn(D,A,a,r);t.arc(e.x,e.y,v,b+E,A+Math.PI)}const s=(b-v/u+(m+y/u))/2;if(t.arc(a,r,u,b-v/u,s,!0),t.arc(a,r,u,s,m+y/u,!0),y>0){const e=Nn(P,O,a,r);t.arc(e.x,e.y,y,O+Math.PI,m-E)}const n=Nn(M,m,a,r);if(t.lineTo(n.x,n.y),x>0){const e=Nn(M,k,a,r);t.arc(e.x,e.y,x,m-E,k)}}else{t.moveTo(a,r);const e=Math.cos(k)*d+a,i=Math.sin(k)*d+r;t.lineTo(e,i);const s=Math.cos(S)*d+a,n=Math.sin(S)*d+r;t.lineTo(s,n)}t.closePath()}function Hn(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r,options:l}=e,{borderWidth:h,borderJoinStyle:c}=l,d=\"inner\"===l.borderAlign;if(!h)return;d?(t.lineWidth=2*h,t.lineJoin=c||\"round\"):(t.lineWidth=h,t.lineJoin=c||\"bevel\");let u=e.endAngle;if(o){Wn(t,e,i,s,u,n);for(let e=0;e<o;++e)t.stroke();isNaN(r)||(u=a+(r%O||O))}d&&function(t,e,i){const{startAngle:s,pixelMargin:n,x:o,y:a,outerRadius:r,innerRadius:l}=e;let h=n/r;t.beginPath(),t.arc(o,a,r,s-h,i+h),l>n?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+E,s-E),t.closePath(),t.clip()}(t,e,u),o||(Wn(t,e,i,s,u,n),t.stroke())}function jn(t,e,i=e){t.lineCap=l(i.borderCapStyle,e.borderCapStyle),t.setLineDash(l(i.borderDash,e.borderDash)),t.lineDashOffset=l(i.borderDashOffset,e.borderDashOffset),t.lineJoin=l(i.borderJoinStyle,e.borderJoinStyle),t.lineWidth=l(i.borderWidth,e.borderWidth),t.strokeStyle=l(i.borderColor,e.borderColor)}function $n(t,e,i){t.lineTo(i.x,i.y)}function Yn(t,e,i={}){const s=t.length,{start:n=0,end:o=s-1}=i,{start:a,end:r}=e,l=Math.max(n,a),h=Math.min(o,r),c=n<a&&o<a||n>r&&o>r;return{count:s,start:l,loop:e.loop,ilen:h<l&&!c?s+h-l:h-l}}function Un(t,e,i,s){const{points:n,options:o}=e,{count:a,start:r,loop:l,ilen:h}=Yn(n,i,s),c=function(t){return t.stepped?ze:t.tension||\"monotone\"===t.cubicInterpolationMode?Fe:$n}(o);let d,u,f,{move:g=!0,reverse:p}=s||{};for(d=0;d<=h;++d)u=n[(r+(p?h-d:d))%a],u.skip||(g?(t.moveTo(u.x,u.y),g=!1):c(t,f,u,p,o.stepped),f=u);return l&&(u=n[(r+(p?h:0))%a],c(t,f,u,p,o.stepped)),!!l}function Xn(t,e,i,s){const n=e.points,{count:o,start:a,ilen:r}=Yn(n,i,s),{move:l=!0,reverse:h}=s||{};let c,d,u,f,g,p,m=0,b=0;const x=t=>(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[x(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[x(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(i<f?f=i:i>g&&(g=i),m=(b*m+e)/++b):(_(),t.lineTo(e,i),u=s,b=0,f=g=i),p=i}_()}function qn(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||\"monotone\"===e.cubicInterpolationMode||e.stepped||i)?Xn:Un}const Kn=\"function\"==typeof Path2D;function Gn(t,e,i,s){Kn&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),jn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=qn(e);for(const r of n)jn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class Zn extends Bs{static id=\"line\";static defaults={borderCapStyle:\"butt\",borderDash:[],borderDashOffset:0,borderJoinStyle:\"miter\",borderWidth:3,capBezierPoints:!0,cubicInterpolationMode:\"default\",fill:!1,spanGaps:!1,stepped:!1,tension:0};static defaultRoutes={backgroundColor:\"backgroundColor\",borderColor:\"borderColor\"};static descriptors={_scriptable:!0,_indexable:t=>\"borderDash\"!==t&&\"fill\"!==t};constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||\"monotone\"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;li(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Ri(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Ei(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?gi:t.tension||\"monotone\"===t.cubicInterpolationMode?pi:fi}(i);let l,h;for(l=0,h=o.length;l<h;++l){const{start:h,end:c}=o[l],d=n[h],u=n[c];if(d===u){a.push(d);continue}const f=r(d,u,Math.abs((s-d[e])/(u[e]-d[e])),i.stepped);f[e]=t[e],a.push(f)}return 1===a.length?a[0]:a}pathSegment(t,e,i){return qn(this)(t,this,e,i)}path(t,e,i){const s=this.segments,n=qn(this);let o=this._loop;e=e||0,i=i||this.points.length-e;for(const a of s)o&=n(t,this,a,{start:e,end:e+i-1});return!!o}draw(t,e,i,s){const n=this.options||{};(this.points||[]).length&&n.borderWidth&&(t.save(),Gn(t,this,i,s),t.restore()),this.animated&&(this._pointsUpdated=!1,this._path=void 0)}}function Jn(t,e,i,s){const n=t.options,{[i]:o}=t.getProps([i],s);return Math.abs(e-o)<n.radius+n.hitRadius}function Qn(t,e){const{x:i,y:s,base:n,width:o,height:a}=t.getProps([\"x\",\"y\",\"base\",\"width\",\"height\"],e);let r,l,h,c,d;return t.horizontal?(d=a/2,r=Math.min(i,n),l=Math.max(i,n),h=s-d,c=s+d):(d=o/2,r=i-d,l=i+d,h=Math.min(s,n),c=Math.max(s,n)),{left:r,top:h,right:l,bottom:c}}function to(t,e,i,s){return t?0:J(e,i,s)}function eo(t){const e=Qn(t),i=e.right-e.left,s=e.bottom-e.top,n=function(t,e,i){const s=t.options.borderWidth,n=t.borderSkipped,o=yi(s);return{t:to(n.top,o.top,0,i),r:to(n.right,o.right,0,e),b:to(n.bottom,o.bottom,0,i),l:to(n.left,o.left,0,e)}}(t,i/2,s/2),a=function(t,e,i){const{enableBorderRadius:s}=t.getProps([\"enableBorderRadius\"]),n=t.options.borderRadius,a=vi(n),r=Math.min(e,i),l=t.borderSkipped,h=s||o(n);return{topLeft:to(!h||l.top||l.left,a.topLeft,0,r),topRight:to(!h||l.top||l.right,a.topRight,0,r),bottomLeft:to(!h||l.bottom||l.left,a.bottomLeft,0,r),bottomRight:to(!h||l.bottom||l.right,a.bottomRight,0,r)}}(t,i/2,s/2);return{outer:{x:e.left,y:e.top,w:i,h:s,radius:a},inner:{x:e.left+n.l,y:e.top+n.t,w:i-n.l-n.r,h:s-n.t-n.b,radius:{topLeft:Math.max(0,a.topLeft-Math.max(n.t,n.l)),topRight:Math.max(0,a.topRight-Math.max(n.t,n.r)),bottomLeft:Math.max(0,a.bottomLeft-Math.max(n.b,n.l)),bottomRight:Math.max(0,a.bottomRight-Math.max(n.b,n.r))}}}}function io(t,e,i,s){const n=null===e,o=null===i,a=t&&!(n&&o)&&Qn(t,s);return a&&(n||tt(e,a.left,a.right))&&(o||tt(i,a.top,a.bottom))}function so(t,e){t.rect(e.x,e.y,e.w,e.h)}function no(t,e,i={}){const s=t.x!==i.x?-e:0,n=t.y!==i.y?-e:0,o=(t.x+t.w!==i.x+i.w?e:0)-s,a=(t.y+t.h!==i.y+i.h?e:0)-n;return{x:t.x+s,y:t.y+n,w:t.w+o,h:t.h+a,radius:t.radius}}var oo=Object.freeze({__proto__:null,ArcElement:class extends Bs{static id=\"arc\";static defaults={borderAlign:\"center\",borderColor:\"#fff\",borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0};static defaultRoutes={backgroundColor:\"backgroundColor\"};constructor(t){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.getProps([\"x\",\"y\"],i),{angle:n,distance:o}=X(s,{x:t,y:e}),{startAngle:a,endAngle:r,innerRadius:h,outerRadius:c,circumference:d}=this.getProps([\"startAngle\",\"endAngle\",\"innerRadius\",\"outerRadius\",\"circumference\"],i),u=this.options.spacing/2,f=l(d,r-a)>=O||Z(n,a,r),g=tt(o,h+u,c+u);return f&&g}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps([\"x\",\"y\",\"startAngle\",\"endAngle\",\"innerRadius\",\"outerRadius\"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/4,n=(e.spacing||0)/2,o=e.circular;if(this.pixelMargin=\"inner\"===e.borderAlign?.33:0,this.fullCircles=i>O?Math.floor(i/O):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();const a=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(a)*s,Math.sin(a)*s);const r=s*(1-Math.sin(Math.min(C,i||0)));t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor,function(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r}=e;let l=e.endAngle;if(o){Wn(t,e,i,s,l,n);for(let e=0;e<o;++e)t.fill();isNaN(r)||(l=a+(r%O||O))}Wn(t,e,i,s,l,n),t.fill()}(t,this,r,n,o),Hn(t,this,r,n,o),t.restore()}},LineElement:Zn,PointElement:class extends Bs{static id=\"point\";static defaults={borderWidth:1,hitRadius:1,hoverBorderWidth:1,hoverRadius:4,pointStyle:\"circle\",radius:3,rotation:0};static defaultRoutes={backgroundColor:\"backgroundColor\",borderColor:\"borderColor\"};constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.options,{x:n,y:o}=this.getProps([\"x\",\"y\"],i);return Math.pow(t-n,2)+Math.pow(e-o,2)<Math.pow(s.hitRadius+s.radius,2)}inXRange(t,e){return Jn(this,t,\"x\",e)}inYRange(t,e){return Jn(this,t,\"y\",e)}getCenterPoint(t){const{x:e,y:i}=this.getProps([\"x\",\"y\"],t);return{x:e,y:i}}size(t){let e=(t=t||this.options||{}).radius||0;e=Math.max(e,e&&t.hoverRadius||0);return 2*(e+(e&&t.borderWidth||0))}draw(t,e){const i=this.options;this.skip||i.radius<.1||!Ee(this,e,this.size(i)/2)||(t.strokeStyle=i.borderColor,t.lineWidth=i.borderWidth,t.fillStyle=i.backgroundColor,Te(t,i,this.x,this.y))}getRange(){const t=this.options||{};return t.radius+t.hitRadius}},BarElement:class extends Bs{static id=\"bar\";static defaults={borderSkipped:\"start\",borderWidth:0,borderRadius:0,inflateAmount:\"auto\",pointStyle:void 0};static defaultRoutes={backgroundColor:\"backgroundColor\",borderColor:\"borderColor\"};constructor(t){super(),this.options=void 0,this.horizontal=void 0,this.base=void 0,this.width=void 0,this.height=void 0,this.inflateAmount=void 0,t&&Object.assign(this,t)}draw(t){const{inflateAmount:e,options:{borderColor:i,backgroundColor:s}}=this,{inner:n,outer:o}=eo(this),a=(r=o.radius).topLeft||r.topRight||r.bottomLeft||r.bottomRight?We:so;var r;t.save(),o.w===n.w&&o.h===n.h||(t.beginPath(),a(t,no(o,e,n)),t.clip(),a(t,no(n,-e,o)),t.fillStyle=i,t.fill(\"evenodd\")),t.beginPath(),a(t,no(n,e)),t.fillStyle=s,t.fill(),t.restore()}inRange(t,e,i){return io(this,t,e,i)}inXRange(t,e){return io(this,t,null,e)}inYRange(t,e){return io(this,null,t,e)}getCenterPoint(t){const{x:e,y:i,base:s,horizontal:n}=this.getProps([\"x\",\"y\",\"base\",\"horizontal\"],t);return{x:n?(e+s)/2:e,y:n?i:(i+s)/2}}getRange(t){return\"x\"===t?this.width/2:this.height/2}}});function ao(t,e,i,s){const n=t.indexOf(e);if(-1===n)return((t,e,i,s)=>(\"string\"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}function ro(t){const e=this.getLabels();return t>=0&&t<e.length?e[t]:t}function lo(t,e,{horizontal:i,minRotation:s}){const n=$(s),o=(i?Math.sin(n):Math.cos(n))||.001,a=.75*e*(\"\"+t).length;return Math.min(e/o,a)}class ho extends Ks{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return s(t)||(\"number\"==typeof t||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:n}=this;const o=t=>s=e?s:t,a=t=>n=i?n:t;if(t){const t=F(s),e=F(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=0===n?1:Math.abs(.05*n);a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const n=function(t,e){const i=[],{bounds:n,step:o,min:a,max:r,precision:l,count:h,maxTicks:c,maxDigits:d,includeBounds:u}=t,f=o||1,g=c-1,{min:p,max:m}=e,b=!s(a),x=!s(r),_=!s(h),y=(m-p)/(d+1);let v,M,w,k,S=B((m-p)/g/f)*f;if(S<1e-14&&!b&&!x)return[{value:p},{value:m}];k=Math.ceil(m/S)-Math.floor(p/S),k>g&&(S=B(k*S/g/f)*f),s(l)||(v=Math.pow(10,l),S=Math.ceil(S*v)/v),\"ticks\"===n?(M=Math.floor(p/S)*S,w=Math.ceil(m/S)*S):(M=p,w=m),b&&x&&o&&H((r-a)/o,S/1e3)?(k=Math.round(Math.min((r-a)/S,c)),S=(r-a)/k,M=a,w=r):_?(M=b?a:M,w=x?r:w,k=h-1,S=(w-M)/k):(k=(w-M)/S,k=V(k,Math.round(k),S/1e3)?Math.round(k):Math.ceil(k));const P=Math.max(U(S),U(M));v=Math.pow(10,s(l)?P:l),M=Math.round(M*v)/v,w=Math.round(w*v)/v;let D=0;for(b&&(u&&M!==a?(i.push({value:a}),M<a&&D++,V(Math.round((M+D*S)*v)/v,a,lo(a,y,t))&&D++):M<a&&D++);D<k;++D)i.push({value:Math.round((M+D*S)*v)/v});return x&&u&&w!==r?i.length&&V(i[i.length-1].value,r,lo(r,y,t))?i[i.length-1].value=r:i.push({value:r}):x&&w!==r||i.push({value:w}),i}({maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:!1!==e.includeBounds},this._range||this);return\"ticks\"===t.bounds&&j(n,this,\"value\"),t.reverse?(n.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),n}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return ne(t,this.chart.options.locale,this.options.ticks.format)}}class co extends ho{static id=\"linear\";static defaults={ticks:{callback:ae.formatters.numeric}};determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?t:0,this.max=a(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=$(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,n=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,n.lineHeight/s))}getPixelForValue(t){return null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}const uo=t=>Math.floor(z(t)),fo=(t,e)=>Math.pow(10,uo(t)+e);function go(t){return 1===t/Math.pow(10,uo(t))}function po(t,e,i){const s=Math.pow(10,i),n=Math.floor(t/s);return Math.ceil(e/s)-n}function mo(t,{min:e,max:i}){e=r(t.min,e);const s=[],n=uo(e);let o=function(t,e){let i=uo(e-t);for(;po(t,e,i)>10;)i++;for(;po(t,e,i)<10;)i--;return Math.min(i,uo(t))}(e,i),a=o<0?Math.pow(10,Math.abs(o)):1;const l=Math.pow(10,o),h=n>o?Math.pow(10,n):0,c=Math.round((e-h)*a)/a,d=Math.floor((e-h)/l/10)*l*10;let u=Math.floor((c-d)/Math.pow(10,o)),f=r(t.min,Math.round((h+d+u*Math.pow(10,o))*a)/a);for(;f<i;)s.push({value:f,major:go(f),significand:u}),u>=10?u=u<15?15:20:u++,u>=20&&(o++,u=2,a=o>=0?1:a),f=Math.round((h+d+u*Math.pow(10,o))*a)/a;const g=r(t.max,f);return s.push({value:g,major:go(g),significand:u}),s}class bo extends Ks{static id=\"logarithmic\";static defaults={ticks:{callback:ae.formatters.logarithmic,major:{enabled:!0}}};constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=ho.prototype.parse.apply(this,[t,e]);if(0!==i)return a(i)&&i>0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?Math.max(0,t):null,this.max=a(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!a(this._userMin)&&(this.min=t===fo(this.min,0)?fo(this.min,-1):fo(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t;i===s&&(i<=0?(n(1),o(10)):(n(fo(i,-1)),o(fo(s,1)))),i<=0&&n(fo(s,-1)),s<=0&&o(fo(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=mo({min:this._userMin,max:this._userMax},this);return\"ticks\"===t.bounds&&j(e,this,\"value\"),t.reverse?(e.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),e}getLabelForValue(t){return void 0===t?\"0\":ne(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=z(t),this._valueRange=z(this.max)-z(t)}getPixelForValue(t){return void 0!==t&&0!==t||(t=this.min),null===t||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(z(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}function xo(t){const e=t.ticks;if(e.display&&t.display){const t=Mi(e.backdropPadding);return l(e.font&&e.font.size,ue.font.size)+t.height}return 0}function _o(t,e,i,s,n){return t===s||t===n?{start:e-i/2,end:e+i/2}:t<s||t>n?{start:e-i,end:e}:{start:e,end:e+i}}function yo(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],o=[],a=t._pointLabels.length,r=t.options.pointLabels,l=r.centerPointLabels?C/a:0;for(let u=0;u<a;u++){const a=r.setContext(t.getPointLabelContext(u));o[u]=a.padding;const f=t.getPointPosition(u,t.drawingArea+o[u],l),g=wi(a.font),p=(h=t.ctx,c=g,d=n(d=t._pointLabels[u])?d:[d],{w:Ce(h,c.string,d),h:d.length*c.lineHeight});s[u]=p;const m=G(t.getIndexAngle(u)+l),b=Math.round(Y(m));vo(i,e,m,_o(b,f.x,p.w,0,180),_o(b,f.y,p.h,90,270))}var h,c,d;t.setCenterPoint(e.l-i.l,i.r-e.r,e.t-i.t,i.b-e.b),t._pointLabelItems=function(t,e,i){const s=[],n=t._pointLabels.length,o=t.options,a=xo(o)/2,r=t.drawingArea,l=o.pointLabels.centerPointLabels?C/n:0;for(let o=0;o<n;o++){const n=t.getPointPosition(o,r+a+i[o],l),h=Math.round(Y(G(n.angle+E))),c=e[o],d=ko(n.y,c.h,h),u=Mo(h),f=wo(n.x,c.w,u);s.push({x:n.x,y:d,textAlign:u,left:f,top:d,right:f+c.w,bottom:d+c.h})}return s}(t,s,o)}function vo(t,e,i,s,n){const o=Math.abs(Math.sin(i)),a=Math.abs(Math.cos(i));let r=0,l=0;s.start<e.l?(r=(e.l-s.start)/o,t.l=Math.min(t.l,e.l-r)):s.end>e.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.start<e.t?(l=(e.t-n.start)/a,t.t=Math.min(t.t,e.t-l)):n.end>e.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function Mo(t){return 0===t||180===t?\"center\":t<180?\"left\":\"right\"}function wo(t,e,i){return\"right\"===i?t-=e:\"center\"===i&&(t-=e/2),t}function ko(t,e,i){return 90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e),t}function So(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,O);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;o<s;o++)i=t.getPointPosition(o,e),n.lineTo(i.x,i.y)}}class Po extends ho{static id=\"radialLinear\";static defaults={display:!0,animate:!0,position:\"chartArea\",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:ae.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}};static defaultRoutes={\"angleLines.color\":\"borderColor\",\"pointLabels.color\":\"color\",\"ticks.color\":\"color\"};static descriptors={angleLines:{_fallback:\"grid\"}};constructor(t){super(t),this.xCenter=void 0,this.yCenter=void 0,this.drawingArea=void 0,this._pointLabels=[],this._pointLabelItems=[]}setDimensions(){const t=this._padding=Mi(xo(this.options)/2),e=this.width=this.maxWidth-t.width,i=this.height=this.maxHeight-t.height;this.xCenter=Math.floor(this.left+e/2+t.left),this.yCenter=Math.floor(this.top+i/2+t.top),this.drawingArea=Math.floor(Math.min(e,i)/2)}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!1);this.min=a(t)&&!isNaN(t)?t:0,this.max=a(e)&&!isNaN(e)?e:0,this.handleTickRangeOptions()}computeTickLimit(){return Math.ceil(this.drawingArea/xo(this.options))}generateTickLabels(t){ho.prototype.generateTickLabels.call(this,t),this._pointLabels=this.getLabels().map(((t,e)=>{const i=d(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:\"\"})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?yo(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return G(t*(O/(this._pointLabels.length||1))+$(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if(s(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(s(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t<e.length){const i=e[t];return function(t,e,i){return Pi(t,{label:i,index:e,type:\"pointLabel\"})}(this.getContext(),t,i)}}getPointPosition(t,e,i=0){const s=this.getIndexAngle(t)-E+i;return{x:Math.cos(s)*e+this.xCenter,y:Math.sin(s)*e+this.yCenter,angle:s}}getPointPositionForValue(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))}getBasePosition(t){return this.getPointPositionForValue(t||0,this.getBaseValue())}getPointLabelPosition(t){const{left:e,top:i,right:s,bottom:n}=this._pointLabelItems[t];return{left:e,top:i,right:s,bottom:n}}drawBackground(){const{backgroundColor:t,grid:{circular:e}}=this.options;if(t){const i=this.ctx;i.save(),i.beginPath(),So(this,this.getDistanceFromCenterForValue(this._endValue),e,this._pointLabels.length),i.closePath(),i.fillStyle=t,i.fill(),i.restore()}}drawGrid(){const t=this.ctx,e=this.options,{angleLines:i,grid:n,border:o}=e,a=this._pointLabels.length;let r,l,h;if(e.pointLabels.display&&function(t,e){const{ctx:i,options:{pointLabels:n}}=t;for(let o=e-1;o>=0;o--){const e=n.setContext(t.getPointLabelContext(o)),a=wi(e.font),{x:r,y:l,textAlign:h,left:c,top:d,right:u,bottom:f}=t._pointLabelItems[o],{backdropColor:g}=e;if(!s(g)){const t=vi(e.borderRadius),s=Mi(e.backdropPadding);i.fillStyle=g;const n=c-s.left,o=d-s.top,a=u-c+s.width,r=f-d+s.height;Object.values(t).some((t=>0!==t))?(i.beginPath(),We(i,{x:n,y:o,w:a,h:r,radius:t}),i.fill()):i.fillRect(n,o,a,r)}Ve(i,t._pointLabels[o],r,l+a.lineHeight/2,a,{color:e.color,textAlign:h,textBaseline:\"middle\"})}}(this,a),n.display&&this.ticks.forEach(((t,e)=>{if(0!==e){l=this.getDistanceFromCenterForValue(t.value);const i=this.getContext(e),s=n.setContext(i),r=o.setContext(i);!function(t,e,i,s,n){const o=t.ctx,a=e.circular,{color:r,lineWidth:l}=e;!a&&!s||!r||!l||i<0||(o.save(),o.strokeStyle=r,o.lineWidth=l,o.setLineDash(n.dash),o.lineDashOffset=n.dashOffset,o.beginPath(),So(t,i,a,s),o.closePath(),o.stroke(),o.restore())}(this,s,l,a,r)}})),i.display){for(t.save(),r=a-1;r>=0;r--){const s=i.setContext(this.getPointLabelContext(r)),{color:n,lineWidth:o}=s;o&&n&&(t.lineWidth=o,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,l=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),h=this.getPointPosition(r,l),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(h.x,h.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign=\"center\",t.textBaseline=\"middle\",this.ticks.forEach(((s,a)=>{if(0===a&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=wi(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=Mi(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}Ve(t,s.label,0,-n,l,{color:r.color})})),t.restore()}drawTitle(){}}const Do={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},Co=Object.keys(Do);function Oo(t,e){return t-e}function Ao(t,e){if(s(e))return null;const i=t._adapter,{parser:n,round:o,isoWeekday:r}=t._parseOpts;let l=e;return\"function\"==typeof n&&(l=n(l)),a(l)||(l=\"string\"==typeof n?i.parse(l,n):i.parse(l)),null===l?null:(o&&(l=\"week\"!==o||!W(r)&&!0!==r?i.startOf(l,o):i.startOf(l,\"isoWeek\",r)),+l)}function To(t,e,i,s){const n=Co.length;for(let o=Co.indexOf(t);o<n-1;++o){const t=Do[Co[o]],n=t.steps?t.steps:Number.MAX_SAFE_INTEGER;if(t.common&&Math.ceil((i-e)/(n*t.size))<=s)return Co[o]}return Co[n-1]}function Lo(t,e,i){if(i){if(i.length){const{lo:s,hi:n}=et(i,e);t[i[s]>=e?i[s]:i[n]]=!0}}else t[e]=!0}function Eo(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a<o;++a)r=e[a],n[r]=a,s.push({value:r,major:!1});return 0!==o&&i?function(t,e,i,s){const n=t._adapter,o=+n.startOf(e[0].value,s),a=e[e.length-1].value;let r,l;for(r=o;r<=a;r=+n.add(r,1,s))l=i[r],l>=0&&(e[l].major=!0);return e}(t,s,n,i):s}class Ro extends Ks{static id=\"time\";static defaults={bounds:\"data\",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:\"millisecond\",displayFormats:{}},ticks:{source:\"auto\",callback:!1,major:{enabled:!1}}};constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit=\"day\",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e={}){const i=t.time||(t.time={}),s=this._adapter=new Dn._date(t.adapters.date);s.init(e),x(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:Ao(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||\"day\";let{min:s,max:n,minDefined:o,maxDefined:r}=this.getUserBounds();function l(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),r||isNaN(t.max)||(n=Math.max(n,t.max))}o&&r||(l(this._getLabelBounds()),\"ticks\"===t.bounds&&\"labels\"===t.ticks.source||l(this.getMinMax(!1))),s=a(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=a(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s=\"labels\"===i.source?this.getLabelTimestamps():this._generate();\"ticks\"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=nt(s,n,this.max);return this._unit=e.unit||(i.autoSkip?To(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=Co.length-1;o>=Co.indexOf(i);o--){const i=Co[o];if(Do[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return Co[i?Co.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&\"year\"!==this._unit?function(t){for(let e=Co.indexOf(t)+1,i=Co.length;e<i;++e)if(Do[Co[e]].common)return Co[e]}(this._unit):void 0,this.initOffsets(s),t.reverse&&o.reverse(),Eo(this,o,this._majorUnit)}afterAutoSkip(){this.options.offsetAfterAutoskip&&this.initOffsets(this.ticks.map((t=>+t.value)))}initOffsets(t=[]){let e,i,s=0,n=0;this.options.offset&&t.length&&(e=this.getDecimalForValue(t[0]),s=1===t.length?1-e:(this.getDecimalForValue(t[1])-e)/2,i=this.getDecimalForValue(t[t.length-1]),n=1===t.length?i:(i-this.getDecimalForValue(t[t.length-2]))/2);const o=t.length<3?.5:.25;s=J(s,0,o),n=J(n,0,o),this._offsets={start:s,end:n,factor:1/(s+1+n)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,n=s.time,o=n.unit||To(n.minUnit,e,i,this._getLabelCapacity(e)),a=l(s.ticks.stepSize,1),r=\"week\"===o&&n.isoWeekday,h=W(r)||!0===r,c={};let d,u,f=e;if(h&&(f=+t.startOf(f,\"isoWeek\",r)),f=+t.startOf(f,h?\"day\":o),t.diff(i,e,o)>1e5*a)throw new Error(e+\" and \"+i+\" are too far apart with stepSize of \"+a+\" \"+o);const g=\"data\"===s.ticks.source&&this.getDataTimestamps();for(d=f,u=0;d<i;d=+t.add(d,a,o),u++)Lo(c,d,g);return d!==i&&\"ticks\"!==s.bounds&&1!==u||Lo(c,d,g),Object.keys(c).sort(((t,e)=>t-e)).map((t=>+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const i=this.options.time.displayFormats,s=this._unit,n=e||i[s];return this._adapter.format(t,n)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.ticks.callback;if(o)return d(o,[t,e,i],this);const a=n.time.displayFormats,r=this._unit,l=this._majorUnit,h=r&&a[r],c=l&&a[l],u=i[e],f=l&&c&&u&&u.major;return this._adapter.format(t,s||(f?c:h))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e<i;++e)s=t[e],s.label=this._tickFormatFunction(s.value,e,t)}getDecimalForValue(t){return null===t?NaN:(t-this.min)/(this.max-this.min)}getPixelForValue(t){const e=this._offsets,i=this.getDecimalForValue(t);return this.getPixelForDecimal((e.start+i)*e.factor)}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return this.min+i*(this.max-this.min)}_getLabelSize(t){const e=this.options.ticks,i=this.ctx.measureText(t).width,s=$(this.isHorizontal()?e.maxRotation:e.minRotation),n=Math.cos(s),o=Math.sin(s),a=this._resolveTickFontOptions(0).size;return{w:i*n+a*o,h:i*o+a*n}}_getLabelCapacity(t){const e=this.options.time,i=e.displayFormats,s=i[e.unit]||i.millisecond,n=this._tickFormatFunction(t,0,Eo(this,[t],this._majorUnit),s),o=this._getLabelSize(n),a=Math.floor(this.isHorizontal()?this.width/o.w:this.height/o.h)-1;return a>0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t<e;++t)i=i.concat(s[t].controller.getAllParsedValues(this));return this._cache.data=this.normalize(i)}getLabelTimestamps(){const t=this._cache.labels||[];let e,i;if(t.length)return t;const s=this.getLabels();for(e=0,i=s.length;e<i;++e)t.push(Ao(this,s[e]));return this._cache.labels=this._normalized?t:this.normalize(t)}normalize(t){return lt(t.sort(Oo))}}function Io(t,e,i){let s,n,o,a,r=0,l=t.length-1;i?(e>=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=it(t,\"pos\",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=it(t,\"time\",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}var zo=Object.freeze({__proto__:null,CategoryScale:class extends Ks{static id=\"category\";static defaults={ticks:{callback:ro}};constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if(s(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:J(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:ao(i,t,l(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);\"ticks\"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){return ro.call(this,t)}configure(){super.configure(),this.isHorizontal()||(this._reversePixels=!this._reversePixels)}getPixelForValue(t){return\"number\"!=typeof t&&(t=this.parse(t)),null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}},LinearScale:co,LogarithmicScale:bo,RadialLinearScale:Po,TimeScale:Ro,TimeSeriesScale:class extends Ro{static id=\"timeseries\";static defaults=Ro.defaults;constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Io(e,this.min),this._tableRange=Io(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o<a;++o)l=t[o],l>=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;o<a;++o)h=s[o+1],r=s[o-1],l=s[o],Math.round((h+r)/2)!==l&&n.push({time:l,pos:o/(a-1)});return n}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return t=e.length&&i.length?this.normalize(e.concat(i)):e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(Io(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return Io(this._table,i*this._tableRange+this._minPos,!0)}}});const Fo=[\"rgb(54, 162, 235)\",\"rgb(255, 99, 132)\",\"rgb(255, 159, 64)\",\"rgb(255, 205, 86)\",\"rgb(75, 192, 192)\",\"rgb(153, 102, 255)\",\"rgb(201, 203, 207)\"],Vo=Fo.map((t=>t.replace(\"rgb(\",\"rgba(\").replace(\")\",\", 0.5)\")));function Bo(t){return Fo[t%Fo.length]}function No(t){return Vo[t%Vo.length]}function Wo(t){let e=0;return(i,s)=>{const n=t.getDatasetMeta(s).controller;n instanceof zn?e=function(t,e){return t.backgroundColor=t.data.map((()=>Bo(e++))),e}(i,e):n instanceof Fn?e=function(t,e){return t.backgroundColor=t.data.map((()=>No(e++))),e}(i,e):n&&(e=function(t,e){return t.borderColor=Bo(e),t.backgroundColor=No(e),++e}(i,e))}}function Ho(t){let e;for(e in t)if(t[e].borderColor||t[e].backgroundColor)return!0;return!1}var jo={id:\"colors\",defaults:{enabled:!0,forceOverride:!1},beforeLayout(t,e,i){if(!i.enabled)return;const{data:{datasets:s},options:n}=t.config,{elements:o}=n;if(!i.forceOverride&&(Ho(s)||(a=n)&&(a.borderColor||a.backgroundColor)||o&&Ho(o)))return;var a;const r=Wo(t);s.forEach(r)}};function $o(t){if(t._decimated){const e=t._data;delete t._decimated,delete t._data,Object.defineProperty(t,\"data\",{configurable:!0,enumerable:!0,writable:!0,value:e})}}function Yo(t){t.data.datasets.forEach((t=>{$o(t)}))}var Uo={id:\"decimation\",defaults:{algorithm:\"min-max\",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void Yo(t);const n=t.width;t.data.datasets.forEach(((e,o)=>{const{_data:a,indexAxis:r}=e,l=t.getDatasetMeta(o),h=a||e.data;if(\"y\"===ki([r,t.options.indexAxis]))return;if(!l.controller.supportsDecimation)return;const c=t.scales[l.xAxisID];if(\"linear\"!==c.type&&\"time\"!==c.type)return;if(t.options.parsing)return;let{start:d,count:u}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=J(it(e,o.axis,a).lo,0,i-1)),s=h?J(it(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(l,h);if(u<=(i.threshold||4*n))return void $o(e);let f;switch(s(a)&&(e._data=h,delete e.data,Object.defineProperty(e,\"data\",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case\"lttb\":f=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;c<o-2;c++){let s,n=0,o=0;const h=Math.floor((c+1)*r)+1+e,m=Math.min(Math.floor((c+2)*r)+1,i)+e,b=m-h;for(s=h;s<m;s++)n+=t[s].x,o+=t[s].y;n/=b,o/=b;const x=Math.floor(c*r)+1+e,_=Math.min(Math.floor((c+1)*r)+1,i)+e,{x:y,y:v}=t[p];for(u=f=-1,s=x;s<_;s++)f=.5*Math.abs((y-n)*(t[s].y-v)-(y-t[s].x)*(o-v)),f>u&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(h,d,u,n,i);break;case\"min-max\":f=function(t,e,i,n){let o,a,r,l,h,c,d,u,f,g,p=0,m=0;const b=[],x=e+i-1,_=t[e].x,y=t[x].x-_;for(o=e;o<e+i;++o){a=t[o],r=(a.x-_)/y*n,l=a.y;const e=0|r;if(e===h)l<f?(f=l,c=o):l>g&&(g=l,d=o),p=(m*p+a.x)/++m;else{const i=o-1;if(!s(c)&&!s(d)){const e=Math.min(c,d),s=Math.max(c,d);e!==u&&e!==i&&b.push({...t[e],x:p}),s!==u&&s!==i&&b.push({...t[s],x:p})}o>0&&i!==u&&b.push(t[i]),b.push(a),h=e,m=0,f=g=l,c=d=u=o}}return b}(h,d,u,n);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=f}))},destroy(t){Yo(t)}};function Xo(t,e,i,s){if(s)return;let n=e[t],o=i[t];return\"angle\"===t&&(n=G(n),o=G(o)),{property:t,start:n,end:o}}function qo(t,e,i){for(;e>t;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function Ko(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function Go(t,e){let i=[],s=!1;return n(t)?(s=!0,i=t):i=function(t,e){const{x:i=null,y:s=null}=t||{},n=e.points,o=[];return e.segments.forEach((({start:t,end:e})=>{e=qo(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new Zn({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function Zo(t){return t&&!1!==t.fill}function Jo(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!a(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function Qo(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=l(i&&i.target,i);void 0===s&&(s=!!e.backgroundColor);if(!1===s||null===s)return!1;if(!0===s)return\"origin\";return s}(t);if(o(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return a(n)&&Math.floor(n)===n?function(t,e,i,s){\"-\"!==t&&\"+\"!==t||(i=e+i);if(i===e||i<0||i>=s)return!1;return i}(s[0],e,n,i):[\"origin\",\"start\",\"end\",\"stack\",\"shape\"].indexOf(s)>=0&&s}function ta(t,e,i){const s=[];for(let n=0;n<i.length;n++){const o=i[n],{first:a,last:r,point:l}=ea(o,e,\"x\");if(!(!l||a&&r))if(a)s.unshift(l);else if(t.push(l),!r)break}t.push(...s)}function ea(t,e,i){const s=t.interpolate(e,i);if(!s)return{};const n=s[i],o=t.segments,a=t.points;let r=!1,l=!1;for(let t=0;t<o.length;t++){const e=o[t],s=a[e.start][i],h=a[e.end][i];if(tt(n,s,h)){r=n===s,l=n===h;break}}return{first:r,last:l,point:s}}class ia{constructor(t){this.x=t.x,this.y=t.y,this.radius=t.radius}pathSegment(t,e,i){const{x:s,y:n,radius:o}=this;return e=e||{start:0,end:O},t.arc(s,n,o,e.end,e.start,!0),!i.bounds}interpolate(t){const{x:e,y:i,radius:s}=this,n=t.angle;return{x:e+Math.cos(n)*s,y:i+Math.sin(n)*s,angle:n}}}function sa(t){const{chart:e,fill:i,line:s}=t;if(a(i))return function(t,e){const i=t.getDatasetMeta(e);return i&&t.isDatasetVisible(e)?i.dataset:null}(e,i);if(\"stack\"===i)return function(t){const{scale:e,index:i,line:s}=t,n=[],o=s.segments,a=s.points,r=function(t,e){const i=[],s=t.getMatchingVisibleMetas(\"line\");for(let t=0;t<s.length;t++){const n=s[t];if(n.index===e)break;n.hidden||i.unshift(n.dataset)}return i}(e,i);r.push(Go({x:null,y:e.bottom},s));for(let t=0;t<o.length;t++){const e=o[t];for(let t=e.start;t<=e.end;t++)ta(n,a[t],r)}return new Zn({points:n,options:{}})}(t);if(\"shape\"===i)return!0;const n=function(t){if((t.scale||{}).getPointPositionForValue)return function(t){const{scale:e,fill:i}=t,s=e.options,n=e.getLabels().length,a=s.reverse?e.max:e.min,r=function(t,e,i){let s;return s=\"start\"===t?i:\"end\"===t?e.options.reverse?e.min:e.max:o(t)?t.value:e.getBaseValue(),s}(i,e,a),l=[];if(s.grid.circular){const t=e.getPointPositionForValue(0,a);return new ia({x:t.x,y:t.y,radius:e.getDistanceFromCenterForValue(r)})}for(let t=0;t<n;++t)l.push(e.getPointPositionForValue(t,r));return l}(t);return function(t){const{scale:e={},fill:i}=t,s=function(t,e){let i=null;return\"start\"===t?i=e.bottom:\"end\"===t?i=e.top:o(t)?i=e.getPixelForValue(t.value):e.getBasePixel&&(i=e.getBasePixel()),i}(i,e);if(a(s)){const t=e.isHorizontal();return{x:t?s:null,y:t?null:s}}return null}(t)}(t);return n instanceof ia?n:Go(n,s)}function na(t,e,i){const s=sa(e),{line:n,scale:o,axis:a}=e,r=n.options,l=r.fill,h=r.backgroundColor,{above:c=h,below:d=h}=l||{};s&&n.points.length&&(Re(t,i),function(t,e){const{line:i,target:s,above:n,below:o,area:a,scale:r}=e,l=i._loop?\"angle\":e.axis;t.save(),\"x\"===l&&o!==n&&(oa(t,s,a.top),aa(t,{line:i,target:s,color:n,scale:r,property:l}),t.restore(),t.save(),oa(t,s,a.bottom));aa(t,{line:i,target:s,color:o,scale:r,property:l}),t.restore()}(t,{line:n,target:s,above:c,below:d,area:i,scale:o,axis:a}),Ie(t))}function oa(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[qo(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(h.x,i),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(c.x,i)}t.lineTo(e.first().x,i),t.closePath(),t.clip()}function aa(t,e){const{line:i,target:s,property:n,color:o,scale:a}=e,r=function(t,e,i){const s=t.segments,n=t.points,o=e.points,a=[];for(const t of s){let{start:s,end:r}=t;r=qo(s,r,n);const l=Xo(i,n[s],n[r],t.loop);if(!e.segments){a.push({source:t,target:l,start:n[s],end:n[r]});continue}const h=Ei(e,l);for(const e of h){const s=Xo(i,o[e.start],o[e.end],e.loop),r=Li(t,n,s);for(const t of r)a.push({source:t,target:e,start:{[i]:Ko(l,s,\"start\",Math.max)},end:{[i]:Ko(l,s,\"end\",Math.min)}})}}return a}(i,s,n);for(const{source:e,target:l,start:h,end:c}of r){const{style:{backgroundColor:r=o}={}}=e,d=!0!==s;t.save(),t.fillStyle=r,ra(t,a,d&&Xo(n,h,c)),t.beginPath();const u=!!i.pathSegment(t,e);let f;if(d){u?t.closePath():la(t,s,c,n);const e=!!s.pathSegment(t,l,{move:u,reverse:!0});f=u&&e,f||la(t,s,h,n)}t.closePath(),t.fill(f?\"evenodd\":\"nonzero\"),t.restore()}}function ra(t,e,i){const{top:s,bottom:n}=e.chart.chartArea,{property:o,start:a,end:r}=i||{};\"x\"===o&&(t.beginPath(),t.rect(a,s,r-a,n-s),t.clip())}function la(t,e,i,s){const n=e.interpolate(i,s);n&&t.lineTo(n.x,n.y)}var ha={id:\"filler\",afterDatasetsUpdate(t,e,i){const s=(t.data.datasets||[]).length,n=[];let o,a,r,l;for(a=0;a<s;++a)o=t.getDatasetMeta(a),r=o.dataset,l=null,r&&r.options&&r instanceof Zn&&(l={visible:t.isDatasetVisible(a),index:a,fill:Qo(r,a,s),chart:t,axis:o.controller.options.indexAxis,scale:o.vScale,line:r}),o.$filler=l,n.push(l);for(a=0;a<s;++a)l=n[a],l&&!1!==l.fill&&(l.fill=Jo(n,a,i.propagate))},beforeDraw(t,e,i){const s=\"beforeDraw\"===i.drawTime,n=t.getSortedVisibleDatasetMetas(),o=t.chartArea;for(let e=n.length-1;e>=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&i.fill&&na(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if(\"beforeDatasetsDraw\"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;Zo(i)&&na(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;Zo(s)&&\"beforeDatasetDraw\"===i.drawTime&&na(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:\"beforeDatasetDraw\"}};const ca=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=t.pointStyleWidth||Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class da extends Bs{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=d(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=wi(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=ca(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,s,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign=\"left\",n.textBaseline=\"middle\";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const{itemWidth:p,itemHeight:m}=function(t,e,i,s,n){const o=function(t,e,i,s){let n=t.text;n&&\"string\"!=typeof n&&(n=n.reduce(((t,e)=>t.length>e.length?t:e)));return e+i.size/2+s.measureText(n).width}(s,t,e,i),a=function(t,e,i){let s=t;\"string\"!=typeof e.text&&(s=ua(e,i));return s}(n,s,e.lineHeight);return{itemWidth:o,itemHeight:a}}(i,e,n,t,s);o>0&&u+m+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:m},d=Math.max(d,p),u+=m+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:n}}=this,o=Di(n,this.left,this.width);if(this.isHorizontal()){let n=0,a=ft(i,this.left+s,this.right-this.lineWidths[n]);for(const r of e)n!==r.row&&(n=r.row,a=ft(i,this.left+s,this.right-this.lineWidths[n])),r.top+=this.top+t+s,r.left=o.leftForLtr(o.x(a),r.width),a+=r.width+s}else{let n=0,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height);for(const r of e)r.col!==n&&(n=r.col,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height)),r.top=a,r.left+=this.left+s,r.left=o.leftForLtr(o.x(r.left),r.width),a+=r.height+s}}isHorizontal(){return\"top\"===this.options.position||\"bottom\"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Re(t,this),this._draw(),Ie(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:n,labels:o}=t,a=ue.color,r=Di(t.rtl,this.left,this.width),h=wi(o.font),{padding:c}=o,d=h.size,u=d/2;let f;this.drawTitle(),s.textAlign=r.textAlign(\"left\"),s.textBaseline=\"middle\",s.lineWidth=.5,s.font=h.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=ca(o,d),b=this.isHorizontal(),x=this._computeTitleHeight();f=b?{x:ft(n,this.left+c,this.right-i[0]),y:this.top+c+x,line:0}:{x:this.left+c,y:ft(n,this.top+x+c,this.bottom-e[0].height),line:0},Ci(this.ctx,t.textDirection);const _=m+c;this.legendItems.forEach(((y,v)=>{s.strokeStyle=y.fontColor,s.fillStyle=y.fontColor;const M=s.measureText(y.text).width,w=r.textAlign(y.textAlign||(y.textAlign=o.textAlign)),k=g+u+M;let S=f.x,P=f.y;r.setWidth(this.width),b?v>0&&S+k+c>this.right&&(P=f.y+=_,f.line++,S=f.x=ft(n,this.left+c,this.right-i[f.line])):v>0&&P+_>this.bottom&&(S=f.x=S+e[f.line].width+c,f.line++,P=f.y=ft(n,this.top+x+c,this.bottom-e[f.line].height));if(function(t,e,i){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const n=l(i.lineWidth,1);if(s.fillStyle=l(i.fillStyle,a),s.lineCap=l(i.lineCap,\"butt\"),s.lineDashOffset=l(i.lineDashOffset,0),s.lineJoin=l(i.lineJoin,\"miter\"),s.lineWidth=n,s.strokeStyle=l(i.strokeStyle,a),s.setLineDash(l(i.lineDash,[])),o.usePointStyle){const a={radius:p*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},l=r.xPlus(t,g/2);Le(s,a,l,e+u,o.pointStyleWidth&&g)}else{const o=e+Math.max((d-p)/2,0),a=r.leftForLtr(t,g),l=vi(i.borderRadius);s.beginPath(),Object.values(l).some((t=>0!==t))?We(s,{x:a,y:o,w:g,h:p,radius:l}):s.rect(a,o,g,p),s.fill(),0!==n&&s.stroke()}s.restore()}(r.x(S),P,y),S=gt(w,S+g+u,b?S+k:this.right,t.rtl),function(t,e,i){Ve(s,i.text,t,e+m/2,h,{strikethrough:i.hidden,textAlign:r.textAlign(i.textAlign)})}(r.x(S),P,y),b)f.x+=k+c;else if(\"string\"!=typeof y.text){const t=h.lineHeight;f.y+=ua(y,t)}else f.y+=_})),Oi(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=wi(e.font),s=Mi(e.padding);if(!e.display)return;const n=Di(t.rtl,this.left,this.width),o=this.ctx,a=e.position,r=i.size/2,l=s.top+r;let h,c=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),h=this.top+l,c=ft(t.align,c,this.right-d);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);h=l+ft(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const u=ft(a,c,c+d);o.textAlign=n.textAlign(ut(a)),o.textBaseline=\"middle\",o.strokeStyle=e.color,o.fillStyle=e.color,o.font=i.string,Ve(o,e.text,u,h,i)}_computeTitleHeight(){const t=this.options.title,e=wi(t.font),i=Mi(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(tt(t,this.left,this.right)&&tt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;i<n.length;++i)if(s=n[i],tt(t,s.left,s.left+s.width)&&tt(e,s.top,s.top+s.height))return this.legendItems[i];return null}handleEvent(t){const e=this.options;if(!function(t,e){if((\"mousemove\"===t||\"mouseout\"===t)&&(e.onHover||e.onLeave))return!0;if(e.onClick&&(\"click\"===t||\"mouseup\"===t))return!0;return!1}(t.type,e))return;const i=this._getLegendItemAt(t.x,t.y);if(\"mousemove\"===t.type||\"mouseout\"===t.type){const o=this._hoveredItem,a=(n=i,null!==(s=o)&&null!==n&&s.datasetIndex===n.datasetIndex&&s.index===n.index);o&&!a&&d(e.onLeave,[t,o,this],this),this._hoveredItem=i,i&&!a&&d(e.onHover,[t,i,this],this)}else i&&d(e.onClick,[t,i,this],this);var s,n}}function ua(t,e){return e*(t.text?t.text.length+.5:0)}var fa={id:\"legend\",_element:da,start(t,e,i){const s=t.legend=new da({ctx:t.ctx,options:i,chart:t});ns.configure(t,s,i),ns.addBox(t,s)},stop(t){ns.removeBox(t,t.legend),delete t.legend},beforeUpdate(t,e,i){const s=t.legend;ns.configure(t,s,i),s.options=i},afterUpdate(t){const e=t.legend;e.buildLabels(),e.adjustHitBoxes()},afterEvent(t,e){e.replay||t.legend.handleEvent(e.event)},defaults:{display:!0,position:\"top\",align:\"center\",fullSize:!0,reverse:!1,weight:1e3,onClick(t,e,i){const s=e.datasetIndex,n=i.chart;n.isDatasetVisible(s)?(n.hide(s),e.hidden=!0):(n.show(s),e.hidden=!1)},onHover:null,onLeave:null,labels:{color:t=>t.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const l=t.controller.getStyle(i?0:void 0),h=Mi(l.borderWidth);return{text:e[t.index].label,fillStyle:l.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:l.borderCapStyle,lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:l.borderColor,pointStyle:s||l.pointStyle,rotation:l.rotation,textAlign:n||l.textAlign,borderRadius:a&&(r||l.borderRadius),datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:\"center\",text:\"\"}},descriptors:{_scriptable:t=>!t.startsWith(\"on\"),labels:{_scriptable:t=>![\"generateLabels\",\"filter\",\"sort\"].includes(t)}}};class ga extends Bs{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=n(i.text)?i.text.length:1;this._padding=Mi(i.padding);const o=s*wi(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return\"top\"===t||\"bottom\"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:n,options:o}=this,a=o.align;let r,l,h,c=0;return this.isHorizontal()?(l=ft(a,i,n),h=e+t,r=n-i):(\"left\"===o.position?(l=i+t,h=ft(a,s,e),c=-.5*C):(l=n-t,h=ft(a,e,s),c=.5*C),r=s-e),{titleX:l,titleY:h,maxWidth:r,rotation:c}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=wi(e.font),s=i.lineHeight/2+this._padding.top,{titleX:n,titleY:o,maxWidth:a,rotation:r}=this._drawArgs(s);Ve(t,e.text,0,0,i,{color:e.color,maxWidth:a,rotation:r,textAlign:ut(e.align),textBaseline:\"middle\",translation:[n,o]})}}var pa={id:\"title\",_element:ga,start(t,e,i){!function(t,e){const i=new ga({ctx:t.ctx,options:e,chart:t});ns.configure(t,i,e),ns.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ns.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ns.configure(t,s,i),s.options=i},defaults:{align:\"center\",display:!1,font:{weight:\"bold\"},fullSize:!0,padding:10,position:\"top\",text:\"\",weight:2e3},defaultRoutes:{color:\"color\"},descriptors:{_scriptable:!0,_indexable:!1}};const ma=new WeakMap;var ba={id:\"subtitle\",start(t,e,i){const s=new ga({ctx:t.ctx,options:i,chart:t});ns.configure(t,s,i),ns.addBox(t,s),ma.set(t,s)},stop(t){ns.removeBox(t,ma.get(t)),ma.delete(t)},beforeUpdate(t,e,i){const s=ma.get(t);ns.configure(t,s,i),s.options=i},defaults:{align:\"center\",display:!1,font:{weight:\"normal\"},fullSize:!0,padding:0,position:\"top\",text:\"\",weight:1500},defaultRoutes:{color:\"color\"},descriptors:{_scriptable:!0,_indexable:!1}};const xa={average(t){if(!t.length)return!1;let e,i,s=0,n=0,o=0;for(e=0,i=t.length;e<i;++e){const i=t[e].element;if(i&&i.hasValue()){const t=i.tooltipPosition();s+=t.x,n+=t.y,++o}}return{x:s/o,y:n/o}},nearest(t,e){if(!t.length)return!1;let i,s,n,o=e.x,a=e.y,r=Number.POSITIVE_INFINITY;for(i=0,s=t.length;i<s;++i){const s=t[i].element;if(s&&s.hasValue()){const t=q(e,s.getCenterPoint());t<r&&(r=t,n=s)}}if(n){const t=n.tooltipPosition();o=t.x,a=t.y}return{x:o,y:a}}};function _a(t,e){return e&&(n(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function ya(t){return(\"string\"==typeof t||t instanceof String)&&t.indexOf(\"\\n\")>-1?t.split(\"\\n\"):t}function va(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Ma(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=wi(e.bodyFont),h=wi(e.titleFont),c=wi(e.footerFont),d=o.length,f=n.length,g=s.length,p=Mi(e.padding);let m=p.height,b=0,x=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(x+=t.beforeBody.length+t.afterBody.length,d&&(m+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),x){m+=g*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(x-g)*l.lineHeight+(x-1)*e.bodySpacing}f&&(m+=e.footerMarginTop+f*c.lineHeight+(f-1)*e.footerSpacing);let _=0;const y=function(t){b=Math.max(b,i.measureText(t).width+_)};return i.save(),i.font=h.string,u(t.title,y),i.font=l.string,u(t.beforeBody.concat(t.afterBody),y),_=e.displayColors?a+2+e.boxPadding:0,u(s,(t=>{u(t.before,y),u(t.lines,y),u(t.after,y)})),_=0,i.font=c.string,u(t.footer,y),i.restore(),b+=p.width,{width:b,height:m}}function wa(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h=\"center\";return\"center\"===s?h=n<=(r+l)/2?\"left\":\"right\":n<=o/2?h=\"left\":n>=a-o/2&&(h=\"right\"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return\"left\"===t&&n+o+a>e.width||\"right\"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h=\"center\"),h}function ka(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return i<s/2?\"top\":i>t.height-s/2?\"bottom\":\"center\"}(t,i);return{xAlign:i.xAlign||e.xAlign||wa(t,e,i,s),yAlign:s}}function Sa(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=vi(a);let g=function(t,e){let{x:i,width:s}=t;return\"right\"===e?i-=s:\"center\"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return\"top\"===e?s+=i:s-=\"bottom\"===e?n+i:n/2,s}(e,l,h);return\"center\"===l?\"left\"===r?g+=h:\"right\"===r&&(g-=h):\"left\"===r?g-=Math.max(c,u)+n:\"right\"===r&&(g+=Math.max(d,f)+n),{x:J(g,0,s.width-e.width),y:J(p,0,s.height-e.height)}}function Pa(t,e,i){const s=Mi(i.padding);return\"center\"===e?t.x+t.width/2:\"right\"===e?t.x+t.width-s.right:t.x+s.left}function Da(t){return _a([],ya(t))}function Ca(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}const Oa={beforeTitle:e,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&\"dataset\"===this.options.mode)return e.dataset.label||\"\";if(e.label)return e.label;if(s>0&&e.dataIndex<s)return i[e.dataIndex]}return\"\"},afterTitle:e,beforeBody:e,beforeLabel:e,label(t){if(this&&this.options&&\"dataset\"===this.options.mode)return t.label+\": \"+t.formattedValue||t.formattedValue;let e=t.dataset.label||\"\";e&&(e+=\": \");const i=t.formattedValue;return s(i)||(e+=i),e},labelColor(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{borderColor:e.borderColor,backgroundColor:e.backgroundColor,borderWidth:e.borderWidth,borderDash:e.borderDash,borderDashOffset:e.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{pointStyle:e.pointStyle,rotation:e.rotation}},afterLabel:e,afterBody:e,beforeFooter:e,footer:e,afterFooter:e};function Aa(t,e,i,s){const n=t[e].call(i,s);return void 0===n?Oa[e].call(i,s):n}class Ta extends Bs{static positioners=xa;constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,n=new Ps(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(n)),n}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,i=this._tooltipItems,Pi(t,{tooltip:e,tooltipItems:i,type:\"tooltip\"})));var t,e,i}getTitle(t,e){const{callbacks:i}=e,s=Aa(i,\"beforeTitle\",this,t),n=Aa(i,\"title\",this,t),o=Aa(i,\"afterTitle\",this,t);let a=[];return a=_a(a,ya(s)),a=_a(a,ya(n)),a=_a(a,ya(o)),a}getBeforeBody(t,e){return Da(Aa(e.callbacks,\"beforeBody\",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return u(t,(t=>{const e={before:[],lines:[],after:[]},n=Ca(i,t);_a(e.before,ya(Aa(n,\"beforeLabel\",this,t))),_a(e.lines,Aa(n,\"label\",this,t)),_a(e.after,ya(Aa(n,\"afterLabel\",this,t))),s.push(e)})),s}getAfterBody(t,e){return Da(Aa(e.callbacks,\"afterBody\",this,t))}getFooter(t,e){const{callbacks:i}=e,s=Aa(i,\"beforeFooter\",this,t),n=Aa(i,\"footer\",this,t),o=Aa(i,\"afterFooter\",this,t);let a=[];return a=_a(a,ya(s)),a=_a(a,ya(n)),a=_a(a,ya(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;a<r;++a)l.push(va(this.chart,e[a]));return t.filter&&(l=l.filter(((e,s,n)=>t.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),u(l,(e=>{const i=Ca(t.callbacks,e);s.push(Aa(i,\"labelColor\",this,e)),n.push(Aa(i,\"labelPointStyle\",this,e)),o.push(Aa(i,\"labelTextColor\",this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=xa[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Ma(this,i),a=Object.assign({},t,e),r=ka(this.chart,i,a),l=Sa(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=vi(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,b,x,_,y;return\"center\"===n?(_=u+g/2,\"left\"===s?(p=d,m=p-o,x=_+o,y=_-o):(p=d+f,m=p+o,x=_-o,y=_+o),b=p):(m=\"left\"===s?d+Math.max(r,h)+o:\"right\"===s?d+f-Math.max(l,c)-o:this.caretX,\"top\"===n?(x=u,_=x-o,p=m-o,b=m+o):(x=u+g,_=x+o,p=m+o,b=m-o),y=x),{x1:p,x2:m,x3:b,y1:x,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Di(i.rtl,this.x,this.width);for(t.x=Pa(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline=\"middle\",o=wi(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r<n;++r)e.fillText(s[r],l.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+a,r+1===n&&(t.y+=i.titleMarginBottom-a)}}_drawColorBox(t,e,i,s,n){const a=this.labelColors[i],r=this.labelPointStyles[i],{boxHeight:l,boxWidth:h,boxPadding:c}=n,d=wi(n.bodyFont),u=Pa(this,\"left\",n),f=s.x(u),g=l<d.lineHeight?(d.lineHeight-l)/2:0,p=e.y+g;if(n.usePointStyle){const e={radius:Math.min(h,l)/2,pointStyle:r.pointStyle,rotation:r.rotation,borderWidth:1},i=s.leftForLtr(f,h)+h/2,o=p+l/2;t.strokeStyle=n.multiKeyBackground,t.fillStyle=n.multiKeyBackground,Te(t,e,i,o),t.strokeStyle=a.borderColor,t.fillStyle=a.backgroundColor,Te(t,e,i,o)}else{t.lineWidth=o(a.borderWidth)?Math.max(...Object.values(a.borderWidth)):a.borderWidth||1,t.strokeStyle=a.borderColor,t.setLineDash(a.borderDash||[]),t.lineDashOffset=a.borderDashOffset||0;const e=s.leftForLtr(f,h-c),i=s.leftForLtr(s.xPlus(f,1),h-c-2),r=vi(a.borderRadius);Object.values(r).some((t=>0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,We(t,{x:e,y:p,w:h,h:l,radius:r}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),We(t,{x:i,y:p+1,w:h-2,h:l-2,radius:r}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,p,h,l),t.strokeRect(e,p,h,l),t.fillStyle=a.backgroundColor,t.fillRect(i,p+1,h-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=wi(i.bodyFont);let d=c.lineHeight,f=0;const g=Di(i.rtl,this.x,this.width),p=function(i){e.fillText(i,g.x(t.x+f),t.y+d/2),t.y+=d+n},m=g.textAlign(o);let b,x,_,y,v,M,w;for(e.textAlign=o,e.textBaseline=\"middle\",e.font=c.string,t.x=Pa(this,m,i),e.fillStyle=i.bodyColor,u(this.beforeBody,p),f=a&&\"right\"!==m?\"center\"===o?l/2+h:l+2+h:0,y=0,M=s.length;y<M;++y){for(b=s[y],x=this.labelTextColors[y],e.fillStyle=x,u(b.before,p),_=b.lines,a&&_.length&&(this._drawColorBox(e,t,y,g,i),d=Math.max(c.lineHeight,r)),v=0,w=_.length;v<w;++v)p(_[v]),d=c.lineHeight;u(b.after,p)}f=0,d=c.lineHeight,u(this.afterBody,p),t.y-=n}drawFooter(t,e,i){const s=this.footer,n=s.length;let o,a;if(n){const r=Di(i.rtl,this.x,this.width);for(t.x=Pa(this,i.footerAlign,i),t.y+=i.footerMarginTop,e.textAlign=r.textAlign(i.footerAlign),e.textBaseline=\"middle\",o=wi(i.footerFont),e.fillStyle=i.footerColor,e.font=o.string,a=0;a<n;++a)e.fillText(s[a],r.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+i.footerSpacing}}drawBackground(t,e,i,s){const{xAlign:n,yAlign:o}=this,{x:a,y:r}=t,{width:l,height:h}=i,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=vi(s.cornerRadius);e.fillStyle=s.backgroundColor,e.strokeStyle=s.borderColor,e.lineWidth=s.borderWidth,e.beginPath(),e.moveTo(a+c,r),\"top\"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+l-d,r),e.quadraticCurveTo(a+l,r,a+l,r+d),\"center\"===o&&\"right\"===n&&this.drawCaret(t,e,i,s),e.lineTo(a+l,r+h-f),e.quadraticCurveTo(a+l,r+h,a+l-f,r+h),\"bottom\"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+u,r+h),e.quadraticCurveTo(a,r+h,a,r+h-u),\"center\"===o&&\"left\"===n&&this.drawCaret(t,e,i,s),e.lineTo(a,r+c),e.quadraticCurveTo(a,r,a+c,r),e.closePath(),e.fill(),s.borderWidth>0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=xa[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Ma(this,t),a=Object.assign({},i,this._size),r=ka(e,t,a),l=Sa(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=Mi(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(n,t,s,e),Ci(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),Oi(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error(\"Cannot find a dataset at index \"+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!f(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!f(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if(\"mouseout\"===t.type)return[];if(!s)return e;const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=xa[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}var La={id:\"tooltip\",_element:Ta,positioners:xa,afterInit(t,e,i){i&&(t.tooltip=new Ta({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip;if(e&&e._willRender()){const i={tooltip:e};if(!1===t.notifyPlugins(\"beforeTooltipDraw\",{...i,cancelable:!0}))return;e.draw(t.ctx),t.notifyPlugins(\"afterTooltipDraw\",i)}},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:\"average\",backgroundColor:\"rgba(0,0,0,0.8)\",titleColor:\"#fff\",titleFont:{weight:\"bold\"},titleSpacing:2,titleMarginBottom:6,titleAlign:\"left\",bodyColor:\"#fff\",bodySpacing:2,bodyFont:{},bodyAlign:\"left\",footerColor:\"#fff\",footerSpacing:2,footerMarginTop:6,footerFont:{weight:\"bold\"},footerAlign:\"left\",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:\"#fff\",displayColors:!0,boxPadding:0,borderColor:\"rgba(0,0,0,0)\",borderWidth:0,animation:{duration:400,easing:\"easeOutQuart\"},animations:{numbers:{type:\"number\",properties:[\"x\",\"y\",\"width\",\"height\",\"caretX\",\"caretY\"]},opacity:{easing:\"linear\",duration:200}},callbacks:Oa},defaultRoutes:{bodyFont:\"font\",footerFont:\"font\",titleFont:\"font\"},descriptors:{_scriptable:t=>\"filter\"!==t&&\"itemSort\"!==t&&\"external\"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:\"animation\"}},additionalOptionScopes:[\"interaction\"]};return wn.register(Vn,zo,oo,t),wn.helpers={...Vi},wn._adapters=Dn,wn.Animation=Ss,wn.Animations=Ps,wn.animator=xt,wn.controllers=Js.controllers.items,wn.DatasetController=Vs,wn.Element=Bs,wn.elements=oo,wn.Interaction=Yi,wn.layouts=ns,wn.platforms=Ms,wn.Scale=Ks,wn.Ticks=ae,Object.assign(wn,Vn,zo,oo,t,Ms),wn.Chart=wn,\"undefined\"!=typeof window&&(window.Chart=wn),wn}));\n//# sourceMappingURL=chart.umd.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/chunks/helpers.segment.cjs",
    "content": "/*!\n * Chart.js v4.2.1\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\n'use strict';\n\nvar color$1 = require('@kurkle/color');\n\n/**\n * @namespace Chart.helpers\n */ /**\n * An empty function that can be used, for example, for optional callback.\n */ function noop() {\n/* noop */ }\n/**\n * Returns a unique id, sequentially generated from a global variable.\n */ const uid = (()=>{\n    let id = 0;\n    return ()=>id++;\n})();\n/**\n * Returns true if `value` is neither null nor undefined, else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */ function isNullOrUndef(value) {\n    return value === null || typeof value === 'undefined';\n}\n/**\n * Returns true if `value` is an array (including typed arrays), else returns false.\n * @param value - The value to test.\n * @function\n */ function isArray(value) {\n    if (Array.isArray && Array.isArray(value)) {\n        return true;\n    }\n    const type = Object.prototype.toString.call(value);\n    if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {\n        return true;\n    }\n    return false;\n}\n/**\n * Returns true if `value` is an object (excluding null), else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */ function isObject(value) {\n    return value !== null && Object.prototype.toString.call(value) === '[object Object]';\n}\n/**\n * Returns true if `value` is a finite number, else returns false\n * @param value  - The value to test.\n */ function isNumberFinite(value) {\n    return (typeof value === 'number' || value instanceof Number) && isFinite(+value);\n}\n/**\n * Returns `value` if finite, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is not finite.\n */ function finiteOrDefault(value, defaultValue) {\n    return isNumberFinite(value) ? value : defaultValue;\n}\n/**\n * Returns `value` if defined, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is undefined.\n */ function valueOrDefault(value, defaultValue) {\n    return typeof value === 'undefined' ? defaultValue : value;\n}\nconst toPercentage = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 : +value / dimension;\nconst toDimension = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value;\n/**\n * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n * @param fn - The function to call.\n * @param args - The arguments with which `fn` should be called.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n */ function callback(fn, args, thisArg) {\n    if (fn && typeof fn.call === 'function') {\n        return fn.apply(thisArg, args);\n    }\n}\nfunction each(loopable, fn, thisArg, reverse) {\n    let i, len, keys;\n    if (isArray(loopable)) {\n        len = loopable.length;\n        if (reverse) {\n            for(i = len - 1; i >= 0; i--){\n                fn.call(thisArg, loopable[i], i);\n            }\n        } else {\n            for(i = 0; i < len; i++){\n                fn.call(thisArg, loopable[i], i);\n            }\n        }\n    } else if (isObject(loopable)) {\n        keys = Object.keys(loopable);\n        len = keys.length;\n        for(i = 0; i < len; i++){\n            fn.call(thisArg, loopable[keys[i]], keys[i]);\n        }\n    }\n}\n/**\n * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n * @param a0 - The array to compare\n * @param a1 - The array to compare\n * @private\n */ function _elementsEqual(a0, a1) {\n    let i, ilen, v0, v1;\n    if (!a0 || !a1 || a0.length !== a1.length) {\n        return false;\n    }\n    for(i = 0, ilen = a0.length; i < ilen; ++i){\n        v0 = a0[i];\n        v1 = a1[i];\n        if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {\n            return false;\n        }\n    }\n    return true;\n}\n/**\n * Returns a deep copy of `source` without keeping references on objects and arrays.\n * @param source - The value to clone.\n */ function clone(source) {\n    if (isArray(source)) {\n        return source.map(clone);\n    }\n    if (isObject(source)) {\n        const target = Object.create(null);\n        const keys = Object.keys(source);\n        const klen = keys.length;\n        let k = 0;\n        for(; k < klen; ++k){\n            target[keys[k]] = clone(source[keys[k]]);\n        }\n        return target;\n    }\n    return source;\n}\nfunction isValidKey(key) {\n    return [\n        '__proto__',\n        'prototype',\n        'constructor'\n    ].indexOf(key) === -1;\n}\n/**\n * The default merger when Chart.helpers.merge is called without merger option.\n * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.\n * @private\n */ function _merger(key, target, source, options) {\n    if (!isValidKey(key)) {\n        return;\n    }\n    const tval = target[key];\n    const sval = source[key];\n    if (isObject(tval) && isObject(sval)) {\n        // eslint-disable-next-line @typescript-eslint/no-use-before-define\n        merge(tval, sval, options);\n    } else {\n        target[key] = clone(sval);\n    }\n}\nfunction merge(target, source, options) {\n    const sources = isArray(source) ? source : [\n        source\n    ];\n    const ilen = sources.length;\n    if (!isObject(target)) {\n        return target;\n    }\n    options = options || {};\n    const merger = options.merger || _merger;\n    let current;\n    for(let i = 0; i < ilen; ++i){\n        current = sources[i];\n        if (!isObject(current)) {\n            continue;\n        }\n        const keys = Object.keys(current);\n        for(let k = 0, klen = keys.length; k < klen; ++k){\n            merger(keys[k], target, current, options);\n        }\n    }\n    return target;\n}\nfunction mergeIf(target, source) {\n    // eslint-disable-next-line @typescript-eslint/no-use-before-define\n    return merge(target, source, {\n        merger: _mergerIf\n    });\n}\n/**\n * Merges source[key] in target[key] only if target[key] is undefined.\n * @private\n */ function _mergerIf(key, target, source) {\n    if (!isValidKey(key)) {\n        return;\n    }\n    const tval = target[key];\n    const sval = source[key];\n    if (isObject(tval) && isObject(sval)) {\n        mergeIf(tval, sval);\n    } else if (!Object.prototype.hasOwnProperty.call(target, key)) {\n        target[key] = clone(sval);\n    }\n}\n/**\n * @private\n */ function _deprecated(scope, value, previous, current) {\n    if (value !== undefined) {\n        console.warn(scope + ': \"' + previous + '\" is deprecated. Please use \"' + current + '\" instead');\n    }\n}\n// resolveObjectKey resolver cache\nconst keyResolvers = {\n    // Chart.helpers.core resolveObjectKey should resolve empty key to root object\n    '': (v)=>v,\n    // default resolvers\n    x: (o)=>o.x,\n    y: (o)=>o.y\n};\n/**\n * @private\n */ function _splitKey(key) {\n    const parts = key.split('.');\n    const keys = [];\n    let tmp = '';\n    for (const part of parts){\n        tmp += part;\n        if (tmp.endsWith('\\\\')) {\n            tmp = tmp.slice(0, -1) + '.';\n        } else {\n            keys.push(tmp);\n            tmp = '';\n        }\n    }\n    return keys;\n}\nfunction _getKeyResolver(key) {\n    const keys = _splitKey(key);\n    return (obj)=>{\n        for (const k of keys){\n            if (k === '') {\n                break;\n            }\n            obj = obj && obj[k];\n        }\n        return obj;\n    };\n}\nfunction resolveObjectKey(obj, key) {\n    const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));\n    return resolver(obj);\n}\n/**\n * @private\n */ function _capitalize(str) {\n    return str.charAt(0).toUpperCase() + str.slice(1);\n}\nconst defined = (value)=>typeof value !== 'undefined';\nconst isFunction = (value)=>typeof value === 'function';\n// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384\nconst setsEqual = (a, b)=>{\n    if (a.size !== b.size) {\n        return false;\n    }\n    for (const item of a){\n        if (!b.has(item)) {\n            return false;\n        }\n    }\n    return true;\n};\n/**\n * @param e - The event\n * @private\n */ function _isClickEvent(e) {\n    return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';\n}\n\n/**\n * @alias Chart.helpers.math\n * @namespace\n */ const PI = Math.PI;\nconst TAU = 2 * PI;\nconst PITAU = TAU + PI;\nconst INFINITY = Number.POSITIVE_INFINITY;\nconst RAD_PER_DEG = PI / 180;\nconst HALF_PI = PI / 2;\nconst QUARTER_PI = PI / 4;\nconst TWO_THIRDS_PI = PI * 2 / 3;\nconst log10 = Math.log10;\nconst sign = Math.sign;\nfunction almostEquals(x, y, epsilon) {\n    return Math.abs(x - y) < epsilon;\n}\n/**\n * Implementation of the nice number algorithm used in determining where axis labels will go\n */ function niceNum(range) {\n    const roundedRange = Math.round(range);\n    range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;\n    const niceRange = Math.pow(10, Math.floor(log10(range)));\n    const fraction = range / niceRange;\n    const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;\n    return niceFraction * niceRange;\n}\n/**\n * Returns an array of factors sorted from 1 to sqrt(value)\n * @private\n */ function _factorize(value) {\n    const result = [];\n    const sqrt = Math.sqrt(value);\n    let i;\n    for(i = 1; i < sqrt; i++){\n        if (value % i === 0) {\n            result.push(i);\n            result.push(value / i);\n        }\n    }\n    if (sqrt === (sqrt | 0)) {\n        result.push(sqrt);\n    }\n    result.sort((a, b)=>a - b).pop();\n    return result;\n}\nfunction isNumber(n) {\n    return !isNaN(parseFloat(n)) && isFinite(n);\n}\nfunction almostWhole(x, epsilon) {\n    const rounded = Math.round(x);\n    return rounded - epsilon <= x && rounded + epsilon >= x;\n}\n/**\n * @private\n */ function _setMinAndMaxByKey(array, target, property) {\n    let i, ilen, value;\n    for(i = 0, ilen = array.length; i < ilen; i++){\n        value = array[i][property];\n        if (!isNaN(value)) {\n            target.min = Math.min(target.min, value);\n            target.max = Math.max(target.max, value);\n        }\n    }\n}\nfunction toRadians(degrees) {\n    return degrees * (PI / 180);\n}\nfunction toDegrees(radians) {\n    return radians * (180 / PI);\n}\n/**\n * Returns the number of decimal places\n * i.e. the number of digits after the decimal point, of the value of this Number.\n * @param x - A number.\n * @returns The number of decimal places.\n * @private\n */ function _decimalPlaces(x) {\n    if (!isNumberFinite(x)) {\n        return;\n    }\n    let e = 1;\n    let p = 0;\n    while(Math.round(x * e) / e !== x){\n        e *= 10;\n        p++;\n    }\n    return p;\n}\n// Gets the angle from vertical upright to the point about a centre.\nfunction getAngleFromPoint(centrePoint, anglePoint) {\n    const distanceFromXCenter = anglePoint.x - centrePoint.x;\n    const distanceFromYCenter = anglePoint.y - centrePoint.y;\n    const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);\n    let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);\n    if (angle < -0.5 * PI) {\n        angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]\n    }\n    return {\n        angle,\n        distance: radialDistanceFromCenter\n    };\n}\nfunction distanceBetweenPoints(pt1, pt2) {\n    return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));\n}\n/**\n * Shortest distance between angles, in either direction.\n * @private\n */ function _angleDiff(a, b) {\n    return (a - b + PITAU) % TAU - PI;\n}\n/**\n * Normalize angle to be between 0 and 2*PI\n * @private\n */ function _normalizeAngle(a) {\n    return (a % TAU + TAU) % TAU;\n}\n/**\n * @private\n */ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {\n    const a = _normalizeAngle(angle);\n    const s = _normalizeAngle(start);\n    const e = _normalizeAngle(end);\n    const angleToStart = _normalizeAngle(s - a);\n    const angleToEnd = _normalizeAngle(e - a);\n    const startToAngle = _normalizeAngle(a - s);\n    const endToAngle = _normalizeAngle(a - e);\n    return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;\n}\n/**\n * Limit `value` between `min` and `max`\n * @param value\n * @param min\n * @param max\n * @private\n */ function _limitValue(value, min, max) {\n    return Math.max(min, Math.min(max, value));\n}\n/**\n * @param {number} value\n * @private\n */ function _int16Range(value) {\n    return _limitValue(value, -32768, 32767);\n}\n/**\n * @param value\n * @param start\n * @param end\n * @param [epsilon]\n * @private\n */ function _isBetween(value, start, end, epsilon = 1e-6) {\n    return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;\n}\n\nfunction _lookup(table, value, cmp) {\n    cmp = cmp || ((index)=>table[index] < value);\n    let hi = table.length - 1;\n    let lo = 0;\n    let mid;\n    while(hi - lo > 1){\n        mid = lo + hi >> 1;\n        if (cmp(mid)) {\n            lo = mid;\n        } else {\n            hi = mid;\n        }\n    }\n    return {\n        lo,\n        hi\n    };\n}\n/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @param last - lookup last index\n * @private\n */ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{\n        const ti = table[index][key];\n        return ti < value || ti === value && table[index + 1][key] === value;\n    } : (index)=>table[index][key] < value);\n/**\n * Reverse binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @private\n */ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value);\n/**\n * Return subset of `values` between `min` and `max` inclusive.\n * Values are assumed to be in sorted order.\n * @param values - sorted array of values\n * @param min - min value\n * @param max - max value\n */ function _filterBetween(values, min, max) {\n    let start = 0;\n    let end = values.length;\n    while(start < end && values[start] < min){\n        start++;\n    }\n    while(end > start && values[end - 1] > max){\n        end--;\n    }\n    return start > 0 || end < values.length ? values.slice(start, end) : values;\n}\nconst arrayEvents = [\n    'push',\n    'pop',\n    'shift',\n    'splice',\n    'unshift'\n];\nfunction listenArrayEvents(array, listener) {\n    if (array._chartjs) {\n        array._chartjs.listeners.push(listener);\n        return;\n    }\n    Object.defineProperty(array, '_chartjs', {\n        configurable: true,\n        enumerable: false,\n        value: {\n            listeners: [\n                listener\n            ]\n        }\n    });\n    arrayEvents.forEach((key)=>{\n        const method = '_onData' + _capitalize(key);\n        const base = array[key];\n        Object.defineProperty(array, key, {\n            configurable: true,\n            enumerable: false,\n            value (...args) {\n                const res = base.apply(this, args);\n                array._chartjs.listeners.forEach((object)=>{\n                    if (typeof object[method] === 'function') {\n                        object[method](...args);\n                    }\n                });\n                return res;\n            }\n        });\n    });\n}\nfunction unlistenArrayEvents(array, listener) {\n    const stub = array._chartjs;\n    if (!stub) {\n        return;\n    }\n    const listeners = stub.listeners;\n    const index = listeners.indexOf(listener);\n    if (index !== -1) {\n        listeners.splice(index, 1);\n    }\n    if (listeners.length > 0) {\n        return;\n    }\n    arrayEvents.forEach((key)=>{\n        delete array[key];\n    });\n    delete array._chartjs;\n}\n/**\n * @param items\n */ function _arrayUnique(items) {\n    const set = new Set();\n    let i, ilen;\n    for(i = 0, ilen = items.length; i < ilen; ++i){\n        set.add(items[i]);\n    }\n    if (set.size === ilen) {\n        return items;\n    }\n    return Array.from(set);\n}\n\nfunction fontString(pixelSize, fontStyle, fontFamily) {\n    return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;\n}\n/**\n* Request animation polyfill\n*/ const requestAnimFrame = function() {\n    if (typeof window === 'undefined') {\n        return function(callback) {\n            return callback();\n        };\n    }\n    return window.requestAnimationFrame;\n}();\n/**\n * Throttles calling `fn` once per animation frame\n * Latest arguments are used on the actual call\n */ function throttled(fn, thisArg) {\n    let argsToUse = [];\n    let ticking = false;\n    return function(...args) {\n        // Save the args for use later\n        argsToUse = args;\n        if (!ticking) {\n            ticking = true;\n            requestAnimFrame.call(window, ()=>{\n                ticking = false;\n                fn.apply(thisArg, argsToUse);\n            });\n        }\n    };\n}\n/**\n * Debounces calling `fn` for `delay` ms\n */ function debounce(fn, delay) {\n    let timeout;\n    return function(...args) {\n        if (delay) {\n            clearTimeout(timeout);\n            timeout = setTimeout(fn, delay, args);\n        } else {\n            fn.apply(this, args);\n        }\n        return delay;\n    };\n}\n/**\n * Converts 'start' to 'left', 'end' to 'right' and others to 'center'\n * @private\n */ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';\n/**\n * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`\n * @private\n */ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2;\n/**\n * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`\n * @private\n */ const _textX = (align, left, right, rtl)=>{\n    const check = rtl ? 'left' : 'right';\n    return align === check ? right : align === 'center' ? (left + right) / 2 : left;\n};\n/**\n * Return start and count of visible points.\n * @private\n */ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {\n    const pointCount = points.length;\n    let start = 0;\n    let count = pointCount;\n    if (meta._sorted) {\n        const { iScale , _parsed  } = meta;\n        const axis = iScale.axis;\n        const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();\n        if (minDefined) {\n            start = _limitValue(Math.min(// @ts-expect-error Need to type _parsed\n            _lookupByKey(_parsed, iScale.axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey\n            animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), 0, pointCount - 1);\n        }\n        if (maxDefined) {\n            count = _limitValue(Math.max(// @ts-expect-error Need to type _parsed\n            _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey\n            animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1), start, pointCount) - start;\n        } else {\n            count = pointCount - start;\n        }\n    }\n    return {\n        start,\n        count\n    };\n}\n/**\n * Checks if the scale ranges have changed.\n * @param {object} meta - dataset meta.\n * @returns {boolean}\n * @private\n */ function _scaleRangesChanged(meta) {\n    const { xScale , yScale , _scaleRanges  } = meta;\n    const newRanges = {\n        xmin: xScale.min,\n        xmax: xScale.max,\n        ymin: yScale.min,\n        ymax: yScale.max\n    };\n    if (!_scaleRanges) {\n        meta._scaleRanges = newRanges;\n        return true;\n    }\n    const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;\n    Object.assign(_scaleRanges, newRanges);\n    return changed;\n}\n\nconst atEdge = (t)=>t === 0 || t === 1;\nconst elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));\nconst elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;\n/**\n * Easing functions adapted from Robert Penner's easing equations.\n * @namespace Chart.helpers.easing.effects\n * @see http://www.robertpenner.com/easing/\n */ const effects = {\n    linear: (t)=>t,\n    easeInQuad: (t)=>t * t,\n    easeOutQuad: (t)=>-t * (t - 2),\n    easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),\n    easeInCubic: (t)=>t * t * t,\n    easeOutCubic: (t)=>(t -= 1) * t * t + 1,\n    easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),\n    easeInQuart: (t)=>t * t * t * t,\n    easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1),\n    easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),\n    easeInQuint: (t)=>t * t * t * t * t,\n    easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1,\n    easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),\n    easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1,\n    easeOutSine: (t)=>Math.sin(t * HALF_PI),\n    easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1),\n    easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),\n    easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,\n    easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),\n    easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),\n    easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t),\n    easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),\n    easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3),\n    easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3),\n    easeInOutElastic (t) {\n        const s = 0.1125;\n        const p = 0.45;\n        return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);\n    },\n    easeInBack (t) {\n        const s = 1.70158;\n        return t * t * ((s + 1) * t - s);\n    },\n    easeOutBack (t) {\n        const s = 1.70158;\n        return (t -= 1) * t * ((s + 1) * t + s) + 1;\n    },\n    easeInOutBack (t) {\n        let s = 1.70158;\n        if ((t /= 0.5) < 1) {\n            return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));\n        }\n        return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);\n    },\n    easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t),\n    easeOutBounce (t) {\n        const m = 7.5625;\n        const d = 2.75;\n        if (t < 1 / d) {\n            return m * t * t;\n        }\n        if (t < 2 / d) {\n            return m * (t -= 1.5 / d) * t + 0.75;\n        }\n        if (t < 2.5 / d) {\n            return m * (t -= 2.25 / d) * t + 0.9375;\n        }\n        return m * (t -= 2.625 / d) * t + 0.984375;\n    },\n    easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5\n};\n\nfunction isPatternOrGradient(value) {\n    if (value && typeof value === 'object') {\n        const type = value.toString();\n        return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';\n    }\n    return false;\n}\nfunction color(value) {\n    return isPatternOrGradient(value) ? value : new color$1.Color(value);\n}\nfunction getHoverColor(value) {\n    return isPatternOrGradient(value) ? value : new color$1.Color(value).saturate(0.5).darken(0.1).hexString();\n}\n\nconst numbers = [\n    'x',\n    'y',\n    'borderWidth',\n    'radius',\n    'tension'\n];\nconst colors = [\n    'color',\n    'borderColor',\n    'backgroundColor'\n];\nfunction applyAnimationsDefaults(defaults) {\n    defaults.set('animation', {\n        delay: undefined,\n        duration: 1000,\n        easing: 'easeOutQuart',\n        fn: undefined,\n        from: undefined,\n        loop: undefined,\n        to: undefined,\n        type: undefined\n    });\n    defaults.describe('animation', {\n        _fallback: false,\n        _indexable: false,\n        _scriptable: (name)=>name !== 'onProgress' && name !== 'onComplete' && name !== 'fn'\n    });\n    defaults.set('animations', {\n        colors: {\n            type: 'color',\n            properties: colors\n        },\n        numbers: {\n            type: 'number',\n            properties: numbers\n        }\n    });\n    defaults.describe('animations', {\n        _fallback: 'animation'\n    });\n    defaults.set('transitions', {\n        active: {\n            animation: {\n                duration: 400\n            }\n        },\n        resize: {\n            animation: {\n                duration: 0\n            }\n        },\n        show: {\n            animations: {\n                colors: {\n                    from: 'transparent'\n                },\n                visible: {\n                    type: 'boolean',\n                    duration: 0\n                }\n            }\n        },\n        hide: {\n            animations: {\n                colors: {\n                    to: 'transparent'\n                },\n                visible: {\n                    type: 'boolean',\n                    easing: 'linear',\n                    fn: (v)=>v | 0\n                }\n            }\n        }\n    });\n}\n\nfunction applyLayoutsDefaults(defaults) {\n    defaults.set('layout', {\n        autoPadding: true,\n        padding: {\n            top: 0,\n            right: 0,\n            bottom: 0,\n            left: 0\n        }\n    });\n}\n\nconst intlCache = new Map();\nfunction getNumberFormat(locale, options) {\n    options = options || {};\n    const cacheKey = locale + JSON.stringify(options);\n    let formatter = intlCache.get(cacheKey);\n    if (!formatter) {\n        formatter = new Intl.NumberFormat(locale, options);\n        intlCache.set(cacheKey, formatter);\n    }\n    return formatter;\n}\nfunction formatNumber(num, locale, options) {\n    return getNumberFormat(locale, options).format(num);\n}\n\nconst formatters = {\n values (value) {\n        return isArray(value) ?  value : '' + value;\n    },\n numeric (tickValue, index, ticks) {\n        if (tickValue === 0) {\n            return '0';\n        }\n        const locale = this.chart.options.locale;\n        let notation;\n        let delta = tickValue;\n        if (ticks.length > 1) {\n            const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));\n            if (maxTick < 1e-4 || maxTick > 1e+15) {\n                notation = 'scientific';\n            }\n            delta = calculateDelta(tickValue, ticks);\n        }\n        const logDelta = log10(Math.abs(delta));\n        const numDecimal = Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);\n        const options = {\n            notation,\n            minimumFractionDigits: numDecimal,\n            maximumFractionDigits: numDecimal\n        };\n        Object.assign(options, this.options.ticks.format);\n        return formatNumber(tickValue, locale, options);\n    },\n logarithmic (tickValue, index, ticks) {\n        if (tickValue === 0) {\n            return '0';\n        }\n        const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));\n        if ([\n            1,\n            2,\n            3,\n            5,\n            10,\n            15\n        ].includes(remain) || index > 0.8 * ticks.length) {\n            return formatters.numeric.call(this, tickValue, index, ticks);\n        }\n        return '';\n    }\n};\nfunction calculateDelta(tickValue, ticks) {\n    let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;\n    if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {\n        delta = tickValue - Math.floor(tickValue);\n    }\n    return delta;\n}\n var Ticks = {\n    formatters\n};\n\nfunction applyScaleDefaults(defaults) {\n    defaults.set('scale', {\n        display: true,\n        offset: false,\n        reverse: false,\n        beginAtZero: false,\n bounds: 'ticks',\n grace: 0,\n        grid: {\n            display: true,\n            lineWidth: 1,\n            drawOnChartArea: true,\n            drawTicks: true,\n            tickLength: 8,\n            tickWidth: (_ctx, options)=>options.lineWidth,\n            tickColor: (_ctx, options)=>options.color,\n            offset: false\n        },\n        border: {\n            display: true,\n            dash: [],\n            dashOffset: 0.0,\n            width: 1\n        },\n        title: {\n            display: false,\n            text: '',\n            padding: {\n                top: 4,\n                bottom: 4\n            }\n        },\n        ticks: {\n            minRotation: 0,\n            maxRotation: 50,\n            mirror: false,\n            textStrokeWidth: 0,\n            textStrokeColor: '',\n            padding: 3,\n            display: true,\n            autoSkip: true,\n            autoSkipPadding: 3,\n            labelOffset: 0,\n            callback: Ticks.formatters.values,\n            minor: {},\n            major: {},\n            align: 'center',\n            crossAlign: 'near',\n            showLabelBackdrop: false,\n            backdropColor: 'rgba(255, 255, 255, 0.75)',\n            backdropPadding: 2\n        }\n    });\n    defaults.route('scale.ticks', 'color', '', 'color');\n    defaults.route('scale.grid', 'color', '', 'borderColor');\n    defaults.route('scale.border', 'color', '', 'borderColor');\n    defaults.route('scale.title', 'color', '', 'color');\n    defaults.describe('scale', {\n        _fallback: false,\n        _scriptable: (name)=>!name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',\n        _indexable: (name)=>name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash'\n    });\n    defaults.describe('scales', {\n        _fallback: 'scale'\n    });\n    defaults.describe('scale.ticks', {\n        _scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback',\n        _indexable: (name)=>name !== 'backdropPadding'\n    });\n}\n\nconst overrides = Object.create(null);\nconst descriptors = Object.create(null);\n function getScope$1(node, key) {\n    if (!key) {\n        return node;\n    }\n    const keys = key.split('.');\n    for(let i = 0, n = keys.length; i < n; ++i){\n        const k = keys[i];\n        node = node[k] || (node[k] = Object.create(null));\n    }\n    return node;\n}\nfunction set(root, scope, values) {\n    if (typeof scope === 'string') {\n        return merge(getScope$1(root, scope), values);\n    }\n    return merge(getScope$1(root, ''), scope);\n}\n class Defaults {\n    constructor(_descriptors, _appliers){\n        this.animation = undefined;\n        this.backgroundColor = 'rgba(0,0,0,0.1)';\n        this.borderColor = 'rgba(0,0,0,0.1)';\n        this.color = '#666';\n        this.datasets = {};\n        this.devicePixelRatio = (context)=>context.chart.platform.getDevicePixelRatio();\n        this.elements = {};\n        this.events = [\n            'mousemove',\n            'mouseout',\n            'click',\n            'touchstart',\n            'touchmove'\n        ];\n        this.font = {\n            family: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n            size: 12,\n            style: 'normal',\n            lineHeight: 1.2,\n            weight: null\n        };\n        this.hover = {};\n        this.hoverBackgroundColor = (ctx, options)=>getHoverColor(options.backgroundColor);\n        this.hoverBorderColor = (ctx, options)=>getHoverColor(options.borderColor);\n        this.hoverColor = (ctx, options)=>getHoverColor(options.color);\n        this.indexAxis = 'x';\n        this.interaction = {\n            mode: 'nearest',\n            intersect: true,\n            includeInvisible: false\n        };\n        this.maintainAspectRatio = true;\n        this.onHover = null;\n        this.onClick = null;\n        this.parsing = true;\n        this.plugins = {};\n        this.responsive = true;\n        this.scale = undefined;\n        this.scales = {};\n        this.showLine = true;\n        this.drawActiveElementsOnTop = true;\n        this.describe(_descriptors);\n        this.apply(_appliers);\n    }\n set(scope, values) {\n        return set(this, scope, values);\n    }\n get(scope) {\n        return getScope$1(this, scope);\n    }\n describe(scope, values) {\n        return set(descriptors, scope, values);\n    }\n    override(scope, values) {\n        return set(overrides, scope, values);\n    }\n route(scope, name, targetScope, targetName) {\n        const scopeObject = getScope$1(this, scope);\n        const targetScopeObject = getScope$1(this, targetScope);\n        const privateName = '_' + name;\n        Object.defineProperties(scopeObject, {\n            [privateName]: {\n                value: scopeObject[name],\n                writable: true\n            },\n            [name]: {\n                enumerable: true,\n                get () {\n                    const local = this[privateName];\n                    const target = targetScopeObject[targetName];\n                    if (isObject(local)) {\n                        return Object.assign({}, target, local);\n                    }\n                    return valueOrDefault(local, target);\n                },\n                set (value) {\n                    this[privateName] = value;\n                }\n            }\n        });\n    }\n    apply(appliers) {\n        appliers.forEach((apply)=>apply(this));\n    }\n}\nvar defaults = /* #__PURE__ */ new Defaults({\n    _scriptable: (name)=>!name.startsWith('on'),\n    _indexable: (name)=>name !== 'events',\n    hover: {\n        _fallback: 'interaction'\n    },\n    interaction: {\n        _scriptable: false,\n        _indexable: false\n    }\n}, [\n    applyAnimationsDefaults,\n    applyLayoutsDefaults,\n    applyScaleDefaults\n]);\n\nfunction toFontString(font) {\n    if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {\n        return null;\n    }\n    return (font.style ? font.style + ' ' : '') + (font.weight ? font.weight + ' ' : '') + font.size + 'px ' + font.family;\n}\n function _measureText(ctx, data, gc, longest, string) {\n    let textWidth = data[string];\n    if (!textWidth) {\n        textWidth = data[string] = ctx.measureText(string).width;\n        gc.push(string);\n    }\n    if (textWidth > longest) {\n        longest = textWidth;\n    }\n    return longest;\n}\n function _longestText(ctx, font, arrayOfThings, cache) {\n    cache = cache || {};\n    let data = cache.data = cache.data || {};\n    let gc = cache.garbageCollect = cache.garbageCollect || [];\n    if (cache.font !== font) {\n        data = cache.data = {};\n        gc = cache.garbageCollect = [];\n        cache.font = font;\n    }\n    ctx.save();\n    ctx.font = font;\n    let longest = 0;\n    const ilen = arrayOfThings.length;\n    let i, j, jlen, thing, nestedThing;\n    for(i = 0; i < ilen; i++){\n        thing = arrayOfThings[i];\n        if (thing !== undefined && thing !== null && isArray(thing) !== true) {\n            longest = _measureText(ctx, data, gc, longest, thing);\n        } else if (isArray(thing)) {\n            for(j = 0, jlen = thing.length; j < jlen; j++){\n                nestedThing = thing[j];\n                if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {\n                    longest = _measureText(ctx, data, gc, longest, nestedThing);\n                }\n            }\n        }\n    }\n    ctx.restore();\n    const gcLen = gc.length / 2;\n    if (gcLen > arrayOfThings.length) {\n        for(i = 0; i < gcLen; i++){\n            delete data[gc[i]];\n        }\n        gc.splice(0, gcLen);\n    }\n    return longest;\n}\n function _alignPixel(chart, pixel, width) {\n    const devicePixelRatio = chart.currentDevicePixelRatio;\n    const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;\n    return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;\n}\n function clearCanvas(canvas, ctx) {\n    ctx = ctx || canvas.getContext('2d');\n    ctx.save();\n    ctx.resetTransform();\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n    ctx.restore();\n}\nfunction drawPoint(ctx, options, x, y) {\n    drawPointLegend(ctx, options, x, y, null);\n}\nfunction drawPointLegend(ctx, options, x, y, w) {\n    let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;\n    const style = options.pointStyle;\n    const rotation = options.rotation;\n    const radius = options.radius;\n    let rad = (rotation || 0) * RAD_PER_DEG;\n    if (style && typeof style === 'object') {\n        type = style.toString();\n        if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {\n            ctx.save();\n            ctx.translate(x, y);\n            ctx.rotate(rad);\n            ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);\n            ctx.restore();\n            return;\n        }\n    }\n    if (isNaN(radius) || radius <= 0) {\n        return;\n    }\n    ctx.beginPath();\n    switch(style){\n        default:\n            if (w) {\n                ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);\n            } else {\n                ctx.arc(x, y, radius, 0, TAU);\n            }\n            ctx.closePath();\n            break;\n        case 'triangle':\n            width = w ? w / 2 : radius;\n            ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n            rad += TWO_THIRDS_PI;\n            ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n            rad += TWO_THIRDS_PI;\n            ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n            ctx.closePath();\n            break;\n        case 'rectRounded':\n            cornerRadius = radius * 0.516;\n            size = radius - cornerRadius;\n            xOffset = Math.cos(rad + QUARTER_PI) * size;\n            xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n            yOffset = Math.sin(rad + QUARTER_PI) * size;\n            yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n            ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);\n            ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);\n            ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);\n            ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);\n            ctx.closePath();\n            break;\n        case 'rect':\n            if (!rotation) {\n                size = Math.SQRT1_2 * radius;\n                width = w ? w / 2 : size;\n                ctx.rect(x - width, y - size, 2 * width, 2 * size);\n                break;\n            }\n            rad += QUARTER_PI;\n         case 'rectRot':\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            ctx.closePath();\n            break;\n        case 'crossRot':\n            rad += QUARTER_PI;\n         case 'cross':\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.moveTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            break;\n        case 'star':\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.moveTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            rad += QUARTER_PI;\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.moveTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            break;\n        case 'line':\n            xOffset = w ? w / 2 : Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            ctx.moveTo(x - xOffset, y - yOffset);\n            ctx.lineTo(x + xOffset, y + yOffset);\n            break;\n        case 'dash':\n            ctx.moveTo(x, y);\n            ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);\n            break;\n        case false:\n            ctx.closePath();\n            break;\n    }\n    ctx.fill();\n    if (options.borderWidth > 0) {\n        ctx.stroke();\n    }\n}\n function _isPointInArea(point, area, margin) {\n    margin = margin || 0.5;\n    return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;\n}\nfunction clipArea(ctx, area) {\n    ctx.save();\n    ctx.beginPath();\n    ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);\n    ctx.clip();\n}\nfunction unclipArea(ctx) {\n    ctx.restore();\n}\n function _steppedLineTo(ctx, previous, target, flip, mode) {\n    if (!previous) {\n        return ctx.lineTo(target.x, target.y);\n    }\n    if (mode === 'middle') {\n        const midpoint = (previous.x + target.x) / 2.0;\n        ctx.lineTo(midpoint, previous.y);\n        ctx.lineTo(midpoint, target.y);\n    } else if (mode === 'after' !== !!flip) {\n        ctx.lineTo(previous.x, target.y);\n    } else {\n        ctx.lineTo(target.x, previous.y);\n    }\n    ctx.lineTo(target.x, target.y);\n}\n function _bezierCurveTo(ctx, previous, target, flip) {\n    if (!previous) {\n        return ctx.lineTo(target.x, target.y);\n    }\n    ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y);\n}\n function renderText(ctx, text, x, y, font, opts = {}) {\n    const lines = isArray(text) ? text : [\n        text\n    ];\n    const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';\n    let i, line;\n    ctx.save();\n    ctx.font = font.string;\n    setRenderOpts(ctx, opts);\n    for(i = 0; i < lines.length; ++i){\n        line = lines[i];\n        if (opts.backdrop) {\n            drawBackdrop(ctx, opts.backdrop);\n        }\n        if (stroke) {\n            if (opts.strokeColor) {\n                ctx.strokeStyle = opts.strokeColor;\n            }\n            if (!isNullOrUndef(opts.strokeWidth)) {\n                ctx.lineWidth = opts.strokeWidth;\n            }\n            ctx.strokeText(line, x, y, opts.maxWidth);\n        }\n        ctx.fillText(line, x, y, opts.maxWidth);\n        decorateText(ctx, x, y, line, opts);\n        y += font.lineHeight;\n    }\n    ctx.restore();\n}\nfunction setRenderOpts(ctx, opts) {\n    if (opts.translation) {\n        ctx.translate(opts.translation[0], opts.translation[1]);\n    }\n    if (!isNullOrUndef(opts.rotation)) {\n        ctx.rotate(opts.rotation);\n    }\n    if (opts.color) {\n        ctx.fillStyle = opts.color;\n    }\n    if (opts.textAlign) {\n        ctx.textAlign = opts.textAlign;\n    }\n    if (opts.textBaseline) {\n        ctx.textBaseline = opts.textBaseline;\n    }\n}\nfunction decorateText(ctx, x, y, line, opts) {\n    if (opts.strikethrough || opts.underline) {\n const metrics = ctx.measureText(line);\n        const left = x - metrics.actualBoundingBoxLeft;\n        const right = x + metrics.actualBoundingBoxRight;\n        const top = y - metrics.actualBoundingBoxAscent;\n        const bottom = y + metrics.actualBoundingBoxDescent;\n        const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;\n        ctx.strokeStyle = ctx.fillStyle;\n        ctx.beginPath();\n        ctx.lineWidth = opts.decorationWidth || 2;\n        ctx.moveTo(left, yDecoration);\n        ctx.lineTo(right, yDecoration);\n        ctx.stroke();\n    }\n}\nfunction drawBackdrop(ctx, opts) {\n    const oldColor = ctx.fillStyle;\n    ctx.fillStyle = opts.color;\n    ctx.fillRect(opts.left, opts.top, opts.width, opts.height);\n    ctx.fillStyle = oldColor;\n}\n function addRoundedRectPath(ctx, rect) {\n    const { x , y , w , h , radius  } = rect;\n    ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_PI, PI, true);\n    ctx.lineTo(x, y + h - radius.bottomLeft);\n    ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);\n    ctx.lineTo(x + w - radius.bottomRight, y + h);\n    ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);\n    ctx.lineTo(x + w, y + radius.topRight);\n    ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);\n    ctx.lineTo(x + radius.topLeft, y);\n}\n\nconst LINE_HEIGHT = /^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/;\nconst FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;\n/**\n * @alias Chart.helpers.options\n * @namespace\n */ /**\n * Converts the given line height `value` in pixels for a specific font `size`.\n * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n * @param size - The font size (in pixels) used to resolve relative `value`.\n * @returns The effective line height in pixels (size * 1.2 if value is invalid).\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n * @since 2.7.0\n */ function toLineHeight(value, size) {\n    const matches = ('' + value).match(LINE_HEIGHT);\n    if (!matches || matches[1] === 'normal') {\n        return size * 1.2;\n    }\n    value = +matches[2];\n    switch(matches[3]){\n        case 'px':\n            return value;\n        case '%':\n            value /= 100;\n            break;\n    }\n    return size * value;\n}\nconst numberOrZero = (v)=>+v || 0;\nfunction _readValueToProps(value, props) {\n    const ret = {};\n    const objProps = isObject(props);\n    const keys = objProps ? Object.keys(props) : props;\n    const read = isObject(value) ? objProps ? (prop)=>valueOrDefault(value[prop], value[props[prop]]) : (prop)=>value[prop] : ()=>value;\n    for (const prop of keys){\n        ret[prop] = numberOrZero(read(prop));\n    }\n    return ret;\n}\n/**\n * Converts the given value into a TRBL object.\n * @param value - If a number, set the value to all TRBL component,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n *  x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left)\n * @since 3.0.0\n */ function toTRBL(value) {\n    return _readValueToProps(value, {\n        top: 'y',\n        right: 'x',\n        bottom: 'y',\n        left: 'x'\n    });\n}\n/**\n * Converts the given value into a TRBL corners object (similar with css border-radius).\n * @param value - If a number, set the value to all TRBL corner components,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)\n * @since 3.0.0\n */ function toTRBLCorners(value) {\n    return _readValueToProps(value, [\n        'topLeft',\n        'topRight',\n        'bottomLeft',\n        'bottomRight'\n    ]);\n}\n/**\n * Converts the given value into a padding object with pre-computed width/height.\n * @param value - If a number, set the value to all TRBL component,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n *  x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left, width, height)\n * @since 2.7.0\n */ function toPadding(value) {\n    const obj = toTRBL(value);\n    obj.width = obj.left + obj.right;\n    obj.height = obj.top + obj.bottom;\n    return obj;\n}\n/**\n * Parses font options and returns the font object.\n * @param options - A object that contains font options to be parsed.\n * @param fallback - A object that contains fallback font options.\n * @return The font object.\n * @private\n */ function toFont(options, fallback) {\n    options = options || {};\n    fallback = fallback || defaults.font;\n    let size = valueOrDefault(options.size, fallback.size);\n    if (typeof size === 'string') {\n        size = parseInt(size, 10);\n    }\n    let style = valueOrDefault(options.style, fallback.style);\n    if (style && !('' + style).match(FONT_STYLE)) {\n        console.warn('Invalid font style specified: \"' + style + '\"');\n        style = undefined;\n    }\n    const font = {\n        family: valueOrDefault(options.family, fallback.family),\n        lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),\n        size,\n        style,\n        weight: valueOrDefault(options.weight, fallback.weight),\n        string: ''\n    };\n    font.string = toFontString(font);\n    return font;\n}\n/**\n * Evaluates the given `inputs` sequentially and returns the first defined value.\n * @param inputs - An array of values, falling back to the last value.\n * @param context - If defined and the current value is a function, the value\n * is called with `context` as first argument and the result becomes the new input.\n * @param index - If defined and the current value is an array, the value\n * at `index` become the new input.\n * @param info - object to return information about resolution in\n * @param info.cacheable - Will be set to `false` if option is not cacheable.\n * @since 2.7.0\n */ function resolve(inputs, context, index, info) {\n    let cacheable = true;\n    let i, ilen, value;\n    for(i = 0, ilen = inputs.length; i < ilen; ++i){\n        value = inputs[i];\n        if (value === undefined) {\n            continue;\n        }\n        if (context !== undefined && typeof value === 'function') {\n            value = value(context);\n            cacheable = false;\n        }\n        if (index !== undefined && isArray(value)) {\n            value = value[index % value.length];\n            cacheable = false;\n        }\n        if (value !== undefined) {\n            if (info && !cacheable) {\n                info.cacheable = false;\n            }\n            return value;\n        }\n    }\n}\n/**\n * @param minmax\n * @param grace\n * @param beginAtZero\n * @private\n */ function _addGrace(minmax, grace, beginAtZero) {\n    const { min , max  } = minmax;\n    const change = toDimension(grace, (max - min) / 2);\n    const keepZero = (value, add)=>beginAtZero && value === 0 ? 0 : value + add;\n    return {\n        min: keepZero(min, -Math.abs(change)),\n        max: keepZero(max, change)\n    };\n}\nfunction createContext(parentContext, context) {\n    return Object.assign(Object.create(parentContext), context);\n}\n\nfunction _createResolver(scopes, prefixes = [\n    ''\n], rootScopes = scopes, fallback, getTarget = ()=>scopes[0]) {\n    if (!defined(fallback)) {\n        fallback = _resolve('_fallback', scopes);\n    }\n    const cache = {\n        [Symbol.toStringTag]: 'Object',\n        _cacheable: true,\n        _scopes: scopes,\n        _rootScopes: rootScopes,\n        _fallback: fallback,\n        _getTarget: getTarget,\n        override: (scope)=>_createResolver([\n                scope,\n                ...scopes\n            ], prefixes, rootScopes, fallback)\n    };\n    return new Proxy(cache, {\n deleteProperty (target, prop) {\n            delete target[prop];\n            delete target._keys;\n            delete scopes[0][prop];\n            return true;\n        },\n get (target, prop) {\n            return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target));\n        },\n getOwnPropertyDescriptor (target, prop) {\n            return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);\n        },\n getPrototypeOf () {\n            return Reflect.getPrototypeOf(scopes[0]);\n        },\n has (target, prop) {\n            return getKeysFromAllScopes(target).includes(prop);\n        },\n ownKeys (target) {\n            return getKeysFromAllScopes(target);\n        },\n set (target, prop, value) {\n            const storage = target._storage || (target._storage = getTarget());\n            target[prop] = storage[prop] = value;\n            delete target._keys;\n            return true;\n        }\n    });\n}\n function _attachContext(proxy, context, subProxy, descriptorDefaults) {\n    const cache = {\n        _cacheable: false,\n        _proxy: proxy,\n        _context: context,\n        _subProxy: subProxy,\n        _stack: new Set(),\n        _descriptors: _descriptors(proxy, descriptorDefaults),\n        setContext: (ctx)=>_attachContext(proxy, ctx, subProxy, descriptorDefaults),\n        override: (scope)=>_attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)\n    };\n    return new Proxy(cache, {\n deleteProperty (target, prop) {\n            delete target[prop];\n            delete proxy[prop];\n            return true;\n        },\n get (target, prop, receiver) {\n            return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver));\n        },\n getOwnPropertyDescriptor (target, prop) {\n            return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {\n                enumerable: true,\n                configurable: true\n            } : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop);\n        },\n getPrototypeOf () {\n            return Reflect.getPrototypeOf(proxy);\n        },\n has (target, prop) {\n            return Reflect.has(proxy, prop);\n        },\n ownKeys () {\n            return Reflect.ownKeys(proxy);\n        },\n set (target, prop, value) {\n            proxy[prop] = value;\n            delete target[prop];\n            return true;\n        }\n    });\n}\n function _descriptors(proxy, defaults = {\n    scriptable: true,\n    indexable: true\n}) {\n    const { _scriptable =defaults.scriptable , _indexable =defaults.indexable , _allKeys =defaults.allKeys  } = proxy;\n    return {\n        allKeys: _allKeys,\n        scriptable: _scriptable,\n        indexable: _indexable,\n        isScriptable: isFunction(_scriptable) ? _scriptable : ()=>_scriptable,\n        isIndexable: isFunction(_indexable) ? _indexable : ()=>_indexable\n    };\n}\nconst readKey = (prefix, name)=>prefix ? prefix + _capitalize(name) : name;\nconst needsSubResolver = (prop, value)=>isObject(value) && prop !== 'adapters' && (Object.getPrototypeOf(value) === null || value.constructor === Object);\nfunction _cached(target, prop, resolve) {\n    if (Object.prototype.hasOwnProperty.call(target, prop)) {\n        return target[prop];\n    }\n    const value = resolve();\n    target[prop] = value;\n    return value;\n}\nfunction _resolveWithContext(target, prop, receiver) {\n    const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;\n    let value = _proxy[prop];\n    if (isFunction(value) && descriptors.isScriptable(prop)) {\n        value = _resolveScriptable(prop, value, target, receiver);\n    }\n    if (isArray(value) && value.length) {\n        value = _resolveArray(prop, value, target, descriptors.isIndexable);\n    }\n    if (needsSubResolver(prop, value)) {\n        value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);\n    }\n    return value;\n}\nfunction _resolveScriptable(prop, value, target, receiver) {\n    const { _proxy , _context , _subProxy , _stack  } = target;\n    if (_stack.has(prop)) {\n        throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);\n    }\n    _stack.add(prop);\n    value = value(_context, _subProxy || receiver);\n    _stack.delete(prop);\n    if (needsSubResolver(prop, value)) {\n        value = createSubResolver(_proxy._scopes, _proxy, prop, value);\n    }\n    return value;\n}\nfunction _resolveArray(prop, value, target, isIndexable) {\n    const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;\n    if (defined(_context.index) && isIndexable(prop)) {\n        value = value[_context.index % value.length];\n    } else if (isObject(value[0])) {\n        const arr = value;\n        const scopes = _proxy._scopes.filter((s)=>s !== arr);\n        value = [];\n        for (const item of arr){\n            const resolver = createSubResolver(scopes, _proxy, prop, item);\n            value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));\n        }\n    }\n    return value;\n}\nfunction resolveFallback(fallback, prop, value) {\n    return isFunction(fallback) ? fallback(prop, value) : fallback;\n}\nconst getScope = (key, parent)=>key === true ? parent : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;\nfunction addScopes(set, parentScopes, key, parentFallback, value) {\n    for (const parent of parentScopes){\n        const scope = getScope(key, parent);\n        if (scope) {\n            set.add(scope);\n            const fallback = resolveFallback(scope._fallback, key, value);\n            if (defined(fallback) && fallback !== key && fallback !== parentFallback) {\n                return fallback;\n            }\n        } else if (scope === false && defined(parentFallback) && key !== parentFallback) {\n            return null;\n        }\n    }\n    return false;\n}\nfunction createSubResolver(parentScopes, resolver, prop, value) {\n    const rootScopes = resolver._rootScopes;\n    const fallback = resolveFallback(resolver._fallback, prop, value);\n    const allScopes = [\n        ...parentScopes,\n        ...rootScopes\n    ];\n    const set = new Set();\n    set.add(value);\n    let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);\n    if (key === null) {\n        return false;\n    }\n    if (defined(fallback) && fallback !== prop) {\n        key = addScopesFromKey(set, allScopes, fallback, key, value);\n        if (key === null) {\n            return false;\n        }\n    }\n    return _createResolver(Array.from(set), [\n        ''\n    ], rootScopes, fallback, ()=>subGetTarget(resolver, prop, value));\n}\nfunction addScopesFromKey(set, allScopes, key, fallback, item) {\n    while(key){\n        key = addScopes(set, allScopes, key, fallback, item);\n    }\n    return key;\n}\nfunction subGetTarget(resolver, prop, value) {\n    const parent = resolver._getTarget();\n    if (!(prop in parent)) {\n        parent[prop] = {};\n    }\n    const target = parent[prop];\n    if (isArray(target) && isObject(value)) {\n        return value;\n    }\n    return target || {};\n}\nfunction _resolveWithPrefixes(prop, prefixes, scopes, proxy) {\n    let value;\n    for (const prefix of prefixes){\n        value = _resolve(readKey(prefix, prop), scopes);\n        if (defined(value)) {\n            return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;\n        }\n    }\n}\nfunction _resolve(key, scopes) {\n    for (const scope of scopes){\n        if (!scope) {\n            continue;\n        }\n        const value = scope[key];\n        if (defined(value)) {\n            return value;\n        }\n    }\n}\nfunction getKeysFromAllScopes(target) {\n    let keys = target._keys;\n    if (!keys) {\n        keys = target._keys = resolveKeysFromAllScopes(target._scopes);\n    }\n    return keys;\n}\nfunction resolveKeysFromAllScopes(scopes) {\n    const set = new Set();\n    for (const scope of scopes){\n        for (const key of Object.keys(scope).filter((k)=>!k.startsWith('_'))){\n            set.add(key);\n        }\n    }\n    return Array.from(set);\n}\nfunction _parseObjectDataRadialScale(meta, data, start, count) {\n    const { iScale  } = meta;\n    const { key ='r'  } = this._parsing;\n    const parsed = new Array(count);\n    let i, ilen, index, item;\n    for(i = 0, ilen = count; i < ilen; ++i){\n        index = i + start;\n        item = data[index];\n        parsed[i] = {\n            r: iScale.parse(resolveObjectKey(item, key), index)\n        };\n    }\n    return parsed;\n}\n\nconst EPSILON = Number.EPSILON || 1e-14;\nconst getPoint = (points, i)=>i < points.length && !points[i].skip && points[i];\nconst getValueAxis = (indexAxis)=>indexAxis === 'x' ? 'y' : 'x';\nfunction splineCurve(firstPoint, middlePoint, afterPoint, t) {\n    // Props to Rob Spencer at scaled innovation for his post on splining between points\n    // http://scaledinnovation.com/analytics/splines/aboutSplines.html\n    // This function must also respect \"skipped\" points\n    const previous = firstPoint.skip ? middlePoint : firstPoint;\n    const current = middlePoint;\n    const next = afterPoint.skip ? middlePoint : afterPoint;\n    const d01 = distanceBetweenPoints(current, previous);\n    const d12 = distanceBetweenPoints(next, current);\n    let s01 = d01 / (d01 + d12);\n    let s12 = d12 / (d01 + d12);\n    // If all points are the same, s01 & s02 will be inf\n    s01 = isNaN(s01) ? 0 : s01;\n    s12 = isNaN(s12) ? 0 : s12;\n    const fa = t * s01; // scaling factor for triangle Ta\n    const fb = t * s12;\n    return {\n        previous: {\n            x: current.x - fa * (next.x - previous.x),\n            y: current.y - fa * (next.y - previous.y)\n        },\n        next: {\n            x: current.x + fb * (next.x - previous.x),\n            y: current.y + fb * (next.y - previous.y)\n        }\n    };\n}\n/**\n * Adjust tangents to ensure monotonic properties\n */ function monotoneAdjust(points, deltaK, mK) {\n    const pointsLen = points.length;\n    let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;\n    let pointAfter = getPoint(points, 0);\n    for(let i = 0; i < pointsLen - 1; ++i){\n        pointCurrent = pointAfter;\n        pointAfter = getPoint(points, i + 1);\n        if (!pointCurrent || !pointAfter) {\n            continue;\n        }\n        if (almostEquals(deltaK[i], 0, EPSILON)) {\n            mK[i] = mK[i + 1] = 0;\n            continue;\n        }\n        alphaK = mK[i] / deltaK[i];\n        betaK = mK[i + 1] / deltaK[i];\n        squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);\n        if (squaredMagnitude <= 9) {\n            continue;\n        }\n        tauK = 3 / Math.sqrt(squaredMagnitude);\n        mK[i] = alphaK * tauK * deltaK[i];\n        mK[i + 1] = betaK * tauK * deltaK[i];\n    }\n}\nfunction monotoneCompute(points, mK, indexAxis = 'x') {\n    const valueAxis = getValueAxis(indexAxis);\n    const pointsLen = points.length;\n    let delta, pointBefore, pointCurrent;\n    let pointAfter = getPoint(points, 0);\n    for(let i = 0; i < pointsLen; ++i){\n        pointBefore = pointCurrent;\n        pointCurrent = pointAfter;\n        pointAfter = getPoint(points, i + 1);\n        if (!pointCurrent) {\n            continue;\n        }\n        const iPixel = pointCurrent[indexAxis];\n        const vPixel = pointCurrent[valueAxis];\n        if (pointBefore) {\n            delta = (iPixel - pointBefore[indexAxis]) / 3;\n            pointCurrent[`cp1${indexAxis}`] = iPixel - delta;\n            pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];\n        }\n        if (pointAfter) {\n            delta = (pointAfter[indexAxis] - iPixel) / 3;\n            pointCurrent[`cp2${indexAxis}`] = iPixel + delta;\n            pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];\n        }\n    }\n}\n/**\n * This function calculates Bézier control points in a similar way than |splineCurve|,\n * but preserves monotonicity of the provided data and ensures no local extremums are added\n * between the dataset discrete points due to the interpolation.\n * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation\n */ function splineCurveMonotone(points, indexAxis = 'x') {\n    const valueAxis = getValueAxis(indexAxis);\n    const pointsLen = points.length;\n    const deltaK = Array(pointsLen).fill(0);\n    const mK = Array(pointsLen);\n    // Calculate slopes (deltaK) and initialize tangents (mK)\n    let i, pointBefore, pointCurrent;\n    let pointAfter = getPoint(points, 0);\n    for(i = 0; i < pointsLen; ++i){\n        pointBefore = pointCurrent;\n        pointCurrent = pointAfter;\n        pointAfter = getPoint(points, i + 1);\n        if (!pointCurrent) {\n            continue;\n        }\n        if (pointAfter) {\n            const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];\n            // In the case of two points that appear at the same x pixel, slopeDeltaX is 0\n            deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;\n        }\n        mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;\n    }\n    monotoneAdjust(points, deltaK, mK);\n    monotoneCompute(points, mK, indexAxis);\n}\nfunction capControlPoint(pt, min, max) {\n    return Math.max(Math.min(pt, max), min);\n}\nfunction capBezierPoints(points, area) {\n    let i, ilen, point, inArea, inAreaPrev;\n    let inAreaNext = _isPointInArea(points[0], area);\n    for(i = 0, ilen = points.length; i < ilen; ++i){\n        inAreaPrev = inArea;\n        inArea = inAreaNext;\n        inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);\n        if (!inArea) {\n            continue;\n        }\n        point = points[i];\n        if (inAreaPrev) {\n            point.cp1x = capControlPoint(point.cp1x, area.left, area.right);\n            point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);\n        }\n        if (inAreaNext) {\n            point.cp2x = capControlPoint(point.cp2x, area.left, area.right);\n            point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);\n        }\n    }\n}\n/**\n * @private\n */ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {\n    let i, ilen, point, controlPoints;\n    // Only consider points that are drawn in case the spanGaps option is used\n    if (options.spanGaps) {\n        points = points.filter((pt)=>!pt.skip);\n    }\n    if (options.cubicInterpolationMode === 'monotone') {\n        splineCurveMonotone(points, indexAxis);\n    } else {\n        let prev = loop ? points[points.length - 1] : points[0];\n        for(i = 0, ilen = points.length; i < ilen; ++i){\n            point = points[i];\n            controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);\n            point.cp1x = controlPoints.previous.x;\n            point.cp1y = controlPoints.previous.y;\n            point.cp2x = controlPoints.next.x;\n            point.cp2y = controlPoints.next.y;\n            prev = point;\n        }\n    }\n    if (options.capBezierPoints) {\n        capBezierPoints(points, area);\n    }\n}\n\n/**\n * Note: typedefs are auto-exported, so use a made-up `dom` namespace where\n * necessary to avoid duplicates with `export * from './helpers`; see\n * https://github.com/microsoft/TypeScript/issues/46011\n * @typedef { import('../core/core.controller.js').default } dom.Chart\n * @typedef { import('../../types').ChartEvent } ChartEvent\n */ /**\n * @private\n */ function _isDomSupported() {\n    return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n/**\n * @private\n */ function _getParentNode(domNode) {\n    let parent = domNode.parentNode;\n    if (parent && parent.toString() === '[object ShadowRoot]') {\n        parent = parent.host;\n    }\n    return parent;\n}\n/**\n * convert max-width/max-height values that may be percentages into a number\n * @private\n */ function parseMaxStyle(styleValue, node, parentProperty) {\n    let valueInPixels;\n    if (typeof styleValue === 'string') {\n        valueInPixels = parseInt(styleValue, 10);\n        if (styleValue.indexOf('%') !== -1) {\n            // percentage * size in dimension\n            valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];\n        }\n    } else {\n        valueInPixels = styleValue;\n    }\n    return valueInPixels;\n}\nconst getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);\nfunction getStyle(el, property) {\n    return getComputedStyle(el).getPropertyValue(property);\n}\nconst positions = [\n    'top',\n    'right',\n    'bottom',\n    'left'\n];\nfunction getPositionedStyle(styles, style, suffix) {\n    const result = {};\n    suffix = suffix ? '-' + suffix : '';\n    for(let i = 0; i < 4; i++){\n        const pos = positions[i];\n        result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;\n    }\n    result.width = result.left + result.right;\n    result.height = result.top + result.bottom;\n    return result;\n}\nconst useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);\n/**\n * @param e\n * @param canvas\n * @returns Canvas position\n */ function getCanvasPosition(e, canvas) {\n    const touches = e.touches;\n    const source = touches && touches.length ? touches[0] : e;\n    const { offsetX , offsetY  } = source;\n    let box = false;\n    let x, y;\n    if (useOffsetPos(offsetX, offsetY, e.target)) {\n        x = offsetX;\n        y = offsetY;\n    } else {\n        const rect = canvas.getBoundingClientRect();\n        x = source.clientX - rect.left;\n        y = source.clientY - rect.top;\n        box = true;\n    }\n    return {\n        x,\n        y,\n        box\n    };\n}\n/**\n * Gets an event's x, y coordinates, relative to the chart area\n * @param event\n * @param chart\n * @returns x and y coordinates of the event\n */ function getRelativePosition(event, chart) {\n    if ('native' in event) {\n        return event;\n    }\n    const { canvas , currentDevicePixelRatio  } = chart;\n    const style = getComputedStyle(canvas);\n    const borderBox = style.boxSizing === 'border-box';\n    const paddings = getPositionedStyle(style, 'padding');\n    const borders = getPositionedStyle(style, 'border', 'width');\n    const { x , y , box  } = getCanvasPosition(event, canvas);\n    const xOffset = paddings.left + (box && borders.left);\n    const yOffset = paddings.top + (box && borders.top);\n    let { width , height  } = chart;\n    if (borderBox) {\n        width -= paddings.width + borders.width;\n        height -= paddings.height + borders.height;\n    }\n    return {\n        x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),\n        y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)\n    };\n}\nfunction getContainerSize(canvas, width, height) {\n    let maxWidth, maxHeight;\n    if (width === undefined || height === undefined) {\n        const container = _getParentNode(canvas);\n        if (!container) {\n            width = canvas.clientWidth;\n            height = canvas.clientHeight;\n        } else {\n            const rect = container.getBoundingClientRect(); // this is the border box of the container\n            const containerStyle = getComputedStyle(container);\n            const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');\n            const containerPadding = getPositionedStyle(containerStyle, 'padding');\n            width = rect.width - containerPadding.width - containerBorder.width;\n            height = rect.height - containerPadding.height - containerBorder.height;\n            maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');\n            maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');\n        }\n    }\n    return {\n        width,\n        height,\n        maxWidth: maxWidth || INFINITY,\n        maxHeight: maxHeight || INFINITY\n    };\n}\nconst round1 = (v)=>Math.round(v * 10) / 10;\n// eslint-disable-next-line complexity\nfunction getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {\n    const style = getComputedStyle(canvas);\n    const margins = getPositionedStyle(style, 'margin');\n    const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;\n    const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;\n    const containerSize = getContainerSize(canvas, bbWidth, bbHeight);\n    let { width , height  } = containerSize;\n    if (style.boxSizing === 'content-box') {\n        const borders = getPositionedStyle(style, 'border', 'width');\n        const paddings = getPositionedStyle(style, 'padding');\n        width -= paddings.width + borders.width;\n        height -= paddings.height + borders.height;\n    }\n    width = Math.max(0, width - margins.width);\n    height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);\n    width = round1(Math.min(width, maxWidth, containerSize.maxWidth));\n    height = round1(Math.min(height, maxHeight, containerSize.maxHeight));\n    if (width && !height) {\n        // https://github.com/chartjs/Chart.js/issues/4659\n        // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)\n        height = round1(width / 2);\n    }\n    const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;\n    if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {\n        height = containerSize.height;\n        width = round1(Math.floor(height * aspectRatio));\n    }\n    return {\n        width,\n        height\n    };\n}\n/**\n * @param chart\n * @param forceRatio\n * @param forceStyle\n * @returns True if the canvas context size or transformation has changed.\n */ function retinaScale(chart, forceRatio, forceStyle) {\n    const pixelRatio = forceRatio || 1;\n    const deviceHeight = Math.floor(chart.height * pixelRatio);\n    const deviceWidth = Math.floor(chart.width * pixelRatio);\n    chart.height = Math.floor(chart.height);\n    chart.width = Math.floor(chart.width);\n    const canvas = chart.canvas;\n    // If no style has been set on the canvas, the render size is used as display size,\n    // making the chart visually bigger, so let's enforce it to the \"correct\" values.\n    // See https://github.com/chartjs/Chart.js/issues/3575\n    if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {\n        canvas.style.height = `${chart.height}px`;\n        canvas.style.width = `${chart.width}px`;\n    }\n    if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {\n        chart.currentDevicePixelRatio = pixelRatio;\n        canvas.height = deviceHeight;\n        canvas.width = deviceWidth;\n        chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);\n        return true;\n    }\n    return false;\n}\n/**\n * Detects support for options object argument in addEventListener.\n * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n * @private\n */ const supportsEventListenerOptions = function() {\n    let passiveSupported = false;\n    try {\n        const options = {\n            get passive () {\n                passiveSupported = true;\n                return false;\n            }\n        };\n        window.addEventListener('test', null, options);\n        window.removeEventListener('test', null, options);\n    } catch (e) {\n    // continue regardless of error\n    }\n    return passiveSupported;\n}();\n/**\n * The \"used\" size is the final value of a dimension property after all calculations have\n * been performed. This method uses the computed style of `element` but returns undefined\n * if the computed style is not expressed in pixels. That can happen in some cases where\n * `element` has a size relative to its parent and this last one is not yet displayed,\n * for example because of `display: none` on a parent node.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n * @returns Size in pixels or undefined if unknown.\n */ function readUsedSize(element, property) {\n    const value = getStyle(element, property);\n    const matches = value && value.match(/^(\\d+)(\\.\\d+)?px$/);\n    return matches ? +matches[1] : undefined;\n}\n\n/**\n * @private\n */ function _pointInLine(p1, p2, t, mode) {\n    return {\n        x: p1.x + t * (p2.x - p1.x),\n        y: p1.y + t * (p2.y - p1.y)\n    };\n}\n/**\n * @private\n */ function _steppedInterpolation(p1, p2, t, mode) {\n    return {\n        x: p1.x + t * (p2.x - p1.x),\n        y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y : mode === 'after' ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y\n    };\n}\n/**\n * @private\n */ function _bezierInterpolation(p1, p2, t, mode) {\n    const cp1 = {\n        x: p1.cp2x,\n        y: p1.cp2y\n    };\n    const cp2 = {\n        x: p2.cp1x,\n        y: p2.cp1y\n    };\n    const a = _pointInLine(p1, cp1, t);\n    const b = _pointInLine(cp1, cp2, t);\n    const c = _pointInLine(cp2, p2, t);\n    const d = _pointInLine(a, b, t);\n    const e = _pointInLine(b, c, t);\n    return _pointInLine(d, e, t);\n}\n\nconst getRightToLeftAdapter = function(rectX, width) {\n    return {\n        x (x) {\n            return rectX + rectX + width - x;\n        },\n        setWidth (w) {\n            width = w;\n        },\n        textAlign (align) {\n            if (align === 'center') {\n                return align;\n            }\n            return align === 'right' ? 'left' : 'right';\n        },\n        xPlus (x, value) {\n            return x - value;\n        },\n        leftForLtr (x, itemWidth) {\n            return x - itemWidth;\n        }\n    };\n};\nconst getLeftToRightAdapter = function() {\n    return {\n        x (x) {\n            return x;\n        },\n        setWidth (w) {},\n        textAlign (align) {\n            return align;\n        },\n        xPlus (x, value) {\n            return x + value;\n        },\n        leftForLtr (x, _itemWidth) {\n            return x;\n        }\n    };\n};\nfunction getRtlAdapter(rtl, rectX, width) {\n    return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();\n}\nfunction overrideTextDirection(ctx, direction) {\n    let style, original;\n    if (direction === 'ltr' || direction === 'rtl') {\n        style = ctx.canvas.style;\n        original = [\n            style.getPropertyValue('direction'),\n            style.getPropertyPriority('direction')\n        ];\n        style.setProperty('direction', direction, 'important');\n        ctx.prevTextDirection = original;\n    }\n}\nfunction restoreTextDirection(ctx, original) {\n    if (original !== undefined) {\n        delete ctx.prevTextDirection;\n        ctx.canvas.style.setProperty('direction', original[0], original[1]);\n    }\n}\n\nfunction propertyFn(property) {\n    if (property === 'angle') {\n        return {\n            between: _angleBetween,\n            compare: _angleDiff,\n            normalize: _normalizeAngle\n        };\n    }\n    return {\n        between: _isBetween,\n        compare: (a, b)=>a - b,\n        normalize: (x)=>x\n    };\n}\nfunction normalizeSegment({ start , end , count , loop , style  }) {\n    return {\n        start: start % count,\n        end: end % count,\n        loop: loop && (end - start + 1) % count === 0,\n        style\n    };\n}\nfunction getSegment(segment, points, bounds) {\n    const { property , start: startBound , end: endBound  } = bounds;\n    const { between , normalize  } = propertyFn(property);\n    const count = points.length;\n    let { start , end , loop  } = segment;\n    let i, ilen;\n    if (loop) {\n        start += count;\n        end += count;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            if (!between(normalize(points[start % count][property]), startBound, endBound)) {\n                break;\n            }\n            start--;\n            end--;\n        }\n        start %= count;\n        end %= count;\n    }\n    if (end < start) {\n        end += count;\n    }\n    return {\n        start,\n        end,\n        loop,\n        style: segment.style\n    };\n}\n function _boundSegment(segment, points, bounds) {\n    if (!bounds) {\n        return [\n            segment\n        ];\n    }\n    const { property , start: startBound , end: endBound  } = bounds;\n    const count = points.length;\n    const { compare , between , normalize  } = propertyFn(property);\n    const { start , end , loop , style  } = getSegment(segment, points, bounds);\n    const result = [];\n    let inside = false;\n    let subStart = null;\n    let value, point, prevValue;\n    const startIsBefore = ()=>between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;\n    const endIsBefore = ()=>compare(endBound, value) === 0 || between(endBound, prevValue, value);\n    const shouldStart = ()=>inside || startIsBefore();\n    const shouldStop = ()=>!inside || endIsBefore();\n    for(let i = start, prev = start; i <= end; ++i){\n        point = points[i % count];\n        if (point.skip) {\n            continue;\n        }\n        value = normalize(point[property]);\n        if (value === prevValue) {\n            continue;\n        }\n        inside = between(value, startBound, endBound);\n        if (subStart === null && shouldStart()) {\n            subStart = compare(value, startBound) === 0 ? i : prev;\n        }\n        if (subStart !== null && shouldStop()) {\n            result.push(normalizeSegment({\n                start: subStart,\n                end: i,\n                loop,\n                count,\n                style\n            }));\n            subStart = null;\n        }\n        prev = i;\n        prevValue = value;\n    }\n    if (subStart !== null) {\n        result.push(normalizeSegment({\n            start: subStart,\n            end,\n            loop,\n            count,\n            style\n        }));\n    }\n    return result;\n}\n function _boundSegments(line, bounds) {\n    const result = [];\n    const segments = line.segments;\n    for(let i = 0; i < segments.length; i++){\n        const sub = _boundSegment(segments[i], line.points, bounds);\n        if (sub.length) {\n            result.push(...sub);\n        }\n    }\n    return result;\n}\n function findStartAndEnd(points, count, loop, spanGaps) {\n    let start = 0;\n    let end = count - 1;\n    if (loop && !spanGaps) {\n        while(start < count && !points[start].skip){\n            start++;\n        }\n    }\n    while(start < count && points[start].skip){\n        start++;\n    }\n    start %= count;\n    if (loop) {\n        end += start;\n    }\n    while(end > start && points[end % count].skip){\n        end--;\n    }\n    end %= count;\n    return {\n        start,\n        end\n    };\n}\n function solidSegments(points, start, max, loop) {\n    const count = points.length;\n    const result = [];\n    let last = start;\n    let prev = points[start];\n    let end;\n    for(end = start + 1; end <= max; ++end){\n        const cur = points[end % count];\n        if (cur.skip || cur.stop) {\n            if (!prev.skip) {\n                loop = false;\n                result.push({\n                    start: start % count,\n                    end: (end - 1) % count,\n                    loop\n                });\n                start = last = cur.stop ? end : null;\n            }\n        } else {\n            last = end;\n            if (prev.skip) {\n                start = end;\n            }\n        }\n        prev = cur;\n    }\n    if (last !== null) {\n        result.push({\n            start: start % count,\n            end: last % count,\n            loop\n        });\n    }\n    return result;\n}\n function _computeSegments(line, segmentOptions) {\n    const points = line.points;\n    const spanGaps = line.options.spanGaps;\n    const count = points.length;\n    if (!count) {\n        return [];\n    }\n    const loop = !!line._loop;\n    const { start , end  } = findStartAndEnd(points, count, loop, spanGaps);\n    if (spanGaps === true) {\n        return splitByStyles(line, [\n            {\n                start,\n                end,\n                loop\n            }\n        ], points, segmentOptions);\n    }\n    const max = end < start ? end + count : end;\n    const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;\n    return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);\n}\n function splitByStyles(line, segments, points, segmentOptions) {\n    if (!segmentOptions || !segmentOptions.setContext || !points) {\n        return segments;\n    }\n    return doSplitByStyles(line, segments, points, segmentOptions);\n}\n function doSplitByStyles(line, segments, points, segmentOptions) {\n    const chartContext = line._chart.getContext();\n    const baseStyle = readStyle(line.options);\n    const { _datasetIndex: datasetIndex , options: { spanGaps  }  } = line;\n    const count = points.length;\n    const result = [];\n    let prevStyle = baseStyle;\n    let start = segments[0].start;\n    let i = start;\n    function addStyle(s, e, l, st) {\n        const dir = spanGaps ? -1 : 1;\n        if (s === e) {\n            return;\n        }\n        s += count;\n        while(points[s % count].skip){\n            s -= dir;\n        }\n        while(points[e % count].skip){\n            e += dir;\n        }\n        if (s % count !== e % count) {\n            result.push({\n                start: s % count,\n                end: e % count,\n                loop: l,\n                style: st\n            });\n            prevStyle = st;\n            start = e % count;\n        }\n    }\n    for (const segment of segments){\n        start = spanGaps ? start : segment.start;\n        let prev = points[start % count];\n        let style;\n        for(i = start + 1; i <= segment.end; i++){\n            const pt = points[i % count];\n            style = readStyle(segmentOptions.setContext(createContext(chartContext, {\n                type: 'segment',\n                p0: prev,\n                p1: pt,\n                p0DataIndex: (i - 1) % count,\n                p1DataIndex: i % count,\n                datasetIndex\n            })));\n            if (styleChanged(style, prevStyle)) {\n                addStyle(start, i - 1, segment.loop, prevStyle);\n            }\n            prev = pt;\n            prevStyle = style;\n        }\n        if (start < i - 1) {\n            addStyle(start, i - 1, segment.loop, prevStyle);\n        }\n    }\n    return result;\n}\nfunction readStyle(options) {\n    return {\n        backgroundColor: options.backgroundColor,\n        borderCapStyle: options.borderCapStyle,\n        borderDash: options.borderDash,\n        borderDashOffset: options.borderDashOffset,\n        borderJoinStyle: options.borderJoinStyle,\n        borderWidth: options.borderWidth,\n        borderColor: options.borderColor\n    };\n}\nfunction styleChanged(style, prevStyle) {\n    return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle);\n}\n\nexports.HALF_PI = HALF_PI;\nexports.INFINITY = INFINITY;\nexports.PI = PI;\nexports.PITAU = PITAU;\nexports.QUARTER_PI = QUARTER_PI;\nexports.RAD_PER_DEG = RAD_PER_DEG;\nexports.TAU = TAU;\nexports.TWO_THIRDS_PI = TWO_THIRDS_PI;\nexports.Ticks = Ticks;\nexports._addGrace = _addGrace;\nexports._alignPixel = _alignPixel;\nexports._alignStartEnd = _alignStartEnd;\nexports._angleBetween = _angleBetween;\nexports._angleDiff = _angleDiff;\nexports._arrayUnique = _arrayUnique;\nexports._attachContext = _attachContext;\nexports._bezierCurveTo = _bezierCurveTo;\nexports._bezierInterpolation = _bezierInterpolation;\nexports._boundSegment = _boundSegment;\nexports._boundSegments = _boundSegments;\nexports._capitalize = _capitalize;\nexports._computeSegments = _computeSegments;\nexports._createResolver = _createResolver;\nexports._decimalPlaces = _decimalPlaces;\nexports._deprecated = _deprecated;\nexports._descriptors = _descriptors;\nexports._elementsEqual = _elementsEqual;\nexports._factorize = _factorize;\nexports._filterBetween = _filterBetween;\nexports._getParentNode = _getParentNode;\nexports._getStartAndCountOfVisiblePoints = _getStartAndCountOfVisiblePoints;\nexports._int16Range = _int16Range;\nexports._isBetween = _isBetween;\nexports._isClickEvent = _isClickEvent;\nexports._isDomSupported = _isDomSupported;\nexports._isPointInArea = _isPointInArea;\nexports._limitValue = _limitValue;\nexports._longestText = _longestText;\nexports._lookup = _lookup;\nexports._lookupByKey = _lookupByKey;\nexports._measureText = _measureText;\nexports._merger = _merger;\nexports._mergerIf = _mergerIf;\nexports._normalizeAngle = _normalizeAngle;\nexports._parseObjectDataRadialScale = _parseObjectDataRadialScale;\nexports._pointInLine = _pointInLine;\nexports._readValueToProps = _readValueToProps;\nexports._rlookupByKey = _rlookupByKey;\nexports._scaleRangesChanged = _scaleRangesChanged;\nexports._setMinAndMaxByKey = _setMinAndMaxByKey;\nexports._splitKey = _splitKey;\nexports._steppedInterpolation = _steppedInterpolation;\nexports._steppedLineTo = _steppedLineTo;\nexports._textX = _textX;\nexports._toLeftRightCenter = _toLeftRightCenter;\nexports._updateBezierControlPoints = _updateBezierControlPoints;\nexports.addRoundedRectPath = addRoundedRectPath;\nexports.almostEquals = almostEquals;\nexports.almostWhole = almostWhole;\nexports.callback = callback;\nexports.clearCanvas = clearCanvas;\nexports.clipArea = clipArea;\nexports.clone = clone;\nexports.color = color;\nexports.createContext = createContext;\nexports.debounce = debounce;\nexports.defaults = defaults;\nexports.defined = defined;\nexports.descriptors = descriptors;\nexports.distanceBetweenPoints = distanceBetweenPoints;\nexports.drawPoint = drawPoint;\nexports.drawPointLegend = drawPointLegend;\nexports.each = each;\nexports.effects = effects;\nexports.finiteOrDefault = finiteOrDefault;\nexports.fontString = fontString;\nexports.formatNumber = formatNumber;\nexports.getAngleFromPoint = getAngleFromPoint;\nexports.getHoverColor = getHoverColor;\nexports.getMaximumSize = getMaximumSize;\nexports.getRelativePosition = getRelativePosition;\nexports.getRtlAdapter = getRtlAdapter;\nexports.getStyle = getStyle;\nexports.isArray = isArray;\nexports.isFunction = isFunction;\nexports.isNullOrUndef = isNullOrUndef;\nexports.isNumber = isNumber;\nexports.isNumberFinite = isNumberFinite;\nexports.isObject = isObject;\nexports.isPatternOrGradient = isPatternOrGradient;\nexports.listenArrayEvents = listenArrayEvents;\nexports.log10 = log10;\nexports.merge = merge;\nexports.mergeIf = mergeIf;\nexports.niceNum = niceNum;\nexports.noop = noop;\nexports.overrideTextDirection = overrideTextDirection;\nexports.overrides = overrides;\nexports.readUsedSize = readUsedSize;\nexports.renderText = renderText;\nexports.requestAnimFrame = requestAnimFrame;\nexports.resolve = resolve;\nexports.resolveObjectKey = resolveObjectKey;\nexports.restoreTextDirection = restoreTextDirection;\nexports.retinaScale = retinaScale;\nexports.setsEqual = setsEqual;\nexports.sign = sign;\nexports.splineCurve = splineCurve;\nexports.splineCurveMonotone = splineCurveMonotone;\nexports.supportsEventListenerOptions = supportsEventListenerOptions;\nexports.throttled = throttled;\nexports.toDegrees = toDegrees;\nexports.toDimension = toDimension;\nexports.toFont = toFont;\nexports.toFontString = toFontString;\nexports.toLineHeight = toLineHeight;\nexports.toPadding = toPadding;\nexports.toPercentage = toPercentage;\nexports.toRadians = toRadians;\nexports.toTRBL = toTRBL;\nexports.toTRBLCorners = toTRBLCorners;\nexports.uid = uid;\nexports.unclipArea = unclipArea;\nexports.unlistenArrayEvents = unlistenArrayEvents;\nexports.valueOrDefault = valueOrDefault;\n//# sourceMappingURL=helpers.segment.cjs.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/chunks/helpers.segment.js",
    "content": "/*!\n * Chart.js v4.2.1\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\nimport { Color } from '@kurkle/color';\n\n/**\n * @namespace Chart.helpers\n */ /**\n * An empty function that can be used, for example, for optional callback.\n */ function noop() {\n/* noop */ }\n/**\n * Returns a unique id, sequentially generated from a global variable.\n */ const uid = (()=>{\n    let id = 0;\n    return ()=>id++;\n})();\n/**\n * Returns true if `value` is neither null nor undefined, else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */ function isNullOrUndef(value) {\n    return value === null || typeof value === 'undefined';\n}\n/**\n * Returns true if `value` is an array (including typed arrays), else returns false.\n * @param value - The value to test.\n * @function\n */ function isArray(value) {\n    if (Array.isArray && Array.isArray(value)) {\n        return true;\n    }\n    const type = Object.prototype.toString.call(value);\n    if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {\n        return true;\n    }\n    return false;\n}\n/**\n * Returns true if `value` is an object (excluding null), else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */ function isObject(value) {\n    return value !== null && Object.prototype.toString.call(value) === '[object Object]';\n}\n/**\n * Returns true if `value` is a finite number, else returns false\n * @param value  - The value to test.\n */ function isNumberFinite(value) {\n    return (typeof value === 'number' || value instanceof Number) && isFinite(+value);\n}\n/**\n * Returns `value` if finite, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is not finite.\n */ function finiteOrDefault(value, defaultValue) {\n    return isNumberFinite(value) ? value : defaultValue;\n}\n/**\n * Returns `value` if defined, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is undefined.\n */ function valueOrDefault(value, defaultValue) {\n    return typeof value === 'undefined' ? defaultValue : value;\n}\nconst toPercentage = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 : +value / dimension;\nconst toDimension = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value;\n/**\n * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n * @param fn - The function to call.\n * @param args - The arguments with which `fn` should be called.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n */ function callback(fn, args, thisArg) {\n    if (fn && typeof fn.call === 'function') {\n        return fn.apply(thisArg, args);\n    }\n}\nfunction each(loopable, fn, thisArg, reverse) {\n    let i, len, keys;\n    if (isArray(loopable)) {\n        len = loopable.length;\n        if (reverse) {\n            for(i = len - 1; i >= 0; i--){\n                fn.call(thisArg, loopable[i], i);\n            }\n        } else {\n            for(i = 0; i < len; i++){\n                fn.call(thisArg, loopable[i], i);\n            }\n        }\n    } else if (isObject(loopable)) {\n        keys = Object.keys(loopable);\n        len = keys.length;\n        for(i = 0; i < len; i++){\n            fn.call(thisArg, loopable[keys[i]], keys[i]);\n        }\n    }\n}\n/**\n * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n * @param a0 - The array to compare\n * @param a1 - The array to compare\n * @private\n */ function _elementsEqual(a0, a1) {\n    let i, ilen, v0, v1;\n    if (!a0 || !a1 || a0.length !== a1.length) {\n        return false;\n    }\n    for(i = 0, ilen = a0.length; i < ilen; ++i){\n        v0 = a0[i];\n        v1 = a1[i];\n        if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {\n            return false;\n        }\n    }\n    return true;\n}\n/**\n * Returns a deep copy of `source` without keeping references on objects and arrays.\n * @param source - The value to clone.\n */ function clone(source) {\n    if (isArray(source)) {\n        return source.map(clone);\n    }\n    if (isObject(source)) {\n        const target = Object.create(null);\n        const keys = Object.keys(source);\n        const klen = keys.length;\n        let k = 0;\n        for(; k < klen; ++k){\n            target[keys[k]] = clone(source[keys[k]]);\n        }\n        return target;\n    }\n    return source;\n}\nfunction isValidKey(key) {\n    return [\n        '__proto__',\n        'prototype',\n        'constructor'\n    ].indexOf(key) === -1;\n}\n/**\n * The default merger when Chart.helpers.merge is called without merger option.\n * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.\n * @private\n */ function _merger(key, target, source, options) {\n    if (!isValidKey(key)) {\n        return;\n    }\n    const tval = target[key];\n    const sval = source[key];\n    if (isObject(tval) && isObject(sval)) {\n        // eslint-disable-next-line @typescript-eslint/no-use-before-define\n        merge(tval, sval, options);\n    } else {\n        target[key] = clone(sval);\n    }\n}\nfunction merge(target, source, options) {\n    const sources = isArray(source) ? source : [\n        source\n    ];\n    const ilen = sources.length;\n    if (!isObject(target)) {\n        return target;\n    }\n    options = options || {};\n    const merger = options.merger || _merger;\n    let current;\n    for(let i = 0; i < ilen; ++i){\n        current = sources[i];\n        if (!isObject(current)) {\n            continue;\n        }\n        const keys = Object.keys(current);\n        for(let k = 0, klen = keys.length; k < klen; ++k){\n            merger(keys[k], target, current, options);\n        }\n    }\n    return target;\n}\nfunction mergeIf(target, source) {\n    // eslint-disable-next-line @typescript-eslint/no-use-before-define\n    return merge(target, source, {\n        merger: _mergerIf\n    });\n}\n/**\n * Merges source[key] in target[key] only if target[key] is undefined.\n * @private\n */ function _mergerIf(key, target, source) {\n    if (!isValidKey(key)) {\n        return;\n    }\n    const tval = target[key];\n    const sval = source[key];\n    if (isObject(tval) && isObject(sval)) {\n        mergeIf(tval, sval);\n    } else if (!Object.prototype.hasOwnProperty.call(target, key)) {\n        target[key] = clone(sval);\n    }\n}\n/**\n * @private\n */ function _deprecated(scope, value, previous, current) {\n    if (value !== undefined) {\n        console.warn(scope + ': \"' + previous + '\" is deprecated. Please use \"' + current + '\" instead');\n    }\n}\n// resolveObjectKey resolver cache\nconst keyResolvers = {\n    // Chart.helpers.core resolveObjectKey should resolve empty key to root object\n    '': (v)=>v,\n    // default resolvers\n    x: (o)=>o.x,\n    y: (o)=>o.y\n};\n/**\n * @private\n */ function _splitKey(key) {\n    const parts = key.split('.');\n    const keys = [];\n    let tmp = '';\n    for (const part of parts){\n        tmp += part;\n        if (tmp.endsWith('\\\\')) {\n            tmp = tmp.slice(0, -1) + '.';\n        } else {\n            keys.push(tmp);\n            tmp = '';\n        }\n    }\n    return keys;\n}\nfunction _getKeyResolver(key) {\n    const keys = _splitKey(key);\n    return (obj)=>{\n        for (const k of keys){\n            if (k === '') {\n                break;\n            }\n            obj = obj && obj[k];\n        }\n        return obj;\n    };\n}\nfunction resolveObjectKey(obj, key) {\n    const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));\n    return resolver(obj);\n}\n/**\n * @private\n */ function _capitalize(str) {\n    return str.charAt(0).toUpperCase() + str.slice(1);\n}\nconst defined = (value)=>typeof value !== 'undefined';\nconst isFunction = (value)=>typeof value === 'function';\n// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384\nconst setsEqual = (a, b)=>{\n    if (a.size !== b.size) {\n        return false;\n    }\n    for (const item of a){\n        if (!b.has(item)) {\n            return false;\n        }\n    }\n    return true;\n};\n/**\n * @param e - The event\n * @private\n */ function _isClickEvent(e) {\n    return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';\n}\n\n/**\n * @alias Chart.helpers.math\n * @namespace\n */ const PI = Math.PI;\nconst TAU = 2 * PI;\nconst PITAU = TAU + PI;\nconst INFINITY = Number.POSITIVE_INFINITY;\nconst RAD_PER_DEG = PI / 180;\nconst HALF_PI = PI / 2;\nconst QUARTER_PI = PI / 4;\nconst TWO_THIRDS_PI = PI * 2 / 3;\nconst log10 = Math.log10;\nconst sign = Math.sign;\nfunction almostEquals(x, y, epsilon) {\n    return Math.abs(x - y) < epsilon;\n}\n/**\n * Implementation of the nice number algorithm used in determining where axis labels will go\n */ function niceNum(range) {\n    const roundedRange = Math.round(range);\n    range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;\n    const niceRange = Math.pow(10, Math.floor(log10(range)));\n    const fraction = range / niceRange;\n    const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;\n    return niceFraction * niceRange;\n}\n/**\n * Returns an array of factors sorted from 1 to sqrt(value)\n * @private\n */ function _factorize(value) {\n    const result = [];\n    const sqrt = Math.sqrt(value);\n    let i;\n    for(i = 1; i < sqrt; i++){\n        if (value % i === 0) {\n            result.push(i);\n            result.push(value / i);\n        }\n    }\n    if (sqrt === (sqrt | 0)) {\n        result.push(sqrt);\n    }\n    result.sort((a, b)=>a - b).pop();\n    return result;\n}\nfunction isNumber(n) {\n    return !isNaN(parseFloat(n)) && isFinite(n);\n}\nfunction almostWhole(x, epsilon) {\n    const rounded = Math.round(x);\n    return rounded - epsilon <= x && rounded + epsilon >= x;\n}\n/**\n * @private\n */ function _setMinAndMaxByKey(array, target, property) {\n    let i, ilen, value;\n    for(i = 0, ilen = array.length; i < ilen; i++){\n        value = array[i][property];\n        if (!isNaN(value)) {\n            target.min = Math.min(target.min, value);\n            target.max = Math.max(target.max, value);\n        }\n    }\n}\nfunction toRadians(degrees) {\n    return degrees * (PI / 180);\n}\nfunction toDegrees(radians) {\n    return radians * (180 / PI);\n}\n/**\n * Returns the number of decimal places\n * i.e. the number of digits after the decimal point, of the value of this Number.\n * @param x - A number.\n * @returns The number of decimal places.\n * @private\n */ function _decimalPlaces(x) {\n    if (!isNumberFinite(x)) {\n        return;\n    }\n    let e = 1;\n    let p = 0;\n    while(Math.round(x * e) / e !== x){\n        e *= 10;\n        p++;\n    }\n    return p;\n}\n// Gets the angle from vertical upright to the point about a centre.\nfunction getAngleFromPoint(centrePoint, anglePoint) {\n    const distanceFromXCenter = anglePoint.x - centrePoint.x;\n    const distanceFromYCenter = anglePoint.y - centrePoint.y;\n    const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);\n    let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);\n    if (angle < -0.5 * PI) {\n        angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]\n    }\n    return {\n        angle,\n        distance: radialDistanceFromCenter\n    };\n}\nfunction distanceBetweenPoints(pt1, pt2) {\n    return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));\n}\n/**\n * Shortest distance between angles, in either direction.\n * @private\n */ function _angleDiff(a, b) {\n    return (a - b + PITAU) % TAU - PI;\n}\n/**\n * Normalize angle to be between 0 and 2*PI\n * @private\n */ function _normalizeAngle(a) {\n    return (a % TAU + TAU) % TAU;\n}\n/**\n * @private\n */ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {\n    const a = _normalizeAngle(angle);\n    const s = _normalizeAngle(start);\n    const e = _normalizeAngle(end);\n    const angleToStart = _normalizeAngle(s - a);\n    const angleToEnd = _normalizeAngle(e - a);\n    const startToAngle = _normalizeAngle(a - s);\n    const endToAngle = _normalizeAngle(a - e);\n    return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;\n}\n/**\n * Limit `value` between `min` and `max`\n * @param value\n * @param min\n * @param max\n * @private\n */ function _limitValue(value, min, max) {\n    return Math.max(min, Math.min(max, value));\n}\n/**\n * @param {number} value\n * @private\n */ function _int16Range(value) {\n    return _limitValue(value, -32768, 32767);\n}\n/**\n * @param value\n * @param start\n * @param end\n * @param [epsilon]\n * @private\n */ function _isBetween(value, start, end, epsilon = 1e-6) {\n    return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;\n}\n\nfunction _lookup(table, value, cmp) {\n    cmp = cmp || ((index)=>table[index] < value);\n    let hi = table.length - 1;\n    let lo = 0;\n    let mid;\n    while(hi - lo > 1){\n        mid = lo + hi >> 1;\n        if (cmp(mid)) {\n            lo = mid;\n        } else {\n            hi = mid;\n        }\n    }\n    return {\n        lo,\n        hi\n    };\n}\n/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @param last - lookup last index\n * @private\n */ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{\n        const ti = table[index][key];\n        return ti < value || ti === value && table[index + 1][key] === value;\n    } : (index)=>table[index][key] < value);\n/**\n * Reverse binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @private\n */ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value);\n/**\n * Return subset of `values` between `min` and `max` inclusive.\n * Values are assumed to be in sorted order.\n * @param values - sorted array of values\n * @param min - min value\n * @param max - max value\n */ function _filterBetween(values, min, max) {\n    let start = 0;\n    let end = values.length;\n    while(start < end && values[start] < min){\n        start++;\n    }\n    while(end > start && values[end - 1] > max){\n        end--;\n    }\n    return start > 0 || end < values.length ? values.slice(start, end) : values;\n}\nconst arrayEvents = [\n    'push',\n    'pop',\n    'shift',\n    'splice',\n    'unshift'\n];\nfunction listenArrayEvents(array, listener) {\n    if (array._chartjs) {\n        array._chartjs.listeners.push(listener);\n        return;\n    }\n    Object.defineProperty(array, '_chartjs', {\n        configurable: true,\n        enumerable: false,\n        value: {\n            listeners: [\n                listener\n            ]\n        }\n    });\n    arrayEvents.forEach((key)=>{\n        const method = '_onData' + _capitalize(key);\n        const base = array[key];\n        Object.defineProperty(array, key, {\n            configurable: true,\n            enumerable: false,\n            value (...args) {\n                const res = base.apply(this, args);\n                array._chartjs.listeners.forEach((object)=>{\n                    if (typeof object[method] === 'function') {\n                        object[method](...args);\n                    }\n                });\n                return res;\n            }\n        });\n    });\n}\nfunction unlistenArrayEvents(array, listener) {\n    const stub = array._chartjs;\n    if (!stub) {\n        return;\n    }\n    const listeners = stub.listeners;\n    const index = listeners.indexOf(listener);\n    if (index !== -1) {\n        listeners.splice(index, 1);\n    }\n    if (listeners.length > 0) {\n        return;\n    }\n    arrayEvents.forEach((key)=>{\n        delete array[key];\n    });\n    delete array._chartjs;\n}\n/**\n * @param items\n */ function _arrayUnique(items) {\n    const set = new Set();\n    let i, ilen;\n    for(i = 0, ilen = items.length; i < ilen; ++i){\n        set.add(items[i]);\n    }\n    if (set.size === ilen) {\n        return items;\n    }\n    return Array.from(set);\n}\n\nfunction fontString(pixelSize, fontStyle, fontFamily) {\n    return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;\n}\n/**\n* Request animation polyfill\n*/ const requestAnimFrame = function() {\n    if (typeof window === 'undefined') {\n        return function(callback) {\n            return callback();\n        };\n    }\n    return window.requestAnimationFrame;\n}();\n/**\n * Throttles calling `fn` once per animation frame\n * Latest arguments are used on the actual call\n */ function throttled(fn, thisArg) {\n    let argsToUse = [];\n    let ticking = false;\n    return function(...args) {\n        // Save the args for use later\n        argsToUse = args;\n        if (!ticking) {\n            ticking = true;\n            requestAnimFrame.call(window, ()=>{\n                ticking = false;\n                fn.apply(thisArg, argsToUse);\n            });\n        }\n    };\n}\n/**\n * Debounces calling `fn` for `delay` ms\n */ function debounce(fn, delay) {\n    let timeout;\n    return function(...args) {\n        if (delay) {\n            clearTimeout(timeout);\n            timeout = setTimeout(fn, delay, args);\n        } else {\n            fn.apply(this, args);\n        }\n        return delay;\n    };\n}\n/**\n * Converts 'start' to 'left', 'end' to 'right' and others to 'center'\n * @private\n */ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';\n/**\n * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`\n * @private\n */ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2;\n/**\n * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`\n * @private\n */ const _textX = (align, left, right, rtl)=>{\n    const check = rtl ? 'left' : 'right';\n    return align === check ? right : align === 'center' ? (left + right) / 2 : left;\n};\n/**\n * Return start and count of visible points.\n * @private\n */ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {\n    const pointCount = points.length;\n    let start = 0;\n    let count = pointCount;\n    if (meta._sorted) {\n        const { iScale , _parsed  } = meta;\n        const axis = iScale.axis;\n        const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();\n        if (minDefined) {\n            start = _limitValue(Math.min(// @ts-expect-error Need to type _parsed\n            _lookupByKey(_parsed, iScale.axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey\n            animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), 0, pointCount - 1);\n        }\n        if (maxDefined) {\n            count = _limitValue(Math.max(// @ts-expect-error Need to type _parsed\n            _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey\n            animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1), start, pointCount) - start;\n        } else {\n            count = pointCount - start;\n        }\n    }\n    return {\n        start,\n        count\n    };\n}\n/**\n * Checks if the scale ranges have changed.\n * @param {object} meta - dataset meta.\n * @returns {boolean}\n * @private\n */ function _scaleRangesChanged(meta) {\n    const { xScale , yScale , _scaleRanges  } = meta;\n    const newRanges = {\n        xmin: xScale.min,\n        xmax: xScale.max,\n        ymin: yScale.min,\n        ymax: yScale.max\n    };\n    if (!_scaleRanges) {\n        meta._scaleRanges = newRanges;\n        return true;\n    }\n    const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;\n    Object.assign(_scaleRanges, newRanges);\n    return changed;\n}\n\nconst atEdge = (t)=>t === 0 || t === 1;\nconst elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));\nconst elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;\n/**\n * Easing functions adapted from Robert Penner's easing equations.\n * @namespace Chart.helpers.easing.effects\n * @see http://www.robertpenner.com/easing/\n */ const effects = {\n    linear: (t)=>t,\n    easeInQuad: (t)=>t * t,\n    easeOutQuad: (t)=>-t * (t - 2),\n    easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),\n    easeInCubic: (t)=>t * t * t,\n    easeOutCubic: (t)=>(t -= 1) * t * t + 1,\n    easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),\n    easeInQuart: (t)=>t * t * t * t,\n    easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1),\n    easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),\n    easeInQuint: (t)=>t * t * t * t * t,\n    easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1,\n    easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),\n    easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1,\n    easeOutSine: (t)=>Math.sin(t * HALF_PI),\n    easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1),\n    easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),\n    easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,\n    easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),\n    easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),\n    easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t),\n    easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),\n    easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3),\n    easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3),\n    easeInOutElastic (t) {\n        const s = 0.1125;\n        const p = 0.45;\n        return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);\n    },\n    easeInBack (t) {\n        const s = 1.70158;\n        return t * t * ((s + 1) * t - s);\n    },\n    easeOutBack (t) {\n        const s = 1.70158;\n        return (t -= 1) * t * ((s + 1) * t + s) + 1;\n    },\n    easeInOutBack (t) {\n        let s = 1.70158;\n        if ((t /= 0.5) < 1) {\n            return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));\n        }\n        return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);\n    },\n    easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t),\n    easeOutBounce (t) {\n        const m = 7.5625;\n        const d = 2.75;\n        if (t < 1 / d) {\n            return m * t * t;\n        }\n        if (t < 2 / d) {\n            return m * (t -= 1.5 / d) * t + 0.75;\n        }\n        if (t < 2.5 / d) {\n            return m * (t -= 2.25 / d) * t + 0.9375;\n        }\n        return m * (t -= 2.625 / d) * t + 0.984375;\n    },\n    easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5\n};\n\nfunction isPatternOrGradient(value) {\n    if (value && typeof value === 'object') {\n        const type = value.toString();\n        return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';\n    }\n    return false;\n}\nfunction color(value) {\n    return isPatternOrGradient(value) ? value : new Color(value);\n}\nfunction getHoverColor(value) {\n    return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString();\n}\n\nconst numbers = [\n    'x',\n    'y',\n    'borderWidth',\n    'radius',\n    'tension'\n];\nconst colors = [\n    'color',\n    'borderColor',\n    'backgroundColor'\n];\nfunction applyAnimationsDefaults(defaults) {\n    defaults.set('animation', {\n        delay: undefined,\n        duration: 1000,\n        easing: 'easeOutQuart',\n        fn: undefined,\n        from: undefined,\n        loop: undefined,\n        to: undefined,\n        type: undefined\n    });\n    defaults.describe('animation', {\n        _fallback: false,\n        _indexable: false,\n        _scriptable: (name)=>name !== 'onProgress' && name !== 'onComplete' && name !== 'fn'\n    });\n    defaults.set('animations', {\n        colors: {\n            type: 'color',\n            properties: colors\n        },\n        numbers: {\n            type: 'number',\n            properties: numbers\n        }\n    });\n    defaults.describe('animations', {\n        _fallback: 'animation'\n    });\n    defaults.set('transitions', {\n        active: {\n            animation: {\n                duration: 400\n            }\n        },\n        resize: {\n            animation: {\n                duration: 0\n            }\n        },\n        show: {\n            animations: {\n                colors: {\n                    from: 'transparent'\n                },\n                visible: {\n                    type: 'boolean',\n                    duration: 0\n                }\n            }\n        },\n        hide: {\n            animations: {\n                colors: {\n                    to: 'transparent'\n                },\n                visible: {\n                    type: 'boolean',\n                    easing: 'linear',\n                    fn: (v)=>v | 0\n                }\n            }\n        }\n    });\n}\n\nfunction applyLayoutsDefaults(defaults) {\n    defaults.set('layout', {\n        autoPadding: true,\n        padding: {\n            top: 0,\n            right: 0,\n            bottom: 0,\n            left: 0\n        }\n    });\n}\n\nconst intlCache = new Map();\nfunction getNumberFormat(locale, options) {\n    options = options || {};\n    const cacheKey = locale + JSON.stringify(options);\n    let formatter = intlCache.get(cacheKey);\n    if (!formatter) {\n        formatter = new Intl.NumberFormat(locale, options);\n        intlCache.set(cacheKey, formatter);\n    }\n    return formatter;\n}\nfunction formatNumber(num, locale, options) {\n    return getNumberFormat(locale, options).format(num);\n}\n\nconst formatters = {\n values (value) {\n        return isArray(value) ?  value : '' + value;\n    },\n numeric (tickValue, index, ticks) {\n        if (tickValue === 0) {\n            return '0';\n        }\n        const locale = this.chart.options.locale;\n        let notation;\n        let delta = tickValue;\n        if (ticks.length > 1) {\n            const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));\n            if (maxTick < 1e-4 || maxTick > 1e+15) {\n                notation = 'scientific';\n            }\n            delta = calculateDelta(tickValue, ticks);\n        }\n        const logDelta = log10(Math.abs(delta));\n        const numDecimal = Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);\n        const options = {\n            notation,\n            minimumFractionDigits: numDecimal,\n            maximumFractionDigits: numDecimal\n        };\n        Object.assign(options, this.options.ticks.format);\n        return formatNumber(tickValue, locale, options);\n    },\n logarithmic (tickValue, index, ticks) {\n        if (tickValue === 0) {\n            return '0';\n        }\n        const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));\n        if ([\n            1,\n            2,\n            3,\n            5,\n            10,\n            15\n        ].includes(remain) || index > 0.8 * ticks.length) {\n            return formatters.numeric.call(this, tickValue, index, ticks);\n        }\n        return '';\n    }\n};\nfunction calculateDelta(tickValue, ticks) {\n    let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;\n    if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {\n        delta = tickValue - Math.floor(tickValue);\n    }\n    return delta;\n}\n var Ticks = {\n    formatters\n};\n\nfunction applyScaleDefaults(defaults) {\n    defaults.set('scale', {\n        display: true,\n        offset: false,\n        reverse: false,\n        beginAtZero: false,\n bounds: 'ticks',\n grace: 0,\n        grid: {\n            display: true,\n            lineWidth: 1,\n            drawOnChartArea: true,\n            drawTicks: true,\n            tickLength: 8,\n            tickWidth: (_ctx, options)=>options.lineWidth,\n            tickColor: (_ctx, options)=>options.color,\n            offset: false\n        },\n        border: {\n            display: true,\n            dash: [],\n            dashOffset: 0.0,\n            width: 1\n        },\n        title: {\n            display: false,\n            text: '',\n            padding: {\n                top: 4,\n                bottom: 4\n            }\n        },\n        ticks: {\n            minRotation: 0,\n            maxRotation: 50,\n            mirror: false,\n            textStrokeWidth: 0,\n            textStrokeColor: '',\n            padding: 3,\n            display: true,\n            autoSkip: true,\n            autoSkipPadding: 3,\n            labelOffset: 0,\n            callback: Ticks.formatters.values,\n            minor: {},\n            major: {},\n            align: 'center',\n            crossAlign: 'near',\n            showLabelBackdrop: false,\n            backdropColor: 'rgba(255, 255, 255, 0.75)',\n            backdropPadding: 2\n        }\n    });\n    defaults.route('scale.ticks', 'color', '', 'color');\n    defaults.route('scale.grid', 'color', '', 'borderColor');\n    defaults.route('scale.border', 'color', '', 'borderColor');\n    defaults.route('scale.title', 'color', '', 'color');\n    defaults.describe('scale', {\n        _fallback: false,\n        _scriptable: (name)=>!name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',\n        _indexable: (name)=>name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash'\n    });\n    defaults.describe('scales', {\n        _fallback: 'scale'\n    });\n    defaults.describe('scale.ticks', {\n        _scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback',\n        _indexable: (name)=>name !== 'backdropPadding'\n    });\n}\n\nconst overrides = Object.create(null);\nconst descriptors = Object.create(null);\n function getScope$1(node, key) {\n    if (!key) {\n        return node;\n    }\n    const keys = key.split('.');\n    for(let i = 0, n = keys.length; i < n; ++i){\n        const k = keys[i];\n        node = node[k] || (node[k] = Object.create(null));\n    }\n    return node;\n}\nfunction set(root, scope, values) {\n    if (typeof scope === 'string') {\n        return merge(getScope$1(root, scope), values);\n    }\n    return merge(getScope$1(root, ''), scope);\n}\n class Defaults {\n    constructor(_descriptors, _appliers){\n        this.animation = undefined;\n        this.backgroundColor = 'rgba(0,0,0,0.1)';\n        this.borderColor = 'rgba(0,0,0,0.1)';\n        this.color = '#666';\n        this.datasets = {};\n        this.devicePixelRatio = (context)=>context.chart.platform.getDevicePixelRatio();\n        this.elements = {};\n        this.events = [\n            'mousemove',\n            'mouseout',\n            'click',\n            'touchstart',\n            'touchmove'\n        ];\n        this.font = {\n            family: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n            size: 12,\n            style: 'normal',\n            lineHeight: 1.2,\n            weight: null\n        };\n        this.hover = {};\n        this.hoverBackgroundColor = (ctx, options)=>getHoverColor(options.backgroundColor);\n        this.hoverBorderColor = (ctx, options)=>getHoverColor(options.borderColor);\n        this.hoverColor = (ctx, options)=>getHoverColor(options.color);\n        this.indexAxis = 'x';\n        this.interaction = {\n            mode: 'nearest',\n            intersect: true,\n            includeInvisible: false\n        };\n        this.maintainAspectRatio = true;\n        this.onHover = null;\n        this.onClick = null;\n        this.parsing = true;\n        this.plugins = {};\n        this.responsive = true;\n        this.scale = undefined;\n        this.scales = {};\n        this.showLine = true;\n        this.drawActiveElementsOnTop = true;\n        this.describe(_descriptors);\n        this.apply(_appliers);\n    }\n set(scope, values) {\n        return set(this, scope, values);\n    }\n get(scope) {\n        return getScope$1(this, scope);\n    }\n describe(scope, values) {\n        return set(descriptors, scope, values);\n    }\n    override(scope, values) {\n        return set(overrides, scope, values);\n    }\n route(scope, name, targetScope, targetName) {\n        const scopeObject = getScope$1(this, scope);\n        const targetScopeObject = getScope$1(this, targetScope);\n        const privateName = '_' + name;\n        Object.defineProperties(scopeObject, {\n            [privateName]: {\n                value: scopeObject[name],\n                writable: true\n            },\n            [name]: {\n                enumerable: true,\n                get () {\n                    const local = this[privateName];\n                    const target = targetScopeObject[targetName];\n                    if (isObject(local)) {\n                        return Object.assign({}, target, local);\n                    }\n                    return valueOrDefault(local, target);\n                },\n                set (value) {\n                    this[privateName] = value;\n                }\n            }\n        });\n    }\n    apply(appliers) {\n        appliers.forEach((apply)=>apply(this));\n    }\n}\nvar defaults = /* #__PURE__ */ new Defaults({\n    _scriptable: (name)=>!name.startsWith('on'),\n    _indexable: (name)=>name !== 'events',\n    hover: {\n        _fallback: 'interaction'\n    },\n    interaction: {\n        _scriptable: false,\n        _indexable: false\n    }\n}, [\n    applyAnimationsDefaults,\n    applyLayoutsDefaults,\n    applyScaleDefaults\n]);\n\nfunction toFontString(font) {\n    if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {\n        return null;\n    }\n    return (font.style ? font.style + ' ' : '') + (font.weight ? font.weight + ' ' : '') + font.size + 'px ' + font.family;\n}\n function _measureText(ctx, data, gc, longest, string) {\n    let textWidth = data[string];\n    if (!textWidth) {\n        textWidth = data[string] = ctx.measureText(string).width;\n        gc.push(string);\n    }\n    if (textWidth > longest) {\n        longest = textWidth;\n    }\n    return longest;\n}\n function _longestText(ctx, font, arrayOfThings, cache) {\n    cache = cache || {};\n    let data = cache.data = cache.data || {};\n    let gc = cache.garbageCollect = cache.garbageCollect || [];\n    if (cache.font !== font) {\n        data = cache.data = {};\n        gc = cache.garbageCollect = [];\n        cache.font = font;\n    }\n    ctx.save();\n    ctx.font = font;\n    let longest = 0;\n    const ilen = arrayOfThings.length;\n    let i, j, jlen, thing, nestedThing;\n    for(i = 0; i < ilen; i++){\n        thing = arrayOfThings[i];\n        if (thing !== undefined && thing !== null && isArray(thing) !== true) {\n            longest = _measureText(ctx, data, gc, longest, thing);\n        } else if (isArray(thing)) {\n            for(j = 0, jlen = thing.length; j < jlen; j++){\n                nestedThing = thing[j];\n                if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {\n                    longest = _measureText(ctx, data, gc, longest, nestedThing);\n                }\n            }\n        }\n    }\n    ctx.restore();\n    const gcLen = gc.length / 2;\n    if (gcLen > arrayOfThings.length) {\n        for(i = 0; i < gcLen; i++){\n            delete data[gc[i]];\n        }\n        gc.splice(0, gcLen);\n    }\n    return longest;\n}\n function _alignPixel(chart, pixel, width) {\n    const devicePixelRatio = chart.currentDevicePixelRatio;\n    const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;\n    return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;\n}\n function clearCanvas(canvas, ctx) {\n    ctx = ctx || canvas.getContext('2d');\n    ctx.save();\n    ctx.resetTransform();\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n    ctx.restore();\n}\nfunction drawPoint(ctx, options, x, y) {\n    drawPointLegend(ctx, options, x, y, null);\n}\nfunction drawPointLegend(ctx, options, x, y, w) {\n    let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;\n    const style = options.pointStyle;\n    const rotation = options.rotation;\n    const radius = options.radius;\n    let rad = (rotation || 0) * RAD_PER_DEG;\n    if (style && typeof style === 'object') {\n        type = style.toString();\n        if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {\n            ctx.save();\n            ctx.translate(x, y);\n            ctx.rotate(rad);\n            ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);\n            ctx.restore();\n            return;\n        }\n    }\n    if (isNaN(radius) || radius <= 0) {\n        return;\n    }\n    ctx.beginPath();\n    switch(style){\n        default:\n            if (w) {\n                ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);\n            } else {\n                ctx.arc(x, y, radius, 0, TAU);\n            }\n            ctx.closePath();\n            break;\n        case 'triangle':\n            width = w ? w / 2 : radius;\n            ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n            rad += TWO_THIRDS_PI;\n            ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n            rad += TWO_THIRDS_PI;\n            ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n            ctx.closePath();\n            break;\n        case 'rectRounded':\n            cornerRadius = radius * 0.516;\n            size = radius - cornerRadius;\n            xOffset = Math.cos(rad + QUARTER_PI) * size;\n            xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n            yOffset = Math.sin(rad + QUARTER_PI) * size;\n            yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n            ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);\n            ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);\n            ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);\n            ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);\n            ctx.closePath();\n            break;\n        case 'rect':\n            if (!rotation) {\n                size = Math.SQRT1_2 * radius;\n                width = w ? w / 2 : size;\n                ctx.rect(x - width, y - size, 2 * width, 2 * size);\n                break;\n            }\n            rad += QUARTER_PI;\n         case 'rectRot':\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            ctx.closePath();\n            break;\n        case 'crossRot':\n            rad += QUARTER_PI;\n         case 'cross':\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.moveTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            break;\n        case 'star':\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.moveTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            rad += QUARTER_PI;\n            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n            xOffset = Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n            ctx.moveTo(x - xOffsetW, y - yOffset);\n            ctx.lineTo(x + xOffsetW, y + yOffset);\n            ctx.moveTo(x + yOffsetW, y - xOffset);\n            ctx.lineTo(x - yOffsetW, y + xOffset);\n            break;\n        case 'line':\n            xOffset = w ? w / 2 : Math.cos(rad) * radius;\n            yOffset = Math.sin(rad) * radius;\n            ctx.moveTo(x - xOffset, y - yOffset);\n            ctx.lineTo(x + xOffset, y + yOffset);\n            break;\n        case 'dash':\n            ctx.moveTo(x, y);\n            ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);\n            break;\n        case false:\n            ctx.closePath();\n            break;\n    }\n    ctx.fill();\n    if (options.borderWidth > 0) {\n        ctx.stroke();\n    }\n}\n function _isPointInArea(point, area, margin) {\n    margin = margin || 0.5;\n    return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;\n}\nfunction clipArea(ctx, area) {\n    ctx.save();\n    ctx.beginPath();\n    ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);\n    ctx.clip();\n}\nfunction unclipArea(ctx) {\n    ctx.restore();\n}\n function _steppedLineTo(ctx, previous, target, flip, mode) {\n    if (!previous) {\n        return ctx.lineTo(target.x, target.y);\n    }\n    if (mode === 'middle') {\n        const midpoint = (previous.x + target.x) / 2.0;\n        ctx.lineTo(midpoint, previous.y);\n        ctx.lineTo(midpoint, target.y);\n    } else if (mode === 'after' !== !!flip) {\n        ctx.lineTo(previous.x, target.y);\n    } else {\n        ctx.lineTo(target.x, previous.y);\n    }\n    ctx.lineTo(target.x, target.y);\n}\n function _bezierCurveTo(ctx, previous, target, flip) {\n    if (!previous) {\n        return ctx.lineTo(target.x, target.y);\n    }\n    ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y);\n}\n function renderText(ctx, text, x, y, font, opts = {}) {\n    const lines = isArray(text) ? text : [\n        text\n    ];\n    const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';\n    let i, line;\n    ctx.save();\n    ctx.font = font.string;\n    setRenderOpts(ctx, opts);\n    for(i = 0; i < lines.length; ++i){\n        line = lines[i];\n        if (opts.backdrop) {\n            drawBackdrop(ctx, opts.backdrop);\n        }\n        if (stroke) {\n            if (opts.strokeColor) {\n                ctx.strokeStyle = opts.strokeColor;\n            }\n            if (!isNullOrUndef(opts.strokeWidth)) {\n                ctx.lineWidth = opts.strokeWidth;\n            }\n            ctx.strokeText(line, x, y, opts.maxWidth);\n        }\n        ctx.fillText(line, x, y, opts.maxWidth);\n        decorateText(ctx, x, y, line, opts);\n        y += font.lineHeight;\n    }\n    ctx.restore();\n}\nfunction setRenderOpts(ctx, opts) {\n    if (opts.translation) {\n        ctx.translate(opts.translation[0], opts.translation[1]);\n    }\n    if (!isNullOrUndef(opts.rotation)) {\n        ctx.rotate(opts.rotation);\n    }\n    if (opts.color) {\n        ctx.fillStyle = opts.color;\n    }\n    if (opts.textAlign) {\n        ctx.textAlign = opts.textAlign;\n    }\n    if (opts.textBaseline) {\n        ctx.textBaseline = opts.textBaseline;\n    }\n}\nfunction decorateText(ctx, x, y, line, opts) {\n    if (opts.strikethrough || opts.underline) {\n const metrics = ctx.measureText(line);\n        const left = x - metrics.actualBoundingBoxLeft;\n        const right = x + metrics.actualBoundingBoxRight;\n        const top = y - metrics.actualBoundingBoxAscent;\n        const bottom = y + metrics.actualBoundingBoxDescent;\n        const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;\n        ctx.strokeStyle = ctx.fillStyle;\n        ctx.beginPath();\n        ctx.lineWidth = opts.decorationWidth || 2;\n        ctx.moveTo(left, yDecoration);\n        ctx.lineTo(right, yDecoration);\n        ctx.stroke();\n    }\n}\nfunction drawBackdrop(ctx, opts) {\n    const oldColor = ctx.fillStyle;\n    ctx.fillStyle = opts.color;\n    ctx.fillRect(opts.left, opts.top, opts.width, opts.height);\n    ctx.fillStyle = oldColor;\n}\n function addRoundedRectPath(ctx, rect) {\n    const { x , y , w , h , radius  } = rect;\n    ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_PI, PI, true);\n    ctx.lineTo(x, y + h - radius.bottomLeft);\n    ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);\n    ctx.lineTo(x + w - radius.bottomRight, y + h);\n    ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);\n    ctx.lineTo(x + w, y + radius.topRight);\n    ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);\n    ctx.lineTo(x + radius.topLeft, y);\n}\n\nconst LINE_HEIGHT = /^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/;\nconst FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;\n/**\n * @alias Chart.helpers.options\n * @namespace\n */ /**\n * Converts the given line height `value` in pixels for a specific font `size`.\n * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n * @param size - The font size (in pixels) used to resolve relative `value`.\n * @returns The effective line height in pixels (size * 1.2 if value is invalid).\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n * @since 2.7.0\n */ function toLineHeight(value, size) {\n    const matches = ('' + value).match(LINE_HEIGHT);\n    if (!matches || matches[1] === 'normal') {\n        return size * 1.2;\n    }\n    value = +matches[2];\n    switch(matches[3]){\n        case 'px':\n            return value;\n        case '%':\n            value /= 100;\n            break;\n    }\n    return size * value;\n}\nconst numberOrZero = (v)=>+v || 0;\nfunction _readValueToProps(value, props) {\n    const ret = {};\n    const objProps = isObject(props);\n    const keys = objProps ? Object.keys(props) : props;\n    const read = isObject(value) ? objProps ? (prop)=>valueOrDefault(value[prop], value[props[prop]]) : (prop)=>value[prop] : ()=>value;\n    for (const prop of keys){\n        ret[prop] = numberOrZero(read(prop));\n    }\n    return ret;\n}\n/**\n * Converts the given value into a TRBL object.\n * @param value - If a number, set the value to all TRBL component,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n *  x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left)\n * @since 3.0.0\n */ function toTRBL(value) {\n    return _readValueToProps(value, {\n        top: 'y',\n        right: 'x',\n        bottom: 'y',\n        left: 'x'\n    });\n}\n/**\n * Converts the given value into a TRBL corners object (similar with css border-radius).\n * @param value - If a number, set the value to all TRBL corner components,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)\n * @since 3.0.0\n */ function toTRBLCorners(value) {\n    return _readValueToProps(value, [\n        'topLeft',\n        'topRight',\n        'bottomLeft',\n        'bottomRight'\n    ]);\n}\n/**\n * Converts the given value into a padding object with pre-computed width/height.\n * @param value - If a number, set the value to all TRBL component,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n *  x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left, width, height)\n * @since 2.7.0\n */ function toPadding(value) {\n    const obj = toTRBL(value);\n    obj.width = obj.left + obj.right;\n    obj.height = obj.top + obj.bottom;\n    return obj;\n}\n/**\n * Parses font options and returns the font object.\n * @param options - A object that contains font options to be parsed.\n * @param fallback - A object that contains fallback font options.\n * @return The font object.\n * @private\n */ function toFont(options, fallback) {\n    options = options || {};\n    fallback = fallback || defaults.font;\n    let size = valueOrDefault(options.size, fallback.size);\n    if (typeof size === 'string') {\n        size = parseInt(size, 10);\n    }\n    let style = valueOrDefault(options.style, fallback.style);\n    if (style && !('' + style).match(FONT_STYLE)) {\n        console.warn('Invalid font style specified: \"' + style + '\"');\n        style = undefined;\n    }\n    const font = {\n        family: valueOrDefault(options.family, fallback.family),\n        lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),\n        size,\n        style,\n        weight: valueOrDefault(options.weight, fallback.weight),\n        string: ''\n    };\n    font.string = toFontString(font);\n    return font;\n}\n/**\n * Evaluates the given `inputs` sequentially and returns the first defined value.\n * @param inputs - An array of values, falling back to the last value.\n * @param context - If defined and the current value is a function, the value\n * is called with `context` as first argument and the result becomes the new input.\n * @param index - If defined and the current value is an array, the value\n * at `index` become the new input.\n * @param info - object to return information about resolution in\n * @param info.cacheable - Will be set to `false` if option is not cacheable.\n * @since 2.7.0\n */ function resolve(inputs, context, index, info) {\n    let cacheable = true;\n    let i, ilen, value;\n    for(i = 0, ilen = inputs.length; i < ilen; ++i){\n        value = inputs[i];\n        if (value === undefined) {\n            continue;\n        }\n        if (context !== undefined && typeof value === 'function') {\n            value = value(context);\n            cacheable = false;\n        }\n        if (index !== undefined && isArray(value)) {\n            value = value[index % value.length];\n            cacheable = false;\n        }\n        if (value !== undefined) {\n            if (info && !cacheable) {\n                info.cacheable = false;\n            }\n            return value;\n        }\n    }\n}\n/**\n * @param minmax\n * @param grace\n * @param beginAtZero\n * @private\n */ function _addGrace(minmax, grace, beginAtZero) {\n    const { min , max  } = minmax;\n    const change = toDimension(grace, (max - min) / 2);\n    const keepZero = (value, add)=>beginAtZero && value === 0 ? 0 : value + add;\n    return {\n        min: keepZero(min, -Math.abs(change)),\n        max: keepZero(max, change)\n    };\n}\nfunction createContext(parentContext, context) {\n    return Object.assign(Object.create(parentContext), context);\n}\n\nfunction _createResolver(scopes, prefixes = [\n    ''\n], rootScopes = scopes, fallback, getTarget = ()=>scopes[0]) {\n    if (!defined(fallback)) {\n        fallback = _resolve('_fallback', scopes);\n    }\n    const cache = {\n        [Symbol.toStringTag]: 'Object',\n        _cacheable: true,\n        _scopes: scopes,\n        _rootScopes: rootScopes,\n        _fallback: fallback,\n        _getTarget: getTarget,\n        override: (scope)=>_createResolver([\n                scope,\n                ...scopes\n            ], prefixes, rootScopes, fallback)\n    };\n    return new Proxy(cache, {\n deleteProperty (target, prop) {\n            delete target[prop];\n            delete target._keys;\n            delete scopes[0][prop];\n            return true;\n        },\n get (target, prop) {\n            return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target));\n        },\n getOwnPropertyDescriptor (target, prop) {\n            return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);\n        },\n getPrototypeOf () {\n            return Reflect.getPrototypeOf(scopes[0]);\n        },\n has (target, prop) {\n            return getKeysFromAllScopes(target).includes(prop);\n        },\n ownKeys (target) {\n            return getKeysFromAllScopes(target);\n        },\n set (target, prop, value) {\n            const storage = target._storage || (target._storage = getTarget());\n            target[prop] = storage[prop] = value;\n            delete target._keys;\n            return true;\n        }\n    });\n}\n function _attachContext(proxy, context, subProxy, descriptorDefaults) {\n    const cache = {\n        _cacheable: false,\n        _proxy: proxy,\n        _context: context,\n        _subProxy: subProxy,\n        _stack: new Set(),\n        _descriptors: _descriptors(proxy, descriptorDefaults),\n        setContext: (ctx)=>_attachContext(proxy, ctx, subProxy, descriptorDefaults),\n        override: (scope)=>_attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)\n    };\n    return new Proxy(cache, {\n deleteProperty (target, prop) {\n            delete target[prop];\n            delete proxy[prop];\n            return true;\n        },\n get (target, prop, receiver) {\n            return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver));\n        },\n getOwnPropertyDescriptor (target, prop) {\n            return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {\n                enumerable: true,\n                configurable: true\n            } : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop);\n        },\n getPrototypeOf () {\n            return Reflect.getPrototypeOf(proxy);\n        },\n has (target, prop) {\n            return Reflect.has(proxy, prop);\n        },\n ownKeys () {\n            return Reflect.ownKeys(proxy);\n        },\n set (target, prop, value) {\n            proxy[prop] = value;\n            delete target[prop];\n            return true;\n        }\n    });\n}\n function _descriptors(proxy, defaults = {\n    scriptable: true,\n    indexable: true\n}) {\n    const { _scriptable =defaults.scriptable , _indexable =defaults.indexable , _allKeys =defaults.allKeys  } = proxy;\n    return {\n        allKeys: _allKeys,\n        scriptable: _scriptable,\n        indexable: _indexable,\n        isScriptable: isFunction(_scriptable) ? _scriptable : ()=>_scriptable,\n        isIndexable: isFunction(_indexable) ? _indexable : ()=>_indexable\n    };\n}\nconst readKey = (prefix, name)=>prefix ? prefix + _capitalize(name) : name;\nconst needsSubResolver = (prop, value)=>isObject(value) && prop !== 'adapters' && (Object.getPrototypeOf(value) === null || value.constructor === Object);\nfunction _cached(target, prop, resolve) {\n    if (Object.prototype.hasOwnProperty.call(target, prop)) {\n        return target[prop];\n    }\n    const value = resolve();\n    target[prop] = value;\n    return value;\n}\nfunction _resolveWithContext(target, prop, receiver) {\n    const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;\n    let value = _proxy[prop];\n    if (isFunction(value) && descriptors.isScriptable(prop)) {\n        value = _resolveScriptable(prop, value, target, receiver);\n    }\n    if (isArray(value) && value.length) {\n        value = _resolveArray(prop, value, target, descriptors.isIndexable);\n    }\n    if (needsSubResolver(prop, value)) {\n        value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);\n    }\n    return value;\n}\nfunction _resolveScriptable(prop, value, target, receiver) {\n    const { _proxy , _context , _subProxy , _stack  } = target;\n    if (_stack.has(prop)) {\n        throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);\n    }\n    _stack.add(prop);\n    value = value(_context, _subProxy || receiver);\n    _stack.delete(prop);\n    if (needsSubResolver(prop, value)) {\n        value = createSubResolver(_proxy._scopes, _proxy, prop, value);\n    }\n    return value;\n}\nfunction _resolveArray(prop, value, target, isIndexable) {\n    const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;\n    if (defined(_context.index) && isIndexable(prop)) {\n        value = value[_context.index % value.length];\n    } else if (isObject(value[0])) {\n        const arr = value;\n        const scopes = _proxy._scopes.filter((s)=>s !== arr);\n        value = [];\n        for (const item of arr){\n            const resolver = createSubResolver(scopes, _proxy, prop, item);\n            value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));\n        }\n    }\n    return value;\n}\nfunction resolveFallback(fallback, prop, value) {\n    return isFunction(fallback) ? fallback(prop, value) : fallback;\n}\nconst getScope = (key, parent)=>key === true ? parent : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;\nfunction addScopes(set, parentScopes, key, parentFallback, value) {\n    for (const parent of parentScopes){\n        const scope = getScope(key, parent);\n        if (scope) {\n            set.add(scope);\n            const fallback = resolveFallback(scope._fallback, key, value);\n            if (defined(fallback) && fallback !== key && fallback !== parentFallback) {\n                return fallback;\n            }\n        } else if (scope === false && defined(parentFallback) && key !== parentFallback) {\n            return null;\n        }\n    }\n    return false;\n}\nfunction createSubResolver(parentScopes, resolver, prop, value) {\n    const rootScopes = resolver._rootScopes;\n    const fallback = resolveFallback(resolver._fallback, prop, value);\n    const allScopes = [\n        ...parentScopes,\n        ...rootScopes\n    ];\n    const set = new Set();\n    set.add(value);\n    let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);\n    if (key === null) {\n        return false;\n    }\n    if (defined(fallback) && fallback !== prop) {\n        key = addScopesFromKey(set, allScopes, fallback, key, value);\n        if (key === null) {\n            return false;\n        }\n    }\n    return _createResolver(Array.from(set), [\n        ''\n    ], rootScopes, fallback, ()=>subGetTarget(resolver, prop, value));\n}\nfunction addScopesFromKey(set, allScopes, key, fallback, item) {\n    while(key){\n        key = addScopes(set, allScopes, key, fallback, item);\n    }\n    return key;\n}\nfunction subGetTarget(resolver, prop, value) {\n    const parent = resolver._getTarget();\n    if (!(prop in parent)) {\n        parent[prop] = {};\n    }\n    const target = parent[prop];\n    if (isArray(target) && isObject(value)) {\n        return value;\n    }\n    return target || {};\n}\nfunction _resolveWithPrefixes(prop, prefixes, scopes, proxy) {\n    let value;\n    for (const prefix of prefixes){\n        value = _resolve(readKey(prefix, prop), scopes);\n        if (defined(value)) {\n            return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;\n        }\n    }\n}\nfunction _resolve(key, scopes) {\n    for (const scope of scopes){\n        if (!scope) {\n            continue;\n        }\n        const value = scope[key];\n        if (defined(value)) {\n            return value;\n        }\n    }\n}\nfunction getKeysFromAllScopes(target) {\n    let keys = target._keys;\n    if (!keys) {\n        keys = target._keys = resolveKeysFromAllScopes(target._scopes);\n    }\n    return keys;\n}\nfunction resolveKeysFromAllScopes(scopes) {\n    const set = new Set();\n    for (const scope of scopes){\n        for (const key of Object.keys(scope).filter((k)=>!k.startsWith('_'))){\n            set.add(key);\n        }\n    }\n    return Array.from(set);\n}\nfunction _parseObjectDataRadialScale(meta, data, start, count) {\n    const { iScale  } = meta;\n    const { key ='r'  } = this._parsing;\n    const parsed = new Array(count);\n    let i, ilen, index, item;\n    for(i = 0, ilen = count; i < ilen; ++i){\n        index = i + start;\n        item = data[index];\n        parsed[i] = {\n            r: iScale.parse(resolveObjectKey(item, key), index)\n        };\n    }\n    return parsed;\n}\n\nconst EPSILON = Number.EPSILON || 1e-14;\nconst getPoint = (points, i)=>i < points.length && !points[i].skip && points[i];\nconst getValueAxis = (indexAxis)=>indexAxis === 'x' ? 'y' : 'x';\nfunction splineCurve(firstPoint, middlePoint, afterPoint, t) {\n    // Props to Rob Spencer at scaled innovation for his post on splining between points\n    // http://scaledinnovation.com/analytics/splines/aboutSplines.html\n    // This function must also respect \"skipped\" points\n    const previous = firstPoint.skip ? middlePoint : firstPoint;\n    const current = middlePoint;\n    const next = afterPoint.skip ? middlePoint : afterPoint;\n    const d01 = distanceBetweenPoints(current, previous);\n    const d12 = distanceBetweenPoints(next, current);\n    let s01 = d01 / (d01 + d12);\n    let s12 = d12 / (d01 + d12);\n    // If all points are the same, s01 & s02 will be inf\n    s01 = isNaN(s01) ? 0 : s01;\n    s12 = isNaN(s12) ? 0 : s12;\n    const fa = t * s01; // scaling factor for triangle Ta\n    const fb = t * s12;\n    return {\n        previous: {\n            x: current.x - fa * (next.x - previous.x),\n            y: current.y - fa * (next.y - previous.y)\n        },\n        next: {\n            x: current.x + fb * (next.x - previous.x),\n            y: current.y + fb * (next.y - previous.y)\n        }\n    };\n}\n/**\n * Adjust tangents to ensure monotonic properties\n */ function monotoneAdjust(points, deltaK, mK) {\n    const pointsLen = points.length;\n    let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;\n    let pointAfter = getPoint(points, 0);\n    for(let i = 0; i < pointsLen - 1; ++i){\n        pointCurrent = pointAfter;\n        pointAfter = getPoint(points, i + 1);\n        if (!pointCurrent || !pointAfter) {\n            continue;\n        }\n        if (almostEquals(deltaK[i], 0, EPSILON)) {\n            mK[i] = mK[i + 1] = 0;\n            continue;\n        }\n        alphaK = mK[i] / deltaK[i];\n        betaK = mK[i + 1] / deltaK[i];\n        squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);\n        if (squaredMagnitude <= 9) {\n            continue;\n        }\n        tauK = 3 / Math.sqrt(squaredMagnitude);\n        mK[i] = alphaK * tauK * deltaK[i];\n        mK[i + 1] = betaK * tauK * deltaK[i];\n    }\n}\nfunction monotoneCompute(points, mK, indexAxis = 'x') {\n    const valueAxis = getValueAxis(indexAxis);\n    const pointsLen = points.length;\n    let delta, pointBefore, pointCurrent;\n    let pointAfter = getPoint(points, 0);\n    for(let i = 0; i < pointsLen; ++i){\n        pointBefore = pointCurrent;\n        pointCurrent = pointAfter;\n        pointAfter = getPoint(points, i + 1);\n        if (!pointCurrent) {\n            continue;\n        }\n        const iPixel = pointCurrent[indexAxis];\n        const vPixel = pointCurrent[valueAxis];\n        if (pointBefore) {\n            delta = (iPixel - pointBefore[indexAxis]) / 3;\n            pointCurrent[`cp1${indexAxis}`] = iPixel - delta;\n            pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];\n        }\n        if (pointAfter) {\n            delta = (pointAfter[indexAxis] - iPixel) / 3;\n            pointCurrent[`cp2${indexAxis}`] = iPixel + delta;\n            pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];\n        }\n    }\n}\n/**\n * This function calculates Bézier control points in a similar way than |splineCurve|,\n * but preserves monotonicity of the provided data and ensures no local extremums are added\n * between the dataset discrete points due to the interpolation.\n * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation\n */ function splineCurveMonotone(points, indexAxis = 'x') {\n    const valueAxis = getValueAxis(indexAxis);\n    const pointsLen = points.length;\n    const deltaK = Array(pointsLen).fill(0);\n    const mK = Array(pointsLen);\n    // Calculate slopes (deltaK) and initialize tangents (mK)\n    let i, pointBefore, pointCurrent;\n    let pointAfter = getPoint(points, 0);\n    for(i = 0; i < pointsLen; ++i){\n        pointBefore = pointCurrent;\n        pointCurrent = pointAfter;\n        pointAfter = getPoint(points, i + 1);\n        if (!pointCurrent) {\n            continue;\n        }\n        if (pointAfter) {\n            const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];\n            // In the case of two points that appear at the same x pixel, slopeDeltaX is 0\n            deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;\n        }\n        mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;\n    }\n    monotoneAdjust(points, deltaK, mK);\n    monotoneCompute(points, mK, indexAxis);\n}\nfunction capControlPoint(pt, min, max) {\n    return Math.max(Math.min(pt, max), min);\n}\nfunction capBezierPoints(points, area) {\n    let i, ilen, point, inArea, inAreaPrev;\n    let inAreaNext = _isPointInArea(points[0], area);\n    for(i = 0, ilen = points.length; i < ilen; ++i){\n        inAreaPrev = inArea;\n        inArea = inAreaNext;\n        inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);\n        if (!inArea) {\n            continue;\n        }\n        point = points[i];\n        if (inAreaPrev) {\n            point.cp1x = capControlPoint(point.cp1x, area.left, area.right);\n            point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);\n        }\n        if (inAreaNext) {\n            point.cp2x = capControlPoint(point.cp2x, area.left, area.right);\n            point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);\n        }\n    }\n}\n/**\n * @private\n */ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {\n    let i, ilen, point, controlPoints;\n    // Only consider points that are drawn in case the spanGaps option is used\n    if (options.spanGaps) {\n        points = points.filter((pt)=>!pt.skip);\n    }\n    if (options.cubicInterpolationMode === 'monotone') {\n        splineCurveMonotone(points, indexAxis);\n    } else {\n        let prev = loop ? points[points.length - 1] : points[0];\n        for(i = 0, ilen = points.length; i < ilen; ++i){\n            point = points[i];\n            controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);\n            point.cp1x = controlPoints.previous.x;\n            point.cp1y = controlPoints.previous.y;\n            point.cp2x = controlPoints.next.x;\n            point.cp2y = controlPoints.next.y;\n            prev = point;\n        }\n    }\n    if (options.capBezierPoints) {\n        capBezierPoints(points, area);\n    }\n}\n\n/**\n * Note: typedefs are auto-exported, so use a made-up `dom` namespace where\n * necessary to avoid duplicates with `export * from './helpers`; see\n * https://github.com/microsoft/TypeScript/issues/46011\n * @typedef { import('../core/core.controller.js').default } dom.Chart\n * @typedef { import('../../types').ChartEvent } ChartEvent\n */ /**\n * @private\n */ function _isDomSupported() {\n    return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n/**\n * @private\n */ function _getParentNode(domNode) {\n    let parent = domNode.parentNode;\n    if (parent && parent.toString() === '[object ShadowRoot]') {\n        parent = parent.host;\n    }\n    return parent;\n}\n/**\n * convert max-width/max-height values that may be percentages into a number\n * @private\n */ function parseMaxStyle(styleValue, node, parentProperty) {\n    let valueInPixels;\n    if (typeof styleValue === 'string') {\n        valueInPixels = parseInt(styleValue, 10);\n        if (styleValue.indexOf('%') !== -1) {\n            // percentage * size in dimension\n            valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];\n        }\n    } else {\n        valueInPixels = styleValue;\n    }\n    return valueInPixels;\n}\nconst getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);\nfunction getStyle(el, property) {\n    return getComputedStyle(el).getPropertyValue(property);\n}\nconst positions = [\n    'top',\n    'right',\n    'bottom',\n    'left'\n];\nfunction getPositionedStyle(styles, style, suffix) {\n    const result = {};\n    suffix = suffix ? '-' + suffix : '';\n    for(let i = 0; i < 4; i++){\n        const pos = positions[i];\n        result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;\n    }\n    result.width = result.left + result.right;\n    result.height = result.top + result.bottom;\n    return result;\n}\nconst useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);\n/**\n * @param e\n * @param canvas\n * @returns Canvas position\n */ function getCanvasPosition(e, canvas) {\n    const touches = e.touches;\n    const source = touches && touches.length ? touches[0] : e;\n    const { offsetX , offsetY  } = source;\n    let box = false;\n    let x, y;\n    if (useOffsetPos(offsetX, offsetY, e.target)) {\n        x = offsetX;\n        y = offsetY;\n    } else {\n        const rect = canvas.getBoundingClientRect();\n        x = source.clientX - rect.left;\n        y = source.clientY - rect.top;\n        box = true;\n    }\n    return {\n        x,\n        y,\n        box\n    };\n}\n/**\n * Gets an event's x, y coordinates, relative to the chart area\n * @param event\n * @param chart\n * @returns x and y coordinates of the event\n */ function getRelativePosition(event, chart) {\n    if ('native' in event) {\n        return event;\n    }\n    const { canvas , currentDevicePixelRatio  } = chart;\n    const style = getComputedStyle(canvas);\n    const borderBox = style.boxSizing === 'border-box';\n    const paddings = getPositionedStyle(style, 'padding');\n    const borders = getPositionedStyle(style, 'border', 'width');\n    const { x , y , box  } = getCanvasPosition(event, canvas);\n    const xOffset = paddings.left + (box && borders.left);\n    const yOffset = paddings.top + (box && borders.top);\n    let { width , height  } = chart;\n    if (borderBox) {\n        width -= paddings.width + borders.width;\n        height -= paddings.height + borders.height;\n    }\n    return {\n        x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),\n        y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)\n    };\n}\nfunction getContainerSize(canvas, width, height) {\n    let maxWidth, maxHeight;\n    if (width === undefined || height === undefined) {\n        const container = _getParentNode(canvas);\n        if (!container) {\n            width = canvas.clientWidth;\n            height = canvas.clientHeight;\n        } else {\n            const rect = container.getBoundingClientRect(); // this is the border box of the container\n            const containerStyle = getComputedStyle(container);\n            const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');\n            const containerPadding = getPositionedStyle(containerStyle, 'padding');\n            width = rect.width - containerPadding.width - containerBorder.width;\n            height = rect.height - containerPadding.height - containerBorder.height;\n            maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');\n            maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');\n        }\n    }\n    return {\n        width,\n        height,\n        maxWidth: maxWidth || INFINITY,\n        maxHeight: maxHeight || INFINITY\n    };\n}\nconst round1 = (v)=>Math.round(v * 10) / 10;\n// eslint-disable-next-line complexity\nfunction getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {\n    const style = getComputedStyle(canvas);\n    const margins = getPositionedStyle(style, 'margin');\n    const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;\n    const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;\n    const containerSize = getContainerSize(canvas, bbWidth, bbHeight);\n    let { width , height  } = containerSize;\n    if (style.boxSizing === 'content-box') {\n        const borders = getPositionedStyle(style, 'border', 'width');\n        const paddings = getPositionedStyle(style, 'padding');\n        width -= paddings.width + borders.width;\n        height -= paddings.height + borders.height;\n    }\n    width = Math.max(0, width - margins.width);\n    height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);\n    width = round1(Math.min(width, maxWidth, containerSize.maxWidth));\n    height = round1(Math.min(height, maxHeight, containerSize.maxHeight));\n    if (width && !height) {\n        // https://github.com/chartjs/Chart.js/issues/4659\n        // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)\n        height = round1(width / 2);\n    }\n    const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;\n    if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {\n        height = containerSize.height;\n        width = round1(Math.floor(height * aspectRatio));\n    }\n    return {\n        width,\n        height\n    };\n}\n/**\n * @param chart\n * @param forceRatio\n * @param forceStyle\n * @returns True if the canvas context size or transformation has changed.\n */ function retinaScale(chart, forceRatio, forceStyle) {\n    const pixelRatio = forceRatio || 1;\n    const deviceHeight = Math.floor(chart.height * pixelRatio);\n    const deviceWidth = Math.floor(chart.width * pixelRatio);\n    chart.height = Math.floor(chart.height);\n    chart.width = Math.floor(chart.width);\n    const canvas = chart.canvas;\n    // If no style has been set on the canvas, the render size is used as display size,\n    // making the chart visually bigger, so let's enforce it to the \"correct\" values.\n    // See https://github.com/chartjs/Chart.js/issues/3575\n    if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {\n        canvas.style.height = `${chart.height}px`;\n        canvas.style.width = `${chart.width}px`;\n    }\n    if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {\n        chart.currentDevicePixelRatio = pixelRatio;\n        canvas.height = deviceHeight;\n        canvas.width = deviceWidth;\n        chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);\n        return true;\n    }\n    return false;\n}\n/**\n * Detects support for options object argument in addEventListener.\n * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n * @private\n */ const supportsEventListenerOptions = function() {\n    let passiveSupported = false;\n    try {\n        const options = {\n            get passive () {\n                passiveSupported = true;\n                return false;\n            }\n        };\n        window.addEventListener('test', null, options);\n        window.removeEventListener('test', null, options);\n    } catch (e) {\n    // continue regardless of error\n    }\n    return passiveSupported;\n}();\n/**\n * The \"used\" size is the final value of a dimension property after all calculations have\n * been performed. This method uses the computed style of `element` but returns undefined\n * if the computed style is not expressed in pixels. That can happen in some cases where\n * `element` has a size relative to its parent and this last one is not yet displayed,\n * for example because of `display: none` on a parent node.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n * @returns Size in pixels or undefined if unknown.\n */ function readUsedSize(element, property) {\n    const value = getStyle(element, property);\n    const matches = value && value.match(/^(\\d+)(\\.\\d+)?px$/);\n    return matches ? +matches[1] : undefined;\n}\n\n/**\n * @private\n */ function _pointInLine(p1, p2, t, mode) {\n    return {\n        x: p1.x + t * (p2.x - p1.x),\n        y: p1.y + t * (p2.y - p1.y)\n    };\n}\n/**\n * @private\n */ function _steppedInterpolation(p1, p2, t, mode) {\n    return {\n        x: p1.x + t * (p2.x - p1.x),\n        y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y : mode === 'after' ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y\n    };\n}\n/**\n * @private\n */ function _bezierInterpolation(p1, p2, t, mode) {\n    const cp1 = {\n        x: p1.cp2x,\n        y: p1.cp2y\n    };\n    const cp2 = {\n        x: p2.cp1x,\n        y: p2.cp1y\n    };\n    const a = _pointInLine(p1, cp1, t);\n    const b = _pointInLine(cp1, cp2, t);\n    const c = _pointInLine(cp2, p2, t);\n    const d = _pointInLine(a, b, t);\n    const e = _pointInLine(b, c, t);\n    return _pointInLine(d, e, t);\n}\n\nconst getRightToLeftAdapter = function(rectX, width) {\n    return {\n        x (x) {\n            return rectX + rectX + width - x;\n        },\n        setWidth (w) {\n            width = w;\n        },\n        textAlign (align) {\n            if (align === 'center') {\n                return align;\n            }\n            return align === 'right' ? 'left' : 'right';\n        },\n        xPlus (x, value) {\n            return x - value;\n        },\n        leftForLtr (x, itemWidth) {\n            return x - itemWidth;\n        }\n    };\n};\nconst getLeftToRightAdapter = function() {\n    return {\n        x (x) {\n            return x;\n        },\n        setWidth (w) {},\n        textAlign (align) {\n            return align;\n        },\n        xPlus (x, value) {\n            return x + value;\n        },\n        leftForLtr (x, _itemWidth) {\n            return x;\n        }\n    };\n};\nfunction getRtlAdapter(rtl, rectX, width) {\n    return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();\n}\nfunction overrideTextDirection(ctx, direction) {\n    let style, original;\n    if (direction === 'ltr' || direction === 'rtl') {\n        style = ctx.canvas.style;\n        original = [\n            style.getPropertyValue('direction'),\n            style.getPropertyPriority('direction')\n        ];\n        style.setProperty('direction', direction, 'important');\n        ctx.prevTextDirection = original;\n    }\n}\nfunction restoreTextDirection(ctx, original) {\n    if (original !== undefined) {\n        delete ctx.prevTextDirection;\n        ctx.canvas.style.setProperty('direction', original[0], original[1]);\n    }\n}\n\nfunction propertyFn(property) {\n    if (property === 'angle') {\n        return {\n            between: _angleBetween,\n            compare: _angleDiff,\n            normalize: _normalizeAngle\n        };\n    }\n    return {\n        between: _isBetween,\n        compare: (a, b)=>a - b,\n        normalize: (x)=>x\n    };\n}\nfunction normalizeSegment({ start , end , count , loop , style  }) {\n    return {\n        start: start % count,\n        end: end % count,\n        loop: loop && (end - start + 1) % count === 0,\n        style\n    };\n}\nfunction getSegment(segment, points, bounds) {\n    const { property , start: startBound , end: endBound  } = bounds;\n    const { between , normalize  } = propertyFn(property);\n    const count = points.length;\n    let { start , end , loop  } = segment;\n    let i, ilen;\n    if (loop) {\n        start += count;\n        end += count;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            if (!between(normalize(points[start % count][property]), startBound, endBound)) {\n                break;\n            }\n            start--;\n            end--;\n        }\n        start %= count;\n        end %= count;\n    }\n    if (end < start) {\n        end += count;\n    }\n    return {\n        start,\n        end,\n        loop,\n        style: segment.style\n    };\n}\n function _boundSegment(segment, points, bounds) {\n    if (!bounds) {\n        return [\n            segment\n        ];\n    }\n    const { property , start: startBound , end: endBound  } = bounds;\n    const count = points.length;\n    const { compare , between , normalize  } = propertyFn(property);\n    const { start , end , loop , style  } = getSegment(segment, points, bounds);\n    const result = [];\n    let inside = false;\n    let subStart = null;\n    let value, point, prevValue;\n    const startIsBefore = ()=>between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;\n    const endIsBefore = ()=>compare(endBound, value) === 0 || between(endBound, prevValue, value);\n    const shouldStart = ()=>inside || startIsBefore();\n    const shouldStop = ()=>!inside || endIsBefore();\n    for(let i = start, prev = start; i <= end; ++i){\n        point = points[i % count];\n        if (point.skip) {\n            continue;\n        }\n        value = normalize(point[property]);\n        if (value === prevValue) {\n            continue;\n        }\n        inside = between(value, startBound, endBound);\n        if (subStart === null && shouldStart()) {\n            subStart = compare(value, startBound) === 0 ? i : prev;\n        }\n        if (subStart !== null && shouldStop()) {\n            result.push(normalizeSegment({\n                start: subStart,\n                end: i,\n                loop,\n                count,\n                style\n            }));\n            subStart = null;\n        }\n        prev = i;\n        prevValue = value;\n    }\n    if (subStart !== null) {\n        result.push(normalizeSegment({\n            start: subStart,\n            end,\n            loop,\n            count,\n            style\n        }));\n    }\n    return result;\n}\n function _boundSegments(line, bounds) {\n    const result = [];\n    const segments = line.segments;\n    for(let i = 0; i < segments.length; i++){\n        const sub = _boundSegment(segments[i], line.points, bounds);\n        if (sub.length) {\n            result.push(...sub);\n        }\n    }\n    return result;\n}\n function findStartAndEnd(points, count, loop, spanGaps) {\n    let start = 0;\n    let end = count - 1;\n    if (loop && !spanGaps) {\n        while(start < count && !points[start].skip){\n            start++;\n        }\n    }\n    while(start < count && points[start].skip){\n        start++;\n    }\n    start %= count;\n    if (loop) {\n        end += start;\n    }\n    while(end > start && points[end % count].skip){\n        end--;\n    }\n    end %= count;\n    return {\n        start,\n        end\n    };\n}\n function solidSegments(points, start, max, loop) {\n    const count = points.length;\n    const result = [];\n    let last = start;\n    let prev = points[start];\n    let end;\n    for(end = start + 1; end <= max; ++end){\n        const cur = points[end % count];\n        if (cur.skip || cur.stop) {\n            if (!prev.skip) {\n                loop = false;\n                result.push({\n                    start: start % count,\n                    end: (end - 1) % count,\n                    loop\n                });\n                start = last = cur.stop ? end : null;\n            }\n        } else {\n            last = end;\n            if (prev.skip) {\n                start = end;\n            }\n        }\n        prev = cur;\n    }\n    if (last !== null) {\n        result.push({\n            start: start % count,\n            end: last % count,\n            loop\n        });\n    }\n    return result;\n}\n function _computeSegments(line, segmentOptions) {\n    const points = line.points;\n    const spanGaps = line.options.spanGaps;\n    const count = points.length;\n    if (!count) {\n        return [];\n    }\n    const loop = !!line._loop;\n    const { start , end  } = findStartAndEnd(points, count, loop, spanGaps);\n    if (spanGaps === true) {\n        return splitByStyles(line, [\n            {\n                start,\n                end,\n                loop\n            }\n        ], points, segmentOptions);\n    }\n    const max = end < start ? end + count : end;\n    const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;\n    return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);\n}\n function splitByStyles(line, segments, points, segmentOptions) {\n    if (!segmentOptions || !segmentOptions.setContext || !points) {\n        return segments;\n    }\n    return doSplitByStyles(line, segments, points, segmentOptions);\n}\n function doSplitByStyles(line, segments, points, segmentOptions) {\n    const chartContext = line._chart.getContext();\n    const baseStyle = readStyle(line.options);\n    const { _datasetIndex: datasetIndex , options: { spanGaps  }  } = line;\n    const count = points.length;\n    const result = [];\n    let prevStyle = baseStyle;\n    let start = segments[0].start;\n    let i = start;\n    function addStyle(s, e, l, st) {\n        const dir = spanGaps ? -1 : 1;\n        if (s === e) {\n            return;\n        }\n        s += count;\n        while(points[s % count].skip){\n            s -= dir;\n        }\n        while(points[e % count].skip){\n            e += dir;\n        }\n        if (s % count !== e % count) {\n            result.push({\n                start: s % count,\n                end: e % count,\n                loop: l,\n                style: st\n            });\n            prevStyle = st;\n            start = e % count;\n        }\n    }\n    for (const segment of segments){\n        start = spanGaps ? start : segment.start;\n        let prev = points[start % count];\n        let style;\n        for(i = start + 1; i <= segment.end; i++){\n            const pt = points[i % count];\n            style = readStyle(segmentOptions.setContext(createContext(chartContext, {\n                type: 'segment',\n                p0: prev,\n                p1: pt,\n                p0DataIndex: (i - 1) % count,\n                p1DataIndex: i % count,\n                datasetIndex\n            })));\n            if (styleChanged(style, prevStyle)) {\n                addStyle(start, i - 1, segment.loop, prevStyle);\n            }\n            prev = pt;\n            prevStyle = style;\n        }\n        if (start < i - 1) {\n            addStyle(start, i - 1, segment.loop, prevStyle);\n        }\n    }\n    return result;\n}\nfunction readStyle(options) {\n    return {\n        backgroundColor: options.backgroundColor,\n        borderCapStyle: options.borderCapStyle,\n        borderDash: options.borderDash,\n        borderDashOffset: options.borderDashOffset,\n        borderJoinStyle: options.borderJoinStyle,\n        borderWidth: options.borderWidth,\n        borderColor: options.borderColor\n    };\n}\nfunction styleChanged(style, prevStyle) {\n    return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle);\n}\n\nexport { unclipArea as $, _rlookupByKey as A, _lookupByKey as B, _isPointInArea as C, getAngleFromPoint as D, toPadding as E, each as F, getMaximumSize as G, HALF_PI as H, _getParentNode as I, readUsedSize as J, supportsEventListenerOptions as K, throttled as L, _isDomSupported as M, _factorize as N, finiteOrDefault as O, PI as P, callback as Q, _addGrace as R, _limitValue as S, TAU as T, toDegrees as U, _measureText as V, _int16Range as W, _alignPixel as X, clipArea as Y, renderText as Z, _arrayUnique as _, resolve as a, fontString as a$, toFont as a0, _toLeftRightCenter as a1, _alignStartEnd as a2, overrides as a3, merge as a4, _capitalize as a5, descriptors as a6, isFunction as a7, _attachContext as a8, _createResolver as a9, overrideTextDirection as aA, _textX as aB, restoreTextDirection as aC, drawPointLegend as aD, distanceBetweenPoints as aE, noop as aF, _setMinAndMaxByKey as aG, niceNum as aH, almostWhole as aI, almostEquals as aJ, _decimalPlaces as aK, Ticks as aL, log10 as aM, _longestText as aN, _filterBetween as aO, _lookup as aP, isPatternOrGradient as aQ, getHoverColor as aR, clone as aS, _merger as aT, _mergerIf as aU, _deprecated as aV, _splitKey as aW, toFontString as aX, splineCurve as aY, splineCurveMonotone as aZ, getStyle as a_, _descriptors as aa, mergeIf as ab, uid as ac, debounce as ad, retinaScale as ae, clearCanvas as af, setsEqual as ag, _elementsEqual as ah, _isClickEvent as ai, _isBetween as aj, _readValueToProps as ak, _updateBezierControlPoints as al, _computeSegments as am, _boundSegments as an, _steppedInterpolation as ao, _bezierInterpolation as ap, _pointInLine as aq, _steppedLineTo as ar, _bezierCurveTo as as, drawPoint as at, addRoundedRectPath as au, toTRBL as av, toTRBLCorners as aw, _boundSegment as ax, _normalizeAngle as ay, getRtlAdapter as az, isArray as b, toLineHeight as b0, PITAU as b1, INFINITY as b2, RAD_PER_DEG as b3, QUARTER_PI as b4, TWO_THIRDS_PI as b5, _angleDiff as b6, color as c, defaults as d, effects as e, resolveObjectKey as f, isNumberFinite as g, defined as h, isObject as i, createContext as j, isNullOrUndef as k, listenArrayEvents as l, toPercentage as m, toDimension as n, formatNumber as o, _angleBetween as p, _getStartAndCountOfVisiblePoints as q, requestAnimFrame as r, sign as s, toRadians as t, unlistenArrayEvents as u, valueOrDefault as v, _scaleRangesChanged as w, isNumber as x, _parseObjectDataRadialScale as y, getRelativePosition as z };\n//# sourceMappingURL=helpers.segment.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.bar.d.ts",
    "content": "export default class BarController extends DatasetController {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static overrides: any;\n    /**\n       * Overriding primitive data parsing since we support mixed primitive/array\n       * data for float bars\n       * @protected\n       */\n    protected parsePrimitiveData(meta: any, data: any, start: any, count: any): any[];\n    /**\n       * Overriding array data parsing since we support mixed primitive/array\n       * data for float bars\n       * @protected\n       */\n    protected parseArrayData(meta: any, data: any, start: any, count: any): any[];\n    /**\n       * Overriding object data parsing since we support mixed primitive/array\n       * value-scale data for float bars\n       * @protected\n       */\n    protected parseObjectData(meta: any, data: any, start: any, count: any): any[];\n    update(mode: any): void;\n    /**\n       * Returns the stacks based on groups and bar visibility.\n       * @param {number} [last] - The dataset index\n       * @param {number} [dataIndex] - The data index of the ruler\n       * @returns {string[]} The list of stack IDs\n       * @private\n       */\n    private _getStacks;\n    /**\n       * Returns the effective number of stacks based on groups and bar visibility.\n       * @private\n       */\n    private _getStackCount;\n    /**\n       * Returns the stack index for the given dataset based on groups and bar visibility.\n       * @param {number} [datasetIndex] - The dataset index\n       * @param {string} [name] - The stack name to find\n     * @param {number} [dataIndex]\n       * @returns {number} The stack index\n       * @private\n       */\n    private _getStackIndex;\n    /**\n       * @private\n       */\n    private _getRuler;\n    /**\n       * Note: pixel values are not clamped to the scale area.\n       * @private\n       */\n    private _calculateBarValuePixels;\n    /**\n       * @private\n       */\n    private _calculateBarIndexPixels;\n}\nimport DatasetController from \"../core/core.datasetController.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.bubble.d.ts",
    "content": "export default class BubbleController extends DatasetController {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static overrides: any;\n    /**\n       * Parse array of primitive values\n       * @protected\n       */\n    protected parsePrimitiveData(meta: any, data: any, start: any, count: any): any;\n    /**\n       * Parse array of arrays\n       * @protected\n       */\n    protected parseArrayData(meta: any, data: any, start: any, count: any): any;\n    /**\n       * Parse array of objects\n       * @protected\n       */\n    protected parseObjectData(meta: any, data: any, start: any, count: any): any;\n    /**\n       * @protected\n       */\n    protected getMaxOverflow(): number;\n    /**\n       * @protected\n       */\n    protected getLabelAndValue(index: any): {\n        label: any;\n        value: string;\n    };\n    update(mode: any): void;\n}\nimport DatasetController from \"../core/core.datasetController.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.doughnut.d.ts",
    "content": "export default class DoughnutController extends DatasetController {\n    static id: string;\n    static descriptors: {\n        _scriptable: (name: any) => boolean;\n        _indexable: (name: any) => boolean;\n    };\n    /**\n     * @type {any}\n     */\n    static overrides: any;\n    constructor(chart: any, datasetIndex: any);\n    innerRadius: number;\n    outerRadius: number;\n    offsetX: number;\n    offsetY: number;\n    /**\n       * Override data parsing, since we are not using scales\n       */\n    parse(start: any, count: any): void;\n    /**\n       * @private\n       */\n    private _getRotation;\n    /**\n       * @private\n       */\n    private _getCircumference;\n    /**\n       * Get the maximal rotation & circumference extents\n       * across all visible datasets.\n       */\n    _getRotationExtents(): {\n        rotation: number;\n        circumference: number;\n    };\n    /**\n     * @private\n     */\n    private _circumference;\n    calculateTotal(): number;\n    calculateCircumference(value: any): number;\n    getLabelAndValue(index: any): {\n        label: any;\n        value: string;\n    };\n    getMaxBorderWidth(arcs: any): number;\n    getMaxOffset(arcs: any): number;\n    /**\n       * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly\n       * @private\n       */\n    private _getRingWeightOffset;\n    /**\n       * @private\n       */\n    private _getRingWeight;\n    /**\n       * Returns the sum of all visible data set weights.\n       * @private\n       */\n    private _getVisibleDatasetWeightTotal;\n}\nexport type Chart = import('../core/core.controller.js').default;\nimport DatasetController from \"../core/core.datasetController.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.line.d.ts",
    "content": "export default class LineController extends DatasetController {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static overrides: any;\n    update(mode: any): void;\n    /**\n       * @protected\n       */\n    protected getMaxOverflow(): any;\n}\nimport DatasetController from \"../core/core.datasetController.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.pie.d.ts",
    "content": "export default class PieController extends DoughnutController {\n}\nimport DoughnutController from \"./controller.doughnut.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.polarArea.d.ts",
    "content": "export default class PolarAreaController extends DatasetController {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static overrides: any;\n    constructor(chart: any, datasetIndex: any);\n    innerRadius: number;\n    outerRadius: number;\n    getLabelAndValue(index: any): {\n        label: any;\n        value: string;\n    };\n    parseObjectData(meta: any, data: any, start: any, count: any): any[];\n    update(mode: any): void;\n    /**\n     * @protected\n     */\n    protected getMinMax(): {\n        min: number;\n        max: number;\n    };\n    /**\n       * @private\n       */\n    private _updateRadius;\n    countVisibleElements(): number;\n    /**\n       * @private\n       */\n    private _computeAngle;\n}\nimport DatasetController from \"../core/core.datasetController.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.radar.d.ts",
    "content": "export default class RadarController extends DatasetController {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static overrides: any;\n    /**\n       * @protected\n       */\n    protected getLabelAndValue(index: any): {\n        label: any;\n        value: string;\n    };\n    parseObjectData(meta: any, data: any, start: any, count: any): any[];\n    update(mode: any): void;\n}\nimport DatasetController from \"../core/core.datasetController.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/controller.scatter.d.ts",
    "content": "export default class ScatterController extends DatasetController {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static overrides: any;\n    /**\n       * @protected\n       */\n    protected getLabelAndValue(index: any): {\n        label: any;\n        value: string;\n    };\n    update(mode: any): void;\n    /**\n       * @protected\n       */\n    protected getMaxOverflow(): any;\n}\nimport DatasetController from \"../core/core.datasetController.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/controllers/index.d.ts",
    "content": "export { default as BarController } from \"./controller.bar.js\";\nexport { default as BubbleController } from \"./controller.bubble.js\";\nexport { default as DoughnutController } from \"./controller.doughnut.js\";\nexport { default as LineController } from \"./controller.line.js\";\nexport { default as PolarAreaController } from \"./controller.polarArea.js\";\nexport { default as PieController } from \"./controller.pie.js\";\nexport { default as RadarController } from \"./controller.radar.js\";\nexport { default as ScatterController } from \"./controller.scatter.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.adapters.d.ts",
    "content": "/**\n * @namespace Chart._adapters\n * @since 2.8.0\n * @private\n */\nimport type { AnyObject } from '../types/basic.js';\nimport type { ChartOptions } from '../types/index.js';\nexport declare type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';\nexport interface DateAdapter<T extends AnyObject = AnyObject> {\n    readonly options: T;\n    /**\n     * Will called with chart options after adapter creation.\n     */\n    init(this: DateAdapter<T>, chartOptions: ChartOptions): void;\n    /**\n     * Returns a map of time formats for the supported formatting units defined\n     * in Unit as well as 'datetime' representing a detailed date/time string.\n     */\n    formats(this: DateAdapter<T>): Record<string, string>;\n    /**\n     * Parses the given `value` and return the associated timestamp.\n     * @param value - the value to parse (usually comes from the data)\n     * @param [format] - the expected data format\n     */\n    parse(this: DateAdapter<T>, value: unknown, format?: TimeUnit): number | null;\n    /**\n     * Returns the formatted date in the specified `format` for a given `timestamp`.\n     * @param timestamp - the timestamp to format\n     * @param format - the date/time token\n     */\n    format(this: DateAdapter<T>, timestamp: number, format: TimeUnit): string;\n    /**\n     * Adds the specified `amount` of `unit` to the given `timestamp`.\n     * @param timestamp - the input timestamp\n     * @param amount - the amount to add\n     * @param unit - the unit as string\n     */\n    add(this: DateAdapter<T>, timestamp: number, amount: number, unit: TimeUnit): number;\n    /**\n     * Returns the number of `unit` between the given timestamps.\n     * @param a - the input timestamp (reference)\n     * @param b - the timestamp to subtract\n     * @param unit - the unit as string\n     */\n    diff(this: DateAdapter<T>, a: number, b: number, unit: TimeUnit): number;\n    /**\n     * Returns start of `unit` for the given `timestamp`.\n     * @param timestamp - the input timestamp\n     * @param unit - the unit as string\n     * @param [weekday] - the ISO day of the week with 1 being Monday\n     * and 7 being Sunday (only needed if param *unit* is `isoWeek`).\n     */\n    startOf(this: DateAdapter<T>, timestamp: number, unit: TimeUnit | 'isoWeek', weekday?: number): number;\n    /**\n     * Returns end of `unit` for the given `timestamp`.\n     * @param timestamp - the input timestamp\n     * @param unit - the unit as string\n     */\n    endOf(this: DateAdapter<T>, timestamp: number, unit: TimeUnit | 'isoWeek'): number;\n}\n/**\n * Date adapter (current used by the time scale)\n * @namespace Chart._adapters._date\n * @memberof Chart._adapters\n * @private\n */\ndeclare class DateAdapterBase implements DateAdapter {\n    /**\n     * Override default date adapter methods.\n     * Accepts type parameter to define options type.\n     * @example\n     * Chart._adapters._date.override<{myAdapterOption: string}>({\n     *   init() {\n     *     console.log(this.options.myAdapterOption);\n     *   }\n     * })\n     */\n    static override<T extends AnyObject = AnyObject>(members: Partial<Omit<DateAdapter<T>, 'options'>>): void;\n    readonly options: AnyObject;\n    constructor(options: AnyObject);\n    init(): void;\n    formats(): Record<string, string>;\n    parse(): number | null;\n    format(): string;\n    add(): number;\n    diff(): number;\n    startOf(): number;\n    endOf(): number;\n}\ndeclare const _default: {\n    _date: typeof DateAdapterBase;\n};\nexport default _default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.animation.d.ts",
    "content": "export default class Animation {\n    constructor(cfg: any, target: any, prop: any, to: any);\n    _active: boolean;\n    _fn: any;\n    _easing: any;\n    _start: number;\n    _duration: number;\n    _total: number;\n    _loop: boolean;\n    _target: any;\n    _prop: any;\n    _from: unknown;\n    _to: any;\n    _promises: any[];\n    active(): boolean;\n    update(cfg: any, to: any, date: any): void;\n    cancel(): void;\n    tick(date: any): void;\n    wait(): Promise<any>;\n    _notify(resolved: any): void;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.animations.d.ts",
    "content": "export default class Animations {\n    constructor(chart: any, config: any);\n    _chart: any;\n    _properties: Map<any, any>;\n    configure(config: any): void;\n    /**\n       * Utility to handle animation of `options`.\n       * @private\n       */\n    private _animateOptions;\n    /**\n       * @private\n       */\n    private _createAnimations;\n    /**\n       * Update `target` properties to new values, using configured animations\n       * @param {object} target - object to update\n       * @param {object} values - new target properties\n       * @returns {boolean|undefined} - `true` if animations were started\n       **/\n    update(target: object, values: object): boolean | undefined;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.animations.defaults.d.ts",
    "content": "export function applyAnimationsDefaults(defaults: any): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.animator.d.ts",
    "content": "/**\n * @typedef { import('./core.animation.js').default } Animation\n * @typedef { import('./core.controller.js').default } Chart\n */\n/**\n * Please use the module's default export which provides a singleton instance\n * Note: class is export for typedoc\n */\nexport class Animator {\n    _request: any;\n    _charts: Map<any, any>;\n    _running: boolean;\n    _lastDate: number;\n    /**\n       * @private\n       */\n    private _notify;\n    /**\n       * @private\n       */\n    private _refresh;\n    /**\n       * @private\n       */\n    private _update;\n    /**\n       * @private\n       */\n    private _getAnims;\n    /**\n       * @param {Chart} chart\n       * @param {string} event - event name\n       * @param {Function} cb - callback\n       */\n    listen(chart: Chart, event: string, cb: Function): void;\n    /**\n       * Add animations\n       * @param {Chart} chart\n       * @param {Animation[]} items - animations\n       */\n    add(chart: Chart, items: Animation[]): void;\n    /**\n       * Counts number of active animations for the chart\n       * @param {Chart} chart\n       */\n    has(chart: Chart): boolean;\n    /**\n       * Start animating (all charts)\n       * @param {Chart} chart\n       */\n    start(chart: Chart): void;\n    running(chart: any): boolean;\n    /**\n       * Stop all animations for the chart\n       * @param {Chart} chart\n       */\n    stop(chart: Chart): void;\n    /**\n       * Remove chart from Animator\n       * @param {Chart} chart\n       */\n    remove(chart: Chart): boolean;\n}\ndeclare const _default: Animator;\nexport default _default;\nexport type Animation = import('./core.animation.js').default;\nexport type Chart = import('./core.controller.js').default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.config.d.ts",
    "content": "export function getIndexAxis(type: any, options: any): any;\nexport function determineAxis(id: any, scaleOptions: any): any;\nexport default class Config {\n    constructor(config: any);\n    _config: any;\n    _scopeCache: Map<any, any>;\n    _resolverCache: Map<any, any>;\n    get platform(): any;\n    set type(arg: any);\n    get type(): any;\n    set data(arg: any);\n    get data(): any;\n    set options(arg: any);\n    get options(): any;\n    get plugins(): any;\n    update(): void;\n    clearCache(): void;\n    /**\n     * Returns the option scope keys for resolving dataset options.\n     * These keys do not include the dataset itself, because it is not under options.\n     * @param {string} datasetType\n     * @return {string[][]}\n     */\n    datasetScopeKeys(datasetType: string): string[][];\n    /**\n     * Returns the option scope keys for resolving dataset animation options.\n     * These keys do not include the dataset itself, because it is not under options.\n     * @param {string} datasetType\n     * @param {string} transition\n     * @return {string[][]}\n     */\n    datasetAnimationScopeKeys(datasetType: string, transition: string): string[][];\n    /**\n     * Returns the options scope keys for resolving element options that belong\n     * to an dataset. These keys do not include the dataset itself, because it\n     * is not under options.\n     * @param {string} datasetType\n     * @param {string} elementType\n     * @return {string[][]}\n     */\n    datasetElementScopeKeys(datasetType: string, elementType: string): string[][];\n    /**\n     * Returns the options scope keys for resolving plugin options.\n     * @param {{id: string, additionalOptionScopes?: string[]}} plugin\n     * @return {string[][]}\n     */\n    pluginScopeKeys(plugin: {\n        id: string;\n        additionalOptionScopes?: string[];\n    }): string[][];\n    /**\n     * @private\n     */\n    private _cachedScopes;\n    /**\n     * Resolves the objects from options and defaults for option value resolution.\n     * @param {object} mainScope - The main scope object for options\n     * @param {string[][]} keyLists - The arrays of keys in resolution order\n     * @param {boolean} [resetCache] - reset the cache for this mainScope\n     */\n    getOptionScopes(mainScope: object, keyLists: string[][], resetCache?: boolean): any;\n    /**\n     * Returns the option scopes for resolving chart options\n     * @return {object[]}\n     */\n    chartOptionScopes(): object[];\n    /**\n     * @param {object[]} scopes\n     * @param {string[]} names\n     * @param {function|object} context\n     * @param {string[]} [prefixes]\n     * @return {object}\n     */\n    resolveNamedOptions(scopes: object[], names: string[], context: Function | object, prefixes?: string[]): object;\n    /**\n     * @param {object[]} scopes\n     * @param {object} [context]\n     * @param {string[]} [prefixes]\n     * @param {{scriptable: boolean, indexable: boolean, allKeys?: boolean}} [descriptorDefaults]\n     */\n    createResolver(scopes: object[], context?: object, prefixes?: string[], descriptorDefaults?: {\n        scriptable: boolean;\n        indexable: boolean;\n        allKeys?: boolean;\n    }): any;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.controller.d.ts",
    "content": "export default Chart;\nexport type ChartEvent = import('../types/index.js').ChartEvent;\nexport type Point = import('../types/index.js').Point;\ndeclare class Chart {\n    static defaults: import(\"./core.defaults.js\").Defaults;\n    static instances: {};\n    static overrides: any;\n    static registry: import(\"./core.registry.js\").Registry;\n    static version: string;\n    static getChart: (key: any) => any;\n    static register(...items: any[]): void;\n    static unregister(...items: any[]): void;\n    constructor(item: any, userConfig: any);\n    config: Config;\n    platform: any;\n    id: number;\n    ctx: any;\n    canvas: any;\n    width: any;\n    height: any;\n    _options: any;\n    _aspectRatio: any;\n    _layers: any[];\n    _metasets: any[];\n    _stacks: any;\n    boxes: any[];\n    currentDevicePixelRatio: any;\n    chartArea: any;\n    _active: any[];\n    _lastEvent: import(\"../types/index.js\").ChartEvent;\n    _listeners: {};\n    /** @type {?{attach?: function, detach?: function, resize?: function}} */\n    _responsiveListeners: {\n        attach?: Function;\n        detach?: Function;\n        resize?: Function;\n    };\n    _sortedMetasets: any[];\n    scales: {};\n    _plugins: PluginService;\n    $proxies: {};\n    _hiddenIndices: {};\n    attached: boolean;\n    _animationsDisabled: boolean;\n    $context: {\n        chart: Chart;\n        type: string;\n    };\n    _doResize: (mode?: any) => number;\n    _dataChanges: any[];\n    get aspectRatio(): any;\n    set data(arg: any);\n    get data(): any;\n    set options(arg: any);\n    get options(): any;\n    get registry(): import(\"./core.registry.js\").Registry;\n    /**\n       * @private\n       */\n    private _initialize;\n    clear(): Chart;\n    stop(): Chart;\n    /**\n       * Resize the chart to its container or to explicit dimensions.\n       * @param {number} [width]\n       * @param {number} [height]\n       */\n    resize(width?: number, height?: number): void;\n    _resizeBeforeDraw: {\n        width: number;\n        height: number;\n    };\n    _resize(width: any, height: any): void;\n    ensureScalesHaveIDs(): void;\n    /**\n       * Builds a map of scale ID to scale object for future lookup.\n       */\n    buildOrUpdateScales(): void;\n    /**\n       * @private\n       */\n    private _updateMetasets;\n    /**\n       * @private\n       */\n    private _removeUnreferencedMetasets;\n    buildOrUpdateControllers(): any[];\n    /**\n       * Reset the elements of all datasets\n       * @private\n       */\n    private _resetElements;\n    /**\n      * Resets the chart back to its state before the initial animation\n      */\n    reset(): void;\n    update(mode: any): void;\n    _minPadding: number;\n    /**\n     * @private\n     */\n    private _updateScales;\n    /**\n     * @private\n     */\n    private _checkEventBindings;\n    /**\n     * @private\n     */\n    private _updateHiddenIndices;\n    /**\n     * @private\n     */\n    private _getUniformDataChanges;\n    /**\n       * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`\n       * hook, in which case, plugins will not be called on `afterLayout`.\n       * @private\n       */\n    private _updateLayout;\n    /**\n       * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`\n       * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.\n       * @private\n       */\n    private _updateDatasets;\n    /**\n       * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`\n       * hook, in which case, plugins will not be called on `afterDatasetUpdate`.\n       * @private\n       */\n    private _updateDataset;\n    render(): void;\n    draw(): void;\n    /**\n       * @private\n       */\n    private _getSortedDatasetMetas;\n    /**\n       * Gets the visible dataset metas in drawing order\n       * @return {object[]}\n       */\n    getSortedVisibleDatasetMetas(): object[];\n    /**\n       * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`\n       * hook, in which case, plugins will not be called on `afterDatasetsDraw`.\n       * @private\n       */\n    private _drawDatasets;\n    /**\n       * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`\n       * hook, in which case, plugins will not be called on `afterDatasetDraw`.\n       * @private\n       */\n    private _drawDataset;\n    /**\n     * Checks whether the given point is in the chart area.\n     * @param {Point} point - in relative coordinates (see, e.g., getRelativePosition)\n     * @returns {boolean}\n     */\n    isPointInArea(point: Point): boolean;\n    getElementsAtEventForMode(e: any, mode: any, options: any, useFinalPosition: any): any;\n    getDatasetMeta(datasetIndex: any): any;\n    getContext(): {\n        chart: Chart;\n        type: string;\n    };\n    getVisibleDatasetCount(): number;\n    isDatasetVisible(datasetIndex: any): boolean;\n    setDatasetVisibility(datasetIndex: any, visible: any): void;\n    toggleDataVisibility(index: any): void;\n    getDataVisibility(index: any): boolean;\n    /**\n       * @private\n       */\n    private _updateVisibility;\n    hide(datasetIndex: any, dataIndex: any): void;\n    show(datasetIndex: any, dataIndex: any): void;\n    /**\n       * @private\n       */\n    private _destroyDatasetMeta;\n    _stop(): void;\n    destroy(): void;\n    toBase64Image(...args: any[]): any;\n    /**\n       * @private\n       */\n    private bindEvents;\n    /**\n     * @private\n     */\n    private bindUserEvents;\n    /**\n     * @private\n     */\n    private bindResponsiveEvents;\n    /**\n       * @private\n       */\n    private unbindEvents;\n    updateHoverStyle(items: any, mode: any, enabled: any): void;\n    /**\n       * Get active (hovered) elements\n       * @returns array\n       */\n    getActiveElements(): any[];\n    /**\n       * Set active (hovered) elements\n       * @param {array} activeElements New active data points\n       */\n    setActiveElements(activeElements: any[]): void;\n    /**\n       * Calls enabled plugins on the specified hook and with the given args.\n       * This method immediately returns as soon as a plugin explicitly returns false. The\n       * returned value can be used, for instance, to interrupt the current action.\n       * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').\n       * @param {Object} [args] - Extra arguments to apply to the hook call.\n     * @param {import('./core.plugins.js').filterCallback} [filter] - Filtering function for limiting which plugins are notified\n       * @returns {boolean} false if any of the plugins return false, else returns true.\n       */\n    notifyPlugins(hook: string, args?: any, filter?: import('./core.plugins.js').filterCallback): boolean;\n    /**\n     * Check if a plugin with the specific ID is registered and enabled\n     * @param {string} pluginId - The ID of the plugin of which to check if it is enabled\n     * @returns {boolean}\n     */\n    isPluginEnabled(pluginId: string): boolean;\n    /**\n       * @private\n       */\n    private _updateHoverStyles;\n    /**\n       * @private\n       */\n    private _eventHandler;\n    /**\n       * Handle an event\n       * @param {ChartEvent} e the event to handle\n       * @param {boolean} [replay] - true if the event was replayed by `update`\n     * @param {boolean} [inChartArea] - true if the event is inside chartArea\n       * @return {boolean} true if the chart needs to re-render\n       * @private\n       */\n    private _handleEvent;\n    /**\n     * @param {ChartEvent} e - The event\n     * @param {import('../types/index.js').ActiveElement[]} lastActive - Previously active elements\n     * @param {boolean} inChartArea - Is the envent inside chartArea\n     * @param {boolean} useFinalPosition - Should the evaluation be done with current or final (after animation) element positions\n     * @returns {import('../types/index.js').ActiveElement[]} - The active elements\n     * @pravate\n     */\n    _getActiveElements(e: ChartEvent, lastActive: import('../types/index.js').ActiveElement[], inChartArea: boolean, useFinalPosition: boolean): import('../types/index.js').ActiveElement[];\n}\nimport Config from \"./core.config.js\";\nimport PluginService from \"./core.plugins.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.datasetController.d.ts",
    "content": "export default class DatasetController {\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    /**\n     * Element type used to generate a meta dataset (e.g. Chart.element.LineElement).\n     */\n    static datasetElementType: any;\n    /**\n     * Element type used to generate a meta data (e.g. Chart.element.PointElement).\n     */\n    static dataElementType: any;\n    /**\n       * @param {Chart} chart\n       * @param {number} datasetIndex\n       */\n    constructor(chart: Chart, datasetIndex: number);\n    chart: import(\"./core.controller.js\").default;\n    _ctx: any;\n    index: number;\n    _cachedDataOpts: {};\n    _cachedMeta: any;\n    _type: any;\n    options: any;\n    /** @type {boolean | object} */\n    _parsing: boolean | object;\n    _data: any;\n    _objectData: any;\n    _sharedOptions: any;\n    _drawStart: any;\n    _drawCount: any;\n    enableOptionSharing: boolean;\n    supportsDecimation: boolean;\n    $context: any;\n    _syncList: any[];\n    datasetElementType: any;\n    dataElementType: any;\n    initialize(): void;\n    updateIndex(datasetIndex: any): void;\n    linkScales(): void;\n    getDataset(): any;\n    getMeta(): any;\n    /**\n       * @param {string} scaleID\n       * @return {Scale}\n       */\n    getScaleForId(scaleID: string): Scale;\n    /**\n       * @private\n       */\n    private _getOtherScale;\n    reset(): void;\n    /**\n       * @private\n       */\n    private _destroy;\n    /**\n       * @private\n       */\n    private _dataCheck;\n    addElements(): void;\n    buildOrUpdateElements(resetNewElements: any): void;\n    /**\n       * Merges user-supplied and default dataset-level options\n       * @private\n       */\n    private configure;\n    /**\n       * @param {number} start\n       * @param {number} count\n       */\n    parse(start: number, count: number): void;\n    /**\n       * Parse array of primitive values\n       * @param {object} meta - dataset meta\n       * @param {array} data - data array. Example [1,3,4]\n       * @param {number} start - start index\n       * @param {number} count - number of items to parse\n       * @returns {object} parsed item - item containing index and a parsed value\n       * for each scale id.\n       * Example: {xScale0: 0, yScale0: 1}\n       * @protected\n       */\n    protected parsePrimitiveData(meta: object, data: any[], start: number, count: number): object;\n    /**\n       * Parse array of arrays\n       * @param {object} meta - dataset meta\n       * @param {array} data - data array. Example [[1,2],[3,4]]\n       * @param {number} start - start index\n       * @param {number} count - number of items to parse\n       * @returns {object} parsed item - item containing index and a parsed value\n       * for each scale id.\n       * Example: {x: 0, y: 1}\n       * @protected\n       */\n    protected parseArrayData(meta: object, data: any[], start: number, count: number): object;\n    /**\n       * Parse array of objects\n       * @param {object} meta - dataset meta\n       * @param {array} data - data array. Example [{x:1, y:5}, {x:2, y:10}]\n       * @param {number} start - start index\n       * @param {number} count - number of items to parse\n       * @returns {object} parsed item - item containing index and a parsed value\n       * for each scale id. _custom is optional\n       * Example: {xScale0: 0, yScale0: 1, _custom: {r: 10, foo: 'bar'}}\n       * @protected\n       */\n    protected parseObjectData(meta: object, data: any[], start: number, count: number): object;\n    /**\n       * @protected\n       */\n    protected getParsed(index: any): any;\n    /**\n       * @protected\n       */\n    protected getDataElement(index: any): any;\n    /**\n       * @protected\n       */\n    protected applyStack(scale: any, parsed: any, mode: any): any;\n    /**\n       * @protected\n       */\n    protected updateRangeFromParsed(range: any, scale: any, parsed: any, stack: any): void;\n    /**\n       * @protected\n       */\n    protected getMinMax(scale: any, canStack: any): {\n        min: number;\n        max: number;\n    };\n    getAllParsedValues(scale: any): number[];\n    /**\n       * @return {number|boolean}\n       * @protected\n       */\n    protected getMaxOverflow(): number | boolean;\n    /**\n       * @protected\n       */\n    protected getLabelAndValue(index: any): {\n        label: string;\n        value: string;\n    };\n    /**\n       * @private\n       */\n    private _update;\n    /**\n       * @param {string} mode\n       */\n    update(mode: string): void;\n    draw(): void;\n    /**\n       * Returns a set of predefined style properties that should be used to represent the dataset\n       * or the data if the index is specified\n       * @param {number} index - data index\n       * @param {boolean} [active] - true if hover\n       * @return {object} style object\n       */\n    getStyle(index: number, active?: boolean): object;\n    /**\n       * @protected\n       */\n    protected getContext(index: any, active: any, mode: any): any;\n    /**\n       * @param {string} [mode]\n       * @protected\n       */\n    protected resolveDatasetElementOptions(mode?: string): any;\n    /**\n       * @param {number} index\n       * @param {string} [mode]\n       * @protected\n       */\n    protected resolveDataElementOptions(index: number, mode?: string): any;\n    /**\n       * @private\n       */\n    private _resolveElementOptions;\n    /**\n       * @private\n       */\n    private _resolveAnimations;\n    /**\n       * Utility for getting the options object shared between elements\n       * @protected\n       */\n    protected getSharedOptions(options: any): any;\n    /**\n       * Utility for determining if `options` should be included in the updated properties\n       * @protected\n       */\n    protected includeOptions(mode: any, sharedOptions: any): boolean;\n    /**\n     * @todo v4, rename to getSharedOptions and remove excess functions\n     */\n    _getSharedOptions(start: any, mode: any): {\n        sharedOptions: any;\n        includeOptions: boolean;\n    };\n    /**\n       * Utility for updating an element with new properties, using animations when appropriate.\n       * @protected\n       */\n    protected updateElement(element: any, index: any, properties: any, mode: any): void;\n    /**\n       * Utility to animate the shared options, that are potentially affecting multiple elements.\n       * @protected\n       */\n    protected updateSharedOptions(sharedOptions: any, mode: any, newOptions: any): void;\n    /**\n       * @private\n       */\n    private _setStyle;\n    removeHoverStyle(element: any, datasetIndex: any, index: any): void;\n    setHoverStyle(element: any, datasetIndex: any, index: any): void;\n    /**\n       * @private\n       */\n    private _removeDatasetHoverStyle;\n    /**\n       * @private\n       */\n    private _setDatasetHoverStyle;\n    /**\n       * @private\n       */\n    private _resyncElements;\n    /**\n       * @private\n       */\n    private _insertElements;\n    updateElements(element: any, start: any, count: any, mode: any): void;\n    /**\n       * @private\n       */\n    private _removeElements;\n    /**\n       * @private\n     */\n    private _sync;\n    _onDataPush(...args: any[]): void;\n    _onDataPop(): void;\n    _onDataShift(): void;\n    _onDataSplice(start: any, count: any, ...args: any[]): void;\n    _onDataUnshift(...args: any[]): void;\n}\nexport type Chart = import('./core.controller.js').default;\nexport type Scale = import('./core.scale.js').default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.defaults.d.ts",
    "content": "export const overrides: any;\nexport const descriptors: any;\n/**\n * Please use the module's default export which provides a singleton instance\n * Note: class is exported for typedoc\n */\nexport class Defaults {\n    constructor(_descriptors: any, _appliers: any);\n    animation: any;\n    backgroundColor: string;\n    borderColor: string;\n    color: string;\n    datasets: {};\n    devicePixelRatio: (context: any) => any;\n    elements: {};\n    events: string[];\n    font: {\n        family: string;\n        size: number;\n        style: string;\n        lineHeight: number;\n        weight: any;\n    };\n    hover: {};\n    hoverBackgroundColor: (ctx: any, options: any) => CanvasGradient;\n    hoverBorderColor: (ctx: any, options: any) => CanvasGradient;\n    hoverColor: (ctx: any, options: any) => CanvasGradient;\n    indexAxis: string;\n    interaction: {\n        mode: string;\n        intersect: boolean;\n        includeInvisible: boolean;\n    };\n    maintainAspectRatio: boolean;\n    onHover: any;\n    onClick: any;\n    parsing: boolean;\n    plugins: {};\n    responsive: boolean;\n    scale: any;\n    scales: {};\n    showLine: boolean;\n    drawActiveElementsOnTop: boolean;\n    /**\n       * @param {string|object} scope\n       * @param {object} [values]\n       */\n    set(scope: string | object, values?: object): any;\n    /**\n       * @param {string} scope\n       */\n    get(scope: string): any;\n    /**\n       * @param {string|object} scope\n       * @param {object} [values]\n       */\n    describe(scope: string | object, values?: object): any;\n    override(scope: any, values: any): any;\n    /**\n       * Routes the named defaults to fallback to another scope/name.\n       * This routing is useful when those target values, like defaults.color, are changed runtime.\n       * If the values would be copied, the runtime change would not take effect. By routing, the\n       * fallback is evaluated at each access, so its always up to date.\n       *\n       * Example:\n       *\n       * \tdefaults.route('elements.arc', 'backgroundColor', '', 'color')\n       *   - reads the backgroundColor from defaults.color when undefined locally\n       *\n       * @param {string} scope Scope this route applies to.\n       * @param {string} name Property name that should be routed to different namespace when not defined here.\n       * @param {string} targetScope The namespace where those properties should be routed to.\n       * Empty string ('') is the root of defaults.\n       * @param {string} targetName The target name in the target scope the property should be routed to.\n       */\n    route(scope: string, name: string, targetScope: string, targetName: string): void;\n    apply(appliers: any): void;\n}\ndeclare const _default: Defaults;\nexport default _default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.element.d.ts",
    "content": "import type { AnyObject } from '../types/basic.js';\nimport type { Point } from '../types/geometric.js';\nimport type { Animation } from '../types/animation.js';\nexport default class Element<T = AnyObject, O = AnyObject> {\n    static defaults: {};\n    static defaultRoutes: any;\n    x: number;\n    y: number;\n    active: boolean;\n    options: O;\n    $animations: Record<keyof T, Animation>;\n    tooltipPosition(useFinalPosition: boolean): Point;\n    hasValue(): boolean;\n    /**\n     * Gets the current or final value of each prop. Can return extra properties (whole object).\n     * @param props - properties to get\n     * @param [final] - get the final value (animation target)\n     */\n    getProps<P extends (keyof T)[]>(props: P, final?: boolean): Pick<T, P[number]>;\n    getProps<P extends string>(props: P[], final?: boolean): Partial<Record<P, unknown>>;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.interaction.d.ts",
    "content": "declare namespace _default {\n    export { evaluateInteractionItems };\n    export namespace modes {\n        /**\n             * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something\n             * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item\n             * @function Chart.Interaction.modes.index\n             * @since v2.4.0\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function index(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something\n             * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item\n             * @function Chart.Interaction.modes.index\n             * @since v2.4.0\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function index(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something\n             * If the options.intersect is false, we find the nearest item and return the items in that dataset\n             * @function Chart.Interaction.modes.dataset\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function dataset(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something\n             * If the options.intersect is false, we find the nearest item and return the items in that dataset\n             * @function Chart.Interaction.modes.dataset\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function dataset(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * Point mode returns all elements that hit test based on the event position\n             * of the event\n             * @function Chart.Interaction.modes.intersect\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function point(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * Point mode returns all elements that hit test based on the event position\n             * of the event\n             * @function Chart.Interaction.modes.intersect\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function point(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * nearest mode returns the element closest to the point\n             * @function Chart.Interaction.modes.intersect\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function nearest(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * nearest mode returns the element closest to the point\n             * @function Chart.Interaction.modes.intersect\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function nearest(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * x mode returns the elements that hit-test at the current x coordinate\n             * @function Chart.Interaction.modes.x\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function x(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * x mode returns the elements that hit-test at the current x coordinate\n             * @function Chart.Interaction.modes.x\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function x(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * y mode returns the elements that hit-test at the current y coordinate\n             * @function Chart.Interaction.modes.y\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function y(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n        /**\n             * y mode returns the elements that hit-test at the current y coordinate\n             * @function Chart.Interaction.modes.y\n             * @param {Chart} chart - the chart we are returning items from\n             * @param {Event} e - the event we are find things at\n             * @param {InteractionOptions} options - options to use\n             * @param {boolean} [useFinalPosition] - use final element position (animation target)\n             * @return {InteractionItem[]} - items that are found\n             */\n        function y(chart: import(\"./core.controller.js\").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];\n    }\n}\nexport default _default;\nexport type Chart = import('./core.controller.js').default;\nexport type ChartEvent = import('../types/index.js').ChartEvent;\nexport type InteractionOptions = {\n    axis?: string;\n    intersect?: boolean;\n    includeInvisible?: boolean;\n};\nexport type InteractionItem = {\n    datasetIndex: number;\n    index: number;\n    element: import('./core.element.js').default;\n};\nexport type Point = import('../types/index.js').Point;\n/**\n * Helper function to select candidate elements for interaction\n * @param {Chart} chart - the chart\n * @param {string} axis - the axis mode. x|y|xy|r\n * @param {Point} position - the point to be nearest to, in relative coordinates\n * @param {function} handler - the callback to execute for each visible item\n * @param {boolean} [intersect] - consider intersecting items\n */\ndeclare function evaluateInteractionItems(chart: Chart, axis: string, position: Point, handler: Function, intersect?: boolean): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.layouts.d.ts",
    "content": "declare namespace _default {\n    /**\n       * Register a box to a chart.\n       * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.\n       * @param {Chart} chart - the chart to use\n       * @param {LayoutItem} item - the item to add to be laid out\n       */\n    function addBox(chart: import(\"./core.controller.js\").default, item: LayoutItem): void;\n    /**\n       * Register a box to a chart.\n       * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.\n       * @param {Chart} chart - the chart to use\n       * @param {LayoutItem} item - the item to add to be laid out\n       */\n    function addBox(chart: import(\"./core.controller.js\").default, item: LayoutItem): void;\n    /**\n       * Remove a layoutItem from a chart\n       * @param {Chart} chart - the chart to remove the box from\n       * @param {LayoutItem} layoutItem - the item to remove from the layout\n       */\n    function removeBox(chart: import(\"./core.controller.js\").default, layoutItem: LayoutItem): void;\n    /**\n       * Remove a layoutItem from a chart\n       * @param {Chart} chart - the chart to remove the box from\n       * @param {LayoutItem} layoutItem - the item to remove from the layout\n       */\n    function removeBox(chart: import(\"./core.controller.js\").default, layoutItem: LayoutItem): void;\n    /**\n       * Sets (or updates) options on the given `item`.\n       * @param {Chart} chart - the chart in which the item lives (or will be added to)\n       * @param {LayoutItem} item - the item to configure with the given options\n       * @param {object} options - the new item options.\n       */\n    function configure(chart: import(\"./core.controller.js\").default, item: LayoutItem, options: any): void;\n    /**\n       * Sets (or updates) options on the given `item`.\n       * @param {Chart} chart - the chart in which the item lives (or will be added to)\n       * @param {LayoutItem} item - the item to configure with the given options\n       * @param {object} options - the new item options.\n       */\n    function configure(chart: import(\"./core.controller.js\").default, item: LayoutItem, options: any): void;\n    /**\n       * Fits boxes of the given chart into the given size by having each box measure itself\n       * then running a fitting algorithm\n       * @param {Chart} chart - the chart\n       * @param {number} width - the width to fit into\n       * @param {number} height - the height to fit into\n     * @param {number} minPadding - minimum padding required for each side of chart area\n       */\n    function update(chart: import(\"./core.controller.js\").default, width: number, height: number, minPadding: number): void;\n    /**\n       * Fits boxes of the given chart into the given size by having each box measure itself\n       * then running a fitting algorithm\n       * @param {Chart} chart - the chart\n       * @param {number} width - the width to fit into\n       * @param {number} height - the height to fit into\n     * @param {number} minPadding - minimum padding required for each side of chart area\n       */\n    function update(chart: import(\"./core.controller.js\").default, width: number, height: number, minPadding: number): void;\n}\nexport default _default;\nexport type Chart = import('./core.controller.js').default;\nexport type LayoutItem = {\n    /**\n     * - The position of the item in the chart layout. Possible values are\n     * 'left', 'top', 'right', 'bottom', and 'chartArea'\n     */\n    position: string;\n    /**\n     * - The weight used to sort the item. Higher weights are further away from the chart area\n     */\n    weight: number;\n    /**\n     * - if true, and the item is horizontal, then push vertical boxes down\n     */\n    fullSize: boolean;\n    /**\n     * - returns true if the layout item is horizontal (ie. top or bottom)\n     */\n    isHorizontal: Function;\n    /**\n     * - Takes two parameters: width and height. Returns size of item\n     */\n    update: Function;\n    /**\n     * - Draws the element\n     */\n    draw: Function;\n    /**\n     * -  Returns an object with padding on the edges\n     */\n    getPadding?: Function;\n    /**\n     * - Width of item. Must be valid after update()\n     */\n    width: number;\n    /**\n     * - Height of item. Must be valid after update()\n     */\n    height: number;\n    /**\n     * - Left edge of the item. Set by layout system and cannot be used in update\n     */\n    left: number;\n    /**\n     * - Top edge of the item. Set by layout system and cannot be used in update\n     */\n    top: number;\n    /**\n     * - Right edge of the item. Set by layout system and cannot be used in update\n     */\n    right: number;\n    /**\n     * - Bottom edge of the item. Set by layout system and cannot be used in update\n     */\n    bottom: number;\n};\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.layouts.defaults.d.ts",
    "content": "export function applyLayoutsDefaults(defaults: any): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.plugins.d.ts",
    "content": "/**\n * @typedef { import('./core.controller.js').default } Chart\n * @typedef { import('../types/index.js').ChartEvent } ChartEvent\n * @typedef { import('../plugins/plugin.tooltip.js').default } Tooltip\n */\n/**\n * @callback filterCallback\n * @param {{plugin: object, options: object}} value\n * @param {number} [index]\n * @param {array} [array]\n * @param {object} [thisArg]\n * @return {boolean}\n */\nexport default class PluginService {\n    _init: any[];\n    /**\n       * Calls enabled plugins for `chart` on the specified hook and with the given args.\n       * This method immediately returns as soon as a plugin explicitly returns false. The\n       * returned value can be used, for instance, to interrupt the current action.\n       * @param {Chart} chart - The chart instance for which plugins should be called.\n       * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').\n       * @param {object} [args] - Extra arguments to apply to the hook call.\n     * @param {filterCallback} [filter] - Filtering function for limiting which plugins are notified\n       * @returns {boolean} false if any of the plugins return false, else returns true.\n       */\n    notify(chart: Chart, hook: string, args?: object, filter?: filterCallback): boolean;\n    /**\n       * @private\n       */\n    private _notify;\n    invalidate(): void;\n    _oldCache: {\n        plugin: any;\n        options: any;\n    }[];\n    _cache: {\n        plugin: any;\n        options: any;\n    }[];\n    /**\n       * @param {Chart} chart\n       * @private\n       */\n    private _descriptors;\n    _createDescriptors(chart: any, all: any): {\n        plugin: any;\n        options: any;\n    }[];\n    /**\n       * @param {Chart} chart\n       * @private\n       */\n    private _notifyStateChanges;\n}\nexport type Chart = import('./core.controller.js').default;\nexport type ChartEvent = import('../types/index.js').ChartEvent;\nexport type Tooltip = any;\nexport type filterCallback = (value: {\n    plugin: object;\n    options: object;\n}, index?: number, array?: any[], thisArg?: object) => boolean;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.registry.d.ts",
    "content": "/**\n * Please use the module's default export which provides a singleton instance\n * Note: class is exported for typedoc\n */\nexport class Registry {\n    controllers: TypedRegistry;\n    elements: TypedRegistry;\n    plugins: TypedRegistry;\n    scales: TypedRegistry;\n    _typedRegistries: TypedRegistry[];\n    /**\n       * @param  {...any} args\n       */\n    add(...args: any[]): void;\n    remove(...args: any[]): void;\n    /**\n       * @param  {...typeof DatasetController} args\n       */\n    addControllers(...args: (typeof DatasetController)[]): void;\n    /**\n       * @param  {...typeof Element} args\n       */\n    addElements(...args: (typeof Element)[]): void;\n    /**\n       * @param  {...any} args\n       */\n    addPlugins(...args: any[]): void;\n    /**\n       * @param  {...typeof Scale} args\n       */\n    addScales(...args: (typeof Scale)[]): void;\n    /**\n       * @param {string} id\n       * @returns {typeof DatasetController}\n       */\n    getController(id: string): typeof DatasetController;\n    /**\n       * @param {string} id\n       * @returns {typeof Element}\n       */\n    getElement(id: string): typeof Element;\n    /**\n       * @param {string} id\n       * @returns {object}\n       */\n    getPlugin(id: string): object;\n    /**\n       * @param {string} id\n       * @returns {typeof Scale}\n       */\n    getScale(id: string): typeof Scale;\n    /**\n       * @param  {...typeof DatasetController} args\n       */\n    removeControllers(...args: (typeof DatasetController)[]): void;\n    /**\n       * @param  {...typeof Element} args\n       */\n    removeElements(...args: (typeof Element)[]): void;\n    /**\n       * @param  {...any} args\n       */\n    removePlugins(...args: any[]): void;\n    /**\n       * @param  {...typeof Scale} args\n       */\n    removeScales(...args: (typeof Scale)[]): void;\n    /**\n       * @private\n       */\n    private _each;\n    /**\n       * @private\n       */\n    private _exec;\n    /**\n       * @private\n       */\n    private _getRegistryForType;\n    /**\n       * @private\n       */\n    private _get;\n}\ndeclare const _default: Registry;\nexport default _default;\nimport TypedRegistry from \"./core.typedRegistry.js\";\nimport DatasetController from \"./core.datasetController.js\";\nimport Element from \"./core.element.js\";\nimport Scale from \"./core.scale.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.scale.autoskip.d.ts",
    "content": "/**\n * @typedef { import('./core.controller.js').default } Chart\n * @typedef {{value:number | string, label?:string, major?:boolean, $context?:any}} Tick\n */\n/**\n * Returns a subset of ticks to be plotted to avoid overlapping labels.\n * @param {import('./core.scale.js').default} scale\n * @param {Tick[]} ticks\n * @return {Tick[]}\n * @private\n */\nexport function autoSkip(scale: import('./core.scale.js').default, ticks: Tick[]): Tick[];\nexport type Chart = import('./core.controller.js').default;\nexport type Tick = {\n    value: number | string;\n    label?: string;\n    major?: boolean;\n    $context?: any;\n};\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.scale.d.ts",
    "content": "export default class Scale extends Element<import(\"../types/basic.js\").AnyObject, import(\"../types/basic.js\").AnyObject> {\n    constructor(cfg: any);\n    /** @type {string} */\n    id: string;\n    /** @type {string} */\n    type: string;\n    /** @type {any} */\n    options: any;\n    /** @type {CanvasRenderingContext2D} */\n    ctx: CanvasRenderingContext2D;\n    /** @type {Chart} */\n    chart: Chart;\n    /** @type {number} */\n    top: number;\n    /** @type {number} */\n    bottom: number;\n    /** @type {number} */\n    left: number;\n    /** @type {number} */\n    right: number;\n    /** @type {number} */\n    width: number;\n    /** @type {number} */\n    height: number;\n    _margins: {\n        left: number;\n        right: number;\n        top: number;\n        bottom: number;\n    };\n    /** @type {number} */\n    maxWidth: number;\n    /** @type {number} */\n    maxHeight: number;\n    /** @type {number} */\n    paddingTop: number;\n    /** @type {number} */\n    paddingBottom: number;\n    /** @type {number} */\n    paddingLeft: number;\n    /** @type {number} */\n    paddingRight: number;\n    /** @type {string=} */\n    axis: string | undefined;\n    /** @type {number=} */\n    labelRotation: number | undefined;\n    min: any;\n    max: any;\n    _range: {\n        min: number; /** @type {object[]|null} */\n        max: number;\n    };\n    /** @type {Tick[]} */\n    ticks: Tick[];\n    /** @type {object[]|null} */\n    _gridLineItems: object[] | null;\n    /** @type {object[]|null} */\n    _labelItems: object[] | null;\n    /** @type {object|null} */\n    _labelSizes: object | null;\n    _length: number;\n    _maxLength: number;\n    _longestTextCache: {};\n    /** @type {number} */\n    _startPixel: number;\n    /** @type {number} */\n    _endPixel: number;\n    _reversePixels: boolean;\n    _userMax: any;\n    _userMin: any;\n    _suggestedMax: any;\n    _suggestedMin: any;\n    _ticksLength: number;\n    _borderValue: number;\n    _cache: {};\n    _dataLimitsCached: boolean;\n    $context: any;\n    /**\n       * @param {any} options\n       * @since 3.0\n       */\n    init(options: any): void;\n    /**\n       * Parse a supported input value to internal representation.\n       * @param {*} raw\n       * @param {number} [index]\n       * @since 3.0\n       */\n    parse(raw: any, index?: number): any;\n    /**\n       * @return {{min: number, max: number, minDefined: boolean, maxDefined: boolean}}\n       * @protected\n       * @since 3.0\n       */\n    protected getUserBounds(): {\n        min: number;\n        max: number;\n        minDefined: boolean;\n        maxDefined: boolean;\n    };\n    /**\n       * @param {boolean} canStack\n       * @return {{min: number, max: number}}\n       * @protected\n       * @since 3.0\n       */\n    protected getMinMax(canStack: boolean): {\n        min: number;\n        max: number;\n    };\n    /**\n       * Get the padding needed for the scale\n       * @return {{top: number, left: number, bottom: number, right: number}} the necessary padding\n       * @private\n       */\n    private getPadding;\n    /**\n       * Returns the scale tick objects\n       * @return {Tick[]}\n       * @since 2.7\n       */\n    getTicks(): Tick[];\n    /**\n       * @return {string[]}\n       */\n    getLabels(): string[];\n    /**\n     * @return {import('../types.js').LabelItem[]}\n     */\n    getLabelItems(chartArea?: any): import('../types.js').LabelItem[];\n    beforeLayout(): void;\n    beforeUpdate(): void;\n    /**\n       * @param {number} maxWidth - the max width in pixels\n       * @param {number} maxHeight - the max height in pixels\n       * @param {{top: number, left: number, bottom: number, right: number}} margins - the space between the edge of the other scales and edge of the chart\n       *   This space comes from two sources:\n       *     - padding - space that's required to show the labels at the edges of the scale\n       *     - thickness of scales or legends in another orientation\n       */\n    update(maxWidth: number, maxHeight: number, margins: {\n        top: number;\n        left: number;\n        bottom: number;\n        right: number;\n    }): void;\n    /**\n       * @protected\n       */\n    protected configure(): void;\n    _alignToPixels: any;\n    afterUpdate(): void;\n    beforeSetDimensions(): void;\n    setDimensions(): void;\n    afterSetDimensions(): void;\n    _callHooks(name: any): void;\n    beforeDataLimits(): void;\n    determineDataLimits(): void;\n    afterDataLimits(): void;\n    beforeBuildTicks(): void;\n    /**\n       * @return {object[]} the ticks\n       */\n    buildTicks(): object[];\n    afterBuildTicks(): void;\n    beforeTickToLabelConversion(): void;\n    /**\n       * Convert ticks to label strings\n       * @param {Tick[]} ticks\n       */\n    generateTickLabels(ticks: Tick[]): void;\n    afterTickToLabelConversion(): void;\n    beforeCalculateLabelRotation(): void;\n    calculateLabelRotation(): void;\n    afterCalculateLabelRotation(): void;\n    afterAutoSkip(): void;\n    beforeFit(): void;\n    fit(): void;\n    _calculatePadding(first: any, last: any, sin: any, cos: any): void;\n    /**\n       * Handle margins and padding interactions\n       * @private\n       */\n    private _handleMargins;\n    afterFit(): void;\n    /**\n       * @return {boolean}\n       */\n    isHorizontal(): boolean;\n    /**\n       * @return {boolean}\n       */\n    isFullSize(): boolean;\n    /**\n       * @param {Tick[]} ticks\n       * @private\n       */\n    private _convertTicksToLabels;\n    /**\n       * @return {{ first: object, last: object, widest: object, highest: object, widths: Array, heights: array }}\n       * @private\n       */\n    private _getLabelSizes;\n    /**\n       * Returns {width, height, offset} objects for the first, last, widest, highest tick\n       * labels where offset indicates the anchor point offset from the top in pixels.\n       * @return {{ first: object, last: object, widest: object, highest: object, widths: Array, heights: array }}\n       * @private\n       */\n    private _computeLabelSizes;\n    /**\n       * Used to get the label to display in the tooltip for the given value\n       * @param {*} value\n       * @return {string}\n       */\n    getLabelForValue(value: any): string;\n    /**\n       * Returns the location of the given data point. Value can either be an index or a numerical value\n       * The coordinate (0, 0) is at the upper-left corner of the canvas\n       * @param {*} value\n       * @param {number} [index]\n       * @return {number}\n       */\n    getPixelForValue(value: any, index?: number): number;\n    /**\n       * Used to get the data value from a given pixel. This is the inverse of getPixelForValue\n       * The coordinate (0, 0) is at the upper-left corner of the canvas\n       * @param {number} pixel\n       * @return {*}\n       */\n    getValueForPixel(pixel: number): any;\n    /**\n       * Returns the location of the tick at the given index\n       * The coordinate (0, 0) is at the upper-left corner of the canvas\n       * @param {number} index\n       * @return {number}\n       */\n    getPixelForTick(index: number): number;\n    /**\n       * Utility for getting the pixel location of a percentage of scale\n       * The coordinate (0, 0) is at the upper-left corner of the canvas\n       * @param {number} decimal\n       * @return {number}\n       */\n    getPixelForDecimal(decimal: number): number;\n    /**\n       * @param {number} pixel\n       * @return {number}\n       */\n    getDecimalForPixel(pixel: number): number;\n    /**\n       * Returns the pixel for the minimum chart value\n       * The coordinate (0, 0) is at the upper-left corner of the canvas\n       * @return {number}\n       */\n    getBasePixel(): number;\n    /**\n       * @return {number}\n       */\n    getBaseValue(): number;\n    /**\n       * @protected\n       */\n    protected getContext(index: any): any;\n    /**\n       * @return {number}\n       * @private\n       */\n    private _tickSize;\n    /**\n       * @return {boolean}\n       * @private\n       */\n    private _isVisible;\n    /**\n       * @private\n       */\n    private _computeGridLineItems;\n    /**\n       * @private\n       */\n    private _computeLabelItems;\n    _getXAxisLabelAlignment(): string;\n    _getYAxisLabelAlignment(tl: any): {\n        textAlign: string;\n        x: any;\n    };\n    /**\n       * @private\n       */\n    private _computeLabelArea;\n    /**\n     * @protected\n     */\n    protected drawBackground(): void;\n    getLineWidthForValue(value: any): any;\n    /**\n       * @protected\n       */\n    protected drawGrid(chartArea: any): void;\n    /**\n       * @protected\n       */\n    protected drawBorder(): void;\n    /**\n       * @protected\n       */\n    protected drawLabels(chartArea: any): void;\n    /**\n       * @protected\n       */\n    protected drawTitle(): void;\n    draw(chartArea: any): void;\n    /**\n       * @return {object[]}\n       * @private\n       */\n    private _layers;\n    /**\n       * Returns visible dataset metas that are attached to this scale\n       * @param {string} [type] - if specified, also filter by dataset type\n       * @return {object[]}\n       */\n    getMatchingVisibleMetas(type?: string): object[];\n    /**\n       * @param {number} index\n       * @return {object}\n       * @protected\n       */\n    protected _resolveTickFontOptions(index: number): object;\n    /**\n     * @protected\n     */\n    protected _maxDigits(): number;\n}\nexport type Chart = import('./core.controller.js').default;\nexport type Tick = {\n    value: number | string;\n    label?: string;\n    major?: boolean;\n    $context?: any;\n};\nimport Element from \"./core.element.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.scale.defaults.d.ts",
    "content": "export function applyScaleDefaults(defaults: any): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.ticks.d.ts",
    "content": "declare namespace _default {\n    export { formatters };\n}\nexport default _default;\ndeclare namespace formatters {\n    /**\n     * Formatter for value labels\n     * @method Chart.Ticks.formatters.values\n     * @param value the value to display\n     * @return {string|string[]} the label to display\n     */\n    function values(value: any): string | string[];\n    /**\n     * Formatter for value labels\n     * @method Chart.Ticks.formatters.values\n     * @param value the value to display\n     * @return {string|string[]} the label to display\n     */\n    function values(value: any): string | string[];\n    /**\n     * Formatter for numeric ticks\n     * @method Chart.Ticks.formatters.numeric\n     * @param tickValue {number} the value to be formatted\n     * @param index {number} the position of the tickValue parameter in the ticks array\n     * @param ticks {object[]} the list of ticks being converted\n     * @return {string} string representation of the tickValue parameter\n     */\n    function numeric(tickValue: number, index: number, ticks: any[]): string;\n    /**\n     * Formatter for numeric ticks\n     * @method Chart.Ticks.formatters.numeric\n     * @param tickValue {number} the value to be formatted\n     * @param index {number} the position of the tickValue parameter in the ticks array\n     * @param ticks {object[]} the list of ticks being converted\n     * @return {string} string representation of the tickValue parameter\n     */\n    function numeric(tickValue: number, index: number, ticks: any[]): string;\n    /**\n     * Formatter for logarithmic ticks\n     * @method Chart.Ticks.formatters.logarithmic\n     * @param tickValue {number} the value to be formatted\n     * @param index {number} the position of the tickValue parameter in the ticks array\n     * @param ticks {object[]} the list of ticks being converted\n     * @return {string} string representation of the tickValue parameter\n     */\n    function logarithmic(tickValue: number, index: number, ticks: any[]): string;\n    /**\n     * Formatter for logarithmic ticks\n     * @method Chart.Ticks.formatters.logarithmic\n     * @param tickValue {number} the value to be formatted\n     * @param index {number} the position of the tickValue parameter in the ticks array\n     * @param ticks {object[]} the list of ticks being converted\n     * @return {string} string representation of the tickValue parameter\n     */\n    function logarithmic(tickValue: number, index: number, ticks: any[]): string;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/core.typedRegistry.d.ts",
    "content": "/**\n * @typedef {{id: string, defaults: any, overrides?: any, defaultRoutes: any}} IChartComponent\n */\nexport default class TypedRegistry {\n    constructor(type: any, scope: any, override: any);\n    type: any;\n    scope: any;\n    override: any;\n    items: any;\n    isForType(type: any): boolean;\n    /**\n       * @param {IChartComponent} item\n       * @returns {string} The scope where items defaults were registered to.\n       */\n    register(item: IChartComponent): string;\n    /**\n       * @param {string} id\n       * @returns {object?}\n       */\n    get(id: string): object | null;\n    /**\n       * @param {IChartComponent} item\n       */\n    unregister(item: IChartComponent): void;\n}\nexport type IChartComponent = {\n    id: string;\n    defaults: any;\n    overrides?: any;\n    defaultRoutes: any;\n};\nimport defaults from \"./core.defaults.js\";\nimport { overrides } from \"./core.defaults.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/core/index.d.ts",
    "content": "export type { DateAdapter, TimeUnit } from './core.adapters.js';\nexport { default as _adapters } from './core.adapters.js';\nexport { default as Animation } from './core.animation.js';\nexport { default as Animations } from './core.animations.js';\nexport { default as animator } from './core.animator.js';\nexport { default as Chart } from './core.controller.js';\nexport { default as DatasetController } from './core.datasetController.js';\nexport { default as defaults } from './core.defaults.js';\nexport { default as Element } from './core.element.js';\nexport { default as Interaction } from './core.interaction.js';\nexport { default as layouts } from './core.layouts.js';\nexport { default as plugins } from './core.plugins.js';\nexport { default as registry } from './core.registry.js';\nexport { default as Scale } from './core.scale.js';\nexport { default as Ticks } from './core.ticks.js';\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/elements/element.arc.d.ts",
    "content": "import Element from '../core/core.element.js';\nimport type { ArcOptions, Point } from '../types/index.js';\nexport interface ArcProps extends Point {\n    startAngle: number;\n    endAngle: number;\n    innerRadius: number;\n    outerRadius: number;\n    circumference: number;\n}\nexport default class ArcElement extends Element<ArcProps, ArcOptions> {\n    static id: string;\n    static defaults: {\n        borderAlign: string;\n        borderColor: string;\n        borderJoinStyle: any;\n        borderRadius: number;\n        borderWidth: number;\n        offset: number;\n        spacing: number;\n        angle: any;\n        circular: boolean;\n    };\n    static defaultRoutes: {\n        backgroundColor: string;\n    };\n    circumference: number;\n    endAngle: number;\n    fullCircles: number;\n    innerRadius: number;\n    outerRadius: number;\n    pixelMargin: number;\n    startAngle: number;\n    constructor(cfg: any);\n    inRange(chartX: number, chartY: number, useFinalPosition: boolean): boolean;\n    getCenterPoint(useFinalPosition: boolean): {\n        x: number;\n        y: number;\n    };\n    tooltipPosition(useFinalPosition: boolean): {\n        x: number;\n        y: number;\n    };\n    draw(ctx: CanvasRenderingContext2D): void;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/elements/element.bar.d.ts",
    "content": "export default class BarElement extends Element<import(\"../types/basic.js\").AnyObject, import(\"../types/basic.js\").AnyObject> {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    constructor(cfg: any);\n    options: any;\n    horizontal: any;\n    base: any;\n    width: any;\n    height: any;\n    inflateAmount: any;\n    draw(ctx: any): void;\n    inRange(mouseX: any, mouseY: any, useFinalPosition: any): boolean;\n    inXRange(mouseX: any, useFinalPosition: any): boolean;\n    inYRange(mouseY: any, useFinalPosition: any): boolean;\n    getCenterPoint(useFinalPosition: any): {\n        x: number;\n        y: number;\n    };\n    getRange(axis: any): number;\n}\nexport type BarProps = {\n    x: number;\n    y: number;\n    base: number;\n    horizontal: boolean;\n    width: number;\n    height: number;\n};\nimport Element from \"../core/core.element.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/elements/element.line.d.ts",
    "content": "export default class LineElement extends Element<import(\"../types/basic.js\").AnyObject, import(\"../types/basic.js\").AnyObject> {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    static descriptors: {\n        _scriptable: boolean;\n        _indexable: (name: any) => boolean;\n    };\n    constructor(cfg: any);\n    animated: boolean;\n    options: any;\n    _chart: any;\n    _loop: any;\n    _fullLoop: any;\n    _path: any;\n    _points: any;\n    _segments: import(\"../helpers/helpers.segment.js\").Segment[];\n    _decimated: boolean;\n    _pointsUpdated: boolean;\n    _datasetIndex: any;\n    updateControlPoints(chartArea: any, indexAxis: any): void;\n    set points(arg: any);\n    get points(): any;\n    get segments(): import(\"../helpers/helpers.segment.js\").Segment[];\n    /**\n       * First non-skipped point on this line\n       * @returns {PointElement|undefined}\n       */\n    first(): PointElement | undefined;\n    /**\n       * Last non-skipped point on this line\n       * @returns {PointElement|undefined}\n       */\n    last(): PointElement | undefined;\n    /**\n       * Interpolate a point in this line at the same value on `property` as\n       * the reference `point` provided\n       * @param {PointElement} point - the reference point\n       * @param {string} property - the property to match on\n       * @returns {PointElement|undefined}\n       */\n    interpolate(point: PointElement, property: string): PointElement | undefined;\n    /**\n       * Append a segment of this line to current path.\n       * @param {CanvasRenderingContext2D} ctx\n       * @param {object} segment\n       * @param {number} segment.start - start index of the segment, referring the points array\n       * @param {number} segment.end - end index of the segment, referring the points array\n       * @param {boolean} segment.loop - indicates that the segment is a loop\n       * @param {object} params\n       * @param {boolean} params.move - move to starting point (vs line to it)\n       * @param {boolean} params.reverse - path the segment from end to start\n       * @param {number} params.start - limit segment to points starting from `start` index\n       * @param {number} params.end - limit segment to points ending at `start` + `count` index\n       * @returns {undefined|boolean} - true if the segment is a full loop (path should be closed)\n       */\n    pathSegment(ctx: CanvasRenderingContext2D, segment: {\n        start: number;\n        end: number;\n        loop: boolean;\n    }, params: {\n        move: boolean;\n        reverse: boolean;\n        start: number;\n        end: number;\n    }): undefined | boolean;\n    /**\n       * Append all segments of this line to current path.\n       * @param {CanvasRenderingContext2D|Path2D} ctx\n       * @param {number} [start]\n       * @param {number} [count]\n       * @returns {undefined|boolean} - true if line is a full loop (path should be closed)\n       */\n    path(ctx: CanvasRenderingContext2D | Path2D, start?: number, count?: number): undefined | boolean;\n    /**\n       * Draw\n       * @param {CanvasRenderingContext2D} ctx\n       * @param {object} chartArea\n       * @param {number} [start]\n       * @param {number} [count]\n       */\n    draw(ctx: CanvasRenderingContext2D, chartArea: object, start?: number, count?: number): void;\n}\nexport type PointElement = import('./element.point.js').default;\nimport Element from \"../core/core.element.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/elements/element.point.d.ts",
    "content": "import Element from '../core/core.element.js';\nimport type { CartesianParsedData, ChartArea, Point, PointHoverOptions, PointOptions } from '../types/index.js';\nexport declare type PointProps = Point;\nexport default class PointElement extends Element<PointProps, PointOptions & PointHoverOptions> {\n    static id: string;\n    parsed: CartesianParsedData;\n    skip?: boolean;\n    stop?: boolean;\n    /**\n     * @type {any}\n     */\n    static defaults: {\n        borderWidth: number;\n        hitRadius: number;\n        hoverBorderWidth: number;\n        hoverRadius: number;\n        pointStyle: string;\n        radius: number;\n        rotation: number;\n    };\n    /**\n     * @type {any}\n     */\n    static defaultRoutes: {\n        backgroundColor: string;\n        borderColor: string;\n    };\n    constructor(cfg: any);\n    inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;\n    inXRange(mouseX: number, useFinalPosition?: boolean): boolean;\n    inYRange(mouseY: number, useFinalPosition?: boolean): boolean;\n    getCenterPoint(useFinalPosition?: boolean): {\n        x: number;\n        y: number;\n    };\n    size(options?: Partial<PointOptions & PointHoverOptions>): number;\n    draw(ctx: CanvasRenderingContext2D, area: ChartArea): void;\n    getRange(): any;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/elements/index.d.ts",
    "content": "export { default as ArcElement } from \"./element.arc.js\";\nexport { default as LineElement } from \"./element.line.js\";\nexport { default as PointElement } from \"./element.point.js\";\nexport { default as BarElement } from \"./element.bar.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.canvas.d.ts",
    "content": "/**\n * Note: typedefs are auto-exported, so use a made-up `canvas` namespace where\n * necessary to avoid duplicates with `export * from './helpers`; see\n * https://github.com/microsoft/TypeScript/issues/46011\n * @typedef { import('../core/core.controller.js').default } canvas.Chart\n * @typedef { import('../types/index.js').Point } Point\n */\n/**\n * @namespace Chart.helpers.canvas\n */\n/**\n * Converts the given font object into a CSS font string.\n * @param {object} font - A font object.\n * @return {string|null} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font\n * @private\n */\nexport function toFontString(font: object): string | null;\n/**\n * @private\n */\nexport function _measureText(ctx: any, data: any, gc: any, longest: any, string: any): any;\n/**\n * @private\n */\nexport function _longestText(ctx: any, font: any, arrayOfThings: any, cache: any): number;\n/**\n * Returns the aligned pixel value to avoid anti-aliasing blur\n * @param {canvas.Chart} chart - The chart instance.\n * @param {number} pixel - A pixel value.\n * @param {number} width - The width of the element.\n * @returns {number} The aligned pixel value.\n * @private\n */\nexport function _alignPixel(chart: canvas.Chart, pixel: number, width: number): number;\n/**\n * Clears the entire canvas.\n * @param {HTMLCanvasElement} canvas\n * @param {CanvasRenderingContext2D} [ctx]\n */\nexport function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void;\nexport function drawPoint(ctx: any, options: any, x: any, y: any): void;\nexport function drawPointLegend(ctx: any, options: any, x: any, y: any, w: any): void;\n/**\n * Returns true if the point is inside the rectangle\n * @param {Point} point - The point to test\n * @param {object} area - The rectangle\n * @param {number} [margin] - allowed margin\n * @returns {boolean}\n * @private\n */\nexport function _isPointInArea(point: Point, area: object, margin?: number): boolean;\nexport function clipArea(ctx: any, area: any): void;\nexport function unclipArea(ctx: any): void;\n/**\n * @private\n */\nexport function _steppedLineTo(ctx: any, previous: any, target: any, flip: any, mode: any): any;\n/**\n * @private\n */\nexport function _bezierCurveTo(ctx: any, previous: any, target: any, flip: any): any;\n/**\n * Render text onto the canvas\n */\nexport function renderText(ctx: any, text: any, x: any, y: any, font: any, opts?: {}): void;\n/**\n * Add a path of a rectangle with rounded corners to the current sub-path\n * @param {CanvasRenderingContext2D} ctx Context\n * @param {*} rect Bounding rect\n */\nexport function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: any): void;\nexport namespace canvas {\n    /**\n     * Note: typedefs are auto-exported, so use a made-up `canvas` namespace where\n     * necessary to avoid duplicates with `export * from './helpers`; see\n     * https://github.com/microsoft/TypeScript/issues/46011\n     */\n    type Chart = import('../core/core.controller.js').default;\n}\n/**\n * Note: typedefs are auto-exported, so use a made-up `canvas` namespace where\n * necessary to avoid duplicates with `export * from './helpers`; see\n * https://github.com/microsoft/TypeScript/issues/46011\n */\nexport type Point = import('../types/index.js').Point;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.collection.d.ts",
    "content": "/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param value - value to find\n * @param cmp\n * @private\n */\nexport declare function _lookup(table: number[], value: number, cmp?: (value: number) => boolean): {\n    lo: number;\n    hi: number;\n};\nexport declare function _lookup<T>(table: T[], value: number, cmp: (value: number) => boolean): {\n    lo: number;\n    hi: number;\n};\n/**\n * Binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @param last - lookup last index\n * @private\n */\nexport declare const _lookupByKey: (table: Record<string, number>[], key: string, value: number, last?: boolean) => {\n    lo: number;\n    hi: number;\n};\n/**\n * Reverse binary search\n * @param table - the table search. must be sorted!\n * @param key - property name for the value in each entry\n * @param value - value to find\n * @private\n */\nexport declare const _rlookupByKey: (table: Record<string, number>[], key: string, value: number) => {\n    lo: number;\n    hi: number;\n};\n/**\n * Return subset of `values` between `min` and `max` inclusive.\n * Values are assumed to be in sorted order.\n * @param values - sorted array of values\n * @param min - min value\n * @param max - max value\n */\nexport declare function _filterBetween(values: number[], min: number, max: number): number[];\nexport interface ArrayListener<T> {\n    _onDataPush?(...item: T[]): void;\n    _onDataPop?(): void;\n    _onDataShift?(): void;\n    _onDataSplice?(index: number, deleteCount: number, ...items: T[]): void;\n    _onDataUnshift?(...item: T[]): void;\n}\n/**\n * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',\n * 'unshift') and notify the listener AFTER the array has been altered. Listeners are\n * called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments.\n */\nexport declare function listenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;\n/**\n * Removes the given array event listener and cleanup extra attached properties (such as\n * the _chartjs stub and overridden methods) if array doesn't have any more listeners.\n */\nexport declare function unlistenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;\n/**\n * @param items\n */\nexport declare function _arrayUnique<T>(items: T[]): T[];\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.color.d.ts",
    "content": "import { Color } from '@kurkle/color';\nexport declare function isPatternOrGradient(value: unknown): value is CanvasPattern | CanvasGradient;\nexport declare function color(value: CanvasGradient): CanvasGradient;\nexport declare function color(value: CanvasPattern): CanvasPattern;\nexport declare function color(value: string | {\n    r: number;\n    g: number;\n    b: number;\n    a: number;\n} | [number, number, number] | [number, number, number, number]): Color;\nexport declare function getHoverColor(value: CanvasGradient): CanvasGradient;\nexport declare function getHoverColor(value: CanvasPattern): CanvasPattern;\nexport declare function getHoverColor(value: string): string;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.config.d.ts",
    "content": "/**\n * Creates a Proxy for resolving raw values for options.\n * @param {object[]} scopes - The option scopes to look for values, in resolution order\n * @param {string[]} [prefixes] - The prefixes for values, in resolution order.\n * @param {object[]} [rootScopes] - The root option scopes\n * @param {string|boolean} [fallback] - Parent scopes fallback\n * @param {function} [getTarget] - callback for getting the target for changed values\n * @returns Proxy\n * @private\n */\nexport function _createResolver(scopes: object[], prefixes?: string[], rootScopes?: object[], fallback?: string | boolean, getTarget?: Function): {\n    [Symbol.toStringTag]: string;\n    _cacheable: boolean;\n    _scopes: any[];\n    _rootScopes: any[];\n    _fallback: string | boolean;\n    _getTarget: Function;\n    override: (scope: any) => any;\n};\n/**\n * Returns an Proxy for resolving option values with context.\n * @param {object} proxy - The Proxy returned by `_createResolver`\n * @param {object} context - Context object for scriptable/indexable options\n * @param {object} [subProxy] - The proxy provided for scriptable options\n * @param {{scriptable: boolean, indexable: boolean, allKeys?: boolean}} [descriptorDefaults] - Defaults for descriptors\n * @private\n */\nexport function _attachContext(proxy: object, context: object, subProxy?: object, descriptorDefaults?: {\n    scriptable: boolean;\n    indexable: boolean;\n    allKeys?: boolean;\n}): {\n    _cacheable: boolean;\n    _proxy: any;\n    _context: any;\n    _subProxy: any;\n    _stack: Set<any>;\n    _descriptors: {\n        allKeys: any;\n        scriptable: any;\n        indexable: any;\n        isScriptable: (...args: any[]) => any;\n        isIndexable: (...args: any[]) => any;\n    };\n    setContext: (ctx: any) => any;\n    override: (scope: any) => any;\n};\n/**\n * @private\n */\nexport function _descriptors(proxy: any, defaults?: {\n    scriptable: boolean;\n    indexable: boolean;\n}): {\n    allKeys: any;\n    scriptable: any;\n    indexable: any;\n    isScriptable: (...args: any[]) => any;\n    isIndexable: (...args: any[]) => any;\n};\nexport function _parseObjectDataRadialScale(meta: any, data: any, start: any, count: any): any[];\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.core.d.ts",
    "content": "/**\n * @namespace Chart.helpers\n */\nimport type { AnyObject } from '../types/basic.js';\nimport type { ActiveDataPoint, ChartEvent } from '../types/index.js';\n/**\n * An empty function that can be used, for example, for optional callback.\n */\nexport declare function noop(): void;\n/**\n * Returns a unique id, sequentially generated from a global variable.\n */\nexport declare const uid: () => number;\n/**\n * Returns true if `value` is neither null nor undefined, else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport declare function isNullOrUndef(value: unknown): value is null | undefined;\n/**\n * Returns true if `value` is an array (including typed arrays), else returns false.\n * @param value - The value to test.\n * @function\n */\nexport declare function isArray<T = unknown>(value: unknown): value is T[];\n/**\n * Returns true if `value` is an object (excluding null), else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport declare function isObject(value: unknown): value is AnyObject;\n/**\n * Returns true if `value` is a finite number, else returns false\n * @param value  - The value to test.\n */\ndeclare function isNumberFinite(value: unknown): value is number;\nexport { isNumberFinite as isFinite, };\n/**\n * Returns `value` if finite, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is not finite.\n */\nexport declare function finiteOrDefault(value: unknown, defaultValue: number): number;\n/**\n * Returns `value` if defined, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is undefined.\n */\nexport declare function valueOrDefault<T>(value: T | undefined, defaultValue: T): T;\nexport declare const toPercentage: (value: number | string, dimension: number) => number;\nexport declare const toDimension: (value: number | string, dimension: number) => number;\n/**\n * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n * @param fn - The function to call.\n * @param args - The arguments with which `fn` should be called.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n */\nexport declare function callback<T extends (this: TA, ...restArgs: unknown[]) => R, TA, R>(fn: T | undefined, args: unknown[], thisArg?: TA): R | undefined;\n/**\n * Note(SB) for performance sake, this method should only be used when loopable type\n * is unknown or in none intensive code (not called often and small loopable). Else\n * it's preferable to use a regular for() loop and save extra function calls.\n * @param loopable - The object or array to be iterated.\n * @param fn - The function to call for each item.\n * @param [thisArg] - The value of `this` provided for the call to `fn`.\n * @param [reverse] - If true, iterates backward on the loopable.\n */\nexport declare function each<T, TA>(loopable: Record<string, T>, fn: (this: TA, v: T, i: string) => void, thisArg?: TA, reverse?: boolean): void;\nexport declare function each<T, TA>(loopable: T[], fn: (this: TA, v: T, i: number) => void, thisArg?: TA, reverse?: boolean): void;\n/**\n * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n * @param a0 - The array to compare\n * @param a1 - The array to compare\n * @private\n */\nexport declare function _elementsEqual(a0: ActiveDataPoint[], a1: ActiveDataPoint[]): boolean;\n/**\n * Returns a deep copy of `source` without keeping references on objects and arrays.\n * @param source - The value to clone.\n */\nexport declare function clone<T>(source: T): T;\n/**\n * The default merger when Chart.helpers.merge is called without merger option.\n * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.\n * @private\n */\nexport declare function _merger(key: string, target: AnyObject, source: AnyObject, options: AnyObject): void;\nexport interface MergeOptions {\n    merger?: (key: string, target: AnyObject, source: AnyObject, options?: AnyObject) => void;\n}\n/**\n * Recursively deep copies `source` properties into `target` with the given `options`.\n * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n * @param target - The target object in which all sources are merged into.\n * @param source - Object(s) to merge into `target`.\n * @param [options] - Merging options:\n * @param [options.merger] - The merge method (key, target, source, options)\n * @returns The `target` object.\n */\nexport declare function merge<T>(target: T, source: [], options?: MergeOptions): T;\nexport declare function merge<T, S1>(target: T, source: S1, options?: MergeOptions): T & S1;\nexport declare function merge<T, S1>(target: T, source: [S1], options?: MergeOptions): T & S1;\nexport declare function merge<T, S1, S2>(target: T, source: [S1, S2], options?: MergeOptions): T & S1 & S2;\nexport declare function merge<T, S1, S2, S3>(target: T, source: [S1, S2, S3], options?: MergeOptions): T & S1 & S2 & S3;\nexport declare function merge<T, S1, S2, S3, S4>(target: T, source: [S1, S2, S3, S4], options?: MergeOptions): T & S1 & S2 & S3 & S4;\nexport declare function merge<T>(target: T, source: AnyObject[], options?: MergeOptions): AnyObject;\n/**\n * Recursively deep copies `source` properties into `target` *only* if not defined in target.\n * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n * @param target - The target object in which all sources are merged into.\n * @param source - Object(s) to merge into `target`.\n * @returns The `target` object.\n */\nexport declare function mergeIf<T>(target: T, source: []): T;\nexport declare function mergeIf<T, S1>(target: T, source: S1): T & S1;\nexport declare function mergeIf<T, S1>(target: T, source: [S1]): T & S1;\nexport declare function mergeIf<T, S1, S2>(target: T, source: [S1, S2]): T & S1 & S2;\nexport declare function mergeIf<T, S1, S2, S3>(target: T, source: [S1, S2, S3]): T & S1 & S2 & S3;\nexport declare function mergeIf<T, S1, S2, S3, S4>(target: T, source: [S1, S2, S3, S4]): T & S1 & S2 & S3 & S4;\nexport declare function mergeIf<T>(target: T, source: AnyObject[]): AnyObject;\n/**\n * Merges source[key] in target[key] only if target[key] is undefined.\n * @private\n */\nexport declare function _mergerIf(key: string, target: AnyObject, source: AnyObject): void;\n/**\n * @private\n */\nexport declare function _deprecated(scope: string, value: unknown, previous: string, current: string): void;\n/**\n * @private\n */\nexport declare function _splitKey(key: string): string[];\nexport declare function resolveObjectKey(obj: AnyObject, key: string): AnyObject;\n/**\n * @private\n */\nexport declare function _capitalize(str: string): string;\nexport declare const defined: (value: unknown) => boolean;\nexport declare const isFunction: (value: unknown) => value is (...args: any[]) => any;\nexport declare const setsEqual: <T>(a: Set<T>, b: Set<T>) => boolean;\n/**\n * @param e - The event\n * @private\n */\nexport declare function _isClickEvent(e: ChartEvent): boolean;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.curve.d.ts",
    "content": "import type { ChartArea } from '../types/index.js';\nexport interface SplinePoint {\n    x: number;\n    y: number;\n    skip?: boolean;\n    cp1x?: number;\n    cp1y?: number;\n    cp2x?: number;\n    cp2y?: number;\n}\nexport declare function splineCurve(firstPoint: SplinePoint, middlePoint: SplinePoint, afterPoint: SplinePoint, t: number): {\n    previous: SplinePoint;\n    next: SplinePoint;\n};\n/**\n * This function calculates Bézier control points in a similar way than |splineCurve|,\n * but preserves monotonicity of the provided data and ensures no local extremums are added\n * between the dataset discrete points due to the interpolation.\n * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation\n */\nexport declare function splineCurveMonotone(points: SplinePoint[], indexAxis?: 'x' | 'y'): void;\n/**\n * @private\n */\nexport declare function _updateBezierControlPoints(points: SplinePoint[], options: any, area: ChartArea, loop: boolean, indexAxis: 'x' | 'y'): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.dom.d.ts",
    "content": "import type Chart from '../core/core.controller.js';\nimport type { ChartEvent } from '../types.js';\n/**\n * Note: typedefs are auto-exported, so use a made-up `dom` namespace where\n * necessary to avoid duplicates with `export * from './helpers`; see\n * https://github.com/microsoft/TypeScript/issues/46011\n * @typedef { import('../core/core.controller.js').default } dom.Chart\n * @typedef { import('../../types').ChartEvent } ChartEvent\n */\n/**\n * @private\n */\nexport declare function _isDomSupported(): boolean;\n/**\n * @private\n */\nexport declare function _getParentNode(domNode: HTMLCanvasElement): HTMLCanvasElement;\nexport declare function getStyle(el: HTMLElement, property: string): string;\n/**\n * Gets an event's x, y coordinates, relative to the chart area\n * @param event\n * @param chart\n * @returns x and y coordinates of the event\n */\nexport declare function getRelativePosition(event: Event | ChartEvent | TouchEvent | MouseEvent, chart: Chart): {\n    x: number;\n    y: number;\n};\nexport declare function getMaximumSize(canvas: HTMLCanvasElement, bbWidth?: number, bbHeight?: number, aspectRatio?: number): {\n    width: number;\n    height: number;\n};\n/**\n * @param chart\n * @param forceRatio\n * @param forceStyle\n * @returns True if the canvas context size or transformation has changed.\n */\nexport declare function retinaScale(chart: Chart, forceRatio: number, forceStyle?: boolean): boolean | void;\n/**\n * Detects support for options object argument in addEventListener.\n * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n * @private\n */\nexport declare const supportsEventListenerOptions: boolean;\n/**\n * The \"used\" size is the final value of a dimension property after all calculations have\n * been performed. This method uses the computed style of `element` but returns undefined\n * if the computed style is not expressed in pixels. That can happen in some cases where\n * `element` has a size relative to its parent and this last one is not yet displayed,\n * for example because of `display: none` on a parent node.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n * @returns Size in pixels or undefined if unknown.\n */\nexport declare function readUsedSize(element: HTMLElement, property: 'width' | 'height'): number | undefined;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.easing.d.ts",
    "content": "/**\n * Easing functions adapted from Robert Penner's easing equations.\n * @namespace Chart.helpers.easing.effects\n * @see http://www.robertpenner.com/easing/\n */\ndeclare const effects: {\n    readonly linear: (t: number) => number;\n    readonly easeInQuad: (t: number) => number;\n    readonly easeOutQuad: (t: number) => number;\n    readonly easeInOutQuad: (t: number) => number;\n    readonly easeInCubic: (t: number) => number;\n    readonly easeOutCubic: (t: number) => number;\n    readonly easeInOutCubic: (t: number) => number;\n    readonly easeInQuart: (t: number) => number;\n    readonly easeOutQuart: (t: number) => number;\n    readonly easeInOutQuart: (t: number) => number;\n    readonly easeInQuint: (t: number) => number;\n    readonly easeOutQuint: (t: number) => number;\n    readonly easeInOutQuint: (t: number) => number;\n    readonly easeInSine: (t: number) => number;\n    readonly easeOutSine: (t: number) => number;\n    readonly easeInOutSine: (t: number) => number;\n    readonly easeInExpo: (t: number) => number;\n    readonly easeOutExpo: (t: number) => number;\n    readonly easeInOutExpo: (t: number) => number;\n    readonly easeInCirc: (t: number) => number;\n    readonly easeOutCirc: (t: number) => number;\n    readonly easeInOutCirc: (t: number) => number;\n    readonly easeInElastic: (t: number) => number;\n    readonly easeOutElastic: (t: number) => number;\n    readonly easeInOutElastic: (t: number) => number;\n    readonly easeInBack: (t: number) => number;\n    readonly easeOutBack: (t: number) => number;\n    readonly easeInOutBack: (t: number) => number;\n    readonly easeInBounce: (t: number) => number;\n    readonly easeOutBounce: (t: number) => number;\n    readonly easeInOutBounce: (t: number) => number;\n};\nexport declare type EasingFunction = keyof typeof effects;\nexport default effects;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.extras.d.ts",
    "content": "import type { ChartMeta, PointElement } from '../types/index.js';\nexport declare function fontString(pixelSize: number, fontStyle: string, fontFamily: string): string;\n/**\n* Request animation polyfill\n*/\nexport declare const requestAnimFrame: (((callback: FrameRequestCallback) => number) & typeof requestAnimationFrame) | ((callback: any) => any);\n/**\n * Throttles calling `fn` once per animation frame\n * Latest arguments are used on the actual call\n */\nexport declare function throttled<TArgs extends Array<any>>(fn: (...args: TArgs) => void, thisArg: any): (...args: TArgs) => void;\n/**\n * Debounces calling `fn` for `delay` ms\n */\nexport declare function debounce<TArgs extends Array<any>>(fn: (...args: TArgs) => void, delay: number): (...args: TArgs) => number;\n/**\n * Converts 'start' to 'left', 'end' to 'right' and others to 'center'\n * @private\n */\nexport declare const _toLeftRightCenter: (align: 'start' | 'end' | 'center') => \"center\" | \"left\" | \"right\";\n/**\n * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`\n * @private\n */\nexport declare const _alignStartEnd: (align: 'start' | 'end' | 'center', start: number, end: number) => number;\n/**\n * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`\n * @private\n */\nexport declare const _textX: (align: 'left' | 'right' | 'center', left: number, right: number, rtl: boolean) => number;\n/**\n * Return start and count of visible points.\n * @private\n */\nexport declare function _getStartAndCountOfVisiblePoints(meta: ChartMeta<'line' | 'scatter'>, points: PointElement[], animationsDisabled: boolean): {\n    start: number;\n    count: number;\n};\n/**\n * Checks if the scale ranges have changed.\n * @param {object} meta - dataset meta.\n * @returns {boolean}\n * @private\n */\nexport declare function _scaleRangesChanged(meta: any): boolean;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.interpolation.d.ts",
    "content": "import type { Point } from '../types/geometric.js';\nimport type { SplinePoint } from './helpers.curve.js';\n/**\n * @private\n */\nexport declare function _pointInLine(p1: Point, p2: Point, t: number, mode?: any): {\n    x: number;\n    y: number;\n};\n/**\n * @private\n */\nexport declare function _steppedInterpolation(p1: Point, p2: Point, t: number, mode: 'middle' | 'after' | unknown): {\n    x: number;\n    y: number;\n};\n/**\n * @private\n */\nexport declare function _bezierInterpolation(p1: SplinePoint, p2: SplinePoint, t: number, mode?: any): {\n    x: number;\n    y: number;\n};\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.intl.d.ts",
    "content": "export declare function formatNumber(num: number, locale: string, options?: Intl.NumberFormatOptions): string;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.math.d.ts",
    "content": "import type { Point } from '../types/geometric.js';\n/**\n * @alias Chart.helpers.math\n * @namespace\n */\nexport declare const PI: number;\nexport declare const TAU: number;\nexport declare const PITAU: number;\nexport declare const INFINITY: number;\nexport declare const RAD_PER_DEG: number;\nexport declare const HALF_PI: number;\nexport declare const QUARTER_PI: number;\nexport declare const TWO_THIRDS_PI: number;\nexport declare const log10: (x: number) => number;\nexport declare const sign: (x: number) => number;\nexport declare function almostEquals(x: number, y: number, epsilon: number): boolean;\n/**\n * Implementation of the nice number algorithm used in determining where axis labels will go\n */\nexport declare function niceNum(range: number): number;\n/**\n * Returns an array of factors sorted from 1 to sqrt(value)\n * @private\n */\nexport declare function _factorize(value: number): number[];\nexport declare function isNumber(n: unknown): n is number;\nexport declare function almostWhole(x: number, epsilon: number): boolean;\n/**\n * @private\n */\nexport declare function _setMinAndMaxByKey(array: Record<string, number>[], target: {\n    min: number;\n    max: number;\n}, property: string): void;\nexport declare function toRadians(degrees: number): number;\nexport declare function toDegrees(radians: number): number;\n/**\n * Returns the number of decimal places\n * i.e. the number of digits after the decimal point, of the value of this Number.\n * @param x - A number.\n * @returns The number of decimal places.\n * @private\n */\nexport declare function _decimalPlaces(x: number): number;\nexport declare function getAngleFromPoint(centrePoint: Point, anglePoint: Point): {\n    angle: number;\n    distance: number;\n};\nexport declare function distanceBetweenPoints(pt1: Point, pt2: Point): number;\n/**\n * Shortest distance between angles, in either direction.\n * @private\n */\nexport declare function _angleDiff(a: number, b: number): number;\n/**\n * Normalize angle to be between 0 and 2*PI\n * @private\n */\nexport declare function _normalizeAngle(a: number): number;\n/**\n * @private\n */\nexport declare function _angleBetween(angle: number, start: number, end: number, sameAngleIsFullCircle?: boolean): boolean;\n/**\n * Limit `value` between `min` and `max`\n * @param value\n * @param min\n * @param max\n * @private\n */\nexport declare function _limitValue(value: number, min: number, max: number): number;\n/**\n * @param {number} value\n * @private\n */\nexport declare function _int16Range(value: number): number;\n/**\n * @param value\n * @param start\n * @param end\n * @param [epsilon]\n * @private\n */\nexport declare function _isBetween(value: number, start: number, end: number, epsilon?: number): boolean;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.options.d.ts",
    "content": "import { Point } from './helpers.canvas.js';\nimport type { ChartArea, FontSpec } from '../types/index.js';\nimport type { TRBL, TRBLCorners } from '../types/geometric.js';\n/**\n * @alias Chart.helpers.options\n * @namespace\n */\n/**\n * Converts the given line height `value` in pixels for a specific font `size`.\n * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n * @param size - The font size (in pixels) used to resolve relative `value`.\n * @returns The effective line height in pixels (size * 1.2 if value is invalid).\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n * @since 2.7.0\n */\nexport declare function toLineHeight(value: number | string, size: number): number;\n/**\n * @param value\n * @param props\n */\nexport declare function _readValueToProps<K extends string>(value: number | Record<K, number>, props: K[]): Record<K, number>;\nexport declare function _readValueToProps<K extends string, T extends string>(value: number | Record<K & T, number>, props: Record<T, K>): Record<T, number>;\n/**\n * Converts the given value into a TRBL object.\n * @param value - If a number, set the value to all TRBL component,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n *  x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left)\n * @since 3.0.0\n */\nexport declare function toTRBL(value: number | TRBL | Point): Record<\"left\" | \"top\" | \"bottom\" | \"right\", number>;\n/**\n * Converts the given value into a TRBL corners object (similar with css border-radius).\n * @param value - If a number, set the value to all TRBL corner components,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)\n * @since 3.0.0\n */\nexport declare function toTRBLCorners(value: number | TRBLCorners): Record<\"topLeft\" | \"topRight\" | \"bottomLeft\" | \"bottomRight\", number>;\n/**\n * Converts the given value into a padding object with pre-computed width/height.\n * @param value - If a number, set the value to all TRBL component,\n *  else, if an object, use defined properties and sets undefined ones to 0.\n *  x / y are shorthands for same value for left/right and top/bottom.\n * @returns The padding values (top, right, bottom, left, width, height)\n * @since 2.7.0\n */\nexport declare function toPadding(value?: number | TRBL): ChartArea;\nexport interface CanvasFontSpec extends FontSpec {\n    string: string;\n}\n/**\n * Parses font options and returns the font object.\n * @param options - A object that contains font options to be parsed.\n * @param fallback - A object that contains fallback font options.\n * @return The font object.\n * @private\n */\nexport declare function toFont(options: Partial<FontSpec>, fallback?: Partial<FontSpec>): {\n    family: string;\n    lineHeight: number;\n    size: number;\n    style: \"normal\" | \"inherit\" | \"italic\" | \"oblique\" | \"initial\";\n    weight: string;\n    string: string;\n};\n/**\n * Evaluates the given `inputs` sequentially and returns the first defined value.\n * @param inputs - An array of values, falling back to the last value.\n * @param context - If defined and the current value is a function, the value\n * is called with `context` as first argument and the result becomes the new input.\n * @param index - If defined and the current value is an array, the value\n * at `index` become the new input.\n * @param info - object to return information about resolution in\n * @param info.cacheable - Will be set to `false` if option is not cacheable.\n * @since 2.7.0\n */\nexport declare function resolve(inputs: Array<unknown>, context?: object, index?: number, info?: {\n    cacheable: boolean;\n}): unknown;\n/**\n * @param minmax\n * @param grace\n * @param beginAtZero\n * @private\n */\nexport declare function _addGrace(minmax: {\n    min: number;\n    max: number;\n}, grace: number | string, beginAtZero: boolean): {\n    min: number;\n    max: number;\n};\n/**\n * Create a context inheriting parentContext\n * @param parentContext\n * @param context\n * @returns\n */\nexport declare function createContext<T extends object>(parentContext: null, context: T): T;\nexport declare function createContext<T extends object, P extends T>(parentContext: P, context: T): P & T;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.rtl.d.ts",
    "content": "export interface RTLAdapter {\n    x(x: number): number;\n    setWidth(w: number): void;\n    textAlign(align: 'center' | 'left' | 'right'): 'center' | 'left' | 'right';\n    xPlus(x: number, value: number): number;\n    leftForLtr(x: number, itemWidth: number): number;\n}\nexport declare function getRtlAdapter(rtl: boolean, rectX: number, width: number): RTLAdapter;\nexport declare function overrideTextDirection(ctx: CanvasRenderingContext2D, direction: 'ltr' | 'rtl'): void;\nexport declare function restoreTextDirection(ctx: CanvasRenderingContext2D, original?: [string, string]): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/helpers.segment.d.ts",
    "content": "/**\n * Returns the sub-segment(s) of a line segment that fall in the given bounds\n * @param {object} segment\n * @param {number} segment.start - start index of the segment, referring the points array\n * @param {number} segment.end - end index of the segment, referring the points array\n * @param {boolean} segment.loop - indicates that the segment is a loop\n * @param {object} [segment.style] - segment style\n * @param {PointElement[]} points - the points that this segment refers to\n * @param {object} [bounds]\n * @param {string} bounds.property - the property of a `PointElement` we are bounding. `x`, `y` or `angle`.\n * @param {number} bounds.start - start value of the property\n * @param {number} bounds.end - end value of the property\n * @private\n **/\nexport function _boundSegment(segment: {\n    start: number;\n    end: number;\n    loop: boolean;\n    style?: object;\n}, points: PointElement[], bounds?: {\n    property: string;\n    start: number;\n    end: number;\n}): {\n    start: number;\n    end: number;\n    loop: boolean;\n    style?: object;\n}[];\n/**\n * Returns the segments of the line that are inside given bounds\n * @param {LineElement} line\n * @param {object} [bounds]\n * @param {string} bounds.property - the property we are bounding with. `x`, `y` or `angle`.\n * @param {number} bounds.start - start value of the `property`\n * @param {number} bounds.end - end value of the `property`\n * @private\n */\nexport function _boundSegments(line: LineElement, bounds?: {\n    property: string;\n    start: number;\n    end: number;\n}): {\n    start: number;\n    end: number;\n    loop: boolean;\n    style?: object;\n}[];\n/**\n * Compute the continuous segments that define the whole line\n * There can be skipped points within a segment, if spanGaps is true.\n * @param {LineElement} line\n * @param {object} [segmentOptions]\n * @return {Segment[]}\n * @private\n */\nexport function _computeSegments(line: LineElement, segmentOptions?: object): Segment[];\nexport type LineElement = import('../elements/element.line.js').default;\nexport type PointElement = import('../elements/element.point.js').default;\nexport type Segment = {\n    start: number;\n    end: number;\n    loop: boolean;\n    style?: any;\n};\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/index.d.ts",
    "content": "export * from './helpers.color.js';\nexport * from './helpers.core.js';\nexport * from './helpers.canvas.js';\nexport * from './helpers.collection.js';\nexport * from './helpers.config.js';\nexport * from './helpers.curve.js';\nexport * from './helpers.dom.js';\nexport { default as easingEffects } from './helpers.easing.js';\nexport * from './helpers.extras.js';\nexport * from './helpers.interpolation.js';\nexport * from './helpers.intl.js';\nexport * from './helpers.options.js';\nexport * from './helpers.math.js';\nexport * from './helpers.rtl.js';\nexport * from './helpers.segment.js';\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers/types.d.ts",
    "content": "/**\n * Temporary entry point of the types at the time of the transition.\n * After transition done need to remove it in favor of index.ts\n */\nexport * from './helpers.color.js';\nexport * from './helpers.collection.js';\nexport * from './helpers.core.js';\nexport * from './helpers.curve.js';\nexport * from './helpers.dom.js';\nexport * from './helpers.easing.js';\nexport * from './helpers.extras.js';\nexport * from './helpers.interpolation.js';\nexport * from './helpers.intl.js';\nexport * from './helpers.math.js';\nexport * from './helpers.options.js';\nexport * from './helpers.rtl.js';\nexport * from '../types/helpers/index.js';\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers.cjs",
    "content": "/*!\n * Chart.js v4.2.1\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\n'use strict';\n\nvar helpers_segment = require('./chunks/helpers.segment.cjs');\nrequire('@kurkle/color');\n\n\n\nexports.HALF_PI = helpers_segment.HALF_PI;\nexports.INFINITY = helpers_segment.INFINITY;\nexports.PI = helpers_segment.PI;\nexports.PITAU = helpers_segment.PITAU;\nexports.QUARTER_PI = helpers_segment.QUARTER_PI;\nexports.RAD_PER_DEG = helpers_segment.RAD_PER_DEG;\nexports.TAU = helpers_segment.TAU;\nexports.TWO_THIRDS_PI = helpers_segment.TWO_THIRDS_PI;\nexports._addGrace = helpers_segment._addGrace;\nexports._alignPixel = helpers_segment._alignPixel;\nexports._alignStartEnd = helpers_segment._alignStartEnd;\nexports._angleBetween = helpers_segment._angleBetween;\nexports._angleDiff = helpers_segment._angleDiff;\nexports._arrayUnique = helpers_segment._arrayUnique;\nexports._attachContext = helpers_segment._attachContext;\nexports._bezierCurveTo = helpers_segment._bezierCurveTo;\nexports._bezierInterpolation = helpers_segment._bezierInterpolation;\nexports._boundSegment = helpers_segment._boundSegment;\nexports._boundSegments = helpers_segment._boundSegments;\nexports._capitalize = helpers_segment._capitalize;\nexports._computeSegments = helpers_segment._computeSegments;\nexports._createResolver = helpers_segment._createResolver;\nexports._decimalPlaces = helpers_segment._decimalPlaces;\nexports._deprecated = helpers_segment._deprecated;\nexports._descriptors = helpers_segment._descriptors;\nexports._elementsEqual = helpers_segment._elementsEqual;\nexports._factorize = helpers_segment._factorize;\nexports._filterBetween = helpers_segment._filterBetween;\nexports._getParentNode = helpers_segment._getParentNode;\nexports._getStartAndCountOfVisiblePoints = helpers_segment._getStartAndCountOfVisiblePoints;\nexports._int16Range = helpers_segment._int16Range;\nexports._isBetween = helpers_segment._isBetween;\nexports._isClickEvent = helpers_segment._isClickEvent;\nexports._isDomSupported = helpers_segment._isDomSupported;\nexports._isPointInArea = helpers_segment._isPointInArea;\nexports._limitValue = helpers_segment._limitValue;\nexports._longestText = helpers_segment._longestText;\nexports._lookup = helpers_segment._lookup;\nexports._lookupByKey = helpers_segment._lookupByKey;\nexports._measureText = helpers_segment._measureText;\nexports._merger = helpers_segment._merger;\nexports._mergerIf = helpers_segment._mergerIf;\nexports._normalizeAngle = helpers_segment._normalizeAngle;\nexports._parseObjectDataRadialScale = helpers_segment._parseObjectDataRadialScale;\nexports._pointInLine = helpers_segment._pointInLine;\nexports._readValueToProps = helpers_segment._readValueToProps;\nexports._rlookupByKey = helpers_segment._rlookupByKey;\nexports._scaleRangesChanged = helpers_segment._scaleRangesChanged;\nexports._setMinAndMaxByKey = helpers_segment._setMinAndMaxByKey;\nexports._splitKey = helpers_segment._splitKey;\nexports._steppedInterpolation = helpers_segment._steppedInterpolation;\nexports._steppedLineTo = helpers_segment._steppedLineTo;\nexports._textX = helpers_segment._textX;\nexports._toLeftRightCenter = helpers_segment._toLeftRightCenter;\nexports._updateBezierControlPoints = helpers_segment._updateBezierControlPoints;\nexports.addRoundedRectPath = helpers_segment.addRoundedRectPath;\nexports.almostEquals = helpers_segment.almostEquals;\nexports.almostWhole = helpers_segment.almostWhole;\nexports.callback = helpers_segment.callback;\nexports.clearCanvas = helpers_segment.clearCanvas;\nexports.clipArea = helpers_segment.clipArea;\nexports.clone = helpers_segment.clone;\nexports.color = helpers_segment.color;\nexports.createContext = helpers_segment.createContext;\nexports.debounce = helpers_segment.debounce;\nexports.defined = helpers_segment.defined;\nexports.distanceBetweenPoints = helpers_segment.distanceBetweenPoints;\nexports.drawPoint = helpers_segment.drawPoint;\nexports.drawPointLegend = helpers_segment.drawPointLegend;\nexports.each = helpers_segment.each;\nexports.easingEffects = helpers_segment.effects;\nexports.finiteOrDefault = helpers_segment.finiteOrDefault;\nexports.fontString = helpers_segment.fontString;\nexports.formatNumber = helpers_segment.formatNumber;\nexports.getAngleFromPoint = helpers_segment.getAngleFromPoint;\nexports.getHoverColor = helpers_segment.getHoverColor;\nexports.getMaximumSize = helpers_segment.getMaximumSize;\nexports.getRelativePosition = helpers_segment.getRelativePosition;\nexports.getRtlAdapter = helpers_segment.getRtlAdapter;\nexports.getStyle = helpers_segment.getStyle;\nexports.isArray = helpers_segment.isArray;\nexports.isFinite = helpers_segment.isNumberFinite;\nexports.isFunction = helpers_segment.isFunction;\nexports.isNullOrUndef = helpers_segment.isNullOrUndef;\nexports.isNumber = helpers_segment.isNumber;\nexports.isObject = helpers_segment.isObject;\nexports.isPatternOrGradient = helpers_segment.isPatternOrGradient;\nexports.listenArrayEvents = helpers_segment.listenArrayEvents;\nexports.log10 = helpers_segment.log10;\nexports.merge = helpers_segment.merge;\nexports.mergeIf = helpers_segment.mergeIf;\nexports.niceNum = helpers_segment.niceNum;\nexports.noop = helpers_segment.noop;\nexports.overrideTextDirection = helpers_segment.overrideTextDirection;\nexports.readUsedSize = helpers_segment.readUsedSize;\nexports.renderText = helpers_segment.renderText;\nexports.requestAnimFrame = helpers_segment.requestAnimFrame;\nexports.resolve = helpers_segment.resolve;\nexports.resolveObjectKey = helpers_segment.resolveObjectKey;\nexports.restoreTextDirection = helpers_segment.restoreTextDirection;\nexports.retinaScale = helpers_segment.retinaScale;\nexports.setsEqual = helpers_segment.setsEqual;\nexports.sign = helpers_segment.sign;\nexports.splineCurve = helpers_segment.splineCurve;\nexports.splineCurveMonotone = helpers_segment.splineCurveMonotone;\nexports.supportsEventListenerOptions = helpers_segment.supportsEventListenerOptions;\nexports.throttled = helpers_segment.throttled;\nexports.toDegrees = helpers_segment.toDegrees;\nexports.toDimension = helpers_segment.toDimension;\nexports.toFont = helpers_segment.toFont;\nexports.toFontString = helpers_segment.toFontString;\nexports.toLineHeight = helpers_segment.toLineHeight;\nexports.toPadding = helpers_segment.toPadding;\nexports.toPercentage = helpers_segment.toPercentage;\nexports.toRadians = helpers_segment.toRadians;\nexports.toTRBL = helpers_segment.toTRBL;\nexports.toTRBLCorners = helpers_segment.toTRBLCorners;\nexports.uid = helpers_segment.uid;\nexports.unclipArea = helpers_segment.unclipArea;\nexports.unlistenArrayEvents = helpers_segment.unlistenArrayEvents;\nexports.valueOrDefault = helpers_segment.valueOrDefault;\n//# sourceMappingURL=helpers.cjs.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/helpers.js",
    "content": "/*!\n * Chart.js v4.2.1\n * https://www.chartjs.org\n * (c) 2023 Chart.js Contributors\n * Released under the MIT License\n */\nexport { H as HALF_PI, b2 as INFINITY, P as PI, b1 as PITAU, b4 as QUARTER_PI, b3 as RAD_PER_DEG, T as TAU, b5 as TWO_THIRDS_PI, R as _addGrace, X as _alignPixel, a2 as _alignStartEnd, p as _angleBetween, b6 as _angleDiff, _ as _arrayUnique, a8 as _attachContext, as as _bezierCurveTo, ap as _bezierInterpolation, ax as _boundSegment, an as _boundSegments, a5 as _capitalize, am as _computeSegments, a9 as _createResolver, aK as _decimalPlaces, aV as _deprecated, aa as _descriptors, ah as _elementsEqual, N as _factorize, aO as _filterBetween, I as _getParentNode, q as _getStartAndCountOfVisiblePoints, W as _int16Range, aj as _isBetween, ai as _isClickEvent, M as _isDomSupported, C as _isPointInArea, S as _limitValue, aN as _longestText, aP as _lookup, B as _lookupByKey, V as _measureText, aT as _merger, aU as _mergerIf, ay as _normalizeAngle, y as _parseObjectDataRadialScale, aq as _pointInLine, ak as _readValueToProps, A as _rlookupByKey, w as _scaleRangesChanged, aG as _setMinAndMaxByKey, aW as _splitKey, ao as _steppedInterpolation, ar as _steppedLineTo, aB as _textX, a1 as _toLeftRightCenter, al as _updateBezierControlPoints, au as addRoundedRectPath, aJ as almostEquals, aI as almostWhole, Q as callback, af as clearCanvas, Y as clipArea, aS as clone, c as color, j as createContext, ad as debounce, h as defined, aE as distanceBetweenPoints, at as drawPoint, aD as drawPointLegend, F as each, e as easingEffects, O as finiteOrDefault, a$ as fontString, o as formatNumber, D as getAngleFromPoint, aR as getHoverColor, G as getMaximumSize, z as getRelativePosition, az as getRtlAdapter, a_ as getStyle, b as isArray, g as isFinite, a7 as isFunction, k as isNullOrUndef, x as isNumber, i as isObject, aQ as isPatternOrGradient, l as listenArrayEvents, aM as log10, a4 as merge, ab as mergeIf, aH as niceNum, aF as noop, aA as overrideTextDirection, J as readUsedSize, Z as renderText, r as requestAnimFrame, a as resolve, f as resolveObjectKey, aC as restoreTextDirection, ae as retinaScale, ag as setsEqual, s as sign, aY as splineCurve, aZ as splineCurveMonotone, K as supportsEventListenerOptions, L as throttled, U as toDegrees, n as toDimension, a0 as toFont, aX as toFontString, b0 as toLineHeight, E as toPadding, m as toPercentage, t as toRadians, av as toTRBL, aw as toTRBLCorners, ac as uid, $ as unclipArea, u as unlistenArrayEvents, v as valueOrDefault } from './chunks/helpers.segment.js';\nimport '@kurkle/color';\n//# sourceMappingURL=helpers.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/index.d.ts",
    "content": "export * from './controllers/index.js';\nexport * from './core/index.js';\nexport * from './elements/index.js';\nexport * from './platform/index.js';\nexport * from './plugins/index.js';\nexport * from './scales/index.js';\nimport * as controllers from './controllers/index.js';\nimport * as elements from './elements/index.js';\nimport * as plugins from './plugins/index.js';\nimport * as scales from './scales/index.js';\nexport { controllers, elements, plugins, scales, };\nexport declare const registerables: (typeof controllers | typeof elements | typeof plugins | typeof scales)[];\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/index.umd.d.ts",
    "content": "/**\n * @namespace Chart\n */\nimport Chart from './core/core.controller.js';\nexport default Chart;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/platform/index.d.ts",
    "content": "export function _detectPlatform(canvas: any): typeof BasicPlatform | typeof DomPlatform;\nimport BasicPlatform from \"./platform.basic.js\";\nimport DomPlatform from \"./platform.dom.js\";\nimport BasePlatform from \"./platform.base.js\";\nexport { BasePlatform, BasicPlatform, DomPlatform };\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/platform/platform.base.d.ts",
    "content": "/**\n * @typedef { import('../core/core.controller.js').default } Chart\n */\n/**\n * Abstract class that allows abstracting platform dependencies away from the chart.\n */\nexport default class BasePlatform {\n    /**\n       * Called at chart construction time, returns a context2d instance implementing\n       * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.\n       * @param {HTMLCanvasElement} canvas - The canvas from which to acquire context (platform specific)\n       * @param {number} [aspectRatio] - The chart options\n       */\n    acquireContext(canvas: HTMLCanvasElement, aspectRatio?: number): void;\n    /**\n       * Called at chart destruction time, releases any resources associated to the context\n       * previously returned by the acquireContext() method.\n       * @param {CanvasRenderingContext2D} context - The context2d instance\n       * @returns {boolean} true if the method succeeded, else false\n       */\n    releaseContext(context: CanvasRenderingContext2D): boolean;\n    /**\n       * Registers the specified listener on the given chart.\n       * @param {Chart} chart - Chart from which to listen for event\n       * @param {string} type - The ({@link ChartEvent}) type to listen for\n       * @param {function} listener - Receives a notification (an object that implements\n       * the {@link ChartEvent} interface) when an event of the specified type occurs.\n       */\n    addEventListener(chart: Chart, type: string, listener: Function): void;\n    /**\n       * Removes the specified listener previously registered with addEventListener.\n       * @param {Chart} chart - Chart from which to remove the listener\n       * @param {string} type - The ({@link ChartEvent}) type to remove\n       * @param {function} listener - The listener function to remove from the event target.\n       */\n    removeEventListener(chart: Chart, type: string, listener: Function): void;\n    /**\n       * @returns {number} the current devicePixelRatio of the device this platform is connected to.\n       */\n    getDevicePixelRatio(): number;\n    /**\n       * Returns the maximum size in pixels of given canvas element.\n       * @param {HTMLCanvasElement} element\n       * @param {number} [width] - content width of parent element\n       * @param {number} [height] - content height of parent element\n       * @param {number} [aspectRatio] - aspect ratio to maintain\n       */\n    getMaximumSize(element: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): {\n        width: number;\n        height: number;\n    };\n    /**\n       * @param {HTMLCanvasElement} canvas\n       * @returns {boolean} true if the canvas is attached to the platform, false if not.\n       */\n    isAttached(canvas: HTMLCanvasElement): boolean;\n    /**\n     * Updates config with platform specific requirements\n     * @param {import('../core/core.config.js').default} config\n     */\n    updateConfig(config: import('../core/core.config.js').default): void;\n}\nexport type Chart = import('../core/core.controller.js').default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/platform/platform.basic.d.ts",
    "content": "/**\n * Platform class for charts without access to the DOM or to many element properties\n * This platform is used by default for any chart passed an OffscreenCanvas.\n * @extends BasePlatform\n */\nexport default class BasicPlatform extends BasePlatform {\n    acquireContext(item: any): any;\n    updateConfig(config: any): void;\n}\nimport BasePlatform from \"./platform.base.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/platform/platform.dom.d.ts",
    "content": "/**\n * Platform class for charts that can access the DOM and global window/document properties\n * @extends BasePlatform\n */\nexport default class DomPlatform extends BasePlatform {\n    /**\n       * @param {HTMLCanvasElement} canvas\n       * @param {number} [aspectRatio]\n       * @return {CanvasRenderingContext2D|null}\n       */\n    acquireContext(canvas: HTMLCanvasElement, aspectRatio?: number): CanvasRenderingContext2D | null;\n    /**\n       * @param {Chart} chart\n       * @param {string} type\n       */\n    removeEventListener(chart: Chart, type: string): void;\n}\nexport type Chart = import('../core/core.controller.js').default;\nimport BasePlatform from \"./platform.base.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/index.d.ts",
    "content": "export { default as Colors } from \"./plugin.colors.js\";\nexport { default as Decimation } from \"./plugin.decimation.js\";\nexport { default as Filler } from \"./plugin.filler/index.js\";\nexport { default as Legend } from \"./plugin.legend.js\";\nexport { default as SubTitle } from \"./plugin.subtitle.js\";\nexport { default as Title } from \"./plugin.title.js\";\nexport { default as Tooltip } from \"./plugin.tooltip.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.colors.d.ts",
    "content": "import type { Chart } from '../types.js';\nexport interface ColorsPluginOptions {\n    enabled?: boolean;\n    forceOverride?: boolean;\n}\ndeclare const _default: {\n    id: string;\n    defaults: ColorsPluginOptions;\n    beforeLayout(chart: Chart, _args: any, options: ColorsPluginOptions): void;\n};\nexport default _default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.decimation.d.ts",
    "content": "declare namespace _default {\n    const id: string;\n    namespace defaults {\n        const algorithm: string;\n        const enabled: boolean;\n    }\n    function beforeElementsUpdate(chart: any, args: any, options: any): void;\n    function destroy(chart: any): void;\n    function destroy(chart: any): void;\n}\nexport default _default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/filler.drawing.d.ts",
    "content": "export function _drawfill(ctx: any, source: any, area: any): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/filler.helper.d.ts",
    "content": "/**\n * @param {PointElement[] | { x: number; y: number; }} boundary\n * @param {LineElement} line\n * @return {LineElement?}\n */\nexport function _createBoundaryLine(boundary: PointElement[] | {\n    x: number;\n    y: number;\n}, line: LineElement): LineElement | null;\nexport function _shouldApplyFill(source: any): boolean;\nexport type Chart = import('../../core/core.controller.js').default;\nexport type Scale = import('../../core/core.scale.js').default;\nexport type PointElement = import('../../elements/element.point.js').default;\nimport { LineElement } from \"../../elements/index.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/filler.options.d.ts",
    "content": "/**\n * @typedef { import('../../core/core.scale.js').default } Scale\n * @typedef { import('../../elements/element.line.js').default } LineElement\n * @typedef { import('../../types/index.js').FillTarget } FillTarget\n * @typedef { import('../../types/index.js').ComplexFillTarget } ComplexFillTarget\n */\nexport function _resolveTarget(sources: any, index: any, propagate: any): any;\n/**\n * @param {LineElement} line\n * @param {number} index\n * @param {number} count\n */\nexport function _decodeFill(line: LineElement, index: number, count: number): any;\n/**\n * @param {FillTarget | ComplexFillTarget} fill\n * @param {Scale} scale\n * @returns {number | null}\n */\nexport function _getTargetPixel(fill: FillTarget | ComplexFillTarget, scale: Scale): number | null;\n/**\n * @param {FillTarget | ComplexFillTarget} fill\n * @param {Scale} scale\n * @param {number} startValue\n * @returns {number | undefined}\n */\nexport function _getTargetValue(fill: FillTarget | ComplexFillTarget, scale: Scale, startValue: number): number | undefined;\nexport type Scale = import('../../core/core.scale.js').default;\nexport type LineElement = import('../../elements/element.line.js').default;\nexport type FillTarget = import('../../types/index.js').FillTarget;\nexport type ComplexFillTarget = import('../../types/index.js').ComplexFillTarget;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/filler.segment.d.ts",
    "content": "export function _segments(line: any, target: any, property: any): ({\n    source: any;\n    target: {\n        property: any;\n        start: any;\n        end: any;\n    };\n    start: any;\n    end: any;\n} | {\n    source: {\n        start: number;\n        end: number;\n        loop: boolean;\n        style?: any;\n    };\n    target: {\n        start: number;\n        end: number;\n        loop: boolean;\n        style?: any;\n    };\n    start: {\n        [x: number]: any;\n    };\n    end: {\n        [x: number]: any;\n    };\n})[];\nexport function _getBounds(property: any, first: any, last: any, loop: any): {\n    property: any;\n    start: any;\n    end: any;\n};\nexport function _pointsFromSegments(boundary: any, line: any): any[];\nexport function _findSegmentEnd(start: any, end: any, points: any): any;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/filler.target.d.ts",
    "content": "/**\n * @typedef { import('../../core/core.controller.js').default } Chart\n * @typedef { import('../../core/core.scale.js').default } Scale\n * @typedef { import('../../elements/element.point.js').default } PointElement\n */\nexport function _getTarget(source: any): any;\nexport type Chart = import('../../core/core.controller.js').default;\nexport type Scale = import('../../core/core.scale.js').default;\nexport type PointElement = import('../../elements/element.point.js').default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/filler.target.stack.d.ts",
    "content": "/**\n * @param {{ chart: Chart; scale: Scale; index: number; line: LineElement; }} source\n * @return {LineElement}\n */\nexport function _buildStackLine(source: {\n    chart: Chart;\n    scale: Scale;\n    index: number;\n    line: LineElement;\n}): LineElement;\nexport type Chart = import('../../core/core.controller.js').default;\nexport type Scale = import('../../core/core.scale.js').default;\nexport type PointElement = import('../../elements/element.point.js').default;\nimport { LineElement } from \"../../elements/index.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/index.d.ts",
    "content": "declare namespace _default {\n    const id: string;\n    function afterDatasetsUpdate(chart: any, _args: any, options: any): void;\n    function afterDatasetsUpdate(chart: any, _args: any, options: any): void;\n    function beforeDraw(chart: any, _args: any, options: any): void;\n    function beforeDraw(chart: any, _args: any, options: any): void;\n    function beforeDatasetsDraw(chart: any, _args: any, options: any): void;\n    function beforeDatasetsDraw(chart: any, _args: any, options: any): void;\n    function beforeDatasetDraw(chart: any, args: any, options: any): void;\n    function beforeDatasetDraw(chart: any, args: any, options: any): void;\n    namespace defaults {\n        const propagate: boolean;\n        const drawTime: string;\n    }\n}\nexport default _default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.filler/simpleArc.d.ts",
    "content": "export class simpleArc {\n    constructor(opts: any);\n    x: any;\n    y: any;\n    radius: any;\n    pathSegment(ctx: any, bounds: any, opts: any): boolean;\n    interpolate(point: any): {\n        x: any;\n        y: any;\n        angle: any;\n    };\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.legend.d.ts",
    "content": "export class Legend extends Element<import(\"../types/basic.js\").AnyObject, import(\"../types/basic.js\").AnyObject> {\n    /**\n       * @param {{ ctx: any; options: any; chart: any; }} config\n       */\n    constructor(config: {\n        ctx: any;\n        options: any;\n        chart: any;\n    });\n    _added: boolean;\n    legendHitBoxes: any[];\n    /**\n         * @private\n         */\n    private _hoveredItem;\n    doughnutMode: boolean;\n    chart: any;\n    options: any;\n    ctx: any;\n    legendItems: any;\n    columnSizes: any[];\n    lineWidths: number[];\n    maxHeight: any;\n    maxWidth: any;\n    top: any;\n    bottom: any;\n    left: any;\n    right: any;\n    height: any;\n    width: any;\n    _margins: any;\n    position: any;\n    weight: any;\n    fullSize: any;\n    update(maxWidth: any, maxHeight: any, margins: any): void;\n    setDimensions(): void;\n    buildLabels(): void;\n    fit(): void;\n    /**\n       * @private\n       */\n    private _fitRows;\n    _fitCols(titleHeight: any, labelFont: any, boxWidth: any, _itemHeight: any): any;\n    adjustHitBoxes(): void;\n    isHorizontal(): boolean;\n    draw(): void;\n    /**\n       * @private\n       */\n    private _draw;\n    /**\n       * @protected\n       */\n    protected drawTitle(): void;\n    /**\n       * @private\n       */\n    private _computeTitleHeight;\n    /**\n       * @private\n       */\n    private _getLegendItemAt;\n    /**\n       * Handle an event\n       * @param {ChartEvent} e - The event to handle\n       */\n    handleEvent(e: ChartEvent): void;\n}\ndeclare namespace _default {\n    export const id: string;\n    export { Legend as _element };\n    export function start(chart: any, _args: any, options: any): void;\n    export function start(chart: any, _args: any, options: any): void;\n    export function stop(chart: any): void;\n    export function stop(chart: any): void;\n    export function beforeUpdate(chart: any, _args: any, options: any): void;\n    export function beforeUpdate(chart: any, _args: any, options: any): void;\n    export function afterUpdate(chart: any): void;\n    export function afterUpdate(chart: any): void;\n    export function afterEvent(chart: any, args: any): void;\n    export function afterEvent(chart: any, args: any): void;\n    export namespace defaults {\n        const display: boolean;\n        const position: string;\n        const align: string;\n        const fullSize: boolean;\n        const reverse: boolean;\n        const weight: number;\n        function onClick(e: any, legendItem: any, legend: any): void;\n        function onClick(e: any, legendItem: any, legend: any): void;\n        const onHover: any;\n        const onLeave: any;\n        namespace labels {\n            function color(ctx: any): any;\n            const boxWidth: number;\n            const padding: number;\n            function generateLabels(chart: any): any;\n            function generateLabels(chart: any): any;\n        }\n        namespace title {\n            export function color_1(ctx: any): any;\n            export { color_1 as color };\n            const display_1: boolean;\n            export { display_1 as display };\n            const position_1: string;\n            export { position_1 as position };\n            export const text: string;\n        }\n    }\n    export namespace descriptors {\n        export function _scriptable(name: any): boolean;\n        export namespace labels_1 {\n            export function _scriptable_1(name: any): boolean;\n            export { _scriptable_1 as _scriptable };\n        }\n        export { labels_1 as labels };\n    }\n}\nexport default _default;\nexport type ChartEvent = import('../types/index.js').ChartEvent;\nimport Element from \"../core/core.element.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.subtitle.d.ts",
    "content": "declare namespace _default {\n    const id: string;\n    function start(chart: any, _args: any, options: any): void;\n    function start(chart: any, _args: any, options: any): void;\n    function stop(chart: any): void;\n    function stop(chart: any): void;\n    function beforeUpdate(chart: any, _args: any, options: any): void;\n    function beforeUpdate(chart: any, _args: any, options: any): void;\n    namespace defaults {\n        export const align: string;\n        export const display: boolean;\n        export namespace font {\n            const weight: string;\n        }\n        export const fullSize: boolean;\n        export const padding: number;\n        export const position: string;\n        export const text: string;\n        const weight_1: number;\n        export { weight_1 as weight };\n    }\n    namespace defaultRoutes {\n        const color: string;\n    }\n    namespace descriptors {\n        const _scriptable: boolean;\n        const _indexable: boolean;\n    }\n}\nexport default _default;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.title.d.ts",
    "content": "export class Title extends Element<import(\"../types/basic.js\").AnyObject, import(\"../types/basic.js\").AnyObject> {\n    /**\n       * @param {{ ctx: any; options: any; chart: any; }} config\n       */\n    constructor(config: {\n        ctx: any;\n        options: any;\n        chart: any;\n    });\n    chart: any;\n    options: any;\n    ctx: any;\n    _padding: import(\"../types.js\").ChartArea;\n    top: number;\n    bottom: any;\n    left: number;\n    right: any;\n    width: any;\n    height: any;\n    position: any;\n    weight: any;\n    fullSize: any;\n    update(maxWidth: any, maxHeight: any): void;\n    isHorizontal(): boolean;\n    _drawArgs(offset: any): {\n        titleX: any;\n        titleY: any;\n        maxWidth: number;\n        rotation: number;\n    };\n    draw(): void;\n}\ndeclare namespace _default {\n    export const id: string;\n    export { Title as _element };\n    export function start(chart: any, _args: any, options: any): void;\n    export function start(chart: any, _args: any, options: any): void;\n    export function stop(chart: any): void;\n    export function stop(chart: any): void;\n    export function beforeUpdate(chart: any, _args: any, options: any): void;\n    export function beforeUpdate(chart: any, _args: any, options: any): void;\n    export namespace defaults {\n        export const align: string;\n        export const display: boolean;\n        export namespace font {\n            const weight: string;\n        }\n        export const fullSize: boolean;\n        export const padding: number;\n        export const position: string;\n        export const text: string;\n        const weight_1: number;\n        export { weight_1 as weight };\n    }\n    export namespace defaultRoutes {\n        const color: string;\n    }\n    export namespace descriptors {\n        const _scriptable: boolean;\n        const _indexable: boolean;\n    }\n}\nexport default _default;\nimport Element from \"../core/core.element.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/plugins/plugin.tooltip.d.ts",
    "content": "export class Tooltip extends Element<import(\"../types/basic.js\").AnyObject, import(\"../types/basic.js\").AnyObject> {\n    /**\n     * @namespace Chart.Tooltip.positioners\n     */\n    static positioners: {\n        /**\n           * Average mode places the tooltip at the average position of the elements shown\n           */\n        average(items: any): false | {\n            x: number;\n            y: number;\n        };\n        /**\n           * Gets the tooltip position nearest of the item nearest to the event position\n           */\n        nearest(items: any, eventPosition: any): false | {\n            x: any;\n            y: any;\n        };\n    };\n    constructor(config: any);\n    opacity: number;\n    _active: any[];\n    _eventPosition: any;\n    _size: {\n        width: number;\n        height: number;\n    };\n    _cachedAnimations: Readonly<Animations>;\n    _tooltipItems: any[];\n    $animations: any;\n    $context: any;\n    chart: any;\n    options: any;\n    dataPoints: {\n        chart: import(\"../core/core.controller.js\").default;\n        label: any;\n        parsed: any;\n        raw: any;\n        formattedValue: any;\n        dataset: any;\n        dataIndex: number;\n        datasetIndex: number;\n        element: Element<import(\"../types/basic.js\").AnyObject, import(\"../types/basic.js\").AnyObject>;\n    }[];\n    title: any;\n    beforeBody: any;\n    body: any[];\n    afterBody: any;\n    footer: any;\n    xAlign: any;\n    yAlign: any;\n    x: any;\n    y: any;\n    height: number;\n    width: number;\n    caretX: any;\n    caretY: any;\n    labelColors: any[];\n    labelPointStyles: any[];\n    labelTextColors: any[];\n    initialize(options: any): void;\n    /**\n       * @private\n       */\n    private _resolveAnimations;\n    /**\n       * @protected\n       */\n    protected getContext(): any;\n    getTitle(context: any, options: any): any;\n    getBeforeBody(tooltipItems: any, options: any): any;\n    getBody(tooltipItems: any, options: any): any[];\n    getAfterBody(tooltipItems: any, options: any): any;\n    getFooter(tooltipItems: any, options: any): any;\n    /**\n       * @private\n       */\n    private _createItems;\n    update(changed: any, replay: any): void;\n    drawCaret(tooltipPoint: any, ctx: any, size: any, options: any): void;\n    getCaretPosition(tooltipPoint: any, size: any, options: any): {\n        x1: any;\n        x2: any;\n        x3: any;\n        y1: any;\n        y2: any;\n        y3: any;\n    };\n    drawTitle(pt: any, ctx: any, options: any): void;\n    /**\n       * @private\n       */\n    private _drawColorBox;\n    drawBody(pt: any, ctx: any, options: any): void;\n    drawFooter(pt: any, ctx: any, options: any): void;\n    drawBackground(pt: any, ctx: any, tooltipSize: any, options: any): void;\n    /**\n       * Update x/y animation targets when _active elements are animating too\n       * @private\n       */\n    private _updateAnimationTarget;\n    /**\n     * Determine if the tooltip will draw anything\n     * @returns {boolean} True if the tooltip will render\n     */\n    _willRender(): boolean;\n    draw(ctx: any): void;\n    /**\n       * Get active elements in the tooltip\n       * @returns {Array} Array of elements that are active in the tooltip\n       */\n    getActiveElements(): any[];\n    /**\n       * Set active elements in the tooltip\n       * @param {array} activeElements Array of active datasetIndex/index pairs.\n       * @param {object} eventPosition Synthetic event position used in positioning\n       */\n    setActiveElements(activeElements: any[], eventPosition: object): void;\n    _ignoreReplayEvents: boolean;\n    /**\n       * Handle an event\n       * @param {ChartEvent} e - The event to handle\n       * @param {boolean} [replay] - This is a replayed event (from update)\n       * @param {boolean} [inChartArea] - The event is inside chartArea\n       * @returns {boolean} true if the tooltip changed\n       */\n    handleEvent(e: ChartEvent, replay?: boolean, inChartArea?: boolean): boolean;\n    /**\n       * Helper for determining the active elements for event\n       * @param {ChartEvent} e - The event to handle\n       * @param {InteractionItem[]} lastActive - Previously active elements\n       * @param {boolean} [replay] - This is a replayed event (from update)\n       * @param {boolean} [inChartArea] - The event is inside chartArea\n       * @returns {InteractionItem[]} - Active elements\n       * @private\n       */\n    private _getActiveElements;\n    /**\n       * Determine if the active elements + event combination changes the\n       * tooltip position\n       * @param {array} active - Active elements\n       * @param {ChartEvent} e - Event that triggered the position change\n       * @returns {boolean} True if the position has changed\n       */\n    _positionChanged(active: any[], e: ChartEvent): boolean;\n}\ndeclare namespace _default {\n    export const id: string;\n    export { Tooltip as _element };\n    export { positioners };\n    export function afterInit(chart: any, _args: any, options: any): void;\n    export function afterInit(chart: any, _args: any, options: any): void;\n    export function beforeUpdate(chart: any, _args: any, options: any): void;\n    export function beforeUpdate(chart: any, _args: any, options: any): void;\n    export function reset(chart: any, _args: any, options: any): void;\n    export function reset(chart: any, _args: any, options: any): void;\n    export function afterDraw(chart: any): void;\n    export function afterDraw(chart: any): void;\n    export function afterEvent(chart: any, args: any): void;\n    export function afterEvent(chart: any, args: any): void;\n    export namespace defaults {\n        export const enabled: boolean;\n        export const external: any;\n        export const position: string;\n        export const backgroundColor: string;\n        export const titleColor: string;\n        export namespace titleFont {\n            const weight: string;\n        }\n        export const titleSpacing: number;\n        export const titleMarginBottom: number;\n        export const titleAlign: string;\n        export const bodyColor: string;\n        export const bodySpacing: number;\n        export const bodyFont: {};\n        export const bodyAlign: string;\n        export const footerColor: string;\n        export const footerSpacing: number;\n        export const footerMarginTop: number;\n        export namespace footerFont {\n            const weight_1: string;\n            export { weight_1 as weight };\n        }\n        export const footerAlign: string;\n        export const padding: number;\n        export const caretPadding: number;\n        export const caretSize: number;\n        export const cornerRadius: number;\n        export function boxHeight(ctx: any, opts: any): any;\n        export function boxWidth(ctx: any, opts: any): any;\n        export const multiKeyBackground: string;\n        export const displayColors: boolean;\n        export const boxPadding: number;\n        export const borderColor: string;\n        export const borderWidth: number;\n        export namespace animation {\n            const duration: number;\n            const easing: string;\n        }\n        export namespace animations {\n            namespace numbers {\n                const type: string;\n                const properties: string[];\n            }\n            namespace opacity {\n                const easing_1: string;\n                export { easing_1 as easing };\n                const duration_1: number;\n                export { duration_1 as duration };\n            }\n        }\n        export { defaultCallbacks as callbacks };\n    }\n    export namespace defaultRoutes {\n        const bodyFont_1: string;\n        export { bodyFont_1 as bodyFont };\n        const footerFont_1: string;\n        export { footerFont_1 as footerFont };\n        const titleFont_1: string;\n        export { titleFont_1 as titleFont };\n    }\n    export namespace descriptors {\n        export function _scriptable(name: any): boolean;\n        export const _indexable: boolean;\n        export namespace callbacks {\n            const _scriptable_1: boolean;\n            export { _scriptable_1 as _scriptable };\n            const _indexable_1: boolean;\n            export { _indexable_1 as _indexable };\n        }\n        export namespace animation_1 {\n            const _fallback: boolean;\n        }\n        export { animation_1 as animation };\n        export namespace animations_1 {\n            const _fallback_1: string;\n            export { _fallback_1 as _fallback };\n        }\n        export { animations_1 as animations };\n    }\n    export const additionalOptionScopes: string[];\n}\nexport default _default;\nexport type Chart = import('../platform/platform.base.js').Chart;\nexport type ChartEvent = import('../types/index.js').ChartEvent;\nexport type ActiveElement = import('../types/index.js').ActiveElement;\nexport type InteractionItem = import('../core/core.interaction.js').InteractionItem;\nimport Element from \"../core/core.element.js\";\nimport Animations from \"../core/core.animations.js\";\ndeclare namespace positioners {\n    /**\n       * Average mode places the tooltip at the average position of the elements shown\n       */\n    function average(items: any): false | {\n        x: number;\n        y: number;\n    };\n    /**\n       * Average mode places the tooltip at the average position of the elements shown\n       */\n    function average(items: any): false | {\n        x: number;\n        y: number;\n    };\n    /**\n       * Gets the tooltip position nearest of the item nearest to the event position\n       */\n    function nearest(items: any, eventPosition: any): false | {\n        x: any;\n        y: any;\n    };\n    /**\n       * Gets the tooltip position nearest of the item nearest to the event position\n       */\n    function nearest(items: any, eventPosition: any): false | {\n        x: any;\n        y: any;\n    };\n}\ndeclare namespace defaultCallbacks {\n    export { noop as beforeTitle };\n    export function title(tooltipItems: any): any;\n    export function title(tooltipItems: any): any;\n    export { noop as afterTitle };\n    export { noop as beforeBody };\n    export { noop as beforeLabel };\n    export function label(tooltipItem: any): any;\n    export function label(tooltipItem: any): any;\n    export function labelColor(tooltipItem: any): {\n        borderColor: any;\n        backgroundColor: any;\n        borderWidth: any;\n        borderDash: any;\n        borderDashOffset: any;\n        borderRadius: number;\n    };\n    export function labelColor(tooltipItem: any): {\n        borderColor: any;\n        backgroundColor: any;\n        borderWidth: any;\n        borderDash: any;\n        borderDashOffset: any;\n        borderRadius: number;\n    };\n    export function labelTextColor(): any;\n    export function labelTextColor(): any;\n    export function labelPointStyle(tooltipItem: any): {\n        pointStyle: any;\n        rotation: any;\n    };\n    export function labelPointStyle(tooltipItem: any): {\n        pointStyle: any;\n        rotation: any;\n    };\n    export { noop as afterLabel };\n    export { noop as afterBody };\n    export { noop as beforeFooter };\n    export { noop as footer };\n    export { noop as afterFooter };\n}\nimport { noop } from \"../helpers/helpers.core.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/index.d.ts",
    "content": "export { default as CategoryScale } from \"./scale.category.js\";\nexport { default as LinearScale } from \"./scale.linear.js\";\nexport { default as LogarithmicScale } from \"./scale.logarithmic.js\";\nexport { default as RadialLinearScale } from \"./scale.radialLinear.js\";\nexport { default as TimeScale } from \"./scale.time.js\";\nexport { default as TimeSeriesScale } from \"./scale.timeseries.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/scale.category.d.ts",
    "content": "export default class CategoryScale extends Scale {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    /** @type {number} */\n    _startValue: number;\n    _valueRange: number;\n    _addedLabels: any[];\n    init(scaleOptions: any): void;\n    parse(raw: any, index: any): number;\n    buildTicks(): {\n        value: any;\n    }[];\n    getLabelForValue(value: any): any;\n    getPixelForValue(value: any): number;\n    getPixelForTick(index: any): number;\n    getValueForPixel(pixel: any): number;\n}\nimport Scale from \"../core/core.scale.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/scale.linear.d.ts",
    "content": "export default class LinearScale extends LinearScaleBase {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    getPixelForValue(value: any): number;\n    getValueForPixel(pixel: any): number;\n}\nimport LinearScaleBase from \"./scale.linearbase.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/scale.linearbase.d.ts",
    "content": "export default class LinearScaleBase extends Scale {\n    /** @type {number} */\n    start: number;\n    /** @type {number} */\n    end: number;\n    /** @type {number} */\n    _startValue: number;\n    /** @type {number} */\n    _endValue: number;\n    _valueRange: number;\n    parse(raw: any, index: any): number;\n    handleTickRangeOptions(): void;\n    getTickLimit(): number;\n    /**\n       * @protected\n       */\n    protected computeTickLimit(): number;\n    getLabelForValue(value: any): string;\n}\nimport Scale from \"../core/core.scale.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/scale.logarithmic.d.ts",
    "content": "export default class LogarithmicScale extends Scale {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    /** @type {number} */\n    start: number;\n    /** @type {number} */\n    end: number;\n    /** @type {number} */\n    _startValue: number;\n    _valueRange: number;\n    parse(raw: any, index: any): number;\n    _zero: boolean;\n    handleTickRangeOptions(): void;\n    /**\n       * @param {number} value\n       * @return {string}\n       */\n    getLabelForValue(value: number): string;\n    getPixelForValue(value: any): number;\n    getValueForPixel(pixel: any): number;\n}\nimport Scale from \"../core/core.scale.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/scale.radialLinear.d.ts",
    "content": "export default class RadialLinearScale extends LinearScaleBase {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    static defaultRoutes: {\n        'angleLines.color': string;\n        'pointLabels.color': string;\n        'ticks.color': string;\n    };\n    static descriptors: {\n        angleLines: {\n            _fallback: string;\n        };\n    };\n    /** @type {number} */\n    xCenter: number;\n    /** @type {number} */\n    yCenter: number;\n    /** @type {number} */\n    drawingArea: number;\n    /** @type {string[]} */\n    _pointLabels: string[];\n    _pointLabelItems: any[];\n    _padding: import(\"../types.js\").ChartArea;\n    generateTickLabels(ticks: any): void;\n    setCenterPoint(leftMovement: any, rightMovement: any, topMovement: any, bottomMovement: any): void;\n    getIndexAngle(index: any): number;\n    getDistanceFromCenterForValue(value: any): number;\n    getValueForDistanceFromCenter(distance: any): any;\n    getPointLabelContext(index: any): any;\n    getPointPosition(index: any, distanceFromCenter: any, additionalAngle?: number): {\n        x: number;\n        y: number;\n        angle: number;\n    };\n    getPointPositionForValue(index: any, value: any): {\n        x: number;\n        y: number;\n        angle: number;\n    };\n    getBasePosition(index: any): {\n        x: number;\n        y: number;\n        angle: number;\n    };\n    getPointLabelPosition(index: any): {\n        left: any;\n        top: any;\n        right: any;\n        bottom: any;\n    };\n    /**\n       * @protected\n       */\n    protected drawGrid(): void;\n    /**\n       * @protected\n       */\n    protected drawLabels(): void;\n}\nimport LinearScaleBase from \"./scale.linearbase.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/scale.time.d.ts",
    "content": "export default class TimeScale extends Scale {\n    static id: string;\n    /**\n     * @type {any}\n     */\n    static defaults: any;\n    /**\n       * @param {object} props\n       */\n    constructor(props: object);\n    /** @type {{data: number[], labels: number[], all: number[]}} */\n    _cache: {\n        data: number[];\n        labels: number[];\n        all: number[];\n    };\n    /** @type {Unit} */\n    _unit: Unit;\n    /** @type {Unit=} */\n    _majorUnit: Unit | undefined;\n    _offsets: {};\n    _normalized: boolean;\n    _parseOpts: {\n        parser: any;\n        round: any;\n        isoWeekday: any;\n    };\n    init(scaleOpts: any, opts?: {}): void;\n    _adapter: DateAdapter;\n    /**\n       * @param {*} raw\n       * @param {number?} [index]\n       * @return {number}\n       */\n    parse(raw: any, index?: number | null): number;\n    /**\n       * @private\n       */\n    private _getLabelBounds;\n    /**\n       * Returns the start and end offsets from edges in the form of {start, end}\n       * where each value is a relative width to the scale and ranges between 0 and 1.\n       * They add extra margins on the both sides by scaling down the original scale.\n       * Offsets are added when the `offset` option is true.\n       * @param {number[]} timestamps\n       * @protected\n       */\n    protected initOffsets(timestamps?: number[]): void;\n    /**\n       * Generates a maximum of `capacity` timestamps between min and max, rounded to the\n       * `minor` unit using the given scale time `options`.\n       * Important: this method can return ticks outside the min and max range, it's the\n       * responsibility of the calling code to clamp values if needed.\n       * @private\n       */\n    private _generate;\n    /**\n       * @param {number} value\n       * @return {string}\n       */\n    getLabelForValue(value: number): string;\n    /**\n       * @param {number} value\n       * @param {string|undefined} format\n       * @return {string}\n       */\n    format(value: number, format: string | undefined): string;\n    /**\n       * Function to format an individual tick mark\n       * @param {number} time\n       * @param {number} index\n       * @param {object[]} ticks\n       * @param {string|undefined} [format]\n       * @return {string}\n       * @private\n       */\n    private _tickFormatFunction;\n    /**\n       * @param {object[]} ticks\n       */\n    generateTickLabels(ticks: object[]): void;\n    /**\n       * @param {number} value - Milliseconds since epoch (1 January 1970 00:00:00 UTC)\n       * @return {number}\n       */\n    getDecimalForValue(value: number): number;\n    /**\n       * @param {number} value - Milliseconds since epoch (1 January 1970 00:00:00 UTC)\n       * @return {number}\n       */\n    getPixelForValue(value: number): number;\n    /**\n       * @param {number} pixel\n       * @return {number}\n       */\n    getValueForPixel(pixel: number): number;\n    /**\n       * @param {string} label\n       * @return {{w:number, h:number}}\n       * @private\n       */\n    private _getLabelSize;\n    /**\n       * @param {number} exampleTime\n       * @return {number}\n       * @private\n       */\n    private _getLabelCapacity;\n    /**\n       * @protected\n       */\n    protected getDataTimestamps(): any;\n    /**\n       * @protected\n       */\n    protected getLabelTimestamps(): number[];\n    /**\n       * @param {number[]} values\n       * @protected\n       */\n    protected normalize(values: number[]): number[];\n}\nexport type Unit = import('../core/core.adapters.js').TimeUnit;\nexport type Interval = {\n    common: boolean;\n    size: number;\n    steps?: number;\n};\nexport type DateAdapter = import('../core/core.adapters.js').DateAdapter;\nimport Scale from \"../core/core.scale.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/scales/scale.timeseries.d.ts",
    "content": "export default TimeSeriesScale;\ndeclare class TimeSeriesScale extends TimeScale {\n    /** @type {object[]} */\n    _table: object[];\n    /** @type {number} */\n    _minPos: number;\n    /** @type {number} */\n    _tableRange: number;\n    /**\n       * @protected\n       */\n    protected initOffsets(): void;\n    /**\n       * Returns an array of {time, pos} objects used to interpolate a specific `time` or position\n       * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is\n       * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other\n       * extremity (left + width or top + height). Note that it would be more optimized to directly\n       * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need\n       * to create the lookup table. The table ALWAYS contains at least two items: min and max.\n       * @param {number[]} timestamps\n       * @return {object[]}\n       * @protected\n       */\n    protected buildLookupTable(timestamps: number[]): object[];\n    /**\n       * Returns all timestamps\n       * @return {number[]}\n       * @private\n       */\n    private _getTimestampsForTable;\n}\nimport TimeScale from \"./scale.time.js\";\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/animation.d.ts",
    "content": "import {Chart} from './index.js';\nimport {AnyObject} from './basic.js';\n\nexport declare class Animation {\n  constructor(cfg: AnyObject, target: AnyObject, prop: string, to?: unknown);\n  active(): boolean;\n  update(cfg: AnyObject, to: unknown, date: number): void;\n  cancel(): void;\n  tick(date: number): void;\n  readonly _to: unknown;\n}\n\nexport interface AnimationEvent {\n  chart: Chart;\n  numSteps: number;\n  initial: boolean;\n  currentStep: number;\n}\n\nexport declare class Animator {\n  listen(chart: Chart, event: 'complete' | 'progress', cb: (event: AnimationEvent) => void): void;\n  add(chart: Chart, items: readonly Animation[]): void;\n  has(chart: Chart): boolean;\n  start(chart: Chart): void;\n  running(chart: Chart): boolean;\n  stop(chart: Chart): void;\n  remove(chart: Chart): boolean;\n}\n\nexport declare class Animations {\n  constructor(chart: Chart, animations: AnyObject);\n  configure(animations: AnyObject): void;\n  update(target: AnyObject, values: AnyObject): undefined | boolean;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/basic.d.ts",
    "content": "\nexport type AnyObject = Record<string, any>;\nexport type EmptyObject = Record<string, never>;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/color.d.ts",
    "content": "export type Color = string | CanvasGradient | CanvasPattern;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/geometric.d.ts",
    "content": "export interface ChartArea {\n  top: number;\n  left: number;\n  right: number;\n  bottom: number;\n  width: number;\n  height: number;\n}\n\nexport interface Point {\n  x: number;\n  y: number;\n}\n\nexport type TRBL = {\n  top: number;\n  right: number;\n  bottom: number;\n  left: number;\n}\n\nexport type TRBLCorners = {\n  topLeft: number;\n  topRight: number;\n  bottomLeft: number;\n  bottomRight: number;\n};\n\nexport type CornerRadius = number | Partial<TRBLCorners>;\n\nexport type RoundedRect = {\n  x: number;\n  y: number;\n  w: number;\n  h: number;\n  radius?: CornerRadius\n}\n\nexport type Padding = Partial<TRBL> | number | Point;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/helpers/helpers.canvas.d.ts",
    "content": "import {PointStyle, Scriptable, ScriptableScaleContext} from '../index.js';\nimport {Color} from '../color.js';\nimport {ChartArea, RoundedRect} from '../geometric.js';\nimport {CanvasFontSpec} from '../../helpers/helpers.options.js';\n\nexport function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void;\n\nexport function clipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void;\n\nexport function unclipArea(ctx: CanvasRenderingContext2D): void;\n\nexport interface DrawPointOptions {\n  pointStyle: PointStyle;\n  rotation?: number;\n  radius: number;\n  borderWidth: number;\n}\n\nexport function drawPoint(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number): void;\n\nexport function drawPointLegend(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number, w: number): void;\n\n/**\n * Converts the given font object into a CSS font string.\n * @param font a font object\n * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font\n */\nexport function toFontString(font: { size: number; family: string; style?: string; weight?: string }): string | null;\n\nexport interface RenderTextOpts {\n  /**\n   * The fill color of the text. If unset, the existing\n   * fillStyle property of the canvas is unchanged.\n   */\n  color?: Color;\n\n  /**\n   * The width of the strikethrough / underline\n   * @default 2\n   */\n  decorationWidth?: number;\n\n  /**\n   * The max width of the text in pixels\n   */\n  maxWidth?: number;\n\n  /**\n   * A rotation to be applied to the canvas\n   * This is applied after the translation is applied\n   */\n  rotation?: number;\n\n  /**\n   * Apply a strikethrough effect to the text\n   */\n  strikethrough?: boolean;\n\n  /**\n   * The color of the text stroke. If unset, the existing\n   * strokeStyle property of the context is unchanged\n   */\n  strokeColor?: Color;\n\n  /**\n   * The text stroke width. If unset, the existing\n   * lineWidth property of the context is unchanged\n   */\n  strokeWidth?: number;\n\n  /**\n   * The text alignment to use. If unset, the existing\n   * textAlign property of the context is unchanged\n   */\n  textAlign?: CanvasTextAlign;\n\n  /**\n   * The text baseline to use. If unset, the existing\n   * textBaseline property of the context is unchanged\n   */\n  textBaseline?: CanvasTextBaseline;\n\n  /**\n   * If specified, a translation to apply to the context\n   */\n  translation?: [number, number];\n\n  /**\n   * Underline the text\n   */\n  underline?: boolean;\n\n  /**\n   * Dimensions for drawing the label backdrop\n   */\n  backdrop?: BackdropOptions;\n}\n\nexport interface BackdropOptions {\n  /**\n   * Left position of backdrop as pixel\n   */\n  left: number;\n\n  /**\n   * Top position of backdrop as pixel\n   */\n  top: number;\n\n  /**\n   * Width of backdrop in pixels\n   */\n  width: number;\n\n  /**\n   * Height of backdrop in pixels\n   */\n  height: number;\n\n  /**\n   * Color of label backdrops.\n   */\n  color: Scriptable<Color, ScriptableScaleContext>;\n}\n\nexport function renderText(\n  ctx: CanvasRenderingContext2D,\n  text: string | string[],\n  x: number,\n  y: number,\n  font: CanvasFontSpec,\n  opts?: RenderTextOpts\n): void;\n\nexport function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: RoundedRect): void;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/helpers/helpers.segment.d.ts",
    "content": "export {};\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/helpers/index.d.ts",
    "content": "export * from './helpers.canvas.js';\nexport * from './helpers.canvas.js';\nexport * from './helpers.segment.js';\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/index.d.ts",
    "content": "/* eslint-disable @typescript-eslint/ban-types */\nimport {DeepPartial, DistributiveArray, UnionToIntersection} from './utils.js';\n\nimport {TimeUnit} from '../core/core.adapters.js';\nimport PointElement from '../elements/element.point.js';\nimport {EasingFunction} from '../helpers/helpers.easing.js';\nimport {AnimationEvent} from './animation.js';\nimport {AnyObject, EmptyObject} from './basic.js';\nimport {Color} from './color.js';\nimport Element from '../core/core.element.js';\nimport {ChartArea, Padding, Point} from './geometric.js';\nimport {LayoutItem, LayoutPosition} from './layout.js';\nimport {RenderTextOpts} from './helpers/helpers.canvas.js';\nimport {CanvasFontSpec} from '../helpers/helpers.options.js';\nimport type {ColorsPluginOptions} from '../plugins/plugin.colors.js';\n\nexport {EasingFunction} from '../helpers/helpers.easing.js';\nexport {default as ArcElement, ArcProps} from '../elements/element.arc.js';\nexport {default as PointElement, PointProps} from '../elements/element.point.js';\nexport {Animation, Animations, Animator, AnimationEvent} from './animation.js';\nexport {Color} from './color.js';\nexport {ChartArea, Point} from './geometric.js';\nexport {LayoutItem, LayoutPosition} from './layout.js';\n\nexport interface ScriptableContext<TType extends ChartType> {\n  active: boolean;\n  chart: Chart;\n  dataIndex: number;\n  dataset: UnionToIntersection<ChartDataset<TType>>;\n  datasetIndex: number;\n  type: string;\n  mode: string;\n  parsed: UnionToIntersection<ParsedDataType<TType>>;\n  raw: unknown;\n}\n\nexport interface ScriptableLineSegmentContext {\n  type: 'segment',\n  p0: PointElement,\n  p1: PointElement,\n  p0DataIndex: number,\n  p1DataIndex: number,\n  datasetIndex: number\n}\n\nexport type Scriptable<T, TContext> = T | ((ctx: TContext, options: AnyObject) => T | undefined);\nexport type ScriptableOptions<T, TContext> = { [P in keyof T]: Scriptable<T[P], TContext> };\nexport type ScriptableAndScriptableOptions<T, TContext> = Scriptable<T, TContext> | ScriptableOptions<T, TContext>;\nexport type ScriptableAndArray<T, TContext> = readonly T[] | Scriptable<T, TContext>;\nexport type ScriptableAndArrayOptions<T, TContext> = { [P in keyof T]: ScriptableAndArray<T[P], TContext> };\n\nexport interface ParsingOptions {\n  /**\n   * How to parse the dataset. The parsing can be disabled by specifying parsing: false at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally.\n   */\n  parsing:\n  {\n    [key: string]: string;\n  }\n  | false;\n\n  /**\n   * Chart.js is fastest if you provide data with indices that are unique, sorted, and consistent across datasets and provide the normalized: true option to let Chart.js know that you have done so.\n   */\n  normalized: boolean;\n}\n\nexport interface ControllerDatasetOptions extends ParsingOptions {\n  /**\n   * The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts.\n   * @default 'x'\n   */\n  indexAxis: 'x' | 'y';\n  /**\n   * How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. 0 = clip at chartArea. Clipping can also be configured per side: `clip: {left: 5, top: false, right: -2, bottom: 0}`\n   */\n  clip: number | ChartArea | false;\n  /**\n   * The label for the dataset which appears in the legend and tooltips.\n   */\n  label: string;\n  /**\n   * The drawing order of dataset. Also affects order for stacking, tooltip and legend.\n   */\n  order: number;\n\n  /**\n   * The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack).\n   */\n  stack: string;\n  /**\n     * Configures the visibility state of the dataset. Set it to true, to hide the dataset from the chart.\n   * @default false\n   */\n  hidden: boolean;\n}\n\nexport interface BarControllerDatasetOptions\n  extends ControllerDatasetOptions,\n  ScriptableAndArrayOptions<BarOptions, ScriptableContext<'bar'>>,\n  ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'bar'>>,\n  AnimationOptions<'bar'> {\n  /**\n   * The ID of the x axis to plot this dataset on.\n   */\n  xAxisID: string;\n  /**\n   * The ID of the y axis to plot this dataset on.\n   */\n  yAxisID: string;\n\n  /**\n   * Percent (0-1) of the available width each bar should be within the category width. 1.0 will take the whole category width and put the bars right next to each other.\n   * @default 0.9\n   */\n  barPercentage: number;\n  /**\n   * Percent (0-1) of the available width each category should be within the sample width.\n   * @default 0.8\n   */\n  categoryPercentage: number;\n\n  /**\n   * Manually set width of each bar in pixels. If set to 'flex', it computes \"optimal\" sample widths that globally arrange bars side by side. If not set (default), bars are equally sized based on the smallest interval.\n   */\n  barThickness: number | 'flex';\n\n  /**\n   * Set this to ensure that bars are not sized thicker than this.\n   */\n  maxBarThickness: number;\n\n  /**\n   * Set this to ensure that bars have a minimum length in pixels.\n   */\n  minBarLength: number;\n\n  /**\n   * Point style for the legend\n   * @default 'circle;\n   */\n  pointStyle: PointStyle;\n\n  /**\n   * Should the bars be grouped on index axis\n   * @default true\n   */\n  grouped: boolean;\n}\n\nexport interface BarControllerChartOptions {\n  /**\n   * Should null or undefined values be omitted from drawing\n   */\n  skipNull?: boolean;\n}\n\nexport type BarController = DatasetController\nexport declare const BarController: ChartComponent & {\n  prototype: BarController;\n  new (chart: Chart, datasetIndex: number): BarController;\n};\n\nexport interface BubbleControllerDatasetOptions\n  extends ControllerDatasetOptions,\n  ScriptableAndArrayOptions<PointOptions, ScriptableContext<'bubble'>>,\n  ScriptableAndArrayOptions<PointHoverOptions, ScriptableContext<'bubble'>> {\n  /**\n   * The ID of the x axis to plot this dataset on.\n   */\n  xAxisID: string;\n  /**\n   * The ID of the y axis to plot this dataset on.\n   */\n  yAxisID: string;\n}\n\nexport interface BubbleDataPoint extends Point {\n  /**\n   * Bubble radius in pixels (not scaled).\n   */\n  r: number;\n}\n\nexport type BubbleController = DatasetController\nexport declare const BubbleController: ChartComponent & {\n  prototype: BubbleController;\n  new (chart: Chart, datasetIndex: number): BubbleController;\n};\n\nexport interface LineControllerDatasetOptions\n  extends ControllerDatasetOptions,\n  ScriptableAndArrayOptions<PointPrefixedOptions, ScriptableContext<'line'>>,\n  ScriptableAndArrayOptions<PointPrefixedHoverOptions, ScriptableContext<'line'>>,\n  ScriptableOptions<Omit<LineOptions, keyof CommonElementOptions>, ScriptableContext<'line'>>,\n  ScriptableAndArrayOptions<CommonElementOptions, ScriptableContext<'line'>>,\n  ScriptableOptions<Omit<LineHoverOptions, keyof CommonHoverOptions>, ScriptableContext<'line'>>,\n  ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'line'>>,\n  AnimationOptions<'line'> {\n  /**\n   * The ID of the x axis to plot this dataset on.\n   */\n  xAxisID: string;\n  /**\n   * The ID of the y axis to plot this dataset on.\n   */\n  yAxisID: string;\n\n  /**\n   * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.\n   * @default false\n   */\n  spanGaps: boolean | number;\n\n  showLine: boolean;\n}\n\nexport interface LineControllerChartOptions {\n  /**\n   * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.\n   * @default false\n   */\n  spanGaps: boolean | number;\n  /**\n   * If false, the lines between points are not drawn.\n   * @default true\n   */\n  showLine: boolean;\n}\n\nexport type LineController = DatasetController\nexport declare const LineController: ChartComponent & {\n  prototype: LineController;\n  new (chart: Chart, datasetIndex: number): LineController;\n};\n\nexport type ScatterControllerDatasetOptions = LineControllerDatasetOptions;\n\nexport type ScatterDataPoint = Point\n\nexport type ScatterControllerChartOptions = LineControllerChartOptions;\n\nexport type ScatterController = LineController\nexport declare const ScatterController: ChartComponent & {\n  prototype: ScatterController;\n  new (chart: Chart, datasetIndex: number): ScatterController;\n};\n\nexport interface DoughnutControllerDatasetOptions\n  extends ControllerDatasetOptions,\n  ScriptableAndArrayOptions<ArcOptions, ScriptableContext<'doughnut'>>,\n  ScriptableAndArrayOptions<ArcHoverOptions, ScriptableContext<'doughnut'>>,\n  AnimationOptions<'doughnut'> {\n\n  /**\n   * Sweep to allow arcs to cover.\n   * @default 360\n   */\n  circumference: number;\n\n  /**\n   * Arc offset (in pixels).\n   */\n  offset: number | number[];\n\n  /**\n   * Starting angle to draw this dataset from.\n   * @default 0\n   */\n  rotation: number;\n\n  /**\n   * The relative thickness of the dataset. Providing a value for weight will cause the pie or doughnut dataset to be drawn with a thickness relative to the sum of all the dataset weight values.\n   * @default 1\n   */\n  weight: number;\n\n  /**\n   * Similar to the `offset` option, but applies to all arcs. This can be used to to add spaces\n   * between arcs\n   * @default 0\n   */\n  spacing: number;\n}\n\nexport interface DoughnutAnimationOptions {\n  /**\n   *   If true, the chart will animate in with a rotation animation. This property is in the options.animation object.\n   * @default true\n   */\n  animateRotate: boolean;\n\n  /**\n   * If true, will animate scaling the chart from the center outwards.\n   * @default false\n   */\n  animateScale: boolean;\n}\n\nexport interface DoughnutControllerChartOptions {\n  /**\n   * Sweep to allow arcs to cover.\n   * @default 360\n   */\n  circumference: number;\n\n  /**\n   * The portion of the chart that is cut out of the middle. ('50%' - for doughnut, 0 - for pie)\n   * String ending with '%' means percentage, number means pixels.\n   * @default 50\n   */\n  cutout: Scriptable<number | string, ScriptableContext<'doughnut'>>;\n\n  /**\n   * Arc offset (in pixels).\n   */\n  offset: number | number[];\n\n  /**\n   * The outer radius of the chart. String ending with '%' means percentage of maximum radius, number means pixels.\n   * @default '100%'\n   */\n  radius: Scriptable<number | string, ScriptableContext<'doughnut'>>;\n\n  /**\n   * Starting angle to draw arcs from.\n   * @default 0\n   */\n  rotation: number;\n\n  /**\n   * Spacing between the arcs\n   * @default 0\n   */\n  spacing: number;\n\n  animation: false | DoughnutAnimationOptions;\n}\n\nexport type DoughnutDataPoint = number;\n\nexport interface DoughnutController extends DatasetController {\n  readonly innerRadius: number;\n  readonly outerRadius: number;\n  readonly offsetX: number;\n  readonly offsetY: number;\n\n  calculateTotal(): number;\n  calculateCircumference(value: number): number;\n}\n\nexport declare const DoughnutController: ChartComponent & {\n  prototype: DoughnutController;\n  new (chart: Chart, datasetIndex: number): DoughnutController;\n};\n\nexport interface DoughnutMetaExtensions {\n  total: number;\n}\n\nexport type PieControllerDatasetOptions = DoughnutControllerDatasetOptions;\nexport type PieControllerChartOptions = DoughnutControllerChartOptions;\nexport type PieAnimationOptions = DoughnutAnimationOptions;\n\nexport type PieDataPoint = DoughnutDataPoint;\nexport type PieMetaExtensions = DoughnutMetaExtensions;\n\nexport type PieController = DoughnutController\nexport declare const PieController: ChartComponent & {\n  prototype: PieController;\n  new (chart: Chart, datasetIndex: number): PieController;\n};\n\nexport interface PolarAreaControllerDatasetOptions extends DoughnutControllerDatasetOptions {\n  /**\n   * Arc angle to cover. - for polar only\n   * @default circumference / (arc count)\n   */\n  angle: number;\n}\n\nexport type PolarAreaAnimationOptions = DoughnutAnimationOptions;\n\nexport interface PolarAreaControllerChartOptions {\n  /**\n   * Starting angle to draw arcs for the first item in a dataset. In degrees, 0 is at top.\n   * @default 0\n   */\n  startAngle: number;\n\n  animation: false | PolarAreaAnimationOptions;\n}\n\nexport interface PolarAreaController extends DoughnutController {\n  countVisibleElements(): number;\n}\nexport declare const PolarAreaController: ChartComponent & {\n  prototype: PolarAreaController;\n  new (chart: Chart, datasetIndex: number): PolarAreaController;\n};\n\nexport interface RadarControllerDatasetOptions\n  extends ControllerDatasetOptions,\n  ScriptableAndArrayOptions<PointOptions & PointHoverOptions & PointPrefixedOptions & PointPrefixedHoverOptions, ScriptableContext<'radar'>>,\n  ScriptableAndArrayOptions<LineOptions & LineHoverOptions, ScriptableContext<'radar'>>,\n  AnimationOptions<'radar'> {\n  /**\n   * The ID of the x axis to plot this dataset on.\n   */\n  xAxisID: string;\n  /**\n   * The ID of the y axis to plot this dataset on.\n   */\n  yAxisID: string;\n\n  /**\n   * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.\n   */\n  spanGaps: boolean | number;\n\n  /**\n   * If false, the line is not drawn for this dataset.\n   */\n  showLine: boolean;\n}\n\nexport type RadarControllerChartOptions = LineControllerChartOptions;\n\nexport type RadarController = DatasetController\nexport declare const RadarController: ChartComponent & {\n  prototype: RadarController;\n  new (chart: Chart, datasetIndex: number): RadarController;\n};\ninterface ChartMetaCommon<TElement extends Element = Element, TDatasetElement extends Element = Element> {\n  type: string;\n  controller: DatasetController;\n  order: number;\n\n  label: string;\n  index: number;\n  visible: boolean;\n\n  stack: number;\n\n  indexAxis: 'x' | 'y';\n\n  data: TElement[];\n  dataset?: TDatasetElement;\n\n  hidden: boolean;\n\n  xAxisID?: string;\n  yAxisID?: string;\n  rAxisID?: string;\n  iAxisID: string;\n  vAxisID: string;\n\n  xScale?: Scale;\n  yScale?: Scale;\n  rScale?: Scale;\n  iScale?: Scale;\n  vScale?: Scale;\n\n  _sorted: boolean;\n  _stacked: boolean | 'single';\n  _parsed: unknown[];\n}\n\nexport type ChartMeta<\n  TType extends ChartType = ChartType,\n  TElement extends Element = Element,\n  TDatasetElement extends Element = Element,\n> = DeepPartial<\n{ [key in ChartType]: ChartTypeRegistry[key]['metaExtensions'] }[TType]\n> & ChartMetaCommon<TElement, TDatasetElement>;\n\nexport interface ActiveDataPoint {\n  datasetIndex: number;\n  index: number;\n}\n\nexport interface ActiveElement extends ActiveDataPoint {\n  element: Element;\n}\n\nexport declare class Chart<\n  TType extends ChartType = ChartType,\n  TData = DefaultDataPoint<TType>,\n  TLabel = unknown\n> {\n  readonly platform: BasePlatform;\n  readonly id: string;\n  readonly canvas: HTMLCanvasElement;\n  readonly ctx: CanvasRenderingContext2D;\n  readonly config: ChartConfiguration<TType, TData, TLabel> | ChartConfigurationCustomTypesPerDataset<TType, TData, TLabel>;\n  readonly width: number;\n  readonly height: number;\n  readonly aspectRatio: number;\n  readonly boxes: LayoutItem[];\n  readonly currentDevicePixelRatio: number;\n  readonly chartArea: ChartArea;\n  readonly scales: { [key: string]: Scale };\n  readonly attached: boolean;\n\n  readonly legend?: LegendElement<TType>; // Only available if legend plugin is registered and enabled\n  readonly tooltip?: TooltipModel<TType>; // Only available if tooltip plugin is registered and enabled\n\n  data: ChartData<TType, TData, TLabel>;\n  options: ChartOptions<TType>;\n\n  constructor(item: ChartItem, config: ChartConfiguration<TType, TData, TLabel> | ChartConfigurationCustomTypesPerDataset<TType, TData, TLabel>);\n\n  clear(): this;\n  stop(): this;\n\n  resize(width?: number, height?: number): void;\n  ensureScalesHaveIDs(): void;\n  buildOrUpdateScales(): void;\n  buildOrUpdateControllers(): void;\n  reset(): void;\n  update(mode?: UpdateMode): void;\n  render(): void;\n  draw(): void;\n\n  isPointInArea(point: Point): boolean;\n  getElementsAtEventForMode(e: Event, mode: string, options: InteractionOptions, useFinalPosition: boolean): InteractionItem[];\n\n  getSortedVisibleDatasetMetas(): ChartMeta[];\n  getDatasetMeta(datasetIndex: number): ChartMeta;\n  getVisibleDatasetCount(): number;\n  isDatasetVisible(datasetIndex: number): boolean;\n  setDatasetVisibility(datasetIndex: number, visible: boolean): void;\n  toggleDataVisibility(index: number): void;\n  getDataVisibility(index: number): boolean;\n  hide(datasetIndex: number, dataIndex?: number): void;\n  show(datasetIndex: number, dataIndex?: number): void;\n\n  getActiveElements(): ActiveElement[];\n  setActiveElements(active: ActiveDataPoint[]): void;\n\n  destroy(): void;\n  toBase64Image(type?: string, quality?: unknown): string;\n  bindEvents(): void;\n  unbindEvents(): void;\n  updateHoverStyle(items: InteractionItem[], mode: 'dataset', enabled: boolean): void;\n\n  notifyPlugins(hook: string, args?: AnyObject): boolean | void;\n\n  isPluginEnabled(pluginId: string): boolean;\n\n  static readonly defaults: Defaults;\n  static readonly overrides: Overrides;\n  static readonly version: string;\n  static readonly instances: { [key: string]: Chart };\n  static readonly registry: Registry;\n  static getChart(key: string | CanvasRenderingContext2D | HTMLCanvasElement): Chart | undefined;\n  static register(...items: ChartComponentLike[]): void;\n  static unregister(...items: ChartComponentLike[]): void;\n}\n\nexport declare const registerables: readonly ChartComponentLike[];\n\nexport declare type ChartItem =\n  | string\n  | CanvasRenderingContext2D\n  | HTMLCanvasElement\n  | { canvas: HTMLCanvasElement }\n  | ArrayLike<CanvasRenderingContext2D | HTMLCanvasElement>;\n\nexport declare enum UpdateModeEnum {\n  resize = 'resize',\n  reset = 'reset',\n  none = 'none',\n  hide = 'hide',\n  show = 'show',\n  default = 'default',\n  active = 'active'\n}\n\nexport type UpdateMode = keyof typeof UpdateModeEnum;\n\nexport declare class DatasetController<\n  TType extends ChartType = ChartType,\n  TElement extends Element = Element,\n  TDatasetElement extends Element = Element,\n  TParsedData = ParsedDataType<TType>,\n> {\n  constructor(chart: Chart, datasetIndex: number);\n\n  readonly chart: Chart;\n  readonly index: number;\n  readonly _cachedMeta: ChartMeta<TType, TElement, TDatasetElement>;\n  enableOptionSharing: boolean;\n  // If true, the controller supports the decimation\n  // plugin. Defaults to `false` for all controllers\n  // except the LineController\n  supportsDecimation: boolean;\n\n  linkScales(): void;\n  getAllParsedValues(scale: Scale): number[];\n  protected getLabelAndValue(index: number): { label: string; value: string };\n  updateElements(elements: TElement[], start: number, count: number, mode: UpdateMode): void;\n  update(mode: UpdateMode): void;\n  updateIndex(datasetIndex: number): void;\n  protected getMaxOverflow(): boolean | number;\n  draw(): void;\n  reset(): void;\n  getDataset(): ChartDataset;\n  getMeta(): ChartMeta<TType, TElement, TDatasetElement>;\n  getScaleForId(scaleID: string): Scale | undefined;\n  configure(): void;\n  initialize(): void;\n  addElements(): void;\n  buildOrUpdateElements(resetNewElements?: boolean): void;\n\n  getStyle(index: number, active: boolean): AnyObject;\n  protected resolveDatasetElementOptions(mode: UpdateMode): AnyObject;\n  protected resolveDataElementOptions(index: number, mode: UpdateMode): AnyObject;\n  /**\n   * Utility for checking if the options are shared and should be animated separately.\n   * @protected\n   */\n  protected getSharedOptions(options: AnyObject): undefined | AnyObject;\n  /**\n   * Utility for determining if `options` should be included in the updated properties\n   * @protected\n   */\n  protected includeOptions(mode: UpdateMode, sharedOptions: AnyObject): boolean;\n  /**\n   * Utility for updating an element with new properties, using animations when appropriate.\n   * @protected\n   */\n\n  protected updateElement(element: TElement | TDatasetElement, index: number | undefined, properties: AnyObject, mode: UpdateMode): void;\n  /**\n   * Utility to animate the shared options, that are potentially affecting multiple elements.\n   * @protected\n   */\n\n  protected updateSharedOptions(sharedOptions: AnyObject, mode: UpdateMode, newOptions: AnyObject): void;\n  removeHoverStyle(element: TElement, datasetIndex: number, index: number): void;\n  setHoverStyle(element: TElement, datasetIndex: number, index: number): void;\n\n  parse(start: number, count: number): void;\n  protected parsePrimitiveData(meta: ChartMeta<TType, TElement, TDatasetElement>, data: AnyObject[], start: number, count: number): AnyObject[];\n  protected parseArrayData(meta: ChartMeta<TType, TElement, TDatasetElement>, data: AnyObject[], start: number, count: number): AnyObject[];\n  protected parseObjectData(meta: ChartMeta<TType, TElement, TDatasetElement>, data: AnyObject[], start: number, count: number): AnyObject[];\n  protected getParsed(index: number): TParsedData;\n  protected applyStack(scale: Scale, parsed: unknown[]): number;\n  protected updateRangeFromParsed(\n    range: { min: number; max: number },\n    scale: Scale,\n    parsed: unknown[],\n    stack: boolean | string\n  ): void;\n  protected getMinMax(scale: Scale, canStack?: boolean): { min: number; max: number };\n}\n\nexport interface DatasetControllerChartComponent extends ChartComponent {\n  defaults: {\n    datasetElementType?: string | null | false;\n    dataElementType?: string | null | false;\n  };\n}\n\nexport interface Defaults extends CoreChartOptions<ChartType>, ElementChartOptions<ChartType>, PluginChartOptions<ChartType> {\n\n  scale: ScaleOptionsByType;\n  scales: {\n    [key in ScaleType]: ScaleOptionsByType<key>;\n  };\n\n  set(values: AnyObject): AnyObject;\n  set(scope: string, values: AnyObject): AnyObject;\n  get(scope: string): AnyObject;\n\n  describe(scope: string, values: AnyObject): AnyObject;\n  override(scope: string, values: AnyObject): AnyObject;\n\n  /**\n   * Routes the named defaults to fallback to another scope/name.\n   * This routing is useful when those target values, like defaults.color, are changed runtime.\n   * If the values would be copied, the runtime change would not take effect. By routing, the\n   * fallback is evaluated at each access, so its always up to date.\n   *\n   * Example:\n   *\n   *   defaults.route('elements.arc', 'backgroundColor', '', 'color')\n   *    - reads the backgroundColor from defaults.color when undefined locally\n   *\n   * @param scope Scope this route applies to.\n   * @param name Property name that should be routed to different namespace when not defined here.\n   * @param targetScope The namespace where those properties should be routed to.\n   * Empty string ('') is the root of defaults.\n   * @param targetName The target name in the target scope the property should be routed to.\n   */\n  route(scope: string, name: string, targetScope: string, targetName: string): void;\n}\n\nexport type Overrides = {\n  [key in ChartType]:\n  CoreChartOptions<key> &\n  ElementChartOptions<key> &\n  PluginChartOptions<key> &\n  DatasetChartOptions<ChartType> &\n  ScaleChartOptions<key> &\n  ChartTypeRegistry[key]['chartOptions'];\n}\n\nexport declare const defaults: Defaults;\nexport interface InteractionOptions {\n  axis?: string;\n  intersect?: boolean;\n  includeInvisible?: boolean;\n}\n\nexport interface InteractionItem {\n  element: Element;\n  datasetIndex: number;\n  index: number;\n}\n\nexport type InteractionModeFunction = (\n  chart: Chart,\n  e: ChartEvent,\n  options: InteractionOptions,\n  useFinalPosition?: boolean\n) => InteractionItem[];\n\nexport interface InteractionModeMap {\n  /**\n   * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something\n   * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item\n   */\n  index: InteractionModeFunction;\n\n  /**\n   * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something\n   * If the options.intersect is false, we find the nearest item and return the items in that dataset\n   */\n  dataset: InteractionModeFunction;\n  /**\n   * Point mode returns all elements that hit test based on the event position\n   * of the event\n   */\n  point: InteractionModeFunction;\n  /**\n   * nearest mode returns the element closest to the point\n   */\n  nearest: InteractionModeFunction;\n  /**\n   * x mode returns the elements that hit-test at the current x coordinate\n   */\n  x: InteractionModeFunction;\n  /**\n   * y mode returns the elements that hit-test at the current y coordinate\n   */\n  y: InteractionModeFunction;\n}\n\nexport type InteractionMode = keyof InteractionModeMap;\n\nexport declare const Interaction: {\n  modes: InteractionModeMap;\n\n  /**\n   * Helper function to select candidate elements for interaction\n   */\n  evaluateInteractionItems(\n    chart: Chart,\n    axis: InteractionAxis,\n    position: Point,\n    handler: (element: Element & VisualElement, datasetIndex: number, index: number) => void,\n    intersect?: boolean\n  ): InteractionItem[];\n};\n\nexport declare const layouts: {\n  /**\n   * Register a box to a chart.\n   * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.\n   * @param {Chart} chart - the chart to use\n   * @param {LayoutItem} item - the item to add to be laid out\n   */\n  addBox(chart: Chart, item: LayoutItem): void;\n\n  /**\n   * Remove a layoutItem from a chart\n   * @param {Chart} chart - the chart to remove the box from\n   * @param {LayoutItem} layoutItem - the item to remove from the layout\n   */\n  removeBox(chart: Chart, layoutItem: LayoutItem): void;\n\n  /**\n   * Sets (or updates) options on the given `item`.\n   * @param {Chart} chart - the chart in which the item lives (or will be added to)\n   * @param {LayoutItem} item - the item to configure with the given options\n   * @param options - the new item options.\n   */\n  configure(\n    chart: Chart,\n    item: LayoutItem,\n    options: { fullSize?: number; position?: LayoutPosition; weight?: number }\n  ): void;\n\n  /**\n   * Fits boxes of the given chart into the given size by having each box measure itself\n   * then running a fitting algorithm\n   * @param {Chart} chart - the chart\n   * @param {number} width - the width to fit into\n   * @param {number} height - the height to fit into\n   */\n  update(chart: Chart, width: number, height: number): void;\n};\n\nexport interface Plugin<TType extends ChartType = ChartType, O = AnyObject> extends ExtendedPlugin<TType, O> {\n  id: string;\n\n  /**\n   * @desc Called when plugin is installed for this chart instance. This hook is also invoked for disabled plugins (options === false).\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @since 3.0.0\n   */\n  install?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called when a plugin is starting. This happens when chart is created or plugin is enabled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @since 3.0.0\n   */\n  start?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called when a plugin stopping. This happens when chart is destroyed or plugin is disabled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @since 3.0.0\n   */\n  stop?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called before initializing `chart`.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  beforeInit?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called after `chart` has been initialized and before the first update.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  afterInit?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called before updating `chart`. If any plugin returns `false`, the update\n   * is cancelled (and thus subsequent render(s)) until another `update` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {UpdateMode} args.mode - The update mode\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart update.\n   */\n  beforeUpdate?(chart: Chart, args: { mode: UpdateMode, cancelable: true }, options: O): boolean | void;\n  /**\n   * @desc Called after `chart` has been updated and before rendering. Note that this\n   * hook will not be called if the chart update has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {UpdateMode} args.mode - The update mode\n   * @param {object} options - The plugin options.\n   */\n  afterUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): void;\n  /**\n   * @desc Called during the update process, before any chart elements have been created.\n   * This can be used for data decimation by changing the data array inside a dataset.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  beforeElementsUpdate?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called during chart reset\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @since version 3.0.0\n   */\n  reset?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called before updating the `chart` datasets. If any plugin returns `false`,\n   * the datasets update is cancelled until another `update` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {UpdateMode} args.mode - The update mode.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} false to cancel the datasets update.\n   * @since version 2.1.5\n   */\n  beforeDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): boolean | void;\n  /**\n   * @desc Called after the `chart` datasets have been updated. Note that this hook\n   * will not be called if the datasets update has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {UpdateMode} args.mode - The update mode.\n   * @param {object} options - The plugin options.\n   * @since version 2.1.5\n   */\n  afterDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode, cancelable: true }, options: O): void;\n  /**\n   * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin\n   * returns `false`, the datasets update is cancelled until another `update` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {number} args.index - The dataset index.\n   * @param {object} args.meta - The dataset metadata.\n   * @param {UpdateMode} args.mode - The update mode.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart datasets drawing.\n   */\n  beforeDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: true }, options: O): boolean | void;\n  /**\n   * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note\n   * that this hook will not be called if the datasets update has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {number} args.index - The dataset index.\n   * @param {object} args.meta - The dataset metadata.\n   * @param {UpdateMode} args.mode - The update mode.\n   * @param {object} options - The plugin options.\n   */\n  afterDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: false }, options: O): void;\n  /**\n   * @desc Called before laying out `chart`. If any plugin returns `false`,\n   * the layout update is cancelled until another `update` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart layout.\n   */\n  beforeLayout?(chart: Chart, args: { cancelable: true }, options: O): boolean | void;\n  /**\n   * @desc Called before scale data limits are calculated. This hook is called separately for each scale in the chart.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {Scale} args.scale - The scale.\n   * @param {object} options - The plugin options.\n   */\n  beforeDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void;\n  /**\n   * @desc Called after scale data limits are calculated. This hook is called separately for each scale in the chart.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {Scale} args.scale - The scale.\n   * @param {object} options - The plugin options.\n   */\n  afterDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void;\n  /**\n   * @desc Called before scale builds its ticks. This hook is called separately for each scale in the chart.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {Scale} args.scale - The scale.\n   * @param {object} options - The plugin options.\n   */\n  beforeBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void;\n  /**\n   * @desc Called after scale has build its ticks. This hook is called separately for each scale in the chart.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {Scale} args.scale - The scale.\n   * @param {object} options - The plugin options.\n   */\n  afterBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void;\n  /**\n   * @desc Called after the `chart` has been laid out. Note that this hook will not\n   * be called if the layout update has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  afterLayout?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called before rendering `chart`. If any plugin returns `false`,\n   * the rendering is cancelled until another `render` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart rendering.\n   */\n  beforeRender?(chart: Chart, args: { cancelable: true }, options: O): boolean | void;\n  /**\n   * @desc Called after the `chart` has been fully rendered (and animation completed). Note\n   * that this hook will not be called if the rendering has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  afterRender?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called before drawing `chart` at every animation frame. If any plugin returns `false`,\n   * the frame drawing is cancelled untilanother `render` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart drawing.\n   */\n  beforeDraw?(chart: Chart, args: { cancelable: true }, options: O): boolean | void;\n  /**\n   * @desc Called after the `chart` has been drawn. Note that this hook will not be called\n   * if the drawing has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  afterDraw?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,\n   * the datasets drawing is cancelled until another `render` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart datasets drawing.\n   */\n  beforeDatasetsDraw?(chart: Chart, args: { cancelable: true }, options: O): boolean | void;\n  /**\n   * @desc Called after the `chart` datasets have been drawn. Note that this hook\n   * will not be called if the datasets drawing has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  afterDatasetsDraw?(chart: Chart, args: EmptyObject, options: O, cancelable: false): void;\n  /**\n   * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets\n   * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing\n   * is cancelled until another `render` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {number} args.index - The dataset index.\n   * @param {object} args.meta - The dataset metadata.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart datasets drawing.\n   */\n  beforeDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): boolean | void;\n  /**\n   * @desc Called after the `chart` datasets at the given `args.index` have been drawn\n   * (datasets are drawn in the reverse order). Note that this hook will not be called\n   * if the datasets drawing has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {number} args.index - The dataset index.\n   * @param {object} args.meta - The dataset metadata.\n   * @param {object} options - The plugin options.\n   */\n  afterDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): void;\n  /**\n   * @desc Called before processing the specified `event`. If any plugin returns `false`,\n   * the event will be discarded.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {ChartEvent} args.event - The event object.\n   * @param {boolean} args.replay - True if this event is replayed from `Chart.update`\n   * @param {boolean} args.inChartArea - The event position is inside chartArea\n   * @param {object} options - The plugin options.\n   */\n  beforeEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean, cancelable: true, inChartArea: boolean }, options: O): boolean | void;\n  /**\n   * @desc Called after the `event` has been consumed. Note that this hook\n   * will not be called if the `event` has been previously discarded.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {ChartEvent} args.event - The event object.\n   * @param {boolean} args.replay - True if this event is replayed from `Chart.update`\n   * @param {boolean} args.inChartArea - The event position is inside chartArea\n   * @param {boolean} [args.changed] - Set to true if the plugin needs a render. Should only be changed to true, because this args object is passed through all plugins.\n   * @param {object} options - The plugin options.\n   */\n  afterEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean, changed?: boolean, cancelable: false, inChartArea: boolean }, options: O): void;\n  /**\n   * @desc Called after the chart as been resized.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {number} args.size - The new canvas display size (eq. canvas.style width & height).\n   * @param {object} options - The plugin options.\n   */\n  resize?(chart: Chart, args: { size: { width: number, height: number } }, options: O): void;\n  /**\n   * Called before the chart is being destroyed.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  beforeDestroy?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * Called after the chart has been destroyed.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   */\n  afterDestroy?(chart: Chart, args: EmptyObject, options: O): void;\n  /**\n   * Called after chart is destroyed on all plugins that were installed for that chart. This hook is also invoked for disabled plugins (options === false).\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {object} options - The plugin options.\n   * @since 3.0.0\n   */\n  uninstall?(chart: Chart, args: EmptyObject, options: O): void;\n\n  /**\n   * Default options used in the plugin\n   */\n  defaults?: Partial<O>;\n}\n\nexport declare type ChartComponentLike = ChartComponent | ChartComponent[] | { [key: string]: ChartComponent } | Plugin | Plugin[];\n\n/**\n * Please use the module's default export which provides a singleton instance\n * Note: class is exported for typedoc\n */\nexport interface Registry {\n  readonly controllers: TypedRegistry<DatasetController>;\n  readonly elements: TypedRegistry<Element>;\n  readonly plugins: TypedRegistry<Plugin>;\n  readonly scales: TypedRegistry<Scale>;\n\n  add(...args: ChartComponentLike[]): void;\n  remove(...args: ChartComponentLike[]): void;\n\n  addControllers(...args: ChartComponentLike[]): void;\n  addElements(...args: ChartComponentLike[]): void;\n  addPlugins(...args: ChartComponentLike[]): void;\n  addScales(...args: ChartComponentLike[]): void;\n\n  getController(id: string): DatasetController | undefined;\n  getElement(id: string): Element | undefined;\n  getPlugin(id: string): Plugin | undefined;\n  getScale(id: string): Scale | undefined;\n}\n\nexport declare const registry: Registry;\n\nexport interface Tick {\n  value: number;\n  label?: string | string[];\n  major?: boolean;\n}\n\nexport interface CoreScaleOptions {\n  /**\n   * Controls the axis global visibility (visible when true, hidden when false). When display: 'auto', the axis is visible only if at least one associated dataset is visible.\n   * @default true\n   */\n  display: boolean | 'auto';\n  /**\n   * Align pixel values to device pixels\n   */\n  alignToPixels: boolean;\n  /**\n   * Reverse the scale.\n   * @default false\n   */\n  reverse: boolean;\n  /**\n   * The weight used to sort the axis. Higher weights are further away from the chart area.\n   * @default true\n   */\n  weight: number;\n  /**\n   * Callback called before the update process starts.\n   */\n  beforeUpdate(axis: Scale): void;\n  /**\n   * Callback that runs before dimensions are set.\n   */\n  beforeSetDimensions(axis: Scale): void;\n  /**\n   * Callback that runs after dimensions are set.\n   */\n  afterSetDimensions(axis: Scale): void;\n  /**\n   * Callback that runs before data limits are determined.\n   */\n  beforeDataLimits(axis: Scale): void;\n  /**\n   * Callback that runs after data limits are determined.\n   */\n  afterDataLimits(axis: Scale): void;\n  /**\n   * Callback that runs before ticks are created.\n   */\n  beforeBuildTicks(axis: Scale): void;\n  /**\n   * Callback that runs after ticks are created. Useful for filtering ticks.\n   */\n  afterBuildTicks(axis: Scale): void;\n  /**\n   * Callback that runs before ticks are converted into strings.\n   */\n  beforeTickToLabelConversion(axis: Scale): void;\n  /**\n   * Callback that runs after ticks are converted into strings.\n   */\n  afterTickToLabelConversion(axis: Scale): void;\n  /**\n   * Callback that runs before tick rotation is determined.\n   */\n  beforeCalculateLabelRotation(axis: Scale): void;\n  /**\n   * Callback that runs after tick rotation is determined.\n   */\n  afterCalculateLabelRotation(axis: Scale): void;\n  /**\n   * Callback that runs before the scale fits to the canvas.\n   */\n  beforeFit(axis: Scale): void;\n  /**\n   * Callback that runs after the scale fits to the canvas.\n   */\n  afterFit(axis: Scale): void;\n  /**\n   * Callback that runs at the end of the update process.\n   */\n  afterUpdate(axis: Scale): void;\n}\n\nexport interface Scale<O extends CoreScaleOptions = CoreScaleOptions> extends Element<unknown, O>, LayoutItem {\n  readonly id: string;\n  readonly type: string;\n  readonly ctx: CanvasRenderingContext2D;\n  readonly chart: Chart;\n\n  maxWidth: number;\n  maxHeight: number;\n\n  paddingTop: number;\n  paddingBottom: number;\n  paddingLeft: number;\n  paddingRight: number;\n\n  axis: string;\n  labelRotation: number;\n  min: number;\n  max: number;\n  ticks: Tick[];\n  getMatchingVisibleMetas(type?: string): ChartMeta[];\n\n  drawTitle(chartArea: ChartArea): void;\n  drawLabels(chartArea: ChartArea): void;\n  drawGrid(chartArea: ChartArea): void;\n\n  /**\n   * @param {number} pixel\n   * @return {number}\n   */\n  getDecimalForPixel(pixel: number): number;\n  /**\n   * Utility for getting the pixel location of a percentage of scale\n   * The coordinate (0, 0) is at the upper-left corner of the canvas\n   * @param {number} decimal\n   * @return {number}\n   */\n  getPixelForDecimal(decimal: number): number;\n  /**\n   * Returns the location of the tick at the given index\n   * The coordinate (0, 0) is at the upper-left corner of the canvas\n   * @param {number} index\n   * @return {number}\n   */\n  getPixelForTick(index: number): number;\n  /**\n   * Used to get the label to display in the tooltip for the given value\n   * @param {*} value\n   * @return {string}\n   */\n  getLabelForValue(value: number): string;\n\n  /**\n   * Returns the grid line width at given value\n   */\n  getLineWidthForValue(value: number): number;\n\n  /**\n   * Returns the location of the given data point. Value can either be an index or a numerical value\n   * The coordinate (0, 0) is at the upper-left corner of the canvas\n   * @param {*} value\n   * @param {number} [index]\n   * @return {number}\n   */\n  getPixelForValue(value: number, index?: number): number;\n\n  /**\n   * Used to get the data value from a given pixel. This is the inverse of getPixelForValue\n   * The coordinate (0, 0) is at the upper-left corner of the canvas\n   * @param {number} pixel\n   * @return {*}\n   */\n  getValueForPixel(pixel: number): number | undefined;\n\n  getBaseValue(): number;\n  /**\n   * Returns the pixel for the minimum chart value\n   * The coordinate (0, 0) is at the upper-left corner of the canvas\n   * @return {number}\n   */\n  getBasePixel(): number;\n\n  init(options: O): void;\n  parse(raw: unknown, index: number): unknown;\n  getUserBounds(): { min: number; max: number; minDefined: boolean; maxDefined: boolean };\n  getMinMax(canStack: boolean): { min: number; max: number };\n  getTicks(): Tick[];\n  getLabels(): string[];\n  getLabelItems(chartArea?: ChartArea): LabelItem[];\n  beforeUpdate(): void;\n  configure(): void;\n  afterUpdate(): void;\n  beforeSetDimensions(): void;\n  setDimensions(): void;\n  afterSetDimensions(): void;\n  beforeDataLimits(): void;\n  determineDataLimits(): void;\n  afterDataLimits(): void;\n  beforeBuildTicks(): void;\n  buildTicks(): Tick[];\n  afterBuildTicks(): void;\n  beforeTickToLabelConversion(): void;\n  generateTickLabels(ticks: Tick[]): void;\n  afterTickToLabelConversion(): void;\n  beforeCalculateLabelRotation(): void;\n  calculateLabelRotation(): void;\n  afterCalculateLabelRotation(): void;\n  beforeFit(): void;\n  fit(): void;\n  afterFit(): void;\n\n  isFullSize(): boolean;\n}\nexport declare class Scale {\n  constructor(cfg: {id: string, type: string, ctx: CanvasRenderingContext2D, chart: Chart});\n}\n\nexport interface ScriptableScaleContext {\n  chart: Chart;\n  scale: Scale;\n  index: number;\n  tick: Tick;\n}\n\nexport interface ScriptableScalePointLabelContext {\n  chart: Chart;\n  scale: Scale;\n  index: number;\n  label: string;\n  type: string;\n}\n\nexport interface LabelItem {\n  label: string | string[];\n  font: CanvasFontSpec;\n  textOffset: number;\n  options: RenderTextOpts;\n}\n\nexport declare const Ticks: {\n  formatters: {\n    /**\n     * Formatter for value labels\n     * @param value the value to display\n     * @return {string|string[]} the label to display\n     */\n    values(value: unknown): string | string[];\n    /**\n     * Formatter for numeric ticks\n     * @param tickValue the value to be formatted\n     * @param index the position of the tickValue parameter in the ticks array\n     * @param ticks the list of ticks being converted\n     * @return string representation of the tickValue parameter\n     */\n    numeric(tickValue: number, index: number, ticks: { value: number }[]): string;\n    /**\n     * Formatter for logarithmic ticks\n     * @param tickValue the value to be formatted\n     * @param index the position of the tickValue parameter in the ticks array\n     * @param ticks the list of ticks being converted\n     * @return string representation of the tickValue parameter\n     */\n    logarithmic(tickValue: number, index: number, ticks: { value: number }[]): string;\n  };\n};\n\nexport interface TypedRegistry<T> {\n  /**\n   * @param {ChartComponent} item\n   * @returns {string} The scope where items defaults were registered to.\n   */\n  register(item: ChartComponent): string;\n  get(id: string): T | undefined;\n  unregister(item: ChartComponent): void;\n}\n\nexport interface ChartEvent {\n  type:\n  | 'contextmenu'\n  | 'mouseenter'\n  | 'mousedown'\n  | 'mousemove'\n  | 'mouseup'\n  | 'mouseout'\n  | 'click'\n  | 'dblclick'\n  | 'keydown'\n  | 'keypress'\n  | 'keyup'\n  | 'resize';\n  native: Event | null;\n  x: number | null;\n  y: number | null;\n}\nexport interface ChartComponent {\n  id: string;\n  defaults?: AnyObject;\n  defaultRoutes?: { [property: string]: string };\n\n  beforeRegister?(): void;\n  afterRegister?(): void;\n  beforeUnregister?(): void;\n  afterUnregister?(): void;\n}\n\nexport type InteractionAxis = 'x' | 'y' | 'xy' | 'r';\n\nexport interface CoreInteractionOptions {\n  /**\n   * Sets which elements appear in the tooltip. See Interaction Modes for details.\n   * @default 'nearest'\n   */\n  mode: InteractionMode;\n  /**\n   * if true, the hover mode only applies when the mouse position intersects an item on the chart.\n   * @default true\n   */\n  intersect: boolean;\n\n  /**\n   * Defines which directions are used in calculating distances. Defaults to 'x' for 'index' mode and 'xy' in dataset and 'nearest' modes.\n   */\n  axis: InteractionAxis;\n\n  /**\n   * if true, the invisible points that are outside of the chart area will also be included when evaluating interactions.\n   * @default false\n   */\n  includeInvisible: boolean;\n}\n\nexport interface CoreChartOptions<TType extends ChartType> extends ParsingOptions, AnimationOptions<TType> {\n\n  datasets: {\n    [key in ChartType]: ChartTypeRegistry[key]['datasetOptions']\n  }\n\n  /**\n   * The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts.\n   * @default 'x'\n   */\n  indexAxis: 'x' | 'y';\n\n  /**\n   * How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. 0 = clip at chartArea. Clipping can also be configured per side: `clip: {left: 5, top: false, right: -2, bottom: 0}`\n   */\n  clip: number | ChartArea | false;\n\n  /**\n   * base color\n   * @see Defaults.color\n   */\n  color: Scriptable<Color, ScriptableContext<TType>>;\n  /**\n   * base background color\n   * @see Defaults.backgroundColor\n   */\n  backgroundColor: Scriptable<Color, ScriptableContext<TType>>;\n  /**\n   * base border color\n   * @see Defaults.borderColor\n   */\n  borderColor: Scriptable<Color, ScriptableContext<TType>>;\n  /**\n   * base font\n   * @see Defaults.font\n   */\n  font: Partial<FontSpec>;\n  /**\n   * Resizes the chart canvas when its container does (important note...).\n   * @default true\n   */\n  responsive: boolean;\n  /**\n   * Maintain the original canvas aspect ratio (width / height) when resizing.\n   * @default true\n   */\n  maintainAspectRatio: boolean;\n  /**\n   * Delay the resize update by give amount of milliseconds. This can ease the resize process by debouncing update of the elements.\n   * @default 0\n   */\n  resizeDelay: number;\n\n  /**\n   * Canvas aspect ratio (i.e. width / height, a value of 1 representing a square canvas). Note that this option is ignored if the height is explicitly defined either as attribute or via the style.\n   * @default 2\n   */\n  aspectRatio: number;\n\n  /**\n   * Locale used for number formatting (using `Intl.NumberFormat`).\n   * @default user's browser setting\n   */\n  locale: string;\n\n  /**\n   * Called when a resize occurs. Gets passed two arguments: the chart instance and the new size.\n   */\n  onResize(chart: Chart, size: { width: number; height: number }): void;\n\n  /**\n   * Override the window's default devicePixelRatio.\n   * @default window.devicePixelRatio\n   */\n  devicePixelRatio: number;\n\n  interaction: CoreInteractionOptions;\n\n  hover: CoreInteractionOptions;\n\n  /**\n   * The events option defines the browser events that the chart should listen to for tooltips and hovering.\n   * @default ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']\n   */\n  events: (keyof HTMLElementEventMap)[]\n\n  /**\n   * Called when any of the events fire. Passed the event, an array of active elements (bars, points, etc), and the chart.\n   */\n  onHover(event: ChartEvent, elements: ActiveElement[], chart: Chart): void;\n\n  /**\n   * Called if the event is of type 'mouseup' or 'click'. Passed the event, an array of active elements, and the chart.\n   */\n  onClick(event: ChartEvent, elements: ActiveElement[], chart: Chart): void;\n\n  layout: Partial<{\n    autoPadding: boolean;\n    padding: Scriptable<Padding, ScriptableContext<TType>>;\n  }>;\n}\n\nexport type AnimationSpec<TType extends ChartType> = {\n  /**\n   * The number of milliseconds an animation takes.\n   * @default 1000\n   */\n  duration?: Scriptable<number, ScriptableContext<TType>>;\n  /**\n   * Easing function to use\n   * @default 'easeOutQuart'\n   */\n  easing?: Scriptable<EasingFunction, ScriptableContext<TType>>;\n\n  /**\n   * Delay before starting the animations.\n   * @default 0\n   */\n  delay?: Scriptable<number, ScriptableContext<TType>>;\n\n  /**\n   *   If set to true, the animations loop endlessly.\n   * @default false\n   */\n  loop?: Scriptable<boolean, ScriptableContext<TType>>;\n}\n\nexport type AnimationsSpec<TType extends ChartType> = {\n  [name: string]: false | AnimationSpec<TType> & {\n    properties: string[];\n\n    /**\n     * Type of property, determines the interpolator used. Possible values: 'number', 'color' and 'boolean'. Only really needed for 'color', because typeof does not get that right.\n     */\n    type: 'color' | 'number' | 'boolean';\n\n    fn: <T>(from: T, to: T, factor: number) => T;\n\n    /**\n     * Start value for the animation. Current value is used when undefined\n     */\n    from: Scriptable<Color | number | boolean, ScriptableContext<TType>>;\n    /**\n     *\n     */\n    to: Scriptable<Color | number | boolean, ScriptableContext<TType>>;\n  }\n}\n\nexport type TransitionSpec<TType extends ChartType> = {\n  animation: AnimationSpec<TType>;\n  animations: AnimationsSpec<TType>;\n}\n\nexport type TransitionsSpec<TType extends ChartType> = {\n  [mode: string]: TransitionSpec<TType>\n}\n\nexport type AnimationOptions<TType extends ChartType> = {\n  animation: false | AnimationSpec<TType> & {\n    /**\n     * Callback called on each step of an animation.\n     */\n    onProgress?: (this: Chart, event: AnimationEvent) => void;\n    /**\n     * Callback called when all animations are completed.\n     */\n    onComplete?: (this: Chart, event: AnimationEvent) => void;\n  };\n  animations: AnimationsSpec<TType>;\n  transitions: TransitionsSpec<TType>;\n};\n\nexport interface FontSpec {\n  /**\n   * Default font family for all text, follows CSS font-family options.\n   * @default \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\"\n   */\n  family: string;\n  /**\n   * Default font size (in px) for text. Does not apply to radialLinear scale point labels.\n   * @default 12\n   */\n  size: number;\n  /**\n   * Default font style. Does not apply to tooltip title or footer. Does not apply to chart title. Follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit)\n   * @default 'normal'\n   */\n  style: 'normal' | 'italic' | 'oblique' | 'initial' | 'inherit';\n  /**\n   * Default font weight (boldness). (see MDN).\n   */\n  weight: string | null;\n  /**\n   * Height of an individual line of text (see MDN).\n   * @default 1.2\n   */\n  lineHeight: number | string;\n}\n\nexport type TextAlign = 'left' | 'center' | 'right';\nexport type Align = 'start' | 'center' | 'end';\n\nexport interface VisualElement {\n  draw(ctx: CanvasRenderingContext2D, area?: ChartArea): void;\n  inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;\n  inXRange(mouseX: number, useFinalPosition?: boolean): boolean;\n  inYRange(mouseY: number, useFinalPosition?: boolean): boolean;\n  getCenterPoint(useFinalPosition?: boolean): Point;\n  getRange?(axis: 'x' | 'y'): number;\n}\n\nexport interface CommonElementOptions {\n  borderWidth: number;\n  borderColor: Color;\n  backgroundColor: Color;\n}\n\nexport interface CommonHoverOptions {\n  hoverBorderWidth: number;\n  hoverBorderColor: Color;\n  hoverBackgroundColor: Color;\n}\n\nexport interface Segment {\n  start: number;\n  end: number;\n  loop: boolean;\n}\n\nexport interface ArcBorderRadius {\n  outerStart: number;\n  outerEnd: number;\n  innerStart: number;\n  innerEnd: number;\n}\n\nexport interface ArcOptions extends CommonElementOptions {\n  /**\n   * Arc stroke alignment.\n   */\n  borderAlign: 'center' | 'inner';\n\n  /**\n   * Line join style. See MDN. Default is 'round' when `borderAlign` is 'inner', else 'bevel'.\n   */\n  borderJoinStyle: CanvasLineJoin;\n\n  /**\n   * Sets the border radius for arcs\n   * @default 0\n   */\n  borderRadius: number | ArcBorderRadius;\n\n  /**\n   * Arc offset (in pixels).\n   */\n  offset: number;\n\n  /**\n   * If false, Arc will be flat.\n   * @default true\n   */\n  circular: boolean;\n\n  /**\n   * Spacing between arcs\n   */\n  spacing: number\n}\n\nexport interface ArcHoverOptions extends CommonHoverOptions {\n  hoverOffset: number;\n}\n\nexport interface LineProps {\n  points: Point[]\n}\n\nexport interface LineOptions extends CommonElementOptions {\n  /**\n   * Line cap style. See MDN.\n   * @default 'butt'\n   */\n  borderCapStyle: CanvasLineCap;\n  /**\n   * Line dash. See MDN.\n   * @default []\n   */\n  borderDash: number[];\n  /**\n   * Line dash offset. See MDN.\n   * @default 0.0\n   */\n  borderDashOffset: number;\n  /**\n   * Line join style. See MDN.\n   * @default 'miter'\n   */\n  borderJoinStyle: CanvasLineJoin;\n  /**\n   *   true to keep Bézier control inside the chart, false for no restriction.\n   * @default true\n   */\n  capBezierPoints: boolean;\n  /**\n   * Interpolation mode to apply.\n   * @default 'default'\n   */\n  cubicInterpolationMode: 'default' | 'monotone';\n  /**\n   * Bézier curve tension (0 for no Bézier curves).\n   * @default 0\n   */\n  tension: number;\n  /**\n   * true to show the line as a stepped line (tension will be ignored).\n   * @default false\n   */\n  stepped: 'before' | 'after' | 'middle' | boolean;\n  /**\n   * Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end\n   */\n  fill: FillTarget | ComplexFillTarget;\n  /**\n   * If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.\n   */\n  spanGaps: boolean | number;\n\n  segment: {\n    backgroundColor: Scriptable<Color|undefined, ScriptableLineSegmentContext>,\n    borderColor: Scriptable<Color|undefined, ScriptableLineSegmentContext>,\n    borderCapStyle: Scriptable<CanvasLineCap|undefined, ScriptableLineSegmentContext>;\n    borderDash: Scriptable<number[]|undefined, ScriptableLineSegmentContext>;\n    borderDashOffset: Scriptable<number|undefined, ScriptableLineSegmentContext>;\n    borderJoinStyle: Scriptable<CanvasLineJoin|undefined, ScriptableLineSegmentContext>;\n    borderWidth: Scriptable<number|undefined, ScriptableLineSegmentContext>;\n  };\n}\n\nexport interface LineHoverOptions extends CommonHoverOptions {\n  hoverBorderCapStyle: CanvasLineCap;\n  hoverBorderDash: number[];\n  hoverBorderDashOffset: number;\n  hoverBorderJoinStyle: CanvasLineJoin;\n}\n\nexport interface LineElement<T extends LineProps = LineProps, O extends LineOptions = LineOptions>\n  extends Element<T, O>,\n  VisualElement {\n  updateControlPoints(chartArea: ChartArea, indexAxis?: 'x' | 'y'): void;\n  points: Point[];\n  readonly segments: Segment[];\n  first(): Point | false;\n  last(): Point | false;\n  interpolate(point: Point, property: 'x' | 'y'): undefined | Point | Point[];\n  pathSegment(ctx: CanvasRenderingContext2D, segment: Segment, params: AnyObject): undefined | boolean;\n  path(ctx: CanvasRenderingContext2D): boolean;\n}\n\nexport declare const LineElement: ChartComponent & {\n  prototype: LineElement;\n  new (cfg: AnyObject): LineElement;\n};\n\nexport type PointStyle =\n  | 'circle'\n  | 'cross'\n  | 'crossRot'\n  | 'dash'\n  | 'line'\n  | 'rect'\n  | 'rectRounded'\n  | 'rectRot'\n  | 'star'\n  | 'triangle'\n  | false\n  | HTMLImageElement\n  | HTMLCanvasElement;\n\nexport interface PointOptions extends CommonElementOptions {\n  /**\n   * Point radius\n   * @default 3\n   */\n  radius: number;\n  /**\n   * Extra radius added to point radius for hit detection.\n   * @default 1\n   */\n  hitRadius: number;\n  /**\n   * Point style\n   * @default 'circle;\n   */\n  pointStyle: PointStyle;\n  /**\n   * Point rotation (in degrees).\n   * @default 0\n   */\n  rotation: number;\n  /**\n   * Draw the active elements over the other elements of the dataset,\n   * @default true\n   */\n  drawActiveElementsOnTop: boolean;\n}\n\nexport interface PointHoverOptions extends CommonHoverOptions {\n  /**\n   * Point radius when hovered.\n   * @default 4\n   */\n  hoverRadius: number;\n}\n\nexport interface PointPrefixedOptions {\n  /**\n   * The fill color for points.\n   */\n  pointBackgroundColor: Color;\n  /**\n   * The border color for points.\n   */\n  pointBorderColor: Color;\n  /**\n   * The width of the point border in pixels.\n   */\n  pointBorderWidth: number;\n  /**\n   * The pixel size of the non-displayed point that reacts to mouse events.\n   */\n  pointHitRadius: number;\n  /**\n   * The radius of the point shape. If set to 0, the point is not rendered.\n   */\n  pointRadius: number;\n  /**\n   * The rotation of the point in degrees.\n   */\n  pointRotation: number;\n  /**\n   * Style of the point.\n   */\n  pointStyle: PointStyle;\n}\n\nexport interface PointPrefixedHoverOptions {\n  /**\n   * Point background color when hovered.\n   */\n  pointHoverBackgroundColor: Color;\n  /**\n   * Point border color when hovered.\n   */\n  pointHoverBorderColor: Color;\n  /**\n   * Border width of point when hovered.\n   */\n  pointHoverBorderWidth: number;\n  /**\n   * The radius of the point when hovered.\n   */\n  pointHoverRadius: number;\n}\n\nexport interface BarProps extends Point {\n  base: number;\n  horizontal: boolean;\n  width: number;\n  height: number;\n}\n\nexport interface BarOptions extends Omit<CommonElementOptions, 'borderWidth'> {\n  /**\n   * The base value for the bar in data units along the value axis.\n   */\n  base: number;\n\n  /**\n   * Skipped (excluded) border: 'start', 'end', 'left',  'right', 'bottom', 'top', 'middle', false (none) or true (all).\n   * @default 'start'\n   */\n  borderSkipped: 'start' | 'end' | 'left' | 'right' | 'bottom' | 'top' | 'middle' | boolean;\n\n  /**\n   * Border radius\n   * @default 0\n   */\n  borderRadius: number | BorderRadius;\n\n  /**\n   * Amount to inflate the rectangle(s). This can be used to hide artifacts between bars.\n   * Unit is pixels. 'auto' translates to 0.33 pixels when barPercentage * categoryPercentage is 1, else 0.\n   * @default 'auto'\n   */\n  inflateAmount: number | 'auto';\n\n  /**\n   * Width of the border, number for all sides, object to specify width for each side specifically\n   * @default 0\n   */\n  borderWidth: number | { top?: number, right?: number, bottom?: number, left?: number };\n}\n\nexport interface BorderRadius {\n  topLeft: number;\n  topRight: number;\n  bottomLeft: number;\n  bottomRight: number;\n}\n\nexport interface BarHoverOptions extends CommonHoverOptions {\n  hoverBorderRadius: number | BorderRadius;\n}\n\nexport interface BarElement<\n  T extends BarProps = BarProps,\n  O extends BarOptions = BarOptions\n> extends Element<T, O>, VisualElement {}\n\nexport declare const BarElement: ChartComponent & {\n  prototype: BarElement;\n  new (cfg: AnyObject): BarElement;\n};\n\nexport interface ElementOptionsByType<TType extends ChartType> {\n  arc: ScriptableAndArrayOptions<ArcOptions & ArcHoverOptions, ScriptableContext<TType>>;\n  bar: ScriptableAndArrayOptions<BarOptions & BarHoverOptions, ScriptableContext<TType>>;\n  line: ScriptableAndArrayOptions<LineOptions & LineHoverOptions, ScriptableContext<TType>>;\n  point: ScriptableAndArrayOptions<PointOptions & PointHoverOptions, ScriptableContext<TType>>;\n}\n\nexport type ElementChartOptions<TType extends ChartType = ChartType> = {\n  elements: ElementOptionsByType<TType>\n};\n\nexport declare class BasePlatform {\n  /**\n   * Called at chart construction time, returns a context2d instance implementing\n   * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.\n   * @param {HTMLCanvasElement} canvas - The canvas from which to acquire context (platform specific)\n   * @param options - The chart options\n   */\n  acquireContext(\n    canvas: HTMLCanvasElement,\n    options?: CanvasRenderingContext2DSettings\n  ): CanvasRenderingContext2D | null;\n  /**\n   * Called at chart destruction time, releases any resources associated to the context\n   * previously returned by the acquireContext() method.\n   * @param {CanvasRenderingContext2D} context - The context2d instance\n   * @returns {boolean} true if the method succeeded, else false\n   */\n  releaseContext(context: CanvasRenderingContext2D): boolean;\n  /**\n   * Registers the specified listener on the given chart.\n   * @param {Chart} chart - Chart from which to listen for event\n   * @param {string} type - The ({@link ChartEvent}) type to listen for\n   * @param listener - Receives a notification (an object that implements\n   * the {@link ChartEvent} interface) when an event of the specified type occurs.\n   */\n  addEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void;\n  /**\n   * Removes the specified listener previously registered with addEventListener.\n   * @param {Chart} chart - Chart from which to remove the listener\n   * @param {string} type - The ({@link ChartEvent}) type to remove\n   * @param listener - The listener function to remove from the event target.\n   */\n  removeEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void;\n  /**\n   * @returns {number} the current devicePixelRatio of the device this platform is connected to.\n   */\n  getDevicePixelRatio(): number;\n  /**\n   * @param {HTMLCanvasElement} canvas - The canvas for which to calculate the maximum size\n   * @param {number} [width] - Parent element's content width\n   * @param {number} [height] - Parent element's content height\n   * @param {number} [aspectRatio] - The aspect ratio to maintain\n   * @returns { width: number, height: number } the maximum size available.\n   */\n  getMaximumSize(canvas: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number };\n  /**\n   * @param {HTMLCanvasElement} canvas\n   * @returns {boolean} true if the canvas is attached to the platform, false if not.\n   */\n  isAttached(canvas: HTMLCanvasElement): boolean;\n  /**\n   * Updates config with platform specific requirements\n   * @param {ChartConfiguration | ChartConfigurationCustomTypes} config\n   */\n  updateConfig(config: ChartConfiguration | ChartConfigurationCustomTypesPerDataset): void;\n}\n\nexport declare class BasicPlatform extends BasePlatform {}\nexport declare class DomPlatform extends BasePlatform {}\n\nexport declare const Decimation: Plugin;\n\nexport declare const enum DecimationAlgorithm {\n  lttb = 'lttb',\n  minmax = 'min-max',\n}\ninterface BaseDecimationOptions {\n  enabled: boolean;\n  threshold?: number;\n}\n\ninterface LttbDecimationOptions extends BaseDecimationOptions {\n  algorithm: DecimationAlgorithm.lttb | 'lttb';\n  samples?: number;\n}\n\ninterface MinMaxDecimationOptions extends BaseDecimationOptions {\n  algorithm: DecimationAlgorithm.minmax | 'min-max';\n}\n\nexport type DecimationOptions = LttbDecimationOptions | MinMaxDecimationOptions;\n\nexport declare const Filler: Plugin;\nexport interface FillerOptions {\n  drawTime: 'beforeDatasetDraw' | 'beforeDatasetsDraw';\n  propagate: boolean;\n}\n\nexport type FillTarget = number | string | { value: number } | 'start' | 'end' | 'origin' | 'stack' | 'shape' | boolean;\n\nexport interface ComplexFillTarget {\n  /**\n   * The accepted values are the same as the filling mode values, so you may use absolute and relative dataset indexes and/or boundaries.\n   */\n  target: FillTarget;\n  /**\n   * If no color is set, the default color will be the background color of the chart.\n   */\n  above: Color;\n  /**\n   * Same as the above.\n   */\n  below: Color;\n}\n\nexport interface FillerControllerDatasetOptions {\n  /**\n   * Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end\n   */\n  fill: FillTarget | ComplexFillTarget;\n}\n\nexport declare const Legend: Plugin;\n\nexport interface LegendItem {\n  /**\n   * Label that will be displayed\n   */\n  text: string;\n\n  /**\n   * Border radius of the legend box\n   * @since 3.1.0\n   */\n  borderRadius?: number | BorderRadius;\n\n  /**\n   * Index of the associated dataset\n   */\n  datasetIndex?: number;\n\n  /**\n   * Index the associated label in the labels array\n   */\n  index?: number\n\n  /**\n   * Fill style of the legend box\n   */\n  fillStyle?: Color;\n\n  /**\n   * Font color for the text\n   * Defaults to LegendOptions.labels.color\n   */\n  fontColor?: Color;\n\n  /**\n   * If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect\n   */\n  hidden?: boolean;\n\n  /**\n   * For box border.\n   * @see https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap\n   */\n  lineCap?: CanvasLineCap;\n\n  /**\n   * For box border.\n   * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash\n   */\n  lineDash?: number[];\n\n  /**\n   * For box border.\n   * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset\n   */\n  lineDashOffset?: number;\n\n  /**\n   * For box border.\n   * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin\n   */\n  lineJoin?: CanvasLineJoin;\n\n  /**\n   * Width of box border\n   */\n  lineWidth?: number;\n\n  /**\n   * Stroke style of the legend box\n   */\n  strokeStyle?: Color;\n\n  /**\n   * Point style of the legend box (only used if usePointStyle is true)\n   */\n  pointStyle?: PointStyle;\n\n  /**\n   * Rotation of the point in degrees (only used if usePointStyle is true)\n   */\n  rotation?: number;\n\n  /**\n   * Text alignment\n   */\n  textAlign?: TextAlign;\n}\n\nexport interface LegendElement<TType extends ChartType> extends Element<AnyObject, LegendOptions<TType>>, LayoutItem {\n  chart: Chart<TType>;\n  ctx: CanvasRenderingContext2D;\n  legendItems?: LegendItem[];\n  options: LegendOptions<TType>;\n}\n\nexport interface LegendOptions<TType extends ChartType> {\n  /**\n   * Is the legend shown?\n   * @default true\n   */\n  display: boolean;\n  /**\n   * Position of the legend.\n   * @default 'top'\n   */\n  position: LayoutPosition;\n  /**\n   * Alignment of the legend.\n   * @default 'center'\n   */\n  align: Align;\n  /**\n   * Maximum height of the legend, in pixels\n   */\n  maxHeight: number;\n  /**\n   * Maximum width of the legend, in pixels\n   */\n  maxWidth: number;\n  /**\n   * Marks that this box should take the full width/height of the canvas (moving other boxes). This is unlikely to need to be changed in day-to-day use.\n   * @default true\n   */\n  fullSize: boolean;\n  /**\n   * Legend will show datasets in reverse order.\n   * @default false\n   */\n  reverse: boolean;\n  /**\n   * A callback that is called when a click event is registered on a label item.\n   */\n  onClick(this: LegendElement<TType>, e: ChartEvent, legendItem: LegendItem, legend: LegendElement<TType>): void;\n  /**\n   * A callback that is called when a 'mousemove' event is registered on top of a label item\n   */\n  onHover(this: LegendElement<TType>, e: ChartEvent, legendItem: LegendItem, legend: LegendElement<TType>): void;\n  /**\n   * A callback that is called when a 'mousemove' event is registered outside of a previously hovered label item.\n   */\n  onLeave(this: LegendElement<TType>, e: ChartEvent, legendItem: LegendItem, legend: LegendElement<TType>): void;\n\n  labels: {\n    /**\n     * Width of colored box.\n     * @default 40\n     */\n    boxWidth: number;\n    /**\n     * Height of the coloured box.\n     * @default fontSize\n     */\n    boxHeight: number;\n    /**\n     * Padding between the color box and the text\n     * @default 1\n     */\n    boxPadding: number;\n    /**\n     * Color of label\n     * @see Defaults.color\n     */\n    color: Color;\n    /**\n     * Font of label\n     * @see Defaults.font\n     */\n    font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableChartContext>;\n    /**\n     * Padding between rows of colored boxes.\n     * @default 10\n     */\n    padding: number;\n    /**\n     * Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See Legend Item for details.\n     */\n    generateLabels(chart: Chart): LegendItem[];\n\n    /**\n     * Filters legend items out of the legend. Receives 2 parameters, a Legend Item and the chart data\n     */\n    filter(item: LegendItem, data: ChartData): boolean;\n\n    /**\n     * Sorts the legend items\n     */\n    sort(a: LegendItem, b: LegendItem, data: ChartData): number;\n\n    /**\n     * Override point style for the legend. Only applies if usePointStyle is true\n     */\n    pointStyle: PointStyle;\n\n    /**\n     * Text alignment\n     */\n    textAlign?: TextAlign;\n\n    /**\n     * Label style will match corresponding point style (size is based on the minimum value between boxWidth and font.size).\n     * @default false\n     */\n    usePointStyle: boolean;\n\n    /**\n     * Label borderRadius will match corresponding borderRadius.\n     * @default false\n     */\n    useBorderRadius: boolean;\n\n    /**\n     * Override the borderRadius to use.\n     * @default undefined\n     */\n    borderRadius: number;\n  };\n  /**\n   * true for rendering the legends from right to left.\n   */\n  rtl: boolean;\n  /**\n   * This will force the text direction 'rtl' or 'ltr' on the canvas for rendering the legend, regardless of the css specified on the canvas\n   * @default canvas' default\n   */\n  textDirection: string;\n\n  title: {\n    /**\n     * Is the legend title displayed.\n     * @default false\n     */\n    display: boolean;\n    /**\n     * Color of title\n     * @see Defaults.color\n     */\n    color: Color;\n    /**\n     * see Fonts\n     */\n    font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableChartContext>;\n    position: 'center' | 'start' | 'end';\n    padding?: number | ChartArea;\n    /**\n     * The string title.\n     */\n    text: string;\n  };\n}\n\nexport declare const SubTitle: Plugin;\nexport declare const Title: Plugin;\n\nexport interface TitleOptions {\n  /**\n   * Alignment of the title.\n   * @default 'center'\n   */\n  align: Align;\n  /**\n   * Is the title shown?\n   * @default false\n   */\n  display: boolean;\n  /**\n   * Position of title\n   * @default 'top'\n   */\n  position: 'top' | 'left' | 'bottom' | 'right';\n  /**\n   * Color of text\n   * @see Defaults.color\n   */\n  color: Color;\n  font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableChartContext>;\n\n  /**\n   * Marks that this box should take the full width/height of the canvas (moving other boxes). If set to `false`, places the box above/beside the\n   * chart area\n   * @default true\n   */\n  fullSize: boolean;\n  /**\n   *   Adds padding above and below the title text if a single number is specified. It is also possible to change top and bottom padding separately.\n   */\n  padding: number | { top: number; bottom: number };\n  /**\n   *   Title text to display. If specified as an array, text is rendered on multiple lines.\n   */\n  text: string | string[];\n}\n\nexport type TooltipXAlignment = 'left' | 'center' | 'right';\nexport type TooltipYAlignment = 'top' | 'center' | 'bottom';\nexport interface TooltipLabelStyle {\n  borderColor: Color;\n  backgroundColor: Color;\n\n  /**\n   * Width of border line\n   * @since 3.1.0\n   */\n  borderWidth?: number;\n\n  /**\n   * Border dash\n   * @since 3.1.0\n   */\n  borderDash?: [number, number];\n\n  /**\n   * Border dash offset\n   * @since 3.1.0\n   */\n  borderDashOffset?: number;\n\n  /**\n   * borderRadius\n   * @since 3.1.0\n   */\n  borderRadius?: number | BorderRadius;\n}\nexport interface TooltipModel<TType extends ChartType> extends Element<AnyObject, TooltipOptions<TType>> {\n  readonly chart: Chart<TType>;\n\n  // The items that we are rendering in the tooltip. See Tooltip Item Interface section\n  dataPoints: TooltipItem<TType>[];\n\n  // Positioning\n  xAlign: TooltipXAlignment;\n  yAlign: TooltipYAlignment;\n\n  // X and Y properties are the top left of the tooltip\n  x: number;\n  y: number;\n  width: number;\n  height: number;\n  // Where the tooltip points to\n  caretX: number;\n  caretY: number;\n\n  // Body\n  // The body lines that need to be rendered\n  // Each object contains 3 parameters\n  // before: string[] // lines of text before the line with the color square\n  // lines: string[]; // lines of text to render as the main item with color square\n  // after: string[]; // lines of text to render after the main lines\n  body: { before: string[]; lines: string[]; after: string[] }[];\n  // lines of text that appear after the title but before the body\n  beforeBody: string[];\n  // line of text that appear after the body and before the footer\n  afterBody: string[];\n\n  // Title\n  // lines of text that form the title\n  title: string[];\n\n  // Footer\n  // lines of text that form the footer\n  footer: string[];\n\n  // Styles to render for each item in body[]. This is the styling of the squares in the tooltip\n  labelColors: TooltipLabelStyle[];\n  labelTextColors: Color[];\n  labelPointStyles: { pointStyle: PointStyle; rotation: number }[];\n\n  // 0 opacity is a hidden tooltip\n  opacity: number;\n\n  // tooltip options\n  options: TooltipOptions<TType>;\n\n  getActiveElements(): ActiveElement[];\n  setActiveElements(active: ActiveDataPoint[], eventPosition: Point): void;\n}\n\nexport interface TooltipPosition extends Point {\n  xAlign?: TooltipXAlignment;\n  yAlign?: TooltipYAlignment;\n}\n\nexport type TooltipPositionerFunction<TType extends ChartType> = (\n  this: TooltipModel<TType>,\n  items: readonly ActiveElement[],\n  eventPosition: Point\n) => TooltipPosition | false;\n\nexport interface TooltipPositionerMap {\n  average: TooltipPositionerFunction<ChartType>;\n  nearest: TooltipPositionerFunction<ChartType>;\n}\n\nexport type TooltipPositioner = keyof TooltipPositionerMap;\n\nexport interface Tooltip extends Plugin {\n  readonly positioners: TooltipPositionerMap;\n}\n\nexport declare const Tooltip: Tooltip;\n\nexport interface TooltipCallbacks<\n  TType extends ChartType,\n  Model = TooltipModel<TType>,\n  Item = TooltipItem<TType>> {\n\n  beforeTitle(this: Model, tooltipItems: Item[]): string | string[] | void;\n  title(this: Model, tooltipItems: Item[]): string | string[] | void;\n  afterTitle(this: Model, tooltipItems: Item[]): string | string[] | void;\n\n  beforeBody(this: Model, tooltipItems: Item[]): string | string[] | void;\n  afterBody(this: Model, tooltipItems: Item[]): string | string[] | void;\n\n  beforeLabel(this: Model, tooltipItem: Item): string | string[] | void;\n  label(this: Model, tooltipItem: Item): string | string[] | void;\n  afterLabel(this: Model, tooltipItem: Item): string | string[] | void;\n\n  labelColor(this: Model, tooltipItem: Item): TooltipLabelStyle | void;\n  labelTextColor(this: Model, tooltipItem: Item): Color | void;\n  labelPointStyle(this: Model, tooltipItem: Item): { pointStyle: PointStyle; rotation: number } | void;\n\n  beforeFooter(this: Model, tooltipItems: Item[]): string | string[] | void;\n  footer(this: Model, tooltipItems: Item[]): string | string[] | void;\n  afterFooter(this: Model, tooltipItems: Item[]): string | string[] | void;\n}\n\nexport interface ExtendedPlugin<\n  TType extends ChartType,\n  O = AnyObject,\n  Model = TooltipModel<TType>> {\n  /**\n   * @desc Called before drawing the `tooltip`. If any plugin returns `false`,\n   * the tooltip drawing is cancelled until another `render` is triggered.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {Tooltip} args.tooltip - The tooltip.\n   * @param {object} options - The plugin options.\n   * @returns {boolean} `false` to cancel the chart tooltip drawing.\n   */\n  beforeTooltipDraw?(chart: Chart, args: { tooltip: Model, cancelable: true }, options: O): boolean | void;\n  /**\n   * @desc Called after drawing the `tooltip`. Note that this hook will not\n   * be called if the tooltip drawing has been previously cancelled.\n   * @param {Chart} chart - The chart instance.\n   * @param {object} args - The call arguments.\n   * @param {Tooltip} args.tooltip - The tooltip.\n   * @param {object} options - The plugin options.\n   */\n  afterTooltipDraw?(chart: Chart, args: { tooltip: Model }, options: O): void;\n}\n\nexport interface ScriptableTooltipContext<TType extends ChartType> {\n  chart: UnionToIntersection<Chart<TType>>;\n  tooltip: UnionToIntersection<TooltipModel<TType>>;\n  tooltipItems: TooltipItem<TType>[];\n}\n\nexport interface TooltipOptions<TType extends ChartType = ChartType> extends CoreInteractionOptions {\n  /**\n   * Are on-canvas tooltips enabled?\n   * @default true\n   */\n  enabled: Scriptable<boolean, ScriptableTooltipContext<TType>>;\n  /**\n   *   See external tooltip section.\n   */\n  external(this: TooltipModel<TType>, args: { chart: Chart; tooltip: TooltipModel<TType> }): void;\n  /**\n   * The mode for positioning the tooltip\n   */\n  position: Scriptable<TooltipPositioner, ScriptableTooltipContext<TType>>\n\n  /**\n   * Override the tooltip alignment calculations\n   */\n  xAlign: Scriptable<TooltipXAlignment, ScriptableTooltipContext<TType>>;\n  yAlign: Scriptable<TooltipYAlignment, ScriptableTooltipContext<TType>>;\n\n  /**\n   * Sort tooltip items.\n   */\n  itemSort: (a: TooltipItem<TType>, b: TooltipItem<TType>, data: ChartData) => number;\n\n  filter: (e: TooltipItem<TType>, index: number, array: TooltipItem<TType>[], data: ChartData) => boolean;\n\n  /**\n   * Background color of the tooltip.\n   * @default 'rgba(0, 0, 0, 0.8)'\n   */\n  backgroundColor: Scriptable<Color, ScriptableTooltipContext<TType>>;\n  /**\n   * Padding between the color box and the text.\n   * @default 1\n   */\n  boxPadding: number;\n  /**\n   * Color of title\n   * @default '#fff'\n   */\n  titleColor: Scriptable<Color, ScriptableTooltipContext<TType>>;\n  /**\n   * See Fonts\n   * @default {weight: 'bold'}\n   */\n  titleFont: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableTooltipContext<TType>>;\n  /**\n   * Spacing to add to top and bottom of each title line.\n   * @default 2\n   */\n  titleSpacing: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Margin to add on bottom of title section.\n   * @default 6\n   */\n  titleMarginBottom: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Horizontal alignment of the title text lines.\n   * @default 'left'\n   */\n  titleAlign: Scriptable<TextAlign, ScriptableTooltipContext<TType>>;\n  /**\n   * Spacing to add to top and bottom of each tooltip item.\n   * @default 2\n   */\n  bodySpacing: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Color of body\n   * @default '#fff'\n   */\n  bodyColor: Scriptable<Color, ScriptableTooltipContext<TType>>;\n  /**\n   * See Fonts.\n   * @default {}\n   */\n  bodyFont: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableTooltipContext<TType>>;\n  /**\n   * Horizontal alignment of the body text lines.\n   * @default 'left'\n   */\n  bodyAlign: Scriptable<TextAlign, ScriptableTooltipContext<TType>>;\n  /**\n   * Spacing to add to top and bottom of each footer line.\n   * @default 2\n   */\n  footerSpacing: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Margin to add before drawing the footer.\n   * @default 6\n   */\n  footerMarginTop: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Color of footer\n   * @default '#fff'\n   */\n  footerColor: Scriptable<Color, ScriptableTooltipContext<TType>>;\n  /**\n   * See Fonts\n   * @default {weight: 'bold'}\n   */\n  footerFont: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableTooltipContext<TType>>;\n  /**\n   * Horizontal alignment of the footer text lines.\n   * @default 'left'\n   */\n  footerAlign: Scriptable<TextAlign, ScriptableTooltipContext<TType>>;\n  /**\n   * Padding to add to the tooltip\n   * @default 6\n   */\n  padding: Scriptable<Padding, ScriptableTooltipContext<TType>>;\n  /**\n   * Extra distance to move the end of the tooltip arrow away from the tooltip point.\n   * @default 2\n   */\n  caretPadding: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Size, in px, of the tooltip arrow.\n   * @default 5\n   */\n  caretSize: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Radius of tooltip corner curves.\n   * @default 6\n   */\n  cornerRadius: Scriptable<number | BorderRadius, ScriptableTooltipContext<TType>>;\n  /**\n   * Color to draw behind the colored boxes when multiple items are in the tooltip.\n   * @default '#fff'\n   */\n  multiKeyBackground: Scriptable<Color, ScriptableTooltipContext<TType>>;\n  /**\n   * If true, color boxes are shown in the tooltip.\n   * @default true\n   */\n  displayColors: Scriptable<boolean, ScriptableTooltipContext<TType>>;\n  /**\n   * Width of the color box if displayColors is true.\n   * @default bodyFont.size\n   */\n  boxWidth: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Height of the color box if displayColors is true.\n   * @default bodyFont.size\n   */\n  boxHeight: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * Use the corresponding point style (from dataset options) instead of color boxes, ex: star, triangle etc. (size is based on the minimum value between boxWidth and boxHeight)\n   * @default false\n   */\n  usePointStyle: Scriptable<boolean, ScriptableTooltipContext<TType>>;\n  /**\n   * Color of the border.\n   * @default 'rgba(0, 0, 0, 0)'\n   */\n  borderColor: Scriptable<Color, ScriptableTooltipContext<TType>>;\n  /**\n   * Size of the border.\n   * @default 0\n   */\n  borderWidth: Scriptable<number, ScriptableTooltipContext<TType>>;\n  /**\n   * true for rendering the legends from right to left.\n   */\n  rtl: Scriptable<boolean, ScriptableTooltipContext<TType>>;\n\n  /**\n   * This will force the text direction 'rtl' or 'ltr on the canvas for rendering the tooltips, regardless of the css specified on the canvas\n   * @default canvas's default\n   */\n  textDirection: Scriptable<string, ScriptableTooltipContext<TType>>;\n\n  animation: AnimationSpec<TType> | false;\n  animations: AnimationsSpec<TType> | false;\n  callbacks: TooltipCallbacks<TType>;\n}\n\nexport interface TooltipItem<TType extends ChartType> {\n  /**\n   * The chart the tooltip is being shown on\n   */\n  chart: Chart;\n\n  /**\n   * Label for the tooltip\n   */\n  label: string;\n\n  /**\n   * Parsed data values for the given `dataIndex` and `datasetIndex`\n   */\n  parsed: UnionToIntersection<ParsedDataType<TType>>;\n\n  /**\n   * Raw data values for the given `dataIndex` and `datasetIndex`\n   */\n  raw: unknown;\n\n  /**\n   * Formatted value for the tooltip\n   */\n  formattedValue: string;\n\n  /**\n   * The dataset the item comes from\n   */\n  dataset: UnionToIntersection<ChartDataset<TType>>;\n\n  /**\n   * Index of the dataset the item comes from\n   */\n  datasetIndex: number;\n\n  /**\n   * Index of this data item in the dataset\n   */\n  dataIndex: number;\n\n  /**\n   * The chart element (point, arc, bar, etc.) for this tooltip item\n   */\n  element: Element;\n}\n\nexport interface PluginOptionsByType<TType extends ChartType> {\n  colors: ColorsPluginOptions;\n  decimation: DecimationOptions;\n  filler: FillerOptions;\n  legend: LegendOptions<TType>;\n  subtitle: TitleOptions;\n  title: TitleOptions;\n  tooltip: TooltipOptions<TType>;\n}\nexport interface PluginChartOptions<TType extends ChartType> {\n  plugins: PluginOptionsByType<TType>;\n}\n\nexport interface BorderOptions {\n  /**\n   * @default true\n   */\n  display: boolean\n  /**\n   * @default []\n   */\n  dash: Scriptable<number[], ScriptableScaleContext>;\n  /**\n   * @default 0\n   */\n  dashOffset: Scriptable<number, ScriptableScaleContext>;\n  color: Color;\n  width: number;\n  z: number;\n}\n\nexport interface GridLineOptions {\n  /**\n   * @default true\n   */\n  display: boolean;\n  /**\n   * @default false\n   */\n  circular: boolean;\n  /**\n   * @default 'rgba(0, 0, 0, 0.1)'\n   */\n  color: ScriptableAndArray<Color, ScriptableScaleContext>;\n  /**\n   * @default 1\n   */\n  lineWidth: ScriptableAndArray<number, ScriptableScaleContext>;\n  /**\n   * @default true\n   */\n  drawOnChartArea: boolean;\n  /**\n   * @default true\n   */\n  drawTicks: boolean;\n  /**\n   * @default []\n   */\n  tickBorderDash: number[];\n  /**\n   * @default 0\n   */\n  tickBorderDashOffset: Scriptable<number, ScriptableScaleContext>;\n  /**\n   * @default 'rgba(0, 0, 0, 0.1)'\n   */\n  tickColor: ScriptableAndArray<Color, ScriptableScaleContext>;\n  /**\n   * @default 10\n   */\n  tickLength: number;\n  /**\n   * @default 1\n   */\n  tickWidth: number;\n  /**\n   * @default false\n   */\n  offset: boolean;\n  /**\n   * @default 0\n   */\n  z: number;\n}\n\nexport interface TickOptions {\n  /**\n   * Color of label backdrops.\n   * @default 'rgba(255, 255, 255, 0.75)'\n   */\n  backdropColor: Scriptable<Color, ScriptableScaleContext>;\n  /**\n   * Padding of tick backdrop.\n   * @default 2\n   */\n  backdropPadding: number | ChartArea;\n\n  /**\n   * Returns the string representation of the tick value as it should be displayed on the chart. See callback.\n   */\n  callback: (this: Scale, tickValue: number | string, index: number, ticks: Tick[]) => string | string[] | number | number[] | null | undefined;\n  /**\n   * If true, show tick labels.\n   * @default true\n   */\n  display: boolean;\n  /**\n   * Color of tick\n   * @see Defaults.color\n   */\n  color: ScriptableAndArray<Color, ScriptableScaleContext>;\n  /**\n   * see Fonts\n   */\n  font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableScaleContext>;\n  /**\n   * Sets the offset of the tick labels from the axis\n   */\n  padding: number;\n  /**\n   * If true, draw a background behind the tick labels.\n   * @default false\n   */\n  showLabelBackdrop: Scriptable<boolean, ScriptableScaleContext>;\n  /**\n   * The color of the stroke around the text.\n   * @default undefined\n   */\n  textStrokeColor: Scriptable<Color, ScriptableScaleContext>;\n  /**\n   * Stroke width around the text.\n   * @default 0\n   */\n  textStrokeWidth: Scriptable<number, ScriptableScaleContext>;\n  /**\n   * z-index of tick layer. Useful when ticks are drawn on chart area. Values <= 0 are drawn under datasets, > 0 on top.\n   * @default 0\n   */\n  z: number;\n\n  major: {\n    /**\n     * If true, major ticks are generated. A major tick will affect autoskipping and major will be defined on ticks in the scriptable options context.\n     * @default false\n     */\n    enabled: boolean;\n  };\n}\n\nexport type CartesianTickOptions = TickOptions & {\n  /**\n   * The number of ticks to examine when deciding how many labels will fit. Setting a smaller value will be faster, but may be less accurate when there is large variability in label length.\n   * @default ticks.length\n   */\n  sampleSize: number;\n  /**\n   * The label alignment\n   * @default 'center'\n   */\n  align: Align | 'inner';\n  /**\n   *   If true, automatically calculates how many labels can be shown and hides labels accordingly. Labels will be rotated up to maxRotation before skipping any. Turn autoSkip off to show all labels no matter what.\n   * @default true\n   */\n  autoSkip: boolean;\n  /**\n   * Padding between the ticks on the horizontal axis when autoSkip is enabled.\n   * @default 0\n   */\n  autoSkipPadding: number;\n\n  /**\n   * How is the label positioned perpendicular to the axis direction.\n   * This only applies when the rotation is 0 and the axis position is one of \"top\", \"left\", \"right\", or \"bottom\"\n   * @default 'near'\n   */\n  crossAlign: 'near' | 'center' | 'far';\n\n  /**\n   * Should the defined `min` and `max` values be presented as ticks even if they are not \"nice\".\n   * @default: true\n   */\n  includeBounds: boolean;\n\n  /**\n   * Distance in pixels to offset the label from the centre point of the tick (in the x direction for the x axis, and the y direction for the y axis). Note: this can cause labels at the edges to be cropped by the edge of the canvas\n   * @default 0\n   */\n  labelOffset: number;\n\n  /**\n   * Minimum rotation for tick labels. Note: Only applicable to horizontal scales.\n   * @default 0\n   */\n  minRotation: number;\n  /**\n   * Maximum rotation for tick labels when rotating to condense labels. Note: Rotation doesn't occur until necessary. Note: Only applicable to horizontal scales.\n   * @default 50\n   */\n  maxRotation: number;\n  /**\n   * Flips tick labels around axis, displaying the labels inside the chart instead of outside. Note: Only applicable to vertical scales.\n   * @default false\n   */\n  mirror: boolean;\n  /**\n   *   Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction.\n   * @default 0\n   */\n  padding: number;\n  /**\n   * Maximum number of ticks and gridlines to show.\n   * @default 11\n   */\n  maxTicksLimit: number;\n}\n\nexport interface ScriptableCartesianScaleContext {\n  scale: keyof CartesianScaleTypeRegistry;\n  type: string;\n}\n\nexport interface ScriptableChartContext {\n  chart: Chart;\n  type: string;\n}\n\nexport interface CartesianScaleOptions extends CoreScaleOptions {\n  /**\n   * Scale boundary strategy (bypassed by min/max time options)\n   * - `data`: make sure data are fully visible, ticks outside are removed\n   * - `ticks`: make sure ticks are fully visible, data outside are truncated\n   * @since 2.7.0\n   * @default 'ticks'\n   */\n  bounds: 'ticks' | 'data';\n\n  /**\n   * Position of the axis.\n   */\n  position: 'left' | 'top' | 'right' | 'bottom' | 'center' | { [scale: string]: number };\n\n  /**\n   * Stack group. Axes at the same `position` with same `stack` are stacked.\n   */\n  stack?: string;\n\n  /**\n   * Weight of the scale in stack group. Used to determine the amount of allocated space for the scale within the group.\n   * @default 1\n   */\n  stackWeight?: number;\n\n  /**\n   *   Which type of axis this is. Possible values are: 'x', 'y', 'r'. If not set, this is inferred from the first character of the ID which should be 'x', 'y' or 'r'.\n   */\n  axis: 'x' | 'y' | 'r';\n\n  /**\n   * User defined minimum value for the scale, overrides minimum value from data.\n   */\n  min: number;\n\n  /**\n   * User defined maximum value for the scale, overrides maximum value from data.\n   */\n  max: number;\n\n  /**\n   *   If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true for a bar chart by default.\n   * @default false\n   */\n  offset: boolean;\n\n  grid: Partial<GridLineOptions>;\n\n  border: BorderOptions;\n\n  /** Options for the scale title. */\n  title: {\n    /** If true, displays the axis title. */\n    display: boolean;\n    /** Alignment of the axis title. */\n    align: Align;\n    /** The text for the title, e.g. \"# of People\" or \"Response Choices\". */\n    text: string | string[];\n    /** Color of the axis label. */\n    color: Color;\n    /** Information about the axis title font. */\n    font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableCartesianScaleContext>;\n    /** Padding to apply around scale labels. */\n    padding: number | {\n      /** Padding on the (relative) top side of this axis label. */\n      top: number;\n      /** Padding on the (relative) bottom side of this axis label. */\n      bottom: number;\n      /** This is a shorthand for defining top/bottom to the same values. */\n      y: number;\n    };\n  };\n\n  /**\n   *   If true, data will be comprised between datasets of data\n   * @default false\n   */\n  stacked?: boolean | 'single';\n\n  ticks: CartesianTickOptions;\n}\n\nexport type CategoryScaleOptions = Omit<CartesianScaleOptions, 'min' | 'max'> & {\n  min: string | number;\n  max: string | number;\n  labels: string[] | string[][];\n};\n\nexport type CategoryScale<O extends CategoryScaleOptions = CategoryScaleOptions> = Scale<O>\nexport declare const CategoryScale: ChartComponent & {\n  prototype: CategoryScale;\n  new <O extends CategoryScaleOptions = CategoryScaleOptions>(cfg: AnyObject): CategoryScale<O>;\n};\n\nexport type LinearScaleOptions = CartesianScaleOptions & {\n\n  /**\n   *  if true, scale will include 0 if it is not already included.\n   * @default true\n   */\n  beginAtZero: boolean;\n  /**\n   * Adjustment used when calculating the maximum data value.\n   */\n  suggestedMin?: number;\n  /**\n   * Adjustment used when calculating the minimum data value.\n   */\n  suggestedMax?: number;\n  /**\n  * Percentage (string ending with %) or amount (number) for added room in the scale range above and below data.\n  */\n  grace?: string | number;\n\n  ticks: {\n    /**\n     * The Intl.NumberFormat options used by the default label formatter\n     */\n    format: Intl.NumberFormatOptions;\n\n    /**\n     * if defined and stepSize is not specified, the step size will be rounded to this many decimal places.\n     */\n    precision: number;\n\n    /**\n     * User defined fixed step size for the scale\n     */\n    stepSize: number;\n\n    /**\n     * User defined count of ticks\n     */\n    count: number;\n  };\n};\n\nexport type LinearScale<O extends LinearScaleOptions = LinearScaleOptions> = Scale<O>\nexport declare const LinearScale: ChartComponent & {\n  prototype: LinearScale;\n  new <O extends LinearScaleOptions = LinearScaleOptions>(cfg: AnyObject): LinearScale<O>;\n};\n\nexport type LogarithmicScaleOptions = CartesianScaleOptions & {\n  /**\n   * Adjustment used when calculating the maximum data value.\n   */\n  suggestedMin?: number;\n  /**\n   * Adjustment used when calculating the minimum data value.\n   */\n  suggestedMax?: number;\n\n  ticks: {\n    /**\n     * The Intl.NumberFormat options used by the default label formatter\n     */\n    format: Intl.NumberFormatOptions;\n  };\n};\n\nexport type LogarithmicScale<O extends LogarithmicScaleOptions = LogarithmicScaleOptions> = Scale<O>\nexport declare const LogarithmicScale: ChartComponent & {\n  prototype: LogarithmicScale;\n  new <O extends LogarithmicScaleOptions = LogarithmicScaleOptions>(cfg: AnyObject): LogarithmicScale<O>;\n};\n\nexport type TimeScaleOptions = Omit<CartesianScaleOptions, 'min' | 'max'> & {\n  min: string | number;\n  max: string | number;\n  suggestedMin: string | number;\n  suggestedMax: string | number;\n  /**\n   * Scale boundary strategy (bypassed by min/max time options)\n   * - `data`: make sure data are fully visible, ticks outside are removed\n   * - `ticks`: make sure ticks are fully visible, data outside are truncated\n   * @since 2.7.0\n   * @default 'data'\n   */\n  bounds: 'ticks' | 'data';\n\n  /**\n   * If true, bar chart offsets are computed with skipped tick sizes\n   * @since 3.8.0\n   * @default false\n   */\n  offsetAfterAutoskip: boolean;\n\n  /**\n   * options for creating a new adapter instance\n   */\n  adapters: {\n    date: unknown;\n  };\n\n  time: {\n    /**\n     * Custom parser for dates.\n     */\n    parser: string | ((v: unknown) => number);\n    /**\n     * If defined, dates will be rounded to the start of this unit. See Time Units below for the allowed units.\n     */\n    round: false | TimeUnit;\n    /**\n     * If boolean and true and the unit is set to 'week', then the first day of the week will be Monday. Otherwise, it will be Sunday.\n     * If `number`, the index of the first day of the week (0 - Sunday, 6 - Saturday).\n     * @default false\n     */\n    isoWeekday: boolean | number;\n    /**\n     * Sets how different time units are displayed.\n     */\n    displayFormats: {\n      [key: string]: string;\n    };\n    /**\n     * The format string to use for the tooltip.\n     */\n    tooltipFormat: string;\n    /**\n     * If defined, will force the unit to be a certain type. See Time Units section below for details.\n     * @default false\n     */\n    unit: false | TimeUnit;\n    /**\n     * The minimum display format to be used for a time unit.\n     * @default 'millisecond'\n     */\n    minUnit: TimeUnit;\n  };\n\n  ticks: {\n    /**\n     * Ticks generation input values:\n     * - 'auto': generates \"optimal\" ticks based on scale size and time options.\n     * - 'data': generates ticks from data (including labels from data `{t|x|y}` objects).\n     * - 'labels': generates ticks from user given `data.labels` values ONLY.\n     * @see https://github.com/chartjs/Chart.js/pull/4507\n     * @since 2.7.0\n     * @default 'auto'\n     */\n    source: 'labels' | 'auto' | 'data';\n    /**\n     * The number of units between grid lines.\n     * @default 1\n     */\n    stepSize: number;\n  };\n};\n\nexport interface TimeScale<O extends TimeScaleOptions = TimeScaleOptions> extends Scale<O> {\n  format(value: number, format?: string): string;\n  getDataTimestamps(): number[];\n  getLabelTimestamps(): string[];\n  normalize(values: number[]): number[];\n}\n\nexport declare const TimeScale: ChartComponent & {\n  prototype: TimeScale;\n  new <O extends TimeScaleOptions = TimeScaleOptions>(cfg: AnyObject): TimeScale<O>;\n};\n\nexport type TimeSeriesScale<O extends TimeScaleOptions = TimeScaleOptions> = TimeScale<O>\nexport declare const TimeSeriesScale: ChartComponent & {\n  prototype: TimeSeriesScale;\n  new <O extends TimeScaleOptions = TimeScaleOptions>(cfg: AnyObject): TimeSeriesScale<O>;\n};\n\nexport type RadialTickOptions = TickOptions & {\n  /**\n   * The Intl.NumberFormat options used by the default label formatter\n   */\n  format: Intl.NumberFormatOptions;\n\n  /**\n   * Maximum number of ticks and gridlines to show.\n   * @default 11\n   */\n  maxTicksLimit: number;\n\n  /**\n   * if defined and stepSize is not specified, the step size will be rounded to this many decimal places.\n   */\n  precision: number;\n\n  /**\n   * User defined fixed step size for the scale.\n   */\n  stepSize: number;\n\n  /**\n   * User defined number of ticks\n   */\n  count: number;\n}\n\nexport type RadialLinearScaleOptions = CoreScaleOptions & {\n  backgroundColor: Color;\n\n  animate: boolean;\n\n  startAngle: number;\n\n  angleLines: {\n    /**\n     * if true, angle lines are shown.\n     * @default true\n     */\n    display: boolean;\n    /**\n     * Color of angled lines.\n     * @default 'rgba(0, 0, 0, 0.1)'\n     */\n    color: Scriptable<Color, ScriptableScaleContext>;\n    /**\n     * Width of angled lines.\n     * @default 1\n     */\n    lineWidth: Scriptable<number, ScriptableScaleContext>;\n    /**\n     * Length and spacing of dashes on angled lines. See MDN.\n     * @default []\n     */\n    borderDash: Scriptable<number[], ScriptableScaleContext>;\n    /**\n     * Offset for line dashes. See MDN.\n     * @default 0\n     */\n    borderDashOffset: Scriptable<number, ScriptableScaleContext>;\n  };\n\n  /**\n   * if true, scale will include 0 if it is not already included.\n   * @default false\n   */\n  beginAtZero: boolean;\n\n  grid: Partial<GridLineOptions>;\n\n  /**\n   * User defined minimum number for the scale, overrides minimum value from data.\n   */\n  min: number;\n  /**\n   * User defined maximum number for the scale, overrides maximum value from data.\n   */\n  max: number;\n\n  pointLabels: {\n    /**\n     * Background color of the point label.\n     * @default undefined\n     */\n    backdropColor: Scriptable<Color, ScriptableScalePointLabelContext>;\n    /**\n     * Padding of label backdrop.\n     * @default 2\n     */\n    backdropPadding: Scriptable<number | ChartArea, ScriptableScalePointLabelContext>;\n\n    /**\n     * Border radius\n     * @default 0\n     * @since 3.8.0\n     */\n    borderRadius: Scriptable<number | BorderRadius, ScriptableScalePointLabelContext>;\n\n    /**\n     * if true, point labels are shown.\n     * @default true\n     */\n    display: boolean;\n    /**\n     * Color of label\n     * @see Defaults.color\n     */\n    color: Scriptable<Color, ScriptableScalePointLabelContext>;\n    /**\n     */\n    font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableScalePointLabelContext>;\n\n    /**\n     * Callback function to transform data labels to point labels. The default implementation simply returns the current string.\n     */\n    callback: (label: string, index: number) => string | string[] | number | number[];\n\n    /**\n     * Padding around the pointLabels\n     * @default 5\n     */\n    padding: Scriptable<number, ScriptableScalePointLabelContext>;\n\n    /**\n     * if true, point labels are centered.\n     * @default false\n     */\n    centerPointLabels: boolean;\n  };\n\n  /**\n   * Adjustment used when calculating the maximum data value.\n   */\n  suggestedMax: number;\n  /**\n   * Adjustment used when calculating the minimum data value.\n   */\n  suggestedMin: number;\n\n  ticks: RadialTickOptions;\n};\n\nexport interface RadialLinearScale<O extends RadialLinearScaleOptions = RadialLinearScaleOptions> extends Scale<O> {\n  setCenterPoint(leftMovement: number, rightMovement: number, topMovement: number, bottomMovement: number): void;\n  getIndexAngle(index: number): number;\n  getDistanceFromCenterForValue(value: number): number;\n  getValueForDistanceFromCenter(distance: number): number;\n  getPointPosition(index: number, distanceFromCenter: number): { x: number; y: number; angle: number };\n  getPointPositionForValue(index: number, value: number): { x: number; y: number; angle: number };\n  getPointLabelPosition(index: number): ChartArea;\n  getBasePosition(index: number): { x: number; y: number; angle: number };\n}\nexport declare const RadialLinearScale: ChartComponent & {\n  prototype: RadialLinearScale;\n  new <O extends RadialLinearScaleOptions = RadialLinearScaleOptions>(cfg: AnyObject): RadialLinearScale<O>;\n};\n\nexport interface CartesianScaleTypeRegistry {\n  linear: {\n    options: LinearScaleOptions;\n  };\n  logarithmic: {\n    options: LogarithmicScaleOptions;\n  };\n  category: {\n    options: CategoryScaleOptions;\n  };\n  time: {\n    options: TimeScaleOptions;\n  };\n  timeseries: {\n    options: TimeScaleOptions;\n  };\n}\n\nexport interface RadialScaleTypeRegistry {\n  radialLinear: {\n    options: RadialLinearScaleOptions;\n  };\n}\n\nexport interface ScaleTypeRegistry extends CartesianScaleTypeRegistry, RadialScaleTypeRegistry {\n}\n\nexport type ScaleType = keyof ScaleTypeRegistry;\n\nexport interface CartesianParsedData extends Point {\n  // Only specified when stacked bars are enabled\n  _stacks?: {\n    // Key is the stack ID which is generally the axis ID\n    [key: string]: {\n      // Inner key is the datasetIndex\n      [key: number]: number;\n    }\n  }\n}\n\ninterface BarParsedData extends CartesianParsedData {\n  // Only specified if floating bars are show\n  _custom?: {\n    barStart: number;\n    barEnd: number;\n    start: number;\n    end: number;\n    min: number;\n    max: number;\n  }\n}\n\ninterface BubbleParsedData extends CartesianParsedData {\n  // The bubble radius value\n  _custom: number;\n}\n\ninterface RadialParsedData {\n  r: number;\n}\n\nexport interface ChartTypeRegistry {\n  bar: {\n    chartOptions: BarControllerChartOptions;\n    datasetOptions: BarControllerDatasetOptions;\n    defaultDataPoint: number | [number, number] | null;\n    metaExtensions: {};\n    parsedDataType: BarParsedData,\n    scales: keyof CartesianScaleTypeRegistry;\n  };\n  line: {\n    chartOptions: LineControllerChartOptions;\n    datasetOptions: LineControllerDatasetOptions & FillerControllerDatasetOptions;\n    defaultDataPoint: ScatterDataPoint | number | null;\n    metaExtensions: {};\n    parsedDataType: CartesianParsedData;\n    scales: keyof CartesianScaleTypeRegistry;\n  };\n  scatter: {\n    chartOptions: ScatterControllerChartOptions;\n    datasetOptions: ScatterControllerDatasetOptions;\n    defaultDataPoint: ScatterDataPoint | number | null;\n    metaExtensions: {};\n    parsedDataType: CartesianParsedData;\n    scales: keyof CartesianScaleTypeRegistry;\n  };\n  bubble: {\n    chartOptions: unknown;\n    datasetOptions: BubbleControllerDatasetOptions;\n    defaultDataPoint: BubbleDataPoint;\n    metaExtensions: {};\n    parsedDataType: BubbleParsedData;\n    scales: keyof CartesianScaleTypeRegistry;\n  };\n  pie: {\n    chartOptions: PieControllerChartOptions;\n    datasetOptions: PieControllerDatasetOptions;\n    defaultDataPoint: PieDataPoint;\n    metaExtensions: PieMetaExtensions;\n    parsedDataType: number;\n    scales: keyof CartesianScaleTypeRegistry;\n  };\n  doughnut: {\n    chartOptions: DoughnutControllerChartOptions;\n    datasetOptions: DoughnutControllerDatasetOptions;\n    defaultDataPoint: DoughnutDataPoint;\n    metaExtensions: DoughnutMetaExtensions;\n    parsedDataType: number;\n    scales: keyof CartesianScaleTypeRegistry;\n  };\n  polarArea: {\n    chartOptions: PolarAreaControllerChartOptions;\n    datasetOptions: PolarAreaControllerDatasetOptions;\n    defaultDataPoint: number;\n    metaExtensions: {};\n    parsedDataType: RadialParsedData;\n    scales: keyof RadialScaleTypeRegistry;\n  };\n  radar: {\n    chartOptions: RadarControllerChartOptions;\n    datasetOptions: RadarControllerDatasetOptions & FillerControllerDatasetOptions;\n    defaultDataPoint: number | null;\n    metaExtensions: {};\n    parsedDataType: RadialParsedData;\n    scales: keyof RadialScaleTypeRegistry;\n  };\n}\n\nexport type ChartType = keyof ChartTypeRegistry;\n\nexport type ScaleOptionsByType<TScale extends ScaleType = ScaleType> =\n  { [key in ScaleType]: { type: key } & ScaleTypeRegistry[key]['options'] }[TScale]\n;\n\n// Convenience alias for creating and manipulating scale options in user code\nexport type ScaleOptions<TScale extends ScaleType = ScaleType> = DeepPartial<ScaleOptionsByType<TScale>>;\n\nexport type DatasetChartOptions<TType extends ChartType = ChartType> = {\n  [key in TType]: {\n    datasets: ChartTypeRegistry[key]['datasetOptions'];\n  };\n};\n\nexport type ScaleChartOptions<TType extends ChartType = ChartType> = {\n  scales: {\n    [key: string]: ScaleOptionsByType<ChartTypeRegistry[TType]['scales']>;\n  };\n};\n\nexport type ChartOptions<TType extends ChartType = ChartType> = DeepPartial<\nCoreChartOptions<TType> &\nElementChartOptions<TType> &\nPluginChartOptions<TType> &\nDatasetChartOptions<TType> &\nScaleChartOptions<TType> &\nChartTypeRegistry[TType]['chartOptions']\n>;\n\nexport type DefaultDataPoint<TType extends ChartType> = DistributiveArray<ChartTypeRegistry[TType]['defaultDataPoint']>;\n\nexport type ParsedDataType<TType extends ChartType = ChartType> = ChartTypeRegistry[TType]['parsedDataType'];\n\nexport interface ChartDatasetProperties<TType extends ChartType, TData> {\n  type?: TType;\n  data: TData;\n}\n\nexport interface ChartDatasetPropertiesCustomTypesPerDataset<TType extends ChartType, TData> {\n  type: TType;\n  data: TData;\n}\n\nexport type ChartDataset<\n  TType extends ChartType = ChartType,\n  TData = DefaultDataPoint<TType>\n> = DeepPartial<\n{ [key in ChartType]: { type: key } & ChartTypeRegistry[key]['datasetOptions'] }[TType]\n> & ChartDatasetProperties<TType, TData>;\n\nexport type ChartDatasetCustomTypesPerDataset<\n  TType extends ChartType = ChartType,\n  TData = DefaultDataPoint<TType>\n> = DeepPartial<\n{ [key in ChartType]: { type: key } & ChartTypeRegistry[key]['datasetOptions'] }[TType]\n> & ChartDatasetPropertiesCustomTypesPerDataset<TType, TData>;\n\n/**\n * TData represents the data point type. If unspecified, a default is provided\n *   based on the chart type.\n * TLabel represents the label type\n */\nexport interface ChartData<\n  TType extends ChartType = ChartType,\n  TData = DefaultDataPoint<TType>,\n  TLabel = unknown\n> {\n  labels?: TLabel[];\n  datasets: ChartDataset<TType, TData>[];\n}\n\nexport interface ChartDataCustomTypesPerDataset<\n  TType extends ChartType = ChartType,\n  TData = DefaultDataPoint<TType>,\n  TLabel = unknown\n> {\n  labels?: TLabel[];\n  datasets: ChartDatasetCustomTypesPerDataset<TType, TData>[];\n}\n\nexport interface ChartConfiguration<\n  TType extends ChartType = ChartType,\n  TData = DefaultDataPoint<TType>,\n  TLabel = unknown\n> {\n  type: TType;\n  data: ChartData<TType, TData, TLabel>;\n  options?: ChartOptions<TType>;\n  plugins?: Plugin<TType>[];\n}\n\nexport interface ChartConfigurationCustomTypesPerDataset<\n  TType extends ChartType = ChartType,\n  TData = DefaultDataPoint<TType>,\n  TLabel = unknown\n> {\n  data: ChartDataCustomTypesPerDataset<TType, TData, TLabel>;\n  options?: ChartOptions<TType>;\n  plugins?: Plugin<TType>[];\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/layout.d.ts",
    "content": "import {ChartArea} from './geometric.js';\n\nexport type LayoutPosition = 'left' | 'top' | 'right' | 'bottom' | 'center' | 'chartArea' | {[scaleId: string]: number};\n\nexport interface LayoutItem {\n  /**\n   * The position of the item in the chart layout. Possible values are\n   */\n  position: LayoutPosition;\n  /**\n   * The weight used to sort the item. Higher weights are further away from the chart area\n   */\n  weight: number;\n  /**\n   * if true, and the item is horizontal, then push vertical boxes down\n   */\n  fullSize: boolean;\n  /**\n   * Width of item. Must be valid after update()\n   */\n  width: number;\n  /**\n   * Height of item. Must be valid after update()\n   */\n  height: number;\n  /**\n   * Left edge of the item. Set by layout system and cannot be used in update\n   */\n  left: number;\n  /**\n   * Top edge of the item. Set by layout system and cannot be used in update\n   */\n  top: number;\n  /**\n   * Right edge of the item. Set by layout system and cannot be used in update\n   */\n  right: number;\n  /**\n   * Bottom edge of the item. Set by layout system and cannot be used in update\n   */\n  bottom: number;\n\n  /**\n   * Called before the layout process starts\n   */\n  beforeLayout?(): void;\n  /**\n   * Draws the element\n   */\n  draw(chartArea: ChartArea): void;\n  /**\n   * Returns an object with padding on the edges\n   */\n  getPadding?(): ChartArea;\n  /**\n   * returns true if the layout item is horizontal (ie. top or bottom)\n   */\n  isHorizontal(): boolean;\n  /**\n   * Takes two parameters: width and height.\n   * @param width\n   * @param height\n   */\n  update(width: number, height: number, margins?: ChartArea): void;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types/utils.d.ts",
    "content": "/* eslint-disable @typescript-eslint/ban-types */\n\n// DeepPartial implementation taken from the utility-types NPM package, which is\n// Copyright (c) 2016 Piotr Witek <piotrek.witek@gmail.com> (http://piotrwitek.github.io)\n// and used under the terms of the MIT license\nexport type DeepPartial<T> = T extends Function\n  ? T\n  : T extends Array<infer U>\n    ? _DeepPartialArray<U>\n    : T extends object\n      ? _DeepPartialObject<T>\n      : T | undefined;\n\ntype _DeepPartialArray<T> = Array<DeepPartial<T>>\ntype _DeepPartialObject<T> = { [P in keyof T]?: DeepPartial<T[P]> };\n\nexport type DistributiveArray<T> = [T] extends [unknown] ? Array<T> : never\n\n// https://stackoverflow.com/a/50375286\nexport type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/chart.js/types.d.ts",
    "content": "/**\n * Temporary entry point of the types at the time of the transition.\n * After transition done need to remove it in favor of index.ts\n */\nexport * from './index.js';\n/**\n * Explicitly re-exporting to resolve the ambiguity.\n */\nexport { BarController, BubbleController, DoughnutController, LineController, PieController, PolarAreaController, RadarController, ScatterController, Animation, Animations, Chart, DatasetController, Interaction, Scale, Ticks, defaults, layouts, registry, ArcElement, BarElement, LineElement, PointElement, BasePlatform, BasicPlatform, DomPlatform, Decimation, Filler, Legend, SubTitle, Title, Tooltip, CategoryScale, LinearScale, LogarithmicScale, RadialLinearScale, TimeScale, TimeSeriesScale, registerables } from './types/index.js';\nexport * from './types/index.js';\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/clipboard/clipboard.js",
    "content": "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT © Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n  try {\n    return document.execCommand(type);\n  } catch (err) {\n    return false;\n  }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n  var selectedText = select_default()(target);\n  command('cut');\n  return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n  var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n  var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n  fakeElement.style.fontSize = '12pt'; // Reset box model\n\n  fakeElement.style.border = '0';\n  fakeElement.style.padding = '0';\n  fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n  fakeElement.style.position = 'absolute';\n  fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n  var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n  fakeElement.style.top = \"\".concat(yPosition, \"px\");\n  fakeElement.setAttribute('readonly', '');\n  fakeElement.value = value;\n  return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n  var fakeElement = createFakeElement(value);\n  options.container.appendChild(fakeElement);\n  var selectedText = select_default()(fakeElement);\n  command('copy');\n  fakeElement.remove();\n  return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n    container: document.body\n  };\n  var selectedText = '';\n\n  if (typeof target === 'string') {\n    selectedText = fakeCopyAction(target, options);\n  } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n    // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n    selectedText = fakeCopyAction(target.value, options);\n  } else {\n    selectedText = select_default()(target);\n    command('copy');\n  }\n\n  return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n  // Defines base properties passed from constructor.\n  var _options$action = options.action,\n      action = _options$action === void 0 ? 'copy' : _options$action,\n      container = options.container,\n      target = options.target,\n      text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n  if (action !== 'copy' && action !== 'cut') {\n    throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n  } // Sets the `target` property using an element that will be have its content copied.\n\n\n  if (target !== undefined) {\n    if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n      if (action === 'copy' && target.hasAttribute('disabled')) {\n        throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n      }\n\n      if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n        throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n      }\n    } else {\n      throw new Error('Invalid \"target\" value, use a valid Element');\n    }\n  } // Define selection strategy based on `text` property.\n\n\n  if (text) {\n    return actions_copy(text, {\n      container: container\n    });\n  } // Defines which selection strategy based on `target` property.\n\n\n  if (target) {\n    return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n      container: container\n    });\n  }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n  var attribute = \"data-clipboard-\".concat(suffix);\n\n  if (!element.hasAttribute(attribute)) {\n    return;\n  }\n\n  return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n  _inherits(Clipboard, _Emitter);\n\n  var _super = _createSuper(Clipboard);\n\n  /**\n   * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n   * @param {Object} options\n   */\n  function Clipboard(trigger, options) {\n    var _this;\n\n    _classCallCheck(this, Clipboard);\n\n    _this = _super.call(this);\n\n    _this.resolveOptions(options);\n\n    _this.listenClick(trigger);\n\n    return _this;\n  }\n  /**\n   * Defines if attributes would be resolved using internal setter functions\n   * or custom functions that were passed in the constructor.\n   * @param {Object} options\n   */\n\n\n  _createClass(Clipboard, [{\n    key: \"resolveOptions\",\n    value: function resolveOptions() {\n      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n      this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n      this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n      this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n      this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n    }\n    /**\n     * Adds a click event listener to the passed trigger.\n     * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n     */\n\n  }, {\n    key: \"listenClick\",\n    value: function listenClick(trigger) {\n      var _this2 = this;\n\n      this.listener = listen_default()(trigger, 'click', function (e) {\n        return _this2.onClick(e);\n      });\n    }\n    /**\n     * Defines a new `ClipboardAction` on each click event.\n     * @param {Event} e\n     */\n\n  }, {\n    key: \"onClick\",\n    value: function onClick(e) {\n      var trigger = e.delegateTarget || e.currentTarget;\n      var action = this.action(trigger) || 'copy';\n      var text = actions_default({\n        action: action,\n        container: this.container,\n        target: this.target(trigger),\n        text: this.text(trigger)\n      }); // Fires an event based on the copy operation result.\n\n      this.emit(text ? 'success' : 'error', {\n        action: action,\n        text: text,\n        trigger: trigger,\n        clearSelection: function clearSelection() {\n          if (trigger) {\n            trigger.focus();\n          }\n\n          window.getSelection().removeAllRanges();\n        }\n      });\n    }\n    /**\n     * Default `action` lookup function.\n     * @param {Element} trigger\n     */\n\n  }, {\n    key: \"defaultAction\",\n    value: function defaultAction(trigger) {\n      return getAttributeValue('action', trigger);\n    }\n    /**\n     * Default `target` lookup function.\n     * @param {Element} trigger\n     */\n\n  }, {\n    key: \"defaultTarget\",\n    value: function defaultTarget(trigger) {\n      var selector = getAttributeValue('target', trigger);\n\n      if (selector) {\n        return document.querySelector(selector);\n      }\n    }\n    /**\n     * Allow fire programmatically a copy action\n     * @param {String|HTMLElement} target\n     * @param {Object} options\n     * @returns Text copied.\n     */\n\n  }, {\n    key: \"defaultText\",\n\n    /**\n     * Default `text` lookup function.\n     * @param {Element} trigger\n     */\n    value: function defaultText(trigger) {\n      return getAttributeValue('text', trigger);\n    }\n    /**\n     * Destroy lifecycle.\n     */\n\n  }, {\n    key: \"destroy\",\n    value: function destroy() {\n      this.listener.destroy();\n    }\n  }], [{\n    key: \"copy\",\n    value: function copy(target) {\n      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n        container: document.body\n      };\n      return actions_copy(target, options);\n    }\n    /**\n     * Allow fire programmatically a cut action\n     * @param {String|HTMLElement} target\n     * @returns Text cutted.\n     */\n\n  }, {\n    key: \"cut\",\n    value: function cut(target) {\n      return actions_cut(target);\n    }\n    /**\n     * Returns the support of the given action, or all actions if no action is\n     * given.\n     * @param {String} [action]\n     */\n\n  }, {\n    key: \"isSupported\",\n    value: function isSupported() {\n      var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n      var actions = typeof action === 'string' ? [action] : action;\n      var support = !!document.queryCommandSupported;\n      actions.forEach(function (action) {\n        support = support && !!document.queryCommandSupported(action);\n      });\n      return support;\n    }\n  }]);\n\n  return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n    var proto = Element.prototype;\n\n    proto.matches = proto.matchesSelector ||\n                    proto.mozMatchesSelector ||\n                    proto.msMatchesSelector ||\n                    proto.oMatchesSelector ||\n                    proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n    while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n        if (typeof element.matches === 'function' &&\n            element.matches(selector)) {\n          return element;\n        }\n        element = element.parentNode;\n    }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n    var listenerFn = listener.apply(this, arguments);\n\n    element.addEventListener(type, listenerFn, useCapture);\n\n    return {\n        destroy: function() {\n            element.removeEventListener(type, listenerFn, useCapture);\n        }\n    }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n    // Handle the regular Element usage\n    if (typeof elements.addEventListener === 'function') {\n        return _delegate.apply(null, arguments);\n    }\n\n    // Handle Element-less usage, it defaults to global delegation\n    if (typeof type === 'function') {\n        // Use `document` as the first parameter, then apply arguments\n        // This is a short way to .unshift `arguments` without running into deoptimizations\n        return _delegate.bind(null, document).apply(null, arguments);\n    }\n\n    // Handle Selector-based usage\n    if (typeof elements === 'string') {\n        elements = document.querySelectorAll(elements);\n    }\n\n    // Handle Array-like based usage\n    return Array.prototype.map.call(elements, function (element) {\n        return _delegate(element, selector, type, callback, useCapture);\n    });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n    return function(e) {\n        e.delegateTarget = closest(e.target, selector);\n\n        if (e.delegateTarget) {\n            callback.call(element, e);\n        }\n    }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n    return value !== undefined\n        && value instanceof HTMLElement\n        && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n    var type = Object.prototype.toString.call(value);\n\n    return value !== undefined\n        && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n        && ('length' in value)\n        && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n    return typeof value === 'string'\n        || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n    var type = Object.prototype.toString.call(value);\n\n    return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n    if (!target && !type && !callback) {\n        throw new Error('Missing required arguments');\n    }\n\n    if (!is.string(type)) {\n        throw new TypeError('Second argument must be a String');\n    }\n\n    if (!is.fn(callback)) {\n        throw new TypeError('Third argument must be a Function');\n    }\n\n    if (is.node(target)) {\n        return listenNode(target, type, callback);\n    }\n    else if (is.nodeList(target)) {\n        return listenNodeList(target, type, callback);\n    }\n    else if (is.string(target)) {\n        return listenSelector(target, type, callback);\n    }\n    else {\n        throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n    }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n    node.addEventListener(type, callback);\n\n    return {\n        destroy: function() {\n            node.removeEventListener(type, callback);\n        }\n    }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n    Array.prototype.forEach.call(nodeList, function(node) {\n        node.addEventListener(type, callback);\n    });\n\n    return {\n        destroy: function() {\n            Array.prototype.forEach.call(nodeList, function(node) {\n                node.removeEventListener(type, callback);\n            });\n        }\n    }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n    return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n    var selectedText;\n\n    if (element.nodeName === 'SELECT') {\n        element.focus();\n\n        selectedText = element.value;\n    }\n    else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n        var isReadOnly = element.hasAttribute('readonly');\n\n        if (!isReadOnly) {\n            element.setAttribute('readonly', '');\n        }\n\n        element.select();\n        element.setSelectionRange(0, element.value.length);\n\n        if (!isReadOnly) {\n            element.removeAttribute('readonly');\n        }\n\n        selectedText = element.value;\n    }\n    else {\n        if (element.hasAttribute('contenteditable')) {\n            element.focus();\n        }\n\n        var selection = window.getSelection();\n        var range = document.createRange();\n\n        range.selectNodeContents(element);\n        selection.removeAllRanges();\n        selection.addRange(range);\n\n        selectedText = selection.toString();\n    }\n\n    return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n  // Keep this empty so it's easier to inherit from\n  // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n  on: function (name, callback, ctx) {\n    var e = this.e || (this.e = {});\n\n    (e[name] || (e[name] = [])).push({\n      fn: callback,\n      ctx: ctx\n    });\n\n    return this;\n  },\n\n  once: function (name, callback, ctx) {\n    var self = this;\n    function listener () {\n      self.off(name, listener);\n      callback.apply(ctx, arguments);\n    };\n\n    listener._ = callback\n    return this.on(name, listener, ctx);\n  },\n\n  emit: function (name) {\n    var data = [].slice.call(arguments, 1);\n    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n    var i = 0;\n    var len = evtArr.length;\n\n    for (i; i < len; i++) {\n      evtArr[i].fn.apply(evtArr[i].ctx, data);\n    }\n\n    return this;\n  },\n\n  off: function (name, callback) {\n    var e = this.e || (this.e = {});\n    var evts = e[name];\n    var liveEvents = [];\n\n    if (evts && callback) {\n      for (var i = 0, len = evts.length; i < len; i++) {\n        if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n          liveEvents.push(evts[i]);\n      }\n    }\n\n    // Remove event from queue to prevent memory leak\n    // Suggested by https://github.com/lazd\n    // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n    (liveEvents.length)\n      ? e[name] = liveEvents\n      : delete e[name];\n\n    return this;\n  }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.bootstrap.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 0, 136, 204;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(0, 136, 204);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\ntable.dataTable {\n  clear: both;\n  margin-top: 6px !important;\n  margin-bottom: 6px !important;\n  max-width: none !important;\n  border-collapse: separate !important;\n}\ntable.dataTable td,\ntable.dataTable th {\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ntable.dataTable td.dataTables_empty,\ntable.dataTable th.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable.nowrap th,\ntable.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgb(0, 136, 204);\n  box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable > tbody > tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable.table-striped > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.023);\n}\ntable.dataTable.table-striped > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 136, 204, 0.923);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.923);\n}\ntable.dataTable.table-hover > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);\n}\ntable.dataTable.table-hover > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 136, 204, 0.975);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);\n}\n\ndiv.dataTables_wrapper div.dataTables_length label {\n  font-weight: normal;\n  text-align: left;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_length select {\n  width: 75px;\n  display: inline-block;\n}\ndiv.dataTables_wrapper div.dataTables_filter {\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_filter label {\n  font-weight: normal;\n  white-space: nowrap;\n  text-align: left;\n}\ndiv.dataTables_wrapper div.dataTables_filter input {\n  margin-left: 0.5em;\n  display: inline-block;\n  width: auto;\n}\ndiv.dataTables_wrapper div.dataTables_info {\n  padding-top: 8px;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_paginate {\n  margin: 0;\n  white-space: nowrap;\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_paginate ul.pagination {\n  margin: 2px 0;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 1em 0;\n}\n\ndiv.dataTables_scrollHead table.dataTable {\n  margin-bottom: 0 !important;\n}\n\ndiv.dataTables_scrollBody > table {\n  border-top: none;\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\ndiv.dataTables_scrollBody > table > thead .sorting:after,\ndiv.dataTables_scrollBody > table > thead .sorting_asc:after,\ndiv.dataTables_scrollBody > table > thead .sorting_desc:after {\n  display: none;\n}\ndiv.dataTables_scrollBody > table > tbody > tr:first-child > th,\ndiv.dataTables_scrollBody > table > tbody > tr:first-child > td {\n  border-top: none;\n}\n\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner {\n  box-sizing: content-box;\n}\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner > table {\n  margin-top: 0 !important;\n  border-top: none;\n}\n\n@media screen and (max-width: 767px) {\n  div.dataTables_wrapper div.dataTables_length,\n  div.dataTables_wrapper div.dataTables_filter,\n  div.dataTables_wrapper div.dataTables_info,\n  div.dataTables_wrapper div.dataTables_paginate {\n    text-align: center;\n  }\n}\ntable.dataTable.table-condensed > thead > tr > th {\n  padding-right: 20px;\n}\n\ntable.table-bordered.dataTable {\n  border-right-width: 0;\n}\ntable.table-bordered.dataTable th,\ntable.table-bordered.dataTable td {\n  border-left-width: 0;\n}\ntable.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,\ntable.table-bordered.dataTable td:last-child,\ntable.table-bordered.dataTable td:last-child {\n  border-right-width: 1px;\n}\ntable.table-bordered.dataTable tbody th,\ntable.table-bordered.dataTable tbody td {\n  border-bottom-width: 0;\n}\n\ndiv.dataTables_scrollHead table.table-bordered {\n  border-bottom-width: 0;\n}\n\ndiv.table-responsive > div.dataTables_wrapper > div.row {\n  margin: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {\n  padding-left: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {\n  padding-right: 0;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.bootstrap4.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 2, 117, 216;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(2, 117, 216);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\ntable.dataTable {\n  clear: both;\n  margin-top: 6px !important;\n  margin-bottom: 6px !important;\n  max-width: none !important;\n  border-collapse: separate !important;\n  border-spacing: 0;\n}\ntable.dataTable td,\ntable.dataTable th {\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ntable.dataTable td.dataTables_empty,\ntable.dataTable th.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable.nowrap th,\ntable.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgb(2, 117, 216);\n  box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable > tbody > tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable.table-striped > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);\n}\ntable.dataTable.table-striped > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(2, 117, 216, 0.95);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);\n}\ntable.dataTable.table-hover > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);\n}\ntable.dataTable.table-hover > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(2, 117, 216, 0.975);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);\n}\n\ndiv.dataTables_wrapper div.dataTables_length label {\n  font-weight: normal;\n  text-align: left;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_length select {\n  width: auto;\n  display: inline-block;\n}\ndiv.dataTables_wrapper div.dataTables_filter {\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_filter label {\n  font-weight: normal;\n  white-space: nowrap;\n  text-align: left;\n}\ndiv.dataTables_wrapper div.dataTables_filter input {\n  margin-left: 0.5em;\n  display: inline-block;\n  width: auto;\n}\ndiv.dataTables_wrapper div.dataTables_info {\n  padding-top: 0.85em;\n}\ndiv.dataTables_wrapper div.dataTables_paginate {\n  margin: 0;\n  white-space: nowrap;\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_paginate ul.pagination {\n  margin: 2px 0;\n  white-space: nowrap;\n  justify-content: flex-end;\n}\ndiv.dataTables_wrapper div.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 1em 0;\n}\n\ndiv.dataTables_scrollHead table.dataTable {\n  margin-bottom: 0 !important;\n}\n\ndiv.dataTables_scrollBody > table {\n  border-top: none;\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\ndiv.dataTables_scrollBody > table > thead .sorting:before,\ndiv.dataTables_scrollBody > table > thead .sorting_asc:before,\ndiv.dataTables_scrollBody > table > thead .sorting_desc:before,\ndiv.dataTables_scrollBody > table > thead .sorting:after,\ndiv.dataTables_scrollBody > table > thead .sorting_asc:after,\ndiv.dataTables_scrollBody > table > thead .sorting_desc:after {\n  display: none;\n}\ndiv.dataTables_scrollBody > table > tbody tr:first-child th,\ndiv.dataTables_scrollBody > table > tbody tr:first-child td {\n  border-top: none;\n}\n\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner {\n  box-sizing: content-box;\n}\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner > table {\n  margin-top: 0 !important;\n  border-top: none;\n}\n\n@media screen and (max-width: 767px) {\n  div.dataTables_wrapper div.dataTables_length,\n  div.dataTables_wrapper div.dataTables_filter,\n  div.dataTables_wrapper div.dataTables_info,\n  div.dataTables_wrapper div.dataTables_paginate {\n    text-align: center;\n  }\n  div.dataTables_wrapper div.dataTables_paginate ul.pagination {\n    justify-content: center !important;\n  }\n}\ntable.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {\n  padding-right: 20px;\n}\n\ntable.table-bordered.dataTable {\n  border-right-width: 0;\n}\ntable.table-bordered.dataTable th,\ntable.table-bordered.dataTable td {\n  border-left-width: 0;\n}\ntable.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,\ntable.table-bordered.dataTable td:last-child,\ntable.table-bordered.dataTable td:last-child {\n  border-right-width: 1px;\n}\ntable.table-bordered.dataTable tbody th,\ntable.table-bordered.dataTable tbody td {\n  border-bottom-width: 0;\n}\n\ndiv.dataTables_scrollHead table.table-bordered {\n  border-bottom-width: 0;\n}\n\ndiv.table-responsive > div.dataTables_wrapper > div.row {\n  margin: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {\n  padding-left: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {\n  padding-right: 0;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.bootstrap5.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 13, 110, 253;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(13, 110, 253);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\n/*! Bootstrap 5 integration for DataTables\n *\n * ©2020 SpryMedia Ltd, all rights reserved.\n * License: MIT datatables.net/license/mit\n */\ntable.dataTable {\n  clear: both;\n  margin-top: 6px !important;\n  margin-bottom: 6px !important;\n  max-width: none !important;\n  border-collapse: separate !important;\n  border-spacing: 0;\n}\ntable.dataTable td,\ntable.dataTable th {\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ntable.dataTable td.dataTables_empty,\ntable.dataTable th.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable.nowrap th,\ntable.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {\n  box-shadow: none;\n}\ntable.dataTable > tbody > tr {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgb(13, 110, 253);\n  box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable > tbody > tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable.table-striped > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);\n}\ntable.dataTable.table-striped > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);\n}\ntable.dataTable.table-hover > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);\n}\ntable.dataTable.table-hover > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);\n}\n\ndiv.dataTables_wrapper div.dataTables_length label {\n  font-weight: normal;\n  text-align: left;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_length select {\n  width: auto;\n  display: inline-block;\n}\ndiv.dataTables_wrapper div.dataTables_filter {\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_filter label {\n  font-weight: normal;\n  white-space: nowrap;\n  text-align: left;\n}\ndiv.dataTables_wrapper div.dataTables_filter input {\n  margin-left: 0.5em;\n  display: inline-block;\n  width: auto;\n}\ndiv.dataTables_wrapper div.dataTables_info {\n  padding-top: 0.85em;\n}\ndiv.dataTables_wrapper div.dataTables_paginate {\n  margin: 0;\n  white-space: nowrap;\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_paginate ul.pagination {\n  margin: 2px 0;\n  white-space: nowrap;\n  justify-content: flex-end;\n}\ndiv.dataTables_wrapper div.dt-row {\n  position: relative;\n}\n\ndiv.dataTables_scrollHead table.dataTable {\n  margin-bottom: 0 !important;\n}\n\ndiv.dataTables_scrollBody > table {\n  border-top: none;\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\ndiv.dataTables_scrollBody > table > thead .sorting:before,\ndiv.dataTables_scrollBody > table > thead .sorting_asc:before,\ndiv.dataTables_scrollBody > table > thead .sorting_desc:before,\ndiv.dataTables_scrollBody > table > thead .sorting:after,\ndiv.dataTables_scrollBody > table > thead .sorting_asc:after,\ndiv.dataTables_scrollBody > table > thead .sorting_desc:after {\n  display: none;\n}\ndiv.dataTables_scrollBody > table > tbody tr:first-child th,\ndiv.dataTables_scrollBody > table > tbody tr:first-child td {\n  border-top: none;\n}\n\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner {\n  box-sizing: content-box;\n}\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner > table {\n  margin-top: 0 !important;\n  border-top: none;\n}\n\n@media screen and (max-width: 767px) {\n  div.dataTables_wrapper div.dataTables_length,\n  div.dataTables_wrapper div.dataTables_filter,\n  div.dataTables_wrapper div.dataTables_info,\n  div.dataTables_wrapper div.dataTables_paginate {\n    text-align: center;\n  }\n  div.dataTables_wrapper div.dataTables_paginate ul.pagination {\n    justify-content: center !important;\n  }\n}\ntable.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {\n  padding-right: 20px;\n}\n\ntable.table-bordered.dataTable {\n  border-right-width: 0;\n}\ntable.table-bordered.dataTable thead tr:first-child th,\ntable.table-bordered.dataTable thead tr:first-child td {\n  border-top-width: 1px;\n}\ntable.table-bordered.dataTable th,\ntable.table-bordered.dataTable td {\n  border-left-width: 0;\n}\ntable.table-bordered.dataTable th:first-child, table.table-bordered.dataTable th:first-child,\ntable.table-bordered.dataTable td:first-child,\ntable.table-bordered.dataTable td:first-child {\n  border-left-width: 1px;\n}\ntable.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,\ntable.table-bordered.dataTable td:last-child,\ntable.table-bordered.dataTable td:last-child {\n  border-right-width: 1px;\n}\ntable.table-bordered.dataTable th,\ntable.table-bordered.dataTable td {\n  border-bottom-width: 1px;\n}\n\ndiv.dataTables_scrollHead table.table-bordered {\n  border-bottom-width: 0;\n}\n\ndiv.table-responsive > div.dataTables_wrapper > div.row {\n  margin: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {\n  padding-left: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {\n  padding-right: 0;\n}\n\n:root[data-bs-theme=dark] {\n  --dt-row-hover: 255, 255, 255;\n  --dt-row-stripe: 255, 255, 255;\n  --dt-column-ordering: 255, 255, 255;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.bulma.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 0, 209, 178;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(0, 209, 178);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\n/*! DataTables Bulma integration\n * ©2020 SpryMedia Ltd - datatables.net/license\n */\ntable.dataTable {\n  clear: both;\n  margin-top: 6px !important;\n  margin-bottom: 6px !important;\n  max-width: none !important;\n  border-collapse: separate !important;\n  border-spacing: 0;\n}\ntable.dataTable td,\ntable.dataTable th {\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ntable.dataTable td.dataTables_empty,\ntable.dataTable th.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable.nowrap th,\ntable.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable.table.is-striped > tbody > tr:nth-child(2n) {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgb(0, 209, 178);\n  box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable > tbody > tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable.is-striped > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.019);\n}\ntable.dataTable.is-striped > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 209, 178, 0.919);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919);\n}\ntable.dataTable.is-hoverable > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.39);\n}\ntable.dataTable.is-hoverable > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px #00d1b2;\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 1.29);\n}\n\ndiv.dataTables_wrapper div.dataTables_length label {\n  font-weight: normal;\n  text-align: left;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_length div {\n  vertical-align: middle;\n}\ndiv.dataTables_wrapper div.dataTables_length select {\n  width: auto;\n  display: inline-block;\n  vertical-align: middle;\n}\ndiv.dataTables_wrapper div.dataTables_filter {\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_filter label {\n  font-weight: normal;\n  white-space: nowrap;\n  text-align: left;\n}\ndiv.dataTables_wrapper div.dataTables_filter input {\n  margin-left: 0.5em;\n  width: auto;\n  vertical-align: middle;\n}\ndiv.dataTables_wrapper div.dataTables_info {\n  padding-top: 0.5em;\n}\ndiv.dataTables_wrapper div.dataTables_paginate ul {\n  justify-content: flex-end;\n  list-style: none;\n  margin: 0;\n}\ndiv.dataTables_wrapper div.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 1em 0;\n}\n\ndiv.dataTables_scrollHead table.dataTable {\n  margin-bottom: 0 !important;\n}\n\ndiv.dataTables_scrollBody table {\n  border-top: none;\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\ndiv.dataTables_scrollBody table thead .sorting:before,\ndiv.dataTables_scrollBody table thead .sorting_asc:before,\ndiv.dataTables_scrollBody table thead .sorting_desc:before,\ndiv.dataTables_scrollBody table thead .sorting:after,\ndiv.dataTables_scrollBody table thead .sorting_asc:after,\ndiv.dataTables_scrollBody table thead .sorting_desc:after {\n  display: none;\n}\ndiv.dataTables_scrollBody table tbody tr:first-child th,\ndiv.dataTables_scrollBody table tbody tr:first-child td {\n  border-top: none;\n}\n\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner {\n  box-sizing: content-box;\n}\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner > table {\n  margin-top: 0 !important;\n  border-top: none;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.dataTables.css",
    "content": ""
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.foundation.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 0, 137, 182;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(0, 137, 182);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\ntable.dataTable {\n  clear: both;\n  margin: 0.5em 0 !important;\n  max-width: none !important;\n  width: 100%;\n}\ntable.dataTable td,\ntable.dataTable th {\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ntable.dataTable td.dataTables_empty,\ntable.dataTable th.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable tr:nth-child(2n) {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgb(0, 137, 182);\n  box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable > tbody > tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable > tbody > tr.even > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.054);\n}\ntable.dataTable > tbody > tr.even.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 137, 182, 0.954);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.954);\n}\ntable.dataTable.hover > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.074);\n}\ntable.dataTable.hover > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 137, 182, 0.974);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.974);\n}\n\ndiv.dataTables_wrapper {\n  position: relative;\n}\ndiv.dataTables_wrapper div.dataTables_length label {\n  float: left;\n  text-align: left;\n  margin-bottom: 0;\n}\ndiv.dataTables_wrapper div.dataTables_length select {\n  width: 75px;\n  margin-bottom: 0;\n}\ndiv.dataTables_wrapper div.dataTables_filter label {\n  float: right;\n  margin-bottom: 0;\n}\ndiv.dataTables_wrapper div.dataTables_filter input {\n  display: inline-block !important;\n  width: auto !important;\n  margin-bottom: 0;\n  margin-left: 0.5em;\n}\ndiv.dataTables_wrapper div.dataTables_info {\n  padding-top: 2px;\n}\ndiv.dataTables_wrapper div.dataTables_paginate {\n  float: right;\n  margin: 0;\n}\ndiv.dataTables_wrapper div.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 1rem 0;\n}\n\ndiv.dataTables_scrollHead table {\n  margin-bottom: 0 !important;\n}\n\ndiv.dataTables_scrollBody table {\n  border-top: none;\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\ndiv.dataTables_scrollBody table tbody tr:first-child th,\ndiv.dataTables_scrollBody table tbody tr:first-child td {\n  border-top: none;\n}\n\ndiv.dataTables_scrollFoot table {\n  margin-top: 0 !important;\n  border-top: none;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.jqueryui.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 13, 110, 253;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\n:root {\n  --dt-row-selected: 13, 110, 253;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(13, 110, 253);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\n/*\n * Table styles\n */\ntable.dataTable {\n  width: 100%;\n  margin: 0 auto;\n  clear: both;\n  border-collapse: separate;\n  border-spacing: 0;\n  /*\n   * Header and footer styles\n   */\n  /*\n   * Body styles\n   */\n}\ntable.dataTable thead th,\ntable.dataTable tfoot th {\n  font-weight: bold;\n}\ntable.dataTable > thead > tr > th,\ntable.dataTable > thead > tr > td {\n  padding: 10px;\n}\ntable.dataTable > thead > tr > th:active,\ntable.dataTable > thead > tr > td:active {\n  outline: none;\n}\ntable.dataTable > tfoot > tr > th,\ntable.dataTable > tfoot > tr > td {\n  padding: 10px 10px 6px 10px;\n}\ntable.dataTable tbody tr {\n  background-color: transparent;\n}\ntable.dataTable tbody tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.9);\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable tbody tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable tbody th,\ntable.dataTable tbody td {\n  padding: 8px 10px;\n}\ntable.dataTable.row-border > tbody > tr > th,\ntable.dataTable.row-border > tbody > tr > td, table.dataTable.display > tbody > tr > th,\ntable.dataTable.display > tbody > tr > td {\n  border-top: 1px solid rgba(0, 0, 0, 0.15);\n}\ntable.dataTable.row-border > tbody > tr:first-child > th,\ntable.dataTable.row-border > tbody > tr:first-child > td, table.dataTable.display > tbody > tr:first-child > th,\ntable.dataTable.display > tbody > tr:first-child > td {\n  border-top: none;\n}\ntable.dataTable.row-border > tbody > tr.selected + tr.selected > td, table.dataTable.display > tbody > tr.selected + tr.selected > td {\n  border-top-color: #0262ef;\n}\ntable.dataTable.cell-border > tbody > tr > th,\ntable.dataTable.cell-border > tbody > tr > td {\n  border-top: 1px solid rgba(0, 0, 0, 0.15);\n  border-right: 1px solid rgba(0, 0, 0, 0.15);\n}\ntable.dataTable.cell-border > tbody > tr > th:first-child,\ntable.dataTable.cell-border > tbody > tr > td:first-child {\n  border-left: 1px solid rgba(0, 0, 0, 0.15);\n}\ntable.dataTable.cell-border > tbody > tr:first-child > th,\ntable.dataTable.cell-border > tbody > tr:first-child > td {\n  border-top: none;\n}\ntable.dataTable.stripe > tbody > tr.odd > *, table.dataTable.display > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.023);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.023);\n}\ntable.dataTable.stripe > tbody > tr.odd.selected > *, table.dataTable.display > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.923);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.923);\n}\ntable.dataTable.hover > tbody > tr:hover > *, table.dataTable.display > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.035);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.035);\n}\ntable.dataTable.hover > tbody > tr.selected:hover > *, table.dataTable.display > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px #0d6efd !important;\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 1) !important;\n}\ntable.dataTable.order-column > tbody tr > .sorting_1,\ntable.dataTable.order-column > tbody tr > .sorting_2,\ntable.dataTable.order-column > tbody tr > .sorting_3, table.dataTable.display > tbody tr > .sorting_1,\ntable.dataTable.display > tbody tr > .sorting_2,\ntable.dataTable.display > tbody tr > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019);\n}\ntable.dataTable.order-column > tbody tr.selected > .sorting_1,\ntable.dataTable.order-column > tbody tr.selected > .sorting_2,\ntable.dataTable.order-column > tbody tr.selected > .sorting_3, table.dataTable.display > tbody tr.selected > .sorting_1,\ntable.dataTable.display > tbody tr.selected > .sorting_2,\ntable.dataTable.display > tbody tr.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919);\n}\ntable.dataTable.display > tbody > tr.odd > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.054);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.054);\n}\ntable.dataTable.display > tbody > tr.odd > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.047);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.047);\n}\ntable.dataTable.display > tbody > tr.odd > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.039);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.039);\n}\ntable.dataTable.display > tbody > tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.954);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.954);\n}\ntable.dataTable.display > tbody > tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.947);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.947);\n}\ntable.dataTable.display > tbody > tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.939);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.939);\n}\ntable.dataTable.display > tbody > tr.even > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.019);\n}\ntable.dataTable.display > tbody > tr.even > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.011);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.011);\n}\ntable.dataTable.display > tbody > tr.even > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.003);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.003);\n}\ntable.dataTable.display > tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919);\n}\ntable.dataTable.display > tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.911);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.911);\n}\ntable.dataTable.display > tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.903);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.903);\n}\ntable.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.082);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.082);\n}\ntable.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.074);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.074);\n}\ntable.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.062);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.062);\n}\ntable.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.982);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.982);\n}\ntable.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.974);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.974);\n}\ntable.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.962);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.962);\n}\ntable.dataTable.no-footer {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.3);\n}\ntable.dataTable.compact thead th,\ntable.dataTable.compact thead td,\ntable.dataTable.compact tfoot th,\ntable.dataTable.compact tfoot td,\ntable.dataTable.compact tbody th,\ntable.dataTable.compact tbody td {\n  padding: 4px;\n}\n\ntable.dataTable th,\ntable.dataTable td {\n  box-sizing: content-box;\n}\n\n/*\n * Control feature layout\n */\n.dataTables_wrapper {\n  position: relative;\n  clear: both;\n}\n.dataTables_wrapper .dataTables_length {\n  float: left;\n}\n.dataTables_wrapper .dataTables_length select {\n  border: 1px solid #aaa;\n  border-radius: 3px;\n  padding: 5px;\n  background-color: transparent;\n  color: inherit;\n  padding: 4px;\n}\n.dataTables_wrapper .dataTables_filter {\n  float: right;\n  text-align: right;\n}\n.dataTables_wrapper .dataTables_filter input {\n  border: 1px solid #aaa;\n  border-radius: 3px;\n  padding: 5px;\n  background-color: transparent;\n  color: inherit;\n  margin-left: 3px;\n}\n.dataTables_wrapper .dataTables_info {\n  clear: both;\n  float: left;\n  padding-top: 0.755em;\n}\n.dataTables_wrapper .dataTables_paginate {\n  float: right;\n  text-align: right;\n  padding-top: 0.25em;\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button {\n  box-sizing: border-box;\n  display: inline-block;\n  min-width: 1.5em;\n  padding: 0.5em 1em;\n  margin-left: 2px;\n  text-align: center;\n  text-decoration: none !important;\n  cursor: pointer;\n  color: inherit !important;\n  border: 1px solid transparent;\n  border-radius: 2px;\n  background: transparent;\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {\n  color: inherit !important;\n  border: 1px solid rgba(0, 0, 0, 0.3);\n  background-color: rgba(0, 0, 0, 0.05);\n  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(230, 230, 230, 0.05)), color-stop(100%, rgba(0, 0, 0, 0.05))); /* Chrome,Safari4+ */\n  background: -webkit-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Chrome10+,Safari5.1+ */\n  background: -moz-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* FF3.6+ */\n  background: -ms-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* IE10+ */\n  background: -o-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Opera 11.10+ */\n  background: linear-gradient(to bottom, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* W3C */\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {\n  cursor: default;\n  color: #666 !important;\n  border: 1px solid transparent;\n  background: transparent;\n  box-shadow: none;\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button:hover {\n  color: white !important;\n  border: 1px solid #111;\n  background-color: #111;\n  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111)); /* Chrome,Safari4+ */\n  background: -webkit-linear-gradient(top, #585858 0%, #111 100%); /* Chrome10+,Safari5.1+ */\n  background: -moz-linear-gradient(top, #585858 0%, #111 100%); /* FF3.6+ */\n  background: -ms-linear-gradient(top, #585858 0%, #111 100%); /* IE10+ */\n  background: -o-linear-gradient(top, #585858 0%, #111 100%); /* Opera 11.10+ */\n  background: linear-gradient(to bottom, #585858 0%, #111 100%); /* W3C */\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button:active {\n  outline: none;\n  background-color: #0c0c0c;\n  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c)); /* Chrome,Safari4+ */\n  background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* Chrome10+,Safari5.1+ */\n  background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* FF3.6+ */\n  background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* IE10+ */\n  background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* Opera 11.10+ */\n  background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%); /* W3C */\n  box-shadow: inset 0 0 3px #111;\n}\n.dataTables_wrapper .dataTables_paginate .ellipsis {\n  padding: 0 1em;\n}\n.dataTables_wrapper .dataTables_length,\n.dataTables_wrapper .dataTables_filter,\n.dataTables_wrapper .dataTables_info,\n.dataTables_wrapper .dataTables_processing,\n.dataTables_wrapper .dataTables_paginate {\n  color: inherit;\n}\n.dataTables_wrapper .dataTables_scroll {\n  clear: both;\n}\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {\n  -webkit-overflow-scrolling: touch;\n}\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td {\n  vertical-align: middle;\n}\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing,\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing,\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing {\n  height: 0;\n  overflow: hidden;\n  margin: 0 !important;\n  padding: 0 !important;\n}\n.dataTables_wrapper.no-footer .dataTables_scrollBody {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.3);\n}\n.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,\n.dataTables_wrapper.no-footer div.dataTables_scrollBody > table {\n  border-bottom: none;\n}\n.dataTables_wrapper:after {\n  visibility: hidden;\n  display: block;\n  content: \"\";\n  clear: both;\n  height: 0;\n}\n\n@media screen and (max-width: 767px) {\n  .dataTables_wrapper .dataTables_info,\n  .dataTables_wrapper .dataTables_paginate {\n    float: none;\n    text-align: center;\n  }\n  .dataTables_wrapper .dataTables_paginate {\n    margin-top: 0.5em;\n  }\n}\n@media screen and (max-width: 640px) {\n  .dataTables_wrapper .dataTables_length,\n  .dataTables_wrapper .dataTables_filter {\n    float: none;\n    text-align: center;\n  }\n  .dataTables_wrapper .dataTables_filter {\n    margin-top: 0.5em;\n  }\n}\nhtml.dark {\n  --dt-row-hover: 255, 255, 255;\n  --dt-row-stripe: 255, 255, 255;\n  --dt-column-ordering: 255, 255, 255;\n}\nhtml.dark table.dataTable > thead > tr > th:active,\nhtml.dark table.dataTable > thead > tr > td:active {\n  outline: none;\n}\nhtml.dark table.dataTable.row-border > tbody > tr > th,\nhtml.dark table.dataTable.row-border > tbody > tr > td, html.dark table.dataTable.display > tbody > tr > th,\nhtml.dark table.dataTable.display > tbody > tr > td {\n  border-top: 1px solid rgb(64, 67, 70);\n}\nhtml.dark table.dataTable.row-border > tbody > tr.selected + tr.selected > td, html.dark table.dataTable.display > tbody > tr.selected + tr.selected > td {\n  border-top-color: #0257d5;\n}\nhtml.dark table.dataTable.cell-border > tbody > tr > th,\nhtml.dark table.dataTable.cell-border > tbody > tr > td {\n  border-top: 1px solid rgb(64, 67, 70);\n  border-right: 1px solid rgb(64, 67, 70);\n}\nhtml.dark table.dataTable.cell-border > tbody > tr > th:first-child,\nhtml.dark table.dataTable.cell-border > tbody > tr > td:first-child {\n  border-left: 1px solid rgb(64, 67, 70);\n}\nhtml.dark .dataTables_wrapper .dataTables_filter input,\nhtml.dark .dataTables_wrapper .dataTables_length select {\n  border: 1px solid rgba(255, 255, 255, 0.2);\n  background-color: var(--dt-html-background);\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button.current, html.dark .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {\n  border: 1px solid rgb(89, 91, 94);\n  background: rgba(255, 255, 255, 0.15);\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button.disabled, html.dark .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, html.dark .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {\n  color: #666 !important;\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button:hover {\n  border: 1px solid rgb(53, 53, 53);\n  background: rgb(53, 53, 53);\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button:active {\n  background: #3a3a3a;\n}\n\ntable.dataTable thead th div.DataTables_sort_wrapper {\n  position: relative;\n}\ntable.dataTable thead th div.DataTables_sort_wrapper span {\n  position: absolute;\n  top: 50%;\n  margin-top: -8px;\n  right: -18px;\n}\ntable.dataTable thead th.ui-state-default,\ntable.dataTable tfoot th.ui-state-default {\n  border-left-width: 0;\n}\ntable.dataTable thead th.ui-state-default:first-child,\ntable.dataTable tfoot th.ui-state-default:first-child {\n  border-left-width: 1px;\n}\n\n/*\n * Control feature layout\n */\n.dataTables_wrapper .dataTables_paginate .fg-button {\n  box-sizing: border-box;\n  display: inline-block;\n  min-width: 1.5em;\n  padding: 0.5em;\n  margin-left: 2px;\n  text-align: center;\n  text-decoration: none !important;\n  cursor: pointer;\n  border: 1px solid transparent;\n}\n.dataTables_wrapper .dataTables_paginate .fg-button:active {\n  outline: none;\n}\n.dataTables_wrapper .dataTables_paginate .fg-button:first-child {\n  border-top-left-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.dataTables_wrapper .dataTables_paginate .fg-button:last-child {\n  border-top-right-radius: 3px;\n  border-bottom-right-radius: 3px;\n}\n.dataTables_wrapper .ui-widget-header {\n  font-weight: normal;\n}\n.dataTables_wrapper .ui-toolbar {\n  padding: 8px;\n}\n.dataTables_wrapper.no-footer .dataTables_scrollBody {\n  border-bottom: none;\n}\n.dataTables_wrapper .dataTables_length,\n.dataTables_wrapper .dataTables_filter,\n.dataTables_wrapper .dataTables_info,\n.dataTables_wrapper .dataTables_processing,\n.dataTables_wrapper .dataTables_paginate {\n  color: inherit;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/dataTables.semanticui.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 224, 224, 224;\n  --dt-row-selected-text: 0, 0, 0;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(224, 224, 224);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\n/*\n * Styling for DataTables with Semantic UI\n */\ntable.dataTable.table {\n  margin: 0;\n}\ntable.dataTable.table td,\ntable.dataTable.table th {\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ntable.dataTable.table td.dataTables_empty,\ntable.dataTable.table th.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable.table.nowrap th,\ntable.dataTable.table.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable.table.ui.striped > tbody > tr:nth-child(2n) {\n  background-color: transparent;\n}\ntable.dataTable.table > tbody > tr {\n  background-color: transparent;\n}\ntable.dataTable.table > tbody > tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgb(224, 224, 224);\n  box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));\n  color: rgb(0, 0, 0);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable.table > tbody > tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable.table.striped > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.02);\n}\ntable.dataTable.table.striped > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(224, 224, 224, 0.92);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.92);\n}\ntable.dataTable.table.hover > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);\n}\ntable.dataTable.table.hover > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(224, 224, 224, 0.975);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);\n}\n\ndiv.dataTables_wrapper div.dataTables_length select {\n  vertical-align: middle;\n  min-height: 2.7142em;\n}\ndiv.dataTables_wrapper div.dataTables_length .ui.selection.dropdown {\n  min-width: 0;\n}\ndiv.dataTables_wrapper div.dataTables_filter span.input {\n  margin-left: 0.5em;\n}\ndiv.dataTables_wrapper div.dataTables_info {\n  padding-top: 13px;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  text-align: center;\n}\ndiv.dataTables_wrapper div.row.dt-table {\n  padding: 0;\n}\ndiv.dataTables_wrapper div.dataTables_scrollHead table.dataTable {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n  border-bottom: none;\n}\ndiv.dataTables_wrapper div.dataTables_scrollBody thead .sorting:after,\ndiv.dataTables_wrapper div.dataTables_scrollBody thead .sorting_asc:after,\ndiv.dataTables_wrapper div.dataTables_scrollBody thead .sorting_desc:after {\n  display: none;\n}\ndiv.dataTables_wrapper div.dataTables_scrollBody table.dataTable {\n  border-radius: 0;\n  border-top: none;\n  border-bottom-width: 0;\n}\ndiv.dataTables_wrapper div.dataTables_scrollBody table.dataTable.no-footer {\n  border-bottom-width: 1px;\n}\ndiv.dataTables_wrapper div.dataTables_scrollFoot table.dataTable {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n  border-top: none;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/css/jquery.dataTables.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 13, 110, 253;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(13, 110, 253);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\n/*\n * Table styles\n */\ntable.dataTable {\n  width: 100%;\n  margin: 0 auto;\n  clear: both;\n  border-collapse: separate;\n  border-spacing: 0;\n  /*\n   * Header and footer styles\n   */\n  /*\n   * Body styles\n   */\n}\ntable.dataTable thead th,\ntable.dataTable tfoot th {\n  font-weight: bold;\n}\ntable.dataTable > thead > tr > th,\ntable.dataTable > thead > tr > td {\n  padding: 10px;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.3);\n}\ntable.dataTable > thead > tr > th:active,\ntable.dataTable > thead > tr > td:active {\n  outline: none;\n}\ntable.dataTable > tfoot > tr > th,\ntable.dataTable > tfoot > tr > td {\n  padding: 10px 10px 6px 10px;\n  border-top: 1px solid rgba(0, 0, 0, 0.3);\n}\ntable.dataTable tbody tr {\n  background-color: transparent;\n}\ntable.dataTable tbody tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.9);\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable tbody tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable tbody th,\ntable.dataTable tbody td {\n  padding: 8px 10px;\n}\ntable.dataTable.row-border > tbody > tr > th,\ntable.dataTable.row-border > tbody > tr > td, table.dataTable.display > tbody > tr > th,\ntable.dataTable.display > tbody > tr > td {\n  border-top: 1px solid rgba(0, 0, 0, 0.15);\n}\ntable.dataTable.row-border > tbody > tr:first-child > th,\ntable.dataTable.row-border > tbody > tr:first-child > td, table.dataTable.display > tbody > tr:first-child > th,\ntable.dataTable.display > tbody > tr:first-child > td {\n  border-top: none;\n}\ntable.dataTable.row-border > tbody > tr.selected + tr.selected > td, table.dataTable.display > tbody > tr.selected + tr.selected > td {\n  border-top-color: #0262ef;\n}\ntable.dataTable.cell-border > tbody > tr > th,\ntable.dataTable.cell-border > tbody > tr > td {\n  border-top: 1px solid rgba(0, 0, 0, 0.15);\n  border-right: 1px solid rgba(0, 0, 0, 0.15);\n}\ntable.dataTable.cell-border > tbody > tr > th:first-child,\ntable.dataTable.cell-border > tbody > tr > td:first-child {\n  border-left: 1px solid rgba(0, 0, 0, 0.15);\n}\ntable.dataTable.cell-border > tbody > tr:first-child > th,\ntable.dataTable.cell-border > tbody > tr:first-child > td {\n  border-top: none;\n}\ntable.dataTable.stripe > tbody > tr.odd > *, table.dataTable.display > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.023);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.023);\n}\ntable.dataTable.stripe > tbody > tr.odd.selected > *, table.dataTable.display > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.923);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.923);\n}\ntable.dataTable.hover > tbody > tr:hover > *, table.dataTable.display > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.035);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.035);\n}\ntable.dataTable.hover > tbody > tr.selected:hover > *, table.dataTable.display > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px #0d6efd !important;\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 1) !important;\n}\ntable.dataTable.order-column > tbody tr > .sorting_1,\ntable.dataTable.order-column > tbody tr > .sorting_2,\ntable.dataTable.order-column > tbody tr > .sorting_3, table.dataTable.display > tbody tr > .sorting_1,\ntable.dataTable.display > tbody tr > .sorting_2,\ntable.dataTable.display > tbody tr > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019);\n}\ntable.dataTable.order-column > tbody tr.selected > .sorting_1,\ntable.dataTable.order-column > tbody tr.selected > .sorting_2,\ntable.dataTable.order-column > tbody tr.selected > .sorting_3, table.dataTable.display > tbody tr.selected > .sorting_1,\ntable.dataTable.display > tbody tr.selected > .sorting_2,\ntable.dataTable.display > tbody tr.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919);\n}\ntable.dataTable.display > tbody > tr.odd > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.054);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.054);\n}\ntable.dataTable.display > tbody > tr.odd > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.047);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.047);\n}\ntable.dataTable.display > tbody > tr.odd > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.039);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.039);\n}\ntable.dataTable.display > tbody > tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.954);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.954);\n}\ntable.dataTable.display > tbody > tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.947);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.947);\n}\ntable.dataTable.display > tbody > tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.939);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.939);\n}\ntable.dataTable.display > tbody > tr.even > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.019);\n}\ntable.dataTable.display > tbody > tr.even > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.011);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.011);\n}\ntable.dataTable.display > tbody > tr.even > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.003);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.003);\n}\ntable.dataTable.display > tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919);\n}\ntable.dataTable.display > tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.911);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.911);\n}\ntable.dataTable.display > tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.903);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.903);\n}\ntable.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.082);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.082);\n}\ntable.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.074);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.074);\n}\ntable.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.062);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.062);\n}\ntable.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.982);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.982);\n}\ntable.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.974);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.974);\n}\ntable.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.962);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.962);\n}\ntable.dataTable.no-footer {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.3);\n}\ntable.dataTable.compact thead th,\ntable.dataTable.compact thead td,\ntable.dataTable.compact tfoot th,\ntable.dataTable.compact tfoot td,\ntable.dataTable.compact tbody th,\ntable.dataTable.compact tbody td {\n  padding: 4px;\n}\n\ntable.dataTable th,\ntable.dataTable td {\n  box-sizing: content-box;\n}\n\n/*\n * Control feature layout\n */\n.dataTables_wrapper {\n  position: relative;\n  clear: both;\n}\n.dataTables_wrapper .dataTables_length {\n  float: left;\n}\n.dataTables_wrapper .dataTables_length select {\n  border: 1px solid #aaa;\n  border-radius: 3px;\n  padding: 5px;\n  background-color: transparent;\n  color: inherit;\n  padding: 4px;\n}\n.dataTables_wrapper .dataTables_filter {\n  float: right;\n  text-align: right;\n}\n.dataTables_wrapper .dataTables_filter input {\n  border: 1px solid #aaa;\n  border-radius: 3px;\n  padding: 5px;\n  background-color: transparent;\n  color: inherit;\n  margin-left: 3px;\n}\n.dataTables_wrapper .dataTables_info {\n  clear: both;\n  float: left;\n  padding-top: 0.755em;\n}\n.dataTables_wrapper .dataTables_paginate {\n  float: right;\n  text-align: right;\n  padding-top: 0.25em;\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button {\n  box-sizing: border-box;\n  display: inline-block;\n  min-width: 1.5em;\n  padding: 0.5em 1em;\n  margin-left: 2px;\n  text-align: center;\n  text-decoration: none !important;\n  cursor: pointer;\n  color: inherit !important;\n  border: 1px solid transparent;\n  border-radius: 2px;\n  background: transparent;\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {\n  color: inherit !important;\n  border: 1px solid rgba(0, 0, 0, 0.3);\n  background-color: rgba(0, 0, 0, 0.05);\n  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(230, 230, 230, 0.05)), color-stop(100%, rgba(0, 0, 0, 0.05))); /* Chrome,Safari4+ */\n  background: -webkit-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Chrome10+,Safari5.1+ */\n  background: -moz-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* FF3.6+ */\n  background: -ms-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* IE10+ */\n  background: -o-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Opera 11.10+ */\n  background: linear-gradient(to bottom, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* W3C */\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {\n  cursor: default;\n  color: #666 !important;\n  border: 1px solid transparent;\n  background: transparent;\n  box-shadow: none;\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button:hover {\n  color: white !important;\n  border: 1px solid #111;\n  background-color: #111;\n  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111)); /* Chrome,Safari4+ */\n  background: -webkit-linear-gradient(top, #585858 0%, #111 100%); /* Chrome10+,Safari5.1+ */\n  background: -moz-linear-gradient(top, #585858 0%, #111 100%); /* FF3.6+ */\n  background: -ms-linear-gradient(top, #585858 0%, #111 100%); /* IE10+ */\n  background: -o-linear-gradient(top, #585858 0%, #111 100%); /* Opera 11.10+ */\n  background: linear-gradient(to bottom, #585858 0%, #111 100%); /* W3C */\n}\n.dataTables_wrapper .dataTables_paginate .paginate_button:active {\n  outline: none;\n  background-color: #0c0c0c;\n  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c)); /* Chrome,Safari4+ */\n  background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* Chrome10+,Safari5.1+ */\n  background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* FF3.6+ */\n  background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* IE10+ */\n  background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* Opera 11.10+ */\n  background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%); /* W3C */\n  box-shadow: inset 0 0 3px #111;\n}\n.dataTables_wrapper .dataTables_paginate .ellipsis {\n  padding: 0 1em;\n}\n.dataTables_wrapper .dataTables_length,\n.dataTables_wrapper .dataTables_filter,\n.dataTables_wrapper .dataTables_info,\n.dataTables_wrapper .dataTables_processing,\n.dataTables_wrapper .dataTables_paginate {\n  color: inherit;\n}\n.dataTables_wrapper .dataTables_scroll {\n  clear: both;\n}\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {\n  -webkit-overflow-scrolling: touch;\n}\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td {\n  vertical-align: middle;\n}\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing,\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing,\n.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing {\n  height: 0;\n  overflow: hidden;\n  margin: 0 !important;\n  padding: 0 !important;\n}\n.dataTables_wrapper.no-footer .dataTables_scrollBody {\n  border-bottom: 1px solid rgba(0, 0, 0, 0.3);\n}\n.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,\n.dataTables_wrapper.no-footer div.dataTables_scrollBody > table {\n  border-bottom: none;\n}\n.dataTables_wrapper:after {\n  visibility: hidden;\n  display: block;\n  content: \"\";\n  clear: both;\n  height: 0;\n}\n\n@media screen and (max-width: 767px) {\n  .dataTables_wrapper .dataTables_info,\n  .dataTables_wrapper .dataTables_paginate {\n    float: none;\n    text-align: center;\n  }\n  .dataTables_wrapper .dataTables_paginate {\n    margin-top: 0.5em;\n  }\n}\n@media screen and (max-width: 640px) {\n  .dataTables_wrapper .dataTables_length,\n  .dataTables_wrapper .dataTables_filter {\n    float: none;\n    text-align: center;\n  }\n  .dataTables_wrapper .dataTables_filter {\n    margin-top: 0.5em;\n  }\n}\nhtml.dark {\n  --dt-row-hover: 255, 255, 255;\n  --dt-row-stripe: 255, 255, 255;\n  --dt-column-ordering: 255, 255, 255;\n}\nhtml.dark table.dataTable > thead > tr > th,\nhtml.dark table.dataTable > thead > tr > td {\n  border-bottom: 1px solid rgb(89, 91, 94);\n}\nhtml.dark table.dataTable > thead > tr > th:active,\nhtml.dark table.dataTable > thead > tr > td:active {\n  outline: none;\n}\nhtml.dark table.dataTable > tfoot > tr > th,\nhtml.dark table.dataTable > tfoot > tr > td {\n  border-top: 1px solid rgb(89, 91, 94);\n}\nhtml.dark table.dataTable.row-border > tbody > tr > th,\nhtml.dark table.dataTable.row-border > tbody > tr > td, html.dark table.dataTable.display > tbody > tr > th,\nhtml.dark table.dataTable.display > tbody > tr > td {\n  border-top: 1px solid rgb(64, 67, 70);\n}\nhtml.dark table.dataTable.row-border > tbody > tr.selected + tr.selected > td, html.dark table.dataTable.display > tbody > tr.selected + tr.selected > td {\n  border-top-color: #0257d5;\n}\nhtml.dark table.dataTable.cell-border > tbody > tr > th,\nhtml.dark table.dataTable.cell-border > tbody > tr > td {\n  border-top: 1px solid rgb(64, 67, 70);\n  border-right: 1px solid rgb(64, 67, 70);\n}\nhtml.dark table.dataTable.cell-border > tbody > tr > th:first-child,\nhtml.dark table.dataTable.cell-border > tbody > tr > td:first-child {\n  border-left: 1px solid rgb(64, 67, 70);\n}\nhtml.dark .dataTables_wrapper .dataTables_filter input,\nhtml.dark .dataTables_wrapper .dataTables_length select {\n  border: 1px solid rgba(255, 255, 255, 0.2);\n  background-color: var(--dt-html-background);\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button.current, html.dark .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {\n  border: 1px solid rgb(89, 91, 94);\n  background: rgba(255, 255, 255, 0.15);\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button.disabled, html.dark .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, html.dark .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {\n  color: #666 !important;\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button:hover {\n  border: 1px solid rgb(53, 53, 53);\n  background: rgb(53, 53, 53);\n}\nhtml.dark .dataTables_wrapper .dataTables_paginate .paginate_button:active {\n  background: #3a3a3a;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/datatables.css",
    "content": "/*\n * This combined file was created by the DataTables downloader builder:\n *   https://datatables.net/download\n *\n * To rebuild or modify this file with the latest versions of the included\n * software please visit:\n *   https://datatables.net/download/#bs5/dt-1.13.5\n *\n * Included libraries:\n *   DataTables 1.13.5\n */\n\n@charset \"UTF-8\";\n:root {\n  --dt-row-selected: 13, 110, 253;\n  --dt-row-selected-text: 255, 255, 255;\n  --dt-row-selected-link: 9, 10, 11;\n  --dt-row-stripe: 0, 0, 0;\n  --dt-row-hover: 0, 0, 0;\n  --dt-column-ordering: 0, 0, 0;\n  --dt-html-background: white;\n}\n:root.dark {\n  --dt-html-background: rgb(33, 37, 41);\n}\n\ntable.dataTable td.dt-control {\n  text-align: center;\n  cursor: pointer;\n}\ntable.dataTable td.dt-control:before {\n  display: inline-block;\n  color: rgba(0, 0, 0, 0.5);\n  content: \"►\";\n}\ntable.dataTable tr.dt-hasChild td.dt-control:before {\n  content: \"▼\";\n}\n\nhtml.dark table.dataTable td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\nhtml.dark table.dataTable tr.dt-hasChild td.dt-control:before {\n  color: rgba(255, 255, 255, 0.5);\n}\n\ntable.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,\ntable.dataTable thead > tr > td.sorting,\ntable.dataTable thead > tr > td.sorting_asc,\ntable.dataTable thead > tr > td.sorting_desc,\ntable.dataTable thead > tr > td.sorting_asc_disabled,\ntable.dataTable thead > tr > td.sorting_desc_disabled {\n  cursor: pointer;\n  position: relative;\n  padding-right: 26px;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  position: absolute;\n  display: block;\n  opacity: 0.125;\n  right: 10px;\n  line-height: 9px;\n  font-size: 0.8em;\n}\ntable.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,\ntable.dataTable thead > tr > td.sorting:before,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:before,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:before {\n  bottom: 50%;\n  content: \"▲\";\n  content: \"▲\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting:after,\ntable.dataTable thead > tr > td.sorting_asc:after,\ntable.dataTable thead > tr > td.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:after,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after {\n  top: 50%;\n  content: \"▼\";\n  content: \"▼\"/\"\";\n}\ntable.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,\ntable.dataTable thead > tr > td.sorting_asc:before,\ntable.dataTable thead > tr > td.sorting_desc:after {\n  opacity: 0.6;\n}\ntable.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,\ntable.dataTable thead > tr > td.sorting_desc_disabled:after,\ntable.dataTable thead > tr > td.sorting_asc_disabled:before {\n  display: none;\n}\ntable.dataTable thead > tr > th:active,\ntable.dataTable thead > tr > td:active {\n  outline: none;\n}\n\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:before,\ndiv.dataTables_scrollBody > table.dataTable > thead > tr > td:after {\n  display: none;\n}\n\ndiv.dataTables_processing {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 200px;\n  margin-left: -100px;\n  margin-top: -26px;\n  text-align: center;\n  padding: 2px;\n}\ndiv.dataTables_processing > div:last-child {\n  position: relative;\n  width: 80px;\n  height: 15px;\n  margin: 1em auto;\n}\ndiv.dataTables_processing > div:last-child > div {\n  position: absolute;\n  top: 0;\n  width: 13px;\n  height: 13px;\n  border-radius: 50%;\n  background: rgb(13, 110, 253);\n  background: rgb(var(--dt-row-selected));\n  animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(1) {\n  left: 8px;\n  animation: datatables-loader-1 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(2) {\n  left: 8px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(3) {\n  left: 32px;\n  animation: datatables-loader-2 0.6s infinite;\n}\ndiv.dataTables_processing > div:last-child > div:nth-child(4) {\n  left: 56px;\n  animation: datatables-loader-3 0.6s infinite;\n}\n\n@keyframes datatables-loader-1 {\n  0% {\n    transform: scale(0);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n@keyframes datatables-loader-3 {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0);\n  }\n}\n@keyframes datatables-loader-2 {\n  0% {\n    transform: translate(0, 0);\n  }\n  100% {\n    transform: translate(24px, 0);\n  }\n}\ntable.dataTable.nowrap th, table.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable th.dt-left,\ntable.dataTable td.dt-left {\n  text-align: left;\n}\ntable.dataTable th.dt-center,\ntable.dataTable td.dt-center,\ntable.dataTable td.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable th.dt-right,\ntable.dataTable td.dt-right {\n  text-align: right;\n}\ntable.dataTable th.dt-justify,\ntable.dataTable td.dt-justify {\n  text-align: justify;\n}\ntable.dataTable th.dt-nowrap,\ntable.dataTable td.dt-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable thead th,\ntable.dataTable thead td,\ntable.dataTable tfoot th,\ntable.dataTable tfoot td {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-left,\ntable.dataTable thead td.dt-head-left,\ntable.dataTable tfoot th.dt-head-left,\ntable.dataTable tfoot td.dt-head-left {\n  text-align: left;\n}\ntable.dataTable thead th.dt-head-center,\ntable.dataTable thead td.dt-head-center,\ntable.dataTable tfoot th.dt-head-center,\ntable.dataTable tfoot td.dt-head-center {\n  text-align: center;\n}\ntable.dataTable thead th.dt-head-right,\ntable.dataTable thead td.dt-head-right,\ntable.dataTable tfoot th.dt-head-right,\ntable.dataTable tfoot td.dt-head-right {\n  text-align: right;\n}\ntable.dataTable thead th.dt-head-justify,\ntable.dataTable thead td.dt-head-justify,\ntable.dataTable tfoot th.dt-head-justify,\ntable.dataTable tfoot td.dt-head-justify {\n  text-align: justify;\n}\ntable.dataTable thead th.dt-head-nowrap,\ntable.dataTable thead td.dt-head-nowrap,\ntable.dataTable tfoot th.dt-head-nowrap,\ntable.dataTable tfoot td.dt-head-nowrap {\n  white-space: nowrap;\n}\ntable.dataTable tbody th.dt-body-left,\ntable.dataTable tbody td.dt-body-left {\n  text-align: left;\n}\ntable.dataTable tbody th.dt-body-center,\ntable.dataTable tbody td.dt-body-center {\n  text-align: center;\n}\ntable.dataTable tbody th.dt-body-right,\ntable.dataTable tbody td.dt-body-right {\n  text-align: right;\n}\ntable.dataTable tbody th.dt-body-justify,\ntable.dataTable tbody td.dt-body-justify {\n  text-align: justify;\n}\ntable.dataTable tbody th.dt-body-nowrap,\ntable.dataTable tbody td.dt-body-nowrap {\n  white-space: nowrap;\n}\n\n/*! Bootstrap 5 integration for DataTables\n *\n * ©2020 SpryMedia Ltd, all rights reserved.\n * License: MIT datatables.net/license/mit\n */\ntable.dataTable {\n  clear: both;\n  margin-top: 6px !important;\n  margin-bottom: 6px !important;\n  max-width: none !important;\n  border-collapse: separate !important;\n  border-spacing: 0;\n}\ntable.dataTable td,\ntable.dataTable th {\n  -webkit-box-sizing: content-box;\n  box-sizing: content-box;\n}\ntable.dataTable td.dataTables_empty,\ntable.dataTable th.dataTables_empty {\n  text-align: center;\n}\ntable.dataTable.nowrap th,\ntable.dataTable.nowrap td {\n  white-space: nowrap;\n}\ntable.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {\n  box-shadow: none;\n}\ntable.dataTable > tbody > tr {\n  background-color: transparent;\n}\ntable.dataTable > tbody > tr.selected > * {\n  box-shadow: inset 0 0 0 9999px rgb(13, 110, 253);\n  box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));\n  color: rgb(255, 255, 255);\n  color: rgb(var(--dt-row-selected-text));\n}\ntable.dataTable > tbody > tr.selected a {\n  color: rgb(9, 10, 11);\n  color: rgb(var(--dt-row-selected-link));\n}\ntable.dataTable.table-striped > tbody > tr.odd > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);\n}\ntable.dataTable.table-striped > tbody > tr.odd.selected > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);\n}\ntable.dataTable.table-hover > tbody > tr:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);\n}\ntable.dataTable.table-hover > tbody > tr.selected:hover > * {\n  box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);\n  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);\n}\n\ndiv.dataTables_wrapper div.dataTables_length label {\n  font-weight: normal;\n  text-align: left;\n  white-space: nowrap;\n}\ndiv.dataTables_wrapper div.dataTables_length select {\n  width: auto;\n  display: inline-block;\n}\ndiv.dataTables_wrapper div.dataTables_filter {\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_filter label {\n  font-weight: normal;\n  white-space: nowrap;\n  text-align: left;\n}\ndiv.dataTables_wrapper div.dataTables_filter input {\n  margin-left: 0.5em;\n  display: inline-block;\n  width: auto;\n}\ndiv.dataTables_wrapper div.dataTables_info {\n  padding-top: 0.85em;\n}\ndiv.dataTables_wrapper div.dataTables_paginate {\n  margin: 0;\n  white-space: nowrap;\n  text-align: right;\n}\ndiv.dataTables_wrapper div.dataTables_paginate ul.pagination {\n  margin: 2px 0;\n  white-space: nowrap;\n  justify-content: flex-end;\n}\ndiv.dataTables_wrapper div.dt-row {\n  position: relative;\n}\n\ndiv.dataTables_scrollHead table.dataTable {\n  margin-bottom: 0 !important;\n}\n\ndiv.dataTables_scrollBody > table {\n  border-top: none;\n  margin-top: 0 !important;\n  margin-bottom: 0 !important;\n}\ndiv.dataTables_scrollBody > table > thead .sorting:before,\ndiv.dataTables_scrollBody > table > thead .sorting_asc:before,\ndiv.dataTables_scrollBody > table > thead .sorting_desc:before,\ndiv.dataTables_scrollBody > table > thead .sorting:after,\ndiv.dataTables_scrollBody > table > thead .sorting_asc:after,\ndiv.dataTables_scrollBody > table > thead .sorting_desc:after {\n  display: none;\n}\ndiv.dataTables_scrollBody > table > tbody tr:first-child th,\ndiv.dataTables_scrollBody > table > tbody tr:first-child td {\n  border-top: none;\n}\n\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner {\n  box-sizing: content-box;\n}\ndiv.dataTables_scrollFoot > .dataTables_scrollFootInner > table {\n  margin-top: 0 !important;\n  border-top: none;\n}\n\n@media screen and (max-width: 767px) {\n  div.dataTables_wrapper div.dataTables_length,\n  div.dataTables_wrapper div.dataTables_filter,\n  div.dataTables_wrapper div.dataTables_info,\n  div.dataTables_wrapper div.dataTables_paginate {\n    text-align: center;\n  }\n  div.dataTables_wrapper div.dataTables_paginate ul.pagination {\n    justify-content: center !important;\n  }\n}\ntable.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {\n  padding-right: 20px;\n}\n\ntable.table-bordered.dataTable {\n  border-right-width: 0;\n}\ntable.table-bordered.dataTable thead tr:first-child th,\ntable.table-bordered.dataTable thead tr:first-child td {\n  border-top-width: 1px;\n}\ntable.table-bordered.dataTable th,\ntable.table-bordered.dataTable td {\n  border-left-width: 0;\n}\ntable.table-bordered.dataTable th:first-child, table.table-bordered.dataTable th:first-child,\ntable.table-bordered.dataTable td:first-child,\ntable.table-bordered.dataTable td:first-child {\n  border-left-width: 1px;\n}\ntable.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,\ntable.table-bordered.dataTable td:last-child,\ntable.table-bordered.dataTable td:last-child {\n  border-right-width: 1px;\n}\ntable.table-bordered.dataTable th,\ntable.table-bordered.dataTable td {\n  border-bottom-width: 1px;\n}\n\ndiv.dataTables_scrollHead table.table-bordered {\n  border-bottom-width: 0;\n}\n\ndiv.table-responsive > div.dataTables_wrapper > div.row {\n  margin: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {\n  padding-left: 0;\n}\ndiv.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {\n  padding-right: 0;\n}\n\n:root[data-bs-theme=dark] {\n  --dt-row-hover: 255, 255, 255;\n  --dt-row-stripe: 255, 255, 255;\n  --dt-column-ordering: 255, 255, 255;\n}\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/datatables.js",
    "content": "/*\n * This combined file was created by the DataTables downloader builder:\n *   https://datatables.net/download\n *\n * To rebuild or modify this file with the latest versions of the included\n * software please visit:\n *   https://datatables.net/download/#bs5/dt-1.13.5\n *\n * Included libraries:\n *   DataTables 1.13.5\n */\n\n/*! DataTables 1.13.5\n * ©2008-2023 SpryMedia Ltd - datatables.net/license\n */\n\n/**\n * @summary     DataTables\n * @description Paginate, search and order HTML tables\n * @version     1.13.4\n * @author      SpryMedia Ltd\n * @contact     www.datatables.net\n * @copyright   SpryMedia Ltd.\n *\n * This source file is free software, available under the following license:\n *   MIT license - http://datatables.net/license\n *\n * This source file is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.\n *\n * For details please refer to: http://www.datatables.net\n */\n\n/*jslint evil: true, undef: true, browser: true */\n/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/\n\n(function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\t// jQuery's factory checks for a global window - if it isn't present then it\n\t\t// returns a factory function that expects the window object\n\t\tvar jq = require('jquery');\n\n\t\tif (typeof window !== 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\treturn factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\twindow.DataTable = factory( jQuery, window, document );\n\t}\n}\n(function( $, window, document, undefined ) {\n\t\"use strict\";\n\n\t\n\tvar DataTable = function ( selector, options )\n\t{\n\t\t// Check if called with a window or jQuery object for DOM less applications\n\t\t// This is for backwards compatibility\n\t\tif (DataTable.factory(selector, options)) {\n\t\t\treturn DataTable;\n\t\t}\n\t\n\t\t// When creating with `new`, create a new DataTable, returning the API instance\n\t\tif (this instanceof DataTable) {\n\t\t\treturn $(selector).DataTable(options);\n\t\t}\n\t\telse {\n\t\t\t// Argument switching\n\t\t\toptions = selector;\n\t\t}\n\t\n\t\t/**\n\t\t * Perform a jQuery selector action on the table's TR elements (from the tbody) and\n\t\t * return the resulting jQuery object.\n\t\t *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on\n\t\t *  @param {object} [oOpts] Optional parameters for modifying the rows to be included\n\t\t *  @param {string} [oOpts.filter=none] Select TR elements that meet the current filter\n\t\t *    criterion (\"applied\") or all TR elements (i.e. no filter).\n\t\t *  @param {string} [oOpts.order=current] Order of the TR elements in the processed array.\n\t\t *    Can be either 'current', whereby the current sorting of the table is used, or\n\t\t *    'original' whereby the original order the data was read into the table is used.\n\t\t *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page\n\t\t *    (\"current\") or not (\"all\"). If 'current' is given, then order is assumed to be\n\t\t *    'current' and filter is 'applied', regardless of what they might be given as.\n\t\t *  @returns {object} jQuery object, filtered by the given selector.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Highlight every second row\n\t\t *      oTable.$('tr:odd').css('backgroundColor', 'blue');\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Filter to rows with 'Webkit' in them, add a background colour and then\n\t\t *      // remove the filter, thus highlighting the 'Webkit' rows only.\n\t\t *      oTable.fnFilter('Webkit');\n\t\t *      oTable.$('tr', {\"search\": \"applied\"}).css('backgroundColor', 'blue');\n\t\t *      oTable.fnFilter('');\n\t\t *    } );\n\t\t */\n\t\tthis.$ = function ( sSelector, oOpts )\n\t\t{\n\t\t\treturn this.api(true).$( sSelector, oOpts );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Almost identical to $ in operation, but in this case returns the data for the matched\n\t\t * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes\n\t\t * rather than any descendants, so the data can be obtained for the row/cell. If matching\n\t\t * rows are found, the data returned is the original data array/object that was used to\n\t\t * create the row (or a generated array if from a DOM source).\n\t\t *\n\t\t * This method is often useful in-combination with $ where both functions are given the\n\t\t * same parameters and the array indexes will match identically.\n\t\t *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on\n\t\t *  @param {object} [oOpts] Optional parameters for modifying the rows to be included\n\t\t *  @param {string} [oOpts.filter=none] Select elements that meet the current filter\n\t\t *    criterion (\"applied\") or all elements (i.e. no filter).\n\t\t *  @param {string} [oOpts.order=current] Order of the data in the processed array.\n\t\t *    Can be either 'current', whereby the current sorting of the table is used, or\n\t\t *    'original' whereby the original order the data was read into the table is used.\n\t\t *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page\n\t\t *    (\"current\") or not (\"all\"). If 'current' is given, then order is assumed to be\n\t\t *    'current' and filter is 'applied', regardless of what they might be given as.\n\t\t *  @returns {array} Data for the matched elements. If any elements, as a result of the\n\t\t *    selector, were not TR, TD or TH elements in the DataTable, they will have a null\n\t\t *    entry in the array.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Get the data from the first row in the table\n\t\t *      var data = oTable._('tr:first');\n\t\t *\n\t\t *      // Do something useful with the data\n\t\t *      alert( \"First cell is: \"+data[0] );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Filter to 'Webkit' and get all data for\n\t\t *      oTable.fnFilter('Webkit');\n\t\t *      var data = oTable._('tr', {\"search\": \"applied\"});\n\t\t *\n\t\t *      // Do something with the data\n\t\t *      alert( data.length+\" rows matched the search\" );\n\t\t *    } );\n\t\t */\n\t\tthis._ = function ( sSelector, oOpts )\n\t\t{\n\t\t\treturn this.api(true).rows( sSelector, oOpts ).data();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Create a DataTables Api instance, with the currently selected tables for\n\t\t * the Api's context.\n\t\t * @param {boolean} [traditional=false] Set the API instance's context to be\n\t\t *   only the table referred to by the `DataTable.ext.iApiIndex` option, as was\n\t\t *   used in the API presented by DataTables 1.9- (i.e. the traditional mode),\n\t\t *   or if all tables captured in the jQuery object should be used.\n\t\t * @return {DataTables.Api}\n\t\t */\n\t\tthis.api = function ( traditional )\n\t\t{\n\t\t\treturn traditional ?\n\t\t\t\tnew _Api(\n\t\t\t\t\t_fnSettingsFromNode( this[ _ext.iApiIndex ] )\n\t\t\t\t) :\n\t\t\t\tnew _Api( this );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Add a single new row or multiple rows of data to the table. Please note\n\t\t * that this is suitable for client-side processing only - if you are using\n\t\t * server-side processing (i.e. \"bServerSide\": true), then to add data, you\n\t\t * must add it to the data source, i.e. the server-side, through an Ajax call.\n\t\t *  @param {array|object} data The data to be added to the table. This can be:\n\t\t *    <ul>\n\t\t *      <li>1D array of data - add a single row with the data provided</li>\n\t\t *      <li>2D array of arrays - add multiple rows in a single call</li>\n\t\t *      <li>object - data object when using <i>mData</i></li>\n\t\t *      <li>array of objects - multiple data objects when using <i>mData</i></li>\n\t\t *    </ul>\n\t\t *  @param {bool} [redraw=true] redraw the table or not\n\t\t *  @returns {array} An array of integers, representing the list of indexes in\n\t\t *    <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to\n\t\t *    the table.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    // Global var for counter\n\t\t *    var giCount = 2;\n\t\t *\n\t\t *    $(document).ready(function() {\n\t\t *      $('#example').dataTable();\n\t\t *    } );\n\t\t *\n\t\t *    function fnClickAddRow() {\n\t\t *      $('#example').dataTable().fnAddData( [\n\t\t *        giCount+\".1\",\n\t\t *        giCount+\".2\",\n\t\t *        giCount+\".3\",\n\t\t *        giCount+\".4\" ]\n\t\t *      );\n\t\t *\n\t\t *      giCount++;\n\t\t *    }\n\t\t */\n\t\tthis.fnAddData = function( data, redraw )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\t/* Check if we want to add multiple rows or not */\n\t\t\tvar rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ?\n\t\t\t\tapi.rows.add( data ) :\n\t\t\t\tapi.row.add( data );\n\t\t\n\t\t\tif ( redraw === undefined || redraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t\n\t\t\treturn rows.flatten().toArray();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * This function will make DataTables recalculate the column sizes, based on the data\n\t\t * contained in the table and the sizes applied to the columns (in the DOM, CSS or\n\t\t * through the sWidth parameter). This can be useful when the width of the table's\n\t\t * parent element changes (for example a window resize).\n\t\t *  @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable( {\n\t\t *        \"sScrollY\": \"200px\",\n\t\t *        \"bPaginate\": false\n\t\t *      } );\n\t\t *\n\t\t *      $(window).on('resize', function () {\n\t\t *        oTable.fnAdjustColumnSizing();\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\tthis.fnAdjustColumnSizing = function ( bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).columns.adjust();\n\t\t\tvar settings = api.settings()[0];\n\t\t\tvar scroll = settings.oScroll;\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw( false );\n\t\t\t}\n\t\t\telse if ( scroll.sX !== \"\" || scroll.sY !== \"\" ) {\n\t\t\t\t/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */\n\t\t\t\t_fnScrollDraw( settings );\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Quickly and simply clear a table\n\t\t *  @param {bool} [bRedraw=true] redraw the table or not\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)\n\t\t *      oTable.fnClearTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnClearTable = function( bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).clear();\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * The exact opposite of 'opening' a row, this function will close any rows which\n\t\t * are currently 'open'.\n\t\t *  @param {node} nTr the table row to 'close'\n\t\t *  @returns {int} 0 on success, or 1 if failed (can't find the row)\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable;\n\t\t *\n\t\t *      // 'open' an information row when a row is clicked on\n\t\t *      $('#example tbody tr').click( function () {\n\t\t *        if ( oTable.fnIsOpen(this) ) {\n\t\t *          oTable.fnClose( this );\n\t\t *        } else {\n\t\t *          oTable.fnOpen( this, \"Temporary row opened\", \"info_row\" );\n\t\t *        }\n\t\t *      } );\n\t\t *\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnClose = function( nTr )\n\t\t{\n\t\t\tthis.api( true ).row( nTr ).child.hide();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Remove a row for the table\n\t\t *  @param {mixed} target The index of the row from aoData to be deleted, or\n\t\t *    the TR element you want to delete\n\t\t *  @param {function|null} [callBack] Callback function\n\t\t *  @param {bool} [redraw=true] Redraw the table or not\n\t\t *  @returns {array} The row that was deleted\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Immediately remove the first row\n\t\t *      oTable.fnDeleteRow( 0 );\n\t\t *    } );\n\t\t */\n\t\tthis.fnDeleteRow = function( target, callback, redraw )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\tvar rows = api.rows( target );\n\t\t\tvar settings = rows.settings()[0];\n\t\t\tvar data = settings.aoData[ rows[0][0] ];\n\t\t\n\t\t\trows.remove();\n\t\t\n\t\t\tif ( callback ) {\n\t\t\t\tcallback.call( this, settings, data );\n\t\t\t}\n\t\t\n\t\t\tif ( redraw === undefined || redraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t\n\t\t\treturn data;\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Restore the table to it's original state in the DOM by removing all of DataTables\n\t\t * enhancements, alterations to the DOM structure of the table and event listeners.\n\t\t *  @param {boolean} [remove=false] Completely remove the table from the DOM\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      // This example is fairly pointless in reality, but shows how fnDestroy can be used\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      oTable.fnDestroy();\n\t\t *    } );\n\t\t */\n\t\tthis.fnDestroy = function ( remove )\n\t\t{\n\t\t\tthis.api( true ).destroy( remove );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Redraw the table\n\t\t *  @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Re-draw the table - you wouldn't want to do it here, but it's an example :-)\n\t\t *      oTable.fnDraw();\n\t\t *    } );\n\t\t */\n\t\tthis.fnDraw = function( complete )\n\t\t{\n\t\t\t// Note that this isn't an exact match to the old call to _fnDraw - it takes\n\t\t\t// into account the new data, but can hold position.\n\t\t\tthis.api( true ).draw( complete );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Filter the input based on data\n\t\t *  @param {string} sInput String to filter the table on\n\t\t *  @param {int|null} [iColumn] Column to limit filtering to\n\t\t *  @param {bool} [bRegex=false] Treat as regular expression or not\n\t\t *  @param {bool} [bSmart=true] Perform smart filtering or not\n\t\t *  @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)\n\t\t *  @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Sometime later - filter...\n\t\t *      oTable.fnFilter( 'test string' );\n\t\t *    } );\n\t\t */\n\t\tthis.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\tif ( iColumn === null || iColumn === undefined ) {\n\t\t\t\tapi.search( sInput, bRegex, bSmart, bCaseInsensitive );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tapi.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );\n\t\t\t}\n\t\t\n\t\t\tapi.draw();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get the data for the whole table, an individual row or an individual cell based on the\n\t\t * provided parameters.\n\t\t *  @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as\n\t\t *    a TR node then the data source for the whole row will be returned. If given as a\n\t\t *    TD/TH cell node then iCol will be automatically calculated and the data for the\n\t\t *    cell returned. If given as an integer, then this is treated as the aoData internal\n\t\t *    data index for the row (see fnGetPosition) and the data for that row used.\n\t\t *  @param {int} [col] Optional column index that you want the data of.\n\t\t *  @returns {array|object|string} If mRow is undefined, then the data for all rows is\n\t\t *    returned. If mRow is defined, just data for that row, and is iCol is\n\t\t *    defined, only data for the designated cell is returned.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    // Row data\n\t\t *    $(document).ready(function() {\n\t\t *      oTable = $('#example').dataTable();\n\t\t *\n\t\t *      oTable.$('tr').click( function () {\n\t\t *        var data = oTable.fnGetData( this );\n\t\t *        // ... do something with the array / object of data for the row\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Individual cell data\n\t\t *    $(document).ready(function() {\n\t\t *      oTable = $('#example').dataTable();\n\t\t *\n\t\t *      oTable.$('td').click( function () {\n\t\t *        var sData = oTable.fnGetData( this );\n\t\t *        alert( 'The cell clicked on had the value of '+sData );\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\tthis.fnGetData = function( src, col )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\tif ( src !== undefined ) {\n\t\t\t\tvar type = src.nodeName ? src.nodeName.toLowerCase() : '';\n\t\t\n\t\t\t\treturn col !== undefined || type == 'td' || type == 'th' ?\n\t\t\t\t\tapi.cell( src, col ).data() :\n\t\t\t\t\tapi.row( src ).data() || null;\n\t\t\t}\n\t\t\n\t\t\treturn api.data().toArray();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get an array of the TR nodes that are used in the table's body. Note that you will\n\t\t * typically want to use the '$' API method in preference to this as it is more\n\t\t * flexible.\n\t\t *  @param {int} [iRow] Optional row index for the TR element you want\n\t\t *  @returns {array|node} If iRow is undefined, returns an array of all TR elements\n\t\t *    in the table's body, or iRow is defined, just the TR element requested.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Get the nodes from the table\n\t\t *      var nNodes = oTable.fnGetNodes( );\n\t\t *    } );\n\t\t */\n\t\tthis.fnGetNodes = function( iRow )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\treturn iRow !== undefined ?\n\t\t\t\tapi.row( iRow ).node() :\n\t\t\t\tapi.rows().nodes().flatten().toArray();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get the array indexes of a particular cell from it's DOM element\n\t\t * and column index including hidden columns\n\t\t *  @param {node} node this can either be a TR, TD or TH in the table's body\n\t\t *  @returns {int} If nNode is given as a TR, then a single index is returned, or\n\t\t *    if given as a cell, an array of [row index, column index (visible),\n\t\t *    column index (all)] is given.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      $('#example tbody td').click( function () {\n\t\t *        // Get the position of the current data from the node\n\t\t *        var aPos = oTable.fnGetPosition( this );\n\t\t *\n\t\t *        // Get the data array for this row\n\t\t *        var aData = oTable.fnGetData( aPos[0] );\n\t\t *\n\t\t *        // Update the data array and return the value\n\t\t *        aData[ aPos[1] ] = 'clicked';\n\t\t *        this.innerHTML = 'clicked';\n\t\t *      } );\n\t\t *\n\t\t *      // Init DataTables\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnGetPosition = function( node )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\tvar nodeName = node.nodeName.toUpperCase();\n\t\t\n\t\t\tif ( nodeName == 'TR' ) {\n\t\t\t\treturn api.row( node ).index();\n\t\t\t}\n\t\t\telse if ( nodeName == 'TD' || nodeName == 'TH' ) {\n\t\t\t\tvar cell = api.cell( node ).index();\n\t\t\n\t\t\t\treturn [\n\t\t\t\t\tcell.row,\n\t\t\t\t\tcell.columnVisible,\n\t\t\t\t\tcell.column\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Check to see if a row is 'open' or not.\n\t\t *  @param {node} nTr the table row to check\n\t\t *  @returns {boolean} true if the row is currently open, false otherwise\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable;\n\t\t *\n\t\t *      // 'open' an information row when a row is clicked on\n\t\t *      $('#example tbody tr').click( function () {\n\t\t *        if ( oTable.fnIsOpen(this) ) {\n\t\t *          oTable.fnClose( this );\n\t\t *        } else {\n\t\t *          oTable.fnOpen( this, \"Temporary row opened\", \"info_row\" );\n\t\t *        }\n\t\t *      } );\n\t\t *\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnIsOpen = function( nTr )\n\t\t{\n\t\t\treturn this.api( true ).row( nTr ).child.isShown();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * This function will place a new row directly after a row which is currently\n\t\t * on display on the page, with the HTML contents that is passed into the\n\t\t * function. This can be used, for example, to ask for confirmation that a\n\t\t * particular record should be deleted.\n\t\t *  @param {node} nTr The table row to 'open'\n\t\t *  @param {string|node|jQuery} mHtml The HTML to put into the row\n\t\t *  @param {string} sClass Class to give the new TD cell\n\t\t *  @returns {node} The row opened. Note that if the table row passed in as the\n\t\t *    first parameter, is not found in the table, this method will silently\n\t\t *    return.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable;\n\t\t *\n\t\t *      // 'open' an information row when a row is clicked on\n\t\t *      $('#example tbody tr').click( function () {\n\t\t *        if ( oTable.fnIsOpen(this) ) {\n\t\t *          oTable.fnClose( this );\n\t\t *        } else {\n\t\t *          oTable.fnOpen( this, \"Temporary row opened\", \"info_row\" );\n\t\t *        }\n\t\t *      } );\n\t\t *\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnOpen = function( nTr, mHtml, sClass )\n\t\t{\n\t\t\treturn this.api( true )\n\t\t\t\t.row( nTr )\n\t\t\t\t.child( mHtml, sClass )\n\t\t\t\t.show()\n\t\t\t\t.child()[0];\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Change the pagination - provides the internal logic for pagination in a simple API\n\t\t * function. With this function you can have a DataTables table go to the next,\n\t\t * previous, first or last pages.\n\t\t *  @param {string|int} mAction Paging action to take: \"first\", \"previous\", \"next\" or \"last\"\n\t\t *    or page number to jump to (integer), note that page 0 is the first page.\n\t\t *  @param {bool} [bRedraw=true] Redraw the table or not\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      oTable.fnPageChange( 'next' );\n\t\t *    } );\n\t\t */\n\t\tthis.fnPageChange = function ( mAction, bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).page( mAction );\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw(false);\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Show a particular column\n\t\t *  @param {int} iCol The column whose display should be changed\n\t\t *  @param {bool} bShow Show (true) or hide (false) the column\n\t\t *  @param {bool} [bRedraw=true] Redraw the table or not\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Hide the second column after initialisation\n\t\t *      oTable.fnSetColumnVis( 1, false );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSetColumnVis = function ( iCol, bShow, bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).column( iCol ).visible( bShow );\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.columns.adjust().draw();\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get the settings for a particular table for external manipulation\n\t\t *  @returns {object} DataTables settings object. See\n\t\t *    {@link DataTable.models.oSettings}\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      var oSettings = oTable.fnSettings();\n\t\t *\n\t\t *      // Show an example parameter from the settings\n\t\t *      alert( oSettings._iDisplayStart );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSettings = function()\n\t\t{\n\t\t\treturn _fnSettingsFromNode( this[_ext.iApiIndex] );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Sort the table by a particular column\n\t\t *  @param {int} iCol the data index to sort on. Note that this will not match the\n\t\t *    'display index' if you have hidden data entries\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Sort immediately with columns 0 and 1\n\t\t *      oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSort = function( aaSort )\n\t\t{\n\t\t\tthis.api( true ).order( aaSort ).draw();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Attach a sort listener to an element for a given column\n\t\t *  @param {node} nNode the element to attach the sort listener to\n\t\t *  @param {int} iColumn the column that a click on this node will sort on\n\t\t *  @param {function} [fnCallback] callback function when sort is run\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Sort on column 1, when 'sorter' is clicked on\n\t\t *      oTable.fnSortListener( document.getElementById('sorter'), 1 );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSortListener = function( nNode, iColumn, fnCallback )\n\t\t{\n\t\t\tthis.api( true ).order.listener( nNode, iColumn, fnCallback );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Update a table cell or row - this method will accept either a single value to\n\t\t * update the cell with, an array of values with one element for each column or\n\t\t * an object in the same format as the original data source. The function is\n\t\t * self-referencing in order to make the multi column updates easier.\n\t\t *  @param {object|array|string} mData Data to update the cell/row with\n\t\t *  @param {node|int} mRow TR element you want to update or the aoData index\n\t\t *  @param {int} [iColumn] The column to update, give as null or undefined to\n\t\t *    update a whole row.\n\t\t *  @param {bool} [bRedraw=true] Redraw the table or not\n\t\t *  @param {bool} [bAction=true] Perform pre-draw actions or not\n\t\t *  @returns {int} 0 on success, 1 on error\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell\n\t\t *      oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row\n\t\t *    } );\n\t\t */\n\t\tthis.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\tif ( iColumn === undefined || iColumn === null ) {\n\t\t\t\tapi.row( mRow ).data( mData );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tapi.cell( mRow, iColumn ).data( mData );\n\t\t\t}\n\t\t\n\t\t\tif ( bAction === undefined || bAction ) {\n\t\t\t\tapi.columns.adjust();\n\t\t\t}\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t\treturn 0;\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Provide a common method for plug-ins to check the version of DataTables being used, in order\n\t\t * to ensure compatibility.\n\t\t *  @param {string} sVersion Version string to check for, in the format \"X.Y.Z\". Note that the\n\t\t *    formats \"X\" and \"X.Y\" are also acceptable.\n\t\t *  @returns {boolean} true if this version of DataTables is greater or equal to the required\n\t\t *    version, or false if this version of DataTales is not suitable\n\t\t *  @method\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      alert( oTable.fnVersionCheck( '1.9.0' ) );\n\t\t *    } );\n\t\t */\n\t\tthis.fnVersionCheck = _ext.fnVersionCheck;\n\t\t\n\t\n\t\tvar _that = this;\n\t\tvar emptyInit = options === undefined;\n\t\tvar len = this.length;\n\t\n\t\tif ( emptyInit ) {\n\t\t\toptions = {};\n\t\t}\n\t\n\t\tthis.oApi = this.internal = _ext.internal;\n\t\n\t\t// Extend with old style plug-in API methods\n\t\tfor ( var fn in DataTable.ext.internal ) {\n\t\t\tif ( fn ) {\n\t\t\t\tthis[fn] = _fnExternApiFunc(fn);\n\t\t\t}\n\t\t}\n\t\n\t\tthis.each(function() {\n\t\t\t// For each initialisation we want to give it a clean initialisation\n\t\t\t// object that can be bashed around\n\t\t\tvar o = {};\n\t\t\tvar oInit = len > 1 ? // optimisation for single table case\n\t\t\t\t_fnExtend( o, options, true ) :\n\t\t\t\toptions;\n\t\n\t\t\t/*global oInit,_that,emptyInit*/\n\t\t\tvar i=0, iLen, j, jLen, k, kLen;\n\t\t\tvar sId = this.getAttribute( 'id' );\n\t\t\tvar bInitHandedOff = false;\n\t\t\tvar defaults = DataTable.defaults;\n\t\t\tvar $this = $(this);\n\t\t\t\n\t\t\t\n\t\t\t/* Sanity check */\n\t\t\tif ( this.nodeName.toLowerCase() != 'table' )\n\t\t\t{\n\t\t\t\t_fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t/* Backwards compatibility for the defaults */\n\t\t\t_fnCompatOpts( defaults );\n\t\t\t_fnCompatCols( defaults.column );\n\t\t\t\n\t\t\t/* Convert the camel-case defaults to Hungarian */\n\t\t\t_fnCamelToHungarian( defaults, defaults, true );\n\t\t\t_fnCamelToHungarian( defaults.column, defaults.column, true );\n\t\t\t\n\t\t\t/* Setting up the initialisation object */\n\t\t\t_fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t/* Check to see if we are re-initialising a table */\n\t\t\tvar allSettings = DataTable.settings;\n\t\t\tfor ( i=0, iLen=allSettings.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\tvar s = allSettings[i];\n\t\t\t\n\t\t\t\t/* Base check on table node */\n\t\t\t\tif (\n\t\t\t\t\ts.nTable == this ||\n\t\t\t\t\t(s.nTHead && s.nTHead.parentNode == this) ||\n\t\t\t\t\t(s.nTFoot && s.nTFoot.parentNode == this)\n\t\t\t\t) {\n\t\t\t\t\tvar bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;\n\t\t\t\t\tvar bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;\n\t\t\t\n\t\t\t\t\tif ( emptyInit || bRetrieve )\n\t\t\t\t\t{\n\t\t\t\t\t\treturn s.oInstance;\n\t\t\t\t\t}\n\t\t\t\t\telse if ( bDestroy )\n\t\t\t\t\t{\n\t\t\t\t\t\ts.oInstance.fnDestroy();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t_fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* If the element we are initialising has the same ID as a table which was previously\n\t\t\t\t * initialised, but the table nodes don't match (from before) then we destroy the old\n\t\t\t\t * instance by simply deleting it. This is under the assumption that the table has been\n\t\t\t\t * destroyed by other methods. Anyone using non-id selectors will need to do this manually\n\t\t\t\t */\n\t\t\t\tif ( s.sTableId == this.id )\n\t\t\t\t{\n\t\t\t\t\tallSettings.splice( i, 1 );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t/* Ensure the table has an ID - required for accessibility */\n\t\t\tif ( sId === null || sId === \"\" )\n\t\t\t{\n\t\t\t\tsId = \"DataTables_Table_\"+(DataTable.ext._unique++);\n\t\t\t\tthis.id = sId;\n\t\t\t}\n\t\t\t\n\t\t\t/* Create the settings object for this table and set some of the default parameters */\n\t\t\tvar oSettings = $.extend( true, {}, DataTable.models.oSettings, {\n\t\t\t\t\"sDestroyWidth\": $this[0].style.width,\n\t\t\t\t\"sInstance\":     sId,\n\t\t\t\t\"sTableId\":      sId\n\t\t\t} );\n\t\t\toSettings.nTable = this;\n\t\t\toSettings.oApi   = _that.internal;\n\t\t\toSettings.oInit  = oInit;\n\t\t\t\n\t\t\tallSettings.push( oSettings );\n\t\t\t\n\t\t\t// Need to add the instance after the instance after the settings object has been added\n\t\t\t// to the settings array, so we can self reference the table instance if more than one\n\t\t\toSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();\n\t\t\t\n\t\t\t// Backwards compatibility, before we apply all the defaults\n\t\t\t_fnCompatOpts( oInit );\n\t\t\t_fnLanguageCompat( oInit.oLanguage );\n\t\t\t\n\t\t\t// If the length menu is given, but the init display length is not, use the length menu\n\t\t\tif ( oInit.aLengthMenu && ! oInit.iDisplayLength )\n\t\t\t{\n\t\t\t\toInit.iDisplayLength = Array.isArray( oInit.aLengthMenu[0] ) ?\n\t\t\t\t\toInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];\n\t\t\t}\n\t\t\t\n\t\t\t// Apply the defaults and init options to make a single init object will all\n\t\t\t// options defined from defaults and instance options.\n\t\t\toInit = _fnExtend( $.extend( true, {}, defaults ), oInit );\n\t\t\t\n\t\t\t\n\t\t\t// Map the initialisation options onto the settings object\n\t\t\t_fnMap( oSettings.oFeatures, oInit, [\n\t\t\t\t\"bPaginate\",\n\t\t\t\t\"bLengthChange\",\n\t\t\t\t\"bFilter\",\n\t\t\t\t\"bSort\",\n\t\t\t\t\"bSortMulti\",\n\t\t\t\t\"bInfo\",\n\t\t\t\t\"bProcessing\",\n\t\t\t\t\"bAutoWidth\",\n\t\t\t\t\"bSortClasses\",\n\t\t\t\t\"bServerSide\",\n\t\t\t\t\"bDeferRender\"\n\t\t\t] );\n\t\t\t_fnMap( oSettings, oInit, [\n\t\t\t\t\"asStripeClasses\",\n\t\t\t\t\"ajax\",\n\t\t\t\t\"fnServerData\",\n\t\t\t\t\"fnFormatNumber\",\n\t\t\t\t\"sServerMethod\",\n\t\t\t\t\"aaSorting\",\n\t\t\t\t\"aaSortingFixed\",\n\t\t\t\t\"aLengthMenu\",\n\t\t\t\t\"sPaginationType\",\n\t\t\t\t\"sAjaxSource\",\n\t\t\t\t\"sAjaxDataProp\",\n\t\t\t\t\"iStateDuration\",\n\t\t\t\t\"sDom\",\n\t\t\t\t\"bSortCellsTop\",\n\t\t\t\t\"iTabIndex\",\n\t\t\t\t\"fnStateLoadCallback\",\n\t\t\t\t\"fnStateSaveCallback\",\n\t\t\t\t\"renderer\",\n\t\t\t\t\"searchDelay\",\n\t\t\t\t\"rowId\",\n\t\t\t\t[ \"iCookieDuration\", \"iStateDuration\" ], // backwards compat\n\t\t\t\t[ \"oSearch\", \"oPreviousSearch\" ],\n\t\t\t\t[ \"aoSearchCols\", \"aoPreSearchCols\" ],\n\t\t\t\t[ \"iDisplayLength\", \"_iDisplayLength\" ]\n\t\t\t] );\n\t\t\t_fnMap( oSettings.oScroll, oInit, [\n\t\t\t\t[ \"sScrollX\", \"sX\" ],\n\t\t\t\t[ \"sScrollXInner\", \"sXInner\" ],\n\t\t\t\t[ \"sScrollY\", \"sY\" ],\n\t\t\t\t[ \"bScrollCollapse\", \"bCollapse\" ]\n\t\t\t] );\n\t\t\t_fnMap( oSettings.oLanguage, oInit, \"fnInfoCallback\" );\n\t\t\t\n\t\t\t/* Callback functions which are array driven */\n\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback',       oInit.fnDrawCallback,      'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoServerParams',       oInit.fnServerParams,      'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoStateSaveParams',    oInit.fnStateSaveParams,   'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoStateLoadParams',    oInit.fnStateLoadParams,   'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoStateLoaded',        oInit.fnStateLoaded,       'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoRowCallback',        oInit.fnRowCallback,       'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow,        'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoHeaderCallback',     oInit.fnHeaderCallback,    'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoFooterCallback',     oInit.fnFooterCallback,    'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoInitComplete',       oInit.fnInitComplete,      'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoPreDrawCallback',    oInit.fnPreDrawCallback,   'user' );\n\t\t\t\n\t\t\toSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );\n\t\t\t\n\t\t\t/* Browser support detection */\n\t\t\t_fnBrowserDetect( oSettings );\n\t\t\t\n\t\t\tvar oClasses = oSettings.oClasses;\n\t\t\t\n\t\t\t$.extend( oClasses, DataTable.ext.classes, oInit.oClasses );\n\t\t\t$this.addClass( oClasses.sTable );\n\t\t\t\n\t\t\t\n\t\t\tif ( oSettings.iInitDisplayStart === undefined )\n\t\t\t{\n\t\t\t\t/* Display start point, taking into account the save saving */\n\t\t\t\toSettings.iInitDisplayStart = oInit.iDisplayStart;\n\t\t\t\toSettings._iDisplayStart = oInit.iDisplayStart;\n\t\t\t}\n\t\t\t\n\t\t\tif ( oInit.iDeferLoading !== null )\n\t\t\t{\n\t\t\t\toSettings.bDeferLoading = true;\n\t\t\t\tvar tmp = Array.isArray( oInit.iDeferLoading );\n\t\t\t\toSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;\n\t\t\t\toSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;\n\t\t\t}\n\t\t\t\n\t\t\t/* Language definitions */\n\t\t\tvar oLanguage = oSettings.oLanguage;\n\t\t\t$.extend( true, oLanguage, oInit.oLanguage );\n\t\t\t\n\t\t\tif ( oLanguage.sUrl )\n\t\t\t{\n\t\t\t\t/* Get the language definitions from a file - because this Ajax call makes the language\n\t\t\t\t * get async to the remainder of this function we use bInitHandedOff to indicate that\n\t\t\t\t * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor\n\t\t\t\t */\n\t\t\t\t$.ajax( {\n\t\t\t\t\tdataType: 'json',\n\t\t\t\t\turl: oLanguage.sUrl,\n\t\t\t\t\tsuccess: function ( json ) {\n\t\t\t\t\t\t_fnCamelToHungarian( defaults.oLanguage, json );\n\t\t\t\t\t\t_fnLanguageCompat( json );\n\t\t\t\t\t\t$.extend( true, oLanguage, json, oSettings.oInit.oLanguage );\n\t\t\t\n\t\t\t\t\t\t_fnCallbackFire( oSettings, null, 'i18n', [oSettings]);\n\t\t\t\t\t\t_fnInitialise( oSettings );\n\t\t\t\t\t},\n\t\t\t\t\terror: function () {\n\t\t\t\t\t\t// Error occurred loading language file, continue on as best we can\n\t\t\t\t\t\t_fnInitialise( oSettings );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\tbInitHandedOff = true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnCallbackFire( oSettings, null, 'i18n', [oSettings]);\n\t\t\t}\n\t\t\t\n\t\t\t/*\n\t\t\t * Stripes\n\t\t\t */\n\t\t\tif ( oInit.asStripeClasses === null )\n\t\t\t{\n\t\t\t\toSettings.asStripeClasses =[\n\t\t\t\t\toClasses.sStripeOdd,\n\t\t\t\t\toClasses.sStripeEven\n\t\t\t\t];\n\t\t\t}\n\t\t\t\n\t\t\t/* Remove row stripe classes if they are already on the table row */\n\t\t\tvar stripeClasses = oSettings.asStripeClasses;\n\t\t\tvar rowOne = $this.children('tbody').find('tr').eq(0);\n\t\t\tif ( $.inArray( true, $.map( stripeClasses, function(el, i) {\n\t\t\t\treturn rowOne.hasClass(el);\n\t\t\t} ) ) !== -1 ) {\n\t\t\t\t$('tbody tr', this).removeClass( stripeClasses.join(' ') );\n\t\t\t\toSettings.asDestroyStripes = stripeClasses.slice();\n\t\t\t}\n\t\t\t\n\t\t\t/*\n\t\t\t * Columns\n\t\t\t * See if we should load columns automatically or use defined ones\n\t\t\t */\n\t\t\tvar anThs = [];\n\t\t\tvar aoColumnsInit;\n\t\t\tvar nThead = this.getElementsByTagName('thead');\n\t\t\tif ( nThead.length !== 0 )\n\t\t\t{\n\t\t\t\t_fnDetectHeader( oSettings.aoHeader, nThead[0] );\n\t\t\t\tanThs = _fnGetUniqueThs( oSettings );\n\t\t\t}\n\t\t\t\n\t\t\t/* If not given a column array, generate one with nulls */\n\t\t\tif ( oInit.aoColumns === null )\n\t\t\t{\n\t\t\t\taoColumnsInit = [];\n\t\t\t\tfor ( i=0, iLen=anThs.length ; i<iLen ; i++ )\n\t\t\t\t{\n\t\t\t\t\taoColumnsInit.push( null );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\taoColumnsInit = oInit.aoColumns;\n\t\t\t}\n\t\t\t\n\t\t\t/* Add the columns */\n\t\t\tfor ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\t_fnAddColumn( oSettings, anThs ? anThs[i] : null );\n\t\t\t}\n\t\t\t\n\t\t\t/* Apply the column definitions */\n\t\t\t_fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {\n\t\t\t\t_fnColumnOptions( oSettings, iCol, oDef );\n\t\t\t} );\n\t\t\t\n\t\t\t/* HTML5 attribute detection - build an mData object automatically if the\n\t\t\t * attributes are found\n\t\t\t */\n\t\t\tif ( rowOne.length ) {\n\t\t\t\tvar a = function ( cell, name ) {\n\t\t\t\t\treturn cell.getAttribute( 'data-'+name ) !== null ? name : null;\n\t\t\t\t};\n\t\t\t\n\t\t\t\t$( rowOne[0] ).children('th, td').each( function (i, cell) {\n\t\t\t\t\tvar col = oSettings.aoColumns[i];\n\t\t\t\n\t\t\t\t\tif (! col) {\n\t\t\t\t\t\t_fnLog( oSettings, 0, 'Incorrect column count', 18 );\n\t\t\t\t\t}\n\t\t\t\n\t\t\t\t\tif ( col.mData === i ) {\n\t\t\t\t\t\tvar sort = a( cell, 'sort' ) || a( cell, 'order' );\n\t\t\t\t\t\tvar filter = a( cell, 'filter' ) || a( cell, 'search' );\n\t\t\t\n\t\t\t\t\t\tif ( sort !== null || filter !== null ) {\n\t\t\t\t\t\t\tcol.mData = {\n\t\t\t\t\t\t\t\t_:      i+'.display',\n\t\t\t\t\t\t\t\tsort:   sort !== null   ? i+'.@data-'+sort   : undefined,\n\t\t\t\t\t\t\t\ttype:   sort !== null   ? i+'.@data-'+sort   : undefined,\n\t\t\t\t\t\t\t\tfilter: filter !== null ? i+'.@data-'+filter : undefined\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcol._isArrayHost = true;\n\t\t\t\n\t\t\t\t\t\t\t_fnColumnOptions( oSettings, i );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\t\t\t\n\t\t\tvar features = oSettings.oFeatures;\n\t\t\tvar loadedInit = function () {\n\t\t\t\t/*\n\t\t\t\t * Sorting\n\t\t\t\t * @todo For modularisation (1.11) this needs to do into a sort start up handler\n\t\t\t\t */\n\t\t\t\n\t\t\t\t// If aaSorting is not defined, then we use the first indicator in asSorting\n\t\t\t\t// in case that has been altered, so the default sort reflects that option\n\t\t\t\tif ( oInit.aaSorting === undefined ) {\n\t\t\t\t\tvar sorting = oSettings.aaSorting;\n\t\t\t\t\tfor ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {\n\t\t\t\t\t\tsorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* Do a first pass on the sorting classes (allows any size changes to be taken into\n\t\t\t\t * account, and also will apply sorting disabled classes if disabled\n\t\t\t\t */\n\t\t\t\t_fnSortingClasses( oSettings );\n\t\t\t\n\t\t\t\tif ( features.bSort ) {\n\t\t\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback', function () {\n\t\t\t\t\t\tif ( oSettings.bSorted ) {\n\t\t\t\t\t\t\tvar aSort = _fnSortFlatten( oSettings );\n\t\t\t\t\t\t\tvar sortedColumns = {};\n\t\t\t\n\t\t\t\t\t\t\t$.each( aSort, function (i, val) {\n\t\t\t\t\t\t\t\tsortedColumns[ val.src ] = val.dir;\n\t\t\t\t\t\t\t} );\n\t\t\t\n\t\t\t\t\t\t\t_fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );\n\t\t\t\t\t\t\t_fnSortAria( oSettings );\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t\n\t\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback', function () {\n\t\t\t\t\tif ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {\n\t\t\t\t\t\t_fnSortingClasses( oSettings );\n\t\t\t\t\t}\n\t\t\t\t}, 'sc' );\n\t\t\t\n\t\t\t\n\t\t\t\t/*\n\t\t\t\t * Final init\n\t\t\t\t * Cache the header, body and footer as required, creating them if needed\n\t\t\t\t */\n\t\t\t\n\t\t\t\t// Work around for Webkit bug 83867 - store the caption-side before removing from doc\n\t\t\t\tvar captions = $this.children('caption').each( function () {\n\t\t\t\t\tthis._captionSide = $(this).css('caption-side');\n\t\t\t\t} );\n\t\t\t\n\t\t\t\tvar thead = $this.children('thead');\n\t\t\t\tif ( thead.length === 0 ) {\n\t\t\t\t\tthead = $('<thead/>').appendTo($this);\n\t\t\t\t}\n\t\t\t\toSettings.nTHead = thead[0];\n\t\t\t\n\t\t\t\tvar tbody = $this.children('tbody');\n\t\t\t\tif ( tbody.length === 0 ) {\n\t\t\t\t\ttbody = $('<tbody/>').insertAfter(thead);\n\t\t\t\t}\n\t\t\t\toSettings.nTBody = tbody[0];\n\t\t\t\n\t\t\t\tvar tfoot = $this.children('tfoot');\n\t\t\t\tif ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== \"\" || oSettings.oScroll.sY !== \"\") ) {\n\t\t\t\t\t// If we are a scrolling table, and no footer has been given, then we need to create\n\t\t\t\t\t// a tfoot element for the caption element to be appended to\n\t\t\t\t\ttfoot = $('<tfoot/>').appendTo($this);\n\t\t\t\t}\n\t\t\t\n\t\t\t\tif ( tfoot.length === 0 || tfoot.children().length === 0 ) {\n\t\t\t\t\t$this.addClass( oClasses.sNoFooter );\n\t\t\t\t}\n\t\t\t\telse if ( tfoot.length > 0 ) {\n\t\t\t\t\toSettings.nTFoot = tfoot[0];\n\t\t\t\t\t_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* Check if there is data passing into the constructor */\n\t\t\t\tif ( oInit.aaData ) {\n\t\t\t\t\tfor ( i=0 ; i<oInit.aaData.length ; i++ ) {\n\t\t\t\t\t\t_fnAddData( oSettings, oInit.aaData[ i ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {\n\t\t\t\t\t/* Grab the data from the page - only do this when deferred loading or no Ajax\n\t\t\t\t\t * source since there is no point in reading the DOM data if we are then going\n\t\t\t\t\t * to replace it with Ajax data\n\t\t\t\t\t */\n\t\t\t\t\t_fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* Copy the data index array */\n\t\t\t\toSettings.aiDisplay = oSettings.aiDisplayMaster.slice();\n\t\t\t\n\t\t\t\t/* Initialisation complete - table can be drawn */\n\t\t\t\toSettings.bInitialised = true;\n\t\t\t\n\t\t\t\t/* Check if we need to initialise the table (it might not have been handed off to the\n\t\t\t\t * language processor)\n\t\t\t\t */\n\t\t\t\tif ( bInitHandedOff === false ) {\n\t\t\t\t\t_fnInitialise( oSettings );\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t/* Must be done after everything which can be overridden by the state saving! */\n\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );\n\t\t\t\n\t\t\tif ( oInit.bStateSave )\n\t\t\t{\n\t\t\t\tfeatures.bStateSave = true;\n\t\t\t\t_fnLoadState( oSettings, oInit, loadedInit );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tloadedInit();\n\t\t\t}\n\t\t\t\n\t\t} );\n\t\t_that = null;\n\t\treturn this;\n\t};\n\t\n\t\n\t/*\n\t * It is useful to have variables which are scoped locally so only the\n\t * DataTables functions can access them and they don't leak into global space.\n\t * At the same time these functions are often useful over multiple files in the\n\t * core and API, so we list, or at least document, all variables which are used\n\t * by DataTables as private variables here. This also ensures that there is no\n\t * clashing of variable names and that they can easily referenced for reuse.\n\t */\n\t\n\t\n\t// Defined else where\n\t//  _selector_run\n\t//  _selector_opts\n\t//  _selector_first\n\t//  _selector_row_indexes\n\t\n\tvar _ext; // DataTable.ext\n\tvar _Api; // DataTable.Api\n\tvar _api_register; // DataTable.Api.register\n\tvar _api_registerPlural; // DataTable.Api.registerPlural\n\t\n\tvar _re_dic = {};\n\tvar _re_new_lines = /[\\r\\n\\u2028]/g;\n\tvar _re_html = /<.*?>/g;\n\t\n\t// This is not strict ISO8601 - Date.parse() is quite lax, although\n\t// implementations differ between browsers.\n\tvar _re_date = /^\\d{2,4}[\\.\\/\\-]\\d{1,2}[\\.\\/\\-]\\d{1,2}([T ]{1}\\d{1,2}[:\\.]\\d{2}([\\.:]\\d{2})?)?$/;\n\t\n\t// Escape regular expression special characters\n\tvar _re_escape_regex = new RegExp( '(\\\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\\\', '$', '^', '-' ].join('|\\\\') + ')', 'g' );\n\t\n\t// http://en.wikipedia.org/wiki/Foreign_exchange_market\n\t// - \\u20BD - Russian ruble.\n\t// - \\u20a9 - South Korean Won\n\t// - \\u20BA - Turkish Lira\n\t// - \\u20B9 - Indian Rupee\n\t// - R - Brazil (R$) and South Africa\n\t// - fr - Swiss Franc\n\t// - kr - Swedish krona, Norwegian krone and Danish krone\n\t// - \\u2009 is thin space and \\u202F is narrow no-break space, both used in many\n\t// - Ƀ - Bitcoin\n\t// - Ξ - Ethereum\n\t//   standards as thousands separators.\n\tvar _re_formatted_numeric = /['\\u00A0,$£€¥%\\u2009\\u202F\\u20BD\\u20a9\\u20BArfkɃΞ]/gi;\n\t\n\t\n\tvar _empty = function ( d ) {\n\t\treturn !d || d === true || d === '-' ? true : false;\n\t};\n\t\n\t\n\tvar _intVal = function ( s ) {\n\t\tvar integer = parseInt( s, 10 );\n\t\treturn !isNaN(integer) && isFinite(s) ? integer : null;\n\t};\n\t\n\t// Convert from a formatted number with characters other than `.` as the\n\t// decimal place, to a Javascript number\n\tvar _numToDecimal = function ( num, decimalPoint ) {\n\t\t// Cache created regular expressions for speed as this function is called often\n\t\tif ( ! _re_dic[ decimalPoint ] ) {\n\t\t\t_re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );\n\t\t}\n\t\treturn typeof num === 'string' && decimalPoint !== '.' ?\n\t\t\tnum.replace( /\\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :\n\t\t\tnum;\n\t};\n\t\n\t\n\tvar _isNumber = function ( d, decimalPoint, formatted ) {\n\t\tvar type = typeof d;\n\t\tvar strType = type === 'string';\n\t\n\t\tif ( type === 'number' || type === 'bigint') {\n\t\t\treturn true;\n\t\t}\n\t\n\t\t// If empty return immediately so there must be a number if it is a\n\t\t// formatted string (this stops the string \"k\", or \"kr\", etc being detected\n\t\t// as a formatted number for currency\n\t\tif ( _empty( d ) ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\tif ( decimalPoint && strType ) {\n\t\t\td = _numToDecimal( d, decimalPoint );\n\t\t}\n\t\n\t\tif ( formatted && strType ) {\n\t\t\td = d.replace( _re_formatted_numeric, '' );\n\t\t}\n\t\n\t\treturn !isNaN( parseFloat(d) ) && isFinite( d );\n\t};\n\t\n\t\n\t// A string without HTML in it can be considered to be HTML still\n\tvar _isHtml = function ( d ) {\n\t\treturn _empty( d ) || typeof d === 'string';\n\t};\n\t\n\t\n\tvar _htmlNumeric = function ( d, decimalPoint, formatted ) {\n\t\tif ( _empty( d ) ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\tvar html = _isHtml( d );\n\t\treturn ! html ?\n\t\t\tnull :\n\t\t\t_isNumber( _stripHtml( d ), decimalPoint, formatted ) ?\n\t\t\t\ttrue :\n\t\t\t\tnull;\n\t};\n\t\n\t\n\tvar _pluck = function ( a, prop, prop2 ) {\n\t\tvar out = [];\n\t\tvar i=0, ien=a.length;\n\t\n\t\t// Could have the test in the loop for slightly smaller code, but speed\n\t\t// is essential here\n\t\tif ( prop2 !== undefined ) {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tif ( a[i] && a[i][ prop ] ) {\n\t\t\t\t\tout.push( a[i][ prop ][ prop2 ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tif ( a[i] ) {\n\t\t\t\t\tout.push( a[i][ prop ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\t// Basically the same as _pluck, but rather than looping over `a` we use `order`\n\t// as the indexes to pick from `a`\n\tvar _pluck_order = function ( a, order, prop, prop2 )\n\t{\n\t\tvar out = [];\n\t\tvar i=0, ien=order.length;\n\t\n\t\t// Could have the test in the loop for slightly smaller code, but speed\n\t\t// is essential here\n\t\tif ( prop2 !== undefined ) {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tif ( a[ order[i] ][ prop ] ) {\n\t\t\t\t\tout.push( a[ order[i] ][ prop ][ prop2 ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tout.push( a[ order[i] ][ prop ] );\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\tvar _range = function ( len, start )\n\t{\n\t\tvar out = [];\n\t\tvar end;\n\t\n\t\tif ( start === undefined ) {\n\t\t\tstart = 0;\n\t\t\tend = len;\n\t\t}\n\t\telse {\n\t\t\tend = start;\n\t\t\tstart = len;\n\t\t}\n\t\n\t\tfor ( var i=start ; i<end ; i++ ) {\n\t\t\tout.push( i );\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\tvar _removeEmpty = function ( a )\n\t{\n\t\tvar out = [];\n\t\n\t\tfor ( var i=0, ien=a.length ; i<ien ; i++ ) {\n\t\t\tif ( a[i] ) { // careful - will remove all falsy values!\n\t\t\t\tout.push( a[i] );\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\tvar _stripHtml = function ( d ) {\n\t\treturn d\n\t\t\t.replace( _re_html, '' ) // Complete tags\n\t\t\t.replace(/<script/i, ''); // Safety for incomplete script tag\n\t};\n\t\n\t\n\t/**\n\t * Determine if all values in the array are unique. This means we can short\n\t * cut the _unique method at the cost of a single loop. A sorted array is used\n\t * to easily check the values.\n\t *\n\t * @param  {array} src Source array\n\t * @return {boolean} true if all unique, false otherwise\n\t * @ignore\n\t */\n\tvar _areAllUnique = function ( src ) {\n\t\tif ( src.length < 2 ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\tvar sorted = src.slice().sort();\n\t\tvar last = sorted[0];\n\t\n\t\tfor ( var i=1, ien=sorted.length ; i<ien ; i++ ) {\n\t\t\tif ( sorted[i] === last ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\n\t\t\tlast = sorted[i];\n\t\t}\n\t\n\t\treturn true;\n\t};\n\t\n\t\n\t/**\n\t * Find the unique elements in a source array.\n\t *\n\t * @param  {array} src Source array\n\t * @return {array} Array of unique items\n\t * @ignore\n\t */\n\tvar _unique = function ( src )\n\t{\n\t\tif ( _areAllUnique( src ) ) {\n\t\t\treturn src.slice();\n\t\t}\n\t\n\t\t// A faster unique method is to use object keys to identify used values,\n\t\t// but this doesn't work with arrays or objects, which we must also\n\t\t// consider. See jsperf.com/compare-array-unique-versions/4 for more\n\t\t// information.\n\t\tvar\n\t\t\tout = [],\n\t\t\tval,\n\t\t\ti, ien=src.length,\n\t\t\tj, k=0;\n\t\n\t\tagain: for ( i=0 ; i<ien ; i++ ) {\n\t\t\tval = src[i];\n\t\n\t\t\tfor ( j=0 ; j<k ; j++ ) {\n\t\t\t\tif ( out[j] === val ) {\n\t\t\t\t\tcontinue again;\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tout.push( val );\n\t\t\tk++;\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t// Surprisingly this is faster than [].concat.apply\n\t// https://jsperf.com/flatten-an-array-loop-vs-reduce/2\n\tvar _flatten = function (out, val) {\n\t\tif (Array.isArray(val)) {\n\t\t\tfor (var i=0 ; i<val.length ; i++) {\n\t\t\t\t_flatten(out, val[i]);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tout.push(val);\n\t\t}\n\t  \n\t\treturn out;\n\t}\n\t\n\tvar _includes = function (search, start) {\n\t\tif (start === undefined) {\n\t\t\tstart = 0;\n\t\t}\n\t\n\t\treturn this.indexOf(search, start) !== -1;\t\n\t};\n\t\n\t// Array.isArray polyfill.\n\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray\n\tif (! Array.isArray) {\n\t    Array.isArray = function(arg) {\n\t        return Object.prototype.toString.call(arg) === '[object Array]';\n\t    };\n\t}\n\t\n\tif (! Array.prototype.includes) {\n\t\tArray.prototype.includes = _includes;\n\t}\n\t\n\t// .trim() polyfill\n\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim\n\tif (!String.prototype.trim) {\n\t  String.prototype.trim = function () {\n\t    return this.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n\t  };\n\t}\n\t\n\tif (! String.prototype.includes) {\n\t\tString.prototype.includes = _includes;\n\t}\n\t\n\t/**\n\t * DataTables utility methods\n\t * \n\t * This namespace provides helper methods that DataTables uses internally to\n\t * create a DataTable, but which are not exclusively used only for DataTables.\n\t * These methods can be used by extension authors to save the duplication of\n\t * code.\n\t *\n\t *  @namespace\n\t */\n\tDataTable.util = {\n\t\t/**\n\t\t * Throttle the calls to a function. Arguments and context are maintained\n\t\t * for the throttled function.\n\t\t *\n\t\t * @param {function} fn Function to be called\n\t\t * @param {integer} freq Call frequency in mS\n\t\t * @return {function} Wrapped function\n\t\t */\n\t\tthrottle: function ( fn, freq ) {\n\t\t\tvar\n\t\t\t\tfrequency = freq !== undefined ? freq : 200,\n\t\t\t\tlast,\n\t\t\t\ttimer;\n\t\n\t\t\treturn function () {\n\t\t\t\tvar\n\t\t\t\t\tthat = this,\n\t\t\t\t\tnow  = +new Date(),\n\t\t\t\t\targs = arguments;\n\t\n\t\t\t\tif ( last && now < last + frequency ) {\n\t\t\t\t\tclearTimeout( timer );\n\t\n\t\t\t\t\ttimer = setTimeout( function () {\n\t\t\t\t\t\tlast = undefined;\n\t\t\t\t\t\tfn.apply( that, args );\n\t\t\t\t\t}, frequency );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlast = now;\n\t\t\t\t\tfn.apply( that, args );\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Escape a string such that it can be used in a regular expression\n\t\t *\n\t\t *  @param {string} val string to escape\n\t\t *  @returns {string} escaped string\n\t\t */\n\t\tescapeRegex: function ( val ) {\n\t\t\treturn val.replace( _re_escape_regex, '\\\\$1' );\n\t\t},\n\t\n\t\t/**\n\t\t * Create a function that will write to a nested object or array\n\t\t * @param {*} source JSON notation string\n\t\t * @returns Write function\n\t\t */\n\t\tset: function ( source ) {\n\t\t\tif ( $.isPlainObject( source ) ) {\n\t\t\t\t/* Unlike get, only the underscore (global) option is used for for\n\t\t\t\t * setting data since we don't know the type here. This is why an object\n\t\t\t\t * option is not documented for `mData` (which is read/write), but it is\n\t\t\t\t * for `mRender` which is read only.\n\t\t\t\t */\n\t\t\t\treturn DataTable.util.set( source._ );\n\t\t\t}\n\t\t\telse if ( source === null ) {\n\t\t\t\t// Nothing to do when the data source is null\n\t\t\t\treturn function () {};\n\t\t\t}\n\t\t\telse if ( typeof source === 'function' ) {\n\t\t\t\treturn function (data, val, meta) {\n\t\t\t\t\tsource( data, 'set', val, meta );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||\n\t\t\t\t\t  source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )\n\t\t\t{\n\t\t\t\t// Like the get, we need to get data from a nested object\n\t\t\t\tvar setData = function (data, val, src) {\n\t\t\t\t\tvar a = _fnSplitObjNotation( src ), b;\n\t\t\t\t\tvar aLast = a[a.length-1];\n\t\t\t\t\tvar arrayNotation, funcNotation, o, innerSrc;\n\t\t\n\t\t\t\t\tfor ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) {\n\t\t\t\t\t\t// Protect against prototype pollution\n\t\t\t\t\t\tif (a[i] === '__proto__' || a[i] === 'constructor') {\n\t\t\t\t\t\t\tthrow new Error('Cannot set prototype values');\n\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t// Check if we are dealing with an array notation request\n\t\t\t\t\t\tarrayNotation = a[i].match(__reArray);\n\t\t\t\t\t\tfuncNotation = a[i].match(__reFn);\n\t\t\n\t\t\t\t\t\tif ( arrayNotation ) {\n\t\t\t\t\t\t\ta[i] = a[i].replace(__reArray, '');\n\t\t\t\t\t\t\tdata[ a[i] ] = [];\n\t\t\n\t\t\t\t\t\t\t// Get the remainder of the nested object to set so we can recurse\n\t\t\t\t\t\t\tb = a.slice();\n\t\t\t\t\t\t\tb.splice( 0, i+1 );\n\t\t\t\t\t\t\tinnerSrc = b.join('.');\n\t\t\n\t\t\t\t\t\t\t// Traverse each entry in the array setting the properties requested\n\t\t\t\t\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\t\t\t\t\tfor ( var j=0, jLen=val.length ; j<jLen ; j++ ) {\n\t\t\t\t\t\t\t\t\to = {};\n\t\t\t\t\t\t\t\t\tsetData( o, val[j], innerSrc );\n\t\t\t\t\t\t\t\t\tdata[ a[i] ].push( o );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t// We've been asked to save data to an array, but it\n\t\t\t\t\t\t\t\t// isn't array data to be saved. Best that can be done\n\t\t\t\t\t\t\t\t// is to just save the value.\n\t\t\t\t\t\t\t\tdata[ a[i] ] = val;\n\t\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t\t// The inner call to setData has already traversed through the remainder\n\t\t\t\t\t\t\t// of the source and has set the data, thus we can exit here\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( funcNotation ) {\n\t\t\t\t\t\t\t// Function call\n\t\t\t\t\t\t\ta[i] = a[i].replace(__reFn, '');\n\t\t\t\t\t\t\tdata = data[ a[i] ]( val );\n\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t// If the nested object doesn't currently exist - since we are\n\t\t\t\t\t\t// trying to set the value - create it\n\t\t\t\t\t\tif ( data[ a[i] ] === null || data[ a[i] ] === undefined ) {\n\t\t\t\t\t\t\tdata[ a[i] ] = {};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdata = data[ a[i] ];\n\t\t\t\t\t}\n\t\t\n\t\t\t\t\t// Last item in the input - i.e, the actual set\n\t\t\t\t\tif ( aLast.match(__reFn ) ) {\n\t\t\t\t\t\t// Function call\n\t\t\t\t\t\tdata = data[ aLast.replace(__reFn, '') ]( val );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// If array notation is used, we just want to strip it and use the property name\n\t\t\t\t\t\t// and assign the value. If it isn't used, then we get the result we want anyway\n\t\t\t\t\t\tdata[ aLast.replace(__reArray, '') ] = val;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\n\t\t\t\treturn function (data, val) { // meta is also passed in, but not used\n\t\t\t\t\treturn setData( data, val, source );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Array or flat object mapping\n\t\t\t\treturn function (data, val) { // meta is also passed in, but not used\n\t\t\t\t\tdata[source] = val;\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t\n\t\t/**\n\t\t * Create a function that will read nested objects from arrays, based on JSON notation\n\t\t * @param {*} source JSON notation string\n\t\t * @returns Value read\n\t\t */\n\t\tget: function ( source ) {\n\t\t\tif ( $.isPlainObject( source ) ) {\n\t\t\t\t// Build an object of get functions, and wrap them in a single call\n\t\t\t\tvar o = {};\n\t\t\t\t$.each( source, function (key, val) {\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\to[key] = DataTable.util.get( val );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\n\t\t\t\treturn function (data, type, row, meta) {\n\t\t\t\t\tvar t = o[type] || o._;\n\t\t\t\t\treturn t !== undefined ?\n\t\t\t\t\t\tt(data, type, row, meta) :\n\t\t\t\t\t\tdata;\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( source === null ) {\n\t\t\t\t// Give an empty string for rendering / sorting etc\n\t\t\t\treturn function (data) { // type, row and meta also passed, but not used\n\t\t\t\t\treturn data;\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( typeof source === 'function' ) {\n\t\t\t\treturn function (data, type, row, meta) {\n\t\t\t\t\treturn source( data, type, row, meta );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||\n\t\t\t\t\t  source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )\n\t\t\t{\n\t\t\t\t/* If there is a . in the source string then the data source is in a\n\t\t\t\t * nested object so we loop over the data for each level to get the next\n\t\t\t\t * level down. On each loop we test for undefined, and if found immediately\n\t\t\t\t * return. This allows entire objects to be missing and sDefaultContent to\n\t\t\t\t * be used if defined, rather than throwing an error\n\t\t\t\t */\n\t\t\t\tvar fetchData = function (data, type, src) {\n\t\t\t\t\tvar arrayNotation, funcNotation, out, innerSrc;\n\t\t\n\t\t\t\t\tif ( src !== \"\" ) {\n\t\t\t\t\t\tvar a = _fnSplitObjNotation( src );\n\t\t\n\t\t\t\t\t\tfor ( var i=0, iLen=a.length ; i<iLen ; i++ ) {\n\t\t\t\t\t\t\t// Check if we are dealing with special notation\n\t\t\t\t\t\t\tarrayNotation = a[i].match(__reArray);\n\t\t\t\t\t\t\tfuncNotation = a[i].match(__reFn);\n\t\t\n\t\t\t\t\t\t\tif ( arrayNotation ) {\n\t\t\t\t\t\t\t\t// Array notation\n\t\t\t\t\t\t\t\ta[i] = a[i].replace(__reArray, '');\n\t\t\n\t\t\t\t\t\t\t\t// Condition allows simply [] to be passed in\n\t\t\t\t\t\t\t\tif ( a[i] !== \"\" ) {\n\t\t\t\t\t\t\t\t\tdata = data[ a[i] ];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tout = [];\n\t\t\n\t\t\t\t\t\t\t\t// Get the remainder of the nested object to get\n\t\t\t\t\t\t\t\ta.splice( 0, i+1 );\n\t\t\t\t\t\t\t\tinnerSrc = a.join('.');\n\t\t\n\t\t\t\t\t\t\t\t// Traverse each entry in the array getting the properties requested\n\t\t\t\t\t\t\t\tif ( Array.isArray( data ) ) {\n\t\t\t\t\t\t\t\t\tfor ( var j=0, jLen=data.length ; j<jLen ; j++ ) {\n\t\t\t\t\t\t\t\t\t\tout.push( fetchData( data[j], type, innerSrc ) );\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t\t\t// If a string is given in between the array notation indicators, that\n\t\t\t\t\t\t\t\t// is used to join the strings together, otherwise an array is returned\n\t\t\t\t\t\t\t\tvar join = arrayNotation[0].substring(1, arrayNotation[0].length-1);\n\t\t\t\t\t\t\t\tdata = (join===\"\") ? out : out.join(join);\n\t\t\n\t\t\t\t\t\t\t\t// The inner call to fetchData has already traversed through the remainder\n\t\t\t\t\t\t\t\t// of the source requested, so we exit from the loop\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( funcNotation ) {\n\t\t\t\t\t\t\t\t// Function call\n\t\t\t\t\t\t\t\ta[i] = a[i].replace(__reFn, '');\n\t\t\t\t\t\t\t\tdata = data[ a[i] ]();\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t\tif (data === null || data[ a[i] ] === null) {\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( data === undefined || data[ a[i] ] === undefined ) {\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\tdata = data[ a[i] ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\n\t\t\t\t\treturn data;\n\t\t\t\t};\n\t\t\n\t\t\t\treturn function (data, type) { // row and meta also passed, but not used\n\t\t\t\t\treturn fetchData( data, type, source );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Array or flat object mapping\n\t\t\t\treturn function (data, type) { // row and meta also passed, but not used\n\t\t\t\t\treturn data[source];\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t};\n\t\n\t\n\t\n\t/**\n\t * Create a mapping object that allows camel case parameters to be looked up\n\t * for their Hungarian counterparts. The mapping is stored in a private\n\t * parameter called `_hungarianMap` which can be accessed on the source object.\n\t *  @param {object} o\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnHungarianMap ( o )\n\t{\n\t\tvar\n\t\t\thungarian = 'a aa ai ao as b fn i m o s ',\n\t\t\tmatch,\n\t\t\tnewKey,\n\t\t\tmap = {};\n\t\n\t\t$.each( o, function (key, val) {\n\t\t\tmatch = key.match(/^([^A-Z]+?)([A-Z])/);\n\t\n\t\t\tif ( match && hungarian.indexOf(match[1]+' ') !== -1 )\n\t\t\t{\n\t\t\t\tnewKey = key.replace( match[0], match[2].toLowerCase() );\n\t\t\t\tmap[ newKey ] = key;\n\t\n\t\t\t\tif ( match[1] === 'o' )\n\t\t\t\t{\n\t\t\t\t\t_fnHungarianMap( o[key] );\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t\n\t\to._hungarianMap = map;\n\t}\n\t\n\t\n\t/**\n\t * Convert from camel case parameters to Hungarian, based on a Hungarian map\n\t * created by _fnHungarianMap.\n\t *  @param {object} src The model object which holds all parameters that can be\n\t *    mapped.\n\t *  @param {object} user The object to convert from camel case to Hungarian.\n\t *  @param {boolean} force When set to `true`, properties which already have a\n\t *    Hungarian value in the `user` object will be overwritten. Otherwise they\n\t *    won't be.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCamelToHungarian ( src, user, force )\n\t{\n\t\tif ( ! src._hungarianMap ) {\n\t\t\t_fnHungarianMap( src );\n\t\t}\n\t\n\t\tvar hungarianKey;\n\t\n\t\t$.each( user, function (key, val) {\n\t\t\thungarianKey = src._hungarianMap[ key ];\n\t\n\t\t\tif ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )\n\t\t\t{\n\t\t\t\t// For objects, we need to buzz down into the object to copy parameters\n\t\t\t\tif ( hungarianKey.charAt(0) === 'o' )\n\t\t\t\t{\n\t\t\t\t\t// Copy the camelCase options over to the hungarian\n\t\t\t\t\tif ( ! user[ hungarianKey ] ) {\n\t\t\t\t\t\tuser[ hungarianKey ] = {};\n\t\t\t\t\t}\n\t\t\t\t\t$.extend( true, user[hungarianKey], user[key] );\n\t\n\t\t\t\t\t_fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tuser[hungarianKey] = user[ key ];\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Language compatibility - when certain options are given, and others aren't, we\n\t * need to duplicate the values over, in order to provide backwards compatibility\n\t * with older language files.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnLanguageCompat( lang )\n\t{\n\t\t// Note the use of the Hungarian notation for the parameters in this method as\n\t\t// this is called after the mapping of camelCase to Hungarian\n\t\tvar defaults = DataTable.defaults.oLanguage;\n\t\n\t\t// Default mapping\n\t\tvar defaultDecimal = defaults.sDecimal;\n\t\tif ( defaultDecimal ) {\n\t\t\t_addNumericSort( defaultDecimal );\n\t\t}\n\t\n\t\tif ( lang ) {\n\t\t\tvar zeroRecords = lang.sZeroRecords;\n\t\n\t\t\t// Backwards compatibility - if there is no sEmptyTable given, then use the same as\n\t\t\t// sZeroRecords - assuming that is given.\n\t\t\tif ( ! lang.sEmptyTable && zeroRecords &&\n\t\t\t\tdefaults.sEmptyTable === \"No data available in table\" )\n\t\t\t{\n\t\t\t\t_fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );\n\t\t\t}\n\t\n\t\t\t// Likewise with loading records\n\t\t\tif ( ! lang.sLoadingRecords && zeroRecords &&\n\t\t\t\tdefaults.sLoadingRecords === \"Loading...\" )\n\t\t\t{\n\t\t\t\t_fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );\n\t\t\t}\n\t\n\t\t\t// Old parameter name of the thousands separator mapped onto the new\n\t\t\tif ( lang.sInfoThousands ) {\n\t\t\t\tlang.sThousands = lang.sInfoThousands;\n\t\t\t}\n\t\n\t\t\tvar decimal = lang.sDecimal;\n\t\t\tif ( decimal && defaultDecimal !== decimal ) {\n\t\t\t\t_addNumericSort( decimal );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Map one parameter onto another\n\t *  @param {object} o Object to map\n\t *  @param {*} knew The new parameter name\n\t *  @param {*} old The old parameter name\n\t */\n\tvar _fnCompatMap = function ( o, knew, old ) {\n\t\tif ( o[ knew ] !== undefined ) {\n\t\t\to[ old ] = o[ knew ];\n\t\t}\n\t};\n\t\n\t\n\t/**\n\t * Provide backwards compatibility for the main DT options. Note that the new\n\t * options are mapped onto the old parameters, so this is an external interface\n\t * change only.\n\t *  @param {object} init Object to map\n\t */\n\tfunction _fnCompatOpts ( init )\n\t{\n\t\t_fnCompatMap( init, 'ordering',      'bSort' );\n\t\t_fnCompatMap( init, 'orderMulti',    'bSortMulti' );\n\t\t_fnCompatMap( init, 'orderClasses',  'bSortClasses' );\n\t\t_fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );\n\t\t_fnCompatMap( init, 'order',         'aaSorting' );\n\t\t_fnCompatMap( init, 'orderFixed',    'aaSortingFixed' );\n\t\t_fnCompatMap( init, 'paging',        'bPaginate' );\n\t\t_fnCompatMap( init, 'pagingType',    'sPaginationType' );\n\t\t_fnCompatMap( init, 'pageLength',    'iDisplayLength' );\n\t\t_fnCompatMap( init, 'searching',     'bFilter' );\n\t\n\t\t// Boolean initialisation of x-scrolling\n\t\tif ( typeof init.sScrollX === 'boolean' ) {\n\t\t\tinit.sScrollX = init.sScrollX ? '100%' : '';\n\t\t}\n\t\tif ( typeof init.scrollX === 'boolean' ) {\n\t\t\tinit.scrollX = init.scrollX ? '100%' : '';\n\t\t}\n\t\n\t\t// Column search objects are in an array, so it needs to be converted\n\t\t// element by element\n\t\tvar searchCols = init.aoSearchCols;\n\t\n\t\tif ( searchCols ) {\n\t\t\tfor ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {\n\t\t\t\tif ( searchCols[i] ) {\n\t\t\t\t\t_fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Provide backwards compatibility for column options. Note that the new options\n\t * are mapped onto the old parameters, so this is an external interface change\n\t * only.\n\t *  @param {object} init Object to map\n\t */\n\tfunction _fnCompatCols ( init )\n\t{\n\t\t_fnCompatMap( init, 'orderable',     'bSortable' );\n\t\t_fnCompatMap( init, 'orderData',     'aDataSort' );\n\t\t_fnCompatMap( init, 'orderSequence', 'asSorting' );\n\t\t_fnCompatMap( init, 'orderDataType', 'sortDataType' );\n\t\n\t\t// orderData can be given as an integer\n\t\tvar dataSort = init.aDataSort;\n\t\tif ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) {\n\t\t\tinit.aDataSort = [ dataSort ];\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Browser feature detection for capabilities, quirks\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnBrowserDetect( settings )\n\t{\n\t\t// We don't need to do this every time DataTables is constructed, the values\n\t\t// calculated are specific to the browser and OS configuration which we\n\t\t// don't expect to change between initialisations\n\t\tif ( ! DataTable.__browser ) {\n\t\t\tvar browser = {};\n\t\t\tDataTable.__browser = browser;\n\t\n\t\t\t// Scrolling feature / quirks detection\n\t\t\tvar n = $('<div/>')\n\t\t\t\t.css( {\n\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\ttop: 0,\n\t\t\t\t\tleft: $(window).scrollLeft()*-1, // allow for scrolling\n\t\t\t\t\theight: 1,\n\t\t\t\t\twidth: 1,\n\t\t\t\t\toverflow: 'hidden'\n\t\t\t\t} )\n\t\t\t\t.append(\n\t\t\t\t\t$('<div/>')\n\t\t\t\t\t\t.css( {\n\t\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\t\ttop: 1,\n\t\t\t\t\t\t\tleft: 1,\n\t\t\t\t\t\t\twidth: 100,\n\t\t\t\t\t\t\toverflow: 'scroll'\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t$('<div/>')\n\t\t\t\t\t\t\t\t.css( {\n\t\t\t\t\t\t\t\t\twidth: '100%',\n\t\t\t\t\t\t\t\t\theight: 10\n\t\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t.appendTo( 'body' );\n\t\n\t\t\tvar outer = n.children();\n\t\t\tvar inner = outer.children();\n\t\n\t\t\t// Numbers below, in order, are:\n\t\t\t// inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth\n\t\t\t//\n\t\t\t// IE6 XP:                           100 100 100  83\n\t\t\t// IE7 Vista:                        100 100 100  83\n\t\t\t// IE 8+ Windows:                     83  83 100  83\n\t\t\t// Evergreen Windows:                 83  83 100  83\n\t\t\t// Evergreen Mac with scrollbars:     85  85 100  85\n\t\t\t// Evergreen Mac without scrollbars: 100 100 100 100\n\t\n\t\t\t// Get scrollbar width\n\t\t\tbrowser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;\n\t\n\t\t\t// IE6/7 will oversize a width 100% element inside a scrolling element, to\n\t\t\t// include the width of the scrollbar, while other browsers ensure the inner\n\t\t\t// element is contained without forcing scrolling\n\t\t\tbrowser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;\n\t\n\t\t\t// In rtl text layout, some browsers (most, but not all) will place the\n\t\t\t// scrollbar on the left, rather than the right.\n\t\t\tbrowser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;\n\t\n\t\t\t// IE8- don't provide height and width for getBoundingClientRect\n\t\t\tbrowser.bBounding = n[0].getBoundingClientRect().width ? true : false;\n\t\n\t\t\tn.remove();\n\t\t}\n\t\n\t\t$.extend( settings.oBrowser, DataTable.__browser );\n\t\tsettings.oScroll.iBarWidth = DataTable.__browser.barWidth;\n\t}\n\t\n\t\n\t/**\n\t * Array.prototype reduce[Right] method, used for browsers which don't support\n\t * JS 1.6. Done this way to reduce code size, since we iterate either way\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnReduce ( that, fn, init, start, end, inc )\n\t{\n\t\tvar\n\t\t\ti = start,\n\t\t\tvalue,\n\t\t\tisSet = false;\n\t\n\t\tif ( init !== undefined ) {\n\t\t\tvalue = init;\n\t\t\tisSet = true;\n\t\t}\n\t\n\t\twhile ( i !== end ) {\n\t\t\tif ( ! that.hasOwnProperty(i) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\n\t\t\tvalue = isSet ?\n\t\t\t\tfn( value, that[i], i, that ) :\n\t\t\t\tthat[i];\n\t\n\t\t\tisSet = true;\n\t\t\ti += inc;\n\t\t}\n\t\n\t\treturn value;\n\t}\n\t\n\t/**\n\t * Add a column to the list used for the table with default values\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {node} nTh The th element for this column\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddColumn( oSettings, nTh )\n\t{\n\t\t// Add column to aoColumns array\n\t\tvar oDefaults = DataTable.defaults.column;\n\t\tvar iCol = oSettings.aoColumns.length;\n\t\tvar oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {\n\t\t\t\"nTh\": nTh ? nTh : document.createElement('th'),\n\t\t\t\"sTitle\":    oDefaults.sTitle    ? oDefaults.sTitle    : nTh ? nTh.innerHTML : '',\n\t\t\t\"aDataSort\": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],\n\t\t\t\"mData\": oDefaults.mData ? oDefaults.mData : iCol,\n\t\t\tidx: iCol\n\t\t} );\n\t\toSettings.aoColumns.push( oCol );\n\t\n\t\t// Add search object for column specific search. Note that the `searchCols[ iCol ]`\n\t\t// passed into extend can be undefined. This allows the user to give a default\n\t\t// with only some of the parameters defined, and also not give a default\n\t\tvar searchCols = oSettings.aoPreSearchCols;\n\t\tsearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );\n\t\n\t\t// Use the default column options function to initialise classes etc\n\t\t_fnColumnOptions( oSettings, iCol, $(nTh).data() );\n\t}\n\t\n\t\n\t/**\n\t * Apply options for a column\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iCol column index to consider\n\t *  @param {object} oOptions object with sType, bVisible and bSearchable etc\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnColumnOptions( oSettings, iCol, oOptions )\n\t{\n\t\tvar oCol = oSettings.aoColumns[ iCol ];\n\t\tvar oClasses = oSettings.oClasses;\n\t\tvar th = $(oCol.nTh);\n\t\n\t\t// Try to get width information from the DOM. We can't get it from CSS\n\t\t// as we'd need to parse the CSS stylesheet. `width` option can override\n\t\tif ( ! oCol.sWidthOrig ) {\n\t\t\t// Width attribute\n\t\t\toCol.sWidthOrig = th.attr('width') || null;\n\t\n\t\t\t// Style attribute\n\t\t\tvar t = (th.attr('style') || '').match(/width:\\s*(\\d+[pxem%]+)/);\n\t\t\tif ( t ) {\n\t\t\t\toCol.sWidthOrig = t[1];\n\t\t\t}\n\t\t}\n\t\n\t\t/* User specified column options */\n\t\tif ( oOptions !== undefined && oOptions !== null )\n\t\t{\n\t\t\t// Backwards compatibility\n\t\t\t_fnCompatCols( oOptions );\n\t\n\t\t\t// Map camel case parameters to their Hungarian counterparts\n\t\t\t_fnCamelToHungarian( DataTable.defaults.column, oOptions, true );\n\t\n\t\t\t/* Backwards compatibility for mDataProp */\n\t\t\tif ( oOptions.mDataProp !== undefined && !oOptions.mData )\n\t\t\t{\n\t\t\t\toOptions.mData = oOptions.mDataProp;\n\t\t\t}\n\t\n\t\t\tif ( oOptions.sType )\n\t\t\t{\n\t\t\t\toCol._sManualType = oOptions.sType;\n\t\t\t}\n\t\n\t\t\t// `class` is a reserved word in Javascript, so we need to provide\n\t\t\t// the ability to use a valid name for the camel case input\n\t\t\tif ( oOptions.className && ! oOptions.sClass )\n\t\t\t{\n\t\t\t\toOptions.sClass = oOptions.className;\n\t\t\t}\n\t\t\tif ( oOptions.sClass ) {\n\t\t\t\tth.addClass( oOptions.sClass );\n\t\t\t}\n\t\n\t\t\tvar origClass = oCol.sClass;\n\t\n\t\t\t$.extend( oCol, oOptions );\n\t\t\t_fnMap( oCol, oOptions, \"sWidth\", \"sWidthOrig\" );\n\t\n\t\t\t// Merge class from previously defined classes with this one, rather than just\n\t\t\t// overwriting it in the extend above\n\t\t\tif (origClass !== oCol.sClass) {\n\t\t\t\toCol.sClass = origClass + ' ' + oCol.sClass;\n\t\t\t}\n\t\n\t\t\t/* iDataSort to be applied (backwards compatibility), but aDataSort will take\n\t\t\t * priority if defined\n\t\t\t */\n\t\t\tif ( oOptions.iDataSort !== undefined )\n\t\t\t{\n\t\t\t\toCol.aDataSort = [ oOptions.iDataSort ];\n\t\t\t}\n\t\t\t_fnMap( oCol, oOptions, \"aDataSort\" );\n\t\t}\n\t\n\t\t/* Cache the data get and set functions for speed */\n\t\tvar mDataSrc = oCol.mData;\n\t\tvar mData = _fnGetObjectDataFn( mDataSrc );\n\t\tvar mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;\n\t\n\t\tvar attrTest = function( src ) {\n\t\t\treturn typeof src === 'string' && src.indexOf('@') !== -1;\n\t\t};\n\t\toCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (\n\t\t\tattrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)\n\t\t);\n\t\toCol._setter = null;\n\t\n\t\toCol.fnGetData = function (rowData, type, meta) {\n\t\t\tvar innerData = mData( rowData, type, undefined, meta );\n\t\n\t\t\treturn mRender && type ?\n\t\t\t\tmRender( innerData, type, rowData, meta ) :\n\t\t\t\tinnerData;\n\t\t};\n\t\toCol.fnSetData = function ( rowData, val, meta ) {\n\t\t\treturn _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );\n\t\t};\n\t\n\t\t// Indicate if DataTables should read DOM data as an object or array\n\t\t// Used in _fnGetRowElements\n\t\tif ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) {\n\t\t\toSettings._rowReadObject = true;\n\t\t}\n\t\n\t\t/* Feature sorting overrides column specific when off */\n\t\tif ( !oSettings.oFeatures.bSort )\n\t\t{\n\t\t\toCol.bSortable = false;\n\t\t\tth.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called\n\t\t}\n\t\n\t\t/* Check that the class assignment is correct for sorting */\n\t\tvar bAsc = $.inArray('asc', oCol.asSorting) !== -1;\n\t\tvar bDesc = $.inArray('desc', oCol.asSorting) !== -1;\n\t\tif ( !oCol.bSortable || (!bAsc && !bDesc) )\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortableNone;\n\t\t\toCol.sSortingClassJUI = \"\";\n\t\t}\n\t\telse if ( bAsc && !bDesc )\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortableAsc;\n\t\t\toCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;\n\t\t}\n\t\telse if ( !bAsc && bDesc )\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortableDesc;\n\t\t\toCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;\n\t\t}\n\t\telse\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortable;\n\t\t\toCol.sSortingClassJUI = oClasses.sSortJUI;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Adjust the table column widths for new data. Note: you would probably want to\n\t * do a redraw after calling this function!\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAdjustColumnSizing ( settings )\n\t{\n\t\t/* Not interested in doing column width calculation if auto-width is disabled */\n\t\tif ( settings.oFeatures.bAutoWidth !== false )\n\t\t{\n\t\t\tvar columns = settings.aoColumns;\n\t\n\t\t\t_fnCalculateColumnWidths( settings );\n\t\t\tfor ( var i=0 , iLen=columns.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\tcolumns[i].nTh.style.width = columns[i].sWidth;\n\t\t\t}\n\t\t}\n\t\n\t\tvar scroll = settings.oScroll;\n\t\tif ( scroll.sY !== '' || scroll.sX !== '')\n\t\t{\n\t\t\t_fnScrollDraw( settings );\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'column-sizing', [settings] );\n\t}\n\t\n\t\n\t/**\n\t * Convert the index of a visible column to the index in the data array (take account\n\t * of hidden columns)\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iMatch Visible column index to lookup\n\t *  @returns {int} i the data index\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnVisibleToColumnIndex( oSettings, iMatch )\n\t{\n\t\tvar aiVis = _fnGetColumns( oSettings, 'bVisible' );\n\t\n\t\treturn typeof aiVis[iMatch] === 'number' ?\n\t\t\taiVis[iMatch] :\n\t\t\tnull;\n\t}\n\t\n\t\n\t/**\n\t * Convert the index of an index in the data array and convert it to the visible\n\t *   column index (take account of hidden columns)\n\t *  @param {int} iMatch Column index to lookup\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {int} i the data index\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnColumnIndexToVisible( oSettings, iMatch )\n\t{\n\t\tvar aiVis = _fnGetColumns( oSettings, 'bVisible' );\n\t\tvar iPos = $.inArray( iMatch, aiVis );\n\t\n\t\treturn iPos !== -1 ? iPos : null;\n\t}\n\t\n\t\n\t/**\n\t * Get the number of visible columns\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {int} i the number of visible columns\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnVisbleColumns( oSettings )\n\t{\n\t\tvar vis = 0;\n\t\n\t\t// No reduce in IE8, use a loop for now\n\t\t$.each( oSettings.aoColumns, function ( i, col ) {\n\t\t\tif ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {\n\t\t\t\tvis++;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn vis;\n\t}\n\t\n\t\n\t/**\n\t * Get an array of column indexes that match a given property\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {string} sParam Parameter in aoColumns to look for - typically\n\t *    bVisible or bSearchable\n\t *  @returns {array} Array of indexes with matched properties\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetColumns( oSettings, sParam )\n\t{\n\t\tvar a = [];\n\t\n\t\t$.map( oSettings.aoColumns, function(val, i) {\n\t\t\tif ( val[sParam] ) {\n\t\t\t\ta.push( i );\n\t\t\t}\n\t\t} );\n\t\n\t\treturn a;\n\t}\n\t\n\t\n\t/**\n\t * Calculate the 'type' of a column\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnColumnTypes ( settings )\n\t{\n\t\tvar columns = settings.aoColumns;\n\t\tvar data = settings.aoData;\n\t\tvar types = DataTable.ext.type.detect;\n\t\tvar i, ien, j, jen, k, ken;\n\t\tvar col, cell, detectedType, cache;\n\t\n\t\t// For each column, spin over the \n\t\tfor ( i=0, ien=columns.length ; i<ien ; i++ ) {\n\t\t\tcol = columns[i];\n\t\t\tcache = [];\n\t\n\t\t\tif ( ! col.sType && col._sManualType ) {\n\t\t\t\tcol.sType = col._sManualType;\n\t\t\t}\n\t\t\telse if ( ! col.sType ) {\n\t\t\t\tfor ( j=0, jen=types.length ; j<jen ; j++ ) {\n\t\t\t\t\tfor ( k=0, ken=data.length ; k<ken ; k++ ) {\n\t\t\t\t\t\t// Use a cache array so we only need to get the type data\n\t\t\t\t\t\t// from the formatter once (when using multiple detectors)\n\t\t\t\t\t\tif ( cache[k] === undefined ) {\n\t\t\t\t\t\t\tcache[k] = _fnGetCellData( settings, k, i, 'type' );\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tdetectedType = types[j]( cache[k], settings );\n\t\n\t\t\t\t\t\t// If null, then this type can't apply to this column, so\n\t\t\t\t\t\t// rather than testing all cells, break out. There is an\n\t\t\t\t\t\t// exception for the last type which is `html`. We need to\n\t\t\t\t\t\t// scan all rows since it is possible to mix string and HTML\n\t\t\t\t\t\t// types\n\t\t\t\t\t\tif ( ! detectedType && j !== types.length-1 ) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t// Only a single match is needed for html type since it is\n\t\t\t\t\t\t// bottom of the pile and very similar to string - but it\n\t\t\t\t\t\t// must not be empty\n\t\t\t\t\t\tif ( detectedType === 'html' && ! _empty(cache[k]) ) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\t// Type is valid for all data points in the column - use this\n\t\t\t\t\t// type\n\t\t\t\t\tif ( detectedType ) {\n\t\t\t\t\t\tcol.sType = detectedType;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Fall back - if no type was detected, always use string\n\t\t\t\tif ( ! col.sType ) {\n\t\t\t\t\tcol.sType = 'string';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Take the column definitions and static columns arrays and calculate how\n\t * they relate to column indexes. The callback function will then apply the\n\t * definition found for a column to a suitable configuration object.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {array} aoColDefs The aoColumnDefs array that is to be applied\n\t *  @param {array} aoCols The aoColumns array that defines columns individually\n\t *  @param {function} fn Callback function - takes two parameters, the calculated\n\t *    column index and the definition for that column.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )\n\t{\n\t\tvar i, iLen, j, jLen, k, kLen, def;\n\t\tvar columns = oSettings.aoColumns;\n\t\n\t\t// Column definitions with aTargets\n\t\tif ( aoColDefs )\n\t\t{\n\t\t\t/* Loop over the definitions array - loop in reverse so first instance has priority */\n\t\t\tfor ( i=aoColDefs.length-1 ; i>=0 ; i-- )\n\t\t\t{\n\t\t\t\tdef = aoColDefs[i];\n\t\n\t\t\t\t/* Each definition can target multiple columns, as it is an array */\n\t\t\t\tvar aTargets = def.target !== undefined\n\t\t\t\t\t? def.target\n\t\t\t\t\t: def.targets !== undefined\n\t\t\t\t\t\t? def.targets\n\t\t\t\t\t\t: def.aTargets;\n\t\n\t\t\t\tif ( ! Array.isArray( aTargets ) )\n\t\t\t\t{\n\t\t\t\t\taTargets = [ aTargets ];\n\t\t\t\t}\n\t\n\t\t\t\tfor ( j=0, jLen=aTargets.length ; j<jLen ; j++ )\n\t\t\t\t{\n\t\t\t\t\tif ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Add columns that we don't yet know about */\n\t\t\t\t\t\twhile( columns.length <= aTargets[j] )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_fnAddColumn( oSettings );\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t/* Integer, basic index */\n\t\t\t\t\t\tfn( aTargets[j], def );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Negative integer, right to left column counting */\n\t\t\t\t\t\tfn( columns.length+aTargets[j], def );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( typeof aTargets[j] === 'string' )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Class name matching on TH element */\n\t\t\t\t\t\tfor ( k=0, kLen=columns.length ; k<kLen ; k++ )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ( aTargets[j] == \"_all\" ||\n\t\t\t\t\t\t\t     $(columns[k].nTh).hasClass( aTargets[j] ) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfn( k, def );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\t// Statically defined columns array\n\t\tif ( aoCols )\n\t\t{\n\t\t\tfor ( i=0, iLen=aoCols.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\tfn( i, aoCols[i] );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * Add a data array to the table, creating DOM node etc. This is the parallel to\n\t * _fnGatherData, but for adding rows from a Javascript source, rather than a\n\t * DOM source.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {array} aData data array to be added\n\t *  @param {node} [nTr] TR element to add to the table - optional. If not given,\n\t *    DataTables will create a row automatically\n\t *  @param {array} [anTds] Array of TD|TH elements for the row - must be given\n\t *    if nTr is.\n\t *  @returns {int} >=0 if successful (index of new aoData entry), -1 if failed\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddData ( oSettings, aDataIn, nTr, anTds )\n\t{\n\t\t/* Create the object for storing information about this new row */\n\t\tvar iRow = oSettings.aoData.length;\n\t\tvar oData = $.extend( true, {}, DataTable.models.oRow, {\n\t\t\tsrc: nTr ? 'dom' : 'data',\n\t\t\tidx: iRow\n\t\t} );\n\t\n\t\toData._aData = aDataIn;\n\t\toSettings.aoData.push( oData );\n\t\n\t\t/* Create the cells */\n\t\tvar nTd, sThisType;\n\t\tvar columns = oSettings.aoColumns;\n\t\n\t\t// Invalidate the column types as the new data needs to be revalidated\n\t\tfor ( var i=0, iLen=columns.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tcolumns[i].sType = null;\n\t\t}\n\t\n\t\t/* Add to the display array */\n\t\toSettings.aiDisplayMaster.push( iRow );\n\t\n\t\tvar id = oSettings.rowIdFn( aDataIn );\n\t\tif ( id !== undefined ) {\n\t\t\toSettings.aIds[ id ] = oData;\n\t\t}\n\t\n\t\t/* Create the DOM information, or register it if already present */\n\t\tif ( nTr || ! oSettings.oFeatures.bDeferRender )\n\t\t{\n\t\t\t_fnCreateTr( oSettings, iRow, nTr, anTds );\n\t\t}\n\t\n\t\treturn iRow;\n\t}\n\t\n\t\n\t/**\n\t * Add one or more TR elements to the table. Generally we'd expect to\n\t * use this for reading data from a DOM sourced table, but it could be\n\t * used for an TR element. Note that if a TR is given, it is used (i.e.\n\t * it is not cloned).\n\t *  @param {object} settings dataTables settings object\n\t *  @param {array|node|jQuery} trs The TR element(s) to add to the table\n\t *  @returns {array} Array of indexes for the added rows\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddTr( settings, trs )\n\t{\n\t\tvar row;\n\t\n\t\t// Allow an individual node to be passed in\n\t\tif ( ! (trs instanceof $) ) {\n\t\t\ttrs = $(trs);\n\t\t}\n\t\n\t\treturn trs.map( function (i, el) {\n\t\t\trow = _fnGetRowElements( settings, el );\n\t\t\treturn _fnAddData( settings, row.data, el, row.cells );\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Take a TR element and convert it to an index in aoData\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {node} n the TR element to find\n\t *  @returns {int} index if the node is found, null if not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnNodeToDataIndex( oSettings, n )\n\t{\n\t\treturn (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;\n\t}\n\t\n\t\n\t/**\n\t * Take a TD element and convert it into a column data index (not the visible index)\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iRow The row number the TD/TH can be found in\n\t *  @param {node} n The TD/TH element to find\n\t *  @returns {int} index if the node is found, -1 if not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnNodeToColumnIndex( oSettings, iRow, n )\n\t{\n\t\treturn $.inArray( n, oSettings.aoData[ iRow ].anCells );\n\t}\n\t\n\t\n\t/**\n\t * Get the data for a given cell from the internal cache, taking into account data mapping\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} rowIdx aoData row id\n\t *  @param {int} colIdx Column index\n\t *  @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order')\n\t *  @returns {*} Cell data\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetCellData( settings, rowIdx, colIdx, type )\n\t{\n\t\tif (type === 'search') {\n\t\t\ttype = 'filter';\n\t\t}\n\t\telse if (type === 'order') {\n\t\t\ttype = 'sort';\n\t\t}\n\t\n\t\tvar draw           = settings.iDraw;\n\t\tvar col            = settings.aoColumns[colIdx];\n\t\tvar rowData        = settings.aoData[rowIdx]._aData;\n\t\tvar defaultContent = col.sDefaultContent;\n\t\tvar cellData       = col.fnGetData( rowData, type, {\n\t\t\tsettings: settings,\n\t\t\trow:      rowIdx,\n\t\t\tcol:      colIdx\n\t\t} );\n\t\n\t\tif ( cellData === undefined ) {\n\t\t\tif ( settings.iDrawError != draw && defaultContent === null ) {\n\t\t\t\t_fnLog( settings, 0, \"Requested unknown parameter \"+\n\t\t\t\t\t(typeof col.mData=='function' ? '{function}' : \"'\"+col.mData+\"'\")+\n\t\t\t\t\t\" for row \"+rowIdx+\", column \"+colIdx, 4 );\n\t\t\t\tsettings.iDrawError = draw;\n\t\t\t}\n\t\t\treturn defaultContent;\n\t\t}\n\t\n\t\t// When the data source is null and a specific data type is requested (i.e.\n\t\t// not the original data), we can use default column data\n\t\tif ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {\n\t\t\tcellData = defaultContent;\n\t\t}\n\t\telse if ( typeof cellData === 'function' ) {\n\t\t\t// If the data source is a function, then we run it and use the return,\n\t\t\t// executing in the scope of the data object (for instances)\n\t\t\treturn cellData.call( rowData );\n\t\t}\n\t\n\t\tif ( cellData === null && type === 'display' ) {\n\t\t\treturn '';\n\t\t}\n\t\n\t\tif ( type === 'filter' ) {\n\t\t\tvar fomatters = DataTable.ext.type.search;\n\t\n\t\t\tif ( fomatters[ col.sType ] ) {\n\t\t\t\tcellData = fomatters[ col.sType ]( cellData );\n\t\t\t}\n\t\t}\n\t\n\t\treturn cellData;\n\t}\n\t\n\t\n\t/**\n\t * Set the value for a specific cell, into the internal data cache\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} rowIdx aoData row id\n\t *  @param {int} colIdx Column index\n\t *  @param {*} val Value to set\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSetCellData( settings, rowIdx, colIdx, val )\n\t{\n\t\tvar col     = settings.aoColumns[colIdx];\n\t\tvar rowData = settings.aoData[rowIdx]._aData;\n\t\n\t\tcol.fnSetData( rowData, val, {\n\t\t\tsettings: settings,\n\t\t\trow:      rowIdx,\n\t\t\tcol:      colIdx\n\t\t}  );\n\t}\n\t\n\t\n\t// Private variable that is used to match action syntax in the data property object\n\tvar __reArray = /\\[.*?\\]$/;\n\tvar __reFn = /\\(\\)$/;\n\t\n\t/**\n\t * Split string on periods, taking into account escaped periods\n\t * @param  {string} str String to split\n\t * @return {array} Split string\n\t */\n\tfunction _fnSplitObjNotation( str )\n\t{\n\t\treturn $.map( str.match(/(\\\\.|[^\\.])+/g) || [''], function ( s ) {\n\t\t\treturn s.replace(/\\\\\\./g, '.');\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Return a function that can be used to get data from a source object, taking\n\t * into account the ability to use nested objects as a source\n\t *  @param {string|int|function} mSource The data source for the object\n\t *  @returns {function} Data get function\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnGetObjectDataFn = DataTable.util.get;\n\t\n\t\n\t/**\n\t * Return a function that can be used to set data from a source object, taking\n\t * into account the ability to use nested objects as a source\n\t *  @param {string|int|function} mSource The data source for the object\n\t *  @returns {function} Data set function\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnSetObjectDataFn = DataTable.util.set;\n\t\n\t\n\t/**\n\t * Return an array with the full table data\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns array {array} aData Master data array\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetDataMaster ( settings )\n\t{\n\t\treturn _pluck( settings.aoData, '_aData' );\n\t}\n\t\n\t\n\t/**\n\t * Nuke the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnClearTable( settings )\n\t{\n\t\tsettings.aoData.length = 0;\n\t\tsettings.aiDisplayMaster.length = 0;\n\t\tsettings.aiDisplay.length = 0;\n\t\tsettings.aIds = {};\n\t}\n\t\n\t\n\t /**\n\t * Take an array of integers (index array) and remove a target integer (value - not\n\t * the key!)\n\t *  @param {array} a Index array to target\n\t *  @param {int} iTarget value to find\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDeleteIndex( a, iTarget, splice )\n\t{\n\t\tvar iTargetIndex = -1;\n\t\n\t\tfor ( var i=0, iLen=a.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tif ( a[i] == iTarget )\n\t\t\t{\n\t\t\t\tiTargetIndex = i;\n\t\t\t}\n\t\t\telse if ( a[i] > iTarget )\n\t\t\t{\n\t\t\t\ta[i]--;\n\t\t\t}\n\t\t}\n\t\n\t\tif ( iTargetIndex != -1 && splice === undefined )\n\t\t{\n\t\t\ta.splice( iTargetIndex, 1 );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Mark cached data as invalid such that a re-read of the data will occur when\n\t * the cached data is next requested. Also update from the data source object.\n\t *\n\t * @param {object} settings DataTables settings object\n\t * @param {int}    rowIdx   Row index to invalidate\n\t * @param {string} [src]    Source to invalidate from: undefined, 'auto', 'dom'\n\t *     or 'data'\n\t * @param {int}    [colIdx] Column index to invalidate. If undefined the whole\n\t *     row will be invalidated\n\t * @memberof DataTable#oApi\n\t *\n\t * @todo For the modularisation of v1.11 this will need to become a callback, so\n\t *   the sort and filter methods can subscribe to it. That will required\n\t *   initialisation options for sorting, which is why it is not already baked in\n\t */\n\tfunction _fnInvalidate( settings, rowIdx, src, colIdx )\n\t{\n\t\tvar row = settings.aoData[ rowIdx ];\n\t\tvar i, ien;\n\t\tvar cellWrite = function ( cell, col ) {\n\t\t\t// This is very frustrating, but in IE if you just write directly\n\t\t\t// to innerHTML, and elements that are overwritten are GC'ed,\n\t\t\t// even if there is a reference to them elsewhere\n\t\t\twhile ( cell.childNodes.length ) {\n\t\t\t\tcell.removeChild( cell.firstChild );\n\t\t\t}\n\t\n\t\t\tcell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );\n\t\t};\n\t\n\t\t// Are we reading last data from DOM or the data object?\n\t\tif ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {\n\t\t\t// Read the data from the DOM\n\t\t\trow._aData = _fnGetRowElements(\n\t\t\t\t\tsettings, row, colIdx, colIdx === undefined ? undefined : row._aData\n\t\t\t\t)\n\t\t\t\t.data;\n\t\t}\n\t\telse {\n\t\t\t// Reading from data object, update the DOM\n\t\t\tvar cells = row.anCells;\n\t\n\t\t\tif ( cells ) {\n\t\t\t\tif ( colIdx !== undefined ) {\n\t\t\t\t\tcellWrite( cells[colIdx], colIdx );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tfor ( i=0, ien=cells.length ; i<ien ; i++ ) {\n\t\t\t\t\t\tcellWrite( cells[i], i );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\t// For both row and cell invalidation, the cached data for sorting and\n\t\t// filtering is nulled out\n\t\trow._aSortData = null;\n\t\trow._aFilterData = null;\n\t\n\t\t// Invalidate the type for a specific column (if given) or all columns since\n\t\t// the data might have changed\n\t\tvar cols = settings.aoColumns;\n\t\tif ( colIdx !== undefined ) {\n\t\t\tcols[ colIdx ].sType = null;\n\t\t}\n\t\telse {\n\t\t\tfor ( i=0, ien=cols.length ; i<ien ; i++ ) {\n\t\t\t\tcols[i].sType = null;\n\t\t\t}\n\t\n\t\t\t// Update DataTables special `DT_*` attributes for the row\n\t\t\t_fnRowAttributes( settings, row );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Build a data source object from an HTML row, reading the contents of the\n\t * cells that are in the row.\n\t *\n\t * @param {object} settings DataTables settings object\n\t * @param {node|object} TR element from which to read data or existing row\n\t *   object from which to re-read the data from the cells\n\t * @param {int} [colIdx] Optional column index\n\t * @param {array|object} [d] Data source object. If `colIdx` is given then this\n\t *   parameter should also be given and will be used to write the data into.\n\t *   Only the column in question will be written\n\t * @returns {object} Object with two parameters: `data` the data read, in\n\t *   document order, and `cells` and array of nodes (they can be useful to the\n\t *   caller, so rather than needing a second traversal to get them, just return\n\t *   them from here).\n\t * @memberof DataTable#oApi\n\t */\n\tfunction _fnGetRowElements( settings, row, colIdx, d )\n\t{\n\t\tvar\n\t\t\ttds = [],\n\t\t\ttd = row.firstChild,\n\t\t\tname, col, o, i=0, contents,\n\t\t\tcolumns = settings.aoColumns,\n\t\t\tobjectRead = settings._rowReadObject;\n\t\n\t\t// Allow the data object to be passed in, or construct\n\t\td = d !== undefined ?\n\t\t\td :\n\t\t\tobjectRead ?\n\t\t\t\t{} :\n\t\t\t\t[];\n\t\n\t\tvar attr = function ( str, td  ) {\n\t\t\tif ( typeof str === 'string' ) {\n\t\t\t\tvar idx = str.indexOf('@');\n\t\n\t\t\t\tif ( idx !== -1 ) {\n\t\t\t\t\tvar attr = str.substring( idx+1 );\n\t\t\t\t\tvar setter = _fnSetObjectDataFn( str );\n\t\t\t\t\tsetter( d, td.getAttribute( attr ) );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\n\t\t// Read data from a cell and store into the data object\n\t\tvar cellProcess = function ( cell ) {\n\t\t\tif ( colIdx === undefined || colIdx === i ) {\n\t\t\t\tcol = columns[i];\n\t\t\t\tcontents = (cell.innerHTML).trim();\n\t\n\t\t\t\tif ( col && col._bAttrSrc ) {\n\t\t\t\t\tvar setter = _fnSetObjectDataFn( col.mData._ );\n\t\t\t\t\tsetter( d, contents );\n\t\n\t\t\t\t\tattr( col.mData.sort, cell );\n\t\t\t\t\tattr( col.mData.type, cell );\n\t\t\t\t\tattr( col.mData.filter, cell );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Depending on the `data` option for the columns the data can\n\t\t\t\t\t// be read to either an object or an array.\n\t\t\t\t\tif ( objectRead ) {\n\t\t\t\t\t\tif ( ! col._setter ) {\n\t\t\t\t\t\t\t// Cache the setter function\n\t\t\t\t\t\t\tcol._setter = _fnSetObjectDataFn( col.mData );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcol._setter( d, contents );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\td[i] = contents;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\ti++;\n\t\t};\n\t\n\t\tif ( td ) {\n\t\t\t// `tr` element was passed in\n\t\t\twhile ( td ) {\n\t\t\t\tname = td.nodeName.toUpperCase();\n\t\n\t\t\t\tif ( name == \"TD\" || name == \"TH\" ) {\n\t\t\t\t\tcellProcess( td );\n\t\t\t\t\ttds.push( td );\n\t\t\t\t}\n\t\n\t\t\t\ttd = td.nextSibling;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Existing row object passed in\n\t\t\ttds = row.anCells;\n\t\n\t\t\tfor ( var j=0, jen=tds.length ; j<jen ; j++ ) {\n\t\t\t\tcellProcess( tds[j] );\n\t\t\t}\n\t\t}\n\t\n\t\t// Read the ID from the DOM if present\n\t\tvar rowNode = row.firstChild ? row : row.nTr;\n\t\n\t\tif ( rowNode ) {\n\t\t\tvar id = rowNode.getAttribute( 'id' );\n\t\n\t\t\tif ( id ) {\n\t\t\t\t_fnSetObjectDataFn( settings.rowId )( d, id );\n\t\t\t}\n\t\t}\n\t\n\t\treturn {\n\t\t\tdata: d,\n\t\t\tcells: tds\n\t\t};\n\t}\n\t/**\n\t * Create a new TR element (and it's TD children) for a row\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iRow Row to consider\n\t *  @param {node} [nTrIn] TR element to add to the table - optional. If not given,\n\t *    DataTables will create a row automatically\n\t *  @param {array} [anTds] Array of TD|TH elements for the row - must be given\n\t *    if nTr is.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCreateTr ( oSettings, iRow, nTrIn, anTds )\n\t{\n\t\tvar\n\t\t\trow = oSettings.aoData[iRow],\n\t\t\trowData = row._aData,\n\t\t\tcells = [],\n\t\t\tnTr, nTd, oCol,\n\t\t\ti, iLen, create;\n\t\n\t\tif ( row.nTr === null )\n\t\t{\n\t\t\tnTr = nTrIn || document.createElement('tr');\n\t\n\t\t\trow.nTr = nTr;\n\t\t\trow.anCells = cells;\n\t\n\t\t\t/* Use a private property on the node to allow reserve mapping from the node\n\t\t\t * to the aoData array for fast look up\n\t\t\t */\n\t\t\tnTr._DT_RowIndex = iRow;\n\t\n\t\t\t/* Special parameters can be given by the data source to be used on the row */\n\t\t\t_fnRowAttributes( oSettings, row );\n\t\n\t\t\t/* Process each column */\n\t\t\tfor ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\toCol = oSettings.aoColumns[i];\n\t\t\t\tcreate = nTrIn ? false : true;\n\t\n\t\t\t\tnTd = create ? document.createElement( oCol.sCellType ) : anTds[i];\n\t\n\t\t\t\tif (! nTd) {\n\t\t\t\t\t_fnLog( oSettings, 0, 'Incorrect column count', 18 );\n\t\t\t\t}\n\t\n\t\t\t\tnTd._DT_CellIndex = {\n\t\t\t\t\trow: iRow,\n\t\t\t\t\tcolumn: i\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\tcells.push( nTd );\n\t\n\t\t\t\t// Need to create the HTML if new, or if a rendering function is defined\n\t\t\t\tif ( create || ((oCol.mRender || oCol.mData !== i) &&\n\t\t\t\t\t (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')\n\t\t\t\t)) {\n\t\t\t\t\tnTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );\n\t\t\t\t}\n\t\n\t\t\t\t/* Add user defined class */\n\t\t\t\tif ( oCol.sClass )\n\t\t\t\t{\n\t\t\t\t\tnTd.className += ' '+oCol.sClass;\n\t\t\t\t}\n\t\n\t\t\t\t// Visibility - add or remove as required\n\t\t\t\tif ( oCol.bVisible && ! nTrIn )\n\t\t\t\t{\n\t\t\t\t\tnTr.appendChild( nTd );\n\t\t\t\t}\n\t\t\t\telse if ( ! oCol.bVisible && nTrIn )\n\t\t\t\t{\n\t\t\t\t\tnTd.parentNode.removeChild( nTd );\n\t\t\t\t}\n\t\n\t\t\t\tif ( oCol.fnCreatedCell )\n\t\t\t\t{\n\t\t\t\t\toCol.fnCreatedCell.call( oSettings.oInstance,\n\t\t\t\t\t\tnTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Add attributes to a row based on the special `DT_*` parameters in a data\n\t * source object.\n\t *  @param {object} settings DataTables settings object\n\t *  @param {object} DataTables row object for the row to be modified\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnRowAttributes( settings, row )\n\t{\n\t\tvar tr = row.nTr;\n\t\tvar data = row._aData;\n\t\n\t\tif ( tr ) {\n\t\t\tvar id = settings.rowIdFn( data );\n\t\n\t\t\tif ( id ) {\n\t\t\t\ttr.id = id;\n\t\t\t}\n\t\n\t\t\tif ( data.DT_RowClass ) {\n\t\t\t\t// Remove any classes added by DT_RowClass before\n\t\t\t\tvar a = data.DT_RowClass.split(' ');\n\t\t\t\trow.__rowc = row.__rowc ?\n\t\t\t\t\t_unique( row.__rowc.concat( a ) ) :\n\t\t\t\t\ta;\n\t\n\t\t\t\t$(tr)\n\t\t\t\t\t.removeClass( row.__rowc.join(' ') )\n\t\t\t\t\t.addClass( data.DT_RowClass );\n\t\t\t}\n\t\n\t\t\tif ( data.DT_RowAttr ) {\n\t\t\t\t$(tr).attr( data.DT_RowAttr );\n\t\t\t}\n\t\n\t\t\tif ( data.DT_RowData ) {\n\t\t\t\t$(tr).data( data.DT_RowData );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Create the HTML header for the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnBuildHead( oSettings )\n\t{\n\t\tvar i, ien, cell, row, column;\n\t\tvar thead = oSettings.nTHead;\n\t\tvar tfoot = oSettings.nTFoot;\n\t\tvar createHeader = $('th, td', thead).length === 0;\n\t\tvar classes = oSettings.oClasses;\n\t\tvar columns = oSettings.aoColumns;\n\t\n\t\tif ( createHeader ) {\n\t\t\trow = $('<tr/>').appendTo( thead );\n\t\t}\n\t\n\t\tfor ( i=0, ien=columns.length ; i<ien ; i++ ) {\n\t\t\tcolumn = columns[i];\n\t\t\tcell = $( column.nTh ).addClass( column.sClass );\n\t\n\t\t\tif ( createHeader ) {\n\t\t\t\tcell.appendTo( row );\n\t\t\t}\n\t\n\t\t\t// 1.11 move into sorting\n\t\t\tif ( oSettings.oFeatures.bSort ) {\n\t\t\t\tcell.addClass( column.sSortingClass );\n\t\n\t\t\t\tif ( column.bSortable !== false ) {\n\t\t\t\t\tcell\n\t\t\t\t\t\t.attr( 'tabindex', oSettings.iTabIndex )\n\t\t\t\t\t\t.attr( 'aria-controls', oSettings.sTableId );\n\t\n\t\t\t\t\t_fnSortAttachListener( oSettings, column.nTh, i );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tif ( column.sTitle != cell[0].innerHTML ) {\n\t\t\t\tcell.html( column.sTitle );\n\t\t\t}\n\t\n\t\t\t_fnRenderer( oSettings, 'header' )(\n\t\t\t\toSettings, cell, column, classes\n\t\t\t);\n\t\t}\n\t\n\t\tif ( createHeader ) {\n\t\t\t_fnDetectHeader( oSettings.aoHeader, thead );\n\t\t}\n\t\n\t\t/* Deal with the footer - add classes if required */\n\t\t$(thead).children('tr').children('th, td').addClass( classes.sHeaderTH );\n\t\t$(tfoot).children('tr').children('th, td').addClass( classes.sFooterTH );\n\t\n\t\t// Cache the footer cells. Note that we only take the cells from the first\n\t\t// row in the footer. If there is more than one row the user wants to\n\t\t// interact with, they need to use the table().foot() method. Note also this\n\t\t// allows cells to be used for multiple columns using colspan\n\t\tif ( tfoot !== null ) {\n\t\t\tvar cells = oSettings.aoFooter[0];\n\t\n\t\t\tfor ( i=0, ien=cells.length ; i<ien ; i++ ) {\n\t\t\t\tcolumn = columns[i];\n\t\n\t\t\t\tif (column) {\n\t\t\t\t\tcolumn.nTf = cells[i].cell;\n\t\t\n\t\t\t\t\tif ( column.sClass ) {\n\t\t\t\t\t\t$(column.nTf).addClass( column.sClass );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_fnLog( oSettings, 0, 'Incorrect column count', 18 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Draw the header (or footer) element based on the column visibility states. The\n\t * methodology here is to use the layout array from _fnDetectHeader, modified for\n\t * the instantaneous column visibility, to construct the new layout. The grid is\n\t * traversed over cell at a time in a rows x columns grid fashion, although each\n\t * cell insert can cover multiple elements in the grid - which is tracks using the\n\t * aApplied array. Cell inserts in the grid will only occur where there isn't\n\t * already a cell in that position.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param array {objects} aoSource Layout array from _fnDetectHeader\n\t *  @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDrawHead( oSettings, aoSource, bIncludeHidden )\n\t{\n\t\tvar i, iLen, j, jLen, k, kLen, n, nLocalTr;\n\t\tvar aoLocal = [];\n\t\tvar aApplied = [];\n\t\tvar iColumns = oSettings.aoColumns.length;\n\t\tvar iRowspan, iColspan;\n\t\n\t\tif ( ! aoSource )\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\n\t\tif (  bIncludeHidden === undefined )\n\t\t{\n\t\t\tbIncludeHidden = false;\n\t\t}\n\t\n\t\t/* Make a copy of the master layout array, but without the visible columns in it */\n\t\tfor ( i=0, iLen=aoSource.length ; i<iLen ; i++ )\n\t\t{\n\t\t\taoLocal[i] = aoSource[i].slice();\n\t\t\taoLocal[i].nTr = aoSource[i].nTr;\n\t\n\t\t\t/* Remove any columns which are currently hidden */\n\t\t\tfor ( j=iColumns-1 ; j>=0 ; j-- )\n\t\t\t{\n\t\t\t\tif ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )\n\t\t\t\t{\n\t\t\t\t\taoLocal[i].splice( j, 1 );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t/* Prep the applied array - it needs an element for each row */\n\t\t\taApplied.push( [] );\n\t\t}\n\t\n\t\tfor ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tnLocalTr = aoLocal[i].nTr;\n\t\n\t\t\t/* All cells are going to be replaced, so empty out the row */\n\t\t\tif ( nLocalTr )\n\t\t\t{\n\t\t\t\twhile( (n = nLocalTr.firstChild) )\n\t\t\t\t{\n\t\t\t\t\tnLocalTr.removeChild( n );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tfor ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )\n\t\t\t{\n\t\t\t\tiRowspan = 1;\n\t\t\t\tiColspan = 1;\n\t\n\t\t\t\t/* Check to see if there is already a cell (row/colspan) covering our target\n\t\t\t\t * insert point. If there is, then there is nothing to do.\n\t\t\t\t */\n\t\t\t\tif ( aApplied[i][j] === undefined )\n\t\t\t\t{\n\t\t\t\t\tnLocalTr.appendChild( aoLocal[i][j].cell );\n\t\t\t\t\taApplied[i][j] = 1;\n\t\n\t\t\t\t\t/* Expand the cell to cover as many rows as needed */\n\t\t\t\t\twhile ( aoLocal[i+iRowspan] !== undefined &&\n\t\t\t\t\t        aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )\n\t\t\t\t\t{\n\t\t\t\t\t\taApplied[i+iRowspan][j] = 1;\n\t\t\t\t\t\tiRowspan++;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* Expand the cell to cover as many columns as needed */\n\t\t\t\t\twhile ( aoLocal[i][j+iColspan] !== undefined &&\n\t\t\t\t\t        aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Must update the applied array over the rows for the columns */\n\t\t\t\t\t\tfor ( k=0 ; k<iRowspan ; k++ )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taApplied[i+k][j+iColspan] = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tiColspan++;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* Do the actual expansion in the DOM */\n\t\t\t\t\t$(aoLocal[i][j].cell)\n\t\t\t\t\t\t.attr('rowspan', iRowspan)\n\t\t\t\t\t\t.attr('colspan', iColspan);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Insert the required TR nodes into the table for display\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param ajaxComplete true after ajax call to complete rendering\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDraw( oSettings, ajaxComplete )\n\t{\n\t\t// Allow for state saving and a custom start position\n\t\t_fnStart( oSettings );\n\t\n\t\t/* Provide a pre-callback function which can be used to cancel the draw is false is returned */\n\t\tvar aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );\n\t\tif ( $.inArray( false, aPreDraw ) !== -1 )\n\t\t{\n\t\t\t_fnProcessingDisplay( oSettings, false );\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar anRows = [];\n\t\tvar iRowCount = 0;\n\t\tvar asStripeClasses = oSettings.asStripeClasses;\n\t\tvar iStripes = asStripeClasses.length;\n\t\tvar oLang = oSettings.oLanguage;\n\t\tvar bServerSide = _fnDataSource( oSettings ) == 'ssp';\n\t\tvar aiDisplay = oSettings.aiDisplay;\n\t\tvar iDisplayStart = oSettings._iDisplayStart;\n\t\tvar iDisplayEnd = oSettings.fnDisplayEnd();\n\t\n\t\toSettings.bDrawing = true;\n\t\n\t\t/* Server-side processing draw intercept */\n\t\tif ( oSettings.bDeferLoading )\n\t\t{\n\t\t\toSettings.bDeferLoading = false;\n\t\t\toSettings.iDraw++;\n\t\t\t_fnProcessingDisplay( oSettings, false );\n\t\t}\n\t\telse if ( !bServerSide )\n\t\t{\n\t\t\toSettings.iDraw++;\n\t\t}\n\t\telse if ( !oSettings.bDestroying && !ajaxComplete)\n\t\t{\n\t\t\t_fnAjaxUpdate( oSettings );\n\t\t\treturn;\n\t\t}\n\t\n\t\tif ( aiDisplay.length !== 0 )\n\t\t{\n\t\t\tvar iStart = bServerSide ? 0 : iDisplayStart;\n\t\t\tvar iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;\n\t\n\t\t\tfor ( var j=iStart ; j<iEnd ; j++ )\n\t\t\t{\n\t\t\t\tvar iDataIndex = aiDisplay[j];\n\t\t\t\tvar aoData = oSettings.aoData[ iDataIndex ];\n\t\t\t\tif ( aoData.nTr === null )\n\t\t\t\t{\n\t\t\t\t\t_fnCreateTr( oSettings, iDataIndex );\n\t\t\t\t}\n\t\n\t\t\t\tvar nRow = aoData.nTr;\n\t\n\t\t\t\t/* Remove the old striping classes and then add the new one */\n\t\t\t\tif ( iStripes !== 0 )\n\t\t\t\t{\n\t\t\t\t\tvar sStripe = asStripeClasses[ iRowCount % iStripes ];\n\t\t\t\t\tif ( aoData._sRowStripe != sStripe )\n\t\t\t\t\t{\n\t\t\t\t\t\t$(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );\n\t\t\t\t\t\taoData._sRowStripe = sStripe;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Row callback functions - might want to manipulate the row\n\t\t\t\t// iRowCount and j are not currently documented. Are they at all\n\t\t\t\t// useful?\n\t\t\t\t_fnCallbackFire( oSettings, 'aoRowCallback', null,\n\t\t\t\t\t[nRow, aoData._aData, iRowCount, j, iDataIndex] );\n\t\n\t\t\t\tanRows.push( nRow );\n\t\t\t\tiRowCount++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Table is empty - create a row with an empty message in it */\n\t\t\tvar sZero = oLang.sZeroRecords;\n\t\t\tif ( oSettings.iDraw == 1 &&  _fnDataSource( oSettings ) == 'ajax' )\n\t\t\t{\n\t\t\t\tsZero = oLang.sLoadingRecords;\n\t\t\t}\n\t\t\telse if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )\n\t\t\t{\n\t\t\t\tsZero = oLang.sEmptyTable;\n\t\t\t}\n\t\n\t\t\tanRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )\n\t\t\t\t.append( $('<td />', {\n\t\t\t\t\t'valign':  'top',\n\t\t\t\t\t'colSpan': _fnVisbleColumns( oSettings ),\n\t\t\t\t\t'class':   oSettings.oClasses.sRowEmpty\n\t\t\t\t} ).html( sZero ) )[0];\n\t\t}\n\t\n\t\t/* Header and footer callbacks */\n\t\t_fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],\n\t\t\t_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );\n\t\n\t\t_fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],\n\t\t\t_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );\n\t\n\t\tvar body = $(oSettings.nTBody);\n\t\n\t\tbody.children().detach();\n\t\tbody.append( $(anRows) );\n\t\n\t\t/* Call all required callback functions for the end of a draw */\n\t\t_fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );\n\t\n\t\t/* Draw is complete, sorting and filtering must be as well */\n\t\toSettings.bSorted = false;\n\t\toSettings.bFiltered = false;\n\t\toSettings.bDrawing = false;\n\t}\n\t\n\t\n\t/**\n\t * Redraw the table - taking account of the various features which are enabled\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {boolean} [holdPosition] Keep the current paging position. By default\n\t *    the paging is reset to the first page\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnReDraw( settings, holdPosition )\n\t{\n\t\tvar\n\t\t\tfeatures = settings.oFeatures,\n\t\t\tsort     = features.bSort,\n\t\t\tfilter   = features.bFilter;\n\t\n\t\tif ( sort ) {\n\t\t\t_fnSort( settings );\n\t\t}\n\t\n\t\tif ( filter ) {\n\t\t\t_fnFilterComplete( settings, settings.oPreviousSearch );\n\t\t}\n\t\telse {\n\t\t\t// No filtering, so we want to just use the display master\n\t\t\tsettings.aiDisplay = settings.aiDisplayMaster.slice();\n\t\t}\n\t\n\t\tif ( holdPosition !== true ) {\n\t\t\tsettings._iDisplayStart = 0;\n\t\t}\n\t\n\t\t// Let any modules know about the draw hold position state (used by\n\t\t// scrolling internally)\n\t\tsettings._drawHold = holdPosition;\n\t\n\t\t_fnDraw( settings );\n\t\n\t\tsettings._drawHold = false;\n\t}\n\t\n\t\n\t/**\n\t * Add the options to the page HTML for the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddOptionsHtml ( oSettings )\n\t{\n\t\tvar classes = oSettings.oClasses;\n\t\tvar table = $(oSettings.nTable);\n\t\tvar holding = $('<div/>').insertBefore( table ); // Holding element for speed\n\t\tvar features = oSettings.oFeatures;\n\t\n\t\t// All DataTables are wrapped in a div\n\t\tvar insert = $('<div/>', {\n\t\t\tid:      oSettings.sTableId+'_wrapper',\n\t\t\t'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)\n\t\t} );\n\t\n\t\toSettings.nHolding = holding[0];\n\t\toSettings.nTableWrapper = insert[0];\n\t\toSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;\n\t\n\t\t/* Loop over the user set positioning and place the elements as needed */\n\t\tvar aDom = oSettings.sDom.split('');\n\t\tvar featureNode, cOption, nNewNode, cNext, sAttr, j;\n\t\tfor ( var i=0 ; i<aDom.length ; i++ )\n\t\t{\n\t\t\tfeatureNode = null;\n\t\t\tcOption = aDom[i];\n\t\n\t\t\tif ( cOption == '<' )\n\t\t\t{\n\t\t\t\t/* New container div */\n\t\t\t\tnNewNode = $('<div/>')[0];\n\t\n\t\t\t\t/* Check to see if we should append an id and/or a class name to the container */\n\t\t\t\tcNext = aDom[i+1];\n\t\t\t\tif ( cNext == \"'\" || cNext == '\"' )\n\t\t\t\t{\n\t\t\t\t\tsAttr = \"\";\n\t\t\t\t\tj = 2;\n\t\t\t\t\twhile ( aDom[i+j] != cNext )\n\t\t\t\t\t{\n\t\t\t\t\t\tsAttr += aDom[i+j];\n\t\t\t\t\t\tj++;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* Replace jQuery UI constants @todo depreciated */\n\t\t\t\t\tif ( sAttr == \"H\" )\n\t\t\t\t\t{\n\t\t\t\t\t\tsAttr = classes.sJUIHeader;\n\t\t\t\t\t}\n\t\t\t\t\telse if ( sAttr == \"F\" )\n\t\t\t\t\t{\n\t\t\t\t\t\tsAttr = classes.sJUIFooter;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* The attribute can be in the format of \"#id.class\", \"#id\" or \"class\" This logic\n\t\t\t\t\t * breaks the string into parts and applies them as needed\n\t\t\t\t\t */\n\t\t\t\t\tif ( sAttr.indexOf('.') != -1 )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar aSplit = sAttr.split('.');\n\t\t\t\t\t\tnNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);\n\t\t\t\t\t\tnNewNode.className = aSplit[1];\n\t\t\t\t\t}\n\t\t\t\t\telse if ( sAttr.charAt(0) == \"#\" )\n\t\t\t\t\t{\n\t\t\t\t\t\tnNewNode.id = sAttr.substr(1, sAttr.length-1);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tnNewNode.className = sAttr;\n\t\t\t\t\t}\n\t\n\t\t\t\t\ti += j; /* Move along the position array */\n\t\t\t\t}\n\t\n\t\t\t\tinsert.append( nNewNode );\n\t\t\t\tinsert = $(nNewNode);\n\t\t\t}\n\t\t\telse if ( cOption == '>' )\n\t\t\t{\n\t\t\t\t/* End container div */\n\t\t\t\tinsert = insert.parent();\n\t\t\t}\n\t\t\t// @todo Move options into their own plugins?\n\t\t\telse if ( cOption == 'l' && features.bPaginate && features.bLengthChange )\n\t\t\t{\n\t\t\t\t/* Length */\n\t\t\t\tfeatureNode = _fnFeatureHtmlLength( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 'f' && features.bFilter )\n\t\t\t{\n\t\t\t\t/* Filter */\n\t\t\t\tfeatureNode = _fnFeatureHtmlFilter( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 'r' && features.bProcessing )\n\t\t\t{\n\t\t\t\t/* pRocessing */\n\t\t\t\tfeatureNode = _fnFeatureHtmlProcessing( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 't' )\n\t\t\t{\n\t\t\t\t/* Table */\n\t\t\t\tfeatureNode = _fnFeatureHtmlTable( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption ==  'i' && features.bInfo )\n\t\t\t{\n\t\t\t\t/* Info */\n\t\t\t\tfeatureNode = _fnFeatureHtmlInfo( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 'p' && features.bPaginate )\n\t\t\t{\n\t\t\t\t/* Pagination */\n\t\t\t\tfeatureNode = _fnFeatureHtmlPaginate( oSettings );\n\t\t\t}\n\t\t\telse if ( DataTable.ext.feature.length !== 0 )\n\t\t\t{\n\t\t\t\t/* Plug-in features */\n\t\t\t\tvar aoFeatures = DataTable.ext.feature;\n\t\t\t\tfor ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )\n\t\t\t\t{\n\t\t\t\t\tif ( cOption == aoFeatures[k].cFeature )\n\t\t\t\t\t{\n\t\t\t\t\t\tfeatureNode = aoFeatures[k].fnInit( oSettings );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t/* Add to the 2D features array */\n\t\t\tif ( featureNode )\n\t\t\t{\n\t\t\t\tvar aanFeatures = oSettings.aanFeatures;\n\t\n\t\t\t\tif ( ! aanFeatures[cOption] )\n\t\t\t\t{\n\t\t\t\t\taanFeatures[cOption] = [];\n\t\t\t\t}\n\t\n\t\t\t\taanFeatures[cOption].push( featureNode );\n\t\t\t\tinsert.append( featureNode );\n\t\t\t}\n\t\t}\n\t\n\t\t/* Built our DOM structure - replace the holding div with what we want */\n\t\tholding.replaceWith( insert );\n\t\toSettings.nHolding = null;\n\t}\n\t\n\t\n\t/**\n\t * Use the DOM source to create up an array of header cells. The idea here is to\n\t * create a layout grid (array) of rows x columns, which contains a reference\n\t * to the cell that that point in the grid (regardless of col/rowspan), such that\n\t * any column / row could be removed and the new grid constructed\n\t *  @param array {object} aLayout Array to store the calculated layout in\n\t *  @param {node} nThead The header/footer element for the table\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDetectHeader ( aLayout, nThead )\n\t{\n\t\tvar nTrs = $(nThead).children('tr');\n\t\tvar nTr, nCell;\n\t\tvar i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;\n\t\tvar bUnique;\n\t\tvar fnShiftCol = function ( a, i, j ) {\n\t\t\tvar k = a[i];\n\t                while ( k[j] ) {\n\t\t\t\tj++;\n\t\t\t}\n\t\t\treturn j;\n\t\t};\n\t\n\t\taLayout.splice( 0, aLayout.length );\n\t\n\t\t/* We know how many rows there are in the layout - so prep it */\n\t\tfor ( i=0, iLen=nTrs.length ; i<iLen ; i++ )\n\t\t{\n\t\t\taLayout.push( [] );\n\t\t}\n\t\n\t\t/* Calculate a layout array */\n\t\tfor ( i=0, iLen=nTrs.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tnTr = nTrs[i];\n\t\t\tiColumn = 0;\n\t\n\t\t\t/* For every cell in the row... */\n\t\t\tnCell = nTr.firstChild;\n\t\t\twhile ( nCell ) {\n\t\t\t\tif ( nCell.nodeName.toUpperCase() == \"TD\" ||\n\t\t\t\t     nCell.nodeName.toUpperCase() == \"TH\" )\n\t\t\t\t{\n\t\t\t\t\t/* Get the col and rowspan attributes from the DOM and sanitise them */\n\t\t\t\t\tiColspan = nCell.getAttribute('colspan') * 1;\n\t\t\t\t\tiRowspan = nCell.getAttribute('rowspan') * 1;\n\t\t\t\t\tiColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;\n\t\t\t\t\tiRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;\n\t\n\t\t\t\t\t/* There might be colspan cells already in this row, so shift our target\n\t\t\t\t\t * accordingly\n\t\t\t\t\t */\n\t\t\t\t\tiColShifted = fnShiftCol( aLayout, i, iColumn );\n\t\n\t\t\t\t\t/* Cache calculation for unique columns */\n\t\t\t\t\tbUnique = iColspan === 1 ? true : false;\n\t\n\t\t\t\t\t/* If there is col / rowspan, copy the information into the layout grid */\n\t\t\t\t\tfor ( l=0 ; l<iColspan ; l++ )\n\t\t\t\t\t{\n\t\t\t\t\t\tfor ( k=0 ; k<iRowspan ; k++ )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taLayout[i+k][iColShifted+l] = {\n\t\t\t\t\t\t\t\t\"cell\": nCell,\n\t\t\t\t\t\t\t\t\"unique\": bUnique\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\taLayout[i+k].nTr = nTr;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnCell = nCell.nextSibling;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Get an array of unique th elements, one for each column\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {node} nHeader automatically detect the layout from this node - optional\n\t *  @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional\n\t *  @returns array {node} aReturn list of unique th's\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetUniqueThs ( oSettings, nHeader, aLayout )\n\t{\n\t\tvar aReturn = [];\n\t\tif ( !aLayout )\n\t\t{\n\t\t\taLayout = oSettings.aoHeader;\n\t\t\tif ( nHeader )\n\t\t\t{\n\t\t\t\taLayout = [];\n\t\t\t\t_fnDetectHeader( aLayout, nHeader );\n\t\t\t}\n\t\t}\n\t\n\t\tfor ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tfor ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )\n\t\t\t{\n\t\t\t\tif ( aLayout[i][j].unique &&\n\t\t\t\t\t (!aReturn[j] || !oSettings.bSortCellsTop) )\n\t\t\t\t{\n\t\t\t\t\taReturn[j] = aLayout[i][j].cell;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn aReturn;\n\t}\n\t\n\t/**\n\t * Set the start position for draw\n\t *  @param {object} oSettings dataTables settings object\n\t */\n\tfunction _fnStart( oSettings )\n\t{\n\t\tvar bServerSide = _fnDataSource( oSettings ) == 'ssp';\n\t\tvar iInitDisplayStart = oSettings.iInitDisplayStart;\n\t\n\t\t// Check and see if we have an initial draw position from state saving\n\t\tif ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )\n\t\t{\n\t\t\toSettings._iDisplayStart = bServerSide ?\n\t\t\t\tiInitDisplayStart :\n\t\t\t\tiInitDisplayStart >= oSettings.fnRecordsDisplay() ?\n\t\t\t\t\t0 :\n\t\t\t\t\tiInitDisplayStart;\n\t\n\t\t\toSettings.iInitDisplayStart = -1;\n\t\t}\n\t}\n\t\n\t/**\n\t * Create an Ajax call based on the table's settings, taking into account that\n\t * parameters can have multiple forms, and backwards compatibility.\n\t *\n\t * @param {object} oSettings dataTables settings object\n\t * @param {array} data Data to send to the server, required by\n\t *     DataTables - may be augmented by developer callbacks\n\t * @param {function} fn Callback function to run when data is obtained\n\t */\n\tfunction _fnBuildAjax( oSettings, data, fn )\n\t{\n\t\t// Compatibility with 1.9-, allow fnServerData and event to manipulate\n\t\t_fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );\n\t\n\t\t// Convert to object based for 1.10+ if using the old array scheme which can\n\t\t// come from server-side processing or serverParams\n\t\tif ( data && Array.isArray(data) ) {\n\t\t\tvar tmp = {};\n\t\t\tvar rbracket = /(.*?)\\[\\]$/;\n\t\n\t\t\t$.each( data, function (key, val) {\n\t\t\t\tvar match = val.name.match(rbracket);\n\t\n\t\t\t\tif ( match ) {\n\t\t\t\t\t// Support for arrays\n\t\t\t\t\tvar name = match[0];\n\t\n\t\t\t\t\tif ( ! tmp[ name ] ) {\n\t\t\t\t\t\ttmp[ name ] = [];\n\t\t\t\t\t}\n\t\t\t\t\ttmp[ name ].push( val.value );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\ttmp[val.name] = val.value;\n\t\t\t\t}\n\t\t\t} );\n\t\t\tdata = tmp;\n\t\t}\n\t\n\t\tvar ajaxData;\n\t\tvar ajax = oSettings.ajax;\n\t\tvar instance = oSettings.oInstance;\n\t\tvar callback = function ( json ) {\n\t\t\tvar status = oSettings.jqXHR\n\t\t\t\t? oSettings.jqXHR.status\n\t\t\t\t: null;\n\t\n\t\t\tif ( json === null || (typeof status === 'number' && status == 204 ) ) {\n\t\t\t\tjson = {};\n\t\t\t\t_fnAjaxDataSrc( oSettings, json, [] );\n\t\t\t}\n\t\n\t\t\tvar error = json.error || json.sError;\n\t\t\tif ( error ) {\n\t\t\t\t_fnLog( oSettings, 0, error );\n\t\t\t}\n\t\n\t\t\toSettings.json = json;\n\t\n\t\t\t_fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );\n\t\t\tfn( json );\n\t\t};\n\t\n\t\tif ( $.isPlainObject( ajax ) && ajax.data )\n\t\t{\n\t\t\tajaxData = ajax.data;\n\t\n\t\t\tvar newData = typeof ajaxData === 'function' ?\n\t\t\t\tajaxData( data, oSettings ) :  // fn can manipulate data or return\n\t\t\t\tajaxData;                      // an object object or array to merge\n\t\n\t\t\t// If the function returned something, use that alone\n\t\t\tdata = typeof ajaxData === 'function' && newData ?\n\t\t\t\tnewData :\n\t\t\t\t$.extend( true, data, newData );\n\t\n\t\t\t// Remove the data property as we've resolved it already and don't want\n\t\t\t// jQuery to do it again (it is restored at the end of the function)\n\t\t\tdelete ajax.data;\n\t\t}\n\t\n\t\tvar baseAjax = {\n\t\t\t\"data\": data,\n\t\t\t\"success\": callback,\n\t\t\t\"dataType\": \"json\",\n\t\t\t\"cache\": false,\n\t\t\t\"type\": oSettings.sServerMethod,\n\t\t\t\"error\": function (xhr, error, thrown) {\n\t\t\t\tvar ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );\n\t\n\t\t\t\tif ( $.inArray( true, ret ) === -1 ) {\n\t\t\t\t\tif ( error == \"parsererror\" ) {\n\t\t\t\t\t\t_fnLog( oSettings, 0, 'Invalid JSON response', 1 );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( xhr.readyState === 4 ) {\n\t\t\t\t\t\t_fnLog( oSettings, 0, 'Ajax error', 7 );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t_fnProcessingDisplay( oSettings, false );\n\t\t\t}\n\t\t};\n\t\n\t\t// Store the data submitted for the API\n\t\toSettings.oAjaxData = data;\n\t\n\t\t// Allow plug-ins and external processes to modify the data\n\t\t_fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );\n\t\n\t\tif ( oSettings.fnServerData )\n\t\t{\n\t\t\t// DataTables 1.9- compatibility\n\t\t\toSettings.fnServerData.call( instance,\n\t\t\t\toSettings.sAjaxSource,\n\t\t\t\t$.map( data, function (val, key) { // Need to convert back to 1.9 trad format\n\t\t\t\t\treturn { name: key, value: val };\n\t\t\t\t} ),\n\t\t\t\tcallback,\n\t\t\t\toSettings\n\t\t\t);\n\t\t}\n\t\telse if ( oSettings.sAjaxSource || typeof ajax === 'string' )\n\t\t{\n\t\t\t// DataTables 1.9- compatibility\n\t\t\toSettings.jqXHR = $.ajax( $.extend( baseAjax, {\n\t\t\t\turl: ajax || oSettings.sAjaxSource\n\t\t\t} ) );\n\t\t}\n\t\telse if ( typeof ajax === 'function' )\n\t\t{\n\t\t\t// Is a function - let the caller define what needs to be done\n\t\t\toSettings.jqXHR = ajax.call( instance, data, callback, oSettings );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Object to extend the base settings\n\t\t\toSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );\n\t\n\t\t\t// Restore for next time around\n\t\t\tajax.data = ajaxData;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Update the table using an Ajax call\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {boolean} Block the table drawing or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAjaxUpdate( settings )\n\t{\n\t\tsettings.iDraw++;\n\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t// Keep track of drawHold state to handle scrolling after the Ajax call\n\t\tvar drawHold = settings._drawHold;\n\t\n\t\t_fnBuildAjax(\n\t\t\tsettings,\n\t\t\t_fnAjaxParameters( settings ),\n\t\t\tfunction(json) {\n\t\t\t\tsettings._drawHold = drawHold;\n\t\t\t\t_fnAjaxUpdateDraw( settings, json );\n\t\t\t\tsettings._drawHold = false;\n\t\t\t}\n\t\t);\n\t}\n\t\n\t\n\t/**\n\t * Build up the parameters in an object needed for a server-side processing\n\t * request. Note that this is basically done twice, is different ways - a modern\n\t * method which is used by default in DataTables 1.10 which uses objects and\n\t * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if\n\t * the sAjaxSource option is used in the initialisation, or the legacyAjax\n\t * option is set.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {bool} block the table drawing or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAjaxParameters( settings )\n\t{\n\t\tvar\n\t\t\tcolumns = settings.aoColumns,\n\t\t\tcolumnCount = columns.length,\n\t\t\tfeatures = settings.oFeatures,\n\t\t\tpreSearch = settings.oPreviousSearch,\n\t\t\tpreColSearch = settings.aoPreSearchCols,\n\t\t\ti, data = [], dataProp, column, columnSearch,\n\t\t\tsort = _fnSortFlatten( settings ),\n\t\t\tdisplayStart = settings._iDisplayStart,\n\t\t\tdisplayLength = features.bPaginate !== false ?\n\t\t\t\tsettings._iDisplayLength :\n\t\t\t\t-1;\n\t\n\t\tvar param = function ( name, value ) {\n\t\t\tdata.push( { 'name': name, 'value': value } );\n\t\t};\n\t\n\t\t// DataTables 1.9- compatible method\n\t\tparam( 'sEcho',          settings.iDraw );\n\t\tparam( 'iColumns',       columnCount );\n\t\tparam( 'sColumns',       _pluck( columns, 'sName' ).join(',') );\n\t\tparam( 'iDisplayStart',  displayStart );\n\t\tparam( 'iDisplayLength', displayLength );\n\t\n\t\t// DataTables 1.10+ method\n\t\tvar d = {\n\t\t\tdraw:    settings.iDraw,\n\t\t\tcolumns: [],\n\t\t\torder:   [],\n\t\t\tstart:   displayStart,\n\t\t\tlength:  displayLength,\n\t\t\tsearch:  {\n\t\t\t\tvalue: preSearch.sSearch,\n\t\t\t\tregex: preSearch.bRegex\n\t\t\t}\n\t\t};\n\t\n\t\tfor ( i=0 ; i<columnCount ; i++ ) {\n\t\t\tcolumn = columns[i];\n\t\t\tcolumnSearch = preColSearch[i];\n\t\t\tdataProp = typeof column.mData==\"function\" ? 'function' : column.mData ;\n\t\n\t\t\td.columns.push( {\n\t\t\t\tdata:       dataProp,\n\t\t\t\tname:       column.sName,\n\t\t\t\tsearchable: column.bSearchable,\n\t\t\t\torderable:  column.bSortable,\n\t\t\t\tsearch:     {\n\t\t\t\t\tvalue: columnSearch.sSearch,\n\t\t\t\t\tregex: columnSearch.bRegex\n\t\t\t\t}\n\t\t\t} );\n\t\n\t\t\tparam( \"mDataProp_\"+i, dataProp );\n\t\n\t\t\tif ( features.bFilter ) {\n\t\t\t\tparam( 'sSearch_'+i,     columnSearch.sSearch );\n\t\t\t\tparam( 'bRegex_'+i,      columnSearch.bRegex );\n\t\t\t\tparam( 'bSearchable_'+i, column.bSearchable );\n\t\t\t}\n\t\n\t\t\tif ( features.bSort ) {\n\t\t\t\tparam( 'bSortable_'+i, column.bSortable );\n\t\t\t}\n\t\t}\n\t\n\t\tif ( features.bFilter ) {\n\t\t\tparam( 'sSearch', preSearch.sSearch );\n\t\t\tparam( 'bRegex', preSearch.bRegex );\n\t\t}\n\t\n\t\tif ( features.bSort ) {\n\t\t\t$.each( sort, function ( i, val ) {\n\t\t\t\td.order.push( { column: val.col, dir: val.dir } );\n\t\n\t\t\t\tparam( 'iSortCol_'+i, val.col );\n\t\t\t\tparam( 'sSortDir_'+i, val.dir );\n\t\t\t} );\n\t\n\t\t\tparam( 'iSortingCols', sort.length );\n\t\t}\n\t\n\t\t// If the legacy.ajax parameter is null, then we automatically decide which\n\t\t// form to use, based on sAjaxSource\n\t\tvar legacy = DataTable.ext.legacy.ajax;\n\t\tif ( legacy === null ) {\n\t\t\treturn settings.sAjaxSource ? data : d;\n\t\t}\n\t\n\t\t// Otherwise, if legacy has been specified then we use that to decide on the\n\t\t// form\n\t\treturn legacy ? data : d;\n\t}\n\t\n\t\n\t/**\n\t * Data the data from the server (nuking the old) and redraw the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} json json data return from the server.\n\t *  @param {string} json.sEcho Tracking flag for DataTables to match requests\n\t *  @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering\n\t *  @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering\n\t *  @param {array} json.aaData The data to display on this page\n\t *  @param {string} [json.sColumns] Column ordering (sName, comma separated)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAjaxUpdateDraw ( settings, json )\n\t{\n\t\t// v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.\n\t\t// Support both\n\t\tvar compat = function ( old, modern ) {\n\t\t\treturn json[old] !== undefined ? json[old] : json[modern];\n\t\t};\n\t\n\t\tvar data = _fnAjaxDataSrc( settings, json );\n\t\tvar draw            = compat( 'sEcho',                'draw' );\n\t\tvar recordsTotal    = compat( 'iTotalRecords',        'recordsTotal' );\n\t\tvar recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );\n\t\n\t\tif ( draw !== undefined ) {\n\t\t\t// Protect against out of sequence returns\n\t\t\tif ( draw*1 < settings.iDraw ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettings.iDraw = draw * 1;\n\t\t}\n\t\n\t\t// No data in returned object, so rather than an array, we show an empty table\n\t\tif ( ! data ) {\n\t\t\tdata = [];\n\t\t}\n\t\n\t\t_fnClearTable( settings );\n\t\tsettings._iRecordsTotal   = parseInt(recordsTotal, 10);\n\t\tsettings._iRecordsDisplay = parseInt(recordsFiltered, 10);\n\t\n\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t_fnAddData( settings, data[i] );\n\t\t}\n\t\tsettings.aiDisplay = settings.aiDisplayMaster.slice();\n\t\n\t\t_fnDraw( settings, true );\n\t\n\t\tif ( ! settings._bInitComplete ) {\n\t\t\t_fnInitComplete( settings, json );\n\t\t}\n\t\n\t\t_fnProcessingDisplay( settings, false );\n\t}\n\t\n\t\n\t/**\n\t * Get the data from the JSON data source to use for drawing a table. Using\n\t * `_fnGetObjectDataFn` allows the data to be sourced from a property of the\n\t * source object, or from a processing function.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param  {object} json Data source object / array from the server\n\t *  @return {array} Array of data to use\n\t */\n\t function _fnAjaxDataSrc ( oSettings, json, write )\n\t {\n\t\tvar dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?\n\t\t\toSettings.ajax.dataSrc :\n\t\t\toSettings.sAjaxDataProp; // Compatibility with 1.9-.\n\t\n\t\tif ( ! write ) {\n\t\t\tif ( dataSrc === 'data' ) {\n\t\t\t\t// If the default, then we still want to support the old style, and safely ignore\n\t\t\t\t// it if possible\n\t\t\t\treturn json.aaData || json[dataSrc];\n\t\t\t}\n\t\n\t\t\treturn dataSrc !== \"\" ?\n\t\t\t\t_fnGetObjectDataFn( dataSrc )( json ) :\n\t\t\t\tjson;\n\t\t}\n\t\n\t\t// set\n\t\t_fnSetObjectDataFn( dataSrc )( json, write );\n\t}\n\t\n\t/**\n\t * Generate the node required for filtering text\n\t *  @returns {node} Filter control element\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlFilter ( settings )\n\t{\n\t\tvar classes = settings.oClasses;\n\t\tvar tableId = settings.sTableId;\n\t\tvar language = settings.oLanguage;\n\t\tvar previousSearch = settings.oPreviousSearch;\n\t\tvar features = settings.aanFeatures;\n\t\tvar input = '<input type=\"search\" class=\"'+classes.sFilterInput+'\"/>';\n\t\n\t\tvar str = language.sSearch;\n\t\tstr = str.match(/_INPUT_/) ?\n\t\t\tstr.replace('_INPUT_', input) :\n\t\t\tstr+input;\n\t\n\t\tvar filter = $('<div/>', {\n\t\t\t\t'id': ! features.f ? tableId+'_filter' : null,\n\t\t\t\t'class': classes.sFilter\n\t\t\t} )\n\t\t\t.append( $('<label/>' ).append( str ) );\n\t\n\t\tvar searchFn = function(event) {\n\t\t\t/* Update all other filter input elements for the new display */\n\t\t\tvar n = features.f;\n\t\t\tvar val = !this.value ? \"\" : this.value; // mental IE8 fix :-(\n\t\t\tif(previousSearch.return && event.key !== \"Enter\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t/* Now do the filter */\n\t\t\tif ( val != previousSearch.sSearch ) {\n\t\t\t\t_fnFilterComplete( settings, {\n\t\t\t\t\t\"sSearch\": val,\n\t\t\t\t\t\"bRegex\": previousSearch.bRegex,\n\t\t\t\t\t\"bSmart\": previousSearch.bSmart ,\n\t\t\t\t\t\"bCaseInsensitive\": previousSearch.bCaseInsensitive,\n\t\t\t\t\t\"return\": previousSearch.return\n\t\t\t\t} );\n\t\n\t\t\t\t// Need to redraw, without resorting\n\t\t\t\tsettings._iDisplayStart = 0;\n\t\t\t\t_fnDraw( settings );\n\t\t\t}\n\t\t};\n\t\n\t\tvar searchDelay = settings.searchDelay !== null ?\n\t\t\tsettings.searchDelay :\n\t\t\t_fnDataSource( settings ) === 'ssp' ?\n\t\t\t\t400 :\n\t\t\t\t0;\n\t\n\t\tvar jqFilter = $('input', filter)\n\t\t\t.val( previousSearch.sSearch )\n\t\t\t.attr( 'placeholder', language.sSearchPlaceholder )\n\t\t\t.on(\n\t\t\t\t'keyup.DT search.DT input.DT paste.DT cut.DT',\n\t\t\t\tsearchDelay ?\n\t\t\t\t\t_fnThrottle( searchFn, searchDelay ) :\n\t\t\t\t\tsearchFn\n\t\t\t)\n\t\t\t.on( 'mouseup.DT', function(e) {\n\t\t\t\t// Edge fix! Edge 17 does not trigger anything other than mouse events when clicking\n\t\t\t\t// on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`\n\t\t\t\t// checks the value to see if it has changed. In other browsers it won't have.\n\t\t\t\tsetTimeout( function () {\n\t\t\t\t\tsearchFn.call(jqFilter[0], e);\n\t\t\t\t}, 10);\n\t\t\t} )\n\t\t\t.on( 'keypress.DT', function(e) {\n\t\t\t\t/* Prevent form submission */\n\t\t\t\tif ( e.keyCode == 13 ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} )\n\t\t\t.attr('aria-controls', tableId);\n\t\n\t\t// Update the input elements whenever the table is filtered\n\t\t$(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {\n\t\t\tif ( settings === s ) {\n\t\t\t\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t\t\t\t// inside an iframe or frame...\n\t\t\t\ttry {\n\t\t\t\t\tif ( jqFilter[0] !== document.activeElement ) {\n\t\t\t\t\t\tjqFilter.val( previousSearch.sSearch );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch ( e ) {}\n\t\t\t}\n\t\t} );\n\t\n\t\treturn filter[0];\n\t}\n\t\n\t\n\t/**\n\t * Filter the table using both the global filter and column based filtering\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} oSearch search information\n\t *  @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterComplete ( oSettings, oInput, iForce )\n\t{\n\t\tvar oPrevSearch = oSettings.oPreviousSearch;\n\t\tvar aoPrevSearch = oSettings.aoPreSearchCols;\n\t\tvar fnSaveFilter = function ( oFilter ) {\n\t\t\t/* Save the filtering values */\n\t\t\toPrevSearch.sSearch = oFilter.sSearch;\n\t\t\toPrevSearch.bRegex = oFilter.bRegex;\n\t\t\toPrevSearch.bSmart = oFilter.bSmart;\n\t\t\toPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;\n\t\t\toPrevSearch.return = oFilter.return;\n\t\t};\n\t\tvar fnRegex = function ( o ) {\n\t\t\t// Backwards compatibility with the bEscapeRegex option\n\t\t\treturn o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;\n\t\t};\n\t\n\t\t// Resolve any column types that are unknown due to addition or invalidation\n\t\t// @todo As per sort - can this be moved into an event handler?\n\t\t_fnColumnTypes( oSettings );\n\t\n\t\t/* In server-side processing all filtering is done by the server, so no point hanging around here */\n\t\tif ( _fnDataSource( oSettings ) != 'ssp' )\n\t\t{\n\t\t\t/* Global filter */\n\t\t\t_fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );\n\t\t\tfnSaveFilter( oInput );\n\t\n\t\t\t/* Now do the individual column filter */\n\t\t\tfor ( var i=0 ; i<aoPrevSearch.length ; i++ )\n\t\t\t{\n\t\t\t\t_fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),\n\t\t\t\t\taoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );\n\t\t\t}\n\t\n\t\t\t/* Custom filtering */\n\t\t\t_fnFilterCustom( oSettings );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfnSaveFilter( oInput );\n\t\t}\n\t\n\t\t/* Tell the draw function we have been filtering */\n\t\toSettings.bFiltered = true;\n\t\t_fnCallbackFire( oSettings, null, 'search', [oSettings] );\n\t}\n\t\n\t\n\t/**\n\t * Apply custom filtering functions\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterCustom( settings )\n\t{\n\t\tvar filters = DataTable.ext.search;\n\t\tvar displayRows = settings.aiDisplay;\n\t\tvar row, rowIdx;\n\t\n\t\tfor ( var i=0, ien=filters.length ; i<ien ; i++ ) {\n\t\t\tvar rows = [];\n\t\n\t\t\t// Loop over each row and see if it should be included\n\t\t\tfor ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {\n\t\t\t\trowIdx = displayRows[ j ];\n\t\t\t\trow = settings.aoData[ rowIdx ];\n\t\n\t\t\t\tif ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {\n\t\t\t\t\trows.push( rowIdx );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// So the array reference doesn't break set the results into the\n\t\t\t// existing array\n\t\t\tdisplayRows.length = 0;\n\t\t\t$.merge( displayRows, rows );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Filter the table on a per-column basis\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {string} sInput string to filter on\n\t *  @param {int} iColumn column to filter\n\t *  @param {bool} bRegex treat search string as a regular expression or not\n\t *  @param {bool} bSmart use smart filtering or not\n\t *  @param {bool} bCaseInsensitive Do case insensitive matching or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )\n\t{\n\t\tif ( searchStr === '' ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar data;\n\t\tvar out = [];\n\t\tvar display = settings.aiDisplay;\n\t\tvar rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );\n\t\n\t\tfor ( var i=0 ; i<display.length ; i++ ) {\n\t\t\tdata = settings.aoData[ display[i] ]._aFilterData[ colIdx ];\n\t\n\t\t\tif ( rpSearch.test( data ) ) {\n\t\t\t\tout.push( display[i] );\n\t\t\t}\n\t\t}\n\t\n\t\tsettings.aiDisplay = out;\n\t}\n\t\n\t\n\t/**\n\t * Filter the data table based on user input and draw the table\n\t *  @param {object} settings dataTables settings object\n\t *  @param {string} input string to filter on\n\t *  @param {int} force optional - force a research of the master array (1) or not (undefined or 0)\n\t *  @param {bool} regex treat as a regular expression or not\n\t *  @param {bool} smart perform smart filtering or not\n\t *  @param {bool} caseInsensitive Do case insensitive matching or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilter( settings, input, force, regex, smart, caseInsensitive )\n\t{\n\t\tvar rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );\n\t\tvar prevSearch = settings.oPreviousSearch.sSearch;\n\t\tvar displayMaster = settings.aiDisplayMaster;\n\t\tvar display, invalidated, i;\n\t\tvar filtered = [];\n\t\n\t\t// Need to take account of custom filtering functions - always filter\n\t\tif ( DataTable.ext.search.length !== 0 ) {\n\t\t\tforce = true;\n\t\t}\n\t\n\t\t// Check if any of the rows were invalidated\n\t\tinvalidated = _fnFilterData( settings );\n\t\n\t\t// If the input is blank - we just want the full data set\n\t\tif ( input.length <= 0 ) {\n\t\t\tsettings.aiDisplay = displayMaster.slice();\n\t\t}\n\t\telse {\n\t\t\t// New search - start from the master array\n\t\t\tif ( invalidated ||\n\t\t\t\t force ||\n\t\t\t\t regex ||\n\t\t\t\t prevSearch.length > input.length ||\n\t\t\t\t input.indexOf(prevSearch) !== 0 ||\n\t\t\t\t settings.bSorted // On resort, the display master needs to be\n\t\t\t\t                  // re-filtered since indexes will have changed\n\t\t\t) {\n\t\t\t\tsettings.aiDisplay = displayMaster.slice();\n\t\t\t}\n\t\n\t\t\t// Search the display array\n\t\t\tdisplay = settings.aiDisplay;\n\t\n\t\t\tfor ( i=0 ; i<display.length ; i++ ) {\n\t\t\t\tif ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {\n\t\t\t\t\tfiltered.push( display[i] );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tsettings.aiDisplay = filtered;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Build a regular expression object suitable for searching a table\n\t *  @param {string} sSearch string to search for\n\t *  @param {bool} bRegex treat as a regular expression or not\n\t *  @param {bool} bSmart perform smart filtering or not\n\t *  @param {bool} bCaseInsensitive Do case insensitive matching or not\n\t *  @returns {RegExp} constructed object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterCreateSearch( search, regex, smart, caseInsensitive )\n\t{\n\t\tsearch = regex ?\n\t\t\tsearch :\n\t\t\t_fnEscapeRegex( search );\n\t\t\n\t\tif ( smart ) {\n\t\t\t/* For smart filtering we want to allow the search to work regardless of\n\t\t\t * word order. We also want double quoted text to be preserved, so word\n\t\t\t * order is important - a la google. So this is what we want to\n\t\t\t * generate:\n\t\t\t * \n\t\t\t * ^(?=.*?\\bone\\b)(?=.*?\\btwo three\\b)(?=.*?\\bfour\\b).*$\n\t\t\t */\n\t\t\tvar a = $.map( search.match( /[\"\\u201C][^\"\\u201D]+[\"\\u201D]|[^ ]+/g ) || [''], function ( word ) {\n\t\t\t\tif ( word.charAt(0) === '\"' ) {\n\t\t\t\t\tvar m = word.match( /^\"(.*)\"$/ );\n\t\t\t\t\tword = m ? m[1] : word;\n\t\t\t\t}\n\t\t\t\telse if ( word.charAt(0) === '\\u201C' ) {\n\t\t\t\t\tvar m = word.match( /^\\u201C(.*)\\u201D$/ );\n\t\t\t\t\tword = m ? m[1] : word;\n\t\t\t\t}\n\t\n\t\t\t\treturn word.replace('\"', '');\n\t\t\t} );\n\t\n\t\t\tsearch = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';\n\t\t}\n\t\n\t\treturn new RegExp( search, caseInsensitive ? 'i' : '' );\n\t}\n\t\n\t\n\t/**\n\t * Escape a string such that it can be used in a regular expression\n\t *  @param {string} sVal string to escape\n\t *  @returns {string} escaped string\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnEscapeRegex = DataTable.util.escapeRegex;\n\t\n\tvar __filter_div = $('<div>')[0];\n\tvar __filter_div_textContent = __filter_div.textContent !== undefined;\n\t\n\t// Update the filtering data for each row if needed (by invalidation or first run)\n\tfunction _fnFilterData ( settings )\n\t{\n\t\tvar columns = settings.aoColumns;\n\t\tvar column;\n\t\tvar i, j, ien, jen, filterData, cellData, row;\n\t\tvar wasInvalidated = false;\n\t\n\t\tfor ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\trow = settings.aoData[i];\n\t\n\t\t\tif ( ! row._aFilterData ) {\n\t\t\t\tfilterData = [];\n\t\n\t\t\t\tfor ( j=0, jen=columns.length ; j<jen ; j++ ) {\n\t\t\t\t\tcolumn = columns[j];\n\t\n\t\t\t\t\tif ( column.bSearchable ) {\n\t\t\t\t\t\tcellData = _fnGetCellData( settings, i, j, 'filter' );\n\t\n\t\t\t\t\t\t// Search in DataTables 1.10 is string based. In 1.11 this\n\t\t\t\t\t\t// should be altered to also allow strict type checking.\n\t\t\t\t\t\tif ( cellData === null ) {\n\t\t\t\t\t\t\tcellData = '';\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tif ( typeof cellData !== 'string' && cellData.toString ) {\n\t\t\t\t\t\t\tcellData = cellData.toString();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tcellData = '';\n\t\t\t\t\t}\n\t\n\t\t\t\t\t// If it looks like there is an HTML entity in the string,\n\t\t\t\t\t// attempt to decode it so sorting works as expected. Note that\n\t\t\t\t\t// we could use a single line of jQuery to do this, but the DOM\n\t\t\t\t\t// method used here is much faster http://jsperf.com/html-decode\n\t\t\t\t\tif ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {\n\t\t\t\t\t\t__filter_div.innerHTML = cellData;\n\t\t\t\t\t\tcellData = __filter_div_textContent ?\n\t\t\t\t\t\t\t__filter_div.textContent :\n\t\t\t\t\t\t\t__filter_div.innerText;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tif ( cellData.replace ) {\n\t\t\t\t\t\tcellData = cellData.replace(/[\\r\\n\\u2028]/g, '');\n\t\t\t\t\t}\n\t\n\t\t\t\t\tfilterData.push( cellData );\n\t\t\t\t}\n\t\n\t\t\t\trow._aFilterData = filterData;\n\t\t\t\trow._sFilterRow = filterData.join('  ');\n\t\t\t\twasInvalidated = true;\n\t\t\t}\n\t\t}\n\t\n\t\treturn wasInvalidated;\n\t}\n\t\n\t\n\t/**\n\t * Convert from the internal Hungarian notation to camelCase for external\n\t * interaction\n\t *  @param {object} obj Object to convert\n\t *  @returns {object} Inverted object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSearchToCamel ( obj )\n\t{\n\t\treturn {\n\t\t\tsearch:          obj.sSearch,\n\t\t\tsmart:           obj.bSmart,\n\t\t\tregex:           obj.bRegex,\n\t\t\tcaseInsensitive: obj.bCaseInsensitive\n\t\t};\n\t}\n\t\n\t\n\t\n\t/**\n\t * Convert from camelCase notation to the internal Hungarian. We could use the\n\t * Hungarian convert function here, but this is cleaner\n\t *  @param {object} obj Object to convert\n\t *  @returns {object} Inverted object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSearchToHung ( obj )\n\t{\n\t\treturn {\n\t\t\tsSearch:          obj.search,\n\t\t\tbSmart:           obj.smart,\n\t\t\tbRegex:           obj.regex,\n\t\t\tbCaseInsensitive: obj.caseInsensitive\n\t\t};\n\t}\n\t\n\t/**\n\t * Generate the node required for the info display\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {node} Information element\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlInfo ( settings )\n\t{\n\t\tvar\n\t\t\ttid = settings.sTableId,\n\t\t\tnodes = settings.aanFeatures.i,\n\t\t\tn = $('<div/>', {\n\t\t\t\t'class': settings.oClasses.sInfo,\n\t\t\t\t'id': ! nodes ? tid+'_info' : null\n\t\t\t} );\n\t\n\t\tif ( ! nodes ) {\n\t\t\t// Update display on each draw\n\t\t\tsettings.aoDrawCallback.push( {\n\t\t\t\t\"fn\": _fnUpdateInfo,\n\t\t\t\t\"sName\": \"information\"\n\t\t\t} );\n\t\n\t\t\tn\n\t\t\t\t.attr( 'role', 'status' )\n\t\t\t\t.attr( 'aria-live', 'polite' );\n\t\n\t\t\t// Table is described by our info div\n\t\t\t$(settings.nTable).attr( 'aria-describedby', tid+'_info' );\n\t\t}\n\t\n\t\treturn n[0];\n\t}\n\t\n\t\n\t/**\n\t * Update the information elements in the display\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnUpdateInfo ( settings )\n\t{\n\t\t/* Show information about the table */\n\t\tvar nodes = settings.aanFeatures.i;\n\t\tif ( nodes.length === 0 ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar\n\t\t\tlang  = settings.oLanguage,\n\t\t\tstart = settings._iDisplayStart+1,\n\t\t\tend   = settings.fnDisplayEnd(),\n\t\t\tmax   = settings.fnRecordsTotal(),\n\t\t\ttotal = settings.fnRecordsDisplay(),\n\t\t\tout   = total ?\n\t\t\t\tlang.sInfo :\n\t\t\t\tlang.sInfoEmpty;\n\t\n\t\tif ( total !== max ) {\n\t\t\t/* Record set after filtering */\n\t\t\tout += ' ' + lang.sInfoFiltered;\n\t\t}\n\t\n\t\t// Convert the macros\n\t\tout += lang.sInfoPostFix;\n\t\tout = _fnInfoMacros( settings, out );\n\t\n\t\tvar callback = lang.fnInfoCallback;\n\t\tif ( callback !== null ) {\n\t\t\tout = callback.call( settings.oInstance,\n\t\t\t\tsettings, start, end, max, total, out\n\t\t\t);\n\t\t}\n\t\n\t\t$(nodes).html( out );\n\t}\n\t\n\t\n\tfunction _fnInfoMacros ( settings, str )\n\t{\n\t\t// When infinite scrolling, we are always starting at 1. _iDisplayStart is used only\n\t\t// internally\n\t\tvar\n\t\t\tformatter  = settings.fnFormatNumber,\n\t\t\tstart      = settings._iDisplayStart+1,\n\t\t\tlen        = settings._iDisplayLength,\n\t\t\tvis        = settings.fnRecordsDisplay(),\n\t\t\tall        = len === -1;\n\t\n\t\treturn str.\n\t\t\treplace(/_START_/g, formatter.call( settings, start ) ).\n\t\t\treplace(/_END_/g,   formatter.call( settings, settings.fnDisplayEnd() ) ).\n\t\t\treplace(/_MAX_/g,   formatter.call( settings, settings.fnRecordsTotal() ) ).\n\t\t\treplace(/_TOTAL_/g, formatter.call( settings, vis ) ).\n\t\t\treplace(/_PAGE_/g,  formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).\n\t\t\treplace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );\n\t}\n\t\n\t\n\t\n\t/**\n\t * Draw the table for the first time, adding all required features\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnInitialise ( settings )\n\t{\n\t\tvar i, iLen, iAjaxStart=settings.iInitDisplayStart;\n\t\tvar columns = settings.aoColumns, column;\n\t\tvar features = settings.oFeatures;\n\t\tvar deferLoading = settings.bDeferLoading; // value modified by the draw\n\t\n\t\t/* Ensure that the table data is fully initialised */\n\t\tif ( ! settings.bInitialised ) {\n\t\t\tsetTimeout( function(){ _fnInitialise( settings ); }, 200 );\n\t\t\treturn;\n\t\t}\n\t\n\t\t/* Show the display HTML options */\n\t\t_fnAddOptionsHtml( settings );\n\t\n\t\t/* Build and draw the header / footer for the table */\n\t\t_fnBuildHead( settings );\n\t\t_fnDrawHead( settings, settings.aoHeader );\n\t\t_fnDrawHead( settings, settings.aoFooter );\n\t\n\t\t/* Okay to show that something is going on now */\n\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t/* Calculate sizes for columns */\n\t\tif ( features.bAutoWidth ) {\n\t\t\t_fnCalculateColumnWidths( settings );\n\t\t}\n\t\n\t\tfor ( i=0, iLen=columns.length ; i<iLen ; i++ ) {\n\t\t\tcolumn = columns[i];\n\t\n\t\t\tif ( column.sWidth ) {\n\t\t\t\tcolumn.nTh.style.width = _fnStringToCss( column.sWidth );\n\t\t\t}\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'preInit', [settings] );\n\t\n\t\t// If there is default sorting required - let's do it. The sort function\n\t\t// will do the drawing for us. Otherwise we draw the table regardless of the\n\t\t// Ajax source - this allows the table to look initialised for Ajax sourcing\n\t\t// data (show 'loading' message possibly)\n\t\t_fnReDraw( settings );\n\t\n\t\t// Server-side processing init complete is done by _fnAjaxUpdateDraw\n\t\tvar dataSrc = _fnDataSource( settings );\n\t\tif ( dataSrc != 'ssp' || deferLoading ) {\n\t\t\t// if there is an ajax source load the data\n\t\t\tif ( dataSrc == 'ajax' ) {\n\t\t\t\t_fnBuildAjax( settings, [], function(json) {\n\t\t\t\t\tvar aData = _fnAjaxDataSrc( settings, json );\n\t\n\t\t\t\t\t// Got the data - add it to the table\n\t\t\t\t\tfor ( i=0 ; i<aData.length ; i++ ) {\n\t\t\t\t\t\t_fnAddData( settings, aData[i] );\n\t\t\t\t\t}\n\t\n\t\t\t\t\t// Reset the init display for cookie saving. We've already done\n\t\t\t\t\t// a filter, and therefore cleared it before. So we need to make\n\t\t\t\t\t// it appear 'fresh'\n\t\t\t\t\tsettings.iInitDisplayStart = iAjaxStart;\n\t\n\t\t\t\t\t_fnReDraw( settings );\n\t\n\t\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t\t\t_fnInitComplete( settings, json );\n\t\t\t\t}, settings );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t\t_fnInitComplete( settings );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Draw the table for the first time, adding all required features\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} [json] JSON from the server that completed the table, if using Ajax source\n\t *    with client-side processing (optional)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnInitComplete ( settings, json )\n\t{\n\t\tsettings._bInitComplete = true;\n\t\n\t\t// When data was added after the initialisation (data or Ajax) we need to\n\t\t// calculate the column sizing\n\t\tif ( json || settings.oInit.aaData ) {\n\t\t\t_fnAdjustColumnSizing( settings );\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'plugin-init', [settings, json] );\n\t\t_fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );\n\t}\n\t\n\t\n\tfunction _fnLengthChange ( settings, val )\n\t{\n\t\tvar len = parseInt( val, 10 );\n\t\tsettings._iDisplayLength = len;\n\t\n\t\t_fnLengthOverflow( settings );\n\t\n\t\t// Fire length change event\n\t\t_fnCallbackFire( settings, null, 'length', [settings, len] );\n\t}\n\t\n\t\n\t/**\n\t * Generate the node required for user display length changing\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {node} Display length feature node\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlLength ( settings )\n\t{\n\t\tvar\n\t\t\tclasses  = settings.oClasses,\n\t\t\ttableId  = settings.sTableId,\n\t\t\tmenu     = settings.aLengthMenu,\n\t\t\td2       = Array.isArray( menu[0] ),\n\t\t\tlengths  = d2 ? menu[0] : menu,\n\t\t\tlanguage = d2 ? menu[1] : menu;\n\t\n\t\tvar select = $('<select/>', {\n\t\t\t'name':          tableId+'_length',\n\t\t\t'aria-controls': tableId,\n\t\t\t'class':         classes.sLengthSelect\n\t\t} );\n\t\n\t\tfor ( var i=0, ien=lengths.length ; i<ien ; i++ ) {\n\t\t\tselect[0][ i ] = new Option(\n\t\t\t\ttypeof language[i] === 'number' ?\n\t\t\t\t\tsettings.fnFormatNumber( language[i] ) :\n\t\t\t\t\tlanguage[i],\n\t\t\t\tlengths[i]\n\t\t\t);\n\t\t}\n\t\n\t\tvar div = $('<div><label/></div>').addClass( classes.sLength );\n\t\tif ( ! settings.aanFeatures.l ) {\n\t\t\tdiv[0].id = tableId+'_length';\n\t\t}\n\t\n\t\tdiv.children().append(\n\t\t\tsettings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )\n\t\t);\n\t\n\t\t// Can't use `select` variable as user might provide their own and the\n\t\t// reference is broken by the use of outerHTML\n\t\t$('select', div)\n\t\t\t.val( settings._iDisplayLength )\n\t\t\t.on( 'change.DT', function(e) {\n\t\t\t\t_fnLengthChange( settings, $(this).val() );\n\t\t\t\t_fnDraw( settings );\n\t\t\t} );\n\t\n\t\t// Update node value whenever anything changes the table's length\n\t\t$(settings.nTable).on( 'length.dt.DT', function (e, s, len) {\n\t\t\tif ( settings === s ) {\n\t\t\t\t$('select', div).val( len );\n\t\t\t}\n\t\t} );\n\t\n\t\treturn div[0];\n\t}\n\t\n\t\n\t\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Note that most of the paging logic is done in\n\t * DataTable.ext.pager\n\t */\n\t\n\t/**\n\t * Generate the node required for default pagination\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {node} Pagination feature node\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlPaginate ( settings )\n\t{\n\t\tvar\n\t\t\ttype   = settings.sPaginationType,\n\t\t\tplugin = DataTable.ext.pager[ type ],\n\t\t\tmodern = typeof plugin === 'function',\n\t\t\tredraw = function( settings ) {\n\t\t\t\t_fnDraw( settings );\n\t\t\t},\n\t\t\tnode = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],\n\t\t\tfeatures = settings.aanFeatures;\n\t\n\t\tif ( ! modern ) {\n\t\t\tplugin.fnInit( settings, node, redraw );\n\t\t}\n\t\n\t\t/* Add a draw callback for the pagination on first instance, to update the paging display */\n\t\tif ( ! features.p )\n\t\t{\n\t\t\tnode.id = settings.sTableId+'_paginate';\n\t\n\t\t\tsettings.aoDrawCallback.push( {\n\t\t\t\t\"fn\": function( settings ) {\n\t\t\t\t\tif ( modern ) {\n\t\t\t\t\t\tvar\n\t\t\t\t\t\t\tstart      = settings._iDisplayStart,\n\t\t\t\t\t\t\tlen        = settings._iDisplayLength,\n\t\t\t\t\t\t\tvisRecords = settings.fnRecordsDisplay(),\n\t\t\t\t\t\t\tall        = len === -1,\n\t\t\t\t\t\t\tpage = all ? 0 : Math.ceil( start / len ),\n\t\t\t\t\t\t\tpages = all ? 1 : Math.ceil( visRecords / len ),\n\t\t\t\t\t\t\tbuttons = plugin(page, pages),\n\t\t\t\t\t\t\ti, ien;\n\t\n\t\t\t\t\t\tfor ( i=0, ien=features.p.length ; i<ien ; i++ ) {\n\t\t\t\t\t\t\t_fnRenderer( settings, 'pageButton' )(\n\t\t\t\t\t\t\t\tsettings, features.p[i], i, buttons, page, pages\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tplugin.fnUpdate( settings, redraw );\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"sName\": \"pagination\"\n\t\t\t} );\n\t\t}\n\t\n\t\treturn node;\n\t}\n\t\n\t\n\t/**\n\t * Alter the display settings to change the page\n\t *  @param {object} settings DataTables settings object\n\t *  @param {string|int} action Paging action to take: \"first\", \"previous\",\n\t *    \"next\" or \"last\" or page number to jump to (integer)\n\t *  @param [bool] redraw Automatically draw the update or not\n\t *  @returns {bool} true page has changed, false - no change\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnPageChange ( settings, action, redraw )\n\t{\n\t\tvar\n\t\t\tstart     = settings._iDisplayStart,\n\t\t\tlen       = settings._iDisplayLength,\n\t\t\trecords   = settings.fnRecordsDisplay();\n\t\n\t\tif ( records === 0 || len === -1 )\n\t\t{\n\t\t\tstart = 0;\n\t\t}\n\t\telse if ( typeof action === \"number\" )\n\t\t{\n\t\t\tstart = action * len;\n\t\n\t\t\tif ( start > records )\n\t\t\t{\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t}\n\t\telse if ( action == \"first\" )\n\t\t{\n\t\t\tstart = 0;\n\t\t}\n\t\telse if ( action == \"previous\" )\n\t\t{\n\t\t\tstart = len >= 0 ?\n\t\t\t\tstart - len :\n\t\t\t\t0;\n\t\n\t\t\tif ( start < 0 )\n\t\t\t{\n\t\t\t  start = 0;\n\t\t\t}\n\t\t}\n\t\telse if ( action == \"next\" )\n\t\t{\n\t\t\tif ( start + len < records )\n\t\t\t{\n\t\t\t\tstart += len;\n\t\t\t}\n\t\t}\n\t\telse if ( action == \"last\" )\n\t\t{\n\t\t\tstart = Math.floor( (records-1) / len) * len;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_fnLog( settings, 0, \"Unknown paging action: \"+action, 5 );\n\t\t}\n\t\n\t\tvar changed = settings._iDisplayStart !== start;\n\t\tsettings._iDisplayStart = start;\n\t\n\t\tif ( changed ) {\n\t\t\t_fnCallbackFire( settings, null, 'page', [settings] );\n\t\n\t\t\tif ( redraw ) {\n\t\t\t\t_fnDraw( settings );\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// No change event - paging was called, but no change\n\t\t\t_fnCallbackFire( settings, null, 'page-nc', [settings] );\n\t\t}\n\t\n\t\treturn changed;\n\t}\n\t\n\t\n\t\n\t/**\n\t * Generate the node required for the processing node\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {node} Processing element\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlProcessing ( settings )\n\t{\n\t\treturn $('<div/>', {\n\t\t\t\t'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,\n\t\t\t\t'class': settings.oClasses.sProcessing,\n\t\t\t\t'role': 'status'\n\t\t\t} )\n\t\t\t.html( settings.oLanguage.sProcessing )\n\t\t\t.append('<div><div></div><div></div><div></div><div></div></div>')\n\t\t\t.insertBefore( settings.nTable )[0];\n\t}\n\t\n\t\n\t/**\n\t * Display or hide the processing indicator\n\t *  @param {object} settings dataTables settings object\n\t *  @param {bool} show Show the processing indicator (true) or not (false)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnProcessingDisplay ( settings, show )\n\t{\n\t\tif ( settings.oFeatures.bProcessing ) {\n\t\t\t$(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'processing', [settings, show] );\n\t}\n\t\n\t/**\n\t * Add any control elements for the table - specifically scrolling\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {node} Node to add to the DOM\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlTable ( settings )\n\t{\n\t\tvar table = $(settings.nTable);\n\t\n\t\t// Scrolling from here on in\n\t\tvar scroll = settings.oScroll;\n\t\n\t\tif ( scroll.sX === '' && scroll.sY === '' ) {\n\t\t\treturn settings.nTable;\n\t\t}\n\t\n\t\tvar scrollX = scroll.sX;\n\t\tvar scrollY = scroll.sY;\n\t\tvar classes = settings.oClasses;\n\t\tvar caption = table.children('caption');\n\t\tvar captionSide = caption.length ? caption[0]._captionSide : null;\n\t\tvar headerClone = $( table[0].cloneNode(false) );\n\t\tvar footerClone = $( table[0].cloneNode(false) );\n\t\tvar footer = table.children('tfoot');\n\t\tvar _div = '<div/>';\n\t\tvar size = function ( s ) {\n\t\t\treturn !s ? null : _fnStringToCss( s );\n\t\t};\n\t\n\t\tif ( ! footer.length ) {\n\t\t\tfooter = null;\n\t\t}\n\t\n\t\t/*\n\t\t * The HTML structure that we want to generate in this function is:\n\t\t *  div - scroller\n\t\t *    div - scroll head\n\t\t *      div - scroll head inner\n\t\t *        table - scroll head table\n\t\t *          thead - thead\n\t\t *    div - scroll body\n\t\t *      table - table (master table)\n\t\t *        thead - thead clone for sizing\n\t\t *        tbody - tbody\n\t\t *    div - scroll foot\n\t\t *      div - scroll foot inner\n\t\t *        table - scroll foot table\n\t\t *          tfoot - tfoot\n\t\t */\n\t\tvar scroller = $( _div, { 'class': classes.sScrollWrapper } )\n\t\t\t.append(\n\t\t\t\t$(_div, { 'class': classes.sScrollHead } )\n\t\t\t\t\t.css( {\n\t\t\t\t\t\toverflow: 'hidden',\n\t\t\t\t\t\tposition: 'relative',\n\t\t\t\t\t\tborder: 0,\n\t\t\t\t\t\twidth: scrollX ? size(scrollX) : '100%'\n\t\t\t\t\t} )\n\t\t\t\t\t.append(\n\t\t\t\t\t\t$(_div, { 'class': classes.sScrollHeadInner } )\n\t\t\t\t\t\t\t.css( {\n\t\t\t\t\t\t\t\t'box-sizing': 'content-box',\n\t\t\t\t\t\t\t\twidth: scroll.sXInner || '100%'\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\theaderClone\n\t\t\t\t\t\t\t\t\t.removeAttr('id')\n\t\t\t\t\t\t\t\t\t.css( 'margin-left', 0 )\n\t\t\t\t\t\t\t\t\t.append( captionSide === 'top' ? caption : null )\n\t\t\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\t\t\ttable.children('thead')\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t)\n\t\t\t.append(\n\t\t\t\t$(_div, { 'class': classes.sScrollBody } )\n\t\t\t\t\t.css( {\n\t\t\t\t\t\tposition: 'relative',\n\t\t\t\t\t\toverflow: 'auto',\n\t\t\t\t\t\twidth: size( scrollX )\n\t\t\t\t\t} )\n\t\t\t\t\t.append( table )\n\t\t\t);\n\t\n\t\tif ( footer ) {\n\t\t\tscroller.append(\n\t\t\t\t$(_div, { 'class': classes.sScrollFoot } )\n\t\t\t\t\t.css( {\n\t\t\t\t\t\toverflow: 'hidden',\n\t\t\t\t\t\tborder: 0,\n\t\t\t\t\t\twidth: scrollX ? size(scrollX) : '100%'\n\t\t\t\t\t} )\n\t\t\t\t\t.append(\n\t\t\t\t\t\t$(_div, { 'class': classes.sScrollFootInner } )\n\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\tfooterClone\n\t\t\t\t\t\t\t\t\t.removeAttr('id')\n\t\t\t\t\t\t\t\t\t.css( 'margin-left', 0 )\n\t\t\t\t\t\t\t\t\t.append( captionSide === 'bottom' ? caption : null )\n\t\t\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\t\t\ttable.children('tfoot')\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\n\t\tvar children = scroller.children();\n\t\tvar scrollHead = children[0];\n\t\tvar scrollBody = children[1];\n\t\tvar scrollFoot = footer ? children[2] : null;\n\t\n\t\t// When the body is scrolled, then we also want to scroll the headers\n\t\tif ( scrollX ) {\n\t\t\t$(scrollBody).on( 'scroll.DT', function (e) {\n\t\t\t\tvar scrollLeft = this.scrollLeft;\n\t\n\t\t\t\tscrollHead.scrollLeft = scrollLeft;\n\t\n\t\t\t\tif ( footer ) {\n\t\t\t\t\tscrollFoot.scrollLeft = scrollLeft;\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t\n\t\t$(scrollBody).css('max-height', scrollY);\n\t\tif (! scroll.bCollapse) {\n\t\t\t$(scrollBody).css('height', scrollY);\n\t\t}\n\t\n\t\tsettings.nScrollHead = scrollHead;\n\t\tsettings.nScrollBody = scrollBody;\n\t\tsettings.nScrollFoot = scrollFoot;\n\t\n\t\t// On redraw - align columns\n\t\tsettings.aoDrawCallback.push( {\n\t\t\t\"fn\": _fnScrollDraw,\n\t\t\t\"sName\": \"scrolling\"\n\t\t} );\n\t\n\t\treturn scroller[0];\n\t}\n\t\n\t\n\t\n\t/**\n\t * Update the header, footer and body tables for resizing - i.e. column\n\t * alignment.\n\t *\n\t * Welcome to the most horrible function DataTables. The process that this\n\t * function follows is basically:\n\t *   1. Re-create the table inside the scrolling div\n\t *   2. Take live measurements from the DOM\n\t *   3. Apply the measurements to align the columns\n\t *   4. Clean up\n\t *\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnScrollDraw ( settings )\n\t{\n\t\t// Given that this is such a monster function, a lot of variables are use\n\t\t// to try and keep the minimised size as small as possible\n\t\tvar\n\t\t\tscroll         = settings.oScroll,\n\t\t\tscrollX        = scroll.sX,\n\t\t\tscrollXInner   = scroll.sXInner,\n\t\t\tscrollY        = scroll.sY,\n\t\t\tbarWidth       = scroll.iBarWidth,\n\t\t\tdivHeader      = $(settings.nScrollHead),\n\t\t\tdivHeaderStyle = divHeader[0].style,\n\t\t\tdivHeaderInner = divHeader.children('div'),\n\t\t\tdivHeaderInnerStyle = divHeaderInner[0].style,\n\t\t\tdivHeaderTable = divHeaderInner.children('table'),\n\t\t\tdivBodyEl      = settings.nScrollBody,\n\t\t\tdivBody        = $(divBodyEl),\n\t\t\tdivBodyStyle   = divBodyEl.style,\n\t\t\tdivFooter      = $(settings.nScrollFoot),\n\t\t\tdivFooterInner = divFooter.children('div'),\n\t\t\tdivFooterTable = divFooterInner.children('table'),\n\t\t\theader         = $(settings.nTHead),\n\t\t\ttable          = $(settings.nTable),\n\t\t\ttableEl        = table[0],\n\t\t\ttableStyle     = tableEl.style,\n\t\t\tfooter         = settings.nTFoot ? $(settings.nTFoot) : null,\n\t\t\tbrowser        = settings.oBrowser,\n\t\t\tie67           = browser.bScrollOversize,\n\t\t\tdtHeaderCells  = _pluck( settings.aoColumns, 'nTh' ),\n\t\t\theaderTrgEls, footerTrgEls,\n\t\t\theaderSrcEls, footerSrcEls,\n\t\t\theaderCopy, footerCopy,\n\t\t\theaderWidths=[], footerWidths=[],\n\t\t\theaderContent=[], footerContent=[],\n\t\t\tidx, correction, sanityWidth,\n\t\t\tzeroOut = function(nSizer) {\n\t\t\t\tvar style = nSizer.style;\n\t\t\t\tstyle.paddingTop = \"0\";\n\t\t\t\tstyle.paddingBottom = \"0\";\n\t\t\t\tstyle.borderTopWidth = \"0\";\n\t\t\t\tstyle.borderBottomWidth = \"0\";\n\t\t\t\tstyle.height = 0;\n\t\t\t};\n\t\n\t\t// If the scrollbar visibility has changed from the last draw, we need to\n\t\t// adjust the column sizes as the table width will have changed to account\n\t\t// for the scrollbar\n\t\tvar scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;\n\t\t\n\t\tif ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {\n\t\t\tsettings.scrollBarVis = scrollBarVis;\n\t\t\t_fnAdjustColumnSizing( settings );\n\t\t\treturn; // adjust column sizing will call this function again\n\t\t}\n\t\telse {\n\t\t\tsettings.scrollBarVis = scrollBarVis;\n\t\t}\n\t\n\t\t/*\n\t\t * 1. Re-create the table inside the scrolling div\n\t\t */\n\t\n\t\t// Remove the old minimised thead and tfoot elements in the inner table\n\t\ttable.children('thead, tfoot').remove();\n\t\n\t\tif ( footer ) {\n\t\t\tfooterCopy = footer.clone().prependTo( table );\n\t\t\tfooterTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized\n\t\t\tfooterSrcEls = footerCopy.find('tr');\n\t\t\tfooterCopy.find('[id]').removeAttr('id');\n\t\t}\n\t\n\t\t// Clone the current header and footer elements and then place it into the inner table\n\t\theaderCopy = header.clone().prependTo( table );\n\t\theaderTrgEls = header.find('tr'); // original header is in its own table\n\t\theaderSrcEls = headerCopy.find('tr');\n\t\theaderCopy.find('th, td').removeAttr('tabindex');\n\t\theaderCopy.find('[id]').removeAttr('id');\n\t\n\t\n\t\t/*\n\t\t * 2. Take live measurements from the DOM - do not alter the DOM itself!\n\t\t */\n\t\n\t\t// Remove old sizing and apply the calculated column widths\n\t\t// Get the unique column headers in the newly created (cloned) header. We want to apply the\n\t\t// calculated sizes to this header\n\t\tif ( ! scrollX )\n\t\t{\n\t\t\tdivBodyStyle.width = '100%';\n\t\t\tdivHeader[0].style.width = '100%';\n\t\t}\n\t\n\t\t$.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {\n\t\t\tidx = _fnVisibleToColumnIndex( settings, i );\n\t\t\tel.style.width = settings.aoColumns[idx].sWidth;\n\t\t} );\n\t\n\t\tif ( footer ) {\n\t\t\t_fnApplyToChildren( function(n) {\n\t\t\t\tn.style.width = \"\";\n\t\t\t}, footerSrcEls );\n\t\t}\n\t\n\t\t// Size the table as a whole\n\t\tsanityWidth = table.outerWidth();\n\t\tif ( scrollX === \"\" ) {\n\t\t\t// No x scrolling\n\t\t\ttableStyle.width = \"100%\";\n\t\n\t\t\t// IE7 will make the width of the table when 100% include the scrollbar\n\t\t\t// - which is shouldn't. When there is a scrollbar we need to take this\n\t\t\t// into account.\n\t\t\tif ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||\n\t\t\t\tdivBody.css('overflow-y') == \"scroll\")\n\t\t\t) {\n\t\t\t\ttableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);\n\t\t\t}\n\t\n\t\t\t// Recalculate the sanity width\n\t\t\tsanityWidth = table.outerWidth();\n\t\t}\n\t\telse if ( scrollXInner !== \"\" ) {\n\t\t\t// legacy x scroll inner has been given - use it\n\t\t\ttableStyle.width = _fnStringToCss(scrollXInner);\n\t\n\t\t\t// Recalculate the sanity width\n\t\t\tsanityWidth = table.outerWidth();\n\t\t}\n\t\n\t\t// Hidden header should have zero height, so remove padding and borders. Then\n\t\t// set the width based on the real headers\n\t\n\t\t// Apply all styles in one pass\n\t\t_fnApplyToChildren( zeroOut, headerSrcEls );\n\t\n\t\t// Read all widths in next pass\n\t\t_fnApplyToChildren( function(nSizer) {\n\t\t\tvar style = window.getComputedStyle ?\n\t\t\t\twindow.getComputedStyle(nSizer).width :\n\t\t\t\t_fnStringToCss( $(nSizer).width() );\n\t\n\t\t\theaderContent.push( nSizer.innerHTML );\n\t\t\theaderWidths.push( style );\n\t\t}, headerSrcEls );\n\t\n\t\t// Apply all widths in final pass\n\t\t_fnApplyToChildren( function(nToSize, i) {\n\t\t\tnToSize.style.width = headerWidths[i];\n\t\t}, headerTrgEls );\n\t\n\t\t$(headerSrcEls).css('height', 0);\n\t\n\t\t/* Same again with the footer if we have one */\n\t\tif ( footer )\n\t\t{\n\t\t\t_fnApplyToChildren( zeroOut, footerSrcEls );\n\t\n\t\t\t_fnApplyToChildren( function(nSizer) {\n\t\t\t\tfooterContent.push( nSizer.innerHTML );\n\t\t\t\tfooterWidths.push( _fnStringToCss( $(nSizer).css('width') ) );\n\t\t\t}, footerSrcEls );\n\t\n\t\t\t_fnApplyToChildren( function(nToSize, i) {\n\t\t\t\tnToSize.style.width = footerWidths[i];\n\t\t\t}, footerTrgEls );\n\t\n\t\t\t$(footerSrcEls).height(0);\n\t\t}\n\t\n\t\n\t\t/*\n\t\t * 3. Apply the measurements\n\t\t */\n\t\n\t\t// \"Hide\" the header and footer that we used for the sizing. We need to keep\n\t\t// the content of the cell so that the width applied to the header and body\n\t\t// both match, but we want to hide it completely. We want to also fix their\n\t\t// width to what they currently are\n\t\t_fnApplyToChildren( function(nSizer, i) {\n\t\t\tnSizer.innerHTML = '<div class=\"dataTables_sizing\">'+headerContent[i]+'</div>';\n\t\t\tnSizer.childNodes[0].style.height = \"0\";\n\t\t\tnSizer.childNodes[0].style.overflow = \"hidden\";\n\t\t\tnSizer.style.width = headerWidths[i];\n\t\t}, headerSrcEls );\n\t\n\t\tif ( footer )\n\t\t{\n\t\t\t_fnApplyToChildren( function(nSizer, i) {\n\t\t\t\tnSizer.innerHTML = '<div class=\"dataTables_sizing\">'+footerContent[i]+'</div>';\n\t\t\t\tnSizer.childNodes[0].style.height = \"0\";\n\t\t\t\tnSizer.childNodes[0].style.overflow = \"hidden\";\n\t\t\t\tnSizer.style.width = footerWidths[i];\n\t\t\t}, footerSrcEls );\n\t\t}\n\t\n\t\t// Sanity check that the table is of a sensible width. If not then we are going to get\n\t\t// misalignment - try to prevent this by not allowing the table to shrink below its min width\n\t\tif ( Math.round(table.outerWidth()) < Math.round(sanityWidth) )\n\t\t{\n\t\t\t// The min width depends upon if we have a vertical scrollbar visible or not */\n\t\t\tcorrection = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||\n\t\t\t\tdivBody.css('overflow-y') == \"scroll\")) ?\n\t\t\t\t\tsanityWidth+barWidth :\n\t\t\t\t\tsanityWidth;\n\t\n\t\t\t// IE6/7 are a law unto themselves...\n\t\t\tif ( ie67 && (divBodyEl.scrollHeight >\n\t\t\t\tdivBodyEl.offsetHeight || divBody.css('overflow-y') == \"scroll\")\n\t\t\t) {\n\t\t\t\ttableStyle.width = _fnStringToCss( correction-barWidth );\n\t\t\t}\n\t\n\t\t\t// And give the user a warning that we've stopped the table getting too small\n\t\t\tif ( scrollX === \"\" || scrollXInner !== \"\" ) {\n\t\t\t\t_fnLog( settings, 1, 'Possible column misalignment', 6 );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcorrection = '100%';\n\t\t}\n\t\n\t\t// Apply to the container elements\n\t\tdivBodyStyle.width = _fnStringToCss( correction );\n\t\tdivHeaderStyle.width = _fnStringToCss( correction );\n\t\n\t\tif ( footer ) {\n\t\t\tsettings.nScrollFoot.style.width = _fnStringToCss( correction );\n\t\t}\n\t\n\t\n\t\t/*\n\t\t * 4. Clean up\n\t\t */\n\t\tif ( ! scrollY ) {\n\t\t\t/* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting\n\t\t\t * the scrollbar height from the visible display, rather than adding it on. We need to\n\t\t\t * set the height in order to sort this. Don't want to do it in any other browsers.\n\t\t\t */\n\t\t\tif ( ie67 ) {\n\t\t\t\tdivBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );\n\t\t\t}\n\t\t}\n\t\n\t\t/* Finally set the width's of the header and footer tables */\n\t\tvar iOuterWidth = table.outerWidth();\n\t\tdivHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );\n\t\tdivHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );\n\t\n\t\t// Figure out if there are scrollbar present - if so then we need a the header and footer to\n\t\t// provide a bit more space to allow \"overflow\" scrolling (i.e. past the scrollbar)\n\t\tvar bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == \"scroll\";\n\t\tvar padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );\n\t\tdivHeaderInnerStyle[ padding ] = bScrolling ? barWidth+\"px\" : \"0px\";\n\t\n\t\tif ( footer ) {\n\t\t\tdivFooterTable[0].style.width = _fnStringToCss( iOuterWidth );\n\t\t\tdivFooterInner[0].style.width = _fnStringToCss( iOuterWidth );\n\t\t\tdivFooterInner[0].style[padding] = bScrolling ? barWidth+\"px\" : \"0px\";\n\t\t}\n\t\n\t\t// Correct DOM ordering for colgroup - comes before the thead\n\t\ttable.children('colgroup').insertBefore( table.children('thead') );\n\t\n\t\t/* Adjust the position of the header in case we loose the y-scrollbar */\n\t\tdivBody.trigger('scroll');\n\t\n\t\t// If sorting or filtering has occurred, jump the scrolling back to the top\n\t\t// only if we aren't holding the position\n\t\tif ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {\n\t\t\tdivBodyEl.scrollTop = 0;\n\t\t}\n\t}\n\t\n\t\n\t\n\t/**\n\t * Apply a given function to the display child nodes of an element array (typically\n\t * TD children of TR rows\n\t *  @param {function} fn Method to apply to the objects\n\t *  @param array {nodes} an1 List of elements to look through for display children\n\t *  @param array {nodes} an2 Another list (identical structure to the first) - optional\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnApplyToChildren( fn, an1, an2 )\n\t{\n\t\tvar index=0, i=0, iLen=an1.length;\n\t\tvar nNode1, nNode2;\n\t\n\t\twhile ( i < iLen ) {\n\t\t\tnNode1 = an1[i].firstChild;\n\t\t\tnNode2 = an2 ? an2[i].firstChild : null;\n\t\n\t\t\twhile ( nNode1 ) {\n\t\t\t\tif ( nNode1.nodeType === 1 ) {\n\t\t\t\t\tif ( an2 ) {\n\t\t\t\t\t\tfn( nNode1, nNode2, index );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tfn( nNode1, index );\n\t\t\t\t\t}\n\t\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\n\t\t\t\tnNode1 = nNode1.nextSibling;\n\t\t\t\tnNode2 = an2 ? nNode2.nextSibling : null;\n\t\t\t}\n\t\n\t\t\ti++;\n\t\t}\n\t}\n\t\n\t\n\t\n\tvar __re_html_remove = /<.*?>/g;\n\t\n\t\n\t/**\n\t * Calculate the width of columns for the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCalculateColumnWidths ( oSettings )\n\t{\n\t\tvar\n\t\t\ttable = oSettings.nTable,\n\t\t\tcolumns = oSettings.aoColumns,\n\t\t\tscroll = oSettings.oScroll,\n\t\t\tscrollY = scroll.sY,\n\t\t\tscrollX = scroll.sX,\n\t\t\tscrollXInner = scroll.sXInner,\n\t\t\tcolumnCount = columns.length,\n\t\t\tvisibleColumns = _fnGetColumns( oSettings, 'bVisible' ),\n\t\t\theaderCells = $('th', oSettings.nTHead),\n\t\t\ttableWidthAttr = table.getAttribute('width'), // from DOM element\n\t\t\ttableContainer = table.parentNode,\n\t\t\tuserInputs = false,\n\t\t\ti, column, columnIdx, width, outerWidth,\n\t\t\tbrowser = oSettings.oBrowser,\n\t\t\tie67 = browser.bScrollOversize;\n\t\n\t\tvar styleWidth = table.style.width;\n\t\tif ( styleWidth && styleWidth.indexOf('%') !== -1 ) {\n\t\t\ttableWidthAttr = styleWidth;\n\t\t}\n\t\n\t\t/* Convert any user input sizes into pixel sizes */\n\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\tcolumn = columns[ visibleColumns[i] ];\n\t\n\t\t\tif ( column.sWidth !== null ) {\n\t\t\t\tcolumn.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );\n\t\n\t\t\t\tuserInputs = true;\n\t\t\t}\n\t\t}\n\t\n\t\t/* If the number of columns in the DOM equals the number that we have to\n\t\t * process in DataTables, then we can use the offsets that are created by\n\t\t * the web- browser. No custom sizes can be set in order for this to happen,\n\t\t * nor scrolling used\n\t\t */\n\t\tif ( ie67 || ! userInputs && ! scrollX && ! scrollY &&\n\t\t     columnCount == _fnVisbleColumns( oSettings ) &&\n\t\t     columnCount == headerCells.length\n\t\t) {\n\t\t\tfor ( i=0 ; i<columnCount ; i++ ) {\n\t\t\t\tvar colIdx = _fnVisibleToColumnIndex( oSettings, i );\n\t\n\t\t\t\tif ( colIdx !== null ) {\n\t\t\t\t\tcolumns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Otherwise construct a single row, worst case, table with the widest\n\t\t\t// node in the data, assign any user defined widths, then insert it into\n\t\t\t// the DOM and allow the browser to do all the hard work of calculating\n\t\t\t// table widths\n\t\t\tvar tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table\n\t\t\t\t.css( 'visibility', 'hidden' )\n\t\t\t\t.removeAttr( 'id' );\n\t\n\t\t\t// Clean up the table body\n\t\t\ttmpTable.find('tbody tr').remove();\n\t\t\tvar tr = $('<tr/>').appendTo( tmpTable.find('tbody') );\n\t\n\t\t\t// Clone the table header and footer - we can't use the header / footer\n\t\t\t// from the cloned table, since if scrolling is active, the table's\n\t\t\t// real header and footer are contained in different table tags\n\t\t\ttmpTable.find('thead, tfoot').remove();\n\t\t\ttmpTable\n\t\t\t\t.append( $(oSettings.nTHead).clone() )\n\t\t\t\t.append( $(oSettings.nTFoot).clone() );\n\t\n\t\t\t// Remove any assigned widths from the footer (from scrolling)\n\t\t\ttmpTable.find('tfoot th, tfoot td').css('width', '');\n\t\n\t\t\t// Apply custom sizing to the cloned header\n\t\t\theaderCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );\n\t\n\t\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\t\tcolumn = columns[ visibleColumns[i] ];\n\t\n\t\t\t\theaderCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?\n\t\t\t\t\t_fnStringToCss( column.sWidthOrig ) :\n\t\t\t\t\t'';\n\t\n\t\t\t\t// For scrollX we need to force the column width otherwise the\n\t\t\t\t// browser will collapse it. If this width is smaller than the\n\t\t\t\t// width the column requires, then it will have no effect\n\t\t\t\tif ( column.sWidthOrig && scrollX ) {\n\t\t\t\t\t$( headerCells[i] ).append( $('<div/>').css( {\n\t\t\t\t\t\twidth: column.sWidthOrig,\n\t\t\t\t\t\tmargin: 0,\n\t\t\t\t\t\tpadding: 0,\n\t\t\t\t\t\tborder: 0,\n\t\t\t\t\t\theight: 1\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Find the widest cell for each column and put it into the table\n\t\t\tif ( oSettings.aoData.length ) {\n\t\t\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\t\t\tcolumnIdx = visibleColumns[i];\n\t\t\t\t\tcolumn = columns[ columnIdx ];\n\t\n\t\t\t\t\t$( _fnGetWidestNode( oSettings, columnIdx ) )\n\t\t\t\t\t\t.clone( false )\n\t\t\t\t\t\t.append( column.sContentPadding )\n\t\t\t\t\t\t.appendTo( tr );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Tidy the temporary table - remove name attributes so there aren't\n\t\t\t// duplicated in the dom (radio elements for example)\n\t\t\t$('[name]', tmpTable).removeAttr('name');\n\t\n\t\t\t// Table has been built, attach to the document so we can work with it.\n\t\t\t// A holding element is used, positioned at the top of the container\n\t\t\t// with minimal height, so it has no effect on if the container scrolls\n\t\t\t// or not. Otherwise it might trigger scrolling when it actually isn't\n\t\t\t// needed\n\t\t\tvar holder = $('<div/>').css( scrollX || scrollY ?\n\t\t\t\t\t{\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\theight: 1,\n\t\t\t\t\t\tright: 0,\n\t\t\t\t\t\toverflow: 'hidden'\n\t\t\t\t\t} :\n\t\t\t\t\t{}\n\t\t\t\t)\n\t\t\t\t.append( tmpTable )\n\t\t\t\t.appendTo( tableContainer );\n\t\n\t\t\t// When scrolling (X or Y) we want to set the width of the table as \n\t\t\t// appropriate. However, when not scrolling leave the table width as it\n\t\t\t// is. This results in slightly different, but I think correct behaviour\n\t\t\tif ( scrollX && scrollXInner ) {\n\t\t\t\ttmpTable.width( scrollXInner );\n\t\t\t}\n\t\t\telse if ( scrollX ) {\n\t\t\t\ttmpTable.css( 'width', 'auto' );\n\t\t\t\ttmpTable.removeAttr('width');\n\t\n\t\t\t\t// If there is no width attribute or style, then allow the table to\n\t\t\t\t// collapse\n\t\t\t\tif ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {\n\t\t\t\t\ttmpTable.width( tableContainer.clientWidth );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( scrollY ) {\n\t\t\t\ttmpTable.width( tableContainer.clientWidth );\n\t\t\t}\n\t\t\telse if ( tableWidthAttr ) {\n\t\t\t\ttmpTable.width( tableWidthAttr );\n\t\t\t}\n\t\n\t\t\t// Get the width of each column in the constructed table - we need to\n\t\t\t// know the inner width (so it can be assigned to the other table's\n\t\t\t// cells) and the outer width so we can calculate the full width of the\n\t\t\t// table. This is safe since DataTables requires a unique cell for each\n\t\t\t// column, but if ever a header can span multiple columns, this will\n\t\t\t// need to be modified.\n\t\t\tvar total = 0;\n\t\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\t\tvar cell = $(headerCells[i]);\n\t\t\t\tvar border = cell.outerWidth() - cell.width();\n\t\n\t\t\t\t// Use getBounding... where possible (not IE8-) because it can give\n\t\t\t\t// sub-pixel accuracy, which we then want to round up!\n\t\t\t\tvar bounding = browser.bBounding ?\n\t\t\t\t\tMath.ceil( headerCells[i].getBoundingClientRect().width ) :\n\t\t\t\t\tcell.outerWidth();\n\t\n\t\t\t\t// Total is tracked to remove any sub-pixel errors as the outerWidth\n\t\t\t\t// of the table might not equal the total given here (IE!).\n\t\t\t\ttotal += bounding;\n\t\n\t\t\t\t// Width for each column to use\n\t\t\t\tcolumns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );\n\t\t\t}\n\t\n\t\t\ttable.style.width = _fnStringToCss( total );\n\t\n\t\t\t// Finished with the table - ditch it\n\t\t\tholder.remove();\n\t\t}\n\t\n\t\t// If there is a width attr, we want to attach an event listener which\n\t\t// allows the table sizing to automatically adjust when the window is\n\t\t// resized. Use the width attr rather than CSS, since we can't know if the\n\t\t// CSS is a relative value or absolute - DOM read is always px.\n\t\tif ( tableWidthAttr ) {\n\t\t\ttable.style.width = _fnStringToCss( tableWidthAttr );\n\t\t}\n\t\n\t\tif ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {\n\t\t\tvar bindResize = function () {\n\t\t\t\t$(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {\n\t\t\t\t\t_fnAdjustColumnSizing( oSettings );\n\t\t\t\t} ) );\n\t\t\t};\n\t\n\t\t\t// IE6/7 will crash if we bind a resize event handler on page load.\n\t\t\t// To be removed in 1.11 which drops IE6/7 support\n\t\t\tif ( ie67 ) {\n\t\t\t\tsetTimeout( bindResize, 1000 );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindResize();\n\t\t\t}\n\t\n\t\t\toSettings._reszEvt = true;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Throttle the calls to a function. Arguments and context are maintained for\n\t * the throttled function\n\t *  @param {function} fn Function to be called\n\t *  @param {int} [freq=200] call frequency in mS\n\t *  @returns {function} wrapped function\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnThrottle = DataTable.util.throttle;\n\t\n\t\n\t/**\n\t * Convert a CSS unit width to pixels (e.g. 2em)\n\t *  @param {string} width width to be converted\n\t *  @param {node} parent parent to get the with for (required for relative widths) - optional\n\t *  @returns {int} width in pixels\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnConvertToWidth ( width, parent )\n\t{\n\t\tif ( ! width ) {\n\t\t\treturn 0;\n\t\t}\n\t\n\t\tvar n = $('<div/>')\n\t\t\t.css( 'width', _fnStringToCss( width ) )\n\t\t\t.appendTo( parent || document.body );\n\t\n\t\tvar val = n[0].offsetWidth;\n\t\tn.remove();\n\t\n\t\treturn val;\n\t}\n\t\n\t\n\t/**\n\t * Get the widest node\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} colIdx column of interest\n\t *  @returns {node} widest table node\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetWidestNode( settings, colIdx )\n\t{\n\t\tvar idx = _fnGetMaxLenString( settings, colIdx );\n\t\tif ( idx < 0 ) {\n\t\t\treturn null;\n\t\t}\n\t\n\t\tvar data = settings.aoData[ idx ];\n\t\treturn ! data.nTr ? // Might not have been created when deferred rendering\n\t\t\t$('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :\n\t\t\tdata.anCells[ colIdx ];\n\t}\n\t\n\t\n\t/**\n\t * Get the maximum strlen for each data column\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} colIdx column of interest\n\t *  @returns {string} max string length for each column\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetMaxLenString( settings, colIdx )\n\t{\n\t\tvar s, max=-1, maxIdx = -1;\n\t\n\t\tfor ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\ts = _fnGetCellData( settings, i, colIdx, 'display' )+'';\n\t\t\ts = s.replace( __re_html_remove, '' );\n\t\t\ts = s.replace( /&nbsp;/g, ' ' );\n\t\n\t\t\tif ( s.length > max ) {\n\t\t\t\tmax = s.length;\n\t\t\t\tmaxIdx = i;\n\t\t\t}\n\t\t}\n\t\n\t\treturn maxIdx;\n\t}\n\t\n\t\n\t/**\n\t * Append a CSS unit (only if required) to a string\n\t *  @param {string} value to css-ify\n\t *  @returns {string} value with css unit\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnStringToCss( s )\n\t{\n\t\tif ( s === null ) {\n\t\t\treturn '0px';\n\t\t}\n\t\n\t\tif ( typeof s == 'number' ) {\n\t\t\treturn s < 0 ?\n\t\t\t\t'0px' :\n\t\t\t\ts+'px';\n\t\t}\n\t\n\t\t// Check it has a unit character already\n\t\treturn s.match(/\\d$/) ?\n\t\t\ts+'px' :\n\t\t\ts;\n\t}\n\t\n\t\n\t\n\tfunction _fnSortFlatten ( settings )\n\t{\n\t\tvar\n\t\t\ti, iLen, k, kLen,\n\t\t\taSort = [],\n\t\t\taiOrig = [],\n\t\t\taoColumns = settings.aoColumns,\n\t\t\taDataSort, iCol, sType, srcCol,\n\t\t\tfixed = settings.aaSortingFixed,\n\t\t\tfixedObj = $.isPlainObject( fixed ),\n\t\t\tnestedSort = [],\n\t\t\tadd = function ( a ) {\n\t\t\t\tif ( a.length && ! Array.isArray( a[0] ) ) {\n\t\t\t\t\t// 1D array\n\t\t\t\t\tnestedSort.push( a );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// 2D array\n\t\t\t\t\t$.merge( nestedSort, a );\n\t\t\t\t}\n\t\t\t};\n\t\n\t\t// Build the sort array, with pre-fix and post-fix options if they have been\n\t\t// specified\n\t\tif ( Array.isArray( fixed ) ) {\n\t\t\tadd( fixed );\n\t\t}\n\t\n\t\tif ( fixedObj && fixed.pre ) {\n\t\t\tadd( fixed.pre );\n\t\t}\n\t\n\t\tadd( settings.aaSorting );\n\t\n\t\tif (fixedObj && fixed.post ) {\n\t\t\tadd( fixed.post );\n\t\t}\n\t\n\t\tfor ( i=0 ; i<nestedSort.length ; i++ )\n\t\t{\n\t\t\tsrcCol = nestedSort[i][0];\n\t\t\taDataSort = aoColumns[ srcCol ].aDataSort;\n\t\n\t\t\tfor ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )\n\t\t\t{\n\t\t\t\tiCol = aDataSort[k];\n\t\t\t\tsType = aoColumns[ iCol ].sType || 'string';\n\t\n\t\t\t\tif ( nestedSort[i]._idx === undefined ) {\n\t\t\t\t\tnestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );\n\t\t\t\t}\n\t\n\t\t\t\taSort.push( {\n\t\t\t\t\tsrc:       srcCol,\n\t\t\t\t\tcol:       iCol,\n\t\t\t\t\tdir:       nestedSort[i][1],\n\t\t\t\t\tindex:     nestedSort[i]._idx,\n\t\t\t\t\ttype:      sType,\n\t\t\t\t\tformatter: DataTable.ext.type.order[ sType+\"-pre\" ]\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t\n\t\treturn aSort;\n\t}\n\t\n\t/**\n\t * Change the order of the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t *  @todo This really needs split up!\n\t */\n\tfunction _fnSort ( oSettings )\n\t{\n\t\tvar\n\t\t\ti, ien, iLen, j, jLen, k, kLen,\n\t\t\tsDataType, nTh,\n\t\t\taiOrig = [],\n\t\t\toExtSort = DataTable.ext.type.order,\n\t\t\taoData = oSettings.aoData,\n\t\t\taoColumns = oSettings.aoColumns,\n\t\t\taDataSort, data, iCol, sType, oSort,\n\t\t\tformatters = 0,\n\t\t\tsortCol,\n\t\t\tdisplayMaster = oSettings.aiDisplayMaster,\n\t\t\taSort;\n\t\n\t\t// Resolve any column types that are unknown due to addition or invalidation\n\t\t// @todo Can this be moved into a 'data-ready' handler which is called when\n\t\t//   data is going to be used in the table?\n\t\t_fnColumnTypes( oSettings );\n\t\n\t\taSort = _fnSortFlatten( oSettings );\n\t\n\t\tfor ( i=0, ien=aSort.length ; i<ien ; i++ ) {\n\t\t\tsortCol = aSort[i];\n\t\n\t\t\t// Track if we can use the fast sort algorithm\n\t\t\tif ( sortCol.formatter ) {\n\t\t\t\tformatters++;\n\t\t\t}\n\t\n\t\t\t// Load the data needed for the sort, for each cell\n\t\t\t_fnSortData( oSettings, sortCol.col );\n\t\t}\n\t\n\t\t/* No sorting required if server-side or no sorting array */\n\t\tif ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )\n\t\t{\n\t\t\t// Create a value - key array of the current row positions such that we can use their\n\t\t\t// current position during the sort, if values match, in order to perform stable sorting\n\t\t\tfor ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {\n\t\t\t\taiOrig[ displayMaster[i] ] = i;\n\t\t\t}\n\t\n\t\t\t/* Do the sort - here we want multi-column sorting based on a given data source (column)\n\t\t\t * and sorting function (from oSort) in a certain direction. It's reasonably complex to\n\t\t\t * follow on it's own, but this is what we want (example two column sorting):\n\t\t\t *  fnLocalSorting = function(a,b){\n\t\t\t *    var iTest;\n\t\t\t *    iTest = oSort['string-asc']('data11', 'data12');\n\t\t\t *      if (iTest !== 0)\n\t\t\t *        return iTest;\n\t\t\t *    iTest = oSort['numeric-desc']('data21', 'data22');\n\t\t\t *    if (iTest !== 0)\n\t\t\t *      return iTest;\n\t\t\t *    return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );\n\t\t\t *  }\n\t\t\t * Basically we have a test for each sorting column, if the data in that column is equal,\n\t\t\t * test the next column. If all columns match, then we use a numeric sort on the row\n\t\t\t * positions in the original data array to provide a stable sort.\n\t\t\t *\n\t\t\t * Note - I know it seems excessive to have two sorting methods, but the first is around\n\t\t\t * 15% faster, so the second is only maintained for backwards compatibility with sorting\n\t\t\t * methods which do not have a pre-sort formatting function.\n\t\t\t */\n\t\t\tif ( formatters === aSort.length ) {\n\t\t\t\t// All sort types have formatting functions\n\t\t\t\tdisplayMaster.sort( function ( a, b ) {\n\t\t\t\t\tvar\n\t\t\t\t\t\tx, y, k, test, sort,\n\t\t\t\t\t\tlen=aSort.length,\n\t\t\t\t\t\tdataA = aoData[a]._aSortData,\n\t\t\t\t\t\tdataB = aoData[b]._aSortData;\n\t\n\t\t\t\t\tfor ( k=0 ; k<len ; k++ ) {\n\t\t\t\t\t\tsort = aSort[k];\n\t\n\t\t\t\t\t\tx = dataA[ sort.col ];\n\t\t\t\t\t\ty = dataB[ sort.col ];\n\t\n\t\t\t\t\t\ttest = x<y ? -1 : x>y ? 1 : 0;\n\t\t\t\t\t\tif ( test !== 0 ) {\n\t\t\t\t\t\t\treturn sort.dir === 'asc' ? test : -test;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\tx = aiOrig[a];\n\t\t\t\t\ty = aiOrig[b];\n\t\t\t\t\treturn x<y ? -1 : x>y ? 1 : 0;\n\t\t\t\t} );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Depreciated - remove in 1.11 (providing a plug-in option)\n\t\t\t\t// Not all sort types have formatting methods, so we have to call their sorting\n\t\t\t\t// methods.\n\t\t\t\tdisplayMaster.sort( function ( a, b ) {\n\t\t\t\t\tvar\n\t\t\t\t\t\tx, y, k, l, test, sort, fn,\n\t\t\t\t\t\tlen=aSort.length,\n\t\t\t\t\t\tdataA = aoData[a]._aSortData,\n\t\t\t\t\t\tdataB = aoData[b]._aSortData;\n\t\n\t\t\t\t\tfor ( k=0 ; k<len ; k++ ) {\n\t\t\t\t\t\tsort = aSort[k];\n\t\n\t\t\t\t\t\tx = dataA[ sort.col ];\n\t\t\t\t\t\ty = dataB[ sort.col ];\n\t\n\t\t\t\t\t\tfn = oExtSort[ sort.type+\"-\"+sort.dir ] || oExtSort[ \"string-\"+sort.dir ];\n\t\t\t\t\t\ttest = fn( x, y );\n\t\t\t\t\t\tif ( test !== 0 ) {\n\t\t\t\t\t\t\treturn test;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\tx = aiOrig[a];\n\t\t\t\t\ty = aiOrig[b];\n\t\t\t\t\treturn x<y ? -1 : x>y ? 1 : 0;\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t\n\t\t/* Tell the draw function that we have sorted the data */\n\t\toSettings.bSorted = true;\n\t}\n\t\n\t\n\tfunction _fnSortAria ( settings )\n\t{\n\t\tvar label;\n\t\tvar nextSort;\n\t\tvar columns = settings.aoColumns;\n\t\tvar aSort = _fnSortFlatten( settings );\n\t\tvar oAria = settings.oLanguage.oAria;\n\t\n\t\t// ARIA attributes - need to loop all columns, to update all (removing old\n\t\t// attributes as needed)\n\t\tfor ( var i=0, iLen=columns.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tvar col = columns[i];\n\t\t\tvar asSorting = col.asSorting;\n\t\t\tvar sTitle = col.ariaTitle || col.sTitle.replace( /<.*?>/g, \"\" );\n\t\t\tvar th = col.nTh;\n\t\n\t\t\t// IE7 is throwing an error when setting these properties with jQuery's\n\t\t\t// attr() and removeAttr() methods...\n\t\t\tth.removeAttribute('aria-sort');\n\t\n\t\t\t/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */\n\t\t\tif ( col.bSortable ) {\n\t\t\t\tif ( aSort.length > 0 && aSort[0].col == i ) {\n\t\t\t\t\tth.setAttribute('aria-sort', aSort[0].dir==\"asc\" ? \"ascending\" : \"descending\" );\n\t\t\t\t\tnextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tnextSort = asSorting[0];\n\t\t\t\t}\n\t\n\t\t\t\tlabel = sTitle + ( nextSort === \"asc\" ?\n\t\t\t\t\toAria.sSortAscending :\n\t\t\t\t\toAria.sSortDescending\n\t\t\t\t);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlabel = sTitle;\n\t\t\t}\n\t\n\t\t\tth.setAttribute('aria-label', label);\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Function to run on user sort request\n\t *  @param {object} settings dataTables settings object\n\t *  @param {node} attachTo node to attach the handler to\n\t *  @param {int} colIdx column sorting index\n\t *  @param {boolean} [append=false] Append the requested sort to the existing\n\t *    sort if true (i.e. multi-column sort)\n\t *  @param {function} [callback] callback function\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSortListener ( settings, colIdx, append, callback )\n\t{\n\t\tvar col = settings.aoColumns[ colIdx ];\n\t\tvar sorting = settings.aaSorting;\n\t\tvar asSorting = col.asSorting;\n\t\tvar nextSortIdx;\n\t\tvar next = function ( a, overflow ) {\n\t\t\tvar idx = a._idx;\n\t\t\tif ( idx === undefined ) {\n\t\t\t\tidx = $.inArray( a[1], asSorting );\n\t\t\t}\n\t\n\t\t\treturn idx+1 < asSorting.length ?\n\t\t\t\tidx+1 :\n\t\t\t\toverflow ?\n\t\t\t\t\tnull :\n\t\t\t\t\t0;\n\t\t};\n\t\n\t\t// Convert to 2D array if needed\n\t\tif ( typeof sorting[0] === 'number' ) {\n\t\t\tsorting = settings.aaSorting = [ sorting ];\n\t\t}\n\t\n\t\t// If appending the sort then we are multi-column sorting\n\t\tif ( append && settings.oFeatures.bSortMulti ) {\n\t\t\t// Are we already doing some kind of sort on this column?\n\t\t\tvar sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );\n\t\n\t\t\tif ( sortIdx !== -1 ) {\n\t\t\t\t// Yes, modify the sort\n\t\t\t\tnextSortIdx = next( sorting[sortIdx], true );\n\t\n\t\t\t\tif ( nextSortIdx === null && sorting.length === 1 ) {\n\t\t\t\t\tnextSortIdx = 0; // can't remove sorting completely\n\t\t\t\t}\n\t\n\t\t\t\tif ( nextSortIdx === null ) {\n\t\t\t\t\tsorting.splice( sortIdx, 1 );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tsorting[sortIdx][1] = asSorting[ nextSortIdx ];\n\t\t\t\t\tsorting[sortIdx]._idx = nextSortIdx;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// No sort on this column yet\n\t\t\t\tsorting.push( [ colIdx, asSorting[0], 0 ] );\n\t\t\t\tsorting[sorting.length-1]._idx = 0;\n\t\t\t}\n\t\t}\n\t\telse if ( sorting.length && sorting[0][0] == colIdx ) {\n\t\t\t// Single column - already sorting on this column, modify the sort\n\t\t\tnextSortIdx = next( sorting[0] );\n\t\n\t\t\tsorting.length = 1;\n\t\t\tsorting[0][1] = asSorting[ nextSortIdx ];\n\t\t\tsorting[0]._idx = nextSortIdx;\n\t\t}\n\t\telse {\n\t\t\t// Single column - sort only on this column\n\t\t\tsorting.length = 0;\n\t\t\tsorting.push( [ colIdx, asSorting[0] ] );\n\t\t\tsorting[0]._idx = 0;\n\t\t}\n\t\n\t\t// Run the sort by calling a full redraw\n\t\t_fnReDraw( settings );\n\t\n\t\t// callback used for async user interaction\n\t\tif ( typeof callback == 'function' ) {\n\t\t\tcallback( settings );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Attach a sort handler (click) to a node\n\t *  @param {object} settings dataTables settings object\n\t *  @param {node} attachTo node to attach the handler to\n\t *  @param {int} colIdx column sorting index\n\t *  @param {function} [callback] callback function\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSortAttachListener ( settings, attachTo, colIdx, callback )\n\t{\n\t\tvar col = settings.aoColumns[ colIdx ];\n\t\n\t\t_fnBindAction( attachTo, {}, function (e) {\n\t\t\t/* If the column is not sortable - don't to anything */\n\t\t\tif ( col.bSortable === false ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// If processing is enabled use a timeout to allow the processing\n\t\t\t// display to be shown - otherwise to it synchronously\n\t\t\tif ( settings.oFeatures.bProcessing ) {\n\t\t\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t\t\tsetTimeout( function() {\n\t\t\t\t\t_fnSortListener( settings, colIdx, e.shiftKey, callback );\n\t\n\t\t\t\t\t// In server-side processing, the draw callback will remove the\n\t\t\t\t\t// processing display\n\t\t\t\t\tif ( _fnDataSource( settings ) !== 'ssp' ) {\n\t\t\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t\t\t}\n\t\t\t\t}, 0 );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnSortListener( settings, colIdx, e.shiftKey, callback );\n\t\t\t}\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Set the sorting classes on table's body, Note: it is safe to call this function\n\t * when bSort and bSortClasses are false\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSortingClasses( settings )\n\t{\n\t\tvar oldSort = settings.aLastSort;\n\t\tvar sortClass = settings.oClasses.sSortColumn;\n\t\tvar sort = _fnSortFlatten( settings );\n\t\tvar features = settings.oFeatures;\n\t\tvar i, ien, colIdx;\n\t\n\t\tif ( features.bSort && features.bSortClasses ) {\n\t\t\t// Remove old sorting classes\n\t\t\tfor ( i=0, ien=oldSort.length ; i<ien ; i++ ) {\n\t\t\t\tcolIdx = oldSort[i].src;\n\t\n\t\t\t\t// Remove column sorting\n\t\t\t\t$( _pluck( settings.aoData, 'anCells', colIdx ) )\n\t\t\t\t\t.removeClass( sortClass + (i<2 ? i+1 : 3) );\n\t\t\t}\n\t\n\t\t\t// Add new column sorting\n\t\t\tfor ( i=0, ien=sort.length ; i<ien ; i++ ) {\n\t\t\t\tcolIdx = sort[i].src;\n\t\n\t\t\t\t$( _pluck( settings.aoData, 'anCells', colIdx ) )\n\t\t\t\t\t.addClass( sortClass + (i<2 ? i+1 : 3) );\n\t\t\t}\n\t\t}\n\t\n\t\tsettings.aLastSort = sort;\n\t}\n\t\n\t\n\t// Get the data to sort a column, be it from cache, fresh (populating the\n\t// cache), or from a sort formatter\n\tfunction _fnSortData( settings, idx )\n\t{\n\t\t// Custom sorting function - provided by the sort data type\n\t\tvar column = settings.aoColumns[ idx ];\n\t\tvar customSort = DataTable.ext.order[ column.sSortDataType ];\n\t\tvar customData;\n\t\n\t\tif ( customSort ) {\n\t\t\tcustomData = customSort.call( settings.oInstance, settings, idx,\n\t\t\t\t_fnColumnIndexToVisible( settings, idx )\n\t\t\t);\n\t\t}\n\t\n\t\t// Use / populate cache\n\t\tvar row, cellData;\n\t\tvar formatter = DataTable.ext.type.order[ column.sType+\"-pre\" ];\n\t\n\t\tfor ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\trow = settings.aoData[i];\n\t\n\t\t\tif ( ! row._aSortData ) {\n\t\t\t\trow._aSortData = [];\n\t\t\t}\n\t\n\t\t\tif ( ! row._aSortData[idx] || customSort ) {\n\t\t\t\tcellData = customSort ?\n\t\t\t\t\tcustomData[i] : // If there was a custom sort function, use data from there\n\t\t\t\t\t_fnGetCellData( settings, i, idx, 'sort' );\n\t\n\t\t\t\trow._aSortData[ idx ] = formatter ?\n\t\t\t\t\tformatter( cellData ) :\n\t\t\t\t\tcellData;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t\n\t/**\n\t * Save the state of a table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSaveState ( settings )\n\t{\n\t\tif (settings._bLoadingState) {\n\t\t\treturn;\n\t\t}\n\t\n\t\t/* Store the interesting variables */\n\t\tvar state = {\n\t\t\ttime:    +new Date(),\n\t\t\tstart:   settings._iDisplayStart,\n\t\t\tlength:  settings._iDisplayLength,\n\t\t\torder:   $.extend( true, [], settings.aaSorting ),\n\t\t\tsearch:  _fnSearchToCamel( settings.oPreviousSearch ),\n\t\t\tcolumns: $.map( settings.aoColumns, function ( col, i ) {\n\t\t\t\treturn {\n\t\t\t\t\tvisible: col.bVisible,\n\t\t\t\t\tsearch: _fnSearchToCamel( settings.aoPreSearchCols[i] )\n\t\t\t\t};\n\t\t\t} )\n\t\t};\n\t\n\t\tsettings.oSavedState = state;\n\t\t_fnCallbackFire( settings, \"aoStateSaveParams\", 'stateSaveParams', [settings, state] );\n\t\t\n\t\tif ( settings.oFeatures.bStateSave && !settings.bDestroying )\n\t\t{\n\t\t\tsettings.fnStateSaveCallback.call( settings.oInstance, settings, state );\n\t\t}\t\n\t}\n\t\n\t\n\t/**\n\t * Attempt to load a saved table state\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} oInit DataTables init object so we can override settings\n\t *  @param {function} callback Callback to execute when the state has been loaded\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnLoadState ( settings, oInit, callback )\n\t{\n\t\tif ( ! settings.oFeatures.bStateSave ) {\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar loaded = function(state) {\n\t\t\t_fnImplementState(settings, state, callback);\n\t\t}\n\t\n\t\tvar state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );\n\t\n\t\tif ( state !== undefined ) {\n\t\t\t_fnImplementState( settings, state, callback );\n\t\t}\n\t\t// otherwise, wait for the loaded callback to be executed\n\t\n\t\treturn true;\n\t}\n\t\n\tfunction _fnImplementState ( settings, s, callback) {\n\t\tvar i, ien;\n\t\tvar columns = settings.aoColumns;\n\t\tsettings._bLoadingState = true;\n\t\n\t\t// When StateRestore was introduced the state could now be implemented at any time\n\t\t// Not just initialisation. To do this an api instance is required in some places\n\t\tvar api = settings._bInitComplete ? new DataTable.Api(settings) : null;\n\t\n\t\tif ( ! s || ! s.time ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Allow custom and plug-in manipulation functions to alter the saved data set and\n\t\t// cancelling of loading by returning false\n\t\tvar abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );\n\t\tif ( $.inArray( false, abStateLoad ) !== -1 ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Reject old data\n\t\tvar duration = settings.iStateDuration;\n\t\tif ( duration > 0 && s.time < +new Date() - (duration*1000) ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Number of columns have changed - all bets are off, no restore of settings\n\t\tif ( s.columns && columns.length !== s.columns.length ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Store the saved state so it might be accessed at any time\n\t\tsettings.oLoadedState = $.extend( true, {}, s );\n\t\n\t\t// Page Length\n\t\tif ( s.length !== undefined ) {\n\t\t\t// If already initialised just set the value directly so that the select element is also updated\n\t\t\tif (api) {\n\t\t\t\tapi.page.len(s.length)\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsettings._iDisplayLength   = s.length;\n\t\t\t}\n\t\t}\n\t\n\t\t// Restore key features - todo - for 1.11 this needs to be done by\n\t\t// subscribed events\n\t\tif ( s.start !== undefined ) {\n\t\t\tif(api === null) {\n\t\t\t\tsettings._iDisplayStart    = s.start;\n\t\t\t\tsettings.iInitDisplayStart = s.start;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnPageChange(settings, s.start/settings._iDisplayLength);\n\t\t\t}\n\t\t}\n\t\n\t\t// Order\n\t\tif ( s.order !== undefined ) {\n\t\t\tsettings.aaSorting = [];\n\t\t\t$.each( s.order, function ( i, col ) {\n\t\t\t\tsettings.aaSorting.push( col[0] >= columns.length ?\n\t\t\t\t\t[ 0, col[1] ] :\n\t\t\t\t\tcol\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\t\n\t\t// Search\n\t\tif ( s.search !== undefined ) {\n\t\t\t$.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );\n\t\t}\n\t\n\t\t// Columns\n\t\tif ( s.columns ) {\n\t\t\tfor ( i=0, ien=s.columns.length ; i<ien ; i++ ) {\n\t\t\t\tvar col = s.columns[i];\n\t\n\t\t\t\t// Visibility\n\t\t\t\tif ( col.visible !== undefined ) {\n\t\t\t\t\t// If the api is defined, the table has been initialised so we need to use it rather than internal settings\n\t\t\t\t\tif (api) {\n\t\t\t\t\t\t// Don't redraw the columns on every iteration of this loop, we will do this at the end instead\n\t\t\t\t\t\tapi.column(i).visible(col.visible, false);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tcolumns[i].bVisible = col.visible;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Search\n\t\t\t\tif ( col.search !== undefined ) {\n\t\t\t\t\t$.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// If the api is defined then we need to adjust the columns once the visibility has been changed\n\t\t\tif (api) {\n\t\t\t\tapi.columns.adjust();\n\t\t\t}\n\t\t}\n\t\n\t\tsettings._bLoadingState = false;\n\t\t_fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );\n\t\tcallback();\n\t};\n\t\n\t\n\t/**\n\t * Return the settings object for a particular table\n\t *  @param {node} table table we are using as a dataTable\n\t *  @returns {object} Settings object - or null if not found\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSettingsFromNode ( table )\n\t{\n\t\tvar settings = DataTable.settings;\n\t\tvar idx = $.inArray( table, _pluck( settings, 'nTable' ) );\n\t\n\t\treturn idx !== -1 ?\n\t\t\tsettings[ idx ] :\n\t\t\tnull;\n\t}\n\t\n\t\n\t/**\n\t * Log an error message\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} level log error messages, or display them to the user\n\t *  @param {string} msg error message\n\t *  @param {int} tn Technical note id to get more information about the error.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnLog( settings, level, msg, tn )\n\t{\n\t\tmsg = 'DataTables warning: '+\n\t\t\t(settings ? 'table id='+settings.sTableId+' - ' : '')+msg;\n\t\n\t\tif ( tn ) {\n\t\t\tmsg += '. For more information about this error, please see '+\n\t\t\t'http://datatables.net/tn/'+tn;\n\t\t}\n\t\n\t\tif ( ! level  ) {\n\t\t\t// Backwards compatibility pre 1.10\n\t\t\tvar ext = DataTable.ext;\n\t\t\tvar type = ext.sErrMode || ext.errMode;\n\t\n\t\t\tif ( settings ) {\n\t\t\t\t_fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );\n\t\t\t}\n\t\n\t\t\tif ( type == 'alert' ) {\n\t\t\t\talert( msg );\n\t\t\t}\n\t\t\telse if ( type == 'throw' ) {\n\t\t\t\tthrow new Error(msg);\n\t\t\t}\n\t\t\telse if ( typeof type == 'function' ) {\n\t\t\t\ttype( settings, tn, msg );\n\t\t\t}\n\t\t}\n\t\telse if ( window.console && console.log ) {\n\t\t\tconsole.log( msg );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * See if a property is defined on one object, if so assign it to the other object\n\t *  @param {object} ret target object\n\t *  @param {object} src source object\n\t *  @param {string} name property\n\t *  @param {string} [mappedName] name to map too - optional, name used if not given\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnMap( ret, src, name, mappedName )\n\t{\n\t\tif ( Array.isArray( name ) ) {\n\t\t\t$.each( name, function (i, val) {\n\t\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\t\t_fnMap( ret, src, val[0], val[1] );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_fnMap( ret, src, val );\n\t\t\t\t}\n\t\t\t} );\n\t\n\t\t\treturn;\n\t\t}\n\t\n\t\tif ( mappedName === undefined ) {\n\t\t\tmappedName = name;\n\t\t}\n\t\n\t\tif ( src[name] !== undefined ) {\n\t\t\tret[mappedName] = src[name];\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Extend objects - very similar to jQuery.extend, but deep copy objects, and\n\t * shallow copy arrays. The reason we need to do this, is that we don't want to\n\t * deep copy array init values (such as aaSorting) since the dev wouldn't be\n\t * able to override them, but we do want to deep copy arrays.\n\t *  @param {object} out Object to extend\n\t *  @param {object} extender Object from which the properties will be applied to\n\t *      out\n\t *  @param {boolean} breakRefs If true, then arrays will be sliced to take an\n\t *      independent copy with the exception of the `data` or `aaData` parameters\n\t *      if they are present. This is so you can pass in a collection to\n\t *      DataTables and have that used as your data source without breaking the\n\t *      references\n\t *  @returns {object} out Reference, just for convenience - out === the return.\n\t *  @memberof DataTable#oApi\n\t *  @todo This doesn't take account of arrays inside the deep copied objects.\n\t */\n\tfunction _fnExtend( out, extender, breakRefs )\n\t{\n\t\tvar val;\n\t\n\t\tfor ( var prop in extender ) {\n\t\t\tif ( extender.hasOwnProperty(prop) ) {\n\t\t\t\tval = extender[prop];\n\t\n\t\t\t\tif ( $.isPlainObject( val ) ) {\n\t\t\t\t\tif ( ! $.isPlainObject( out[prop] ) ) {\n\t\t\t\t\t\tout[prop] = {};\n\t\t\t\t\t}\n\t\t\t\t\t$.extend( true, out[prop], val );\n\t\t\t\t}\n\t\t\t\telse if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) {\n\t\t\t\t\tout[prop] = val.slice();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tout[prop] = val;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t}\n\t\n\t\n\t/**\n\t * Bind an event handers to allow a click or return key to activate the callback.\n\t * This is good for accessibility since a return on the keyboard will have the\n\t * same effect as a click, if the element has focus.\n\t *  @param {element} n Element to bind the action to\n\t *  @param {object} oData Data object to pass to the triggered function\n\t *  @param {function} fn Callback function for when the event is triggered\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnBindAction( n, oData, fn )\n\t{\n\t\t$(n)\n\t\t\t.on( 'click.DT', oData, function (e) {\n\t\t\t\t\t$(n).trigger('blur'); // Remove focus outline for mouse users\n\t\t\t\t\tfn(e);\n\t\t\t\t} )\n\t\t\t.on( 'keypress.DT', oData, function (e){\n\t\t\t\t\tif ( e.which === 13 ) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tfn(e);\n\t\t\t\t\t}\n\t\t\t\t} )\n\t\t\t.on( 'selectstart.DT', function () {\n\t\t\t\t\t/* Take the brutal approach to cancelling text selection */\n\t\t\t\t\treturn false;\n\t\t\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Register a callback function. Easily allows a callback function to be added to\n\t * an array store of callback functions that can then all be called together.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {string} sStore Name of the array storage for the callbacks in oSettings\n\t *  @param {function} fn Function to be called back\n\t *  @param {string} sName Identifying name for the callback (i.e. a label)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCallbackReg( oSettings, sStore, fn, sName )\n\t{\n\t\tif ( fn )\n\t\t{\n\t\t\toSettings[sStore].push( {\n\t\t\t\t\"fn\": fn,\n\t\t\t\t\"sName\": sName\n\t\t\t} );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Fire callback functions and trigger events. Note that the loop over the\n\t * callback array store is done backwards! Further note that you do not want to\n\t * fire off triggers in time sensitive applications (for example cell creation)\n\t * as its slow.\n\t *  @param {object} settings dataTables settings object\n\t *  @param {string} callbackArr Name of the array storage for the callbacks in\n\t *      oSettings\n\t *  @param {string} eventName Name of the jQuery custom event to trigger. If\n\t *      null no trigger is fired\n\t *  @param {array} args Array of arguments to pass to the callback function /\n\t *      trigger\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCallbackFire( settings, callbackArr, eventName, args )\n\t{\n\t\tvar ret = [];\n\t\n\t\tif ( callbackArr ) {\n\t\t\tret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {\n\t\t\t\treturn val.fn.apply( settings.oInstance, args );\n\t\t\t} );\n\t\t}\n\t\n\t\tif ( eventName !== null ) {\n\t\t\tvar e = $.Event( eventName+'.dt' );\n\t\t\tvar table = $(settings.nTable);\n\t\n\t\t\ttable.trigger( e, args );\n\t\n\t\t\t// If not yet attached to the document, trigger the event\n\t\t\t// on the body directly to sort of simulate the bubble\n\t\t\tif (table.parents('body').length === 0) {\n\t\t\t\t$('body').trigger( e, args );\n\t\t\t}\n\t\n\t\t\tret.push( e.result );\n\t\t}\n\t\n\t\treturn ret;\n\t}\n\t\n\t\n\tfunction _fnLengthOverflow ( settings )\n\t{\n\t\tvar\n\t\t\tstart = settings._iDisplayStart,\n\t\t\tend = settings.fnDisplayEnd(),\n\t\t\tlen = settings._iDisplayLength;\n\t\n\t\t/* If we have space to show extra rows (backing up from the end point - then do so */\n\t\tif ( start >= end )\n\t\t{\n\t\t\tstart = end - len;\n\t\t}\n\t\n\t\t// Keep the start record on the current page\n\t\tstart -= (start % len);\n\t\n\t\tif ( len === -1 || start < 0 )\n\t\t{\n\t\t\tstart = 0;\n\t\t}\n\t\n\t\tsettings._iDisplayStart = start;\n\t}\n\t\n\t\n\tfunction _fnRenderer( settings, type )\n\t{\n\t\tvar renderer = settings.renderer;\n\t\tvar host = DataTable.ext.renderer[type];\n\t\n\t\tif ( $.isPlainObject( renderer ) && renderer[type] ) {\n\t\t\t// Specific renderer for this type. If available use it, otherwise use\n\t\t\t// the default.\n\t\t\treturn host[renderer[type]] || host._;\n\t\t}\n\t\telse if ( typeof renderer === 'string' ) {\n\t\t\t// Common renderer - if there is one available for this type use it,\n\t\t\t// otherwise use the default\n\t\t\treturn host[renderer] || host._;\n\t\t}\n\t\n\t\t// Use the default\n\t\treturn host._;\n\t}\n\t\n\t\n\t/**\n\t * Detect the data source being used for the table. Used to simplify the code\n\t * a little (ajax) and to make it compress a little smaller.\n\t *\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {string} Data source\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDataSource ( settings )\n\t{\n\t\tif ( settings.oFeatures.bServerSide ) {\n\t\t\treturn 'ssp';\n\t\t}\n\t\telse if ( settings.ajax || settings.sAjaxSource ) {\n\t\t\treturn 'ajax';\n\t\t}\n\t\treturn 'dom';\n\t}\n\t\n\t\n\t\n\t\n\t/**\n\t * Computed structure of the DataTables API, defined by the options passed to\n\t * `DataTable.Api.register()` when building the API.\n\t *\n\t * The structure is built in order to speed creation and extension of the Api\n\t * objects since the extensions are effectively pre-parsed.\n\t *\n\t * The array is an array of objects with the following structure, where this\n\t * base array represents the Api prototype base:\n\t *\n\t *     [\n\t *       {\n\t *         name:      'data'                -- string   - Property name\n\t *         val:       function () {},       -- function - Api method (or undefined if just an object\n\t *         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result\n\t *         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property\n\t *       },\n\t *       {\n\t *         name:     'row'\n\t *         val:       {},\n\t *         methodExt: [ ... ],\n\t *         propExt:   [\n\t *           {\n\t *             name:      'data'\n\t *             val:       function () {},\n\t *             methodExt: [ ... ],\n\t *             propExt:   [ ... ]\n\t *           },\n\t *           ...\n\t *         ]\n\t *       }\n\t *     ]\n\t *\n\t * @type {Array}\n\t * @ignore\n\t */\n\tvar __apiStruct = [];\n\t\n\t\n\t/**\n\t * `Array.prototype` reference.\n\t *\n\t * @type object\n\t * @ignore\n\t */\n\tvar __arrayProto = Array.prototype;\n\t\n\t\n\t/**\n\t * Abstraction for `context` parameter of the `Api` constructor to allow it to\n\t * take several different forms for ease of use.\n\t *\n\t * Each of the input parameter types will be converted to a DataTables settings\n\t * object where possible.\n\t *\n\t * @param  {string|node|jQuery|object} mixed DataTable identifier. Can be one\n\t *   of:\n\t *\n\t *   * `string` - jQuery selector. Any DataTables' matching the given selector\n\t *     with be found and used.\n\t *   * `node` - `TABLE` node which has already been formed into a DataTable.\n\t *   * `jQuery` - A jQuery object of `TABLE` nodes.\n\t *   * `object` - DataTables settings object\n\t *   * `DataTables.Api` - API instance\n\t * @return {array|null} Matching DataTables settings objects. `null` or\n\t *   `undefined` is returned if no matching DataTable is found.\n\t * @ignore\n\t */\n\tvar _toSettings = function ( mixed )\n\t{\n\t\tvar idx, jq;\n\t\tvar settings = DataTable.settings;\n\t\tvar tables = $.map( settings, function (el, i) {\n\t\t\treturn el.nTable;\n\t\t} );\n\t\n\t\tif ( ! mixed ) {\n\t\t\treturn [];\n\t\t}\n\t\telse if ( mixed.nTable && mixed.oApi ) {\n\t\t\t// DataTables settings object\n\t\t\treturn [ mixed ];\n\t\t}\n\t\telse if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {\n\t\t\t// Table node\n\t\t\tidx = $.inArray( mixed, tables );\n\t\t\treturn idx !== -1 ? [ settings[idx] ] : null;\n\t\t}\n\t\telse if ( mixed && typeof mixed.settings === 'function' ) {\n\t\t\treturn mixed.settings().toArray();\n\t\t}\n\t\telse if ( typeof mixed === 'string' ) {\n\t\t\t// jQuery selector\n\t\t\tjq = $(mixed);\n\t\t}\n\t\telse if ( mixed instanceof $ ) {\n\t\t\t// jQuery object (also DataTables instance)\n\t\t\tjq = mixed;\n\t\t}\n\t\n\t\tif ( jq ) {\n\t\t\treturn jq.map( function(i) {\n\t\t\t\tidx = $.inArray( this, tables );\n\t\t\t\treturn idx !== -1 ? settings[idx] : null;\n\t\t\t} ).toArray();\n\t\t}\n\t};\n\t\n\t\n\t/**\n\t * DataTables API class - used to control and interface with  one or more\n\t * DataTables enhanced tables.\n\t *\n\t * The API class is heavily based on jQuery, presenting a chainable interface\n\t * that you can use to interact with tables. Each instance of the API class has\n\t * a \"context\" - i.e. the tables that it will operate on. This could be a single\n\t * table, all tables on a page or a sub-set thereof.\n\t *\n\t * Additionally the API is designed to allow you to easily work with the data in\n\t * the tables, retrieving and manipulating it as required. This is done by\n\t * presenting the API class as an array like interface. The contents of the\n\t * array depend upon the actions requested by each method (for example\n\t * `rows().nodes()` will return an array of nodes, while `rows().data()` will\n\t * return an array of objects or arrays depending upon your table's\n\t * configuration). The API object has a number of array like methods (`push`,\n\t * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,\n\t * `unique` etc) to assist your working with the data held in a table.\n\t *\n\t * Most methods (those which return an Api instance) are chainable, which means\n\t * the return from a method call also has all of the methods available that the\n\t * top level object had. For example, these two calls are equivalent:\n\t *\n\t *     // Not chained\n\t *     api.row.add( {...} );\n\t *     api.draw();\n\t *\n\t *     // Chained\n\t *     api.row.add( {...} ).draw();\n\t *\n\t * @class DataTable.Api\n\t * @param {array|object|string|jQuery} context DataTable identifier. This is\n\t *   used to define which DataTables enhanced tables this API will operate on.\n\t *   Can be one of:\n\t *\n\t *   * `string` - jQuery selector. Any DataTables' matching the given selector\n\t *     with be found and used.\n\t *   * `node` - `TABLE` node which has already been formed into a DataTable.\n\t *   * `jQuery` - A jQuery object of `TABLE` nodes.\n\t *   * `object` - DataTables settings object\n\t * @param {array} [data] Data to initialise the Api instance with.\n\t *\n\t * @example\n\t *   // Direct initialisation during DataTables construction\n\t *   var api = $('#example').DataTable();\n\t *\n\t * @example\n\t *   // Initialisation using a DataTables jQuery object\n\t *   var api = $('#example').dataTable().api();\n\t *\n\t * @example\n\t *   // Initialisation as a constructor\n\t *   var api = new $.fn.DataTable.Api( 'table.dataTable' );\n\t */\n\t_Api = function ( context, data )\n\t{\n\t\tif ( ! (this instanceof _Api) ) {\n\t\t\treturn new _Api( context, data );\n\t\t}\n\t\n\t\tvar settings = [];\n\t\tvar ctxSettings = function ( o ) {\n\t\t\tvar a = _toSettings( o );\n\t\t\tif ( a ) {\n\t\t\t\tsettings.push.apply( settings, a );\n\t\t\t}\n\t\t};\n\t\n\t\tif ( Array.isArray( context ) ) {\n\t\t\tfor ( var i=0, ien=context.length ; i<ien ; i++ ) {\n\t\t\t\tctxSettings( context[i] );\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tctxSettings( context );\n\t\t}\n\t\n\t\t// Remove duplicates\n\t\tthis.context = _unique( settings );\n\t\n\t\t// Initial data\n\t\tif ( data ) {\n\t\t\t$.merge( this, data );\n\t\t}\n\t\n\t\t// selector\n\t\tthis.selector = {\n\t\t\trows: null,\n\t\t\tcols: null,\n\t\t\topts: null\n\t\t};\n\t\n\t\t_Api.extend( this, this, __apiStruct );\n\t};\n\t\n\tDataTable.Api = _Api;\n\t\n\t// Don't destroy the existing prototype, just extend it. Required for jQuery 2's\n\t// isPlainObject.\n\t$.extend( _Api.prototype, {\n\t\tany: function ()\n\t\t{\n\t\t\treturn this.count() !== 0;\n\t\t},\n\t\n\t\n\t\tconcat:  __arrayProto.concat,\n\t\n\t\n\t\tcontext: [], // array of table settings objects\n\t\n\t\n\t\tcount: function ()\n\t\t{\n\t\t\treturn this.flatten().length;\n\t\t},\n\t\n\t\n\t\teach: function ( fn )\n\t\t{\n\t\t\tfor ( var i=0, ien=this.length ; i<ien; i++ ) {\n\t\t\t\tfn.call( this, this[i], i, this );\n\t\t\t}\n\t\n\t\t\treturn this;\n\t\t},\n\t\n\t\n\t\teq: function ( idx )\n\t\t{\n\t\t\tvar ctx = this.context;\n\t\n\t\t\treturn ctx.length > idx ?\n\t\t\t\tnew _Api( ctx[idx], this[idx] ) :\n\t\t\t\tnull;\n\t\t},\n\t\n\t\n\t\tfilter: function ( fn )\n\t\t{\n\t\t\tvar a = [];\n\t\n\t\t\tif ( __arrayProto.filter ) {\n\t\t\t\ta = __arrayProto.filter.call( this, fn, this );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Compatibility for browsers without EMCA-252-5 (JS 1.6)\n\t\t\t\tfor ( var i=0, ien=this.length ; i<ien ; i++ ) {\n\t\t\t\t\tif ( fn.call( this, this[i], i, this ) ) {\n\t\t\t\t\t\ta.push( this[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\treturn new _Api( this.context, a );\n\t\t},\n\t\n\t\n\t\tflatten: function ()\n\t\t{\n\t\t\tvar a = [];\n\t\t\treturn new _Api( this.context, a.concat.apply( a, this.toArray() ) );\n\t\t},\n\t\n\t\n\t\tjoin:    __arrayProto.join,\n\t\n\t\n\t\tindexOf: __arrayProto.indexOf || function (obj, start)\n\t\t{\n\t\t\tfor ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {\n\t\t\t\tif ( this[i] === obj ) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t},\n\t\n\t\titerator: function ( flatten, type, fn, alwaysNew ) {\n\t\t\tvar\n\t\t\t\ta = [], ret,\n\t\t\t\ti, ien, j, jen,\n\t\t\t\tcontext = this.context,\n\t\t\t\trows, items, item,\n\t\t\t\tselector = this.selector;\n\t\n\t\t\t// Argument shifting\n\t\t\tif ( typeof flatten === 'string' ) {\n\t\t\t\talwaysNew = fn;\n\t\t\t\tfn = type;\n\t\t\t\ttype = flatten;\n\t\t\t\tflatten = false;\n\t\t\t}\n\t\n\t\t\tfor ( i=0, ien=context.length ; i<ien ; i++ ) {\n\t\t\t\tvar apiInst = new _Api( context[i] );\n\t\n\t\t\t\tif ( type === 'table' ) {\n\t\t\t\t\tret = fn.call( apiInst, context[i], i );\n\t\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\ta.push( ret );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( type === 'columns' || type === 'rows' ) {\n\t\t\t\t\t// this has same length as context - one entry for each table\n\t\t\t\t\tret = fn.call( apiInst, context[i], this[i], i );\n\t\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\ta.push( ret );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {\n\t\t\t\t\t// columns and rows share the same structure.\n\t\t\t\t\t// 'this' is an array of column indexes for each context\n\t\t\t\t\titems = this[i];\n\t\n\t\t\t\t\tif ( type === 'column-rows' ) {\n\t\t\t\t\t\trows = _selector_row_indexes( context[i], selector.opts );\n\t\t\t\t\t}\n\t\n\t\t\t\t\tfor ( j=0, jen=items.length ; j<jen ; j++ ) {\n\t\t\t\t\t\titem = items[j];\n\t\n\t\t\t\t\t\tif ( type === 'cell' ) {\n\t\t\t\t\t\t\tret = fn.call( apiInst, context[i], item.row, item.column, i, j );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tret = fn.call( apiInst, context[i], item, i, j, rows );\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\t\ta.push( ret );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tif ( a.length || alwaysNew ) {\n\t\t\t\tvar api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );\n\t\t\t\tvar apiSelector = api.selector;\n\t\t\t\tapiSelector.rows = selector.rows;\n\t\t\t\tapiSelector.cols = selector.cols;\n\t\t\t\tapiSelector.opts = selector.opts;\n\t\t\t\treturn api;\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\t\n\t\n\t\tlastIndexOf: __arrayProto.lastIndexOf || function (obj, start)\n\t\t{\n\t\t\t// Bit cheeky...\n\t\t\treturn this.indexOf.apply( this.toArray.reverse(), arguments );\n\t\t},\n\t\n\t\n\t\tlength:  0,\n\t\n\t\n\t\tmap: function ( fn )\n\t\t{\n\t\t\tvar a = [];\n\t\n\t\t\tif ( __arrayProto.map ) {\n\t\t\t\ta = __arrayProto.map.call( this, fn, this );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Compatibility for browsers without EMCA-252-5 (JS 1.6)\n\t\t\t\tfor ( var i=0, ien=this.length ; i<ien ; i++ ) {\n\t\t\t\t\ta.push( fn.call( this, this[i], i ) );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\treturn new _Api( this.context, a );\n\t\t},\n\t\n\t\n\t\tpluck: function ( prop )\n\t\t{\n\t\t\tvar fn = DataTable.util.get(prop);\n\t\n\t\t\treturn this.map( function ( el ) {\n\t\t\t\treturn fn(el);\n\t\t\t} );\n\t\t},\n\t\n\t\tpop:     __arrayProto.pop,\n\t\n\t\n\t\tpush:    __arrayProto.push,\n\t\n\t\n\t\t// Does not return an API instance\n\t\treduce: __arrayProto.reduce || function ( fn, init )\n\t\t{\n\t\t\treturn _fnReduce( this, fn, init, 0, this.length, 1 );\n\t\t},\n\t\n\t\n\t\treduceRight: __arrayProto.reduceRight || function ( fn, init )\n\t\t{\n\t\t\treturn _fnReduce( this, fn, init, this.length-1, -1, -1 );\n\t\t},\n\t\n\t\n\t\treverse: __arrayProto.reverse,\n\t\n\t\n\t\t// Object with rows, columns and opts\n\t\tselector: null,\n\t\n\t\n\t\tshift:   __arrayProto.shift,\n\t\n\t\n\t\tslice: function () {\n\t\t\treturn new _Api( this.context, this );\n\t\t},\n\t\n\t\n\t\tsort:    __arrayProto.sort, // ? name - order?\n\t\n\t\n\t\tsplice:  __arrayProto.splice,\n\t\n\t\n\t\ttoArray: function ()\n\t\t{\n\t\t\treturn __arrayProto.slice.call( this );\n\t\t},\n\t\n\t\n\t\tto$: function ()\n\t\t{\n\t\t\treturn $( this );\n\t\t},\n\t\n\t\n\t\ttoJQuery: function ()\n\t\t{\n\t\t\treturn $( this );\n\t\t},\n\t\n\t\n\t\tunique: function ()\n\t\t{\n\t\t\treturn new _Api( this.context, _unique(this) );\n\t\t},\n\t\n\t\n\t\tunshift: __arrayProto.unshift\n\t} );\n\t\n\t\n\t_Api.extend = function ( scope, obj, ext )\n\t{\n\t\t// Only extend API instances and static properties of the API\n\t\tif ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar\n\t\t\ti, ien,\n\t\t\tstruct,\n\t\t\tmethodScoping = function ( scope, fn, struc ) {\n\t\t\t\treturn function () {\n\t\t\t\t\tvar ret = fn.apply( scope, arguments );\n\t\n\t\t\t\t\t// Method extension\n\t\t\t\t\t_Api.extend( ret, ret, struc.methodExt );\n\t\t\t\t\treturn ret;\n\t\t\t\t};\n\t\t\t};\n\t\n\t\tfor ( i=0, ien=ext.length ; i<ien ; i++ ) {\n\t\t\tstruct = ext[i];\n\t\n\t\t\t// Value\n\t\t\tobj[ struct.name ] = struct.type === 'function' ?\n\t\t\t\tmethodScoping( scope, struct.val, struct ) :\n\t\t\t\tstruct.type === 'object' ?\n\t\t\t\t\t{} :\n\t\t\t\t\tstruct.val;\n\t\n\t\t\tobj[ struct.name ].__dt_wrapper = true;\n\t\n\t\t\t// Property extension\n\t\t\t_Api.extend( scope, obj[ struct.name ], struct.propExt );\n\t\t}\n\t};\n\t\n\t\n\t// @todo - Is there need for an augment function?\n\t// _Api.augment = function ( inst, name )\n\t// {\n\t// \t// Find src object in the structure from the name\n\t// \tvar parts = name.split('.');\n\t\n\t// \t_Api.extend( inst, obj );\n\t// };\n\t\n\t\n\t//     [\n\t//       {\n\t//         name:      'data'                -- string   - Property name\n\t//         val:       function () {},       -- function - Api method (or undefined if just an object\n\t//         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result\n\t//         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property\n\t//       },\n\t//       {\n\t//         name:     'row'\n\t//         val:       {},\n\t//         methodExt: [ ... ],\n\t//         propExt:   [\n\t//           {\n\t//             name:      'data'\n\t//             val:       function () {},\n\t//             methodExt: [ ... ],\n\t//             propExt:   [ ... ]\n\t//           },\n\t//           ...\n\t//         ]\n\t//       }\n\t//     ]\n\t\n\t_Api.register = _api_register = function ( name, val )\n\t{\n\t\tif ( Array.isArray( name ) ) {\n\t\t\tfor ( var j=0, jen=name.length ; j<jen ; j++ ) {\n\t\t\t\t_Api.register( name[j], val );\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar\n\t\t\ti, ien,\n\t\t\their = name.split('.'),\n\t\t\tstruct = __apiStruct,\n\t\t\tkey, method;\n\t\n\t\tvar find = function ( src, name ) {\n\t\t\tfor ( var i=0, ien=src.length ; i<ien ; i++ ) {\n\t\t\t\tif ( src[i].name === name ) {\n\t\t\t\t\treturn src[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\n\t\tfor ( i=0, ien=heir.length ; i<ien ; i++ ) {\n\t\t\tmethod = heir[i].indexOf('()') !== -1;\n\t\t\tkey = method ?\n\t\t\t\their[i].replace('()', '') :\n\t\t\t\their[i];\n\t\n\t\t\tvar src = find( struct, key );\n\t\t\tif ( ! src ) {\n\t\t\t\tsrc = {\n\t\t\t\t\tname:      key,\n\t\t\t\t\tval:       {},\n\t\t\t\t\tmethodExt: [],\n\t\t\t\t\tpropExt:   [],\n\t\t\t\t\ttype:      'object'\n\t\t\t\t};\n\t\t\t\tstruct.push( src );\n\t\t\t}\n\t\n\t\t\tif ( i === ien-1 ) {\n\t\t\t\tsrc.val = val;\n\t\t\t\tsrc.type = typeof val === 'function' ?\n\t\t\t\t\t'function' :\n\t\t\t\t\t$.isPlainObject( val ) ?\n\t\t\t\t\t\t'object' :\n\t\t\t\t\t\t'other';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tstruct = method ?\n\t\t\t\t\tsrc.methodExt :\n\t\t\t\t\tsrc.propExt;\n\t\t\t}\n\t\t}\n\t};\n\t\n\t_Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {\n\t\t_Api.register( pluralName, val );\n\t\n\t\t_Api.register( singularName, function () {\n\t\t\tvar ret = val.apply( this, arguments );\n\t\n\t\t\tif ( ret === this ) {\n\t\t\t\t// Returned item is the API instance that was passed in, return it\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\telse if ( ret instanceof _Api ) {\n\t\t\t\t// New API instance returned, want the value from the first item\n\t\t\t\t// in the returned array for the singular result.\n\t\t\t\treturn ret.length ?\n\t\t\t\t\tArray.isArray( ret[0] ) ?\n\t\t\t\t\t\tnew _Api( ret.context, ret[0] ) : // Array results are 'enhanced'\n\t\t\t\t\t\tret[0] :\n\t\t\t\t\tundefined;\n\t\t\t}\n\t\n\t\t\t// Non-API return - just fire it back\n\t\t\treturn ret;\n\t\t} );\n\t};\n\t\n\t\n\t/**\n\t * Selector for HTML tables. Apply the given selector to the give array of\n\t * DataTables settings objects.\n\t *\n\t * @param {string|integer} [selector] jQuery selector string or integer\n\t * @param  {array} Array of DataTables settings objects to be filtered\n\t * @return {array}\n\t * @ignore\n\t */\n\tvar __table_selector = function ( selector, a )\n\t{\n\t\tif ( Array.isArray(selector) ) {\n\t\t\treturn $.map( selector, function (item) {\n\t\t\t\treturn __table_selector(item, a);\n\t\t\t} );\n\t\t}\n\t\n\t\t// Integer is used to pick out a table by index\n\t\tif ( typeof selector === 'number' ) {\n\t\t\treturn [ a[ selector ] ];\n\t\t}\n\t\n\t\t// Perform a jQuery selector on the table nodes\n\t\tvar nodes = $.map( a, function (el, i) {\n\t\t\treturn el.nTable;\n\t\t} );\n\t\n\t\treturn $(nodes)\n\t\t\t.filter( selector )\n\t\t\t.map( function (i) {\n\t\t\t\t// Need to translate back from the table node to the settings\n\t\t\t\tvar idx = $.inArray( this, nodes );\n\t\t\t\treturn a[ idx ];\n\t\t\t} )\n\t\t\t.toArray();\n\t};\n\t\n\t\n\t\n\t/**\n\t * Context selector for the API's context (i.e. the tables the API instance\n\t * refers to.\n\t *\n\t * @name    DataTable.Api#tables\n\t * @param {string|integer} [selector] Selector to pick which tables the iterator\n\t *   should operate on. If not given, all tables in the current context are\n\t *   used. This can be given as a jQuery selector (for example `':gt(0)'`) to\n\t *   select multiple tables or as an integer to select a single table.\n\t * @returns {DataTable.Api} Returns a new API instance if a selector is given.\n\t */\n\t_api_register( 'tables()', function ( selector ) {\n\t\t// A new instance is created if there was a selector specified\n\t\treturn selector !== undefined && selector !== null ?\n\t\t\tnew _Api( __table_selector( selector, this.context ) ) :\n\t\t\tthis;\n\t} );\n\t\n\t\n\t_api_register( 'table()', function ( selector ) {\n\t\tvar tables = this.tables( selector );\n\t\tvar ctx = tables.context;\n\t\n\t\t// Truncate to the first matched table\n\t\treturn ctx.length ?\n\t\t\tnew _Api( ctx[0] ) :\n\t\t\ttables;\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().nodes()', 'table().node()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTable;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().body()', 'table().body()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTBody;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().header()', 'table().header()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTHead;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().footer()', 'table().footer()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTFoot;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().containers()', 'table().container()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTableWrapper;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Redraw the tables in the current context.\n\t */\n\t_api_register( 'draw()', function ( paging ) {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tif ( paging === 'page' ) {\n\t\t\t\t_fnDraw( settings );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif ( typeof paging === 'string' ) {\n\t\t\t\t\tpaging = paging === 'full-hold' ?\n\t\t\t\t\t\tfalse :\n\t\t\t\t\t\ttrue;\n\t\t\t\t}\n\t\n\t\t\t\t_fnReDraw( settings, paging===false );\n\t\t\t}\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Get the current page index.\n\t *\n\t * @return {integer} Current page index (zero based)\n\t *//**\n\t * Set the current page.\n\t *\n\t * Note that if you attempt to show a page which does not exist, DataTables will\n\t * not throw an error, but rather reset the paging.\n\t *\n\t * @param {integer|string} action The paging action to take. This can be one of:\n\t *  * `integer` - The page index to jump to\n\t *  * `string` - An action to take:\n\t *    * `first` - Jump to first page.\n\t *    * `next` - Jump to the next page\n\t *    * `previous` - Jump to previous page\n\t *    * `last` - Jump to the last page.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'page()', function ( action ) {\n\t\tif ( action === undefined ) {\n\t\t\treturn this.page.info().page; // not an expensive call\n\t\t}\n\t\n\t\t// else, have an action to take on all tables\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnPageChange( settings, action );\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Paging information for the first table in the current context.\n\t *\n\t * If you require paging information for another table, use the `table()` method\n\t * with a suitable selector.\n\t *\n\t * @return {object} Object with the following properties set:\n\t *  * `page` - Current page index (zero based - i.e. the first page is `0`)\n\t *  * `pages` - Total number of pages\n\t *  * `start` - Display index for the first record shown on the current page\n\t *  * `end` - Display index for the last record shown on the current page\n\t *  * `length` - Display length (number of records). Note that generally `start\n\t *    + length = end`, but this is not always true, for example if there are\n\t *    only 2 records to show on the final page, with a length of 10.\n\t *  * `recordsTotal` - Full data set length\n\t *  * `recordsDisplay` - Data set length once the current filtering criterion\n\t *    are applied.\n\t */\n\t_api_register( 'page.info()', function ( action ) {\n\t\tif ( this.context.length === 0 ) {\n\t\t\treturn undefined;\n\t\t}\n\t\n\t\tvar\n\t\t\tsettings   = this.context[0],\n\t\t\tstart      = settings._iDisplayStart,\n\t\t\tlen        = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,\n\t\t\tvisRecords = settings.fnRecordsDisplay(),\n\t\t\tall        = len === -1;\n\t\n\t\treturn {\n\t\t\t\"page\":           all ? 0 : Math.floor( start / len ),\n\t\t\t\"pages\":          all ? 1 : Math.ceil( visRecords / len ),\n\t\t\t\"start\":          start,\n\t\t\t\"end\":            settings.fnDisplayEnd(),\n\t\t\t\"length\":         len,\n\t\t\t\"recordsTotal\":   settings.fnRecordsTotal(),\n\t\t\t\"recordsDisplay\": visRecords,\n\t\t\t\"serverSide\":     _fnDataSource( settings ) === 'ssp'\n\t\t};\n\t} );\n\t\n\t\n\t/**\n\t * Get the current page length.\n\t *\n\t * @return {integer} Current page length. Note `-1` indicates that all records\n\t *   are to be shown.\n\t *//**\n\t * Set the current page length.\n\t *\n\t * @param {integer} Page length to set. Use `-1` to show all records.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'page.len()', function ( len ) {\n\t\t// Note that we can't call this function 'length()' because `length`\n\t\t// is a Javascript property of functions which defines how many arguments\n\t\t// the function expects.\n\t\tif ( len === undefined ) {\n\t\t\treturn this.context.length !== 0 ?\n\t\t\t\tthis.context[0]._iDisplayLength :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// else, set the page length\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnLengthChange( settings, len );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\tvar __reload = function ( settings, holdPosition, callback ) {\n\t\t// Use the draw event to trigger a callback\n\t\tif ( callback ) {\n\t\t\tvar api = new _Api( settings );\n\t\n\t\t\tapi.one( 'draw', function () {\n\t\t\t\tcallback( api.ajax.json() );\n\t\t\t} );\n\t\t}\n\t\n\t\tif ( _fnDataSource( settings ) == 'ssp' ) {\n\t\t\t_fnReDraw( settings, holdPosition );\n\t\t}\n\t\telse {\n\t\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t\t// Cancel an existing request\n\t\t\tvar xhr = settings.jqXHR;\n\t\t\tif ( xhr && xhr.readyState !== 4 ) {\n\t\t\t\txhr.abort();\n\t\t\t}\n\t\n\t\t\t// Trigger xhr\n\t\t\t_fnBuildAjax( settings, [], function( json ) {\n\t\t\t\t_fnClearTable( settings );\n\t\n\t\t\t\tvar data = _fnAjaxDataSrc( settings, json );\n\t\t\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\t\t_fnAddData( settings, data[i] );\n\t\t\t\t}\n\t\n\t\t\t\t_fnReDraw( settings, holdPosition );\n\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t} );\n\t\t}\n\t};\n\t\n\t\n\t/**\n\t * Get the JSON response from the last Ajax request that DataTables made to the\n\t * server. Note that this returns the JSON from the first table in the current\n\t * context.\n\t *\n\t * @return {object} JSON received from the server.\n\t */\n\t_api_register( 'ajax.json()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( ctx.length > 0 ) {\n\t\t\treturn ctx[0].json;\n\t\t}\n\t\n\t\t// else return undefined;\n\t} );\n\t\n\t\n\t/**\n\t * Get the data submitted in the last Ajax request\n\t */\n\t_api_register( 'ajax.params()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( ctx.length > 0 ) {\n\t\t\treturn ctx[0].oAjaxData;\n\t\t}\n\t\n\t\t// else return undefined;\n\t} );\n\t\n\t\n\t/**\n\t * Reload tables from the Ajax data source. Note that this function will\n\t * automatically re-draw the table when the remote data has been loaded.\n\t *\n\t * @param {boolean} [reset=true] Reset (default) or hold the current paging\n\t *   position. A full re-sort and re-filter is performed when this method is\n\t *   called, which is why the pagination reset is the default action.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'ajax.reload()', function ( callback, resetPaging ) {\n\t\treturn this.iterator( 'table', function (settings) {\n\t\t\t__reload( settings, resetPaging===false, callback );\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Get the current Ajax URL. Note that this returns the URL from the first\n\t * table in the current context.\n\t *\n\t * @return {string} Current Ajax source URL\n\t *//**\n\t * Set the Ajax URL. Note that this will set the URL for all tables in the\n\t * current context.\n\t *\n\t * @param {string} url URL to set.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'ajax.url()', function ( url ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( url === undefined ) {\n\t\t\t// get\n\t\t\tif ( ctx.length === 0 ) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tctx = ctx[0];\n\t\n\t\t\treturn ctx.ajax ?\n\t\t\t\t$.isPlainObject( ctx.ajax ) ?\n\t\t\t\t\tctx.ajax.url :\n\t\t\t\t\tctx.ajax :\n\t\t\t\tctx.sAjaxSource;\n\t\t}\n\t\n\t\t// set\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tif ( $.isPlainObject( settings.ajax ) ) {\n\t\t\t\tsettings.ajax.url = url;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsettings.ajax = url;\n\t\t\t}\n\t\t\t// No need to consider sAjaxSource here since DataTables gives priority\n\t\t\t// to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any\n\t\t\t// value of `sAjaxSource` redundant.\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Load data from the newly set Ajax URL. Note that this method is only\n\t * available when `ajax.url()` is used to set a URL. Additionally, this method\n\t * has the same effect as calling `ajax.reload()` but is provided for\n\t * convenience when setting a new URL. Like `ajax.reload()` it will\n\t * automatically redraw the table once the remote data has been loaded.\n\t *\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'ajax.url().load()', function ( callback, resetPaging ) {\n\t\t// Same as a reload, but makes sense to present it for easy access after a\n\t\t// url change\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\t__reload( ctx, resetPaging===false, callback );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t\n\tvar _selector_run = function ( type, selector, selectFn, settings, opts )\n\t{\n\t\tvar\n\t\t\tout = [], res,\n\t\t\ta, i, ien, j, jen,\n\t\t\tselectorType = typeof selector;\n\t\n\t\t// Can't just check for isArray here, as an API or jQuery instance might be\n\t\t// given with their array like look\n\t\tif ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {\n\t\t\tselector = [ selector ];\n\t\t}\n\t\n\t\tfor ( i=0, ien=selector.length ; i<ien ; i++ ) {\n\t\t\t// Only split on simple strings - complex expressions will be jQuery selectors\n\t\t\ta = selector[i] && selector[i].split && ! selector[i].match(/[\\[\\(:]/) ?\n\t\t\t\tselector[i].split(',') :\n\t\t\t\t[ selector[i] ];\n\t\n\t\t\tfor ( j=0, jen=a.length ; j<jen ; j++ ) {\n\t\t\t\tres = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );\n\t\n\t\t\t\tif ( res && res.length ) {\n\t\t\t\t\tout = out.concat( res );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\t// selector extensions\n\t\tvar ext = _ext.selector[ type ];\n\t\tif ( ext.length ) {\n\t\t\tfor ( i=0, ien=ext.length ; i<ien ; i++ ) {\n\t\t\t\tout = ext[i]( settings, opts, out );\n\t\t\t}\n\t\t}\n\t\n\t\treturn _unique( out );\n\t};\n\t\n\t\n\tvar _selector_opts = function ( opts )\n\t{\n\t\tif ( ! opts ) {\n\t\t\topts = {};\n\t\t}\n\t\n\t\t// Backwards compatibility for 1.9- which used the terminology filter rather\n\t\t// than search\n\t\tif ( opts.filter && opts.search === undefined ) {\n\t\t\topts.search = opts.filter;\n\t\t}\n\t\n\t\treturn $.extend( {\n\t\t\tsearch: 'none',\n\t\t\torder: 'current',\n\t\t\tpage: 'all'\n\t\t}, opts );\n\t};\n\t\n\t\n\tvar _selector_first = function ( inst )\n\t{\n\t\t// Reduce the API instance to the first item found\n\t\tfor ( var i=0, ien=inst.length ; i<ien ; i++ ) {\n\t\t\tif ( inst[i].length > 0 ) {\n\t\t\t\t// Assign the first element to the first item in the instance\n\t\t\t\t// and truncate the instance and context\n\t\t\t\tinst[0] = inst[i];\n\t\t\t\tinst[0].length = 1;\n\t\t\t\tinst.length = 1;\n\t\t\t\tinst.context = [ inst.context[i] ];\n\t\n\t\t\t\treturn inst;\n\t\t\t}\n\t\t}\n\t\n\t\t// Not found - return an empty instance\n\t\tinst.length = 0;\n\t\treturn inst;\n\t};\n\t\n\t\n\tvar _selector_row_indexes = function ( settings, opts )\n\t{\n\t\tvar\n\t\t\ti, ien, tmp, a=[],\n\t\t\tdisplayFiltered = settings.aiDisplay,\n\t\t\tdisplayMaster = settings.aiDisplayMaster;\n\t\n\t\tvar\n\t\t\tsearch = opts.search,  // none, applied, removed\n\t\t\torder  = opts.order,   // applied, current, index (original - compatibility with 1.9)\n\t\t\tpage   = opts.page;    // all, current\n\t\n\t\tif ( _fnDataSource( settings ) == 'ssp' ) {\n\t\t\t// In server-side processing mode, most options are irrelevant since\n\t\t\t// rows not shown don't exist and the index order is the applied order\n\t\t\t// Removed is a special case - for consistency just return an empty\n\t\t\t// array\n\t\t\treturn search === 'removed' ?\n\t\t\t\t[] :\n\t\t\t\t_range( 0, displayMaster.length );\n\t\t}\n\t\telse if ( page == 'current' ) {\n\t\t\t// Current page implies that order=current and filter=applied, since it is\n\t\t\t// fairly senseless otherwise, regardless of what order and search actually\n\t\t\t// are\n\t\t\tfor ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {\n\t\t\t\ta.push( displayFiltered[i] );\n\t\t\t}\n\t\t}\n\t\telse if ( order == 'current' || order == 'applied' ) {\n\t\t\tif ( search == 'none') {\n\t\t\t\ta = displayMaster.slice();\n\t\t\t}\n\t\t\telse if ( search == 'applied' ) {\n\t\t\t\ta = displayFiltered.slice();\n\t\t\t}\n\t\t\telse if ( search == 'removed' ) {\n\t\t\t\t// O(n+m) solution by creating a hash map\n\t\t\t\tvar displayFilteredMap = {};\n\t\n\t\t\t\tfor ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {\n\t\t\t\t\tdisplayFilteredMap[displayFiltered[i]] = null;\n\t\t\t\t}\n\t\n\t\t\t\ta = $.map( displayMaster, function (el) {\n\t\t\t\t\treturn ! displayFilteredMap.hasOwnProperty(el) ?\n\t\t\t\t\t\tel :\n\t\t\t\t\t\tnull;\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t\telse if ( order == 'index' || order == 'original' ) {\n\t\t\tfor ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\t\tif ( search == 'none' ) {\n\t\t\t\t\ta.push( i );\n\t\t\t\t}\n\t\t\t\telse { // applied | removed\n\t\t\t\t\ttmp = $.inArray( i, displayFiltered );\n\t\n\t\t\t\t\tif ((tmp === -1 && search == 'removed') ||\n\t\t\t\t\t\t(tmp >= 0   && search == 'applied') )\n\t\t\t\t\t{\n\t\t\t\t\t\ta.push( i );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn a;\n\t};\n\t\n\t\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Rows\n\t *\n\t * {}          - no selector - use all available rows\n\t * {integer}   - row aoData index\n\t * {node}      - TR node\n\t * {string}    - jQuery selector to apply to the TR elements\n\t * {array}     - jQuery array of nodes, or simply an array of TR nodes\n\t *\n\t */\n\tvar __row_selector = function ( settings, selector, opts )\n\t{\n\t\tvar rows;\n\t\tvar run = function ( sel ) {\n\t\t\tvar selInt = _intVal( sel );\n\t\t\tvar i, ien;\n\t\t\tvar aoData = settings.aoData;\n\t\n\t\t\t// Short cut - selector is a number and no options provided (default is\n\t\t\t// all records, so no need to check if the index is in there, since it\n\t\t\t// must be - dev error if the index doesn't exist).\n\t\t\tif ( selInt !== null && ! opts ) {\n\t\t\t\treturn [ selInt ];\n\t\t\t}\n\t\n\t\t\tif ( ! rows ) {\n\t\t\t\trows = _selector_row_indexes( settings, opts );\n\t\t\t}\n\t\n\t\t\tif ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {\n\t\t\t\t// Selector - integer\n\t\t\t\treturn [ selInt ];\n\t\t\t}\n\t\t\telse if ( sel === null || sel === undefined || sel === '' ) {\n\t\t\t\t// Selector - none\n\t\t\t\treturn rows;\n\t\t\t}\n\t\n\t\t\t// Selector - function\n\t\t\tif ( typeof sel === 'function' ) {\n\t\t\t\treturn $.map( rows, function (idx) {\n\t\t\t\t\tvar row = aoData[ idx ];\n\t\t\t\t\treturn sel( idx, row._aData, row.nTr ) ? idx : null;\n\t\t\t\t} );\n\t\t\t}\n\t\n\t\t\t// Selector - node\n\t\t\tif ( sel.nodeName ) {\n\t\t\t\tvar rowIdx = sel._DT_RowIndex;  // Property added by DT for fast lookup\n\t\t\t\tvar cellIdx = sel._DT_CellIndex;\n\t\n\t\t\t\tif ( rowIdx !== undefined ) {\n\t\t\t\t\t// Make sure that the row is actually still present in the table\n\t\t\t\t\treturn aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?\n\t\t\t\t\t\t[ rowIdx ] :\n\t\t\t\t\t\t[];\n\t\t\t\t}\n\t\t\t\telse if ( cellIdx ) {\n\t\t\t\t\treturn aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?\n\t\t\t\t\t\t[ cellIdx.row ] :\n\t\t\t\t\t\t[];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tvar host = $(sel).closest('*[data-dt-row]');\n\t\t\t\t\treturn host.length ?\n\t\t\t\t\t\t[ host.data('dt-row') ] :\n\t\t\t\t\t\t[];\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// ID selector. Want to always be able to select rows by id, regardless\n\t\t\t// of if the tr element has been created or not, so can't rely upon\n\t\t\t// jQuery here - hence a custom implementation. This does not match\n\t\t\t// Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,\n\t\t\t// but to select it using a CSS selector engine (like Sizzle or\n\t\t\t// querySelect) it would need to need to be escaped for some characters.\n\t\t\t// DataTables simplifies this for row selectors since you can select\n\t\t\t// only a row. A # indicates an id any anything that follows is the id -\n\t\t\t// unescaped.\n\t\t\tif ( typeof sel === 'string' && sel.charAt(0) === '#' ) {\n\t\t\t\t// get row index from id\n\t\t\t\tvar rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];\n\t\t\t\tif ( rowObj !== undefined ) {\n\t\t\t\t\treturn [ rowObj.idx ];\n\t\t\t\t}\n\t\n\t\t\t\t// need to fall through to jQuery in case there is DOM id that\n\t\t\t\t// matches\n\t\t\t}\n\t\t\t\n\t\t\t// Get nodes in the order from the `rows` array with null values removed\n\t\t\tvar nodes = _removeEmpty(\n\t\t\t\t_pluck_order( settings.aoData, rows, 'nTr' )\n\t\t\t);\n\t\n\t\t\t// Selector - jQuery selector string, array of nodes or jQuery object/\n\t\t\t// As jQuery's .filter() allows jQuery objects to be passed in filter,\n\t\t\t// it also allows arrays, so this will cope with all three options\n\t\t\treturn $(nodes)\n\t\t\t\t.filter( sel )\n\t\t\t\t.map( function () {\n\t\t\t\t\treturn this._DT_RowIndex;\n\t\t\t\t} )\n\t\t\t\t.toArray();\n\t\t};\n\t\n\t\treturn _selector_run( 'row', selector, run, settings, opts );\n\t};\n\t\n\t\n\t_api_register( 'rows()', function ( selector, opts ) {\n\t\t// argument shifting\n\t\tif ( selector === undefined ) {\n\t\t\tselector = '';\n\t\t}\n\t\telse if ( $.isPlainObject( selector ) ) {\n\t\t\topts = selector;\n\t\t\tselector = '';\n\t\t}\n\t\n\t\topts = _selector_opts( opts );\n\t\n\t\tvar inst = this.iterator( 'table', function ( settings ) {\n\t\t\treturn __row_selector( settings, selector, opts );\n\t\t}, 1 );\n\t\n\t\t// Want argument shifting here and in __row_selector?\n\t\tinst.selector.rows = selector;\n\t\tinst.selector.opts = opts;\n\t\n\t\treturn inst;\n\t} );\n\t\n\t_api_register( 'rows().nodes()', function () {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\treturn settings.aoData[ row ].nTr || undefined;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_register( 'rows().data()', function () {\n\t\treturn this.iterator( true, 'rows', function ( settings, rows ) {\n\t\t\treturn _pluck_order( settings.aoData, rows, '_aData' );\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\tvar r = settings.aoData[ row ];\n\t\t\treturn type === 'search' ? r._aFilterData : r._aSortData;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\t_fnInvalidate( settings, row, src );\n\t\t} );\n\t} );\n\t\n\t_api_registerPlural( 'rows().indexes()', 'row().index()', function () {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\treturn row;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {\n\t\tvar a = [];\n\t\tvar context = this.context;\n\t\n\t\t// `iterator` will drop undefined values, but in this case we want them\n\t\tfor ( var i=0, ien=context.length ; i<ien ; i++ ) {\n\t\t\tfor ( var j=0, jen=this[i].length ; j<jen ; j++ ) {\n\t\t\t\tvar id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );\n\t\t\t\ta.push( (hash === true ? '#' : '' )+ id );\n\t\t\t}\n\t\t}\n\t\n\t\treturn new _Api( context, a );\n\t} );\n\t\n\t_api_registerPlural( 'rows().remove()', 'row().remove()', function () {\n\t\tvar that = this;\n\t\n\t\tthis.iterator( 'row', function ( settings, row, thatIdx ) {\n\t\t\tvar data = settings.aoData;\n\t\t\tvar rowData = data[ row ];\n\t\t\tvar i, ien, j, jen;\n\t\t\tvar loopRow, loopCells;\n\t\n\t\t\tdata.splice( row, 1 );\n\t\n\t\t\t// Update the cached indexes\n\t\t\tfor ( i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\tloopRow = data[i];\n\t\t\t\tloopCells = loopRow.anCells;\n\t\n\t\t\t\t// Rows\n\t\t\t\tif ( loopRow.nTr !== null ) {\n\t\t\t\t\tloopRow.nTr._DT_RowIndex = i;\n\t\t\t\t}\n\t\n\t\t\t\t// Cells\n\t\t\t\tif ( loopCells !== null ) {\n\t\t\t\t\tfor ( j=0, jen=loopCells.length ; j<jen ; j++ ) {\n\t\t\t\t\t\tloopCells[j]._DT_CellIndex.row = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Delete from the display arrays\n\t\t\t_fnDeleteIndex( settings.aiDisplayMaster, row );\n\t\t\t_fnDeleteIndex( settings.aiDisplay, row );\n\t\t\t_fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes\n\t\n\t\t\t// For server-side processing tables - subtract the deleted row from the count\n\t\t\tif ( settings._iRecordsDisplay > 0 ) {\n\t\t\t\tsettings._iRecordsDisplay--;\n\t\t\t}\n\t\n\t\t\t// Check for an 'overflow' they case for displaying the table\n\t\t\t_fnLengthOverflow( settings );\n\t\n\t\t\t// Remove the row's ID reference if there is one\n\t\t\tvar id = settings.rowIdFn( rowData._aData );\n\t\t\tif ( id !== undefined ) {\n\t\t\t\tdelete settings.aIds[ id ];\n\t\t\t}\n\t\t} );\n\t\n\t\tthis.iterator( 'table', function ( settings ) {\n\t\t\tfor ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\t\tsettings.aoData[i].idx = i;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( 'rows.add()', function ( rows ) {\n\t\tvar newRows = this.iterator( 'table', function ( settings ) {\n\t\t\t\tvar row, i, ien;\n\t\t\t\tvar out = [];\n\t\n\t\t\t\tfor ( i=0, ien=rows.length ; i<ien ; i++ ) {\n\t\t\t\t\trow = rows[i];\n\t\n\t\t\t\t\tif ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {\n\t\t\t\t\t\tout.push( _fnAddTr( settings, row )[0] );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tout.push( _fnAddData( settings, row ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\treturn out;\n\t\t\t}, 1 );\n\t\n\t\t// Return an Api.rows() extended instance, so rows().nodes() etc can be used\n\t\tvar modRows = this.rows( -1 );\n\t\tmodRows.pop();\n\t\t$.merge( modRows, newRows );\n\t\n\t\treturn modRows;\n\t} );\n\t\n\t\n\t\n\t\n\t\n\t/**\n\t *\n\t */\n\t_api_register( 'row()', function ( selector, opts ) {\n\t\treturn _selector_first( this.rows( selector, opts ) );\n\t} );\n\t\n\t\n\t_api_register( 'row().data()', function ( data ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( data === undefined ) {\n\t\t\t// Get\n\t\t\treturn ctx.length && this.length ?\n\t\t\t\tctx[0].aoData[ this[0] ]._aData :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// Set\n\t\tvar row = ctx[0].aoData[ this[0] ];\n\t\trow._aData = data;\n\t\n\t\t// If the DOM has an id, and the data source is an array\n\t\tif ( Array.isArray( data ) && row.nTr && row.nTr.id ) {\n\t\t\t_fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );\n\t\t}\n\t\n\t\t// Automatically invalidate\n\t\t_fnInvalidate( ctx[0], this[0], 'data' );\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( 'row().node()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\treturn ctx.length && this.length ?\n\t\t\tctx[0].aoData[ this[0] ].nTr || null :\n\t\t\tnull;\n\t} );\n\t\n\t\n\t_api_register( 'row.add()', function ( row ) {\n\t\t// Allow a jQuery object to be passed in - only a single row is added from\n\t\t// it though - the first element in the set\n\t\tif ( row instanceof $ && row.length ) {\n\t\t\trow = row[0];\n\t\t}\n\t\n\t\tvar rows = this.iterator( 'table', function ( settings ) {\n\t\t\tif ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {\n\t\t\t\treturn _fnAddTr( settings, row )[0];\n\t\t\t}\n\t\t\treturn _fnAddData( settings, row );\n\t\t} );\n\t\n\t\t// Return an Api.rows() extended instance, with the newly added row selected\n\t\treturn this.row( rows[0] );\n\t} );\n\t\n\t\n\t$(document).on('plugin-init.dt', function (e, context) {\n\t\tvar api = new _Api( context );\n\t\tvar namespace = 'on-plugin-init';\n\t\tvar stateSaveParamsEvent = 'stateSaveParams.' + namespace;\n\t\tvar destroyEvent = 'destroy. ' + namespace;\n\t\n\t\tapi.on( stateSaveParamsEvent, function ( e, settings, d ) {\n\t\t\t// This could be more compact with the API, but it is a lot faster as a simple\n\t\t\t// internal loop\n\t\t\tvar idFn = settings.rowIdFn;\n\t\t\tvar data = settings.aoData;\n\t\t\tvar ids = [];\n\t\n\t\t\tfor (var i=0 ; i<data.length ; i++) {\n\t\t\t\tif (data[i]._detailsShow) {\n\t\t\t\t\tids.push( '#' + idFn(data[i]._aData) );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\td.childRows = ids;\n\t\t});\n\t\n\t\tapi.on( destroyEvent, function () {\n\t\t\tapi.off(stateSaveParamsEvent + ' ' + destroyEvent);\n\t\t});\n\t\n\t\tvar loaded = api.state.loaded();\n\t\n\t\tif ( loaded && loaded.childRows ) {\n\t\t\tapi\n\t\t\t\t.rows( $.map(loaded.childRows, function (id){\n\t\t\t\t\treturn id.replace(/:/g, '\\\\:')\n\t\t\t\t}) )\n\t\t\t\t.every( function () {\n\t\t\t\t\t_fnCallbackFire( context, null, 'requestChild', [ this ] )\n\t\t\t\t});\n\t\t}\n\t});\n\t\n\tvar __details_add = function ( ctx, row, data, klass )\n\t{\n\t\t// Convert to array of TR elements\n\t\tvar rows = [];\n\t\tvar addRow = function ( r, k ) {\n\t\t\t// Recursion to allow for arrays of jQuery objects\n\t\t\tif ( Array.isArray( r ) || r instanceof $ ) {\n\t\t\t\tfor ( var i=0, ien=r.length ; i<ien ; i++ ) {\n\t\t\t\t\taddRow( r[i], k );\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// If we get a TR element, then just add it directly - up to the dev\n\t\t\t// to add the correct number of columns etc\n\t\t\tif ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {\n\t\t\t\trows.push( r );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Otherwise create a row with a wrapper\n\t\t\t\tvar created = $('<tr><td></td></tr>').addClass( k );\n\t\t\t\t$('td', created)\n\t\t\t\t\t.addClass( k )\n\t\t\t\t\t.html( r )\n\t\t\t\t\t[0].colSpan = _fnVisbleColumns( ctx );\n\t\n\t\t\t\trows.push( created[0] );\n\t\t\t}\n\t\t};\n\t\n\t\taddRow( data, klass );\n\t\n\t\tif ( row._details ) {\n\t\t\trow._details.detach();\n\t\t}\n\t\n\t\trow._details = $(rows);\n\t\n\t\t// If the children were already shown, that state should be retained\n\t\tif ( row._detailsShow ) {\n\t\t\trow._details.insertAfter( row.nTr );\n\t\t}\n\t};\n\t\n\t\n\t// Make state saving of child row details async to allow them to be batch processed\n\tvar __details_state = DataTable.util.throttle(\n\t\tfunction (ctx) {\n\t\t\t_fnSaveState( ctx[0] )\n\t\t},\n\t\t500\n\t);\n\t\n\t\n\tvar __details_remove = function ( api, idx )\n\t{\n\t\tvar ctx = api.context;\n\t\n\t\tif ( ctx.length ) {\n\t\t\tvar row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];\n\t\n\t\t\tif ( row && row._details ) {\n\t\t\t\trow._details.remove();\n\t\n\t\t\t\trow._detailsShow = undefined;\n\t\t\t\trow._details = undefined;\n\t\t\t\t$( row.nTr ).removeClass( 'dt-hasChild' );\n\t\t\t\t__details_state( ctx );\n\t\t\t}\n\t\t}\n\t};\n\t\n\t\n\tvar __details_display = function ( api, show ) {\n\t\tvar ctx = api.context;\n\t\n\t\tif ( ctx.length && api.length ) {\n\t\t\tvar row = ctx[0].aoData[ api[0] ];\n\t\n\t\t\tif ( row._details ) {\n\t\t\t\trow._detailsShow = show;\n\t\n\t\t\t\tif ( show ) {\n\t\t\t\t\trow._details.insertAfter( row.nTr );\n\t\t\t\t\t$( row.nTr ).addClass( 'dt-hasChild' );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\trow._details.detach();\n\t\t\t\t\t$( row.nTr ).removeClass( 'dt-hasChild' );\n\t\t\t\t}\n\t\n\t\t\t\t_fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] )\n\t\n\t\t\t\t__details_events( ctx[0] );\n\t\t\t\t__details_state( ctx );\n\t\t\t}\n\t\t}\n\t};\n\t\n\t\n\tvar __details_events = function ( settings )\n\t{\n\t\tvar api = new _Api( settings );\n\t\tvar namespace = '.dt.DT_details';\n\t\tvar drawEvent = 'draw'+namespace;\n\t\tvar colvisEvent = 'column-sizing'+namespace;\n\t\tvar destroyEvent = 'destroy'+namespace;\n\t\tvar data = settings.aoData;\n\t\n\t\tapi.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );\n\t\n\t\tif ( _pluck( data, '_details' ).length > 0 ) {\n\t\t\t// On each draw, insert the required elements into the document\n\t\t\tapi.on( drawEvent, function ( e, ctx ) {\n\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\tapi.rows( {page:'current'} ).eq(0).each( function (idx) {\n\t\t\t\t\t// Internal data grab\n\t\t\t\t\tvar row = data[ idx ];\n\t\n\t\t\t\t\tif ( row._detailsShow ) {\n\t\t\t\t\t\trow._details.insertAfter( row.nTr );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t} );\n\t\n\t\t\t// Column visibility change - update the colspan\n\t\t\tapi.on( colvisEvent, function ( e, ctx, idx, vis ) {\n\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\t// Update the colspan for the details rows (note, only if it already has\n\t\t\t\t// a colspan)\n\t\t\t\tvar row, visible = _fnVisbleColumns( ctx );\n\t\n\t\t\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\t\trow = data[i];\n\t\n\t\t\t\t\tif ( row._details ) {\n\t\t\t\t\t\trow._details.children('td[colspan]').attr('colspan', visible );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\t\n\t\t\t// Table destroyed - nuke any child rows\n\t\t\tapi.on( destroyEvent, function ( e, ctx ) {\n\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\t\tif ( data[i]._details ) {\n\t\t\t\t\t\t__details_remove( api, i );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t};\n\t\n\t// Strings for the method names to help minification\n\tvar _emp = '';\n\tvar _child_obj = _emp+'row().child';\n\tvar _child_mth = _child_obj+'()';\n\t\n\t// data can be:\n\t//  tr\n\t//  string\n\t//  jQuery or array of any of the above\n\t_api_register( _child_mth, function ( data, klass ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( data === undefined ) {\n\t\t\t// get\n\t\t\treturn ctx.length && this.length ?\n\t\t\t\tctx[0].aoData[ this[0] ]._details :\n\t\t\t\tundefined;\n\t\t}\n\t\telse if ( data === true ) {\n\t\t\t// show\n\t\t\tthis.child.show();\n\t\t}\n\t\telse if ( data === false ) {\n\t\t\t// remove\n\t\t\t__details_remove( this );\n\t\t}\n\t\telse if ( ctx.length && this.length ) {\n\t\t\t// set\n\t\t\t__details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );\n\t\t}\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( [\n\t\t_child_obj+'.show()',\n\t\t_child_mth+'.show()' // only when `child()` was called with parameters (without\n\t], function ( show ) {   // it returns an object and this method is not executed)\n\t\t__details_display( this, true );\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( [\n\t\t_child_obj+'.hide()',\n\t\t_child_mth+'.hide()' // only when `child()` was called with parameters (without\n\t], function () {         // it returns an object and this method is not executed)\n\t\t__details_display( this, false );\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( [\n\t\t_child_obj+'.remove()',\n\t\t_child_mth+'.remove()' // only when `child()` was called with parameters (without\n\t], function () {           // it returns an object and this method is not executed)\n\t\t__details_remove( this );\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( _child_obj+'.isShown()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( ctx.length && this.length ) {\n\t\t\t// _detailsShown as false or undefined will fall through to return false\n\t\t\treturn ctx[0].aoData[ this[0] ]._detailsShow || false;\n\t\t}\n\t\treturn false;\n\t} );\n\t\n\t\n\t\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Columns\n\t *\n\t * {integer}           - column index (>=0 count from left, <0 count from right)\n\t * \"{integer}:visIdx\"  - visible column index (i.e. translate to column index)  (>=0 count from left, <0 count from right)\n\t * \"{integer}:visible\" - alias for {integer}:visIdx  (>=0 count from left, <0 count from right)\n\t * \"{string}:name\"     - column name\n\t * \"{string}\"          - jQuery selector on column header nodes\n\t *\n\t */\n\t\n\t// can be an array of these items, comma separated list, or an array of comma\n\t// separated lists\n\t\n\tvar __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;\n\t\n\t\n\t// r1 and r2 are redundant - but it means that the parameters match for the\n\t// iterator callback in columns().data()\n\tvar __columnData = function ( settings, column, r1, r2, rows ) {\n\t\tvar a = [];\n\t\tfor ( var row=0, ien=rows.length ; row<ien ; row++ ) {\n\t\t\ta.push( _fnGetCellData( settings, rows[row], column ) );\n\t\t}\n\t\treturn a;\n\t};\n\t\n\t\n\tvar __column_selector = function ( settings, selector, opts )\n\t{\n\t\tvar\n\t\t\tcolumns = settings.aoColumns,\n\t\t\tnames = _pluck( columns, 'sName' ),\n\t\t\tnodes = _pluck( columns, 'nTh' );\n\t\n\t\tvar run = function ( s ) {\n\t\t\tvar selInt = _intVal( s );\n\t\n\t\t\t// Selector - all\n\t\t\tif ( s === '' ) {\n\t\t\t\treturn _range( columns.length );\n\t\t\t}\n\t\n\t\t\t// Selector - index\n\t\t\tif ( selInt !== null ) {\n\t\t\t\treturn [ selInt >= 0 ?\n\t\t\t\t\tselInt : // Count from left\n\t\t\t\t\tcolumns.length + selInt // Count from right (+ because its a negative value)\n\t\t\t\t];\n\t\t\t}\n\t\n\t\t\t// Selector = function\n\t\t\tif ( typeof s === 'function' ) {\n\t\t\t\tvar rows = _selector_row_indexes( settings, opts );\n\t\n\t\t\t\treturn $.map( columns, function (col, idx) {\n\t\t\t\t\treturn s(\n\t\t\t\t\t\t\tidx,\n\t\t\t\t\t\t\t__columnData( settings, idx, 0, 0, rows ),\n\t\t\t\t\t\t\tnodes[ idx ]\n\t\t\t\t\t\t) ? idx : null;\n\t\t\t\t} );\n\t\t\t}\n\t\n\t\t\t// jQuery or string selector\n\t\t\tvar match = typeof s === 'string' ?\n\t\t\t\ts.match( __re_column_selector ) :\n\t\t\t\t'';\n\t\n\t\t\tif ( match ) {\n\t\t\t\tswitch( match[2] ) {\n\t\t\t\t\tcase 'visIdx':\n\t\t\t\t\tcase 'visible':\n\t\t\t\t\t\tvar idx = parseInt( match[1], 10 );\n\t\t\t\t\t\t// Visible index given, convert to column index\n\t\t\t\t\t\tif ( idx < 0 ) {\n\t\t\t\t\t\t\t// Counting from the right\n\t\t\t\t\t\t\tvar visColumns = $.map( columns, function (col,i) {\n\t\t\t\t\t\t\t\treturn col.bVisible ? i : null;\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t\treturn [ visColumns[ visColumns.length + idx ] ];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Counting from the left\n\t\t\t\t\t\treturn [ _fnVisibleToColumnIndex( settings, idx ) ];\n\t\n\t\t\t\t\tcase 'name':\n\t\t\t\t\t\t// match by name. `names` is column index complete and in order\n\t\t\t\t\t\treturn $.map( names, function (name, i) {\n\t\t\t\t\t\t\treturn name === match[1] ? i : null;\n\t\t\t\t\t\t} );\n\t\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Cell in the table body\n\t\t\tif ( s.nodeName && s._DT_CellIndex ) {\n\t\t\t\treturn [ s._DT_CellIndex.column ];\n\t\t\t}\n\t\n\t\t\t// jQuery selector on the TH elements for the columns\n\t\t\tvar jqResult = $( nodes )\n\t\t\t\t.filter( s )\n\t\t\t\t.map( function () {\n\t\t\t\t\treturn $.inArray( this, nodes ); // `nodes` is column index complete and in order\n\t\t\t\t} )\n\t\t\t\t.toArray();\n\t\n\t\t\tif ( jqResult.length || ! s.nodeName ) {\n\t\t\t\treturn jqResult;\n\t\t\t}\n\t\n\t\t\t// Otherwise a node which might have a `dt-column` data attribute, or be\n\t\t\t// a child or such an element\n\t\t\tvar host = $(s).closest('*[data-dt-column]');\n\t\t\treturn host.length ?\n\t\t\t\t[ host.data('dt-column') ] :\n\t\t\t\t[];\n\t\t};\n\t\n\t\treturn _selector_run( 'column', selector, run, settings, opts );\n\t};\n\t\n\t\n\tvar __setColumnVis = function ( settings, column, vis ) {\n\t\tvar\n\t\t\tcols = settings.aoColumns,\n\t\t\tcol  = cols[ column ],\n\t\t\tdata = settings.aoData,\n\t\t\trow, cells, i, ien, tr;\n\t\n\t\t// Get\n\t\tif ( vis === undefined ) {\n\t\t\treturn col.bVisible;\n\t\t}\n\t\n\t\t// Set\n\t\t// No change\n\t\tif ( col.bVisible === vis ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tif ( vis ) {\n\t\t\t// Insert column\n\t\t\t// Need to decide if we should use appendChild or insertBefore\n\t\t\tvar insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );\n\t\n\t\t\tfor ( i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\ttr = data[i].nTr;\n\t\t\t\tcells = data[i].anCells;\n\t\n\t\t\t\tif ( tr ) {\n\t\t\t\t\t// insertBefore can act like appendChild if 2nd arg is null\n\t\t\t\t\ttr.insertBefore( cells[ column ], cells[ insertBefore ] || null );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Remove column\n\t\t\t$( _pluck( settings.aoData, 'anCells', column ) ).detach();\n\t\t}\n\t\n\t\t// Common actions\n\t\tcol.bVisible = vis;\n\t};\n\t\n\t\n\t_api_register( 'columns()', function ( selector, opts ) {\n\t\t// argument shifting\n\t\tif ( selector === undefined ) {\n\t\t\tselector = '';\n\t\t}\n\t\telse if ( $.isPlainObject( selector ) ) {\n\t\t\topts = selector;\n\t\t\tselector = '';\n\t\t}\n\t\n\t\topts = _selector_opts( opts );\n\t\n\t\tvar inst = this.iterator( 'table', function ( settings ) {\n\t\t\treturn __column_selector( settings, selector, opts );\n\t\t}, 1 );\n\t\n\t\t// Want argument shifting here and in _row_selector?\n\t\tinst.selector.cols = selector;\n\t\tinst.selector.opts = opts;\n\t\n\t\treturn inst;\n\t} );\n\t\n\t_api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn settings.aoColumns[column].nTh;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn settings.aoColumns[column].nTf;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().data()', 'column().data()', function () {\n\t\treturn this.iterator( 'column-rows', __columnData, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn settings.aoColumns[column].mData;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {\n\t\treturn this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {\n\t\t\treturn _pluck_order( settings.aoData, rows,\n\t\t\t\ttype === 'search' ? '_aFilterData' : '_aSortData', column\n\t\t\t);\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {\n\t\treturn this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {\n\t\t\treturn _pluck_order( settings.aoData, rows, 'anCells', column ) ;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {\n\t\tvar that = this;\n\t\tvar ret = this.iterator( 'column', function ( settings, column ) {\n\t\t\tif ( vis === undefined ) {\n\t\t\t\treturn settings.aoColumns[ column ].bVisible;\n\t\t\t} // else\n\t\t\t__setColumnVis( settings, column, vis );\n\t\t} );\n\t\n\t\t// Group the column visibility changes\n\t\tif ( vis !== undefined ) {\n\t\t\tthis.iterator( 'table', function ( settings ) {\n\t\t\t\t// Redraw the header after changes\n\t\t\t\t_fnDrawHead( settings, settings.aoHeader );\n\t\t\t\t_fnDrawHead( settings, settings.aoFooter );\n\t\t\n\t\t\t\t// Update colspan for no records display. Child rows and extensions will use their own\n\t\t\t\t// listeners to do this - only need to update the empty table item here\n\t\t\t\tif ( ! settings.aiDisplay.length ) {\n\t\t\t\t\t$(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));\n\t\t\t\t}\n\t\t\n\t\t\t\t_fnSaveState( settings );\n\t\n\t\t\t\t// Second loop once the first is done for events\n\t\t\t\tthat.iterator( 'column', function ( settings, column ) {\n\t\t\t\t\t_fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );\n\t\t\t\t} );\n\t\n\t\t\t\tif ( calc === undefined || calc ) {\n\t\t\t\t\tthat.columns.adjust();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\n\t\treturn ret;\n\t} );\n\t\n\t_api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn type === 'visible' ?\n\t\t\t\t_fnColumnIndexToVisible( settings, column ) :\n\t\t\t\tcolumn;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_register( 'columns.adjust()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnAdjustColumnSizing( settings );\n\t\t}, 1 );\n\t} );\n\t\n\t_api_register( 'column.index()', function ( type, idx ) {\n\t\tif ( this.context.length !== 0 ) {\n\t\t\tvar ctx = this.context[0];\n\t\n\t\t\tif ( type === 'fromVisible' || type === 'toData' ) {\n\t\t\t\treturn _fnVisibleToColumnIndex( ctx, idx );\n\t\t\t}\n\t\t\telse if ( type === 'fromData' || type === 'toVisible' ) {\n\t\t\t\treturn _fnColumnIndexToVisible( ctx, idx );\n\t\t\t}\n\t\t}\n\t} );\n\t\n\t_api_register( 'column()', function ( selector, opts ) {\n\t\treturn _selector_first( this.columns( selector, opts ) );\n\t} );\n\t\n\tvar __cell_selector = function ( settings, selector, opts )\n\t{\n\t\tvar data = settings.aoData;\n\t\tvar rows = _selector_row_indexes( settings, opts );\n\t\tvar cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );\n\t\tvar allCells = $(_flatten( [], cells ));\n\t\tvar row;\n\t\tvar columns = settings.aoColumns.length;\n\t\tvar a, i, ien, j, o, host;\n\t\n\t\tvar run = function ( s ) {\n\t\t\tvar fnSelector = typeof s === 'function';\n\t\n\t\t\tif ( s === null || s === undefined || fnSelector ) {\n\t\t\t\t// All cells and function selectors\n\t\t\t\ta = [];\n\t\n\t\t\t\tfor ( i=0, ien=rows.length ; i<ien ; i++ ) {\n\t\t\t\t\trow = rows[i];\n\t\n\t\t\t\t\tfor ( j=0 ; j<columns ; j++ ) {\n\t\t\t\t\t\to = {\n\t\t\t\t\t\t\trow: row,\n\t\t\t\t\t\t\tcolumn: j\n\t\t\t\t\t\t};\n\t\n\t\t\t\t\t\tif ( fnSelector ) {\n\t\t\t\t\t\t\t// Selector - function\n\t\t\t\t\t\t\thost = data[ row ];\n\t\n\t\t\t\t\t\t\tif ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {\n\t\t\t\t\t\t\t\ta.push( o );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Selector - all\n\t\t\t\t\t\t\ta.push( o );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\treturn a;\n\t\t\t}\n\t\t\t\n\t\t\t// Selector - index\n\t\t\tif ( $.isPlainObject( s ) ) {\n\t\t\t\t// Valid cell index and its in the array of selectable rows\n\t\t\t\treturn s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?\n\t\t\t\t\t[s] :\n\t\t\t\t\t[];\n\t\t\t}\n\t\n\t\t\t// Selector - jQuery filtered cells\n\t\t\tvar jqResult = allCells\n\t\t\t\t.filter( s )\n\t\t\t\t.map( function (i, el) {\n\t\t\t\t\treturn { // use a new object, in case someone changes the values\n\t\t\t\t\t\trow:    el._DT_CellIndex.row,\n\t\t\t\t\t\tcolumn: el._DT_CellIndex.column\n\t \t\t\t\t};\n\t\t\t\t} )\n\t\t\t\t.toArray();\n\t\n\t\t\tif ( jqResult.length || ! s.nodeName ) {\n\t\t\t\treturn jqResult;\n\t\t\t}\n\t\n\t\t\t// Otherwise the selector is a node, and there is one last option - the\n\t\t\t// element might be a child of an element which has dt-row and dt-column\n\t\t\t// data attributes\n\t\t\thost = $(s).closest('*[data-dt-row]');\n\t\t\treturn host.length ?\n\t\t\t\t[ {\n\t\t\t\t\trow: host.data('dt-row'),\n\t\t\t\t\tcolumn: host.data('dt-column')\n\t\t\t\t} ] :\n\t\t\t\t[];\n\t\t};\n\t\n\t\treturn _selector_run( 'cell', selector, run, settings, opts );\n\t};\n\t\n\t\n\t\n\t\n\t_api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {\n\t\t// Argument shifting\n\t\tif ( $.isPlainObject( rowSelector ) ) {\n\t\t\t// Indexes\n\t\t\tif ( rowSelector.row === undefined ) {\n\t\t\t\t// Selector options in first parameter\n\t\t\t\topts = rowSelector;\n\t\t\t\trowSelector = null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Cell index objects in first parameter\n\t\t\t\topts = columnSelector;\n\t\t\t\tcolumnSelector = null;\n\t\t\t}\n\t\t}\n\t\tif ( $.isPlainObject( columnSelector ) ) {\n\t\t\topts = columnSelector;\n\t\t\tcolumnSelector = null;\n\t\t}\n\t\n\t\t// Cell selector\n\t\tif ( columnSelector === null || columnSelector === undefined ) {\n\t\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t\treturn __cell_selector( settings, rowSelector, _selector_opts( opts ) );\n\t\t\t} );\n\t\t}\n\t\n\t\t// The default built in options need to apply to row and columns\n\t\tvar internalOpts = opts ? {\n\t\t\tpage: opts.page,\n\t\t\torder: opts.order,\n\t\t\tsearch: opts.search\n\t\t} : {};\n\t\n\t\t// Row + column selector\n\t\tvar columns = this.columns( columnSelector, internalOpts );\n\t\tvar rows = this.rows( rowSelector, internalOpts );\n\t\tvar i, ien, j, jen;\n\t\n\t\tvar cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {\n\t\t\tvar a = [];\n\t\n\t\t\tfor ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {\n\t\t\t\tfor ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {\n\t\t\t\t\ta.push( {\n\t\t\t\t\t\trow:    rows[idx][i],\n\t\t\t\t\t\tcolumn: columns[idx][j]\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\treturn a;\n\t\t}, 1 );\n\t\n\t\t// There is currently only one extension which uses a cell selector extension\n\t\t// It is a _major_ performance drag to run this if it isn't needed, so this is\n\t\t// an extension specific check at the moment\n\t\tvar cells = opts && opts.selected ?\n\t\t\tthis.cells( cellsNoOpts, opts ) :\n\t\t\tcellsNoOpts;\n\t\n\t\t$.extend( cells.selector, {\n\t\t\tcols: columnSelector,\n\t\t\trows: rowSelector,\n\t\t\topts: opts\n\t\t} );\n\t\n\t\treturn cells;\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().nodes()', 'cell().node()', function () {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\tvar data = settings.aoData[ row ];\n\t\n\t\t\treturn data && data.anCells ?\n\t\t\t\tdata.anCells[ column ] :\n\t\t\t\tundefined;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_register( 'cells().data()', function () {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn _fnGetCellData( settings, row, column );\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {\n\t\ttype = type === 'search' ? '_aFilterData' : '_aSortData';\n\t\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn settings.aoData[ row ][ type ][ column ];\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn _fnGetCellData( settings, row, column, type );\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().indexes()', 'cell().index()', function () {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn {\n\t\t\t\trow: row,\n\t\t\t\tcolumn: column,\n\t\t\t\tcolumnVisible: _fnColumnIndexToVisible( settings, column )\n\t\t\t};\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\t_fnInvalidate( settings, row, src, column );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t_api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {\n\t\treturn _selector_first( this.cells( rowSelector, columnSelector, opts ) );\n\t} );\n\t\n\t\n\t_api_register( 'cell().data()', function ( data ) {\n\t\tvar ctx = this.context;\n\t\tvar cell = this[0];\n\t\n\t\tif ( data === undefined ) {\n\t\t\t// Get\n\t\t\treturn ctx.length && cell.length ?\n\t\t\t\t_fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// Set\n\t\t_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );\n\t\t_fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Get current ordering (sorting) that has been applied to the table.\n\t *\n\t * @returns {array} 2D array containing the sorting information for the first\n\t *   table in the current context. Each element in the parent array represents\n\t *   a column being sorted upon (i.e. multi-sorting with two columns would have\n\t *   2 inner arrays). The inner arrays may have 2 or 3 elements. The first is\n\t *   the column index that the sorting condition applies to, the second is the\n\t *   direction of the sort (`desc` or `asc`) and, optionally, the third is the\n\t *   index of the sorting order from the `column.sorting` initialisation array.\n\t *//**\n\t * Set the ordering for the table.\n\t *\n\t * @param {integer} order Column index to sort upon.\n\t * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)\n\t * @returns {DataTables.Api} this\n\t *//**\n\t * Set the ordering for the table.\n\t *\n\t * @param {array} order 1D array of sorting information to be applied.\n\t * @param {array} [...] Optional additional sorting conditions\n\t * @returns {DataTables.Api} this\n\t *//**\n\t * Set the ordering for the table.\n\t *\n\t * @param {array} order 2D array of sorting information to be applied.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'order()', function ( order, dir ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( order === undefined ) {\n\t\t\t// get\n\t\t\treturn ctx.length !== 0 ?\n\t\t\t\tctx[0].aaSorting :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// set\n\t\tif ( typeof order === 'number' ) {\n\t\t\t// Simple column / direction passed in\n\t\t\torder = [ [ order, dir ] ];\n\t\t}\n\t\telse if ( order.length && ! Array.isArray( order[0] ) ) {\n\t\t\t// Arguments passed in (list of 1D arrays)\n\t\t\torder = Array.prototype.slice.call( arguments );\n\t\t}\n\t\t// otherwise a 2D array was passed in\n\t\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tsettings.aaSorting = order.slice();\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Attach a sort listener to an element for a given column\n\t *\n\t * @param {node|jQuery|string} node Identifier for the element(s) to attach the\n\t *   listener to. This can take the form of a single DOM node, a jQuery\n\t *   collection of nodes or a jQuery selector which will identify the node(s).\n\t * @param {integer} column the column that a click on this node will sort on\n\t * @param {function} [callback] callback function when sort is run\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'order.listener()', function ( node, column, callback ) {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnSortAttachListener( settings, node, column, callback );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'order.fixed()', function ( set ) {\n\t\tif ( ! set ) {\n\t\t\tvar ctx = this.context;\n\t\t\tvar fixed = ctx.length ?\n\t\t\t\tctx[0].aaSortingFixed :\n\t\t\t\tundefined;\n\t\n\t\t\treturn Array.isArray( fixed ) ?\n\t\t\t\t{ pre: fixed } :\n\t\t\t\tfixed;\n\t\t}\n\t\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tsettings.aaSortingFixed = $.extend( true, {}, set );\n\t\t} );\n\t} );\n\t\n\t\n\t// Order by the selected column(s)\n\t_api_register( [\n\t\t'columns().order()',\n\t\t'column().order()'\n\t], function ( dir ) {\n\t\tvar that = this;\n\t\n\t\treturn this.iterator( 'table', function ( settings, i ) {\n\t\t\tvar sort = [];\n\t\n\t\t\t$.each( that[i], function (j, col) {\n\t\t\t\tsort.push( [ col, dir ] );\n\t\t\t} );\n\t\n\t\t\tsettings.aaSorting = sort;\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t_api_register( 'search()', function ( input, regex, smart, caseInsen ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( input === undefined ) {\n\t\t\t// get\n\t\t\treturn ctx.length !== 0 ?\n\t\t\t\tctx[0].oPreviousSearch.sSearch :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// set\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tif ( ! settings.oFeatures.bFilter ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t_fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {\n\t\t\t\t\"sSearch\": input+\"\",\n\t\t\t\t\"bRegex\":  regex === null ? false : regex,\n\t\t\t\t\"bSmart\":  smart === null ? true  : smart,\n\t\t\t\t\"bCaseInsensitive\": caseInsen === null ? true : caseInsen\n\t\t\t} ), 1 );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_registerPlural(\n\t\t'columns().search()',\n\t\t'column().search()',\n\t\tfunction ( input, regex, smart, caseInsen ) {\n\t\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\t\tvar preSearch = settings.aoPreSearchCols;\n\t\n\t\t\t\tif ( input === undefined ) {\n\t\t\t\t\t// get\n\t\t\t\t\treturn preSearch[ column ].sSearch;\n\t\t\t\t}\n\t\n\t\t\t\t// set\n\t\t\t\tif ( ! settings.oFeatures.bFilter ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\t$.extend( preSearch[ column ], {\n\t\t\t\t\t\"sSearch\": input+\"\",\n\t\t\t\t\t\"bRegex\":  regex === null ? false : regex,\n\t\t\t\t\t\"bSmart\":  smart === null ? true  : smart,\n\t\t\t\t\t\"bCaseInsensitive\": caseInsen === null ? true : caseInsen\n\t\t\t\t} );\n\t\n\t\t\t\t_fnFilterComplete( settings, settings.oPreviousSearch, 1 );\n\t\t\t} );\n\t\t}\n\t);\n\t\n\t/*\n\t * State API methods\n\t */\n\t\n\t_api_register( 'state()', function () {\n\t\treturn this.context.length ?\n\t\t\tthis.context[0].oSavedState :\n\t\t\tnull;\n\t} );\n\t\n\t\n\t_api_register( 'state.clear()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t// Save an empty object\n\t\t\tsettings.fnStateSaveCallback.call( settings.oInstance, settings, {} );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'state.loaded()', function () {\n\t\treturn this.context.length ?\n\t\t\tthis.context[0].oLoadedState :\n\t\t\tnull;\n\t} );\n\t\n\t\n\t_api_register( 'state.save()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnSaveState( settings );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Set the jQuery or window object to be used by DataTables\n\t *\n\t * @param {*} module Library / container object\n\t * @param {string} [type] Library or container type `lib`, `win` or `datetime`.\n\t *   If not provided, automatic detection is attempted.\n\t */\n\tDataTable.use = function (module, type) {\n\t\tif (type === 'lib' || module.fn) {\n\t\t\t$ = module;\n\t\t}\n\t\telse if (type == 'win' || module.document) {\n\t\t\twindow = module;\n\t\t\tdocument = module.document;\n\t\t}\n\t\telse if (type === 'datetime' || module.type === 'DateTime') {\n\t\t\tDataTable.DateTime = module;\n\t\t}\n\t}\n\t\n\t/**\n\t * CommonJS factory function pass through. This will check if the arguments\n\t * given are a window object or a jQuery object. If so they are set\n\t * accordingly.\n\t * @param {*} root Window\n\t * @param {*} jq jQUery\n\t * @returns {boolean} Indicator\n\t */\n\tDataTable.factory = function (root, jq) {\n\t\tvar is = false;\n\t\n\t\t// Test if the first parameter is a window object\n\t\tif (root && root.document) {\n\t\t\twindow = root;\n\t\t\tdocument = root.document;\n\t\t}\n\t\n\t\t// Test if the second parameter is a jQuery object\n\t\tif (jq && jq.fn && jq.fn.jquery) {\n\t\t\t$ = jq;\n\t\t\tis = true;\n\t\t}\n\t\n\t\treturn is;\n\t}\n\t\n\t/**\n\t * Provide a common method for plug-ins to check the version of DataTables being\n\t * used, in order to ensure compatibility.\n\t *\n\t *  @param {string} version Version string to check for, in the format \"X.Y.Z\".\n\t *    Note that the formats \"X\" and \"X.Y\" are also acceptable.\n\t *  @returns {boolean} true if this version of DataTables is greater or equal to\n\t *    the required version, or false if this version of DataTales is not\n\t *    suitable\n\t *  @static\n\t *  @dtopt API-Static\n\t *\n\t *  @example\n\t *    alert( $.fn.dataTable.versionCheck( '1.9.0' ) );\n\t */\n\tDataTable.versionCheck = DataTable.fnVersionCheck = function( version )\n\t{\n\t\tvar aThis = DataTable.version.split('.');\n\t\tvar aThat = version.split('.');\n\t\tvar iThis, iThat;\n\t\n\t\tfor ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {\n\t\t\tiThis = parseInt( aThis[i], 10 ) || 0;\n\t\t\tiThat = parseInt( aThat[i], 10 ) || 0;\n\t\n\t\t\t// Parts are the same, keep comparing\n\t\t\tif (iThis === iThat) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\n\t\t\t// Parts are different, return immediately\n\t\t\treturn iThis > iThat;\n\t\t}\n\t\n\t\treturn true;\n\t};\n\t\n\t\n\t/**\n\t * Check if a `<table>` node is a DataTable table already or not.\n\t *\n\t *  @param {node|jquery|string} table Table node, jQuery object or jQuery\n\t *      selector for the table to test. Note that if more than more than one\n\t *      table is passed on, only the first will be checked\n\t *  @returns {boolean} true the table given is a DataTable, or false otherwise\n\t *  @static\n\t *  @dtopt API-Static\n\t *\n\t *  @example\n\t *    if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {\n\t *      $('#example').dataTable();\n\t *    }\n\t */\n\tDataTable.isDataTable = DataTable.fnIsDataTable = function ( table )\n\t{\n\t\tvar t = $(table).get(0);\n\t\tvar is = false;\n\t\n\t\tif ( table instanceof DataTable.Api ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\t$.each( DataTable.settings, function (i, o) {\n\t\t\tvar head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;\n\t\t\tvar foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;\n\t\n\t\t\tif ( o.nTable === t || head === t || foot === t ) {\n\t\t\t\tis = true;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn is;\n\t};\n\t\n\t\n\t/**\n\t * Get all DataTable tables that have been initialised - optionally you can\n\t * select to get only currently visible tables.\n\t *\n\t *  @param {boolean} [visible=false] Flag to indicate if you want all (default)\n\t *    or visible tables only.\n\t *  @returns {array} Array of `table` nodes (not DataTable instances) which are\n\t *    DataTables\n\t *  @static\n\t *  @dtopt API-Static\n\t *\n\t *  @example\n\t *    $.each( $.fn.dataTable.tables(true), function () {\n\t *      $(table).DataTable().columns.adjust();\n\t *    } );\n\t */\n\tDataTable.tables = DataTable.fnTables = function ( visible )\n\t{\n\t\tvar api = false;\n\t\n\t\tif ( $.isPlainObject( visible ) ) {\n\t\t\tapi = visible.api;\n\t\t\tvisible = visible.visible;\n\t\t}\n\t\n\t\tvar a = $.map( DataTable.settings, function (o) {\n\t\t\tif ( !visible || (visible && $(o.nTable).is(':visible')) ) {\n\t\t\t\treturn o.nTable;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn api ?\n\t\t\tnew _Api( a ) :\n\t\t\ta;\n\t};\n\t\n\t\n\t/**\n\t * Convert from camel case parameters to Hungarian notation. This is made public\n\t * for the extensions to provide the same ability as DataTables core to accept\n\t * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase\n\t * parameters.\n\t *\n\t *  @param {object} src The model object which holds all parameters that can be\n\t *    mapped.\n\t *  @param {object} user The object to convert from camel case to Hungarian.\n\t *  @param {boolean} force When set to `true`, properties which already have a\n\t *    Hungarian value in the `user` object will be overwritten. Otherwise they\n\t *    won't be.\n\t */\n\tDataTable.camelToHungarian = _fnCamelToHungarian;\n\t\n\t\n\t\n\t/**\n\t *\n\t */\n\t_api_register( '$()', function ( selector, opts ) {\n\t\tvar\n\t\t\trows   = this.rows( opts ).nodes(), // Get all rows\n\t\t\tjqRows = $(rows);\n\t\n\t\treturn $( [].concat(\n\t\t\tjqRows.filter( selector ).toArray(),\n\t\t\tjqRows.find( selector ).toArray()\n\t\t) );\n\t} );\n\t\n\t\n\t// jQuery functions to operate on the tables\n\t$.each( [ 'on', 'one', 'off' ], function (i, key) {\n\t\t_api_register( key+'()', function ( /* event, handler */ ) {\n\t\t\tvar args = Array.prototype.slice.call(arguments);\n\t\n\t\t\t// Add the `dt` namespace automatically if it isn't already present\n\t\t\targs[0] = $.map( args[0].split( /\\s/ ), function ( e ) {\n\t\t\t\treturn ! e.match(/\\.dt\\b/) ?\n\t\t\t\t\te+'.dt' :\n\t\t\t\t\te;\n\t\t\t\t} ).join( ' ' );\n\t\n\t\t\tvar inst = $( this.tables().nodes() );\n\t\t\tinst[key].apply( inst, args );\n\t\t\treturn this;\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'clear()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnClearTable( settings );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'settings()', function () {\n\t\treturn new _Api( this.context, this.context );\n\t} );\n\t\n\t\n\t_api_register( 'init()', function () {\n\t\tvar ctx = this.context;\n\t\treturn ctx.length ? ctx[0].oInit : null;\n\t} );\n\t\n\t\n\t_api_register( 'data()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\treturn _pluck( settings.aoData, '_aData' );\n\t\t} ).flatten();\n\t} );\n\t\n\t\n\t_api_register( 'destroy()', function ( remove ) {\n\t\tremove = remove || false;\n\t\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tvar classes   = settings.oClasses;\n\t\t\tvar table     = settings.nTable;\n\t\t\tvar tbody     = settings.nTBody;\n\t\t\tvar thead     = settings.nTHead;\n\t\t\tvar tfoot     = settings.nTFoot;\n\t\t\tvar jqTable   = $(table);\n\t\t\tvar jqTbody   = $(tbody);\n\t\t\tvar jqWrapper = $(settings.nTableWrapper);\n\t\t\tvar rows      = $.map( settings.aoData, function (r) { return r.nTr; } );\n\t\t\tvar i, ien;\n\t\n\t\t\t// Flag to note that the table is currently being destroyed - no action\n\t\t\t// should be taken\n\t\t\tsettings.bDestroying = true;\n\t\n\t\t\t// Fire off the destroy callbacks for plug-ins etc\n\t\t\t_fnCallbackFire( settings, \"aoDestroyCallback\", \"destroy\", [settings] );\n\t\n\t\t\t// If not being removed from the document, make all columns visible\n\t\t\tif ( ! remove ) {\n\t\t\t\tnew _Api( settings ).columns().visible( true );\n\t\t\t}\n\t\n\t\t\t// Blitz all `DT` namespaced events (these are internal events, the\n\t\t\t// lowercase, `dt` events are user subscribed and they are responsible\n\t\t\t// for removing them\n\t\t\tjqWrapper.off('.DT').find(':not(tbody *)').off('.DT');\n\t\t\t$(window).off('.DT-'+settings.sInstance);\n\t\n\t\t\t// When scrolling we had to break the table up - restore it\n\t\t\tif ( table != thead.parentNode ) {\n\t\t\t\tjqTable.children('thead').detach();\n\t\t\t\tjqTable.append( thead );\n\t\t\t}\n\t\n\t\t\tif ( tfoot && table != tfoot.parentNode ) {\n\t\t\t\tjqTable.children('tfoot').detach();\n\t\t\t\tjqTable.append( tfoot );\n\t\t\t}\n\t\n\t\t\tsettings.aaSorting = [];\n\t\t\tsettings.aaSortingFixed = [];\n\t\t\t_fnSortingClasses( settings );\n\t\n\t\t\t$( rows ).removeClass( settings.asStripeClasses.join(' ') );\n\t\n\t\t\t$('th, td', thead).removeClass( classes.sSortable+' '+\n\t\t\t\tclasses.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone\n\t\t\t);\n\t\n\t\t\t// Add the TR elements back into the table in their original order\n\t\t\tjqTbody.children().detach();\n\t\t\tjqTbody.append( rows );\n\t\n\t\t\tvar orig = settings.nTableWrapper.parentNode;\n\t\n\t\t\t// Remove the DataTables generated nodes, events and classes\n\t\t\tvar removedMethod = remove ? 'remove' : 'detach';\n\t\t\tjqTable[ removedMethod ]();\n\t\t\tjqWrapper[ removedMethod ]();\n\t\n\t\t\t// If we need to reattach the table to the document\n\t\t\tif ( ! remove && orig ) {\n\t\t\t\t// insertBefore acts like appendChild if !arg[1]\n\t\t\t\torig.insertBefore( table, settings.nTableReinsertBefore );\n\t\n\t\t\t\t// Restore the width of the original table - was read from the style property,\n\t\t\t\t// so we can restore directly to that\n\t\t\t\tjqTable\n\t\t\t\t\t.css( 'width', settings.sDestroyWidth )\n\t\t\t\t\t.removeClass( classes.sTable );\n\t\n\t\t\t\t// If the were originally stripe classes - then we add them back here.\n\t\t\t\t// Note this is not fool proof (for example if not all rows had stripe\n\t\t\t\t// classes - but it's a good effort without getting carried away\n\t\t\t\tien = settings.asDestroyStripes.length;\n\t\n\t\t\t\tif ( ien ) {\n\t\t\t\t\tjqTbody.children().each( function (i) {\n\t\t\t\t\t\t$(this).addClass( settings.asDestroyStripes[i % ien] );\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t/* Remove the settings object from the settings array */\n\t\t\tvar idx = $.inArray( settings, DataTable.settings );\n\t\t\tif ( idx !== -1 ) {\n\t\t\t\tDataTable.settings.splice( idx, 1 );\n\t\t\t}\n\t\t} );\n\t} );\n\t\n\t\n\t// Add the `every()` method for rows, columns and cells in a compact form\n\t$.each( [ 'column', 'row', 'cell' ], function ( i, type ) {\n\t\t_api_register( type+'s().every()', function ( fn ) {\n\t\t\tvar opts = this.selector.opts;\n\t\t\tvar api = this;\n\t\n\t\t\treturn this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {\n\t\t\t\t// Rows and columns:\n\t\t\t\t//  arg1 - index\n\t\t\t\t//  arg2 - table counter\n\t\t\t\t//  arg3 - loop counter\n\t\t\t\t//  arg4 - undefined\n\t\t\t\t// Cells:\n\t\t\t\t//  arg1 - row index\n\t\t\t\t//  arg2 - column index\n\t\t\t\t//  arg3 - table counter\n\t\t\t\t//  arg4 - loop counter\n\t\t\t\tfn.call(\n\t\t\t\t\tapi[ type ](\n\t\t\t\t\t\targ1,\n\t\t\t\t\t\ttype==='cell' ? arg2 : opts,\n\t\t\t\t\t\ttype==='cell' ? opts : undefined\n\t\t\t\t\t),\n\t\t\t\t\targ1, arg2, arg3, arg4\n\t\t\t\t);\n\t\t\t} );\n\t\t} );\n\t} );\n\t\n\t\n\t// i18n method for extensions to be able to use the language object from the\n\t// DataTable\n\t_api_register( 'i18n()', function ( token, def, plural ) {\n\t\tvar ctx = this.context[0];\n\t\tvar resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );\n\t\n\t\tif ( resolved === undefined ) {\n\t\t\tresolved = def;\n\t\t}\n\t\n\t\tif ( plural !== undefined && $.isPlainObject( resolved ) ) {\n\t\t\tresolved = resolved[ plural ] !== undefined ?\n\t\t\t\tresolved[ plural ] :\n\t\t\t\tresolved._;\n\t\t}\n\t\n\t\treturn typeof resolved === 'string'\n\t\t\t? resolved.replace( '%d', plural ) // nb: plural might be undefined,\n\t\t\t: resolved;\n\t} );\t\n\t/**\n\t * Version string for plug-ins to check compatibility. Allowed format is\n\t * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used\n\t * only for non-release builds. See http://semver.org/ for more information.\n\t *  @member\n\t *  @type string\n\t *  @default Version number\n\t */\n\tDataTable.version = \"1.13.5\";\n\t\n\t/**\n\t * Private data store, containing all of the settings objects that are\n\t * created for the tables on a given page.\n\t *\n\t * Note that the `DataTable.settings` object is aliased to\n\t * `jQuery.fn.dataTableExt` through which it may be accessed and\n\t * manipulated, or `jQuery.fn.dataTable.settings`.\n\t *  @member\n\t *  @type array\n\t *  @default []\n\t *  @private\n\t */\n\tDataTable.settings = [];\n\t\n\t/**\n\t * Object models container, for the various models that DataTables has\n\t * available to it. These models define the objects that are used to hold\n\t * the active state and configuration of the table.\n\t *  @namespace\n\t */\n\tDataTable.models = {};\n\t\n\t\n\t\n\t/**\n\t * Template object for the way in which DataTables holds information about\n\t * search information for the global filter and individual column filters.\n\t *  @namespace\n\t */\n\tDataTable.models.oSearch = {\n\t\t/**\n\t\t * Flag to indicate if the filtering should be case insensitive or not\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t */\n\t\t\"bCaseInsensitive\": true,\n\t\n\t\t/**\n\t\t * Applied search term\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t */\n\t\t\"sSearch\": \"\",\n\t\n\t\t/**\n\t\t * Flag to indicate if the search term should be interpreted as a\n\t\t * regular expression (true) or not (false) and therefore and special\n\t\t * regex characters escaped.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bRegex\": false,\n\t\n\t\t/**\n\t\t * Flag to indicate if DataTables is to use its smart filtering or not.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t */\n\t\t\"bSmart\": true,\n\t\n\t\t/**\n\t\t * Flag to indicate if DataTables should only trigger a search when\n\t\t * the return key is pressed.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"return\": false\n\t};\n\t\n\t\n\t\n\t\n\t/**\n\t * Template object for the way in which DataTables holds information about\n\t * each individual row. This is the object format used for the settings\n\t * aoData array.\n\t *  @namespace\n\t */\n\tDataTable.models.oRow = {\n\t\t/**\n\t\t * TR element for the row\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTr\": null,\n\t\n\t\t/**\n\t\t * Array of TD elements for each row. This is null until the row has been\n\t\t * created.\n\t\t *  @type array nodes\n\t\t *  @default []\n\t\t */\n\t\t\"anCells\": null,\n\t\n\t\t/**\n\t\t * Data object from the original data source for the row. This is either\n\t\t * an array if using the traditional form of DataTables, or an object if\n\t\t * using mData options. The exact type will depend on the passed in\n\t\t * data from the data source, or will be an array if using DOM a data\n\t\t * source.\n\t\t *  @type array|object\n\t\t *  @default []\n\t\t */\n\t\t\"_aData\": [],\n\t\n\t\t/**\n\t\t * Sorting data cache - this array is ostensibly the same length as the\n\t\t * number of columns (although each index is generated only as it is\n\t\t * needed), and holds the data that is used for sorting each column in the\n\t\t * row. We do this cache generation at the start of the sort in order that\n\t\t * the formatting of the sort data need be done only once for each cell\n\t\t * per sort. This array should not be read from or written to by anything\n\t\t * other than the master sorting methods.\n\t\t *  @type array\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_aSortData\": null,\n\t\n\t\t/**\n\t\t * Per cell filtering data cache. As per the sort data cache, used to\n\t\t * increase the performance of the filtering in DataTables\n\t\t *  @type array\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_aFilterData\": null,\n\t\n\t\t/**\n\t\t * Filtering data cache. This is the same as the cell filtering cache, but\n\t\t * in this case a string rather than an array. This is easily computed with\n\t\t * a join on `_aFilterData`, but is provided as a cache so the join isn't\n\t\t * needed on every search (memory traded for performance)\n\t\t *  @type array\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_sFilterRow\": null,\n\t\n\t\t/**\n\t\t * Cache of the class name that DataTables has applied to the row, so we\n\t\t * can quickly look at this variable rather than needing to do a DOM check\n\t\t * on className for the nTr property.\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t *  @private\n\t\t */\n\t\t\"_sRowStripe\": \"\",\n\t\n\t\t/**\n\t\t * Denote if the original data source was from the DOM, or the data source\n\t\t * object. This is used for invalidating data, so DataTables can\n\t\t * automatically read data from the original source, unless uninstructed\n\t\t * otherwise.\n\t\t *  @type string\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"src\": null,\n\t\n\t\t/**\n\t\t * Index in the aoData array. This saves an indexOf lookup when we have the\n\t\t * object, but want to know the index\n\t\t *  @type integer\n\t\t *  @default -1\n\t\t *  @private\n\t\t */\n\t\t\"idx\": -1\n\t};\n\t\n\t\n\t/**\n\t * Template object for the column information object in DataTables. This object\n\t * is held in the settings aoColumns array and contains all the information that\n\t * DataTables needs about each individual column.\n\t *\n\t * Note that this object is related to {@link DataTable.defaults.column}\n\t * but this one is the internal data store for DataTables's cache of columns.\n\t * It should NOT be manipulated outside of DataTables. Any configuration should\n\t * be done through the initialisation options.\n\t *  @namespace\n\t */\n\tDataTable.models.oColumn = {\n\t\t/**\n\t\t * Column index. This could be worked out on-the-fly with $.inArray, but it\n\t\t * is faster to just hold it as a variable\n\t\t *  @type integer\n\t\t *  @default null\n\t\t */\n\t\t\"idx\": null,\n\t\n\t\t/**\n\t\t * A list of the columns that sorting should occur on when this column\n\t\t * is sorted. That this property is an array allows multi-column sorting\n\t\t * to be defined for a column (for example first name / last name columns\n\t\t * would benefit from this). The values are integers pointing to the\n\t\t * columns to be sorted on (typically it will be a single integer pointing\n\t\t * at itself, but that doesn't need to be the case).\n\t\t *  @type array\n\t\t */\n\t\t\"aDataSort\": null,\n\t\n\t\t/**\n\t\t * Define the sorting directions that are applied to the column, in sequence\n\t\t * as the column is repeatedly sorted upon - i.e. the first value is used\n\t\t * as the sorting direction when the column if first sorted (clicked on).\n\t\t * Sort it again (click again) and it will move on to the next index.\n\t\t * Repeat until loop.\n\t\t *  @type array\n\t\t */\n\t\t\"asSorting\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if the column is searchable, and thus should be included\n\t\t * in the filtering or not.\n\t\t *  @type boolean\n\t\t */\n\t\t\"bSearchable\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if the column is sortable or not.\n\t\t *  @type boolean\n\t\t */\n\t\t\"bSortable\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if the column is currently visible in the table or not\n\t\t *  @type boolean\n\t\t */\n\t\t\"bVisible\": null,\n\t\n\t\t/**\n\t\t * Store for manual type assignment using the `column.type` option. This\n\t\t * is held in store so we can manipulate the column's `sType` property.\n\t\t *  @type string\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_sManualType\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if HTML5 data attributes should be used as the data\n\t\t * source for filtering or sorting. True is either are.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *  @private\n\t\t */\n\t\t\"_bAttrSrc\": false,\n\t\n\t\t/**\n\t\t * Developer definable function that is called whenever a cell is created (Ajax source,\n\t\t * etc) or processed for input (DOM source). This can be used as a compliment to mRender\n\t\t * allowing you to modify the DOM element (add background colour for example) when the\n\t\t * element is available.\n\t\t *  @type function\n\t\t *  @param {element} nTd The TD node that has been created\n\t\t *  @param {*} sData The Data for the cell\n\t\t *  @param {array|object} oData The data for the whole row\n\t\t *  @param {int} iRow The row index for the aoData data store\n\t\t *  @default null\n\t\t */\n\t\t\"fnCreatedCell\": null,\n\t\n\t\t/**\n\t\t * Function to get data from a cell in a column. You should <b>never</b>\n\t\t * access data directly through _aData internally in DataTables - always use\n\t\t * the method attached to this property. It allows mData to function as\n\t\t * required. This function is automatically assigned by the column\n\t\t * initialisation method\n\t\t *  @type function\n\t\t *  @param {array|object} oData The data array/object for the array\n\t\t *    (i.e. aoData[]._aData)\n\t\t *  @param {string} sSpecific The specific data type you want to get -\n\t\t *    'display', 'type' 'filter' 'sort'\n\t\t *  @returns {*} The data for the cell from the given row's data\n\t\t *  @default null\n\t\t */\n\t\t\"fnGetData\": null,\n\t\n\t\t/**\n\t\t * Function to set data for a cell in the column. You should <b>never</b>\n\t\t * set the data directly to _aData internally in DataTables - always use\n\t\t * this method. It allows mData to function as required. This function\n\t\t * is automatically assigned by the column initialisation method\n\t\t *  @type function\n\t\t *  @param {array|object} oData The data array/object for the array\n\t\t *    (i.e. aoData[]._aData)\n\t\t *  @param {*} sValue Value to set\n\t\t *  @default null\n\t\t */\n\t\t\"fnSetData\": null,\n\t\n\t\t/**\n\t\t * Property to read the value for the cells in the column from the data\n\t\t * source array / object. If null, then the default content is used, if a\n\t\t * function is given then the return from the function is used.\n\t\t *  @type function|int|string|null\n\t\t *  @default null\n\t\t */\n\t\t\"mData\": null,\n\t\n\t\t/**\n\t\t * Partner property to mData which is used (only when defined) to get\n\t\t * the data - i.e. it is basically the same as mData, but without the\n\t\t * 'set' option, and also the data fed to it is the result from mData.\n\t\t * This is the rendering method to match the data method of mData.\n\t\t *  @type function|int|string|null\n\t\t *  @default null\n\t\t */\n\t\t\"mRender\": null,\n\t\n\t\t/**\n\t\t * Unique header TH/TD element for this column - this is what the sorting\n\t\t * listener is attached to (if sorting is enabled.)\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTh\": null,\n\t\n\t\t/**\n\t\t * Unique footer TH/TD element for this column (if there is one). Not used\n\t\t * in DataTables as such, but can be used for plug-ins to reference the\n\t\t * footer for each column.\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTf\": null,\n\t\n\t\t/**\n\t\t * The class to apply to all TD elements in the table's TBODY for the column\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sClass\": null,\n\t\n\t\t/**\n\t\t * When DataTables calculates the column widths to assign to each column,\n\t\t * it finds the longest string in each column and then constructs a\n\t\t * temporary table and reads the widths from that. The problem with this\n\t\t * is that \"mmm\" is much wider then \"iiii\", but the latter is a longer\n\t\t * string - thus the calculation can go wrong (doing it properly and putting\n\t\t * it into an DOM object and measuring that is horribly(!) slow). Thus as\n\t\t * a \"work around\" we provide this option. It will append its value to the\n\t\t * text that is found to be the longest string for the column - i.e. padding.\n\t\t *  @type string\n\t\t */\n\t\t\"sContentPadding\": null,\n\t\n\t\t/**\n\t\t * Allows a default value to be given for a column's data, and will be used\n\t\t * whenever a null data source is encountered (this can be because mData\n\t\t * is set to null, or because the data source itself is null).\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sDefaultContent\": null,\n\t\n\t\t/**\n\t\t * Name for the column, allowing reference to the column by name as well as\n\t\t * by index (needs a lookup to work by name).\n\t\t *  @type string\n\t\t */\n\t\t\"sName\": null,\n\t\n\t\t/**\n\t\t * Custom sorting data type - defines which of the available plug-ins in\n\t\t * afnSortData the custom sorting will use - if any is defined.\n\t\t *  @type string\n\t\t *  @default std\n\t\t */\n\t\t\"sSortDataType\": 'std',\n\t\n\t\t/**\n\t\t * Class to be applied to the header element when sorting on this column\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sSortingClass\": null,\n\t\n\t\t/**\n\t\t * Class to be applied to the header element when sorting on this column -\n\t\t * when jQuery UI theming is used.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sSortingClassJUI\": null,\n\t\n\t\t/**\n\t\t * Title of the column - what is seen in the TH element (nTh).\n\t\t *  @type string\n\t\t */\n\t\t\"sTitle\": null,\n\t\n\t\t/**\n\t\t * Column sorting and filtering type\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sType\": null,\n\t\n\t\t/**\n\t\t * Width of the column\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sWidth\": null,\n\t\n\t\t/**\n\t\t * Width of the column when it was first \"encountered\"\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sWidthOrig\": null\n\t};\n\t\n\t\n\t/*\n\t * Developer note: The properties of the object below are given in Hungarian\n\t * notation, that was used as the interface for DataTables prior to v1.10, however\n\t * from v1.10 onwards the primary interface is camel case. In order to avoid\n\t * breaking backwards compatibility utterly with this change, the Hungarian\n\t * version is still, internally the primary interface, but is is not documented\n\t * - hence the @name tags in each doc comment. This allows a Javascript function\n\t * to create a map from Hungarian notation to camel case (going the other direction\n\t * would require each property to be listed, which would add around 3K to the size\n\t * of DataTables, while this method is about a 0.5K hit).\n\t *\n\t * Ultimately this does pave the way for Hungarian notation to be dropped\n\t * completely, but that is a massive amount of work and will break current\n\t * installs (therefore is on-hold until v2).\n\t */\n\t\n\t/**\n\t * Initialisation options that can be given to DataTables at initialisation\n\t * time.\n\t *  @namespace\n\t */\n\tDataTable.defaults = {\n\t\t/**\n\t\t * An array of data to use for the table, passed in at initialisation which\n\t\t * will be used in preference to any data which is already in the DOM. This is\n\t\t * particularly useful for constructing tables purely in Javascript, for\n\t\t * example with a custom Ajax call.\n\t\t *  @type array\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.data\n\t\t *\n\t\t *  @example\n\t\t *    // Using a 2D array data source\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"data\": [\n\t\t *          ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],\n\t\t *          ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],\n\t\t *        ],\n\t\t *        \"columns\": [\n\t\t *          { \"title\": \"Engine\" },\n\t\t *          { \"title\": \"Browser\" },\n\t\t *          { \"title\": \"Platform\" },\n\t\t *          { \"title\": \"Version\" },\n\t\t *          { \"title\": \"Grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using an array of objects as a data source (`data`)\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"data\": [\n\t\t *          {\n\t\t *            \"engine\":   \"Trident\",\n\t\t *            \"browser\":  \"Internet Explorer 4.0\",\n\t\t *            \"platform\": \"Win 95+\",\n\t\t *            \"version\":  4,\n\t\t *            \"grade\":    \"X\"\n\t\t *          },\n\t\t *          {\n\t\t *            \"engine\":   \"Trident\",\n\t\t *            \"browser\":  \"Internet Explorer 5.0\",\n\t\t *            \"platform\": \"Win 95+\",\n\t\t *            \"version\":  5,\n\t\t *            \"grade\":    \"C\"\n\t\t *          }\n\t\t *        ],\n\t\t *        \"columns\": [\n\t\t *          { \"title\": \"Engine\",   \"data\": \"engine\" },\n\t\t *          { \"title\": \"Browser\",  \"data\": \"browser\" },\n\t\t *          { \"title\": \"Platform\", \"data\": \"platform\" },\n\t\t *          { \"title\": \"Version\",  \"data\": \"version\" },\n\t\t *          { \"title\": \"Grade\",    \"data\": \"grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aaData\": null,\n\t\n\t\n\t\t/**\n\t\t * If ordering is enabled, then DataTables will perform a first pass sort on\n\t\t * initialisation. You can define which column(s) the sort is performed\n\t\t * upon, and the sorting direction, with this variable. The `sorting` array\n\t\t * should contain an array for each column to be sorted initially containing\n\t\t * the column's index and a direction string ('asc' or 'desc').\n\t\t *  @type array\n\t\t *  @default [[0,'asc']]\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.order\n\t\t *\n\t\t *  @example\n\t\t *    // Sort by 3rd column first, and then 4th column\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"order\": [[2,'asc'], [3,'desc']]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *    // No initial sorting\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"order\": []\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aaSorting\": [[0,'asc']],\n\t\n\t\n\t\t/**\n\t\t * This parameter is basically identical to the `sorting` parameter, but\n\t\t * cannot be overridden by user interaction with the table. What this means\n\t\t * is that you could have a column (visible or hidden) which the sorting\n\t\t * will always be forced on first - any sorting after that (from the user)\n\t\t * will then be performed as required. This can be useful for grouping rows\n\t\t * together.\n\t\t *  @type array\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.orderFixed\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderFixed\": [[0,'asc']]\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"aaSortingFixed\": [],\n\t\n\t\n\t\t/**\n\t\t * DataTables can be instructed to load data to display in the table from a\n\t\t * Ajax source. This option defines how that Ajax call is made and where to.\n\t\t *\n\t\t * The `ajax` property has three different modes of operation, depending on\n\t\t * how it is defined. These are:\n\t\t *\n\t\t * * `string` - Set the URL from where the data should be loaded from.\n\t\t * * `object` - Define properties for `jQuery.ajax`.\n\t\t * * `function` - Custom data get function\n\t\t *\n\t\t * `string`\n\t\t * --------\n\t\t *\n\t\t * As a string, the `ajax` property simply defines the URL from which\n\t\t * DataTables will load data.\n\t\t *\n\t\t * `object`\n\t\t * --------\n\t\t *\n\t\t * As an object, the parameters in the object are passed to\n\t\t * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control\n\t\t * of the Ajax request. DataTables has a number of default parameters which\n\t\t * you can override using this option. Please refer to the jQuery\n\t\t * documentation for a full description of the options available, although\n\t\t * the following parameters provide additional options in DataTables or\n\t\t * require special consideration:\n\t\t *\n\t\t * * `data` - As with jQuery, `data` can be provided as an object, but it\n\t\t *   can also be used as a function to manipulate the data DataTables sends\n\t\t *   to the server. The function takes a single parameter, an object of\n\t\t *   parameters with the values that DataTables has readied for sending. An\n\t\t *   object may be returned which will be merged into the DataTables\n\t\t *   defaults, or you can add the items to the object that was passed in and\n\t\t *   not return anything from the function. This supersedes `fnServerParams`\n\t\t *   from DataTables 1.9-.\n\t\t *\n\t\t * * `dataSrc` - By default DataTables will look for the property `data` (or\n\t\t *   `aaData` for compatibility with DataTables 1.9-) when obtaining data\n\t\t *   from an Ajax source or for server-side processing - this parameter\n\t\t *   allows that property to be changed. You can use Javascript dotted\n\t\t *   object notation to get a data source for multiple levels of nesting, or\n\t\t *   it my be used as a function. As a function it takes a single parameter,\n\t\t *   the JSON returned from the server, which can be manipulated as\n\t\t *   required, with the returned value being that used by DataTables as the\n\t\t *   data source for the table. This supersedes `sAjaxDataProp` from\n\t\t *   DataTables 1.9-.\n\t\t *\n\t\t * * `success` - Should not be overridden it is used internally in\n\t\t *   DataTables. To manipulate / transform the data returned by the server\n\t\t *   use `ajax.dataSrc`, or use `ajax` as a function (see below).\n\t\t *\n\t\t * `function`\n\t\t * ----------\n\t\t *\n\t\t * As a function, making the Ajax call is left up to yourself allowing\n\t\t * complete control of the Ajax request. Indeed, if desired, a method other\n\t\t * than Ajax could be used to obtain the required data, such as Web storage\n\t\t * or an AIR database.\n\t\t *\n\t\t * The function is given four parameters and no return is required. The\n\t\t * parameters are:\n\t\t *\n\t\t * 1. _object_ - Data to send to the server\n\t\t * 2. _function_ - Callback function that must be executed when the required\n\t\t *    data has been obtained. That data should be passed into the callback\n\t\t *    as the only parameter\n\t\t * 3. _object_ - DataTables settings object for the table\n\t\t *\n\t\t * Note that this supersedes `fnServerData` from DataTables 1.9-.\n\t\t *\n\t\t *  @type string|object|function\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.ajax\n\t\t *  @since 1.10.0\n\t\t *\n\t\t * @example\n\t\t *   // Get JSON data from a file via Ajax.\n\t\t *   // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": \"data.json\"\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Get JSON data from a file via Ajax, using `dataSrc` to change\n\t\t *   // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"dataSrc\": \"tableData\"\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Get JSON data from a file via Ajax, using `dataSrc` to read data\n\t\t *   // from a plain array rather than an array in an object\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"dataSrc\": \"\"\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Manipulate the data returned from the server - add a link to data\n\t\t *   // (note this can, should, be done using `render` for the column - this\n\t\t *   // is just a simple example of how the data can be manipulated).\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"dataSrc\": function ( json ) {\n\t\t *         for ( var i=0, ien=json.length ; i<ien ; i++ ) {\n\t\t *           json[i][0] = '<a href=\"/message/'+json[i][0]+'>View message</a>';\n\t\t *         }\n\t\t *         return json;\n\t\t *       }\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Add data to the request\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"data\": function ( d ) {\n\t\t *         return {\n\t\t *           \"extra_search\": $('#extra').val()\n\t\t *         };\n\t\t *       }\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Send request as POST\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"type\": \"POST\"\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Get the data from localStorage (could interface with a form for\n\t\t *   // adding, editing and removing rows).\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": function (data, callback, settings) {\n\t\t *       callback(\n\t\t *         JSON.parse( localStorage.getItem('dataTablesData') )\n\t\t *       );\n\t\t *     }\n\t\t *   } );\n\t\t */\n\t\t\"ajax\": null,\n\t\n\t\n\t\t/**\n\t\t * This parameter allows you to readily specify the entries in the length drop\n\t\t * down menu that DataTables shows when pagination is enabled. It can be\n\t\t * either a 1D array of options which will be used for both the displayed\n\t\t * option and the value, or a 2D array which will use the array in the first\n\t\t * position as the value, and the array in the second position as the\n\t\t * displayed options (useful for language strings such as 'All').\n\t\t *\n\t\t * Note that the `pageLength` property will be automatically set to the\n\t\t * first value given in this array, unless `pageLength` is also provided.\n\t\t *  @type array\n\t\t *  @default [ 10, 25, 50, 100 ]\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.lengthMenu\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"lengthMenu\": [[10, 25, 50, -1], [10, 25, 50, \"All\"]]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aLengthMenu\": [ 10, 25, 50, 100 ],\n\t\n\t\n\t\t/**\n\t\t * The `columns` option in the initialisation parameter allows you to define\n\t\t * details about the way individual columns behave. For a full list of\n\t\t * column options that can be set, please see\n\t\t * {@link DataTable.defaults.column}. Note that if you use `columns` to\n\t\t * define your columns, you must have an entry in the array for every single\n\t\t * column that you have in your table (these can be null if you don't which\n\t\t * to specify any options).\n\t\t *  @member\n\t\t *\n\t\t *  @name DataTable.defaults.column\n\t\t */\n\t\t\"aoColumns\": null,\n\t\n\t\t/**\n\t\t * Very similar to `columns`, `columnDefs` allows you to target a specific\n\t\t * column, multiple columns, or all columns, using the `targets` property of\n\t\t * each object in the array. This allows great flexibility when creating\n\t\t * tables, as the `columnDefs` arrays can be of any length, targeting the\n\t\t * columns you specifically want. `columnDefs` may use any of the column\n\t\t * options available: {@link DataTable.defaults.column}, but it _must_\n\t\t * have `targets` defined in each object in the array. Values in the `targets`\n\t\t * array may be:\n\t\t *   <ul>\n\t\t *     <li>a string - class name will be matched on the TH for the column</li>\n\t\t *     <li>0 or a positive integer - column index counting from the left</li>\n\t\t *     <li>a negative integer - column index counting from the right</li>\n\t\t *     <li>the string \"_all\" - all columns (i.e. assign a default)</li>\n\t\t *   </ul>\n\t\t *  @member\n\t\t *\n\t\t *  @name DataTable.defaults.columnDefs\n\t\t */\n\t\t\"aoColumnDefs\": null,\n\t\n\t\n\t\t/**\n\t\t * Basically the same as `search`, this parameter defines the individual column\n\t\t * filtering state at initialisation time. The array must be of the same size\n\t\t * as the number of columns, and each element be an object with the parameters\n\t\t * `search` and `escapeRegex` (the latter is optional). 'null' is also\n\t\t * accepted and the default will be used.\n\t\t *  @type array\n\t\t *  @default []\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.searchCols\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"searchCols\": [\n\t\t *          null,\n\t\t *          { \"search\": \"My filter\" },\n\t\t *          null,\n\t\t *          { \"search\": \"^[0-9]\", \"escapeRegex\": false }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"aoSearchCols\": [],\n\t\n\t\n\t\t/**\n\t\t * An array of CSS classes that should be applied to displayed rows. This\n\t\t * array may be of any length, and DataTables will apply each class\n\t\t * sequentially, looping when required.\n\t\t *  @type array\n\t\t *  @default null <i>Will take the values determined by the `oClasses.stripe*`\n\t\t *    options</i>\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.stripeClasses\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stripeClasses\": [ 'strip1', 'strip2', 'strip3' ]\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"asStripeClasses\": null,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable automatic column width calculation. This can be disabled\n\t\t * as an optimisation (it takes some time to calculate the widths) if the\n\t\t * tables widths are passed in using `columns`.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.autoWidth\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"autoWidth\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bAutoWidth\": true,\n\t\n\t\n\t\t/**\n\t\t * Deferred rendering can provide DataTables with a huge speed boost when you\n\t\t * are using an Ajax or JS data source for the table. This option, when set to\n\t\t * true, will cause DataTables to defer the creation of the table elements for\n\t\t * each row until they are needed for a draw - saving a significant amount of\n\t\t * time.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.deferRender\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajax\": \"sources/arrays.txt\",\n\t\t *        \"deferRender\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bDeferRender\": false,\n\t\n\t\n\t\t/**\n\t\t * Replace a DataTable which matches the given selector and replace it with\n\t\t * one which has the properties of the new initialisation object passed. If no\n\t\t * table matches the selector, then the new DataTable will be constructed as\n\t\t * per normal.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.destroy\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"srollY\": \"200px\",\n\t\t *        \"paginate\": false\n\t\t *      } );\n\t\t *\n\t\t *      // Some time later....\n\t\t *      $('#example').dataTable( {\n\t\t *        \"filter\": false,\n\t\t *        \"destroy\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bDestroy\": false,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable filtering of data. Filtering in DataTables is \"smart\" in\n\t\t * that it allows the end user to input multiple words (space separated) and\n\t\t * will match a row containing those words, even if not in the order that was\n\t\t * specified (this allow matching across multiple columns). Note that if you\n\t\t * wish to use filtering in DataTables this must remain 'true' - to remove the\n\t\t * default filtering input box and retain filtering abilities, please use\n\t\t * {@link DataTable.defaults.dom}.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.searching\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"searching\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bFilter\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the table information display. This shows information\n\t\t * about the data that is currently visible on the page, including information\n\t\t * about filtered data if that action is being performed.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.info\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"info\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bInfo\": true,\n\t\n\t\n\t\t/**\n\t\t * Allows the end user to select the size of a formatted page from a select\n\t\t * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.lengthChange\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"lengthChange\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bLengthChange\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable pagination.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.paging\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"paging\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bPaginate\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the display of a 'processing' indicator when the table is\n\t\t * being processed (e.g. a sort). This is particularly useful for tables with\n\t\t * large amounts of data where it can take a noticeable amount of time to sort\n\t\t * the entries.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.processing\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"processing\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bProcessing\": false,\n\t\n\t\n\t\t/**\n\t\t * Retrieve the DataTables object for the given selector. Note that if the\n\t\t * table has already been initialised, this parameter will cause DataTables\n\t\t * to simply return the object that has already been set up - it will not take\n\t\t * account of any changes you might have made to the initialisation object\n\t\t * passed to DataTables (setting this parameter to true is an acknowledgement\n\t\t * that you understand this). `destroy` can be used to reinitialise a table if\n\t\t * you need.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.retrieve\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      initTable();\n\t\t *      tableActions();\n\t\t *    } );\n\t\t *\n\t\t *    function initTable ()\n\t\t *    {\n\t\t *      return $('#example').dataTable( {\n\t\t *        \"scrollY\": \"200px\",\n\t\t *        \"paginate\": false,\n\t\t *        \"retrieve\": true\n\t\t *      } );\n\t\t *    }\n\t\t *\n\t\t *    function tableActions ()\n\t\t *    {\n\t\t *      var table = initTable();\n\t\t *      // perform API operations with oTable\n\t\t *    }\n\t\t */\n\t\t\"bRetrieve\": false,\n\t\n\t\n\t\t/**\n\t\t * When vertical (y) scrolling is enabled, DataTables will force the height of\n\t\t * the table's viewport to the given height at all times (useful for layout).\n\t\t * However, this can look odd when filtering data down to a small data set,\n\t\t * and the footer is left \"floating\" further down. This parameter (when\n\t\t * enabled) will cause DataTables to collapse the table's viewport down when\n\t\t * the result set will fit within the given Y height.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.scrollCollapse\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollY\": \"200\",\n\t\t *        \"scrollCollapse\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bScrollCollapse\": false,\n\t\n\t\n\t\t/**\n\t\t * Configure DataTables to use server-side processing. Note that the\n\t\t * `ajax` parameter must also be given in order to give DataTables a\n\t\t * source to obtain the required data for each draw.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverSide\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"serverSide\": true,\n\t\t *        \"ajax\": \"xhr.php\"\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bServerSide\": false,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable sorting of columns. Sorting of individual columns can be\n\t\t * disabled by the `sortable` option for each column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.ordering\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ordering\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSort\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or display DataTables' ability to sort multiple columns at the\n\t\t * same time (activated by shift-click by the user).\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.orderMulti\n\t\t *\n\t\t *  @example\n\t\t *    // Disable multiple column sorting ability\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderMulti\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortMulti\": true,\n\t\n\t\n\t\t/**\n\t\t * Allows control over whether DataTables should use the top (true) unique\n\t\t * cell that is found for a single column, or the bottom (false - default).\n\t\t * This is useful when using complex headers.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.orderCellsTop\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderCellsTop\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortCellsTop\": false,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the addition of the classes `sorting\\_1`, `sorting\\_2` and\n\t\t * `sorting\\_3` to the columns which are currently being sorted on. This is\n\t\t * presented as a feature switch as it can increase processing time (while\n\t\t * classes are removed and added) so for large data sets you might want to\n\t\t * turn this off.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.orderClasses\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderClasses\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortClasses\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable state saving. When enabled HTML5 `localStorage` will be\n\t\t * used to save table display information such as pagination information,\n\t\t * display length, filtering and sorting. As such when the end user reloads\n\t\t * the page the display display will match what thy had previously set up.\n\t\t *\n\t\t * Due to the use of `localStorage` the default state saving is not supported\n\t\t * in IE6 or 7. If state saving is required in those browsers, use\n\t\t * `stateSaveCallback` to provide a storage solution such as cookies.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.stateSave\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bStateSave\": false,\n\t\n\t\n\t\t/**\n\t\t * This function is called when a TR element is created (and all TD child\n\t\t * elements have been inserted), or registered if using a DOM source, allowing\n\t\t * manipulation of the TR element (adding classes etc).\n\t\t *  @type function\n\t\t *  @param {node} row \"TR\" element for the current row\n\t\t *  @param {array} data Raw data array for this row\n\t\t *  @param {int} dataIndex The index of this row in the internal aoData array\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.createdRow\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"createdRow\": function( row, data, dataIndex ) {\n\t\t *          // Bold the grade for all 'A' grade browsers\n\t\t *          if ( data[4] == \"A\" )\n\t\t *          {\n\t\t *            $('td:eq(4)', row).html( '<b>A</b>' );\n\t\t *          }\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnCreatedRow\": null,\n\t\n\t\n\t\t/**\n\t\t * This function is called on every 'draw' event, and allows you to\n\t\t * dynamically modify any aspect you want about the created DOM.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.drawCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"drawCallback\": function( settings ) {\n\t\t *          alert( 'DataTables has redrawn the table' );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnDrawCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * Identical to fnHeaderCallback() but for the table footer this function\n\t\t * allows you to modify the table footer on every 'draw' event.\n\t\t *  @type function\n\t\t *  @param {node} foot \"TR\" element for the footer\n\t\t *  @param {array} data Full table data (as derived from the original HTML)\n\t\t *  @param {int} start Index for the current display starting point in the\n\t\t *    display array\n\t\t *  @param {int} end Index for the current display ending point in the\n\t\t *    display array\n\t\t *  @param {array int} display Index array to translate the visual position\n\t\t *    to the full data array\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.footerCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"footerCallback\": function( tfoot, data, start, end, display ) {\n\t\t *          tfoot.getElementsByTagName('th')[0].innerHTML = \"Starting index is \"+start;\n\t\t *        }\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"fnFooterCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * When rendering large numbers in the information element for the table\n\t\t * (i.e. \"Showing 1 to 10 of 57 entries\") DataTables will render large numbers\n\t\t * to have a comma separator for the 'thousands' units (e.g. 1 million is\n\t\t * rendered as \"1,000,000\") to help readability for the end user. This\n\t\t * function will override the default method DataTables uses.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {int} toFormat number to be formatted\n\t\t *  @returns {string} formatted string for DataTables to show the number\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.formatNumber\n\t\t *\n\t\t *  @example\n\t\t *    // Format a number using a single quote for the separator (note that\n\t\t *    // this can also be done with the language.thousands option)\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"formatNumber\": function ( toFormat ) {\n\t\t *          return toFormat.toString().replace(\n\t\t *            /\\B(?=(\\d{3})+(?!\\d))/g, \"'\"\n\t\t *          );\n\t\t *        };\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnFormatNumber\": function ( toFormat ) {\n\t\t\treturn toFormat.toString().replace(\n\t\t\t\t/\\B(?=(\\d{3})+(?!\\d))/g,\n\t\t\t\tthis.oLanguage.sThousands\n\t\t\t);\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * This function is called on every 'draw' event, and allows you to\n\t\t * dynamically modify the header row. This can be used to calculate and\n\t\t * display useful information about the table.\n\t\t *  @type function\n\t\t *  @param {node} head \"TR\" element for the header\n\t\t *  @param {array} data Full table data (as derived from the original HTML)\n\t\t *  @param {int} start Index for the current display starting point in the\n\t\t *    display array\n\t\t *  @param {int} end Index for the current display ending point in the\n\t\t *    display array\n\t\t *  @param {array int} display Index array to translate the visual position\n\t\t *    to the full data array\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.headerCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"fheaderCallback\": function( head, data, start, end, display ) {\n\t\t *          head.getElementsByTagName('th')[0].innerHTML = \"Displaying \"+(end-start)+\" records\";\n\t\t *        }\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"fnHeaderCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * The information element can be used to convey information about the current\n\t\t * state of the table. Although the internationalisation options presented by\n\t\t * DataTables are quite capable of dealing with most customisations, there may\n\t\t * be times where you wish to customise the string further. This callback\n\t\t * allows you to do exactly that.\n\t\t *  @type function\n\t\t *  @param {object} oSettings DataTables settings object\n\t\t *  @param {int} start Starting position in data for the draw\n\t\t *  @param {int} end End position in data for the draw\n\t\t *  @param {int} max Total number of rows in the table (regardless of\n\t\t *    filtering)\n\t\t *  @param {int} total Total number of rows in the data set, after filtering\n\t\t *  @param {string} pre The string that DataTables has formatted using it's\n\t\t *    own rules\n\t\t *  @returns {string} The string to be displayed in the information element.\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.infoCallback\n\t\t *\n\t\t *  @example\n\t\t *    $('#example').dataTable( {\n\t\t *      \"infoCallback\": function( settings, start, end, max, total, pre ) {\n\t\t *        return start +\" to \"+ end;\n\t\t *      }\n\t\t *    } );\n\t\t */\n\t\t\"fnInfoCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * Called when the table has been initialised. Normally DataTables will\n\t\t * initialise sequentially and there will be no need for this function,\n\t\t * however, this does not hold true when using external language information\n\t\t * since that is obtained using an async XHR call.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} json The JSON object request from the server - only\n\t\t *    present if client-side Ajax sourced data is used\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.initComplete\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"initComplete\": function(settings, json) {\n\t\t *          alert( 'DataTables has finished its initialisation.' );\n\t\t *        }\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"fnInitComplete\": null,\n\t\n\t\n\t\t/**\n\t\t * Called at the very start of each table draw and can be used to cancel the\n\t\t * draw by returning false, any other return (including undefined) results in\n\t\t * the full draw occurring).\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @returns {boolean} False will cancel the draw, anything else (including no\n\t\t *    return) will allow it to complete.\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.preDrawCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"preDrawCallback\": function( settings ) {\n\t\t *          if ( $('#test').val() == 1 ) {\n\t\t *            return false;\n\t\t *          }\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnPreDrawCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * This function allows you to 'post process' each row after it have been\n\t\t * generated for each table draw, but before it is rendered on screen. This\n\t\t * function might be used for setting the row class name etc.\n\t\t *  @type function\n\t\t *  @param {node} row \"TR\" element for the current row\n\t\t *  @param {array} data Raw data array for this row\n\t\t *  @param {int} displayIndex The display index for the current table draw\n\t\t *  @param {int} displayIndexFull The index of the data in the full list of\n\t\t *    rows (after filtering)\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.rowCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"rowCallback\": function( row, data, displayIndex, displayIndexFull ) {\n\t\t *          // Bold the grade for all 'A' grade browsers\n\t\t *          if ( data[4] == \"A\" ) {\n\t\t *            $('td:eq(4)', row).html( '<b>A</b>' );\n\t\t *          }\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnRowCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * This parameter allows you to override the default function which obtains\n\t\t * the data from the server so something more suitable for your application.\n\t\t * For example you could use POST data, or pull information from a Gears or\n\t\t * AIR database.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {string} source HTTP source to obtain the data from (`ajax`)\n\t\t *  @param {array} data A key/value pair object containing the data to send\n\t\t *    to the server\n\t\t *  @param {function} callback to be called on completion of the data get\n\t\t *    process that will draw the data on the page.\n\t\t *  @param {object} settings DataTables settings object\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverData\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"fnServerData\": null,\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t *  It is often useful to send extra data to the server when making an Ajax\n\t\t * request - for example custom filtering information, and this callback\n\t\t * function makes it trivial to send extra information to the server. The\n\t\t * passed in parameter is the data set that has been constructed by\n\t\t * DataTables, and you can add to this or modify it as you require.\n\t\t *  @type function\n\t\t *  @param {array} data Data array (array of objects which are name/value\n\t\t *    pairs) that has been constructed by DataTables and will be sent to the\n\t\t *    server. In the case of Ajax sourced data with server-side processing\n\t\t *    this will be an empty array, for server-side processing there will be a\n\t\t *    significant number of parameters!\n\t\t *  @returns {undefined} Ensure that you modify the data array passed in,\n\t\t *    as this is passed by reference.\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverParams\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"fnServerParams\": null,\n\t\n\t\n\t\t/**\n\t\t * Load the table state. With this function you can define from where, and how, the\n\t\t * state of a table is loaded. By default DataTables will load from `localStorage`\n\t\t * but you might wish to use a server-side database or cookies.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} callback Callback that can be executed when done. It\n\t\t *    should be passed the loaded state object.\n\t\t *  @return {object} The DataTables state object to be loaded\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateLoadCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoadCallback\": function (settings, callback) {\n\t\t *          $.ajax( {\n\t\t *            \"url\": \"/state_load\",\n\t\t *            \"dataType\": \"json\",\n\t\t *            \"success\": function (json) {\n\t\t *              callback( json );\n\t\t *            }\n\t\t *          } );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateLoadCallback\": function ( settings ) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(\n\t\t\t\t\t(settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(\n\t\t\t\t\t\t'DataTables_'+settings.sInstance+'_'+location.pathname\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Callback which allows modification of the saved state prior to loading that state.\n\t\t * This callback is called when the table is loading state from the stored data, but\n\t\t * prior to the settings object being modified by the saved state. Note that for\n\t\t * plug-in authors, you should use the `stateLoadParams` event to load parameters for\n\t\t * a plug-in.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object that is to be loaded\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateLoadParams\n\t\t *\n\t\t *  @example\n\t\t *    // Remove a saved filter, so filtering is never loaded\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoadParams\": function (settings, data) {\n\t\t *          data.oSearch.sSearch = \"\";\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Disallow state loading by returning false\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoadParams\": function (settings, data) {\n\t\t *          return false;\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateLoadParams\": null,\n\t\n\t\n\t\t/**\n\t\t * Callback that is called when the state has been loaded from the state saving method\n\t\t * and the DataTables settings object has been modified as a result of the loaded state.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object that was loaded\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateLoaded\n\t\t *\n\t\t *  @example\n\t\t *    // Show an alert with the filtering value that was saved\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoaded\": function (settings, data) {\n\t\t *          alert( 'Saved filter was: '+data.oSearch.sSearch );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateLoaded\": null,\n\t\n\t\n\t\t/**\n\t\t * Save the table state. This function allows you to define where and how the state\n\t\t * information for the table is stored By default DataTables will use `localStorage`\n\t\t * but you might wish to use a server-side database or cookies.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object to be saved\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateSaveCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateSaveCallback\": function (settings, data) {\n\t\t *          // Send an Ajax request to the server with the state object\n\t\t *          $.ajax( {\n\t\t *            \"url\": \"/state_save\",\n\t\t *            \"data\": data,\n\t\t *            \"dataType\": \"json\",\n\t\t *            \"method\": \"POST\"\n\t\t *            \"success\": function () {}\n\t\t *          } );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateSaveCallback\": function ( settings, data ) {\n\t\t\ttry {\n\t\t\t\t(settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(\n\t\t\t\t\t'DataTables_'+settings.sInstance+'_'+location.pathname,\n\t\t\t\t\tJSON.stringify( data )\n\t\t\t\t);\n\t\t\t} catch (e) {}\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Callback which allows modification of the state to be saved. Called when the table\n\t\t * has changed state a new state save is required. This method allows modification of\n\t\t * the state saving object prior to actually doing the save, including addition or\n\t\t * other state properties or modification. Note that for plug-in authors, you should\n\t\t * use the `stateSaveParams` event to save parameters for a plug-in.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object to be saved\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateSaveParams\n\t\t *\n\t\t *  @example\n\t\t *    // Remove a saved filter, so filtering is never saved\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateSaveParams\": function (settings, data) {\n\t\t *          data.oSearch.sSearch = \"\";\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateSaveParams\": null,\n\t\n\t\n\t\t/**\n\t\t * Duration for which the saved state information is considered valid. After this period\n\t\t * has elapsed the state will be returned to the default.\n\t\t * Value is given in seconds.\n\t\t *  @type int\n\t\t *  @default 7200 <i>(2 hours)</i>\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.stateDuration\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateDuration\": 60*60*24; // 1 day\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"iStateDuration\": 7200,\n\t\n\t\n\t\t/**\n\t\t * When enabled DataTables will not make a request to the server for the first\n\t\t * page draw - rather it will use the data already on the page (no sorting etc\n\t\t * will be applied to it), thus saving on an XHR at load time. `deferLoading`\n\t\t * is used to indicate that deferred loading is required, but it is also used\n\t\t * to tell DataTables how many records there are in the full table (allowing\n\t\t * the information element and pagination to be displayed correctly). In the case\n\t\t * where a filtering is applied to the table on initial load, this can be\n\t\t * indicated by giving the parameter as an array, where the first element is\n\t\t * the number of records available after filtering and the second element is the\n\t\t * number of records without filtering (allowing the table information element\n\t\t * to be shown correctly).\n\t\t *  @type int | array\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.deferLoading\n\t\t *\n\t\t *  @example\n\t\t *    // 57 records available in the table, no filtering applied\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"serverSide\": true,\n\t\t *        \"ajax\": \"scripts/server_processing.php\",\n\t\t *        \"deferLoading\": 57\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // 57 records after filtering, 100 without filtering (an initial filter applied)\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"serverSide\": true,\n\t\t *        \"ajax\": \"scripts/server_processing.php\",\n\t\t *        \"deferLoading\": [ 57, 100 ],\n\t\t *        \"search\": {\n\t\t *          \"search\": \"my_filter\"\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"iDeferLoading\": null,\n\t\n\t\n\t\t/**\n\t\t * Number of rows to display on a single page when using pagination. If\n\t\t * feature enabled (`lengthChange`) then the end user will be able to override\n\t\t * this to a custom setting using a pop-up menu.\n\t\t *  @type int\n\t\t *  @default 10\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.pageLength\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"pageLength\": 50\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"iDisplayLength\": 10,\n\t\n\t\n\t\t/**\n\t\t * Define the starting point for data display when using DataTables with\n\t\t * pagination. Note that this parameter is the number of records, rather than\n\t\t * the page number, so if you have 10 records per page and want to start on\n\t\t * the third page, it should be \"20\".\n\t\t *  @type int\n\t\t *  @default 0\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.displayStart\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"displayStart\": 20\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"iDisplayStart\": 0,\n\t\n\t\n\t\t/**\n\t\t * By default DataTables allows keyboard navigation of the table (sorting, paging,\n\t\t * and filtering) by adding a `tabindex` attribute to the required elements. This\n\t\t * allows you to tab through the controls and press the enter key to activate them.\n\t\t * The tabindex is default 0, meaning that the tab follows the flow of the document.\n\t\t * You can overrule this using this parameter if you wish. Use a value of -1 to\n\t\t * disable built-in keyboard navigation.\n\t\t *  @type int\n\t\t *  @default 0\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.tabIndex\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"tabIndex\": 1\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"iTabIndex\": 0,\n\t\n\t\n\t\t/**\n\t\t * Classes that DataTables assigns to the various components and features\n\t\t * that it adds to the HTML table. This allows classes to be configured\n\t\t * during initialisation in addition to through the static\n\t\t * {@link DataTable.ext.oStdClasses} object).\n\t\t *  @namespace\n\t\t *  @name DataTable.defaults.classes\n\t\t */\n\t\t\"oClasses\": {},\n\t\n\t\n\t\t/**\n\t\t * All strings that DataTables uses in the user interface that it creates\n\t\t * are defined in this object, allowing you to modified them individually or\n\t\t * completely replace them all as required.\n\t\t *  @namespace\n\t\t *  @name DataTable.defaults.language\n\t\t */\n\t\t\"oLanguage\": {\n\t\t\t/**\n\t\t\t * Strings that are used for WAI-ARIA labels and controls only (these are not\n\t\t\t * actually visible on the page, but will be read by screenreaders, and thus\n\t\t\t * must be internationalised as well).\n\t\t\t *  @namespace\n\t\t\t *  @name DataTable.defaults.language.aria\n\t\t\t */\n\t\t\t\"oAria\": {\n\t\t\t\t/**\n\t\t\t\t * ARIA label that is added to the table headers when the column may be\n\t\t\t\t * sorted ascending by activing the column (click or return when focused).\n\t\t\t\t * Note that the column header is prefixed to this string.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default : activate to sort column ascending\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.aria.sortAscending\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"aria\": {\n\t\t\t\t *            \"sortAscending\": \" - click/return to sort ascending\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sSortAscending\": \": activate to sort column ascending\",\n\t\n\t\t\t\t/**\n\t\t\t\t * ARIA label that is added to the table headers when the column may be\n\t\t\t\t * sorted descending by activing the column (click or return when focused).\n\t\t\t\t * Note that the column header is prefixed to this string.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default : activate to sort column ascending\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.aria.sortDescending\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"aria\": {\n\t\t\t\t *            \"sortDescending\": \" - click/return to sort descending\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sSortDescending\": \": activate to sort column descending\"\n\t\t\t},\n\t\n\t\t\t/**\n\t\t\t * Pagination string used by DataTables for the built-in pagination\n\t\t\t * control types.\n\t\t\t *  @namespace\n\t\t\t *  @name DataTable.defaults.language.paginate\n\t\t\t */\n\t\t\t\"oPaginate\": {\n\t\t\t\t/**\n\t\t\t\t * Text to use when using the 'full_numbers' type of pagination for the\n\t\t\t\t * button to take the user to the first page.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default First\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.first\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"first\": \"First page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sFirst\": \"First\",\n\t\n\t\n\t\t\t\t/**\n\t\t\t\t * Text to use when using the 'full_numbers' type of pagination for the\n\t\t\t\t * button to take the user to the last page.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default Last\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.last\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"last\": \"Last page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sLast\": \"Last\",\n\t\n\t\n\t\t\t\t/**\n\t\t\t\t * Text to use for the 'next' pagination button (to take the user to the\n\t\t\t\t * next page).\n\t\t\t\t *  @type string\n\t\t\t\t *  @default Next\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.next\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"next\": \"Next page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sNext\": \"Next\",\n\t\n\t\n\t\t\t\t/**\n\t\t\t\t * Text to use for the 'previous' pagination button (to take the user to\n\t\t\t\t * the previous page).\n\t\t\t\t *  @type string\n\t\t\t\t *  @default Previous\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.previous\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"previous\": \"Previous page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sPrevious\": \"Previous\"\n\t\t\t},\n\t\n\t\t\t/**\n\t\t\t * This string is shown in preference to `zeroRecords` when the table is\n\t\t\t * empty of data (regardless of filtering). Note that this is an optional\n\t\t\t * parameter - if it is not given, the value of `zeroRecords` will be used\n\t\t\t * instead (either the default or given value).\n\t\t\t *  @type string\n\t\t\t *  @default No data available in table\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.emptyTable\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"emptyTable\": \"No data available in table\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sEmptyTable\": \"No data available in table\",\n\t\n\t\n\t\t\t/**\n\t\t\t * This string gives information to the end user about the information\n\t\t\t * that is current on display on the page. The following tokens can be\n\t\t\t * used in the string and will be dynamically replaced as the table\n\t\t\t * display updates. This tokens can be placed anywhere in the string, or\n\t\t\t * removed as needed by the language requires:\n\t\t\t *\n\t\t\t * * `\\_START\\_` - Display index of the first record on the current page\n\t\t\t * * `\\_END\\_` - Display index of the last record on the current page\n\t\t\t * * `\\_TOTAL\\_` - Number of records in the table after filtering\n\t\t\t * * `\\_MAX\\_` - Number of records in the table without filtering\n\t\t\t * * `\\_PAGE\\_` - Current page number\n\t\t\t * * `\\_PAGES\\_` - Total number of pages of data in the table\n\t\t\t *\n\t\t\t *  @type string\n\t\t\t *  @default Showing _START_ to _END_ of _TOTAL_ entries\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.info\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"info\": \"Showing page _PAGE_ of _PAGES_\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfo\": \"Showing _START_ to _END_ of _TOTAL_ entries\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Display information string for when the table is empty. Typically the\n\t\t\t * format of this string should match `info`.\n\t\t\t *  @type string\n\t\t\t *  @default Showing 0 to 0 of 0 entries\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.infoEmpty\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"infoEmpty\": \"No entries to show\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfoEmpty\": \"Showing 0 to 0 of 0 entries\",\n\t\n\t\n\t\t\t/**\n\t\t\t * When a user filters the information in a table, this string is appended\n\t\t\t * to the information (`info`) to give an idea of how strong the filtering\n\t\t\t * is. The variable _MAX_ is dynamically updated.\n\t\t\t *  @type string\n\t\t\t *  @default (filtered from _MAX_ total entries)\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.infoFiltered\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"infoFiltered\": \" - filtering from _MAX_ records\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfoFiltered\": \"(filtered from _MAX_ total entries)\",\n\t\n\t\n\t\t\t/**\n\t\t\t * If can be useful to append extra information to the info string at times,\n\t\t\t * and this variable does exactly that. This information will be appended to\n\t\t\t * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are\n\t\t\t * being used) at all times.\n\t\t\t *  @type string\n\t\t\t *  @default <i>Empty string</i>\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.infoPostFix\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"infoPostFix\": \"All records shown are derived from real information.\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfoPostFix\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * This decimal place operator is a little different from the other\n\t\t\t * language options since DataTables doesn't output floating point\n\t\t\t * numbers, so it won't ever use this for display of a number. Rather,\n\t\t\t * what this parameter does is modify the sort methods of the table so\n\t\t\t * that numbers which are in a format which has a character other than\n\t\t\t * a period (`.`) as a decimal place will be sorted numerically.\n\t\t\t *\n\t\t\t * Note that numbers with different decimal places cannot be shown in\n\t\t\t * the same table and still be sortable, the table must be consistent.\n\t\t\t * However, multiple different tables on the page can use different\n\t\t\t * decimal place characters.\n\t\t\t *  @type string\n\t\t\t *  @default \n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.decimal\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"decimal\": \",\"\n\t\t\t *          \"thousands\": \".\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sDecimal\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * DataTables has a build in number formatter (`formatNumber`) which is\n\t\t\t * used to format large numbers that are used in the table information.\n\t\t\t * By default a comma is used, but this can be trivially changed to any\n\t\t\t * character you wish with this parameter.\n\t\t\t *  @type string\n\t\t\t *  @default ,\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.thousands\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"thousands\": \"'\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sThousands\": \",\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Detail the action that will be taken when the drop down menu for the\n\t\t\t * pagination length option is changed. The '_MENU_' variable is replaced\n\t\t\t * with a default select list of 10, 25, 50 and 100, and can be replaced\n\t\t\t * with a custom select box if required.\n\t\t\t *  @type string\n\t\t\t *  @default Show _MENU_ entries\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.lengthMenu\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Language change only\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"lengthMenu\": \"Display _MENU_ records\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Language and options change\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"lengthMenu\": 'Display <select>'+\n\t\t\t *            '<option value=\"10\">10</option>'+\n\t\t\t *            '<option value=\"20\">20</option>'+\n\t\t\t *            '<option value=\"30\">30</option>'+\n\t\t\t *            '<option value=\"40\">40</option>'+\n\t\t\t *            '<option value=\"50\">50</option>'+\n\t\t\t *            '<option value=\"-1\">All</option>'+\n\t\t\t *            '</select> records'\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sLengthMenu\": \"Show _MENU_ entries\",\n\t\n\t\n\t\t\t/**\n\t\t\t * When using Ajax sourced data and during the first draw when DataTables is\n\t\t\t * gathering the data, this message is shown in an empty row in the table to\n\t\t\t * indicate to the end user the the data is being loaded. Note that this\n\t\t\t * parameter is not used when loading data by server-side processing, just\n\t\t\t * Ajax sourced data with client-side processing.\n\t\t\t *  @type string\n\t\t\t *  @default Loading...\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.loadingRecords\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"loadingRecords\": \"Please wait - loading...\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sLoadingRecords\": \"Loading...\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Text which is displayed when the table is processing a user action\n\t\t\t * (usually a sort command or similar).\n\t\t\t *  @type string\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.processing\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"processing\": \"DataTables is currently busy\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sProcessing\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Details the actions that will be taken when the user types into the\n\t\t\t * filtering input text box. The variable \"_INPUT_\", if used in the string,\n\t\t\t * is replaced with the HTML text box for the filtering input allowing\n\t\t\t * control over where it appears in the string. If \"_INPUT_\" is not given\n\t\t\t * then the input box is appended to the string automatically.\n\t\t\t *  @type string\n\t\t\t *  @default Search:\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.search\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Input text box will be appended at the end automatically\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"search\": \"Filter records:\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Specify where the filter should appear\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"search\": \"Apply filter _INPUT_ to table\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sSearch\": \"Search:\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Assign a `placeholder` attribute to the search `input` element\n\t\t\t *  @type string\n\t\t\t *  @default \n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.searchPlaceholder\n\t\t\t */\n\t\t\t\"sSearchPlaceholder\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * All of the language information can be stored in a file on the\n\t\t\t * server-side, which DataTables will look up if this parameter is passed.\n\t\t\t * It must store the URL of the language file, which is in a JSON format,\n\t\t\t * and the object has the same properties as the oLanguage object in the\n\t\t\t * initialiser object (i.e. the above parameters). Please refer to one of\n\t\t\t * the example language files to see how this works in action.\n\t\t\t *  @type string\n\t\t\t *  @default <i>Empty string - i.e. disabled</i>\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.url\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"url\": \"http://www.sprymedia.co.uk/dataTables/lang.txt\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sUrl\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Text shown inside the table records when the is no information to be\n\t\t\t * displayed after filtering. `emptyTable` is shown when there is simply no\n\t\t\t * information in the table at all (regardless of filtering).\n\t\t\t *  @type string\n\t\t\t *  @default No matching records found\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.zeroRecords\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"zeroRecords\": \"No records to display\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sZeroRecords\": \"No matching records found\"\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * This parameter allows you to have define the global filtering state at\n\t\t * initialisation time. As an object the `search` parameter must be\n\t\t * defined, but all other parameters are optional. When `regex` is true,\n\t\t * the search string will be treated as a regular expression, when false\n\t\t * (default) it will be treated as a straight string. When `smart`\n\t\t * DataTables will use it's smart filtering methods (to word match at\n\t\t * any point in the data), when false this will not be done.\n\t\t *  @namespace\n\t\t *  @extends DataTable.models.oSearch\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.search\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"search\": {\"search\": \"Initial search\"}\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"oSearch\": $.extend( {}, DataTable.models.oSearch ),\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * By default DataTables will look for the property `data` (or `aaData` for\n\t\t * compatibility with DataTables 1.9-) when obtaining data from an Ajax\n\t\t * source or for server-side processing - this parameter allows that\n\t\t * property to be changed. You can use Javascript dotted object notation to\n\t\t * get a data source for multiple levels of nesting.\n\t\t *  @type string\n\t\t *  @default data\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.ajaxDataProp\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"sAjaxDataProp\": \"data\",\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * You can instruct DataTables to load data from an external\n\t\t * source using this parameter (use aData if you want to pass data in you\n\t\t * already have). Simply provide a url a JSON object can be obtained from.\n\t\t *  @type string\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.ajaxSource\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"sAjaxSource\": null,\n\t\n\t\n\t\t/**\n\t\t * This initialisation variable allows you to specify exactly where in the\n\t\t * DOM you want DataTables to inject the various controls it adds to the page\n\t\t * (for example you might want the pagination controls at the top of the\n\t\t * table). DIV elements (with or without a custom class) can also be added to\n\t\t * aid styling. The follow syntax is used:\n\t\t *   <ul>\n\t\t *     <li>The following options are allowed:\n\t\t *       <ul>\n\t\t *         <li>'l' - Length changing</li>\n\t\t *         <li>'f' - Filtering input</li>\n\t\t *         <li>'t' - The table!</li>\n\t\t *         <li>'i' - Information</li>\n\t\t *         <li>'p' - Pagination</li>\n\t\t *         <li>'r' - pRocessing</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *     <li>The following constants are allowed:\n\t\t *       <ul>\n\t\t *         <li>'H' - jQueryUI theme \"header\" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>\n\t\t *         <li>'F' - jQueryUI theme \"footer\" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *     <li>The following syntax is expected:\n\t\t *       <ul>\n\t\t *         <li>'&lt;' and '&gt;' - div elements</li>\n\t\t *         <li>'&lt;\"class\" and '&gt;' - div with a class</li>\n\t\t *         <li>'&lt;\"#id\" and '&gt;' - div with an ID</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *     <li>Examples:\n\t\t *       <ul>\n\t\t *         <li>'&lt;\"wrapper\"flipt&gt;'</li>\n\t\t *         <li>'&lt;lf&lt;t&gt;ip&gt;'</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *   </ul>\n\t\t *  @type string\n\t\t *  @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>\n\t\t *    <\"H\"lfr>t<\"F\"ip> <i>(when `jQueryUI` is true)</i>\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.dom\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"dom\": '&lt;\"top\"i&gt;rt&lt;\"bottom\"flp&gt;&lt;\"clear\"&gt;'\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sDom\": \"lfrtip\",\n\t\n\t\n\t\t/**\n\t\t * Search delay option. This will throttle full table searches that use the\n\t\t * DataTables provided search input element (it does not effect calls to\n\t\t * `dt-api search()`, providing a delay before the search is made.\n\t\t *  @type integer\n\t\t *  @default 0\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.searchDelay\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"searchDelay\": 200\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"searchDelay\": null,\n\t\n\t\n\t\t/**\n\t\t * DataTables features six different built-in options for the buttons to\n\t\t * display for pagination control:\n\t\t *\n\t\t * * `numbers` - Page number buttons only\n\t\t * * `simple` - 'Previous' and 'Next' buttons only\n\t\t * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers\n\t\t * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons\n\t\t * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers\n\t\t * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers\n\t\t *  \n\t\t * Further methods can be added using {@link DataTable.ext.oPagination}.\n\t\t *  @type string\n\t\t *  @default simple_numbers\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.pagingType\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"pagingType\": \"full_numbers\"\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"sPaginationType\": \"simple_numbers\",\n\t\n\t\n\t\t/**\n\t\t * Enable horizontal scrolling. When a table is too wide to fit into a\n\t\t * certain layout, or you have a large number of columns in the table, you\n\t\t * can enable x-scrolling to show the table in a viewport, which can be\n\t\t * scrolled. This property can be `true` which will allow the table to\n\t\t * scroll horizontally when needed, or any CSS unit, or a number (in which\n\t\t * case it will be treated as a pixel measurement). Setting as simply `true`\n\t\t * is recommended.\n\t\t *  @type boolean|string\n\t\t *  @default <i>blank string - i.e. disabled</i>\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.scrollX\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollX\": true,\n\t\t *        \"scrollCollapse\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sScrollX\": \"\",\n\t\n\t\n\t\t/**\n\t\t * This property can be used to force a DataTable to use more width than it\n\t\t * might otherwise do when x-scrolling is enabled. For example if you have a\n\t\t * table which requires to be well spaced, this parameter is useful for\n\t\t * \"over-sizing\" the table, and thus forcing scrolling. This property can by\n\t\t * any CSS unit, or a number (in which case it will be treated as a pixel\n\t\t * measurement).\n\t\t *  @type string\n\t\t *  @default <i>blank string - i.e. disabled</i>\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.scrollXInner\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollX\": \"100%\",\n\t\t *        \"scrollXInner\": \"110%\"\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sScrollXInner\": \"\",\n\t\n\t\n\t\t/**\n\t\t * Enable vertical scrolling. Vertical scrolling will constrain the DataTable\n\t\t * to the given height, and enable scrolling for any data which overflows the\n\t\t * current viewport. This can be used as an alternative to paging to display\n\t\t * a lot of data in a small area (although paging and scrolling can both be\n\t\t * enabled at the same time). This property can be any CSS unit, or a number\n\t\t * (in which case it will be treated as a pixel measurement).\n\t\t *  @type string\n\t\t *  @default <i>blank string - i.e. disabled</i>\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.scrollY\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollY\": \"200px\",\n\t\t *        \"paginate\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sScrollY\": \"\",\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * Set the HTTP method that is used to make the Ajax call for server-side\n\t\t * processing or Ajax sourced data.\n\t\t *  @type string\n\t\t *  @default GET\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverMethod\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"sServerMethod\": \"GET\",\n\t\n\t\n\t\t/**\n\t\t * DataTables makes use of renderers when displaying HTML elements for\n\t\t * a table. These renderers can be added or modified by plug-ins to\n\t\t * generate suitable mark-up for a site. For example the Bootstrap\n\t\t * integration plug-in for DataTables uses a paging button renderer to\n\t\t * display pagination buttons in the mark-up required by Bootstrap.\n\t\t *\n\t\t * For further information about the renderers available see\n\t\t * DataTable.ext.renderer\n\t\t *  @type string|object\n\t\t *  @default null\n\t\t *\n\t\t *  @name DataTable.defaults.renderer\n\t\t *\n\t\t */\n\t\t\"renderer\": null,\n\t\n\t\n\t\t/**\n\t\t * Set the data property name that DataTables should use to get a row's id\n\t\t * to set as the `id` property in the node.\n\t\t *  @type string\n\t\t *  @default DT_RowId\n\t\t *\n\t\t *  @name DataTable.defaults.rowId\n\t\t */\n\t\t\"rowId\": \"DT_RowId\"\n\t};\n\t\n\t_fnHungarianMap( DataTable.defaults );\n\t\n\t\n\t\n\t/*\n\t * Developer note - See note in model.defaults.js about the use of Hungarian\n\t * notation and camel case.\n\t */\n\t\n\t/**\n\t * Column options that can be given to DataTables at initialisation time.\n\t *  @namespace\n\t */\n\tDataTable.defaults.column = {\n\t\t/**\n\t\t * Define which column(s) an order will occur on for this column. This\n\t\t * allows a column's ordering to take multiple columns into account when\n\t\t * doing a sort or use the data from a different column. For example first\n\t\t * name / last name columns make sense to do a multi-column sort over the\n\t\t * two columns.\n\t\t *  @type array|int\n\t\t *  @default null <i>Takes the value of the column index automatically</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderData\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderData\": [ 0, 1 ], \"targets\": [ 0 ] },\n\t\t *          { \"orderData\": [ 1, 0 ], \"targets\": [ 1 ] },\n\t\t *          { \"orderData\": 2, \"targets\": [ 2 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"orderData\": [ 0, 1 ] },\n\t\t *          { \"orderData\": [ 1, 0 ] },\n\t\t *          { \"orderData\": 2 },\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aDataSort\": null,\n\t\t\"iDataSort\": -1,\n\t\n\t\n\t\t/**\n\t\t * You can control the default ordering direction, and even alter the\n\t\t * behaviour of the sort handler (i.e. only allow ascending ordering etc)\n\t\t * using this parameter.\n\t\t *  @type array\n\t\t *  @default [ 'asc', 'desc' ]\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderSequence\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderSequence\": [ \"asc\" ], \"targets\": [ 1 ] },\n\t\t *          { \"orderSequence\": [ \"desc\", \"asc\", \"asc\" ], \"targets\": [ 2 ] },\n\t\t *          { \"orderSequence\": [ \"desc\" ], \"targets\": [ 3 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          { \"orderSequence\": [ \"asc\" ] },\n\t\t *          { \"orderSequence\": [ \"desc\", \"asc\", \"asc\" ] },\n\t\t *          { \"orderSequence\": [ \"desc\" ] },\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"asSorting\": [ 'asc', 'desc' ],\n\t\n\t\n\t\t/**\n\t\t * Enable or disable filtering on the data in this column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @name DataTable.defaults.column.searchable\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"searchable\": false, \"targets\": [ 0 ] }\n\t\t *        ] } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"searchable\": false },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ] } );\n\t\t *    } );\n\t\t */\n\t\t\"bSearchable\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable ordering on this column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderable\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderable\": false, \"targets\": [ 0 ] }\n\t\t *        ] } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"orderable\": false },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ] } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortable\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the display of this column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @name DataTable.defaults.column.visible\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"visible\": false, \"targets\": [ 0 ] }\n\t\t *        ] } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"visible\": false },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ] } );\n\t\t *    } );\n\t\t */\n\t\t\"bVisible\": true,\n\t\n\t\n\t\t/**\n\t\t * Developer definable function that is called whenever a cell is created (Ajax source,\n\t\t * etc) or processed for input (DOM source). This can be used as a compliment to mRender\n\t\t * allowing you to modify the DOM element (add background colour for example) when the\n\t\t * element is available.\n\t\t *  @type function\n\t\t *  @param {element} td The TD node that has been created\n\t\t *  @param {*} cellData The Data for the cell\n\t\t *  @param {array|object} rowData The data for the whole row\n\t\t *  @param {int} row The row index for the aoData data store\n\t\t *  @param {int} col The column index for aoColumns\n\t\t *\n\t\t *  @name DataTable.defaults.column.createdCell\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [3],\n\t\t *          \"createdCell\": function (td, cellData, rowData, row, col) {\n\t\t *            if ( cellData == \"1.7\" ) {\n\t\t *              $(td).css('color', 'blue')\n\t\t *            }\n\t\t *          }\n\t\t *        } ]\n\t\t *      });\n\t\t *    } );\n\t\t */\n\t\t\"fnCreatedCell\": null,\n\t\n\t\n\t\t/**\n\t\t * This parameter has been replaced by `data` in DataTables to ensure naming\n\t\t * consistency. `dataProp` can still be used, as there is backwards\n\t\t * compatibility in DataTables for this option, but it is strongly\n\t\t * recommended that you use `data` in preference to `dataProp`.\n\t\t *  @name DataTable.defaults.column.dataProp\n\t\t */\n\t\n\t\n\t\t/**\n\t\t * This property can be used to read data from any data source property,\n\t\t * including deeply nested objects / properties. `data` can be given in a\n\t\t * number of different ways which effect its behaviour:\n\t\t *\n\t\t * * `integer` - treated as an array index for the data source. This is the\n\t\t *   default that DataTables uses (incrementally increased for each column).\n\t\t * * `string` - read an object property from the data source. There are\n\t\t *   three 'special' options that can be used in the string to alter how\n\t\t *   DataTables reads the data from the source object:\n\t\t *    * `.` - Dotted Javascript notation. Just as you use a `.` in\n\t\t *      Javascript to read from nested objects, so to can the options\n\t\t *      specified in `data`. For example: `browser.version` or\n\t\t *      `browser.name`. If your object parameter name contains a period, use\n\t\t *      `\\\\` to escape it - i.e. `first\\\\.name`.\n\t\t *    * `[]` - Array notation. DataTables can automatically combine data\n\t\t *      from and array source, joining the data with the characters provided\n\t\t *      between the two brackets. For example: `name[, ]` would provide a\n\t\t *      comma-space separated list from the source array. If no characters\n\t\t *      are provided between the brackets, the original array source is\n\t\t *      returned.\n\t\t *    * `()` - Function notation. Adding `()` to the end of a parameter will\n\t\t *      execute a function of the name given. For example: `browser()` for a\n\t\t *      simple function on the data source, `browser.version()` for a\n\t\t *      function in a nested property or even `browser().version` to get an\n\t\t *      object property if the function called returns an object. Note that\n\t\t *      function notation is recommended for use in `render` rather than\n\t\t *      `data` as it is much simpler to use as a renderer.\n\t\t * * `null` - use the original data source for the row rather than plucking\n\t\t *   data directly from it. This action has effects on two other\n\t\t *   initialisation options:\n\t\t *    * `defaultContent` - When null is given as the `data` option and\n\t\t *      `defaultContent` is specified for the column, the value defined by\n\t\t *      `defaultContent` will be used for the cell.\n\t\t *    * `render` - When null is used for the `data` option and the `render`\n\t\t *      option is specified for the column, the whole data source for the\n\t\t *      row is used for the renderer.\n\t\t * * `function` - the function given will be executed whenever DataTables\n\t\t *   needs to set or get the data for a cell in the column. The function\n\t\t *   takes three parameters:\n\t\t *    * Parameters:\n\t\t *      * `{array|object}` The data source for the row\n\t\t *      * `{string}` The type call data requested - this will be 'set' when\n\t\t *        setting data or 'filter', 'display', 'type', 'sort' or undefined\n\t\t *        when gathering data. Note that when `undefined` is given for the\n\t\t *        type DataTables expects to get the raw data for the object back<\n\t\t *      * `{*}` Data to set when the second parameter is 'set'.\n\t\t *    * Return:\n\t\t *      * The return value from the function is not required when 'set' is\n\t\t *        the type of call, but otherwise the return is what will be used\n\t\t *        for the data requested.\n\t\t *\n\t\t * Note that `data` is a getter and setter option. If you just require\n\t\t * formatting of data for output, you will likely want to use `render` which\n\t\t * is simply a getter and thus simpler to use.\n\t\t *\n\t\t * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The\n\t\t * name change reflects the flexibility of this property and is consistent\n\t\t * with the naming of mRender. If 'mDataProp' is given, then it will still\n\t\t * be used by DataTables, as it automatically maps the old name to the new\n\t\t * if required.\n\t\t *\n\t\t *  @type string|int|function|null\n\t\t *  @default null <i>Use automatically calculated column index</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.data\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Read table data from objects\n\t\t *    // JSON structure for each row:\n\t\t *    //   {\n\t\t *    //      \"engine\": {value},\n\t\t *    //      \"browser\": {value},\n\t\t *    //      \"platform\": {value},\n\t\t *    //      \"version\": {value},\n\t\t *    //      \"grade\": {value}\n\t\t *    //   }\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajaxSource\": \"sources/objects.txt\",\n\t\t *        \"columns\": [\n\t\t *          { \"data\": \"engine\" },\n\t\t *          { \"data\": \"browser\" },\n\t\t *          { \"data\": \"platform\" },\n\t\t *          { \"data\": \"version\" },\n\t\t *          { \"data\": \"grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Read information from deeply nested objects\n\t\t *    // JSON structure for each row:\n\t\t *    //   {\n\t\t *    //      \"engine\": {value},\n\t\t *    //      \"browser\": {value},\n\t\t *    //      \"platform\": {\n\t\t *    //         \"inner\": {value}\n\t\t *    //      },\n\t\t *    //      \"details\": [\n\t\t *    //         {value}, {value}\n\t\t *    //      ]\n\t\t *    //   }\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajaxSource\": \"sources/deep.txt\",\n\t\t *        \"columns\": [\n\t\t *          { \"data\": \"engine\" },\n\t\t *          { \"data\": \"browser\" },\n\t\t *          { \"data\": \"platform.inner\" },\n\t\t *          { \"data\": \"details.0\" },\n\t\t *          { \"data\": \"details.1\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `data` as a function to provide different information for\n\t\t *    // sorting, filtering and display. In this case, currency (price)\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": function ( source, type, val ) {\n\t\t *            if (type === 'set') {\n\t\t *              source.price = val;\n\t\t *              // Store the computed display and filter values for efficiency\n\t\t *              source.price_display = val==\"\" ? \"\" : \"$\"+numberFormat(val);\n\t\t *              source.price_filter  = val==\"\" ? \"\" : \"$\"+numberFormat(val)+\" \"+val;\n\t\t *              return;\n\t\t *            }\n\t\t *            else if (type === 'display') {\n\t\t *              return source.price_display;\n\t\t *            }\n\t\t *            else if (type === 'filter') {\n\t\t *              return source.price_filter;\n\t\t *            }\n\t\t *            // 'sort', 'type' and undefined all just use the integer\n\t\t *            return source.price;\n\t\t *          }\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using default content\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": null,\n\t\t *          \"defaultContent\": \"Click to edit\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using array notation - outputting a list from an array\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": \"name[, ]\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t */\n\t\t\"mData\": null,\n\t\n\t\n\t\t/**\n\t\t * This property is the rendering partner to `data` and it is suggested that\n\t\t * when you want to manipulate data for display (including filtering,\n\t\t * sorting etc) without altering the underlying data for the table, use this\n\t\t * property. `render` can be considered to be the the read only companion to\n\t\t * `data` which is read / write (then as such more complex). Like `data`\n\t\t * this option can be given in a number of different ways to effect its\n\t\t * behaviour:\n\t\t *\n\t\t * * `integer` - treated as an array index for the data source. This is the\n\t\t *   default that DataTables uses (incrementally increased for each column).\n\t\t * * `string` - read an object property from the data source. There are\n\t\t *   three 'special' options that can be used in the string to alter how\n\t\t *   DataTables reads the data from the source object:\n\t\t *    * `.` - Dotted Javascript notation. Just as you use a `.` in\n\t\t *      Javascript to read from nested objects, so to can the options\n\t\t *      specified in `data`. For example: `browser.version` or\n\t\t *      `browser.name`. If your object parameter name contains a period, use\n\t\t *      `\\\\` to escape it - i.e. `first\\\\.name`.\n\t\t *    * `[]` - Array notation. DataTables can automatically combine data\n\t\t *      from and array source, joining the data with the characters provided\n\t\t *      between the two brackets. For example: `name[, ]` would provide a\n\t\t *      comma-space separated list from the source array. If no characters\n\t\t *      are provided between the brackets, the original array source is\n\t\t *      returned.\n\t\t *    * `()` - Function notation. Adding `()` to the end of a parameter will\n\t\t *      execute a function of the name given. For example: `browser()` for a\n\t\t *      simple function on the data source, `browser.version()` for a\n\t\t *      function in a nested property or even `browser().version` to get an\n\t\t *      object property if the function called returns an object.\n\t\t * * `object` - use different data for the different data types requested by\n\t\t *   DataTables ('filter', 'display', 'type' or 'sort'). The property names\n\t\t *   of the object is the data type the property refers to and the value can\n\t\t *   defined using an integer, string or function using the same rules as\n\t\t *   `render` normally does. Note that an `_` option _must_ be specified.\n\t\t *   This is the default value to use if you haven't specified a value for\n\t\t *   the data type requested by DataTables.\n\t\t * * `function` - the function given will be executed whenever DataTables\n\t\t *   needs to set or get the data for a cell in the column. The function\n\t\t *   takes three parameters:\n\t\t *    * Parameters:\n\t\t *      * {array|object} The data source for the row (based on `data`)\n\t\t *      * {string} The type call data requested - this will be 'filter',\n\t\t *        'display', 'type' or 'sort'.\n\t\t *      * {array|object} The full data source for the row (not based on\n\t\t *        `data`)\n\t\t *    * Return:\n\t\t *      * The return value from the function is what will be used for the\n\t\t *        data requested.\n\t\t *\n\t\t *  @type string|int|function|object|null\n\t\t *  @default null Use the data source value.\n\t\t *\n\t\t *  @name DataTable.defaults.column.render\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Create a comma separated list from an array of objects\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajaxSource\": \"sources/deep.txt\",\n\t\t *        \"columns\": [\n\t\t *          { \"data\": \"engine\" },\n\t\t *          { \"data\": \"browser\" },\n\t\t *          {\n\t\t *            \"data\": \"platform\",\n\t\t *            \"render\": \"[, ].name\"\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Execute a function to obtain data\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": null, // Use the full data source object for the renderer's source\n\t\t *          \"render\": \"browserName()\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // As an object, extracting different data for the different types\n\t\t *    // This would be used with a data source such as:\n\t\t *    //   { \"phone\": 5552368, \"phone_filter\": \"5552368 555-2368\", \"phone_display\": \"555-2368\" }\n\t\t *    // Here the `phone` integer is used for sorting and type detection, while `phone_filter`\n\t\t *    // (which has both forms) is used for filtering for if a user inputs either format, while\n\t\t *    // the formatted phone number is the one that is shown in the table.\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": null, // Use the full data source object for the renderer's source\n\t\t *          \"render\": {\n\t\t *            \"_\": \"phone\",\n\t\t *            \"filter\": \"phone_filter\",\n\t\t *            \"display\": \"phone_display\"\n\t\t *          }\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Use as a function to create a link from the data source\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": \"download_link\",\n\t\t *          \"render\": function ( data, type, full ) {\n\t\t *            return '<a href=\"'+data+'\">Download</a>';\n\t\t *          }\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"mRender\": null,\n\t\n\t\n\t\t/**\n\t\t * Change the cell type created for the column - either TD cells or TH cells. This\n\t\t * can be useful as TH cells have semantic meaning in the table body, allowing them\n\t\t * to act as a header for a row (you may wish to add scope='row' to the TH elements).\n\t\t *  @type string\n\t\t *  @default td\n\t\t *\n\t\t *  @name DataTable.defaults.column.cellType\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Make the first column use TH cells\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"cellType\": \"th\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sCellType\": \"td\",\n\t\n\t\n\t\t/**\n\t\t * Class to give to each cell in this column.\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.class\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"class\": \"my_class\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"class\": \"my_class\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sClass\": \"\",\n\t\n\t\t/**\n\t\t * When DataTables calculates the column widths to assign to each column,\n\t\t * it finds the longest string in each column and then constructs a\n\t\t * temporary table and reads the widths from that. The problem with this\n\t\t * is that \"mmm\" is much wider then \"iiii\", but the latter is a longer\n\t\t * string - thus the calculation can go wrong (doing it properly and putting\n\t\t * it into an DOM object and measuring that is horribly(!) slow). Thus as\n\t\t * a \"work around\" we provide this option. It will append its value to the\n\t\t * text that is found to be the longest string for the column - i.e. padding.\n\t\t * Generally you shouldn't need this!\n\t\t *  @type string\n\t\t *  @default <i>Empty string<i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.contentPadding\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          {\n\t\t *            \"contentPadding\": \"mmm\"\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sContentPadding\": \"\",\n\t\n\t\n\t\t/**\n\t\t * Allows a default value to be given for a column's data, and will be used\n\t\t * whenever a null data source is encountered (this can be because `data`\n\t\t * is set to null, or because the data source itself is null).\n\t\t *  @type string\n\t\t *  @default null\n\t\t *\n\t\t *  @name DataTable.defaults.column.defaultContent\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          {\n\t\t *            \"data\": null,\n\t\t *            \"defaultContent\": \"Edit\",\n\t\t *            \"targets\": [ -1 ]\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          {\n\t\t *            \"data\": null,\n\t\t *            \"defaultContent\": \"Edit\"\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sDefaultContent\": null,\n\t\n\t\n\t\t/**\n\t\t * This parameter is only used in DataTables' server-side processing. It can\n\t\t * be exceptionally useful to know what columns are being displayed on the\n\t\t * client side, and to map these to database fields. When defined, the names\n\t\t * also allow DataTables to reorder information from the server if it comes\n\t\t * back in an unexpected order (i.e. if you switch your columns around on the\n\t\t * client-side, your server-side code does not also need updating).\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.name\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"name\": \"engine\", \"targets\": [ 0 ] },\n\t\t *          { \"name\": \"browser\", \"targets\": [ 1 ] },\n\t\t *          { \"name\": \"platform\", \"targets\": [ 2 ] },\n\t\t *          { \"name\": \"version\", \"targets\": [ 3 ] },\n\t\t *          { \"name\": \"grade\", \"targets\": [ 4 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"name\": \"engine\" },\n\t\t *          { \"name\": \"browser\" },\n\t\t *          { \"name\": \"platform\" },\n\t\t *          { \"name\": \"version\" },\n\t\t *          { \"name\": \"grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sName\": \"\",\n\t\n\t\n\t\t/**\n\t\t * Defines a data source type for the ordering which can be used to read\n\t\t * real-time information from the table (updating the internally cached\n\t\t * version) prior to ordering. This allows ordering to occur on user\n\t\t * editable elements such as form inputs.\n\t\t *  @type string\n\t\t *  @default std\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderDataType\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderDataType\": \"dom-text\", \"targets\": [ 2, 3 ] },\n\t\t *          { \"type\": \"numeric\", \"targets\": [ 3 ] },\n\t\t *          { \"orderDataType\": \"dom-select\", \"targets\": [ 4 ] },\n\t\t *          { \"orderDataType\": \"dom-checkbox\", \"targets\": [ 5 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          null,\n\t\t *          { \"orderDataType\": \"dom-text\" },\n\t\t *          { \"orderDataType\": \"dom-text\", \"type\": \"numeric\" },\n\t\t *          { \"orderDataType\": \"dom-select\" },\n\t\t *          { \"orderDataType\": \"dom-checkbox\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sSortDataType\": \"std\",\n\t\n\t\n\t\t/**\n\t\t * The title of this column.\n\t\t *  @type string\n\t\t *  @default null <i>Derived from the 'TH' value for this column in the\n\t\t *    original HTML table.</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.title\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"title\": \"My column title\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"title\": \"My column title\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sTitle\": null,\n\t\n\t\n\t\t/**\n\t\t * The type allows you to specify how the data for this column will be\n\t\t * ordered. Four types (string, numeric, date and html (which will strip\n\t\t * HTML tags before ordering)) are currently available. Note that only date\n\t\t * formats understood by Javascript's Date() object will be accepted as type\n\t\t * date. For example: \"Mar 26, 2008 5:03 PM\". May take the values: 'string',\n\t\t * 'numeric', 'date' or 'html' (by default). Further types can be adding\n\t\t * through plug-ins.\n\t\t *  @type string\n\t\t *  @default null <i>Auto-detected from raw data</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.type\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"type\": \"html\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"type\": \"html\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sType\": null,\n\t\n\t\n\t\t/**\n\t\t * Defining the width of the column, this parameter may take any CSS value\n\t\t * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not\n\t\t * been given a specific width through this interface ensuring that the table\n\t\t * remains readable.\n\t\t *  @type string\n\t\t *  @default null <i>Automatic</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.width\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"width\": \"20%\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"width\": \"20%\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sWidth\": null\n\t};\n\t\n\t_fnHungarianMap( DataTable.defaults.column );\n\t\n\t\n\t\n\t/**\n\t * DataTables settings object - this holds all the information needed for a\n\t * given table, including configuration, data and current application of the\n\t * table options. DataTables does not have a single instance for each DataTable\n\t * with the settings attached to that instance, but rather instances of the\n\t * DataTable \"class\" are created on-the-fly as needed (typically by a\n\t * $().dataTable() call) and the settings object is then applied to that\n\t * instance.\n\t *\n\t * Note that this object is related to {@link DataTable.defaults} but this\n\t * one is the internal data store for DataTables's cache of columns. It should\n\t * NOT be manipulated outside of DataTables. Any configuration should be done\n\t * through the initialisation options.\n\t *  @namespace\n\t *  @todo Really should attach the settings object to individual instances so we\n\t *    don't need to create new instances on each $().dataTable() call (if the\n\t *    table already exists). It would also save passing oSettings around and\n\t *    into every single function. However, this is a very significant\n\t *    architecture change for DataTables and will almost certainly break\n\t *    backwards compatibility with older installations. This is something that\n\t *    will be done in 2.0.\n\t */\n\tDataTable.models.oSettings = {\n\t\t/**\n\t\t * Primary features of DataTables and their enablement state.\n\t\t *  @namespace\n\t\t */\n\t\t\"oFeatures\": {\n\t\n\t\t\t/**\n\t\t\t * Flag to say if DataTables should automatically try to calculate the\n\t\t\t * optimum table and columns widths (true) or not (false).\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bAutoWidth\": null,\n\t\n\t\t\t/**\n\t\t\t * Delay the creation of TR and TD elements until they are actually\n\t\t\t * needed by a driven page draw. This can give a significant speed\n\t\t\t * increase for Ajax source and Javascript source data, but makes no\n\t\t\t * difference at all for DOM and server-side processing tables.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bDeferRender\": null,\n\t\n\t\t\t/**\n\t\t\t * Enable filtering on the table or not. Note that if this is disabled\n\t\t\t * then there is no filtering at all on the table, including fnFilter.\n\t\t\t * To just remove the filtering input use sDom and remove the 'f' option.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bFilter\": null,\n\t\n\t\t\t/**\n\t\t\t * Table information element (the 'Showing x of y records' div) enable\n\t\t\t * flag.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bInfo\": null,\n\t\n\t\t\t/**\n\t\t\t * Present a user control allowing the end user to change the page size\n\t\t\t * when pagination is enabled.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bLengthChange\": null,\n\t\n\t\t\t/**\n\t\t\t * Pagination enabled or not. Note that if this is disabled then length\n\t\t\t * changing must also be disabled.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bPaginate\": null,\n\t\n\t\t\t/**\n\t\t\t * Processing indicator enable flag whenever DataTables is enacting a\n\t\t\t * user request - typically an Ajax request for server-side processing.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bProcessing\": null,\n\t\n\t\t\t/**\n\t\t\t * Server-side processing enabled flag - when enabled DataTables will\n\t\t\t * get all data from the server for every draw - there is no filtering,\n\t\t\t * sorting or paging done on the client-side.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bServerSide\": null,\n\t\n\t\t\t/**\n\t\t\t * Sorting enablement flag.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bSort\": null,\n\t\n\t\t\t/**\n\t\t\t * Multi-column sorting\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bSortMulti\": null,\n\t\n\t\t\t/**\n\t\t\t * Apply a class to the columns which are being sorted to provide a\n\t\t\t * visual highlight or not. This can slow things down when enabled since\n\t\t\t * there is a lot of DOM interaction.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bSortClasses\": null,\n\t\n\t\t\t/**\n\t\t\t * State saving enablement flag.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bStateSave\": null\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Scrolling settings for a table.\n\t\t *  @namespace\n\t\t */\n\t\t\"oScroll\": {\n\t\t\t/**\n\t\t\t * When the table is shorter in height than sScrollY, collapse the\n\t\t\t * table container down to the height of the table (when true).\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bCollapse\": null,\n\t\n\t\t\t/**\n\t\t\t * Width of the scrollbar for the web-browser's platform. Calculated\n\t\t\t * during table initialisation.\n\t\t\t *  @type int\n\t\t\t *  @default 0\n\t\t\t */\n\t\t\t\"iBarWidth\": 0,\n\t\n\t\t\t/**\n\t\t\t * Viewport width for horizontal scrolling. Horizontal scrolling is\n\t\t\t * disabled if an empty string.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type string\n\t\t\t */\n\t\t\t\"sX\": null,\n\t\n\t\t\t/**\n\t\t\t * Width to expand the table to when using x-scrolling. Typically you\n\t\t\t * should not need to use this.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type string\n\t\t\t *  @deprecated\n\t\t\t */\n\t\t\t\"sXInner\": null,\n\t\n\t\t\t/**\n\t\t\t * Viewport height for vertical scrolling. Vertical scrolling is disabled\n\t\t\t * if an empty string.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type string\n\t\t\t */\n\t\t\t\"sY\": null\n\t\t},\n\t\n\t\t/**\n\t\t * Language information for the table.\n\t\t *  @namespace\n\t\t *  @extends DataTable.defaults.oLanguage\n\t\t */\n\t\t\"oLanguage\": {\n\t\t\t/**\n\t\t\t * Information callback function. See\n\t\t\t * {@link DataTable.defaults.fnInfoCallback}\n\t\t\t *  @type function\n\t\t\t *  @default null\n\t\t\t */\n\t\t\t\"fnInfoCallback\": null\n\t\t},\n\t\n\t\t/**\n\t\t * Browser support parameters\n\t\t *  @namespace\n\t\t */\n\t\t\"oBrowser\": {\n\t\t\t/**\n\t\t\t * Indicate if the browser incorrectly calculates width:100% inside a\n\t\t\t * scrolling element (IE6/7)\n\t\t\t *  @type boolean\n\t\t\t *  @default false\n\t\t\t */\n\t\t\t\"bScrollOversize\": false,\n\t\n\t\t\t/**\n\t\t\t * Determine if the vertical scrollbar is on the right or left of the\n\t\t\t * scrolling container - needed for rtl language layout, although not\n\t\t\t * all browsers move the scrollbar (Safari).\n\t\t\t *  @type boolean\n\t\t\t *  @default false\n\t\t\t */\n\t\t\t\"bScrollbarLeft\": false,\n\t\n\t\t\t/**\n\t\t\t * Flag for if `getBoundingClientRect` is fully supported or not\n\t\t\t *  @type boolean\n\t\t\t *  @default false\n\t\t\t */\n\t\t\t\"bBounding\": false,\n\t\n\t\t\t/**\n\t\t\t * Browser scrollbar width\n\t\t\t *  @type integer\n\t\t\t *  @default 0\n\t\t\t */\n\t\t\t\"barWidth\": 0\n\t\t},\n\t\n\t\n\t\t\"ajax\": null,\n\t\n\t\n\t\t/**\n\t\t * Array referencing the nodes which are used for the features. The\n\t\t * parameters of this object match what is allowed by sDom - i.e.\n\t\t *   <ul>\n\t\t *     <li>'l' - Length changing</li>\n\t\t *     <li>'f' - Filtering input</li>\n\t\t *     <li>'t' - The table!</li>\n\t\t *     <li>'i' - Information</li>\n\t\t *     <li>'p' - Pagination</li>\n\t\t *     <li>'r' - pRocessing</li>\n\t\t *   </ul>\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aanFeatures\": [],\n\t\n\t\t/**\n\t\t * Store data information - see {@link DataTable.models.oRow} for detailed\n\t\t * information.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoData\": [],\n\t\n\t\t/**\n\t\t * Array of indexes which are in the current display (after filtering etc)\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aiDisplay\": [],\n\t\n\t\t/**\n\t\t * Array of indexes for display - no filtering\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aiDisplayMaster\": [],\n\t\n\t\t/**\n\t\t * Map of row ids to data indexes\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\t\"aIds\": {},\n\t\n\t\t/**\n\t\t * Store information about each column that is in use\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoColumns\": [],\n\t\n\t\t/**\n\t\t * Store information about the table's header\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoHeader\": [],\n\t\n\t\t/**\n\t\t * Store information about the table's footer\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoFooter\": [],\n\t\n\t\t/**\n\t\t * Store the applied global search information in case we want to force a\n\t\t * research or compare the old search to a new one.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @namespace\n\t\t *  @extends DataTable.models.oSearch\n\t\t */\n\t\t\"oPreviousSearch\": {},\n\t\n\t\t/**\n\t\t * Store the applied search for each column - see\n\t\t * {@link DataTable.models.oSearch} for the format that is used for the\n\t\t * filtering information for each column.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoPreSearchCols\": [],\n\t\n\t\t/**\n\t\t * Sorting that is applied to the table. Note that the inner arrays are\n\t\t * used in the following manner:\n\t\t * <ul>\n\t\t *   <li>Index 0 - column number</li>\n\t\t *   <li>Index 1 - current sorting direction</li>\n\t\t * </ul>\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @todo These inner arrays should really be objects\n\t\t */\n\t\t\"aaSorting\": null,\n\t\n\t\t/**\n\t\t * Sorting that is always applied to the table (i.e. prefixed in front of\n\t\t * aaSorting).\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aaSortingFixed\": [],\n\t\n\t\t/**\n\t\t * Classes to use for the striping of a table.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"asStripeClasses\": null,\n\t\n\t\t/**\n\t\t * If restoring a table - we should restore its striping classes as well\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"asDestroyStripes\": [],\n\t\n\t\t/**\n\t\t * If restoring a table - we should restore its width\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"sDestroyWidth\": 0,\n\t\n\t\t/**\n\t\t * Callback functions array for every time a row is inserted (i.e. on a draw).\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoRowCallback\": [],\n\t\n\t\t/**\n\t\t * Callback functions for the header on each draw.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoHeaderCallback\": [],\n\t\n\t\t/**\n\t\t * Callback function for the footer on each draw.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoFooterCallback\": [],\n\t\n\t\t/**\n\t\t * Array of callback functions for draw callback functions\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoDrawCallback\": [],\n\t\n\t\t/**\n\t\t * Array of callback functions for row created function\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoRowCreatedCallback\": [],\n\t\n\t\t/**\n\t\t * Callback functions for just before the table is redrawn. A return of\n\t\t * false will be used to cancel the draw.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoPreDrawCallback\": [],\n\t\n\t\t/**\n\t\t * Callback functions for when the table has been initialised.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoInitComplete\": [],\n\t\n\t\n\t\t/**\n\t\t * Callbacks for modifying the settings to be stored for state saving, prior to\n\t\t * saving state.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateSaveParams\": [],\n\t\n\t\t/**\n\t\t * Callbacks for modifying the settings that have been stored for state saving\n\t\t * prior to using the stored values to restore the state.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateLoadParams\": [],\n\t\n\t\t/**\n\t\t * Callbacks for operating on the settings object once the saved state has been\n\t\t * loaded\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateLoaded\": [],\n\t\n\t\t/**\n\t\t * Cache the table ID for quick access\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t */\n\t\t\"sTableId\": \"\",\n\t\n\t\t/**\n\t\t * The TABLE node for the main table\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTable\": null,\n\t\n\t\t/**\n\t\t * Permanent ref to the thead element\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTHead\": null,\n\t\n\t\t/**\n\t\t * Permanent ref to the tfoot element - if it exists\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTFoot\": null,\n\t\n\t\t/**\n\t\t * Permanent ref to the tbody element\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTBody\": null,\n\t\n\t\t/**\n\t\t * Cache the wrapper node (contains all DataTables controlled elements)\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTableWrapper\": null,\n\t\n\t\t/**\n\t\t * Indicate if when using server-side processing the loading of data\n\t\t * should be deferred until the second draw.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bDeferLoading\": false,\n\t\n\t\t/**\n\t\t * Indicate if all required information has been read in\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bInitialised\": false,\n\t\n\t\t/**\n\t\t * Information about open rows. Each object in the array has the parameters\n\t\t * 'nTr' and 'nParent'\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoOpenRows\": [],\n\t\n\t\t/**\n\t\t * Dictate the positioning of DataTables' control elements - see\n\t\t * {@link DataTable.model.oInit.sDom}.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sDom\": null,\n\t\n\t\t/**\n\t\t * Search delay (in mS)\n\t\t *  @type integer\n\t\t *  @default null\n\t\t */\n\t\t\"searchDelay\": null,\n\t\n\t\t/**\n\t\t * Which type of pagination should be used.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t *  @default two_button\n\t\t */\n\t\t\"sPaginationType\": \"two_button\",\n\t\n\t\t/**\n\t\t * The state duration (for `stateSave`) in seconds.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"iStateDuration\": 0,\n\t\n\t\t/**\n\t\t * Array of callback functions for state saving. Each array element is an\n\t\t * object with the following parameters:\n\t\t *   <ul>\n\t\t *     <li>function:fn - function to call. Takes two parameters, oSettings\n\t\t *       and the JSON string to save that has been thus far created. Returns\n\t\t *       a JSON string to be inserted into a json object\n\t\t *       (i.e. '\"param\": [ 0, 1, 2]')</li>\n\t\t *     <li>string:sName - name of callback</li>\n\t\t *   </ul>\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateSave\": [],\n\t\n\t\t/**\n\t\t * Array of callback functions for state loading. Each array element is an\n\t\t * object with the following parameters:\n\t\t *   <ul>\n\t\t *     <li>function:fn - function to call. Takes two parameters, oSettings\n\t\t *       and the object stored. May return false to cancel state loading</li>\n\t\t *     <li>string:sName - name of callback</li>\n\t\t *   </ul>\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateLoad\": [],\n\t\n\t\t/**\n\t\t * State that was saved. Useful for back reference\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oSavedState\": null,\n\t\n\t\t/**\n\t\t * State that was loaded. Useful for back reference\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oLoadedState\": null,\n\t\n\t\t/**\n\t\t * Source url for AJAX data for the table.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sAjaxSource\": null,\n\t\n\t\t/**\n\t\t * Property from a given object from which to read the table data from. This\n\t\t * can be an empty string (when not server-side processing), in which case\n\t\t * it is  assumed an an array is given directly.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t */\n\t\t\"sAjaxDataProp\": null,\n\t\n\t\t/**\n\t\t * The last jQuery XHR object that was used for server-side data gathering.\n\t\t * This can be used for working with the XHR information in one of the\n\t\t * callbacks\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"jqXHR\": null,\n\t\n\t\t/**\n\t\t * JSON returned from the server in the last Ajax request\n\t\t *  @type object\n\t\t *  @default undefined\n\t\t */\n\t\t\"json\": undefined,\n\t\n\t\t/**\n\t\t * Data submitted as part of the last Ajax request\n\t\t *  @type object\n\t\t *  @default undefined\n\t\t */\n\t\t\"oAjaxData\": undefined,\n\t\n\t\t/**\n\t\t * Function to get the server-side data.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type function\n\t\t */\n\t\t\"fnServerData\": null,\n\t\n\t\t/**\n\t\t * Functions which are called prior to sending an Ajax request so extra\n\t\t * parameters can easily be sent to the server\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoServerParams\": [],\n\t\n\t\t/**\n\t\t * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if\n\t\t * required).\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t */\n\t\t\"sServerMethod\": null,\n\t\n\t\t/**\n\t\t * Format numbers for display.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type function\n\t\t */\n\t\t\"fnFormatNumber\": null,\n\t\n\t\t/**\n\t\t * List of options that can be used for the user selectable length menu.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aLengthMenu\": null,\n\t\n\t\t/**\n\t\t * Counter for the draws that the table does. Also used as a tracker for\n\t\t * server-side processing\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"iDraw\": 0,\n\t\n\t\t/**\n\t\t * Indicate if a redraw is being done - useful for Ajax\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bDrawing\": false,\n\t\n\t\t/**\n\t\t * Draw index (iDraw) of the last error when parsing the returned data\n\t\t *  @type int\n\t\t *  @default -1\n\t\t */\n\t\t\"iDrawError\": -1,\n\t\n\t\t/**\n\t\t * Paging display length\n\t\t *  @type int\n\t\t *  @default 10\n\t\t */\n\t\t\"_iDisplayLength\": 10,\n\t\n\t\t/**\n\t\t * Paging start point - aiDisplay index\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"_iDisplayStart\": 0,\n\t\n\t\t/**\n\t\t * Server-side processing - number of records in the result set\n\t\t * (i.e. before filtering), Use fnRecordsTotal rather than\n\t\t * this property to get the value of the number of records, regardless of\n\t\t * the server-side processing setting.\n\t\t *  @type int\n\t\t *  @default 0\n\t\t *  @private\n\t\t */\n\t\t\"_iRecordsTotal\": 0,\n\t\n\t\t/**\n\t\t * Server-side processing - number of records in the current display set\n\t\t * (i.e. after filtering). Use fnRecordsDisplay rather than\n\t\t * this property to get the value of the number of records, regardless of\n\t\t * the server-side processing setting.\n\t\t *  @type boolean\n\t\t *  @default 0\n\t\t *  @private\n\t\t */\n\t\t\"_iRecordsDisplay\": 0,\n\t\n\t\t/**\n\t\t * The classes to use for the table\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\t\"oClasses\": {},\n\t\n\t\t/**\n\t\t * Flag attached to the settings object so you can check in the draw\n\t\t * callback if filtering has been done in the draw. Deprecated in favour of\n\t\t * events.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *  @deprecated\n\t\t */\n\t\t\"bFiltered\": false,\n\t\n\t\t/**\n\t\t * Flag attached to the settings object so you can check in the draw\n\t\t * callback if sorting has been done in the draw. Deprecated in favour of\n\t\t * events.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *  @deprecated\n\t\t */\n\t\t\"bSorted\": false,\n\t\n\t\t/**\n\t\t * Indicate that if multiple rows are in the header and there is more than\n\t\t * one unique cell per column, if the top one (true) or bottom one (false)\n\t\t * should be used for sorting / title by DataTables.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type boolean\n\t\t */\n\t\t\"bSortCellsTop\": null,\n\t\n\t\t/**\n\t\t * Initialisation object that is used for the table\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oInit\": null,\n\t\n\t\t/**\n\t\t * Destroy callback functions - for plug-ins to attach themselves to the\n\t\t * destroy so they can clean up markup and events.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoDestroyCallback\": [],\n\t\n\t\n\t\t/**\n\t\t * Get the number of records in the current record set, before filtering\n\t\t *  @type function\n\t\t */\n\t\t\"fnRecordsTotal\": function ()\n\t\t{\n\t\t\treturn _fnDataSource( this ) == 'ssp' ?\n\t\t\t\tthis._iRecordsTotal * 1 :\n\t\t\t\tthis.aiDisplayMaster.length;\n\t\t},\n\t\n\t\t/**\n\t\t * Get the number of records in the current record set, after filtering\n\t\t *  @type function\n\t\t */\n\t\t\"fnRecordsDisplay\": function ()\n\t\t{\n\t\t\treturn _fnDataSource( this ) == 'ssp' ?\n\t\t\t\tthis._iRecordsDisplay * 1 :\n\t\t\t\tthis.aiDisplay.length;\n\t\t},\n\t\n\t\t/**\n\t\t * Get the display end point - aiDisplay index\n\t\t *  @type function\n\t\t */\n\t\t\"fnDisplayEnd\": function ()\n\t\t{\n\t\t\tvar\n\t\t\t\tlen      = this._iDisplayLength,\n\t\t\t\tstart    = this._iDisplayStart,\n\t\t\t\tcalc     = start + len,\n\t\t\t\trecords  = this.aiDisplay.length,\n\t\t\t\tfeatures = this.oFeatures,\n\t\t\t\tpaginate = features.bPaginate;\n\t\n\t\t\tif ( features.bServerSide ) {\n\t\t\t\treturn paginate === false || len === -1 ?\n\t\t\t\t\tstart + records :\n\t\t\t\t\tMath.min( start+len, this._iRecordsDisplay );\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn ! paginate || calc>records || len===-1 ?\n\t\t\t\t\trecords :\n\t\t\t\t\tcalc;\n\t\t\t}\n\t\t},\n\t\n\t\t/**\n\t\t * The DataTables object for this table\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oInstance\": null,\n\t\n\t\t/**\n\t\t * Unique identifier for each instance of the DataTables object. If there\n\t\t * is an ID on the table node, then it takes that value, otherwise an\n\t\t * incrementing internal counter is used.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sInstance\": null,\n\t\n\t\t/**\n\t\t * tabindex attribute value that is added to DataTables control elements, allowing\n\t\t * keyboard navigation of the table and its controls.\n\t\t */\n\t\t\"iTabIndex\": 0,\n\t\n\t\t/**\n\t\t * DIV container for the footer scrolling table if scrolling\n\t\t */\n\t\t\"nScrollHead\": null,\n\t\n\t\t/**\n\t\t * DIV container for the footer scrolling table if scrolling\n\t\t */\n\t\t\"nScrollFoot\": null,\n\t\n\t\t/**\n\t\t * Last applied sort\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aLastSort\": [],\n\t\n\t\t/**\n\t\t * Stored plug-in instances\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\t\"oPlugins\": {},\n\t\n\t\t/**\n\t\t * Function used to get a row's id from the row's data\n\t\t *  @type function\n\t\t *  @default null\n\t\t */\n\t\t\"rowIdFn\": null,\n\t\n\t\t/**\n\t\t * Data location where to store a row's id\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"rowId\": null\n\t};\n\t\n\t/**\n\t * Extension object for DataTables that is used to provide all extension\n\t * options.\n\t *\n\t * Note that the `DataTable.ext` object is available through\n\t * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is\n\t * also aliased to `jQuery.fn.dataTableExt` for historic reasons.\n\t *  @namespace\n\t *  @extends DataTable.models.ext\n\t */\n\t\n\t\n\t/**\n\t * DataTables extensions\n\t * \n\t * This namespace acts as a collection area for plug-ins that can be used to\n\t * extend DataTables capabilities. Indeed many of the build in methods\n\t * use this method to provide their own capabilities (sorting methods for\n\t * example).\n\t *\n\t * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy\n\t * reasons\n\t *\n\t *  @namespace\n\t */\n\tDataTable.ext = _ext = {\n\t\t/**\n\t\t * Buttons. For use with the Buttons extension for DataTables. This is\n\t\t * defined here so other extensions can define buttons regardless of load\n\t\t * order. It is _not_ used by DataTables core.\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\tbuttons: {},\n\t\n\t\n\t\t/**\n\t\t * Element class names\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\tclasses: {},\n\t\n\t\n\t\t/**\n\t\t * DataTables build type (expanded by the download builder)\n\t\t *\n\t\t *  @type string\n\t\t */\n\t\tbuild:\"bs5/dt-1.13.5\",\n\t\n\t\n\t\t/**\n\t\t * Error reporting.\n\t\t * \n\t\t * How should DataTables report an error. Can take the value 'alert',\n\t\t * 'throw', 'none' or a function.\n\t\t *\n\t\t *  @type string|function\n\t\t *  @default alert\n\t\t */\n\t\terrMode: \"alert\",\n\t\n\t\n\t\t/**\n\t\t * Feature plug-ins.\n\t\t * \n\t\t * This is an array of objects which describe the feature plug-ins that are\n\t\t * available to DataTables. These feature plug-ins are then available for\n\t\t * use through the `dom` initialisation option.\n\t\t * \n\t\t * Each feature plug-in is described by an object which must have the\n\t\t * following properties:\n\t\t * \n\t\t * * `fnInit` - function that is used to initialise the plug-in,\n\t\t * * `cFeature` - a character so the feature can be enabled by the `dom`\n\t\t *   instillation option. This is case sensitive.\n\t\t *\n\t\t * The `fnInit` function has the following input parameters:\n\t\t *\n\t\t * 1. `{object}` DataTables settings object: see\n\t\t *    {@link DataTable.models.oSettings}\n\t\t *\n\t\t * And the following return is expected:\n\t\t * \n\t\t * * {node|null} The element which contains your feature. Note that the\n\t\t *   return may also be void if your plug-in does not require to inject any\n\t\t *   DOM elements into DataTables control (`dom`) - for example this might\n\t\t *   be useful when developing a plug-in which allows table control via\n\t\t *   keyboard entry\n\t\t *\n\t\t *  @type array\n\t\t *\n\t\t *  @example\n\t\t *    $.fn.dataTable.ext.features.push( {\n\t\t *      \"fnInit\": function( oSettings ) {\n\t\t *        return new TableTools( { \"oDTSettings\": oSettings } );\n\t\t *      },\n\t\t *      \"cFeature\": \"T\"\n\t\t *    } );\n\t\t */\n\t\tfeature: [],\n\t\n\t\n\t\t/**\n\t\t * Row searching.\n\t\t * \n\t\t * This method of searching is complimentary to the default type based\n\t\t * searching, and a lot more comprehensive as it allows you complete control\n\t\t * over the searching logic. Each element in this array is a function\n\t\t * (parameters described below) that is called for every row in the table,\n\t\t * and your logic decides if it should be included in the searching data set\n\t\t * or not.\n\t\t *\n\t\t * Searching functions have the following input parameters:\n\t\t *\n\t\t * 1. `{object}` DataTables settings object: see\n\t\t *    {@link DataTable.models.oSettings}\n\t\t * 2. `{array|object}` Data for the row to be processed (same as the\n\t\t *    original format that was passed in as the data source, or an array\n\t\t *    from a DOM data source\n\t\t * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which\n\t\t *    can be useful to retrieve the `TR` element if you need DOM interaction.\n\t\t *\n\t\t * And the following return is expected:\n\t\t *\n\t\t * * {boolean} Include the row in the searched result set (true) or not\n\t\t *   (false)\n\t\t *\n\t\t * Note that as with the main search ability in DataTables, technically this\n\t\t * is \"filtering\", since it is subtractive. However, for consistency in\n\t\t * naming we call it searching here.\n\t\t *\n\t\t *  @type array\n\t\t *  @default []\n\t\t *\n\t\t *  @example\n\t\t *    // The following example shows custom search being applied to the\n\t\t *    // fourth column (i.e. the data[3] index) based on two input values\n\t\t *    // from the end-user, matching the data in a certain range.\n\t\t *    $.fn.dataTable.ext.search.push(\n\t\t *      function( settings, data, dataIndex ) {\n\t\t *        var min = document.getElementById('min').value * 1;\n\t\t *        var max = document.getElementById('max').value * 1;\n\t\t *        var version = data[3] == \"-\" ? 0 : data[3]*1;\n\t\t *\n\t\t *        if ( min == \"\" && max == \"\" ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        else if ( min == \"\" && version < max ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        else if ( min < version && \"\" == max ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        else if ( min < version && version < max ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        return false;\n\t\t *      }\n\t\t *    );\n\t\t */\n\t\tsearch: [],\n\t\n\t\n\t\t/**\n\t\t * Selector extensions\n\t\t *\n\t\t * The `selector` option can be used to extend the options available for the\n\t\t * selector modifier options (`selector-modifier` object data type) that\n\t\t * each of the three built in selector types offer (row, column and cell +\n\t\t * their plural counterparts). For example the Select extension uses this\n\t\t * mechanism to provide an option to select only rows, columns and cells\n\t\t * that have been marked as selected by the end user (`{selected: true}`),\n\t\t * which can be used in conjunction with the existing built in selector\n\t\t * options.\n\t\t *\n\t\t * Each property is an array to which functions can be pushed. The functions\n\t\t * take three attributes:\n\t\t *\n\t\t * * Settings object for the host table\n\t\t * * Options object (`selector-modifier` object type)\n\t\t * * Array of selected item indexes\n\t\t *\n\t\t * The return is an array of the resulting item indexes after the custom\n\t\t * selector has been applied.\n\t\t *\n\t\t *  @type object\n\t\t */\n\t\tselector: {\n\t\t\tcell: [],\n\t\t\tcolumn: [],\n\t\t\trow: []\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Internal functions, exposed for used in plug-ins.\n\t\t * \n\t\t * Please note that you should not need to use the internal methods for\n\t\t * anything other than a plug-in (and even then, try to avoid if possible).\n\t\t * The internal function may change between releases.\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\tinternal: {},\n\t\n\t\n\t\t/**\n\t\t * Legacy configuration options. Enable and disable legacy options that\n\t\t * are available in DataTables.\n\t\t *\n\t\t *  @type object\n\t\t */\n\t\tlegacy: {\n\t\t\t/**\n\t\t\t * Enable / disable DataTables 1.9 compatible server-side processing\n\t\t\t * requests\n\t\t\t *\n\t\t\t *  @type boolean\n\t\t\t *  @default null\n\t\t\t */\n\t\t\tajax: null\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Pagination plug-in methods.\n\t\t * \n\t\t * Each entry in this object is a function and defines which buttons should\n\t\t * be shown by the pagination rendering method that is used for the table:\n\t\t * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the\n\t\t * buttons are displayed in the document, while the functions here tell it\n\t\t * what buttons to display. This is done by returning an array of button\n\t\t * descriptions (what each button will do).\n\t\t *\n\t\t * Pagination types (the four built in options and any additional plug-in\n\t\t * options defined here) can be used through the `paginationType`\n\t\t * initialisation parameter.\n\t\t *\n\t\t * The functions defined take two parameters:\n\t\t *\n\t\t * 1. `{int} page` The current page index\n\t\t * 2. `{int} pages` The number of pages in the table\n\t\t *\n\t\t * Each function is expected to return an array where each element of the\n\t\t * array can be one of:\n\t\t *\n\t\t * * `first` - Jump to first page when activated\n\t\t * * `last` - Jump to last page when activated\n\t\t * * `previous` - Show previous page when activated\n\t\t * * `next` - Show next page when activated\n\t\t * * `{int}` - Show page of the index given\n\t\t * * `{array}` - A nested array containing the above elements to add a\n\t\t *   containing 'DIV' element (might be useful for styling).\n\t\t *\n\t\t * Note that DataTables v1.9- used this object slightly differently whereby\n\t\t * an object with two functions would be defined for each plug-in. That\n\t\t * ability is still supported by DataTables 1.10+ to provide backwards\n\t\t * compatibility, but this option of use is now decremented and no longer\n\t\t * documented in DataTables 1.10+.\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t *\n\t\t *  @example\n\t\t *    // Show previous, next and current page buttons only\n\t\t *    $.fn.dataTableExt.oPagination.current = function ( page, pages ) {\n\t\t *      return [ 'previous', page, 'next' ];\n\t\t *    };\n\t\t */\n\t\tpager: {},\n\t\n\t\n\t\trenderer: {\n\t\t\tpageButton: {},\n\t\t\theader: {}\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Ordering plug-ins - custom data source\n\t\t * \n\t\t * The extension options for ordering of data available here is complimentary\n\t\t * to the default type based ordering that DataTables typically uses. It\n\t\t * allows much greater control over the the data that is being used to\n\t\t * order a column, but is necessarily therefore more complex.\n\t\t * \n\t\t * This type of ordering is useful if you want to do ordering based on data\n\t\t * live from the DOM (for example the contents of an 'input' element) rather\n\t\t * than just the static string that DataTables knows of.\n\t\t * \n\t\t * The way these plug-ins work is that you create an array of the values you\n\t\t * wish to be ordering for the column in question and then return that\n\t\t * array. The data in the array much be in the index order of the rows in\n\t\t * the table (not the currently ordering order!). Which order data gathering\n\t\t * function is run here depends on the `dt-init columns.orderDataType`\n\t\t * parameter that is used for the column (if any).\n\t\t *\n\t\t * The functions defined take two parameters:\n\t\t *\n\t\t * 1. `{object}` DataTables settings object: see\n\t\t *    {@link DataTable.models.oSettings}\n\t\t * 2. `{int}` Target column index\n\t\t *\n\t\t * Each function is expected to return an array:\n\t\t *\n\t\t * * `{array}` Data for the column to be ordering upon\n\t\t *\n\t\t *  @type array\n\t\t *\n\t\t *  @example\n\t\t *    // Ordering using `input` node values\n\t\t *    $.fn.dataTable.ext.order['dom-text'] = function  ( settings, col )\n\t\t *    {\n\t\t *      return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {\n\t\t *        return $('input', td).val();\n\t\t *      } );\n\t\t *    }\n\t\t */\n\t\torder: {},\n\t\n\t\n\t\t/**\n\t\t * Type based plug-ins.\n\t\t *\n\t\t * Each column in DataTables has a type assigned to it, either by automatic\n\t\t * detection or by direct assignment using the `type` option for the column.\n\t\t * The type of a column will effect how it is ordering and search (plug-ins\n\t\t * can also make use of the column type if required).\n\t\t *\n\t\t * @namespace\n\t\t */\n\t\ttype: {\n\t\t\t/**\n\t\t\t * Type detection functions.\n\t\t\t *\n\t\t\t * The functions defined in this object are used to automatically detect\n\t\t\t * a column's type, making initialisation of DataTables super easy, even\n\t\t\t * when complex data is in the table.\n\t\t\t *\n\t\t\t * The functions defined take two parameters:\n\t\t\t *\n\t\t     *  1. `{*}` Data from the column cell to be analysed\n\t\t     *  2. `{settings}` DataTables settings object. This can be used to\n\t\t     *     perform context specific type detection - for example detection\n\t\t     *     based on language settings such as using a comma for a decimal\n\t\t     *     place. Generally speaking the options from the settings will not\n\t\t     *     be required\n\t\t\t *\n\t\t\t * Each function is expected to return:\n\t\t\t *\n\t\t\t * * `{string|null}` Data type detected, or null if unknown (and thus\n\t\t\t *   pass it on to the other type detection functions.\n\t\t\t *\n\t\t\t *  @type array\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Currency type detection plug-in:\n\t\t\t *    $.fn.dataTable.ext.type.detect.push(\n\t\t\t *      function ( data, settings ) {\n\t\t\t *        // Check the numeric part\n\t\t\t *        if ( ! data.substring(1).match(/[0-9]/) ) {\n\t\t\t *          return null;\n\t\t\t *        }\n\t\t\t *\n\t\t\t *        // Check prefixed by currency\n\t\t\t *        if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {\n\t\t\t *          return 'currency';\n\t\t\t *        }\n\t\t\t *        return null;\n\t\t\t *      }\n\t\t\t *    );\n\t\t\t */\n\t\t\tdetect: [],\n\t\n\t\n\t\t\t/**\n\t\t\t * Type based search formatting.\n\t\t\t *\n\t\t\t * The type based searching functions can be used to pre-format the\n\t\t\t * data to be search on. For example, it can be used to strip HTML\n\t\t\t * tags or to de-format telephone numbers for numeric only searching.\n\t\t\t *\n\t\t\t * Note that is a search is not defined for a column of a given type,\n\t\t\t * no search formatting will be performed.\n\t\t\t * \n\t\t\t * Pre-processing of searching data plug-ins - When you assign the sType\n\t\t\t * for a column (or have it automatically detected for you by DataTables\n\t\t\t * or a type detection plug-in), you will typically be using this for\n\t\t\t * custom sorting, but it can also be used to provide custom searching\n\t\t\t * by allowing you to pre-processing the data and returning the data in\n\t\t\t * the format that should be searched upon. This is done by adding\n\t\t\t * functions this object with a parameter name which matches the sType\n\t\t\t * for that target column. This is the corollary of <i>afnSortData</i>\n\t\t\t * for searching data.\n\t\t\t *\n\t\t\t * The functions defined take a single parameter:\n\t\t\t *\n\t\t     *  1. `{*}` Data from the column cell to be prepared for searching\n\t\t\t *\n\t\t\t * Each function is expected to return:\n\t\t\t *\n\t\t\t * * `{string|null}` Formatted string that will be used for the searching.\n\t\t\t *\n\t\t\t *  @type object\n\t\t\t *  @default {}\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {\n\t\t\t *      return d.replace(/\\n/g,\" \").replace( /<.*?>/g, \"\" );\n\t\t\t *    }\n\t\t\t */\n\t\t\tsearch: {},\n\t\n\t\n\t\t\t/**\n\t\t\t * Type based ordering.\n\t\t\t *\n\t\t\t * The column type tells DataTables what ordering to apply to the table\n\t\t\t * when a column is sorted upon. The order for each type that is defined,\n\t\t\t * is defined by the functions available in this object.\n\t\t\t *\n\t\t\t * Each ordering option can be described by three properties added to\n\t\t\t * this object:\n\t\t\t *\n\t\t\t * * `{type}-pre` - Pre-formatting function\n\t\t\t * * `{type}-asc` - Ascending order function\n\t\t\t * * `{type}-desc` - Descending order function\n\t\t\t *\n\t\t\t * All three can be used together, only `{type}-pre` or only\n\t\t\t * `{type}-asc` and `{type}-desc` together. It is generally recommended\n\t\t\t * that only `{type}-pre` is used, as this provides the optimal\n\t\t\t * implementation in terms of speed, although the others are provided\n\t\t\t * for compatibility with existing Javascript sort functions.\n\t\t\t *\n\t\t\t * `{type}-pre`: Functions defined take a single parameter:\n\t\t\t *\n\t\t     *  1. `{*}` Data from the column cell to be prepared for ordering\n\t\t\t *\n\t\t\t * And return:\n\t\t\t *\n\t\t\t * * `{*}` Data to be sorted upon\n\t\t\t *\n\t\t\t * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort\n\t\t\t * functions, taking two parameters:\n\t\t\t *\n\t\t     *  1. `{*}` Data to compare to the second parameter\n\t\t     *  2. `{*}` Data to compare to the first parameter\n\t\t\t *\n\t\t\t * And returning:\n\t\t\t *\n\t\t\t * * `{*}` Ordering match: <0 if first parameter should be sorted lower\n\t\t\t *   than the second parameter, ===0 if the two parameters are equal and\n\t\t\t *   >0 if the first parameter should be sorted height than the second\n\t\t\t *   parameter.\n\t\t\t * \n\t\t\t *  @type object\n\t\t\t *  @default {}\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Numeric ordering of formatted numbers with a pre-formatter\n\t\t\t *    $.extend( $.fn.dataTable.ext.type.order, {\n\t\t\t *      \"string-pre\": function(x) {\n\t\t\t *        a = (a === \"-\" || a === \"\") ? 0 : a.replace( /[^\\d\\-\\.]/g, \"\" );\n\t\t\t *        return parseFloat( a );\n\t\t\t *      }\n\t\t\t *    } );\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Case-sensitive string ordering, with no pre-formatting method\n\t\t\t *    $.extend( $.fn.dataTable.ext.order, {\n\t\t\t *      \"string-case-asc\": function(x,y) {\n\t\t\t *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));\n\t\t\t *      },\n\t\t\t *      \"string-case-desc\": function(x,y) {\n\t\t\t *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));\n\t\t\t *      }\n\t\t\t *    } );\n\t\t\t */\n\t\t\torder: {}\n\t\t},\n\t\n\t\t/**\n\t\t * Unique DataTables instance counter\n\t\t *\n\t\t * @type int\n\t\t * @private\n\t\t */\n\t\t_unique: 0,\n\t\n\t\n\t\t//\n\t\t// Depreciated\n\t\t// The following properties are retained for backwards compatibility only.\n\t\t// The should not be used in new projects and will be removed in a future\n\t\t// version\n\t\t//\n\t\n\t\t/**\n\t\t * Version check function.\n\t\t *  @type function\n\t\t *  @depreciated Since 1.10\n\t\t */\n\t\tfnVersionCheck: DataTable.fnVersionCheck,\n\t\n\t\n\t\t/**\n\t\t * Index for what 'this' index API functions should use\n\t\t *  @type int\n\t\t *  @deprecated Since v1.10\n\t\t */\n\t\tiApiIndex: 0,\n\t\n\t\n\t\t/**\n\t\t * jQuery UI class container\n\t\t *  @type object\n\t\t *  @deprecated Since v1.10\n\t\t */\n\t\toJUIClasses: {},\n\t\n\t\n\t\t/**\n\t\t * Software version\n\t\t *  @type string\n\t\t *  @deprecated Since v1.10\n\t\t */\n\t\tsVersion: DataTable.version\n\t};\n\t\n\t\n\t//\n\t// Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts\n\t//\n\t$.extend( _ext, {\n\t\tafnFiltering: _ext.search,\n\t\taTypes:       _ext.type.detect,\n\t\tofnSearch:    _ext.type.search,\n\t\toSort:        _ext.type.order,\n\t\tafnSortData:  _ext.order,\n\t\taoFeatures:   _ext.feature,\n\t\toApi:         _ext.internal,\n\t\toStdClasses:  _ext.classes,\n\t\toPagination:  _ext.pager\n\t} );\n\t\n\t\n\t$.extend( DataTable.ext.classes, {\n\t\t\"sTable\": \"dataTable\",\n\t\t\"sNoFooter\": \"no-footer\",\n\t\n\t\t/* Paging buttons */\n\t\t\"sPageButton\": \"paginate_button\",\n\t\t\"sPageButtonActive\": \"current\",\n\t\t\"sPageButtonDisabled\": \"disabled\",\n\t\n\t\t/* Striping classes */\n\t\t\"sStripeOdd\": \"odd\",\n\t\t\"sStripeEven\": \"even\",\n\t\n\t\t/* Empty row */\n\t\t\"sRowEmpty\": \"dataTables_empty\",\n\t\n\t\t/* Features */\n\t\t\"sWrapper\": \"dataTables_wrapper\",\n\t\t\"sFilter\": \"dataTables_filter\",\n\t\t\"sInfo\": \"dataTables_info\",\n\t\t\"sPaging\": \"dataTables_paginate paging_\", /* Note that the type is postfixed */\n\t\t\"sLength\": \"dataTables_length\",\n\t\t\"sProcessing\": \"dataTables_processing\",\n\t\n\t\t/* Sorting */\n\t\t\"sSortAsc\": \"sorting_asc\",\n\t\t\"sSortDesc\": \"sorting_desc\",\n\t\t\"sSortable\": \"sorting\", /* Sortable in both directions */\n\t\t\"sSortableAsc\": \"sorting_desc_disabled\",\n\t\t\"sSortableDesc\": \"sorting_asc_disabled\",\n\t\t\"sSortableNone\": \"sorting_disabled\",\n\t\t\"sSortColumn\": \"sorting_\", /* Note that an int is postfixed for the sorting order */\n\t\n\t\t/* Filtering */\n\t\t\"sFilterInput\": \"\",\n\t\n\t\t/* Page length */\n\t\t\"sLengthSelect\": \"\",\n\t\n\t\t/* Scrolling */\n\t\t\"sScrollWrapper\": \"dataTables_scroll\",\n\t\t\"sScrollHead\": \"dataTables_scrollHead\",\n\t\t\"sScrollHeadInner\": \"dataTables_scrollHeadInner\",\n\t\t\"sScrollBody\": \"dataTables_scrollBody\",\n\t\t\"sScrollFoot\": \"dataTables_scrollFoot\",\n\t\t\"sScrollFootInner\": \"dataTables_scrollFootInner\",\n\t\n\t\t/* Misc */\n\t\t\"sHeaderTH\": \"\",\n\t\t\"sFooterTH\": \"\",\n\t\n\t\t// Deprecated\n\t\t\"sSortJUIAsc\": \"\",\n\t\t\"sSortJUIDesc\": \"\",\n\t\t\"sSortJUI\": \"\",\n\t\t\"sSortJUIAscAllowed\": \"\",\n\t\t\"sSortJUIDescAllowed\": \"\",\n\t\t\"sSortJUIWrapper\": \"\",\n\t\t\"sSortIcon\": \"\",\n\t\t\"sJUIHeader\": \"\",\n\t\t\"sJUIFooter\": \"\"\n\t} );\n\t\n\t\n\tvar extPagination = DataTable.ext.pager;\n\t\n\tfunction _numbers ( page, pages ) {\n\t\tvar\n\t\t\tnumbers = [],\n\t\t\tbuttons = extPagination.numbers_length,\n\t\t\thalf = Math.floor( buttons / 2 ),\n\t\t\ti = 1;\n\t\n\t\tif ( pages <= buttons ) {\n\t\t\tnumbers = _range( 0, pages );\n\t\t}\n\t\telse if ( page <= half ) {\n\t\t\tnumbers = _range( 0, buttons-2 );\n\t\t\tnumbers.push( 'ellipsis' );\n\t\t\tnumbers.push( pages-1 );\n\t\t}\n\t\telse if ( page >= pages - 1 - half ) {\n\t\t\tnumbers = _range( pages-(buttons-2), pages );\n\t\t\tnumbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6\n\t\t\tnumbers.splice( 0, 0, 0 );\n\t\t}\n\t\telse {\n\t\t\tnumbers = _range( page-half+2, page+half-1 );\n\t\t\tnumbers.push( 'ellipsis' );\n\t\t\tnumbers.push( pages-1 );\n\t\t\tnumbers.splice( 0, 0, 'ellipsis' );\n\t\t\tnumbers.splice( 0, 0, 0 );\n\t\t}\n\t\n\t\tnumbers.DT_el = 'span';\n\t\treturn numbers;\n\t}\n\t\n\t\n\t$.extend( extPagination, {\n\t\tsimple: function ( page, pages ) {\n\t\t\treturn [ 'previous', 'next' ];\n\t\t},\n\t\n\t\tfull: function ( page, pages ) {\n\t\t\treturn [  'first', 'previous', 'next', 'last' ];\n\t\t},\n\t\n\t\tnumbers: function ( page, pages ) {\n\t\t\treturn [ _numbers(page, pages) ];\n\t\t},\n\t\n\t\tsimple_numbers: function ( page, pages ) {\n\t\t\treturn [ 'previous', _numbers(page, pages), 'next' ];\n\t\t},\n\t\n\t\tfull_numbers: function ( page, pages ) {\n\t\t\treturn [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];\n\t\t},\n\t\t\n\t\tfirst_last_numbers: function (page, pages) {\n\t \t\treturn ['first', _numbers(page, pages), 'last'];\n\t \t},\n\t\n\t\t// For testing and plug-ins to use\n\t\t_numbers: _numbers,\n\t\n\t\t// Number of number buttons (including ellipsis) to show. _Must be odd!_\n\t\tnumbers_length: 7\n\t} );\n\t\n\t\n\t$.extend( true, DataTable.ext.renderer, {\n\t\tpageButton: {\n\t\t\t_: function ( settings, host, idx, buttons, page, pages ) {\n\t\t\t\tvar classes = settings.oClasses;\n\t\t\t\tvar lang = settings.oLanguage.oPaginate;\n\t\t\t\tvar aria = settings.oLanguage.oAria.paginate || {};\n\t\t\t\tvar btnDisplay, btnClass;\n\t\n\t\t\t\tvar attach = function( container, buttons ) {\n\t\t\t\t\tvar i, ien, node, button, tabIndex;\n\t\t\t\t\tvar disabledClass = classes.sPageButtonDisabled;\n\t\t\t\t\tvar clickHandler = function ( e ) {\n\t\t\t\t\t\t_fnPageChange( settings, e.data.action, true );\n\t\t\t\t\t};\n\t\n\t\t\t\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\t\t\t\tbutton = buttons[i];\n\t\n\t\t\t\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\t\t\t\tvar inner = $( '<'+(button.DT_el || 'div')+'/>' )\n\t\t\t\t\t\t\t\t.appendTo( container );\n\t\t\t\t\t\t\tattach( inner, button );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tbtnDisplay = null;\n\t\t\t\t\t\t\tbtnClass = button;\n\t\t\t\t\t\t\ttabIndex = settings.iTabIndex;\n\t\n\t\t\t\t\t\t\tswitch ( button ) {\n\t\t\t\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\t\t\t\tcontainer.append('<span class=\"ellipsis\">&#x2026;</span>');\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'first':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\n\t\t\t\t\t\t\t\t\tif ( page === 0 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\n\t\t\t\t\t\t\t\t\tif ( page === 0 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'next':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\n\t\t\t\t\t\t\t\t\tif ( pages === 0 || page === pages-1 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'last':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\n\t\t\t\t\t\t\t\t\tif ( pages === 0 || page === pages-1 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tbtnDisplay = settings.fnFormatNumber( button + 1 );\n\t\t\t\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t\t\t\tclasses.sPageButtonActive : '';\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\tif ( btnDisplay !== null ) {\n\t\t\t\t\t\t\t\tvar tag = settings.oInit.pagingTag || 'a';\n\t\t\t\t\t\t\t\tvar disabled = btnClass.indexOf(disabledClass) !== -1;\n\t\t\t\n\t\n\t\t\t\t\t\t\t\tnode = $('<'+tag+'>', {\n\t\t\t\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t\t\t'aria-current': btnClass === classes.sPageButtonActive ? 'page' : null,\n\t\t\t\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t\t\t\t'tabindex': tabIndex,\n\t\t\t\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t\t\t\t.appendTo( container );\n\t\n\t\t\t\t\t\t\t\t_fnBindAction(\n\t\t\t\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\n\t\t\t\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t\t\t\t// inside an iframe or frame. Try / catch the error. Not good for\n\t\t\t\t// accessibility, but neither are frames.\n\t\t\t\tvar activeEl;\n\t\n\t\t\t\ttry {\n\t\t\t\t\t// Because this approach is destroying and recreating the paging\n\t\t\t\t\t// elements, focus is lost on the select button which is bad for\n\t\t\t\t\t// accessibility. So we want to restore focus once the draw has\n\t\t\t\t\t// completed\n\t\t\t\t\tactiveEl = $(host).find(document.activeElement).data('dt-idx');\n\t\t\t\t}\n\t\t\t\tcatch (e) {}\n\t\n\t\t\t\tattach( $(host).empty(), buttons );\n\t\n\t\t\t\tif ( activeEl !== undefined ) {\n\t\t\t\t\t$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} );\n\t\n\t\n\t\n\t// Built in type detection. See model.ext.aTypes for information about\n\t// what is required from this methods.\n\t$.extend( DataTable.ext.type.detect, [\n\t\t// Plain numbers - first since V8 detects some plain numbers as dates\n\t\t// e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _isNumber( d, decimal ) ? 'num'+decimal : null;\n\t\t},\n\t\n\t\t// Dates (only those recognised by the browser's Date.parse)\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\t// V8 tries _very_ hard to make a string passed into `Date.parse()`\n\t\t\t// valid, so we need to use a regex to restrict date formats. Use a\n\t\t\t// plug-in for anything other than ISO8601 style strings\n\t\t\tif ( d && !(d instanceof Date) && ! _re_date.test(d) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tvar parsed = Date.parse(d);\n\t\t\treturn (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;\n\t\t},\n\t\n\t\t// Formatted numbers\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;\n\t\t},\n\t\n\t\t// HTML numeric\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;\n\t\t},\n\t\n\t\t// HTML numeric, formatted\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;\n\t\t},\n\t\n\t\t// HTML (this is strict checking - there must be html)\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\treturn _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?\n\t\t\t\t'html' : null;\n\t\t}\n\t] );\n\t\n\t\n\t\n\t// Filter formatting functions. See model.ext.ofnSearch for information about\n\t// what is required from these methods.\n\t// \n\t// Note that additional search methods are added for the html numbers and\n\t// html formatted numbers by `_addNumericSort()` when we know what the decimal\n\t// place is\n\t\n\t\n\t$.extend( DataTable.ext.type.search, {\n\t\thtml: function ( data ) {\n\t\t\treturn _empty(data) ?\n\t\t\t\tdata :\n\t\t\t\ttypeof data === 'string' ?\n\t\t\t\t\tdata\n\t\t\t\t\t\t.replace( _re_new_lines, \" \" )\n\t\t\t\t\t\t.replace( _re_html, \"\" ) :\n\t\t\t\t\t'';\n\t\t},\n\t\n\t\tstring: function ( data ) {\n\t\t\treturn _empty(data) ?\n\t\t\t\tdata :\n\t\t\t\ttypeof data === 'string' ?\n\t\t\t\t\tdata.replace( _re_new_lines, \" \" ) :\n\t\t\t\t\tdata;\n\t\t}\n\t} );\n\t\n\t\n\t\n\tvar __numericReplace = function ( d, decimalPlace, re1, re2 ) {\n\t\tif ( d !== 0 && (!d || d === '-') ) {\n\t\t\treturn -Infinity;\n\t\t}\n\t\t\n\t\tvar type = typeof d;\n\t\n\t\tif (type === 'number' || type === 'bigint') {\n\t\t\treturn d;\n\t\t}\n\t\n\t\t// If a decimal place other than `.` is used, it needs to be given to the\n\t\t// function so we can detect it and replace with a `.` which is the only\n\t\t// decimal place Javascript recognises - it is not locale aware.\n\t\tif ( decimalPlace ) {\n\t\t\td = _numToDecimal( d, decimalPlace );\n\t\t}\n\t\n\t\tif ( d.replace ) {\n\t\t\tif ( re1 ) {\n\t\t\t\td = d.replace( re1, '' );\n\t\t\t}\n\t\n\t\t\tif ( re2 ) {\n\t\t\t\td = d.replace( re2, '' );\n\t\t\t}\n\t\t}\n\t\n\t\treturn d * 1;\n\t};\n\t\n\t\n\t// Add the numeric 'deformatting' functions for sorting and search. This is done\n\t// in a function to provide an easy ability for the language options to add\n\t// additional methods if a non-period decimal place is used.\n\tfunction _addNumericSort ( decimalPlace ) {\n\t\t$.each(\n\t\t\t{\n\t\t\t\t// Plain numbers\n\t\t\t\t\"num\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace );\n\t\t\t\t},\n\t\n\t\t\t\t// Formatted numbers\n\t\t\t\t\"num-fmt\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace, _re_formatted_numeric );\n\t\t\t\t},\n\t\n\t\t\t\t// HTML numeric\n\t\t\t\t\"html-num\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace, _re_html );\n\t\t\t\t},\n\t\n\t\t\t\t// HTML numeric, formatted\n\t\t\t\t\"html-num-fmt\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );\n\t\t\t\t}\n\t\t\t},\n\t\t\tfunction ( key, fn ) {\n\t\t\t\t// Add the ordering method\n\t\t\t\t_ext.type.order[ key+decimalPlace+'-pre' ] = fn;\n\t\n\t\t\t\t// For HTML types add a search formatter that will strip the HTML\n\t\t\t\tif ( key.match(/^html\\-/) ) {\n\t\t\t\t\t_ext.type.search[ key+decimalPlace ] = _ext.type.search.html;\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}\n\t\n\t\n\t// Default sort methods\n\t$.extend( _ext.type.order, {\n\t\t// Dates\n\t\t\"date-pre\": function ( d ) {\n\t\t\tvar ts = Date.parse( d );\n\t\t\treturn isNaN(ts) ? -Infinity : ts;\n\t\t},\n\t\n\t\t// html\n\t\t\"html-pre\": function ( a ) {\n\t\t\treturn _empty(a) ?\n\t\t\t\t'' :\n\t\t\t\ta.replace ?\n\t\t\t\t\ta.replace( /<.*?>/g, \"\" ).toLowerCase() :\n\t\t\t\t\ta+'';\n\t\t},\n\t\n\t\t// string\n\t\t\"string-pre\": function ( a ) {\n\t\t\t// This is a little complex, but faster than always calling toString,\n\t\t\t// http://jsperf.com/tostring-v-check\n\t\t\treturn _empty(a) ?\n\t\t\t\t'' :\n\t\t\t\ttypeof a === 'string' ?\n\t\t\t\t\ta.toLowerCase() :\n\t\t\t\t\t! a.toString ?\n\t\t\t\t\t\t'' :\n\t\t\t\t\t\ta.toString();\n\t\t},\n\t\n\t\t// string-asc and -desc are retained only for compatibility with the old\n\t\t// sort methods\n\t\t\"string-asc\": function ( x, y ) {\n\t\t\treturn ((x < y) ? -1 : ((x > y) ? 1 : 0));\n\t\t},\n\t\n\t\t\"string-desc\": function ( x, y ) {\n\t\t\treturn ((x < y) ? 1 : ((x > y) ? -1 : 0));\n\t\t}\n\t} );\n\t\n\t\n\t// Numeric sorting types - order doesn't matter here\n\t_addNumericSort( '' );\n\t\n\t\n\t$.extend( true, DataTable.ext.renderer, {\n\t\theader: {\n\t\t\t_: function ( settings, cell, column, classes ) {\n\t\t\t\t// No additional mark-up required\n\t\t\t\t// Attach a sort listener to update on sort - note that using the\n\t\t\t\t// `DT` namespace will allow the event to be removed automatically\n\t\t\t\t// on destroy, while the `dt` namespaced event is the one we are\n\t\t\t\t// listening for\n\t\t\t\t$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {\n\t\t\t\t\tif ( settings !== ctx ) { // need to check this this is the host\n\t\t\t\t\t\treturn;               // table, not a nested one\n\t\t\t\t\t}\n\t\n\t\t\t\t\tvar colIdx = column.idx;\n\t\n\t\t\t\t\tcell\n\t\t\t\t\t\t.removeClass(\n\t\t\t\t\t\t\tclasses.sSortAsc +' '+\n\t\t\t\t\t\t\tclasses.sSortDesc\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.addClass( columns[ colIdx ] == 'asc' ?\n\t\t\t\t\t\t\tclasses.sSortAsc : columns[ colIdx ] == 'desc' ?\n\t\t\t\t\t\t\t\tclasses.sSortDesc :\n\t\t\t\t\t\t\t\tcolumn.sSortingClass\n\t\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t},\n\t\n\t\t\tjqueryui: function ( settings, cell, column, classes ) {\n\t\t\t\t$('<div/>')\n\t\t\t\t\t.addClass( classes.sSortJUIWrapper )\n\t\t\t\t\t.append( cell.contents() )\n\t\t\t\t\t.append( $('<span/>')\n\t\t\t\t\t\t.addClass( classes.sSortIcon+' '+column.sSortingClassJUI )\n\t\t\t\t\t)\n\t\t\t\t\t.appendTo( cell );\n\t\n\t\t\t\t// Attach a sort listener to update on sort\n\t\t\t\t$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {\n\t\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tvar colIdx = column.idx;\n\t\n\t\t\t\t\tcell\n\t\t\t\t\t\t.removeClass( classes.sSortAsc +\" \"+classes.sSortDesc )\n\t\t\t\t\t\t.addClass( columns[ colIdx ] == 'asc' ?\n\t\t\t\t\t\t\tclasses.sSortAsc : columns[ colIdx ] == 'desc' ?\n\t\t\t\t\t\t\t\tclasses.sSortDesc :\n\t\t\t\t\t\t\t\tcolumn.sSortingClass\n\t\t\t\t\t\t);\n\t\n\t\t\t\t\tcell\n\t\t\t\t\t\t.find( 'span.'+classes.sSortIcon )\n\t\t\t\t\t\t.removeClass(\n\t\t\t\t\t\t\tclasses.sSortJUIAsc +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUIDesc +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUI +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUIAscAllowed +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUIDescAllowed\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.addClass( columns[ colIdx ] == 'asc' ?\n\t\t\t\t\t\t\tclasses.sSortJUIAsc : columns[ colIdx ] == 'desc' ?\n\t\t\t\t\t\t\t\tclasses.sSortJUIDesc :\n\t\t\t\t\t\t\t\tcolumn.sSortingClassJUI\n\t\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} );\n\t\n\t/*\n\t * Public helper functions. These aren't used internally by DataTables, or\n\t * called by any of the options passed into DataTables, but they can be used\n\t * externally by developers working with DataTables. They are helper functions\n\t * to make working with DataTables a little bit easier.\n\t */\n\t\n\tvar __htmlEscapeEntities = function ( d ) {\n\t\tif (Array.isArray(d)) {\n\t\t\td = d.join(',');\n\t\t}\n\t\n\t\treturn typeof d === 'string' ?\n\t\t\td\n\t\t\t\t.replace(/&/g, '&amp;')\n\t\t\t\t.replace(/</g, '&lt;')\n\t\t\t\t.replace(/>/g, '&gt;')\n\t\t\t\t.replace(/\"/g, '&quot;') :\n\t\t\td;\n\t};\n\t\n\t// Common logic for moment, luxon or a date action\n\tfunction __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {\n\t\tif (window.moment) {\n\t\t\treturn dt[momentFn]( arg1 );\n\t\t}\n\t\telse if (window.luxon) {\n\t\t\treturn dt[luxonFn]( arg1 );\n\t\t}\n\t\t\n\t\treturn dateFn ? dt[dateFn]( arg1 ) : dt;\n\t}\n\t\n\t\n\tvar __mlWarning = false;\n\tfunction __mldObj (d, format, locale) {\n\t\tvar dt;\n\t\n\t\tif (window.moment) {\n\t\t\tdt = window.moment.utc( d, format, locale, true );\n\t\n\t\t\tif (! dt.isValid()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\telse if (window.luxon) {\n\t\t\tdt = format && typeof d === 'string'\n\t\t\t\t? window.luxon.DateTime.fromFormat( d, format )\n\t\t\t\t: window.luxon.DateTime.fromISO( d );\n\t\n\t\t\tif (! dt.isValid) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\n\t\t\tdt.setLocale(locale);\n\t\t}\n\t\telse if (! format) {\n\t\t\t// No format given, must be ISO\n\t\t\tdt = new Date(d);\n\t\t}\n\t\telse {\n\t\t\tif (! __mlWarning) {\n\t\t\t\talert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');\n\t\t\t}\n\t\n\t\t\t__mlWarning = true;\n\t\t}\n\t\n\t\treturn dt;\n\t}\n\t\n\t// Wrapper for date, datetime and time which all operate the same way with the exception of\n\t// the output string for auto locale support\n\tfunction __mlHelper (localeString) {\n\t\treturn function ( from, to, locale, def ) {\n\t\t\t// Luxon and Moment support\n\t\t\t// Argument shifting\n\t\t\tif ( arguments.length === 0 ) {\n\t\t\t\tlocale = 'en';\n\t\t\t\tto = null; // means toLocaleString\n\t\t\t\tfrom = null; // means iso8601\n\t\t\t}\n\t\t\telse if ( arguments.length === 1 ) {\n\t\t\t\tlocale = 'en';\n\t\t\t\tto = from;\n\t\t\t\tfrom = null;\n\t\t\t}\n\t\t\telse if ( arguments.length === 2 ) {\n\t\t\t\tlocale = to;\n\t\t\t\tto = from;\n\t\t\t\tfrom = null;\n\t\t\t}\n\t\n\t\t\tvar typeName = 'datetime-' + to;\n\t\n\t\t\t// Add type detection and sorting specific to this date format - we need to be able to identify\n\t\t\t// date type columns as such, rather than as numbers in extensions. Hence the need for this.\n\t\t\tif (! DataTable.ext.type.order[typeName]) {\n\t\t\t\t// The renderer will give the value to type detect as the type!\n\t\t\t\tDataTable.ext.type.detect.unshift(function (d) {\n\t\t\t\t\treturn d === typeName ? typeName : false;\n\t\t\t\t});\n\t\n\t\t\t\t// The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a\n\t\t\t\t// `valueOf` which gives milliseconds epoch\n\t\t\t\tDataTable.ext.type.order[typeName + '-asc'] = function (a, b) {\n\t\t\t\t\tvar x = a.valueOf();\n\t\t\t\t\tvar y = b.valueOf();\n\t\n\t\t\t\t\treturn x === y\n\t\t\t\t\t\t? 0\n\t\t\t\t\t\t: x < y\n\t\t\t\t\t\t\t? -1\n\t\t\t\t\t\t\t: 1;\n\t\t\t\t}\n\t\n\t\t\t\tDataTable.ext.type.order[typeName + '-desc'] = function (a, b) {\n\t\t\t\t\tvar x = a.valueOf();\n\t\t\t\t\tvar y = b.valueOf();\n\t\n\t\t\t\t\treturn x === y\n\t\t\t\t\t\t? 0\n\t\t\t\t\t\t: x > y\n\t\t\t\t\t\t\t? -1\n\t\t\t\t\t\t\t: 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\treturn function ( d, type ) {\n\t\t\t\t// Allow for a default value\n\t\t\t\tif (d === null || d === undefined) {\n\t\t\t\t\tif (def === '--now') {\n\t\t\t\t\t\t// We treat everything as UTC further down, so no changes are\n\t\t\t\t\t\t// made, as such need to get the local date / time as if it were\n\t\t\t\t\t\t// UTC\n\t\t\t\t\t\tvar local = new Date();\n\t\t\t\t\t\td = new Date( Date.UTC(\n\t\t\t\t\t\t\tlocal.getFullYear(), local.getMonth(), local.getDate(),\n\t\t\t\t\t\t\tlocal.getHours(), local.getMinutes(), local.getSeconds()\n\t\t\t\t\t\t) );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\td = '';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\tif (type === 'type') {\n\t\t\t\t\t// Typing uses the type name for fast matching\n\t\t\t\t\treturn typeName;\n\t\t\t\t}\n\t\n\t\t\t\tif (d === '') {\n\t\t\t\t\treturn type !== 'sort'\n\t\t\t\t\t\t? ''\n\t\t\t\t\t\t: __mldObj('0000-01-01 00:00:00', null, locale);\n\t\t\t\t}\n\t\n\t\t\t\t// Shortcut. If `from` and `to` are the same, we are using the renderer to\n\t\t\t\t// format for ordering, not display - its already in the display format.\n\t\t\t\tif ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {\n\t\t\t\t\treturn d;\n\t\t\t\t}\n\t\n\t\t\t\tvar dt = __mldObj(d, from, locale);\n\t\n\t\t\t\tif (dt === null) {\n\t\t\t\t\treturn d;\n\t\t\t\t}\n\t\n\t\t\t\tif (type === 'sort') {\n\t\t\t\t\treturn dt;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar formatted = to === null\n\t\t\t\t\t? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()\n\t\t\t\t\t: __mld(dt, 'format', 'toFormat', 'toISOString', to);\n\t\n\t\t\t\t// XSS protection\n\t\t\t\treturn type === 'display' ?\n\t\t\t\t\t__htmlEscapeEntities( formatted ) :\n\t\t\t\t\tformatted;\n\t\t\t};\n\t\t}\n\t}\n\t\n\t// Based on locale, determine standard number formatting\n\t// Fallback for legacy browsers is US English\n\tvar __thousands = ',';\n\tvar __decimal = '.';\n\t\n\tif (window.Intl !== undefined) {\n\t\ttry {\n\t\t\tvar num = new Intl.NumberFormat().formatToParts(100000.1);\n\t\t\n\t\t\tfor (var i=0 ; i<num.length ; i++) {\n\t\t\t\tif (num[i].type === 'group') {\n\t\t\t\t\t__thousands = num[i].value;\n\t\t\t\t}\n\t\t\t\telse if (num[i].type === 'decimal') {\n\t\t\t\t\t__decimal = num[i].value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (e) {\n\t\t\t// noop\n\t\t}\n\t}\n\t\n\t// Formatted date time detection - use by declaring the formats you are going to use\n\tDataTable.datetime = function ( format, locale ) {\n\t\tvar typeName = 'datetime-detect-' + format;\n\t\n\t\tif (! locale) {\n\t\t\tlocale = 'en';\n\t\t}\n\t\n\t\tif (! DataTable.ext.type.order[typeName]) {\n\t\t\tDataTable.ext.type.detect.unshift(function (d) {\n\t\t\t\tvar dt = __mldObj(d, format, locale);\n\t\t\t\treturn d === '' || dt ? typeName : false;\n\t\t\t});\n\t\n\t\t\tDataTable.ext.type.order[typeName + '-pre'] = function (d) {\n\t\t\t\treturn __mldObj(d, format, locale) || 0;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * Helpers for `columns.render`.\n\t *\n\t * The options defined here can be used with the `columns.render` initialisation\n\t * option to provide a display renderer. The following functions are defined:\n\t *\n\t * * `number` - Will format numeric data (defined by `columns.data`) for\n\t *   display, retaining the original unformatted data for sorting and filtering.\n\t *   It takes 5 parameters:\n\t *   * `string` - Thousands grouping separator\n\t *   * `string` - Decimal point indicator\n\t *   * `integer` - Number of decimal points to show\n\t *   * `string` (optional) - Prefix.\n\t *   * `string` (optional) - Postfix (/suffix).\n\t * * `text` - Escape HTML to help prevent XSS attacks. It has no optional\n\t *   parameters.\n\t *\n\t * @example\n\t *   // Column definition using the number renderer\n\t *   {\n\t *     data: \"salary\",\n\t *     render: $.fn.dataTable.render.number( '\\'', '.', 0, '$' )\n\t *   }\n\t *\n\t * @namespace\n\t */\n\tDataTable.render = {\n\t\tdate: __mlHelper('toLocaleDateString'),\n\t\tdatetime: __mlHelper('toLocaleString'),\n\t\ttime: __mlHelper('toLocaleTimeString'),\n\t\tnumber: function ( thousands, decimal, precision, prefix, postfix ) {\n\t\t\t// Auto locale detection\n\t\t\tif (thousands === null || thousands === undefined) {\n\t\t\t\tthousands = __thousands;\n\t\t\t}\n\t\n\t\t\tif (decimal === null || decimal === undefined) {\n\t\t\t\tdecimal = __decimal;\n\t\t\t}\n\t\n\t\t\treturn {\n\t\t\t\tdisplay: function ( d ) {\n\t\t\t\t\tif ( typeof d !== 'number' && typeof d !== 'string' ) {\n\t\t\t\t\t\treturn d;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tif (d === '' || d === null) {\n\t\t\t\t\t\treturn d;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tvar negative = d < 0 ? '-' : '';\n\t\t\t\t\tvar flo = parseFloat( d );\n\t\n\t\t\t\t\t// If NaN then there isn't much formatting that we can do - just\n\t\t\t\t\t// return immediately, escaping any HTML (this was supposed to\n\t\t\t\t\t// be a number after all)\n\t\t\t\t\tif ( isNaN( flo ) ) {\n\t\t\t\t\t\treturn __htmlEscapeEntities( d );\n\t\t\t\t\t}\n\t\n\t\t\t\t\tflo = flo.toFixed( precision );\n\t\t\t\t\td = Math.abs( flo );\n\t\n\t\t\t\t\tvar intPart = parseInt( d, 10 );\n\t\t\t\t\tvar floatPart = precision ?\n\t\t\t\t\t\tdecimal+(d - intPart).toFixed( precision ).substring( 2 ):\n\t\t\t\t\t\t'';\n\t\n\t\t\t\t\t// If zero, then can't have a negative prefix\n\t\t\t\t\tif (intPart === 0 && parseFloat(floatPart) === 0) {\n\t\t\t\t\t\tnegative = '';\n\t\t\t\t\t}\n\t\n\t\t\t\t\treturn negative + (prefix||'') +\n\t\t\t\t\t\tintPart.toString().replace(\n\t\t\t\t\t\t\t/\\B(?=(\\d{3})+(?!\\d))/g, thousands\n\t\t\t\t\t\t) +\n\t\t\t\t\t\tfloatPart +\n\t\t\t\t\t\t(postfix||'');\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\n\t\ttext: function () {\n\t\t\treturn {\n\t\t\t\tdisplay: __htmlEscapeEntities,\n\t\t\t\tfilter: __htmlEscapeEntities\n\t\t\t};\n\t\t}\n\t};\n\t\n\t\n\t/*\n\t * This is really a good bit rubbish this method of exposing the internal methods\n\t * publicly... - To be fixed in 2.0 using methods on the prototype\n\t */\n\t\n\t\n\t/**\n\t * Create a wrapper function for exporting an internal functions to an external API.\n\t *  @param {string} fn API function name\n\t *  @returns {function} wrapped function\n\t *  @memberof DataTable#internal\n\t */\n\tfunction _fnExternApiFunc (fn)\n\t{\n\t\treturn function() {\n\t\t\tvar args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(\n\t\t\t\tArray.prototype.slice.call(arguments)\n\t\t\t);\n\t\t\treturn DataTable.ext.internal[fn].apply( this, args );\n\t\t};\n\t}\n\t\n\t\n\t/**\n\t * Reference to internal functions for use by plug-in developers. Note that\n\t * these methods are references to internal functions and are considered to be\n\t * private. If you use these methods, be aware that they are liable to change\n\t * between versions.\n\t *  @namespace\n\t */\n\t$.extend( DataTable.ext.internal, {\n\t\t_fnExternApiFunc: _fnExternApiFunc,\n\t\t_fnBuildAjax: _fnBuildAjax,\n\t\t_fnAjaxUpdate: _fnAjaxUpdate,\n\t\t_fnAjaxParameters: _fnAjaxParameters,\n\t\t_fnAjaxUpdateDraw: _fnAjaxUpdateDraw,\n\t\t_fnAjaxDataSrc: _fnAjaxDataSrc,\n\t\t_fnAddColumn: _fnAddColumn,\n\t\t_fnColumnOptions: _fnColumnOptions,\n\t\t_fnAdjustColumnSizing: _fnAdjustColumnSizing,\n\t\t_fnVisibleToColumnIndex: _fnVisibleToColumnIndex,\n\t\t_fnColumnIndexToVisible: _fnColumnIndexToVisible,\n\t\t_fnVisbleColumns: _fnVisbleColumns,\n\t\t_fnGetColumns: _fnGetColumns,\n\t\t_fnColumnTypes: _fnColumnTypes,\n\t\t_fnApplyColumnDefs: _fnApplyColumnDefs,\n\t\t_fnHungarianMap: _fnHungarianMap,\n\t\t_fnCamelToHungarian: _fnCamelToHungarian,\n\t\t_fnLanguageCompat: _fnLanguageCompat,\n\t\t_fnBrowserDetect: _fnBrowserDetect,\n\t\t_fnAddData: _fnAddData,\n\t\t_fnAddTr: _fnAddTr,\n\t\t_fnNodeToDataIndex: _fnNodeToDataIndex,\n\t\t_fnNodeToColumnIndex: _fnNodeToColumnIndex,\n\t\t_fnGetCellData: _fnGetCellData,\n\t\t_fnSetCellData: _fnSetCellData,\n\t\t_fnSplitObjNotation: _fnSplitObjNotation,\n\t\t_fnGetObjectDataFn: _fnGetObjectDataFn,\n\t\t_fnSetObjectDataFn: _fnSetObjectDataFn,\n\t\t_fnGetDataMaster: _fnGetDataMaster,\n\t\t_fnClearTable: _fnClearTable,\n\t\t_fnDeleteIndex: _fnDeleteIndex,\n\t\t_fnInvalidate: _fnInvalidate,\n\t\t_fnGetRowElements: _fnGetRowElements,\n\t\t_fnCreateTr: _fnCreateTr,\n\t\t_fnBuildHead: _fnBuildHead,\n\t\t_fnDrawHead: _fnDrawHead,\n\t\t_fnDraw: _fnDraw,\n\t\t_fnReDraw: _fnReDraw,\n\t\t_fnAddOptionsHtml: _fnAddOptionsHtml,\n\t\t_fnDetectHeader: _fnDetectHeader,\n\t\t_fnGetUniqueThs: _fnGetUniqueThs,\n\t\t_fnFeatureHtmlFilter: _fnFeatureHtmlFilter,\n\t\t_fnFilterComplete: _fnFilterComplete,\n\t\t_fnFilterCustom: _fnFilterCustom,\n\t\t_fnFilterColumn: _fnFilterColumn,\n\t\t_fnFilter: _fnFilter,\n\t\t_fnFilterCreateSearch: _fnFilterCreateSearch,\n\t\t_fnEscapeRegex: _fnEscapeRegex,\n\t\t_fnFilterData: _fnFilterData,\n\t\t_fnFeatureHtmlInfo: _fnFeatureHtmlInfo,\n\t\t_fnUpdateInfo: _fnUpdateInfo,\n\t\t_fnInfoMacros: _fnInfoMacros,\n\t\t_fnInitialise: _fnInitialise,\n\t\t_fnInitComplete: _fnInitComplete,\n\t\t_fnLengthChange: _fnLengthChange,\n\t\t_fnFeatureHtmlLength: _fnFeatureHtmlLength,\n\t\t_fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,\n\t\t_fnPageChange: _fnPageChange,\n\t\t_fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,\n\t\t_fnProcessingDisplay: _fnProcessingDisplay,\n\t\t_fnFeatureHtmlTable: _fnFeatureHtmlTable,\n\t\t_fnScrollDraw: _fnScrollDraw,\n\t\t_fnApplyToChildren: _fnApplyToChildren,\n\t\t_fnCalculateColumnWidths: _fnCalculateColumnWidths,\n\t\t_fnThrottle: _fnThrottle,\n\t\t_fnConvertToWidth: _fnConvertToWidth,\n\t\t_fnGetWidestNode: _fnGetWidestNode,\n\t\t_fnGetMaxLenString: _fnGetMaxLenString,\n\t\t_fnStringToCss: _fnStringToCss,\n\t\t_fnSortFlatten: _fnSortFlatten,\n\t\t_fnSort: _fnSort,\n\t\t_fnSortAria: _fnSortAria,\n\t\t_fnSortListener: _fnSortListener,\n\t\t_fnSortAttachListener: _fnSortAttachListener,\n\t\t_fnSortingClasses: _fnSortingClasses,\n\t\t_fnSortData: _fnSortData,\n\t\t_fnSaveState: _fnSaveState,\n\t\t_fnLoadState: _fnLoadState,\n\t\t_fnImplementState: _fnImplementState,\n\t\t_fnSettingsFromNode: _fnSettingsFromNode,\n\t\t_fnLog: _fnLog,\n\t\t_fnMap: _fnMap,\n\t\t_fnBindAction: _fnBindAction,\n\t\t_fnCallbackReg: _fnCallbackReg,\n\t\t_fnCallbackFire: _fnCallbackFire,\n\t\t_fnLengthOverflow: _fnLengthOverflow,\n\t\t_fnRenderer: _fnRenderer,\n\t\t_fnDataSource: _fnDataSource,\n\t\t_fnRowAttributes: _fnRowAttributes,\n\t\t_fnExtend: _fnExtend,\n\t\t_fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant\n\t\t                                // in 1.10, so this dead-end function is\n\t\t                                // added to prevent errors\n\t} );\n\t\n\t\n\t// jQuery access\n\t$.fn.dataTable = DataTable;\n\t\n\t// Provide access to the host jQuery object (circular reference)\n\tDataTable.$ = $;\n\t\n\t// Legacy aliases\n\t$.fn.dataTableSettings = DataTable.settings;\n\t$.fn.dataTableExt = DataTable.ext;\n\t\n\t// With a capital `D` we return a DataTables API instance rather than a\n\t// jQuery object\n\t$.fn.DataTable = function ( opts ) {\n\t\treturn $(this).dataTable( opts ).api();\n\t};\n\t\n\t// All properties that are available to $.fn.dataTable should also be\n\t// available on $.fn.DataTable\n\t$.each( DataTable, function ( prop, val ) {\n\t\t$.fn.DataTable[ prop ] = val;\n\t} );\n\n\treturn DataTable;\n}));\n\n\n/*! DataTables Bootstrap 5 integration\n * 2020 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/**\n * DataTables integration for Bootstrap 5. This requires Bootstrap 5 and\n * DataTables 1.10 or newer.\n *\n * This file sets the defaults and adds options to DataTables to style its\n * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap\n * for further information.\n */\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t\"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>\" +\n\t\t\"<'row dt-row'<'col-sm-12'tr>>\" +\n\t\t\"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>\",\n\trenderer: 'bootstrap'\n} );\n\n\n/* Default class modification */\n$.extend( DataTable.ext.classes, {\n\tsWrapper:      \"dataTables_wrapper dt-bootstrap5\",\n\tsFilterInput:  \"form-control form-control-sm\",\n\tsLengthSelect: \"form-select form-select-sm\",\n\tsProcessing:   \"dataTables_processing card\",\n\tsPageButton:   \"paginate_button page-item\"\n} );\n\n\n/* Bootstrap paging button renderer */\nDataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {\n\tvar api     = new DataTable.Api( settings );\n\tvar classes = settings.oClasses;\n\tvar lang    = settings.oLanguage.oPaginate;\n\tvar aria = settings.oLanguage.oAria.paginate || {};\n\tvar btnDisplay, btnClass;\n\n\tvar attach = function( container, buttons ) {\n\t\tvar i, ien, node, button;\n\t\tvar clickHandler = function ( e ) {\n\t\t\te.preventDefault();\n\t\t\tif ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {\n\t\t\t\tapi.page( e.data.action ).draw( 'page' );\n\t\t\t}\n\t\t};\n\n\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\tbutton = buttons[i];\n\n\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\tattach( container, button );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbtnDisplay = '';\n\t\t\t\tbtnClass = '';\n\n\t\t\t\tswitch ( button ) {\n\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\tbtnDisplay = '&#x2026;';\n\t\t\t\t\t\tbtnClass = 'disabled';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'first':\n\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'next':\n\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'last':\n\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbtnDisplay = button + 1;\n\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t'active' : '';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( btnDisplay ) {\n\t\t\t\t\tvar disabled = btnClass.indexOf('disabled') !== -1;\n\n\t\t\t\t\tnode = $('<li>', {\n\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append( $('<a>', {\n\t\t\t\t\t\t\t\t'href': disabled ? null : '#',\n\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t'aria-current': btnClass === 'active' ? 'page' : null,\n\t\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t\t'tabindex': settings.iTabIndex,\n\t\t\t\t\t\t\t\t'class': 'page-link'\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo( container );\n\n\t\t\t\t\tsettings.oApi._fnBindAction(\n\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tvar hostEl = $(host);\n\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t// inside an iframe or frame. \n\tvar activeEl;\n\n\ttry {\n\t\t// Because this approach is destroying and recreating the paging\n\t\t// elements, focus is lost on the select button which is bad for\n\t\t// accessibility. So we want to restore focus once the draw has\n\t\t// completed\n\t\tactiveEl = hostEl.find(document.activeElement).data('dt-idx');\n\t}\n\tcatch (e) {}\n\n\tvar paginationEl = hostEl.children('ul.pagination');\n\n\tif (paginationEl.length) {\n\t\tpaginationEl.empty();\n\t}\n\telse {\n\t\tpaginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination');\n\t}\n\n\tattach(\n\t\tpaginationEl,\n\t\tbuttons\n\t);\n\n\tif ( activeEl !== undefined ) {\n\t\thostEl.find('[data-dt-idx='+activeEl+']').trigger('focus');\n\t}\n};\n\n\nreturn DataTable;\n}));\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.bootstrap.js",
    "content": "/*! DataTables Bootstrap 3 integration\n * ©2011-2015 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/**\n * DataTables integration for Bootstrap 3. This requires Bootstrap 3 and\n * DataTables 1.10 or newer.\n *\n * This file sets the defaults and adds options to DataTables to style its\n * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap\n * for further information.\n */\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t\"<'row'<'col-sm-6'l><'col-sm-6'f>>\" +\n\t\t\"<'row'<'col-sm-12'tr>>\" +\n\t\t\"<'row'<'col-sm-5'i><'col-sm-7'p>>\",\n\trenderer: 'bootstrap'\n} );\n\n\n/* Default class modification */\n$.extend( DataTable.ext.classes, {\n\tsWrapper:      \"dataTables_wrapper form-inline dt-bootstrap\",\n\tsFilterInput:  \"form-control input-sm\",\n\tsLengthSelect: \"form-control input-sm\",\n\tsProcessing:   \"dataTables_processing panel panel-default\"\n} );\n\n\n/* Bootstrap paging button renderer */\nDataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {\n\tvar api     = new DataTable.Api( settings );\n\tvar classes = settings.oClasses;\n\tvar lang    = settings.oLanguage.oPaginate;\n\tvar aria = settings.oLanguage.oAria.paginate || {};\n\tvar btnDisplay, btnClass;\n\n\tvar attach = function( container, buttons ) {\n\t\tvar i, ien, node, button;\n\t\tvar clickHandler = function ( e ) {\n\t\t\te.preventDefault();\n\t\t\tif ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {\n\t\t\t\tapi.page( e.data.action ).draw( 'page' );\n\t\t\t}\n\t\t};\n\n\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\tbutton = buttons[i];\n\n\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\tattach( container, button );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbtnDisplay = '';\n\t\t\t\tbtnClass = '';\n\n\t\t\t\tswitch ( button ) {\n\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\tbtnDisplay = '&#x2026;';\n\t\t\t\t\t\tbtnClass = 'disabled';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'first':\n\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'next':\n\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'last':\n\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbtnDisplay = button + 1;\n\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t'active' : '';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( btnDisplay ) {\n\t\t\t\t\tvar disabled = btnClass.indexOf('disabled') !== -1;\n\n\t\t\t\t\tnode = $('<li>', {\n\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append( $('<a>', {\n\t\t\t\t\t\t\t\t'href': disabled ? null : '#',\n\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t'aria-current': btnClass === 'active' ? 'page' : null,\n\t\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t\t'tabindex': settings.iTabIndex\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo( container );\n\n\t\t\t\t\tsettings.oApi._fnBindAction(\n\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t// inside an iframe or frame. \n\tvar activeEl;\n\n\ttry {\n\t\t// Because this approach is destroying and recreating the paging\n\t\t// elements, focus is lost on the select button which is bad for\n\t\t// accessibility. So we want to restore focus once the draw has\n\t\t// completed\n\t\tactiveEl = $(host).find(document.activeElement).data('dt-idx');\n\t}\n\tcatch (e) {}\n\n\tattach(\n\t\t$(host).empty().html('<ul class=\"pagination\"/>').children('ul'),\n\t\tbuttons\n\t);\n\n\tif ( activeEl !== undefined ) {\n\t\t$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');\n\t}\n};\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.bootstrap4.js",
    "content": "/*! DataTables Bootstrap 4 integration\n * ©2011-2017 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/**\n * DataTables integration for Bootstrap 4. This requires Bootstrap 4 and\n * DataTables 1.10 or newer.\n *\n * This file sets the defaults and adds options to DataTables to style its\n * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap\n * for further information.\n */\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t\"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>\" +\n\t\t\"<'row'<'col-sm-12'tr>>\" +\n\t\t\"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>\",\n\trenderer: 'bootstrap'\n} );\n\n\n/* Default class modification */\n$.extend( DataTable.ext.classes, {\n\tsWrapper:      \"dataTables_wrapper dt-bootstrap4\",\n\tsFilterInput:  \"form-control form-control-sm\",\n\tsLengthSelect: \"custom-select custom-select-sm form-control form-control-sm\",\n\tsProcessing:   \"dataTables_processing card\",\n\tsPageButton:   \"paginate_button page-item\"\n} );\n\n\n/* Bootstrap paging button renderer */\nDataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {\n\tvar api     = new DataTable.Api( settings );\n\tvar classes = settings.oClasses;\n\tvar lang    = settings.oLanguage.oPaginate;\n\tvar aria = settings.oLanguage.oAria.paginate || {};\n\tvar btnDisplay, btnClass;\n\n\tvar attach = function( container, buttons ) {\n\t\tvar i, ien, node, button;\n\t\tvar clickHandler = function ( e ) {\n\t\t\te.preventDefault();\n\t\t\tif ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {\n\t\t\t\tapi.page( e.data.action ).draw( 'page' );\n\t\t\t}\n\t\t};\n\n\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\tbutton = buttons[i];\n\n\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\tattach( container, button );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbtnDisplay = '';\n\t\t\t\tbtnClass = '';\n\n\t\t\t\tswitch ( button ) {\n\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\tbtnDisplay = '&#x2026;';\n\t\t\t\t\t\tbtnClass = 'disabled';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'first':\n\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'next':\n\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'last':\n\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbtnDisplay = button + 1;\n\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t'active' : '';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( btnDisplay ) {\n\t\t\t\t\tvar disabled = btnClass.indexOf('disabled') !== -1;\n\n\t\t\t\t\tnode = $('<li>', {\n\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append( $('<a>', {\n\t\t\t\t\t\t\t\t'href': disabled ? null : '#',\n\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t'aria-current': btnClass === 'active' ? 'page' : null,\n\t\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t\t'tabindex': settings.iTabIndex,\n\t\t\t\t\t\t\t\t'class': 'page-link'\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo( container );\n\n\t\t\t\t\tsettings.oApi._fnBindAction(\n\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t// inside an iframe or frame. \n\tvar activeEl;\n\n\ttry {\n\t\t// Because this approach is destroying and recreating the paging\n\t\t// elements, focus is lost on the select button which is bad for\n\t\t// accessibility. So we want to restore focus once the draw has\n\t\t// completed\n\t\tactiveEl = $(host).find(document.activeElement).data('dt-idx');\n\t}\n\tcatch (e) {}\n\n\tattach(\n\t\t$(host).empty().html('<ul class=\"pagination\"/>').children('ul'),\n\t\tbuttons\n\t);\n\n\tif ( activeEl !== undefined ) {\n\t\t$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');\n\t}\n};\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.bootstrap5.js",
    "content": "/*! DataTables Bootstrap 5 integration\n * 2020 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/**\n * DataTables integration for Bootstrap 5. This requires Bootstrap 5 and\n * DataTables 1.10 or newer.\n *\n * This file sets the defaults and adds options to DataTables to style its\n * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap\n * for further information.\n */\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t\"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>\" +\n\t\t\"<'row dt-row'<'col-sm-12'tr>>\" +\n\t\t\"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>\",\n\trenderer: 'bootstrap'\n} );\n\n\n/* Default class modification */\n$.extend( DataTable.ext.classes, {\n\tsWrapper:      \"dataTables_wrapper dt-bootstrap5\",\n\tsFilterInput:  \"form-control form-control-sm\",\n\tsLengthSelect: \"form-select form-select-sm\",\n\tsProcessing:   \"dataTables_processing card\",\n\tsPageButton:   \"paginate_button page-item\"\n} );\n\n\n/* Bootstrap paging button renderer */\nDataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {\n\tvar api     = new DataTable.Api( settings );\n\tvar classes = settings.oClasses;\n\tvar lang    = settings.oLanguage.oPaginate;\n\tvar aria = settings.oLanguage.oAria.paginate || {};\n\tvar btnDisplay, btnClass;\n\n\tvar attach = function( container, buttons ) {\n\t\tvar i, ien, node, button;\n\t\tvar clickHandler = function ( e ) {\n\t\t\te.preventDefault();\n\t\t\tif ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {\n\t\t\t\tapi.page( e.data.action ).draw( 'page' );\n\t\t\t}\n\t\t};\n\n\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\tbutton = buttons[i];\n\n\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\tattach( container, button );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbtnDisplay = '';\n\t\t\t\tbtnClass = '';\n\n\t\t\t\tswitch ( button ) {\n\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\tbtnDisplay = '&#x2026;';\n\t\t\t\t\t\tbtnClass = 'disabled';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'first':\n\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'next':\n\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'last':\n\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbtnDisplay = button + 1;\n\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t'active' : '';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( btnDisplay ) {\n\t\t\t\t\tvar disabled = btnClass.indexOf('disabled') !== -1;\n\n\t\t\t\t\tnode = $('<li>', {\n\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append( $('<a>', {\n\t\t\t\t\t\t\t\t'href': disabled ? null : '#',\n\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t'aria-current': btnClass === 'active' ? 'page' : null,\n\t\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t\t'tabindex': settings.iTabIndex,\n\t\t\t\t\t\t\t\t'class': 'page-link'\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo( container );\n\n\t\t\t\t\tsettings.oApi._fnBindAction(\n\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tvar hostEl = $(host);\n\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t// inside an iframe or frame. \n\tvar activeEl;\n\n\ttry {\n\t\t// Because this approach is destroying and recreating the paging\n\t\t// elements, focus is lost on the select button which is bad for\n\t\t// accessibility. So we want to restore focus once the draw has\n\t\t// completed\n\t\tactiveEl = hostEl.find(document.activeElement).data('dt-idx');\n\t}\n\tcatch (e) {}\n\n\tvar paginationEl = hostEl.children('ul.pagination');\n\n\tif (paginationEl.length) {\n\t\tpaginationEl.empty();\n\t}\n\telse {\n\t\tpaginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination');\n\t}\n\n\tattach(\n\t\tpaginationEl,\n\t\tbuttons\n\t);\n\n\tif ( activeEl !== undefined ) {\n\t\thostEl.find('[data-dt-idx='+activeEl+']').trigger('focus');\n\t}\n};\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.bulma.js",
    "content": "/*! DataTables Bulma integration\n * ©2020 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t\"<'columns is-gapless is-multiline'\" +\n\t\t\t\"<'column is-half'l>\" +\n\t\t\t\"<'column is-half'f>\" +\n\t\t\t\"<'column is-full'tr>\" +\n\t\t\t\"<'column is-half'i>\" +\n\t\t\t\"<'column is-half'p>\" +\n\t\t\">\",\n\trenderer: 'bulma'\n} );\n\n\n/* Default class modification */\n$.extend( DataTable.ext.classes, {\n\tsWrapper:      \"dataTables_wrapper dt-bulma\",\n\tsFilterInput:  \"input\",\n\tsLengthSelect: \"custom-select custom-select-sm form-control form-control-sm\",\n\tsProcessing:   \"dataTables_processing card\"\n} );\n\n\n/* Bulma paging button renderer */\nDataTable.ext.renderer.pageButton.bulma = function ( settings, host, idx, buttons, page, pages ) {\n\tvar api     = new DataTable.Api( settings );\n\tvar classes = settings.oClasses;\n\tvar lang    = settings.oLanguage.oPaginate;\n\tvar aria = settings.oLanguage.oAria.paginate || {};\n\tvar btnDisplay, btnClass;\n\n\tvar attach = function( container, buttons ) {\n\t\tvar i, ien, node, button, tag, disabled;\n\t\tvar clickHandler = function ( e ) {\n\t\t\te.preventDefault();\n\t\t\tif ( ! $(e.currentTarget.firstChild).attr('disabled') && api.page() != e.data.action ) {\n\t\t\t\tapi.page( e.data.action ).draw( 'page' );\n\t\t\t}\n\t\t};\n\n\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\tbutton = buttons[i];\n\n\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\tattach( container, button );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbtnDisplay = '';\n\t\t\t\tbtnClass = '';\n\t\t\t\ttag = 'a';\n\t\t\t\tdisabled = false;\n\n\t\t\t\tswitch ( button ) {\n\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\tbtnDisplay = '&#x2026;';\n\t\t\t\t\t\tbtnClass = 'pagination-link';\n\t\t\t\t\t\tdisabled = true;\n\t\t\t\t\t\ttag = 'span';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'first':\n\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\t\t\t\t\tbtnClass = button;\n\t\t\t\t\t\tdisabled = page <= 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\t\t\t\t\tbtnClass = button;\n\t\t\t\t\t\tdisabled = page <= 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'next':\n\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\t\t\t\t\tbtnClass = button;\n\t\t\t\t\t\tdisabled = page >= pages - 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'last':\n\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\t\t\t\t\tbtnClass = button;\n\t\t\t\t\t\tdisabled = page >= pages - 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbtnDisplay = button + 1;\n\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t'is-current' : '';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( btnDisplay ) {\n\t\t\t\t\tnode = $('<li>', {\n\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append( $('<' + tag + '>', {\n\t\t\t\t\t\t\t\t'href': disabled ? null : '#',\n\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t'aria-current': btnClass === 'is-current' ? 'page' : null,\n\t\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t\t'tabindex': settings.iTabIndex,\n\t\t\t\t\t\t\t\t'class': 'pagination-link ' + btnClass,\n\t\t\t\t\t\t\t\t'disabled': disabled\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo( container );\n\n\t\t\t\t\tsettings.oApi._fnBindAction(\n\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t// inside an iframe or frame. \n\tvar activeEl;\n\n\ttry {\n\t\t// Because this approach is destroying and recreating the paging\n\t\t// elements, focus is lost on the select button which is bad for\n\t\t// accessibility. So we want to restore focus once the draw has\n\t\t// completed\n\t\tactiveEl = $(host).find(document.activeElement).data('dt-idx');\n\t}\n\tcatch (e) {}\n\n\tvar nav = $('<nav class=\"pagination\" role=\"navigation\" aria-label=\"pagination\"><ul class=\"pagination-list\"></ul></nav>');\n\t$(host).empty().append(nav);\n\n\tattach(nav.find('ul'), buttons);\n\n\tif ( activeEl !== undefined ) {\n\t\t$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');\n\t}\n};\n\n// Javascript enhancements on table initialisation\n$(document).on( 'init.dt', function (e, ctx) {\n\tif ( e.namespace !== 'dt' ) {\n\t\treturn;\n\t}\n\n\tvar api = new $.fn.dataTable.Api( ctx );\n\n\t// Length menu drop down - needs to be wrapped with a div\n\t$( 'div.dataTables_length select', api.table().container() ).wrap('<div class=\"select\">');\n\n\t// Filtering input\n\t// $( 'div.dataTables_filter.ui.input', api.table().container() ).removeClass('input').addClass('form');\n\t// $( 'div.dataTables_filter input', api.table().container() ).wrap( '<span class=\"ui input\" />' );\n} );\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.dataTables.js",
    "content": "/*! DataTables styling integration\n * ©2018 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.foundation.js",
    "content": "/*! DataTables Foundation integration\n * ©2011-2015 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/**\n * DataTables integration for Foundation. This requires Foundation 5 and\n * DataTables 1.10 or newer.\n *\n * This file sets the defaults and adds options to DataTables to style its\n * controls using Foundation. See http://datatables.net/manual/styling/foundation\n * for further information.\n */\n\n// Detect Foundation 5 / 6 as they have different element and class requirements\nvar meta = $('<meta class=\"foundation-mq\"/>').appendTo('head');\nDataTable.ext.foundationVersion = meta.css('font-family').match(/small|medium|large/) ? 6 : 5;\nmeta.remove();\n\n\n$.extend( DataTable.ext.classes, {\n\tsWrapper:    \"dataTables_wrapper dt-foundation\",\n\tsProcessing: \"dataTables_processing panel callout\"\n} );\n\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t\"<'row grid-x'<'small-6 columns cell'l><'small-6 columns cell'f>r>\"+\n\t\t\"t\"+\n\t\t\"<'row grid-x'<'small-6 columns cell'i><'small-6 columns cell'p>>\",\n\trenderer: 'foundation'\n} );\n\n\n/* Page button renderer */\nDataTable.ext.renderer.pageButton.foundation = function ( settings, host, idx, buttons, page, pages ) {\n\tvar api = new DataTable.Api( settings );\n\tvar classes = settings.oClasses;\n\tvar lang = settings.oLanguage.oPaginate;\n\tvar aria = settings.oLanguage.oAria.paginate || {};\n\tvar btnDisplay, btnClass;\n\tvar tag;\n\tvar v5 = DataTable.ext.foundationVersion === 5;\n\n\tvar attach = function( container, buttons ) {\n\t\tvar i, ien, node, button;\n\t\tvar clickHandler = function ( e ) {\n\t\t\te.preventDefault();\n\t\t\tif ( !$(e.currentTarget).hasClass('unavailable') && api.page() != e.data.action ) {\n\t\t\t\tapi.page( e.data.action ).draw( 'page' );\n\t\t\t}\n\t\t};\n\n\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\tbutton = buttons[i];\n\n\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\tattach( container, button );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbtnDisplay = '';\n\t\t\t\tbtnClass = '';\n\t\t\t\ttag = null;\n\n\t\t\t\tswitch ( button ) {\n\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\tbtnDisplay = '&#x2026;';\n\t\t\t\t\t\tbtnClass = 'unavailable disabled';\n\t\t\t\t\t\ttag = null;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'first':\n\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' unavailable disabled');\n\t\t\t\t\t\ttag = page > 0 ? 'a' : null;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' unavailable disabled');\n\t\t\t\t\t\ttag = page > 0 ? 'a' : null;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'next':\n\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' unavailable disabled');\n\t\t\t\t\t\ttag = page < pages-1 ? 'a' : null;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'last':\n\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' unavailable disabled');\n\t\t\t\t\t\ttag = page < pages-1 ? 'a' : null;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbtnDisplay = button + 1;\n\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t'current' : '';\n\t\t\t\t\t\ttag = page === button ?\n\t\t\t\t\t\t\tnull : 'a';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( v5 ) {\n\t\t\t\t\ttag = 'a';\n\t\t\t\t}\n\n\t\t\t\tif ( btnDisplay ) {\n\t\t\t\t\tvar disabled = btnClass.indexOf('disabled') !== -1;\n\n\t\t\t\t\tnode = $('<li>', {\n\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append( tag ?\n\t\t\t\t\t\t\t$('<'+tag+'/>', {\n\t\t\t\t\t\t\t\t'href': disabled ? null : '#',\n\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t'aria-current': btnClass === 'current' ? 'page' : null,\n\t\t\t\t\t\t\t\t'tabindex': settings.iTabIndex,\n\t\t\t\t\t\t\t} ).html( btnDisplay ) :\n\t\t\t\t\t\t\tbtnDisplay\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo( container );\n\n\t\t\t\t\tsettings.oApi._fnBindAction(\n\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tattach(\n\t\t$(host).empty().html('<ul class=\"pagination\"/>').children('ul'),\n\t\tbuttons\n\t);\n};\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.jqueryui.js",
    "content": "/*! DataTables jQuery UI integration\n * ©2011-2014 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/**\n * DataTables integration for jQuery UI. This requires jQuery UI and\n * DataTables 1.10 or newer.\n *\n * This file sets the defaults and adds options to DataTables to style its\n * controls using jQuery UI. See http://datatables.net/manual/styling/jqueryui\n * for further information.\n */\n\nvar toolbar_prefix = 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix ui-corner-';\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t'<\"'+toolbar_prefix+'tl ui-corner-tr\"lfr>'+\n\t\t't'+\n\t\t'<\"'+toolbar_prefix+'bl ui-corner-br\"ip>'\n} );\n\n\n$.extend( DataTable.ext.classes, {\n\t\"sWrapper\":            \"dataTables_wrapper dt-jqueryui\",\n\n\t/* Full numbers paging buttons */\n\t\"sPageButton\":         \"fg-button ui-button ui-state-default\",\n\t\"sPageButtonActive\":   \"ui-state-disabled\",\n\t\"sPageButtonDisabled\": \"ui-state-disabled\",\n\n\t/* Features */\n\t\"sPaging\": \"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi \"+\n\t\t\"ui-buttonset-multi paging_\", /* Note that the type is postfixed */\n\n\t/* Scrolling */\n\t\"sScrollHead\": \"dataTables_scrollHead \"+\"ui-state-default\",\n\t\"sScrollFoot\": \"dataTables_scrollFoot \"+\"ui-state-default\",\n\n\t/* Misc */\n\t\"sHeaderTH\":  \"ui-state-default\",\n\t\"sFooterTH\":  \"ui-state-default\"\n} );\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/dataTables.semanticui.js",
    "content": "/*! DataTables Bootstrap 3 integration\n * ©2011-2015 SpryMedia Ltd - datatables.net/license\n */\n\n(function( factory ){\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery', 'datatables.net'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\tvar jq = require('jquery');\n\t\tvar cjsRequires = function (root, $) {\n\t\t\tif ( ! $.fn.dataTable ) {\n\t\t\t\trequire('datatables.net')(root, $);\n\t\t\t}\n\t\t};\n\n\t\tif (typeof window === 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\tcjsRequires( root, $ );\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tcjsRequires( window, jq );\n\t\t\tmodule.exports = factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\tfactory( jQuery, window, document );\n\t}\n}(function( $, window, document, undefined ) {\n'use strict';\nvar DataTable = $.fn.dataTable;\n\n\n\n/**\n * DataTables integration for FomanticUI (formally SemanticUI)\n *\n * This file sets the defaults and adds options to DataTables to style its\n * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap\n * for further information.\n */\n\n/* Set the defaults for DataTables initialisation */\n$.extend( true, DataTable.defaults, {\n\tdom:\n\t\t\"<'ui stackable grid'\"+\n\t\t\t\"<'row'\"+\n\t\t\t\t\"<'eight wide column'l>\"+\n\t\t\t\t\"<'right aligned eight wide column'f>\"+\n\t\t\t\">\"+\n\t\t\t\"<'row dt-table'\"+\n\t\t\t\t\"<'sixteen wide column'tr>\"+\n\t\t\t\">\"+\n\t\t\t\"<'row'\"+\n\t\t\t\t\"<'seven wide column'i>\"+\n\t\t\t\t\"<'right aligned nine wide column'p>\"+\n\t\t\t\">\"+\n\t\t\">\",\n\trenderer: 'semanticUI'\n} );\n\n\n/* Default class modification */\n$.extend( DataTable.ext.classes, {\n\tsWrapper:      \"dataTables_wrapper dt-semanticUI\",\n\tsFilter:       \"dataTables_filter ui input\",\n\tsProcessing:   \"dataTables_processing ui segment\",\n\tsPageButton:   \"paginate_button item\"\n} );\n\n\n/* Bootstrap paging button renderer */\nDataTable.ext.renderer.pageButton.semanticUI = function ( settings, host, idx, buttons, page, pages ) {\n\tvar api     = new DataTable.Api( settings );\n\tvar classes = settings.oClasses;\n\tvar lang    = settings.oLanguage.oPaginate;\n\tvar aria = settings.oLanguage.oAria.paginate || {};\n\tvar btnDisplay, btnClass;\n\n\tvar attach = function( container, buttons ) {\n\t\tvar i, ien, node, button;\n\t\tvar clickHandler = function ( e ) {\n\t\t\te.preventDefault();\n\t\t\tif ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {\n\t\t\t\tapi.page( e.data.action ).draw( 'page' );\n\t\t\t}\n\t\t};\n\n\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\tbutton = buttons[i];\n\n\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\tattach( container, button );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbtnDisplay = '';\n\t\t\t\tbtnClass = '';\n\n\t\t\t\tswitch ( button ) {\n\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\tbtnDisplay = '&#x2026;';\n\t\t\t\t\t\tbtnClass = 'disabled';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'first':\n\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\t\t\t\t\tbtnClass = button + (page > 0 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'next':\n\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'last':\n\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\t\t\t\t\tbtnClass = button + (page < pages-1 ?\n\t\t\t\t\t\t\t'' : ' disabled');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbtnDisplay = button + 1;\n\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t'active' : '';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tvar disabled = btnClass.indexOf('disabled') !== -1;\n\t\t\t\tvar tag = disabled ?\n\t\t\t\t\t'div' :\n\t\t\t\t\t'a';\n\n\t\t\t\tif ( btnDisplay ) {\n\t\t\t\t\tnode = $('<'+tag+'>', {\n\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t'href': disabled ? null : '#',\n\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t'aria-current': btnClass === 'active' ? 'page' : null,\n\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t'tabindex': settings.iTabIndex\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t.appendTo( container );\n\n\t\t\t\t\tsettings.oApi._fnBindAction(\n\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t// inside an iframe or frame. \n\tvar activeEl;\n\n\ttry {\n\t\t// Because this approach is destroying and recreating the paging\n\t\t// elements, focus is lost on the select button which is bad for\n\t\t// accessibility. So we want to restore focus once the draw has\n\t\t// completed\n\t\tactiveEl = $(host).find(document.activeElement).data('dt-idx');\n\t}\n\tcatch (e) {}\n\n\tattach(\n\t\t$(host).empty().html('<div class=\"ui stackable pagination menu\"/>').children(),\n\t\tbuttons\n\t);\n\n\tif ( activeEl !== undefined ) {\n\t\t$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');\n\t}\n};\n\n\n// Javascript enhancements on table initialisation\n$(document).on( 'init.dt', function (e, ctx) {\n\tif ( e.namespace !== 'dt' ) {\n\t\treturn;\n\t}\n\n\tvar api = new $.fn.dataTable.Api( ctx );\n\n\t// Length menu drop down\n\tif ( $.fn.dropdown ) {\n\t\t$( 'div.dataTables_length select', api.table().container() ).dropdown();\n\t}\n\n\t// Filtering input\n\t$( 'div.dataTables_filter.ui.input', api.table().container() ).removeClass('input').addClass('form');\n\t$( 'div.dataTables_filter input', api.table().container() ).wrap( '<span class=\"ui input\" />' );\n} );\n\n\nreturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/js/jquery.dataTables.js",
    "content": "/*! DataTables 1.13.5\n * ©2008-2023 SpryMedia Ltd - datatables.net/license\n */\n\n/**\n * @summary     DataTables\n * @description Paginate, search and order HTML tables\n * @version     1.13.4\n * @author      SpryMedia Ltd\n * @contact     www.datatables.net\n * @copyright   SpryMedia Ltd.\n *\n * This source file is free software, available under the following license:\n *   MIT license - http://datatables.net/license\n *\n * This source file is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.\n *\n * For details please refer to: http://www.datatables.net\n */\n\n/*jslint evil: true, undef: true, browser: true */\n/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/\n\n(function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === 'function' && define.amd ) {\n\t\t// AMD\n\t\tdefine( ['jquery'], function ( $ ) {\n\t\t\treturn factory( $, window, document );\n\t\t} );\n\t}\n\telse if ( typeof exports === 'object' ) {\n\t\t// CommonJS\n\t\t// jQuery's factory checks for a global window - if it isn't present then it\n\t\t// returns a factory function that expects the window object\n\t\tvar jq = require('jquery');\n\n\t\tif (typeof window !== 'undefined') {\n\t\t\tmodule.exports = function (root, $) {\n\t\t\t\tif ( ! root ) {\n\t\t\t\t\t// CommonJS environments without a window global must pass a\n\t\t\t\t\t// root. This will give an error otherwise\n\t\t\t\t\troot = window;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $ ) {\n\t\t\t\t\t$ = jq( root );\n\t\t\t\t}\n\n\t\t\t\treturn factory( $, root, root.document );\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\treturn factory( jq, window, window.document );\n\t\t}\n\t}\n\telse {\n\t\t// Browser\n\t\twindow.DataTable = factory( jQuery, window, document );\n\t}\n}\n(function( $, window, document, undefined ) {\n\t\"use strict\";\n\n\t\n\tvar DataTable = function ( selector, options )\n\t{\n\t\t// Check if called with a window or jQuery object for DOM less applications\n\t\t// This is for backwards compatibility\n\t\tif (DataTable.factory(selector, options)) {\n\t\t\treturn DataTable;\n\t\t}\n\t\n\t\t// When creating with `new`, create a new DataTable, returning the API instance\n\t\tif (this instanceof DataTable) {\n\t\t\treturn $(selector).DataTable(options);\n\t\t}\n\t\telse {\n\t\t\t// Argument switching\n\t\t\toptions = selector;\n\t\t}\n\t\n\t\t/**\n\t\t * Perform a jQuery selector action on the table's TR elements (from the tbody) and\n\t\t * return the resulting jQuery object.\n\t\t *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on\n\t\t *  @param {object} [oOpts] Optional parameters for modifying the rows to be included\n\t\t *  @param {string} [oOpts.filter=none] Select TR elements that meet the current filter\n\t\t *    criterion (\"applied\") or all TR elements (i.e. no filter).\n\t\t *  @param {string} [oOpts.order=current] Order of the TR elements in the processed array.\n\t\t *    Can be either 'current', whereby the current sorting of the table is used, or\n\t\t *    'original' whereby the original order the data was read into the table is used.\n\t\t *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page\n\t\t *    (\"current\") or not (\"all\"). If 'current' is given, then order is assumed to be\n\t\t *    'current' and filter is 'applied', regardless of what they might be given as.\n\t\t *  @returns {object} jQuery object, filtered by the given selector.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Highlight every second row\n\t\t *      oTable.$('tr:odd').css('backgroundColor', 'blue');\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Filter to rows with 'Webkit' in them, add a background colour and then\n\t\t *      // remove the filter, thus highlighting the 'Webkit' rows only.\n\t\t *      oTable.fnFilter('Webkit');\n\t\t *      oTable.$('tr', {\"search\": \"applied\"}).css('backgroundColor', 'blue');\n\t\t *      oTable.fnFilter('');\n\t\t *    } );\n\t\t */\n\t\tthis.$ = function ( sSelector, oOpts )\n\t\t{\n\t\t\treturn this.api(true).$( sSelector, oOpts );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Almost identical to $ in operation, but in this case returns the data for the matched\n\t\t * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes\n\t\t * rather than any descendants, so the data can be obtained for the row/cell. If matching\n\t\t * rows are found, the data returned is the original data array/object that was used to\n\t\t * create the row (or a generated array if from a DOM source).\n\t\t *\n\t\t * This method is often useful in-combination with $ where both functions are given the\n\t\t * same parameters and the array indexes will match identically.\n\t\t *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on\n\t\t *  @param {object} [oOpts] Optional parameters for modifying the rows to be included\n\t\t *  @param {string} [oOpts.filter=none] Select elements that meet the current filter\n\t\t *    criterion (\"applied\") or all elements (i.e. no filter).\n\t\t *  @param {string} [oOpts.order=current] Order of the data in the processed array.\n\t\t *    Can be either 'current', whereby the current sorting of the table is used, or\n\t\t *    'original' whereby the original order the data was read into the table is used.\n\t\t *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page\n\t\t *    (\"current\") or not (\"all\"). If 'current' is given, then order is assumed to be\n\t\t *    'current' and filter is 'applied', regardless of what they might be given as.\n\t\t *  @returns {array} Data for the matched elements. If any elements, as a result of the\n\t\t *    selector, were not TR, TD or TH elements in the DataTable, they will have a null\n\t\t *    entry in the array.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Get the data from the first row in the table\n\t\t *      var data = oTable._('tr:first');\n\t\t *\n\t\t *      // Do something useful with the data\n\t\t *      alert( \"First cell is: \"+data[0] );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Filter to 'Webkit' and get all data for\n\t\t *      oTable.fnFilter('Webkit');\n\t\t *      var data = oTable._('tr', {\"search\": \"applied\"});\n\t\t *\n\t\t *      // Do something with the data\n\t\t *      alert( data.length+\" rows matched the search\" );\n\t\t *    } );\n\t\t */\n\t\tthis._ = function ( sSelector, oOpts )\n\t\t{\n\t\t\treturn this.api(true).rows( sSelector, oOpts ).data();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Create a DataTables Api instance, with the currently selected tables for\n\t\t * the Api's context.\n\t\t * @param {boolean} [traditional=false] Set the API instance's context to be\n\t\t *   only the table referred to by the `DataTable.ext.iApiIndex` option, as was\n\t\t *   used in the API presented by DataTables 1.9- (i.e. the traditional mode),\n\t\t *   or if all tables captured in the jQuery object should be used.\n\t\t * @return {DataTables.Api}\n\t\t */\n\t\tthis.api = function ( traditional )\n\t\t{\n\t\t\treturn traditional ?\n\t\t\t\tnew _Api(\n\t\t\t\t\t_fnSettingsFromNode( this[ _ext.iApiIndex ] )\n\t\t\t\t) :\n\t\t\t\tnew _Api( this );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Add a single new row or multiple rows of data to the table. Please note\n\t\t * that this is suitable for client-side processing only - if you are using\n\t\t * server-side processing (i.e. \"bServerSide\": true), then to add data, you\n\t\t * must add it to the data source, i.e. the server-side, through an Ajax call.\n\t\t *  @param {array|object} data The data to be added to the table. This can be:\n\t\t *    <ul>\n\t\t *      <li>1D array of data - add a single row with the data provided</li>\n\t\t *      <li>2D array of arrays - add multiple rows in a single call</li>\n\t\t *      <li>object - data object when using <i>mData</i></li>\n\t\t *      <li>array of objects - multiple data objects when using <i>mData</i></li>\n\t\t *    </ul>\n\t\t *  @param {bool} [redraw=true] redraw the table or not\n\t\t *  @returns {array} An array of integers, representing the list of indexes in\n\t\t *    <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to\n\t\t *    the table.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    // Global var for counter\n\t\t *    var giCount = 2;\n\t\t *\n\t\t *    $(document).ready(function() {\n\t\t *      $('#example').dataTable();\n\t\t *    } );\n\t\t *\n\t\t *    function fnClickAddRow() {\n\t\t *      $('#example').dataTable().fnAddData( [\n\t\t *        giCount+\".1\",\n\t\t *        giCount+\".2\",\n\t\t *        giCount+\".3\",\n\t\t *        giCount+\".4\" ]\n\t\t *      );\n\t\t *\n\t\t *      giCount++;\n\t\t *    }\n\t\t */\n\t\tthis.fnAddData = function( data, redraw )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\t/* Check if we want to add multiple rows or not */\n\t\t\tvar rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ?\n\t\t\t\tapi.rows.add( data ) :\n\t\t\t\tapi.row.add( data );\n\t\t\n\t\t\tif ( redraw === undefined || redraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t\n\t\t\treturn rows.flatten().toArray();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * This function will make DataTables recalculate the column sizes, based on the data\n\t\t * contained in the table and the sizes applied to the columns (in the DOM, CSS or\n\t\t * through the sWidth parameter). This can be useful when the width of the table's\n\t\t * parent element changes (for example a window resize).\n\t\t *  @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable( {\n\t\t *        \"sScrollY\": \"200px\",\n\t\t *        \"bPaginate\": false\n\t\t *      } );\n\t\t *\n\t\t *      $(window).on('resize', function () {\n\t\t *        oTable.fnAdjustColumnSizing();\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\tthis.fnAdjustColumnSizing = function ( bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).columns.adjust();\n\t\t\tvar settings = api.settings()[0];\n\t\t\tvar scroll = settings.oScroll;\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw( false );\n\t\t\t}\n\t\t\telse if ( scroll.sX !== \"\" || scroll.sY !== \"\" ) {\n\t\t\t\t/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */\n\t\t\t\t_fnScrollDraw( settings );\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Quickly and simply clear a table\n\t\t *  @param {bool} [bRedraw=true] redraw the table or not\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)\n\t\t *      oTable.fnClearTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnClearTable = function( bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).clear();\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * The exact opposite of 'opening' a row, this function will close any rows which\n\t\t * are currently 'open'.\n\t\t *  @param {node} nTr the table row to 'close'\n\t\t *  @returns {int} 0 on success, or 1 if failed (can't find the row)\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable;\n\t\t *\n\t\t *      // 'open' an information row when a row is clicked on\n\t\t *      $('#example tbody tr').click( function () {\n\t\t *        if ( oTable.fnIsOpen(this) ) {\n\t\t *          oTable.fnClose( this );\n\t\t *        } else {\n\t\t *          oTable.fnOpen( this, \"Temporary row opened\", \"info_row\" );\n\t\t *        }\n\t\t *      } );\n\t\t *\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnClose = function( nTr )\n\t\t{\n\t\t\tthis.api( true ).row( nTr ).child.hide();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Remove a row for the table\n\t\t *  @param {mixed} target The index of the row from aoData to be deleted, or\n\t\t *    the TR element you want to delete\n\t\t *  @param {function|null} [callBack] Callback function\n\t\t *  @param {bool} [redraw=true] Redraw the table or not\n\t\t *  @returns {array} The row that was deleted\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Immediately remove the first row\n\t\t *      oTable.fnDeleteRow( 0 );\n\t\t *    } );\n\t\t */\n\t\tthis.fnDeleteRow = function( target, callback, redraw )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\tvar rows = api.rows( target );\n\t\t\tvar settings = rows.settings()[0];\n\t\t\tvar data = settings.aoData[ rows[0][0] ];\n\t\t\n\t\t\trows.remove();\n\t\t\n\t\t\tif ( callback ) {\n\t\t\t\tcallback.call( this, settings, data );\n\t\t\t}\n\t\t\n\t\t\tif ( redraw === undefined || redraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t\n\t\t\treturn data;\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Restore the table to it's original state in the DOM by removing all of DataTables\n\t\t * enhancements, alterations to the DOM structure of the table and event listeners.\n\t\t *  @param {boolean} [remove=false] Completely remove the table from the DOM\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      // This example is fairly pointless in reality, but shows how fnDestroy can be used\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      oTable.fnDestroy();\n\t\t *    } );\n\t\t */\n\t\tthis.fnDestroy = function ( remove )\n\t\t{\n\t\t\tthis.api( true ).destroy( remove );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Redraw the table\n\t\t *  @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Re-draw the table - you wouldn't want to do it here, but it's an example :-)\n\t\t *      oTable.fnDraw();\n\t\t *    } );\n\t\t */\n\t\tthis.fnDraw = function( complete )\n\t\t{\n\t\t\t// Note that this isn't an exact match to the old call to _fnDraw - it takes\n\t\t\t// into account the new data, but can hold position.\n\t\t\tthis.api( true ).draw( complete );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Filter the input based on data\n\t\t *  @param {string} sInput String to filter the table on\n\t\t *  @param {int|null} [iColumn] Column to limit filtering to\n\t\t *  @param {bool} [bRegex=false] Treat as regular expression or not\n\t\t *  @param {bool} [bSmart=true] Perform smart filtering or not\n\t\t *  @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)\n\t\t *  @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Sometime later - filter...\n\t\t *      oTable.fnFilter( 'test string' );\n\t\t *    } );\n\t\t */\n\t\tthis.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\tif ( iColumn === null || iColumn === undefined ) {\n\t\t\t\tapi.search( sInput, bRegex, bSmart, bCaseInsensitive );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tapi.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );\n\t\t\t}\n\t\t\n\t\t\tapi.draw();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get the data for the whole table, an individual row or an individual cell based on the\n\t\t * provided parameters.\n\t\t *  @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as\n\t\t *    a TR node then the data source for the whole row will be returned. If given as a\n\t\t *    TD/TH cell node then iCol will be automatically calculated and the data for the\n\t\t *    cell returned. If given as an integer, then this is treated as the aoData internal\n\t\t *    data index for the row (see fnGetPosition) and the data for that row used.\n\t\t *  @param {int} [col] Optional column index that you want the data of.\n\t\t *  @returns {array|object|string} If mRow is undefined, then the data for all rows is\n\t\t *    returned. If mRow is defined, just data for that row, and is iCol is\n\t\t *    defined, only data for the designated cell is returned.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    // Row data\n\t\t *    $(document).ready(function() {\n\t\t *      oTable = $('#example').dataTable();\n\t\t *\n\t\t *      oTable.$('tr').click( function () {\n\t\t *        var data = oTable.fnGetData( this );\n\t\t *        // ... do something with the array / object of data for the row\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Individual cell data\n\t\t *    $(document).ready(function() {\n\t\t *      oTable = $('#example').dataTable();\n\t\t *\n\t\t *      oTable.$('td').click( function () {\n\t\t *        var sData = oTable.fnGetData( this );\n\t\t *        alert( 'The cell clicked on had the value of '+sData );\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\tthis.fnGetData = function( src, col )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\tif ( src !== undefined ) {\n\t\t\t\tvar type = src.nodeName ? src.nodeName.toLowerCase() : '';\n\t\t\n\t\t\t\treturn col !== undefined || type == 'td' || type == 'th' ?\n\t\t\t\t\tapi.cell( src, col ).data() :\n\t\t\t\t\tapi.row( src ).data() || null;\n\t\t\t}\n\t\t\n\t\t\treturn api.data().toArray();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get an array of the TR nodes that are used in the table's body. Note that you will\n\t\t * typically want to use the '$' API method in preference to this as it is more\n\t\t * flexible.\n\t\t *  @param {int} [iRow] Optional row index for the TR element you want\n\t\t *  @returns {array|node} If iRow is undefined, returns an array of all TR elements\n\t\t *    in the table's body, or iRow is defined, just the TR element requested.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Get the nodes from the table\n\t\t *      var nNodes = oTable.fnGetNodes( );\n\t\t *    } );\n\t\t */\n\t\tthis.fnGetNodes = function( iRow )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\treturn iRow !== undefined ?\n\t\t\t\tapi.row( iRow ).node() :\n\t\t\t\tapi.rows().nodes().flatten().toArray();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get the array indexes of a particular cell from it's DOM element\n\t\t * and column index including hidden columns\n\t\t *  @param {node} node this can either be a TR, TD or TH in the table's body\n\t\t *  @returns {int} If nNode is given as a TR, then a single index is returned, or\n\t\t *    if given as a cell, an array of [row index, column index (visible),\n\t\t *    column index (all)] is given.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      $('#example tbody td').click( function () {\n\t\t *        // Get the position of the current data from the node\n\t\t *        var aPos = oTable.fnGetPosition( this );\n\t\t *\n\t\t *        // Get the data array for this row\n\t\t *        var aData = oTable.fnGetData( aPos[0] );\n\t\t *\n\t\t *        // Update the data array and return the value\n\t\t *        aData[ aPos[1] ] = 'clicked';\n\t\t *        this.innerHTML = 'clicked';\n\t\t *      } );\n\t\t *\n\t\t *      // Init DataTables\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnGetPosition = function( node )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\tvar nodeName = node.nodeName.toUpperCase();\n\t\t\n\t\t\tif ( nodeName == 'TR' ) {\n\t\t\t\treturn api.row( node ).index();\n\t\t\t}\n\t\t\telse if ( nodeName == 'TD' || nodeName == 'TH' ) {\n\t\t\t\tvar cell = api.cell( node ).index();\n\t\t\n\t\t\t\treturn [\n\t\t\t\t\tcell.row,\n\t\t\t\t\tcell.columnVisible,\n\t\t\t\t\tcell.column\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Check to see if a row is 'open' or not.\n\t\t *  @param {node} nTr the table row to check\n\t\t *  @returns {boolean} true if the row is currently open, false otherwise\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable;\n\t\t *\n\t\t *      // 'open' an information row when a row is clicked on\n\t\t *      $('#example tbody tr').click( function () {\n\t\t *        if ( oTable.fnIsOpen(this) ) {\n\t\t *          oTable.fnClose( this );\n\t\t *        } else {\n\t\t *          oTable.fnOpen( this, \"Temporary row opened\", \"info_row\" );\n\t\t *        }\n\t\t *      } );\n\t\t *\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnIsOpen = function( nTr )\n\t\t{\n\t\t\treturn this.api( true ).row( nTr ).child.isShown();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * This function will place a new row directly after a row which is currently\n\t\t * on display on the page, with the HTML contents that is passed into the\n\t\t * function. This can be used, for example, to ask for confirmation that a\n\t\t * particular record should be deleted.\n\t\t *  @param {node} nTr The table row to 'open'\n\t\t *  @param {string|node|jQuery} mHtml The HTML to put into the row\n\t\t *  @param {string} sClass Class to give the new TD cell\n\t\t *  @returns {node} The row opened. Note that if the table row passed in as the\n\t\t *    first parameter, is not found in the table, this method will silently\n\t\t *    return.\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable;\n\t\t *\n\t\t *      // 'open' an information row when a row is clicked on\n\t\t *      $('#example tbody tr').click( function () {\n\t\t *        if ( oTable.fnIsOpen(this) ) {\n\t\t *          oTable.fnClose( this );\n\t\t *        } else {\n\t\t *          oTable.fnOpen( this, \"Temporary row opened\", \"info_row\" );\n\t\t *        }\n\t\t *      } );\n\t\t *\n\t\t *      oTable = $('#example').dataTable();\n\t\t *    } );\n\t\t */\n\t\tthis.fnOpen = function( nTr, mHtml, sClass )\n\t\t{\n\t\t\treturn this.api( true )\n\t\t\t\t.row( nTr )\n\t\t\t\t.child( mHtml, sClass )\n\t\t\t\t.show()\n\t\t\t\t.child()[0];\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Change the pagination - provides the internal logic for pagination in a simple API\n\t\t * function. With this function you can have a DataTables table go to the next,\n\t\t * previous, first or last pages.\n\t\t *  @param {string|int} mAction Paging action to take: \"first\", \"previous\", \"next\" or \"last\"\n\t\t *    or page number to jump to (integer), note that page 0 is the first page.\n\t\t *  @param {bool} [bRedraw=true] Redraw the table or not\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      oTable.fnPageChange( 'next' );\n\t\t *    } );\n\t\t */\n\t\tthis.fnPageChange = function ( mAction, bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).page( mAction );\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw(false);\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Show a particular column\n\t\t *  @param {int} iCol The column whose display should be changed\n\t\t *  @param {bool} bShow Show (true) or hide (false) the column\n\t\t *  @param {bool} [bRedraw=true] Redraw the table or not\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Hide the second column after initialisation\n\t\t *      oTable.fnSetColumnVis( 1, false );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSetColumnVis = function ( iCol, bShow, bRedraw )\n\t\t{\n\t\t\tvar api = this.api( true ).column( iCol ).visible( bShow );\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.columns.adjust().draw();\n\t\t\t}\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Get the settings for a particular table for external manipulation\n\t\t *  @returns {object} DataTables settings object. See\n\t\t *    {@link DataTable.models.oSettings}\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      var oSettings = oTable.fnSettings();\n\t\t *\n\t\t *      // Show an example parameter from the settings\n\t\t *      alert( oSettings._iDisplayStart );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSettings = function()\n\t\t{\n\t\t\treturn _fnSettingsFromNode( this[_ext.iApiIndex] );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Sort the table by a particular column\n\t\t *  @param {int} iCol the data index to sort on. Note that this will not match the\n\t\t *    'display index' if you have hidden data entries\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Sort immediately with columns 0 and 1\n\t\t *      oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSort = function( aaSort )\n\t\t{\n\t\t\tthis.api( true ).order( aaSort ).draw();\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Attach a sort listener to an element for a given column\n\t\t *  @param {node} nNode the element to attach the sort listener to\n\t\t *  @param {int} iColumn the column that a click on this node will sort on\n\t\t *  @param {function} [fnCallback] callback function when sort is run\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *\n\t\t *      // Sort on column 1, when 'sorter' is clicked on\n\t\t *      oTable.fnSortListener( document.getElementById('sorter'), 1 );\n\t\t *    } );\n\t\t */\n\t\tthis.fnSortListener = function( nNode, iColumn, fnCallback )\n\t\t{\n\t\t\tthis.api( true ).order.listener( nNode, iColumn, fnCallback );\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Update a table cell or row - this method will accept either a single value to\n\t\t * update the cell with, an array of values with one element for each column or\n\t\t * an object in the same format as the original data source. The function is\n\t\t * self-referencing in order to make the multi column updates easier.\n\t\t *  @param {object|array|string} mData Data to update the cell/row with\n\t\t *  @param {node|int} mRow TR element you want to update or the aoData index\n\t\t *  @param {int} [iColumn] The column to update, give as null or undefined to\n\t\t *    update a whole row.\n\t\t *  @param {bool} [bRedraw=true] Redraw the table or not\n\t\t *  @param {bool} [bAction=true] Perform pre-draw actions or not\n\t\t *  @returns {int} 0 on success, 1 on error\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell\n\t\t *      oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row\n\t\t *    } );\n\t\t */\n\t\tthis.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )\n\t\t{\n\t\t\tvar api = this.api( true );\n\t\t\n\t\t\tif ( iColumn === undefined || iColumn === null ) {\n\t\t\t\tapi.row( mRow ).data( mData );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tapi.cell( mRow, iColumn ).data( mData );\n\t\t\t}\n\t\t\n\t\t\tif ( bAction === undefined || bAction ) {\n\t\t\t\tapi.columns.adjust();\n\t\t\t}\n\t\t\n\t\t\tif ( bRedraw === undefined || bRedraw ) {\n\t\t\t\tapi.draw();\n\t\t\t}\n\t\t\treturn 0;\n\t\t};\n\t\t\n\t\t\n\t\t/**\n\t\t * Provide a common method for plug-ins to check the version of DataTables being used, in order\n\t\t * to ensure compatibility.\n\t\t *  @param {string} sVersion Version string to check for, in the format \"X.Y.Z\". Note that the\n\t\t *    formats \"X\" and \"X.Y\" are also acceptable.\n\t\t *  @returns {boolean} true if this version of DataTables is greater or equal to the required\n\t\t *    version, or false if this version of DataTales is not suitable\n\t\t *  @method\n\t\t *  @dtopt API\n\t\t *  @deprecated Since v1.10\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready(function() {\n\t\t *      var oTable = $('#example').dataTable();\n\t\t *      alert( oTable.fnVersionCheck( '1.9.0' ) );\n\t\t *    } );\n\t\t */\n\t\tthis.fnVersionCheck = _ext.fnVersionCheck;\n\t\t\n\t\n\t\tvar _that = this;\n\t\tvar emptyInit = options === undefined;\n\t\tvar len = this.length;\n\t\n\t\tif ( emptyInit ) {\n\t\t\toptions = {};\n\t\t}\n\t\n\t\tthis.oApi = this.internal = _ext.internal;\n\t\n\t\t// Extend with old style plug-in API methods\n\t\tfor ( var fn in DataTable.ext.internal ) {\n\t\t\tif ( fn ) {\n\t\t\t\tthis[fn] = _fnExternApiFunc(fn);\n\t\t\t}\n\t\t}\n\t\n\t\tthis.each(function() {\n\t\t\t// For each initialisation we want to give it a clean initialisation\n\t\t\t// object that can be bashed around\n\t\t\tvar o = {};\n\t\t\tvar oInit = len > 1 ? // optimisation for single table case\n\t\t\t\t_fnExtend( o, options, true ) :\n\t\t\t\toptions;\n\t\n\t\t\t/*global oInit,_that,emptyInit*/\n\t\t\tvar i=0, iLen, j, jLen, k, kLen;\n\t\t\tvar sId = this.getAttribute( 'id' );\n\t\t\tvar bInitHandedOff = false;\n\t\t\tvar defaults = DataTable.defaults;\n\t\t\tvar $this = $(this);\n\t\t\t\n\t\t\t\n\t\t\t/* Sanity check */\n\t\t\tif ( this.nodeName.toLowerCase() != 'table' )\n\t\t\t{\n\t\t\t\t_fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t/* Backwards compatibility for the defaults */\n\t\t\t_fnCompatOpts( defaults );\n\t\t\t_fnCompatCols( defaults.column );\n\t\t\t\n\t\t\t/* Convert the camel-case defaults to Hungarian */\n\t\t\t_fnCamelToHungarian( defaults, defaults, true );\n\t\t\t_fnCamelToHungarian( defaults.column, defaults.column, true );\n\t\t\t\n\t\t\t/* Setting up the initialisation object */\n\t\t\t_fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t/* Check to see if we are re-initialising a table */\n\t\t\tvar allSettings = DataTable.settings;\n\t\t\tfor ( i=0, iLen=allSettings.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\tvar s = allSettings[i];\n\t\t\t\n\t\t\t\t/* Base check on table node */\n\t\t\t\tif (\n\t\t\t\t\ts.nTable == this ||\n\t\t\t\t\t(s.nTHead && s.nTHead.parentNode == this) ||\n\t\t\t\t\t(s.nTFoot && s.nTFoot.parentNode == this)\n\t\t\t\t) {\n\t\t\t\t\tvar bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;\n\t\t\t\t\tvar bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;\n\t\t\t\n\t\t\t\t\tif ( emptyInit || bRetrieve )\n\t\t\t\t\t{\n\t\t\t\t\t\treturn s.oInstance;\n\t\t\t\t\t}\n\t\t\t\t\telse if ( bDestroy )\n\t\t\t\t\t{\n\t\t\t\t\t\ts.oInstance.fnDestroy();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t_fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* If the element we are initialising has the same ID as a table which was previously\n\t\t\t\t * initialised, but the table nodes don't match (from before) then we destroy the old\n\t\t\t\t * instance by simply deleting it. This is under the assumption that the table has been\n\t\t\t\t * destroyed by other methods. Anyone using non-id selectors will need to do this manually\n\t\t\t\t */\n\t\t\t\tif ( s.sTableId == this.id )\n\t\t\t\t{\n\t\t\t\t\tallSettings.splice( i, 1 );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t/* Ensure the table has an ID - required for accessibility */\n\t\t\tif ( sId === null || sId === \"\" )\n\t\t\t{\n\t\t\t\tsId = \"DataTables_Table_\"+(DataTable.ext._unique++);\n\t\t\t\tthis.id = sId;\n\t\t\t}\n\t\t\t\n\t\t\t/* Create the settings object for this table and set some of the default parameters */\n\t\t\tvar oSettings = $.extend( true, {}, DataTable.models.oSettings, {\n\t\t\t\t\"sDestroyWidth\": $this[0].style.width,\n\t\t\t\t\"sInstance\":     sId,\n\t\t\t\t\"sTableId\":      sId\n\t\t\t} );\n\t\t\toSettings.nTable = this;\n\t\t\toSettings.oApi   = _that.internal;\n\t\t\toSettings.oInit  = oInit;\n\t\t\t\n\t\t\tallSettings.push( oSettings );\n\t\t\t\n\t\t\t// Need to add the instance after the instance after the settings object has been added\n\t\t\t// to the settings array, so we can self reference the table instance if more than one\n\t\t\toSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();\n\t\t\t\n\t\t\t// Backwards compatibility, before we apply all the defaults\n\t\t\t_fnCompatOpts( oInit );\n\t\t\t_fnLanguageCompat( oInit.oLanguage );\n\t\t\t\n\t\t\t// If the length menu is given, but the init display length is not, use the length menu\n\t\t\tif ( oInit.aLengthMenu && ! oInit.iDisplayLength )\n\t\t\t{\n\t\t\t\toInit.iDisplayLength = Array.isArray( oInit.aLengthMenu[0] ) ?\n\t\t\t\t\toInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];\n\t\t\t}\n\t\t\t\n\t\t\t// Apply the defaults and init options to make a single init object will all\n\t\t\t// options defined from defaults and instance options.\n\t\t\toInit = _fnExtend( $.extend( true, {}, defaults ), oInit );\n\t\t\t\n\t\t\t\n\t\t\t// Map the initialisation options onto the settings object\n\t\t\t_fnMap( oSettings.oFeatures, oInit, [\n\t\t\t\t\"bPaginate\",\n\t\t\t\t\"bLengthChange\",\n\t\t\t\t\"bFilter\",\n\t\t\t\t\"bSort\",\n\t\t\t\t\"bSortMulti\",\n\t\t\t\t\"bInfo\",\n\t\t\t\t\"bProcessing\",\n\t\t\t\t\"bAutoWidth\",\n\t\t\t\t\"bSortClasses\",\n\t\t\t\t\"bServerSide\",\n\t\t\t\t\"bDeferRender\"\n\t\t\t] );\n\t\t\t_fnMap( oSettings, oInit, [\n\t\t\t\t\"asStripeClasses\",\n\t\t\t\t\"ajax\",\n\t\t\t\t\"fnServerData\",\n\t\t\t\t\"fnFormatNumber\",\n\t\t\t\t\"sServerMethod\",\n\t\t\t\t\"aaSorting\",\n\t\t\t\t\"aaSortingFixed\",\n\t\t\t\t\"aLengthMenu\",\n\t\t\t\t\"sPaginationType\",\n\t\t\t\t\"sAjaxSource\",\n\t\t\t\t\"sAjaxDataProp\",\n\t\t\t\t\"iStateDuration\",\n\t\t\t\t\"sDom\",\n\t\t\t\t\"bSortCellsTop\",\n\t\t\t\t\"iTabIndex\",\n\t\t\t\t\"fnStateLoadCallback\",\n\t\t\t\t\"fnStateSaveCallback\",\n\t\t\t\t\"renderer\",\n\t\t\t\t\"searchDelay\",\n\t\t\t\t\"rowId\",\n\t\t\t\t[ \"iCookieDuration\", \"iStateDuration\" ], // backwards compat\n\t\t\t\t[ \"oSearch\", \"oPreviousSearch\" ],\n\t\t\t\t[ \"aoSearchCols\", \"aoPreSearchCols\" ],\n\t\t\t\t[ \"iDisplayLength\", \"_iDisplayLength\" ]\n\t\t\t] );\n\t\t\t_fnMap( oSettings.oScroll, oInit, [\n\t\t\t\t[ \"sScrollX\", \"sX\" ],\n\t\t\t\t[ \"sScrollXInner\", \"sXInner\" ],\n\t\t\t\t[ \"sScrollY\", \"sY\" ],\n\t\t\t\t[ \"bScrollCollapse\", \"bCollapse\" ]\n\t\t\t] );\n\t\t\t_fnMap( oSettings.oLanguage, oInit, \"fnInfoCallback\" );\n\t\t\t\n\t\t\t/* Callback functions which are array driven */\n\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback',       oInit.fnDrawCallback,      'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoServerParams',       oInit.fnServerParams,      'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoStateSaveParams',    oInit.fnStateSaveParams,   'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoStateLoadParams',    oInit.fnStateLoadParams,   'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoStateLoaded',        oInit.fnStateLoaded,       'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoRowCallback',        oInit.fnRowCallback,       'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow,        'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoHeaderCallback',     oInit.fnHeaderCallback,    'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoFooterCallback',     oInit.fnFooterCallback,    'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoInitComplete',       oInit.fnInitComplete,      'user' );\n\t\t\t_fnCallbackReg( oSettings, 'aoPreDrawCallback',    oInit.fnPreDrawCallback,   'user' );\n\t\t\t\n\t\t\toSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );\n\t\t\t\n\t\t\t/* Browser support detection */\n\t\t\t_fnBrowserDetect( oSettings );\n\t\t\t\n\t\t\tvar oClasses = oSettings.oClasses;\n\t\t\t\n\t\t\t$.extend( oClasses, DataTable.ext.classes, oInit.oClasses );\n\t\t\t$this.addClass( oClasses.sTable );\n\t\t\t\n\t\t\t\n\t\t\tif ( oSettings.iInitDisplayStart === undefined )\n\t\t\t{\n\t\t\t\t/* Display start point, taking into account the save saving */\n\t\t\t\toSettings.iInitDisplayStart = oInit.iDisplayStart;\n\t\t\t\toSettings._iDisplayStart = oInit.iDisplayStart;\n\t\t\t}\n\t\t\t\n\t\t\tif ( oInit.iDeferLoading !== null )\n\t\t\t{\n\t\t\t\toSettings.bDeferLoading = true;\n\t\t\t\tvar tmp = Array.isArray( oInit.iDeferLoading );\n\t\t\t\toSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;\n\t\t\t\toSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;\n\t\t\t}\n\t\t\t\n\t\t\t/* Language definitions */\n\t\t\tvar oLanguage = oSettings.oLanguage;\n\t\t\t$.extend( true, oLanguage, oInit.oLanguage );\n\t\t\t\n\t\t\tif ( oLanguage.sUrl )\n\t\t\t{\n\t\t\t\t/* Get the language definitions from a file - because this Ajax call makes the language\n\t\t\t\t * get async to the remainder of this function we use bInitHandedOff to indicate that\n\t\t\t\t * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor\n\t\t\t\t */\n\t\t\t\t$.ajax( {\n\t\t\t\t\tdataType: 'json',\n\t\t\t\t\turl: oLanguage.sUrl,\n\t\t\t\t\tsuccess: function ( json ) {\n\t\t\t\t\t\t_fnCamelToHungarian( defaults.oLanguage, json );\n\t\t\t\t\t\t_fnLanguageCompat( json );\n\t\t\t\t\t\t$.extend( true, oLanguage, json, oSettings.oInit.oLanguage );\n\t\t\t\n\t\t\t\t\t\t_fnCallbackFire( oSettings, null, 'i18n', [oSettings]);\n\t\t\t\t\t\t_fnInitialise( oSettings );\n\t\t\t\t\t},\n\t\t\t\t\terror: function () {\n\t\t\t\t\t\t// Error occurred loading language file, continue on as best we can\n\t\t\t\t\t\t_fnInitialise( oSettings );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\tbInitHandedOff = true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnCallbackFire( oSettings, null, 'i18n', [oSettings]);\n\t\t\t}\n\t\t\t\n\t\t\t/*\n\t\t\t * Stripes\n\t\t\t */\n\t\t\tif ( oInit.asStripeClasses === null )\n\t\t\t{\n\t\t\t\toSettings.asStripeClasses =[\n\t\t\t\t\toClasses.sStripeOdd,\n\t\t\t\t\toClasses.sStripeEven\n\t\t\t\t];\n\t\t\t}\n\t\t\t\n\t\t\t/* Remove row stripe classes if they are already on the table row */\n\t\t\tvar stripeClasses = oSettings.asStripeClasses;\n\t\t\tvar rowOne = $this.children('tbody').find('tr').eq(0);\n\t\t\tif ( $.inArray( true, $.map( stripeClasses, function(el, i) {\n\t\t\t\treturn rowOne.hasClass(el);\n\t\t\t} ) ) !== -1 ) {\n\t\t\t\t$('tbody tr', this).removeClass( stripeClasses.join(' ') );\n\t\t\t\toSettings.asDestroyStripes = stripeClasses.slice();\n\t\t\t}\n\t\t\t\n\t\t\t/*\n\t\t\t * Columns\n\t\t\t * See if we should load columns automatically or use defined ones\n\t\t\t */\n\t\t\tvar anThs = [];\n\t\t\tvar aoColumnsInit;\n\t\t\tvar nThead = this.getElementsByTagName('thead');\n\t\t\tif ( nThead.length !== 0 )\n\t\t\t{\n\t\t\t\t_fnDetectHeader( oSettings.aoHeader, nThead[0] );\n\t\t\t\tanThs = _fnGetUniqueThs( oSettings );\n\t\t\t}\n\t\t\t\n\t\t\t/* If not given a column array, generate one with nulls */\n\t\t\tif ( oInit.aoColumns === null )\n\t\t\t{\n\t\t\t\taoColumnsInit = [];\n\t\t\t\tfor ( i=0, iLen=anThs.length ; i<iLen ; i++ )\n\t\t\t\t{\n\t\t\t\t\taoColumnsInit.push( null );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\taoColumnsInit = oInit.aoColumns;\n\t\t\t}\n\t\t\t\n\t\t\t/* Add the columns */\n\t\t\tfor ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\t_fnAddColumn( oSettings, anThs ? anThs[i] : null );\n\t\t\t}\n\t\t\t\n\t\t\t/* Apply the column definitions */\n\t\t\t_fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {\n\t\t\t\t_fnColumnOptions( oSettings, iCol, oDef );\n\t\t\t} );\n\t\t\t\n\t\t\t/* HTML5 attribute detection - build an mData object automatically if the\n\t\t\t * attributes are found\n\t\t\t */\n\t\t\tif ( rowOne.length ) {\n\t\t\t\tvar a = function ( cell, name ) {\n\t\t\t\t\treturn cell.getAttribute( 'data-'+name ) !== null ? name : null;\n\t\t\t\t};\n\t\t\t\n\t\t\t\t$( rowOne[0] ).children('th, td').each( function (i, cell) {\n\t\t\t\t\tvar col = oSettings.aoColumns[i];\n\t\t\t\n\t\t\t\t\tif (! col) {\n\t\t\t\t\t\t_fnLog( oSettings, 0, 'Incorrect column count', 18 );\n\t\t\t\t\t}\n\t\t\t\n\t\t\t\t\tif ( col.mData === i ) {\n\t\t\t\t\t\tvar sort = a( cell, 'sort' ) || a( cell, 'order' );\n\t\t\t\t\t\tvar filter = a( cell, 'filter' ) || a( cell, 'search' );\n\t\t\t\n\t\t\t\t\t\tif ( sort !== null || filter !== null ) {\n\t\t\t\t\t\t\tcol.mData = {\n\t\t\t\t\t\t\t\t_:      i+'.display',\n\t\t\t\t\t\t\t\tsort:   sort !== null   ? i+'.@data-'+sort   : undefined,\n\t\t\t\t\t\t\t\ttype:   sort !== null   ? i+'.@data-'+sort   : undefined,\n\t\t\t\t\t\t\t\tfilter: filter !== null ? i+'.@data-'+filter : undefined\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcol._isArrayHost = true;\n\t\t\t\n\t\t\t\t\t\t\t_fnColumnOptions( oSettings, i );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\t\t\t\n\t\t\tvar features = oSettings.oFeatures;\n\t\t\tvar loadedInit = function () {\n\t\t\t\t/*\n\t\t\t\t * Sorting\n\t\t\t\t * @todo For modularisation (1.11) this needs to do into a sort start up handler\n\t\t\t\t */\n\t\t\t\n\t\t\t\t// If aaSorting is not defined, then we use the first indicator in asSorting\n\t\t\t\t// in case that has been altered, so the default sort reflects that option\n\t\t\t\tif ( oInit.aaSorting === undefined ) {\n\t\t\t\t\tvar sorting = oSettings.aaSorting;\n\t\t\t\t\tfor ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {\n\t\t\t\t\t\tsorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* Do a first pass on the sorting classes (allows any size changes to be taken into\n\t\t\t\t * account, and also will apply sorting disabled classes if disabled\n\t\t\t\t */\n\t\t\t\t_fnSortingClasses( oSettings );\n\t\t\t\n\t\t\t\tif ( features.bSort ) {\n\t\t\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback', function () {\n\t\t\t\t\t\tif ( oSettings.bSorted ) {\n\t\t\t\t\t\t\tvar aSort = _fnSortFlatten( oSettings );\n\t\t\t\t\t\t\tvar sortedColumns = {};\n\t\t\t\n\t\t\t\t\t\t\t$.each( aSort, function (i, val) {\n\t\t\t\t\t\t\t\tsortedColumns[ val.src ] = val.dir;\n\t\t\t\t\t\t\t} );\n\t\t\t\n\t\t\t\t\t\t\t_fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );\n\t\t\t\t\t\t\t_fnSortAria( oSettings );\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t\n\t\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback', function () {\n\t\t\t\t\tif ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {\n\t\t\t\t\t\t_fnSortingClasses( oSettings );\n\t\t\t\t\t}\n\t\t\t\t}, 'sc' );\n\t\t\t\n\t\t\t\n\t\t\t\t/*\n\t\t\t\t * Final init\n\t\t\t\t * Cache the header, body and footer as required, creating them if needed\n\t\t\t\t */\n\t\t\t\n\t\t\t\t// Work around for Webkit bug 83867 - store the caption-side before removing from doc\n\t\t\t\tvar captions = $this.children('caption').each( function () {\n\t\t\t\t\tthis._captionSide = $(this).css('caption-side');\n\t\t\t\t} );\n\t\t\t\n\t\t\t\tvar thead = $this.children('thead');\n\t\t\t\tif ( thead.length === 0 ) {\n\t\t\t\t\tthead = $('<thead/>').appendTo($this);\n\t\t\t\t}\n\t\t\t\toSettings.nTHead = thead[0];\n\t\t\t\n\t\t\t\tvar tbody = $this.children('tbody');\n\t\t\t\tif ( tbody.length === 0 ) {\n\t\t\t\t\ttbody = $('<tbody/>').insertAfter(thead);\n\t\t\t\t}\n\t\t\t\toSettings.nTBody = tbody[0];\n\t\t\t\n\t\t\t\tvar tfoot = $this.children('tfoot');\n\t\t\t\tif ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== \"\" || oSettings.oScroll.sY !== \"\") ) {\n\t\t\t\t\t// If we are a scrolling table, and no footer has been given, then we need to create\n\t\t\t\t\t// a tfoot element for the caption element to be appended to\n\t\t\t\t\ttfoot = $('<tfoot/>').appendTo($this);\n\t\t\t\t}\n\t\t\t\n\t\t\t\tif ( tfoot.length === 0 || tfoot.children().length === 0 ) {\n\t\t\t\t\t$this.addClass( oClasses.sNoFooter );\n\t\t\t\t}\n\t\t\t\telse if ( tfoot.length > 0 ) {\n\t\t\t\t\toSettings.nTFoot = tfoot[0];\n\t\t\t\t\t_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* Check if there is data passing into the constructor */\n\t\t\t\tif ( oInit.aaData ) {\n\t\t\t\t\tfor ( i=0 ; i<oInit.aaData.length ; i++ ) {\n\t\t\t\t\t\t_fnAddData( oSettings, oInit.aaData[ i ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {\n\t\t\t\t\t/* Grab the data from the page - only do this when deferred loading or no Ajax\n\t\t\t\t\t * source since there is no point in reading the DOM data if we are then going\n\t\t\t\t\t * to replace it with Ajax data\n\t\t\t\t\t */\n\t\t\t\t\t_fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );\n\t\t\t\t}\n\t\t\t\n\t\t\t\t/* Copy the data index array */\n\t\t\t\toSettings.aiDisplay = oSettings.aiDisplayMaster.slice();\n\t\t\t\n\t\t\t\t/* Initialisation complete - table can be drawn */\n\t\t\t\toSettings.bInitialised = true;\n\t\t\t\n\t\t\t\t/* Check if we need to initialise the table (it might not have been handed off to the\n\t\t\t\t * language processor)\n\t\t\t\t */\n\t\t\t\tif ( bInitHandedOff === false ) {\n\t\t\t\t\t_fnInitialise( oSettings );\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t/* Must be done after everything which can be overridden by the state saving! */\n\t\t\t_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );\n\t\t\t\n\t\t\tif ( oInit.bStateSave )\n\t\t\t{\n\t\t\t\tfeatures.bStateSave = true;\n\t\t\t\t_fnLoadState( oSettings, oInit, loadedInit );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tloadedInit();\n\t\t\t}\n\t\t\t\n\t\t} );\n\t\t_that = null;\n\t\treturn this;\n\t};\n\t\n\t\n\t/*\n\t * It is useful to have variables which are scoped locally so only the\n\t * DataTables functions can access them and they don't leak into global space.\n\t * At the same time these functions are often useful over multiple files in the\n\t * core and API, so we list, or at least document, all variables which are used\n\t * by DataTables as private variables here. This also ensures that there is no\n\t * clashing of variable names and that they can easily referenced for reuse.\n\t */\n\t\n\t\n\t// Defined else where\n\t//  _selector_run\n\t//  _selector_opts\n\t//  _selector_first\n\t//  _selector_row_indexes\n\t\n\tvar _ext; // DataTable.ext\n\tvar _Api; // DataTable.Api\n\tvar _api_register; // DataTable.Api.register\n\tvar _api_registerPlural; // DataTable.Api.registerPlural\n\t\n\tvar _re_dic = {};\n\tvar _re_new_lines = /[\\r\\n\\u2028]/g;\n\tvar _re_html = /<.*?>/g;\n\t\n\t// This is not strict ISO8601 - Date.parse() is quite lax, although\n\t// implementations differ between browsers.\n\tvar _re_date = /^\\d{2,4}[\\.\\/\\-]\\d{1,2}[\\.\\/\\-]\\d{1,2}([T ]{1}\\d{1,2}[:\\.]\\d{2}([\\.:]\\d{2})?)?$/;\n\t\n\t// Escape regular expression special characters\n\tvar _re_escape_regex = new RegExp( '(\\\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\\\', '$', '^', '-' ].join('|\\\\') + ')', 'g' );\n\t\n\t// http://en.wikipedia.org/wiki/Foreign_exchange_market\n\t// - \\u20BD - Russian ruble.\n\t// - \\u20a9 - South Korean Won\n\t// - \\u20BA - Turkish Lira\n\t// - \\u20B9 - Indian Rupee\n\t// - R - Brazil (R$) and South Africa\n\t// - fr - Swiss Franc\n\t// - kr - Swedish krona, Norwegian krone and Danish krone\n\t// - \\u2009 is thin space and \\u202F is narrow no-break space, both used in many\n\t// - Ƀ - Bitcoin\n\t// - Ξ - Ethereum\n\t//   standards as thousands separators.\n\tvar _re_formatted_numeric = /['\\u00A0,$£€¥%\\u2009\\u202F\\u20BD\\u20a9\\u20BArfkɃΞ]/gi;\n\t\n\t\n\tvar _empty = function ( d ) {\n\t\treturn !d || d === true || d === '-' ? true : false;\n\t};\n\t\n\t\n\tvar _intVal = function ( s ) {\n\t\tvar integer = parseInt( s, 10 );\n\t\treturn !isNaN(integer) && isFinite(s) ? integer : null;\n\t};\n\t\n\t// Convert from a formatted number with characters other than `.` as the\n\t// decimal place, to a Javascript number\n\tvar _numToDecimal = function ( num, decimalPoint ) {\n\t\t// Cache created regular expressions for speed as this function is called often\n\t\tif ( ! _re_dic[ decimalPoint ] ) {\n\t\t\t_re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );\n\t\t}\n\t\treturn typeof num === 'string' && decimalPoint !== '.' ?\n\t\t\tnum.replace( /\\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :\n\t\t\tnum;\n\t};\n\t\n\t\n\tvar _isNumber = function ( d, decimalPoint, formatted ) {\n\t\tvar type = typeof d;\n\t\tvar strType = type === 'string';\n\t\n\t\tif ( type === 'number' || type === 'bigint') {\n\t\t\treturn true;\n\t\t}\n\t\n\t\t// If empty return immediately so there must be a number if it is a\n\t\t// formatted string (this stops the string \"k\", or \"kr\", etc being detected\n\t\t// as a formatted number for currency\n\t\tif ( _empty( d ) ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\tif ( decimalPoint && strType ) {\n\t\t\td = _numToDecimal( d, decimalPoint );\n\t\t}\n\t\n\t\tif ( formatted && strType ) {\n\t\t\td = d.replace( _re_formatted_numeric, '' );\n\t\t}\n\t\n\t\treturn !isNaN( parseFloat(d) ) && isFinite( d );\n\t};\n\t\n\t\n\t// A string without HTML in it can be considered to be HTML still\n\tvar _isHtml = function ( d ) {\n\t\treturn _empty( d ) || typeof d === 'string';\n\t};\n\t\n\t\n\tvar _htmlNumeric = function ( d, decimalPoint, formatted ) {\n\t\tif ( _empty( d ) ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\tvar html = _isHtml( d );\n\t\treturn ! html ?\n\t\t\tnull :\n\t\t\t_isNumber( _stripHtml( d ), decimalPoint, formatted ) ?\n\t\t\t\ttrue :\n\t\t\t\tnull;\n\t};\n\t\n\t\n\tvar _pluck = function ( a, prop, prop2 ) {\n\t\tvar out = [];\n\t\tvar i=0, ien=a.length;\n\t\n\t\t// Could have the test in the loop for slightly smaller code, but speed\n\t\t// is essential here\n\t\tif ( prop2 !== undefined ) {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tif ( a[i] && a[i][ prop ] ) {\n\t\t\t\t\tout.push( a[i][ prop ][ prop2 ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tif ( a[i] ) {\n\t\t\t\t\tout.push( a[i][ prop ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\t// Basically the same as _pluck, but rather than looping over `a` we use `order`\n\t// as the indexes to pick from `a`\n\tvar _pluck_order = function ( a, order, prop, prop2 )\n\t{\n\t\tvar out = [];\n\t\tvar i=0, ien=order.length;\n\t\n\t\t// Could have the test in the loop for slightly smaller code, but speed\n\t\t// is essential here\n\t\tif ( prop2 !== undefined ) {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tif ( a[ order[i] ][ prop ] ) {\n\t\t\t\t\tout.push( a[ order[i] ][ prop ][ prop2 ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfor ( ; i<ien ; i++ ) {\n\t\t\t\tout.push( a[ order[i] ][ prop ] );\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\tvar _range = function ( len, start )\n\t{\n\t\tvar out = [];\n\t\tvar end;\n\t\n\t\tif ( start === undefined ) {\n\t\t\tstart = 0;\n\t\t\tend = len;\n\t\t}\n\t\telse {\n\t\t\tend = start;\n\t\t\tstart = len;\n\t\t}\n\t\n\t\tfor ( var i=start ; i<end ; i++ ) {\n\t\t\tout.push( i );\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\tvar _removeEmpty = function ( a )\n\t{\n\t\tvar out = [];\n\t\n\t\tfor ( var i=0, ien=a.length ; i<ien ; i++ ) {\n\t\t\tif ( a[i] ) { // careful - will remove all falsy values!\n\t\t\t\tout.push( a[i] );\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t\n\tvar _stripHtml = function ( d ) {\n\t\treturn d\n\t\t\t.replace( _re_html, '' ) // Complete tags\n\t\t\t.replace(/<script/i, ''); // Safety for incomplete script tag\n\t};\n\t\n\t\n\t/**\n\t * Determine if all values in the array are unique. This means we can short\n\t * cut the _unique method at the cost of a single loop. A sorted array is used\n\t * to easily check the values.\n\t *\n\t * @param  {array} src Source array\n\t * @return {boolean} true if all unique, false otherwise\n\t * @ignore\n\t */\n\tvar _areAllUnique = function ( src ) {\n\t\tif ( src.length < 2 ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\tvar sorted = src.slice().sort();\n\t\tvar last = sorted[0];\n\t\n\t\tfor ( var i=1, ien=sorted.length ; i<ien ; i++ ) {\n\t\t\tif ( sorted[i] === last ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\n\t\t\tlast = sorted[i];\n\t\t}\n\t\n\t\treturn true;\n\t};\n\t\n\t\n\t/**\n\t * Find the unique elements in a source array.\n\t *\n\t * @param  {array} src Source array\n\t * @return {array} Array of unique items\n\t * @ignore\n\t */\n\tvar _unique = function ( src )\n\t{\n\t\tif ( _areAllUnique( src ) ) {\n\t\t\treturn src.slice();\n\t\t}\n\t\n\t\t// A faster unique method is to use object keys to identify used values,\n\t\t// but this doesn't work with arrays or objects, which we must also\n\t\t// consider. See jsperf.com/compare-array-unique-versions/4 for more\n\t\t// information.\n\t\tvar\n\t\t\tout = [],\n\t\t\tval,\n\t\t\ti, ien=src.length,\n\t\t\tj, k=0;\n\t\n\t\tagain: for ( i=0 ; i<ien ; i++ ) {\n\t\t\tval = src[i];\n\t\n\t\t\tfor ( j=0 ; j<k ; j++ ) {\n\t\t\t\tif ( out[j] === val ) {\n\t\t\t\t\tcontinue again;\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tout.push( val );\n\t\t\tk++;\n\t\t}\n\t\n\t\treturn out;\n\t};\n\t\n\t// Surprisingly this is faster than [].concat.apply\n\t// https://jsperf.com/flatten-an-array-loop-vs-reduce/2\n\tvar _flatten = function (out, val) {\n\t\tif (Array.isArray(val)) {\n\t\t\tfor (var i=0 ; i<val.length ; i++) {\n\t\t\t\t_flatten(out, val[i]);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tout.push(val);\n\t\t}\n\t  \n\t\treturn out;\n\t}\n\t\n\tvar _includes = function (search, start) {\n\t\tif (start === undefined) {\n\t\t\tstart = 0;\n\t\t}\n\t\n\t\treturn this.indexOf(search, start) !== -1;\t\n\t};\n\t\n\t// Array.isArray polyfill.\n\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray\n\tif (! Array.isArray) {\n\t    Array.isArray = function(arg) {\n\t        return Object.prototype.toString.call(arg) === '[object Array]';\n\t    };\n\t}\n\t\n\tif (! Array.prototype.includes) {\n\t\tArray.prototype.includes = _includes;\n\t}\n\t\n\t// .trim() polyfill\n\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim\n\tif (!String.prototype.trim) {\n\t  String.prototype.trim = function () {\n\t    return this.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n\t  };\n\t}\n\t\n\tif (! String.prototype.includes) {\n\t\tString.prototype.includes = _includes;\n\t}\n\t\n\t/**\n\t * DataTables utility methods\n\t * \n\t * This namespace provides helper methods that DataTables uses internally to\n\t * create a DataTable, but which are not exclusively used only for DataTables.\n\t * These methods can be used by extension authors to save the duplication of\n\t * code.\n\t *\n\t *  @namespace\n\t */\n\tDataTable.util = {\n\t\t/**\n\t\t * Throttle the calls to a function. Arguments and context are maintained\n\t\t * for the throttled function.\n\t\t *\n\t\t * @param {function} fn Function to be called\n\t\t * @param {integer} freq Call frequency in mS\n\t\t * @return {function} Wrapped function\n\t\t */\n\t\tthrottle: function ( fn, freq ) {\n\t\t\tvar\n\t\t\t\tfrequency = freq !== undefined ? freq : 200,\n\t\t\t\tlast,\n\t\t\t\ttimer;\n\t\n\t\t\treturn function () {\n\t\t\t\tvar\n\t\t\t\t\tthat = this,\n\t\t\t\t\tnow  = +new Date(),\n\t\t\t\t\targs = arguments;\n\t\n\t\t\t\tif ( last && now < last + frequency ) {\n\t\t\t\t\tclearTimeout( timer );\n\t\n\t\t\t\t\ttimer = setTimeout( function () {\n\t\t\t\t\t\tlast = undefined;\n\t\t\t\t\t\tfn.apply( that, args );\n\t\t\t\t\t}, frequency );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlast = now;\n\t\t\t\t\tfn.apply( that, args );\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Escape a string such that it can be used in a regular expression\n\t\t *\n\t\t *  @param {string} val string to escape\n\t\t *  @returns {string} escaped string\n\t\t */\n\t\tescapeRegex: function ( val ) {\n\t\t\treturn val.replace( _re_escape_regex, '\\\\$1' );\n\t\t},\n\t\n\t\t/**\n\t\t * Create a function that will write to a nested object or array\n\t\t * @param {*} source JSON notation string\n\t\t * @returns Write function\n\t\t */\n\t\tset: function ( source ) {\n\t\t\tif ( $.isPlainObject( source ) ) {\n\t\t\t\t/* Unlike get, only the underscore (global) option is used for for\n\t\t\t\t * setting data since we don't know the type here. This is why an object\n\t\t\t\t * option is not documented for `mData` (which is read/write), but it is\n\t\t\t\t * for `mRender` which is read only.\n\t\t\t\t */\n\t\t\t\treturn DataTable.util.set( source._ );\n\t\t\t}\n\t\t\telse if ( source === null ) {\n\t\t\t\t// Nothing to do when the data source is null\n\t\t\t\treturn function () {};\n\t\t\t}\n\t\t\telse if ( typeof source === 'function' ) {\n\t\t\t\treturn function (data, val, meta) {\n\t\t\t\t\tsource( data, 'set', val, meta );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||\n\t\t\t\t\t  source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )\n\t\t\t{\n\t\t\t\t// Like the get, we need to get data from a nested object\n\t\t\t\tvar setData = function (data, val, src) {\n\t\t\t\t\tvar a = _fnSplitObjNotation( src ), b;\n\t\t\t\t\tvar aLast = a[a.length-1];\n\t\t\t\t\tvar arrayNotation, funcNotation, o, innerSrc;\n\t\t\n\t\t\t\t\tfor ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) {\n\t\t\t\t\t\t// Protect against prototype pollution\n\t\t\t\t\t\tif (a[i] === '__proto__' || a[i] === 'constructor') {\n\t\t\t\t\t\t\tthrow new Error('Cannot set prototype values');\n\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t// Check if we are dealing with an array notation request\n\t\t\t\t\t\tarrayNotation = a[i].match(__reArray);\n\t\t\t\t\t\tfuncNotation = a[i].match(__reFn);\n\t\t\n\t\t\t\t\t\tif ( arrayNotation ) {\n\t\t\t\t\t\t\ta[i] = a[i].replace(__reArray, '');\n\t\t\t\t\t\t\tdata[ a[i] ] = [];\n\t\t\n\t\t\t\t\t\t\t// Get the remainder of the nested object to set so we can recurse\n\t\t\t\t\t\t\tb = a.slice();\n\t\t\t\t\t\t\tb.splice( 0, i+1 );\n\t\t\t\t\t\t\tinnerSrc = b.join('.');\n\t\t\n\t\t\t\t\t\t\t// Traverse each entry in the array setting the properties requested\n\t\t\t\t\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\t\t\t\t\tfor ( var j=0, jLen=val.length ; j<jLen ; j++ ) {\n\t\t\t\t\t\t\t\t\to = {};\n\t\t\t\t\t\t\t\t\tsetData( o, val[j], innerSrc );\n\t\t\t\t\t\t\t\t\tdata[ a[i] ].push( o );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t// We've been asked to save data to an array, but it\n\t\t\t\t\t\t\t\t// isn't array data to be saved. Best that can be done\n\t\t\t\t\t\t\t\t// is to just save the value.\n\t\t\t\t\t\t\t\tdata[ a[i] ] = val;\n\t\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t\t// The inner call to setData has already traversed through the remainder\n\t\t\t\t\t\t\t// of the source and has set the data, thus we can exit here\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( funcNotation ) {\n\t\t\t\t\t\t\t// Function call\n\t\t\t\t\t\t\ta[i] = a[i].replace(__reFn, '');\n\t\t\t\t\t\t\tdata = data[ a[i] ]( val );\n\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t// If the nested object doesn't currently exist - since we are\n\t\t\t\t\t\t// trying to set the value - create it\n\t\t\t\t\t\tif ( data[ a[i] ] === null || data[ a[i] ] === undefined ) {\n\t\t\t\t\t\t\tdata[ a[i] ] = {};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdata = data[ a[i] ];\n\t\t\t\t\t}\n\t\t\n\t\t\t\t\t// Last item in the input - i.e, the actual set\n\t\t\t\t\tif ( aLast.match(__reFn ) ) {\n\t\t\t\t\t\t// Function call\n\t\t\t\t\t\tdata = data[ aLast.replace(__reFn, '') ]( val );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// If array notation is used, we just want to strip it and use the property name\n\t\t\t\t\t\t// and assign the value. If it isn't used, then we get the result we want anyway\n\t\t\t\t\t\tdata[ aLast.replace(__reArray, '') ] = val;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\n\t\t\t\treturn function (data, val) { // meta is also passed in, but not used\n\t\t\t\t\treturn setData( data, val, source );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Array or flat object mapping\n\t\t\t\treturn function (data, val) { // meta is also passed in, but not used\n\t\t\t\t\tdata[source] = val;\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t\n\t\t/**\n\t\t * Create a function that will read nested objects from arrays, based on JSON notation\n\t\t * @param {*} source JSON notation string\n\t\t * @returns Value read\n\t\t */\n\t\tget: function ( source ) {\n\t\t\tif ( $.isPlainObject( source ) ) {\n\t\t\t\t// Build an object of get functions, and wrap them in a single call\n\t\t\t\tvar o = {};\n\t\t\t\t$.each( source, function (key, val) {\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\to[key] = DataTable.util.get( val );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\n\t\t\t\treturn function (data, type, row, meta) {\n\t\t\t\t\tvar t = o[type] || o._;\n\t\t\t\t\treturn t !== undefined ?\n\t\t\t\t\t\tt(data, type, row, meta) :\n\t\t\t\t\t\tdata;\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( source === null ) {\n\t\t\t\t// Give an empty string for rendering / sorting etc\n\t\t\t\treturn function (data) { // type, row and meta also passed, but not used\n\t\t\t\t\treturn data;\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( typeof source === 'function' ) {\n\t\t\t\treturn function (data, type, row, meta) {\n\t\t\t\t\treturn source( data, type, row, meta );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||\n\t\t\t\t\t  source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )\n\t\t\t{\n\t\t\t\t/* If there is a . in the source string then the data source is in a\n\t\t\t\t * nested object so we loop over the data for each level to get the next\n\t\t\t\t * level down. On each loop we test for undefined, and if found immediately\n\t\t\t\t * return. This allows entire objects to be missing and sDefaultContent to\n\t\t\t\t * be used if defined, rather than throwing an error\n\t\t\t\t */\n\t\t\t\tvar fetchData = function (data, type, src) {\n\t\t\t\t\tvar arrayNotation, funcNotation, out, innerSrc;\n\t\t\n\t\t\t\t\tif ( src !== \"\" ) {\n\t\t\t\t\t\tvar a = _fnSplitObjNotation( src );\n\t\t\n\t\t\t\t\t\tfor ( var i=0, iLen=a.length ; i<iLen ; i++ ) {\n\t\t\t\t\t\t\t// Check if we are dealing with special notation\n\t\t\t\t\t\t\tarrayNotation = a[i].match(__reArray);\n\t\t\t\t\t\t\tfuncNotation = a[i].match(__reFn);\n\t\t\n\t\t\t\t\t\t\tif ( arrayNotation ) {\n\t\t\t\t\t\t\t\t// Array notation\n\t\t\t\t\t\t\t\ta[i] = a[i].replace(__reArray, '');\n\t\t\n\t\t\t\t\t\t\t\t// Condition allows simply [] to be passed in\n\t\t\t\t\t\t\t\tif ( a[i] !== \"\" ) {\n\t\t\t\t\t\t\t\t\tdata = data[ a[i] ];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tout = [];\n\t\t\n\t\t\t\t\t\t\t\t// Get the remainder of the nested object to get\n\t\t\t\t\t\t\t\ta.splice( 0, i+1 );\n\t\t\t\t\t\t\t\tinnerSrc = a.join('.');\n\t\t\n\t\t\t\t\t\t\t\t// Traverse each entry in the array getting the properties requested\n\t\t\t\t\t\t\t\tif ( Array.isArray( data ) ) {\n\t\t\t\t\t\t\t\t\tfor ( var j=0, jLen=data.length ; j<jLen ; j++ ) {\n\t\t\t\t\t\t\t\t\t\tout.push( fetchData( data[j], type, innerSrc ) );\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t\t\t// If a string is given in between the array notation indicators, that\n\t\t\t\t\t\t\t\t// is used to join the strings together, otherwise an array is returned\n\t\t\t\t\t\t\t\tvar join = arrayNotation[0].substring(1, arrayNotation[0].length-1);\n\t\t\t\t\t\t\t\tdata = (join===\"\") ? out : out.join(join);\n\t\t\n\t\t\t\t\t\t\t\t// The inner call to fetchData has already traversed through the remainder\n\t\t\t\t\t\t\t\t// of the source requested, so we exit from the loop\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( funcNotation ) {\n\t\t\t\t\t\t\t\t// Function call\n\t\t\t\t\t\t\t\ta[i] = a[i].replace(__reFn, '');\n\t\t\t\t\t\t\t\tdata = data[ a[i] ]();\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\n\t\t\t\t\t\t\tif (data === null || data[ a[i] ] === null) {\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if ( data === undefined || data[ a[i] ] === undefined ) {\n\t\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\tdata = data[ a[i] ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\n\t\t\t\t\treturn data;\n\t\t\t\t};\n\t\t\n\t\t\t\treturn function (data, type) { // row and meta also passed, but not used\n\t\t\t\t\treturn fetchData( data, type, source );\n\t\t\t\t};\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Array or flat object mapping\n\t\t\t\treturn function (data, type) { // row and meta also passed, but not used\n\t\t\t\t\treturn data[source];\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t};\n\t\n\t\n\t\n\t/**\n\t * Create a mapping object that allows camel case parameters to be looked up\n\t * for their Hungarian counterparts. The mapping is stored in a private\n\t * parameter called `_hungarianMap` which can be accessed on the source object.\n\t *  @param {object} o\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnHungarianMap ( o )\n\t{\n\t\tvar\n\t\t\thungarian = 'a aa ai ao as b fn i m o s ',\n\t\t\tmatch,\n\t\t\tnewKey,\n\t\t\tmap = {};\n\t\n\t\t$.each( o, function (key, val) {\n\t\t\tmatch = key.match(/^([^A-Z]+?)([A-Z])/);\n\t\n\t\t\tif ( match && hungarian.indexOf(match[1]+' ') !== -1 )\n\t\t\t{\n\t\t\t\tnewKey = key.replace( match[0], match[2].toLowerCase() );\n\t\t\t\tmap[ newKey ] = key;\n\t\n\t\t\t\tif ( match[1] === 'o' )\n\t\t\t\t{\n\t\t\t\t\t_fnHungarianMap( o[key] );\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t\n\t\to._hungarianMap = map;\n\t}\n\t\n\t\n\t/**\n\t * Convert from camel case parameters to Hungarian, based on a Hungarian map\n\t * created by _fnHungarianMap.\n\t *  @param {object} src The model object which holds all parameters that can be\n\t *    mapped.\n\t *  @param {object} user The object to convert from camel case to Hungarian.\n\t *  @param {boolean} force When set to `true`, properties which already have a\n\t *    Hungarian value in the `user` object will be overwritten. Otherwise they\n\t *    won't be.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCamelToHungarian ( src, user, force )\n\t{\n\t\tif ( ! src._hungarianMap ) {\n\t\t\t_fnHungarianMap( src );\n\t\t}\n\t\n\t\tvar hungarianKey;\n\t\n\t\t$.each( user, function (key, val) {\n\t\t\thungarianKey = src._hungarianMap[ key ];\n\t\n\t\t\tif ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )\n\t\t\t{\n\t\t\t\t// For objects, we need to buzz down into the object to copy parameters\n\t\t\t\tif ( hungarianKey.charAt(0) === 'o' )\n\t\t\t\t{\n\t\t\t\t\t// Copy the camelCase options over to the hungarian\n\t\t\t\t\tif ( ! user[ hungarianKey ] ) {\n\t\t\t\t\t\tuser[ hungarianKey ] = {};\n\t\t\t\t\t}\n\t\t\t\t\t$.extend( true, user[hungarianKey], user[key] );\n\t\n\t\t\t\t\t_fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tuser[hungarianKey] = user[ key ];\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Language compatibility - when certain options are given, and others aren't, we\n\t * need to duplicate the values over, in order to provide backwards compatibility\n\t * with older language files.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnLanguageCompat( lang )\n\t{\n\t\t// Note the use of the Hungarian notation for the parameters in this method as\n\t\t// this is called after the mapping of camelCase to Hungarian\n\t\tvar defaults = DataTable.defaults.oLanguage;\n\t\n\t\t// Default mapping\n\t\tvar defaultDecimal = defaults.sDecimal;\n\t\tif ( defaultDecimal ) {\n\t\t\t_addNumericSort( defaultDecimal );\n\t\t}\n\t\n\t\tif ( lang ) {\n\t\t\tvar zeroRecords = lang.sZeroRecords;\n\t\n\t\t\t// Backwards compatibility - if there is no sEmptyTable given, then use the same as\n\t\t\t// sZeroRecords - assuming that is given.\n\t\t\tif ( ! lang.sEmptyTable && zeroRecords &&\n\t\t\t\tdefaults.sEmptyTable === \"No data available in table\" )\n\t\t\t{\n\t\t\t\t_fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );\n\t\t\t}\n\t\n\t\t\t// Likewise with loading records\n\t\t\tif ( ! lang.sLoadingRecords && zeroRecords &&\n\t\t\t\tdefaults.sLoadingRecords === \"Loading...\" )\n\t\t\t{\n\t\t\t\t_fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );\n\t\t\t}\n\t\n\t\t\t// Old parameter name of the thousands separator mapped onto the new\n\t\t\tif ( lang.sInfoThousands ) {\n\t\t\t\tlang.sThousands = lang.sInfoThousands;\n\t\t\t}\n\t\n\t\t\tvar decimal = lang.sDecimal;\n\t\t\tif ( decimal && defaultDecimal !== decimal ) {\n\t\t\t\t_addNumericSort( decimal );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Map one parameter onto another\n\t *  @param {object} o Object to map\n\t *  @param {*} knew The new parameter name\n\t *  @param {*} old The old parameter name\n\t */\n\tvar _fnCompatMap = function ( o, knew, old ) {\n\t\tif ( o[ knew ] !== undefined ) {\n\t\t\to[ old ] = o[ knew ];\n\t\t}\n\t};\n\t\n\t\n\t/**\n\t * Provide backwards compatibility for the main DT options. Note that the new\n\t * options are mapped onto the old parameters, so this is an external interface\n\t * change only.\n\t *  @param {object} init Object to map\n\t */\n\tfunction _fnCompatOpts ( init )\n\t{\n\t\t_fnCompatMap( init, 'ordering',      'bSort' );\n\t\t_fnCompatMap( init, 'orderMulti',    'bSortMulti' );\n\t\t_fnCompatMap( init, 'orderClasses',  'bSortClasses' );\n\t\t_fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );\n\t\t_fnCompatMap( init, 'order',         'aaSorting' );\n\t\t_fnCompatMap( init, 'orderFixed',    'aaSortingFixed' );\n\t\t_fnCompatMap( init, 'paging',        'bPaginate' );\n\t\t_fnCompatMap( init, 'pagingType',    'sPaginationType' );\n\t\t_fnCompatMap( init, 'pageLength',    'iDisplayLength' );\n\t\t_fnCompatMap( init, 'searching',     'bFilter' );\n\t\n\t\t// Boolean initialisation of x-scrolling\n\t\tif ( typeof init.sScrollX === 'boolean' ) {\n\t\t\tinit.sScrollX = init.sScrollX ? '100%' : '';\n\t\t}\n\t\tif ( typeof init.scrollX === 'boolean' ) {\n\t\t\tinit.scrollX = init.scrollX ? '100%' : '';\n\t\t}\n\t\n\t\t// Column search objects are in an array, so it needs to be converted\n\t\t// element by element\n\t\tvar searchCols = init.aoSearchCols;\n\t\n\t\tif ( searchCols ) {\n\t\t\tfor ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {\n\t\t\t\tif ( searchCols[i] ) {\n\t\t\t\t\t_fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Provide backwards compatibility for column options. Note that the new options\n\t * are mapped onto the old parameters, so this is an external interface change\n\t * only.\n\t *  @param {object} init Object to map\n\t */\n\tfunction _fnCompatCols ( init )\n\t{\n\t\t_fnCompatMap( init, 'orderable',     'bSortable' );\n\t\t_fnCompatMap( init, 'orderData',     'aDataSort' );\n\t\t_fnCompatMap( init, 'orderSequence', 'asSorting' );\n\t\t_fnCompatMap( init, 'orderDataType', 'sortDataType' );\n\t\n\t\t// orderData can be given as an integer\n\t\tvar dataSort = init.aDataSort;\n\t\tif ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) {\n\t\t\tinit.aDataSort = [ dataSort ];\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Browser feature detection for capabilities, quirks\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnBrowserDetect( settings )\n\t{\n\t\t// We don't need to do this every time DataTables is constructed, the values\n\t\t// calculated are specific to the browser and OS configuration which we\n\t\t// don't expect to change between initialisations\n\t\tif ( ! DataTable.__browser ) {\n\t\t\tvar browser = {};\n\t\t\tDataTable.__browser = browser;\n\t\n\t\t\t// Scrolling feature / quirks detection\n\t\t\tvar n = $('<div/>')\n\t\t\t\t.css( {\n\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\ttop: 0,\n\t\t\t\t\tleft: $(window).scrollLeft()*-1, // allow for scrolling\n\t\t\t\t\theight: 1,\n\t\t\t\t\twidth: 1,\n\t\t\t\t\toverflow: 'hidden'\n\t\t\t\t} )\n\t\t\t\t.append(\n\t\t\t\t\t$('<div/>')\n\t\t\t\t\t\t.css( {\n\t\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\t\ttop: 1,\n\t\t\t\t\t\t\tleft: 1,\n\t\t\t\t\t\t\twidth: 100,\n\t\t\t\t\t\t\toverflow: 'scroll'\n\t\t\t\t\t\t} )\n\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t$('<div/>')\n\t\t\t\t\t\t\t\t.css( {\n\t\t\t\t\t\t\t\t\twidth: '100%',\n\t\t\t\t\t\t\t\t\theight: 10\n\t\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t.appendTo( 'body' );\n\t\n\t\t\tvar outer = n.children();\n\t\t\tvar inner = outer.children();\n\t\n\t\t\t// Numbers below, in order, are:\n\t\t\t// inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth\n\t\t\t//\n\t\t\t// IE6 XP:                           100 100 100  83\n\t\t\t// IE7 Vista:                        100 100 100  83\n\t\t\t// IE 8+ Windows:                     83  83 100  83\n\t\t\t// Evergreen Windows:                 83  83 100  83\n\t\t\t// Evergreen Mac with scrollbars:     85  85 100  85\n\t\t\t// Evergreen Mac without scrollbars: 100 100 100 100\n\t\n\t\t\t// Get scrollbar width\n\t\t\tbrowser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;\n\t\n\t\t\t// IE6/7 will oversize a width 100% element inside a scrolling element, to\n\t\t\t// include the width of the scrollbar, while other browsers ensure the inner\n\t\t\t// element is contained without forcing scrolling\n\t\t\tbrowser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;\n\t\n\t\t\t// In rtl text layout, some browsers (most, but not all) will place the\n\t\t\t// scrollbar on the left, rather than the right.\n\t\t\tbrowser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;\n\t\n\t\t\t// IE8- don't provide height and width for getBoundingClientRect\n\t\t\tbrowser.bBounding = n[0].getBoundingClientRect().width ? true : false;\n\t\n\t\t\tn.remove();\n\t\t}\n\t\n\t\t$.extend( settings.oBrowser, DataTable.__browser );\n\t\tsettings.oScroll.iBarWidth = DataTable.__browser.barWidth;\n\t}\n\t\n\t\n\t/**\n\t * Array.prototype reduce[Right] method, used for browsers which don't support\n\t * JS 1.6. Done this way to reduce code size, since we iterate either way\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnReduce ( that, fn, init, start, end, inc )\n\t{\n\t\tvar\n\t\t\ti = start,\n\t\t\tvalue,\n\t\t\tisSet = false;\n\t\n\t\tif ( init !== undefined ) {\n\t\t\tvalue = init;\n\t\t\tisSet = true;\n\t\t}\n\t\n\t\twhile ( i !== end ) {\n\t\t\tif ( ! that.hasOwnProperty(i) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\n\t\t\tvalue = isSet ?\n\t\t\t\tfn( value, that[i], i, that ) :\n\t\t\t\tthat[i];\n\t\n\t\t\tisSet = true;\n\t\t\ti += inc;\n\t\t}\n\t\n\t\treturn value;\n\t}\n\t\n\t/**\n\t * Add a column to the list used for the table with default values\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {node} nTh The th element for this column\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddColumn( oSettings, nTh )\n\t{\n\t\t// Add column to aoColumns array\n\t\tvar oDefaults = DataTable.defaults.column;\n\t\tvar iCol = oSettings.aoColumns.length;\n\t\tvar oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {\n\t\t\t\"nTh\": nTh ? nTh : document.createElement('th'),\n\t\t\t\"sTitle\":    oDefaults.sTitle    ? oDefaults.sTitle    : nTh ? nTh.innerHTML : '',\n\t\t\t\"aDataSort\": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],\n\t\t\t\"mData\": oDefaults.mData ? oDefaults.mData : iCol,\n\t\t\tidx: iCol\n\t\t} );\n\t\toSettings.aoColumns.push( oCol );\n\t\n\t\t// Add search object for column specific search. Note that the `searchCols[ iCol ]`\n\t\t// passed into extend can be undefined. This allows the user to give a default\n\t\t// with only some of the parameters defined, and also not give a default\n\t\tvar searchCols = oSettings.aoPreSearchCols;\n\t\tsearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );\n\t\n\t\t// Use the default column options function to initialise classes etc\n\t\t_fnColumnOptions( oSettings, iCol, $(nTh).data() );\n\t}\n\t\n\t\n\t/**\n\t * Apply options for a column\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iCol column index to consider\n\t *  @param {object} oOptions object with sType, bVisible and bSearchable etc\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnColumnOptions( oSettings, iCol, oOptions )\n\t{\n\t\tvar oCol = oSettings.aoColumns[ iCol ];\n\t\tvar oClasses = oSettings.oClasses;\n\t\tvar th = $(oCol.nTh);\n\t\n\t\t// Try to get width information from the DOM. We can't get it from CSS\n\t\t// as we'd need to parse the CSS stylesheet. `width` option can override\n\t\tif ( ! oCol.sWidthOrig ) {\n\t\t\t// Width attribute\n\t\t\toCol.sWidthOrig = th.attr('width') || null;\n\t\n\t\t\t// Style attribute\n\t\t\tvar t = (th.attr('style') || '').match(/width:\\s*(\\d+[pxem%]+)/);\n\t\t\tif ( t ) {\n\t\t\t\toCol.sWidthOrig = t[1];\n\t\t\t}\n\t\t}\n\t\n\t\t/* User specified column options */\n\t\tif ( oOptions !== undefined && oOptions !== null )\n\t\t{\n\t\t\t// Backwards compatibility\n\t\t\t_fnCompatCols( oOptions );\n\t\n\t\t\t// Map camel case parameters to their Hungarian counterparts\n\t\t\t_fnCamelToHungarian( DataTable.defaults.column, oOptions, true );\n\t\n\t\t\t/* Backwards compatibility for mDataProp */\n\t\t\tif ( oOptions.mDataProp !== undefined && !oOptions.mData )\n\t\t\t{\n\t\t\t\toOptions.mData = oOptions.mDataProp;\n\t\t\t}\n\t\n\t\t\tif ( oOptions.sType )\n\t\t\t{\n\t\t\t\toCol._sManualType = oOptions.sType;\n\t\t\t}\n\t\n\t\t\t// `class` is a reserved word in Javascript, so we need to provide\n\t\t\t// the ability to use a valid name for the camel case input\n\t\t\tif ( oOptions.className && ! oOptions.sClass )\n\t\t\t{\n\t\t\t\toOptions.sClass = oOptions.className;\n\t\t\t}\n\t\t\tif ( oOptions.sClass ) {\n\t\t\t\tth.addClass( oOptions.sClass );\n\t\t\t}\n\t\n\t\t\tvar origClass = oCol.sClass;\n\t\n\t\t\t$.extend( oCol, oOptions );\n\t\t\t_fnMap( oCol, oOptions, \"sWidth\", \"sWidthOrig\" );\n\t\n\t\t\t// Merge class from previously defined classes with this one, rather than just\n\t\t\t// overwriting it in the extend above\n\t\t\tif (origClass !== oCol.sClass) {\n\t\t\t\toCol.sClass = origClass + ' ' + oCol.sClass;\n\t\t\t}\n\t\n\t\t\t/* iDataSort to be applied (backwards compatibility), but aDataSort will take\n\t\t\t * priority if defined\n\t\t\t */\n\t\t\tif ( oOptions.iDataSort !== undefined )\n\t\t\t{\n\t\t\t\toCol.aDataSort = [ oOptions.iDataSort ];\n\t\t\t}\n\t\t\t_fnMap( oCol, oOptions, \"aDataSort\" );\n\t\t}\n\t\n\t\t/* Cache the data get and set functions for speed */\n\t\tvar mDataSrc = oCol.mData;\n\t\tvar mData = _fnGetObjectDataFn( mDataSrc );\n\t\tvar mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;\n\t\n\t\tvar attrTest = function( src ) {\n\t\t\treturn typeof src === 'string' && src.indexOf('@') !== -1;\n\t\t};\n\t\toCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (\n\t\t\tattrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)\n\t\t);\n\t\toCol._setter = null;\n\t\n\t\toCol.fnGetData = function (rowData, type, meta) {\n\t\t\tvar innerData = mData( rowData, type, undefined, meta );\n\t\n\t\t\treturn mRender && type ?\n\t\t\t\tmRender( innerData, type, rowData, meta ) :\n\t\t\t\tinnerData;\n\t\t};\n\t\toCol.fnSetData = function ( rowData, val, meta ) {\n\t\t\treturn _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );\n\t\t};\n\t\n\t\t// Indicate if DataTables should read DOM data as an object or array\n\t\t// Used in _fnGetRowElements\n\t\tif ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) {\n\t\t\toSettings._rowReadObject = true;\n\t\t}\n\t\n\t\t/* Feature sorting overrides column specific when off */\n\t\tif ( !oSettings.oFeatures.bSort )\n\t\t{\n\t\t\toCol.bSortable = false;\n\t\t\tth.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called\n\t\t}\n\t\n\t\t/* Check that the class assignment is correct for sorting */\n\t\tvar bAsc = $.inArray('asc', oCol.asSorting) !== -1;\n\t\tvar bDesc = $.inArray('desc', oCol.asSorting) !== -1;\n\t\tif ( !oCol.bSortable || (!bAsc && !bDesc) )\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortableNone;\n\t\t\toCol.sSortingClassJUI = \"\";\n\t\t}\n\t\telse if ( bAsc && !bDesc )\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortableAsc;\n\t\t\toCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;\n\t\t}\n\t\telse if ( !bAsc && bDesc )\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortableDesc;\n\t\t\toCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;\n\t\t}\n\t\telse\n\t\t{\n\t\t\toCol.sSortingClass = oClasses.sSortable;\n\t\t\toCol.sSortingClassJUI = oClasses.sSortJUI;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Adjust the table column widths for new data. Note: you would probably want to\n\t * do a redraw after calling this function!\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAdjustColumnSizing ( settings )\n\t{\n\t\t/* Not interested in doing column width calculation if auto-width is disabled */\n\t\tif ( settings.oFeatures.bAutoWidth !== false )\n\t\t{\n\t\t\tvar columns = settings.aoColumns;\n\t\n\t\t\t_fnCalculateColumnWidths( settings );\n\t\t\tfor ( var i=0 , iLen=columns.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\tcolumns[i].nTh.style.width = columns[i].sWidth;\n\t\t\t}\n\t\t}\n\t\n\t\tvar scroll = settings.oScroll;\n\t\tif ( scroll.sY !== '' || scroll.sX !== '')\n\t\t{\n\t\t\t_fnScrollDraw( settings );\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'column-sizing', [settings] );\n\t}\n\t\n\t\n\t/**\n\t * Convert the index of a visible column to the index in the data array (take account\n\t * of hidden columns)\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iMatch Visible column index to lookup\n\t *  @returns {int} i the data index\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnVisibleToColumnIndex( oSettings, iMatch )\n\t{\n\t\tvar aiVis = _fnGetColumns( oSettings, 'bVisible' );\n\t\n\t\treturn typeof aiVis[iMatch] === 'number' ?\n\t\t\taiVis[iMatch] :\n\t\t\tnull;\n\t}\n\t\n\t\n\t/**\n\t * Convert the index of an index in the data array and convert it to the visible\n\t *   column index (take account of hidden columns)\n\t *  @param {int} iMatch Column index to lookup\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {int} i the data index\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnColumnIndexToVisible( oSettings, iMatch )\n\t{\n\t\tvar aiVis = _fnGetColumns( oSettings, 'bVisible' );\n\t\tvar iPos = $.inArray( iMatch, aiVis );\n\t\n\t\treturn iPos !== -1 ? iPos : null;\n\t}\n\t\n\t\n\t/**\n\t * Get the number of visible columns\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {int} i the number of visible columns\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnVisbleColumns( oSettings )\n\t{\n\t\tvar vis = 0;\n\t\n\t\t// No reduce in IE8, use a loop for now\n\t\t$.each( oSettings.aoColumns, function ( i, col ) {\n\t\t\tif ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {\n\t\t\t\tvis++;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn vis;\n\t}\n\t\n\t\n\t/**\n\t * Get an array of column indexes that match a given property\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {string} sParam Parameter in aoColumns to look for - typically\n\t *    bVisible or bSearchable\n\t *  @returns {array} Array of indexes with matched properties\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetColumns( oSettings, sParam )\n\t{\n\t\tvar a = [];\n\t\n\t\t$.map( oSettings.aoColumns, function(val, i) {\n\t\t\tif ( val[sParam] ) {\n\t\t\t\ta.push( i );\n\t\t\t}\n\t\t} );\n\t\n\t\treturn a;\n\t}\n\t\n\t\n\t/**\n\t * Calculate the 'type' of a column\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnColumnTypes ( settings )\n\t{\n\t\tvar columns = settings.aoColumns;\n\t\tvar data = settings.aoData;\n\t\tvar types = DataTable.ext.type.detect;\n\t\tvar i, ien, j, jen, k, ken;\n\t\tvar col, cell, detectedType, cache;\n\t\n\t\t// For each column, spin over the \n\t\tfor ( i=0, ien=columns.length ; i<ien ; i++ ) {\n\t\t\tcol = columns[i];\n\t\t\tcache = [];\n\t\n\t\t\tif ( ! col.sType && col._sManualType ) {\n\t\t\t\tcol.sType = col._sManualType;\n\t\t\t}\n\t\t\telse if ( ! col.sType ) {\n\t\t\t\tfor ( j=0, jen=types.length ; j<jen ; j++ ) {\n\t\t\t\t\tfor ( k=0, ken=data.length ; k<ken ; k++ ) {\n\t\t\t\t\t\t// Use a cache array so we only need to get the type data\n\t\t\t\t\t\t// from the formatter once (when using multiple detectors)\n\t\t\t\t\t\tif ( cache[k] === undefined ) {\n\t\t\t\t\t\t\tcache[k] = _fnGetCellData( settings, k, i, 'type' );\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tdetectedType = types[j]( cache[k], settings );\n\t\n\t\t\t\t\t\t// If null, then this type can't apply to this column, so\n\t\t\t\t\t\t// rather than testing all cells, break out. There is an\n\t\t\t\t\t\t// exception for the last type which is `html`. We need to\n\t\t\t\t\t\t// scan all rows since it is possible to mix string and HTML\n\t\t\t\t\t\t// types\n\t\t\t\t\t\tif ( ! detectedType && j !== types.length-1 ) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t// Only a single match is needed for html type since it is\n\t\t\t\t\t\t// bottom of the pile and very similar to string - but it\n\t\t\t\t\t\t// must not be empty\n\t\t\t\t\t\tif ( detectedType === 'html' && ! _empty(cache[k]) ) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\t// Type is valid for all data points in the column - use this\n\t\t\t\t\t// type\n\t\t\t\t\tif ( detectedType ) {\n\t\t\t\t\t\tcol.sType = detectedType;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Fall back - if no type was detected, always use string\n\t\t\t\tif ( ! col.sType ) {\n\t\t\t\t\tcol.sType = 'string';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Take the column definitions and static columns arrays and calculate how\n\t * they relate to column indexes. The callback function will then apply the\n\t * definition found for a column to a suitable configuration object.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {array} aoColDefs The aoColumnDefs array that is to be applied\n\t *  @param {array} aoCols The aoColumns array that defines columns individually\n\t *  @param {function} fn Callback function - takes two parameters, the calculated\n\t *    column index and the definition for that column.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )\n\t{\n\t\tvar i, iLen, j, jLen, k, kLen, def;\n\t\tvar columns = oSettings.aoColumns;\n\t\n\t\t// Column definitions with aTargets\n\t\tif ( aoColDefs )\n\t\t{\n\t\t\t/* Loop over the definitions array - loop in reverse so first instance has priority */\n\t\t\tfor ( i=aoColDefs.length-1 ; i>=0 ; i-- )\n\t\t\t{\n\t\t\t\tdef = aoColDefs[i];\n\t\n\t\t\t\t/* Each definition can target multiple columns, as it is an array */\n\t\t\t\tvar aTargets = def.target !== undefined\n\t\t\t\t\t? def.target\n\t\t\t\t\t: def.targets !== undefined\n\t\t\t\t\t\t? def.targets\n\t\t\t\t\t\t: def.aTargets;\n\t\n\t\t\t\tif ( ! Array.isArray( aTargets ) )\n\t\t\t\t{\n\t\t\t\t\taTargets = [ aTargets ];\n\t\t\t\t}\n\t\n\t\t\t\tfor ( j=0, jLen=aTargets.length ; j<jLen ; j++ )\n\t\t\t\t{\n\t\t\t\t\tif ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Add columns that we don't yet know about */\n\t\t\t\t\t\twhile( columns.length <= aTargets[j] )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_fnAddColumn( oSettings );\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t/* Integer, basic index */\n\t\t\t\t\t\tfn( aTargets[j], def );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Negative integer, right to left column counting */\n\t\t\t\t\t\tfn( columns.length+aTargets[j], def );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( typeof aTargets[j] === 'string' )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Class name matching on TH element */\n\t\t\t\t\t\tfor ( k=0, kLen=columns.length ; k<kLen ; k++ )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ( aTargets[j] == \"_all\" ||\n\t\t\t\t\t\t\t     $(columns[k].nTh).hasClass( aTargets[j] ) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfn( k, def );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\t// Statically defined columns array\n\t\tif ( aoCols )\n\t\t{\n\t\t\tfor ( i=0, iLen=aoCols.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\tfn( i, aoCols[i] );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * Add a data array to the table, creating DOM node etc. This is the parallel to\n\t * _fnGatherData, but for adding rows from a Javascript source, rather than a\n\t * DOM source.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {array} aData data array to be added\n\t *  @param {node} [nTr] TR element to add to the table - optional. If not given,\n\t *    DataTables will create a row automatically\n\t *  @param {array} [anTds] Array of TD|TH elements for the row - must be given\n\t *    if nTr is.\n\t *  @returns {int} >=0 if successful (index of new aoData entry), -1 if failed\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddData ( oSettings, aDataIn, nTr, anTds )\n\t{\n\t\t/* Create the object for storing information about this new row */\n\t\tvar iRow = oSettings.aoData.length;\n\t\tvar oData = $.extend( true, {}, DataTable.models.oRow, {\n\t\t\tsrc: nTr ? 'dom' : 'data',\n\t\t\tidx: iRow\n\t\t} );\n\t\n\t\toData._aData = aDataIn;\n\t\toSettings.aoData.push( oData );\n\t\n\t\t/* Create the cells */\n\t\tvar nTd, sThisType;\n\t\tvar columns = oSettings.aoColumns;\n\t\n\t\t// Invalidate the column types as the new data needs to be revalidated\n\t\tfor ( var i=0, iLen=columns.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tcolumns[i].sType = null;\n\t\t}\n\t\n\t\t/* Add to the display array */\n\t\toSettings.aiDisplayMaster.push( iRow );\n\t\n\t\tvar id = oSettings.rowIdFn( aDataIn );\n\t\tif ( id !== undefined ) {\n\t\t\toSettings.aIds[ id ] = oData;\n\t\t}\n\t\n\t\t/* Create the DOM information, or register it if already present */\n\t\tif ( nTr || ! oSettings.oFeatures.bDeferRender )\n\t\t{\n\t\t\t_fnCreateTr( oSettings, iRow, nTr, anTds );\n\t\t}\n\t\n\t\treturn iRow;\n\t}\n\t\n\t\n\t/**\n\t * Add one or more TR elements to the table. Generally we'd expect to\n\t * use this for reading data from a DOM sourced table, but it could be\n\t * used for an TR element. Note that if a TR is given, it is used (i.e.\n\t * it is not cloned).\n\t *  @param {object} settings dataTables settings object\n\t *  @param {array|node|jQuery} trs The TR element(s) to add to the table\n\t *  @returns {array} Array of indexes for the added rows\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddTr( settings, trs )\n\t{\n\t\tvar row;\n\t\n\t\t// Allow an individual node to be passed in\n\t\tif ( ! (trs instanceof $) ) {\n\t\t\ttrs = $(trs);\n\t\t}\n\t\n\t\treturn trs.map( function (i, el) {\n\t\t\trow = _fnGetRowElements( settings, el );\n\t\t\treturn _fnAddData( settings, row.data, el, row.cells );\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Take a TR element and convert it to an index in aoData\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {node} n the TR element to find\n\t *  @returns {int} index if the node is found, null if not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnNodeToDataIndex( oSettings, n )\n\t{\n\t\treturn (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;\n\t}\n\t\n\t\n\t/**\n\t * Take a TD element and convert it into a column data index (not the visible index)\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iRow The row number the TD/TH can be found in\n\t *  @param {node} n The TD/TH element to find\n\t *  @returns {int} index if the node is found, -1 if not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnNodeToColumnIndex( oSettings, iRow, n )\n\t{\n\t\treturn $.inArray( n, oSettings.aoData[ iRow ].anCells );\n\t}\n\t\n\t\n\t/**\n\t * Get the data for a given cell from the internal cache, taking into account data mapping\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} rowIdx aoData row id\n\t *  @param {int} colIdx Column index\n\t *  @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order')\n\t *  @returns {*} Cell data\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetCellData( settings, rowIdx, colIdx, type )\n\t{\n\t\tif (type === 'search') {\n\t\t\ttype = 'filter';\n\t\t}\n\t\telse if (type === 'order') {\n\t\t\ttype = 'sort';\n\t\t}\n\t\n\t\tvar draw           = settings.iDraw;\n\t\tvar col            = settings.aoColumns[colIdx];\n\t\tvar rowData        = settings.aoData[rowIdx]._aData;\n\t\tvar defaultContent = col.sDefaultContent;\n\t\tvar cellData       = col.fnGetData( rowData, type, {\n\t\t\tsettings: settings,\n\t\t\trow:      rowIdx,\n\t\t\tcol:      colIdx\n\t\t} );\n\t\n\t\tif ( cellData === undefined ) {\n\t\t\tif ( settings.iDrawError != draw && defaultContent === null ) {\n\t\t\t\t_fnLog( settings, 0, \"Requested unknown parameter \"+\n\t\t\t\t\t(typeof col.mData=='function' ? '{function}' : \"'\"+col.mData+\"'\")+\n\t\t\t\t\t\" for row \"+rowIdx+\", column \"+colIdx, 4 );\n\t\t\t\tsettings.iDrawError = draw;\n\t\t\t}\n\t\t\treturn defaultContent;\n\t\t}\n\t\n\t\t// When the data source is null and a specific data type is requested (i.e.\n\t\t// not the original data), we can use default column data\n\t\tif ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {\n\t\t\tcellData = defaultContent;\n\t\t}\n\t\telse if ( typeof cellData === 'function' ) {\n\t\t\t// If the data source is a function, then we run it and use the return,\n\t\t\t// executing in the scope of the data object (for instances)\n\t\t\treturn cellData.call( rowData );\n\t\t}\n\t\n\t\tif ( cellData === null && type === 'display' ) {\n\t\t\treturn '';\n\t\t}\n\t\n\t\tif ( type === 'filter' ) {\n\t\t\tvar fomatters = DataTable.ext.type.search;\n\t\n\t\t\tif ( fomatters[ col.sType ] ) {\n\t\t\t\tcellData = fomatters[ col.sType ]( cellData );\n\t\t\t}\n\t\t}\n\t\n\t\treturn cellData;\n\t}\n\t\n\t\n\t/**\n\t * Set the value for a specific cell, into the internal data cache\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} rowIdx aoData row id\n\t *  @param {int} colIdx Column index\n\t *  @param {*} val Value to set\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSetCellData( settings, rowIdx, colIdx, val )\n\t{\n\t\tvar col     = settings.aoColumns[colIdx];\n\t\tvar rowData = settings.aoData[rowIdx]._aData;\n\t\n\t\tcol.fnSetData( rowData, val, {\n\t\t\tsettings: settings,\n\t\t\trow:      rowIdx,\n\t\t\tcol:      colIdx\n\t\t}  );\n\t}\n\t\n\t\n\t// Private variable that is used to match action syntax in the data property object\n\tvar __reArray = /\\[.*?\\]$/;\n\tvar __reFn = /\\(\\)$/;\n\t\n\t/**\n\t * Split string on periods, taking into account escaped periods\n\t * @param  {string} str String to split\n\t * @return {array} Split string\n\t */\n\tfunction _fnSplitObjNotation( str )\n\t{\n\t\treturn $.map( str.match(/(\\\\.|[^\\.])+/g) || [''], function ( s ) {\n\t\t\treturn s.replace(/\\\\\\./g, '.');\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Return a function that can be used to get data from a source object, taking\n\t * into account the ability to use nested objects as a source\n\t *  @param {string|int|function} mSource The data source for the object\n\t *  @returns {function} Data get function\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnGetObjectDataFn = DataTable.util.get;\n\t\n\t\n\t/**\n\t * Return a function that can be used to set data from a source object, taking\n\t * into account the ability to use nested objects as a source\n\t *  @param {string|int|function} mSource The data source for the object\n\t *  @returns {function} Data set function\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnSetObjectDataFn = DataTable.util.set;\n\t\n\t\n\t/**\n\t * Return an array with the full table data\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns array {array} aData Master data array\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetDataMaster ( settings )\n\t{\n\t\treturn _pluck( settings.aoData, '_aData' );\n\t}\n\t\n\t\n\t/**\n\t * Nuke the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnClearTable( settings )\n\t{\n\t\tsettings.aoData.length = 0;\n\t\tsettings.aiDisplayMaster.length = 0;\n\t\tsettings.aiDisplay.length = 0;\n\t\tsettings.aIds = {};\n\t}\n\t\n\t\n\t /**\n\t * Take an array of integers (index array) and remove a target integer (value - not\n\t * the key!)\n\t *  @param {array} a Index array to target\n\t *  @param {int} iTarget value to find\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDeleteIndex( a, iTarget, splice )\n\t{\n\t\tvar iTargetIndex = -1;\n\t\n\t\tfor ( var i=0, iLen=a.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tif ( a[i] == iTarget )\n\t\t\t{\n\t\t\t\tiTargetIndex = i;\n\t\t\t}\n\t\t\telse if ( a[i] > iTarget )\n\t\t\t{\n\t\t\t\ta[i]--;\n\t\t\t}\n\t\t}\n\t\n\t\tif ( iTargetIndex != -1 && splice === undefined )\n\t\t{\n\t\t\ta.splice( iTargetIndex, 1 );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Mark cached data as invalid such that a re-read of the data will occur when\n\t * the cached data is next requested. Also update from the data source object.\n\t *\n\t * @param {object} settings DataTables settings object\n\t * @param {int}    rowIdx   Row index to invalidate\n\t * @param {string} [src]    Source to invalidate from: undefined, 'auto', 'dom'\n\t *     or 'data'\n\t * @param {int}    [colIdx] Column index to invalidate. If undefined the whole\n\t *     row will be invalidated\n\t * @memberof DataTable#oApi\n\t *\n\t * @todo For the modularisation of v1.11 this will need to become a callback, so\n\t *   the sort and filter methods can subscribe to it. That will required\n\t *   initialisation options for sorting, which is why it is not already baked in\n\t */\n\tfunction _fnInvalidate( settings, rowIdx, src, colIdx )\n\t{\n\t\tvar row = settings.aoData[ rowIdx ];\n\t\tvar i, ien;\n\t\tvar cellWrite = function ( cell, col ) {\n\t\t\t// This is very frustrating, but in IE if you just write directly\n\t\t\t// to innerHTML, and elements that are overwritten are GC'ed,\n\t\t\t// even if there is a reference to them elsewhere\n\t\t\twhile ( cell.childNodes.length ) {\n\t\t\t\tcell.removeChild( cell.firstChild );\n\t\t\t}\n\t\n\t\t\tcell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );\n\t\t};\n\t\n\t\t// Are we reading last data from DOM or the data object?\n\t\tif ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {\n\t\t\t// Read the data from the DOM\n\t\t\trow._aData = _fnGetRowElements(\n\t\t\t\t\tsettings, row, colIdx, colIdx === undefined ? undefined : row._aData\n\t\t\t\t)\n\t\t\t\t.data;\n\t\t}\n\t\telse {\n\t\t\t// Reading from data object, update the DOM\n\t\t\tvar cells = row.anCells;\n\t\n\t\t\tif ( cells ) {\n\t\t\t\tif ( colIdx !== undefined ) {\n\t\t\t\t\tcellWrite( cells[colIdx], colIdx );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tfor ( i=0, ien=cells.length ; i<ien ; i++ ) {\n\t\t\t\t\t\tcellWrite( cells[i], i );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\t// For both row and cell invalidation, the cached data for sorting and\n\t\t// filtering is nulled out\n\t\trow._aSortData = null;\n\t\trow._aFilterData = null;\n\t\n\t\t// Invalidate the type for a specific column (if given) or all columns since\n\t\t// the data might have changed\n\t\tvar cols = settings.aoColumns;\n\t\tif ( colIdx !== undefined ) {\n\t\t\tcols[ colIdx ].sType = null;\n\t\t}\n\t\telse {\n\t\t\tfor ( i=0, ien=cols.length ; i<ien ; i++ ) {\n\t\t\t\tcols[i].sType = null;\n\t\t\t}\n\t\n\t\t\t// Update DataTables special `DT_*` attributes for the row\n\t\t\t_fnRowAttributes( settings, row );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Build a data source object from an HTML row, reading the contents of the\n\t * cells that are in the row.\n\t *\n\t * @param {object} settings DataTables settings object\n\t * @param {node|object} TR element from which to read data or existing row\n\t *   object from which to re-read the data from the cells\n\t * @param {int} [colIdx] Optional column index\n\t * @param {array|object} [d] Data source object. If `colIdx` is given then this\n\t *   parameter should also be given and will be used to write the data into.\n\t *   Only the column in question will be written\n\t * @returns {object} Object with two parameters: `data` the data read, in\n\t *   document order, and `cells` and array of nodes (they can be useful to the\n\t *   caller, so rather than needing a second traversal to get them, just return\n\t *   them from here).\n\t * @memberof DataTable#oApi\n\t */\n\tfunction _fnGetRowElements( settings, row, colIdx, d )\n\t{\n\t\tvar\n\t\t\ttds = [],\n\t\t\ttd = row.firstChild,\n\t\t\tname, col, o, i=0, contents,\n\t\t\tcolumns = settings.aoColumns,\n\t\t\tobjectRead = settings._rowReadObject;\n\t\n\t\t// Allow the data object to be passed in, or construct\n\t\td = d !== undefined ?\n\t\t\td :\n\t\t\tobjectRead ?\n\t\t\t\t{} :\n\t\t\t\t[];\n\t\n\t\tvar attr = function ( str, td  ) {\n\t\t\tif ( typeof str === 'string' ) {\n\t\t\t\tvar idx = str.indexOf('@');\n\t\n\t\t\t\tif ( idx !== -1 ) {\n\t\t\t\t\tvar attr = str.substring( idx+1 );\n\t\t\t\t\tvar setter = _fnSetObjectDataFn( str );\n\t\t\t\t\tsetter( d, td.getAttribute( attr ) );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\n\t\t// Read data from a cell and store into the data object\n\t\tvar cellProcess = function ( cell ) {\n\t\t\tif ( colIdx === undefined || colIdx === i ) {\n\t\t\t\tcol = columns[i];\n\t\t\t\tcontents = (cell.innerHTML).trim();\n\t\n\t\t\t\tif ( col && col._bAttrSrc ) {\n\t\t\t\t\tvar setter = _fnSetObjectDataFn( col.mData._ );\n\t\t\t\t\tsetter( d, contents );\n\t\n\t\t\t\t\tattr( col.mData.sort, cell );\n\t\t\t\t\tattr( col.mData.type, cell );\n\t\t\t\t\tattr( col.mData.filter, cell );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Depending on the `data` option for the columns the data can\n\t\t\t\t\t// be read to either an object or an array.\n\t\t\t\t\tif ( objectRead ) {\n\t\t\t\t\t\tif ( ! col._setter ) {\n\t\t\t\t\t\t\t// Cache the setter function\n\t\t\t\t\t\t\tcol._setter = _fnSetObjectDataFn( col.mData );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcol._setter( d, contents );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\td[i] = contents;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\ti++;\n\t\t};\n\t\n\t\tif ( td ) {\n\t\t\t// `tr` element was passed in\n\t\t\twhile ( td ) {\n\t\t\t\tname = td.nodeName.toUpperCase();\n\t\n\t\t\t\tif ( name == \"TD\" || name == \"TH\" ) {\n\t\t\t\t\tcellProcess( td );\n\t\t\t\t\ttds.push( td );\n\t\t\t\t}\n\t\n\t\t\t\ttd = td.nextSibling;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Existing row object passed in\n\t\t\ttds = row.anCells;\n\t\n\t\t\tfor ( var j=0, jen=tds.length ; j<jen ; j++ ) {\n\t\t\t\tcellProcess( tds[j] );\n\t\t\t}\n\t\t}\n\t\n\t\t// Read the ID from the DOM if present\n\t\tvar rowNode = row.firstChild ? row : row.nTr;\n\t\n\t\tif ( rowNode ) {\n\t\t\tvar id = rowNode.getAttribute( 'id' );\n\t\n\t\t\tif ( id ) {\n\t\t\t\t_fnSetObjectDataFn( settings.rowId )( d, id );\n\t\t\t}\n\t\t}\n\t\n\t\treturn {\n\t\t\tdata: d,\n\t\t\tcells: tds\n\t\t};\n\t}\n\t/**\n\t * Create a new TR element (and it's TD children) for a row\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {int} iRow Row to consider\n\t *  @param {node} [nTrIn] TR element to add to the table - optional. If not given,\n\t *    DataTables will create a row automatically\n\t *  @param {array} [anTds] Array of TD|TH elements for the row - must be given\n\t *    if nTr is.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCreateTr ( oSettings, iRow, nTrIn, anTds )\n\t{\n\t\tvar\n\t\t\trow = oSettings.aoData[iRow],\n\t\t\trowData = row._aData,\n\t\t\tcells = [],\n\t\t\tnTr, nTd, oCol,\n\t\t\ti, iLen, create;\n\t\n\t\tif ( row.nTr === null )\n\t\t{\n\t\t\tnTr = nTrIn || document.createElement('tr');\n\t\n\t\t\trow.nTr = nTr;\n\t\t\trow.anCells = cells;\n\t\n\t\t\t/* Use a private property on the node to allow reserve mapping from the node\n\t\t\t * to the aoData array for fast look up\n\t\t\t */\n\t\t\tnTr._DT_RowIndex = iRow;\n\t\n\t\t\t/* Special parameters can be given by the data source to be used on the row */\n\t\t\t_fnRowAttributes( oSettings, row );\n\t\n\t\t\t/* Process each column */\n\t\t\tfor ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )\n\t\t\t{\n\t\t\t\toCol = oSettings.aoColumns[i];\n\t\t\t\tcreate = nTrIn ? false : true;\n\t\n\t\t\t\tnTd = create ? document.createElement( oCol.sCellType ) : anTds[i];\n\t\n\t\t\t\tif (! nTd) {\n\t\t\t\t\t_fnLog( oSettings, 0, 'Incorrect column count', 18 );\n\t\t\t\t}\n\t\n\t\t\t\tnTd._DT_CellIndex = {\n\t\t\t\t\trow: iRow,\n\t\t\t\t\tcolumn: i\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\tcells.push( nTd );\n\t\n\t\t\t\t// Need to create the HTML if new, or if a rendering function is defined\n\t\t\t\tif ( create || ((oCol.mRender || oCol.mData !== i) &&\n\t\t\t\t\t (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')\n\t\t\t\t)) {\n\t\t\t\t\tnTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );\n\t\t\t\t}\n\t\n\t\t\t\t/* Add user defined class */\n\t\t\t\tif ( oCol.sClass )\n\t\t\t\t{\n\t\t\t\t\tnTd.className += ' '+oCol.sClass;\n\t\t\t\t}\n\t\n\t\t\t\t// Visibility - add or remove as required\n\t\t\t\tif ( oCol.bVisible && ! nTrIn )\n\t\t\t\t{\n\t\t\t\t\tnTr.appendChild( nTd );\n\t\t\t\t}\n\t\t\t\telse if ( ! oCol.bVisible && nTrIn )\n\t\t\t\t{\n\t\t\t\t\tnTd.parentNode.removeChild( nTd );\n\t\t\t\t}\n\t\n\t\t\t\tif ( oCol.fnCreatedCell )\n\t\t\t\t{\n\t\t\t\t\toCol.fnCreatedCell.call( oSettings.oInstance,\n\t\t\t\t\t\tnTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Add attributes to a row based on the special `DT_*` parameters in a data\n\t * source object.\n\t *  @param {object} settings DataTables settings object\n\t *  @param {object} DataTables row object for the row to be modified\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnRowAttributes( settings, row )\n\t{\n\t\tvar tr = row.nTr;\n\t\tvar data = row._aData;\n\t\n\t\tif ( tr ) {\n\t\t\tvar id = settings.rowIdFn( data );\n\t\n\t\t\tif ( id ) {\n\t\t\t\ttr.id = id;\n\t\t\t}\n\t\n\t\t\tif ( data.DT_RowClass ) {\n\t\t\t\t// Remove any classes added by DT_RowClass before\n\t\t\t\tvar a = data.DT_RowClass.split(' ');\n\t\t\t\trow.__rowc = row.__rowc ?\n\t\t\t\t\t_unique( row.__rowc.concat( a ) ) :\n\t\t\t\t\ta;\n\t\n\t\t\t\t$(tr)\n\t\t\t\t\t.removeClass( row.__rowc.join(' ') )\n\t\t\t\t\t.addClass( data.DT_RowClass );\n\t\t\t}\n\t\n\t\t\tif ( data.DT_RowAttr ) {\n\t\t\t\t$(tr).attr( data.DT_RowAttr );\n\t\t\t}\n\t\n\t\t\tif ( data.DT_RowData ) {\n\t\t\t\t$(tr).data( data.DT_RowData );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Create the HTML header for the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnBuildHead( oSettings )\n\t{\n\t\tvar i, ien, cell, row, column;\n\t\tvar thead = oSettings.nTHead;\n\t\tvar tfoot = oSettings.nTFoot;\n\t\tvar createHeader = $('th, td', thead).length === 0;\n\t\tvar classes = oSettings.oClasses;\n\t\tvar columns = oSettings.aoColumns;\n\t\n\t\tif ( createHeader ) {\n\t\t\trow = $('<tr/>').appendTo( thead );\n\t\t}\n\t\n\t\tfor ( i=0, ien=columns.length ; i<ien ; i++ ) {\n\t\t\tcolumn = columns[i];\n\t\t\tcell = $( column.nTh ).addClass( column.sClass );\n\t\n\t\t\tif ( createHeader ) {\n\t\t\t\tcell.appendTo( row );\n\t\t\t}\n\t\n\t\t\t// 1.11 move into sorting\n\t\t\tif ( oSettings.oFeatures.bSort ) {\n\t\t\t\tcell.addClass( column.sSortingClass );\n\t\n\t\t\t\tif ( column.bSortable !== false ) {\n\t\t\t\t\tcell\n\t\t\t\t\t\t.attr( 'tabindex', oSettings.iTabIndex )\n\t\t\t\t\t\t.attr( 'aria-controls', oSettings.sTableId );\n\t\n\t\t\t\t\t_fnSortAttachListener( oSettings, column.nTh, i );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tif ( column.sTitle != cell[0].innerHTML ) {\n\t\t\t\tcell.html( column.sTitle );\n\t\t\t}\n\t\n\t\t\t_fnRenderer( oSettings, 'header' )(\n\t\t\t\toSettings, cell, column, classes\n\t\t\t);\n\t\t}\n\t\n\t\tif ( createHeader ) {\n\t\t\t_fnDetectHeader( oSettings.aoHeader, thead );\n\t\t}\n\t\n\t\t/* Deal with the footer - add classes if required */\n\t\t$(thead).children('tr').children('th, td').addClass( classes.sHeaderTH );\n\t\t$(tfoot).children('tr').children('th, td').addClass( classes.sFooterTH );\n\t\n\t\t// Cache the footer cells. Note that we only take the cells from the first\n\t\t// row in the footer. If there is more than one row the user wants to\n\t\t// interact with, they need to use the table().foot() method. Note also this\n\t\t// allows cells to be used for multiple columns using colspan\n\t\tif ( tfoot !== null ) {\n\t\t\tvar cells = oSettings.aoFooter[0];\n\t\n\t\t\tfor ( i=0, ien=cells.length ; i<ien ; i++ ) {\n\t\t\t\tcolumn = columns[i];\n\t\n\t\t\t\tif (column) {\n\t\t\t\t\tcolumn.nTf = cells[i].cell;\n\t\t\n\t\t\t\t\tif ( column.sClass ) {\n\t\t\t\t\t\t$(column.nTf).addClass( column.sClass );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_fnLog( oSettings, 0, 'Incorrect column count', 18 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Draw the header (or footer) element based on the column visibility states. The\n\t * methodology here is to use the layout array from _fnDetectHeader, modified for\n\t * the instantaneous column visibility, to construct the new layout. The grid is\n\t * traversed over cell at a time in a rows x columns grid fashion, although each\n\t * cell insert can cover multiple elements in the grid - which is tracks using the\n\t * aApplied array. Cell inserts in the grid will only occur where there isn't\n\t * already a cell in that position.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param array {objects} aoSource Layout array from _fnDetectHeader\n\t *  @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDrawHead( oSettings, aoSource, bIncludeHidden )\n\t{\n\t\tvar i, iLen, j, jLen, k, kLen, n, nLocalTr;\n\t\tvar aoLocal = [];\n\t\tvar aApplied = [];\n\t\tvar iColumns = oSettings.aoColumns.length;\n\t\tvar iRowspan, iColspan;\n\t\n\t\tif ( ! aoSource )\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\n\t\tif (  bIncludeHidden === undefined )\n\t\t{\n\t\t\tbIncludeHidden = false;\n\t\t}\n\t\n\t\t/* Make a copy of the master layout array, but without the visible columns in it */\n\t\tfor ( i=0, iLen=aoSource.length ; i<iLen ; i++ )\n\t\t{\n\t\t\taoLocal[i] = aoSource[i].slice();\n\t\t\taoLocal[i].nTr = aoSource[i].nTr;\n\t\n\t\t\t/* Remove any columns which are currently hidden */\n\t\t\tfor ( j=iColumns-1 ; j>=0 ; j-- )\n\t\t\t{\n\t\t\t\tif ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )\n\t\t\t\t{\n\t\t\t\t\taoLocal[i].splice( j, 1 );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t/* Prep the applied array - it needs an element for each row */\n\t\t\taApplied.push( [] );\n\t\t}\n\t\n\t\tfor ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tnLocalTr = aoLocal[i].nTr;\n\t\n\t\t\t/* All cells are going to be replaced, so empty out the row */\n\t\t\tif ( nLocalTr )\n\t\t\t{\n\t\t\t\twhile( (n = nLocalTr.firstChild) )\n\t\t\t\t{\n\t\t\t\t\tnLocalTr.removeChild( n );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tfor ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )\n\t\t\t{\n\t\t\t\tiRowspan = 1;\n\t\t\t\tiColspan = 1;\n\t\n\t\t\t\t/* Check to see if there is already a cell (row/colspan) covering our target\n\t\t\t\t * insert point. If there is, then there is nothing to do.\n\t\t\t\t */\n\t\t\t\tif ( aApplied[i][j] === undefined )\n\t\t\t\t{\n\t\t\t\t\tnLocalTr.appendChild( aoLocal[i][j].cell );\n\t\t\t\t\taApplied[i][j] = 1;\n\t\n\t\t\t\t\t/* Expand the cell to cover as many rows as needed */\n\t\t\t\t\twhile ( aoLocal[i+iRowspan] !== undefined &&\n\t\t\t\t\t        aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )\n\t\t\t\t\t{\n\t\t\t\t\t\taApplied[i+iRowspan][j] = 1;\n\t\t\t\t\t\tiRowspan++;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* Expand the cell to cover as many columns as needed */\n\t\t\t\t\twhile ( aoLocal[i][j+iColspan] !== undefined &&\n\t\t\t\t\t        aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Must update the applied array over the rows for the columns */\n\t\t\t\t\t\tfor ( k=0 ; k<iRowspan ; k++ )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taApplied[i+k][j+iColspan] = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tiColspan++;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* Do the actual expansion in the DOM */\n\t\t\t\t\t$(aoLocal[i][j].cell)\n\t\t\t\t\t\t.attr('rowspan', iRowspan)\n\t\t\t\t\t\t.attr('colspan', iColspan);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Insert the required TR nodes into the table for display\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param ajaxComplete true after ajax call to complete rendering\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDraw( oSettings, ajaxComplete )\n\t{\n\t\t// Allow for state saving and a custom start position\n\t\t_fnStart( oSettings );\n\t\n\t\t/* Provide a pre-callback function which can be used to cancel the draw is false is returned */\n\t\tvar aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );\n\t\tif ( $.inArray( false, aPreDraw ) !== -1 )\n\t\t{\n\t\t\t_fnProcessingDisplay( oSettings, false );\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar anRows = [];\n\t\tvar iRowCount = 0;\n\t\tvar asStripeClasses = oSettings.asStripeClasses;\n\t\tvar iStripes = asStripeClasses.length;\n\t\tvar oLang = oSettings.oLanguage;\n\t\tvar bServerSide = _fnDataSource( oSettings ) == 'ssp';\n\t\tvar aiDisplay = oSettings.aiDisplay;\n\t\tvar iDisplayStart = oSettings._iDisplayStart;\n\t\tvar iDisplayEnd = oSettings.fnDisplayEnd();\n\t\n\t\toSettings.bDrawing = true;\n\t\n\t\t/* Server-side processing draw intercept */\n\t\tif ( oSettings.bDeferLoading )\n\t\t{\n\t\t\toSettings.bDeferLoading = false;\n\t\t\toSettings.iDraw++;\n\t\t\t_fnProcessingDisplay( oSettings, false );\n\t\t}\n\t\telse if ( !bServerSide )\n\t\t{\n\t\t\toSettings.iDraw++;\n\t\t}\n\t\telse if ( !oSettings.bDestroying && !ajaxComplete)\n\t\t{\n\t\t\t_fnAjaxUpdate( oSettings );\n\t\t\treturn;\n\t\t}\n\t\n\t\tif ( aiDisplay.length !== 0 )\n\t\t{\n\t\t\tvar iStart = bServerSide ? 0 : iDisplayStart;\n\t\t\tvar iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;\n\t\n\t\t\tfor ( var j=iStart ; j<iEnd ; j++ )\n\t\t\t{\n\t\t\t\tvar iDataIndex = aiDisplay[j];\n\t\t\t\tvar aoData = oSettings.aoData[ iDataIndex ];\n\t\t\t\tif ( aoData.nTr === null )\n\t\t\t\t{\n\t\t\t\t\t_fnCreateTr( oSettings, iDataIndex );\n\t\t\t\t}\n\t\n\t\t\t\tvar nRow = aoData.nTr;\n\t\n\t\t\t\t/* Remove the old striping classes and then add the new one */\n\t\t\t\tif ( iStripes !== 0 )\n\t\t\t\t{\n\t\t\t\t\tvar sStripe = asStripeClasses[ iRowCount % iStripes ];\n\t\t\t\t\tif ( aoData._sRowStripe != sStripe )\n\t\t\t\t\t{\n\t\t\t\t\t\t$(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );\n\t\t\t\t\t\taoData._sRowStripe = sStripe;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Row callback functions - might want to manipulate the row\n\t\t\t\t// iRowCount and j are not currently documented. Are they at all\n\t\t\t\t// useful?\n\t\t\t\t_fnCallbackFire( oSettings, 'aoRowCallback', null,\n\t\t\t\t\t[nRow, aoData._aData, iRowCount, j, iDataIndex] );\n\t\n\t\t\t\tanRows.push( nRow );\n\t\t\t\tiRowCount++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Table is empty - create a row with an empty message in it */\n\t\t\tvar sZero = oLang.sZeroRecords;\n\t\t\tif ( oSettings.iDraw == 1 &&  _fnDataSource( oSettings ) == 'ajax' )\n\t\t\t{\n\t\t\t\tsZero = oLang.sLoadingRecords;\n\t\t\t}\n\t\t\telse if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )\n\t\t\t{\n\t\t\t\tsZero = oLang.sEmptyTable;\n\t\t\t}\n\t\n\t\t\tanRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )\n\t\t\t\t.append( $('<td />', {\n\t\t\t\t\t'valign':  'top',\n\t\t\t\t\t'colSpan': _fnVisbleColumns( oSettings ),\n\t\t\t\t\t'class':   oSettings.oClasses.sRowEmpty\n\t\t\t\t} ).html( sZero ) )[0];\n\t\t}\n\t\n\t\t/* Header and footer callbacks */\n\t\t_fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],\n\t\t\t_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );\n\t\n\t\t_fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],\n\t\t\t_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );\n\t\n\t\tvar body = $(oSettings.nTBody);\n\t\n\t\tbody.children().detach();\n\t\tbody.append( $(anRows) );\n\t\n\t\t/* Call all required callback functions for the end of a draw */\n\t\t_fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );\n\t\n\t\t/* Draw is complete, sorting and filtering must be as well */\n\t\toSettings.bSorted = false;\n\t\toSettings.bFiltered = false;\n\t\toSettings.bDrawing = false;\n\t}\n\t\n\t\n\t/**\n\t * Redraw the table - taking account of the various features which are enabled\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {boolean} [holdPosition] Keep the current paging position. By default\n\t *    the paging is reset to the first page\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnReDraw( settings, holdPosition )\n\t{\n\t\tvar\n\t\t\tfeatures = settings.oFeatures,\n\t\t\tsort     = features.bSort,\n\t\t\tfilter   = features.bFilter;\n\t\n\t\tif ( sort ) {\n\t\t\t_fnSort( settings );\n\t\t}\n\t\n\t\tif ( filter ) {\n\t\t\t_fnFilterComplete( settings, settings.oPreviousSearch );\n\t\t}\n\t\telse {\n\t\t\t// No filtering, so we want to just use the display master\n\t\t\tsettings.aiDisplay = settings.aiDisplayMaster.slice();\n\t\t}\n\t\n\t\tif ( holdPosition !== true ) {\n\t\t\tsettings._iDisplayStart = 0;\n\t\t}\n\t\n\t\t// Let any modules know about the draw hold position state (used by\n\t\t// scrolling internally)\n\t\tsettings._drawHold = holdPosition;\n\t\n\t\t_fnDraw( settings );\n\t\n\t\tsettings._drawHold = false;\n\t}\n\t\n\t\n\t/**\n\t * Add the options to the page HTML for the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAddOptionsHtml ( oSettings )\n\t{\n\t\tvar classes = oSettings.oClasses;\n\t\tvar table = $(oSettings.nTable);\n\t\tvar holding = $('<div/>').insertBefore( table ); // Holding element for speed\n\t\tvar features = oSettings.oFeatures;\n\t\n\t\t// All DataTables are wrapped in a div\n\t\tvar insert = $('<div/>', {\n\t\t\tid:      oSettings.sTableId+'_wrapper',\n\t\t\t'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)\n\t\t} );\n\t\n\t\toSettings.nHolding = holding[0];\n\t\toSettings.nTableWrapper = insert[0];\n\t\toSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;\n\t\n\t\t/* Loop over the user set positioning and place the elements as needed */\n\t\tvar aDom = oSettings.sDom.split('');\n\t\tvar featureNode, cOption, nNewNode, cNext, sAttr, j;\n\t\tfor ( var i=0 ; i<aDom.length ; i++ )\n\t\t{\n\t\t\tfeatureNode = null;\n\t\t\tcOption = aDom[i];\n\t\n\t\t\tif ( cOption == '<' )\n\t\t\t{\n\t\t\t\t/* New container div */\n\t\t\t\tnNewNode = $('<div/>')[0];\n\t\n\t\t\t\t/* Check to see if we should append an id and/or a class name to the container */\n\t\t\t\tcNext = aDom[i+1];\n\t\t\t\tif ( cNext == \"'\" || cNext == '\"' )\n\t\t\t\t{\n\t\t\t\t\tsAttr = \"\";\n\t\t\t\t\tj = 2;\n\t\t\t\t\twhile ( aDom[i+j] != cNext )\n\t\t\t\t\t{\n\t\t\t\t\t\tsAttr += aDom[i+j];\n\t\t\t\t\t\tj++;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* Replace jQuery UI constants @todo depreciated */\n\t\t\t\t\tif ( sAttr == \"H\" )\n\t\t\t\t\t{\n\t\t\t\t\t\tsAttr = classes.sJUIHeader;\n\t\t\t\t\t}\n\t\t\t\t\telse if ( sAttr == \"F\" )\n\t\t\t\t\t{\n\t\t\t\t\t\tsAttr = classes.sJUIFooter;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t/* The attribute can be in the format of \"#id.class\", \"#id\" or \"class\" This logic\n\t\t\t\t\t * breaks the string into parts and applies them as needed\n\t\t\t\t\t */\n\t\t\t\t\tif ( sAttr.indexOf('.') != -1 )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar aSplit = sAttr.split('.');\n\t\t\t\t\t\tnNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);\n\t\t\t\t\t\tnNewNode.className = aSplit[1];\n\t\t\t\t\t}\n\t\t\t\t\telse if ( sAttr.charAt(0) == \"#\" )\n\t\t\t\t\t{\n\t\t\t\t\t\tnNewNode.id = sAttr.substr(1, sAttr.length-1);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tnNewNode.className = sAttr;\n\t\t\t\t\t}\n\t\n\t\t\t\t\ti += j; /* Move along the position array */\n\t\t\t\t}\n\t\n\t\t\t\tinsert.append( nNewNode );\n\t\t\t\tinsert = $(nNewNode);\n\t\t\t}\n\t\t\telse if ( cOption == '>' )\n\t\t\t{\n\t\t\t\t/* End container div */\n\t\t\t\tinsert = insert.parent();\n\t\t\t}\n\t\t\t// @todo Move options into their own plugins?\n\t\t\telse if ( cOption == 'l' && features.bPaginate && features.bLengthChange )\n\t\t\t{\n\t\t\t\t/* Length */\n\t\t\t\tfeatureNode = _fnFeatureHtmlLength( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 'f' && features.bFilter )\n\t\t\t{\n\t\t\t\t/* Filter */\n\t\t\t\tfeatureNode = _fnFeatureHtmlFilter( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 'r' && features.bProcessing )\n\t\t\t{\n\t\t\t\t/* pRocessing */\n\t\t\t\tfeatureNode = _fnFeatureHtmlProcessing( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 't' )\n\t\t\t{\n\t\t\t\t/* Table */\n\t\t\t\tfeatureNode = _fnFeatureHtmlTable( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption ==  'i' && features.bInfo )\n\t\t\t{\n\t\t\t\t/* Info */\n\t\t\t\tfeatureNode = _fnFeatureHtmlInfo( oSettings );\n\t\t\t}\n\t\t\telse if ( cOption == 'p' && features.bPaginate )\n\t\t\t{\n\t\t\t\t/* Pagination */\n\t\t\t\tfeatureNode = _fnFeatureHtmlPaginate( oSettings );\n\t\t\t}\n\t\t\telse if ( DataTable.ext.feature.length !== 0 )\n\t\t\t{\n\t\t\t\t/* Plug-in features */\n\t\t\t\tvar aoFeatures = DataTable.ext.feature;\n\t\t\t\tfor ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )\n\t\t\t\t{\n\t\t\t\t\tif ( cOption == aoFeatures[k].cFeature )\n\t\t\t\t\t{\n\t\t\t\t\t\tfeatureNode = aoFeatures[k].fnInit( oSettings );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t/* Add to the 2D features array */\n\t\t\tif ( featureNode )\n\t\t\t{\n\t\t\t\tvar aanFeatures = oSettings.aanFeatures;\n\t\n\t\t\t\tif ( ! aanFeatures[cOption] )\n\t\t\t\t{\n\t\t\t\t\taanFeatures[cOption] = [];\n\t\t\t\t}\n\t\n\t\t\t\taanFeatures[cOption].push( featureNode );\n\t\t\t\tinsert.append( featureNode );\n\t\t\t}\n\t\t}\n\t\n\t\t/* Built our DOM structure - replace the holding div with what we want */\n\t\tholding.replaceWith( insert );\n\t\toSettings.nHolding = null;\n\t}\n\t\n\t\n\t/**\n\t * Use the DOM source to create up an array of header cells. The idea here is to\n\t * create a layout grid (array) of rows x columns, which contains a reference\n\t * to the cell that that point in the grid (regardless of col/rowspan), such that\n\t * any column / row could be removed and the new grid constructed\n\t *  @param array {object} aLayout Array to store the calculated layout in\n\t *  @param {node} nThead The header/footer element for the table\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDetectHeader ( aLayout, nThead )\n\t{\n\t\tvar nTrs = $(nThead).children('tr');\n\t\tvar nTr, nCell;\n\t\tvar i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;\n\t\tvar bUnique;\n\t\tvar fnShiftCol = function ( a, i, j ) {\n\t\t\tvar k = a[i];\n\t                while ( k[j] ) {\n\t\t\t\tj++;\n\t\t\t}\n\t\t\treturn j;\n\t\t};\n\t\n\t\taLayout.splice( 0, aLayout.length );\n\t\n\t\t/* We know how many rows there are in the layout - so prep it */\n\t\tfor ( i=0, iLen=nTrs.length ; i<iLen ; i++ )\n\t\t{\n\t\t\taLayout.push( [] );\n\t\t}\n\t\n\t\t/* Calculate a layout array */\n\t\tfor ( i=0, iLen=nTrs.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tnTr = nTrs[i];\n\t\t\tiColumn = 0;\n\t\n\t\t\t/* For every cell in the row... */\n\t\t\tnCell = nTr.firstChild;\n\t\t\twhile ( nCell ) {\n\t\t\t\tif ( nCell.nodeName.toUpperCase() == \"TD\" ||\n\t\t\t\t     nCell.nodeName.toUpperCase() == \"TH\" )\n\t\t\t\t{\n\t\t\t\t\t/* Get the col and rowspan attributes from the DOM and sanitise them */\n\t\t\t\t\tiColspan = nCell.getAttribute('colspan') * 1;\n\t\t\t\t\tiRowspan = nCell.getAttribute('rowspan') * 1;\n\t\t\t\t\tiColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;\n\t\t\t\t\tiRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;\n\t\n\t\t\t\t\t/* There might be colspan cells already in this row, so shift our target\n\t\t\t\t\t * accordingly\n\t\t\t\t\t */\n\t\t\t\t\tiColShifted = fnShiftCol( aLayout, i, iColumn );\n\t\n\t\t\t\t\t/* Cache calculation for unique columns */\n\t\t\t\t\tbUnique = iColspan === 1 ? true : false;\n\t\n\t\t\t\t\t/* If there is col / rowspan, copy the information into the layout grid */\n\t\t\t\t\tfor ( l=0 ; l<iColspan ; l++ )\n\t\t\t\t\t{\n\t\t\t\t\t\tfor ( k=0 ; k<iRowspan ; k++ )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taLayout[i+k][iColShifted+l] = {\n\t\t\t\t\t\t\t\t\"cell\": nCell,\n\t\t\t\t\t\t\t\t\"unique\": bUnique\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\taLayout[i+k].nTr = nTr;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnCell = nCell.nextSibling;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Get an array of unique th elements, one for each column\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {node} nHeader automatically detect the layout from this node - optional\n\t *  @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional\n\t *  @returns array {node} aReturn list of unique th's\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetUniqueThs ( oSettings, nHeader, aLayout )\n\t{\n\t\tvar aReturn = [];\n\t\tif ( !aLayout )\n\t\t{\n\t\t\taLayout = oSettings.aoHeader;\n\t\t\tif ( nHeader )\n\t\t\t{\n\t\t\t\taLayout = [];\n\t\t\t\t_fnDetectHeader( aLayout, nHeader );\n\t\t\t}\n\t\t}\n\t\n\t\tfor ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tfor ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )\n\t\t\t{\n\t\t\t\tif ( aLayout[i][j].unique &&\n\t\t\t\t\t (!aReturn[j] || !oSettings.bSortCellsTop) )\n\t\t\t\t{\n\t\t\t\t\taReturn[j] = aLayout[i][j].cell;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn aReturn;\n\t}\n\t\n\t/**\n\t * Set the start position for draw\n\t *  @param {object} oSettings dataTables settings object\n\t */\n\tfunction _fnStart( oSettings )\n\t{\n\t\tvar bServerSide = _fnDataSource( oSettings ) == 'ssp';\n\t\tvar iInitDisplayStart = oSettings.iInitDisplayStart;\n\t\n\t\t// Check and see if we have an initial draw position from state saving\n\t\tif ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )\n\t\t{\n\t\t\toSettings._iDisplayStart = bServerSide ?\n\t\t\t\tiInitDisplayStart :\n\t\t\t\tiInitDisplayStart >= oSettings.fnRecordsDisplay() ?\n\t\t\t\t\t0 :\n\t\t\t\t\tiInitDisplayStart;\n\t\n\t\t\toSettings.iInitDisplayStart = -1;\n\t\t}\n\t}\n\t\n\t/**\n\t * Create an Ajax call based on the table's settings, taking into account that\n\t * parameters can have multiple forms, and backwards compatibility.\n\t *\n\t * @param {object} oSettings dataTables settings object\n\t * @param {array} data Data to send to the server, required by\n\t *     DataTables - may be augmented by developer callbacks\n\t * @param {function} fn Callback function to run when data is obtained\n\t */\n\tfunction _fnBuildAjax( oSettings, data, fn )\n\t{\n\t\t// Compatibility with 1.9-, allow fnServerData and event to manipulate\n\t\t_fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );\n\t\n\t\t// Convert to object based for 1.10+ if using the old array scheme which can\n\t\t// come from server-side processing or serverParams\n\t\tif ( data && Array.isArray(data) ) {\n\t\t\tvar tmp = {};\n\t\t\tvar rbracket = /(.*?)\\[\\]$/;\n\t\n\t\t\t$.each( data, function (key, val) {\n\t\t\t\tvar match = val.name.match(rbracket);\n\t\n\t\t\t\tif ( match ) {\n\t\t\t\t\t// Support for arrays\n\t\t\t\t\tvar name = match[0];\n\t\n\t\t\t\t\tif ( ! tmp[ name ] ) {\n\t\t\t\t\t\ttmp[ name ] = [];\n\t\t\t\t\t}\n\t\t\t\t\ttmp[ name ].push( val.value );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\ttmp[val.name] = val.value;\n\t\t\t\t}\n\t\t\t} );\n\t\t\tdata = tmp;\n\t\t}\n\t\n\t\tvar ajaxData;\n\t\tvar ajax = oSettings.ajax;\n\t\tvar instance = oSettings.oInstance;\n\t\tvar callback = function ( json ) {\n\t\t\tvar status = oSettings.jqXHR\n\t\t\t\t? oSettings.jqXHR.status\n\t\t\t\t: null;\n\t\n\t\t\tif ( json === null || (typeof status === 'number' && status == 204 ) ) {\n\t\t\t\tjson = {};\n\t\t\t\t_fnAjaxDataSrc( oSettings, json, [] );\n\t\t\t}\n\t\n\t\t\tvar error = json.error || json.sError;\n\t\t\tif ( error ) {\n\t\t\t\t_fnLog( oSettings, 0, error );\n\t\t\t}\n\t\n\t\t\toSettings.json = json;\n\t\n\t\t\t_fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );\n\t\t\tfn( json );\n\t\t};\n\t\n\t\tif ( $.isPlainObject( ajax ) && ajax.data )\n\t\t{\n\t\t\tajaxData = ajax.data;\n\t\n\t\t\tvar newData = typeof ajaxData === 'function' ?\n\t\t\t\tajaxData( data, oSettings ) :  // fn can manipulate data or return\n\t\t\t\tajaxData;                      // an object object or array to merge\n\t\n\t\t\t// If the function returned something, use that alone\n\t\t\tdata = typeof ajaxData === 'function' && newData ?\n\t\t\t\tnewData :\n\t\t\t\t$.extend( true, data, newData );\n\t\n\t\t\t// Remove the data property as we've resolved it already and don't want\n\t\t\t// jQuery to do it again (it is restored at the end of the function)\n\t\t\tdelete ajax.data;\n\t\t}\n\t\n\t\tvar baseAjax = {\n\t\t\t\"data\": data,\n\t\t\t\"success\": callback,\n\t\t\t\"dataType\": \"json\",\n\t\t\t\"cache\": false,\n\t\t\t\"type\": oSettings.sServerMethod,\n\t\t\t\"error\": function (xhr, error, thrown) {\n\t\t\t\tvar ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );\n\t\n\t\t\t\tif ( $.inArray( true, ret ) === -1 ) {\n\t\t\t\t\tif ( error == \"parsererror\" ) {\n\t\t\t\t\t\t_fnLog( oSettings, 0, 'Invalid JSON response', 1 );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( xhr.readyState === 4 ) {\n\t\t\t\t\t\t_fnLog( oSettings, 0, 'Ajax error', 7 );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t_fnProcessingDisplay( oSettings, false );\n\t\t\t}\n\t\t};\n\t\n\t\t// Store the data submitted for the API\n\t\toSettings.oAjaxData = data;\n\t\n\t\t// Allow plug-ins and external processes to modify the data\n\t\t_fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );\n\t\n\t\tif ( oSettings.fnServerData )\n\t\t{\n\t\t\t// DataTables 1.9- compatibility\n\t\t\toSettings.fnServerData.call( instance,\n\t\t\t\toSettings.sAjaxSource,\n\t\t\t\t$.map( data, function (val, key) { // Need to convert back to 1.9 trad format\n\t\t\t\t\treturn { name: key, value: val };\n\t\t\t\t} ),\n\t\t\t\tcallback,\n\t\t\t\toSettings\n\t\t\t);\n\t\t}\n\t\telse if ( oSettings.sAjaxSource || typeof ajax === 'string' )\n\t\t{\n\t\t\t// DataTables 1.9- compatibility\n\t\t\toSettings.jqXHR = $.ajax( $.extend( baseAjax, {\n\t\t\t\turl: ajax || oSettings.sAjaxSource\n\t\t\t} ) );\n\t\t}\n\t\telse if ( typeof ajax === 'function' )\n\t\t{\n\t\t\t// Is a function - let the caller define what needs to be done\n\t\t\toSettings.jqXHR = ajax.call( instance, data, callback, oSettings );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Object to extend the base settings\n\t\t\toSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );\n\t\n\t\t\t// Restore for next time around\n\t\t\tajax.data = ajaxData;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Update the table using an Ajax call\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {boolean} Block the table drawing or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAjaxUpdate( settings )\n\t{\n\t\tsettings.iDraw++;\n\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t// Keep track of drawHold state to handle scrolling after the Ajax call\n\t\tvar drawHold = settings._drawHold;\n\t\n\t\t_fnBuildAjax(\n\t\t\tsettings,\n\t\t\t_fnAjaxParameters( settings ),\n\t\t\tfunction(json) {\n\t\t\t\tsettings._drawHold = drawHold;\n\t\t\t\t_fnAjaxUpdateDraw( settings, json );\n\t\t\t\tsettings._drawHold = false;\n\t\t\t}\n\t\t);\n\t}\n\t\n\t\n\t/**\n\t * Build up the parameters in an object needed for a server-side processing\n\t * request. Note that this is basically done twice, is different ways - a modern\n\t * method which is used by default in DataTables 1.10 which uses objects and\n\t * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if\n\t * the sAjaxSource option is used in the initialisation, or the legacyAjax\n\t * option is set.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {bool} block the table drawing or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAjaxParameters( settings )\n\t{\n\t\tvar\n\t\t\tcolumns = settings.aoColumns,\n\t\t\tcolumnCount = columns.length,\n\t\t\tfeatures = settings.oFeatures,\n\t\t\tpreSearch = settings.oPreviousSearch,\n\t\t\tpreColSearch = settings.aoPreSearchCols,\n\t\t\ti, data = [], dataProp, column, columnSearch,\n\t\t\tsort = _fnSortFlatten( settings ),\n\t\t\tdisplayStart = settings._iDisplayStart,\n\t\t\tdisplayLength = features.bPaginate !== false ?\n\t\t\t\tsettings._iDisplayLength :\n\t\t\t\t-1;\n\t\n\t\tvar param = function ( name, value ) {\n\t\t\tdata.push( { 'name': name, 'value': value } );\n\t\t};\n\t\n\t\t// DataTables 1.9- compatible method\n\t\tparam( 'sEcho',          settings.iDraw );\n\t\tparam( 'iColumns',       columnCount );\n\t\tparam( 'sColumns',       _pluck( columns, 'sName' ).join(',') );\n\t\tparam( 'iDisplayStart',  displayStart );\n\t\tparam( 'iDisplayLength', displayLength );\n\t\n\t\t// DataTables 1.10+ method\n\t\tvar d = {\n\t\t\tdraw:    settings.iDraw,\n\t\t\tcolumns: [],\n\t\t\torder:   [],\n\t\t\tstart:   displayStart,\n\t\t\tlength:  displayLength,\n\t\t\tsearch:  {\n\t\t\t\tvalue: preSearch.sSearch,\n\t\t\t\tregex: preSearch.bRegex\n\t\t\t}\n\t\t};\n\t\n\t\tfor ( i=0 ; i<columnCount ; i++ ) {\n\t\t\tcolumn = columns[i];\n\t\t\tcolumnSearch = preColSearch[i];\n\t\t\tdataProp = typeof column.mData==\"function\" ? 'function' : column.mData ;\n\t\n\t\t\td.columns.push( {\n\t\t\t\tdata:       dataProp,\n\t\t\t\tname:       column.sName,\n\t\t\t\tsearchable: column.bSearchable,\n\t\t\t\torderable:  column.bSortable,\n\t\t\t\tsearch:     {\n\t\t\t\t\tvalue: columnSearch.sSearch,\n\t\t\t\t\tregex: columnSearch.bRegex\n\t\t\t\t}\n\t\t\t} );\n\t\n\t\t\tparam( \"mDataProp_\"+i, dataProp );\n\t\n\t\t\tif ( features.bFilter ) {\n\t\t\t\tparam( 'sSearch_'+i,     columnSearch.sSearch );\n\t\t\t\tparam( 'bRegex_'+i,      columnSearch.bRegex );\n\t\t\t\tparam( 'bSearchable_'+i, column.bSearchable );\n\t\t\t}\n\t\n\t\t\tif ( features.bSort ) {\n\t\t\t\tparam( 'bSortable_'+i, column.bSortable );\n\t\t\t}\n\t\t}\n\t\n\t\tif ( features.bFilter ) {\n\t\t\tparam( 'sSearch', preSearch.sSearch );\n\t\t\tparam( 'bRegex', preSearch.bRegex );\n\t\t}\n\t\n\t\tif ( features.bSort ) {\n\t\t\t$.each( sort, function ( i, val ) {\n\t\t\t\td.order.push( { column: val.col, dir: val.dir } );\n\t\n\t\t\t\tparam( 'iSortCol_'+i, val.col );\n\t\t\t\tparam( 'sSortDir_'+i, val.dir );\n\t\t\t} );\n\t\n\t\t\tparam( 'iSortingCols', sort.length );\n\t\t}\n\t\n\t\t// If the legacy.ajax parameter is null, then we automatically decide which\n\t\t// form to use, based on sAjaxSource\n\t\tvar legacy = DataTable.ext.legacy.ajax;\n\t\tif ( legacy === null ) {\n\t\t\treturn settings.sAjaxSource ? data : d;\n\t\t}\n\t\n\t\t// Otherwise, if legacy has been specified then we use that to decide on the\n\t\t// form\n\t\treturn legacy ? data : d;\n\t}\n\t\n\t\n\t/**\n\t * Data the data from the server (nuking the old) and redraw the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} json json data return from the server.\n\t *  @param {string} json.sEcho Tracking flag for DataTables to match requests\n\t *  @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering\n\t *  @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering\n\t *  @param {array} json.aaData The data to display on this page\n\t *  @param {string} [json.sColumns] Column ordering (sName, comma separated)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnAjaxUpdateDraw ( settings, json )\n\t{\n\t\t// v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.\n\t\t// Support both\n\t\tvar compat = function ( old, modern ) {\n\t\t\treturn json[old] !== undefined ? json[old] : json[modern];\n\t\t};\n\t\n\t\tvar data = _fnAjaxDataSrc( settings, json );\n\t\tvar draw            = compat( 'sEcho',                'draw' );\n\t\tvar recordsTotal    = compat( 'iTotalRecords',        'recordsTotal' );\n\t\tvar recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );\n\t\n\t\tif ( draw !== undefined ) {\n\t\t\t// Protect against out of sequence returns\n\t\t\tif ( draw*1 < settings.iDraw ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettings.iDraw = draw * 1;\n\t\t}\n\t\n\t\t// No data in returned object, so rather than an array, we show an empty table\n\t\tif ( ! data ) {\n\t\t\tdata = [];\n\t\t}\n\t\n\t\t_fnClearTable( settings );\n\t\tsettings._iRecordsTotal   = parseInt(recordsTotal, 10);\n\t\tsettings._iRecordsDisplay = parseInt(recordsFiltered, 10);\n\t\n\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t_fnAddData( settings, data[i] );\n\t\t}\n\t\tsettings.aiDisplay = settings.aiDisplayMaster.slice();\n\t\n\t\t_fnDraw( settings, true );\n\t\n\t\tif ( ! settings._bInitComplete ) {\n\t\t\t_fnInitComplete( settings, json );\n\t\t}\n\t\n\t\t_fnProcessingDisplay( settings, false );\n\t}\n\t\n\t\n\t/**\n\t * Get the data from the JSON data source to use for drawing a table. Using\n\t * `_fnGetObjectDataFn` allows the data to be sourced from a property of the\n\t * source object, or from a processing function.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param  {object} json Data source object / array from the server\n\t *  @return {array} Array of data to use\n\t */\n\t function _fnAjaxDataSrc ( oSettings, json, write )\n\t {\n\t\tvar dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?\n\t\t\toSettings.ajax.dataSrc :\n\t\t\toSettings.sAjaxDataProp; // Compatibility with 1.9-.\n\t\n\t\tif ( ! write ) {\n\t\t\tif ( dataSrc === 'data' ) {\n\t\t\t\t// If the default, then we still want to support the old style, and safely ignore\n\t\t\t\t// it if possible\n\t\t\t\treturn json.aaData || json[dataSrc];\n\t\t\t}\n\t\n\t\t\treturn dataSrc !== \"\" ?\n\t\t\t\t_fnGetObjectDataFn( dataSrc )( json ) :\n\t\t\t\tjson;\n\t\t}\n\t\n\t\t// set\n\t\t_fnSetObjectDataFn( dataSrc )( json, write );\n\t}\n\t\n\t/**\n\t * Generate the node required for filtering text\n\t *  @returns {node} Filter control element\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlFilter ( settings )\n\t{\n\t\tvar classes = settings.oClasses;\n\t\tvar tableId = settings.sTableId;\n\t\tvar language = settings.oLanguage;\n\t\tvar previousSearch = settings.oPreviousSearch;\n\t\tvar features = settings.aanFeatures;\n\t\tvar input = '<input type=\"search\" class=\"'+classes.sFilterInput+'\"/>';\n\t\n\t\tvar str = language.sSearch;\n\t\tstr = str.match(/_INPUT_/) ?\n\t\t\tstr.replace('_INPUT_', input) :\n\t\t\tstr+input;\n\t\n\t\tvar filter = $('<div/>', {\n\t\t\t\t'id': ! features.f ? tableId+'_filter' : null,\n\t\t\t\t'class': classes.sFilter\n\t\t\t} )\n\t\t\t.append( $('<label/>' ).append( str ) );\n\t\n\t\tvar searchFn = function(event) {\n\t\t\t/* Update all other filter input elements for the new display */\n\t\t\tvar n = features.f;\n\t\t\tvar val = !this.value ? \"\" : this.value; // mental IE8 fix :-(\n\t\t\tif(previousSearch.return && event.key !== \"Enter\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t/* Now do the filter */\n\t\t\tif ( val != previousSearch.sSearch ) {\n\t\t\t\t_fnFilterComplete( settings, {\n\t\t\t\t\t\"sSearch\": val,\n\t\t\t\t\t\"bRegex\": previousSearch.bRegex,\n\t\t\t\t\t\"bSmart\": previousSearch.bSmart ,\n\t\t\t\t\t\"bCaseInsensitive\": previousSearch.bCaseInsensitive,\n\t\t\t\t\t\"return\": previousSearch.return\n\t\t\t\t} );\n\t\n\t\t\t\t// Need to redraw, without resorting\n\t\t\t\tsettings._iDisplayStart = 0;\n\t\t\t\t_fnDraw( settings );\n\t\t\t}\n\t\t};\n\t\n\t\tvar searchDelay = settings.searchDelay !== null ?\n\t\t\tsettings.searchDelay :\n\t\t\t_fnDataSource( settings ) === 'ssp' ?\n\t\t\t\t400 :\n\t\t\t\t0;\n\t\n\t\tvar jqFilter = $('input', filter)\n\t\t\t.val( previousSearch.sSearch )\n\t\t\t.attr( 'placeholder', language.sSearchPlaceholder )\n\t\t\t.on(\n\t\t\t\t'keyup.DT search.DT input.DT paste.DT cut.DT',\n\t\t\t\tsearchDelay ?\n\t\t\t\t\t_fnThrottle( searchFn, searchDelay ) :\n\t\t\t\t\tsearchFn\n\t\t\t)\n\t\t\t.on( 'mouseup.DT', function(e) {\n\t\t\t\t// Edge fix! Edge 17 does not trigger anything other than mouse events when clicking\n\t\t\t\t// on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`\n\t\t\t\t// checks the value to see if it has changed. In other browsers it won't have.\n\t\t\t\tsetTimeout( function () {\n\t\t\t\t\tsearchFn.call(jqFilter[0], e);\n\t\t\t\t}, 10);\n\t\t\t} )\n\t\t\t.on( 'keypress.DT', function(e) {\n\t\t\t\t/* Prevent form submission */\n\t\t\t\tif ( e.keyCode == 13 ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} )\n\t\t\t.attr('aria-controls', tableId);\n\t\n\t\t// Update the input elements whenever the table is filtered\n\t\t$(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {\n\t\t\tif ( settings === s ) {\n\t\t\t\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t\t\t\t// inside an iframe or frame...\n\t\t\t\ttry {\n\t\t\t\t\tif ( jqFilter[0] !== document.activeElement ) {\n\t\t\t\t\t\tjqFilter.val( previousSearch.sSearch );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch ( e ) {}\n\t\t\t}\n\t\t} );\n\t\n\t\treturn filter[0];\n\t}\n\t\n\t\n\t/**\n\t * Filter the table using both the global filter and column based filtering\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} oSearch search information\n\t *  @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterComplete ( oSettings, oInput, iForce )\n\t{\n\t\tvar oPrevSearch = oSettings.oPreviousSearch;\n\t\tvar aoPrevSearch = oSettings.aoPreSearchCols;\n\t\tvar fnSaveFilter = function ( oFilter ) {\n\t\t\t/* Save the filtering values */\n\t\t\toPrevSearch.sSearch = oFilter.sSearch;\n\t\t\toPrevSearch.bRegex = oFilter.bRegex;\n\t\t\toPrevSearch.bSmart = oFilter.bSmart;\n\t\t\toPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;\n\t\t\toPrevSearch.return = oFilter.return;\n\t\t};\n\t\tvar fnRegex = function ( o ) {\n\t\t\t// Backwards compatibility with the bEscapeRegex option\n\t\t\treturn o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;\n\t\t};\n\t\n\t\t// Resolve any column types that are unknown due to addition or invalidation\n\t\t// @todo As per sort - can this be moved into an event handler?\n\t\t_fnColumnTypes( oSettings );\n\t\n\t\t/* In server-side processing all filtering is done by the server, so no point hanging around here */\n\t\tif ( _fnDataSource( oSettings ) != 'ssp' )\n\t\t{\n\t\t\t/* Global filter */\n\t\t\t_fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );\n\t\t\tfnSaveFilter( oInput );\n\t\n\t\t\t/* Now do the individual column filter */\n\t\t\tfor ( var i=0 ; i<aoPrevSearch.length ; i++ )\n\t\t\t{\n\t\t\t\t_fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),\n\t\t\t\t\taoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );\n\t\t\t}\n\t\n\t\t\t/* Custom filtering */\n\t\t\t_fnFilterCustom( oSettings );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfnSaveFilter( oInput );\n\t\t}\n\t\n\t\t/* Tell the draw function we have been filtering */\n\t\toSettings.bFiltered = true;\n\t\t_fnCallbackFire( oSettings, null, 'search', [oSettings] );\n\t}\n\t\n\t\n\t/**\n\t * Apply custom filtering functions\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterCustom( settings )\n\t{\n\t\tvar filters = DataTable.ext.search;\n\t\tvar displayRows = settings.aiDisplay;\n\t\tvar row, rowIdx;\n\t\n\t\tfor ( var i=0, ien=filters.length ; i<ien ; i++ ) {\n\t\t\tvar rows = [];\n\t\n\t\t\t// Loop over each row and see if it should be included\n\t\t\tfor ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {\n\t\t\t\trowIdx = displayRows[ j ];\n\t\t\t\trow = settings.aoData[ rowIdx ];\n\t\n\t\t\t\tif ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {\n\t\t\t\t\trows.push( rowIdx );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// So the array reference doesn't break set the results into the\n\t\t\t// existing array\n\t\t\tdisplayRows.length = 0;\n\t\t\t$.merge( displayRows, rows );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Filter the table on a per-column basis\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {string} sInput string to filter on\n\t *  @param {int} iColumn column to filter\n\t *  @param {bool} bRegex treat search string as a regular expression or not\n\t *  @param {bool} bSmart use smart filtering or not\n\t *  @param {bool} bCaseInsensitive Do case insensitive matching or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )\n\t{\n\t\tif ( searchStr === '' ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar data;\n\t\tvar out = [];\n\t\tvar display = settings.aiDisplay;\n\t\tvar rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );\n\t\n\t\tfor ( var i=0 ; i<display.length ; i++ ) {\n\t\t\tdata = settings.aoData[ display[i] ]._aFilterData[ colIdx ];\n\t\n\t\t\tif ( rpSearch.test( data ) ) {\n\t\t\t\tout.push( display[i] );\n\t\t\t}\n\t\t}\n\t\n\t\tsettings.aiDisplay = out;\n\t}\n\t\n\t\n\t/**\n\t * Filter the data table based on user input and draw the table\n\t *  @param {object} settings dataTables settings object\n\t *  @param {string} input string to filter on\n\t *  @param {int} force optional - force a research of the master array (1) or not (undefined or 0)\n\t *  @param {bool} regex treat as a regular expression or not\n\t *  @param {bool} smart perform smart filtering or not\n\t *  @param {bool} caseInsensitive Do case insensitive matching or not\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilter( settings, input, force, regex, smart, caseInsensitive )\n\t{\n\t\tvar rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );\n\t\tvar prevSearch = settings.oPreviousSearch.sSearch;\n\t\tvar displayMaster = settings.aiDisplayMaster;\n\t\tvar display, invalidated, i;\n\t\tvar filtered = [];\n\t\n\t\t// Need to take account of custom filtering functions - always filter\n\t\tif ( DataTable.ext.search.length !== 0 ) {\n\t\t\tforce = true;\n\t\t}\n\t\n\t\t// Check if any of the rows were invalidated\n\t\tinvalidated = _fnFilterData( settings );\n\t\n\t\t// If the input is blank - we just want the full data set\n\t\tif ( input.length <= 0 ) {\n\t\t\tsettings.aiDisplay = displayMaster.slice();\n\t\t}\n\t\telse {\n\t\t\t// New search - start from the master array\n\t\t\tif ( invalidated ||\n\t\t\t\t force ||\n\t\t\t\t regex ||\n\t\t\t\t prevSearch.length > input.length ||\n\t\t\t\t input.indexOf(prevSearch) !== 0 ||\n\t\t\t\t settings.bSorted // On resort, the display master needs to be\n\t\t\t\t                  // re-filtered since indexes will have changed\n\t\t\t) {\n\t\t\t\tsettings.aiDisplay = displayMaster.slice();\n\t\t\t}\n\t\n\t\t\t// Search the display array\n\t\t\tdisplay = settings.aiDisplay;\n\t\n\t\t\tfor ( i=0 ; i<display.length ; i++ ) {\n\t\t\t\tif ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {\n\t\t\t\t\tfiltered.push( display[i] );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tsettings.aiDisplay = filtered;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Build a regular expression object suitable for searching a table\n\t *  @param {string} sSearch string to search for\n\t *  @param {bool} bRegex treat as a regular expression or not\n\t *  @param {bool} bSmart perform smart filtering or not\n\t *  @param {bool} bCaseInsensitive Do case insensitive matching or not\n\t *  @returns {RegExp} constructed object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFilterCreateSearch( search, regex, smart, caseInsensitive )\n\t{\n\t\tsearch = regex ?\n\t\t\tsearch :\n\t\t\t_fnEscapeRegex( search );\n\t\t\n\t\tif ( smart ) {\n\t\t\t/* For smart filtering we want to allow the search to work regardless of\n\t\t\t * word order. We also want double quoted text to be preserved, so word\n\t\t\t * order is important - a la google. So this is what we want to\n\t\t\t * generate:\n\t\t\t * \n\t\t\t * ^(?=.*?\\bone\\b)(?=.*?\\btwo three\\b)(?=.*?\\bfour\\b).*$\n\t\t\t */\n\t\t\tvar a = $.map( search.match( /[\"\\u201C][^\"\\u201D]+[\"\\u201D]|[^ ]+/g ) || [''], function ( word ) {\n\t\t\t\tif ( word.charAt(0) === '\"' ) {\n\t\t\t\t\tvar m = word.match( /^\"(.*)\"$/ );\n\t\t\t\t\tword = m ? m[1] : word;\n\t\t\t\t}\n\t\t\t\telse if ( word.charAt(0) === '\\u201C' ) {\n\t\t\t\t\tvar m = word.match( /^\\u201C(.*)\\u201D$/ );\n\t\t\t\t\tword = m ? m[1] : word;\n\t\t\t\t}\n\t\n\t\t\t\treturn word.replace('\"', '');\n\t\t\t} );\n\t\n\t\t\tsearch = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';\n\t\t}\n\t\n\t\treturn new RegExp( search, caseInsensitive ? 'i' : '' );\n\t}\n\t\n\t\n\t/**\n\t * Escape a string such that it can be used in a regular expression\n\t *  @param {string} sVal string to escape\n\t *  @returns {string} escaped string\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnEscapeRegex = DataTable.util.escapeRegex;\n\t\n\tvar __filter_div = $('<div>')[0];\n\tvar __filter_div_textContent = __filter_div.textContent !== undefined;\n\t\n\t// Update the filtering data for each row if needed (by invalidation or first run)\n\tfunction _fnFilterData ( settings )\n\t{\n\t\tvar columns = settings.aoColumns;\n\t\tvar column;\n\t\tvar i, j, ien, jen, filterData, cellData, row;\n\t\tvar wasInvalidated = false;\n\t\n\t\tfor ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\trow = settings.aoData[i];\n\t\n\t\t\tif ( ! row._aFilterData ) {\n\t\t\t\tfilterData = [];\n\t\n\t\t\t\tfor ( j=0, jen=columns.length ; j<jen ; j++ ) {\n\t\t\t\t\tcolumn = columns[j];\n\t\n\t\t\t\t\tif ( column.bSearchable ) {\n\t\t\t\t\t\tcellData = _fnGetCellData( settings, i, j, 'filter' );\n\t\n\t\t\t\t\t\t// Search in DataTables 1.10 is string based. In 1.11 this\n\t\t\t\t\t\t// should be altered to also allow strict type checking.\n\t\t\t\t\t\tif ( cellData === null ) {\n\t\t\t\t\t\t\tcellData = '';\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tif ( typeof cellData !== 'string' && cellData.toString ) {\n\t\t\t\t\t\t\tcellData = cellData.toString();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tcellData = '';\n\t\t\t\t\t}\n\t\n\t\t\t\t\t// If it looks like there is an HTML entity in the string,\n\t\t\t\t\t// attempt to decode it so sorting works as expected. Note that\n\t\t\t\t\t// we could use a single line of jQuery to do this, but the DOM\n\t\t\t\t\t// method used here is much faster http://jsperf.com/html-decode\n\t\t\t\t\tif ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {\n\t\t\t\t\t\t__filter_div.innerHTML = cellData;\n\t\t\t\t\t\tcellData = __filter_div_textContent ?\n\t\t\t\t\t\t\t__filter_div.textContent :\n\t\t\t\t\t\t\t__filter_div.innerText;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tif ( cellData.replace ) {\n\t\t\t\t\t\tcellData = cellData.replace(/[\\r\\n\\u2028]/g, '');\n\t\t\t\t\t}\n\t\n\t\t\t\t\tfilterData.push( cellData );\n\t\t\t\t}\n\t\n\t\t\t\trow._aFilterData = filterData;\n\t\t\t\trow._sFilterRow = filterData.join('  ');\n\t\t\t\twasInvalidated = true;\n\t\t\t}\n\t\t}\n\t\n\t\treturn wasInvalidated;\n\t}\n\t\n\t\n\t/**\n\t * Convert from the internal Hungarian notation to camelCase for external\n\t * interaction\n\t *  @param {object} obj Object to convert\n\t *  @returns {object} Inverted object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSearchToCamel ( obj )\n\t{\n\t\treturn {\n\t\t\tsearch:          obj.sSearch,\n\t\t\tsmart:           obj.bSmart,\n\t\t\tregex:           obj.bRegex,\n\t\t\tcaseInsensitive: obj.bCaseInsensitive\n\t\t};\n\t}\n\t\n\t\n\t\n\t/**\n\t * Convert from camelCase notation to the internal Hungarian. We could use the\n\t * Hungarian convert function here, but this is cleaner\n\t *  @param {object} obj Object to convert\n\t *  @returns {object} Inverted object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSearchToHung ( obj )\n\t{\n\t\treturn {\n\t\t\tsSearch:          obj.search,\n\t\t\tbSmart:           obj.smart,\n\t\t\tbRegex:           obj.regex,\n\t\t\tbCaseInsensitive: obj.caseInsensitive\n\t\t};\n\t}\n\t\n\t/**\n\t * Generate the node required for the info display\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {node} Information element\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlInfo ( settings )\n\t{\n\t\tvar\n\t\t\ttid = settings.sTableId,\n\t\t\tnodes = settings.aanFeatures.i,\n\t\t\tn = $('<div/>', {\n\t\t\t\t'class': settings.oClasses.sInfo,\n\t\t\t\t'id': ! nodes ? tid+'_info' : null\n\t\t\t} );\n\t\n\t\tif ( ! nodes ) {\n\t\t\t// Update display on each draw\n\t\t\tsettings.aoDrawCallback.push( {\n\t\t\t\t\"fn\": _fnUpdateInfo,\n\t\t\t\t\"sName\": \"information\"\n\t\t\t} );\n\t\n\t\t\tn\n\t\t\t\t.attr( 'role', 'status' )\n\t\t\t\t.attr( 'aria-live', 'polite' );\n\t\n\t\t\t// Table is described by our info div\n\t\t\t$(settings.nTable).attr( 'aria-describedby', tid+'_info' );\n\t\t}\n\t\n\t\treturn n[0];\n\t}\n\t\n\t\n\t/**\n\t * Update the information elements in the display\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnUpdateInfo ( settings )\n\t{\n\t\t/* Show information about the table */\n\t\tvar nodes = settings.aanFeatures.i;\n\t\tif ( nodes.length === 0 ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar\n\t\t\tlang  = settings.oLanguage,\n\t\t\tstart = settings._iDisplayStart+1,\n\t\t\tend   = settings.fnDisplayEnd(),\n\t\t\tmax   = settings.fnRecordsTotal(),\n\t\t\ttotal = settings.fnRecordsDisplay(),\n\t\t\tout   = total ?\n\t\t\t\tlang.sInfo :\n\t\t\t\tlang.sInfoEmpty;\n\t\n\t\tif ( total !== max ) {\n\t\t\t/* Record set after filtering */\n\t\t\tout += ' ' + lang.sInfoFiltered;\n\t\t}\n\t\n\t\t// Convert the macros\n\t\tout += lang.sInfoPostFix;\n\t\tout = _fnInfoMacros( settings, out );\n\t\n\t\tvar callback = lang.fnInfoCallback;\n\t\tif ( callback !== null ) {\n\t\t\tout = callback.call( settings.oInstance,\n\t\t\t\tsettings, start, end, max, total, out\n\t\t\t);\n\t\t}\n\t\n\t\t$(nodes).html( out );\n\t}\n\t\n\t\n\tfunction _fnInfoMacros ( settings, str )\n\t{\n\t\t// When infinite scrolling, we are always starting at 1. _iDisplayStart is used only\n\t\t// internally\n\t\tvar\n\t\t\tformatter  = settings.fnFormatNumber,\n\t\t\tstart      = settings._iDisplayStart+1,\n\t\t\tlen        = settings._iDisplayLength,\n\t\t\tvis        = settings.fnRecordsDisplay(),\n\t\t\tall        = len === -1;\n\t\n\t\treturn str.\n\t\t\treplace(/_START_/g, formatter.call( settings, start ) ).\n\t\t\treplace(/_END_/g,   formatter.call( settings, settings.fnDisplayEnd() ) ).\n\t\t\treplace(/_MAX_/g,   formatter.call( settings, settings.fnRecordsTotal() ) ).\n\t\t\treplace(/_TOTAL_/g, formatter.call( settings, vis ) ).\n\t\t\treplace(/_PAGE_/g,  formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).\n\t\t\treplace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );\n\t}\n\t\n\t\n\t\n\t/**\n\t * Draw the table for the first time, adding all required features\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnInitialise ( settings )\n\t{\n\t\tvar i, iLen, iAjaxStart=settings.iInitDisplayStart;\n\t\tvar columns = settings.aoColumns, column;\n\t\tvar features = settings.oFeatures;\n\t\tvar deferLoading = settings.bDeferLoading; // value modified by the draw\n\t\n\t\t/* Ensure that the table data is fully initialised */\n\t\tif ( ! settings.bInitialised ) {\n\t\t\tsetTimeout( function(){ _fnInitialise( settings ); }, 200 );\n\t\t\treturn;\n\t\t}\n\t\n\t\t/* Show the display HTML options */\n\t\t_fnAddOptionsHtml( settings );\n\t\n\t\t/* Build and draw the header / footer for the table */\n\t\t_fnBuildHead( settings );\n\t\t_fnDrawHead( settings, settings.aoHeader );\n\t\t_fnDrawHead( settings, settings.aoFooter );\n\t\n\t\t/* Okay to show that something is going on now */\n\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t/* Calculate sizes for columns */\n\t\tif ( features.bAutoWidth ) {\n\t\t\t_fnCalculateColumnWidths( settings );\n\t\t}\n\t\n\t\tfor ( i=0, iLen=columns.length ; i<iLen ; i++ ) {\n\t\t\tcolumn = columns[i];\n\t\n\t\t\tif ( column.sWidth ) {\n\t\t\t\tcolumn.nTh.style.width = _fnStringToCss( column.sWidth );\n\t\t\t}\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'preInit', [settings] );\n\t\n\t\t// If there is default sorting required - let's do it. The sort function\n\t\t// will do the drawing for us. Otherwise we draw the table regardless of the\n\t\t// Ajax source - this allows the table to look initialised for Ajax sourcing\n\t\t// data (show 'loading' message possibly)\n\t\t_fnReDraw( settings );\n\t\n\t\t// Server-side processing init complete is done by _fnAjaxUpdateDraw\n\t\tvar dataSrc = _fnDataSource( settings );\n\t\tif ( dataSrc != 'ssp' || deferLoading ) {\n\t\t\t// if there is an ajax source load the data\n\t\t\tif ( dataSrc == 'ajax' ) {\n\t\t\t\t_fnBuildAjax( settings, [], function(json) {\n\t\t\t\t\tvar aData = _fnAjaxDataSrc( settings, json );\n\t\n\t\t\t\t\t// Got the data - add it to the table\n\t\t\t\t\tfor ( i=0 ; i<aData.length ; i++ ) {\n\t\t\t\t\t\t_fnAddData( settings, aData[i] );\n\t\t\t\t\t}\n\t\n\t\t\t\t\t// Reset the init display for cookie saving. We've already done\n\t\t\t\t\t// a filter, and therefore cleared it before. So we need to make\n\t\t\t\t\t// it appear 'fresh'\n\t\t\t\t\tsettings.iInitDisplayStart = iAjaxStart;\n\t\n\t\t\t\t\t_fnReDraw( settings );\n\t\n\t\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t\t\t_fnInitComplete( settings, json );\n\t\t\t\t}, settings );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t\t_fnInitComplete( settings );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Draw the table for the first time, adding all required features\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} [json] JSON from the server that completed the table, if using Ajax source\n\t *    with client-side processing (optional)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnInitComplete ( settings, json )\n\t{\n\t\tsettings._bInitComplete = true;\n\t\n\t\t// When data was added after the initialisation (data or Ajax) we need to\n\t\t// calculate the column sizing\n\t\tif ( json || settings.oInit.aaData ) {\n\t\t\t_fnAdjustColumnSizing( settings );\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'plugin-init', [settings, json] );\n\t\t_fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );\n\t}\n\t\n\t\n\tfunction _fnLengthChange ( settings, val )\n\t{\n\t\tvar len = parseInt( val, 10 );\n\t\tsettings._iDisplayLength = len;\n\t\n\t\t_fnLengthOverflow( settings );\n\t\n\t\t// Fire length change event\n\t\t_fnCallbackFire( settings, null, 'length', [settings, len] );\n\t}\n\t\n\t\n\t/**\n\t * Generate the node required for user display length changing\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {node} Display length feature node\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlLength ( settings )\n\t{\n\t\tvar\n\t\t\tclasses  = settings.oClasses,\n\t\t\ttableId  = settings.sTableId,\n\t\t\tmenu     = settings.aLengthMenu,\n\t\t\td2       = Array.isArray( menu[0] ),\n\t\t\tlengths  = d2 ? menu[0] : menu,\n\t\t\tlanguage = d2 ? menu[1] : menu;\n\t\n\t\tvar select = $('<select/>', {\n\t\t\t'name':          tableId+'_length',\n\t\t\t'aria-controls': tableId,\n\t\t\t'class':         classes.sLengthSelect\n\t\t} );\n\t\n\t\tfor ( var i=0, ien=lengths.length ; i<ien ; i++ ) {\n\t\t\tselect[0][ i ] = new Option(\n\t\t\t\ttypeof language[i] === 'number' ?\n\t\t\t\t\tsettings.fnFormatNumber( language[i] ) :\n\t\t\t\t\tlanguage[i],\n\t\t\t\tlengths[i]\n\t\t\t);\n\t\t}\n\t\n\t\tvar div = $('<div><label/></div>').addClass( classes.sLength );\n\t\tif ( ! settings.aanFeatures.l ) {\n\t\t\tdiv[0].id = tableId+'_length';\n\t\t}\n\t\n\t\tdiv.children().append(\n\t\t\tsettings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )\n\t\t);\n\t\n\t\t// Can't use `select` variable as user might provide their own and the\n\t\t// reference is broken by the use of outerHTML\n\t\t$('select', div)\n\t\t\t.val( settings._iDisplayLength )\n\t\t\t.on( 'change.DT', function(e) {\n\t\t\t\t_fnLengthChange( settings, $(this).val() );\n\t\t\t\t_fnDraw( settings );\n\t\t\t} );\n\t\n\t\t// Update node value whenever anything changes the table's length\n\t\t$(settings.nTable).on( 'length.dt.DT', function (e, s, len) {\n\t\t\tif ( settings === s ) {\n\t\t\t\t$('select', div).val( len );\n\t\t\t}\n\t\t} );\n\t\n\t\treturn div[0];\n\t}\n\t\n\t\n\t\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Note that most of the paging logic is done in\n\t * DataTable.ext.pager\n\t */\n\t\n\t/**\n\t * Generate the node required for default pagination\n\t *  @param {object} oSettings dataTables settings object\n\t *  @returns {node} Pagination feature node\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlPaginate ( settings )\n\t{\n\t\tvar\n\t\t\ttype   = settings.sPaginationType,\n\t\t\tplugin = DataTable.ext.pager[ type ],\n\t\t\tmodern = typeof plugin === 'function',\n\t\t\tredraw = function( settings ) {\n\t\t\t\t_fnDraw( settings );\n\t\t\t},\n\t\t\tnode = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],\n\t\t\tfeatures = settings.aanFeatures;\n\t\n\t\tif ( ! modern ) {\n\t\t\tplugin.fnInit( settings, node, redraw );\n\t\t}\n\t\n\t\t/* Add a draw callback for the pagination on first instance, to update the paging display */\n\t\tif ( ! features.p )\n\t\t{\n\t\t\tnode.id = settings.sTableId+'_paginate';\n\t\n\t\t\tsettings.aoDrawCallback.push( {\n\t\t\t\t\"fn\": function( settings ) {\n\t\t\t\t\tif ( modern ) {\n\t\t\t\t\t\tvar\n\t\t\t\t\t\t\tstart      = settings._iDisplayStart,\n\t\t\t\t\t\t\tlen        = settings._iDisplayLength,\n\t\t\t\t\t\t\tvisRecords = settings.fnRecordsDisplay(),\n\t\t\t\t\t\t\tall        = len === -1,\n\t\t\t\t\t\t\tpage = all ? 0 : Math.ceil( start / len ),\n\t\t\t\t\t\t\tpages = all ? 1 : Math.ceil( visRecords / len ),\n\t\t\t\t\t\t\tbuttons = plugin(page, pages),\n\t\t\t\t\t\t\ti, ien;\n\t\n\t\t\t\t\t\tfor ( i=0, ien=features.p.length ; i<ien ; i++ ) {\n\t\t\t\t\t\t\t_fnRenderer( settings, 'pageButton' )(\n\t\t\t\t\t\t\t\tsettings, features.p[i], i, buttons, page, pages\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tplugin.fnUpdate( settings, redraw );\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"sName\": \"pagination\"\n\t\t\t} );\n\t\t}\n\t\n\t\treturn node;\n\t}\n\t\n\t\n\t/**\n\t * Alter the display settings to change the page\n\t *  @param {object} settings DataTables settings object\n\t *  @param {string|int} action Paging action to take: \"first\", \"previous\",\n\t *    \"next\" or \"last\" or page number to jump to (integer)\n\t *  @param [bool] redraw Automatically draw the update or not\n\t *  @returns {bool} true page has changed, false - no change\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnPageChange ( settings, action, redraw )\n\t{\n\t\tvar\n\t\t\tstart     = settings._iDisplayStart,\n\t\t\tlen       = settings._iDisplayLength,\n\t\t\trecords   = settings.fnRecordsDisplay();\n\t\n\t\tif ( records === 0 || len === -1 )\n\t\t{\n\t\t\tstart = 0;\n\t\t}\n\t\telse if ( typeof action === \"number\" )\n\t\t{\n\t\t\tstart = action * len;\n\t\n\t\t\tif ( start > records )\n\t\t\t{\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t}\n\t\telse if ( action == \"first\" )\n\t\t{\n\t\t\tstart = 0;\n\t\t}\n\t\telse if ( action == \"previous\" )\n\t\t{\n\t\t\tstart = len >= 0 ?\n\t\t\t\tstart - len :\n\t\t\t\t0;\n\t\n\t\t\tif ( start < 0 )\n\t\t\t{\n\t\t\t  start = 0;\n\t\t\t}\n\t\t}\n\t\telse if ( action == \"next\" )\n\t\t{\n\t\t\tif ( start + len < records )\n\t\t\t{\n\t\t\t\tstart += len;\n\t\t\t}\n\t\t}\n\t\telse if ( action == \"last\" )\n\t\t{\n\t\t\tstart = Math.floor( (records-1) / len) * len;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_fnLog( settings, 0, \"Unknown paging action: \"+action, 5 );\n\t\t}\n\t\n\t\tvar changed = settings._iDisplayStart !== start;\n\t\tsettings._iDisplayStart = start;\n\t\n\t\tif ( changed ) {\n\t\t\t_fnCallbackFire( settings, null, 'page', [settings] );\n\t\n\t\t\tif ( redraw ) {\n\t\t\t\t_fnDraw( settings );\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// No change event - paging was called, but no change\n\t\t\t_fnCallbackFire( settings, null, 'page-nc', [settings] );\n\t\t}\n\t\n\t\treturn changed;\n\t}\n\t\n\t\n\t\n\t/**\n\t * Generate the node required for the processing node\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {node} Processing element\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlProcessing ( settings )\n\t{\n\t\treturn $('<div/>', {\n\t\t\t\t'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,\n\t\t\t\t'class': settings.oClasses.sProcessing,\n\t\t\t\t'role': 'status'\n\t\t\t} )\n\t\t\t.html( settings.oLanguage.sProcessing )\n\t\t\t.append('<div><div></div><div></div><div></div><div></div></div>')\n\t\t\t.insertBefore( settings.nTable )[0];\n\t}\n\t\n\t\n\t/**\n\t * Display or hide the processing indicator\n\t *  @param {object} settings dataTables settings object\n\t *  @param {bool} show Show the processing indicator (true) or not (false)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnProcessingDisplay ( settings, show )\n\t{\n\t\tif ( settings.oFeatures.bProcessing ) {\n\t\t\t$(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );\n\t\t}\n\t\n\t\t_fnCallbackFire( settings, null, 'processing', [settings, show] );\n\t}\n\t\n\t/**\n\t * Add any control elements for the table - specifically scrolling\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {node} Node to add to the DOM\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnFeatureHtmlTable ( settings )\n\t{\n\t\tvar table = $(settings.nTable);\n\t\n\t\t// Scrolling from here on in\n\t\tvar scroll = settings.oScroll;\n\t\n\t\tif ( scroll.sX === '' && scroll.sY === '' ) {\n\t\t\treturn settings.nTable;\n\t\t}\n\t\n\t\tvar scrollX = scroll.sX;\n\t\tvar scrollY = scroll.sY;\n\t\tvar classes = settings.oClasses;\n\t\tvar caption = table.children('caption');\n\t\tvar captionSide = caption.length ? caption[0]._captionSide : null;\n\t\tvar headerClone = $( table[0].cloneNode(false) );\n\t\tvar footerClone = $( table[0].cloneNode(false) );\n\t\tvar footer = table.children('tfoot');\n\t\tvar _div = '<div/>';\n\t\tvar size = function ( s ) {\n\t\t\treturn !s ? null : _fnStringToCss( s );\n\t\t};\n\t\n\t\tif ( ! footer.length ) {\n\t\t\tfooter = null;\n\t\t}\n\t\n\t\t/*\n\t\t * The HTML structure that we want to generate in this function is:\n\t\t *  div - scroller\n\t\t *    div - scroll head\n\t\t *      div - scroll head inner\n\t\t *        table - scroll head table\n\t\t *          thead - thead\n\t\t *    div - scroll body\n\t\t *      table - table (master table)\n\t\t *        thead - thead clone for sizing\n\t\t *        tbody - tbody\n\t\t *    div - scroll foot\n\t\t *      div - scroll foot inner\n\t\t *        table - scroll foot table\n\t\t *          tfoot - tfoot\n\t\t */\n\t\tvar scroller = $( _div, { 'class': classes.sScrollWrapper } )\n\t\t\t.append(\n\t\t\t\t$(_div, { 'class': classes.sScrollHead } )\n\t\t\t\t\t.css( {\n\t\t\t\t\t\toverflow: 'hidden',\n\t\t\t\t\t\tposition: 'relative',\n\t\t\t\t\t\tborder: 0,\n\t\t\t\t\t\twidth: scrollX ? size(scrollX) : '100%'\n\t\t\t\t\t} )\n\t\t\t\t\t.append(\n\t\t\t\t\t\t$(_div, { 'class': classes.sScrollHeadInner } )\n\t\t\t\t\t\t\t.css( {\n\t\t\t\t\t\t\t\t'box-sizing': 'content-box',\n\t\t\t\t\t\t\t\twidth: scroll.sXInner || '100%'\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\theaderClone\n\t\t\t\t\t\t\t\t\t.removeAttr('id')\n\t\t\t\t\t\t\t\t\t.css( 'margin-left', 0 )\n\t\t\t\t\t\t\t\t\t.append( captionSide === 'top' ? caption : null )\n\t\t\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\t\t\ttable.children('thead')\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t)\n\t\t\t.append(\n\t\t\t\t$(_div, { 'class': classes.sScrollBody } )\n\t\t\t\t\t.css( {\n\t\t\t\t\t\tposition: 'relative',\n\t\t\t\t\t\toverflow: 'auto',\n\t\t\t\t\t\twidth: size( scrollX )\n\t\t\t\t\t} )\n\t\t\t\t\t.append( table )\n\t\t\t);\n\t\n\t\tif ( footer ) {\n\t\t\tscroller.append(\n\t\t\t\t$(_div, { 'class': classes.sScrollFoot } )\n\t\t\t\t\t.css( {\n\t\t\t\t\t\toverflow: 'hidden',\n\t\t\t\t\t\tborder: 0,\n\t\t\t\t\t\twidth: scrollX ? size(scrollX) : '100%'\n\t\t\t\t\t} )\n\t\t\t\t\t.append(\n\t\t\t\t\t\t$(_div, { 'class': classes.sScrollFootInner } )\n\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\tfooterClone\n\t\t\t\t\t\t\t\t\t.removeAttr('id')\n\t\t\t\t\t\t\t\t\t.css( 'margin-left', 0 )\n\t\t\t\t\t\t\t\t\t.append( captionSide === 'bottom' ? caption : null )\n\t\t\t\t\t\t\t\t\t.append(\n\t\t\t\t\t\t\t\t\t\ttable.children('tfoot')\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\n\t\tvar children = scroller.children();\n\t\tvar scrollHead = children[0];\n\t\tvar scrollBody = children[1];\n\t\tvar scrollFoot = footer ? children[2] : null;\n\t\n\t\t// When the body is scrolled, then we also want to scroll the headers\n\t\tif ( scrollX ) {\n\t\t\t$(scrollBody).on( 'scroll.DT', function (e) {\n\t\t\t\tvar scrollLeft = this.scrollLeft;\n\t\n\t\t\t\tscrollHead.scrollLeft = scrollLeft;\n\t\n\t\t\t\tif ( footer ) {\n\t\t\t\t\tscrollFoot.scrollLeft = scrollLeft;\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t\n\t\t$(scrollBody).css('max-height', scrollY);\n\t\tif (! scroll.bCollapse) {\n\t\t\t$(scrollBody).css('height', scrollY);\n\t\t}\n\t\n\t\tsettings.nScrollHead = scrollHead;\n\t\tsettings.nScrollBody = scrollBody;\n\t\tsettings.nScrollFoot = scrollFoot;\n\t\n\t\t// On redraw - align columns\n\t\tsettings.aoDrawCallback.push( {\n\t\t\t\"fn\": _fnScrollDraw,\n\t\t\t\"sName\": \"scrolling\"\n\t\t} );\n\t\n\t\treturn scroller[0];\n\t}\n\t\n\t\n\t\n\t/**\n\t * Update the header, footer and body tables for resizing - i.e. column\n\t * alignment.\n\t *\n\t * Welcome to the most horrible function DataTables. The process that this\n\t * function follows is basically:\n\t *   1. Re-create the table inside the scrolling div\n\t *   2. Take live measurements from the DOM\n\t *   3. Apply the measurements to align the columns\n\t *   4. Clean up\n\t *\n\t *  @param {object} settings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnScrollDraw ( settings )\n\t{\n\t\t// Given that this is such a monster function, a lot of variables are use\n\t\t// to try and keep the minimised size as small as possible\n\t\tvar\n\t\t\tscroll         = settings.oScroll,\n\t\t\tscrollX        = scroll.sX,\n\t\t\tscrollXInner   = scroll.sXInner,\n\t\t\tscrollY        = scroll.sY,\n\t\t\tbarWidth       = scroll.iBarWidth,\n\t\t\tdivHeader      = $(settings.nScrollHead),\n\t\t\tdivHeaderStyle = divHeader[0].style,\n\t\t\tdivHeaderInner = divHeader.children('div'),\n\t\t\tdivHeaderInnerStyle = divHeaderInner[0].style,\n\t\t\tdivHeaderTable = divHeaderInner.children('table'),\n\t\t\tdivBodyEl      = settings.nScrollBody,\n\t\t\tdivBody        = $(divBodyEl),\n\t\t\tdivBodyStyle   = divBodyEl.style,\n\t\t\tdivFooter      = $(settings.nScrollFoot),\n\t\t\tdivFooterInner = divFooter.children('div'),\n\t\t\tdivFooterTable = divFooterInner.children('table'),\n\t\t\theader         = $(settings.nTHead),\n\t\t\ttable          = $(settings.nTable),\n\t\t\ttableEl        = table[0],\n\t\t\ttableStyle     = tableEl.style,\n\t\t\tfooter         = settings.nTFoot ? $(settings.nTFoot) : null,\n\t\t\tbrowser        = settings.oBrowser,\n\t\t\tie67           = browser.bScrollOversize,\n\t\t\tdtHeaderCells  = _pluck( settings.aoColumns, 'nTh' ),\n\t\t\theaderTrgEls, footerTrgEls,\n\t\t\theaderSrcEls, footerSrcEls,\n\t\t\theaderCopy, footerCopy,\n\t\t\theaderWidths=[], footerWidths=[],\n\t\t\theaderContent=[], footerContent=[],\n\t\t\tidx, correction, sanityWidth,\n\t\t\tzeroOut = function(nSizer) {\n\t\t\t\tvar style = nSizer.style;\n\t\t\t\tstyle.paddingTop = \"0\";\n\t\t\t\tstyle.paddingBottom = \"0\";\n\t\t\t\tstyle.borderTopWidth = \"0\";\n\t\t\t\tstyle.borderBottomWidth = \"0\";\n\t\t\t\tstyle.height = 0;\n\t\t\t};\n\t\n\t\t// If the scrollbar visibility has changed from the last draw, we need to\n\t\t// adjust the column sizes as the table width will have changed to account\n\t\t// for the scrollbar\n\t\tvar scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;\n\t\t\n\t\tif ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {\n\t\t\tsettings.scrollBarVis = scrollBarVis;\n\t\t\t_fnAdjustColumnSizing( settings );\n\t\t\treturn; // adjust column sizing will call this function again\n\t\t}\n\t\telse {\n\t\t\tsettings.scrollBarVis = scrollBarVis;\n\t\t}\n\t\n\t\t/*\n\t\t * 1. Re-create the table inside the scrolling div\n\t\t */\n\t\n\t\t// Remove the old minimised thead and tfoot elements in the inner table\n\t\ttable.children('thead, tfoot').remove();\n\t\n\t\tif ( footer ) {\n\t\t\tfooterCopy = footer.clone().prependTo( table );\n\t\t\tfooterTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized\n\t\t\tfooterSrcEls = footerCopy.find('tr');\n\t\t\tfooterCopy.find('[id]').removeAttr('id');\n\t\t}\n\t\n\t\t// Clone the current header and footer elements and then place it into the inner table\n\t\theaderCopy = header.clone().prependTo( table );\n\t\theaderTrgEls = header.find('tr'); // original header is in its own table\n\t\theaderSrcEls = headerCopy.find('tr');\n\t\theaderCopy.find('th, td').removeAttr('tabindex');\n\t\theaderCopy.find('[id]').removeAttr('id');\n\t\n\t\n\t\t/*\n\t\t * 2. Take live measurements from the DOM - do not alter the DOM itself!\n\t\t */\n\t\n\t\t// Remove old sizing and apply the calculated column widths\n\t\t// Get the unique column headers in the newly created (cloned) header. We want to apply the\n\t\t// calculated sizes to this header\n\t\tif ( ! scrollX )\n\t\t{\n\t\t\tdivBodyStyle.width = '100%';\n\t\t\tdivHeader[0].style.width = '100%';\n\t\t}\n\t\n\t\t$.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {\n\t\t\tidx = _fnVisibleToColumnIndex( settings, i );\n\t\t\tel.style.width = settings.aoColumns[idx].sWidth;\n\t\t} );\n\t\n\t\tif ( footer ) {\n\t\t\t_fnApplyToChildren( function(n) {\n\t\t\t\tn.style.width = \"\";\n\t\t\t}, footerSrcEls );\n\t\t}\n\t\n\t\t// Size the table as a whole\n\t\tsanityWidth = table.outerWidth();\n\t\tif ( scrollX === \"\" ) {\n\t\t\t// No x scrolling\n\t\t\ttableStyle.width = \"100%\";\n\t\n\t\t\t// IE7 will make the width of the table when 100% include the scrollbar\n\t\t\t// - which is shouldn't. When there is a scrollbar we need to take this\n\t\t\t// into account.\n\t\t\tif ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||\n\t\t\t\tdivBody.css('overflow-y') == \"scroll\")\n\t\t\t) {\n\t\t\t\ttableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);\n\t\t\t}\n\t\n\t\t\t// Recalculate the sanity width\n\t\t\tsanityWidth = table.outerWidth();\n\t\t}\n\t\telse if ( scrollXInner !== \"\" ) {\n\t\t\t// legacy x scroll inner has been given - use it\n\t\t\ttableStyle.width = _fnStringToCss(scrollXInner);\n\t\n\t\t\t// Recalculate the sanity width\n\t\t\tsanityWidth = table.outerWidth();\n\t\t}\n\t\n\t\t// Hidden header should have zero height, so remove padding and borders. Then\n\t\t// set the width based on the real headers\n\t\n\t\t// Apply all styles in one pass\n\t\t_fnApplyToChildren( zeroOut, headerSrcEls );\n\t\n\t\t// Read all widths in next pass\n\t\t_fnApplyToChildren( function(nSizer) {\n\t\t\tvar style = window.getComputedStyle ?\n\t\t\t\twindow.getComputedStyle(nSizer).width :\n\t\t\t\t_fnStringToCss( $(nSizer).width() );\n\t\n\t\t\theaderContent.push( nSizer.innerHTML );\n\t\t\theaderWidths.push( style );\n\t\t}, headerSrcEls );\n\t\n\t\t// Apply all widths in final pass\n\t\t_fnApplyToChildren( function(nToSize, i) {\n\t\t\tnToSize.style.width = headerWidths[i];\n\t\t}, headerTrgEls );\n\t\n\t\t$(headerSrcEls).css('height', 0);\n\t\n\t\t/* Same again with the footer if we have one */\n\t\tif ( footer )\n\t\t{\n\t\t\t_fnApplyToChildren( zeroOut, footerSrcEls );\n\t\n\t\t\t_fnApplyToChildren( function(nSizer) {\n\t\t\t\tfooterContent.push( nSizer.innerHTML );\n\t\t\t\tfooterWidths.push( _fnStringToCss( $(nSizer).css('width') ) );\n\t\t\t}, footerSrcEls );\n\t\n\t\t\t_fnApplyToChildren( function(nToSize, i) {\n\t\t\t\tnToSize.style.width = footerWidths[i];\n\t\t\t}, footerTrgEls );\n\t\n\t\t\t$(footerSrcEls).height(0);\n\t\t}\n\t\n\t\n\t\t/*\n\t\t * 3. Apply the measurements\n\t\t */\n\t\n\t\t// \"Hide\" the header and footer that we used for the sizing. We need to keep\n\t\t// the content of the cell so that the width applied to the header and body\n\t\t// both match, but we want to hide it completely. We want to also fix their\n\t\t// width to what they currently are\n\t\t_fnApplyToChildren( function(nSizer, i) {\n\t\t\tnSizer.innerHTML = '<div class=\"dataTables_sizing\">'+headerContent[i]+'</div>';\n\t\t\tnSizer.childNodes[0].style.height = \"0\";\n\t\t\tnSizer.childNodes[0].style.overflow = \"hidden\";\n\t\t\tnSizer.style.width = headerWidths[i];\n\t\t}, headerSrcEls );\n\t\n\t\tif ( footer )\n\t\t{\n\t\t\t_fnApplyToChildren( function(nSizer, i) {\n\t\t\t\tnSizer.innerHTML = '<div class=\"dataTables_sizing\">'+footerContent[i]+'</div>';\n\t\t\t\tnSizer.childNodes[0].style.height = \"0\";\n\t\t\t\tnSizer.childNodes[0].style.overflow = \"hidden\";\n\t\t\t\tnSizer.style.width = footerWidths[i];\n\t\t\t}, footerSrcEls );\n\t\t}\n\t\n\t\t// Sanity check that the table is of a sensible width. If not then we are going to get\n\t\t// misalignment - try to prevent this by not allowing the table to shrink below its min width\n\t\tif ( Math.round(table.outerWidth()) < Math.round(sanityWidth) )\n\t\t{\n\t\t\t// The min width depends upon if we have a vertical scrollbar visible or not */\n\t\t\tcorrection = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||\n\t\t\t\tdivBody.css('overflow-y') == \"scroll\")) ?\n\t\t\t\t\tsanityWidth+barWidth :\n\t\t\t\t\tsanityWidth;\n\t\n\t\t\t// IE6/7 are a law unto themselves...\n\t\t\tif ( ie67 && (divBodyEl.scrollHeight >\n\t\t\t\tdivBodyEl.offsetHeight || divBody.css('overflow-y') == \"scroll\")\n\t\t\t) {\n\t\t\t\ttableStyle.width = _fnStringToCss( correction-barWidth );\n\t\t\t}\n\t\n\t\t\t// And give the user a warning that we've stopped the table getting too small\n\t\t\tif ( scrollX === \"\" || scrollXInner !== \"\" ) {\n\t\t\t\t_fnLog( settings, 1, 'Possible column misalignment', 6 );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcorrection = '100%';\n\t\t}\n\t\n\t\t// Apply to the container elements\n\t\tdivBodyStyle.width = _fnStringToCss( correction );\n\t\tdivHeaderStyle.width = _fnStringToCss( correction );\n\t\n\t\tif ( footer ) {\n\t\t\tsettings.nScrollFoot.style.width = _fnStringToCss( correction );\n\t\t}\n\t\n\t\n\t\t/*\n\t\t * 4. Clean up\n\t\t */\n\t\tif ( ! scrollY ) {\n\t\t\t/* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting\n\t\t\t * the scrollbar height from the visible display, rather than adding it on. We need to\n\t\t\t * set the height in order to sort this. Don't want to do it in any other browsers.\n\t\t\t */\n\t\t\tif ( ie67 ) {\n\t\t\t\tdivBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );\n\t\t\t}\n\t\t}\n\t\n\t\t/* Finally set the width's of the header and footer tables */\n\t\tvar iOuterWidth = table.outerWidth();\n\t\tdivHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );\n\t\tdivHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );\n\t\n\t\t// Figure out if there are scrollbar present - if so then we need a the header and footer to\n\t\t// provide a bit more space to allow \"overflow\" scrolling (i.e. past the scrollbar)\n\t\tvar bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == \"scroll\";\n\t\tvar padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );\n\t\tdivHeaderInnerStyle[ padding ] = bScrolling ? barWidth+\"px\" : \"0px\";\n\t\n\t\tif ( footer ) {\n\t\t\tdivFooterTable[0].style.width = _fnStringToCss( iOuterWidth );\n\t\t\tdivFooterInner[0].style.width = _fnStringToCss( iOuterWidth );\n\t\t\tdivFooterInner[0].style[padding] = bScrolling ? barWidth+\"px\" : \"0px\";\n\t\t}\n\t\n\t\t// Correct DOM ordering for colgroup - comes before the thead\n\t\ttable.children('colgroup').insertBefore( table.children('thead') );\n\t\n\t\t/* Adjust the position of the header in case we loose the y-scrollbar */\n\t\tdivBody.trigger('scroll');\n\t\n\t\t// If sorting or filtering has occurred, jump the scrolling back to the top\n\t\t// only if we aren't holding the position\n\t\tif ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {\n\t\t\tdivBodyEl.scrollTop = 0;\n\t\t}\n\t}\n\t\n\t\n\t\n\t/**\n\t * Apply a given function to the display child nodes of an element array (typically\n\t * TD children of TR rows\n\t *  @param {function} fn Method to apply to the objects\n\t *  @param array {nodes} an1 List of elements to look through for display children\n\t *  @param array {nodes} an2 Another list (identical structure to the first) - optional\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnApplyToChildren( fn, an1, an2 )\n\t{\n\t\tvar index=0, i=0, iLen=an1.length;\n\t\tvar nNode1, nNode2;\n\t\n\t\twhile ( i < iLen ) {\n\t\t\tnNode1 = an1[i].firstChild;\n\t\t\tnNode2 = an2 ? an2[i].firstChild : null;\n\t\n\t\t\twhile ( nNode1 ) {\n\t\t\t\tif ( nNode1.nodeType === 1 ) {\n\t\t\t\t\tif ( an2 ) {\n\t\t\t\t\t\tfn( nNode1, nNode2, index );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tfn( nNode1, index );\n\t\t\t\t\t}\n\t\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\n\t\t\t\tnNode1 = nNode1.nextSibling;\n\t\t\t\tnNode2 = an2 ? nNode2.nextSibling : null;\n\t\t\t}\n\t\n\t\t\ti++;\n\t\t}\n\t}\n\t\n\t\n\t\n\tvar __re_html_remove = /<.*?>/g;\n\t\n\t\n\t/**\n\t * Calculate the width of columns for the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCalculateColumnWidths ( oSettings )\n\t{\n\t\tvar\n\t\t\ttable = oSettings.nTable,\n\t\t\tcolumns = oSettings.aoColumns,\n\t\t\tscroll = oSettings.oScroll,\n\t\t\tscrollY = scroll.sY,\n\t\t\tscrollX = scroll.sX,\n\t\t\tscrollXInner = scroll.sXInner,\n\t\t\tcolumnCount = columns.length,\n\t\t\tvisibleColumns = _fnGetColumns( oSettings, 'bVisible' ),\n\t\t\theaderCells = $('th', oSettings.nTHead),\n\t\t\ttableWidthAttr = table.getAttribute('width'), // from DOM element\n\t\t\ttableContainer = table.parentNode,\n\t\t\tuserInputs = false,\n\t\t\ti, column, columnIdx, width, outerWidth,\n\t\t\tbrowser = oSettings.oBrowser,\n\t\t\tie67 = browser.bScrollOversize;\n\t\n\t\tvar styleWidth = table.style.width;\n\t\tif ( styleWidth && styleWidth.indexOf('%') !== -1 ) {\n\t\t\ttableWidthAttr = styleWidth;\n\t\t}\n\t\n\t\t/* Convert any user input sizes into pixel sizes */\n\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\tcolumn = columns[ visibleColumns[i] ];\n\t\n\t\t\tif ( column.sWidth !== null ) {\n\t\t\t\tcolumn.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );\n\t\n\t\t\t\tuserInputs = true;\n\t\t\t}\n\t\t}\n\t\n\t\t/* If the number of columns in the DOM equals the number that we have to\n\t\t * process in DataTables, then we can use the offsets that are created by\n\t\t * the web- browser. No custom sizes can be set in order for this to happen,\n\t\t * nor scrolling used\n\t\t */\n\t\tif ( ie67 || ! userInputs && ! scrollX && ! scrollY &&\n\t\t     columnCount == _fnVisbleColumns( oSettings ) &&\n\t\t     columnCount == headerCells.length\n\t\t) {\n\t\t\tfor ( i=0 ; i<columnCount ; i++ ) {\n\t\t\t\tvar colIdx = _fnVisibleToColumnIndex( oSettings, i );\n\t\n\t\t\t\tif ( colIdx !== null ) {\n\t\t\t\t\tcolumns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Otherwise construct a single row, worst case, table with the widest\n\t\t\t// node in the data, assign any user defined widths, then insert it into\n\t\t\t// the DOM and allow the browser to do all the hard work of calculating\n\t\t\t// table widths\n\t\t\tvar tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table\n\t\t\t\t.css( 'visibility', 'hidden' )\n\t\t\t\t.removeAttr( 'id' );\n\t\n\t\t\t// Clean up the table body\n\t\t\ttmpTable.find('tbody tr').remove();\n\t\t\tvar tr = $('<tr/>').appendTo( tmpTable.find('tbody') );\n\t\n\t\t\t// Clone the table header and footer - we can't use the header / footer\n\t\t\t// from the cloned table, since if scrolling is active, the table's\n\t\t\t// real header and footer are contained in different table tags\n\t\t\ttmpTable.find('thead, tfoot').remove();\n\t\t\ttmpTable\n\t\t\t\t.append( $(oSettings.nTHead).clone() )\n\t\t\t\t.append( $(oSettings.nTFoot).clone() );\n\t\n\t\t\t// Remove any assigned widths from the footer (from scrolling)\n\t\t\ttmpTable.find('tfoot th, tfoot td').css('width', '');\n\t\n\t\t\t// Apply custom sizing to the cloned header\n\t\t\theaderCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );\n\t\n\t\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\t\tcolumn = columns[ visibleColumns[i] ];\n\t\n\t\t\t\theaderCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?\n\t\t\t\t\t_fnStringToCss( column.sWidthOrig ) :\n\t\t\t\t\t'';\n\t\n\t\t\t\t// For scrollX we need to force the column width otherwise the\n\t\t\t\t// browser will collapse it. If this width is smaller than the\n\t\t\t\t// width the column requires, then it will have no effect\n\t\t\t\tif ( column.sWidthOrig && scrollX ) {\n\t\t\t\t\t$( headerCells[i] ).append( $('<div/>').css( {\n\t\t\t\t\t\twidth: column.sWidthOrig,\n\t\t\t\t\t\tmargin: 0,\n\t\t\t\t\t\tpadding: 0,\n\t\t\t\t\t\tborder: 0,\n\t\t\t\t\t\theight: 1\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Find the widest cell for each column and put it into the table\n\t\t\tif ( oSettings.aoData.length ) {\n\t\t\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\t\t\tcolumnIdx = visibleColumns[i];\n\t\t\t\t\tcolumn = columns[ columnIdx ];\n\t\n\t\t\t\t\t$( _fnGetWidestNode( oSettings, columnIdx ) )\n\t\t\t\t\t\t.clone( false )\n\t\t\t\t\t\t.append( column.sContentPadding )\n\t\t\t\t\t\t.appendTo( tr );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Tidy the temporary table - remove name attributes so there aren't\n\t\t\t// duplicated in the dom (radio elements for example)\n\t\t\t$('[name]', tmpTable).removeAttr('name');\n\t\n\t\t\t// Table has been built, attach to the document so we can work with it.\n\t\t\t// A holding element is used, positioned at the top of the container\n\t\t\t// with minimal height, so it has no effect on if the container scrolls\n\t\t\t// or not. Otherwise it might trigger scrolling when it actually isn't\n\t\t\t// needed\n\t\t\tvar holder = $('<div/>').css( scrollX || scrollY ?\n\t\t\t\t\t{\n\t\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\theight: 1,\n\t\t\t\t\t\tright: 0,\n\t\t\t\t\t\toverflow: 'hidden'\n\t\t\t\t\t} :\n\t\t\t\t\t{}\n\t\t\t\t)\n\t\t\t\t.append( tmpTable )\n\t\t\t\t.appendTo( tableContainer );\n\t\n\t\t\t// When scrolling (X or Y) we want to set the width of the table as \n\t\t\t// appropriate. However, when not scrolling leave the table width as it\n\t\t\t// is. This results in slightly different, but I think correct behaviour\n\t\t\tif ( scrollX && scrollXInner ) {\n\t\t\t\ttmpTable.width( scrollXInner );\n\t\t\t}\n\t\t\telse if ( scrollX ) {\n\t\t\t\ttmpTable.css( 'width', 'auto' );\n\t\t\t\ttmpTable.removeAttr('width');\n\t\n\t\t\t\t// If there is no width attribute or style, then allow the table to\n\t\t\t\t// collapse\n\t\t\t\tif ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {\n\t\t\t\t\ttmpTable.width( tableContainer.clientWidth );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( scrollY ) {\n\t\t\t\ttmpTable.width( tableContainer.clientWidth );\n\t\t\t}\n\t\t\telse if ( tableWidthAttr ) {\n\t\t\t\ttmpTable.width( tableWidthAttr );\n\t\t\t}\n\t\n\t\t\t// Get the width of each column in the constructed table - we need to\n\t\t\t// know the inner width (so it can be assigned to the other table's\n\t\t\t// cells) and the outer width so we can calculate the full width of the\n\t\t\t// table. This is safe since DataTables requires a unique cell for each\n\t\t\t// column, but if ever a header can span multiple columns, this will\n\t\t\t// need to be modified.\n\t\t\tvar total = 0;\n\t\t\tfor ( i=0 ; i<visibleColumns.length ; i++ ) {\n\t\t\t\tvar cell = $(headerCells[i]);\n\t\t\t\tvar border = cell.outerWidth() - cell.width();\n\t\n\t\t\t\t// Use getBounding... where possible (not IE8-) because it can give\n\t\t\t\t// sub-pixel accuracy, which we then want to round up!\n\t\t\t\tvar bounding = browser.bBounding ?\n\t\t\t\t\tMath.ceil( headerCells[i].getBoundingClientRect().width ) :\n\t\t\t\t\tcell.outerWidth();\n\t\n\t\t\t\t// Total is tracked to remove any sub-pixel errors as the outerWidth\n\t\t\t\t// of the table might not equal the total given here (IE!).\n\t\t\t\ttotal += bounding;\n\t\n\t\t\t\t// Width for each column to use\n\t\t\t\tcolumns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );\n\t\t\t}\n\t\n\t\t\ttable.style.width = _fnStringToCss( total );\n\t\n\t\t\t// Finished with the table - ditch it\n\t\t\tholder.remove();\n\t\t}\n\t\n\t\t// If there is a width attr, we want to attach an event listener which\n\t\t// allows the table sizing to automatically adjust when the window is\n\t\t// resized. Use the width attr rather than CSS, since we can't know if the\n\t\t// CSS is a relative value or absolute - DOM read is always px.\n\t\tif ( tableWidthAttr ) {\n\t\t\ttable.style.width = _fnStringToCss( tableWidthAttr );\n\t\t}\n\t\n\t\tif ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {\n\t\t\tvar bindResize = function () {\n\t\t\t\t$(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {\n\t\t\t\t\t_fnAdjustColumnSizing( oSettings );\n\t\t\t\t} ) );\n\t\t\t};\n\t\n\t\t\t// IE6/7 will crash if we bind a resize event handler on page load.\n\t\t\t// To be removed in 1.11 which drops IE6/7 support\n\t\t\tif ( ie67 ) {\n\t\t\t\tsetTimeout( bindResize, 1000 );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindResize();\n\t\t\t}\n\t\n\t\t\toSettings._reszEvt = true;\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Throttle the calls to a function. Arguments and context are maintained for\n\t * the throttled function\n\t *  @param {function} fn Function to be called\n\t *  @param {int} [freq=200] call frequency in mS\n\t *  @returns {function} wrapped function\n\t *  @memberof DataTable#oApi\n\t */\n\tvar _fnThrottle = DataTable.util.throttle;\n\t\n\t\n\t/**\n\t * Convert a CSS unit width to pixels (e.g. 2em)\n\t *  @param {string} width width to be converted\n\t *  @param {node} parent parent to get the with for (required for relative widths) - optional\n\t *  @returns {int} width in pixels\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnConvertToWidth ( width, parent )\n\t{\n\t\tif ( ! width ) {\n\t\t\treturn 0;\n\t\t}\n\t\n\t\tvar n = $('<div/>')\n\t\t\t.css( 'width', _fnStringToCss( width ) )\n\t\t\t.appendTo( parent || document.body );\n\t\n\t\tvar val = n[0].offsetWidth;\n\t\tn.remove();\n\t\n\t\treturn val;\n\t}\n\t\n\t\n\t/**\n\t * Get the widest node\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} colIdx column of interest\n\t *  @returns {node} widest table node\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetWidestNode( settings, colIdx )\n\t{\n\t\tvar idx = _fnGetMaxLenString( settings, colIdx );\n\t\tif ( idx < 0 ) {\n\t\t\treturn null;\n\t\t}\n\t\n\t\tvar data = settings.aoData[ idx ];\n\t\treturn ! data.nTr ? // Might not have been created when deferred rendering\n\t\t\t$('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :\n\t\t\tdata.anCells[ colIdx ];\n\t}\n\t\n\t\n\t/**\n\t * Get the maximum strlen for each data column\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} colIdx column of interest\n\t *  @returns {string} max string length for each column\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnGetMaxLenString( settings, colIdx )\n\t{\n\t\tvar s, max=-1, maxIdx = -1;\n\t\n\t\tfor ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\ts = _fnGetCellData( settings, i, colIdx, 'display' )+'';\n\t\t\ts = s.replace( __re_html_remove, '' );\n\t\t\ts = s.replace( /&nbsp;/g, ' ' );\n\t\n\t\t\tif ( s.length > max ) {\n\t\t\t\tmax = s.length;\n\t\t\t\tmaxIdx = i;\n\t\t\t}\n\t\t}\n\t\n\t\treturn maxIdx;\n\t}\n\t\n\t\n\t/**\n\t * Append a CSS unit (only if required) to a string\n\t *  @param {string} value to css-ify\n\t *  @returns {string} value with css unit\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnStringToCss( s )\n\t{\n\t\tif ( s === null ) {\n\t\t\treturn '0px';\n\t\t}\n\t\n\t\tif ( typeof s == 'number' ) {\n\t\t\treturn s < 0 ?\n\t\t\t\t'0px' :\n\t\t\t\ts+'px';\n\t\t}\n\t\n\t\t// Check it has a unit character already\n\t\treturn s.match(/\\d$/) ?\n\t\t\ts+'px' :\n\t\t\ts;\n\t}\n\t\n\t\n\t\n\tfunction _fnSortFlatten ( settings )\n\t{\n\t\tvar\n\t\t\ti, iLen, k, kLen,\n\t\t\taSort = [],\n\t\t\taiOrig = [],\n\t\t\taoColumns = settings.aoColumns,\n\t\t\taDataSort, iCol, sType, srcCol,\n\t\t\tfixed = settings.aaSortingFixed,\n\t\t\tfixedObj = $.isPlainObject( fixed ),\n\t\t\tnestedSort = [],\n\t\t\tadd = function ( a ) {\n\t\t\t\tif ( a.length && ! Array.isArray( a[0] ) ) {\n\t\t\t\t\t// 1D array\n\t\t\t\t\tnestedSort.push( a );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// 2D array\n\t\t\t\t\t$.merge( nestedSort, a );\n\t\t\t\t}\n\t\t\t};\n\t\n\t\t// Build the sort array, with pre-fix and post-fix options if they have been\n\t\t// specified\n\t\tif ( Array.isArray( fixed ) ) {\n\t\t\tadd( fixed );\n\t\t}\n\t\n\t\tif ( fixedObj && fixed.pre ) {\n\t\t\tadd( fixed.pre );\n\t\t}\n\t\n\t\tadd( settings.aaSorting );\n\t\n\t\tif (fixedObj && fixed.post ) {\n\t\t\tadd( fixed.post );\n\t\t}\n\t\n\t\tfor ( i=0 ; i<nestedSort.length ; i++ )\n\t\t{\n\t\t\tsrcCol = nestedSort[i][0];\n\t\t\taDataSort = aoColumns[ srcCol ].aDataSort;\n\t\n\t\t\tfor ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )\n\t\t\t{\n\t\t\t\tiCol = aDataSort[k];\n\t\t\t\tsType = aoColumns[ iCol ].sType || 'string';\n\t\n\t\t\t\tif ( nestedSort[i]._idx === undefined ) {\n\t\t\t\t\tnestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );\n\t\t\t\t}\n\t\n\t\t\t\taSort.push( {\n\t\t\t\t\tsrc:       srcCol,\n\t\t\t\t\tcol:       iCol,\n\t\t\t\t\tdir:       nestedSort[i][1],\n\t\t\t\t\tindex:     nestedSort[i]._idx,\n\t\t\t\t\ttype:      sType,\n\t\t\t\t\tformatter: DataTable.ext.type.order[ sType+\"-pre\" ]\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t\n\t\treturn aSort;\n\t}\n\t\n\t/**\n\t * Change the order of the table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t *  @todo This really needs split up!\n\t */\n\tfunction _fnSort ( oSettings )\n\t{\n\t\tvar\n\t\t\ti, ien, iLen, j, jLen, k, kLen,\n\t\t\tsDataType, nTh,\n\t\t\taiOrig = [],\n\t\t\toExtSort = DataTable.ext.type.order,\n\t\t\taoData = oSettings.aoData,\n\t\t\taoColumns = oSettings.aoColumns,\n\t\t\taDataSort, data, iCol, sType, oSort,\n\t\t\tformatters = 0,\n\t\t\tsortCol,\n\t\t\tdisplayMaster = oSettings.aiDisplayMaster,\n\t\t\taSort;\n\t\n\t\t// Resolve any column types that are unknown due to addition or invalidation\n\t\t// @todo Can this be moved into a 'data-ready' handler which is called when\n\t\t//   data is going to be used in the table?\n\t\t_fnColumnTypes( oSettings );\n\t\n\t\taSort = _fnSortFlatten( oSettings );\n\t\n\t\tfor ( i=0, ien=aSort.length ; i<ien ; i++ ) {\n\t\t\tsortCol = aSort[i];\n\t\n\t\t\t// Track if we can use the fast sort algorithm\n\t\t\tif ( sortCol.formatter ) {\n\t\t\t\tformatters++;\n\t\t\t}\n\t\n\t\t\t// Load the data needed for the sort, for each cell\n\t\t\t_fnSortData( oSettings, sortCol.col );\n\t\t}\n\t\n\t\t/* No sorting required if server-side or no sorting array */\n\t\tif ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )\n\t\t{\n\t\t\t// Create a value - key array of the current row positions such that we can use their\n\t\t\t// current position during the sort, if values match, in order to perform stable sorting\n\t\t\tfor ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {\n\t\t\t\taiOrig[ displayMaster[i] ] = i;\n\t\t\t}\n\t\n\t\t\t/* Do the sort - here we want multi-column sorting based on a given data source (column)\n\t\t\t * and sorting function (from oSort) in a certain direction. It's reasonably complex to\n\t\t\t * follow on it's own, but this is what we want (example two column sorting):\n\t\t\t *  fnLocalSorting = function(a,b){\n\t\t\t *    var iTest;\n\t\t\t *    iTest = oSort['string-asc']('data11', 'data12');\n\t\t\t *      if (iTest !== 0)\n\t\t\t *        return iTest;\n\t\t\t *    iTest = oSort['numeric-desc']('data21', 'data22');\n\t\t\t *    if (iTest !== 0)\n\t\t\t *      return iTest;\n\t\t\t *    return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );\n\t\t\t *  }\n\t\t\t * Basically we have a test for each sorting column, if the data in that column is equal,\n\t\t\t * test the next column. If all columns match, then we use a numeric sort on the row\n\t\t\t * positions in the original data array to provide a stable sort.\n\t\t\t *\n\t\t\t * Note - I know it seems excessive to have two sorting methods, but the first is around\n\t\t\t * 15% faster, so the second is only maintained for backwards compatibility with sorting\n\t\t\t * methods which do not have a pre-sort formatting function.\n\t\t\t */\n\t\t\tif ( formatters === aSort.length ) {\n\t\t\t\t// All sort types have formatting functions\n\t\t\t\tdisplayMaster.sort( function ( a, b ) {\n\t\t\t\t\tvar\n\t\t\t\t\t\tx, y, k, test, sort,\n\t\t\t\t\t\tlen=aSort.length,\n\t\t\t\t\t\tdataA = aoData[a]._aSortData,\n\t\t\t\t\t\tdataB = aoData[b]._aSortData;\n\t\n\t\t\t\t\tfor ( k=0 ; k<len ; k++ ) {\n\t\t\t\t\t\tsort = aSort[k];\n\t\n\t\t\t\t\t\tx = dataA[ sort.col ];\n\t\t\t\t\t\ty = dataB[ sort.col ];\n\t\n\t\t\t\t\t\ttest = x<y ? -1 : x>y ? 1 : 0;\n\t\t\t\t\t\tif ( test !== 0 ) {\n\t\t\t\t\t\t\treturn sort.dir === 'asc' ? test : -test;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\tx = aiOrig[a];\n\t\t\t\t\ty = aiOrig[b];\n\t\t\t\t\treturn x<y ? -1 : x>y ? 1 : 0;\n\t\t\t\t} );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Depreciated - remove in 1.11 (providing a plug-in option)\n\t\t\t\t// Not all sort types have formatting methods, so we have to call their sorting\n\t\t\t\t// methods.\n\t\t\t\tdisplayMaster.sort( function ( a, b ) {\n\t\t\t\t\tvar\n\t\t\t\t\t\tx, y, k, l, test, sort, fn,\n\t\t\t\t\t\tlen=aSort.length,\n\t\t\t\t\t\tdataA = aoData[a]._aSortData,\n\t\t\t\t\t\tdataB = aoData[b]._aSortData;\n\t\n\t\t\t\t\tfor ( k=0 ; k<len ; k++ ) {\n\t\t\t\t\t\tsort = aSort[k];\n\t\n\t\t\t\t\t\tx = dataA[ sort.col ];\n\t\t\t\t\t\ty = dataB[ sort.col ];\n\t\n\t\t\t\t\t\tfn = oExtSort[ sort.type+\"-\"+sort.dir ] || oExtSort[ \"string-\"+sort.dir ];\n\t\t\t\t\t\ttest = fn( x, y );\n\t\t\t\t\t\tif ( test !== 0 ) {\n\t\t\t\t\t\t\treturn test;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\tx = aiOrig[a];\n\t\t\t\t\ty = aiOrig[b];\n\t\t\t\t\treturn x<y ? -1 : x>y ? 1 : 0;\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t\n\t\t/* Tell the draw function that we have sorted the data */\n\t\toSettings.bSorted = true;\n\t}\n\t\n\t\n\tfunction _fnSortAria ( settings )\n\t{\n\t\tvar label;\n\t\tvar nextSort;\n\t\tvar columns = settings.aoColumns;\n\t\tvar aSort = _fnSortFlatten( settings );\n\t\tvar oAria = settings.oLanguage.oAria;\n\t\n\t\t// ARIA attributes - need to loop all columns, to update all (removing old\n\t\t// attributes as needed)\n\t\tfor ( var i=0, iLen=columns.length ; i<iLen ; i++ )\n\t\t{\n\t\t\tvar col = columns[i];\n\t\t\tvar asSorting = col.asSorting;\n\t\t\tvar sTitle = col.ariaTitle || col.sTitle.replace( /<.*?>/g, \"\" );\n\t\t\tvar th = col.nTh;\n\t\n\t\t\t// IE7 is throwing an error when setting these properties with jQuery's\n\t\t\t// attr() and removeAttr() methods...\n\t\t\tth.removeAttribute('aria-sort');\n\t\n\t\t\t/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */\n\t\t\tif ( col.bSortable ) {\n\t\t\t\tif ( aSort.length > 0 && aSort[0].col == i ) {\n\t\t\t\t\tth.setAttribute('aria-sort', aSort[0].dir==\"asc\" ? \"ascending\" : \"descending\" );\n\t\t\t\t\tnextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tnextSort = asSorting[0];\n\t\t\t\t}\n\t\n\t\t\t\tlabel = sTitle + ( nextSort === \"asc\" ?\n\t\t\t\t\toAria.sSortAscending :\n\t\t\t\t\toAria.sSortDescending\n\t\t\t\t);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlabel = sTitle;\n\t\t\t}\n\t\n\t\t\tth.setAttribute('aria-label', label);\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Function to run on user sort request\n\t *  @param {object} settings dataTables settings object\n\t *  @param {node} attachTo node to attach the handler to\n\t *  @param {int} colIdx column sorting index\n\t *  @param {boolean} [append=false] Append the requested sort to the existing\n\t *    sort if true (i.e. multi-column sort)\n\t *  @param {function} [callback] callback function\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSortListener ( settings, colIdx, append, callback )\n\t{\n\t\tvar col = settings.aoColumns[ colIdx ];\n\t\tvar sorting = settings.aaSorting;\n\t\tvar asSorting = col.asSorting;\n\t\tvar nextSortIdx;\n\t\tvar next = function ( a, overflow ) {\n\t\t\tvar idx = a._idx;\n\t\t\tif ( idx === undefined ) {\n\t\t\t\tidx = $.inArray( a[1], asSorting );\n\t\t\t}\n\t\n\t\t\treturn idx+1 < asSorting.length ?\n\t\t\t\tidx+1 :\n\t\t\t\toverflow ?\n\t\t\t\t\tnull :\n\t\t\t\t\t0;\n\t\t};\n\t\n\t\t// Convert to 2D array if needed\n\t\tif ( typeof sorting[0] === 'number' ) {\n\t\t\tsorting = settings.aaSorting = [ sorting ];\n\t\t}\n\t\n\t\t// If appending the sort then we are multi-column sorting\n\t\tif ( append && settings.oFeatures.bSortMulti ) {\n\t\t\t// Are we already doing some kind of sort on this column?\n\t\t\tvar sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );\n\t\n\t\t\tif ( sortIdx !== -1 ) {\n\t\t\t\t// Yes, modify the sort\n\t\t\t\tnextSortIdx = next( sorting[sortIdx], true );\n\t\n\t\t\t\tif ( nextSortIdx === null && sorting.length === 1 ) {\n\t\t\t\t\tnextSortIdx = 0; // can't remove sorting completely\n\t\t\t\t}\n\t\n\t\t\t\tif ( nextSortIdx === null ) {\n\t\t\t\t\tsorting.splice( sortIdx, 1 );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tsorting[sortIdx][1] = asSorting[ nextSortIdx ];\n\t\t\t\t\tsorting[sortIdx]._idx = nextSortIdx;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// No sort on this column yet\n\t\t\t\tsorting.push( [ colIdx, asSorting[0], 0 ] );\n\t\t\t\tsorting[sorting.length-1]._idx = 0;\n\t\t\t}\n\t\t}\n\t\telse if ( sorting.length && sorting[0][0] == colIdx ) {\n\t\t\t// Single column - already sorting on this column, modify the sort\n\t\t\tnextSortIdx = next( sorting[0] );\n\t\n\t\t\tsorting.length = 1;\n\t\t\tsorting[0][1] = asSorting[ nextSortIdx ];\n\t\t\tsorting[0]._idx = nextSortIdx;\n\t\t}\n\t\telse {\n\t\t\t// Single column - sort only on this column\n\t\t\tsorting.length = 0;\n\t\t\tsorting.push( [ colIdx, asSorting[0] ] );\n\t\t\tsorting[0]._idx = 0;\n\t\t}\n\t\n\t\t// Run the sort by calling a full redraw\n\t\t_fnReDraw( settings );\n\t\n\t\t// callback used for async user interaction\n\t\tif ( typeof callback == 'function' ) {\n\t\t\tcallback( settings );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Attach a sort handler (click) to a node\n\t *  @param {object} settings dataTables settings object\n\t *  @param {node} attachTo node to attach the handler to\n\t *  @param {int} colIdx column sorting index\n\t *  @param {function} [callback] callback function\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSortAttachListener ( settings, attachTo, colIdx, callback )\n\t{\n\t\tvar col = settings.aoColumns[ colIdx ];\n\t\n\t\t_fnBindAction( attachTo, {}, function (e) {\n\t\t\t/* If the column is not sortable - don't to anything */\n\t\t\tif ( col.bSortable === false ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// If processing is enabled use a timeout to allow the processing\n\t\t\t// display to be shown - otherwise to it synchronously\n\t\t\tif ( settings.oFeatures.bProcessing ) {\n\t\t\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t\t\tsetTimeout( function() {\n\t\t\t\t\t_fnSortListener( settings, colIdx, e.shiftKey, callback );\n\t\n\t\t\t\t\t// In server-side processing, the draw callback will remove the\n\t\t\t\t\t// processing display\n\t\t\t\t\tif ( _fnDataSource( settings ) !== 'ssp' ) {\n\t\t\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t\t\t}\n\t\t\t\t}, 0 );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnSortListener( settings, colIdx, e.shiftKey, callback );\n\t\t\t}\n\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Set the sorting classes on table's body, Note: it is safe to call this function\n\t * when bSort and bSortClasses are false\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSortingClasses( settings )\n\t{\n\t\tvar oldSort = settings.aLastSort;\n\t\tvar sortClass = settings.oClasses.sSortColumn;\n\t\tvar sort = _fnSortFlatten( settings );\n\t\tvar features = settings.oFeatures;\n\t\tvar i, ien, colIdx;\n\t\n\t\tif ( features.bSort && features.bSortClasses ) {\n\t\t\t// Remove old sorting classes\n\t\t\tfor ( i=0, ien=oldSort.length ; i<ien ; i++ ) {\n\t\t\t\tcolIdx = oldSort[i].src;\n\t\n\t\t\t\t// Remove column sorting\n\t\t\t\t$( _pluck( settings.aoData, 'anCells', colIdx ) )\n\t\t\t\t\t.removeClass( sortClass + (i<2 ? i+1 : 3) );\n\t\t\t}\n\t\n\t\t\t// Add new column sorting\n\t\t\tfor ( i=0, ien=sort.length ; i<ien ; i++ ) {\n\t\t\t\tcolIdx = sort[i].src;\n\t\n\t\t\t\t$( _pluck( settings.aoData, 'anCells', colIdx ) )\n\t\t\t\t\t.addClass( sortClass + (i<2 ? i+1 : 3) );\n\t\t\t}\n\t\t}\n\t\n\t\tsettings.aLastSort = sort;\n\t}\n\t\n\t\n\t// Get the data to sort a column, be it from cache, fresh (populating the\n\t// cache), or from a sort formatter\n\tfunction _fnSortData( settings, idx )\n\t{\n\t\t// Custom sorting function - provided by the sort data type\n\t\tvar column = settings.aoColumns[ idx ];\n\t\tvar customSort = DataTable.ext.order[ column.sSortDataType ];\n\t\tvar customData;\n\t\n\t\tif ( customSort ) {\n\t\t\tcustomData = customSort.call( settings.oInstance, settings, idx,\n\t\t\t\t_fnColumnIndexToVisible( settings, idx )\n\t\t\t);\n\t\t}\n\t\n\t\t// Use / populate cache\n\t\tvar row, cellData;\n\t\tvar formatter = DataTable.ext.type.order[ column.sType+\"-pre\" ];\n\t\n\t\tfor ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\trow = settings.aoData[i];\n\t\n\t\t\tif ( ! row._aSortData ) {\n\t\t\t\trow._aSortData = [];\n\t\t\t}\n\t\n\t\t\tif ( ! row._aSortData[idx] || customSort ) {\n\t\t\t\tcellData = customSort ?\n\t\t\t\t\tcustomData[i] : // If there was a custom sort function, use data from there\n\t\t\t\t\t_fnGetCellData( settings, i, idx, 'sort' );\n\t\n\t\t\t\trow._aSortData[ idx ] = formatter ?\n\t\t\t\t\tformatter( cellData ) :\n\t\t\t\t\tcellData;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t\n\t/**\n\t * Save the state of a table\n\t *  @param {object} oSettings dataTables settings object\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSaveState ( settings )\n\t{\n\t\tif (settings._bLoadingState) {\n\t\t\treturn;\n\t\t}\n\t\n\t\t/* Store the interesting variables */\n\t\tvar state = {\n\t\t\ttime:    +new Date(),\n\t\t\tstart:   settings._iDisplayStart,\n\t\t\tlength:  settings._iDisplayLength,\n\t\t\torder:   $.extend( true, [], settings.aaSorting ),\n\t\t\tsearch:  _fnSearchToCamel( settings.oPreviousSearch ),\n\t\t\tcolumns: $.map( settings.aoColumns, function ( col, i ) {\n\t\t\t\treturn {\n\t\t\t\t\tvisible: col.bVisible,\n\t\t\t\t\tsearch: _fnSearchToCamel( settings.aoPreSearchCols[i] )\n\t\t\t\t};\n\t\t\t} )\n\t\t};\n\t\n\t\tsettings.oSavedState = state;\n\t\t_fnCallbackFire( settings, \"aoStateSaveParams\", 'stateSaveParams', [settings, state] );\n\t\t\n\t\tif ( settings.oFeatures.bStateSave && !settings.bDestroying )\n\t\t{\n\t\t\tsettings.fnStateSaveCallback.call( settings.oInstance, settings, state );\n\t\t}\t\n\t}\n\t\n\t\n\t/**\n\t * Attempt to load a saved table state\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {object} oInit DataTables init object so we can override settings\n\t *  @param {function} callback Callback to execute when the state has been loaded\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnLoadState ( settings, oInit, callback )\n\t{\n\t\tif ( ! settings.oFeatures.bStateSave ) {\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar loaded = function(state) {\n\t\t\t_fnImplementState(settings, state, callback);\n\t\t}\n\t\n\t\tvar state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );\n\t\n\t\tif ( state !== undefined ) {\n\t\t\t_fnImplementState( settings, state, callback );\n\t\t}\n\t\t// otherwise, wait for the loaded callback to be executed\n\t\n\t\treturn true;\n\t}\n\t\n\tfunction _fnImplementState ( settings, s, callback) {\n\t\tvar i, ien;\n\t\tvar columns = settings.aoColumns;\n\t\tsettings._bLoadingState = true;\n\t\n\t\t// When StateRestore was introduced the state could now be implemented at any time\n\t\t// Not just initialisation. To do this an api instance is required in some places\n\t\tvar api = settings._bInitComplete ? new DataTable.Api(settings) : null;\n\t\n\t\tif ( ! s || ! s.time ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Allow custom and plug-in manipulation functions to alter the saved data set and\n\t\t// cancelling of loading by returning false\n\t\tvar abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );\n\t\tif ( $.inArray( false, abStateLoad ) !== -1 ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Reject old data\n\t\tvar duration = settings.iStateDuration;\n\t\tif ( duration > 0 && s.time < +new Date() - (duration*1000) ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Number of columns have changed - all bets are off, no restore of settings\n\t\tif ( s.columns && columns.length !== s.columns.length ) {\n\t\t\tsettings._bLoadingState = false;\n\t\t\tcallback();\n\t\t\treturn;\n\t\t}\n\t\n\t\t// Store the saved state so it might be accessed at any time\n\t\tsettings.oLoadedState = $.extend( true, {}, s );\n\t\n\t\t// Page Length\n\t\tif ( s.length !== undefined ) {\n\t\t\t// If already initialised just set the value directly so that the select element is also updated\n\t\t\tif (api) {\n\t\t\t\tapi.page.len(s.length)\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsettings._iDisplayLength   = s.length;\n\t\t\t}\n\t\t}\n\t\n\t\t// Restore key features - todo - for 1.11 this needs to be done by\n\t\t// subscribed events\n\t\tif ( s.start !== undefined ) {\n\t\t\tif(api === null) {\n\t\t\t\tsettings._iDisplayStart    = s.start;\n\t\t\t\tsettings.iInitDisplayStart = s.start;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_fnPageChange(settings, s.start/settings._iDisplayLength);\n\t\t\t}\n\t\t}\n\t\n\t\t// Order\n\t\tif ( s.order !== undefined ) {\n\t\t\tsettings.aaSorting = [];\n\t\t\t$.each( s.order, function ( i, col ) {\n\t\t\t\tsettings.aaSorting.push( col[0] >= columns.length ?\n\t\t\t\t\t[ 0, col[1] ] :\n\t\t\t\t\tcol\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\t\n\t\t// Search\n\t\tif ( s.search !== undefined ) {\n\t\t\t$.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );\n\t\t}\n\t\n\t\t// Columns\n\t\tif ( s.columns ) {\n\t\t\tfor ( i=0, ien=s.columns.length ; i<ien ; i++ ) {\n\t\t\t\tvar col = s.columns[i];\n\t\n\t\t\t\t// Visibility\n\t\t\t\tif ( col.visible !== undefined ) {\n\t\t\t\t\t// If the api is defined, the table has been initialised so we need to use it rather than internal settings\n\t\t\t\t\tif (api) {\n\t\t\t\t\t\t// Don't redraw the columns on every iteration of this loop, we will do this at the end instead\n\t\t\t\t\t\tapi.column(i).visible(col.visible, false);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tcolumns[i].bVisible = col.visible;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Search\n\t\t\t\tif ( col.search !== undefined ) {\n\t\t\t\t\t$.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// If the api is defined then we need to adjust the columns once the visibility has been changed\n\t\t\tif (api) {\n\t\t\t\tapi.columns.adjust();\n\t\t\t}\n\t\t}\n\t\n\t\tsettings._bLoadingState = false;\n\t\t_fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );\n\t\tcallback();\n\t};\n\t\n\t\n\t/**\n\t * Return the settings object for a particular table\n\t *  @param {node} table table we are using as a dataTable\n\t *  @returns {object} Settings object - or null if not found\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnSettingsFromNode ( table )\n\t{\n\t\tvar settings = DataTable.settings;\n\t\tvar idx = $.inArray( table, _pluck( settings, 'nTable' ) );\n\t\n\t\treturn idx !== -1 ?\n\t\t\tsettings[ idx ] :\n\t\t\tnull;\n\t}\n\t\n\t\n\t/**\n\t * Log an error message\n\t *  @param {object} settings dataTables settings object\n\t *  @param {int} level log error messages, or display them to the user\n\t *  @param {string} msg error message\n\t *  @param {int} tn Technical note id to get more information about the error.\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnLog( settings, level, msg, tn )\n\t{\n\t\tmsg = 'DataTables warning: '+\n\t\t\t(settings ? 'table id='+settings.sTableId+' - ' : '')+msg;\n\t\n\t\tif ( tn ) {\n\t\t\tmsg += '. For more information about this error, please see '+\n\t\t\t'http://datatables.net/tn/'+tn;\n\t\t}\n\t\n\t\tif ( ! level  ) {\n\t\t\t// Backwards compatibility pre 1.10\n\t\t\tvar ext = DataTable.ext;\n\t\t\tvar type = ext.sErrMode || ext.errMode;\n\t\n\t\t\tif ( settings ) {\n\t\t\t\t_fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );\n\t\t\t}\n\t\n\t\t\tif ( type == 'alert' ) {\n\t\t\t\talert( msg );\n\t\t\t}\n\t\t\telse if ( type == 'throw' ) {\n\t\t\t\tthrow new Error(msg);\n\t\t\t}\n\t\t\telse if ( typeof type == 'function' ) {\n\t\t\t\ttype( settings, tn, msg );\n\t\t\t}\n\t\t}\n\t\telse if ( window.console && console.log ) {\n\t\t\tconsole.log( msg );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * See if a property is defined on one object, if so assign it to the other object\n\t *  @param {object} ret target object\n\t *  @param {object} src source object\n\t *  @param {string} name property\n\t *  @param {string} [mappedName] name to map too - optional, name used if not given\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnMap( ret, src, name, mappedName )\n\t{\n\t\tif ( Array.isArray( name ) ) {\n\t\t\t$.each( name, function (i, val) {\n\t\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\t\t_fnMap( ret, src, val[0], val[1] );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_fnMap( ret, src, val );\n\t\t\t\t}\n\t\t\t} );\n\t\n\t\t\treturn;\n\t\t}\n\t\n\t\tif ( mappedName === undefined ) {\n\t\t\tmappedName = name;\n\t\t}\n\t\n\t\tif ( src[name] !== undefined ) {\n\t\t\tret[mappedName] = src[name];\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Extend objects - very similar to jQuery.extend, but deep copy objects, and\n\t * shallow copy arrays. The reason we need to do this, is that we don't want to\n\t * deep copy array init values (such as aaSorting) since the dev wouldn't be\n\t * able to override them, but we do want to deep copy arrays.\n\t *  @param {object} out Object to extend\n\t *  @param {object} extender Object from which the properties will be applied to\n\t *      out\n\t *  @param {boolean} breakRefs If true, then arrays will be sliced to take an\n\t *      independent copy with the exception of the `data` or `aaData` parameters\n\t *      if they are present. This is so you can pass in a collection to\n\t *      DataTables and have that used as your data source without breaking the\n\t *      references\n\t *  @returns {object} out Reference, just for convenience - out === the return.\n\t *  @memberof DataTable#oApi\n\t *  @todo This doesn't take account of arrays inside the deep copied objects.\n\t */\n\tfunction _fnExtend( out, extender, breakRefs )\n\t{\n\t\tvar val;\n\t\n\t\tfor ( var prop in extender ) {\n\t\t\tif ( extender.hasOwnProperty(prop) ) {\n\t\t\t\tval = extender[prop];\n\t\n\t\t\t\tif ( $.isPlainObject( val ) ) {\n\t\t\t\t\tif ( ! $.isPlainObject( out[prop] ) ) {\n\t\t\t\t\t\tout[prop] = {};\n\t\t\t\t\t}\n\t\t\t\t\t$.extend( true, out[prop], val );\n\t\t\t\t}\n\t\t\t\telse if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) {\n\t\t\t\t\tout[prop] = val.slice();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tout[prop] = val;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn out;\n\t}\n\t\n\t\n\t/**\n\t * Bind an event handers to allow a click or return key to activate the callback.\n\t * This is good for accessibility since a return on the keyboard will have the\n\t * same effect as a click, if the element has focus.\n\t *  @param {element} n Element to bind the action to\n\t *  @param {object} oData Data object to pass to the triggered function\n\t *  @param {function} fn Callback function for when the event is triggered\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnBindAction( n, oData, fn )\n\t{\n\t\t$(n)\n\t\t\t.on( 'click.DT', oData, function (e) {\n\t\t\t\t\t$(n).trigger('blur'); // Remove focus outline for mouse users\n\t\t\t\t\tfn(e);\n\t\t\t\t} )\n\t\t\t.on( 'keypress.DT', oData, function (e){\n\t\t\t\t\tif ( e.which === 13 ) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tfn(e);\n\t\t\t\t\t}\n\t\t\t\t} )\n\t\t\t.on( 'selectstart.DT', function () {\n\t\t\t\t\t/* Take the brutal approach to cancelling text selection */\n\t\t\t\t\treturn false;\n\t\t\t\t} );\n\t}\n\t\n\t\n\t/**\n\t * Register a callback function. Easily allows a callback function to be added to\n\t * an array store of callback functions that can then all be called together.\n\t *  @param {object} oSettings dataTables settings object\n\t *  @param {string} sStore Name of the array storage for the callbacks in oSettings\n\t *  @param {function} fn Function to be called back\n\t *  @param {string} sName Identifying name for the callback (i.e. a label)\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCallbackReg( oSettings, sStore, fn, sName )\n\t{\n\t\tif ( fn )\n\t\t{\n\t\t\toSettings[sStore].push( {\n\t\t\t\t\"fn\": fn,\n\t\t\t\t\"sName\": sName\n\t\t\t} );\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Fire callback functions and trigger events. Note that the loop over the\n\t * callback array store is done backwards! Further note that you do not want to\n\t * fire off triggers in time sensitive applications (for example cell creation)\n\t * as its slow.\n\t *  @param {object} settings dataTables settings object\n\t *  @param {string} callbackArr Name of the array storage for the callbacks in\n\t *      oSettings\n\t *  @param {string} eventName Name of the jQuery custom event to trigger. If\n\t *      null no trigger is fired\n\t *  @param {array} args Array of arguments to pass to the callback function /\n\t *      trigger\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnCallbackFire( settings, callbackArr, eventName, args )\n\t{\n\t\tvar ret = [];\n\t\n\t\tif ( callbackArr ) {\n\t\t\tret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {\n\t\t\t\treturn val.fn.apply( settings.oInstance, args );\n\t\t\t} );\n\t\t}\n\t\n\t\tif ( eventName !== null ) {\n\t\t\tvar e = $.Event( eventName+'.dt' );\n\t\t\tvar table = $(settings.nTable);\n\t\n\t\t\ttable.trigger( e, args );\n\t\n\t\t\t// If not yet attached to the document, trigger the event\n\t\t\t// on the body directly to sort of simulate the bubble\n\t\t\tif (table.parents('body').length === 0) {\n\t\t\t\t$('body').trigger( e, args );\n\t\t\t}\n\t\n\t\t\tret.push( e.result );\n\t\t}\n\t\n\t\treturn ret;\n\t}\n\t\n\t\n\tfunction _fnLengthOverflow ( settings )\n\t{\n\t\tvar\n\t\t\tstart = settings._iDisplayStart,\n\t\t\tend = settings.fnDisplayEnd(),\n\t\t\tlen = settings._iDisplayLength;\n\t\n\t\t/* If we have space to show extra rows (backing up from the end point - then do so */\n\t\tif ( start >= end )\n\t\t{\n\t\t\tstart = end - len;\n\t\t}\n\t\n\t\t// Keep the start record on the current page\n\t\tstart -= (start % len);\n\t\n\t\tif ( len === -1 || start < 0 )\n\t\t{\n\t\t\tstart = 0;\n\t\t}\n\t\n\t\tsettings._iDisplayStart = start;\n\t}\n\t\n\t\n\tfunction _fnRenderer( settings, type )\n\t{\n\t\tvar renderer = settings.renderer;\n\t\tvar host = DataTable.ext.renderer[type];\n\t\n\t\tif ( $.isPlainObject( renderer ) && renderer[type] ) {\n\t\t\t// Specific renderer for this type. If available use it, otherwise use\n\t\t\t// the default.\n\t\t\treturn host[renderer[type]] || host._;\n\t\t}\n\t\telse if ( typeof renderer === 'string' ) {\n\t\t\t// Common renderer - if there is one available for this type use it,\n\t\t\t// otherwise use the default\n\t\t\treturn host[renderer] || host._;\n\t\t}\n\t\n\t\t// Use the default\n\t\treturn host._;\n\t}\n\t\n\t\n\t/**\n\t * Detect the data source being used for the table. Used to simplify the code\n\t * a little (ajax) and to make it compress a little smaller.\n\t *\n\t *  @param {object} settings dataTables settings object\n\t *  @returns {string} Data source\n\t *  @memberof DataTable#oApi\n\t */\n\tfunction _fnDataSource ( settings )\n\t{\n\t\tif ( settings.oFeatures.bServerSide ) {\n\t\t\treturn 'ssp';\n\t\t}\n\t\telse if ( settings.ajax || settings.sAjaxSource ) {\n\t\t\treturn 'ajax';\n\t\t}\n\t\treturn 'dom';\n\t}\n\t\n\t\n\t\n\t\n\t/**\n\t * Computed structure of the DataTables API, defined by the options passed to\n\t * `DataTable.Api.register()` when building the API.\n\t *\n\t * The structure is built in order to speed creation and extension of the Api\n\t * objects since the extensions are effectively pre-parsed.\n\t *\n\t * The array is an array of objects with the following structure, where this\n\t * base array represents the Api prototype base:\n\t *\n\t *     [\n\t *       {\n\t *         name:      'data'                -- string   - Property name\n\t *         val:       function () {},       -- function - Api method (or undefined if just an object\n\t *         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result\n\t *         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property\n\t *       },\n\t *       {\n\t *         name:     'row'\n\t *         val:       {},\n\t *         methodExt: [ ... ],\n\t *         propExt:   [\n\t *           {\n\t *             name:      'data'\n\t *             val:       function () {},\n\t *             methodExt: [ ... ],\n\t *             propExt:   [ ... ]\n\t *           },\n\t *           ...\n\t *         ]\n\t *       }\n\t *     ]\n\t *\n\t * @type {Array}\n\t * @ignore\n\t */\n\tvar __apiStruct = [];\n\t\n\t\n\t/**\n\t * `Array.prototype` reference.\n\t *\n\t * @type object\n\t * @ignore\n\t */\n\tvar __arrayProto = Array.prototype;\n\t\n\t\n\t/**\n\t * Abstraction for `context` parameter of the `Api` constructor to allow it to\n\t * take several different forms for ease of use.\n\t *\n\t * Each of the input parameter types will be converted to a DataTables settings\n\t * object where possible.\n\t *\n\t * @param  {string|node|jQuery|object} mixed DataTable identifier. Can be one\n\t *   of:\n\t *\n\t *   * `string` - jQuery selector. Any DataTables' matching the given selector\n\t *     with be found and used.\n\t *   * `node` - `TABLE` node which has already been formed into a DataTable.\n\t *   * `jQuery` - A jQuery object of `TABLE` nodes.\n\t *   * `object` - DataTables settings object\n\t *   * `DataTables.Api` - API instance\n\t * @return {array|null} Matching DataTables settings objects. `null` or\n\t *   `undefined` is returned if no matching DataTable is found.\n\t * @ignore\n\t */\n\tvar _toSettings = function ( mixed )\n\t{\n\t\tvar idx, jq;\n\t\tvar settings = DataTable.settings;\n\t\tvar tables = $.map( settings, function (el, i) {\n\t\t\treturn el.nTable;\n\t\t} );\n\t\n\t\tif ( ! mixed ) {\n\t\t\treturn [];\n\t\t}\n\t\telse if ( mixed.nTable && mixed.oApi ) {\n\t\t\t// DataTables settings object\n\t\t\treturn [ mixed ];\n\t\t}\n\t\telse if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {\n\t\t\t// Table node\n\t\t\tidx = $.inArray( mixed, tables );\n\t\t\treturn idx !== -1 ? [ settings[idx] ] : null;\n\t\t}\n\t\telse if ( mixed && typeof mixed.settings === 'function' ) {\n\t\t\treturn mixed.settings().toArray();\n\t\t}\n\t\telse if ( typeof mixed === 'string' ) {\n\t\t\t// jQuery selector\n\t\t\tjq = $(mixed);\n\t\t}\n\t\telse if ( mixed instanceof $ ) {\n\t\t\t// jQuery object (also DataTables instance)\n\t\t\tjq = mixed;\n\t\t}\n\t\n\t\tif ( jq ) {\n\t\t\treturn jq.map( function(i) {\n\t\t\t\tidx = $.inArray( this, tables );\n\t\t\t\treturn idx !== -1 ? settings[idx] : null;\n\t\t\t} ).toArray();\n\t\t}\n\t};\n\t\n\t\n\t/**\n\t * DataTables API class - used to control and interface with  one or more\n\t * DataTables enhanced tables.\n\t *\n\t * The API class is heavily based on jQuery, presenting a chainable interface\n\t * that you can use to interact with tables. Each instance of the API class has\n\t * a \"context\" - i.e. the tables that it will operate on. This could be a single\n\t * table, all tables on a page or a sub-set thereof.\n\t *\n\t * Additionally the API is designed to allow you to easily work with the data in\n\t * the tables, retrieving and manipulating it as required. This is done by\n\t * presenting the API class as an array like interface. The contents of the\n\t * array depend upon the actions requested by each method (for example\n\t * `rows().nodes()` will return an array of nodes, while `rows().data()` will\n\t * return an array of objects or arrays depending upon your table's\n\t * configuration). The API object has a number of array like methods (`push`,\n\t * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,\n\t * `unique` etc) to assist your working with the data held in a table.\n\t *\n\t * Most methods (those which return an Api instance) are chainable, which means\n\t * the return from a method call also has all of the methods available that the\n\t * top level object had. For example, these two calls are equivalent:\n\t *\n\t *     // Not chained\n\t *     api.row.add( {...} );\n\t *     api.draw();\n\t *\n\t *     // Chained\n\t *     api.row.add( {...} ).draw();\n\t *\n\t * @class DataTable.Api\n\t * @param {array|object|string|jQuery} context DataTable identifier. This is\n\t *   used to define which DataTables enhanced tables this API will operate on.\n\t *   Can be one of:\n\t *\n\t *   * `string` - jQuery selector. Any DataTables' matching the given selector\n\t *     with be found and used.\n\t *   * `node` - `TABLE` node which has already been formed into a DataTable.\n\t *   * `jQuery` - A jQuery object of `TABLE` nodes.\n\t *   * `object` - DataTables settings object\n\t * @param {array} [data] Data to initialise the Api instance with.\n\t *\n\t * @example\n\t *   // Direct initialisation during DataTables construction\n\t *   var api = $('#example').DataTable();\n\t *\n\t * @example\n\t *   // Initialisation using a DataTables jQuery object\n\t *   var api = $('#example').dataTable().api();\n\t *\n\t * @example\n\t *   // Initialisation as a constructor\n\t *   var api = new $.fn.DataTable.Api( 'table.dataTable' );\n\t */\n\t_Api = function ( context, data )\n\t{\n\t\tif ( ! (this instanceof _Api) ) {\n\t\t\treturn new _Api( context, data );\n\t\t}\n\t\n\t\tvar settings = [];\n\t\tvar ctxSettings = function ( o ) {\n\t\t\tvar a = _toSettings( o );\n\t\t\tif ( a ) {\n\t\t\t\tsettings.push.apply( settings, a );\n\t\t\t}\n\t\t};\n\t\n\t\tif ( Array.isArray( context ) ) {\n\t\t\tfor ( var i=0, ien=context.length ; i<ien ; i++ ) {\n\t\t\t\tctxSettings( context[i] );\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tctxSettings( context );\n\t\t}\n\t\n\t\t// Remove duplicates\n\t\tthis.context = _unique( settings );\n\t\n\t\t// Initial data\n\t\tif ( data ) {\n\t\t\t$.merge( this, data );\n\t\t}\n\t\n\t\t// selector\n\t\tthis.selector = {\n\t\t\trows: null,\n\t\t\tcols: null,\n\t\t\topts: null\n\t\t};\n\t\n\t\t_Api.extend( this, this, __apiStruct );\n\t};\n\t\n\tDataTable.Api = _Api;\n\t\n\t// Don't destroy the existing prototype, just extend it. Required for jQuery 2's\n\t// isPlainObject.\n\t$.extend( _Api.prototype, {\n\t\tany: function ()\n\t\t{\n\t\t\treturn this.count() !== 0;\n\t\t},\n\t\n\t\n\t\tconcat:  __arrayProto.concat,\n\t\n\t\n\t\tcontext: [], // array of table settings objects\n\t\n\t\n\t\tcount: function ()\n\t\t{\n\t\t\treturn this.flatten().length;\n\t\t},\n\t\n\t\n\t\teach: function ( fn )\n\t\t{\n\t\t\tfor ( var i=0, ien=this.length ; i<ien; i++ ) {\n\t\t\t\tfn.call( this, this[i], i, this );\n\t\t\t}\n\t\n\t\t\treturn this;\n\t\t},\n\t\n\t\n\t\teq: function ( idx )\n\t\t{\n\t\t\tvar ctx = this.context;\n\t\n\t\t\treturn ctx.length > idx ?\n\t\t\t\tnew _Api( ctx[idx], this[idx] ) :\n\t\t\t\tnull;\n\t\t},\n\t\n\t\n\t\tfilter: function ( fn )\n\t\t{\n\t\t\tvar a = [];\n\t\n\t\t\tif ( __arrayProto.filter ) {\n\t\t\t\ta = __arrayProto.filter.call( this, fn, this );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Compatibility for browsers without EMCA-252-5 (JS 1.6)\n\t\t\t\tfor ( var i=0, ien=this.length ; i<ien ; i++ ) {\n\t\t\t\t\tif ( fn.call( this, this[i], i, this ) ) {\n\t\t\t\t\t\ta.push( this[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\treturn new _Api( this.context, a );\n\t\t},\n\t\n\t\n\t\tflatten: function ()\n\t\t{\n\t\t\tvar a = [];\n\t\t\treturn new _Api( this.context, a.concat.apply( a, this.toArray() ) );\n\t\t},\n\t\n\t\n\t\tjoin:    __arrayProto.join,\n\t\n\t\n\t\tindexOf: __arrayProto.indexOf || function (obj, start)\n\t\t{\n\t\t\tfor ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {\n\t\t\t\tif ( this[i] === obj ) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t},\n\t\n\t\titerator: function ( flatten, type, fn, alwaysNew ) {\n\t\t\tvar\n\t\t\t\ta = [], ret,\n\t\t\t\ti, ien, j, jen,\n\t\t\t\tcontext = this.context,\n\t\t\t\trows, items, item,\n\t\t\t\tselector = this.selector;\n\t\n\t\t\t// Argument shifting\n\t\t\tif ( typeof flatten === 'string' ) {\n\t\t\t\talwaysNew = fn;\n\t\t\t\tfn = type;\n\t\t\t\ttype = flatten;\n\t\t\t\tflatten = false;\n\t\t\t}\n\t\n\t\t\tfor ( i=0, ien=context.length ; i<ien ; i++ ) {\n\t\t\t\tvar apiInst = new _Api( context[i] );\n\t\n\t\t\t\tif ( type === 'table' ) {\n\t\t\t\t\tret = fn.call( apiInst, context[i], i );\n\t\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\ta.push( ret );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( type === 'columns' || type === 'rows' ) {\n\t\t\t\t\t// this has same length as context - one entry for each table\n\t\t\t\t\tret = fn.call( apiInst, context[i], this[i], i );\n\t\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\ta.push( ret );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {\n\t\t\t\t\t// columns and rows share the same structure.\n\t\t\t\t\t// 'this' is an array of column indexes for each context\n\t\t\t\t\titems = this[i];\n\t\n\t\t\t\t\tif ( type === 'column-rows' ) {\n\t\t\t\t\t\trows = _selector_row_indexes( context[i], selector.opts );\n\t\t\t\t\t}\n\t\n\t\t\t\t\tfor ( j=0, jen=items.length ; j<jen ; j++ ) {\n\t\t\t\t\t\titem = items[j];\n\t\n\t\t\t\t\t\tif ( type === 'cell' ) {\n\t\t\t\t\t\t\tret = fn.call( apiInst, context[i], item.row, item.column, i, j );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tret = fn.call( apiInst, context[i], item, i, j, rows );\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\t\ta.push( ret );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\tif ( a.length || alwaysNew ) {\n\t\t\t\tvar api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );\n\t\t\t\tvar apiSelector = api.selector;\n\t\t\t\tapiSelector.rows = selector.rows;\n\t\t\t\tapiSelector.cols = selector.cols;\n\t\t\t\tapiSelector.opts = selector.opts;\n\t\t\t\treturn api;\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\t\n\t\n\t\tlastIndexOf: __arrayProto.lastIndexOf || function (obj, start)\n\t\t{\n\t\t\t// Bit cheeky...\n\t\t\treturn this.indexOf.apply( this.toArray.reverse(), arguments );\n\t\t},\n\t\n\t\n\t\tlength:  0,\n\t\n\t\n\t\tmap: function ( fn )\n\t\t{\n\t\t\tvar a = [];\n\t\n\t\t\tif ( __arrayProto.map ) {\n\t\t\t\ta = __arrayProto.map.call( this, fn, this );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Compatibility for browsers without EMCA-252-5 (JS 1.6)\n\t\t\t\tfor ( var i=0, ien=this.length ; i<ien ; i++ ) {\n\t\t\t\t\ta.push( fn.call( this, this[i], i ) );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\treturn new _Api( this.context, a );\n\t\t},\n\t\n\t\n\t\tpluck: function ( prop )\n\t\t{\n\t\t\tvar fn = DataTable.util.get(prop);\n\t\n\t\t\treturn this.map( function ( el ) {\n\t\t\t\treturn fn(el);\n\t\t\t} );\n\t\t},\n\t\n\t\tpop:     __arrayProto.pop,\n\t\n\t\n\t\tpush:    __arrayProto.push,\n\t\n\t\n\t\t// Does not return an API instance\n\t\treduce: __arrayProto.reduce || function ( fn, init )\n\t\t{\n\t\t\treturn _fnReduce( this, fn, init, 0, this.length, 1 );\n\t\t},\n\t\n\t\n\t\treduceRight: __arrayProto.reduceRight || function ( fn, init )\n\t\t{\n\t\t\treturn _fnReduce( this, fn, init, this.length-1, -1, -1 );\n\t\t},\n\t\n\t\n\t\treverse: __arrayProto.reverse,\n\t\n\t\n\t\t// Object with rows, columns and opts\n\t\tselector: null,\n\t\n\t\n\t\tshift:   __arrayProto.shift,\n\t\n\t\n\t\tslice: function () {\n\t\t\treturn new _Api( this.context, this );\n\t\t},\n\t\n\t\n\t\tsort:    __arrayProto.sort, // ? name - order?\n\t\n\t\n\t\tsplice:  __arrayProto.splice,\n\t\n\t\n\t\ttoArray: function ()\n\t\t{\n\t\t\treturn __arrayProto.slice.call( this );\n\t\t},\n\t\n\t\n\t\tto$: function ()\n\t\t{\n\t\t\treturn $( this );\n\t\t},\n\t\n\t\n\t\ttoJQuery: function ()\n\t\t{\n\t\t\treturn $( this );\n\t\t},\n\t\n\t\n\t\tunique: function ()\n\t\t{\n\t\t\treturn new _Api( this.context, _unique(this) );\n\t\t},\n\t\n\t\n\t\tunshift: __arrayProto.unshift\n\t} );\n\t\n\t\n\t_Api.extend = function ( scope, obj, ext )\n\t{\n\t\t// Only extend API instances and static properties of the API\n\t\tif ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar\n\t\t\ti, ien,\n\t\t\tstruct,\n\t\t\tmethodScoping = function ( scope, fn, struc ) {\n\t\t\t\treturn function () {\n\t\t\t\t\tvar ret = fn.apply( scope, arguments );\n\t\n\t\t\t\t\t// Method extension\n\t\t\t\t\t_Api.extend( ret, ret, struc.methodExt );\n\t\t\t\t\treturn ret;\n\t\t\t\t};\n\t\t\t};\n\t\n\t\tfor ( i=0, ien=ext.length ; i<ien ; i++ ) {\n\t\t\tstruct = ext[i];\n\t\n\t\t\t// Value\n\t\t\tobj[ struct.name ] = struct.type === 'function' ?\n\t\t\t\tmethodScoping( scope, struct.val, struct ) :\n\t\t\t\tstruct.type === 'object' ?\n\t\t\t\t\t{} :\n\t\t\t\t\tstruct.val;\n\t\n\t\t\tobj[ struct.name ].__dt_wrapper = true;\n\t\n\t\t\t// Property extension\n\t\t\t_Api.extend( scope, obj[ struct.name ], struct.propExt );\n\t\t}\n\t};\n\t\n\t\n\t// @todo - Is there need for an augment function?\n\t// _Api.augment = function ( inst, name )\n\t// {\n\t// \t// Find src object in the structure from the name\n\t// \tvar parts = name.split('.');\n\t\n\t// \t_Api.extend( inst, obj );\n\t// };\n\t\n\t\n\t//     [\n\t//       {\n\t//         name:      'data'                -- string   - Property name\n\t//         val:       function () {},       -- function - Api method (or undefined if just an object\n\t//         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result\n\t//         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property\n\t//       },\n\t//       {\n\t//         name:     'row'\n\t//         val:       {},\n\t//         methodExt: [ ... ],\n\t//         propExt:   [\n\t//           {\n\t//             name:      'data'\n\t//             val:       function () {},\n\t//             methodExt: [ ... ],\n\t//             propExt:   [ ... ]\n\t//           },\n\t//           ...\n\t//         ]\n\t//       }\n\t//     ]\n\t\n\t_Api.register = _api_register = function ( name, val )\n\t{\n\t\tif ( Array.isArray( name ) ) {\n\t\t\tfor ( var j=0, jen=name.length ; j<jen ; j++ ) {\n\t\t\t\t_Api.register( name[j], val );\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\n\t\tvar\n\t\t\ti, ien,\n\t\t\their = name.split('.'),\n\t\t\tstruct = __apiStruct,\n\t\t\tkey, method;\n\t\n\t\tvar find = function ( src, name ) {\n\t\t\tfor ( var i=0, ien=src.length ; i<ien ; i++ ) {\n\t\t\t\tif ( src[i].name === name ) {\n\t\t\t\t\treturn src[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\n\t\tfor ( i=0, ien=heir.length ; i<ien ; i++ ) {\n\t\t\tmethod = heir[i].indexOf('()') !== -1;\n\t\t\tkey = method ?\n\t\t\t\their[i].replace('()', '') :\n\t\t\t\their[i];\n\t\n\t\t\tvar src = find( struct, key );\n\t\t\tif ( ! src ) {\n\t\t\t\tsrc = {\n\t\t\t\t\tname:      key,\n\t\t\t\t\tval:       {},\n\t\t\t\t\tmethodExt: [],\n\t\t\t\t\tpropExt:   [],\n\t\t\t\t\ttype:      'object'\n\t\t\t\t};\n\t\t\t\tstruct.push( src );\n\t\t\t}\n\t\n\t\t\tif ( i === ien-1 ) {\n\t\t\t\tsrc.val = val;\n\t\t\t\tsrc.type = typeof val === 'function' ?\n\t\t\t\t\t'function' :\n\t\t\t\t\t$.isPlainObject( val ) ?\n\t\t\t\t\t\t'object' :\n\t\t\t\t\t\t'other';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tstruct = method ?\n\t\t\t\t\tsrc.methodExt :\n\t\t\t\t\tsrc.propExt;\n\t\t\t}\n\t\t}\n\t};\n\t\n\t_Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {\n\t\t_Api.register( pluralName, val );\n\t\n\t\t_Api.register( singularName, function () {\n\t\t\tvar ret = val.apply( this, arguments );\n\t\n\t\t\tif ( ret === this ) {\n\t\t\t\t// Returned item is the API instance that was passed in, return it\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\telse if ( ret instanceof _Api ) {\n\t\t\t\t// New API instance returned, want the value from the first item\n\t\t\t\t// in the returned array for the singular result.\n\t\t\t\treturn ret.length ?\n\t\t\t\t\tArray.isArray( ret[0] ) ?\n\t\t\t\t\t\tnew _Api( ret.context, ret[0] ) : // Array results are 'enhanced'\n\t\t\t\t\t\tret[0] :\n\t\t\t\t\tundefined;\n\t\t\t}\n\t\n\t\t\t// Non-API return - just fire it back\n\t\t\treturn ret;\n\t\t} );\n\t};\n\t\n\t\n\t/**\n\t * Selector for HTML tables. Apply the given selector to the give array of\n\t * DataTables settings objects.\n\t *\n\t * @param {string|integer} [selector] jQuery selector string or integer\n\t * @param  {array} Array of DataTables settings objects to be filtered\n\t * @return {array}\n\t * @ignore\n\t */\n\tvar __table_selector = function ( selector, a )\n\t{\n\t\tif ( Array.isArray(selector) ) {\n\t\t\treturn $.map( selector, function (item) {\n\t\t\t\treturn __table_selector(item, a);\n\t\t\t} );\n\t\t}\n\t\n\t\t// Integer is used to pick out a table by index\n\t\tif ( typeof selector === 'number' ) {\n\t\t\treturn [ a[ selector ] ];\n\t\t}\n\t\n\t\t// Perform a jQuery selector on the table nodes\n\t\tvar nodes = $.map( a, function (el, i) {\n\t\t\treturn el.nTable;\n\t\t} );\n\t\n\t\treturn $(nodes)\n\t\t\t.filter( selector )\n\t\t\t.map( function (i) {\n\t\t\t\t// Need to translate back from the table node to the settings\n\t\t\t\tvar idx = $.inArray( this, nodes );\n\t\t\t\treturn a[ idx ];\n\t\t\t} )\n\t\t\t.toArray();\n\t};\n\t\n\t\n\t\n\t/**\n\t * Context selector for the API's context (i.e. the tables the API instance\n\t * refers to.\n\t *\n\t * @name    DataTable.Api#tables\n\t * @param {string|integer} [selector] Selector to pick which tables the iterator\n\t *   should operate on. If not given, all tables in the current context are\n\t *   used. This can be given as a jQuery selector (for example `':gt(0)'`) to\n\t *   select multiple tables or as an integer to select a single table.\n\t * @returns {DataTable.Api} Returns a new API instance if a selector is given.\n\t */\n\t_api_register( 'tables()', function ( selector ) {\n\t\t// A new instance is created if there was a selector specified\n\t\treturn selector !== undefined && selector !== null ?\n\t\t\tnew _Api( __table_selector( selector, this.context ) ) :\n\t\t\tthis;\n\t} );\n\t\n\t\n\t_api_register( 'table()', function ( selector ) {\n\t\tvar tables = this.tables( selector );\n\t\tvar ctx = tables.context;\n\t\n\t\t// Truncate to the first matched table\n\t\treturn ctx.length ?\n\t\t\tnew _Api( ctx[0] ) :\n\t\t\ttables;\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().nodes()', 'table().node()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTable;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().body()', 'table().body()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTBody;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().header()', 'table().header()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTHead;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().footer()', 'table().footer()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTFoot;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'tables().containers()', 'table().container()' , function () {\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\treturn ctx.nTableWrapper;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Redraw the tables in the current context.\n\t */\n\t_api_register( 'draw()', function ( paging ) {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tif ( paging === 'page' ) {\n\t\t\t\t_fnDraw( settings );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif ( typeof paging === 'string' ) {\n\t\t\t\t\tpaging = paging === 'full-hold' ?\n\t\t\t\t\t\tfalse :\n\t\t\t\t\t\ttrue;\n\t\t\t\t}\n\t\n\t\t\t\t_fnReDraw( settings, paging===false );\n\t\t\t}\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Get the current page index.\n\t *\n\t * @return {integer} Current page index (zero based)\n\t *//**\n\t * Set the current page.\n\t *\n\t * Note that if you attempt to show a page which does not exist, DataTables will\n\t * not throw an error, but rather reset the paging.\n\t *\n\t * @param {integer|string} action The paging action to take. This can be one of:\n\t *  * `integer` - The page index to jump to\n\t *  * `string` - An action to take:\n\t *    * `first` - Jump to first page.\n\t *    * `next` - Jump to the next page\n\t *    * `previous` - Jump to previous page\n\t *    * `last` - Jump to the last page.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'page()', function ( action ) {\n\t\tif ( action === undefined ) {\n\t\t\treturn this.page.info().page; // not an expensive call\n\t\t}\n\t\n\t\t// else, have an action to take on all tables\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnPageChange( settings, action );\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Paging information for the first table in the current context.\n\t *\n\t * If you require paging information for another table, use the `table()` method\n\t * with a suitable selector.\n\t *\n\t * @return {object} Object with the following properties set:\n\t *  * `page` - Current page index (zero based - i.e. the first page is `0`)\n\t *  * `pages` - Total number of pages\n\t *  * `start` - Display index for the first record shown on the current page\n\t *  * `end` - Display index for the last record shown on the current page\n\t *  * `length` - Display length (number of records). Note that generally `start\n\t *    + length = end`, but this is not always true, for example if there are\n\t *    only 2 records to show on the final page, with a length of 10.\n\t *  * `recordsTotal` - Full data set length\n\t *  * `recordsDisplay` - Data set length once the current filtering criterion\n\t *    are applied.\n\t */\n\t_api_register( 'page.info()', function ( action ) {\n\t\tif ( this.context.length === 0 ) {\n\t\t\treturn undefined;\n\t\t}\n\t\n\t\tvar\n\t\t\tsettings   = this.context[0],\n\t\t\tstart      = settings._iDisplayStart,\n\t\t\tlen        = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,\n\t\t\tvisRecords = settings.fnRecordsDisplay(),\n\t\t\tall        = len === -1;\n\t\n\t\treturn {\n\t\t\t\"page\":           all ? 0 : Math.floor( start / len ),\n\t\t\t\"pages\":          all ? 1 : Math.ceil( visRecords / len ),\n\t\t\t\"start\":          start,\n\t\t\t\"end\":            settings.fnDisplayEnd(),\n\t\t\t\"length\":         len,\n\t\t\t\"recordsTotal\":   settings.fnRecordsTotal(),\n\t\t\t\"recordsDisplay\": visRecords,\n\t\t\t\"serverSide\":     _fnDataSource( settings ) === 'ssp'\n\t\t};\n\t} );\n\t\n\t\n\t/**\n\t * Get the current page length.\n\t *\n\t * @return {integer} Current page length. Note `-1` indicates that all records\n\t *   are to be shown.\n\t *//**\n\t * Set the current page length.\n\t *\n\t * @param {integer} Page length to set. Use `-1` to show all records.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'page.len()', function ( len ) {\n\t\t// Note that we can't call this function 'length()' because `length`\n\t\t// is a Javascript property of functions which defines how many arguments\n\t\t// the function expects.\n\t\tif ( len === undefined ) {\n\t\t\treturn this.context.length !== 0 ?\n\t\t\t\tthis.context[0]._iDisplayLength :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// else, set the page length\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnLengthChange( settings, len );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\tvar __reload = function ( settings, holdPosition, callback ) {\n\t\t// Use the draw event to trigger a callback\n\t\tif ( callback ) {\n\t\t\tvar api = new _Api( settings );\n\t\n\t\t\tapi.one( 'draw', function () {\n\t\t\t\tcallback( api.ajax.json() );\n\t\t\t} );\n\t\t}\n\t\n\t\tif ( _fnDataSource( settings ) == 'ssp' ) {\n\t\t\t_fnReDraw( settings, holdPosition );\n\t\t}\n\t\telse {\n\t\t\t_fnProcessingDisplay( settings, true );\n\t\n\t\t\t// Cancel an existing request\n\t\t\tvar xhr = settings.jqXHR;\n\t\t\tif ( xhr && xhr.readyState !== 4 ) {\n\t\t\t\txhr.abort();\n\t\t\t}\n\t\n\t\t\t// Trigger xhr\n\t\t\t_fnBuildAjax( settings, [], function( json ) {\n\t\t\t\t_fnClearTable( settings );\n\t\n\t\t\t\tvar data = _fnAjaxDataSrc( settings, json );\n\t\t\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\t\t_fnAddData( settings, data[i] );\n\t\t\t\t}\n\t\n\t\t\t\t_fnReDraw( settings, holdPosition );\n\t\t\t\t_fnProcessingDisplay( settings, false );\n\t\t\t} );\n\t\t}\n\t};\n\t\n\t\n\t/**\n\t * Get the JSON response from the last Ajax request that DataTables made to the\n\t * server. Note that this returns the JSON from the first table in the current\n\t * context.\n\t *\n\t * @return {object} JSON received from the server.\n\t */\n\t_api_register( 'ajax.json()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( ctx.length > 0 ) {\n\t\t\treturn ctx[0].json;\n\t\t}\n\t\n\t\t// else return undefined;\n\t} );\n\t\n\t\n\t/**\n\t * Get the data submitted in the last Ajax request\n\t */\n\t_api_register( 'ajax.params()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( ctx.length > 0 ) {\n\t\t\treturn ctx[0].oAjaxData;\n\t\t}\n\t\n\t\t// else return undefined;\n\t} );\n\t\n\t\n\t/**\n\t * Reload tables from the Ajax data source. Note that this function will\n\t * automatically re-draw the table when the remote data has been loaded.\n\t *\n\t * @param {boolean} [reset=true] Reset (default) or hold the current paging\n\t *   position. A full re-sort and re-filter is performed when this method is\n\t *   called, which is why the pagination reset is the default action.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'ajax.reload()', function ( callback, resetPaging ) {\n\t\treturn this.iterator( 'table', function (settings) {\n\t\t\t__reload( settings, resetPaging===false, callback );\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Get the current Ajax URL. Note that this returns the URL from the first\n\t * table in the current context.\n\t *\n\t * @return {string} Current Ajax source URL\n\t *//**\n\t * Set the Ajax URL. Note that this will set the URL for all tables in the\n\t * current context.\n\t *\n\t * @param {string} url URL to set.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'ajax.url()', function ( url ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( url === undefined ) {\n\t\t\t// get\n\t\t\tif ( ctx.length === 0 ) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tctx = ctx[0];\n\t\n\t\t\treturn ctx.ajax ?\n\t\t\t\t$.isPlainObject( ctx.ajax ) ?\n\t\t\t\t\tctx.ajax.url :\n\t\t\t\t\tctx.ajax :\n\t\t\t\tctx.sAjaxSource;\n\t\t}\n\t\n\t\t// set\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tif ( $.isPlainObject( settings.ajax ) ) {\n\t\t\t\tsettings.ajax.url = url;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsettings.ajax = url;\n\t\t\t}\n\t\t\t// No need to consider sAjaxSource here since DataTables gives priority\n\t\t\t// to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any\n\t\t\t// value of `sAjaxSource` redundant.\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Load data from the newly set Ajax URL. Note that this method is only\n\t * available when `ajax.url()` is used to set a URL. Additionally, this method\n\t * has the same effect as calling `ajax.reload()` but is provided for\n\t * convenience when setting a new URL. Like `ajax.reload()` it will\n\t * automatically redraw the table once the remote data has been loaded.\n\t *\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'ajax.url().load()', function ( callback, resetPaging ) {\n\t\t// Same as a reload, but makes sense to present it for easy access after a\n\t\t// url change\n\t\treturn this.iterator( 'table', function ( ctx ) {\n\t\t\t__reload( ctx, resetPaging===false, callback );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t\n\tvar _selector_run = function ( type, selector, selectFn, settings, opts )\n\t{\n\t\tvar\n\t\t\tout = [], res,\n\t\t\ta, i, ien, j, jen,\n\t\t\tselectorType = typeof selector;\n\t\n\t\t// Can't just check for isArray here, as an API or jQuery instance might be\n\t\t// given with their array like look\n\t\tif ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {\n\t\t\tselector = [ selector ];\n\t\t}\n\t\n\t\tfor ( i=0, ien=selector.length ; i<ien ; i++ ) {\n\t\t\t// Only split on simple strings - complex expressions will be jQuery selectors\n\t\t\ta = selector[i] && selector[i].split && ! selector[i].match(/[\\[\\(:]/) ?\n\t\t\t\tselector[i].split(',') :\n\t\t\t\t[ selector[i] ];\n\t\n\t\t\tfor ( j=0, jen=a.length ; j<jen ; j++ ) {\n\t\t\t\tres = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );\n\t\n\t\t\t\tif ( res && res.length ) {\n\t\t\t\t\tout = out.concat( res );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\t// selector extensions\n\t\tvar ext = _ext.selector[ type ];\n\t\tif ( ext.length ) {\n\t\t\tfor ( i=0, ien=ext.length ; i<ien ; i++ ) {\n\t\t\t\tout = ext[i]( settings, opts, out );\n\t\t\t}\n\t\t}\n\t\n\t\treturn _unique( out );\n\t};\n\t\n\t\n\tvar _selector_opts = function ( opts )\n\t{\n\t\tif ( ! opts ) {\n\t\t\topts = {};\n\t\t}\n\t\n\t\t// Backwards compatibility for 1.9- which used the terminology filter rather\n\t\t// than search\n\t\tif ( opts.filter && opts.search === undefined ) {\n\t\t\topts.search = opts.filter;\n\t\t}\n\t\n\t\treturn $.extend( {\n\t\t\tsearch: 'none',\n\t\t\torder: 'current',\n\t\t\tpage: 'all'\n\t\t}, opts );\n\t};\n\t\n\t\n\tvar _selector_first = function ( inst )\n\t{\n\t\t// Reduce the API instance to the first item found\n\t\tfor ( var i=0, ien=inst.length ; i<ien ; i++ ) {\n\t\t\tif ( inst[i].length > 0 ) {\n\t\t\t\t// Assign the first element to the first item in the instance\n\t\t\t\t// and truncate the instance and context\n\t\t\t\tinst[0] = inst[i];\n\t\t\t\tinst[0].length = 1;\n\t\t\t\tinst.length = 1;\n\t\t\t\tinst.context = [ inst.context[i] ];\n\t\n\t\t\t\treturn inst;\n\t\t\t}\n\t\t}\n\t\n\t\t// Not found - return an empty instance\n\t\tinst.length = 0;\n\t\treturn inst;\n\t};\n\t\n\t\n\tvar _selector_row_indexes = function ( settings, opts )\n\t{\n\t\tvar\n\t\t\ti, ien, tmp, a=[],\n\t\t\tdisplayFiltered = settings.aiDisplay,\n\t\t\tdisplayMaster = settings.aiDisplayMaster;\n\t\n\t\tvar\n\t\t\tsearch = opts.search,  // none, applied, removed\n\t\t\torder  = opts.order,   // applied, current, index (original - compatibility with 1.9)\n\t\t\tpage   = opts.page;    // all, current\n\t\n\t\tif ( _fnDataSource( settings ) == 'ssp' ) {\n\t\t\t// In server-side processing mode, most options are irrelevant since\n\t\t\t// rows not shown don't exist and the index order is the applied order\n\t\t\t// Removed is a special case - for consistency just return an empty\n\t\t\t// array\n\t\t\treturn search === 'removed' ?\n\t\t\t\t[] :\n\t\t\t\t_range( 0, displayMaster.length );\n\t\t}\n\t\telse if ( page == 'current' ) {\n\t\t\t// Current page implies that order=current and filter=applied, since it is\n\t\t\t// fairly senseless otherwise, regardless of what order and search actually\n\t\t\t// are\n\t\t\tfor ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {\n\t\t\t\ta.push( displayFiltered[i] );\n\t\t\t}\n\t\t}\n\t\telse if ( order == 'current' || order == 'applied' ) {\n\t\t\tif ( search == 'none') {\n\t\t\t\ta = displayMaster.slice();\n\t\t\t}\n\t\t\telse if ( search == 'applied' ) {\n\t\t\t\ta = displayFiltered.slice();\n\t\t\t}\n\t\t\telse if ( search == 'removed' ) {\n\t\t\t\t// O(n+m) solution by creating a hash map\n\t\t\t\tvar displayFilteredMap = {};\n\t\n\t\t\t\tfor ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {\n\t\t\t\t\tdisplayFilteredMap[displayFiltered[i]] = null;\n\t\t\t\t}\n\t\n\t\t\t\ta = $.map( displayMaster, function (el) {\n\t\t\t\t\treturn ! displayFilteredMap.hasOwnProperty(el) ?\n\t\t\t\t\t\tel :\n\t\t\t\t\t\tnull;\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t\telse if ( order == 'index' || order == 'original' ) {\n\t\t\tfor ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\t\tif ( search == 'none' ) {\n\t\t\t\t\ta.push( i );\n\t\t\t\t}\n\t\t\t\telse { // applied | removed\n\t\t\t\t\ttmp = $.inArray( i, displayFiltered );\n\t\n\t\t\t\t\tif ((tmp === -1 && search == 'removed') ||\n\t\t\t\t\t\t(tmp >= 0   && search == 'applied') )\n\t\t\t\t\t{\n\t\t\t\t\t\ta.push( i );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\treturn a;\n\t};\n\t\n\t\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Rows\n\t *\n\t * {}          - no selector - use all available rows\n\t * {integer}   - row aoData index\n\t * {node}      - TR node\n\t * {string}    - jQuery selector to apply to the TR elements\n\t * {array}     - jQuery array of nodes, or simply an array of TR nodes\n\t *\n\t */\n\tvar __row_selector = function ( settings, selector, opts )\n\t{\n\t\tvar rows;\n\t\tvar run = function ( sel ) {\n\t\t\tvar selInt = _intVal( sel );\n\t\t\tvar i, ien;\n\t\t\tvar aoData = settings.aoData;\n\t\n\t\t\t// Short cut - selector is a number and no options provided (default is\n\t\t\t// all records, so no need to check if the index is in there, since it\n\t\t\t// must be - dev error if the index doesn't exist).\n\t\t\tif ( selInt !== null && ! opts ) {\n\t\t\t\treturn [ selInt ];\n\t\t\t}\n\t\n\t\t\tif ( ! rows ) {\n\t\t\t\trows = _selector_row_indexes( settings, opts );\n\t\t\t}\n\t\n\t\t\tif ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {\n\t\t\t\t// Selector - integer\n\t\t\t\treturn [ selInt ];\n\t\t\t}\n\t\t\telse if ( sel === null || sel === undefined || sel === '' ) {\n\t\t\t\t// Selector - none\n\t\t\t\treturn rows;\n\t\t\t}\n\t\n\t\t\t// Selector - function\n\t\t\tif ( typeof sel === 'function' ) {\n\t\t\t\treturn $.map( rows, function (idx) {\n\t\t\t\t\tvar row = aoData[ idx ];\n\t\t\t\t\treturn sel( idx, row._aData, row.nTr ) ? idx : null;\n\t\t\t\t} );\n\t\t\t}\n\t\n\t\t\t// Selector - node\n\t\t\tif ( sel.nodeName ) {\n\t\t\t\tvar rowIdx = sel._DT_RowIndex;  // Property added by DT for fast lookup\n\t\t\t\tvar cellIdx = sel._DT_CellIndex;\n\t\n\t\t\t\tif ( rowIdx !== undefined ) {\n\t\t\t\t\t// Make sure that the row is actually still present in the table\n\t\t\t\t\treturn aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?\n\t\t\t\t\t\t[ rowIdx ] :\n\t\t\t\t\t\t[];\n\t\t\t\t}\n\t\t\t\telse if ( cellIdx ) {\n\t\t\t\t\treturn aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?\n\t\t\t\t\t\t[ cellIdx.row ] :\n\t\t\t\t\t\t[];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tvar host = $(sel).closest('*[data-dt-row]');\n\t\t\t\t\treturn host.length ?\n\t\t\t\t\t\t[ host.data('dt-row') ] :\n\t\t\t\t\t\t[];\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// ID selector. Want to always be able to select rows by id, regardless\n\t\t\t// of if the tr element has been created or not, so can't rely upon\n\t\t\t// jQuery here - hence a custom implementation. This does not match\n\t\t\t// Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,\n\t\t\t// but to select it using a CSS selector engine (like Sizzle or\n\t\t\t// querySelect) it would need to need to be escaped for some characters.\n\t\t\t// DataTables simplifies this for row selectors since you can select\n\t\t\t// only a row. A # indicates an id any anything that follows is the id -\n\t\t\t// unescaped.\n\t\t\tif ( typeof sel === 'string' && sel.charAt(0) === '#' ) {\n\t\t\t\t// get row index from id\n\t\t\t\tvar rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];\n\t\t\t\tif ( rowObj !== undefined ) {\n\t\t\t\t\treturn [ rowObj.idx ];\n\t\t\t\t}\n\t\n\t\t\t\t// need to fall through to jQuery in case there is DOM id that\n\t\t\t\t// matches\n\t\t\t}\n\t\t\t\n\t\t\t// Get nodes in the order from the `rows` array with null values removed\n\t\t\tvar nodes = _removeEmpty(\n\t\t\t\t_pluck_order( settings.aoData, rows, 'nTr' )\n\t\t\t);\n\t\n\t\t\t// Selector - jQuery selector string, array of nodes or jQuery object/\n\t\t\t// As jQuery's .filter() allows jQuery objects to be passed in filter,\n\t\t\t// it also allows arrays, so this will cope with all three options\n\t\t\treturn $(nodes)\n\t\t\t\t.filter( sel )\n\t\t\t\t.map( function () {\n\t\t\t\t\treturn this._DT_RowIndex;\n\t\t\t\t} )\n\t\t\t\t.toArray();\n\t\t};\n\t\n\t\treturn _selector_run( 'row', selector, run, settings, opts );\n\t};\n\t\n\t\n\t_api_register( 'rows()', function ( selector, opts ) {\n\t\t// argument shifting\n\t\tif ( selector === undefined ) {\n\t\t\tselector = '';\n\t\t}\n\t\telse if ( $.isPlainObject( selector ) ) {\n\t\t\topts = selector;\n\t\t\tselector = '';\n\t\t}\n\t\n\t\topts = _selector_opts( opts );\n\t\n\t\tvar inst = this.iterator( 'table', function ( settings ) {\n\t\t\treturn __row_selector( settings, selector, opts );\n\t\t}, 1 );\n\t\n\t\t// Want argument shifting here and in __row_selector?\n\t\tinst.selector.rows = selector;\n\t\tinst.selector.opts = opts;\n\t\n\t\treturn inst;\n\t} );\n\t\n\t_api_register( 'rows().nodes()', function () {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\treturn settings.aoData[ row ].nTr || undefined;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_register( 'rows().data()', function () {\n\t\treturn this.iterator( true, 'rows', function ( settings, rows ) {\n\t\t\treturn _pluck_order( settings.aoData, rows, '_aData' );\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\tvar r = settings.aoData[ row ];\n\t\t\treturn type === 'search' ? r._aFilterData : r._aSortData;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\t_fnInvalidate( settings, row, src );\n\t\t} );\n\t} );\n\t\n\t_api_registerPlural( 'rows().indexes()', 'row().index()', function () {\n\t\treturn this.iterator( 'row', function ( settings, row ) {\n\t\t\treturn row;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {\n\t\tvar a = [];\n\t\tvar context = this.context;\n\t\n\t\t// `iterator` will drop undefined values, but in this case we want them\n\t\tfor ( var i=0, ien=context.length ; i<ien ; i++ ) {\n\t\t\tfor ( var j=0, jen=this[i].length ; j<jen ; j++ ) {\n\t\t\t\tvar id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );\n\t\t\t\ta.push( (hash === true ? '#' : '' )+ id );\n\t\t\t}\n\t\t}\n\t\n\t\treturn new _Api( context, a );\n\t} );\n\t\n\t_api_registerPlural( 'rows().remove()', 'row().remove()', function () {\n\t\tvar that = this;\n\t\n\t\tthis.iterator( 'row', function ( settings, row, thatIdx ) {\n\t\t\tvar data = settings.aoData;\n\t\t\tvar rowData = data[ row ];\n\t\t\tvar i, ien, j, jen;\n\t\t\tvar loopRow, loopCells;\n\t\n\t\t\tdata.splice( row, 1 );\n\t\n\t\t\t// Update the cached indexes\n\t\t\tfor ( i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\tloopRow = data[i];\n\t\t\t\tloopCells = loopRow.anCells;\n\t\n\t\t\t\t// Rows\n\t\t\t\tif ( loopRow.nTr !== null ) {\n\t\t\t\t\tloopRow.nTr._DT_RowIndex = i;\n\t\t\t\t}\n\t\n\t\t\t\t// Cells\n\t\t\t\tif ( loopCells !== null ) {\n\t\t\t\t\tfor ( j=0, jen=loopCells.length ; j<jen ; j++ ) {\n\t\t\t\t\t\tloopCells[j]._DT_CellIndex.row = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Delete from the display arrays\n\t\t\t_fnDeleteIndex( settings.aiDisplayMaster, row );\n\t\t\t_fnDeleteIndex( settings.aiDisplay, row );\n\t\t\t_fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes\n\t\n\t\t\t// For server-side processing tables - subtract the deleted row from the count\n\t\t\tif ( settings._iRecordsDisplay > 0 ) {\n\t\t\t\tsettings._iRecordsDisplay--;\n\t\t\t}\n\t\n\t\t\t// Check for an 'overflow' they case for displaying the table\n\t\t\t_fnLengthOverflow( settings );\n\t\n\t\t\t// Remove the row's ID reference if there is one\n\t\t\tvar id = settings.rowIdFn( rowData._aData );\n\t\t\tif ( id !== undefined ) {\n\t\t\t\tdelete settings.aIds[ id ];\n\t\t\t}\n\t\t} );\n\t\n\t\tthis.iterator( 'table', function ( settings ) {\n\t\t\tfor ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {\n\t\t\t\tsettings.aoData[i].idx = i;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( 'rows.add()', function ( rows ) {\n\t\tvar newRows = this.iterator( 'table', function ( settings ) {\n\t\t\t\tvar row, i, ien;\n\t\t\t\tvar out = [];\n\t\n\t\t\t\tfor ( i=0, ien=rows.length ; i<ien ; i++ ) {\n\t\t\t\t\trow = rows[i];\n\t\n\t\t\t\t\tif ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {\n\t\t\t\t\t\tout.push( _fnAddTr( settings, row )[0] );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tout.push( _fnAddData( settings, row ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\treturn out;\n\t\t\t}, 1 );\n\t\n\t\t// Return an Api.rows() extended instance, so rows().nodes() etc can be used\n\t\tvar modRows = this.rows( -1 );\n\t\tmodRows.pop();\n\t\t$.merge( modRows, newRows );\n\t\n\t\treturn modRows;\n\t} );\n\t\n\t\n\t\n\t\n\t\n\t/**\n\t *\n\t */\n\t_api_register( 'row()', function ( selector, opts ) {\n\t\treturn _selector_first( this.rows( selector, opts ) );\n\t} );\n\t\n\t\n\t_api_register( 'row().data()', function ( data ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( data === undefined ) {\n\t\t\t// Get\n\t\t\treturn ctx.length && this.length ?\n\t\t\t\tctx[0].aoData[ this[0] ]._aData :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// Set\n\t\tvar row = ctx[0].aoData[ this[0] ];\n\t\trow._aData = data;\n\t\n\t\t// If the DOM has an id, and the data source is an array\n\t\tif ( Array.isArray( data ) && row.nTr && row.nTr.id ) {\n\t\t\t_fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );\n\t\t}\n\t\n\t\t// Automatically invalidate\n\t\t_fnInvalidate( ctx[0], this[0], 'data' );\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( 'row().node()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\treturn ctx.length && this.length ?\n\t\t\tctx[0].aoData[ this[0] ].nTr || null :\n\t\t\tnull;\n\t} );\n\t\n\t\n\t_api_register( 'row.add()', function ( row ) {\n\t\t// Allow a jQuery object to be passed in - only a single row is added from\n\t\t// it though - the first element in the set\n\t\tif ( row instanceof $ && row.length ) {\n\t\t\trow = row[0];\n\t\t}\n\t\n\t\tvar rows = this.iterator( 'table', function ( settings ) {\n\t\t\tif ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {\n\t\t\t\treturn _fnAddTr( settings, row )[0];\n\t\t\t}\n\t\t\treturn _fnAddData( settings, row );\n\t\t} );\n\t\n\t\t// Return an Api.rows() extended instance, with the newly added row selected\n\t\treturn this.row( rows[0] );\n\t} );\n\t\n\t\n\t$(document).on('plugin-init.dt', function (e, context) {\n\t\tvar api = new _Api( context );\n\t\tvar namespace = 'on-plugin-init';\n\t\tvar stateSaveParamsEvent = 'stateSaveParams.' + namespace;\n\t\tvar destroyEvent = 'destroy. ' + namespace;\n\t\n\t\tapi.on( stateSaveParamsEvent, function ( e, settings, d ) {\n\t\t\t// This could be more compact with the API, but it is a lot faster as a simple\n\t\t\t// internal loop\n\t\t\tvar idFn = settings.rowIdFn;\n\t\t\tvar data = settings.aoData;\n\t\t\tvar ids = [];\n\t\n\t\t\tfor (var i=0 ; i<data.length ; i++) {\n\t\t\t\tif (data[i]._detailsShow) {\n\t\t\t\t\tids.push( '#' + idFn(data[i]._aData) );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\td.childRows = ids;\n\t\t});\n\t\n\t\tapi.on( destroyEvent, function () {\n\t\t\tapi.off(stateSaveParamsEvent + ' ' + destroyEvent);\n\t\t});\n\t\n\t\tvar loaded = api.state.loaded();\n\t\n\t\tif ( loaded && loaded.childRows ) {\n\t\t\tapi\n\t\t\t\t.rows( $.map(loaded.childRows, function (id){\n\t\t\t\t\treturn id.replace(/:/g, '\\\\:')\n\t\t\t\t}) )\n\t\t\t\t.every( function () {\n\t\t\t\t\t_fnCallbackFire( context, null, 'requestChild', [ this ] )\n\t\t\t\t});\n\t\t}\n\t});\n\t\n\tvar __details_add = function ( ctx, row, data, klass )\n\t{\n\t\t// Convert to array of TR elements\n\t\tvar rows = [];\n\t\tvar addRow = function ( r, k ) {\n\t\t\t// Recursion to allow for arrays of jQuery objects\n\t\t\tif ( Array.isArray( r ) || r instanceof $ ) {\n\t\t\t\tfor ( var i=0, ien=r.length ; i<ien ; i++ ) {\n\t\t\t\t\taddRow( r[i], k );\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// If we get a TR element, then just add it directly - up to the dev\n\t\t\t// to add the correct number of columns etc\n\t\t\tif ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {\n\t\t\t\trows.push( r );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Otherwise create a row with a wrapper\n\t\t\t\tvar created = $('<tr><td></td></tr>').addClass( k );\n\t\t\t\t$('td', created)\n\t\t\t\t\t.addClass( k )\n\t\t\t\t\t.html( r )\n\t\t\t\t\t[0].colSpan = _fnVisbleColumns( ctx );\n\t\n\t\t\t\trows.push( created[0] );\n\t\t\t}\n\t\t};\n\t\n\t\taddRow( data, klass );\n\t\n\t\tif ( row._details ) {\n\t\t\trow._details.detach();\n\t\t}\n\t\n\t\trow._details = $(rows);\n\t\n\t\t// If the children were already shown, that state should be retained\n\t\tif ( row._detailsShow ) {\n\t\t\trow._details.insertAfter( row.nTr );\n\t\t}\n\t};\n\t\n\t\n\t// Make state saving of child row details async to allow them to be batch processed\n\tvar __details_state = DataTable.util.throttle(\n\t\tfunction (ctx) {\n\t\t\t_fnSaveState( ctx[0] )\n\t\t},\n\t\t500\n\t);\n\t\n\t\n\tvar __details_remove = function ( api, idx )\n\t{\n\t\tvar ctx = api.context;\n\t\n\t\tif ( ctx.length ) {\n\t\t\tvar row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];\n\t\n\t\t\tif ( row && row._details ) {\n\t\t\t\trow._details.remove();\n\t\n\t\t\t\trow._detailsShow = undefined;\n\t\t\t\trow._details = undefined;\n\t\t\t\t$( row.nTr ).removeClass( 'dt-hasChild' );\n\t\t\t\t__details_state( ctx );\n\t\t\t}\n\t\t}\n\t};\n\t\n\t\n\tvar __details_display = function ( api, show ) {\n\t\tvar ctx = api.context;\n\t\n\t\tif ( ctx.length && api.length ) {\n\t\t\tvar row = ctx[0].aoData[ api[0] ];\n\t\n\t\t\tif ( row._details ) {\n\t\t\t\trow._detailsShow = show;\n\t\n\t\t\t\tif ( show ) {\n\t\t\t\t\trow._details.insertAfter( row.nTr );\n\t\t\t\t\t$( row.nTr ).addClass( 'dt-hasChild' );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\trow._details.detach();\n\t\t\t\t\t$( row.nTr ).removeClass( 'dt-hasChild' );\n\t\t\t\t}\n\t\n\t\t\t\t_fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] )\n\t\n\t\t\t\t__details_events( ctx[0] );\n\t\t\t\t__details_state( ctx );\n\t\t\t}\n\t\t}\n\t};\n\t\n\t\n\tvar __details_events = function ( settings )\n\t{\n\t\tvar api = new _Api( settings );\n\t\tvar namespace = '.dt.DT_details';\n\t\tvar drawEvent = 'draw'+namespace;\n\t\tvar colvisEvent = 'column-sizing'+namespace;\n\t\tvar destroyEvent = 'destroy'+namespace;\n\t\tvar data = settings.aoData;\n\t\n\t\tapi.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );\n\t\n\t\tif ( _pluck( data, '_details' ).length > 0 ) {\n\t\t\t// On each draw, insert the required elements into the document\n\t\t\tapi.on( drawEvent, function ( e, ctx ) {\n\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\tapi.rows( {page:'current'} ).eq(0).each( function (idx) {\n\t\t\t\t\t// Internal data grab\n\t\t\t\t\tvar row = data[ idx ];\n\t\n\t\t\t\t\tif ( row._detailsShow ) {\n\t\t\t\t\t\trow._details.insertAfter( row.nTr );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t} );\n\t\n\t\t\t// Column visibility change - update the colspan\n\t\t\tapi.on( colvisEvent, function ( e, ctx, idx, vis ) {\n\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\t// Update the colspan for the details rows (note, only if it already has\n\t\t\t\t// a colspan)\n\t\t\t\tvar row, visible = _fnVisbleColumns( ctx );\n\t\n\t\t\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\t\trow = data[i];\n\t\n\t\t\t\t\tif ( row._details ) {\n\t\t\t\t\t\trow._details.children('td[colspan]').attr('colspan', visible );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\t\n\t\t\t// Table destroyed - nuke any child rows\n\t\t\tapi.on( destroyEvent, function ( e, ctx ) {\n\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\tfor ( var i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\t\tif ( data[i]._details ) {\n\t\t\t\t\t\t__details_remove( api, i );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t};\n\t\n\t// Strings for the method names to help minification\n\tvar _emp = '';\n\tvar _child_obj = _emp+'row().child';\n\tvar _child_mth = _child_obj+'()';\n\t\n\t// data can be:\n\t//  tr\n\t//  string\n\t//  jQuery or array of any of the above\n\t_api_register( _child_mth, function ( data, klass ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( data === undefined ) {\n\t\t\t// get\n\t\t\treturn ctx.length && this.length ?\n\t\t\t\tctx[0].aoData[ this[0] ]._details :\n\t\t\t\tundefined;\n\t\t}\n\t\telse if ( data === true ) {\n\t\t\t// show\n\t\t\tthis.child.show();\n\t\t}\n\t\telse if ( data === false ) {\n\t\t\t// remove\n\t\t\t__details_remove( this );\n\t\t}\n\t\telse if ( ctx.length && this.length ) {\n\t\t\t// set\n\t\t\t__details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );\n\t\t}\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( [\n\t\t_child_obj+'.show()',\n\t\t_child_mth+'.show()' // only when `child()` was called with parameters (without\n\t], function ( show ) {   // it returns an object and this method is not executed)\n\t\t__details_display( this, true );\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( [\n\t\t_child_obj+'.hide()',\n\t\t_child_mth+'.hide()' // only when `child()` was called with parameters (without\n\t], function () {         // it returns an object and this method is not executed)\n\t\t__details_display( this, false );\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( [\n\t\t_child_obj+'.remove()',\n\t\t_child_mth+'.remove()' // only when `child()` was called with parameters (without\n\t], function () {           // it returns an object and this method is not executed)\n\t\t__details_remove( this );\n\t\treturn this;\n\t} );\n\t\n\t\n\t_api_register( _child_obj+'.isShown()', function () {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( ctx.length && this.length ) {\n\t\t\t// _detailsShown as false or undefined will fall through to return false\n\t\t\treturn ctx[0].aoData[ this[0] ]._detailsShow || false;\n\t\t}\n\t\treturn false;\n\t} );\n\t\n\t\n\t\n\t/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\t * Columns\n\t *\n\t * {integer}           - column index (>=0 count from left, <0 count from right)\n\t * \"{integer}:visIdx\"  - visible column index (i.e. translate to column index)  (>=0 count from left, <0 count from right)\n\t * \"{integer}:visible\" - alias for {integer}:visIdx  (>=0 count from left, <0 count from right)\n\t * \"{string}:name\"     - column name\n\t * \"{string}\"          - jQuery selector on column header nodes\n\t *\n\t */\n\t\n\t// can be an array of these items, comma separated list, or an array of comma\n\t// separated lists\n\t\n\tvar __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;\n\t\n\t\n\t// r1 and r2 are redundant - but it means that the parameters match for the\n\t// iterator callback in columns().data()\n\tvar __columnData = function ( settings, column, r1, r2, rows ) {\n\t\tvar a = [];\n\t\tfor ( var row=0, ien=rows.length ; row<ien ; row++ ) {\n\t\t\ta.push( _fnGetCellData( settings, rows[row], column ) );\n\t\t}\n\t\treturn a;\n\t};\n\t\n\t\n\tvar __column_selector = function ( settings, selector, opts )\n\t{\n\t\tvar\n\t\t\tcolumns = settings.aoColumns,\n\t\t\tnames = _pluck( columns, 'sName' ),\n\t\t\tnodes = _pluck( columns, 'nTh' );\n\t\n\t\tvar run = function ( s ) {\n\t\t\tvar selInt = _intVal( s );\n\t\n\t\t\t// Selector - all\n\t\t\tif ( s === '' ) {\n\t\t\t\treturn _range( columns.length );\n\t\t\t}\n\t\n\t\t\t// Selector - index\n\t\t\tif ( selInt !== null ) {\n\t\t\t\treturn [ selInt >= 0 ?\n\t\t\t\t\tselInt : // Count from left\n\t\t\t\t\tcolumns.length + selInt // Count from right (+ because its a negative value)\n\t\t\t\t];\n\t\t\t}\n\t\n\t\t\t// Selector = function\n\t\t\tif ( typeof s === 'function' ) {\n\t\t\t\tvar rows = _selector_row_indexes( settings, opts );\n\t\n\t\t\t\treturn $.map( columns, function (col, idx) {\n\t\t\t\t\treturn s(\n\t\t\t\t\t\t\tidx,\n\t\t\t\t\t\t\t__columnData( settings, idx, 0, 0, rows ),\n\t\t\t\t\t\t\tnodes[ idx ]\n\t\t\t\t\t\t) ? idx : null;\n\t\t\t\t} );\n\t\t\t}\n\t\n\t\t\t// jQuery or string selector\n\t\t\tvar match = typeof s === 'string' ?\n\t\t\t\ts.match( __re_column_selector ) :\n\t\t\t\t'';\n\t\n\t\t\tif ( match ) {\n\t\t\t\tswitch( match[2] ) {\n\t\t\t\t\tcase 'visIdx':\n\t\t\t\t\tcase 'visible':\n\t\t\t\t\t\tvar idx = parseInt( match[1], 10 );\n\t\t\t\t\t\t// Visible index given, convert to column index\n\t\t\t\t\t\tif ( idx < 0 ) {\n\t\t\t\t\t\t\t// Counting from the right\n\t\t\t\t\t\t\tvar visColumns = $.map( columns, function (col,i) {\n\t\t\t\t\t\t\t\treturn col.bVisible ? i : null;\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t\treturn [ visColumns[ visColumns.length + idx ] ];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Counting from the left\n\t\t\t\t\t\treturn [ _fnVisibleToColumnIndex( settings, idx ) ];\n\t\n\t\t\t\t\tcase 'name':\n\t\t\t\t\t\t// match by name. `names` is column index complete and in order\n\t\t\t\t\t\treturn $.map( names, function (name, i) {\n\t\t\t\t\t\t\treturn name === match[1] ? i : null;\n\t\t\t\t\t\t} );\n\t\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Cell in the table body\n\t\t\tif ( s.nodeName && s._DT_CellIndex ) {\n\t\t\t\treturn [ s._DT_CellIndex.column ];\n\t\t\t}\n\t\n\t\t\t// jQuery selector on the TH elements for the columns\n\t\t\tvar jqResult = $( nodes )\n\t\t\t\t.filter( s )\n\t\t\t\t.map( function () {\n\t\t\t\t\treturn $.inArray( this, nodes ); // `nodes` is column index complete and in order\n\t\t\t\t} )\n\t\t\t\t.toArray();\n\t\n\t\t\tif ( jqResult.length || ! s.nodeName ) {\n\t\t\t\treturn jqResult;\n\t\t\t}\n\t\n\t\t\t// Otherwise a node which might have a `dt-column` data attribute, or be\n\t\t\t// a child or such an element\n\t\t\tvar host = $(s).closest('*[data-dt-column]');\n\t\t\treturn host.length ?\n\t\t\t\t[ host.data('dt-column') ] :\n\t\t\t\t[];\n\t\t};\n\t\n\t\treturn _selector_run( 'column', selector, run, settings, opts );\n\t};\n\t\n\t\n\tvar __setColumnVis = function ( settings, column, vis ) {\n\t\tvar\n\t\t\tcols = settings.aoColumns,\n\t\t\tcol  = cols[ column ],\n\t\t\tdata = settings.aoData,\n\t\t\trow, cells, i, ien, tr;\n\t\n\t\t// Get\n\t\tif ( vis === undefined ) {\n\t\t\treturn col.bVisible;\n\t\t}\n\t\n\t\t// Set\n\t\t// No change\n\t\tif ( col.bVisible === vis ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tif ( vis ) {\n\t\t\t// Insert column\n\t\t\t// Need to decide if we should use appendChild or insertBefore\n\t\t\tvar insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );\n\t\n\t\t\tfor ( i=0, ien=data.length ; i<ien ; i++ ) {\n\t\t\t\ttr = data[i].nTr;\n\t\t\t\tcells = data[i].anCells;\n\t\n\t\t\t\tif ( tr ) {\n\t\t\t\t\t// insertBefore can act like appendChild if 2nd arg is null\n\t\t\t\t\ttr.insertBefore( cells[ column ], cells[ insertBefore ] || null );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Remove column\n\t\t\t$( _pluck( settings.aoData, 'anCells', column ) ).detach();\n\t\t}\n\t\n\t\t// Common actions\n\t\tcol.bVisible = vis;\n\t};\n\t\n\t\n\t_api_register( 'columns()', function ( selector, opts ) {\n\t\t// argument shifting\n\t\tif ( selector === undefined ) {\n\t\t\tselector = '';\n\t\t}\n\t\telse if ( $.isPlainObject( selector ) ) {\n\t\t\topts = selector;\n\t\t\tselector = '';\n\t\t}\n\t\n\t\topts = _selector_opts( opts );\n\t\n\t\tvar inst = this.iterator( 'table', function ( settings ) {\n\t\t\treturn __column_selector( settings, selector, opts );\n\t\t}, 1 );\n\t\n\t\t// Want argument shifting here and in _row_selector?\n\t\tinst.selector.cols = selector;\n\t\tinst.selector.opts = opts;\n\t\n\t\treturn inst;\n\t} );\n\t\n\t_api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn settings.aoColumns[column].nTh;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn settings.aoColumns[column].nTf;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().data()', 'column().data()', function () {\n\t\treturn this.iterator( 'column-rows', __columnData, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn settings.aoColumns[column].mData;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {\n\t\treturn this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {\n\t\t\treturn _pluck_order( settings.aoData, rows,\n\t\t\t\ttype === 'search' ? '_aFilterData' : '_aSortData', column\n\t\t\t);\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {\n\t\treturn this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {\n\t\t\treturn _pluck_order( settings.aoData, rows, 'anCells', column ) ;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {\n\t\tvar that = this;\n\t\tvar ret = this.iterator( 'column', function ( settings, column ) {\n\t\t\tif ( vis === undefined ) {\n\t\t\t\treturn settings.aoColumns[ column ].bVisible;\n\t\t\t} // else\n\t\t\t__setColumnVis( settings, column, vis );\n\t\t} );\n\t\n\t\t// Group the column visibility changes\n\t\tif ( vis !== undefined ) {\n\t\t\tthis.iterator( 'table', function ( settings ) {\n\t\t\t\t// Redraw the header after changes\n\t\t\t\t_fnDrawHead( settings, settings.aoHeader );\n\t\t\t\t_fnDrawHead( settings, settings.aoFooter );\n\t\t\n\t\t\t\t// Update colspan for no records display. Child rows and extensions will use their own\n\t\t\t\t// listeners to do this - only need to update the empty table item here\n\t\t\t\tif ( ! settings.aiDisplay.length ) {\n\t\t\t\t\t$(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));\n\t\t\t\t}\n\t\t\n\t\t\t\t_fnSaveState( settings );\n\t\n\t\t\t\t// Second loop once the first is done for events\n\t\t\t\tthat.iterator( 'column', function ( settings, column ) {\n\t\t\t\t\t_fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );\n\t\t\t\t} );\n\t\n\t\t\t\tif ( calc === undefined || calc ) {\n\t\t\t\t\tthat.columns.adjust();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\n\t\treturn ret;\n\t} );\n\t\n\t_api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {\n\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\treturn type === 'visible' ?\n\t\t\t\t_fnColumnIndexToVisible( settings, column ) :\n\t\t\t\tcolumn;\n\t\t}, 1 );\n\t} );\n\t\n\t_api_register( 'columns.adjust()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnAdjustColumnSizing( settings );\n\t\t}, 1 );\n\t} );\n\t\n\t_api_register( 'column.index()', function ( type, idx ) {\n\t\tif ( this.context.length !== 0 ) {\n\t\t\tvar ctx = this.context[0];\n\t\n\t\t\tif ( type === 'fromVisible' || type === 'toData' ) {\n\t\t\t\treturn _fnVisibleToColumnIndex( ctx, idx );\n\t\t\t}\n\t\t\telse if ( type === 'fromData' || type === 'toVisible' ) {\n\t\t\t\treturn _fnColumnIndexToVisible( ctx, idx );\n\t\t\t}\n\t\t}\n\t} );\n\t\n\t_api_register( 'column()', function ( selector, opts ) {\n\t\treturn _selector_first( this.columns( selector, opts ) );\n\t} );\n\t\n\tvar __cell_selector = function ( settings, selector, opts )\n\t{\n\t\tvar data = settings.aoData;\n\t\tvar rows = _selector_row_indexes( settings, opts );\n\t\tvar cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );\n\t\tvar allCells = $(_flatten( [], cells ));\n\t\tvar row;\n\t\tvar columns = settings.aoColumns.length;\n\t\tvar a, i, ien, j, o, host;\n\t\n\t\tvar run = function ( s ) {\n\t\t\tvar fnSelector = typeof s === 'function';\n\t\n\t\t\tif ( s === null || s === undefined || fnSelector ) {\n\t\t\t\t// All cells and function selectors\n\t\t\t\ta = [];\n\t\n\t\t\t\tfor ( i=0, ien=rows.length ; i<ien ; i++ ) {\n\t\t\t\t\trow = rows[i];\n\t\n\t\t\t\t\tfor ( j=0 ; j<columns ; j++ ) {\n\t\t\t\t\t\to = {\n\t\t\t\t\t\t\trow: row,\n\t\t\t\t\t\t\tcolumn: j\n\t\t\t\t\t\t};\n\t\n\t\t\t\t\t\tif ( fnSelector ) {\n\t\t\t\t\t\t\t// Selector - function\n\t\t\t\t\t\t\thost = data[ row ];\n\t\n\t\t\t\t\t\t\tif ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {\n\t\t\t\t\t\t\t\ta.push( o );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Selector - all\n\t\t\t\t\t\t\ta.push( o );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\treturn a;\n\t\t\t}\n\t\t\t\n\t\t\t// Selector - index\n\t\t\tif ( $.isPlainObject( s ) ) {\n\t\t\t\t// Valid cell index and its in the array of selectable rows\n\t\t\t\treturn s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?\n\t\t\t\t\t[s] :\n\t\t\t\t\t[];\n\t\t\t}\n\t\n\t\t\t// Selector - jQuery filtered cells\n\t\t\tvar jqResult = allCells\n\t\t\t\t.filter( s )\n\t\t\t\t.map( function (i, el) {\n\t\t\t\t\treturn { // use a new object, in case someone changes the values\n\t\t\t\t\t\trow:    el._DT_CellIndex.row,\n\t\t\t\t\t\tcolumn: el._DT_CellIndex.column\n\t \t\t\t\t};\n\t\t\t\t} )\n\t\t\t\t.toArray();\n\t\n\t\t\tif ( jqResult.length || ! s.nodeName ) {\n\t\t\t\treturn jqResult;\n\t\t\t}\n\t\n\t\t\t// Otherwise the selector is a node, and there is one last option - the\n\t\t\t// element might be a child of an element which has dt-row and dt-column\n\t\t\t// data attributes\n\t\t\thost = $(s).closest('*[data-dt-row]');\n\t\t\treturn host.length ?\n\t\t\t\t[ {\n\t\t\t\t\trow: host.data('dt-row'),\n\t\t\t\t\tcolumn: host.data('dt-column')\n\t\t\t\t} ] :\n\t\t\t\t[];\n\t\t};\n\t\n\t\treturn _selector_run( 'cell', selector, run, settings, opts );\n\t};\n\t\n\t\n\t\n\t\n\t_api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {\n\t\t// Argument shifting\n\t\tif ( $.isPlainObject( rowSelector ) ) {\n\t\t\t// Indexes\n\t\t\tif ( rowSelector.row === undefined ) {\n\t\t\t\t// Selector options in first parameter\n\t\t\t\topts = rowSelector;\n\t\t\t\trowSelector = null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Cell index objects in first parameter\n\t\t\t\topts = columnSelector;\n\t\t\t\tcolumnSelector = null;\n\t\t\t}\n\t\t}\n\t\tif ( $.isPlainObject( columnSelector ) ) {\n\t\t\topts = columnSelector;\n\t\t\tcolumnSelector = null;\n\t\t}\n\t\n\t\t// Cell selector\n\t\tif ( columnSelector === null || columnSelector === undefined ) {\n\t\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t\treturn __cell_selector( settings, rowSelector, _selector_opts( opts ) );\n\t\t\t} );\n\t\t}\n\t\n\t\t// The default built in options need to apply to row and columns\n\t\tvar internalOpts = opts ? {\n\t\t\tpage: opts.page,\n\t\t\torder: opts.order,\n\t\t\tsearch: opts.search\n\t\t} : {};\n\t\n\t\t// Row + column selector\n\t\tvar columns = this.columns( columnSelector, internalOpts );\n\t\tvar rows = this.rows( rowSelector, internalOpts );\n\t\tvar i, ien, j, jen;\n\t\n\t\tvar cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {\n\t\t\tvar a = [];\n\t\n\t\t\tfor ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {\n\t\t\t\tfor ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {\n\t\t\t\t\ta.push( {\n\t\t\t\t\t\trow:    rows[idx][i],\n\t\t\t\t\t\tcolumn: columns[idx][j]\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\treturn a;\n\t\t}, 1 );\n\t\n\t\t// There is currently only one extension which uses a cell selector extension\n\t\t// It is a _major_ performance drag to run this if it isn't needed, so this is\n\t\t// an extension specific check at the moment\n\t\tvar cells = opts && opts.selected ?\n\t\t\tthis.cells( cellsNoOpts, opts ) :\n\t\t\tcellsNoOpts;\n\t\n\t\t$.extend( cells.selector, {\n\t\t\tcols: columnSelector,\n\t\t\trows: rowSelector,\n\t\t\topts: opts\n\t\t} );\n\t\n\t\treturn cells;\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().nodes()', 'cell().node()', function () {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\tvar data = settings.aoData[ row ];\n\t\n\t\t\treturn data && data.anCells ?\n\t\t\t\tdata.anCells[ column ] :\n\t\t\t\tundefined;\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_register( 'cells().data()', function () {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn _fnGetCellData( settings, row, column );\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {\n\t\ttype = type === 'search' ? '_aFilterData' : '_aSortData';\n\t\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn settings.aoData[ row ][ type ][ column ];\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn _fnGetCellData( settings, row, column, type );\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().indexes()', 'cell().index()', function () {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\treturn {\n\t\t\t\trow: row,\n\t\t\t\tcolumn: column,\n\t\t\t\tcolumnVisible: _fnColumnIndexToVisible( settings, column )\n\t\t\t};\n\t\t}, 1 );\n\t} );\n\t\n\t\n\t_api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {\n\t\treturn this.iterator( 'cell', function ( settings, row, column ) {\n\t\t\t_fnInvalidate( settings, row, src, column );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t_api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {\n\t\treturn _selector_first( this.cells( rowSelector, columnSelector, opts ) );\n\t} );\n\t\n\t\n\t_api_register( 'cell().data()', function ( data ) {\n\t\tvar ctx = this.context;\n\t\tvar cell = this[0];\n\t\n\t\tif ( data === undefined ) {\n\t\t\t// Get\n\t\t\treturn ctx.length && cell.length ?\n\t\t\t\t_fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// Set\n\t\t_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );\n\t\t_fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );\n\t\n\t\treturn this;\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Get current ordering (sorting) that has been applied to the table.\n\t *\n\t * @returns {array} 2D array containing the sorting information for the first\n\t *   table in the current context. Each element in the parent array represents\n\t *   a column being sorted upon (i.e. multi-sorting with two columns would have\n\t *   2 inner arrays). The inner arrays may have 2 or 3 elements. The first is\n\t *   the column index that the sorting condition applies to, the second is the\n\t *   direction of the sort (`desc` or `asc`) and, optionally, the third is the\n\t *   index of the sorting order from the `column.sorting` initialisation array.\n\t *//**\n\t * Set the ordering for the table.\n\t *\n\t * @param {integer} order Column index to sort upon.\n\t * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)\n\t * @returns {DataTables.Api} this\n\t *//**\n\t * Set the ordering for the table.\n\t *\n\t * @param {array} order 1D array of sorting information to be applied.\n\t * @param {array} [...] Optional additional sorting conditions\n\t * @returns {DataTables.Api} this\n\t *//**\n\t * Set the ordering for the table.\n\t *\n\t * @param {array} order 2D array of sorting information to be applied.\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'order()', function ( order, dir ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( order === undefined ) {\n\t\t\t// get\n\t\t\treturn ctx.length !== 0 ?\n\t\t\t\tctx[0].aaSorting :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// set\n\t\tif ( typeof order === 'number' ) {\n\t\t\t// Simple column / direction passed in\n\t\t\torder = [ [ order, dir ] ];\n\t\t}\n\t\telse if ( order.length && ! Array.isArray( order[0] ) ) {\n\t\t\t// Arguments passed in (list of 1D arrays)\n\t\t\torder = Array.prototype.slice.call( arguments );\n\t\t}\n\t\t// otherwise a 2D array was passed in\n\t\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tsettings.aaSorting = order.slice();\n\t\t} );\n\t} );\n\t\n\t\n\t/**\n\t * Attach a sort listener to an element for a given column\n\t *\n\t * @param {node|jQuery|string} node Identifier for the element(s) to attach the\n\t *   listener to. This can take the form of a single DOM node, a jQuery\n\t *   collection of nodes or a jQuery selector which will identify the node(s).\n\t * @param {integer} column the column that a click on this node will sort on\n\t * @param {function} [callback] callback function when sort is run\n\t * @returns {DataTables.Api} this\n\t */\n\t_api_register( 'order.listener()', function ( node, column, callback ) {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnSortAttachListener( settings, node, column, callback );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'order.fixed()', function ( set ) {\n\t\tif ( ! set ) {\n\t\t\tvar ctx = this.context;\n\t\t\tvar fixed = ctx.length ?\n\t\t\t\tctx[0].aaSortingFixed :\n\t\t\t\tundefined;\n\t\n\t\t\treturn Array.isArray( fixed ) ?\n\t\t\t\t{ pre: fixed } :\n\t\t\t\tfixed;\n\t\t}\n\t\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tsettings.aaSortingFixed = $.extend( true, {}, set );\n\t\t} );\n\t} );\n\t\n\t\n\t// Order by the selected column(s)\n\t_api_register( [\n\t\t'columns().order()',\n\t\t'column().order()'\n\t], function ( dir ) {\n\t\tvar that = this;\n\t\n\t\treturn this.iterator( 'table', function ( settings, i ) {\n\t\t\tvar sort = [];\n\t\n\t\t\t$.each( that[i], function (j, col) {\n\t\t\t\tsort.push( [ col, dir ] );\n\t\t\t} );\n\t\n\t\t\tsettings.aaSorting = sort;\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t_api_register( 'search()', function ( input, regex, smart, caseInsen ) {\n\t\tvar ctx = this.context;\n\t\n\t\tif ( input === undefined ) {\n\t\t\t// get\n\t\t\treturn ctx.length !== 0 ?\n\t\t\t\tctx[0].oPreviousSearch.sSearch :\n\t\t\t\tundefined;\n\t\t}\n\t\n\t\t// set\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tif ( ! settings.oFeatures.bFilter ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t_fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {\n\t\t\t\t\"sSearch\": input+\"\",\n\t\t\t\t\"bRegex\":  regex === null ? false : regex,\n\t\t\t\t\"bSmart\":  smart === null ? true  : smart,\n\t\t\t\t\"bCaseInsensitive\": caseInsen === null ? true : caseInsen\n\t\t\t} ), 1 );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_registerPlural(\n\t\t'columns().search()',\n\t\t'column().search()',\n\t\tfunction ( input, regex, smart, caseInsen ) {\n\t\t\treturn this.iterator( 'column', function ( settings, column ) {\n\t\t\t\tvar preSearch = settings.aoPreSearchCols;\n\t\n\t\t\t\tif ( input === undefined ) {\n\t\t\t\t\t// get\n\t\t\t\t\treturn preSearch[ column ].sSearch;\n\t\t\t\t}\n\t\n\t\t\t\t// set\n\t\t\t\tif ( ! settings.oFeatures.bFilter ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\t$.extend( preSearch[ column ], {\n\t\t\t\t\t\"sSearch\": input+\"\",\n\t\t\t\t\t\"bRegex\":  regex === null ? false : regex,\n\t\t\t\t\t\"bSmart\":  smart === null ? true  : smart,\n\t\t\t\t\t\"bCaseInsensitive\": caseInsen === null ? true : caseInsen\n\t\t\t\t} );\n\t\n\t\t\t\t_fnFilterComplete( settings, settings.oPreviousSearch, 1 );\n\t\t\t} );\n\t\t}\n\t);\n\t\n\t/*\n\t * State API methods\n\t */\n\t\n\t_api_register( 'state()', function () {\n\t\treturn this.context.length ?\n\t\t\tthis.context[0].oSavedState :\n\t\t\tnull;\n\t} );\n\t\n\t\n\t_api_register( 'state.clear()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t// Save an empty object\n\t\t\tsettings.fnStateSaveCallback.call( settings.oInstance, settings, {} );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'state.loaded()', function () {\n\t\treturn this.context.length ?\n\t\t\tthis.context[0].oLoadedState :\n\t\t\tnull;\n\t} );\n\t\n\t\n\t_api_register( 'state.save()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnSaveState( settings );\n\t\t} );\n\t} );\n\t\n\t\n\t\n\t/**\n\t * Set the jQuery or window object to be used by DataTables\n\t *\n\t * @param {*} module Library / container object\n\t * @param {string} [type] Library or container type `lib`, `win` or `datetime`.\n\t *   If not provided, automatic detection is attempted.\n\t */\n\tDataTable.use = function (module, type) {\n\t\tif (type === 'lib' || module.fn) {\n\t\t\t$ = module;\n\t\t}\n\t\telse if (type == 'win' || module.document) {\n\t\t\twindow = module;\n\t\t\tdocument = module.document;\n\t\t}\n\t\telse if (type === 'datetime' || module.type === 'DateTime') {\n\t\t\tDataTable.DateTime = module;\n\t\t}\n\t}\n\t\n\t/**\n\t * CommonJS factory function pass through. This will check if the arguments\n\t * given are a window object or a jQuery object. If so they are set\n\t * accordingly.\n\t * @param {*} root Window\n\t * @param {*} jq jQUery\n\t * @returns {boolean} Indicator\n\t */\n\tDataTable.factory = function (root, jq) {\n\t\tvar is = false;\n\t\n\t\t// Test if the first parameter is a window object\n\t\tif (root && root.document) {\n\t\t\twindow = root;\n\t\t\tdocument = root.document;\n\t\t}\n\t\n\t\t// Test if the second parameter is a jQuery object\n\t\tif (jq && jq.fn && jq.fn.jquery) {\n\t\t\t$ = jq;\n\t\t\tis = true;\n\t\t}\n\t\n\t\treturn is;\n\t}\n\t\n\t/**\n\t * Provide a common method for plug-ins to check the version of DataTables being\n\t * used, in order to ensure compatibility.\n\t *\n\t *  @param {string} version Version string to check for, in the format \"X.Y.Z\".\n\t *    Note that the formats \"X\" and \"X.Y\" are also acceptable.\n\t *  @returns {boolean} true if this version of DataTables is greater or equal to\n\t *    the required version, or false if this version of DataTales is not\n\t *    suitable\n\t *  @static\n\t *  @dtopt API-Static\n\t *\n\t *  @example\n\t *    alert( $.fn.dataTable.versionCheck( '1.9.0' ) );\n\t */\n\tDataTable.versionCheck = DataTable.fnVersionCheck = function( version )\n\t{\n\t\tvar aThis = DataTable.version.split('.');\n\t\tvar aThat = version.split('.');\n\t\tvar iThis, iThat;\n\t\n\t\tfor ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {\n\t\t\tiThis = parseInt( aThis[i], 10 ) || 0;\n\t\t\tiThat = parseInt( aThat[i], 10 ) || 0;\n\t\n\t\t\t// Parts are the same, keep comparing\n\t\t\tif (iThis === iThat) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\n\t\t\t// Parts are different, return immediately\n\t\t\treturn iThis > iThat;\n\t\t}\n\t\n\t\treturn true;\n\t};\n\t\n\t\n\t/**\n\t * Check if a `<table>` node is a DataTable table already or not.\n\t *\n\t *  @param {node|jquery|string} table Table node, jQuery object or jQuery\n\t *      selector for the table to test. Note that if more than more than one\n\t *      table is passed on, only the first will be checked\n\t *  @returns {boolean} true the table given is a DataTable, or false otherwise\n\t *  @static\n\t *  @dtopt API-Static\n\t *\n\t *  @example\n\t *    if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {\n\t *      $('#example').dataTable();\n\t *    }\n\t */\n\tDataTable.isDataTable = DataTable.fnIsDataTable = function ( table )\n\t{\n\t\tvar t = $(table).get(0);\n\t\tvar is = false;\n\t\n\t\tif ( table instanceof DataTable.Api ) {\n\t\t\treturn true;\n\t\t}\n\t\n\t\t$.each( DataTable.settings, function (i, o) {\n\t\t\tvar head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;\n\t\t\tvar foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;\n\t\n\t\t\tif ( o.nTable === t || head === t || foot === t ) {\n\t\t\t\tis = true;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn is;\n\t};\n\t\n\t\n\t/**\n\t * Get all DataTable tables that have been initialised - optionally you can\n\t * select to get only currently visible tables.\n\t *\n\t *  @param {boolean} [visible=false] Flag to indicate if you want all (default)\n\t *    or visible tables only.\n\t *  @returns {array} Array of `table` nodes (not DataTable instances) which are\n\t *    DataTables\n\t *  @static\n\t *  @dtopt API-Static\n\t *\n\t *  @example\n\t *    $.each( $.fn.dataTable.tables(true), function () {\n\t *      $(table).DataTable().columns.adjust();\n\t *    } );\n\t */\n\tDataTable.tables = DataTable.fnTables = function ( visible )\n\t{\n\t\tvar api = false;\n\t\n\t\tif ( $.isPlainObject( visible ) ) {\n\t\t\tapi = visible.api;\n\t\t\tvisible = visible.visible;\n\t\t}\n\t\n\t\tvar a = $.map( DataTable.settings, function (o) {\n\t\t\tif ( !visible || (visible && $(o.nTable).is(':visible')) ) {\n\t\t\t\treturn o.nTable;\n\t\t\t}\n\t\t} );\n\t\n\t\treturn api ?\n\t\t\tnew _Api( a ) :\n\t\t\ta;\n\t};\n\t\n\t\n\t/**\n\t * Convert from camel case parameters to Hungarian notation. This is made public\n\t * for the extensions to provide the same ability as DataTables core to accept\n\t * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase\n\t * parameters.\n\t *\n\t *  @param {object} src The model object which holds all parameters that can be\n\t *    mapped.\n\t *  @param {object} user The object to convert from camel case to Hungarian.\n\t *  @param {boolean} force When set to `true`, properties which already have a\n\t *    Hungarian value in the `user` object will be overwritten. Otherwise they\n\t *    won't be.\n\t */\n\tDataTable.camelToHungarian = _fnCamelToHungarian;\n\t\n\t\n\t\n\t/**\n\t *\n\t */\n\t_api_register( '$()', function ( selector, opts ) {\n\t\tvar\n\t\t\trows   = this.rows( opts ).nodes(), // Get all rows\n\t\t\tjqRows = $(rows);\n\t\n\t\treturn $( [].concat(\n\t\t\tjqRows.filter( selector ).toArray(),\n\t\t\tjqRows.find( selector ).toArray()\n\t\t) );\n\t} );\n\t\n\t\n\t// jQuery functions to operate on the tables\n\t$.each( [ 'on', 'one', 'off' ], function (i, key) {\n\t\t_api_register( key+'()', function ( /* event, handler */ ) {\n\t\t\tvar args = Array.prototype.slice.call(arguments);\n\t\n\t\t\t// Add the `dt` namespace automatically if it isn't already present\n\t\t\targs[0] = $.map( args[0].split( /\\s/ ), function ( e ) {\n\t\t\t\treturn ! e.match(/\\.dt\\b/) ?\n\t\t\t\t\te+'.dt' :\n\t\t\t\t\te;\n\t\t\t\t} ).join( ' ' );\n\t\n\t\t\tvar inst = $( this.tables().nodes() );\n\t\t\tinst[key].apply( inst, args );\n\t\t\treturn this;\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'clear()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\t_fnClearTable( settings );\n\t\t} );\n\t} );\n\t\n\t\n\t_api_register( 'settings()', function () {\n\t\treturn new _Api( this.context, this.context );\n\t} );\n\t\n\t\n\t_api_register( 'init()', function () {\n\t\tvar ctx = this.context;\n\t\treturn ctx.length ? ctx[0].oInit : null;\n\t} );\n\t\n\t\n\t_api_register( 'data()', function () {\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\treturn _pluck( settings.aoData, '_aData' );\n\t\t} ).flatten();\n\t} );\n\t\n\t\n\t_api_register( 'destroy()', function ( remove ) {\n\t\tremove = remove || false;\n\t\n\t\treturn this.iterator( 'table', function ( settings ) {\n\t\t\tvar classes   = settings.oClasses;\n\t\t\tvar table     = settings.nTable;\n\t\t\tvar tbody     = settings.nTBody;\n\t\t\tvar thead     = settings.nTHead;\n\t\t\tvar tfoot     = settings.nTFoot;\n\t\t\tvar jqTable   = $(table);\n\t\t\tvar jqTbody   = $(tbody);\n\t\t\tvar jqWrapper = $(settings.nTableWrapper);\n\t\t\tvar rows      = $.map( settings.aoData, function (r) { return r.nTr; } );\n\t\t\tvar i, ien;\n\t\n\t\t\t// Flag to note that the table is currently being destroyed - no action\n\t\t\t// should be taken\n\t\t\tsettings.bDestroying = true;\n\t\n\t\t\t// Fire off the destroy callbacks for plug-ins etc\n\t\t\t_fnCallbackFire( settings, \"aoDestroyCallback\", \"destroy\", [settings] );\n\t\n\t\t\t// If not being removed from the document, make all columns visible\n\t\t\tif ( ! remove ) {\n\t\t\t\tnew _Api( settings ).columns().visible( true );\n\t\t\t}\n\t\n\t\t\t// Blitz all `DT` namespaced events (these are internal events, the\n\t\t\t// lowercase, `dt` events are user subscribed and they are responsible\n\t\t\t// for removing them\n\t\t\tjqWrapper.off('.DT').find(':not(tbody *)').off('.DT');\n\t\t\t$(window).off('.DT-'+settings.sInstance);\n\t\n\t\t\t// When scrolling we had to break the table up - restore it\n\t\t\tif ( table != thead.parentNode ) {\n\t\t\t\tjqTable.children('thead').detach();\n\t\t\t\tjqTable.append( thead );\n\t\t\t}\n\t\n\t\t\tif ( tfoot && table != tfoot.parentNode ) {\n\t\t\t\tjqTable.children('tfoot').detach();\n\t\t\t\tjqTable.append( tfoot );\n\t\t\t}\n\t\n\t\t\tsettings.aaSorting = [];\n\t\t\tsettings.aaSortingFixed = [];\n\t\t\t_fnSortingClasses( settings );\n\t\n\t\t\t$( rows ).removeClass( settings.asStripeClasses.join(' ') );\n\t\n\t\t\t$('th, td', thead).removeClass( classes.sSortable+' '+\n\t\t\t\tclasses.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone\n\t\t\t);\n\t\n\t\t\t// Add the TR elements back into the table in their original order\n\t\t\tjqTbody.children().detach();\n\t\t\tjqTbody.append( rows );\n\t\n\t\t\tvar orig = settings.nTableWrapper.parentNode;\n\t\n\t\t\t// Remove the DataTables generated nodes, events and classes\n\t\t\tvar removedMethod = remove ? 'remove' : 'detach';\n\t\t\tjqTable[ removedMethod ]();\n\t\t\tjqWrapper[ removedMethod ]();\n\t\n\t\t\t// If we need to reattach the table to the document\n\t\t\tif ( ! remove && orig ) {\n\t\t\t\t// insertBefore acts like appendChild if !arg[1]\n\t\t\t\torig.insertBefore( table, settings.nTableReinsertBefore );\n\t\n\t\t\t\t// Restore the width of the original table - was read from the style property,\n\t\t\t\t// so we can restore directly to that\n\t\t\t\tjqTable\n\t\t\t\t\t.css( 'width', settings.sDestroyWidth )\n\t\t\t\t\t.removeClass( classes.sTable );\n\t\n\t\t\t\t// If the were originally stripe classes - then we add them back here.\n\t\t\t\t// Note this is not fool proof (for example if not all rows had stripe\n\t\t\t\t// classes - but it's a good effort without getting carried away\n\t\t\t\tien = settings.asDestroyStripes.length;\n\t\n\t\t\t\tif ( ien ) {\n\t\t\t\t\tjqTbody.children().each( function (i) {\n\t\t\t\t\t\t$(this).addClass( settings.asDestroyStripes[i % ien] );\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t/* Remove the settings object from the settings array */\n\t\t\tvar idx = $.inArray( settings, DataTable.settings );\n\t\t\tif ( idx !== -1 ) {\n\t\t\t\tDataTable.settings.splice( idx, 1 );\n\t\t\t}\n\t\t} );\n\t} );\n\t\n\t\n\t// Add the `every()` method for rows, columns and cells in a compact form\n\t$.each( [ 'column', 'row', 'cell' ], function ( i, type ) {\n\t\t_api_register( type+'s().every()', function ( fn ) {\n\t\t\tvar opts = this.selector.opts;\n\t\t\tvar api = this;\n\t\n\t\t\treturn this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {\n\t\t\t\t// Rows and columns:\n\t\t\t\t//  arg1 - index\n\t\t\t\t//  arg2 - table counter\n\t\t\t\t//  arg3 - loop counter\n\t\t\t\t//  arg4 - undefined\n\t\t\t\t// Cells:\n\t\t\t\t//  arg1 - row index\n\t\t\t\t//  arg2 - column index\n\t\t\t\t//  arg3 - table counter\n\t\t\t\t//  arg4 - loop counter\n\t\t\t\tfn.call(\n\t\t\t\t\tapi[ type ](\n\t\t\t\t\t\targ1,\n\t\t\t\t\t\ttype==='cell' ? arg2 : opts,\n\t\t\t\t\t\ttype==='cell' ? opts : undefined\n\t\t\t\t\t),\n\t\t\t\t\targ1, arg2, arg3, arg4\n\t\t\t\t);\n\t\t\t} );\n\t\t} );\n\t} );\n\t\n\t\n\t// i18n method for extensions to be able to use the language object from the\n\t// DataTable\n\t_api_register( 'i18n()', function ( token, def, plural ) {\n\t\tvar ctx = this.context[0];\n\t\tvar resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );\n\t\n\t\tif ( resolved === undefined ) {\n\t\t\tresolved = def;\n\t\t}\n\t\n\t\tif ( plural !== undefined && $.isPlainObject( resolved ) ) {\n\t\t\tresolved = resolved[ plural ] !== undefined ?\n\t\t\t\tresolved[ plural ] :\n\t\t\t\tresolved._;\n\t\t}\n\t\n\t\treturn typeof resolved === 'string'\n\t\t\t? resolved.replace( '%d', plural ) // nb: plural might be undefined,\n\t\t\t: resolved;\n\t} );\t\n\t/**\n\t * Version string for plug-ins to check compatibility. Allowed format is\n\t * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used\n\t * only for non-release builds. See http://semver.org/ for more information.\n\t *  @member\n\t *  @type string\n\t *  @default Version number\n\t */\n\tDataTable.version = \"1.13.5\";\n\t\n\t/**\n\t * Private data store, containing all of the settings objects that are\n\t * created for the tables on a given page.\n\t *\n\t * Note that the `DataTable.settings` object is aliased to\n\t * `jQuery.fn.dataTableExt` through which it may be accessed and\n\t * manipulated, or `jQuery.fn.dataTable.settings`.\n\t *  @member\n\t *  @type array\n\t *  @default []\n\t *  @private\n\t */\n\tDataTable.settings = [];\n\t\n\t/**\n\t * Object models container, for the various models that DataTables has\n\t * available to it. These models define the objects that are used to hold\n\t * the active state and configuration of the table.\n\t *  @namespace\n\t */\n\tDataTable.models = {};\n\t\n\t\n\t\n\t/**\n\t * Template object for the way in which DataTables holds information about\n\t * search information for the global filter and individual column filters.\n\t *  @namespace\n\t */\n\tDataTable.models.oSearch = {\n\t\t/**\n\t\t * Flag to indicate if the filtering should be case insensitive or not\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t */\n\t\t\"bCaseInsensitive\": true,\n\t\n\t\t/**\n\t\t * Applied search term\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t */\n\t\t\"sSearch\": \"\",\n\t\n\t\t/**\n\t\t * Flag to indicate if the search term should be interpreted as a\n\t\t * regular expression (true) or not (false) and therefore and special\n\t\t * regex characters escaped.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bRegex\": false,\n\t\n\t\t/**\n\t\t * Flag to indicate if DataTables is to use its smart filtering or not.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t */\n\t\t\"bSmart\": true,\n\t\n\t\t/**\n\t\t * Flag to indicate if DataTables should only trigger a search when\n\t\t * the return key is pressed.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"return\": false\n\t};\n\t\n\t\n\t\n\t\n\t/**\n\t * Template object for the way in which DataTables holds information about\n\t * each individual row. This is the object format used for the settings\n\t * aoData array.\n\t *  @namespace\n\t */\n\tDataTable.models.oRow = {\n\t\t/**\n\t\t * TR element for the row\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTr\": null,\n\t\n\t\t/**\n\t\t * Array of TD elements for each row. This is null until the row has been\n\t\t * created.\n\t\t *  @type array nodes\n\t\t *  @default []\n\t\t */\n\t\t\"anCells\": null,\n\t\n\t\t/**\n\t\t * Data object from the original data source for the row. This is either\n\t\t * an array if using the traditional form of DataTables, or an object if\n\t\t * using mData options. The exact type will depend on the passed in\n\t\t * data from the data source, or will be an array if using DOM a data\n\t\t * source.\n\t\t *  @type array|object\n\t\t *  @default []\n\t\t */\n\t\t\"_aData\": [],\n\t\n\t\t/**\n\t\t * Sorting data cache - this array is ostensibly the same length as the\n\t\t * number of columns (although each index is generated only as it is\n\t\t * needed), and holds the data that is used for sorting each column in the\n\t\t * row. We do this cache generation at the start of the sort in order that\n\t\t * the formatting of the sort data need be done only once for each cell\n\t\t * per sort. This array should not be read from or written to by anything\n\t\t * other than the master sorting methods.\n\t\t *  @type array\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_aSortData\": null,\n\t\n\t\t/**\n\t\t * Per cell filtering data cache. As per the sort data cache, used to\n\t\t * increase the performance of the filtering in DataTables\n\t\t *  @type array\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_aFilterData\": null,\n\t\n\t\t/**\n\t\t * Filtering data cache. This is the same as the cell filtering cache, but\n\t\t * in this case a string rather than an array. This is easily computed with\n\t\t * a join on `_aFilterData`, but is provided as a cache so the join isn't\n\t\t * needed on every search (memory traded for performance)\n\t\t *  @type array\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_sFilterRow\": null,\n\t\n\t\t/**\n\t\t * Cache of the class name that DataTables has applied to the row, so we\n\t\t * can quickly look at this variable rather than needing to do a DOM check\n\t\t * on className for the nTr property.\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t *  @private\n\t\t */\n\t\t\"_sRowStripe\": \"\",\n\t\n\t\t/**\n\t\t * Denote if the original data source was from the DOM, or the data source\n\t\t * object. This is used for invalidating data, so DataTables can\n\t\t * automatically read data from the original source, unless uninstructed\n\t\t * otherwise.\n\t\t *  @type string\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"src\": null,\n\t\n\t\t/**\n\t\t * Index in the aoData array. This saves an indexOf lookup when we have the\n\t\t * object, but want to know the index\n\t\t *  @type integer\n\t\t *  @default -1\n\t\t *  @private\n\t\t */\n\t\t\"idx\": -1\n\t};\n\t\n\t\n\t/**\n\t * Template object for the column information object in DataTables. This object\n\t * is held in the settings aoColumns array and contains all the information that\n\t * DataTables needs about each individual column.\n\t *\n\t * Note that this object is related to {@link DataTable.defaults.column}\n\t * but this one is the internal data store for DataTables's cache of columns.\n\t * It should NOT be manipulated outside of DataTables. Any configuration should\n\t * be done through the initialisation options.\n\t *  @namespace\n\t */\n\tDataTable.models.oColumn = {\n\t\t/**\n\t\t * Column index. This could be worked out on-the-fly with $.inArray, but it\n\t\t * is faster to just hold it as a variable\n\t\t *  @type integer\n\t\t *  @default null\n\t\t */\n\t\t\"idx\": null,\n\t\n\t\t/**\n\t\t * A list of the columns that sorting should occur on when this column\n\t\t * is sorted. That this property is an array allows multi-column sorting\n\t\t * to be defined for a column (for example first name / last name columns\n\t\t * would benefit from this). The values are integers pointing to the\n\t\t * columns to be sorted on (typically it will be a single integer pointing\n\t\t * at itself, but that doesn't need to be the case).\n\t\t *  @type array\n\t\t */\n\t\t\"aDataSort\": null,\n\t\n\t\t/**\n\t\t * Define the sorting directions that are applied to the column, in sequence\n\t\t * as the column is repeatedly sorted upon - i.e. the first value is used\n\t\t * as the sorting direction when the column if first sorted (clicked on).\n\t\t * Sort it again (click again) and it will move on to the next index.\n\t\t * Repeat until loop.\n\t\t *  @type array\n\t\t */\n\t\t\"asSorting\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if the column is searchable, and thus should be included\n\t\t * in the filtering or not.\n\t\t *  @type boolean\n\t\t */\n\t\t\"bSearchable\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if the column is sortable or not.\n\t\t *  @type boolean\n\t\t */\n\t\t\"bSortable\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if the column is currently visible in the table or not\n\t\t *  @type boolean\n\t\t */\n\t\t\"bVisible\": null,\n\t\n\t\t/**\n\t\t * Store for manual type assignment using the `column.type` option. This\n\t\t * is held in store so we can manipulate the column's `sType` property.\n\t\t *  @type string\n\t\t *  @default null\n\t\t *  @private\n\t\t */\n\t\t\"_sManualType\": null,\n\t\n\t\t/**\n\t\t * Flag to indicate if HTML5 data attributes should be used as the data\n\t\t * source for filtering or sorting. True is either are.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *  @private\n\t\t */\n\t\t\"_bAttrSrc\": false,\n\t\n\t\t/**\n\t\t * Developer definable function that is called whenever a cell is created (Ajax source,\n\t\t * etc) or processed for input (DOM source). This can be used as a compliment to mRender\n\t\t * allowing you to modify the DOM element (add background colour for example) when the\n\t\t * element is available.\n\t\t *  @type function\n\t\t *  @param {element} nTd The TD node that has been created\n\t\t *  @param {*} sData The Data for the cell\n\t\t *  @param {array|object} oData The data for the whole row\n\t\t *  @param {int} iRow The row index for the aoData data store\n\t\t *  @default null\n\t\t */\n\t\t\"fnCreatedCell\": null,\n\t\n\t\t/**\n\t\t * Function to get data from a cell in a column. You should <b>never</b>\n\t\t * access data directly through _aData internally in DataTables - always use\n\t\t * the method attached to this property. It allows mData to function as\n\t\t * required. This function is automatically assigned by the column\n\t\t * initialisation method\n\t\t *  @type function\n\t\t *  @param {array|object} oData The data array/object for the array\n\t\t *    (i.e. aoData[]._aData)\n\t\t *  @param {string} sSpecific The specific data type you want to get -\n\t\t *    'display', 'type' 'filter' 'sort'\n\t\t *  @returns {*} The data for the cell from the given row's data\n\t\t *  @default null\n\t\t */\n\t\t\"fnGetData\": null,\n\t\n\t\t/**\n\t\t * Function to set data for a cell in the column. You should <b>never</b>\n\t\t * set the data directly to _aData internally in DataTables - always use\n\t\t * this method. It allows mData to function as required. This function\n\t\t * is automatically assigned by the column initialisation method\n\t\t *  @type function\n\t\t *  @param {array|object} oData The data array/object for the array\n\t\t *    (i.e. aoData[]._aData)\n\t\t *  @param {*} sValue Value to set\n\t\t *  @default null\n\t\t */\n\t\t\"fnSetData\": null,\n\t\n\t\t/**\n\t\t * Property to read the value for the cells in the column from the data\n\t\t * source array / object. If null, then the default content is used, if a\n\t\t * function is given then the return from the function is used.\n\t\t *  @type function|int|string|null\n\t\t *  @default null\n\t\t */\n\t\t\"mData\": null,\n\t\n\t\t/**\n\t\t * Partner property to mData which is used (only when defined) to get\n\t\t * the data - i.e. it is basically the same as mData, but without the\n\t\t * 'set' option, and also the data fed to it is the result from mData.\n\t\t * This is the rendering method to match the data method of mData.\n\t\t *  @type function|int|string|null\n\t\t *  @default null\n\t\t */\n\t\t\"mRender\": null,\n\t\n\t\t/**\n\t\t * Unique header TH/TD element for this column - this is what the sorting\n\t\t * listener is attached to (if sorting is enabled.)\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTh\": null,\n\t\n\t\t/**\n\t\t * Unique footer TH/TD element for this column (if there is one). Not used\n\t\t * in DataTables as such, but can be used for plug-ins to reference the\n\t\t * footer for each column.\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTf\": null,\n\t\n\t\t/**\n\t\t * The class to apply to all TD elements in the table's TBODY for the column\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sClass\": null,\n\t\n\t\t/**\n\t\t * When DataTables calculates the column widths to assign to each column,\n\t\t * it finds the longest string in each column and then constructs a\n\t\t * temporary table and reads the widths from that. The problem with this\n\t\t * is that \"mmm\" is much wider then \"iiii\", but the latter is a longer\n\t\t * string - thus the calculation can go wrong (doing it properly and putting\n\t\t * it into an DOM object and measuring that is horribly(!) slow). Thus as\n\t\t * a \"work around\" we provide this option. It will append its value to the\n\t\t * text that is found to be the longest string for the column - i.e. padding.\n\t\t *  @type string\n\t\t */\n\t\t\"sContentPadding\": null,\n\t\n\t\t/**\n\t\t * Allows a default value to be given for a column's data, and will be used\n\t\t * whenever a null data source is encountered (this can be because mData\n\t\t * is set to null, or because the data source itself is null).\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sDefaultContent\": null,\n\t\n\t\t/**\n\t\t * Name for the column, allowing reference to the column by name as well as\n\t\t * by index (needs a lookup to work by name).\n\t\t *  @type string\n\t\t */\n\t\t\"sName\": null,\n\t\n\t\t/**\n\t\t * Custom sorting data type - defines which of the available plug-ins in\n\t\t * afnSortData the custom sorting will use - if any is defined.\n\t\t *  @type string\n\t\t *  @default std\n\t\t */\n\t\t\"sSortDataType\": 'std',\n\t\n\t\t/**\n\t\t * Class to be applied to the header element when sorting on this column\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sSortingClass\": null,\n\t\n\t\t/**\n\t\t * Class to be applied to the header element when sorting on this column -\n\t\t * when jQuery UI theming is used.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sSortingClassJUI\": null,\n\t\n\t\t/**\n\t\t * Title of the column - what is seen in the TH element (nTh).\n\t\t *  @type string\n\t\t */\n\t\t\"sTitle\": null,\n\t\n\t\t/**\n\t\t * Column sorting and filtering type\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sType\": null,\n\t\n\t\t/**\n\t\t * Width of the column\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sWidth\": null,\n\t\n\t\t/**\n\t\t * Width of the column when it was first \"encountered\"\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sWidthOrig\": null\n\t};\n\t\n\t\n\t/*\n\t * Developer note: The properties of the object below are given in Hungarian\n\t * notation, that was used as the interface for DataTables prior to v1.10, however\n\t * from v1.10 onwards the primary interface is camel case. In order to avoid\n\t * breaking backwards compatibility utterly with this change, the Hungarian\n\t * version is still, internally the primary interface, but is is not documented\n\t * - hence the @name tags in each doc comment. This allows a Javascript function\n\t * to create a map from Hungarian notation to camel case (going the other direction\n\t * would require each property to be listed, which would add around 3K to the size\n\t * of DataTables, while this method is about a 0.5K hit).\n\t *\n\t * Ultimately this does pave the way for Hungarian notation to be dropped\n\t * completely, but that is a massive amount of work and will break current\n\t * installs (therefore is on-hold until v2).\n\t */\n\t\n\t/**\n\t * Initialisation options that can be given to DataTables at initialisation\n\t * time.\n\t *  @namespace\n\t */\n\tDataTable.defaults = {\n\t\t/**\n\t\t * An array of data to use for the table, passed in at initialisation which\n\t\t * will be used in preference to any data which is already in the DOM. This is\n\t\t * particularly useful for constructing tables purely in Javascript, for\n\t\t * example with a custom Ajax call.\n\t\t *  @type array\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.data\n\t\t *\n\t\t *  @example\n\t\t *    // Using a 2D array data source\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"data\": [\n\t\t *          ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],\n\t\t *          ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],\n\t\t *        ],\n\t\t *        \"columns\": [\n\t\t *          { \"title\": \"Engine\" },\n\t\t *          { \"title\": \"Browser\" },\n\t\t *          { \"title\": \"Platform\" },\n\t\t *          { \"title\": \"Version\" },\n\t\t *          { \"title\": \"Grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using an array of objects as a data source (`data`)\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"data\": [\n\t\t *          {\n\t\t *            \"engine\":   \"Trident\",\n\t\t *            \"browser\":  \"Internet Explorer 4.0\",\n\t\t *            \"platform\": \"Win 95+\",\n\t\t *            \"version\":  4,\n\t\t *            \"grade\":    \"X\"\n\t\t *          },\n\t\t *          {\n\t\t *            \"engine\":   \"Trident\",\n\t\t *            \"browser\":  \"Internet Explorer 5.0\",\n\t\t *            \"platform\": \"Win 95+\",\n\t\t *            \"version\":  5,\n\t\t *            \"grade\":    \"C\"\n\t\t *          }\n\t\t *        ],\n\t\t *        \"columns\": [\n\t\t *          { \"title\": \"Engine\",   \"data\": \"engine\" },\n\t\t *          { \"title\": \"Browser\",  \"data\": \"browser\" },\n\t\t *          { \"title\": \"Platform\", \"data\": \"platform\" },\n\t\t *          { \"title\": \"Version\",  \"data\": \"version\" },\n\t\t *          { \"title\": \"Grade\",    \"data\": \"grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aaData\": null,\n\t\n\t\n\t\t/**\n\t\t * If ordering is enabled, then DataTables will perform a first pass sort on\n\t\t * initialisation. You can define which column(s) the sort is performed\n\t\t * upon, and the sorting direction, with this variable. The `sorting` array\n\t\t * should contain an array for each column to be sorted initially containing\n\t\t * the column's index and a direction string ('asc' or 'desc').\n\t\t *  @type array\n\t\t *  @default [[0,'asc']]\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.order\n\t\t *\n\t\t *  @example\n\t\t *    // Sort by 3rd column first, and then 4th column\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"order\": [[2,'asc'], [3,'desc']]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *    // No initial sorting\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"order\": []\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aaSorting\": [[0,'asc']],\n\t\n\t\n\t\t/**\n\t\t * This parameter is basically identical to the `sorting` parameter, but\n\t\t * cannot be overridden by user interaction with the table. What this means\n\t\t * is that you could have a column (visible or hidden) which the sorting\n\t\t * will always be forced on first - any sorting after that (from the user)\n\t\t * will then be performed as required. This can be useful for grouping rows\n\t\t * together.\n\t\t *  @type array\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.orderFixed\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderFixed\": [[0,'asc']]\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"aaSortingFixed\": [],\n\t\n\t\n\t\t/**\n\t\t * DataTables can be instructed to load data to display in the table from a\n\t\t * Ajax source. This option defines how that Ajax call is made and where to.\n\t\t *\n\t\t * The `ajax` property has three different modes of operation, depending on\n\t\t * how it is defined. These are:\n\t\t *\n\t\t * * `string` - Set the URL from where the data should be loaded from.\n\t\t * * `object` - Define properties for `jQuery.ajax`.\n\t\t * * `function` - Custom data get function\n\t\t *\n\t\t * `string`\n\t\t * --------\n\t\t *\n\t\t * As a string, the `ajax` property simply defines the URL from which\n\t\t * DataTables will load data.\n\t\t *\n\t\t * `object`\n\t\t * --------\n\t\t *\n\t\t * As an object, the parameters in the object are passed to\n\t\t * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control\n\t\t * of the Ajax request. DataTables has a number of default parameters which\n\t\t * you can override using this option. Please refer to the jQuery\n\t\t * documentation for a full description of the options available, although\n\t\t * the following parameters provide additional options in DataTables or\n\t\t * require special consideration:\n\t\t *\n\t\t * * `data` - As with jQuery, `data` can be provided as an object, but it\n\t\t *   can also be used as a function to manipulate the data DataTables sends\n\t\t *   to the server. The function takes a single parameter, an object of\n\t\t *   parameters with the values that DataTables has readied for sending. An\n\t\t *   object may be returned which will be merged into the DataTables\n\t\t *   defaults, or you can add the items to the object that was passed in and\n\t\t *   not return anything from the function. This supersedes `fnServerParams`\n\t\t *   from DataTables 1.9-.\n\t\t *\n\t\t * * `dataSrc` - By default DataTables will look for the property `data` (or\n\t\t *   `aaData` for compatibility with DataTables 1.9-) when obtaining data\n\t\t *   from an Ajax source or for server-side processing - this parameter\n\t\t *   allows that property to be changed. You can use Javascript dotted\n\t\t *   object notation to get a data source for multiple levels of nesting, or\n\t\t *   it my be used as a function. As a function it takes a single parameter,\n\t\t *   the JSON returned from the server, which can be manipulated as\n\t\t *   required, with the returned value being that used by DataTables as the\n\t\t *   data source for the table. This supersedes `sAjaxDataProp` from\n\t\t *   DataTables 1.9-.\n\t\t *\n\t\t * * `success` - Should not be overridden it is used internally in\n\t\t *   DataTables. To manipulate / transform the data returned by the server\n\t\t *   use `ajax.dataSrc`, or use `ajax` as a function (see below).\n\t\t *\n\t\t * `function`\n\t\t * ----------\n\t\t *\n\t\t * As a function, making the Ajax call is left up to yourself allowing\n\t\t * complete control of the Ajax request. Indeed, if desired, a method other\n\t\t * than Ajax could be used to obtain the required data, such as Web storage\n\t\t * or an AIR database.\n\t\t *\n\t\t * The function is given four parameters and no return is required. The\n\t\t * parameters are:\n\t\t *\n\t\t * 1. _object_ - Data to send to the server\n\t\t * 2. _function_ - Callback function that must be executed when the required\n\t\t *    data has been obtained. That data should be passed into the callback\n\t\t *    as the only parameter\n\t\t * 3. _object_ - DataTables settings object for the table\n\t\t *\n\t\t * Note that this supersedes `fnServerData` from DataTables 1.9-.\n\t\t *\n\t\t *  @type string|object|function\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.ajax\n\t\t *  @since 1.10.0\n\t\t *\n\t\t * @example\n\t\t *   // Get JSON data from a file via Ajax.\n\t\t *   // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": \"data.json\"\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Get JSON data from a file via Ajax, using `dataSrc` to change\n\t\t *   // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"dataSrc\": \"tableData\"\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Get JSON data from a file via Ajax, using `dataSrc` to read data\n\t\t *   // from a plain array rather than an array in an object\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"dataSrc\": \"\"\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Manipulate the data returned from the server - add a link to data\n\t\t *   // (note this can, should, be done using `render` for the column - this\n\t\t *   // is just a simple example of how the data can be manipulated).\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"dataSrc\": function ( json ) {\n\t\t *         for ( var i=0, ien=json.length ; i<ien ; i++ ) {\n\t\t *           json[i][0] = '<a href=\"/message/'+json[i][0]+'>View message</a>';\n\t\t *         }\n\t\t *         return json;\n\t\t *       }\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Add data to the request\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"data\": function ( d ) {\n\t\t *         return {\n\t\t *           \"extra_search\": $('#extra').val()\n\t\t *         };\n\t\t *       }\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Send request as POST\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": {\n\t\t *       \"url\": \"data.json\",\n\t\t *       \"type\": \"POST\"\n\t\t *     }\n\t\t *   } );\n\t\t *\n\t\t * @example\n\t\t *   // Get the data from localStorage (could interface with a form for\n\t\t *   // adding, editing and removing rows).\n\t\t *   $('#example').dataTable( {\n\t\t *     \"ajax\": function (data, callback, settings) {\n\t\t *       callback(\n\t\t *         JSON.parse( localStorage.getItem('dataTablesData') )\n\t\t *       );\n\t\t *     }\n\t\t *   } );\n\t\t */\n\t\t\"ajax\": null,\n\t\n\t\n\t\t/**\n\t\t * This parameter allows you to readily specify the entries in the length drop\n\t\t * down menu that DataTables shows when pagination is enabled. It can be\n\t\t * either a 1D array of options which will be used for both the displayed\n\t\t * option and the value, or a 2D array which will use the array in the first\n\t\t * position as the value, and the array in the second position as the\n\t\t * displayed options (useful for language strings such as 'All').\n\t\t *\n\t\t * Note that the `pageLength` property will be automatically set to the\n\t\t * first value given in this array, unless `pageLength` is also provided.\n\t\t *  @type array\n\t\t *  @default [ 10, 25, 50, 100 ]\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.lengthMenu\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"lengthMenu\": [[10, 25, 50, -1], [10, 25, 50, \"All\"]]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aLengthMenu\": [ 10, 25, 50, 100 ],\n\t\n\t\n\t\t/**\n\t\t * The `columns` option in the initialisation parameter allows you to define\n\t\t * details about the way individual columns behave. For a full list of\n\t\t * column options that can be set, please see\n\t\t * {@link DataTable.defaults.column}. Note that if you use `columns` to\n\t\t * define your columns, you must have an entry in the array for every single\n\t\t * column that you have in your table (these can be null if you don't which\n\t\t * to specify any options).\n\t\t *  @member\n\t\t *\n\t\t *  @name DataTable.defaults.column\n\t\t */\n\t\t\"aoColumns\": null,\n\t\n\t\t/**\n\t\t * Very similar to `columns`, `columnDefs` allows you to target a specific\n\t\t * column, multiple columns, or all columns, using the `targets` property of\n\t\t * each object in the array. This allows great flexibility when creating\n\t\t * tables, as the `columnDefs` arrays can be of any length, targeting the\n\t\t * columns you specifically want. `columnDefs` may use any of the column\n\t\t * options available: {@link DataTable.defaults.column}, but it _must_\n\t\t * have `targets` defined in each object in the array. Values in the `targets`\n\t\t * array may be:\n\t\t *   <ul>\n\t\t *     <li>a string - class name will be matched on the TH for the column</li>\n\t\t *     <li>0 or a positive integer - column index counting from the left</li>\n\t\t *     <li>a negative integer - column index counting from the right</li>\n\t\t *     <li>the string \"_all\" - all columns (i.e. assign a default)</li>\n\t\t *   </ul>\n\t\t *  @member\n\t\t *\n\t\t *  @name DataTable.defaults.columnDefs\n\t\t */\n\t\t\"aoColumnDefs\": null,\n\t\n\t\n\t\t/**\n\t\t * Basically the same as `search`, this parameter defines the individual column\n\t\t * filtering state at initialisation time. The array must be of the same size\n\t\t * as the number of columns, and each element be an object with the parameters\n\t\t * `search` and `escapeRegex` (the latter is optional). 'null' is also\n\t\t * accepted and the default will be used.\n\t\t *  @type array\n\t\t *  @default []\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.searchCols\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"searchCols\": [\n\t\t *          null,\n\t\t *          { \"search\": \"My filter\" },\n\t\t *          null,\n\t\t *          { \"search\": \"^[0-9]\", \"escapeRegex\": false }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"aoSearchCols\": [],\n\t\n\t\n\t\t/**\n\t\t * An array of CSS classes that should be applied to displayed rows. This\n\t\t * array may be of any length, and DataTables will apply each class\n\t\t * sequentially, looping when required.\n\t\t *  @type array\n\t\t *  @default null <i>Will take the values determined by the `oClasses.stripe*`\n\t\t *    options</i>\n\t\t *\n\t\t *  @dtopt Option\n\t\t *  @name DataTable.defaults.stripeClasses\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stripeClasses\": [ 'strip1', 'strip2', 'strip3' ]\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"asStripeClasses\": null,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable automatic column width calculation. This can be disabled\n\t\t * as an optimisation (it takes some time to calculate the widths) if the\n\t\t * tables widths are passed in using `columns`.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.autoWidth\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"autoWidth\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bAutoWidth\": true,\n\t\n\t\n\t\t/**\n\t\t * Deferred rendering can provide DataTables with a huge speed boost when you\n\t\t * are using an Ajax or JS data source for the table. This option, when set to\n\t\t * true, will cause DataTables to defer the creation of the table elements for\n\t\t * each row until they are needed for a draw - saving a significant amount of\n\t\t * time.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.deferRender\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajax\": \"sources/arrays.txt\",\n\t\t *        \"deferRender\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bDeferRender\": false,\n\t\n\t\n\t\t/**\n\t\t * Replace a DataTable which matches the given selector and replace it with\n\t\t * one which has the properties of the new initialisation object passed. If no\n\t\t * table matches the selector, then the new DataTable will be constructed as\n\t\t * per normal.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.destroy\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"srollY\": \"200px\",\n\t\t *        \"paginate\": false\n\t\t *      } );\n\t\t *\n\t\t *      // Some time later....\n\t\t *      $('#example').dataTable( {\n\t\t *        \"filter\": false,\n\t\t *        \"destroy\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bDestroy\": false,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable filtering of data. Filtering in DataTables is \"smart\" in\n\t\t * that it allows the end user to input multiple words (space separated) and\n\t\t * will match a row containing those words, even if not in the order that was\n\t\t * specified (this allow matching across multiple columns). Note that if you\n\t\t * wish to use filtering in DataTables this must remain 'true' - to remove the\n\t\t * default filtering input box and retain filtering abilities, please use\n\t\t * {@link DataTable.defaults.dom}.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.searching\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"searching\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bFilter\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the table information display. This shows information\n\t\t * about the data that is currently visible on the page, including information\n\t\t * about filtered data if that action is being performed.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.info\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"info\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bInfo\": true,\n\t\n\t\n\t\t/**\n\t\t * Allows the end user to select the size of a formatted page from a select\n\t\t * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.lengthChange\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"lengthChange\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bLengthChange\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable pagination.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.paging\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"paging\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bPaginate\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the display of a 'processing' indicator when the table is\n\t\t * being processed (e.g. a sort). This is particularly useful for tables with\n\t\t * large amounts of data where it can take a noticeable amount of time to sort\n\t\t * the entries.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.processing\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"processing\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bProcessing\": false,\n\t\n\t\n\t\t/**\n\t\t * Retrieve the DataTables object for the given selector. Note that if the\n\t\t * table has already been initialised, this parameter will cause DataTables\n\t\t * to simply return the object that has already been set up - it will not take\n\t\t * account of any changes you might have made to the initialisation object\n\t\t * passed to DataTables (setting this parameter to true is an acknowledgement\n\t\t * that you understand this). `destroy` can be used to reinitialise a table if\n\t\t * you need.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.retrieve\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      initTable();\n\t\t *      tableActions();\n\t\t *    } );\n\t\t *\n\t\t *    function initTable ()\n\t\t *    {\n\t\t *      return $('#example').dataTable( {\n\t\t *        \"scrollY\": \"200px\",\n\t\t *        \"paginate\": false,\n\t\t *        \"retrieve\": true\n\t\t *      } );\n\t\t *    }\n\t\t *\n\t\t *    function tableActions ()\n\t\t *    {\n\t\t *      var table = initTable();\n\t\t *      // perform API operations with oTable\n\t\t *    }\n\t\t */\n\t\t\"bRetrieve\": false,\n\t\n\t\n\t\t/**\n\t\t * When vertical (y) scrolling is enabled, DataTables will force the height of\n\t\t * the table's viewport to the given height at all times (useful for layout).\n\t\t * However, this can look odd when filtering data down to a small data set,\n\t\t * and the footer is left \"floating\" further down. This parameter (when\n\t\t * enabled) will cause DataTables to collapse the table's viewport down when\n\t\t * the result set will fit within the given Y height.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.scrollCollapse\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollY\": \"200\",\n\t\t *        \"scrollCollapse\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bScrollCollapse\": false,\n\t\n\t\n\t\t/**\n\t\t * Configure DataTables to use server-side processing. Note that the\n\t\t * `ajax` parameter must also be given in order to give DataTables a\n\t\t * source to obtain the required data for each draw.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverSide\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"serverSide\": true,\n\t\t *        \"ajax\": \"xhr.php\"\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bServerSide\": false,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable sorting of columns. Sorting of individual columns can be\n\t\t * disabled by the `sortable` option for each column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.ordering\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ordering\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSort\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or display DataTables' ability to sort multiple columns at the\n\t\t * same time (activated by shift-click by the user).\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.orderMulti\n\t\t *\n\t\t *  @example\n\t\t *    // Disable multiple column sorting ability\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderMulti\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortMulti\": true,\n\t\n\t\n\t\t/**\n\t\t * Allows control over whether DataTables should use the top (true) unique\n\t\t * cell that is found for a single column, or the bottom (false - default).\n\t\t * This is useful when using complex headers.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.orderCellsTop\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderCellsTop\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortCellsTop\": false,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the addition of the classes `sorting\\_1`, `sorting\\_2` and\n\t\t * `sorting\\_3` to the columns which are currently being sorted on. This is\n\t\t * presented as a feature switch as it can increase processing time (while\n\t\t * classes are removed and added) so for large data sets you might want to\n\t\t * turn this off.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.orderClasses\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"orderClasses\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortClasses\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable state saving. When enabled HTML5 `localStorage` will be\n\t\t * used to save table display information such as pagination information,\n\t\t * display length, filtering and sorting. As such when the end user reloads\n\t\t * the page the display display will match what thy had previously set up.\n\t\t *\n\t\t * Due to the use of `localStorage` the default state saving is not supported\n\t\t * in IE6 or 7. If state saving is required in those browsers, use\n\t\t * `stateSaveCallback` to provide a storage solution such as cookies.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.stateSave\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function () {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"bStateSave\": false,\n\t\n\t\n\t\t/**\n\t\t * This function is called when a TR element is created (and all TD child\n\t\t * elements have been inserted), or registered if using a DOM source, allowing\n\t\t * manipulation of the TR element (adding classes etc).\n\t\t *  @type function\n\t\t *  @param {node} row \"TR\" element for the current row\n\t\t *  @param {array} data Raw data array for this row\n\t\t *  @param {int} dataIndex The index of this row in the internal aoData array\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.createdRow\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"createdRow\": function( row, data, dataIndex ) {\n\t\t *          // Bold the grade for all 'A' grade browsers\n\t\t *          if ( data[4] == \"A\" )\n\t\t *          {\n\t\t *            $('td:eq(4)', row).html( '<b>A</b>' );\n\t\t *          }\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnCreatedRow\": null,\n\t\n\t\n\t\t/**\n\t\t * This function is called on every 'draw' event, and allows you to\n\t\t * dynamically modify any aspect you want about the created DOM.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.drawCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"drawCallback\": function( settings ) {\n\t\t *          alert( 'DataTables has redrawn the table' );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnDrawCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * Identical to fnHeaderCallback() but for the table footer this function\n\t\t * allows you to modify the table footer on every 'draw' event.\n\t\t *  @type function\n\t\t *  @param {node} foot \"TR\" element for the footer\n\t\t *  @param {array} data Full table data (as derived from the original HTML)\n\t\t *  @param {int} start Index for the current display starting point in the\n\t\t *    display array\n\t\t *  @param {int} end Index for the current display ending point in the\n\t\t *    display array\n\t\t *  @param {array int} display Index array to translate the visual position\n\t\t *    to the full data array\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.footerCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"footerCallback\": function( tfoot, data, start, end, display ) {\n\t\t *          tfoot.getElementsByTagName('th')[0].innerHTML = \"Starting index is \"+start;\n\t\t *        }\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"fnFooterCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * When rendering large numbers in the information element for the table\n\t\t * (i.e. \"Showing 1 to 10 of 57 entries\") DataTables will render large numbers\n\t\t * to have a comma separator for the 'thousands' units (e.g. 1 million is\n\t\t * rendered as \"1,000,000\") to help readability for the end user. This\n\t\t * function will override the default method DataTables uses.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {int} toFormat number to be formatted\n\t\t *  @returns {string} formatted string for DataTables to show the number\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.formatNumber\n\t\t *\n\t\t *  @example\n\t\t *    // Format a number using a single quote for the separator (note that\n\t\t *    // this can also be done with the language.thousands option)\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"formatNumber\": function ( toFormat ) {\n\t\t *          return toFormat.toString().replace(\n\t\t *            /\\B(?=(\\d{3})+(?!\\d))/g, \"'\"\n\t\t *          );\n\t\t *        };\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnFormatNumber\": function ( toFormat ) {\n\t\t\treturn toFormat.toString().replace(\n\t\t\t\t/\\B(?=(\\d{3})+(?!\\d))/g,\n\t\t\t\tthis.oLanguage.sThousands\n\t\t\t);\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * This function is called on every 'draw' event, and allows you to\n\t\t * dynamically modify the header row. This can be used to calculate and\n\t\t * display useful information about the table.\n\t\t *  @type function\n\t\t *  @param {node} head \"TR\" element for the header\n\t\t *  @param {array} data Full table data (as derived from the original HTML)\n\t\t *  @param {int} start Index for the current display starting point in the\n\t\t *    display array\n\t\t *  @param {int} end Index for the current display ending point in the\n\t\t *    display array\n\t\t *  @param {array int} display Index array to translate the visual position\n\t\t *    to the full data array\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.headerCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"fheaderCallback\": function( head, data, start, end, display ) {\n\t\t *          head.getElementsByTagName('th')[0].innerHTML = \"Displaying \"+(end-start)+\" records\";\n\t\t *        }\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"fnHeaderCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * The information element can be used to convey information about the current\n\t\t * state of the table. Although the internationalisation options presented by\n\t\t * DataTables are quite capable of dealing with most customisations, there may\n\t\t * be times where you wish to customise the string further. This callback\n\t\t * allows you to do exactly that.\n\t\t *  @type function\n\t\t *  @param {object} oSettings DataTables settings object\n\t\t *  @param {int} start Starting position in data for the draw\n\t\t *  @param {int} end End position in data for the draw\n\t\t *  @param {int} max Total number of rows in the table (regardless of\n\t\t *    filtering)\n\t\t *  @param {int} total Total number of rows in the data set, after filtering\n\t\t *  @param {string} pre The string that DataTables has formatted using it's\n\t\t *    own rules\n\t\t *  @returns {string} The string to be displayed in the information element.\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.infoCallback\n\t\t *\n\t\t *  @example\n\t\t *    $('#example').dataTable( {\n\t\t *      \"infoCallback\": function( settings, start, end, max, total, pre ) {\n\t\t *        return start +\" to \"+ end;\n\t\t *      }\n\t\t *    } );\n\t\t */\n\t\t\"fnInfoCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * Called when the table has been initialised. Normally DataTables will\n\t\t * initialise sequentially and there will be no need for this function,\n\t\t * however, this does not hold true when using external language information\n\t\t * since that is obtained using an async XHR call.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} json The JSON object request from the server - only\n\t\t *    present if client-side Ajax sourced data is used\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.initComplete\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"initComplete\": function(settings, json) {\n\t\t *          alert( 'DataTables has finished its initialisation.' );\n\t\t *        }\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"fnInitComplete\": null,\n\t\n\t\n\t\t/**\n\t\t * Called at the very start of each table draw and can be used to cancel the\n\t\t * draw by returning false, any other return (including undefined) results in\n\t\t * the full draw occurring).\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @returns {boolean} False will cancel the draw, anything else (including no\n\t\t *    return) will allow it to complete.\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.preDrawCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"preDrawCallback\": function( settings ) {\n\t\t *          if ( $('#test').val() == 1 ) {\n\t\t *            return false;\n\t\t *          }\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnPreDrawCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * This function allows you to 'post process' each row after it have been\n\t\t * generated for each table draw, but before it is rendered on screen. This\n\t\t * function might be used for setting the row class name etc.\n\t\t *  @type function\n\t\t *  @param {node} row \"TR\" element for the current row\n\t\t *  @param {array} data Raw data array for this row\n\t\t *  @param {int} displayIndex The display index for the current table draw\n\t\t *  @param {int} displayIndexFull The index of the data in the full list of\n\t\t *    rows (after filtering)\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.rowCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"rowCallback\": function( row, data, displayIndex, displayIndexFull ) {\n\t\t *          // Bold the grade for all 'A' grade browsers\n\t\t *          if ( data[4] == \"A\" ) {\n\t\t *            $('td:eq(4)', row).html( '<b>A</b>' );\n\t\t *          }\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnRowCallback\": null,\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * This parameter allows you to override the default function which obtains\n\t\t * the data from the server so something more suitable for your application.\n\t\t * For example you could use POST data, or pull information from a Gears or\n\t\t * AIR database.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {string} source HTTP source to obtain the data from (`ajax`)\n\t\t *  @param {array} data A key/value pair object containing the data to send\n\t\t *    to the server\n\t\t *  @param {function} callback to be called on completion of the data get\n\t\t *    process that will draw the data on the page.\n\t\t *  @param {object} settings DataTables settings object\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverData\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"fnServerData\": null,\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t *  It is often useful to send extra data to the server when making an Ajax\n\t\t * request - for example custom filtering information, and this callback\n\t\t * function makes it trivial to send extra information to the server. The\n\t\t * passed in parameter is the data set that has been constructed by\n\t\t * DataTables, and you can add to this or modify it as you require.\n\t\t *  @type function\n\t\t *  @param {array} data Data array (array of objects which are name/value\n\t\t *    pairs) that has been constructed by DataTables and will be sent to the\n\t\t *    server. In the case of Ajax sourced data with server-side processing\n\t\t *    this will be an empty array, for server-side processing there will be a\n\t\t *    significant number of parameters!\n\t\t *  @returns {undefined} Ensure that you modify the data array passed in,\n\t\t *    as this is passed by reference.\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverParams\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"fnServerParams\": null,\n\t\n\t\n\t\t/**\n\t\t * Load the table state. With this function you can define from where, and how, the\n\t\t * state of a table is loaded. By default DataTables will load from `localStorage`\n\t\t * but you might wish to use a server-side database or cookies.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} callback Callback that can be executed when done. It\n\t\t *    should be passed the loaded state object.\n\t\t *  @return {object} The DataTables state object to be loaded\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateLoadCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoadCallback\": function (settings, callback) {\n\t\t *          $.ajax( {\n\t\t *            \"url\": \"/state_load\",\n\t\t *            \"dataType\": \"json\",\n\t\t *            \"success\": function (json) {\n\t\t *              callback( json );\n\t\t *            }\n\t\t *          } );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateLoadCallback\": function ( settings ) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(\n\t\t\t\t\t(settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(\n\t\t\t\t\t\t'DataTables_'+settings.sInstance+'_'+location.pathname\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Callback which allows modification of the saved state prior to loading that state.\n\t\t * This callback is called when the table is loading state from the stored data, but\n\t\t * prior to the settings object being modified by the saved state. Note that for\n\t\t * plug-in authors, you should use the `stateLoadParams` event to load parameters for\n\t\t * a plug-in.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object that is to be loaded\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateLoadParams\n\t\t *\n\t\t *  @example\n\t\t *    // Remove a saved filter, so filtering is never loaded\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoadParams\": function (settings, data) {\n\t\t *          data.oSearch.sSearch = \"\";\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Disallow state loading by returning false\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoadParams\": function (settings, data) {\n\t\t *          return false;\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateLoadParams\": null,\n\t\n\t\n\t\t/**\n\t\t * Callback that is called when the state has been loaded from the state saving method\n\t\t * and the DataTables settings object has been modified as a result of the loaded state.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object that was loaded\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateLoaded\n\t\t *\n\t\t *  @example\n\t\t *    // Show an alert with the filtering value that was saved\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateLoaded\": function (settings, data) {\n\t\t *          alert( 'Saved filter was: '+data.oSearch.sSearch );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateLoaded\": null,\n\t\n\t\n\t\t/**\n\t\t * Save the table state. This function allows you to define where and how the state\n\t\t * information for the table is stored By default DataTables will use `localStorage`\n\t\t * but you might wish to use a server-side database or cookies.\n\t\t *  @type function\n\t\t *  @member\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object to be saved\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateSaveCallback\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateSaveCallback\": function (settings, data) {\n\t\t *          // Send an Ajax request to the server with the state object\n\t\t *          $.ajax( {\n\t\t *            \"url\": \"/state_save\",\n\t\t *            \"data\": data,\n\t\t *            \"dataType\": \"json\",\n\t\t *            \"method\": \"POST\"\n\t\t *            \"success\": function () {}\n\t\t *          } );\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateSaveCallback\": function ( settings, data ) {\n\t\t\ttry {\n\t\t\t\t(settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(\n\t\t\t\t\t'DataTables_'+settings.sInstance+'_'+location.pathname,\n\t\t\t\t\tJSON.stringify( data )\n\t\t\t\t);\n\t\t\t} catch (e) {}\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Callback which allows modification of the state to be saved. Called when the table\n\t\t * has changed state a new state save is required. This method allows modification of\n\t\t * the state saving object prior to actually doing the save, including addition or\n\t\t * other state properties or modification. Note that for plug-in authors, you should\n\t\t * use the `stateSaveParams` event to save parameters for a plug-in.\n\t\t *  @type function\n\t\t *  @param {object} settings DataTables settings object\n\t\t *  @param {object} data The state object to be saved\n\t\t *\n\t\t *  @dtopt Callbacks\n\t\t *  @name DataTable.defaults.stateSaveParams\n\t\t *\n\t\t *  @example\n\t\t *    // Remove a saved filter, so filtering is never saved\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateSave\": true,\n\t\t *        \"stateSaveParams\": function (settings, data) {\n\t\t *          data.oSearch.sSearch = \"\";\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"fnStateSaveParams\": null,\n\t\n\t\n\t\t/**\n\t\t * Duration for which the saved state information is considered valid. After this period\n\t\t * has elapsed the state will be returned to the default.\n\t\t * Value is given in seconds.\n\t\t *  @type int\n\t\t *  @default 7200 <i>(2 hours)</i>\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.stateDuration\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"stateDuration\": 60*60*24; // 1 day\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"iStateDuration\": 7200,\n\t\n\t\n\t\t/**\n\t\t * When enabled DataTables will not make a request to the server for the first\n\t\t * page draw - rather it will use the data already on the page (no sorting etc\n\t\t * will be applied to it), thus saving on an XHR at load time. `deferLoading`\n\t\t * is used to indicate that deferred loading is required, but it is also used\n\t\t * to tell DataTables how many records there are in the full table (allowing\n\t\t * the information element and pagination to be displayed correctly). In the case\n\t\t * where a filtering is applied to the table on initial load, this can be\n\t\t * indicated by giving the parameter as an array, where the first element is\n\t\t * the number of records available after filtering and the second element is the\n\t\t * number of records without filtering (allowing the table information element\n\t\t * to be shown correctly).\n\t\t *  @type int | array\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.deferLoading\n\t\t *\n\t\t *  @example\n\t\t *    // 57 records available in the table, no filtering applied\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"serverSide\": true,\n\t\t *        \"ajax\": \"scripts/server_processing.php\",\n\t\t *        \"deferLoading\": 57\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // 57 records after filtering, 100 without filtering (an initial filter applied)\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"serverSide\": true,\n\t\t *        \"ajax\": \"scripts/server_processing.php\",\n\t\t *        \"deferLoading\": [ 57, 100 ],\n\t\t *        \"search\": {\n\t\t *          \"search\": \"my_filter\"\n\t\t *        }\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"iDeferLoading\": null,\n\t\n\t\n\t\t/**\n\t\t * Number of rows to display on a single page when using pagination. If\n\t\t * feature enabled (`lengthChange`) then the end user will be able to override\n\t\t * this to a custom setting using a pop-up menu.\n\t\t *  @type int\n\t\t *  @default 10\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.pageLength\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"pageLength\": 50\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"iDisplayLength\": 10,\n\t\n\t\n\t\t/**\n\t\t * Define the starting point for data display when using DataTables with\n\t\t * pagination. Note that this parameter is the number of records, rather than\n\t\t * the page number, so if you have 10 records per page and want to start on\n\t\t * the third page, it should be \"20\".\n\t\t *  @type int\n\t\t *  @default 0\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.displayStart\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"displayStart\": 20\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"iDisplayStart\": 0,\n\t\n\t\n\t\t/**\n\t\t * By default DataTables allows keyboard navigation of the table (sorting, paging,\n\t\t * and filtering) by adding a `tabindex` attribute to the required elements. This\n\t\t * allows you to tab through the controls and press the enter key to activate them.\n\t\t * The tabindex is default 0, meaning that the tab follows the flow of the document.\n\t\t * You can overrule this using this parameter if you wish. Use a value of -1 to\n\t\t * disable built-in keyboard navigation.\n\t\t *  @type int\n\t\t *  @default 0\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.tabIndex\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"tabIndex\": 1\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"iTabIndex\": 0,\n\t\n\t\n\t\t/**\n\t\t * Classes that DataTables assigns to the various components and features\n\t\t * that it adds to the HTML table. This allows classes to be configured\n\t\t * during initialisation in addition to through the static\n\t\t * {@link DataTable.ext.oStdClasses} object).\n\t\t *  @namespace\n\t\t *  @name DataTable.defaults.classes\n\t\t */\n\t\t\"oClasses\": {},\n\t\n\t\n\t\t/**\n\t\t * All strings that DataTables uses in the user interface that it creates\n\t\t * are defined in this object, allowing you to modified them individually or\n\t\t * completely replace them all as required.\n\t\t *  @namespace\n\t\t *  @name DataTable.defaults.language\n\t\t */\n\t\t\"oLanguage\": {\n\t\t\t/**\n\t\t\t * Strings that are used for WAI-ARIA labels and controls only (these are not\n\t\t\t * actually visible on the page, but will be read by screenreaders, and thus\n\t\t\t * must be internationalised as well).\n\t\t\t *  @namespace\n\t\t\t *  @name DataTable.defaults.language.aria\n\t\t\t */\n\t\t\t\"oAria\": {\n\t\t\t\t/**\n\t\t\t\t * ARIA label that is added to the table headers when the column may be\n\t\t\t\t * sorted ascending by activing the column (click or return when focused).\n\t\t\t\t * Note that the column header is prefixed to this string.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default : activate to sort column ascending\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.aria.sortAscending\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"aria\": {\n\t\t\t\t *            \"sortAscending\": \" - click/return to sort ascending\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sSortAscending\": \": activate to sort column ascending\",\n\t\n\t\t\t\t/**\n\t\t\t\t * ARIA label that is added to the table headers when the column may be\n\t\t\t\t * sorted descending by activing the column (click or return when focused).\n\t\t\t\t * Note that the column header is prefixed to this string.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default : activate to sort column ascending\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.aria.sortDescending\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"aria\": {\n\t\t\t\t *            \"sortDescending\": \" - click/return to sort descending\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sSortDescending\": \": activate to sort column descending\"\n\t\t\t},\n\t\n\t\t\t/**\n\t\t\t * Pagination string used by DataTables for the built-in pagination\n\t\t\t * control types.\n\t\t\t *  @namespace\n\t\t\t *  @name DataTable.defaults.language.paginate\n\t\t\t */\n\t\t\t\"oPaginate\": {\n\t\t\t\t/**\n\t\t\t\t * Text to use when using the 'full_numbers' type of pagination for the\n\t\t\t\t * button to take the user to the first page.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default First\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.first\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"first\": \"First page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sFirst\": \"First\",\n\t\n\t\n\t\t\t\t/**\n\t\t\t\t * Text to use when using the 'full_numbers' type of pagination for the\n\t\t\t\t * button to take the user to the last page.\n\t\t\t\t *  @type string\n\t\t\t\t *  @default Last\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.last\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"last\": \"Last page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sLast\": \"Last\",\n\t\n\t\n\t\t\t\t/**\n\t\t\t\t * Text to use for the 'next' pagination button (to take the user to the\n\t\t\t\t * next page).\n\t\t\t\t *  @type string\n\t\t\t\t *  @default Next\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.next\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"next\": \"Next page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sNext\": \"Next\",\n\t\n\t\n\t\t\t\t/**\n\t\t\t\t * Text to use for the 'previous' pagination button (to take the user to\n\t\t\t\t * the previous page).\n\t\t\t\t *  @type string\n\t\t\t\t *  @default Previous\n\t\t\t\t *\n\t\t\t\t *  @dtopt Language\n\t\t\t\t *  @name DataTable.defaults.language.paginate.previous\n\t\t\t\t *\n\t\t\t\t *  @example\n\t\t\t\t *    $(document).ready( function() {\n\t\t\t\t *      $('#example').dataTable( {\n\t\t\t\t *        \"language\": {\n\t\t\t\t *          \"paginate\": {\n\t\t\t\t *            \"previous\": \"Previous page\"\n\t\t\t\t *          }\n\t\t\t\t *        }\n\t\t\t\t *      } );\n\t\t\t\t *    } );\n\t\t\t\t */\n\t\t\t\t\"sPrevious\": \"Previous\"\n\t\t\t},\n\t\n\t\t\t/**\n\t\t\t * This string is shown in preference to `zeroRecords` when the table is\n\t\t\t * empty of data (regardless of filtering). Note that this is an optional\n\t\t\t * parameter - if it is not given, the value of `zeroRecords` will be used\n\t\t\t * instead (either the default or given value).\n\t\t\t *  @type string\n\t\t\t *  @default No data available in table\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.emptyTable\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"emptyTable\": \"No data available in table\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sEmptyTable\": \"No data available in table\",\n\t\n\t\n\t\t\t/**\n\t\t\t * This string gives information to the end user about the information\n\t\t\t * that is current on display on the page. The following tokens can be\n\t\t\t * used in the string and will be dynamically replaced as the table\n\t\t\t * display updates. This tokens can be placed anywhere in the string, or\n\t\t\t * removed as needed by the language requires:\n\t\t\t *\n\t\t\t * * `\\_START\\_` - Display index of the first record on the current page\n\t\t\t * * `\\_END\\_` - Display index of the last record on the current page\n\t\t\t * * `\\_TOTAL\\_` - Number of records in the table after filtering\n\t\t\t * * `\\_MAX\\_` - Number of records in the table without filtering\n\t\t\t * * `\\_PAGE\\_` - Current page number\n\t\t\t * * `\\_PAGES\\_` - Total number of pages of data in the table\n\t\t\t *\n\t\t\t *  @type string\n\t\t\t *  @default Showing _START_ to _END_ of _TOTAL_ entries\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.info\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"info\": \"Showing page _PAGE_ of _PAGES_\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfo\": \"Showing _START_ to _END_ of _TOTAL_ entries\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Display information string for when the table is empty. Typically the\n\t\t\t * format of this string should match `info`.\n\t\t\t *  @type string\n\t\t\t *  @default Showing 0 to 0 of 0 entries\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.infoEmpty\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"infoEmpty\": \"No entries to show\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfoEmpty\": \"Showing 0 to 0 of 0 entries\",\n\t\n\t\n\t\t\t/**\n\t\t\t * When a user filters the information in a table, this string is appended\n\t\t\t * to the information (`info`) to give an idea of how strong the filtering\n\t\t\t * is. The variable _MAX_ is dynamically updated.\n\t\t\t *  @type string\n\t\t\t *  @default (filtered from _MAX_ total entries)\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.infoFiltered\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"infoFiltered\": \" - filtering from _MAX_ records\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfoFiltered\": \"(filtered from _MAX_ total entries)\",\n\t\n\t\n\t\t\t/**\n\t\t\t * If can be useful to append extra information to the info string at times,\n\t\t\t * and this variable does exactly that. This information will be appended to\n\t\t\t * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are\n\t\t\t * being used) at all times.\n\t\t\t *  @type string\n\t\t\t *  @default <i>Empty string</i>\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.infoPostFix\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"infoPostFix\": \"All records shown are derived from real information.\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sInfoPostFix\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * This decimal place operator is a little different from the other\n\t\t\t * language options since DataTables doesn't output floating point\n\t\t\t * numbers, so it won't ever use this for display of a number. Rather,\n\t\t\t * what this parameter does is modify the sort methods of the table so\n\t\t\t * that numbers which are in a format which has a character other than\n\t\t\t * a period (`.`) as a decimal place will be sorted numerically.\n\t\t\t *\n\t\t\t * Note that numbers with different decimal places cannot be shown in\n\t\t\t * the same table and still be sortable, the table must be consistent.\n\t\t\t * However, multiple different tables on the page can use different\n\t\t\t * decimal place characters.\n\t\t\t *  @type string\n\t\t\t *  @default \n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.decimal\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"decimal\": \",\"\n\t\t\t *          \"thousands\": \".\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sDecimal\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * DataTables has a build in number formatter (`formatNumber`) which is\n\t\t\t * used to format large numbers that are used in the table information.\n\t\t\t * By default a comma is used, but this can be trivially changed to any\n\t\t\t * character you wish with this parameter.\n\t\t\t *  @type string\n\t\t\t *  @default ,\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.thousands\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"thousands\": \"'\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sThousands\": \",\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Detail the action that will be taken when the drop down menu for the\n\t\t\t * pagination length option is changed. The '_MENU_' variable is replaced\n\t\t\t * with a default select list of 10, 25, 50 and 100, and can be replaced\n\t\t\t * with a custom select box if required.\n\t\t\t *  @type string\n\t\t\t *  @default Show _MENU_ entries\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.lengthMenu\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Language change only\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"lengthMenu\": \"Display _MENU_ records\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Language and options change\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"lengthMenu\": 'Display <select>'+\n\t\t\t *            '<option value=\"10\">10</option>'+\n\t\t\t *            '<option value=\"20\">20</option>'+\n\t\t\t *            '<option value=\"30\">30</option>'+\n\t\t\t *            '<option value=\"40\">40</option>'+\n\t\t\t *            '<option value=\"50\">50</option>'+\n\t\t\t *            '<option value=\"-1\">All</option>'+\n\t\t\t *            '</select> records'\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sLengthMenu\": \"Show _MENU_ entries\",\n\t\n\t\n\t\t\t/**\n\t\t\t * When using Ajax sourced data and during the first draw when DataTables is\n\t\t\t * gathering the data, this message is shown in an empty row in the table to\n\t\t\t * indicate to the end user the the data is being loaded. Note that this\n\t\t\t * parameter is not used when loading data by server-side processing, just\n\t\t\t * Ajax sourced data with client-side processing.\n\t\t\t *  @type string\n\t\t\t *  @default Loading...\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.loadingRecords\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"loadingRecords\": \"Please wait - loading...\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sLoadingRecords\": \"Loading...\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Text which is displayed when the table is processing a user action\n\t\t\t * (usually a sort command or similar).\n\t\t\t *  @type string\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.processing\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"processing\": \"DataTables is currently busy\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sProcessing\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Details the actions that will be taken when the user types into the\n\t\t\t * filtering input text box. The variable \"_INPUT_\", if used in the string,\n\t\t\t * is replaced with the HTML text box for the filtering input allowing\n\t\t\t * control over where it appears in the string. If \"_INPUT_\" is not given\n\t\t\t * then the input box is appended to the string automatically.\n\t\t\t *  @type string\n\t\t\t *  @default Search:\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.search\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Input text box will be appended at the end automatically\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"search\": \"Filter records:\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Specify where the filter should appear\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"search\": \"Apply filter _INPUT_ to table\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sSearch\": \"Search:\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Assign a `placeholder` attribute to the search `input` element\n\t\t\t *  @type string\n\t\t\t *  @default \n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.searchPlaceholder\n\t\t\t */\n\t\t\t\"sSearchPlaceholder\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * All of the language information can be stored in a file on the\n\t\t\t * server-side, which DataTables will look up if this parameter is passed.\n\t\t\t * It must store the URL of the language file, which is in a JSON format,\n\t\t\t * and the object has the same properties as the oLanguage object in the\n\t\t\t * initialiser object (i.e. the above parameters). Please refer to one of\n\t\t\t * the example language files to see how this works in action.\n\t\t\t *  @type string\n\t\t\t *  @default <i>Empty string - i.e. disabled</i>\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.url\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"url\": \"http://www.sprymedia.co.uk/dataTables/lang.txt\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sUrl\": \"\",\n\t\n\t\n\t\t\t/**\n\t\t\t * Text shown inside the table records when the is no information to be\n\t\t\t * displayed after filtering. `emptyTable` is shown when there is simply no\n\t\t\t * information in the table at all (regardless of filtering).\n\t\t\t *  @type string\n\t\t\t *  @default No matching records found\n\t\t\t *\n\t\t\t *  @dtopt Language\n\t\t\t *  @name DataTable.defaults.language.zeroRecords\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $(document).ready( function() {\n\t\t\t *      $('#example').dataTable( {\n\t\t\t *        \"language\": {\n\t\t\t *          \"zeroRecords\": \"No records to display\"\n\t\t\t *        }\n\t\t\t *      } );\n\t\t\t *    } );\n\t\t\t */\n\t\t\t\"sZeroRecords\": \"No matching records found\"\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * This parameter allows you to have define the global filtering state at\n\t\t * initialisation time. As an object the `search` parameter must be\n\t\t * defined, but all other parameters are optional. When `regex` is true,\n\t\t * the search string will be treated as a regular expression, when false\n\t\t * (default) it will be treated as a straight string. When `smart`\n\t\t * DataTables will use it's smart filtering methods (to word match at\n\t\t * any point in the data), when false this will not be done.\n\t\t *  @namespace\n\t\t *  @extends DataTable.models.oSearch\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.search\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"search\": {\"search\": \"Initial search\"}\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"oSearch\": $.extend( {}, DataTable.models.oSearch ),\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * By default DataTables will look for the property `data` (or `aaData` for\n\t\t * compatibility with DataTables 1.9-) when obtaining data from an Ajax\n\t\t * source or for server-side processing - this parameter allows that\n\t\t * property to be changed. You can use Javascript dotted object notation to\n\t\t * get a data source for multiple levels of nesting.\n\t\t *  @type string\n\t\t *  @default data\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.ajaxDataProp\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"sAjaxDataProp\": \"data\",\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * You can instruct DataTables to load data from an external\n\t\t * source using this parameter (use aData if you want to pass data in you\n\t\t * already have). Simply provide a url a JSON object can be obtained from.\n\t\t *  @type string\n\t\t *  @default null\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.ajaxSource\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"sAjaxSource\": null,\n\t\n\t\n\t\t/**\n\t\t * This initialisation variable allows you to specify exactly where in the\n\t\t * DOM you want DataTables to inject the various controls it adds to the page\n\t\t * (for example you might want the pagination controls at the top of the\n\t\t * table). DIV elements (with or without a custom class) can also be added to\n\t\t * aid styling. The follow syntax is used:\n\t\t *   <ul>\n\t\t *     <li>The following options are allowed:\n\t\t *       <ul>\n\t\t *         <li>'l' - Length changing</li>\n\t\t *         <li>'f' - Filtering input</li>\n\t\t *         <li>'t' - The table!</li>\n\t\t *         <li>'i' - Information</li>\n\t\t *         <li>'p' - Pagination</li>\n\t\t *         <li>'r' - pRocessing</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *     <li>The following constants are allowed:\n\t\t *       <ul>\n\t\t *         <li>'H' - jQueryUI theme \"header\" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>\n\t\t *         <li>'F' - jQueryUI theme \"footer\" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *     <li>The following syntax is expected:\n\t\t *       <ul>\n\t\t *         <li>'&lt;' and '&gt;' - div elements</li>\n\t\t *         <li>'&lt;\"class\" and '&gt;' - div with a class</li>\n\t\t *         <li>'&lt;\"#id\" and '&gt;' - div with an ID</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *     <li>Examples:\n\t\t *       <ul>\n\t\t *         <li>'&lt;\"wrapper\"flipt&gt;'</li>\n\t\t *         <li>'&lt;lf&lt;t&gt;ip&gt;'</li>\n\t\t *       </ul>\n\t\t *     </li>\n\t\t *   </ul>\n\t\t *  @type string\n\t\t *  @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>\n\t\t *    <\"H\"lfr>t<\"F\"ip> <i>(when `jQueryUI` is true)</i>\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.dom\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"dom\": '&lt;\"top\"i&gt;rt&lt;\"bottom\"flp&gt;&lt;\"clear\"&gt;'\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sDom\": \"lfrtip\",\n\t\n\t\n\t\t/**\n\t\t * Search delay option. This will throttle full table searches that use the\n\t\t * DataTables provided search input element (it does not effect calls to\n\t\t * `dt-api search()`, providing a delay before the search is made.\n\t\t *  @type integer\n\t\t *  @default 0\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.searchDelay\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"searchDelay\": 200\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"searchDelay\": null,\n\t\n\t\n\t\t/**\n\t\t * DataTables features six different built-in options for the buttons to\n\t\t * display for pagination control:\n\t\t *\n\t\t * * `numbers` - Page number buttons only\n\t\t * * `simple` - 'Previous' and 'Next' buttons only\n\t\t * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers\n\t\t * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons\n\t\t * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers\n\t\t * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers\n\t\t *  \n\t\t * Further methods can be added using {@link DataTable.ext.oPagination}.\n\t\t *  @type string\n\t\t *  @default simple_numbers\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.pagingType\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"pagingType\": \"full_numbers\"\n\t\t *      } );\n\t\t *    } )\n\t\t */\n\t\t\"sPaginationType\": \"simple_numbers\",\n\t\n\t\n\t\t/**\n\t\t * Enable horizontal scrolling. When a table is too wide to fit into a\n\t\t * certain layout, or you have a large number of columns in the table, you\n\t\t * can enable x-scrolling to show the table in a viewport, which can be\n\t\t * scrolled. This property can be `true` which will allow the table to\n\t\t * scroll horizontally when needed, or any CSS unit, or a number (in which\n\t\t * case it will be treated as a pixel measurement). Setting as simply `true`\n\t\t * is recommended.\n\t\t *  @type boolean|string\n\t\t *  @default <i>blank string - i.e. disabled</i>\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.scrollX\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollX\": true,\n\t\t *        \"scrollCollapse\": true\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sScrollX\": \"\",\n\t\n\t\n\t\t/**\n\t\t * This property can be used to force a DataTable to use more width than it\n\t\t * might otherwise do when x-scrolling is enabled. For example if you have a\n\t\t * table which requires to be well spaced, this parameter is useful for\n\t\t * \"over-sizing\" the table, and thus forcing scrolling. This property can by\n\t\t * any CSS unit, or a number (in which case it will be treated as a pixel\n\t\t * measurement).\n\t\t *  @type string\n\t\t *  @default <i>blank string - i.e. disabled</i>\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @name DataTable.defaults.scrollXInner\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollX\": \"100%\",\n\t\t *        \"scrollXInner\": \"110%\"\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sScrollXInner\": \"\",\n\t\n\t\n\t\t/**\n\t\t * Enable vertical scrolling. Vertical scrolling will constrain the DataTable\n\t\t * to the given height, and enable scrolling for any data which overflows the\n\t\t * current viewport. This can be used as an alternative to paging to display\n\t\t * a lot of data in a small area (although paging and scrolling can both be\n\t\t * enabled at the same time). This property can be any CSS unit, or a number\n\t\t * (in which case it will be treated as a pixel measurement).\n\t\t *  @type string\n\t\t *  @default <i>blank string - i.e. disabled</i>\n\t\t *\n\t\t *  @dtopt Features\n\t\t *  @name DataTable.defaults.scrollY\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"scrollY\": \"200px\",\n\t\t *        \"paginate\": false\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sScrollY\": \"\",\n\t\n\t\n\t\t/**\n\t\t * __Deprecated__ The functionality provided by this parameter has now been\n\t\t * superseded by that provided through `ajax`, which should be used instead.\n\t\t *\n\t\t * Set the HTTP method that is used to make the Ajax call for server-side\n\t\t * processing or Ajax sourced data.\n\t\t *  @type string\n\t\t *  @default GET\n\t\t *\n\t\t *  @dtopt Options\n\t\t *  @dtopt Server-side\n\t\t *  @name DataTable.defaults.serverMethod\n\t\t *\n\t\t *  @deprecated 1.10. Please use `ajax` for this functionality now.\n\t\t */\n\t\t\"sServerMethod\": \"GET\",\n\t\n\t\n\t\t/**\n\t\t * DataTables makes use of renderers when displaying HTML elements for\n\t\t * a table. These renderers can be added or modified by plug-ins to\n\t\t * generate suitable mark-up for a site. For example the Bootstrap\n\t\t * integration plug-in for DataTables uses a paging button renderer to\n\t\t * display pagination buttons in the mark-up required by Bootstrap.\n\t\t *\n\t\t * For further information about the renderers available see\n\t\t * DataTable.ext.renderer\n\t\t *  @type string|object\n\t\t *  @default null\n\t\t *\n\t\t *  @name DataTable.defaults.renderer\n\t\t *\n\t\t */\n\t\t\"renderer\": null,\n\t\n\t\n\t\t/**\n\t\t * Set the data property name that DataTables should use to get a row's id\n\t\t * to set as the `id` property in the node.\n\t\t *  @type string\n\t\t *  @default DT_RowId\n\t\t *\n\t\t *  @name DataTable.defaults.rowId\n\t\t */\n\t\t\"rowId\": \"DT_RowId\"\n\t};\n\t\n\t_fnHungarianMap( DataTable.defaults );\n\t\n\t\n\t\n\t/*\n\t * Developer note - See note in model.defaults.js about the use of Hungarian\n\t * notation and camel case.\n\t */\n\t\n\t/**\n\t * Column options that can be given to DataTables at initialisation time.\n\t *  @namespace\n\t */\n\tDataTable.defaults.column = {\n\t\t/**\n\t\t * Define which column(s) an order will occur on for this column. This\n\t\t * allows a column's ordering to take multiple columns into account when\n\t\t * doing a sort or use the data from a different column. For example first\n\t\t * name / last name columns make sense to do a multi-column sort over the\n\t\t * two columns.\n\t\t *  @type array|int\n\t\t *  @default null <i>Takes the value of the column index automatically</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderData\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderData\": [ 0, 1 ], \"targets\": [ 0 ] },\n\t\t *          { \"orderData\": [ 1, 0 ], \"targets\": [ 1 ] },\n\t\t *          { \"orderData\": 2, \"targets\": [ 2 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"orderData\": [ 0, 1 ] },\n\t\t *          { \"orderData\": [ 1, 0 ] },\n\t\t *          { \"orderData\": 2 },\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"aDataSort\": null,\n\t\t\"iDataSort\": -1,\n\t\n\t\n\t\t/**\n\t\t * You can control the default ordering direction, and even alter the\n\t\t * behaviour of the sort handler (i.e. only allow ascending ordering etc)\n\t\t * using this parameter.\n\t\t *  @type array\n\t\t *  @default [ 'asc', 'desc' ]\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderSequence\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderSequence\": [ \"asc\" ], \"targets\": [ 1 ] },\n\t\t *          { \"orderSequence\": [ \"desc\", \"asc\", \"asc\" ], \"targets\": [ 2 ] },\n\t\t *          { \"orderSequence\": [ \"desc\" ], \"targets\": [ 3 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          { \"orderSequence\": [ \"asc\" ] },\n\t\t *          { \"orderSequence\": [ \"desc\", \"asc\", \"asc\" ] },\n\t\t *          { \"orderSequence\": [ \"desc\" ] },\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"asSorting\": [ 'asc', 'desc' ],\n\t\n\t\n\t\t/**\n\t\t * Enable or disable filtering on the data in this column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @name DataTable.defaults.column.searchable\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"searchable\": false, \"targets\": [ 0 ] }\n\t\t *        ] } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"searchable\": false },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ] } );\n\t\t *    } );\n\t\t */\n\t\t\"bSearchable\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable ordering on this column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderable\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderable\": false, \"targets\": [ 0 ] }\n\t\t *        ] } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"orderable\": false },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ] } );\n\t\t *    } );\n\t\t */\n\t\t\"bSortable\": true,\n\t\n\t\n\t\t/**\n\t\t * Enable or disable the display of this column.\n\t\t *  @type boolean\n\t\t *  @default true\n\t\t *\n\t\t *  @name DataTable.defaults.column.visible\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"visible\": false, \"targets\": [ 0 ] }\n\t\t *        ] } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"visible\": false },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ] } );\n\t\t *    } );\n\t\t */\n\t\t\"bVisible\": true,\n\t\n\t\n\t\t/**\n\t\t * Developer definable function that is called whenever a cell is created (Ajax source,\n\t\t * etc) or processed for input (DOM source). This can be used as a compliment to mRender\n\t\t * allowing you to modify the DOM element (add background colour for example) when the\n\t\t * element is available.\n\t\t *  @type function\n\t\t *  @param {element} td The TD node that has been created\n\t\t *  @param {*} cellData The Data for the cell\n\t\t *  @param {array|object} rowData The data for the whole row\n\t\t *  @param {int} row The row index for the aoData data store\n\t\t *  @param {int} col The column index for aoColumns\n\t\t *\n\t\t *  @name DataTable.defaults.column.createdCell\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [3],\n\t\t *          \"createdCell\": function (td, cellData, rowData, row, col) {\n\t\t *            if ( cellData == \"1.7\" ) {\n\t\t *              $(td).css('color', 'blue')\n\t\t *            }\n\t\t *          }\n\t\t *        } ]\n\t\t *      });\n\t\t *    } );\n\t\t */\n\t\t\"fnCreatedCell\": null,\n\t\n\t\n\t\t/**\n\t\t * This parameter has been replaced by `data` in DataTables to ensure naming\n\t\t * consistency. `dataProp` can still be used, as there is backwards\n\t\t * compatibility in DataTables for this option, but it is strongly\n\t\t * recommended that you use `data` in preference to `dataProp`.\n\t\t *  @name DataTable.defaults.column.dataProp\n\t\t */\n\t\n\t\n\t\t/**\n\t\t * This property can be used to read data from any data source property,\n\t\t * including deeply nested objects / properties. `data` can be given in a\n\t\t * number of different ways which effect its behaviour:\n\t\t *\n\t\t * * `integer` - treated as an array index for the data source. This is the\n\t\t *   default that DataTables uses (incrementally increased for each column).\n\t\t * * `string` - read an object property from the data source. There are\n\t\t *   three 'special' options that can be used in the string to alter how\n\t\t *   DataTables reads the data from the source object:\n\t\t *    * `.` - Dotted Javascript notation. Just as you use a `.` in\n\t\t *      Javascript to read from nested objects, so to can the options\n\t\t *      specified in `data`. For example: `browser.version` or\n\t\t *      `browser.name`. If your object parameter name contains a period, use\n\t\t *      `\\\\` to escape it - i.e. `first\\\\.name`.\n\t\t *    * `[]` - Array notation. DataTables can automatically combine data\n\t\t *      from and array source, joining the data with the characters provided\n\t\t *      between the two brackets. For example: `name[, ]` would provide a\n\t\t *      comma-space separated list from the source array. If no characters\n\t\t *      are provided between the brackets, the original array source is\n\t\t *      returned.\n\t\t *    * `()` - Function notation. Adding `()` to the end of a parameter will\n\t\t *      execute a function of the name given. For example: `browser()` for a\n\t\t *      simple function on the data source, `browser.version()` for a\n\t\t *      function in a nested property or even `browser().version` to get an\n\t\t *      object property if the function called returns an object. Note that\n\t\t *      function notation is recommended for use in `render` rather than\n\t\t *      `data` as it is much simpler to use as a renderer.\n\t\t * * `null` - use the original data source for the row rather than plucking\n\t\t *   data directly from it. This action has effects on two other\n\t\t *   initialisation options:\n\t\t *    * `defaultContent` - When null is given as the `data` option and\n\t\t *      `defaultContent` is specified for the column, the value defined by\n\t\t *      `defaultContent` will be used for the cell.\n\t\t *    * `render` - When null is used for the `data` option and the `render`\n\t\t *      option is specified for the column, the whole data source for the\n\t\t *      row is used for the renderer.\n\t\t * * `function` - the function given will be executed whenever DataTables\n\t\t *   needs to set or get the data for a cell in the column. The function\n\t\t *   takes three parameters:\n\t\t *    * Parameters:\n\t\t *      * `{array|object}` The data source for the row\n\t\t *      * `{string}` The type call data requested - this will be 'set' when\n\t\t *        setting data or 'filter', 'display', 'type', 'sort' or undefined\n\t\t *        when gathering data. Note that when `undefined` is given for the\n\t\t *        type DataTables expects to get the raw data for the object back<\n\t\t *      * `{*}` Data to set when the second parameter is 'set'.\n\t\t *    * Return:\n\t\t *      * The return value from the function is not required when 'set' is\n\t\t *        the type of call, but otherwise the return is what will be used\n\t\t *        for the data requested.\n\t\t *\n\t\t * Note that `data` is a getter and setter option. If you just require\n\t\t * formatting of data for output, you will likely want to use `render` which\n\t\t * is simply a getter and thus simpler to use.\n\t\t *\n\t\t * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The\n\t\t * name change reflects the flexibility of this property and is consistent\n\t\t * with the naming of mRender. If 'mDataProp' is given, then it will still\n\t\t * be used by DataTables, as it automatically maps the old name to the new\n\t\t * if required.\n\t\t *\n\t\t *  @type string|int|function|null\n\t\t *  @default null <i>Use automatically calculated column index</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.data\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Read table data from objects\n\t\t *    // JSON structure for each row:\n\t\t *    //   {\n\t\t *    //      \"engine\": {value},\n\t\t *    //      \"browser\": {value},\n\t\t *    //      \"platform\": {value},\n\t\t *    //      \"version\": {value},\n\t\t *    //      \"grade\": {value}\n\t\t *    //   }\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajaxSource\": \"sources/objects.txt\",\n\t\t *        \"columns\": [\n\t\t *          { \"data\": \"engine\" },\n\t\t *          { \"data\": \"browser\" },\n\t\t *          { \"data\": \"platform\" },\n\t\t *          { \"data\": \"version\" },\n\t\t *          { \"data\": \"grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Read information from deeply nested objects\n\t\t *    // JSON structure for each row:\n\t\t *    //   {\n\t\t *    //      \"engine\": {value},\n\t\t *    //      \"browser\": {value},\n\t\t *    //      \"platform\": {\n\t\t *    //         \"inner\": {value}\n\t\t *    //      },\n\t\t *    //      \"details\": [\n\t\t *    //         {value}, {value}\n\t\t *    //      ]\n\t\t *    //   }\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajaxSource\": \"sources/deep.txt\",\n\t\t *        \"columns\": [\n\t\t *          { \"data\": \"engine\" },\n\t\t *          { \"data\": \"browser\" },\n\t\t *          { \"data\": \"platform.inner\" },\n\t\t *          { \"data\": \"details.0\" },\n\t\t *          { \"data\": \"details.1\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `data` as a function to provide different information for\n\t\t *    // sorting, filtering and display. In this case, currency (price)\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": function ( source, type, val ) {\n\t\t *            if (type === 'set') {\n\t\t *              source.price = val;\n\t\t *              // Store the computed display and filter values for efficiency\n\t\t *              source.price_display = val==\"\" ? \"\" : \"$\"+numberFormat(val);\n\t\t *              source.price_filter  = val==\"\" ? \"\" : \"$\"+numberFormat(val)+\" \"+val;\n\t\t *              return;\n\t\t *            }\n\t\t *            else if (type === 'display') {\n\t\t *              return source.price_display;\n\t\t *            }\n\t\t *            else if (type === 'filter') {\n\t\t *              return source.price_filter;\n\t\t *            }\n\t\t *            // 'sort', 'type' and undefined all just use the integer\n\t\t *            return source.price;\n\t\t *          }\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using default content\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": null,\n\t\t *          \"defaultContent\": \"Click to edit\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using array notation - outputting a list from an array\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": \"name[, ]\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t */\n\t\t\"mData\": null,\n\t\n\t\n\t\t/**\n\t\t * This property is the rendering partner to `data` and it is suggested that\n\t\t * when you want to manipulate data for display (including filtering,\n\t\t * sorting etc) without altering the underlying data for the table, use this\n\t\t * property. `render` can be considered to be the the read only companion to\n\t\t * `data` which is read / write (then as such more complex). Like `data`\n\t\t * this option can be given in a number of different ways to effect its\n\t\t * behaviour:\n\t\t *\n\t\t * * `integer` - treated as an array index for the data source. This is the\n\t\t *   default that DataTables uses (incrementally increased for each column).\n\t\t * * `string` - read an object property from the data source. There are\n\t\t *   three 'special' options that can be used in the string to alter how\n\t\t *   DataTables reads the data from the source object:\n\t\t *    * `.` - Dotted Javascript notation. Just as you use a `.` in\n\t\t *      Javascript to read from nested objects, so to can the options\n\t\t *      specified in `data`. For example: `browser.version` or\n\t\t *      `browser.name`. If your object parameter name contains a period, use\n\t\t *      `\\\\` to escape it - i.e. `first\\\\.name`.\n\t\t *    * `[]` - Array notation. DataTables can automatically combine data\n\t\t *      from and array source, joining the data with the characters provided\n\t\t *      between the two brackets. For example: `name[, ]` would provide a\n\t\t *      comma-space separated list from the source array. If no characters\n\t\t *      are provided between the brackets, the original array source is\n\t\t *      returned.\n\t\t *    * `()` - Function notation. Adding `()` to the end of a parameter will\n\t\t *      execute a function of the name given. For example: `browser()` for a\n\t\t *      simple function on the data source, `browser.version()` for a\n\t\t *      function in a nested property or even `browser().version` to get an\n\t\t *      object property if the function called returns an object.\n\t\t * * `object` - use different data for the different data types requested by\n\t\t *   DataTables ('filter', 'display', 'type' or 'sort'). The property names\n\t\t *   of the object is the data type the property refers to and the value can\n\t\t *   defined using an integer, string or function using the same rules as\n\t\t *   `render` normally does. Note that an `_` option _must_ be specified.\n\t\t *   This is the default value to use if you haven't specified a value for\n\t\t *   the data type requested by DataTables.\n\t\t * * `function` - the function given will be executed whenever DataTables\n\t\t *   needs to set or get the data for a cell in the column. The function\n\t\t *   takes three parameters:\n\t\t *    * Parameters:\n\t\t *      * {array|object} The data source for the row (based on `data`)\n\t\t *      * {string} The type call data requested - this will be 'filter',\n\t\t *        'display', 'type' or 'sort'.\n\t\t *      * {array|object} The full data source for the row (not based on\n\t\t *        `data`)\n\t\t *    * Return:\n\t\t *      * The return value from the function is what will be used for the\n\t\t *        data requested.\n\t\t *\n\t\t *  @type string|int|function|object|null\n\t\t *  @default null Use the data source value.\n\t\t *\n\t\t *  @name DataTable.defaults.column.render\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Create a comma separated list from an array of objects\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"ajaxSource\": \"sources/deep.txt\",\n\t\t *        \"columns\": [\n\t\t *          { \"data\": \"engine\" },\n\t\t *          { \"data\": \"browser\" },\n\t\t *          {\n\t\t *            \"data\": \"platform\",\n\t\t *            \"render\": \"[, ].name\"\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Execute a function to obtain data\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": null, // Use the full data source object for the renderer's source\n\t\t *          \"render\": \"browserName()\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // As an object, extracting different data for the different types\n\t\t *    // This would be used with a data source such as:\n\t\t *    //   { \"phone\": 5552368, \"phone_filter\": \"5552368 555-2368\", \"phone_display\": \"555-2368\" }\n\t\t *    // Here the `phone` integer is used for sorting and type detection, while `phone_filter`\n\t\t *    // (which has both forms) is used for filtering for if a user inputs either format, while\n\t\t *    // the formatted phone number is the one that is shown in the table.\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": null, // Use the full data source object for the renderer's source\n\t\t *          \"render\": {\n\t\t *            \"_\": \"phone\",\n\t\t *            \"filter\": \"phone_filter\",\n\t\t *            \"display\": \"phone_display\"\n\t\t *          }\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Use as a function to create a link from the data source\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"data\": \"download_link\",\n\t\t *          \"render\": function ( data, type, full ) {\n\t\t *            return '<a href=\"'+data+'\">Download</a>';\n\t\t *          }\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"mRender\": null,\n\t\n\t\n\t\t/**\n\t\t * Change the cell type created for the column - either TD cells or TH cells. This\n\t\t * can be useful as TH cells have semantic meaning in the table body, allowing them\n\t\t * to act as a header for a row (you may wish to add scope='row' to the TH elements).\n\t\t *  @type string\n\t\t *  @default td\n\t\t *\n\t\t *  @name DataTable.defaults.column.cellType\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Make the first column use TH cells\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [ {\n\t\t *          \"targets\": [ 0 ],\n\t\t *          \"cellType\": \"th\"\n\t\t *        } ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sCellType\": \"td\",\n\t\n\t\n\t\t/**\n\t\t * Class to give to each cell in this column.\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.class\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"class\": \"my_class\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"class\": \"my_class\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sClass\": \"\",\n\t\n\t\t/**\n\t\t * When DataTables calculates the column widths to assign to each column,\n\t\t * it finds the longest string in each column and then constructs a\n\t\t * temporary table and reads the widths from that. The problem with this\n\t\t * is that \"mmm\" is much wider then \"iiii\", but the latter is a longer\n\t\t * string - thus the calculation can go wrong (doing it properly and putting\n\t\t * it into an DOM object and measuring that is horribly(!) slow). Thus as\n\t\t * a \"work around\" we provide this option. It will append its value to the\n\t\t * text that is found to be the longest string for the column - i.e. padding.\n\t\t * Generally you shouldn't need this!\n\t\t *  @type string\n\t\t *  @default <i>Empty string<i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.contentPadding\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          {\n\t\t *            \"contentPadding\": \"mmm\"\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sContentPadding\": \"\",\n\t\n\t\n\t\t/**\n\t\t * Allows a default value to be given for a column's data, and will be used\n\t\t * whenever a null data source is encountered (this can be because `data`\n\t\t * is set to null, or because the data source itself is null).\n\t\t *  @type string\n\t\t *  @default null\n\t\t *\n\t\t *  @name DataTable.defaults.column.defaultContent\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          {\n\t\t *            \"data\": null,\n\t\t *            \"defaultContent\": \"Edit\",\n\t\t *            \"targets\": [ -1 ]\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          {\n\t\t *            \"data\": null,\n\t\t *            \"defaultContent\": \"Edit\"\n\t\t *          }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sDefaultContent\": null,\n\t\n\t\n\t\t/**\n\t\t * This parameter is only used in DataTables' server-side processing. It can\n\t\t * be exceptionally useful to know what columns are being displayed on the\n\t\t * client side, and to map these to database fields. When defined, the names\n\t\t * also allow DataTables to reorder information from the server if it comes\n\t\t * back in an unexpected order (i.e. if you switch your columns around on the\n\t\t * client-side, your server-side code does not also need updating).\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.name\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"name\": \"engine\", \"targets\": [ 0 ] },\n\t\t *          { \"name\": \"browser\", \"targets\": [ 1 ] },\n\t\t *          { \"name\": \"platform\", \"targets\": [ 2 ] },\n\t\t *          { \"name\": \"version\", \"targets\": [ 3 ] },\n\t\t *          { \"name\": \"grade\", \"targets\": [ 4 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"name\": \"engine\" },\n\t\t *          { \"name\": \"browser\" },\n\t\t *          { \"name\": \"platform\" },\n\t\t *          { \"name\": \"version\" },\n\t\t *          { \"name\": \"grade\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sName\": \"\",\n\t\n\t\n\t\t/**\n\t\t * Defines a data source type for the ordering which can be used to read\n\t\t * real-time information from the table (updating the internally cached\n\t\t * version) prior to ordering. This allows ordering to occur on user\n\t\t * editable elements such as form inputs.\n\t\t *  @type string\n\t\t *  @default std\n\t\t *\n\t\t *  @name DataTable.defaults.column.orderDataType\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"orderDataType\": \"dom-text\", \"targets\": [ 2, 3 ] },\n\t\t *          { \"type\": \"numeric\", \"targets\": [ 3 ] },\n\t\t *          { \"orderDataType\": \"dom-select\", \"targets\": [ 4 ] },\n\t\t *          { \"orderDataType\": \"dom-checkbox\", \"targets\": [ 5 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          null,\n\t\t *          null,\n\t\t *          { \"orderDataType\": \"dom-text\" },\n\t\t *          { \"orderDataType\": \"dom-text\", \"type\": \"numeric\" },\n\t\t *          { \"orderDataType\": \"dom-select\" },\n\t\t *          { \"orderDataType\": \"dom-checkbox\" }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sSortDataType\": \"std\",\n\t\n\t\n\t\t/**\n\t\t * The title of this column.\n\t\t *  @type string\n\t\t *  @default null <i>Derived from the 'TH' value for this column in the\n\t\t *    original HTML table.</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.title\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"title\": \"My column title\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"title\": \"My column title\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sTitle\": null,\n\t\n\t\n\t\t/**\n\t\t * The type allows you to specify how the data for this column will be\n\t\t * ordered. Four types (string, numeric, date and html (which will strip\n\t\t * HTML tags before ordering)) are currently available. Note that only date\n\t\t * formats understood by Javascript's Date() object will be accepted as type\n\t\t * date. For example: \"Mar 26, 2008 5:03 PM\". May take the values: 'string',\n\t\t * 'numeric', 'date' or 'html' (by default). Further types can be adding\n\t\t * through plug-ins.\n\t\t *  @type string\n\t\t *  @default null <i>Auto-detected from raw data</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.type\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"type\": \"html\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"type\": \"html\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sType\": null,\n\t\n\t\n\t\t/**\n\t\t * Defining the width of the column, this parameter may take any CSS value\n\t\t * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not\n\t\t * been given a specific width through this interface ensuring that the table\n\t\t * remains readable.\n\t\t *  @type string\n\t\t *  @default null <i>Automatic</i>\n\t\t *\n\t\t *  @name DataTable.defaults.column.width\n\t\t *  @dtopt Columns\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columnDefs`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columnDefs\": [\n\t\t *          { \"width\": \"20%\", \"targets\": [ 0 ] }\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t *\n\t\t *  @example\n\t\t *    // Using `columns`\n\t\t *    $(document).ready( function() {\n\t\t *      $('#example').dataTable( {\n\t\t *        \"columns\": [\n\t\t *          { \"width\": \"20%\" },\n\t\t *          null,\n\t\t *          null,\n\t\t *          null,\n\t\t *          null\n\t\t *        ]\n\t\t *      } );\n\t\t *    } );\n\t\t */\n\t\t\"sWidth\": null\n\t};\n\t\n\t_fnHungarianMap( DataTable.defaults.column );\n\t\n\t\n\t\n\t/**\n\t * DataTables settings object - this holds all the information needed for a\n\t * given table, including configuration, data and current application of the\n\t * table options. DataTables does not have a single instance for each DataTable\n\t * with the settings attached to that instance, but rather instances of the\n\t * DataTable \"class\" are created on-the-fly as needed (typically by a\n\t * $().dataTable() call) and the settings object is then applied to that\n\t * instance.\n\t *\n\t * Note that this object is related to {@link DataTable.defaults} but this\n\t * one is the internal data store for DataTables's cache of columns. It should\n\t * NOT be manipulated outside of DataTables. Any configuration should be done\n\t * through the initialisation options.\n\t *  @namespace\n\t *  @todo Really should attach the settings object to individual instances so we\n\t *    don't need to create new instances on each $().dataTable() call (if the\n\t *    table already exists). It would also save passing oSettings around and\n\t *    into every single function. However, this is a very significant\n\t *    architecture change for DataTables and will almost certainly break\n\t *    backwards compatibility with older installations. This is something that\n\t *    will be done in 2.0.\n\t */\n\tDataTable.models.oSettings = {\n\t\t/**\n\t\t * Primary features of DataTables and their enablement state.\n\t\t *  @namespace\n\t\t */\n\t\t\"oFeatures\": {\n\t\n\t\t\t/**\n\t\t\t * Flag to say if DataTables should automatically try to calculate the\n\t\t\t * optimum table and columns widths (true) or not (false).\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bAutoWidth\": null,\n\t\n\t\t\t/**\n\t\t\t * Delay the creation of TR and TD elements until they are actually\n\t\t\t * needed by a driven page draw. This can give a significant speed\n\t\t\t * increase for Ajax source and Javascript source data, but makes no\n\t\t\t * difference at all for DOM and server-side processing tables.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bDeferRender\": null,\n\t\n\t\t\t/**\n\t\t\t * Enable filtering on the table or not. Note that if this is disabled\n\t\t\t * then there is no filtering at all on the table, including fnFilter.\n\t\t\t * To just remove the filtering input use sDom and remove the 'f' option.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bFilter\": null,\n\t\n\t\t\t/**\n\t\t\t * Table information element (the 'Showing x of y records' div) enable\n\t\t\t * flag.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bInfo\": null,\n\t\n\t\t\t/**\n\t\t\t * Present a user control allowing the end user to change the page size\n\t\t\t * when pagination is enabled.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bLengthChange\": null,\n\t\n\t\t\t/**\n\t\t\t * Pagination enabled or not. Note that if this is disabled then length\n\t\t\t * changing must also be disabled.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bPaginate\": null,\n\t\n\t\t\t/**\n\t\t\t * Processing indicator enable flag whenever DataTables is enacting a\n\t\t\t * user request - typically an Ajax request for server-side processing.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bProcessing\": null,\n\t\n\t\t\t/**\n\t\t\t * Server-side processing enabled flag - when enabled DataTables will\n\t\t\t * get all data from the server for every draw - there is no filtering,\n\t\t\t * sorting or paging done on the client-side.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bServerSide\": null,\n\t\n\t\t\t/**\n\t\t\t * Sorting enablement flag.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bSort\": null,\n\t\n\t\t\t/**\n\t\t\t * Multi-column sorting\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bSortMulti\": null,\n\t\n\t\t\t/**\n\t\t\t * Apply a class to the columns which are being sorted to provide a\n\t\t\t * visual highlight or not. This can slow things down when enabled since\n\t\t\t * there is a lot of DOM interaction.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bSortClasses\": null,\n\t\n\t\t\t/**\n\t\t\t * State saving enablement flag.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bStateSave\": null\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Scrolling settings for a table.\n\t\t *  @namespace\n\t\t */\n\t\t\"oScroll\": {\n\t\t\t/**\n\t\t\t * When the table is shorter in height than sScrollY, collapse the\n\t\t\t * table container down to the height of the table (when true).\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type boolean\n\t\t\t */\n\t\t\t\"bCollapse\": null,\n\t\n\t\t\t/**\n\t\t\t * Width of the scrollbar for the web-browser's platform. Calculated\n\t\t\t * during table initialisation.\n\t\t\t *  @type int\n\t\t\t *  @default 0\n\t\t\t */\n\t\t\t\"iBarWidth\": 0,\n\t\n\t\t\t/**\n\t\t\t * Viewport width for horizontal scrolling. Horizontal scrolling is\n\t\t\t * disabled if an empty string.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type string\n\t\t\t */\n\t\t\t\"sX\": null,\n\t\n\t\t\t/**\n\t\t\t * Width to expand the table to when using x-scrolling. Typically you\n\t\t\t * should not need to use this.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type string\n\t\t\t *  @deprecated\n\t\t\t */\n\t\t\t\"sXInner\": null,\n\t\n\t\t\t/**\n\t\t\t * Viewport height for vertical scrolling. Vertical scrolling is disabled\n\t\t\t * if an empty string.\n\t\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t\t * set a default use {@link DataTable.defaults}.\n\t\t\t *  @type string\n\t\t\t */\n\t\t\t\"sY\": null\n\t\t},\n\t\n\t\t/**\n\t\t * Language information for the table.\n\t\t *  @namespace\n\t\t *  @extends DataTable.defaults.oLanguage\n\t\t */\n\t\t\"oLanguage\": {\n\t\t\t/**\n\t\t\t * Information callback function. See\n\t\t\t * {@link DataTable.defaults.fnInfoCallback}\n\t\t\t *  @type function\n\t\t\t *  @default null\n\t\t\t */\n\t\t\t\"fnInfoCallback\": null\n\t\t},\n\t\n\t\t/**\n\t\t * Browser support parameters\n\t\t *  @namespace\n\t\t */\n\t\t\"oBrowser\": {\n\t\t\t/**\n\t\t\t * Indicate if the browser incorrectly calculates width:100% inside a\n\t\t\t * scrolling element (IE6/7)\n\t\t\t *  @type boolean\n\t\t\t *  @default false\n\t\t\t */\n\t\t\t\"bScrollOversize\": false,\n\t\n\t\t\t/**\n\t\t\t * Determine if the vertical scrollbar is on the right or left of the\n\t\t\t * scrolling container - needed for rtl language layout, although not\n\t\t\t * all browsers move the scrollbar (Safari).\n\t\t\t *  @type boolean\n\t\t\t *  @default false\n\t\t\t */\n\t\t\t\"bScrollbarLeft\": false,\n\t\n\t\t\t/**\n\t\t\t * Flag for if `getBoundingClientRect` is fully supported or not\n\t\t\t *  @type boolean\n\t\t\t *  @default false\n\t\t\t */\n\t\t\t\"bBounding\": false,\n\t\n\t\t\t/**\n\t\t\t * Browser scrollbar width\n\t\t\t *  @type integer\n\t\t\t *  @default 0\n\t\t\t */\n\t\t\t\"barWidth\": 0\n\t\t},\n\t\n\t\n\t\t\"ajax\": null,\n\t\n\t\n\t\t/**\n\t\t * Array referencing the nodes which are used for the features. The\n\t\t * parameters of this object match what is allowed by sDom - i.e.\n\t\t *   <ul>\n\t\t *     <li>'l' - Length changing</li>\n\t\t *     <li>'f' - Filtering input</li>\n\t\t *     <li>'t' - The table!</li>\n\t\t *     <li>'i' - Information</li>\n\t\t *     <li>'p' - Pagination</li>\n\t\t *     <li>'r' - pRocessing</li>\n\t\t *   </ul>\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aanFeatures\": [],\n\t\n\t\t/**\n\t\t * Store data information - see {@link DataTable.models.oRow} for detailed\n\t\t * information.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoData\": [],\n\t\n\t\t/**\n\t\t * Array of indexes which are in the current display (after filtering etc)\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aiDisplay\": [],\n\t\n\t\t/**\n\t\t * Array of indexes for display - no filtering\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aiDisplayMaster\": [],\n\t\n\t\t/**\n\t\t * Map of row ids to data indexes\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\t\"aIds\": {},\n\t\n\t\t/**\n\t\t * Store information about each column that is in use\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoColumns\": [],\n\t\n\t\t/**\n\t\t * Store information about the table's header\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoHeader\": [],\n\t\n\t\t/**\n\t\t * Store information about the table's footer\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoFooter\": [],\n\t\n\t\t/**\n\t\t * Store the applied global search information in case we want to force a\n\t\t * research or compare the old search to a new one.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @namespace\n\t\t *  @extends DataTable.models.oSearch\n\t\t */\n\t\t\"oPreviousSearch\": {},\n\t\n\t\t/**\n\t\t * Store the applied search for each column - see\n\t\t * {@link DataTable.models.oSearch} for the format that is used for the\n\t\t * filtering information for each column.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoPreSearchCols\": [],\n\t\n\t\t/**\n\t\t * Sorting that is applied to the table. Note that the inner arrays are\n\t\t * used in the following manner:\n\t\t * <ul>\n\t\t *   <li>Index 0 - column number</li>\n\t\t *   <li>Index 1 - current sorting direction</li>\n\t\t * </ul>\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @todo These inner arrays should really be objects\n\t\t */\n\t\t\"aaSorting\": null,\n\t\n\t\t/**\n\t\t * Sorting that is always applied to the table (i.e. prefixed in front of\n\t\t * aaSorting).\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aaSortingFixed\": [],\n\t\n\t\t/**\n\t\t * Classes to use for the striping of a table.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"asStripeClasses\": null,\n\t\n\t\t/**\n\t\t * If restoring a table - we should restore its striping classes as well\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"asDestroyStripes\": [],\n\t\n\t\t/**\n\t\t * If restoring a table - we should restore its width\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"sDestroyWidth\": 0,\n\t\n\t\t/**\n\t\t * Callback functions array for every time a row is inserted (i.e. on a draw).\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoRowCallback\": [],\n\t\n\t\t/**\n\t\t * Callback functions for the header on each draw.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoHeaderCallback\": [],\n\t\n\t\t/**\n\t\t * Callback function for the footer on each draw.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoFooterCallback\": [],\n\t\n\t\t/**\n\t\t * Array of callback functions for draw callback functions\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoDrawCallback\": [],\n\t\n\t\t/**\n\t\t * Array of callback functions for row created function\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoRowCreatedCallback\": [],\n\t\n\t\t/**\n\t\t * Callback functions for just before the table is redrawn. A return of\n\t\t * false will be used to cancel the draw.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoPreDrawCallback\": [],\n\t\n\t\t/**\n\t\t * Callback functions for when the table has been initialised.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoInitComplete\": [],\n\t\n\t\n\t\t/**\n\t\t * Callbacks for modifying the settings to be stored for state saving, prior to\n\t\t * saving state.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateSaveParams\": [],\n\t\n\t\t/**\n\t\t * Callbacks for modifying the settings that have been stored for state saving\n\t\t * prior to using the stored values to restore the state.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateLoadParams\": [],\n\t\n\t\t/**\n\t\t * Callbacks for operating on the settings object once the saved state has been\n\t\t * loaded\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateLoaded\": [],\n\t\n\t\t/**\n\t\t * Cache the table ID for quick access\n\t\t *  @type string\n\t\t *  @default <i>Empty string</i>\n\t\t */\n\t\t\"sTableId\": \"\",\n\t\n\t\t/**\n\t\t * The TABLE node for the main table\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTable\": null,\n\t\n\t\t/**\n\t\t * Permanent ref to the thead element\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTHead\": null,\n\t\n\t\t/**\n\t\t * Permanent ref to the tfoot element - if it exists\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTFoot\": null,\n\t\n\t\t/**\n\t\t * Permanent ref to the tbody element\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTBody\": null,\n\t\n\t\t/**\n\t\t * Cache the wrapper node (contains all DataTables controlled elements)\n\t\t *  @type node\n\t\t *  @default null\n\t\t */\n\t\t\"nTableWrapper\": null,\n\t\n\t\t/**\n\t\t * Indicate if when using server-side processing the loading of data\n\t\t * should be deferred until the second draw.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bDeferLoading\": false,\n\t\n\t\t/**\n\t\t * Indicate if all required information has been read in\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bInitialised\": false,\n\t\n\t\t/**\n\t\t * Information about open rows. Each object in the array has the parameters\n\t\t * 'nTr' and 'nParent'\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoOpenRows\": [],\n\t\n\t\t/**\n\t\t * Dictate the positioning of DataTables' control elements - see\n\t\t * {@link DataTable.model.oInit.sDom}.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sDom\": null,\n\t\n\t\t/**\n\t\t * Search delay (in mS)\n\t\t *  @type integer\n\t\t *  @default null\n\t\t */\n\t\t\"searchDelay\": null,\n\t\n\t\t/**\n\t\t * Which type of pagination should be used.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t *  @default two_button\n\t\t */\n\t\t\"sPaginationType\": \"two_button\",\n\t\n\t\t/**\n\t\t * The state duration (for `stateSave`) in seconds.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"iStateDuration\": 0,\n\t\n\t\t/**\n\t\t * Array of callback functions for state saving. Each array element is an\n\t\t * object with the following parameters:\n\t\t *   <ul>\n\t\t *     <li>function:fn - function to call. Takes two parameters, oSettings\n\t\t *       and the JSON string to save that has been thus far created. Returns\n\t\t *       a JSON string to be inserted into a json object\n\t\t *       (i.e. '\"param\": [ 0, 1, 2]')</li>\n\t\t *     <li>string:sName - name of callback</li>\n\t\t *   </ul>\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateSave\": [],\n\t\n\t\t/**\n\t\t * Array of callback functions for state loading. Each array element is an\n\t\t * object with the following parameters:\n\t\t *   <ul>\n\t\t *     <li>function:fn - function to call. Takes two parameters, oSettings\n\t\t *       and the object stored. May return false to cancel state loading</li>\n\t\t *     <li>string:sName - name of callback</li>\n\t\t *   </ul>\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoStateLoad\": [],\n\t\n\t\t/**\n\t\t * State that was saved. Useful for back reference\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oSavedState\": null,\n\t\n\t\t/**\n\t\t * State that was loaded. Useful for back reference\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oLoadedState\": null,\n\t\n\t\t/**\n\t\t * Source url for AJAX data for the table.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sAjaxSource\": null,\n\t\n\t\t/**\n\t\t * Property from a given object from which to read the table data from. This\n\t\t * can be an empty string (when not server-side processing), in which case\n\t\t * it is  assumed an an array is given directly.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t */\n\t\t\"sAjaxDataProp\": null,\n\t\n\t\t/**\n\t\t * The last jQuery XHR object that was used for server-side data gathering.\n\t\t * This can be used for working with the XHR information in one of the\n\t\t * callbacks\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"jqXHR\": null,\n\t\n\t\t/**\n\t\t * JSON returned from the server in the last Ajax request\n\t\t *  @type object\n\t\t *  @default undefined\n\t\t */\n\t\t\"json\": undefined,\n\t\n\t\t/**\n\t\t * Data submitted as part of the last Ajax request\n\t\t *  @type object\n\t\t *  @default undefined\n\t\t */\n\t\t\"oAjaxData\": undefined,\n\t\n\t\t/**\n\t\t * Function to get the server-side data.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type function\n\t\t */\n\t\t\"fnServerData\": null,\n\t\n\t\t/**\n\t\t * Functions which are called prior to sending an Ajax request so extra\n\t\t * parameters can easily be sent to the server\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoServerParams\": [],\n\t\n\t\t/**\n\t\t * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if\n\t\t * required).\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type string\n\t\t */\n\t\t\"sServerMethod\": null,\n\t\n\t\t/**\n\t\t * Format numbers for display.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type function\n\t\t */\n\t\t\"fnFormatNumber\": null,\n\t\n\t\t/**\n\t\t * List of options that can be used for the user selectable length menu.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aLengthMenu\": null,\n\t\n\t\t/**\n\t\t * Counter for the draws that the table does. Also used as a tracker for\n\t\t * server-side processing\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"iDraw\": 0,\n\t\n\t\t/**\n\t\t * Indicate if a redraw is being done - useful for Ajax\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t */\n\t\t\"bDrawing\": false,\n\t\n\t\t/**\n\t\t * Draw index (iDraw) of the last error when parsing the returned data\n\t\t *  @type int\n\t\t *  @default -1\n\t\t */\n\t\t\"iDrawError\": -1,\n\t\n\t\t/**\n\t\t * Paging display length\n\t\t *  @type int\n\t\t *  @default 10\n\t\t */\n\t\t\"_iDisplayLength\": 10,\n\t\n\t\t/**\n\t\t * Paging start point - aiDisplay index\n\t\t *  @type int\n\t\t *  @default 0\n\t\t */\n\t\t\"_iDisplayStart\": 0,\n\t\n\t\t/**\n\t\t * Server-side processing - number of records in the result set\n\t\t * (i.e. before filtering), Use fnRecordsTotal rather than\n\t\t * this property to get the value of the number of records, regardless of\n\t\t * the server-side processing setting.\n\t\t *  @type int\n\t\t *  @default 0\n\t\t *  @private\n\t\t */\n\t\t\"_iRecordsTotal\": 0,\n\t\n\t\t/**\n\t\t * Server-side processing - number of records in the current display set\n\t\t * (i.e. after filtering). Use fnRecordsDisplay rather than\n\t\t * this property to get the value of the number of records, regardless of\n\t\t * the server-side processing setting.\n\t\t *  @type boolean\n\t\t *  @default 0\n\t\t *  @private\n\t\t */\n\t\t\"_iRecordsDisplay\": 0,\n\t\n\t\t/**\n\t\t * The classes to use for the table\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\t\"oClasses\": {},\n\t\n\t\t/**\n\t\t * Flag attached to the settings object so you can check in the draw\n\t\t * callback if filtering has been done in the draw. Deprecated in favour of\n\t\t * events.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *  @deprecated\n\t\t */\n\t\t\"bFiltered\": false,\n\t\n\t\t/**\n\t\t * Flag attached to the settings object so you can check in the draw\n\t\t * callback if sorting has been done in the draw. Deprecated in favour of\n\t\t * events.\n\t\t *  @type boolean\n\t\t *  @default false\n\t\t *  @deprecated\n\t\t */\n\t\t\"bSorted\": false,\n\t\n\t\t/**\n\t\t * Indicate that if multiple rows are in the header and there is more than\n\t\t * one unique cell per column, if the top one (true) or bottom one (false)\n\t\t * should be used for sorting / title by DataTables.\n\t\t * Note that this parameter will be set by the initialisation routine. To\n\t\t * set a default use {@link DataTable.defaults}.\n\t\t *  @type boolean\n\t\t */\n\t\t\"bSortCellsTop\": null,\n\t\n\t\t/**\n\t\t * Initialisation object that is used for the table\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oInit\": null,\n\t\n\t\t/**\n\t\t * Destroy callback functions - for plug-ins to attach themselves to the\n\t\t * destroy so they can clean up markup and events.\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aoDestroyCallback\": [],\n\t\n\t\n\t\t/**\n\t\t * Get the number of records in the current record set, before filtering\n\t\t *  @type function\n\t\t */\n\t\t\"fnRecordsTotal\": function ()\n\t\t{\n\t\t\treturn _fnDataSource( this ) == 'ssp' ?\n\t\t\t\tthis._iRecordsTotal * 1 :\n\t\t\t\tthis.aiDisplayMaster.length;\n\t\t},\n\t\n\t\t/**\n\t\t * Get the number of records in the current record set, after filtering\n\t\t *  @type function\n\t\t */\n\t\t\"fnRecordsDisplay\": function ()\n\t\t{\n\t\t\treturn _fnDataSource( this ) == 'ssp' ?\n\t\t\t\tthis._iRecordsDisplay * 1 :\n\t\t\t\tthis.aiDisplay.length;\n\t\t},\n\t\n\t\t/**\n\t\t * Get the display end point - aiDisplay index\n\t\t *  @type function\n\t\t */\n\t\t\"fnDisplayEnd\": function ()\n\t\t{\n\t\t\tvar\n\t\t\t\tlen      = this._iDisplayLength,\n\t\t\t\tstart    = this._iDisplayStart,\n\t\t\t\tcalc     = start + len,\n\t\t\t\trecords  = this.aiDisplay.length,\n\t\t\t\tfeatures = this.oFeatures,\n\t\t\t\tpaginate = features.bPaginate;\n\t\n\t\t\tif ( features.bServerSide ) {\n\t\t\t\treturn paginate === false || len === -1 ?\n\t\t\t\t\tstart + records :\n\t\t\t\t\tMath.min( start+len, this._iRecordsDisplay );\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn ! paginate || calc>records || len===-1 ?\n\t\t\t\t\trecords :\n\t\t\t\t\tcalc;\n\t\t\t}\n\t\t},\n\t\n\t\t/**\n\t\t * The DataTables object for this table\n\t\t *  @type object\n\t\t *  @default null\n\t\t */\n\t\t\"oInstance\": null,\n\t\n\t\t/**\n\t\t * Unique identifier for each instance of the DataTables object. If there\n\t\t * is an ID on the table node, then it takes that value, otherwise an\n\t\t * incrementing internal counter is used.\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"sInstance\": null,\n\t\n\t\t/**\n\t\t * tabindex attribute value that is added to DataTables control elements, allowing\n\t\t * keyboard navigation of the table and its controls.\n\t\t */\n\t\t\"iTabIndex\": 0,\n\t\n\t\t/**\n\t\t * DIV container for the footer scrolling table if scrolling\n\t\t */\n\t\t\"nScrollHead\": null,\n\t\n\t\t/**\n\t\t * DIV container for the footer scrolling table if scrolling\n\t\t */\n\t\t\"nScrollFoot\": null,\n\t\n\t\t/**\n\t\t * Last applied sort\n\t\t *  @type array\n\t\t *  @default []\n\t\t */\n\t\t\"aLastSort\": [],\n\t\n\t\t/**\n\t\t * Stored plug-in instances\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\t\"oPlugins\": {},\n\t\n\t\t/**\n\t\t * Function used to get a row's id from the row's data\n\t\t *  @type function\n\t\t *  @default null\n\t\t */\n\t\t\"rowIdFn\": null,\n\t\n\t\t/**\n\t\t * Data location where to store a row's id\n\t\t *  @type string\n\t\t *  @default null\n\t\t */\n\t\t\"rowId\": null\n\t};\n\t\n\t/**\n\t * Extension object for DataTables that is used to provide all extension\n\t * options.\n\t *\n\t * Note that the `DataTable.ext` object is available through\n\t * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is\n\t * also aliased to `jQuery.fn.dataTableExt` for historic reasons.\n\t *  @namespace\n\t *  @extends DataTable.models.ext\n\t */\n\t\n\t\n\t/**\n\t * DataTables extensions\n\t * \n\t * This namespace acts as a collection area for plug-ins that can be used to\n\t * extend DataTables capabilities. Indeed many of the build in methods\n\t * use this method to provide their own capabilities (sorting methods for\n\t * example).\n\t *\n\t * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy\n\t * reasons\n\t *\n\t *  @namespace\n\t */\n\tDataTable.ext = _ext = {\n\t\t/**\n\t\t * Buttons. For use with the Buttons extension for DataTables. This is\n\t\t * defined here so other extensions can define buttons regardless of load\n\t\t * order. It is _not_ used by DataTables core.\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\tbuttons: {},\n\t\n\t\n\t\t/**\n\t\t * Element class names\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\tclasses: {},\n\t\n\t\n\t\t/**\n\t\t * DataTables build type (expanded by the download builder)\n\t\t *\n\t\t *  @type string\n\t\t */\n\t\tbuilder: \"-source-\",\n\t\n\t\n\t\t/**\n\t\t * Error reporting.\n\t\t * \n\t\t * How should DataTables report an error. Can take the value 'alert',\n\t\t * 'throw', 'none' or a function.\n\t\t *\n\t\t *  @type string|function\n\t\t *  @default alert\n\t\t */\n\t\terrMode: \"alert\",\n\t\n\t\n\t\t/**\n\t\t * Feature plug-ins.\n\t\t * \n\t\t * This is an array of objects which describe the feature plug-ins that are\n\t\t * available to DataTables. These feature plug-ins are then available for\n\t\t * use through the `dom` initialisation option.\n\t\t * \n\t\t * Each feature plug-in is described by an object which must have the\n\t\t * following properties:\n\t\t * \n\t\t * * `fnInit` - function that is used to initialise the plug-in,\n\t\t * * `cFeature` - a character so the feature can be enabled by the `dom`\n\t\t *   instillation option. This is case sensitive.\n\t\t *\n\t\t * The `fnInit` function has the following input parameters:\n\t\t *\n\t\t * 1. `{object}` DataTables settings object: see\n\t\t *    {@link DataTable.models.oSettings}\n\t\t *\n\t\t * And the following return is expected:\n\t\t * \n\t\t * * {node|null} The element which contains your feature. Note that the\n\t\t *   return may also be void if your plug-in does not require to inject any\n\t\t *   DOM elements into DataTables control (`dom`) - for example this might\n\t\t *   be useful when developing a plug-in which allows table control via\n\t\t *   keyboard entry\n\t\t *\n\t\t *  @type array\n\t\t *\n\t\t *  @example\n\t\t *    $.fn.dataTable.ext.features.push( {\n\t\t *      \"fnInit\": function( oSettings ) {\n\t\t *        return new TableTools( { \"oDTSettings\": oSettings } );\n\t\t *      },\n\t\t *      \"cFeature\": \"T\"\n\t\t *    } );\n\t\t */\n\t\tfeature: [],\n\t\n\t\n\t\t/**\n\t\t * Row searching.\n\t\t * \n\t\t * This method of searching is complimentary to the default type based\n\t\t * searching, and a lot more comprehensive as it allows you complete control\n\t\t * over the searching logic. Each element in this array is a function\n\t\t * (parameters described below) that is called for every row in the table,\n\t\t * and your logic decides if it should be included in the searching data set\n\t\t * or not.\n\t\t *\n\t\t * Searching functions have the following input parameters:\n\t\t *\n\t\t * 1. `{object}` DataTables settings object: see\n\t\t *    {@link DataTable.models.oSettings}\n\t\t * 2. `{array|object}` Data for the row to be processed (same as the\n\t\t *    original format that was passed in as the data source, or an array\n\t\t *    from a DOM data source\n\t\t * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which\n\t\t *    can be useful to retrieve the `TR` element if you need DOM interaction.\n\t\t *\n\t\t * And the following return is expected:\n\t\t *\n\t\t * * {boolean} Include the row in the searched result set (true) or not\n\t\t *   (false)\n\t\t *\n\t\t * Note that as with the main search ability in DataTables, technically this\n\t\t * is \"filtering\", since it is subtractive. However, for consistency in\n\t\t * naming we call it searching here.\n\t\t *\n\t\t *  @type array\n\t\t *  @default []\n\t\t *\n\t\t *  @example\n\t\t *    // The following example shows custom search being applied to the\n\t\t *    // fourth column (i.e. the data[3] index) based on two input values\n\t\t *    // from the end-user, matching the data in a certain range.\n\t\t *    $.fn.dataTable.ext.search.push(\n\t\t *      function( settings, data, dataIndex ) {\n\t\t *        var min = document.getElementById('min').value * 1;\n\t\t *        var max = document.getElementById('max').value * 1;\n\t\t *        var version = data[3] == \"-\" ? 0 : data[3]*1;\n\t\t *\n\t\t *        if ( min == \"\" && max == \"\" ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        else if ( min == \"\" && version < max ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        else if ( min < version && \"\" == max ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        else if ( min < version && version < max ) {\n\t\t *          return true;\n\t\t *        }\n\t\t *        return false;\n\t\t *      }\n\t\t *    );\n\t\t */\n\t\tsearch: [],\n\t\n\t\n\t\t/**\n\t\t * Selector extensions\n\t\t *\n\t\t * The `selector` option can be used to extend the options available for the\n\t\t * selector modifier options (`selector-modifier` object data type) that\n\t\t * each of the three built in selector types offer (row, column and cell +\n\t\t * their plural counterparts). For example the Select extension uses this\n\t\t * mechanism to provide an option to select only rows, columns and cells\n\t\t * that have been marked as selected by the end user (`{selected: true}`),\n\t\t * which can be used in conjunction with the existing built in selector\n\t\t * options.\n\t\t *\n\t\t * Each property is an array to which functions can be pushed. The functions\n\t\t * take three attributes:\n\t\t *\n\t\t * * Settings object for the host table\n\t\t * * Options object (`selector-modifier` object type)\n\t\t * * Array of selected item indexes\n\t\t *\n\t\t * The return is an array of the resulting item indexes after the custom\n\t\t * selector has been applied.\n\t\t *\n\t\t *  @type object\n\t\t */\n\t\tselector: {\n\t\t\tcell: [],\n\t\t\tcolumn: [],\n\t\t\trow: []\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Internal functions, exposed for used in plug-ins.\n\t\t * \n\t\t * Please note that you should not need to use the internal methods for\n\t\t * anything other than a plug-in (and even then, try to avoid if possible).\n\t\t * The internal function may change between releases.\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t */\n\t\tinternal: {},\n\t\n\t\n\t\t/**\n\t\t * Legacy configuration options. Enable and disable legacy options that\n\t\t * are available in DataTables.\n\t\t *\n\t\t *  @type object\n\t\t */\n\t\tlegacy: {\n\t\t\t/**\n\t\t\t * Enable / disable DataTables 1.9 compatible server-side processing\n\t\t\t * requests\n\t\t\t *\n\t\t\t *  @type boolean\n\t\t\t *  @default null\n\t\t\t */\n\t\t\tajax: null\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Pagination plug-in methods.\n\t\t * \n\t\t * Each entry in this object is a function and defines which buttons should\n\t\t * be shown by the pagination rendering method that is used for the table:\n\t\t * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the\n\t\t * buttons are displayed in the document, while the functions here tell it\n\t\t * what buttons to display. This is done by returning an array of button\n\t\t * descriptions (what each button will do).\n\t\t *\n\t\t * Pagination types (the four built in options and any additional plug-in\n\t\t * options defined here) can be used through the `paginationType`\n\t\t * initialisation parameter.\n\t\t *\n\t\t * The functions defined take two parameters:\n\t\t *\n\t\t * 1. `{int} page` The current page index\n\t\t * 2. `{int} pages` The number of pages in the table\n\t\t *\n\t\t * Each function is expected to return an array where each element of the\n\t\t * array can be one of:\n\t\t *\n\t\t * * `first` - Jump to first page when activated\n\t\t * * `last` - Jump to last page when activated\n\t\t * * `previous` - Show previous page when activated\n\t\t * * `next` - Show next page when activated\n\t\t * * `{int}` - Show page of the index given\n\t\t * * `{array}` - A nested array containing the above elements to add a\n\t\t *   containing 'DIV' element (might be useful for styling).\n\t\t *\n\t\t * Note that DataTables v1.9- used this object slightly differently whereby\n\t\t * an object with two functions would be defined for each plug-in. That\n\t\t * ability is still supported by DataTables 1.10+ to provide backwards\n\t\t * compatibility, but this option of use is now decremented and no longer\n\t\t * documented in DataTables 1.10+.\n\t\t *\n\t\t *  @type object\n\t\t *  @default {}\n\t\t *\n\t\t *  @example\n\t\t *    // Show previous, next and current page buttons only\n\t\t *    $.fn.dataTableExt.oPagination.current = function ( page, pages ) {\n\t\t *      return [ 'previous', page, 'next' ];\n\t\t *    };\n\t\t */\n\t\tpager: {},\n\t\n\t\n\t\trenderer: {\n\t\t\tpageButton: {},\n\t\t\theader: {}\n\t\t},\n\t\n\t\n\t\t/**\n\t\t * Ordering plug-ins - custom data source\n\t\t * \n\t\t * The extension options for ordering of data available here is complimentary\n\t\t * to the default type based ordering that DataTables typically uses. It\n\t\t * allows much greater control over the the data that is being used to\n\t\t * order a column, but is necessarily therefore more complex.\n\t\t * \n\t\t * This type of ordering is useful if you want to do ordering based on data\n\t\t * live from the DOM (for example the contents of an 'input' element) rather\n\t\t * than just the static string that DataTables knows of.\n\t\t * \n\t\t * The way these plug-ins work is that you create an array of the values you\n\t\t * wish to be ordering for the column in question and then return that\n\t\t * array. The data in the array much be in the index order of the rows in\n\t\t * the table (not the currently ordering order!). Which order data gathering\n\t\t * function is run here depends on the `dt-init columns.orderDataType`\n\t\t * parameter that is used for the column (if any).\n\t\t *\n\t\t * The functions defined take two parameters:\n\t\t *\n\t\t * 1. `{object}` DataTables settings object: see\n\t\t *    {@link DataTable.models.oSettings}\n\t\t * 2. `{int}` Target column index\n\t\t *\n\t\t * Each function is expected to return an array:\n\t\t *\n\t\t * * `{array}` Data for the column to be ordering upon\n\t\t *\n\t\t *  @type array\n\t\t *\n\t\t *  @example\n\t\t *    // Ordering using `input` node values\n\t\t *    $.fn.dataTable.ext.order['dom-text'] = function  ( settings, col )\n\t\t *    {\n\t\t *      return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {\n\t\t *        return $('input', td).val();\n\t\t *      } );\n\t\t *    }\n\t\t */\n\t\torder: {},\n\t\n\t\n\t\t/**\n\t\t * Type based plug-ins.\n\t\t *\n\t\t * Each column in DataTables has a type assigned to it, either by automatic\n\t\t * detection or by direct assignment using the `type` option for the column.\n\t\t * The type of a column will effect how it is ordering and search (plug-ins\n\t\t * can also make use of the column type if required).\n\t\t *\n\t\t * @namespace\n\t\t */\n\t\ttype: {\n\t\t\t/**\n\t\t\t * Type detection functions.\n\t\t\t *\n\t\t\t * The functions defined in this object are used to automatically detect\n\t\t\t * a column's type, making initialisation of DataTables super easy, even\n\t\t\t * when complex data is in the table.\n\t\t\t *\n\t\t\t * The functions defined take two parameters:\n\t\t\t *\n\t\t     *  1. `{*}` Data from the column cell to be analysed\n\t\t     *  2. `{settings}` DataTables settings object. This can be used to\n\t\t     *     perform context specific type detection - for example detection\n\t\t     *     based on language settings such as using a comma for a decimal\n\t\t     *     place. Generally speaking the options from the settings will not\n\t\t     *     be required\n\t\t\t *\n\t\t\t * Each function is expected to return:\n\t\t\t *\n\t\t\t * * `{string|null}` Data type detected, or null if unknown (and thus\n\t\t\t *   pass it on to the other type detection functions.\n\t\t\t *\n\t\t\t *  @type array\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Currency type detection plug-in:\n\t\t\t *    $.fn.dataTable.ext.type.detect.push(\n\t\t\t *      function ( data, settings ) {\n\t\t\t *        // Check the numeric part\n\t\t\t *        if ( ! data.substring(1).match(/[0-9]/) ) {\n\t\t\t *          return null;\n\t\t\t *        }\n\t\t\t *\n\t\t\t *        // Check prefixed by currency\n\t\t\t *        if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {\n\t\t\t *          return 'currency';\n\t\t\t *        }\n\t\t\t *        return null;\n\t\t\t *      }\n\t\t\t *    );\n\t\t\t */\n\t\t\tdetect: [],\n\t\n\t\n\t\t\t/**\n\t\t\t * Type based search formatting.\n\t\t\t *\n\t\t\t * The type based searching functions can be used to pre-format the\n\t\t\t * data to be search on. For example, it can be used to strip HTML\n\t\t\t * tags or to de-format telephone numbers for numeric only searching.\n\t\t\t *\n\t\t\t * Note that is a search is not defined for a column of a given type,\n\t\t\t * no search formatting will be performed.\n\t\t\t * \n\t\t\t * Pre-processing of searching data plug-ins - When you assign the sType\n\t\t\t * for a column (or have it automatically detected for you by DataTables\n\t\t\t * or a type detection plug-in), you will typically be using this for\n\t\t\t * custom sorting, but it can also be used to provide custom searching\n\t\t\t * by allowing you to pre-processing the data and returning the data in\n\t\t\t * the format that should be searched upon. This is done by adding\n\t\t\t * functions this object with a parameter name which matches the sType\n\t\t\t * for that target column. This is the corollary of <i>afnSortData</i>\n\t\t\t * for searching data.\n\t\t\t *\n\t\t\t * The functions defined take a single parameter:\n\t\t\t *\n\t\t     *  1. `{*}` Data from the column cell to be prepared for searching\n\t\t\t *\n\t\t\t * Each function is expected to return:\n\t\t\t *\n\t\t\t * * `{string|null}` Formatted string that will be used for the searching.\n\t\t\t *\n\t\t\t *  @type object\n\t\t\t *  @default {}\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {\n\t\t\t *      return d.replace(/\\n/g,\" \").replace( /<.*?>/g, \"\" );\n\t\t\t *    }\n\t\t\t */\n\t\t\tsearch: {},\n\t\n\t\n\t\t\t/**\n\t\t\t * Type based ordering.\n\t\t\t *\n\t\t\t * The column type tells DataTables what ordering to apply to the table\n\t\t\t * when a column is sorted upon. The order for each type that is defined,\n\t\t\t * is defined by the functions available in this object.\n\t\t\t *\n\t\t\t * Each ordering option can be described by three properties added to\n\t\t\t * this object:\n\t\t\t *\n\t\t\t * * `{type}-pre` - Pre-formatting function\n\t\t\t * * `{type}-asc` - Ascending order function\n\t\t\t * * `{type}-desc` - Descending order function\n\t\t\t *\n\t\t\t * All three can be used together, only `{type}-pre` or only\n\t\t\t * `{type}-asc` and `{type}-desc` together. It is generally recommended\n\t\t\t * that only `{type}-pre` is used, as this provides the optimal\n\t\t\t * implementation in terms of speed, although the others are provided\n\t\t\t * for compatibility with existing Javascript sort functions.\n\t\t\t *\n\t\t\t * `{type}-pre`: Functions defined take a single parameter:\n\t\t\t *\n\t\t     *  1. `{*}` Data from the column cell to be prepared for ordering\n\t\t\t *\n\t\t\t * And return:\n\t\t\t *\n\t\t\t * * `{*}` Data to be sorted upon\n\t\t\t *\n\t\t\t * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort\n\t\t\t * functions, taking two parameters:\n\t\t\t *\n\t\t     *  1. `{*}` Data to compare to the second parameter\n\t\t     *  2. `{*}` Data to compare to the first parameter\n\t\t\t *\n\t\t\t * And returning:\n\t\t\t *\n\t\t\t * * `{*}` Ordering match: <0 if first parameter should be sorted lower\n\t\t\t *   than the second parameter, ===0 if the two parameters are equal and\n\t\t\t *   >0 if the first parameter should be sorted height than the second\n\t\t\t *   parameter.\n\t\t\t * \n\t\t\t *  @type object\n\t\t\t *  @default {}\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Numeric ordering of formatted numbers with a pre-formatter\n\t\t\t *    $.extend( $.fn.dataTable.ext.type.order, {\n\t\t\t *      \"string-pre\": function(x) {\n\t\t\t *        a = (a === \"-\" || a === \"\") ? 0 : a.replace( /[^\\d\\-\\.]/g, \"\" );\n\t\t\t *        return parseFloat( a );\n\t\t\t *      }\n\t\t\t *    } );\n\t\t\t *\n\t\t\t *  @example\n\t\t\t *    // Case-sensitive string ordering, with no pre-formatting method\n\t\t\t *    $.extend( $.fn.dataTable.ext.order, {\n\t\t\t *      \"string-case-asc\": function(x,y) {\n\t\t\t *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));\n\t\t\t *      },\n\t\t\t *      \"string-case-desc\": function(x,y) {\n\t\t\t *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));\n\t\t\t *      }\n\t\t\t *    } );\n\t\t\t */\n\t\t\torder: {}\n\t\t},\n\t\n\t\t/**\n\t\t * Unique DataTables instance counter\n\t\t *\n\t\t * @type int\n\t\t * @private\n\t\t */\n\t\t_unique: 0,\n\t\n\t\n\t\t//\n\t\t// Depreciated\n\t\t// The following properties are retained for backwards compatibility only.\n\t\t// The should not be used in new projects and will be removed in a future\n\t\t// version\n\t\t//\n\t\n\t\t/**\n\t\t * Version check function.\n\t\t *  @type function\n\t\t *  @depreciated Since 1.10\n\t\t */\n\t\tfnVersionCheck: DataTable.fnVersionCheck,\n\t\n\t\n\t\t/**\n\t\t * Index for what 'this' index API functions should use\n\t\t *  @type int\n\t\t *  @deprecated Since v1.10\n\t\t */\n\t\tiApiIndex: 0,\n\t\n\t\n\t\t/**\n\t\t * jQuery UI class container\n\t\t *  @type object\n\t\t *  @deprecated Since v1.10\n\t\t */\n\t\toJUIClasses: {},\n\t\n\t\n\t\t/**\n\t\t * Software version\n\t\t *  @type string\n\t\t *  @deprecated Since v1.10\n\t\t */\n\t\tsVersion: DataTable.version\n\t};\n\t\n\t\n\t//\n\t// Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts\n\t//\n\t$.extend( _ext, {\n\t\tafnFiltering: _ext.search,\n\t\taTypes:       _ext.type.detect,\n\t\tofnSearch:    _ext.type.search,\n\t\toSort:        _ext.type.order,\n\t\tafnSortData:  _ext.order,\n\t\taoFeatures:   _ext.feature,\n\t\toApi:         _ext.internal,\n\t\toStdClasses:  _ext.classes,\n\t\toPagination:  _ext.pager\n\t} );\n\t\n\t\n\t$.extend( DataTable.ext.classes, {\n\t\t\"sTable\": \"dataTable\",\n\t\t\"sNoFooter\": \"no-footer\",\n\t\n\t\t/* Paging buttons */\n\t\t\"sPageButton\": \"paginate_button\",\n\t\t\"sPageButtonActive\": \"current\",\n\t\t\"sPageButtonDisabled\": \"disabled\",\n\t\n\t\t/* Striping classes */\n\t\t\"sStripeOdd\": \"odd\",\n\t\t\"sStripeEven\": \"even\",\n\t\n\t\t/* Empty row */\n\t\t\"sRowEmpty\": \"dataTables_empty\",\n\t\n\t\t/* Features */\n\t\t\"sWrapper\": \"dataTables_wrapper\",\n\t\t\"sFilter\": \"dataTables_filter\",\n\t\t\"sInfo\": \"dataTables_info\",\n\t\t\"sPaging\": \"dataTables_paginate paging_\", /* Note that the type is postfixed */\n\t\t\"sLength\": \"dataTables_length\",\n\t\t\"sProcessing\": \"dataTables_processing\",\n\t\n\t\t/* Sorting */\n\t\t\"sSortAsc\": \"sorting_asc\",\n\t\t\"sSortDesc\": \"sorting_desc\",\n\t\t\"sSortable\": \"sorting\", /* Sortable in both directions */\n\t\t\"sSortableAsc\": \"sorting_desc_disabled\",\n\t\t\"sSortableDesc\": \"sorting_asc_disabled\",\n\t\t\"sSortableNone\": \"sorting_disabled\",\n\t\t\"sSortColumn\": \"sorting_\", /* Note that an int is postfixed for the sorting order */\n\t\n\t\t/* Filtering */\n\t\t\"sFilterInput\": \"\",\n\t\n\t\t/* Page length */\n\t\t\"sLengthSelect\": \"\",\n\t\n\t\t/* Scrolling */\n\t\t\"sScrollWrapper\": \"dataTables_scroll\",\n\t\t\"sScrollHead\": \"dataTables_scrollHead\",\n\t\t\"sScrollHeadInner\": \"dataTables_scrollHeadInner\",\n\t\t\"sScrollBody\": \"dataTables_scrollBody\",\n\t\t\"sScrollFoot\": \"dataTables_scrollFoot\",\n\t\t\"sScrollFootInner\": \"dataTables_scrollFootInner\",\n\t\n\t\t/* Misc */\n\t\t\"sHeaderTH\": \"\",\n\t\t\"sFooterTH\": \"\",\n\t\n\t\t// Deprecated\n\t\t\"sSortJUIAsc\": \"\",\n\t\t\"sSortJUIDesc\": \"\",\n\t\t\"sSortJUI\": \"\",\n\t\t\"sSortJUIAscAllowed\": \"\",\n\t\t\"sSortJUIDescAllowed\": \"\",\n\t\t\"sSortJUIWrapper\": \"\",\n\t\t\"sSortIcon\": \"\",\n\t\t\"sJUIHeader\": \"\",\n\t\t\"sJUIFooter\": \"\"\n\t} );\n\t\n\t\n\tvar extPagination = DataTable.ext.pager;\n\t\n\tfunction _numbers ( page, pages ) {\n\t\tvar\n\t\t\tnumbers = [],\n\t\t\tbuttons = extPagination.numbers_length,\n\t\t\thalf = Math.floor( buttons / 2 ),\n\t\t\ti = 1;\n\t\n\t\tif ( pages <= buttons ) {\n\t\t\tnumbers = _range( 0, pages );\n\t\t}\n\t\telse if ( page <= half ) {\n\t\t\tnumbers = _range( 0, buttons-2 );\n\t\t\tnumbers.push( 'ellipsis' );\n\t\t\tnumbers.push( pages-1 );\n\t\t}\n\t\telse if ( page >= pages - 1 - half ) {\n\t\t\tnumbers = _range( pages-(buttons-2), pages );\n\t\t\tnumbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6\n\t\t\tnumbers.splice( 0, 0, 0 );\n\t\t}\n\t\telse {\n\t\t\tnumbers = _range( page-half+2, page+half-1 );\n\t\t\tnumbers.push( 'ellipsis' );\n\t\t\tnumbers.push( pages-1 );\n\t\t\tnumbers.splice( 0, 0, 'ellipsis' );\n\t\t\tnumbers.splice( 0, 0, 0 );\n\t\t}\n\t\n\t\tnumbers.DT_el = 'span';\n\t\treturn numbers;\n\t}\n\t\n\t\n\t$.extend( extPagination, {\n\t\tsimple: function ( page, pages ) {\n\t\t\treturn [ 'previous', 'next' ];\n\t\t},\n\t\n\t\tfull: function ( page, pages ) {\n\t\t\treturn [  'first', 'previous', 'next', 'last' ];\n\t\t},\n\t\n\t\tnumbers: function ( page, pages ) {\n\t\t\treturn [ _numbers(page, pages) ];\n\t\t},\n\t\n\t\tsimple_numbers: function ( page, pages ) {\n\t\t\treturn [ 'previous', _numbers(page, pages), 'next' ];\n\t\t},\n\t\n\t\tfull_numbers: function ( page, pages ) {\n\t\t\treturn [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];\n\t\t},\n\t\t\n\t\tfirst_last_numbers: function (page, pages) {\n\t \t\treturn ['first', _numbers(page, pages), 'last'];\n\t \t},\n\t\n\t\t// For testing and plug-ins to use\n\t\t_numbers: _numbers,\n\t\n\t\t// Number of number buttons (including ellipsis) to show. _Must be odd!_\n\t\tnumbers_length: 7\n\t} );\n\t\n\t\n\t$.extend( true, DataTable.ext.renderer, {\n\t\tpageButton: {\n\t\t\t_: function ( settings, host, idx, buttons, page, pages ) {\n\t\t\t\tvar classes = settings.oClasses;\n\t\t\t\tvar lang = settings.oLanguage.oPaginate;\n\t\t\t\tvar aria = settings.oLanguage.oAria.paginate || {};\n\t\t\t\tvar btnDisplay, btnClass;\n\t\n\t\t\t\tvar attach = function( container, buttons ) {\n\t\t\t\t\tvar i, ien, node, button, tabIndex;\n\t\t\t\t\tvar disabledClass = classes.sPageButtonDisabled;\n\t\t\t\t\tvar clickHandler = function ( e ) {\n\t\t\t\t\t\t_fnPageChange( settings, e.data.action, true );\n\t\t\t\t\t};\n\t\n\t\t\t\t\tfor ( i=0, ien=buttons.length ; i<ien ; i++ ) {\n\t\t\t\t\t\tbutton = buttons[i];\n\t\n\t\t\t\t\t\tif ( Array.isArray( button ) ) {\n\t\t\t\t\t\t\tvar inner = $( '<'+(button.DT_el || 'div')+'/>' )\n\t\t\t\t\t\t\t\t.appendTo( container );\n\t\t\t\t\t\t\tattach( inner, button );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tbtnDisplay = null;\n\t\t\t\t\t\t\tbtnClass = button;\n\t\t\t\t\t\t\ttabIndex = settings.iTabIndex;\n\t\n\t\t\t\t\t\t\tswitch ( button ) {\n\t\t\t\t\t\t\t\tcase 'ellipsis':\n\t\t\t\t\t\t\t\t\tcontainer.append('<span class=\"ellipsis\">&#x2026;</span>');\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'first':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sFirst;\n\t\n\t\t\t\t\t\t\t\t\tif ( page === 0 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'previous':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sPrevious;\n\t\n\t\t\t\t\t\t\t\t\tif ( page === 0 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'next':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sNext;\n\t\n\t\t\t\t\t\t\t\t\tif ( pages === 0 || page === pages-1 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tcase 'last':\n\t\t\t\t\t\t\t\t\tbtnDisplay = lang.sLast;\n\t\n\t\t\t\t\t\t\t\t\tif ( pages === 0 || page === pages-1 ) {\n\t\t\t\t\t\t\t\t\t\ttabIndex = -1;\n\t\t\t\t\t\t\t\t\t\tbtnClass += ' ' + disabledClass;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tbtnDisplay = settings.fnFormatNumber( button + 1 );\n\t\t\t\t\t\t\t\t\tbtnClass = page === button ?\n\t\t\t\t\t\t\t\t\t\tclasses.sPageButtonActive : '';\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\tif ( btnDisplay !== null ) {\n\t\t\t\t\t\t\t\tvar tag = settings.oInit.pagingTag || 'a';\n\t\t\t\t\t\t\t\tvar disabled = btnClass.indexOf(disabledClass) !== -1;\n\t\t\t\n\t\n\t\t\t\t\t\t\t\tnode = $('<'+tag+'>', {\n\t\t\t\t\t\t\t\t\t\t'class': classes.sPageButton+' '+btnClass,\n\t\t\t\t\t\t\t\t\t\t'aria-controls': settings.sTableId,\n\t\t\t\t\t\t\t\t\t\t'aria-disabled': disabled ? 'true' : null,\n\t\t\t\t\t\t\t\t\t\t'aria-label': aria[ button ],\n\t\t\t\t\t\t\t\t\t\t'role': 'link',\n\t\t\t\t\t\t\t\t\t\t'aria-current': btnClass === classes.sPageButtonActive ? 'page' : null,\n\t\t\t\t\t\t\t\t\t\t'data-dt-idx': button,\n\t\t\t\t\t\t\t\t\t\t'tabindex': tabIndex,\n\t\t\t\t\t\t\t\t\t\t'id': idx === 0 && typeof button === 'string' ?\n\t\t\t\t\t\t\t\t\t\t\tsettings.sTableId +'_'+ button :\n\t\t\t\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t\t\t.html( btnDisplay )\n\t\t\t\t\t\t\t\t\t.appendTo( container );\n\t\n\t\t\t\t\t\t\t\t_fnBindAction(\n\t\t\t\t\t\t\t\t\tnode, {action: button}, clickHandler\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\n\t\t\t\t// IE9 throws an 'unknown error' if document.activeElement is used\n\t\t\t\t// inside an iframe or frame. Try / catch the error. Not good for\n\t\t\t\t// accessibility, but neither are frames.\n\t\t\t\tvar activeEl;\n\t\n\t\t\t\ttry {\n\t\t\t\t\t// Because this approach is destroying and recreating the paging\n\t\t\t\t\t// elements, focus is lost on the select button which is bad for\n\t\t\t\t\t// accessibility. So we want to restore focus once the draw has\n\t\t\t\t\t// completed\n\t\t\t\t\tactiveEl = $(host).find(document.activeElement).data('dt-idx');\n\t\t\t\t}\n\t\t\t\tcatch (e) {}\n\t\n\t\t\t\tattach( $(host).empty(), buttons );\n\t\n\t\t\t\tif ( activeEl !== undefined ) {\n\t\t\t\t\t$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} );\n\t\n\t\n\t\n\t// Built in type detection. See model.ext.aTypes for information about\n\t// what is required from this methods.\n\t$.extend( DataTable.ext.type.detect, [\n\t\t// Plain numbers - first since V8 detects some plain numbers as dates\n\t\t// e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _isNumber( d, decimal ) ? 'num'+decimal : null;\n\t\t},\n\t\n\t\t// Dates (only those recognised by the browser's Date.parse)\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\t// V8 tries _very_ hard to make a string passed into `Date.parse()`\n\t\t\t// valid, so we need to use a regex to restrict date formats. Use a\n\t\t\t// plug-in for anything other than ISO8601 style strings\n\t\t\tif ( d && !(d instanceof Date) && ! _re_date.test(d) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tvar parsed = Date.parse(d);\n\t\t\treturn (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;\n\t\t},\n\t\n\t\t// Formatted numbers\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;\n\t\t},\n\t\n\t\t// HTML numeric\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;\n\t\t},\n\t\n\t\t// HTML numeric, formatted\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\tvar decimal = settings.oLanguage.sDecimal;\n\t\t\treturn _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;\n\t\t},\n\t\n\t\t// HTML (this is strict checking - there must be html)\n\t\tfunction ( d, settings )\n\t\t{\n\t\t\treturn _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?\n\t\t\t\t'html' : null;\n\t\t}\n\t] );\n\t\n\t\n\t\n\t// Filter formatting functions. See model.ext.ofnSearch for information about\n\t// what is required from these methods.\n\t// \n\t// Note that additional search methods are added for the html numbers and\n\t// html formatted numbers by `_addNumericSort()` when we know what the decimal\n\t// place is\n\t\n\t\n\t$.extend( DataTable.ext.type.search, {\n\t\thtml: function ( data ) {\n\t\t\treturn _empty(data) ?\n\t\t\t\tdata :\n\t\t\t\ttypeof data === 'string' ?\n\t\t\t\t\tdata\n\t\t\t\t\t\t.replace( _re_new_lines, \" \" )\n\t\t\t\t\t\t.replace( _re_html, \"\" ) :\n\t\t\t\t\t'';\n\t\t},\n\t\n\t\tstring: function ( data ) {\n\t\t\treturn _empty(data) ?\n\t\t\t\tdata :\n\t\t\t\ttypeof data === 'string' ?\n\t\t\t\t\tdata.replace( _re_new_lines, \" \" ) :\n\t\t\t\t\tdata;\n\t\t}\n\t} );\n\t\n\t\n\t\n\tvar __numericReplace = function ( d, decimalPlace, re1, re2 ) {\n\t\tif ( d !== 0 && (!d || d === '-') ) {\n\t\t\treturn -Infinity;\n\t\t}\n\t\t\n\t\tvar type = typeof d;\n\t\n\t\tif (type === 'number' || type === 'bigint') {\n\t\t\treturn d;\n\t\t}\n\t\n\t\t// If a decimal place other than `.` is used, it needs to be given to the\n\t\t// function so we can detect it and replace with a `.` which is the only\n\t\t// decimal place Javascript recognises - it is not locale aware.\n\t\tif ( decimalPlace ) {\n\t\t\td = _numToDecimal( d, decimalPlace );\n\t\t}\n\t\n\t\tif ( d.replace ) {\n\t\t\tif ( re1 ) {\n\t\t\t\td = d.replace( re1, '' );\n\t\t\t}\n\t\n\t\t\tif ( re2 ) {\n\t\t\t\td = d.replace( re2, '' );\n\t\t\t}\n\t\t}\n\t\n\t\treturn d * 1;\n\t};\n\t\n\t\n\t// Add the numeric 'deformatting' functions for sorting and search. This is done\n\t// in a function to provide an easy ability for the language options to add\n\t// additional methods if a non-period decimal place is used.\n\tfunction _addNumericSort ( decimalPlace ) {\n\t\t$.each(\n\t\t\t{\n\t\t\t\t// Plain numbers\n\t\t\t\t\"num\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace );\n\t\t\t\t},\n\t\n\t\t\t\t// Formatted numbers\n\t\t\t\t\"num-fmt\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace, _re_formatted_numeric );\n\t\t\t\t},\n\t\n\t\t\t\t// HTML numeric\n\t\t\t\t\"html-num\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace, _re_html );\n\t\t\t\t},\n\t\n\t\t\t\t// HTML numeric, formatted\n\t\t\t\t\"html-num-fmt\": function ( d ) {\n\t\t\t\t\treturn __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );\n\t\t\t\t}\n\t\t\t},\n\t\t\tfunction ( key, fn ) {\n\t\t\t\t// Add the ordering method\n\t\t\t\t_ext.type.order[ key+decimalPlace+'-pre' ] = fn;\n\t\n\t\t\t\t// For HTML types add a search formatter that will strip the HTML\n\t\t\t\tif ( key.match(/^html\\-/) ) {\n\t\t\t\t\t_ext.type.search[ key+decimalPlace ] = _ext.type.search.html;\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}\n\t\n\t\n\t// Default sort methods\n\t$.extend( _ext.type.order, {\n\t\t// Dates\n\t\t\"date-pre\": function ( d ) {\n\t\t\tvar ts = Date.parse( d );\n\t\t\treturn isNaN(ts) ? -Infinity : ts;\n\t\t},\n\t\n\t\t// html\n\t\t\"html-pre\": function ( a ) {\n\t\t\treturn _empty(a) ?\n\t\t\t\t'' :\n\t\t\t\ta.replace ?\n\t\t\t\t\ta.replace( /<.*?>/g, \"\" ).toLowerCase() :\n\t\t\t\t\ta+'';\n\t\t},\n\t\n\t\t// string\n\t\t\"string-pre\": function ( a ) {\n\t\t\t// This is a little complex, but faster than always calling toString,\n\t\t\t// http://jsperf.com/tostring-v-check\n\t\t\treturn _empty(a) ?\n\t\t\t\t'' :\n\t\t\t\ttypeof a === 'string' ?\n\t\t\t\t\ta.toLowerCase() :\n\t\t\t\t\t! a.toString ?\n\t\t\t\t\t\t'' :\n\t\t\t\t\t\ta.toString();\n\t\t},\n\t\n\t\t// string-asc and -desc are retained only for compatibility with the old\n\t\t// sort methods\n\t\t\"string-asc\": function ( x, y ) {\n\t\t\treturn ((x < y) ? -1 : ((x > y) ? 1 : 0));\n\t\t},\n\t\n\t\t\"string-desc\": function ( x, y ) {\n\t\t\treturn ((x < y) ? 1 : ((x > y) ? -1 : 0));\n\t\t}\n\t} );\n\t\n\t\n\t// Numeric sorting types - order doesn't matter here\n\t_addNumericSort( '' );\n\t\n\t\n\t$.extend( true, DataTable.ext.renderer, {\n\t\theader: {\n\t\t\t_: function ( settings, cell, column, classes ) {\n\t\t\t\t// No additional mark-up required\n\t\t\t\t// Attach a sort listener to update on sort - note that using the\n\t\t\t\t// `DT` namespace will allow the event to be removed automatically\n\t\t\t\t// on destroy, while the `dt` namespaced event is the one we are\n\t\t\t\t// listening for\n\t\t\t\t$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {\n\t\t\t\t\tif ( settings !== ctx ) { // need to check this this is the host\n\t\t\t\t\t\treturn;               // table, not a nested one\n\t\t\t\t\t}\n\t\n\t\t\t\t\tvar colIdx = column.idx;\n\t\n\t\t\t\t\tcell\n\t\t\t\t\t\t.removeClass(\n\t\t\t\t\t\t\tclasses.sSortAsc +' '+\n\t\t\t\t\t\t\tclasses.sSortDesc\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.addClass( columns[ colIdx ] == 'asc' ?\n\t\t\t\t\t\t\tclasses.sSortAsc : columns[ colIdx ] == 'desc' ?\n\t\t\t\t\t\t\t\tclasses.sSortDesc :\n\t\t\t\t\t\t\t\tcolumn.sSortingClass\n\t\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t},\n\t\n\t\t\tjqueryui: function ( settings, cell, column, classes ) {\n\t\t\t\t$('<div/>')\n\t\t\t\t\t.addClass( classes.sSortJUIWrapper )\n\t\t\t\t\t.append( cell.contents() )\n\t\t\t\t\t.append( $('<span/>')\n\t\t\t\t\t\t.addClass( classes.sSortIcon+' '+column.sSortingClassJUI )\n\t\t\t\t\t)\n\t\t\t\t\t.appendTo( cell );\n\t\n\t\t\t\t// Attach a sort listener to update on sort\n\t\t\t\t$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {\n\t\t\t\t\tif ( settings !== ctx ) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tvar colIdx = column.idx;\n\t\n\t\t\t\t\tcell\n\t\t\t\t\t\t.removeClass( classes.sSortAsc +\" \"+classes.sSortDesc )\n\t\t\t\t\t\t.addClass( columns[ colIdx ] == 'asc' ?\n\t\t\t\t\t\t\tclasses.sSortAsc : columns[ colIdx ] == 'desc' ?\n\t\t\t\t\t\t\t\tclasses.sSortDesc :\n\t\t\t\t\t\t\t\tcolumn.sSortingClass\n\t\t\t\t\t\t);\n\t\n\t\t\t\t\tcell\n\t\t\t\t\t\t.find( 'span.'+classes.sSortIcon )\n\t\t\t\t\t\t.removeClass(\n\t\t\t\t\t\t\tclasses.sSortJUIAsc +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUIDesc +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUI +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUIAscAllowed +\" \"+\n\t\t\t\t\t\t\tclasses.sSortJUIDescAllowed\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.addClass( columns[ colIdx ] == 'asc' ?\n\t\t\t\t\t\t\tclasses.sSortJUIAsc : columns[ colIdx ] == 'desc' ?\n\t\t\t\t\t\t\t\tclasses.sSortJUIDesc :\n\t\t\t\t\t\t\t\tcolumn.sSortingClassJUI\n\t\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} );\n\t\n\t/*\n\t * Public helper functions. These aren't used internally by DataTables, or\n\t * called by any of the options passed into DataTables, but they can be used\n\t * externally by developers working with DataTables. They are helper functions\n\t * to make working with DataTables a little bit easier.\n\t */\n\t\n\tvar __htmlEscapeEntities = function ( d ) {\n\t\tif (Array.isArray(d)) {\n\t\t\td = d.join(',');\n\t\t}\n\t\n\t\treturn typeof d === 'string' ?\n\t\t\td\n\t\t\t\t.replace(/&/g, '&amp;')\n\t\t\t\t.replace(/</g, '&lt;')\n\t\t\t\t.replace(/>/g, '&gt;')\n\t\t\t\t.replace(/\"/g, '&quot;') :\n\t\t\td;\n\t};\n\t\n\t// Common logic for moment, luxon or a date action\n\tfunction __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {\n\t\tif (window.moment) {\n\t\t\treturn dt[momentFn]( arg1 );\n\t\t}\n\t\telse if (window.luxon) {\n\t\t\treturn dt[luxonFn]( arg1 );\n\t\t}\n\t\t\n\t\treturn dateFn ? dt[dateFn]( arg1 ) : dt;\n\t}\n\t\n\t\n\tvar __mlWarning = false;\n\tfunction __mldObj (d, format, locale) {\n\t\tvar dt;\n\t\n\t\tif (window.moment) {\n\t\t\tdt = window.moment.utc( d, format, locale, true );\n\t\n\t\t\tif (! dt.isValid()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\telse if (window.luxon) {\n\t\t\tdt = format && typeof d === 'string'\n\t\t\t\t? window.luxon.DateTime.fromFormat( d, format )\n\t\t\t\t: window.luxon.DateTime.fromISO( d );\n\t\n\t\t\tif (! dt.isValid) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\n\t\t\tdt.setLocale(locale);\n\t\t}\n\t\telse if (! format) {\n\t\t\t// No format given, must be ISO\n\t\t\tdt = new Date(d);\n\t\t}\n\t\telse {\n\t\t\tif (! __mlWarning) {\n\t\t\t\talert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');\n\t\t\t}\n\t\n\t\t\t__mlWarning = true;\n\t\t}\n\t\n\t\treturn dt;\n\t}\n\t\n\t// Wrapper for date, datetime and time which all operate the same way with the exception of\n\t// the output string for auto locale support\n\tfunction __mlHelper (localeString) {\n\t\treturn function ( from, to, locale, def ) {\n\t\t\t// Luxon and Moment support\n\t\t\t// Argument shifting\n\t\t\tif ( arguments.length === 0 ) {\n\t\t\t\tlocale = 'en';\n\t\t\t\tto = null; // means toLocaleString\n\t\t\t\tfrom = null; // means iso8601\n\t\t\t}\n\t\t\telse if ( arguments.length === 1 ) {\n\t\t\t\tlocale = 'en';\n\t\t\t\tto = from;\n\t\t\t\tfrom = null;\n\t\t\t}\n\t\t\telse if ( arguments.length === 2 ) {\n\t\t\t\tlocale = to;\n\t\t\t\tto = from;\n\t\t\t\tfrom = null;\n\t\t\t}\n\t\n\t\t\tvar typeName = 'datetime-' + to;\n\t\n\t\t\t// Add type detection and sorting specific to this date format - we need to be able to identify\n\t\t\t// date type columns as such, rather than as numbers in extensions. Hence the need for this.\n\t\t\tif (! DataTable.ext.type.order[typeName]) {\n\t\t\t\t// The renderer will give the value to type detect as the type!\n\t\t\t\tDataTable.ext.type.detect.unshift(function (d) {\n\t\t\t\t\treturn d === typeName ? typeName : false;\n\t\t\t\t});\n\t\n\t\t\t\t// The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a\n\t\t\t\t// `valueOf` which gives milliseconds epoch\n\t\t\t\tDataTable.ext.type.order[typeName + '-asc'] = function (a, b) {\n\t\t\t\t\tvar x = a.valueOf();\n\t\t\t\t\tvar y = b.valueOf();\n\t\n\t\t\t\t\treturn x === y\n\t\t\t\t\t\t? 0\n\t\t\t\t\t\t: x < y\n\t\t\t\t\t\t\t? -1\n\t\t\t\t\t\t\t: 1;\n\t\t\t\t}\n\t\n\t\t\t\tDataTable.ext.type.order[typeName + '-desc'] = function (a, b) {\n\t\t\t\t\tvar x = a.valueOf();\n\t\t\t\t\tvar y = b.valueOf();\n\t\n\t\t\t\t\treturn x === y\n\t\t\t\t\t\t? 0\n\t\t\t\t\t\t: x > y\n\t\t\t\t\t\t\t? -1\n\t\t\t\t\t\t\t: 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\treturn function ( d, type ) {\n\t\t\t\t// Allow for a default value\n\t\t\t\tif (d === null || d === undefined) {\n\t\t\t\t\tif (def === '--now') {\n\t\t\t\t\t\t// We treat everything as UTC further down, so no changes are\n\t\t\t\t\t\t// made, as such need to get the local date / time as if it were\n\t\t\t\t\t\t// UTC\n\t\t\t\t\t\tvar local = new Date();\n\t\t\t\t\t\td = new Date( Date.UTC(\n\t\t\t\t\t\t\tlocal.getFullYear(), local.getMonth(), local.getDate(),\n\t\t\t\t\t\t\tlocal.getHours(), local.getMinutes(), local.getSeconds()\n\t\t\t\t\t\t) );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\td = '';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\tif (type === 'type') {\n\t\t\t\t\t// Typing uses the type name for fast matching\n\t\t\t\t\treturn typeName;\n\t\t\t\t}\n\t\n\t\t\t\tif (d === '') {\n\t\t\t\t\treturn type !== 'sort'\n\t\t\t\t\t\t? ''\n\t\t\t\t\t\t: __mldObj('0000-01-01 00:00:00', null, locale);\n\t\t\t\t}\n\t\n\t\t\t\t// Shortcut. If `from` and `to` are the same, we are using the renderer to\n\t\t\t\t// format for ordering, not display - its already in the display format.\n\t\t\t\tif ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {\n\t\t\t\t\treturn d;\n\t\t\t\t}\n\t\n\t\t\t\tvar dt = __mldObj(d, from, locale);\n\t\n\t\t\t\tif (dt === null) {\n\t\t\t\t\treturn d;\n\t\t\t\t}\n\t\n\t\t\t\tif (type === 'sort') {\n\t\t\t\t\treturn dt;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar formatted = to === null\n\t\t\t\t\t? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()\n\t\t\t\t\t: __mld(dt, 'format', 'toFormat', 'toISOString', to);\n\t\n\t\t\t\t// XSS protection\n\t\t\t\treturn type === 'display' ?\n\t\t\t\t\t__htmlEscapeEntities( formatted ) :\n\t\t\t\t\tformatted;\n\t\t\t};\n\t\t}\n\t}\n\t\n\t// Based on locale, determine standard number formatting\n\t// Fallback for legacy browsers is US English\n\tvar __thousands = ',';\n\tvar __decimal = '.';\n\t\n\tif (window.Intl !== undefined) {\n\t\ttry {\n\t\t\tvar num = new Intl.NumberFormat().formatToParts(100000.1);\n\t\t\n\t\t\tfor (var i=0 ; i<num.length ; i++) {\n\t\t\t\tif (num[i].type === 'group') {\n\t\t\t\t\t__thousands = num[i].value;\n\t\t\t\t}\n\t\t\t\telse if (num[i].type === 'decimal') {\n\t\t\t\t\t__decimal = num[i].value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (e) {\n\t\t\t// noop\n\t\t}\n\t}\n\t\n\t// Formatted date time detection - use by declaring the formats you are going to use\n\tDataTable.datetime = function ( format, locale ) {\n\t\tvar typeName = 'datetime-detect-' + format;\n\t\n\t\tif (! locale) {\n\t\t\tlocale = 'en';\n\t\t}\n\t\n\t\tif (! DataTable.ext.type.order[typeName]) {\n\t\t\tDataTable.ext.type.detect.unshift(function (d) {\n\t\t\t\tvar dt = __mldObj(d, format, locale);\n\t\t\t\treturn d === '' || dt ? typeName : false;\n\t\t\t});\n\t\n\t\t\tDataTable.ext.type.order[typeName + '-pre'] = function (d) {\n\t\t\t\treturn __mldObj(d, format, locale) || 0;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * Helpers for `columns.render`.\n\t *\n\t * The options defined here can be used with the `columns.render` initialisation\n\t * option to provide a display renderer. The following functions are defined:\n\t *\n\t * * `number` - Will format numeric data (defined by `columns.data`) for\n\t *   display, retaining the original unformatted data for sorting and filtering.\n\t *   It takes 5 parameters:\n\t *   * `string` - Thousands grouping separator\n\t *   * `string` - Decimal point indicator\n\t *   * `integer` - Number of decimal points to show\n\t *   * `string` (optional) - Prefix.\n\t *   * `string` (optional) - Postfix (/suffix).\n\t * * `text` - Escape HTML to help prevent XSS attacks. It has no optional\n\t *   parameters.\n\t *\n\t * @example\n\t *   // Column definition using the number renderer\n\t *   {\n\t *     data: \"salary\",\n\t *     render: $.fn.dataTable.render.number( '\\'', '.', 0, '$' )\n\t *   }\n\t *\n\t * @namespace\n\t */\n\tDataTable.render = {\n\t\tdate: __mlHelper('toLocaleDateString'),\n\t\tdatetime: __mlHelper('toLocaleString'),\n\t\ttime: __mlHelper('toLocaleTimeString'),\n\t\tnumber: function ( thousands, decimal, precision, prefix, postfix ) {\n\t\t\t// Auto locale detection\n\t\t\tif (thousands === null || thousands === undefined) {\n\t\t\t\tthousands = __thousands;\n\t\t\t}\n\t\n\t\t\tif (decimal === null || decimal === undefined) {\n\t\t\t\tdecimal = __decimal;\n\t\t\t}\n\t\n\t\t\treturn {\n\t\t\t\tdisplay: function ( d ) {\n\t\t\t\t\tif ( typeof d !== 'number' && typeof d !== 'string' ) {\n\t\t\t\t\t\treturn d;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tif (d === '' || d === null) {\n\t\t\t\t\t\treturn d;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tvar negative = d < 0 ? '-' : '';\n\t\t\t\t\tvar flo = parseFloat( d );\n\t\n\t\t\t\t\t// If NaN then there isn't much formatting that we can do - just\n\t\t\t\t\t// return immediately, escaping any HTML (this was supposed to\n\t\t\t\t\t// be a number after all)\n\t\t\t\t\tif ( isNaN( flo ) ) {\n\t\t\t\t\t\treturn __htmlEscapeEntities( d );\n\t\t\t\t\t}\n\t\n\t\t\t\t\tflo = flo.toFixed( precision );\n\t\t\t\t\td = Math.abs( flo );\n\t\n\t\t\t\t\tvar intPart = parseInt( d, 10 );\n\t\t\t\t\tvar floatPart = precision ?\n\t\t\t\t\t\tdecimal+(d - intPart).toFixed( precision ).substring( 2 ):\n\t\t\t\t\t\t'';\n\t\n\t\t\t\t\t// If zero, then can't have a negative prefix\n\t\t\t\t\tif (intPart === 0 && parseFloat(floatPart) === 0) {\n\t\t\t\t\t\tnegative = '';\n\t\t\t\t\t}\n\t\n\t\t\t\t\treturn negative + (prefix||'') +\n\t\t\t\t\t\tintPart.toString().replace(\n\t\t\t\t\t\t\t/\\B(?=(\\d{3})+(?!\\d))/g, thousands\n\t\t\t\t\t\t) +\n\t\t\t\t\t\tfloatPart +\n\t\t\t\t\t\t(postfix||'');\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\n\t\ttext: function () {\n\t\t\treturn {\n\t\t\t\tdisplay: __htmlEscapeEntities,\n\t\t\t\tfilter: __htmlEscapeEntities\n\t\t\t};\n\t\t}\n\t};\n\t\n\t\n\t/*\n\t * This is really a good bit rubbish this method of exposing the internal methods\n\t * publicly... - To be fixed in 2.0 using methods on the prototype\n\t */\n\t\n\t\n\t/**\n\t * Create a wrapper function for exporting an internal functions to an external API.\n\t *  @param {string} fn API function name\n\t *  @returns {function} wrapped function\n\t *  @memberof DataTable#internal\n\t */\n\tfunction _fnExternApiFunc (fn)\n\t{\n\t\treturn function() {\n\t\t\tvar args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(\n\t\t\t\tArray.prototype.slice.call(arguments)\n\t\t\t);\n\t\t\treturn DataTable.ext.internal[fn].apply( this, args );\n\t\t};\n\t}\n\t\n\t\n\t/**\n\t * Reference to internal functions for use by plug-in developers. Note that\n\t * these methods are references to internal functions and are considered to be\n\t * private. If you use these methods, be aware that they are liable to change\n\t * between versions.\n\t *  @namespace\n\t */\n\t$.extend( DataTable.ext.internal, {\n\t\t_fnExternApiFunc: _fnExternApiFunc,\n\t\t_fnBuildAjax: _fnBuildAjax,\n\t\t_fnAjaxUpdate: _fnAjaxUpdate,\n\t\t_fnAjaxParameters: _fnAjaxParameters,\n\t\t_fnAjaxUpdateDraw: _fnAjaxUpdateDraw,\n\t\t_fnAjaxDataSrc: _fnAjaxDataSrc,\n\t\t_fnAddColumn: _fnAddColumn,\n\t\t_fnColumnOptions: _fnColumnOptions,\n\t\t_fnAdjustColumnSizing: _fnAdjustColumnSizing,\n\t\t_fnVisibleToColumnIndex: _fnVisibleToColumnIndex,\n\t\t_fnColumnIndexToVisible: _fnColumnIndexToVisible,\n\t\t_fnVisbleColumns: _fnVisbleColumns,\n\t\t_fnGetColumns: _fnGetColumns,\n\t\t_fnColumnTypes: _fnColumnTypes,\n\t\t_fnApplyColumnDefs: _fnApplyColumnDefs,\n\t\t_fnHungarianMap: _fnHungarianMap,\n\t\t_fnCamelToHungarian: _fnCamelToHungarian,\n\t\t_fnLanguageCompat: _fnLanguageCompat,\n\t\t_fnBrowserDetect: _fnBrowserDetect,\n\t\t_fnAddData: _fnAddData,\n\t\t_fnAddTr: _fnAddTr,\n\t\t_fnNodeToDataIndex: _fnNodeToDataIndex,\n\t\t_fnNodeToColumnIndex: _fnNodeToColumnIndex,\n\t\t_fnGetCellData: _fnGetCellData,\n\t\t_fnSetCellData: _fnSetCellData,\n\t\t_fnSplitObjNotation: _fnSplitObjNotation,\n\t\t_fnGetObjectDataFn: _fnGetObjectDataFn,\n\t\t_fnSetObjectDataFn: _fnSetObjectDataFn,\n\t\t_fnGetDataMaster: _fnGetDataMaster,\n\t\t_fnClearTable: _fnClearTable,\n\t\t_fnDeleteIndex: _fnDeleteIndex,\n\t\t_fnInvalidate: _fnInvalidate,\n\t\t_fnGetRowElements: _fnGetRowElements,\n\t\t_fnCreateTr: _fnCreateTr,\n\t\t_fnBuildHead: _fnBuildHead,\n\t\t_fnDrawHead: _fnDrawHead,\n\t\t_fnDraw: _fnDraw,\n\t\t_fnReDraw: _fnReDraw,\n\t\t_fnAddOptionsHtml: _fnAddOptionsHtml,\n\t\t_fnDetectHeader: _fnDetectHeader,\n\t\t_fnGetUniqueThs: _fnGetUniqueThs,\n\t\t_fnFeatureHtmlFilter: _fnFeatureHtmlFilter,\n\t\t_fnFilterComplete: _fnFilterComplete,\n\t\t_fnFilterCustom: _fnFilterCustom,\n\t\t_fnFilterColumn: _fnFilterColumn,\n\t\t_fnFilter: _fnFilter,\n\t\t_fnFilterCreateSearch: _fnFilterCreateSearch,\n\t\t_fnEscapeRegex: _fnEscapeRegex,\n\t\t_fnFilterData: _fnFilterData,\n\t\t_fnFeatureHtmlInfo: _fnFeatureHtmlInfo,\n\t\t_fnUpdateInfo: _fnUpdateInfo,\n\t\t_fnInfoMacros: _fnInfoMacros,\n\t\t_fnInitialise: _fnInitialise,\n\t\t_fnInitComplete: _fnInitComplete,\n\t\t_fnLengthChange: _fnLengthChange,\n\t\t_fnFeatureHtmlLength: _fnFeatureHtmlLength,\n\t\t_fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,\n\t\t_fnPageChange: _fnPageChange,\n\t\t_fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,\n\t\t_fnProcessingDisplay: _fnProcessingDisplay,\n\t\t_fnFeatureHtmlTable: _fnFeatureHtmlTable,\n\t\t_fnScrollDraw: _fnScrollDraw,\n\t\t_fnApplyToChildren: _fnApplyToChildren,\n\t\t_fnCalculateColumnWidths: _fnCalculateColumnWidths,\n\t\t_fnThrottle: _fnThrottle,\n\t\t_fnConvertToWidth: _fnConvertToWidth,\n\t\t_fnGetWidestNode: _fnGetWidestNode,\n\t\t_fnGetMaxLenString: _fnGetMaxLenString,\n\t\t_fnStringToCss: _fnStringToCss,\n\t\t_fnSortFlatten: _fnSortFlatten,\n\t\t_fnSort: _fnSort,\n\t\t_fnSortAria: _fnSortAria,\n\t\t_fnSortListener: _fnSortListener,\n\t\t_fnSortAttachListener: _fnSortAttachListener,\n\t\t_fnSortingClasses: _fnSortingClasses,\n\t\t_fnSortData: _fnSortData,\n\t\t_fnSaveState: _fnSaveState,\n\t\t_fnLoadState: _fnLoadState,\n\t\t_fnImplementState: _fnImplementState,\n\t\t_fnSettingsFromNode: _fnSettingsFromNode,\n\t\t_fnLog: _fnLog,\n\t\t_fnMap: _fnMap,\n\t\t_fnBindAction: _fnBindAction,\n\t\t_fnCallbackReg: _fnCallbackReg,\n\t\t_fnCallbackFire: _fnCallbackFire,\n\t\t_fnLengthOverflow: _fnLengthOverflow,\n\t\t_fnRenderer: _fnRenderer,\n\t\t_fnDataSource: _fnDataSource,\n\t\t_fnRowAttributes: _fnRowAttributes,\n\t\t_fnExtend: _fnExtend,\n\t\t_fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant\n\t\t                                // in 1.10, so this dead-end function is\n\t\t                                // added to prevent errors\n\t} );\n\t\n\t\n\t// jQuery access\n\t$.fn.dataTable = DataTable;\n\t\n\t// Provide access to the host jQuery object (circular reference)\n\tDataTable.$ = $;\n\t\n\t// Legacy aliases\n\t$.fn.dataTableSettings = DataTable.settings;\n\t$.fn.dataTableExt = DataTable.ext;\n\t\n\t// With a capital `D` we return a DataTables API instance rather than a\n\t// jQuery object\n\t$.fn.DataTable = function ( opts ) {\n\t\treturn $(this).dataTable( opts ).api();\n\t};\n\t\n\t// All properties that are available to $.fn.dataTable should also be\n\t// available on $.fn.DataTable\n\t$.each( DataTable, function ( prop, val ) {\n\t\t$.fn.DataTable[ prop ] = val;\n\t} );\n\n\treturn DataTable;\n}));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/data-tables/scrolling.js",
    "content": "/**\n * This pagination plug-in provides pagination controls for DataTables which\n * match the style and interaction of the ExtJS library's grid component.\n *\n *  @name ExtJS style\n *  @summary Pagination in the styling of ExtJS\n *  @author [Zach Curtis](http://zachariahtimothy.wordpress.com/)\n *\n *  @example\n *    $(document).ready(function() {\n *        $('#example').dataTable( {\n *            \"sPaginationType\": \"extStyle\"\n *        } );\n *    } );\n */\n\n$.fn.dataTableExt.oApi.fnExtStylePagingInfo = function ( oSettings )\n{\n    return {\n        \"iStart\":         oSettings._iDisplayStart,\n        \"iEnd\":           oSettings.fnDisplayEnd(),\n        \"iLength\":        oSettings._iDisplayLength,\n        \"iTotal\":         oSettings.fnRecordsTotal(),\n        \"iFilteredTotal\": oSettings.fnRecordsDisplay(),\n        \"iPage\":          oSettings._iDisplayLength === -1 ?\n            0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),\n        \"iTotalPages\":    oSettings._iDisplayLength === -1 ?\n            0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )\n    };\n};\n\n$.fn.dataTableExt.oPagination.extStyle = {\n\n\n    \"fnInit\": function (oSettings, nPaging, fnCallbackDraw) {\n\n        var oPaging = oSettings.oInstance.fnExtStylePagingInfo();\n\n        nFirst = $('<span/>', { 'class': 'paginate_button first' , text : \"<<\" });\n        nPrevious = $('<span/>', { 'class': 'paginate_button previous' , text : \"<\" });\n        nNext = $('<span/>', { 'class': 'paginate_button next' , text : \">\" });\n        nLast = $('<span/>', { 'class': 'paginate_button last' , text : \">>\" });\n        nPageTxt = $(\"<span />\", { text: 'Page' });\n        nPageNumBox = $('<input />', { type: 'text', val: 1, 'class': 'pageinate_input_box' });\n        nPageOf = $('<span />', { text: '/' });\n        nTotalPages = $('<span />', { class :  \"paginate_total\" , text : oPaging.iTotalPages });\n\n\n        $(nPaging)\n            .append(nFirst)\n            .append(nPrevious)\n            .append(nPageTxt)\n            .append(nPageNumBox)\n            .append(nPageOf)\n            .append(nTotalPages)\n            .append(nNext)\n            .append(nLast);\n\n        nFirst.click(function () {\n            if( $(this).hasClass(\"disabled\") )\n                return;\n            oSettings.oApi._fnPageChange(oSettings, \"first\");\n            fnCallbackDraw(oSettings);\n        }).bind('selectstart', function () { return false; });\n\n        nPrevious.click(function () {\n            if( $(this).hasClass(\"disabled\") )\n                return;\n            oSettings.oApi._fnPageChange(oSettings, \"previous\");\n            fnCallbackDraw(oSettings);\n        }).bind('selectstart', function () { return false; });\n\n        nNext.click(function () {\n            if( $(this).hasClass(\"disabled\") )\n                return;\n            oSettings.oApi._fnPageChange(oSettings, \"next\");\n            fnCallbackDraw(oSettings);\n        }).bind('selectstart', function () { return false; });\n\n        nLast.click(function () {\n            if( $(this).hasClass(\"disabled\") )\n                return;\n            oSettings.oApi._fnPageChange(oSettings, \"last\");\n            fnCallbackDraw(oSettings);\n        }).bind('selectstart', function () { return false; });\n\n        nPageNumBox.change(function () {\n            var pageValue = parseInt($(this).val(), 10) - 1 ; // -1 because pages are 0 indexed, but the UI is 1\n            var oPaging = oSettings.oInstance.fnPagingInfo();\n\n            if(pageValue === NaN || pageValue<0 ){\n                pageValue = 0;\n            }else if(pageValue >= oPaging.iTotalPages ){\n                pageValue = oPaging.iTotalPages -1;\n            }\n            oSettings.oApi._fnPageChange(oSettings, pageValue);\n            fnCallbackDraw(oSettings);\n        });\n\n    },\n\n\n    \"fnUpdate\": function (oSettings, fnCallbackDraw) {\n        if (!oSettings.aanFeatures.p) {\n            return;\n        }\n\n        var oPaging = oSettings.oInstance.fnExtStylePagingInfo();\n\n        /* Loop over each instance of the pager */\n        var an = oSettings.aanFeatures.p;\n\n        $(an).find('span.paginate_total').html(oPaging.iTotalPages);\n        $(an).find('.pageinate_input_box').val(oPaging.iPage+1);\n\n        $(an).each(function(index,item) {\n\n            var $item = $(item);\n\n            if (oPaging.iPage == 0) {\n                var prev = $item.find('span.paginate_button.first').add($item.find('span.paginate_button.previous'));\n                prev.addClass(\"disabled\");\n            }else {\n                var prev = $item.find('span.paginate_button.first').add($item.find('span.paginate_button.previous'));\n                prev.removeClass(\"disabled\");\n            }\n\n            if (oPaging.iPage+1 == oPaging.iTotalPages) {\n                var next = $item.find('span.paginate_button.last').add($item.find('span.paginate_button.next'));\n                next.addClass(\"disabled\");\n            }else {\n                var next = $item.find('span.paginate_button.last').add($item.find('span.paginate_button.next'));\n                next.removeClass(\"disabled\");\n            }\n        });\n    }\n};"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/echarts/echarts.common.js",
    "content": "\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n    typeof define === 'function' && define.amd ? define(['exports'], factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.echarts = {}));\n}(this, (function (exports) { 'use strict';\n\n    /*! *****************************************************************************\n    Copyright (c) Microsoft Corporation.\n\n    Permission to use, copy, modify, and/or distribute this software for any\n    purpose with or without fee is hereby granted.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\n    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n    PERFORMANCE OF THIS SOFTWARE.\n    ***************************************************************************** */\n    /* global Reflect, Promise */\n\n    var extendStatics = function(d, b) {\n        extendStatics = Object.setPrototypeOf ||\n            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n        return extendStatics(d, b);\n    };\n\n    function __extends(d, b) {\n        if (typeof b !== \"function\" && b !== null)\n            throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    }\n\n    var Browser = (function () {\n        function Browser() {\n            this.firefox = false;\n            this.ie = false;\n            this.edge = false;\n            this.newEdge = false;\n            this.weChat = false;\n        }\n        return Browser;\n    }());\n    var Env = (function () {\n        function Env() {\n            this.browser = new Browser();\n            this.node = false;\n            this.wxa = false;\n            this.worker = false;\n            this.svgSupported = false;\n            this.touchEventsSupported = false;\n            this.pointerEventsSupported = false;\n            this.domSupported = false;\n            this.transformSupported = false;\n            this.transform3dSupported = false;\n            this.hasGlobalWindow = typeof window !== 'undefined';\n        }\n        return Env;\n    }());\n    var env = new Env();\n    if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {\n        env.wxa = true;\n        env.touchEventsSupported = true;\n    }\n    else if (typeof document === 'undefined' && typeof self !== 'undefined') {\n        env.worker = true;\n    }\n    else if (typeof navigator === 'undefined') {\n        env.node = true;\n        env.svgSupported = true;\n    }\n    else {\n        detect(navigator.userAgent, env);\n    }\n    function detect(ua, env) {\n        var browser = env.browser;\n        var firefox = ua.match(/Firefox\\/([\\d.]+)/);\n        var ie = ua.match(/MSIE\\s([\\d.]+)/)\n            || ua.match(/Trident\\/.+?rv:(([\\d.]+))/);\n        var edge = ua.match(/Edge?\\/([\\d.]+)/);\n        var weChat = (/micromessenger/i).test(ua);\n        if (firefox) {\n            browser.firefox = true;\n            browser.version = firefox[1];\n        }\n        if (ie) {\n            browser.ie = true;\n            browser.version = ie[1];\n        }\n        if (edge) {\n            browser.edge = true;\n            browser.version = edge[1];\n            browser.newEdge = +edge[1].split('.')[0] > 18;\n        }\n        if (weChat) {\n            browser.weChat = true;\n        }\n        env.svgSupported = typeof SVGRect !== 'undefined';\n        env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge;\n        env.pointerEventsSupported = 'onpointerdown' in window\n            && (browser.edge || (browser.ie && +browser.version >= 11));\n        env.domSupported = typeof document !== 'undefined';\n        var style = document.documentElement.style;\n        env.transform3dSupported = ((browser.ie && 'transition' in style)\n            || browser.edge\n            || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix()))\n            || 'MozPerspective' in style)\n            && !('OTransition' in style);\n        env.transformSupported = env.transform3dSupported\n            || (browser.ie && +browser.version >= 9);\n    }\n\n    var DEFAULT_FONT_SIZE = 12;\n    var DEFAULT_FONT_FAMILY = 'sans-serif';\n    var DEFAULT_FONT = DEFAULT_FONT_SIZE + \"px \" + DEFAULT_FONT_FAMILY;\n    var OFFSET = 20;\n    var SCALE = 100;\n    var defaultWidthMapStr = \"007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\\\\\WQb\\\\0FWLg\\\\bWb\\\\WQ\\\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\\\FFF5.5N\";\n    function getTextWidthMap(mapStr) {\n        var map = {};\n        if (typeof JSON === 'undefined') {\n            return map;\n        }\n        for (var i = 0; i < mapStr.length; i++) {\n            var char = String.fromCharCode(i + 32);\n            var size = (mapStr.charCodeAt(i) - OFFSET) / SCALE;\n            map[char] = size;\n        }\n        return map;\n    }\n    var DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr);\n    var platformApi = {\n        createCanvas: function () {\n            return typeof document !== 'undefined'\n                && document.createElement('canvas');\n        },\n        measureText: (function () {\n            var _ctx;\n            var _cachedFont;\n            return function (text, font) {\n                if (!_ctx) {\n                    var canvas = platformApi.createCanvas();\n                    _ctx = canvas && canvas.getContext('2d');\n                }\n                if (_ctx) {\n                    if (_cachedFont !== font) {\n                        _cachedFont = _ctx.font = font || DEFAULT_FONT;\n                    }\n                    return _ctx.measureText(text);\n                }\n                else {\n                    text = text || '';\n                    font = font || DEFAULT_FONT;\n                    var res = /(\\d+)px/.exec(font);\n                    var fontSize = res && +res[1] || DEFAULT_FONT_SIZE;\n                    var width = 0;\n                    if (font.indexOf('mono') >= 0) {\n                        width = fontSize * text.length;\n                    }\n                    else {\n                        for (var i = 0; i < text.length; i++) {\n                            var preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]];\n                            width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize);\n                        }\n                    }\n                    return { width: width };\n                }\n            };\n        })(),\n        loadImage: function (src, onload, onerror) {\n            var image = new Image();\n            image.onload = onload;\n            image.onerror = onerror;\n            image.src = src;\n            return image;\n        }\n    };\n    function setPlatformAPI(newPlatformApis) {\n        for (var key in platformApi) {\n            if (newPlatformApis[key]) {\n                platformApi[key] = newPlatformApis[key];\n            }\n        }\n    }\n\n    var BUILTIN_OBJECT = reduce([\n        'Function',\n        'RegExp',\n        'Date',\n        'Error',\n        'CanvasGradient',\n        'CanvasPattern',\n        'Image',\n        'Canvas'\n    ], function (obj, val) {\n        obj['[object ' + val + ']'] = true;\n        return obj;\n    }, {});\n    var TYPED_ARRAY = reduce([\n        'Int8',\n        'Uint8',\n        'Uint8Clamped',\n        'Int16',\n        'Uint16',\n        'Int32',\n        'Uint32',\n        'Float32',\n        'Float64'\n    ], function (obj, val) {\n        obj['[object ' + val + 'Array]'] = true;\n        return obj;\n    }, {});\n    var objToString = Object.prototype.toString;\n    var arrayProto = Array.prototype;\n    var nativeForEach = arrayProto.forEach;\n    var nativeFilter = arrayProto.filter;\n    var nativeSlice = arrayProto.slice;\n    var nativeMap = arrayProto.map;\n    var ctorFunction = function () { }.constructor;\n    var protoFunction = ctorFunction ? ctorFunction.prototype : null;\n    var protoKey = '__proto__';\n    var idStart = 0x0907;\n    function guid() {\n        return idStart++;\n    }\n    function logError() {\n        var args = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            args[_i] = arguments[_i];\n        }\n        if (typeof console !== 'undefined') {\n            console.error.apply(console, args);\n        }\n    }\n    function clone(source) {\n        if (source == null || typeof source !== 'object') {\n            return source;\n        }\n        var result = source;\n        var typeStr = objToString.call(source);\n        if (typeStr === '[object Array]') {\n            if (!isPrimitive(source)) {\n                result = [];\n                for (var i = 0, len = source.length; i < len; i++) {\n                    result[i] = clone(source[i]);\n                }\n            }\n        }\n        else if (TYPED_ARRAY[typeStr]) {\n            if (!isPrimitive(source)) {\n                var Ctor = source.constructor;\n                if (Ctor.from) {\n                    result = Ctor.from(source);\n                }\n                else {\n                    result = new Ctor(source.length);\n                    for (var i = 0, len = source.length; i < len; i++) {\n                        result[i] = source[i];\n                    }\n                }\n            }\n        }\n        else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {\n            result = {};\n            for (var key in source) {\n                if (source.hasOwnProperty(key) && key !== protoKey) {\n                    result[key] = clone(source[key]);\n                }\n            }\n        }\n        return result;\n    }\n    function merge(target, source, overwrite) {\n        if (!isObject(source) || !isObject(target)) {\n            return overwrite ? clone(source) : target;\n        }\n        for (var key in source) {\n            if (source.hasOwnProperty(key) && key !== protoKey) {\n                var targetProp = target[key];\n                var sourceProp = source[key];\n                if (isObject(sourceProp)\n                    && isObject(targetProp)\n                    && !isArray(sourceProp)\n                    && !isArray(targetProp)\n                    && !isDom(sourceProp)\n                    && !isDom(targetProp)\n                    && !isBuiltInObject(sourceProp)\n                    && !isBuiltInObject(targetProp)\n                    && !isPrimitive(sourceProp)\n                    && !isPrimitive(targetProp)) {\n                    merge(targetProp, sourceProp, overwrite);\n                }\n                else if (overwrite || !(key in target)) {\n                    target[key] = clone(source[key]);\n                }\n            }\n        }\n        return target;\n    }\n    function mergeAll(targetAndSources, overwrite) {\n        var result = targetAndSources[0];\n        for (var i = 1, len = targetAndSources.length; i < len; i++) {\n            result = merge(result, targetAndSources[i], overwrite);\n        }\n        return result;\n    }\n    function extend(target, source) {\n        if (Object.assign) {\n            Object.assign(target, source);\n        }\n        else {\n            for (var key in source) {\n                if (source.hasOwnProperty(key) && key !== protoKey) {\n                    target[key] = source[key];\n                }\n            }\n        }\n        return target;\n    }\n    function defaults(target, source, overlay) {\n        var keysArr = keys(source);\n        for (var i = 0; i < keysArr.length; i++) {\n            var key = keysArr[i];\n            if ((overlay ? source[key] != null : target[key] == null)) {\n                target[key] = source[key];\n            }\n        }\n        return target;\n    }\n    var createCanvas = platformApi.createCanvas;\n    function indexOf(array, value) {\n        if (array) {\n            if (array.indexOf) {\n                return array.indexOf(value);\n            }\n            for (var i = 0, len = array.length; i < len; i++) {\n                if (array[i] === value) {\n                    return i;\n                }\n            }\n        }\n        return -1;\n    }\n    function inherits(clazz, baseClazz) {\n        var clazzPrototype = clazz.prototype;\n        function F() { }\n        F.prototype = baseClazz.prototype;\n        clazz.prototype = new F();\n        for (var prop in clazzPrototype) {\n            if (clazzPrototype.hasOwnProperty(prop)) {\n                clazz.prototype[prop] = clazzPrototype[prop];\n            }\n        }\n        clazz.prototype.constructor = clazz;\n        clazz.superClass = baseClazz;\n    }\n    function mixin(target, source, override) {\n        target = 'prototype' in target ? target.prototype : target;\n        source = 'prototype' in source ? source.prototype : source;\n        if (Object.getOwnPropertyNames) {\n            var keyList = Object.getOwnPropertyNames(source);\n            for (var i = 0; i < keyList.length; i++) {\n                var key = keyList[i];\n                if (key !== 'constructor') {\n                    if ((override ? source[key] != null : target[key] == null)) {\n                        target[key] = source[key];\n                    }\n                }\n            }\n        }\n        else {\n            defaults(target, source, override);\n        }\n    }\n    function isArrayLike(data) {\n        if (!data) {\n            return false;\n        }\n        if (typeof data === 'string') {\n            return false;\n        }\n        return typeof data.length === 'number';\n    }\n    function each(arr, cb, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        if (arr.forEach && arr.forEach === nativeForEach) {\n            arr.forEach(cb, context);\n        }\n        else if (arr.length === +arr.length) {\n            for (var i = 0, len = arr.length; i < len; i++) {\n                cb.call(context, arr[i], i, arr);\n            }\n        }\n        else {\n            for (var key in arr) {\n                if (arr.hasOwnProperty(key)) {\n                    cb.call(context, arr[key], key, arr);\n                }\n            }\n        }\n    }\n    function map(arr, cb, context) {\n        if (!arr) {\n            return [];\n        }\n        if (!cb) {\n            return slice(arr);\n        }\n        if (arr.map && arr.map === nativeMap) {\n            return arr.map(cb, context);\n        }\n        else {\n            var result = [];\n            for (var i = 0, len = arr.length; i < len; i++) {\n                result.push(cb.call(context, arr[i], i, arr));\n            }\n            return result;\n        }\n    }\n    function reduce(arr, cb, memo, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        for (var i = 0, len = arr.length; i < len; i++) {\n            memo = cb.call(context, memo, arr[i], i, arr);\n        }\n        return memo;\n    }\n    function filter(arr, cb, context) {\n        if (!arr) {\n            return [];\n        }\n        if (!cb) {\n            return slice(arr);\n        }\n        if (arr.filter && arr.filter === nativeFilter) {\n            return arr.filter(cb, context);\n        }\n        else {\n            var result = [];\n            for (var i = 0, len = arr.length; i < len; i++) {\n                if (cb.call(context, arr[i], i, arr)) {\n                    result.push(arr[i]);\n                }\n            }\n            return result;\n        }\n    }\n    function find(arr, cb, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        for (var i = 0, len = arr.length; i < len; i++) {\n            if (cb.call(context, arr[i], i, arr)) {\n                return arr[i];\n            }\n        }\n    }\n    function keys(obj) {\n        if (!obj) {\n            return [];\n        }\n        if (Object.keys) {\n            return Object.keys(obj);\n        }\n        var keyList = [];\n        for (var key in obj) {\n            if (obj.hasOwnProperty(key)) {\n                keyList.push(key);\n            }\n        }\n        return keyList;\n    }\n    function bindPolyfill(func, context) {\n        var args = [];\n        for (var _i = 2; _i < arguments.length; _i++) {\n            args[_i - 2] = arguments[_i];\n        }\n        return function () {\n            return func.apply(context, args.concat(nativeSlice.call(arguments)));\n        };\n    }\n    var bind = (protoFunction && isFunction(protoFunction.bind))\n        ? protoFunction.call.bind(protoFunction.bind)\n        : bindPolyfill;\n    function curry(func) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        return function () {\n            return func.apply(this, args.concat(nativeSlice.call(arguments)));\n        };\n    }\n    function isArray(value) {\n        if (Array.isArray) {\n            return Array.isArray(value);\n        }\n        return objToString.call(value) === '[object Array]';\n    }\n    function isFunction(value) {\n        return typeof value === 'function';\n    }\n    function isString(value) {\n        return typeof value === 'string';\n    }\n    function isStringSafe(value) {\n        return objToString.call(value) === '[object String]';\n    }\n    function isNumber(value) {\n        return typeof value === 'number';\n    }\n    function isObject(value) {\n        var type = typeof value;\n        return type === 'function' || (!!value && type === 'object');\n    }\n    function isBuiltInObject(value) {\n        return !!BUILTIN_OBJECT[objToString.call(value)];\n    }\n    function isTypedArray(value) {\n        return !!TYPED_ARRAY[objToString.call(value)];\n    }\n    function isDom(value) {\n        return typeof value === 'object'\n            && typeof value.nodeType === 'number'\n            && typeof value.ownerDocument === 'object';\n    }\n    function isGradientObject(value) {\n        return value.colorStops != null;\n    }\n    function isImagePatternObject(value) {\n        return value.image != null;\n    }\n    function isRegExp(value) {\n        return objToString.call(value) === '[object RegExp]';\n    }\n    function eqNaN(value) {\n        return value !== value;\n    }\n    function retrieve() {\n        var args = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            args[_i] = arguments[_i];\n        }\n        for (var i = 0, len = args.length; i < len; i++) {\n            if (args[i] != null) {\n                return args[i];\n            }\n        }\n    }\n    function retrieve2(value0, value1) {\n        return value0 != null\n            ? value0\n            : value1;\n    }\n    function retrieve3(value0, value1, value2) {\n        return value0 != null\n            ? value0\n            : value1 != null\n                ? value1\n                : value2;\n    }\n    function slice(arr) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        return nativeSlice.apply(arr, args);\n    }\n    function normalizeCssArray(val) {\n        if (typeof (val) === 'number') {\n            return [val, val, val, val];\n        }\n        var len = val.length;\n        if (len === 2) {\n            return [val[0], val[1], val[0], val[1]];\n        }\n        else if (len === 3) {\n            return [val[0], val[1], val[2], val[1]];\n        }\n        return val;\n    }\n    function assert(condition, message) {\n        if (!condition) {\n            throw new Error(message);\n        }\n    }\n    function trim(str) {\n        if (str == null) {\n            return null;\n        }\n        else if (typeof str.trim === 'function') {\n            return str.trim();\n        }\n        else {\n            return str.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n        }\n    }\n    var primitiveKey = '__ec_primitive__';\n    function setAsPrimitive(obj) {\n        obj[primitiveKey] = true;\n    }\n    function isPrimitive(obj) {\n        return obj[primitiveKey];\n    }\n    var MapPolyfill = (function () {\n        function MapPolyfill() {\n            this.data = {};\n        }\n        MapPolyfill.prototype[\"delete\"] = function (key) {\n            var existed = this.has(key);\n            if (existed) {\n                delete this.data[key];\n            }\n            return existed;\n        };\n        MapPolyfill.prototype.has = function (key) {\n            return this.data.hasOwnProperty(key);\n        };\n        MapPolyfill.prototype.get = function (key) {\n            return this.data[key];\n        };\n        MapPolyfill.prototype.set = function (key, value) {\n            this.data[key] = value;\n            return this;\n        };\n        MapPolyfill.prototype.keys = function () {\n            return keys(this.data);\n        };\n        MapPolyfill.prototype.forEach = function (callback) {\n            var data = this.data;\n            for (var key in data) {\n                if (data.hasOwnProperty(key)) {\n                    callback(data[key], key);\n                }\n            }\n        };\n        return MapPolyfill;\n    }());\n    var isNativeMapSupported = typeof Map === 'function';\n    function maybeNativeMap() {\n        return (isNativeMapSupported ? new Map() : new MapPolyfill());\n    }\n    var HashMap = (function () {\n        function HashMap(obj) {\n            var isArr = isArray(obj);\n            this.data = maybeNativeMap();\n            var thisMap = this;\n            (obj instanceof HashMap)\n                ? obj.each(visit)\n                : (obj && each(obj, visit));\n            function visit(value, key) {\n                isArr ? thisMap.set(value, key) : thisMap.set(key, value);\n            }\n        }\n        HashMap.prototype.hasKey = function (key) {\n            return this.data.has(key);\n        };\n        HashMap.prototype.get = function (key) {\n            return this.data.get(key);\n        };\n        HashMap.prototype.set = function (key, value) {\n            this.data.set(key, value);\n            return value;\n        };\n        HashMap.prototype.each = function (cb, context) {\n            this.data.forEach(function (value, key) {\n                cb.call(context, value, key);\n            });\n        };\n        HashMap.prototype.keys = function () {\n            var keys = this.data.keys();\n            return isNativeMapSupported\n                ? Array.from(keys)\n                : keys;\n        };\n        HashMap.prototype.removeKey = function (key) {\n            this.data[\"delete\"](key);\n        };\n        return HashMap;\n    }());\n    function createHashMap(obj) {\n        return new HashMap(obj);\n    }\n    function concatArray(a, b) {\n        var newArray = new a.constructor(a.length + b.length);\n        for (var i = 0; i < a.length; i++) {\n            newArray[i] = a[i];\n        }\n        var offset = a.length;\n        for (var i = 0; i < b.length; i++) {\n            newArray[i + offset] = b[i];\n        }\n        return newArray;\n    }\n    function createObject(proto, properties) {\n        var obj;\n        if (Object.create) {\n            obj = Object.create(proto);\n        }\n        else {\n            var StyleCtor = function () { };\n            StyleCtor.prototype = proto;\n            obj = new StyleCtor();\n        }\n        if (properties) {\n            extend(obj, properties);\n        }\n        return obj;\n    }\n    function disableUserSelect(dom) {\n        var domStyle = dom.style;\n        domStyle.webkitUserSelect = 'none';\n        domStyle.userSelect = 'none';\n        domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)';\n        domStyle['-webkit-touch-callout'] = 'none';\n    }\n    function hasOwn(own, prop) {\n        return own.hasOwnProperty(prop);\n    }\n    function noop() { }\n    var RADIAN_TO_DEGREE = 180 / Math.PI;\n\n    var util = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        guid: guid,\n        logError: logError,\n        clone: clone,\n        merge: merge,\n        mergeAll: mergeAll,\n        extend: extend,\n        defaults: defaults,\n        createCanvas: createCanvas,\n        indexOf: indexOf,\n        inherits: inherits,\n        mixin: mixin,\n        isArrayLike: isArrayLike,\n        each: each,\n        map: map,\n        reduce: reduce,\n        filter: filter,\n        find: find,\n        keys: keys,\n        bind: bind,\n        curry: curry,\n        isArray: isArray,\n        isFunction: isFunction,\n        isString: isString,\n        isStringSafe: isStringSafe,\n        isNumber: isNumber,\n        isObject: isObject,\n        isBuiltInObject: isBuiltInObject,\n        isTypedArray: isTypedArray,\n        isDom: isDom,\n        isGradientObject: isGradientObject,\n        isImagePatternObject: isImagePatternObject,\n        isRegExp: isRegExp,\n        eqNaN: eqNaN,\n        retrieve: retrieve,\n        retrieve2: retrieve2,\n        retrieve3: retrieve3,\n        slice: slice,\n        normalizeCssArray: normalizeCssArray,\n        assert: assert,\n        trim: trim,\n        setAsPrimitive: setAsPrimitive,\n        isPrimitive: isPrimitive,\n        HashMap: HashMap,\n        createHashMap: createHashMap,\n        concatArray: concatArray,\n        createObject: createObject,\n        disableUserSelect: disableUserSelect,\n        hasOwn: hasOwn,\n        noop: noop,\n        RADIAN_TO_DEGREE: RADIAN_TO_DEGREE\n    });\n\n    function create(x, y) {\n        if (x == null) {\n            x = 0;\n        }\n        if (y == null) {\n            y = 0;\n        }\n        return [x, y];\n    }\n    function copy(out, v) {\n        out[0] = v[0];\n        out[1] = v[1];\n        return out;\n    }\n    function clone$1(v) {\n        return [v[0], v[1]];\n    }\n    function set(out, a, b) {\n        out[0] = a;\n        out[1] = b;\n        return out;\n    }\n    function add(out, v1, v2) {\n        out[0] = v1[0] + v2[0];\n        out[1] = v1[1] + v2[1];\n        return out;\n    }\n    function scaleAndAdd(out, v1, v2, a) {\n        out[0] = v1[0] + v2[0] * a;\n        out[1] = v1[1] + v2[1] * a;\n        return out;\n    }\n    function sub(out, v1, v2) {\n        out[0] = v1[0] - v2[0];\n        out[1] = v1[1] - v2[1];\n        return out;\n    }\n    function len(v) {\n        return Math.sqrt(lenSquare(v));\n    }\n    var length = len;\n    function lenSquare(v) {\n        return v[0] * v[0] + v[1] * v[1];\n    }\n    var lengthSquare = lenSquare;\n    function mul(out, v1, v2) {\n        out[0] = v1[0] * v2[0];\n        out[1] = v1[1] * v2[1];\n        return out;\n    }\n    function div(out, v1, v2) {\n        out[0] = v1[0] / v2[0];\n        out[1] = v1[1] / v2[1];\n        return out;\n    }\n    function dot(v1, v2) {\n        return v1[0] * v2[0] + v1[1] * v2[1];\n    }\n    function scale(out, v, s) {\n        out[0] = v[0] * s;\n        out[1] = v[1] * s;\n        return out;\n    }\n    function normalize(out, v) {\n        var d = len(v);\n        if (d === 0) {\n            out[0] = 0;\n            out[1] = 0;\n        }\n        else {\n            out[0] = v[0] / d;\n            out[1] = v[1] / d;\n        }\n        return out;\n    }\n    function distance(v1, v2) {\n        return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0])\n            + (v1[1] - v2[1]) * (v1[1] - v2[1]));\n    }\n    var dist = distance;\n    function distanceSquare(v1, v2) {\n        return (v1[0] - v2[0]) * (v1[0] - v2[0])\n            + (v1[1] - v2[1]) * (v1[1] - v2[1]);\n    }\n    var distSquare = distanceSquare;\n    function negate(out, v) {\n        out[0] = -v[0];\n        out[1] = -v[1];\n        return out;\n    }\n    function lerp(out, v1, v2, t) {\n        out[0] = v1[0] + t * (v2[0] - v1[0]);\n        out[1] = v1[1] + t * (v2[1] - v1[1]);\n        return out;\n    }\n    function applyTransform(out, v, m) {\n        var x = v[0];\n        var y = v[1];\n        out[0] = m[0] * x + m[2] * y + m[4];\n        out[1] = m[1] * x + m[3] * y + m[5];\n        return out;\n    }\n    function min(out, v1, v2) {\n        out[0] = Math.min(v1[0], v2[0]);\n        out[1] = Math.min(v1[1], v2[1]);\n        return out;\n    }\n    function max(out, v1, v2) {\n        out[0] = Math.max(v1[0], v2[0]);\n        out[1] = Math.max(v1[1], v2[1]);\n        return out;\n    }\n\n    var vector = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        create: create,\n        copy: copy,\n        clone: clone$1,\n        set: set,\n        add: add,\n        scaleAndAdd: scaleAndAdd,\n        sub: sub,\n        len: len,\n        length: length,\n        lenSquare: lenSquare,\n        lengthSquare: lengthSquare,\n        mul: mul,\n        div: div,\n        dot: dot,\n        scale: scale,\n        normalize: normalize,\n        distance: distance,\n        dist: dist,\n        distanceSquare: distanceSquare,\n        distSquare: distSquare,\n        negate: negate,\n        lerp: lerp,\n        applyTransform: applyTransform,\n        min: min,\n        max: max\n    });\n\n    var Param = (function () {\n        function Param(target, e) {\n            this.target = target;\n            this.topTarget = e && e.topTarget;\n        }\n        return Param;\n    }());\n    var Draggable = (function () {\n        function Draggable(handler) {\n            this.handler = handler;\n            handler.on('mousedown', this._dragStart, this);\n            handler.on('mousemove', this._drag, this);\n            handler.on('mouseup', this._dragEnd, this);\n        }\n        Draggable.prototype._dragStart = function (e) {\n            var draggingTarget = e.target;\n            while (draggingTarget && !draggingTarget.draggable) {\n                draggingTarget = draggingTarget.parent || draggingTarget.__hostTarget;\n            }\n            if (draggingTarget) {\n                this._draggingTarget = draggingTarget;\n                draggingTarget.dragging = true;\n                this._x = e.offsetX;\n                this._y = e.offsetY;\n                this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragstart', e.event);\n            }\n        };\n        Draggable.prototype._drag = function (e) {\n            var draggingTarget = this._draggingTarget;\n            if (draggingTarget) {\n                var x = e.offsetX;\n                var y = e.offsetY;\n                var dx = x - this._x;\n                var dy = y - this._y;\n                this._x = x;\n                this._y = y;\n                draggingTarget.drift(dx, dy, e);\n                this.handler.dispatchToElement(new Param(draggingTarget, e), 'drag', e.event);\n                var dropTarget = this.handler.findHover(x, y, draggingTarget).target;\n                var lastDropTarget = this._dropTarget;\n                this._dropTarget = dropTarget;\n                if (draggingTarget !== dropTarget) {\n                    if (lastDropTarget && dropTarget !== lastDropTarget) {\n                        this.handler.dispatchToElement(new Param(lastDropTarget, e), 'dragleave', e.event);\n                    }\n                    if (dropTarget && dropTarget !== lastDropTarget) {\n                        this.handler.dispatchToElement(new Param(dropTarget, e), 'dragenter', e.event);\n                    }\n                }\n            }\n        };\n        Draggable.prototype._dragEnd = function (e) {\n            var draggingTarget = this._draggingTarget;\n            if (draggingTarget) {\n                draggingTarget.dragging = false;\n            }\n            this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragend', e.event);\n            if (this._dropTarget) {\n                this.handler.dispatchToElement(new Param(this._dropTarget, e), 'drop', e.event);\n            }\n            this._draggingTarget = null;\n            this._dropTarget = null;\n        };\n        return Draggable;\n    }());\n\n    var Eventful = (function () {\n        function Eventful(eventProcessors) {\n            if (eventProcessors) {\n                this._$eventProcessor = eventProcessors;\n            }\n        }\n        Eventful.prototype.on = function (event, query, handler, context) {\n            if (!this._$handlers) {\n                this._$handlers = {};\n            }\n            var _h = this._$handlers;\n            if (typeof query === 'function') {\n                context = handler;\n                handler = query;\n                query = null;\n            }\n            if (!handler || !event) {\n                return this;\n            }\n            var eventProcessor = this._$eventProcessor;\n            if (query != null && eventProcessor && eventProcessor.normalizeQuery) {\n                query = eventProcessor.normalizeQuery(query);\n            }\n            if (!_h[event]) {\n                _h[event] = [];\n            }\n            for (var i = 0; i < _h[event].length; i++) {\n                if (_h[event][i].h === handler) {\n                    return this;\n                }\n            }\n            var wrap = {\n                h: handler,\n                query: query,\n                ctx: (context || this),\n                callAtLast: handler.zrEventfulCallAtLast\n            };\n            var lastIndex = _h[event].length - 1;\n            var lastWrap = _h[event][lastIndex];\n            (lastWrap && lastWrap.callAtLast)\n                ? _h[event].splice(lastIndex, 0, wrap)\n                : _h[event].push(wrap);\n            return this;\n        };\n        Eventful.prototype.isSilent = function (eventName) {\n            var _h = this._$handlers;\n            return !_h || !_h[eventName] || !_h[eventName].length;\n        };\n        Eventful.prototype.off = function (eventType, handler) {\n            var _h = this._$handlers;\n            if (!_h) {\n                return this;\n            }\n            if (!eventType) {\n                this._$handlers = {};\n                return this;\n            }\n            if (handler) {\n                if (_h[eventType]) {\n                    var newList = [];\n                    for (var i = 0, l = _h[eventType].length; i < l; i++) {\n                        if (_h[eventType][i].h !== handler) {\n                            newList.push(_h[eventType][i]);\n                        }\n                    }\n                    _h[eventType] = newList;\n                }\n                if (_h[eventType] && _h[eventType].length === 0) {\n                    delete _h[eventType];\n                }\n            }\n            else {\n                delete _h[eventType];\n            }\n            return this;\n        };\n        Eventful.prototype.trigger = function (eventType) {\n            var args = [];\n            for (var _i = 1; _i < arguments.length; _i++) {\n                args[_i - 1] = arguments[_i];\n            }\n            if (!this._$handlers) {\n                return this;\n            }\n            var _h = this._$handlers[eventType];\n            var eventProcessor = this._$eventProcessor;\n            if (_h) {\n                var argLen = args.length;\n                var len = _h.length;\n                for (var i = 0; i < len; i++) {\n                    var hItem = _h[i];\n                    if (eventProcessor\n                        && eventProcessor.filter\n                        && hItem.query != null\n                        && !eventProcessor.filter(eventType, hItem.query)) {\n                        continue;\n                    }\n                    switch (argLen) {\n                        case 0:\n                            hItem.h.call(hItem.ctx);\n                            break;\n                        case 1:\n                            hItem.h.call(hItem.ctx, args[0]);\n                            break;\n                        case 2:\n                            hItem.h.call(hItem.ctx, args[0], args[1]);\n                            break;\n                        default:\n                            hItem.h.apply(hItem.ctx, args);\n                            break;\n                    }\n                }\n            }\n            eventProcessor && eventProcessor.afterTrigger\n                && eventProcessor.afterTrigger(eventType);\n            return this;\n        };\n        Eventful.prototype.triggerWithContext = function (type) {\n            var args = [];\n            for (var _i = 1; _i < arguments.length; _i++) {\n                args[_i - 1] = arguments[_i];\n            }\n            if (!this._$handlers) {\n                return this;\n            }\n            var _h = this._$handlers[type];\n            var eventProcessor = this._$eventProcessor;\n            if (_h) {\n                var argLen = args.length;\n                var ctx = args[argLen - 1];\n                var len = _h.length;\n                for (var i = 0; i < len; i++) {\n                    var hItem = _h[i];\n                    if (eventProcessor\n                        && eventProcessor.filter\n                        && hItem.query != null\n                        && !eventProcessor.filter(type, hItem.query)) {\n                        continue;\n                    }\n                    switch (argLen) {\n                        case 0:\n                            hItem.h.call(ctx);\n                            break;\n                        case 1:\n                            hItem.h.call(ctx, args[0]);\n                            break;\n                        case 2:\n                            hItem.h.call(ctx, args[0], args[1]);\n                            break;\n                        default:\n                            hItem.h.apply(ctx, args.slice(1, argLen - 1));\n                            break;\n                    }\n                }\n            }\n            eventProcessor && eventProcessor.afterTrigger\n                && eventProcessor.afterTrigger(type);\n            return this;\n        };\n        return Eventful;\n    }());\n\n    var LN2 = Math.log(2);\n    function determinant(rows, rank, rowStart, rowMask, colMask, detCache) {\n        var cacheKey = rowMask + '-' + colMask;\n        var fullRank = rows.length;\n        if (detCache.hasOwnProperty(cacheKey)) {\n            return detCache[cacheKey];\n        }\n        if (rank === 1) {\n            var colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2);\n            return rows[rowStart][colStart];\n        }\n        var subRowMask = rowMask | (1 << rowStart);\n        var subRowStart = rowStart + 1;\n        while (rowMask & (1 << subRowStart)) {\n            subRowStart++;\n        }\n        var sum = 0;\n        for (var j = 0, colLocalIdx = 0; j < fullRank; j++) {\n            var colTag = 1 << j;\n            if (!(colTag & colMask)) {\n                sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j]\n                    * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache);\n                colLocalIdx++;\n            }\n        }\n        detCache[cacheKey] = sum;\n        return sum;\n    }\n    function buildTransformer(src, dest) {\n        var mA = [\n            [src[0], src[1], 1, 0, 0, 0, -dest[0] * src[0], -dest[0] * src[1]],\n            [0, 0, 0, src[0], src[1], 1, -dest[1] * src[0], -dest[1] * src[1]],\n            [src[2], src[3], 1, 0, 0, 0, -dest[2] * src[2], -dest[2] * src[3]],\n            [0, 0, 0, src[2], src[3], 1, -dest[3] * src[2], -dest[3] * src[3]],\n            [src[4], src[5], 1, 0, 0, 0, -dest[4] * src[4], -dest[4] * src[5]],\n            [0, 0, 0, src[4], src[5], 1, -dest[5] * src[4], -dest[5] * src[5]],\n            [src[6], src[7], 1, 0, 0, 0, -dest[6] * src[6], -dest[6] * src[7]],\n            [0, 0, 0, src[6], src[7], 1, -dest[7] * src[6], -dest[7] * src[7]]\n        ];\n        var detCache = {};\n        var det = determinant(mA, 8, 0, 0, 0, detCache);\n        if (det === 0) {\n            return;\n        }\n        var vh = [];\n        for (var i = 0; i < 8; i++) {\n            for (var j = 0; j < 8; j++) {\n                vh[j] == null && (vh[j] = 0);\n                vh[j] += ((i + j) % 2 ? -1 : 1)\n                    * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache)\n                    / det * dest[i];\n            }\n        }\n        return function (out, srcPointX, srcPointY) {\n            var pk = srcPointX * vh[6] + srcPointY * vh[7] + 1;\n            out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk;\n            out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk;\n        };\n    }\n\n    var EVENT_SAVED_PROP = '___zrEVENTSAVED';\n    var _calcOut = [];\n    function transformLocalCoord(out, elFrom, elTarget, inX, inY) {\n        return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true)\n            && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]);\n    }\n    function transformCoordWithViewport(out, el, inX, inY, inverse) {\n        if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) {\n            var saved = el[EVENT_SAVED_PROP] || (el[EVENT_SAVED_PROP] = {});\n            var markers = prepareCoordMarkers(el, saved);\n            var transformer = preparePointerTransformer(markers, saved, inverse);\n            if (transformer) {\n                transformer(out, inX, inY);\n                return true;\n            }\n        }\n        return false;\n    }\n    function prepareCoordMarkers(el, saved) {\n        var markers = saved.markers;\n        if (markers) {\n            return markers;\n        }\n        markers = saved.markers = [];\n        var propLR = ['left', 'right'];\n        var propTB = ['top', 'bottom'];\n        for (var i = 0; i < 4; i++) {\n            var marker = document.createElement('div');\n            var stl = marker.style;\n            var idxLR = i % 2;\n            var idxTB = (i >> 1) % 2;\n            stl.cssText = [\n                'position: absolute',\n                'visibility: hidden',\n                'padding: 0',\n                'margin: 0',\n                'border-width: 0',\n                'user-select: none',\n                'width:0',\n                'height:0',\n                propLR[idxLR] + ':0',\n                propTB[idxTB] + ':0',\n                propLR[1 - idxLR] + ':auto',\n                propTB[1 - idxTB] + ':auto',\n                ''\n            ].join('!important;');\n            el.appendChild(marker);\n            markers.push(marker);\n        }\n        return markers;\n    }\n    function preparePointerTransformer(markers, saved, inverse) {\n        var transformerName = inverse ? 'invTrans' : 'trans';\n        var transformer = saved[transformerName];\n        var oldSrcCoords = saved.srcCoords;\n        var srcCoords = [];\n        var destCoords = [];\n        var oldCoordTheSame = true;\n        for (var i = 0; i < 4; i++) {\n            var rect = markers[i].getBoundingClientRect();\n            var ii = 2 * i;\n            var x = rect.left;\n            var y = rect.top;\n            srcCoords.push(x, y);\n            oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1];\n            destCoords.push(markers[i].offsetLeft, markers[i].offsetTop);\n        }\n        return (oldCoordTheSame && transformer)\n            ? transformer\n            : (saved.srcCoords = srcCoords,\n                saved[transformerName] = inverse\n                    ? buildTransformer(destCoords, srcCoords)\n                    : buildTransformer(srcCoords, destCoords));\n    }\n    function isCanvasEl(el) {\n        return el.nodeName.toUpperCase() === 'CANVAS';\n    }\n    var replaceReg = /([&<>\"'])/g;\n    var replaceMap = {\n        '&': '&amp;',\n        '<': '&lt;',\n        '>': '&gt;',\n        '\"': '&quot;',\n        '\\'': '&#39;'\n    };\n    function encodeHTML(source) {\n        return source == null\n            ? ''\n            : (source + '').replace(replaceReg, function (str, c) {\n                return replaceMap[c];\n            });\n    }\n\n    var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;\n    var _calcOut$1 = [];\n    var firefoxNotSupportOffsetXY = env.browser.firefox\n        && +env.browser.version.split('.')[0] < 39;\n    function clientToLocal(el, e, out, calculate) {\n        out = out || {};\n        if (calculate) {\n            calculateZrXY(el, e, out);\n        }\n        else if (firefoxNotSupportOffsetXY\n            && e.layerX != null\n            && e.layerX !== e.offsetX) {\n            out.zrX = e.layerX;\n            out.zrY = e.layerY;\n        }\n        else if (e.offsetX != null) {\n            out.zrX = e.offsetX;\n            out.zrY = e.offsetY;\n        }\n        else {\n            calculateZrXY(el, e, out);\n        }\n        return out;\n    }\n    function calculateZrXY(el, e, out) {\n        if (env.domSupported && el.getBoundingClientRect) {\n            var ex = e.clientX;\n            var ey = e.clientY;\n            if (isCanvasEl(el)) {\n                var box = el.getBoundingClientRect();\n                out.zrX = ex - box.left;\n                out.zrY = ey - box.top;\n                return;\n            }\n            else {\n                if (transformCoordWithViewport(_calcOut$1, el, ex, ey)) {\n                    out.zrX = _calcOut$1[0];\n                    out.zrY = _calcOut$1[1];\n                    return;\n                }\n            }\n        }\n        out.zrX = out.zrY = 0;\n    }\n    function getNativeEvent(e) {\n        return e\n            || window.event;\n    }\n    function normalizeEvent(el, e, calculate) {\n        e = getNativeEvent(e);\n        if (e.zrX != null) {\n            return e;\n        }\n        var eventType = e.type;\n        var isTouch = eventType && eventType.indexOf('touch') >= 0;\n        if (!isTouch) {\n            clientToLocal(el, e, e, calculate);\n            var wheelDelta = getWheelDeltaMayPolyfill(e);\n            e.zrDelta = wheelDelta ? wheelDelta / 120 : -(e.detail || 0) / 3;\n        }\n        else {\n            var touch = eventType !== 'touchend'\n                ? e.targetTouches[0]\n                : e.changedTouches[0];\n            touch && clientToLocal(el, touch, e, calculate);\n        }\n        var button = e.button;\n        if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {\n            e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n        }\n        return e;\n    }\n    function getWheelDeltaMayPolyfill(e) {\n        var rawWheelDelta = e.wheelDelta;\n        if (rawWheelDelta) {\n            return rawWheelDelta;\n        }\n        var deltaX = e.deltaX;\n        var deltaY = e.deltaY;\n        if (deltaX == null || deltaY == null) {\n            return rawWheelDelta;\n        }\n        var delta = deltaY !== 0 ? Math.abs(deltaY) : Math.abs(deltaX);\n        var sign = deltaY > 0 ? -1\n            : deltaY < 0 ? 1\n                : deltaX > 0 ? -1\n                    : 1;\n        return 3 * delta * sign;\n    }\n    function addEventListener(el, name, handler, opt) {\n        el.addEventListener(name, handler, opt);\n    }\n    function removeEventListener(el, name, handler, opt) {\n        el.removeEventListener(name, handler, opt);\n    }\n    var stop = function (e) {\n        e.preventDefault();\n        e.stopPropagation();\n        e.cancelBubble = true;\n    };\n    function isMiddleOrRightButtonOnMouseUpDown(e) {\n        return e.which === 2 || e.which === 3;\n    }\n\n    var GestureMgr = (function () {\n        function GestureMgr() {\n            this._track = [];\n        }\n        GestureMgr.prototype.recognize = function (event, target, root) {\n            this._doTrack(event, target, root);\n            return this._recognize(event);\n        };\n        GestureMgr.prototype.clear = function () {\n            this._track.length = 0;\n            return this;\n        };\n        GestureMgr.prototype._doTrack = function (event, target, root) {\n            var touches = event.touches;\n            if (!touches) {\n                return;\n            }\n            var trackItem = {\n                points: [],\n                touches: [],\n                target: target,\n                event: event\n            };\n            for (var i = 0, len = touches.length; i < len; i++) {\n                var touch = touches[i];\n                var pos = clientToLocal(root, touch, {});\n                trackItem.points.push([pos.zrX, pos.zrY]);\n                trackItem.touches.push(touch);\n            }\n            this._track.push(trackItem);\n        };\n        GestureMgr.prototype._recognize = function (event) {\n            for (var eventName in recognizers) {\n                if (recognizers.hasOwnProperty(eventName)) {\n                    var gestureInfo = recognizers[eventName](this._track, event);\n                    if (gestureInfo) {\n                        return gestureInfo;\n                    }\n                }\n            }\n        };\n        return GestureMgr;\n    }());\n    function dist$1(pointPair) {\n        var dx = pointPair[1][0] - pointPair[0][0];\n        var dy = pointPair[1][1] - pointPair[0][1];\n        return Math.sqrt(dx * dx + dy * dy);\n    }\n    function center(pointPair) {\n        return [\n            (pointPair[0][0] + pointPair[1][0]) / 2,\n            (pointPair[0][1] + pointPair[1][1]) / 2\n        ];\n    }\n    var recognizers = {\n        pinch: function (tracks, event) {\n            var trackLen = tracks.length;\n            if (!trackLen) {\n                return;\n            }\n            var pinchEnd = (tracks[trackLen - 1] || {}).points;\n            var pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd;\n            if (pinchPre\n                && pinchPre.length > 1\n                && pinchEnd\n                && pinchEnd.length > 1) {\n                var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);\n                !isFinite(pinchScale) && (pinchScale = 1);\n                event.pinchScale = pinchScale;\n                var pinchCenter = center(pinchEnd);\n                event.pinchX = pinchCenter[0];\n                event.pinchY = pinchCenter[1];\n                return {\n                    type: 'pinch',\n                    target: tracks[0].target,\n                    event: event\n                };\n            }\n        }\n    };\n\n    function create$1() {\n        return [1, 0, 0, 1, 0, 0];\n    }\n    function identity(out) {\n        out[0] = 1;\n        out[1] = 0;\n        out[2] = 0;\n        out[3] = 1;\n        out[4] = 0;\n        out[5] = 0;\n        return out;\n    }\n    function copy$1(out, m) {\n        out[0] = m[0];\n        out[1] = m[1];\n        out[2] = m[2];\n        out[3] = m[3];\n        out[4] = m[4];\n        out[5] = m[5];\n        return out;\n    }\n    function mul$1(out, m1, m2) {\n        var out0 = m1[0] * m2[0] + m1[2] * m2[1];\n        var out1 = m1[1] * m2[0] + m1[3] * m2[1];\n        var out2 = m1[0] * m2[2] + m1[2] * m2[3];\n        var out3 = m1[1] * m2[2] + m1[3] * m2[3];\n        var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];\n        var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];\n        out[0] = out0;\n        out[1] = out1;\n        out[2] = out2;\n        out[3] = out3;\n        out[4] = out4;\n        out[5] = out5;\n        return out;\n    }\n    function translate(out, a, v) {\n        out[0] = a[0];\n        out[1] = a[1];\n        out[2] = a[2];\n        out[3] = a[3];\n        out[4] = a[4] + v[0];\n        out[5] = a[5] + v[1];\n        return out;\n    }\n    function rotate(out, a, rad) {\n        var aa = a[0];\n        var ac = a[2];\n        var atx = a[4];\n        var ab = a[1];\n        var ad = a[3];\n        var aty = a[5];\n        var st = Math.sin(rad);\n        var ct = Math.cos(rad);\n        out[0] = aa * ct + ab * st;\n        out[1] = -aa * st + ab * ct;\n        out[2] = ac * ct + ad * st;\n        out[3] = -ac * st + ct * ad;\n        out[4] = ct * atx + st * aty;\n        out[5] = ct * aty - st * atx;\n        return out;\n    }\n    function scale$1(out, a, v) {\n        var vx = v[0];\n        var vy = v[1];\n        out[0] = a[0] * vx;\n        out[1] = a[1] * vy;\n        out[2] = a[2] * vx;\n        out[3] = a[3] * vy;\n        out[4] = a[4] * vx;\n        out[5] = a[5] * vy;\n        return out;\n    }\n    function invert(out, a) {\n        var aa = a[0];\n        var ac = a[2];\n        var atx = a[4];\n        var ab = a[1];\n        var ad = a[3];\n        var aty = a[5];\n        var det = aa * ad - ab * ac;\n        if (!det) {\n            return null;\n        }\n        det = 1.0 / det;\n        out[0] = ad * det;\n        out[1] = -ab * det;\n        out[2] = -ac * det;\n        out[3] = aa * det;\n        out[4] = (ac * aty - ad * atx) * det;\n        out[5] = (ab * atx - aa * aty) * det;\n        return out;\n    }\n    function clone$2(a) {\n        var b = create$1();\n        copy$1(b, a);\n        return b;\n    }\n\n    var matrix = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        create: create$1,\n        identity: identity,\n        copy: copy$1,\n        mul: mul$1,\n        translate: translate,\n        rotate: rotate,\n        scale: scale$1,\n        invert: invert,\n        clone: clone$2\n    });\n\n    var Point = (function () {\n        function Point(x, y) {\n            this.x = x || 0;\n            this.y = y || 0;\n        }\n        Point.prototype.copy = function (other) {\n            this.x = other.x;\n            this.y = other.y;\n            return this;\n        };\n        Point.prototype.clone = function () {\n            return new Point(this.x, this.y);\n        };\n        Point.prototype.set = function (x, y) {\n            this.x = x;\n            this.y = y;\n            return this;\n        };\n        Point.prototype.equal = function (other) {\n            return other.x === this.x && other.y === this.y;\n        };\n        Point.prototype.add = function (other) {\n            this.x += other.x;\n            this.y += other.y;\n            return this;\n        };\n        Point.prototype.scale = function (scalar) {\n            this.x *= scalar;\n            this.y *= scalar;\n        };\n        Point.prototype.scaleAndAdd = function (other, scalar) {\n            this.x += other.x * scalar;\n            this.y += other.y * scalar;\n        };\n        Point.prototype.sub = function (other) {\n            this.x -= other.x;\n            this.y -= other.y;\n            return this;\n        };\n        Point.prototype.dot = function (other) {\n            return this.x * other.x + this.y * other.y;\n        };\n        Point.prototype.len = function () {\n            return Math.sqrt(this.x * this.x + this.y * this.y);\n        };\n        Point.prototype.lenSquare = function () {\n            return this.x * this.x + this.y * this.y;\n        };\n        Point.prototype.normalize = function () {\n            var len = this.len();\n            this.x /= len;\n            this.y /= len;\n            return this;\n        };\n        Point.prototype.distance = function (other) {\n            var dx = this.x - other.x;\n            var dy = this.y - other.y;\n            return Math.sqrt(dx * dx + dy * dy);\n        };\n        Point.prototype.distanceSquare = function (other) {\n            var dx = this.x - other.x;\n            var dy = this.y - other.y;\n            return dx * dx + dy * dy;\n        };\n        Point.prototype.negate = function () {\n            this.x = -this.x;\n            this.y = -this.y;\n            return this;\n        };\n        Point.prototype.transform = function (m) {\n            if (!m) {\n                return;\n            }\n            var x = this.x;\n            var y = this.y;\n            this.x = m[0] * x + m[2] * y + m[4];\n            this.y = m[1] * x + m[3] * y + m[5];\n            return this;\n        };\n        Point.prototype.toArray = function (out) {\n            out[0] = this.x;\n            out[1] = this.y;\n            return out;\n        };\n        Point.prototype.fromArray = function (input) {\n            this.x = input[0];\n            this.y = input[1];\n        };\n        Point.set = function (p, x, y) {\n            p.x = x;\n            p.y = y;\n        };\n        Point.copy = function (p, p2) {\n            p.x = p2.x;\n            p.y = p2.y;\n        };\n        Point.len = function (p) {\n            return Math.sqrt(p.x * p.x + p.y * p.y);\n        };\n        Point.lenSquare = function (p) {\n            return p.x * p.x + p.y * p.y;\n        };\n        Point.dot = function (p0, p1) {\n            return p0.x * p1.x + p0.y * p1.y;\n        };\n        Point.add = function (out, p0, p1) {\n            out.x = p0.x + p1.x;\n            out.y = p0.y + p1.y;\n        };\n        Point.sub = function (out, p0, p1) {\n            out.x = p0.x - p1.x;\n            out.y = p0.y - p1.y;\n        };\n        Point.scale = function (out, p0, scalar) {\n            out.x = p0.x * scalar;\n            out.y = p0.y * scalar;\n        };\n        Point.scaleAndAdd = function (out, p0, p1, scalar) {\n            out.x = p0.x + p1.x * scalar;\n            out.y = p0.y + p1.y * scalar;\n        };\n        Point.lerp = function (out, p0, p1, t) {\n            var onet = 1 - t;\n            out.x = onet * p0.x + t * p1.x;\n            out.y = onet * p0.y + t * p1.y;\n        };\n        return Point;\n    }());\n\n    var mathMin = Math.min;\n    var mathMax = Math.max;\n    var lt = new Point();\n    var rb = new Point();\n    var lb = new Point();\n    var rt = new Point();\n    var minTv = new Point();\n    var maxTv = new Point();\n    var BoundingRect = (function () {\n        function BoundingRect(x, y, width, height) {\n            if (width < 0) {\n                x = x + width;\n                width = -width;\n            }\n            if (height < 0) {\n                y = y + height;\n                height = -height;\n            }\n            this.x = x;\n            this.y = y;\n            this.width = width;\n            this.height = height;\n        }\n        BoundingRect.prototype.union = function (other) {\n            var x = mathMin(other.x, this.x);\n            var y = mathMin(other.y, this.y);\n            if (isFinite(this.x) && isFinite(this.width)) {\n                this.width = mathMax(other.x + other.width, this.x + this.width) - x;\n            }\n            else {\n                this.width = other.width;\n            }\n            if (isFinite(this.y) && isFinite(this.height)) {\n                this.height = mathMax(other.y + other.height, this.y + this.height) - y;\n            }\n            else {\n                this.height = other.height;\n            }\n            this.x = x;\n            this.y = y;\n        };\n        BoundingRect.prototype.applyTransform = function (m) {\n            BoundingRect.applyTransform(this, this, m);\n        };\n        BoundingRect.prototype.calculateTransform = function (b) {\n            var a = this;\n            var sx = b.width / a.width;\n            var sy = b.height / a.height;\n            var m = create$1();\n            translate(m, m, [-a.x, -a.y]);\n            scale$1(m, m, [sx, sy]);\n            translate(m, m, [b.x, b.y]);\n            return m;\n        };\n        BoundingRect.prototype.intersect = function (b, mtv) {\n            if (!b) {\n                return false;\n            }\n            if (!(b instanceof BoundingRect)) {\n                b = BoundingRect.create(b);\n            }\n            var a = this;\n            var ax0 = a.x;\n            var ax1 = a.x + a.width;\n            var ay0 = a.y;\n            var ay1 = a.y + a.height;\n            var bx0 = b.x;\n            var bx1 = b.x + b.width;\n            var by0 = b.y;\n            var by1 = b.y + b.height;\n            var overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);\n            if (mtv) {\n                var dMin = Infinity;\n                var dMax = 0;\n                var d0 = Math.abs(ax1 - bx0);\n                var d1 = Math.abs(bx1 - ax0);\n                var d2 = Math.abs(ay1 - by0);\n                var d3 = Math.abs(by1 - ay0);\n                var dx = Math.min(d0, d1);\n                var dy = Math.min(d2, d3);\n                if (ax1 < bx0 || bx1 < ax0) {\n                    if (dx > dMax) {\n                        dMax = dx;\n                        if (d0 < d1) {\n                            Point.set(maxTv, -d0, 0);\n                        }\n                        else {\n                            Point.set(maxTv, d1, 0);\n                        }\n                    }\n                }\n                else {\n                    if (dx < dMin) {\n                        dMin = dx;\n                        if (d0 < d1) {\n                            Point.set(minTv, d0, 0);\n                        }\n                        else {\n                            Point.set(minTv, -d1, 0);\n                        }\n                    }\n                }\n                if (ay1 < by0 || by1 < ay0) {\n                    if (dy > dMax) {\n                        dMax = dy;\n                        if (d2 < d3) {\n                            Point.set(maxTv, 0, -d2);\n                        }\n                        else {\n                            Point.set(maxTv, 0, d3);\n                        }\n                    }\n                }\n                else {\n                    if (dx < dMin) {\n                        dMin = dx;\n                        if (d2 < d3) {\n                            Point.set(minTv, 0, d2);\n                        }\n                        else {\n                            Point.set(minTv, 0, -d3);\n                        }\n                    }\n                }\n            }\n            if (mtv) {\n                Point.copy(mtv, overlap ? minTv : maxTv);\n            }\n            return overlap;\n        };\n        BoundingRect.prototype.contain = function (x, y) {\n            var rect = this;\n            return x >= rect.x\n                && x <= (rect.x + rect.width)\n                && y >= rect.y\n                && y <= (rect.y + rect.height);\n        };\n        BoundingRect.prototype.clone = function () {\n            return new BoundingRect(this.x, this.y, this.width, this.height);\n        };\n        BoundingRect.prototype.copy = function (other) {\n            BoundingRect.copy(this, other);\n        };\n        BoundingRect.prototype.plain = function () {\n            return {\n                x: this.x,\n                y: this.y,\n                width: this.width,\n                height: this.height\n            };\n        };\n        BoundingRect.prototype.isFinite = function () {\n            return isFinite(this.x)\n                && isFinite(this.y)\n                && isFinite(this.width)\n                && isFinite(this.height);\n        };\n        BoundingRect.prototype.isZero = function () {\n            return this.width === 0 || this.height === 0;\n        };\n        BoundingRect.create = function (rect) {\n            return new BoundingRect(rect.x, rect.y, rect.width, rect.height);\n        };\n        BoundingRect.copy = function (target, source) {\n            target.x = source.x;\n            target.y = source.y;\n            target.width = source.width;\n            target.height = source.height;\n        };\n        BoundingRect.applyTransform = function (target, source, m) {\n            if (!m) {\n                if (target !== source) {\n                    BoundingRect.copy(target, source);\n                }\n                return;\n            }\n            if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) {\n                var sx = m[0];\n                var sy = m[3];\n                var tx = m[4];\n                var ty = m[5];\n                target.x = source.x * sx + tx;\n                target.y = source.y * sy + ty;\n                target.width = source.width * sx;\n                target.height = source.height * sy;\n                if (target.width < 0) {\n                    target.x += target.width;\n                    target.width = -target.width;\n                }\n                if (target.height < 0) {\n                    target.y += target.height;\n                    target.height = -target.height;\n                }\n                return;\n            }\n            lt.x = lb.x = source.x;\n            lt.y = rt.y = source.y;\n            rb.x = rt.x = source.x + source.width;\n            rb.y = lb.y = source.y + source.height;\n            lt.transform(m);\n            rt.transform(m);\n            rb.transform(m);\n            lb.transform(m);\n            target.x = mathMin(lt.x, rb.x, lb.x, rt.x);\n            target.y = mathMin(lt.y, rb.y, lb.y, rt.y);\n            var maxX = mathMax(lt.x, rb.x, lb.x, rt.x);\n            var maxY = mathMax(lt.y, rb.y, lb.y, rt.y);\n            target.width = maxX - target.x;\n            target.height = maxY - target.y;\n        };\n        return BoundingRect;\n    }());\n\n    var SILENT = 'silent';\n    function makeEventPacket(eveType, targetInfo, event) {\n        return {\n            type: eveType,\n            event: event,\n            target: targetInfo.target,\n            topTarget: targetInfo.topTarget,\n            cancelBubble: false,\n            offsetX: event.zrX,\n            offsetY: event.zrY,\n            gestureEvent: event.gestureEvent,\n            pinchX: event.pinchX,\n            pinchY: event.pinchY,\n            pinchScale: event.pinchScale,\n            wheelDelta: event.zrDelta,\n            zrByTouch: event.zrByTouch,\n            which: event.which,\n            stop: stopEvent\n        };\n    }\n    function stopEvent() {\n        stop(this.event);\n    }\n    var EmptyProxy = (function (_super) {\n        __extends(EmptyProxy, _super);\n        function EmptyProxy() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.handler = null;\n            return _this;\n        }\n        EmptyProxy.prototype.dispose = function () { };\n        EmptyProxy.prototype.setCursor = function () { };\n        return EmptyProxy;\n    }(Eventful));\n    var HoveredResult = (function () {\n        function HoveredResult(x, y) {\n            this.x = x;\n            this.y = y;\n        }\n        return HoveredResult;\n    }());\n    var handlerNames = [\n        'click', 'dblclick', 'mousewheel', 'mouseout',\n        'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n    ];\n    var tmpRect = new BoundingRect(0, 0, 0, 0);\n    var Handler = (function (_super) {\n        __extends(Handler, _super);\n        function Handler(storage, painter, proxy, painterRoot, pointerSize) {\n            var _this = _super.call(this) || this;\n            _this._hovered = new HoveredResult(0, 0);\n            _this.storage = storage;\n            _this.painter = painter;\n            _this.painterRoot = painterRoot;\n            _this._pointerSize = pointerSize;\n            proxy = proxy || new EmptyProxy();\n            _this.proxy = null;\n            _this.setHandlerProxy(proxy);\n            _this._draggingMgr = new Draggable(_this);\n            return _this;\n        }\n        Handler.prototype.setHandlerProxy = function (proxy) {\n            if (this.proxy) {\n                this.proxy.dispose();\n            }\n            if (proxy) {\n                each(handlerNames, function (name) {\n                    proxy.on && proxy.on(name, this[name], this);\n                }, this);\n                proxy.handler = this;\n            }\n            this.proxy = proxy;\n        };\n        Handler.prototype.mousemove = function (event) {\n            var x = event.zrX;\n            var y = event.zrY;\n            var isOutside = isOutsideBoundary(this, x, y);\n            var lastHovered = this._hovered;\n            var lastHoveredTarget = lastHovered.target;\n            if (lastHoveredTarget && !lastHoveredTarget.__zr) {\n                lastHovered = this.findHover(lastHovered.x, lastHovered.y);\n                lastHoveredTarget = lastHovered.target;\n            }\n            var hovered = this._hovered = isOutside ? new HoveredResult(x, y) : this.findHover(x, y);\n            var hoveredTarget = hovered.target;\n            var proxy = this.proxy;\n            proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');\n            if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {\n                this.dispatchToElement(lastHovered, 'mouseout', event);\n            }\n            this.dispatchToElement(hovered, 'mousemove', event);\n            if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {\n                this.dispatchToElement(hovered, 'mouseover', event);\n            }\n        };\n        Handler.prototype.mouseout = function (event) {\n            var eventControl = event.zrEventControl;\n            if (eventControl !== 'only_globalout') {\n                this.dispatchToElement(this._hovered, 'mouseout', event);\n            }\n            if (eventControl !== 'no_globalout') {\n                this.trigger('globalout', { type: 'globalout', event: event });\n            }\n        };\n        Handler.prototype.resize = function () {\n            this._hovered = new HoveredResult(0, 0);\n        };\n        Handler.prototype.dispatch = function (eventName, eventArgs) {\n            var handler = this[eventName];\n            handler && handler.call(this, eventArgs);\n        };\n        Handler.prototype.dispose = function () {\n            this.proxy.dispose();\n            this.storage = null;\n            this.proxy = null;\n            this.painter = null;\n        };\n        Handler.prototype.setCursorStyle = function (cursorStyle) {\n            var proxy = this.proxy;\n            proxy.setCursor && proxy.setCursor(cursorStyle);\n        };\n        Handler.prototype.dispatchToElement = function (targetInfo, eventName, event) {\n            targetInfo = targetInfo || {};\n            var el = targetInfo.target;\n            if (el && el.silent) {\n                return;\n            }\n            var eventKey = ('on' + eventName);\n            var eventPacket = makeEventPacket(eventName, targetInfo, event);\n            while (el) {\n                el[eventKey]\n                    && (eventPacket.cancelBubble = !!el[eventKey].call(el, eventPacket));\n                el.trigger(eventName, eventPacket);\n                el = el.__hostTarget ? el.__hostTarget : el.parent;\n                if (eventPacket.cancelBubble) {\n                    break;\n                }\n            }\n            if (!eventPacket.cancelBubble) {\n                this.trigger(eventName, eventPacket);\n                if (this.painter && this.painter.eachOtherLayer) {\n                    this.painter.eachOtherLayer(function (layer) {\n                        if (typeof (layer[eventKey]) === 'function') {\n                            layer[eventKey].call(layer, eventPacket);\n                        }\n                        if (layer.trigger) {\n                            layer.trigger(eventName, eventPacket);\n                        }\n                    });\n                }\n            }\n        };\n        Handler.prototype.findHover = function (x, y, exclude) {\n            var list = this.storage.getDisplayList();\n            var out = new HoveredResult(x, y);\n            setHoverTarget(list, out, x, y, exclude);\n            if (this._pointerSize && !out.target) {\n                var candidates = [];\n                var pointerSize = this._pointerSize;\n                var targetSizeHalf = pointerSize / 2;\n                var pointerRect = new BoundingRect(x - targetSizeHalf, y - targetSizeHalf, pointerSize, pointerSize);\n                for (var i = list.length - 1; i >= 0; i--) {\n                    var el = list[i];\n                    if (el !== exclude\n                        && !el.ignore\n                        && !el.ignoreCoarsePointer\n                        && (!el.parent || !el.parent.ignoreCoarsePointer)) {\n                        tmpRect.copy(el.getBoundingRect());\n                        if (el.transform) {\n                            tmpRect.applyTransform(el.transform);\n                        }\n                        if (tmpRect.intersect(pointerRect)) {\n                            candidates.push(el);\n                        }\n                    }\n                }\n                if (candidates.length) {\n                    var rStep = 4;\n                    var thetaStep = Math.PI / 12;\n                    var PI2 = Math.PI * 2;\n                    for (var r = 0; r < targetSizeHalf; r += rStep) {\n                        for (var theta = 0; theta < PI2; theta += thetaStep) {\n                            var x1 = x + r * Math.cos(theta);\n                            var y1 = y + r * Math.sin(theta);\n                            setHoverTarget(candidates, out, x1, y1, exclude);\n                            if (out.target) {\n                                return out;\n                            }\n                        }\n                    }\n                }\n            }\n            return out;\n        };\n        Handler.prototype.processGesture = function (event, stage) {\n            if (!this._gestureMgr) {\n                this._gestureMgr = new GestureMgr();\n            }\n            var gestureMgr = this._gestureMgr;\n            stage === 'start' && gestureMgr.clear();\n            var gestureInfo = gestureMgr.recognize(event, this.findHover(event.zrX, event.zrY, null).target, this.proxy.dom);\n            stage === 'end' && gestureMgr.clear();\n            if (gestureInfo) {\n                var type = gestureInfo.type;\n                event.gestureEvent = type;\n                var res = new HoveredResult();\n                res.target = gestureInfo.target;\n                this.dispatchToElement(res, type, gestureInfo.event);\n            }\n        };\n        return Handler;\n    }(Eventful));\n    each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {\n        Handler.prototype[name] = function (event) {\n            var x = event.zrX;\n            var y = event.zrY;\n            var isOutside = isOutsideBoundary(this, x, y);\n            var hovered;\n            var hoveredTarget;\n            if (name !== 'mouseup' || !isOutside) {\n                hovered = this.findHover(x, y);\n                hoveredTarget = hovered.target;\n            }\n            if (name === 'mousedown') {\n                this._downEl = hoveredTarget;\n                this._downPoint = [event.zrX, event.zrY];\n                this._upEl = hoveredTarget;\n            }\n            else if (name === 'mouseup') {\n                this._upEl = hoveredTarget;\n            }\n            else if (name === 'click') {\n                if (this._downEl !== this._upEl\n                    || !this._downPoint\n                    || dist(this._downPoint, [event.zrX, event.zrY]) > 4) {\n                    return;\n                }\n                this._downPoint = null;\n            }\n            this.dispatchToElement(hovered, name, event);\n        };\n    });\n    function isHover(displayable, x, y) {\n        if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {\n            var el = displayable;\n            var isSilent = void 0;\n            var ignoreClip = false;\n            while (el) {\n                if (el.ignoreClip) {\n                    ignoreClip = true;\n                }\n                if (!ignoreClip) {\n                    var clipPath = el.getClipPath();\n                    if (clipPath && !clipPath.contain(x, y)) {\n                        return false;\n                    }\n                    if (el.silent) {\n                        isSilent = true;\n                    }\n                }\n                var hostEl = el.__hostTarget;\n                el = hostEl ? hostEl : el.parent;\n            }\n            return isSilent ? SILENT : true;\n        }\n        return false;\n    }\n    function setHoverTarget(list, out, x, y, exclude) {\n        for (var i = list.length - 1; i >= 0; i--) {\n            var el = list[i];\n            var hoverCheckResult = void 0;\n            if (el !== exclude\n                && !el.ignore\n                && (hoverCheckResult = isHover(el, x, y))) {\n                !out.topTarget && (out.topTarget = el);\n                if (hoverCheckResult !== SILENT) {\n                    out.target = el;\n                    break;\n                }\n            }\n        }\n    }\n    function isOutsideBoundary(handlerInstance, x, y) {\n        var painter = handlerInstance.painter;\n        return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight();\n    }\n\n    var DEFAULT_MIN_MERGE = 32;\n    var DEFAULT_MIN_GALLOPING = 7;\n    function minRunLength(n) {\n        var r = 0;\n        while (n >= DEFAULT_MIN_MERGE) {\n            r |= n & 1;\n            n >>= 1;\n        }\n        return n + r;\n    }\n    function makeAscendingRun(array, lo, hi, compare) {\n        var runHi = lo + 1;\n        if (runHi === hi) {\n            return 1;\n        }\n        if (compare(array[runHi++], array[lo]) < 0) {\n            while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {\n                runHi++;\n            }\n            reverseRun(array, lo, runHi);\n        }\n        else {\n            while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {\n                runHi++;\n            }\n        }\n        return runHi - lo;\n    }\n    function reverseRun(array, lo, hi) {\n        hi--;\n        while (lo < hi) {\n            var t = array[lo];\n            array[lo++] = array[hi];\n            array[hi--] = t;\n        }\n    }\n    function binaryInsertionSort(array, lo, hi, start, compare) {\n        if (start === lo) {\n            start++;\n        }\n        for (; start < hi; start++) {\n            var pivot = array[start];\n            var left = lo;\n            var right = start;\n            var mid;\n            while (left < right) {\n                mid = left + right >>> 1;\n                if (compare(pivot, array[mid]) < 0) {\n                    right = mid;\n                }\n                else {\n                    left = mid + 1;\n                }\n            }\n            var n = start - left;\n            switch (n) {\n                case 3:\n                    array[left + 3] = array[left + 2];\n                case 2:\n                    array[left + 2] = array[left + 1];\n                case 1:\n                    array[left + 1] = array[left];\n                    break;\n                default:\n                    while (n > 0) {\n                        array[left + n] = array[left + n - 1];\n                        n--;\n                    }\n            }\n            array[left] = pivot;\n        }\n    }\n    function gallopLeft(value, array, start, length, hint, compare) {\n        var lastOffset = 0;\n        var maxOffset = 0;\n        var offset = 1;\n        if (compare(value, array[start + hint]) > 0) {\n            maxOffset = length - hint;\n            while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            lastOffset += hint;\n            offset += hint;\n        }\n        else {\n            maxOffset = hint + 1;\n            while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            var tmp = lastOffset;\n            lastOffset = hint - offset;\n            offset = hint - tmp;\n        }\n        lastOffset++;\n        while (lastOffset < offset) {\n            var m = lastOffset + (offset - lastOffset >>> 1);\n            if (compare(value, array[start + m]) > 0) {\n                lastOffset = m + 1;\n            }\n            else {\n                offset = m;\n            }\n        }\n        return offset;\n    }\n    function gallopRight(value, array, start, length, hint, compare) {\n        var lastOffset = 0;\n        var maxOffset = 0;\n        var offset = 1;\n        if (compare(value, array[start + hint]) < 0) {\n            maxOffset = hint + 1;\n            while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            var tmp = lastOffset;\n            lastOffset = hint - offset;\n            offset = hint - tmp;\n        }\n        else {\n            maxOffset = length - hint;\n            while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            lastOffset += hint;\n            offset += hint;\n        }\n        lastOffset++;\n        while (lastOffset < offset) {\n            var m = lastOffset + (offset - lastOffset >>> 1);\n            if (compare(value, array[start + m]) < 0) {\n                offset = m;\n            }\n            else {\n                lastOffset = m + 1;\n            }\n        }\n        return offset;\n    }\n    function TimSort(array, compare) {\n        var minGallop = DEFAULT_MIN_GALLOPING;\n        var length = 0;\n        var runStart;\n        var runLength;\n        var stackSize = 0;\n        length = array.length;\n        var tmp = [];\n        runStart = [];\n        runLength = [];\n        function pushRun(_runStart, _runLength) {\n            runStart[stackSize] = _runStart;\n            runLength[stackSize] = _runLength;\n            stackSize += 1;\n        }\n        function mergeRuns() {\n            while (stackSize > 1) {\n                var n = stackSize - 2;\n                if ((n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1])\n                    || (n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1])) {\n                    if (runLength[n - 1] < runLength[n + 1]) {\n                        n--;\n                    }\n                }\n                else if (runLength[n] > runLength[n + 1]) {\n                    break;\n                }\n                mergeAt(n);\n            }\n        }\n        function forceMergeRuns() {\n            while (stackSize > 1) {\n                var n = stackSize - 2;\n                if (n > 0 && runLength[n - 1] < runLength[n + 1]) {\n                    n--;\n                }\n                mergeAt(n);\n            }\n        }\n        function mergeAt(i) {\n            var start1 = runStart[i];\n            var length1 = runLength[i];\n            var start2 = runStart[i + 1];\n            var length2 = runLength[i + 1];\n            runLength[i] = length1 + length2;\n            if (i === stackSize - 3) {\n                runStart[i + 1] = runStart[i + 2];\n                runLength[i + 1] = runLength[i + 2];\n            }\n            stackSize--;\n            var k = gallopRight(array[start2], array, start1, length1, 0, compare);\n            start1 += k;\n            length1 -= k;\n            if (length1 === 0) {\n                return;\n            }\n            length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);\n            if (length2 === 0) {\n                return;\n            }\n            if (length1 <= length2) {\n                mergeLow(start1, length1, start2, length2);\n            }\n            else {\n                mergeHigh(start1, length1, start2, length2);\n            }\n        }\n        function mergeLow(start1, length1, start2, length2) {\n            var i = 0;\n            for (i = 0; i < length1; i++) {\n                tmp[i] = array[start1 + i];\n            }\n            var cursor1 = 0;\n            var cursor2 = start2;\n            var dest = start1;\n            array[dest++] = array[cursor2++];\n            if (--length2 === 0) {\n                for (i = 0; i < length1; i++) {\n                    array[dest + i] = tmp[cursor1 + i];\n                }\n                return;\n            }\n            if (length1 === 1) {\n                for (i = 0; i < length2; i++) {\n                    array[dest + i] = array[cursor2 + i];\n                }\n                array[dest + length2] = tmp[cursor1];\n                return;\n            }\n            var _minGallop = minGallop;\n            var count1;\n            var count2;\n            var exit;\n            while (1) {\n                count1 = 0;\n                count2 = 0;\n                exit = false;\n                do {\n                    if (compare(array[cursor2], tmp[cursor1]) < 0) {\n                        array[dest++] = array[cursor2++];\n                        count2++;\n                        count1 = 0;\n                        if (--length2 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    else {\n                        array[dest++] = tmp[cursor1++];\n                        count1++;\n                        count2 = 0;\n                        if (--length1 === 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                } while ((count1 | count2) < _minGallop);\n                if (exit) {\n                    break;\n                }\n                do {\n                    count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);\n                    if (count1 !== 0) {\n                        for (i = 0; i < count1; i++) {\n                            array[dest + i] = tmp[cursor1 + i];\n                        }\n                        dest += count1;\n                        cursor1 += count1;\n                        length1 -= count1;\n                        if (length1 <= 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest++] = array[cursor2++];\n                    if (--length2 === 0) {\n                        exit = true;\n                        break;\n                    }\n                    count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);\n                    if (count2 !== 0) {\n                        for (i = 0; i < count2; i++) {\n                            array[dest + i] = array[cursor2 + i];\n                        }\n                        dest += count2;\n                        cursor2 += count2;\n                        length2 -= count2;\n                        if (length2 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest++] = tmp[cursor1++];\n                    if (--length1 === 1) {\n                        exit = true;\n                        break;\n                    }\n                    _minGallop--;\n                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n                if (exit) {\n                    break;\n                }\n                if (_minGallop < 0) {\n                    _minGallop = 0;\n                }\n                _minGallop += 2;\n            }\n            minGallop = _minGallop;\n            minGallop < 1 && (minGallop = 1);\n            if (length1 === 1) {\n                for (i = 0; i < length2; i++) {\n                    array[dest + i] = array[cursor2 + i];\n                }\n                array[dest + length2] = tmp[cursor1];\n            }\n            else if (length1 === 0) {\n                throw new Error();\n            }\n            else {\n                for (i = 0; i < length1; i++) {\n                    array[dest + i] = tmp[cursor1 + i];\n                }\n            }\n        }\n        function mergeHigh(start1, length1, start2, length2) {\n            var i = 0;\n            for (i = 0; i < length2; i++) {\n                tmp[i] = array[start2 + i];\n            }\n            var cursor1 = start1 + length1 - 1;\n            var cursor2 = length2 - 1;\n            var dest = start2 + length2 - 1;\n            var customCursor = 0;\n            var customDest = 0;\n            array[dest--] = array[cursor1--];\n            if (--length1 === 0) {\n                customCursor = dest - (length2 - 1);\n                for (i = 0; i < length2; i++) {\n                    array[customCursor + i] = tmp[i];\n                }\n                return;\n            }\n            if (length2 === 1) {\n                dest -= length1;\n                cursor1 -= length1;\n                customDest = dest + 1;\n                customCursor = cursor1 + 1;\n                for (i = length1 - 1; i >= 0; i--) {\n                    array[customDest + i] = array[customCursor + i];\n                }\n                array[dest] = tmp[cursor2];\n                return;\n            }\n            var _minGallop = minGallop;\n            while (true) {\n                var count1 = 0;\n                var count2 = 0;\n                var exit = false;\n                do {\n                    if (compare(tmp[cursor2], array[cursor1]) < 0) {\n                        array[dest--] = array[cursor1--];\n                        count1++;\n                        count2 = 0;\n                        if (--length1 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    else {\n                        array[dest--] = tmp[cursor2--];\n                        count2++;\n                        count1 = 0;\n                        if (--length2 === 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                } while ((count1 | count2) < _minGallop);\n                if (exit) {\n                    break;\n                }\n                do {\n                    count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);\n                    if (count1 !== 0) {\n                        dest -= count1;\n                        cursor1 -= count1;\n                        length1 -= count1;\n                        customDest = dest + 1;\n                        customCursor = cursor1 + 1;\n                        for (i = count1 - 1; i >= 0; i--) {\n                            array[customDest + i] = array[customCursor + i];\n                        }\n                        if (length1 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest--] = tmp[cursor2--];\n                    if (--length2 === 1) {\n                        exit = true;\n                        break;\n                    }\n                    count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);\n                    if (count2 !== 0) {\n                        dest -= count2;\n                        cursor2 -= count2;\n                        length2 -= count2;\n                        customDest = dest + 1;\n                        customCursor = cursor2 + 1;\n                        for (i = 0; i < count2; i++) {\n                            array[customDest + i] = tmp[customCursor + i];\n                        }\n                        if (length2 <= 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest--] = array[cursor1--];\n                    if (--length1 === 0) {\n                        exit = true;\n                        break;\n                    }\n                    _minGallop--;\n                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n                if (exit) {\n                    break;\n                }\n                if (_minGallop < 0) {\n                    _minGallop = 0;\n                }\n                _minGallop += 2;\n            }\n            minGallop = _minGallop;\n            if (minGallop < 1) {\n                minGallop = 1;\n            }\n            if (length2 === 1) {\n                dest -= length1;\n                cursor1 -= length1;\n                customDest = dest + 1;\n                customCursor = cursor1 + 1;\n                for (i = length1 - 1; i >= 0; i--) {\n                    array[customDest + i] = array[customCursor + i];\n                }\n                array[dest] = tmp[cursor2];\n            }\n            else if (length2 === 0) {\n                throw new Error();\n            }\n            else {\n                customCursor = dest - (length2 - 1);\n                for (i = 0; i < length2; i++) {\n                    array[customCursor + i] = tmp[i];\n                }\n            }\n        }\n        return {\n            mergeRuns: mergeRuns,\n            forceMergeRuns: forceMergeRuns,\n            pushRun: pushRun\n        };\n    }\n    function sort(array, compare, lo, hi) {\n        if (!lo) {\n            lo = 0;\n        }\n        if (!hi) {\n            hi = array.length;\n        }\n        var remaining = hi - lo;\n        if (remaining < 2) {\n            return;\n        }\n        var runLength = 0;\n        if (remaining < DEFAULT_MIN_MERGE) {\n            runLength = makeAscendingRun(array, lo, hi, compare);\n            binaryInsertionSort(array, lo, hi, lo + runLength, compare);\n            return;\n        }\n        var ts = TimSort(array, compare);\n        var minRun = minRunLength(remaining);\n        do {\n            runLength = makeAscendingRun(array, lo, hi, compare);\n            if (runLength < minRun) {\n                var force = remaining;\n                if (force > minRun) {\n                    force = minRun;\n                }\n                binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);\n                runLength = force;\n            }\n            ts.pushRun(lo, runLength);\n            ts.mergeRuns();\n            remaining -= runLength;\n            lo += runLength;\n        } while (remaining !== 0);\n        ts.forceMergeRuns();\n    }\n\n    var REDRAW_BIT = 1;\n    var STYLE_CHANGED_BIT = 2;\n    var SHAPE_CHANGED_BIT = 4;\n\n    var invalidZErrorLogged = false;\n    function logInvalidZError() {\n        if (invalidZErrorLogged) {\n            return;\n        }\n        invalidZErrorLogged = true;\n        console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors');\n    }\n    function shapeCompareFunc(a, b) {\n        if (a.zlevel === b.zlevel) {\n            if (a.z === b.z) {\n                return a.z2 - b.z2;\n            }\n            return a.z - b.z;\n        }\n        return a.zlevel - b.zlevel;\n    }\n    var Storage = (function () {\n        function Storage() {\n            this._roots = [];\n            this._displayList = [];\n            this._displayListLen = 0;\n            this.displayableSortFunc = shapeCompareFunc;\n        }\n        Storage.prototype.traverse = function (cb, context) {\n            for (var i = 0; i < this._roots.length; i++) {\n                this._roots[i].traverse(cb, context);\n            }\n        };\n        Storage.prototype.getDisplayList = function (update, includeIgnore) {\n            includeIgnore = includeIgnore || false;\n            var displayList = this._displayList;\n            if (update || !displayList.length) {\n                this.updateDisplayList(includeIgnore);\n            }\n            return displayList;\n        };\n        Storage.prototype.updateDisplayList = function (includeIgnore) {\n            this._displayListLen = 0;\n            var roots = this._roots;\n            var displayList = this._displayList;\n            for (var i = 0, len = roots.length; i < len; i++) {\n                this._updateAndAddDisplayable(roots[i], null, includeIgnore);\n            }\n            displayList.length = this._displayListLen;\n            sort(displayList, shapeCompareFunc);\n        };\n        Storage.prototype._updateAndAddDisplayable = function (el, clipPaths, includeIgnore) {\n            if (el.ignore && !includeIgnore) {\n                return;\n            }\n            el.beforeUpdate();\n            el.update();\n            el.afterUpdate();\n            var userSetClipPath = el.getClipPath();\n            if (el.ignoreClip) {\n                clipPaths = null;\n            }\n            else if (userSetClipPath) {\n                if (clipPaths) {\n                    clipPaths = clipPaths.slice();\n                }\n                else {\n                    clipPaths = [];\n                }\n                var currentClipPath = userSetClipPath;\n                var parentClipPath = el;\n                while (currentClipPath) {\n                    currentClipPath.parent = parentClipPath;\n                    currentClipPath.updateTransform();\n                    clipPaths.push(currentClipPath);\n                    parentClipPath = currentClipPath;\n                    currentClipPath = currentClipPath.getClipPath();\n                }\n            }\n            if (el.childrenRef) {\n                var children = el.childrenRef();\n                for (var i = 0; i < children.length; i++) {\n                    var child = children[i];\n                    if (el.__dirty) {\n                        child.__dirty |= REDRAW_BIT;\n                    }\n                    this._updateAndAddDisplayable(child, clipPaths, includeIgnore);\n                }\n                el.__dirty = 0;\n            }\n            else {\n                var disp = el;\n                if (clipPaths && clipPaths.length) {\n                    disp.__clipPaths = clipPaths;\n                }\n                else if (disp.__clipPaths && disp.__clipPaths.length > 0) {\n                    disp.__clipPaths = [];\n                }\n                if (isNaN(disp.z)) {\n                    logInvalidZError();\n                    disp.z = 0;\n                }\n                if (isNaN(disp.z2)) {\n                    logInvalidZError();\n                    disp.z2 = 0;\n                }\n                if (isNaN(disp.zlevel)) {\n                    logInvalidZError();\n                    disp.zlevel = 0;\n                }\n                this._displayList[this._displayListLen++] = disp;\n            }\n            var decalEl = el.getDecalElement && el.getDecalElement();\n            if (decalEl) {\n                this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore);\n            }\n            var textGuide = el.getTextGuideLine();\n            if (textGuide) {\n                this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore);\n            }\n            var textEl = el.getTextContent();\n            if (textEl) {\n                this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore);\n            }\n        };\n        Storage.prototype.addRoot = function (el) {\n            if (el.__zr && el.__zr.storage === this) {\n                return;\n            }\n            this._roots.push(el);\n        };\n        Storage.prototype.delRoot = function (el) {\n            if (el instanceof Array) {\n                for (var i = 0, l = el.length; i < l; i++) {\n                    this.delRoot(el[i]);\n                }\n                return;\n            }\n            var idx = indexOf(this._roots, el);\n            if (idx >= 0) {\n                this._roots.splice(idx, 1);\n            }\n        };\n        Storage.prototype.delAllRoots = function () {\n            this._roots = [];\n            this._displayList = [];\n            this._displayListLen = 0;\n            return;\n        };\n        Storage.prototype.getRoots = function () {\n            return this._roots;\n        };\n        Storage.prototype.dispose = function () {\n            this._displayList = null;\n            this._roots = null;\n        };\n        return Storage;\n    }());\n\n    var requestAnimationFrame;\n    requestAnimationFrame = (env.hasGlobalWindow\n        && ((window.requestAnimationFrame && window.requestAnimationFrame.bind(window))\n            || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))\n            || window.mozRequestAnimationFrame\n            || window.webkitRequestAnimationFrame)) || function (func) {\n        return setTimeout(func, 16);\n    };\n    var requestAnimationFrame$1 = requestAnimationFrame;\n\n    var easingFuncs = {\n        linear: function (k) {\n            return k;\n        },\n        quadraticIn: function (k) {\n            return k * k;\n        },\n        quadraticOut: function (k) {\n            return k * (2 - k);\n        },\n        quadraticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k;\n            }\n            return -0.5 * (--k * (k - 2) - 1);\n        },\n        cubicIn: function (k) {\n            return k * k * k;\n        },\n        cubicOut: function (k) {\n            return --k * k * k + 1;\n        },\n        cubicInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k;\n            }\n            return 0.5 * ((k -= 2) * k * k + 2);\n        },\n        quarticIn: function (k) {\n            return k * k * k * k;\n        },\n        quarticOut: function (k) {\n            return 1 - (--k * k * k * k);\n        },\n        quarticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k * k;\n            }\n            return -0.5 * ((k -= 2) * k * k * k - 2);\n        },\n        quinticIn: function (k) {\n            return k * k * k * k * k;\n        },\n        quinticOut: function (k) {\n            return --k * k * k * k * k + 1;\n        },\n        quinticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k * k * k;\n            }\n            return 0.5 * ((k -= 2) * k * k * k * k + 2);\n        },\n        sinusoidalIn: function (k) {\n            return 1 - Math.cos(k * Math.PI / 2);\n        },\n        sinusoidalOut: function (k) {\n            return Math.sin(k * Math.PI / 2);\n        },\n        sinusoidalInOut: function (k) {\n            return 0.5 * (1 - Math.cos(Math.PI * k));\n        },\n        exponentialIn: function (k) {\n            return k === 0 ? 0 : Math.pow(1024, k - 1);\n        },\n        exponentialOut: function (k) {\n            return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);\n        },\n        exponentialInOut: function (k) {\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if ((k *= 2) < 1) {\n                return 0.5 * Math.pow(1024, k - 1);\n            }\n            return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);\n        },\n        circularIn: function (k) {\n            return 1 - Math.sqrt(1 - k * k);\n        },\n        circularOut: function (k) {\n            return Math.sqrt(1 - (--k * k));\n        },\n        circularInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return -0.5 * (Math.sqrt(1 - k * k) - 1);\n            }\n            return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);\n        },\n        elasticIn: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            return -(a * Math.pow(2, 10 * (k -= 1))\n                * Math.sin((k - s) * (2 * Math.PI) / p));\n        },\n        elasticOut: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            return (a * Math.pow(2, -10 * k)\n                * Math.sin((k - s) * (2 * Math.PI) / p) + 1);\n        },\n        elasticInOut: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            if ((k *= 2) < 1) {\n                return -0.5 * (a * Math.pow(2, 10 * (k -= 1))\n                    * Math.sin((k - s) * (2 * Math.PI) / p));\n            }\n            return a * Math.pow(2, -10 * (k -= 1))\n                * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;\n        },\n        backIn: function (k) {\n            var s = 1.70158;\n            return k * k * ((s + 1) * k - s);\n        },\n        backOut: function (k) {\n            var s = 1.70158;\n            return --k * k * ((s + 1) * k + s) + 1;\n        },\n        backInOut: function (k) {\n            var s = 1.70158 * 1.525;\n            if ((k *= 2) < 1) {\n                return 0.5 * (k * k * ((s + 1) * k - s));\n            }\n            return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);\n        },\n        bounceIn: function (k) {\n            return 1 - easingFuncs.bounceOut(1 - k);\n        },\n        bounceOut: function (k) {\n            if (k < (1 / 2.75)) {\n                return 7.5625 * k * k;\n            }\n            else if (k < (2 / 2.75)) {\n                return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;\n            }\n            else if (k < (2.5 / 2.75)) {\n                return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;\n            }\n            else {\n                return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;\n            }\n        },\n        bounceInOut: function (k) {\n            if (k < 0.5) {\n                return easingFuncs.bounceIn(k * 2) * 0.5;\n            }\n            return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5;\n        }\n    };\n\n    var mathPow = Math.pow;\n    var mathSqrt = Math.sqrt;\n    var EPSILON = 1e-8;\n    var EPSILON_NUMERIC = 1e-4;\n    var THREE_SQRT = mathSqrt(3);\n    var ONE_THIRD = 1 / 3;\n    var _v0 = create();\n    var _v1 = create();\n    var _v2 = create();\n    function isAroundZero(val) {\n        return val > -EPSILON && val < EPSILON;\n    }\n    function isNotAroundZero(val) {\n        return val > EPSILON || val < -EPSILON;\n    }\n    function cubicAt(p0, p1, p2, p3, t) {\n        var onet = 1 - t;\n        return onet * onet * (onet * p0 + 3 * t * p1)\n            + t * t * (t * p3 + 3 * onet * p2);\n    }\n    function cubicDerivativeAt(p0, p1, p2, p3, t) {\n        var onet = 1 - t;\n        return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet\n            + (p3 - p2) * t * t);\n    }\n    function cubicRootAt(p0, p1, p2, p3, val, roots) {\n        var a = p3 + 3 * (p1 - p2) - p0;\n        var b = 3 * (p2 - p1 * 2 + p0);\n        var c = 3 * (p1 - p0);\n        var d = p0 - val;\n        var A = b * b - 3 * a * c;\n        var B = b * c - 9 * a * d;\n        var C = c * c - 3 * b * d;\n        var n = 0;\n        if (isAroundZero(A) && isAroundZero(B)) {\n            if (isAroundZero(b)) {\n                roots[0] = 0;\n            }\n            else {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = B * B - 4 * A * C;\n            if (isAroundZero(disc)) {\n                var K = B / A;\n                var t1 = -b / a + K;\n                var t2 = -K / 2;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var Y1 = A * b + 1.5 * a * (-B + discSqrt);\n                var Y2 = A * b + 1.5 * a * (-B - discSqrt);\n                if (Y1 < 0) {\n                    Y1 = -mathPow(-Y1, ONE_THIRD);\n                }\n                else {\n                    Y1 = mathPow(Y1, ONE_THIRD);\n                }\n                if (Y2 < 0) {\n                    Y2 = -mathPow(-Y2, ONE_THIRD);\n                }\n                else {\n                    Y2 = mathPow(Y2, ONE_THIRD);\n                }\n                var t1 = (-b - (Y1 + Y2)) / (3 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n            else {\n                var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));\n                var theta = Math.acos(T) / 3;\n                var ASqrt = mathSqrt(A);\n                var tmp = Math.cos(theta);\n                var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);\n                var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);\n                var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n                if (t3 >= 0 && t3 <= 1) {\n                    roots[n++] = t3;\n                }\n            }\n        }\n        return n;\n    }\n    function cubicExtrema(p0, p1, p2, p3, extrema) {\n        var b = 6 * p2 - 12 * p1 + 6 * p0;\n        var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;\n        var c = 3 * p1 - 3 * p0;\n        var n = 0;\n        if (isAroundZero(a)) {\n            if (isNotAroundZero(b)) {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    extrema[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = b * b - 4 * a * c;\n            if (isAroundZero(disc)) {\n                extrema[0] = -b / (2 * a);\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var t1 = (-b + discSqrt) / (2 * a);\n                var t2 = (-b - discSqrt) / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    extrema[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    extrema[n++] = t2;\n                }\n            }\n        }\n        return n;\n    }\n    function cubicSubdivide(p0, p1, p2, p3, t, out) {\n        var p01 = (p1 - p0) * t + p0;\n        var p12 = (p2 - p1) * t + p1;\n        var p23 = (p3 - p2) * t + p2;\n        var p012 = (p12 - p01) * t + p01;\n        var p123 = (p23 - p12) * t + p12;\n        var p0123 = (p123 - p012) * t + p012;\n        out[0] = p0;\n        out[1] = p01;\n        out[2] = p012;\n        out[3] = p0123;\n        out[4] = p0123;\n        out[5] = p123;\n        out[6] = p23;\n        out[7] = p3;\n    }\n    function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) {\n        var t;\n        var interval = 0.005;\n        var d = Infinity;\n        var prev;\n        var next;\n        var d1;\n        var d2;\n        _v0[0] = x;\n        _v0[1] = y;\n        for (var _t = 0; _t < 1; _t += 0.05) {\n            _v1[0] = cubicAt(x0, x1, x2, x3, _t);\n            _v1[1] = cubicAt(y0, y1, y2, y3, _t);\n            d1 = distSquare(_v0, _v1);\n            if (d1 < d) {\n                t = _t;\n                d = d1;\n            }\n        }\n        d = Infinity;\n        for (var i = 0; i < 32; i++) {\n            if (interval < EPSILON_NUMERIC) {\n                break;\n            }\n            prev = t - interval;\n            next = t + interval;\n            _v1[0] = cubicAt(x0, x1, x2, x3, prev);\n            _v1[1] = cubicAt(y0, y1, y2, y3, prev);\n            d1 = distSquare(_v1, _v0);\n            if (prev >= 0 && d1 < d) {\n                t = prev;\n                d = d1;\n            }\n            else {\n                _v2[0] = cubicAt(x0, x1, x2, x3, next);\n                _v2[1] = cubicAt(y0, y1, y2, y3, next);\n                d2 = distSquare(_v2, _v0);\n                if (next <= 1 && d2 < d) {\n                    t = next;\n                    d = d2;\n                }\n                else {\n                    interval *= 0.5;\n                }\n            }\n        }\n        if (out) {\n            out[0] = cubicAt(x0, x1, x2, x3, t);\n            out[1] = cubicAt(y0, y1, y2, y3, t);\n        }\n        return mathSqrt(d);\n    }\n    function cubicLength(x0, y0, x1, y1, x2, y2, x3, y3, iteration) {\n        var px = x0;\n        var py = y0;\n        var d = 0;\n        var step = 1 / iteration;\n        for (var i = 1; i <= iteration; i++) {\n            var t = i * step;\n            var x = cubicAt(x0, x1, x2, x3, t);\n            var y = cubicAt(y0, y1, y2, y3, t);\n            var dx = x - px;\n            var dy = y - py;\n            d += Math.sqrt(dx * dx + dy * dy);\n            px = x;\n            py = y;\n        }\n        return d;\n    }\n    function quadraticAt(p0, p1, p2, t) {\n        var onet = 1 - t;\n        return onet * (onet * p0 + 2 * t * p1) + t * t * p2;\n    }\n    function quadraticDerivativeAt(p0, p1, p2, t) {\n        return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));\n    }\n    function quadraticRootAt(p0, p1, p2, val, roots) {\n        var a = p0 - 2 * p1 + p2;\n        var b = 2 * (p1 - p0);\n        var c = p0 - val;\n        var n = 0;\n        if (isAroundZero(a)) {\n            if (isNotAroundZero(b)) {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = b * b - 4 * a * c;\n            if (isAroundZero(disc)) {\n                var t1 = -b / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var t1 = (-b + discSqrt) / (2 * a);\n                var t2 = (-b - discSqrt) / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n            }\n        }\n        return n;\n    }\n    function quadraticExtremum(p0, p1, p2) {\n        var divider = p0 + p2 - 2 * p1;\n        if (divider === 0) {\n            return 0.5;\n        }\n        else {\n            return (p0 - p1) / divider;\n        }\n    }\n    function quadraticSubdivide(p0, p1, p2, t, out) {\n        var p01 = (p1 - p0) * t + p0;\n        var p12 = (p2 - p1) * t + p1;\n        var p012 = (p12 - p01) * t + p01;\n        out[0] = p0;\n        out[1] = p01;\n        out[2] = p012;\n        out[3] = p012;\n        out[4] = p12;\n        out[5] = p2;\n    }\n    function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) {\n        var t;\n        var interval = 0.005;\n        var d = Infinity;\n        _v0[0] = x;\n        _v0[1] = y;\n        for (var _t = 0; _t < 1; _t += 0.05) {\n            _v1[0] = quadraticAt(x0, x1, x2, _t);\n            _v1[1] = quadraticAt(y0, y1, y2, _t);\n            var d1 = distSquare(_v0, _v1);\n            if (d1 < d) {\n                t = _t;\n                d = d1;\n            }\n        }\n        d = Infinity;\n        for (var i = 0; i < 32; i++) {\n            if (interval < EPSILON_NUMERIC) {\n                break;\n            }\n            var prev = t - interval;\n            var next = t + interval;\n            _v1[0] = quadraticAt(x0, x1, x2, prev);\n            _v1[1] = quadraticAt(y0, y1, y2, prev);\n            var d1 = distSquare(_v1, _v0);\n            if (prev >= 0 && d1 < d) {\n                t = prev;\n                d = d1;\n            }\n            else {\n                _v2[0] = quadraticAt(x0, x1, x2, next);\n                _v2[1] = quadraticAt(y0, y1, y2, next);\n                var d2 = distSquare(_v2, _v0);\n                if (next <= 1 && d2 < d) {\n                    t = next;\n                    d = d2;\n                }\n                else {\n                    interval *= 0.5;\n                }\n            }\n        }\n        if (out) {\n            out[0] = quadraticAt(x0, x1, x2, t);\n            out[1] = quadraticAt(y0, y1, y2, t);\n        }\n        return mathSqrt(d);\n    }\n    function quadraticLength(x0, y0, x1, y1, x2, y2, iteration) {\n        var px = x0;\n        var py = y0;\n        var d = 0;\n        var step = 1 / iteration;\n        for (var i = 1; i <= iteration; i++) {\n            var t = i * step;\n            var x = quadraticAt(x0, x1, x2, t);\n            var y = quadraticAt(y0, y1, y2, t);\n            var dx = x - px;\n            var dy = y - py;\n            d += Math.sqrt(dx * dx + dy * dy);\n            px = x;\n            py = y;\n        }\n        return d;\n    }\n\n    var regexp = /cubic-bezier\\(([0-9,\\.e ]+)\\)/;\n    function createCubicEasingFunc(cubicEasingStr) {\n        var cubic = cubicEasingStr && regexp.exec(cubicEasingStr);\n        if (cubic) {\n            var points = cubic[1].split(',');\n            var a_1 = +trim(points[0]);\n            var b_1 = +trim(points[1]);\n            var c_1 = +trim(points[2]);\n            var d_1 = +trim(points[3]);\n            if (isNaN(a_1 + b_1 + c_1 + d_1)) {\n                return;\n            }\n            var roots_1 = [];\n            return function (p) {\n                return p <= 0\n                    ? 0 : p >= 1\n                    ? 1\n                    : cubicRootAt(0, a_1, c_1, 1, p, roots_1) && cubicAt(0, b_1, d_1, 1, roots_1[0]);\n            };\n        }\n    }\n\n    var Clip = (function () {\n        function Clip(opts) {\n            this._inited = false;\n            this._startTime = 0;\n            this._pausedTime = 0;\n            this._paused = false;\n            this._life = opts.life || 1000;\n            this._delay = opts.delay || 0;\n            this.loop = opts.loop || false;\n            this.onframe = opts.onframe || noop;\n            this.ondestroy = opts.ondestroy || noop;\n            this.onrestart = opts.onrestart || noop;\n            opts.easing && this.setEasing(opts.easing);\n        }\n        Clip.prototype.step = function (globalTime, deltaTime) {\n            if (!this._inited) {\n                this._startTime = globalTime + this._delay;\n                this._inited = true;\n            }\n            if (this._paused) {\n                this._pausedTime += deltaTime;\n                return;\n            }\n            var life = this._life;\n            var elapsedTime = globalTime - this._startTime - this._pausedTime;\n            var percent = elapsedTime / life;\n            if (percent < 0) {\n                percent = 0;\n            }\n            percent = Math.min(percent, 1);\n            var easingFunc = this.easingFunc;\n            var schedule = easingFunc ? easingFunc(percent) : percent;\n            this.onframe(schedule);\n            if (percent === 1) {\n                if (this.loop) {\n                    var remainder = elapsedTime % life;\n                    this._startTime = globalTime - remainder;\n                    this._pausedTime = 0;\n                    this.onrestart();\n                }\n                else {\n                    return true;\n                }\n            }\n            return false;\n        };\n        Clip.prototype.pause = function () {\n            this._paused = true;\n        };\n        Clip.prototype.resume = function () {\n            this._paused = false;\n        };\n        Clip.prototype.setEasing = function (easing) {\n            this.easing = easing;\n            this.easingFunc = isFunction(easing)\n                ? easing\n                : easingFuncs[easing] || createCubicEasingFunc(easing);\n        };\n        return Clip;\n    }());\n\n    var Entry = (function () {\n        function Entry(val) {\n            this.value = val;\n        }\n        return Entry;\n    }());\n    var LinkedList = (function () {\n        function LinkedList() {\n            this._len = 0;\n        }\n        LinkedList.prototype.insert = function (val) {\n            var entry = new Entry(val);\n            this.insertEntry(entry);\n            return entry;\n        };\n        LinkedList.prototype.insertEntry = function (entry) {\n            if (!this.head) {\n                this.head = this.tail = entry;\n            }\n            else {\n                this.tail.next = entry;\n                entry.prev = this.tail;\n                entry.next = null;\n                this.tail = entry;\n            }\n            this._len++;\n        };\n        LinkedList.prototype.remove = function (entry) {\n            var prev = entry.prev;\n            var next = entry.next;\n            if (prev) {\n                prev.next = next;\n            }\n            else {\n                this.head = next;\n            }\n            if (next) {\n                next.prev = prev;\n            }\n            else {\n                this.tail = prev;\n            }\n            entry.next = entry.prev = null;\n            this._len--;\n        };\n        LinkedList.prototype.len = function () {\n            return this._len;\n        };\n        LinkedList.prototype.clear = function () {\n            this.head = this.tail = null;\n            this._len = 0;\n        };\n        return LinkedList;\n    }());\n    var LRU = (function () {\n        function LRU(maxSize) {\n            this._list = new LinkedList();\n            this._maxSize = 10;\n            this._map = {};\n            this._maxSize = maxSize;\n        }\n        LRU.prototype.put = function (key, value) {\n            var list = this._list;\n            var map = this._map;\n            var removed = null;\n            if (map[key] == null) {\n                var len = list.len();\n                var entry = this._lastRemovedEntry;\n                if (len >= this._maxSize && len > 0) {\n                    var leastUsedEntry = list.head;\n                    list.remove(leastUsedEntry);\n                    delete map[leastUsedEntry.key];\n                    removed = leastUsedEntry.value;\n                    this._lastRemovedEntry = leastUsedEntry;\n                }\n                if (entry) {\n                    entry.value = value;\n                }\n                else {\n                    entry = new Entry(value);\n                }\n                entry.key = key;\n                list.insertEntry(entry);\n                map[key] = entry;\n            }\n            return removed;\n        };\n        LRU.prototype.get = function (key) {\n            var entry = this._map[key];\n            var list = this._list;\n            if (entry != null) {\n                if (entry !== list.tail) {\n                    list.remove(entry);\n                    list.insertEntry(entry);\n                }\n                return entry.value;\n            }\n        };\n        LRU.prototype.clear = function () {\n            this._list.clear();\n            this._map = {};\n        };\n        LRU.prototype.len = function () {\n            return this._list.len();\n        };\n        return LRU;\n    }());\n\n    var kCSSColorTable = {\n        'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1],\n        'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1],\n        'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1],\n        'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1],\n        'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1],\n        'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1],\n        'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1],\n        'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1],\n        'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1],\n        'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1],\n        'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1],\n        'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1],\n        'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1],\n        'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1],\n        'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1],\n        'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1],\n        'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1],\n        'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1],\n        'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1],\n        'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1],\n        'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1],\n        'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1],\n        'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1],\n        'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1],\n        'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1],\n        'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1],\n        'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1],\n        'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1],\n        'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1],\n        'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1],\n        'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1],\n        'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1],\n        'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1],\n        'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1],\n        'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1],\n        'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1],\n        'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1],\n        'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1],\n        'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1],\n        'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1],\n        'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1],\n        'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1],\n        'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1],\n        'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1],\n        'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1],\n        'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1],\n        'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1],\n        'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1],\n        'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1],\n        'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1],\n        'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1],\n        'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1],\n        'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1],\n        'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1],\n        'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1],\n        'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1],\n        'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1],\n        'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1],\n        'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1],\n        'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1],\n        'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1],\n        'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1],\n        'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1],\n        'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1],\n        'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1],\n        'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1],\n        'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1],\n        'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1],\n        'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1],\n        'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1],\n        'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1],\n        'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1],\n        'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1],\n        'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1]\n    };\n    function clampCssByte(i) {\n        i = Math.round(i);\n        return i < 0 ? 0 : i > 255 ? 255 : i;\n    }\n    function clampCssAngle(i) {\n        i = Math.round(i);\n        return i < 0 ? 0 : i > 360 ? 360 : i;\n    }\n    function clampCssFloat(f) {\n        return f < 0 ? 0 : f > 1 ? 1 : f;\n    }\n    function parseCssInt(val) {\n        var str = val;\n        if (str.length && str.charAt(str.length - 1) === '%') {\n            return clampCssByte(parseFloat(str) / 100 * 255);\n        }\n        return clampCssByte(parseInt(str, 10));\n    }\n    function parseCssFloat(val) {\n        var str = val;\n        if (str.length && str.charAt(str.length - 1) === '%') {\n            return clampCssFloat(parseFloat(str) / 100);\n        }\n        return clampCssFloat(parseFloat(str));\n    }\n    function cssHueToRgb(m1, m2, h) {\n        if (h < 0) {\n            h += 1;\n        }\n        else if (h > 1) {\n            h -= 1;\n        }\n        if (h * 6 < 1) {\n            return m1 + (m2 - m1) * h * 6;\n        }\n        if (h * 2 < 1) {\n            return m2;\n        }\n        if (h * 3 < 2) {\n            return m1 + (m2 - m1) * (2 / 3 - h) * 6;\n        }\n        return m1;\n    }\n    function lerpNumber(a, b, p) {\n        return a + (b - a) * p;\n    }\n    function setRgba(out, r, g, b, a) {\n        out[0] = r;\n        out[1] = g;\n        out[2] = b;\n        out[3] = a;\n        return out;\n    }\n    function copyRgba(out, a) {\n        out[0] = a[0];\n        out[1] = a[1];\n        out[2] = a[2];\n        out[3] = a[3];\n        return out;\n    }\n    var colorCache = new LRU(20);\n    var lastRemovedArr = null;\n    function putToCache(colorStr, rgbaArr) {\n        if (lastRemovedArr) {\n            copyRgba(lastRemovedArr, rgbaArr);\n        }\n        lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));\n    }\n    function parse(colorStr, rgbaArr) {\n        if (!colorStr) {\n            return;\n        }\n        rgbaArr = rgbaArr || [];\n        var cached = colorCache.get(colorStr);\n        if (cached) {\n            return copyRgba(rgbaArr, cached);\n        }\n        colorStr = colorStr + '';\n        var str = colorStr.replace(/ /g, '').toLowerCase();\n        if (str in kCSSColorTable) {\n            copyRgba(rgbaArr, kCSSColorTable[str]);\n            putToCache(colorStr, rgbaArr);\n            return rgbaArr;\n        }\n        var strLen = str.length;\n        if (str.charAt(0) === '#') {\n            if (strLen === 4 || strLen === 5) {\n                var iv = parseInt(str.slice(1, 4), 16);\n                if (!(iv >= 0 && iv <= 0xfff)) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                setRgba(rgbaArr, ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), (iv & 0xf0) | ((iv & 0xf0) >> 4), (iv & 0xf) | ((iv & 0xf) << 4), strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            }\n            else if (strLen === 7 || strLen === 9) {\n                var iv = parseInt(str.slice(1, 7), 16);\n                if (!(iv >= 0 && iv <= 0xffffff)) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            }\n            return;\n        }\n        var op = str.indexOf('(');\n        var ep = str.indexOf(')');\n        if (op !== -1 && ep + 1 === strLen) {\n            var fname = str.substr(0, op);\n            var params = str.substr(op + 1, ep - (op + 1)).split(',');\n            var alpha = 1;\n            switch (fname) {\n                case 'rgba':\n                    if (params.length !== 4) {\n                        return params.length === 3\n                            ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1)\n                            : setRgba(rgbaArr, 0, 0, 0, 1);\n                    }\n                    alpha = parseCssFloat(params.pop());\n                case 'rgb':\n                    if (params.length >= 3) {\n                        setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), params.length === 3 ? alpha : parseCssFloat(params[3]));\n                        putToCache(colorStr, rgbaArr);\n                        return rgbaArr;\n                    }\n                    else {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                case 'hsla':\n                    if (params.length !== 4) {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                    params[3] = parseCssFloat(params[3]);\n                    hsla2rgba(params, rgbaArr);\n                    putToCache(colorStr, rgbaArr);\n                    return rgbaArr;\n                case 'hsl':\n                    if (params.length !== 3) {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                    hsla2rgba(params, rgbaArr);\n                    putToCache(colorStr, rgbaArr);\n                    return rgbaArr;\n                default:\n                    return;\n            }\n        }\n        setRgba(rgbaArr, 0, 0, 0, 1);\n        return;\n    }\n    function hsla2rgba(hsla, rgba) {\n        var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360;\n        var s = parseCssFloat(hsla[1]);\n        var l = parseCssFloat(hsla[2]);\n        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n        var m1 = l * 2 - m2;\n        rgba = rgba || [];\n        setRgba(rgba, clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), clampCssByte(cssHueToRgb(m1, m2, h) * 255), clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 1);\n        if (hsla.length === 4) {\n            rgba[3] = hsla[3];\n        }\n        return rgba;\n    }\n    function rgba2hsla(rgba) {\n        if (!rgba) {\n            return;\n        }\n        var R = rgba[0] / 255;\n        var G = rgba[1] / 255;\n        var B = rgba[2] / 255;\n        var vMin = Math.min(R, G, B);\n        var vMax = Math.max(R, G, B);\n        var delta = vMax - vMin;\n        var L = (vMax + vMin) / 2;\n        var H;\n        var S;\n        if (delta === 0) {\n            H = 0;\n            S = 0;\n        }\n        else {\n            if (L < 0.5) {\n                S = delta / (vMax + vMin);\n            }\n            else {\n                S = delta / (2 - vMax - vMin);\n            }\n            var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;\n            var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;\n            var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;\n            if (R === vMax) {\n                H = deltaB - deltaG;\n            }\n            else if (G === vMax) {\n                H = (1 / 3) + deltaR - deltaB;\n            }\n            else if (B === vMax) {\n                H = (2 / 3) + deltaG - deltaR;\n            }\n            if (H < 0) {\n                H += 1;\n            }\n            if (H > 1) {\n                H -= 1;\n            }\n        }\n        var hsla = [H * 360, S, L];\n        if (rgba[3] != null) {\n            hsla.push(rgba[3]);\n        }\n        return hsla;\n    }\n    function lift(color, level) {\n        var colorArr = parse(color);\n        if (colorArr) {\n            for (var i = 0; i < 3; i++) {\n                if (level < 0) {\n                    colorArr[i] = colorArr[i] * (1 - level) | 0;\n                }\n                else {\n                    colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;\n                }\n                if (colorArr[i] > 255) {\n                    colorArr[i] = 255;\n                }\n                else if (colorArr[i] < 0) {\n                    colorArr[i] = 0;\n                }\n            }\n            return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');\n        }\n    }\n    function toHex(color) {\n        var colorArr = parse(color);\n        if (colorArr) {\n            return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);\n        }\n    }\n    function fastLerp(normalizedValue, colors, out) {\n        if (!(colors && colors.length)\n            || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n            return;\n        }\n        out = out || [];\n        var value = normalizedValue * (colors.length - 1);\n        var leftIndex = Math.floor(value);\n        var rightIndex = Math.ceil(value);\n        var leftColor = colors[leftIndex];\n        var rightColor = colors[rightIndex];\n        var dv = value - leftIndex;\n        out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));\n        out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));\n        out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));\n        out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));\n        return out;\n    }\n    var fastMapToColor = fastLerp;\n    function lerp$1(normalizedValue, colors, fullOutput) {\n        if (!(colors && colors.length)\n            || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n            return;\n        }\n        var value = normalizedValue * (colors.length - 1);\n        var leftIndex = Math.floor(value);\n        var rightIndex = Math.ceil(value);\n        var leftColor = parse(colors[leftIndex]);\n        var rightColor = parse(colors[rightIndex]);\n        var dv = value - leftIndex;\n        var color = stringify([\n            clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),\n            clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),\n            clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),\n            clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))\n        ], 'rgba');\n        return fullOutput\n            ? {\n                color: color,\n                leftIndex: leftIndex,\n                rightIndex: rightIndex,\n                value: value\n            }\n            : color;\n    }\n    var mapToColor = lerp$1;\n    function modifyHSL(color, h, s, l) {\n        var colorArr = parse(color);\n        if (color) {\n            colorArr = rgba2hsla(colorArr);\n            h != null && (colorArr[0] = clampCssAngle(h));\n            s != null && (colorArr[1] = parseCssFloat(s));\n            l != null && (colorArr[2] = parseCssFloat(l));\n            return stringify(hsla2rgba(colorArr), 'rgba');\n        }\n    }\n    function modifyAlpha(color, alpha) {\n        var colorArr = parse(color);\n        if (colorArr && alpha != null) {\n            colorArr[3] = clampCssFloat(alpha);\n            return stringify(colorArr, 'rgba');\n        }\n    }\n    function stringify(arrColor, type) {\n        if (!arrColor || !arrColor.length) {\n            return;\n        }\n        var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];\n        if (type === 'rgba' || type === 'hsva' || type === 'hsla') {\n            colorStr += ',' + arrColor[3];\n        }\n        return type + '(' + colorStr + ')';\n    }\n    function lum(color, backgroundLum) {\n        var arr = parse(color);\n        return arr\n            ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255\n                + (1 - arr[3]) * backgroundLum\n            : 0;\n    }\n    function random() {\n        return stringify([\n            Math.round(Math.random() * 255),\n            Math.round(Math.random() * 255),\n            Math.round(Math.random() * 255)\n        ], 'rgb');\n    }\n\n    var color = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        parse: parse,\n        lift: lift,\n        toHex: toHex,\n        fastLerp: fastLerp,\n        fastMapToColor: fastMapToColor,\n        lerp: lerp$1,\n        mapToColor: mapToColor,\n        modifyHSL: modifyHSL,\n        modifyAlpha: modifyAlpha,\n        stringify: stringify,\n        lum: lum,\n        random: random\n    });\n\n    var mathRound = Math.round;\n    function normalizeColor(color) {\n        var opacity;\n        if (!color || color === 'transparent') {\n            color = 'none';\n        }\n        else if (typeof color === 'string' && color.indexOf('rgba') > -1) {\n            var arr = parse(color);\n            if (arr) {\n                color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')';\n                opacity = arr[3];\n            }\n        }\n        return {\n            color: color,\n            opacity: opacity == null ? 1 : opacity\n        };\n    }\n    var EPSILON$1 = 1e-4;\n    function isAroundZero$1(transform) {\n        return transform < EPSILON$1 && transform > -EPSILON$1;\n    }\n    function round3(transform) {\n        return mathRound(transform * 1e3) / 1e3;\n    }\n    function round4(transform) {\n        return mathRound(transform * 1e4) / 1e4;\n    }\n    function getMatrixStr(m) {\n        return 'matrix('\n            + round3(m[0]) + ','\n            + round3(m[1]) + ','\n            + round3(m[2]) + ','\n            + round3(m[3]) + ','\n            + round4(m[4]) + ','\n            + round4(m[5])\n            + ')';\n    }\n    var TEXT_ALIGN_TO_ANCHOR = {\n        left: 'start',\n        right: 'end',\n        center: 'middle',\n        middle: 'middle'\n    };\n    function adjustTextY(y, lineHeight, textBaseline) {\n        if (textBaseline === 'top') {\n            y += lineHeight / 2;\n        }\n        else if (textBaseline === 'bottom') {\n            y -= lineHeight / 2;\n        }\n        return y;\n    }\n    function hasShadow(style) {\n        return style\n            && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY);\n    }\n    function getShadowKey(displayable) {\n        var style = displayable.style;\n        var globalScale = displayable.getGlobalScale();\n        return [\n            style.shadowColor,\n            (style.shadowBlur || 0).toFixed(2),\n            (style.shadowOffsetX || 0).toFixed(2),\n            (style.shadowOffsetY || 0).toFixed(2),\n            globalScale[0],\n            globalScale[1]\n        ].join(',');\n    }\n    function isImagePattern(val) {\n        return val && (!!val.image);\n    }\n    function isSVGPattern(val) {\n        return val && (!!val.svgElement);\n    }\n    function isPattern(val) {\n        return isImagePattern(val) || isSVGPattern(val);\n    }\n    function isLinearGradient(val) {\n        return val.type === 'linear';\n    }\n    function isRadialGradient(val) {\n        return val.type === 'radial';\n    }\n    function isGradient(val) {\n        return val && (val.type === 'linear'\n            || val.type === 'radial');\n    }\n    function getIdURL(id) {\n        return \"url(#\" + id + \")\";\n    }\n    function getPathPrecision(el) {\n        var scale = el.getGlobalScale();\n        var size = Math.max(scale[0], scale[1]);\n        return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1);\n    }\n    function getSRTTransformString(transform) {\n        var x = transform.x || 0;\n        var y = transform.y || 0;\n        var rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE;\n        var scaleX = retrieve2(transform.scaleX, 1);\n        var scaleY = retrieve2(transform.scaleY, 1);\n        var skewX = transform.skewX || 0;\n        var skewY = transform.skewY || 0;\n        var res = [];\n        if (x || y) {\n            res.push(\"translate(\" + x + \"px,\" + y + \"px)\");\n        }\n        if (rotation) {\n            res.push(\"rotate(\" + rotation + \")\");\n        }\n        if (scaleX !== 1 || scaleY !== 1) {\n            res.push(\"scale(\" + scaleX + \",\" + scaleY + \")\");\n        }\n        if (skewX || skewY) {\n            res.push(\"skew(\" + mathRound(skewX * RADIAN_TO_DEGREE) + \"deg, \" + mathRound(skewY * RADIAN_TO_DEGREE) + \"deg)\");\n        }\n        return res.join(' ');\n    }\n    var encodeBase64 = (function () {\n        if (env.hasGlobalWindow && isFunction(window.btoa)) {\n            return function (str) {\n                return window.btoa(unescape(encodeURIComponent(str)));\n            };\n        }\n        if (typeof Buffer !== 'undefined') {\n            return function (str) {\n                return Buffer.from(str).toString('base64');\n            };\n        }\n        return function (str) {\n            if (\"development\" !== 'production') {\n                logError('Base64 isn\\'t natively supported in the current environment.');\n            }\n            return null;\n        };\n    })();\n\n    var arraySlice = Array.prototype.slice;\n    function interpolateNumber(p0, p1, percent) {\n        return (p1 - p0) * percent + p0;\n    }\n    function interpolate1DArray(out, p0, p1, percent) {\n        var len = p0.length;\n        for (var i = 0; i < len; i++) {\n            out[i] = interpolateNumber(p0[i], p1[i], percent);\n        }\n        return out;\n    }\n    function interpolate2DArray(out, p0, p1, percent) {\n        var len = p0.length;\n        var len2 = len && p0[0].length;\n        for (var i = 0; i < len; i++) {\n            if (!out[i]) {\n                out[i] = [];\n            }\n            for (var j = 0; j < len2; j++) {\n                out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent);\n            }\n        }\n        return out;\n    }\n    function add1DArray(out, p0, p1, sign) {\n        var len = p0.length;\n        for (var i = 0; i < len; i++) {\n            out[i] = p0[i] + p1[i] * sign;\n        }\n        return out;\n    }\n    function add2DArray(out, p0, p1, sign) {\n        var len = p0.length;\n        var len2 = len && p0[0].length;\n        for (var i = 0; i < len; i++) {\n            if (!out[i]) {\n                out[i] = [];\n            }\n            for (var j = 0; j < len2; j++) {\n                out[i][j] = p0[i][j] + p1[i][j] * sign;\n            }\n        }\n        return out;\n    }\n    function fillColorStops(val0, val1) {\n        var len0 = val0.length;\n        var len1 = val1.length;\n        var shorterArr = len0 > len1 ? val1 : val0;\n        var shorterLen = Math.min(len0, len1);\n        var last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 };\n        for (var i = shorterLen; i < Math.max(len0, len1); i++) {\n            shorterArr.push({\n                offset: last.offset,\n                color: last.color.slice()\n            });\n        }\n    }\n    function fillArray(val0, val1, arrDim) {\n        var arr0 = val0;\n        var arr1 = val1;\n        if (!arr0.push || !arr1.push) {\n            return;\n        }\n        var arr0Len = arr0.length;\n        var arr1Len = arr1.length;\n        if (arr0Len !== arr1Len) {\n            var isPreviousLarger = arr0Len > arr1Len;\n            if (isPreviousLarger) {\n                arr0.length = arr1Len;\n            }\n            else {\n                for (var i = arr0Len; i < arr1Len; i++) {\n                    arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]));\n                }\n            }\n        }\n        var len2 = arr0[0] && arr0[0].length;\n        for (var i = 0; i < arr0.length; i++) {\n            if (arrDim === 1) {\n                if (isNaN(arr0[i])) {\n                    arr0[i] = arr1[i];\n                }\n            }\n            else {\n                for (var j = 0; j < len2; j++) {\n                    if (isNaN(arr0[i][j])) {\n                        arr0[i][j] = arr1[i][j];\n                    }\n                }\n            }\n        }\n    }\n    function cloneValue(value) {\n        if (isArrayLike(value)) {\n            var len = value.length;\n            if (isArrayLike(value[0])) {\n                var ret = [];\n                for (var i = 0; i < len; i++) {\n                    ret.push(arraySlice.call(value[i]));\n                }\n                return ret;\n            }\n            return arraySlice.call(value);\n        }\n        return value;\n    }\n    function rgba2String(rgba) {\n        rgba[0] = Math.floor(rgba[0]) || 0;\n        rgba[1] = Math.floor(rgba[1]) || 0;\n        rgba[2] = Math.floor(rgba[2]) || 0;\n        rgba[3] = rgba[3] == null ? 1 : rgba[3];\n        return 'rgba(' + rgba.join(',') + ')';\n    }\n    function guessArrayDim(value) {\n        return isArrayLike(value && value[0]) ? 2 : 1;\n    }\n    var VALUE_TYPE_NUMBER = 0;\n    var VALUE_TYPE_1D_ARRAY = 1;\n    var VALUE_TYPE_2D_ARRAY = 2;\n    var VALUE_TYPE_COLOR = 3;\n    var VALUE_TYPE_LINEAR_GRADIENT = 4;\n    var VALUE_TYPE_RADIAL_GRADIENT = 5;\n    var VALUE_TYPE_UNKOWN = 6;\n    function isGradientValueType(valType) {\n        return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT;\n    }\n    function isArrayValueType(valType) {\n        return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY;\n    }\n    var tmpRgba = [0, 0, 0, 0];\n    var Track = (function () {\n        function Track(propName) {\n            this.keyframes = [];\n            this.discrete = false;\n            this._invalid = false;\n            this._needsSort = false;\n            this._lastFr = 0;\n            this._lastFrP = 0;\n            this.propName = propName;\n        }\n        Track.prototype.isFinished = function () {\n            return this._finished;\n        };\n        Track.prototype.setFinished = function () {\n            this._finished = true;\n            if (this._additiveTrack) {\n                this._additiveTrack.setFinished();\n            }\n        };\n        Track.prototype.needsAnimate = function () {\n            return this.keyframes.length >= 1;\n        };\n        Track.prototype.getAdditiveTrack = function () {\n            return this._additiveTrack;\n        };\n        Track.prototype.addKeyframe = function (time, rawValue, easing) {\n            this._needsSort = true;\n            var keyframes = this.keyframes;\n            var len = keyframes.length;\n            var discrete = false;\n            var valType = VALUE_TYPE_UNKOWN;\n            var value = rawValue;\n            if (isArrayLike(rawValue)) {\n                var arrayDim = guessArrayDim(rawValue);\n                valType = arrayDim;\n                if (arrayDim === 1 && !isNumber(rawValue[0])\n                    || arrayDim === 2 && !isNumber(rawValue[0][0])) {\n                    discrete = true;\n                }\n            }\n            else {\n                if (isNumber(rawValue) && !eqNaN(rawValue)) {\n                    valType = VALUE_TYPE_NUMBER;\n                }\n                else if (isString(rawValue)) {\n                    if (!isNaN(+rawValue)) {\n                        valType = VALUE_TYPE_NUMBER;\n                    }\n                    else {\n                        var colorArray = parse(rawValue);\n                        if (colorArray) {\n                            value = colorArray;\n                            valType = VALUE_TYPE_COLOR;\n                        }\n                    }\n                }\n                else if (isGradientObject(rawValue)) {\n                    var parsedGradient = extend({}, value);\n                    parsedGradient.colorStops = map(rawValue.colorStops, function (colorStop) { return ({\n                        offset: colorStop.offset,\n                        color: parse(colorStop.color)\n                    }); });\n                    if (isLinearGradient(rawValue)) {\n                        valType = VALUE_TYPE_LINEAR_GRADIENT;\n                    }\n                    else if (isRadialGradient(rawValue)) {\n                        valType = VALUE_TYPE_RADIAL_GRADIENT;\n                    }\n                    value = parsedGradient;\n                }\n            }\n            if (len === 0) {\n                this.valType = valType;\n            }\n            else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) {\n                discrete = true;\n            }\n            this.discrete = this.discrete || discrete;\n            var kf = {\n                time: time,\n                value: value,\n                rawValue: rawValue,\n                percent: 0\n            };\n            if (easing) {\n                kf.easing = easing;\n                kf.easingFunc = isFunction(easing)\n                    ? easing\n                    : easingFuncs[easing] || createCubicEasingFunc(easing);\n            }\n            keyframes.push(kf);\n            return kf;\n        };\n        Track.prototype.prepare = function (maxTime, additiveTrack) {\n            var kfs = this.keyframes;\n            if (this._needsSort) {\n                kfs.sort(function (a, b) {\n                    return a.time - b.time;\n                });\n            }\n            var valType = this.valType;\n            var kfsLen = kfs.length;\n            var lastKf = kfs[kfsLen - 1];\n            var isDiscrete = this.discrete;\n            var isArr = isArrayValueType(valType);\n            var isGradient = isGradientValueType(valType);\n            for (var i = 0; i < kfsLen; i++) {\n                var kf = kfs[i];\n                var value = kf.value;\n                var lastValue = lastKf.value;\n                kf.percent = kf.time / maxTime;\n                if (!isDiscrete) {\n                    if (isArr && i !== kfsLen - 1) {\n                        fillArray(value, lastValue, valType);\n                    }\n                    else if (isGradient) {\n                        fillColorStops(value.colorStops, lastValue.colorStops);\n                    }\n                }\n            }\n            if (!isDiscrete\n                && valType !== VALUE_TYPE_RADIAL_GRADIENT\n                && additiveTrack\n                && this.needsAnimate()\n                && additiveTrack.needsAnimate()\n                && valType === additiveTrack.valType\n                && !additiveTrack._finished) {\n                this._additiveTrack = additiveTrack;\n                var startValue = kfs[0].value;\n                for (var i = 0; i < kfsLen; i++) {\n                    if (valType === VALUE_TYPE_NUMBER) {\n                        kfs[i].additiveValue = kfs[i].value - startValue;\n                    }\n                    else if (valType === VALUE_TYPE_COLOR) {\n                        kfs[i].additiveValue =\n                            add1DArray([], kfs[i].value, startValue, -1);\n                    }\n                    else if (isArrayValueType(valType)) {\n                        kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY\n                            ? add1DArray([], kfs[i].value, startValue, -1)\n                            : add2DArray([], kfs[i].value, startValue, -1);\n                    }\n                }\n            }\n        };\n        Track.prototype.step = function (target, percent) {\n            if (this._finished) {\n                return;\n            }\n            if (this._additiveTrack && this._additiveTrack._finished) {\n                this._additiveTrack = null;\n            }\n            var isAdditive = this._additiveTrack != null;\n            var valueKey = isAdditive ? 'additiveValue' : 'value';\n            var valType = this.valType;\n            var keyframes = this.keyframes;\n            var kfsNum = keyframes.length;\n            var propName = this.propName;\n            var isValueColor = valType === VALUE_TYPE_COLOR;\n            var frameIdx;\n            var lastFrame = this._lastFr;\n            var mathMin = Math.min;\n            var frame;\n            var nextFrame;\n            if (kfsNum === 1) {\n                frame = nextFrame = keyframes[0];\n            }\n            else {\n                if (percent < 0) {\n                    frameIdx = 0;\n                }\n                else if (percent < this._lastFrP) {\n                    var start = mathMin(lastFrame + 1, kfsNum - 1);\n                    for (frameIdx = start; frameIdx >= 0; frameIdx--) {\n                        if (keyframes[frameIdx].percent <= percent) {\n                            break;\n                        }\n                    }\n                    frameIdx = mathMin(frameIdx, kfsNum - 2);\n                }\n                else {\n                    for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) {\n                        if (keyframes[frameIdx].percent > percent) {\n                            break;\n                        }\n                    }\n                    frameIdx = mathMin(frameIdx - 1, kfsNum - 2);\n                }\n                nextFrame = keyframes[frameIdx + 1];\n                frame = keyframes[frameIdx];\n            }\n            if (!(frame && nextFrame)) {\n                return;\n            }\n            this._lastFr = frameIdx;\n            this._lastFrP = percent;\n            var interval = (nextFrame.percent - frame.percent);\n            var w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1);\n            if (nextFrame.easingFunc) {\n                w = nextFrame.easingFunc(w);\n            }\n            var targetArr = isAdditive ? this._additiveValue\n                : (isValueColor ? tmpRgba : target[propName]);\n            if ((isArrayValueType(valType) || isValueColor) && !targetArr) {\n                targetArr = this._additiveValue = [];\n            }\n            if (this.discrete) {\n                target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue;\n            }\n            else if (isArrayValueType(valType)) {\n                valType === VALUE_TYPE_1D_ARRAY\n                    ? interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w)\n                    : interpolate2DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n            }\n            else if (isGradientValueType(valType)) {\n                var val = frame[valueKey];\n                var nextVal_1 = nextFrame[valueKey];\n                var isLinearGradient_1 = valType === VALUE_TYPE_LINEAR_GRADIENT;\n                target[propName] = {\n                    type: isLinearGradient_1 ? 'linear' : 'radial',\n                    x: interpolateNumber(val.x, nextVal_1.x, w),\n                    y: interpolateNumber(val.y, nextVal_1.y, w),\n                    colorStops: map(val.colorStops, function (colorStop, idx) {\n                        var nextColorStop = nextVal_1.colorStops[idx];\n                        return {\n                            offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w),\n                            color: rgba2String(interpolate1DArray([], colorStop.color, nextColorStop.color, w))\n                        };\n                    }),\n                    global: nextVal_1.global\n                };\n                if (isLinearGradient_1) {\n                    target[propName].x2 = interpolateNumber(val.x2, nextVal_1.x2, w);\n                    target[propName].y2 = interpolateNumber(val.y2, nextVal_1.y2, w);\n                }\n                else {\n                    target[propName].r = interpolateNumber(val.r, nextVal_1.r, w);\n                }\n            }\n            else if (isValueColor) {\n                interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n                if (!isAdditive) {\n                    target[propName] = rgba2String(targetArr);\n                }\n            }\n            else {\n                var value = interpolateNumber(frame[valueKey], nextFrame[valueKey], w);\n                if (isAdditive) {\n                    this._additiveValue = value;\n                }\n                else {\n                    target[propName] = value;\n                }\n            }\n            if (isAdditive) {\n                this._addToTarget(target);\n            }\n        };\n        Track.prototype._addToTarget = function (target) {\n            var valType = this.valType;\n            var propName = this.propName;\n            var additiveValue = this._additiveValue;\n            if (valType === VALUE_TYPE_NUMBER) {\n                target[propName] = target[propName] + additiveValue;\n            }\n            else if (valType === VALUE_TYPE_COLOR) {\n                parse(target[propName], tmpRgba);\n                add1DArray(tmpRgba, tmpRgba, additiveValue, 1);\n                target[propName] = rgba2String(tmpRgba);\n            }\n            else if (valType === VALUE_TYPE_1D_ARRAY) {\n                add1DArray(target[propName], target[propName], additiveValue, 1);\n            }\n            else if (valType === VALUE_TYPE_2D_ARRAY) {\n                add2DArray(target[propName], target[propName], additiveValue, 1);\n            }\n        };\n        return Track;\n    }());\n    var Animator = (function () {\n        function Animator(target, loop, allowDiscreteAnimation, additiveTo) {\n            this._tracks = {};\n            this._trackKeys = [];\n            this._maxTime = 0;\n            this._started = 0;\n            this._clip = null;\n            this._target = target;\n            this._loop = loop;\n            if (loop && additiveTo) {\n                logError('Can\\' use additive animation on looped animation.');\n                return;\n            }\n            this._additiveAnimators = additiveTo;\n            this._allowDiscrete = allowDiscreteAnimation;\n        }\n        Animator.prototype.getMaxTime = function () {\n            return this._maxTime;\n        };\n        Animator.prototype.getDelay = function () {\n            return this._delay;\n        };\n        Animator.prototype.getLoop = function () {\n            return this._loop;\n        };\n        Animator.prototype.getTarget = function () {\n            return this._target;\n        };\n        Animator.prototype.changeTarget = function (target) {\n            this._target = target;\n        };\n        Animator.prototype.when = function (time, props, easing) {\n            return this.whenWithKeys(time, props, keys(props), easing);\n        };\n        Animator.prototype.whenWithKeys = function (time, props, propNames, easing) {\n            var tracks = this._tracks;\n            for (var i = 0; i < propNames.length; i++) {\n                var propName = propNames[i];\n                var track = tracks[propName];\n                if (!track) {\n                    track = tracks[propName] = new Track(propName);\n                    var initialValue = void 0;\n                    var additiveTrack = this._getAdditiveTrack(propName);\n                    if (additiveTrack) {\n                        var addtiveTrackKfs = additiveTrack.keyframes;\n                        var lastFinalKf = addtiveTrackKfs[addtiveTrackKfs.length - 1];\n                        initialValue = lastFinalKf && lastFinalKf.value;\n                        if (additiveTrack.valType === VALUE_TYPE_COLOR && initialValue) {\n                            initialValue = rgba2String(initialValue);\n                        }\n                    }\n                    else {\n                        initialValue = this._target[propName];\n                    }\n                    if (initialValue == null) {\n                        continue;\n                    }\n                    if (time > 0) {\n                        track.addKeyframe(0, cloneValue(initialValue), easing);\n                    }\n                    this._trackKeys.push(propName);\n                }\n                track.addKeyframe(time, cloneValue(props[propName]), easing);\n            }\n            this._maxTime = Math.max(this._maxTime, time);\n            return this;\n        };\n        Animator.prototype.pause = function () {\n            this._clip.pause();\n            this._paused = true;\n        };\n        Animator.prototype.resume = function () {\n            this._clip.resume();\n            this._paused = false;\n        };\n        Animator.prototype.isPaused = function () {\n            return !!this._paused;\n        };\n        Animator.prototype.duration = function (duration) {\n            this._maxTime = duration;\n            this._force = true;\n            return this;\n        };\n        Animator.prototype._doneCallback = function () {\n            this._setTracksFinished();\n            this._clip = null;\n            var doneList = this._doneCbs;\n            if (doneList) {\n                var len = doneList.length;\n                for (var i = 0; i < len; i++) {\n                    doneList[i].call(this);\n                }\n            }\n        };\n        Animator.prototype._abortedCallback = function () {\n            this._setTracksFinished();\n            var animation = this.animation;\n            var abortedList = this._abortedCbs;\n            if (animation) {\n                animation.removeClip(this._clip);\n            }\n            this._clip = null;\n            if (abortedList) {\n                for (var i = 0; i < abortedList.length; i++) {\n                    abortedList[i].call(this);\n                }\n            }\n        };\n        Animator.prototype._setTracksFinished = function () {\n            var tracks = this._tracks;\n            var tracksKeys = this._trackKeys;\n            for (var i = 0; i < tracksKeys.length; i++) {\n                tracks[tracksKeys[i]].setFinished();\n            }\n        };\n        Animator.prototype._getAdditiveTrack = function (trackName) {\n            var additiveTrack;\n            var additiveAnimators = this._additiveAnimators;\n            if (additiveAnimators) {\n                for (var i = 0; i < additiveAnimators.length; i++) {\n                    var track = additiveAnimators[i].getTrack(trackName);\n                    if (track) {\n                        additiveTrack = track;\n                    }\n                }\n            }\n            return additiveTrack;\n        };\n        Animator.prototype.start = function (easing) {\n            if (this._started > 0) {\n                return;\n            }\n            this._started = 1;\n            var self = this;\n            var tracks = [];\n            var maxTime = this._maxTime || 0;\n            for (var i = 0; i < this._trackKeys.length; i++) {\n                var propName = this._trackKeys[i];\n                var track = this._tracks[propName];\n                var additiveTrack = this._getAdditiveTrack(propName);\n                var kfs = track.keyframes;\n                var kfsNum = kfs.length;\n                track.prepare(maxTime, additiveTrack);\n                if (track.needsAnimate()) {\n                    if (!this._allowDiscrete && track.discrete) {\n                        var lastKf = kfs[kfsNum - 1];\n                        if (lastKf) {\n                            self._target[track.propName] = lastKf.rawValue;\n                        }\n                        track.setFinished();\n                    }\n                    else {\n                        tracks.push(track);\n                    }\n                }\n            }\n            if (tracks.length || this._force) {\n                var clip = new Clip({\n                    life: maxTime,\n                    loop: this._loop,\n                    delay: this._delay || 0,\n                    onframe: function (percent) {\n                        self._started = 2;\n                        var additiveAnimators = self._additiveAnimators;\n                        if (additiveAnimators) {\n                            var stillHasAdditiveAnimator = false;\n                            for (var i = 0; i < additiveAnimators.length; i++) {\n                                if (additiveAnimators[i]._clip) {\n                                    stillHasAdditiveAnimator = true;\n                                    break;\n                                }\n                            }\n                            if (!stillHasAdditiveAnimator) {\n                                self._additiveAnimators = null;\n                            }\n                        }\n                        for (var i = 0; i < tracks.length; i++) {\n                            tracks[i].step(self._target, percent);\n                        }\n                        var onframeList = self._onframeCbs;\n                        if (onframeList) {\n                            for (var i = 0; i < onframeList.length; i++) {\n                                onframeList[i](self._target, percent);\n                            }\n                        }\n                    },\n                    ondestroy: function () {\n                        self._doneCallback();\n                    }\n                });\n                this._clip = clip;\n                if (this.animation) {\n                    this.animation.addClip(clip);\n                }\n                if (easing) {\n                    clip.setEasing(easing);\n                }\n            }\n            else {\n                this._doneCallback();\n            }\n            return this;\n        };\n        Animator.prototype.stop = function (forwardToLast) {\n            if (!this._clip) {\n                return;\n            }\n            var clip = this._clip;\n            if (forwardToLast) {\n                clip.onframe(1);\n            }\n            this._abortedCallback();\n        };\n        Animator.prototype.delay = function (time) {\n            this._delay = time;\n            return this;\n        };\n        Animator.prototype.during = function (cb) {\n            if (cb) {\n                if (!this._onframeCbs) {\n                    this._onframeCbs = [];\n                }\n                this._onframeCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.done = function (cb) {\n            if (cb) {\n                if (!this._doneCbs) {\n                    this._doneCbs = [];\n                }\n                this._doneCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.aborted = function (cb) {\n            if (cb) {\n                if (!this._abortedCbs) {\n                    this._abortedCbs = [];\n                }\n                this._abortedCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.getClip = function () {\n            return this._clip;\n        };\n        Animator.prototype.getTrack = function (propName) {\n            return this._tracks[propName];\n        };\n        Animator.prototype.getTracks = function () {\n            var _this = this;\n            return map(this._trackKeys, function (key) { return _this._tracks[key]; });\n        };\n        Animator.prototype.stopTracks = function (propNames, forwardToLast) {\n            if (!propNames.length || !this._clip) {\n                return true;\n            }\n            var tracks = this._tracks;\n            var tracksKeys = this._trackKeys;\n            for (var i = 0; i < propNames.length; i++) {\n                var track = tracks[propNames[i]];\n                if (track && !track.isFinished()) {\n                    if (forwardToLast) {\n                        track.step(this._target, 1);\n                    }\n                    else if (this._started === 1) {\n                        track.step(this._target, 0);\n                    }\n                    track.setFinished();\n                }\n            }\n            var allAborted = true;\n            for (var i = 0; i < tracksKeys.length; i++) {\n                if (!tracks[tracksKeys[i]].isFinished()) {\n                    allAborted = false;\n                    break;\n                }\n            }\n            if (allAborted) {\n                this._abortedCallback();\n            }\n            return allAborted;\n        };\n        Animator.prototype.saveTo = function (target, trackKeys, firstOrLast) {\n            if (!target) {\n                return;\n            }\n            trackKeys = trackKeys || this._trackKeys;\n            for (var i = 0; i < trackKeys.length; i++) {\n                var propName = trackKeys[i];\n                var track = this._tracks[propName];\n                if (!track || track.isFinished()) {\n                    continue;\n                }\n                var kfs = track.keyframes;\n                var kf = kfs[firstOrLast ? 0 : kfs.length - 1];\n                if (kf) {\n                    target[propName] = cloneValue(kf.rawValue);\n                }\n            }\n        };\n        Animator.prototype.__changeFinalValue = function (finalProps, trackKeys) {\n            trackKeys = trackKeys || keys(finalProps);\n            for (var i = 0; i < trackKeys.length; i++) {\n                var propName = trackKeys[i];\n                var track = this._tracks[propName];\n                if (!track) {\n                    continue;\n                }\n                var kfs = track.keyframes;\n                if (kfs.length > 1) {\n                    var lastKf = kfs.pop();\n                    track.addKeyframe(lastKf.time, finalProps[propName]);\n                    track.prepare(this._maxTime, track.getAdditiveTrack());\n                }\n            }\n        };\n        return Animator;\n    }());\n\n    function getTime() {\n        return new Date().getTime();\n    }\n    var Animation = (function (_super) {\n        __extends(Animation, _super);\n        function Animation(opts) {\n            var _this = _super.call(this) || this;\n            _this._running = false;\n            _this._time = 0;\n            _this._pausedTime = 0;\n            _this._pauseStart = 0;\n            _this._paused = false;\n            opts = opts || {};\n            _this.stage = opts.stage || {};\n            return _this;\n        }\n        Animation.prototype.addClip = function (clip) {\n            if (clip.animation) {\n                this.removeClip(clip);\n            }\n            if (!this._head) {\n                this._head = this._tail = clip;\n            }\n            else {\n                this._tail.next = clip;\n                clip.prev = this._tail;\n                clip.next = null;\n                this._tail = clip;\n            }\n            clip.animation = this;\n        };\n        Animation.prototype.addAnimator = function (animator) {\n            animator.animation = this;\n            var clip = animator.getClip();\n            if (clip) {\n                this.addClip(clip);\n            }\n        };\n        Animation.prototype.removeClip = function (clip) {\n            if (!clip.animation) {\n                return;\n            }\n            var prev = clip.prev;\n            var next = clip.next;\n            if (prev) {\n                prev.next = next;\n            }\n            else {\n                this._head = next;\n            }\n            if (next) {\n                next.prev = prev;\n            }\n            else {\n                this._tail = prev;\n            }\n            clip.next = clip.prev = clip.animation = null;\n        };\n        Animation.prototype.removeAnimator = function (animator) {\n            var clip = animator.getClip();\n            if (clip) {\n                this.removeClip(clip);\n            }\n            animator.animation = null;\n        };\n        Animation.prototype.update = function (notTriggerFrameAndStageUpdate) {\n            var time = getTime() - this._pausedTime;\n            var delta = time - this._time;\n            var clip = this._head;\n            while (clip) {\n                var nextClip = clip.next;\n                var finished = clip.step(time, delta);\n                if (finished) {\n                    clip.ondestroy();\n                    this.removeClip(clip);\n                    clip = nextClip;\n                }\n                else {\n                    clip = nextClip;\n                }\n            }\n            this._time = time;\n            if (!notTriggerFrameAndStageUpdate) {\n                this.trigger('frame', delta);\n                this.stage.update && this.stage.update();\n            }\n        };\n        Animation.prototype._startLoop = function () {\n            var self = this;\n            this._running = true;\n            function step() {\n                if (self._running) {\n                    requestAnimationFrame$1(step);\n                    !self._paused && self.update();\n                }\n            }\n            requestAnimationFrame$1(step);\n        };\n        Animation.prototype.start = function () {\n            if (this._running) {\n                return;\n            }\n            this._time = getTime();\n            this._pausedTime = 0;\n            this._startLoop();\n        };\n        Animation.prototype.stop = function () {\n            this._running = false;\n        };\n        Animation.prototype.pause = function () {\n            if (!this._paused) {\n                this._pauseStart = getTime();\n                this._paused = true;\n            }\n        };\n        Animation.prototype.resume = function () {\n            if (this._paused) {\n                this._pausedTime += getTime() - this._pauseStart;\n                this._paused = false;\n            }\n        };\n        Animation.prototype.clear = function () {\n            var clip = this._head;\n            while (clip) {\n                var nextClip = clip.next;\n                clip.prev = clip.next = clip.animation = null;\n                clip = nextClip;\n            }\n            this._head = this._tail = null;\n        };\n        Animation.prototype.isFinished = function () {\n            return this._head == null;\n        };\n        Animation.prototype.animate = function (target, options) {\n            options = options || {};\n            this.start();\n            var animator = new Animator(target, options.loop);\n            this.addAnimator(animator);\n            return animator;\n        };\n        return Animation;\n    }(Eventful));\n\n    var TOUCH_CLICK_DELAY = 300;\n    var globalEventSupported = env.domSupported;\n    var localNativeListenerNames = (function () {\n        var mouseHandlerNames = [\n            'click', 'dblclick', 'mousewheel', 'wheel', 'mouseout',\n            'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n        ];\n        var touchHandlerNames = [\n            'touchstart', 'touchend', 'touchmove'\n        ];\n        var pointerEventNameMap = {\n            pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1\n        };\n        var pointerHandlerNames = map(mouseHandlerNames, function (name) {\n            var nm = name.replace('mouse', 'pointer');\n            return pointerEventNameMap.hasOwnProperty(nm) ? nm : name;\n        });\n        return {\n            mouse: mouseHandlerNames,\n            touch: touchHandlerNames,\n            pointer: pointerHandlerNames\n        };\n    })();\n    var globalNativeListenerNames = {\n        mouse: ['mousemove', 'mouseup'],\n        pointer: ['pointermove', 'pointerup']\n    };\n    var wheelEventSupported = false;\n    function isPointerFromTouch(event) {\n        var pointerType = event.pointerType;\n        return pointerType === 'pen' || pointerType === 'touch';\n    }\n    function setTouchTimer(scope) {\n        scope.touching = true;\n        if (scope.touchTimer != null) {\n            clearTimeout(scope.touchTimer);\n            scope.touchTimer = null;\n        }\n        scope.touchTimer = setTimeout(function () {\n            scope.touching = false;\n            scope.touchTimer = null;\n        }, 700);\n    }\n    function markTouch(event) {\n        event && (event.zrByTouch = true);\n    }\n    function normalizeGlobalEvent(instance, event) {\n        return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true);\n    }\n    function isLocalEl(instance, el) {\n        var elTmp = el;\n        var isLocal = false;\n        while (elTmp && elTmp.nodeType !== 9\n            && !(isLocal = elTmp.domBelongToZr\n                || (elTmp !== el && elTmp === instance.painterRoot))) {\n            elTmp = elTmp.parentNode;\n        }\n        return isLocal;\n    }\n    var FakeGlobalEvent = (function () {\n        function FakeGlobalEvent(instance, event) {\n            this.stopPropagation = noop;\n            this.stopImmediatePropagation = noop;\n            this.preventDefault = noop;\n            this.type = event.type;\n            this.target = this.currentTarget = instance.dom;\n            this.pointerType = event.pointerType;\n            this.clientX = event.clientX;\n            this.clientY = event.clientY;\n        }\n        return FakeGlobalEvent;\n    }());\n    var localDOMHandlers = {\n        mousedown: function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.__mayPointerCapture = [event.zrX, event.zrY];\n            this.trigger('mousedown', event);\n        },\n        mousemove: function (event) {\n            event = normalizeEvent(this.dom, event);\n            var downPoint = this.__mayPointerCapture;\n            if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) {\n                this.__togglePointerCapture(true);\n            }\n            this.trigger('mousemove', event);\n        },\n        mouseup: function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.__togglePointerCapture(false);\n            this.trigger('mouseup', event);\n        },\n        mouseout: function (event) {\n            event = normalizeEvent(this.dom, event);\n            var element = event.toElement || event.relatedTarget;\n            if (!isLocalEl(this, element)) {\n                if (this.__pointerCapturing) {\n                    event.zrEventControl = 'no_globalout';\n                }\n                this.trigger('mouseout', event);\n            }\n        },\n        wheel: function (event) {\n            wheelEventSupported = true;\n            event = normalizeEvent(this.dom, event);\n            this.trigger('mousewheel', event);\n        },\n        mousewheel: function (event) {\n            if (wheelEventSupported) {\n                return;\n            }\n            event = normalizeEvent(this.dom, event);\n            this.trigger('mousewheel', event);\n        },\n        touchstart: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.__lastTouchMoment = new Date();\n            this.handler.processGesture(event, 'start');\n            localDOMHandlers.mousemove.call(this, event);\n            localDOMHandlers.mousedown.call(this, event);\n        },\n        touchmove: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.handler.processGesture(event, 'change');\n            localDOMHandlers.mousemove.call(this, event);\n        },\n        touchend: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.handler.processGesture(event, 'end');\n            localDOMHandlers.mouseup.call(this, event);\n            if (+new Date() - (+this.__lastTouchMoment) < TOUCH_CLICK_DELAY) {\n                localDOMHandlers.click.call(this, event);\n            }\n        },\n        pointerdown: function (event) {\n            localDOMHandlers.mousedown.call(this, event);\n        },\n        pointermove: function (event) {\n            if (!isPointerFromTouch(event)) {\n                localDOMHandlers.mousemove.call(this, event);\n            }\n        },\n        pointerup: function (event) {\n            localDOMHandlers.mouseup.call(this, event);\n        },\n        pointerout: function (event) {\n            if (!isPointerFromTouch(event)) {\n                localDOMHandlers.mouseout.call(this, event);\n            }\n        }\n    };\n    each(['click', 'dblclick', 'contextmenu'], function (name) {\n        localDOMHandlers[name] = function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.trigger(name, event);\n        };\n    });\n    var globalDOMHandlers = {\n        pointermove: function (event) {\n            if (!isPointerFromTouch(event)) {\n                globalDOMHandlers.mousemove.call(this, event);\n            }\n        },\n        pointerup: function (event) {\n            globalDOMHandlers.mouseup.call(this, event);\n        },\n        mousemove: function (event) {\n            this.trigger('mousemove', event);\n        },\n        mouseup: function (event) {\n            var pointerCaptureReleasing = this.__pointerCapturing;\n            this.__togglePointerCapture(false);\n            this.trigger('mouseup', event);\n            if (pointerCaptureReleasing) {\n                event.zrEventControl = 'only_globalout';\n                this.trigger('mouseout', event);\n            }\n        }\n    };\n    function mountLocalDOMEventListeners(instance, scope) {\n        var domHandlers = scope.domHandlers;\n        if (env.pointerEventsSupported) {\n            each(localNativeListenerNames.pointer, function (nativeEventName) {\n                mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                    domHandlers[nativeEventName].call(instance, event);\n                });\n            });\n        }\n        else {\n            if (env.touchEventsSupported) {\n                each(localNativeListenerNames.touch, function (nativeEventName) {\n                    mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                        domHandlers[nativeEventName].call(instance, event);\n                        setTouchTimer(scope);\n                    });\n                });\n            }\n            each(localNativeListenerNames.mouse, function (nativeEventName) {\n                mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                    event = getNativeEvent(event);\n                    if (!scope.touching) {\n                        domHandlers[nativeEventName].call(instance, event);\n                    }\n                });\n            });\n        }\n    }\n    function mountGlobalDOMEventListeners(instance, scope) {\n        if (env.pointerEventsSupported) {\n            each(globalNativeListenerNames.pointer, mount);\n        }\n        else if (!env.touchEventsSupported) {\n            each(globalNativeListenerNames.mouse, mount);\n        }\n        function mount(nativeEventName) {\n            function nativeEventListener(event) {\n                event = getNativeEvent(event);\n                if (!isLocalEl(instance, event.target)) {\n                    event = normalizeGlobalEvent(instance, event);\n                    scope.domHandlers[nativeEventName].call(instance, event);\n                }\n            }\n            mountSingleDOMEventListener(scope, nativeEventName, nativeEventListener, { capture: true });\n        }\n    }\n    function mountSingleDOMEventListener(scope, nativeEventName, listener, opt) {\n        scope.mounted[nativeEventName] = listener;\n        scope.listenerOpts[nativeEventName] = opt;\n        addEventListener(scope.domTarget, nativeEventName, listener, opt);\n    }\n    function unmountDOMEventListeners(scope) {\n        var mounted = scope.mounted;\n        for (var nativeEventName in mounted) {\n            if (mounted.hasOwnProperty(nativeEventName)) {\n                removeEventListener(scope.domTarget, nativeEventName, mounted[nativeEventName], scope.listenerOpts[nativeEventName]);\n            }\n        }\n        scope.mounted = {};\n    }\n    var DOMHandlerScope = (function () {\n        function DOMHandlerScope(domTarget, domHandlers) {\n            this.mounted = {};\n            this.listenerOpts = {};\n            this.touching = false;\n            this.domTarget = domTarget;\n            this.domHandlers = domHandlers;\n        }\n        return DOMHandlerScope;\n    }());\n    var HandlerDomProxy = (function (_super) {\n        __extends(HandlerDomProxy, _super);\n        function HandlerDomProxy(dom, painterRoot) {\n            var _this = _super.call(this) || this;\n            _this.__pointerCapturing = false;\n            _this.dom = dom;\n            _this.painterRoot = painterRoot;\n            _this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers);\n            if (globalEventSupported) {\n                _this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers);\n            }\n            mountLocalDOMEventListeners(_this, _this._localHandlerScope);\n            return _this;\n        }\n        HandlerDomProxy.prototype.dispose = function () {\n            unmountDOMEventListeners(this._localHandlerScope);\n            if (globalEventSupported) {\n                unmountDOMEventListeners(this._globalHandlerScope);\n            }\n        };\n        HandlerDomProxy.prototype.setCursor = function (cursorStyle) {\n            this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');\n        };\n        HandlerDomProxy.prototype.__togglePointerCapture = function (isPointerCapturing) {\n            this.__mayPointerCapture = null;\n            if (globalEventSupported\n                && ((+this.__pointerCapturing) ^ (+isPointerCapturing))) {\n                this.__pointerCapturing = isPointerCapturing;\n                var globalHandlerScope = this._globalHandlerScope;\n                isPointerCapturing\n                    ? mountGlobalDOMEventListeners(this, globalHandlerScope)\n                    : unmountDOMEventListeners(globalHandlerScope);\n            }\n        };\n        return HandlerDomProxy;\n    }(Eventful));\n\n    var dpr = 1;\n    if (env.hasGlobalWindow) {\n        dpr = Math.max(window.devicePixelRatio\n            || (window.screen && window.screen.deviceXDPI / window.screen.logicalXDPI)\n            || 1, 1);\n    }\n    var devicePixelRatio = dpr;\n    var DARK_MODE_THRESHOLD = 0.4;\n    var DARK_LABEL_COLOR = '#333';\n    var LIGHT_LABEL_COLOR = '#ccc';\n    var LIGHTER_LABEL_COLOR = '#eee';\n\n    var mIdentity = identity;\n    var EPSILON$2 = 5e-5;\n    function isNotAroundZero$1(val) {\n        return val > EPSILON$2 || val < -EPSILON$2;\n    }\n    var scaleTmp = [];\n    var tmpTransform = [];\n    var originTransform = create$1();\n    var abs = Math.abs;\n    var Transformable = (function () {\n        function Transformable() {\n        }\n        Transformable.prototype.getLocalTransform = function (m) {\n            return Transformable.getLocalTransform(this, m);\n        };\n        Transformable.prototype.setPosition = function (arr) {\n            this.x = arr[0];\n            this.y = arr[1];\n        };\n        Transformable.prototype.setScale = function (arr) {\n            this.scaleX = arr[0];\n            this.scaleY = arr[1];\n        };\n        Transformable.prototype.setSkew = function (arr) {\n            this.skewX = arr[0];\n            this.skewY = arr[1];\n        };\n        Transformable.prototype.setOrigin = function (arr) {\n            this.originX = arr[0];\n            this.originY = arr[1];\n        };\n        Transformable.prototype.needLocalTransform = function () {\n            return isNotAroundZero$1(this.rotation)\n                || isNotAroundZero$1(this.x)\n                || isNotAroundZero$1(this.y)\n                || isNotAroundZero$1(this.scaleX - 1)\n                || isNotAroundZero$1(this.scaleY - 1)\n                || isNotAroundZero$1(this.skewX)\n                || isNotAroundZero$1(this.skewY);\n        };\n        Transformable.prototype.updateTransform = function () {\n            var parentTransform = this.parent && this.parent.transform;\n            var needLocalTransform = this.needLocalTransform();\n            var m = this.transform;\n            if (!(needLocalTransform || parentTransform)) {\n                m && mIdentity(m);\n                return;\n            }\n            m = m || create$1();\n            if (needLocalTransform) {\n                this.getLocalTransform(m);\n            }\n            else {\n                mIdentity(m);\n            }\n            if (parentTransform) {\n                if (needLocalTransform) {\n                    mul$1(m, parentTransform, m);\n                }\n                else {\n                    copy$1(m, parentTransform);\n                }\n            }\n            this.transform = m;\n            this._resolveGlobalScaleRatio(m);\n        };\n        Transformable.prototype._resolveGlobalScaleRatio = function (m) {\n            var globalScaleRatio = this.globalScaleRatio;\n            if (globalScaleRatio != null && globalScaleRatio !== 1) {\n                this.getGlobalScale(scaleTmp);\n                var relX = scaleTmp[0] < 0 ? -1 : 1;\n                var relY = scaleTmp[1] < 0 ? -1 : 1;\n                var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;\n                var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;\n                m[0] *= sx;\n                m[1] *= sx;\n                m[2] *= sy;\n                m[3] *= sy;\n            }\n            this.invTransform = this.invTransform || create$1();\n            invert(this.invTransform, m);\n        };\n        Transformable.prototype.getComputedTransform = function () {\n            var transformNode = this;\n            var ancestors = [];\n            while (transformNode) {\n                ancestors.push(transformNode);\n                transformNode = transformNode.parent;\n            }\n            while (transformNode = ancestors.pop()) {\n                transformNode.updateTransform();\n            }\n            return this.transform;\n        };\n        Transformable.prototype.setLocalTransform = function (m) {\n            if (!m) {\n                return;\n            }\n            var sx = m[0] * m[0] + m[1] * m[1];\n            var sy = m[2] * m[2] + m[3] * m[3];\n            var rotation = Math.atan2(m[1], m[0]);\n            var shearX = Math.PI / 2 + rotation - Math.atan2(m[3], m[2]);\n            sy = Math.sqrt(sy) * Math.cos(shearX);\n            sx = Math.sqrt(sx);\n            this.skewX = shearX;\n            this.skewY = 0;\n            this.rotation = -rotation;\n            this.x = +m[4];\n            this.y = +m[5];\n            this.scaleX = sx;\n            this.scaleY = sy;\n            this.originX = 0;\n            this.originY = 0;\n        };\n        Transformable.prototype.decomposeTransform = function () {\n            if (!this.transform) {\n                return;\n            }\n            var parent = this.parent;\n            var m = this.transform;\n            if (parent && parent.transform) {\n                mul$1(tmpTransform, parent.invTransform, m);\n                m = tmpTransform;\n            }\n            var ox = this.originX;\n            var oy = this.originY;\n            if (ox || oy) {\n                originTransform[4] = ox;\n                originTransform[5] = oy;\n                mul$1(tmpTransform, m, originTransform);\n                tmpTransform[4] -= ox;\n                tmpTransform[5] -= oy;\n                m = tmpTransform;\n            }\n            this.setLocalTransform(m);\n        };\n        Transformable.prototype.getGlobalScale = function (out) {\n            var m = this.transform;\n            out = out || [];\n            if (!m) {\n                out[0] = 1;\n                out[1] = 1;\n                return out;\n            }\n            out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);\n            out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);\n            if (m[0] < 0) {\n                out[0] = -out[0];\n            }\n            if (m[3] < 0) {\n                out[1] = -out[1];\n            }\n            return out;\n        };\n        Transformable.prototype.transformCoordToLocal = function (x, y) {\n            var v2 = [x, y];\n            var invTransform = this.invTransform;\n            if (invTransform) {\n                applyTransform(v2, v2, invTransform);\n            }\n            return v2;\n        };\n        Transformable.prototype.transformCoordToGlobal = function (x, y) {\n            var v2 = [x, y];\n            var transform = this.transform;\n            if (transform) {\n                applyTransform(v2, v2, transform);\n            }\n            return v2;\n        };\n        Transformable.prototype.getLineScale = function () {\n            var m = this.transform;\n            return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10\n                ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))\n                : 1;\n        };\n        Transformable.prototype.copyTransform = function (source) {\n            copyTransform(this, source);\n        };\n        Transformable.getLocalTransform = function (target, m) {\n            m = m || [];\n            var ox = target.originX || 0;\n            var oy = target.originY || 0;\n            var sx = target.scaleX;\n            var sy = target.scaleY;\n            var ax = target.anchorX;\n            var ay = target.anchorY;\n            var rotation = target.rotation || 0;\n            var x = target.x;\n            var y = target.y;\n            var skewX = target.skewX ? Math.tan(target.skewX) : 0;\n            var skewY = target.skewY ? Math.tan(-target.skewY) : 0;\n            if (ox || oy || ax || ay) {\n                var dx = ox + ax;\n                var dy = oy + ay;\n                m[4] = -dx * sx - skewX * dy * sy;\n                m[5] = -dy * sy - skewY * dx * sx;\n            }\n            else {\n                m[4] = m[5] = 0;\n            }\n            m[0] = sx;\n            m[3] = sy;\n            m[1] = skewY * sx;\n            m[2] = skewX * sy;\n            rotation && rotate(m, m, rotation);\n            m[4] += ox + x;\n            m[5] += oy + y;\n            return m;\n        };\n        Transformable.initDefaultProps = (function () {\n            var proto = Transformable.prototype;\n            proto.scaleX =\n                proto.scaleY =\n                    proto.globalScaleRatio = 1;\n            proto.x =\n                proto.y =\n                    proto.originX =\n                        proto.originY =\n                            proto.skewX =\n                                proto.skewY =\n                                    proto.rotation =\n                                        proto.anchorX =\n                                            proto.anchorY = 0;\n        })();\n        return Transformable;\n    }());\n    var TRANSFORMABLE_PROPS = [\n        'x', 'y', 'originX', 'originY', 'anchorX', 'anchorY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY'\n    ];\n    function copyTransform(target, source) {\n        for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {\n            var propName = TRANSFORMABLE_PROPS[i];\n            target[propName] = source[propName];\n        }\n    }\n\n    var textWidthCache = {};\n    function getWidth(text, font) {\n        font = font || DEFAULT_FONT;\n        var cacheOfFont = textWidthCache[font];\n        if (!cacheOfFont) {\n            cacheOfFont = textWidthCache[font] = new LRU(500);\n        }\n        var width = cacheOfFont.get(text);\n        if (width == null) {\n            width = platformApi.measureText(text, font).width;\n            cacheOfFont.put(text, width);\n        }\n        return width;\n    }\n    function innerGetBoundingRect(text, font, textAlign, textBaseline) {\n        var width = getWidth(text, font);\n        var height = getLineHeight(font);\n        var x = adjustTextX(0, width, textAlign);\n        var y = adjustTextY$1(0, height, textBaseline);\n        var rect = new BoundingRect(x, y, width, height);\n        return rect;\n    }\n    function getBoundingRect(text, font, textAlign, textBaseline) {\n        var textLines = ((text || '') + '').split('\\n');\n        var len = textLines.length;\n        if (len === 1) {\n            return innerGetBoundingRect(textLines[0], font, textAlign, textBaseline);\n        }\n        else {\n            var uniondRect = new BoundingRect(0, 0, 0, 0);\n            for (var i = 0; i < textLines.length; i++) {\n                var rect = innerGetBoundingRect(textLines[i], font, textAlign, textBaseline);\n                i === 0 ? uniondRect.copy(rect) : uniondRect.union(rect);\n            }\n            return uniondRect;\n        }\n    }\n    function adjustTextX(x, width, textAlign) {\n        if (textAlign === 'right') {\n            x -= width;\n        }\n        else if (textAlign === 'center') {\n            x -= width / 2;\n        }\n        return x;\n    }\n    function adjustTextY$1(y, height, verticalAlign) {\n        if (verticalAlign === 'middle') {\n            y -= height / 2;\n        }\n        else if (verticalAlign === 'bottom') {\n            y -= height;\n        }\n        return y;\n    }\n    function getLineHeight(font) {\n        return getWidth('国', font);\n    }\n    function parsePercent(value, maxValue) {\n        if (typeof value === 'string') {\n            if (value.lastIndexOf('%') >= 0) {\n                return parseFloat(value) / 100 * maxValue;\n            }\n            return parseFloat(value);\n        }\n        return value;\n    }\n    function calculateTextPosition(out, opts, rect) {\n        var textPosition = opts.position || 'inside';\n        var distance = opts.distance != null ? opts.distance : 5;\n        var height = rect.height;\n        var width = rect.width;\n        var halfHeight = height / 2;\n        var x = rect.x;\n        var y = rect.y;\n        var textAlign = 'left';\n        var textVerticalAlign = 'top';\n        if (textPosition instanceof Array) {\n            x += parsePercent(textPosition[0], rect.width);\n            y += parsePercent(textPosition[1], rect.height);\n            textAlign = null;\n            textVerticalAlign = null;\n        }\n        else {\n            switch (textPosition) {\n                case 'left':\n                    x -= distance;\n                    y += halfHeight;\n                    textAlign = 'right';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'right':\n                    x += distance + width;\n                    y += halfHeight;\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'top':\n                    x += width / 2;\n                    y -= distance;\n                    textAlign = 'center';\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'bottom':\n                    x += width / 2;\n                    y += height + distance;\n                    textAlign = 'center';\n                    break;\n                case 'inside':\n                    x += width / 2;\n                    y += halfHeight;\n                    textAlign = 'center';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideLeft':\n                    x += distance;\n                    y += halfHeight;\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideRight':\n                    x += width - distance;\n                    y += halfHeight;\n                    textAlign = 'right';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideTop':\n                    x += width / 2;\n                    y += distance;\n                    textAlign = 'center';\n                    break;\n                case 'insideBottom':\n                    x += width / 2;\n                    y += height - distance;\n                    textAlign = 'center';\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'insideTopLeft':\n                    x += distance;\n                    y += distance;\n                    break;\n                case 'insideTopRight':\n                    x += width - distance;\n                    y += distance;\n                    textAlign = 'right';\n                    break;\n                case 'insideBottomLeft':\n                    x += distance;\n                    y += height - distance;\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'insideBottomRight':\n                    x += width - distance;\n                    y += height - distance;\n                    textAlign = 'right';\n                    textVerticalAlign = 'bottom';\n                    break;\n            }\n        }\n        out = out || {};\n        out.x = x;\n        out.y = y;\n        out.align = textAlign;\n        out.verticalAlign = textVerticalAlign;\n        return out;\n    }\n\n    var PRESERVED_NORMAL_STATE = '__zr_normal__';\n    var PRIMARY_STATES_KEYS = TRANSFORMABLE_PROPS.concat(['ignore']);\n    var DEFAULT_ANIMATABLE_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {\n        obj[key] = true;\n        return obj;\n    }, { ignore: false });\n    var tmpTextPosCalcRes = {};\n    var tmpBoundingRect = new BoundingRect(0, 0, 0, 0);\n    var Element = (function () {\n        function Element(props) {\n            this.id = guid();\n            this.animators = [];\n            this.currentStates = [];\n            this.states = {};\n            this._init(props);\n        }\n        Element.prototype._init = function (props) {\n            this.attr(props);\n        };\n        Element.prototype.drift = function (dx, dy, e) {\n            switch (this.draggable) {\n                case 'horizontal':\n                    dy = 0;\n                    break;\n                case 'vertical':\n                    dx = 0;\n                    break;\n            }\n            var m = this.transform;\n            if (!m) {\n                m = this.transform = [1, 0, 0, 1, 0, 0];\n            }\n            m[4] += dx;\n            m[5] += dy;\n            this.decomposeTransform();\n            this.markRedraw();\n        };\n        Element.prototype.beforeUpdate = function () { };\n        Element.prototype.afterUpdate = function () { };\n        Element.prototype.update = function () {\n            this.updateTransform();\n            if (this.__dirty) {\n                this.updateInnerText();\n            }\n        };\n        Element.prototype.updateInnerText = function (forceUpdate) {\n            var textEl = this._textContent;\n            if (textEl && (!textEl.ignore || forceUpdate)) {\n                if (!this.textConfig) {\n                    this.textConfig = {};\n                }\n                var textConfig = this.textConfig;\n                var isLocal = textConfig.local;\n                var innerTransformable = textEl.innerTransformable;\n                var textAlign = void 0;\n                var textVerticalAlign = void 0;\n                var textStyleChanged = false;\n                innerTransformable.parent = isLocal ? this : null;\n                var innerOrigin = false;\n                innerTransformable.copyTransform(textEl);\n                if (textConfig.position != null) {\n                    var layoutRect = tmpBoundingRect;\n                    if (textConfig.layoutRect) {\n                        layoutRect.copy(textConfig.layoutRect);\n                    }\n                    else {\n                        layoutRect.copy(this.getBoundingRect());\n                    }\n                    if (!isLocal) {\n                        layoutRect.applyTransform(this.transform);\n                    }\n                    if (this.calculateTextPosition) {\n                        this.calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                    }\n                    else {\n                        calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                    }\n                    innerTransformable.x = tmpTextPosCalcRes.x;\n                    innerTransformable.y = tmpTextPosCalcRes.y;\n                    textAlign = tmpTextPosCalcRes.align;\n                    textVerticalAlign = tmpTextPosCalcRes.verticalAlign;\n                    var textOrigin = textConfig.origin;\n                    if (textOrigin && textConfig.rotation != null) {\n                        var relOriginX = void 0;\n                        var relOriginY = void 0;\n                        if (textOrigin === 'center') {\n                            relOriginX = layoutRect.width * 0.5;\n                            relOriginY = layoutRect.height * 0.5;\n                        }\n                        else {\n                            relOriginX = parsePercent(textOrigin[0], layoutRect.width);\n                            relOriginY = parsePercent(textOrigin[1], layoutRect.height);\n                        }\n                        innerOrigin = true;\n                        innerTransformable.originX = -innerTransformable.x + relOriginX + (isLocal ? 0 : layoutRect.x);\n                        innerTransformable.originY = -innerTransformable.y + relOriginY + (isLocal ? 0 : layoutRect.y);\n                    }\n                }\n                if (textConfig.rotation != null) {\n                    innerTransformable.rotation = textConfig.rotation;\n                }\n                var textOffset = textConfig.offset;\n                if (textOffset) {\n                    innerTransformable.x += textOffset[0];\n                    innerTransformable.y += textOffset[1];\n                    if (!innerOrigin) {\n                        innerTransformable.originX = -textOffset[0];\n                        innerTransformable.originY = -textOffset[1];\n                    }\n                }\n                var isInside = textConfig.inside == null\n                    ? (typeof textConfig.position === 'string' && textConfig.position.indexOf('inside') >= 0)\n                    : textConfig.inside;\n                var innerTextDefaultStyle = this._innerTextDefaultStyle || (this._innerTextDefaultStyle = {});\n                var textFill = void 0;\n                var textStroke = void 0;\n                var autoStroke = void 0;\n                if (isInside && this.canBeInsideText()) {\n                    textFill = textConfig.insideFill;\n                    textStroke = textConfig.insideStroke;\n                    if (textFill == null || textFill === 'auto') {\n                        textFill = this.getInsideTextFill();\n                    }\n                    if (textStroke == null || textStroke === 'auto') {\n                        textStroke = this.getInsideTextStroke(textFill);\n                        autoStroke = true;\n                    }\n                }\n                else {\n                    textFill = textConfig.outsideFill;\n                    textStroke = textConfig.outsideStroke;\n                    if (textFill == null || textFill === 'auto') {\n                        textFill = this.getOutsideFill();\n                    }\n                    if (textStroke == null || textStroke === 'auto') {\n                        textStroke = this.getOutsideStroke(textFill);\n                        autoStroke = true;\n                    }\n                }\n                textFill = textFill || '#000';\n                if (textFill !== innerTextDefaultStyle.fill\n                    || textStroke !== innerTextDefaultStyle.stroke\n                    || autoStroke !== innerTextDefaultStyle.autoStroke\n                    || textAlign !== innerTextDefaultStyle.align\n                    || textVerticalAlign !== innerTextDefaultStyle.verticalAlign) {\n                    textStyleChanged = true;\n                    innerTextDefaultStyle.fill = textFill;\n                    innerTextDefaultStyle.stroke = textStroke;\n                    innerTextDefaultStyle.autoStroke = autoStroke;\n                    innerTextDefaultStyle.align = textAlign;\n                    innerTextDefaultStyle.verticalAlign = textVerticalAlign;\n                    textEl.setDefaultTextStyle(innerTextDefaultStyle);\n                }\n                textEl.__dirty |= REDRAW_BIT;\n                if (textStyleChanged) {\n                    textEl.dirtyStyle(true);\n                }\n            }\n        };\n        Element.prototype.canBeInsideText = function () {\n            return true;\n        };\n        Element.prototype.getInsideTextFill = function () {\n            return '#fff';\n        };\n        Element.prototype.getInsideTextStroke = function (textFill) {\n            return '#000';\n        };\n        Element.prototype.getOutsideFill = function () {\n            return this.__zr && this.__zr.isDarkMode() ? LIGHT_LABEL_COLOR : DARK_LABEL_COLOR;\n        };\n        Element.prototype.getOutsideStroke = function (textFill) {\n            var backgroundColor = this.__zr && this.__zr.getBackgroundColor();\n            var colorArr = typeof backgroundColor === 'string' && parse(backgroundColor);\n            if (!colorArr) {\n                colorArr = [255, 255, 255, 1];\n            }\n            var alpha = colorArr[3];\n            var isDark = this.__zr.isDarkMode();\n            for (var i = 0; i < 3; i++) {\n                colorArr[i] = colorArr[i] * alpha + (isDark ? 0 : 255) * (1 - alpha);\n            }\n            colorArr[3] = 1;\n            return stringify(colorArr, 'rgba');\n        };\n        Element.prototype.traverse = function (cb, context) { };\n        Element.prototype.attrKV = function (key, value) {\n            if (key === 'textConfig') {\n                this.setTextConfig(value);\n            }\n            else if (key === 'textContent') {\n                this.setTextContent(value);\n            }\n            else if (key === 'clipPath') {\n                this.setClipPath(value);\n            }\n            else if (key === 'extra') {\n                this.extra = this.extra || {};\n                extend(this.extra, value);\n            }\n            else {\n                this[key] = value;\n            }\n        };\n        Element.prototype.hide = function () {\n            this.ignore = true;\n            this.markRedraw();\n        };\n        Element.prototype.show = function () {\n            this.ignore = false;\n            this.markRedraw();\n        };\n        Element.prototype.attr = function (keyOrObj, value) {\n            if (typeof keyOrObj === 'string') {\n                this.attrKV(keyOrObj, value);\n            }\n            else if (isObject(keyOrObj)) {\n                var obj = keyOrObj;\n                var keysArr = keys(obj);\n                for (var i = 0; i < keysArr.length; i++) {\n                    var key = keysArr[i];\n                    this.attrKV(key, keyOrObj[key]);\n                }\n            }\n            this.markRedraw();\n            return this;\n        };\n        Element.prototype.saveCurrentToNormalState = function (toState) {\n            this._innerSaveToNormal(toState);\n            var normalState = this._normalState;\n            for (var i = 0; i < this.animators.length; i++) {\n                var animator = this.animators[i];\n                var fromStateTransition = animator.__fromStateTransition;\n                if (animator.getLoop() || fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) {\n                    continue;\n                }\n                var targetName = animator.targetName;\n                var target = targetName\n                    ? normalState[targetName] : normalState;\n                animator.saveTo(target);\n            }\n        };\n        Element.prototype._innerSaveToNormal = function (toState) {\n            var normalState = this._normalState;\n            if (!normalState) {\n                normalState = this._normalState = {};\n            }\n            if (toState.textConfig && !normalState.textConfig) {\n                normalState.textConfig = this.textConfig;\n            }\n            this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS);\n        };\n        Element.prototype._savePrimaryToNormal = function (toState, normalState, primaryKeys) {\n            for (var i = 0; i < primaryKeys.length; i++) {\n                var key = primaryKeys[i];\n                if (toState[key] != null && !(key in normalState)) {\n                    normalState[key] = this[key];\n                }\n            }\n        };\n        Element.prototype.hasState = function () {\n            return this.currentStates.length > 0;\n        };\n        Element.prototype.getState = function (name) {\n            return this.states[name];\n        };\n        Element.prototype.ensureState = function (name) {\n            var states = this.states;\n            if (!states[name]) {\n                states[name] = {};\n            }\n            return states[name];\n        };\n        Element.prototype.clearStates = function (noAnimation) {\n            this.useState(PRESERVED_NORMAL_STATE, false, noAnimation);\n        };\n        Element.prototype.useState = function (stateName, keepCurrentStates, noAnimation, forceUseHoverLayer) {\n            var toNormalState = stateName === PRESERVED_NORMAL_STATE;\n            var hasStates = this.hasState();\n            if (!hasStates && toNormalState) {\n                return;\n            }\n            var currentStates = this.currentStates;\n            var animationCfg = this.stateTransition;\n            if (indexOf(currentStates, stateName) >= 0 && (keepCurrentStates || currentStates.length === 1)) {\n                return;\n            }\n            var state;\n            if (this.stateProxy && !toNormalState) {\n                state = this.stateProxy(stateName);\n            }\n            if (!state) {\n                state = (this.states && this.states[stateName]);\n            }\n            if (!state && !toNormalState) {\n                logError(\"State \" + stateName + \" not exists.\");\n                return;\n            }\n            if (!toNormalState) {\n                this.saveCurrentToNormalState(state);\n            }\n            var useHoverLayer = !!((state && state.hoverLayer) || forceUseHoverLayer);\n            if (useHoverLayer) {\n                this._toggleHoverLayerFlag(true);\n            }\n            this._applyStateObj(stateName, state, this._normalState, keepCurrentStates, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n            var textContent = this._textContent;\n            var textGuide = this._textGuide;\n            if (textContent) {\n                textContent.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n            }\n            if (textGuide) {\n                textGuide.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n            }\n            if (toNormalState) {\n                this.currentStates = [];\n                this._normalState = {};\n            }\n            else {\n                if (!keepCurrentStates) {\n                    this.currentStates = [stateName];\n                }\n                else {\n                    this.currentStates.push(stateName);\n                }\n            }\n            this._updateAnimationTargets();\n            this.markRedraw();\n            if (!useHoverLayer && this.__inHover) {\n                this._toggleHoverLayerFlag(false);\n                this.__dirty &= ~REDRAW_BIT;\n            }\n            return state;\n        };\n        Element.prototype.useStates = function (states, noAnimation, forceUseHoverLayer) {\n            if (!states.length) {\n                this.clearStates();\n            }\n            else {\n                var stateObjects = [];\n                var currentStates = this.currentStates;\n                var len = states.length;\n                var notChange = len === currentStates.length;\n                if (notChange) {\n                    for (var i = 0; i < len; i++) {\n                        if (states[i] !== currentStates[i]) {\n                            notChange = false;\n                            break;\n                        }\n                    }\n                }\n                if (notChange) {\n                    return;\n                }\n                for (var i = 0; i < len; i++) {\n                    var stateName = states[i];\n                    var stateObj = void 0;\n                    if (this.stateProxy) {\n                        stateObj = this.stateProxy(stateName, states);\n                    }\n                    if (!stateObj) {\n                        stateObj = this.states[stateName];\n                    }\n                    if (stateObj) {\n                        stateObjects.push(stateObj);\n                    }\n                }\n                var lastStateObj = stateObjects[len - 1];\n                var useHoverLayer = !!((lastStateObj && lastStateObj.hoverLayer) || forceUseHoverLayer);\n                if (useHoverLayer) {\n                    this._toggleHoverLayerFlag(true);\n                }\n                var mergedState = this._mergeStates(stateObjects);\n                var animationCfg = this.stateTransition;\n                this.saveCurrentToNormalState(mergedState);\n                this._applyStateObj(states.join(','), mergedState, this._normalState, false, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n                var textContent = this._textContent;\n                var textGuide = this._textGuide;\n                if (textContent) {\n                    textContent.useStates(states, noAnimation, useHoverLayer);\n                }\n                if (textGuide) {\n                    textGuide.useStates(states, noAnimation, useHoverLayer);\n                }\n                this._updateAnimationTargets();\n                this.currentStates = states.slice();\n                this.markRedraw();\n                if (!useHoverLayer && this.__inHover) {\n                    this._toggleHoverLayerFlag(false);\n                    this.__dirty &= ~REDRAW_BIT;\n                }\n            }\n        };\n        Element.prototype._updateAnimationTargets = function () {\n            for (var i = 0; i < this.animators.length; i++) {\n                var animator = this.animators[i];\n                if (animator.targetName) {\n                    animator.changeTarget(this[animator.targetName]);\n                }\n            }\n        };\n        Element.prototype.removeState = function (state) {\n            var idx = indexOf(this.currentStates, state);\n            if (idx >= 0) {\n                var currentStates = this.currentStates.slice();\n                currentStates.splice(idx, 1);\n                this.useStates(currentStates);\n            }\n        };\n        Element.prototype.replaceState = function (oldState, newState, forceAdd) {\n            var currentStates = this.currentStates.slice();\n            var idx = indexOf(currentStates, oldState);\n            var newStateExists = indexOf(currentStates, newState) >= 0;\n            if (idx >= 0) {\n                if (!newStateExists) {\n                    currentStates[idx] = newState;\n                }\n                else {\n                    currentStates.splice(idx, 1);\n                }\n            }\n            else if (forceAdd && !newStateExists) {\n                currentStates.push(newState);\n            }\n            this.useStates(currentStates);\n        };\n        Element.prototype.toggleState = function (state, enable) {\n            if (enable) {\n                this.useState(state, true);\n            }\n            else {\n                this.removeState(state);\n            }\n        };\n        Element.prototype._mergeStates = function (states) {\n            var mergedState = {};\n            var mergedTextConfig;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                extend(mergedState, state);\n                if (state.textConfig) {\n                    mergedTextConfig = mergedTextConfig || {};\n                    extend(mergedTextConfig, state.textConfig);\n                }\n            }\n            if (mergedTextConfig) {\n                mergedState.textConfig = mergedTextConfig;\n            }\n            return mergedState;\n        };\n        Element.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            if (state && state.textConfig) {\n                this.textConfig = extend({}, keepCurrentStates ? this.textConfig : normalState.textConfig);\n                extend(this.textConfig, state.textConfig);\n            }\n            else if (needsRestoreToNormal) {\n                if (normalState.textConfig) {\n                    this.textConfig = normalState.textConfig;\n                }\n            }\n            var transitionTarget = {};\n            var hasTransition = false;\n            for (var i = 0; i < PRIMARY_STATES_KEYS.length; i++) {\n                var key = PRIMARY_STATES_KEYS[i];\n                var propNeedsTransition = transition && DEFAULT_ANIMATABLE_MAP[key];\n                if (state && state[key] != null) {\n                    if (propNeedsTransition) {\n                        hasTransition = true;\n                        transitionTarget[key] = state[key];\n                    }\n                    else {\n                        this[key] = state[key];\n                    }\n                }\n                else if (needsRestoreToNormal) {\n                    if (normalState[key] != null) {\n                        if (propNeedsTransition) {\n                            hasTransition = true;\n                            transitionTarget[key] = normalState[key];\n                        }\n                        else {\n                            this[key] = normalState[key];\n                        }\n                    }\n                }\n            }\n            if (!transition) {\n                for (var i = 0; i < this.animators.length; i++) {\n                    var animator = this.animators[i];\n                    var targetName = animator.targetName;\n                    if (!animator.getLoop()) {\n                        animator.__changeFinalValue(targetName\n                            ? (state || normalState)[targetName]\n                            : (state || normalState));\n                    }\n                }\n            }\n            if (hasTransition) {\n                this._transitionState(stateName, transitionTarget, animationCfg);\n            }\n        };\n        Element.prototype._attachComponent = function (componentEl) {\n            if (componentEl.__zr && !componentEl.__hostTarget) {\n                if (\"development\" !== 'production') {\n                    throw new Error('Text element has been added to zrender.');\n                }\n                return;\n            }\n            if (componentEl === this) {\n                if (\"development\" !== 'production') {\n                    throw new Error('Recursive component attachment.');\n                }\n                return;\n            }\n            var zr = this.__zr;\n            if (zr) {\n                componentEl.addSelfToZr(zr);\n            }\n            componentEl.__zr = zr;\n            componentEl.__hostTarget = this;\n        };\n        Element.prototype._detachComponent = function (componentEl) {\n            if (componentEl.__zr) {\n                componentEl.removeSelfFromZr(componentEl.__zr);\n            }\n            componentEl.__zr = null;\n            componentEl.__hostTarget = null;\n        };\n        Element.prototype.getClipPath = function () {\n            return this._clipPath;\n        };\n        Element.prototype.setClipPath = function (clipPath) {\n            if (this._clipPath && this._clipPath !== clipPath) {\n                this.removeClipPath();\n            }\n            this._attachComponent(clipPath);\n            this._clipPath = clipPath;\n            this.markRedraw();\n        };\n        Element.prototype.removeClipPath = function () {\n            var clipPath = this._clipPath;\n            if (clipPath) {\n                this._detachComponent(clipPath);\n                this._clipPath = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.getTextContent = function () {\n            return this._textContent;\n        };\n        Element.prototype.setTextContent = function (textEl) {\n            var previousTextContent = this._textContent;\n            if (previousTextContent === textEl) {\n                return;\n            }\n            if (previousTextContent && previousTextContent !== textEl) {\n                this.removeTextContent();\n            }\n            if (\"development\" !== 'production') {\n                if (textEl.__zr && !textEl.__hostTarget) {\n                    throw new Error('Text element has been added to zrender.');\n                }\n            }\n            textEl.innerTransformable = new Transformable();\n            this._attachComponent(textEl);\n            this._textContent = textEl;\n            this.markRedraw();\n        };\n        Element.prototype.setTextConfig = function (cfg) {\n            if (!this.textConfig) {\n                this.textConfig = {};\n            }\n            extend(this.textConfig, cfg);\n            this.markRedraw();\n        };\n        Element.prototype.removeTextConfig = function () {\n            this.textConfig = null;\n            this.markRedraw();\n        };\n        Element.prototype.removeTextContent = function () {\n            var textEl = this._textContent;\n            if (textEl) {\n                textEl.innerTransformable = null;\n                this._detachComponent(textEl);\n                this._textContent = null;\n                this._innerTextDefaultStyle = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.getTextGuideLine = function () {\n            return this._textGuide;\n        };\n        Element.prototype.setTextGuideLine = function (guideLine) {\n            if (this._textGuide && this._textGuide !== guideLine) {\n                this.removeTextGuideLine();\n            }\n            this._attachComponent(guideLine);\n            this._textGuide = guideLine;\n            this.markRedraw();\n        };\n        Element.prototype.removeTextGuideLine = function () {\n            var textGuide = this._textGuide;\n            if (textGuide) {\n                this._detachComponent(textGuide);\n                this._textGuide = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.markRedraw = function () {\n            this.__dirty |= REDRAW_BIT;\n            var zr = this.__zr;\n            if (zr) {\n                if (this.__inHover) {\n                    zr.refreshHover();\n                }\n                else {\n                    zr.refresh();\n                }\n            }\n            if (this.__hostTarget) {\n                this.__hostTarget.markRedraw();\n            }\n        };\n        Element.prototype.dirty = function () {\n            this.markRedraw();\n        };\n        Element.prototype._toggleHoverLayerFlag = function (inHover) {\n            this.__inHover = inHover;\n            var textContent = this._textContent;\n            var textGuide = this._textGuide;\n            if (textContent) {\n                textContent.__inHover = inHover;\n            }\n            if (textGuide) {\n                textGuide.__inHover = inHover;\n            }\n        };\n        Element.prototype.addSelfToZr = function (zr) {\n            if (this.__zr === zr) {\n                return;\n            }\n            this.__zr = zr;\n            var animators = this.animators;\n            if (animators) {\n                for (var i = 0; i < animators.length; i++) {\n                    zr.animation.addAnimator(animators[i]);\n                }\n            }\n            if (this._clipPath) {\n                this._clipPath.addSelfToZr(zr);\n            }\n            if (this._textContent) {\n                this._textContent.addSelfToZr(zr);\n            }\n            if (this._textGuide) {\n                this._textGuide.addSelfToZr(zr);\n            }\n        };\n        Element.prototype.removeSelfFromZr = function (zr) {\n            if (!this.__zr) {\n                return;\n            }\n            this.__zr = null;\n            var animators = this.animators;\n            if (animators) {\n                for (var i = 0; i < animators.length; i++) {\n                    zr.animation.removeAnimator(animators[i]);\n                }\n            }\n            if (this._clipPath) {\n                this._clipPath.removeSelfFromZr(zr);\n            }\n            if (this._textContent) {\n                this._textContent.removeSelfFromZr(zr);\n            }\n            if (this._textGuide) {\n                this._textGuide.removeSelfFromZr(zr);\n            }\n        };\n        Element.prototype.animate = function (key, loop, allowDiscreteAnimation) {\n            var target = key ? this[key] : this;\n            if (\"development\" !== 'production') {\n                if (!target) {\n                    logError('Property \"'\n                        + key\n                        + '\" is not existed in element '\n                        + this.id);\n                    return;\n                }\n            }\n            var animator = new Animator(target, loop, allowDiscreteAnimation);\n            key && (animator.targetName = key);\n            this.addAnimator(animator, key);\n            return animator;\n        };\n        Element.prototype.addAnimator = function (animator, key) {\n            var zr = this.__zr;\n            var el = this;\n            animator.during(function () {\n                el.updateDuringAnimation(key);\n            }).done(function () {\n                var animators = el.animators;\n                var idx = indexOf(animators, animator);\n                if (idx >= 0) {\n                    animators.splice(idx, 1);\n                }\n            });\n            this.animators.push(animator);\n            if (zr) {\n                zr.animation.addAnimator(animator);\n            }\n            zr && zr.wakeUp();\n        };\n        Element.prototype.updateDuringAnimation = function (key) {\n            this.markRedraw();\n        };\n        Element.prototype.stopAnimation = function (scope, forwardToLast) {\n            var animators = this.animators;\n            var len = animators.length;\n            var leftAnimators = [];\n            for (var i = 0; i < len; i++) {\n                var animator = animators[i];\n                if (!scope || scope === animator.scope) {\n                    animator.stop(forwardToLast);\n                }\n                else {\n                    leftAnimators.push(animator);\n                }\n            }\n            this.animators = leftAnimators;\n            return this;\n        };\n        Element.prototype.animateTo = function (target, cfg, animationProps) {\n            animateTo(this, target, cfg, animationProps);\n        };\n        Element.prototype.animateFrom = function (target, cfg, animationProps) {\n            animateTo(this, target, cfg, animationProps, true);\n        };\n        Element.prototype._transitionState = function (stateName, target, cfg, animationProps) {\n            var animators = animateTo(this, target, cfg, animationProps);\n            for (var i = 0; i < animators.length; i++) {\n                animators[i].__fromStateTransition = stateName;\n            }\n        };\n        Element.prototype.getBoundingRect = function () {\n            return null;\n        };\n        Element.prototype.getPaintRect = function () {\n            return null;\n        };\n        Element.initDefaultProps = (function () {\n            var elProto = Element.prototype;\n            elProto.type = 'element';\n            elProto.name = '';\n            elProto.ignore =\n                elProto.silent =\n                    elProto.isGroup =\n                        elProto.draggable =\n                            elProto.dragging =\n                                elProto.ignoreClip =\n                                    elProto.__inHover = false;\n            elProto.__dirty = REDRAW_BIT;\n            var logs = {};\n            function logDeprecatedError(key, xKey, yKey) {\n                if (!logs[key + xKey + yKey]) {\n                    console.warn(\"DEPRECATED: '\" + key + \"' has been deprecated. use '\" + xKey + \"', '\" + yKey + \"' instead\");\n                    logs[key + xKey + yKey] = true;\n                }\n            }\n            function createLegacyProperty(key, privateKey, xKey, yKey) {\n                Object.defineProperty(elProto, key, {\n                    get: function () {\n                        if (\"development\" !== 'production') {\n                            logDeprecatedError(key, xKey, yKey);\n                        }\n                        if (!this[privateKey]) {\n                            var pos = this[privateKey] = [];\n                            enhanceArray(this, pos);\n                        }\n                        return this[privateKey];\n                    },\n                    set: function (pos) {\n                        if (\"development\" !== 'production') {\n                            logDeprecatedError(key, xKey, yKey);\n                        }\n                        this[xKey] = pos[0];\n                        this[yKey] = pos[1];\n                        this[privateKey] = pos;\n                        enhanceArray(this, pos);\n                    }\n                });\n                function enhanceArray(self, pos) {\n                    Object.defineProperty(pos, 0, {\n                        get: function () {\n                            return self[xKey];\n                        },\n                        set: function (val) {\n                            self[xKey] = val;\n                        }\n                    });\n                    Object.defineProperty(pos, 1, {\n                        get: function () {\n                            return self[yKey];\n                        },\n                        set: function (val) {\n                            self[yKey] = val;\n                        }\n                    });\n                }\n            }\n            if (Object.defineProperty) {\n                createLegacyProperty('position', '_legacyPos', 'x', 'y');\n                createLegacyProperty('scale', '_legacyScale', 'scaleX', 'scaleY');\n                createLegacyProperty('origin', '_legacyOrigin', 'originX', 'originY');\n            }\n        })();\n        return Element;\n    }());\n    mixin(Element, Eventful);\n    mixin(Element, Transformable);\n    function animateTo(animatable, target, cfg, animationProps, reverse) {\n        cfg = cfg || {};\n        var animators = [];\n        animateToShallow(animatable, '', animatable, target, cfg, animationProps, animators, reverse);\n        var finishCount = animators.length;\n        var doneHappened = false;\n        var cfgDone = cfg.done;\n        var cfgAborted = cfg.aborted;\n        var doneCb = function () {\n            doneHappened = true;\n            finishCount--;\n            if (finishCount <= 0) {\n                doneHappened\n                    ? (cfgDone && cfgDone())\n                    : (cfgAborted && cfgAborted());\n            }\n        };\n        var abortedCb = function () {\n            finishCount--;\n            if (finishCount <= 0) {\n                doneHappened\n                    ? (cfgDone && cfgDone())\n                    : (cfgAborted && cfgAborted());\n            }\n        };\n        if (!finishCount) {\n            cfgDone && cfgDone();\n        }\n        if (animators.length > 0 && cfg.during) {\n            animators[0].during(function (target, percent) {\n                cfg.during(percent);\n            });\n        }\n        for (var i = 0; i < animators.length; i++) {\n            var animator = animators[i];\n            if (doneCb) {\n                animator.done(doneCb);\n            }\n            if (abortedCb) {\n                animator.aborted(abortedCb);\n            }\n            if (cfg.force) {\n                animator.duration(cfg.duration);\n            }\n            animator.start(cfg.easing);\n        }\n        return animators;\n    }\n    function copyArrShallow(source, target, len) {\n        for (var i = 0; i < len; i++) {\n            source[i] = target[i];\n        }\n    }\n    function is2DArray(value) {\n        return isArrayLike(value[0]);\n    }\n    function copyValue(target, source, key) {\n        if (isArrayLike(source[key])) {\n            if (!isArrayLike(target[key])) {\n                target[key] = [];\n            }\n            if (isTypedArray(source[key])) {\n                var len = source[key].length;\n                if (target[key].length !== len) {\n                    target[key] = new (source[key].constructor)(len);\n                    copyArrShallow(target[key], source[key], len);\n                }\n            }\n            else {\n                var sourceArr = source[key];\n                var targetArr = target[key];\n                var len0 = sourceArr.length;\n                if (is2DArray(sourceArr)) {\n                    var len1 = sourceArr[0].length;\n                    for (var i = 0; i < len0; i++) {\n                        if (!targetArr[i]) {\n                            targetArr[i] = Array.prototype.slice.call(sourceArr[i]);\n                        }\n                        else {\n                            copyArrShallow(targetArr[i], sourceArr[i], len1);\n                        }\n                    }\n                }\n                else {\n                    copyArrShallow(targetArr, sourceArr, len0);\n                }\n                targetArr.length = sourceArr.length;\n            }\n        }\n        else {\n            target[key] = source[key];\n        }\n    }\n    function isValueSame(val1, val2) {\n        return val1 === val2\n            || isArrayLike(val1) && isArrayLike(val2) && is1DArraySame(val1, val2);\n    }\n    function is1DArraySame(arr0, arr1) {\n        var len = arr0.length;\n        if (len !== arr1.length) {\n            return false;\n        }\n        for (var i = 0; i < len; i++) {\n            if (arr0[i] !== arr1[i]) {\n                return false;\n            }\n        }\n        return true;\n    }\n    function animateToShallow(animatable, topKey, animateObj, target, cfg, animationProps, animators, reverse) {\n        var targetKeys = keys(target);\n        var duration = cfg.duration;\n        var delay = cfg.delay;\n        var additive = cfg.additive;\n        var setToFinal = cfg.setToFinal;\n        var animateAll = !isObject(animationProps);\n        var existsAnimators = animatable.animators;\n        var animationKeys = [];\n        for (var k = 0; k < targetKeys.length; k++) {\n            var innerKey = targetKeys[k];\n            var targetVal = target[innerKey];\n            if (targetVal != null && animateObj[innerKey] != null\n                && (animateAll || animationProps[innerKey])) {\n                if (isObject(targetVal)\n                    && !isArrayLike(targetVal)\n                    && !isGradientObject(targetVal)) {\n                    if (topKey) {\n                        if (!reverse) {\n                            animateObj[innerKey] = targetVal;\n                            animatable.updateDuringAnimation(topKey);\n                        }\n                        continue;\n                    }\n                    animateToShallow(animatable, innerKey, animateObj[innerKey], targetVal, cfg, animationProps && animationProps[innerKey], animators, reverse);\n                }\n                else {\n                    animationKeys.push(innerKey);\n                }\n            }\n            else if (!reverse) {\n                animateObj[innerKey] = targetVal;\n                animatable.updateDuringAnimation(topKey);\n                animationKeys.push(innerKey);\n            }\n        }\n        var keyLen = animationKeys.length;\n        if (!additive && keyLen) {\n            for (var i = 0; i < existsAnimators.length; i++) {\n                var animator = existsAnimators[i];\n                if (animator.targetName === topKey) {\n                    var allAborted = animator.stopTracks(animationKeys);\n                    if (allAborted) {\n                        var idx = indexOf(existsAnimators, animator);\n                        existsAnimators.splice(idx, 1);\n                    }\n                }\n            }\n        }\n        if (!cfg.force) {\n            animationKeys = filter(animationKeys, function (key) { return !isValueSame(target[key], animateObj[key]); });\n            keyLen = animationKeys.length;\n        }\n        if (keyLen > 0\n            || (cfg.force && !animators.length)) {\n            var revertedSource = void 0;\n            var reversedTarget = void 0;\n            var sourceClone = void 0;\n            if (reverse) {\n                reversedTarget = {};\n                if (setToFinal) {\n                    revertedSource = {};\n                }\n                for (var i = 0; i < keyLen; i++) {\n                    var innerKey = animationKeys[i];\n                    reversedTarget[innerKey] = animateObj[innerKey];\n                    if (setToFinal) {\n                        revertedSource[innerKey] = target[innerKey];\n                    }\n                    else {\n                        animateObj[innerKey] = target[innerKey];\n                    }\n                }\n            }\n            else if (setToFinal) {\n                sourceClone = {};\n                for (var i = 0; i < keyLen; i++) {\n                    var innerKey = animationKeys[i];\n                    sourceClone[innerKey] = cloneValue(animateObj[innerKey]);\n                    copyValue(animateObj, target, innerKey);\n                }\n            }\n            var animator = new Animator(animateObj, false, false, additive ? filter(existsAnimators, function (animator) { return animator.targetName === topKey; }) : null);\n            animator.targetName = topKey;\n            if (cfg.scope) {\n                animator.scope = cfg.scope;\n            }\n            if (setToFinal && revertedSource) {\n                animator.whenWithKeys(0, revertedSource, animationKeys);\n            }\n            if (sourceClone) {\n                animator.whenWithKeys(0, sourceClone, animationKeys);\n            }\n            animator.whenWithKeys(duration == null ? 500 : duration, reverse ? reversedTarget : target, animationKeys).delay(delay || 0);\n            animatable.addAnimator(animator, topKey);\n            animators.push(animator);\n        }\n    }\n\n    var Group = (function (_super) {\n        __extends(Group, _super);\n        function Group(opts) {\n            var _this = _super.call(this) || this;\n            _this.isGroup = true;\n            _this._children = [];\n            _this.attr(opts);\n            return _this;\n        }\n        Group.prototype.childrenRef = function () {\n            return this._children;\n        };\n        Group.prototype.children = function () {\n            return this._children.slice();\n        };\n        Group.prototype.childAt = function (idx) {\n            return this._children[idx];\n        };\n        Group.prototype.childOfName = function (name) {\n            var children = this._children;\n            for (var i = 0; i < children.length; i++) {\n                if (children[i].name === name) {\n                    return children[i];\n                }\n            }\n        };\n        Group.prototype.childCount = function () {\n            return this._children.length;\n        };\n        Group.prototype.add = function (child) {\n            if (child) {\n                if (child !== this && child.parent !== this) {\n                    this._children.push(child);\n                    this._doAdd(child);\n                }\n                if (\"development\" !== 'production') {\n                    if (child.__hostTarget) {\n                        throw 'This elemenet has been used as an attachment';\n                    }\n                }\n            }\n            return this;\n        };\n        Group.prototype.addBefore = function (child, nextSibling) {\n            if (child && child !== this && child.parent !== this\n                && nextSibling && nextSibling.parent === this) {\n                var children = this._children;\n                var idx = children.indexOf(nextSibling);\n                if (idx >= 0) {\n                    children.splice(idx, 0, child);\n                    this._doAdd(child);\n                }\n            }\n            return this;\n        };\n        Group.prototype.replace = function (oldChild, newChild) {\n            var idx = indexOf(this._children, oldChild);\n            if (idx >= 0) {\n                this.replaceAt(newChild, idx);\n            }\n            return this;\n        };\n        Group.prototype.replaceAt = function (child, index) {\n            var children = this._children;\n            var old = children[index];\n            if (child && child !== this && child.parent !== this && child !== old) {\n                children[index] = child;\n                old.parent = null;\n                var zr = this.__zr;\n                if (zr) {\n                    old.removeSelfFromZr(zr);\n                }\n                this._doAdd(child);\n            }\n            return this;\n        };\n        Group.prototype._doAdd = function (child) {\n            if (child.parent) {\n                child.parent.remove(child);\n            }\n            child.parent = this;\n            var zr = this.__zr;\n            if (zr && zr !== child.__zr) {\n                child.addSelfToZr(zr);\n            }\n            zr && zr.refresh();\n        };\n        Group.prototype.remove = function (child) {\n            var zr = this.__zr;\n            var children = this._children;\n            var idx = indexOf(children, child);\n            if (idx < 0) {\n                return this;\n            }\n            children.splice(idx, 1);\n            child.parent = null;\n            if (zr) {\n                child.removeSelfFromZr(zr);\n            }\n            zr && zr.refresh();\n            return this;\n        };\n        Group.prototype.removeAll = function () {\n            var children = this._children;\n            var zr = this.__zr;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                if (zr) {\n                    child.removeSelfFromZr(zr);\n                }\n                child.parent = null;\n            }\n            children.length = 0;\n            return this;\n        };\n        Group.prototype.eachChild = function (cb, context) {\n            var children = this._children;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                cb.call(context, child, i);\n            }\n            return this;\n        };\n        Group.prototype.traverse = function (cb, context) {\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                var stopped = cb.call(context, child);\n                if (child.isGroup && !stopped) {\n                    child.traverse(cb, context);\n                }\n            }\n            return this;\n        };\n        Group.prototype.addSelfToZr = function (zr) {\n            _super.prototype.addSelfToZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.addSelfToZr(zr);\n            }\n        };\n        Group.prototype.removeSelfFromZr = function (zr) {\n            _super.prototype.removeSelfFromZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.removeSelfFromZr(zr);\n            }\n        };\n        Group.prototype.getBoundingRect = function (includeChildren) {\n            var tmpRect = new BoundingRect(0, 0, 0, 0);\n            var children = includeChildren || this._children;\n            var tmpMat = [];\n            var rect = null;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                if (child.ignore || child.invisible) {\n                    continue;\n                }\n                var childRect = child.getBoundingRect();\n                var transform = child.getLocalTransform(tmpMat);\n                if (transform) {\n                    BoundingRect.applyTransform(tmpRect, childRect, transform);\n                    rect = rect || tmpRect.clone();\n                    rect.union(tmpRect);\n                }\n                else {\n                    rect = rect || childRect.clone();\n                    rect.union(childRect);\n                }\n            }\n            return rect || tmpRect;\n        };\n        return Group;\n    }(Element));\n    Group.prototype.type = 'group';\n\n    /*!\n    * ZRender, a high performance 2d drawing library.\n    *\n    * Copyright (c) 2013, Baidu Inc.\n    * All rights reserved.\n    *\n    * LICENSE\n    * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt\n    */\n    var painterCtors = {};\n    var instances = {};\n    function delInstance(id) {\n        delete instances[id];\n    }\n    function isDarkMode(backgroundColor) {\n        if (!backgroundColor) {\n            return false;\n        }\n        if (typeof backgroundColor === 'string') {\n            return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD;\n        }\n        else if (backgroundColor.colorStops) {\n            var colorStops = backgroundColor.colorStops;\n            var totalLum = 0;\n            var len = colorStops.length;\n            for (var i = 0; i < len; i++) {\n                totalLum += lum(colorStops[i].color, 1);\n            }\n            totalLum /= len;\n            return totalLum < DARK_MODE_THRESHOLD;\n        }\n        return false;\n    }\n    var ZRender = (function () {\n        function ZRender(id, dom, opts) {\n            var _this = this;\n            this._sleepAfterStill = 10;\n            this._stillFrameAccum = 0;\n            this._needsRefresh = true;\n            this._needsRefreshHover = true;\n            this._darkMode = false;\n            opts = opts || {};\n            this.dom = dom;\n            this.id = id;\n            var storage = new Storage();\n            var rendererType = opts.renderer || 'canvas';\n            if (!painterCtors[rendererType]) {\n                rendererType = keys(painterCtors)[0];\n            }\n            if (\"development\" !== 'production') {\n                if (!painterCtors[rendererType]) {\n                    throw new Error(\"Renderer '\" + rendererType + \"' is not imported. Please import it first.\");\n                }\n            }\n            opts.useDirtyRect = opts.useDirtyRect == null\n                ? false\n                : opts.useDirtyRect;\n            var painter = new painterCtors[rendererType](dom, storage, opts, id);\n            var ssrMode = opts.ssr || painter.ssrOnly;\n            this.storage = storage;\n            this.painter = painter;\n            var handerProxy = (!env.node && !env.worker && !ssrMode)\n                ? new HandlerDomProxy(painter.getViewportRoot(), painter.root)\n                : null;\n            var useCoarsePointer = opts.useCoarsePointer;\n            var usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto')\n                ? env.touchEventsSupported\n                : !!useCoarsePointer;\n            var defaultPointerSize = 44;\n            var pointerSize;\n            if (usePointerSize) {\n                pointerSize = retrieve2(opts.pointerSize, defaultPointerSize);\n            }\n            this.handler = new Handler(storage, painter, handerProxy, painter.root, pointerSize);\n            this.animation = new Animation({\n                stage: {\n                    update: ssrMode ? null : function () { return _this._flush(true); }\n                }\n            });\n            if (!ssrMode) {\n                this.animation.start();\n            }\n        }\n        ZRender.prototype.add = function (el) {\n            if (!el) {\n                return;\n            }\n            this.storage.addRoot(el);\n            el.addSelfToZr(this);\n            this.refresh();\n        };\n        ZRender.prototype.remove = function (el) {\n            if (!el) {\n                return;\n            }\n            this.storage.delRoot(el);\n            el.removeSelfFromZr(this);\n            this.refresh();\n        };\n        ZRender.prototype.configLayer = function (zLevel, config) {\n            if (this.painter.configLayer) {\n                this.painter.configLayer(zLevel, config);\n            }\n            this.refresh();\n        };\n        ZRender.prototype.setBackgroundColor = function (backgroundColor) {\n            if (this.painter.setBackgroundColor) {\n                this.painter.setBackgroundColor(backgroundColor);\n            }\n            this.refresh();\n            this._backgroundColor = backgroundColor;\n            this._darkMode = isDarkMode(backgroundColor);\n        };\n        ZRender.prototype.getBackgroundColor = function () {\n            return this._backgroundColor;\n        };\n        ZRender.prototype.setDarkMode = function (darkMode) {\n            this._darkMode = darkMode;\n        };\n        ZRender.prototype.isDarkMode = function () {\n            return this._darkMode;\n        };\n        ZRender.prototype.refreshImmediately = function (fromInside) {\n            if (!fromInside) {\n                this.animation.update(true);\n            }\n            this._needsRefresh = false;\n            this.painter.refresh();\n            this._needsRefresh = false;\n        };\n        ZRender.prototype.refresh = function () {\n            this._needsRefresh = true;\n            this.animation.start();\n        };\n        ZRender.prototype.flush = function () {\n            this._flush(false);\n        };\n        ZRender.prototype._flush = function (fromInside) {\n            var triggerRendered;\n            var start = getTime();\n            if (this._needsRefresh) {\n                triggerRendered = true;\n                this.refreshImmediately(fromInside);\n            }\n            if (this._needsRefreshHover) {\n                triggerRendered = true;\n                this.refreshHoverImmediately();\n            }\n            var end = getTime();\n            if (triggerRendered) {\n                this._stillFrameAccum = 0;\n                this.trigger('rendered', {\n                    elapsedTime: end - start\n                });\n            }\n            else if (this._sleepAfterStill > 0) {\n                this._stillFrameAccum++;\n                if (this._stillFrameAccum > this._sleepAfterStill) {\n                    this.animation.stop();\n                }\n            }\n        };\n        ZRender.prototype.setSleepAfterStill = function (stillFramesCount) {\n            this._sleepAfterStill = stillFramesCount;\n        };\n        ZRender.prototype.wakeUp = function () {\n            this.animation.start();\n            this._stillFrameAccum = 0;\n        };\n        ZRender.prototype.refreshHover = function () {\n            this._needsRefreshHover = true;\n        };\n        ZRender.prototype.refreshHoverImmediately = function () {\n            this._needsRefreshHover = false;\n            if (this.painter.refreshHover && this.painter.getType() === 'canvas') {\n                this.painter.refreshHover();\n            }\n        };\n        ZRender.prototype.resize = function (opts) {\n            opts = opts || {};\n            this.painter.resize(opts.width, opts.height);\n            this.handler.resize();\n        };\n        ZRender.prototype.clearAnimation = function () {\n            this.animation.clear();\n        };\n        ZRender.prototype.getWidth = function () {\n            return this.painter.getWidth();\n        };\n        ZRender.prototype.getHeight = function () {\n            return this.painter.getHeight();\n        };\n        ZRender.prototype.setCursorStyle = function (cursorStyle) {\n            this.handler.setCursorStyle(cursorStyle);\n        };\n        ZRender.prototype.findHover = function (x, y) {\n            return this.handler.findHover(x, y);\n        };\n        ZRender.prototype.on = function (eventName, eventHandler, context) {\n            this.handler.on(eventName, eventHandler, context);\n            return this;\n        };\n        ZRender.prototype.off = function (eventName, eventHandler) {\n            this.handler.off(eventName, eventHandler);\n        };\n        ZRender.prototype.trigger = function (eventName, event) {\n            this.handler.trigger(eventName, event);\n        };\n        ZRender.prototype.clear = function () {\n            var roots = this.storage.getRoots();\n            for (var i = 0; i < roots.length; i++) {\n                if (roots[i] instanceof Group) {\n                    roots[i].removeSelfFromZr(this);\n                }\n            }\n            this.storage.delAllRoots();\n            this.painter.clear();\n        };\n        ZRender.prototype.dispose = function () {\n            this.animation.stop();\n            this.clear();\n            this.storage.dispose();\n            this.painter.dispose();\n            this.handler.dispose();\n            this.animation =\n                this.storage =\n                    this.painter =\n                        this.handler = null;\n            delInstance(this.id);\n        };\n        return ZRender;\n    }());\n    function init(dom, opts) {\n        var zr = new ZRender(guid(), dom, opts);\n        instances[zr.id] = zr;\n        return zr;\n    }\n    function dispose(zr) {\n        zr.dispose();\n    }\n    function disposeAll() {\n        for (var key in instances) {\n            if (instances.hasOwnProperty(key)) {\n                instances[key].dispose();\n            }\n        }\n        instances = {};\n    }\n    function getInstance(id) {\n        return instances[id];\n    }\n    function registerPainter(name, Ctor) {\n        painterCtors[name] = Ctor;\n    }\n    var version = '5.4.1';\n\n    var zrender = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init,\n        dispose: dispose,\n        disposeAll: disposeAll,\n        getInstance: getInstance,\n        registerPainter: registerPainter,\n        version: version\n    });\n\n    var RADIAN_EPSILON = 1e-4; // Although chrome already enlarge this number to 100 for `toFixed`, but\n    // we sill follow the spec for compatibility.\n\n    var ROUND_SUPPORTED_PRECISION_MAX = 20;\n\n    function _trim(str) {\n      return str.replace(/^\\s+|\\s+$/g, '');\n    }\n    /**\n     * Linear mapping a value from domain to range\n     * @param  val\n     * @param  domain Domain extent domain[0] can be bigger than domain[1]\n     * @param  range  Range extent range[0] can be bigger than range[1]\n     * @param  clamp Default to be false\n     */\n\n\n    function linearMap(val, domain, range, clamp) {\n      var d0 = domain[0];\n      var d1 = domain[1];\n      var r0 = range[0];\n      var r1 = range[1];\n      var subDomain = d1 - d0;\n      var subRange = r1 - r0;\n\n      if (subDomain === 0) {\n        return subRange === 0 ? r0 : (r0 + r1) / 2;\n      } // Avoid accuracy problem in edge, such as\n      // 146.39 - 62.83 === 83.55999999999999.\n      // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError\n      // It is a little verbose for efficiency considering this method\n      // is a hotspot.\n\n\n      if (clamp) {\n        if (subDomain > 0) {\n          if (val <= d0) {\n            return r0;\n          } else if (val >= d1) {\n            return r1;\n          }\n        } else {\n          if (val >= d0) {\n            return r0;\n          } else if (val <= d1) {\n            return r1;\n          }\n        }\n      } else {\n        if (val === d0) {\n          return r0;\n        }\n\n        if (val === d1) {\n          return r1;\n        }\n      }\n\n      return (val - d0) / subDomain * subRange + r0;\n    }\n    /**\n     * Convert a percent string to absolute number.\n     * Returns NaN if percent is not a valid string or number\n     */\n\n    function parsePercent$1(percent, all) {\n      switch (percent) {\n        case 'center':\n        case 'middle':\n          percent = '50%';\n          break;\n\n        case 'left':\n        case 'top':\n          percent = '0%';\n          break;\n\n        case 'right':\n        case 'bottom':\n          percent = '100%';\n          break;\n      }\n\n      if (isString(percent)) {\n        if (_trim(percent).match(/%$/)) {\n          return parseFloat(percent) / 100 * all;\n        }\n\n        return parseFloat(percent);\n      }\n\n      return percent == null ? NaN : +percent;\n    }\n    function round(x, precision, returnStr) {\n      if (precision == null) {\n        precision = 10;\n      } // Avoid range error\n\n\n      precision = Math.min(Math.max(0, precision), ROUND_SUPPORTED_PRECISION_MAX); // PENDING: 1.005.toFixed(2) is '1.00' rather than '1.01'\n\n      x = (+x).toFixed(precision);\n      return returnStr ? x : +x;\n    }\n    /**\n     * Inplacd asc sort arr.\n     * The input arr will be modified.\n     */\n\n    function asc(arr) {\n      arr.sort(function (a, b) {\n        return a - b;\n      });\n      return arr;\n    }\n    /**\n     * Get precision.\n     */\n\n    function getPrecision(val) {\n      val = +val;\n\n      if (isNaN(val)) {\n        return 0;\n      } // It is much faster than methods converting number to string as follows\n      //      let tmp = val.toString();\n      //      return tmp.length - 1 - tmp.indexOf('.');\n      // especially when precision is low\n      // Notice:\n      // (1) If the loop count is over about 20, it is slower than `getPrecisionSafe`.\n      //     (see https://jsbench.me/2vkpcekkvw/1)\n      // (2) If the val is less than for example 1e-15, the result may be incorrect.\n      //     (see test/ut/spec/util/number.test.ts `getPrecision_equal_random`)\n\n\n      if (val > 1e-14) {\n        var e = 1;\n\n        for (var i = 0; i < 15; i++, e *= 10) {\n          if (Math.round(val * e) / e === val) {\n            return i;\n          }\n        }\n      }\n\n      return getPrecisionSafe(val);\n    }\n    /**\n     * Get precision with slow but safe method\n     */\n\n    function getPrecisionSafe(val) {\n      // toLowerCase for: '3.4E-12'\n      var str = val.toString().toLowerCase(); // Consider scientific notation: '3.4e-12' '3.4e+12'\n\n      var eIndex = str.indexOf('e');\n      var exp = eIndex > 0 ? +str.slice(eIndex + 1) : 0;\n      var significandPartLen = eIndex > 0 ? eIndex : str.length;\n      var dotIndex = str.indexOf('.');\n      var decimalPartLen = dotIndex < 0 ? 0 : significandPartLen - 1 - dotIndex;\n      return Math.max(0, decimalPartLen - exp);\n    }\n    /**\n     * Minimal dicernible data precisioin according to a single pixel.\n     */\n\n    function getPixelPrecision(dataExtent, pixelExtent) {\n      var log = Math.log;\n      var LN10 = Math.LN10;\n      var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);\n      var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); // toFixed() digits argument must be between 0 and 20.\n\n      var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);\n      return !isFinite(precision) ? 20 : precision;\n    }\n    /**\n     * Get a data of given precision, assuring the sum of percentages\n     * in valueList is 1.\n     * The largest remainder method is used.\n     * https://en.wikipedia.org/wiki/Largest_remainder_method\n     *\n     * @param valueList a list of all data\n     * @param idx index of the data to be processed in valueList\n     * @param precision integer number showing digits of precision\n     * @return percent ranging from 0 to 100\n     */\n\n    function getPercentWithPrecision(valueList, idx, precision) {\n      if (!valueList[idx]) {\n        return 0;\n      }\n\n      var seats = getPercentSeats(valueList, precision);\n      return seats[idx] || 0;\n    }\n    /**\n     * Get a data of given precision, assuring the sum of percentages\n     * in valueList is 1.\n     * The largest remainder method is used.\n     * https://en.wikipedia.org/wiki/Largest_remainder_method\n     *\n     * @param valueList a list of all data\n     * @param precision integer number showing digits of precision\n     * @return {Array<number>}\n     */\n\n    function getPercentSeats(valueList, precision) {\n      var sum = reduce(valueList, function (acc, val) {\n        return acc + (isNaN(val) ? 0 : val);\n      }, 0);\n\n      if (sum === 0) {\n        return [];\n      }\n\n      var digits = Math.pow(10, precision);\n      var votesPerQuota = map(valueList, function (val) {\n        return (isNaN(val) ? 0 : val) / sum * digits * 100;\n      });\n      var targetSeats = digits * 100;\n      var seats = map(votesPerQuota, function (votes) {\n        // Assign automatic seats.\n        return Math.floor(votes);\n      });\n      var currentSum = reduce(seats, function (acc, val) {\n        return acc + val;\n      }, 0);\n      var remainder = map(votesPerQuota, function (votes, idx) {\n        return votes - seats[idx];\n      }); // Has remainding votes.\n\n      while (currentSum < targetSeats) {\n        // Find next largest remainder.\n        var max = Number.NEGATIVE_INFINITY;\n        var maxId = null;\n\n        for (var i = 0, len = remainder.length; i < len; ++i) {\n          if (remainder[i] > max) {\n            max = remainder[i];\n            maxId = i;\n          }\n        } // Add a vote to max remainder.\n\n\n        ++seats[maxId];\n        remainder[maxId] = 0;\n        ++currentSum;\n      }\n\n      return map(seats, function (seat) {\n        return seat / digits;\n      });\n    }\n    /**\n     * Solve the floating point adding problem like 0.1 + 0.2 === 0.30000000000000004\n     * See <http://0.30000000000000004.com/>\n     */\n\n    function addSafe(val0, val1) {\n      var maxPrecision = Math.max(getPrecision(val0), getPrecision(val1)); // const multiplier = Math.pow(10, maxPrecision);\n      // return (Math.round(val0 * multiplier) + Math.round(val1 * multiplier)) / multiplier;\n\n      var sum = val0 + val1; // // PENDING: support more?\n\n      return maxPrecision > ROUND_SUPPORTED_PRECISION_MAX ? sum : round(sum, maxPrecision);\n    } // Number.MAX_SAFE_INTEGER, ie do not support.\n\n    var MAX_SAFE_INTEGER = 9007199254740991;\n    /**\n     * To 0 - 2 * PI, considering negative radian.\n     */\n\n    function remRadian(radian) {\n      var pi2 = Math.PI * 2;\n      return (radian % pi2 + pi2) % pi2;\n    }\n    /**\n     * @param {type} radian\n     * @return {boolean}\n     */\n\n    function isRadianAroundZero(val) {\n      return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;\n    } // eslint-disable-next-line\n\n    var TIME_REG = /^(?:(\\d{4})(?:[-\\/](\\d{1,2})(?:[-\\/](\\d{1,2})(?:[T ](\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2})(?:[.,](\\d+))?)?)?(Z|[\\+\\-]\\d\\d:?\\d\\d)?)?)?)?)?$/; // jshint ignore:line\n\n    /**\n     * @param value valid type: number | string | Date, otherwise return `new Date(NaN)`\n     *   These values can be accepted:\n     *   + An instance of Date, represent a time in its own time zone.\n     *   + Or string in a subset of ISO 8601, only including:\n     *     + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',\n     *     + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',\n     *     + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',\n     *     all of which will be treated as local time if time zone is not specified\n     *     (see <https://momentjs.com/>).\n     *   + Or other string format, including (all of which will be treated as local time):\n     *     '2012', '2012-3-1', '2012/3/1', '2012/03/01',\n     *     '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'\n     *   + a timestamp, which represent a time in UTC.\n     * @return date Never be null/undefined. If invalid, return `new Date(NaN)`.\n     */\n\n    function parseDate(value) {\n      if (value instanceof Date) {\n        return value;\n      } else if (isString(value)) {\n        // Different browsers parse date in different way, so we parse it manually.\n        // Some other issues:\n        // new Date('1970-01-01') is UTC,\n        // new Date('1970/01/01') and new Date('1970-1-01') is local.\n        // See issue #3623\n        var match = TIME_REG.exec(value);\n\n        if (!match) {\n          // return Invalid Date.\n          return new Date(NaN);\n        } // Use local time when no timezone offset is specified.\n\n\n        if (!match[8]) {\n          // match[n] can only be string or undefined.\n          // But take care of '12' + 1 => '121'.\n          return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0);\n        } // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,\n        // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).\n        // For example, system timezone is set as \"Time Zone: America/Toronto\",\n        // then these code will get different result:\n        // `new Date(1478411999999).getTimezoneOffset();  // get 240`\n        // `new Date(1478412000000).getTimezoneOffset();  // get 300`\n        // So we should not use `new Date`, but use `Date.UTC`.\n        else {\n            var hour = +match[4] || 0;\n\n            if (match[8].toUpperCase() !== 'Z') {\n              hour -= +match[8].slice(0, 3);\n            }\n\n            return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0));\n          }\n      } else if (value == null) {\n        return new Date(NaN);\n      }\n\n      return new Date(Math.round(value));\n    }\n    /**\n     * Quantity of a number. e.g. 0.1, 1, 10, 100\n     *\n     * @param val\n     * @return\n     */\n\n    function quantity(val) {\n      return Math.pow(10, quantityExponent(val));\n    }\n    /**\n     * Exponent of the quantity of a number\n     * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3\n     *\n     * @param val non-negative value\n     * @return\n     */\n\n    function quantityExponent(val) {\n      if (val === 0) {\n        return 0;\n      }\n\n      var exp = Math.floor(Math.log(val) / Math.LN10);\n      /**\n       * exp is expected to be the rounded-down result of the base-10 log of val.\n       * But due to the precision loss with Math.log(val), we need to restore it\n       * using 10^exp to make sure we can get val back from exp. #11249\n       */\n\n      if (val / Math.pow(10, exp) >= 10) {\n        exp++;\n      }\n\n      return exp;\n    }\n    /**\n     * find a “nice” number approximately equal to x. Round the number if round = true,\n     * take ceiling if round = false. The primary observation is that the “nicest”\n     * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.\n     *\n     * See \"Nice Numbers for Graph Labels\" of Graphic Gems.\n     *\n     * @param  val Non-negative value.\n     * @param  round\n     * @return Niced number\n     */\n\n    function nice(val, round) {\n      var exponent = quantityExponent(val);\n      var exp10 = Math.pow(10, exponent);\n      var f = val / exp10; // 1 <= f < 10\n\n      var nf;\n\n      if (round) {\n        if (f < 1.5) {\n          nf = 1;\n        } else if (f < 2.5) {\n          nf = 2;\n        } else if (f < 4) {\n          nf = 3;\n        } else if (f < 7) {\n          nf = 5;\n        } else {\n          nf = 10;\n        }\n      } else {\n        if (f < 1) {\n          nf = 1;\n        } else if (f < 2) {\n          nf = 2;\n        } else if (f < 3) {\n          nf = 3;\n        } else if (f < 5) {\n          nf = 5;\n        } else {\n          nf = 10;\n        }\n      }\n\n      val = nf * exp10; // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).\n      // 20 is the uppper bound of toFixed.\n\n      return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;\n    }\n    /**\n     * This code was copied from \"d3.js\"\n     * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.\n     * See the license statement at the head of this file.\n     * @param ascArr\n     */\n\n    function quantile(ascArr, p) {\n      var H = (ascArr.length - 1) * p + 1;\n      var h = Math.floor(H);\n      var v = +ascArr[h - 1];\n      var e = H - h;\n      return e ? v + e * (ascArr[h] - v) : v;\n    }\n    /**\n     * Order intervals asc, and split them when overlap.\n     * expect(numberUtil.reformIntervals([\n     *     {interval: [18, 62], close: [1, 1]},\n     *     {interval: [-Infinity, -70], close: [0, 0]},\n     *     {interval: [-70, -26], close: [1, 1]},\n     *     {interval: [-26, 18], close: [1, 1]},\n     *     {interval: [62, 150], close: [1, 1]},\n     *     {interval: [106, 150], close: [1, 1]},\n     *     {interval: [150, Infinity], close: [0, 0]}\n     * ])).toEqual([\n     *     {interval: [-Infinity, -70], close: [0, 0]},\n     *     {interval: [-70, -26], close: [1, 1]},\n     *     {interval: [-26, 18], close: [0, 1]},\n     *     {interval: [18, 62], close: [0, 1]},\n     *     {interval: [62, 150], close: [0, 1]},\n     *     {interval: [150, Infinity], close: [0, 0]}\n     * ]);\n     * @param list, where `close` mean open or close\n     *        of the interval, and Infinity can be used.\n     * @return The origin list, which has been reformed.\n     */\n\n    function reformIntervals(list) {\n      list.sort(function (a, b) {\n        return littleThan(a, b, 0) ? -1 : 1;\n      });\n      var curr = -Infinity;\n      var currClose = 1;\n\n      for (var i = 0; i < list.length;) {\n        var interval = list[i].interval;\n        var close_1 = list[i].close;\n\n        for (var lg = 0; lg < 2; lg++) {\n          if (interval[lg] <= curr) {\n            interval[lg] = curr;\n            close_1[lg] = !lg ? 1 - currClose : 1;\n          }\n\n          curr = interval[lg];\n          currClose = close_1[lg];\n        }\n\n        if (interval[0] === interval[1] && close_1[0] * close_1[1] !== 1) {\n          list.splice(i, 1);\n        } else {\n          i++;\n        }\n      }\n\n      return list;\n\n      function littleThan(a, b, lg) {\n        return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1));\n      }\n    }\n    /**\n     * [Numeric is defined as]:\n     *     `parseFloat(val) == val`\n     * For example:\n     * numeric:\n     *     typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity,\n     *     and they rounded by white-spaces or line-terminal like ' -123 \\n ' (see es spec)\n     * not-numeric:\n     *     null, undefined, [], {}, true, false, 'NaN', NaN, '123ab',\n     *     empty string, string with only white-spaces or line-terminal (see es spec),\n     *     0x12, '0x12', '-0x12', 012, '012', '-012',\n     *     non-string, ...\n     *\n     * @test See full test cases in `test/ut/spec/util/number.js`.\n     * @return Must be a typeof number. If not numeric, return NaN.\n     */\n\n    function numericToNumber(val) {\n      var valFloat = parseFloat(val);\n      return valFloat == val // eslint-disable-line eqeqeq\n      && (valFloat !== 0 || !isString(val) || val.indexOf('x') <= 0) // For case ' 0x0 '.\n      ? valFloat : NaN;\n    }\n    /**\n     * Definition of \"numeric\": see `numericToNumber`.\n     */\n\n    function isNumeric(val) {\n      return !isNaN(numericToNumber(val));\n    }\n    /**\n     * Use random base to prevent users hard code depending on\n     * this auto generated marker id.\n     * @return An positive integer.\n     */\n\n    function getRandomIdBase() {\n      return Math.round(Math.random() * 9);\n    }\n    /**\n     * Get the greatest common divisor.\n     *\n     * @param {number} a one number\n     * @param {number} b the other number\n     */\n\n    function getGreatestCommonDividor(a, b) {\n      if (b === 0) {\n        return a;\n      }\n\n      return getGreatestCommonDividor(b, a % b);\n    }\n    /**\n     * Get the least common multiple.\n     *\n     * @param {number} a one number\n     * @param {number} b the other number\n     */\n\n    function getLeastCommonMultiple(a, b) {\n      if (a == null) {\n        return b;\n      }\n\n      if (b == null) {\n        return a;\n      }\n\n      return a * b / getGreatestCommonDividor(a, b);\n    }\n\n    var ECHARTS_PREFIX = '[ECharts] ';\n    var storedLogs = {};\n    var hasConsole = typeof console !== 'undefined' // eslint-disable-next-line\n    && console.warn && console.log;\n\n    function outputLog(type, str, onlyOnce) {\n      if (hasConsole) {\n        if (onlyOnce) {\n          if (storedLogs[str]) {\n            return;\n          }\n\n          storedLogs[str] = true;\n        } // eslint-disable-next-line\n\n\n        console[type](ECHARTS_PREFIX + str);\n      }\n    }\n\n    function log(str, onlyOnce) {\n      outputLog('log', str, onlyOnce);\n    }\n    function warn(str, onlyOnce) {\n      outputLog('warn', str, onlyOnce);\n    }\n    function error(str, onlyOnce) {\n      outputLog('error', str, onlyOnce);\n    }\n    function deprecateLog(str) {\n      if (\"development\" !== 'production') {\n        // Not display duplicate message.\n        outputLog('warn', 'DEPRECATED: ' + str, true);\n      }\n    }\n    function deprecateReplaceLog(oldOpt, newOpt, scope) {\n      if (\"development\" !== 'production') {\n        deprecateLog((scope ? \"[\" + scope + \"]\" : '') + (oldOpt + \" is deprecated, use \" + newOpt + \" instead.\"));\n      }\n    }\n    /**\n     * If in __DEV__ environment, get console printable message for users hint.\n     * Parameters are separated by ' '.\n     * @usage\n     * makePrintable('This is an error on', someVar, someObj);\n     *\n     * @param hintInfo anything about the current execution context to hint users.\n     * @throws Error\n     */\n\n    function makePrintable() {\n      var hintInfo = [];\n\n      for (var _i = 0; _i < arguments.length; _i++) {\n        hintInfo[_i] = arguments[_i];\n      }\n\n      var msg = '';\n\n      if (\"development\" !== 'production') {\n        // Fuzzy stringify for print.\n        // This code only exist in dev environment.\n        var makePrintableStringIfPossible_1 = function (val) {\n          return val === void 0 ? 'undefined' : val === Infinity ? 'Infinity' : val === -Infinity ? '-Infinity' : eqNaN(val) ? 'NaN' : val instanceof Date ? 'Date(' + val.toISOString() + ')' : isFunction(val) ? 'function () { ... }' : isRegExp(val) ? val + '' : null;\n        };\n\n        msg = map(hintInfo, function (arg) {\n          if (isString(arg)) {\n            // Print without quotation mark for some statement.\n            return arg;\n          } else {\n            var printableStr = makePrintableStringIfPossible_1(arg);\n\n            if (printableStr != null) {\n              return printableStr;\n            } else if (typeof JSON !== 'undefined' && JSON.stringify) {\n              try {\n                return JSON.stringify(arg, function (n, val) {\n                  var printableStr = makePrintableStringIfPossible_1(val);\n                  return printableStr == null ? val : printableStr;\n                }); // In most cases the info object is small, so do not line break.\n              } catch (err) {\n                return '?';\n              }\n            } else {\n              return '?';\n            }\n          }\n        }).join(' ');\n      }\n\n      return msg;\n    }\n    /**\n     * @throws Error\n     */\n\n    function throwError(msg) {\n      throw new Error(msg);\n    }\n\n    function interpolateNumber$1(p0, p1, percent) {\n      return (p1 - p0) * percent + p0;\n    }\n    /**\n     * Make the name displayable. But we should\n     * make sure it is not duplicated with user\n     * specified name, so use '\\0';\n     */\n\n\n    var DUMMY_COMPONENT_NAME_PREFIX = 'series\\0';\n    var INTERNAL_COMPONENT_ID_PREFIX = '\\0_ec_\\0';\n    /**\n     * If value is not array, then translate it to array.\n     * @param  {*} value\n     * @return {Array} [value] or value\n     */\n\n    function normalizeToArray(value) {\n      return value instanceof Array ? value : value == null ? [] : [value];\n    }\n    /**\n     * Sync default option between normal and emphasis like `position` and `show`\n     * In case some one will write code like\n     *     label: {\n     *          show: false,\n     *          position: 'outside',\n     *          fontSize: 18\n     *     },\n     *     emphasis: {\n     *          label: { show: true }\n     *     }\n     */\n\n    function defaultEmphasis(opt, key, subOpts) {\n      // Caution: performance sensitive.\n      if (opt) {\n        opt[key] = opt[key] || {};\n        opt.emphasis = opt.emphasis || {};\n        opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal\n\n        for (var i = 0, len = subOpts.length; i < len; i++) {\n          var subOptName = subOpts[i];\n\n          if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) {\n            opt.emphasis[key][subOptName] = opt[key][subOptName];\n          }\n        }\n      }\n    }\n    var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([\n    //     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',\n    //     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',\n    //     // FIXME: deprecated, check and remove it.\n    //     'textStyle'\n    // ]);\n\n    /**\n     * The method does not ensure performance.\n     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n     * This helper method retrieves value from data.\n     */\n\n    function getDataItemValue(dataItem) {\n      return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;\n    }\n    /**\n     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n     * This helper method determine if dataItem has extra option besides value\n     */\n\n    function isDataItemOption(dataItem) {\n      return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array\n      // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));\n    }\n    /**\n     * Mapping to existings for merge.\n     *\n     * Mode \"normalMege\":\n     *     The mapping result (merge result) will keep the order of the existing\n     *     component, rather than the order of new option. Because we should ensure\n     *     some specified index reference (like xAxisIndex) keep work.\n     *     And in most cases, \"merge option\" is used to update partial option but not\n     *     be expected to change the order.\n     *\n     * Mode \"replaceMege\":\n     *     (1) Only the id mapped components will be merged.\n     *     (2) Other existing components (except internal components) will be removed.\n     *     (3) Other new options will be used to create new component.\n     *     (4) The index of the existing components will not be modified.\n     *     That means their might be \"hole\" after the removal.\n     *     The new components are created first at those available index.\n     *\n     * Mode \"replaceAll\":\n     *     This mode try to support that reproduce an echarts instance from another\n     *     echarts instance (via `getOption`) in some simple cases.\n     *     In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`,\n     *     which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is,\n     *     the \"hole\" in `newCmptOptions` will also be kept.\n     *     On the contrary, other modes try best to eliminate holes.\n     *     PENDING: This is an experimental mode yet.\n     *\n     * @return See the comment of <MappingResult>.\n     */\n\n    function mappingToExists(existings, newCmptOptions, mode) {\n      var isNormalMergeMode = mode === 'normalMerge';\n      var isReplaceMergeMode = mode === 'replaceMerge';\n      var isReplaceAllMode = mode === 'replaceAll';\n      existings = existings || [];\n      newCmptOptions = (newCmptOptions || []).slice();\n      var existingIdIdxMap = createHashMap(); // Validate id and name on user input option.\n\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!isObject(cmptOption)) {\n          newCmptOptions[index] = null;\n          return;\n        }\n\n        if (\"development\" !== 'production') {\n          // There is some legacy case that name is set as `false`.\n          // But should work normally rather than throw error.\n          if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) {\n            warnInvalidateIdOrName(cmptOption.id);\n          }\n\n          if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) {\n            warnInvalidateIdOrName(cmptOption.name);\n          }\n        }\n      });\n      var result = prepareResult(existings, existingIdIdxMap, mode);\n\n      if (isNormalMergeMode || isReplaceMergeMode) {\n        mappingById(result, existings, existingIdIdxMap, newCmptOptions);\n      }\n\n      if (isNormalMergeMode) {\n        mappingByName(result, newCmptOptions);\n      }\n\n      if (isNormalMergeMode || isReplaceMergeMode) {\n        mappingByIndex(result, newCmptOptions, isReplaceMergeMode);\n      } else if (isReplaceAllMode) {\n        mappingInReplaceAllMode(result, newCmptOptions);\n      }\n\n      makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the\n      // forEach will omit those items and result in incorrect result.\n\n      return result;\n    }\n\n    function prepareResult(existings, existingIdIdxMap, mode) {\n      var result = [];\n\n      if (mode === 'replaceAll') {\n        return result;\n      } // Do not use native `map` to in case that the array `existings`\n      // contains elided items, which will be omitted.\n\n\n      for (var index = 0; index < existings.length; index++) {\n        var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined.\n\n        if (existing && existing.id != null) {\n          existingIdIdxMap.set(existing.id, index);\n        } // For non-internal-componnets:\n        //     Mode \"normalMerge\": all existings kept.\n        //     Mode \"replaceMerge\": all existing removed unless mapped by id.\n        // For internal-components:\n        //     go with \"replaceMerge\" approach in both mode.\n\n\n        result.push({\n          existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing,\n          newOption: null,\n          keyInfo: null,\n          brandNew: null\n        });\n      }\n\n      return result;\n    }\n\n    function mappingById(result, existings, existingIdIdxMap, newCmptOptions) {\n      // Mapping by id if specified.\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!cmptOption || cmptOption.id == null) {\n          return;\n        }\n\n        var optionId = makeComparableKey(cmptOption.id);\n        var existingIdx = existingIdIdxMap.get(optionId);\n\n        if (existingIdx != null) {\n          var resultItem = result[existingIdx];\n          assert(!resultItem.newOption, 'Duplicated option on id \"' + optionId + '\".');\n          resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to\n          // the existings rather than creating new component model.\n\n          resultItem.existing = existings[existingIdx];\n          newCmptOptions[index] = null;\n        }\n      });\n    }\n\n    function mappingByName(result, newCmptOptions) {\n      // Mapping by name if specified.\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!cmptOption || cmptOption.name == null) {\n          return;\n        }\n\n        for (var i = 0; i < result.length; i++) {\n          var existing = result[i].existing;\n\n          if (!result[i].newOption // Consider name: two map to one.\n          // Can not match when both ids existing but different.\n          && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {\n            result[i].newOption = cmptOption;\n            newCmptOptions[index] = null;\n            return;\n          }\n        }\n      });\n    }\n\n    function mappingByIndex(result, newCmptOptions, brandNew) {\n      each(newCmptOptions, function (cmptOption) {\n        if (!cmptOption) {\n          return;\n        } // Find the first place that not mapped by id and not internal component (consider the \"hole\").\n\n\n        var resultItem;\n        var nextIdx = 0;\n\n        while ( // Be `!resultItem` only when `nextIdx >= result.length`.\n        (resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because\n        // after mapping performed, model will always be assigned with an id if user not given.\n        // After that all models have id.\n        // (2) If new option has id, it can only set to a hole or append to the last. It should\n        // not be merged to the existings with different id. Because id should not be overwritten.\n        // (3) Name can be overwritten, because axis use name as 'show label text'.\n        resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode \"replaceMerge\", here no not-mapped-non-internal-existing.\n        resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {\n          nextIdx++;\n        }\n\n        if (resultItem) {\n          resultItem.newOption = cmptOption;\n          resultItem.brandNew = brandNew;\n        } else {\n          result.push({\n            newOption: cmptOption,\n            brandNew: brandNew,\n            existing: null,\n            keyInfo: null\n          });\n        }\n\n        nextIdx++;\n      });\n    }\n\n    function mappingInReplaceAllMode(result, newCmptOptions) {\n      each(newCmptOptions, function (cmptOption) {\n        // The feature \"reproduce\" requires \"hole\" will also reproduced\n        // in case that component index referring are broken.\n        result.push({\n          newOption: cmptOption,\n          brandNew: true,\n          existing: null,\n          keyInfo: null\n        });\n      });\n    }\n    /**\n     * Make id and name for mapping result (result of mappingToExists)\n     * into `keyInfo` field.\n     */\n\n\n    function makeIdAndName(mapResult) {\n      // We use this id to hash component models and view instances\n      // in echarts. id can be specified by user, or auto generated.\n      // The id generation rule ensures new view instance are able\n      // to mapped to old instance when setOption are called in\n      // no-merge mode. So we generate model id by name and plus\n      // type in view id.\n      // name can be duplicated among components, which is convenient\n      // to specify multi components (like series) by one name.\n      // Ensure that each id is distinct.\n      var idMap = createHashMap();\n      each(mapResult, function (item) {\n        var existing = item.existing;\n        existing && idMap.set(existing.id, item);\n      });\n      each(mapResult, function (item) {\n        var opt = item.newOption; // Force ensure id not duplicated.\n\n        assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));\n        opt && opt.id != null && idMap.set(opt.id, item);\n        !item.keyInfo && (item.keyInfo = {});\n      }); // Make name and id.\n\n      each(mapResult, function (item, index) {\n        var existing = item.existing;\n        var opt = item.newOption;\n        var keyInfo = item.keyInfo;\n\n        if (!isObject(opt)) {\n          return;\n        } // Name can be overwritten. Consider case: axis.name = '20km'.\n        // But id generated by name will not be changed, which affect\n        // only in that case: setOption with 'not merge mode' and view\n        // instance will be recreated, which can be accepted.\n\n\n        keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid that different series has the same name,\n        // because name may be used like in color pallet.\n        : DUMMY_COMPONENT_NAME_PREFIX + index;\n\n        if (existing) {\n          keyInfo.id = makeComparableKey(existing.id);\n        } else if (opt.id != null) {\n          keyInfo.id = makeComparableKey(opt.id);\n        } else {\n          // Consider this situatoin:\n          //  optionA: [{name: 'a'}, {name: 'a'}, {..}]\n          //  optionB [{..}, {name: 'a'}, {name: 'a'}]\n          // Series with the same name between optionA and optionB\n          // should be mapped.\n          var idNum = 0;\n\n          do {\n            keyInfo.id = '\\0' + keyInfo.name + '\\0' + idNum++;\n          } while (idMap.get(keyInfo.id));\n        }\n\n        idMap.set(keyInfo.id, item);\n      });\n    }\n\n    function keyExistAndEqual(attr, obj1, obj2) {\n      var key1 = convertOptionIdName(obj1[attr], null);\n      var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number.\n\n      return key1 != null && key2 != null && key1 === key2;\n    }\n    /**\n     * @return return null if not exist.\n     */\n\n\n    function makeComparableKey(val) {\n      if (\"development\" !== 'production') {\n        if (val == null) {\n          throw new Error();\n        }\n      }\n\n      return convertOptionIdName(val, '');\n    }\n\n    function convertOptionIdName(idOrName, defaultValue) {\n      if (idOrName == null) {\n        return defaultValue;\n      }\n\n      return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue;\n    }\n\n    function warnInvalidateIdOrName(idOrName) {\n      if (\"development\" !== 'production') {\n        warn('`' + idOrName + '` is invalid id or name. Must be a string or number.');\n      }\n    }\n\n    function isValidIdOrName(idOrName) {\n      return isStringSafe(idOrName) || isNumeric(idOrName);\n    }\n\n    function isNameSpecified(componentModel) {\n      var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.\n\n      return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));\n    }\n    /**\n     * @public\n     * @param {Object} cmptOption\n     * @return {boolean}\n     */\n\n    function isComponentIdInternal(cmptOption) {\n      return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;\n    }\n    function makeInternalComponentId(idSuffix) {\n      return INTERNAL_COMPONENT_ID_PREFIX + idSuffix;\n    }\n    function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) {\n      // Set mainType and complete subType.\n      each(mappingResult, function (item) {\n        var newOption = item.newOption;\n\n        if (isObject(newOption)) {\n          item.keyInfo.mainType = mainType;\n          item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor);\n        }\n      });\n    }\n\n    function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) {\n      var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.\n      : componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType\n\n      return subType;\n    }\n    /**\n     * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name\n     *                         each of which can be Array or primary type.\n     * @return dataIndex If not found, return undefined/null.\n     */\n\n    function queryDataIndex(data, payload) {\n      if (payload.dataIndexInside != null) {\n        return payload.dataIndexInside;\n      } else if (payload.dataIndex != null) {\n        return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) {\n          return data.indexOfRawIndex(value);\n        }) : data.indexOfRawIndex(payload.dataIndex);\n      } else if (payload.name != null) {\n        return isArray(payload.name) ? map(payload.name, function (value) {\n          return data.indexOfName(value);\n        }) : data.indexOfName(payload.name);\n      }\n    }\n    /**\n     * Enable property storage to any host object.\n     * Notice: Serialization is not supported.\n     *\n     * For example:\n     * let inner = zrUitl.makeInner();\n     *\n     * function some1(hostObj) {\n     *      inner(hostObj).someProperty = 1212;\n     *      ...\n     * }\n     * function some2() {\n     *      let fields = inner(this);\n     *      fields.someProperty1 = 1212;\n     *      fields.someProperty2 = 'xx';\n     *      ...\n     * }\n     *\n     * @return {Function}\n     */\n\n    function makeInner() {\n      var key = '__ec_inner_' + innerUniqueIndex++;\n      return function (hostObj) {\n        return hostObj[key] || (hostObj[key] = {});\n      };\n    }\n    var innerUniqueIndex = getRandomIdBase();\n    /**\n     * The same behavior as `component.getReferringComponents`.\n     */\n\n    function parseFinder(ecModel, finderInput, opt) {\n      var _a = preParseFinder(finderInput, opt),\n          mainTypeSpecified = _a.mainTypeSpecified,\n          queryOptionMap = _a.queryOptionMap,\n          others = _a.others;\n\n      var result = others;\n      var defaultMainType = opt ? opt.defaultMainType : null;\n\n      if (!mainTypeSpecified && defaultMainType) {\n        queryOptionMap.set(defaultMainType, {});\n      }\n\n      queryOptionMap.each(function (queryOption, mainType) {\n        var queryResult = queryReferringComponents(ecModel, mainType, queryOption, {\n          useDefault: defaultMainType === mainType,\n          enableAll: opt && opt.enableAll != null ? opt.enableAll : true,\n          enableNone: opt && opt.enableNone != null ? opt.enableNone : true\n        });\n        result[mainType + 'Models'] = queryResult.models;\n        result[mainType + 'Model'] = queryResult.models[0];\n      });\n      return result;\n    }\n    function preParseFinder(finderInput, opt) {\n      var finder;\n\n      if (isString(finderInput)) {\n        var obj = {};\n        obj[finderInput + 'Index'] = 0;\n        finder = obj;\n      } else {\n        finder = finderInput;\n      }\n\n      var queryOptionMap = createHashMap();\n      var others = {};\n      var mainTypeSpecified = false;\n      each(finder, function (value, key) {\n        // Exclude 'dataIndex' and other illgal keys.\n        if (key === 'dataIndex' || key === 'dataIndexInside') {\n          others[key] = value;\n          return;\n        }\n\n        var parsedKey = key.match(/^(\\w+)(Index|Id|Name)$/) || [];\n        var mainType = parsedKey[1];\n        var queryType = (parsedKey[2] || '').toLowerCase();\n\n        if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) {\n          return;\n        }\n\n        mainTypeSpecified = mainTypeSpecified || !!mainType;\n        var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {});\n        queryOption[queryType] = value;\n      });\n      return {\n        mainTypeSpecified: mainTypeSpecified,\n        queryOptionMap: queryOptionMap,\n        others: others\n      };\n    }\n    var SINGLE_REFERRING = {\n      useDefault: true,\n      enableAll: false,\n      enableNone: false\n    };\n    var MULTIPLE_REFERRING = {\n      useDefault: false,\n      enableAll: true,\n      enableNone: true\n    };\n    function queryReferringComponents(ecModel, mainType, userOption, opt) {\n      opt = opt || SINGLE_REFERRING;\n      var indexOption = userOption.index;\n      var idOption = userOption.id;\n      var nameOption = userOption.name;\n      var result = {\n        models: null,\n        specified: indexOption != null || idOption != null || nameOption != null\n      };\n\n      if (!result.specified) {\n        // Use the first as default if `useDefault`.\n        var firstCmpt = void 0;\n        result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : [];\n        return result;\n      }\n\n      if (indexOption === 'none' || indexOption === false) {\n        assert(opt.enableNone, '`\"none\"` or `false` is not a valid value on index option.');\n        result.models = [];\n        return result;\n      } // `queryComponents` will return all components if\n      // both all of index/id/name are null/undefined.\n\n\n      if (indexOption === 'all') {\n        assert(opt.enableAll, '`\"all\"` is not a valid value on index option.');\n        indexOption = idOption = nameOption = null;\n      }\n\n      result.models = ecModel.queryComponents({\n        mainType: mainType,\n        index: indexOption,\n        id: idOption,\n        name: nameOption\n      });\n      return result;\n    }\n    function setAttribute(dom, key, value) {\n      dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value;\n    }\n    function getAttribute(dom, key) {\n      return dom.getAttribute ? dom.getAttribute(key) : dom[key];\n    }\n    function getTooltipRenderMode(renderModeOption) {\n      if (renderModeOption === 'auto') {\n        // Using html when `document` exists, use richText otherwise\n        return env.domSupported ? 'html' : 'richText';\n      } else {\n        return renderModeOption || 'html';\n      }\n    }\n    /**\n     * Interpolate raw values of a series with percent\n     *\n     * @param data         data\n     * @param labelModel   label model of the text element\n     * @param sourceValue  start value. May be null/undefined when init.\n     * @param targetValue  end value\n     * @param percent      0~1 percentage; 0 uses start value while 1 uses end value\n     * @return             interpolated values\n     *                     If `sourceValue` and `targetValue` are `number`, return `number`.\n     *                     If `sourceValue` and `targetValue` are `string`, return `string`.\n     *                     If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`.\n     *                     Other cases do not supported.\n     */\n\n    function interpolateRawValues(data, precision, sourceValue, targetValue, percent) {\n      var isAutoPrecision = precision == null || precision === 'auto';\n\n      if (targetValue == null) {\n        return targetValue;\n      }\n\n      if (isNumber(targetValue)) {\n        var value = interpolateNumber$1(sourceValue || 0, targetValue, percent);\n        return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision);\n      } else if (isString(targetValue)) {\n        return percent < 1 ? sourceValue : targetValue;\n      } else {\n        var interpolated = [];\n        var leftArr = sourceValue;\n        var rightArr = targetValue;\n        var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length);\n\n        for (var i = 0; i < length_1; ++i) {\n          var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims\n\n          if (info && info.type === 'ordinal') {\n            // In init, there is no `sourceValue`, but should better not to get undefined result.\n            interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i];\n          } else {\n            var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0;\n            var rightVal = rightArr[i];\n            var value = interpolateNumber$1(leftVal, rightVal, percent);\n            interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision);\n          }\n        }\n\n        return interpolated;\n      }\n    }\n\n    var TYPE_DELIMITER = '.';\n    var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';\n    var IS_EXTENDED_CLASS = '___EC__EXTENDED_CLASS___';\n    /**\n     * Notice, parseClassType('') should returns {main: '', sub: ''}\n     * @public\n     */\n\n    function parseClassType(componentType) {\n      var ret = {\n        main: '',\n        sub: ''\n      };\n\n      if (componentType) {\n        var typeArr = componentType.split(TYPE_DELIMITER);\n        ret.main = typeArr[0] || '';\n        ret.sub = typeArr[1] || '';\n      }\n\n      return ret;\n    }\n    /**\n     * @public\n     */\n\n    function checkClassType(componentType) {\n      assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType \"' + componentType + '\" illegal');\n    }\n\n    function isExtendedClass(clz) {\n      return !!(clz && clz[IS_EXTENDED_CLASS]);\n    }\n    /**\n     * Implements `ExtendableConstructor` for `rootClz`.\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & ExtendableConstructor\n     * enableClassExtend(Xxx as XxxConstructor);\n     * ```\n     */\n\n    function enableClassExtend(rootClz, mandatoryMethods) {\n      rootClz.$constructor = rootClz; // FIXME: not necessary?\n\n      rootClz.extend = function (proto) {\n        if (\"development\" !== 'production') {\n          each(mandatoryMethods, function (method) {\n            if (!proto[method]) {\n              console.warn('Method `' + method + '` should be implemented' + (proto.type ? ' in ' + proto.type : '') + '.');\n            }\n          });\n        }\n\n        var superClass = this;\n        var ExtendedClass;\n\n        if (isESClass(superClass)) {\n          ExtendedClass =\n          /** @class */\n          function (_super) {\n            __extends(class_1, _super);\n\n            function class_1() {\n              return _super.apply(this, arguments) || this;\n            }\n\n            return class_1;\n          }(superClass);\n        } else {\n          // For backward compat, we both support ts class inheritance and this\n          // \"extend\" approach.\n          // The constructor should keep the same behavior as ts class inheritance:\n          // If this constructor/$constructor is not declared, auto invoke the super\n          // constructor.\n          // If this constructor/$constructor is declared, it is responsible for\n          // calling the super constructor.\n          ExtendedClass = function () {\n            (proto.$constructor || superClass).apply(this, arguments);\n          };\n\n          inherits(ExtendedClass, this);\n        }\n\n        extend(ExtendedClass.prototype, proto);\n        ExtendedClass[IS_EXTENDED_CLASS] = true;\n        ExtendedClass.extend = this.extend;\n        ExtendedClass.superCall = superCall;\n        ExtendedClass.superApply = superApply;\n        ExtendedClass.superClass = superClass;\n        return ExtendedClass;\n      };\n    }\n\n    function isESClass(fn) {\n      return isFunction(fn) && /^class\\s/.test(Function.prototype.toString.call(fn));\n    }\n    /**\n     * A work around to both support ts extend and this extend mechanism.\n     * on sub-class.\n     * @usage\n     * ```ts\n     * class Component { ... }\n     * classUtil.enableClassExtend(Component);\n     * classUtil.enableClassManagement(Component, {registerWhenExtend: true});\n     *\n     * class Series extends Component { ... }\n     * // Without calling `markExtend`, `registerWhenExtend` will not work.\n     * Component.markExtend(Series);\n     * ```\n     */\n\n\n    function mountExtend(SubClz, SupperClz) {\n      SubClz.extend = SupperClz.extend;\n    } // A random offset.\n\n    var classBase = Math.round(Math.random() * 10);\n    /**\n     * Implements `CheckableConstructor` for `target`.\n     * Can not use instanceof, consider different scope by\n     * cross domain or es module import in ec extensions.\n     * Mount a method \"isInstance()\" to Clz.\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & CheckableConstructor;\n     * enableClassCheck(Xxx as XxxConstructor)\n     * ```\n     */\n\n    function enableClassCheck(target) {\n      var classAttr = ['__\\0is_clz', classBase++].join('_');\n      target.prototype[classAttr] = true;\n\n      if (\"development\" !== 'production') {\n        assert(!target.isInstance, 'The method \"is\" can not be defined.');\n      }\n\n      target.isInstance = function (obj) {\n        return !!(obj && obj[classAttr]);\n      };\n    } // superCall should have class info, which can not be fetched from 'this'.\n    // Consider this case:\n    // class A has method f,\n    // class B inherits class A, overrides method f, f call superApply('f'),\n    // class C inherits class B, does not override method f,\n    // then when method of class C is called, dead loop occurred.\n\n    function superCall(context, methodName) {\n      var args = [];\n\n      for (var _i = 2; _i < arguments.length; _i++) {\n        args[_i - 2] = arguments[_i];\n      }\n\n      return this.superClass.prototype[methodName].apply(context, args);\n    }\n\n    function superApply(context, methodName, args) {\n      return this.superClass.prototype[methodName].apply(context, args);\n    }\n    /**\n     * Implements `ClassManager` for `target`\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & ClassManager\n     * enableClassManagement(Xxx as XxxConstructor);\n     * ```\n     */\n\n\n    function enableClassManagement(target) {\n      /**\n       * Component model classes\n       * key: componentType,\n       * value:\n       *     componentClass, when componentType is 'a'\n       *     or Object.<subKey, componentClass>, when componentType is 'a.b'\n       */\n      var storage = {};\n\n      target.registerClass = function (clz) {\n        // `type` should not be a \"instance member\".\n        // If using TS class, should better declared as `static type = 'series.pie'`.\n        // otherwise users have to mount `type` on prototype manually.\n        // For backward compat and enable instance visit type via `this.type`,\n        // we still support fetch `type` from prototype.\n        var componentFullType = clz.type || clz.prototype.type;\n\n        if (componentFullType) {\n          checkClassType(componentFullType); // If only static type declared, we assign it to prototype mandatorily.\n\n          clz.prototype.type = componentFullType;\n          var componentTypeInfo = parseClassType(componentFullType);\n\n          if (!componentTypeInfo.sub) {\n            if (\"development\" !== 'production') {\n              if (storage[componentTypeInfo.main]) {\n                console.warn(componentTypeInfo.main + ' exists.');\n              }\n            }\n\n            storage[componentTypeInfo.main] = clz;\n          } else if (componentTypeInfo.sub !== IS_CONTAINER) {\n            var container = makeContainer(componentTypeInfo);\n            container[componentTypeInfo.sub] = clz;\n          }\n        }\n\n        return clz;\n      };\n\n      target.getClass = function (mainType, subType, throwWhenNotFound) {\n        var clz = storage[mainType];\n\n        if (clz && clz[IS_CONTAINER]) {\n          clz = subType ? clz[subType] : null;\n        }\n\n        if (throwWhenNotFound && !clz) {\n          throw new Error(!subType ? mainType + '.' + 'type should be specified.' : 'Component ' + mainType + '.' + (subType || '') + ' is used but not imported.');\n        }\n\n        return clz;\n      };\n\n      target.getClassesByMainType = function (componentType) {\n        var componentTypeInfo = parseClassType(componentType);\n        var result = [];\n        var obj = storage[componentTypeInfo.main];\n\n        if (obj && obj[IS_CONTAINER]) {\n          each(obj, function (o, type) {\n            type !== IS_CONTAINER && result.push(o);\n          });\n        } else {\n          result.push(obj);\n        }\n\n        return result;\n      };\n\n      target.hasClass = function (componentType) {\n        // Just consider componentType.main.\n        var componentTypeInfo = parseClassType(componentType);\n        return !!storage[componentTypeInfo.main];\n      };\n      /**\n       * @return Like ['aa', 'bb'], but can not be ['aa.xx']\n       */\n\n\n      target.getAllClassMainTypes = function () {\n        var types = [];\n        each(storage, function (obj, type) {\n          types.push(type);\n        });\n        return types;\n      };\n      /**\n       * If a main type is container and has sub types\n       */\n\n\n      target.hasSubTypes = function (componentType) {\n        var componentTypeInfo = parseClassType(componentType);\n        var obj = storage[componentTypeInfo.main];\n        return obj && obj[IS_CONTAINER];\n      };\n\n      function makeContainer(componentTypeInfo) {\n        var container = storage[componentTypeInfo.main];\n\n        if (!container || !container[IS_CONTAINER]) {\n          container = storage[componentTypeInfo.main] = {};\n          container[IS_CONTAINER] = true;\n        }\n\n        return container;\n      }\n    } // /**\n    //  * @param {string|Array.<string>} properties\n    //  */\n    // export function setReadOnly(obj, properties) {\n    // FIXME It seems broken in IE8 simulation of IE11\n    // if (!zrUtil.isArray(properties)) {\n    //     properties = properties != null ? [properties] : [];\n    // }\n    // zrUtil.each(properties, function (prop) {\n    //     let value = obj[prop];\n    //     Object.defineProperty\n    //         && Object.defineProperty(obj, prop, {\n    //             value: value, writable: false\n    //         });\n    //     zrUtil.isArray(obj[prop])\n    //         && Object.freeze\n    //         && Object.freeze(obj[prop]);\n    // });\n    // }\n\n    function makeStyleMapper(properties, ignoreParent) {\n      // Normalize\n      for (var i = 0; i < properties.length; i++) {\n        if (!properties[i][1]) {\n          properties[i][1] = properties[i][0];\n        }\n      }\n\n      ignoreParent = ignoreParent || false;\n      return function (model, excludes, includes) {\n        var style = {};\n\n        for (var i = 0; i < properties.length; i++) {\n          var propName = properties[i][1];\n\n          if (excludes && indexOf(excludes, propName) >= 0 || includes && indexOf(includes, propName) < 0) {\n            continue;\n          }\n\n          var val = model.getShallow(propName, ignoreParent);\n\n          if (val != null) {\n            style[properties[i][0]] = val;\n          }\n        } // TODO Text or image?\n\n\n        return style;\n      };\n    }\n\n    var AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getAreaStyle = makeStyleMapper(AREA_STYLE_KEY_MAP);\n\n    var AreaStyleMixin =\n    /** @class */\n    function () {\n      function AreaStyleMixin() {}\n\n      AreaStyleMixin.prototype.getAreaStyle = function (excludes, includes) {\n        return getAreaStyle(this, excludes, includes);\n      };\n\n      return AreaStyleMixin;\n    }();\n\n    var globalImageCache = new LRU(50);\n    function findExistImage(newImageOrSrc) {\n        if (typeof newImageOrSrc === 'string') {\n            var cachedImgObj = globalImageCache.get(newImageOrSrc);\n            return cachedImgObj && cachedImgObj.image;\n        }\n        else {\n            return newImageOrSrc;\n        }\n    }\n    function createOrUpdateImage(newImageOrSrc, image, hostEl, onload, cbPayload) {\n        if (!newImageOrSrc) {\n            return image;\n        }\n        else if (typeof newImageOrSrc === 'string') {\n            if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {\n                return image;\n            }\n            var cachedImgObj = globalImageCache.get(newImageOrSrc);\n            var pendingWrap = { hostEl: hostEl, cb: onload, cbPayload: cbPayload };\n            if (cachedImgObj) {\n                image = cachedImgObj.image;\n                !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);\n            }\n            else {\n                image = platformApi.loadImage(newImageOrSrc, imageOnLoad, imageOnLoad);\n                image.__zrImageSrc = newImageOrSrc;\n                globalImageCache.put(newImageOrSrc, image.__cachedImgObj = {\n                    image: image,\n                    pending: [pendingWrap]\n                });\n            }\n            return image;\n        }\n        else {\n            return newImageOrSrc;\n        }\n    }\n    function imageOnLoad() {\n        var cachedImgObj = this.__cachedImgObj;\n        this.onload = this.onerror = this.__cachedImgObj = null;\n        for (var i = 0; i < cachedImgObj.pending.length; i++) {\n            var pendingWrap = cachedImgObj.pending[i];\n            var cb = pendingWrap.cb;\n            cb && cb(this, pendingWrap.cbPayload);\n            pendingWrap.hostEl.dirty();\n        }\n        cachedImgObj.pending.length = 0;\n    }\n    function isImageReady(image) {\n        return image && image.width && image.height;\n    }\n\n    var STYLE_REG = /\\{([a-zA-Z0-9_]+)\\|([^}]*)\\}/g;\n    function truncateText(text, containerWidth, font, ellipsis, options) {\n        if (!containerWidth) {\n            return '';\n        }\n        var textLines = (text + '').split('\\n');\n        options = prepareTruncateOptions(containerWidth, font, ellipsis, options);\n        for (var i = 0, len = textLines.length; i < len; i++) {\n            textLines[i] = truncateSingleLine(textLines[i], options);\n        }\n        return textLines.join('\\n');\n    }\n    function prepareTruncateOptions(containerWidth, font, ellipsis, options) {\n        options = options || {};\n        var preparedOpts = extend({}, options);\n        preparedOpts.font = font;\n        ellipsis = retrieve2(ellipsis, '...');\n        preparedOpts.maxIterations = retrieve2(options.maxIterations, 2);\n        var minChar = preparedOpts.minChar = retrieve2(options.minChar, 0);\n        preparedOpts.cnCharWidth = getWidth('国', font);\n        var ascCharWidth = preparedOpts.ascCharWidth = getWidth('a', font);\n        preparedOpts.placeholder = retrieve2(options.placeholder, '');\n        var contentWidth = containerWidth = Math.max(0, containerWidth - 1);\n        for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {\n            contentWidth -= ascCharWidth;\n        }\n        var ellipsisWidth = getWidth(ellipsis, font);\n        if (ellipsisWidth > contentWidth) {\n            ellipsis = '';\n            ellipsisWidth = 0;\n        }\n        contentWidth = containerWidth - ellipsisWidth;\n        preparedOpts.ellipsis = ellipsis;\n        preparedOpts.ellipsisWidth = ellipsisWidth;\n        preparedOpts.contentWidth = contentWidth;\n        preparedOpts.containerWidth = containerWidth;\n        return preparedOpts;\n    }\n    function truncateSingleLine(textLine, options) {\n        var containerWidth = options.containerWidth;\n        var font = options.font;\n        var contentWidth = options.contentWidth;\n        if (!containerWidth) {\n            return '';\n        }\n        var lineWidth = getWidth(textLine, font);\n        if (lineWidth <= containerWidth) {\n            return textLine;\n        }\n        for (var j = 0;; j++) {\n            if (lineWidth <= contentWidth || j >= options.maxIterations) {\n                textLine += options.ellipsis;\n                break;\n            }\n            var subLength = j === 0\n                ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)\n                : lineWidth > 0\n                    ? Math.floor(textLine.length * contentWidth / lineWidth)\n                    : 0;\n            textLine = textLine.substr(0, subLength);\n            lineWidth = getWidth(textLine, font);\n        }\n        if (textLine === '') {\n            textLine = options.placeholder;\n        }\n        return textLine;\n    }\n    function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {\n        var width = 0;\n        var i = 0;\n        for (var len = text.length; i < len && width < contentWidth; i++) {\n            var charCode = text.charCodeAt(i);\n            width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;\n        }\n        return i;\n    }\n    function parsePlainText(text, style) {\n        text != null && (text += '');\n        var overflow = style.overflow;\n        var padding = style.padding;\n        var font = style.font;\n        var truncate = overflow === 'truncate';\n        var calculatedLineHeight = getLineHeight(font);\n        var lineHeight = retrieve2(style.lineHeight, calculatedLineHeight);\n        var bgColorDrawn = !!(style.backgroundColor);\n        var truncateLineOverflow = style.lineOverflow === 'truncate';\n        var width = style.width;\n        var lines;\n        if (width != null && (overflow === 'break' || overflow === 'breakAll')) {\n            lines = text ? wrapText(text, style.font, width, overflow === 'breakAll', 0).lines : [];\n        }\n        else {\n            lines = text ? text.split('\\n') : [];\n        }\n        var contentHeight = lines.length * lineHeight;\n        var height = retrieve2(style.height, contentHeight);\n        if (contentHeight > height && truncateLineOverflow) {\n            var lineCount = Math.floor(height / lineHeight);\n            lines = lines.slice(0, lineCount);\n        }\n        if (text && truncate && width != null) {\n            var options = prepareTruncateOptions(width, font, style.ellipsis, {\n                minChar: style.truncateMinChar,\n                placeholder: style.placeholder\n            });\n            for (var i = 0; i < lines.length; i++) {\n                lines[i] = truncateSingleLine(lines[i], options);\n            }\n        }\n        var outerHeight = height;\n        var contentWidth = 0;\n        for (var i = 0; i < lines.length; i++) {\n            contentWidth = Math.max(getWidth(lines[i], font), contentWidth);\n        }\n        if (width == null) {\n            width = contentWidth;\n        }\n        var outerWidth = contentWidth;\n        if (padding) {\n            outerHeight += padding[0] + padding[2];\n            outerWidth += padding[1] + padding[3];\n            width += padding[1] + padding[3];\n        }\n        if (bgColorDrawn) {\n            outerWidth = width;\n        }\n        return {\n            lines: lines,\n            height: height,\n            outerWidth: outerWidth,\n            outerHeight: outerHeight,\n            lineHeight: lineHeight,\n            calculatedLineHeight: calculatedLineHeight,\n            contentWidth: contentWidth,\n            contentHeight: contentHeight,\n            width: width\n        };\n    }\n    var RichTextToken = (function () {\n        function RichTextToken() {\n        }\n        return RichTextToken;\n    }());\n    var RichTextLine = (function () {\n        function RichTextLine(tokens) {\n            this.tokens = [];\n            if (tokens) {\n                this.tokens = tokens;\n            }\n        }\n        return RichTextLine;\n    }());\n    var RichTextContentBlock = (function () {\n        function RichTextContentBlock() {\n            this.width = 0;\n            this.height = 0;\n            this.contentWidth = 0;\n            this.contentHeight = 0;\n            this.outerWidth = 0;\n            this.outerHeight = 0;\n            this.lines = [];\n        }\n        return RichTextContentBlock;\n    }());\n    function parseRichText(text, style) {\n        var contentBlock = new RichTextContentBlock();\n        text != null && (text += '');\n        if (!text) {\n            return contentBlock;\n        }\n        var topWidth = style.width;\n        var topHeight = style.height;\n        var overflow = style.overflow;\n        var wrapInfo = (overflow === 'break' || overflow === 'breakAll') && topWidth != null\n            ? { width: topWidth, accumWidth: 0, breakAll: overflow === 'breakAll' }\n            : null;\n        var lastIndex = STYLE_REG.lastIndex = 0;\n        var result;\n        while ((result = STYLE_REG.exec(text)) != null) {\n            var matchedIndex = result.index;\n            if (matchedIndex > lastIndex) {\n                pushTokens(contentBlock, text.substring(lastIndex, matchedIndex), style, wrapInfo);\n            }\n            pushTokens(contentBlock, result[2], style, wrapInfo, result[1]);\n            lastIndex = STYLE_REG.lastIndex;\n        }\n        if (lastIndex < text.length) {\n            pushTokens(contentBlock, text.substring(lastIndex, text.length), style, wrapInfo);\n        }\n        var pendingList = [];\n        var calculatedHeight = 0;\n        var calculatedWidth = 0;\n        var stlPadding = style.padding;\n        var truncate = overflow === 'truncate';\n        var truncateLine = style.lineOverflow === 'truncate';\n        function finishLine(line, lineWidth, lineHeight) {\n            line.width = lineWidth;\n            line.lineHeight = lineHeight;\n            calculatedHeight += lineHeight;\n            calculatedWidth = Math.max(calculatedWidth, lineWidth);\n        }\n        outer: for (var i = 0; i < contentBlock.lines.length; i++) {\n            var line = contentBlock.lines[i];\n            var lineHeight = 0;\n            var lineWidth = 0;\n            for (var j = 0; j < line.tokens.length; j++) {\n                var token = line.tokens[j];\n                var tokenStyle = token.styleName && style.rich[token.styleName] || {};\n                var textPadding = token.textPadding = tokenStyle.padding;\n                var paddingH = textPadding ? textPadding[1] + textPadding[3] : 0;\n                var font = token.font = tokenStyle.font || style.font;\n                token.contentHeight = getLineHeight(font);\n                var tokenHeight = retrieve2(tokenStyle.height, token.contentHeight);\n                token.innerHeight = tokenHeight;\n                textPadding && (tokenHeight += textPadding[0] + textPadding[2]);\n                token.height = tokenHeight;\n                token.lineHeight = retrieve3(tokenStyle.lineHeight, style.lineHeight, tokenHeight);\n                token.align = tokenStyle && tokenStyle.align || style.align;\n                token.verticalAlign = tokenStyle && tokenStyle.verticalAlign || 'middle';\n                if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) {\n                    if (j > 0) {\n                        line.tokens = line.tokens.slice(0, j);\n                        finishLine(line, lineWidth, lineHeight);\n                        contentBlock.lines = contentBlock.lines.slice(0, i + 1);\n                    }\n                    else {\n                        contentBlock.lines = contentBlock.lines.slice(0, i);\n                    }\n                    break outer;\n                }\n                var styleTokenWidth = tokenStyle.width;\n                var tokenWidthNotSpecified = styleTokenWidth == null || styleTokenWidth === 'auto';\n                if (typeof styleTokenWidth === 'string' && styleTokenWidth.charAt(styleTokenWidth.length - 1) === '%') {\n                    token.percentWidth = styleTokenWidth;\n                    pendingList.push(token);\n                    token.contentWidth = getWidth(token.text, font);\n                }\n                else {\n                    if (tokenWidthNotSpecified) {\n                        var textBackgroundColor = tokenStyle.backgroundColor;\n                        var bgImg = textBackgroundColor && textBackgroundColor.image;\n                        if (bgImg) {\n                            bgImg = findExistImage(bgImg);\n                            if (isImageReady(bgImg)) {\n                                token.width = Math.max(token.width, bgImg.width * tokenHeight / bgImg.height);\n                            }\n                        }\n                    }\n                    var remainTruncWidth = truncate && topWidth != null\n                        ? topWidth - lineWidth : null;\n                    if (remainTruncWidth != null && remainTruncWidth < token.width) {\n                        if (!tokenWidthNotSpecified || remainTruncWidth < paddingH) {\n                            token.text = '';\n                            token.width = token.contentWidth = 0;\n                        }\n                        else {\n                            token.text = truncateText(token.text, remainTruncWidth - paddingH, font, style.ellipsis, { minChar: style.truncateMinChar });\n                            token.width = token.contentWidth = getWidth(token.text, font);\n                        }\n                    }\n                    else {\n                        token.contentWidth = getWidth(token.text, font);\n                    }\n                }\n                token.width += paddingH;\n                lineWidth += token.width;\n                tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));\n            }\n            finishLine(line, lineWidth, lineHeight);\n        }\n        contentBlock.outerWidth = contentBlock.width = retrieve2(topWidth, calculatedWidth);\n        contentBlock.outerHeight = contentBlock.height = retrieve2(topHeight, calculatedHeight);\n        contentBlock.contentHeight = calculatedHeight;\n        contentBlock.contentWidth = calculatedWidth;\n        if (stlPadding) {\n            contentBlock.outerWidth += stlPadding[1] + stlPadding[3];\n            contentBlock.outerHeight += stlPadding[0] + stlPadding[2];\n        }\n        for (var i = 0; i < pendingList.length; i++) {\n            var token = pendingList[i];\n            var percentWidth = token.percentWidth;\n            token.width = parseInt(percentWidth, 10) / 100 * contentBlock.width;\n        }\n        return contentBlock;\n    }\n    function pushTokens(block, str, style, wrapInfo, styleName) {\n        var isEmptyStr = str === '';\n        var tokenStyle = styleName && style.rich[styleName] || {};\n        var lines = block.lines;\n        var font = tokenStyle.font || style.font;\n        var newLine = false;\n        var strLines;\n        var linesWidths;\n        if (wrapInfo) {\n            var tokenPadding = tokenStyle.padding;\n            var tokenPaddingH = tokenPadding ? tokenPadding[1] + tokenPadding[3] : 0;\n            if (tokenStyle.width != null && tokenStyle.width !== 'auto') {\n                var outerWidth_1 = parsePercent(tokenStyle.width, wrapInfo.width) + tokenPaddingH;\n                if (lines.length > 0) {\n                    if (outerWidth_1 + wrapInfo.accumWidth > wrapInfo.width) {\n                        strLines = str.split('\\n');\n                        newLine = true;\n                    }\n                }\n                wrapInfo.accumWidth = outerWidth_1;\n            }\n            else {\n                var res = wrapText(str, font, wrapInfo.width, wrapInfo.breakAll, wrapInfo.accumWidth);\n                wrapInfo.accumWidth = res.accumWidth + tokenPaddingH;\n                linesWidths = res.linesWidths;\n                strLines = res.lines;\n            }\n        }\n        else {\n            strLines = str.split('\\n');\n        }\n        for (var i = 0; i < strLines.length; i++) {\n            var text = strLines[i];\n            var token = new RichTextToken();\n            token.styleName = styleName;\n            token.text = text;\n            token.isLineHolder = !text && !isEmptyStr;\n            if (typeof tokenStyle.width === 'number') {\n                token.width = tokenStyle.width;\n            }\n            else {\n                token.width = linesWidths\n                    ? linesWidths[i]\n                    : getWidth(text, font);\n            }\n            if (!i && !newLine) {\n                var tokens = (lines[lines.length - 1] || (lines[0] = new RichTextLine())).tokens;\n                var tokensLen = tokens.length;\n                (tokensLen === 1 && tokens[0].isLineHolder)\n                    ? (tokens[0] = token)\n                    : ((text || !tokensLen || isEmptyStr) && tokens.push(token));\n            }\n            else {\n                lines.push(new RichTextLine([token]));\n            }\n        }\n    }\n    function isLatin(ch) {\n        var code = ch.charCodeAt(0);\n        return code >= 0x21 && code <= 0x17F;\n    }\n    var breakCharMap = reduce(',&?/;] '.split(''), function (obj, ch) {\n        obj[ch] = true;\n        return obj;\n    }, {});\n    function isWordBreakChar(ch) {\n        if (isLatin(ch)) {\n            if (breakCharMap[ch]) {\n                return true;\n            }\n            return false;\n        }\n        return true;\n    }\n    function wrapText(text, font, lineWidth, isBreakAll, lastAccumWidth) {\n        var lines = [];\n        var linesWidths = [];\n        var line = '';\n        var currentWord = '';\n        var currentWordWidth = 0;\n        var accumWidth = 0;\n        for (var i = 0; i < text.length; i++) {\n            var ch = text.charAt(i);\n            if (ch === '\\n') {\n                if (currentWord) {\n                    line += currentWord;\n                    accumWidth += currentWordWidth;\n                }\n                lines.push(line);\n                linesWidths.push(accumWidth);\n                line = '';\n                currentWord = '';\n                currentWordWidth = 0;\n                accumWidth = 0;\n                continue;\n            }\n            var chWidth = getWidth(ch, font);\n            var inWord = isBreakAll ? false : !isWordBreakChar(ch);\n            if (!lines.length\n                ? lastAccumWidth + accumWidth + chWidth > lineWidth\n                : accumWidth + chWidth > lineWidth) {\n                if (!accumWidth) {\n                    if (inWord) {\n                        lines.push(currentWord);\n                        linesWidths.push(currentWordWidth);\n                        currentWord = ch;\n                        currentWordWidth = chWidth;\n                    }\n                    else {\n                        lines.push(ch);\n                        linesWidths.push(chWidth);\n                    }\n                }\n                else if (line || currentWord) {\n                    if (inWord) {\n                        if (!line) {\n                            line = currentWord;\n                            currentWord = '';\n                            currentWordWidth = 0;\n                            accumWidth = currentWordWidth;\n                        }\n                        lines.push(line);\n                        linesWidths.push(accumWidth - currentWordWidth);\n                        currentWord += ch;\n                        currentWordWidth += chWidth;\n                        line = '';\n                        accumWidth = currentWordWidth;\n                    }\n                    else {\n                        if (currentWord) {\n                            line += currentWord;\n                            currentWord = '';\n                            currentWordWidth = 0;\n                        }\n                        lines.push(line);\n                        linesWidths.push(accumWidth);\n                        line = ch;\n                        accumWidth = chWidth;\n                    }\n                }\n                continue;\n            }\n            accumWidth += chWidth;\n            if (inWord) {\n                currentWord += ch;\n                currentWordWidth += chWidth;\n            }\n            else {\n                if (currentWord) {\n                    line += currentWord;\n                    currentWord = '';\n                    currentWordWidth = 0;\n                }\n                line += ch;\n            }\n        }\n        if (!lines.length && !line) {\n            line = text;\n            currentWord = '';\n            currentWordWidth = 0;\n        }\n        if (currentWord) {\n            line += currentWord;\n        }\n        if (line) {\n            lines.push(line);\n            linesWidths.push(accumWidth);\n        }\n        if (lines.length === 1) {\n            accumWidth += lastAccumWidth;\n        }\n        return {\n            accumWidth: accumWidth,\n            lines: lines,\n            linesWidths: linesWidths\n        };\n    }\n\n    var STYLE_MAGIC_KEY = '__zr_style_' + Math.round((Math.random() * 10));\n    var DEFAULT_COMMON_STYLE = {\n        shadowBlur: 0,\n        shadowOffsetX: 0,\n        shadowOffsetY: 0,\n        shadowColor: '#000',\n        opacity: 1,\n        blend: 'source-over'\n    };\n    var DEFAULT_COMMON_ANIMATION_PROPS = {\n        style: {\n            shadowBlur: true,\n            shadowOffsetX: true,\n            shadowOffsetY: true,\n            shadowColor: true,\n            opacity: true\n        }\n    };\n    DEFAULT_COMMON_STYLE[STYLE_MAGIC_KEY] = true;\n    var PRIMARY_STATES_KEYS$1 = ['z', 'z2', 'invisible'];\n    var PRIMARY_STATES_KEYS_IN_HOVER_LAYER = ['invisible'];\n    var Displayable = (function (_super) {\n        __extends(Displayable, _super);\n        function Displayable(props) {\n            return _super.call(this, props) || this;\n        }\n        Displayable.prototype._init = function (props) {\n            var keysArr = keys(props);\n            for (var i = 0; i < keysArr.length; i++) {\n                var key = keysArr[i];\n                if (key === 'style') {\n                    this.useStyle(props[key]);\n                }\n                else {\n                    _super.prototype.attrKV.call(this, key, props[key]);\n                }\n            }\n            if (!this.style) {\n                this.useStyle({});\n            }\n        };\n        Displayable.prototype.beforeBrush = function () { };\n        Displayable.prototype.afterBrush = function () { };\n        Displayable.prototype.innerBeforeBrush = function () { };\n        Displayable.prototype.innerAfterBrush = function () { };\n        Displayable.prototype.shouldBePainted = function (viewWidth, viewHeight, considerClipPath, considerAncestors) {\n            var m = this.transform;\n            if (this.ignore\n                || this.invisible\n                || this.style.opacity === 0\n                || (this.culling\n                    && isDisplayableCulled(this, viewWidth, viewHeight))\n                || (m && !m[0] && !m[3])) {\n                return false;\n            }\n            if (considerClipPath && this.__clipPaths) {\n                for (var i = 0; i < this.__clipPaths.length; ++i) {\n                    if (this.__clipPaths[i].isZeroArea()) {\n                        return false;\n                    }\n                }\n            }\n            if (considerAncestors && this.parent) {\n                var parent_1 = this.parent;\n                while (parent_1) {\n                    if (parent_1.ignore) {\n                        return false;\n                    }\n                    parent_1 = parent_1.parent;\n                }\n            }\n            return true;\n        };\n        Displayable.prototype.contain = function (x, y) {\n            return this.rectContain(x, y);\n        };\n        Displayable.prototype.traverse = function (cb, context) {\n            cb.call(context, this);\n        };\n        Displayable.prototype.rectContain = function (x, y) {\n            var coord = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            return rect.contain(coord[0], coord[1]);\n        };\n        Displayable.prototype.getPaintRect = function () {\n            var rect = this._paintRect;\n            if (!this._paintRect || this.__dirty) {\n                var transform = this.transform;\n                var elRect = this.getBoundingRect();\n                var style = this.style;\n                var shadowSize = style.shadowBlur || 0;\n                var shadowOffsetX = style.shadowOffsetX || 0;\n                var shadowOffsetY = style.shadowOffsetY || 0;\n                rect = this._paintRect || (this._paintRect = new BoundingRect(0, 0, 0, 0));\n                if (transform) {\n                    BoundingRect.applyTransform(rect, elRect, transform);\n                }\n                else {\n                    rect.copy(elRect);\n                }\n                if (shadowSize || shadowOffsetX || shadowOffsetY) {\n                    rect.width += shadowSize * 2 + Math.abs(shadowOffsetX);\n                    rect.height += shadowSize * 2 + Math.abs(shadowOffsetY);\n                    rect.x = Math.min(rect.x, rect.x + shadowOffsetX - shadowSize);\n                    rect.y = Math.min(rect.y, rect.y + shadowOffsetY - shadowSize);\n                }\n                var tolerance = this.dirtyRectTolerance;\n                if (!rect.isZero()) {\n                    rect.x = Math.floor(rect.x - tolerance);\n                    rect.y = Math.floor(rect.y - tolerance);\n                    rect.width = Math.ceil(rect.width + 1 + tolerance * 2);\n                    rect.height = Math.ceil(rect.height + 1 + tolerance * 2);\n                }\n            }\n            return rect;\n        };\n        Displayable.prototype.setPrevPaintRect = function (paintRect) {\n            if (paintRect) {\n                this._prevPaintRect = this._prevPaintRect || new BoundingRect(0, 0, 0, 0);\n                this._prevPaintRect.copy(paintRect);\n            }\n            else {\n                this._prevPaintRect = null;\n            }\n        };\n        Displayable.prototype.getPrevPaintRect = function () {\n            return this._prevPaintRect;\n        };\n        Displayable.prototype.animateStyle = function (loop) {\n            return this.animate('style', loop);\n        };\n        Displayable.prototype.updateDuringAnimation = function (targetKey) {\n            if (targetKey === 'style') {\n                this.dirtyStyle();\n            }\n            else {\n                this.markRedraw();\n            }\n        };\n        Displayable.prototype.attrKV = function (key, value) {\n            if (key !== 'style') {\n                _super.prototype.attrKV.call(this, key, value);\n            }\n            else {\n                if (!this.style) {\n                    this.useStyle(value);\n                }\n                else {\n                    this.setStyle(value);\n                }\n            }\n        };\n        Displayable.prototype.setStyle = function (keyOrObj, value) {\n            if (typeof keyOrObj === 'string') {\n                this.style[keyOrObj] = value;\n            }\n            else {\n                extend(this.style, keyOrObj);\n            }\n            this.dirtyStyle();\n            return this;\n        };\n        Displayable.prototype.dirtyStyle = function (notRedraw) {\n            if (!notRedraw) {\n                this.markRedraw();\n            }\n            this.__dirty |= STYLE_CHANGED_BIT;\n            if (this._rect) {\n                this._rect = null;\n            }\n        };\n        Displayable.prototype.dirty = function () {\n            this.dirtyStyle();\n        };\n        Displayable.prototype.styleChanged = function () {\n            return !!(this.__dirty & STYLE_CHANGED_BIT);\n        };\n        Displayable.prototype.styleUpdated = function () {\n            this.__dirty &= ~STYLE_CHANGED_BIT;\n        };\n        Displayable.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_COMMON_STYLE, obj);\n        };\n        Displayable.prototype.useStyle = function (obj) {\n            if (!obj[STYLE_MAGIC_KEY]) {\n                obj = this.createStyle(obj);\n            }\n            if (this.__inHover) {\n                this.__hoverStyle = obj;\n            }\n            else {\n                this.style = obj;\n            }\n            this.dirtyStyle();\n        };\n        Displayable.prototype.isStyleObject = function (obj) {\n            return obj[STYLE_MAGIC_KEY];\n        };\n        Displayable.prototype._innerSaveToNormal = function (toState) {\n            _super.prototype._innerSaveToNormal.call(this, toState);\n            var normalState = this._normalState;\n            if (toState.style && !normalState.style) {\n                normalState.style = this._mergeStyle(this.createStyle(), this.style);\n            }\n            this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS$1);\n        };\n        Displayable.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            var targetStyle;\n            if (state && state.style) {\n                if (transition) {\n                    if (keepCurrentStates) {\n                        targetStyle = state.style;\n                    }\n                    else {\n                        targetStyle = this._mergeStyle(this.createStyle(), normalState.style);\n                        this._mergeStyle(targetStyle, state.style);\n                    }\n                }\n                else {\n                    targetStyle = this._mergeStyle(this.createStyle(), keepCurrentStates ? this.style : normalState.style);\n                    this._mergeStyle(targetStyle, state.style);\n                }\n            }\n            else if (needsRestoreToNormal) {\n                targetStyle = normalState.style;\n            }\n            if (targetStyle) {\n                if (transition) {\n                    var sourceStyle = this.style;\n                    this.style = this.createStyle(needsRestoreToNormal ? {} : sourceStyle);\n                    if (needsRestoreToNormal) {\n                        var changedKeys = keys(sourceStyle);\n                        for (var i = 0; i < changedKeys.length; i++) {\n                            var key = changedKeys[i];\n                            if (key in targetStyle) {\n                                targetStyle[key] = targetStyle[key];\n                                this.style[key] = sourceStyle[key];\n                            }\n                        }\n                    }\n                    var targetKeys = keys(targetStyle);\n                    for (var i = 0; i < targetKeys.length; i++) {\n                        var key = targetKeys[i];\n                        this.style[key] = this.style[key];\n                    }\n                    this._transitionState(stateName, {\n                        style: targetStyle\n                    }, animationCfg, this.getAnimationStyleProps());\n                }\n                else {\n                    this.useStyle(targetStyle);\n                }\n            }\n            var statesKeys = this.__inHover ? PRIMARY_STATES_KEYS_IN_HOVER_LAYER : PRIMARY_STATES_KEYS$1;\n            for (var i = 0; i < statesKeys.length; i++) {\n                var key = statesKeys[i];\n                if (state && state[key] != null) {\n                    this[key] = state[key];\n                }\n                else if (needsRestoreToNormal) {\n                    if (normalState[key] != null) {\n                        this[key] = normalState[key];\n                    }\n                }\n            }\n        };\n        Displayable.prototype._mergeStates = function (states) {\n            var mergedState = _super.prototype._mergeStates.call(this, states);\n            var mergedStyle;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                if (state.style) {\n                    mergedStyle = mergedStyle || {};\n                    this._mergeStyle(mergedStyle, state.style);\n                }\n            }\n            if (mergedStyle) {\n                mergedState.style = mergedStyle;\n            }\n            return mergedState;\n        };\n        Displayable.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n            extend(targetStyle, sourceStyle);\n            return targetStyle;\n        };\n        Displayable.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_COMMON_ANIMATION_PROPS;\n        };\n        Displayable.initDefaultProps = (function () {\n            var dispProto = Displayable.prototype;\n            dispProto.type = 'displayable';\n            dispProto.invisible = false;\n            dispProto.z = 0;\n            dispProto.z2 = 0;\n            dispProto.zlevel = 0;\n            dispProto.culling = false;\n            dispProto.cursor = 'pointer';\n            dispProto.rectHover = false;\n            dispProto.incremental = false;\n            dispProto._rect = null;\n            dispProto.dirtyRectTolerance = 0;\n            dispProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT;\n        })();\n        return Displayable;\n    }(Element));\n    var tmpRect$1 = new BoundingRect(0, 0, 0, 0);\n    var viewRect = new BoundingRect(0, 0, 0, 0);\n    function isDisplayableCulled(el, width, height) {\n        tmpRect$1.copy(el.getBoundingRect());\n        if (el.transform) {\n            tmpRect$1.applyTransform(el.transform);\n        }\n        viewRect.width = width;\n        viewRect.height = height;\n        return !tmpRect$1.intersect(viewRect);\n    }\n\n    var mathMin$1 = Math.min;\n    var mathMax$1 = Math.max;\n    var mathSin = Math.sin;\n    var mathCos = Math.cos;\n    var PI2 = Math.PI * 2;\n    var start = create();\n    var end = create();\n    var extremity = create();\n    function fromLine(x0, y0, x1, y1, min, max) {\n        min[0] = mathMin$1(x0, x1);\n        min[1] = mathMin$1(y0, y1);\n        max[0] = mathMax$1(x0, x1);\n        max[1] = mathMax$1(y0, y1);\n    }\n    var xDim = [];\n    var yDim = [];\n    function fromCubic(x0, y0, x1, y1, x2, y2, x3, y3, min, max) {\n        var cubicExtrema$1 = cubicExtrema;\n        var cubicAt$1 = cubicAt;\n        var n = cubicExtrema$1(x0, x1, x2, x3, xDim);\n        min[0] = Infinity;\n        min[1] = Infinity;\n        max[0] = -Infinity;\n        max[1] = -Infinity;\n        for (var i = 0; i < n; i++) {\n            var x = cubicAt$1(x0, x1, x2, x3, xDim[i]);\n            min[0] = mathMin$1(x, min[0]);\n            max[0] = mathMax$1(x, max[0]);\n        }\n        n = cubicExtrema$1(y0, y1, y2, y3, yDim);\n        for (var i = 0; i < n; i++) {\n            var y = cubicAt$1(y0, y1, y2, y3, yDim[i]);\n            min[1] = mathMin$1(y, min[1]);\n            max[1] = mathMax$1(y, max[1]);\n        }\n        min[0] = mathMin$1(x0, min[0]);\n        max[0] = mathMax$1(x0, max[0]);\n        min[0] = mathMin$1(x3, min[0]);\n        max[0] = mathMax$1(x3, max[0]);\n        min[1] = mathMin$1(y0, min[1]);\n        max[1] = mathMax$1(y0, max[1]);\n        min[1] = mathMin$1(y3, min[1]);\n        max[1] = mathMax$1(y3, max[1]);\n    }\n    function fromQuadratic(x0, y0, x1, y1, x2, y2, min, max) {\n        var quadraticExtremum$1 = quadraticExtremum;\n        var quadraticAt$1 = quadraticAt;\n        var tx = mathMax$1(mathMin$1(quadraticExtremum$1(x0, x1, x2), 1), 0);\n        var ty = mathMax$1(mathMin$1(quadraticExtremum$1(y0, y1, y2), 1), 0);\n        var x = quadraticAt$1(x0, x1, x2, tx);\n        var y = quadraticAt$1(y0, y1, y2, ty);\n        min[0] = mathMin$1(x0, x2, x);\n        min[1] = mathMin$1(y0, y2, y);\n        max[0] = mathMax$1(x0, x2, x);\n        max[1] = mathMax$1(y0, y2, y);\n    }\n    function fromArc(x, y, rx, ry, startAngle, endAngle, anticlockwise, min$1, max$1) {\n        var vec2Min = min;\n        var vec2Max = max;\n        var diff = Math.abs(startAngle - endAngle);\n        if (diff % PI2 < 1e-4 && diff > 1e-4) {\n            min$1[0] = x - rx;\n            min$1[1] = y - ry;\n            max$1[0] = x + rx;\n            max$1[1] = y + ry;\n            return;\n        }\n        start[0] = mathCos(startAngle) * rx + x;\n        start[1] = mathSin(startAngle) * ry + y;\n        end[0] = mathCos(endAngle) * rx + x;\n        end[1] = mathSin(endAngle) * ry + y;\n        vec2Min(min$1, start, end);\n        vec2Max(max$1, start, end);\n        startAngle = startAngle % (PI2);\n        if (startAngle < 0) {\n            startAngle = startAngle + PI2;\n        }\n        endAngle = endAngle % (PI2);\n        if (endAngle < 0) {\n            endAngle = endAngle + PI2;\n        }\n        if (startAngle > endAngle && !anticlockwise) {\n            endAngle += PI2;\n        }\n        else if (startAngle < endAngle && anticlockwise) {\n            startAngle += PI2;\n        }\n        if (anticlockwise) {\n            var tmp = endAngle;\n            endAngle = startAngle;\n            startAngle = tmp;\n        }\n        for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {\n            if (angle > startAngle) {\n                extremity[0] = mathCos(angle) * rx + x;\n                extremity[1] = mathSin(angle) * ry + y;\n                vec2Min(min$1, extremity, min$1);\n                vec2Max(max$1, extremity, max$1);\n            }\n        }\n    }\n\n    var CMD = {\n        M: 1,\n        L: 2,\n        C: 3,\n        Q: 4,\n        A: 5,\n        Z: 6,\n        R: 7\n    };\n    var tmpOutX = [];\n    var tmpOutY = [];\n    var min$1 = [];\n    var max$1 = [];\n    var min2 = [];\n    var max2 = [];\n    var mathMin$2 = Math.min;\n    var mathMax$2 = Math.max;\n    var mathCos$1 = Math.cos;\n    var mathSin$1 = Math.sin;\n    var mathAbs = Math.abs;\n    var PI = Math.PI;\n    var PI2$1 = PI * 2;\n    var hasTypedArray = typeof Float32Array !== 'undefined';\n    var tmpAngles = [];\n    function modPI2(radian) {\n        var n = Math.round(radian / PI * 1e8) / 1e8;\n        return (n % 2) * PI;\n    }\n    function normalizeArcAngles(angles, anticlockwise) {\n        var newStartAngle = modPI2(angles[0]);\n        if (newStartAngle < 0) {\n            newStartAngle += PI2$1;\n        }\n        var delta = newStartAngle - angles[0];\n        var newEndAngle = angles[1];\n        newEndAngle += delta;\n        if (!anticlockwise && newEndAngle - newStartAngle >= PI2$1) {\n            newEndAngle = newStartAngle + PI2$1;\n        }\n        else if (anticlockwise && newStartAngle - newEndAngle >= PI2$1) {\n            newEndAngle = newStartAngle - PI2$1;\n        }\n        else if (!anticlockwise && newStartAngle > newEndAngle) {\n            newEndAngle = newStartAngle + (PI2$1 - modPI2(newStartAngle - newEndAngle));\n        }\n        else if (anticlockwise && newStartAngle < newEndAngle) {\n            newEndAngle = newStartAngle - (PI2$1 - modPI2(newEndAngle - newStartAngle));\n        }\n        angles[0] = newStartAngle;\n        angles[1] = newEndAngle;\n    }\n    var PathProxy = (function () {\n        function PathProxy(notSaveData) {\n            this.dpr = 1;\n            this._xi = 0;\n            this._yi = 0;\n            this._x0 = 0;\n            this._y0 = 0;\n            this._len = 0;\n            if (notSaveData) {\n                this._saveData = false;\n            }\n            if (this._saveData) {\n                this.data = [];\n            }\n        }\n        PathProxy.prototype.increaseVersion = function () {\n            this._version++;\n        };\n        PathProxy.prototype.getVersion = function () {\n            return this._version;\n        };\n        PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) {\n            segmentIgnoreThreshold = segmentIgnoreThreshold || 0;\n            if (segmentIgnoreThreshold > 0) {\n                this._ux = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sx) || 0;\n                this._uy = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sy) || 0;\n            }\n        };\n        PathProxy.prototype.setDPR = function (dpr) {\n            this.dpr = dpr;\n        };\n        PathProxy.prototype.setContext = function (ctx) {\n            this._ctx = ctx;\n        };\n        PathProxy.prototype.getContext = function () {\n            return this._ctx;\n        };\n        PathProxy.prototype.beginPath = function () {\n            this._ctx && this._ctx.beginPath();\n            this.reset();\n            return this;\n        };\n        PathProxy.prototype.reset = function () {\n            if (this._saveData) {\n                this._len = 0;\n            }\n            if (this._pathSegLen) {\n                this._pathSegLen = null;\n                this._pathLen = 0;\n            }\n            this._version++;\n        };\n        PathProxy.prototype.moveTo = function (x, y) {\n            this._drawPendingPt();\n            this.addData(CMD.M, x, y);\n            this._ctx && this._ctx.moveTo(x, y);\n            this._x0 = x;\n            this._y0 = y;\n            this._xi = x;\n            this._yi = y;\n            return this;\n        };\n        PathProxy.prototype.lineTo = function (x, y) {\n            var dx = mathAbs(x - this._xi);\n            var dy = mathAbs(y - this._yi);\n            var exceedUnit = dx > this._ux || dy > this._uy;\n            this.addData(CMD.L, x, y);\n            if (this._ctx && exceedUnit) {\n                this._ctx.lineTo(x, y);\n            }\n            if (exceedUnit) {\n                this._xi = x;\n                this._yi = y;\n                this._pendingPtDist = 0;\n            }\n            else {\n                var d2 = dx * dx + dy * dy;\n                if (d2 > this._pendingPtDist) {\n                    this._pendingPtX = x;\n                    this._pendingPtY = y;\n                    this._pendingPtDist = d2;\n                }\n            }\n            return this;\n        };\n        PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) {\n            this._drawPendingPt();\n            this.addData(CMD.C, x1, y1, x2, y2, x3, y3);\n            if (this._ctx) {\n                this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n            }\n            this._xi = x3;\n            this._yi = y3;\n            return this;\n        };\n        PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) {\n            this._drawPendingPt();\n            this.addData(CMD.Q, x1, y1, x2, y2);\n            if (this._ctx) {\n                this._ctx.quadraticCurveTo(x1, y1, x2, y2);\n            }\n            this._xi = x2;\n            this._yi = y2;\n            return this;\n        };\n        PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n            this._drawPendingPt();\n            tmpAngles[0] = startAngle;\n            tmpAngles[1] = endAngle;\n            normalizeArcAngles(tmpAngles, anticlockwise);\n            startAngle = tmpAngles[0];\n            endAngle = tmpAngles[1];\n            var delta = endAngle - startAngle;\n            this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 0, anticlockwise ? 0 : 1);\n            this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n            this._xi = mathCos$1(endAngle) * r + cx;\n            this._yi = mathSin$1(endAngle) * r + cy;\n            return this;\n        };\n        PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) {\n            this._drawPendingPt();\n            if (this._ctx) {\n                this._ctx.arcTo(x1, y1, x2, y2, radius);\n            }\n            return this;\n        };\n        PathProxy.prototype.rect = function (x, y, w, h) {\n            this._drawPendingPt();\n            this._ctx && this._ctx.rect(x, y, w, h);\n            this.addData(CMD.R, x, y, w, h);\n            return this;\n        };\n        PathProxy.prototype.closePath = function () {\n            this._drawPendingPt();\n            this.addData(CMD.Z);\n            var ctx = this._ctx;\n            var x0 = this._x0;\n            var y0 = this._y0;\n            if (ctx) {\n                ctx.closePath();\n            }\n            this._xi = x0;\n            this._yi = y0;\n            return this;\n        };\n        PathProxy.prototype.fill = function (ctx) {\n            ctx && ctx.fill();\n            this.toStatic();\n        };\n        PathProxy.prototype.stroke = function (ctx) {\n            ctx && ctx.stroke();\n            this.toStatic();\n        };\n        PathProxy.prototype.len = function () {\n            return this._len;\n        };\n        PathProxy.prototype.setData = function (data) {\n            var len = data.length;\n            if (!(this.data && this.data.length === len) && hasTypedArray) {\n                this.data = new Float32Array(len);\n            }\n            for (var i = 0; i < len; i++) {\n                this.data[i] = data[i];\n            }\n            this._len = len;\n        };\n        PathProxy.prototype.appendPath = function (path) {\n            if (!(path instanceof Array)) {\n                path = [path];\n            }\n            var len = path.length;\n            var appendSize = 0;\n            var offset = this._len;\n            for (var i = 0; i < len; i++) {\n                appendSize += path[i].len();\n            }\n            if (hasTypedArray && (this.data instanceof Float32Array)) {\n                this.data = new Float32Array(offset + appendSize);\n            }\n            for (var i = 0; i < len; i++) {\n                var appendPathData = path[i].data;\n                for (var k = 0; k < appendPathData.length; k++) {\n                    this.data[offset++] = appendPathData[k];\n                }\n            }\n            this._len = offset;\n        };\n        PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) {\n            if (!this._saveData) {\n                return;\n            }\n            var data = this.data;\n            if (this._len + arguments.length > data.length) {\n                this._expandData();\n                data = this.data;\n            }\n            for (var i = 0; i < arguments.length; i++) {\n                data[this._len++] = arguments[i];\n            }\n        };\n        PathProxy.prototype._drawPendingPt = function () {\n            if (this._pendingPtDist > 0) {\n                this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY);\n                this._pendingPtDist = 0;\n            }\n        };\n        PathProxy.prototype._expandData = function () {\n            if (!(this.data instanceof Array)) {\n                var newData = [];\n                for (var i = 0; i < this._len; i++) {\n                    newData[i] = this.data[i];\n                }\n                this.data = newData;\n            }\n        };\n        PathProxy.prototype.toStatic = function () {\n            if (!this._saveData) {\n                return;\n            }\n            this._drawPendingPt();\n            var data = this.data;\n            if (data instanceof Array) {\n                data.length = this._len;\n                if (hasTypedArray && this._len > 11) {\n                    this.data = new Float32Array(data);\n                }\n            }\n        };\n        PathProxy.prototype.getBoundingRect = function () {\n            min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;\n            max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;\n            var data = this.data;\n            var xi = 0;\n            var yi = 0;\n            var x0 = 0;\n            var y0 = 0;\n            var i;\n            for (i = 0; i < this._len;) {\n                var cmd = data[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = data[i];\n                    yi = data[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                switch (cmd) {\n                    case CMD.M:\n                        xi = x0 = data[i++];\n                        yi = y0 = data[i++];\n                        min2[0] = x0;\n                        min2[1] = y0;\n                        max2[0] = x0;\n                        max2[1] = y0;\n                        break;\n                    case CMD.L:\n                        fromLine(xi, yi, data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.C:\n                        fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.Q:\n                        fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.A:\n                        var cx = data[i++];\n                        var cy = data[i++];\n                        var rx = data[i++];\n                        var ry = data[i++];\n                        var startAngle = data[i++];\n                        var endAngle = data[i++] + startAngle;\n                        i += 1;\n                        var anticlockwise = !data[i++];\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R:\n                        x0 = xi = data[i++];\n                        y0 = yi = data[i++];\n                        var width = data[i++];\n                        var height = data[i++];\n                        fromLine(x0, y0, x0 + width, y0 + height, min2, max2);\n                        break;\n                    case CMD.Z:\n                        xi = x0;\n                        yi = y0;\n                        break;\n                }\n                min(min$1, min$1, min2);\n                max(max$1, max$1, max2);\n            }\n            if (i === 0) {\n                min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;\n            }\n            return new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]);\n        };\n        PathProxy.prototype._calculateLength = function () {\n            var data = this.data;\n            var len = this._len;\n            var ux = this._ux;\n            var uy = this._uy;\n            var xi = 0;\n            var yi = 0;\n            var x0 = 0;\n            var y0 = 0;\n            if (!this._pathSegLen) {\n                this._pathSegLen = [];\n            }\n            var pathSegLen = this._pathSegLen;\n            var pathTotalLen = 0;\n            var segCount = 0;\n            for (var i = 0; i < len;) {\n                var cmd = data[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = data[i];\n                    yi = data[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                var l = -1;\n                switch (cmd) {\n                    case CMD.M:\n                        xi = x0 = data[i++];\n                        yi = y0 = data[i++];\n                        break;\n                    case CMD.L: {\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        var dx = x2 - xi;\n                        var dy = y2 - yi;\n                        if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) {\n                            l = Math.sqrt(dx * dx + dy * dy);\n                            xi = x2;\n                            yi = y2;\n                        }\n                        break;\n                    }\n                    case CMD.C: {\n                        var x1 = data[i++];\n                        var y1 = data[i++];\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        var x3 = data[i++];\n                        var y3 = data[i++];\n                        l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10);\n                        xi = x3;\n                        yi = y3;\n                        break;\n                    }\n                    case CMD.Q: {\n                        var x1 = data[i++];\n                        var y1 = data[i++];\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        l = quadraticLength(xi, yi, x1, y1, x2, y2, 10);\n                        xi = x2;\n                        yi = y2;\n                        break;\n                    }\n                    case CMD.A:\n                        var cx = data[i++];\n                        var cy = data[i++];\n                        var rx = data[i++];\n                        var ry = data[i++];\n                        var startAngle = data[i++];\n                        var delta = data[i++];\n                        var endAngle = delta + startAngle;\n                        i += 1;\n                        var anticlockwise = !data[i++];\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        l = mathMax$2(rx, ry) * mathMin$2(PI2$1, Math.abs(delta));\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R: {\n                        x0 = xi = data[i++];\n                        y0 = yi = data[i++];\n                        var width = data[i++];\n                        var height = data[i++];\n                        l = width * 2 + height * 2;\n                        break;\n                    }\n                    case CMD.Z: {\n                        var dx = x0 - xi;\n                        var dy = y0 - yi;\n                        l = Math.sqrt(dx * dx + dy * dy);\n                        xi = x0;\n                        yi = y0;\n                        break;\n                    }\n                }\n                if (l >= 0) {\n                    pathSegLen[segCount++] = l;\n                    pathTotalLen += l;\n                }\n            }\n            this._pathLen = pathTotalLen;\n            return pathTotalLen;\n        };\n        PathProxy.prototype.rebuildPath = function (ctx, percent) {\n            var d = this.data;\n            var ux = this._ux;\n            var uy = this._uy;\n            var len = this._len;\n            var x0;\n            var y0;\n            var xi;\n            var yi;\n            var x;\n            var y;\n            var drawPart = percent < 1;\n            var pathSegLen;\n            var pathTotalLen;\n            var accumLength = 0;\n            var segCount = 0;\n            var displayedLength;\n            var pendingPtDist = 0;\n            var pendingPtX;\n            var pendingPtY;\n            if (drawPart) {\n                if (!this._pathSegLen) {\n                    this._calculateLength();\n                }\n                pathSegLen = this._pathSegLen;\n                pathTotalLen = this._pathLen;\n                displayedLength = percent * pathTotalLen;\n                if (!displayedLength) {\n                    return;\n                }\n            }\n            lo: for (var i = 0; i < len;) {\n                var cmd = d[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = d[i];\n                    yi = d[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                if (cmd !== CMD.L && pendingPtDist > 0) {\n                    ctx.lineTo(pendingPtX, pendingPtY);\n                    pendingPtDist = 0;\n                }\n                switch (cmd) {\n                    case CMD.M:\n                        x0 = xi = d[i++];\n                        y0 = yi = d[i++];\n                        ctx.moveTo(xi, yi);\n                        break;\n                    case CMD.L: {\n                        x = d[i++];\n                        y = d[i++];\n                        var dx = mathAbs(x - xi);\n                        var dy = mathAbs(y - yi);\n                        if (dx > ux || dy > uy) {\n                            if (drawPart) {\n                                var l = pathSegLen[segCount++];\n                                if (accumLength + l > displayedLength) {\n                                    var t = (displayedLength - accumLength) / l;\n                                    ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t);\n                                    break lo;\n                                }\n                                accumLength += l;\n                            }\n                            ctx.lineTo(x, y);\n                            xi = x;\n                            yi = y;\n                            pendingPtDist = 0;\n                        }\n                        else {\n                            var d2 = dx * dx + dy * dy;\n                            if (d2 > pendingPtDist) {\n                                pendingPtX = x;\n                                pendingPtY = y;\n                                pendingPtDist = d2;\n                            }\n                        }\n                        break;\n                    }\n                    case CMD.C: {\n                        var x1 = d[i++];\n                        var y1 = d[i++];\n                        var x2 = d[i++];\n                        var y2 = d[i++];\n                        var x3 = d[i++];\n                        var y3 = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                cubicSubdivide(xi, x1, x2, x3, t, tmpOutX);\n                                cubicSubdivide(yi, y1, y2, y3, t, tmpOutY);\n                                ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n                        xi = x3;\n                        yi = y3;\n                        break;\n                    }\n                    case CMD.Q: {\n                        var x1 = d[i++];\n                        var y1 = d[i++];\n                        var x2 = d[i++];\n                        var y2 = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                quadraticSubdivide(xi, x1, x2, t, tmpOutX);\n                                quadraticSubdivide(yi, y1, y2, t, tmpOutY);\n                                ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.quadraticCurveTo(x1, y1, x2, y2);\n                        xi = x2;\n                        yi = y2;\n                        break;\n                    }\n                    case CMD.A:\n                        var cx = d[i++];\n                        var cy = d[i++];\n                        var rx = d[i++];\n                        var ry = d[i++];\n                        var startAngle = d[i++];\n                        var delta = d[i++];\n                        var psi = d[i++];\n                        var anticlockwise = !d[i++];\n                        var r = (rx > ry) ? rx : ry;\n                        var isEllipse = mathAbs(rx - ry) > 1e-3;\n                        var endAngle = startAngle + delta;\n                        var breakBuild = false;\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                endAngle = startAngle + delta * (displayedLength - accumLength) / l;\n                                breakBuild = true;\n                            }\n                            accumLength += l;\n                        }\n                        if (isEllipse && ctx.ellipse) {\n                            ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise);\n                        }\n                        else {\n                            ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n                        }\n                        if (breakBuild) {\n                            break lo;\n                        }\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R:\n                        x0 = xi = d[i];\n                        y0 = yi = d[i + 1];\n                        x = d[i++];\n                        y = d[i++];\n                        var width = d[i++];\n                        var height = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var d_1 = displayedLength - accumLength;\n                                ctx.moveTo(x, y);\n                                ctx.lineTo(x + mathMin$2(d_1, width), y);\n                                d_1 -= width;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x + width, y + mathMin$2(d_1, height));\n                                }\n                                d_1 -= height;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x + mathMax$2(width - d_1, 0), y + height);\n                                }\n                                d_1 -= width;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x, y + mathMax$2(height - d_1, 0));\n                                }\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.rect(x, y, width, height);\n                        break;\n                    case CMD.Z:\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.closePath();\n                        xi = x0;\n                        yi = y0;\n                }\n            }\n        };\n        PathProxy.prototype.clone = function () {\n            var newProxy = new PathProxy();\n            var data = this.data;\n            newProxy.data = data.slice ? data.slice()\n                : Array.prototype.slice.call(data);\n            newProxy._len = this._len;\n            return newProxy;\n        };\n        PathProxy.CMD = CMD;\n        PathProxy.initDefaultProps = (function () {\n            var proto = PathProxy.prototype;\n            proto._saveData = true;\n            proto._ux = 0;\n            proto._uy = 0;\n            proto._pendingPtDist = 0;\n            proto._version = 0;\n        })();\n        return PathProxy;\n    }());\n\n    function containStroke(x0, y0, x1, y1, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        var _a = 0;\n        var _b = x0;\n        if ((y > y0 + _l && y > y1 + _l)\n            || (y < y0 - _l && y < y1 - _l)\n            || (x > x0 + _l && x > x1 + _l)\n            || (x < x0 - _l && x < x1 - _l)) {\n            return false;\n        }\n        if (x0 !== x1) {\n            _a = (y0 - y1) / (x0 - x1);\n            _b = (x0 * y1 - x1 * y0) / (x0 - x1);\n        }\n        else {\n            return Math.abs(x - x0) <= _l / 2;\n        }\n        var tmp = _a * x - y + _b;\n        var _s = tmp * tmp / (_a * _a + 1);\n        return _s <= _l / 2 * _l / 2;\n    }\n\n    function containStroke$1(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        if ((y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)\n            || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)\n            || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)\n            || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)) {\n            return false;\n        }\n        var d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null);\n        return d <= _l / 2;\n    }\n\n    function containStroke$2(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        if ((y > y0 + _l && y > y1 + _l && y > y2 + _l)\n            || (y < y0 - _l && y < y1 - _l && y < y2 - _l)\n            || (x > x0 + _l && x > x1 + _l && x > x2 + _l)\n            || (x < x0 - _l && x < x1 - _l && x < x2 - _l)) {\n            return false;\n        }\n        var d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null);\n        return d <= _l / 2;\n    }\n\n    var PI2$2 = Math.PI * 2;\n    function normalizeRadian(angle) {\n        angle %= PI2$2;\n        if (angle < 0) {\n            angle += PI2$2;\n        }\n        return angle;\n    }\n\n    var PI2$3 = Math.PI * 2;\n    function containStroke$3(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        x -= cx;\n        y -= cy;\n        var d = Math.sqrt(x * x + y * y);\n        if ((d - _l > r) || (d + _l < r)) {\n            return false;\n        }\n        if (Math.abs(startAngle - endAngle) % PI2$3 < 1e-4) {\n            return true;\n        }\n        if (anticlockwise) {\n            var tmp = startAngle;\n            startAngle = normalizeRadian(endAngle);\n            endAngle = normalizeRadian(tmp);\n        }\n        else {\n            startAngle = normalizeRadian(startAngle);\n            endAngle = normalizeRadian(endAngle);\n        }\n        if (startAngle > endAngle) {\n            endAngle += PI2$3;\n        }\n        var angle = Math.atan2(y, x);\n        if (angle < 0) {\n            angle += PI2$3;\n        }\n        return (angle >= startAngle && angle <= endAngle)\n            || (angle + PI2$3 >= startAngle && angle + PI2$3 <= endAngle);\n    }\n\n    function windingLine(x0, y0, x1, y1, x, y) {\n        if ((y > y0 && y > y1) || (y < y0 && y < y1)) {\n            return 0;\n        }\n        if (y1 === y0) {\n            return 0;\n        }\n        var t = (y - y0) / (y1 - y0);\n        var dir = y1 < y0 ? 1 : -1;\n        if (t === 1 || t === 0) {\n            dir = y1 < y0 ? 0.5 : -0.5;\n        }\n        var x_ = t * (x1 - x0) + x0;\n        return x_ === x ? Infinity : x_ > x ? dir : 0;\n    }\n\n    var CMD$1 = PathProxy.CMD;\n    var PI2$4 = Math.PI * 2;\n    var EPSILON$3 = 1e-4;\n    function isAroundEqual(a, b) {\n        return Math.abs(a - b) < EPSILON$3;\n    }\n    var roots = [-1, -1, -1];\n    var extrema = [-1, -1];\n    function swapExtrema() {\n        var tmp = extrema[0];\n        extrema[0] = extrema[1];\n        extrema[1] = tmp;\n    }\n    function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {\n        if ((y > y0 && y > y1 && y > y2 && y > y3)\n            || (y < y0 && y < y1 && y < y2 && y < y3)) {\n            return 0;\n        }\n        var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);\n        if (nRoots === 0) {\n            return 0;\n        }\n        else {\n            var w = 0;\n            var nExtrema = -1;\n            var y0_ = void 0;\n            var y1_ = void 0;\n            for (var i = 0; i < nRoots; i++) {\n                var t = roots[i];\n                var unit = (t === 0 || t === 1) ? 0.5 : 1;\n                var x_ = cubicAt(x0, x1, x2, x3, t);\n                if (x_ < x) {\n                    continue;\n                }\n                if (nExtrema < 0) {\n                    nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);\n                    if (extrema[1] < extrema[0] && nExtrema > 1) {\n                        swapExtrema();\n                    }\n                    y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);\n                    if (nExtrema > 1) {\n                        y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);\n                    }\n                }\n                if (nExtrema === 2) {\n                    if (t < extrema[0]) {\n                        w += y0_ < y0 ? unit : -unit;\n                    }\n                    else if (t < extrema[1]) {\n                        w += y1_ < y0_ ? unit : -unit;\n                    }\n                    else {\n                        w += y3 < y1_ ? unit : -unit;\n                    }\n                }\n                else {\n                    if (t < extrema[0]) {\n                        w += y0_ < y0 ? unit : -unit;\n                    }\n                    else {\n                        w += y3 < y0_ ? unit : -unit;\n                    }\n                }\n            }\n            return w;\n        }\n    }\n    function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {\n        if ((y > y0 && y > y1 && y > y2)\n            || (y < y0 && y < y1 && y < y2)) {\n            return 0;\n        }\n        var nRoots = quadraticRootAt(y0, y1, y2, y, roots);\n        if (nRoots === 0) {\n            return 0;\n        }\n        else {\n            var t = quadraticExtremum(y0, y1, y2);\n            if (t >= 0 && t <= 1) {\n                var w = 0;\n                var y_ = quadraticAt(y0, y1, y2, t);\n                for (var i = 0; i < nRoots; i++) {\n                    var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;\n                    var x_ = quadraticAt(x0, x1, x2, roots[i]);\n                    if (x_ < x) {\n                        continue;\n                    }\n                    if (roots[i] < t) {\n                        w += y_ < y0 ? unit : -unit;\n                    }\n                    else {\n                        w += y2 < y_ ? unit : -unit;\n                    }\n                }\n                return w;\n            }\n            else {\n                var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;\n                var x_ = quadraticAt(x0, x1, x2, roots[0]);\n                if (x_ < x) {\n                    return 0;\n                }\n                return y2 < y0 ? unit : -unit;\n            }\n        }\n    }\n    function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {\n        y -= cy;\n        if (y > r || y < -r) {\n            return 0;\n        }\n        var tmp = Math.sqrt(r * r - y * y);\n        roots[0] = -tmp;\n        roots[1] = tmp;\n        var dTheta = Math.abs(startAngle - endAngle);\n        if (dTheta < 1e-4) {\n            return 0;\n        }\n        if (dTheta >= PI2$4 - 1e-4) {\n            startAngle = 0;\n            endAngle = PI2$4;\n            var dir = anticlockwise ? 1 : -1;\n            if (x >= roots[0] + cx && x <= roots[1] + cx) {\n                return dir;\n            }\n            else {\n                return 0;\n            }\n        }\n        if (startAngle > endAngle) {\n            var tmp_1 = startAngle;\n            startAngle = endAngle;\n            endAngle = tmp_1;\n        }\n        if (startAngle < 0) {\n            startAngle += PI2$4;\n            endAngle += PI2$4;\n        }\n        var w = 0;\n        for (var i = 0; i < 2; i++) {\n            var x_ = roots[i];\n            if (x_ + cx > x) {\n                var angle = Math.atan2(y, x_);\n                var dir = anticlockwise ? 1 : -1;\n                if (angle < 0) {\n                    angle = PI2$4 + angle;\n                }\n                if ((angle >= startAngle && angle <= endAngle)\n                    || (angle + PI2$4 >= startAngle && angle + PI2$4 <= endAngle)) {\n                    if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {\n                        dir = -dir;\n                    }\n                    w += dir;\n                }\n            }\n        }\n        return w;\n    }\n    function containPath(path, lineWidth, isStroke, x, y) {\n        var data = path.data;\n        var len = path.len();\n        var w = 0;\n        var xi = 0;\n        var yi = 0;\n        var x0 = 0;\n        var y0 = 0;\n        var x1;\n        var y1;\n        for (var i = 0; i < len;) {\n            var cmd = data[i++];\n            var isFirst = i === 1;\n            if (cmd === CMD$1.M && i > 1) {\n                if (!isStroke) {\n                    w += windingLine(xi, yi, x0, y0, x, y);\n                }\n            }\n            if (isFirst) {\n                xi = data[i];\n                yi = data[i + 1];\n                x0 = xi;\n                y0 = yi;\n            }\n            switch (cmd) {\n                case CMD$1.M:\n                    x0 = data[i++];\n                    y0 = data[i++];\n                    xi = x0;\n                    yi = y0;\n                    break;\n                case CMD$1.L:\n                    if (isStroke) {\n                        if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.C:\n                    if (isStroke) {\n                        if (containStroke$1(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.Q:\n                    if (isStroke) {\n                        if (containStroke$2(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.A:\n                    var cx = data[i++];\n                    var cy = data[i++];\n                    var rx = data[i++];\n                    var ry = data[i++];\n                    var theta = data[i++];\n                    var dTheta = data[i++];\n                    i += 1;\n                    var anticlockwise = !!(1 - data[i++]);\n                    x1 = Math.cos(theta) * rx + cx;\n                    y1 = Math.sin(theta) * ry + cy;\n                    if (!isFirst) {\n                        w += windingLine(xi, yi, x1, y1, x, y);\n                    }\n                    else {\n                        x0 = x1;\n                        y0 = y1;\n                    }\n                    var _x = (x - cx) * ry / rx + cx;\n                    if (isStroke) {\n                        if (containStroke$3(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);\n                    }\n                    xi = Math.cos(theta + dTheta) * rx + cx;\n                    yi = Math.sin(theta + dTheta) * ry + cy;\n                    break;\n                case CMD$1.R:\n                    x0 = xi = data[i++];\n                    y0 = yi = data[i++];\n                    var width = data[i++];\n                    var height = data[i++];\n                    x1 = x0 + width;\n                    y1 = y0 + height;\n                    if (isStroke) {\n                        if (containStroke(x0, y0, x1, y0, lineWidth, x, y)\n                            || containStroke(x1, y0, x1, y1, lineWidth, x, y)\n                            || containStroke(x1, y1, x0, y1, lineWidth, x, y)\n                            || containStroke(x0, y1, x0, y0, lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(x1, y0, x1, y1, x, y);\n                        w += windingLine(x0, y1, x0, y0, x, y);\n                    }\n                    break;\n                case CMD$1.Z:\n                    if (isStroke) {\n                        if (containStroke(xi, yi, x0, y0, lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(xi, yi, x0, y0, x, y);\n                    }\n                    xi = x0;\n                    yi = y0;\n                    break;\n            }\n        }\n        if (!isStroke && !isAroundEqual(yi, y0)) {\n            w += windingLine(xi, yi, x0, y0, x, y) || 0;\n        }\n        return w !== 0;\n    }\n    function contain(pathProxy, x, y) {\n        return containPath(pathProxy, 0, false, x, y);\n    }\n    function containStroke$4(pathProxy, lineWidth, x, y) {\n        return containPath(pathProxy, lineWidth, true, x, y);\n    }\n\n    var DEFAULT_PATH_STYLE = defaults({\n        fill: '#000',\n        stroke: null,\n        strokePercent: 1,\n        fillOpacity: 1,\n        strokeOpacity: 1,\n        lineDashOffset: 0,\n        lineWidth: 1,\n        lineCap: 'butt',\n        miterLimit: 10,\n        strokeNoScale: false,\n        strokeFirst: false\n    }, DEFAULT_COMMON_STYLE);\n    var DEFAULT_PATH_ANIMATION_PROPS = {\n        style: defaults({\n            fill: true,\n            stroke: true,\n            strokePercent: true,\n            fillOpacity: true,\n            strokeOpacity: true,\n            lineDashOffset: true,\n            lineWidth: true,\n            miterLimit: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    var pathCopyParams = TRANSFORMABLE_PROPS.concat(['invisible',\n        'culling', 'z', 'z2', 'zlevel', 'parent'\n    ]);\n    var Path = (function (_super) {\n        __extends(Path, _super);\n        function Path(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Path.prototype.update = function () {\n            var _this = this;\n            _super.prototype.update.call(this);\n            var style = this.style;\n            if (style.decal) {\n                var decalEl = this._decalEl = this._decalEl || new Path();\n                if (decalEl.buildPath === Path.prototype.buildPath) {\n                    decalEl.buildPath = function (ctx) {\n                        _this.buildPath(ctx, _this.shape);\n                    };\n                }\n                decalEl.silent = true;\n                var decalElStyle = decalEl.style;\n                for (var key in style) {\n                    if (decalElStyle[key] !== style[key]) {\n                        decalElStyle[key] = style[key];\n                    }\n                }\n                decalElStyle.fill = style.fill ? style.decal : null;\n                decalElStyle.decal = null;\n                decalElStyle.shadowColor = null;\n                style.strokeFirst && (decalElStyle.stroke = null);\n                for (var i = 0; i < pathCopyParams.length; ++i) {\n                    decalEl[pathCopyParams[i]] = this[pathCopyParams[i]];\n                }\n                decalEl.__dirty |= REDRAW_BIT;\n            }\n            else if (this._decalEl) {\n                this._decalEl = null;\n            }\n        };\n        Path.prototype.getDecalElement = function () {\n            return this._decalEl;\n        };\n        Path.prototype._init = function (props) {\n            var keysArr = keys(props);\n            this.shape = this.getDefaultShape();\n            var defaultStyle = this.getDefaultStyle();\n            if (defaultStyle) {\n                this.useStyle(defaultStyle);\n            }\n            for (var i = 0; i < keysArr.length; i++) {\n                var key = keysArr[i];\n                var value = props[key];\n                if (key === 'style') {\n                    if (!this.style) {\n                        this.useStyle(value);\n                    }\n                    else {\n                        extend(this.style, value);\n                    }\n                }\n                else if (key === 'shape') {\n                    extend(this.shape, value);\n                }\n                else {\n                    _super.prototype.attrKV.call(this, key, value);\n                }\n            }\n            if (!this.style) {\n                this.useStyle({});\n            }\n        };\n        Path.prototype.getDefaultStyle = function () {\n            return null;\n        };\n        Path.prototype.getDefaultShape = function () {\n            return {};\n        };\n        Path.prototype.canBeInsideText = function () {\n            return this.hasFill();\n        };\n        Path.prototype.getInsideTextFill = function () {\n            var pathFill = this.style.fill;\n            if (pathFill !== 'none') {\n                if (isString(pathFill)) {\n                    var fillLum = lum(pathFill, 0);\n                    if (fillLum > 0.5) {\n                        return DARK_LABEL_COLOR;\n                    }\n                    else if (fillLum > 0.2) {\n                        return LIGHTER_LABEL_COLOR;\n                    }\n                    return LIGHT_LABEL_COLOR;\n                }\n                else if (pathFill) {\n                    return LIGHT_LABEL_COLOR;\n                }\n            }\n            return DARK_LABEL_COLOR;\n        };\n        Path.prototype.getInsideTextStroke = function (textFill) {\n            var pathFill = this.style.fill;\n            if (isString(pathFill)) {\n                var zr = this.__zr;\n                var isDarkMode = !!(zr && zr.isDarkMode());\n                var isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD;\n                if (isDarkMode === isDarkLabel) {\n                    return pathFill;\n                }\n            }\n        };\n        Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { };\n        Path.prototype.pathUpdated = function () {\n            this.__dirty &= ~SHAPE_CHANGED_BIT;\n        };\n        Path.prototype.getUpdatedPathProxy = function (inBatch) {\n            !this.path && this.createPathProxy();\n            this.path.beginPath();\n            this.buildPath(this.path, this.shape, inBatch);\n            return this.path;\n        };\n        Path.prototype.createPathProxy = function () {\n            this.path = new PathProxy(false);\n        };\n        Path.prototype.hasStroke = function () {\n            var style = this.style;\n            var stroke = style.stroke;\n            return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n        };\n        Path.prototype.hasFill = function () {\n            var style = this.style;\n            var fill = style.fill;\n            return fill != null && fill !== 'none';\n        };\n        Path.prototype.getBoundingRect = function () {\n            var rect = this._rect;\n            var style = this.style;\n            var needsUpdateRect = !rect;\n            if (needsUpdateRect) {\n                var firstInvoke = false;\n                if (!this.path) {\n                    firstInvoke = true;\n                    this.createPathProxy();\n                }\n                var path = this.path;\n                if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) {\n                    path.beginPath();\n                    this.buildPath(path, this.shape, false);\n                    this.pathUpdated();\n                }\n                rect = path.getBoundingRect();\n            }\n            this._rect = rect;\n            if (this.hasStroke() && this.path && this.path.len() > 0) {\n                var rectStroke = this._rectStroke || (this._rectStroke = rect.clone());\n                if (this.__dirty || needsUpdateRect) {\n                    rectStroke.copy(rect);\n                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                    var w = style.lineWidth;\n                    if (!this.hasFill()) {\n                        var strokeContainThreshold = this.strokeContainThreshold;\n                        w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold);\n                    }\n                    if (lineScale > 1e-10) {\n                        rectStroke.width += w / lineScale;\n                        rectStroke.height += w / lineScale;\n                        rectStroke.x -= w / lineScale / 2;\n                        rectStroke.y -= w / lineScale / 2;\n                    }\n                }\n                return rectStroke;\n            }\n            return rect;\n        };\n        Path.prototype.contain = function (x, y) {\n            var localPos = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            var style = this.style;\n            x = localPos[0];\n            y = localPos[1];\n            if (rect.contain(x, y)) {\n                var pathProxy = this.path;\n                if (this.hasStroke()) {\n                    var lineWidth = style.lineWidth;\n                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                    if (lineScale > 1e-10) {\n                        if (!this.hasFill()) {\n                            lineWidth = Math.max(lineWidth, this.strokeContainThreshold);\n                        }\n                        if (containStroke$4(pathProxy, lineWidth / lineScale, x, y)) {\n                            return true;\n                        }\n                    }\n                }\n                if (this.hasFill()) {\n                    return contain(pathProxy, x, y);\n                }\n            }\n            return false;\n        };\n        Path.prototype.dirtyShape = function () {\n            this.__dirty |= SHAPE_CHANGED_BIT;\n            if (this._rect) {\n                this._rect = null;\n            }\n            if (this._decalEl) {\n                this._decalEl.dirtyShape();\n            }\n            this.markRedraw();\n        };\n        Path.prototype.dirty = function () {\n            this.dirtyStyle();\n            this.dirtyShape();\n        };\n        Path.prototype.animateShape = function (loop) {\n            return this.animate('shape', loop);\n        };\n        Path.prototype.updateDuringAnimation = function (targetKey) {\n            if (targetKey === 'style') {\n                this.dirtyStyle();\n            }\n            else if (targetKey === 'shape') {\n                this.dirtyShape();\n            }\n            else {\n                this.markRedraw();\n            }\n        };\n        Path.prototype.attrKV = function (key, value) {\n            if (key === 'shape') {\n                this.setShape(value);\n            }\n            else {\n                _super.prototype.attrKV.call(this, key, value);\n            }\n        };\n        Path.prototype.setShape = function (keyOrObj, value) {\n            var shape = this.shape;\n            if (!shape) {\n                shape = this.shape = {};\n            }\n            if (typeof keyOrObj === 'string') {\n                shape[keyOrObj] = value;\n            }\n            else {\n                extend(shape, keyOrObj);\n            }\n            this.dirtyShape();\n            return this;\n        };\n        Path.prototype.shapeChanged = function () {\n            return !!(this.__dirty & SHAPE_CHANGED_BIT);\n        };\n        Path.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_PATH_STYLE, obj);\n        };\n        Path.prototype._innerSaveToNormal = function (toState) {\n            _super.prototype._innerSaveToNormal.call(this, toState);\n            var normalState = this._normalState;\n            if (toState.shape && !normalState.shape) {\n                normalState.shape = extend({}, this.shape);\n            }\n        };\n        Path.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            var targetShape;\n            if (state && state.shape) {\n                if (transition) {\n                    if (keepCurrentStates) {\n                        targetShape = state.shape;\n                    }\n                    else {\n                        targetShape = extend({}, normalState.shape);\n                        extend(targetShape, state.shape);\n                    }\n                }\n                else {\n                    targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape);\n                    extend(targetShape, state.shape);\n                }\n            }\n            else if (needsRestoreToNormal) {\n                targetShape = normalState.shape;\n            }\n            if (targetShape) {\n                if (transition) {\n                    this.shape = extend({}, this.shape);\n                    var targetShapePrimaryProps = {};\n                    var shapeKeys = keys(targetShape);\n                    for (var i = 0; i < shapeKeys.length; i++) {\n                        var key = shapeKeys[i];\n                        if (typeof targetShape[key] === 'object') {\n                            this.shape[key] = targetShape[key];\n                        }\n                        else {\n                            targetShapePrimaryProps[key] = targetShape[key];\n                        }\n                    }\n                    this._transitionState(stateName, {\n                        shape: targetShapePrimaryProps\n                    }, animationCfg);\n                }\n                else {\n                    this.shape = targetShape;\n                    this.dirtyShape();\n                }\n            }\n        };\n        Path.prototype._mergeStates = function (states) {\n            var mergedState = _super.prototype._mergeStates.call(this, states);\n            var mergedShape;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                if (state.shape) {\n                    mergedShape = mergedShape || {};\n                    this._mergeStyle(mergedShape, state.shape);\n                }\n            }\n            if (mergedShape) {\n                mergedState.shape = mergedShape;\n            }\n            return mergedState;\n        };\n        Path.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_PATH_ANIMATION_PROPS;\n        };\n        Path.prototype.isZeroArea = function () {\n            return false;\n        };\n        Path.extend = function (defaultProps) {\n            var Sub = (function (_super) {\n                __extends(Sub, _super);\n                function Sub(opts) {\n                    var _this = _super.call(this, opts) || this;\n                    defaultProps.init && defaultProps.init.call(_this, opts);\n                    return _this;\n                }\n                Sub.prototype.getDefaultStyle = function () {\n                    return clone(defaultProps.style);\n                };\n                Sub.prototype.getDefaultShape = function () {\n                    return clone(defaultProps.shape);\n                };\n                return Sub;\n            }(Path));\n            for (var key in defaultProps) {\n                if (typeof defaultProps[key] === 'function') {\n                    Sub.prototype[key] = defaultProps[key];\n                }\n            }\n            return Sub;\n        };\n        Path.initDefaultProps = (function () {\n            var pathProto = Path.prototype;\n            pathProto.type = 'path';\n            pathProto.strokeContainThreshold = 5;\n            pathProto.segmentIgnoreThreshold = 0;\n            pathProto.subPixelOptimize = false;\n            pathProto.autoBatch = false;\n            pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT;\n        })();\n        return Path;\n    }(Displayable));\n\n    var DEFAULT_TSPAN_STYLE = defaults({\n        strokeFirst: true,\n        font: DEFAULT_FONT,\n        x: 0,\n        y: 0,\n        textAlign: 'left',\n        textBaseline: 'top',\n        miterLimit: 2\n    }, DEFAULT_PATH_STYLE);\n    var TSpan = (function (_super) {\n        __extends(TSpan, _super);\n        function TSpan() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        TSpan.prototype.hasStroke = function () {\n            var style = this.style;\n            var stroke = style.stroke;\n            return stroke != null && stroke !== 'none' && style.lineWidth > 0;\n        };\n        TSpan.prototype.hasFill = function () {\n            var style = this.style;\n            var fill = style.fill;\n            return fill != null && fill !== 'none';\n        };\n        TSpan.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_TSPAN_STYLE, obj);\n        };\n        TSpan.prototype.setBoundingRect = function (rect) {\n            this._rect = rect;\n        };\n        TSpan.prototype.getBoundingRect = function () {\n            var style = this.style;\n            if (!this._rect) {\n                var text = style.text;\n                text != null ? (text += '') : (text = '');\n                var rect = getBoundingRect(text, style.font, style.textAlign, style.textBaseline);\n                rect.x += style.x || 0;\n                rect.y += style.y || 0;\n                if (this.hasStroke()) {\n                    var w = style.lineWidth;\n                    rect.x -= w / 2;\n                    rect.y -= w / 2;\n                    rect.width += w;\n                    rect.height += w;\n                }\n                this._rect = rect;\n            }\n            return this._rect;\n        };\n        TSpan.initDefaultProps = (function () {\n            var tspanProto = TSpan.prototype;\n            tspanProto.dirtyRectTolerance = 10;\n        })();\n        return TSpan;\n    }(Displayable));\n    TSpan.prototype.type = 'tspan';\n\n    var DEFAULT_IMAGE_STYLE = defaults({\n        x: 0,\n        y: 0\n    }, DEFAULT_COMMON_STYLE);\n    var DEFAULT_IMAGE_ANIMATION_PROPS = {\n        style: defaults({\n            x: true,\n            y: true,\n            width: true,\n            height: true,\n            sx: true,\n            sy: true,\n            sWidth: true,\n            sHeight: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    function isImageLike(source) {\n        return !!(source\n            && typeof source !== 'string'\n            && source.width && source.height);\n    }\n    var ZRImage = (function (_super) {\n        __extends(ZRImage, _super);\n        function ZRImage() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        ZRImage.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_IMAGE_STYLE, obj);\n        };\n        ZRImage.prototype._getSize = function (dim) {\n            var style = this.style;\n            var size = style[dim];\n            if (size != null) {\n                return size;\n            }\n            var imageSource = isImageLike(style.image)\n                ? style.image : this.__image;\n            if (!imageSource) {\n                return 0;\n            }\n            var otherDim = dim === 'width' ? 'height' : 'width';\n            var otherDimSize = style[otherDim];\n            if (otherDimSize == null) {\n                return imageSource[dim];\n            }\n            else {\n                return imageSource[dim] / imageSource[otherDim] * otherDimSize;\n            }\n        };\n        ZRImage.prototype.getWidth = function () {\n            return this._getSize('width');\n        };\n        ZRImage.prototype.getHeight = function () {\n            return this._getSize('height');\n        };\n        ZRImage.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_IMAGE_ANIMATION_PROPS;\n        };\n        ZRImage.prototype.getBoundingRect = function () {\n            var style = this.style;\n            if (!this._rect) {\n                this._rect = new BoundingRect(style.x || 0, style.y || 0, this.getWidth(), this.getHeight());\n            }\n            return this._rect;\n        };\n        return ZRImage;\n    }(Displayable));\n    ZRImage.prototype.type = 'image';\n\n    function buildPath(ctx, shape) {\n        var x = shape.x;\n        var y = shape.y;\n        var width = shape.width;\n        var height = shape.height;\n        var r = shape.r;\n        var r1;\n        var r2;\n        var r3;\n        var r4;\n        if (width < 0) {\n            x = x + width;\n            width = -width;\n        }\n        if (height < 0) {\n            y = y + height;\n            height = -height;\n        }\n        if (typeof r === 'number') {\n            r1 = r2 = r3 = r4 = r;\n        }\n        else if (r instanceof Array) {\n            if (r.length === 1) {\n                r1 = r2 = r3 = r4 = r[0];\n            }\n            else if (r.length === 2) {\n                r1 = r3 = r[0];\n                r2 = r4 = r[1];\n            }\n            else if (r.length === 3) {\n                r1 = r[0];\n                r2 = r4 = r[1];\n                r3 = r[2];\n            }\n            else {\n                r1 = r[0];\n                r2 = r[1];\n                r3 = r[2];\n                r4 = r[3];\n            }\n        }\n        else {\n            r1 = r2 = r3 = r4 = 0;\n        }\n        var total;\n        if (r1 + r2 > width) {\n            total = r1 + r2;\n            r1 *= width / total;\n            r2 *= width / total;\n        }\n        if (r3 + r4 > width) {\n            total = r3 + r4;\n            r3 *= width / total;\n            r4 *= width / total;\n        }\n        if (r2 + r3 > height) {\n            total = r2 + r3;\n            r2 *= height / total;\n            r3 *= height / total;\n        }\n        if (r1 + r4 > height) {\n            total = r1 + r4;\n            r1 *= height / total;\n            r4 *= height / total;\n        }\n        ctx.moveTo(x + r1, y);\n        ctx.lineTo(x + width - r2, y);\n        r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0);\n        ctx.lineTo(x + width, y + height - r3);\n        r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2);\n        ctx.lineTo(x + r4, y + height);\n        r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI);\n        ctx.lineTo(x, y + r1);\n        r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5);\n    }\n\n    var round$1 = Math.round;\n    function subPixelOptimizeLine(outputShape, inputShape, style) {\n        if (!inputShape) {\n            return;\n        }\n        var x1 = inputShape.x1;\n        var x2 = inputShape.x2;\n        var y1 = inputShape.y1;\n        var y2 = inputShape.y2;\n        outputShape.x1 = x1;\n        outputShape.x2 = x2;\n        outputShape.y1 = y1;\n        outputShape.y2 = y2;\n        var lineWidth = style && style.lineWidth;\n        if (!lineWidth) {\n            return outputShape;\n        }\n        if (round$1(x1 * 2) === round$1(x2 * 2)) {\n            outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true);\n        }\n        if (round$1(y1 * 2) === round$1(y2 * 2)) {\n            outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true);\n        }\n        return outputShape;\n    }\n    function subPixelOptimizeRect(outputShape, inputShape, style) {\n        if (!inputShape) {\n            return;\n        }\n        var originX = inputShape.x;\n        var originY = inputShape.y;\n        var originWidth = inputShape.width;\n        var originHeight = inputShape.height;\n        outputShape.x = originX;\n        outputShape.y = originY;\n        outputShape.width = originWidth;\n        outputShape.height = originHeight;\n        var lineWidth = style && style.lineWidth;\n        if (!lineWidth) {\n            return outputShape;\n        }\n        outputShape.x = subPixelOptimize(originX, lineWidth, true);\n        outputShape.y = subPixelOptimize(originY, lineWidth, true);\n        outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1);\n        outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1);\n        return outputShape;\n    }\n    function subPixelOptimize(position, lineWidth, positiveOrNegative) {\n        if (!lineWidth) {\n            return position;\n        }\n        var doubledPosition = round$1(position * 2);\n        return (doubledPosition + round$1(lineWidth)) % 2 === 0\n            ? doubledPosition / 2\n            : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;\n    }\n\n    var RectShape = (function () {\n        function RectShape() {\n            this.x = 0;\n            this.y = 0;\n            this.width = 0;\n            this.height = 0;\n        }\n        return RectShape;\n    }());\n    var subPixelOptimizeOutputShape = {};\n    var Rect = (function (_super) {\n        __extends(Rect, _super);\n        function Rect(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Rect.prototype.getDefaultShape = function () {\n            return new RectShape();\n        };\n        Rect.prototype.buildPath = function (ctx, shape) {\n            var x;\n            var y;\n            var width;\n            var height;\n            if (this.subPixelOptimize) {\n                var optimizedShape = subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style);\n                x = optimizedShape.x;\n                y = optimizedShape.y;\n                width = optimizedShape.width;\n                height = optimizedShape.height;\n                optimizedShape.r = shape.r;\n                shape = optimizedShape;\n            }\n            else {\n                x = shape.x;\n                y = shape.y;\n                width = shape.width;\n                height = shape.height;\n            }\n            if (!shape.r) {\n                ctx.rect(x, y, width, height);\n            }\n            else {\n                buildPath(ctx, shape);\n            }\n        };\n        Rect.prototype.isZeroArea = function () {\n            return !this.shape.width || !this.shape.height;\n        };\n        return Rect;\n    }(Path));\n    Rect.prototype.type = 'rect';\n\n    var DEFAULT_RICH_TEXT_COLOR = {\n        fill: '#000'\n    };\n    var DEFAULT_STROKE_LINE_WIDTH = 2;\n    var DEFAULT_TEXT_ANIMATION_PROPS = {\n        style: defaults({\n            fill: true,\n            stroke: true,\n            fillOpacity: true,\n            strokeOpacity: true,\n            lineWidth: true,\n            fontSize: true,\n            lineHeight: true,\n            width: true,\n            height: true,\n            textShadowColor: true,\n            textShadowBlur: true,\n            textShadowOffsetX: true,\n            textShadowOffsetY: true,\n            backgroundColor: true,\n            padding: true,\n            borderColor: true,\n            borderWidth: true,\n            borderRadius: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    var ZRText = (function (_super) {\n        __extends(ZRText, _super);\n        function ZRText(opts) {\n            var _this = _super.call(this) || this;\n            _this.type = 'text';\n            _this._children = [];\n            _this._defaultStyle = DEFAULT_RICH_TEXT_COLOR;\n            _this.attr(opts);\n            return _this;\n        }\n        ZRText.prototype.childrenRef = function () {\n            return this._children;\n        };\n        ZRText.prototype.update = function () {\n            _super.prototype.update.call(this);\n            if (this.styleChanged()) {\n                this._updateSubTexts();\n            }\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.zlevel = this.zlevel;\n                child.z = this.z;\n                child.z2 = this.z2;\n                child.culling = this.culling;\n                child.cursor = this.cursor;\n                child.invisible = this.invisible;\n            }\n        };\n        ZRText.prototype.updateTransform = function () {\n            var innerTransformable = this.innerTransformable;\n            if (innerTransformable) {\n                innerTransformable.updateTransform();\n                if (innerTransformable.transform) {\n                    this.transform = innerTransformable.transform;\n                }\n            }\n            else {\n                _super.prototype.updateTransform.call(this);\n            }\n        };\n        ZRText.prototype.getLocalTransform = function (m) {\n            var innerTransformable = this.innerTransformable;\n            return innerTransformable\n                ? innerTransformable.getLocalTransform(m)\n                : _super.prototype.getLocalTransform.call(this, m);\n        };\n        ZRText.prototype.getComputedTransform = function () {\n            if (this.__hostTarget) {\n                this.__hostTarget.getComputedTransform();\n                this.__hostTarget.updateInnerText(true);\n            }\n            return _super.prototype.getComputedTransform.call(this);\n        };\n        ZRText.prototype._updateSubTexts = function () {\n            this._childCursor = 0;\n            normalizeTextStyle(this.style);\n            this.style.rich\n                ? this._updateRichTexts()\n                : this._updatePlainTexts();\n            this._children.length = this._childCursor;\n            this.styleUpdated();\n        };\n        ZRText.prototype.addSelfToZr = function (zr) {\n            _super.prototype.addSelfToZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                this._children[i].__zr = zr;\n            }\n        };\n        ZRText.prototype.removeSelfFromZr = function (zr) {\n            _super.prototype.removeSelfFromZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                this._children[i].__zr = null;\n            }\n        };\n        ZRText.prototype.getBoundingRect = function () {\n            if (this.styleChanged()) {\n                this._updateSubTexts();\n            }\n            if (!this._rect) {\n                var tmpRect = new BoundingRect(0, 0, 0, 0);\n                var children = this._children;\n                var tmpMat = [];\n                var rect = null;\n                for (var i = 0; i < children.length; i++) {\n                    var child = children[i];\n                    var childRect = child.getBoundingRect();\n                    var transform = child.getLocalTransform(tmpMat);\n                    if (transform) {\n                        tmpRect.copy(childRect);\n                        tmpRect.applyTransform(transform);\n                        rect = rect || tmpRect.clone();\n                        rect.union(tmpRect);\n                    }\n                    else {\n                        rect = rect || childRect.clone();\n                        rect.union(childRect);\n                    }\n                }\n                this._rect = rect || tmpRect;\n            }\n            return this._rect;\n        };\n        ZRText.prototype.setDefaultTextStyle = function (defaultTextStyle) {\n            this._defaultStyle = defaultTextStyle || DEFAULT_RICH_TEXT_COLOR;\n        };\n        ZRText.prototype.setTextContent = function (textContent) {\n            if (\"development\" !== 'production') {\n                throw new Error('Can\\'t attach text on another text');\n            }\n        };\n        ZRText.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n            if (!sourceStyle) {\n                return targetStyle;\n            }\n            var sourceRich = sourceStyle.rich;\n            var targetRich = targetStyle.rich || (sourceRich && {});\n            extend(targetStyle, sourceStyle);\n            if (sourceRich && targetRich) {\n                this._mergeRich(targetRich, sourceRich);\n                targetStyle.rich = targetRich;\n            }\n            else if (targetRich) {\n                targetStyle.rich = targetRich;\n            }\n            return targetStyle;\n        };\n        ZRText.prototype._mergeRich = function (targetRich, sourceRich) {\n            var richNames = keys(sourceRich);\n            for (var i = 0; i < richNames.length; i++) {\n                var richName = richNames[i];\n                targetRich[richName] = targetRich[richName] || {};\n                extend(targetRich[richName], sourceRich[richName]);\n            }\n        };\n        ZRText.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_TEXT_ANIMATION_PROPS;\n        };\n        ZRText.prototype._getOrCreateChild = function (Ctor) {\n            var child = this._children[this._childCursor];\n            if (!child || !(child instanceof Ctor)) {\n                child = new Ctor();\n            }\n            this._children[this._childCursor++] = child;\n            child.__zr = this.__zr;\n            child.parent = this;\n            return child;\n        };\n        ZRText.prototype._updatePlainTexts = function () {\n            var style = this.style;\n            var textFont = style.font || DEFAULT_FONT;\n            var textPadding = style.padding;\n            var text = getStyleText(style);\n            var contentBlock = parsePlainText(text, style);\n            var needDrawBg = needDrawBackground(style);\n            var bgColorDrawn = !!(style.backgroundColor);\n            var outerHeight = contentBlock.outerHeight;\n            var outerWidth = contentBlock.outerWidth;\n            var contentWidth = contentBlock.contentWidth;\n            var textLines = contentBlock.lines;\n            var lineHeight = contentBlock.lineHeight;\n            var defaultStyle = this._defaultStyle;\n            var baseX = style.x || 0;\n            var baseY = style.y || 0;\n            var textAlign = style.align || defaultStyle.align || 'left';\n            var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign || 'top';\n            var textX = baseX;\n            var textY = adjustTextY$1(baseY, contentBlock.contentHeight, verticalAlign);\n            if (needDrawBg || textPadding) {\n                var boxX = adjustTextX(baseX, outerWidth, textAlign);\n                var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign);\n                needDrawBg && this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n            }\n            textY += lineHeight / 2;\n            if (textPadding) {\n                textX = getTextXForPadding(baseX, textAlign, textPadding);\n                if (verticalAlign === 'top') {\n                    textY += textPadding[0];\n                }\n                else if (verticalAlign === 'bottom') {\n                    textY -= textPadding[2];\n                }\n            }\n            var defaultLineWidth = 0;\n            var useDefaultFill = false;\n            var textFill = getFill('fill' in style\n                ? style.fill\n                : (useDefaultFill = true, defaultStyle.fill));\n            var textStroke = getStroke('stroke' in style\n                ? style.stroke\n                : (!bgColorDrawn\n                    && (!defaultStyle.autoStroke || useDefaultFill))\n                    ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                    : null);\n            var hasShadow = style.textShadowBlur > 0;\n            var fixedBoundingRect = style.width != null\n                && (style.overflow === 'truncate' || style.overflow === 'break' || style.overflow === 'breakAll');\n            var calculatedLineHeight = contentBlock.calculatedLineHeight;\n            for (var i = 0; i < textLines.length; i++) {\n                var el = this._getOrCreateChild(TSpan);\n                var subElStyle = el.createStyle();\n                el.useStyle(subElStyle);\n                subElStyle.text = textLines[i];\n                subElStyle.x = textX;\n                subElStyle.y = textY;\n                if (textAlign) {\n                    subElStyle.textAlign = textAlign;\n                }\n                subElStyle.textBaseline = 'middle';\n                subElStyle.opacity = style.opacity;\n                subElStyle.strokeFirst = true;\n                if (hasShadow) {\n                    subElStyle.shadowBlur = style.textShadowBlur || 0;\n                    subElStyle.shadowColor = style.textShadowColor || 'transparent';\n                    subElStyle.shadowOffsetX = style.textShadowOffsetX || 0;\n                    subElStyle.shadowOffsetY = style.textShadowOffsetY || 0;\n                }\n                subElStyle.stroke = textStroke;\n                subElStyle.fill = textFill;\n                if (textStroke) {\n                    subElStyle.lineWidth = style.lineWidth || defaultLineWidth;\n                    subElStyle.lineDash = style.lineDash;\n                    subElStyle.lineDashOffset = style.lineDashOffset || 0;\n                }\n                subElStyle.font = textFont;\n                setSeparateFont(subElStyle, style);\n                textY += lineHeight;\n                if (fixedBoundingRect) {\n                    el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, style.width, subElStyle.textAlign), adjustTextY$1(subElStyle.y, calculatedLineHeight, subElStyle.textBaseline), contentWidth, calculatedLineHeight));\n                }\n            }\n        };\n        ZRText.prototype._updateRichTexts = function () {\n            var style = this.style;\n            var text = getStyleText(style);\n            var contentBlock = parseRichText(text, style);\n            var contentWidth = contentBlock.width;\n            var outerWidth = contentBlock.outerWidth;\n            var outerHeight = contentBlock.outerHeight;\n            var textPadding = style.padding;\n            var baseX = style.x || 0;\n            var baseY = style.y || 0;\n            var defaultStyle = this._defaultStyle;\n            var textAlign = style.align || defaultStyle.align;\n            var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign;\n            var boxX = adjustTextX(baseX, outerWidth, textAlign);\n            var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign);\n            var xLeft = boxX;\n            var lineTop = boxY;\n            if (textPadding) {\n                xLeft += textPadding[3];\n                lineTop += textPadding[0];\n            }\n            var xRight = xLeft + contentWidth;\n            if (needDrawBackground(style)) {\n                this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n            }\n            var bgColorDrawn = !!(style.backgroundColor);\n            for (var i = 0; i < contentBlock.lines.length; i++) {\n                var line = contentBlock.lines[i];\n                var tokens = line.tokens;\n                var tokenCount = tokens.length;\n                var lineHeight = line.lineHeight;\n                var remainedWidth = line.width;\n                var leftIndex = 0;\n                var lineXLeft = xLeft;\n                var lineXRight = xRight;\n                var rightIndex = tokenCount - 1;\n                var token = void 0;\n                while (leftIndex < tokenCount\n                    && (token = tokens[leftIndex], !token.align || token.align === 'left')) {\n                    this._placeToken(token, style, lineHeight, lineTop, lineXLeft, 'left', bgColorDrawn);\n                    remainedWidth -= token.width;\n                    lineXLeft += token.width;\n                    leftIndex++;\n                }\n                while (rightIndex >= 0\n                    && (token = tokens[rightIndex], token.align === 'right')) {\n                    this._placeToken(token, style, lineHeight, lineTop, lineXRight, 'right', bgColorDrawn);\n                    remainedWidth -= token.width;\n                    lineXRight -= token.width;\n                    rightIndex--;\n                }\n                lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - remainedWidth) / 2;\n                while (leftIndex <= rightIndex) {\n                    token = tokens[leftIndex];\n                    this._placeToken(token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center', bgColorDrawn);\n                    lineXLeft += token.width;\n                    leftIndex++;\n                }\n                lineTop += lineHeight;\n            }\n        };\n        ZRText.prototype._placeToken = function (token, style, lineHeight, lineTop, x, textAlign, parentBgColorDrawn) {\n            var tokenStyle = style.rich[token.styleName] || {};\n            tokenStyle.text = token.text;\n            var verticalAlign = token.verticalAlign;\n            var y = lineTop + lineHeight / 2;\n            if (verticalAlign === 'top') {\n                y = lineTop + token.height / 2;\n            }\n            else if (verticalAlign === 'bottom') {\n                y = lineTop + lineHeight - token.height / 2;\n            }\n            var needDrawBg = !token.isLineHolder && needDrawBackground(tokenStyle);\n            needDrawBg && this._renderBackground(tokenStyle, style, textAlign === 'right'\n                ? x - token.width\n                : textAlign === 'center'\n                    ? x - token.width / 2\n                    : x, y - token.height / 2, token.width, token.height);\n            var bgColorDrawn = !!tokenStyle.backgroundColor;\n            var textPadding = token.textPadding;\n            if (textPadding) {\n                x = getTextXForPadding(x, textAlign, textPadding);\n                y -= token.height / 2 - textPadding[0] - token.innerHeight / 2;\n            }\n            var el = this._getOrCreateChild(TSpan);\n            var subElStyle = el.createStyle();\n            el.useStyle(subElStyle);\n            var defaultStyle = this._defaultStyle;\n            var useDefaultFill = false;\n            var defaultLineWidth = 0;\n            var textFill = getFill('fill' in tokenStyle ? tokenStyle.fill\n                : 'fill' in style ? style.fill\n                    : (useDefaultFill = true, defaultStyle.fill));\n            var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke\n                : 'stroke' in style ? style.stroke\n                    : (!bgColorDrawn\n                        && !parentBgColorDrawn\n                        && (!defaultStyle.autoStroke || useDefaultFill)) ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                        : null);\n            var hasShadow = tokenStyle.textShadowBlur > 0\n                || style.textShadowBlur > 0;\n            subElStyle.text = token.text;\n            subElStyle.x = x;\n            subElStyle.y = y;\n            if (hasShadow) {\n                subElStyle.shadowBlur = tokenStyle.textShadowBlur || style.textShadowBlur || 0;\n                subElStyle.shadowColor = tokenStyle.textShadowColor || style.textShadowColor || 'transparent';\n                subElStyle.shadowOffsetX = tokenStyle.textShadowOffsetX || style.textShadowOffsetX || 0;\n                subElStyle.shadowOffsetY = tokenStyle.textShadowOffsetY || style.textShadowOffsetY || 0;\n            }\n            subElStyle.textAlign = textAlign;\n            subElStyle.textBaseline = 'middle';\n            subElStyle.font = token.font || DEFAULT_FONT;\n            subElStyle.opacity = retrieve3(tokenStyle.opacity, style.opacity, 1);\n            setSeparateFont(subElStyle, tokenStyle);\n            if (textStroke) {\n                subElStyle.lineWidth = retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultLineWidth);\n                subElStyle.lineDash = retrieve2(tokenStyle.lineDash, style.lineDash);\n                subElStyle.lineDashOffset = style.lineDashOffset || 0;\n                subElStyle.stroke = textStroke;\n            }\n            if (textFill) {\n                subElStyle.fill = textFill;\n            }\n            var textWidth = token.contentWidth;\n            var textHeight = token.contentHeight;\n            el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, textWidth, subElStyle.textAlign), adjustTextY$1(subElStyle.y, textHeight, subElStyle.textBaseline), textWidth, textHeight));\n        };\n        ZRText.prototype._renderBackground = function (style, topStyle, x, y, width, height) {\n            var textBackgroundColor = style.backgroundColor;\n            var textBorderWidth = style.borderWidth;\n            var textBorderColor = style.borderColor;\n            var isImageBg = textBackgroundColor && textBackgroundColor.image;\n            var isPlainOrGradientBg = textBackgroundColor && !isImageBg;\n            var textBorderRadius = style.borderRadius;\n            var self = this;\n            var rectEl;\n            var imgEl;\n            if (isPlainOrGradientBg || style.lineHeight || (textBorderWidth && textBorderColor)) {\n                rectEl = this._getOrCreateChild(Rect);\n                rectEl.useStyle(rectEl.createStyle());\n                rectEl.style.fill = null;\n                var rectShape = rectEl.shape;\n                rectShape.x = x;\n                rectShape.y = y;\n                rectShape.width = width;\n                rectShape.height = height;\n                rectShape.r = textBorderRadius;\n                rectEl.dirtyShape();\n            }\n            if (isPlainOrGradientBg) {\n                var rectStyle = rectEl.style;\n                rectStyle.fill = textBackgroundColor || null;\n                rectStyle.fillOpacity = retrieve2(style.fillOpacity, 1);\n            }\n            else if (isImageBg) {\n                imgEl = this._getOrCreateChild(ZRImage);\n                imgEl.onload = function () {\n                    self.dirtyStyle();\n                };\n                var imgStyle = imgEl.style;\n                imgStyle.image = textBackgroundColor.image;\n                imgStyle.x = x;\n                imgStyle.y = y;\n                imgStyle.width = width;\n                imgStyle.height = height;\n            }\n            if (textBorderWidth && textBorderColor) {\n                var rectStyle = rectEl.style;\n                rectStyle.lineWidth = textBorderWidth;\n                rectStyle.stroke = textBorderColor;\n                rectStyle.strokeOpacity = retrieve2(style.strokeOpacity, 1);\n                rectStyle.lineDash = style.borderDash;\n                rectStyle.lineDashOffset = style.borderDashOffset || 0;\n                rectEl.strokeContainThreshold = 0;\n                if (rectEl.hasFill() && rectEl.hasStroke()) {\n                    rectStyle.strokeFirst = true;\n                    rectStyle.lineWidth *= 2;\n                }\n            }\n            var commonStyle = (rectEl || imgEl).style;\n            commonStyle.shadowBlur = style.shadowBlur || 0;\n            commonStyle.shadowColor = style.shadowColor || 'transparent';\n            commonStyle.shadowOffsetX = style.shadowOffsetX || 0;\n            commonStyle.shadowOffsetY = style.shadowOffsetY || 0;\n            commonStyle.opacity = retrieve3(style.opacity, topStyle.opacity, 1);\n        };\n        ZRText.makeFont = function (style) {\n            var font = '';\n            if (hasSeparateFont(style)) {\n                font = [\n                    style.fontStyle,\n                    style.fontWeight,\n                    parseFontSize(style.fontSize),\n                    style.fontFamily || 'sans-serif'\n                ].join(' ');\n            }\n            return font && trim(font) || style.textFont || style.font;\n        };\n        return ZRText;\n    }(Displayable));\n    var VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 };\n    var VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 };\n    var FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];\n    function parseFontSize(fontSize) {\n        if (typeof fontSize === 'string'\n            && (fontSize.indexOf('px') !== -1\n                || fontSize.indexOf('rem') !== -1\n                || fontSize.indexOf('em') !== -1)) {\n            return fontSize;\n        }\n        else if (!isNaN(+fontSize)) {\n            return fontSize + 'px';\n        }\n        else {\n            return DEFAULT_FONT_SIZE + 'px';\n        }\n    }\n    function setSeparateFont(targetStyle, sourceStyle) {\n        for (var i = 0; i < FONT_PARTS.length; i++) {\n            var fontProp = FONT_PARTS[i];\n            var val = sourceStyle[fontProp];\n            if (val != null) {\n                targetStyle[fontProp] = val;\n            }\n        }\n    }\n    function hasSeparateFont(style) {\n        return style.fontSize != null || style.fontFamily || style.fontWeight;\n    }\n    function normalizeTextStyle(style) {\n        normalizeStyle(style);\n        each(style.rich, normalizeStyle);\n        return style;\n    }\n    function normalizeStyle(style) {\n        if (style) {\n            style.font = ZRText.makeFont(style);\n            var textAlign = style.align;\n            textAlign === 'middle' && (textAlign = 'center');\n            style.align = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left';\n            var verticalAlign = style.verticalAlign;\n            verticalAlign === 'center' && (verticalAlign = 'middle');\n            style.verticalAlign = (verticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[verticalAlign]) ? verticalAlign : 'top';\n            var textPadding = style.padding;\n            if (textPadding) {\n                style.padding = normalizeCssArray(style.padding);\n            }\n        }\n    }\n    function getStroke(stroke, lineWidth) {\n        return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')\n            ? null\n            : (stroke.image || stroke.colorStops)\n                ? '#000'\n                : stroke;\n    }\n    function getFill(fill) {\n        return (fill == null || fill === 'none')\n            ? null\n            : (fill.image || fill.colorStops)\n                ? '#000'\n                : fill;\n    }\n    function getTextXForPadding(x, textAlign, textPadding) {\n        return textAlign === 'right'\n            ? (x - textPadding[1])\n            : textAlign === 'center'\n                ? (x + textPadding[3] / 2 - textPadding[1] / 2)\n                : (x + textPadding[3]);\n    }\n    function getStyleText(style) {\n        var text = style.text;\n        text != null && (text += '');\n        return text;\n    }\n    function needDrawBackground(style) {\n        return !!(style.backgroundColor\n            || style.lineHeight\n            || (style.borderWidth && style.borderColor));\n    }\n\n    var getECData = makeInner();\n    var setCommonECData = function (seriesIndex, dataType, dataIdx, el) {\n      if (el) {\n        var ecData = getECData(el); // Add data index and series index for indexing the data by element\n        // Useful in tooltip\n\n        ecData.dataIndex = dataIdx;\n        ecData.dataType = dataType;\n        ecData.seriesIndex = seriesIndex; // TODO: not store dataIndex on children.\n\n        if (el.type === 'group') {\n          el.traverse(function (child) {\n            var childECData = getECData(child);\n            childECData.seriesIndex = seriesIndex;\n            childECData.dataIndex = dataIdx;\n            childECData.dataType = dataType;\n          });\n        }\n      }\n    };\n\n    var _highlightNextDigit = 1;\n    var _highlightKeyMap = {};\n    var getSavedStates = makeInner();\n    var getComponentStates = makeInner();\n    var HOVER_STATE_NORMAL = 0;\n    var HOVER_STATE_BLUR = 1;\n    var HOVER_STATE_EMPHASIS = 2;\n    var SPECIAL_STATES = ['emphasis', 'blur', 'select'];\n    var DISPLAY_STATES = ['normal', 'emphasis', 'blur', 'select'];\n    var Z2_EMPHASIS_LIFT = 10;\n    var Z2_SELECT_LIFT = 9;\n    var HIGHLIGHT_ACTION_TYPE = 'highlight';\n    var DOWNPLAY_ACTION_TYPE = 'downplay';\n    var SELECT_ACTION_TYPE = 'select';\n    var UNSELECT_ACTION_TYPE = 'unselect';\n    var TOGGLE_SELECT_ACTION_TYPE = 'toggleSelect';\n\n    function hasFillOrStroke(fillOrStroke) {\n      return fillOrStroke != null && fillOrStroke !== 'none';\n    } // Most lifted color are duplicated.\n\n\n    var liftedColorCache = new LRU(100);\n\n    function liftColor(color$1) {\n      if (isString(color$1)) {\n        var liftedColor = liftedColorCache.get(color$1);\n\n        if (!liftedColor) {\n          liftedColor = lift(color$1, -0.1);\n          liftedColorCache.put(color$1, liftedColor);\n        }\n\n        return liftedColor;\n      } else if (isGradientObject(color$1)) {\n        var ret = extend({}, color$1);\n        ret.colorStops = map(color$1.colorStops, function (stop) {\n          return {\n            offset: stop.offset,\n            color: lift(stop.color, -0.1)\n          };\n        });\n        return ret;\n      } // Change nothing.\n\n\n      return color$1;\n    }\n\n    function doChangeHoverState(el, stateName, hoverStateEnum) {\n      if (el.onHoverStateChange && (el.hoverState || 0) !== hoverStateEnum) {\n        el.onHoverStateChange(stateName);\n      }\n\n      el.hoverState = hoverStateEnum;\n    }\n\n    function singleEnterEmphasis(el) {\n      // Only mark the flag.\n      // States will be applied in the echarts.ts in next frame.\n      doChangeHoverState(el, 'emphasis', HOVER_STATE_EMPHASIS);\n    }\n\n    function singleLeaveEmphasis(el) {\n      // Only mark the flag.\n      // States will be applied in the echarts.ts in next frame.\n      if (el.hoverState === HOVER_STATE_EMPHASIS) {\n        doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n      }\n    }\n\n    function singleEnterBlur(el) {\n      doChangeHoverState(el, 'blur', HOVER_STATE_BLUR);\n    }\n\n    function singleLeaveBlur(el) {\n      if (el.hoverState === HOVER_STATE_BLUR) {\n        doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n      }\n    }\n\n    function singleEnterSelect(el) {\n      el.selected = true;\n    }\n\n    function singleLeaveSelect(el) {\n      el.selected = false;\n    }\n\n    function updateElementState(el, updater, commonParam) {\n      updater(el, commonParam);\n    }\n\n    function traverseUpdateState(el, updater, commonParam) {\n      updateElementState(el, updater, commonParam);\n      el.isGroup && el.traverse(function (child) {\n        updateElementState(child, updater, commonParam);\n      });\n    }\n\n    function setStatesFlag(el, stateName) {\n      switch (stateName) {\n        case 'emphasis':\n          el.hoverState = HOVER_STATE_EMPHASIS;\n          break;\n\n        case 'normal':\n          el.hoverState = HOVER_STATE_NORMAL;\n          break;\n\n        case 'blur':\n          el.hoverState = HOVER_STATE_BLUR;\n          break;\n\n        case 'select':\n          el.selected = true;\n      }\n    }\n\n    function getFromStateStyle(el, props, toStateName, defaultValue) {\n      var style = el.style;\n      var fromState = {};\n\n      for (var i = 0; i < props.length; i++) {\n        var propName = props[i];\n        var val = style[propName];\n        fromState[propName] = val == null ? defaultValue && defaultValue[propName] : val;\n      }\n\n      for (var i = 0; i < el.animators.length; i++) {\n        var animator = el.animators[i];\n\n        if (animator.__fromStateTransition // Don't consider the animation to emphasis state.\n        && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') {\n          animator.saveTo(fromState, props);\n        }\n      }\n\n      return fromState;\n    }\n\n    function createEmphasisDefaultState(el, stateName, targetStates, state) {\n      var hasSelect = targetStates && indexOf(targetStates, 'select') >= 0;\n      var cloned = false;\n\n      if (el instanceof Path) {\n        var store = getSavedStates(el);\n        var fromFill = hasSelect ? store.selectFill || store.normalFill : store.normalFill;\n        var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke;\n\n        if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) {\n          state = state || {};\n          var emphasisStyle = state.style || {}; // inherit case\n\n          if (emphasisStyle.fill === 'inherit') {\n            cloned = true;\n            state = extend({}, state);\n            emphasisStyle = extend({}, emphasisStyle);\n            emphasisStyle.fill = fromFill;\n          } // Apply default color lift\n          else if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) {\n              cloned = true; // Not modify the original value.\n\n              state = extend({}, state);\n              emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times.\n\n              emphasisStyle.fill = liftColor(fromFill);\n            } // Not highlight stroke if fill has been highlighted.\n            else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) {\n                if (!cloned) {\n                  state = extend({}, state);\n                  emphasisStyle = extend({}, emphasisStyle);\n                }\n\n                emphasisStyle.stroke = liftColor(fromStroke);\n              }\n\n          state.style = emphasisStyle;\n        }\n      }\n\n      if (state) {\n        // TODO Share with textContent?\n        if (state.z2 == null) {\n          if (!cloned) {\n            state = extend({}, state);\n          }\n\n          var z2EmphasisLift = el.z2EmphasisLift;\n          state.z2 = el.z2 + (z2EmphasisLift != null ? z2EmphasisLift : Z2_EMPHASIS_LIFT);\n        }\n      }\n\n      return state;\n    }\n\n    function createSelectDefaultState(el, stateName, state) {\n      // const hasSelect = indexOf(el.currentStates, stateName) >= 0;\n      if (state) {\n        // TODO Share with textContent?\n        if (state.z2 == null) {\n          state = extend({}, state);\n          var z2SelectLift = el.z2SelectLift;\n          state.z2 = el.z2 + (z2SelectLift != null ? z2SelectLift : Z2_SELECT_LIFT);\n        }\n      }\n\n      return state;\n    }\n\n    function createBlurDefaultState(el, stateName, state) {\n      var hasBlur = indexOf(el.currentStates, stateName) >= 0;\n      var currentOpacity = el.style.opacity;\n      var fromState = !hasBlur ? getFromStateStyle(el, ['opacity'], stateName, {\n        opacity: 1\n      }) : null;\n      state = state || {};\n      var blurStyle = state.style || {};\n\n      if (blurStyle.opacity == null) {\n        // clone state\n        state = extend({}, state);\n        blurStyle = extend({\n          // Already being applied 'emphasis'. DON'T mul opacity multiple times.\n          opacity: hasBlur ? currentOpacity : fromState.opacity * 0.1\n        }, blurStyle);\n        state.style = blurStyle;\n      }\n\n      return state;\n    }\n\n    function elementStateProxy(stateName, targetStates) {\n      var state = this.states[stateName];\n\n      if (this.style) {\n        if (stateName === 'emphasis') {\n          return createEmphasisDefaultState(this, stateName, targetStates, state);\n        } else if (stateName === 'blur') {\n          return createBlurDefaultState(this, stateName, state);\n        } else if (stateName === 'select') {\n          return createSelectDefaultState(this, stateName, state);\n        }\n      }\n\n      return state;\n    }\n    /**\n     * Set hover style (namely \"emphasis style\") of element.\n     * @param el Should not be `zrender/graphic/Group`.\n     * @param focus 'self' | 'selfInSeries' | 'series'\n     */\n\n\n    function setDefaultStateProxy(el) {\n      el.stateProxy = elementStateProxy;\n      var textContent = el.getTextContent();\n      var textGuide = el.getTextGuideLine();\n\n      if (textContent) {\n        textContent.stateProxy = elementStateProxy;\n      }\n\n      if (textGuide) {\n        textGuide.stateProxy = elementStateProxy;\n      }\n    }\n    function enterEmphasisWhenMouseOver(el, e) {\n      !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n      && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis);\n    }\n    function leaveEmphasisWhenMouseOut(el, e) {\n      !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n      && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis);\n    }\n    function enterEmphasis(el, highlightDigit) {\n      el.__highByOuter |= 1 << (highlightDigit || 0);\n      traverseUpdateState(el, singleEnterEmphasis);\n    }\n    function leaveEmphasis(el, highlightDigit) {\n      !(el.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdateState(el, singleLeaveEmphasis);\n    }\n    function enterBlur(el) {\n      traverseUpdateState(el, singleEnterBlur);\n    }\n    function leaveBlur(el) {\n      traverseUpdateState(el, singleLeaveBlur);\n    }\n    function enterSelect(el) {\n      traverseUpdateState(el, singleEnterSelect);\n    }\n    function leaveSelect(el) {\n      traverseUpdateState(el, singleLeaveSelect);\n    }\n\n    function shouldSilent(el, e) {\n      return el.__highDownSilentOnTouch && e.zrByTouch;\n    }\n\n    function allLeaveBlur(api) {\n      var model = api.getModel();\n      var leaveBlurredSeries = [];\n      var allComponentViews = [];\n      model.eachComponent(function (componentType, componentModel) {\n        var componentStates = getComponentStates(componentModel);\n        var isSeries = componentType === 'series';\n        var view = isSeries ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel);\n        !isSeries && allComponentViews.push(view);\n\n        if (componentStates.isBlured) {\n          // Leave blur anyway\n          view.group.traverse(function (child) {\n            singleLeaveBlur(child);\n          });\n          isSeries && leaveBlurredSeries.push(componentModel);\n        }\n\n        componentStates.isBlured = false;\n      });\n      each(allComponentViews, function (view) {\n        if (view && view.toggleBlurSeries) {\n          view.toggleBlurSeries(leaveBlurredSeries, false, model);\n        }\n      });\n    }\n    function blurSeries(targetSeriesIndex, focus, blurScope, api) {\n      var ecModel = api.getModel();\n      blurScope = blurScope || 'coordinateSystem';\n\n      function leaveBlurOfIndices(data, dataIndices) {\n        for (var i = 0; i < dataIndices.length; i++) {\n          var itemEl = data.getItemGraphicEl(dataIndices[i]);\n          itemEl && leaveBlur(itemEl);\n        }\n      }\n\n      if (targetSeriesIndex == null) {\n        return;\n      }\n\n      if (!focus || focus === 'none') {\n        return;\n      }\n\n      var targetSeriesModel = ecModel.getSeriesByIndex(targetSeriesIndex);\n      var targetCoordSys = targetSeriesModel.coordinateSystem;\n\n      if (targetCoordSys && targetCoordSys.master) {\n        targetCoordSys = targetCoordSys.master;\n      }\n\n      var blurredSeries = [];\n      ecModel.eachSeries(function (seriesModel) {\n        var sameSeries = targetSeriesModel === seriesModel;\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys && coordSys.master) {\n          coordSys = coordSys.master;\n        }\n\n        var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries; // If there is no coordinate system. use sameSeries instead.\n\n        if (!( // Not blur other series if blurScope series\n        blurScope === 'series' && !sameSeries // Not blur other coordinate system if blurScope is coordinateSystem\n        || blurScope === 'coordinateSystem' && !sameCoordSys // Not blur self series if focus is series.\n        || focus === 'series' && sameSeries // TODO blurScope: coordinate system\n        )) {\n          var view = api.getViewOfSeriesModel(seriesModel);\n          view.group.traverse(function (child) {\n            singleEnterBlur(child);\n          });\n\n          if (isArrayLike(focus)) {\n            leaveBlurOfIndices(seriesModel.getData(), focus);\n          } else if (isObject(focus)) {\n            var dataTypes = keys(focus);\n\n            for (var d = 0; d < dataTypes.length; d++) {\n              leaveBlurOfIndices(seriesModel.getData(dataTypes[d]), focus[dataTypes[d]]);\n            }\n          }\n\n          blurredSeries.push(seriesModel);\n          getComponentStates(seriesModel).isBlured = true;\n        }\n      });\n      ecModel.eachComponent(function (componentType, componentModel) {\n        if (componentType === 'series') {\n          return;\n        }\n\n        var view = api.getViewOfComponentModel(componentModel);\n\n        if (view && view.toggleBlurSeries) {\n          view.toggleBlurSeries(blurredSeries, true, ecModel);\n        }\n      });\n    }\n    function blurComponent(componentMainType, componentIndex, api) {\n      if (componentMainType == null || componentIndex == null) {\n        return;\n      }\n\n      var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n      if (!componentModel) {\n        return;\n      }\n\n      getComponentStates(componentModel).isBlured = true;\n      var view = api.getViewOfComponentModel(componentModel);\n\n      if (!view || !view.focusBlurEnabled) {\n        return;\n      }\n\n      view.group.traverse(function (child) {\n        singleEnterBlur(child);\n      });\n    }\n    function blurSeriesFromHighlightPayload(seriesModel, payload, api) {\n      var seriesIndex = seriesModel.seriesIndex;\n      var data = seriesModel.getData(payload.dataType);\n\n      if (!data) {\n        if (\"development\" !== 'production') {\n          error(\"Unknown dataType \" + payload.dataType);\n        }\n\n        return;\n      }\n\n      var dataIndex = queryDataIndex(data, payload); // Pick the first one if there is multiple/none exists.\n\n      dataIndex = (isArray(dataIndex) ? dataIndex[0] : dataIndex) || 0;\n      var el = data.getItemGraphicEl(dataIndex);\n\n      if (!el) {\n        var count = data.count();\n        var current = 0; // If data on dataIndex is NaN.\n\n        while (!el && current < count) {\n          el = data.getItemGraphicEl(current++);\n        }\n      }\n\n      if (el) {\n        var ecData = getECData(el);\n        blurSeries(seriesIndex, ecData.focus, ecData.blurScope, api);\n      } else {\n        // If there is no element put on the data. Try getting it from raw option\n        // TODO Should put it on seriesModel?\n        var focus_1 = seriesModel.get(['emphasis', 'focus']);\n        var blurScope = seriesModel.get(['emphasis', 'blurScope']);\n\n        if (focus_1 != null) {\n          blurSeries(seriesIndex, focus_1, blurScope, api);\n        }\n      }\n    }\n    function findComponentHighDownDispatchers(componentMainType, componentIndex, name, api) {\n      var ret = {\n        focusSelf: false,\n        dispatchers: null\n      };\n\n      if (componentMainType == null || componentMainType === 'series' || componentIndex == null || name == null) {\n        return ret;\n      }\n\n      var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n      if (!componentModel) {\n        return ret;\n      }\n\n      var view = api.getViewOfComponentModel(componentModel);\n\n      if (!view || !view.findHighDownDispatchers) {\n        return ret;\n      }\n\n      var dispatchers = view.findHighDownDispatchers(name); // At presnet, the component (like Geo) only blur inside itself.\n      // So we do not use `blurScope` in component.\n\n      var focusSelf;\n\n      for (var i = 0; i < dispatchers.length; i++) {\n        if (\"development\" !== 'production' && !isHighDownDispatcher(dispatchers[i])) {\n          error('param should be highDownDispatcher');\n        }\n\n        if (getECData(dispatchers[i]).focus === 'self') {\n          focusSelf = true;\n          break;\n        }\n      }\n\n      return {\n        focusSelf: focusSelf,\n        dispatchers: dispatchers\n      };\n    }\n    function handleGlobalMouseOverForHighDown(dispatcher, e, api) {\n      if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n        error('param should be highDownDispatcher');\n      }\n\n      var ecData = getECData(dispatcher);\n\n      var _a = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api),\n          dispatchers = _a.dispatchers,\n          focusSelf = _a.focusSelf; // If `findHighDownDispatchers` is supported on the component,\n      // highlight/downplay elements with the same name.\n\n\n      if (dispatchers) {\n        if (focusSelf) {\n          blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n        }\n\n        each(dispatchers, function (dispatcher) {\n          return enterEmphasisWhenMouseOver(dispatcher, e);\n        });\n      } else {\n        // Try blur all in the related series. Then emphasis the hoverred.\n        // TODO. progressive mode.\n        blurSeries(ecData.seriesIndex, ecData.focus, ecData.blurScope, api);\n\n        if (ecData.focus === 'self') {\n          blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n        } // Other than series, component that not support `findHighDownDispatcher` will\n        // also use it. But in this case, highlight/downplay are only supported in\n        // mouse hover but not in dispatchAction.\n\n\n        enterEmphasisWhenMouseOver(dispatcher, e);\n      }\n    }\n    function handleGlobalMouseOutForHighDown(dispatcher, e, api) {\n      if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n        error('param should be highDownDispatcher');\n      }\n\n      allLeaveBlur(api);\n      var ecData = getECData(dispatcher);\n      var dispatchers = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api).dispatchers;\n\n      if (dispatchers) {\n        each(dispatchers, function (dispatcher) {\n          return leaveEmphasisWhenMouseOut(dispatcher, e);\n        });\n      } else {\n        leaveEmphasisWhenMouseOut(dispatcher, e);\n      }\n    }\n    function toggleSelectionFromPayload(seriesModel, payload, api) {\n      if (!isSelectChangePayload(payload)) {\n        return;\n      }\n\n      var dataType = payload.dataType;\n      var data = seriesModel.getData(dataType);\n      var dataIndex = queryDataIndex(data, payload);\n\n      if (!isArray(dataIndex)) {\n        dataIndex = [dataIndex];\n      }\n\n      seriesModel[payload.type === TOGGLE_SELECT_ACTION_TYPE ? 'toggleSelect' : payload.type === SELECT_ACTION_TYPE ? 'select' : 'unselect'](dataIndex, dataType);\n    }\n    function updateSeriesElementSelection(seriesModel) {\n      var allData = seriesModel.getAllData();\n      each(allData, function (_a) {\n        var data = _a.data,\n            type = _a.type;\n        data.eachItemGraphicEl(function (el, idx) {\n          seriesModel.isSelected(idx, type) ? enterSelect(el) : leaveSelect(el);\n        });\n      });\n    }\n    function getAllSelectedIndices(ecModel) {\n      var ret = [];\n      ecModel.eachSeries(function (seriesModel) {\n        var allData = seriesModel.getAllData();\n        each(allData, function (_a) {\n          var data = _a.data,\n              type = _a.type;\n          var dataIndices = seriesModel.getSelectedDataIndices();\n\n          if (dataIndices.length > 0) {\n            var item = {\n              dataIndex: dataIndices,\n              seriesIndex: seriesModel.seriesIndex\n            };\n\n            if (type != null) {\n              item.dataType = type;\n            }\n\n            ret.push(item);\n          }\n        });\n      });\n      return ret;\n    }\n    /**\n     * Enable the function that mouseover will trigger the emphasis state.\n     *\n     * NOTE:\n     * This function should be used on the element with dataIndex, seriesIndex.\n     *\n     */\n\n    function enableHoverEmphasis(el, focus, blurScope) {\n      setAsHighDownDispatcher(el, true);\n      traverseUpdateState(el, setDefaultStateProxy);\n      enableHoverFocus(el, focus, blurScope);\n    }\n    function disableHoverEmphasis(el) {\n      setAsHighDownDispatcher(el, false);\n    }\n    function toggleHoverEmphasis(el, focus, blurScope, isDisabled) {\n      isDisabled ? disableHoverEmphasis(el) : enableHoverEmphasis(el, focus, blurScope);\n    }\n    function enableHoverFocus(el, focus, blurScope) {\n      var ecData = getECData(el);\n\n      if (focus != null) {\n        // TODO dataIndex may be set after this function. This check is not useful.\n        // if (ecData.dataIndex == null) {\n        //     if (__DEV__) {\n        //         console.warn('focus can only been set on element with dataIndex');\n        //     }\n        // }\n        // else {\n        ecData.focus = focus;\n        ecData.blurScope = blurScope; // }\n      } else if (ecData.focus) {\n        ecData.focus = null;\n      }\n    }\n    var OTHER_STATES = ['emphasis', 'blur', 'select'];\n    var defaultStyleGetterMap = {\n      itemStyle: 'getItemStyle',\n      lineStyle: 'getLineStyle',\n      areaStyle: 'getAreaStyle'\n    };\n    /**\n     * Set emphasis/blur/selected states of element.\n     */\n\n    function setStatesStylesFromModel(el, itemModel, styleType, // default itemStyle\n    getter) {\n      styleType = styleType || 'itemStyle';\n\n      for (var i = 0; i < OTHER_STATES.length; i++) {\n        var stateName = OTHER_STATES[i];\n        var model = itemModel.getModel([stateName, styleType]);\n        var state = el.ensureState(stateName); // Let it throw error if getterType is not found.\n\n        state.style = getter ? getter(model) : model[defaultStyleGetterMap[styleType]]();\n      }\n    }\n    /**\n     *\n     * Set element as highlight / downplay dispatcher.\n     * It will be checked when element received mouseover event or from highlight action.\n     * It's in change of all highlight/downplay behavior of it's children.\n     *\n     * @param el\n     * @param el.highDownSilentOnTouch\n     *        In touch device, mouseover event will be trigger on touchstart event\n     *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can\n     *        conveniently use hoverStyle when tap on touch screen without additional\n     *        code for compatibility.\n     *        But if the chart/component has select feature, which usually also use\n     *        hoverStyle, there might be conflict between 'select-highlight' and\n     *        'hover-highlight' especially when roam is enabled (see geo for example).\n     *        In this case, `highDownSilentOnTouch` should be used to disable\n     *        hover-highlight on touch device.\n     * @param asDispatcher If `false`, do not set as \"highDownDispatcher\".\n     */\n\n    function setAsHighDownDispatcher(el, asDispatcher) {\n      var disable = asDispatcher === false;\n      var extendedEl = el; // Make `highDownSilentOnTouch` and `onStateChange` only work after\n      // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.\n\n      if (el.highDownSilentOnTouch) {\n        extendedEl.__highDownSilentOnTouch = el.highDownSilentOnTouch;\n      } // Simple optimize, since this method might be\n      // called for each elements of a group in some cases.\n\n\n      if (!disable || extendedEl.__highDownDispatcher) {\n        // Emphasis, normal can be triggered manually by API or other components like hover link.\n        // el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent);\n        // Also keep previous record.\n        extendedEl.__highByOuter = extendedEl.__highByOuter || 0;\n        extendedEl.__highDownDispatcher = !disable;\n      }\n    }\n    function isHighDownDispatcher(el) {\n      return !!(el && el.__highDownDispatcher);\n    }\n    /**\n     * Support highlight/downplay record on each elements.\n     * For the case: hover highlight/downplay (legend, visualMap, ...) and\n     * user triggered highlight/downplay should not conflict.\n     * Only all of the highlightDigit cleared, return to normal.\n     * @param {string} highlightKey\n     * @return {number} highlightDigit\n     */\n\n    function getHighlightDigit(highlightKey) {\n      var highlightDigit = _highlightKeyMap[highlightKey];\n\n      if (highlightDigit == null && _highlightNextDigit <= 32) {\n        highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++;\n      }\n\n      return highlightDigit;\n    }\n    function isSelectChangePayload(payload) {\n      var payloadType = payload.type;\n      return payloadType === SELECT_ACTION_TYPE || payloadType === UNSELECT_ACTION_TYPE || payloadType === TOGGLE_SELECT_ACTION_TYPE;\n    }\n    function isHighDownPayload(payload) {\n      var payloadType = payload.type;\n      return payloadType === HIGHLIGHT_ACTION_TYPE || payloadType === DOWNPLAY_ACTION_TYPE;\n    }\n    function savePathStates(el) {\n      var store = getSavedStates(el);\n      store.normalFill = el.style.fill;\n      store.normalStroke = el.style.stroke;\n      var selectState = el.states.select || {};\n      store.selectFill = selectState.style && selectState.style.fill || null;\n      store.selectStroke = selectState.style && selectState.style.stroke || null;\n    }\n\n    var CMD$2 = PathProxy.CMD;\n    var points = [[], [], []];\n    var mathSqrt$1 = Math.sqrt;\n    var mathAtan2 = Math.atan2;\n    function transformPath(path, m) {\n        if (!m) {\n            return;\n        }\n        var data = path.data;\n        var len = path.len();\n        var cmd;\n        var nPoint;\n        var i;\n        var j;\n        var k;\n        var p;\n        var M = CMD$2.M;\n        var C = CMD$2.C;\n        var L = CMD$2.L;\n        var R = CMD$2.R;\n        var A = CMD$2.A;\n        var Q = CMD$2.Q;\n        for (i = 0, j = 0; i < len;) {\n            cmd = data[i++];\n            j = i;\n            nPoint = 0;\n            switch (cmd) {\n                case M:\n                    nPoint = 1;\n                    break;\n                case L:\n                    nPoint = 1;\n                    break;\n                case C:\n                    nPoint = 3;\n                    break;\n                case Q:\n                    nPoint = 2;\n                    break;\n                case A:\n                    var x = m[4];\n                    var y = m[5];\n                    var sx = mathSqrt$1(m[0] * m[0] + m[1] * m[1]);\n                    var sy = mathSqrt$1(m[2] * m[2] + m[3] * m[3]);\n                    var angle = mathAtan2(-m[1] / sy, m[0] / sx);\n                    data[i] *= sx;\n                    data[i++] += x;\n                    data[i] *= sy;\n                    data[i++] += y;\n                    data[i++] *= sx;\n                    data[i++] *= sy;\n                    data[i++] += angle;\n                    data[i++] += angle;\n                    i += 2;\n                    j = i;\n                    break;\n                case R:\n                    p[0] = data[i++];\n                    p[1] = data[i++];\n                    applyTransform(p, p, m);\n                    data[j++] = p[0];\n                    data[j++] = p[1];\n                    p[0] += data[i++];\n                    p[1] += data[i++];\n                    applyTransform(p, p, m);\n                    data[j++] = p[0];\n                    data[j++] = p[1];\n            }\n            for (k = 0; k < nPoint; k++) {\n                var p_1 = points[k];\n                p_1[0] = data[i++];\n                p_1[1] = data[i++];\n                applyTransform(p_1, p_1, m);\n                data[j++] = p_1[0];\n                data[j++] = p_1[1];\n            }\n        }\n        path.increaseVersion();\n    }\n\n    var mathSqrt$2 = Math.sqrt;\n    var mathSin$2 = Math.sin;\n    var mathCos$2 = Math.cos;\n    var PI$1 = Math.PI;\n    function vMag(v) {\n        return Math.sqrt(v[0] * v[0] + v[1] * v[1]);\n    }\n    function vRatio(u, v) {\n        return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));\n    }\n    function vAngle(u, v) {\n        return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)\n            * Math.acos(vRatio(u, v));\n    }\n    function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {\n        var psi = psiDeg * (PI$1 / 180.0);\n        var xp = mathCos$2(psi) * (x1 - x2) / 2.0\n            + mathSin$2(psi) * (y1 - y2) / 2.0;\n        var yp = -1 * mathSin$2(psi) * (x1 - x2) / 2.0\n            + mathCos$2(psi) * (y1 - y2) / 2.0;\n        var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);\n        if (lambda > 1) {\n            rx *= mathSqrt$2(lambda);\n            ry *= mathSqrt$2(lambda);\n        }\n        var f = (fa === fs ? -1 : 1)\n            * mathSqrt$2((((rx * rx) * (ry * ry))\n                - ((rx * rx) * (yp * yp))\n                - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)\n                + (ry * ry) * (xp * xp))) || 0;\n        var cxp = f * rx * yp / ry;\n        var cyp = f * -ry * xp / rx;\n        var cx = (x1 + x2) / 2.0\n            + mathCos$2(psi) * cxp\n            - mathSin$2(psi) * cyp;\n        var cy = (y1 + y2) / 2.0\n            + mathSin$2(psi) * cxp\n            + mathCos$2(psi) * cyp;\n        var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);\n        var u = [(xp - cxp) / rx, (yp - cyp) / ry];\n        var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];\n        var dTheta = vAngle(u, v);\n        if (vRatio(u, v) <= -1) {\n            dTheta = PI$1;\n        }\n        if (vRatio(u, v) >= 1) {\n            dTheta = 0;\n        }\n        if (dTheta < 0) {\n            var n = Math.round(dTheta / PI$1 * 1e6) / 1e6;\n            dTheta = PI$1 * 2 + (n % 2) * PI$1;\n        }\n        path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);\n    }\n    var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig;\n    var numberReg = /-?([0-9]*\\.)?[0-9]+([eE]-?[0-9]+)?/g;\n    function createPathProxyFromString(data) {\n        var path = new PathProxy();\n        if (!data) {\n            return path;\n        }\n        var cpx = 0;\n        var cpy = 0;\n        var subpathX = cpx;\n        var subpathY = cpy;\n        var prevCmd;\n        var CMD = PathProxy.CMD;\n        var cmdList = data.match(commandReg);\n        if (!cmdList) {\n            return path;\n        }\n        for (var l = 0; l < cmdList.length; l++) {\n            var cmdText = cmdList[l];\n            var cmdStr = cmdText.charAt(0);\n            var cmd = void 0;\n            var p = cmdText.match(numberReg) || [];\n            var pLen = p.length;\n            for (var i = 0; i < pLen; i++) {\n                p[i] = parseFloat(p[i]);\n            }\n            var off = 0;\n            while (off < pLen) {\n                var ctlPtx = void 0;\n                var ctlPty = void 0;\n                var rx = void 0;\n                var ry = void 0;\n                var psi = void 0;\n                var fa = void 0;\n                var fs = void 0;\n                var x1 = cpx;\n                var y1 = cpy;\n                var len = void 0;\n                var pathData = void 0;\n                switch (cmdStr) {\n                    case 'l':\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'L':\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'm':\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.M;\n                        path.addData(cmd, cpx, cpy);\n                        subpathX = cpx;\n                        subpathY = cpy;\n                        cmdStr = 'l';\n                        break;\n                    case 'M':\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.M;\n                        path.addData(cmd, cpx, cpy);\n                        subpathX = cpx;\n                        subpathY = cpy;\n                        cmdStr = 'L';\n                        break;\n                    case 'h':\n                        cpx += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'H':\n                        cpx = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'v':\n                        cpy += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'V':\n                        cpy = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'C':\n                        cmd = CMD.C;\n                        path.addData(cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]);\n                        cpx = p[off - 2];\n                        cpy = p[off - 1];\n                        break;\n                    case 'c':\n                        cmd = CMD.C;\n                        path.addData(cmd, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy);\n                        cpx += p[off - 2];\n                        cpy += p[off - 1];\n                        break;\n                    case 'S':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.C) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cmd = CMD.C;\n                        x1 = p[off++];\n                        y1 = p[off++];\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                        break;\n                    case 's':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.C) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cmd = CMD.C;\n                        x1 = cpx + p[off++];\n                        y1 = cpy + p[off++];\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                        break;\n                    case 'Q':\n                        x1 = p[off++];\n                        y1 = p[off++];\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, x1, y1, cpx, cpy);\n                        break;\n                    case 'q':\n                        x1 = p[off++] + cpx;\n                        y1 = p[off++] + cpy;\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, x1, y1, cpx, cpy);\n                        break;\n                    case 'T':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.Q) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                        break;\n                    case 't':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.Q) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                        break;\n                    case 'A':\n                        rx = p[off++];\n                        ry = p[off++];\n                        psi = p[off++];\n                        fa = p[off++];\n                        fs = p[off++];\n                        x1 = cpx, y1 = cpy;\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.A;\n                        processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                        break;\n                    case 'a':\n                        rx = p[off++];\n                        ry = p[off++];\n                        psi = p[off++];\n                        fa = p[off++];\n                        fs = p[off++];\n                        x1 = cpx, y1 = cpy;\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.A;\n                        processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                        break;\n                }\n            }\n            if (cmdStr === 'z' || cmdStr === 'Z') {\n                cmd = CMD.Z;\n                path.addData(cmd);\n                cpx = subpathX;\n                cpy = subpathY;\n            }\n            prevCmd = cmd;\n        }\n        path.toStatic();\n        return path;\n    }\n    var SVGPath = (function (_super) {\n        __extends(SVGPath, _super);\n        function SVGPath() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        SVGPath.prototype.applyTransform = function (m) { };\n        return SVGPath;\n    }(Path));\n    function isPathProxy(path) {\n        return path.setData != null;\n    }\n    function createPathOptions(str, opts) {\n        var pathProxy = createPathProxyFromString(str);\n        var innerOpts = extend({}, opts);\n        innerOpts.buildPath = function (path) {\n            if (isPathProxy(path)) {\n                path.setData(pathProxy.data);\n                var ctx = path.getContext();\n                if (ctx) {\n                    path.rebuildPath(ctx, 1);\n                }\n            }\n            else {\n                var ctx = path;\n                pathProxy.rebuildPath(ctx, 1);\n            }\n        };\n        innerOpts.applyTransform = function (m) {\n            transformPath(pathProxy, m);\n            this.dirtyShape();\n        };\n        return innerOpts;\n    }\n    function createFromString(str, opts) {\n        return new SVGPath(createPathOptions(str, opts));\n    }\n    function extendFromString(str, defaultOpts) {\n        var innerOpts = createPathOptions(str, defaultOpts);\n        var Sub = (function (_super) {\n            __extends(Sub, _super);\n            function Sub(opts) {\n                var _this = _super.call(this, opts) || this;\n                _this.applyTransform = innerOpts.applyTransform;\n                _this.buildPath = innerOpts.buildPath;\n                return _this;\n            }\n            return Sub;\n        }(SVGPath));\n        return Sub;\n    }\n    function mergePath(pathEls, opts) {\n        var pathList = [];\n        var len = pathEls.length;\n        for (var i = 0; i < len; i++) {\n            var pathEl = pathEls[i];\n            pathList.push(pathEl.getUpdatedPathProxy(true));\n        }\n        var pathBundle = new Path(opts);\n        pathBundle.createPathProxy();\n        pathBundle.buildPath = function (path) {\n            if (isPathProxy(path)) {\n                path.appendPath(pathList);\n                var ctx = path.getContext();\n                if (ctx) {\n                    path.rebuildPath(ctx, 1);\n                }\n            }\n        };\n        return pathBundle;\n    }\n\n    var CircleShape = (function () {\n        function CircleShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n        }\n        return CircleShape;\n    }());\n    var Circle = (function (_super) {\n        __extends(Circle, _super);\n        function Circle(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Circle.prototype.getDefaultShape = function () {\n            return new CircleShape();\n        };\n        Circle.prototype.buildPath = function (ctx, shape) {\n            ctx.moveTo(shape.cx + shape.r, shape.cy);\n            ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);\n        };\n        return Circle;\n    }(Path));\n    Circle.prototype.type = 'circle';\n\n    var EllipseShape = (function () {\n        function EllipseShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.rx = 0;\n            this.ry = 0;\n        }\n        return EllipseShape;\n    }());\n    var Ellipse = (function (_super) {\n        __extends(Ellipse, _super);\n        function Ellipse(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Ellipse.prototype.getDefaultShape = function () {\n            return new EllipseShape();\n        };\n        Ellipse.prototype.buildPath = function (ctx, shape) {\n            var k = 0.5522848;\n            var x = shape.cx;\n            var y = shape.cy;\n            var a = shape.rx;\n            var b = shape.ry;\n            var ox = a * k;\n            var oy = b * k;\n            ctx.moveTo(x - a, y);\n            ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);\n            ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);\n            ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);\n            ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);\n            ctx.closePath();\n        };\n        return Ellipse;\n    }(Path));\n    Ellipse.prototype.type = 'ellipse';\n\n    var PI$2 = Math.PI;\n    var PI2$5 = PI$2 * 2;\n    var mathSin$3 = Math.sin;\n    var mathCos$3 = Math.cos;\n    var mathACos = Math.acos;\n    var mathATan2 = Math.atan2;\n    var mathAbs$1 = Math.abs;\n    var mathSqrt$3 = Math.sqrt;\n    var mathMax$3 = Math.max;\n    var mathMin$3 = Math.min;\n    var e = 1e-4;\n    function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n        var dx10 = x1 - x0;\n        var dy10 = y1 - y0;\n        var dx32 = x3 - x2;\n        var dy32 = y3 - y2;\n        var t = dy32 * dx10 - dx32 * dy10;\n        if (t * t < e) {\n            return;\n        }\n        t = (dx32 * (y0 - y2) - dy32 * (x0 - x2)) / t;\n        return [x0 + t * dx10, y0 + t * dy10];\n    }\n    function computeCornerTangents(x0, y0, x1, y1, radius, cr, clockwise) {\n        var x01 = x0 - x1;\n        var y01 = y0 - y1;\n        var lo = (clockwise ? cr : -cr) / mathSqrt$3(x01 * x01 + y01 * y01);\n        var ox = lo * y01;\n        var oy = -lo * x01;\n        var x11 = x0 + ox;\n        var y11 = y0 + oy;\n        var x10 = x1 + ox;\n        var y10 = y1 + oy;\n        var x00 = (x11 + x10) / 2;\n        var y00 = (y11 + y10) / 2;\n        var dx = x10 - x11;\n        var dy = y10 - y11;\n        var d2 = dx * dx + dy * dy;\n        var r = radius - cr;\n        var s = x11 * y10 - x10 * y11;\n        var d = (dy < 0 ? -1 : 1) * mathSqrt$3(mathMax$3(0, r * r * d2 - s * s));\n        var cx0 = (s * dy - dx * d) / d2;\n        var cy0 = (-s * dx - dy * d) / d2;\n        var cx1 = (s * dy + dx * d) / d2;\n        var cy1 = (-s * dx + dy * d) / d2;\n        var dx0 = cx0 - x00;\n        var dy0 = cy0 - y00;\n        var dx1 = cx1 - x00;\n        var dy1 = cy1 - y00;\n        if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) {\n            cx0 = cx1;\n            cy0 = cy1;\n        }\n        return {\n            cx: cx0,\n            cy: cy0,\n            x0: -ox,\n            y0: -oy,\n            x1: cx0 * (radius / r - 1),\n            y1: cy0 * (radius / r - 1)\n        };\n    }\n    function normalizeCornerRadius(cr) {\n        var arr;\n        if (isArray(cr)) {\n            var len = cr.length;\n            if (!len) {\n                return cr;\n            }\n            if (len === 1) {\n                arr = [cr[0], cr[0], 0, 0];\n            }\n            else if (len === 2) {\n                arr = [cr[0], cr[0], cr[1], cr[1]];\n            }\n            else if (len === 3) {\n                arr = cr.concat(cr[2]);\n            }\n            else {\n                arr = cr;\n            }\n        }\n        else {\n            arr = [cr, cr, cr, cr];\n        }\n        return arr;\n    }\n    function buildPath$1(ctx, shape) {\n        var _a;\n        var radius = mathMax$3(shape.r, 0);\n        var innerRadius = mathMax$3(shape.r0 || 0, 0);\n        var hasRadius = radius > 0;\n        var hasInnerRadius = innerRadius > 0;\n        if (!hasRadius && !hasInnerRadius) {\n            return;\n        }\n        if (!hasRadius) {\n            radius = innerRadius;\n            innerRadius = 0;\n        }\n        if (innerRadius > radius) {\n            var tmp = radius;\n            radius = innerRadius;\n            innerRadius = tmp;\n        }\n        var startAngle = shape.startAngle, endAngle = shape.endAngle;\n        if (isNaN(startAngle) || isNaN(endAngle)) {\n            return;\n        }\n        var cx = shape.cx, cy = shape.cy;\n        var clockwise = !!shape.clockwise;\n        var arc = mathAbs$1(endAngle - startAngle);\n        var mod = arc > PI2$5 && arc % PI2$5;\n        mod > e && (arc = mod);\n        if (!(radius > e)) {\n            ctx.moveTo(cx, cy);\n        }\n        else if (arc > PI2$5 - e) {\n            ctx.moveTo(cx + radius * mathCos$3(startAngle), cy + radius * mathSin$3(startAngle));\n            ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n            if (innerRadius > e) {\n                ctx.moveTo(cx + innerRadius * mathCos$3(endAngle), cy + innerRadius * mathSin$3(endAngle));\n                ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n            }\n        }\n        else {\n            var icrStart = void 0;\n            var icrEnd = void 0;\n            var ocrStart = void 0;\n            var ocrEnd = void 0;\n            var ocrs = void 0;\n            var ocre = void 0;\n            var icrs = void 0;\n            var icre = void 0;\n            var ocrMax = void 0;\n            var icrMax = void 0;\n            var limitedOcrMax = void 0;\n            var limitedIcrMax = void 0;\n            var xre = void 0;\n            var yre = void 0;\n            var xirs = void 0;\n            var yirs = void 0;\n            var xrs = radius * mathCos$3(startAngle);\n            var yrs = radius * mathSin$3(startAngle);\n            var xire = innerRadius * mathCos$3(endAngle);\n            var yire = innerRadius * mathSin$3(endAngle);\n            var hasArc = arc > e;\n            if (hasArc) {\n                var cornerRadius = shape.cornerRadius;\n                if (cornerRadius) {\n                    _a = normalizeCornerRadius(cornerRadius), icrStart = _a[0], icrEnd = _a[1], ocrStart = _a[2], ocrEnd = _a[3];\n                }\n                var halfRd = mathAbs$1(radius - innerRadius) / 2;\n                ocrs = mathMin$3(halfRd, ocrStart);\n                ocre = mathMin$3(halfRd, ocrEnd);\n                icrs = mathMin$3(halfRd, icrStart);\n                icre = mathMin$3(halfRd, icrEnd);\n                limitedOcrMax = ocrMax = mathMax$3(ocrs, ocre);\n                limitedIcrMax = icrMax = mathMax$3(icrs, icre);\n                if (ocrMax > e || icrMax > e) {\n                    xre = radius * mathCos$3(endAngle);\n                    yre = radius * mathSin$3(endAngle);\n                    xirs = innerRadius * mathCos$3(startAngle);\n                    yirs = innerRadius * mathSin$3(startAngle);\n                    if (arc < PI$2) {\n                        var it_1 = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire);\n                        if (it_1) {\n                            var x0 = xrs - it_1[0];\n                            var y0 = yrs - it_1[1];\n                            var x1 = xre - it_1[0];\n                            var y1 = yre - it_1[1];\n                            var a = 1 / mathSin$3(mathACos((x0 * x1 + y0 * y1) / (mathSqrt$3(x0 * x0 + y0 * y0) * mathSqrt$3(x1 * x1 + y1 * y1))) / 2);\n                            var b = mathSqrt$3(it_1[0] * it_1[0] + it_1[1] * it_1[1]);\n                            limitedOcrMax = mathMin$3(ocrMax, (radius - b) / (a + 1));\n                            limitedIcrMax = mathMin$3(icrMax, (innerRadius - b) / (a - 1));\n                        }\n                    }\n                }\n            }\n            if (!hasArc) {\n                ctx.moveTo(cx + xrs, cy + yrs);\n            }\n            else if (limitedOcrMax > e) {\n                var crStart = mathMin$3(ocrStart, limitedOcrMax);\n                var crEnd = mathMin$3(ocrEnd, limitedOcrMax);\n                var ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, crStart, clockwise);\n                var ct1 = computeCornerTangents(xre, yre, xire, yire, radius, crEnd, clockwise);\n                ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n                if (limitedOcrMax < ocrMax && crStart === crEnd) {\n                    ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n                else {\n                    crStart > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                    ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise);\n                    crEnd > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n            }\n            else {\n                ctx.moveTo(cx + xrs, cy + yrs);\n                ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n            }\n            if (!(innerRadius > e) || !hasArc) {\n                ctx.lineTo(cx + xire, cy + yire);\n            }\n            else if (limitedIcrMax > e) {\n                var crStart = mathMin$3(icrStart, limitedIcrMax);\n                var crEnd = mathMin$3(icrEnd, limitedIcrMax);\n                var ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -crEnd, clockwise);\n                var ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -crStart, clockwise);\n                ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n                if (limitedIcrMax < icrMax && crStart === crEnd) {\n                    ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n                else {\n                    crEnd > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                    ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise);\n                    crStart > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n            }\n            else {\n                ctx.lineTo(cx + xire, cy + yire);\n                ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n            }\n        }\n        ctx.closePath();\n    }\n\n    var SectorShape = (function () {\n        function SectorShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r0 = 0;\n            this.r = 0;\n            this.startAngle = 0;\n            this.endAngle = Math.PI * 2;\n            this.clockwise = true;\n            this.cornerRadius = 0;\n        }\n        return SectorShape;\n    }());\n    var Sector = (function (_super) {\n        __extends(Sector, _super);\n        function Sector(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Sector.prototype.getDefaultShape = function () {\n            return new SectorShape();\n        };\n        Sector.prototype.buildPath = function (ctx, shape) {\n            buildPath$1(ctx, shape);\n        };\n        Sector.prototype.isZeroArea = function () {\n            return this.shape.startAngle === this.shape.endAngle\n                || this.shape.r === this.shape.r0;\n        };\n        return Sector;\n    }(Path));\n    Sector.prototype.type = 'sector';\n\n    var RingShape = (function () {\n        function RingShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n            this.r0 = 0;\n        }\n        return RingShape;\n    }());\n    var Ring = (function (_super) {\n        __extends(Ring, _super);\n        function Ring(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Ring.prototype.getDefaultShape = function () {\n            return new RingShape();\n        };\n        Ring.prototype.buildPath = function (ctx, shape) {\n            var x = shape.cx;\n            var y = shape.cy;\n            var PI2 = Math.PI * 2;\n            ctx.moveTo(x + shape.r, y);\n            ctx.arc(x, y, shape.r, 0, PI2, false);\n            ctx.moveTo(x + shape.r0, y);\n            ctx.arc(x, y, shape.r0, 0, PI2, true);\n        };\n        return Ring;\n    }(Path));\n    Ring.prototype.type = 'ring';\n\n    function smoothBezier(points, smooth, isLoop, constraint) {\n        var cps = [];\n        var v = [];\n        var v1 = [];\n        var v2 = [];\n        var prevPoint;\n        var nextPoint;\n        var min$1;\n        var max$1;\n        if (constraint) {\n            min$1 = [Infinity, Infinity];\n            max$1 = [-Infinity, -Infinity];\n            for (var i = 0, len = points.length; i < len; i++) {\n                min(min$1, min$1, points[i]);\n                max(max$1, max$1, points[i]);\n            }\n            min(min$1, min$1, constraint[0]);\n            max(max$1, max$1, constraint[1]);\n        }\n        for (var i = 0, len = points.length; i < len; i++) {\n            var point = points[i];\n            if (isLoop) {\n                prevPoint = points[i ? i - 1 : len - 1];\n                nextPoint = points[(i + 1) % len];\n            }\n            else {\n                if (i === 0 || i === len - 1) {\n                    cps.push(clone$1(points[i]));\n                    continue;\n                }\n                else {\n                    prevPoint = points[i - 1];\n                    nextPoint = points[i + 1];\n                }\n            }\n            sub(v, nextPoint, prevPoint);\n            scale(v, v, smooth);\n            var d0 = distance(point, prevPoint);\n            var d1 = distance(point, nextPoint);\n            var sum = d0 + d1;\n            if (sum !== 0) {\n                d0 /= sum;\n                d1 /= sum;\n            }\n            scale(v1, v, -d0);\n            scale(v2, v, d1);\n            var cp0 = add([], point, v1);\n            var cp1 = add([], point, v2);\n            if (constraint) {\n                max(cp0, cp0, min$1);\n                min(cp0, cp0, max$1);\n                max(cp1, cp1, min$1);\n                min(cp1, cp1, max$1);\n            }\n            cps.push(cp0);\n            cps.push(cp1);\n        }\n        if (isLoop) {\n            cps.push(cps.shift());\n        }\n        return cps;\n    }\n\n    function buildPath$2(ctx, shape, closePath) {\n        var smooth = shape.smooth;\n        var points = shape.points;\n        if (points && points.length >= 2) {\n            if (smooth) {\n                var controlPoints = smoothBezier(points, smooth, closePath, shape.smoothConstraint);\n                ctx.moveTo(points[0][0], points[0][1]);\n                var len = points.length;\n                for (var i = 0; i < (closePath ? len : len - 1); i++) {\n                    var cp1 = controlPoints[i * 2];\n                    var cp2 = controlPoints[i * 2 + 1];\n                    var p = points[(i + 1) % len];\n                    ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]);\n                }\n            }\n            else {\n                ctx.moveTo(points[0][0], points[0][1]);\n                for (var i = 1, l = points.length; i < l; i++) {\n                    ctx.lineTo(points[i][0], points[i][1]);\n                }\n            }\n            closePath && ctx.closePath();\n        }\n    }\n\n    var PolygonShape = (function () {\n        function PolygonShape() {\n            this.points = null;\n            this.smooth = 0;\n            this.smoothConstraint = null;\n        }\n        return PolygonShape;\n    }());\n    var Polygon = (function (_super) {\n        __extends(Polygon, _super);\n        function Polygon(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Polygon.prototype.getDefaultShape = function () {\n            return new PolygonShape();\n        };\n        Polygon.prototype.buildPath = function (ctx, shape) {\n            buildPath$2(ctx, shape, true);\n        };\n        return Polygon;\n    }(Path));\n    Polygon.prototype.type = 'polygon';\n\n    var PolylineShape = (function () {\n        function PolylineShape() {\n            this.points = null;\n            this.percent = 1;\n            this.smooth = 0;\n            this.smoothConstraint = null;\n        }\n        return PolylineShape;\n    }());\n    var Polyline = (function (_super) {\n        __extends(Polyline, _super);\n        function Polyline(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Polyline.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Polyline.prototype.getDefaultShape = function () {\n            return new PolylineShape();\n        };\n        Polyline.prototype.buildPath = function (ctx, shape) {\n            buildPath$2(ctx, shape, false);\n        };\n        return Polyline;\n    }(Path));\n    Polyline.prototype.type = 'polyline';\n\n    var subPixelOptimizeOutputShape$1 = {};\n    var LineShape = (function () {\n        function LineShape() {\n            this.x1 = 0;\n            this.y1 = 0;\n            this.x2 = 0;\n            this.y2 = 0;\n            this.percent = 1;\n        }\n        return LineShape;\n    }());\n    var Line = (function (_super) {\n        __extends(Line, _super);\n        function Line(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Line.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Line.prototype.getDefaultShape = function () {\n            return new LineShape();\n        };\n        Line.prototype.buildPath = function (ctx, shape) {\n            var x1;\n            var y1;\n            var x2;\n            var y2;\n            if (this.subPixelOptimize) {\n                var optimizedShape = subPixelOptimizeLine(subPixelOptimizeOutputShape$1, shape, this.style);\n                x1 = optimizedShape.x1;\n                y1 = optimizedShape.y1;\n                x2 = optimizedShape.x2;\n                y2 = optimizedShape.y2;\n            }\n            else {\n                x1 = shape.x1;\n                y1 = shape.y1;\n                x2 = shape.x2;\n                y2 = shape.y2;\n            }\n            var percent = shape.percent;\n            if (percent === 0) {\n                return;\n            }\n            ctx.moveTo(x1, y1);\n            if (percent < 1) {\n                x2 = x1 * (1 - percent) + x2 * percent;\n                y2 = y1 * (1 - percent) + y2 * percent;\n            }\n            ctx.lineTo(x2, y2);\n        };\n        Line.prototype.pointAt = function (p) {\n            var shape = this.shape;\n            return [\n                shape.x1 * (1 - p) + shape.x2 * p,\n                shape.y1 * (1 - p) + shape.y2 * p\n            ];\n        };\n        return Line;\n    }(Path));\n    Line.prototype.type = 'line';\n\n    var out = [];\n    var BezierCurveShape = (function () {\n        function BezierCurveShape() {\n            this.x1 = 0;\n            this.y1 = 0;\n            this.x2 = 0;\n            this.y2 = 0;\n            this.cpx1 = 0;\n            this.cpy1 = 0;\n            this.percent = 1;\n        }\n        return BezierCurveShape;\n    }());\n    function someVectorAt(shape, t, isTangent) {\n        var cpx2 = shape.cpx2;\n        var cpy2 = shape.cpy2;\n        if (cpx2 != null || cpy2 != null) {\n            return [\n                (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),\n                (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)\n            ];\n        }\n        else {\n            return [\n                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),\n                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)\n            ];\n        }\n    }\n    var BezierCurve = (function (_super) {\n        __extends(BezierCurve, _super);\n        function BezierCurve(opts) {\n            return _super.call(this, opts) || this;\n        }\n        BezierCurve.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        BezierCurve.prototype.getDefaultShape = function () {\n            return new BezierCurveShape();\n        };\n        BezierCurve.prototype.buildPath = function (ctx, shape) {\n            var x1 = shape.x1;\n            var y1 = shape.y1;\n            var x2 = shape.x2;\n            var y2 = shape.y2;\n            var cpx1 = shape.cpx1;\n            var cpy1 = shape.cpy1;\n            var cpx2 = shape.cpx2;\n            var cpy2 = shape.cpy2;\n            var percent = shape.percent;\n            if (percent === 0) {\n                return;\n            }\n            ctx.moveTo(x1, y1);\n            if (cpx2 == null || cpy2 == null) {\n                if (percent < 1) {\n                    quadraticSubdivide(x1, cpx1, x2, percent, out);\n                    cpx1 = out[1];\n                    x2 = out[2];\n                    quadraticSubdivide(y1, cpy1, y2, percent, out);\n                    cpy1 = out[1];\n                    y2 = out[2];\n                }\n                ctx.quadraticCurveTo(cpx1, cpy1, x2, y2);\n            }\n            else {\n                if (percent < 1) {\n                    cubicSubdivide(x1, cpx1, cpx2, x2, percent, out);\n                    cpx1 = out[1];\n                    cpx2 = out[2];\n                    x2 = out[3];\n                    cubicSubdivide(y1, cpy1, cpy2, y2, percent, out);\n                    cpy1 = out[1];\n                    cpy2 = out[2];\n                    y2 = out[3];\n                }\n                ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2);\n            }\n        };\n        BezierCurve.prototype.pointAt = function (t) {\n            return someVectorAt(this.shape, t, false);\n        };\n        BezierCurve.prototype.tangentAt = function (t) {\n            var p = someVectorAt(this.shape, t, true);\n            return normalize(p, p);\n        };\n        return BezierCurve;\n    }(Path));\n    BezierCurve.prototype.type = 'bezier-curve';\n\n    var ArcShape = (function () {\n        function ArcShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n            this.startAngle = 0;\n            this.endAngle = Math.PI * 2;\n            this.clockwise = true;\n        }\n        return ArcShape;\n    }());\n    var Arc = (function (_super) {\n        __extends(Arc, _super);\n        function Arc(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Arc.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Arc.prototype.getDefaultShape = function () {\n            return new ArcShape();\n        };\n        Arc.prototype.buildPath = function (ctx, shape) {\n            var x = shape.cx;\n            var y = shape.cy;\n            var r = Math.max(shape.r, 0);\n            var startAngle = shape.startAngle;\n            var endAngle = shape.endAngle;\n            var clockwise = shape.clockwise;\n            var unitX = Math.cos(startAngle);\n            var unitY = Math.sin(startAngle);\n            ctx.moveTo(unitX * r + x, unitY * r + y);\n            ctx.arc(x, y, r, startAngle, endAngle, !clockwise);\n        };\n        return Arc;\n    }(Path));\n    Arc.prototype.type = 'arc';\n\n    var CompoundPath = (function (_super) {\n        __extends(CompoundPath, _super);\n        function CompoundPath() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.type = 'compound';\n            return _this;\n        }\n        CompoundPath.prototype._updatePathDirty = function () {\n            var paths = this.shape.paths;\n            var dirtyPath = this.shapeChanged();\n            for (var i = 0; i < paths.length; i++) {\n                dirtyPath = dirtyPath || paths[i].shapeChanged();\n            }\n            if (dirtyPath) {\n                this.dirtyShape();\n            }\n        };\n        CompoundPath.prototype.beforeBrush = function () {\n            this._updatePathDirty();\n            var paths = this.shape.paths || [];\n            var scale = this.getGlobalScale();\n            for (var i = 0; i < paths.length; i++) {\n                if (!paths[i].path) {\n                    paths[i].createPathProxy();\n                }\n                paths[i].path.setScale(scale[0], scale[1], paths[i].segmentIgnoreThreshold);\n            }\n        };\n        CompoundPath.prototype.buildPath = function (ctx, shape) {\n            var paths = shape.paths || [];\n            for (var i = 0; i < paths.length; i++) {\n                paths[i].buildPath(ctx, paths[i].shape, true);\n            }\n        };\n        CompoundPath.prototype.afterBrush = function () {\n            var paths = this.shape.paths || [];\n            for (var i = 0; i < paths.length; i++) {\n                paths[i].pathUpdated();\n            }\n        };\n        CompoundPath.prototype.getBoundingRect = function () {\n            this._updatePathDirty.call(this);\n            return Path.prototype.getBoundingRect.call(this);\n        };\n        return CompoundPath;\n    }(Path));\n\n    var Gradient = (function () {\n        function Gradient(colorStops) {\n            this.colorStops = colorStops || [];\n        }\n        Gradient.prototype.addColorStop = function (offset, color) {\n            this.colorStops.push({\n                offset: offset,\n                color: color\n            });\n        };\n        return Gradient;\n    }());\n\n    var LinearGradient = (function (_super) {\n        __extends(LinearGradient, _super);\n        function LinearGradient(x, y, x2, y2, colorStops, globalCoord) {\n            var _this = _super.call(this, colorStops) || this;\n            _this.x = x == null ? 0 : x;\n            _this.y = y == null ? 0 : y;\n            _this.x2 = x2 == null ? 1 : x2;\n            _this.y2 = y2 == null ? 0 : y2;\n            _this.type = 'linear';\n            _this.global = globalCoord || false;\n            return _this;\n        }\n        return LinearGradient;\n    }(Gradient));\n\n    var RadialGradient = (function (_super) {\n        __extends(RadialGradient, _super);\n        function RadialGradient(x, y, r, colorStops, globalCoord) {\n            var _this = _super.call(this, colorStops) || this;\n            _this.x = x == null ? 0.5 : x;\n            _this.y = y == null ? 0.5 : y;\n            _this.r = r == null ? 0.5 : r;\n            _this.type = 'radial';\n            _this.global = globalCoord || false;\n            return _this;\n        }\n        return RadialGradient;\n    }(Gradient));\n\n    var extent = [0, 0];\n    var extent2 = [0, 0];\n    var minTv$1 = new Point();\n    var maxTv$1 = new Point();\n    var OrientedBoundingRect = (function () {\n        function OrientedBoundingRect(rect, transform) {\n            this._corners = [];\n            this._axes = [];\n            this._origin = [0, 0];\n            for (var i = 0; i < 4; i++) {\n                this._corners[i] = new Point();\n            }\n            for (var i = 0; i < 2; i++) {\n                this._axes[i] = new Point();\n            }\n            if (rect) {\n                this.fromBoundingRect(rect, transform);\n            }\n        }\n        OrientedBoundingRect.prototype.fromBoundingRect = function (rect, transform) {\n            var corners = this._corners;\n            var axes = this._axes;\n            var x = rect.x;\n            var y = rect.y;\n            var x2 = x + rect.width;\n            var y2 = y + rect.height;\n            corners[0].set(x, y);\n            corners[1].set(x2, y);\n            corners[2].set(x2, y2);\n            corners[3].set(x, y2);\n            if (transform) {\n                for (var i = 0; i < 4; i++) {\n                    corners[i].transform(transform);\n                }\n            }\n            Point.sub(axes[0], corners[1], corners[0]);\n            Point.sub(axes[1], corners[3], corners[0]);\n            axes[0].normalize();\n            axes[1].normalize();\n            for (var i = 0; i < 2; i++) {\n                this._origin[i] = axes[i].dot(corners[0]);\n            }\n        };\n        OrientedBoundingRect.prototype.intersect = function (other, mtv) {\n            var overlapped = true;\n            var noMtv = !mtv;\n            minTv$1.set(Infinity, Infinity);\n            maxTv$1.set(0, 0);\n            if (!this._intersectCheckOneSide(this, other, minTv$1, maxTv$1, noMtv, 1)) {\n                overlapped = false;\n                if (noMtv) {\n                    return overlapped;\n                }\n            }\n            if (!this._intersectCheckOneSide(other, this, minTv$1, maxTv$1, noMtv, -1)) {\n                overlapped = false;\n                if (noMtv) {\n                    return overlapped;\n                }\n            }\n            if (!noMtv) {\n                Point.copy(mtv, overlapped ? minTv$1 : maxTv$1);\n            }\n            return overlapped;\n        };\n        OrientedBoundingRect.prototype._intersectCheckOneSide = function (self, other, minTv, maxTv, noMtv, inverse) {\n            var overlapped = true;\n            for (var i = 0; i < 2; i++) {\n                var axis = this._axes[i];\n                this._getProjMinMaxOnAxis(i, self._corners, extent);\n                this._getProjMinMaxOnAxis(i, other._corners, extent2);\n                if (extent[1] < extent2[0] || extent[0] > extent2[1]) {\n                    overlapped = false;\n                    if (noMtv) {\n                        return overlapped;\n                    }\n                    var dist0 = Math.abs(extent2[0] - extent[1]);\n                    var dist1 = Math.abs(extent[0] - extent2[1]);\n                    if (Math.min(dist0, dist1) > maxTv.len()) {\n                        if (dist0 < dist1) {\n                            Point.scale(maxTv, axis, -dist0 * inverse);\n                        }\n                        else {\n                            Point.scale(maxTv, axis, dist1 * inverse);\n                        }\n                    }\n                }\n                else if (minTv) {\n                    var dist0 = Math.abs(extent2[0] - extent[1]);\n                    var dist1 = Math.abs(extent[0] - extent2[1]);\n                    if (Math.min(dist0, dist1) < minTv.len()) {\n                        if (dist0 < dist1) {\n                            Point.scale(minTv, axis, dist0 * inverse);\n                        }\n                        else {\n                            Point.scale(minTv, axis, -dist1 * inverse);\n                        }\n                    }\n                }\n            }\n            return overlapped;\n        };\n        OrientedBoundingRect.prototype._getProjMinMaxOnAxis = function (dim, corners, out) {\n            var axis = this._axes[dim];\n            var origin = this._origin;\n            var proj = corners[0].dot(axis) + origin[dim];\n            var min = proj;\n            var max = proj;\n            for (var i = 1; i < corners.length; i++) {\n                var proj_1 = corners[i].dot(axis) + origin[dim];\n                min = Math.min(proj_1, min);\n                max = Math.max(proj_1, max);\n            }\n            out[0] = min;\n            out[1] = max;\n        };\n        return OrientedBoundingRect;\n    }());\n\n    var m = [];\n    var IncrementalDisplayable = (function (_super) {\n        __extends(IncrementalDisplayable, _super);\n        function IncrementalDisplayable() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.notClear = true;\n            _this.incremental = true;\n            _this._displayables = [];\n            _this._temporaryDisplayables = [];\n            _this._cursor = 0;\n            return _this;\n        }\n        IncrementalDisplayable.prototype.traverse = function (cb, context) {\n            cb.call(context, this);\n        };\n        IncrementalDisplayable.prototype.useStyle = function () {\n            this.style = {};\n        };\n        IncrementalDisplayable.prototype.getCursor = function () {\n            return this._cursor;\n        };\n        IncrementalDisplayable.prototype.innerAfterBrush = function () {\n            this._cursor = this._displayables.length;\n        };\n        IncrementalDisplayable.prototype.clearDisplaybles = function () {\n            this._displayables = [];\n            this._temporaryDisplayables = [];\n            this._cursor = 0;\n            this.markRedraw();\n            this.notClear = false;\n        };\n        IncrementalDisplayable.prototype.clearTemporalDisplayables = function () {\n            this._temporaryDisplayables = [];\n        };\n        IncrementalDisplayable.prototype.addDisplayable = function (displayable, notPersistent) {\n            if (notPersistent) {\n                this._temporaryDisplayables.push(displayable);\n            }\n            else {\n                this._displayables.push(displayable);\n            }\n            this.markRedraw();\n        };\n        IncrementalDisplayable.prototype.addDisplayables = function (displayables, notPersistent) {\n            notPersistent = notPersistent || false;\n            for (var i = 0; i < displayables.length; i++) {\n                this.addDisplayable(displayables[i], notPersistent);\n            }\n        };\n        IncrementalDisplayable.prototype.getDisplayables = function () {\n            return this._displayables;\n        };\n        IncrementalDisplayable.prototype.getTemporalDisplayables = function () {\n            return this._temporaryDisplayables;\n        };\n        IncrementalDisplayable.prototype.eachPendingDisplayable = function (cb) {\n            for (var i = this._cursor; i < this._displayables.length; i++) {\n                cb && cb(this._displayables[i]);\n            }\n            for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n                cb && cb(this._temporaryDisplayables[i]);\n            }\n        };\n        IncrementalDisplayable.prototype.update = function () {\n            this.updateTransform();\n            for (var i = this._cursor; i < this._displayables.length; i++) {\n                var displayable = this._displayables[i];\n                displayable.parent = this;\n                displayable.update();\n                displayable.parent = null;\n            }\n            for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n                var displayable = this._temporaryDisplayables[i];\n                displayable.parent = this;\n                displayable.update();\n                displayable.parent = null;\n            }\n        };\n        IncrementalDisplayable.prototype.getBoundingRect = function () {\n            if (!this._rect) {\n                var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);\n                for (var i = 0; i < this._displayables.length; i++) {\n                    var displayable = this._displayables[i];\n                    var childRect = displayable.getBoundingRect().clone();\n                    if (displayable.needLocalTransform()) {\n                        childRect.applyTransform(displayable.getLocalTransform(m));\n                    }\n                    rect.union(childRect);\n                }\n                this._rect = rect;\n            }\n            return this._rect;\n        };\n        IncrementalDisplayable.prototype.contain = function (x, y) {\n            var localPos = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            if (rect.contain(localPos[0], localPos[1])) {\n                for (var i = 0; i < this._displayables.length; i++) {\n                    var displayable = this._displayables[i];\n                    if (displayable.contain(x, y)) {\n                        return true;\n                    }\n                }\n            }\n            return false;\n        };\n        return IncrementalDisplayable;\n    }(Displayable));\n\n    var transitionStore = makeInner();\n    /**\n     * Return null if animation is disabled.\n     */\n\n    function getAnimationConfig(animationType, animatableModel, dataIndex, // Extra opts can override the option in animatable model.\n    extraOpts, // TODO It's only for pictorial bar now.\n    extraDelayParams) {\n      var animationPayload; // Check if there is global animation configuration from dataZoom/resize can override the config in option.\n      // If animation is enabled. Will use this animation config in payload.\n      // If animation is disabled. Just ignore it.\n\n      if (animatableModel && animatableModel.ecModel) {\n        var updatePayload = animatableModel.ecModel.getUpdatePayload();\n        animationPayload = updatePayload && updatePayload.animation;\n      }\n\n      var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();\n      var isUpdate = animationType === 'update';\n\n      if (animationEnabled) {\n        var duration = void 0;\n        var easing = void 0;\n        var delay = void 0;\n\n        if (extraOpts) {\n          duration = retrieve2(extraOpts.duration, 200);\n          easing = retrieve2(extraOpts.easing, 'cubicOut');\n          delay = 0;\n        } else {\n          duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration');\n          easing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing');\n          delay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay');\n        } // animation from payload has highest priority.\n\n\n        if (animationPayload) {\n          animationPayload.duration != null && (duration = animationPayload.duration);\n          animationPayload.easing != null && (easing = animationPayload.easing);\n          animationPayload.delay != null && (delay = animationPayload.delay);\n        }\n\n        if (isFunction(delay)) {\n          delay = delay(dataIndex, extraDelayParams);\n        }\n\n        if (isFunction(duration)) {\n          duration = duration(dataIndex);\n        }\n\n        var config = {\n          duration: duration || 0,\n          delay: delay,\n          easing: easing\n        };\n        return config;\n      } else {\n        return null;\n      }\n    }\n\n    function animateOrSetProps(animationType, el, props, animatableModel, dataIndex, cb, during) {\n      var isFrom = false;\n      var removeOpt;\n\n      if (isFunction(dataIndex)) {\n        during = cb;\n        cb = dataIndex;\n        dataIndex = null;\n      } else if (isObject(dataIndex)) {\n        cb = dataIndex.cb;\n        during = dataIndex.during;\n        isFrom = dataIndex.isFrom;\n        removeOpt = dataIndex.removeOpt;\n        dataIndex = dataIndex.dataIndex;\n      }\n\n      var isRemove = animationType === 'leave';\n\n      if (!isRemove) {\n        // Must stop the remove animation.\n        el.stopAnimation('leave');\n      }\n\n      var animationConfig = getAnimationConfig(animationType, animatableModel, dataIndex, isRemove ? removeOpt || {} : null, animatableModel && animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);\n\n      if (animationConfig && animationConfig.duration > 0) {\n        var duration = animationConfig.duration;\n        var animationDelay = animationConfig.delay;\n        var animationEasing = animationConfig.easing;\n        var animateConfig = {\n          duration: duration,\n          delay: animationDelay || 0,\n          easing: animationEasing,\n          done: cb,\n          force: !!cb || !!during,\n          // Set to final state in update/init animation.\n          // So the post processing based on the path shape can be done correctly.\n          setToFinal: !isRemove,\n          scope: animationType,\n          during: during\n        };\n        isFrom ? el.animateFrom(props, animateConfig) : el.animateTo(props, animateConfig);\n      } else {\n        el.stopAnimation(); // If `isFrom`, the props is the \"from\" props.\n\n        !isFrom && el.attr(props); // Call during at least once.\n\n        during && during(1);\n        cb && cb();\n      }\n    }\n    /**\n     * Update graphic element properties with or without animation according to the\n     * configuration in series.\n     *\n     * Caution: this method will stop previous animation.\n     * So do not use this method to one element twice before\n     * animation starts, unless you know what you are doing.\n     * @example\n     *     graphic.updateProps(el, {\n     *         position: [100, 100]\n     *     }, seriesModel, dataIndex, function () { console.log('Animation done!'); });\n     *     // Or\n     *     graphic.updateProps(el, {\n     *         position: [100, 100]\n     *     }, seriesModel, function () { console.log('Animation done!'); });\n     */\n\n\n    function updateProps(el, props, // TODO: TYPE AnimatableModel\n    animatableModel, dataIndex, cb, during) {\n      animateOrSetProps('update', el, props, animatableModel, dataIndex, cb, during);\n    }\n    /**\n     * Init graphic element properties with or without animation according to the\n     * configuration in series.\n     *\n     * Caution: this method will stop previous animation.\n     * So do not use this method to one element twice before\n     * animation starts, unless you know what you are doing.\n     */\n\n    function initProps(el, props, animatableModel, dataIndex, cb, during) {\n      animateOrSetProps('enter', el, props, animatableModel, dataIndex, cb, during);\n    }\n    /**\n     * If element is removed.\n     * It can determine if element is having remove animation.\n     */\n\n    function isElementRemoved(el) {\n      if (!el.__zr) {\n        return true;\n      }\n\n      for (var i = 0; i < el.animators.length; i++) {\n        var animator = el.animators[i];\n\n        if (animator.scope === 'leave') {\n          return true;\n        }\n      }\n\n      return false;\n    }\n    /**\n     * Remove graphic element\n     */\n\n    function removeElement(el, props, animatableModel, dataIndex, cb, during) {\n      // Don't do remove animation twice.\n      if (isElementRemoved(el)) {\n        return;\n      }\n\n      animateOrSetProps('leave', el, props, animatableModel, dataIndex, cb, during);\n    }\n\n    function fadeOutDisplayable(el, animatableModel, dataIndex, done) {\n      el.removeTextContent();\n      el.removeTextGuideLine();\n      removeElement(el, {\n        style: {\n          opacity: 0\n        }\n      }, animatableModel, dataIndex, done);\n    }\n\n    function removeElementWithFadeOut(el, animatableModel, dataIndex) {\n      function doRemove() {\n        el.parent && el.parent.remove(el);\n      } // Hide label and labelLine first\n      // TODO Also use fade out animation?\n\n\n      if (!el.isGroup) {\n        fadeOutDisplayable(el, animatableModel, dataIndex, doRemove);\n      } else {\n        el.traverse(function (disp) {\n          if (!disp.isGroup) {\n            // Can invoke doRemove multiple times.\n            fadeOutDisplayable(disp, animatableModel, dataIndex, doRemove);\n          }\n        });\n      }\n    }\n    /**\n     * Save old style for style transition in universalTransition module.\n     * It's used when element will be reused in each render.\n     * For chart like map, heatmap, which will always create new element.\n     * We don't need to save this because universalTransition can get old style from the old element\n     */\n\n    function saveOldStyle(el) {\n      transitionStore(el).oldStyle = el.style;\n    }\n\n    var mathMax$4 = Math.max;\n    var mathMin$4 = Math.min;\n    var _customShapeMap = {};\n    /**\n     * Extend shape with parameters\n     */\n\n    function extendShape(opts) {\n      return Path.extend(opts);\n    }\n    var extendPathFromString = extendFromString;\n    /**\n     * Extend path\n     */\n\n    function extendPath(pathData, opts) {\n      return extendPathFromString(pathData, opts);\n    }\n    /**\n     * Register a user defined shape.\n     * The shape class can be fetched by `getShapeClass`\n     * This method will overwrite the registered shapes, including\n     * the registered built-in shapes, if using the same `name`.\n     * The shape can be used in `custom series` and\n     * `graphic component` by declaring `{type: name}`.\n     *\n     * @param name\n     * @param ShapeClass Can be generated by `extendShape`.\n     */\n\n    function registerShape(name, ShapeClass) {\n      _customShapeMap[name] = ShapeClass;\n    }\n    /**\n     * Find shape class registered by `registerShape`. Usually used in\n     * fetching user defined shape.\n     *\n     * [Caution]:\n     * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared\n     * to use user registered shapes.\n     * Because the built-in shape (see `getBuiltInShape`) will be registered by\n     * `registerShape` by default. That enables users to get both built-in\n     * shapes as well as the shapes belonging to themsleves. But users can overwrite\n     * the built-in shapes by using names like 'circle', 'rect' via calling\n     * `registerShape`. So the echarts inner featrues should not fetch shapes from here\n     * in case that it is overwritten by users, except that some features, like\n     * `custom series`, `graphic component`, do it deliberately.\n     *\n     * (2) In the features like `custom series`, `graphic component`, the user input\n     * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic\n     * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names\n     * are reserved names, that is, if some user registers a shape named `'image'`,\n     * the shape will not be used. If we intending to add some more reserved names\n     * in feature, that might bring break changes (disable some existing user shape\n     * names). But that case probably rarely happens. So we don't make more mechanism\n     * to resolve this issue here.\n     *\n     * @param name\n     * @return The shape class. If not found, return nothing.\n     */\n\n    function getShapeClass(name) {\n      if (_customShapeMap.hasOwnProperty(name)) {\n        return _customShapeMap[name];\n      }\n    }\n    /**\n     * Create a path element from path data string\n     * @param pathData\n     * @param opts\n     * @param rect\n     * @param layout 'center' or 'cover' default to be cover\n     */\n\n    function makePath(pathData, opts, rect, layout) {\n      var path = createFromString(pathData, opts);\n\n      if (rect) {\n        if (layout === 'center') {\n          rect = centerGraphic(rect, path.getBoundingRect());\n        }\n\n        resizePath(path, rect);\n      }\n\n      return path;\n    }\n    /**\n     * Create a image element from image url\n     * @param imageUrl image url\n     * @param opts options\n     * @param rect constrain rect\n     * @param layout 'center' or 'cover'. Default to be 'cover'\n     */\n\n    function makeImage(imageUrl, rect, layout) {\n      var zrImg = new ZRImage({\n        style: {\n          image: imageUrl,\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height\n        },\n        onload: function (img) {\n          if (layout === 'center') {\n            var boundingRect = {\n              width: img.width,\n              height: img.height\n            };\n            zrImg.setStyle(centerGraphic(rect, boundingRect));\n          }\n        }\n      });\n      return zrImg;\n    }\n    /**\n     * Get position of centered element in bounding box.\n     *\n     * @param  rect         element local bounding box\n     * @param  boundingRect constraint bounding box\n     * @return element position containing x, y, width, and height\n     */\n\n    function centerGraphic(rect, boundingRect) {\n      // Set rect to center, keep width / height ratio.\n      var aspect = boundingRect.width / boundingRect.height;\n      var width = rect.height * aspect;\n      var height;\n\n      if (width <= rect.width) {\n        height = rect.height;\n      } else {\n        width = rect.width;\n        height = width / aspect;\n      }\n\n      var cx = rect.x + rect.width / 2;\n      var cy = rect.y + rect.height / 2;\n      return {\n        x: cx - width / 2,\n        y: cy - height / 2,\n        width: width,\n        height: height\n      };\n    }\n\n    var mergePath$1 = mergePath;\n    /**\n     * Resize a path to fit the rect\n     * @param path\n     * @param rect\n     */\n\n    function resizePath(path, rect) {\n      if (!path.applyTransform) {\n        return;\n      }\n\n      var pathRect = path.getBoundingRect();\n      var m = pathRect.calculateTransform(rect);\n      path.applyTransform(m);\n    }\n    /**\n     * Sub pixel optimize line for canvas\n     */\n\n    function subPixelOptimizeLine$1(shape, lineWidth) {\n      subPixelOptimizeLine(shape, shape, {\n        lineWidth: lineWidth\n      });\n      return shape;\n    }\n    /**\n     * Sub pixel optimize rect for canvas\n     */\n\n    function subPixelOptimizeRect$1(param) {\n      subPixelOptimizeRect(param.shape, param.shape, param.style);\n      return param;\n    }\n    /**\n     * Sub pixel optimize for canvas\n     *\n     * @param position Coordinate, such as x, y\n     * @param lineWidth Should be nonnegative integer.\n     * @param positiveOrNegative Default false (negative).\n     * @return Optimized position.\n     */\n\n    var subPixelOptimize$1 = subPixelOptimize;\n    /**\n     * Get transform matrix of target (param target),\n     * in coordinate of its ancestor (param ancestor)\n     *\n     * @param target\n     * @param [ancestor]\n     */\n\n    function getTransform(target, ancestor) {\n      var mat = identity([]);\n\n      while (target && target !== ancestor) {\n        mul$1(mat, target.getLocalTransform(), mat);\n        target = target.parent;\n      }\n\n      return mat;\n    }\n    /**\n     * Apply transform to an vertex.\n     * @param target [x, y]\n     * @param transform Can be:\n     *      + Transform matrix: like [1, 0, 0, 1, 0, 0]\n     *      + {position, rotation, scale}, the same as `zrender/Transformable`.\n     * @param invert Whether use invert matrix.\n     * @return [x, y]\n     */\n\n    function applyTransform$1(target, transform, invert$1) {\n      if (transform && !isArrayLike(transform)) {\n        transform = Transformable.getLocalTransform(transform);\n      }\n\n      if (invert$1) {\n        transform = invert([], transform);\n      }\n\n      return applyTransform([], target, transform);\n    }\n    /**\n     * @param direction 'left' 'right' 'top' 'bottom'\n     * @param transform Transform matrix: like [1, 0, 0, 1, 0, 0]\n     * @param invert Whether use invert matrix.\n     * @return Transformed direction. 'left' 'right' 'top' 'bottom'\n     */\n\n    function transformDirection(direction, transform, invert) {\n      // Pick a base, ensure that transform result will not be (0, 0).\n      var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]);\n      var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]);\n      var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0];\n      vertex = applyTransform$1(vertex, transform, invert);\n      return Math.abs(vertex[0]) > Math.abs(vertex[1]) ? vertex[0] > 0 ? 'right' : 'left' : vertex[1] > 0 ? 'bottom' : 'top';\n    }\n\n    function isNotGroup(el) {\n      return !el.isGroup;\n    }\n\n    function isPath(el) {\n      return el.shape != null;\n    }\n    /**\n     * Apply group transition animation from g1 to g2.\n     * If no animatableModel, no animation.\n     */\n\n\n    function groupTransition(g1, g2, animatableModel) {\n      if (!g1 || !g2) {\n        return;\n      }\n\n      function getElMap(g) {\n        var elMap = {};\n        g.traverse(function (el) {\n          if (isNotGroup(el) && el.anid) {\n            elMap[el.anid] = el;\n          }\n        });\n        return elMap;\n      }\n\n      function getAnimatableProps(el) {\n        var obj = {\n          x: el.x,\n          y: el.y,\n          rotation: el.rotation\n        };\n\n        if (isPath(el)) {\n          obj.shape = extend({}, el.shape);\n        }\n\n        return obj;\n      }\n\n      var elMap1 = getElMap(g1);\n      g2.traverse(function (el) {\n        if (isNotGroup(el) && el.anid) {\n          var oldEl = elMap1[el.anid];\n\n          if (oldEl) {\n            var newProp = getAnimatableProps(el);\n            el.attr(getAnimatableProps(oldEl));\n            updateProps(el, newProp, animatableModel, getECData(el).dataIndex);\n          }\n        }\n      });\n    }\n    function clipPointsByRect(points, rect) {\n      // FIXME: This way might be incorrect when graphic clipped by a corner\n      // and when element has a border.\n      return map(points, function (point) {\n        var x = point[0];\n        x = mathMax$4(x, rect.x);\n        x = mathMin$4(x, rect.x + rect.width);\n        var y = point[1];\n        y = mathMax$4(y, rect.y);\n        y = mathMin$4(y, rect.y + rect.height);\n        return [x, y];\n      });\n    }\n    /**\n     * Return a new clipped rect. If rect size are negative, return undefined.\n     */\n\n    function clipRectByRect(targetRect, rect) {\n      var x = mathMax$4(targetRect.x, rect.x);\n      var x2 = mathMin$4(targetRect.x + targetRect.width, rect.x + rect.width);\n      var y = mathMax$4(targetRect.y, rect.y);\n      var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border,\n      // should be painted. So return undefined.\n\n      if (x2 >= x && y2 >= y) {\n        return {\n          x: x,\n          y: y,\n          width: x2 - x,\n          height: y2 - y\n        };\n      }\n    }\n    function createIcon(iconStr, // Support 'image://' or 'path://' or direct svg path.\n    opt, rect) {\n      var innerOpts = extend({\n        rectHover: true\n      }, opt);\n      var style = innerOpts.style = {\n        strokeNoScale: true\n      };\n      rect = rect || {\n        x: -1,\n        y: -1,\n        width: 2,\n        height: 2\n      };\n\n      if (iconStr) {\n        return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), defaults(style, rect), new ZRImage(innerOpts)) : makePath(iconStr.replace('path://', ''), innerOpts, rect, 'center');\n      }\n    }\n    /**\n     * Return `true` if the given line (line `a`) and the given polygon\n     * are intersect.\n     * Note that we do not count colinear as intersect here because no\n     * requirement for that. We could do that if required in future.\n     */\n\n    function linePolygonIntersect(a1x, a1y, a2x, a2y, points) {\n      for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) {\n        var p = points[i];\n\n        if (lineLineIntersect(a1x, a1y, a2x, a2y, p[0], p[1], p2[0], p2[1])) {\n          return true;\n        }\n\n        p2 = p;\n      }\n    }\n    /**\n     * Return `true` if the given two lines (line `a` and line `b`)\n     * are intersect.\n     * Note that we do not count colinear as intersect here because no\n     * requirement for that. We could do that if required in future.\n     */\n\n    function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {\n      // let `vec_m` to be `vec_a2 - vec_a1` and `vec_n` to be `vec_b2 - vec_b1`.\n      var mx = a2x - a1x;\n      var my = a2y - a1y;\n      var nx = b2x - b1x;\n      var ny = b2y - b1y; // `vec_m` and `vec_n` are parallel iff\n      //     existing `k` such that `vec_m = k · vec_n`, equivalent to `vec_m X vec_n = 0`.\n\n      var nmCrossProduct = crossProduct2d(nx, ny, mx, my);\n\n      if (nearZero(nmCrossProduct)) {\n        return false;\n      } // `vec_m` and `vec_n` are intersect iff\n      //     existing `p` and `q` in [0, 1] such that `vec_a1 + p * vec_m = vec_b1 + q * vec_n`,\n      //     such that `q = ((vec_a1 - vec_b1) X vec_m) / (vec_n X vec_m)`\n      //           and `p = ((vec_a1 - vec_b1) X vec_n) / (vec_n X vec_m)`.\n\n\n      var b1a1x = a1x - b1x;\n      var b1a1y = a1y - b1y;\n      var q = crossProduct2d(b1a1x, b1a1y, mx, my) / nmCrossProduct;\n\n      if (q < 0 || q > 1) {\n        return false;\n      }\n\n      var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct;\n\n      if (p < 0 || p > 1) {\n        return false;\n      }\n\n      return true;\n    }\n    /**\n     * Cross product of 2-dimension vector.\n     */\n\n    function crossProduct2d(x1, y1, x2, y2) {\n      return x1 * y2 - x2 * y1;\n    }\n\n    function nearZero(val) {\n      return val <= 1e-6 && val >= -1e-6;\n    }\n\n    function setTooltipConfig(opt) {\n      var itemTooltipOption = opt.itemTooltipOption;\n      var componentModel = opt.componentModel;\n      var itemName = opt.itemName;\n      var itemTooltipOptionObj = isString(itemTooltipOption) ? {\n        formatter: itemTooltipOption\n      } : itemTooltipOption;\n      var mainType = componentModel.mainType;\n      var componentIndex = componentModel.componentIndex;\n      var formatterParams = {\n        componentType: mainType,\n        name: itemName,\n        $vars: ['name']\n      };\n      formatterParams[mainType + 'Index'] = componentIndex;\n      var formatterParamsExtra = opt.formatterParamsExtra;\n\n      if (formatterParamsExtra) {\n        each(keys(formatterParamsExtra), function (key) {\n          if (!hasOwn(formatterParams, key)) {\n            formatterParams[key] = formatterParamsExtra[key];\n            formatterParams.$vars.push(key);\n          }\n        });\n      }\n\n      var ecData = getECData(opt.el);\n      ecData.componentMainType = mainType;\n      ecData.componentIndex = componentIndex;\n      ecData.tooltipConfig = {\n        name: itemName,\n        option: defaults({\n          content: itemName,\n          formatterParams: formatterParams\n        }, itemTooltipOptionObj)\n      };\n    }\n\n    function traverseElement(el, cb) {\n      var stopped; // TODO\n      // Polyfill for fixing zrender group traverse don't visit it's root issue.\n\n      if (el.isGroup) {\n        stopped = cb(el);\n      }\n\n      if (!stopped) {\n        el.traverse(cb);\n      }\n    }\n\n    function traverseElements(els, cb) {\n      if (els) {\n        if (isArray(els)) {\n          for (var i = 0; i < els.length; i++) {\n            traverseElement(els[i], cb);\n          }\n        } else {\n          traverseElement(els, cb);\n        }\n      }\n    } // Register built-in shapes. These shapes might be overwritten\n    // by users, although we do not recommend that.\n\n    registerShape('circle', Circle);\n    registerShape('ellipse', Ellipse);\n    registerShape('sector', Sector);\n    registerShape('ring', Ring);\n    registerShape('polygon', Polygon);\n    registerShape('polyline', Polyline);\n    registerShape('rect', Rect);\n    registerShape('line', Line);\n    registerShape('bezierCurve', BezierCurve);\n    registerShape('arc', Arc);\n\n    var graphic = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        updateProps: updateProps,\n        initProps: initProps,\n        removeElement: removeElement,\n        removeElementWithFadeOut: removeElementWithFadeOut,\n        isElementRemoved: isElementRemoved,\n        extendShape: extendShape,\n        extendPath: extendPath,\n        registerShape: registerShape,\n        getShapeClass: getShapeClass,\n        makePath: makePath,\n        makeImage: makeImage,\n        mergePath: mergePath$1,\n        resizePath: resizePath,\n        subPixelOptimizeLine: subPixelOptimizeLine$1,\n        subPixelOptimizeRect: subPixelOptimizeRect$1,\n        subPixelOptimize: subPixelOptimize$1,\n        getTransform: getTransform,\n        applyTransform: applyTransform$1,\n        transformDirection: transformDirection,\n        groupTransition: groupTransition,\n        clipPointsByRect: clipPointsByRect,\n        clipRectByRect: clipRectByRect,\n        createIcon: createIcon,\n        linePolygonIntersect: linePolygonIntersect,\n        lineLineIntersect: lineLineIntersect,\n        setTooltipConfig: setTooltipConfig,\n        traverseElements: traverseElements,\n        Group: Group,\n        Image: ZRImage,\n        Text: ZRText,\n        Circle: Circle,\n        Ellipse: Ellipse,\n        Sector: Sector,\n        Ring: Ring,\n        Polygon: Polygon,\n        Polyline: Polyline,\n        Rect: Rect,\n        Line: Line,\n        BezierCurve: BezierCurve,\n        Arc: Arc,\n        IncrementalDisplayable: IncrementalDisplayable,\n        CompoundPath: CompoundPath,\n        LinearGradient: LinearGradient,\n        RadialGradient: RadialGradient,\n        BoundingRect: BoundingRect,\n        OrientedBoundingRect: OrientedBoundingRect,\n        Point: Point,\n        Path: Path\n    });\n\n    var EMPTY_OBJ = {};\n    function setLabelText(label, labelTexts) {\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        var text = labelTexts[stateName];\n        var state = label.ensureState(stateName);\n        state.style = state.style || {};\n        state.style.text = text;\n      }\n\n      var oldStates = label.currentStates.slice();\n      label.clearStates(true);\n      label.setStyle({\n        text: labelTexts.normal\n      });\n      label.useStates(oldStates, true);\n    }\n\n    function getLabelText(opt, stateModels, interpolatedValue) {\n      var labelFetcher = opt.labelFetcher;\n      var labelDataIndex = opt.labelDataIndex;\n      var labelDimIndex = opt.labelDimIndex;\n      var normalModel = stateModels.normal;\n      var baseText;\n\n      if (labelFetcher) {\n        baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, normalModel && normalModel.get('formatter'), interpolatedValue != null ? {\n          interpolatedValue: interpolatedValue\n        } : null);\n      }\n\n      if (baseText == null) {\n        baseText = isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt, interpolatedValue) : opt.defaultText;\n      }\n\n      var statesText = {\n        normal: baseText\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        var stateModel = stateModels[stateName];\n        statesText[stateName] = retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, stateName, null, labelDimIndex, stateModel && stateModel.get('formatter')) : null, baseText);\n      }\n\n      return statesText;\n    }\n\n    function setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified // TODO specified position?\n    ) {\n      opt = opt || EMPTY_OBJ;\n      var isSetOnText = targetEl instanceof ZRText;\n      var needsCreateText = false;\n\n      for (var i = 0; i < DISPLAY_STATES.length; i++) {\n        var stateModel = labelStatesModels[DISPLAY_STATES[i]];\n\n        if (stateModel && stateModel.getShallow('show')) {\n          needsCreateText = true;\n          break;\n        }\n      }\n\n      var textContent = isSetOnText ? targetEl : targetEl.getTextContent();\n\n      if (needsCreateText) {\n        if (!isSetOnText) {\n          // Reuse the previous\n          if (!textContent) {\n            textContent = new ZRText();\n            targetEl.setTextContent(textContent);\n          } // Use same state proxy\n\n\n          if (targetEl.stateProxy) {\n            textContent.stateProxy = targetEl.stateProxy;\n          }\n        }\n\n        var labelStatesTexts = getLabelText(opt, labelStatesModels);\n        var normalModel = labelStatesModels.normal;\n        var showNormal = !!normalModel.getShallow('show');\n        var normalStyle = createTextStyle(normalModel, stateSpecified && stateSpecified.normal, opt, false, !isSetOnText);\n        normalStyle.text = labelStatesTexts.normal;\n\n        if (!isSetOnText) {\n          // Always create new\n          targetEl.setTextConfig(createTextConfig(normalModel, opt, false));\n        }\n\n        for (var i = 0; i < SPECIAL_STATES.length; i++) {\n          var stateName = SPECIAL_STATES[i];\n          var stateModel = labelStatesModels[stateName];\n\n          if (stateModel) {\n            var stateObj = textContent.ensureState(stateName);\n            var stateShow = !!retrieve2(stateModel.getShallow('show'), showNormal);\n\n            if (stateShow !== showNormal) {\n              stateObj.ignore = !stateShow;\n            }\n\n            stateObj.style = createTextStyle(stateModel, stateSpecified && stateSpecified[stateName], opt, true, !isSetOnText);\n            stateObj.style.text = labelStatesTexts[stateName];\n\n            if (!isSetOnText) {\n              var targetElEmphasisState = targetEl.ensureState(stateName);\n              targetElEmphasisState.textConfig = createTextConfig(stateModel, opt, true);\n            }\n          }\n        } // PENDING: if there is many requirements that emphasis position\n        // need to be different from normal position, we might consider\n        // auto silent is those cases.\n\n\n        textContent.silent = !!normalModel.getShallow('silent'); // Keep x and y\n\n        if (textContent.style.x != null) {\n          normalStyle.x = textContent.style.x;\n        }\n\n        if (textContent.style.y != null) {\n          normalStyle.y = textContent.style.y;\n        }\n\n        textContent.ignore = !showNormal; // Always create new style.\n\n        textContent.useStyle(normalStyle);\n        textContent.dirty();\n\n        if (opt.enableTextSetter) {\n          labelInner(textContent).setLabelText = function (interpolatedValue) {\n            var labelStatesTexts = getLabelText(opt, labelStatesModels, interpolatedValue);\n            setLabelText(textContent, labelStatesTexts);\n          };\n        }\n      } else if (textContent) {\n        // Not display rich text.\n        textContent.ignore = true;\n      }\n\n      targetEl.dirty();\n    }\n    function getLabelStatesModels(itemModel, labelName) {\n      labelName = labelName || 'label';\n      var statesModels = {\n        normal: itemModel.getModel(labelName)\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        statesModels[stateName] = itemModel.getModel([stateName, labelName]);\n      }\n\n      return statesModels;\n    }\n    /**\n     * Set basic textStyle properties.\n     */\n\n    function createTextStyle(textStyleModel, specifiedTextStyle, // Fixed style in the code. Can't be set by model.\n    opt, isNotNormal, isAttached // If text is attached on an element. If so, auto color will handling in zrender.\n    ) {\n      var textStyle = {};\n      setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached);\n      specifiedTextStyle && extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\n\n      return textStyle;\n    }\n    function createTextConfig(textStyleModel, opt, isNotNormal) {\n      opt = opt || {};\n      var textConfig = {};\n      var labelPosition;\n      var labelRotate = textStyleModel.getShallow('rotate');\n      var labelDistance = retrieve2(textStyleModel.getShallow('distance'), isNotNormal ? null : 5);\n      var labelOffset = textStyleModel.getShallow('offset');\n      labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used\n      // in bar series, and magric type should be considered.\n\n      labelPosition === 'outside' && (labelPosition = opt.defaultOutsidePosition || 'top');\n\n      if (labelPosition != null) {\n        textConfig.position = labelPosition;\n      }\n\n      if (labelOffset != null) {\n        textConfig.offset = labelOffset;\n      }\n\n      if (labelRotate != null) {\n        labelRotate *= Math.PI / 180;\n        textConfig.rotation = labelRotate;\n      }\n\n      if (labelDistance != null) {\n        textConfig.distance = labelDistance;\n      } // fill and auto is determined by the color of path fill if it's not specified by developers.\n\n\n      textConfig.outsideFill = textStyleModel.get('color') === 'inherit' ? opt.inheritColor || null : 'auto';\n      return textConfig;\n    }\n    /**\n     * The uniform entry of set text style, that is, retrieve style definitions\n     * from `model` and set to `textStyle` object.\n     *\n     * Never in merge mode, but in overwrite mode, that is, all of the text style\n     * properties will be set. (Consider the states of normal and emphasis and\n     * default value can be adopted, merge would make the logic too complicated\n     * to manage.)\n     */\n\n    function setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached) {\n      // Consider there will be abnormal when merge hover style to normal style if given default value.\n      opt = opt || EMPTY_OBJ;\n      var ecModel = textStyleModel.ecModel;\n      var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:\n      // {\n      //     data: [{\n      //         value: 12,\n      //         label: {\n      //             rich: {\n      //                 // no 'a' here but using parent 'a'.\n      //             }\n      //         }\n      //     }],\n      //     rich: {\n      //         a: { ... }\n      //     }\n      // }\n\n      var richItemNames = getRichItemNames(textStyleModel);\n      var richResult;\n\n      if (richItemNames) {\n        richResult = {};\n\n        for (var name_1 in richItemNames) {\n          if (richItemNames.hasOwnProperty(name_1)) {\n            // Cascade is supported in rich.\n            var richTextStyle = textStyleModel.getModel(['rich', name_1]); // In rich, never `disableBox`.\n            // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,\n            // the default color `'blue'` will not be adopted if no color declared in `rich`.\n            // That might confuses users. So probably we should put `textStyleModel` as the\n            // root ancestor of the `richTextStyle`. But that would be a break change.\n\n            setTokenTextStyle(richResult[name_1] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true);\n          }\n        }\n      }\n\n      if (richResult) {\n        textStyle.rich = richResult;\n      }\n\n      var overflow = textStyleModel.get('overflow');\n\n      if (overflow) {\n        textStyle.overflow = overflow;\n      }\n\n      var margin = textStyleModel.get('minMargin');\n\n      if (margin != null) {\n        textStyle.margin = margin;\n      }\n\n      setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false);\n    } // Consider case:\n    // {\n    //     data: [{\n    //         value: 12,\n    //         label: {\n    //             rich: {\n    //                 // no 'a' here but using parent 'a'.\n    //             }\n    //         }\n    //     }],\n    //     rich: {\n    //         a: { ... }\n    //     }\n    // }\n    // TODO TextStyleModel\n\n\n    function getRichItemNames(textStyleModel) {\n      // Use object to remove duplicated names.\n      var richItemNameMap;\n\n      while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {\n        var rich = (textStyleModel.option || EMPTY_OBJ).rich;\n\n        if (rich) {\n          richItemNameMap = richItemNameMap || {};\n          var richKeys = keys(rich);\n\n          for (var i = 0; i < richKeys.length; i++) {\n            var richKey = richKeys[i];\n            richItemNameMap[richKey] = 1;\n          }\n        }\n\n        textStyleModel = textStyleModel.parentModel;\n      }\n\n      return richItemNameMap;\n    }\n\n    var TEXT_PROPS_WITH_GLOBAL = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'];\n    var TEXT_PROPS_SELF = ['align', 'lineHeight', 'width', 'height', 'tag', 'verticalAlign'];\n    var TEXT_PROPS_BOX = ['padding', 'borderWidth', 'borderRadius', 'borderDashOffset', 'backgroundColor', 'borderColor', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\n\n    function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, isBlock, inRich) {\n      // In merge mode, default value should not be given.\n      globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ;\n      var inheritColor = opt && opt.inheritColor;\n      var fillColor = textStyleModel.getShallow('color');\n      var strokeColor = textStyleModel.getShallow('textBorderColor');\n      var opacity = retrieve2(textStyleModel.getShallow('opacity'), globalTextStyle.opacity);\n\n      if (fillColor === 'inherit' || fillColor === 'auto') {\n        if (\"development\" !== 'production') {\n          if (fillColor === 'auto') {\n            deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n          }\n        }\n\n        if (inheritColor) {\n          fillColor = inheritColor;\n        } else {\n          fillColor = null;\n        }\n      }\n\n      if (strokeColor === 'inherit' || strokeColor === 'auto') {\n        if (\"development\" !== 'production') {\n          if (strokeColor === 'auto') {\n            deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n          }\n        }\n\n        if (inheritColor) {\n          strokeColor = inheritColor;\n        } else {\n          strokeColor = null;\n        }\n      }\n\n      if (!isAttached) {\n        // Only use default global textStyle.color if text is individual.\n        // Otherwise it will use the strategy of attached text color because text may be on a path.\n        fillColor = fillColor || globalTextStyle.color;\n        strokeColor = strokeColor || globalTextStyle.textBorderColor;\n      }\n\n      if (fillColor != null) {\n        textStyle.fill = fillColor;\n      }\n\n      if (strokeColor != null) {\n        textStyle.stroke = strokeColor;\n      }\n\n      var textBorderWidth = retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth);\n\n      if (textBorderWidth != null) {\n        textStyle.lineWidth = textBorderWidth;\n      }\n\n      var textBorderType = retrieve2(textStyleModel.getShallow('textBorderType'), globalTextStyle.textBorderType);\n\n      if (textBorderType != null) {\n        textStyle.lineDash = textBorderType;\n      }\n\n      var textBorderDashOffset = retrieve2(textStyleModel.getShallow('textBorderDashOffset'), globalTextStyle.textBorderDashOffset);\n\n      if (textBorderDashOffset != null) {\n        textStyle.lineDashOffset = textBorderDashOffset;\n      }\n\n      if (!isNotNormal && opacity == null && !inRich) {\n        opacity = opt && opt.defaultOpacity;\n      }\n\n      if (opacity != null) {\n        textStyle.opacity = opacity;\n      } // TODO\n\n\n      if (!isNotNormal && !isAttached) {\n        // Set default finally.\n        if (textStyle.fill == null && opt.inheritColor) {\n          textStyle.fill = opt.inheritColor;\n        }\n      } // Do not use `getFont` here, because merge should be supported, where\n      // part of these properties may be changed in emphasis style, and the\n      // others should remain their original value got from normal style.\n\n\n      for (var i = 0; i < TEXT_PROPS_WITH_GLOBAL.length; i++) {\n        var key = TEXT_PROPS_WITH_GLOBAL[i];\n        var val = retrieve2(textStyleModel.getShallow(key), globalTextStyle[key]);\n\n        if (val != null) {\n          textStyle[key] = val;\n        }\n      }\n\n      for (var i = 0; i < TEXT_PROPS_SELF.length; i++) {\n        var key = TEXT_PROPS_SELF[i];\n        var val = textStyleModel.getShallow(key);\n\n        if (val != null) {\n          textStyle[key] = val;\n        }\n      }\n\n      if (textStyle.verticalAlign == null) {\n        var baseline = textStyleModel.getShallow('baseline');\n\n        if (baseline != null) {\n          textStyle.verticalAlign = baseline;\n        }\n      }\n\n      if (!isBlock || !opt.disableBox) {\n        for (var i = 0; i < TEXT_PROPS_BOX.length; i++) {\n          var key = TEXT_PROPS_BOX[i];\n          var val = textStyleModel.getShallow(key);\n\n          if (val != null) {\n            textStyle[key] = val;\n          }\n        }\n\n        var borderType = textStyleModel.getShallow('borderType');\n\n        if (borderType != null) {\n          textStyle.borderDash = borderType;\n        }\n\n        if ((textStyle.backgroundColor === 'auto' || textStyle.backgroundColor === 'inherit') && inheritColor) {\n          if (\"development\" !== 'production') {\n            if (textStyle.backgroundColor === 'auto') {\n              deprecateReplaceLog('backgroundColor: \\'auto\\'', 'backgroundColor: \\'inherit\\'');\n            }\n          }\n\n          textStyle.backgroundColor = inheritColor;\n        }\n\n        if ((textStyle.borderColor === 'auto' || textStyle.borderColor === 'inherit') && inheritColor) {\n          if (\"development\" !== 'production') {\n            if (textStyle.borderColor === 'auto') {\n              deprecateReplaceLog('borderColor: \\'auto\\'', 'borderColor: \\'inherit\\'');\n            }\n          }\n\n          textStyle.borderColor = inheritColor;\n        }\n      }\n    }\n\n    function getFont(opt, ecModel) {\n      var gTextStyleModel = ecModel && ecModel.getModel('textStyle');\n      return trim([// FIXME in node-canvas fontWeight is before fontStyle\n      opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));\n    }\n    var labelInner = makeInner();\n    function setLabelValueAnimation(label, labelStatesModels, value, getDefaultText) {\n      if (!label) {\n        return;\n      }\n\n      var obj = labelInner(label);\n      obj.prevValue = obj.value;\n      obj.value = value;\n      var normalLabelModel = labelStatesModels.normal;\n      obj.valueAnimation = normalLabelModel.get('valueAnimation');\n\n      if (obj.valueAnimation) {\n        obj.precision = normalLabelModel.get('precision');\n        obj.defaultInterpolatedText = getDefaultText;\n        obj.statesModels = labelStatesModels;\n      }\n    }\n\n    var PATH_COLOR = ['textStyle', 'color'];\n    var textStyleParams = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'padding', 'lineHeight', 'rich', 'width', 'height', 'overflow']; // TODO Performance improvement?\n\n    var tmpText = new ZRText();\n\n    var TextStyleMixin =\n    /** @class */\n    function () {\n      function TextStyleMixin() {}\n      /**\n       * Get color property or get color from option.textStyle.color\n       */\n      // TODO Callback\n\n\n      TextStyleMixin.prototype.getTextColor = function (isEmphasis) {\n        var ecModel = this.ecModel;\n        return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null);\n      };\n      /**\n       * Create font string from fontStyle, fontWeight, fontSize, fontFamily\n       * @return {string}\n       */\n\n\n      TextStyleMixin.prototype.getFont = function () {\n        return getFont({\n          fontStyle: this.getShallow('fontStyle'),\n          fontWeight: this.getShallow('fontWeight'),\n          fontSize: this.getShallow('fontSize'),\n          fontFamily: this.getShallow('fontFamily')\n        }, this.ecModel);\n      };\n\n      TextStyleMixin.prototype.getTextRect = function (text) {\n        var style = {\n          text: text,\n          verticalAlign: this.getShallow('verticalAlign') || this.getShallow('baseline')\n        };\n\n        for (var i = 0; i < textStyleParams.length; i++) {\n          style[textStyleParams[i]] = this.getShallow(textStyleParams[i]);\n        }\n\n        tmpText.useStyle(style);\n        tmpText.update();\n        return tmpText.getBoundingRect();\n      };\n\n      return TextStyleMixin;\n    }();\n\n    var LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getLineStyle = makeStyleMapper(LINE_STYLE_KEY_MAP);\n\n    var LineStyleMixin =\n    /** @class */\n    function () {\n      function LineStyleMixin() {}\n\n      LineStyleMixin.prototype.getLineStyle = function (excludes) {\n        return getLineStyle(this, excludes);\n      };\n\n      return LineStyleMixin;\n    }();\n\n    var ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getItemStyle = makeStyleMapper(ITEM_STYLE_KEY_MAP);\n\n    var ItemStyleMixin =\n    /** @class */\n    function () {\n      function ItemStyleMixin() {}\n\n      ItemStyleMixin.prototype.getItemStyle = function (excludes, includes) {\n        return getItemStyle(this, excludes, includes);\n      };\n\n      return ItemStyleMixin;\n    }();\n\n    var Model =\n    /** @class */\n    function () {\n      function Model(option, parentModel, ecModel) {\n        this.parentModel = parentModel;\n        this.ecModel = ecModel;\n        this.option = option; // Simple optimization\n        // if (this.init) {\n        //     if (arguments.length <= 4) {\n        //         this.init(option, parentModel, ecModel, extraOpt);\n        //     }\n        //     else {\n        //         this.init.apply(this, arguments);\n        //     }\n        // }\n      }\n\n      Model.prototype.init = function (option, parentModel, ecModel) {\n        var rest = [];\n\n        for (var _i = 3; _i < arguments.length; _i++) {\n          rest[_i - 3] = arguments[_i];\n        }\n      };\n      /**\n       * Merge the input option to me.\n       */\n\n\n      Model.prototype.mergeOption = function (option, ecModel) {\n        merge(this.option, option, true);\n      }; // `path` can be 'a.b.c', so the return value type have to be `ModelOption`\n      // TODO: TYPE strict key check?\n      // get(path: string | string[], ignoreParent?: boolean): ModelOption;\n\n\n      Model.prototype.get = function (path, ignoreParent) {\n        if (path == null) {\n          return this.option;\n        }\n\n        return this._doGet(this.parsePath(path), !ignoreParent && this.parentModel);\n      };\n\n      Model.prototype.getShallow = function (key, ignoreParent) {\n        var option = this.option;\n        var val = option == null ? option : option[key];\n\n        if (val == null && !ignoreParent) {\n          var parentModel = this.parentModel;\n\n          if (parentModel) {\n            // FIXME:TS do not know how to make it works\n            val = parentModel.getShallow(key);\n          }\n        }\n\n        return val;\n      }; // `path` can be 'a.b.c', so the return value type have to be `Model<ModelOption>`\n      // getModel(path: string | string[], parentModel?: Model): Model;\n      // TODO 'a.b.c' is deprecated\n\n\n      Model.prototype.getModel = function (path, parentModel) {\n        var hasPath = path != null;\n        var pathFinal = hasPath ? this.parsePath(path) : null;\n        var obj = hasPath ? this._doGet(pathFinal) : this.option;\n        parentModel = parentModel || this.parentModel && this.parentModel.getModel(this.resolveParentPath(pathFinal));\n        return new Model(obj, parentModel, this.ecModel);\n      };\n      /**\n       * If model has option\n       */\n\n\n      Model.prototype.isEmpty = function () {\n        return this.option == null;\n      };\n\n      Model.prototype.restoreData = function () {}; // Pending\n\n\n      Model.prototype.clone = function () {\n        var Ctor = this.constructor;\n        return new Ctor(clone(this.option));\n      }; // setReadOnly(properties): void {\n      // clazzUtil.setReadOnly(this, properties);\n      // }\n      // If path is null/undefined, return null/undefined.\n\n\n      Model.prototype.parsePath = function (path) {\n        if (typeof path === 'string') {\n          return path.split('.');\n        }\n\n        return path;\n      }; // Resolve path for parent. Perhaps useful when parent use a different property.\n      // Default to be a identity resolver.\n      // Can be modified to a different resolver.\n\n\n      Model.prototype.resolveParentPath = function (path) {\n        return path;\n      }; // FIXME:TS check whether put this method here\n\n\n      Model.prototype.isAnimationEnabled = function () {\n        if (!env.node && this.option) {\n          if (this.option.animation != null) {\n            return !!this.option.animation;\n          } else if (this.parentModel) {\n            return this.parentModel.isAnimationEnabled();\n          }\n        }\n      };\n\n      Model.prototype._doGet = function (pathArr, parentModel) {\n        var obj = this.option;\n\n        if (!pathArr) {\n          return obj;\n        }\n\n        for (var i = 0; i < pathArr.length; i++) {\n          // Ignore empty\n          if (!pathArr[i]) {\n            continue;\n          } // obj could be number/string/... (like 0)\n\n\n          obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null;\n\n          if (obj == null) {\n            break;\n          }\n        }\n\n        if (obj == null && parentModel) {\n          obj = parentModel._doGet(this.resolveParentPath(pathArr), parentModel.parentModel);\n        }\n\n        return obj;\n      };\n\n      return Model;\n    }();\n\n    enableClassExtend(Model);\n    enableClassCheck(Model);\n    mixin(Model, LineStyleMixin);\n    mixin(Model, ItemStyleMixin);\n    mixin(Model, AreaStyleMixin);\n    mixin(Model, TextStyleMixin);\n\n    var base = Math.round(Math.random() * 10);\n    /**\n     * @public\n     * @param {string} type\n     * @return {string}\n     */\n\n    function getUID(type) {\n      // Considering the case of crossing js context,\n      // use Math.random to make id as unique as possible.\n      return [type || '', base++].join('_');\n    }\n    /**\n     * Implements `SubTypeDefaulterManager` for `target`.\n     */\n\n    function enableSubTypeDefaulter(target) {\n      var subTypeDefaulters = {};\n\n      target.registerSubTypeDefaulter = function (componentType, defaulter) {\n        var componentTypeInfo = parseClassType(componentType);\n        subTypeDefaulters[componentTypeInfo.main] = defaulter;\n      };\n\n      target.determineSubType = function (componentType, option) {\n        var type = option.type;\n\n        if (!type) {\n          var componentTypeMain = parseClassType(componentType).main;\n\n          if (target.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {\n            type = subTypeDefaulters[componentTypeMain](option);\n          }\n        }\n\n        return type;\n      };\n    }\n    /**\n     * Implements `TopologicalTravelable<any>` for `entity`.\n     *\n     * Topological travel on Activity Network (Activity On Vertices).\n     * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].\n     * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.\n     * If there is circular dependencey, Error will be thrown.\n     */\n\n    function enableTopologicalTravel(entity, dependencyGetter) {\n      /**\n       * @param targetNameList Target Component type list.\n       *                       Can be ['aa', 'bb', 'aa.xx']\n       * @param fullNameList By which we can build dependency graph.\n       * @param callback Params: componentType, dependencies.\n       * @param context Scope of callback.\n       */\n      entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {\n        if (!targetNameList.length) {\n          return;\n        }\n\n        var result = makeDepndencyGraph(fullNameList);\n        var graph = result.graph;\n        var noEntryList = result.noEntryList;\n        var targetNameSet = {};\n        each(targetNameList, function (name) {\n          targetNameSet[name] = true;\n        });\n\n        while (noEntryList.length) {\n          var currComponentType = noEntryList.pop();\n          var currVertex = graph[currComponentType];\n          var isInTargetNameSet = !!targetNameSet[currComponentType];\n\n          if (isInTargetNameSet) {\n            callback.call(context, currComponentType, currVertex.originalDeps.slice());\n            delete targetNameSet[currComponentType];\n          }\n\n          each(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge);\n        }\n\n        each(targetNameSet, function () {\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('Circular dependency may exists: ', targetNameSet, targetNameList, fullNameList);\n          }\n\n          throw new Error(errMsg);\n        });\n\n        function removeEdge(succComponentType) {\n          graph[succComponentType].entryCount--;\n\n          if (graph[succComponentType].entryCount === 0) {\n            noEntryList.push(succComponentType);\n          }\n        } // Consider this case: legend depends on series, and we call\n        // chart.setOption({series: [...]}), where only series is in option.\n        // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will\n        // not be called, but only sereis.mergeOption is called. Thus legend\n        // have no chance to update its local record about series (like which\n        // name of series is available in legend).\n\n\n        function removeEdgeAndAdd(succComponentType) {\n          targetNameSet[succComponentType] = true;\n          removeEdge(succComponentType);\n        }\n      };\n\n      function makeDepndencyGraph(fullNameList) {\n        var graph = {};\n        var noEntryList = [];\n        each(fullNameList, function (name) {\n          var thisItem = createDependencyGraphItem(graph, name);\n          var originalDeps = thisItem.originalDeps = dependencyGetter(name);\n          var availableDeps = getAvailableDependencies(originalDeps, fullNameList);\n          thisItem.entryCount = availableDeps.length;\n\n          if (thisItem.entryCount === 0) {\n            noEntryList.push(name);\n          }\n\n          each(availableDeps, function (dependentName) {\n            if (indexOf(thisItem.predecessor, dependentName) < 0) {\n              thisItem.predecessor.push(dependentName);\n            }\n\n            var thatItem = createDependencyGraphItem(graph, dependentName);\n\n            if (indexOf(thatItem.successor, dependentName) < 0) {\n              thatItem.successor.push(name);\n            }\n          });\n        });\n        return {\n          graph: graph,\n          noEntryList: noEntryList\n        };\n      }\n\n      function createDependencyGraphItem(graph, name) {\n        if (!graph[name]) {\n          graph[name] = {\n            predecessor: [],\n            successor: []\n          };\n        }\n\n        return graph[name];\n      }\n\n      function getAvailableDependencies(originalDeps, fullNameList) {\n        var availableDeps = [];\n        each(originalDeps, function (dep) {\n          indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);\n        });\n        return availableDeps;\n      }\n    }\n    function inheritDefaultOption(superOption, subOption) {\n      // See also `model/Component.ts#getDefaultOption`\n      return merge(merge({}, superOption, true), subOption, true);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * Language: English.\n     */\n    var langEN = {\n      time: {\n        month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n        monthAbbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n        dayOfWeek: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],\n        dayOfWeekAbbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']\n      },\n      legend: {\n        selector: {\n          all: 'All',\n          inverse: 'Inv'\n        }\n      },\n      toolbox: {\n        brush: {\n          title: {\n            rect: 'Box Select',\n            polygon: 'Lasso Select',\n            lineX: 'Horizontally Select',\n            lineY: 'Vertically Select',\n            keep: 'Keep Selections',\n            clear: 'Clear Selections'\n          }\n        },\n        dataView: {\n          title: 'Data View',\n          lang: ['Data View', 'Close', 'Refresh']\n        },\n        dataZoom: {\n          title: {\n            zoom: 'Zoom',\n            back: 'Zoom Reset'\n          }\n        },\n        magicType: {\n          title: {\n            line: 'Switch to Line Chart',\n            bar: 'Switch to Bar Chart',\n            stack: 'Stack',\n            tiled: 'Tile'\n          }\n        },\n        restore: {\n          title: 'Restore'\n        },\n        saveAsImage: {\n          title: 'Save as Image',\n          lang: ['Right Click to Save Image']\n        }\n      },\n      series: {\n        typeNames: {\n          pie: 'Pie chart',\n          bar: 'Bar chart',\n          line: 'Line chart',\n          scatter: 'Scatter plot',\n          effectScatter: 'Ripple scatter plot',\n          radar: 'Radar chart',\n          tree: 'Tree',\n          treemap: 'Treemap',\n          boxplot: 'Boxplot',\n          candlestick: 'Candlestick',\n          k: 'K line chart',\n          heatmap: 'Heat map',\n          map: 'Map',\n          parallel: 'Parallel coordinate map',\n          lines: 'Line graph',\n          graph: 'Relationship graph',\n          sankey: 'Sankey diagram',\n          funnel: 'Funnel chart',\n          gauge: 'Gauge',\n          pictorialBar: 'Pictorial bar',\n          themeRiver: 'Theme River Map',\n          sunburst: 'Sunburst'\n        }\n      },\n      aria: {\n        general: {\n          withTitle: 'This is a chart about \"{title}\"',\n          withoutTitle: 'This is a chart'\n        },\n        series: {\n          single: {\n            prefix: '',\n            withName: ' with type {seriesType} named {seriesName}.',\n            withoutName: ' with type {seriesType}.'\n          },\n          multiple: {\n            prefix: '. It consists of {seriesCount} series count.',\n            withName: ' The {seriesId} series is a {seriesType} representing {seriesName}.',\n            withoutName: ' The {seriesId} series is a {seriesType}.',\n            separator: {\n              middle: '',\n              end: ''\n            }\n          }\n        },\n        data: {\n          allData: 'The data is as follows: ',\n          partialData: 'The first {displayCnt} items are: ',\n          withName: 'the data for {name} is {value}',\n          withoutName: '{value}',\n          separator: {\n            middle: ', ',\n            end: '. '\n          }\n        }\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n     * Licensed to the Apache Software Foundation (ASF) under one\n     * or more contributor license agreements.  See the NOTICE file\n     * distributed with this work for additional information\n     * regarding copyright ownership.  The ASF licenses this file\n     * to you under the Apache License, Version 2.0 (the\n     * \"License\"); you may not use this file except in compliance\n     * with the License.  You may obtain a copy of the License at\n     *\n     *   http://www.apache.org/licenses/LICENSE-2.0\n     *\n     * Unless required by applicable law or agreed to in writing,\n     * software distributed under the License is distributed on an\n     * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n     * KIND, either express or implied.  See the License for the\n     * specific language governing permissions and limitations\n     * under the License.\n     */\n    var langZH = {\n      time: {\n        month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],\n        monthAbbr: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],\n        dayOfWeek: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],\n        dayOfWeekAbbr: ['日', '一', '二', '三', '四', '五', '六']\n      },\n      legend: {\n        selector: {\n          all: '全选',\n          inverse: '反选'\n        }\n      },\n      toolbox: {\n        brush: {\n          title: {\n            rect: '矩形选择',\n            polygon: '圈选',\n            lineX: '横向选择',\n            lineY: '纵向选择',\n            keep: '保持选择',\n            clear: '清除选择'\n          }\n        },\n        dataView: {\n          title: '数据视图',\n          lang: ['数据视图', '关闭', '刷新']\n        },\n        dataZoom: {\n          title: {\n            zoom: '区域缩放',\n            back: '区域缩放还原'\n          }\n        },\n        magicType: {\n          title: {\n            line: '切换为折线图',\n            bar: '切换为柱状图',\n            stack: '切换为堆叠',\n            tiled: '切换为平铺'\n          }\n        },\n        restore: {\n          title: '还原'\n        },\n        saveAsImage: {\n          title: '保存为图片',\n          lang: ['右键另存为图片']\n        }\n      },\n      series: {\n        typeNames: {\n          pie: '饼图',\n          bar: '柱状图',\n          line: '折线图',\n          scatter: '散点图',\n          effectScatter: '涟漪散点图',\n          radar: '雷达图',\n          tree: '树图',\n          treemap: '矩形树图',\n          boxplot: '箱型图',\n          candlestick: 'K线图',\n          k: 'K线图',\n          heatmap: '热力图',\n          map: '地图',\n          parallel: '平行坐标图',\n          lines: '线图',\n          graph: '关系图',\n          sankey: '桑基图',\n          funnel: '漏斗图',\n          gauge: '仪表盘图',\n          pictorialBar: '象形柱图',\n          themeRiver: '主题河流图',\n          sunburst: '旭日图'\n        }\n      },\n      aria: {\n        general: {\n          withTitle: '这是一个关于“{title}”的图表。',\n          withoutTitle: '这是一个图表，'\n        },\n        series: {\n          single: {\n            prefix: '',\n            withName: '图表类型是{seriesType}，表示{seriesName}。',\n            withoutName: '图表类型是{seriesType}。'\n          },\n          multiple: {\n            prefix: '它由{seriesCount}个图表系列组成。',\n            withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType}，',\n            withoutName: '第{seriesId}个系列是一个{seriesType}，',\n            separator: {\n              middle: '；',\n              end: '。'\n            }\n          }\n        },\n        data: {\n          allData: '其数据是——',\n          partialData: '其中，前{displayCnt}项是——',\n          withName: '{name}的数据是{value}',\n          withoutName: '{value}',\n          separator: {\n            middle: '，',\n            end: ''\n          }\n        }\n      }\n    };\n\n    var LOCALE_ZH = 'ZH';\n    var LOCALE_EN = 'EN';\n    var DEFAULT_LOCALE = LOCALE_EN;\n    var localeStorage = {};\n    var localeModels = {};\n    var SYSTEM_LANG = !env.domSupported ? DEFAULT_LOCALE : function () {\n      var langStr = (\n      /* eslint-disable-next-line */\n      document.documentElement.lang || navigator.language || navigator.browserLanguage).toUpperCase();\n      return langStr.indexOf(LOCALE_ZH) > -1 ? LOCALE_ZH : DEFAULT_LOCALE;\n    }();\n    function registerLocale(locale, localeObj) {\n      locale = locale.toUpperCase();\n      localeModels[locale] = new Model(localeObj);\n      localeStorage[locale] = localeObj;\n    } // export function getLocale(locale: string) {\n    //     return localeStorage[locale];\n    // }\n\n    function createLocaleObject(locale) {\n      if (isString(locale)) {\n        var localeObj = localeStorage[locale.toUpperCase()] || {};\n\n        if (locale === LOCALE_ZH || locale === LOCALE_EN) {\n          return clone(localeObj);\n        } else {\n          return merge(clone(localeObj), clone(localeStorage[DEFAULT_LOCALE]), false);\n        }\n      } else {\n        return merge(clone(locale), clone(localeStorage[DEFAULT_LOCALE]), false);\n      }\n    }\n    function getLocaleModel(lang) {\n      return localeModels[lang];\n    }\n    function getDefaultLocaleModel() {\n      return localeModels[DEFAULT_LOCALE];\n    } // Default locale\n\n    registerLocale(LOCALE_EN, langEN);\n    registerLocale(LOCALE_ZH, langZH);\n\n    var ONE_SECOND = 1000;\n    var ONE_MINUTE = ONE_SECOND * 60;\n    var ONE_HOUR = ONE_MINUTE * 60;\n    var ONE_DAY = ONE_HOUR * 24;\n    var ONE_YEAR = ONE_DAY * 365;\n    var defaultLeveledFormatter = {\n      year: '{yyyy}',\n      month: '{MMM}',\n      day: '{d}',\n      hour: '{HH}:{mm}',\n      minute: '{HH}:{mm}',\n      second: '{HH}:{mm}:{ss}',\n      millisecond: '{HH}:{mm}:{ss} {SSS}',\n      none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}'\n    };\n    var fullDayFormatter = '{yyyy}-{MM}-{dd}';\n    var fullLeveledFormatter = {\n      year: '{yyyy}',\n      month: '{yyyy}-{MM}',\n      day: fullDayFormatter,\n      hour: fullDayFormatter + ' ' + defaultLeveledFormatter.hour,\n      minute: fullDayFormatter + ' ' + defaultLeveledFormatter.minute,\n      second: fullDayFormatter + ' ' + defaultLeveledFormatter.second,\n      millisecond: defaultLeveledFormatter.none\n    };\n    var primaryTimeUnits = ['year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond'];\n    var timeUnits = ['year', 'half-year', 'quarter', 'month', 'week', 'half-week', 'day', 'half-day', 'quarter-day', 'hour', 'minute', 'second', 'millisecond'];\n    function pad(str, len) {\n      str += '';\n      return '0000'.substr(0, len - str.length) + str;\n    }\n    function getPrimaryTimeUnit(timeUnit) {\n      switch (timeUnit) {\n        case 'half-year':\n        case 'quarter':\n          return 'month';\n\n        case 'week':\n        case 'half-week':\n          return 'day';\n\n        case 'half-day':\n        case 'quarter-day':\n          return 'hour';\n\n        default:\n          // year, minutes, second, milliseconds\n          return timeUnit;\n      }\n    }\n    function isPrimaryTimeUnit(timeUnit) {\n      return timeUnit === getPrimaryTimeUnit(timeUnit);\n    }\n    function getDefaultFormatPrecisionOfInterval(timeUnit) {\n      switch (timeUnit) {\n        case 'year':\n        case 'month':\n          return 'day';\n\n        case 'millisecond':\n          return 'millisecond';\n\n        default:\n          // Also for day, hour, minute, second\n          return 'second';\n      }\n    }\n    function format( // Note: The result based on `isUTC` are totally different, which can not be just simply\n    // substituted by the result without `isUTC`. So we make the param `isUTC` mandatory.\n    time, template, isUTC, lang) {\n      var date = parseDate(time);\n      var y = date[fullYearGetterName(isUTC)]();\n      var M = date[monthGetterName(isUTC)]() + 1;\n      var q = Math.floor((M - 1) / 3) + 1;\n      var d = date[dateGetterName(isUTC)]();\n      var e = date['get' + (isUTC ? 'UTC' : '') + 'Day']();\n      var H = date[hoursGetterName(isUTC)]();\n      var h = (H - 1) % 12 + 1;\n      var m = date[minutesGetterName(isUTC)]();\n      var s = date[secondsGetterName(isUTC)]();\n      var S = date[millisecondsGetterName(isUTC)]();\n      var localeModel = lang instanceof Model ? lang : getLocaleModel(lang || SYSTEM_LANG) || getDefaultLocaleModel();\n      var timeModel = localeModel.getModel('time');\n      var month = timeModel.get('month');\n      var monthAbbr = timeModel.get('monthAbbr');\n      var dayOfWeek = timeModel.get('dayOfWeek');\n      var dayOfWeekAbbr = timeModel.get('dayOfWeekAbbr');\n      return (template || '').replace(/{yyyy}/g, y + '').replace(/{yy}/g, y % 100 + '').replace(/{Q}/g, q + '').replace(/{MMMM}/g, month[M - 1]).replace(/{MMM}/g, monthAbbr[M - 1]).replace(/{MM}/g, pad(M, 2)).replace(/{M}/g, M + '').replace(/{dd}/g, pad(d, 2)).replace(/{d}/g, d + '').replace(/{eeee}/g, dayOfWeek[e]).replace(/{ee}/g, dayOfWeekAbbr[e]).replace(/{e}/g, e + '').replace(/{HH}/g, pad(H, 2)).replace(/{H}/g, H + '').replace(/{hh}/g, pad(h + '', 2)).replace(/{h}/g, h + '').replace(/{mm}/g, pad(m, 2)).replace(/{m}/g, m + '').replace(/{ss}/g, pad(s, 2)).replace(/{s}/g, s + '').replace(/{SSS}/g, pad(S, 3)).replace(/{S}/g, S + '');\n    }\n    function leveledFormat(tick, idx, formatter, lang, isUTC) {\n      var template = null;\n\n      if (isString(formatter)) {\n        // Single formatter for all units at all levels\n        template = formatter;\n      } else if (isFunction(formatter)) {\n        // Callback formatter\n        template = formatter(tick.value, idx, {\n          level: tick.level\n        });\n      } else {\n        var defaults$1 = extend({}, defaultLeveledFormatter);\n\n        if (tick.level > 0) {\n          for (var i = 0; i < primaryTimeUnits.length; ++i) {\n            defaults$1[primaryTimeUnits[i]] = \"{primary|\" + defaults$1[primaryTimeUnits[i]] + \"}\";\n          }\n        }\n\n        var mergedFormatter = formatter ? formatter.inherit === false ? formatter // Use formatter with bigger units\n        : defaults(formatter, defaults$1) : defaults$1;\n        var unit = getUnitFromValue(tick.value, isUTC);\n\n        if (mergedFormatter[unit]) {\n          template = mergedFormatter[unit];\n        } else if (mergedFormatter.inherit) {\n          // Unit formatter is not defined and should inherit from bigger units\n          var targetId = timeUnits.indexOf(unit);\n\n          for (var i = targetId - 1; i >= 0; --i) {\n            if (mergedFormatter[unit]) {\n              template = mergedFormatter[unit];\n              break;\n            }\n          }\n\n          template = template || defaults$1.none;\n        }\n\n        if (isArray(template)) {\n          var levelId = tick.level == null ? 0 : tick.level >= 0 ? tick.level : template.length + tick.level;\n          levelId = Math.min(levelId, template.length - 1);\n          template = template[levelId];\n        }\n      }\n\n      return format(new Date(tick.value), template, isUTC, lang);\n    }\n    function getUnitFromValue(value, isUTC) {\n      var date = parseDate(value);\n      var M = date[monthGetterName(isUTC)]() + 1;\n      var d = date[dateGetterName(isUTC)]();\n      var h = date[hoursGetterName(isUTC)]();\n      var m = date[minutesGetterName(isUTC)]();\n      var s = date[secondsGetterName(isUTC)]();\n      var S = date[millisecondsGetterName(isUTC)]();\n      var isSecond = S === 0;\n      var isMinute = isSecond && s === 0;\n      var isHour = isMinute && m === 0;\n      var isDay = isHour && h === 0;\n      var isMonth = isDay && d === 1;\n      var isYear = isMonth && M === 1;\n\n      if (isYear) {\n        return 'year';\n      } else if (isMonth) {\n        return 'month';\n      } else if (isDay) {\n        return 'day';\n      } else if (isHour) {\n        return 'hour';\n      } else if (isMinute) {\n        return 'minute';\n      } else if (isSecond) {\n        return 'second';\n      } else {\n        return 'millisecond';\n      }\n    }\n    function getUnitValue(value, unit, isUTC) {\n      var date = isNumber(value) ? parseDate(value) : value;\n      unit = unit || getUnitFromValue(value, isUTC);\n\n      switch (unit) {\n        case 'year':\n          return date[fullYearGetterName(isUTC)]();\n\n        case 'half-year':\n          return date[monthGetterName(isUTC)]() >= 6 ? 1 : 0;\n\n        case 'quarter':\n          return Math.floor((date[monthGetterName(isUTC)]() + 1) / 4);\n\n        case 'month':\n          return date[monthGetterName(isUTC)]();\n\n        case 'day':\n          return date[dateGetterName(isUTC)]();\n\n        case 'half-day':\n          return date[hoursGetterName(isUTC)]() / 24;\n\n        case 'hour':\n          return date[hoursGetterName(isUTC)]();\n\n        case 'minute':\n          return date[minutesGetterName(isUTC)]();\n\n        case 'second':\n          return date[secondsGetterName(isUTC)]();\n\n        case 'millisecond':\n          return date[millisecondsGetterName(isUTC)]();\n      }\n    }\n    function fullYearGetterName(isUTC) {\n      return isUTC ? 'getUTCFullYear' : 'getFullYear';\n    }\n    function monthGetterName(isUTC) {\n      return isUTC ? 'getUTCMonth' : 'getMonth';\n    }\n    function dateGetterName(isUTC) {\n      return isUTC ? 'getUTCDate' : 'getDate';\n    }\n    function hoursGetterName(isUTC) {\n      return isUTC ? 'getUTCHours' : 'getHours';\n    }\n    function minutesGetterName(isUTC) {\n      return isUTC ? 'getUTCMinutes' : 'getMinutes';\n    }\n    function secondsGetterName(isUTC) {\n      return isUTC ? 'getUTCSeconds' : 'getSeconds';\n    }\n    function millisecondsGetterName(isUTC) {\n      return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds';\n    }\n    function fullYearSetterName(isUTC) {\n      return isUTC ? 'setUTCFullYear' : 'setFullYear';\n    }\n    function monthSetterName(isUTC) {\n      return isUTC ? 'setUTCMonth' : 'setMonth';\n    }\n    function dateSetterName(isUTC) {\n      return isUTC ? 'setUTCDate' : 'setDate';\n    }\n    function hoursSetterName(isUTC) {\n      return isUTC ? 'setUTCHours' : 'setHours';\n    }\n    function minutesSetterName(isUTC) {\n      return isUTC ? 'setUTCMinutes' : 'setMinutes';\n    }\n    function secondsSetterName(isUTC) {\n      return isUTC ? 'setUTCSeconds' : 'setSeconds';\n    }\n    function millisecondsSetterName(isUTC) {\n      return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds';\n    }\n\n    function getTextRect(text, font, align, verticalAlign, padding, rich, truncate, lineHeight) {\n      var textEl = new ZRText({\n        style: {\n          text: text,\n          font: font,\n          align: align,\n          verticalAlign: verticalAlign,\n          padding: padding,\n          rich: rich,\n          overflow: truncate ? 'truncate' : null,\n          lineHeight: lineHeight\n        }\n      });\n      return textEl.getBoundingRect();\n    }\n\n    /**\n     * Add a comma each three digit.\n     */\n\n    function addCommas(x) {\n      if (!isNumeric(x)) {\n        return isString(x) ? x : '-';\n      }\n\n      var parts = (x + '').split('.');\n      return parts[0].replace(/(\\d{1,3})(?=(?:\\d{3})+(?!\\d))/g, '$1,') + (parts.length > 1 ? '.' + parts[1] : '');\n    }\n    function toCamelCase(str, upperCaseFirst) {\n      str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {\n        return group1.toUpperCase();\n      });\n\n      if (upperCaseFirst && str) {\n        str = str.charAt(0).toUpperCase() + str.slice(1);\n      }\n\n      return str;\n    }\n    var normalizeCssArray$1 = normalizeCssArray;\n    /**\n     * Make value user readable for tooltip and label.\n     * \"User readable\":\n     *     Try to not print programmer-specific text like NaN, Infinity, null, undefined.\n     *     Avoid to display an empty string, which users can not recognize there is\n     *     a value and it might look like a bug.\n     */\n\n    function makeValueReadable(value, valueType, useUTC) {\n      var USER_READABLE_DEFUALT_TIME_PATTERN = '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}';\n\n      function stringToUserReadable(str) {\n        return str && trim(str) ? str : '-';\n      }\n\n      function isNumberUserReadable(num) {\n        return !!(num != null && !isNaN(num) && isFinite(num));\n      }\n\n      var isTypeTime = valueType === 'time';\n      var isValueDate = value instanceof Date;\n\n      if (isTypeTime || isValueDate) {\n        var date = isTypeTime ? parseDate(value) : value;\n\n        if (!isNaN(+date)) {\n          return format(date, USER_READABLE_DEFUALT_TIME_PATTERN, useUTC);\n        } else if (isValueDate) {\n          return '-';\n        } // In other cases, continue to try to display the value in the following code.\n\n      }\n\n      if (valueType === 'ordinal') {\n        return isStringSafe(value) ? stringToUserReadable(value) : isNumber(value) ? isNumberUserReadable(value) ? value + '' : '-' : '-';\n      } // By default.\n\n\n      var numericResult = numericToNumber(value);\n      return isNumberUserReadable(numericResult) ? addCommas(numericResult) : isStringSafe(value) ? stringToUserReadable(value) : typeof value === 'boolean' ? value + '' : '-';\n    }\n    var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];\n\n    var wrapVar = function (varName, seriesIdx) {\n      return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';\n    };\n    /**\n     * Template formatter\n     * @param {Array.<Object>|Object} paramsList\n     */\n\n\n    function formatTpl(tpl, paramsList, encode) {\n      if (!isArray(paramsList)) {\n        paramsList = [paramsList];\n      }\n\n      var seriesLen = paramsList.length;\n\n      if (!seriesLen) {\n        return '';\n      }\n\n      var $vars = paramsList[0].$vars || [];\n\n      for (var i = 0; i < $vars.length; i++) {\n        var alias = TPL_VAR_ALIAS[i];\n        tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0));\n      }\n\n      for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {\n        for (var k = 0; k < $vars.length; k++) {\n          var val = paramsList[seriesIdx][$vars[k]];\n          tpl = tpl.replace(wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val);\n        }\n      }\n\n      return tpl;\n    }\n    function getTooltipMarker(inOpt, extraCssText) {\n      var opt = isString(inOpt) ? {\n        color: inOpt,\n        extraCssText: extraCssText\n      } : inOpt || {};\n      var color = opt.color;\n      var type = opt.type;\n      extraCssText = opt.extraCssText;\n      var renderMode = opt.renderMode || 'html';\n\n      if (!color) {\n        return '';\n      }\n\n      if (renderMode === 'html') {\n        return type === 'subItem' ? '<span style=\"display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' + 'border-radius:4px;width:4px;height:4px;background-color:' // Only support string\n        + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>' : '<span style=\"display:inline-block;margin-right:4px;' + 'border-radius:10px;width:10px;height:10px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>';\n      } else {\n        // Should better not to auto generate style name by auto-increment number here.\n        // Because this util is usually called in tooltip formatter, which is probably\n        // called repeatedly when mouse move and the auto-increment number increases fast.\n        // Users can make their own style name by theirselves, make it unique and readable.\n        var markerId = opt.markerId || 'markerX';\n        return {\n          renderMode: renderMode,\n          content: '{' + markerId + '|}  ',\n          style: type === 'subItem' ? {\n            width: 4,\n            height: 4,\n            borderRadius: 2,\n            backgroundColor: color\n          } : {\n            width: 10,\n            height: 10,\n            borderRadius: 5,\n            backgroundColor: color\n          }\n        };\n      }\n    }\n    /**\n     * @deprecated Use `time/format` instead.\n     * ISO Date format\n     * @param {string} tpl\n     * @param {number} value\n     * @param {boolean} [isUTC=false] Default in local time.\n     *           see `module:echarts/scale/Time`\n     *           and `module:echarts/util/number#parseDate`.\n     * @inner\n     */\n\n    function formatTime(tpl, value, isUTC) {\n      if (\"development\" !== 'production') {\n        deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format');\n      }\n\n      if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year') {\n        tpl = 'MM-dd\\nyyyy';\n      }\n\n      var date = parseDate(value);\n      var getUTC = isUTC ? 'getUTC' : 'get';\n      var y = date[getUTC + 'FullYear']();\n      var M = date[getUTC + 'Month']() + 1;\n      var d = date[getUTC + 'Date']();\n      var h = date[getUTC + 'Hours']();\n      var m = date[getUTC + 'Minutes']();\n      var s = date[getUTC + 'Seconds']();\n      var S = date[getUTC + 'Milliseconds']();\n      tpl = tpl.replace('MM', pad(M, 2)).replace('M', M).replace('yyyy', y).replace('yy', pad(y % 100 + '', 2)).replace('dd', pad(d, 2)).replace('d', d).replace('hh', pad(h, 2)).replace('h', h).replace('mm', pad(m, 2)).replace('m', m).replace('ss', pad(s, 2)).replace('s', s).replace('SSS', pad(S, 3));\n      return tpl;\n    }\n    /**\n     * Capital first\n     * @param {string} str\n     * @return {string}\n     */\n\n    function capitalFirst(str) {\n      return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;\n    }\n    /**\n     * @return Never be null/undefined.\n     */\n\n    function convertToColorString(color, defaultColor) {\n      defaultColor = defaultColor || 'transparent';\n      return isString(color) ? color : isObject(color) ? color.colorStops && (color.colorStops[0] || {}).color || defaultColor : defaultColor;\n    }\n    /**\n     * open new tab\n     * @param link url\n     * @param target blank or self\n     */\n\n    function windowOpen(link, target) {\n      /* global window */\n      if (target === '_blank' || target === 'blank') {\n        var blank = window.open();\n        blank.opener = null;\n        blank.location.href = link;\n      } else {\n        window.open(link, target);\n      }\n    }\n\n    var each$1 = each;\n    /**\n     * @public\n     */\n\n    var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];\n    /**\n     * @public\n     */\n\n    var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];\n\n    function boxLayout(orient, group, gap, maxWidth, maxHeight) {\n      var x = 0;\n      var y = 0;\n\n      if (maxWidth == null) {\n        maxWidth = Infinity;\n      }\n\n      if (maxHeight == null) {\n        maxHeight = Infinity;\n      }\n\n      var currentLineMaxSize = 0;\n      group.eachChild(function (child, idx) {\n        var rect = child.getBoundingRect();\n        var nextChild = group.childAt(idx + 1);\n        var nextChildRect = nextChild && nextChild.getBoundingRect();\n        var nextX;\n        var nextY;\n\n        if (orient === 'horizontal') {\n          var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);\n          nextX = x + moveX; // Wrap when width exceeds maxWidth or meet a `newline` group\n          // FIXME compare before adding gap?\n\n          if (nextX > maxWidth || child.newline) {\n            x = 0;\n            nextX = moveX;\n            y += currentLineMaxSize + gap;\n            currentLineMaxSize = rect.height;\n          } else {\n            // FIXME: consider rect.y is not `0`?\n            currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);\n          }\n        } else {\n          var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);\n          nextY = y + moveY; // Wrap when width exceeds maxHeight or meet a `newline` group\n\n          if (nextY > maxHeight || child.newline) {\n            x += currentLineMaxSize + gap;\n            y = 0;\n            nextY = moveY;\n            currentLineMaxSize = rect.width;\n          } else {\n            currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);\n          }\n        }\n\n        if (child.newline) {\n          return;\n        }\n\n        child.x = x;\n        child.y = y;\n        child.markRedraw();\n        orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;\n      });\n    }\n    /**\n     * VBox or HBox layouting\n     * @param {string} orient\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n\n    var box = boxLayout;\n    /**\n     * VBox layouting\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n    var vbox = curry(boxLayout, 'vertical');\n    /**\n     * HBox layouting\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n    var hbox = curry(boxLayout, 'horizontal');\n    /**\n     * Parse position info.\n     */\n\n    function getLayoutRect(positionInfo, containerRect, margin) {\n      margin = normalizeCssArray$1(margin || 0);\n      var containerWidth = containerRect.width;\n      var containerHeight = containerRect.height;\n      var left = parsePercent$1(positionInfo.left, containerWidth);\n      var top = parsePercent$1(positionInfo.top, containerHeight);\n      var right = parsePercent$1(positionInfo.right, containerWidth);\n      var bottom = parsePercent$1(positionInfo.bottom, containerHeight);\n      var width = parsePercent$1(positionInfo.width, containerWidth);\n      var height = parsePercent$1(positionInfo.height, containerHeight);\n      var verticalMargin = margin[2] + margin[0];\n      var horizontalMargin = margin[1] + margin[3];\n      var aspect = positionInfo.aspect; // If width is not specified, calculate width from left and right\n\n      if (isNaN(width)) {\n        width = containerWidth - right - horizontalMargin - left;\n      }\n\n      if (isNaN(height)) {\n        height = containerHeight - bottom - verticalMargin - top;\n      }\n\n      if (aspect != null) {\n        // If width and height are not given\n        // 1. Graph should not exceeds the container\n        // 2. Aspect must be keeped\n        // 3. Graph should take the space as more as possible\n        // FIXME\n        // Margin is not considered, because there is no case that both\n        // using margin and aspect so far.\n        if (isNaN(width) && isNaN(height)) {\n          if (aspect > containerWidth / containerHeight) {\n            width = containerWidth * 0.8;\n          } else {\n            height = containerHeight * 0.8;\n          }\n        } // Calculate width or height with given aspect\n\n\n        if (isNaN(width)) {\n          width = aspect * height;\n        }\n\n        if (isNaN(height)) {\n          height = width / aspect;\n        }\n      } // If left is not specified, calculate left from right and width\n\n\n      if (isNaN(left)) {\n        left = containerWidth - right - width - horizontalMargin;\n      }\n\n      if (isNaN(top)) {\n        top = containerHeight - bottom - height - verticalMargin;\n      } // Align left and top\n\n\n      switch (positionInfo.left || positionInfo.right) {\n        case 'center':\n          left = containerWidth / 2 - width / 2 - margin[3];\n          break;\n\n        case 'right':\n          left = containerWidth - width - horizontalMargin;\n          break;\n      }\n\n      switch (positionInfo.top || positionInfo.bottom) {\n        case 'middle':\n        case 'center':\n          top = containerHeight / 2 - height / 2 - margin[0];\n          break;\n\n        case 'bottom':\n          top = containerHeight - height - verticalMargin;\n          break;\n      } // If something is wrong and left, top, width, height are calculated as NaN\n\n\n      left = left || 0;\n      top = top || 0;\n\n      if (isNaN(width)) {\n        // Width may be NaN if only one value is given except width\n        width = containerWidth - horizontalMargin - left - (right || 0);\n      }\n\n      if (isNaN(height)) {\n        // Height may be NaN if only one value is given except height\n        height = containerHeight - verticalMargin - top - (bottom || 0);\n      }\n\n      var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);\n      rect.margin = margin;\n      return rect;\n    }\n    /**\n     * Position a zr element in viewport\n     *  Group position is specified by either\n     *  {left, top}, {right, bottom}\n     *  If all properties exists, right and bottom will be igonred.\n     *\n     * Logic:\n     *     1. Scale (against origin point in parent coord)\n     *     2. Rotate (against origin point in parent coord)\n     *     3. Translate (with el.position by this method)\n     * So this method only fixes the last step 'Translate', which does not affect\n     * scaling and rotating.\n     *\n     * If be called repeatedly with the same input el, the same result will be gotten.\n     *\n     * Return true if the layout happened.\n     *\n     * @param el Should have `getBoundingRect` method.\n     * @param positionInfo\n     * @param positionInfo.left\n     * @param positionInfo.top\n     * @param positionInfo.right\n     * @param positionInfo.bottom\n     * @param positionInfo.width Only for opt.boundingModel: 'raw'\n     * @param positionInfo.height Only for opt.boundingModel: 'raw'\n     * @param containerRect\n     * @param margin\n     * @param opt\n     * @param opt.hv Only horizontal or only vertical. Default to be [1, 1]\n     * @param opt.boundingMode\n     *        Specify how to calculate boundingRect when locating.\n     *        'all': Position the boundingRect that is transformed and uioned\n     *               both itself and its descendants.\n     *               This mode simplies confine the elements in the bounding\n     *               of their container (e.g., using 'right: 0').\n     *        'raw': Position the boundingRect that is not transformed and only itself.\n     *               This mode is useful when you want a element can overflow its\n     *               container. (Consider a rotated circle needs to be located in a corner.)\n     *               In this mode positionInfo.width/height can only be number.\n     */\n\n    function positionElement(el, positionInfo, containerRect, margin, opt, out) {\n      var h = !opt || !opt.hv || opt.hv[0];\n      var v = !opt || !opt.hv || opt.hv[1];\n      var boundingMode = opt && opt.boundingMode || 'all';\n      out = out || el;\n      out.x = el.x;\n      out.y = el.y;\n\n      if (!h && !v) {\n        return false;\n      }\n\n      var rect;\n\n      if (boundingMode === 'raw') {\n        rect = el.type === 'group' ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) : el.getBoundingRect();\n      } else {\n        rect = el.getBoundingRect();\n\n        if (el.needLocalTransform()) {\n          var transform = el.getLocalTransform(); // Notice: raw rect may be inner object of el,\n          // which should not be modified.\n\n          rect = rect.clone();\n          rect.applyTransform(transform);\n        }\n      } // The real width and height can not be specified but calculated by the given el.\n\n\n      var layoutRect = getLayoutRect(defaults({\n        width: rect.width,\n        height: rect.height\n      }, positionInfo), containerRect, margin); // Because 'tranlate' is the last step in transform\n      // (see zrender/core/Transformable#getLocalTransform),\n      // we can just only modify el.position to get final result.\n\n      var dx = h ? layoutRect.x - rect.x : 0;\n      var dy = v ? layoutRect.y - rect.y : 0;\n\n      if (boundingMode === 'raw') {\n        out.x = dx;\n        out.y = dy;\n      } else {\n        out.x += dx;\n        out.y += dy;\n      }\n\n      if (out === el) {\n        el.markRedraw();\n      }\n\n      return true;\n    }\n    function fetchLayoutMode(ins) {\n      var layoutMode = ins.layoutMode || ins.constructor.layoutMode;\n      return isObject(layoutMode) ? layoutMode : layoutMode ? {\n        type: layoutMode\n      } : null;\n    }\n    /**\n     * Consider Case:\n     * When default option has {left: 0, width: 100}, and we set {right: 0}\n     * through setOption or media query, using normal zrUtil.merge will cause\n     * {right: 0} does not take effect.\n     *\n     * @example\n     * ComponentModel.extend({\n     *     init: function () {\n     *         ...\n     *         let inputPositionParams = layout.getLayoutParams(option);\n     *         this.mergeOption(inputPositionParams);\n     *     },\n     *     mergeOption: function (newOption) {\n     *         newOption && zrUtil.merge(thisOption, newOption, true);\n     *         layout.mergeLayoutParam(thisOption, newOption);\n     *     }\n     * });\n     *\n     * @param targetOption\n     * @param newOption\n     * @param opt\n     */\n\n    function mergeLayoutParam(targetOption, newOption, opt) {\n      var ignoreSize = opt && opt.ignoreSize;\n      !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);\n      var hResult = merge(HV_NAMES[0], 0);\n      var vResult = merge(HV_NAMES[1], 1);\n      copy(HV_NAMES[0], targetOption, hResult);\n      copy(HV_NAMES[1], targetOption, vResult);\n\n      function merge(names, hvIdx) {\n        var newParams = {};\n        var newValueCount = 0;\n        var merged = {};\n        var mergedValueCount = 0;\n        var enoughParamNumber = 2;\n        each$1(names, function (name) {\n          merged[name] = targetOption[name];\n        });\n        each$1(names, function (name) {\n          // Consider case: newOption.width is null, which is\n          // set by user for removing width setting.\n          hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);\n          hasValue(newParams, name) && newValueCount++;\n          hasValue(merged, name) && mergedValueCount++;\n        });\n\n        if (ignoreSize[hvIdx]) {\n          // Only one of left/right is premitted to exist.\n          if (hasValue(newOption, names[1])) {\n            merged[names[2]] = null;\n          } else if (hasValue(newOption, names[2])) {\n            merged[names[1]] = null;\n          }\n\n          return merged;\n        } // Case: newOption: {width: ..., right: ...},\n        // or targetOption: {right: ...} and newOption: {width: ...},\n        // There is no conflict when merged only has params count\n        // little than enoughParamNumber.\n\n\n        if (mergedValueCount === enoughParamNumber || !newValueCount) {\n          return merged;\n        } // Case: newOption: {width: ..., right: ...},\n        // Than we can make sure user only want those two, and ignore\n        // all origin params in targetOption.\n        else if (newValueCount >= enoughParamNumber) {\n            return newParams;\n          } else {\n            // Chose another param from targetOption by priority.\n            for (var i = 0; i < names.length; i++) {\n              var name_1 = names[i];\n\n              if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) {\n                newParams[name_1] = targetOption[name_1];\n                break;\n              }\n            }\n\n            return newParams;\n          }\n      }\n\n      function hasProp(obj, name) {\n        return obj.hasOwnProperty(name);\n      }\n\n      function hasValue(obj, name) {\n        return obj[name] != null && obj[name] !== 'auto';\n      }\n\n      function copy(names, target, source) {\n        each$1(names, function (name) {\n          target[name] = source[name];\n        });\n      }\n    }\n    /**\n     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n     */\n\n    function getLayoutParams(source) {\n      return copyLayoutParams({}, source);\n    }\n    /**\n     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n     * @param {Object} source\n     * @return {Object} Result contains those props.\n     */\n\n    function copyLayoutParams(target, source) {\n      source && target && each$1(LOCATION_PARAMS, function (name) {\n        source.hasOwnProperty(name) && (target[name] = source[name]);\n      });\n      return target;\n    }\n\n    var inner = makeInner();\n\n    var ComponentModel =\n    /** @class */\n    function (_super) {\n      __extends(ComponentModel, _super);\n\n      function ComponentModel(option, parentModel, ecModel) {\n        var _this = _super.call(this, option, parentModel, ecModel) || this;\n\n        _this.uid = getUID('ec_cpt_model');\n        return _this;\n      }\n\n      ComponentModel.prototype.init = function (option, parentModel, ecModel) {\n        this.mergeDefaultAndTheme(option, ecModel);\n      };\n\n      ComponentModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n        var layoutMode = fetchLayoutMode(this);\n        var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n        var themeModel = ecModel.getTheme();\n        merge(option, themeModel.get(this.mainType));\n        merge(option, this.getDefaultOption());\n\n        if (layoutMode) {\n          mergeLayoutParam(option, inputPositionParams, layoutMode);\n        }\n      };\n\n      ComponentModel.prototype.mergeOption = function (option, ecModel) {\n        merge(this.option, option, true);\n        var layoutMode = fetchLayoutMode(this);\n\n        if (layoutMode) {\n          mergeLayoutParam(this.option, option, layoutMode);\n        }\n      };\n      /**\n       * Called immediately after `init` or `mergeOption` of this instance called.\n       */\n\n\n      ComponentModel.prototype.optionUpdated = function (newCptOption, isInit) {};\n      /**\n       * [How to declare defaultOption]:\n       *\n       * (A) If using class declaration in typescript (since echarts 5):\n       * ```ts\n       * import {ComponentOption} from '../model/option.js';\n       * export interface XxxOption extends ComponentOption {\n       *     aaa: number\n       * }\n       * export class XxxModel extends Component {\n       *     static type = 'xxx';\n       *     static defaultOption: XxxOption = {\n       *         aaa: 123\n       *     }\n       * }\n       * Component.registerClass(XxxModel);\n       * ```\n       * ```ts\n       * import {inheritDefaultOption} from '../util/component.js';\n       * import {XxxModel, XxxOption} from './XxxModel.js';\n       * export interface XxxSubOption extends XxxOption {\n       *     bbb: number\n       * }\n       * class XxxSubModel extends XxxModel {\n       *     static defaultOption: XxxSubOption = inheritDefaultOption(XxxModel.defaultOption, {\n       *         bbb: 456\n       *     })\n       *     fn() {\n       *         let opt = this.getDefaultOption();\n       *         // opt is {aaa: 123, bbb: 456}\n       *     }\n       * }\n       * ```\n       *\n       * (B) If using class extend (previous approach in echarts 3 & 4):\n       * ```js\n       * let XxxComponent = Component.extend({\n       *     defaultOption: {\n       *         xx: 123\n       *     }\n       * })\n       * ```\n       * ```js\n       * let XxxSubComponent = XxxComponent.extend({\n       *     defaultOption: {\n       *         yy: 456\n       *     },\n       *     fn: function () {\n       *         let opt = this.getDefaultOption();\n       *         // opt is {xx: 123, yy: 456}\n       *     }\n       * })\n       * ```\n       */\n\n\n      ComponentModel.prototype.getDefaultOption = function () {\n        var ctor = this.constructor; // If using class declaration, it is different to travel super class\n        // in legacy env and auto merge defaultOption. So if using class\n        // declaration, defaultOption should be merged manually.\n\n        if (!isExtendedClass(ctor)) {\n          // When using ts class, defaultOption must be declared as static.\n          return ctor.defaultOption;\n        } // FIXME: remove this approach?\n\n\n        var fields = inner(this);\n\n        if (!fields.defaultOption) {\n          var optList = [];\n          var clz = ctor;\n\n          while (clz) {\n            var opt = clz.prototype.defaultOption;\n            opt && optList.push(opt);\n            clz = clz.superClass;\n          }\n\n          var defaultOption = {};\n\n          for (var i = optList.length - 1; i >= 0; i--) {\n            defaultOption = merge(defaultOption, optList[i], true);\n          }\n\n          fields.defaultOption = defaultOption;\n        }\n\n        return fields.defaultOption;\n      };\n      /**\n       * Notice: always force to input param `useDefault` in case that forget to consider it.\n       * The same behavior as `modelUtil.parseFinder`.\n       *\n       * @param useDefault In many cases like series refer axis and axis refer grid,\n       *        If axis index / axis id not specified, use the first target as default.\n       *        In other cases like dataZoom refer axis, if not specified, measn no refer.\n       */\n\n\n      ComponentModel.prototype.getReferringComponents = function (mainType, opt) {\n        var indexKey = mainType + 'Index';\n        var idKey = mainType + 'Id';\n        return queryReferringComponents(this.ecModel, mainType, {\n          index: this.get(indexKey, true),\n          id: this.get(idKey, true)\n        }, opt);\n      };\n\n      ComponentModel.prototype.getBoxLayoutParams = function () {\n        // Consider itself having box layout configs.\n        var boxLayoutModel = this;\n        return {\n          left: boxLayoutModel.get('left'),\n          top: boxLayoutModel.get('top'),\n          right: boxLayoutModel.get('right'),\n          bottom: boxLayoutModel.get('bottom'),\n          width: boxLayoutModel.get('width'),\n          height: boxLayoutModel.get('height')\n        };\n      };\n      /**\n       * Get key for zlevel.\n       * If developers don't configure zlevel. We will assign zlevel to series based on the key.\n       * For example, lines with trail effect and progressive series will in an individual zlevel.\n       */\n\n\n      ComponentModel.prototype.getZLevelKey = function () {\n        return '';\n      };\n\n      ComponentModel.prototype.setZLevel = function (zlevel) {\n        this.option.zlevel = zlevel;\n      };\n\n      ComponentModel.protoInitialize = function () {\n        var proto = ComponentModel.prototype;\n        proto.type = 'component';\n        proto.id = '';\n        proto.name = '';\n        proto.mainType = '';\n        proto.subType = '';\n        proto.componentIndex = 0;\n      }();\n\n      return ComponentModel;\n    }(Model);\n\n    mountExtend(ComponentModel, Model);\n    enableClassManagement(ComponentModel);\n    enableSubTypeDefaulter(ComponentModel);\n    enableTopologicalTravel(ComponentModel, getDependencies);\n\n    function getDependencies(componentType) {\n      var deps = [];\n      each(ComponentModel.getClassesByMainType(componentType), function (clz) {\n        deps = deps.concat(clz.dependencies || clz.prototype.dependencies || []);\n      }); // Ensure main type.\n\n      deps = map(deps, function (type) {\n        return parseClassType(type).main;\n      }); // Hack dataset for convenience.\n\n      if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {\n        deps.unshift('dataset');\n      }\n\n      return deps;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var platform = ''; // Navigator not exists in node\n\n    if (typeof navigator !== 'undefined') {\n      /* global navigator */\n      platform = navigator.platform || '';\n    }\n\n    var decalColor = 'rgba(0, 0, 0, 0.2)';\n    var globalDefault = {\n      darkMode: 'auto',\n      // backgroundColor: 'rgba(0,0,0,0)',\n      colorBy: 'series',\n      color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],\n      gradientColor: ['#f6efa6', '#d88273', '#bf444c'],\n      aria: {\n        decal: {\n          decals: [{\n            color: decalColor,\n            dashArrayX: [1, 0],\n            dashArrayY: [2, 5],\n            symbolSize: 1,\n            rotation: Math.PI / 6\n          }, {\n            color: decalColor,\n            symbol: 'circle',\n            dashArrayX: [[8, 8], [0, 8, 8, 0]],\n            dashArrayY: [6, 0],\n            symbolSize: 0.8\n          }, {\n            color: decalColor,\n            dashArrayX: [1, 0],\n            dashArrayY: [4, 3],\n            rotation: -Math.PI / 4\n          }, {\n            color: decalColor,\n            dashArrayX: [[6, 6], [0, 6, 6, 0]],\n            dashArrayY: [6, 0]\n          }, {\n            color: decalColor,\n            dashArrayX: [[1, 0], [1, 6]],\n            dashArrayY: [1, 0, 6, 0],\n            rotation: Math.PI / 4\n          }, {\n            color: decalColor,\n            symbol: 'triangle',\n            dashArrayX: [[9, 9], [0, 9, 9, 0]],\n            dashArrayY: [7, 2],\n            symbolSize: 0.75\n          }]\n        }\n      },\n      // If xAxis and yAxis declared, grid is created by default.\n      // grid: {},\n      textStyle: {\n        // color: '#000',\n        // decoration: 'none',\n        // PENDING\n        fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',\n        // fontFamily: 'Arial, Verdana, sans-serif',\n        fontSize: 12,\n        fontStyle: 'normal',\n        fontWeight: 'normal'\n      },\n      // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/\n      // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n      // Default is source-over\n      blendMode: null,\n      stateAnimation: {\n        duration: 300,\n        easing: 'cubicOut'\n      },\n      animation: 'auto',\n      animationDuration: 1000,\n      animationDurationUpdate: 500,\n      animationEasing: 'cubicInOut',\n      animationEasingUpdate: 'cubicInOut',\n      animationThreshold: 2000,\n      // Configuration for progressive/incremental rendering\n      progressiveThreshold: 3000,\n      progressive: 400,\n      // Threshold of if use single hover layer to optimize.\n      // It is recommended that `hoverLayerThreshold` is equivalent to or less than\n      // `progressiveThreshold`, otherwise hover will cause restart of progressive,\n      // which is unexpected.\n      // see example <echarts/test/heatmap-large.html>.\n      hoverLayerThreshold: 3000,\n      // See: module:echarts/scale/Time\n      useUTC: false\n    };\n\n    var VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName']);\n    var SOURCE_FORMAT_ORIGINAL = 'original';\n    var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';\n    var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';\n    var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';\n    var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';\n    var SOURCE_FORMAT_UNKNOWN = 'unknown';\n    var SERIES_LAYOUT_BY_COLUMN = 'column';\n    var SERIES_LAYOUT_BY_ROW = 'row';\n\n    var BE_ORDINAL = {\n      Must: 1,\n      Might: 2,\n      Not: 3 // Other cases\n\n    };\n    var innerGlobalModel = makeInner();\n    /**\n     * MUST be called before mergeOption of all series.\n     */\n\n    function resetSourceDefaulter(ecModel) {\n      // `datasetMap` is used to make default encode.\n      innerGlobalModel(ecModel).datasetMap = createHashMap();\n    }\n    /**\n     * [The strategy of the arrengment of data dimensions for dataset]:\n     * \"value way\": all axes are non-category axes. So series one by one take\n     *     several (the number is coordSysDims.length) dimensions from dataset.\n     *     The result of data arrengment of data dimensions like:\n     *     | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y |\n     * \"category way\": at least one axis is category axis. So the the first data\n     *     dimension is always mapped to the first category axis and shared by\n     *     all of the series. The other data dimensions are taken by series like\n     *     \"value way\" does.\n     *     The result of data arrengment of data dimensions like:\n     *     | ser_shared_x | ser0_y | ser1_y | ser2_y |\n     *\n     * @return encode Never be `null/undefined`.\n     */\n\n    function makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) {\n      var encode = {};\n      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n      if (!datasetModel || !coordDimensions) {\n        return encode;\n      }\n\n      var encodeItemName = [];\n      var encodeSeriesName = [];\n      var ecModel = seriesModel.ecModel;\n      var datasetMap = innerGlobalModel(ecModel).datasetMap;\n      var key = datasetModel.uid + '_' + source.seriesLayoutBy;\n      var baseCategoryDimIndex;\n      var categoryWayValueDimStart;\n      coordDimensions = coordDimensions.slice();\n      each(coordDimensions, function (coordDimInfoLoose, coordDimIdx) {\n        var coordDimInfo = isObject(coordDimInfoLoose) ? coordDimInfoLoose : coordDimensions[coordDimIdx] = {\n          name: coordDimInfoLoose\n        };\n\n        if (coordDimInfo.type === 'ordinal' && baseCategoryDimIndex == null) {\n          baseCategoryDimIndex = coordDimIdx;\n          categoryWayValueDimStart = getDataDimCountOnCoordDim(coordDimInfo);\n        }\n\n        encode[coordDimInfo.name] = [];\n      });\n      var datasetRecord = datasetMap.get(key) || datasetMap.set(key, {\n        categoryWayDim: categoryWayValueDimStart,\n        valueWayDim: 0\n      }); // TODO\n      // Auto detect first time axis and do arrangement.\n\n      each(coordDimensions, function (coordDimInfo, coordDimIdx) {\n        var coordDimName = coordDimInfo.name;\n        var count = getDataDimCountOnCoordDim(coordDimInfo); // In value way.\n\n        if (baseCategoryDimIndex == null) {\n          var start = datasetRecord.valueWayDim;\n          pushDim(encode[coordDimName], start, count);\n          pushDim(encodeSeriesName, start, count);\n          datasetRecord.valueWayDim += count; // ??? TODO give a better default series name rule?\n          // especially when encode x y specified.\n          // consider: when multiple series share one dimension\n          // category axis, series name should better use\n          // the other dimension name. On the other hand, use\n          // both dimensions name.\n        } // In category way, the first category axis.\n        else if (baseCategoryDimIndex === coordDimIdx) {\n            pushDim(encode[coordDimName], 0, count);\n            pushDim(encodeItemName, 0, count);\n          } // In category way, the other axis.\n          else {\n              var start = datasetRecord.categoryWayDim;\n              pushDim(encode[coordDimName], start, count);\n              pushDim(encodeSeriesName, start, count);\n              datasetRecord.categoryWayDim += count;\n            }\n      });\n\n      function pushDim(dimIdxArr, idxFrom, idxCount) {\n        for (var i = 0; i < idxCount; i++) {\n          dimIdxArr.push(idxFrom + i);\n        }\n      }\n\n      function getDataDimCountOnCoordDim(coordDimInfo) {\n        var dimsDef = coordDimInfo.dimsDef;\n        return dimsDef ? dimsDef.length : 1;\n      }\n\n      encodeItemName.length && (encode.itemName = encodeItemName);\n      encodeSeriesName.length && (encode.seriesName = encodeSeriesName);\n      return encode;\n    }\n    /**\n     * Work for data like [{name: ..., value: ...}, ...].\n     *\n     * @return encode Never be `null/undefined`.\n     */\n\n    function makeSeriesEncodeForNameBased(seriesModel, source, dimCount) {\n      var encode = {};\n      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n      if (!datasetModel) {\n        return encode;\n      }\n\n      var sourceFormat = source.sourceFormat;\n      var dimensionsDefine = source.dimensionsDefine;\n      var potentialNameDimIndex;\n\n      if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        each(dimensionsDefine, function (dim, idx) {\n          if ((isObject(dim) ? dim.name : dim) === 'name') {\n            potentialNameDimIndex = idx;\n          }\n        });\n      }\n\n      var idxResult = function () {\n        var idxRes0 = {};\n        var idxRes1 = {};\n        var guessRecords = []; // 5 is an experience value.\n\n        for (var i = 0, len = Math.min(5, dimCount); i < len; i++) {\n          var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i);\n          guessRecords.push(guessResult);\n          var isPureNumber = guessResult === BE_ORDINAL.Not; // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim,\n          // and then find a name dim with the priority:\n          // \"BE_ORDINAL.Might|BE_ORDINAL.Must\" > \"other dim\" > \"the value dim itself\".\n\n          if (isPureNumber && idxRes0.v == null && i !== potentialNameDimIndex) {\n            idxRes0.v = i;\n          }\n\n          if (idxRes0.n == null || idxRes0.n === idxRes0.v || !isPureNumber && guessRecords[idxRes0.n] === BE_ORDINAL.Not) {\n            idxRes0.n = i;\n          }\n\n          if (fulfilled(idxRes0) && guessRecords[idxRes0.n] !== BE_ORDINAL.Not) {\n            return idxRes0;\n          } // [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not),\n          // find the first BE_ORDINAL.Might as the value dim,\n          // and then find a name dim with the priority:\n          // \"other dim\" > \"the value dim itself\".\n          // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be\n          // treated as number.\n\n\n          if (!isPureNumber) {\n            if (guessResult === BE_ORDINAL.Might && idxRes1.v == null && i !== potentialNameDimIndex) {\n              idxRes1.v = i;\n            }\n\n            if (idxRes1.n == null || idxRes1.n === idxRes1.v) {\n              idxRes1.n = i;\n            }\n          }\n        }\n\n        function fulfilled(idxResult) {\n          return idxResult.v != null && idxResult.n != null;\n        }\n\n        return fulfilled(idxRes0) ? idxRes0 : fulfilled(idxRes1) ? idxRes1 : null;\n      }();\n\n      if (idxResult) {\n        encode.value = [idxResult.v]; // `potentialNameDimIndex` has highest priority.\n\n        var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; // By default, label uses itemName in charts.\n        // So we don't set encodeLabel here.\n\n        encode.itemName = [nameDimIndex];\n        encode.seriesName = [nameDimIndex];\n      }\n\n      return encode;\n    }\n    /**\n     * @return If return null/undefined, indicate that should not use datasetModel.\n     */\n\n    function querySeriesUpstreamDatasetModel(seriesModel) {\n      // Caution: consider the scenario:\n      // A dataset is declared and a series is not expected to use the dataset,\n      // and at the beginning `setOption({series: { noData })` (just prepare other\n      // option but no data), then `setOption({series: {data: [...]}); In this case,\n      // the user should set an empty array to avoid that dataset is used by default.\n      var thisData = seriesModel.get('data', true);\n\n      if (!thisData) {\n        return queryReferringComponents(seriesModel.ecModel, 'dataset', {\n          index: seriesModel.get('datasetIndex', true),\n          id: seriesModel.get('datasetId', true)\n        }, SINGLE_REFERRING).models[0];\n      }\n    }\n    /**\n     * @return Always return an array event empty.\n     */\n\n    function queryDatasetUpstreamDatasetModels(datasetModel) {\n      // Only these attributes declared, we by defualt reference to `datasetIndex: 0`.\n      // Otherwise, no reference.\n      if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true)) {\n        return [];\n      }\n\n      return queryReferringComponents(datasetModel.ecModel, 'dataset', {\n        index: datasetModel.get('fromDatasetIndex', true),\n        id: datasetModel.get('fromDatasetId', true)\n      }, SINGLE_REFERRING).models;\n    }\n    /**\n     * The rule should not be complex, otherwise user might not\n     * be able to known where the data is wrong.\n     * The code is ugly, but how to make it neat?\n     */\n\n    function guessOrdinal(source, dimIndex) {\n      return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex);\n    } // dimIndex may be overflow source data.\n    // return {BE_ORDINAL}\n\n    function doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) {\n      var result; // Experience value.\n\n      var maxLoop = 5;\n\n      if (isTypedArray(data)) {\n        return BE_ORDINAL.Not;\n      } // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine\n      // always exists in source.\n\n\n      var dimName;\n      var dimType;\n\n      if (dimensionsDefine) {\n        var dimDefItem = dimensionsDefine[dimIndex];\n\n        if (isObject(dimDefItem)) {\n          dimName = dimDefItem.name;\n          dimType = dimDefItem.type;\n        } else if (isString(dimDefItem)) {\n          dimName = dimDefItem;\n        }\n      }\n\n      if (dimType != null) {\n        return dimType === 'ordinal' ? BE_ORDINAL.Must : BE_ORDINAL.Not;\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var dataArrayRows = data;\n\n        if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n          var sample = dataArrayRows[dimIndex];\n\n          for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {\n            if ((result = detectValue(sample[startIndex + i])) != null) {\n              return result;\n            }\n          }\n        } else {\n          for (var i = 0; i < dataArrayRows.length && i < maxLoop; i++) {\n            var row = dataArrayRows[startIndex + i];\n\n            if (row && (result = detectValue(row[dimIndex])) != null) {\n              return result;\n            }\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        var dataObjectRows = data;\n\n        if (!dimName) {\n          return BE_ORDINAL.Not;\n        }\n\n        for (var i = 0; i < dataObjectRows.length && i < maxLoop; i++) {\n          var item = dataObjectRows[i];\n\n          if (item && (result = detectValue(item[dimName])) != null) {\n            return result;\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        var dataKeyedColumns = data;\n\n        if (!dimName) {\n          return BE_ORDINAL.Not;\n        }\n\n        var sample = dataKeyedColumns[dimName];\n\n        if (!sample || isTypedArray(sample)) {\n          return BE_ORDINAL.Not;\n        }\n\n        for (var i = 0; i < sample.length && i < maxLoop; i++) {\n          if ((result = detectValue(sample[i])) != null) {\n            return result;\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var dataOriginal = data;\n\n        for (var i = 0; i < dataOriginal.length && i < maxLoop; i++) {\n          var item = dataOriginal[i];\n          var val = getDataItemValue(item);\n\n          if (!isArray(val)) {\n            return BE_ORDINAL.Not;\n          }\n\n          if ((result = detectValue(val[dimIndex])) != null) {\n            return result;\n          }\n        }\n      }\n\n      function detectValue(val) {\n        var beStr = isString(val); // Consider usage convenience, '1', '2' will be treated as \"number\".\n        // `isFinit('')` get `true`.\n\n        if (val != null && isFinite(val) && val !== '') {\n          return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not;\n        } else if (beStr && val !== '-') {\n          return BE_ORDINAL.Must;\n        }\n      }\n\n      return BE_ORDINAL.Not;\n    }\n\n    var internalOptionCreatorMap = createHashMap();\n    function registerInternalOptionCreator(mainType, creator) {\n      assert(internalOptionCreatorMap.get(mainType) == null && creator);\n      internalOptionCreatorMap.set(mainType, creator);\n    }\n    function concatInternalOptions(ecModel, mainType, newCmptOptionList) {\n      var internalOptionCreator = internalOptionCreatorMap.get(mainType);\n\n      if (!internalOptionCreator) {\n        return newCmptOptionList;\n      }\n\n      var internalOptions = internalOptionCreator(ecModel);\n\n      if (!internalOptions) {\n        return newCmptOptionList;\n      }\n\n      if (\"development\" !== 'production') {\n        for (var i = 0; i < internalOptions.length; i++) {\n          assert(isComponentIdInternal(internalOptions[i]));\n        }\n      }\n\n      return newCmptOptionList.concat(internalOptions);\n    }\n\n    var innerColor = makeInner();\n    var innerDecal = makeInner();\n\n    var PaletteMixin =\n    /** @class */\n    function () {\n      function PaletteMixin() {}\n\n      PaletteMixin.prototype.getColorFromPalette = function (name, scope, requestNum) {\n        var defaultPalette = normalizeToArray(this.get('color', true));\n        var layeredPalette = this.get('colorLayer', true);\n        return getFromPalette(this, innerColor, defaultPalette, layeredPalette, name, scope, requestNum);\n      };\n\n      PaletteMixin.prototype.clearColorPalette = function () {\n        clearPalette(this, innerColor);\n      };\n\n      return PaletteMixin;\n    }();\n\n    function getDecalFromPalette(ecModel, name, scope, requestNum) {\n      var defaultDecals = normalizeToArray(ecModel.get(['aria', 'decal', 'decals']));\n      return getFromPalette(ecModel, innerDecal, defaultDecals, null, name, scope, requestNum);\n    }\n\n    function getNearestPalette(palettes, requestColorNum) {\n      var paletteNum = palettes.length; // TODO palettes must be in order\n\n      for (var i = 0; i < paletteNum; i++) {\n        if (palettes[i].length > requestColorNum) {\n          return palettes[i];\n        }\n      }\n\n      return palettes[paletteNum - 1];\n    }\n    /**\n     * @param name MUST NOT be null/undefined. Otherwise call this function\n     *             twise with the same parameters will get different result.\n     * @param scope default this.\n     * @return Can be null/undefined\n     */\n\n\n    function getFromPalette(that, inner, defaultPalette, layeredPalette, name, scope, requestNum) {\n      scope = scope || that;\n      var scopeFields = inner(scope);\n      var paletteIdx = scopeFields.paletteIdx || 0;\n      var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; // Use `hasOwnProperty` to avoid conflict with Object.prototype.\n\n      if (paletteNameMap.hasOwnProperty(name)) {\n        return paletteNameMap[name];\n      }\n\n      var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum); // In case can't find in layered color palette.\n\n      palette = palette || defaultPalette;\n\n      if (!palette || !palette.length) {\n        return;\n      }\n\n      var pickedPaletteItem = palette[paletteIdx];\n\n      if (name) {\n        paletteNameMap[name] = pickedPaletteItem;\n      }\n\n      scopeFields.paletteIdx = (paletteIdx + 1) % palette.length;\n      return pickedPaletteItem;\n    }\n\n    function clearPalette(that, inner) {\n      inner(that).paletteIdx = 0;\n      inner(that).paletteNameMap = {};\n    }\n\n    // Internal method names:\n    // -----------------------\n\n    var reCreateSeriesIndices;\n    var assertSeriesInitialized;\n    var initBase;\n    var OPTION_INNER_KEY = '\\0_ec_inner';\n    var OPTION_INNER_VALUE = 1;\n    var BUITIN_COMPONENTS_MAP = {\n      grid: 'GridComponent',\n      polar: 'PolarComponent',\n      geo: 'GeoComponent',\n      singleAxis: 'SingleAxisComponent',\n      parallel: 'ParallelComponent',\n      calendar: 'CalendarComponent',\n      graphic: 'GraphicComponent',\n      toolbox: 'ToolboxComponent',\n      tooltip: 'TooltipComponent',\n      axisPointer: 'AxisPointerComponent',\n      brush: 'BrushComponent',\n      title: 'TitleComponent',\n      timeline: 'TimelineComponent',\n      markPoint: 'MarkPointComponent',\n      markLine: 'MarkLineComponent',\n      markArea: 'MarkAreaComponent',\n      legend: 'LegendComponent',\n      dataZoom: 'DataZoomComponent',\n      visualMap: 'VisualMapComponent',\n      // aria: 'AriaComponent',\n      // dataset: 'DatasetComponent',\n      // Dependencies\n      xAxis: 'GridComponent',\n      yAxis: 'GridComponent',\n      angleAxis: 'PolarComponent',\n      radiusAxis: 'PolarComponent'\n    };\n    var BUILTIN_CHARTS_MAP = {\n      line: 'LineChart',\n      bar: 'BarChart',\n      pie: 'PieChart',\n      scatter: 'ScatterChart',\n      radar: 'RadarChart',\n      map: 'MapChart',\n      tree: 'TreeChart',\n      treemap: 'TreemapChart',\n      graph: 'GraphChart',\n      gauge: 'GaugeChart',\n      funnel: 'FunnelChart',\n      parallel: 'ParallelChart',\n      sankey: 'SankeyChart',\n      boxplot: 'BoxplotChart',\n      candlestick: 'CandlestickChart',\n      effectScatter: 'EffectScatterChart',\n      lines: 'LinesChart',\n      heatmap: 'HeatmapChart',\n      pictorialBar: 'PictorialBarChart',\n      themeRiver: 'ThemeRiverChart',\n      sunburst: 'SunburstChart',\n      custom: 'CustomChart'\n    };\n    var componetsMissingLogPrinted = {};\n\n    function checkMissingComponents(option) {\n      each(option, function (componentOption, mainType) {\n        if (!ComponentModel.hasClass(mainType)) {\n          var componentImportName = BUITIN_COMPONENTS_MAP[mainType];\n\n          if (componentImportName && !componetsMissingLogPrinted[componentImportName]) {\n            error(\"Component \" + mainType + \" is used but not imported.\\nimport { \" + componentImportName + \" } from 'echarts/components';\\necharts.use([\" + componentImportName + \"]);\");\n            componetsMissingLogPrinted[componentImportName] = true;\n          }\n        }\n      });\n    }\n\n    var GlobalModel =\n    /** @class */\n    function (_super) {\n      __extends(GlobalModel, _super);\n\n      function GlobalModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) {\n        theme = theme || {};\n        this.option = null; // Mark as not initialized.\n\n        this._theme = new Model(theme);\n        this._locale = new Model(locale);\n        this._optionManager = optionManager;\n      };\n\n      GlobalModel.prototype.setOption = function (option, opts, optionPreprocessorFuncs) {\n        if (\"development\" !== 'production') {\n          assert(option != null, 'option is null/undefined');\n          assert(option[OPTION_INNER_KEY] !== OPTION_INNER_VALUE, 'please use chart.getOption()');\n        }\n\n        var innerOpt = normalizeSetOptionInput(opts);\n\n        this._optionManager.setOption(option, optionPreprocessorFuncs, innerOpt);\n\n        this._resetOption(null, innerOpt);\n      };\n      /**\n       * @param type null/undefined: reset all.\n       *        'recreate': force recreate all.\n       *        'timeline': only reset timeline option\n       *        'media': only reset media query option\n       * @return Whether option changed.\n       */\n\n\n      GlobalModel.prototype.resetOption = function (type, opt) {\n        return this._resetOption(type, normalizeSetOptionInput(opt));\n      };\n\n      GlobalModel.prototype._resetOption = function (type, opt) {\n        var optionChanged = false;\n        var optionManager = this._optionManager;\n\n        if (!type || type === 'recreate') {\n          var baseOption = optionManager.mountOption(type === 'recreate');\n\n          if (\"development\" !== 'production') {\n            checkMissingComponents(baseOption);\n          }\n\n          if (!this.option || type === 'recreate') {\n            initBase(this, baseOption);\n          } else {\n            this.restoreData();\n\n            this._mergeOption(baseOption, opt);\n          }\n\n          optionChanged = true;\n        }\n\n        if (type === 'timeline' || type === 'media') {\n          this.restoreData();\n        } // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`,\n        // it should better not have the same props with `MediaUnit['option']`.\n        // Because either `option2` or `MediaUnit['option']` will be always merged to \"current option\"\n        // rather than original \"baseOption\". If they both override a prop, the result might be\n        // unexpected when media state changed after `setOption` called.\n        // If we really need to modify a props in each `MediaUnit['option']`, use the full version\n        // (`{baseOption, media}`) in `setOption`.\n        // For `timeline`, the case is the same.\n\n\n        if (!type || type === 'recreate' || type === 'timeline') {\n          var timelineOption = optionManager.getTimelineOption(this);\n\n          if (timelineOption) {\n            optionChanged = true;\n\n            this._mergeOption(timelineOption, opt);\n          }\n        }\n\n        if (!type || type === 'recreate' || type === 'media') {\n          var mediaOptions = optionManager.getMediaOption(this);\n\n          if (mediaOptions.length) {\n            each(mediaOptions, function (mediaOption) {\n              optionChanged = true;\n\n              this._mergeOption(mediaOption, opt);\n            }, this);\n          }\n        }\n\n        return optionChanged;\n      };\n\n      GlobalModel.prototype.mergeOption = function (option) {\n        this._mergeOption(option, null);\n      };\n\n      GlobalModel.prototype._mergeOption = function (newOption, opt) {\n        var option = this.option;\n        var componentsMap = this._componentsMap;\n        var componentsCount = this._componentsCount;\n        var newCmptTypes = [];\n        var newCmptTypeMap = createHashMap();\n        var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap;\n        resetSourceDefaulter(this); // If no component class, merge directly.\n        // For example: color, animaiton options, etc.\n\n        each(newOption, function (componentOption, mainType) {\n          if (componentOption == null) {\n            return;\n          }\n\n          if (!ComponentModel.hasClass(mainType)) {\n            // globalSettingTask.dirty();\n            option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true);\n          } else if (mainType) {\n            newCmptTypes.push(mainType);\n            newCmptTypeMap.set(mainType, true);\n          }\n        });\n\n        if (replaceMergeMainTypeMap) {\n          // If there is a mainType `xxx` in `replaceMerge` but not declared in option,\n          // we trade it as it is declared in option as `{xxx: []}`. Because:\n          // (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`.\n          // (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`.\n          replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) {\n            if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) {\n              newCmptTypes.push(mainTypeInReplaceMerge);\n              newCmptTypeMap.set(mainTypeInReplaceMerge, true);\n            }\n          });\n        }\n\n        ComponentModel.topologicalTravel(newCmptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this);\n\n        function visitComponent(mainType) {\n          var newCmptOptionList = concatInternalOptions(this, mainType, normalizeToArray(newOption[mainType]));\n          var oldCmptList = componentsMap.get(mainType);\n          var mergeMode = // `!oldCmptList` means init. See the comment in `mappingToExists`\n          !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge';\n          var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode); // Set mainType and complete subType.\n\n          setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); // Empty it before the travel, in order to prevent `this._componentsMap`\n          // from being used in the `init`/`mergeOption`/`optionUpdated` of some\n          // components, which is probably incorrect logic.\n\n          option[mainType] = null;\n          componentsMap.set(mainType, null);\n          componentsCount.set(mainType, 0);\n          var optionsByMainType = [];\n          var cmptsByMainType = [];\n          var cmptsCountByMainType = 0;\n          var tooltipExists;\n          var tooltipWarningLogged;\n          each(mappingResult, function (resultItem, index) {\n            var componentModel = resultItem.existing;\n            var newCmptOption = resultItem.newOption;\n\n            if (!newCmptOption) {\n              if (componentModel) {\n                // Consider where is no new option and should be merged using {},\n                // see removeEdgeAndAdd in topologicalTravel and\n                // ComponentModel.getAllClassMainTypes.\n                componentModel.mergeOption({}, this);\n                componentModel.optionUpdated({}, false);\n              } // If no both `resultItem.exist` and `resultItem.option`,\n              // either it is in `replaceMerge` and not matched by any id,\n              // or it has been removed in previous `replaceMerge` and left a \"hole\" in this component index.\n\n            } else {\n              var isSeriesType = mainType === 'series';\n              var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, !isSeriesType // Give a more detailed warn later if series don't exists\n              );\n\n              if (!ComponentModelClass) {\n                if (\"development\" !== 'production') {\n                  var subType = resultItem.keyInfo.subType;\n                  var seriesImportName = BUILTIN_CHARTS_MAP[subType];\n\n                  if (!componetsMissingLogPrinted[subType]) {\n                    componetsMissingLogPrinted[subType] = true;\n\n                    if (seriesImportName) {\n                      error(\"Series \" + subType + \" is used but not imported.\\nimport { \" + seriesImportName + \" } from 'echarts/charts';\\necharts.use([\" + seriesImportName + \"]);\");\n                    } else {\n                      error(\"Unknown series \" + subType);\n                    }\n                  }\n                }\n\n                return;\n              } // TODO Before multiple tooltips get supported, we do this check to avoid unexpected exception.\n\n\n              if (mainType === 'tooltip') {\n                if (tooltipExists) {\n                  if (\"development\" !== 'production') {\n                    if (!tooltipWarningLogged) {\n                      warn('Currently only one tooltip component is allowed.');\n                      tooltipWarningLogged = true;\n                    }\n                  }\n\n                  return;\n                }\n\n                tooltipExists = true;\n              }\n\n              if (componentModel && componentModel.constructor === ComponentModelClass) {\n                componentModel.name = resultItem.keyInfo.name; // componentModel.settingTask && componentModel.settingTask.dirty();\n\n                componentModel.mergeOption(newCmptOption, this);\n                componentModel.optionUpdated(newCmptOption, false);\n              } else {\n                // PENDING Global as parent ?\n                var extraOpt = extend({\n                  componentIndex: index\n                }, resultItem.keyInfo);\n                componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); // Assign `keyInfo`\n\n                extend(componentModel, extraOpt);\n\n                if (resultItem.brandNew) {\n                  componentModel.__requireNewView = true;\n                }\n\n                componentModel.init(newCmptOption, this, this); // Call optionUpdated after init.\n                // newCmptOption has been used as componentModel.option\n                // and may be merged with theme and default, so pass null\n                // to avoid confusion.\n\n                componentModel.optionUpdated(null, true);\n              }\n            }\n\n            if (componentModel) {\n              optionsByMainType.push(componentModel.option);\n              cmptsByMainType.push(componentModel);\n              cmptsCountByMainType++;\n            } else {\n              // Always do assign to avoid elided item in array.\n              optionsByMainType.push(void 0);\n              cmptsByMainType.push(void 0);\n            }\n          }, this);\n          option[mainType] = optionsByMainType;\n          componentsMap.set(mainType, cmptsByMainType);\n          componentsCount.set(mainType, cmptsCountByMainType); // Backup series for filtering.\n\n          if (mainType === 'series') {\n            reCreateSeriesIndices(this);\n          }\n        } // If no series declared, ensure `_seriesIndices` initialized.\n\n\n        if (!this._seriesIndices) {\n          reCreateSeriesIndices(this);\n        }\n      };\n      /**\n       * Get option for output (cloned option and inner info removed)\n       */\n\n\n      GlobalModel.prototype.getOption = function () {\n        var option = clone(this.option);\n        each(option, function (optInMainType, mainType) {\n          if (ComponentModel.hasClass(mainType)) {\n            var opts = normalizeToArray(optInMainType); // Inner cmpts need to be removed.\n            // Inner cmpts might not be at last since ec5.0, but still\n            // compatible for users: if inner cmpt at last, splice the returned array.\n\n            var realLen = opts.length;\n            var metNonInner = false;\n\n            for (var i = realLen - 1; i >= 0; i--) {\n              // Remove options with inner id.\n              if (opts[i] && !isComponentIdInternal(opts[i])) {\n                metNonInner = true;\n              } else {\n                opts[i] = null;\n                !metNonInner && realLen--;\n              }\n            }\n\n            opts.length = realLen;\n            option[mainType] = opts;\n          }\n        });\n        delete option[OPTION_INNER_KEY];\n        return option;\n      };\n\n      GlobalModel.prototype.getTheme = function () {\n        return this._theme;\n      };\n\n      GlobalModel.prototype.getLocaleModel = function () {\n        return this._locale;\n      };\n\n      GlobalModel.prototype.setUpdatePayload = function (payload) {\n        this._payload = payload;\n      };\n\n      GlobalModel.prototype.getUpdatePayload = function () {\n        return this._payload;\n      };\n      /**\n       * @param idx If not specified, return the first one.\n       */\n\n\n      GlobalModel.prototype.getComponent = function (mainType, idx) {\n        var list = this._componentsMap.get(mainType);\n\n        if (list) {\n          var cmpt = list[idx || 0];\n\n          if (cmpt) {\n            return cmpt;\n          } else if (idx == null) {\n            for (var i = 0; i < list.length; i++) {\n              if (list[i]) {\n                return list[i];\n              }\n            }\n          }\n        }\n      };\n      /**\n       * @return Never be null/undefined.\n       */\n\n\n      GlobalModel.prototype.queryComponents = function (condition) {\n        var mainType = condition.mainType;\n\n        if (!mainType) {\n          return [];\n        }\n\n        var index = condition.index;\n        var id = condition.id;\n        var name = condition.name;\n\n        var cmpts = this._componentsMap.get(mainType);\n\n        if (!cmpts || !cmpts.length) {\n          return [];\n        }\n\n        var result;\n\n        if (index != null) {\n          result = [];\n          each(normalizeToArray(index), function (idx) {\n            cmpts[idx] && result.push(cmpts[idx]);\n          });\n        } else if (id != null) {\n          result = queryByIdOrName('id', id, cmpts);\n        } else if (name != null) {\n          result = queryByIdOrName('name', name, cmpts);\n        } else {\n          // Return all non-empty components in that mainType\n          result = filter(cmpts, function (cmpt) {\n            return !!cmpt;\n          });\n        }\n\n        return filterBySubType(result, condition);\n      };\n      /**\n       * The interface is different from queryComponents,\n       * which is convenient for inner usage.\n       *\n       * @usage\n       * let result = findComponents(\n       *     {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}\n       * );\n       * let result = findComponents(\n       *     {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}\n       * );\n       * let result = findComponents(\n       *     {mainType: 'series',\n       *     filter: function (model, index) {...}}\n       * );\n       * // result like [component0, componnet1, ...]\n       */\n\n\n      GlobalModel.prototype.findComponents = function (condition) {\n        var query = condition.query;\n        var mainType = condition.mainType;\n        var queryCond = getQueryCond(query);\n        var result = queryCond ? this.queryComponents(queryCond) // Retrieve all non-empty components.\n        : filter(this._componentsMap.get(mainType), function (cmpt) {\n          return !!cmpt;\n        });\n        return doFilter(filterBySubType(result, condition));\n\n        function getQueryCond(q) {\n          var indexAttr = mainType + 'Index';\n          var idAttr = mainType + 'Id';\n          var nameAttr = mainType + 'Name';\n          return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? {\n            mainType: mainType,\n            // subType will be filtered finally.\n            index: q[indexAttr],\n            id: q[idAttr],\n            name: q[nameAttr]\n          } : null;\n        }\n\n        function doFilter(res) {\n          return condition.filter ? filter(res, condition.filter) : res;\n        }\n      };\n\n      GlobalModel.prototype.eachComponent = function (mainType, cb, context) {\n        var componentsMap = this._componentsMap;\n\n        if (isFunction(mainType)) {\n          var ctxForAll_1 = cb;\n          var cbForAll_1 = mainType;\n          componentsMap.each(function (cmpts, componentType) {\n            for (var i = 0; cmpts && i < cmpts.length; i++) {\n              var cmpt = cmpts[i];\n              cmpt && cbForAll_1.call(ctxForAll_1, componentType, cmpt, cmpt.componentIndex);\n            }\n          });\n        } else {\n          var cmpts = isString(mainType) ? componentsMap.get(mainType) : isObject(mainType) ? this.findComponents(mainType) : null;\n\n          for (var i = 0; cmpts && i < cmpts.length; i++) {\n            var cmpt = cmpts[i];\n            cmpt && cb.call(context, cmpt, cmpt.componentIndex);\n          }\n        }\n      };\n      /**\n       * Get series list before filtered by name.\n       */\n\n\n      GlobalModel.prototype.getSeriesByName = function (name) {\n        var nameStr = convertOptionIdName(name, null);\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries && nameStr != null && oneSeries.name === nameStr;\n        });\n      };\n      /**\n       * Get series list before filtered by index.\n       */\n\n\n      GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) {\n        return this._componentsMap.get('series')[seriesIndex];\n      };\n      /**\n       * Get series list before filtered by type.\n       * FIXME: rename to getRawSeriesByType?\n       */\n\n\n      GlobalModel.prototype.getSeriesByType = function (subType) {\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries && oneSeries.subType === subType;\n        });\n      };\n      /**\n       * Get all series before filtered.\n       */\n\n\n      GlobalModel.prototype.getSeries = function () {\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries;\n        });\n      };\n      /**\n       * Count series before filtered.\n       */\n\n\n      GlobalModel.prototype.getSeriesCount = function () {\n        return this._componentsCount.get('series');\n      };\n      /**\n       * After filtering, series may be different\n       * from raw series.\n       */\n\n\n      GlobalModel.prototype.eachSeries = function (cb, context) {\n        assertSeriesInitialized(this);\n        each(this._seriesIndices, function (rawSeriesIndex) {\n          var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n          cb.call(context, series, rawSeriesIndex);\n        }, this);\n      };\n      /**\n       * Iterate raw series before filtered.\n       *\n       * @param {Function} cb\n       * @param {*} context\n       */\n\n\n      GlobalModel.prototype.eachRawSeries = function (cb, context) {\n        each(this._componentsMap.get('series'), function (series) {\n          series && cb.call(context, series, series.componentIndex);\n        });\n      };\n      /**\n       * After filtering, series may be different.\n       * from raw series.\n       */\n\n\n      GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) {\n        assertSeriesInitialized(this);\n        each(this._seriesIndices, function (rawSeriesIndex) {\n          var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n          if (series.subType === subType) {\n            cb.call(context, series, rawSeriesIndex);\n          }\n        }, this);\n      };\n      /**\n       * Iterate raw series before filtered of given type.\n       */\n\n\n      GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) {\n        return each(this.getSeriesByType(subType), cb, context);\n      };\n\n      GlobalModel.prototype.isSeriesFiltered = function (seriesModel) {\n        assertSeriesInitialized(this);\n        return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;\n      };\n\n      GlobalModel.prototype.getCurrentSeriesIndices = function () {\n        return (this._seriesIndices || []).slice();\n      };\n\n      GlobalModel.prototype.filterSeries = function (cb, context) {\n        assertSeriesInitialized(this);\n        var newSeriesIndices = [];\n        each(this._seriesIndices, function (seriesRawIdx) {\n          var series = this._componentsMap.get('series')[seriesRawIdx];\n\n          cb.call(context, series, seriesRawIdx) && newSeriesIndices.push(seriesRawIdx);\n        }, this);\n        this._seriesIndices = newSeriesIndices;\n        this._seriesIndicesMap = createHashMap(newSeriesIndices);\n      };\n\n      GlobalModel.prototype.restoreData = function (payload) {\n        reCreateSeriesIndices(this);\n        var componentsMap = this._componentsMap;\n        var componentTypes = [];\n        componentsMap.each(function (components, componentType) {\n          if (ComponentModel.hasClass(componentType)) {\n            componentTypes.push(componentType);\n          }\n        });\n        ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType) {\n          each(componentsMap.get(componentType), function (component) {\n            if (component && (componentType !== 'series' || !isNotTargetSeries(component, payload))) {\n              component.restoreData();\n            }\n          });\n        });\n      };\n\n      GlobalModel.internalField = function () {\n        reCreateSeriesIndices = function (ecModel) {\n          var seriesIndices = ecModel._seriesIndices = [];\n          each(ecModel._componentsMap.get('series'), function (series) {\n            // series may have been removed by `replaceMerge`.\n            series && seriesIndices.push(series.componentIndex);\n          });\n          ecModel._seriesIndicesMap = createHashMap(seriesIndices);\n        };\n\n        assertSeriesInitialized = function (ecModel) {\n          // Components that use _seriesIndices should depends on series component,\n          // which make sure that their initialization is after series.\n          if (\"development\" !== 'production') {\n            if (!ecModel._seriesIndices) {\n              throw new Error('Option should contains series.');\n            }\n          }\n        };\n\n        initBase = function (ecModel, baseOption) {\n          // Using OPTION_INNER_KEY to mark that this option cannot be used outside,\n          // i.e. `chart.setOption(chart.getModel().option);` is forbidden.\n          ecModel.option = {};\n          ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; // Init with series: [], in case of calling findSeries method\n          // before series initialized.\n\n          ecModel._componentsMap = createHashMap({\n            series: []\n          });\n          ecModel._componentsCount = createHashMap(); // If user spefied `option.aria`, aria will be enable. This detection should be\n          // performed before theme and globalDefault merge.\n\n          var airaOption = baseOption.aria;\n\n          if (isObject(airaOption) && airaOption.enabled == null) {\n            airaOption.enabled = true;\n          }\n\n          mergeTheme(baseOption, ecModel._theme.option); // TODO Needs clone when merging to the unexisted property\n\n          merge(baseOption, globalDefault, false);\n\n          ecModel._mergeOption(baseOption, null);\n        };\n      }();\n\n      return GlobalModel;\n    }(Model);\n\n    function isNotTargetSeries(seriesModel, payload) {\n      if (payload) {\n        var index = payload.seriesIndex;\n        var id = payload.seriesId;\n        var name_1 = payload.seriesName;\n        return index != null && seriesModel.componentIndex !== index || id != null && seriesModel.id !== id || name_1 != null && seriesModel.name !== name_1;\n      }\n    }\n\n    function mergeTheme(option, theme) {\n      // PENDING\n      // NOT use `colorLayer` in theme if option has `color`\n      var notMergeColorLayer = option.color && !option.colorLayer;\n      each(theme, function (themeItem, name) {\n        if (name === 'colorLayer' && notMergeColorLayer) {\n          return;\n        } // If it is component model mainType, the model handles that merge later.\n        // otherwise, merge them here.\n\n\n        if (!ComponentModel.hasClass(name)) {\n          if (typeof themeItem === 'object') {\n            option[name] = !option[name] ? clone(themeItem) : merge(option[name], themeItem, false);\n          } else {\n            if (option[name] == null) {\n              option[name] = themeItem;\n            }\n          }\n        }\n      });\n    }\n\n    function queryByIdOrName(attr, idOrName, cmpts) {\n      // Here is a break from echarts4: string and number are\n      // treated as equal.\n      if (isArray(idOrName)) {\n        var keyMap_1 = createHashMap();\n        each(idOrName, function (idOrNameItem) {\n          if (idOrNameItem != null) {\n            var idName = convertOptionIdName(idOrNameItem, null);\n            idName != null && keyMap_1.set(idOrNameItem, true);\n          }\n        });\n        return filter(cmpts, function (cmpt) {\n          return cmpt && keyMap_1.get(cmpt[attr]);\n        });\n      } else {\n        var idName_1 = convertOptionIdName(idOrName, null);\n        return filter(cmpts, function (cmpt) {\n          return cmpt && idName_1 != null && cmpt[attr] === idName_1;\n        });\n      }\n    }\n\n    function filterBySubType(components, condition) {\n      // Using hasOwnProperty for restrict. Consider\n      // subType is undefined in user payload.\n      return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) {\n        return cmpt && cmpt.subType === condition.subType;\n      }) : components;\n    }\n\n    function normalizeSetOptionInput(opts) {\n      var replaceMergeMainTypeMap = createHashMap();\n      opts && each(normalizeToArray(opts.replaceMerge), function (mainType) {\n        if (\"development\" !== 'production') {\n          assert(ComponentModel.hasClass(mainType), '\"' + mainType + '\" is not valid component main type in \"replaceMerge\"');\n        }\n\n        replaceMergeMainTypeMap.set(mainType, true);\n      });\n      return {\n        replaceMergeMainTypeMap: replaceMergeMainTypeMap\n      };\n    }\n\n    mixin(GlobalModel, PaletteMixin);\n\n    var availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isSSR', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', // 'getModel',\n    'getOption', // 'getViewOfComponentModel',\n    // 'getViewOfSeriesModel',\n    'getId', 'updateLabelLayout'];\n\n    var ExtensionAPI =\n    /** @class */\n    function () {\n      function ExtensionAPI(ecInstance) {\n        each(availableMethods, function (methodName) {\n          this[methodName] = bind(ecInstance[methodName], ecInstance);\n        }, this);\n      }\n\n      return ExtensionAPI;\n    }();\n\n    var coordinateSystemCreators = {};\n\n    var CoordinateSystemManager =\n    /** @class */\n    function () {\n      function CoordinateSystemManager() {\n        this._coordinateSystems = [];\n      }\n\n      CoordinateSystemManager.prototype.create = function (ecModel, api) {\n        var coordinateSystems = [];\n        each(coordinateSystemCreators, function (creator, type) {\n          var list = creator.create(ecModel, api);\n          coordinateSystems = coordinateSystems.concat(list || []);\n        });\n        this._coordinateSystems = coordinateSystems;\n      };\n\n      CoordinateSystemManager.prototype.update = function (ecModel, api) {\n        each(this._coordinateSystems, function (coordSys) {\n          coordSys.update && coordSys.update(ecModel, api);\n        });\n      };\n\n      CoordinateSystemManager.prototype.getCoordinateSystems = function () {\n        return this._coordinateSystems.slice();\n      };\n\n      CoordinateSystemManager.register = function (type, creator) {\n        coordinateSystemCreators[type] = creator;\n      };\n\n      CoordinateSystemManager.get = function (type) {\n        return coordinateSystemCreators[type];\n      };\n\n      return CoordinateSystemManager;\n    }();\n\n    var QUERY_REG = /^(min|max)?(.+)$/; // Key: mainType\n    // type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>;\n\n    /**\n     * TERM EXPLANATIONS:\n     * See `ECOption` and `ECUnitOption` in `src/util/types.ts`.\n     */\n\n    var OptionManager =\n    /** @class */\n    function () {\n      // timeline.notMerge is not supported in ec3. Firstly there is rearly\n      // case that notMerge is needed. Secondly supporting 'notMerge' requires\n      // rawOption cloned and backuped when timeline changed, which does no\n      // good to performance. What's more, that both timeline and setOption\n      // method supply 'notMerge' brings complex and some problems.\n      // Consider this case:\n      // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);\n      // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);\n      function OptionManager(api) {\n        this._timelineOptions = [];\n        this._mediaList = [];\n        /**\n         * -1, means default.\n         * empty means no media.\n         */\n\n        this._currentMediaIndices = [];\n        this._api = api;\n      }\n\n      OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) {\n        if (rawOption) {\n          // That set dat primitive is dangerous if user reuse the data when setOption again.\n          each(normalizeToArray(rawOption.series), function (series) {\n            series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);\n          });\n          each(normalizeToArray(rawOption.dataset), function (dataset) {\n            dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source);\n          });\n        } // Caution: some series modify option data, if do not clone,\n        // it should ensure that the repeat modify correctly\n        // (create a new object when modify itself).\n\n\n        rawOption = clone(rawOption); // FIXME\n        // If some property is set in timeline options or media option but\n        // not set in baseOption, a warning should be given.\n\n        var optionBackup = this._optionBackup;\n        var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup);\n        this._newBaseOption = newParsedOption.baseOption; // For setOption at second time (using merge mode);\n\n        if (optionBackup) {\n          // FIXME\n          // the restore merge solution is essentially incorrect.\n          // the mapping can not be 100% consistent with ecModel, which probably brings\n          // potential bug!\n          // The first merge is delayed, because in most cases, users do not call `setOption` twice.\n          // let fakeCmptsMap = this._fakeCmptsMap;\n          // if (!fakeCmptsMap) {\n          //     fakeCmptsMap = this._fakeCmptsMap = createHashMap();\n          //     mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null);\n          // }\n          // mergeToBackupOption(\n          //     fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt\n          // );\n          // For simplicity, timeline options and media options do not support merge,\n          // that is, if you `setOption` twice and both has timeline options, the latter\n          // timeline options will not be merged to the former, but just substitute them.\n          if (newParsedOption.timelineOptions.length) {\n            optionBackup.timelineOptions = newParsedOption.timelineOptions;\n          }\n\n          if (newParsedOption.mediaList.length) {\n            optionBackup.mediaList = newParsedOption.mediaList;\n          }\n\n          if (newParsedOption.mediaDefault) {\n            optionBackup.mediaDefault = newParsedOption.mediaDefault;\n          }\n        } else {\n          this._optionBackup = newParsedOption;\n        }\n      };\n\n      OptionManager.prototype.mountOption = function (isRecreate) {\n        var optionBackup = this._optionBackup;\n        this._timelineOptions = optionBackup.timelineOptions;\n        this._mediaList = optionBackup.mediaList;\n        this._mediaDefault = optionBackup.mediaDefault;\n        this._currentMediaIndices = [];\n        return clone(isRecreate // this._optionBackup.baseOption, which is created at the first `setOption`\n        // called, and is merged into every new option by inner method `mergeToBackupOption`\n        // each time `setOption` called, can be only used in `isRecreate`, because\n        // its reliability is under suspicion. In other cases option merge is\n        // performed by `model.mergeOption`.\n        ? optionBackup.baseOption : this._newBaseOption);\n      };\n\n      OptionManager.prototype.getTimelineOption = function (ecModel) {\n        var option;\n        var timelineOptions = this._timelineOptions;\n\n        if (timelineOptions.length) {\n          // getTimelineOption can only be called after ecModel inited,\n          // so we can get currentIndex from timelineModel.\n          var timelineModel = ecModel.getComponent('timeline');\n\n          if (timelineModel) {\n            option = clone( // FIXME:TS as TimelineModel or quivlant interface\n            timelineOptions[timelineModel.getCurrentIndex()]);\n          }\n        }\n\n        return option;\n      };\n\n      OptionManager.prototype.getMediaOption = function (ecModel) {\n        var ecWidth = this._api.getWidth();\n\n        var ecHeight = this._api.getHeight();\n\n        var mediaList = this._mediaList;\n        var mediaDefault = this._mediaDefault;\n        var indices = [];\n        var result = []; // No media defined.\n\n        if (!mediaList.length && !mediaDefault) {\n          return result;\n        } // Multi media may be applied, the latter defined media has higher priority.\n\n\n        for (var i = 0, len = mediaList.length; i < len; i++) {\n          if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {\n            indices.push(i);\n          }\n        } // FIXME\n        // Whether mediaDefault should force users to provide? Otherwise\n        // the change by media query can not be recorvered.\n\n\n        if (!indices.length && mediaDefault) {\n          indices = [-1];\n        }\n\n        if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {\n          result = map(indices, function (index) {\n            return clone(index === -1 ? mediaDefault.option : mediaList[index].option);\n          });\n        } // Otherwise return nothing.\n\n\n        this._currentMediaIndices = indices;\n        return result;\n      };\n\n      return OptionManager;\n    }();\n    /**\n     * [RAW_OPTION_PATTERNS]\n     * (Note: \"series: []\" represents all other props in `ECUnitOption`)\n     *\n     * (1) No prop \"baseOption\" declared:\n     * Root option is used as \"baseOption\" (except prop \"options\" and \"media\").\n     * ```js\n     * option = {\n     *     series: [],\n     *     timeline: {},\n     *     options: [],\n     * };\n     * option = {\n     *     series: [],\n     *     media: {},\n     * };\n     * option = {\n     *     series: [],\n     *     timeline: {},\n     *     options: [],\n     *     media: {},\n     * }\n     * ```\n     *\n     * (2) Prop \"baseOption\" declared:\n     * If \"baseOption\" declared, `ECUnitOption` props can only be declared\n     * inside \"baseOption\" except prop \"timeline\" (compat ec2).\n     * ```js\n     * option = {\n     *     baseOption: {\n     *         timeline: {},\n     *         series: [],\n     *     },\n     *     options: []\n     * };\n     * option = {\n     *     baseOption: {\n     *         series: [],\n     *     },\n     *     media: []\n     * };\n     * option = {\n     *     baseOption: {\n     *         timeline: {},\n     *         series: [],\n     *     },\n     *     options: []\n     *     media: []\n     * };\n     * option = {\n     *     // ec3 compat ec2: allow (only) `timeline` declared\n     *     // outside baseOption. Keep this setting for compat.\n     *     timeline: {},\n     *     baseOption: {\n     *         series: [],\n     *     },\n     *     options: [],\n     *     media: []\n     * };\n     * ```\n     */\n\n\n    function parseRawOption( // `rawOption` May be modified\n    rawOption, optionPreprocessorFuncs, isNew) {\n      var mediaList = [];\n      var mediaDefault;\n      var baseOption;\n      var declaredBaseOption = rawOption.baseOption; // Compatible with ec2, [RAW_OPTION_PATTERNS] above.\n\n      var timelineOnRoot = rawOption.timeline;\n      var timelineOptionsOnRoot = rawOption.options;\n      var mediaOnRoot = rawOption.media;\n      var hasMedia = !!rawOption.media;\n      var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline);\n\n      if (declaredBaseOption) {\n        baseOption = declaredBaseOption; // For merge option.\n\n        if (!baseOption.timeline) {\n          baseOption.timeline = timelineOnRoot;\n        }\n      } // For convenience, enable to use the root option as the `baseOption`:\n      // `{ ...normalOptionProps, media: [{ ... }, { ... }] }`\n      else {\n          if (hasTimeline || hasMedia) {\n            rawOption.options = rawOption.media = null;\n          }\n\n          baseOption = rawOption;\n        }\n\n      if (hasMedia) {\n        if (isArray(mediaOnRoot)) {\n          each(mediaOnRoot, function (singleMedia) {\n            if (\"development\" !== 'production') {\n              // Real case of wrong config.\n              if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) {\n                error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }');\n              }\n            }\n\n            if (singleMedia && singleMedia.option) {\n              if (singleMedia.query) {\n                mediaList.push(singleMedia);\n              } else if (!mediaDefault) {\n                // Use the first media default.\n                mediaDefault = singleMedia;\n              }\n            }\n          });\n        } else {\n          if (\"development\" !== 'production') {\n            // Real case of wrong config.\n            error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }');\n          }\n        }\n      }\n\n      doPreprocess(baseOption);\n      each(timelineOptionsOnRoot, function (option) {\n        return doPreprocess(option);\n      });\n      each(mediaList, function (media) {\n        return doPreprocess(media.option);\n      });\n\n      function doPreprocess(option) {\n        each(optionPreprocessorFuncs, function (preProcess) {\n          preProcess(option, isNew);\n        });\n      }\n\n      return {\n        baseOption: baseOption,\n        timelineOptions: timelineOptionsOnRoot || [],\n        mediaDefault: mediaDefault,\n        mediaList: mediaList\n      };\n    }\n    /**\n     * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>\n     * Support: width, height, aspectRatio\n     * Can use max or min as prefix.\n     */\n\n\n    function applyMediaQuery(query, ecWidth, ecHeight) {\n      var realMap = {\n        width: ecWidth,\n        height: ecHeight,\n        aspectratio: ecWidth / ecHeight // lower case for convenience.\n\n      };\n      var applicable = true;\n      each(query, function (value, attr) {\n        var matched = attr.match(QUERY_REG);\n\n        if (!matched || !matched[1] || !matched[2]) {\n          return;\n        }\n\n        var operator = matched[1];\n        var realAttr = matched[2].toLowerCase();\n\n        if (!compare(realMap[realAttr], value, operator)) {\n          applicable = false;\n        }\n      });\n      return applicable;\n    }\n\n    function compare(real, expect, operator) {\n      if (operator === 'min') {\n        return real >= expect;\n      } else if (operator === 'max') {\n        return real <= expect;\n      } else {\n        // Equals\n        return real === expect;\n      }\n    }\n\n    function indicesEquals(indices1, indices2) {\n      // indices is always order by asc and has only finite number.\n      return indices1.join(',') === indices2.join(',');\n    }\n\n    var each$2 = each;\n    var isObject$1 = isObject;\n    var POSSIBLE_STYLES = ['areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', 'chordStyle', 'label', 'labelLine'];\n\n    function compatEC2ItemStyle(opt) {\n      var itemStyleOpt = opt && opt.itemStyle;\n\n      if (!itemStyleOpt) {\n        return;\n      }\n\n      for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {\n        var styleName = POSSIBLE_STYLES[i];\n        var normalItemStyleOpt = itemStyleOpt.normal;\n        var emphasisItemStyleOpt = itemStyleOpt.emphasis;\n\n        if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(\"itemStyle.normal.\" + styleName, styleName);\n          }\n\n          opt[styleName] = opt[styleName] || {};\n\n          if (!opt[styleName].normal) {\n            opt[styleName].normal = normalItemStyleOpt[styleName];\n          } else {\n            merge(opt[styleName].normal, normalItemStyleOpt[styleName]);\n          }\n\n          normalItemStyleOpt[styleName] = null;\n        }\n\n        if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(\"itemStyle.emphasis.\" + styleName, \"emphasis.\" + styleName);\n          }\n\n          opt[styleName] = opt[styleName] || {};\n\n          if (!opt[styleName].emphasis) {\n            opt[styleName].emphasis = emphasisItemStyleOpt[styleName];\n          } else {\n            merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);\n          }\n\n          emphasisItemStyleOpt[styleName] = null;\n        }\n      }\n    }\n\n    function convertNormalEmphasis(opt, optType, useExtend) {\n      if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) {\n        var normalOpt = opt[optType].normal;\n        var emphasisOpt = opt[optType].emphasis;\n\n        if (normalOpt) {\n          if (\"development\" !== 'production') {\n            // eslint-disable-next-line max-len\n            deprecateLog(\"'normal' hierarchy in \" + optType + \" has been removed since 4.0. All style properties are configured in \" + optType + \" directly now.\");\n          } // Timeline controlStyle has other properties besides normal and emphasis\n\n\n          if (useExtend) {\n            opt[optType].normal = opt[optType].emphasis = null;\n            defaults(opt[optType], normalOpt);\n          } else {\n            opt[optType] = normalOpt;\n          }\n        }\n\n        if (emphasisOpt) {\n          if (\"development\" !== 'production') {\n            deprecateLog(optType + \".emphasis has been changed to emphasis.\" + optType + \" since 4.0\");\n          }\n\n          opt.emphasis = opt.emphasis || {};\n          opt.emphasis[optType] = emphasisOpt; // Also compat the case user mix the style and focus together in ec3 style\n          // for example: { itemStyle: { normal: {}, emphasis: {focus, shadowBlur} } }\n\n          if (emphasisOpt.focus) {\n            opt.emphasis.focus = emphasisOpt.focus;\n          }\n\n          if (emphasisOpt.blurScope) {\n            opt.emphasis.blurScope = emphasisOpt.blurScope;\n          }\n        }\n      }\n    }\n\n    function removeEC3NormalStatus(opt) {\n      convertNormalEmphasis(opt, 'itemStyle');\n      convertNormalEmphasis(opt, 'lineStyle');\n      convertNormalEmphasis(opt, 'areaStyle');\n      convertNormalEmphasis(opt, 'label');\n      convertNormalEmphasis(opt, 'labelLine'); // treemap\n\n      convertNormalEmphasis(opt, 'upperLabel'); // graph\n\n      convertNormalEmphasis(opt, 'edgeLabel');\n    }\n\n    function compatTextStyle(opt, propName) {\n      // Check whether is not object (string\\null\\undefined ...)\n      var labelOptSingle = isObject$1(opt) && opt[propName];\n      var textStyle = isObject$1(labelOptSingle) && labelOptSingle.textStyle;\n\n      if (textStyle) {\n        if (\"development\" !== 'production') {\n          // eslint-disable-next-line max-len\n          deprecateLog(\"textStyle hierarchy in \" + propName + \" has been removed since 4.0. All textStyle properties are configured in \" + propName + \" directly now.\");\n        }\n\n        for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {\n          var textPropName = TEXT_STYLE_OPTIONS[i];\n\n          if (textStyle.hasOwnProperty(textPropName)) {\n            labelOptSingle[textPropName] = textStyle[textPropName];\n          }\n        }\n      }\n    }\n\n    function compatEC3CommonStyles(opt) {\n      if (opt) {\n        removeEC3NormalStatus(opt);\n        compatTextStyle(opt, 'label');\n        opt.emphasis && compatTextStyle(opt.emphasis, 'label');\n      }\n    }\n\n    function processSeries(seriesOpt) {\n      if (!isObject$1(seriesOpt)) {\n        return;\n      }\n\n      compatEC2ItemStyle(seriesOpt);\n      removeEC3NormalStatus(seriesOpt);\n      compatTextStyle(seriesOpt, 'label'); // treemap\n\n      compatTextStyle(seriesOpt, 'upperLabel'); // graph\n\n      compatTextStyle(seriesOpt, 'edgeLabel');\n\n      if (seriesOpt.emphasis) {\n        compatTextStyle(seriesOpt.emphasis, 'label'); // treemap\n\n        compatTextStyle(seriesOpt.emphasis, 'upperLabel'); // graph\n\n        compatTextStyle(seriesOpt.emphasis, 'edgeLabel');\n      }\n\n      var markPoint = seriesOpt.markPoint;\n\n      if (markPoint) {\n        compatEC2ItemStyle(markPoint);\n        compatEC3CommonStyles(markPoint);\n      }\n\n      var markLine = seriesOpt.markLine;\n\n      if (markLine) {\n        compatEC2ItemStyle(markLine);\n        compatEC3CommonStyles(markLine);\n      }\n\n      var markArea = seriesOpt.markArea;\n\n      if (markArea) {\n        compatEC3CommonStyles(markArea);\n      }\n\n      var data = seriesOpt.data; // Break with ec3: if `setOption` again, there may be no `type` in option,\n      // then the backward compat based on option type will not be performed.\n\n      if (seriesOpt.type === 'graph') {\n        data = data || seriesOpt.nodes;\n        var edgeData = seriesOpt.links || seriesOpt.edges;\n\n        if (edgeData && !isTypedArray(edgeData)) {\n          for (var i = 0; i < edgeData.length; i++) {\n            compatEC3CommonStyles(edgeData[i]);\n          }\n        }\n\n        each(seriesOpt.categories, function (opt) {\n          removeEC3NormalStatus(opt);\n        });\n      }\n\n      if (data && !isTypedArray(data)) {\n        for (var i = 0; i < data.length; i++) {\n          compatEC3CommonStyles(data[i]);\n        }\n      } // mark point data\n\n\n      markPoint = seriesOpt.markPoint;\n\n      if (markPoint && markPoint.data) {\n        var mpData = markPoint.data;\n\n        for (var i = 0; i < mpData.length; i++) {\n          compatEC3CommonStyles(mpData[i]);\n        }\n      } // mark line data\n\n\n      markLine = seriesOpt.markLine;\n\n      if (markLine && markLine.data) {\n        var mlData = markLine.data;\n\n        for (var i = 0; i < mlData.length; i++) {\n          if (isArray(mlData[i])) {\n            compatEC3CommonStyles(mlData[i][0]);\n            compatEC3CommonStyles(mlData[i][1]);\n          } else {\n            compatEC3CommonStyles(mlData[i]);\n          }\n        }\n      } // Series\n\n\n      if (seriesOpt.type === 'gauge') {\n        compatTextStyle(seriesOpt, 'axisLabel');\n        compatTextStyle(seriesOpt, 'title');\n        compatTextStyle(seriesOpt, 'detail');\n      } else if (seriesOpt.type === 'treemap') {\n        convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle');\n        each(seriesOpt.levels, function (opt) {\n          removeEC3NormalStatus(opt);\n        });\n      } else if (seriesOpt.type === 'tree') {\n        removeEC3NormalStatus(seriesOpt.leaves);\n      } // sunburst starts from ec4, so it does not need to compat levels.\n\n    }\n\n    function toArr(o) {\n      return isArray(o) ? o : o ? [o] : [];\n    }\n\n    function toObj(o) {\n      return (isArray(o) ? o[0] : o) || {};\n    }\n\n    function globalCompatStyle(option, isTheme) {\n      each$2(toArr(option.series), function (seriesOpt) {\n        isObject$1(seriesOpt) && processSeries(seriesOpt);\n      });\n      var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];\n      isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');\n      each$2(axes, function (axisName) {\n        each$2(toArr(option[axisName]), function (axisOpt) {\n          if (axisOpt) {\n            compatTextStyle(axisOpt, 'axisLabel');\n            compatTextStyle(axisOpt.axisPointer, 'label');\n          }\n        });\n      });\n      each$2(toArr(option.parallel), function (parallelOpt) {\n        var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;\n        compatTextStyle(parallelAxisDefault, 'axisLabel');\n        compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');\n      });\n      each$2(toArr(option.calendar), function (calendarOpt) {\n        convertNormalEmphasis(calendarOpt, 'itemStyle');\n        compatTextStyle(calendarOpt, 'dayLabel');\n        compatTextStyle(calendarOpt, 'monthLabel');\n        compatTextStyle(calendarOpt, 'yearLabel');\n      }); // radar.name.textStyle\n\n      each$2(toArr(option.radar), function (radarOpt) {\n        compatTextStyle(radarOpt, 'name'); // Use axisName instead of name because component has name property\n\n        if (radarOpt.name && radarOpt.axisName == null) {\n          radarOpt.axisName = radarOpt.name;\n          delete radarOpt.name;\n\n          if (\"development\" !== 'production') {\n            deprecateLog('name property in radar component has been changed to axisName');\n          }\n        }\n\n        if (radarOpt.nameGap != null && radarOpt.axisNameGap == null) {\n          radarOpt.axisNameGap = radarOpt.nameGap;\n          delete radarOpt.nameGap;\n\n          if (\"development\" !== 'production') {\n            deprecateLog('nameGap property in radar component has been changed to axisNameGap');\n          }\n        }\n\n        if (\"development\" !== 'production') {\n          each$2(radarOpt.indicator, function (indicatorOpt) {\n            if (indicatorOpt.text) {\n              deprecateReplaceLog('text', 'name', 'radar.indicator');\n            }\n          });\n        }\n      });\n      each$2(toArr(option.geo), function (geoOpt) {\n        if (isObject$1(geoOpt)) {\n          compatEC3CommonStyles(geoOpt);\n          each$2(toArr(geoOpt.regions), function (regionObj) {\n            compatEC3CommonStyles(regionObj);\n          });\n        }\n      });\n      each$2(toArr(option.timeline), function (timelineOpt) {\n        compatEC3CommonStyles(timelineOpt);\n        convertNormalEmphasis(timelineOpt, 'label');\n        convertNormalEmphasis(timelineOpt, 'itemStyle');\n        convertNormalEmphasis(timelineOpt, 'controlStyle', true);\n        var data = timelineOpt.data;\n        isArray(data) && each(data, function (item) {\n          if (isObject(item)) {\n            convertNormalEmphasis(item, 'label');\n            convertNormalEmphasis(item, 'itemStyle');\n          }\n        });\n      });\n      each$2(toArr(option.toolbox), function (toolboxOpt) {\n        convertNormalEmphasis(toolboxOpt, 'iconStyle');\n        each$2(toolboxOpt.feature, function (featureOpt) {\n          convertNormalEmphasis(featureOpt, 'iconStyle');\n        });\n      });\n      compatTextStyle(toObj(option.axisPointer), 'label');\n      compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); // Clean logs\n      // storedLogs = {};\n    }\n\n    function get(opt, path) {\n      var pathArr = path.split(',');\n      var obj = opt;\n\n      for (var i = 0; i < pathArr.length; i++) {\n        obj = obj && obj[pathArr[i]];\n\n        if (obj == null) {\n          break;\n        }\n      }\n\n      return obj;\n    }\n\n    function set$1(opt, path, val, overwrite) {\n      var pathArr = path.split(',');\n      var obj = opt;\n      var key;\n      var i = 0;\n\n      for (; i < pathArr.length - 1; i++) {\n        key = pathArr[i];\n\n        if (obj[key] == null) {\n          obj[key] = {};\n        }\n\n        obj = obj[key];\n      }\n\n      if (overwrite || obj[pathArr[i]] == null) {\n        obj[pathArr[i]] = val;\n      }\n    }\n\n    function compatLayoutProperties(option) {\n      option && each(LAYOUT_PROPERTIES, function (prop) {\n        if (prop[0] in option && !(prop[1] in option)) {\n          option[prop[1]] = option[prop[0]];\n        }\n      });\n    }\n\n    var LAYOUT_PROPERTIES = [['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']];\n    var COMPATITABLE_COMPONENTS = ['grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'];\n    var BAR_ITEM_STYLE_MAP = [['borderRadius', 'barBorderRadius'], ['borderColor', 'barBorderColor'], ['borderWidth', 'barBorderWidth']];\n\n    function compatBarItemStyle(option) {\n      var itemStyle = option && option.itemStyle;\n\n      if (itemStyle) {\n        for (var i = 0; i < BAR_ITEM_STYLE_MAP.length; i++) {\n          var oldName = BAR_ITEM_STYLE_MAP[i][1];\n          var newName = BAR_ITEM_STYLE_MAP[i][0];\n\n          if (itemStyle[oldName] != null) {\n            itemStyle[newName] = itemStyle[oldName];\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog(oldName, newName);\n            }\n          }\n        }\n      }\n    }\n\n    function compatPieLabel(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.alignTo === 'edge' && option.margin != null && option.edgeDistance == null) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('label.margin', 'label.edgeDistance', 'pie');\n        }\n\n        option.edgeDistance = option.margin;\n      }\n    }\n\n    function compatSunburstState(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.downplay && !option.blur) {\n        option.blur = option.downplay;\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('downplay', 'blur', 'sunburst');\n        }\n      }\n    }\n\n    function compatGraphFocus(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.focusNodeAdjacency != null) {\n        option.emphasis = option.emphasis || {};\n\n        if (option.emphasis.focus == null) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog('focusNodeAdjacency', 'emphasis: { focus: \\'adjacency\\'}', 'graph/sankey');\n          }\n\n          option.emphasis.focus = 'adjacency';\n        }\n      }\n    }\n\n    function traverseTree(data, cb) {\n      if (data) {\n        for (var i = 0; i < data.length; i++) {\n          cb(data[i]);\n          data[i] && traverseTree(data[i].children, cb);\n        }\n      }\n    }\n\n    function globalBackwardCompat(option, isTheme) {\n      globalCompatStyle(option, isTheme); // Make sure series array for model initialization.\n\n      option.series = normalizeToArray(option.series);\n      each(option.series, function (seriesOpt) {\n        if (!isObject(seriesOpt)) {\n          return;\n        }\n\n        var seriesType = seriesOpt.type;\n\n        if (seriesType === 'line') {\n          if (seriesOpt.clipOverflow != null) {\n            seriesOpt.clip = seriesOpt.clipOverflow;\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('clipOverflow', 'clip', 'line');\n            }\n          }\n        } else if (seriesType === 'pie' || seriesType === 'gauge') {\n          if (seriesOpt.clockWise != null) {\n            seriesOpt.clockwise = seriesOpt.clockWise;\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('clockWise', 'clockwise');\n            }\n          }\n\n          compatPieLabel(seriesOpt.label);\n          var data = seriesOpt.data;\n\n          if (data && !isTypedArray(data)) {\n            for (var i = 0; i < data.length; i++) {\n              compatPieLabel(data[i]);\n            }\n          }\n\n          if (seriesOpt.hoverOffset != null) {\n            seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n            if (seriesOpt.emphasis.scaleSize = null) {\n              if (\"development\" !== 'production') {\n                deprecateReplaceLog('hoverOffset', 'emphasis.scaleSize');\n              }\n\n              seriesOpt.emphasis.scaleSize = seriesOpt.hoverOffset;\n            }\n          }\n        } else if (seriesType === 'gauge') {\n          var pointerColor = get(seriesOpt, 'pointer.color');\n          pointerColor != null && set$1(seriesOpt, 'itemStyle.color', pointerColor);\n        } else if (seriesType === 'bar') {\n          compatBarItemStyle(seriesOpt);\n          compatBarItemStyle(seriesOpt.backgroundStyle);\n          compatBarItemStyle(seriesOpt.emphasis);\n          var data = seriesOpt.data;\n\n          if (data && !isTypedArray(data)) {\n            for (var i = 0; i < data.length; i++) {\n              if (typeof data[i] === 'object') {\n                compatBarItemStyle(data[i]);\n                compatBarItemStyle(data[i] && data[i].emphasis);\n              }\n            }\n          }\n        } else if (seriesType === 'sunburst') {\n          var highlightPolicy = seriesOpt.highlightPolicy;\n\n          if (highlightPolicy) {\n            seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n            if (!seriesOpt.emphasis.focus) {\n              seriesOpt.emphasis.focus = highlightPolicy;\n\n              if (\"development\" !== 'production') {\n                deprecateReplaceLog('highlightPolicy', 'emphasis.focus', 'sunburst');\n              }\n            }\n          }\n\n          compatSunburstState(seriesOpt);\n          traverseTree(seriesOpt.data, compatSunburstState);\n        } else if (seriesType === 'graph' || seriesType === 'sankey') {\n          compatGraphFocus(seriesOpt); // TODO nodes, edges?\n        } else if (seriesType === 'map') {\n          if (seriesOpt.mapType && !seriesOpt.map) {\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('mapType', 'map', 'map');\n            }\n\n            seriesOpt.map = seriesOpt.mapType;\n          }\n\n          if (seriesOpt.mapLocation) {\n            if (\"development\" !== 'production') {\n              deprecateLog('`mapLocation` is not used anymore.');\n            }\n\n            defaults(seriesOpt, seriesOpt.mapLocation);\n          }\n        }\n\n        if (seriesOpt.hoverAnimation != null) {\n          seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n          if (seriesOpt.emphasis && seriesOpt.emphasis.scale == null) {\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('hoverAnimation', 'emphasis.scale');\n            }\n\n            seriesOpt.emphasis.scale = seriesOpt.hoverAnimation;\n          }\n        }\n\n        compatLayoutProperties(seriesOpt);\n      }); // dataRange has changed to visualMap\n\n      if (option.dataRange) {\n        option.visualMap = option.dataRange;\n      }\n\n      each(COMPATITABLE_COMPONENTS, function (componentName) {\n        var options = option[componentName];\n\n        if (options) {\n          if (!isArray(options)) {\n            options = [options];\n          }\n\n          each(options, function (option) {\n            compatLayoutProperties(option);\n          });\n        }\n      });\n    }\n\n    //     data processing stage is blocked in stream.\n    //     See <module:echarts/stream/Scheduler#performDataProcessorTasks>\n    // (2) Only register once when import repeatedly.\n    //     Should be executed after series is filtered and before stack calculation.\n\n    function dataStack(ecModel) {\n      var stackInfoMap = createHashMap();\n      ecModel.eachSeries(function (seriesModel) {\n        var stack = seriesModel.get('stack'); // Compatible: when `stack` is set as '', do not stack.\n\n        if (stack) {\n          var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []);\n          var data = seriesModel.getData();\n          var stackInfo = {\n            // Used for calculate axis extent automatically.\n            // TODO: Type getCalculationInfo return more specific type?\n            stackResultDimension: data.getCalculationInfo('stackResultDimension'),\n            stackedOverDimension: data.getCalculationInfo('stackedOverDimension'),\n            stackedDimension: data.getCalculationInfo('stackedDimension'),\n            stackedByDimension: data.getCalculationInfo('stackedByDimension'),\n            isStackedByIndex: data.getCalculationInfo('isStackedByIndex'),\n            data: data,\n            seriesModel: seriesModel\n          }; // If stacked on axis that do not support data stack.\n\n          if (!stackInfo.stackedDimension || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)) {\n            return;\n          }\n\n          stackInfoList.length && data.setCalculationInfo('stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel);\n          stackInfoList.push(stackInfo);\n        }\n      });\n      stackInfoMap.each(calculateStack);\n    }\n\n    function calculateStack(stackInfoList) {\n      each(stackInfoList, function (targetStackInfo, idxInStack) {\n        var resultVal = [];\n        var resultNaN = [NaN, NaN];\n        var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension];\n        var targetData = targetStackInfo.data;\n        var isStackedByIndex = targetStackInfo.isStackedByIndex;\n        var stackStrategy = targetStackInfo.seriesModel.get('stackStrategy') || 'samesign'; // Should not write on raw data, because stack series model list changes\n        // depending on legend selection.\n\n        targetData.modify(dims, function (v0, v1, dataIndex) {\n          var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); // Consider `connectNulls` of line area, if value is NaN, stackedOver\n          // should also be NaN, to draw a appropriate belt area.\n\n          if (isNaN(sum)) {\n            return resultNaN;\n          }\n\n          var byValue;\n          var stackedDataRawIndex;\n\n          if (isStackedByIndex) {\n            stackedDataRawIndex = targetData.getRawIndex(dataIndex);\n          } else {\n            byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex);\n          } // If stackOver is NaN, chart view will render point on value start.\n\n\n          var stackedOver = NaN;\n\n          for (var j = idxInStack - 1; j >= 0; j--) {\n            var stackInfo = stackInfoList[j]; // Has been optimized by inverted indices on `stackedByDimension`.\n\n            if (!isStackedByIndex) {\n              stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue);\n            }\n\n            if (stackedDataRawIndex >= 0) {\n              var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); // Considering positive stack, negative stack and empty data\n\n              if (stackStrategy === 'all' // single stack group\n              || stackStrategy === 'positive' && val > 0 || stackStrategy === 'negative' && val < 0 || stackStrategy === 'samesign' && sum >= 0 && val > 0 // All positive stack\n              || stackStrategy === 'samesign' && sum <= 0 && val < 0 // All negative stack\n              ) {\n                  // The sum has to be very small to be affected by the\n                  // floating arithmetic problem. An incorrect result will probably\n                  // cause axis min/max to be filtered incorrectly.\n                  sum = addSafe(sum, val);\n                  stackedOver = val;\n                  break;\n                }\n            }\n          }\n\n          resultVal[0] = sum;\n          resultVal[1] = stackedOver;\n          return resultVal;\n        });\n      });\n    }\n\n    var SourceImpl =\n    /** @class */\n    function () {\n      function SourceImpl(fields) {\n        this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []);\n        this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; // Visit config\n\n        this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;\n        this.startIndex = fields.startIndex || 0;\n        this.dimensionsDetectedCount = fields.dimensionsDetectedCount;\n        this.metaRawOption = fields.metaRawOption;\n        var dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine;\n\n        if (dimensionsDefine) {\n          for (var i = 0; i < dimensionsDefine.length; i++) {\n            var dim = dimensionsDefine[i];\n\n            if (dim.type == null) {\n              if (guessOrdinal(this, i) === BE_ORDINAL.Must) {\n                dim.type = 'ordinal';\n              }\n            }\n          }\n        }\n      }\n\n      return SourceImpl;\n    }();\n\n    function isSourceInstance(val) {\n      return val instanceof SourceImpl;\n    }\n    /**\n     * Create a source from option.\n     * NOTE: Created source is immutable. Don't change any properties in it.\n     */\n\n    function createSource(sourceData, thisMetaRawOption, // can be null. If not provided, auto detect it from `sourceData`.\n    sourceFormat) {\n      sourceFormat = sourceFormat || detectSourceFormat(sourceData);\n      var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy;\n      var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions);\n      var source = new SourceImpl({\n        data: sourceData,\n        sourceFormat: sourceFormat,\n        seriesLayoutBy: seriesLayoutBy,\n        dimensionsDefine: determined.dimensionsDefine,\n        startIndex: determined.startIndex,\n        dimensionsDetectedCount: determined.dimensionsDetectedCount,\n        metaRawOption: clone(thisMetaRawOption)\n      });\n      return source;\n    }\n    /**\n     * Wrap original series data for some compatibility cases.\n     */\n\n    function createSourceFromSeriesDataOption(data) {\n      return new SourceImpl({\n        data: data,\n        sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL\n      });\n    }\n    /**\n     * Clone source but excludes source data.\n     */\n\n    function cloneSourceShallow(source) {\n      return new SourceImpl({\n        data: source.data,\n        sourceFormat: source.sourceFormat,\n        seriesLayoutBy: source.seriesLayoutBy,\n        dimensionsDefine: clone(source.dimensionsDefine),\n        startIndex: source.startIndex,\n        dimensionsDetectedCount: source.dimensionsDetectedCount\n      });\n    }\n    /**\n     * Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`.\n     */\n\n    function detectSourceFormat(data) {\n      var sourceFormat = SOURCE_FORMAT_UNKNOWN;\n\n      if (isTypedArray(data)) {\n        sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;\n      } else if (isArray(data)) {\n        // FIXME Whether tolerate null in top level array?\n        if (data.length === 0) {\n          sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n        }\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          var item = data[i];\n\n          if (item == null) {\n            continue;\n          } else if (isArray(item)) {\n            sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n            break;\n          } else if (isObject(item)) {\n            sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;\n            break;\n          }\n        }\n      } else if (isObject(data)) {\n        for (var key in data) {\n          if (hasOwn(data, key) && isArrayLike(data[key])) {\n            sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;\n            break;\n          }\n        }\n      }\n\n      return sourceFormat;\n    }\n    /**\n     * Determine the source definitions from data standalone dimensions definitions\n     * are not specified.\n     */\n\n    function determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, // standalone raw dimensions definition, like:\n    // {\n    //     dimensions: ['aa', 'bb', { name: 'cc', type: 'time' }]\n    // }\n    // in `dataset` or `series`\n    dimensionsDefine) {\n      var dimensionsDetectedCount;\n      var startIndex; // PENDING: Could data be null/undefined here?\n      // currently, if `dataset.source` not specified, error thrown.\n      // if `series.data` not specified, nothing rendered without error thrown.\n      // Should test these cases.\n\n      if (!data) {\n        return {\n          dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n          startIndex: startIndex,\n          dimensionsDetectedCount: dimensionsDetectedCount\n        };\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var dataArrayRows = data; // Rule: Most of the first line are string: it is header.\n        // Caution: consider a line with 5 string and 1 number,\n        // it still can not be sure it is a head, because the\n        // 5 string may be 5 values of category columns.\n\n        if (sourceHeader === 'auto' || sourceHeader == null) {\n          arrayRowsTravelFirst(function (val) {\n            // '-' is regarded as null/undefined.\n            if (val != null && val !== '-') {\n              if (isString(val)) {\n                startIndex == null && (startIndex = 1);\n              } else {\n                startIndex = 0;\n              }\n            } // 10 is an experience number, avoid long loop.\n\n          }, seriesLayoutBy, dataArrayRows, 10);\n        } else {\n          startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0;\n        }\n\n        if (!dimensionsDefine && startIndex === 1) {\n          dimensionsDefine = [];\n          arrayRowsTravelFirst(function (val, index) {\n            dimensionsDefine[index] = val != null ? val + '' : '';\n          }, seriesLayoutBy, dataArrayRows, Infinity);\n        }\n\n        dimensionsDetectedCount = dimensionsDefine ? dimensionsDefine.length : seriesLayoutBy === SERIES_LAYOUT_BY_ROW ? dataArrayRows.length : dataArrayRows[0] ? dataArrayRows[0].length : null;\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        if (!dimensionsDefine) {\n          dimensionsDefine = objectRowsCollectDimensions(data);\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        if (!dimensionsDefine) {\n          dimensionsDefine = [];\n          each(data, function (colArr, key) {\n            dimensionsDefine.push(key);\n          });\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var value0 = getDataItemValue(data[0]);\n        dimensionsDetectedCount = isArray(value0) && value0.length || 1;\n      } else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n        if (\"development\" !== 'production') {\n          assert(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.');\n        }\n      }\n\n      return {\n        startIndex: startIndex,\n        dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n        dimensionsDetectedCount: dimensionsDetectedCount\n      };\n    }\n\n    function objectRowsCollectDimensions(data) {\n      var firstIndex = 0;\n      var obj;\n\n      while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line\n\n\n      if (obj) {\n        var dimensions_1 = [];\n        each(obj, function (value, key) {\n          dimensions_1.push(key);\n        });\n        return dimensions_1;\n      }\n    } // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],\n    // which is reasonable. But dimension name is duplicated.\n    // Returns undefined or an array contains only object without null/undefined or string.\n\n\n    function normalizeDimensionsOption(dimensionsDefine) {\n      if (!dimensionsDefine) {\n        // The meaning of null/undefined is different from empty array.\n        return;\n      }\n\n      var nameMap = createHashMap();\n      return map(dimensionsDefine, function (rawItem, index) {\n        rawItem = isObject(rawItem) ? rawItem : {\n          name: rawItem\n        }; // Other fields will be discarded.\n\n        var item = {\n          name: rawItem.name,\n          displayName: rawItem.displayName,\n          type: rawItem.type\n        }; // User can set null in dimensions.\n        // We don't auto specify name, otherwise a given name may\n        // cause it to be referred unexpectedly.\n\n        if (item.name == null) {\n          return item;\n        } // Also consider number form like 2012.\n\n\n        item.name += ''; // User may also specify displayName.\n        // displayName will always exists except user not\n        // specified or dim name is not specified or detected.\n        // (A auto generated dim name will not be used as\n        // displayName).\n\n        if (item.displayName == null) {\n          item.displayName = item.name;\n        }\n\n        var exist = nameMap.get(item.name);\n\n        if (!exist) {\n          nameMap.set(item.name, {\n            count: 1\n          });\n        } else {\n          item.name += '-' + exist.count++;\n        }\n\n        return item;\n      });\n    }\n\n    function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {\n      if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n        for (var i = 0; i < data.length && i < maxLoop; i++) {\n          cb(data[i] ? data[i][0] : null, i);\n        }\n      } else {\n        var value0 = data[0] || [];\n\n        for (var i = 0; i < value0.length && i < maxLoop; i++) {\n          cb(value0[i], i);\n        }\n      }\n    }\n\n    function shouldRetrieveDataByName(source) {\n      var sourceFormat = source.sourceFormat;\n      return sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var _a, _b, _c; // TODO\n    var providerMethods;\n    var mountMethods;\n    /**\n     * If normal array used, mutable chunk size is supported.\n     * If typed array used, chunk size must be fixed.\n     */\n\n    var DefaultDataProvider =\n    /** @class */\n    function () {\n      function DefaultDataProvider(sourceParam, dimSize) {\n        // let source: Source;\n        var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam; // declare source is Source;\n\n        this._source = source;\n        var data = this._data = source.data; // Typed array. TODO IE10+?\n\n        if (source.sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n          if (\"development\" !== 'production') {\n            if (dimSize == null) {\n              throw new Error('Typed array data must specify dimension size');\n            }\n          }\n\n          this._offset = 0;\n          this._dimSize = dimSize;\n          this._data = data;\n        }\n\n        mountMethods(this, data, source);\n      }\n\n      DefaultDataProvider.prototype.getSource = function () {\n        return this._source;\n      };\n\n      DefaultDataProvider.prototype.count = function () {\n        return 0;\n      };\n\n      DefaultDataProvider.prototype.getItem = function (idx, out) {\n        return;\n      };\n\n      DefaultDataProvider.prototype.appendData = function (newData) {};\n\n      DefaultDataProvider.prototype.clean = function () {};\n\n      DefaultDataProvider.protoInitialize = function () {\n        // PENDING: To avoid potential incompat (e.g., prototype\n        // is visited somewhere), still init them on prototype.\n        var proto = DefaultDataProvider.prototype;\n        proto.pure = false;\n        proto.persistent = true;\n      }();\n\n      DefaultDataProvider.internalField = function () {\n        var _a;\n\n        mountMethods = function (provider, data, source) {\n          var sourceFormat = source.sourceFormat;\n          var seriesLayoutBy = source.seriesLayoutBy;\n          var startIndex = source.startIndex;\n          var dimsDef = source.dimensionsDefine;\n          var methods = providerMethods[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n          if (\"development\" !== 'production') {\n            assert(methods, 'Invalide sourceFormat: ' + sourceFormat);\n          }\n\n          extend(provider, methods);\n\n          if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n            provider.getItem = getItemForTypedArray;\n            provider.count = countForTypedArray;\n            provider.fillStorage = fillStorageForTypedArray;\n          } else {\n            var rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy);\n            provider.getItem = bind(rawItemGetter, null, data, startIndex, dimsDef);\n            var rawCounter = getRawSourceDataCounter(sourceFormat, seriesLayoutBy);\n            provider.count = bind(rawCounter, null, data, startIndex, dimsDef);\n          }\n        };\n\n        var getItemForTypedArray = function (idx, out) {\n          idx = idx - this._offset;\n          out = out || [];\n          var data = this._data;\n          var dimSize = this._dimSize;\n          var offset = dimSize * idx;\n\n          for (var i = 0; i < dimSize; i++) {\n            out[i] = data[offset + i];\n          }\n\n          return out;\n        };\n\n        var fillStorageForTypedArray = function (start, end, storage, extent) {\n          var data = this._data;\n          var dimSize = this._dimSize;\n\n          for (var dim = 0; dim < dimSize; dim++) {\n            var dimExtent = extent[dim];\n            var min = dimExtent[0] == null ? Infinity : dimExtent[0];\n            var max = dimExtent[1] == null ? -Infinity : dimExtent[1];\n            var count = end - start;\n            var arr = storage[dim];\n\n            for (var i = 0; i < count; i++) {\n              // appendData with TypedArray will always do replace in provider.\n              var val = data[i * dimSize + dim];\n              arr[start + i] = val;\n              val < min && (min = val);\n              val > max && (max = val);\n            }\n\n            dimExtent[0] = min;\n            dimExtent[1] = max;\n          }\n        };\n\n        var countForTypedArray = function () {\n          return this._data ? this._data.length / this._dimSize : 0;\n        };\n\n        providerMethods = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = {\n          pure: true,\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = {\n          pure: true,\n          appendData: function () {\n            throw new Error('Do not support appendData when set seriesLayoutBy: \"row\".');\n          }\n        }, _a[SOURCE_FORMAT_OBJECT_ROWS] = {\n          pure: true,\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_KEYED_COLUMNS] = {\n          pure: true,\n          appendData: function (newData) {\n            var data = this._data;\n            each(newData, function (newCol, key) {\n              var oldCol = data[key] || (data[key] = []);\n\n              for (var i = 0; i < (newCol || []).length; i++) {\n                oldCol.push(newCol[i]);\n              }\n            });\n          }\n        }, _a[SOURCE_FORMAT_ORIGINAL] = {\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_TYPED_ARRAY] = {\n          persistent: false,\n          pure: true,\n          appendData: function (newData) {\n            if (\"development\" !== 'production') {\n              assert(isTypedArray(newData), 'Added data must be TypedArray if data in initialization is TypedArray');\n            }\n\n            this._data = newData;\n          },\n          // Clean self if data is already used.\n          clean: function () {\n            // PENDING\n            this._offset += this.count();\n            this._data = null;\n          }\n        }, _a);\n\n        function appendDataSimply(newData) {\n          for (var i = 0; i < newData.length; i++) {\n            this._data.push(newData[i]);\n          }\n        }\n      }();\n\n      return DefaultDataProvider;\n    }();\n\n    var getItemSimply = function (rawData, startIndex, dimsDef, idx) {\n      return rawData[idx];\n    };\n\n    var rawSourceItemGetterMap = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef, idx) {\n      return rawData[idx + startIndex];\n    }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx, out) {\n      idx += startIndex;\n      var item = out || [];\n      var data = rawData;\n\n      for (var i = 0; i < data.length; i++) {\n        var row = data[i];\n        item[i] = row ? row[idx] : null;\n      }\n\n      return item;\n    }, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx, out) {\n      var item = out || [];\n\n      for (var i = 0; i < dimsDef.length; i++) {\n        var dimName = dimsDef[i].name;\n\n        if (\"development\" !== 'production') {\n          if (dimName == null) {\n            throw new Error();\n          }\n        }\n\n        var col = rawData[dimName];\n        item[i] = col ? col[idx] : null;\n      }\n\n      return item;\n    }, _a[SOURCE_FORMAT_ORIGINAL] = getItemSimply, _a);\n    function getRawSourceItemGetter(sourceFormat, seriesLayoutBy) {\n      var method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support get item on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n      }\n\n      return method;\n    }\n\n    var countSimply = function (rawData, startIndex, dimsDef) {\n      return rawData.length;\n    };\n\n    var rawSourceDataCounterMap = (_b = {}, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef) {\n      return Math.max(0, rawData.length - startIndex);\n    }, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef) {\n      var row = rawData[0];\n      return row ? Math.max(0, row.length - startIndex) : 0;\n    }, _b[SOURCE_FORMAT_OBJECT_ROWS] = countSimply, _b[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef) {\n      var dimName = dimsDef[0].name;\n\n      if (\"development\" !== 'production') {\n        if (dimName == null) {\n          throw new Error();\n        }\n      }\n\n      var col = rawData[dimName];\n      return col ? col.length : 0;\n    }, _b[SOURCE_FORMAT_ORIGINAL] = countSimply, _b);\n    function getRawSourceDataCounter(sourceFormat, seriesLayoutBy) {\n      var method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support count on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n      }\n\n      return method;\n    }\n\n    var getRawValueSimply = function (dataItem, dimIndex, property) {\n      return dataItem[dimIndex];\n    };\n\n    var rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, property) {\n      return dataItem[property];\n    }, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, property) {\n      // FIXME: In some case (markpoint in geo (geo-map.html)),\n      // dataItem is {coord: [...]}\n      var value = getDataItemValue(dataItem);\n      return !(value instanceof Array) ? value : value[dimIndex];\n    }, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c);\n    function getRawSourceValueGetter(sourceFormat) {\n      var method = rawSourceValueGetterMap[sourceFormat];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support get value on \"' + sourceFormat + '\".');\n      }\n\n      return method;\n    }\n\n    function getMethodMapKey(sourceFormat, seriesLayoutBy) {\n      return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + seriesLayoutBy : sourceFormat;\n    } // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,\n    // Consider persistent.\n    // Caution: why use raw value to display on label or tooltip?\n    // A reason is to avoid format. For example time value we do not know\n    // how to format is expected. More over, if stack is used, calculated\n    // value may be 0.91000000001, which have brings trouble to display.\n    // TODO: consider how to treat null/undefined/NaN when display?\n\n\n    function retrieveRawValue(data, dataIndex, // If dimIndex is null/undefined, return OptionDataItem.\n    // Otherwise, return OptionDataValue.\n    dim) {\n      if (!data) {\n        return;\n      } // Consider data may be not persistent.\n\n\n      var dataItem = data.getRawDataItem(dataIndex);\n\n      if (dataItem == null) {\n        return;\n      }\n\n      var store = data.getStore();\n      var sourceFormat = store.getSource().sourceFormat;\n\n      if (dim != null) {\n        var dimIndex = data.getDimensionIndex(dim);\n        var property = store.getDimensionProperty(dimIndex);\n        return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, property);\n      } else {\n        var result = dataItem;\n\n        if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n          result = getDataItemValue(dataItem);\n        }\n\n        return result;\n      }\n    }\n\n    var DIMENSION_LABEL_REG = /\\{@(.+?)\\}/g;\n\n    var DataFormatMixin =\n    /** @class */\n    function () {\n      function DataFormatMixin() {}\n      /**\n       * Get params for formatter\n       */\n\n\n      DataFormatMixin.prototype.getDataParams = function (dataIndex, dataType) {\n        var data = this.getData(dataType);\n        var rawValue = this.getRawValue(dataIndex, dataType);\n        var rawDataIndex = data.getRawIndex(dataIndex);\n        var name = data.getName(dataIndex);\n        var itemOpt = data.getRawDataItem(dataIndex);\n        var style = data.getItemVisual(dataIndex, 'style');\n        var color = style && style[data.getItemVisual(dataIndex, 'drawType') || 'fill'];\n        var borderColor = style && style.stroke;\n        var mainType = this.mainType;\n        var isSeries = mainType === 'series';\n        var userOutput = data.userOutput && data.userOutput.get();\n        return {\n          componentType: mainType,\n          componentSubType: this.subType,\n          componentIndex: this.componentIndex,\n          seriesType: isSeries ? this.subType : null,\n          seriesIndex: this.seriesIndex,\n          seriesId: isSeries ? this.id : null,\n          seriesName: isSeries ? this.name : null,\n          name: name,\n          dataIndex: rawDataIndex,\n          data: itemOpt,\n          dataType: dataType,\n          value: rawValue,\n          color: color,\n          borderColor: borderColor,\n          dimensionNames: userOutput ? userOutput.fullDimensions : null,\n          encode: userOutput ? userOutput.encode : null,\n          // Param name list for mapping `a`, `b`, `c`, `d`, `e`\n          $vars: ['seriesName', 'name', 'value']\n        };\n      };\n      /**\n       * Format label\n       * @param dataIndex\n       * @param status 'normal' by default\n       * @param dataType\n       * @param labelDimIndex Only used in some chart that\n       *        use formatter in different dimensions, like radar.\n       * @param formatter Formatter given outside.\n       * @return return null/undefined if no formatter\n       */\n\n\n      DataFormatMixin.prototype.getFormattedLabel = function (dataIndex, status, dataType, labelDimIndex, formatter, extendParams) {\n        status = status || 'normal';\n        var data = this.getData(dataType);\n        var params = this.getDataParams(dataIndex, dataType);\n\n        if (extendParams) {\n          params.value = extendParams.interpolatedValue;\n        }\n\n        if (labelDimIndex != null && isArray(params.value)) {\n          params.value = params.value[labelDimIndex];\n        }\n\n        if (!formatter) {\n          var itemModel = data.getItemModel(dataIndex); // @ts-ignore\n\n          formatter = itemModel.get(status === 'normal' ? ['label', 'formatter'] : [status, 'label', 'formatter']);\n        }\n\n        if (isFunction(formatter)) {\n          params.status = status;\n          params.dimensionIndex = labelDimIndex;\n          return formatter(params);\n        } else if (isString(formatter)) {\n          var str = formatTpl(formatter, params); // Support 'aaa{@[3]}bbb{@product}ccc'.\n          // Do not support '}' in dim name util have to.\n\n          return str.replace(DIMENSION_LABEL_REG, function (origin, dimStr) {\n            var len = dimStr.length;\n            var dimLoose = dimStr;\n\n            if (dimLoose.charAt(0) === '[' && dimLoose.charAt(len - 1) === ']') {\n              dimLoose = +dimLoose.slice(1, len - 1); // Also support: '[]' => 0\n\n              if (\"development\" !== 'production') {\n                if (isNaN(dimLoose)) {\n                  error(\"Invalide label formatter: @\" + dimStr + \", only support @[0], @[1], @[2], ...\");\n                }\n              }\n            }\n\n            var val = retrieveRawValue(data, dataIndex, dimLoose);\n\n            if (extendParams && isArray(extendParams.interpolatedValue)) {\n              var dimIndex = data.getDimensionIndex(dimLoose);\n\n              if (dimIndex >= 0) {\n                val = extendParams.interpolatedValue[dimIndex];\n              }\n            }\n\n            return val != null ? val + '' : '';\n          });\n        }\n      };\n      /**\n       * Get raw value in option\n       */\n\n\n      DataFormatMixin.prototype.getRawValue = function (idx, dataType) {\n        return retrieveRawValue(this.getData(dataType), idx);\n      };\n      /**\n       * Should be implemented.\n       * @param {number} dataIndex\n       * @param {boolean} [multipleSeries=false]\n       * @param {string} [dataType]\n       */\n\n\n      DataFormatMixin.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        // Empty function\n        return;\n      };\n\n      return DataFormatMixin;\n    }();\n    // but guess little chance has been used outside. Do we need to backward\n    // compat it?\n    // type TooltipFormatResultLegacyObject = {\n    //     // `html` means the markup language text, either in 'html' or 'richText'.\n    //     // The name `html` is not appropriate because in 'richText' it is not a HTML\n    //     // string. But still support it for backward compatibility.\n    //     html: string;\n    //     markers: Dictionary<ColorString>;\n    // };\n\n    /**\n     * For backward compat, normalize the return from `formatTooltip`.\n     */\n\n    function normalizeTooltipFormatResult(result) {\n      var markupText; // let markers: Dictionary<ColorString>;\n\n      var markupFragment;\n\n      if (isObject(result)) {\n        if (result.type) {\n          markupFragment = result;\n        } else {\n          if (\"development\" !== 'production') {\n            console.warn('The return type of `formatTooltip` is not supported: ' + makePrintable(result));\n          }\n        } // else {\n        //     markupText = (result as TooltipFormatResultLegacyObject).html;\n        //     markers = (result as TooltipFormatResultLegacyObject).markers;\n        //     if (markersExisting) {\n        //         markers = zrUtil.merge(markersExisting, markers);\n        //     }\n        // }\n\n      } else {\n        markupText = result;\n      }\n\n      return {\n        text: markupText,\n        // markers: markers || markersExisting,\n        frag: markupFragment\n      };\n    }\n\n    /**\n     * @param {Object} define\n     * @return See the return of `createTask`.\n     */\n\n    function createTask(define) {\n      return new Task(define);\n    }\n\n    var Task =\n    /** @class */\n    function () {\n      function Task(define) {\n        define = define || {};\n        this._reset = define.reset;\n        this._plan = define.plan;\n        this._count = define.count;\n        this._onDirty = define.onDirty;\n        this._dirty = true;\n      }\n      /**\n       * @param step Specified step.\n       * @param skip Skip customer perform call.\n       * @param modBy Sampling window size.\n       * @param modDataCount Sampling count.\n       * @return whether unfinished.\n       */\n\n\n      Task.prototype.perform = function (performArgs) {\n        var upTask = this._upstream;\n        var skip = performArgs && performArgs.skip; // TODO some refactor.\n        // Pull data. Must pull data each time, because context.data\n        // may be updated by Series.setData.\n\n        if (this._dirty && upTask) {\n          var context = this.context;\n          context.data = context.outputData = upTask.context.outputData;\n        }\n\n        if (this.__pipeline) {\n          this.__pipeline.currentTask = this;\n        }\n\n        var planResult;\n\n        if (this._plan && !skip) {\n          planResult = this._plan(this.context);\n        } // Support sharding by mod, which changes the render sequence and makes the rendered graphic\n        // elements uniformed distributed when progress, especially when moving or zooming.\n\n\n        var lastModBy = normalizeModBy(this._modBy);\n        var lastModDataCount = this._modDataCount || 0;\n        var modBy = normalizeModBy(performArgs && performArgs.modBy);\n        var modDataCount = performArgs && performArgs.modDataCount || 0;\n\n        if (lastModBy !== modBy || lastModDataCount !== modDataCount) {\n          planResult = 'reset';\n        }\n\n        function normalizeModBy(val) {\n          !(val >= 1) && (val = 1); // jshint ignore:line\n\n          return val;\n        }\n\n        var forceFirstProgress;\n\n        if (this._dirty || planResult === 'reset') {\n          this._dirty = false;\n          forceFirstProgress = this._doReset(skip);\n        }\n\n        this._modBy = modBy;\n        this._modDataCount = modDataCount;\n        var step = performArgs && performArgs.step;\n\n        if (upTask) {\n          if (\"development\" !== 'production') {\n            assert(upTask._outputDueEnd != null);\n          }\n\n          this._dueEnd = upTask._outputDueEnd;\n        } // DataTask or overallTask\n        else {\n            if (\"development\" !== 'production') {\n              assert(!this._progress || this._count);\n            }\n\n            this._dueEnd = this._count ? this._count(this.context) : Infinity;\n          } // Note: Stubs, that its host overall task let it has progress, has progress.\n        // If no progress, pass index from upstream to downstream each time plan called.\n\n\n        if (this._progress) {\n          var start = this._dueIndex;\n          var end = Math.min(step != null ? this._dueIndex + step : Infinity, this._dueEnd);\n\n          if (!skip && (forceFirstProgress || start < end)) {\n            var progress = this._progress;\n\n            if (isArray(progress)) {\n              for (var i = 0; i < progress.length; i++) {\n                this._doProgress(progress[i], start, end, modBy, modDataCount);\n              }\n            } else {\n              this._doProgress(progress, start, end, modBy, modDataCount);\n            }\n          }\n\n          this._dueIndex = end; // If no `outputDueEnd`, assume that output data and\n          // input data is the same, so use `dueIndex` as `outputDueEnd`.\n\n          var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end;\n\n          if (\"development\" !== 'production') {\n            // ??? Can not rollback.\n            assert(outputDueEnd >= this._outputDueEnd);\n          }\n\n          this._outputDueEnd = outputDueEnd;\n        } else {\n          // (1) Some overall task has no progress.\n          // (2) Stubs, that its host overall task do not let it has progress, has no progress.\n          // This should always be performed so it can be passed to downstream.\n          this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd;\n        }\n\n        return this.unfinished();\n      };\n\n      Task.prototype.dirty = function () {\n        this._dirty = true;\n        this._onDirty && this._onDirty(this.context);\n      };\n\n      Task.prototype._doProgress = function (progress, start, end, modBy, modDataCount) {\n        iterator.reset(start, end, modBy, modDataCount);\n        this._callingProgress = progress;\n\n        this._callingProgress({\n          start: start,\n          end: end,\n          count: end - start,\n          next: iterator.next\n        }, this.context);\n      };\n\n      Task.prototype._doReset = function (skip) {\n        this._dueIndex = this._outputDueEnd = this._dueEnd = 0;\n        this._settedOutputEnd = null;\n        var progress;\n        var forceFirstProgress;\n\n        if (!skip && this._reset) {\n          progress = this._reset(this.context);\n\n          if (progress && progress.progress) {\n            forceFirstProgress = progress.forceFirstProgress;\n            progress = progress.progress;\n          } // To simplify no progress checking, array must has item.\n\n\n          if (isArray(progress) && !progress.length) {\n            progress = null;\n          }\n        }\n\n        this._progress = progress;\n        this._modBy = this._modDataCount = null;\n        var downstream = this._downstream;\n        downstream && downstream.dirty();\n        return forceFirstProgress;\n      };\n\n      Task.prototype.unfinished = function () {\n        return this._progress && this._dueIndex < this._dueEnd;\n      };\n      /**\n       * @param downTask The downstream task.\n       * @return The downstream task.\n       */\n\n\n      Task.prototype.pipe = function (downTask) {\n        if (\"development\" !== 'production') {\n          assert(downTask && !downTask._disposed && downTask !== this);\n        } // If already downstream, do not dirty downTask.\n\n\n        if (this._downstream !== downTask || this._dirty) {\n          this._downstream = downTask;\n          downTask._upstream = this;\n          downTask.dirty();\n        }\n      };\n\n      Task.prototype.dispose = function () {\n        if (this._disposed) {\n          return;\n        }\n\n        this._upstream && (this._upstream._downstream = null);\n        this._downstream && (this._downstream._upstream = null);\n        this._dirty = false;\n        this._disposed = true;\n      };\n\n      Task.prototype.getUpstream = function () {\n        return this._upstream;\n      };\n\n      Task.prototype.getDownstream = function () {\n        return this._downstream;\n      };\n\n      Task.prototype.setOutputEnd = function (end) {\n        // This only happens in dataTask, dataZoom, map, currently.\n        // where dataZoom do not set end each time, but only set\n        // when reset. So we should record the set end, in case\n        // that the stub of dataZoom perform again and earse the\n        // set end by upstream.\n        this._outputDueEnd = this._settedOutputEnd = end;\n      };\n\n      return Task;\n    }();\n\n    var iterator = function () {\n      var end;\n      var current;\n      var modBy;\n      var modDataCount;\n      var winCount;\n      var it = {\n        reset: function (s, e, sStep, sCount) {\n          current = s;\n          end = e;\n          modBy = sStep;\n          modDataCount = sCount;\n          winCount = Math.ceil(modDataCount / modBy);\n          it.next = modBy > 1 && modDataCount > 0 ? modNext : sequentialNext;\n        }\n      };\n      return it;\n\n      function sequentialNext() {\n        return current < end ? current++ : null;\n      }\n\n      function modNext() {\n        var dataIndex = current % winCount * modBy + Math.ceil(current / winCount);\n        var result = current >= end ? null : dataIndex < modDataCount ? dataIndex // If modDataCount is smaller than data.count() (consider `appendData` case),\n        // Use normal linear rendering mode.\n        : current;\n        current++;\n        return result;\n      }\n    }(); // -----------------------------------------------------------------------------\n    // For stream debug (Should be commented out after used!)\n    // @usage: printTask(this, 'begin');\n    // @usage: printTask(this, null, {someExtraProp});\n    // @usage: Use `__idxInPipeline` as conditional breakpiont.\n    //\n    // window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void {\n    //     window.ecTaskUID == null && (window.ecTaskUID = 0);\n    //     task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);\n    //     task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);\n    //     let props = [];\n    //     if (task.__pipeline) {\n    //         let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;\n    //         props.push({text: '__idxInPipeline/total', value: val});\n    //     } else {\n    //         let stubCount = 0;\n    //         task.agentStubMap.each(() => stubCount++);\n    //         props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});\n    //     }\n    //     props.push({text: 'uid', value: task.uidDebug});\n    //     if (task.__pipeline) {\n    //         props.push({text: 'pipelineId', value: task.__pipeline.id});\n    //         task.agent && props.push(\n    //             {text: 'stubFor', value: task.agent.uidDebug}\n    //         );\n    //     }\n    //     props.push(\n    //         {text: 'dirty', value: task._dirty},\n    //         {text: 'dueIndex', value: task._dueIndex},\n    //         {text: 'dueEnd', value: task._dueEnd},\n    //         {text: 'outputDueEnd', value: task._outputDueEnd}\n    //     );\n    //     if (extra) {\n    //         Object.keys(extra).forEach(key => {\n    //             props.push({text: key, value: extra[key]});\n    //         });\n    //     }\n    //     let args = ['color: blue'];\n    //     let msg = `%c[${prefix || 'T'}] %c` + props.map(item => (\n    //         args.push('color: green', 'color: red'),\n    //         `${item.text}: %c${item.value}`\n    //     )).join('%c, ');\n    //     console.log.apply(console, [msg].concat(args));\n    //     // console.log(this);\n    // };\n    // window.printPipeline = function (task: any, prefix: string) {\n    //     const pipeline = task.__pipeline;\n    //     let currTask = pipeline.head;\n    //     while (currTask) {\n    //         window.printTask(currTask, prefix);\n    //         currTask = currTask._downstream;\n    //     }\n    // };\n    // window.showChain = function (chainHeadTask) {\n    //     var chain = [];\n    //     var task = chainHeadTask;\n    //     while (task) {\n    //         chain.push({\n    //             task: task,\n    //             up: task._upstream,\n    //             down: task._downstream,\n    //             idxInPipeline: task.__idxInPipeline\n    //         });\n    //         task = task._downstream;\n    //     }\n    //     return chain;\n    // };\n    // window.findTaskInChain = function (task, chainHeadTask) {\n    //     let chain = window.showChain(chainHeadTask);\n    //     let result = [];\n    //     for (let i = 0; i < chain.length; i++) {\n    //         let chainItem = chain[i];\n    //         if (chainItem.task === task) {\n    //             result.push(i);\n    //         }\n    //     }\n    //     return result;\n    // };\n    // window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) {\n    //     let chainA = window.showChain(chainHeadTaskA);\n    //     for (let i = 0; i < chainA.length; i++) {\n    //         console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB));\n    //     }\n    // };\n\n    /**\n     * Convert raw the value in to inner value in List.\n     *\n     * [Performance sensitive]\n     *\n     * [Caution]: this is the key logic of user value parser.\n     * For backward compatibility, do not modify it until you have to!\n     */\n\n    function parseDataValue(value, // For high performance, do not omit the second param.\n    opt) {\n      // Performance sensitive.\n      var dimType = opt && opt.type;\n\n      if (dimType === 'ordinal') {\n        // If given value is a category string\n        return value;\n      }\n\n      if (dimType === 'time' // spead up when using timestamp\n      && !isNumber(value) && value != null && value !== '-') {\n        value = +parseDate(value);\n      } // dimType defaults 'number'.\n      // If dimType is not ordinal and value is null or undefined or NaN or '-',\n      // parse to NaN.\n      // number-like string (like ' 123 ') can be converted to a number.\n      // where null/undefined or other string will be converted to NaN.\n\n\n      return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN\n      // If object, also parse to NaN\n      : +value;\n    }\n    var valueParserMap = createHashMap({\n      'number': function (val) {\n        // Do not use `numericToNumber` here. We have `numericToNumber` by default.\n        // Here the number parser can have loose rule:\n        // enable to cut suffix: \"120px\" => 120, \"14%\" => 14.\n        return parseFloat(val);\n      },\n      'time': function (val) {\n        // return timestamp.\n        return +parseDate(val);\n      },\n      'trim': function (val) {\n        return isString(val) ? trim(val) : val;\n      }\n    });\n\n    var SortOrderComparator =\n    /** @class */\n    function () {\n      /**\n       * @param order by default: 'asc'\n       * @param incomparable by default: Always on the tail.\n       *        That is, if 'asc' => 'max', if 'desc' => 'min'\n       *        See the definition of \"incomparable\" in [SORT_COMPARISON_RULE].\n       */\n      function SortOrderComparator(order, incomparable) {\n        var isDesc = order === 'desc';\n        this._resultLT = isDesc ? 1 : -1;\n\n        if (incomparable == null) {\n          incomparable = isDesc ? 'min' : 'max';\n        }\n\n        this._incomparable = incomparable === 'min' ? -Infinity : Infinity;\n      } // See [SORT_COMPARISON_RULE].\n      // Performance sensitive.\n\n\n      SortOrderComparator.prototype.evaluate = function (lval, rval) {\n        // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.\n        var lvalFloat = isNumber(lval) ? lval : numericToNumber(lval);\n        var rvalFloat = isNumber(rval) ? rval : numericToNumber(rval);\n        var lvalNotNumeric = isNaN(lvalFloat);\n        var rvalNotNumeric = isNaN(rvalFloat);\n\n        if (lvalNotNumeric) {\n          lvalFloat = this._incomparable;\n        }\n\n        if (rvalNotNumeric) {\n          rvalFloat = this._incomparable;\n        }\n\n        if (lvalNotNumeric && rvalNotNumeric) {\n          var lvalIsStr = isString(lval);\n          var rvalIsStr = isString(rval);\n\n          if (lvalIsStr) {\n            lvalFloat = rvalIsStr ? lval : 0;\n          }\n\n          if (rvalIsStr) {\n            rvalFloat = lvalIsStr ? rval : 0;\n          }\n        }\n\n        return lvalFloat < rvalFloat ? this._resultLT : lvalFloat > rvalFloat ? -this._resultLT : 0;\n      };\n\n      return SortOrderComparator;\n    }();\n\n    /**\n     * TODO: disable writable.\n     * This structure will be exposed to users.\n     */\n\n    var ExternalSource =\n    /** @class */\n    function () {\n      function ExternalSource() {}\n\n      ExternalSource.prototype.getRawData = function () {\n        // Only built-in transform available.\n        throw new Error('not supported');\n      };\n\n      ExternalSource.prototype.getRawDataItem = function (dataIndex) {\n        // Only built-in transform available.\n        throw new Error('not supported');\n      };\n\n      ExternalSource.prototype.cloneRawData = function () {\n        return;\n      };\n      /**\n       * @return If dimension not found, return null/undefined.\n       */\n\n\n      ExternalSource.prototype.getDimensionInfo = function (dim) {\n        return;\n      };\n      /**\n       * dimensions defined if and only if either:\n       * (a) dataset.dimensions are declared.\n       * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).\n       * If dimensions are defined, `dimensionInfoAll` is corresponding to\n       * the defined dimensions.\n       * Otherwise, `dimensionInfoAll` is determined by data columns.\n       * @return Always return an array (even empty array).\n       */\n\n\n      ExternalSource.prototype.cloneAllDimensionInfo = function () {\n        return;\n      };\n\n      ExternalSource.prototype.count = function () {\n        return;\n      };\n      /**\n       * Only support by dimension index.\n       * No need to support by dimension name in transform function,\n       * because transform function is not case-specific, no need to use name literally.\n       */\n\n\n      ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {\n        return;\n      };\n\n      ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {\n        return;\n      };\n\n      ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {\n        return parseDataValue(rawVal, dimInfo);\n      };\n\n      return ExternalSource;\n    }();\n\n    function createExternalSource(internalSource, externalTransform) {\n      var extSource = new ExternalSource();\n      var data = internalSource.data;\n      var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;\n      var sourceHeaderCount = internalSource.startIndex;\n      var errMsg = '';\n\n      if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {\n        // For the logic simplicity in transformer, only 'culumn' is\n        // supported in data transform. Otherwise, the `dimensionsDefine`\n        // might be detected by 'row', which probably confuses users.\n        if (\"development\" !== 'production') {\n          errMsg = '`seriesLayoutBy` of upstream dataset can only be \"column\" in data transform.';\n        }\n\n        throwError(errMsg);\n      } // [MEMO]\n      // Create a new dimensions structure for exposing.\n      // Do not expose all dimension info to users directly.\n      // Because the dimension is probably auto detected from data and not might reliable.\n      // Should not lead the transformers to think that is reliable and return it.\n      // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n\n\n      var dimensions = [];\n      var dimsByName = {};\n      var dimsDef = internalSource.dimensionsDefine;\n\n      if (dimsDef) {\n        each(dimsDef, function (dimDef, idx) {\n          var name = dimDef.name;\n          var dimDefExt = {\n            index: idx,\n            name: name,\n            displayName: dimDef.displayName\n          };\n          dimensions.push(dimDefExt); // Users probably do not specify dimension name. For simplicity, data transform\n          // does not generate dimension name.\n\n          if (name != null) {\n            // Dimension name should not be duplicated.\n            // For simplicity, data transform forbids name duplication, do not generate\n            // new name like module `completeDimensions.ts` did, but just tell users.\n            var errMsg_1 = '';\n\n            if (hasOwn(dimsByName, name)) {\n              if (\"development\" !== 'production') {\n                errMsg_1 = 'dimension name \"' + name + '\" duplicated.';\n              }\n\n              throwError(errMsg_1);\n            }\n\n            dimsByName[name] = dimDefExt;\n          }\n        });\n      } // If dimension definitions are not defined and can not be detected.\n      // e.g., pure data `[[11, 22], ...]`.\n      else {\n          for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {\n            // Do not generete name or anything others. The consequence process in\n            // `transform` or `series` probably have there own name generation strategry.\n            dimensions.push({\n              index: i\n            });\n          }\n        } // Implement public methods:\n\n\n      var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n\n      if (externalTransform.__isBuiltIn) {\n        extSource.getRawDataItem = function (dataIndex) {\n          return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n        };\n\n        extSource.getRawData = bind(getRawData, null, internalSource);\n      }\n\n      extSource.cloneRawData = bind(cloneRawData, null, internalSource);\n      var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n      extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);\n      var rawValueGetter = getRawSourceValueGetter(sourceFormat);\n\n      extSource.retrieveValue = function (dataIndex, dimIndex) {\n        var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n        return retrieveValueFromItem(rawItem, dimIndex);\n      };\n\n      var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {\n        if (dataItem == null) {\n          return;\n        }\n\n        var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item.\n\n        if (dimDef) {\n          return rawValueGetter(dataItem, dimIndex, dimDef.name);\n        }\n      };\n\n      extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);\n      extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);\n      return extSource;\n    }\n\n    function getRawData(upstream) {\n      var sourceFormat = upstream.sourceFormat;\n\n      if (!isSupportedSourceFormat(sourceFormat)) {\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '`getRawData` is not supported in source format ' + sourceFormat;\n        }\n\n        throwError(errMsg);\n      }\n\n      return upstream.data;\n    }\n\n    function cloneRawData(upstream) {\n      var sourceFormat = upstream.sourceFormat;\n      var data = upstream.data;\n\n      if (!isSupportedSourceFormat(sourceFormat)) {\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;\n        }\n\n        throwError(errMsg);\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var result = [];\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          // Not strictly clone for performance\n          result.push(data[i].slice());\n        }\n\n        return result;\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        var result = [];\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          // Not strictly clone for performance\n          result.push(extend({}, data[i]));\n        }\n\n        return result;\n      }\n    }\n\n    function getDimensionInfo(dimensions, dimsByName, dim) {\n      if (dim == null) {\n        return;\n      } // Keep the same logic as `List::getDimension` did.\n\n\n      if (isNumber(dim) // If being a number-like string but not being defined a dimension name.\n      || !isNaN(dim) && !hasOwn(dimsByName, dim)) {\n        return dimensions[dim];\n      } else if (hasOwn(dimsByName, dim)) {\n        return dimsByName[dim];\n      }\n    }\n\n    function cloneAllDimensionInfo(dimensions) {\n      return clone(dimensions);\n    }\n\n    var externalTransformMap = createHashMap();\n    function registerExternalTransform(externalTransform) {\n      externalTransform = clone(externalTransform);\n      var type = externalTransform.type;\n      var errMsg = '';\n\n      if (!type) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Must have a `type` when `registerTransform`.';\n        }\n\n        throwError(errMsg);\n      }\n\n      var typeParsed = type.split(':');\n\n      if (typeParsed.length !== 2) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Name must include namespace like \"ns:regression\".';\n        }\n\n        throwError(errMsg);\n      } // Namespace 'echarts:xxx' is official namespace, where the transforms should\n      // be called directly via 'xxx' rather than 'echarts:xxx'.\n\n\n      var isBuiltIn = false;\n\n      if (typeParsed[0] === 'echarts') {\n        type = typeParsed[1];\n        isBuiltIn = true;\n      }\n\n      externalTransform.__isBuiltIn = isBuiltIn;\n      externalTransformMap.set(type, externalTransform);\n    }\n    function applyDataTransform(rawTransOption, sourceList, infoForPrint) {\n      var pipedTransOption = normalizeToArray(rawTransOption);\n      var pipeLen = pipedTransOption.length;\n      var errMsg = '';\n\n      if (!pipeLen) {\n        if (\"development\" !== 'production') {\n          errMsg = 'If `transform` declared, it should at least contain one transform.';\n        }\n\n        throwError(errMsg);\n      }\n\n      for (var i = 0, len = pipeLen; i < len; i++) {\n        var transOption = pipedTransOption[i];\n        sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one.\n        // piped transform only support single output, except the last one.\n\n        if (i !== len - 1) {\n          sourceList.length = Math.max(sourceList.length, 1);\n        }\n      }\n\n      return sourceList;\n    }\n\n    function applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform.\n    pipeIndex) {\n      var errMsg = '';\n\n      if (!upSourceList.length) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Must have at least one upstream dataset.';\n        }\n\n        throwError(errMsg);\n      }\n\n      if (!isObject(transOption)) {\n        if (\"development\" !== 'production') {\n          errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';\n        }\n\n        throwError(errMsg);\n      }\n\n      var transType = transOption.type;\n      var externalTransform = externalTransformMap.get(transType);\n\n      if (!externalTransform) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Can not find transform on type \"' + transType + '\".';\n        }\n\n        throwError(errMsg);\n      } // Prepare source\n\n\n      var extUpSourceList = map(upSourceList, function (upSource) {\n        return createExternalSource(upSource, externalTransform);\n      });\n      var resultList = normalizeToArray(externalTransform.transform({\n        upstream: extUpSourceList[0],\n        upstreamList: extUpSourceList,\n        config: clone(transOption.config)\n      }));\n\n      if (\"development\" !== 'production') {\n        if (transOption.print) {\n          var printStrArr = map(resultList, function (extSource) {\n            var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';\n            return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\\n');\n          }).join('\\n');\n          log(printStrArr);\n        }\n      }\n\n      return map(resultList, function (result, resultIndex) {\n        var errMsg = '';\n\n        if (!isObject(result)) {\n          if (\"development\" !== 'production') {\n            errMsg = 'A transform should not return some empty results.';\n          }\n\n          throwError(errMsg);\n        }\n\n        if (!result.data) {\n          if (\"development\" !== 'production') {\n            errMsg = 'Transform result data should be not be null or undefined';\n          }\n\n          throwError(errMsg);\n        }\n\n        var sourceFormat = detectSourceFormat(result.data);\n\n        if (!isSupportedSourceFormat(sourceFormat)) {\n          if (\"development\" !== 'production') {\n            errMsg = 'Transform result data should be array rows or object rows.';\n          }\n\n          throwError(errMsg);\n        }\n\n        var resultMetaRawOption;\n        var firstUpSource = upSourceList[0];\n        /**\n         * Intuitively, the end users known the content of the original `dataset.source`,\n         * calucating the transform result in mind.\n         * Suppose the original `dataset.source` is:\n         * ```js\n         * [\n         *     ['product', '2012', '2013', '2014', '2015'],\n         *     ['AAA', 41.1, 30.4, 65.1, 53.3],\n         *     ['BBB', 86.5, 92.1, 85.7, 83.1],\n         *     ['CCC', 24.1, 67.2, 79.5, 86.4]\n         * ]\n         * ```\n         * The dimension info have to be detected from the source data.\n         * Some of the transformers (like filter, sort) will follow the dimension info\n         * of upstream, while others use new dimensions (like aggregate).\n         * Transformer can output a field `dimensions` to define the its own output dimensions.\n         * We also allow transformers to ignore the output `dimensions` field, and\n         * inherit the upstream dimensions definition. It can reduce the burden of handling\n         * dimensions in transformers.\n         *\n         * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n         */\n\n        if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different\n        // dimensions definitions. We do not inherit anything from upstream.\n        && !result.dimensions) {\n          var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result, because:\n          // (1) The returned data always does not contain header line and can not be used\n          // as dimension-detection. In this case we can not use \"detected dimensions\" of\n          // upstream directly, because it might be detected based on different `seriesLayoutBy`.\n          // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.\n          // So the original detected header should be add to the result, otherwise they can not be read.\n\n          if (startIndex) {\n            result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);\n          }\n\n          resultMetaRawOption = {\n            seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n            sourceHeader: startIndex,\n            dimensions: firstUpSource.metaRawOption.dimensions\n          };\n        } else {\n          resultMetaRawOption = {\n            seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n            sourceHeader: 0,\n            dimensions: result.dimensions\n          };\n        }\n\n        return createSource(result.data, resultMetaRawOption, null);\n      });\n    }\n\n    function isSupportedSourceFormat(sourceFormat) {\n      return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;\n    }\n\n    var UNDEFINED = 'undefined';\n    /* global Float64Array, Int32Array, Uint32Array, Uint16Array */\n    // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is\n    // different from the Ctor of typed array.\n\n    var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;\n    var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;\n    var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;\n    var CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array;\n    /**\n     * Multi dimensional data store\n     */\n\n    var dataCtors = {\n      'float': CtorFloat64Array,\n      'int': CtorInt32Array,\n      // Ordinal data type can be string or int\n      'ordinal': Array,\n      'number': Array,\n      'time': CtorFloat64Array\n    };\n    var defaultDimValueGetters;\n\n    function getIndicesCtor(rawCount) {\n      // The possible max value in this._indicies is always this._rawCount despite of filtering.\n      return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;\n    }\n\n    function getInitialExtent() {\n      return [Infinity, -Infinity];\n    }\n\n    function cloneChunk(originalChunk) {\n      var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array.\n\n      return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);\n    }\n\n    function prepareStore(store, dimIdx, dimType, end, append) {\n      var DataCtor = dataCtors[dimType || 'float'];\n\n      if (append) {\n        var oldStore = store[dimIdx];\n        var oldLen = oldStore && oldStore.length;\n\n        if (!(oldLen === end)) {\n          var newStore = new DataCtor(end); // The cost of the copy is probably inconsiderable\n          // within the initial chunkSize.\n\n          for (var j = 0; j < oldLen; j++) {\n            newStore[j] = oldStore[j];\n          }\n\n          store[dimIdx] = newStore;\n        }\n      } else {\n        store[dimIdx] = new DataCtor(end);\n      }\n    }\n    /**\n     * Basically, DataStore API keep immutable.\n     */\n\n    var DataStore =\n    /** @class */\n    function () {\n      function DataStore() {\n        this._chunks = []; // It will not be calculated util needed.\n\n        this._rawExtent = [];\n        this._extent = [];\n        this._count = 0;\n        this._rawCount = 0;\n        this._calcDimNameToIdx = createHashMap();\n      }\n      /**\n       * Initialize from data\n       */\n\n\n      DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {\n        if (\"development\" !== 'production') {\n          assert(isFunction(provider.getItem) && isFunction(provider.count), 'Invalid data provider.');\n        }\n\n        this._provider = provider; // Clear\n\n        this._chunks = [];\n        this._indices = null;\n        this.getRawIndex = this._getRawIdxIdentity;\n        var source = provider.getSource();\n        var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; // Default dim value getter\n\n        this._dimValueGetter = dimValueGetter || defaultGetter; // Reset raw extent.\n\n        this._rawExtent = [];\n        var willRetrieveDataByName = shouldRetrieveDataByName(source);\n        this._dimensions = map(inputDimensions, function (dim) {\n          if (\"development\" !== 'production') {\n            if (willRetrieveDataByName) {\n              assert(dim.property != null);\n            }\n          }\n\n          return {\n            // Only pick these two props. Not leak other properties like orderMeta.\n            type: dim.type,\n            property: dim.property\n          };\n        });\n\n        this._initDataFromProvider(0, provider.count());\n      };\n\n      DataStore.prototype.getProvider = function () {\n        return this._provider;\n      };\n      /**\n       * Caution: even when a `source` instance owned by a series, the created data store\n       * may still be shared by different sereis (the source hash does not use all `source`\n       * props, see `sourceManager`). In this case, the `source` props that are not used in\n       * hash (like `source.dimensionDefine`) probably only belongs to a certain series and\n       * thus should not be fetch here.\n       */\n\n\n      DataStore.prototype.getSource = function () {\n        return this._provider.getSource();\n      };\n      /**\n       * @caution Only used in dataStack.\n       */\n\n\n      DataStore.prototype.ensureCalculationDimension = function (dimName, type) {\n        var calcDimNameToIdx = this._calcDimNameToIdx;\n        var dimensions = this._dimensions;\n        var calcDimIdx = calcDimNameToIdx.get(dimName);\n\n        if (calcDimIdx != null) {\n          if (dimensions[calcDimIdx].type === type) {\n            return calcDimIdx;\n          }\n        } else {\n          calcDimIdx = dimensions.length;\n        }\n\n        dimensions[calcDimIdx] = {\n          type: type\n        };\n        calcDimNameToIdx.set(dimName, calcDimIdx);\n        this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);\n        this._rawExtent[calcDimIdx] = getInitialExtent();\n        return calcDimIdx;\n      };\n\n      DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) {\n        var chunk = this._chunks[dimIdx];\n        var dim = this._dimensions[dimIdx];\n        var rawExtents = this._rawExtent;\n        var offset = dim.ordinalOffset || 0;\n        var len = chunk.length;\n\n        if (offset === 0) {\n          // We need to reset the rawExtent if collect is from start.\n          // Because this dimension may be guessed as number and calcuating a wrong extent.\n          rawExtents[dimIdx] = getInitialExtent();\n        }\n\n        var dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData\n\n        for (var i = offset; i < len; i++) {\n          var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]);\n\n          if (!isNaN(val)) {\n            dimRawExtent[0] = Math.min(val, dimRawExtent[0]);\n            dimRawExtent[1] = Math.max(val, dimRawExtent[1]);\n          }\n        }\n\n        dim.ordinalMeta = ordinalMeta;\n        dim.ordinalOffset = len;\n        dim.type = 'ordinal'; // Force to be ordinal\n      };\n\n      DataStore.prototype.getOrdinalMeta = function (dimIdx) {\n        var dimInfo = this._dimensions[dimIdx];\n        var ordinalMeta = dimInfo.ordinalMeta;\n        return ordinalMeta;\n      };\n\n      DataStore.prototype.getDimensionProperty = function (dimIndex) {\n        var item = this._dimensions[dimIndex];\n        return item && item.property;\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       */\n\n\n      DataStore.prototype.appendData = function (data) {\n        if (\"development\" !== 'production') {\n          assert(!this._indices, 'appendData can only be called on raw data.');\n        }\n\n        var provider = this._provider;\n        var start = this.count();\n        provider.appendData(data);\n        var end = provider.count();\n\n        if (!provider.persistent) {\n          end += start;\n        }\n\n        if (start < end) {\n          this._initDataFromProvider(start, end, true);\n        }\n\n        return [start, end];\n      };\n\n      DataStore.prototype.appendValues = function (values, minFillLen) {\n        var chunks = this._chunks;\n        var dimensions = this._dimensions;\n        var dimLen = dimensions.length;\n        var rawExtent = this._rawExtent;\n        var start = this.count();\n        var end = start + Math.max(values.length, minFillLen || 0);\n\n        for (var i = 0; i < dimLen; i++) {\n          var dim = dimensions[i];\n          prepareStore(chunks, i, dim.type, end, true);\n        }\n\n        var emptyDataItem = [];\n\n        for (var idx = start; idx < end; idx++) {\n          var sourceIdx = idx - start; // Store the data by dimensions\n\n          for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n            var dim = dimensions[dimIdx];\n            var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx);\n            chunks[dimIdx][idx] = val;\n            var dimRawExtent = rawExtent[dimIdx];\n            val < dimRawExtent[0] && (dimRawExtent[0] = val);\n            val > dimRawExtent[1] && (dimRawExtent[1] = val);\n          }\n        }\n\n        this._rawCount = this._count = end;\n        return {\n          start: start,\n          end: end\n        };\n      };\n\n      DataStore.prototype._initDataFromProvider = function (start, end, append) {\n        var provider = this._provider;\n        var chunks = this._chunks;\n        var dimensions = this._dimensions;\n        var dimLen = dimensions.length;\n        var rawExtent = this._rawExtent;\n        var dimNames = map(dimensions, function (dim) {\n          return dim.property;\n        });\n\n        for (var i = 0; i < dimLen; i++) {\n          var dim = dimensions[i];\n\n          if (!rawExtent[i]) {\n            rawExtent[i] = getInitialExtent();\n          }\n\n          prepareStore(chunks, i, dim.type, end, append);\n        }\n\n        if (provider.fillStorage) {\n          provider.fillStorage(start, end, chunks, rawExtent);\n        } else {\n          var dataItem = [];\n\n          for (var idx = start; idx < end; idx++) {\n            // NOTICE: Try not to write things into dataItem\n            dataItem = provider.getItem(idx, dataItem); // Each data item is value\n            // [1, 2]\n            // 2\n            // Bar chart, line chart which uses category axis\n            // only gives the 'y' value. 'x' value is the indices of category\n            // Use a tempValue to normalize the value to be a (x, y) value\n            // Store the data by dimensions\n\n            for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n              var dimStorage = chunks[dimIdx]; // PENDING NULL is empty or zero\n\n              var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx);\n\n              dimStorage[idx] = val;\n              var dimRawExtent = rawExtent[dimIdx];\n              val < dimRawExtent[0] && (dimRawExtent[0] = val);\n              val > dimRawExtent[1] && (dimRawExtent[1] = val);\n            }\n          }\n        }\n\n        if (!provider.persistent && provider.clean) {\n          // Clean unused data if data source is typed array.\n          provider.clean();\n        }\n\n        this._rawCount = this._count = end; // Reset data extent\n\n        this._extent = [];\n      };\n\n      DataStore.prototype.count = function () {\n        return this._count;\n      };\n      /**\n       * Get value. Return NaN if idx is out of range.\n       */\n\n\n      DataStore.prototype.get = function (dim, idx) {\n        if (!(idx >= 0 && idx < this._count)) {\n          return NaN;\n        }\n\n        var dimStore = this._chunks[dim];\n        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;\n      };\n\n      DataStore.prototype.getValues = function (dimensions, idx) {\n        var values = [];\n        var dimArr = [];\n\n        if (idx == null) {\n          idx = dimensions; // TODO get all from store?\n\n          dimensions = []; // All dimensions\n\n          for (var i = 0; i < this._dimensions.length; i++) {\n            dimArr.push(i);\n          }\n        } else {\n          dimArr = dimensions;\n        }\n\n        for (var i = 0, len = dimArr.length; i < len; i++) {\n          values.push(this.get(dimArr[i], idx));\n        }\n\n        return values;\n      };\n      /**\n       * @param dim concrete dim\n       */\n\n\n      DataStore.prototype.getByRawIndex = function (dim, rawIdx) {\n        if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {\n          return NaN;\n        }\n\n        var dimStore = this._chunks[dim];\n        return dimStore ? dimStore[rawIdx] : NaN;\n      };\n      /**\n       * Get sum of data in one dimension\n       */\n\n\n      DataStore.prototype.getSum = function (dim) {\n        var dimData = this._chunks[dim];\n        var sum = 0;\n\n        if (dimData) {\n          for (var i = 0, len = this.count(); i < len; i++) {\n            var value = this.get(dim, i);\n\n            if (!isNaN(value)) {\n              sum += value;\n            }\n          }\n        }\n\n        return sum;\n      };\n      /**\n       * Get median of data in one dimension\n       */\n\n\n      DataStore.prototype.getMedian = function (dim) {\n        var dimDataArray = []; // map all data of one dimension\n\n        this.each([dim], function (val) {\n          if (!isNaN(val)) {\n            dimDataArray.push(val);\n          }\n        }); // TODO\n        // Use quick select?\n\n        var sortedDimDataArray = dimDataArray.sort(function (a, b) {\n          return a - b;\n        });\n        var len = this.count(); // calculate median\n\n        return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;\n      };\n      /**\n       * Retrieve the index with given raw data index.\n       */\n\n\n      DataStore.prototype.indexOfRawIndex = function (rawIndex) {\n        if (rawIndex >= this._rawCount || rawIndex < 0) {\n          return -1;\n        }\n\n        if (!this._indices) {\n          return rawIndex;\n        } // Indices are ascending\n\n\n        var indices = this._indices; // If rawIndex === dataIndex\n\n        var rawDataIndex = indices[rawIndex];\n\n        if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {\n          return rawIndex;\n        }\n\n        var left = 0;\n        var right = this._count - 1;\n\n        while (left <= right) {\n          var mid = (left + right) / 2 | 0;\n\n          if (indices[mid] < rawIndex) {\n            left = mid + 1;\n          } else if (indices[mid] > rawIndex) {\n            right = mid - 1;\n          } else {\n            return mid;\n          }\n        }\n\n        return -1;\n      };\n      /**\n       * Retrieve the index of nearest value.\n       * @param dim\n       * @param value\n       * @param [maxDistance=Infinity]\n       * @return If and only if multiple indices have\n       *         the same value, they are put to the result.\n       */\n\n\n      DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n        var chunks = this._chunks;\n        var dimData = chunks[dim];\n        var nearestIndices = [];\n\n        if (!dimData) {\n          return nearestIndices;\n        }\n\n        if (maxDistance == null) {\n          maxDistance = Infinity;\n        }\n\n        var minDist = Infinity;\n        var minDiff = -1;\n        var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/SeriesData.js`.\n\n        for (var i = 0, len = this.count(); i < len; i++) {\n          var dataIndex = this.getRawIndex(i);\n          var diff = value - dimData[dataIndex];\n          var dist = Math.abs(diff);\n\n          if (dist <= maxDistance) {\n            // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,\n            // we'd better not push both of them to `nearestIndices`, otherwise it is easy to\n            // get more than one item in `nearestIndices` (more specifically, in `tooltip`).\n            // So we chose the one that `diff >= 0` in this csae.\n            // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them\n            // should be push to `nearestIndices`.\n            if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) {\n              minDist = dist;\n              minDiff = diff;\n              nearestIndicesLen = 0;\n            }\n\n            if (diff === minDiff) {\n              nearestIndices[nearestIndicesLen++] = i;\n            }\n          }\n        }\n\n        nearestIndices.length = nearestIndicesLen;\n        return nearestIndices;\n      };\n\n      DataStore.prototype.getIndices = function () {\n        var newIndices;\n        var indices = this._indices;\n\n        if (indices) {\n          var Ctor = indices.constructor;\n          var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.\n\n          if (Ctor === Array) {\n            newIndices = new Ctor(thisCount);\n\n            for (var i = 0; i < thisCount; i++) {\n              newIndices[i] = indices[i];\n            }\n          } else {\n            newIndices = new Ctor(indices.buffer, 0, thisCount);\n          }\n        } else {\n          var Ctor = getIndicesCtor(this._rawCount);\n          newIndices = new Ctor(this.count());\n\n          for (var i = 0; i < newIndices.length; i++) {\n            newIndices[i] = i;\n          }\n        }\n\n        return newIndices;\n      };\n      /**\n       * Data filter.\n       */\n\n\n      DataStore.prototype.filter = function (dims, cb) {\n        if (!this._count) {\n          return this;\n        }\n\n        var newStore = this.clone();\n        var count = newStore.count();\n        var Ctor = getIndicesCtor(newStore._rawCount);\n        var newIndices = new Ctor(count);\n        var value = [];\n        var dimSize = dims.length;\n        var offset = 0;\n        var dim0 = dims[0];\n        var chunks = newStore._chunks;\n\n        for (var i = 0; i < count; i++) {\n          var keep = void 0;\n          var rawIdx = newStore.getRawIndex(i); // Simple optimization\n\n          if (dimSize === 0) {\n            keep = cb(i);\n          } else if (dimSize === 1) {\n            var val = chunks[dim0][rawIdx];\n            keep = cb(val, i);\n          } else {\n            var k = 0;\n\n            for (; k < dimSize; k++) {\n              value[k] = chunks[dims[k]][rawIdx];\n            }\n\n            value[k] = i;\n            keep = cb.apply(null, value);\n          }\n\n          if (keep) {\n            newIndices[offset++] = rawIdx;\n          }\n        } // Set indices after filtered.\n\n\n        if (offset < count) {\n          newStore._indices = newIndices;\n        }\n\n        newStore._count = offset; // Reset data extent\n\n        newStore._extent = [];\n\n        newStore._updateGetRawIdx();\n\n        return newStore;\n      };\n      /**\n       * Select data in range. (For optimization of filter)\n       * (Manually inline code, support 5 million data filtering in data zoom.)\n       */\n\n\n      DataStore.prototype.selectRange = function (range) {\n        var newStore = this.clone();\n        var len = newStore._count;\n\n        if (!len) {\n          return this;\n        }\n\n        var dims = keys(range);\n        var dimSize = dims.length;\n\n        if (!dimSize) {\n          return this;\n        }\n\n        var originalCount = newStore.count();\n        var Ctor = getIndicesCtor(newStore._rawCount);\n        var newIndices = new Ctor(originalCount);\n        var offset = 0;\n        var dim0 = dims[0];\n        var min = range[dim0][0];\n        var max = range[dim0][1];\n        var storeArr = newStore._chunks;\n        var quickFinished = false;\n\n        if (!newStore._indices) {\n          // Extreme optimization for common case. About 2x faster in chrome.\n          var idx = 0;\n\n          if (dimSize === 1) {\n            var dimStorage = storeArr[dims[0]];\n\n            for (var i = 0; i < len; i++) {\n              var val = dimStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty\n              // value indicates the line should be broken. But for the case like\n              // scatter plot, a data item with empty value will not be rendered,\n              // but the axis extent may be effected if some other dim of the data\n              // item has value. Fortunately it is not a significant negative effect.\n\n              if (val >= min && val <= max || isNaN(val)) {\n                newIndices[offset++] = idx;\n              }\n\n              idx++;\n            }\n\n            quickFinished = true;\n          } else if (dimSize === 2) {\n            var dimStorage = storeArr[dims[0]];\n            var dimStorage2 = storeArr[dims[1]];\n            var min2 = range[dims[1]][0];\n            var max2 = range[dims[1]][1];\n\n            for (var i = 0; i < len; i++) {\n              var val = dimStorage[i];\n              var val2 = dimStorage2[i]; // Do not filter NaN, see comment above.\n\n              if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {\n                newIndices[offset++] = idx;\n              }\n\n              idx++;\n            }\n\n            quickFinished = true;\n          }\n        }\n\n        if (!quickFinished) {\n          if (dimSize === 1) {\n            for (var i = 0; i < originalCount; i++) {\n              var rawIndex = newStore.getRawIndex(i);\n              var val = storeArr[dims[0]][rawIndex]; // Do not filter NaN, see comment above.\n\n              if (val >= min && val <= max || isNaN(val)) {\n                newIndices[offset++] = rawIndex;\n              }\n            }\n          } else {\n            for (var i = 0; i < originalCount; i++) {\n              var keep = true;\n              var rawIndex = newStore.getRawIndex(i);\n\n              for (var k = 0; k < dimSize; k++) {\n                var dimk = dims[k];\n                var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above.\n\n                if (val < range[dimk][0] || val > range[dimk][1]) {\n                  keep = false;\n                }\n              }\n\n              if (keep) {\n                newIndices[offset++] = newStore.getRawIndex(i);\n              }\n            }\n          }\n        } // Set indices after filtered.\n\n\n        if (offset < originalCount) {\n          newStore._indices = newIndices;\n        }\n\n        newStore._count = offset; // Reset data extent\n\n        newStore._extent = [];\n\n        newStore._updateGetRawIdx();\n\n        return newStore;\n      }; // /**\n      //  * Data mapping to a plain array\n      //  */\n      // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {\n      //     const result: any[] = [];\n      //     this.each(dims, function () {\n      //         result.push(cb && (cb as MapArrayCb).apply(null, arguments));\n      //     });\n      //     return result;\n      // }\n\n      /**\n       * Data mapping to a new List with given dimensions\n       */\n\n\n      DataStore.prototype.map = function (dims, cb) {\n        // TODO only clone picked chunks.\n        var target = this.clone(dims);\n\n        this._updateDims(target, dims, cb);\n\n        return target;\n      };\n      /**\n       * @caution Danger!! Only used in dataStack.\n       */\n\n\n      DataStore.prototype.modify = function (dims, cb) {\n        this._updateDims(this, dims, cb);\n      };\n\n      DataStore.prototype._updateDims = function (target, dims, cb) {\n        var targetChunks = target._chunks;\n        var tmpRetValue = [];\n        var dimSize = dims.length;\n        var dataCount = target.count();\n        var values = [];\n        var rawExtent = target._rawExtent;\n\n        for (var i = 0; i < dims.length; i++) {\n          rawExtent[dims[i]] = getInitialExtent();\n        }\n\n        for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {\n          var rawIndex = target.getRawIndex(dataIndex);\n\n          for (var k = 0; k < dimSize; k++) {\n            values[k] = targetChunks[dims[k]][rawIndex];\n          }\n\n          values[dimSize] = dataIndex;\n          var retValue = cb && cb.apply(null, values);\n\n          if (retValue != null) {\n            // a number or string (in oridinal dimension)?\n            if (typeof retValue !== 'object') {\n              tmpRetValue[0] = retValue;\n              retValue = tmpRetValue;\n            }\n\n            for (var i = 0; i < retValue.length; i++) {\n              var dim = dims[i];\n              var val = retValue[i];\n              var rawExtentOnDim = rawExtent[dim];\n              var dimStore = targetChunks[dim];\n\n              if (dimStore) {\n                dimStore[rawIndex] = val;\n              }\n\n              if (val < rawExtentOnDim[0]) {\n                rawExtentOnDim[0] = val;\n              }\n\n              if (val > rawExtentOnDim[1]) {\n                rawExtentOnDim[1] = val;\n              }\n            }\n          }\n        }\n      };\n      /**\n       * Large data down sampling using largest-triangle-three-buckets\n       * @param {string} valueDimension\n       * @param {number} targetCount\n       */\n\n\n      DataStore.prototype.lttbDownSample = function (valueDimension, rate) {\n        var target = this.clone([valueDimension], true);\n        var targetStorage = target._chunks;\n        var dimStore = targetStorage[valueDimension];\n        var len = this.count();\n        var sampledIndex = 0;\n        var frameSize = Math.floor(1 / rate);\n        var currentRawIndex = this.getRawIndex(0);\n        var maxArea;\n        var area;\n        var nextRawIndex;\n        var newIndices = new (getIndicesCtor(this._rawCount))(Math.min((Math.ceil(len / frameSize) + 2) * 2, len)); // First frame use the first data.\n\n        newIndices[sampledIndex++] = currentRawIndex;\n\n        for (var i = 1; i < len - 1; i += frameSize) {\n          var nextFrameStart = Math.min(i + frameSize, len - 1);\n          var nextFrameEnd = Math.min(i + frameSize * 2, len);\n          var avgX = (nextFrameEnd + nextFrameStart) / 2;\n          var avgY = 0;\n\n          for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {\n            var rawIndex = this.getRawIndex(idx);\n            var y = dimStore[rawIndex];\n\n            if (isNaN(y)) {\n              continue;\n            }\n\n            avgY += y;\n          }\n\n          avgY /= nextFrameEnd - nextFrameStart;\n          var frameStart = i;\n          var frameEnd = Math.min(i + frameSize, len);\n          var pointAX = i - 1;\n          var pointAY = dimStore[currentRawIndex];\n          maxArea = -1;\n          nextRawIndex = frameStart;\n          var firstNaNIndex = -1;\n          var countNaN = 0; // Find a point from current frame that construct a triangel with largest area with previous selected point\n          // And the average of next frame.\n\n          for (var idx = frameStart; idx < frameEnd; idx++) {\n            var rawIndex = this.getRawIndex(idx);\n            var y = dimStore[rawIndex];\n\n            if (isNaN(y)) {\n              countNaN++;\n\n              if (firstNaNIndex < 0) {\n                firstNaNIndex = rawIndex;\n              }\n\n              continue;\n            } // Calculate triangle area over three buckets\n\n\n            area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));\n\n            if (area > maxArea) {\n              maxArea = area;\n              nextRawIndex = rawIndex; // Next a is this b\n            }\n          }\n\n          if (countNaN > 0 && countNaN < frameEnd - frameStart) {\n            // Append first NaN point in every bucket.\n            // It is necessary to ensure the correct order of indices.\n            newIndices[sampledIndex++] = Math.min(firstNaNIndex, nextRawIndex);\n            nextRawIndex = Math.max(firstNaNIndex, nextRawIndex);\n          }\n\n          newIndices[sampledIndex++] = nextRawIndex;\n          currentRawIndex = nextRawIndex; // This a is the next a (chosen b)\n        } // First frame use the last data.\n\n\n        newIndices[sampledIndex++] = this.getRawIndex(len - 1);\n        target._count = sampledIndex;\n        target._indices = newIndices;\n        target.getRawIndex = this._getRawIdx;\n        return target;\n      };\n      /**\n       * Large data down sampling on given dimension\n       * @param sampleIndex Sample index for name and id\n       */\n\n\n      DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n        var target = this.clone([dimension], true);\n        var targetStorage = target._chunks;\n        var frameValues = [];\n        var frameSize = Math.floor(1 / rate);\n        var dimStore = targetStorage[dimension];\n        var len = this.count();\n        var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent();\n        var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize));\n        var offset = 0;\n\n        for (var i = 0; i < len; i += frameSize) {\n          // Last frame\n          if (frameSize > len - i) {\n            frameSize = len - i;\n            frameValues.length = frameSize;\n          }\n\n          for (var k = 0; k < frameSize; k++) {\n            var dataIdx = this.getRawIndex(i + k);\n            frameValues[k] = dimStore[dataIdx];\n          }\n\n          var value = sampleValue(frameValues);\n          var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data\n\n          dimStore[sampleFrameIdx] = value;\n\n          if (value < rawExtentOnDim[0]) {\n            rawExtentOnDim[0] = value;\n          }\n\n          if (value > rawExtentOnDim[1]) {\n            rawExtentOnDim[1] = value;\n          }\n\n          newIndices[offset++] = sampleFrameIdx;\n        }\n\n        target._count = offset;\n        target._indices = newIndices;\n\n        target._updateGetRawIdx();\n\n        return target;\n      };\n      /**\n       * Data iteration\n       * @param ctx default this\n       * @example\n       *  list.each('x', function (x, idx) {});\n       *  list.each(['x', 'y'], function (x, y, idx) {});\n       *  list.each(function (idx) {})\n       */\n\n\n      DataStore.prototype.each = function (dims, cb) {\n        if (!this._count) {\n          return;\n        }\n\n        var dimSize = dims.length;\n        var chunks = this._chunks;\n\n        for (var i = 0, len = this.count(); i < len; i++) {\n          var rawIdx = this.getRawIndex(i); // Simple optimization\n\n          switch (dimSize) {\n            case 0:\n              cb(i);\n              break;\n\n            case 1:\n              cb(chunks[dims[0]][rawIdx], i);\n              break;\n\n            case 2:\n              cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);\n              break;\n\n            default:\n              var k = 0;\n              var value = [];\n\n              for (; k < dimSize; k++) {\n                value[k] = chunks[dims[k]][rawIdx];\n              } // Index\n\n\n              value[k] = i;\n              cb.apply(null, value);\n          }\n        }\n      };\n      /**\n       * Get extent of data in one dimension\n       */\n\n\n      DataStore.prototype.getDataExtent = function (dim) {\n        // Make sure use concrete dim as cache name.\n        var dimData = this._chunks[dim];\n        var initialExtent = getInitialExtent();\n\n        if (!dimData) {\n          return initialExtent;\n        } // Make more strict checkings to ensure hitting cache.\n\n\n        var currEnd = this.count(); // Consider the most cases when using data zoom, `getDataExtent`\n        // happened before filtering. We cache raw extent, which is not\n        // necessary to be cleared and recalculated when restore data.\n\n        var useRaw = !this._indices;\n        var dimExtent;\n\n        if (useRaw) {\n          return this._rawExtent[dim].slice();\n        }\n\n        dimExtent = this._extent[dim];\n\n        if (dimExtent) {\n          return dimExtent.slice();\n        }\n\n        dimExtent = initialExtent;\n        var min = dimExtent[0];\n        var max = dimExtent[1];\n\n        for (var i = 0; i < currEnd; i++) {\n          var rawIdx = this.getRawIndex(i);\n          var value = dimData[rawIdx];\n          value < min && (min = value);\n          value > max && (max = value);\n        }\n\n        dimExtent = [min, max];\n        this._extent[dim] = dimExtent;\n        return dimExtent;\n      };\n      /**\n       * Get raw data item\n       */\n\n\n      DataStore.prototype.getRawDataItem = function (idx) {\n        var rawIdx = this.getRawIndex(idx);\n\n        if (!this._provider.persistent) {\n          var val = [];\n          var chunks = this._chunks;\n\n          for (var i = 0; i < chunks.length; i++) {\n            val.push(chunks[i][rawIdx]);\n          }\n\n          return val;\n        } else {\n          return this._provider.getItem(rawIdx);\n        }\n      };\n      /**\n       * Clone shallow.\n       *\n       * @param clonedDims Determine which dims to clone. Will share the data if not specified.\n       */\n\n\n      DataStore.prototype.clone = function (clonedDims, ignoreIndices) {\n        var target = new DataStore();\n        var chunks = this._chunks;\n        var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {\n          obj[dimIdx] = true;\n          return obj;\n        }, {});\n\n        if (clonedDimsMap) {\n          for (var i = 0; i < chunks.length; i++) {\n            // Not clone if dim is not picked.\n            target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);\n          }\n        } else {\n          target._chunks = chunks;\n        }\n\n        this._copyCommonProps(target);\n\n        if (!ignoreIndices) {\n          target._indices = this._cloneIndices();\n        }\n\n        target._updateGetRawIdx();\n\n        return target;\n      };\n\n      DataStore.prototype._copyCommonProps = function (target) {\n        target._count = this._count;\n        target._rawCount = this._rawCount;\n        target._provider = this._provider;\n        target._dimensions = this._dimensions;\n        target._extent = clone(this._extent);\n        target._rawExtent = clone(this._rawExtent);\n      };\n\n      DataStore.prototype._cloneIndices = function () {\n        if (this._indices) {\n          var Ctor = this._indices.constructor;\n          var indices = void 0;\n\n          if (Ctor === Array) {\n            var thisCount = this._indices.length;\n            indices = new Ctor(thisCount);\n\n            for (var i = 0; i < thisCount; i++) {\n              indices[i] = this._indices[i];\n            }\n          } else {\n            indices = new Ctor(this._indices);\n          }\n\n          return indices;\n        }\n\n        return null;\n      };\n\n      DataStore.prototype._getRawIdxIdentity = function (idx) {\n        return idx;\n      };\n\n      DataStore.prototype._getRawIdx = function (idx) {\n        if (idx < this._count && idx >= 0) {\n          return this._indices[idx];\n        }\n\n        return -1;\n      };\n\n      DataStore.prototype._updateGetRawIdx = function () {\n        this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;\n      };\n\n      DataStore.internalField = function () {\n        function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {\n          return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);\n        }\n\n        defaultDimValueGetters = {\n          arrayRows: getDimValueSimply,\n          objectRows: function (dataItem, property, dataIndex, dimIndex) {\n            return parseDataValue(dataItem[property], this._dimensions[dimIndex]);\n          },\n          keyedColumns: getDimValueSimply,\n          original: function (dataItem, property, dataIndex, dimIndex) {\n            // Performance sensitive, do not use modelUtil.getDataItemValue.\n            // If dataItem is an plain object with no value field, the let `value`\n            // will be assigned with the object, but it will be tread correctly\n            // in the `convertValue`.\n            var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);\n            return parseDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.\n            : value, this._dimensions[dimIndex]);\n          },\n          typedArray: function (dataItem, property, dataIndex, dimIndex) {\n            return dataItem[dimIndex];\n          }\n        };\n      }();\n\n      return DataStore;\n    }();\n\n    /**\n     * [REQUIREMENT_MEMO]:\n     * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option.\n     * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and\n     * `root-dataset`. Them on `series` has higher priority.\n     * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might\n     * confuse users: whether those props indicate how to visit the upstream source or visit\n     * the transform result source, and some transforms has nothing to do with these props,\n     * and some transforms might have multiple upstream.\n     * (3) Transforms should specify `metaRawOption` in each output, just like they can be\n     * declared in `root-dataset`.\n     * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms.\n     * That is for reducing complexity in transforms.\n     * PENDING: Whether to provide transposition transform?\n     *\n     * [IMPLEMENTAION_MEMO]:\n     * \"sourceVisitConfig\" are calculated from `metaRawOption` and `data`.\n     * They will not be calculated until `source` is about to be visited (to prevent from\n     * duplicate calcuation). `source` is visited only in series and input to transforms.\n     *\n     * [DIMENSION_INHERIT_RULE]:\n     * By default the dimensions are inherited from ancestors, unless a transform return\n     * a new dimensions definition.\n     * Consider the case:\n     * ```js\n     * dataset: [{\n     *     source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...]\n     * }, {\n     *     transform: { type: 'filter', ... }\n     * }]\n     * dataset: [{\n     *     dimension: ['Product', 'Sales', 'Prise'],\n     *     source: [ ['Cookies', 321, 44.21], ...]\n     * }, {\n     *     transform: { type: 'filter', ... }\n     * }]\n     * ```\n     * The two types of option should have the same behavior after transform.\n     *\n     *\n     * [SCENARIO]:\n     * (1) Provide source data directly:\n     * ```js\n     * series: {\n     *     encode: {...},\n     *     dimensions: [...]\n     *     seriesLayoutBy: 'row',\n     *     data: [[...]]\n     * }\n     * ```\n     * (2) Series refer to dataset.\n     * ```js\n     * series: [{\n     *     encode: {...}\n     *     // Ignore datasetIndex means `datasetIndex: 0`\n     *     // and the dimensions defination in dataset is used\n     * }, {\n     *     encode: {...},\n     *     seriesLayoutBy: 'column',\n     *     datasetIndex: 1\n     * }]\n     * ```\n     * (3) dataset transform\n     * ```js\n     * dataset: [{\n     *     source: [...]\n     * }, {\n     *     source: [...]\n     * }, {\n     *     // By default from 0.\n     *     transform: { type: 'filter', config: {...} }\n     * }, {\n     *     // Piped.\n     *     transform: [\n     *         { type: 'filter', config: {...} },\n     *         { type: 'sort', config: {...} }\n     *     ]\n     * }, {\n     *     id: 'regressionData',\n     *     fromDatasetIndex: 1,\n     *     // Third-party transform\n     *     transform: { type: 'ecStat:regression', config: {...} }\n     * }, {\n     *     // retrieve the extra result.\n     *     id: 'regressionFormula',\n     *     fromDatasetId: 'regressionData',\n     *     fromTransformResult: 1\n     * }]\n     * ```\n     */\n\n    var SourceManager =\n    /** @class */\n    function () {\n      function SourceManager(sourceHost) {\n        // Cached source. Do not repeat calculating if not dirty.\n        this._sourceList = [];\n        this._storeList = []; // version sign of each upstream source manager.\n\n        this._upstreamSignList = [];\n        this._versionSignBase = 0;\n        this._dirty = true;\n        this._sourceHost = sourceHost;\n      }\n      /**\n       * Mark dirty.\n       */\n\n\n      SourceManager.prototype.dirty = function () {\n        this._setLocalSource([], []);\n\n        this._storeList = [];\n        this._dirty = true;\n      };\n\n      SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {\n        this._sourceList = sourceList;\n        this._upstreamSignList = upstreamSignList;\n        this._versionSignBase++;\n\n        if (this._versionSignBase > 9e10) {\n          this._versionSignBase = 0;\n        }\n      };\n      /**\n       * For detecting whether the upstream source is dirty, so that\n       * the local cached source (in `_sourceList`) should be discarded.\n       */\n\n\n      SourceManager.prototype._getVersionSign = function () {\n        return this._sourceHost.uid + '_' + this._versionSignBase;\n      };\n      /**\n       * Always return a source instance. Otherwise throw error.\n       */\n\n\n      SourceManager.prototype.prepareSource = function () {\n        // For the case that call `setOption` multiple time but no data changed,\n        // cache the result source to prevent from repeating transform.\n        if (this._isDirty()) {\n          this._createSource();\n\n          this._dirty = false;\n        }\n      };\n\n      SourceManager.prototype._createSource = function () {\n        this._setLocalSource([], []);\n\n        var sourceHost = this._sourceHost;\n\n        var upSourceMgrList = this._getUpstreamSourceManagers();\n\n        var hasUpstream = !!upSourceMgrList.length;\n        var resultSourceList;\n        var upstreamSignList;\n\n        if (isSeries(sourceHost)) {\n          var seriesModel = sourceHost;\n          var data = void 0;\n          var sourceFormat = void 0;\n          var upSource = void 0; // Has upstream dataset\n\n          if (hasUpstream) {\n            var upSourceMgr = upSourceMgrList[0];\n            upSourceMgr.prepareSource();\n            upSource = upSourceMgr.getSource();\n            data = upSource.data;\n            sourceFormat = upSource.sourceFormat;\n            upstreamSignList = [upSourceMgr._getVersionSign()];\n          } // Series data is from own.\n          else {\n              data = seriesModel.get('data', true);\n              sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;\n              upstreamSignList = [];\n            } // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root.\n\n\n          var newMetaRawOption = this._getSourceMetaRawOption() || {};\n          var upMetaRawOption = upSource && upSource.metaRawOption || {};\n          var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null;\n          var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); // Note here we should not use `upSource.dimensionsDefine`. Consider the case:\n          // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`,\n          // but series need `seriesLayoutBy: 'row'`.\n\n          var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); // We share source with dataset as much as possible\n          // to avoid extra memory cost of high dimensional data.\n\n          var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions;\n          resultSourceList = needsCreateSource ? [createSource(data, {\n            seriesLayoutBy: seriesLayoutBy,\n            sourceHeader: sourceHeader,\n            dimensions: dimensions\n          }, sourceFormat)] : [];\n        } else {\n          var datasetModel = sourceHost; // Has upstream dataset.\n\n          if (hasUpstream) {\n            var result = this._applyTransform(upSourceMgrList);\n\n            resultSourceList = result.sourceList;\n            upstreamSignList = result.upstreamSignList;\n          } // Is root dataset.\n          else {\n              var sourceData = datasetModel.get('source', true);\n              resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)];\n              upstreamSignList = [];\n            }\n        }\n\n        if (\"development\" !== 'production') {\n          assert(resultSourceList && upstreamSignList);\n        }\n\n        this._setLocalSource(resultSourceList, upstreamSignList);\n      };\n\n      SourceManager.prototype._applyTransform = function (upMgrList) {\n        var datasetModel = this._sourceHost;\n        var transformOption = datasetModel.get('transform', true);\n        var fromTransformResult = datasetModel.get('fromTransformResult', true);\n\n        if (\"development\" !== 'production') {\n          assert(fromTransformResult != null || transformOption != null);\n        }\n\n        if (fromTransformResult != null) {\n          var errMsg = '';\n\n          if (upMgrList.length !== 1) {\n            if (\"development\" !== 'production') {\n              errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset';\n            }\n\n            doThrow(errMsg);\n          }\n        }\n\n        var sourceList;\n        var upSourceList = [];\n        var upstreamSignList = [];\n        each(upMgrList, function (upMgr) {\n          upMgr.prepareSource();\n          var upSource = upMgr.getSource(fromTransformResult || 0);\n          var errMsg = '';\n\n          if (fromTransformResult != null && !upSource) {\n            if (\"development\" !== 'production') {\n              errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult;\n            }\n\n            doThrow(errMsg);\n          }\n\n          upSourceList.push(upSource);\n          upstreamSignList.push(upMgr._getVersionSign());\n        });\n\n        if (transformOption) {\n          sourceList = applyDataTransform(transformOption, upSourceList, {\n            datasetIndex: datasetModel.componentIndex\n          });\n        } else if (fromTransformResult != null) {\n          sourceList = [cloneSourceShallow(upSourceList[0])];\n        }\n\n        return {\n          sourceList: sourceList,\n          upstreamSignList: upstreamSignList\n        };\n      };\n\n      SourceManager.prototype._isDirty = function () {\n        if (this._dirty) {\n          return true;\n        } // All sourceList is from the some upstream.\n\n\n        var upSourceMgrList = this._getUpstreamSourceManagers();\n\n        for (var i = 0; i < upSourceMgrList.length; i++) {\n          var upSrcMgr = upSourceMgrList[i];\n\n          if ( // Consider the case that there is ancestor diry, call it recursively.\n          // The performance is probably not an issue because usually the chain is not long.\n          upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {\n            return true;\n          }\n        }\n      };\n      /**\n       * @param sourceIndex By default 0, means \"main source\".\n       *                    In most cases there is only one source.\n       */\n\n\n      SourceManager.prototype.getSource = function (sourceIndex) {\n        sourceIndex = sourceIndex || 0;\n        var source = this._sourceList[sourceIndex];\n\n        if (!source) {\n          // Series may share source instance with dataset.\n          var upSourceMgrList = this._getUpstreamSourceManagers();\n\n          return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex);\n        }\n\n        return source;\n      };\n      /**\n       *\n       * Get a data store which can be shared across series.\n       * Only available for series.\n       *\n       * @param seriesDimRequest Dimensions that are generated in series.\n       *        Should have been sorted by `storeDimIndex` asc.\n       */\n\n\n      SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) {\n        if (\"development\" !== 'production') {\n          assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.');\n        }\n\n        var schema = seriesDimRequest.makeStoreSchema();\n        return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash);\n      };\n\n      SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) {\n        // TODO Can use other sourceIndex?\n        var sourceIndex = 0;\n        var storeList = this._storeList;\n        var cachedStoreMap = storeList[sourceIndex];\n\n        if (!cachedStoreMap) {\n          cachedStoreMap = storeList[sourceIndex] = {};\n        }\n\n        var cachedStore = cachedStoreMap[sourceReadKey];\n\n        if (!cachedStore) {\n          var upSourceMgr = this._getUpstreamSourceManagers()[0];\n\n          if (isSeries(this._sourceHost) && upSourceMgr) {\n            cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey);\n          } else {\n            cachedStore = new DataStore(); // Always create store from source of series.\n\n            cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims);\n          }\n\n          cachedStoreMap[sourceReadKey] = cachedStore;\n        }\n\n        return cachedStore;\n      };\n      /**\n       * PENDING: Is it fast enough?\n       * If no upstream, return empty array.\n       */\n\n\n      SourceManager.prototype._getUpstreamSourceManagers = function () {\n        // Always get the relationship from the raw option.\n        // Do not cache the link of the dependency graph, so that\n        // there is no need to update them when change happens.\n        var sourceHost = this._sourceHost;\n\n        if (isSeries(sourceHost)) {\n          var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);\n          return !datasetModel ? [] : [datasetModel.getSourceManager()];\n        } else {\n          return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {\n            return datasetModel.getSourceManager();\n          });\n        }\n      };\n\n      SourceManager.prototype._getSourceMetaRawOption = function () {\n        var sourceHost = this._sourceHost;\n        var seriesLayoutBy;\n        var sourceHeader;\n        var dimensions;\n\n        if (isSeries(sourceHost)) {\n          seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);\n          sourceHeader = sourceHost.get('sourceHeader', true);\n          dimensions = sourceHost.get('dimensions', true);\n        } // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them.\n        else if (!this._getUpstreamSourceManagers().length) {\n            var model = sourceHost;\n            seriesLayoutBy = model.get('seriesLayoutBy', true);\n            sourceHeader = model.get('sourceHeader', true);\n            dimensions = model.get('dimensions', true);\n          }\n\n        return {\n          seriesLayoutBy: seriesLayoutBy,\n          sourceHeader: sourceHeader,\n          dimensions: dimensions\n        };\n      };\n\n      return SourceManager;\n    }();\n    // disable the transform merge, but do not disable transform clone from rawOption.\n\n    function disableTransformOptionMerge(datasetModel) {\n      var transformOption = datasetModel.option.transform;\n      transformOption && setAsPrimitive(datasetModel.option.transform);\n    }\n\n    function isSeries(sourceHost) {\n      // Avoid circular dependency with Series.ts\n      return sourceHost.mainType === 'series';\n    }\n\n    function doThrow(errMsg) {\n      throw new Error(errMsg);\n    }\n\n    var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; // TODO: more textStyle option\n\n    function getTooltipTextStyle(textStyle, renderMode) {\n      var nameFontColor = textStyle.color || '#6e7079';\n      var nameFontSize = textStyle.fontSize || 12;\n      var nameFontWeight = textStyle.fontWeight || '400';\n      var valueFontColor = textStyle.color || '#464646';\n      var valueFontSize = textStyle.fontSize || 14;\n      var valueFontWeight = textStyle.fontWeight || '900';\n\n      if (renderMode === 'html') {\n        // `textStyle` is probably from user input, should be encoded to reduce security risk.\n        return {\n          // eslint-disable-next-line max-len\n          nameStyle: \"font-size:\" + encodeHTML(nameFontSize + '') + \"px;color:\" + encodeHTML(nameFontColor) + \";font-weight:\" + encodeHTML(nameFontWeight + ''),\n          // eslint-disable-next-line max-len\n          valueStyle: \"font-size:\" + encodeHTML(valueFontSize + '') + \"px;color:\" + encodeHTML(valueFontColor) + \";font-weight:\" + encodeHTML(valueFontWeight + '')\n        };\n      } else {\n        return {\n          nameStyle: {\n            fontSize: nameFontSize,\n            fill: nameFontColor,\n            fontWeight: nameFontWeight\n          },\n          valueStyle: {\n            fontSize: valueFontSize,\n            fill: valueFontColor,\n            fontWeight: valueFontWeight\n          }\n        };\n      }\n    } // See `TooltipMarkupLayoutIntent['innerGapLevel']`.\n    // (value from UI design)\n\n\n    var HTML_GAPS = [0, 10, 20, 30];\n    var RICH_TEXT_GAPS = ['', '\\n', '\\n\\n', '\\n\\n\\n']; // eslint-disable-next-line max-len\n\n    function createTooltipMarkup(type, option) {\n      option.type = type;\n      return option;\n    }\n\n    function isSectionFragment(frag) {\n      return frag.type === 'section';\n    }\n\n    function getBuilder(frag) {\n      return isSectionFragment(frag) ? buildSection : buildNameValue;\n    }\n\n    function getBlockGapLevel(frag) {\n      if (isSectionFragment(frag)) {\n        var gapLevel_1 = 0;\n        var subBlockLen = frag.blocks.length;\n        var hasInnerGap_1 = subBlockLen > 1 || subBlockLen > 0 && !frag.noHeader;\n        each(frag.blocks, function (subBlock) {\n          var subGapLevel = getBlockGapLevel(subBlock); // If the some of the sub-blocks have some gaps (like 10px) inside, this block\n          // should use a larger gap (like 20px) to distinguish those sub-blocks.\n\n          if (subGapLevel >= gapLevel_1) {\n            gapLevel_1 = subGapLevel + +(hasInnerGap_1 && ( // 0 always can not be readable gap level.\n            !subGapLevel // If no header, always keep the sub gap level. Otherwise\n            // look weird in case `multipleSeries`.\n            || isSectionFragment(subBlock) && !subBlock.noHeader));\n          }\n        });\n        return gapLevel_1;\n      }\n\n      return 0;\n    }\n\n    function buildSection(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {\n      var noHeader = fragment.noHeader;\n      var gaps = getGap(getBlockGapLevel(fragment));\n      var subMarkupTextList = [];\n      var subBlocks = fragment.blocks || [];\n      assert(!subBlocks || isArray(subBlocks));\n      subBlocks = subBlocks || [];\n      var orderMode = ctx.orderMode;\n\n      if (fragment.sortBlocks && orderMode) {\n        subBlocks = subBlocks.slice();\n        var orderMap = {\n          valueAsc: 'asc',\n          valueDesc: 'desc'\n        };\n\n        if (hasOwn(orderMap, orderMode)) {\n          var comparator_1 = new SortOrderComparator(orderMap[orderMode], null);\n          subBlocks.sort(function (a, b) {\n            return comparator_1.evaluate(a.sortParam, b.sortParam);\n          });\n        } // FIXME 'seriesDesc' necessary?\n        else if (orderMode === 'seriesDesc') {\n            subBlocks.reverse();\n          }\n      }\n\n      each(subBlocks, function (subBlock, idx) {\n        var valueFormatter = fragment.valueFormatter;\n        var subMarkupText = getBuilder(subBlock)( // Inherit valueFormatter\n        valueFormatter ? extend(extend({}, ctx), {\n          valueFormatter: valueFormatter\n        }) : ctx, subBlock, idx > 0 ? gaps.html : 0, toolTipTextStyle);\n        subMarkupText != null && subMarkupTextList.push(subMarkupText);\n      });\n      var subMarkupText = ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), noHeader ? topMarginForOuterGap : gaps.html);\n\n      if (noHeader) {\n        return subMarkupText;\n      }\n\n      var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC);\n      var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle;\n\n      if (ctx.renderMode === 'richText') {\n        return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText;\n      } else {\n        return wrapBlockHTML(\"<div style=\\\"\" + nameStyle + \";\" + TOOLTIP_LINE_HEIGHT_CSS + \";\\\">\" + encodeHTML(displayableHeader) + '</div>' + subMarkupText, topMarginForOuterGap);\n      }\n    }\n\n    function buildNameValue(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {\n      var renderMode = ctx.renderMode;\n      var noName = fragment.noName;\n      var noValue = fragment.noValue;\n      var noMarker = !fragment.markerType;\n      var name = fragment.name;\n      var useUTC = ctx.useUTC;\n\n      var valueFormatter = fragment.valueFormatter || ctx.valueFormatter || function (value) {\n        value = isArray(value) ? value : [value];\n        return map(value, function (val, idx) {\n          return makeValueReadable(val, isArray(valueTypeOption) ? valueTypeOption[idx] : valueTypeOption, useUTC);\n        });\n      };\n\n      if (noName && noValue) {\n        return;\n      }\n\n      var markerStr = noMarker ? '' : ctx.markupStyleCreator.makeTooltipMarker(fragment.markerType, fragment.markerColor || '#333', renderMode);\n      var readableName = noName ? '' : makeValueReadable(name, 'ordinal', useUTC);\n      var valueTypeOption = fragment.valueType;\n      var readableValueList = noValue ? [] : valueFormatter(fragment.value);\n      var valueAlignRight = !noMarker || !noName; // It little weird if only value next to marker but far from marker.\n\n      var valueCloseToMarker = !noMarker && noName;\n\n      var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),\n          nameStyle = _a.nameStyle,\n          valueStyle = _a.valueStyle;\n\n      return renderMode === 'richText' ? (noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameRichText(ctx, readableName, nameStyle)) // Value has commas inside, so use ' ' as delimiter for multiple values.\n      + (noValue ? '' : wrapInlineValueRichText(ctx, readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)) : wrapBlockHTML((noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameHTML(readableName, !noMarker, nameStyle)) + (noValue ? '' : wrapInlineValueHTML(readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)), topMarginForOuterGap);\n    }\n    /**\n     * @return markupText. null/undefined means no content.\n     */\n\n\n    function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {\n      if (!fragment) {\n        return;\n      }\n\n      var builder = getBuilder(fragment);\n      var ctx = {\n        useUTC: useUTC,\n        renderMode: renderMode,\n        orderMode: orderMode,\n        markupStyleCreator: markupStyleCreator,\n        valueFormatter: fragment.valueFormatter\n      };\n      return builder(ctx, fragment, 0, toolTipTextStyle);\n    }\n\n    function getGap(gapLevel) {\n      return {\n        html: HTML_GAPS[gapLevel],\n        richText: RICH_TEXT_GAPS[gapLevel]\n      };\n    }\n\n    function wrapBlockHTML(encodedContent, topGap) {\n      var clearfix = '<div style=\"clear:both\"></div>';\n      var marginCSS = \"margin: \" + topGap + \"px 0 0\";\n      return \"<div style=\\\"\" + marginCSS + \";\" + TOOLTIP_LINE_HEIGHT_CSS + \";\\\">\" + encodedContent + clearfix + '</div>';\n    }\n\n    function wrapInlineNameHTML(name, leftHasMarker, style) {\n      var marginCss = leftHasMarker ? 'margin-left:2px' : '';\n      return \"<span style=\\\"\" + style + \";\" + marginCss + \"\\\">\" + encodeHTML(name) + '</span>';\n    }\n\n    function wrapInlineValueHTML(valueList, alignRight, valueCloseToMarker, style) {\n      // Do not too close to marker, considering there are multiple values separated by spaces.\n      var paddingStr = valueCloseToMarker ? '10px' : '20px';\n      var alignCSS = alignRight ? \"float:right;margin-left:\" + paddingStr : '';\n      valueList = isArray(valueList) ? valueList : [valueList];\n      return \"<span style=\\\"\" + alignCSS + \";\" + style + \"\\\">\" // Value has commas inside, so use '  ' as delimiter for multiple values.\n      + map(valueList, function (value) {\n        return encodeHTML(value);\n      }).join('&nbsp;&nbsp;') + '</span>';\n    }\n\n    function wrapInlineNameRichText(ctx, name, style) {\n      return ctx.markupStyleCreator.wrapRichTextStyle(name, style);\n    }\n\n    function wrapInlineValueRichText(ctx, values, alignRight, valueCloseToMarker, style) {\n      var styles = [style];\n      var paddingLeft = valueCloseToMarker ? 10 : 20;\n      alignRight && styles.push({\n        padding: [0, 0, 0, paddingLeft],\n        align: 'right'\n      }); // Value has commas inside, so use '  ' as delimiter for multiple values.\n\n      return ctx.markupStyleCreator.wrapRichTextStyle(isArray(values) ? values.join('  ') : values, styles);\n    }\n\n    function retrieveVisualColorForTooltipMarker(series, dataIndex) {\n      var style = series.getData().getItemVisual(dataIndex, 'style');\n      var color = style[series.visualDrawType];\n      return convertToColorString(color);\n    }\n    function getPaddingFromTooltipModel(model, renderMode) {\n      var padding = model.get('padding');\n      return padding != null ? padding // We give slightly different to look pretty.\n      : renderMode === 'richText' ? [8, 10] : 10;\n    }\n    /**\n     * The major feature is generate styles for `renderMode: 'richText'`.\n     * But it also serves `renderMode: 'html'` to provide\n     * \"renderMode-independent\" API.\n     */\n\n    var TooltipMarkupStyleCreator =\n    /** @class */\n    function () {\n      function TooltipMarkupStyleCreator() {\n        this.richTextStyles = {}; // Notice that \"generate a style name\" usuall happens repeatly when mouse moving and\n        // displaying a tooltip. So we put the `_nextStyleNameId` as a member of each creator\n        // rather than static shared by all creators (which will cause it increase to fast).\n\n        this._nextStyleNameId = getRandomIdBase();\n      }\n\n      TooltipMarkupStyleCreator.prototype._generateStyleName = function () {\n        return '__EC_aUTo_' + this._nextStyleNameId++;\n      };\n\n      TooltipMarkupStyleCreator.prototype.makeTooltipMarker = function (markerType, colorStr, renderMode) {\n        var markerId = renderMode === 'richText' ? this._generateStyleName() : null;\n        var marker = getTooltipMarker({\n          color: colorStr,\n          type: markerType,\n          renderMode: renderMode,\n          markerId: markerId\n        });\n\n        if (isString(marker)) {\n          return marker;\n        } else {\n          if (\"development\" !== 'production') {\n            assert(markerId);\n          }\n\n          this.richTextStyles[markerId] = marker.style;\n          return marker.content;\n        }\n      };\n      /**\n       * @usage\n       * ```ts\n       * const styledText = markupStyleCreator.wrapRichTextStyle([\n       *     // The styles will be auto merged.\n       *     {\n       *         fontSize: 12,\n       *         color: 'blue'\n       *     },\n       *     {\n       *         padding: 20\n       *     }\n       * ]);\n       * ```\n       */\n\n\n      TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {\n        var finalStl = {};\n\n        if (isArray(styles)) {\n          each(styles, function (stl) {\n            return extend(finalStl, stl);\n          });\n        } else {\n          extend(finalStl, styles);\n        }\n\n        var styleName = this._generateStyleName();\n\n        this.richTextStyles[styleName] = finalStl;\n        return \"{\" + styleName + \"|\" + text + \"}\";\n      };\n\n      return TooltipMarkupStyleCreator;\n    }();\n\n    function defaultSeriesFormatTooltip(opt) {\n      var series = opt.series;\n      var dataIndex = opt.dataIndex;\n      var multipleSeries = opt.multipleSeries;\n      var data = series.getData();\n      var tooltipDims = data.mapDimensionsAll('defaultedTooltip');\n      var tooltipDimLen = tooltipDims.length;\n      var value = series.getRawValue(dataIndex);\n      var isValueArr = isArray(value);\n      var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip.\n\n      var inlineValue;\n      var inlineValueType;\n      var subBlocks;\n      var sortParam;\n\n      if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) {\n        var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);\n        inlineValue = formatArrResult.inlineValues;\n        inlineValueType = formatArrResult.inlineValueTypes;\n        subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases.\n\n        sortParam = formatArrResult.inlineValues[0];\n      } else if (tooltipDimLen) {\n        var dimInfo = data.getDimensionInfo(tooltipDims[0]);\n        sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]);\n        inlineValueType = dimInfo.type;\n      } else {\n        sortParam = inlineValue = isValueArr ? value[0] : value;\n      } // Do not show generated series name. It might not be readable.\n\n\n      var seriesNameSpecified = isNameSpecified(series);\n      var seriesName = seriesNameSpecified && series.name || '';\n      var itemName = data.getName(dataIndex);\n      var inlineName = multipleSeries ? seriesName : itemName;\n      return createTooltipMarkup('section', {\n        header: seriesName,\n        // When series name not specified, do not show a header line with only '-'.\n        // This case alway happen in tooltip.trigger: 'item'.\n        noHeader: multipleSeries || !seriesNameSpecified,\n        sortParam: sortParam,\n        blocks: [createTooltipMarkup('nameValue', {\n          markerType: 'item',\n          markerColor: markerColor,\n          // Do not mix display seriesName and itemName in one tooltip,\n          // which might confuses users.\n          name: inlineName,\n          // name dimension might be auto assigned, where the name might\n          // be not readable. So we check trim here.\n          noName: !trim(inlineName),\n          value: inlineValue,\n          valueType: inlineValueType\n        })].concat(subBlocks || [])\n      });\n    }\n\n    function formatTooltipArrayValue(value, series, dataIndex, tooltipDims, colorStr) {\n      // check: category-no-encode-has-axis-data in dataset.html\n      var data = series.getData();\n      var isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) {\n        var dimItem = data.getDimensionInfo(idx);\n        return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null;\n      }, false);\n      var inlineValues = [];\n      var inlineValueTypes = [];\n      var blocks = [];\n      tooltipDims.length ? each(tooltipDims, function (dim) {\n        setEachItem(retrieveRawValue(data, dataIndex, dim), dim);\n      }) // By default, all dims is used on tooltip.\n      : each(value, setEachItem);\n\n      function setEachItem(val, dim) {\n        var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip.\n\n        if (!dimInfo || dimInfo.otherDims.tooltip === false) {\n          return;\n        }\n\n        if (isValueMultipleLine) {\n          blocks.push(createTooltipMarkup('nameValue', {\n            markerType: 'subItem',\n            markerColor: colorStr,\n            name: dimInfo.displayName,\n            value: val,\n            valueType: dimInfo.type\n          }));\n        } else {\n          inlineValues.push(val);\n          inlineValueTypes.push(dimInfo.type);\n        }\n      }\n\n      return {\n        inlineValues: inlineValues,\n        inlineValueTypes: inlineValueTypes,\n        blocks: blocks\n      };\n    }\n\n    var inner$1 = makeInner();\n\n    function getSelectionKey(data, dataIndex) {\n      return data.getName(dataIndex) || data.getId(dataIndex);\n    }\n\n    var SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled';\n\n    var SeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(SeriesModel, _super);\n\n      function SeriesModel() {\n        // [Caution]: Because this class or desecendants can be used as `XXX.extend(subProto)`,\n        // the class members must not be initialized in constructor or declaration place.\n        // Otherwise there is bad case:\n        //   class A {xxx = 1;}\n        //   enableClassExtend(A);\n        //   class B extends A {}\n        //   var C = B.extend({xxx: 5});\n        //   var c = new C();\n        //   console.log(c.xxx); // expect 5 but always 1.\n        var _this = _super !== null && _super.apply(this, arguments) || this; // ---------------------------------------\n        // Props about data selection\n        // ---------------------------------------\n\n\n        _this._selectedDataIndicesMap = {};\n        return _this;\n      }\n\n      SeriesModel.prototype.init = function (option, parentModel, ecModel) {\n        this.seriesIndex = this.componentIndex;\n        this.dataTask = createTask({\n          count: dataTaskCount,\n          reset: dataTaskReset\n        });\n        this.dataTask.context = {\n          model: this\n        };\n        this.mergeDefaultAndTheme(option, ecModel);\n        var sourceManager = inner$1(this).sourceManager = new SourceManager(this);\n        sourceManager.prepareSource();\n        var data = this.getInitialData(option, ecModel);\n        wrapData(data, this);\n        this.dataTask.context.data = data;\n\n        if (\"development\" !== 'production') {\n          assert(data, 'getInitialData returned invalid data.');\n        }\n\n        inner$1(this).dataBeforeProcessed = data; // If we reverse the order (make data firstly, and then make\n        // dataBeforeProcessed by cloneShallow), cloneShallow will\n        // cause data.graph.data !== data when using\n        // module:echarts/data/Graph or module:echarts/data/Tree.\n        // See module:echarts/data/helper/linkSeriesData\n        // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model\n        // init or merge stage, because the data can be restored. So we do not `restoreData`\n        // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.\n        // Call `seriesModel.getRawData()` instead.\n        // this.restoreData();\n\n        autoSeriesName(this);\n\n        this._initSelectedMapFromData(data);\n      };\n      /**\n       * Util for merge default and theme to option\n       */\n\n\n      SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n        var layoutMode = fetchLayoutMode(this);\n        var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme.\n        // But if name duplicate between series subType\n        // (for example: parallel) add component mainType,\n        // add suffix 'Series'.\n\n        var themeSubType = this.subType;\n\n        if (ComponentModel.hasClass(themeSubType)) {\n          themeSubType += 'Series';\n        }\n\n        merge(option, ecModel.getTheme().get(this.subType));\n        merge(option, this.getDefaultOption()); // Default label emphasis `show`\n\n        defaultEmphasis(option, 'label', ['show']);\n        this.fillDataTextStyle(option.data);\n\n        if (layoutMode) {\n          mergeLayoutParam(option, inputPositionParams, layoutMode);\n        }\n      };\n\n      SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {\n        // this.settingTask.dirty();\n        newSeriesOption = merge(this.option, newSeriesOption, true);\n        this.fillDataTextStyle(newSeriesOption.data);\n        var layoutMode = fetchLayoutMode(this);\n\n        if (layoutMode) {\n          mergeLayoutParam(this.option, newSeriesOption, layoutMode);\n        }\n\n        var sourceManager = inner$1(this).sourceManager;\n        sourceManager.dirty();\n        sourceManager.prepareSource();\n        var data = this.getInitialData(newSeriesOption, ecModel);\n        wrapData(data, this);\n        this.dataTask.dirty();\n        this.dataTask.context.data = data;\n        inner$1(this).dataBeforeProcessed = data;\n        autoSeriesName(this);\n\n        this._initSelectedMapFromData(data);\n      };\n\n      SeriesModel.prototype.fillDataTextStyle = function (data) {\n        // Default data label emphasis `show`\n        // FIXME Tree structure data ?\n        // FIXME Performance ?\n        if (data && !isTypedArray(data)) {\n          var props = ['show'];\n\n          for (var i = 0; i < data.length; i++) {\n            if (data[i] && data[i].label) {\n              defaultEmphasis(data[i], 'label', props);\n            }\n          }\n        }\n      };\n      /**\n       * Init a data structure from data related option in series\n       * Must be overridden.\n       */\n\n\n      SeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return;\n      };\n      /**\n       * Append data to list\n       */\n\n\n      SeriesModel.prototype.appendData = function (params) {\n        // FIXME ???\n        // (1) If data from dataset, forbidden append.\n        // (2) support append data of dataset.\n        var data = this.getRawData();\n        data.appendData(params.data);\n      };\n      /**\n       * Consider some method like `filter`, `map` need make new data,\n       * We should make sure that `seriesModel.getData()` get correct\n       * data in the stream procedure. So we fetch data from upstream\n       * each time `task.perform` called.\n       */\n\n\n      SeriesModel.prototype.getData = function (dataType) {\n        var task = getCurrentTask(this);\n\n        if (task) {\n          var data = task.context.data;\n          return dataType == null ? data : data.getLinkedData(dataType);\n        } else {\n          // When series is not alive (that may happen when click toolbox\n          // restore or setOption with not merge mode), series data may\n          // be still need to judge animation or something when graphic\n          // elements want to know whether fade out.\n          return inner$1(this).data;\n        }\n      };\n\n      SeriesModel.prototype.getAllData = function () {\n        var mainData = this.getData();\n        return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{\n          data: mainData\n        }];\n      };\n\n      SeriesModel.prototype.setData = function (data) {\n        var task = getCurrentTask(this);\n\n        if (task) {\n          var context = task.context; // Consider case: filter, data sample.\n          // FIXME:TS never used, so comment it\n          // if (context.data !== data && task.modifyOutputEnd) {\n          //     task.setOutputEnd(data.count());\n          // }\n\n          context.outputData = data; // Caution: setData should update context.data,\n          // Because getData may be called multiply in a\n          // single stage and expect to get the data just\n          // set. (For example, AxisProxy, x y both call\n          // getData and setDate sequentially).\n          // So the context.data should be fetched from\n          // upstream each time when a stage starts to be\n          // performed.\n\n          if (task !== this.dataTask) {\n            context.data = data;\n          }\n        }\n\n        inner$1(this).data = data;\n      };\n\n      SeriesModel.prototype.getEncode = function () {\n        var encode = this.get('encode', true);\n\n        if (encode) {\n          return createHashMap(encode);\n        }\n      };\n\n      SeriesModel.prototype.getSourceManager = function () {\n        return inner$1(this).sourceManager;\n      };\n\n      SeriesModel.prototype.getSource = function () {\n        return this.getSourceManager().getSource();\n      };\n      /**\n       * Get data before processed\n       */\n\n\n      SeriesModel.prototype.getRawData = function () {\n        return inner$1(this).dataBeforeProcessed;\n      };\n\n      SeriesModel.prototype.getColorBy = function () {\n        var colorBy = this.get('colorBy');\n        return colorBy || 'series';\n      };\n\n      SeriesModel.prototype.isColorBySeries = function () {\n        return this.getColorBy() === 'series';\n      };\n      /**\n       * Get base axis if has coordinate system and has axis.\n       * By default use coordSys.getBaseAxis();\n       * Can be overridden for some chart.\n       * @return {type} description\n       */\n\n\n      SeriesModel.prototype.getBaseAxis = function () {\n        var coordSys = this.coordinateSystem; // @ts-ignore\n\n        return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();\n      };\n      /**\n       * Default tooltip formatter\n       *\n       * @param dataIndex\n       * @param multipleSeries\n       * @param dataType\n       * @param renderMode valid values: 'html'(by default) and 'richText'.\n       *        'html' is used for rendering tooltip in extra DOM form, and the result\n       *        string is used as DOM HTML content.\n       *        'richText' is used for rendering tooltip in rich text form, for those where\n       *        DOM operation is not supported.\n       * @return formatted tooltip with `html` and `markers`\n       *        Notice: The override method can also return string\n       */\n\n\n      SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        return defaultSeriesFormatTooltip({\n          series: this,\n          dataIndex: dataIndex,\n          multipleSeries: multipleSeries\n        });\n      };\n\n      SeriesModel.prototype.isAnimationEnabled = function () {\n        var ecModel = this.ecModel; // Disable animation if using echarts in node but not give ssr flag.\n        // In ssr mode, renderToString will generate svg with css animation.\n\n        if (env.node && !(ecModel && ecModel.ssr)) {\n          return false;\n        }\n\n        var animationEnabled = this.getShallow('animation');\n\n        if (animationEnabled) {\n          if (this.getData().count() > this.getShallow('animationThreshold')) {\n            animationEnabled = false;\n          }\n        }\n\n        return !!animationEnabled;\n      };\n\n      SeriesModel.prototype.restoreData = function () {\n        this.dataTask.dirty();\n      };\n\n      SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {\n        var ecModel = this.ecModel; // PENDING\n\n        var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);\n\n        if (!color) {\n          color = ecModel.getColorFromPalette(name, scope, requestColorNum);\n        }\n\n        return color;\n      };\n      /**\n       * Use `data.mapDimensionsAll(coordDim)` instead.\n       * @deprecated\n       */\n\n\n      SeriesModel.prototype.coordDimToDataDim = function (coordDim) {\n        return this.getRawData().mapDimensionsAll(coordDim);\n      };\n      /**\n       * Get progressive rendering count each step\n       */\n\n\n      SeriesModel.prototype.getProgressive = function () {\n        return this.get('progressive');\n      };\n      /**\n       * Get progressive rendering count each step\n       */\n\n\n      SeriesModel.prototype.getProgressiveThreshold = function () {\n        return this.get('progressiveThreshold');\n      }; // PENGING If selectedMode is null ?\n\n\n      SeriesModel.prototype.select = function (innerDataIndices, dataType) {\n        this._innerSelect(this.getData(dataType), innerDataIndices);\n      };\n\n      SeriesModel.prototype.unselect = function (innerDataIndices, dataType) {\n        var selectedMap = this.option.selectedMap;\n\n        if (!selectedMap) {\n          return;\n        }\n\n        var selectedMode = this.option.selectedMode;\n        var data = this.getData(dataType);\n\n        if (selectedMode === 'series' || selectedMap === 'all') {\n          this.option.selectedMap = {};\n          this._selectedDataIndicesMap = {};\n          return;\n        }\n\n        for (var i = 0; i < innerDataIndices.length; i++) {\n          var dataIndex = innerDataIndices[i];\n          var nameOrId = getSelectionKey(data, dataIndex);\n          selectedMap[nameOrId] = false;\n          this._selectedDataIndicesMap[nameOrId] = -1;\n        }\n      };\n\n      SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) {\n        var tmpArr = [];\n\n        for (var i = 0; i < innerDataIndices.length; i++) {\n          tmpArr[0] = innerDataIndices[i];\n          this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType);\n        }\n      };\n\n      SeriesModel.prototype.getSelectedDataIndices = function () {\n        if (this.option.selectedMap === 'all') {\n          return [].slice.call(this.getData().getIndices());\n        }\n\n        var selectedDataIndicesMap = this._selectedDataIndicesMap;\n        var nameOrIds = keys(selectedDataIndicesMap);\n        var dataIndices = [];\n\n        for (var i = 0; i < nameOrIds.length; i++) {\n          var dataIndex = selectedDataIndicesMap[nameOrIds[i]];\n\n          if (dataIndex >= 0) {\n            dataIndices.push(dataIndex);\n          }\n        }\n\n        return dataIndices;\n      };\n\n      SeriesModel.prototype.isSelected = function (dataIndex, dataType) {\n        var selectedMap = this.option.selectedMap;\n\n        if (!selectedMap) {\n          return false;\n        }\n\n        var data = this.getData(dataType);\n        return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) && !data.getItemModel(dataIndex).get(['select', 'disabled']);\n      };\n\n      SeriesModel.prototype.isUniversalTransitionEnabled = function () {\n        if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) {\n          return true;\n        }\n\n        var universalTransitionOpt = this.option.universalTransition; // Quick reject\n\n        if (!universalTransitionOpt) {\n          return false;\n        }\n\n        if (universalTransitionOpt === true) {\n          return true;\n        } // Can be simply 'universalTransition: true'\n\n\n        return universalTransitionOpt && universalTransitionOpt.enabled;\n      };\n\n      SeriesModel.prototype._innerSelect = function (data, innerDataIndices) {\n        var _a, _b;\n\n        var option = this.option;\n        var selectedMode = option.selectedMode;\n        var len = innerDataIndices.length;\n\n        if (!selectedMode || !len) {\n          return;\n        }\n\n        if (selectedMode === 'series') {\n          option.selectedMap = 'all';\n        } else if (selectedMode === 'multiple') {\n          if (!isObject(option.selectedMap)) {\n            option.selectedMap = {};\n          }\n\n          var selectedMap = option.selectedMap;\n\n          for (var i = 0; i < len; i++) {\n            var dataIndex = innerDataIndices[i]; // TODO different types of data share same object.\n\n            var nameOrId = getSelectionKey(data, dataIndex);\n            selectedMap[nameOrId] = true;\n            this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex);\n          }\n        } else if (selectedMode === 'single' || selectedMode === true) {\n          var lastDataIndex = innerDataIndices[len - 1];\n          var nameOrId = getSelectionKey(data, lastDataIndex);\n          option.selectedMap = (_a = {}, _a[nameOrId] = true, _a);\n          this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b);\n        }\n      };\n\n      SeriesModel.prototype._initSelectedMapFromData = function (data) {\n        // Ignore select info in data if selectedMap exists.\n        // NOTE It's only for legacy usage. edge data is not supported.\n        if (this.option.selectedMap) {\n          return;\n        }\n\n        var dataIndices = [];\n\n        if (data.hasItemOption) {\n          data.each(function (idx) {\n            var rawItem = data.getRawDataItem(idx);\n\n            if (rawItem && rawItem.selected) {\n              dataIndices.push(idx);\n            }\n          });\n        }\n\n        if (dataIndices.length > 0) {\n          this._innerSelect(data, dataIndices);\n        }\n      }; // /**\n      //  * @see {module:echarts/stream/Scheduler}\n      //  */\n      // abstract pipeTask: null\n\n\n      SeriesModel.registerClass = function (clz) {\n        return ComponentModel.registerClass(clz);\n      };\n\n      SeriesModel.protoInitialize = function () {\n        var proto = SeriesModel.prototype;\n        proto.type = 'series.__base__';\n        proto.seriesIndex = 0;\n        proto.ignoreStyleOnData = false;\n        proto.hasSymbolVisual = false;\n        proto.defaultSymbol = 'circle'; // Make sure the values can be accessed!\n\n        proto.visualStyleAccessPath = 'itemStyle';\n        proto.visualDrawType = 'fill';\n      }();\n\n      return SeriesModel;\n    }(ComponentModel);\n\n    mixin(SeriesModel, DataFormatMixin);\n    mixin(SeriesModel, PaletteMixin);\n    mountExtend(SeriesModel, ComponentModel);\n    /**\n     * MUST be called after `prepareSource` called\n     * Here we need to make auto series, especially for auto legend. But we\n     * do not modify series.name in option to avoid side effects.\n     */\n\n    function autoSeriesName(seriesModel) {\n      // User specified name has higher priority, otherwise it may cause\n      // series can not be queried unexpectedly.\n      var name = seriesModel.name;\n\n      if (!isNameSpecified(seriesModel)) {\n        seriesModel.name = getSeriesAutoName(seriesModel) || name;\n      }\n    }\n\n    function getSeriesAutoName(seriesModel) {\n      var data = seriesModel.getRawData();\n      var dataDims = data.mapDimensionsAll('seriesName');\n      var nameArr = [];\n      each(dataDims, function (dataDim) {\n        var dimInfo = data.getDimensionInfo(dataDim);\n        dimInfo.displayName && nameArr.push(dimInfo.displayName);\n      });\n      return nameArr.join(' ');\n    }\n\n    function dataTaskCount(context) {\n      return context.model.getRawData().count();\n    }\n\n    function dataTaskReset(context) {\n      var seriesModel = context.model;\n      seriesModel.setData(seriesModel.getRawData().cloneShallow());\n      return dataTaskProgress;\n    }\n\n    function dataTaskProgress(param, context) {\n      // Avoid repead cloneShallow when data just created in reset.\n      if (context.outputData && param.end > context.outputData.count()) {\n        context.model.getRawData().cloneShallow(context.outputData);\n      }\n    } // TODO refactor\n\n\n    function wrapData(data, seriesModel) {\n      each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {\n        data.wrapMethod(methodName, curry(onDataChange, seriesModel));\n      });\n    }\n\n    function onDataChange(seriesModel, newList) {\n      var task = getCurrentTask(seriesModel);\n\n      if (task) {\n        // Consider case: filter, selectRange\n        task.setOutputEnd((newList || this).count());\n      }\n\n      return newList;\n    }\n\n    function getCurrentTask(seriesModel) {\n      var scheduler = (seriesModel.ecModel || {}).scheduler;\n      var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);\n\n      if (pipeline) {\n        // When pipline finished, the currrentTask keep the last\n        // task (renderTask).\n        var task = pipeline.currentTask;\n\n        if (task) {\n          var agentStubMap = task.agentStubMap;\n\n          if (agentStubMap) {\n            task = agentStubMap.get(seriesModel.uid);\n          }\n        }\n\n        return task;\n      }\n    }\n\n    var ComponentView =\n    /** @class */\n    function () {\n      function ComponentView() {\n        this.group = new Group();\n        this.uid = getUID('viewComponent');\n      }\n\n      ComponentView.prototype.init = function (ecModel, api) {};\n\n      ComponentView.prototype.render = function (model, ecModel, api, payload) {};\n\n      ComponentView.prototype.dispose = function (ecModel, api) {};\n\n      ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;\n      };\n\n      ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing;\n      };\n\n      ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing;\n      };\n      /**\n       * Hook for toggle blur target series.\n       * Can be used in marker for blur or leave blur the markers\n       */\n\n\n      ComponentView.prototype.toggleBlurSeries = function (seriesModels, isBlur, ecModel) {// Do nothing;\n      };\n      /**\n       * Traverse the new rendered elements.\n       *\n       * It will traverse the new added element in progressive rendering.\n       * And traverse all in normal rendering.\n       */\n\n\n      ComponentView.prototype.eachRendered = function (cb) {\n        var group = this.group;\n\n        if (group) {\n          group.traverse(cb);\n        }\n      };\n\n      return ComponentView;\n    }();\n    enableClassExtend(ComponentView);\n    enableClassManagement(ComponentView);\n\n    /**\n     * @return {string} If large mode changed, return string 'reset';\n     */\n\n    function createRenderPlanner() {\n      var inner = makeInner();\n      return function (seriesModel) {\n        var fields = inner(seriesModel);\n        var pipelineContext = seriesModel.pipelineContext;\n        var originalLarge = !!fields.large;\n        var originalProgressive = !!fields.progressiveRender; // FIXME: if the planner works on a filtered series, `pipelineContext` does not\n        // exists. See #11611 . Probably we need to modify this structure, see the comment\n        // on `performRawSeries` in `Schedular.js`.\n\n        var large = fields.large = !!(pipelineContext && pipelineContext.large);\n        var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender);\n        return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset';\n      };\n    }\n\n    var inner$2 = makeInner();\n    var renderPlanner = createRenderPlanner();\n\n    var ChartView =\n    /** @class */\n    function () {\n      function ChartView() {\n        this.group = new Group();\n        this.uid = getUID('viewChart');\n        this.renderTask = createTask({\n          plan: renderTaskPlan,\n          reset: renderTaskReset\n        });\n        this.renderTask.context = {\n          view: this\n        };\n      }\n\n      ChartView.prototype.init = function (ecModel, api) {};\n\n      ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        if (\"development\" !== 'production') {\n          throw new Error('render method must been implemented');\n        }\n      };\n      /**\n       * Highlight series or specified data item.\n       */\n\n\n      ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData(payload && payload.dataType);\n\n        if (!data) {\n          if (\"development\" !== 'production') {\n            error(\"Unknown dataType \" + payload.dataType);\n          }\n\n          return;\n        }\n\n        toggleHighlight(data, payload, 'emphasis');\n      };\n      /**\n       * Downplay series or specified data item.\n       */\n\n\n      ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData(payload && payload.dataType);\n\n        if (!data) {\n          if (\"development\" !== 'production') {\n            error(\"Unknown dataType \" + payload.dataType);\n          }\n\n          return;\n        }\n\n        toggleHighlight(data, payload, 'normal');\n      };\n      /**\n       * Remove self.\n       */\n\n\n      ChartView.prototype.remove = function (ecModel, api) {\n        this.group.removeAll();\n      };\n      /**\n       * Dispose self.\n       */\n\n\n      ChartView.prototype.dispose = function (ecModel, api) {};\n\n      ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      }; // FIXME never used?\n\n\n      ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      }; // FIXME never used?\n\n\n      ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      };\n      /**\n       * Traverse the new rendered elements.\n       *\n       * It will traverse the new added element in progressive rendering.\n       * And traverse all in normal rendering.\n       */\n\n\n      ChartView.prototype.eachRendered = function (cb) {\n        traverseElements(this.group, cb);\n      };\n\n      ChartView.markUpdateMethod = function (payload, methodName) {\n        inner$2(payload).updateMethod = methodName;\n      };\n\n      ChartView.protoInitialize = function () {\n        var proto = ChartView.prototype;\n        proto.type = 'chart';\n      }();\n\n      return ChartView;\n    }();\n    /**\n     * Set state of single element\n     */\n\n    function elSetState(el, state, highlightDigit) {\n      if (el && isHighDownDispatcher(el)) {\n        (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit);\n      }\n    }\n\n    function toggleHighlight(data, payload, state) {\n      var dataIndex = queryDataIndex(data, payload);\n      var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null;\n\n      if (dataIndex != null) {\n        each(normalizeToArray(dataIndex), function (dataIdx) {\n          elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);\n        });\n      } else {\n        data.eachItemGraphicEl(function (el) {\n          elSetState(el, state, highlightDigit);\n        });\n      }\n    }\n\n    enableClassExtend(ChartView, ['dispose']);\n    enableClassManagement(ChartView);\n\n    function renderTaskPlan(context) {\n      return renderPlanner(context.model);\n    }\n\n    function renderTaskReset(context) {\n      var seriesModel = context.model;\n      var ecModel = context.ecModel;\n      var api = context.api;\n      var payload = context.payload; // FIXME: remove updateView updateVisual\n\n      var progressiveRender = seriesModel.pipelineContext.progressiveRender;\n      var view = context.view;\n      var updateMethod = payload && inner$2(payload).updateMethod;\n      var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount\n      // is less than progressive threshold.\n      : 'render';\n\n      if (methodName !== 'render') {\n        view[methodName](seriesModel, ecModel, api, payload);\n      }\n\n      return progressMethodMap[methodName];\n    }\n\n    var progressMethodMap = {\n      incrementalPrepareRender: {\n        progress: function (params, context) {\n          context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload);\n        }\n      },\n      render: {\n        // Put view.render in `progress` to support appendData. But in this case\n        // view.render should not be called in reset, otherwise it will be called\n        // twise. Use `forceFirstProgress` to make sure that view.render is called\n        // in any cases.\n        forceFirstProgress: true,\n        progress: function (params, context) {\n          context.view.render(context.model, context.ecModel, context.api, context.payload);\n        }\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var ORIGIN_METHOD = '\\0__throttleOriginMethod';\n    var RATE = '\\0__throttleRate';\n    var THROTTLE_TYPE = '\\0__throttleType';\n    /**\n     * @public\n     * @param {(Function)} fn\n     * @param {number} [delay=0] Unit: ms.\n     * @param {boolean} [debounce=false]\n     *        true: If call interval less than `delay`, only the last call works.\n     *        false: If call interval less than `delay, call works on fixed rate.\n     * @return {(Function)} throttled fn.\n     */\n\n    function throttle(fn, delay, debounce) {\n      var currCall;\n      var lastCall = 0;\n      var lastExec = 0;\n      var timer = null;\n      var diff;\n      var scope;\n      var args;\n      var debounceNextCall;\n      delay = delay || 0;\n\n      function exec() {\n        lastExec = new Date().getTime();\n        timer = null;\n        fn.apply(scope, args || []);\n      }\n\n      var cb = function () {\n        var cbArgs = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          cbArgs[_i] = arguments[_i];\n        }\n\n        currCall = new Date().getTime();\n        scope = this;\n        args = cbArgs;\n        var thisDelay = debounceNextCall || delay;\n        var thisDebounce = debounceNextCall || debounce;\n        debounceNextCall = null;\n        diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;\n        clearTimeout(timer); // Here we should make sure that: the `exec` SHOULD NOT be called later\n        // than a new call of `cb`, that is, preserving the command order. Consider\n        // calculating \"scale rate\" when roaming as an example. When a call of `cb`\n        // happens, either the `exec` is called dierectly, or the call is delayed.\n        // But the delayed call should never be later than next call of `cb`. Under\n        // this assurance, we can simply update view state each time `dispatchAction`\n        // triggered by user roaming, but not need to add extra code to avoid the\n        // state being \"rolled-back\".\n\n        if (thisDebounce) {\n          timer = setTimeout(exec, thisDelay);\n        } else {\n          if (diff >= 0) {\n            exec();\n          } else {\n            timer = setTimeout(exec, -diff);\n          }\n        }\n\n        lastCall = currCall;\n      };\n      /**\n       * Clear throttle.\n       * @public\n       */\n\n\n      cb.clear = function () {\n        if (timer) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      /**\n       * Enable debounce once.\n       */\n\n\n      cb.debounceNextCall = function (debounceDelay) {\n        debounceNextCall = debounceDelay;\n      };\n\n      return cb;\n    }\n    /**\n     * Create throttle method or update throttle rate.\n     *\n     * @example\n     * ComponentView.prototype.render = function () {\n     *     ...\n     *     throttle.createOrUpdate(\n     *         this,\n     *         '_dispatchAction',\n     *         this.model.get('throttle'),\n     *         'fixRate'\n     *     );\n     * };\n     * ComponentView.prototype.remove = function () {\n     *     throttle.clear(this, '_dispatchAction');\n     * };\n     * ComponentView.prototype.dispose = function () {\n     *     throttle.clear(this, '_dispatchAction');\n     * };\n     *\n     */\n\n    function createOrUpdate(obj, fnAttr, rate, throttleType) {\n      var fn = obj[fnAttr];\n\n      if (!fn) {\n        return;\n      }\n\n      var originFn = fn[ORIGIN_METHOD] || fn;\n      var lastThrottleType = fn[THROTTLE_TYPE];\n      var lastRate = fn[RATE];\n\n      if (lastRate !== rate || lastThrottleType !== throttleType) {\n        if (rate == null || !throttleType) {\n          return obj[fnAttr] = originFn;\n        }\n\n        fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce');\n        fn[ORIGIN_METHOD] = originFn;\n        fn[THROTTLE_TYPE] = throttleType;\n        fn[RATE] = rate;\n      }\n\n      return fn;\n    }\n    /**\n     * Clear throttle. Example see throttle.createOrUpdate.\n     */\n\n    function clear(obj, fnAttr) {\n      var fn = obj[fnAttr];\n\n      if (fn && fn[ORIGIN_METHOD]) {\n        // Clear throttle\n        fn.clear && fn.clear();\n        obj[fnAttr] = fn[ORIGIN_METHOD];\n      }\n    }\n\n    var inner$3 = makeInner();\n    var defaultStyleMappers = {\n      itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true),\n      lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true)\n    };\n    var defaultColorKey = {\n      lineStyle: 'stroke',\n      itemStyle: 'fill'\n    };\n\n    function getStyleMapper(seriesModel, stylePath) {\n      var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath];\n\n      if (!styleMapper) {\n        console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n        return defaultStyleMappers.itemStyle;\n      }\n\n      return styleMapper;\n    }\n\n    function getDefaultColorKey(seriesModel, stylePath) {\n      // return defaultColorKey[stylePath] ||\n      var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath];\n\n      if (!colorKey) {\n        console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n        return 'fill';\n      }\n\n      return colorKey;\n    }\n\n    var seriesStyleTask = {\n      createOnAllSeries: true,\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var data = seriesModel.getData();\n        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n        var styleModel = seriesModel.getModel(stylePath);\n        var getStyle = getStyleMapper(seriesModel, stylePath);\n        var globalStyle = getStyle(styleModel);\n        var decalOption = styleModel.getShallow('decal');\n\n        if (decalOption) {\n          data.setVisual('decal', decalOption);\n          decalOption.dirty = true;\n        } // TODO\n\n\n        var colorKey = getDefaultColorKey(seriesModel, stylePath);\n        var color = globalStyle[colorKey]; // TODO style callback\n\n        var colorCallback = isFunction(color) ? color : null;\n        var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; // Get from color palette by default.\n\n        if (!globalStyle[colorKey] || colorCallback || hasAutoColor) {\n          // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT\n          // make it effect palette. Because some scenarios users need to make some series\n          // transparent or as background, which should better not effect the palette.\n          var colorPalette = seriesModel.getColorFromPalette( // TODO series count changed.\n          seriesModel.name, null, ecModel.getSeriesCount());\n\n          if (!globalStyle[colorKey]) {\n            globalStyle[colorKey] = colorPalette;\n            data.setVisual('colorFromPalette', true);\n          }\n\n          globalStyle.fill = globalStyle.fill === 'auto' || isFunction(globalStyle.fill) ? colorPalette : globalStyle.fill;\n          globalStyle.stroke = globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke) ? colorPalette : globalStyle.stroke;\n        }\n\n        data.setVisual('style', globalStyle);\n        data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded\n\n        if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) {\n          data.setVisual('colorFromPalette', false);\n          return {\n            dataEach: function (data, idx) {\n              var dataParams = seriesModel.getDataParams(idx);\n              var itemStyle = extend({}, globalStyle);\n              itemStyle[colorKey] = colorCallback(dataParams);\n              data.setItemVisual(idx, 'style', itemStyle);\n            }\n          };\n        }\n      }\n    };\n    var sharedModel = new Model();\n    var dataStyleTask = {\n      createOnAllSeries: true,\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n        var getStyle = getStyleMapper(seriesModel, stylePath);\n        var colorKey = data.getVisual('drawType');\n        return {\n          dataEach: data.hasItemOption ? function (data, idx) {\n            // Not use getItemModel for performance considuration\n            var rawItem = data.getRawDataItem(idx);\n\n            if (rawItem && rawItem[stylePath]) {\n              sharedModel.option = rawItem[stylePath];\n              var style = getStyle(sharedModel);\n              var existsStyle = data.ensureUniqueItemVisual(idx, 'style');\n              extend(existsStyle, style);\n\n              if (sharedModel.option.decal) {\n                data.setItemVisual(idx, 'decal', sharedModel.option.decal);\n                sharedModel.option.decal.dirty = true;\n              }\n\n              if (colorKey in style) {\n                data.setItemVisual(idx, 'colorFromPalette', false);\n              }\n            }\n          } : null\n        };\n      }\n    }; // Pick color from palette for the data which has not been set with color yet.\n    // Note: do not support stream rendering. No such cases yet.\n\n    var dataColorPaletteTask = {\n      performRawSeries: true,\n      overallReset: function (ecModel) {\n        // Each type of series uses one scope.\n        // Pie and funnel are using different scopes.\n        var paletteScopeGroupByType = createHashMap();\n        ecModel.eachSeries(function (seriesModel) {\n          var colorBy = seriesModel.getColorBy();\n\n          if (seriesModel.isColorBySeries()) {\n            return;\n          }\n\n          var key = seriesModel.type + '-' + colorBy;\n          var colorScope = paletteScopeGroupByType.get(key);\n\n          if (!colorScope) {\n            colorScope = {};\n            paletteScopeGroupByType.set(key, colorScope);\n          }\n\n          inner$3(seriesModel).scope = colorScope;\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) {\n            return;\n          }\n\n          var dataAll = seriesModel.getRawData();\n          var idxMap = {};\n          var data = seriesModel.getData();\n          var colorScope = inner$3(seriesModel).scope;\n          var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';\n          var colorKey = getDefaultColorKey(seriesModel, stylePath);\n          data.each(function (idx) {\n            var rawIdx = data.getRawIndex(idx);\n            idxMap[rawIdx] = idx;\n          }); // Iterate on data before filtered. To make sure color from palette can be\n          // Consistent when toggling legend.\n\n          dataAll.each(function (rawIdx) {\n            var idx = idxMap[rawIdx];\n            var fromPalette = data.getItemVisual(idx, 'colorFromPalette'); // Get color from palette for each data only when the color is inherited from series color, which is\n            // also picked from color palette. So following situation is not in the case:\n            // 1. series.itemStyle.color is set\n            // 2. color is encoded by visualMap\n\n            if (fromPalette) {\n              var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n              var name_1 = dataAll.getName(rawIdx) || rawIdx + '';\n              var dataCount = dataAll.count();\n              itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount);\n            }\n          });\n        });\n      }\n    };\n\n    var PI$3 = Math.PI;\n    /**\n     * @param {module:echarts/ExtensionAPI} api\n     * @param {Object} [opts]\n     * @param {string} [opts.text]\n     * @param {string} [opts.color]\n     * @param {string} [opts.textColor]\n     * @return {module:zrender/Element}\n     */\n\n    function defaultLoading(api, opts) {\n      opts = opts || {};\n      defaults(opts, {\n        text: 'loading',\n        textColor: '#000',\n        fontSize: 12,\n        fontWeight: 'normal',\n        fontStyle: 'normal',\n        fontFamily: 'sans-serif',\n        maskColor: 'rgba(255, 255, 255, 0.8)',\n        showSpinner: true,\n        color: '#5470c6',\n        spinnerRadius: 10,\n        lineWidth: 5,\n        zlevel: 0\n      });\n      var group = new Group();\n      var mask = new Rect({\n        style: {\n          fill: opts.maskColor\n        },\n        zlevel: opts.zlevel,\n        z: 10000\n      });\n      group.add(mask);\n      var textContent = new ZRText({\n        style: {\n          text: opts.text,\n          fill: opts.textColor,\n          fontSize: opts.fontSize,\n          fontWeight: opts.fontWeight,\n          fontStyle: opts.fontStyle,\n          fontFamily: opts.fontFamily\n        },\n        zlevel: opts.zlevel,\n        z: 10001\n      });\n      var labelRect = new Rect({\n        style: {\n          fill: 'none'\n        },\n        textContent: textContent,\n        textConfig: {\n          position: 'right',\n          distance: 10\n        },\n        zlevel: opts.zlevel,\n        z: 10001\n      });\n      group.add(labelRect);\n      var arc;\n\n      if (opts.showSpinner) {\n        arc = new Arc({\n          shape: {\n            startAngle: -PI$3 / 2,\n            endAngle: -PI$3 / 2 + 0.1,\n            r: opts.spinnerRadius\n          },\n          style: {\n            stroke: opts.color,\n            lineCap: 'round',\n            lineWidth: opts.lineWidth\n          },\n          zlevel: opts.zlevel,\n          z: 10001\n        });\n        arc.animateShape(true).when(1000, {\n          endAngle: PI$3 * 3 / 2\n        }).start('circularInOut');\n        arc.animateShape(true).when(1000, {\n          startAngle: PI$3 * 3 / 2\n        }).delay(300).start('circularInOut');\n        group.add(arc);\n      } // Inject resize\n\n\n      group.resize = function () {\n        var textWidth = textContent.getBoundingRect().width;\n        var r = opts.showSpinner ? opts.spinnerRadius : 0; // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2\n        // textDistance needs to be calculated when both animation and text exist\n\n        var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) // only show the text\n        + (opts.showSpinner ? 0 : textWidth / 2) // only show the spinner\n        + (textWidth ? 0 : r);\n        var cy = api.getHeight() / 2;\n        opts.showSpinner && arc.setShape({\n          cx: cx,\n          cy: cy\n        });\n        labelRect.setShape({\n          x: cx - r,\n          y: cy - r,\n          width: r * 2,\n          height: r * 2\n        });\n        mask.setShape({\n          x: 0,\n          y: 0,\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n      };\n\n      group.resize();\n      return group;\n    }\n\n    var Scheduler =\n    /** @class */\n    function () {\n      function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {\n        // key: handlerUID\n        this._stageTaskMap = createHashMap();\n        this.ecInstance = ecInstance;\n        this.api = api; // Fix current processors in case that in some rear cases that\n        // processors might be registered after echarts instance created.\n        // Register processors incrementally for a echarts instance is\n        // not supported by this stream architecture.\n\n        dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();\n        visualHandlers = this._visualHandlers = visualHandlers.slice();\n        this._allHandlers = dataProcessorHandlers.concat(visualHandlers);\n      }\n\n      Scheduler.prototype.restoreData = function (ecModel, payload) {\n        // TODO: Only restore needed series and components, but not all components.\n        // Currently `restoreData` of all of the series and component will be called.\n        // But some independent components like `title`, `legend`, `graphic`, `toolbox`,\n        // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`,\n        // and some components like coordinate system, axes, dataZoom, visualMap only\n        // need their target series refresh.\n        // (1) If we are implementing this feature some day, we should consider these cases:\n        // if a data processor depends on a component (e.g., dataZoomProcessor depends\n        // on the settings of `dataZoom`), it should be re-performed if the component\n        // is modified by `setOption`.\n        // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`,\n        // it should be re-performed when the result array of `getTargetSeries` changed.\n        // We use `dependencies` to cover these issues.\n        // (3) How to update target series when coordinate system related components modified.\n        // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty,\n        // and this case all of the tasks will be set as dirty.\n        ecModel.restoreData(payload); // Theoretically an overall task not only depends on each of its target series, but also\n        // depends on all of the series.\n        // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks\n        // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure\n        // that the overall task is set as dirty and to be performed, otherwise it probably cause\n        // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it\n        // probably cause state chaos (consider `dataZoomProcessor`).\n\n        this._stageTaskMap.each(function (taskRecord) {\n          var overallTask = taskRecord.overallTask;\n          overallTask && overallTask.dirty();\n        });\n      }; // If seriesModel provided, incremental threshold is check by series data.\n\n\n      Scheduler.prototype.getPerformArgs = function (task, isBlock) {\n        // For overall task\n        if (!task.__pipeline) {\n          return;\n        }\n\n        var pipeline = this._pipelineMap.get(task.__pipeline.id);\n\n        var pCtx = pipeline.context;\n        var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex;\n        var step = incremental ? pipeline.step : null;\n        var modDataCount = pCtx && pCtx.modDataCount;\n        var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null;\n        return {\n          step: step,\n          modBy: modBy,\n          modDataCount: modDataCount\n        };\n      };\n\n      Scheduler.prototype.getPipeline = function (pipelineId) {\n        return this._pipelineMap.get(pipelineId);\n      };\n      /**\n       * Current, progressive rendering starts from visual and layout.\n       * Always detect render mode in the same stage, avoiding that incorrect\n       * detection caused by data filtering.\n       * Caution:\n       * `updateStreamModes` use `seriesModel.getData()`.\n       */\n\n\n      Scheduler.prototype.updateStreamModes = function (seriesModel, view) {\n        var pipeline = this._pipelineMap.get(seriesModel.uid);\n\n        var data = seriesModel.getData();\n        var dataLen = data.count(); // `progressiveRender` means that can render progressively in each\n        // animation frame. Note that some types of series do not provide\n        // `view.incrementalPrepareRender` but support `chart.appendData`. We\n        // use the term `incremental` but not `progressive` to describe the\n        // case that `chart.appendData`.\n\n        var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;\n        var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.\n        // see `test/candlestick-large3.html`\n\n        var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;\n        seriesModel.pipelineContext = pipeline.context = {\n          progressiveRender: progressiveRender,\n          modDataCount: modDataCount,\n          large: large\n        };\n      };\n\n      Scheduler.prototype.restorePipelines = function (ecModel) {\n        var scheduler = this;\n        var pipelineMap = scheduler._pipelineMap = createHashMap();\n        ecModel.eachSeries(function (seriesModel) {\n          var progressive = seriesModel.getProgressive();\n          var pipelineId = seriesModel.uid;\n          pipelineMap.set(pipelineId, {\n            id: pipelineId,\n            head: null,\n            tail: null,\n            threshold: seriesModel.getProgressiveThreshold(),\n            progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),\n            blockIndex: -1,\n            step: Math.round(progressive || 700),\n            count: 0\n          });\n\n          scheduler._pipe(seriesModel, seriesModel.dataTask);\n        });\n      };\n\n      Scheduler.prototype.prepareStageTasks = function () {\n        var stageTaskMap = this._stageTaskMap;\n        var ecModel = this.api.getModel();\n        var api = this.api;\n        each(this._allHandlers, function (handler) {\n          var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {});\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            // Currently do not need to support to sepecify them both.\n            errMsg = '\"reset\" and \"overallReset\" must not be both specified.';\n          }\n\n          assert(!(handler.reset && handler.overallReset), errMsg);\n          handler.reset && this._createSeriesStageTask(handler, record, ecModel, api);\n          handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api);\n        }, this);\n      };\n\n      Scheduler.prototype.prepareView = function (view, model, ecModel, api) {\n        var renderTask = view.renderTask;\n        var context = renderTask.context;\n        context.model = model;\n        context.ecModel = ecModel;\n        context.api = api;\n        renderTask.__block = !view.incrementalPrepareRender;\n\n        this._pipe(model, renderTask);\n      };\n\n      Scheduler.prototype.performDataProcessorTasks = function (ecModel, payload) {\n        // If we do not use `block` here, it should be considered when to update modes.\n        this._performStageTasks(this._dataProcessorHandlers, ecModel, payload, {\n          block: true\n        });\n      };\n\n      Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) {\n        this._performStageTasks(this._visualHandlers, ecModel, payload, opt);\n      };\n\n      Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) {\n        opt = opt || {};\n        var unfinished = false;\n        var scheduler = this;\n        each(stageHandlers, function (stageHandler, idx) {\n          if (opt.visualType && opt.visualType !== stageHandler.visualType) {\n            return;\n          }\n\n          var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);\n\n          var seriesTaskMap = stageHandlerRecord.seriesTaskMap;\n          var overallTask = stageHandlerRecord.overallTask;\n\n          if (overallTask) {\n            var overallNeedDirty_1;\n            var agentStubMap = overallTask.agentStubMap;\n            agentStubMap.each(function (stub) {\n              if (needSetDirty(opt, stub)) {\n                stub.dirty();\n                overallNeedDirty_1 = true;\n              }\n            });\n            overallNeedDirty_1 && overallTask.dirty();\n            scheduler.updatePayload(overallTask, payload);\n            var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block); // Execute stubs firstly, which may set the overall task dirty,\n            // then execute the overall task. And stub will call seriesModel.setData,\n            // which ensures that in the overallTask seriesModel.getData() will not\n            // return incorrect data.\n\n            agentStubMap.each(function (stub) {\n              stub.perform(performArgs_1);\n            });\n\n            if (overallTask.perform(performArgs_1)) {\n              unfinished = true;\n            }\n          } else if (seriesTaskMap) {\n            seriesTaskMap.each(function (task, pipelineId) {\n              if (needSetDirty(opt, task)) {\n                task.dirty();\n              }\n\n              var performArgs = scheduler.getPerformArgs(task, opt.block); // FIXME\n              // if intending to declare `performRawSeries` in handlers, only\n              // stream-independent (specifically, data item independent) operations can be\n              // performed. Because if a series is filtered, most of the tasks will not\n              // be performed. A stream-dependent operation probably cause wrong biz logic.\n              // Perhaps we should not provide a separate callback for this case instead\n              // of providing the config `performRawSeries`. The stream-dependent operations\n              // and stream-independent operations should better not be mixed.\n\n              performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);\n              scheduler.updatePayload(task, payload);\n\n              if (task.perform(performArgs)) {\n                unfinished = true;\n              }\n            });\n          }\n        });\n\n        function needSetDirty(opt, task) {\n          return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));\n        }\n\n        this.unfinished = unfinished || this.unfinished;\n      };\n\n      Scheduler.prototype.performSeriesTasks = function (ecModel) {\n        var unfinished;\n        ecModel.eachSeries(function (seriesModel) {\n          // Progress to the end for dataInit and dataRestore.\n          unfinished = seriesModel.dataTask.perform() || unfinished;\n        });\n        this.unfinished = unfinished || this.unfinished;\n      };\n\n      Scheduler.prototype.plan = function () {\n        // Travel pipelines, check block.\n        this._pipelineMap.each(function (pipeline) {\n          var task = pipeline.tail;\n\n          do {\n            if (task.__block) {\n              pipeline.blockIndex = task.__idxInPipeline;\n              break;\n            }\n\n            task = task.getUpstream();\n          } while (task);\n        });\n      };\n\n      Scheduler.prototype.updatePayload = function (task, payload) {\n        payload !== 'remain' && (task.context.payload = payload);\n      };\n\n      Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n        var scheduler = this;\n        var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; // The count of stages are totally about only several dozen, so\n        // do not need to reuse the map.\n\n        var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap();\n        var seriesType = stageHandler.seriesType;\n        var getTargetSeries = stageHandler.getTargetSeries; // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,\n        // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,\n        // it works but it may cause other irrelevant charts blocked.\n\n        if (stageHandler.createOnAllSeries) {\n          ecModel.eachRawSeries(create);\n        } else if (seriesType) {\n          ecModel.eachRawSeriesByType(seriesType, create);\n        } else if (getTargetSeries) {\n          getTargetSeries(ecModel, api).each(create);\n        }\n\n        function create(seriesModel) {\n          var pipelineId = seriesModel.uid; // Init tasks for each seriesModel only once.\n          // Reuse original task instance.\n\n          var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({\n            plan: seriesTaskPlan,\n            reset: seriesTaskReset,\n            count: seriesTaskCount\n          }));\n          task.context = {\n            model: seriesModel,\n            ecModel: ecModel,\n            api: api,\n            // PENDING: `useClearVisual` not used?\n            useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,\n            plan: stageHandler.plan,\n            reset: stageHandler.reset,\n            scheduler: scheduler\n          };\n\n          scheduler._pipe(seriesModel, task);\n        }\n      };\n\n      Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n        var scheduler = this;\n        var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage.\n        || createTask({\n          reset: overallTaskReset\n        });\n        overallTask.context = {\n          ecModel: ecModel,\n          api: api,\n          overallReset: stageHandler.overallReset,\n          scheduler: scheduler\n        };\n        var oldAgentStubMap = overallTask.agentStubMap; // The count of stages are totally about only several dozen, so\n        // do not need to reuse the map.\n\n        var newAgentStubMap = overallTask.agentStubMap = createHashMap();\n        var seriesType = stageHandler.seriesType;\n        var getTargetSeries = stageHandler.getTargetSeries;\n        var overallProgress = true;\n        var shouldOverallTaskDirty = false; // FIXME:TS never used, so comment it\n        // let modifyOutputEnd = stageHandler.modifyOutputEnd;\n        // An overall task with seriesType detected or has `getTargetSeries`, we add\n        // stub in each pipelines, it will set the overall task dirty when the pipeline\n        // progress. Moreover, to avoid call the overall task each frame (too frequent),\n        // we set the pipeline block.\n\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '\"createOnAllSeries\" is not supported for \"overallReset\", ' + 'because it will block all streams.';\n        }\n\n        assert(!stageHandler.createOnAllSeries, errMsg);\n\n        if (seriesType) {\n          ecModel.eachRawSeriesByType(seriesType, createStub);\n        } else if (getTargetSeries) {\n          getTargetSeries(ecModel, api).each(createStub);\n        } // Otherwise, (usually it is legacy case), the overall task will only be\n        // executed when upstream is dirty. Otherwise the progressive rendering of all\n        // pipelines will be disabled unexpectedly. But it still needs stubs to receive\n        // dirty info from upstream.\n        else {\n            overallProgress = false;\n            each(ecModel.getSeries(), createStub);\n          }\n\n        function createStub(seriesModel) {\n          var pipelineId = seriesModel.uid;\n          var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || ( // When the result of `getTargetSeries` changed, the overallTask\n          // should be set as dirty and re-performed.\n          shouldOverallTaskDirty = true, createTask({\n            reset: stubReset,\n            onDirty: stubOnDirty\n          })));\n          stub.context = {\n            model: seriesModel,\n            overallProgress: overallProgress // FIXME:TS never used, so comment it\n            // modifyOutputEnd: modifyOutputEnd\n\n          };\n          stub.agent = overallTask;\n          stub.__block = overallProgress;\n\n          scheduler._pipe(seriesModel, stub);\n        }\n\n        if (shouldOverallTaskDirty) {\n          overallTask.dirty();\n        }\n      };\n\n      Scheduler.prototype._pipe = function (seriesModel, task) {\n        var pipelineId = seriesModel.uid;\n\n        var pipeline = this._pipelineMap.get(pipelineId);\n\n        !pipeline.head && (pipeline.head = task);\n        pipeline.tail && pipeline.tail.pipe(task);\n        pipeline.tail = task;\n        task.__idxInPipeline = pipeline.count++;\n        task.__pipeline = pipeline;\n      };\n\n      Scheduler.wrapStageHandler = function (stageHandler, visualType) {\n        if (isFunction(stageHandler)) {\n          stageHandler = {\n            overallReset: stageHandler,\n            seriesType: detectSeriseType(stageHandler)\n          };\n        }\n\n        stageHandler.uid = getUID('stageHandler');\n        visualType && (stageHandler.visualType = visualType);\n        return stageHandler;\n      };\n      return Scheduler;\n    }();\n\n    function overallTaskReset(context) {\n      context.overallReset(context.ecModel, context.api, context.payload);\n    }\n\n    function stubReset(context) {\n      return context.overallProgress && stubProgress;\n    }\n\n    function stubProgress() {\n      this.agent.dirty();\n      this.getDownstream().dirty();\n    }\n\n    function stubOnDirty() {\n      this.agent && this.agent.dirty();\n    }\n\n    function seriesTaskPlan(context) {\n      return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null;\n    }\n\n    function seriesTaskReset(context) {\n      if (context.useClearVisual) {\n        context.data.clearAllVisual();\n      }\n\n      var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload));\n      return resetDefines.length > 1 ? map(resetDefines, function (v, idx) {\n        return makeSeriesTaskProgress(idx);\n      }) : singleSeriesTaskProgress;\n    }\n\n    var singleSeriesTaskProgress = makeSeriesTaskProgress(0);\n\n    function makeSeriesTaskProgress(resetDefineIdx) {\n      return function (params, context) {\n        var data = context.data;\n        var resetDefine = context.resetDefines[resetDefineIdx];\n\n        if (resetDefine && resetDefine.dataEach) {\n          for (var i = params.start; i < params.end; i++) {\n            resetDefine.dataEach(data, i);\n          }\n        } else if (resetDefine && resetDefine.progress) {\n          resetDefine.progress(params, data);\n        }\n      };\n    }\n\n    function seriesTaskCount(context) {\n      return context.data.count();\n    }\n    /**\n     * Only some legacy stage handlers (usually in echarts extensions) are pure function.\n     * To ensure that they can work normally, they should work in block mode, that is,\n     * they should not be started util the previous tasks finished. So they cause the\n     * progressive rendering disabled. We try to detect the series type, to narrow down\n     * the block range to only the series type they concern, but not all series.\n     */\n\n\n    function detectSeriseType(legacyFunc) {\n      seriesType = null;\n\n      try {\n        // Assume there is no async when calling `eachSeriesByType`.\n        legacyFunc(ecModelMock, apiMock);\n      } catch (e) {}\n\n      return seriesType;\n    }\n\n    var ecModelMock = {};\n    var apiMock = {};\n    var seriesType;\n    mockMethods(ecModelMock, GlobalModel);\n    mockMethods(apiMock, ExtensionAPI);\n\n    ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {\n      seriesType = type;\n    };\n\n    ecModelMock.eachComponent = function (cond) {\n      if (cond.mainType === 'series' && cond.subType) {\n        seriesType = cond.subType;\n      }\n    };\n\n    function mockMethods(target, Clz) {\n      /* eslint-disable */\n      for (var name_1 in Clz.prototype) {\n        // Do not use hasOwnProperty\n        target[name_1] = noop;\n      }\n      /* eslint-enable */\n\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];\n    var lightTheme = {\n      color: colorAll,\n      colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll]\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var contrastColor = '#B9B8CE';\n    var backgroundColor = '#100C2A';\n\n    var axisCommon = function () {\n      return {\n        axisLine: {\n          lineStyle: {\n            color: contrastColor\n          }\n        },\n        splitLine: {\n          lineStyle: {\n            color: '#484753'\n          }\n        },\n        splitArea: {\n          areaStyle: {\n            color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)']\n          }\n        },\n        minorSplitLine: {\n          lineStyle: {\n            color: '#20203B'\n          }\n        }\n      };\n    };\n\n    var colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff'];\n    var theme = {\n      darkMode: true,\n      color: colorPalette,\n      backgroundColor: backgroundColor,\n      axisPointer: {\n        lineStyle: {\n          color: '#817f91'\n        },\n        crossStyle: {\n          color: '#817f91'\n        },\n        label: {\n          // TODO Contrast of label backgorundColor\n          color: '#fff'\n        }\n      },\n      legend: {\n        textStyle: {\n          color: contrastColor\n        }\n      },\n      textStyle: {\n        color: contrastColor\n      },\n      title: {\n        textStyle: {\n          color: '#EEF1FA'\n        },\n        subtextStyle: {\n          color: '#B9B8CE'\n        }\n      },\n      toolbox: {\n        iconStyle: {\n          borderColor: contrastColor\n        }\n      },\n      dataZoom: {\n        borderColor: '#71708A',\n        textStyle: {\n          color: contrastColor\n        },\n        brushStyle: {\n          color: 'rgba(135,163,206,0.3)'\n        },\n        handleStyle: {\n          color: '#353450',\n          borderColor: '#C5CBE3'\n        },\n        moveHandleStyle: {\n          color: '#B0B6C3',\n          opacity: 0.3\n        },\n        fillerColor: 'rgba(135,163,206,0.2)',\n        emphasis: {\n          handleStyle: {\n            borderColor: '#91B7F2',\n            color: '#4D587D'\n          },\n          moveHandleStyle: {\n            color: '#636D9A',\n            opacity: 0.7\n          }\n        },\n        dataBackground: {\n          lineStyle: {\n            color: '#71708A',\n            width: 1\n          },\n          areaStyle: {\n            color: '#71708A'\n          }\n        },\n        selectedDataBackground: {\n          lineStyle: {\n            color: '#87A3CE'\n          },\n          areaStyle: {\n            color: '#87A3CE'\n          }\n        }\n      },\n      visualMap: {\n        textStyle: {\n          color: contrastColor\n        }\n      },\n      timeline: {\n        lineStyle: {\n          color: contrastColor\n        },\n        label: {\n          color: contrastColor\n        },\n        controlStyle: {\n          color: contrastColor,\n          borderColor: contrastColor\n        }\n      },\n      calendar: {\n        itemStyle: {\n          color: backgroundColor\n        },\n        dayLabel: {\n          color: contrastColor\n        },\n        monthLabel: {\n          color: contrastColor\n        },\n        yearLabel: {\n          color: contrastColor\n        }\n      },\n      timeAxis: axisCommon(),\n      logAxis: axisCommon(),\n      valueAxis: axisCommon(),\n      categoryAxis: axisCommon(),\n      line: {\n        symbol: 'circle'\n      },\n      graph: {\n        color: colorPalette\n      },\n      gauge: {\n        title: {\n          color: contrastColor\n        },\n        axisLine: {\n          lineStyle: {\n            color: [[1, 'rgba(207,212,219,0.2)']]\n          }\n        },\n        axisLabel: {\n          color: contrastColor\n        },\n        detail: {\n          color: '#EEF1FA'\n        }\n      },\n      candlestick: {\n        itemStyle: {\n          color: '#f64e56',\n          color0: '#54ea92',\n          borderColor: '#f64e56',\n          borderColor0: '#54ea92' // borderColor: '#ca2824',\n          // borderColor0: '#09a443'\n\n        }\n      }\n    };\n    theme.categoryAxis.splitLine.show = false;\n\n    /**\n     * Usage of query:\n     * `chart.on('click', query, handler);`\n     * The `query` can be:\n     * + The component type query string, only `mainType` or `mainType.subType`,\n     *   like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.\n     * + The component query object, like:\n     *   `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,\n     *   `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.\n     * + The data query object, like:\n     *   `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.\n     * + The other query object (cmponent customized query), like:\n     *   `{element: 'some'}` (only available in custom series).\n     *\n     * Caveat: If a prop in the `query` object is `null/undefined`, it is the\n     * same as there is no such prop in the `query` object.\n     */\n\n    var ECEventProcessor =\n    /** @class */\n    function () {\n      function ECEventProcessor() {}\n\n      ECEventProcessor.prototype.normalizeQuery = function (query) {\n        var cptQuery = {};\n        var dataQuery = {};\n        var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.\n\n        if (isString(query)) {\n          var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.\n\n          cptQuery.mainType = condCptType.main || null;\n          cptQuery.subType = condCptType.sub || null;\n        } // `query` is an object, convert to {mainType, index, name, id}.\n        else {\n            // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,\n            // can not be used in `compomentModel.filterForExposedEvent`.\n            var suffixes_1 = ['Index', 'Name', 'Id'];\n            var dataKeys_1 = {\n              name: 1,\n              dataIndex: 1,\n              dataType: 1\n            };\n            each(query, function (val, key) {\n              var reserved = false;\n\n              for (var i = 0; i < suffixes_1.length; i++) {\n                var propSuffix = suffixes_1[i];\n                var suffixPos = key.lastIndexOf(propSuffix);\n\n                if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {\n                  var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.\n\n                  if (mainType !== 'data') {\n                    cptQuery.mainType = mainType;\n                    cptQuery[propSuffix.toLowerCase()] = val;\n                    reserved = true;\n                  }\n                }\n              }\n\n              if (dataKeys_1.hasOwnProperty(key)) {\n                dataQuery[key] = val;\n                reserved = true;\n              }\n\n              if (!reserved) {\n                otherQuery[key] = val;\n              }\n            });\n          }\n\n        return {\n          cptQuery: cptQuery,\n          dataQuery: dataQuery,\n          otherQuery: otherQuery\n        };\n      };\n\n      ECEventProcessor.prototype.filter = function (eventType, query) {\n        // They should be assigned before each trigger call.\n        var eventInfo = this.eventInfo;\n\n        if (!eventInfo) {\n          return true;\n        }\n\n        var targetEl = eventInfo.targetEl;\n        var packedEvent = eventInfo.packedEvent;\n        var model = eventInfo.model;\n        var view = eventInfo.view; // For event like 'globalout'.\n\n        if (!model || !view) {\n          return true;\n        }\n\n        var cptQuery = query.cptQuery;\n        var dataQuery = query.dataQuery;\n        return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent));\n\n        function check(query, host, prop, propOnHost) {\n          return query[prop] == null || host[propOnHost || prop] === query[prop];\n        }\n      };\n\n      ECEventProcessor.prototype.afterTrigger = function () {\n        // Make sure the eventInfo won't be used in next trigger.\n        this.eventInfo = null;\n      };\n\n      return ECEventProcessor;\n    }();\n\n    var SYMBOL_PROPS_WITH_CB = ['symbol', 'symbolSize', 'symbolRotate', 'symbolOffset'];\n    var SYMBOL_PROPS = SYMBOL_PROPS_WITH_CB.concat(['symbolKeepAspect']); // Encoding visual for all series include which is filtered for legend drawing\n\n    var seriesSymbolTask = {\n      createOnAllSeries: true,\n      // For legend.\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var data = seriesModel.getData();\n\n        if (seriesModel.legendIcon) {\n          data.setVisual('legendIcon', seriesModel.legendIcon);\n        }\n\n        if (!seriesModel.hasSymbolVisual) {\n          return;\n        }\n\n        var symbolOptions = {};\n        var symbolOptionsCb = {};\n        var hasCallback = false;\n\n        for (var i = 0; i < SYMBOL_PROPS_WITH_CB.length; i++) {\n          var symbolPropName = SYMBOL_PROPS_WITH_CB[i];\n          var val = seriesModel.get(symbolPropName);\n\n          if (isFunction(val)) {\n            hasCallback = true;\n            symbolOptionsCb[symbolPropName] = val;\n          } else {\n            symbolOptions[symbolPropName] = val;\n          }\n        }\n\n        symbolOptions.symbol = symbolOptions.symbol || seriesModel.defaultSymbol;\n        data.setVisual(extend({\n          legendIcon: seriesModel.legendIcon || symbolOptions.symbol,\n          symbolKeepAspect: seriesModel.get('symbolKeepAspect')\n        }, symbolOptions)); // Only visible series has each data be visual encoded\n\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var symbolPropsCb = keys(symbolOptionsCb);\n\n        function dataEach(data, idx) {\n          var rawValue = seriesModel.getRawValue(idx);\n          var params = seriesModel.getDataParams(idx);\n\n          for (var i = 0; i < symbolPropsCb.length; i++) {\n            var symbolPropName = symbolPropsCb[i];\n            data.setItemVisual(idx, symbolPropName, symbolOptionsCb[symbolPropName](rawValue, params));\n          }\n        }\n\n        return {\n          dataEach: hasCallback ? dataEach : null\n        };\n      }\n    };\n    var dataSymbolTask = {\n      createOnAllSeries: true,\n      // For legend.\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        if (!seriesModel.hasSymbolVisual) {\n          return;\n        } // Only visible series has each data be visual encoded\n\n\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n\n        function dataEach(data, idx) {\n          var itemModel = data.getItemModel(idx);\n\n          for (var i = 0; i < SYMBOL_PROPS.length; i++) {\n            var symbolPropName = SYMBOL_PROPS[i];\n            var val = itemModel.getShallow(symbolPropName, true);\n\n            if (val != null) {\n              data.setItemVisual(idx, symbolPropName, val);\n            }\n          }\n        }\n\n        return {\n          dataEach: data.hasItemOption ? dataEach : null\n        };\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function getItemVisualFromData(data, dataIndex, key) {\n      switch (key) {\n        case 'color':\n          var style = data.getItemVisual(dataIndex, 'style');\n          return style[data.getVisual('drawType')];\n\n        case 'opacity':\n          return data.getItemVisual(dataIndex, 'style').opacity;\n\n        case 'symbol':\n        case 'symbolSize':\n        case 'liftZ':\n          return data.getItemVisual(dataIndex, key);\n\n        default:\n          if (\"development\" !== 'production') {\n            console.warn(\"Unknown visual type \" + key);\n          }\n\n      }\n    }\n    function getVisualFromData(data, key) {\n      switch (key) {\n        case 'color':\n          var style = data.getVisual('style');\n          return style[data.getVisual('drawType')];\n\n        case 'opacity':\n          return data.getVisual('style').opacity;\n\n        case 'symbol':\n        case 'symbolSize':\n        case 'liftZ':\n          return data.getVisual(key);\n\n        default:\n          if (\"development\" !== 'production') {\n            console.warn(\"Unknown visual type \" + key);\n          }\n\n      }\n    }\n\n    // Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect\n\n    function createLegacyDataSelectAction(seriesType, ecRegisterAction) {\n      function getSeriesIndices(ecModel, payload) {\n        var seriesIndices = [];\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: seriesType,\n          query: payload\n        }, function (seriesModel) {\n          seriesIndices.push(seriesModel.seriesIndex);\n        });\n        return seriesIndices;\n      }\n\n      each([[seriesType + 'ToggleSelect', 'toggleSelect'], [seriesType + 'Select', 'select'], [seriesType + 'UnSelect', 'unselect']], function (eventsMap) {\n        ecRegisterAction(eventsMap[0], function (payload, ecModel, api) {\n          payload = extend({}, payload);\n\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(payload.type, eventsMap[1]);\n          }\n\n          api.dispatchAction(extend(payload, {\n            type: eventsMap[1],\n            seriesIndex: getSeriesIndices(ecModel, payload)\n          }));\n        });\n      });\n    }\n\n    function handleSeriesLegacySelectEvents(type, eventPostfix, ecIns, ecModel, payload) {\n      var legacyEventName = type + eventPostfix;\n\n      if (!ecIns.isSilent(legacyEventName)) {\n        if (\"development\" !== 'production') {\n          deprecateLog(\"event \" + legacyEventName + \" is deprecated.\");\n        }\n\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'pie'\n        }, function (seriesModel) {\n          var seriesIndex = seriesModel.seriesIndex;\n          var selectedMap = seriesModel.option.selectedMap;\n          var selected = payload.selected;\n\n          for (var i = 0; i < selected.length; i++) {\n            if (selected[i].seriesIndex === seriesIndex) {\n              var data = seriesModel.getData();\n              var dataIndex = queryDataIndex(data, payload.fromActionPayload);\n              ecIns.trigger(legacyEventName, {\n                type: legacyEventName,\n                seriesId: seriesModel.id,\n                name: isArray(dataIndex) ? data.getName(dataIndex[0]) : data.getName(dataIndex),\n                selected: isString(selectedMap) ? selectedMap : extend({}, selectedMap)\n              });\n            }\n          }\n        });\n      }\n    }\n\n    function handleLegacySelectEvents(messageCenter, ecIns, api) {\n      messageCenter.on('selectchanged', function (params) {\n        var ecModel = api.getModel();\n\n        if (params.isFromClick) {\n          handleSeriesLegacySelectEvents('map', 'selectchanged', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'selectchanged', ecIns, ecModel, params);\n        } else if (params.fromAction === 'select') {\n          handleSeriesLegacySelectEvents('map', 'selected', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'selected', ecIns, ecModel, params);\n        } else if (params.fromAction === 'unselect') {\n          handleSeriesLegacySelectEvents('map', 'unselected', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'unselected', ecIns, ecModel, params);\n        }\n      });\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function findEventDispatcher(target, det, returnFirstMatch) {\n      var found;\n\n      while (target) {\n        if (det(target)) {\n          found = target;\n\n          if (returnFirstMatch) {\n            break;\n          }\n        }\n\n        target = target.__hostTarget || target.parent;\n      }\n\n      return found;\n    }\n\n    var wmUniqueIndex = Math.round(Math.random() * 9);\n    var supportDefineProperty = typeof Object.defineProperty === 'function';\n    var WeakMap = (function () {\n        function WeakMap() {\n            this._id = '__ec_inner_' + wmUniqueIndex++;\n        }\n        WeakMap.prototype.get = function (key) {\n            return this._guard(key)[this._id];\n        };\n        WeakMap.prototype.set = function (key, value) {\n            var target = this._guard(key);\n            if (supportDefineProperty) {\n                Object.defineProperty(target, this._id, {\n                    value: value,\n                    enumerable: false,\n                    configurable: true\n                });\n            }\n            else {\n                target[this._id] = value;\n            }\n            return this;\n        };\n        WeakMap.prototype[\"delete\"] = function (key) {\n            if (this.has(key)) {\n                delete this._guard(key)[this._id];\n                return true;\n            }\n            return false;\n        };\n        WeakMap.prototype.has = function (key) {\n            return !!this._guard(key)[this._id];\n        };\n        WeakMap.prototype._guard = function (key) {\n            if (key !== Object(key)) {\n                throw TypeError('Value of WeakMap is not a non-null object.');\n            }\n            return key;\n        };\n        return WeakMap;\n    }());\n\n    /**\n     * Triangle shape\n     * @inner\n     */\n\n    var Triangle = Path.extend({\n      type: 'triangle',\n      shape: {\n        cx: 0,\n        cy: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var width = shape.width / 2;\n        var height = shape.height / 2;\n        path.moveTo(cx, cy - height);\n        path.lineTo(cx + width, cy + height);\n        path.lineTo(cx - width, cy + height);\n        path.closePath();\n      }\n    });\n    /**\n     * Diamond shape\n     * @inner\n     */\n\n    var Diamond = Path.extend({\n      type: 'diamond',\n      shape: {\n        cx: 0,\n        cy: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var width = shape.width / 2;\n        var height = shape.height / 2;\n        path.moveTo(cx, cy - height);\n        path.lineTo(cx + width, cy);\n        path.lineTo(cx, cy + height);\n        path.lineTo(cx - width, cy);\n        path.closePath();\n      }\n    });\n    /**\n     * Pin shape\n     * @inner\n     */\n\n    var Pin = Path.extend({\n      type: 'pin',\n      shape: {\n        // x, y on the cusp\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var x = shape.x;\n        var y = shape.y;\n        var w = shape.width / 5 * 3; // Height must be larger than width\n\n        var h = Math.max(w, shape.height);\n        var r = w / 2; // Dist on y with tangent point and circle center\n\n        var dy = r * r / (h - r);\n        var cy = y - h + r + dy;\n        var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center\n\n        var dx = Math.cos(angle) * r;\n        var tanX = Math.sin(angle);\n        var tanY = Math.cos(angle);\n        var cpLen = r * 0.6;\n        var cpLen2 = r * 0.7;\n        path.moveTo(x - dx, cy + dy);\n        path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);\n        path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y);\n        path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy);\n        path.closePath();\n      }\n    });\n    /**\n     * Arrow shape\n     * @inner\n     */\n\n    var Arrow = Path.extend({\n      type: 'arrow',\n      shape: {\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (ctx, shape) {\n        var height = shape.height;\n        var width = shape.width;\n        var x = shape.x;\n        var y = shape.y;\n        var dx = width / 3 * 2;\n        ctx.moveTo(x, y);\n        ctx.lineTo(x + dx, y + height);\n        ctx.lineTo(x, y + height / 4 * 3);\n        ctx.lineTo(x - dx, y + height);\n        ctx.lineTo(x, y);\n        ctx.closePath();\n      }\n    });\n    /**\n     * Map of path constructors\n     */\n    // TODO Use function to build symbol path.\n\n    var symbolCtors = {\n      line: Line,\n      rect: Rect,\n      roundRect: Rect,\n      square: Rect,\n      circle: Circle,\n      diamond: Diamond,\n      pin: Pin,\n      arrow: Arrow,\n      triangle: Triangle\n    };\n    var symbolShapeMakers = {\n      line: function (x, y, w, h, shape) {\n        shape.x1 = x;\n        shape.y1 = y + h / 2;\n        shape.x2 = x + w;\n        shape.y2 = y + h / 2;\n      },\n      rect: function (x, y, w, h, shape) {\n        shape.x = x;\n        shape.y = y;\n        shape.width = w;\n        shape.height = h;\n      },\n      roundRect: function (x, y, w, h, shape) {\n        shape.x = x;\n        shape.y = y;\n        shape.width = w;\n        shape.height = h;\n        shape.r = Math.min(w, h) / 4;\n      },\n      square: function (x, y, w, h, shape) {\n        var size = Math.min(w, h);\n        shape.x = x;\n        shape.y = y;\n        shape.width = size;\n        shape.height = size;\n      },\n      circle: function (x, y, w, h, shape) {\n        // Put circle in the center of square\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.r = Math.min(w, h) / 2;\n      },\n      diamond: function (x, y, w, h, shape) {\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      pin: function (x, y, w, h, shape) {\n        shape.x = x + w / 2;\n        shape.y = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      arrow: function (x, y, w, h, shape) {\n        shape.x = x + w / 2;\n        shape.y = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      triangle: function (x, y, w, h, shape) {\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      }\n    };\n    var symbolBuildProxies = {};\n    each(symbolCtors, function (Ctor, name) {\n      symbolBuildProxies[name] = new Ctor();\n    });\n    var SymbolClz = Path.extend({\n      type: 'symbol',\n      shape: {\n        symbolType: '',\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      calculateTextPosition: function (out, config, rect) {\n        var res = calculateTextPosition(out, config, rect);\n        var shape = this.shape;\n\n        if (shape && shape.symbolType === 'pin' && config.position === 'inside') {\n          res.y = rect.y + rect.height * 0.4;\n        }\n\n        return res;\n      },\n      buildPath: function (ctx, shape, inBundle) {\n        var symbolType = shape.symbolType;\n\n        if (symbolType !== 'none') {\n          var proxySymbol = symbolBuildProxies[symbolType];\n\n          if (!proxySymbol) {\n            // Default rect\n            symbolType = 'rect';\n            proxySymbol = symbolBuildProxies[symbolType];\n          }\n\n          symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape);\n          proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);\n        }\n      }\n    }); // Provide setColor helper method to avoid determine if set the fill or stroke outside\n\n    function symbolPathSetColor(color, innerColor) {\n      if (this.type !== 'image') {\n        var symbolStyle = this.style;\n\n        if (this.__isEmptyBrush) {\n          symbolStyle.stroke = color;\n          symbolStyle.fill = innerColor || '#fff'; // TODO Same width with lineStyle in LineView\n\n          symbolStyle.lineWidth = 2;\n        } else if (this.shape.symbolType === 'line') {\n          symbolStyle.stroke = color;\n        } else {\n          symbolStyle.fill = color;\n        }\n\n        this.markRedraw();\n      }\n    }\n    /**\n     * Create a symbol element with given symbol configuration: shape, x, y, width, height, color\n     */\n\n\n    function createSymbol(symbolType, x, y, w, h, color, // whether to keep the ratio of w/h,\n    keepAspect) {\n      // TODO Support image object, DynamicImage.\n      var isEmpty = symbolType.indexOf('empty') === 0;\n\n      if (isEmpty) {\n        symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);\n      }\n\n      var symbolPath;\n\n      if (symbolType.indexOf('image://') === 0) {\n        symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n      } else if (symbolType.indexOf('path://') === 0) {\n        symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n      } else {\n        symbolPath = new SymbolClz({\n          shape: {\n            symbolType: symbolType,\n            x: x,\n            y: y,\n            width: w,\n            height: h\n          }\n        });\n      }\n\n      symbolPath.__isEmptyBrush = isEmpty; // TODO Should deprecate setColor\n\n      symbolPath.setColor = symbolPathSetColor;\n\n      if (color) {\n        symbolPath.setColor(color);\n      }\n\n      return symbolPath;\n    }\n    function normalizeSymbolSize(symbolSize) {\n      if (!isArray(symbolSize)) {\n        symbolSize = [+symbolSize, +symbolSize];\n      }\n\n      return [symbolSize[0] || 0, symbolSize[1] || 0];\n    }\n    function normalizeSymbolOffset(symbolOffset, symbolSize) {\n      if (symbolOffset == null) {\n        return;\n      }\n\n      if (!isArray(symbolOffset)) {\n        symbolOffset = [symbolOffset, symbolOffset];\n      }\n\n      return [parsePercent$1(symbolOffset[0], symbolSize[0]) || 0, parsePercent$1(retrieve2(symbolOffset[1], symbolOffset[0]), symbolSize[1]) || 0];\n    }\n\n    function isSafeNum(num) {\n        return isFinite(num);\n    }\n    function createLinearGradient(ctx, obj, rect) {\n        var x = obj.x == null ? 0 : obj.x;\n        var x2 = obj.x2 == null ? 1 : obj.x2;\n        var y = obj.y == null ? 0 : obj.y;\n        var y2 = obj.y2 == null ? 0 : obj.y2;\n        if (!obj.global) {\n            x = x * rect.width + rect.x;\n            x2 = x2 * rect.width + rect.x;\n            y = y * rect.height + rect.y;\n            y2 = y2 * rect.height + rect.y;\n        }\n        x = isSafeNum(x) ? x : 0;\n        x2 = isSafeNum(x2) ? x2 : 1;\n        y = isSafeNum(y) ? y : 0;\n        y2 = isSafeNum(y2) ? y2 : 0;\n        var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);\n        return canvasGradient;\n    }\n    function createRadialGradient(ctx, obj, rect) {\n        var width = rect.width;\n        var height = rect.height;\n        var min = Math.min(width, height);\n        var x = obj.x == null ? 0.5 : obj.x;\n        var y = obj.y == null ? 0.5 : obj.y;\n        var r = obj.r == null ? 0.5 : obj.r;\n        if (!obj.global) {\n            x = x * width + rect.x;\n            y = y * height + rect.y;\n            r = r * min;\n        }\n        x = isSafeNum(x) ? x : 0.5;\n        y = isSafeNum(y) ? y : 0.5;\n        r = r >= 0 && isSafeNum(r) ? r : 0.5;\n        var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);\n        return canvasGradient;\n    }\n    function getCanvasGradient(ctx, obj, rect) {\n        var canvasGradient = obj.type === 'radial'\n            ? createRadialGradient(ctx, obj, rect)\n            : createLinearGradient(ctx, obj, rect);\n        var colorStops = obj.colorStops;\n        for (var i = 0; i < colorStops.length; i++) {\n            canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color);\n        }\n        return canvasGradient;\n    }\n    function isClipPathChanged(clipPaths, prevClipPaths) {\n        if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) {\n            return false;\n        }\n        if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {\n            return true;\n        }\n        for (var i = 0; i < clipPaths.length; i++) {\n            if (clipPaths[i] !== prevClipPaths[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n    function parseInt10(val) {\n        return parseInt(val, 10);\n    }\n    function getSize(root, whIdx, opts) {\n        var wh = ['width', 'height'][whIdx];\n        var cwh = ['clientWidth', 'clientHeight'][whIdx];\n        var plt = ['paddingLeft', 'paddingTop'][whIdx];\n        var prb = ['paddingRight', 'paddingBottom'][whIdx];\n        if (opts[wh] != null && opts[wh] !== 'auto') {\n            return parseFloat(opts[wh]);\n        }\n        var stl = document.defaultView.getComputedStyle(root);\n        return ((root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))\n            - (parseInt10(stl[plt]) || 0)\n            - (parseInt10(stl[prb]) || 0)) | 0;\n    }\n\n    function normalizeLineDash(lineType, lineWidth) {\n        if (!lineType || lineType === 'solid' || !(lineWidth > 0)) {\n            return null;\n        }\n        return lineType === 'dashed'\n            ? [4 * lineWidth, 2 * lineWidth]\n            : lineType === 'dotted'\n                ? [lineWidth]\n                : isNumber(lineType)\n                    ? [lineType] : isArray(lineType) ? lineType : null;\n    }\n    function getLineDash(el) {\n        var style = el.style;\n        var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);\n        var lineDashOffset = style.lineDashOffset;\n        if (lineDash) {\n            var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;\n            if (lineScale_1 && lineScale_1 !== 1) {\n                lineDash = map(lineDash, function (rawVal) {\n                    return rawVal / lineScale_1;\n                });\n                lineDashOffset /= lineScale_1;\n            }\n        }\n        return [lineDash, lineDashOffset];\n    }\n\n    var pathProxyForDraw = new PathProxy(true);\n    function styleHasStroke(style) {\n        var stroke = style.stroke;\n        return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n    }\n    function isValidStrokeFillStyle(strokeOrFill) {\n        return typeof strokeOrFill === 'string' && strokeOrFill !== 'none';\n    }\n    function styleHasFill(style) {\n        var fill = style.fill;\n        return fill != null && fill !== 'none';\n    }\n    function doFillPath(ctx, style) {\n        if (style.fillOpacity != null && style.fillOpacity !== 1) {\n            var originalGlobalAlpha = ctx.globalAlpha;\n            ctx.globalAlpha = style.fillOpacity * style.opacity;\n            ctx.fill();\n            ctx.globalAlpha = originalGlobalAlpha;\n        }\n        else {\n            ctx.fill();\n        }\n    }\n    function doStrokePath(ctx, style) {\n        if (style.strokeOpacity != null && style.strokeOpacity !== 1) {\n            var originalGlobalAlpha = ctx.globalAlpha;\n            ctx.globalAlpha = style.strokeOpacity * style.opacity;\n            ctx.stroke();\n            ctx.globalAlpha = originalGlobalAlpha;\n        }\n        else {\n            ctx.stroke();\n        }\n    }\n    function createCanvasPattern(ctx, pattern, el) {\n        var image = createOrUpdateImage(pattern.image, pattern.__image, el);\n        if (isImageReady(image)) {\n            var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat');\n            if (typeof DOMMatrix === 'function'\n                && canvasPattern\n                && canvasPattern.setTransform) {\n                var matrix = new DOMMatrix();\n                matrix.translateSelf((pattern.x || 0), (pattern.y || 0));\n                matrix.rotateSelf(0, 0, (pattern.rotation || 0) * RADIAN_TO_DEGREE);\n                matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1));\n                canvasPattern.setTransform(matrix);\n            }\n            return canvasPattern;\n        }\n    }\n    function brushPath(ctx, el, style, inBatch) {\n        var _a;\n        var hasStroke = styleHasStroke(style);\n        var hasFill = styleHasFill(style);\n        var strokePercent = style.strokePercent;\n        var strokePart = strokePercent < 1;\n        var firstDraw = !el.path;\n        if ((!el.silent || strokePart) && firstDraw) {\n            el.createPathProxy();\n        }\n        var path = el.path || pathProxyForDraw;\n        var dirtyFlag = el.__dirty;\n        if (!inBatch) {\n            var fill = style.fill;\n            var stroke = style.stroke;\n            var hasFillGradient = hasFill && !!fill.colorStops;\n            var hasStrokeGradient = hasStroke && !!stroke.colorStops;\n            var hasFillPattern = hasFill && !!fill.image;\n            var hasStrokePattern = hasStroke && !!stroke.image;\n            var fillGradient = void 0;\n            var strokeGradient = void 0;\n            var fillPattern = void 0;\n            var strokePattern = void 0;\n            var rect = void 0;\n            if (hasFillGradient || hasStrokeGradient) {\n                rect = el.getBoundingRect();\n            }\n            if (hasFillGradient) {\n                fillGradient = dirtyFlag\n                    ? getCanvasGradient(ctx, fill, rect)\n                    : el.__canvasFillGradient;\n                el.__canvasFillGradient = fillGradient;\n            }\n            if (hasStrokeGradient) {\n                strokeGradient = dirtyFlag\n                    ? getCanvasGradient(ctx, stroke, rect)\n                    : el.__canvasStrokeGradient;\n                el.__canvasStrokeGradient = strokeGradient;\n            }\n            if (hasFillPattern) {\n                fillPattern = (dirtyFlag || !el.__canvasFillPattern)\n                    ? createCanvasPattern(ctx, fill, el)\n                    : el.__canvasFillPattern;\n                el.__canvasFillPattern = fillPattern;\n            }\n            if (hasStrokePattern) {\n                strokePattern = (dirtyFlag || !el.__canvasStrokePattern)\n                    ? createCanvasPattern(ctx, stroke, el)\n                    : el.__canvasStrokePattern;\n                el.__canvasStrokePattern = fillPattern;\n            }\n            if (hasFillGradient) {\n                ctx.fillStyle = fillGradient;\n            }\n            else if (hasFillPattern) {\n                if (fillPattern) {\n                    ctx.fillStyle = fillPattern;\n                }\n                else {\n                    hasFill = false;\n                }\n            }\n            if (hasStrokeGradient) {\n                ctx.strokeStyle = strokeGradient;\n            }\n            else if (hasStrokePattern) {\n                if (strokePattern) {\n                    ctx.strokeStyle = strokePattern;\n                }\n                else {\n                    hasStroke = false;\n                }\n            }\n        }\n        var scale = el.getGlobalScale();\n        path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold);\n        var lineDash;\n        var lineDashOffset;\n        if (ctx.setLineDash && style.lineDash) {\n            _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n        }\n        var needsRebuild = true;\n        if (firstDraw || (dirtyFlag & SHAPE_CHANGED_BIT)) {\n            path.setDPR(ctx.dpr);\n            if (strokePart) {\n                path.setContext(null);\n            }\n            else {\n                path.setContext(ctx);\n                needsRebuild = false;\n            }\n            path.reset();\n            el.buildPath(path, el.shape, inBatch);\n            path.toStatic();\n            el.pathUpdated();\n        }\n        if (needsRebuild) {\n            path.rebuildPath(ctx, strokePart ? strokePercent : 1);\n        }\n        if (lineDash) {\n            ctx.setLineDash(lineDash);\n            ctx.lineDashOffset = lineDashOffset;\n        }\n        if (!inBatch) {\n            if (style.strokeFirst) {\n                if (hasStroke) {\n                    doStrokePath(ctx, style);\n                }\n                if (hasFill) {\n                    doFillPath(ctx, style);\n                }\n            }\n            else {\n                if (hasFill) {\n                    doFillPath(ctx, style);\n                }\n                if (hasStroke) {\n                    doStrokePath(ctx, style);\n                }\n            }\n        }\n        if (lineDash) {\n            ctx.setLineDash([]);\n        }\n    }\n    function brushImage(ctx, el, style) {\n        var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload);\n        if (!image || !isImageReady(image)) {\n            return;\n        }\n        var x = style.x || 0;\n        var y = style.y || 0;\n        var width = el.getWidth();\n        var height = el.getHeight();\n        var aspect = image.width / image.height;\n        if (width == null && height != null) {\n            width = height * aspect;\n        }\n        else if (height == null && width != null) {\n            height = width / aspect;\n        }\n        else if (width == null && height == null) {\n            width = image.width;\n            height = image.height;\n        }\n        if (style.sWidth && style.sHeight) {\n            var sx = style.sx || 0;\n            var sy = style.sy || 0;\n            ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height);\n        }\n        else if (style.sx && style.sy) {\n            var sx = style.sx;\n            var sy = style.sy;\n            var sWidth = width - sx;\n            var sHeight = height - sy;\n            ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height);\n        }\n        else {\n            ctx.drawImage(image, x, y, width, height);\n        }\n    }\n    function brushText(ctx, el, style) {\n        var _a;\n        var text = style.text;\n        text != null && (text += '');\n        if (text) {\n            ctx.font = style.font || DEFAULT_FONT;\n            ctx.textAlign = style.textAlign;\n            ctx.textBaseline = style.textBaseline;\n            var lineDash = void 0;\n            var lineDashOffset = void 0;\n            if (ctx.setLineDash && style.lineDash) {\n                _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n            }\n            if (lineDash) {\n                ctx.setLineDash(lineDash);\n                ctx.lineDashOffset = lineDashOffset;\n            }\n            if (style.strokeFirst) {\n                if (styleHasStroke(style)) {\n                    ctx.strokeText(text, style.x, style.y);\n                }\n                if (styleHasFill(style)) {\n                    ctx.fillText(text, style.x, style.y);\n                }\n            }\n            else {\n                if (styleHasFill(style)) {\n                    ctx.fillText(text, style.x, style.y);\n                }\n                if (styleHasStroke(style)) {\n                    ctx.strokeText(text, style.x, style.y);\n                }\n            }\n            if (lineDash) {\n                ctx.setLineDash([]);\n            }\n        }\n    }\n    var SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\n    var STROKE_PROPS = [\n        ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]\n    ];\n    function bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) {\n        var styleChanged = false;\n        if (!forceSetAll) {\n            prevStyle = prevStyle || {};\n            if (style === prevStyle) {\n                return false;\n            }\n        }\n        if (forceSetAll || style.opacity !== prevStyle.opacity) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n            var opacity = Math.max(Math.min(style.opacity, 1), 0);\n            ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity;\n        }\n        if (forceSetAll || style.blend !== prevStyle.blend) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend;\n        }\n        for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) {\n            var propName = SHADOW_NUMBER_PROPS[i];\n            if (forceSetAll || style[propName] !== prevStyle[propName]) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx[propName] = ctx.dpr * (style[propName] || 0);\n            }\n        }\n        if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor;\n        }\n        return styleChanged;\n    }\n    function bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) {\n        var style = getStyle(el, scope.inHover);\n        var prevStyle = forceSetAll\n            ? null\n            : (prevEl && getStyle(prevEl, scope.inHover) || {});\n        if (style === prevStyle) {\n            return false;\n        }\n        var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope);\n        if (forceSetAll || style.fill !== prevStyle.fill) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill);\n        }\n        if (forceSetAll || style.stroke !== prevStyle.stroke) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke);\n        }\n        if (forceSetAll || style.opacity !== prevStyle.opacity) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;\n        }\n        if (el.hasStroke()) {\n            var lineWidth = style.lineWidth;\n            var newLineWidth = lineWidth / ((style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1);\n            if (ctx.lineWidth !== newLineWidth) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx.lineWidth = newLineWidth;\n            }\n        }\n        for (var i = 0; i < STROKE_PROPS.length; i++) {\n            var prop = STROKE_PROPS[i];\n            var propName = prop[0];\n            if (forceSetAll || style[propName] !== prevStyle[propName]) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx[propName] = style[propName] || prop[1];\n            }\n        }\n        return styleChanged;\n    }\n    function bindImageStyle(ctx, el, prevEl, forceSetAll, scope) {\n        return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope);\n    }\n    function setContextTransform(ctx, el) {\n        var m = el.transform;\n        var dpr = ctx.dpr || 1;\n        if (m) {\n            ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);\n        }\n        else {\n            ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n        }\n    }\n    function updateClipStatus(clipPaths, ctx, scope) {\n        var allClipped = false;\n        for (var i = 0; i < clipPaths.length; i++) {\n            var clipPath = clipPaths[i];\n            allClipped = allClipped || clipPath.isZeroArea();\n            setContextTransform(ctx, clipPath);\n            ctx.beginPath();\n            clipPath.buildPath(ctx, clipPath.shape);\n            ctx.clip();\n        }\n        scope.allClipped = allClipped;\n    }\n    function isTransformChanged(m0, m1) {\n        if (m0 && m1) {\n            return m0[0] !== m1[0]\n                || m0[1] !== m1[1]\n                || m0[2] !== m1[2]\n                || m0[3] !== m1[3]\n                || m0[4] !== m1[4]\n                || m0[5] !== m1[5];\n        }\n        else if (!m0 && !m1) {\n            return false;\n        }\n        return true;\n    }\n    var DRAW_TYPE_PATH = 1;\n    var DRAW_TYPE_IMAGE = 2;\n    var DRAW_TYPE_TEXT = 3;\n    var DRAW_TYPE_INCREMENTAL = 4;\n    function canPathBatch(style) {\n        var hasFill = styleHasFill(style);\n        var hasStroke = styleHasStroke(style);\n        return !(style.lineDash\n            || !(+hasFill ^ +hasStroke)\n            || (hasFill && typeof style.fill !== 'string')\n            || (hasStroke && typeof style.stroke !== 'string')\n            || style.strokePercent < 1\n            || style.strokeOpacity < 1\n            || style.fillOpacity < 1);\n    }\n    function flushPathDrawn(ctx, scope) {\n        scope.batchFill && ctx.fill();\n        scope.batchStroke && ctx.stroke();\n        scope.batchFill = '';\n        scope.batchStroke = '';\n    }\n    function getStyle(el, inHover) {\n        return inHover ? (el.__hoverStyle || el.style) : el.style;\n    }\n    function brushSingle(ctx, el) {\n        brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true);\n    }\n    function brush(ctx, el, scope, isLast) {\n        var m = el.transform;\n        if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) {\n            el.__dirty &= ~REDRAW_BIT;\n            el.__isRendered = false;\n            return;\n        }\n        var clipPaths = el.__clipPaths;\n        var prevElClipPaths = scope.prevElClipPaths;\n        var forceSetTransform = false;\n        var forceSetStyle = false;\n        if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) {\n            if (prevElClipPaths && prevElClipPaths.length) {\n                flushPathDrawn(ctx, scope);\n                ctx.restore();\n                forceSetStyle = forceSetTransform = true;\n                scope.prevElClipPaths = null;\n                scope.allClipped = false;\n                scope.prevEl = null;\n            }\n            if (clipPaths && clipPaths.length) {\n                flushPathDrawn(ctx, scope);\n                ctx.save();\n                updateClipStatus(clipPaths, ctx, scope);\n                forceSetTransform = true;\n            }\n            scope.prevElClipPaths = clipPaths;\n        }\n        if (scope.allClipped) {\n            el.__isRendered = false;\n            return;\n        }\n        el.beforeBrush && el.beforeBrush();\n        el.innerBeforeBrush();\n        var prevEl = scope.prevEl;\n        if (!prevEl) {\n            forceSetStyle = forceSetTransform = true;\n        }\n        var canBatchPath = el instanceof Path\n            && el.autoBatch\n            && canPathBatch(el.style);\n        if (forceSetTransform || isTransformChanged(m, prevEl.transform)) {\n            flushPathDrawn(ctx, scope);\n            setContextTransform(ctx, el);\n        }\n        else if (!canBatchPath) {\n            flushPathDrawn(ctx, scope);\n        }\n        var style = getStyle(el, scope.inHover);\n        if (el instanceof Path) {\n            if (scope.lastDrawType !== DRAW_TYPE_PATH) {\n                forceSetStyle = true;\n                scope.lastDrawType = DRAW_TYPE_PATH;\n            }\n            bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n            if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) {\n                ctx.beginPath();\n            }\n            brushPath(ctx, el, style, canBatchPath);\n            if (canBatchPath) {\n                scope.batchFill = style.fill || '';\n                scope.batchStroke = style.stroke || '';\n            }\n        }\n        else {\n            if (el instanceof TSpan) {\n                if (scope.lastDrawType !== DRAW_TYPE_TEXT) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_TEXT;\n                }\n                bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n                brushText(ctx, el, style);\n            }\n            else if (el instanceof ZRImage) {\n                if (scope.lastDrawType !== DRAW_TYPE_IMAGE) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_IMAGE;\n                }\n                bindImageStyle(ctx, el, prevEl, forceSetStyle, scope);\n                brushImage(ctx, el, style);\n            }\n            else if (el.getTemporalDisplayables) {\n                if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_INCREMENTAL;\n                }\n                brushIncremental(ctx, el, scope);\n            }\n        }\n        if (canBatchPath && isLast) {\n            flushPathDrawn(ctx, scope);\n        }\n        el.innerAfterBrush();\n        el.afterBrush && el.afterBrush();\n        scope.prevEl = el;\n        el.__dirty = 0;\n        el.__isRendered = true;\n    }\n    function brushIncremental(ctx, el, scope) {\n        var displayables = el.getDisplayables();\n        var temporalDisplayables = el.getTemporalDisplayables();\n        ctx.save();\n        var innerScope = {\n            prevElClipPaths: null,\n            prevEl: null,\n            allClipped: false,\n            viewWidth: scope.viewWidth,\n            viewHeight: scope.viewHeight,\n            inHover: scope.inHover\n        };\n        var i;\n        var len;\n        for (i = el.getCursor(), len = displayables.length; i < len; i++) {\n            var displayable = displayables[i];\n            displayable.beforeBrush && displayable.beforeBrush();\n            displayable.innerBeforeBrush();\n            brush(ctx, displayable, innerScope, i === len - 1);\n            displayable.innerAfterBrush();\n            displayable.afterBrush && displayable.afterBrush();\n            innerScope.prevEl = displayable;\n        }\n        for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) {\n            var displayable = temporalDisplayables[i_1];\n            displayable.beforeBrush && displayable.beforeBrush();\n            displayable.innerBeforeBrush();\n            brush(ctx, displayable, innerScope, i_1 === len_1 - 1);\n            displayable.innerAfterBrush();\n            displayable.afterBrush && displayable.afterBrush();\n            innerScope.prevEl = displayable;\n        }\n        el.clearTemporalDisplayables();\n        el.notClear = true;\n        ctx.restore();\n    }\n\n    var decalMap = new WeakMap();\n    var decalCache = new LRU(100);\n    var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight'];\n    /**\n     * Create or update pattern image from decal options\n     *\n     * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal\n     * @return {Pattern} pattern with generated image, null if no decal\n     */\n\n    function createOrUpdatePatternFromDecal(decalObject, api) {\n      if (decalObject === 'none') {\n        return null;\n      }\n\n      var dpr = api.getDevicePixelRatio();\n      var zr = api.getZr();\n      var isSVG = zr.painter.type === 'svg';\n\n      if (decalObject.dirty) {\n        decalMap[\"delete\"](decalObject);\n      }\n\n      var oldPattern = decalMap.get(decalObject);\n\n      if (oldPattern) {\n        return oldPattern;\n      }\n\n      var decalOpt = defaults(decalObject, {\n        symbol: 'rect',\n        symbolSize: 1,\n        symbolKeepAspect: true,\n        color: 'rgba(0, 0, 0, 0.2)',\n        backgroundColor: null,\n        dashArrayX: 5,\n        dashArrayY: 5,\n        rotation: 0,\n        maxTileWidth: 512,\n        maxTileHeight: 512\n      });\n\n      if (decalOpt.backgroundColor === 'none') {\n        decalOpt.backgroundColor = null;\n      }\n\n      var pattern = {\n        repeat: 'repeat'\n      };\n      setPatternnSource(pattern);\n      pattern.rotation = decalOpt.rotation;\n      pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr;\n      decalMap.set(decalObject, pattern);\n      decalObject.dirty = false;\n      return pattern;\n\n      function setPatternnSource(pattern) {\n        var keys = [dpr];\n        var isValidKey = true;\n\n        for (var i = 0; i < decalKeys.length; ++i) {\n          var value = decalOpt[decalKeys[i]];\n\n          if (value != null && !isArray(value) && !isString(value) && !isNumber(value) && typeof value !== 'boolean') {\n            isValidKey = false;\n            break;\n          }\n\n          keys.push(value);\n        }\n\n        var cacheKey;\n\n        if (isValidKey) {\n          cacheKey = keys.join(',') + (isSVG ? '-svg' : '');\n          var cache = decalCache.get(cacheKey);\n\n          if (cache) {\n            isSVG ? pattern.svgElement = cache : pattern.image = cache;\n          }\n        }\n\n        var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX);\n        var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY);\n        var symbolArray = normalizeSymbolArray(decalOpt.symbol);\n        var lineBlockLengthsX = getLineBlockLengthX(dashArrayX);\n        var lineBlockLengthY = getLineBlockLengthY(dashArrayY);\n        var canvas = !isSVG && platformApi.createCanvas();\n        var svgRoot = isSVG && {\n          tag: 'g',\n          attrs: {},\n          key: 'dcl',\n          children: []\n        };\n        var pSize = getPatternSize();\n        var ctx;\n\n        if (canvas) {\n          canvas.width = pSize.width * dpr;\n          canvas.height = pSize.height * dpr;\n          ctx = canvas.getContext('2d');\n        }\n\n        brushDecal();\n\n        if (isValidKey) {\n          decalCache.put(cacheKey, canvas || svgRoot);\n        }\n\n        pattern.image = canvas;\n        pattern.svgElement = svgRoot;\n        pattern.svgWidth = pSize.width;\n        pattern.svgHeight = pSize.height;\n        /**\n         * Get minimum length that can make a repeatable pattern.\n         *\n         * @return {Object} pattern width and height\n         */\n\n        function getPatternSize() {\n          /**\n           * For example, if dash is [[3, 2], [2, 1]] for X, it looks like\n           * |---  ---  ---  ---  --- ...\n           * |-- -- -- -- -- -- -- -- ...\n           * |---  ---  ---  ---  --- ...\n           * |-- -- -- -- -- -- -- -- ...\n           * So the minimum length of X is 15,\n           * which is the least common multiple of `3 + 2` and `2 + 1`\n           * |---  ---  ---  |---  --- ...\n           * |-- -- -- -- -- |-- -- -- ...\n           */\n          var width = 1;\n\n          for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) {\n            width = getLeastCommonMultiple(width, lineBlockLengthsX[i]);\n          }\n\n          var symbolRepeats = 1;\n\n          for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) {\n            symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length);\n          }\n\n          width *= symbolRepeats;\n          var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length;\n\n          if (\"development\" !== 'production') {\n            var warn = function (attrName) {\n              /* eslint-disable-next-line */\n              console.warn(\"Calculated decal size is greater than \" + attrName + \" due to decal option settings so \" + attrName + \" is used for the decal size. Please consider changing the decal option to make a smaller decal or set \" + attrName + \" to be larger to avoid incontinuity.\");\n            };\n\n            if (width > decalOpt.maxTileWidth) {\n              warn('maxTileWidth');\n            }\n\n            if (height > decalOpt.maxTileHeight) {\n              warn('maxTileHeight');\n            }\n          }\n\n          return {\n            width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)),\n            height: Math.max(1, Math.min(height, decalOpt.maxTileHeight))\n          };\n        }\n\n        function brushDecal() {\n          if (ctx) {\n            ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n            if (decalOpt.backgroundColor) {\n              ctx.fillStyle = decalOpt.backgroundColor;\n              ctx.fillRect(0, 0, canvas.width, canvas.height);\n            }\n          }\n\n          var ySum = 0;\n\n          for (var i = 0; i < dashArrayY.length; ++i) {\n            ySum += dashArrayY[i];\n          }\n\n          if (ySum <= 0) {\n            // dashArrayY is 0, draw nothing\n            return;\n          }\n\n          var y = -lineBlockLengthY;\n          var yId = 0;\n          var yIdTotal = 0;\n          var xId0 = 0;\n\n          while (y < pSize.height) {\n            if (yId % 2 === 0) {\n              var symbolYId = yIdTotal / 2 % symbolArray.length;\n              var x = 0;\n              var xId1 = 0;\n              var xId1Total = 0;\n\n              while (x < pSize.width * 2) {\n                var xSum = 0;\n\n                for (var i = 0; i < dashArrayX[xId0].length; ++i) {\n                  xSum += dashArrayX[xId0][i];\n                }\n\n                if (xSum <= 0) {\n                  // Skip empty line\n                  break;\n                } // E.g., [15, 5, 20, 5] draws only for 15 and 20\n\n\n                if (xId1 % 2 === 0) {\n                  var size = (1 - decalOpt.symbolSize) * 0.5;\n                  var left = x + dashArrayX[xId0][xId1] * size;\n                  var top_1 = y + dashArrayY[yId] * size;\n                  var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize;\n                  var height = dashArrayY[yId] * decalOpt.symbolSize;\n                  var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length;\n                  brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]);\n                }\n\n                x += dashArrayX[xId0][xId1];\n                ++xId1Total;\n                ++xId1;\n\n                if (xId1 === dashArrayX[xId0].length) {\n                  xId1 = 0;\n                }\n              }\n\n              ++xId0;\n\n              if (xId0 === dashArrayX.length) {\n                xId0 = 0;\n              }\n            }\n\n            y += dashArrayY[yId];\n            ++yIdTotal;\n            ++yId;\n\n            if (yId === dashArrayY.length) {\n              yId = 0;\n            }\n          }\n\n          function brushSymbol(x, y, width, height, symbolType) {\n            var scale = isSVG ? 1 : dpr;\n            var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect);\n\n            if (isSVG) {\n              var symbolVNode = zr.painter.renderOneToVNode(symbol);\n\n              if (symbolVNode) {\n                svgRoot.children.push(symbolVNode);\n              }\n            } else {\n              // Paint to canvas for all other renderers.\n              brushSingle(ctx, symbol);\n            }\n          }\n        }\n      }\n    }\n    /**\n     * Convert symbol array into normalized array\n     *\n     * @param {string | (string | string[])[]} symbol symbol input\n     * @return {string[][]} normolized symbol array\n     */\n\n    function normalizeSymbolArray(symbol) {\n      if (!symbol || symbol.length === 0) {\n        return [['rect']];\n      }\n\n      if (isString(symbol)) {\n        return [[symbol]];\n      }\n\n      var isAllString = true;\n\n      for (var i = 0; i < symbol.length; ++i) {\n        if (!isString(symbol[i])) {\n          isAllString = false;\n          break;\n        }\n      }\n\n      if (isAllString) {\n        return normalizeSymbolArray([symbol]);\n      }\n\n      var result = [];\n\n      for (var i = 0; i < symbol.length; ++i) {\n        if (isString(symbol[i])) {\n          result.push([symbol[i]]);\n        } else {\n          result.push(symbol[i]);\n        }\n      }\n\n      return result;\n    }\n    /**\n     * Convert dash input into dashArray\n     *\n     * @param {DecalDashArrayX} dash dash input\n     * @return {number[][]} normolized dash array\n     */\n\n\n    function normalizeDashArrayX(dash) {\n      if (!dash || dash.length === 0) {\n        return [[0, 0]];\n      }\n\n      if (isNumber(dash)) {\n        var dashValue = Math.ceil(dash);\n        return [[dashValue, dashValue]];\n      }\n      /**\n       * [20, 5] should be normalized into [[20, 5]],\n       * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]]\n       */\n\n\n      var isAllNumber = true;\n\n      for (var i = 0; i < dash.length; ++i) {\n        if (!isNumber(dash[i])) {\n          isAllNumber = false;\n          break;\n        }\n      }\n\n      if (isAllNumber) {\n        return normalizeDashArrayX([dash]);\n      }\n\n      var result = [];\n\n      for (var i = 0; i < dash.length; ++i) {\n        if (isNumber(dash[i])) {\n          var dashValue = Math.ceil(dash[i]);\n          result.push([dashValue, dashValue]);\n        } else {\n          var dashValue = map(dash[i], function (n) {\n            return Math.ceil(n);\n          });\n\n          if (dashValue.length % 2 === 1) {\n            // [4, 2, 1] means |----  -    -- |----  -    -- |\n            // so normalize it to be [4, 2, 1, 4, 2, 1]\n            result.push(dashValue.concat(dashValue));\n          } else {\n            result.push(dashValue);\n          }\n        }\n      }\n\n      return result;\n    }\n    /**\n     * Convert dash input into dashArray\n     *\n     * @param {DecalDashArrayY} dash dash input\n     * @return {number[]} normolized dash array\n     */\n\n\n    function normalizeDashArrayY(dash) {\n      if (!dash || typeof dash === 'object' && dash.length === 0) {\n        return [0, 0];\n      }\n\n      if (isNumber(dash)) {\n        var dashValue_1 = Math.ceil(dash);\n        return [dashValue_1, dashValue_1];\n      }\n\n      var dashValue = map(dash, function (n) {\n        return Math.ceil(n);\n      });\n      return dash.length % 2 ? dashValue.concat(dashValue) : dashValue;\n    }\n    /**\n     * Get block length of each line. A block is the length of dash line and space.\n     * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after\n     * that, so the block length of this line is 5.\n     *\n     * @param {number[][]} dash dash array of X or Y\n     * @return {number[]} block length of each line\n     */\n\n\n    function getLineBlockLengthX(dash) {\n      return map(dash, function (line) {\n        return getLineBlockLengthY(line);\n      });\n    }\n\n    function getLineBlockLengthY(dash) {\n      var blockLength = 0;\n\n      for (var i = 0; i < dash.length; ++i) {\n        blockLength += dash[i];\n      }\n\n      if (dash.length % 2 === 1) {\n        // [4, 2, 1] means |----  -    -- |----  -    -- |\n        // So total length is (4 + 2 + 1) * 2\n        return blockLength * 2;\n      }\n\n      return blockLength;\n    }\n\n    function decalVisual(ecModel, api) {\n      ecModel.eachRawSeries(function (seriesModel) {\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n\n        if (data.hasItemVisual()) {\n          data.each(function (idx) {\n            var decal = data.getItemVisual(idx, 'decal');\n\n            if (decal) {\n              var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n              itemStyle.decal = createOrUpdatePatternFromDecal(decal, api);\n            }\n          });\n        }\n\n        var decal = data.getVisual('decal');\n\n        if (decal) {\n          var style = data.getVisual('style');\n          style.decal = createOrUpdatePatternFromDecal(decal, api);\n        }\n      });\n    }\n\n    var lifecycle = new Eventful();\n\n    // The implementations will be registered when installing the component.\n    // Avoid these code being bundled to the core module.\n\n    var implsStore = {}; // TODO Type\n\n    function registerImpl(name, impl) {\n      if (\"development\" !== 'production') {\n        if (implsStore[name]) {\n          error(\"Already has an implementation of \" + name + \".\");\n        }\n      }\n\n      implsStore[name] = impl;\n    }\n    function getImpl(name) {\n      if (\"development\" !== 'production') {\n        if (!implsStore[name]) {\n          error(\"Implementation of \" + name + \" doesn't exists.\");\n        }\n      }\n\n      return implsStore[name];\n    }\n\n    var version$1 = '5.4.1';\n    var dependencies = {\n      zrender: '5.4.1'\n    };\n    var TEST_FRAME_REMAIN_TIME = 1;\n    var PRIORITY_PROCESSOR_SERIES_FILTER = 800; // Some data processors depends on the stack result dimension (to calculate data extent).\n    // So data stack stage should be in front of data processing stage.\n\n    var PRIORITY_PROCESSOR_DATASTACK = 900; // \"Data filter\" will block the stream, so it should be\n    // put at the beginning of data processing.\n\n    var PRIORITY_PROCESSOR_FILTER = 1000;\n    var PRIORITY_PROCESSOR_DEFAULT = 2000;\n    var PRIORITY_PROCESSOR_STATISTIC = 5000;\n    var PRIORITY_VISUAL_LAYOUT = 1000;\n    var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100;\n    var PRIORITY_VISUAL_GLOBAL = 2000;\n    var PRIORITY_VISUAL_CHART = 3000;\n    var PRIORITY_VISUAL_COMPONENT = 4000; // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to\n    // overwrite the viusal result of component (like `visualMap`)\n    // using data item specific setting (like itemStyle.xxx on data item)\n\n    var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on\n    // visual result like `symbolSize`.\n\n    var PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600;\n    var PRIORITY_VISUAL_BRUSH = 5000;\n    var PRIORITY_VISUAL_ARIA = 6000;\n    var PRIORITY_VISUAL_DECAL = 7000;\n    var PRIORITY = {\n      PROCESSOR: {\n        FILTER: PRIORITY_PROCESSOR_FILTER,\n        SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER,\n        STATISTIC: PRIORITY_PROCESSOR_STATISTIC\n      },\n      VISUAL: {\n        LAYOUT: PRIORITY_VISUAL_LAYOUT,\n        PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT,\n        GLOBAL: PRIORITY_VISUAL_GLOBAL,\n        CHART: PRIORITY_VISUAL_CHART,\n        POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT,\n        COMPONENT: PRIORITY_VISUAL_COMPONENT,\n        BRUSH: PRIORITY_VISUAL_BRUSH,\n        CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM,\n        ARIA: PRIORITY_VISUAL_ARIA,\n        DECAL: PRIORITY_VISUAL_DECAL\n      }\n    }; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,\n    // where they must not be invoked nestedly, except the only case: invoke\n    // dispatchAction with updateMethod \"none\" in main process.\n    // This flag is used to carry out this rule.\n    // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).\n\n    var IN_MAIN_PROCESS_KEY = '__flagInMainProcess';\n    var PENDING_UPDATE = '__pendingUpdate';\n    var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus';\n    var ACTION_REG = /^[a-zA-Z0-9_]+$/;\n    var CONNECT_STATUS_KEY = '__connectUpdateStatus';\n    var CONNECT_STATUS_PENDING = 0;\n    var CONNECT_STATUS_UPDATING = 1;\n    var CONNECT_STATUS_UPDATED = 2;\n\n    function createRegisterEventWithLowercaseECharts(method) {\n      return function () {\n        var args = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args[_i] = arguments[_i];\n        }\n\n        if (this.isDisposed()) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        return toLowercaseNameAndCallEventful(this, method, args);\n      };\n    }\n\n    function createRegisterEventWithLowercaseMessageCenter(method) {\n      return function () {\n        var args = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args[_i] = arguments[_i];\n        }\n\n        return toLowercaseNameAndCallEventful(this, method, args);\n      };\n    }\n\n    function toLowercaseNameAndCallEventful(host, method, args) {\n      // `args[0]` is event name. Event name is all lowercase.\n      args[0] = args[0] && args[0].toLowerCase();\n      return Eventful.prototype[method].apply(host, args);\n    }\n\n    var MessageCenter =\n    /** @class */\n    function (_super) {\n      __extends(MessageCenter, _super);\n\n      function MessageCenter() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return MessageCenter;\n    }(Eventful);\n\n    var messageCenterProto = MessageCenter.prototype;\n    messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on');\n    messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); // ---------------------------------------\n    // Internal method names for class ECharts\n    // ---------------------------------------\n\n    var prepare;\n    var prepareView;\n    var updateDirectly;\n    var updateMethods;\n    var doConvertPixel;\n    var updateStreamModes;\n    var doDispatchAction;\n    var flushPendingActions;\n    var triggerUpdatedEvent;\n    var bindRenderedEvent;\n    var bindMouseEvent;\n    var render;\n    var renderComponents;\n    var renderSeries;\n    var createExtensionAPI;\n    var enableConnect;\n    var markStatusToUpdate;\n    var applyChangedStates;\n\n    var ECharts =\n    /** @class */\n    function (_super) {\n      __extends(ECharts, _super);\n\n      function ECharts(dom, // Theme name or themeOption.\n      theme, opts) {\n        var _this = _super.call(this, new ECEventProcessor()) || this;\n\n        _this._chartsViews = [];\n        _this._chartsMap = {};\n        _this._componentsViews = [];\n        _this._componentsMap = {}; // Can't dispatch action during rendering procedure\n\n        _this._pendingActions = [];\n        opts = opts || {}; // Get theme by name\n\n        if (isString(theme)) {\n          theme = themeStorage[theme];\n        }\n\n        _this._dom = dom;\n        var defaultRenderer = 'canvas';\n        var defaultCoarsePointer = 'auto';\n        var defaultUseDirtyRect = false;\n\n        if (\"development\" !== 'production') {\n          var root =\n          /* eslint-disable-next-line */\n          env.hasGlobalWindow ? window : global;\n          defaultRenderer = root.__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;\n          defaultCoarsePointer = retrieve2(root.__ECHARTS__DEFAULT__COARSE_POINTER, defaultCoarsePointer);\n          var devUseDirtyRect = root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__;\n          defaultUseDirtyRect = devUseDirtyRect == null ? defaultUseDirtyRect : devUseDirtyRect;\n        }\n\n        var zr = _this._zr = init(dom, {\n          renderer: opts.renderer || defaultRenderer,\n          devicePixelRatio: opts.devicePixelRatio,\n          width: opts.width,\n          height: opts.height,\n          ssr: opts.ssr,\n          useDirtyRect: retrieve2(opts.useDirtyRect, defaultUseDirtyRect),\n          useCoarsePointer: retrieve2(opts.useCoarsePointer, defaultCoarsePointer),\n          pointerSize: opts.pointerSize\n        });\n        _this._ssr = opts.ssr; // Expect 60 fps.\n\n        _this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);\n        theme = clone(theme);\n        theme && globalBackwardCompat(theme, true);\n        _this._theme = theme;\n        _this._locale = createLocaleObject(opts.locale || SYSTEM_LANG);\n        _this._coordSysMgr = new CoordinateSystemManager();\n        var api = _this._api = createExtensionAPI(_this); // Sort on demand\n\n        function prioritySortFunc(a, b) {\n          return a.__prio - b.__prio;\n        }\n\n        sort(visualFuncs, prioritySortFunc);\n        sort(dataProcessorFuncs, prioritySortFunc);\n        _this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs);\n        _this._messageCenter = new MessageCenter(); // Init mouse events\n\n        _this._initEvents(); // In case some people write `window.onresize = chart.resize`\n\n\n        _this.resize = bind(_this.resize, _this);\n        zr.animation.on('frame', _this._onframe, _this);\n        bindRenderedEvent(zr, _this);\n        bindMouseEvent(zr, _this); // ECharts instance can be used as value.\n\n        setAsPrimitive(_this);\n        return _this;\n      }\n\n      ECharts.prototype._onframe = function () {\n        if (this._disposed) {\n          return;\n        }\n\n        applyChangedStates(this);\n        var scheduler = this._scheduler; // Lazy update\n\n        if (this[PENDING_UPDATE]) {\n          var silent = this[PENDING_UPDATE].silent;\n          this[IN_MAIN_PROCESS_KEY] = true;\n\n          try {\n            prepare(this);\n            updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams);\n          } catch (e) {\n            this[IN_MAIN_PROCESS_KEY] = false;\n            this[PENDING_UPDATE] = null;\n            throw e;\n          } // At present, in each frame, zrender performs:\n          //   (1) animation step forward.\n          //   (2) trigger('frame') (where this `_onframe` is called)\n          //   (3) zrender flush (render).\n          // If we do nothing here, since we use `setToFinal: true`, the step (3) above\n          // will render the final state of the elements before the real animation started.\n\n\n          this._zr.flush();\n\n          this[IN_MAIN_PROCESS_KEY] = false;\n          this[PENDING_UPDATE] = null;\n          flushPendingActions.call(this, silent);\n          triggerUpdatedEvent.call(this, silent);\n        } // Avoid do both lazy update and progress in one frame.\n        else if (scheduler.unfinished) {\n            // Stream progress.\n            var remainTime = TEST_FRAME_REMAIN_TIME;\n            var ecModel = this._model;\n            var api = this._api;\n            scheduler.unfinished = false;\n\n            do {\n              var startTime = +new Date();\n              scheduler.performSeriesTasks(ecModel); // Currently dataProcessorFuncs do not check threshold.\n\n              scheduler.performDataProcessorTasks(ecModel);\n              updateStreamModes(this, ecModel); // Do not update coordinate system here. Because that coord system update in\n              // each frame is not a good user experience. So we follow the rule that\n              // the extent of the coordinate system is determined in the first frame (the\n              // frame is executed immediately after task reset.\n              // this._coordSysMgr.update(ecModel, api);\n              // console.log('--- ec frame visual ---', remainTime);\n\n              scheduler.performVisualTasks(ecModel);\n              renderSeries(this, this._model, api, 'remain', {});\n              remainTime -= +new Date() - startTime;\n            } while (remainTime > 0 && scheduler.unfinished); // Call flush explicitly for trigger finished event.\n\n\n            if (!scheduler.unfinished) {\n              this._zr.flush();\n            } // Else, zr flushing be ensue within the same frame,\n            // because zr flushing is after onframe event.\n\n          }\n      };\n\n      ECharts.prototype.getDom = function () {\n        return this._dom;\n      };\n\n      ECharts.prototype.getId = function () {\n        return this.id;\n      };\n\n      ECharts.prototype.getZr = function () {\n        return this._zr;\n      };\n\n      ECharts.prototype.isSSR = function () {\n        return this._ssr;\n      };\n      /* eslint-disable-next-line */\n\n\n      ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) {\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          if (\"development\" !== 'production') {\n            error('`setOption` should not be called during main process.');\n          }\n\n          return;\n        }\n\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var silent;\n        var replaceMerge;\n        var transitionOpt;\n\n        if (isObject(notMerge)) {\n          lazyUpdate = notMerge.lazyUpdate;\n          silent = notMerge.silent;\n          replaceMerge = notMerge.replaceMerge;\n          transitionOpt = notMerge.transition;\n          notMerge = notMerge.notMerge;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = true;\n\n        if (!this._model || notMerge) {\n          var optionManager = new OptionManager(this._api);\n          var theme = this._theme;\n          var ecModel = this._model = new GlobalModel();\n          ecModel.scheduler = this._scheduler;\n          ecModel.ssr = this._ssr;\n          ecModel.init(null, null, null, theme, this._locale, optionManager);\n        }\n\n        this._model.setOption(option, {\n          replaceMerge: replaceMerge\n        }, optionPreprocessorFuncs);\n\n        var updateParams = {\n          seriesTransition: transitionOpt,\n          optionChanged: true\n        };\n\n        if (lazyUpdate) {\n          this[PENDING_UPDATE] = {\n            silent: silent,\n            updateParams: updateParams\n          };\n          this[IN_MAIN_PROCESS_KEY] = false; // `setOption(option, {lazyMode: true})` may be called when zrender has been slept.\n          // It should wake it up to make sure zrender start to render at the next frame.\n\n          this.getZr().wakeUp();\n        } else {\n          try {\n            prepare(this);\n            updateMethods.update.call(this, null, updateParams);\n          } catch (e) {\n            this[PENDING_UPDATE] = null;\n            this[IN_MAIN_PROCESS_KEY] = false;\n            throw e;\n          } // Ensure zr refresh sychronously, and then pixel in canvas can be\n          // fetched after `setOption`.\n\n\n          if (!this._ssr) {\n            // not use flush when using ssr mode.\n            this._zr.flush();\n          }\n\n          this[PENDING_UPDATE] = null;\n          this[IN_MAIN_PROCESS_KEY] = false;\n          flushPendingActions.call(this, silent);\n          triggerUpdatedEvent.call(this, silent);\n        }\n      };\n      /**\n       * @deprecated\n       */\n\n\n      ECharts.prototype.setTheme = function () {\n        deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0');\n      }; // We don't want developers to use getModel directly.\n\n\n      ECharts.prototype.getModel = function () {\n        return this._model;\n      };\n\n      ECharts.prototype.getOption = function () {\n        return this._model && this._model.getOption();\n      };\n\n      ECharts.prototype.getWidth = function () {\n        return this._zr.getWidth();\n      };\n\n      ECharts.prototype.getHeight = function () {\n        return this._zr.getHeight();\n      };\n\n      ECharts.prototype.getDevicePixelRatio = function () {\n        return this._zr.painter.dpr\n        /* eslint-disable-next-line */\n        || env.hasGlobalWindow && window.devicePixelRatio || 1;\n      };\n      /**\n       * Get canvas which has all thing rendered\n       * @deprecated Use renderToCanvas instead.\n       */\n\n\n      ECharts.prototype.getRenderedCanvas = function (opts) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas');\n        }\n\n        return this.renderToCanvas(opts);\n      };\n\n      ECharts.prototype.renderToCanvas = function (opts) {\n        opts = opts || {};\n        var painter = this._zr.painter;\n\n        if (\"development\" !== 'production') {\n          if (painter.type !== 'canvas') {\n            throw new Error('renderToCanvas can only be used in the canvas renderer.');\n          }\n        }\n\n        return painter.getRenderedCanvas({\n          backgroundColor: opts.backgroundColor || this._model.get('backgroundColor'),\n          pixelRatio: opts.pixelRatio || this.getDevicePixelRatio()\n        });\n      };\n\n      ECharts.prototype.renderToSVGString = function (opts) {\n        opts = opts || {};\n        var painter = this._zr.painter;\n\n        if (\"development\" !== 'production') {\n          if (painter.type !== 'svg') {\n            throw new Error('renderToSVGString can only be used in the svg renderer.');\n          }\n        }\n\n        return painter.renderToString({\n          useViewBox: opts.useViewBox\n        });\n      };\n      /**\n       * Get svg data url\n       */\n\n\n      ECharts.prototype.getSvgDataURL = function () {\n        if (!env.svgSupported) {\n          return;\n        }\n\n        var zr = this._zr;\n        var list = zr.storage.getDisplayList(); // Stop animations\n\n        each(list, function (el) {\n          el.stopAnimation(null, true);\n        });\n        return zr.painter.toDataURL();\n      };\n\n      ECharts.prototype.getDataURL = function (opts) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        opts = opts || {};\n        var excludeComponents = opts.excludeComponents;\n        var ecModel = this._model;\n        var excludesComponentViews = [];\n        var self = this;\n        each(excludeComponents, function (componentType) {\n          ecModel.eachComponent({\n            mainType: componentType\n          }, function (component) {\n            var view = self._componentsMap[component.__viewId];\n\n            if (!view.group.ignore) {\n              excludesComponentViews.push(view);\n              view.group.ignore = true;\n            }\n          });\n        });\n        var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() : this.renderToCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));\n        each(excludesComponentViews, function (view) {\n          view.group.ignore = false;\n        });\n        return url;\n      };\n\n      ECharts.prototype.getConnectedDataURL = function (opts) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var isSvg = opts.type === 'svg';\n        var groupId = this.group;\n        var mathMin = Math.min;\n        var mathMax = Math.max;\n        var MAX_NUMBER = Infinity;\n\n        if (connectedGroups[groupId]) {\n          var left_1 = MAX_NUMBER;\n          var top_1 = MAX_NUMBER;\n          var right_1 = -MAX_NUMBER;\n          var bottom_1 = -MAX_NUMBER;\n          var canvasList_1 = [];\n          var dpr_1 = opts && opts.pixelRatio || this.getDevicePixelRatio();\n          each(instances$1, function (chart, id) {\n            if (chart.group === groupId) {\n              var canvas = isSvg ? chart.getZr().painter.getSvgDom().innerHTML : chart.renderToCanvas(clone(opts));\n              var boundingRect = chart.getDom().getBoundingClientRect();\n              left_1 = mathMin(boundingRect.left, left_1);\n              top_1 = mathMin(boundingRect.top, top_1);\n              right_1 = mathMax(boundingRect.right, right_1);\n              bottom_1 = mathMax(boundingRect.bottom, bottom_1);\n              canvasList_1.push({\n                dom: canvas,\n                left: boundingRect.left,\n                top: boundingRect.top\n              });\n            }\n          });\n          left_1 *= dpr_1;\n          top_1 *= dpr_1;\n          right_1 *= dpr_1;\n          bottom_1 *= dpr_1;\n          var width = right_1 - left_1;\n          var height = bottom_1 - top_1;\n          var targetCanvas = platformApi.createCanvas();\n          var zr_1 = init(targetCanvas, {\n            renderer: isSvg ? 'svg' : 'canvas'\n          });\n          zr_1.resize({\n            width: width,\n            height: height\n          });\n\n          if (isSvg) {\n            var content_1 = '';\n            each(canvasList_1, function (item) {\n              var x = item.left - left_1;\n              var y = item.top - top_1;\n              content_1 += '<g transform=\"translate(' + x + ',' + y + ')\">' + item.dom + '</g>';\n            });\n            zr_1.painter.getSvgRoot().innerHTML = content_1;\n\n            if (opts.connectedBackgroundColor) {\n              zr_1.painter.setBackgroundColor(opts.connectedBackgroundColor);\n            }\n\n            zr_1.refreshImmediately();\n            return zr_1.painter.toDataURL();\n          } else {\n            // Background between the charts\n            if (opts.connectedBackgroundColor) {\n              zr_1.add(new Rect({\n                shape: {\n                  x: 0,\n                  y: 0,\n                  width: width,\n                  height: height\n                },\n                style: {\n                  fill: opts.connectedBackgroundColor\n                }\n              }));\n            }\n\n            each(canvasList_1, function (item) {\n              var img = new ZRImage({\n                style: {\n                  x: item.left * dpr_1 - left_1,\n                  y: item.top * dpr_1 - top_1,\n                  image: item.dom\n                }\n              });\n              zr_1.add(img);\n            });\n            zr_1.refreshImmediately();\n            return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));\n          }\n        } else {\n          return this.getDataURL(opts);\n        }\n      };\n\n      ECharts.prototype.convertToPixel = function (finder, value) {\n        return doConvertPixel(this, 'convertToPixel', finder, value);\n      };\n\n      ECharts.prototype.convertFromPixel = function (finder, value) {\n        return doConvertPixel(this, 'convertFromPixel', finder, value);\n      };\n      /**\n       * Is the specified coordinate systems or components contain the given pixel point.\n       * @param {Array|number} value\n       * @return {boolean} result\n       */\n\n\n      ECharts.prototype.containPixel = function (finder, value) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var ecModel = this._model;\n        var result;\n        var findResult = parseFinder(ecModel, finder);\n        each(findResult, function (models, key) {\n          key.indexOf('Models') >= 0 && each(models, function (model) {\n            var coordSys = model.coordinateSystem;\n\n            if (coordSys && coordSys.containPoint) {\n              result = result || !!coordSys.containPoint(value);\n            } else if (key === 'seriesModels') {\n              var view = this._chartsMap[model.__viewId];\n\n              if (view && view.containPoint) {\n                result = result || view.containPoint(value, model);\n              } else {\n                if (\"development\" !== 'production') {\n                  warn(key + ': ' + (view ? 'The found component do not support containPoint.' : 'No view mapping to the found component.'));\n                }\n              }\n            } else {\n              if (\"development\" !== 'production') {\n                warn(key + ': containPoint is not supported');\n              }\n            }\n          }, this);\n        }, this);\n        return !!result;\n      };\n      /**\n       * Get visual from series or data.\n       * @param finder\n       *        If string, e.g., 'series', means {seriesIndex: 0}.\n       *        If Object, could contain some of these properties below:\n       *        {\n       *            seriesIndex / seriesId / seriesName,\n       *            dataIndex / dataIndexInside\n       *        }\n       *        If dataIndex is not specified, series visual will be fetched,\n       *        but not data item visual.\n       *        If all of seriesIndex, seriesId, seriesName are not specified,\n       *        visual will be fetched from first series.\n       * @param visualType 'color', 'symbol', 'symbolSize'\n       */\n\n\n      ECharts.prototype.getVisual = function (finder, visualType) {\n        var ecModel = this._model;\n        var parsedFinder = parseFinder(ecModel, finder, {\n          defaultMainType: 'series'\n        });\n        var seriesModel = parsedFinder.seriesModel;\n\n        if (\"development\" !== 'production') {\n          if (!seriesModel) {\n            warn('There is no specified series model');\n          }\n        }\n\n        var data = seriesModel.getData();\n        var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') ? parsedFinder.dataIndexInside : parsedFinder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(parsedFinder.dataIndex) : null;\n        return dataIndexInside != null ? getItemVisualFromData(data, dataIndexInside, visualType) : getVisualFromData(data, visualType);\n      };\n      /**\n       * Get view of corresponding component model\n       */\n\n\n      ECharts.prototype.getViewOfComponentModel = function (componentModel) {\n        return this._componentsMap[componentModel.__viewId];\n      };\n      /**\n       * Get view of corresponding series model\n       */\n\n\n      ECharts.prototype.getViewOfSeriesModel = function (seriesModel) {\n        return this._chartsMap[seriesModel.__viewId];\n      };\n\n      ECharts.prototype._initEvents = function () {\n        var _this = this;\n\n        each(MOUSE_EVENT_NAMES, function (eveName) {\n          var handler = function (e) {\n            var ecModel = _this.getModel();\n\n            var el = e.target;\n            var params;\n            var isGlobalOut = eveName === 'globalout'; // no e.target when 'globalout'.\n\n            if (isGlobalOut) {\n              params = {};\n            } else {\n              el && findEventDispatcher(el, function (parent) {\n                var ecData = getECData(parent);\n\n                if (ecData && ecData.dataIndex != null) {\n                  var dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex);\n                  params = dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType) || {};\n                  return true;\n                } // If element has custom eventData of components\n                else if (ecData.eventData) {\n                    params = extend({}, ecData.eventData);\n                    return true;\n                  }\n              }, true);\n            } // Contract: if params prepared in mouse event,\n            // these properties must be specified:\n            // {\n            //    componentType: string (component main type)\n            //    componentIndex: number\n            // }\n            // Otherwise event query can not work.\n\n\n            if (params) {\n              var componentType = params.componentType;\n              var componentIndex = params.componentIndex; // Special handling for historic reason: when trigger by\n              // markLine/markPoint/markArea, the componentType is\n              // 'markLine'/'markPoint'/'markArea', but we should better\n              // enable them to be queried by seriesIndex, since their\n              // option is set in each series.\n\n              if (componentType === 'markLine' || componentType === 'markPoint' || componentType === 'markArea') {\n                componentType = 'series';\n                componentIndex = params.seriesIndex;\n              }\n\n              var model = componentType && componentIndex != null && ecModel.getComponent(componentType, componentIndex);\n              var view = model && _this[model.mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId];\n\n              if (\"development\" !== 'production') {\n                // `event.componentType` and `event[componentTpype + 'Index']` must not\n                // be missed, otherwise there is no way to distinguish source component.\n                // See `dataFormat.getDataParams`.\n                if (!isGlobalOut && !(model && view)) {\n                  warn('model or view can not be found by params');\n                }\n              }\n\n              params.event = e;\n              params.type = eveName;\n              _this._$eventProcessor.eventInfo = {\n                targetEl: el,\n                packedEvent: params,\n                model: model,\n                view: view\n              };\n\n              _this.trigger(eveName, params);\n            }\n          }; // Consider that some component (like tooltip, brush, ...)\n          // register zr event handler, but user event handler might\n          // do anything, such as call `setOption` or `dispatchAction`,\n          // which probably update any of the content and probably\n          // cause problem if it is called previous other inner handlers.\n\n\n          handler.zrEventfulCallAtLast = true;\n\n          _this._zr.on(eveName, handler, _this);\n        });\n        each(eventActionMap, function (actionType, eventType) {\n          _this._messageCenter.on(eventType, function (event) {\n            this.trigger(eventType, event);\n          }, _this);\n        }); // Extra events\n        // TODO register?\n\n        each(['selectchanged'], function (eventType) {\n          _this._messageCenter.on(eventType, function (event) {\n            this.trigger(eventType, event);\n          }, _this);\n        });\n        handleLegacySelectEvents(this._messageCenter, this, this._api);\n      };\n\n      ECharts.prototype.isDisposed = function () {\n        return this._disposed;\n      };\n\n      ECharts.prototype.clear = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this.setOption({\n          series: []\n        }, true);\n      };\n\n      ECharts.prototype.dispose = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._disposed = true;\n        var dom = this.getDom();\n\n        if (dom) {\n          setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');\n        }\n\n        var chart = this;\n        var api = chart._api;\n        var ecModel = chart._model;\n        each(chart._componentsViews, function (component) {\n          component.dispose(ecModel, api);\n        });\n        each(chart._chartsViews, function (chart) {\n          chart.dispose(ecModel, api);\n        }); // Dispose after all views disposed\n\n        chart._zr.dispose(); // Set properties to null.\n        // To reduce the memory cost in case the top code still holds this instance unexpectedly.\n\n\n        chart._dom = chart._model = chart._chartsMap = chart._componentsMap = chart._chartsViews = chart._componentsViews = chart._scheduler = chart._api = chart._zr = chart._throttledZrFlush = chart._theme = chart._coordSysMgr = chart._messageCenter = null;\n        delete instances$1[chart.id];\n      };\n      /**\n       * Resize the chart\n       */\n\n\n      ECharts.prototype.resize = function (opts) {\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          if (\"development\" !== 'production') {\n            error('`resize` should not be called during main process.');\n          }\n\n          return;\n        }\n\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._zr.resize(opts);\n\n        var ecModel = this._model; // Resize loading effect\n\n        this._loadingFX && this._loadingFX.resize();\n\n        if (!ecModel) {\n          return;\n        }\n\n        var needPrepare = ecModel.resetOption('media');\n        var silent = opts && opts.silent; // There is some real cases that:\n        // chart.setOption(option, { lazyUpdate: true });\n        // chart.resize();\n\n        if (this[PENDING_UPDATE]) {\n          if (silent == null) {\n            silent = this[PENDING_UPDATE].silent;\n          }\n\n          needPrepare = true;\n          this[PENDING_UPDATE] = null;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = true;\n\n        try {\n          needPrepare && prepare(this);\n          updateMethods.update.call(this, {\n            type: 'resize',\n            animation: extend({\n              // Disable animation\n              duration: 0\n            }, opts && opts.animation)\n          });\n        } catch (e) {\n          this[IN_MAIN_PROCESS_KEY] = false;\n          throw e;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = false;\n        flushPendingActions.call(this, silent);\n        triggerUpdatedEvent.call(this, silent);\n      };\n\n      ECharts.prototype.showLoading = function (name, cfg) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        if (isObject(name)) {\n          cfg = name;\n          name = '';\n        }\n\n        name = name || 'default';\n        this.hideLoading();\n\n        if (!loadingEffects[name]) {\n          if (\"development\" !== 'production') {\n            warn('Loading effects ' + name + ' not exists.');\n          }\n\n          return;\n        }\n\n        var el = loadingEffects[name](this._api, cfg);\n        var zr = this._zr;\n        this._loadingFX = el;\n        zr.add(el);\n      };\n      /**\n       * Hide loading effect\n       */\n\n\n      ECharts.prototype.hideLoading = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._loadingFX && this._zr.remove(this._loadingFX);\n        this._loadingFX = null;\n      };\n\n      ECharts.prototype.makeActionFromEvent = function (eventObj) {\n        var payload = extend({}, eventObj);\n        payload.type = eventActionMap[eventObj.type];\n        return payload;\n      };\n      /**\n       * @param opt If pass boolean, means opt.silent\n       * @param opt.silent Default `false`. Whether trigger events.\n       * @param opt.flush Default `undefined`.\n       *        true: Flush immediately, and then pixel in canvas can be fetched\n       *            immediately. Caution: it might affect performance.\n       *        false: Not flush.\n       *        undefined: Auto decide whether perform flush.\n       */\n\n\n      ECharts.prototype.dispatchAction = function (payload, opt) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        if (!isObject(opt)) {\n          opt = {\n            silent: !!opt\n          };\n        }\n\n        if (!actions[payload.type]) {\n          return;\n        } // Avoid dispatch action before setOption. Especially in `connect`.\n\n\n        if (!this._model) {\n          return;\n        } // May dispatchAction in rendering procedure\n\n\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          this._pendingActions.push(payload);\n\n          return;\n        }\n\n        var silent = opt.silent;\n        doDispatchAction.call(this, payload, silent);\n        var flush = opt.flush;\n\n        if (flush) {\n          this._zr.flush();\n        } else if (flush !== false && env.browser.weChat) {\n          // In WeChat embedded browser, `requestAnimationFrame` and `setInterval`\n          // hang when sliding page (on touch event), which cause that zr does not\n          // refresh until user interaction finished, which is not expected.\n          // But `dispatchAction` may be called too frequently when pan on touch\n          // screen, which impacts performance if do not throttle them.\n          this._throttledZrFlush();\n        }\n\n        flushPendingActions.call(this, silent);\n        triggerUpdatedEvent.call(this, silent);\n      };\n\n      ECharts.prototype.updateLabelLayout = function () {\n        lifecycle.trigger('series:layoutlabels', this._model, this._api, {\n          // Not adding series labels.\n          // TODO\n          updatedSeries: []\n        });\n      };\n\n      ECharts.prototype.appendData = function (params) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var seriesIndex = params.seriesIndex;\n        var ecModel = this.getModel();\n        var seriesModel = ecModel.getSeriesByIndex(seriesIndex);\n\n        if (\"development\" !== 'production') {\n          assert(params.data && seriesModel);\n        }\n\n        seriesModel.appendData(params); // Note: `appendData` does not support that update extent of coordinate\n        // system, util some scenario require that. In the expected usage of\n        // `appendData`, the initial extent of coordinate system should better\n        // be fixed by axis `min`/`max` setting or initial data, otherwise if\n        // the extent changed while `appendData`, the location of the painted\n        // graphic elements have to be changed, which make the usage of\n        // `appendData` meaningless.\n\n        this._scheduler.unfinished = true;\n        this.getZr().wakeUp();\n      }; // A work around for no `internal` modifier in ts yet but\n      // need to strictly hide private methods to JS users.\n\n\n      ECharts.internalField = function () {\n        prepare = function (ecIns) {\n          var scheduler = ecIns._scheduler;\n          scheduler.restorePipelines(ecIns._model);\n          scheduler.prepareStageTasks();\n          prepareView(ecIns, true);\n          prepareView(ecIns, false);\n          scheduler.plan();\n        };\n        /**\n         * Prepare view instances of charts and components\n         */\n\n\n        prepareView = function (ecIns, isComponent) {\n          var ecModel = ecIns._model;\n          var scheduler = ecIns._scheduler;\n          var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;\n          var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;\n          var zr = ecIns._zr;\n          var api = ecIns._api;\n\n          for (var i = 0; i < viewList.length; i++) {\n            viewList[i].__alive = false;\n          }\n\n          isComponent ? ecModel.eachComponent(function (componentType, model) {\n            componentType !== 'series' && doPrepare(model);\n          }) : ecModel.eachSeries(doPrepare);\n\n          function doPrepare(model) {\n            // By default view will be reused if possible for the case that `setOption` with \"notMerge\"\n            // mode and need to enable transition animation. (Usually, when they have the same id, or\n            // especially no id but have the same type & name & index. See the `model.id` generation\n            // rule in `makeIdAndName` and `viewId` generation rule here).\n            // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that\n            // the new model has nothing to do with the old model.\n            var requireNewView = model.__requireNewView; // This command should not work twice.\n\n            model.__requireNewView = false; // Consider: id same and type changed.\n\n            var viewId = '_ec_' + model.id + '_' + model.type;\n            var view = !requireNewView && viewMap[viewId];\n\n            if (!view) {\n              var classType = parseClassType(model.type);\n              var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : // FIXME:TS\n              // (ChartView as ChartViewConstructor).getClass('series', classType.sub)\n              // For backward compat, still support a chart type declared as only subType\n              // like \"liquidfill\", but recommend \"series.liquidfill\"\n              // But need a base class to make a type series.\n              ChartView.getClass(classType.sub);\n\n              if (\"development\" !== 'production') {\n                assert(Clazz, classType.sub + ' does not exist.');\n              }\n\n              view = new Clazz();\n              view.init(ecModel, api);\n              viewMap[viewId] = view;\n              viewList.push(view);\n              zr.add(view.group);\n            }\n\n            model.__viewId = view.__id = viewId;\n            view.__alive = true;\n            view.__model = model;\n            view.group.__ecComponentInfo = {\n              mainType: model.mainType,\n              index: model.componentIndex\n            };\n            !isComponent && scheduler.prepareView(view, model, ecModel, api);\n          }\n\n          for (var i = 0; i < viewList.length;) {\n            var view = viewList[i];\n\n            if (!view.__alive) {\n              !isComponent && view.renderTask.dispose();\n              zr.remove(view.group);\n              view.dispose(ecModel, api);\n              viewList.splice(i, 1);\n\n              if (viewMap[view.__id] === view) {\n                delete viewMap[view.__id];\n              }\n\n              view.__id = view.group.__ecComponentInfo = null;\n            } else {\n              i++;\n            }\n          }\n        };\n\n        updateDirectly = function (ecIns, method, payload, mainType, subType) {\n          var ecModel = ecIns._model;\n          ecModel.setUpdatePayload(payload); // broadcast\n\n          if (!mainType) {\n            // FIXME\n            // Chart will not be update directly here, except set dirty.\n            // But there is no such scenario now.\n            each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView);\n            return;\n          }\n\n          var query = {};\n          query[mainType + 'Id'] = payload[mainType + 'Id'];\n          query[mainType + 'Index'] = payload[mainType + 'Index'];\n          query[mainType + 'Name'] = payload[mainType + 'Name'];\n          var condition = {\n            mainType: mainType,\n            query: query\n          };\n          subType && (condition.subType = subType); // subType may be '' by parseClassType;\n\n          var excludeSeriesId = payload.excludeSeriesId;\n          var excludeSeriesIdMap;\n\n          if (excludeSeriesId != null) {\n            excludeSeriesIdMap = createHashMap();\n            each(normalizeToArray(excludeSeriesId), function (id) {\n              var modelId = convertOptionIdName(id, null);\n\n              if (modelId != null) {\n                excludeSeriesIdMap.set(modelId, true);\n              }\n            });\n          } // If dispatchAction before setOption, do nothing.\n\n\n          ecModel && ecModel.eachComponent(condition, function (model) {\n            var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n            if (isExcluded) {\n              return;\n            }\n\n            if (isHighDownPayload(payload)) {\n              if (model instanceof SeriesModel) {\n                if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur && !model.get(['emphasis', 'disabled'])) {\n                  blurSeriesFromHighlightPayload(model, payload, ecIns._api);\n                }\n              } else {\n                var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api),\n                    focusSelf = _a.focusSelf,\n                    dispatchers = _a.dispatchers;\n\n                if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) {\n                  blurComponent(model.mainType, model.componentIndex, ecIns._api);\n                } // PENDING:\n                // Whether to put this \"enter emphasis\" code in `ComponentView`,\n                // which will be the same as `ChartView` but might be not necessary\n                // and will be far from this logic.\n\n\n                if (dispatchers) {\n                  each(dispatchers, function (dispatcher) {\n                    payload.type === HIGHLIGHT_ACTION_TYPE ? enterEmphasis(dispatcher) : leaveEmphasis(dispatcher);\n                  });\n                }\n              }\n            } else if (isSelectChangePayload(payload)) {\n              // TODO geo\n              if (model instanceof SeriesModel) {\n                toggleSelectionFromPayload(model, payload, ecIns._api);\n                updateSeriesElementSelection(model);\n                markStatusToUpdate(ecIns);\n              }\n            }\n          }, ecIns);\n          ecModel && ecModel.eachComponent(condition, function (model) {\n            var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n            if (isExcluded) {\n              return;\n            }\n            callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]);\n          }, ecIns);\n\n          function callView(view) {\n            view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload);\n          }\n        };\n\n        updateMethods = {\n          prepareAndUpdate: function (payload) {\n            prepare(this);\n            updateMethods.update.call(this, payload, {\n              // Needs to mark option changed if newOption is given.\n              // It's from MagicType.\n              // TODO If use a separate flag optionChanged in payload?\n              optionChanged: payload.newOption != null\n            });\n          },\n          update: function (payload, updateParams) {\n            var ecModel = this._model;\n            var api = this._api;\n            var zr = this._zr;\n            var coordSysMgr = this._coordSysMgr;\n            var scheduler = this._scheduler; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload);\n            scheduler.restoreData(ecModel, payload);\n            scheduler.performSeriesTasks(ecModel); // TODO\n            // Save total ecModel here for undo/redo (after restoring data and before processing data).\n            // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.\n            // Create new coordinate system each update\n            // In LineView may save the old coordinate system and use it to get the original point.\n\n            coordSysMgr.create(ecModel, api);\n            scheduler.performDataProcessorTasks(ecModel, payload); // Current stream render is not supported in data process. So we can update\n            // stream modes after data processing, where the filtered data is used to\n            // determine whether to use progressive rendering.\n\n            updateStreamModes(this, ecModel); // We update stream modes before coordinate system updated, then the modes info\n            // can be fetched when coord sys updating (consider the barGrid extent fix). But\n            // the drawback is the full coord info can not be fetched. Fortunately this full\n            // coord is not required in stream mode updater currently.\n\n            coordSysMgr.update(ecModel, api);\n            clearColorPalette(ecModel);\n            scheduler.performVisualTasks(ecModel, payload);\n            render(this, ecModel, api, payload, updateParams); // Set background\n\n            var backgroundColor = ecModel.get('backgroundColor') || 'transparent';\n            var darkMode = ecModel.get('darkMode');\n            zr.setBackgroundColor(backgroundColor); // Force set dark mode.\n\n            if (darkMode != null && darkMode !== 'auto') {\n              zr.setDarkMode(darkMode);\n            }\n\n            lifecycle.trigger('afterupdate', ecModel, api);\n          },\n          updateTransform: function (payload) {\n            var _this = this;\n\n            var ecModel = this._model;\n            var api = this._api; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload); // ChartView.markUpdateMethod(payload, 'updateTransform');\n\n            var componentDirtyList = [];\n            ecModel.eachComponent(function (componentType, componentModel) {\n              if (componentType === 'series') {\n                return;\n              }\n\n              var componentView = _this.getViewOfComponentModel(componentModel);\n\n              if (componentView && componentView.__alive) {\n                if (componentView.updateTransform) {\n                  var result = componentView.updateTransform(componentModel, ecModel, api, payload);\n                  result && result.update && componentDirtyList.push(componentView);\n                } else {\n                  componentDirtyList.push(componentView);\n                }\n              }\n            });\n            var seriesDirtyMap = createHashMap();\n            ecModel.eachSeries(function (seriesModel) {\n              var chartView = _this._chartsMap[seriesModel.__viewId];\n\n              if (chartView.updateTransform) {\n                var result = chartView.updateTransform(seriesModel, ecModel, api, payload);\n                result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);\n              } else {\n                seriesDirtyMap.set(seriesModel.uid, 1);\n              }\n            });\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n            // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              setDirty: true,\n              dirtyMap: seriesDirtyMap\n            }); // Currently, not call render of components. Geo render cost a lot.\n            // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);\n\n\n            renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap);\n            lifecycle.trigger('afterupdate', ecModel, api);\n          },\n          updateView: function (payload) {\n            var ecModel = this._model; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload);\n            ChartView.markUpdateMethod(payload, 'updateView');\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              setDirty: true\n            });\n\n            render(this, ecModel, this._api, payload, {});\n            lifecycle.trigger('afterupdate', ecModel, this._api);\n          },\n          updateVisual: function (payload) {\n            // updateMethods.update.call(this, payload);\n            var _this = this;\n\n            var ecModel = this._model; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload); // clear all visual\n\n            ecModel.eachSeries(function (seriesModel) {\n              seriesModel.getData().clearAllVisual();\n            }); // Perform visual\n\n            ChartView.markUpdateMethod(payload, 'updateVisual');\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              visualType: 'visual',\n              setDirty: true\n            });\n\n            ecModel.eachComponent(function (componentType, componentModel) {\n              if (componentType !== 'series') {\n                var componentView = _this.getViewOfComponentModel(componentModel);\n\n                componentView && componentView.__alive && componentView.updateVisual(componentModel, ecModel, _this._api, payload);\n              }\n            });\n            ecModel.eachSeries(function (seriesModel) {\n              var chartView = _this._chartsMap[seriesModel.__viewId];\n              chartView.updateVisual(seriesModel, ecModel, _this._api, payload);\n            });\n            lifecycle.trigger('afterupdate', ecModel, this._api);\n          },\n          updateLayout: function (payload) {\n            updateMethods.update.call(this, payload);\n          }\n        };\n\n        doConvertPixel = function (ecIns, methodName, finder, value) {\n          if (ecIns._disposed) {\n            disposedWarning(ecIns.id);\n            return;\n          }\n\n          var ecModel = ecIns._model;\n\n          var coordSysList = ecIns._coordSysMgr.getCoordinateSystems();\n\n          var result;\n          var parsedFinder = parseFinder(ecModel, finder);\n\n          for (var i = 0; i < coordSysList.length; i++) {\n            var coordSys = coordSysList[i];\n\n            if (coordSys[methodName] && (result = coordSys[methodName](ecModel, parsedFinder, value)) != null) {\n              return result;\n            }\n          }\n\n          if (\"development\" !== 'production') {\n            warn('No coordinate system that supports ' + methodName + ' found by the given finder.');\n          }\n        };\n\n        updateStreamModes = function (ecIns, ecModel) {\n          var chartsMap = ecIns._chartsMap;\n          var scheduler = ecIns._scheduler;\n          ecModel.eachSeries(function (seriesModel) {\n            scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);\n          });\n        };\n\n        doDispatchAction = function (payload, silent) {\n          var _this = this;\n\n          var ecModel = this.getModel();\n          var payloadType = payload.type;\n          var escapeConnect = payload.escapeConnect;\n          var actionWrap = actions[payloadType];\n          var actionInfo = actionWrap.actionInfo;\n          var cptTypeTmp = (actionInfo.update || 'update').split(':');\n          var updateMethod = cptTypeTmp.pop();\n          var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]);\n          this[IN_MAIN_PROCESS_KEY] = true;\n          var payloads = [payload];\n          var batched = false; // Batch action\n\n          if (payload.batch) {\n            batched = true;\n            payloads = map(payload.batch, function (item) {\n              item = defaults(extend({}, item), payload);\n              item.batch = null;\n              return item;\n            });\n          }\n\n          var eventObjBatch = [];\n          var eventObj;\n          var isSelectChange = isSelectChangePayload(payload);\n          var isHighDown = isHighDownPayload(payload); // Only leave blur once if there are multiple batches.\n\n          if (isHighDown) {\n            allLeaveBlur(this._api);\n          }\n\n          each(payloads, function (batchItem) {\n            // Action can specify the event by return it.\n            eventObj = actionWrap.action(batchItem, _this._model, _this._api); // Emit event outside\n\n            eventObj = eventObj || extend({}, batchItem); // Convert type to eventType\n\n            eventObj.type = actionInfo.event || eventObj.type;\n            eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.\n\n            if (isHighDown) {\n              var _a = preParseFinder(payload),\n                  queryOptionMap = _a.queryOptionMap,\n                  mainTypeSpecified = _a.mainTypeSpecified;\n\n              var componentMainType = mainTypeSpecified ? queryOptionMap.keys()[0] : 'series';\n              updateDirectly(_this, updateMethod, batchItem, componentMainType);\n              markStatusToUpdate(_this);\n            } else if (isSelectChange) {\n              // At present `dispatchAction({ type: 'select', ... })` is not supported on components.\n              // geo still use 'geoselect'.\n              updateDirectly(_this, updateMethod, batchItem, 'series');\n              markStatusToUpdate(_this);\n            } else if (cptType) {\n              updateDirectly(_this, updateMethod, batchItem, cptType.main, cptType.sub);\n            }\n          });\n\n          if (updateMethod !== 'none' && !isHighDown && !isSelectChange && !cptType) {\n            try {\n              // Still dirty\n              if (this[PENDING_UPDATE]) {\n                prepare(this);\n                updateMethods.update.call(this, payload);\n                this[PENDING_UPDATE] = null;\n              } else {\n                updateMethods[updateMethod].call(this, payload);\n              }\n            } catch (e) {\n              this[IN_MAIN_PROCESS_KEY] = false;\n              throw e;\n            }\n          } // Follow the rule of action batch\n\n\n          if (batched) {\n            eventObj = {\n              type: actionInfo.event || payloadType,\n              escapeConnect: escapeConnect,\n              batch: eventObjBatch\n            };\n          } else {\n            eventObj = eventObjBatch[0];\n          }\n\n          this[IN_MAIN_PROCESS_KEY] = false;\n\n          if (!silent) {\n            var messageCenter = this._messageCenter;\n            messageCenter.trigger(eventObj.type, eventObj); // Extra triggered 'selectchanged' event\n\n            if (isSelectChange) {\n              var newObj = {\n                type: 'selectchanged',\n                escapeConnect: escapeConnect,\n                selected: getAllSelectedIndices(ecModel),\n                isFromClick: payload.isFromClick || false,\n                fromAction: payload.type,\n                fromActionPayload: payload\n              };\n              messageCenter.trigger(newObj.type, newObj);\n            }\n          }\n        };\n\n        flushPendingActions = function (silent) {\n          var pendingActions = this._pendingActions;\n\n          while (pendingActions.length) {\n            var payload = pendingActions.shift();\n            doDispatchAction.call(this, payload, silent);\n          }\n        };\n\n        triggerUpdatedEvent = function (silent) {\n          !silent && this.trigger('updated');\n        };\n        /**\n         * Event `rendered` is triggered when zr\n         * rendered. It is useful for realtime\n         * snapshot (reflect animation).\n         *\n         * Event `finished` is triggered when:\n         * (1) zrender rendering finished.\n         * (2) initial animation finished.\n         * (3) progressive rendering finished.\n         * (4) no pending action.\n         * (5) no delayed setOption needs to be processed.\n         */\n\n\n        bindRenderedEvent = function (zr, ecIns) {\n          zr.on('rendered', function (params) {\n            ecIns.trigger('rendered', params); // The `finished` event should not be triggered repeatedly,\n            // so it should only be triggered when rendering indeed happens\n            // in zrender. (Consider the case that dipatchAction is keep\n            // triggering when mouse move).\n\n            if ( // Although zr is dirty if initial animation is not finished\n            // and this checking is called on frame, we also check\n            // animation finished for robustness.\n            zr.animation.isFinished() && !ecIns[PENDING_UPDATE] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) {\n              ecIns.trigger('finished');\n            }\n          });\n        };\n\n        bindMouseEvent = function (zr, ecIns) {\n          zr.on('mouseover', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n            if (dispatcher) {\n              handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api);\n              markStatusToUpdate(ecIns);\n            }\n          }).on('mouseout', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n            if (dispatcher) {\n              handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api);\n              markStatusToUpdate(ecIns);\n            }\n          }).on('click', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, function (target) {\n              return getECData(target).dataIndex != null;\n            }, true);\n\n            if (dispatcher) {\n              var actionType = dispatcher.selected ? 'unselect' : 'select';\n              var ecData = getECData(dispatcher);\n\n              ecIns._api.dispatchAction({\n                type: actionType,\n                dataType: ecData.dataType,\n                dataIndexInside: ecData.dataIndex,\n                seriesIndex: ecData.seriesIndex,\n                isFromClick: true\n              });\n            }\n          });\n        };\n\n        function clearColorPalette(ecModel) {\n          ecModel.clearColorPalette();\n          ecModel.eachSeries(function (seriesModel) {\n            seriesModel.clearColorPalette();\n          });\n        }\n\n        function allocateZlevels(ecModel) {\n          var componentZLevels = [];\n          var seriesZLevels = [];\n          var hasSeperateZLevel = false;\n          ecModel.eachComponent(function (componentType, componentModel) {\n            var zlevel = componentModel.get('zlevel') || 0;\n            var z = componentModel.get('z') || 0;\n            var zlevelKey = componentModel.getZLevelKey();\n            hasSeperateZLevel = hasSeperateZLevel || !!zlevelKey;\n            (componentType === 'series' ? seriesZLevels : componentZLevels).push({\n              zlevel: zlevel,\n              z: z,\n              idx: componentModel.componentIndex,\n              type: componentType,\n              key: zlevelKey\n            });\n          });\n\n          if (hasSeperateZLevel) {\n            // Series after component\n            var zLevels = componentZLevels.concat(seriesZLevels);\n            var lastSeriesZLevel_1;\n            var lastSeriesKey_1;\n            sort(zLevels, function (a, b) {\n              if (a.zlevel === b.zlevel) {\n                return a.z - b.z;\n              }\n\n              return a.zlevel - b.zlevel;\n            });\n            each(zLevels, function (item) {\n              var componentModel = ecModel.getComponent(item.type, item.idx);\n              var zlevel = item.zlevel;\n              var key = item.key;\n\n              if (lastSeriesZLevel_1 != null) {\n                zlevel = Math.max(lastSeriesZLevel_1, zlevel);\n              }\n\n              if (key) {\n                if (zlevel === lastSeriesZLevel_1 && key !== lastSeriesKey_1) {\n                  zlevel++;\n                }\n\n                lastSeriesKey_1 = key;\n              } else if (lastSeriesKey_1) {\n                if (zlevel === lastSeriesZLevel_1) {\n                  zlevel++;\n                }\n\n                lastSeriesKey_1 = '';\n              }\n\n              lastSeriesZLevel_1 = zlevel;\n              componentModel.setZLevel(zlevel);\n            });\n          }\n        }\n\n        render = function (ecIns, ecModel, api, payload, updateParams) {\n          allocateZlevels(ecModel);\n          renderComponents(ecIns, ecModel, api, payload, updateParams);\n          each(ecIns._chartsViews, function (chart) {\n            chart.__alive = false;\n          });\n          renderSeries(ecIns, ecModel, api, payload, updateParams); // Remove groups of unrendered charts\n\n          each(ecIns._chartsViews, function (chart) {\n            if (!chart.__alive) {\n              chart.remove(ecModel, api);\n            }\n          });\n        };\n\n        renderComponents = function (ecIns, ecModel, api, payload, updateParams, dirtyList) {\n          each(dirtyList || ecIns._componentsViews, function (componentView) {\n            var componentModel = componentView.__model;\n            clearStates(componentModel, componentView);\n            componentView.render(componentModel, ecModel, api, payload);\n            updateZ(componentModel, componentView);\n            updateStates(componentModel, componentView);\n          });\n        };\n        /**\n         * Render each chart and component\n         */\n\n\n        renderSeries = function (ecIns, ecModel, api, payload, updateParams, dirtyMap) {\n          // Render all charts\n          var scheduler = ecIns._scheduler;\n          updateParams = extend(updateParams || {}, {\n            updatedSeries: ecModel.getSeries()\n          }); // TODO progressive?\n\n          lifecycle.trigger('series:beforeupdate', ecModel, api, updateParams);\n          var unfinished = false;\n          ecModel.eachSeries(function (seriesModel) {\n            var chartView = ecIns._chartsMap[seriesModel.__viewId];\n            chartView.__alive = true;\n            var renderTask = chartView.renderTask;\n            scheduler.updatePayload(renderTask, payload); // TODO states on marker.\n\n            clearStates(seriesModel, chartView);\n\n            if (dirtyMap && dirtyMap.get(seriesModel.uid)) {\n              renderTask.dirty();\n            }\n\n            if (renderTask.perform(scheduler.getPerformArgs(renderTask))) {\n              unfinished = true;\n            }\n\n            chartView.group.silent = !!seriesModel.get('silent'); // Should not call markRedraw on group, because it will disable zrender\n            // incremental render (always render from the __startIndex each frame)\n            // chartView.group.markRedraw();\n\n            updateBlend(seriesModel, chartView);\n            updateSeriesElementSelection(seriesModel);\n          });\n          scheduler.unfinished = unfinished || scheduler.unfinished;\n          lifecycle.trigger('series:layoutlabels', ecModel, api, updateParams); // transition after label is layouted.\n\n          lifecycle.trigger('series:transition', ecModel, api, updateParams);\n          ecModel.eachSeries(function (seriesModel) {\n            var chartView = ecIns._chartsMap[seriesModel.__viewId]; // Update Z after labels updated. Before applying states.\n\n            updateZ(seriesModel, chartView); // NOTE: Update states after label is updated.\n            // label should be in normal status when layouting.\n\n            updateStates(seriesModel, chartView);\n          }); // If use hover layer\n\n          updateHoverLayerStatus(ecIns, ecModel);\n          lifecycle.trigger('series:afterupdate', ecModel, api, updateParams);\n        };\n\n        markStatusToUpdate = function (ecIns) {\n          ecIns[STATUS_NEEDS_UPDATE_KEY] = true; // Wake up zrender if it's sleep. Let it update states in the next frame.\n\n          ecIns.getZr().wakeUp();\n        };\n\n        applyChangedStates = function (ecIns) {\n          if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) {\n            return;\n          }\n\n          ecIns.getZr().storage.traverse(function (el) {\n            // Not applied on removed elements, it may still in fading.\n            if (isElementRemoved(el)) {\n              return;\n            }\n\n            applyElementStates(el);\n          });\n          ecIns[STATUS_NEEDS_UPDATE_KEY] = false;\n        };\n\n        function applyElementStates(el) {\n          var newStates = [];\n          var oldStates = el.currentStates; // Keep other states.\n\n          for (var i = 0; i < oldStates.length; i++) {\n            var stateName = oldStates[i];\n\n            if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) {\n              newStates.push(stateName);\n            }\n          } // Only use states when it's exists.\n\n\n          if (el.selected && el.states.select) {\n            newStates.push('select');\n          }\n\n          if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) {\n            newStates.push('emphasis');\n          } else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) {\n            newStates.push('blur');\n          }\n\n          el.useStates(newStates);\n        }\n\n        function updateHoverLayerStatus(ecIns, ecModel) {\n          var zr = ecIns._zr;\n          var storage = zr.storage;\n          var elCount = 0;\n          storage.traverse(function (el) {\n            if (!el.isGroup) {\n              elCount++;\n            }\n          });\n\n          if (elCount > ecModel.get('hoverLayerThreshold') && !env.node && !env.worker) {\n            ecModel.eachSeries(function (seriesModel) {\n              if (seriesModel.preventUsingHoverLayer) {\n                return;\n              }\n\n              var chartView = ecIns._chartsMap[seriesModel.__viewId];\n\n              if (chartView.__alive) {\n                chartView.eachRendered(function (el) {\n                  if (el.states.emphasis) {\n                    el.states.emphasis.hoverLayer = true;\n                  }\n                });\n              }\n            });\n          }\n        }\n        /**\n         * Update chart and blend.\n         */\n\n        function updateBlend(seriesModel, chartView) {\n          var blendMode = seriesModel.get('blendMode') || null;\n          chartView.eachRendered(function (el) {\n            // FIXME marker and other components\n            if (!el.isGroup) {\n              // DON'T mark the element dirty. In case element is incremental and don't want to rerender.\n              el.style.blend = blendMode;\n            }\n          });\n        }\n\n        function updateZ(model, view) {\n          if (model.preventAutoZ) {\n            return;\n          }\n\n          var z = model.get('z') || 0;\n          var zlevel = model.get('zlevel') || 0; // Set z and zlevel\n\n          view.eachRendered(function (el) {\n            doUpdateZ(el, z, zlevel, -Infinity); // Don't traverse the children because it has been traversed in _updateZ.\n\n            return true;\n          });\n        }\n\n        function doUpdateZ(el, z, zlevel, maxZ2) {\n          // Group may also have textContent\n          var label = el.getTextContent();\n          var labelLine = el.getTextGuideLine();\n          var isGroup = el.isGroup;\n\n          if (isGroup) {\n            // set z & zlevel of children elements of Group\n            var children = el.childrenRef();\n\n            for (var i = 0; i < children.length; i++) {\n              maxZ2 = Math.max(doUpdateZ(children[i], z, zlevel, maxZ2), maxZ2);\n            }\n          } else {\n            // not Group\n            el.z = z;\n            el.zlevel = zlevel;\n            maxZ2 = Math.max(el.z2, maxZ2);\n          } // always set z and zlevel if label/labelLine exists\n\n\n          if (label) {\n            label.z = z;\n            label.zlevel = zlevel; // lift z2 of text content\n            // TODO if el.emphasis.z2 is spcefied, what about textContent.\n\n            isFinite(maxZ2) && (label.z2 = maxZ2 + 2);\n          }\n\n          if (labelLine) {\n            var textGuideLineConfig = el.textGuideLineConfig;\n            labelLine.z = z;\n            labelLine.zlevel = zlevel;\n            isFinite(maxZ2) && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1));\n          }\n\n          return maxZ2;\n        } // Clear states without animation.\n        // TODO States on component.\n\n\n        function clearStates(model, view) {\n          view.eachRendered(function (el) {\n            // Not applied on removed elements, it may still in fading.\n            if (isElementRemoved(el)) {\n              return;\n            }\n\n            var textContent = el.getTextContent();\n            var textGuide = el.getTextGuideLine();\n\n            if (el.stateTransition) {\n              el.stateTransition = null;\n            }\n\n            if (textContent && textContent.stateTransition) {\n              textContent.stateTransition = null;\n            }\n\n            if (textGuide && textGuide.stateTransition) {\n              textGuide.stateTransition = null;\n            } // TODO If el is incremental.\n\n\n            if (el.hasState()) {\n              el.prevStates = el.currentStates;\n              el.clearStates();\n            } else if (el.prevStates) {\n              el.prevStates = null;\n            }\n          });\n        }\n\n        function updateStates(model, view) {\n          var stateAnimationModel = model.getModel('stateAnimation');\n          var enableAnimation = model.isAnimationEnabled();\n          var duration = stateAnimationModel.get('duration');\n          var stateTransition = duration > 0 ? {\n            duration: duration,\n            delay: stateAnimationModel.get('delay'),\n            easing: stateAnimationModel.get('easing') // additive: stateAnimationModel.get('additive')\n\n          } : null;\n          view.eachRendered(function (el) {\n            if (el.states && el.states.emphasis) {\n              // Not applied on removed elements, it may still in fading.\n              if (isElementRemoved(el)) {\n                return;\n              }\n\n              if (el instanceof Path) {\n                savePathStates(el);\n              } // Only updated on changed element. In case element is incremental and don't want to rerender.\n              // TODO, a more proper way?\n\n\n              if (el.__dirty) {\n                var prevStates = el.prevStates; // Restore states without animation\n\n                if (prevStates) {\n                  el.useStates(prevStates);\n                }\n              } // Update state transition and enable animation again.\n\n\n              if (enableAnimation) {\n                el.stateTransition = stateTransition;\n                var textContent = el.getTextContent();\n                var textGuide = el.getTextGuideLine(); // TODO Is it necessary to animate label?\n\n                if (textContent) {\n                  textContent.stateTransition = stateTransition;\n                }\n\n                if (textGuide) {\n                  textGuide.stateTransition = stateTransition;\n                }\n              } // Use highlighted and selected flag to toggle states.\n\n\n              if (el.__dirty) {\n                applyElementStates(el);\n              }\n            }\n          });\n        }\n\n        createExtensionAPI = function (ecIns) {\n          return new (\n          /** @class */\n          function (_super) {\n            __extends(class_1, _super);\n\n            function class_1() {\n              return _super !== null && _super.apply(this, arguments) || this;\n            }\n\n            class_1.prototype.getCoordinateSystems = function () {\n              return ecIns._coordSysMgr.getCoordinateSystems();\n            };\n\n            class_1.prototype.getComponentByElement = function (el) {\n              while (el) {\n                var modelInfo = el.__ecComponentInfo;\n\n                if (modelInfo != null) {\n                  return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index);\n                }\n\n                el = el.parent;\n              }\n            };\n\n            class_1.prototype.enterEmphasis = function (el, highlightDigit) {\n              enterEmphasis(el, highlightDigit);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveEmphasis = function (el, highlightDigit) {\n              leaveEmphasis(el, highlightDigit);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.enterBlur = function (el) {\n              enterBlur(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveBlur = function (el) {\n              leaveBlur(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.enterSelect = function (el) {\n              enterSelect(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveSelect = function (el) {\n              leaveSelect(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.getModel = function () {\n              return ecIns.getModel();\n            };\n\n            class_1.prototype.getViewOfComponentModel = function (componentModel) {\n              return ecIns.getViewOfComponentModel(componentModel);\n            };\n\n            class_1.prototype.getViewOfSeriesModel = function (seriesModel) {\n              return ecIns.getViewOfSeriesModel(seriesModel);\n            };\n\n            return class_1;\n          }(ExtensionAPI))(ecIns);\n        };\n\n        enableConnect = function (chart) {\n          function updateConnectedChartsStatus(charts, status) {\n            for (var i = 0; i < charts.length; i++) {\n              var otherChart = charts[i];\n              otherChart[CONNECT_STATUS_KEY] = status;\n            }\n          }\n\n          each(eventActionMap, function (actionType, eventType) {\n            chart._messageCenter.on(eventType, function (event) {\n              if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) {\n                if (event && event.escapeConnect) {\n                  return;\n                }\n\n                var action_1 = chart.makeActionFromEvent(event);\n                var otherCharts_1 = [];\n                each(instances$1, function (otherChart) {\n                  if (otherChart !== chart && otherChart.group === chart.group) {\n                    otherCharts_1.push(otherChart);\n                  }\n                });\n                updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_PENDING);\n                each(otherCharts_1, function (otherChart) {\n                  if (otherChart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_UPDATING) {\n                    otherChart.dispatchAction(action_1);\n                  }\n                });\n                updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_UPDATED);\n              }\n            });\n          });\n        };\n      }();\n\n      return ECharts;\n    }(Eventful);\n\n    var echartsProto = ECharts.prototype;\n    echartsProto.on = createRegisterEventWithLowercaseECharts('on');\n    echartsProto.off = createRegisterEventWithLowercaseECharts('off');\n    /**\n     * @deprecated\n     */\n    // @ts-ignore\n\n    echartsProto.one = function (eventName, cb, ctx) {\n      var self = this;\n      deprecateLog('ECharts#one is deprecated.');\n\n      function wrapped() {\n        var args2 = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args2[_i] = arguments[_i];\n        }\n\n        cb && cb.apply && cb.apply(this, args2); // @ts-ignore\n\n        self.off(eventName, wrapped);\n      }\n\n      this.on.call(this, eventName, wrapped, ctx);\n    };\n\n    var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];\n\n    function disposedWarning(id) {\n      if (\"development\" !== 'production') {\n        warn('Instance ' + id + ' has been disposed');\n      }\n    }\n\n    var actions = {};\n    /**\n     * Map eventType to actionType\n     */\n\n    var eventActionMap = {};\n    var dataProcessorFuncs = [];\n    var optionPreprocessorFuncs = [];\n    var visualFuncs = [];\n    var themeStorage = {};\n    var loadingEffects = {};\n    var instances$1 = {};\n    var connectedGroups = {};\n    var idBase = +new Date() - 0;\n    var groupIdBase = +new Date() - 0;\n    var DOM_ATTRIBUTE_KEY = '_echarts_instance_';\n    /**\n     * @param opts.devicePixelRatio Use window.devicePixelRatio by default\n     * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart.\n     * @param opts.width Use clientWidth of the input `dom` by default.\n     *        Can be 'auto' (the same as null/undefined)\n     * @param opts.height Use clientHeight of the input `dom` by default.\n     *        Can be 'auto' (the same as null/undefined)\n     * @param opts.locale Specify the locale.\n     * @param opts.useDirtyRect Enable dirty rectangle rendering or not.\n     */\n\n    function init$1(dom, theme, opts) {\n      var isClient = !(opts && opts.ssr);\n\n      if (isClient) {\n        if (\"development\" !== 'production') {\n          if (!dom) {\n            throw new Error('Initialize failed: invalid dom.');\n          }\n        }\n\n        var existInstance = getInstanceByDom(dom);\n\n        if (existInstance) {\n          if (\"development\" !== 'production') {\n            warn('There is a chart instance already initialized on the dom.');\n          }\n\n          return existInstance;\n        }\n\n        if (\"development\" !== 'production') {\n          if (isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth && (!opts || opts.width == null) || !dom.clientHeight && (!opts || opts.height == null))) {\n            warn('Can\\'t get DOM width or height. Please check ' + 'dom.clientWidth and dom.clientHeight. They should not be 0.' + 'For example, you may need to call this in the callback ' + 'of window.onload.');\n          }\n        }\n      }\n\n      var chart = new ECharts(dom, theme, opts);\n      chart.id = 'ec_' + idBase++;\n      instances$1[chart.id] = chart;\n      isClient && setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);\n      enableConnect(chart);\n      lifecycle.trigger('afterinit', chart);\n      return chart;\n    }\n    /**\n     * @usage\n     * (A)\n     * ```js\n     * let chart1 = echarts.init(dom1);\n     * let chart2 = echarts.init(dom2);\n     * chart1.group = 'xxx';\n     * chart2.group = 'xxx';\n     * echarts.connect('xxx');\n     * ```\n     * (B)\n     * ```js\n     * let chart1 = echarts.init(dom1);\n     * let chart2 = echarts.init(dom2);\n     * echarts.connect('xxx', [chart1, chart2]);\n     * ```\n     */\n\n    function connect(groupId) {\n      // Is array of charts\n      if (isArray(groupId)) {\n        var charts = groupId;\n        groupId = null; // If any chart has group\n\n        each(charts, function (chart) {\n          if (chart.group != null) {\n            groupId = chart.group;\n          }\n        });\n        groupId = groupId || 'g_' + groupIdBase++;\n        each(charts, function (chart) {\n          chart.group = groupId;\n        });\n      }\n\n      connectedGroups[groupId] = true;\n      return groupId;\n    }\n    /**\n     * @deprecated\n     */\n\n    function disConnect(groupId) {\n      connectedGroups[groupId] = false;\n    }\n    /**\n     * Alias and backward compatibility\n     */\n\n    var disconnect = disConnect;\n    /**\n     * Dispose a chart instance\n     */\n\n    function dispose$1(chart) {\n      if (isString(chart)) {\n        chart = instances$1[chart];\n      } else if (!(chart instanceof ECharts)) {\n        // Try to treat as dom\n        chart = getInstanceByDom(chart);\n      }\n\n      if (chart instanceof ECharts && !chart.isDisposed()) {\n        chart.dispose();\n      }\n    }\n    function getInstanceByDom(dom) {\n      return instances$1[getAttribute(dom, DOM_ATTRIBUTE_KEY)];\n    }\n    function getInstanceById(key) {\n      return instances$1[key];\n    }\n    /**\n     * Register theme\n     */\n\n    function registerTheme(name, theme) {\n      themeStorage[name] = theme;\n    }\n    /**\n     * Register option preprocessor\n     */\n\n    function registerPreprocessor(preprocessorFunc) {\n      if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) {\n        optionPreprocessorFuncs.push(preprocessorFunc);\n      }\n    }\n    function registerProcessor(priority, processor) {\n      normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT);\n    }\n    /**\n     * Register postIniter\n     * @param {Function} postInitFunc\n     */\n\n    function registerPostInit(postInitFunc) {\n      registerUpdateLifecycle('afterinit', postInitFunc);\n    }\n    /**\n     * Register postUpdater\n     * @param {Function} postUpdateFunc\n     */\n\n    function registerPostUpdate(postUpdateFunc) {\n      registerUpdateLifecycle('afterupdate', postUpdateFunc);\n    }\n    function registerUpdateLifecycle(name, cb) {\n      lifecycle.on(name, cb);\n    }\n    function registerAction(actionInfo, eventName, action) {\n      if (isFunction(eventName)) {\n        action = eventName;\n        eventName = '';\n      }\n\n      var actionType = isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = {\n        event: eventName\n      }][0]; // Event name is all lowercase\n\n      actionInfo.event = (actionInfo.event || actionType).toLowerCase();\n      eventName = actionInfo.event;\n\n      if (eventActionMap[eventName]) {\n        // Already registered.\n        return;\n      } // Validate action type and event name.\n\n\n      assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));\n\n      if (!actions[actionType]) {\n        actions[actionType] = {\n          action: action,\n          actionInfo: actionInfo\n        };\n      }\n\n      eventActionMap[eventName] = actionType;\n    }\n    function registerCoordinateSystem(type, coordSysCreator) {\n      CoordinateSystemManager.register(type, coordSysCreator);\n    }\n    /**\n     * Get dimensions of specified coordinate system.\n     * @param {string} type\n     * @return {Array.<string|Object>}\n     */\n\n    function getCoordinateSystemDimensions(type) {\n      var coordSysCreator = CoordinateSystemManager.get(type);\n\n      if (coordSysCreator) {\n        return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice();\n      }\n    }\n\n    function registerLayout(priority, layoutTask) {\n      normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');\n    }\n\n    function registerVisual(priority, visualTask) {\n      normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');\n    }\n    var registeredTasks = [];\n\n    function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {\n      if (isFunction(priority) || isObject(priority)) {\n        fn = priority;\n        priority = defaultPriority;\n      }\n\n      if (\"development\" !== 'production') {\n        if (isNaN(priority) || priority == null) {\n          throw new Error('Illegal priority');\n        } // Check duplicate\n\n\n        each(targetList, function (wrap) {\n          assert(wrap.__raw !== fn);\n        });\n      } // Already registered\n\n\n      if (indexOf(registeredTasks, fn) >= 0) {\n        return;\n      }\n\n      registeredTasks.push(fn);\n      var stageHandler = Scheduler.wrapStageHandler(fn, visualType);\n      stageHandler.__prio = priority;\n      stageHandler.__raw = fn;\n      targetList.push(stageHandler);\n    }\n\n    function registerLoading(name, loadingFx) {\n      loadingEffects[name] = loadingFx;\n    }\n    /**\n     * ZRender need a canvas context to do measureText.\n     * But in node environment canvas may be created by node-canvas.\n     * So we need to specify how to create a canvas instead of using document.createElement('canvas')\n     *\n     *\n     * @deprecated use setPlatformAPI({ createCanvas }) instead.\n     *\n     * @example\n     *     let Canvas = require('canvas');\n     *     let echarts = require('echarts');\n     *     echarts.setCanvasCreator(function () {\n     *         // Small size is enough.\n     *         return new Canvas(32, 32);\n     *     });\n     */\n\n    function setCanvasCreator(creator) {\n      if (\"development\" !== 'production') {\n        deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.');\n      }\n\n      setPlatformAPI({\n        createCanvas: creator\n      });\n    }\n    /**\n     * The parameters and usage: see `geoSourceManager.registerMap`.\n     * Compatible with previous `echarts.registerMap`.\n     */\n\n    function registerMap(mapName, geoJson, specialAreas) {\n      var registerMap = getImpl('registerMap');\n      registerMap && registerMap(mapName, geoJson, specialAreas);\n    }\n    function getMap(mapName) {\n      var getMap = getImpl('getMap');\n      return getMap && getMap(mapName);\n    }\n    var registerTransform = registerExternalTransform;\n    /**\n     * Globa dispatchAction to a specified chart instance.\n     */\n    // export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters<ECharts['dispatchAction']>[1]) {\n    //     if (!payload || !payload.chartId) {\n    //         // Must have chartId to find chart\n    //         return;\n    //     }\n    //     const chart = instances[payload.chartId];\n    //     if (chart) {\n    //         chart.dispatchAction(payload, opt);\n    //     }\n    // }\n    // Builtin global visual\n\n    registerVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataColorPaletteTask);\n    registerVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask);\n    registerVisual(PRIORITY_VISUAL_DECAL, decalVisual);\n    registerPreprocessor(globalBackwardCompat);\n    registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack);\n    registerLoading('default', defaultLoading); // Default actions\n\n    registerAction({\n      type: HIGHLIGHT_ACTION_TYPE,\n      event: HIGHLIGHT_ACTION_TYPE,\n      update: HIGHLIGHT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: DOWNPLAY_ACTION_TYPE,\n      event: DOWNPLAY_ACTION_TYPE,\n      update: DOWNPLAY_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: SELECT_ACTION_TYPE,\n      event: SELECT_ACTION_TYPE,\n      update: SELECT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: UNSELECT_ACTION_TYPE,\n      event: UNSELECT_ACTION_TYPE,\n      update: UNSELECT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: TOGGLE_SELECT_ACTION_TYPE,\n      event: TOGGLE_SELECT_ACTION_TYPE,\n      update: TOGGLE_SELECT_ACTION_TYPE\n    }, noop); // Default theme\n\n    registerTheme('light', lightTheme);\n    registerTheme('dark', theme); // For backward compatibility, where the namespace `dataTool` will\n    // be mounted on `echarts` is the extension `dataTool` is imported.\n\n    var dataTool = {};\n\n    var extensions = [];\n    var extensionRegisters = {\n      registerPreprocessor: registerPreprocessor,\n      registerProcessor: registerProcessor,\n      registerPostInit: registerPostInit,\n      registerPostUpdate: registerPostUpdate,\n      registerUpdateLifecycle: registerUpdateLifecycle,\n      registerAction: registerAction,\n      registerCoordinateSystem: registerCoordinateSystem,\n      registerLayout: registerLayout,\n      registerVisual: registerVisual,\n      registerTransform: registerTransform,\n      registerLoading: registerLoading,\n      registerMap: registerMap,\n      registerImpl: registerImpl,\n      PRIORITY: PRIORITY,\n      ComponentModel: ComponentModel,\n      ComponentView: ComponentView,\n      SeriesModel: SeriesModel,\n      ChartView: ChartView,\n      // TODO Use ComponentModel and SeriesModel instead of Constructor\n      registerComponentModel: function (ComponentModelClass) {\n        ComponentModel.registerClass(ComponentModelClass);\n      },\n      registerComponentView: function (ComponentViewClass) {\n        ComponentView.registerClass(ComponentViewClass);\n      },\n      registerSeriesModel: function (SeriesModelClass) {\n        SeriesModel.registerClass(SeriesModelClass);\n      },\n      registerChartView: function (ChartViewClass) {\n        ChartView.registerClass(ChartViewClass);\n      },\n      registerSubTypeDefaulter: function (componentType, defaulter) {\n        ComponentModel.registerSubTypeDefaulter(componentType, defaulter);\n      },\n      registerPainter: function (painterType, PainterCtor) {\n        registerPainter(painterType, PainterCtor);\n      }\n    };\n    function use(ext) {\n      if (isArray(ext)) {\n        // use([ChartLine, ChartBar]);\n        each(ext, function (singleExt) {\n          use(singleExt);\n        });\n        return;\n      }\n\n      if (indexOf(extensions, ext) >= 0) {\n        return;\n      }\n\n      extensions.push(ext);\n\n      if (isFunction(ext)) {\n        ext = {\n          install: ext\n        };\n      }\n\n      ext.install(extensionRegisters);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) {\n      return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1;\n    }\n\n    function defaultKeyGetter(item) {\n      return item;\n    }\n\n    var DataDiffer =\n    /** @class */\n    function () {\n      /**\n       * @param context Can be visited by this.context in callback.\n       */\n      function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'.\n      diffMode) {\n        this._old = oldArr;\n        this._new = newArr;\n        this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;\n        this._newKeyGetter = newKeyGetter || defaultKeyGetter; // Visible in callback via `this.context`;\n\n        this.context = context;\n        this._diffModeMultiple = diffMode === 'multiple';\n      }\n      /**\n       * Callback function when add a data\n       */\n\n\n      DataDiffer.prototype.add = function (func) {\n        this._add = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data\n       */\n\n\n      DataDiffer.prototype.update = function (func) {\n        this._update = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateManyToOne = function (func) {\n        this._updateManyToOne = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateOneToMany = function (func) {\n        this._updateOneToMany = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateManyToMany = function (func) {\n        this._updateManyToMany = func;\n        return this;\n      };\n      /**\n       * Callback function when remove a data\n       */\n\n\n      DataDiffer.prototype.remove = function (func) {\n        this._remove = func;\n        return this;\n      };\n\n      DataDiffer.prototype.execute = function () {\n        this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne']();\n      };\n\n      DataDiffer.prototype._executeOneToOne = function () {\n        var oldArr = this._old;\n        var newArr = this._new;\n        var newDataIndexMap = {};\n        var oldDataKeyArr = new Array(oldArr.length);\n        var newDataKeyArr = new Array(newArr.length);\n\n        this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter');\n\n        this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n        for (var i = 0; i < oldArr.length; i++) {\n          var oldKey = oldDataKeyArr[i];\n          var newIdxMapVal = newDataIndexMap[oldKey];\n          var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); // idx can never be empty array here. see 'set null' logic below.\n\n          if (newIdxMapValLen > 1) {\n            // Consider there is duplicate key (for example, use dataItem.name as key).\n            // We should make sure every item in newArr and oldArr can be visited.\n            var newIdx = newIdxMapVal.shift();\n\n            if (newIdxMapVal.length === 1) {\n              newDataIndexMap[oldKey] = newIdxMapVal[0];\n            }\n\n            this._update && this._update(newIdx, i);\n          } else if (newIdxMapValLen === 1) {\n            newDataIndexMap[oldKey] = null;\n            this._update && this._update(newIdxMapVal, i);\n          } else {\n            this._remove && this._remove(i);\n          }\n        }\n\n        this._performRestAdd(newDataKeyArr, newDataIndexMap);\n      };\n      /**\n       * For example, consider the case:\n       * oldData: [o0, o1, o2, o3, o4, o5, o6, o7],\n       * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],\n       * Where:\n       *     o0, o1, n0 has key 'a' (many to one)\n       *     o5, n4, n5, n6 has key 'b' (one to many)\n       *     o2, n1 has key 'c' (one to one)\n       *     n2, n3 has key 'd' (add)\n       *     o3, o4 has key 'e' (remove)\n       *     o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)\n       * Then:\n       *     (The order of the following directives are not ensured.)\n       *     this._updateManyToOne(n0, [o0, o1]);\n       *     this._updateOneToMany([n4, n5, n6], o5);\n       *     this._update(n1, o2);\n       *     this._remove(o3);\n       *     this._remove(o4);\n       *     this._remove(o6);\n       *     this._remove(o7);\n       *     this._add(n2);\n       *     this._add(n3);\n       *     this._add(n7);\n       *     this._add(n8);\n       */\n\n\n      DataDiffer.prototype._executeMultiple = function () {\n        var oldArr = this._old;\n        var newArr = this._new;\n        var oldDataIndexMap = {};\n        var newDataIndexMap = {};\n        var oldDataKeyArr = [];\n        var newDataKeyArr = [];\n\n        this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');\n\n        this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n        for (var i = 0; i < oldDataKeyArr.length; i++) {\n          var oldKey = oldDataKeyArr[i];\n          var oldIdxMapVal = oldDataIndexMap[oldKey];\n          var newIdxMapVal = newDataIndexMap[oldKey];\n          var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal);\n          var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n          if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {\n            this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {\n            this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {\n            this._update && this._update(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {\n            this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen > 1) {\n            for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {\n              this._remove && this._remove(oldIdxMapVal[i_1]);\n            }\n          } else {\n            this._remove && this._remove(oldIdxMapVal);\n          }\n        }\n\n        this._performRestAdd(newDataKeyArr, newDataIndexMap);\n      };\n\n      DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) {\n        for (var i = 0; i < newDataKeyArr.length; i++) {\n          var newKey = newDataKeyArr[i];\n          var newIdxMapVal = newDataIndexMap[newKey];\n          var idxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n          if (idxMapValLen > 1) {\n            for (var j = 0; j < idxMapValLen; j++) {\n              this._add && this._add(newIdxMapVal[j]);\n            }\n          } else if (idxMapValLen === 1) {\n            this._add && this._add(newIdxMapVal);\n          } // Support both `newDataKeyArr` are duplication removed or not removed.\n\n\n          newDataIndexMap[newKey] = null;\n        }\n      };\n\n      DataDiffer.prototype._initIndexMap = function (arr, // Can be null.\n      map, // In 'byKey', the output `keyArr` is duplication removed.\n      // In 'byIndex', the output `keyArr` is not duplication removed and\n      //     its indices are accurately corresponding to `arr`.\n      keyArr, keyGetterName) {\n        var cbModeMultiple = this._diffModeMultiple;\n\n        for (var i = 0; i < arr.length; i++) {\n          // Add prefix to avoid conflict with Object.prototype.\n          var key = '_ec_' + this[keyGetterName](arr[i], i);\n\n          if (!cbModeMultiple) {\n            keyArr[i] = key;\n          }\n\n          if (!map) {\n            continue;\n          }\n\n          var idxMapVal = map[key];\n          var idxMapValLen = dataIndexMapValueLength(idxMapVal);\n\n          if (idxMapValLen === 0) {\n            // Simple optimize: in most cases, one index has one key,\n            // do not need array.\n            map[key] = i;\n\n            if (cbModeMultiple) {\n              keyArr.push(key);\n            }\n          } else if (idxMapValLen === 1) {\n            map[key] = [idxMapVal, i];\n          } else {\n            idxMapVal.push(i);\n          }\n        }\n      };\n\n      return DataDiffer;\n    }();\n\n    var DimensionUserOuput =\n    /** @class */\n    function () {\n      function DimensionUserOuput(encode, dimRequest) {\n        this._encode = encode;\n        this._schema = dimRequest;\n      }\n\n      DimensionUserOuput.prototype.get = function () {\n        return {\n          // Do not generate full dimension name until fist used.\n          fullDimensions: this._getFullDimensionNames(),\n          encode: this._encode\n        };\n      };\n      /**\n       * Get all data store dimension names.\n       * Theoretically a series data store is defined both by series and used dataset (if any).\n       * If some dimensions are omitted for performance reason in `this.dimensions`,\n       * the dimension name may not be auto-generated if user does not specify a dimension name.\n       * In this case, the dimension name is `null`/`undefined`.\n       */\n\n\n      DimensionUserOuput.prototype._getFullDimensionNames = function () {\n        if (!this._cachedDimNames) {\n          this._cachedDimNames = this._schema ? this._schema.makeOutputDimensionNames() : [];\n        }\n\n        return this._cachedDimNames;\n      };\n\n      return DimensionUserOuput;\n    }();\n    function summarizeDimensions(data, schema) {\n      var summary = {};\n      var encode = summary.encode = {};\n      var notExtraCoordDimMap = createHashMap();\n      var defaultedLabel = [];\n      var defaultedTooltip = [];\n      var userOutputEncode = {};\n      each(data.dimensions, function (dimName) {\n        var dimItem = data.getDimensionInfo(dimName);\n        var coordDim = dimItem.coordDim;\n\n        if (coordDim) {\n          if (\"development\" !== 'production') {\n            assert(VISUAL_DIMENSIONS.get(coordDim) == null);\n          }\n\n          var coordDimIndex = dimItem.coordDimIndex;\n          getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName;\n\n          if (!dimItem.isExtraCoord) {\n            notExtraCoordDimMap.set(coordDim, 1); // Use the last coord dim (and label friendly) as default label,\n            // because when dataset is used, it is hard to guess which dimension\n            // can be value dimension. If both show x, y on label is not look good,\n            // and conventionally y axis is focused more.\n\n            if (mayLabelDimType(dimItem.type)) {\n              defaultedLabel[0] = dimName;\n            } // User output encode do not contain generated coords.\n            // And it only has index. User can use index to retrieve value from the raw item array.\n\n\n            getOrCreateEncodeArr(userOutputEncode, coordDim)[coordDimIndex] = data.getDimensionIndex(dimItem.name);\n          }\n\n          if (dimItem.defaultTooltip) {\n            defaultedTooltip.push(dimName);\n          }\n        }\n\n        VISUAL_DIMENSIONS.each(function (v, otherDim) {\n          var encodeArr = getOrCreateEncodeArr(encode, otherDim);\n          var dimIndex = dimItem.otherDims[otherDim];\n\n          if (dimIndex != null && dimIndex !== false) {\n            encodeArr[dimIndex] = dimItem.name;\n          }\n        });\n      });\n      var dataDimsOnCoord = [];\n      var encodeFirstDimNotExtra = {};\n      notExtraCoordDimMap.each(function (v, coordDim) {\n        var dimArr = encode[coordDim];\n        encodeFirstDimNotExtra[coordDim] = dimArr[0]; // Not necessary to remove duplicate, because a data\n        // dim canot on more than one coordDim.\n\n        dataDimsOnCoord = dataDimsOnCoord.concat(dimArr);\n      });\n      summary.dataDimsOnCoord = dataDimsOnCoord;\n      summary.dataDimIndicesOnCoord = map(dataDimsOnCoord, function (dimName) {\n        return data.getDimensionInfo(dimName).storeDimIndex;\n      });\n      summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra;\n      var encodeLabel = encode.label; // FIXME `encode.label` is not recommended, because formatter cannot be set\n      // in this way. Use label.formatter instead. Maybe remove this approach someday.\n\n      if (encodeLabel && encodeLabel.length) {\n        defaultedLabel = encodeLabel.slice();\n      }\n\n      var encodeTooltip = encode.tooltip;\n\n      if (encodeTooltip && encodeTooltip.length) {\n        defaultedTooltip = encodeTooltip.slice();\n      } else if (!defaultedTooltip.length) {\n        defaultedTooltip = defaultedLabel.slice();\n      }\n\n      encode.defaultedLabel = defaultedLabel;\n      encode.defaultedTooltip = defaultedTooltip;\n      summary.userOutput = new DimensionUserOuput(userOutputEncode, schema);\n      return summary;\n    }\n\n    function getOrCreateEncodeArr(encode, dim) {\n      if (!encode.hasOwnProperty(dim)) {\n        encode[dim] = [];\n      }\n\n      return encode[dim];\n    } // FIXME:TS should be type `AxisType`\n\n\n    function getDimensionTypeByAxis(axisType) {\n      return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float';\n    }\n\n    function mayLabelDimType(dimType) {\n      // In most cases, ordinal and time do not suitable for label.\n      // Ordinal info can be displayed on axis. Time is too long.\n      return !(dimType === 'ordinal' || dimType === 'time');\n    } // function findTheLastDimMayLabel(data) {\n    //     // Get last value dim\n    //     let dimensions = data.dimensions.slice();\n    //     let valueType;\n    //     let valueDim;\n    //     while (dimensions.length && (\n    //         valueDim = dimensions.pop(),\n    //         valueType = data.getDimensionInfo(valueDim).type,\n    //         valueType === 'ordinal' || valueType === 'time'\n    //     )) {} // jshint ignore:line\n    //     return valueDim;\n    // }\n\n    var SeriesDimensionDefine =\n    /** @class */\n    function () {\n      /**\n       * @param opt All of the fields will be shallow copied.\n       */\n      function SeriesDimensionDefine(opt) {\n        /**\n         * The format of `otherDims` is:\n         * ```js\n         * {\n         *     tooltip?: number\n         *     label?: number\n         *     itemName?: number\n         *     seriesName?: number\n         * }\n         * ```\n         *\n         * A `series.encode` can specified these fields:\n         * ```js\n         * encode: {\n         *     // \"3, 1, 5\" is the index of data dimension.\n         *     tooltip: [3, 1, 5],\n         *     label: [0, 3],\n         *     ...\n         * }\n         * ```\n         * `otherDims` is the parse result of the `series.encode` above, like:\n         * ```js\n         * // Suppose the index of this data dimension is `3`.\n         * this.otherDims = {\n         *     // `3` is at the index `0` of the `encode.tooltip`\n         *     tooltip: 0,\n         *     // `3` is at the index `1` of the `encode.label`\n         *     label: 1\n         * };\n         * ```\n         *\n         * This prop should never be `null`/`undefined` after initialized.\n         */\n        this.otherDims = {};\n\n        if (opt != null) {\n          extend(this, opt);\n        }\n      }\n\n      return SeriesDimensionDefine;\n    }();\n\n    var inner$4 = makeInner();\n    var dimTypeShort = {\n      float: 'f',\n      int: 'i',\n      ordinal: 'o',\n      number: 'n',\n      time: 't'\n    };\n    /**\n     * Represents the dimension requirement of a series.\n     *\n     * NOTICE:\n     * When there are too many dimensions in dataset and many series, only the used dimensions\n     * (i.e., used by coord sys and declared in `series.encode`) are add to `dimensionDefineList`.\n     * But users may query data by other unused dimension names.\n     * In this case, users can only query data if and only if they have defined dimension names\n     * via ec option, so we provide `getDimensionIndexFromSource`, which only query them from\n     * `source` dimensions.\n     */\n\n    var SeriesDataSchema =\n    /** @class */\n    function () {\n      function SeriesDataSchema(opt) {\n        this.dimensions = opt.dimensions;\n        this._dimOmitted = opt.dimensionOmitted;\n        this.source = opt.source;\n        this._fullDimCount = opt.fullDimensionCount;\n\n        this._updateDimOmitted(opt.dimensionOmitted);\n      }\n\n      SeriesDataSchema.prototype.isDimensionOmitted = function () {\n        return this._dimOmitted;\n      };\n\n      SeriesDataSchema.prototype._updateDimOmitted = function (dimensionOmitted) {\n        this._dimOmitted = dimensionOmitted;\n\n        if (!dimensionOmitted) {\n          return;\n        }\n\n        if (!this._dimNameMap) {\n          this._dimNameMap = ensureSourceDimNameMap(this.source);\n        }\n      };\n      /**\n       * @caution Can only be used when `dimensionOmitted: true`.\n       *\n       * Get index by user defined dimension name (i.e., not internal generate name).\n       * That is, get index from `dimensionsDefine`.\n       * If no `dimensionsDefine`, or no name get, return -1.\n       */\n\n\n      SeriesDataSchema.prototype.getSourceDimensionIndex = function (dimName) {\n        return retrieve2(this._dimNameMap.get(dimName), -1);\n      };\n      /**\n       * @caution Can only be used when `dimensionOmitted: true`.\n       *\n       * Notice: may return `null`/`undefined` if user not specify dimension names.\n       */\n\n\n      SeriesDataSchema.prototype.getSourceDimension = function (dimIndex) {\n        var dimensionsDefine = this.source.dimensionsDefine;\n\n        if (dimensionsDefine) {\n          return dimensionsDefine[dimIndex];\n        }\n      };\n\n      SeriesDataSchema.prototype.makeStoreSchema = function () {\n        var dimCount = this._fullDimCount;\n        var willRetrieveDataByName = shouldRetrieveDataByName(this.source);\n        var makeHashStrict = !shouldOmitUnusedDimensions(dimCount); // If source don't have dimensions or series don't omit unsed dimensions.\n        // Generate from seriesDimList directly\n\n        var dimHash = '';\n        var dims = [];\n\n        for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < dimCount; fullDimIdx++) {\n          var property = void 0;\n          var type = void 0;\n          var ordinalMeta = void 0;\n          var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n          if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n            property = willRetrieveDataByName ? seriesDimDef.name : null;\n            type = seriesDimDef.type;\n            ordinalMeta = seriesDimDef.ordinalMeta;\n            seriesDimIdx++;\n          } else {\n            var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n            if (sourceDimDef) {\n              property = willRetrieveDataByName ? sourceDimDef.name : null;\n              type = sourceDimDef.type;\n            }\n          }\n\n          dims.push({\n            property: property,\n            type: type,\n            ordinalMeta: ordinalMeta\n          }); // If retrieving data by index,\n          //   use <index, type, ordinalMeta> to determine whether data can be shared.\n          //   (Because in this case there might be no dimension name defined in dataset, but indices always exists).\n          //   (Indices are always 0, 1, 2, ..., so we can ignore them to shorten the hash).\n          // Otherwise if retrieving data by property name (like `data: [{aa: 123, bb: 765}, ...]`),\n          //   use <property, type, ordinalMeta> in hash.\n\n          if (willRetrieveDataByName && property != null // For data stack, we have make sure each series has its own dim on this store.\n          // So we do not add property to hash to make sure they can share this store.\n          && (!seriesDimDef || !seriesDimDef.isCalculationCoord)) {\n            dimHash += makeHashStrict // Use escape character '`' in case that property name contains '$'.\n            ? property.replace(/\\`/g, '`1').replace(/\\$/g, '`2') // For better performance, when there are large dimensions, tolerant this defects that hardly meet.\n            : property;\n          }\n\n          dimHash += '$';\n          dimHash += dimTypeShort[type] || 'f';\n\n          if (ordinalMeta) {\n            dimHash += ordinalMeta.uid;\n          }\n\n          dimHash += '$';\n        } // Source from endpoint(usually series) will be read differently\n        // when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different.\n        // So we use this three props as key.\n\n\n        var source = this.source;\n        var hash = [source.seriesLayoutBy, source.startIndex, dimHash].join('$$');\n        return {\n          dimensions: dims,\n          hash: hash\n        };\n      };\n\n      SeriesDataSchema.prototype.makeOutputDimensionNames = function () {\n        var result = [];\n\n        for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < this._fullDimCount; fullDimIdx++) {\n          var name_1 = void 0;\n          var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n          if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n            if (!seriesDimDef.isCalculationCoord) {\n              name_1 = seriesDimDef.name;\n            }\n\n            seriesDimIdx++;\n          } else {\n            var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n            if (sourceDimDef) {\n              name_1 = sourceDimDef.name;\n            }\n          }\n\n          result.push(name_1);\n        }\n\n        return result;\n      };\n\n      SeriesDataSchema.prototype.appendCalculationDimension = function (dimDef) {\n        this.dimensions.push(dimDef);\n        dimDef.isCalculationCoord = true;\n        this._fullDimCount++; // If append dimension on a data store, consider the store\n        // might be shared by different series, series dimensions not\n        // really map to store dimensions.\n\n        this._updateDimOmitted(true);\n      };\n\n      return SeriesDataSchema;\n    }();\n    function isSeriesDataSchema(schema) {\n      return schema instanceof SeriesDataSchema;\n    }\n    function createDimNameMap(dimsDef) {\n      var dataDimNameMap = createHashMap();\n\n      for (var i = 0; i < (dimsDef || []).length; i++) {\n        var dimDefItemRaw = dimsDef[i];\n        var userDimName = isObject(dimDefItemRaw) ? dimDefItemRaw.name : dimDefItemRaw;\n\n        if (userDimName != null && dataDimNameMap.get(userDimName) == null) {\n          dataDimNameMap.set(userDimName, i);\n        }\n      }\n\n      return dataDimNameMap;\n    }\n    function ensureSourceDimNameMap(source) {\n      var innerSource = inner$4(source);\n      return innerSource.dimNameMap || (innerSource.dimNameMap = createDimNameMap(source.dimensionsDefine));\n    }\n    function shouldOmitUnusedDimensions(dimCount) {\n      return dimCount > 30;\n    }\n\n    var isObject$2 = isObject;\n    var map$1 = map;\n    var CtorInt32Array$1 = typeof Int32Array === 'undefined' ? Array : Int32Array; // Use prefix to avoid index to be the same as otherIdList[idx],\n    // which will cause weird update animation.\n\n    var ID_PREFIX = 'e\\0\\0';\n    var INDEX_NOT_FOUND = -1; // type SeriesDimensionIndex = DimensionIndex;\n\n    var TRANSFERABLE_PROPERTIES = ['hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', '_dimSummary', 'userOutput', '_rawData', '_dimValueGetter', '_nameDimIdx', '_idDimIdx', '_nameRepeatCount'];\n    var CLONE_PROPERTIES = ['_approximateExtent']; // -----------------------------\n    // Internal method declarations:\n    // -----------------------------\n\n    var prepareInvertedIndex;\n    var getId;\n    var getIdNameFromStore;\n    var normalizeDimensions;\n    var transferProperties;\n    var cloneListForMapAndSample;\n    var makeIdFromName;\n\n    var SeriesData =\n    /** @class */\n    function () {\n      /**\n       * @param dimensionsInput.dimensions\n       *        For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].\n       *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius\n       */\n      function SeriesData(dimensionsInput, hostModel) {\n        this.type = 'list';\n        this._dimOmitted = false;\n        this._nameList = [];\n        this._idList = []; // Models of data option is stored sparse for optimizing memory cost\n        // Never used yet (not used yet).\n        // private _optionModels: Model[] = [];\n        // Global visual properties after visual coding\n\n        this._visual = {}; // Global layout properties.\n\n        this._layout = {}; // Item visual properties after visual coding\n\n        this._itemVisuals = []; // Item layout properties after layout\n\n        this._itemLayouts = []; // Graphic elements\n\n        this._graphicEls = []; // key: dim, value: extent\n\n        this._approximateExtent = {};\n        this._calculationInfo = {}; // Having detected that there is data item is non primitive type\n        // (in type `OptionDataItemObject`).\n        // Like `data: [ { value: xx, itemStyle: {...} }, ...]`\n        // At present it only happen in `SOURCE_FORMAT_ORIGINAL`.\n\n        this.hasItemOption = false; // Methods that create a new list based on this list should be listed here.\n        // Notice that those method should `RETURN` the new list.\n\n        this.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map']; // Methods that change indices of this list should be listed here.\n\n        this.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];\n        this.DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'];\n        var dimensions;\n        var assignStoreDimIdx = false;\n\n        if (isSeriesDataSchema(dimensionsInput)) {\n          dimensions = dimensionsInput.dimensions;\n          this._dimOmitted = dimensionsInput.isDimensionOmitted();\n          this._schema = dimensionsInput;\n        } else {\n          assignStoreDimIdx = true;\n          dimensions = dimensionsInput;\n        }\n\n        dimensions = dimensions || ['x', 'y'];\n        var dimensionInfos = {};\n        var dimensionNames = [];\n        var invertedIndicesMap = {};\n        var needsHasOwn = false;\n        var emptyObj = {};\n\n        for (var i = 0; i < dimensions.length; i++) {\n          // Use the original dimensions[i], where other flag props may exists.\n          var dimInfoInput = dimensions[i];\n          var dimensionInfo = isString(dimInfoInput) ? new SeriesDimensionDefine({\n            name: dimInfoInput\n          }) : !(dimInfoInput instanceof SeriesDimensionDefine) ? new SeriesDimensionDefine(dimInfoInput) : dimInfoInput;\n          var dimensionName = dimensionInfo.name;\n          dimensionInfo.type = dimensionInfo.type || 'float';\n\n          if (!dimensionInfo.coordDim) {\n            dimensionInfo.coordDim = dimensionName;\n            dimensionInfo.coordDimIndex = 0;\n          }\n\n          var otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {};\n          dimensionNames.push(dimensionName);\n          dimensionInfos[dimensionName] = dimensionInfo;\n\n          if (emptyObj[dimensionName] != null) {\n            needsHasOwn = true;\n          }\n\n          if (dimensionInfo.createInvertedIndices) {\n            invertedIndicesMap[dimensionName] = [];\n          }\n\n          if (otherDims.itemName === 0) {\n            this._nameDimIdx = i;\n          }\n\n          if (otherDims.itemId === 0) {\n            this._idDimIdx = i;\n          }\n\n          if (\"development\" !== 'production') {\n            assert(assignStoreDimIdx || dimensionInfo.storeDimIndex >= 0);\n          }\n\n          if (assignStoreDimIdx) {\n            dimensionInfo.storeDimIndex = i;\n          }\n        }\n\n        this.dimensions = dimensionNames;\n        this._dimInfos = dimensionInfos;\n\n        this._initGetDimensionInfo(needsHasOwn);\n\n        this.hostModel = hostModel;\n        this._invertedIndicesMap = invertedIndicesMap;\n\n        if (this._dimOmitted) {\n          var dimIdxToName_1 = this._dimIdxToName = createHashMap();\n          each(dimensionNames, function (dimName) {\n            dimIdxToName_1.set(dimensionInfos[dimName].storeDimIndex, dimName);\n          });\n        }\n      }\n      /**\n       *\n       * Get concrete dimension name by dimension name or dimension index.\n       * If input a dimension name, do not validate whether the dimension name exits.\n       *\n       * @caution\n       * @param dim Must make sure the dimension is `SeriesDimensionLoose`.\n       * Because only those dimensions will have auto-generated dimension names if not\n       * have a user-specified name, and other dimensions will get a return of null/undefined.\n       *\n       * @notice Because of this reason, should better use `getDimensionIndex` instead, for examples:\n       * ```js\n       * const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx);\n       * ```\n       *\n       * @return Concrete dim name.\n       */\n\n\n      SeriesData.prototype.getDimension = function (dim) {\n        var dimIdx = this._recognizeDimIndex(dim);\n\n        if (dimIdx == null) {\n          return dim;\n        }\n\n        dimIdx = dim;\n\n        if (!this._dimOmitted) {\n          return this.dimensions[dimIdx];\n        } // Retrieve from series dimension definition because it probably contains\n        // generated dimension name (like 'x', 'y').\n\n\n        var dimName = this._dimIdxToName.get(dimIdx);\n\n        if (dimName != null) {\n          return dimName;\n        }\n\n        var sourceDimDef = this._schema.getSourceDimension(dimIdx);\n\n        if (sourceDimDef) {\n          return sourceDimDef.name;\n        }\n      };\n      /**\n       * Get dimension index in data store. Return -1 if not found.\n       * Can be used to index value from getRawValue.\n       */\n\n\n      SeriesData.prototype.getDimensionIndex = function (dim) {\n        var dimIdx = this._recognizeDimIndex(dim);\n\n        if (dimIdx != null) {\n          return dimIdx;\n        }\n\n        if (dim == null) {\n          return -1;\n        }\n\n        var dimInfo = this._getDimInfo(dim);\n\n        return dimInfo ? dimInfo.storeDimIndex : this._dimOmitted ? this._schema.getSourceDimensionIndex(dim) : -1;\n      };\n      /**\n       * The meanings of the input parameter `dim`:\n       *\n       * + If dim is a number (e.g., `1`), it means the index of the dimension.\n       *   For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.\n       * + If dim is a number-like string (e.g., `\"1\"`):\n       *     + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`,\n       *        it means that concrete name.\n       *     + If not, it will be converted to a number, which means the index of the dimension.\n       *        (why? because of the backward compatibility. We have been tolerating number-like string in\n       *        dimension setting, although now it seems that it is not a good idea.)\n       *     For example, `visualMap[i].dimension: \"1\"` is the same meaning as `visualMap[i].dimension: 1`,\n       *     if no dimension name is defined as `\"1\"`.\n       * + If dim is a not-number-like string, it means the concrete dim name.\n       *   For example, it can be be default name `\"x\"`, `\"y\"`, `\"z\"`, `\"lng\"`, `\"lat\"`, `\"angle\"`, `\"radius\"`,\n       *   or customized in `dimensions` property of option like `\"age\"`.\n       *\n       * @return recognized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`).\n       */\n\n\n      SeriesData.prototype._recognizeDimIndex = function (dim) {\n        if (isNumber(dim) // If being a number-like string but not being defined as a dimension name.\n        || dim != null && !isNaN(dim) && !this._getDimInfo(dim) && (!this._dimOmitted || this._schema.getSourceDimensionIndex(dim) < 0)) {\n          return +dim;\n        }\n      };\n\n      SeriesData.prototype._getStoreDimIndex = function (dim) {\n        var dimIdx = this.getDimensionIndex(dim);\n\n        if (\"development\" !== 'production') {\n          if (dimIdx == null) {\n            throw new Error('Unknown dimension ' + dim);\n          }\n        }\n\n        return dimIdx;\n      };\n      /**\n       * Get type and calculation info of particular dimension\n       * @param dim\n       *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius\n       *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'\n       */\n\n\n      SeriesData.prototype.getDimensionInfo = function (dim) {\n        // Do not clone, because there may be categories in dimInfo.\n        return this._getDimInfo(this.getDimension(dim));\n      };\n\n      SeriesData.prototype._initGetDimensionInfo = function (needsHasOwn) {\n        var dimensionInfos = this._dimInfos;\n        this._getDimInfo = needsHasOwn ? function (dimName) {\n          return dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined;\n        } : function (dimName) {\n          return dimensionInfos[dimName];\n        };\n      };\n      /**\n       * concrete dimension name list on coord.\n       */\n\n\n      SeriesData.prototype.getDimensionsOnCoord = function () {\n        return this._dimSummary.dataDimsOnCoord.slice();\n      };\n\n      SeriesData.prototype.mapDimension = function (coordDim, idx) {\n        var dimensionsSummary = this._dimSummary;\n\n        if (idx == null) {\n          return dimensionsSummary.encodeFirstDimNotExtra[coordDim];\n        }\n\n        var dims = dimensionsSummary.encode[coordDim];\n        return dims ? dims[idx] : null;\n      };\n\n      SeriesData.prototype.mapDimensionsAll = function (coordDim) {\n        var dimensionsSummary = this._dimSummary;\n        var dims = dimensionsSummary.encode[coordDim];\n        return (dims || []).slice();\n      };\n\n      SeriesData.prototype.getStore = function () {\n        return this._store;\n      };\n      /**\n       * Initialize from data\n       * @param data source or data or data store.\n       * @param nameList The name of a datum is used on data diff and\n       *        default label/tooltip.\n       *        A name can be specified in encode.itemName,\n       *        or dataItem.name (only for series option data),\n       *        or provided in nameList from outside.\n       */\n\n\n      SeriesData.prototype.initData = function (data, nameList, dimValueGetter) {\n        var _this = this;\n\n        var store;\n\n        if (data instanceof DataStore) {\n          store = data;\n        }\n\n        if (!store) {\n          var dimensions = this.dimensions;\n          var provider = isSourceInstance(data) || isArrayLike(data) ? new DefaultDataProvider(data, dimensions.length) : data;\n          store = new DataStore();\n          var dimensionInfos = map$1(dimensions, function (dimName) {\n            return {\n              type: _this._dimInfos[dimName].type,\n              property: dimName\n            };\n          });\n          store.initData(provider, dimensionInfos, dimValueGetter);\n        }\n\n        this._store = store; // Reset\n\n        this._nameList = (nameList || []).slice();\n        this._idList = [];\n        this._nameRepeatCount = {};\n\n        this._doInit(0, store.count()); // Cache summary info for fast visit. See \"dimensionHelper\".\n        // Needs to be initialized after store is prepared.\n\n\n        this._dimSummary = summarizeDimensions(this, this._schema);\n        this.userOutput = this._dimSummary.userOutput;\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       */\n\n\n      SeriesData.prototype.appendData = function (data) {\n        var range = this._store.appendData(data);\n\n        this._doInit(range[0], range[1]);\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       * This method does not modify `rawData` (`dataProvider`), but only\n       * add values to store.\n       *\n       * The final count will be increased by `Math.max(values.length, names.length)`.\n       *\n       * @param values That is the SourceType: 'arrayRows', like\n       *        [\n       *            [12, 33, 44],\n       *            [NaN, 43, 1],\n       *            ['-', 'asdf', 0]\n       *        ]\n       *        Each item is exactly corresponding to a dimension.\n       */\n\n\n      SeriesData.prototype.appendValues = function (values, names) {\n        var _a = this._store.appendValues(values, names.length),\n            start = _a.start,\n            end = _a.end;\n\n        var shouldMakeIdFromName = this._shouldMakeIdFromName();\n\n        this._updateOrdinalMeta();\n\n        if (names) {\n          for (var idx = start; idx < end; idx++) {\n            var sourceIdx = idx - start;\n            this._nameList[idx] = names[sourceIdx];\n\n            if (shouldMakeIdFromName) {\n              makeIdFromName(this, idx);\n            }\n          }\n        }\n      };\n\n      SeriesData.prototype._updateOrdinalMeta = function () {\n        var store = this._store;\n        var dimensions = this.dimensions;\n\n        for (var i = 0; i < dimensions.length; i++) {\n          var dimInfo = this._dimInfos[dimensions[i]];\n\n          if (dimInfo.ordinalMeta) {\n            store.collectOrdinalMeta(dimInfo.storeDimIndex, dimInfo.ordinalMeta);\n          }\n        }\n      };\n\n      SeriesData.prototype._shouldMakeIdFromName = function () {\n        var provider = this._store.getProvider();\n\n        return this._idDimIdx == null && provider.getSource().sourceFormat !== SOURCE_FORMAT_TYPED_ARRAY && !provider.fillStorage;\n      };\n\n      SeriesData.prototype._doInit = function (start, end) {\n        if (start >= end) {\n          return;\n        }\n\n        var store = this._store;\n        var provider = store.getProvider();\n\n        this._updateOrdinalMeta();\n\n        var nameList = this._nameList;\n        var idList = this._idList;\n        var sourceFormat = provider.getSource().sourceFormat;\n        var isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL; // Each data item is value\n        // [1, 2]\n        // 2\n        // Bar chart, line chart which uses category axis\n        // only gives the 'y' value. 'x' value is the indices of category\n        // Use a tempValue to normalize the value to be a (x, y) value\n        // If dataItem is {name: ...} or {id: ...}, it has highest priority.\n        // This kind of ids and names are always stored `_nameList` and `_idList`.\n\n        if (isFormatOriginal && !provider.pure) {\n          var sharedDataItem = [];\n\n          for (var idx = start; idx < end; idx++) {\n            // NOTICE: Try not to write things into dataItem\n            var dataItem = provider.getItem(idx, sharedDataItem);\n\n            if (!this.hasItemOption && isDataItemOption(dataItem)) {\n              this.hasItemOption = true;\n            }\n\n            if (dataItem) {\n              var itemName = dataItem.name;\n\n              if (nameList[idx] == null && itemName != null) {\n                nameList[idx] = convertOptionIdName(itemName, null);\n              }\n\n              var itemId = dataItem.id;\n\n              if (idList[idx] == null && itemId != null) {\n                idList[idx] = convertOptionIdName(itemId, null);\n              }\n            }\n          }\n        }\n\n        if (this._shouldMakeIdFromName()) {\n          for (var idx = start; idx < end; idx++) {\n            makeIdFromName(this, idx);\n          }\n        }\n\n        prepareInvertedIndex(this);\n      };\n      /**\n       * PENDING: In fact currently this function is only used to short-circuit\n       * the calling of `scale.unionExtentFromData` when data have been filtered by modules\n       * like \"dataZoom\". `scale.unionExtentFromData` is used to calculate data extent for series on\n       * an axis, but if a \"axis related data filter module\" is used, the extent of the axis have\n       * been fixed and no need to calling `scale.unionExtentFromData` actually.\n       * But if we add \"custom data filter\" in future, which is not \"axis related\", this method may\n       * be still needed.\n       *\n       * Optimize for the scenario that data is filtered by a given extent.\n       * Consider that if data amount is more than hundreds of thousand,\n       * extent calculation will cost more than 10ms and the cache will\n       * be erased because of the filtering.\n       */\n\n\n      SeriesData.prototype.getApproximateExtent = function (dim) {\n        return this._approximateExtent[dim] || this._store.getDataExtent(this._getStoreDimIndex(dim));\n      };\n      /**\n       * Calculate extent on a filtered data might be time consuming.\n       * Approximate extent is only used for: calculate extent of filtered data outside.\n       */\n\n\n      SeriesData.prototype.setApproximateExtent = function (extent, dim) {\n        dim = this.getDimension(dim);\n        this._approximateExtent[dim] = extent.slice();\n      };\n\n      SeriesData.prototype.getCalculationInfo = function (key) {\n        return this._calculationInfo[key];\n      };\n\n      SeriesData.prototype.setCalculationInfo = function (key, value) {\n        isObject$2(key) ? extend(this._calculationInfo, key) : this._calculationInfo[key] = value;\n      };\n      /**\n       * @return Never be null/undefined. `number` will be converted to string. Because:\n       * In most cases, name is used in display, where returning a string is more convenient.\n       * In other cases, name is used in query (see `indexOfName`), where we can keep the\n       * rule that name `2` equals to name `'2'`.\n       */\n\n\n      SeriesData.prototype.getName = function (idx) {\n        var rawIndex = this.getRawIndex(idx);\n        var name = this._nameList[rawIndex];\n\n        if (name == null && this._nameDimIdx != null) {\n          name = getIdNameFromStore(this, this._nameDimIdx, rawIndex);\n        }\n\n        if (name == null) {\n          name = '';\n        }\n\n        return name;\n      };\n\n      SeriesData.prototype._getCategory = function (dimIdx, idx) {\n        var ordinal = this._store.get(dimIdx, idx);\n\n        var ordinalMeta = this._store.getOrdinalMeta(dimIdx);\n\n        if (ordinalMeta) {\n          return ordinalMeta.categories[ordinal];\n        }\n\n        return ordinal;\n      };\n      /**\n       * @return Never null/undefined. `number` will be converted to string. Because:\n       * In all cases having encountered at present, id is used in making diff comparison, which\n       * are usually based on hash map. We can keep the rule that the internal id are always string\n       * (treat `2` is the same as `'2'`) to make the related logic simple.\n       */\n\n\n      SeriesData.prototype.getId = function (idx) {\n        return getId(this, this.getRawIndex(idx));\n      };\n\n      SeriesData.prototype.count = function () {\n        return this._store.count();\n      };\n      /**\n       * Get value. Return NaN if idx is out of range.\n       *\n       * @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead.\n       */\n\n\n      SeriesData.prototype.get = function (dim, idx) {\n        var store = this._store;\n        var dimInfo = this._dimInfos[dim];\n\n        if (dimInfo) {\n          return store.get(dimInfo.storeDimIndex, idx);\n        }\n      };\n      /**\n       * @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead.\n       */\n\n\n      SeriesData.prototype.getByRawIndex = function (dim, rawIdx) {\n        var store = this._store;\n        var dimInfo = this._dimInfos[dim];\n\n        if (dimInfo) {\n          return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx);\n        }\n      };\n\n      SeriesData.prototype.getIndices = function () {\n        return this._store.getIndices();\n      };\n\n      SeriesData.prototype.getDataExtent = function (dim) {\n        return this._store.getDataExtent(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getSum = function (dim) {\n        return this._store.getSum(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getMedian = function (dim) {\n        return this._store.getMedian(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getValues = function (dimensions, idx) {\n        var _this = this;\n\n        var store = this._store;\n        return isArray(dimensions) ? store.getValues(map$1(dimensions, function (dim) {\n          return _this._getStoreDimIndex(dim);\n        }), idx) : store.getValues(dimensions);\n      };\n      /**\n       * If value is NaN. Including '-'\n       * Only check the coord dimensions.\n       */\n\n\n      SeriesData.prototype.hasValue = function (idx) {\n        var dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord;\n\n        for (var i = 0, len = dataDimIndicesOnCoord.length; i < len; i++) {\n          // Ordinal type originally can be string or number.\n          // But when an ordinal type is used on coord, it can\n          // not be string but only number. So we can also use isNaN.\n          if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx))) {\n            return false;\n          }\n        }\n\n        return true;\n      };\n      /**\n       * Retrieve the index with given name\n       */\n\n\n      SeriesData.prototype.indexOfName = function (name) {\n        for (var i = 0, len = this._store.count(); i < len; i++) {\n          if (this.getName(i) === name) {\n            return i;\n          }\n        }\n\n        return -1;\n      };\n\n      SeriesData.prototype.getRawIndex = function (idx) {\n        return this._store.getRawIndex(idx);\n      };\n\n      SeriesData.prototype.indexOfRawIndex = function (rawIndex) {\n        return this._store.indexOfRawIndex(rawIndex);\n      };\n      /**\n       * Only support the dimension which inverted index created.\n       * Do not support other cases until required.\n       * @param dim concrete dim\n       * @param value ordinal index\n       * @return rawIndex\n       */\n\n\n      SeriesData.prototype.rawIndexOf = function (dim, value) {\n        var invertedIndices = dim && this._invertedIndicesMap[dim];\n\n        if (\"development\" !== 'production') {\n          if (!invertedIndices) {\n            throw new Error('Do not supported yet');\n          }\n        }\n\n        var rawIndex = invertedIndices[value];\n\n        if (rawIndex == null || isNaN(rawIndex)) {\n          return INDEX_NOT_FOUND;\n        }\n\n        return rawIndex;\n      };\n      /**\n       * Retrieve the index of nearest value\n       * @param dim\n       * @param value\n       * @param [maxDistance=Infinity]\n       * @return If and only if multiple indices has\n       *         the same value, they are put to the result.\n       */\n\n\n      SeriesData.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n        return this._store.indicesOfNearest(this._getStoreDimIndex(dim), value, maxDistance);\n      };\n\n      SeriesData.prototype.each = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n\n        this._store.each(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n      };\n\n      SeriesData.prototype.filterSelf = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n        this._store = this._store.filter(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n        return this;\n      };\n      /**\n       * Select data in range. (For optimization of filter)\n       * (Manually inline code, support 5 million data filtering in data zoom.)\n       */\n\n\n      SeriesData.prototype.selectRange = function (range) {\n\n        var _this = this;\n\n        var innerRange = {};\n        var dims = keys(range);\n        each(dims, function (dim) {\n          var dimIdx = _this._getStoreDimIndex(dim);\n\n          innerRange[dimIdx] = range[dim];\n        });\n        this._store = this._store.selectRange(innerRange);\n        return this;\n      };\n      /* eslint-enable max-len */\n\n\n      SeriesData.prototype.mapArray = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        ctx = ctx || this;\n        var result = [];\n        this.each(dims, function () {\n          result.push(cb && cb.apply(this, arguments));\n        }, ctx);\n        return result;\n      };\n\n      SeriesData.prototype.map = function (dims, cb, ctx, ctxCompat) {\n\n        var fCtx = ctx || ctxCompat || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.map(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n        return list;\n      };\n\n      SeriesData.prototype.modify = function (dims, cb, ctx, ctxCompat) {\n        var _this = this; // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || ctxCompat || this;\n\n        if (\"development\" !== 'production') {\n          each(normalizeDimensions(dims), function (dim) {\n            var dimInfo = _this.getDimensionInfo(dim);\n\n            if (!dimInfo.isCalculationCoord) {\n              console.error('Danger: only stack dimension can be modified');\n            }\n          });\n        }\n\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); // If do shallow clone here, if there are too many stacked series,\n        // it still cost lots of memory, because `_store.dimensions` are not shared.\n        // We should consider there probably be shallow clone happen in each series\n        // in consequent filter/map.\n\n        this._store.modify(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n      };\n      /**\n       * Large data down sampling on given dimension\n       * @param sampleIndex Sample index for name and id\n       */\n\n\n      SeriesData.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.downSample(this._getStoreDimIndex(dimension), rate, sampleValue, sampleIndex);\n        return list;\n      };\n      /**\n       * Large data down sampling using largest-triangle-three-buckets\n       * @param {string} valueDimension\n       * @param {number} targetCount\n       */\n\n\n      SeriesData.prototype.lttbDownSample = function (valueDimension, rate) {\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.lttbDownSample(this._getStoreDimIndex(valueDimension), rate);\n        return list;\n      };\n\n      SeriesData.prototype.getRawDataItem = function (idx) {\n        return this._store.getRawDataItem(idx);\n      };\n      /**\n       * Get model of one data item.\n       */\n      // TODO: Type of data item\n\n\n      SeriesData.prototype.getItemModel = function (idx) {\n        var hostModel = this.hostModel;\n        var dataItem = this.getRawDataItem(idx);\n        return new Model(dataItem, hostModel, hostModel && hostModel.ecModel);\n      };\n      /**\n       * Create a data differ\n       */\n\n\n      SeriesData.prototype.diff = function (otherList) {\n        var thisList = this;\n        return new DataDiffer(otherList ? otherList.getStore().getIndices() : [], this.getStore().getIndices(), function (idx) {\n          return getId(otherList, idx);\n        }, function (idx) {\n          return getId(thisList, idx);\n        });\n      };\n      /**\n       * Get visual property.\n       */\n\n\n      SeriesData.prototype.getVisual = function (key) {\n        var visual = this._visual;\n        return visual && visual[key];\n      };\n\n      SeriesData.prototype.setVisual = function (kvObj, val) {\n        this._visual = this._visual || {};\n\n        if (isObject$2(kvObj)) {\n          extend(this._visual, kvObj);\n        } else {\n          this._visual[kvObj] = val;\n        }\n      };\n      /**\n       * Get visual property of single data item\n       */\n      // eslint-disable-next-line\n\n\n      SeriesData.prototype.getItemVisual = function (idx, key) {\n        var itemVisual = this._itemVisuals[idx];\n        var val = itemVisual && itemVisual[key];\n\n        if (val == null) {\n          // Use global visual property\n          return this.getVisual(key);\n        }\n\n        return val;\n      };\n      /**\n       * If exists visual property of single data item\n       */\n\n\n      SeriesData.prototype.hasItemVisual = function () {\n        return this._itemVisuals.length > 0;\n      };\n      /**\n       * Make sure itemVisual property is unique\n       */\n      // TODO: use key to save visual to reduce memory.\n\n\n      SeriesData.prototype.ensureUniqueItemVisual = function (idx, key) {\n        var itemVisuals = this._itemVisuals;\n        var itemVisual = itemVisuals[idx];\n\n        if (!itemVisual) {\n          itemVisual = itemVisuals[idx] = {};\n        }\n\n        var val = itemVisual[key];\n\n        if (val == null) {\n          val = this.getVisual(key); // TODO Performance?\n\n          if (isArray(val)) {\n            val = val.slice();\n          } else if (isObject$2(val)) {\n            val = extend({}, val);\n          }\n\n          itemVisual[key] = val;\n        }\n\n        return val;\n      }; // eslint-disable-next-line\n\n\n      SeriesData.prototype.setItemVisual = function (idx, key, value) {\n        var itemVisual = this._itemVisuals[idx] || {};\n        this._itemVisuals[idx] = itemVisual;\n\n        if (isObject$2(key)) {\n          extend(itemVisual, key);\n        } else {\n          itemVisual[key] = value;\n        }\n      };\n      /**\n       * Clear itemVisuals and list visual.\n       */\n\n\n      SeriesData.prototype.clearAllVisual = function () {\n        this._visual = {};\n        this._itemVisuals = [];\n      };\n\n      SeriesData.prototype.setLayout = function (key, val) {\n        isObject$2(key) ? extend(this._layout, key) : this._layout[key] = val;\n      };\n      /**\n       * Get layout property.\n       */\n\n\n      SeriesData.prototype.getLayout = function (key) {\n        return this._layout[key];\n      };\n      /**\n       * Get layout of single data item\n       */\n\n\n      SeriesData.prototype.getItemLayout = function (idx) {\n        return this._itemLayouts[idx];\n      };\n      /**\n       * Set layout of single data item\n       */\n\n\n      SeriesData.prototype.setItemLayout = function (idx, layout, merge) {\n        this._itemLayouts[idx] = merge ? extend(this._itemLayouts[idx] || {}, layout) : layout;\n      };\n      /**\n       * Clear all layout of single data item\n       */\n\n\n      SeriesData.prototype.clearItemLayouts = function () {\n        this._itemLayouts.length = 0;\n      };\n      /**\n       * Set graphic element relative to data. It can be set as null\n       */\n\n\n      SeriesData.prototype.setItemGraphicEl = function (idx, el) {\n        var seriesIndex = this.hostModel && this.hostModel.seriesIndex;\n        setCommonECData(seriesIndex, this.dataType, idx, el);\n        this._graphicEls[idx] = el;\n      };\n\n      SeriesData.prototype.getItemGraphicEl = function (idx) {\n        return this._graphicEls[idx];\n      };\n\n      SeriesData.prototype.eachItemGraphicEl = function (cb, context) {\n        each(this._graphicEls, function (el, idx) {\n          if (el) {\n            cb && cb.call(context, el, idx);\n          }\n        });\n      };\n      /**\n       * Shallow clone a new list except visual and layout properties, and graph elements.\n       * New list only change the indices.\n       */\n\n\n      SeriesData.prototype.cloneShallow = function (list) {\n        if (!list) {\n          list = new SeriesData(this._schema ? this._schema : map$1(this.dimensions, this._getDimInfo, this), this.hostModel);\n        }\n\n        transferProperties(list, this);\n        list._store = this._store;\n        return list;\n      };\n      /**\n       * Wrap some method to add more feature\n       */\n\n\n      SeriesData.prototype.wrapMethod = function (methodName, injectFunction) {\n        var originalMethod = this[methodName];\n\n        if (!isFunction(originalMethod)) {\n          return;\n        }\n\n        this.__wrappedMethods = this.__wrappedMethods || [];\n\n        this.__wrappedMethods.push(methodName);\n\n        this[methodName] = function () {\n          var res = originalMethod.apply(this, arguments);\n          return injectFunction.apply(this, [res].concat(slice(arguments)));\n        };\n      }; // ----------------------------------------------------------\n      // A work around for internal method visiting private member.\n      // ----------------------------------------------------------\n\n\n      SeriesData.internalField = function () {\n        prepareInvertedIndex = function (data) {\n          var invertedIndicesMap = data._invertedIndicesMap;\n          each(invertedIndicesMap, function (invertedIndices, dim) {\n            var dimInfo = data._dimInfos[dim]; // Currently, only dimensions that has ordinalMeta can create inverted indices.\n\n            var ordinalMeta = dimInfo.ordinalMeta;\n            var store = data._store;\n\n            if (ordinalMeta) {\n              invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array$1(ordinalMeta.categories.length); // The default value of TypedArray is 0. To avoid miss\n              // mapping to 0, we should set it as INDEX_NOT_FOUND.\n\n              for (var i = 0; i < invertedIndices.length; i++) {\n                invertedIndices[i] = INDEX_NOT_FOUND;\n              }\n\n              for (var i = 0; i < store.count(); i++) {\n                // Only support the case that all values are distinct.\n                invertedIndices[store.get(dimInfo.storeDimIndex, i)] = i;\n              }\n            }\n          });\n        };\n\n        getIdNameFromStore = function (data, dimIdx, idx) {\n          return convertOptionIdName(data._getCategory(dimIdx, idx), null);\n        };\n        /**\n         * @see the comment of `List['getId']`.\n         */\n\n\n        getId = function (data, rawIndex) {\n          var id = data._idList[rawIndex];\n\n          if (id == null && data._idDimIdx != null) {\n            id = getIdNameFromStore(data, data._idDimIdx, rawIndex);\n          }\n\n          if (id == null) {\n            id = ID_PREFIX + rawIndex;\n          }\n\n          return id;\n        };\n\n        normalizeDimensions = function (dimensions) {\n          if (!isArray(dimensions)) {\n            dimensions = dimensions != null ? [dimensions] : [];\n          }\n\n          return dimensions;\n        };\n        /**\n         * Data in excludeDimensions is copied, otherwise transferred.\n         */\n\n\n        cloneListForMapAndSample = function (original) {\n          var list = new SeriesData(original._schema ? original._schema : map$1(original.dimensions, original._getDimInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked\n\n          transferProperties(list, original);\n          return list;\n        };\n\n        transferProperties = function (target, source) {\n          each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {\n            if (source.hasOwnProperty(propName)) {\n              target[propName] = source[propName];\n            }\n          });\n          target.__wrappedMethods = source.__wrappedMethods;\n          each(CLONE_PROPERTIES, function (propName) {\n            target[propName] = clone(source[propName]);\n          });\n          target._calculationInfo = extend({}, source._calculationInfo);\n        };\n\n        makeIdFromName = function (data, idx) {\n          var nameList = data._nameList;\n          var idList = data._idList;\n          var nameDimIdx = data._nameDimIdx;\n          var idDimIdx = data._idDimIdx;\n          var name = nameList[idx];\n          var id = idList[idx];\n\n          if (name == null && nameDimIdx != null) {\n            nameList[idx] = name = getIdNameFromStore(data, nameDimIdx, idx);\n          }\n\n          if (id == null && idDimIdx != null) {\n            idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx);\n          }\n\n          if (id == null && name != null) {\n            var nameRepeatCount = data._nameRepeatCount;\n            var nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1;\n            id = name;\n\n            if (nmCnt > 1) {\n              id += '__ec__' + nmCnt;\n            }\n\n            idList[idx] = id;\n          }\n        };\n      }();\n\n      return SeriesData;\n    }();\n\n    /**\n     * For outside usage compat (like echarts-gl are using it).\n     */\n\n    function createDimensions(source, opt) {\n      return prepareSeriesDataSchema(source, opt).dimensions;\n    }\n    /**\n     * This method builds the relationship between:\n     * + \"what the coord sys or series requires (see `coordDimensions`)\",\n     * + \"what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)\"\n     * + \"what the data source provids (see `source`)\".\n     *\n     * Some guess strategy will be adapted if user does not define something.\n     * If no 'value' dimension specified, the first no-named dimension will be\n     * named as 'value'.\n     *\n     * @return The results are always sorted by `storeDimIndex` asc.\n     */\n\n    function prepareSeriesDataSchema( // TODO: TYPE completeDimensions type\n    source, opt) {\n      if (!isSourceInstance(source)) {\n        source = createSourceFromSeriesDataOption(source);\n      }\n\n      opt = opt || {};\n      var sysDims = opt.coordDimensions || [];\n      var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || [];\n      var coordDimNameMap = createHashMap();\n      var resultList = [];\n      var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount); // Try to ignore unused dimensions if sharing a high dimension datastore\n      // 30 is an experience value.\n\n      var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount);\n      var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine;\n      var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef);\n      var encodeDef = opt.encodeDefine;\n\n      if (!encodeDef && opt.encodeDefaulter) {\n        encodeDef = opt.encodeDefaulter(source, dimCount);\n      }\n\n      var encodeDefMap = createHashMap(encodeDef);\n      var indicesMap = new CtorInt32Array(dimCount);\n\n      for (var i = 0; i < indicesMap.length; i++) {\n        indicesMap[i] = -1;\n      }\n\n      function getResultItem(dimIdx) {\n        var idx = indicesMap[dimIdx];\n\n        if (idx < 0) {\n          var dimDefItemRaw = dimsDef[dimIdx];\n          var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : {\n            name: dimDefItemRaw\n          };\n          var resultItem = new SeriesDimensionDefine();\n          var userDimName = dimDefItem.name;\n\n          if (userDimName != null && dataDimNameMap.get(userDimName) != null) {\n            // Only if `series.dimensions` is defined in option\n            // displayName, will be set, and dimension will be displayed vertically in\n            // tooltip by default.\n            resultItem.name = resultItem.displayName = userDimName;\n          }\n\n          dimDefItem.type != null && (resultItem.type = dimDefItem.type);\n          dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);\n          var newIdx = resultList.length;\n          indicesMap[dimIdx] = newIdx;\n          resultItem.storeDimIndex = dimIdx;\n          resultList.push(resultItem);\n          return resultItem;\n        }\n\n        return resultList[idx];\n      }\n\n      if (!omitUnusedDimensions) {\n        for (var i = 0; i < dimCount; i++) {\n          getResultItem(i);\n        }\n      } // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.\n\n\n      encodeDefMap.each(function (dataDimsRaw, coordDim) {\n        var dataDims = normalizeToArray(dataDimsRaw).slice(); // Note: It is allowed that `dataDims.length` is `0`, e.g., options is\n        // `{encode: {x: -1, y: 1}}`. Should not filter anything in\n        // this case.\n\n        if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) {\n          encodeDefMap.set(coordDim, false);\n          return;\n        }\n\n        var validDataDims = encodeDefMap.set(coordDim, []);\n        each(dataDims, function (resultDimIdxOrName, idx) {\n          // The input resultDimIdx can be dim name or index.\n          var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName;\n\n          if (resultDimIdx != null && resultDimIdx < dimCount) {\n            validDataDims[idx] = resultDimIdx;\n            applyDim(getResultItem(resultDimIdx), coordDim, idx);\n          }\n        });\n      }); // Apply templates and default order from `sysDims`.\n\n      var availDimIdx = 0;\n      each(sysDims, function (sysDimItemRaw) {\n        var coordDim;\n        var sysDimItemDimsDef;\n        var sysDimItemOtherDims;\n        var sysDimItem;\n\n        if (isString(sysDimItemRaw)) {\n          coordDim = sysDimItemRaw;\n          sysDimItem = {};\n        } else {\n          sysDimItem = sysDimItemRaw;\n          coordDim = sysDimItem.name;\n          var ordinalMeta = sysDimItem.ordinalMeta;\n          sysDimItem.ordinalMeta = null;\n          sysDimItem = extend({}, sysDimItem);\n          sysDimItem.ordinalMeta = ordinalMeta; // `coordDimIndex` should not be set directly.\n\n          sysDimItemDimsDef = sysDimItem.dimsDef;\n          sysDimItemOtherDims = sysDimItem.otherDims;\n          sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null;\n        }\n\n        var dataDims = encodeDefMap.get(coordDim); // negative resultDimIdx means no need to mapping.\n\n        if (dataDims === false) {\n          return;\n        }\n\n        dataDims = normalizeToArray(dataDims); // dimensions provides default dim sequences.\n\n        if (!dataDims.length) {\n          for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {\n            while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) {\n              availDimIdx++;\n            }\n\n            availDimIdx < dimCount && dataDims.push(availDimIdx++);\n          }\n        } // Apply templates.\n\n\n        each(dataDims, function (resultDimIdx, coordDimIndex) {\n          var resultItem = getResultItem(resultDimIdx); // Coordinate system has a higher priority on dim type than source.\n\n          if (isUsingSourceDimensionsDef && sysDimItem.type != null) {\n            resultItem.type = sysDimItem.type;\n          }\n\n          applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);\n\n          if (resultItem.name == null && sysDimItemDimsDef) {\n            var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];\n            !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {\n              name: sysDimItemDimsDefItem\n            });\n            resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;\n            resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;\n          } // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}\n\n\n          sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);\n        });\n      });\n\n      function applyDim(resultItem, coordDim, coordDimIndex) {\n        if (VISUAL_DIMENSIONS.get(coordDim) != null) {\n          resultItem.otherDims[coordDim] = coordDimIndex;\n        } else {\n          resultItem.coordDim = coordDim;\n          resultItem.coordDimIndex = coordDimIndex;\n          coordDimNameMap.set(coordDim, true);\n        }\n      } // Make sure the first extra dim is 'value'.\n\n\n      var generateCoord = opt.generateCoord;\n      var generateCoordCount = opt.generateCoordCount;\n      var fromZero = generateCoordCount != null;\n      generateCoordCount = generateCoord ? generateCoordCount || 1 : 0;\n      var extra = generateCoord || 'value';\n\n      function ifNoNameFillWithCoordName(resultItem) {\n        if (resultItem.name == null) {\n          // Duplication will be removed in the next step.\n          resultItem.name = resultItem.coordDim;\n        }\n      } // Set dim `name` and other `coordDim` and other props.\n\n\n      if (!omitUnusedDimensions) {\n        for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {\n          var resultItem = getResultItem(resultDimIdx);\n          var coordDim = resultItem.coordDim;\n\n          if (coordDim == null) {\n            // TODO no need to generate coordDim for isExtraCoord?\n            resultItem.coordDim = genCoordDimName(extra, coordDimNameMap, fromZero);\n            resultItem.coordDimIndex = 0; // Series specified generateCoord is using out.\n\n            if (!generateCoord || generateCoordCount <= 0) {\n              resultItem.isExtraCoord = true;\n            }\n\n            generateCoordCount--;\n          }\n\n          ifNoNameFillWithCoordName(resultItem);\n\n          if (resultItem.type == null && (guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must // Consider the case:\n          // {\n          //    dataset: {source: [\n          //        ['2001', 123],\n          //        ['2002', 456],\n          //        ...\n          //        ['The others', 987],\n          //    ]},\n          //    series: {type: 'pie'}\n          // }\n          // The first column should better be treated as a \"ordinal\" although it\n          // might not be detected as an \"ordinal\" by `guessOrdinal`.\n          || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) {\n            resultItem.type = 'ordinal';\n          }\n        }\n      } else {\n        each(resultList, function (resultItem) {\n          // PENDING: guessOrdinal or let user specify type: 'ordinal' manually?\n          ifNoNameFillWithCoordName(resultItem);\n        }); // Sort dimensions: there are some rule that use the last dim as label,\n        // and for some latter travel process easier.\n\n        resultList.sort(function (item0, item1) {\n          return item0.storeDimIndex - item1.storeDimIndex;\n        });\n      }\n\n      removeDuplication(resultList);\n      return new SeriesDataSchema({\n        source: source,\n        dimensions: resultList,\n        fullDimensionCount: dimCount,\n        dimensionOmitted: omitUnusedDimensions\n      });\n    }\n\n    function removeDuplication(result) {\n      var duplicationMap = createHashMap();\n\n      for (var i = 0; i < result.length; i++) {\n        var dim = result[i];\n        var dimOriginalName = dim.name;\n        var count = duplicationMap.get(dimOriginalName) || 0;\n\n        if (count > 0) {\n          // Starts from 0.\n          dim.name = dimOriginalName + (count - 1);\n        }\n\n        count++;\n        duplicationMap.set(dimOriginalName, count);\n      }\n    } // ??? TODO\n    // Originally detect dimCount by data[0]. Should we\n    // optimize it to only by sysDims and dimensions and encode.\n    // So only necessary dims will be initialized.\n    // But\n    // (1) custom series should be considered. where other dims\n    // may be visited.\n    // (2) sometimes user need to calculate bubble size or use visualMap\n    // on other dimensions besides coordSys needed.\n    // So, dims that is not used by system, should be shared in data store?\n\n\n    function getDimCount(source, sysDims, dimsDef, optDimCount) {\n      // Note that the result dimCount should not small than columns count\n      // of data, otherwise `dataDimNameMap` checking will be incorrect.\n      var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0);\n      each(sysDims, function (sysDimItem) {\n        var sysDimItemDimsDef;\n\n        if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) {\n          dimCount = Math.max(dimCount, sysDimItemDimsDef.length);\n        }\n      });\n      return dimCount;\n    }\n\n    function genCoordDimName(name, map, fromZero) {\n      if (fromZero || map.hasKey(name)) {\n        var i = 0;\n\n        while (map.hasKey(name + i)) {\n          i++;\n        }\n\n        name += i;\n      }\n\n      map.set(name, true);\n      return name;\n    }\n\n    /**\n     * @class\n     * For example:\n     * {\n     *     coordSysName: 'cartesian2d',\n     *     coordSysDims: ['x', 'y', ...],\n     *     axisMap: HashMap({\n     *         x: xAxisModel,\n     *         y: yAxisModel\n     *     }),\n     *     categoryAxisMap: HashMap({\n     *         x: xAxisModel,\n     *         y: undefined\n     *     }),\n     *     // The index of the first category axis in `coordSysDims`.\n     *     // `null/undefined` means no category axis exists.\n     *     firstCategoryDimIndex: 1,\n     *     // To replace user specified encode.\n     * }\n     */\n\n    var CoordSysInfo =\n    /** @class */\n    function () {\n      function CoordSysInfo(coordSysName) {\n        this.coordSysDims = [];\n        this.axisMap = createHashMap();\n        this.categoryAxisMap = createHashMap();\n        this.coordSysName = coordSysName;\n      }\n\n      return CoordSysInfo;\n    }();\n\n    function getCoordSysInfoBySeries(seriesModel) {\n      var coordSysName = seriesModel.get('coordinateSystem');\n      var result = new CoordSysInfo(coordSysName);\n      var fetch = fetchers[coordSysName];\n\n      if (fetch) {\n        fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);\n        return result;\n      }\n    }\n    var fetchers = {\n      cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var xAxisModel = seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n        var yAxisModel = seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!xAxisModel) {\n            throw new Error('xAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('xAxisId'), 0) + '\" not found');\n          }\n\n          if (!yAxisModel) {\n            throw new Error('yAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('yAxisId'), 0) + '\" not found');\n          }\n        }\n\n        result.coordSysDims = ['x', 'y'];\n        axisMap.set('x', xAxisModel);\n        axisMap.set('y', yAxisModel);\n\n        if (isCategory(xAxisModel)) {\n          categoryAxisMap.set('x', xAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n\n        if (isCategory(yAxisModel)) {\n          categoryAxisMap.set('y', yAxisModel);\n          result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n        }\n      },\n      singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!singleAxisModel) {\n            throw new Error('singleAxis should be specified.');\n          }\n        }\n\n        result.coordSysDims = ['single'];\n        axisMap.set('single', singleAxisModel);\n\n        if (isCategory(singleAxisModel)) {\n          categoryAxisMap.set('single', singleAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n      },\n      polar: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n        var radiusAxisModel = polarModel.findAxisModel('radiusAxis');\n        var angleAxisModel = polarModel.findAxisModel('angleAxis');\n\n        if (\"development\" !== 'production') {\n          if (!angleAxisModel) {\n            throw new Error('angleAxis option not found');\n          }\n\n          if (!radiusAxisModel) {\n            throw new Error('radiusAxis option not found');\n          }\n        }\n\n        result.coordSysDims = ['radius', 'angle'];\n        axisMap.set('radius', radiusAxisModel);\n        axisMap.set('angle', angleAxisModel);\n\n        if (isCategory(radiusAxisModel)) {\n          categoryAxisMap.set('radius', radiusAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n\n        if (isCategory(angleAxisModel)) {\n          categoryAxisMap.set('angle', angleAxisModel);\n          result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n        }\n      },\n      geo: function (seriesModel, result, axisMap, categoryAxisMap) {\n        result.coordSysDims = ['lng', 'lat'];\n      },\n      parallel: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var ecModel = seriesModel.ecModel;\n        var parallelModel = ecModel.getComponent('parallel', seriesModel.get('parallelIndex'));\n        var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice();\n        each(parallelModel.parallelAxisIndex, function (axisIndex, index) {\n          var axisModel = ecModel.getComponent('parallelAxis', axisIndex);\n          var axisDim = coordSysDims[index];\n          axisMap.set(axisDim, axisModel);\n\n          if (isCategory(axisModel)) {\n            categoryAxisMap.set(axisDim, axisModel);\n\n            if (result.firstCategoryDimIndex == null) {\n              result.firstCategoryDimIndex = index;\n            }\n          }\n        });\n      }\n    };\n\n    function isCategory(axisModel) {\n      return axisModel.get('type') === 'category';\n    }\n\n    /**\n     * Note that it is too complicated to support 3d stack by value\n     * (have to create two-dimension inverted index), so in 3d case\n     * we just support that stacked by index.\n     *\n     * @param seriesModel\n     * @param dimensionsInput The same as the input of <module:echarts/data/SeriesData>.\n     *        The input will be modified.\n     * @param opt\n     * @param opt.stackedCoordDimension Specify a coord dimension if needed.\n     * @param opt.byIndex=false\n     * @return calculationInfo\n     * {\n     *     stackedDimension: string\n     *     stackedByDimension: string\n     *     isStackedByIndex: boolean\n     *     stackedOverDimension: string\n     *     stackResultDimension: string\n     * }\n     */\n\n    function enableDataStack(seriesModel, dimensionsInput, opt) {\n      opt = opt || {};\n      var byIndex = opt.byIndex;\n      var stackedCoordDimension = opt.stackedCoordDimension;\n      var dimensionDefineList;\n      var schema;\n      var store;\n\n      if (isLegacyDimensionsInput(dimensionsInput)) {\n        dimensionDefineList = dimensionsInput;\n      } else {\n        schema = dimensionsInput.schema;\n        dimensionDefineList = schema.dimensions;\n        store = dimensionsInput.store;\n      } // Compatibal: when `stack` is set as '', do not stack.\n\n\n      var mayStack = !!(seriesModel && seriesModel.get('stack'));\n      var stackedByDimInfo;\n      var stackedDimInfo;\n      var stackResultDimension;\n      var stackedOverDimension;\n      each(dimensionDefineList, function (dimensionInfo, index) {\n        if (isString(dimensionInfo)) {\n          dimensionDefineList[index] = dimensionInfo = {\n            name: dimensionInfo\n          };\n        }\n\n        if (mayStack && !dimensionInfo.isExtraCoord) {\n          // Find the first ordinal dimension as the stackedByDimInfo.\n          if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) {\n            stackedByDimInfo = dimensionInfo;\n          } // Find the first stackable dimension as the stackedDimInfo.\n\n\n          if (!stackedDimInfo && dimensionInfo.type !== 'ordinal' && dimensionInfo.type !== 'time' && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)) {\n            stackedDimInfo = dimensionInfo;\n          }\n        }\n      });\n\n      if (stackedDimInfo && !byIndex && !stackedByDimInfo) {\n        // Compatible with previous design, value axis (time axis) only stack by index.\n        // It may make sense if the user provides elaborately constructed data.\n        byIndex = true;\n      } // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`.\n      // That put stack logic in List is for using conveniently in echarts extensions, but it\n      // might not be a good way.\n\n\n      if (stackedDimInfo) {\n        // Use a weird name that not duplicated with other names.\n        // Also need to use seriesModel.id as postfix because different\n        // series may share same data store. The stack dimension needs to be distinguished.\n        stackResultDimension = '__\\0ecstackresult_' + seriesModel.id;\n        stackedOverDimension = '__\\0ecstackedover_' + seriesModel.id; // Create inverted index to fast query index by value.\n\n        if (stackedByDimInfo) {\n          stackedByDimInfo.createInvertedIndices = true;\n        }\n\n        var stackedDimCoordDim_1 = stackedDimInfo.coordDim;\n        var stackedDimType = stackedDimInfo.type;\n        var stackedDimCoordIndex_1 = 0;\n        each(dimensionDefineList, function (dimensionInfo) {\n          if (dimensionInfo.coordDim === stackedDimCoordDim_1) {\n            stackedDimCoordIndex_1++;\n          }\n        });\n        var stackedOverDimensionDefine = {\n          name: stackResultDimension,\n          coordDim: stackedDimCoordDim_1,\n          coordDimIndex: stackedDimCoordIndex_1,\n          type: stackedDimType,\n          isExtraCoord: true,\n          isCalculationCoord: true,\n          storeDimIndex: dimensionDefineList.length\n        };\n        var stackResultDimensionDefine = {\n          name: stackedOverDimension,\n          // This dimension contains stack base (generally, 0), so do not set it as\n          // `stackedDimCoordDim` to avoid extent calculation, consider log scale.\n          coordDim: stackedOverDimension,\n          coordDimIndex: stackedDimCoordIndex_1 + 1,\n          type: stackedDimType,\n          isExtraCoord: true,\n          isCalculationCoord: true,\n          storeDimIndex: dimensionDefineList.length + 1\n        };\n\n        if (schema) {\n          if (store) {\n            stackedOverDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackedOverDimension, stackedDimType);\n            stackResultDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackResultDimension, stackedDimType);\n          }\n\n          schema.appendCalculationDimension(stackedOverDimensionDefine);\n          schema.appendCalculationDimension(stackResultDimensionDefine);\n        } else {\n          dimensionDefineList.push(stackedOverDimensionDefine);\n          dimensionDefineList.push(stackResultDimensionDefine);\n        }\n      }\n\n      return {\n        stackedDimension: stackedDimInfo && stackedDimInfo.name,\n        stackedByDimension: stackedByDimInfo && stackedByDimInfo.name,\n        isStackedByIndex: byIndex,\n        stackedOverDimension: stackedOverDimension,\n        stackResultDimension: stackResultDimension\n      };\n    }\n\n    function isLegacyDimensionsInput(dimensionsInput) {\n      return !isSeriesDataSchema(dimensionsInput.schema);\n    }\n\n    function isDimensionStacked(data, stackedDim) {\n      // Each single series only maps to one pair of axis. So we do not need to\n      // check stackByDim, whatever stacked by a dimension or stacked by index.\n      return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension');\n    }\n    function getStackedDimension(data, targetDim) {\n      return isDimensionStacked(data, targetDim) ? data.getCalculationInfo('stackResultDimension') : targetDim;\n    }\n\n    function getCoordSysDimDefs(seriesModel, coordSysInfo) {\n      var coordSysName = seriesModel.get('coordinateSystem');\n      var registeredCoordSys = CoordinateSystemManager.get(coordSysName);\n      var coordSysDimDefs;\n\n      if (coordSysInfo && coordSysInfo.coordSysDims) {\n        coordSysDimDefs = map(coordSysInfo.coordSysDims, function (dim) {\n          var dimInfo = {\n            name: dim\n          };\n          var axisModel = coordSysInfo.axisMap.get(dim);\n\n          if (axisModel) {\n            var axisType = axisModel.get('type');\n            dimInfo.type = getDimensionTypeByAxis(axisType);\n          }\n\n          return dimInfo;\n        });\n      }\n\n      if (!coordSysDimDefs) {\n        // Get dimensions from registered coordinate system\n        coordSysDimDefs = registeredCoordSys && (registeredCoordSys.getDimensionsInfo ? registeredCoordSys.getDimensionsInfo() : registeredCoordSys.dimensions.slice()) || ['x', 'y'];\n      }\n\n      return coordSysDimDefs;\n    }\n\n    function injectOrdinalMeta(dimInfoList, createInvertedIndices, coordSysInfo) {\n      var firstCategoryDimIndex;\n      var hasNameEncode;\n      coordSysInfo && each(dimInfoList, function (dimInfo, dimIndex) {\n        var coordDim = dimInfo.coordDim;\n        var categoryAxisModel = coordSysInfo.categoryAxisMap.get(coordDim);\n\n        if (categoryAxisModel) {\n          if (firstCategoryDimIndex == null) {\n            firstCategoryDimIndex = dimIndex;\n          }\n\n          dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta();\n\n          if (createInvertedIndices) {\n            dimInfo.createInvertedIndices = true;\n          }\n        }\n\n        if (dimInfo.otherDims.itemName != null) {\n          hasNameEncode = true;\n        }\n      });\n\n      if (!hasNameEncode && firstCategoryDimIndex != null) {\n        dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0;\n      }\n\n      return firstCategoryDimIndex;\n    }\n    /**\n     * Caution: there are side effects to `sourceManager` in this method.\n     * Should better only be called in `Series['getInitialData']`.\n     */\n\n\n    function createSeriesData(sourceRaw, seriesModel, opt) {\n      opt = opt || {};\n      var sourceManager = seriesModel.getSourceManager();\n      var source;\n      var isOriginalSource = false;\n\n      if (sourceRaw) {\n        isOriginalSource = true;\n        source = createSourceFromSeriesDataOption(sourceRaw);\n      } else {\n        source = sourceManager.getSource(); // Is series.data. not dataset.\n\n        isOriginalSource = source.sourceFormat === SOURCE_FORMAT_ORIGINAL;\n      }\n\n      var coordSysInfo = getCoordSysInfoBySeries(seriesModel);\n      var coordSysDimDefs = getCoordSysDimDefs(seriesModel, coordSysInfo);\n      var useEncodeDefaulter = opt.useEncodeDefaulter;\n      var encodeDefaulter = isFunction(useEncodeDefaulter) ? useEncodeDefaulter : useEncodeDefaulter ? curry(makeSeriesEncodeForAxisCoordSys, coordSysDimDefs, seriesModel) : null;\n      var createDimensionOptions = {\n        coordDimensions: coordSysDimDefs,\n        generateCoord: opt.generateCoord,\n        encodeDefine: seriesModel.getEncode(),\n        encodeDefaulter: encodeDefaulter,\n        canOmitUnusedDimensions: !isOriginalSource\n      };\n      var schema = prepareSeriesDataSchema(source, createDimensionOptions);\n      var firstCategoryDimIndex = injectOrdinalMeta(schema.dimensions, opt.createInvertedIndices, coordSysInfo);\n      var store = !isOriginalSource ? sourceManager.getSharedDataStore(schema) : null;\n      var stackCalculationInfo = enableDataStack(seriesModel, {\n        schema: schema,\n        store: store\n      });\n      var data = new SeriesData(schema, seriesModel);\n      data.setCalculationInfo(stackCalculationInfo);\n      var dimValueGetter = firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source) ? function (itemOpt, dimName, dataIndex, dimIndex) {\n        // Use dataIndex as ordinal value in categoryAxis\n        return dimIndex === firstCategoryDimIndex ? dataIndex : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);\n      } : null;\n      data.hasItemOption = false;\n      data.initData( // Try to reuse the data store in sourceManager if using dataset.\n      isOriginalSource ? source : store, null, dimValueGetter);\n      return data;\n    }\n\n    function isNeedCompleteOrdinalData(source) {\n      if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var sampleItem = firstDataNotNull(source.data || []);\n        return !isArray(getDataItemValue(sampleItem));\n      }\n    }\n\n    function firstDataNotNull(arr) {\n      var i = 0;\n\n      while (i < arr.length && arr[i] == null) {\n        i++;\n      }\n\n      return arr[i];\n    }\n\n    var Scale =\n    /** @class */\n    function () {\n      function Scale(setting) {\n        this._setting = setting || {};\n        this._extent = [Infinity, -Infinity];\n      }\n\n      Scale.prototype.getSetting = function (name) {\n        return this._setting[name];\n      };\n      /**\n       * Set extent from data\n       */\n\n\n      Scale.prototype.unionExtent = function (other) {\n        var extent = this._extent;\n        other[0] < extent[0] && (extent[0] = other[0]);\n        other[1] > extent[1] && (extent[1] = other[1]); // not setExtent because in log axis it may transformed to power\n        // this.setExtent(extent[0], extent[1]);\n      };\n      /**\n       * Set extent from data\n       */\n\n\n      Scale.prototype.unionExtentFromData = function (data, dim) {\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * Get extent\n       *\n       * Extent is always in increase order.\n       */\n\n\n      Scale.prototype.getExtent = function () {\n        return this._extent.slice();\n      };\n      /**\n       * Set extent\n       */\n\n\n      Scale.prototype.setExtent = function (start, end) {\n        var thisExtent = this._extent;\n\n        if (!isNaN(start)) {\n          thisExtent[0] = start;\n        }\n\n        if (!isNaN(end)) {\n          thisExtent[1] = end;\n        }\n      };\n      /**\n       * If value is in extent range\n       */\n\n\n      Scale.prototype.isInExtentRange = function (value) {\n        return this._extent[0] <= value && this._extent[1] >= value;\n      };\n      /**\n       * When axis extent depends on data and no data exists,\n       * axis ticks should not be drawn, which is named 'blank'.\n       */\n\n\n      Scale.prototype.isBlank = function () {\n        return this._isBlank;\n      };\n      /**\n       * When axis extent depends on data and no data exists,\n       * axis ticks should not be drawn, which is named 'blank'.\n       */\n\n\n      Scale.prototype.setBlank = function (isBlank) {\n        this._isBlank = isBlank;\n      };\n\n      return Scale;\n    }();\n\n    enableClassManagement(Scale);\n\n    var uidBase = 0;\n\n    var OrdinalMeta =\n    /** @class */\n    function () {\n      function OrdinalMeta(opt) {\n        this.categories = opt.categories || [];\n        this._needCollect = opt.needCollect;\n        this._deduplication = opt.deduplication;\n        this.uid = ++uidBase;\n      }\n\n      OrdinalMeta.createByAxisModel = function (axisModel) {\n        var option = axisModel.option;\n        var data = option.data;\n        var categories = data && map(data, getName);\n        return new OrdinalMeta({\n          categories: categories,\n          needCollect: !categories,\n          // deduplication is default in axis.\n          deduplication: option.dedplication !== false\n        });\n      };\n\n      OrdinalMeta.prototype.getOrdinal = function (category) {\n        // @ts-ignore\n        return this._getOrCreateMap().get(category);\n      };\n      /**\n       * @return The ordinal. If not found, return NaN.\n       */\n\n\n      OrdinalMeta.prototype.parseAndCollect = function (category) {\n        var index;\n        var needCollect = this._needCollect; // The value of category dim can be the index of the given category set.\n        // This feature is only supported when !needCollect, because we should\n        // consider a common case: a value is 2017, which is a number but is\n        // expected to be tread as a category. This case usually happen in dataset,\n        // where it happent to be no need of the index feature.\n\n        if (!isString(category) && !needCollect) {\n          return category;\n        } // Optimize for the scenario:\n        // category is ['2012-01-01', '2012-01-02', ...], where the input\n        // data has been ensured not duplicate and is large data.\n        // Notice, if a dataset dimension provide categroies, usually echarts\n        // should remove duplication except user tell echarts dont do that\n        // (set axis.deduplication = false), because echarts do not know whether\n        // the values in the category dimension has duplication (consider the\n        // parallel-aqi example)\n\n\n        if (needCollect && !this._deduplication) {\n          index = this.categories.length;\n          this.categories[index] = category;\n          return index;\n        }\n\n        var map = this._getOrCreateMap(); // @ts-ignore\n\n\n        index = map.get(category);\n\n        if (index == null) {\n          if (needCollect) {\n            index = this.categories.length;\n            this.categories[index] = category; // @ts-ignore\n\n            map.set(category, index);\n          } else {\n            index = NaN;\n          }\n        }\n\n        return index;\n      }; // Consider big data, do not create map until needed.\n\n\n      OrdinalMeta.prototype._getOrCreateMap = function () {\n        return this._map || (this._map = createHashMap(this.categories));\n      };\n\n      return OrdinalMeta;\n    }();\n\n    function getName(obj) {\n      if (isObject(obj) && obj.value != null) {\n        return obj.value;\n      } else {\n        return obj + '';\n      }\n    }\n\n    function isValueNice(val) {\n      var exp10 = Math.pow(10, quantityExponent(Math.abs(val)));\n      var f = Math.abs(val / exp10);\n      return f === 0 || f === 1 || f === 2 || f === 3 || f === 5;\n    }\n    function isIntervalOrLogScale(scale) {\n      return scale.type === 'interval' || scale.type === 'log';\n    }\n    /**\n     * @param extent Both extent[0] and extent[1] should be valid number.\n     *               Should be extent[0] < extent[1].\n     * @param splitNumber splitNumber should be >= 1.\n     */\n\n    function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {\n      var result = {};\n      var span = extent[1] - extent[0];\n      var interval = result.interval = nice(span / splitNumber, true);\n\n      if (minInterval != null && interval < minInterval) {\n        interval = result.interval = minInterval;\n      }\n\n      if (maxInterval != null && interval > maxInterval) {\n        interval = result.interval = maxInterval;\n      } // Tow more digital for tick.\n\n\n      var precision = result.intervalPrecision = getIntervalPrecision(interval); // Niced extent inside original extent\n\n      var niceTickExtent = result.niceTickExtent = [round(Math.ceil(extent[0] / interval) * interval, precision), round(Math.floor(extent[1] / interval) * interval, precision)];\n      fixExtent(niceTickExtent, extent);\n      return result;\n    }\n    function increaseInterval(interval) {\n      var exp10 = Math.pow(10, quantityExponent(interval)); // Increase interval\n\n      var f = interval / exp10;\n\n      if (!f) {\n        f = 1;\n      } else if (f === 2) {\n        f = 3;\n      } else if (f === 3) {\n        f = 5;\n      } else {\n        // f is 1 or 5\n        f *= 2;\n      }\n\n      return round(f * exp10);\n    }\n    /**\n     * @return interval precision\n     */\n\n    function getIntervalPrecision(interval) {\n      // Tow more digital for tick.\n      return getPrecision(interval) + 2;\n    }\n\n    function clamp(niceTickExtent, idx, extent) {\n      niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);\n    } // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.\n\n\n    function fixExtent(niceTickExtent, extent) {\n      !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);\n      !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);\n      clamp(niceTickExtent, 0, extent);\n      clamp(niceTickExtent, 1, extent);\n\n      if (niceTickExtent[0] > niceTickExtent[1]) {\n        niceTickExtent[0] = niceTickExtent[1];\n      }\n    }\n    function contain$1(val, extent) {\n      return val >= extent[0] && val <= extent[1];\n    }\n    function normalize$1(val, extent) {\n      if (extent[1] === extent[0]) {\n        return 0.5;\n      }\n\n      return (val - extent[0]) / (extent[1] - extent[0]);\n    }\n    function scale$2(val, extent) {\n      return val * (extent[1] - extent[0]) + extent[0];\n    }\n\n    var OrdinalScale =\n    /** @class */\n    function (_super) {\n      __extends(OrdinalScale, _super);\n\n      function OrdinalScale(setting) {\n        var _this = _super.call(this, setting) || this;\n\n        _this.type = 'ordinal';\n\n        var ordinalMeta = _this.getSetting('ordinalMeta'); // Caution: Should not use instanceof, consider ec-extensions using\n        // import approach to get OrdinalMeta class.\n\n\n        if (!ordinalMeta) {\n          ordinalMeta = new OrdinalMeta({});\n        }\n\n        if (isArray(ordinalMeta)) {\n          ordinalMeta = new OrdinalMeta({\n            categories: map(ordinalMeta, function (item) {\n              return isObject(item) ? item.value : item;\n            })\n          });\n        }\n\n        _this._ordinalMeta = ordinalMeta;\n        _this._extent = _this.getSetting('extent') || [0, ordinalMeta.categories.length - 1];\n        return _this;\n      }\n\n      OrdinalScale.prototype.parse = function (val) {\n        // Caution: Math.round(null) will return `0` rather than `NaN`\n        if (val == null) {\n          return NaN;\n        }\n\n        return isString(val) ? this._ordinalMeta.getOrdinal(val) // val might be float.\n        : Math.round(val);\n      };\n\n      OrdinalScale.prototype.contain = function (rank) {\n        rank = this.parse(rank);\n        return contain$1(rank, this._extent) && this._ordinalMeta.categories[rank] != null;\n      };\n      /**\n       * Normalize given rank or name to linear [0, 1]\n       * @param val raw ordinal number.\n       * @return normalized value in [0, 1].\n       */\n\n\n      OrdinalScale.prototype.normalize = function (val) {\n        val = this._getTickNumber(this.parse(val));\n        return normalize$1(val, this._extent);\n      };\n      /**\n       * @param val normalized value in [0, 1].\n       * @return raw ordinal number.\n       */\n\n\n      OrdinalScale.prototype.scale = function (val) {\n        val = Math.round(scale$2(val, this._extent));\n        return this.getRawOrdinalNumber(val);\n      };\n\n      OrdinalScale.prototype.getTicks = function () {\n        var ticks = [];\n        var extent = this._extent;\n        var rank = extent[0];\n\n        while (rank <= extent[1]) {\n          ticks.push({\n            value: rank\n          });\n          rank++;\n        }\n\n        return ticks;\n      };\n\n      OrdinalScale.prototype.getMinorTicks = function (splitNumber) {\n        // Not support.\n        return;\n      };\n      /**\n       * @see `Ordinal['_ordinalNumbersByTick']`\n       */\n\n\n      OrdinalScale.prototype.setSortInfo = function (info) {\n        if (info == null) {\n          this._ordinalNumbersByTick = this._ticksByOrdinalNumber = null;\n          return;\n        }\n\n        var infoOrdinalNumbers = info.ordinalNumbers;\n        var ordinalsByTick = this._ordinalNumbersByTick = [];\n        var ticksByOrdinal = this._ticksByOrdinalNumber = []; // Unnecessary support negative tick in `realtimeSort`.\n\n        var tickNum = 0;\n        var allCategoryLen = this._ordinalMeta.categories.length;\n\n        for (var len = Math.min(allCategoryLen, infoOrdinalNumbers.length); tickNum < len; ++tickNum) {\n          var ordinalNumber = infoOrdinalNumbers[tickNum];\n          ordinalsByTick[tickNum] = ordinalNumber;\n          ticksByOrdinal[ordinalNumber] = tickNum;\n        } // Handle that `series.data` only covers part of the `axis.category.data`.\n\n\n        var unusedOrdinal = 0;\n\n        for (; tickNum < allCategoryLen; ++tickNum) {\n          while (ticksByOrdinal[unusedOrdinal] != null) {\n            unusedOrdinal++;\n          }\n          ordinalsByTick.push(unusedOrdinal);\n          ticksByOrdinal[unusedOrdinal] = tickNum;\n        }\n      };\n\n      OrdinalScale.prototype._getTickNumber = function (ordinal) {\n        var ticksByOrdinalNumber = this._ticksByOrdinalNumber; // also support ordinal out of range of `ordinalMeta.categories.length`,\n        // where ordinal numbers are used as tick value directly.\n\n        return ticksByOrdinalNumber && ordinal >= 0 && ordinal < ticksByOrdinalNumber.length ? ticksByOrdinalNumber[ordinal] : ordinal;\n      };\n      /**\n       * @usage\n       * ```js\n       * const ordinalNumber = ordinalScale.getRawOrdinalNumber(tickVal);\n       *\n       * // case0\n       * const rawOrdinalValue = axisModel.getCategories()[ordinalNumber];\n       * // case1\n       * const rawOrdinalValue = this._ordinalMeta.categories[ordinalNumber];\n       * // case2\n       * const coord = axis.dataToCoord(ordinalNumber);\n       * ```\n       *\n       * @param {OrdinalNumber} tickNumber index of display\n       */\n\n\n      OrdinalScale.prototype.getRawOrdinalNumber = function (tickNumber) {\n        var ordinalNumbersByTick = this._ordinalNumbersByTick; // tickNumber may be out of range, e.g., when axis max is larger than `ordinalMeta.categories.length`.,\n        // where ordinal numbers are used as tick value directly.\n\n        return ordinalNumbersByTick && tickNumber >= 0 && tickNumber < ordinalNumbersByTick.length ? ordinalNumbersByTick[tickNumber] : tickNumber;\n      };\n      /**\n       * Get item on tick\n       */\n\n\n      OrdinalScale.prototype.getLabel = function (tick) {\n        if (!this.isBlank()) {\n          var ordinalNumber = this.getRawOrdinalNumber(tick.value);\n          var cateogry = this._ordinalMeta.categories[ordinalNumber]; // Note that if no data, ordinalMeta.categories is an empty array.\n          // Return empty if it's not exist.\n\n          return cateogry == null ? '' : cateogry + '';\n        }\n      };\n\n      OrdinalScale.prototype.count = function () {\n        return this._extent[1] - this._extent[0] + 1;\n      };\n\n      OrdinalScale.prototype.unionExtentFromData = function (data, dim) {\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * @override\n       * If value is in extent range\n       */\n\n\n      OrdinalScale.prototype.isInExtentRange = function (value) {\n        value = this._getTickNumber(value);\n        return this._extent[0] <= value && this._extent[1] >= value;\n      };\n\n      OrdinalScale.prototype.getOrdinalMeta = function () {\n        return this._ordinalMeta;\n      };\n\n      OrdinalScale.prototype.calcNiceTicks = function () {};\n\n      OrdinalScale.prototype.calcNiceExtent = function () {};\n\n      OrdinalScale.type = 'ordinal';\n      return OrdinalScale;\n    }(Scale);\n\n    Scale.registerClass(OrdinalScale);\n\n    var roundNumber = round;\n\n    var IntervalScale =\n    /** @class */\n    function (_super) {\n      __extends(IntervalScale, _super);\n\n      function IntervalScale() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'interval'; // Step is calculated in adjustExtent.\n\n        _this._interval = 0;\n        _this._intervalPrecision = 2;\n        return _this;\n      }\n\n      IntervalScale.prototype.parse = function (val) {\n        return val;\n      };\n\n      IntervalScale.prototype.contain = function (val) {\n        return contain$1(val, this._extent);\n      };\n\n      IntervalScale.prototype.normalize = function (val) {\n        return normalize$1(val, this._extent);\n      };\n\n      IntervalScale.prototype.scale = function (val) {\n        return scale$2(val, this._extent);\n      };\n\n      IntervalScale.prototype.setExtent = function (start, end) {\n        var thisExtent = this._extent; // start,end may be a Number like '25',so...\n\n        if (!isNaN(start)) {\n          thisExtent[0] = parseFloat(start);\n        }\n\n        if (!isNaN(end)) {\n          thisExtent[1] = parseFloat(end);\n        }\n      };\n\n      IntervalScale.prototype.unionExtent = function (other) {\n        var extent = this._extent;\n        other[0] < extent[0] && (extent[0] = other[0]);\n        other[1] > extent[1] && (extent[1] = other[1]); // unionExtent may called by it's sub classes\n\n        this.setExtent(extent[0], extent[1]);\n      };\n\n      IntervalScale.prototype.getInterval = function () {\n        return this._interval;\n      };\n\n      IntervalScale.prototype.setInterval = function (interval) {\n        this._interval = interval; // Dropped auto calculated niceExtent and use user-set extent.\n        // We assume user wants to set both interval, min, max to get a better result.\n\n        this._niceExtent = this._extent.slice();\n        this._intervalPrecision = getIntervalPrecision(interval);\n      };\n      /**\n       * @param expandToNicedExtent Whether expand the ticks to niced extent.\n       */\n\n\n      IntervalScale.prototype.getTicks = function (expandToNicedExtent) {\n        var interval = this._interval;\n        var extent = this._extent;\n        var niceTickExtent = this._niceExtent;\n        var intervalPrecision = this._intervalPrecision;\n        var ticks = []; // If interval is 0, return [];\n\n        if (!interval) {\n          return ticks;\n        } // Consider this case: using dataZoom toolbox, zoom and zoom.\n\n\n        var safeLimit = 10000;\n\n        if (extent[0] < niceTickExtent[0]) {\n          if (expandToNicedExtent) {\n            ticks.push({\n              value: roundNumber(niceTickExtent[0] - interval, intervalPrecision)\n            });\n          } else {\n            ticks.push({\n              value: extent[0]\n            });\n          }\n        }\n\n        var tick = niceTickExtent[0];\n\n        while (tick <= niceTickExtent[1]) {\n          ticks.push({\n            value: tick\n          }); // Avoid rounding error\n\n          tick = roundNumber(tick + interval, intervalPrecision);\n\n          if (tick === ticks[ticks.length - 1].value) {\n            // Consider out of safe float point, e.g.,\n            // -3711126.9907707 + 2e-10 === -3711126.9907707\n            break;\n          }\n\n          if (ticks.length > safeLimit) {\n            return [];\n          }\n        } // Consider this case: the last item of ticks is smaller\n        // than niceTickExtent[1] and niceTickExtent[1] === extent[1].\n\n\n        var lastNiceTick = ticks.length ? ticks[ticks.length - 1].value : niceTickExtent[1];\n\n        if (extent[1] > lastNiceTick) {\n          if (expandToNicedExtent) {\n            ticks.push({\n              value: roundNumber(lastNiceTick + interval, intervalPrecision)\n            });\n          } else {\n            ticks.push({\n              value: extent[1]\n            });\n          }\n        }\n\n        return ticks;\n      };\n\n      IntervalScale.prototype.getMinorTicks = function (splitNumber) {\n        var ticks = this.getTicks(true);\n        var minorTicks = [];\n        var extent = this.getExtent();\n\n        for (var i = 1; i < ticks.length; i++) {\n          var nextTick = ticks[i];\n          var prevTick = ticks[i - 1];\n          var count = 0;\n          var minorTicksGroup = [];\n          var interval = nextTick.value - prevTick.value;\n          var minorInterval = interval / splitNumber;\n\n          while (count < splitNumber - 1) {\n            var minorTick = roundNumber(prevTick.value + (count + 1) * minorInterval); // For the first and last interval. The count may be less than splitNumber.\n\n            if (minorTick > extent[0] && minorTick < extent[1]) {\n              minorTicksGroup.push(minorTick);\n            }\n\n            count++;\n          }\n\n          minorTicks.push(minorTicksGroup);\n        }\n\n        return minorTicks;\n      };\n      /**\n       * @param opt.precision If 'auto', use nice presision.\n       * @param opt.pad returns 1.50 but not 1.5 if precision is 2.\n       */\n\n\n      IntervalScale.prototype.getLabel = function (data, opt) {\n        if (data == null) {\n          return '';\n        }\n\n        var precision = opt && opt.precision;\n\n        if (precision == null) {\n          precision = getPrecision(data.value) || 0;\n        } else if (precision === 'auto') {\n          // Should be more precise then tick.\n          precision = this._intervalPrecision;\n        } // (1) If `precision` is set, 12.005 should be display as '12.00500'.\n        // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.\n\n\n        var dataNum = roundNumber(data.value, precision, true);\n        return addCommas(dataNum);\n      };\n      /**\n       * @param splitNumber By default `5`.\n       */\n\n\n      IntervalScale.prototype.calcNiceTicks = function (splitNumber, minInterval, maxInterval) {\n        splitNumber = splitNumber || 5;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n\n        if (!isFinite(span)) {\n          return;\n        } // User may set axis min 0 and data are all negative\n        // FIXME If it needs to reverse ?\n\n\n        if (span < 0) {\n          span = -span;\n          extent.reverse();\n        }\n\n        var result = intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval);\n        this._intervalPrecision = result.intervalPrecision;\n        this._interval = result.interval;\n        this._niceExtent = result.niceTickExtent;\n      };\n\n      IntervalScale.prototype.calcNiceExtent = function (opt) {\n        var extent = this._extent; // If extent start and end are same, expand them\n\n        if (extent[0] === extent[1]) {\n          if (extent[0] !== 0) {\n            // Expand extent\n            // Note that extents can be both negative. See #13154\n            var expandSize = Math.abs(extent[0]); // In the fowllowing case\n            //      Axis has been fixed max 100\n            //      Plus data are all 100 and axis extent are [100, 100].\n            // Extend to the both side will cause expanded max is larger than fixed max.\n            // So only expand to the smaller side.\n\n            if (!opt.fixMax) {\n              extent[1] += expandSize / 2;\n              extent[0] -= expandSize / 2;\n            } else {\n              extent[0] -= expandSize / 2;\n            }\n          } else {\n            extent[1] = 1;\n          }\n        }\n\n        var span = extent[1] - extent[0]; // If there are no data and extent are [Infinity, -Infinity]\n\n        if (!isFinite(span)) {\n          extent[0] = 0;\n          extent[1] = 1;\n        }\n\n        this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // let extent = this._extent;\n\n        var interval = this._interval;\n\n        if (!opt.fixMin) {\n          extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);\n        }\n\n        if (!opt.fixMax) {\n          extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);\n        }\n      };\n\n      IntervalScale.prototype.setNiceExtent = function (min, max) {\n        this._niceExtent = [min, max];\n      };\n\n      IntervalScale.type = 'interval';\n      return IntervalScale;\n    }(Scale);\n\n    Scale.registerClass(IntervalScale);\n\n    /* global Float32Array */\n\n    var supportFloat32Array = typeof Float32Array !== 'undefined';\n    var Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array;\n    function createFloat32Array(arg) {\n      if (isArray(arg)) {\n        // Return self directly if don't support TypedArray.\n        return supportFloat32Array ? new Float32Array(arg) : arg;\n      } // Else is number\n\n\n      return new Float32ArrayCtor(arg);\n    }\n\n    var STACK_PREFIX = '__ec_stack_';\n\n    function getSeriesStackId(seriesModel) {\n      return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;\n    }\n\n    function getAxisKey(axis) {\n      return axis.dim + axis.index;\n    }\n    function prepareLayoutBarSeries(seriesType, ecModel) {\n      var seriesModels = [];\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        // Check series coordinate, do layout for cartesian2d only\n        if (isOnCartesian(seriesModel)) {\n          seriesModels.push(seriesModel);\n        }\n      });\n      return seriesModels;\n    }\n    /**\n     * Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent\n     * values.\n     * This works for time axes, value axes, and log axes.\n     * For a single time axis, return value is in the form like\n     * {'x_0': [1000000]}.\n     * The value of 1000000 is in milliseconds.\n     */\n\n    function getValueAxesMinGaps(barSeries) {\n      /**\n       * Map from axis.index to values.\n       * For a single time axis, axisValues is in the form like\n       * {'x_0': [1495555200000, 1495641600000, 1495728000000]}.\n       * Items in axisValues[x], e.g. 1495555200000, are time values of all\n       * series.\n       */\n      var axisValues = {};\n      each(barSeries, function (seriesModel) {\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n\n        if (baseAxis.type !== 'time' && baseAxis.type !== 'value') {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var key = baseAxis.dim + '_' + baseAxis.index;\n        var dimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n        var store = data.getStore();\n\n        for (var i = 0, cnt = store.count(); i < cnt; ++i) {\n          var value = store.get(dimIdx, i);\n\n          if (!axisValues[key]) {\n            // No previous data for the axis\n            axisValues[key] = [value];\n          } else {\n            // No value in previous series\n            axisValues[key].push(value);\n          } // Ignore duplicated time values in the same axis\n\n        }\n      });\n      var axisMinGaps = {};\n\n      for (var key in axisValues) {\n        if (axisValues.hasOwnProperty(key)) {\n          var valuesInAxis = axisValues[key];\n\n          if (valuesInAxis) {\n            // Sort axis values into ascending order to calculate gaps\n            valuesInAxis.sort(function (a, b) {\n              return a - b;\n            });\n            var min = null;\n\n            for (var j = 1; j < valuesInAxis.length; ++j) {\n              var delta = valuesInAxis[j] - valuesInAxis[j - 1];\n\n              if (delta > 0) {\n                // Ignore 0 delta because they are of the same axis value\n                min = min === null ? delta : Math.min(min, delta);\n              }\n            } // Set to null if only have one data\n\n\n            axisMinGaps[key] = min;\n          }\n        }\n      }\n\n      return axisMinGaps;\n    }\n\n    function makeColumnLayout(barSeries) {\n      var axisMinGaps = getValueAxesMinGaps(barSeries);\n      var seriesInfoList = [];\n      each(barSeries, function (seriesModel) {\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n        var axisExtent = baseAxis.getExtent();\n        var bandWidth;\n\n        if (baseAxis.type === 'category') {\n          bandWidth = baseAxis.getBandWidth();\n        } else if (baseAxis.type === 'value' || baseAxis.type === 'time') {\n          var key = baseAxis.dim + '_' + baseAxis.index;\n          var minGap = axisMinGaps[key];\n          var extentSpan = Math.abs(axisExtent[1] - axisExtent[0]);\n          var scale = baseAxis.scale.getExtent();\n          var scaleSpan = Math.abs(scale[1] - scale[0]);\n          bandWidth = minGap ? extentSpan / scaleSpan * minGap : extentSpan; // When there is only one data value\n        } else {\n          var data = seriesModel.getData();\n          bandWidth = Math.abs(axisExtent[1] - axisExtent[0]) / data.count();\n        }\n\n        var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth);\n        var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth);\n        var barMinWidth = parsePercent$1( // barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis,\n        // the auto-calculated bar width might be less than 0.5 / 1.\n        seriesModel.get('barMinWidth') || (isInLargeMode(seriesModel) ? 0.5 : 1), bandWidth);\n        var barGap = seriesModel.get('barGap');\n        var barCategoryGap = seriesModel.get('barCategoryGap');\n        seriesInfoList.push({\n          bandWidth: bandWidth,\n          barWidth: barWidth,\n          barMaxWidth: barMaxWidth,\n          barMinWidth: barMinWidth,\n          barGap: barGap,\n          barCategoryGap: barCategoryGap,\n          axisKey: getAxisKey(baseAxis),\n          stackId: getSeriesStackId(seriesModel)\n        });\n      });\n      return doCalBarWidthAndOffset(seriesInfoList);\n    }\n\n    function doCalBarWidthAndOffset(seriesInfoList) {\n      // Columns info on each category axis. Key is cartesian name\n      var columnsMap = {};\n      each(seriesInfoList, function (seriesInfo, idx) {\n        var axisKey = seriesInfo.axisKey;\n        var bandWidth = seriesInfo.bandWidth;\n        var columnsOnAxis = columnsMap[axisKey] || {\n          bandWidth: bandWidth,\n          remainedWidth: bandWidth,\n          autoWidthCount: 0,\n          categoryGap: null,\n          gap: '20%',\n          stacks: {}\n        };\n        var stacks = columnsOnAxis.stacks;\n        columnsMap[axisKey] = columnsOnAxis;\n        var stackId = seriesInfo.stackId;\n\n        if (!stacks[stackId]) {\n          columnsOnAxis.autoWidthCount++;\n        }\n\n        stacks[stackId] = stacks[stackId] || {\n          width: 0,\n          maxWidth: 0\n        }; // Caution: In a single coordinate system, these barGrid attributes\n        // will be shared by series. Consider that they have default values,\n        // only the attributes set on the last series will work.\n        // Do not change this fact unless there will be a break change.\n\n        var barWidth = seriesInfo.barWidth;\n\n        if (barWidth && !stacks[stackId].width) {\n          // See #6312, do not restrict width.\n          stacks[stackId].width = barWidth;\n          barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);\n          columnsOnAxis.remainedWidth -= barWidth;\n        }\n\n        var barMaxWidth = seriesInfo.barMaxWidth;\n        barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);\n        var barMinWidth = seriesInfo.barMinWidth;\n        barMinWidth && (stacks[stackId].minWidth = barMinWidth);\n        var barGap = seriesInfo.barGap;\n        barGap != null && (columnsOnAxis.gap = barGap);\n        var barCategoryGap = seriesInfo.barCategoryGap;\n        barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap);\n      });\n      var result = {};\n      each(columnsMap, function (columnsOnAxis, coordSysName) {\n        result[coordSysName] = {};\n        var stacks = columnsOnAxis.stacks;\n        var bandWidth = columnsOnAxis.bandWidth;\n        var categoryGapPercent = columnsOnAxis.categoryGap;\n\n        if (categoryGapPercent == null) {\n          var columnCount = keys(stacks).length; // More columns in one group\n          // the spaces between group is smaller. Or the column will be too thin.\n\n          categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%';\n        }\n\n        var categoryGap = parsePercent$1(categoryGapPercent, bandWidth);\n        var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);\n        var remainedWidth = columnsOnAxis.remainedWidth;\n        var autoWidthCount = columnsOnAxis.autoWidthCount;\n        var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0); // Find if any auto calculated bar exceeded maxBarWidth\n\n        each(stacks, function (column) {\n          var maxWidth = column.maxWidth;\n          var minWidth = column.minWidth;\n\n          if (!column.width) {\n            var finalWidth = autoWidth;\n\n            if (maxWidth && maxWidth < finalWidth) {\n              finalWidth = Math.min(maxWidth, remainedWidth);\n            } // `minWidth` has higher priority. `minWidth` decide that whether the\n            // bar is able to be visible. So `minWidth` should not be restricted\n            // by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In\n            // the extreme cases for `value` axis, bars are allowed to overlap\n            // with each other if `minWidth` specified.\n\n\n            if (minWidth && minWidth > finalWidth) {\n              finalWidth = minWidth;\n            }\n\n            if (finalWidth !== autoWidth) {\n              column.width = finalWidth;\n              remainedWidth -= finalWidth + barGapPercent * finalWidth;\n              autoWidthCount--;\n            }\n          } else {\n            // `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as\n            // CSS does. Because barWidth can be a percent value, where\n            // `barMaxWidth` can be used to restrict the final width.\n            var finalWidth = column.width;\n\n            if (maxWidth) {\n              finalWidth = Math.min(finalWidth, maxWidth);\n            } // `minWidth` has higher priority, as described above\n\n\n            if (minWidth) {\n              finalWidth = Math.max(finalWidth, minWidth);\n            }\n\n            column.width = finalWidth;\n            remainedWidth -= finalWidth + barGapPercent * finalWidth;\n            autoWidthCount--;\n          }\n        }); // Recalculate width again\n\n        autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0);\n        var widthSum = 0;\n        var lastColumn;\n        each(stacks, function (column, idx) {\n          if (!column.width) {\n            column.width = autoWidth;\n          }\n\n          lastColumn = column;\n          widthSum += column.width * (1 + barGapPercent);\n        });\n\n        if (lastColumn) {\n          widthSum -= lastColumn.width * barGapPercent;\n        }\n\n        var offset = -widthSum / 2;\n        each(stacks, function (column, stackId) {\n          result[coordSysName][stackId] = result[coordSysName][stackId] || {\n            bandWidth: bandWidth,\n            offset: offset,\n            width: column.width\n          };\n          offset += column.width * (1 + barGapPercent);\n        });\n      });\n      return result;\n    }\n\n    function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) {\n      if (barWidthAndOffset && axis) {\n        var result = barWidthAndOffset[getAxisKey(axis)];\n\n        if (result != null && seriesModel != null) {\n          return result[getSeriesStackId(seriesModel)];\n        }\n\n        return result;\n      }\n    }\n    function layout(seriesType, ecModel) {\n      var seriesModels = prepareLayoutBarSeries(seriesType, ecModel);\n      var barWidthAndOffset = makeColumnLayout(seriesModels);\n      each(seriesModels, function (seriesModel) {\n        var data = seriesModel.getData();\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n        var stackId = getSeriesStackId(seriesModel);\n        var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];\n        var columnOffset = columnLayoutInfo.offset;\n        var columnWidth = columnLayoutInfo.width;\n        data.setLayout({\n          bandWidth: columnLayoutInfo.bandWidth,\n          offset: columnOffset,\n          size: columnWidth\n        });\n      });\n    } // TODO: Do not support stack in large mode yet.\n\n    function createProgressiveLayout(seriesType) {\n      return {\n        seriesType: seriesType,\n        plan: createRenderPlanner(),\n        reset: function (seriesModel) {\n          if (!isOnCartesian(seriesModel)) {\n            return;\n          }\n\n          var data = seriesModel.getData();\n          var cartesian = seriesModel.coordinateSystem;\n          var baseAxis = cartesian.getBaseAxis();\n          var valueAxis = cartesian.getOtherAxis(baseAxis);\n          var valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim));\n          var baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n          var drawBackground = seriesModel.get('showBackground', true);\n          var valueDim = data.mapDimension(valueAxis.dim);\n          var stackResultDim = data.getCalculationInfo('stackResultDimension');\n          var stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries');\n          var isValueAxisH = valueAxis.isHorizontal();\n          var valueAxisStart = getValueAxisStart(baseAxis, valueAxis);\n          var isLarge = isInLargeMode(seriesModel);\n          var barMinHeight = seriesModel.get('barMinHeight') || 0;\n          var stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim); // Layout info.\n\n          var columnWidth = data.getLayout('size');\n          var columnOffset = data.getLayout('offset');\n          return {\n            progress: function (params, data) {\n              var count = params.count;\n              var largePoints = isLarge && createFloat32Array(count * 3);\n              var largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3);\n              var largeDataIndices = isLarge && createFloat32Array(count);\n              var coordLayout = cartesian.master.getRect();\n              var bgSize = isValueAxisH ? coordLayout.width : coordLayout.height;\n              var dataIndex;\n              var store = data.getStore();\n              var idxOffset = 0;\n\n              while ((dataIndex = params.next()) != null) {\n                var value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex);\n                var baseValue = store.get(baseDimIdx, dataIndex);\n                var baseCoord = valueAxisStart;\n                var startValue = void 0; // Because of the barMinHeight, we can not use the value in\n                // stackResultDimension directly.\n\n                if (stacked) {\n                  startValue = +value - store.get(valueDimIdx, dataIndex);\n                }\n\n                var x = void 0;\n                var y = void 0;\n                var width = void 0;\n                var height = void 0;\n\n                if (isValueAxisH) {\n                  var coord = cartesian.dataToPoint([value, baseValue]);\n\n                  if (stacked) {\n                    var startCoord = cartesian.dataToPoint([startValue, baseValue]);\n                    baseCoord = startCoord[0];\n                  }\n\n                  x = baseCoord;\n                  y = coord[1] + columnOffset;\n                  width = coord[0] - baseCoord;\n                  height = columnWidth;\n\n                  if (Math.abs(width) < barMinHeight) {\n                    width = (width < 0 ? -1 : 1) * barMinHeight;\n                  }\n                } else {\n                  var coord = cartesian.dataToPoint([baseValue, value]);\n\n                  if (stacked) {\n                    var startCoord = cartesian.dataToPoint([baseValue, startValue]);\n                    baseCoord = startCoord[1];\n                  }\n\n                  x = coord[0] + columnOffset;\n                  y = baseCoord;\n                  width = columnWidth;\n                  height = coord[1] - baseCoord;\n\n                  if (Math.abs(height) < barMinHeight) {\n                    // Include zero to has a positive bar\n                    height = (height <= 0 ? -1 : 1) * barMinHeight;\n                  }\n                }\n\n                if (!isLarge) {\n                  data.setItemLayout(dataIndex, {\n                    x: x,\n                    y: y,\n                    width: width,\n                    height: height\n                  });\n                } else {\n                  largePoints[idxOffset] = x;\n                  largePoints[idxOffset + 1] = y;\n                  largePoints[idxOffset + 2] = isValueAxisH ? width : height;\n\n                  if (largeBackgroundPoints) {\n                    largeBackgroundPoints[idxOffset] = isValueAxisH ? coordLayout.x : x;\n                    largeBackgroundPoints[idxOffset + 1] = isValueAxisH ? y : coordLayout.y;\n                    largeBackgroundPoints[idxOffset + 2] = bgSize;\n                  }\n\n                  largeDataIndices[dataIndex] = dataIndex;\n                }\n\n                idxOffset += 3;\n              }\n\n              if (isLarge) {\n                data.setLayout({\n                  largePoints: largePoints,\n                  largeDataIndices: largeDataIndices,\n                  largeBackgroundPoints: largeBackgroundPoints,\n                  valueAxisHorizontal: isValueAxisH\n                });\n              }\n            }\n          };\n        }\n      };\n    }\n\n    function isOnCartesian(seriesModel) {\n      return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d';\n    }\n\n    function isInLargeMode(seriesModel) {\n      return seriesModel.pipelineContext && seriesModel.pipelineContext.large;\n    } // See cases in `test/bar-start.html` and `#7412`, `#8747`.\n\n\n    function getValueAxisStart(baseAxis, valueAxis) {\n      return valueAxis.toGlobalCoord(valueAxis.dataToCoord(valueAxis.type === 'log' ? 1 : 0));\n    }\n\n    var bisect = function (a, x, lo, hi) {\n      while (lo < hi) {\n        var mid = lo + hi >>> 1;\n\n        if (a[mid][1] < x) {\n          lo = mid + 1;\n        } else {\n          hi = mid;\n        }\n      }\n\n      return lo;\n    };\n\n    var TimeScale =\n    /** @class */\n    function (_super) {\n      __extends(TimeScale, _super);\n\n      function TimeScale(settings) {\n        var _this = _super.call(this, settings) || this;\n\n        _this.type = 'time';\n        return _this;\n      }\n      /**\n       * Get label is mainly for other components like dataZoom, tooltip.\n       */\n\n\n      TimeScale.prototype.getLabel = function (tick) {\n        var useUTC = this.getSetting('useUTC');\n        return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale'));\n      };\n\n      TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) {\n        var isUTC = this.getSetting('useUTC');\n        var lang = this.getSetting('locale');\n        return leveledFormat(tick, idx, labelFormatter, lang, isUTC);\n      };\n      /**\n       * @override\n       */\n\n\n      TimeScale.prototype.getTicks = function () {\n        var interval = this._interval;\n        var extent = this._extent;\n        var ticks = []; // If interval is 0, return [];\n\n        if (!interval) {\n          return ticks;\n        }\n\n        ticks.push({\n          value: extent[0],\n          level: 0\n        });\n        var useUTC = this.getSetting('useUTC');\n        var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent);\n        ticks = ticks.concat(innerTicks);\n        ticks.push({\n          value: extent[1],\n          level: 0\n        });\n        return ticks;\n      };\n\n      TimeScale.prototype.calcNiceExtent = function (opt) {\n        var extent = this._extent; // If extent start and end are same, expand them\n\n        if (extent[0] === extent[1]) {\n          // Expand extent\n          extent[0] -= ONE_DAY;\n          extent[1] += ONE_DAY;\n        } // If there are no data and extent are [Infinity, -Infinity]\n\n\n        if (extent[1] === -Infinity && extent[0] === Infinity) {\n          var d = new Date();\n          extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());\n          extent[0] = extent[1] - ONE_DAY;\n        }\n\n        this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);\n      };\n\n      TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) {\n        approxTickNum = approxTickNum || 10;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n        this._approxInterval = span / approxTickNum;\n\n        if (minInterval != null && this._approxInterval < minInterval) {\n          this._approxInterval = minInterval;\n        }\n\n        if (maxInterval != null && this._approxInterval > maxInterval) {\n          this._approxInterval = maxInterval;\n        }\n\n        var scaleIntervalsLen = scaleIntervals.length;\n        var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1); // Interval that can be used to calculate ticks\n\n        this._interval = scaleIntervals[idx][1]; // Min level used when picking ticks from top down.\n        // We check one more level to avoid the ticks are to sparse in some case.\n\n        this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0];\n      };\n\n      TimeScale.prototype.parse = function (val) {\n        // val might be float.\n        return isNumber(val) ? val : +parseDate(val);\n      };\n\n      TimeScale.prototype.contain = function (val) {\n        return contain$1(this.parse(val), this._extent);\n      };\n\n      TimeScale.prototype.normalize = function (val) {\n        return normalize$1(this.parse(val), this._extent);\n      };\n\n      TimeScale.prototype.scale = function (val) {\n        return scale$2(val, this._extent);\n      };\n\n      TimeScale.type = 'time';\n      return TimeScale;\n    }(IntervalScale);\n    /**\n     * This implementation was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     */\n\n\n    var scaleIntervals = [// Format                           interval\n    ['second', ONE_SECOND], ['minute', ONE_MINUTE], ['hour', ONE_HOUR], ['quarter-day', ONE_HOUR * 6], ['half-day', ONE_HOUR * 12], ['day', ONE_DAY * 1.2], ['half-week', ONE_DAY * 3.5], ['week', ONE_DAY * 7], ['month', ONE_DAY * 31], ['quarter', ONE_DAY * 95], ['half-year', ONE_YEAR / 2], ['year', ONE_YEAR] // 1Y\n    ];\n\n    function isUnitValueSame(unit, valueA, valueB, isUTC) {\n      var dateA = parseDate(valueA);\n      var dateB = parseDate(valueB);\n\n      var isSame = function (unit) {\n        return getUnitValue(dateA, unit, isUTC) === getUnitValue(dateB, unit, isUTC);\n      };\n\n      var isSameYear = function () {\n        return isSame('year');\n      }; // const isSameHalfYear = () => isSameYear() && isSame('half-year');\n      // const isSameQuater = () => isSameYear() && isSame('quarter');\n\n\n      var isSameMonth = function () {\n        return isSameYear() && isSame('month');\n      };\n\n      var isSameDay = function () {\n        return isSameMonth() && isSame('day');\n      }; // const isSameHalfDay = () => isSameDay() && isSame('half-day');\n\n\n      var isSameHour = function () {\n        return isSameDay() && isSame('hour');\n      };\n\n      var isSameMinute = function () {\n        return isSameHour() && isSame('minute');\n      };\n\n      var isSameSecond = function () {\n        return isSameMinute() && isSame('second');\n      };\n\n      var isSameMilliSecond = function () {\n        return isSameSecond() && isSame('millisecond');\n      };\n\n      switch (unit) {\n        case 'year':\n          return isSameYear();\n\n        case 'month':\n          return isSameMonth();\n\n        case 'day':\n          return isSameDay();\n\n        case 'hour':\n          return isSameHour();\n\n        case 'minute':\n          return isSameMinute();\n\n        case 'second':\n          return isSameSecond();\n\n        case 'millisecond':\n          return isSameMilliSecond();\n      }\n    } // const primaryUnitGetters = {\n    //     year: fullYearGetterName(),\n    //     month: monthGetterName(),\n    //     day: dateGetterName(),\n    //     hour: hoursGetterName(),\n    //     minute: minutesGetterName(),\n    //     second: secondsGetterName(),\n    //     millisecond: millisecondsGetterName()\n    // };\n    // const primaryUnitUTCGetters = {\n    //     year: fullYearGetterName(true),\n    //     month: monthGetterName(true),\n    //     day: dateGetterName(true),\n    //     hour: hoursGetterName(true),\n    //     minute: minutesGetterName(true),\n    //     second: secondsGetterName(true),\n    //     millisecond: millisecondsGetterName(true)\n    // };\n    // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) {\n    //     step = step || 1;\n    //     switch (getPrimaryTimeUnit(unitName)) {\n    //         case 'year':\n    //             date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'month':\n    //             date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'day':\n    //             date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'hour':\n    //             date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'minute':\n    //             date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'second':\n    //             date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'millisecond':\n    //             date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step);\n    //             break;\n    //     }\n    //     return date.getTime();\n    // }\n    // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]];\n    // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]];\n    // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]];\n\n\n    function getDateInterval(approxInterval, daysInMonth) {\n      approxInterval /= ONE_DAY;\n      return approxInterval > 16 ? 16 // Math.floor(daysInMonth / 2) + 1  // In this case we only want one tick between two months.\n      : approxInterval > 7.5 ? 7 // TODO week 7 or day 8?\n      : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1;\n    }\n\n    function getMonthInterval(approxInterval) {\n      var APPROX_ONE_MONTH = 30 * ONE_DAY;\n      approxInterval /= APPROX_ONE_MONTH;\n      return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getHourInterval(approxInterval) {\n      approxInterval /= ONE_HOUR;\n      return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getMinutesAndSecondsInterval(approxInterval, isMinutes) {\n      approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND;\n      return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getMillisecondsInterval(approxInterval) {\n      return nice(approxInterval, true);\n    }\n\n    function getFirstTimestampOfUnit(date, unitName, isUTC) {\n      var outDate = new Date(date);\n\n      switch (getPrimaryTimeUnit(unitName)) {\n        case 'year':\n        case 'month':\n          outDate[monthSetterName(isUTC)](0);\n\n        case 'day':\n          outDate[dateSetterName(isUTC)](1);\n\n        case 'hour':\n          outDate[hoursSetterName(isUTC)](0);\n\n        case 'minute':\n          outDate[minutesSetterName(isUTC)](0);\n\n        case 'second':\n          outDate[secondsSetterName(isUTC)](0);\n          outDate[millisecondsSetterName(isUTC)](0);\n      }\n\n      return outDate.getTime();\n    }\n\n    function getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent) {\n      var safeLimit = 10000;\n      var unitNames = timeUnits;\n      var iter = 0;\n\n      function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) {\n        var date = new Date(minTimestamp);\n        var dateTime = minTimestamp;\n        var d = date[getMethodName](); // if (isDate) {\n        //     d -= 1; // Starts with 0;   PENDING\n        // }\n\n        while (dateTime < maxTimestamp && dateTime <= extent[1]) {\n          out.push({\n            value: dateTime\n          });\n          d += interval;\n          date[setMethodName](d);\n          dateTime = date.getTime();\n        } // This extra tick is for calcuating ticks of next level. Will not been added to the final result\n\n\n        out.push({\n          value: dateTime,\n          notAdd: true\n        });\n      }\n\n      function addLevelTicks(unitName, lastLevelTicks, levelTicks) {\n        var newAddedTicks = [];\n        var isFirstLevel = !lastLevelTicks.length;\n\n        if (isUnitValueSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) {\n          return;\n        }\n\n        if (isFirstLevel) {\n          lastLevelTicks = [{\n            // TODO Optimize. Not include so may ticks.\n            value: getFirstTimestampOfUnit(new Date(extent[0]), unitName, isUTC)\n          }, {\n            value: extent[1]\n          }];\n        }\n\n        for (var i = 0; i < lastLevelTicks.length - 1; i++) {\n          var startTick = lastLevelTicks[i].value;\n          var endTick = lastLevelTicks[i + 1].value;\n\n          if (startTick === endTick) {\n            continue;\n          }\n\n          var interval = void 0;\n          var getterName = void 0;\n          var setterName = void 0;\n          var isDate = false;\n\n          switch (unitName) {\n            case 'year':\n              interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365));\n              getterName = fullYearGetterName(isUTC);\n              setterName = fullYearSetterName(isUTC);\n              break;\n\n            case 'half-year':\n            case 'quarter':\n            case 'month':\n              interval = getMonthInterval(approxInterval);\n              getterName = monthGetterName(isUTC);\n              setterName = monthSetterName(isUTC);\n              break;\n\n            case 'week': // PENDING If week is added. Ignore day.\n\n            case 'half-week':\n            case 'day':\n              interval = getDateInterval(approxInterval); // Use 32 days and let interval been 16\n\n              getterName = dateGetterName(isUTC);\n              setterName = dateSetterName(isUTC);\n              isDate = true;\n              break;\n\n            case 'half-day':\n            case 'quarter-day':\n            case 'hour':\n              interval = getHourInterval(approxInterval);\n              getterName = hoursGetterName(isUTC);\n              setterName = hoursSetterName(isUTC);\n              break;\n\n            case 'minute':\n              interval = getMinutesAndSecondsInterval(approxInterval, true);\n              getterName = minutesGetterName(isUTC);\n              setterName = minutesSetterName(isUTC);\n              break;\n\n            case 'second':\n              interval = getMinutesAndSecondsInterval(approxInterval, false);\n              getterName = secondsGetterName(isUTC);\n              setterName = secondsSetterName(isUTC);\n              break;\n\n            case 'millisecond':\n              interval = getMillisecondsInterval(approxInterval);\n              getterName = millisecondsGetterName(isUTC);\n              setterName = millisecondsSetterName(isUTC);\n              break;\n          }\n\n          addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks);\n\n          if (unitName === 'year' && levelTicks.length > 1 && i === 0) {\n            // Add nearest years to the left extent.\n            levelTicks.unshift({\n              value: levelTicks[0].value - interval\n            });\n          }\n        }\n\n        for (var i = 0; i < newAddedTicks.length; i++) {\n          levelTicks.push(newAddedTicks[i]);\n        } // newAddedTicks.length && console.log(unitName, newAddedTicks);\n\n\n        return newAddedTicks;\n      }\n\n      var levelsTicks = [];\n      var currentLevelTicks = [];\n      var tickCount = 0;\n      var lastLevelTickCount = 0;\n\n      for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) {\n        var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]);\n\n        if (!isPrimaryTimeUnit(unitNames[i])) {\n          // TODO\n          continue;\n        }\n\n        addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks);\n        var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null;\n\n        if (primaryTimeUnit !== nextPrimaryTimeUnit) {\n          if (currentLevelTicks.length) {\n            lastLevelTickCount = tickCount; // Remove the duplicate so the tick count can be precisely.\n\n            currentLevelTicks.sort(function (a, b) {\n              return a.value - b.value;\n            });\n            var levelTicksRemoveDuplicated = [];\n\n            for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) {\n              var tickValue = currentLevelTicks[i_1].value;\n\n              if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) {\n                levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]);\n\n                if (tickValue >= extent[0] && tickValue <= extent[1]) {\n                  tickCount++;\n                }\n              }\n            }\n\n            var targetTickNum = (extent[1] - extent[0]) / approxInterval; // Added too much in this level and not too less in last level\n\n            if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) {\n              break;\n            } // Only treat primary time unit as one level.\n\n\n            levelsTicks.push(levelTicksRemoveDuplicated);\n\n            if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) {\n              break;\n            }\n          } // Reset if next unitName is primary\n\n\n          currentLevelTicks = [];\n        }\n      }\n\n      if (\"development\" !== 'production') {\n        if (iter >= safeLimit) {\n          warn('Exceed safe limit.');\n        }\n      }\n\n      var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) {\n        return filter(levelTicks, function (tick) {\n          return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd;\n        });\n      }), function (levelTicks) {\n        return levelTicks.length > 0;\n      });\n      var ticks = [];\n      var maxLevel = levelsTicksInExtent.length - 1;\n\n      for (var i = 0; i < levelsTicksInExtent.length; ++i) {\n        var levelTicks = levelsTicksInExtent[i];\n\n        for (var k = 0; k < levelTicks.length; ++k) {\n          ticks.push({\n            value: levelTicks[k].value,\n            level: maxLevel - i\n          });\n        }\n      }\n\n      ticks.sort(function (a, b) {\n        return a.value - b.value;\n      }); // Remove duplicates\n\n      var result = [];\n\n      for (var i = 0; i < ticks.length; ++i) {\n        if (i === 0 || ticks[i].value !== ticks[i - 1].value) {\n          result.push(ticks[i]);\n        }\n      }\n\n      return result;\n    }\n\n    Scale.registerClass(TimeScale);\n\n    var scaleProto = Scale.prototype; // FIXME:TS refactor: not good to call it directly with `this`?\n\n    var intervalScaleProto = IntervalScale.prototype;\n    var roundingErrorFix = round;\n    var mathFloor = Math.floor;\n    var mathCeil = Math.ceil;\n    var mathPow$1 = Math.pow;\n    var mathLog = Math.log;\n\n    var LogScale =\n    /** @class */\n    function (_super) {\n      __extends(LogScale, _super);\n\n      function LogScale() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'log';\n        _this.base = 10;\n        _this._originalScale = new IntervalScale(); // FIXME:TS actually used by `IntervalScale`\n\n        _this._interval = 0;\n        return _this;\n      }\n      /**\n       * @param Whether expand the ticks to niced extent.\n       */\n\n\n      LogScale.prototype.getTicks = function (expandToNicedExtent) {\n        var originalScale = this._originalScale;\n        var extent = this._extent;\n        var originalExtent = originalScale.getExtent();\n        var ticks = intervalScaleProto.getTicks.call(this, expandToNicedExtent);\n        return map(ticks, function (tick) {\n          var val = tick.value;\n          var powVal = round(mathPow$1(this.base, val)); // Fix #4158\n\n          powVal = val === extent[0] && this._fixMin ? fixRoundingError(powVal, originalExtent[0]) : powVal;\n          powVal = val === extent[1] && this._fixMax ? fixRoundingError(powVal, originalExtent[1]) : powVal;\n          return {\n            value: powVal\n          };\n        }, this);\n      };\n\n      LogScale.prototype.setExtent = function (start, end) {\n        var base = mathLog(this.base); // log(-Infinity) is NaN, so safe guard here\n\n        start = mathLog(Math.max(0, start)) / base;\n        end = mathLog(Math.max(0, end)) / base;\n        intervalScaleProto.setExtent.call(this, start, end);\n      };\n      /**\n       * @return {number} end\n       */\n\n\n      LogScale.prototype.getExtent = function () {\n        var base = this.base;\n        var extent = scaleProto.getExtent.call(this);\n        extent[0] = mathPow$1(base, extent[0]);\n        extent[1] = mathPow$1(base, extent[1]); // Fix #4158\n\n        var originalScale = this._originalScale;\n        var originalExtent = originalScale.getExtent();\n        this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));\n        this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));\n        return extent;\n      };\n\n      LogScale.prototype.unionExtent = function (extent) {\n        this._originalScale.unionExtent(extent);\n\n        var base = this.base;\n        extent[0] = mathLog(extent[0]) / mathLog(base);\n        extent[1] = mathLog(extent[1]) / mathLog(base);\n        scaleProto.unionExtent.call(this, extent);\n      };\n\n      LogScale.prototype.unionExtentFromData = function (data, dim) {\n        // TODO\n        // filter value that <= 0\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * Update interval and extent of intervals for nice ticks\n       * @param approxTickNum default 10 Given approx tick number\n       */\n\n\n      LogScale.prototype.calcNiceTicks = function (approxTickNum) {\n        approxTickNum = approxTickNum || 10;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n\n        if (span === Infinity || span <= 0) {\n          return;\n        }\n\n        var interval = quantity(span);\n        var err = approxTickNum / span * interval; // Filter ticks to get closer to the desired count.\n\n        if (err <= 0.5) {\n          interval *= 10;\n        } // Interval should be integer\n\n\n        while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {\n          interval *= 10;\n        }\n\n        var niceExtent = [round(mathCeil(extent[0] / interval) * interval), round(mathFloor(extent[1] / interval) * interval)];\n        this._interval = interval;\n        this._niceExtent = niceExtent;\n      };\n\n      LogScale.prototype.calcNiceExtent = function (opt) {\n        intervalScaleProto.calcNiceExtent.call(this, opt);\n        this._fixMin = opt.fixMin;\n        this._fixMax = opt.fixMax;\n      };\n\n      LogScale.prototype.parse = function (val) {\n        return val;\n      };\n\n      LogScale.prototype.contain = function (val) {\n        val = mathLog(val) / mathLog(this.base);\n        return contain$1(val, this._extent);\n      };\n\n      LogScale.prototype.normalize = function (val) {\n        val = mathLog(val) / mathLog(this.base);\n        return normalize$1(val, this._extent);\n      };\n\n      LogScale.prototype.scale = function (val) {\n        val = scale$2(val, this._extent);\n        return mathPow$1(this.base, val);\n      };\n\n      LogScale.type = 'log';\n      return LogScale;\n    }(Scale);\n\n    var proto = LogScale.prototype;\n    proto.getMinorTicks = intervalScaleProto.getMinorTicks;\n    proto.getLabel = intervalScaleProto.getLabel;\n\n    function fixRoundingError(val, originalVal) {\n      return roundingErrorFix(val, getPrecision(originalVal));\n    }\n\n    Scale.registerClass(LogScale);\n\n    var ScaleRawExtentInfo =\n    /** @class */\n    function () {\n      function ScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\n      originalExtent) {\n        this._prepareParams(scale, model, originalExtent);\n      }\n      /**\n       * Parameters depending on outside (like model, user callback)\n       * are prepared and fixed here.\n       */\n\n\n      ScaleRawExtentInfo.prototype._prepareParams = function (scale, model, // Usually: data extent from all series on this axis.\n      dataExtent) {\n        if (dataExtent[1] < dataExtent[0]) {\n          dataExtent = [NaN, NaN];\n        }\n\n        this._dataMin = dataExtent[0];\n        this._dataMax = dataExtent[1];\n        var isOrdinal = this._isOrdinal = scale.type === 'ordinal';\n        this._needCrossZero = scale.type === 'interval' && model.getNeedCrossZero && model.getNeedCrossZero();\n        var modelMinRaw = this._modelMinRaw = model.get('min', true);\n\n        if (isFunction(modelMinRaw)) {\n          // This callback always provides users the full data extent (before data is filtered).\n          this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw({\n            min: dataExtent[0],\n            max: dataExtent[1]\n          }));\n        } else if (modelMinRaw !== 'dataMin') {\n          this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw);\n        }\n\n        var modelMaxRaw = this._modelMaxRaw = model.get('max', true);\n\n        if (isFunction(modelMaxRaw)) {\n          // This callback always provides users the full data extent (before data is filtered).\n          this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw({\n            min: dataExtent[0],\n            max: dataExtent[1]\n          }));\n        } else if (modelMaxRaw !== 'dataMax') {\n          this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw);\n        }\n\n        if (isOrdinal) {\n          // FIXME: there is a flaw here: if there is no \"block\" data processor like `dataZoom`,\n          // and progressive rendering is using, here the category result might just only contain\n          // the processed chunk rather than the entire result.\n          this._axisDataLen = model.getCategories().length;\n        } else {\n          var boundaryGap = model.get('boundaryGap');\n          var boundaryGapArr = isArray(boundaryGap) ? boundaryGap : [boundaryGap || 0, boundaryGap || 0];\n\n          if (typeof boundaryGapArr[0] === 'boolean' || typeof boundaryGapArr[1] === 'boolean') {\n            if (\"development\" !== 'production') {\n              console.warn('Boolean type for boundaryGap is only ' + 'allowed for ordinal axis. Please use string in ' + 'percentage instead, e.g., \"20%\". Currently, ' + 'boundaryGap is set to be 0.');\n            }\n\n            this._boundaryGapInner = [0, 0];\n          } else {\n            this._boundaryGapInner = [parsePercent(boundaryGapArr[0], 1), parsePercent(boundaryGapArr[1], 1)];\n          }\n        }\n      };\n      /**\n       * Calculate extent by prepared parameters.\n       * This method has no external dependency and can be called duplicatedly,\n       * getting the same result.\n       * If parameters changed, should call this method to recalcuate.\n       */\n\n\n      ScaleRawExtentInfo.prototype.calculate = function () {\n        // Notice: When min/max is not set (that is, when there are null/undefined,\n        // which is the most common case), these cases should be ensured:\n        // (1) For 'ordinal', show all axis.data.\n        // (2) For others:\n        //      + `boundaryGap` is applied (if min/max set, boundaryGap is\n        //      disabled).\n        //      + If `needCrossZero`, min/max should be zero, otherwise, min/max should\n        //      be the result that originalExtent enlarged by boundaryGap.\n        // (3) If no data, it should be ensured that `scale.setBlank` is set.\n        var isOrdinal = this._isOrdinal;\n        var dataMin = this._dataMin;\n        var dataMax = this._dataMax;\n        var axisDataLen = this._axisDataLen;\n        var boundaryGapInner = this._boundaryGapInner;\n        var span = !isOrdinal ? dataMax - dataMin || Math.abs(dataMin) : null; // Currently if a `'value'` axis model min is specified as 'dataMin'/'dataMax',\n        // `boundaryGap` will not be used. It's the different from specifying as `null`/`undefined`.\n\n        var min = this._modelMinRaw === 'dataMin' ? dataMin : this._modelMinNum;\n        var max = this._modelMaxRaw === 'dataMax' ? dataMax : this._modelMaxNum; // If `_modelMinNum`/`_modelMaxNum` is `null`/`undefined`, should not be fixed.\n\n        var minFixed = min != null;\n        var maxFixed = max != null;\n\n        if (min == null) {\n          min = isOrdinal ? axisDataLen ? 0 : NaN : dataMin - boundaryGapInner[0] * span;\n        }\n\n        if (max == null) {\n          max = isOrdinal ? axisDataLen ? axisDataLen - 1 : NaN : dataMax + boundaryGapInner[1] * span;\n        }\n\n        (min == null || !isFinite(min)) && (min = NaN);\n        (max == null || !isFinite(max)) && (max = NaN);\n        var isBlank = eqNaN(min) || eqNaN(max) || isOrdinal && !axisDataLen; // If data extent modified, need to recalculated to ensure cross zero.\n\n        if (this._needCrossZero) {\n          // Axis is over zero and min is not set\n          if (min > 0 && max > 0 && !minFixed) {\n            min = 0; // minFixed = true;\n          } // Axis is under zero and max is not set\n\n\n          if (min < 0 && max < 0 && !maxFixed) {\n            max = 0; // maxFixed = true;\n          } // PENDING:\n          // When `needCrossZero` and all data is positive/negative, should it be ensured\n          // that the results processed by boundaryGap are positive/negative?\n          // If so, here `minFixed`/`maxFixed` need to be set.\n\n        }\n\n        var determinedMin = this._determinedMin;\n        var determinedMax = this._determinedMax;\n\n        if (determinedMin != null) {\n          min = determinedMin;\n          minFixed = true;\n        }\n\n        if (determinedMax != null) {\n          max = determinedMax;\n          maxFixed = true;\n        } // Ensure min/max be finite number or NaN here. (not to be null/undefined)\n        // `NaN` means min/max axis is blank.\n\n\n        return {\n          min: min,\n          max: max,\n          minFixed: minFixed,\n          maxFixed: maxFixed,\n          isBlank: isBlank\n        };\n      };\n\n      ScaleRawExtentInfo.prototype.modifyDataMinMax = function (minMaxName, val) {\n        if (\"development\" !== 'production') {\n          assert(!this.frozen);\n        }\n\n        this[DATA_MIN_MAX_ATTR[minMaxName]] = val;\n      };\n\n      ScaleRawExtentInfo.prototype.setDeterminedMinMax = function (minMaxName, val) {\n        var attr = DETERMINED_MIN_MAX_ATTR[minMaxName];\n\n        if (\"development\" !== 'production') {\n          assert(!this.frozen // Earse them usually means logic flaw.\n          && this[attr] == null);\n        }\n\n        this[attr] = val;\n      };\n\n      ScaleRawExtentInfo.prototype.freeze = function () {\n        // @ts-ignore\n        this.frozen = true;\n      };\n\n      return ScaleRawExtentInfo;\n    }();\n    var DETERMINED_MIN_MAX_ATTR = {\n      min: '_determinedMin',\n      max: '_determinedMax'\n    };\n    var DATA_MIN_MAX_ATTR = {\n      min: '_dataMin',\n      max: '_dataMax'\n    };\n    /**\n     * Get scale min max and related info only depends on model settings.\n     * This method can be called after coordinate system created.\n     * For example, in data processing stage.\n     *\n     * Scale extent info probably be required multiple times during a workflow.\n     * For example:\n     * (1) `dataZoom` depends it to get the axis extent in \"100%\" state.\n     * (2) `processor/extentCalculator` depends it to make sure whether axis extent is specified.\n     * (3) `coordSys.update` use it to finally decide the scale extent.\n     * But the callback of `min`/`max` should not be called multiple times.\n     * The code below should not be implemented repeatedly either.\n     * So we cache the result in the scale instance, which will be recreated at the beginning\n     * of the workflow (because `scale` instance will be recreated each round of the workflow).\n     */\n\n    function ensureScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\n    originalExtent) {\n      // Do not permit to recreate.\n      var rawExtentInfo = scale.rawExtentInfo;\n\n      if (rawExtentInfo) {\n        return rawExtentInfo;\n      }\n\n      rawExtentInfo = new ScaleRawExtentInfo(scale, model, originalExtent); // @ts-ignore\n\n      scale.rawExtentInfo = rawExtentInfo;\n      return rawExtentInfo;\n    }\n    function parseAxisModelMinMax(scale, minMax) {\n      return minMax == null ? null : eqNaN(minMax) ? NaN : scale.parse(minMax);\n    }\n\n    /**\n     * Get axis scale extent before niced.\n     * Item of returned array can only be number (including Infinity and NaN).\n     *\n     * Caution:\n     * Precondition of calling this method:\n     * The scale extent has been initialized using series data extent via\n     * `scale.setExtent` or `scale.unionExtentFromData`;\n     */\n\n    function getScaleExtent(scale, model) {\n      var scaleType = scale.type;\n      var rawExtentResult = ensureScaleRawExtentInfo(scale, model, scale.getExtent()).calculate();\n      scale.setBlank(rawExtentResult.isBlank);\n      var min = rawExtentResult.min;\n      var max = rawExtentResult.max; // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis\n      // is base axis\n      // FIXME\n      // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly.\n      // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent?\n      //     Should not depend on series type `bar`?\n      // (3) Fix that might overlap when using dataZoom.\n      // (4) Consider other chart types using `barGrid`?\n      // See #6728, #4862, `test/bar-overflow-time-plot.html`\n\n      var ecModel = model.ecModel;\n\n      if (ecModel && scaleType === 'time'\n      /* || scaleType === 'interval' */\n      ) {\n        var barSeriesModels = prepareLayoutBarSeries('bar', ecModel);\n        var isBaseAxisAndHasBarSeries_1 = false;\n        each(barSeriesModels, function (seriesModel) {\n          isBaseAxisAndHasBarSeries_1 = isBaseAxisAndHasBarSeries_1 || seriesModel.getBaseAxis() === model.axis;\n        });\n\n        if (isBaseAxisAndHasBarSeries_1) {\n          // Calculate placement of bars on axis. TODO should be decoupled\n          // with barLayout\n          var barWidthAndOffset = makeColumnLayout(barSeriesModels); // Adjust axis min and max to account for overflow\n\n          var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset);\n          min = adjustedScale.min;\n          max = adjustedScale.max;\n        }\n      }\n\n      return {\n        extent: [min, max],\n        // \"fix\" means \"fixed\", the value should not be\n        // changed in the subsequent steps.\n        fixMin: rawExtentResult.minFixed,\n        fixMax: rawExtentResult.maxFixed\n      };\n    }\n\n    function adjustScaleForOverflow(min, max, model, // Only support cartesian coord yet.\n    barWidthAndOffset) {\n      // Get Axis Length\n      var axisExtent = model.axis.getExtent();\n      var axisLength = axisExtent[1] - axisExtent[0]; // Get bars on current base axis and calculate min and max overflow\n\n      var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis);\n\n      if (barsOnCurrentAxis === undefined) {\n        return {\n          min: min,\n          max: max\n        };\n      }\n\n      var minOverflow = Infinity;\n      each(barsOnCurrentAxis, function (item) {\n        minOverflow = Math.min(item.offset, minOverflow);\n      });\n      var maxOverflow = -Infinity;\n      each(barsOnCurrentAxis, function (item) {\n        maxOverflow = Math.max(item.offset + item.width, maxOverflow);\n      });\n      minOverflow = Math.abs(minOverflow);\n      maxOverflow = Math.abs(maxOverflow);\n      var totalOverFlow = minOverflow + maxOverflow; // Calculate required buffer based on old range and overflow\n\n      var oldRange = max - min;\n      var oldRangePercentOfNew = 1 - (minOverflow + maxOverflow) / axisLength;\n      var overflowBuffer = oldRange / oldRangePercentOfNew - oldRange;\n      max += overflowBuffer * (maxOverflow / totalOverFlow);\n      min -= overflowBuffer * (minOverflow / totalOverFlow);\n      return {\n        min: min,\n        max: max\n      };\n    } // Precondition of calling this method:\n    // The scale extent has been initialized using series data extent via\n    // `scale.setExtent` or `scale.unionExtentFromData`;\n\n\n    function niceScaleExtent(scale, inModel) {\n      var model = inModel;\n      var extentInfo = getScaleExtent(scale, model);\n      var extent = extentInfo.extent;\n      var splitNumber = model.get('splitNumber');\n\n      if (scale instanceof LogScale) {\n        scale.base = model.get('logBase');\n      }\n\n      var scaleType = scale.type;\n      var interval = model.get('interval');\n      var isIntervalOrTime = scaleType === 'interval' || scaleType === 'time';\n      scale.setExtent(extent[0], extent[1]);\n      scale.calcNiceExtent({\n        splitNumber: splitNumber,\n        fixMin: extentInfo.fixMin,\n        fixMax: extentInfo.fixMax,\n        minInterval: isIntervalOrTime ? model.get('minInterval') : null,\n        maxInterval: isIntervalOrTime ? model.get('maxInterval') : null\n      }); // If some one specified the min, max. And the default calculated interval\n      // is not good enough. He can specify the interval. It is often appeared\n      // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard\n      // to be 60.\n      // FIXME\n\n      if (interval != null) {\n        scale.setInterval && scale.setInterval(interval);\n      }\n    }\n    /**\n     * @param axisType Default retrieve from model.type\n     */\n\n    function createScaleByModel(model, axisType) {\n      axisType = axisType || model.get('type');\n\n      if (axisType) {\n        switch (axisType) {\n          // Buildin scale\n          case 'category':\n            return new OrdinalScale({\n              ordinalMeta: model.getOrdinalMeta ? model.getOrdinalMeta() : model.getCategories(),\n              extent: [Infinity, -Infinity]\n            });\n\n          case 'time':\n            return new TimeScale({\n              locale: model.ecModel.getLocaleModel(),\n              useUTC: model.ecModel.get('useUTC')\n            });\n\n          default:\n            // case 'value'/'interval', 'log', or others.\n            return new (Scale.getClass(axisType) || IntervalScale)();\n        }\n      }\n    }\n    /**\n     * Check if the axis cross 0\n     */\n\n    function ifAxisCrossZero(axis) {\n      var dataExtent = axis.scale.getExtent();\n      var min = dataExtent[0];\n      var max = dataExtent[1];\n      return !(min > 0 && max > 0 || min < 0 && max < 0);\n    }\n    /**\n     * @param axis\n     * @return Label formatter function.\n     *         param: {number} tickValue,\n     *         param: {number} idx, the index in all ticks.\n     *                         If category axis, this param is not required.\n     *         return: {string} label string.\n     */\n\n    function makeLabelFormatter(axis) {\n      var labelFormatter = axis.getLabelModel().get('formatter');\n      var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null;\n\n      if (axis.scale.type === 'time') {\n        return function (tpl) {\n          return function (tick, idx) {\n            return axis.scale.getFormattedLabel(tick, idx, tpl);\n          };\n        }(labelFormatter);\n      } else if (isString(labelFormatter)) {\n        return function (tpl) {\n          return function (tick) {\n            // For category axis, get raw value; for numeric axis,\n            // get formatted label like '1,333,444'.\n            var label = axis.scale.getLabel(tick);\n            var text = tpl.replace('{value}', label != null ? label : '');\n            return text;\n          };\n        }(labelFormatter);\n      } else if (isFunction(labelFormatter)) {\n        return function (cb) {\n          return function (tick, idx) {\n            // The original intention of `idx` is \"the index of the tick in all ticks\".\n            // But the previous implementation of category axis do not consider the\n            // `axisLabel.interval`, which cause that, for example, the `interval` is\n            // `1`, then the ticks \"name5\", \"name7\", \"name9\" are displayed, where the\n            // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep\n            // the definition here for back compatibility.\n            if (categoryTickStart != null) {\n              idx = tick.value - categoryTickStart;\n            }\n\n            return cb(getAxisRawValue(axis, tick), idx, tick.level != null ? {\n              level: tick.level\n            } : null);\n          };\n        }(labelFormatter);\n      } else {\n        return function (tick) {\n          return axis.scale.getLabel(tick);\n        };\n      }\n    }\n    function getAxisRawValue(axis, tick) {\n      // In category axis with data zoom, tick is not the original\n      // index of axis.data. So tick should not be exposed to user\n      // in category axis.\n      return axis.type === 'category' ? axis.scale.getLabel(tick) : tick.value;\n    }\n    /**\n     * @param axis\n     * @return Be null/undefined if no labels.\n     */\n\n    function estimateLabelUnionRect(axis) {\n      var axisModel = axis.model;\n      var scale = axis.scale;\n\n      if (!axisModel.get(['axisLabel', 'show']) || scale.isBlank()) {\n        return;\n      }\n\n      var realNumberScaleTicks;\n      var tickCount;\n      var categoryScaleExtent = scale.getExtent(); // Optimize for large category data, avoid call `getTicks()`.\n\n      if (scale instanceof OrdinalScale) {\n        tickCount = scale.count();\n      } else {\n        realNumberScaleTicks = scale.getTicks();\n        tickCount = realNumberScaleTicks.length;\n      }\n\n      var axisLabelModel = axis.getLabelModel();\n      var labelFormatter = makeLabelFormatter(axis);\n      var rect;\n      var step = 1; // Simple optimization for large amount of labels\n\n      if (tickCount > 40) {\n        step = Math.ceil(tickCount / 40);\n      }\n\n      for (var i = 0; i < tickCount; i += step) {\n        var tick = realNumberScaleTicks ? realNumberScaleTicks[i] : {\n          value: categoryScaleExtent[0] + i\n        };\n        var label = labelFormatter(tick, i);\n        var unrotatedSingleRect = axisLabelModel.getTextRect(label);\n        var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);\n        rect ? rect.union(singleRect) : rect = singleRect;\n      }\n\n      return rect;\n    }\n\n    function rotateTextRect(textRect, rotate) {\n      var rotateRadians = rotate * Math.PI / 180;\n      var beforeWidth = textRect.width;\n      var beforeHeight = textRect.height;\n      var afterWidth = beforeWidth * Math.abs(Math.cos(rotateRadians)) + Math.abs(beforeHeight * Math.sin(rotateRadians));\n      var afterHeight = beforeWidth * Math.abs(Math.sin(rotateRadians)) + Math.abs(beforeHeight * Math.cos(rotateRadians));\n      var rotatedRect = new BoundingRect(textRect.x, textRect.y, afterWidth, afterHeight);\n      return rotatedRect;\n    }\n    /**\n     * @param model axisLabelModel or axisTickModel\n     * @return {number|String} Can be null|'auto'|number|function\n     */\n\n\n    function getOptionCategoryInterval(model) {\n      var interval = model.get('interval');\n      return interval == null ? 'auto' : interval;\n    }\n    /**\n     * Set `categoryInterval` as 0 implicitly indicates that\n     * show all labels regardless of overlap.\n     * @param {Object} axis axisModel.axis\n     */\n\n    function shouldShowAllLabels(axis) {\n      return axis.type === 'category' && getOptionCategoryInterval(axis.getLabelModel()) === 0;\n    }\n    function getDataDimensionsOnAxis(data, axisDim) {\n      // Remove duplicated dat dimensions caused by `getStackedDimension`.\n      var dataDimMap = {}; // Currently `mapDimensionsAll` will contain stack result dimension ('__\\0ecstackresult').\n      // PENDING: is it reasonable? Do we need to remove the original dim from \"coord dim\" since\n      // there has been stacked result dim?\n\n      each(data.mapDimensionsAll(axisDim), function (dataDim) {\n        // For example, the extent of the original dimension\n        // is [0.1, 0.5], the extent of the `stackResultDimension`\n        // is [7, 9], the final extent should NOT include [0.1, 0.5],\n        // because there is no graphic corresponding to [0.1, 0.5].\n        // See the case in `test/area-stack.html` `main1`, where area line\n        // stack needs `yAxis` not start from 0.\n        dataDimMap[getStackedDimension(data, dataDim)] = true;\n      });\n      return keys(dataDimMap);\n    }\n    function unionAxisExtentFromData(dataExtent, data, axisDim) {\n      if (data) {\n        each(getDataDimensionsOnAxis(data, axisDim), function (dim) {\n          var seriesExtent = data.getApproximateExtent(dim);\n          seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]);\n          seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]);\n        });\n      }\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    var AxisModelCommonMixin =\n    /** @class */\n    function () {\n      function AxisModelCommonMixin() {}\n\n      AxisModelCommonMixin.prototype.getNeedCrossZero = function () {\n        var option = this.option;\n        return !option.scale;\n      };\n      /**\n       * Should be implemented by each axis model if necessary.\n       * @return coordinate system model\n       */\n\n\n      AxisModelCommonMixin.prototype.getCoordSysModel = function () {\n        return;\n      };\n\n      return AxisModelCommonMixin;\n    }();\n\n    /**\n     * Create a multi dimension List structure from seriesModel.\n     */\n\n    function createList(seriesModel) {\n      return createSeriesData(null, seriesModel);\n    } // export function createGraph(seriesModel) {\n    var dataStack$1 = {\n      isDimensionStacked: isDimensionStacked,\n      enableDataStack: enableDataStack,\n      getStackedDimension: getStackedDimension\n    };\n    /**\n     * Create scale\n     * @param {Array.<number>} dataExtent\n     * @param {Object|module:echarts/Model} option If `optoin.type`\n     *        is secified, it can only be `'value'` currently.\n     */\n\n    function createScale(dataExtent, option) {\n      var axisModel = option;\n\n      if (!(option instanceof Model)) {\n        axisModel = new Model(option); // FIXME\n        // Currently AxisModelCommonMixin has nothing to do with the\n        // the requirements of `axisHelper.createScaleByModel`. For\n        // example the methods `getCategories` and `getOrdinalMeta`\n        // are required for `'category'` axis, and ecModel is required\n        // for `'time'` axis. But occasionally echarts-gl happened\n        // to only use `'value'` axis.\n        // zrUtil.mixin(axisModel, AxisModelCommonMixin);\n      }\n\n      var scale = createScaleByModel(axisModel);\n      scale.setExtent(dataExtent[0], dataExtent[1]);\n      niceScaleExtent(scale, axisModel);\n      return scale;\n    }\n    /**\n     * Mixin common methods to axis model,\n     *\n     * Include methods\n     * `getFormattedLabels() => Array.<string>`\n     * `getCategories() => Array.<string>`\n     * `getMin(origin: boolean) => number`\n     * `getMax(origin: boolean) => number`\n     * `getNeedCrossZero() => boolean`\n     */\n\n    function mixinAxisModelCommonMethods(Model) {\n      mixin(Model, AxisModelCommonMixin);\n    }\n    function createTextStyle$1(textStyleModel, opts) {\n      opts = opts || {};\n      return createTextStyle(textStyleModel, null, null, opts.state !== 'normal');\n    }\n\n    var helper = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        createList: createList,\n        getLayoutRect: getLayoutRect,\n        dataStack: dataStack$1,\n        createScale: createScale,\n        mixinAxisModelCommonMethods: mixinAxisModelCommonMethods,\n        getECData: getECData,\n        createTextStyle: createTextStyle$1,\n        createDimensions: createDimensions,\n        createSymbol: createSymbol,\n        enableHoverEmphasis: enableHoverEmphasis\n    });\n\n    var EPSILON$4 = 1e-8;\n    function isAroundEqual$1(a, b) {\n        return Math.abs(a - b) < EPSILON$4;\n    }\n    function contain$2(points, x, y) {\n        var w = 0;\n        var p = points[0];\n        if (!p) {\n            return false;\n        }\n        for (var i = 1; i < points.length; i++) {\n            var p2 = points[i];\n            w += windingLine(p[0], p[1], p2[0], p2[1], x, y);\n            p = p2;\n        }\n        var p0 = points[0];\n        if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) {\n            w += windingLine(p[0], p[1], p0[0], p0[1], x, y);\n        }\n        return w !== 0;\n    }\n\n    var TMP_TRANSFORM = [];\n\n    function transformPoints(points, transform) {\n      for (var p = 0; p < points.length; p++) {\n        applyTransform(points[p], points[p], transform);\n      }\n    }\n\n    function updateBBoxFromPoints(points, min$1, max$1, projection) {\n      for (var i = 0; i < points.length; i++) {\n        var p = points[i];\n\n        if (projection) {\n          // projection may return null point.\n          p = projection.project(p);\n        }\n\n        if (p && isFinite(p[0]) && isFinite(p[1])) {\n          min(min$1, min$1, p);\n          max(max$1, max$1, p);\n        }\n      }\n    }\n\n    function centroid(points) {\n      var signedArea = 0;\n      var cx = 0;\n      var cy = 0;\n      var len = points.length;\n      var x0 = points[len - 1][0];\n      var y0 = points[len - 1][1]; // Polygon should been closed.\n\n      for (var i = 0; i < len; i++) {\n        var x1 = points[i][0];\n        var y1 = points[i][1];\n        var a = x0 * y1 - x1 * y0;\n        signedArea += a;\n        cx += (x0 + x1) * a;\n        cy += (y0 + y1) * a;\n        x0 = x1;\n        y0 = y1;\n      }\n\n      return signedArea ? [cx / signedArea / 3, cy / signedArea / 3, signedArea] : [points[0][0] || 0, points[0][1] || 0];\n    }\n\n    var Region =\n    /** @class */\n    function () {\n      function Region(name) {\n        this.name = name;\n      }\n\n      Region.prototype.setCenter = function (center) {\n        this._center = center;\n      };\n      /**\n       * Get center point in data unit. That is,\n       * for GeoJSONRegion, the unit is lat/lng,\n       * for GeoSVGRegion, the unit is SVG local coord.\n       */\n\n\n      Region.prototype.getCenter = function () {\n        var center = this._center;\n\n        if (!center) {\n          // In most cases there are no need to calculate this center.\n          // So calculate only when called.\n          center = this._center = this.calcCenter();\n        }\n\n        return center;\n      };\n\n      return Region;\n    }();\n\n    var GeoJSONPolygonGeometry =\n    /** @class */\n    function () {\n      function GeoJSONPolygonGeometry(exterior, interiors) {\n        this.type = 'polygon';\n        this.exterior = exterior;\n        this.interiors = interiors;\n      }\n\n      return GeoJSONPolygonGeometry;\n    }();\n\n    var GeoJSONLineStringGeometry =\n    /** @class */\n    function () {\n      function GeoJSONLineStringGeometry(points) {\n        this.type = 'linestring';\n        this.points = points;\n      }\n\n      return GeoJSONLineStringGeometry;\n    }();\n\n    var GeoJSONRegion =\n    /** @class */\n    function (_super) {\n      __extends(GeoJSONRegion, _super);\n\n      function GeoJSONRegion(name, geometries, cp) {\n        var _this = _super.call(this, name) || this;\n\n        _this.type = 'geoJSON';\n        _this.geometries = geometries;\n        _this._center = cp && [cp[0], cp[1]];\n        return _this;\n      }\n\n      GeoJSONRegion.prototype.calcCenter = function () {\n        var geometries = this.geometries;\n        var largestGeo;\n        var largestGeoSize = 0;\n\n        for (var i = 0; i < geometries.length; i++) {\n          var geo = geometries[i];\n          var exterior = geo.exterior; // Simple trick to use points count instead of polygon area as region size.\n          // Ignore linestring\n\n          var size = exterior && exterior.length;\n\n          if (size > largestGeoSize) {\n            largestGeo = geo;\n            largestGeoSize = size;\n          }\n        }\n\n        if (largestGeo) {\n          return centroid(largestGeo.exterior);\n        } // from bounding rect by default.\n\n\n        var rect = this.getBoundingRect();\n        return [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      };\n\n      GeoJSONRegion.prototype.getBoundingRect = function (projection) {\n        var rect = this._rect; // Always recalculate if using projection.\n\n        if (rect && !projection) {\n          return rect;\n        }\n\n        var min = [Infinity, Infinity];\n        var max = [-Infinity, -Infinity];\n        var geometries = this.geometries;\n        each(geometries, function (geo) {\n          if (geo.type === 'polygon') {\n            // Doesn't consider hole\n            updateBBoxFromPoints(geo.exterior, min, max, projection);\n          } else {\n            each(geo.points, function (points) {\n              updateBBoxFromPoints(points, min, max, projection);\n            });\n          }\n        }); // Normalie invalid bounding.\n\n        if (!(isFinite(min[0]) && isFinite(min[1]) && isFinite(max[0]) && isFinite(max[1]))) {\n          min[0] = min[1] = max[0] = max[1] = 0;\n        }\n\n        rect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n\n        if (!projection) {\n          this._rect = rect;\n        }\n\n        return rect;\n      };\n\n      GeoJSONRegion.prototype.contain = function (coord) {\n        var rect = this.getBoundingRect();\n        var geometries = this.geometries;\n\n        if (!rect.contain(coord[0], coord[1])) {\n          return false;\n        }\n\n        loopGeo: for (var i = 0, len = geometries.length; i < len; i++) {\n          var geo = geometries[i]; // Only support polygon.\n\n          if (geo.type !== 'polygon') {\n            continue;\n          }\n\n          var exterior = geo.exterior;\n          var interiors = geo.interiors;\n\n          if (contain$2(exterior, coord[0], coord[1])) {\n            // Not in the region if point is in the hole.\n            for (var k = 0; k < (interiors ? interiors.length : 0); k++) {\n              if (contain$2(interiors[k], coord[0], coord[1])) {\n                continue loopGeo;\n              }\n            }\n\n            return true;\n          }\n        }\n\n        return false;\n      };\n      /**\n       * Transform the raw coords to target bounding.\n       * @param x\n       * @param y\n       * @param width\n       * @param height\n       */\n\n\n      GeoJSONRegion.prototype.transformTo = function (x, y, width, height) {\n        var rect = this.getBoundingRect();\n        var aspect = rect.width / rect.height;\n\n        if (!width) {\n          width = aspect * height;\n        } else if (!height) {\n          height = width / aspect;\n        }\n\n        var target = new BoundingRect(x, y, width, height);\n        var transform = rect.calculateTransform(target);\n        var geometries = this.geometries;\n\n        for (var i = 0; i < geometries.length; i++) {\n          var geo = geometries[i];\n\n          if (geo.type === 'polygon') {\n            transformPoints(geo.exterior, transform);\n            each(geo.interiors, function (interior) {\n              transformPoints(interior, transform);\n            });\n          } else {\n            each(geo.points, function (points) {\n              transformPoints(points, transform);\n            });\n          }\n        }\n\n        rect = this._rect;\n        rect.copy(target); // Update center\n\n        this._center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      };\n\n      GeoJSONRegion.prototype.cloneShallow = function (name) {\n        name == null && (name = this.name);\n        var newRegion = new GeoJSONRegion(name, this.geometries, this._center);\n        newRegion._rect = this._rect;\n        newRegion.transformTo = null; // Simply avoid to be called.\n\n        return newRegion;\n      };\n\n      return GeoJSONRegion;\n    }(Region);\n\n    var GeoSVGRegion =\n    /** @class */\n    function (_super) {\n      __extends(GeoSVGRegion, _super);\n\n      function GeoSVGRegion(name, elOnlyForCalculate) {\n        var _this = _super.call(this, name) || this;\n\n        _this.type = 'geoSVG';\n        _this._elOnlyForCalculate = elOnlyForCalculate;\n        return _this;\n      }\n\n      GeoSVGRegion.prototype.calcCenter = function () {\n        var el = this._elOnlyForCalculate;\n        var rect = el.getBoundingRect();\n        var center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n        var mat = identity(TMP_TRANSFORM);\n        var target = el;\n\n        while (target && !target.isGeoSVGGraphicRoot) {\n          mul$1(mat, target.getLocalTransform(), mat);\n          target = target.parent;\n        }\n\n        invert(mat, mat);\n        applyTransform(center, center, mat);\n        return center;\n      };\n\n      return GeoSVGRegion;\n    }(Region);\n\n    function decode(json) {\n      if (!json.UTF8Encoding) {\n        return json;\n      }\n\n      var jsonCompressed = json;\n      var encodeScale = jsonCompressed.UTF8Scale;\n\n      if (encodeScale == null) {\n        encodeScale = 1024;\n      }\n\n      var features = jsonCompressed.features;\n      each(features, function (feature) {\n        var geometry = feature.geometry;\n        var encodeOffsets = geometry.encodeOffsets;\n        var coordinates = geometry.coordinates; // Geometry may be appeded manually in the script after json loaded.\n        // In this case this geometry is usually not encoded.\n\n        if (!encodeOffsets) {\n          return;\n        }\n\n        switch (geometry.type) {\n          case 'LineString':\n            geometry.coordinates = decodeRing(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'Polygon':\n            decodeRings(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'MultiLineString':\n            decodeRings(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'MultiPolygon':\n            each(coordinates, function (rings, idx) {\n              return decodeRings(rings, encodeOffsets[idx], encodeScale);\n            });\n        }\n      }); // Has been decoded\n\n      jsonCompressed.UTF8Encoding = false;\n      return jsonCompressed;\n    }\n\n    function decodeRings(rings, encodeOffsets, encodeScale) {\n      for (var c = 0; c < rings.length; c++) {\n        rings[c] = decodeRing(rings[c], encodeOffsets[c], encodeScale);\n      }\n    }\n\n    function decodeRing(coordinate, encodeOffsets, encodeScale) {\n      var result = [];\n      var prevX = encodeOffsets[0];\n      var prevY = encodeOffsets[1];\n\n      for (var i = 0; i < coordinate.length; i += 2) {\n        var x = coordinate.charCodeAt(i) - 64;\n        var y = coordinate.charCodeAt(i + 1) - 64; // ZigZag decoding\n\n        x = x >> 1 ^ -(x & 1);\n        y = y >> 1 ^ -(y & 1); // Delta deocding\n\n        x += prevX;\n        y += prevY;\n        prevX = x;\n        prevY = y; // Dequantize\n\n        result.push([x / encodeScale, y / encodeScale]);\n      }\n\n      return result;\n    }\n\n    function parseGeoJSON(geoJson, nameProperty) {\n      geoJson = decode(geoJson);\n      return map(filter(geoJson.features, function (featureObj) {\n        // Output of mapshaper may have geometry null\n        return featureObj.geometry && featureObj.properties && featureObj.geometry.coordinates.length > 0;\n      }), function (featureObj) {\n        var properties = featureObj.properties;\n        var geo = featureObj.geometry;\n        var geometries = [];\n\n        switch (geo.type) {\n          case 'Polygon':\n            var coordinates = geo.coordinates; // According to the GeoJSON specification.\n            // First must be exterior, and the rest are all interior(holes).\n\n            geometries.push(new GeoJSONPolygonGeometry(coordinates[0], coordinates.slice(1)));\n            break;\n\n          case 'MultiPolygon':\n            each(geo.coordinates, function (item) {\n              if (item[0]) {\n                geometries.push(new GeoJSONPolygonGeometry(item[0], item.slice(1)));\n              }\n            });\n            break;\n\n          case 'LineString':\n            geometries.push(new GeoJSONLineStringGeometry([geo.coordinates]));\n            break;\n\n          case 'MultiLineString':\n            geometries.push(new GeoJSONLineStringGeometry(geo.coordinates));\n        }\n\n        var region = new GeoJSONRegion(properties[nameProperty || 'name'], geometries, properties.cp);\n        region.properties = properties;\n        return region;\n      });\n    }\n\n    var number = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        linearMap: linearMap,\n        round: round,\n        asc: asc,\n        getPrecision: getPrecision,\n        getPrecisionSafe: getPrecisionSafe,\n        getPixelPrecision: getPixelPrecision,\n        getPercentWithPrecision: getPercentWithPrecision,\n        MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,\n        remRadian: remRadian,\n        isRadianAroundZero: isRadianAroundZero,\n        parseDate: parseDate,\n        quantity: quantity,\n        quantityExponent: quantityExponent,\n        nice: nice,\n        quantile: quantile,\n        reformIntervals: reformIntervals,\n        isNumeric: isNumeric,\n        numericToNumber: numericToNumber\n    });\n\n    var time = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        parse: parseDate,\n        format: format\n    });\n\n    var graphic$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        extendShape: extendShape,\n        extendPath: extendPath,\n        makePath: makePath,\n        makeImage: makeImage,\n        mergePath: mergePath$1,\n        resizePath: resizePath,\n        createIcon: createIcon,\n        updateProps: updateProps,\n        initProps: initProps,\n        getTransform: getTransform,\n        clipPointsByRect: clipPointsByRect,\n        clipRectByRect: clipRectByRect,\n        registerShape: registerShape,\n        getShapeClass: getShapeClass,\n        Group: Group,\n        Image: ZRImage,\n        Text: ZRText,\n        Circle: Circle,\n        Ellipse: Ellipse,\n        Sector: Sector,\n        Ring: Ring,\n        Polygon: Polygon,\n        Polyline: Polyline,\n        Rect: Rect,\n        Line: Line,\n        BezierCurve: BezierCurve,\n        Arc: Arc,\n        IncrementalDisplayable: IncrementalDisplayable,\n        CompoundPath: CompoundPath,\n        LinearGradient: LinearGradient,\n        RadialGradient: RadialGradient,\n        BoundingRect: BoundingRect\n    });\n\n    var format$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        addCommas: addCommas,\n        toCamelCase: toCamelCase,\n        normalizeCssArray: normalizeCssArray$1,\n        encodeHTML: encodeHTML,\n        formatTpl: formatTpl,\n        getTooltipMarker: getTooltipMarker,\n        formatTime: formatTime,\n        capitalFirst: capitalFirst,\n        truncateText: truncateText,\n        getTextRect: getTextRect\n    });\n\n    var util$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        map: map,\n        each: each,\n        indexOf: indexOf,\n        inherits: inherits,\n        reduce: reduce,\n        filter: filter,\n        bind: bind,\n        curry: curry,\n        isArray: isArray,\n        isString: isString,\n        isObject: isObject,\n        isFunction: isFunction,\n        extend: extend,\n        defaults: defaults,\n        clone: clone,\n        merge: merge\n    });\n\n    var inner$5 = makeInner();\n    function createAxisLabels(axis) {\n      // Only ordinal scale support tick interval\n      return axis.type === 'category' ? makeCategoryLabels(axis) : makeRealNumberLabels(axis);\n    }\n    /**\n     * @param {module:echats/coord/Axis} axis\n     * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea.\n     * @return {Object} {\n     *     ticks: Array.<number>\n     *     tickCategoryInterval: number\n     * }\n     */\n\n    function createAxisTicks(axis, tickModel) {\n      // Only ordinal scale support tick interval\n      return axis.type === 'category' ? makeCategoryTicks(axis, tickModel) : {\n        ticks: map(axis.scale.getTicks(), function (tick) {\n          return tick.value;\n        })\n      };\n    }\n\n    function makeCategoryLabels(axis) {\n      var labelModel = axis.getLabelModel();\n      var result = makeCategoryLabelsActually(axis, labelModel);\n      return !labelModel.get('show') || axis.scale.isBlank() ? {\n        labels: [],\n        labelCategoryInterval: result.labelCategoryInterval\n      } : result;\n    }\n\n    function makeCategoryLabelsActually(axis, labelModel) {\n      var labelsCache = getListCache(axis, 'labels');\n      var optionLabelInterval = getOptionCategoryInterval(labelModel);\n      var result = listCacheGet(labelsCache, optionLabelInterval);\n\n      if (result) {\n        return result;\n      }\n\n      var labels;\n      var numericLabelInterval;\n\n      if (isFunction(optionLabelInterval)) {\n        labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval);\n      } else {\n        numericLabelInterval = optionLabelInterval === 'auto' ? makeAutoCategoryInterval(axis) : optionLabelInterval;\n        labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval);\n      } // Cache to avoid calling interval function repeatedly.\n\n\n      return listCacheSet(labelsCache, optionLabelInterval, {\n        labels: labels,\n        labelCategoryInterval: numericLabelInterval\n      });\n    }\n\n    function makeCategoryTicks(axis, tickModel) {\n      var ticksCache = getListCache(axis, 'ticks');\n      var optionTickInterval = getOptionCategoryInterval(tickModel);\n      var result = listCacheGet(ticksCache, optionTickInterval);\n\n      if (result) {\n        return result;\n      }\n\n      var ticks;\n      var tickCategoryInterval; // Optimize for the case that large category data and no label displayed,\n      // we should not return all ticks.\n\n      if (!tickModel.get('show') || axis.scale.isBlank()) {\n        ticks = [];\n      }\n\n      if (isFunction(optionTickInterval)) {\n        ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true);\n      } // Always use label interval by default despite label show. Consider this\n      // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows\n      // labels. `splitLine` and `axisTick` should be consistent in this case.\n      else if (optionTickInterval === 'auto') {\n          var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel());\n          tickCategoryInterval = labelsResult.labelCategoryInterval;\n          ticks = map(labelsResult.labels, function (labelItem) {\n            return labelItem.tickValue;\n          });\n        } else {\n          tickCategoryInterval = optionTickInterval;\n          ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true);\n        } // Cache to avoid calling interval function repeatedly.\n\n\n      return listCacheSet(ticksCache, optionTickInterval, {\n        ticks: ticks,\n        tickCategoryInterval: tickCategoryInterval\n      });\n    }\n\n    function makeRealNumberLabels(axis) {\n      var ticks = axis.scale.getTicks();\n      var labelFormatter = makeLabelFormatter(axis);\n      return {\n        labels: map(ticks, function (tick, idx) {\n          return {\n            level: tick.level,\n            formattedLabel: labelFormatter(tick, idx),\n            rawLabel: axis.scale.getLabel(tick),\n            tickValue: tick.value\n          };\n        })\n      };\n    }\n\n    function getListCache(axis, prop) {\n      // Because key can be a function, and cache size always is small, we use array cache.\n      return inner$5(axis)[prop] || (inner$5(axis)[prop] = []);\n    }\n\n    function listCacheGet(cache, key) {\n      for (var i = 0; i < cache.length; i++) {\n        if (cache[i].key === key) {\n          return cache[i].value;\n        }\n      }\n    }\n\n    function listCacheSet(cache, key, value) {\n      cache.push({\n        key: key,\n        value: value\n      });\n      return value;\n    }\n\n    function makeAutoCategoryInterval(axis) {\n      var result = inner$5(axis).autoInterval;\n      return result != null ? result : inner$5(axis).autoInterval = axis.calculateCategoryInterval();\n    }\n    /**\n     * Calculate interval for category axis ticks and labels.\n     * To get precise result, at least one of `getRotate` and `isHorizontal`\n     * should be implemented in axis.\n     */\n\n\n    function calculateCategoryInterval(axis) {\n      var params = fetchAutoCategoryIntervalCalculationParams(axis);\n      var labelFormatter = makeLabelFormatter(axis);\n      var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI;\n      var ordinalScale = axis.scale;\n      var ordinalExtent = ordinalScale.getExtent(); // Providing this method is for optimization:\n      // avoid generating a long array by `getTicks`\n      // in large category data case.\n\n      var tickCount = ordinalScale.count();\n\n      if (ordinalExtent[1] - ordinalExtent[0] < 1) {\n        return 0;\n      }\n\n      var step = 1; // Simple optimization. Empirical value: tick count should less than 40.\n\n      if (tickCount > 40) {\n        step = Math.max(1, Math.floor(tickCount / 40));\n      }\n\n      var tickValue = ordinalExtent[0];\n      var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);\n      var unitW = Math.abs(unitSpan * Math.cos(rotation));\n      var unitH = Math.abs(unitSpan * Math.sin(rotation));\n      var maxW = 0;\n      var maxH = 0; // Caution: Performance sensitive for large category data.\n      // Consider dataZoom, we should make appropriate step to avoid O(n) loop.\n\n      for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n        var width = 0;\n        var height = 0; // Not precise, do not consider align and vertical align\n        // and each distance from axis line yet.\n\n        var rect = getBoundingRect(labelFormatter({\n          value: tickValue\n        }), params.font, 'center', 'top'); // Magic number\n\n        width = rect.width * 1.3;\n        height = rect.height * 1.3; // Min size, void long loop.\n\n        maxW = Math.max(maxW, width, 7);\n        maxH = Math.max(maxH, height, 7);\n      }\n\n      var dw = maxW / unitW;\n      var dh = maxH / unitH; // 0/0 is NaN, 1/0 is Infinity.\n\n      isNaN(dw) && (dw = Infinity);\n      isNaN(dh) && (dh = Infinity);\n      var interval = Math.max(0, Math.floor(Math.min(dw, dh)));\n      var cache = inner$5(axis.model);\n      var axisExtent = axis.getExtent();\n      var lastAutoInterval = cache.lastAutoInterval;\n      var lastTickCount = cache.lastTickCount; // Use cache to keep interval stable while moving zoom window,\n      // otherwise the calculated interval might jitter when the zoom\n      // window size is close to the interval-changing size.\n      // For example, if all of the axis labels are `a, b, c, d, e, f, g`.\n      // The jitter will cause that sometimes the displayed labels are\n      // `a, d, g` (interval: 2) sometimes `a, c, e`(interval: 1).\n\n      if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 // Always choose the bigger one, otherwise the critical\n      // point is not the same when zooming in or zooming out.\n      && lastAutoInterval > interval // If the axis change is caused by chart resize, the cache should not\n      // be used. Otherwise some hidden labels might not be shown again.\n      && cache.axisExtent0 === axisExtent[0] && cache.axisExtent1 === axisExtent[1]) {\n        interval = lastAutoInterval;\n      } // Only update cache if cache not used, otherwise the\n      // changing of interval is too insensitive.\n      else {\n          cache.lastTickCount = tickCount;\n          cache.lastAutoInterval = interval;\n          cache.axisExtent0 = axisExtent[0];\n          cache.axisExtent1 = axisExtent[1];\n        }\n\n      return interval;\n    }\n\n    function fetchAutoCategoryIntervalCalculationParams(axis) {\n      var labelModel = axis.getLabelModel();\n      return {\n        axisRotate: axis.getRotate ? axis.getRotate() : axis.isHorizontal && !axis.isHorizontal() ? 90 : 0,\n        labelRotate: labelModel.get('rotate') || 0,\n        font: labelModel.getFont()\n      };\n    }\n\n    function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) {\n      var labelFormatter = makeLabelFormatter(axis);\n      var ordinalScale = axis.scale;\n      var ordinalExtent = ordinalScale.getExtent();\n      var labelModel = axis.getLabelModel();\n      var result = []; // TODO: axisType: ordinalTime, pick the tick from each month/day/year/...\n\n      var step = Math.max((categoryInterval || 0) + 1, 1);\n      var startTick = ordinalExtent[0];\n      var tickCount = ordinalScale.count(); // Calculate start tick based on zero if possible to keep label consistent\n      // while zooming and moving while interval > 0. Otherwise the selection\n      // of displayable ticks and symbols probably keep changing.\n      // 3 is empirical value.\n\n      if (startTick !== 0 && step > 1 && tickCount / step > 2) {\n        startTick = Math.round(Math.ceil(startTick / step) * step);\n      } // (1) Only add min max label here but leave overlap checking\n      // to render stage, which also ensure the returned list\n      // suitable for splitLine and splitArea rendering.\n      // (2) Scales except category always contain min max label so\n      // do not need to perform this process.\n\n\n      var showAllLabel = shouldShowAllLabels(axis);\n      var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel;\n      var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel;\n\n      if (includeMinLabel && startTick !== ordinalExtent[0]) {\n        addItem(ordinalExtent[0]);\n      } // Optimize: avoid generating large array by `ordinalScale.getTicks()`.\n\n\n      var tickValue = startTick;\n\n      for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n        addItem(tickValue);\n      }\n\n      if (includeMaxLabel && tickValue - step !== ordinalExtent[1]) {\n        addItem(ordinalExtent[1]);\n      }\n\n      function addItem(tickValue) {\n        var tickObj = {\n          value: tickValue\n        };\n        result.push(onlyTick ? tickValue : {\n          formattedLabel: labelFormatter(tickObj),\n          rawLabel: ordinalScale.getLabel(tickObj),\n          tickValue: tickValue\n        });\n      }\n\n      return result;\n    }\n\n    function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) {\n      var ordinalScale = axis.scale;\n      var labelFormatter = makeLabelFormatter(axis);\n      var result = [];\n      each(ordinalScale.getTicks(), function (tick) {\n        var rawLabel = ordinalScale.getLabel(tick);\n        var tickValue = tick.value;\n\n        if (categoryInterval(tick.value, rawLabel)) {\n          result.push(onlyTick ? tickValue : {\n            formattedLabel: labelFormatter(tick),\n            rawLabel: rawLabel,\n            tickValue: tickValue\n          });\n        }\n      });\n      return result;\n    }\n\n    var NORMALIZED_EXTENT = [0, 1];\n    /**\n     * Base class of Axis.\n     */\n\n    var Axis =\n    /** @class */\n    function () {\n      function Axis(dim, scale, extent) {\n        this.onBand = false;\n        this.inverse = false;\n        this.dim = dim;\n        this.scale = scale;\n        this._extent = extent || [0, 0];\n      }\n      /**\n       * If axis extent contain given coord\n       */\n\n\n      Axis.prototype.contain = function (coord) {\n        var extent = this._extent;\n        var min = Math.min(extent[0], extent[1]);\n        var max = Math.max(extent[0], extent[1]);\n        return coord >= min && coord <= max;\n      };\n      /**\n       * If axis extent contain given data\n       */\n\n\n      Axis.prototype.containData = function (data) {\n        return this.scale.contain(data);\n      };\n      /**\n       * Get coord extent.\n       */\n\n\n      Axis.prototype.getExtent = function () {\n        return this._extent.slice();\n      };\n      /**\n       * Get precision used for formatting\n       */\n\n\n      Axis.prototype.getPixelPrecision = function (dataExtent) {\n        return getPixelPrecision(dataExtent || this.scale.getExtent(), this._extent);\n      };\n      /**\n       * Set coord extent\n       */\n\n\n      Axis.prototype.setExtent = function (start, end) {\n        var extent = this._extent;\n        extent[0] = start;\n        extent[1] = end;\n      };\n      /**\n       * Convert data to coord. Data is the rank if it has an ordinal scale\n       */\n\n\n      Axis.prototype.dataToCoord = function (data, clamp) {\n        var extent = this._extent;\n        var scale = this.scale;\n        data = scale.normalize(data);\n\n        if (this.onBand && scale.type === 'ordinal') {\n          extent = extent.slice();\n          fixExtentWithBands(extent, scale.count());\n        }\n\n        return linearMap(data, NORMALIZED_EXTENT, extent, clamp);\n      };\n      /**\n       * Convert coord to data. Data is the rank if it has an ordinal scale\n       */\n\n\n      Axis.prototype.coordToData = function (coord, clamp) {\n        var extent = this._extent;\n        var scale = this.scale;\n\n        if (this.onBand && scale.type === 'ordinal') {\n          extent = extent.slice();\n          fixExtentWithBands(extent, scale.count());\n        }\n\n        var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp);\n        return this.scale.scale(t);\n      };\n      /**\n       * Convert pixel point to data in axis\n       */\n\n\n      Axis.prototype.pointToData = function (point, clamp) {\n        // Should be implemented in derived class if necessary.\n        return;\n      };\n      /**\n       * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`,\n       * `axis.getTicksCoords` considers `onBand`, which is used by\n       * `boundaryGap:true` of category axis and splitLine and splitArea.\n       * @param opt.tickModel default: axis.model.getModel('axisTick')\n       * @param opt.clamp If `true`, the first and the last\n       *        tick must be at the axis end points. Otherwise, clip ticks\n       *        that outside the axis extent.\n       */\n\n\n      Axis.prototype.getTicksCoords = function (opt) {\n        opt = opt || {};\n        var tickModel = opt.tickModel || this.getTickModel();\n        var result = createAxisTicks(this, tickModel);\n        var ticks = result.ticks;\n        var ticksCoords = map(ticks, function (tickVal) {\n          return {\n            coord: this.dataToCoord(this.scale.type === 'ordinal' ? this.scale.getRawOrdinalNumber(tickVal) : tickVal),\n            tickValue: tickVal\n          };\n        }, this);\n        var alignWithLabel = tickModel.get('alignWithLabel');\n        fixOnBandTicksCoords(this, ticksCoords, alignWithLabel, opt.clamp);\n        return ticksCoords;\n      };\n\n      Axis.prototype.getMinorTicksCoords = function () {\n        if (this.scale.type === 'ordinal') {\n          // Category axis doesn't support minor ticks\n          return [];\n        }\n\n        var minorTickModel = this.model.getModel('minorTick');\n        var splitNumber = minorTickModel.get('splitNumber'); // Protection.\n\n        if (!(splitNumber > 0 && splitNumber < 100)) {\n          splitNumber = 5;\n        }\n\n        var minorTicks = this.scale.getMinorTicks(splitNumber);\n        var minorTicksCoords = map(minorTicks, function (minorTicksGroup) {\n          return map(minorTicksGroup, function (minorTick) {\n            return {\n              coord: this.dataToCoord(minorTick),\n              tickValue: minorTick\n            };\n          }, this);\n        }, this);\n        return minorTicksCoords;\n      };\n\n      Axis.prototype.getViewLabels = function () {\n        return createAxisLabels(this).labels;\n      };\n\n      Axis.prototype.getLabelModel = function () {\n        return this.model.getModel('axisLabel');\n      };\n      /**\n       * Notice here we only get the default tick model. For splitLine\n       * or splitArea, we should pass the splitLineModel or splitAreaModel\n       * manually when calling `getTicksCoords`.\n       * In GL, this method may be overridden to:\n       * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`\n       */\n\n\n      Axis.prototype.getTickModel = function () {\n        return this.model.getModel('axisTick');\n      };\n      /**\n       * Get width of band\n       */\n\n\n      Axis.prototype.getBandWidth = function () {\n        var axisExtent = this._extent;\n        var dataExtent = this.scale.getExtent();\n        var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); // Fix #2728, avoid NaN when only one data.\n\n        len === 0 && (len = 1);\n        var size = Math.abs(axisExtent[1] - axisExtent[0]);\n        return Math.abs(size) / len;\n      };\n      /**\n       * Only be called in category axis.\n       * Can be overridden, consider other axes like in 3D.\n       * @return Auto interval for cateogry axis tick and label\n       */\n\n\n      Axis.prototype.calculateCategoryInterval = function () {\n        return calculateCategoryInterval(this);\n      };\n\n      return Axis;\n    }();\n\n    function fixExtentWithBands(extent, nTick) {\n      var size = extent[1] - extent[0];\n      var len = nTick;\n      var margin = size / len / 2;\n      extent[0] += margin;\n      extent[1] -= margin;\n    } // If axis has labels [1, 2, 3, 4]. Bands on the axis are\n    // |---1---|---2---|---3---|---4---|.\n    // So the displayed ticks and splitLine/splitArea should between\n    // each data item, otherwise cause misleading (e.g., split tow bars\n    // of a single data item when there are two bar series).\n    // Also consider if tickCategoryInterval > 0 and onBand, ticks and\n    // splitLine/spliteArea should layout appropriately corresponding\n    // to displayed labels. (So we should not use `getBandWidth` in this\n    // case).\n\n\n    function fixOnBandTicksCoords(axis, ticksCoords, alignWithLabel, clamp) {\n      var ticksLen = ticksCoords.length;\n\n      if (!axis.onBand || alignWithLabel || !ticksLen) {\n        return;\n      }\n\n      var axisExtent = axis.getExtent();\n      var last;\n      var diffSize;\n\n      if (ticksLen === 1) {\n        ticksCoords[0].coord = axisExtent[0];\n        last = ticksCoords[1] = {\n          coord: axisExtent[0]\n        };\n      } else {\n        var crossLen = ticksCoords[ticksLen - 1].tickValue - ticksCoords[0].tickValue;\n        var shift_1 = (ticksCoords[ticksLen - 1].coord - ticksCoords[0].coord) / crossLen;\n        each(ticksCoords, function (ticksItem) {\n          ticksItem.coord -= shift_1 / 2;\n        });\n        var dataExtent = axis.scale.getExtent();\n        diffSize = 1 + dataExtent[1] - ticksCoords[ticksLen - 1].tickValue;\n        last = {\n          coord: ticksCoords[ticksLen - 1].coord + shift_1 * diffSize\n        };\n        ticksCoords.push(last);\n      }\n\n      var inverse = axisExtent[0] > axisExtent[1]; // Handling clamp.\n\n      if (littleThan(ticksCoords[0].coord, axisExtent[0])) {\n        clamp ? ticksCoords[0].coord = axisExtent[0] : ticksCoords.shift();\n      }\n\n      if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) {\n        ticksCoords.unshift({\n          coord: axisExtent[0]\n        });\n      }\n\n      if (littleThan(axisExtent[1], last.coord)) {\n        clamp ? last.coord = axisExtent[1] : ticksCoords.pop();\n      }\n\n      if (clamp && littleThan(last.coord, axisExtent[1])) {\n        ticksCoords.push({\n          coord: axisExtent[1]\n        });\n      }\n\n      function littleThan(a, b) {\n        // Avoid rounding error cause calculated tick coord different with extent.\n        // It may cause an extra unnecessary tick added.\n        a = round(a);\n        b = round(b);\n        return inverse ? a > b : a < b;\n      }\n    }\n\n    // Should use `ComponentModel.extend` or `class XXXX extend ComponentModel` to create class.\n    // Then use `registerComponentModel` in `install` parameter when `use` this extension. For example:\n    // class Bar3DModel extends ComponentModel {}\n    // export function install(registers) { registers.registerComponentModel(Bar3DModel); }\n    // echarts.use(install);\n\n    function extendComponentModel(proto) {\n      var Model = ComponentModel.extend(proto);\n      ComponentModel.registerClass(Model);\n      return Model;\n    }\n    function extendComponentView(proto) {\n      var View = ComponentView.extend(proto);\n      ComponentView.registerClass(View);\n      return View;\n    }\n    function extendSeriesModel(proto) {\n      var Model = SeriesModel.extend(proto);\n      SeriesModel.registerClass(Model);\n      return Model;\n    }\n    function extendChartView(proto) {\n      var View = ChartView.extend(proto);\n      ChartView.registerClass(View);\n      return View;\n    }\n\n    function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) {\n      var dx = x - x1;\n      var dy = y - y1;\n      var dx1 = x2 - x1;\n      var dy1 = y2 - y1;\n      var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n      dx1 /= lineLen;\n      dy1 /= lineLen; // dot product\n\n      var projectedLen = dx * dx1 + dy * dy1;\n      var t = projectedLen / lineLen;\n\n      if (limitToEnds) {\n        t = Math.min(Math.max(t, 0), 1);\n      }\n\n      t *= lineLen;\n      var ox = out[0] = x1 + t * dx1;\n      var oy = out[1] = y1 + t * dy1;\n      return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));\n    }\n\n\n    var pt0 = new Point();\n    var pt1 = new Point();\n    var pt2 = new Point();\n    var dir = new Point();\n    var dir2 = new Point();\n\n    var tmpArr = [];\n    var tmpProjPoint = new Point();\n    /**\n     * Reduce the line segment attached to the label to limit the turn angle between two segments.\n     * @param linePoints\n     * @param minTurnAngle Radian of minimum turn angle. 0 - 180\n     */\n\n    function limitTurnAngle(linePoints, minTurnAngle) {\n      if (!(minTurnAngle <= 180 && minTurnAngle > 0)) {\n        return;\n      }\n\n      minTurnAngle = minTurnAngle / 180 * Math.PI; // The line points can be\n      //      /pt1----pt2 (label)\n      //     /\n      // pt0/\n\n      pt0.fromArray(linePoints[0]);\n      pt1.fromArray(linePoints[1]);\n      pt2.fromArray(linePoints[2]);\n      Point.sub(dir, pt0, pt1);\n      Point.sub(dir2, pt2, pt1);\n      var len1 = dir.len();\n      var len2 = dir2.len();\n\n      if (len1 < 1e-3 || len2 < 1e-3) {\n        return;\n      }\n\n      dir.scale(1 / len1);\n      dir2.scale(1 / len2);\n      var angleCos = dir.dot(dir2);\n      var minTurnAngleCos = Math.cos(minTurnAngle);\n\n      if (minTurnAngleCos < angleCos) {\n        // Smaller than minTurnAngle\n        // Calculate project point of pt0 on pt1-pt2\n        var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n        tmpProjPoint.fromArray(tmpArr); // Calculate new projected length with limited minTurnAngle and get the new connect point\n\n        tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n        var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n        if (isNaN(t)) {\n          return;\n        }\n\n        if (t < 0) {\n          Point.copy(tmpProjPoint, pt1);\n        } else if (t > 1) {\n          Point.copy(tmpProjPoint, pt2);\n        }\n\n        tmpProjPoint.toArray(linePoints[1]);\n      }\n    }\n    /**\n     * Limit the angle of line and the surface\n     * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite\n     */\n\n    function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) {\n      if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {\n        return;\n      }\n\n      maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI;\n      pt0.fromArray(linePoints[0]);\n      pt1.fromArray(linePoints[1]);\n      pt2.fromArray(linePoints[2]);\n      Point.sub(dir, pt1, pt0);\n      Point.sub(dir2, pt2, pt1);\n      var len1 = dir.len();\n      var len2 = dir2.len();\n\n      if (len1 < 1e-3 || len2 < 1e-3) {\n        return;\n      }\n\n      dir.scale(1 / len1);\n      dir2.scale(1 / len2);\n      var angleCos = dir.dot(surfaceNormal);\n      var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle);\n\n      if (angleCos < maxSurfaceAngleCos) {\n        // Calculate project point of pt0 on pt1-pt2\n        var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n        tmpProjPoint.fromArray(tmpArr);\n        var HALF_PI = Math.PI / 2;\n        var angle2 = Math.acos(dir2.dot(surfaceNormal));\n        var newAngle = HALF_PI + angle2 - maxSurfaceAngle;\n\n        if (newAngle >= HALF_PI) {\n          // parallel\n          Point.copy(tmpProjPoint, pt2);\n        } else {\n          // Calculate new projected length with limited minTurnAngle and get the new connect point\n          tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n          var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n          if (isNaN(t)) {\n            return;\n          }\n\n          if (t < 0) {\n            Point.copy(tmpProjPoint, pt1);\n          } else if (t > 1) {\n            Point.copy(tmpProjPoint, pt2);\n          }\n        }\n\n        tmpProjPoint.toArray(linePoints[1]);\n      }\n    }\n\n    function setLabelLineState(labelLine, ignore, stateName, stateModel) {\n      var isNormal = stateName === 'normal';\n      var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); // Make sure display.\n\n      stateObj.ignore = ignore; // Set smooth\n\n      var smooth = stateModel.get('smooth');\n\n      if (smooth && smooth === true) {\n        smooth = 0.3;\n      }\n\n      stateObj.shape = stateObj.shape || {};\n\n      if (smooth > 0) {\n        stateObj.shape.smooth = smooth;\n      }\n\n      var styleObj = stateModel.getModel('lineStyle').getLineStyle();\n      isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj;\n    }\n\n    function buildLabelLinePath(path, shape) {\n      var smooth = shape.smooth;\n      var points = shape.points;\n\n      if (!points) {\n        return;\n      }\n\n      path.moveTo(points[0][0], points[0][1]);\n\n      if (smooth > 0 && points.length >= 3) {\n        var len1 = dist(points[0], points[1]);\n        var len2 = dist(points[1], points[2]);\n\n        if (!len1 || !len2) {\n          path.lineTo(points[1][0], points[1][1]);\n          path.lineTo(points[2][0], points[2][1]);\n          return;\n        }\n\n        var moveLen = Math.min(len1, len2) * smooth;\n        var midPoint0 = lerp([], points[1], points[0], moveLen / len1);\n        var midPoint2 = lerp([], points[1], points[2], moveLen / len2);\n        var midPoint1 = lerp([], midPoint0, midPoint2, 0.5);\n        path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]);\n        path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]);\n      } else {\n        for (var i = 1; i < points.length; i++) {\n          path.lineTo(points[i][0], points[i][1]);\n        }\n      }\n    }\n    /**\n     * Create a label line if necessary and set it's style.\n     */\n\n\n    function setLabelLineStyle(targetEl, statesModels, defaultStyle) {\n      var labelLine = targetEl.getTextGuideLine();\n      var label = targetEl.getTextContent();\n\n      if (!label) {\n        // Not show label line if there is no label.\n        if (labelLine) {\n          targetEl.removeTextGuideLine();\n        }\n\n        return;\n      }\n\n      var normalModel = statesModels.normal;\n      var showNormal = normalModel.get('show');\n      var labelIgnoreNormal = label.ignore;\n\n      for (var i = 0; i < DISPLAY_STATES.length; i++) {\n        var stateName = DISPLAY_STATES[i];\n        var stateModel = statesModels[stateName];\n        var isNormal = stateName === 'normal';\n\n        if (stateModel) {\n          var stateShow = stateModel.get('show');\n          var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal);\n\n          if (isLabelIgnored // Not show when label is not shown in this state.\n          || !retrieve2(stateShow, showNormal) // Use normal state by default if not set.\n          ) {\n              var stateObj = isNormal ? labelLine : labelLine && labelLine.states[stateName];\n\n              if (stateObj) {\n                stateObj.ignore = true;\n              }\n\n              continue;\n            } // Create labelLine if not exists\n\n\n          if (!labelLine) {\n            labelLine = new Polyline();\n            targetEl.setTextGuideLine(labelLine); // Reset state of normal because it's new created.\n            // NOTE: NORMAL should always been the first!\n\n            if (!isNormal && (labelIgnoreNormal || !showNormal)) {\n              setLabelLineState(labelLine, true, 'normal', statesModels.normal);\n            } // Use same state proxy.\n\n\n            if (targetEl.stateProxy) {\n              labelLine.stateProxy = targetEl.stateProxy;\n            }\n          }\n\n          setLabelLineState(labelLine, false, stateName, stateModel);\n        }\n      }\n\n      if (labelLine) {\n        defaults(labelLine.style, defaultStyle); // Not fill.\n\n        labelLine.style.fill = null;\n        var showAbove = normalModel.get('showAbove');\n        var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {};\n        labelLineConfig.showAbove = showAbove || false; // Custom the buildPath.\n\n        labelLine.buildPath = buildLabelLinePath;\n      }\n    }\n    function getLabelLineStatesModels(itemModel, labelLineName) {\n      labelLineName = labelLineName || 'labelLine';\n      var statesModels = {\n        normal: itemModel.getModel(labelLineName)\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        statesModels[stateName] = itemModel.getModel([stateName, labelLineName]);\n      }\n\n      return statesModels;\n    }\n\n    function prepareLayoutList(input) {\n      var list = [];\n\n      for (var i = 0; i < input.length; i++) {\n        var rawItem = input[i];\n\n        if (rawItem.defaultAttr.ignore) {\n          continue;\n        }\n\n        var label = rawItem.label;\n        var transform = label.getComputedTransform(); // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el.\n\n        var localRect = label.getBoundingRect();\n        var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5;\n        var minMargin = label.style.margin || 0;\n        var globalRect = localRect.clone();\n        globalRect.applyTransform(transform);\n        globalRect.x -= minMargin / 2;\n        globalRect.y -= minMargin / 2;\n        globalRect.width += minMargin;\n        globalRect.height += minMargin;\n        var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null;\n        list.push({\n          label: label,\n          labelLine: rawItem.labelLine,\n          rect: globalRect,\n          localRect: localRect,\n          obb: obb,\n          priority: rawItem.priority,\n          defaultAttr: rawItem.defaultAttr,\n          layoutOption: rawItem.computedLayoutOption,\n          axisAligned: isAxisAligned,\n          transform: transform\n        });\n      }\n\n      return list;\n    }\n\n    function shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) {\n      var len = list.length;\n\n      if (len < 2) {\n        return;\n      }\n\n      list.sort(function (a, b) {\n        return a.rect[xyDim] - b.rect[xyDim];\n      });\n      var lastPos = 0;\n      var delta;\n      var adjusted = false;\n      var totalShifts = 0;\n\n      for (var i = 0; i < len; i++) {\n        var item = list[i];\n        var rect = item.rect;\n        delta = rect[xyDim] - lastPos;\n\n        if (delta < 0) {\n          // shiftForward(i, len, -delta);\n          rect[xyDim] -= delta;\n          item.label[xyDim] -= delta;\n          adjusted = true;\n        }\n\n        var shift = Math.max(-delta, 0);\n        totalShifts += shift;\n        lastPos = rect[xyDim] + rect[sizeDim];\n      }\n\n      if (totalShifts > 0 && balanceShift) {\n        // Shift back to make the distribution more equally.\n        shiftList(-totalShifts / len, 0, len);\n      } // TODO bleedMargin?\n\n\n      var first = list[0];\n      var last = list[len - 1];\n      var minGap;\n      var maxGap;\n      updateMinMaxGap(); // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds.\n\n      minGap < 0 && squeezeGaps(-minGap, 0.8);\n      maxGap < 0 && squeezeGaps(maxGap, 0.8);\n      updateMinMaxGap();\n      takeBoundsGap(minGap, maxGap, 1);\n      takeBoundsGap(maxGap, minGap, -1); // Handle bailout when there is not enough space.\n\n      updateMinMaxGap();\n\n      if (minGap < 0) {\n        squeezeWhenBailout(-minGap);\n      }\n\n      if (maxGap < 0) {\n        squeezeWhenBailout(maxGap);\n      }\n\n      function updateMinMaxGap() {\n        minGap = first.rect[xyDim] - minBound;\n        maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim];\n      }\n\n      function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) {\n        if (gapThisBound < 0) {\n          // Move from other gap if can.\n          var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound);\n\n          if (moveFromMaxGap > 0) {\n            shiftList(moveFromMaxGap * moveDir, 0, len);\n            var remained = moveFromMaxGap + gapThisBound;\n\n            if (remained < 0) {\n              squeezeGaps(-remained * moveDir, 1);\n            }\n          } else {\n            squeezeGaps(-gapThisBound * moveDir, 1);\n          }\n        }\n      }\n\n      function shiftList(delta, start, end) {\n        if (delta !== 0) {\n          adjusted = true;\n        }\n\n        for (var i = start; i < end; i++) {\n          var item = list[i];\n          var rect = item.rect;\n          rect[xyDim] += delta;\n          item.label[xyDim] += delta;\n        }\n      } // Squeeze gaps if the labels exceed margin.\n\n\n      function squeezeGaps(delta, maxSqeezePercent) {\n        var gaps = [];\n        var totalGaps = 0;\n\n        for (var i = 1; i < len; i++) {\n          var prevItemRect = list[i - 1].rect;\n          var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0);\n          gaps.push(gap);\n          totalGaps += gap;\n        }\n\n        if (!totalGaps) {\n          return;\n        }\n\n        var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent);\n\n        if (delta > 0) {\n          for (var i = 0; i < len - 1; i++) {\n            // Distribute the shift delta to all gaps.\n            var movement = gaps[i] * squeezePercent; // Forward\n\n            shiftList(movement, 0, i + 1);\n          }\n        } else {\n          // Backward\n          for (var i = len - 1; i > 0; i--) {\n            // Distribute the shift delta to all gaps.\n            var movement = gaps[i - 1] * squeezePercent;\n            shiftList(-movement, i, len);\n          }\n        }\n      }\n      /**\n       * Squeeze to allow overlap if there is no more space available.\n       * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds.\n       */\n\n\n      function squeezeWhenBailout(delta) {\n        var dir = delta < 0 ? -1 : 1;\n        delta = Math.abs(delta);\n        var moveForEachLabel = Math.ceil(delta / (len - 1));\n\n        for (var i = 0; i < len - 1; i++) {\n          if (dir > 0) {\n            // Forward\n            shiftList(moveForEachLabel, 0, i + 1);\n          } else {\n            // Backward\n            shiftList(-moveForEachLabel, len - i - 1, len);\n          }\n\n          delta -= moveForEachLabel;\n\n          if (delta <= 0) {\n            return;\n          }\n        }\n      }\n\n      return adjusted;\n    }\n    /**\n     * Adjust labels on y direction to avoid overlap.\n     */\n\n    function shiftLayoutOnY(list, topBound, bottomBound, // If average the shifts on all labels and add them to 0\n    balanceShift) {\n      return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift);\n    }\n    function hideOverlap(labelList) {\n      var displayedLabels = []; // TODO, render overflow visible first, put in the displayedLabels.\n\n      labelList.sort(function (a, b) {\n        return b.priority - a.priority;\n      });\n      var globalRect = new BoundingRect(0, 0, 0, 0);\n\n      function hideEl(el) {\n        if (!el.ignore) {\n          // Show on emphasis.\n          var emphasisState = el.ensureState('emphasis');\n\n          if (emphasisState.ignore == null) {\n            emphasisState.ignore = false;\n          }\n        }\n\n        el.ignore = true;\n      }\n\n      for (var i = 0; i < labelList.length; i++) {\n        var labelItem = labelList[i];\n        var isAxisAligned = labelItem.axisAligned;\n        var localRect = labelItem.localRect;\n        var transform = labelItem.transform;\n        var label = labelItem.label;\n        var labelLine = labelItem.labelLine;\n        globalRect.copy(labelItem.rect); // Add a threshold because layout may be aligned precisely.\n\n        globalRect.width -= 0.1;\n        globalRect.height -= 0.1;\n        globalRect.x += 0.05;\n        globalRect.y += 0.05;\n        var obb = labelItem.obb;\n        var overlapped = false;\n\n        for (var j = 0; j < displayedLabels.length; j++) {\n          var existsTextCfg = displayedLabels[j]; // Fast rejection.\n\n          if (!globalRect.intersect(existsTextCfg.rect)) {\n            continue;\n          }\n\n          if (isAxisAligned && existsTextCfg.axisAligned) {\n            // Is overlapped\n            overlapped = true;\n            break;\n          }\n\n          if (!existsTextCfg.obb) {\n            // If self is not axis aligned. But other is.\n            existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform);\n          }\n\n          if (!obb) {\n            // If self is axis aligned. But other is not.\n            obb = new OrientedBoundingRect(localRect, transform);\n          }\n\n          if (obb.intersect(existsTextCfg.obb)) {\n            overlapped = true;\n            break;\n          }\n        } // TODO Callback to determine if this overlap should be handled?\n\n\n        if (overlapped) {\n          hideEl(label);\n          labelLine && hideEl(labelLine);\n        } else {\n          label.attr('ignore', labelItem.defaultAttr.ignore);\n          labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore);\n          displayedLabels.push(labelItem);\n        }\n      }\n    }\n\n    var mathSin$4 = Math.sin;\n    var mathCos$4 = Math.cos;\n    var PI$4 = Math.PI;\n    var PI2$6 = Math.PI * 2;\n    var degree = 180 / PI$4;\n    var SVGPathRebuilder = (function () {\n        function SVGPathRebuilder() {\n        }\n        SVGPathRebuilder.prototype.reset = function (precision) {\n            this._start = true;\n            this._d = [];\n            this._str = '';\n            this._p = Math.pow(10, precision || 4);\n        };\n        SVGPathRebuilder.prototype.moveTo = function (x, y) {\n            this._add('M', x, y);\n        };\n        SVGPathRebuilder.prototype.lineTo = function (x, y) {\n            this._add('L', x, y);\n        };\n        SVGPathRebuilder.prototype.bezierCurveTo = function (x, y, x2, y2, x3, y3) {\n            this._add('C', x, y, x2, y2, x3, y3);\n        };\n        SVGPathRebuilder.prototype.quadraticCurveTo = function (x, y, x2, y2) {\n            this._add('Q', x, y, x2, y2);\n        };\n        SVGPathRebuilder.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n            this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise);\n        };\n        SVGPathRebuilder.prototype.ellipse = function (cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise) {\n            var dTheta = endAngle - startAngle;\n            var clockwise = !anticlockwise;\n            var dThetaPositive = Math.abs(dTheta);\n            var isCircle = isAroundZero$1(dThetaPositive - PI2$6)\n                || (clockwise ? dTheta >= PI2$6 : -dTheta >= PI2$6);\n            var unifiedTheta = dTheta > 0 ? dTheta % PI2$6 : (dTheta % PI2$6 + PI2$6);\n            var large = false;\n            if (isCircle) {\n                large = true;\n            }\n            else if (isAroundZero$1(dThetaPositive)) {\n                large = false;\n            }\n            else {\n                large = (unifiedTheta >= PI$4) === !!clockwise;\n            }\n            var x0 = cx + rx * mathCos$4(startAngle);\n            var y0 = cy + ry * mathSin$4(startAngle);\n            if (this._start) {\n                this._add('M', x0, y0);\n            }\n            var xRot = Math.round(psi * degree);\n            if (isCircle) {\n                var p = 1 / this._p;\n                var dTheta_1 = (clockwise ? 1 : -1) * (PI2$6 - p);\n                this._add('A', rx, ry, xRot, 1, +clockwise, cx + rx * mathCos$4(startAngle + dTheta_1), cy + ry * mathSin$4(startAngle + dTheta_1));\n                if (p > 1e-2) {\n                    this._add('A', rx, ry, xRot, 0, +clockwise, x0, y0);\n                }\n            }\n            else {\n                var x = cx + rx * mathCos$4(endAngle);\n                var y = cy + ry * mathSin$4(endAngle);\n                this._add('A', rx, ry, xRot, +large, +clockwise, x, y);\n            }\n        };\n        SVGPathRebuilder.prototype.rect = function (x, y, w, h) {\n            this._add('M', x, y);\n            this._add('l', w, 0);\n            this._add('l', 0, h);\n            this._add('l', -w, 0);\n            this._add('Z');\n        };\n        SVGPathRebuilder.prototype.closePath = function () {\n            if (this._d.length > 0) {\n                this._add('Z');\n            }\n        };\n        SVGPathRebuilder.prototype._add = function (cmd, a, b, c, d, e, f, g, h) {\n            var vals = [];\n            var p = this._p;\n            for (var i = 1; i < arguments.length; i++) {\n                var val = arguments[i];\n                if (isNaN(val)) {\n                    this._invalid = true;\n                    return;\n                }\n                vals.push(Math.round(val * p) / p);\n            }\n            this._d.push(cmd + vals.join(' '));\n            this._start = cmd === 'Z';\n        };\n        SVGPathRebuilder.prototype.generateStr = function () {\n            this._str = this._invalid ? '' : this._d.join('');\n            this._d = [];\n        };\n        SVGPathRebuilder.prototype.getStr = function () {\n            return this._str;\n        };\n        return SVGPathRebuilder;\n    }());\n\n    var NONE = 'none';\n    var mathRound$1 = Math.round;\n    function pathHasFill(style) {\n        var fill = style.fill;\n        return fill != null && fill !== NONE;\n    }\n    function pathHasStroke(style) {\n        var stroke = style.stroke;\n        return stroke != null && stroke !== NONE;\n    }\n    var strokeProps = ['lineCap', 'miterLimit', 'lineJoin'];\n    var svgStrokeProps = map(strokeProps, function (prop) { return \"stroke-\" + prop.toLowerCase(); });\n    function mapStyleToAttrs(updateAttr, style, el, forceUpdate) {\n        var opacity = style.opacity == null ? 1 : style.opacity;\n        if (el instanceof ZRImage) {\n            updateAttr('opacity', opacity);\n            return;\n        }\n        if (pathHasFill(style)) {\n            var fill = normalizeColor(style.fill);\n            updateAttr('fill', fill.color);\n            var fillOpacity = style.fillOpacity != null\n                ? style.fillOpacity * fill.opacity * opacity\n                : fill.opacity * opacity;\n            if (forceUpdate || fillOpacity < 1) {\n                updateAttr('fill-opacity', fillOpacity);\n            }\n        }\n        else {\n            updateAttr('fill', NONE);\n        }\n        if (pathHasStroke(style)) {\n            var stroke = normalizeColor(style.stroke);\n            updateAttr('stroke', stroke.color);\n            var strokeScale = style.strokeNoScale\n                ? el.getLineScale()\n                : 1;\n            var strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0);\n            var strokeOpacity = style.strokeOpacity != null\n                ? style.strokeOpacity * stroke.opacity * opacity\n                : stroke.opacity * opacity;\n            var strokeFirst = style.strokeFirst;\n            if (forceUpdate || strokeWidth !== 1) {\n                updateAttr('stroke-width', strokeWidth);\n            }\n            if (forceUpdate || strokeFirst) {\n                updateAttr('paint-order', strokeFirst ? 'stroke' : 'fill');\n            }\n            if (forceUpdate || strokeOpacity < 1) {\n                updateAttr('stroke-opacity', strokeOpacity);\n            }\n            if (style.lineDash) {\n                var _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n                if (lineDash) {\n                    lineDashOffset = mathRound$1(lineDashOffset || 0);\n                    updateAttr('stroke-dasharray', lineDash.join(','));\n                    if (lineDashOffset || forceUpdate) {\n                        updateAttr('stroke-dashoffset', lineDashOffset);\n                    }\n                }\n            }\n            else if (forceUpdate) {\n                updateAttr('stroke-dasharray', NONE);\n            }\n            for (var i = 0; i < strokeProps.length; i++) {\n                var propName = strokeProps[i];\n                if (forceUpdate || style[propName] !== DEFAULT_PATH_STYLE[propName]) {\n                    var val = style[propName] || DEFAULT_PATH_STYLE[propName];\n                    val && updateAttr(svgStrokeProps[i], val);\n                }\n            }\n        }\n        else if (forceUpdate) {\n            updateAttr('stroke', NONE);\n        }\n    }\n\n    var SVGNS = 'http://www.w3.org/2000/svg';\n    var XLINKNS = 'http://www.w3.org/1999/xlink';\n    var XMLNS = 'http://www.w3.org/2000/xmlns/';\n    var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';\n    function createElement(name) {\n        return document.createElementNS(SVGNS, name);\n    }\n    function createVNode(tag, key, attrs, children, text) {\n        return {\n            tag: tag,\n            attrs: attrs || {},\n            children: children,\n            text: text,\n            key: key\n        };\n    }\n    function createElementOpen(name, attrs) {\n        var attrsStr = [];\n        if (attrs) {\n            for (var key in attrs) {\n                var val = attrs[key];\n                var part = key;\n                if (val === false) {\n                    continue;\n                }\n                else if (val !== true && val != null) {\n                    part += \"=\\\"\" + val + \"\\\"\";\n                }\n                attrsStr.push(part);\n            }\n        }\n        return \"<\" + name + \" \" + attrsStr.join(' ') + \">\";\n    }\n    function createElementClose(name) {\n        return \"</\" + name + \">\";\n    }\n    function vNodeToString(el, opts) {\n        opts = opts || {};\n        var S = opts.newline ? '\\n' : '';\n        function convertElToString(el) {\n            var children = el.children, tag = el.tag, attrs = el.attrs;\n            return createElementOpen(tag, attrs)\n                + encodeHTML(el.text)\n                + (children ? \"\" + S + map(children, function (child) { return convertElToString(child); }).join(S) + S : '')\n                + createElementClose(tag);\n        }\n        return convertElToString(el);\n    }\n    function getCssString(selectorNodes, animationNodes, opts) {\n        opts = opts || {};\n        var S = opts.newline ? '\\n' : '';\n        var bracketBegin = \" {\" + S;\n        var bracketEnd = S + \"}\";\n        var selectors = map(keys(selectorNodes), function (className) {\n            return className + bracketBegin + map(keys(selectorNodes[className]), function (attrName) {\n                return attrName + \":\" + selectorNodes[className][attrName] + \";\";\n            }).join(S) + bracketEnd;\n        }).join(S);\n        var animations = map(keys(animationNodes), function (animationName) {\n            return \"@keyframes \" + animationName + bracketBegin + map(keys(animationNodes[animationName]), function (percent) {\n                return percent + bracketBegin + map(keys(animationNodes[animationName][percent]), function (attrName) {\n                    var val = animationNodes[animationName][percent][attrName];\n                    if (attrName === 'd') {\n                        val = \"path(\\\"\" + val + \"\\\")\";\n                    }\n                    return attrName + \":\" + val + \";\";\n                }).join(S) + bracketEnd;\n            }).join(S) + bracketEnd;\n        }).join(S);\n        if (!selectors && !animations) {\n            return '';\n        }\n        return ['<![CDATA[', selectors, animations, ']]>'].join(S);\n    }\n    function createBrushScope(zrId) {\n        return {\n            zrId: zrId,\n            shadowCache: {},\n            patternCache: {},\n            gradientCache: {},\n            clipPathCache: {},\n            defs: {},\n            cssNodes: {},\n            cssAnims: {},\n            cssClassIdx: 0,\n            cssAnimIdx: 0,\n            shadowIdx: 0,\n            gradientIdx: 0,\n            patternIdx: 0,\n            clipPathIdx: 0\n        };\n    }\n    function createSVGVNode(width, height, children, useViewBox) {\n        return createVNode('svg', 'root', {\n            'width': width,\n            'height': height,\n            'xmlns': SVGNS,\n            'xmlns:xlink': XLINKNS,\n            'version': '1.1',\n            'baseProfile': 'full',\n            'viewBox': useViewBox ? \"0 0 \" + width + \" \" + height : false\n        }, children);\n    }\n\n    var EASING_MAP = {\n        cubicIn: '0.32,0,0.67,0',\n        cubicOut: '0.33,1,0.68,1',\n        cubicInOut: '0.65,0,0.35,1',\n        quadraticIn: '0.11,0,0.5,0',\n        quadraticOut: '0.5,1,0.89,1',\n        quadraticInOut: '0.45,0,0.55,1',\n        quarticIn: '0.5,0,0.75,0',\n        quarticOut: '0.25,1,0.5,1',\n        quarticInOut: '0.76,0,0.24,1',\n        quinticIn: '0.64,0,0.78,0',\n        quinticOut: '0.22,1,0.36,1',\n        quinticInOut: '0.83,0,0.17,1',\n        sinusoidalIn: '0.12,0,0.39,0',\n        sinusoidalOut: '0.61,1,0.88,1',\n        sinusoidalInOut: '0.37,0,0.63,1',\n        exponentialIn: '0.7,0,0.84,0',\n        exponentialOut: '0.16,1,0.3,1',\n        exponentialInOut: '0.87,0,0.13,1',\n        circularIn: '0.55,0,1,0.45',\n        circularOut: '0,0.55,0.45,1',\n        circularInOut: '0.85,0,0.15,1'\n    };\n    var transformOriginKey = 'transform-origin';\n    function buildPathString(el, kfShape, path) {\n        var shape = extend({}, el.shape);\n        extend(shape, kfShape);\n        el.buildPath(path, shape);\n        var svgPathBuilder = new SVGPathRebuilder();\n        svgPathBuilder.reset(getPathPrecision(el));\n        path.rebuildPath(svgPathBuilder, 1);\n        svgPathBuilder.generateStr();\n        return svgPathBuilder.getStr();\n    }\n    function setTransformOrigin(target, transform) {\n        var originX = transform.originX, originY = transform.originY;\n        if (originX || originY) {\n            target[transformOriginKey] = originX + \"px \" + originY + \"px\";\n        }\n    }\n    var ANIMATE_STYLE_MAP = {\n        fill: 'fill',\n        opacity: 'opacity',\n        lineWidth: 'stroke-width',\n        lineDashOffset: 'stroke-dashoffset'\n    };\n    function addAnimation(cssAnim, scope) {\n        var animationName = scope.zrId + '-ani-' + scope.cssAnimIdx++;\n        scope.cssAnims[animationName] = cssAnim;\n        return animationName;\n    }\n    function createCompoundPathCSSAnimation(el, attrs, scope) {\n        var paths = el.shape.paths;\n        var composedAnim = {};\n        var cssAnimationCfg;\n        var cssAnimationName;\n        each(paths, function (path) {\n            var subScope = createBrushScope(scope.zrId);\n            subScope.animation = true;\n            createCSSAnimation(path, {}, subScope, true);\n            var cssAnims = subScope.cssAnims;\n            var cssNodes = subScope.cssNodes;\n            var animNames = keys(cssAnims);\n            var len = animNames.length;\n            if (!len) {\n                return;\n            }\n            cssAnimationName = animNames[len - 1];\n            var lastAnim = cssAnims[cssAnimationName];\n            for (var percent in lastAnim) {\n                var kf = lastAnim[percent];\n                composedAnim[percent] = composedAnim[percent] || { d: '' };\n                composedAnim[percent].d += kf.d || '';\n            }\n            for (var className in cssNodes) {\n                var val = cssNodes[className].animation;\n                if (val.indexOf(cssAnimationName) >= 0) {\n                    cssAnimationCfg = val;\n                }\n            }\n        });\n        if (!cssAnimationCfg) {\n            return;\n        }\n        attrs.d = false;\n        var animationName = addAnimation(composedAnim, scope);\n        return cssAnimationCfg.replace(cssAnimationName, animationName);\n    }\n    function getEasingFunc(easing) {\n        return isString(easing)\n            ? EASING_MAP[easing]\n                ? \"cubic-bezier(\" + EASING_MAP[easing] + \")\"\n                : createCubicEasingFunc(easing) ? easing : ''\n            : '';\n    }\n    function createCSSAnimation(el, attrs, scope, onlyShape) {\n        var animators = el.animators;\n        var len = animators.length;\n        var cssAnimations = [];\n        if (el instanceof CompoundPath) {\n            var animationCfg = createCompoundPathCSSAnimation(el, attrs, scope);\n            if (animationCfg) {\n                cssAnimations.push(animationCfg);\n            }\n            else if (!len) {\n                return;\n            }\n        }\n        else if (!len) {\n            return;\n        }\n        var groupAnimators = {};\n        for (var i = 0; i < len; i++) {\n            var animator = animators[i];\n            var cfgArr = [animator.getMaxTime() / 1000 + 's'];\n            var easing = getEasingFunc(animator.getClip().easing);\n            var delay = animator.getDelay();\n            if (easing) {\n                cfgArr.push(easing);\n            }\n            else {\n                cfgArr.push('linear');\n            }\n            if (delay) {\n                cfgArr.push(delay / 1000 + 's');\n            }\n            if (animator.getLoop()) {\n                cfgArr.push('infinite');\n            }\n            var cfg = cfgArr.join(' ');\n            groupAnimators[cfg] = groupAnimators[cfg] || [cfg, []];\n            groupAnimators[cfg][1].push(animator);\n        }\n        function createSingleCSSAnimation(groupAnimator) {\n            var animators = groupAnimator[1];\n            var len = animators.length;\n            var transformKfs = {};\n            var shapeKfs = {};\n            var finalKfs = {};\n            var animationTimingFunctionAttrName = 'animation-timing-function';\n            function saveAnimatorTrackToCssKfs(animator, cssKfs, toCssAttrName) {\n                var tracks = animator.getTracks();\n                var maxTime = animator.getMaxTime();\n                for (var k = 0; k < tracks.length; k++) {\n                    var track = tracks[k];\n                    if (track.needsAnimate()) {\n                        var kfs = track.keyframes;\n                        var attrName = track.propName;\n                        toCssAttrName && (attrName = toCssAttrName(attrName));\n                        if (attrName) {\n                            for (var i = 0; i < kfs.length; i++) {\n                                var kf = kfs[i];\n                                var percent = Math.round(kf.time / maxTime * 100) + '%';\n                                var kfEasing = getEasingFunc(kf.easing);\n                                var rawValue = kf.rawValue;\n                                if (isString(rawValue) || isNumber(rawValue)) {\n                                    cssKfs[percent] = cssKfs[percent] || {};\n                                    cssKfs[percent][attrName] = kf.rawValue;\n                                    if (kfEasing) {\n                                        cssKfs[percent][animationTimingFunctionAttrName] = kfEasing;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            for (var i = 0; i < len; i++) {\n                var animator = animators[i];\n                var targetProp = animator.targetName;\n                if (!targetProp) {\n                    !onlyShape && saveAnimatorTrackToCssKfs(animator, transformKfs);\n                }\n                else if (targetProp === 'shape') {\n                    saveAnimatorTrackToCssKfs(animator, shapeKfs);\n                }\n            }\n            for (var percent in transformKfs) {\n                var transform = {};\n                copyTransform(transform, el);\n                extend(transform, transformKfs[percent]);\n                var str = getSRTTransformString(transform);\n                var timingFunction = transformKfs[percent][animationTimingFunctionAttrName];\n                finalKfs[percent] = str ? {\n                    transform: str\n                } : {};\n                setTransformOrigin(finalKfs[percent], transform);\n                if (timingFunction) {\n                    finalKfs[percent][animationTimingFunctionAttrName] = timingFunction;\n                }\n            }\n            var path;\n            var canAnimateShape = true;\n            for (var percent in shapeKfs) {\n                finalKfs[percent] = finalKfs[percent] || {};\n                var isFirst = !path;\n                var timingFunction = shapeKfs[percent][animationTimingFunctionAttrName];\n                if (isFirst) {\n                    path = new PathProxy();\n                }\n                var len_1 = path.len();\n                path.reset();\n                finalKfs[percent].d = buildPathString(el, shapeKfs[percent], path);\n                var newLen = path.len();\n                if (!isFirst && len_1 !== newLen) {\n                    canAnimateShape = false;\n                    break;\n                }\n                if (timingFunction) {\n                    finalKfs[percent][animationTimingFunctionAttrName] = timingFunction;\n                }\n            }\n            if (!canAnimateShape) {\n                for (var percent in finalKfs) {\n                    delete finalKfs[percent].d;\n                }\n            }\n            if (!onlyShape) {\n                for (var i = 0; i < len; i++) {\n                    var animator = animators[i];\n                    var targetProp = animator.targetName;\n                    if (targetProp === 'style') {\n                        saveAnimatorTrackToCssKfs(animator, finalKfs, function (propName) { return ANIMATE_STYLE_MAP[propName]; });\n                    }\n                }\n            }\n            var percents = keys(finalKfs);\n            var allTransformOriginSame = true;\n            var transformOrigin;\n            for (var i = 1; i < percents.length; i++) {\n                var p0 = percents[i - 1];\n                var p1 = percents[i];\n                if (finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) {\n                    allTransformOriginSame = false;\n                    break;\n                }\n                transformOrigin = finalKfs[p0][transformOriginKey];\n            }\n            if (allTransformOriginSame && transformOrigin) {\n                for (var percent in finalKfs) {\n                    if (finalKfs[percent][transformOriginKey]) {\n                        delete finalKfs[percent][transformOriginKey];\n                    }\n                }\n                attrs[transformOriginKey] = transformOrigin;\n            }\n            if (filter(percents, function (percent) { return keys(finalKfs[percent]).length > 0; }).length) {\n                var animationName = addAnimation(finalKfs, scope);\n                return animationName + \" \" + groupAnimator[0] + \" both\";\n            }\n        }\n        for (var key in groupAnimators) {\n            var animationCfg = createSingleCSSAnimation(groupAnimators[key]);\n            if (animationCfg) {\n                cssAnimations.push(animationCfg);\n            }\n        }\n        if (cssAnimations.length) {\n            var className = scope.zrId + '-cls-' + scope.cssClassIdx++;\n            scope.cssNodes['.' + className] = {\n                animation: cssAnimations.join(',')\n            };\n            attrs[\"class\"] = className;\n        }\n    }\n\n    var round$2 = Math.round;\n    function isImageLike$1(val) {\n        return val && isString(val.src);\n    }\n    function isCanvasLike(val) {\n        return val && isFunction(val.toDataURL);\n    }\n    function setStyleAttrs(attrs, style, el, scope) {\n        mapStyleToAttrs(function (key, val) {\n            var isFillStroke = key === 'fill' || key === 'stroke';\n            if (isFillStroke && isGradient(val)) {\n                setGradient(style, attrs, key, scope);\n            }\n            else if (isFillStroke && isPattern(val)) {\n                setPattern(el, attrs, key, scope);\n            }\n            else {\n                attrs[key] = val;\n            }\n        }, style, el, false);\n        setShadow(el, attrs, scope);\n    }\n    function noRotateScale(m) {\n        return isAroundZero$1(m[0] - 1)\n            && isAroundZero$1(m[1])\n            && isAroundZero$1(m[2])\n            && isAroundZero$1(m[3] - 1);\n    }\n    function noTranslate(m) {\n        return isAroundZero$1(m[4]) && isAroundZero$1(m[5]);\n    }\n    function setTransform(attrs, m, compress) {\n        if (m && !(noTranslate(m) && noRotateScale(m))) {\n            var mul = compress ? 10 : 1e4;\n            attrs.transform = noRotateScale(m)\n                ? \"translate(\" + round$2(m[4] * mul) / mul + \" \" + round$2(m[5] * mul) / mul + \")\" : getMatrixStr(m);\n        }\n    }\n    function convertPolyShape(shape, attrs, mul) {\n        var points = shape.points;\n        var strArr = [];\n        for (var i = 0; i < points.length; i++) {\n            strArr.push(round$2(points[i][0] * mul) / mul);\n            strArr.push(round$2(points[i][1] * mul) / mul);\n        }\n        attrs.points = strArr.join(' ');\n    }\n    function validatePolyShape(shape) {\n        return !shape.smooth;\n    }\n    function createAttrsConvert(desc) {\n        var normalizedDesc = map(desc, function (item) {\n            return (typeof item === 'string' ? [item, item] : item);\n        });\n        return function (shape, attrs, mul) {\n            for (var i = 0; i < normalizedDesc.length; i++) {\n                var item = normalizedDesc[i];\n                var val = shape[item[0]];\n                if (val != null) {\n                    attrs[item[1]] = round$2(val * mul) / mul;\n                }\n            }\n        };\n    }\n    var builtinShapesDef = {\n        circle: [createAttrsConvert(['cx', 'cy', 'r'])],\n        polyline: [convertPolyShape, validatePolyShape],\n        polygon: [convertPolyShape, validatePolyShape]\n    };\n    function hasShapeAnimation(el) {\n        var animators = el.animators;\n        for (var i = 0; i < animators.length; i++) {\n            if (animators[i].targetName === 'shape') {\n                return true;\n            }\n        }\n        return false;\n    }\n    function brushSVGPath(el, scope) {\n        var style = el.style;\n        var shape = el.shape;\n        var builtinShpDef = builtinShapesDef[el.type];\n        var attrs = {};\n        var needsAnimate = scope.animation;\n        var svgElType = 'path';\n        var strokePercent = el.style.strokePercent;\n        var precision = (scope.compress && getPathPrecision(el)) || 4;\n        if (builtinShpDef\n            && !scope.willUpdate\n            && !(builtinShpDef[1] && !builtinShpDef[1](shape))\n            && !(needsAnimate && hasShapeAnimation(el))\n            && !(strokePercent < 1)) {\n            svgElType = el.type;\n            var mul = Math.pow(10, precision);\n            builtinShpDef[0](shape, attrs, mul);\n        }\n        else {\n            var needBuildPath = !el.path || el.shapeChanged();\n            if (!el.path) {\n                el.createPathProxy();\n            }\n            var path = el.path;\n            if (needBuildPath) {\n                path.beginPath();\n                el.buildPath(path, el.shape);\n                el.pathUpdated();\n            }\n            var pathVersion = path.getVersion();\n            var elExt = el;\n            var svgPathBuilder = elExt.__svgPathBuilder;\n            if (elExt.__svgPathVersion !== pathVersion\n                || !svgPathBuilder\n                || strokePercent !== elExt.__svgPathStrokePercent) {\n                if (!svgPathBuilder) {\n                    svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder();\n                }\n                svgPathBuilder.reset(precision);\n                path.rebuildPath(svgPathBuilder, strokePercent);\n                svgPathBuilder.generateStr();\n                elExt.__svgPathVersion = pathVersion;\n                elExt.__svgPathStrokePercent = strokePercent;\n            }\n            attrs.d = svgPathBuilder.getStr();\n        }\n        setTransform(attrs, el.transform);\n        setStyleAttrs(attrs, style, el, scope);\n        scope.animation && createCSSAnimation(el, attrs, scope);\n        return createVNode(svgElType, el.id + '', attrs);\n    }\n    function brushSVGImage(el, scope) {\n        var style = el.style;\n        var image = style.image;\n        if (image && !isString(image)) {\n            if (isImageLike$1(image)) {\n                image = image.src;\n            }\n            else if (isCanvasLike(image)) {\n                image = image.toDataURL();\n            }\n        }\n        if (!image) {\n            return;\n        }\n        var x = style.x || 0;\n        var y = style.y || 0;\n        var dw = style.width;\n        var dh = style.height;\n        var attrs = {\n            href: image,\n            width: dw,\n            height: dh\n        };\n        if (x) {\n            attrs.x = x;\n        }\n        if (y) {\n            attrs.y = y;\n        }\n        setTransform(attrs, el.transform);\n        setStyleAttrs(attrs, style, el, scope);\n        scope.animation && createCSSAnimation(el, attrs, scope);\n        return createVNode('image', el.id + '', attrs);\n    }\n    function brushSVGTSpan(el, scope) {\n        var style = el.style;\n        var text = style.text;\n        text != null && (text += '');\n        if (!text || isNaN(style.x) || isNaN(style.y)) {\n            return;\n        }\n        var font = style.font || DEFAULT_FONT;\n        var x = style.x || 0;\n        var y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline);\n        var textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign]\n            || style.textAlign;\n        var attrs = {\n            'dominant-baseline': 'central',\n            'text-anchor': textAlign\n        };\n        if (hasSeparateFont(style)) {\n            var separatedFontStr = '';\n            var fontStyle = style.fontStyle;\n            var fontSize = parseFontSize(style.fontSize);\n            if (!parseFloat(fontSize)) {\n                return;\n            }\n            var fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY;\n            var fontWeight = style.fontWeight;\n            separatedFontStr += \"font-size:\" + fontSize + \";font-family:\" + fontFamily + \";\";\n            if (fontStyle && fontStyle !== 'normal') {\n                separatedFontStr += \"font-style:\" + fontStyle + \";\";\n            }\n            if (fontWeight && fontWeight !== 'normal') {\n                separatedFontStr += \"font-weight:\" + fontWeight + \";\";\n            }\n            attrs.style = separatedFontStr;\n        }\n        else {\n            attrs.style = \"font: \" + font;\n        }\n        if (text.match(/\\s/)) {\n            attrs['xml:space'] = 'preserve';\n        }\n        if (x) {\n            attrs.x = x;\n        }\n        if (y) {\n            attrs.y = y;\n        }\n        setTransform(attrs, el.transform);\n        setStyleAttrs(attrs, style, el, scope);\n        scope.animation && createCSSAnimation(el, attrs, scope);\n        return createVNode('text', el.id + '', attrs, undefined, text);\n    }\n    function brush$1(el, scope) {\n        if (el instanceof Path) {\n            return brushSVGPath(el, scope);\n        }\n        else if (el instanceof ZRImage) {\n            return brushSVGImage(el, scope);\n        }\n        else if (el instanceof TSpan) {\n            return brushSVGTSpan(el, scope);\n        }\n    }\n    function setShadow(el, attrs, scope) {\n        var style = el.style;\n        if (hasShadow(style)) {\n            var shadowKey = getShadowKey(el);\n            var shadowCache = scope.shadowCache;\n            var shadowId = shadowCache[shadowKey];\n            if (!shadowId) {\n                var globalScale = el.getGlobalScale();\n                var scaleX = globalScale[0];\n                var scaleY = globalScale[1];\n                if (!scaleX || !scaleY) {\n                    return;\n                }\n                var offsetX = style.shadowOffsetX || 0;\n                var offsetY = style.shadowOffsetY || 0;\n                var blur_1 = style.shadowBlur;\n                var _a = normalizeColor(style.shadowColor), opacity = _a.opacity, color = _a.color;\n                var stdDx = blur_1 / 2 / scaleX;\n                var stdDy = blur_1 / 2 / scaleY;\n                var stdDeviation = stdDx + ' ' + stdDy;\n                shadowId = scope.zrId + '-s' + scope.shadowIdx++;\n                scope.defs[shadowId] = createVNode('filter', shadowId, {\n                    'id': shadowId,\n                    'x': '-100%',\n                    'y': '-100%',\n                    'width': '300%',\n                    'height': '300%'\n                }, [\n                    createVNode('feDropShadow', '', {\n                        'dx': offsetX / scaleX,\n                        'dy': offsetY / scaleY,\n                        'stdDeviation': stdDeviation,\n                        'flood-color': color,\n                        'flood-opacity': opacity\n                    })\n                ]);\n                shadowCache[shadowKey] = shadowId;\n            }\n            attrs.filter = getIdURL(shadowId);\n        }\n    }\n    function setGradient(style, attrs, target, scope) {\n        var val = style[target];\n        var gradientTag;\n        var gradientAttrs = {\n            'gradientUnits': val.global\n                ? 'userSpaceOnUse'\n                : 'objectBoundingBox'\n        };\n        if (isLinearGradient(val)) {\n            gradientTag = 'linearGradient';\n            gradientAttrs.x1 = val.x;\n            gradientAttrs.y1 = val.y;\n            gradientAttrs.x2 = val.x2;\n            gradientAttrs.y2 = val.y2;\n        }\n        else if (isRadialGradient(val)) {\n            gradientTag = 'radialGradient';\n            gradientAttrs.cx = retrieve2(val.x, 0.5);\n            gradientAttrs.cy = retrieve2(val.y, 0.5);\n            gradientAttrs.r = retrieve2(val.r, 0.5);\n        }\n        else {\n            if (\"development\" !== 'production') {\n                logError('Illegal gradient type.');\n            }\n            return;\n        }\n        var colors = val.colorStops;\n        var colorStops = [];\n        for (var i = 0, len = colors.length; i < len; ++i) {\n            var offset = round4(colors[i].offset) * 100 + '%';\n            var stopColor = colors[i].color;\n            var _a = normalizeColor(stopColor), color = _a.color, opacity = _a.opacity;\n            var stopsAttrs = {\n                'offset': offset\n            };\n            stopsAttrs['stop-color'] = color;\n            if (opacity < 1) {\n                stopsAttrs['stop-opacity'] = opacity;\n            }\n            colorStops.push(createVNode('stop', i + '', stopsAttrs));\n        }\n        var gradientVNode = createVNode(gradientTag, '', gradientAttrs, colorStops);\n        var gradientKey = vNodeToString(gradientVNode);\n        var gradientCache = scope.gradientCache;\n        var gradientId = gradientCache[gradientKey];\n        if (!gradientId) {\n            gradientId = scope.zrId + '-g' + scope.gradientIdx++;\n            gradientCache[gradientKey] = gradientId;\n            gradientAttrs.id = gradientId;\n            scope.defs[gradientId] = createVNode(gradientTag, gradientId, gradientAttrs, colorStops);\n        }\n        attrs[target] = getIdURL(gradientId);\n    }\n    function setPattern(el, attrs, target, scope) {\n        var val = el.style[target];\n        var boundingRect = el.getBoundingRect();\n        var patternAttrs = {};\n        var repeat = val.repeat;\n        var noRepeat = repeat === 'no-repeat';\n        var repeatX = repeat === 'repeat-x';\n        var repeatY = repeat === 'repeat-y';\n        var child;\n        if (isImagePattern(val)) {\n            var imageWidth_1 = val.imageWidth;\n            var imageHeight_1 = val.imageHeight;\n            var imageSrc = void 0;\n            var patternImage = val.image;\n            if (isString(patternImage)) {\n                imageSrc = patternImage;\n            }\n            else if (isImageLike$1(patternImage)) {\n                imageSrc = patternImage.src;\n            }\n            else if (isCanvasLike(patternImage)) {\n                imageSrc = patternImage.toDataURL();\n            }\n            if (typeof Image === 'undefined') {\n                var errMsg = 'Image width/height must been given explictly in svg-ssr renderer.';\n                assert(imageWidth_1, errMsg);\n                assert(imageHeight_1, errMsg);\n            }\n            else if (imageWidth_1 == null || imageHeight_1 == null) {\n                var setSizeToVNode_1 = function (vNode, img) {\n                    if (vNode) {\n                        var svgEl = vNode.elm;\n                        var width = imageWidth_1 || img.width;\n                        var height = imageHeight_1 || img.height;\n                        if (vNode.tag === 'pattern') {\n                            if (repeatX) {\n                                height = 1;\n                                width /= boundingRect.width;\n                            }\n                            else if (repeatY) {\n                                width = 1;\n                                height /= boundingRect.height;\n                            }\n                        }\n                        vNode.attrs.width = width;\n                        vNode.attrs.height = height;\n                        if (svgEl) {\n                            svgEl.setAttribute('width', width);\n                            svgEl.setAttribute('height', height);\n                        }\n                    }\n                };\n                var createdImage = createOrUpdateImage(imageSrc, null, el, function (img) {\n                    noRepeat || setSizeToVNode_1(patternVNode, img);\n                    setSizeToVNode_1(child, img);\n                });\n                if (createdImage && createdImage.width && createdImage.height) {\n                    imageWidth_1 = imageWidth_1 || createdImage.width;\n                    imageHeight_1 = imageHeight_1 || createdImage.height;\n                }\n            }\n            child = createVNode('image', 'img', {\n                href: imageSrc,\n                width: imageWidth_1,\n                height: imageHeight_1\n            });\n            patternAttrs.width = imageWidth_1;\n            patternAttrs.height = imageHeight_1;\n        }\n        else if (val.svgElement) {\n            child = clone(val.svgElement);\n            patternAttrs.width = val.svgWidth;\n            patternAttrs.height = val.svgHeight;\n        }\n        if (!child) {\n            return;\n        }\n        var patternWidth;\n        var patternHeight;\n        if (noRepeat) {\n            patternWidth = patternHeight = 1;\n        }\n        else if (repeatX) {\n            patternHeight = 1;\n            patternWidth = patternAttrs.width / boundingRect.width;\n        }\n        else if (repeatY) {\n            patternWidth = 1;\n            patternHeight = patternAttrs.height / boundingRect.height;\n        }\n        else {\n            patternAttrs.patternUnits = 'userSpaceOnUse';\n        }\n        if (patternWidth != null && !isNaN(patternWidth)) {\n            patternAttrs.width = patternWidth;\n        }\n        if (patternHeight != null && !isNaN(patternHeight)) {\n            patternAttrs.height = patternHeight;\n        }\n        var patternTransform = getSRTTransformString(val);\n        patternTransform && (patternAttrs.patternTransform = patternTransform);\n        var patternVNode = createVNode('pattern', '', patternAttrs, [child]);\n        var patternKey = vNodeToString(patternVNode);\n        var patternCache = scope.patternCache;\n        var patternId = patternCache[patternKey];\n        if (!patternId) {\n            patternId = scope.zrId + '-p' + scope.patternIdx++;\n            patternCache[patternKey] = patternId;\n            patternAttrs.id = patternId;\n            patternVNode = scope.defs[patternId] = createVNode('pattern', patternId, patternAttrs, [child]);\n        }\n        attrs[target] = getIdURL(patternId);\n    }\n    function setClipPath(clipPath, attrs, scope) {\n        var clipPathCache = scope.clipPathCache, defs = scope.defs;\n        var clipPathId = clipPathCache[clipPath.id];\n        if (!clipPathId) {\n            clipPathId = scope.zrId + '-c' + scope.clipPathIdx++;\n            var clipPathAttrs = {\n                id: clipPathId\n            };\n            clipPathCache[clipPath.id] = clipPathId;\n            defs[clipPathId] = createVNode('clipPath', clipPathId, clipPathAttrs, [brushSVGPath(clipPath, scope)]);\n        }\n        attrs['clip-path'] = getIdURL(clipPathId);\n    }\n\n    function createTextNode(text) {\n        return document.createTextNode(text);\n    }\n    function insertBefore(parentNode, newNode, referenceNode) {\n        parentNode.insertBefore(newNode, referenceNode);\n    }\n    function removeChild(node, child) {\n        node.removeChild(child);\n    }\n    function appendChild(node, child) {\n        node.appendChild(child);\n    }\n    function parentNode(node) {\n        return node.parentNode;\n    }\n    function nextSibling(node) {\n        return node.nextSibling;\n    }\n    function setTextContent(node, text) {\n        node.textContent = text;\n    }\n\n    var colonChar = 58;\n    var xChar = 120;\n    var emptyNode = createVNode('', '');\n    function isUndef(s) {\n        return s === undefined;\n    }\n    function isDef(s) {\n        return s !== undefined;\n    }\n    function createKeyToOldIdx(children, beginIdx, endIdx) {\n        var map = {};\n        for (var i = beginIdx; i <= endIdx; ++i) {\n            var key = children[i].key;\n            if (key !== undefined) {\n                if (\"development\" !== 'production') {\n                    if (map[key] != null) {\n                        console.error(\"Duplicate key \" + key);\n                    }\n                }\n                map[key] = i;\n            }\n        }\n        return map;\n    }\n    function sameVnode(vnode1, vnode2) {\n        var isSameKey = vnode1.key === vnode2.key;\n        var isSameTag = vnode1.tag === vnode2.tag;\n        return isSameTag && isSameKey;\n    }\n    function createElm(vnode) {\n        var i;\n        var children = vnode.children;\n        var tag = vnode.tag;\n        if (isDef(tag)) {\n            var elm = (vnode.elm = createElement(tag));\n            updateAttrs(emptyNode, vnode);\n            if (isArray(children)) {\n                for (i = 0; i < children.length; ++i) {\n                    var ch = children[i];\n                    if (ch != null) {\n                        appendChild(elm, createElm(ch));\n                    }\n                }\n            }\n            else if (isDef(vnode.text) && !isObject(vnode.text)) {\n                appendChild(elm, createTextNode(vnode.text));\n            }\n        }\n        else {\n            vnode.elm = createTextNode(vnode.text);\n        }\n        return vnode.elm;\n    }\n    function addVnodes(parentElm, before, vnodes, startIdx, endIdx) {\n        for (; startIdx <= endIdx; ++startIdx) {\n            var ch = vnodes[startIdx];\n            if (ch != null) {\n                insertBefore(parentElm, createElm(ch), before);\n            }\n        }\n    }\n    function removeVnodes(parentElm, vnodes, startIdx, endIdx) {\n        for (; startIdx <= endIdx; ++startIdx) {\n            var ch = vnodes[startIdx];\n            if (ch != null) {\n                if (isDef(ch.tag)) {\n                    var parent_1 = parentNode(ch.elm);\n                    removeChild(parent_1, ch.elm);\n                }\n                else {\n                    removeChild(parentElm, ch.elm);\n                }\n            }\n        }\n    }\n    function updateAttrs(oldVnode, vnode) {\n        var key;\n        var elm = vnode.elm;\n        var oldAttrs = oldVnode && oldVnode.attrs || {};\n        var attrs = vnode.attrs || {};\n        if (oldAttrs === attrs) {\n            return;\n        }\n        for (key in attrs) {\n            var cur = attrs[key];\n            var old = oldAttrs[key];\n            if (old !== cur) {\n                if (cur === true) {\n                    elm.setAttribute(key, '');\n                }\n                else if (cur === false) {\n                    elm.removeAttribute(key);\n                }\n                else {\n                    if (key.charCodeAt(0) !== xChar) {\n                        elm.setAttribute(key, cur);\n                    }\n                    else if (key === 'xmlns:xlink' || key === 'xmlns') {\n                        elm.setAttributeNS(XMLNS, key, cur);\n                    }\n                    else if (key.charCodeAt(3) === colonChar) {\n                        elm.setAttributeNS(XML_NAMESPACE, key, cur);\n                    }\n                    else if (key.charCodeAt(5) === colonChar) {\n                        elm.setAttributeNS(XLINKNS, key, cur);\n                    }\n                    else {\n                        elm.setAttribute(key, cur);\n                    }\n                }\n            }\n        }\n        for (key in oldAttrs) {\n            if (!(key in attrs)) {\n                elm.removeAttribute(key);\n            }\n        }\n    }\n    function updateChildren(parentElm, oldCh, newCh) {\n        var oldStartIdx = 0;\n        var newStartIdx = 0;\n        var oldEndIdx = oldCh.length - 1;\n        var oldStartVnode = oldCh[0];\n        var oldEndVnode = oldCh[oldEndIdx];\n        var newEndIdx = newCh.length - 1;\n        var newStartVnode = newCh[0];\n        var newEndVnode = newCh[newEndIdx];\n        var oldKeyToIdx;\n        var idxInOld;\n        var elmToMove;\n        var before;\n        while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {\n            if (oldStartVnode == null) {\n                oldStartVnode = oldCh[++oldStartIdx];\n            }\n            else if (oldEndVnode == null) {\n                oldEndVnode = oldCh[--oldEndIdx];\n            }\n            else if (newStartVnode == null) {\n                newStartVnode = newCh[++newStartIdx];\n            }\n            else if (newEndVnode == null) {\n                newEndVnode = newCh[--newEndIdx];\n            }\n            else if (sameVnode(oldStartVnode, newStartVnode)) {\n                patchVnode(oldStartVnode, newStartVnode);\n                oldStartVnode = oldCh[++oldStartIdx];\n                newStartVnode = newCh[++newStartIdx];\n            }\n            else if (sameVnode(oldEndVnode, newEndVnode)) {\n                patchVnode(oldEndVnode, newEndVnode);\n                oldEndVnode = oldCh[--oldEndIdx];\n                newEndVnode = newCh[--newEndIdx];\n            }\n            else if (sameVnode(oldStartVnode, newEndVnode)) {\n                patchVnode(oldStartVnode, newEndVnode);\n                insertBefore(parentElm, oldStartVnode.elm, nextSibling(oldEndVnode.elm));\n                oldStartVnode = oldCh[++oldStartIdx];\n                newEndVnode = newCh[--newEndIdx];\n            }\n            else if (sameVnode(oldEndVnode, newStartVnode)) {\n                patchVnode(oldEndVnode, newStartVnode);\n                insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);\n                oldEndVnode = oldCh[--oldEndIdx];\n                newStartVnode = newCh[++newStartIdx];\n            }\n            else {\n                if (isUndef(oldKeyToIdx)) {\n                    oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);\n                }\n                idxInOld = oldKeyToIdx[newStartVnode.key];\n                if (isUndef(idxInOld)) {\n                    insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm);\n                }\n                else {\n                    elmToMove = oldCh[idxInOld];\n                    if (elmToMove.tag !== newStartVnode.tag) {\n                        insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm);\n                    }\n                    else {\n                        patchVnode(elmToMove, newStartVnode);\n                        oldCh[idxInOld] = undefined;\n                        insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);\n                    }\n                }\n                newStartVnode = newCh[++newStartIdx];\n            }\n        }\n        if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) {\n            if (oldStartIdx > oldEndIdx) {\n                before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;\n                addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx);\n            }\n            else {\n                removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);\n            }\n        }\n    }\n    function patchVnode(oldVnode, vnode) {\n        var elm = (vnode.elm = oldVnode.elm);\n        var oldCh = oldVnode.children;\n        var ch = vnode.children;\n        if (oldVnode === vnode) {\n            return;\n        }\n        updateAttrs(oldVnode, vnode);\n        if (isUndef(vnode.text)) {\n            if (isDef(oldCh) && isDef(ch)) {\n                if (oldCh !== ch) {\n                    updateChildren(elm, oldCh, ch);\n                }\n            }\n            else if (isDef(ch)) {\n                if (isDef(oldVnode.text)) {\n                    setTextContent(elm, '');\n                }\n                addVnodes(elm, null, ch, 0, ch.length - 1);\n            }\n            else if (isDef(oldCh)) {\n                removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n            }\n            else if (isDef(oldVnode.text)) {\n                setTextContent(elm, '');\n            }\n        }\n        else if (oldVnode.text !== vnode.text) {\n            if (isDef(oldCh)) {\n                removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n            }\n            setTextContent(elm, vnode.text);\n        }\n    }\n    function patch(oldVnode, vnode) {\n        if (sameVnode(oldVnode, vnode)) {\n            patchVnode(oldVnode, vnode);\n        }\n        else {\n            var elm = oldVnode.elm;\n            var parent_2 = parentNode(elm);\n            createElm(vnode);\n            if (parent_2 !== null) {\n                insertBefore(parent_2, vnode.elm, nextSibling(elm));\n                removeVnodes(parent_2, [oldVnode], 0, 0);\n            }\n        }\n        return vnode;\n    }\n\n    var svgId = 0;\n    var SVGPainter = (function () {\n        function SVGPainter(root, storage, opts) {\n            this.type = 'svg';\n            this.refreshHover = createMethodNotSupport('refreshHover');\n            this.configLayer = createMethodNotSupport('configLayer');\n            this.storage = storage;\n            this._opts = opts = extend({}, opts);\n            this.root = root;\n            this._id = 'zr' + svgId++;\n            this._oldVNode = createSVGVNode(opts.width, opts.height);\n            if (root && !opts.ssr) {\n                var viewport = this._viewport = document.createElement('div');\n                viewport.style.cssText = 'position:relative;overflow:hidden';\n                var svgDom = this._svgDom = this._oldVNode.elm = createElement('svg');\n                updateAttrs(null, this._oldVNode);\n                viewport.appendChild(svgDom);\n                root.appendChild(viewport);\n            }\n            this.resize(opts.width, opts.height);\n        }\n        SVGPainter.prototype.getType = function () {\n            return this.type;\n        };\n        SVGPainter.prototype.getViewportRoot = function () {\n            return this._viewport;\n        };\n        SVGPainter.prototype.getViewportRootOffset = function () {\n            var viewportRoot = this.getViewportRoot();\n            if (viewportRoot) {\n                return {\n                    offsetLeft: viewportRoot.offsetLeft || 0,\n                    offsetTop: viewportRoot.offsetTop || 0\n                };\n            }\n        };\n        SVGPainter.prototype.getSvgDom = function () {\n            return this._svgDom;\n        };\n        SVGPainter.prototype.refresh = function () {\n            if (this.root) {\n                var vnode = this.renderToVNode({\n                    willUpdate: true\n                });\n                vnode.attrs.style = 'position:absolute;left:0;top:0;user-select:none';\n                patch(this._oldVNode, vnode);\n                this._oldVNode = vnode;\n            }\n        };\n        SVGPainter.prototype.renderOneToVNode = function (el) {\n            return brush$1(el, createBrushScope(this._id));\n        };\n        SVGPainter.prototype.renderToVNode = function (opts) {\n            opts = opts || {};\n            var list = this.storage.getDisplayList(true);\n            var width = this._width;\n            var height = this._height;\n            var scope = createBrushScope(this._id);\n            scope.animation = opts.animation;\n            scope.willUpdate = opts.willUpdate;\n            scope.compress = opts.compress;\n            var children = [];\n            var bgVNode = this._bgVNode = createBackgroundVNode(width, height, this._backgroundColor, scope);\n            bgVNode && children.push(bgVNode);\n            var mainVNode = !opts.compress\n                ? (this._mainVNode = createVNode('g', 'main', {}, [])) : null;\n            this._paintList(list, scope, mainVNode ? mainVNode.children : children);\n            mainVNode && children.push(mainVNode);\n            var defs = map(keys(scope.defs), function (id) { return scope.defs[id]; });\n            if (defs.length) {\n                children.push(createVNode('defs', 'defs', {}, defs));\n            }\n            if (opts.animation) {\n                var animationCssStr = getCssString(scope.cssNodes, scope.cssAnims, { newline: true });\n                if (animationCssStr) {\n                    var styleNode = createVNode('style', 'stl', {}, [], animationCssStr);\n                    children.push(styleNode);\n                }\n            }\n            return createSVGVNode(width, height, children, opts.useViewBox);\n        };\n        SVGPainter.prototype.renderToString = function (opts) {\n            opts = opts || {};\n            return vNodeToString(this.renderToVNode({\n                animation: retrieve2(opts.cssAnimation, true),\n                willUpdate: false,\n                compress: true,\n                useViewBox: retrieve2(opts.useViewBox, true)\n            }), { newline: true });\n        };\n        SVGPainter.prototype.setBackgroundColor = function (backgroundColor) {\n            this._backgroundColor = backgroundColor;\n        };\n        SVGPainter.prototype.getSvgRoot = function () {\n            return this._mainVNode && this._mainVNode.elm;\n        };\n        SVGPainter.prototype._paintList = function (list, scope, out) {\n            var listLen = list.length;\n            var clipPathsGroupsStack = [];\n            var clipPathsGroupsStackDepth = 0;\n            var currentClipPathGroup;\n            var prevClipPaths;\n            var clipGroupNodeIdx = 0;\n            for (var i = 0; i < listLen; i++) {\n                var displayable = list[i];\n                if (!displayable.invisible) {\n                    var clipPaths = displayable.__clipPaths;\n                    var len = clipPaths && clipPaths.length || 0;\n                    var prevLen = prevClipPaths && prevClipPaths.length || 0;\n                    var lca = void 0;\n                    for (lca = Math.max(len - 1, prevLen - 1); lca >= 0; lca--) {\n                        if (clipPaths && prevClipPaths\n                            && clipPaths[lca] === prevClipPaths[lca]) {\n                            break;\n                        }\n                    }\n                    for (var i_1 = prevLen - 1; i_1 > lca; i_1--) {\n                        clipPathsGroupsStackDepth--;\n                        currentClipPathGroup = clipPathsGroupsStack[clipPathsGroupsStackDepth - 1];\n                    }\n                    for (var i_2 = lca + 1; i_2 < len; i_2++) {\n                        var groupAttrs = {};\n                        setClipPath(clipPaths[i_2], groupAttrs, scope);\n                        var g = createVNode('g', 'clip-g-' + clipGroupNodeIdx++, groupAttrs, []);\n                        (currentClipPathGroup ? currentClipPathGroup.children : out).push(g);\n                        clipPathsGroupsStack[clipPathsGroupsStackDepth++] = g;\n                        currentClipPathGroup = g;\n                    }\n                    prevClipPaths = clipPaths;\n                    var ret = brush$1(displayable, scope);\n                    if (ret) {\n                        (currentClipPathGroup ? currentClipPathGroup.children : out).push(ret);\n                    }\n                }\n            }\n        };\n        SVGPainter.prototype.resize = function (width, height) {\n            var opts = this._opts;\n            var root = this.root;\n            var viewport = this._viewport;\n            width != null && (opts.width = width);\n            height != null && (opts.height = height);\n            if (root && viewport) {\n                viewport.style.display = 'none';\n                width = getSize(root, 0, opts);\n                height = getSize(root, 1, opts);\n                viewport.style.display = '';\n            }\n            if (this._width !== width || this._height !== height) {\n                this._width = width;\n                this._height = height;\n                if (viewport) {\n                    var viewportStyle = viewport.style;\n                    viewportStyle.width = width + 'px';\n                    viewportStyle.height = height + 'px';\n                }\n                if (!isPattern(this._backgroundColor)) {\n                    var svgDom = this._svgDom;\n                    if (svgDom) {\n                        svgDom.setAttribute('width', width);\n                        svgDom.setAttribute('height', height);\n                    }\n                    var bgEl = this._bgVNode && this._bgVNode.elm;\n                    if (bgEl) {\n                        bgEl.setAttribute('width', width);\n                        bgEl.setAttribute('height', height);\n                    }\n                }\n                else {\n                    this.refresh();\n                }\n            }\n        };\n        SVGPainter.prototype.getWidth = function () {\n            return this._width;\n        };\n        SVGPainter.prototype.getHeight = function () {\n            return this._height;\n        };\n        SVGPainter.prototype.dispose = function () {\n            if (this.root) {\n                this.root.innerHTML = '';\n            }\n            this._svgDom =\n                this._viewport =\n                    this.storage =\n                        this._oldVNode =\n                            this._bgVNode =\n                                this._mainVNode = null;\n        };\n        SVGPainter.prototype.clear = function () {\n            if (this._svgDom) {\n                this._svgDom.innerHTML = null;\n            }\n            this._oldVNode = null;\n        };\n        SVGPainter.prototype.toDataURL = function (base64) {\n            var str = this.renderToString();\n            var prefix = 'data:image/svg+xml;';\n            if (base64) {\n                str = encodeBase64(str);\n                return str && prefix + 'base64,' + str;\n            }\n            return prefix + 'charset=UTF-8,' + encodeURIComponent(str);\n        };\n        return SVGPainter;\n    }());\n    function createMethodNotSupport(method) {\n        return function () {\n            if (\"development\" !== 'production') {\n                logError('In SVG mode painter not support method \"' + method + '\"');\n            }\n        };\n    }\n    function createBackgroundVNode(width, height, backgroundColor, scope) {\n        var bgVNode;\n        if (backgroundColor && backgroundColor !== 'none') {\n            bgVNode = createVNode('rect', 'bg', {\n                width: width,\n                height: height,\n                x: '0',\n                y: '0',\n                id: '0'\n            });\n            if (isGradient(backgroundColor)) {\n                setGradient({ fill: backgroundColor }, bgVNode.attrs, 'fill', scope);\n            }\n            else if (isPattern(backgroundColor)) {\n                setPattern({\n                    style: {\n                        fill: backgroundColor\n                    },\n                    dirty: noop,\n                    getBoundingRect: function () { return ({ width: width, height: height }); }\n                }, bgVNode.attrs, 'fill', scope);\n            }\n            else {\n                var _a = normalizeColor(backgroundColor), color = _a.color, opacity = _a.opacity;\n                bgVNode.attrs.fill = color;\n                opacity < 1 && (bgVNode.attrs['fill-opacity'] = opacity);\n            }\n        }\n        return bgVNode;\n    }\n\n    function install(registers) {\n      registers.registerPainter('svg', SVGPainter);\n    }\n\n    function createDom(id, painter, dpr) {\n        var newDom = platformApi.createCanvas();\n        var width = painter.getWidth();\n        var height = painter.getHeight();\n        var newDomStyle = newDom.style;\n        if (newDomStyle) {\n            newDomStyle.position = 'absolute';\n            newDomStyle.left = '0';\n            newDomStyle.top = '0';\n            newDomStyle.width = width + 'px';\n            newDomStyle.height = height + 'px';\n            newDom.setAttribute('data-zr-dom-id', id);\n        }\n        newDom.width = width * dpr;\n        newDom.height = height * dpr;\n        return newDom;\n    }\n    var Layer = (function (_super) {\n        __extends(Layer, _super);\n        function Layer(id, painter, dpr) {\n            var _this = _super.call(this) || this;\n            _this.motionBlur = false;\n            _this.lastFrameAlpha = 0.7;\n            _this.dpr = 1;\n            _this.virtual = false;\n            _this.config = {};\n            _this.incremental = false;\n            _this.zlevel = 0;\n            _this.maxRepaintRectCount = 5;\n            _this.__dirty = true;\n            _this.__firstTimePaint = true;\n            _this.__used = false;\n            _this.__drawIndex = 0;\n            _this.__startIndex = 0;\n            _this.__endIndex = 0;\n            _this.__prevStartIndex = null;\n            _this.__prevEndIndex = null;\n            var dom;\n            dpr = dpr || devicePixelRatio;\n            if (typeof id === 'string') {\n                dom = createDom(id, painter, dpr);\n            }\n            else if (isObject(id)) {\n                dom = id;\n                id = dom.id;\n            }\n            _this.id = id;\n            _this.dom = dom;\n            var domStyle = dom.style;\n            if (domStyle) {\n                disableUserSelect(dom);\n                dom.onselectstart = function () { return false; };\n                domStyle.padding = '0';\n                domStyle.margin = '0';\n                domStyle.borderWidth = '0';\n            }\n            _this.painter = painter;\n            _this.dpr = dpr;\n            return _this;\n        }\n        Layer.prototype.getElementCount = function () {\n            return this.__endIndex - this.__startIndex;\n        };\n        Layer.prototype.afterBrush = function () {\n            this.__prevStartIndex = this.__startIndex;\n            this.__prevEndIndex = this.__endIndex;\n        };\n        Layer.prototype.initContext = function () {\n            this.ctx = this.dom.getContext('2d');\n            this.ctx.dpr = this.dpr;\n        };\n        Layer.prototype.setUnpainted = function () {\n            this.__firstTimePaint = true;\n        };\n        Layer.prototype.createBackBuffer = function () {\n            var dpr = this.dpr;\n            this.domBack = createDom('back-' + this.id, this.painter, dpr);\n            this.ctxBack = this.domBack.getContext('2d');\n            if (dpr !== 1) {\n                this.ctxBack.scale(dpr, dpr);\n            }\n        };\n        Layer.prototype.createRepaintRects = function (displayList, prevList, viewWidth, viewHeight) {\n            if (this.__firstTimePaint) {\n                this.__firstTimePaint = false;\n                return null;\n            }\n            var mergedRepaintRects = [];\n            var maxRepaintRectCount = this.maxRepaintRectCount;\n            var full = false;\n            var pendingRect = new BoundingRect(0, 0, 0, 0);\n            function addRectToMergePool(rect) {\n                if (!rect.isFinite() || rect.isZero()) {\n                    return;\n                }\n                if (mergedRepaintRects.length === 0) {\n                    var boundingRect = new BoundingRect(0, 0, 0, 0);\n                    boundingRect.copy(rect);\n                    mergedRepaintRects.push(boundingRect);\n                }\n                else {\n                    var isMerged = false;\n                    var minDeltaArea = Infinity;\n                    var bestRectToMergeIdx = 0;\n                    for (var i = 0; i < mergedRepaintRects.length; ++i) {\n                        var mergedRect = mergedRepaintRects[i];\n                        if (mergedRect.intersect(rect)) {\n                            var pendingRect_1 = new BoundingRect(0, 0, 0, 0);\n                            pendingRect_1.copy(mergedRect);\n                            pendingRect_1.union(rect);\n                            mergedRepaintRects[i] = pendingRect_1;\n                            isMerged = true;\n                            break;\n                        }\n                        else if (full) {\n                            pendingRect.copy(rect);\n                            pendingRect.union(mergedRect);\n                            var aArea = rect.width * rect.height;\n                            var bArea = mergedRect.width * mergedRect.height;\n                            var pendingArea = pendingRect.width * pendingRect.height;\n                            var deltaArea = pendingArea - aArea - bArea;\n                            if (deltaArea < minDeltaArea) {\n                                minDeltaArea = deltaArea;\n                                bestRectToMergeIdx = i;\n                            }\n                        }\n                    }\n                    if (full) {\n                        mergedRepaintRects[bestRectToMergeIdx].union(rect);\n                        isMerged = true;\n                    }\n                    if (!isMerged) {\n                        var boundingRect = new BoundingRect(0, 0, 0, 0);\n                        boundingRect.copy(rect);\n                        mergedRepaintRects.push(boundingRect);\n                    }\n                    if (!full) {\n                        full = mergedRepaintRects.length >= maxRepaintRectCount;\n                    }\n                }\n            }\n            for (var i = this.__startIndex; i < this.__endIndex; ++i) {\n                var el = displayList[i];\n                if (el) {\n                    var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n                    var prevRect = el.__isRendered && ((el.__dirty & REDRAW_BIT) || !shouldPaint)\n                        ? el.getPrevPaintRect()\n                        : null;\n                    if (prevRect) {\n                        addRectToMergePool(prevRect);\n                    }\n                    var curRect = shouldPaint && ((el.__dirty & REDRAW_BIT) || !el.__isRendered)\n                        ? el.getPaintRect()\n                        : null;\n                    if (curRect) {\n                        addRectToMergePool(curRect);\n                    }\n                }\n            }\n            for (var i = this.__prevStartIndex; i < this.__prevEndIndex; ++i) {\n                var el = prevList[i];\n                var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n                if (el && (!shouldPaint || !el.__zr) && el.__isRendered) {\n                    var prevRect = el.getPrevPaintRect();\n                    if (prevRect) {\n                        addRectToMergePool(prevRect);\n                    }\n                }\n            }\n            var hasIntersections;\n            do {\n                hasIntersections = false;\n                for (var i = 0; i < mergedRepaintRects.length;) {\n                    if (mergedRepaintRects[i].isZero()) {\n                        mergedRepaintRects.splice(i, 1);\n                        continue;\n                    }\n                    for (var j = i + 1; j < mergedRepaintRects.length;) {\n                        if (mergedRepaintRects[i].intersect(mergedRepaintRects[j])) {\n                            hasIntersections = true;\n                            mergedRepaintRects[i].union(mergedRepaintRects[j]);\n                            mergedRepaintRects.splice(j, 1);\n                        }\n                        else {\n                            j++;\n                        }\n                    }\n                    i++;\n                }\n            } while (hasIntersections);\n            this._paintRects = mergedRepaintRects;\n            return mergedRepaintRects;\n        };\n        Layer.prototype.debugGetPaintRects = function () {\n            return (this._paintRects || []).slice();\n        };\n        Layer.prototype.resize = function (width, height) {\n            var dpr = this.dpr;\n            var dom = this.dom;\n            var domStyle = dom.style;\n            var domBack = this.domBack;\n            if (domStyle) {\n                domStyle.width = width + 'px';\n                domStyle.height = height + 'px';\n            }\n            dom.width = width * dpr;\n            dom.height = height * dpr;\n            if (domBack) {\n                domBack.width = width * dpr;\n                domBack.height = height * dpr;\n                if (dpr !== 1) {\n                    this.ctxBack.scale(dpr, dpr);\n                }\n            }\n        };\n        Layer.prototype.clear = function (clearAll, clearColor, repaintRects) {\n            var dom = this.dom;\n            var ctx = this.ctx;\n            var width = dom.width;\n            var height = dom.height;\n            clearColor = clearColor || this.clearColor;\n            var haveMotionBLur = this.motionBlur && !clearAll;\n            var lastFrameAlpha = this.lastFrameAlpha;\n            var dpr = this.dpr;\n            var self = this;\n            if (haveMotionBLur) {\n                if (!this.domBack) {\n                    this.createBackBuffer();\n                }\n                this.ctxBack.globalCompositeOperation = 'copy';\n                this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr);\n            }\n            var domBack = this.domBack;\n            function doClear(x, y, width, height) {\n                ctx.clearRect(x, y, width, height);\n                if (clearColor && clearColor !== 'transparent') {\n                    var clearColorGradientOrPattern = void 0;\n                    if (isGradientObject(clearColor)) {\n                        var shouldCache = clearColor.global || (clearColor.__width === width\n                            && clearColor.__height === height);\n                        clearColorGradientOrPattern = shouldCache\n                            && clearColor.__canvasGradient\n                            || getCanvasGradient(ctx, clearColor, {\n                                x: 0,\n                                y: 0,\n                                width: width,\n                                height: height\n                            });\n                        clearColor.__canvasGradient = clearColorGradientOrPattern;\n                        clearColor.__width = width;\n                        clearColor.__height = height;\n                    }\n                    else if (isImagePatternObject(clearColor)) {\n                        clearColor.scaleX = clearColor.scaleX || dpr;\n                        clearColor.scaleY = clearColor.scaleY || dpr;\n                        clearColorGradientOrPattern = createCanvasPattern(ctx, clearColor, {\n                            dirty: function () {\n                                self.setUnpainted();\n                                self.__painter.refresh();\n                            }\n                        });\n                    }\n                    ctx.save();\n                    ctx.fillStyle = clearColorGradientOrPattern || clearColor;\n                    ctx.fillRect(x, y, width, height);\n                    ctx.restore();\n                }\n                if (haveMotionBLur) {\n                    ctx.save();\n                    ctx.globalAlpha = lastFrameAlpha;\n                    ctx.drawImage(domBack, x, y, width, height);\n                    ctx.restore();\n                }\n            }\n            if (!repaintRects || haveMotionBLur) {\n                doClear(0, 0, width, height);\n            }\n            else if (repaintRects.length) {\n                each(repaintRects, function (rect) {\n                    doClear(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n                });\n            }\n        };\n        return Layer;\n    }(Eventful));\n\n    var HOVER_LAYER_ZLEVEL = 1e5;\n    var CANVAS_ZLEVEL = 314159;\n    var EL_AFTER_INCREMENTAL_INC = 0.01;\n    var INCREMENTAL_INC = 0.001;\n    function isLayerValid(layer) {\n        if (!layer) {\n            return false;\n        }\n        if (layer.__builtin__) {\n            return true;\n        }\n        if (typeof (layer.resize) !== 'function'\n            || typeof (layer.refresh) !== 'function') {\n            return false;\n        }\n        return true;\n    }\n    function createRoot(width, height) {\n        var domRoot = document.createElement('div');\n        domRoot.style.cssText = [\n            'position:relative',\n            'width:' + width + 'px',\n            'height:' + height + 'px',\n            'padding:0',\n            'margin:0',\n            'border-width:0'\n        ].join(';') + ';';\n        return domRoot;\n    }\n    var CanvasPainter = (function () {\n        function CanvasPainter(root, storage, opts, id) {\n            this.type = 'canvas';\n            this._zlevelList = [];\n            this._prevDisplayList = [];\n            this._layers = {};\n            this._layerConfig = {};\n            this._needsManuallyCompositing = false;\n            this.type = 'canvas';\n            var singleCanvas = !root.nodeName\n                || root.nodeName.toUpperCase() === 'CANVAS';\n            this._opts = opts = extend({}, opts || {});\n            this.dpr = opts.devicePixelRatio || devicePixelRatio;\n            this._singleCanvas = singleCanvas;\n            this.root = root;\n            var rootStyle = root.style;\n            if (rootStyle) {\n                disableUserSelect(root);\n                root.innerHTML = '';\n            }\n            this.storage = storage;\n            var zlevelList = this._zlevelList;\n            this._prevDisplayList = [];\n            var layers = this._layers;\n            if (!singleCanvas) {\n                this._width = getSize(root, 0, opts);\n                this._height = getSize(root, 1, opts);\n                var domRoot = this._domRoot = createRoot(this._width, this._height);\n                root.appendChild(domRoot);\n            }\n            else {\n                var rootCanvas = root;\n                var width = rootCanvas.width;\n                var height = rootCanvas.height;\n                if (opts.width != null) {\n                    width = opts.width;\n                }\n                if (opts.height != null) {\n                    height = opts.height;\n                }\n                this.dpr = opts.devicePixelRatio || 1;\n                rootCanvas.width = width * this.dpr;\n                rootCanvas.height = height * this.dpr;\n                this._width = width;\n                this._height = height;\n                var mainLayer = new Layer(rootCanvas, this, this.dpr);\n                mainLayer.__builtin__ = true;\n                mainLayer.initContext();\n                layers[CANVAS_ZLEVEL] = mainLayer;\n                mainLayer.zlevel = CANVAS_ZLEVEL;\n                zlevelList.push(CANVAS_ZLEVEL);\n                this._domRoot = root;\n            }\n        }\n        CanvasPainter.prototype.getType = function () {\n            return 'canvas';\n        };\n        CanvasPainter.prototype.isSingleCanvas = function () {\n            return this._singleCanvas;\n        };\n        CanvasPainter.prototype.getViewportRoot = function () {\n            return this._domRoot;\n        };\n        CanvasPainter.prototype.getViewportRootOffset = function () {\n            var viewportRoot = this.getViewportRoot();\n            if (viewportRoot) {\n                return {\n                    offsetLeft: viewportRoot.offsetLeft || 0,\n                    offsetTop: viewportRoot.offsetTop || 0\n                };\n            }\n        };\n        CanvasPainter.prototype.refresh = function (paintAll) {\n            var list = this.storage.getDisplayList(true);\n            var prevList = this._prevDisplayList;\n            var zlevelList = this._zlevelList;\n            this._redrawId = Math.random();\n            this._paintList(list, prevList, paintAll, this._redrawId);\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (!layer.__builtin__ && layer.refresh) {\n                    var clearColor = i === 0 ? this._backgroundColor : null;\n                    layer.refresh(clearColor);\n                }\n            }\n            if (this._opts.useDirtyRect) {\n                this._prevDisplayList = list.slice();\n            }\n            return this;\n        };\n        CanvasPainter.prototype.refreshHover = function () {\n            this._paintHoverList(this.storage.getDisplayList(false));\n        };\n        CanvasPainter.prototype._paintHoverList = function (list) {\n            var len = list.length;\n            var hoverLayer = this._hoverlayer;\n            hoverLayer && hoverLayer.clear();\n            if (!len) {\n                return;\n            }\n            var scope = {\n                inHover: true,\n                viewWidth: this._width,\n                viewHeight: this._height\n            };\n            var ctx;\n            for (var i = 0; i < len; i++) {\n                var el = list[i];\n                if (el.__inHover) {\n                    if (!hoverLayer) {\n                        hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);\n                    }\n                    if (!ctx) {\n                        ctx = hoverLayer.ctx;\n                        ctx.save();\n                    }\n                    brush(ctx, el, scope, i === len - 1);\n                }\n            }\n            if (ctx) {\n                ctx.restore();\n            }\n        };\n        CanvasPainter.prototype.getHoverLayer = function () {\n            return this.getLayer(HOVER_LAYER_ZLEVEL);\n        };\n        CanvasPainter.prototype.paintOne = function (ctx, el) {\n            brushSingle(ctx, el);\n        };\n        CanvasPainter.prototype._paintList = function (list, prevList, paintAll, redrawId) {\n            if (this._redrawId !== redrawId) {\n                return;\n            }\n            paintAll = paintAll || false;\n            this._updateLayerStatus(list);\n            var _a = this._doPaintList(list, prevList, paintAll), finished = _a.finished, needsRefreshHover = _a.needsRefreshHover;\n            if (this._needsManuallyCompositing) {\n                this._compositeManually();\n            }\n            if (needsRefreshHover) {\n                this._paintHoverList(list);\n            }\n            if (!finished) {\n                var self_1 = this;\n                requestAnimationFrame$1(function () {\n                    self_1._paintList(list, prevList, paintAll, redrawId);\n                });\n            }\n            else {\n                this.eachLayer(function (layer) {\n                    layer.afterBrush && layer.afterBrush();\n                });\n            }\n        };\n        CanvasPainter.prototype._compositeManually = function () {\n            var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;\n            var width = this._domRoot.width;\n            var height = this._domRoot.height;\n            ctx.clearRect(0, 0, width, height);\n            this.eachBuiltinLayer(function (layer) {\n                if (layer.virtual) {\n                    ctx.drawImage(layer.dom, 0, 0, width, height);\n                }\n            });\n        };\n        CanvasPainter.prototype._doPaintList = function (list, prevList, paintAll) {\n            var _this = this;\n            var layerList = [];\n            var useDirtyRect = this._opts.useDirtyRect;\n            for (var zi = 0; zi < this._zlevelList.length; zi++) {\n                var zlevel = this._zlevelList[zi];\n                var layer = this._layers[zlevel];\n                if (layer.__builtin__\n                    && layer !== this._hoverlayer\n                    && (layer.__dirty || paintAll)) {\n                    layerList.push(layer);\n                }\n            }\n            var finished = true;\n            var needsRefreshHover = false;\n            var _loop_1 = function (k) {\n                var layer = layerList[k];\n                var ctx = layer.ctx;\n                var repaintRects = useDirtyRect\n                    && layer.createRepaintRects(list, prevList, this_1._width, this_1._height);\n                var start = paintAll ? layer.__startIndex : layer.__drawIndex;\n                var useTimer = !paintAll && layer.incremental && Date.now;\n                var startTime = useTimer && Date.now();\n                var clearColor = layer.zlevel === this_1._zlevelList[0]\n                    ? this_1._backgroundColor : null;\n                if (layer.__startIndex === layer.__endIndex) {\n                    layer.clear(false, clearColor, repaintRects);\n                }\n                else if (start === layer.__startIndex) {\n                    var firstEl = list[start];\n                    if (!firstEl.incremental || !firstEl.notClear || paintAll) {\n                        layer.clear(false, clearColor, repaintRects);\n                    }\n                }\n                if (start === -1) {\n                    console.error('For some unknown reason. drawIndex is -1');\n                    start = layer.__startIndex;\n                }\n                var i;\n                var repaint = function (repaintRect) {\n                    var scope = {\n                        inHover: false,\n                        allClipped: false,\n                        prevEl: null,\n                        viewWidth: _this._width,\n                        viewHeight: _this._height\n                    };\n                    for (i = start; i < layer.__endIndex; i++) {\n                        var el = list[i];\n                        if (el.__inHover) {\n                            needsRefreshHover = true;\n                        }\n                        _this._doPaintEl(el, layer, useDirtyRect, repaintRect, scope, i === layer.__endIndex - 1);\n                        if (useTimer) {\n                            var dTime = Date.now() - startTime;\n                            if (dTime > 15) {\n                                break;\n                            }\n                        }\n                    }\n                    if (scope.prevElClipPaths) {\n                        ctx.restore();\n                    }\n                };\n                if (repaintRects) {\n                    if (repaintRects.length === 0) {\n                        i = layer.__endIndex;\n                    }\n                    else {\n                        var dpr = this_1.dpr;\n                        for (var r = 0; r < repaintRects.length; ++r) {\n                            var rect = repaintRects[r];\n                            ctx.save();\n                            ctx.beginPath();\n                            ctx.rect(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n                            ctx.clip();\n                            repaint(rect);\n                            ctx.restore();\n                        }\n                    }\n                }\n                else {\n                    ctx.save();\n                    repaint();\n                    ctx.restore();\n                }\n                layer.__drawIndex = i;\n                if (layer.__drawIndex < layer.__endIndex) {\n                    finished = false;\n                }\n            };\n            var this_1 = this;\n            for (var k = 0; k < layerList.length; k++) {\n                _loop_1(k);\n            }\n            if (env.wxa) {\n                each(this._layers, function (layer) {\n                    if (layer && layer.ctx && layer.ctx.draw) {\n                        layer.ctx.draw();\n                    }\n                });\n            }\n            return {\n                finished: finished,\n                needsRefreshHover: needsRefreshHover\n            };\n        };\n        CanvasPainter.prototype._doPaintEl = function (el, currentLayer, useDirtyRect, repaintRect, scope, isLast) {\n            var ctx = currentLayer.ctx;\n            if (useDirtyRect) {\n                var paintRect = el.getPaintRect();\n                if (!repaintRect || paintRect && paintRect.intersect(repaintRect)) {\n                    brush(ctx, el, scope, isLast);\n                    el.setPrevPaintRect(paintRect);\n                }\n            }\n            else {\n                brush(ctx, el, scope, isLast);\n            }\n        };\n        CanvasPainter.prototype.getLayer = function (zlevel, virtual) {\n            if (this._singleCanvas && !this._needsManuallyCompositing) {\n                zlevel = CANVAS_ZLEVEL;\n            }\n            var layer = this._layers[zlevel];\n            if (!layer) {\n                layer = new Layer('zr_' + zlevel, this, this.dpr);\n                layer.zlevel = zlevel;\n                layer.__builtin__ = true;\n                if (this._layerConfig[zlevel]) {\n                    merge(layer, this._layerConfig[zlevel], true);\n                }\n                else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) {\n                    merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true);\n                }\n                if (virtual) {\n                    layer.virtual = virtual;\n                }\n                this.insertLayer(zlevel, layer);\n                layer.initContext();\n            }\n            return layer;\n        };\n        CanvasPainter.prototype.insertLayer = function (zlevel, layer) {\n            var layersMap = this._layers;\n            var zlevelList = this._zlevelList;\n            var len = zlevelList.length;\n            var domRoot = this._domRoot;\n            var prevLayer = null;\n            var i = -1;\n            if (layersMap[zlevel]) {\n                if (\"development\" !== 'production') {\n                    logError('ZLevel ' + zlevel + ' has been used already');\n                }\n                return;\n            }\n            if (!isLayerValid(layer)) {\n                if (\"development\" !== 'production') {\n                    logError('Layer of zlevel ' + zlevel + ' is not valid');\n                }\n                return;\n            }\n            if (len > 0 && zlevel > zlevelList[0]) {\n                for (i = 0; i < len - 1; i++) {\n                    if (zlevelList[i] < zlevel\n                        && zlevelList[i + 1] > zlevel) {\n                        break;\n                    }\n                }\n                prevLayer = layersMap[zlevelList[i]];\n            }\n            zlevelList.splice(i + 1, 0, zlevel);\n            layersMap[zlevel] = layer;\n            if (!layer.virtual) {\n                if (prevLayer) {\n                    var prevDom = prevLayer.dom;\n                    if (prevDom.nextSibling) {\n                        domRoot.insertBefore(layer.dom, prevDom.nextSibling);\n                    }\n                    else {\n                        domRoot.appendChild(layer.dom);\n                    }\n                }\n                else {\n                    if (domRoot.firstChild) {\n                        domRoot.insertBefore(layer.dom, domRoot.firstChild);\n                    }\n                    else {\n                        domRoot.appendChild(layer.dom);\n                    }\n                }\n            }\n            layer.__painter = this;\n        };\n        CanvasPainter.prototype.eachLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                cb.call(context, this._layers[z], z);\n            }\n        };\n        CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (layer.__builtin__) {\n                    cb.call(context, layer, z);\n                }\n            }\n        };\n        CanvasPainter.prototype.eachOtherLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (!layer.__builtin__) {\n                    cb.call(context, layer, z);\n                }\n            }\n        };\n        CanvasPainter.prototype.getLayers = function () {\n            return this._layers;\n        };\n        CanvasPainter.prototype._updateLayerStatus = function (list) {\n            this.eachBuiltinLayer(function (layer, z) {\n                layer.__dirty = layer.__used = false;\n            });\n            function updatePrevLayer(idx) {\n                if (prevLayer) {\n                    if (prevLayer.__endIndex !== idx) {\n                        prevLayer.__dirty = true;\n                    }\n                    prevLayer.__endIndex = idx;\n                }\n            }\n            if (this._singleCanvas) {\n                for (var i_1 = 1; i_1 < list.length; i_1++) {\n                    var el = list[i_1];\n                    if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) {\n                        this._needsManuallyCompositing = true;\n                        break;\n                    }\n                }\n            }\n            var prevLayer = null;\n            var incrementalLayerCount = 0;\n            var prevZlevel;\n            var i;\n            for (i = 0; i < list.length; i++) {\n                var el = list[i];\n                var zlevel = el.zlevel;\n                var layer = void 0;\n                if (prevZlevel !== zlevel) {\n                    prevZlevel = zlevel;\n                    incrementalLayerCount = 0;\n                }\n                if (el.incremental) {\n                    layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);\n                    layer.incremental = true;\n                    incrementalLayerCount = 1;\n                }\n                else {\n                    layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);\n                }\n                if (!layer.__builtin__) {\n                    logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);\n                }\n                if (layer !== prevLayer) {\n                    layer.__used = true;\n                    if (layer.__startIndex !== i) {\n                        layer.__dirty = true;\n                    }\n                    layer.__startIndex = i;\n                    if (!layer.incremental) {\n                        layer.__drawIndex = i;\n                    }\n                    else {\n                        layer.__drawIndex = -1;\n                    }\n                    updatePrevLayer(i);\n                    prevLayer = layer;\n                }\n                if ((el.__dirty & REDRAW_BIT) && !el.__inHover) {\n                    layer.__dirty = true;\n                    if (layer.incremental && layer.__drawIndex < 0) {\n                        layer.__drawIndex = i;\n                    }\n                }\n            }\n            updatePrevLayer(i);\n            this.eachBuiltinLayer(function (layer, z) {\n                if (!layer.__used && layer.getElementCount() > 0) {\n                    layer.__dirty = true;\n                    layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;\n                }\n                if (layer.__dirty && layer.__drawIndex < 0) {\n                    layer.__drawIndex = layer.__startIndex;\n                }\n            });\n        };\n        CanvasPainter.prototype.clear = function () {\n            this.eachBuiltinLayer(this._clearLayer);\n            return this;\n        };\n        CanvasPainter.prototype._clearLayer = function (layer) {\n            layer.clear();\n        };\n        CanvasPainter.prototype.setBackgroundColor = function (backgroundColor) {\n            this._backgroundColor = backgroundColor;\n            each(this._layers, function (layer) {\n                layer.setUnpainted();\n            });\n        };\n        CanvasPainter.prototype.configLayer = function (zlevel, config) {\n            if (config) {\n                var layerConfig = this._layerConfig;\n                if (!layerConfig[zlevel]) {\n                    layerConfig[zlevel] = config;\n                }\n                else {\n                    merge(layerConfig[zlevel], config, true);\n                }\n                for (var i = 0; i < this._zlevelList.length; i++) {\n                    var _zlevel = this._zlevelList[i];\n                    if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {\n                        var layer = this._layers[_zlevel];\n                        merge(layer, layerConfig[zlevel], true);\n                    }\n                }\n            }\n        };\n        CanvasPainter.prototype.delLayer = function (zlevel) {\n            var layers = this._layers;\n            var zlevelList = this._zlevelList;\n            var layer = layers[zlevel];\n            if (!layer) {\n                return;\n            }\n            layer.dom.parentNode.removeChild(layer.dom);\n            delete layers[zlevel];\n            zlevelList.splice(indexOf(zlevelList, zlevel), 1);\n        };\n        CanvasPainter.prototype.resize = function (width, height) {\n            if (!this._domRoot.style) {\n                if (width == null || height == null) {\n                    return;\n                }\n                this._width = width;\n                this._height = height;\n                this.getLayer(CANVAS_ZLEVEL).resize(width, height);\n            }\n            else {\n                var domRoot = this._domRoot;\n                domRoot.style.display = 'none';\n                var opts = this._opts;\n                var root = this.root;\n                width != null && (opts.width = width);\n                height != null && (opts.height = height);\n                width = getSize(root, 0, opts);\n                height = getSize(root, 1, opts);\n                domRoot.style.display = '';\n                if (this._width !== width || height !== this._height) {\n                    domRoot.style.width = width + 'px';\n                    domRoot.style.height = height + 'px';\n                    for (var id in this._layers) {\n                        if (this._layers.hasOwnProperty(id)) {\n                            this._layers[id].resize(width, height);\n                        }\n                    }\n                    this.refresh(true);\n                }\n                this._width = width;\n                this._height = height;\n            }\n            return this;\n        };\n        CanvasPainter.prototype.clearLayer = function (zlevel) {\n            var layer = this._layers[zlevel];\n            if (layer) {\n                layer.clear();\n            }\n        };\n        CanvasPainter.prototype.dispose = function () {\n            this.root.innerHTML = '';\n            this.root =\n                this.storage =\n                    this._domRoot =\n                        this._layers = null;\n        };\n        CanvasPainter.prototype.getRenderedCanvas = function (opts) {\n            opts = opts || {};\n            if (this._singleCanvas && !this._compositeManually) {\n                return this._layers[CANVAS_ZLEVEL].dom;\n            }\n            var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);\n            imageLayer.initContext();\n            imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);\n            var ctx = imageLayer.ctx;\n            if (opts.pixelRatio <= this.dpr) {\n                this.refresh();\n                var width_1 = imageLayer.dom.width;\n                var height_1 = imageLayer.dom.height;\n                this.eachLayer(function (layer) {\n                    if (layer.__builtin__) {\n                        ctx.drawImage(layer.dom, 0, 0, width_1, height_1);\n                    }\n                    else if (layer.renderToCanvas) {\n                        ctx.save();\n                        layer.renderToCanvas(ctx);\n                        ctx.restore();\n                    }\n                });\n            }\n            else {\n                var scope = {\n                    inHover: false,\n                    viewWidth: this._width,\n                    viewHeight: this._height\n                };\n                var displayList = this.storage.getDisplayList(true);\n                for (var i = 0, len = displayList.length; i < len; i++) {\n                    var el = displayList[i];\n                    brush(ctx, el, scope, i === len - 1);\n                }\n            }\n            return imageLayer.dom;\n        };\n        CanvasPainter.prototype.getWidth = function () {\n            return this._width;\n        };\n        CanvasPainter.prototype.getHeight = function () {\n            return this._height;\n        };\n        return CanvasPainter;\n    }());\n\n    function install$1(registers) {\n      registers.registerPainter('canvas', CanvasPainter);\n    }\n\n    var LineSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(LineSeriesModel, _super);\n\n      function LineSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LineSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      }\n\n      LineSeriesModel.prototype.getInitialData = function (option) {\n        if (\"development\" !== 'production') {\n          var coordSys = option.coordinateSystem;\n\n          if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {\n            throw new Error('Line not support coordinateSystem besides cartesian and polar');\n          }\n        }\n\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      LineSeriesModel.prototype.getLegendIcon = function (opt) {\n        var group = new Group();\n        var line = createSymbol('line', 0, opt.itemHeight / 2, opt.itemWidth, 0, opt.lineStyle.stroke, false);\n        group.add(line);\n        line.setStyle(opt.lineStyle);\n        var visualType = this.getData().getVisual('symbol');\n        var visualRotate = this.getData().getVisual('symbolRotate');\n        var symbolType = visualType === 'none' ? 'circle' : visualType; // Symbol size is 80% when there is a line\n\n        var size = opt.itemHeight * 0.8;\n        var symbol = createSymbol(symbolType, (opt.itemWidth - size) / 2, (opt.itemHeight - size) / 2, size, size, opt.itemStyle.fill);\n        group.add(symbol);\n        symbol.setStyle(opt.itemStyle);\n        var symbolRotate = opt.iconRotate === 'inherit' ? visualRotate : opt.iconRotate || 0;\n        symbol.rotation = symbolRotate * Math.PI / 180;\n        symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);\n\n        if (symbolType.indexOf('empty') > -1) {\n          symbol.style.stroke = symbol.style.fill;\n          symbol.style.fill = '#fff';\n          symbol.style.lineWidth = 2;\n        }\n\n        return group;\n      };\n\n      LineSeriesModel.type = 'series.line';\n      LineSeriesModel.dependencies = ['grid', 'polar'];\n      LineSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 3,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        clip: true,\n        label: {\n          position: 'top'\n        },\n        // itemStyle: {\n        // },\n        endLabel: {\n          show: false,\n          valueAnimation: true,\n          distance: 8\n        },\n        lineStyle: {\n          width: 2,\n          type: 'solid'\n        },\n        emphasis: {\n          scale: true\n        },\n        // areaStyle: {\n        // origin of areaStyle. Valid values:\n        // `'auto'/null/undefined`: from axisLine to data\n        // `'start'`: from min to data\n        // `'end'`: from data to max\n        // origin: 'auto'\n        // },\n        // false, 'start', 'end', 'middle'\n        step: false,\n        // Disabled if step is true\n        smooth: false,\n        smoothMonotone: null,\n        symbol: 'emptyCircle',\n        symbolSize: 4,\n        symbolRotate: null,\n        showSymbol: true,\n        // `false`: follow the label interval strategy.\n        // `true`: show all symbols.\n        // `'auto'`: If possible, show all symbols, otherwise\n        //           follow the label interval strategy.\n        showAllSymbol: 'auto',\n        // Whether to connect break point.\n        connectNulls: false,\n        // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'.\n        sampling: 'none',\n        animationEasing: 'linear',\n        // Disable progressive\n        progressive: 0,\n        hoverLayerThreshold: Infinity,\n        universalTransition: {\n          divideShape: 'clone'\n        },\n        triggerLineEvent: false\n      };\n      return LineSeriesModel;\n    }(SeriesModel);\n\n    /**\n     * @return label string. Not null/undefined\n     */\n\n    function getDefaultLabel(data, dataIndex) {\n      var labelDims = data.mapDimensionsAll('defaultedLabel');\n      var len = labelDims.length; // Simple optimization (in lots of cases, label dims length is 1)\n\n      if (len === 1) {\n        var rawVal = retrieveRawValue(data, dataIndex, labelDims[0]);\n        return rawVal != null ? rawVal + '' : null;\n      } else if (len) {\n        var vals = [];\n\n        for (var i = 0; i < labelDims.length; i++) {\n          vals.push(retrieveRawValue(data, dataIndex, labelDims[i]));\n        }\n\n        return vals.join(' ');\n      }\n    }\n    function getDefaultInterpolatedLabel(data, interpolatedValue) {\n      var labelDims = data.mapDimensionsAll('defaultedLabel');\n\n      if (!isArray(interpolatedValue)) {\n        return interpolatedValue + '';\n      }\n\n      var vals = [];\n\n      for (var i = 0; i < labelDims.length; i++) {\n        var dimIndex = data.getDimensionIndex(labelDims[i]);\n\n        if (dimIndex >= 0) {\n          vals.push(interpolatedValue[dimIndex]);\n        }\n      }\n\n      return vals.join(' ');\n    }\n\n    var Symbol =\n    /** @class */\n    function (_super) {\n      __extends(Symbol, _super);\n\n      function Symbol(data, idx, seriesScope, opts) {\n        var _this = _super.call(this) || this;\n\n        _this.updateData(data, idx, seriesScope, opts);\n\n        return _this;\n      }\n\n      Symbol.prototype._createSymbol = function (symbolType, data, idx, symbolSize, keepAspect) {\n        // Remove paths created before\n        this.removeAll(); // let symbolPath = createSymbol(\n        //     symbolType, -0.5, -0.5, 1, 1, color\n        // );\n        // If width/height are set too small (e.g., set to 1) on ios10\n        // and macOS Sierra, a circle stroke become a rect, no matter what\n        // the scale is set. So we set width/height as 2. See #4150.\n\n        var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, null, keepAspect);\n        symbolPath.attr({\n          z2: 100,\n          culling: true,\n          scaleX: symbolSize[0] / 2,\n          scaleY: symbolSize[1] / 2\n        }); // Rewrite drift method\n\n        symbolPath.drift = driftSymbol;\n        this._symbolType = symbolType;\n        this.add(symbolPath);\n      };\n      /**\n       * Stop animation\n       * @param {boolean} toLastFrame\n       */\n\n\n      Symbol.prototype.stopSymbolAnimation = function (toLastFrame) {\n        this.childAt(0).stopAnimation(null, toLastFrame);\n      };\n\n      Symbol.prototype.getSymbolType = function () {\n        return this._symbolType;\n      };\n      /**\n       * FIXME:\n       * Caution: This method breaks the encapsulation of this module,\n       * but it indeed brings convenience. So do not use the method\n       * unless you detailedly know all the implements of `Symbol`,\n       * especially animation.\n       *\n       * Get symbol path element.\n       */\n\n\n      Symbol.prototype.getSymbolPath = function () {\n        return this.childAt(0);\n      };\n      /**\n       * Highlight symbol\n       */\n\n\n      Symbol.prototype.highlight = function () {\n        enterEmphasis(this.childAt(0));\n      };\n      /**\n       * Downplay symbol\n       */\n\n\n      Symbol.prototype.downplay = function () {\n        leaveEmphasis(this.childAt(0));\n      };\n      /**\n       * @param {number} zlevel\n       * @param {number} z\n       */\n\n\n      Symbol.prototype.setZ = function (zlevel, z) {\n        var symbolPath = this.childAt(0);\n        symbolPath.zlevel = zlevel;\n        symbolPath.z = z;\n      };\n\n      Symbol.prototype.setDraggable = function (draggable, hasCursorOption) {\n        var symbolPath = this.childAt(0);\n        symbolPath.draggable = draggable;\n        symbolPath.cursor = !hasCursorOption && draggable ? 'move' : symbolPath.cursor;\n      };\n      /**\n       * Update symbol properties\n       */\n\n\n      Symbol.prototype.updateData = function (data, idx, seriesScope, opts) {\n        this.silent = false;\n        var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';\n        var seriesModel = data.hostModel;\n        var symbolSize = Symbol.getSymbolSize(data, idx);\n        var isInit = symbolType !== this._symbolType;\n        var disableAnimation = opts && opts.disableAnimation;\n\n        if (isInit) {\n          var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect');\n\n          this._createSymbol(symbolType, data, idx, symbolSize, keepAspect);\n        } else {\n          var symbolPath = this.childAt(0);\n          symbolPath.silent = false;\n          var target = {\n            scaleX: symbolSize[0] / 2,\n            scaleY: symbolSize[1] / 2\n          };\n          disableAnimation ? symbolPath.attr(target) : updateProps(symbolPath, target, seriesModel, idx);\n          saveOldStyle(symbolPath);\n        }\n\n        this._updateCommon(data, idx, symbolSize, seriesScope, opts);\n\n        if (isInit) {\n          var symbolPath = this.childAt(0);\n\n          if (!disableAnimation) {\n            var target = {\n              scaleX: this._sizeX,\n              scaleY: this._sizeY,\n              style: {\n                // Always fadeIn. Because it has fadeOut animation when symbol is removed..\n                opacity: symbolPath.style.opacity\n              }\n            };\n            symbolPath.scaleX = symbolPath.scaleY = 0;\n            symbolPath.style.opacity = 0;\n            initProps(symbolPath, target, seriesModel, idx);\n          }\n        }\n\n        if (disableAnimation) {\n          // Must stop leave transition manually if don't call initProps or updateProps.\n          this.childAt(0).stopAnimation('leave');\n        }\n      };\n\n      Symbol.prototype._updateCommon = function (data, idx, symbolSize, seriesScope, opts) {\n        var symbolPath = this.childAt(0);\n        var seriesModel = data.hostModel;\n        var emphasisItemStyle;\n        var blurItemStyle;\n        var selectItemStyle;\n        var focus;\n        var blurScope;\n        var emphasisDisabled;\n        var labelStatesModels;\n        var hoverScale;\n        var cursorStyle;\n\n        if (seriesScope) {\n          emphasisItemStyle = seriesScope.emphasisItemStyle;\n          blurItemStyle = seriesScope.blurItemStyle;\n          selectItemStyle = seriesScope.selectItemStyle;\n          focus = seriesScope.focus;\n          blurScope = seriesScope.blurScope;\n          labelStatesModels = seriesScope.labelStatesModels;\n          hoverScale = seriesScope.hoverScale;\n          cursorStyle = seriesScope.cursorStyle;\n          emphasisDisabled = seriesScope.emphasisDisabled;\n        }\n\n        if (!seriesScope || data.hasItemOption) {\n          var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx);\n          var emphasisModel = itemModel.getModel('emphasis');\n          emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n          selectItemStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();\n          blurItemStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n          focus = emphasisModel.get('focus');\n          blurScope = emphasisModel.get('blurScope');\n          emphasisDisabled = emphasisModel.get('disabled');\n          labelStatesModels = getLabelStatesModels(itemModel);\n          hoverScale = emphasisModel.getShallow('scale');\n          cursorStyle = itemModel.getShallow('cursor');\n        }\n\n        var symbolRotate = data.getItemVisual(idx, 'symbolRotate');\n        symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);\n        var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize);\n\n        if (symbolOffset) {\n          symbolPath.x = symbolOffset[0];\n          symbolPath.y = symbolOffset[1];\n        }\n\n        cursorStyle && symbolPath.attr('cursor', cursorStyle);\n        var symbolStyle = data.getItemVisual(idx, 'style');\n        var visualColor = symbolStyle.fill;\n\n        if (symbolPath instanceof ZRImage) {\n          var pathStyle = symbolPath.style;\n          symbolPath.useStyle(extend({\n            // TODO other properties like x, y ?\n            image: pathStyle.image,\n            x: pathStyle.x,\n            y: pathStyle.y,\n            width: pathStyle.width,\n            height: pathStyle.height\n          }, symbolStyle));\n        } else {\n          if (symbolPath.__isEmptyBrush) {\n            // fill and stroke will be swapped if it's empty.\n            // So we cloned a new style to avoid it affecting the original style in visual storage.\n            // TODO Better implementation. No empty logic!\n            symbolPath.useStyle(extend({}, symbolStyle));\n          } else {\n            symbolPath.useStyle(symbolStyle);\n          } // Disable decal because symbol scale will been applied on the decal.\n\n\n          symbolPath.style.decal = null;\n          symbolPath.setColor(visualColor, opts && opts.symbolInnerColor);\n          symbolPath.style.strokeNoScale = true;\n        }\n\n        var liftZ = data.getItemVisual(idx, 'liftZ');\n        var z2Origin = this._z2;\n\n        if (liftZ != null) {\n          if (z2Origin == null) {\n            this._z2 = symbolPath.z2;\n            symbolPath.z2 += liftZ;\n          }\n        } else if (z2Origin != null) {\n          symbolPath.z2 = z2Origin;\n          this._z2 = null;\n        }\n\n        var useNameLabel = opts && opts.useNameLabel;\n        setLabelStyle(symbolPath, labelStatesModels, {\n          labelFetcher: seriesModel,\n          labelDataIndex: idx,\n          defaultText: getLabelDefaultText,\n          inheritColor: visualColor,\n          defaultOpacity: symbolStyle.opacity\n        }); // Do not execute util needed.\n\n        function getLabelDefaultText(idx) {\n          return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);\n        }\n\n        this._sizeX = symbolSize[0] / 2;\n        this._sizeY = symbolSize[1] / 2;\n        var emphasisState = symbolPath.ensureState('emphasis');\n        emphasisState.style = emphasisItemStyle;\n        symbolPath.ensureState('select').style = selectItemStyle;\n        symbolPath.ensureState('blur').style = blurItemStyle; // null / undefined / true means to use default strategy.\n        // 0 / false / negative number / NaN / Infinity means no scale.\n\n        var scaleRatio = hoverScale == null || hoverScale === true ? Math.max(1.1, 3 / this._sizeY) // PENDING: restrict hoverScale > 1? It seems unreasonable to scale down\n        : isFinite(hoverScale) && hoverScale > 0 ? +hoverScale : 1; // always set scale to allow resetting\n\n        emphasisState.scaleX = this._sizeX * scaleRatio;\n        emphasisState.scaleY = this._sizeY * scaleRatio;\n        this.setSymbolScale(1);\n        toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n      };\n\n      Symbol.prototype.setSymbolScale = function (scale) {\n        this.scaleX = this.scaleY = scale;\n      };\n\n      Symbol.prototype.fadeOut = function (cb, seriesModel, opt) {\n        var symbolPath = this.childAt(0);\n        var dataIndex = getECData(this).dataIndex;\n        var animationOpt = opt && opt.animation; // Avoid mistaken hover when fading out\n\n        this.silent = symbolPath.silent = true; // Not show text when animating\n\n        if (opt && opt.fadeLabel) {\n          var textContent = symbolPath.getTextContent();\n\n          if (textContent) {\n            removeElement(textContent, {\n              style: {\n                opacity: 0\n              }\n            }, seriesModel, {\n              dataIndex: dataIndex,\n              removeOpt: animationOpt,\n              cb: function () {\n                symbolPath.removeTextContent();\n              }\n            });\n          }\n        } else {\n          symbolPath.removeTextContent();\n        }\n\n        removeElement(symbolPath, {\n          style: {\n            opacity: 0\n          },\n          scaleX: 0,\n          scaleY: 0\n        }, seriesModel, {\n          dataIndex: dataIndex,\n          cb: cb,\n          removeOpt: animationOpt\n        });\n      };\n\n      Symbol.getSymbolSize = function (data, idx) {\n        return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n      };\n\n      return Symbol;\n    }(Group);\n\n    function driftSymbol(dx, dy) {\n      this.parent.drift(dx, dy);\n    }\n\n    function symbolNeedsDraw(data, point, idx, opt) {\n      return point && !isNaN(point[0]) && !isNaN(point[1]) && !(opt.isIgnore && opt.isIgnore(idx)) // We do not set clipShape on group, because it will cut part of\n      // the symbol element shape. We use the same clip shape here as\n      // the line clip.\n      && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) && data.getItemVisual(idx, 'symbol') !== 'none';\n    }\n\n    function normalizeUpdateOpt(opt) {\n      if (opt != null && !isObject(opt)) {\n        opt = {\n          isIgnore: opt\n        };\n      }\n\n      return opt || {};\n    }\n\n    function makeSeriesScope(data) {\n      var seriesModel = data.hostModel;\n      var emphasisModel = seriesModel.getModel('emphasis');\n      return {\n        emphasisItemStyle: emphasisModel.getModel('itemStyle').getItemStyle(),\n        blurItemStyle: seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(),\n        selectItemStyle: seriesModel.getModel(['select', 'itemStyle']).getItemStyle(),\n        focus: emphasisModel.get('focus'),\n        blurScope: emphasisModel.get('blurScope'),\n        emphasisDisabled: emphasisModel.get('disabled'),\n        hoverScale: emphasisModel.get('scale'),\n        labelStatesModels: getLabelStatesModels(seriesModel),\n        cursorStyle: seriesModel.get('cursor')\n      };\n    }\n\n    var SymbolDraw =\n    /** @class */\n    function () {\n      function SymbolDraw(SymbolCtor) {\n        this.group = new Group();\n        this._SymbolCtor = SymbolCtor || Symbol;\n      }\n      /**\n       * Update symbols draw by new data\n       */\n\n\n      SymbolDraw.prototype.updateData = function (data, opt) {\n        // Remove progressive els.\n        this._progressiveEls = null;\n        opt = normalizeUpdateOpt(opt);\n        var group = this.group;\n        var seriesModel = data.hostModel;\n        var oldData = this._data;\n        var SymbolCtor = this._SymbolCtor;\n        var disableAnimation = opt.disableAnimation;\n        var seriesScope = makeSeriesScope(data);\n        var symbolUpdateOpt = {\n          disableAnimation: disableAnimation\n        };\n\n        var getSymbolPoint = opt.getSymbolPoint || function (idx) {\n          return data.getItemLayout(idx);\n        }; // There is no oldLineData only when first rendering or switching from\n        // stream mode to normal mode, where previous elements should be removed.\n\n\n        if (!oldData) {\n          group.removeAll();\n        }\n\n        data.diff(oldData).add(function (newIdx) {\n          var point = getSymbolPoint(newIdx);\n\n          if (symbolNeedsDraw(data, point, newIdx, opt)) {\n            var symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n            symbolEl.setPosition(point);\n            data.setItemGraphicEl(newIdx, symbolEl);\n            group.add(symbolEl);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var symbolEl = oldData.getItemGraphicEl(oldIdx);\n          var point = getSymbolPoint(newIdx);\n\n          if (!symbolNeedsDraw(data, point, newIdx, opt)) {\n            group.remove(symbolEl);\n            return;\n          }\n\n          var newSymbolType = data.getItemVisual(newIdx, 'symbol') || 'circle';\n          var oldSymbolType = symbolEl && symbolEl.getSymbolType && symbolEl.getSymbolType();\n\n          if (!symbolEl // Create a new if symbol type changed.\n          || oldSymbolType && oldSymbolType !== newSymbolType) {\n            group.remove(symbolEl);\n            symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n            symbolEl.setPosition(point);\n          } else {\n            symbolEl.updateData(data, newIdx, seriesScope, symbolUpdateOpt);\n            var target = {\n              x: point[0],\n              y: point[1]\n            };\n            disableAnimation ? symbolEl.attr(target) : updateProps(symbolEl, target, seriesModel);\n          } // Add back\n\n\n          group.add(symbolEl);\n          data.setItemGraphicEl(newIdx, symbolEl);\n        }).remove(function (oldIdx) {\n          var el = oldData.getItemGraphicEl(oldIdx);\n          el && el.fadeOut(function () {\n            group.remove(el);\n          }, seriesModel);\n        }).execute();\n        this._getSymbolPoint = getSymbolPoint;\n        this._data = data;\n      };\n\n      SymbolDraw.prototype.updateLayout = function () {\n        var _this = this;\n\n        var data = this._data;\n\n        if (data) {\n          // Not use animation\n          data.eachItemGraphicEl(function (el, idx) {\n            var point = _this._getSymbolPoint(idx);\n\n            el.setPosition(point);\n            el.markRedraw();\n          });\n        }\n      };\n\n      SymbolDraw.prototype.incrementalPrepareUpdate = function (data) {\n        this._seriesScope = makeSeriesScope(data);\n        this._data = null;\n        this.group.removeAll();\n      };\n      /**\n       * Update symbols draw by new data\n       */\n\n      SymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {\n        // Clear\n        this._progressiveEls = [];\n        opt = normalizeUpdateOpt(opt);\n\n        function updateIncrementalAndHover(el) {\n          if (!el.isGroup) {\n            el.incremental = true;\n            el.ensureState('emphasis').hoverLayer = true;\n          }\n        }\n\n        for (var idx = taskParams.start; idx < taskParams.end; idx++) {\n          var point = data.getItemLayout(idx);\n\n          if (symbolNeedsDraw(data, point, idx, opt)) {\n            var el = new this._SymbolCtor(data, idx, this._seriesScope);\n            el.traverse(updateIncrementalAndHover);\n            el.setPosition(point);\n            this.group.add(el);\n            data.setItemGraphicEl(idx, el);\n\n            this._progressiveEls.push(el);\n          }\n        }\n      };\n\n      SymbolDraw.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      SymbolDraw.prototype.remove = function (enableAnimation) {\n        var group = this.group;\n        var data = this._data; // Incremental model do not have this._data.\n\n        if (data && enableAnimation) {\n          data.eachItemGraphicEl(function (el) {\n            el.fadeOut(function () {\n              group.remove(el);\n            }, data.hostModel);\n          });\n        } else {\n          group.removeAll();\n        }\n      };\n      return SymbolDraw;\n    }();\n\n    function prepareDataCoordInfo(coordSys, data, valueOrigin) {\n      var baseAxis = coordSys.getBaseAxis();\n      var valueAxis = coordSys.getOtherAxis(baseAxis);\n      var valueStart = getValueStart(valueAxis, valueOrigin);\n      var baseAxisDim = baseAxis.dim;\n      var valueAxisDim = valueAxis.dim;\n      var valueDim = data.mapDimension(valueAxisDim);\n      var baseDim = data.mapDimension(baseAxisDim);\n      var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;\n      var dims = map(coordSys.dimensions, function (coordDim) {\n        return data.mapDimension(coordDim);\n      });\n      var stacked = false;\n      var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n      if (isDimensionStacked(data, dims[0]\n      /* , dims[1] */\n      )) {\n        // jshint ignore:line\n        stacked = true;\n        dims[0] = stackResultDim;\n      }\n\n      if (isDimensionStacked(data, dims[1]\n      /* , dims[0] */\n      )) {\n        // jshint ignore:line\n        stacked = true;\n        dims[1] = stackResultDim;\n      }\n\n      return {\n        dataDimsForPoint: dims,\n        valueStart: valueStart,\n        valueAxisDim: valueAxisDim,\n        baseAxisDim: baseAxisDim,\n        stacked: !!stacked,\n        valueDim: valueDim,\n        baseDim: baseDim,\n        baseDataOffset: baseDataOffset,\n        stackedOverDimension: data.getCalculationInfo('stackedOverDimension')\n      };\n    }\n\n    function getValueStart(valueAxis, valueOrigin) {\n      var valueStart = 0;\n      var extent = valueAxis.scale.getExtent();\n\n      if (valueOrigin === 'start') {\n        valueStart = extent[0];\n      } else if (valueOrigin === 'end') {\n        valueStart = extent[1];\n      } // If origin is specified as a number, use it as\n      // valueStart directly\n      else if (isNumber(valueOrigin) && !isNaN(valueOrigin)) {\n          valueStart = valueOrigin;\n        } // auto\n        else {\n            // Both positive\n            if (extent[0] > 0) {\n              valueStart = extent[0];\n            } // Both negative\n            else if (extent[1] < 0) {\n                valueStart = extent[1];\n              } // If is one positive, and one negative, onZero shall be true\n\n          }\n\n      return valueStart;\n    }\n\n    function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) {\n      var value = NaN;\n\n      if (dataCoordInfo.stacked) {\n        value = data.get(data.getCalculationInfo('stackedOverDimension'), idx);\n      }\n\n      if (isNaN(value)) {\n        value = dataCoordInfo.valueStart;\n      }\n\n      var baseDataOffset = dataCoordInfo.baseDataOffset;\n      var stackedData = [];\n      stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx);\n      stackedData[1 - baseDataOffset] = value;\n      return coordSys.dataToPoint(stackedData);\n    }\n\n    function diffData(oldData, newData) {\n      var diffResult = [];\n      newData.diff(oldData).add(function (idx) {\n        diffResult.push({\n          cmd: '+',\n          idx: idx\n        });\n      }).update(function (newIdx, oldIdx) {\n        diffResult.push({\n          cmd: '=',\n          idx: oldIdx,\n          idx1: newIdx\n        });\n      }).remove(function (idx) {\n        diffResult.push({\n          cmd: '-',\n          idx: idx\n        });\n      }).execute();\n      return diffResult;\n    }\n\n    function lineAnimationDiff(oldData, newData, oldStackedOnPoints, newStackedOnPoints, oldCoordSys, newCoordSys, oldValueOrigin, newValueOrigin) {\n      var diff = diffData(oldData, newData); // let newIdList = newData.mapArray(newData.getId);\n      // let oldIdList = oldData.mapArray(oldData.getId);\n      // convertToIntId(newIdList, oldIdList);\n      // // FIXME One data ?\n      // diff = arrayDiff(oldIdList, newIdList);\n\n      var currPoints = [];\n      var nextPoints = []; // Points for stacking base line\n\n      var currStackedPoints = [];\n      var nextStackedPoints = [];\n      var status = [];\n      var sortedIndices = [];\n      var rawIndices = [];\n      var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); // const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin);\n\n      var oldPoints = oldData.getLayout('points') || [];\n      var newPoints = newData.getLayout('points') || [];\n\n      for (var i = 0; i < diff.length; i++) {\n        var diffItem = diff[i];\n        var pointAdded = true;\n        var oldIdx2 = void 0;\n        var newIdx2 = void 0; // FIXME, animation is not so perfect when dataZoom window moves fast\n        // Which is in case remvoing or add more than one data in the tail or head\n\n        switch (diffItem.cmd) {\n          case '=':\n            oldIdx2 = diffItem.idx * 2;\n            newIdx2 = diffItem.idx1 * 2;\n            var currentX = oldPoints[oldIdx2];\n            var currentY = oldPoints[oldIdx2 + 1];\n            var nextX = newPoints[newIdx2];\n            var nextY = newPoints[newIdx2 + 1]; // If previous data is NaN, use next point directly\n\n            if (isNaN(currentX) || isNaN(currentY)) {\n              currentX = nextX;\n              currentY = nextY;\n            }\n\n            currPoints.push(currentX, currentY);\n            nextPoints.push(nextX, nextY);\n            currStackedPoints.push(oldStackedOnPoints[oldIdx2], oldStackedOnPoints[oldIdx2 + 1]);\n            nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n            rawIndices.push(newData.getRawIndex(diffItem.idx1));\n            break;\n\n          case '+':\n            var newIdx = diffItem.idx;\n            var newDataDimsForPoint = newDataOldCoordInfo.dataDimsForPoint;\n            var oldPt = oldCoordSys.dataToPoint([newData.get(newDataDimsForPoint[0], newIdx), newData.get(newDataDimsForPoint[1], newIdx)]);\n            newIdx2 = newIdx * 2;\n            currPoints.push(oldPt[0], oldPt[1]);\n            nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1]);\n            var stackedOnPoint = getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, newIdx);\n            currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1]);\n            nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n            rawIndices.push(newData.getRawIndex(newIdx));\n            break;\n\n          case '-':\n            pointAdded = false;\n        } // Original indices\n\n\n        if (pointAdded) {\n          status.push(diffItem);\n          sortedIndices.push(sortedIndices.length);\n        }\n      } // Diff result may be crossed if all items are changed\n      // Sort by data index\n\n\n      sortedIndices.sort(function (a, b) {\n        return rawIndices[a] - rawIndices[b];\n      });\n      var len = currPoints.length;\n      var sortedCurrPoints = createFloat32Array(len);\n      var sortedNextPoints = createFloat32Array(len);\n      var sortedCurrStackedPoints = createFloat32Array(len);\n      var sortedNextStackedPoints = createFloat32Array(len);\n      var sortedStatus = [];\n\n      for (var i = 0; i < sortedIndices.length; i++) {\n        var idx = sortedIndices[i];\n        var i2 = i * 2;\n        var idx2 = idx * 2;\n        sortedCurrPoints[i2] = currPoints[idx2];\n        sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1];\n        sortedNextPoints[i2] = nextPoints[idx2];\n        sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1];\n        sortedCurrStackedPoints[i2] = currStackedPoints[idx2];\n        sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1];\n        sortedNextStackedPoints[i2] = nextStackedPoints[idx2];\n        sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1];\n        sortedStatus[i] = status[idx];\n      }\n\n      return {\n        current: sortedCurrPoints,\n        next: sortedNextPoints,\n        stackedOnCurrent: sortedCurrStackedPoints,\n        stackedOnNext: sortedNextStackedPoints,\n        status: sortedStatus\n      };\n    }\n\n    var mathMin$5 = Math.min;\n    var mathMax$5 = Math.max;\n\n    function isPointNull(x, y) {\n      return isNaN(x) || isNaN(y);\n    }\n    /**\n     * Draw smoothed line in non-monotone, in may cause undesired curve in extreme\n     * situations. This should be used when points are non-monotone neither in x or\n     * y dimension.\n     */\n\n\n    function drawSegment(ctx, points, start, segLen, allLen, dir, smooth, smoothMonotone, connectNulls) {\n      var prevX;\n      var prevY;\n      var cpx0;\n      var cpy0;\n      var cpx1;\n      var cpy1;\n      var idx = start;\n      var k = 0;\n\n      for (; k < segLen; k++) {\n        var x = points[idx * 2];\n        var y = points[idx * 2 + 1];\n\n        if (idx >= allLen || idx < 0) {\n          break;\n        }\n\n        if (isPointNull(x, y)) {\n          if (connectNulls) {\n            idx += dir;\n            continue;\n          }\n\n          break;\n        }\n\n        if (idx === start) {\n          ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y);\n          cpx0 = x;\n          cpy0 = y;\n        } else {\n          var dx = x - prevX;\n          var dy = y - prevY; // Ignore tiny segment.\n\n          if (dx * dx + dy * dy < 0.5) {\n            idx += dir;\n            continue;\n          }\n\n          if (smooth > 0) {\n            var nextIdx = idx + dir;\n            var nextX = points[nextIdx * 2];\n            var nextY = points[nextIdx * 2 + 1]; // Ignore duplicate point\n\n            while (nextX === x && nextY === y && k < segLen) {\n              k++;\n              nextIdx += dir;\n              idx += dir;\n              nextX = points[nextIdx * 2];\n              nextY = points[nextIdx * 2 + 1];\n              x = points[idx * 2];\n              y = points[idx * 2 + 1];\n              dx = x - prevX;\n              dy = y - prevY;\n            }\n\n            var tmpK = k + 1;\n\n            if (connectNulls) {\n              // Find next point not null\n              while (isPointNull(nextX, nextY) && tmpK < segLen) {\n                tmpK++;\n                nextIdx += dir;\n                nextX = points[nextIdx * 2];\n                nextY = points[nextIdx * 2 + 1];\n              }\n            }\n\n            var ratioNextSeg = 0.5;\n            var vx = 0;\n            var vy = 0;\n            var nextCpx0 = void 0;\n            var nextCpy0 = void 0; // Is last point\n\n            if (tmpK >= segLen || isPointNull(nextX, nextY)) {\n              cpx1 = x;\n              cpy1 = y;\n            } else {\n              vx = nextX - prevX;\n              vy = nextY - prevY;\n              var dx0 = x - prevX;\n              var dx1 = nextX - x;\n              var dy0 = y - prevY;\n              var dy1 = nextY - y;\n              var lenPrevSeg = void 0;\n              var lenNextSeg = void 0;\n\n              if (smoothMonotone === 'x') {\n                lenPrevSeg = Math.abs(dx0);\n                lenNextSeg = Math.abs(dx1);\n                var dir_1 = vx > 0 ? 1 : -1;\n                cpx1 = x - dir_1 * lenPrevSeg * smooth;\n                cpy1 = y;\n                nextCpx0 = x + dir_1 * lenNextSeg * smooth;\n                nextCpy0 = y;\n              } else if (smoothMonotone === 'y') {\n                lenPrevSeg = Math.abs(dy0);\n                lenNextSeg = Math.abs(dy1);\n                var dir_2 = vy > 0 ? 1 : -1;\n                cpx1 = x;\n                cpy1 = y - dir_2 * lenPrevSeg * smooth;\n                nextCpx0 = x;\n                nextCpy0 = y + dir_2 * lenNextSeg * smooth;\n              } else {\n                lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0);\n                lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1); // Use ratio of seg length\n\n                ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);\n                cpx1 = x - vx * smooth * (1 - ratioNextSeg);\n                cpy1 = y - vy * smooth * (1 - ratioNextSeg); // cp0 of next segment\n\n                nextCpx0 = x + vx * smooth * ratioNextSeg;\n                nextCpy0 = y + vy * smooth * ratioNextSeg; // Smooth constraint between point and next point.\n                // Avoid exceeding extreme after smoothing.\n\n                nextCpx0 = mathMin$5(nextCpx0, mathMax$5(nextX, x));\n                nextCpy0 = mathMin$5(nextCpy0, mathMax$5(nextY, y));\n                nextCpx0 = mathMax$5(nextCpx0, mathMin$5(nextX, x));\n                nextCpy0 = mathMax$5(nextCpy0, mathMin$5(nextY, y)); // Reclaculate cp1 based on the adjusted cp0 of next seg.\n\n                vx = nextCpx0 - x;\n                vy = nextCpy0 - y;\n                cpx1 = x - vx * lenPrevSeg / lenNextSeg;\n                cpy1 = y - vy * lenPrevSeg / lenNextSeg; // Smooth constraint between point and prev point.\n                // Avoid exceeding extreme after smoothing.\n\n                cpx1 = mathMin$5(cpx1, mathMax$5(prevX, x));\n                cpy1 = mathMin$5(cpy1, mathMax$5(prevY, y));\n                cpx1 = mathMax$5(cpx1, mathMin$5(prevX, x));\n                cpy1 = mathMax$5(cpy1, mathMin$5(prevY, y)); // Adjust next cp0 again.\n\n                vx = x - cpx1;\n                vy = y - cpy1;\n                nextCpx0 = x + vx * lenNextSeg / lenPrevSeg;\n                nextCpy0 = y + vy * lenNextSeg / lenPrevSeg;\n              }\n            }\n\n            ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y);\n            cpx0 = nextCpx0;\n            cpy0 = nextCpy0;\n          } else {\n            ctx.lineTo(x, y);\n          }\n        }\n\n        prevX = x;\n        prevY = y;\n        idx += dir;\n      }\n\n      return k;\n    }\n\n    var ECPolylineShape =\n    /** @class */\n    function () {\n      function ECPolylineShape() {\n        this.smooth = 0;\n        this.smoothConstraint = true;\n      }\n\n      return ECPolylineShape;\n    }();\n\n    var ECPolyline =\n    /** @class */\n    function (_super) {\n      __extends(ECPolyline, _super);\n\n      function ECPolyline(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-polyline';\n        return _this;\n      }\n\n      ECPolyline.prototype.getDefaultStyle = function () {\n        return {\n          stroke: '#000',\n          fill: null\n        };\n      };\n\n      ECPolyline.prototype.getDefaultShape = function () {\n        return new ECPolylineShape();\n      };\n\n      ECPolyline.prototype.buildPath = function (ctx, shape) {\n        var points = shape.points;\n        var i = 0;\n        var len = points.length / 2; // const result = getBoundingBox(points, shape.smoothConstraint);\n\n        if (shape.connectNulls) {\n          // Must remove first and last null values avoid draw error in polygon\n          for (; len > 0; len--) {\n            if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n              break;\n            }\n          }\n\n          for (; i < len; i++) {\n            if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n              break;\n            }\n          }\n        }\n\n        while (i < len) {\n          i += drawSegment(ctx, points, i, len, len, 1, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1;\n        }\n      };\n\n      ECPolyline.prototype.getPointOn = function (xOrY, dim) {\n        if (!this.path) {\n          this.createPathProxy();\n          this.buildPath(this.path, this.shape);\n        }\n\n        var path = this.path;\n        var data = path.data;\n        var CMD = PathProxy.CMD;\n        var x0;\n        var y0;\n        var isDimX = dim === 'x';\n        var roots = [];\n\n        for (var i = 0; i < data.length;) {\n          var cmd = data[i++];\n          var x = void 0;\n          var y = void 0;\n          var x2 = void 0;\n          var y2 = void 0;\n          var x3 = void 0;\n          var y3 = void 0;\n          var t = void 0;\n\n          switch (cmd) {\n            case CMD.M:\n              x0 = data[i++];\n              y0 = data[i++];\n              break;\n\n            case CMD.L:\n              x = data[i++];\n              y = data[i++];\n              t = isDimX ? (xOrY - x0) / (x - x0) : (xOrY - y0) / (y - y0);\n\n              if (t <= 1 && t >= 0) {\n                var val = isDimX ? (y - y0) * t + y0 : (x - x0) * t + x0;\n                return isDimX ? [xOrY, val] : [val, xOrY];\n              }\n\n              x0 = x;\n              y0 = y;\n              break;\n\n            case CMD.C:\n              x = data[i++];\n              y = data[i++];\n              x2 = data[i++];\n              y2 = data[i++];\n              x3 = data[i++];\n              y3 = data[i++];\n              var nRoot = isDimX ? cubicRootAt(x0, x, x2, x3, xOrY, roots) : cubicRootAt(y0, y, y2, y3, xOrY, roots);\n\n              if (nRoot > 0) {\n                for (var i_1 = 0; i_1 < nRoot; i_1++) {\n                  var t_1 = roots[i_1];\n\n                  if (t_1 <= 1 && t_1 >= 0) {\n                    var val = isDimX ? cubicAt(y0, y, y2, y3, t_1) : cubicAt(x0, x, x2, x3, t_1);\n                    return isDimX ? [xOrY, val] : [val, xOrY];\n                  }\n                }\n              }\n\n              x0 = x3;\n              y0 = y3;\n              break;\n          }\n        }\n      };\n\n      return ECPolyline;\n    }(Path);\n\n    var ECPolygonShape =\n    /** @class */\n    function (_super) {\n      __extends(ECPolygonShape, _super);\n\n      function ECPolygonShape() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return ECPolygonShape;\n    }(ECPolylineShape);\n\n    var ECPolygon =\n    /** @class */\n    function (_super) {\n      __extends(ECPolygon, _super);\n\n      function ECPolygon(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-polygon';\n        return _this;\n      }\n\n      ECPolygon.prototype.getDefaultShape = function () {\n        return new ECPolygonShape();\n      };\n\n      ECPolygon.prototype.buildPath = function (ctx, shape) {\n        var points = shape.points;\n        var stackedOnPoints = shape.stackedOnPoints;\n        var i = 0;\n        var len = points.length / 2;\n        var smoothMonotone = shape.smoothMonotone;\n\n        if (shape.connectNulls) {\n          // Must remove first and last null values avoid draw error in polygon\n          for (; len > 0; len--) {\n            if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n              break;\n            }\n          }\n\n          for (; i < len; i++) {\n            if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n              break;\n            }\n          }\n        }\n\n        while (i < len) {\n          var k = drawSegment(ctx, points, i, len, len, 1, shape.smooth, smoothMonotone, shape.connectNulls);\n          drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls);\n          i += k + 1;\n          ctx.closePath();\n        }\n      };\n\n      return ECPolygon;\n    }(Path);\n\n    function createGridClipPath(cartesian, hasAnimation, seriesModel, done, during) {\n      var rect = cartesian.getArea();\n      var x = rect.x;\n      var y = rect.y;\n      var width = rect.width;\n      var height = rect.height;\n      var lineWidth = seriesModel.get(['lineStyle', 'width']) || 2; // Expand the clip path a bit to avoid the border is clipped and looks thinner\n\n      x -= lineWidth / 2;\n      y -= lineWidth / 2;\n      width += lineWidth;\n      height += lineWidth; // fix: https://github.com/apache/incubator-echarts/issues/11369\n\n      x = Math.floor(x);\n      width = Math.round(width);\n      var clipPath = new Rect({\n        shape: {\n          x: x,\n          y: y,\n          width: width,\n          height: height\n        }\n      });\n\n      if (hasAnimation) {\n        var baseAxis = cartesian.getBaseAxis();\n        var isHorizontal = baseAxis.isHorizontal();\n        var isAxisInversed = baseAxis.inverse;\n\n        if (isHorizontal) {\n          if (isAxisInversed) {\n            clipPath.shape.x += width;\n          }\n\n          clipPath.shape.width = 0;\n        } else {\n          if (!isAxisInversed) {\n            clipPath.shape.y += height;\n          }\n\n          clipPath.shape.height = 0;\n        }\n\n        var duringCb = isFunction(during) ? function (percent) {\n          during(percent, clipPath);\n        } : null;\n        initProps(clipPath, {\n          shape: {\n            width: width,\n            height: height,\n            x: x,\n            y: y\n          }\n        }, seriesModel, null, done, duringCb);\n      }\n\n      return clipPath;\n    }\n\n    function createPolarClipPath(polar, hasAnimation, seriesModel) {\n      var sectorArea = polar.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n\n      var r0 = round(sectorArea.r0, 1);\n      var r = round(sectorArea.r, 1);\n      var clipPath = new Sector({\n        shape: {\n          cx: round(polar.cx, 1),\n          cy: round(polar.cy, 1),\n          r0: r0,\n          r: r,\n          startAngle: sectorArea.startAngle,\n          endAngle: sectorArea.endAngle,\n          clockwise: sectorArea.clockwise\n        }\n      });\n\n      if (hasAnimation) {\n        var isRadial = polar.getBaseAxis().dim === 'angle';\n\n        if (isRadial) {\n          clipPath.shape.endAngle = sectorArea.startAngle;\n        } else {\n          clipPath.shape.r = r0;\n        }\n\n        initProps(clipPath, {\n          shape: {\n            endAngle: sectorArea.endAngle,\n            r: r\n          }\n        }, seriesModel);\n      }\n\n      return clipPath;\n    }\n\n    function createClipPath(coordSys, hasAnimation, seriesModel, done, during) {\n      if (!coordSys) {\n        return null;\n      } else if (coordSys.type === 'polar') {\n        return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n      } else if (coordSys.type === 'cartesian2d') {\n        return createGridClipPath(coordSys, hasAnimation, seriesModel, done, during);\n      }\n\n      return null;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function isCoordinateSystemType(coordSys, type) {\n      return coordSys.type === type;\n    }\n\n    function isPointsSame(points1, points2) {\n      if (points1.length !== points2.length) {\n        return;\n      }\n\n      for (var i = 0; i < points1.length; i++) {\n        if (points1[i] !== points2[i]) {\n          return;\n        }\n      }\n\n      return true;\n    }\n\n    function bboxFromPoints(points) {\n      var minX = Infinity;\n      var minY = Infinity;\n      var maxX = -Infinity;\n      var maxY = -Infinity;\n\n      for (var i = 0; i < points.length;) {\n        var x = points[i++];\n        var y = points[i++];\n\n        if (!isNaN(x)) {\n          minX = Math.min(x, minX);\n          maxX = Math.max(x, maxX);\n        }\n\n        if (!isNaN(y)) {\n          minY = Math.min(y, minY);\n          maxY = Math.max(y, maxY);\n        }\n      }\n\n      return [[minX, minY], [maxX, maxY]];\n    }\n\n    function getBoundingDiff(points1, points2) {\n      var _a = bboxFromPoints(points1),\n          min1 = _a[0],\n          max1 = _a[1];\n\n      var _b = bboxFromPoints(points2),\n          min2 = _b[0],\n          max2 = _b[1]; // Get a max value from each corner of two boundings.\n\n\n      return Math.max(Math.abs(min1[0] - min2[0]), Math.abs(min1[1] - min2[1]), Math.abs(max1[0] - max2[0]), Math.abs(max1[1] - max2[1]));\n    }\n\n    function getSmooth(smooth) {\n      return isNumber(smooth) ? smooth : smooth ? 0.5 : 0;\n    }\n\n    function getStackedOnPoints(coordSys, data, dataCoordInfo) {\n      if (!dataCoordInfo.valueDim) {\n        return [];\n      }\n\n      var len = data.count();\n      var points = createFloat32Array(len * 2);\n\n      for (var idx = 0; idx < len; idx++) {\n        var pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx);\n        points[idx * 2] = pt[0];\n        points[idx * 2 + 1] = pt[1];\n      }\n\n      return points;\n    }\n\n    function turnPointsIntoStep(points, coordSys, stepTurnAt, connectNulls) {\n      var baseAxis = coordSys.getBaseAxis();\n      var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;\n      var stepPoints = [];\n      var i = 0;\n      var stepPt = [];\n      var pt = [];\n      var nextPt = [];\n      var filteredPoints = [];\n\n      if (connectNulls) {\n        for (i = 0; i < points.length; i += 2) {\n          if (!isNaN(points[i]) && !isNaN(points[i + 1])) {\n            filteredPoints.push(points[i], points[i + 1]);\n          }\n        }\n\n        points = filteredPoints;\n      }\n\n      for (i = 0; i < points.length - 2; i += 2) {\n        nextPt[0] = points[i + 2];\n        nextPt[1] = points[i + 3];\n        pt[0] = points[i];\n        pt[1] = points[i + 1];\n        stepPoints.push(pt[0], pt[1]);\n\n        switch (stepTurnAt) {\n          case 'end':\n            stepPt[baseIndex] = nextPt[baseIndex];\n            stepPt[1 - baseIndex] = pt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n            break;\n\n          case 'middle':\n            var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;\n            var stepPt2 = [];\n            stepPt[baseIndex] = stepPt2[baseIndex] = middle;\n            stepPt[1 - baseIndex] = pt[1 - baseIndex];\n            stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n            stepPoints.push(stepPt2[0], stepPt2[1]);\n            break;\n\n          default:\n            // default is start\n            stepPt[baseIndex] = pt[baseIndex];\n            stepPt[1 - baseIndex] = nextPt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n        }\n      } // Last points\n\n\n      stepPoints.push(points[i++], points[i++]);\n      return stepPoints;\n    }\n    /**\n     * Clip color stops to edge. Avoid creating too large gradients.\n     * Which may lead to blurry when GPU acceleration is enabled. See #15680\n     *\n     * The stops has been sorted from small to large.\n     */\n\n\n    function clipColorStops(colorStops, maxSize) {\n      var newColorStops = [];\n      var len = colorStops.length; // coord will always < 0 in prevOutOfRangeColorStop.\n\n      var prevOutOfRangeColorStop;\n      var prevInRangeColorStop;\n\n      function lerpStop(stop0, stop1, clippedCoord) {\n        var coord0 = stop0.coord;\n        var p = (clippedCoord - coord0) / (stop1.coord - coord0);\n        var color = lerp$1(p, [stop0.color, stop1.color]);\n        return {\n          coord: clippedCoord,\n          color: color\n        };\n      }\n\n      for (var i = 0; i < len; i++) {\n        var stop_1 = colorStops[i];\n        var coord = stop_1.coord;\n\n        if (coord < 0) {\n          prevOutOfRangeColorStop = stop_1;\n        } else if (coord > maxSize) {\n          if (prevInRangeColorStop) {\n            newColorStops.push(lerpStop(prevInRangeColorStop, stop_1, maxSize));\n          } else if (prevOutOfRangeColorStop) {\n            // If there are two stops and coord range is between these two stops\n            newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0), lerpStop(prevOutOfRangeColorStop, stop_1, maxSize));\n          } // All following stop will be out of range. So just ignore them.\n\n\n          break;\n        } else {\n          if (prevOutOfRangeColorStop) {\n            newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0)); // Reset\n\n            prevOutOfRangeColorStop = null;\n          }\n\n          newColorStops.push(stop_1);\n          prevInRangeColorStop = stop_1;\n        }\n      }\n\n      return newColorStops;\n    }\n\n    function getVisualGradient(data, coordSys, api) {\n      var visualMetaList = data.getVisual('visualMeta');\n\n      if (!visualMetaList || !visualMetaList.length || !data.count()) {\n        // When data.count() is 0, gradient range can not be calculated.\n        return;\n      }\n\n      if (coordSys.type !== 'cartesian2d') {\n        if (\"development\" !== 'production') {\n          console.warn('Visual map on line style is only supported on cartesian2d.');\n        }\n\n        return;\n      }\n\n      var coordDim;\n      var visualMeta;\n\n      for (var i = visualMetaList.length - 1; i >= 0; i--) {\n        var dimInfo = data.getDimensionInfo(visualMetaList[i].dimension);\n        coordDim = dimInfo && dimInfo.coordDim; // Can only be x or y\n\n        if (coordDim === 'x' || coordDim === 'y') {\n          visualMeta = visualMetaList[i];\n          break;\n        }\n      }\n\n      if (!visualMeta) {\n        if (\"development\" !== 'production') {\n          console.warn('Visual map on line style only support x or y dimension.');\n        }\n\n        return;\n      } // If the area to be rendered is bigger than area defined by LinearGradient,\n      // the canvas spec prescribes that the color of the first stop and the last\n      // stop should be used. But if two stops are added at offset 0, in effect\n      // browsers use the color of the second stop to render area outside\n      // LinearGradient. So we can only infinitesimally extend area defined in\n      // LinearGradient to render `outerColors`.\n\n\n      var axis = coordSys.getAxis(coordDim); // dataToCoord mapping may not be linear, but must be monotonic.\n\n      var colorStops = map(visualMeta.stops, function (stop) {\n        // offset will be calculated later.\n        return {\n          coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),\n          color: stop.color\n        };\n      });\n      var stopLen = colorStops.length;\n      var outerColors = visualMeta.outerColors.slice();\n\n      if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {\n        colorStops.reverse();\n        outerColors.reverse();\n      }\n\n      var colorStopsInRange = clipColorStops(colorStops, coordDim === 'x' ? api.getWidth() : api.getHeight());\n      var inRangeStopLen = colorStopsInRange.length;\n\n      if (!inRangeStopLen && stopLen) {\n        // All stops are out of range. All will be the same color.\n        return colorStops[0].coord < 0 ? outerColors[1] ? outerColors[1] : colorStops[stopLen - 1].color : outerColors[0] ? outerColors[0] : colorStops[0].color;\n      }\n\n      var tinyExtent = 10; // Arbitrary value: 10px\n\n      var minCoord = colorStopsInRange[0].coord - tinyExtent;\n      var maxCoord = colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent;\n      var coordSpan = maxCoord - minCoord;\n\n      if (coordSpan < 1e-3) {\n        return 'transparent';\n      }\n\n      each(colorStopsInRange, function (stop) {\n        stop.offset = (stop.coord - minCoord) / coordSpan;\n      });\n      colorStopsInRange.push({\n        // NOTE: inRangeStopLen may still be 0 if stoplen is zero.\n        offset: inRangeStopLen ? colorStopsInRange[inRangeStopLen - 1].offset : 0.5,\n        color: outerColors[1] || 'transparent'\n      });\n      colorStopsInRange.unshift({\n        offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5,\n        color: outerColors[0] || 'transparent'\n      });\n      var gradient = new LinearGradient(0, 0, 0, 0, colorStopsInRange, true);\n      gradient[coordDim] = minCoord;\n      gradient[coordDim + '2'] = maxCoord;\n      return gradient;\n    }\n\n    function getIsIgnoreFunc(seriesModel, data, coordSys) {\n      var showAllSymbol = seriesModel.get('showAllSymbol');\n      var isAuto = showAllSymbol === 'auto';\n\n      if (showAllSymbol && !isAuto) {\n        return;\n      }\n\n      var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n\n      if (!categoryAxis) {\n        return;\n      } // Note that category label interval strategy might bring some weird effect\n      // in some scenario: users may wonder why some of the symbols are not\n      // displayed. So we show all symbols as possible as we can.\n\n\n      if (isAuto // Simplify the logic, do not determine label overlap here.\n      && canShowAllSymbolForCategory(categoryAxis, data)) {\n        return;\n      } // Otherwise follow the label interval strategy on category axis.\n\n\n      var categoryDataDim = data.mapDimension(categoryAxis.dim);\n      var labelMap = {};\n      each(categoryAxis.getViewLabels(), function (labelItem) {\n        var ordinalNumber = categoryAxis.scale.getRawOrdinalNumber(labelItem.tickValue);\n        labelMap[ordinalNumber] = 1;\n      });\n      return function (dataIndex) {\n        return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex));\n      };\n    }\n\n    function canShowAllSymbolForCategory(categoryAxis, data) {\n      // In most cases, line is monotonous on category axis, and the label size\n      // is close with each other. So we check the symbol size and some of the\n      // label size alone with the category axis to estimate whether all symbol\n      // can be shown without overlap.\n      var axisExtent = categoryAxis.getExtent();\n      var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count();\n      isNaN(availSize) && (availSize = 0); // 0/0 is NaN.\n      // Sampling some points, max 5.\n\n      var dataLen = data.count();\n      var step = Math.max(1, Math.round(dataLen / 5));\n\n      for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) {\n        if (Symbol.getSymbolSize(data, dataIndex // Only for cartesian, where `isHorizontal` exists.\n        )[categoryAxis.isHorizontal() ? 1 : 0] // Empirical number\n        * 1.5 > availSize) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    function isPointNull$1(x, y) {\n      return isNaN(x) || isNaN(y);\n    }\n\n    function getLastIndexNotNull(points) {\n      var len = points.length / 2;\n\n      for (; len > 0; len--) {\n        if (!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])) {\n          break;\n        }\n      }\n\n      return len - 1;\n    }\n\n    function getPointAtIndex(points, idx) {\n      return [points[idx * 2], points[idx * 2 + 1]];\n    }\n\n    function getIndexRange(points, xOrY, dim) {\n      var len = points.length / 2;\n      var dimIdx = dim === 'x' ? 0 : 1;\n      var a;\n      var b;\n      var prevIndex = 0;\n      var nextIndex = -1;\n\n      for (var i = 0; i < len; i++) {\n        b = points[i * 2 + dimIdx];\n\n        if (isNaN(b) || isNaN(points[i * 2 + 1 - dimIdx])) {\n          continue;\n        }\n\n        if (i === 0) {\n          a = b;\n          continue;\n        }\n\n        if (a <= xOrY && b >= xOrY || a >= xOrY && b <= xOrY) {\n          nextIndex = i;\n          break;\n        }\n\n        prevIndex = i;\n        a = b;\n      }\n\n      return {\n        range: [prevIndex, nextIndex],\n        t: (xOrY - a) / (b - a)\n      };\n    }\n\n    function anyStateShowEndLabel(seriesModel) {\n      if (seriesModel.get(['endLabel', 'show'])) {\n        return true;\n      }\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        if (seriesModel.get([SPECIAL_STATES[i], 'endLabel', 'show'])) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    function createLineClipPath(lineView, coordSys, hasAnimation, seriesModel) {\n      if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n        var endLabelModel_1 = seriesModel.getModel('endLabel');\n        var valueAnimation_1 = endLabelModel_1.get('valueAnimation');\n        var data_1 = seriesModel.getData();\n        var labelAnimationRecord_1 = {\n          lastFrameIndex: 0\n        };\n        var during = anyStateShowEndLabel(seriesModel) ? function (percent, clipRect) {\n          lineView._endLabelOnDuring(percent, clipRect, data_1, labelAnimationRecord_1, valueAnimation_1, endLabelModel_1, coordSys);\n        } : null;\n        var isHorizontal = coordSys.getBaseAxis().isHorizontal();\n        var clipPath = createGridClipPath(coordSys, hasAnimation, seriesModel, function () {\n          var endLabel = lineView._endLabel;\n\n          if (endLabel && hasAnimation) {\n            if (labelAnimationRecord_1.originalX != null) {\n              endLabel.attr({\n                x: labelAnimationRecord_1.originalX,\n                y: labelAnimationRecord_1.originalY\n              });\n            }\n          }\n        }, during); // Expand clip shape to avoid clipping when line value exceeds axis\n\n        if (!seriesModel.get('clip', true)) {\n          var rectShape = clipPath.shape;\n          var expandSize = Math.max(rectShape.width, rectShape.height);\n\n          if (isHorizontal) {\n            rectShape.y -= expandSize;\n            rectShape.height += expandSize * 2;\n          } else {\n            rectShape.x -= expandSize;\n            rectShape.width += expandSize * 2;\n          }\n        } // Set to the final frame. To make sure label layout is right.\n\n\n        if (during) {\n          during(1, clipPath);\n        }\n\n        return clipPath;\n      } else {\n        if (\"development\" !== 'production') {\n          if (seriesModel.get(['endLabel', 'show'])) {\n            console.warn('endLabel is not supported for lines in polar systems.');\n          }\n        }\n\n        return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n      }\n    }\n\n    function getEndLabelStateSpecified(endLabelModel, coordSys) {\n      var baseAxis = coordSys.getBaseAxis();\n      var isHorizontal = baseAxis.isHorizontal();\n      var isBaseInversed = baseAxis.inverse;\n      var align = isHorizontal ? isBaseInversed ? 'right' : 'left' : 'center';\n      var verticalAlign = isHorizontal ? 'middle' : isBaseInversed ? 'top' : 'bottom';\n      return {\n        normal: {\n          align: endLabelModel.get('align') || align,\n          verticalAlign: endLabelModel.get('verticalAlign') || verticalAlign\n        }\n      };\n    }\n\n    var LineView =\n    /** @class */\n    function (_super) {\n      __extends(LineView, _super);\n\n      function LineView() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      LineView.prototype.init = function () {\n        var lineGroup = new Group();\n        var symbolDraw = new SymbolDraw();\n        this.group.add(symbolDraw.group);\n        this._symbolDraw = symbolDraw;\n        this._lineGroup = lineGroup;\n      };\n\n      LineView.prototype.render = function (seriesModel, ecModel, api) {\n        var _this = this;\n\n        var coordSys = seriesModel.coordinateSystem;\n        var group = this.group;\n        var data = seriesModel.getData();\n        var lineStyleModel = seriesModel.getModel('lineStyle');\n        var areaStyleModel = seriesModel.getModel('areaStyle');\n        var points = data.getLayout('points') || [];\n        var isCoordSysPolar = coordSys.type === 'polar';\n        var prevCoordSys = this._coordSys;\n        var symbolDraw = this._symbolDraw;\n        var polyline = this._polyline;\n        var polygon = this._polygon;\n        var lineGroup = this._lineGroup;\n        var hasAnimation = seriesModel.get('animation');\n        var isAreaChart = !areaStyleModel.isEmpty();\n        var valueOrigin = areaStyleModel.get('origin');\n        var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin);\n        var stackedOnPoints = isAreaChart && getStackedOnPoints(coordSys, data, dataCoordInfo);\n        var showSymbol = seriesModel.get('showSymbol');\n        var connectNulls = seriesModel.get('connectNulls');\n        var isIgnoreFunc = showSymbol && !isCoordSysPolar && getIsIgnoreFunc(seriesModel, data, coordSys); // Remove temporary symbols\n\n        var oldData = this._data;\n        oldData && oldData.eachItemGraphicEl(function (el, idx) {\n          if (el.__temp) {\n            group.remove(el);\n            oldData.setItemGraphicEl(idx, null);\n          }\n        }); // Remove previous created symbols if showSymbol changed to false\n\n        if (!showSymbol) {\n          symbolDraw.remove();\n        }\n\n        group.add(lineGroup); // FIXME step not support polar\n\n        var step = !isCoordSysPolar ? seriesModel.get('step') : false;\n        var clipShapeForSymbol;\n\n        if (coordSys && coordSys.getArea && seriesModel.get('clip', true)) {\n          clipShapeForSymbol = coordSys.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n          // See #7913 and `test/dataZoom-clip.html`.\n\n          if (clipShapeForSymbol.width != null) {\n            clipShapeForSymbol.x -= 0.1;\n            clipShapeForSymbol.y -= 0.1;\n            clipShapeForSymbol.width += 0.2;\n            clipShapeForSymbol.height += 0.2;\n          } else if (clipShapeForSymbol.r0) {\n            clipShapeForSymbol.r0 -= 0.5;\n            clipShapeForSymbol.r += 0.5;\n          }\n        }\n\n        this._clipShapeForSymbol = clipShapeForSymbol;\n        var visualColor = getVisualGradient(data, coordSys, api) || data.getVisual('style')[data.getVisual('drawType')]; // Initialization animation or coordinate system changed\n\n        if (!(polyline && prevCoordSys.type === coordSys.type && step === this._step)) {\n          showSymbol && symbolDraw.updateData(data, {\n            isIgnore: isIgnoreFunc,\n            clipShape: clipShapeForSymbol,\n            disableAnimation: true,\n            getSymbolPoint: function (idx) {\n              return [points[idx * 2], points[idx * 2 + 1]];\n            }\n          });\n          hasAnimation && this._initSymbolLabelAnimation(data, coordSys, clipShapeForSymbol);\n\n          if (step) {\n            // TODO If stacked series is not step\n            points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n            if (stackedOnPoints) {\n              stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n            }\n          }\n\n          polyline = this._newPolyline(points);\n\n          if (isAreaChart) {\n            polygon = this._newPolygon(points, stackedOnPoints);\n          } // If areaStyle is removed\n          else if (polygon) {\n              lineGroup.remove(polygon);\n              polygon = this._polygon = null;\n            } // NOTE: Must update _endLabel before setClipPath.\n\n\n          if (!isCoordSysPolar) {\n            this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n          }\n\n          lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n        } else {\n          if (isAreaChart && !polygon) {\n            // If areaStyle is added\n            polygon = this._newPolygon(points, stackedOnPoints);\n          } else if (polygon && !isAreaChart) {\n            // If areaStyle is removed\n            lineGroup.remove(polygon);\n            polygon = this._polygon = null;\n          } // NOTE: Must update _endLabel before setClipPath.\n\n\n          if (!isCoordSysPolar) {\n            this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n          } // Update clipPath\n\n\n          var oldClipPath = lineGroup.getClipPath();\n\n          if (oldClipPath) {\n            var newClipPath = createLineClipPath(this, coordSys, false, seriesModel);\n            initProps(oldClipPath, {\n              shape: newClipPath.shape\n            }, seriesModel);\n          } else {\n            lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n          } // Always update, or it is wrong in the case turning on legend\n          // because points are not changed.\n\n\n          showSymbol && symbolDraw.updateData(data, {\n            isIgnore: isIgnoreFunc,\n            clipShape: clipShapeForSymbol,\n            disableAnimation: true,\n            getSymbolPoint: function (idx) {\n              return [points[idx * 2], points[idx * 2 + 1]];\n            }\n          }); // In the case data zoom triggered refreshing frequently\n          // Data may not change if line has a category axis. So it should animate nothing.\n\n          if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) || !isPointsSame(this._points, points)) {\n            if (hasAnimation) {\n              this._doUpdateAnimation(data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls);\n            } else {\n              // Not do it in update with animation\n              if (step) {\n                // TODO If stacked series is not step\n                points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n                if (stackedOnPoints) {\n                  stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n                }\n              }\n\n              polyline.setShape({\n                points: points\n              });\n              polygon && polygon.setShape({\n                points: points,\n                stackedOnPoints: stackedOnPoints\n              });\n            }\n          }\n        }\n\n        var emphasisModel = seriesModel.getModel('emphasis');\n        var focus = emphasisModel.get('focus');\n        var blurScope = emphasisModel.get('blurScope');\n        var emphasisDisabled = emphasisModel.get('disabled');\n        polyline.useStyle(defaults( // Use color in lineStyle first\n        lineStyleModel.getLineStyle(), {\n          fill: 'none',\n          stroke: visualColor,\n          lineJoin: 'bevel'\n        }));\n        setStatesStylesFromModel(polyline, seriesModel, 'lineStyle');\n\n        if (polyline.style.lineWidth > 0 && seriesModel.get(['emphasis', 'lineStyle', 'width']) === 'bolder') {\n          var emphasisLineStyle = polyline.getState('emphasis').style;\n          emphasisLineStyle.lineWidth = +polyline.style.lineWidth + 1;\n        } // Needs seriesIndex for focus\n\n\n        getECData(polyline).seriesIndex = seriesModel.seriesIndex;\n        toggleHoverEmphasis(polyline, focus, blurScope, emphasisDisabled);\n        var smooth = getSmooth(seriesModel.get('smooth'));\n        var smoothMonotone = seriesModel.get('smoothMonotone');\n        polyline.setShape({\n          smooth: smooth,\n          smoothMonotone: smoothMonotone,\n          connectNulls: connectNulls\n        });\n\n        if (polygon) {\n          var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');\n          var stackedOnSmooth = 0;\n          polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), {\n            fill: visualColor,\n            opacity: 0.7,\n            lineJoin: 'bevel',\n            decal: data.getVisual('style').decal\n          }));\n\n          if (stackedOnSeries) {\n            stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));\n          }\n\n          polygon.setShape({\n            smooth: smooth,\n            stackedOnSmooth: stackedOnSmooth,\n            smoothMonotone: smoothMonotone,\n            connectNulls: connectNulls\n          });\n          setStatesStylesFromModel(polygon, seriesModel, 'areaStyle'); // Needs seriesIndex for focus\n\n          getECData(polygon).seriesIndex = seriesModel.seriesIndex;\n          toggleHoverEmphasis(polygon, focus, blurScope, emphasisDisabled);\n        }\n\n        var changePolyState = function (toState) {\n          _this._changePolyState(toState);\n        };\n\n        data.eachItemGraphicEl(function (el) {\n          // Switch polyline / polygon state if element changed its state.\n          el && (el.onHoverStateChange = changePolyState);\n        });\n        this._polyline.onHoverStateChange = changePolyState;\n        this._data = data; // Save the coordinate system for transition animation when data changed\n\n        this._coordSys = coordSys;\n        this._stackedOnPoints = stackedOnPoints;\n        this._points = points;\n        this._step = step;\n        this._valueOrigin = valueOrigin;\n\n        if (seriesModel.get('triggerLineEvent')) {\n          this.packEventData(seriesModel, polyline);\n          polygon && this.packEventData(seriesModel, polygon);\n        }\n      };\n\n      LineView.prototype.packEventData = function (seriesModel, el) {\n        getECData(el).eventData = {\n          componentType: 'series',\n          componentSubType: 'line',\n          componentIndex: seriesModel.componentIndex,\n          seriesIndex: seriesModel.seriesIndex,\n          seriesName: seriesModel.name,\n          seriesType: 'line'\n        };\n      };\n\n      LineView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var dataIndex = queryDataIndex(data, payload);\n\n        this._changePolyState('emphasis');\n\n        if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {\n          var points = data.getLayout('points');\n          var symbol = data.getItemGraphicEl(dataIndex);\n\n          if (!symbol) {\n            // Create a temporary symbol if it is not exists\n            var x = points[dataIndex * 2];\n            var y = points[dataIndex * 2 + 1];\n\n            if (isNaN(x) || isNaN(y)) {\n              // Null data\n              return;\n            } // fix #11360: shouldn't draw symbol outside clipShapeForSymbol\n\n\n            if (this._clipShapeForSymbol && !this._clipShapeForSymbol.contain(x, y)) {\n              return;\n            }\n\n            var zlevel = seriesModel.get('zlevel') || 0;\n            var z = seriesModel.get('z') || 0;\n            symbol = new Symbol(data, dataIndex);\n            symbol.x = x;\n            symbol.y = y;\n            symbol.setZ(zlevel, z); // ensure label text of the temporary symbol is in front of line and area polygon\n\n            var symbolLabel = symbol.getSymbolPath().getTextContent();\n\n            if (symbolLabel) {\n              symbolLabel.zlevel = zlevel;\n              symbolLabel.z = z;\n              symbolLabel.z2 = this._polyline.z2 + 1;\n            }\n\n            symbol.__temp = true;\n            data.setItemGraphicEl(dataIndex, symbol); // Stop scale animation\n\n            symbol.stopSymbolAnimation(true);\n            this.group.add(symbol);\n          }\n\n          symbol.highlight();\n        } else {\n          // Highlight whole series\n          ChartView.prototype.highlight.call(this, seriesModel, ecModel, api, payload);\n        }\n      };\n\n      LineView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var dataIndex = queryDataIndex(data, payload);\n\n        this._changePolyState('normal');\n\n        if (dataIndex != null && dataIndex >= 0) {\n          var symbol = data.getItemGraphicEl(dataIndex);\n\n          if (symbol) {\n            if (symbol.__temp) {\n              data.setItemGraphicEl(dataIndex, null);\n              this.group.remove(symbol);\n            } else {\n              symbol.downplay();\n            }\n          }\n        } else {\n          // FIXME\n          // can not downplay completely.\n          // Downplay whole series\n          ChartView.prototype.downplay.call(this, seriesModel, ecModel, api, payload);\n        }\n      };\n\n      LineView.prototype._changePolyState = function (toState) {\n        var polygon = this._polygon;\n        setStatesFlag(this._polyline, toState);\n        polygon && setStatesFlag(polygon, toState);\n      };\n\n      LineView.prototype._newPolyline = function (points) {\n        var polyline = this._polyline; // Remove previous created polyline\n\n        if (polyline) {\n          this._lineGroup.remove(polyline);\n        }\n\n        polyline = new ECPolyline({\n          shape: {\n            points: points\n          },\n          segmentIgnoreThreshold: 2,\n          z2: 10\n        });\n\n        this._lineGroup.add(polyline);\n\n        this._polyline = polyline;\n        return polyline;\n      };\n\n      LineView.prototype._newPolygon = function (points, stackedOnPoints) {\n        var polygon = this._polygon; // Remove previous created polygon\n\n        if (polygon) {\n          this._lineGroup.remove(polygon);\n        }\n\n        polygon = new ECPolygon({\n          shape: {\n            points: points,\n            stackedOnPoints: stackedOnPoints\n          },\n          segmentIgnoreThreshold: 2\n        });\n\n        this._lineGroup.add(polygon);\n\n        this._polygon = polygon;\n        return polygon;\n      };\n\n      LineView.prototype._initSymbolLabelAnimation = function (data, coordSys, clipShape) {\n        var isHorizontalOrRadial;\n        var isCoordSysPolar;\n        var baseAxis = coordSys.getBaseAxis();\n        var isAxisInverse = baseAxis.inverse;\n\n        if (coordSys.type === 'cartesian2d') {\n          isHorizontalOrRadial = baseAxis.isHorizontal();\n          isCoordSysPolar = false;\n        } else if (coordSys.type === 'polar') {\n          isHorizontalOrRadial = baseAxis.dim === 'angle';\n          isCoordSysPolar = true;\n        }\n\n        var seriesModel = data.hostModel;\n        var seriesDuration = seriesModel.get('animationDuration');\n\n        if (isFunction(seriesDuration)) {\n          seriesDuration = seriesDuration(null);\n        }\n\n        var seriesDalay = seriesModel.get('animationDelay') || 0;\n        var seriesDalayValue = isFunction(seriesDalay) ? seriesDalay(null) : seriesDalay;\n        data.eachItemGraphicEl(function (symbol, idx) {\n          var el = symbol;\n\n          if (el) {\n            var point = [symbol.x, symbol.y];\n            var start = void 0;\n            var end = void 0;\n            var current = void 0;\n\n            if (clipShape) {\n              if (isCoordSysPolar) {\n                var polarClip = clipShape;\n                var coord = coordSys.pointToCoord(point);\n\n                if (isHorizontalOrRadial) {\n                  start = polarClip.startAngle;\n                  end = polarClip.endAngle;\n                  current = -coord[1] / 180 * Math.PI;\n                } else {\n                  start = polarClip.r0;\n                  end = polarClip.r;\n                  current = coord[0];\n                }\n              } else {\n                var gridClip = clipShape;\n\n                if (isHorizontalOrRadial) {\n                  start = gridClip.x;\n                  end = gridClip.x + gridClip.width;\n                  current = symbol.x;\n                } else {\n                  start = gridClip.y + gridClip.height;\n                  end = gridClip.y;\n                  current = symbol.y;\n                }\n              }\n            }\n\n            var ratio = end === start ? 0 : (current - start) / (end - start);\n\n            if (isAxisInverse) {\n              ratio = 1 - ratio;\n            }\n\n            var delay = isFunction(seriesDalay) ? seriesDalay(idx) : seriesDuration * ratio + seriesDalayValue;\n            var symbolPath = el.getSymbolPath();\n            var text = symbolPath.getTextContent();\n            el.attr({\n              scaleX: 0,\n              scaleY: 0\n            });\n            el.animateTo({\n              scaleX: 1,\n              scaleY: 1\n            }, {\n              duration: 200,\n              setToFinal: true,\n              delay: delay\n            });\n\n            if (text) {\n              text.animateFrom({\n                style: {\n                  opacity: 0\n                }\n              }, {\n                duration: 300,\n                delay: delay\n              });\n            }\n\n            symbolPath.disableLabelAnimation = true;\n          }\n        });\n      };\n\n      LineView.prototype._initOrUpdateEndLabel = function (seriesModel, coordSys, inheritColor) {\n        var endLabelModel = seriesModel.getModel('endLabel');\n\n        if (anyStateShowEndLabel(seriesModel)) {\n          var data_2 = seriesModel.getData();\n          var polyline = this._polyline; // series may be filtered.\n\n          var points = data_2.getLayout('points');\n\n          if (!points) {\n            polyline.removeTextContent();\n            this._endLabel = null;\n            return;\n          }\n\n          var endLabel = this._endLabel;\n\n          if (!endLabel) {\n            endLabel = this._endLabel = new ZRText({\n              z2: 200 // should be higher than item symbol\n\n            });\n            endLabel.ignoreClip = true;\n            polyline.setTextContent(this._endLabel);\n            polyline.disableLabelAnimation = true;\n          } // Find last non-NaN data to display data\n\n\n          var dataIndex = getLastIndexNotNull(points);\n\n          if (dataIndex >= 0) {\n            setLabelStyle(polyline, getLabelStatesModels(seriesModel, 'endLabel'), {\n              inheritColor: inheritColor,\n              labelFetcher: seriesModel,\n              labelDataIndex: dataIndex,\n              defaultText: function (dataIndex, opt, interpolatedValue) {\n                return interpolatedValue != null ? getDefaultInterpolatedLabel(data_2, interpolatedValue) : getDefaultLabel(data_2, dataIndex);\n              },\n              enableTextSetter: true\n            }, getEndLabelStateSpecified(endLabelModel, coordSys));\n            polyline.textConfig.position = null;\n          }\n        } else if (this._endLabel) {\n          this._polyline.removeTextContent();\n\n          this._endLabel = null;\n        }\n      };\n\n      LineView.prototype._endLabelOnDuring = function (percent, clipRect, data, animationRecord, valueAnimation, endLabelModel, coordSys) {\n        var endLabel = this._endLabel;\n        var polyline = this._polyline;\n\n        if (endLabel) {\n          // NOTE: Don't remove percent < 1. percent === 1 means the first frame during render.\n          // The label is not prepared at this time.\n          if (percent < 1 && animationRecord.originalX == null) {\n            animationRecord.originalX = endLabel.x;\n            animationRecord.originalY = endLabel.y;\n          }\n\n          var points = data.getLayout('points');\n          var seriesModel = data.hostModel;\n          var connectNulls = seriesModel.get('connectNulls');\n          var precision = endLabelModel.get('precision');\n          var distance = endLabelModel.get('distance') || 0;\n          var baseAxis = coordSys.getBaseAxis();\n          var isHorizontal = baseAxis.isHorizontal();\n          var isBaseInversed = baseAxis.inverse;\n          var clipShape = clipRect.shape;\n          var xOrY = isBaseInversed ? isHorizontal ? clipShape.x : clipShape.y + clipShape.height : isHorizontal ? clipShape.x + clipShape.width : clipShape.y;\n          var distanceX = (isHorizontal ? distance : 0) * (isBaseInversed ? -1 : 1);\n          var distanceY = (isHorizontal ? 0 : -distance) * (isBaseInversed ? -1 : 1);\n          var dim = isHorizontal ? 'x' : 'y';\n          var dataIndexRange = getIndexRange(points, xOrY, dim);\n          var indices = dataIndexRange.range;\n          var diff = indices[1] - indices[0];\n          var value = void 0;\n\n          if (diff >= 1) {\n            // diff > 1 && connectNulls, which is on the null data.\n            if (diff > 1 && !connectNulls) {\n              var pt = getPointAtIndex(points, indices[0]);\n              endLabel.attr({\n                x: pt[0] + distanceX,\n                y: pt[1] + distanceY\n              });\n              valueAnimation && (value = seriesModel.getRawValue(indices[0]));\n            } else {\n              var pt = polyline.getPointOn(xOrY, dim);\n              pt && endLabel.attr({\n                x: pt[0] + distanceX,\n                y: pt[1] + distanceY\n              });\n              var startValue = seriesModel.getRawValue(indices[0]);\n              var endValue = seriesModel.getRawValue(indices[1]);\n              valueAnimation && (value = interpolateRawValues(data, precision, startValue, endValue, dataIndexRange.t));\n            }\n\n            animationRecord.lastFrameIndex = indices[0];\n          } else {\n            // If diff <= 0, which is the range is not found(Include NaN)\n            // Choose the first point or last point.\n            var idx = percent === 1 || animationRecord.lastFrameIndex > 0 ? indices[0] : 0;\n            var pt = getPointAtIndex(points, idx);\n            valueAnimation && (value = seriesModel.getRawValue(idx));\n            endLabel.attr({\n              x: pt[0] + distanceX,\n              y: pt[1] + distanceY\n            });\n          }\n\n          if (valueAnimation) {\n            labelInner(endLabel).setLabelText(value);\n          }\n        }\n      };\n      /**\n       * @private\n       */\n      // FIXME Two value axis\n\n\n      LineView.prototype._doUpdateAnimation = function (data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls) {\n        var polyline = this._polyline;\n        var polygon = this._polygon;\n        var seriesModel = data.hostModel;\n        var diff = lineAnimationDiff(this._data, data, this._stackedOnPoints, stackedOnPoints, this._coordSys, coordSys, this._valueOrigin);\n        var current = diff.current;\n        var stackedOnCurrent = diff.stackedOnCurrent;\n        var next = diff.next;\n        var stackedOnNext = diff.stackedOnNext;\n\n        if (step) {\n          // TODO If stacked series is not step\n          current = turnPointsIntoStep(diff.current, coordSys, step, connectNulls);\n          stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step, connectNulls);\n          next = turnPointsIntoStep(diff.next, coordSys, step, connectNulls);\n          stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step, connectNulls);\n        } // Don't apply animation if diff is large.\n        // For better result and avoid memory explosion problems like\n        // https://github.com/apache/incubator-echarts/issues/12229\n\n\n        if (getBoundingDiff(current, next) > 3000 || polygon && getBoundingDiff(stackedOnCurrent, stackedOnNext) > 3000) {\n          polyline.stopAnimation();\n          polyline.setShape({\n            points: next\n          });\n\n          if (polygon) {\n            polygon.stopAnimation();\n            polygon.setShape({\n              points: next,\n              stackedOnPoints: stackedOnNext\n            });\n          }\n\n          return;\n        }\n\n        polyline.shape.__points = diff.current;\n        polyline.shape.points = current;\n        var target = {\n          shape: {\n            points: next\n          }\n        }; // Also animate the original points.\n        // If points reference is changed when turning into step line.\n\n        if (diff.current !== current) {\n          target.shape.__points = diff.next;\n        } // Stop previous animation.\n\n\n        polyline.stopAnimation();\n        updateProps(polyline, target, seriesModel);\n\n        if (polygon) {\n          polygon.setShape({\n            // Reuse the points with polyline.\n            points: current,\n            stackedOnPoints: stackedOnCurrent\n          });\n          polygon.stopAnimation();\n          updateProps(polygon, {\n            shape: {\n              stackedOnPoints: stackedOnNext\n            }\n          }, seriesModel); // If use attr directly in updateProps.\n\n          if (polyline.shape.points !== polygon.shape.points) {\n            polygon.shape.points = polyline.shape.points;\n          }\n        }\n\n        var updatedDataInfo = [];\n        var diffStatus = diff.status;\n\n        for (var i = 0; i < diffStatus.length; i++) {\n          var cmd = diffStatus[i].cmd;\n\n          if (cmd === '=') {\n            var el = data.getItemGraphicEl(diffStatus[i].idx1);\n\n            if (el) {\n              updatedDataInfo.push({\n                el: el,\n                ptIdx: i // Index of points\n\n              });\n            }\n          }\n        }\n\n        if (polyline.animators && polyline.animators.length) {\n          polyline.animators[0].during(function () {\n            polygon && polygon.dirtyShape();\n            var points = polyline.shape.__points;\n\n            for (var i = 0; i < updatedDataInfo.length; i++) {\n              var el = updatedDataInfo[i].el;\n              var offset = updatedDataInfo[i].ptIdx * 2;\n              el.x = points[offset];\n              el.y = points[offset + 1];\n              el.markRedraw();\n            }\n          });\n        }\n      };\n\n      LineView.prototype.remove = function (ecModel) {\n        var group = this.group;\n        var oldData = this._data;\n\n        this._lineGroup.removeAll();\n\n        this._symbolDraw.remove(true); // Remove temporary created elements when highlighting\n\n\n        oldData && oldData.eachItemGraphicEl(function (el, idx) {\n          if (el.__temp) {\n            group.remove(el);\n            oldData.setItemGraphicEl(idx, null);\n          }\n        });\n        this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._endLabel = this._data = null;\n      };\n\n      LineView.type = 'line';\n      return LineView;\n    }(ChartView);\n\n    function pointsLayout(seriesType, forceStoreInTypedArray) {\n      return {\n        seriesType: seriesType,\n        plan: createRenderPlanner(),\n        reset: function (seriesModel) {\n          var data = seriesModel.getData();\n          var coordSys = seriesModel.coordinateSystem;\n          var pipelineContext = seriesModel.pipelineContext;\n          var useTypedArray = forceStoreInTypedArray || pipelineContext.large;\n\n          if (!coordSys) {\n            return;\n          }\n\n          var dims = map(coordSys.dimensions, function (dim) {\n            return data.mapDimension(dim);\n          }).slice(0, 2);\n          var dimLen = dims.length;\n          var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n          if (isDimensionStacked(data, dims[0])) {\n            dims[0] = stackResultDim;\n          }\n\n          if (isDimensionStacked(data, dims[1])) {\n            dims[1] = stackResultDim;\n          }\n\n          var store = data.getStore();\n          var dimIdx0 = data.getDimensionIndex(dims[0]);\n          var dimIdx1 = data.getDimensionIndex(dims[1]);\n          return dimLen && {\n            progress: function (params, data) {\n              var segCount = params.end - params.start;\n              var points = useTypedArray && createFloat32Array(segCount * dimLen);\n              var tmpIn = [];\n              var tmpOut = [];\n\n              for (var i = params.start, offset = 0; i < params.end; i++) {\n                var point = void 0;\n\n                if (dimLen === 1) {\n                  var x = store.get(dimIdx0, i); // NOTE: Make sure the second parameter is null to use default strategy.\n\n                  point = coordSys.dataToPoint(x, null, tmpOut);\n                } else {\n                  tmpIn[0] = store.get(dimIdx0, i);\n                  tmpIn[1] = store.get(dimIdx1, i); // Let coordinate system to handle the NaN data.\n\n                  point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n                }\n\n                if (useTypedArray) {\n                  points[offset++] = point[0];\n                  points[offset++] = point[1];\n                } else {\n                  data.setItemLayout(i, point.slice());\n                }\n              }\n\n              useTypedArray && data.setLayout('points', points);\n            }\n          };\n        }\n      };\n    }\n\n    var samplers = {\n      average: function (frame) {\n        var sum = 0;\n        var count = 0;\n\n        for (var i = 0; i < frame.length; i++) {\n          if (!isNaN(frame[i])) {\n            sum += frame[i];\n            count++;\n          }\n        } // Return NaN if count is 0\n\n\n        return count === 0 ? NaN : sum / count;\n      },\n      sum: function (frame) {\n        var sum = 0;\n\n        for (var i = 0; i < frame.length; i++) {\n          // Ignore NaN\n          sum += frame[i] || 0;\n        }\n\n        return sum;\n      },\n      max: function (frame) {\n        var max = -Infinity;\n\n        for (var i = 0; i < frame.length; i++) {\n          frame[i] > max && (max = frame[i]);\n        } // NaN will cause illegal axis extent.\n\n\n        return isFinite(max) ? max : NaN;\n      },\n      min: function (frame) {\n        var min = Infinity;\n\n        for (var i = 0; i < frame.length; i++) {\n          frame[i] < min && (min = frame[i]);\n        } // NaN will cause illegal axis extent.\n\n\n        return isFinite(min) ? min : NaN;\n      },\n      // TODO\n      // Median\n      nearest: function (frame) {\n        return frame[0];\n      }\n    };\n\n    var indexSampler = function (frame) {\n      return Math.round(frame.length / 2);\n    };\n\n    function dataSample(seriesType) {\n      return {\n        seriesType: seriesType,\n        // FIXME:TS never used, so comment it\n        // modifyOutputEnd: true,\n        reset: function (seriesModel, ecModel, api) {\n          var data = seriesModel.getData();\n          var sampling = seriesModel.get('sampling');\n          var coordSys = seriesModel.coordinateSystem;\n          var count = data.count(); // Only cartesian2d support down sampling. Disable it when there is few data.\n\n          if (count > 10 && coordSys.type === 'cartesian2d' && sampling) {\n            var baseAxis = coordSys.getBaseAxis();\n            var valueAxis = coordSys.getOtherAxis(baseAxis);\n            var extent = baseAxis.getExtent();\n            var dpr = api.getDevicePixelRatio(); // Coordinste system has been resized\n\n            var size = Math.abs(extent[1] - extent[0]) * (dpr || 1);\n            var rate = Math.round(count / size);\n\n            if (isFinite(rate) && rate > 1) {\n              if (sampling === 'lttb') {\n                seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate));\n              }\n\n              var sampler = void 0;\n\n              if (isString(sampling)) {\n                sampler = samplers[sampling];\n              } else if (isFunction(sampling)) {\n                sampler = sampling;\n              }\n\n              if (sampler) {\n                // Only support sample the first dim mapped from value axis.\n                seriesModel.setData(data.downSample(data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler));\n              }\n            }\n          }\n        }\n      };\n    }\n\n    function install$2(registers) {\n      registers.registerChartView(LineView);\n      registers.registerSeriesModel(LineSeriesModel);\n      registers.registerLayout(pointsLayout('line', true));\n      registers.registerVisual({\n        seriesType: 'line',\n        reset: function (seriesModel) {\n          var data = seriesModel.getData(); // Visual coding for legend\n\n          var lineStyle = seriesModel.getModel('lineStyle').getLineStyle();\n\n          if (lineStyle && !lineStyle.stroke) {\n            // Fill in visual should be palette color if\n            // has color callback\n            lineStyle.stroke = data.getVisual('style').fill;\n          }\n\n          data.setVisual('legendLineStyle', lineStyle);\n        }\n      }); // Down sample after filter\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('line'));\n    }\n\n    var BaseBarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(BaseBarSeriesModel, _super);\n\n      function BaseBarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BaseBarSeriesModel.type;\n        return _this;\n      }\n\n      BaseBarSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      BaseBarSeriesModel.prototype.getMarkerPosition = function (value, dims, startingAtTick) {\n        var coordSys = this.coordinateSystem;\n\n        if (coordSys && coordSys.clampData) {\n          // PENDING if clamp ?\n          var pt_1 = coordSys.dataToPoint(coordSys.clampData(value));\n\n          if (startingAtTick) {\n            each(coordSys.getAxes(), function (axis, idx) {\n              // If axis type is category, use tick coords instead\n              if (axis.type === 'category') {\n                var tickCoords = axis.getTicksCoords();\n                var tickIdx = coordSys.clampData(value)[idx]; // The index of rightmost tick of markArea is 1 larger than x1/y1 index\n\n                if (dims && (dims[idx] === 'x1' || dims[idx] === 'y1')) {\n                  tickIdx += 1;\n                }\n\n                tickIdx > tickCoords.length - 1 && (tickIdx = tickCoords.length - 1);\n                tickIdx < 0 && (tickIdx = 0);\n                tickCoords[tickIdx] && (pt_1[idx] = axis.toGlobalCoord(tickCoords[tickIdx].coord));\n              }\n            });\n          } else {\n            var data = this.getData();\n            var offset = data.getLayout('offset');\n            var size = data.getLayout('size');\n            var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;\n            pt_1[offsetIndex] += offset + size / 2;\n          }\n\n          return pt_1;\n        }\n\n        return [NaN, NaN];\n      };\n\n      BaseBarSeriesModel.type = 'series.__base_bar__';\n      BaseBarSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        // stack: null\n        // Cartesian coordinate system\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        barMinHeight: 0,\n        barMinAngle: 0,\n        // cursor: null,\n        large: false,\n        largeThreshold: 400,\n        progressive: 3e3,\n        progressiveChunkMode: 'mod'\n      };\n      return BaseBarSeriesModel;\n    }(SeriesModel);\n\n    SeriesModel.registerClass(BaseBarSeriesModel);\n\n    var BarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(BarSeriesModel, _super);\n\n      function BarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BarSeriesModel.type;\n        return _this;\n      }\n\n      BarSeriesModel.prototype.getInitialData = function () {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true,\n          createInvertedIndices: !!this.get('realtimeSort', true) || null\n        });\n      };\n      /**\n       * @override\n       */\n\n\n      BarSeriesModel.prototype.getProgressive = function () {\n        // Do not support progressive in normal mode.\n        return this.get('large') ? this.get('progressive') : false;\n      };\n      /**\n       * @override\n       */\n\n\n      BarSeriesModel.prototype.getProgressiveThreshold = function () {\n        // Do not support progressive in normal mode.\n        var progressiveThreshold = this.get('progressiveThreshold');\n        var largeThreshold = this.get('largeThreshold');\n\n        if (largeThreshold > progressiveThreshold) {\n          progressiveThreshold = largeThreshold;\n        }\n\n        return progressiveThreshold;\n      };\n\n      BarSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n        return selectors.rect(data.getItemLayout(dataIndex));\n      };\n\n      BarSeriesModel.type = 'series.bar';\n      BarSeriesModel.dependencies = ['grid', 'polar'];\n      BarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, {\n        // If clipped\n        // Only available on cartesian2d\n        clip: true,\n        roundCap: false,\n        showBackground: false,\n        backgroundStyle: {\n          color: 'rgba(180, 180, 180, 0.2)',\n          borderColor: null,\n          borderWidth: 0,\n          borderType: 'solid',\n          borderRadius: 0,\n          shadowBlur: 0,\n          shadowColor: null,\n          shadowOffsetX: 0,\n          shadowOffsetY: 0,\n          opacity: 1\n        },\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        },\n        realtimeSort: false\n      });\n      return BarSeriesModel;\n    }(BaseBarSeriesModel);\n\n    /**\n     * Sausage: similar to sector, but have half circle on both sides\n     */\n\n    var SausageShape =\n    /** @class */\n    function () {\n      function SausageShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.r0 = 0;\n        this.r = 0;\n        this.startAngle = 0;\n        this.endAngle = Math.PI * 2;\n        this.clockwise = true;\n      }\n\n      return SausageShape;\n    }();\n\n    var SausagePath =\n    /** @class */\n    function (_super) {\n      __extends(SausagePath, _super);\n\n      function SausagePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'sausage';\n        return _this;\n      }\n\n      SausagePath.prototype.getDefaultShape = function () {\n        return new SausageShape();\n      };\n\n      SausagePath.prototype.buildPath = function (ctx, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var r0 = Math.max(shape.r0 || 0, 0);\n        var r = Math.max(shape.r, 0);\n        var dr = (r - r0) * 0.5;\n        var rCenter = r0 + dr;\n        var startAngle = shape.startAngle;\n        var endAngle = shape.endAngle;\n        var clockwise = shape.clockwise;\n        var PI2 = Math.PI * 2;\n        var lessThanCircle = clockwise ? endAngle - startAngle < PI2 : startAngle - endAngle < PI2;\n\n        if (!lessThanCircle) {\n          // Normalize angles\n          startAngle = endAngle - (clockwise ? PI2 : -PI2);\n        }\n\n        var unitStartX = Math.cos(startAngle);\n        var unitStartY = Math.sin(startAngle);\n        var unitEndX = Math.cos(endAngle);\n        var unitEndY = Math.sin(endAngle);\n\n        if (lessThanCircle) {\n          ctx.moveTo(unitStartX * r0 + cx, unitStartY * r0 + cy);\n          ctx.arc(unitStartX * rCenter + cx, unitStartY * rCenter + cy, dr, -Math.PI + startAngle, startAngle, !clockwise);\n        } else {\n          ctx.moveTo(unitStartX * r + cx, unitStartY * r + cy);\n        }\n\n        ctx.arc(cx, cy, r, startAngle, endAngle, !clockwise);\n        ctx.arc(unitEndX * rCenter + cx, unitEndY * rCenter + cy, dr, endAngle - Math.PI * 2, endAngle - Math.PI, !clockwise);\n\n        if (r0 !== 0) {\n          ctx.arc(cx, cy, r0, endAngle, startAngle, clockwise);\n        } // ctx.closePath();\n\n      };\n\n      return SausagePath;\n    }(Path);\n\n    function createSectorCalculateTextPosition(positionMapping, opts) {\n      opts = opts || {};\n      var isRoundCap = opts.isRoundCap;\n      return function (out, opts, boundingRect) {\n        var textPosition = opts.position;\n\n        if (!textPosition || textPosition instanceof Array) {\n          return calculateTextPosition(out, opts, boundingRect);\n        }\n\n        var mappedSectorPosition = positionMapping(textPosition);\n        var distance = opts.distance != null ? opts.distance : 5;\n        var sector = this.shape;\n        var cx = sector.cx;\n        var cy = sector.cy;\n        var r = sector.r;\n        var r0 = sector.r0;\n        var middleR = (r + r0) / 2;\n        var startAngle = sector.startAngle;\n        var endAngle = sector.endAngle;\n        var middleAngle = (startAngle + endAngle) / 2;\n        var extraDist = isRoundCap ? Math.abs(r - r0) / 2 : 0;\n        var mathCos = Math.cos;\n        var mathSin = Math.sin; // base position: top-left\n\n        var x = cx + r * mathCos(startAngle);\n        var y = cy + r * mathSin(startAngle);\n        var textAlign = 'left';\n        var textVerticalAlign = 'top';\n\n        switch (mappedSectorPosition) {\n          case 'startArc':\n            x = cx + (r0 - distance) * mathCos(middleAngle);\n            y = cy + (r0 - distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'top';\n            break;\n\n          case 'insideStartArc':\n            x = cx + (r0 + distance) * mathCos(middleAngle);\n            y = cy + (r0 + distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'bottom';\n            break;\n\n          case 'startAngle':\n            x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, distance + extraDist, false);\n            y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, distance + extraDist, false);\n            textAlign = 'right';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'insideStartAngle':\n            x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, -distance + extraDist, false);\n            y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, -distance + extraDist, false);\n            textAlign = 'left';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'middle':\n            x = cx + middleR * mathCos(middleAngle);\n            y = cy + middleR * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'endArc':\n            x = cx + (r + distance) * mathCos(middleAngle);\n            y = cy + (r + distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'bottom';\n            break;\n\n          case 'insideEndArc':\n            x = cx + (r - distance) * mathCos(middleAngle);\n            y = cy + (r - distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'top';\n            break;\n\n          case 'endAngle':\n            x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, distance + extraDist, true);\n            y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, distance + extraDist, true);\n            textAlign = 'left';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'insideEndAngle':\n            x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, -distance + extraDist, true);\n            y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, -distance + extraDist, true);\n            textAlign = 'right';\n            textVerticalAlign = 'middle';\n            break;\n\n          default:\n            return calculateTextPosition(out, opts, boundingRect);\n        }\n\n        out = out || {};\n        out.x = x;\n        out.y = y;\n        out.align = textAlign;\n        out.verticalAlign = textVerticalAlign;\n        return out;\n      };\n    }\n    function setSectorTextRotation(sector, textPosition, positionMapping, rotateType) {\n      if (isNumber(rotateType)) {\n        // user-set rotation\n        sector.setTextConfig({\n          rotation: rotateType\n        });\n        return;\n      } else if (isArray(textPosition)) {\n        // user-set position, use 0 as auto rotation\n        sector.setTextConfig({\n          rotation: 0\n        });\n        return;\n      }\n\n      var shape = sector.shape;\n      var startAngle = shape.clockwise ? shape.startAngle : shape.endAngle;\n      var endAngle = shape.clockwise ? shape.endAngle : shape.startAngle;\n      var middleAngle = (startAngle + endAngle) / 2;\n      var anchorAngle;\n      var mappedSectorPosition = positionMapping(textPosition);\n\n      switch (mappedSectorPosition) {\n        case 'startArc':\n        case 'insideStartArc':\n        case 'middle':\n        case 'insideEndArc':\n        case 'endArc':\n          anchorAngle = middleAngle;\n          break;\n\n        case 'startAngle':\n        case 'insideStartAngle':\n          anchorAngle = startAngle;\n          break;\n\n        case 'endAngle':\n        case 'insideEndAngle':\n          anchorAngle = endAngle;\n          break;\n\n        default:\n          sector.setTextConfig({\n            rotation: 0\n          });\n          return;\n      }\n\n      var rotate = Math.PI * 1.5 - anchorAngle;\n      /**\n       * TODO: labels with rotate > Math.PI / 2 should be rotate another\n       * half round flipped to increase readability. However, only middle\n       * position supports this for now, because in other positions, the\n       * anchor point is not at the center of the text, so the positions\n       * after rotating is not as expected.\n       */\n\n      if (mappedSectorPosition === 'middle' && rotate > Math.PI / 2 && rotate < Math.PI * 1.5) {\n        rotate -= Math.PI;\n      }\n\n      sector.setTextConfig({\n        rotation: rotate\n      });\n    }\n\n    function adjustAngleDistanceX(angle, distance, isEnd) {\n      return distance * Math.sin(angle) * (isEnd ? -1 : 1);\n    }\n\n    function adjustAngleDistanceY(angle, distance, isEnd) {\n      return distance * Math.cos(angle) * (isEnd ? 1 : -1);\n    }\n\n    var mathMax$6 = Math.max;\n    var mathMin$6 = Math.min;\n\n    function getClipArea(coord, data) {\n      var coordSysClipArea = coord.getArea && coord.getArea();\n\n      if (isCoordinateSystemType(coord, 'cartesian2d')) {\n        var baseAxis = coord.getBaseAxis(); // When boundaryGap is false or using time axis. bar may exceed the grid.\n        // We should not clip this part.\n        // See test/bar2.html\n\n        if (baseAxis.type !== 'category' || !baseAxis.onBand) {\n          var expandWidth = data.getLayout('bandWidth');\n\n          if (baseAxis.isHorizontal()) {\n            coordSysClipArea.x -= expandWidth;\n            coordSysClipArea.width += expandWidth * 2;\n          } else {\n            coordSysClipArea.y -= expandWidth;\n            coordSysClipArea.height += expandWidth * 2;\n          }\n        }\n      }\n\n      return coordSysClipArea;\n    }\n\n    var BarView =\n    /** @class */\n    function (_super) {\n      __extends(BarView, _super);\n\n      function BarView() {\n        var _this = _super.call(this) || this;\n\n        _this.type = BarView.type;\n        _this._isFirstFrame = true;\n        return _this;\n      }\n\n      BarView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        this._model = seriesModel;\n\n        this._removeOnRenderedListener(api);\n\n        this._updateDrawMode(seriesModel);\n\n        var coordinateSystemType = seriesModel.get('coordinateSystem');\n\n        if (coordinateSystemType === 'cartesian2d' || coordinateSystemType === 'polar') {\n          // Clear previously rendered progressive elements.\n          this._progressiveEls = null;\n          this._isLargeDraw ? this._renderLarge(seriesModel, ecModel, api) : this._renderNormal(seriesModel, ecModel, api, payload);\n        } else if (\"development\" !== 'production') {\n          warn('Only cartesian2d and polar supported for bar.');\n        }\n      };\n\n      BarView.prototype.incrementalPrepareRender = function (seriesModel) {\n        this._clear();\n\n        this._updateDrawMode(seriesModel); // incremental also need to clip, otherwise might be overlow.\n        // But must not set clip in each frame, otherwise all of the children will be marked redraw.\n\n\n        this._updateLargeClip(seriesModel);\n      };\n\n      BarView.prototype.incrementalRender = function (params, seriesModel) {\n        // Reset\n        this._progressiveEls = []; // Do not support progressive in normal mode.\n\n        this._incrementalRenderLarge(params, seriesModel);\n      };\n\n      BarView.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      BarView.prototype._updateDrawMode = function (seriesModel) {\n        var isLargeDraw = seriesModel.pipelineContext.large;\n\n        if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) {\n          this._isLargeDraw = isLargeDraw;\n\n          this._clear();\n        }\n      };\n\n      BarView.prototype._renderNormal = function (seriesModel, ecModel, api, payload) {\n        var group = this.group;\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var coord = seriesModel.coordinateSystem;\n        var baseAxis = coord.getBaseAxis();\n        var isHorizontalOrRadial;\n\n        if (coord.type === 'cartesian2d') {\n          isHorizontalOrRadial = baseAxis.isHorizontal();\n        } else if (coord.type === 'polar') {\n          isHorizontalOrRadial = baseAxis.dim === 'angle';\n        }\n\n        var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;\n        var realtimeSortCfg = shouldRealtimeSort(seriesModel, coord);\n\n        if (realtimeSortCfg) {\n          this._enableRealtimeSort(realtimeSortCfg, data, api);\n        }\n\n        var needsClip = seriesModel.get('clip', true) || realtimeSortCfg;\n        var coordSysClipArea = getClipArea(coord, data); // If there is clipPath created in large mode. Remove it.\n\n        group.removeClipPath(); // We don't use clipPath in normal mode because we needs a perfect animation\n        // And don't want the label are clipped.\n\n        var roundCap = seriesModel.get('roundCap', true);\n        var drawBackground = seriesModel.get('showBackground', true);\n        var backgroundModel = seriesModel.getModel('backgroundStyle');\n        var barBorderRadius = backgroundModel.get('borderRadius') || 0;\n        var bgEls = [];\n        var oldBgEls = this._backgroundEls;\n        var isInitSort = payload && payload.isInitSort;\n        var isChangeOrder = payload && payload.type === 'changeAxisOrder';\n\n        function createBackground(dataIndex) {\n          var bgLayout = getLayout[coord.type](data, dataIndex);\n          var bgEl = createBackgroundEl(coord, isHorizontalOrRadial, bgLayout);\n          bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n          if (coord.type === 'cartesian2d') {\n            bgEl.setShape('r', barBorderRadius);\n          }\n\n          bgEls[dataIndex] = bgEl;\n          return bgEl;\n        }\n        data.diff(oldData).add(function (dataIndex) {\n          var itemModel = data.getItemModel(dataIndex);\n          var layout = getLayout[coord.type](data, dataIndex, itemModel);\n\n          if (drawBackground) {\n            createBackground(dataIndex);\n          } // If dataZoom in filteMode: 'empty', the baseValue can be set as NaN in \"axisProxy\".\n\n\n          if (!data.hasValue(dataIndex) || !isValidLayout[coord.type](layout)) {\n            return;\n          }\n\n          var isClipped = false;\n\n          if (needsClip) {\n            // Clip will modify the layout params.\n            // And return a boolean to determine if the shape are fully clipped.\n            isClipped = clip[coord.type](coordSysClipArea, layout);\n          }\n\n          var el = elementCreator[coord.type](seriesModel, data, dataIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, false, roundCap);\n\n          if (realtimeSortCfg) {\n            /**\n             * Force label animation because even if the element is\n             * ignored because it's clipped, it may not be clipped after\n             * changing order. Then, if not using forceLabelAnimation,\n             * the label animation was never started, in which case,\n             * the label will be the final value and doesn't have label\n             * animation.\n             */\n            el.forceLabelAnimation = true;\n          }\n\n          updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n\n          if (isInitSort) {\n            el.attr({\n              shape: layout\n            });\n          } else if (realtimeSortCfg) {\n            updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, dataIndex, isHorizontalOrRadial, false, false);\n          } else {\n            initProps(el, {\n              shape: layout\n            }, seriesModel, dataIndex);\n          }\n\n          data.setItemGraphicEl(dataIndex, el);\n          group.add(el);\n          el.ignore = isClipped;\n        }).update(function (newIndex, oldIndex) {\n          var itemModel = data.getItemModel(newIndex);\n          var layout = getLayout[coord.type](data, newIndex, itemModel);\n\n          if (drawBackground) {\n            var bgEl = void 0;\n\n            if (oldBgEls.length === 0) {\n              bgEl = createBackground(oldIndex);\n            } else {\n              bgEl = oldBgEls[oldIndex];\n              bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n              if (coord.type === 'cartesian2d') {\n                bgEl.setShape('r', barBorderRadius);\n              }\n\n              bgEls[newIndex] = bgEl;\n            }\n\n            var bgLayout = getLayout[coord.type](data, newIndex);\n            var shape = createBackgroundShape(isHorizontalOrRadial, bgLayout, coord);\n            updateProps(bgEl, {\n              shape: shape\n            }, animationModel, newIndex);\n          }\n\n          var el = oldData.getItemGraphicEl(oldIndex);\n\n          if (!data.hasValue(newIndex) || !isValidLayout[coord.type](layout)) {\n            group.remove(el);\n            return;\n          }\n\n          var isClipped = false;\n\n          if (needsClip) {\n            isClipped = clip[coord.type](coordSysClipArea, layout);\n\n            if (isClipped) {\n              group.remove(el);\n            }\n          }\n\n          if (!el) {\n            el = elementCreator[coord.type](seriesModel, data, newIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, !!el, roundCap);\n          } else {\n            saveOldStyle(el);\n          }\n\n          if (realtimeSortCfg) {\n            el.forceLabelAnimation = true;\n          }\n\n          if (isChangeOrder) {\n            var textEl = el.getTextContent();\n\n            if (textEl) {\n              var labelInnerStore = labelInner(textEl);\n\n              if (labelInnerStore.prevValue != null) {\n                /**\n                 * Set preValue to be value so that no new label\n                 * should be started, otherwise, it will take a full\n                 * `animationDurationUpdate` time to finish the\n                 * animation, which is not expected.\n                 */\n                labelInnerStore.prevValue = labelInnerStore.value;\n              }\n            }\n          } // Not change anything if only order changed.\n          // Especially not change label.\n          else {\n              updateStyle(el, data, newIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n            }\n\n          if (isInitSort) {\n            el.attr({\n              shape: layout\n            });\n          } else if (realtimeSortCfg) {\n            updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, newIndex, isHorizontalOrRadial, true, isChangeOrder);\n          } else {\n            updateProps(el, {\n              shape: layout\n            }, seriesModel, newIndex, null);\n          }\n\n          data.setItemGraphicEl(newIndex, el);\n          el.ignore = isClipped;\n          group.add(el);\n        }).remove(function (dataIndex) {\n          var el = oldData.getItemGraphicEl(dataIndex);\n          el && removeElementWithFadeOut(el, seriesModel, dataIndex);\n        }).execute();\n        var bgGroup = this._backgroundGroup || (this._backgroundGroup = new Group());\n        bgGroup.removeAll();\n\n        for (var i = 0; i < bgEls.length; ++i) {\n          bgGroup.add(bgEls[i]);\n        }\n\n        group.add(bgGroup);\n        this._backgroundEls = bgEls;\n        this._data = data;\n      };\n\n      BarView.prototype._renderLarge = function (seriesModel, ecModel, api) {\n        this._clear();\n\n        createLarge(seriesModel, this.group);\n\n        this._updateLargeClip(seriesModel);\n      };\n\n      BarView.prototype._incrementalRenderLarge = function (params, seriesModel) {\n        this._removeBackground();\n\n        createLarge(seriesModel, this.group, this._progressiveEls, true);\n      };\n\n      BarView.prototype._updateLargeClip = function (seriesModel) {\n        // Use clipPath in large mode.\n        var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel);\n        var group = this.group;\n\n        if (clipPath) {\n          group.setClipPath(clipPath);\n        } else {\n          group.removeClipPath();\n        }\n      };\n\n      BarView.prototype._enableRealtimeSort = function (realtimeSortCfg, data, api) {\n        var _this = this; // If no data in the first frame, wait for data to initSort\n\n\n        if (!data.count()) {\n          return;\n        }\n\n        var baseAxis = realtimeSortCfg.baseAxis;\n\n        if (this._isFirstFrame) {\n          this._dispatchInitSort(data, realtimeSortCfg, api);\n\n          this._isFirstFrame = false;\n        } else {\n          var orderMapping_1 = function (idx) {\n            var el = data.getItemGraphicEl(idx);\n            var shape = el && el.shape;\n            return shape && // The result should be consistent with the initial sort by data value.\n            // Do not support the case that both positive and negative exist.\n            Math.abs(baseAxis.isHorizontal() ? shape.height : shape.width) // If data is NaN, shape.xxx may be NaN, so use || 0 here in case\n            || 0;\n          };\n\n          this._onRendered = function () {\n            _this._updateSortWithinSameData(data, orderMapping_1, baseAxis, api);\n          };\n\n          api.getZr().on('rendered', this._onRendered);\n        }\n      };\n\n      BarView.prototype._dataSort = function (data, baseAxis, orderMapping) {\n        var info = [];\n        data.each(data.mapDimension(baseAxis.dim), function (ordinalNumber, dataIdx) {\n          var mappedValue = orderMapping(dataIdx);\n          mappedValue = mappedValue == null ? NaN : mappedValue;\n          info.push({\n            dataIndex: dataIdx,\n            mappedValue: mappedValue,\n            ordinalNumber: ordinalNumber\n          });\n        });\n        info.sort(function (a, b) {\n          // If NaN, it will be treated as min val.\n          return b.mappedValue - a.mappedValue;\n        });\n        return {\n          ordinalNumbers: map(info, function (item) {\n            return item.ordinalNumber;\n          })\n        };\n      };\n\n      BarView.prototype._isOrderChangedWithinSameData = function (data, orderMapping, baseAxis) {\n        var scale = baseAxis.scale;\n        var ordinalDataDim = data.mapDimension(baseAxis.dim);\n        var lastValue = Number.MAX_VALUE;\n\n        for (var tickNum = 0, len = scale.getOrdinalMeta().categories.length; tickNum < len; ++tickNum) {\n          var rawIdx = data.rawIndexOf(ordinalDataDim, scale.getRawOrdinalNumber(tickNum));\n          var value = rawIdx < 0 // If some tick have no bar, the tick will be treated as min.\n          ? Number.MIN_VALUE // PENDING: if dataZoom on baseAxis exits, is it a performance issue?\n          : orderMapping(data.indexOfRawIndex(rawIdx));\n\n          if (value > lastValue) {\n            return true;\n          }\n\n          lastValue = value;\n        }\n\n        return false;\n      };\n      /*\n       * Consider the case when A and B changed order, whose representing\n       * bars are both out of sight, we don't wish to trigger reorder action\n       * as long as the order in the view doesn't change.\n       */\n\n\n      BarView.prototype._isOrderDifferentInView = function (orderInfo, baseAxis) {\n        var scale = baseAxis.scale;\n        var extent = scale.getExtent();\n        var tickNum = Math.max(0, extent[0]);\n        var tickMax = Math.min(extent[1], scale.getOrdinalMeta().categories.length - 1);\n\n        for (; tickNum <= tickMax; ++tickNum) {\n          if (orderInfo.ordinalNumbers[tickNum] !== scale.getRawOrdinalNumber(tickNum)) {\n            return true;\n          }\n        }\n      };\n\n      BarView.prototype._updateSortWithinSameData = function (data, orderMapping, baseAxis, api) {\n        if (!this._isOrderChangedWithinSameData(data, orderMapping, baseAxis)) {\n          return;\n        }\n\n        var sortInfo = this._dataSort(data, baseAxis, orderMapping);\n\n        if (this._isOrderDifferentInView(sortInfo, baseAxis)) {\n          this._removeOnRenderedListener(api);\n\n          api.dispatchAction({\n            type: 'changeAxisOrder',\n            componentType: baseAxis.dim + 'Axis',\n            axisId: baseAxis.index,\n            sortInfo: sortInfo\n          });\n        }\n      };\n\n      BarView.prototype._dispatchInitSort = function (data, realtimeSortCfg, api) {\n        var baseAxis = realtimeSortCfg.baseAxis;\n\n        var sortResult = this._dataSort(data, baseAxis, function (dataIdx) {\n          return data.get(data.mapDimension(realtimeSortCfg.otherAxis.dim), dataIdx);\n        });\n\n        api.dispatchAction({\n          type: 'changeAxisOrder',\n          componentType: baseAxis.dim + 'Axis',\n          isInitSort: true,\n          axisId: baseAxis.index,\n          sortInfo: sortResult\n        });\n      };\n\n      BarView.prototype.remove = function (ecModel, api) {\n        this._clear(this._model);\n\n        this._removeOnRenderedListener(api);\n      };\n\n      BarView.prototype.dispose = function (ecModel, api) {\n        this._removeOnRenderedListener(api);\n      };\n\n      BarView.prototype._removeOnRenderedListener = function (api) {\n        if (this._onRendered) {\n          api.getZr().off('rendered', this._onRendered);\n          this._onRendered = null;\n        }\n      };\n\n      BarView.prototype._clear = function (model) {\n        var group = this.group;\n        var data = this._data;\n\n        if (model && model.isAnimationEnabled() && data && !this._isLargeDraw) {\n          this._removeBackground();\n\n          this._backgroundEls = [];\n          data.eachItemGraphicEl(function (el) {\n            removeElementWithFadeOut(el, model, getECData(el).dataIndex);\n          });\n        } else {\n          group.removeAll();\n        }\n\n        this._data = null;\n        this._isFirstFrame = true;\n      };\n\n      BarView.prototype._removeBackground = function () {\n        this.group.remove(this._backgroundGroup);\n        this._backgroundGroup = null;\n      };\n\n      BarView.type = 'bar';\n      return BarView;\n    }(ChartView);\n\n    var clip = {\n      cartesian2d: function (coordSysBoundingRect, layout) {\n        var signWidth = layout.width < 0 ? -1 : 1;\n        var signHeight = layout.height < 0 ? -1 : 1; // Needs positive width and height\n\n        if (signWidth < 0) {\n          layout.x += layout.width;\n          layout.width = -layout.width;\n        }\n\n        if (signHeight < 0) {\n          layout.y += layout.height;\n          layout.height = -layout.height;\n        }\n\n        var coordSysX2 = coordSysBoundingRect.x + coordSysBoundingRect.width;\n        var coordSysY2 = coordSysBoundingRect.y + coordSysBoundingRect.height;\n        var x = mathMax$6(layout.x, coordSysBoundingRect.x);\n        var x2 = mathMin$6(layout.x + layout.width, coordSysX2);\n        var y = mathMax$6(layout.y, coordSysBoundingRect.y);\n        var y2 = mathMin$6(layout.y + layout.height, coordSysY2);\n        var xClipped = x2 < x;\n        var yClipped = y2 < y; // When xClipped or yClipped, the element will be marked as `ignore`.\n        // But we should also place the element at the edge of the coord sys bounding rect.\n        // Because if data changed and the bar shows again, its transition animation\n        // will begin at this place.\n\n        layout.x = xClipped && x > coordSysX2 ? x2 : x;\n        layout.y = yClipped && y > coordSysY2 ? y2 : y;\n        layout.width = xClipped ? 0 : x2 - x;\n        layout.height = yClipped ? 0 : y2 - y; // Reverse back\n\n        if (signWidth < 0) {\n          layout.x += layout.width;\n          layout.width = -layout.width;\n        }\n\n        if (signHeight < 0) {\n          layout.y += layout.height;\n          layout.height = -layout.height;\n        }\n\n        return xClipped || yClipped;\n      },\n      polar: function (coordSysClipArea, layout) {\n        var signR = layout.r0 <= layout.r ? 1 : -1; // Make sure r is larger than r0\n\n        if (signR < 0) {\n          var tmp = layout.r;\n          layout.r = layout.r0;\n          layout.r0 = tmp;\n        }\n\n        var r = mathMin$6(layout.r, coordSysClipArea.r);\n        var r0 = mathMax$6(layout.r0, coordSysClipArea.r0);\n        layout.r = r;\n        layout.r0 = r0;\n        var clipped = r - r0 < 0; // Reverse back\n\n        if (signR < 0) {\n          var tmp = layout.r;\n          layout.r = layout.r0;\n          layout.r0 = tmp;\n        }\n\n        return clipped;\n      }\n    };\n    var elementCreator = {\n      cartesian2d: function (seriesModel, data, newIndex, layout, isHorizontal, animationModel, axisModel, isUpdate, roundCap) {\n        var rect = new Rect({\n          shape: extend({}, layout),\n          z2: 1\n        });\n        rect.__dataIndex = newIndex;\n        rect.name = 'item';\n\n        if (animationModel) {\n          var rectShape = rect.shape;\n          var animateProperty = isHorizontal ? 'height' : 'width';\n          rectShape[animateProperty] = 0;\n        }\n\n        return rect;\n      },\n      polar: function (seriesModel, data, newIndex, layout, isRadial, animationModel, axisModel, isUpdate, roundCap) {\n        var ShapeClass = !isRadial && roundCap ? SausagePath : Sector;\n        var sector = new ShapeClass({\n          shape: layout,\n          z2: 1\n        });\n        sector.name = 'item';\n        var positionMap = createPolarPositionMapping(isRadial);\n        sector.calculateTextPosition = createSectorCalculateTextPosition(positionMap, {\n          isRoundCap: ShapeClass === SausagePath\n        }); // Animation\n\n        if (animationModel) {\n          var sectorShape = sector.shape;\n          var animateProperty = isRadial ? 'r' : 'endAngle';\n          var animateTarget = {};\n          sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;\n          animateTarget[animateProperty] = layout[animateProperty];\n          (isUpdate ? updateProps : initProps)(sector, {\n            shape: animateTarget // __value: typeof dataValue === 'string' ? parseInt(dataValue, 10) : dataValue\n\n          }, animationModel);\n        }\n\n        return sector;\n      }\n    };\n\n    function shouldRealtimeSort(seriesModel, coordSys) {\n      var realtimeSortOption = seriesModel.get('realtimeSort', true);\n      var baseAxis = coordSys.getBaseAxis();\n\n      if (\"development\" !== 'production') {\n        if (realtimeSortOption) {\n          if (baseAxis.type !== 'category') {\n            warn('`realtimeSort` will not work because this bar series is not based on a category axis.');\n          }\n\n          if (coordSys.type !== 'cartesian2d') {\n            warn('`realtimeSort` will not work because this bar series is not on cartesian2d.');\n          }\n        }\n      }\n\n      if (realtimeSortOption && baseAxis.type === 'category' && coordSys.type === 'cartesian2d') {\n        return {\n          baseAxis: baseAxis,\n          otherAxis: coordSys.getOtherAxis(baseAxis)\n        };\n      }\n    }\n\n    function updateRealtimeAnimation(realtimeSortCfg, seriesAnimationModel, el, layout, newIndex, isHorizontal, isUpdate, isChangeOrder) {\n      var seriesTarget;\n      var axisTarget;\n\n      if (isHorizontal) {\n        axisTarget = {\n          x: layout.x,\n          width: layout.width\n        };\n        seriesTarget = {\n          y: layout.y,\n          height: layout.height\n        };\n      } else {\n        axisTarget = {\n          y: layout.y,\n          height: layout.height\n        };\n        seriesTarget = {\n          x: layout.x,\n          width: layout.width\n        };\n      }\n\n      if (!isChangeOrder) {\n        // Keep the original growth animation if only axis order changed.\n        // Not start a new animation.\n        (isUpdate ? updateProps : initProps)(el, {\n          shape: seriesTarget\n        }, seriesAnimationModel, newIndex, null);\n      }\n\n      var axisAnimationModel = seriesAnimationModel ? realtimeSortCfg.baseAxis.model : null;\n      (isUpdate ? updateProps : initProps)(el, {\n        shape: axisTarget\n      }, axisAnimationModel, newIndex);\n    }\n\n    function checkPropertiesNotValid(obj, props) {\n      for (var i = 0; i < props.length; i++) {\n        if (!isFinite(obj[props[i]])) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    var rectPropties = ['x', 'y', 'width', 'height'];\n    var polarPropties = ['cx', 'cy', 'r', 'startAngle', 'endAngle'];\n    var isValidLayout = {\n      cartesian2d: function (layout) {\n        return !checkPropertiesNotValid(layout, rectPropties);\n      },\n      polar: function (layout) {\n        return !checkPropertiesNotValid(layout, polarPropties);\n      }\n    };\n    var getLayout = {\n      // itemModel is only used to get borderWidth, which is not needed\n      // when calculating bar background layout.\n      cartesian2d: function (data, dataIndex, itemModel) {\n        var layout = data.getItemLayout(dataIndex);\n        var fixedLineWidth = itemModel ? getLineWidth(itemModel, layout) : 0; // fix layout with lineWidth\n\n        var signX = layout.width > 0 ? 1 : -1;\n        var signY = layout.height > 0 ? 1 : -1;\n        return {\n          x: layout.x + signX * fixedLineWidth / 2,\n          y: layout.y + signY * fixedLineWidth / 2,\n          width: layout.width - signX * fixedLineWidth,\n          height: layout.height - signY * fixedLineWidth\n        };\n      },\n      polar: function (data, dataIndex, itemModel) {\n        var layout = data.getItemLayout(dataIndex);\n        return {\n          cx: layout.cx,\n          cy: layout.cy,\n          r0: layout.r0,\n          r: layout.r,\n          startAngle: layout.startAngle,\n          endAngle: layout.endAngle,\n          clockwise: layout.clockwise\n        };\n      }\n    };\n\n    function isZeroOnPolar(layout) {\n      return layout.startAngle != null && layout.endAngle != null && layout.startAngle === layout.endAngle;\n    }\n\n    function createPolarPositionMapping(isRadial) {\n      return function (isRadial) {\n        var arcOrAngle = isRadial ? 'Arc' : 'Angle';\n        return function (position) {\n          switch (position) {\n            case 'start':\n            case 'insideStart':\n            case 'end':\n            case 'insideEnd':\n              return position + arcOrAngle;\n\n            default:\n              return position;\n          }\n        };\n      }(isRadial);\n    }\n\n    function updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, isPolar) {\n      var style = data.getItemVisual(dataIndex, 'style');\n\n      if (!isPolar) {\n        el.setShape('r', itemModel.get(['itemStyle', 'borderRadius']) || 0);\n      }\n\n      el.useStyle(style);\n      var cursorStyle = itemModel.getShallow('cursor');\n      cursorStyle && el.attr('cursor', cursorStyle);\n      var labelPositionOutside = isPolar ? isHorizontalOrRadial ? layout.r >= layout.r0 ? 'endArc' : 'startArc' : layout.endAngle >= layout.startAngle ? 'endAngle' : 'startAngle' : isHorizontalOrRadial ? layout.height >= 0 ? 'bottom' : 'top' : layout.width >= 0 ? 'right' : 'left';\n      var labelStatesModels = getLabelStatesModels(itemModel);\n      setLabelStyle(el, labelStatesModels, {\n        labelFetcher: seriesModel,\n        labelDataIndex: dataIndex,\n        defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),\n        inheritColor: style.fill,\n        defaultOpacity: style.opacity,\n        defaultOutsidePosition: labelPositionOutside\n      });\n      var label = el.getTextContent();\n\n      if (isPolar && label) {\n        var position = itemModel.get(['label', 'position']);\n        el.textConfig.inside = position === 'middle' ? true : null;\n        setSectorTextRotation(el, position === 'outside' ? labelPositionOutside : position, createPolarPositionMapping(isHorizontalOrRadial), itemModel.get(['label', 'rotate']));\n      }\n\n      setLabelValueAnimation(label, labelStatesModels, seriesModel.getRawValue(dataIndex), function (value) {\n        return getDefaultInterpolatedLabel(data, value);\n      });\n      var emphasisModel = itemModel.getModel(['emphasis']);\n      toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      setStatesStylesFromModel(el, itemModel);\n\n      if (isZeroOnPolar(layout)) {\n        el.style.fill = 'none';\n        el.style.stroke = 'none';\n        each(el.states, function (state) {\n          if (state.style) {\n            state.style.fill = state.style.stroke = 'none';\n          }\n        });\n      }\n    } // In case width or height are too small.\n\n\n    function getLineWidth(itemModel, rawLayout) {\n      // Has no border.\n      var borderColor = itemModel.get(['itemStyle', 'borderColor']);\n\n      if (!borderColor || borderColor === 'none') {\n        return 0;\n      }\n\n      var lineWidth = itemModel.get(['itemStyle', 'borderWidth']) || 0; // width or height may be NaN for empty data\n\n      var width = isNaN(rawLayout.width) ? Number.MAX_VALUE : Math.abs(rawLayout.width);\n      var height = isNaN(rawLayout.height) ? Number.MAX_VALUE : Math.abs(rawLayout.height);\n      return Math.min(lineWidth, width, height);\n    }\n\n    var LagePathShape =\n    /** @class */\n    function () {\n      function LagePathShape() {}\n\n      return LagePathShape;\n    }();\n\n    var LargePath =\n    /** @class */\n    function (_super) {\n      __extends(LargePath, _super);\n\n      function LargePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'largeBar';\n        return _this;\n      }\n\n      LargePath.prototype.getDefaultShape = function () {\n        return new LagePathShape();\n      };\n\n      LargePath.prototype.buildPath = function (ctx, shape) {\n        // Drawing lines is more efficient than drawing\n        // a whole line or drawing rects.\n        var points = shape.points;\n        var baseDimIdx = this.baseDimIdx;\n        var valueDimIdx = 1 - this.baseDimIdx;\n        var startPoint = [];\n        var size = [];\n        var barWidth = this.barWidth;\n\n        for (var i = 0; i < points.length; i += 3) {\n          size[baseDimIdx] = barWidth;\n          size[valueDimIdx] = points[i + 2];\n          startPoint[baseDimIdx] = points[i + baseDimIdx];\n          startPoint[valueDimIdx] = points[i + valueDimIdx];\n          ctx.rect(startPoint[0], startPoint[1], size[0], size[1]);\n        }\n      };\n\n      return LargePath;\n    }(Path);\n\n    function createLarge(seriesModel, group, progressiveEls, incremental) {\n      // TODO support polar\n      var data = seriesModel.getData();\n      var baseDimIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0;\n      var largeDataIndices = data.getLayout('largeDataIndices');\n      var barWidth = data.getLayout('size');\n      var backgroundModel = seriesModel.getModel('backgroundStyle');\n      var bgPoints = data.getLayout('largeBackgroundPoints');\n\n      if (bgPoints) {\n        var bgEl = new LargePath({\n          shape: {\n            points: bgPoints\n          },\n          incremental: !!incremental,\n          silent: true,\n          z2: 0\n        });\n        bgEl.baseDimIdx = baseDimIdx;\n        bgEl.largeDataIndices = largeDataIndices;\n        bgEl.barWidth = barWidth;\n        bgEl.useStyle(backgroundModel.getItemStyle());\n        group.add(bgEl);\n        progressiveEls && progressiveEls.push(bgEl);\n      }\n\n      var el = new LargePath({\n        shape: {\n          points: data.getLayout('largePoints')\n        },\n        incremental: !!incremental,\n        ignoreCoarsePointer: true,\n        z2: 1\n      });\n      el.baseDimIdx = baseDimIdx;\n      el.largeDataIndices = largeDataIndices;\n      el.barWidth = barWidth;\n      group.add(el);\n      el.useStyle(data.getVisual('style')); // Enable tooltip and user mouse/touch event handlers.\n\n      getECData(el).seriesIndex = seriesModel.seriesIndex;\n\n      if (!seriesModel.get('silent')) {\n        el.on('mousedown', largePathUpdateDataIndex);\n        el.on('mousemove', largePathUpdateDataIndex);\n      }\n\n      progressiveEls && progressiveEls.push(el);\n    } // Use throttle to avoid frequently traverse to find dataIndex.\n\n\n    var largePathUpdateDataIndex = throttle(function (event) {\n      var largePath = this;\n      var dataIndex = largePathFindDataIndex(largePath, event.offsetX, event.offsetY);\n      getECData(largePath).dataIndex = dataIndex >= 0 ? dataIndex : null;\n    }, 30, false);\n\n    function largePathFindDataIndex(largePath, x, y) {\n      var baseDimIdx = largePath.baseDimIdx;\n      var valueDimIdx = 1 - baseDimIdx;\n      var points = largePath.shape.points;\n      var largeDataIndices = largePath.largeDataIndices;\n      var startPoint = [];\n      var size = [];\n      var barWidth = largePath.barWidth;\n\n      for (var i = 0, len = points.length / 3; i < len; i++) {\n        var ii = i * 3;\n        size[baseDimIdx] = barWidth;\n        size[valueDimIdx] = points[ii + 2];\n        startPoint[baseDimIdx] = points[ii + baseDimIdx];\n        startPoint[valueDimIdx] = points[ii + valueDimIdx];\n\n        if (size[valueDimIdx] < 0) {\n          startPoint[valueDimIdx] += size[valueDimIdx];\n          size[valueDimIdx] = -size[valueDimIdx];\n        }\n\n        if (x >= startPoint[0] && x <= startPoint[0] + size[0] && y >= startPoint[1] && y <= startPoint[1] + size[1]) {\n          return largeDataIndices[i];\n        }\n      }\n\n      return -1;\n    }\n\n    function createBackgroundShape(isHorizontalOrRadial, layout, coord) {\n      if (isCoordinateSystemType(coord, 'cartesian2d')) {\n        var rectShape = layout;\n        var coordLayout = coord.getArea();\n        return {\n          x: isHorizontalOrRadial ? rectShape.x : coordLayout.x,\n          y: isHorizontalOrRadial ? coordLayout.y : rectShape.y,\n          width: isHorizontalOrRadial ? rectShape.width : coordLayout.width,\n          height: isHorizontalOrRadial ? coordLayout.height : rectShape.height\n        };\n      } else {\n        var coordLayout = coord.getArea();\n        var sectorShape = layout;\n        return {\n          cx: coordLayout.cx,\n          cy: coordLayout.cy,\n          r0: isHorizontalOrRadial ? coordLayout.r0 : sectorShape.r0,\n          r: isHorizontalOrRadial ? coordLayout.r : sectorShape.r,\n          startAngle: isHorizontalOrRadial ? sectorShape.startAngle : 0,\n          endAngle: isHorizontalOrRadial ? sectorShape.endAngle : Math.PI * 2\n        };\n      }\n    }\n\n    function createBackgroundEl(coord, isHorizontalOrRadial, layout) {\n      var ElementClz = coord.type === 'polar' ? Sector : Rect;\n      return new ElementClz({\n        shape: createBackgroundShape(isHorizontalOrRadial, layout, coord),\n        silent: true,\n        z2: 0\n      });\n    }\n\n    function install$3(registers) {\n      registers.registerChartView(BarView);\n      registers.registerSeriesModel(BarSeriesModel);\n      registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'bar')); // Do layout after other overall layout, which can prepare some information.\n\n      registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('bar')); // Down sample after filter\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('bar'));\n      /**\n       * @payload\n       * @property {string} [componentType=series]\n       * @property {number} [dx]\n       * @property {number} [dy]\n       * @property {number} [zoom]\n       * @property {number} [originX]\n       * @property {number} [originY]\n       */\n\n      registers.registerAction({\n        type: 'changeAxisOrder',\n        event: 'changeAxisOrder',\n        update: 'update'\n      }, function (payload, ecModel) {\n        var componentType = payload.componentType || 'series';\n        ecModel.eachComponent({\n          mainType: componentType,\n          query: payload\n        }, function (componentModel) {\n          if (payload.sortInfo) {\n            componentModel.axis.setCategorySortInfo(payload.sortInfo);\n          }\n        });\n      });\n    }\n\n    var PI2$7 = Math.PI * 2;\n    var RADIAN = Math.PI / 180;\n\n    function getViewRect(seriesModel, api) {\n      return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n        width: api.getWidth(),\n        height: api.getHeight()\n      });\n    }\n\n    function getBasicPieLayout(seriesModel, api) {\n      var viewRect = getViewRect(seriesModel, api); // center can be string or number when coordinateSystem is specified\n\n      var center = seriesModel.get('center');\n      var radius = seriesModel.get('radius');\n\n      if (!isArray(radius)) {\n        radius = [0, radius];\n      }\n\n      var width = parsePercent$1(viewRect.width, api.getWidth());\n      var height = parsePercent$1(viewRect.height, api.getHeight());\n      var size = Math.min(width, height);\n      var r0 = parsePercent$1(radius[0], size / 2);\n      var r = parsePercent$1(radius[1], size / 2);\n      var cx;\n      var cy;\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (coordSys) {\n        // percentage is not allowed when coordinate system is specified\n        var point = coordSys.dataToPoint(center);\n        cx = point[0] || 0;\n        cy = point[1] || 0;\n      } else {\n        if (!isArray(center)) {\n          center = [center, center];\n        }\n\n        cx = parsePercent$1(center[0], width) + viewRect.x;\n        cy = parsePercent$1(center[1], height) + viewRect.y;\n      }\n\n      return {\n        cx: cx,\n        cy: cy,\n        r0: r0,\n        r: r\n      };\n    }\n    function pieLayout(seriesType, ecModel, api) {\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        var data = seriesModel.getData();\n        var valueDim = data.mapDimension('value');\n        var viewRect = getViewRect(seriesModel, api);\n\n        var _a = getBasicPieLayout(seriesModel, api),\n            cx = _a.cx,\n            cy = _a.cy,\n            r = _a.r,\n            r0 = _a.r0;\n\n        var startAngle = -seriesModel.get('startAngle') * RADIAN;\n        var minAngle = seriesModel.get('minAngle') * RADIAN;\n        var validDataCount = 0;\n        data.each(valueDim, function (value) {\n          !isNaN(value) && validDataCount++;\n        });\n        var sum = data.getSum(valueDim); // Sum may be 0\n\n        var unitRadian = Math.PI / (sum || validDataCount) * 2;\n        var clockwise = seriesModel.get('clockwise');\n        var roseType = seriesModel.get('roseType');\n        var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); // [0...max]\n\n        var extent = data.getDataExtent(valueDim);\n        extent[0] = 0; // In the case some sector angle is smaller than minAngle\n\n        var restAngle = PI2$7;\n        var valueSumLargerThanMinAngle = 0;\n        var currentAngle = startAngle;\n        var dir = clockwise ? 1 : -1;\n        data.setLayout({\n          viewRect: viewRect,\n          r: r\n        });\n        data.each(valueDim, function (value, idx) {\n          var angle;\n\n          if (isNaN(value)) {\n            data.setItemLayout(idx, {\n              angle: NaN,\n              startAngle: NaN,\n              endAngle: NaN,\n              clockwise: clockwise,\n              cx: cx,\n              cy: cy,\n              r0: r0,\n              r: roseType ? NaN : r\n            });\n            return;\n          } // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样？\n\n\n          if (roseType !== 'area') {\n            angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian;\n          } else {\n            angle = PI2$7 / validDataCount;\n          }\n\n          if (angle < minAngle) {\n            angle = minAngle;\n            restAngle -= minAngle;\n          } else {\n            valueSumLargerThanMinAngle += value;\n          }\n\n          var endAngle = currentAngle + dir * angle;\n          data.setItemLayout(idx, {\n            angle: angle,\n            startAngle: currentAngle,\n            endAngle: endAngle,\n            clockwise: clockwise,\n            cx: cx,\n            cy: cy,\n            r0: r0,\n            r: roseType ? linearMap(value, extent, [r0, r]) : r\n          });\n          currentAngle = endAngle;\n        }); // Some sector is constrained by minAngle\n        // Rest sectors needs recalculate angle\n\n        if (restAngle < PI2$7 && validDataCount) {\n          // Average the angle if rest angle is not enough after all angles is\n          // Constrained by minAngle\n          if (restAngle <= 1e-3) {\n            var angle_1 = PI2$7 / validDataCount;\n            data.each(valueDim, function (value, idx) {\n              if (!isNaN(value)) {\n                var layout_1 = data.getItemLayout(idx);\n                layout_1.angle = angle_1;\n                layout_1.startAngle = startAngle + dir * idx * angle_1;\n                layout_1.endAngle = startAngle + dir * (idx + 1) * angle_1;\n              }\n            });\n          } else {\n            unitRadian = restAngle / valueSumLargerThanMinAngle;\n            currentAngle = startAngle;\n            data.each(valueDim, function (value, idx) {\n              if (!isNaN(value)) {\n                var layout_2 = data.getItemLayout(idx);\n                var angle = layout_2.angle === minAngle ? minAngle : value * unitRadian;\n                layout_2.startAngle = currentAngle;\n                layout_2.endAngle = currentAngle + dir * angle;\n                currentAngle += dir * angle;\n              }\n            });\n          }\n        }\n      });\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function dataFilter(seriesType) {\n      return {\n        seriesType: seriesType,\n        reset: function (seriesModel, ecModel) {\n          var legendModels = ecModel.findComponents({\n            mainType: 'legend'\n          });\n\n          if (!legendModels || !legendModels.length) {\n            return;\n          }\n\n          var data = seriesModel.getData();\n          data.filterSelf(function (idx) {\n            var name = data.getName(idx); // If in any legend component the status is not selected.\n\n            for (var i = 0; i < legendModels.length; i++) {\n              // @ts-ignore FIXME: LegendModel\n              if (!legendModels[i].isSelected(name)) {\n                return false;\n              }\n            }\n\n            return true;\n          });\n        }\n      };\n    }\n\n    var RADIAN$1 = Math.PI / 180;\n\n    function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight, viewLeft, viewTop, farthestX) {\n      if (list.length < 2) {\n        return;\n      }\n\n      function recalculateXOnSemiToAlignOnEllipseCurve(semi) {\n        var rB = semi.rB;\n        var rB2 = rB * rB;\n\n        for (var i = 0; i < semi.list.length; i++) {\n          var item = semi.list[i];\n          var dy = Math.abs(item.label.y - cy); // horizontal r is always same with original r because x is not changed.\n\n          var rA = r + item.len;\n          var rA2 = rA * rA; // Use ellipse implicit function to calculate x\n\n          var dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2);\n          var newX = cx + (dx + item.len2) * dir;\n          var deltaX = newX - item.label.x;\n          var newTargetWidth = item.targetTextWidth - deltaX * dir; // text x is changed, so need to recalculate width.\n\n          constrainTextWidth(item, newTargetWidth, true);\n          item.label.x = newX;\n        }\n      } // Adjust X based on the shifted y. Make tight labels aligned on an ellipse curve.\n\n\n      function recalculateX(items) {\n        // Extremes of\n        var topSemi = {\n          list: [],\n          maxY: 0\n        };\n        var bottomSemi = {\n          list: [],\n          maxY: 0\n        };\n\n        for (var i = 0; i < items.length; i++) {\n          if (items[i].labelAlignTo !== 'none') {\n            continue;\n          }\n\n          var item = items[i];\n          var semi = item.label.y > cy ? bottomSemi : topSemi;\n          var dy = Math.abs(item.label.y - cy);\n\n          if (dy >= semi.maxY) {\n            var dx = item.label.x - cx - item.len2 * dir; // horizontal r is always same with original r because x is not changed.\n\n            var rA = r + item.len; // Canculate rB based on the topest / bottemest label.\n\n            var rB = Math.abs(dx) < rA ? Math.sqrt(dy * dy / (1 - dx * dx / rA / rA)) : rA;\n            semi.rB = rB;\n            semi.maxY = dy;\n          }\n\n          semi.list.push(item);\n        }\n\n        recalculateXOnSemiToAlignOnEllipseCurve(topSemi);\n        recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi);\n      }\n\n      var len = list.length;\n\n      for (var i = 0; i < len; i++) {\n        if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') {\n          var dx = list[i].label.x - farthestX;\n          list[i].linePoints[1][0] += dx;\n          list[i].label.x = farthestX;\n        }\n      }\n\n      if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) {\n        recalculateX(list);\n      }\n    }\n\n    function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop) {\n      var leftList = [];\n      var rightList = [];\n      var leftmostX = Number.MAX_VALUE;\n      var rightmostX = -Number.MAX_VALUE;\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var label = labelLayoutList[i].label;\n\n        if (isPositionCenter(labelLayoutList[i])) {\n          continue;\n        }\n\n        if (label.x < cx) {\n          leftmostX = Math.min(leftmostX, label.x);\n          leftList.push(labelLayoutList[i]);\n        } else {\n          rightmostX = Math.max(rightmostX, label.x);\n          rightList.push(labelLayoutList[i]);\n        }\n      }\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n\n        if (!isPositionCenter(layout) && layout.linePoints) {\n          if (layout.labelStyleWidth != null) {\n            continue;\n          }\n\n          var label = layout.label;\n          var linePoints = layout.linePoints;\n          var targetTextWidth = void 0;\n\n          if (layout.labelAlignTo === 'edge') {\n            if (label.x < cx) {\n              targetTextWidth = linePoints[2][0] - layout.labelDistance - viewLeft - layout.edgeDistance;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - layout.edgeDistance - linePoints[2][0] - layout.labelDistance;\n            }\n          } else if (layout.labelAlignTo === 'labelLine') {\n            if (label.x < cx) {\n              targetTextWidth = leftmostX - viewLeft - layout.bleedMargin;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - rightmostX - layout.bleedMargin;\n            }\n          } else {\n            if (label.x < cx) {\n              targetTextWidth = label.x - viewLeft - layout.bleedMargin;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin;\n            }\n          }\n\n          layout.targetTextWidth = targetTextWidth;\n          constrainTextWidth(layout, targetTextWidth);\n        }\n      }\n\n      adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX);\n      adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n\n        if (!isPositionCenter(layout) && layout.linePoints) {\n          var label = layout.label;\n          var linePoints = layout.linePoints;\n          var isAlignToEdge = layout.labelAlignTo === 'edge';\n          var padding = label.style.padding;\n          var paddingH = padding ? padding[1] + padding[3] : 0; // textRect.width already contains paddingH if bgColor is set\n\n          var extraPaddingH = label.style.backgroundColor ? 0 : paddingH;\n          var realTextWidth = layout.rect.width + extraPaddingH;\n          var dist = linePoints[1][0] - linePoints[2][0];\n\n          if (isAlignToEdge) {\n            if (label.x < cx) {\n              linePoints[2][0] = viewLeft + layout.edgeDistance + realTextWidth + layout.labelDistance;\n            } else {\n              linePoints[2][0] = viewLeft + viewWidth - layout.edgeDistance - realTextWidth - layout.labelDistance;\n            }\n          } else {\n            if (label.x < cx) {\n              linePoints[2][0] = label.x + layout.labelDistance;\n            } else {\n              linePoints[2][0] = label.x - layout.labelDistance;\n            }\n\n            linePoints[1][0] = linePoints[2][0] + dist;\n          }\n\n          linePoints[1][1] = linePoints[2][1] = label.y;\n        }\n      }\n    }\n    /**\n     * Set max width of each label, and then wrap each label to the max width.\n     *\n     * @param layout label layout\n     * @param availableWidth max width for the label to display\n     * @param forceRecalculate recaculate the text layout even if the current width\n     * is smaller than `availableWidth`. This is useful when the text was previously\n     * wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in\n     * which case, previous wrapping should be redo.\n     */\n\n\n    function constrainTextWidth(layout, availableWidth, forceRecalculate) {\n      if (forceRecalculate === void 0) {\n        forceRecalculate = false;\n      }\n\n      if (layout.labelStyleWidth != null) {\n        // User-defined style.width has the highest priority.\n        return;\n      }\n\n      var label = layout.label;\n      var style = label.style;\n      var textRect = layout.rect;\n      var bgColor = style.backgroundColor;\n      var padding = style.padding;\n      var paddingH = padding ? padding[1] + padding[3] : 0;\n      var overflow = style.overflow; // textRect.width already contains paddingH if bgColor is set\n\n      var oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH);\n\n      if (availableWidth < oldOuterWidth || forceRecalculate) {\n        var oldHeight = textRect.height;\n\n        if (overflow && overflow.match('break')) {\n          // Temporarily set background to be null to calculate\n          // the bounding box without background.\n          label.setStyle('backgroundColor', null); // Set constraining width\n\n          label.setStyle('width', availableWidth - paddingH); // This is the real bounding box of the text without padding.\n\n          var innerRect = label.getBoundingRect();\n          label.setStyle('width', Math.ceil(innerRect.width));\n          label.setStyle('backgroundColor', bgColor);\n        } else {\n          var availableInnerWidth = availableWidth - paddingH;\n          var newWidth = availableWidth < oldOuterWidth // Current text is too wide, use `availableWidth` as max width.\n          ? availableInnerWidth : // Current available width is enough, but the text may have\n          // already been wrapped with a smaller available width.\n          forceRecalculate ? availableInnerWidth > layout.unconstrainedWidth // Current available is larger than text width,\n          // so don't constrain width (otherwise it may have\n          // empty space in the background).\n          ? null // Current available is smaller than text width, so\n          // use the current available width as constraining\n          // width.\n          : availableInnerWidth : // Current available width is enough, so no need to\n          // constrain.\n          null;\n          label.setStyle('width', newWidth);\n        }\n\n        var newRect = label.getBoundingRect();\n        textRect.width = newRect.width;\n        var margin = (label.style.margin || 0) + 2.1;\n        textRect.height = newRect.height + margin;\n        textRect.y -= (textRect.height - oldHeight) / 2;\n      }\n    }\n\n    function isPositionCenter(sectorShape) {\n      // Not change x for center label\n      return sectorShape.position === 'center';\n    }\n\n    function pieLabelLayout(seriesModel) {\n      var data = seriesModel.getData();\n      var labelLayoutList = [];\n      var cx;\n      var cy;\n      var hasLabelRotate = false;\n      var minShowLabelRadian = (seriesModel.get('minShowLabelAngle') || 0) * RADIAN$1;\n      var viewRect = data.getLayout('viewRect');\n      var r = data.getLayout('r');\n      var viewWidth = viewRect.width;\n      var viewLeft = viewRect.x;\n      var viewTop = viewRect.y;\n      var viewHeight = viewRect.height;\n\n      function setNotShow(el) {\n        el.ignore = true;\n      }\n\n      function isLabelShown(label) {\n        if (!label.ignore) {\n          return true;\n        }\n\n        for (var key in label.states) {\n          if (label.states[key].ignore === false) {\n            return true;\n          }\n        }\n\n        return false;\n      }\n\n      data.each(function (idx) {\n        var sector = data.getItemGraphicEl(idx);\n        var sectorShape = sector.shape;\n        var label = sector.getTextContent();\n        var labelLine = sector.getTextGuideLine();\n        var itemModel = data.getItemModel(idx);\n        var labelModel = itemModel.getModel('label'); // Use position in normal or emphasis\n\n        var labelPosition = labelModel.get('position') || itemModel.get(['emphasis', 'label', 'position']);\n        var labelDistance = labelModel.get('distanceToLabelLine');\n        var labelAlignTo = labelModel.get('alignTo');\n        var edgeDistance = parsePercent$1(labelModel.get('edgeDistance'), viewWidth);\n        var bleedMargin = labelModel.get('bleedMargin');\n        var labelLineModel = itemModel.getModel('labelLine');\n        var labelLineLen = labelLineModel.get('length');\n        labelLineLen = parsePercent$1(labelLineLen, viewWidth);\n        var labelLineLen2 = labelLineModel.get('length2');\n        labelLineLen2 = parsePercent$1(labelLineLen2, viewWidth);\n\n        if (Math.abs(sectorShape.endAngle - sectorShape.startAngle) < minShowLabelRadian) {\n          each(label.states, setNotShow);\n          label.ignore = true;\n\n          if (labelLine) {\n            each(labelLine.states, setNotShow);\n            labelLine.ignore = true;\n          }\n\n          return;\n        }\n\n        if (!isLabelShown(label)) {\n          return;\n        }\n\n        var midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2;\n        var nx = Math.cos(midAngle);\n        var ny = Math.sin(midAngle);\n        var textX;\n        var textY;\n        var linePoints;\n        var textAlign;\n        cx = sectorShape.cx;\n        cy = sectorShape.cy;\n        var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';\n\n        if (labelPosition === 'center') {\n          textX = sectorShape.cx;\n          textY = sectorShape.cy;\n          textAlign = 'center';\n        } else {\n          var x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * nx : sectorShape.r * nx) + cx;\n          var y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * ny : sectorShape.r * ny) + cy;\n          textX = x1 + nx * 3;\n          textY = y1 + ny * 3;\n\n          if (!isLabelInside) {\n            // For roseType\n            var x2 = x1 + nx * (labelLineLen + r - sectorShape.r);\n            var y2 = y1 + ny * (labelLineLen + r - sectorShape.r);\n            var x3 = x2 + (nx < 0 ? -1 : 1) * labelLineLen2;\n            var y3 = y2;\n\n            if (labelAlignTo === 'edge') {\n              // Adjust textX because text align of edge is opposite\n              textX = nx < 0 ? viewLeft + edgeDistance : viewLeft + viewWidth - edgeDistance;\n            } else {\n              textX = x3 + (nx < 0 ? -labelDistance : labelDistance);\n            }\n\n            textY = y3;\n            linePoints = [[x1, y1], [x2, y2], [x3, y3]];\n          }\n\n          textAlign = isLabelInside ? 'center' : labelAlignTo === 'edge' ? nx > 0 ? 'right' : 'left' : nx > 0 ? 'left' : 'right';\n        }\n\n        var PI = Math.PI;\n        var labelRotate = 0;\n        var rotate = labelModel.get('rotate');\n\n        if (isNumber(rotate)) {\n          labelRotate = rotate * (PI / 180);\n        } else if (labelPosition === 'center') {\n          labelRotate = 0;\n        } else if (rotate === 'radial' || rotate === true) {\n          var radialAngle = nx < 0 ? -midAngle + PI : -midAngle;\n          labelRotate = radialAngle;\n        } else if (rotate === 'tangential' && labelPosition !== 'outside' && labelPosition !== 'outer') {\n          var rad = Math.atan2(nx, ny);\n\n          if (rad < 0) {\n            rad = PI * 2 + rad;\n          }\n\n          var isDown = ny > 0;\n\n          if (isDown) {\n            rad = PI + rad;\n          }\n\n          labelRotate = rad - PI;\n        }\n\n        hasLabelRotate = !!labelRotate;\n        label.x = textX;\n        label.y = textY;\n        label.rotation = labelRotate;\n        label.setStyle({\n          verticalAlign: 'middle'\n        }); // Not sectorShape the inside label\n\n        if (!isLabelInside) {\n          var textRect = label.getBoundingRect().clone();\n          textRect.applyTransform(label.getComputedTransform()); // Text has a default 1px stroke. Exclude this.\n\n          var margin = (label.style.margin || 0) + 2.1;\n          textRect.y -= margin / 2;\n          textRect.height += margin;\n          labelLayoutList.push({\n            label: label,\n            labelLine: labelLine,\n            position: labelPosition,\n            len: labelLineLen,\n            len2: labelLineLen2,\n            minTurnAngle: labelLineModel.get('minTurnAngle'),\n            maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'),\n            surfaceNormal: new Point(nx, ny),\n            linePoints: linePoints,\n            textAlign: textAlign,\n            labelDistance: labelDistance,\n            labelAlignTo: labelAlignTo,\n            edgeDistance: edgeDistance,\n            bleedMargin: bleedMargin,\n            rect: textRect,\n            unconstrainedWidth: textRect.width,\n            labelStyleWidth: label.style.width\n          });\n        } else {\n          label.setStyle({\n            align: textAlign\n          });\n          var selectState = label.states.select;\n\n          if (selectState) {\n            selectState.x += label.x;\n            selectState.y += label.y;\n          }\n        }\n\n        sector.setTextConfig({\n          inside: isLabelInside\n        });\n      });\n\n      if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {\n        avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop);\n      }\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n        var label = layout.label;\n        var labelLine = layout.labelLine;\n        var notShowLabel = isNaN(label.x) || isNaN(label.y);\n\n        if (label) {\n          label.setStyle({\n            align: layout.textAlign\n          });\n\n          if (notShowLabel) {\n            each(label.states, setNotShow);\n            label.ignore = true;\n          }\n\n          var selectState = label.states.select;\n\n          if (selectState) {\n            selectState.x += label.x;\n            selectState.y += label.y;\n          }\n        }\n\n        if (labelLine) {\n          var linePoints = layout.linePoints;\n\n          if (notShowLabel || !linePoints) {\n            each(labelLine.states, setNotShow);\n            labelLine.ignore = true;\n          } else {\n            limitTurnAngle(linePoints, layout.minTurnAngle);\n            limitSurfaceAngle(linePoints, layout.surfaceNormal, layout.maxSurfaceAngle);\n            labelLine.setShape({\n              points: linePoints\n            }); // Set the anchor to the midpoint of sector\n\n            label.__hostTarget.textGuideLineConfig = {\n              anchor: new Point(linePoints[0][0], linePoints[0][1])\n            };\n          }\n        }\n      }\n    }\n\n    function getSectorCornerRadius(model, shape, zeroIfNull) {\n      var cornerRadius = model.get('borderRadius');\n\n      if (cornerRadius == null) {\n        return zeroIfNull ? {\n          cornerRadius: 0\n        } : null;\n      }\n\n      if (!isArray(cornerRadius)) {\n        cornerRadius = [cornerRadius, cornerRadius, cornerRadius, cornerRadius];\n      }\n\n      var dr = Math.abs(shape.r || 0 - shape.r0 || 0);\n      return {\n        cornerRadius: map(cornerRadius, function (cr) {\n          return parsePercent(cr, dr);\n        })\n      };\n    }\n\n    /**\n     * Piece of pie including Sector, Label, LabelLine\n     */\n\n    var PiePiece =\n    /** @class */\n    function (_super) {\n      __extends(PiePiece, _super);\n\n      function PiePiece(data, idx, startAngle) {\n        var _this = _super.call(this) || this;\n\n        _this.z2 = 2;\n        var text = new ZRText();\n\n        _this.setTextContent(text);\n\n        _this.updateData(data, idx, startAngle, true);\n\n        return _this;\n      }\n\n      PiePiece.prototype.updateData = function (data, idx, startAngle, firstCreate) {\n        var sector = this;\n        var seriesModel = data.hostModel;\n        var itemModel = data.getItemModel(idx);\n        var emphasisModel = itemModel.getModel('emphasis');\n        var layout = data.getItemLayout(idx); // cornerRadius & innerCornerRadius doesn't exist in the item layout. Use `0` if null value is specified.\n        // see `setItemLayout` in `pieLayout.ts`.\n\n        var sectorShape = extend(getSectorCornerRadius(itemModel.getModel('itemStyle'), layout, true), layout); // Ignore NaN data.\n\n        if (isNaN(sectorShape.startAngle)) {\n          // Use NaN shape to avoid drawing shape.\n          sector.setShape(sectorShape);\n          return;\n        }\n\n        if (firstCreate) {\n          sector.setShape(sectorShape);\n          var animationType = seriesModel.getShallow('animationType');\n\n          if (seriesModel.ecModel.ssr) {\n            // Use scale animation in SSR mode(opacity?)\n            // Because CSS SVG animation doesn't support very customized shape animation.\n            initProps(sector, {\n              scaleX: 0,\n              scaleY: 0\n            }, seriesModel, {\n              dataIndex: idx,\n              isFrom: true\n            });\n            sector.originX = sectorShape.cx;\n            sector.originY = sectorShape.cy;\n          } else if (animationType === 'scale') {\n            sector.shape.r = layout.r0;\n            initProps(sector, {\n              shape: {\n                r: layout.r\n              }\n            }, seriesModel, idx);\n          } // Expansion\n          else {\n              if (startAngle != null) {\n                sector.setShape({\n                  startAngle: startAngle,\n                  endAngle: startAngle\n                });\n                initProps(sector, {\n                  shape: {\n                    startAngle: layout.startAngle,\n                    endAngle: layout.endAngle\n                  }\n                }, seriesModel, idx);\n              } else {\n                sector.shape.endAngle = layout.startAngle;\n                updateProps(sector, {\n                  shape: {\n                    endAngle: layout.endAngle\n                  }\n                }, seriesModel, idx);\n              }\n            }\n        } else {\n          saveOldStyle(sector); // Transition animation from the old shape\n\n          updateProps(sector, {\n            shape: sectorShape\n          }, seriesModel, idx);\n        }\n\n        sector.useStyle(data.getItemVisual(idx, 'style'));\n        setStatesStylesFromModel(sector, itemModel);\n        var midAngle = (layout.startAngle + layout.endAngle) / 2;\n        var offset = seriesModel.get('selectedOffset');\n        var dx = Math.cos(midAngle) * offset;\n        var dy = Math.sin(midAngle) * offset;\n        var cursorStyle = itemModel.getShallow('cursor');\n        cursorStyle && sector.attr('cursor', cursorStyle);\n\n        this._updateLabel(seriesModel, data, idx);\n\n        sector.ensureState('emphasis').shape = extend({\n          r: layout.r + (emphasisModel.get('scale') ? emphasisModel.get('scaleSize') || 0 : 0)\n        }, getSectorCornerRadius(emphasisModel.getModel('itemStyle'), layout));\n        extend(sector.ensureState('select'), {\n          x: dx,\n          y: dy,\n          shape: getSectorCornerRadius(itemModel.getModel(['select', 'itemStyle']), layout)\n        });\n        extend(sector.ensureState('blur'), {\n          shape: getSectorCornerRadius(itemModel.getModel(['blur', 'itemStyle']), layout)\n        });\n        var labelLine = sector.getTextGuideLine();\n        var labelText = sector.getTextContent();\n        labelLine && extend(labelLine.ensureState('select'), {\n          x: dx,\n          y: dy\n        }); // TODO: needs dx, dy in zrender?\n\n        extend(labelText.ensureState('select'), {\n          x: dx,\n          y: dy\n        });\n        toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      };\n\n      PiePiece.prototype._updateLabel = function (seriesModel, data, idx) {\n        var sector = this;\n        var itemModel = data.getItemModel(idx);\n        var labelLineModel = itemModel.getModel('labelLine');\n        var style = data.getItemVisual(idx, 'style');\n        var visualColor = style && style.fill;\n        var visualOpacity = style && style.opacity;\n        setLabelStyle(sector, getLabelStatesModels(itemModel), {\n          labelFetcher: data.hostModel,\n          labelDataIndex: idx,\n          inheritColor: visualColor,\n          defaultOpacity: visualOpacity,\n          defaultText: seriesModel.getFormattedLabel(idx, 'normal') || data.getName(idx)\n        });\n        var labelText = sector.getTextContent(); // Set textConfig on sector.\n\n        sector.setTextConfig({\n          // reset position, rotation\n          position: null,\n          rotation: null\n        }); // Make sure update style on labelText after setLabelStyle.\n        // Because setLabelStyle will replace a new style on it.\n\n        labelText.attr({\n          z2: 10\n        });\n        var labelPosition = seriesModel.get(['label', 'position']);\n\n        if (labelPosition !== 'outside' && labelPosition !== 'outer') {\n          sector.removeTextGuideLine();\n        } else {\n          var polyline = this.getTextGuideLine();\n\n          if (!polyline) {\n            polyline = new Polyline();\n            this.setTextGuideLine(polyline);\n          } // Default use item visual color\n\n\n          setLabelLineStyle(this, getLabelLineStatesModels(itemModel), {\n            stroke: visualColor,\n            opacity: retrieve3(labelLineModel.get(['lineStyle', 'opacity']), visualOpacity, 1)\n          });\n        }\n      };\n\n      return PiePiece;\n    }(Sector); // Pie view\n\n\n    var PieView =\n    /** @class */\n    function (_super) {\n      __extends(PieView, _super);\n\n      function PieView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.ignoreLabelLineUpdate = true;\n        return _this;\n      }\n\n      PieView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var group = this.group;\n        var startAngle; // First render\n\n        if (!oldData && data.count() > 0) {\n          var shape = data.getItemLayout(0);\n\n          for (var s = 1; isNaN(shape && shape.startAngle) && s < data.count(); ++s) {\n            shape = data.getItemLayout(s);\n          }\n\n          if (shape) {\n            startAngle = shape.startAngle;\n          }\n        } // remove empty-circle if it exists\n\n\n        if (this._emptyCircleSector) {\n          group.remove(this._emptyCircleSector);\n        } // when all data are filtered, show lightgray empty circle\n\n\n        if (data.count() === 0 && seriesModel.get('showEmptyCircle')) {\n          var sector = new Sector({\n            shape: getBasicPieLayout(seriesModel, api)\n          });\n          sector.useStyle(seriesModel.getModel('emptyCircleStyle').getItemStyle());\n          this._emptyCircleSector = sector;\n          group.add(sector);\n        }\n\n        data.diff(oldData).add(function (idx) {\n          var piePiece = new PiePiece(data, idx, startAngle);\n          data.setItemGraphicEl(idx, piePiece);\n          group.add(piePiece);\n        }).update(function (newIdx, oldIdx) {\n          var piePiece = oldData.getItemGraphicEl(oldIdx);\n          piePiece.updateData(data, newIdx, startAngle);\n          piePiece.off('click');\n          group.add(piePiece);\n          data.setItemGraphicEl(newIdx, piePiece);\n        }).remove(function (idx) {\n          var piePiece = oldData.getItemGraphicEl(idx);\n          removeElementWithFadeOut(piePiece, seriesModel, idx);\n        }).execute();\n        pieLabelLayout(seriesModel); // Always use initial animation.\n\n        if (seriesModel.get('animationTypeUpdate') !== 'expansion') {\n          this._data = data;\n        }\n      };\n\n      PieView.prototype.dispose = function () {};\n\n      PieView.prototype.containPoint = function (point, seriesModel) {\n        var data = seriesModel.getData();\n        var itemLayout = data.getItemLayout(0);\n\n        if (itemLayout) {\n          var dx = point[0] - itemLayout.cx;\n          var dy = point[1] - itemLayout.cy;\n          var radius = Math.sqrt(dx * dx + dy * dy);\n          return radius <= itemLayout.r && radius >= itemLayout.r0;\n        }\n      };\n\n      PieView.type = 'pie';\n      return PieView;\n    }(ChartView);\n\n    /**\n     * [Usage]:\n     * (1)\n     * createListSimply(seriesModel, ['value']);\n     * (2)\n     * createListSimply(seriesModel, {\n     *     coordDimensions: ['value'],\n     *     dimensionsCount: 5\n     * });\n     */\n\n    function createSeriesDataSimply(seriesModel, opt, nameList) {\n      opt = isArray(opt) && {\n        coordDimensions: opt\n      } || extend({\n        encodeDefine: seriesModel.getEncode()\n      }, opt);\n      var source = seriesModel.getSource();\n      var dimensions = prepareSeriesDataSchema(source, opt).dimensions;\n      var list = new SeriesData(dimensions, seriesModel);\n      list.initData(source, nameList);\n      return list;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * LegendVisualProvider is an bridge that pick encoded color from data and\n     * provide to the legend component.\n     */\n    var LegendVisualProvider =\n    /** @class */\n    function () {\n      function LegendVisualProvider( // Function to get data after filtered. It stores all the encoding info\n      getDataWithEncodedVisual, // Function to get raw data before filtered.\n      getRawData) {\n        this._getDataWithEncodedVisual = getDataWithEncodedVisual;\n        this._getRawData = getRawData;\n      }\n\n      LegendVisualProvider.prototype.getAllNames = function () {\n        var rawData = this._getRawData(); // We find the name from the raw data. In case it's filtered by the legend component.\n        // Normally, the name can be found in rawData, but can't be found in filtered data will display as gray.\n\n\n        return rawData.mapArray(rawData.getName);\n      };\n\n      LegendVisualProvider.prototype.containName = function (name) {\n        var rawData = this._getRawData();\n\n        return rawData.indexOfName(name) >= 0;\n      };\n\n      LegendVisualProvider.prototype.indexOfName = function (name) {\n        // Only get data when necessary.\n        // Because LegendVisualProvider constructor may be new in the stage that data is not prepared yet.\n        // Invoking Series#getData immediately will throw an error.\n        var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n        return dataWithEncodedVisual.indexOfName(name);\n      };\n\n      LegendVisualProvider.prototype.getItemVisual = function (dataIndex, key) {\n        // Get encoded visual properties from final filtered data.\n        var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n        return dataWithEncodedVisual.getItemVisual(dataIndex, key);\n      };\n\n      return LegendVisualProvider;\n    }();\n\n    var innerData = makeInner();\n\n    var PieSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(PieSeriesModel, _super);\n\n      function PieSeriesModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.init = function (option) {\n        _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n        // Use a function instead of direct access because data reference may changed\n\n\n        this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n\n        this._defaultLabelLine(option);\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.mergeOption = function () {\n        _super.prototype.mergeOption.apply(this, arguments);\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.getInitialData = function () {\n        return createSeriesDataSimply(this, {\n          coordDimensions: ['value'],\n          encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n        });\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.getDataParams = function (dataIndex) {\n        var data = this.getData(); // update seats when data is changed\n\n        var dataInner = innerData(data);\n        var seats = dataInner.seats;\n\n        if (!seats) {\n          var valueList_1 = [];\n          data.each(data.mapDimension('value'), function (value) {\n            valueList_1.push(value);\n          });\n          seats = dataInner.seats = getPercentSeats(valueList_1, data.hostModel.get('percentPrecision'));\n        }\n\n        var params = _super.prototype.getDataParams.call(this, dataIndex); // seats may be empty when sum is 0\n\n\n        params.percent = seats[dataIndex] || 0;\n        params.$vars.push('percent');\n        return params;\n      };\n\n      PieSeriesModel.prototype._defaultLabelLine = function (option) {\n        // Extend labelLine emphasis\n        defaultEmphasis(option, 'labelLine', ['show']);\n        var labelLineNormalOpt = option.labelLine;\n        var labelLineEmphasisOpt = option.emphasis.labelLine; // Not show label line if `label.normal.show = false`\n\n        labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show;\n        labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show;\n      };\n\n      PieSeriesModel.type = 'series.pie';\n      PieSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        colorBy: 'data',\n        // 默认全局居中\n        center: ['50%', '50%'],\n        radius: [0, '75%'],\n        // 默认顺时针\n        clockwise: true,\n        startAngle: 90,\n        // 最小角度改为0\n        minAngle: 0,\n        // If the angle of a sector less than `minShowLabelAngle`,\n        // the label will not be displayed.\n        minShowLabelAngle: 0,\n        // 选中时扇区偏移量\n        selectedOffset: 10,\n        // 选择模式，默认关闭，可选single，multiple\n        // selectedMode: false,\n        // 南丁格尔玫瑰图模式，'radius'（半径） | 'area'（面积）\n        // roseType: null,\n        percentPrecision: 2,\n        // If still show when all data zero.\n        stillShowZeroSum: true,\n        // cursor: null,\n        left: 0,\n        top: 0,\n        right: 0,\n        bottom: 0,\n        width: null,\n        height: null,\n        label: {\n          // color: 'inherit',\n          // If rotate around circle\n          rotate: 0,\n          show: true,\n          overflow: 'truncate',\n          // 'outer', 'inside', 'center'\n          position: 'outer',\n          // 'none', 'labelLine', 'edge'. Works only when position is 'outer'\n          alignTo: 'none',\n          // Closest distance between label and chart edge.\n          // Works only position is 'outer' and alignTo is 'edge'.\n          edgeDistance: '25%',\n          // Works only position is 'outer' and alignTo is not 'edge'.\n          bleedMargin: 10,\n          // Distance between text and label line.\n          distanceToLabelLine: 5 // formatter: 标签文本格式器，同 tooltip.formatter，不支持异步回调\n          // 默认使用全局文本样式，详见 textStyle\n          // distance: 当position为inner时有效，为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数\n\n        },\n        // Enabled when label.normal.position is 'outer'\n        labelLine: {\n          show: true,\n          // 引导线两段中的第一段长度\n          length: 15,\n          // 引导线两段中的第二段长度\n          length2: 15,\n          smooth: false,\n          minTurnAngle: 90,\n          maxSurfaceAngle: 90,\n          lineStyle: {\n            // color: 各异,\n            width: 1,\n            type: 'solid'\n          }\n        },\n        itemStyle: {\n          borderWidth: 1,\n          borderJoin: 'round'\n        },\n        showEmptyCircle: true,\n        emptyCircleStyle: {\n          color: 'lightgray',\n          opacity: 1\n        },\n        labelLayout: {\n          // Hide the overlapped label.\n          hideOverlap: true\n        },\n        emphasis: {\n          scale: true,\n          scaleSize: 5\n        },\n        // If use strategy to avoid label overlapping\n        avoidLabelOverlap: true,\n        // Animation type. Valid values: expansion, scale\n        animationType: 'expansion',\n        animationDuration: 1000,\n        // Animation type when update. Valid values: transition, expansion\n        animationTypeUpdate: 'transition',\n        animationEasingUpdate: 'cubicInOut',\n        animationDurationUpdate: 500,\n        animationEasing: 'cubicInOut'\n      };\n      return PieSeriesModel;\n    }(SeriesModel);\n\n    function negativeDataFilter(seriesType) {\n      return {\n        seriesType: seriesType,\n        reset: function (seriesModel, ecModel) {\n          var data = seriesModel.getData();\n          data.filterSelf(function (idx) {\n            // handle negative value condition\n            var valueDim = data.mapDimension('value');\n            var curValue = data.get(valueDim, idx);\n\n            if (isNumber(curValue) && !isNaN(curValue) && curValue < 0) {\n              return false;\n            }\n\n            return true;\n          });\n        }\n      };\n    }\n\n    function install$4(registers) {\n      registers.registerChartView(PieView);\n      registers.registerSeriesModel(PieSeriesModel);\n      createLegacyDataSelectAction('pie', registers.registerAction);\n      registers.registerLayout(curry(pieLayout, 'pie'));\n      registers.registerProcessor(dataFilter('pie'));\n      registers.registerProcessor(negativeDataFilter('pie'));\n    }\n\n    var ScatterSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(ScatterSeriesModel, _super);\n\n      function ScatterSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScatterSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      }\n\n      ScatterSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      ScatterSeriesModel.prototype.getProgressive = function () {\n        var progressive = this.option.progressive;\n\n        if (progressive == null) {\n          // PENDING\n          return this.option.large ? 5e3 : this.get('progressive');\n        }\n\n        return progressive;\n      };\n\n      ScatterSeriesModel.prototype.getProgressiveThreshold = function () {\n        var progressiveThreshold = this.option.progressiveThreshold;\n\n        if (progressiveThreshold == null) {\n          // PENDING\n          return this.option.large ? 1e4 : this.get('progressiveThreshold');\n        }\n\n        return progressiveThreshold;\n      };\n\n      ScatterSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n        return selectors.point(data.getItemLayout(dataIndex));\n      };\n\n      ScatterSeriesModel.prototype.getZLevelKey = function () {\n        // Each progressive series has individual key.\n        return this.getData().count() > this.getProgressiveThreshold() ? this.id : '';\n      };\n\n      ScatterSeriesModel.type = 'series.scatter';\n      ScatterSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];\n      ScatterSeriesModel.defaultOption = {\n        coordinateSystem: 'cartesian2d',\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        symbolSize: 10,\n        // symbolRotate: null,  // 图形旋转控制\n        large: false,\n        // Available when large is true\n        largeThreshold: 2000,\n        // cursor: null,\n        itemStyle: {\n          opacity: 0.8 // color: 各异\n\n        },\n        emphasis: {\n          scale: true\n        },\n        // If clip the overflow graphics\n        // Works on cartesian / polar series\n        clip: true,\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        },\n        universalTransition: {\n          divideShape: 'clone'\n        } // progressive: null\n\n      };\n      return ScatterSeriesModel;\n    }(SeriesModel);\n\n    var BOOST_SIZE_THRESHOLD = 4;\n\n    var LargeSymbolPathShape =\n    /** @class */\n    function () {\n      function LargeSymbolPathShape() {}\n\n      return LargeSymbolPathShape;\n    }();\n\n    var LargeSymbolPath =\n    /** @class */\n    function (_super) {\n      __extends(LargeSymbolPath, _super);\n\n      function LargeSymbolPath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this._off = 0;\n        _this.hoverDataIdx = -1;\n        return _this;\n      }\n\n      LargeSymbolPath.prototype.getDefaultShape = function () {\n        return new LargeSymbolPathShape();\n      };\n\n      LargeSymbolPath.prototype.reset = function () {\n        this.notClear = false;\n        this._off = 0;\n      };\n\n      LargeSymbolPath.prototype.buildPath = function (path, shape) {\n        var points = shape.points;\n        var size = shape.size;\n        var symbolProxy = this.symbolProxy;\n        var symbolProxyShape = symbolProxy.shape;\n        var ctx = path.getContext ? path.getContext() : path;\n        var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD;\n        var softClipShape = this.softClipShape;\n        var i; // Do draw in afterBrush.\n\n        if (canBoost) {\n          this._ctx = ctx;\n          return;\n        }\n\n        this._ctx = null;\n\n        for (i = this._off; i < points.length;) {\n          var x = points[i++];\n          var y = points[i++];\n\n          if (isNaN(x) || isNaN(y)) {\n            continue;\n          }\n\n          if (softClipShape && !softClipShape.contain(x, y)) {\n            continue;\n          }\n\n          symbolProxyShape.x = x - size[0] / 2;\n          symbolProxyShape.y = y - size[1] / 2;\n          symbolProxyShape.width = size[0];\n          symbolProxyShape.height = size[1];\n          symbolProxy.buildPath(path, symbolProxyShape, true);\n        }\n\n        if (this.incremental) {\n          this._off = i;\n          this.notClear = true;\n        }\n      };\n\n      LargeSymbolPath.prototype.afterBrush = function () {\n        var shape = this.shape;\n        var points = shape.points;\n        var size = shape.size;\n        var ctx = this._ctx;\n        var softClipShape = this.softClipShape;\n        var i;\n\n        if (!ctx) {\n          return;\n        } // PENDING If style or other canvas status changed?\n\n\n        for (i = this._off; i < points.length;) {\n          var x = points[i++];\n          var y = points[i++];\n\n          if (isNaN(x) || isNaN(y)) {\n            continue;\n          }\n\n          if (softClipShape && !softClipShape.contain(x, y)) {\n            continue;\n          } // fillRect is faster than building a rect path and draw.\n          // And it support light globalCompositeOperation.\n\n\n          ctx.fillRect(x - size[0] / 2, y - size[1] / 2, size[0], size[1]);\n        }\n\n        if (this.incremental) {\n          this._off = i;\n          this.notClear = true;\n        }\n      };\n\n      LargeSymbolPath.prototype.findDataIndex = function (x, y) {\n        // TODO ???\n        // Consider transform\n        var shape = this.shape;\n        var points = shape.points;\n        var size = shape.size;\n        var w = Math.max(size[0], 4);\n        var h = Math.max(size[1], 4); // Not consider transform\n        // Treat each element as a rect\n        // top down traverse\n\n        for (var idx = points.length / 2 - 1; idx >= 0; idx--) {\n          var i = idx * 2;\n          var x0 = points[i] - w / 2;\n          var y0 = points[i + 1] - h / 2;\n\n          if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) {\n            return idx;\n          }\n        }\n\n        return -1;\n      };\n\n      LargeSymbolPath.prototype.contain = function (x, y) {\n        var localPos = this.transformCoordToLocal(x, y);\n        var rect = this.getBoundingRect();\n        x = localPos[0];\n        y = localPos[1];\n\n        if (rect.contain(x, y)) {\n          // Cache found data index.\n          var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y);\n          return dataIdx >= 0;\n        }\n\n        this.hoverDataIdx = -1;\n        return false;\n      };\n\n      LargeSymbolPath.prototype.getBoundingRect = function () {\n        // Ignore stroke for large symbol draw.\n        var rect = this._rect;\n\n        if (!rect) {\n          var shape = this.shape;\n          var points = shape.points;\n          var size = shape.size;\n          var w = size[0];\n          var h = size[1];\n          var minX = Infinity;\n          var minY = Infinity;\n          var maxX = -Infinity;\n          var maxY = -Infinity;\n\n          for (var i = 0; i < points.length;) {\n            var x = points[i++];\n            var y = points[i++];\n            minX = Math.min(x, minX);\n            maxX = Math.max(x, maxX);\n            minY = Math.min(y, minY);\n            maxY = Math.max(y, maxY);\n          }\n\n          rect = this._rect = new BoundingRect(minX - w / 2, minY - h / 2, maxX - minX + w, maxY - minY + h);\n        }\n\n        return rect;\n      };\n\n      return LargeSymbolPath;\n    }(Path);\n\n    var LargeSymbolDraw =\n    /** @class */\n    function () {\n      function LargeSymbolDraw() {\n        this.group = new Group();\n      }\n      /**\n       * Update symbols draw by new data\n       */\n\n\n      LargeSymbolDraw.prototype.updateData = function (data, opt) {\n        this._clear();\n\n        var symbolEl = this._create();\n\n        symbolEl.setShape({\n          points: data.getLayout('points')\n        });\n\n        this._setCommon(symbolEl, data, opt);\n      };\n\n      LargeSymbolDraw.prototype.updateLayout = function (data) {\n        var points = data.getLayout('points');\n        this.group.eachChild(function (child) {\n          if (child.startIndex != null) {\n            var len = (child.endIndex - child.startIndex) * 2;\n            var byteOffset = child.startIndex * 4 * 2;\n            points = new Float32Array(points.buffer, byteOffset, len);\n          }\n\n          child.setShape('points', points); // Reset draw cursor.\n\n          child.reset();\n        });\n      };\n\n      LargeSymbolDraw.prototype.incrementalPrepareUpdate = function (data) {\n        this._clear();\n      };\n\n      LargeSymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {\n        var lastAdded = this._newAdded[0];\n        var points = data.getLayout('points');\n        var oldPoints = lastAdded && lastAdded.shape.points; // Merging the exists. Each element has 1e4 points.\n        // Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization)\n\n        if (oldPoints && oldPoints.length < 2e4) {\n          var oldLen = oldPoints.length;\n          var newPoints = new Float32Array(oldLen + points.length); // Concat two array\n\n          newPoints.set(oldPoints);\n          newPoints.set(points, oldLen); // Update endIndex\n\n          lastAdded.endIndex = taskParams.end;\n          lastAdded.setShape({\n            points: newPoints\n          });\n        } else {\n          // Clear\n          this._newAdded = [];\n\n          var symbolEl = this._create();\n\n          symbolEl.startIndex = taskParams.start;\n          symbolEl.endIndex = taskParams.end;\n          symbolEl.incremental = true;\n          symbolEl.setShape({\n            points: points\n          });\n\n          this._setCommon(symbolEl, data, opt);\n        }\n      };\n\n      LargeSymbolDraw.prototype.eachRendered = function (cb) {\n        this._newAdded[0] && cb(this._newAdded[0]);\n      };\n\n      LargeSymbolDraw.prototype._create = function () {\n        var symbolEl = new LargeSymbolPath({\n          cursor: 'default'\n        });\n        symbolEl.ignoreCoarsePointer = true;\n        this.group.add(symbolEl);\n\n        this._newAdded.push(symbolEl);\n\n        return symbolEl;\n      };\n\n      LargeSymbolDraw.prototype._setCommon = function (symbolEl, data, opt) {\n        var hostModel = data.hostModel;\n        opt = opt || {};\n        var size = data.getVisual('symbolSize');\n        symbolEl.setShape('size', size instanceof Array ? size : [size, size]);\n        symbolEl.softClipShape = opt.clipShape || null; // Create symbolProxy to build path for each data\n\n        symbolEl.symbolProxy = createSymbol(data.getVisual('symbol'), 0, 0, 0, 0); // Use symbolProxy setColor method\n\n        symbolEl.setColor = symbolEl.symbolProxy.setColor;\n        var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD;\n        symbolEl.useStyle( // Draw shadow when doing fillRect is extremely slow.\n        hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color']));\n        var globalStyle = data.getVisual('style');\n        var visualColor = globalStyle && globalStyle.fill;\n\n        if (visualColor) {\n          symbolEl.setColor(visualColor);\n        }\n\n        var ecData = getECData(symbolEl); // Enable tooltip\n        // PENDING May have performance issue when path is extremely large\n\n        ecData.seriesIndex = hostModel.seriesIndex;\n        symbolEl.on('mousemove', function (e) {\n          ecData.dataIndex = null;\n          var dataIndex = symbolEl.hoverDataIdx;\n\n          if (dataIndex >= 0) {\n            // Provide dataIndex for tooltip\n            ecData.dataIndex = dataIndex + (symbolEl.startIndex || 0);\n          }\n        });\n      };\n\n      LargeSymbolDraw.prototype.remove = function () {\n        this._clear();\n      };\n\n      LargeSymbolDraw.prototype._clear = function () {\n        this._newAdded = [];\n        this.group.removeAll();\n      };\n\n      return LargeSymbolDraw;\n    }();\n\n    var ScatterView =\n    /** @class */\n    function (_super) {\n      __extends(ScatterView, _super);\n\n      function ScatterView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScatterView.type;\n        return _this;\n      }\n\n      ScatterView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n\n        var symbolDraw = this._updateSymbolDraw(data, seriesModel);\n\n        symbolDraw.updateData(data, {\n          // TODO\n          // If this parameter should be a shape or a bounding volume\n          // shape will be more general.\n          // But bounding volume like bounding rect will be much faster in the contain calculation\n          clipShape: this._getClipShape(seriesModel)\n        });\n        this._finished = true;\n      };\n\n      ScatterView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n\n        var symbolDraw = this._updateSymbolDraw(data, seriesModel);\n\n        symbolDraw.incrementalPrepareUpdate(data);\n        this._finished = false;\n      };\n\n      ScatterView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) {\n        this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData(), {\n          clipShape: this._getClipShape(seriesModel)\n        });\n\n        this._finished = taskParams.end === seriesModel.getData().count();\n      };\n\n      ScatterView.prototype.updateTransform = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData(); // Must mark group dirty and make sure the incremental layer will be cleared\n        // PENDING\n\n        this.group.dirty();\n\n        if (!this._finished || data.count() > 1e4) {\n          return {\n            update: true\n          };\n        } else {\n          var res = pointsLayout('').reset(seriesModel, ecModel, api);\n\n          if (res.progress) {\n            res.progress({\n              start: 0,\n              end: data.count(),\n              count: data.count()\n            }, data);\n          }\n\n          this._symbolDraw.updateLayout(data);\n        }\n      };\n\n      ScatterView.prototype.eachRendered = function (cb) {\n        this._symbolDraw && this._symbolDraw.eachRendered(cb);\n      };\n\n      ScatterView.prototype._getClipShape = function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n        var clipArea = coordSys && coordSys.getArea && coordSys.getArea();\n        return seriesModel.get('clip', true) ? clipArea : null;\n      };\n\n      ScatterView.prototype._updateSymbolDraw = function (data, seriesModel) {\n        var symbolDraw = this._symbolDraw;\n        var pipelineContext = seriesModel.pipelineContext;\n        var isLargeDraw = pipelineContext.large;\n\n        if (!symbolDraw || isLargeDraw !== this._isLargeDraw) {\n          symbolDraw && symbolDraw.remove();\n          symbolDraw = this._symbolDraw = isLargeDraw ? new LargeSymbolDraw() : new SymbolDraw();\n          this._isLargeDraw = isLargeDraw;\n          this.group.removeAll();\n        }\n\n        this.group.add(symbolDraw.group);\n        return symbolDraw;\n      };\n\n      ScatterView.prototype.remove = function (ecModel, api) {\n        this._symbolDraw && this._symbolDraw.remove(true);\n        this._symbolDraw = null;\n      };\n\n      ScatterView.prototype.dispose = function () {};\n\n      ScatterView.type = 'scatter';\n      return ScatterView;\n    }(ChartView);\n\n    var GridModel =\n    /** @class */\n    function (_super) {\n      __extends(GridModel, _super);\n\n      function GridModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      GridModel.type = 'grid';\n      GridModel.dependencies = ['xAxis', 'yAxis'];\n      GridModel.layoutMode = 'box';\n      GridModel.defaultOption = {\n        show: false,\n        // zlevel: 0,\n        z: 0,\n        left: '10%',\n        top: 60,\n        right: '10%',\n        bottom: 70,\n        // If grid size contain label\n        containLabel: false,\n        // width: {totalWidth} - left - right,\n        // height: {totalHeight} - top - bottom,\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderWidth: 1,\n        borderColor: '#ccc'\n      };\n      return GridModel;\n    }(ComponentModel);\n\n    var CartesianAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisModel, _super);\n\n      function CartesianAxisModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      CartesianAxisModel.prototype.getCoordSysModel = function () {\n        return this.getReferringComponents('grid', SINGLE_REFERRING).models[0];\n      };\n\n      CartesianAxisModel.type = 'cartesian2dAxis';\n      return CartesianAxisModel;\n    }(ComponentModel);\n    mixin(CartesianAxisModel, AxisModelCommonMixin);\n\n    var defaultOption = {\n      show: true,\n      // zlevel: 0,\n      z: 0,\n      // Inverse the axis.\n      inverse: false,\n      // Axis name displayed.\n      name: '',\n      // 'start' | 'middle' | 'end'\n      nameLocation: 'end',\n      // By degree. By default auto rotate by nameLocation.\n      nameRotate: null,\n      nameTruncate: {\n        maxWidth: null,\n        ellipsis: '...',\n        placeholder: '.'\n      },\n      // Use global text style by default.\n      nameTextStyle: {},\n      // The gap between axisName and axisLine.\n      nameGap: 15,\n      // Default `false` to support tooltip.\n      silent: false,\n      // Default `false` to avoid legacy user event listener fail.\n      triggerEvent: false,\n      tooltip: {\n        show: false\n      },\n      axisPointer: {},\n      axisLine: {\n        show: true,\n        onZero: true,\n        onZeroAxisIndex: null,\n        lineStyle: {\n          color: '#6E7079',\n          width: 1,\n          type: 'solid'\n        },\n        // The arrow at both ends the the axis.\n        symbol: ['none', 'none'],\n        symbolSize: [10, 15]\n      },\n      axisTick: {\n        show: true,\n        // Whether axisTick is inside the grid or outside the grid.\n        inside: false,\n        // The length of axisTick.\n        length: 5,\n        lineStyle: {\n          width: 1\n        }\n      },\n      axisLabel: {\n        show: true,\n        // Whether axisLabel is inside the grid or outside the grid.\n        inside: false,\n        rotate: 0,\n        // true | false | null/undefined (auto)\n        showMinLabel: null,\n        // true | false | null/undefined (auto)\n        showMaxLabel: null,\n        margin: 8,\n        // formatter: null,\n        fontSize: 12\n      },\n      splitLine: {\n        show: true,\n        lineStyle: {\n          color: ['#E0E6F1'],\n          width: 1,\n          type: 'solid'\n        }\n      },\n      splitArea: {\n        show: false,\n        areaStyle: {\n          color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)']\n        }\n      }\n    };\n    var categoryAxis = merge({\n      // The gap at both ends of the axis. For categoryAxis, boolean.\n      boundaryGap: true,\n      // Set false to faster category collection.\n      deduplication: null,\n      // splitArea: {\n      // show: false\n      // },\n      splitLine: {\n        show: false\n      },\n      axisTick: {\n        // If tick is align with label when boundaryGap is true\n        alignWithLabel: false,\n        interval: 'auto'\n      },\n      axisLabel: {\n        interval: 'auto'\n      }\n    }, defaultOption);\n    var valueAxis = merge({\n      boundaryGap: [0, 0],\n      axisLine: {\n        // Not shown when other axis is categoryAxis in cartesian\n        show: 'auto'\n      },\n      axisTick: {\n        // Not shown when other axis is categoryAxis in cartesian\n        show: 'auto'\n      },\n      // TODO\n      // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]\n      splitNumber: 5,\n      minorTick: {\n        // Minor tick, not available for cateogry axis.\n        show: false,\n        // Split number of minor ticks. The value should be in range of (0, 100)\n        splitNumber: 5,\n        // Length of minor tick\n        length: 3,\n        // Line style\n        lineStyle: {// Default to be same with axisTick\n        }\n      },\n      minorSplitLine: {\n        show: false,\n        lineStyle: {\n          color: '#F4F7FD',\n          width: 1\n        }\n      }\n    }, defaultOption);\n    var timeAxis = merge({\n      splitNumber: 6,\n      axisLabel: {\n        // To eliminate labels that are not nice\n        showMinLabel: false,\n        showMaxLabel: false,\n        rich: {\n          primary: {\n            fontWeight: 'bold'\n          }\n        }\n      },\n      splitLine: {\n        show: false\n      }\n    }, valueAxis);\n    var logAxis = defaults({\n      logBase: 10\n    }, valueAxis);\n    var axisDefault = {\n      category: categoryAxis,\n      value: valueAxis,\n      time: timeAxis,\n      log: logAxis\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var AXIS_TYPES = {\n      value: 1,\n      category: 1,\n      time: 1,\n      log: 1\n    };\n\n    /**\n     * Generate sub axis model class\n     * @param axisName 'x' 'y' 'radius' 'angle' 'parallel' ...\n     */\n\n    function axisModelCreator(registers, axisName, BaseAxisModelClass, extraDefaultOption) {\n      each(AXIS_TYPES, function (v, axisType) {\n        var defaultOption = merge(merge({}, axisDefault[axisType], true), extraDefaultOption, true);\n\n        var AxisModel =\n        /** @class */\n        function (_super) {\n          __extends(AxisModel, _super);\n\n          function AxisModel() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n\n            _this.type = axisName + 'Axis.' + axisType;\n            return _this;\n          }\n\n          AxisModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n            var layoutMode = fetchLayoutMode(this);\n            var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n            var themeModel = ecModel.getTheme();\n            merge(option, themeModel.get(axisType + 'Axis'));\n            merge(option, this.getDefaultOption());\n            option.type = getAxisType(option);\n\n            if (layoutMode) {\n              mergeLayoutParam(option, inputPositionParams, layoutMode);\n            }\n          };\n\n          AxisModel.prototype.optionUpdated = function () {\n            var thisOption = this.option;\n\n            if (thisOption.type === 'category') {\n              this.__ordinalMeta = OrdinalMeta.createByAxisModel(this);\n            }\n          };\n          /**\n           * Should not be called before all of 'getInitailData' finished.\n           * Because categories are collected during initializing data.\n           */\n\n\n          AxisModel.prototype.getCategories = function (rawData) {\n            var option = this.option; // FIXME\n            // warning if called before all of 'getInitailData' finished.\n\n            if (option.type === 'category') {\n              if (rawData) {\n                return option.data;\n              }\n\n              return this.__ordinalMeta.categories;\n            }\n          };\n\n          AxisModel.prototype.getOrdinalMeta = function () {\n            return this.__ordinalMeta;\n          };\n\n          AxisModel.type = axisName + 'Axis.' + axisType;\n          AxisModel.defaultOption = defaultOption;\n          return AxisModel;\n        }(BaseAxisModelClass);\n\n        registers.registerComponentModel(AxisModel);\n      });\n      registers.registerSubTypeDefaulter(axisName + 'Axis', getAxisType);\n    }\n\n    function getAxisType(option) {\n      // Default axis with data is category axis\n      return option.type || (option.data ? 'category' : 'value');\n    }\n\n    var Cartesian =\n    /** @class */\n    function () {\n      function Cartesian(name) {\n        this.type = 'cartesian';\n        this._dimList = [];\n        this._axes = {};\n        this.name = name || '';\n      }\n\n      Cartesian.prototype.getAxis = function (dim) {\n        return this._axes[dim];\n      };\n\n      Cartesian.prototype.getAxes = function () {\n        return map(this._dimList, function (dim) {\n          return this._axes[dim];\n        }, this);\n      };\n\n      Cartesian.prototype.getAxesByScale = function (scaleType) {\n        scaleType = scaleType.toLowerCase();\n        return filter(this.getAxes(), function (axis) {\n          return axis.scale.type === scaleType;\n        });\n      };\n\n      Cartesian.prototype.addAxis = function (axis) {\n        var dim = axis.dim;\n        this._axes[dim] = axis;\n\n        this._dimList.push(dim);\n      };\n\n      return Cartesian;\n    }();\n\n    var cartesian2DDimensions = ['x', 'y'];\n\n    function canCalculateAffineTransform(scale) {\n      return scale.type === 'interval' || scale.type === 'time';\n    }\n\n    var Cartesian2D =\n    /** @class */\n    function (_super) {\n      __extends(Cartesian2D, _super);\n\n      function Cartesian2D() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'cartesian2d';\n        _this.dimensions = cartesian2DDimensions;\n        return _this;\n      }\n      /**\n       * Calculate an affine transform matrix if two axes are time or value.\n       * It's mainly for accelartion on the large time series data.\n       */\n\n\n      Cartesian2D.prototype.calcAffineTransform = function () {\n        this._transform = this._invTransform = null;\n        var xAxisScale = this.getAxis('x').scale;\n        var yAxisScale = this.getAxis('y').scale;\n\n        if (!canCalculateAffineTransform(xAxisScale) || !canCalculateAffineTransform(yAxisScale)) {\n          return;\n        }\n\n        var xScaleExtent = xAxisScale.getExtent();\n        var yScaleExtent = yAxisScale.getExtent();\n        var start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]]);\n        var end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]]);\n        var xScaleSpan = xScaleExtent[1] - xScaleExtent[0];\n        var yScaleSpan = yScaleExtent[1] - yScaleExtent[0];\n\n        if (!xScaleSpan || !yScaleSpan) {\n          return;\n        } // Accelerate data to point calculation on the special large time series data.\n\n\n        var scaleX = (end[0] - start[0]) / xScaleSpan;\n        var scaleY = (end[1] - start[1]) / yScaleSpan;\n        var translateX = start[0] - xScaleExtent[0] * scaleX;\n        var translateY = start[1] - yScaleExtent[0] * scaleY;\n        var m = this._transform = [scaleX, 0, 0, scaleY, translateX, translateY];\n        this._invTransform = invert([], m);\n      };\n      /**\n       * Base axis will be used on stacking.\n       */\n\n\n      Cartesian2D.prototype.getBaseAxis = function () {\n        return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAxis('x');\n      };\n\n      Cartesian2D.prototype.containPoint = function (point) {\n        var axisX = this.getAxis('x');\n        var axisY = this.getAxis('y');\n        return axisX.contain(axisX.toLocalCoord(point[0])) && axisY.contain(axisY.toLocalCoord(point[1]));\n      };\n\n      Cartesian2D.prototype.containData = function (data) {\n        return this.getAxis('x').containData(data[0]) && this.getAxis('y').containData(data[1]);\n      };\n\n      Cartesian2D.prototype.containZone = function (data1, data2) {\n        var zoneDiag1 = this.dataToPoint(data1);\n        var zoneDiag2 = this.dataToPoint(data2);\n        var area = this.getArea();\n        var zone = new BoundingRect(zoneDiag1[0], zoneDiag1[1], zoneDiag2[0] - zoneDiag1[0], zoneDiag2[1] - zoneDiag1[1]);\n        return area.intersect(zone);\n      };\n\n      Cartesian2D.prototype.dataToPoint = function (data, clamp, out) {\n        out = out || [];\n        var xVal = data[0];\n        var yVal = data[1]; // Fast path\n\n        if (this._transform // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated.\n        && xVal != null && isFinite(xVal) && yVal != null && isFinite(yVal)) {\n          return applyTransform(out, data, this._transform);\n        }\n\n        var xAxis = this.getAxis('x');\n        var yAxis = this.getAxis('y');\n        out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal, clamp));\n        out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal, clamp));\n        return out;\n      };\n\n      Cartesian2D.prototype.clampData = function (data, out) {\n        var xScale = this.getAxis('x').scale;\n        var yScale = this.getAxis('y').scale;\n        var xAxisExtent = xScale.getExtent();\n        var yAxisExtent = yScale.getExtent();\n        var x = xScale.parse(data[0]);\n        var y = yScale.parse(data[1]);\n        out = out || [];\n        out[0] = Math.min(Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), Math.max(xAxisExtent[0], xAxisExtent[1]));\n        out[1] = Math.min(Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), Math.max(yAxisExtent[0], yAxisExtent[1]));\n        return out;\n      };\n\n      Cartesian2D.prototype.pointToData = function (point, clamp) {\n        var out = [];\n\n        if (this._invTransform) {\n          return applyTransform(out, point, this._invTransform);\n        }\n\n        var xAxis = this.getAxis('x');\n        var yAxis = this.getAxis('y');\n        out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp);\n        out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp);\n        return out;\n      };\n\n      Cartesian2D.prototype.getOtherAxis = function (axis) {\n        return this.getAxis(axis.dim === 'x' ? 'y' : 'x');\n      };\n      /**\n       * Get rect area of cartesian.\n       * Area will have a contain function to determine if a point is in the coordinate system.\n       */\n\n\n      Cartesian2D.prototype.getArea = function () {\n        var xExtent = this.getAxis('x').getGlobalExtent();\n        var yExtent = this.getAxis('y').getGlobalExtent();\n        var x = Math.min(xExtent[0], xExtent[1]);\n        var y = Math.min(yExtent[0], yExtent[1]);\n        var width = Math.max(xExtent[0], xExtent[1]) - x;\n        var height = Math.max(yExtent[0], yExtent[1]) - y;\n        return new BoundingRect(x, y, width, height);\n      };\n\n      return Cartesian2D;\n    }(Cartesian);\n\n    var Axis2D =\n    /** @class */\n    function (_super) {\n      __extends(Axis2D, _super);\n\n      function Axis2D(dim, scale, coordExtent, axisType, position) {\n        var _this = _super.call(this, dim, scale, coordExtent) || this;\n        /**\n         * Index of axis, can be used as key\n         * Injected outside.\n         */\n\n\n        _this.index = 0;\n        _this.type = axisType || 'value';\n        _this.position = position || 'bottom';\n        return _this;\n      }\n\n      Axis2D.prototype.isHorizontal = function () {\n        var position = this.position;\n        return position === 'top' || position === 'bottom';\n      };\n      /**\n       * Each item cooresponds to this.getExtent(), which\n       * means globalExtent[0] may greater than globalExtent[1],\n       * unless `asc` is input.\n       *\n       * @param {boolean} [asc]\n       * @return {Array.<number>}\n       */\n\n\n      Axis2D.prototype.getGlobalExtent = function (asc) {\n        var ret = this.getExtent();\n        ret[0] = this.toGlobalCoord(ret[0]);\n        ret[1] = this.toGlobalCoord(ret[1]);\n        asc && ret[0] > ret[1] && ret.reverse();\n        return ret;\n      };\n\n      Axis2D.prototype.pointToData = function (point, clamp) {\n        return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);\n      };\n      /**\n       * Set ordinalSortInfo\n       * @param info new OrdinalSortInfo\n       */\n\n\n      Axis2D.prototype.setCategorySortInfo = function (info) {\n        if (this.type !== 'category') {\n          return false;\n        }\n\n        this.model.option.categorySortInfo = info;\n        this.scale.setSortInfo(info);\n      };\n\n      return Axis2D;\n    }(Axis);\n\n    /**\n     * Can only be called after coordinate system creation stage.\n     * (Can be called before coordinate system update stage).\n     */\n\n    function layout$1(gridModel, axisModel, opt) {\n      opt = opt || {};\n      var grid = gridModel.coordinateSystem;\n      var axis = axisModel.axis;\n      var layout = {};\n      var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0];\n      var rawAxisPosition = axis.position;\n      var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition;\n      var axisDim = axis.dim;\n      var rect = grid.getRect();\n      var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];\n      var idx = {\n        left: 0,\n        right: 1,\n        top: 0,\n        bottom: 1,\n        onZero: 2\n      };\n      var axisOffset = axisModel.get('offset') || 0;\n      var posBound = axisDim === 'x' ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];\n\n      if (otherAxisOnZeroOf) {\n        var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0));\n        posBound[idx.onZero] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);\n      } // Axis position\n\n\n      layout.position = [axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]]; // Axis rotation\n\n      layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); // Tick and label direction, x y is axisDim\n\n      var dirMap = {\n        top: -1,\n        bottom: 1,\n        left: -1,\n        right: 1\n      };\n      layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];\n      layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0;\n\n      if (axisModel.get(['axisTick', 'inside'])) {\n        layout.tickDirection = -layout.tickDirection;\n      }\n\n      if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) {\n        layout.labelDirection = -layout.labelDirection;\n      } // Special label rotation\n\n\n      var labelRotate = axisModel.get(['axisLabel', 'rotate']);\n      layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; // Over splitLine and splitArea\n\n      layout.z2 = 1;\n      return layout;\n    }\n    function isCartesian2DSeries(seriesModel) {\n      return seriesModel.get('coordinateSystem') === 'cartesian2d';\n    }\n    function findAxisModels(seriesModel) {\n      var axisModelMap = {\n        xAxisModel: null,\n        yAxisModel: null\n      };\n      each(axisModelMap, function (v, key) {\n        var axisType = key.replace(/Model$/, '');\n        var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!axisModel) {\n            throw new Error(axisType + ' \"' + retrieve3(seriesModel.get(axisType + 'Index'), seriesModel.get(axisType + 'Id'), 0) + '\" not found');\n          }\n        }\n\n        axisModelMap[key] = axisModel;\n      });\n      return axisModelMap;\n    }\n\n    var mathLog$1 = Math.log;\n    function alignScaleTicks(scale, axisModel, alignToScale) {\n      var intervalScaleProto = IntervalScale.prototype; // NOTE: There is a precondition for log scale  here:\n      // In log scale we store _interval and _extent of exponent value.\n      // So if we use the method of InternalScale to set/get these data.\n      // It process the exponent value, which is linear and what we want here.\n\n      var alignToTicks = intervalScaleProto.getTicks.call(alignToScale);\n      var alignToNicedTicks = intervalScaleProto.getTicks.call(alignToScale, true);\n      var alignToSplitNumber = alignToTicks.length - 1;\n      var alignToInterval = intervalScaleProto.getInterval.call(alignToScale);\n      var scaleExtent = getScaleExtent(scale, axisModel);\n      var rawExtent = scaleExtent.extent;\n      var isMinFixed = scaleExtent.fixMin;\n      var isMaxFixed = scaleExtent.fixMax;\n\n      if (scale.type === 'log') {\n        var logBase = mathLog$1(scale.base);\n        rawExtent = [mathLog$1(rawExtent[0]) / logBase, mathLog$1(rawExtent[1]) / logBase];\n      }\n\n      scale.setExtent(rawExtent[0], rawExtent[1]);\n      scale.calcNiceExtent({\n        splitNumber: alignToSplitNumber,\n        fixMin: isMinFixed,\n        fixMax: isMaxFixed\n      });\n      var extent = intervalScaleProto.getExtent.call(scale); // Need to update the rawExtent.\n      // Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax'\n\n      if (isMinFixed) {\n        rawExtent[0] = extent[0];\n      }\n\n      if (isMaxFixed) {\n        rawExtent[1] = extent[1];\n      }\n\n      var interval = intervalScaleProto.getInterval.call(scale);\n      var min = rawExtent[0];\n      var max = rawExtent[1];\n\n      if (isMinFixed && isMaxFixed) {\n        // User set min, max, divide to get new interval\n        interval = (max - min) / alignToSplitNumber;\n      } else if (isMinFixed) {\n        max = rawExtent[0] + interval * alignToSplitNumber; // User set min, expand extent on the other side\n\n        while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])) {\n          interval = increaseInterval(interval);\n          max = rawExtent[0] + interval * alignToSplitNumber;\n        }\n      } else if (isMaxFixed) {\n        // User set max, expand extent on the other side\n        min = rawExtent[1] - interval * alignToSplitNumber;\n\n        while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])) {\n          interval = increaseInterval(interval);\n          min = rawExtent[1] - interval * alignToSplitNumber;\n        }\n      } else {\n        var nicedSplitNumber = scale.getTicks().length - 1;\n\n        if (nicedSplitNumber > alignToSplitNumber) {\n          interval = increaseInterval(interval);\n        }\n\n        var range = interval * alignToSplitNumber;\n        max = Math.ceil(rawExtent[1] / interval) * interval;\n        min = round(max - range); // Not change the result that crossing zero.\n\n        if (min < 0 && rawExtent[0] >= 0) {\n          min = 0;\n          max = round(range);\n        } else if (max > 0 && rawExtent[1] <= 0) {\n          max = 0;\n          min = -round(range);\n        }\n      } // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale\n\n\n      var t0 = (alignToTicks[0].value - alignToNicedTicks[0].value) / alignToInterval;\n      var t1 = (alignToTicks[alignToSplitNumber].value - alignToNicedTicks[alignToSplitNumber].value) / alignToInterval; // NOTE: Must in setExtent -> setInterval -> setNiceExtent order.\n\n      intervalScaleProto.setExtent.call(scale, min + interval * t0, max + interval * t1);\n      intervalScaleProto.setInterval.call(scale, interval);\n\n      if (t0 || t1) {\n        intervalScaleProto.setNiceExtent.call(scale, min + interval, max - interval);\n      }\n\n      if (\"development\" !== 'production') {\n        var ticks = intervalScaleProto.getTicks.call(scale);\n\n        if (ticks[1] && (!isValueNice(interval) || getPrecisionSafe(ticks[1].value) > getPrecisionSafe(interval))) {\n          warn( // eslint-disable-next-line\n          \"The ticks may be not readable when set min: \" + axisModel.get('min') + \", max: \" + axisModel.get('max') + \" and alignTicks: true\");\n        }\n      }\n    }\n\n    var Grid =\n    /** @class */\n    function () {\n      function Grid(gridModel, ecModel, api) {\n        // FIXME:TS where used (different from registered type 'cartesian2d')?\n        this.type = 'grid';\n        this._coordsMap = {};\n        this._coordsList = [];\n        this._axesMap = {};\n        this._axesList = [];\n        this.axisPointerEnabled = true;\n        this.dimensions = cartesian2DDimensions;\n\n        this._initCartesian(gridModel, ecModel, api);\n\n        this.model = gridModel;\n      }\n\n      Grid.prototype.getRect = function () {\n        return this._rect;\n      };\n\n      Grid.prototype.update = function (ecModel, api) {\n        var axesMap = this._axesMap;\n\n        this._updateScale(ecModel, this.model);\n\n        function updateAxisTicks(axes) {\n          var alignTo; // Axis is added in order of axisIndex.\n\n          var axesIndices = keys(axes);\n          var len = axesIndices.length;\n\n          if (!len) {\n            return;\n          }\n\n          var axisNeedsAlign = []; // Process once and calculate the ticks for those don't use alignTicks.\n\n          for (var i = len - 1; i >= 0; i--) {\n            var idx = +axesIndices[i]; // Convert to number.\n\n            var axis = axes[idx];\n            var model = axis.model;\n            var scale = axis.scale;\n\n            if ( // Only value and log axis without interval support alignTicks.\n            isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) {\n              axisNeedsAlign.push(axis);\n            } else {\n              niceScaleExtent(scale, model);\n\n              if (isIntervalOrLogScale(scale)) {\n                // Can only align to interval or log axis.\n                alignTo = axis;\n              }\n            }\n          }\n          // PENDING. Should we find the axis that both set interval, min, max and align to this one?\n\n          if (axisNeedsAlign.length) {\n            if (!alignTo) {\n              alignTo = axisNeedsAlign.pop();\n              niceScaleExtent(alignTo.scale, alignTo.model);\n            }\n\n            each(axisNeedsAlign, function (axis) {\n              alignScaleTicks(axis.scale, axis.model, alignTo.scale);\n            });\n          }\n        }\n\n        updateAxisTicks(axesMap.x);\n        updateAxisTicks(axesMap.y); // Key: axisDim_axisIndex, value: boolean, whether onZero target.\n\n        var onZeroRecords = {};\n        each(axesMap.x, function (xAxis) {\n          fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords);\n        });\n        each(axesMap.y, function (yAxis) {\n          fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords);\n        }); // Resize again if containLabel is enabled\n        // FIXME It may cause getting wrong grid size in data processing stage\n\n        this.resize(this.model, api);\n      };\n      /**\n       * Resize the grid\n       */\n\n\n      Grid.prototype.resize = function (gridModel, api, ignoreContainLabel) {\n        var boxLayoutParams = gridModel.getBoxLayoutParams();\n        var isContainLabel = !ignoreContainLabel && gridModel.get('containLabel');\n        var gridRect = getLayoutRect(boxLayoutParams, {\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n        this._rect = gridRect;\n        var axesList = this._axesList;\n        adjustAxes(); // Minus label size\n\n        if (isContainLabel) {\n          each(axesList, function (axis) {\n            if (!axis.model.get(['axisLabel', 'inside'])) {\n              var labelUnionRect = estimateLabelUnionRect(axis);\n\n              if (labelUnionRect) {\n                var dim = axis.isHorizontal() ? 'height' : 'width';\n                var margin = axis.model.get(['axisLabel', 'margin']);\n                gridRect[dim] -= labelUnionRect[dim] + margin;\n\n                if (axis.position === 'top') {\n                  gridRect.y += labelUnionRect.height + margin;\n                } else if (axis.position === 'left') {\n                  gridRect.x += labelUnionRect.width + margin;\n                }\n              }\n            }\n          });\n          adjustAxes();\n        }\n\n        each(this._coordsList, function (coord) {\n          // Calculate affine matrix to accelerate the data to point transform.\n          // If all the axes scales are time or value.\n          coord.calcAffineTransform();\n        });\n\n        function adjustAxes() {\n          each(axesList, function (axis) {\n            var isHorizontal = axis.isHorizontal();\n            var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];\n            var idx = axis.inverse ? 1 : 0;\n            axis.setExtent(extent[idx], extent[1 - idx]);\n            updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);\n          });\n        }\n      };\n\n      Grid.prototype.getAxis = function (dim, axisIndex) {\n        var axesMapOnDim = this._axesMap[dim];\n\n        if (axesMapOnDim != null) {\n          return axesMapOnDim[axisIndex || 0];\n        }\n      };\n\n      Grid.prototype.getAxes = function () {\n        return this._axesList.slice();\n      };\n\n      Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) {\n        if (xAxisIndex != null && yAxisIndex != null) {\n          var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n          return this._coordsMap[key];\n        }\n\n        if (isObject(xAxisIndex)) {\n          yAxisIndex = xAxisIndex.yAxisIndex;\n          xAxisIndex = xAxisIndex.xAxisIndex;\n        }\n\n        for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {\n          if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {\n            return coordList[i];\n          }\n        }\n      };\n\n      Grid.prototype.getCartesians = function () {\n        return this._coordsList.slice();\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.convertToPixel = function (ecModel, finder, value) {\n        var target = this._findConvertTarget(finder);\n\n        return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.convertFromPixel = function (ecModel, finder, value) {\n        var target = this._findConvertTarget(finder);\n\n        return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;\n      };\n\n      Grid.prototype._findConvertTarget = function (finder) {\n        var seriesModel = finder.seriesModel;\n        var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n        var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n        var gridModel = finder.gridModel;\n        var coordsList = this._coordsList;\n        var cartesian;\n        var axis;\n\n        if (seriesModel) {\n          cartesian = seriesModel.coordinateSystem;\n          indexOf(coordsList, cartesian) < 0 && (cartesian = null);\n        } else if (xAxisModel && yAxisModel) {\n          cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n        } else if (xAxisModel) {\n          axis = this.getAxis('x', xAxisModel.componentIndex);\n        } else if (yAxisModel) {\n          axis = this.getAxis('y', yAxisModel.componentIndex);\n        } // Lowest priority.\n        else if (gridModel) {\n            var grid = gridModel.coordinateSystem;\n\n            if (grid === this) {\n              cartesian = this._coordsList[0];\n            }\n          }\n\n        return {\n          cartesian: cartesian,\n          axis: axis\n        };\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.containPoint = function (point) {\n        var coord = this._coordsList[0];\n\n        if (coord) {\n          return coord.containPoint(point);\n        }\n      };\n      /**\n       * Initialize cartesian coordinate systems\n       */\n\n\n      Grid.prototype._initCartesian = function (gridModel, ecModel, api) {\n        var _this = this;\n\n        var grid = this;\n        var axisPositionUsed = {\n          left: false,\n          right: false,\n          top: false,\n          bottom: false\n        };\n        var axesMap = {\n          x: {},\n          y: {}\n        };\n        var axesCount = {\n          x: 0,\n          y: 0\n        }; // Create axis\n\n        ecModel.eachComponent('xAxis', createAxisCreator('x'), this);\n        ecModel.eachComponent('yAxis', createAxisCreator('y'), this);\n\n        if (!axesCount.x || !axesCount.y) {\n          // Roll back when there no either x or y axis\n          this._axesMap = {};\n          this._axesList = [];\n          return;\n        }\n\n        this._axesMap = axesMap; // Create cartesian2d\n\n        each(axesMap.x, function (xAxis, xAxisIndex) {\n          each(axesMap.y, function (yAxis, yAxisIndex) {\n            var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n            var cartesian = new Cartesian2D(key);\n            cartesian.master = _this;\n            cartesian.model = gridModel;\n            _this._coordsMap[key] = cartesian;\n\n            _this._coordsList.push(cartesian);\n\n            cartesian.addAxis(xAxis);\n            cartesian.addAxis(yAxis);\n          });\n        });\n\n        function createAxisCreator(dimName) {\n          return function (axisModel, idx) {\n            if (!isAxisUsedInTheGrid(axisModel, gridModel)) {\n              return;\n            }\n\n            var axisPosition = axisModel.get('position');\n\n            if (dimName === 'x') {\n              // Fix position\n              if (axisPosition !== 'top' && axisPosition !== 'bottom') {\n                // Default bottom of X\n                axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom';\n              }\n            } else {\n              // Fix position\n              if (axisPosition !== 'left' && axisPosition !== 'right') {\n                // Default left of Y\n                axisPosition = axisPositionUsed.left ? 'right' : 'left';\n              }\n            }\n\n            axisPositionUsed[axisPosition] = true;\n            var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);\n            var isCategory = axis.type === 'category';\n            axis.onBand = isCategory && axisModel.get('boundaryGap');\n            axis.inverse = axisModel.get('inverse'); // Inject axis into axisModel\n\n            axisModel.axis = axis; // Inject axisModel into axis\n\n            axis.model = axisModel; // Inject grid info axis\n\n            axis.grid = grid; // Index of axis, can be used as key\n\n            axis.index = idx;\n\n            grid._axesList.push(axis);\n\n            axesMap[dimName][idx] = axis;\n            axesCount[dimName]++;\n          };\n        }\n      };\n      /**\n       * Update cartesian properties from series.\n       */\n\n\n      Grid.prototype._updateScale = function (ecModel, gridModel) {\n        // Reset scale\n        each(this._axesList, function (axis) {\n          axis.scale.setExtent(Infinity, -Infinity);\n\n          if (axis.type === 'category') {\n            var categorySortInfo = axis.model.get('categorySortInfo');\n            axis.scale.setSortInfo(categorySortInfo);\n          }\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          if (isCartesian2DSeries(seriesModel)) {\n            var axesModelMap = findAxisModels(seriesModel);\n            var xAxisModel = axesModelMap.xAxisModel;\n            var yAxisModel = axesModelMap.yAxisModel;\n\n            if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) {\n              return;\n            }\n\n            var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n            var data = seriesModel.getData();\n            var xAxis = cartesian.getAxis('x');\n            var yAxis = cartesian.getAxis('y');\n            unionExtent(data, xAxis);\n            unionExtent(data, yAxis);\n          }\n        }, this);\n\n        function unionExtent(data, axis) {\n          each(getDataDimensionsOnAxis(data, axis.dim), function (dim) {\n            axis.scale.unionExtentFromData(data, dim);\n          });\n        }\n      };\n      /**\n       * @param dim 'x' or 'y' or 'auto' or null/undefined\n       */\n\n\n      Grid.prototype.getTooltipAxes = function (dim) {\n        var baseAxes = [];\n        var otherAxes = [];\n        each(this.getCartesians(), function (cartesian) {\n          var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();\n          var otherAxis = cartesian.getOtherAxis(baseAxis);\n          indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);\n          indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);\n        });\n        return {\n          baseAxes: baseAxes,\n          otherAxes: otherAxes\n        };\n      };\n\n      Grid.create = function (ecModel, api) {\n        var grids = [];\n        ecModel.eachComponent('grid', function (gridModel, idx) {\n          var grid = new Grid(gridModel, ecModel, api);\n          grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize\n          // should be performed in create stage.\n\n          grid.resize(gridModel, api, true);\n          gridModel.coordinateSystem = grid;\n          grids.push(grid);\n        }); // Inject the coordinateSystems into seriesModel\n\n        ecModel.eachSeries(function (seriesModel) {\n          if (!isCartesian2DSeries(seriesModel)) {\n            return;\n          }\n\n          var axesModelMap = findAxisModels(seriesModel);\n          var xAxisModel = axesModelMap.xAxisModel;\n          var yAxisModel = axesModelMap.yAxisModel;\n          var gridModel = xAxisModel.getCoordSysModel();\n\n          if (\"development\" !== 'production') {\n            if (!gridModel) {\n              throw new Error('Grid \"' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '\" not found');\n            }\n\n            if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {\n              throw new Error('xAxis and yAxis must use the same grid');\n            }\n          }\n\n          var grid = gridModel.coordinateSystem;\n          seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n        });\n        return grids;\n      }; // For deciding which dimensions to use when creating list data\n\n\n      Grid.dimensions = cartesian2DDimensions;\n      return Grid;\n    }();\n    /**\n     * Check if the axis is used in the specified grid.\n     */\n\n\n    function isAxisUsedInTheGrid(axisModel, gridModel) {\n      return axisModel.getCoordSysModel() === gridModel;\n    }\n\n    function fixAxisOnZero(axesMap, otherAxisDim, axis, // Key: see `getOnZeroRecordKey`\n    onZeroRecords) {\n      axis.getAxesOnZeroOf = function () {\n        // TODO: onZero of multiple axes.\n        return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : [];\n      }; // onZero can not be enabled in these two situations:\n      // 1. When any other axis is a category axis.\n      // 2. When no axis is cross 0 point.\n\n\n      var otherAxes = axesMap[otherAxisDim];\n      var otherAxisOnZeroOf;\n      var axisModel = axis.model;\n      var onZero = axisModel.get(['axisLine', 'onZero']);\n      var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']);\n\n      if (!onZero) {\n        return;\n      } // If target axis is specified.\n\n\n      if (onZeroAxisIndex != null) {\n        if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {\n          otherAxisOnZeroOf = otherAxes[onZeroAxisIndex];\n        }\n      } else {\n        // Find the first available other axis.\n        for (var idx in otherAxes) {\n          if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx]) // Consider that two Y axes on one value axis,\n          // if both onZero, the two Y axes overlap.\n          && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) {\n            otherAxisOnZeroOf = otherAxes[idx];\n            break;\n          }\n        }\n      }\n\n      if (otherAxisOnZeroOf) {\n        onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true;\n      }\n\n      function getOnZeroRecordKey(axis) {\n        return axis.dim + '_' + axis.index;\n      }\n    }\n\n    function canOnZeroToAxis(axis) {\n      return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);\n    }\n\n    function updateAxisTransform(axis, coordBase) {\n      var axisExtent = axis.getExtent();\n      var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform\n\n      axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {\n        return coord + coordBase;\n      } : function (coord) {\n        return axisExtentSum - coord + coordBase;\n      };\n      axis.toLocalCoord = axis.dim === 'x' ? function (coord) {\n        return coord - coordBase;\n      } : function (coord) {\n        return axisExtentSum - coord + coordBase;\n      };\n    }\n\n    var PI$5 = Math.PI;\n    /**\n     * A final axis is translated and rotated from a \"standard axis\".\n     * So opt.position and opt.rotation is required.\n     *\n     * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],\n     * for example: (0, 0) ------------> (0, 50)\n     *\n     * nameDirection or tickDirection or labelDirection is 1 means tick\n     * or label is below the standard axis, whereas is -1 means above\n     * the standard axis. labelOffset means offset between label and axis,\n     * which is useful when 'onZero', where axisLabel is in the grid and\n     * label in outside grid.\n     *\n     * Tips: like always,\n     * positive rotation represents anticlockwise, and negative rotation\n     * represents clockwise.\n     * The direction of position coordinate is the same as the direction\n     * of screen coordinate.\n     *\n     * Do not need to consider axis 'inverse', which is auto processed by\n     * axis extent.\n     */\n\n    var AxisBuilder =\n    /** @class */\n    function () {\n      function AxisBuilder(axisModel, opt) {\n        this.group = new Group();\n        this.opt = opt;\n        this.axisModel = axisModel; // Default value\n\n        defaults(opt, {\n          labelOffset: 0,\n          nameDirection: 1,\n          tickDirection: 1,\n          labelDirection: 1,\n          silent: true,\n          handleAutoShown: function () {\n            return true;\n          }\n        }); // FIXME Not use a seperate text group?\n\n        var transformGroup = new Group({\n          x: opt.position[0],\n          y: opt.position[1],\n          rotation: opt.rotation\n        }); // this.group.add(transformGroup);\n        // this._transformGroup = transformGroup;\n\n        transformGroup.updateTransform();\n        this._transformGroup = transformGroup;\n      }\n\n      AxisBuilder.prototype.hasBuilder = function (name) {\n        return !!builders[name];\n      };\n\n      AxisBuilder.prototype.add = function (name) {\n        builders[name](this.opt, this.axisModel, this.group, this._transformGroup);\n      };\n\n      AxisBuilder.prototype.getGroup = function () {\n        return this.group;\n      };\n\n      AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {\n        var rotationDiff = remRadian(textRotation - axisRotation);\n        var textAlign;\n        var textVerticalAlign;\n\n        if (isRadianAroundZero(rotationDiff)) {\n          // Label is parallel with axis line.\n          textVerticalAlign = direction > 0 ? 'top' : 'bottom';\n          textAlign = 'center';\n        } else if (isRadianAroundZero(rotationDiff - PI$5)) {\n          // Label is inverse parallel with axis line.\n          textVerticalAlign = direction > 0 ? 'bottom' : 'top';\n          textAlign = 'center';\n        } else {\n          textVerticalAlign = 'middle';\n\n          if (rotationDiff > 0 && rotationDiff < PI$5) {\n            textAlign = direction > 0 ? 'right' : 'left';\n          } else {\n            textAlign = direction > 0 ? 'left' : 'right';\n          }\n        }\n\n        return {\n          rotation: rotationDiff,\n          textAlign: textAlign,\n          textVerticalAlign: textVerticalAlign\n        };\n      };\n\n      AxisBuilder.makeAxisEventDataBase = function (axisModel) {\n        var eventData = {\n          componentType: axisModel.mainType,\n          componentIndex: axisModel.componentIndex\n        };\n        eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;\n        return eventData;\n      };\n\n      AxisBuilder.isLabelSilent = function (axisModel) {\n        var tooltipOpt = axisModel.get('tooltip');\n        return axisModel.get('silent') // Consider mouse cursor, add these restrictions.\n        || !(axisModel.get('triggerEvent') || tooltipOpt && tooltipOpt.show);\n      };\n\n      return AxisBuilder;\n    }();\n    var builders = {\n      axisLine: function (opt, axisModel, group, transformGroup) {\n        var shown = axisModel.get(['axisLine', 'show']);\n\n        if (shown === 'auto' && opt.handleAutoShown) {\n          shown = opt.handleAutoShown('axisLine');\n        }\n\n        if (!shown) {\n          return;\n        }\n\n        var extent = axisModel.axis.getExtent();\n        var matrix = transformGroup.transform;\n        var pt1 = [extent[0], 0];\n        var pt2 = [extent[1], 0];\n        var inverse = pt1[0] > pt2[0];\n\n        if (matrix) {\n          applyTransform(pt1, pt1, matrix);\n          applyTransform(pt2, pt2, matrix);\n        }\n\n        var lineStyle = extend({\n          lineCap: 'round'\n        }, axisModel.getModel(['axisLine', 'lineStyle']).getLineStyle());\n        var line = new Line({\n          shape: {\n            x1: pt1[0],\n            y1: pt1[1],\n            x2: pt2[0],\n            y2: pt2[1]\n          },\n          style: lineStyle,\n          strokeContainThreshold: opt.strokeContainThreshold || 5,\n          silent: true,\n          z2: 1\n        });\n        subPixelOptimizeLine$1(line.shape, line.style.lineWidth);\n        line.anid = 'line';\n        group.add(line);\n        var arrows = axisModel.get(['axisLine', 'symbol']);\n\n        if (arrows != null) {\n          var arrowSize = axisModel.get(['axisLine', 'symbolSize']);\n\n          if (isString(arrows)) {\n            // Use the same arrow for start and end point\n            arrows = [arrows, arrows];\n          }\n\n          if (isString(arrowSize) || isNumber(arrowSize)) {\n            // Use the same size for width and height\n            arrowSize = [arrowSize, arrowSize];\n          }\n\n          var arrowOffset = normalizeSymbolOffset(axisModel.get(['axisLine', 'symbolOffset']) || 0, arrowSize);\n          var symbolWidth_1 = arrowSize[0];\n          var symbolHeight_1 = arrowSize[1];\n          each([{\n            rotate: opt.rotation + Math.PI / 2,\n            offset: arrowOffset[0],\n            r: 0\n          }, {\n            rotate: opt.rotation - Math.PI / 2,\n            offset: arrowOffset[1],\n            r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1]))\n          }], function (point, index) {\n            if (arrows[index] !== 'none' && arrows[index] != null) {\n              var symbol = createSymbol(arrows[index], -symbolWidth_1 / 2, -symbolHeight_1 / 2, symbolWidth_1, symbolHeight_1, lineStyle.stroke, true); // Calculate arrow position with offset\n\n              var r = point.r + point.offset;\n              var pt = inverse ? pt2 : pt1;\n              symbol.attr({\n                rotation: point.rotate,\n                x: pt[0] + r * Math.cos(opt.rotation),\n                y: pt[1] - r * Math.sin(opt.rotation),\n                silent: true,\n                z2: 11\n              });\n              group.add(symbol);\n            }\n          });\n        }\n      },\n      axisTickLabel: function (opt, axisModel, group, transformGroup) {\n        var ticksEls = buildAxisMajorTicks(group, transformGroup, axisModel, opt);\n        var labelEls = buildAxisLabel(group, transformGroup, axisModel, opt);\n        fixMinMaxLabelShow(axisModel, labelEls, ticksEls);\n        buildAxisMinorTicks(group, transformGroup, axisModel, opt.tickDirection); // This bit fixes the label overlap issue for the time chart.\n        // See https://github.com/apache/echarts/issues/14266 for more.\n\n        if (axisModel.get(['axisLabel', 'hideOverlap'])) {\n          var labelList = prepareLayoutList(map(labelEls, function (label) {\n            return {\n              label: label,\n              priority: label.z2,\n              defaultAttr: {\n                ignore: label.ignore\n              }\n            };\n          }));\n          hideOverlap(labelList);\n        }\n      },\n      axisName: function (opt, axisModel, group, transformGroup) {\n        var name = retrieve(opt.axisName, axisModel.get('name'));\n\n        if (!name) {\n          return;\n        }\n\n        var nameLocation = axisModel.get('nameLocation');\n        var nameDirection = opt.nameDirection;\n        var textStyleModel = axisModel.getModel('nameTextStyle');\n        var gap = axisModel.get('nameGap') || 0;\n        var extent = axisModel.axis.getExtent();\n        var gapSignal = extent[0] > extent[1] ? -1 : 1;\n        var pos = [nameLocation === 'start' ? extent[0] - gapSignal * gap : nameLocation === 'end' ? extent[1] + gapSignal * gap : (extent[0] + extent[1]) / 2, // Reuse labelOffset.\n        isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0];\n        var labelLayout;\n        var nameRotation = axisModel.get('nameRotate');\n\n        if (nameRotation != null) {\n          nameRotation = nameRotation * PI$5 / 180; // To radian.\n        }\n\n        var axisNameAvailableWidth;\n\n        if (isNameLocationCenter(nameLocation)) {\n          labelLayout = AxisBuilder.innerTextLayout(opt.rotation, nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.\n          nameDirection);\n        } else {\n          labelLayout = endTextLayout(opt.rotation, nameLocation, nameRotation || 0, extent);\n          axisNameAvailableWidth = opt.axisNameAvailableWidth;\n\n          if (axisNameAvailableWidth != null) {\n            axisNameAvailableWidth = Math.abs(axisNameAvailableWidth / Math.sin(labelLayout.rotation));\n            !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);\n          }\n        }\n\n        var textFont = textStyleModel.getFont();\n        var truncateOpt = axisModel.get('nameTruncate', true) || {};\n        var ellipsis = truncateOpt.ellipsis;\n        var maxWidth = retrieve(opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth);\n        var textEl = new ZRText({\n          x: pos[0],\n          y: pos[1],\n          rotation: labelLayout.rotation,\n          silent: AxisBuilder.isLabelSilent(axisModel),\n          style: createTextStyle(textStyleModel, {\n            text: name,\n            font: textFont,\n            overflow: 'truncate',\n            width: maxWidth,\n            ellipsis: ellipsis,\n            fill: textStyleModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']),\n            align: textStyleModel.get('align') || labelLayout.textAlign,\n            verticalAlign: textStyleModel.get('verticalAlign') || labelLayout.textVerticalAlign\n          }),\n          z2: 1\n        });\n        setTooltipConfig({\n          el: textEl,\n          componentModel: axisModel,\n          itemName: name\n        });\n        textEl.__fullText = name; // Id for animation\n\n        textEl.anid = 'name';\n\n        if (axisModel.get('triggerEvent')) {\n          var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n          eventData.targetType = 'axisName';\n          eventData.name = name;\n          getECData(textEl).eventData = eventData;\n        } // FIXME\n\n\n        transformGroup.add(textEl);\n        textEl.updateTransform();\n        group.add(textEl);\n        textEl.decomposeTransform();\n      }\n    };\n\n    function endTextLayout(rotation, textPosition, textRotate, extent) {\n      var rotationDiff = remRadian(textRotate - rotation);\n      var textAlign;\n      var textVerticalAlign;\n      var inverse = extent[0] > extent[1];\n      var onLeft = textPosition === 'start' && !inverse || textPosition !== 'start' && inverse;\n\n      if (isRadianAroundZero(rotationDiff - PI$5 / 2)) {\n        textVerticalAlign = onLeft ? 'bottom' : 'top';\n        textAlign = 'center';\n      } else if (isRadianAroundZero(rotationDiff - PI$5 * 1.5)) {\n        textVerticalAlign = onLeft ? 'top' : 'bottom';\n        textAlign = 'center';\n      } else {\n        textVerticalAlign = 'middle';\n\n        if (rotationDiff < PI$5 * 1.5 && rotationDiff > PI$5 / 2) {\n          textAlign = onLeft ? 'left' : 'right';\n        } else {\n          textAlign = onLeft ? 'right' : 'left';\n        }\n      }\n\n      return {\n        rotation: rotationDiff,\n        textAlign: textAlign,\n        textVerticalAlign: textVerticalAlign\n      };\n    }\n\n    function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {\n      if (shouldShowAllLabels(axisModel.axis)) {\n        return;\n      } // If min or max are user set, we need to check\n      // If the tick on min(max) are overlap on their neighbour tick\n      // If they are overlapped, we need to hide the min(max) tick label\n\n\n      var showMinLabel = axisModel.get(['axisLabel', 'showMinLabel']);\n      var showMaxLabel = axisModel.get(['axisLabel', 'showMaxLabel']); // FIXME\n      // Have not consider onBand yet, where tick els is more than label els.\n\n      labelEls = labelEls || [];\n      tickEls = tickEls || [];\n      var firstLabel = labelEls[0];\n      var nextLabel = labelEls[1];\n      var lastLabel = labelEls[labelEls.length - 1];\n      var prevLabel = labelEls[labelEls.length - 2];\n      var firstTick = tickEls[0];\n      var nextTick = tickEls[1];\n      var lastTick = tickEls[tickEls.length - 1];\n      var prevTick = tickEls[tickEls.length - 2];\n\n      if (showMinLabel === false) {\n        ignoreEl(firstLabel);\n        ignoreEl(firstTick);\n      } else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {\n        if (showMinLabel) {\n          ignoreEl(nextLabel);\n          ignoreEl(nextTick);\n        } else {\n          ignoreEl(firstLabel);\n          ignoreEl(firstTick);\n        }\n      }\n\n      if (showMaxLabel === false) {\n        ignoreEl(lastLabel);\n        ignoreEl(lastTick);\n      } else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {\n        if (showMaxLabel) {\n          ignoreEl(prevLabel);\n          ignoreEl(prevTick);\n        } else {\n          ignoreEl(lastLabel);\n          ignoreEl(lastTick);\n        }\n      }\n    }\n\n    function ignoreEl(el) {\n      el && (el.ignore = true);\n    }\n\n    function isTwoLabelOverlapped(current, next) {\n      // current and next has the same rotation.\n      var firstRect = current && current.getBoundingRect().clone();\n      var nextRect = next && next.getBoundingRect().clone();\n\n      if (!firstRect || !nextRect) {\n        return;\n      } // When checking intersect of two rotated labels, we use mRotationBack\n      // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.\n\n\n      var mRotationBack = identity([]);\n      rotate(mRotationBack, mRotationBack, -current.rotation);\n      firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));\n      nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));\n      return firstRect.intersect(nextRect);\n    }\n\n    function isNameLocationCenter(nameLocation) {\n      return nameLocation === 'middle' || nameLocation === 'center';\n    }\n\n    function createTicks(ticksCoords, tickTransform, tickEndCoord, tickLineStyle, anidPrefix) {\n      var tickEls = [];\n      var pt1 = [];\n      var pt2 = [];\n\n      for (var i = 0; i < ticksCoords.length; i++) {\n        var tickCoord = ticksCoords[i].coord;\n        pt1[0] = tickCoord;\n        pt1[1] = 0;\n        pt2[0] = tickCoord;\n        pt2[1] = tickEndCoord;\n\n        if (tickTransform) {\n          applyTransform(pt1, pt1, tickTransform);\n          applyTransform(pt2, pt2, tickTransform);\n        } // Tick line, Not use group transform to have better line draw\n\n\n        var tickEl = new Line({\n          shape: {\n            x1: pt1[0],\n            y1: pt1[1],\n            x2: pt2[0],\n            y2: pt2[1]\n          },\n          style: tickLineStyle,\n          z2: 2,\n          autoBatch: true,\n          silent: true\n        });\n        subPixelOptimizeLine$1(tickEl.shape, tickEl.style.lineWidth);\n        tickEl.anid = anidPrefix + '_' + ticksCoords[i].tickValue;\n        tickEls.push(tickEl);\n      }\n\n      return tickEls;\n    }\n\n    function buildAxisMajorTicks(group, transformGroup, axisModel, opt) {\n      var axis = axisModel.axis;\n      var tickModel = axisModel.getModel('axisTick');\n      var shown = tickModel.get('show');\n\n      if (shown === 'auto' && opt.handleAutoShown) {\n        shown = opt.handleAutoShown('axisTick');\n      }\n\n      if (!shown || axis.scale.isBlank()) {\n        return;\n      }\n\n      var lineStyleModel = tickModel.getModel('lineStyle');\n      var tickEndCoord = opt.tickDirection * tickModel.get('length');\n      var ticksCoords = axis.getTicksCoords();\n      var ticksEls = createTicks(ticksCoords, transformGroup.transform, tickEndCoord, defaults(lineStyleModel.getLineStyle(), {\n        stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n      }), 'ticks');\n\n      for (var i = 0; i < ticksEls.length; i++) {\n        group.add(ticksEls[i]);\n      }\n\n      return ticksEls;\n    }\n\n    function buildAxisMinorTicks(group, transformGroup, axisModel, tickDirection) {\n      var axis = axisModel.axis;\n      var minorTickModel = axisModel.getModel('minorTick');\n\n      if (!minorTickModel.get('show') || axis.scale.isBlank()) {\n        return;\n      }\n\n      var minorTicksCoords = axis.getMinorTicksCoords();\n\n      if (!minorTicksCoords.length) {\n        return;\n      }\n\n      var lineStyleModel = minorTickModel.getModel('lineStyle');\n      var tickEndCoord = tickDirection * minorTickModel.get('length');\n      var minorTickLineStyle = defaults(lineStyleModel.getLineStyle(), defaults(axisModel.getModel('axisTick').getLineStyle(), {\n        stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n      }));\n\n      for (var i = 0; i < minorTicksCoords.length; i++) {\n        var minorTicksEls = createTicks(minorTicksCoords[i], transformGroup.transform, tickEndCoord, minorTickLineStyle, 'minorticks_' + i);\n\n        for (var k = 0; k < minorTicksEls.length; k++) {\n          group.add(minorTicksEls[k]);\n        }\n      }\n    }\n\n    function buildAxisLabel(group, transformGroup, axisModel, opt) {\n      var axis = axisModel.axis;\n      var show = retrieve(opt.axisLabelShow, axisModel.get(['axisLabel', 'show']));\n\n      if (!show || axis.scale.isBlank()) {\n        return;\n      }\n\n      var labelModel = axisModel.getModel('axisLabel');\n      var labelMargin = labelModel.get('margin');\n      var labels = axis.getViewLabels(); // Special label rotate.\n\n      var labelRotation = (retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) * PI$5 / 180;\n      var labelLayout = AxisBuilder.innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);\n      var rawCategoryData = axisModel.getCategories && axisModel.getCategories(true);\n      var labelEls = [];\n      var silent = AxisBuilder.isLabelSilent(axisModel);\n      var triggerEvent = axisModel.get('triggerEvent');\n      each(labels, function (labelItem, index) {\n        var tickValue = axis.scale.type === 'ordinal' ? axis.scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue;\n        var formattedLabel = labelItem.formattedLabel;\n        var rawLabel = labelItem.rawLabel;\n        var itemLabelModel = labelModel;\n\n        if (rawCategoryData && rawCategoryData[tickValue]) {\n          var rawCategoryItem = rawCategoryData[tickValue];\n\n          if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) {\n            itemLabelModel = new Model(rawCategoryItem.textStyle, labelModel, axisModel.ecModel);\n          }\n        }\n\n        var textColor = itemLabelModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']);\n        var tickCoord = axis.dataToCoord(tickValue);\n        var textEl = new ZRText({\n          x: tickCoord,\n          y: opt.labelOffset + opt.labelDirection * labelMargin,\n          rotation: labelLayout.rotation,\n          silent: silent,\n          z2: 10 + (labelItem.level || 0),\n          style: createTextStyle(itemLabelModel, {\n            text: formattedLabel,\n            align: itemLabelModel.getShallow('align', true) || labelLayout.textAlign,\n            verticalAlign: itemLabelModel.getShallow('verticalAlign', true) || itemLabelModel.getShallow('baseline', true) || labelLayout.textVerticalAlign,\n            fill: isFunction(textColor) ? textColor( // (1) In category axis with data zoom, tick is not the original\n            // index of axis.data. So tick should not be exposed to user\n            // in category axis.\n            // (2) Compatible with previous version, which always use formatted label as\n            // input. But in interval scale the formatted label is like '223,445', which\n            // maked user repalce ','. So we modify it to return original val but remain\n            // it as 'string' to avoid error in replacing.\n            axis.type === 'category' ? rawLabel : axis.type === 'value' ? tickValue + '' : tickValue, index) : textColor\n          })\n        });\n        textEl.anid = 'label_' + tickValue; // Pack data for mouse event\n\n        if (triggerEvent) {\n          var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n          eventData.targetType = 'axisLabel';\n          eventData.value = rawLabel;\n          eventData.tickIndex = index;\n\n          if (axis.type === 'category') {\n            eventData.dataIndex = tickValue;\n          }\n\n          getECData(textEl).eventData = eventData;\n        } // FIXME\n\n\n        transformGroup.add(textEl);\n        textEl.updateTransform();\n        labelEls.push(textEl);\n        group.add(textEl);\n        textEl.decomposeTransform();\n      });\n      return labelEls;\n    }\n\n    // allAxesInfo should be updated when setOption performed.\n\n    function collect(ecModel, api) {\n      var result = {\n        /**\n         * key: makeKey(axis.model)\n         * value: {\n         *      axis,\n         *      coordSys,\n         *      axisPointerModel,\n         *      triggerTooltip,\n         *      involveSeries,\n         *      snap,\n         *      seriesModels,\n         *      seriesDataCount\n         * }\n         */\n        axesInfo: {},\n        seriesInvolved: false,\n\n        /**\n         * key: makeKey(coordSys.model)\n         * value: Object: key makeKey(axis.model), value: axisInfo\n         */\n        coordSysAxesInfo: {},\n        coordSysMap: {}\n      };\n      collectAxesInfo(result, ecModel, api); // Check seriesInvolved for performance, in case too many series in some chart.\n\n      result.seriesInvolved && collectSeriesInfo(result, ecModel);\n      return result;\n    }\n\n    function collectAxesInfo(result, ecModel, api) {\n      var globalTooltipModel = ecModel.getComponent('tooltip');\n      var globalAxisPointerModel = ecModel.getComponent('axisPointer'); // links can only be set on global.\n\n      var linksOption = globalAxisPointerModel.get('link', true) || [];\n      var linkGroups = []; // Collect axes info.\n\n      each(api.getCoordinateSystems(), function (coordSys) {\n        // Some coordinate system do not support axes, like geo.\n        if (!coordSys.axisPointerEnabled) {\n          return;\n        }\n\n        var coordSysKey = makeKey(coordSys.model);\n        var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};\n        result.coordSysMap[coordSysKey] = coordSys; // Set tooltip (like 'cross') is a convienent way to show axisPointer\n        // for user. So we enable seting tooltip on coordSys model.\n\n        var coordSysModel = coordSys.model;\n        var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);\n        each(coordSys.getAxes(), curry(saveTooltipAxisInfo, false, null)); // If axis tooltip used, choose tooltip axis for each coordSys.\n        // Notice this case: coordSys is `grid` but not `cartesian2D` here.\n\n        if (coordSys.getTooltipAxes && globalTooltipModel // If tooltip.showContent is set as false, tooltip will not\n        // show but axisPointer will show as normal.\n        && baseTooltipModel.get('show')) {\n          // Compatible with previous logic. But series.tooltip.trigger: 'axis'\n          // or series.data[n].tooltip.trigger: 'axis' are not support any more.\n          var triggerAxis = baseTooltipModel.get('trigger') === 'axis';\n          var cross = baseTooltipModel.get(['axisPointer', 'type']) === 'cross';\n          var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get(['axisPointer', 'axis']));\n\n          if (triggerAxis || cross) {\n            each(tooltipAxes.baseAxes, curry(saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis));\n          }\n\n          if (cross) {\n            each(tooltipAxes.otherAxes, curry(saveTooltipAxisInfo, 'cross', false));\n          }\n        } // fromTooltip: true | false | 'cross'\n        // triggerTooltip: true | false | null\n\n\n        function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {\n          var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);\n          var axisPointerShow = axisPointerModel.get('show');\n\n          if (!axisPointerShow || axisPointerShow === 'auto' && !fromTooltip && !isHandleTrigger(axisPointerModel)) {\n            return;\n          }\n\n          if (triggerTooltip == null) {\n            triggerTooltip = axisPointerModel.get('triggerTooltip');\n          }\n\n          axisPointerModel = fromTooltip ? makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) : axisPointerModel;\n          var snap = axisPointerModel.get('snap');\n          var axisKey = makeKey(axis.model);\n          var involveSeries = triggerTooltip || snap || axis.type === 'category'; // If result.axesInfo[key] exist, override it (tooltip has higher priority).\n\n          var axisInfo = result.axesInfo[axisKey] = {\n            key: axisKey,\n            axis: axis,\n            coordSys: coordSys,\n            axisPointerModel: axisPointerModel,\n            triggerTooltip: triggerTooltip,\n            involveSeries: involveSeries,\n            snap: snap,\n            useHandle: isHandleTrigger(axisPointerModel),\n            seriesModels: [],\n            linkGroup: null\n          };\n          axesInfoInCoordSys[axisKey] = axisInfo;\n          result.seriesInvolved = result.seriesInvolved || involveSeries;\n          var groupIndex = getLinkGroupIndex(linksOption, axis);\n\n          if (groupIndex != null) {\n            var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {\n              axesInfo: {}\n            });\n            linkGroup.axesInfo[axisKey] = axisInfo;\n            linkGroup.mapper = linksOption[groupIndex].mapper;\n            axisInfo.linkGroup = linkGroup;\n          }\n        }\n      });\n    }\n\n    function makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) {\n      var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');\n      var fields = ['type', 'snap', 'lineStyle', 'shadowStyle', 'label', 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'];\n      var volatileOption = {};\n      each(fields, function (field) {\n        volatileOption[field] = clone(tooltipAxisPointerModel.get(field));\n      }); // category axis do not auto snap, otherwise some tick that do not\n      // has value can not be hovered. value/time/log axis default snap if\n      // triggered from tooltip and trigger tooltip.\n\n      volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; // Compatibel with previous behavior, tooltip axis do not show label by default.\n      // Only these properties can be overrided from tooltip to axisPointer.\n\n      if (tooltipAxisPointerModel.get('type') === 'cross') {\n        volatileOption.type = 'line';\n      }\n\n      var labelOption = volatileOption.label || (volatileOption.label = {}); // Follow the convention, do not show label when triggered by tooltip by default.\n\n      labelOption.show == null && (labelOption.show = false);\n\n      if (fromTooltip === 'cross') {\n        // When 'cross', both axes show labels.\n        var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get(['label', 'show']);\n        labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true; // If triggerTooltip, this is a base axis, which should better not use cross style\n        // (cross style is dashed by default)\n\n        if (!triggerTooltip) {\n          var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');\n          crossStyle && defaults(labelOption, crossStyle.textStyle);\n        }\n      }\n\n      return axis.model.getModel('axisPointer', new Model(volatileOption, globalAxisPointerModel, ecModel));\n    }\n\n    function collectSeriesInfo(result, ecModel) {\n      // Prepare data for axis trigger\n      ecModel.eachSeries(function (seriesModel) {\n        // Notice this case: this coordSys is `cartesian2D` but not `grid`.\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesTooltipTrigger = seriesModel.get(['tooltip', 'trigger'], true);\n        var seriesTooltipShow = seriesModel.get(['tooltip', 'show'], true);\n\n        if (!coordSys || seriesTooltipTrigger === 'none' || seriesTooltipTrigger === false || seriesTooltipTrigger === 'item' || seriesTooltipShow === false || seriesModel.get(['axisPointer', 'show'], true) === false) {\n          return;\n        }\n\n        each(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {\n          var axis = axisInfo.axis;\n\n          if (coordSys.getAxis(axis.dim) === axis) {\n            axisInfo.seriesModels.push(seriesModel);\n            axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);\n            axisInfo.seriesDataCount += seriesModel.getData().count();\n          }\n        });\n      });\n    }\n    /**\n     * For example:\n     * {\n     *     axisPointer: {\n     *         links: [{\n     *             xAxisIndex: [2, 4],\n     *             yAxisIndex: 'all'\n     *         }, {\n     *             xAxisId: ['a5', 'a7'],\n     *             xAxisName: 'xxx'\n     *         }]\n     *     }\n     * }\n     */\n\n\n    function getLinkGroupIndex(linksOption, axis) {\n      var axisModel = axis.model;\n      var dim = axis.dim;\n\n      for (var i = 0; i < linksOption.length; i++) {\n        var linkOption = linksOption[i] || {};\n\n        if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)) {\n          return i;\n        }\n      }\n    }\n\n    function checkPropInLink(linkPropValue, axisPropValue) {\n      return linkPropValue === 'all' || isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0 || linkPropValue === axisPropValue;\n    }\n\n    function fixValue(axisModel) {\n      var axisInfo = getAxisInfo(axisModel);\n\n      if (!axisInfo) {\n        return;\n      }\n\n      var axisPointerModel = axisInfo.axisPointerModel;\n      var scale = axisInfo.axis.scale;\n      var option = axisPointerModel.option;\n      var status = axisPointerModel.get('status');\n      var value = axisPointerModel.get('value'); // Parse init value for category and time axis.\n\n      if (value != null) {\n        value = scale.parse(value);\n      }\n\n      var useHandle = isHandleTrigger(axisPointerModel); // If `handle` used, `axisPointer` will always be displayed, so value\n      // and status should be initialized.\n\n      if (status == null) {\n        option.status = useHandle ? 'show' : 'hide';\n      }\n\n      var extent = scale.getExtent().slice();\n      extent[0] > extent[1] && extent.reverse();\n\n      if ( // Pick a value on axis when initializing.\n      value == null // If both `handle` and `dataZoom` are used, value may be out of axis extent,\n      // where we should re-pick a value to keep `handle` displaying normally.\n      || value > extent[1]) {\n        // Make handle displayed on the end of the axis when init, which looks better.\n        value = extent[1];\n      }\n\n      if (value < extent[0]) {\n        value = extent[0];\n      }\n\n      option.value = value;\n\n      if (useHandle) {\n        option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';\n      }\n    }\n    function getAxisInfo(axisModel) {\n      var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;\n      return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];\n    }\n    function getAxisPointerModel(axisModel) {\n      var axisInfo = getAxisInfo(axisModel);\n      return axisInfo && axisInfo.axisPointerModel;\n    }\n\n    function isHandleTrigger(axisPointerModel) {\n      return !!axisPointerModel.get(['handle', 'show']);\n    }\n    /**\n     * @param {module:echarts/model/Model} model\n     * @return {string} unique key\n     */\n\n\n    function makeKey(model) {\n      return model.type + '||' + model.id;\n    }\n\n    var axisPointerClazz = {};\n    /**\n     * Base class of AxisView.\n     */\n\n    var AxisView =\n    /** @class */\n    function (_super) {\n      __extends(AxisView, _super);\n\n      function AxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AxisView.type;\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        // FIXME\n        // This process should proformed after coordinate systems updated\n        // (axis scale updated), and should be performed each time update.\n        // So put it here temporarily, although it is not appropriate to\n        // put a model-writing procedure in `view`.\n        this.axisPointerClass && fixValue(axisModel);\n\n        _super.prototype.render.apply(this, arguments);\n\n        this._doUpdateAxisPointerClass(axisModel, api, true);\n      };\n      /**\n       * Action handler.\n       */\n\n\n      AxisView.prototype.updateAxisPointer = function (axisModel, ecModel, api, payload) {\n        this._doUpdateAxisPointerClass(axisModel, api, false);\n      };\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.remove = function (ecModel, api) {\n        var axisPointer = this._axisPointer;\n        axisPointer && axisPointer.remove(api);\n      };\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.dispose = function (ecModel, api) {\n        this._disposeAxisPointer(api);\n\n        _super.prototype.dispose.apply(this, arguments);\n      };\n\n      AxisView.prototype._doUpdateAxisPointerClass = function (axisModel, api, forceRender) {\n        var Clazz = AxisView.getAxisPointerClass(this.axisPointerClass);\n\n        if (!Clazz) {\n          return;\n        }\n\n        var axisPointerModel = getAxisPointerModel(axisModel);\n        axisPointerModel ? (this._axisPointer || (this._axisPointer = new Clazz())).render(axisModel, axisPointerModel, api, forceRender) : this._disposeAxisPointer(api);\n      };\n\n      AxisView.prototype._disposeAxisPointer = function (api) {\n        this._axisPointer && this._axisPointer.dispose(api);\n        this._axisPointer = null;\n      };\n\n      AxisView.registerAxisPointerClass = function (type, clazz) {\n        if (\"development\" !== 'production') {\n          if (axisPointerClazz[type]) {\n            throw new Error('axisPointer ' + type + ' exists');\n          }\n        }\n\n        axisPointerClazz[type] = clazz;\n      };\n\n      AxisView.getAxisPointerClass = function (type) {\n        return type && axisPointerClazz[type];\n      };\n      AxisView.type = 'axis';\n      return AxisView;\n    }(ComponentView);\n\n    var inner$6 = makeInner();\n    function rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel) {\n      var axis = axisModel.axis;\n\n      if (axis.scale.isBlank()) {\n        return;\n      } // TODO: TYPE\n\n\n      var splitAreaModel = axisModel.getModel('splitArea');\n      var areaStyleModel = splitAreaModel.getModel('areaStyle');\n      var areaColors = areaStyleModel.get('color');\n      var gridRect = gridModel.coordinateSystem.getRect();\n      var ticksCoords = axis.getTicksCoords({\n        tickModel: splitAreaModel,\n        clamp: true\n      });\n\n      if (!ticksCoords.length) {\n        return;\n      } // For Making appropriate splitArea animation, the color and anid\n      // should be corresponding to previous one if possible.\n\n\n      var areaColorsLen = areaColors.length;\n      var lastSplitAreaColors = inner$6(axisView).splitAreaColors;\n      var newSplitAreaColors = createHashMap();\n      var colorIndex = 0;\n\n      if (lastSplitAreaColors) {\n        for (var i = 0; i < ticksCoords.length; i++) {\n          var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue);\n\n          if (cIndex != null) {\n            colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen;\n            break;\n          }\n        }\n      }\n\n      var prev = axis.toGlobalCoord(ticksCoords[0].coord);\n      var areaStyle = areaStyleModel.getAreaStyle();\n      areaColors = isArray(areaColors) ? areaColors : [areaColors];\n\n      for (var i = 1; i < ticksCoords.length; i++) {\n        var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n        var x = void 0;\n        var y = void 0;\n        var width = void 0;\n        var height = void 0;\n\n        if (axis.isHorizontal()) {\n          x = prev;\n          y = gridRect.y;\n          width = tickCoord - x;\n          height = gridRect.height;\n          prev = x + width;\n        } else {\n          x = gridRect.x;\n          y = prev;\n          width = gridRect.width;\n          height = tickCoord - y;\n          prev = y + height;\n        }\n\n        var tickValue = ticksCoords[i - 1].tickValue;\n        tickValue != null && newSplitAreaColors.set(tickValue, colorIndex);\n        axisGroup.add(new Rect({\n          anid: tickValue != null ? 'area_' + tickValue : null,\n          shape: {\n            x: x,\n            y: y,\n            width: width,\n            height: height\n          },\n          style: defaults({\n            fill: areaColors[colorIndex]\n          }, areaStyle),\n          autoBatch: true,\n          silent: true\n        }));\n        colorIndex = (colorIndex + 1) % areaColorsLen;\n      }\n\n      inner$6(axisView).splitAreaColors = newSplitAreaColors;\n    }\n    function rectCoordAxisHandleRemove(axisView) {\n      inner$6(axisView).splitAreaColors = null;\n    }\n\n    var axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName'];\n    var selfBuilderAttrs = ['splitArea', 'splitLine', 'minorSplitLine'];\n\n    var CartesianAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisView, _super);\n\n      function CartesianAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianAxisView.type;\n        _this.axisPointerClass = 'CartesianAxisPointer';\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      CartesianAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        this.group.removeAll();\n        var oldAxisGroup = this._axisGroup;\n        this._axisGroup = new Group();\n        this.group.add(this._axisGroup);\n\n        if (!axisModel.get('show')) {\n          return;\n        }\n\n        var gridModel = axisModel.getCoordSysModel();\n        var layout = layout$1(gridModel, axisModel);\n        var axisBuilder = new AxisBuilder(axisModel, extend({\n          handleAutoShown: function (elementType) {\n            var cartesians = gridModel.coordinateSystem.getCartesians();\n\n            for (var i = 0; i < cartesians.length; i++) {\n              if (isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) {\n                // Still show axis tick or axisLine if other axis is value / log\n                return true;\n              }\n            } // Not show axisTick or axisLine if other axis is category / time\n\n\n            return false;\n          }\n        }, layout));\n        each(axisBuilderAttrs, axisBuilder.add, axisBuilder);\n\n        this._axisGroup.add(axisBuilder.getGroup());\n\n        each(selfBuilderAttrs, function (name) {\n          if (axisModel.get([name, 'show'])) {\n            axisElementBuilders[name](this, this._axisGroup, axisModel, gridModel);\n          }\n        }, this); // THIS is a special case for bar racing chart.\n        // Update the axis label from the natural initial layout to\n        // sorted layout should has no animation.\n\n        var isInitialSortFromBarRacing = payload && payload.type === 'changeAxisOrder' && payload.isInitSort;\n\n        if (!isInitialSortFromBarRacing) {\n          groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n        }\n\n        _super.prototype.render.call(this, axisModel, ecModel, api, payload);\n      };\n\n      CartesianAxisView.prototype.remove = function () {\n        rectCoordAxisHandleRemove(this);\n      };\n\n      CartesianAxisView.type = 'cartesianAxis';\n      return CartesianAxisView;\n    }(AxisView);\n\n    var axisElementBuilders = {\n      splitLine: function (axisView, axisGroup, axisModel, gridModel) {\n        var axis = axisModel.axis;\n\n        if (axis.scale.isBlank()) {\n          return;\n        }\n\n        var splitLineModel = axisModel.getModel('splitLine');\n        var lineStyleModel = splitLineModel.getModel('lineStyle');\n        var lineColors = lineStyleModel.get('color');\n        lineColors = isArray(lineColors) ? lineColors : [lineColors];\n        var gridRect = gridModel.coordinateSystem.getRect();\n        var isHorizontal = axis.isHorizontal();\n        var lineCount = 0;\n        var ticksCoords = axis.getTicksCoords({\n          tickModel: splitLineModel\n        });\n        var p1 = [];\n        var p2 = [];\n        var lineStyle = lineStyleModel.getLineStyle();\n\n        for (var i = 0; i < ticksCoords.length; i++) {\n          var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n\n          if (isHorizontal) {\n            p1[0] = tickCoord;\n            p1[1] = gridRect.y;\n            p2[0] = tickCoord;\n            p2[1] = gridRect.y + gridRect.height;\n          } else {\n            p1[0] = gridRect.x;\n            p1[1] = tickCoord;\n            p2[0] = gridRect.x + gridRect.width;\n            p2[1] = tickCoord;\n          }\n\n          var colorIndex = lineCount++ % lineColors.length;\n          var tickValue = ticksCoords[i].tickValue;\n          var line = new Line({\n            anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null,\n            autoBatch: true,\n            shape: {\n              x1: p1[0],\n              y1: p1[1],\n              x2: p2[0],\n              y2: p2[1]\n            },\n            style: defaults({\n              stroke: lineColors[colorIndex]\n            }, lineStyle),\n            silent: true\n          });\n          subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n          axisGroup.add(line);\n        }\n      },\n      minorSplitLine: function (axisView, axisGroup, axisModel, gridModel) {\n        var axis = axisModel.axis;\n        var minorSplitLineModel = axisModel.getModel('minorSplitLine');\n        var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n        var gridRect = gridModel.coordinateSystem.getRect();\n        var isHorizontal = axis.isHorizontal();\n        var minorTicksCoords = axis.getMinorTicksCoords();\n\n        if (!minorTicksCoords.length) {\n          return;\n        }\n\n        var p1 = [];\n        var p2 = [];\n        var lineStyle = lineStyleModel.getLineStyle();\n\n        for (var i = 0; i < minorTicksCoords.length; i++) {\n          for (var k = 0; k < minorTicksCoords[i].length; k++) {\n            var tickCoord = axis.toGlobalCoord(minorTicksCoords[i][k].coord);\n\n            if (isHorizontal) {\n              p1[0] = tickCoord;\n              p1[1] = gridRect.y;\n              p2[0] = tickCoord;\n              p2[1] = gridRect.y + gridRect.height;\n            } else {\n              p1[0] = gridRect.x;\n              p1[1] = tickCoord;\n              p2[0] = gridRect.x + gridRect.width;\n              p2[1] = tickCoord;\n            }\n\n            var line = new Line({\n              anid: 'minor_line_' + minorTicksCoords[i][k].tickValue,\n              autoBatch: true,\n              shape: {\n                x1: p1[0],\n                y1: p1[1],\n                x2: p2[0],\n                y2: p2[1]\n              },\n              style: lineStyle,\n              silent: true\n            });\n            subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n            axisGroup.add(line);\n          }\n        }\n      },\n      splitArea: function (axisView, axisGroup, axisModel, gridModel) {\n        rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel);\n      }\n    };\n\n    var CartesianXAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianXAxisView, _super);\n\n      function CartesianXAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianXAxisView.type;\n        return _this;\n      }\n\n      CartesianXAxisView.type = 'xAxis';\n      return CartesianXAxisView;\n    }(CartesianAxisView);\n\n    var CartesianYAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianYAxisView, _super);\n\n      function CartesianYAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianXAxisView.type;\n        return _this;\n      }\n\n      CartesianYAxisView.type = 'yAxis';\n      return CartesianYAxisView;\n    }(CartesianAxisView);\n\n    var GridView =\n    /** @class */\n    function (_super) {\n      __extends(GridView, _super);\n\n      function GridView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'grid';\n        return _this;\n      }\n\n      GridView.prototype.render = function (gridModel, ecModel) {\n        this.group.removeAll();\n\n        if (gridModel.get('show')) {\n          this.group.add(new Rect({\n            shape: gridModel.coordinateSystem.getRect(),\n            style: defaults({\n              fill: gridModel.get('backgroundColor')\n            }, gridModel.getItemStyle()),\n            silent: true,\n            z2: -1\n          }));\n        }\n      };\n\n      GridView.type = 'grid';\n      return GridView;\n    }(ComponentView);\n\n    var extraOption = {\n      // gridIndex: 0,\n      // gridId: '',\n      offset: 0\n    };\n    function install$5(registers) {\n      registers.registerComponentView(GridView);\n      registers.registerComponentModel(GridModel);\n      registers.registerCoordinateSystem('cartesian2d', Grid);\n      axisModelCreator(registers, 'x', CartesianAxisModel, extraOption);\n      axisModelCreator(registers, 'y', CartesianAxisModel, extraOption);\n      registers.registerComponentView(CartesianXAxisView);\n      registers.registerComponentView(CartesianYAxisView);\n      registers.registerPreprocessor(function (option) {\n        // Only create grid when need\n        if (option.xAxis && option.yAxis && !option.grid) {\n          option.grid = {};\n        }\n      });\n    }\n\n    function install$6(registers) {\n      // In case developer forget to include grid component\n      use(install$5);\n      registers.registerSeriesModel(ScatterSeriesModel);\n      registers.registerChartView(ScatterView);\n      registers.registerLayout(pointsLayout('scatter'));\n    }\n\n    var inner$7 = makeInner();\n    var clone$3 = clone;\n    var bind$1 = bind;\n    /**\n     * Base axis pointer class in 2D.\n     */\n\n    var BaseAxisPointer =\n    /** @class */\n    function () {\n      function BaseAxisPointer() {\n        this._dragging = false;\n        /**\n         * In px, arbitrary value. Do not set too small,\n         * no animation is ok for most cases.\n         */\n\n        this.animationThreshold = 15;\n      }\n      /**\n       * @implement\n       */\n\n\n      BaseAxisPointer.prototype.render = function (axisModel, axisPointerModel, api, forceRender) {\n        var value = axisPointerModel.get('value');\n        var status = axisPointerModel.get('status'); // Bind them to `this`, not in closure, otherwise they will not\n        // be replaced when user calling setOption in not merge mode.\n\n        this._axisModel = axisModel;\n        this._axisPointerModel = axisPointerModel;\n        this._api = api; // Optimize: `render` will be called repeatly during mouse move.\n        // So it is power consuming if performing `render` each time,\n        // especially on mobile device.\n\n        if (!forceRender && this._lastValue === value && this._lastStatus === status) {\n          return;\n        }\n\n        this._lastValue = value;\n        this._lastStatus = status;\n        var group = this._group;\n        var handle = this._handle;\n\n        if (!status || status === 'hide') {\n          // Do not clear here, for animation better.\n          group && group.hide();\n          handle && handle.hide();\n          return;\n        }\n\n        group && group.show();\n        handle && handle.show(); // Otherwise status is 'show'\n\n        var elOption = {};\n        this.makeElOption(elOption, value, axisModel, axisPointerModel, api); // Enable change axis pointer type.\n\n        var graphicKey = elOption.graphicKey;\n\n        if (graphicKey !== this._lastGraphicKey) {\n          this.clear(api);\n        }\n\n        this._lastGraphicKey = graphicKey;\n        var moveAnimation = this._moveAnimation = this.determineAnimation(axisModel, axisPointerModel);\n\n        if (!group) {\n          group = this._group = new Group();\n          this.createPointerEl(group, elOption, axisModel, axisPointerModel);\n          this.createLabelEl(group, elOption, axisModel, axisPointerModel);\n          api.getZr().add(group);\n        } else {\n          var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation);\n          this.updatePointerEl(group, elOption, doUpdateProps);\n          this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);\n        }\n\n        updateMandatoryProps(group, axisPointerModel, true);\n\n        this._renderHandle(value);\n      };\n      /**\n       * @implement\n       */\n\n\n      BaseAxisPointer.prototype.remove = function (api) {\n        this.clear(api);\n      };\n      /**\n       * @implement\n       */\n\n\n      BaseAxisPointer.prototype.dispose = function (api) {\n        this.clear(api);\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.determineAnimation = function (axisModel, axisPointerModel) {\n        var animation = axisPointerModel.get('animation');\n        var axis = axisModel.axis;\n        var isCategoryAxis = axis.type === 'category';\n        var useSnap = axisPointerModel.get('snap'); // Value axis without snap always do not snap.\n\n        if (!useSnap && !isCategoryAxis) {\n          return false;\n        }\n\n        if (animation === 'auto' || animation == null) {\n          var animationThreshold = this.animationThreshold;\n\n          if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {\n            return true;\n          } // It is important to auto animation when snap used. Consider if there is\n          // a dataZoom, animation will be disabled when too many points exist, while\n          // it will be enabled for better visual effect when little points exist.\n\n\n          if (useSnap) {\n            var seriesDataCount = getAxisInfo(axisModel).seriesDataCount;\n            var axisExtent = axis.getExtent(); // Approximate band width\n\n            return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;\n          }\n\n          return false;\n        }\n\n        return animation === true;\n      };\n      /**\n       * add {pointer, label, graphicKey} to elOption\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {// Shoule be implemenented by sub-class.\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.createPointerEl = function (group, elOption, axisModel, axisPointerModel) {\n        var pointerOption = elOption.pointer;\n\n        if (pointerOption) {\n          var pointerEl = inner$7(group).pointerEl = new graphic[pointerOption.type](clone$3(elOption.pointer));\n          group.add(pointerEl);\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.createLabelEl = function (group, elOption, axisModel, axisPointerModel) {\n        if (elOption.label) {\n          var labelEl = inner$7(group).labelEl = new ZRText(clone$3(elOption.label));\n          group.add(labelEl);\n          updateLabelShowHide(labelEl, axisPointerModel);\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.updatePointerEl = function (group, elOption, updateProps) {\n        var pointerEl = inner$7(group).pointerEl;\n\n        if (pointerEl && elOption.pointer) {\n          pointerEl.setStyle(elOption.pointer.style);\n          updateProps(pointerEl, {\n            shape: elOption.pointer.shape\n          });\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.updateLabelEl = function (group, elOption, updateProps, axisPointerModel) {\n        var labelEl = inner$7(group).labelEl;\n\n        if (labelEl) {\n          labelEl.setStyle(elOption.label.style);\n          updateProps(labelEl, {\n            // Consider text length change in vertical axis, animation should\n            // be used on shape, otherwise the effect will be weird.\n            // TODOTODO\n            // shape: elOption.label.shape,\n            x: elOption.label.x,\n            y: elOption.label.y\n          });\n          updateLabelShowHide(labelEl, axisPointerModel);\n        }\n      };\n      /**\n       * @private\n       */\n\n\n      BaseAxisPointer.prototype._renderHandle = function (value) {\n        if (this._dragging || !this.updateHandleTransform) {\n          return;\n        }\n\n        var axisPointerModel = this._axisPointerModel;\n\n        var zr = this._api.getZr();\n\n        var handle = this._handle;\n        var handleModel = axisPointerModel.getModel('handle');\n        var status = axisPointerModel.get('status');\n\n        if (!handleModel.get('show') || !status || status === 'hide') {\n          handle && zr.remove(handle);\n          this._handle = null;\n          return;\n        }\n\n        var isInit;\n\n        if (!this._handle) {\n          isInit = true;\n          handle = this._handle = createIcon(handleModel.get('icon'), {\n            cursor: 'move',\n            draggable: true,\n            onmousemove: function (e) {\n              // Fot mobile devicem, prevent screen slider on the button.\n              stop(e.event);\n            },\n            onmousedown: bind$1(this._onHandleDragMove, this, 0, 0),\n            drift: bind$1(this._onHandleDragMove, this),\n            ondragend: bind$1(this._onHandleDragEnd, this)\n          });\n          zr.add(handle);\n        }\n\n        updateMandatoryProps(handle, axisPointerModel, false); // update style\n\n        handle.setStyle(handleModel.getItemStyle(null, ['color', 'borderColor', 'borderWidth', 'opacity', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'])); // update position\n\n        var handleSize = handleModel.get('size');\n\n        if (!isArray(handleSize)) {\n          handleSize = [handleSize, handleSize];\n        }\n\n        handle.scaleX = handleSize[0] / 2;\n        handle.scaleY = handleSize[1] / 2;\n        createOrUpdate(this, '_doDispatchAxisPointer', handleModel.get('throttle') || 0, 'fixRate');\n\n        this._moveHandleToValue(value, isInit);\n      };\n\n      BaseAxisPointer.prototype._moveHandleToValue = function (value, isInit) {\n        updateProps$1(this._axisPointerModel, !isInit && this._moveAnimation, this._handle, getHandleTransProps(this.getHandleTransform(value, this._axisModel, this._axisPointerModel)));\n      };\n\n      BaseAxisPointer.prototype._onHandleDragMove = function (dx, dy) {\n        var handle = this._handle;\n\n        if (!handle) {\n          return;\n        }\n\n        this._dragging = true; // Persistent for throttle.\n\n        var trans = this.updateHandleTransform(getHandleTransProps(handle), [dx, dy], this._axisModel, this._axisPointerModel);\n        this._payloadInfo = trans;\n        handle.stopAnimation();\n        handle.attr(getHandleTransProps(trans));\n        inner$7(handle).lastProp = null;\n\n        this._doDispatchAxisPointer();\n      };\n      /**\n       * Throttled method.\n       */\n\n\n      BaseAxisPointer.prototype._doDispatchAxisPointer = function () {\n        var handle = this._handle;\n\n        if (!handle) {\n          return;\n        }\n\n        var payloadInfo = this._payloadInfo;\n        var axisModel = this._axisModel;\n\n        this._api.dispatchAction({\n          type: 'updateAxisPointer',\n          x: payloadInfo.cursorPoint[0],\n          y: payloadInfo.cursorPoint[1],\n          tooltipOption: payloadInfo.tooltipOption,\n          axesInfo: [{\n            axisDim: axisModel.axis.dim,\n            axisIndex: axisModel.componentIndex\n          }]\n        });\n      };\n\n      BaseAxisPointer.prototype._onHandleDragEnd = function () {\n        this._dragging = false;\n        var handle = this._handle;\n\n        if (!handle) {\n          return;\n        }\n\n        var value = this._axisPointerModel.get('value'); // Consider snap or categroy axis, handle may be not consistent with\n        // axisPointer. So move handle to align the exact value position when\n        // drag ended.\n\n\n        this._moveHandleToValue(value); // For the effect: tooltip will be shown when finger holding on handle\n        // button, and will be hidden after finger left handle button.\n\n\n        this._api.dispatchAction({\n          type: 'hideTip'\n        });\n      };\n      /**\n       * @private\n       */\n\n\n      BaseAxisPointer.prototype.clear = function (api) {\n        this._lastValue = null;\n        this._lastStatus = null;\n        var zr = api.getZr();\n        var group = this._group;\n        var handle = this._handle;\n\n        if (zr && group) {\n          this._lastGraphicKey = null;\n          group && zr.remove(group);\n          handle && zr.remove(handle);\n          this._group = null;\n          this._handle = null;\n          this._payloadInfo = null;\n        }\n\n        clear(this, '_doDispatchAxisPointer');\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.doClear = function () {// Implemented by sub-class if necessary.\n      };\n\n      BaseAxisPointer.prototype.buildLabel = function (xy, wh, xDimIndex) {\n        xDimIndex = xDimIndex || 0;\n        return {\n          x: xy[xDimIndex],\n          y: xy[1 - xDimIndex],\n          width: wh[xDimIndex],\n          height: wh[1 - xDimIndex]\n        };\n      };\n\n      return BaseAxisPointer;\n    }();\n\n    function updateProps$1(animationModel, moveAnimation, el, props) {\n      // Animation optimize.\n      if (!propsEqual(inner$7(el).lastProp, props)) {\n        inner$7(el).lastProp = props;\n        moveAnimation ? updateProps(el, props, animationModel) : (el.stopAnimation(), el.attr(props));\n      }\n    }\n\n    function propsEqual(lastProps, newProps) {\n      if (isObject(lastProps) && isObject(newProps)) {\n        var equals_1 = true;\n        each(newProps, function (item, key) {\n          equals_1 = equals_1 && propsEqual(lastProps[key], item);\n        });\n        return !!equals_1;\n      } else {\n        return lastProps === newProps;\n      }\n    }\n\n    function updateLabelShowHide(labelEl, axisPointerModel) {\n      labelEl[axisPointerModel.get(['label', 'show']) ? 'show' : 'hide']();\n    }\n\n    function getHandleTransProps(trans) {\n      return {\n        x: trans.x || 0,\n        y: trans.y || 0,\n        rotation: trans.rotation || 0\n      };\n    }\n\n    function updateMandatoryProps(group, axisPointerModel, silent) {\n      var z = axisPointerModel.get('z');\n      var zlevel = axisPointerModel.get('zlevel');\n      group && group.traverse(function (el) {\n        if (el.type !== 'group') {\n          z != null && (el.z = z);\n          zlevel != null && (el.zlevel = zlevel);\n          el.silent = silent;\n        }\n      });\n    }\n\n    function buildElStyle(axisPointerModel) {\n      var axisPointerType = axisPointerModel.get('type');\n      var styleModel = axisPointerModel.getModel(axisPointerType + 'Style');\n      var style;\n\n      if (axisPointerType === 'line') {\n        style = styleModel.getLineStyle();\n        style.fill = null;\n      } else if (axisPointerType === 'shadow') {\n        style = styleModel.getAreaStyle();\n        style.stroke = null;\n      }\n\n      return style;\n    }\n    /**\n     * @param {Function} labelPos {align, verticalAlign, position}\n     */\n\n    function buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos) {\n      var value = axisPointerModel.get('value');\n      var text = getValueLabel(value, axisModel.axis, axisModel.ecModel, axisPointerModel.get('seriesDataIndices'), {\n        precision: axisPointerModel.get(['label', 'precision']),\n        formatter: axisPointerModel.get(['label', 'formatter'])\n      });\n      var labelModel = axisPointerModel.getModel('label');\n      var paddings = normalizeCssArray$1(labelModel.get('padding') || 0);\n      var font = labelModel.getFont();\n      var textRect = getBoundingRect(text, font);\n      var position = labelPos.position;\n      var width = textRect.width + paddings[1] + paddings[3];\n      var height = textRect.height + paddings[0] + paddings[2]; // Adjust by align.\n\n      var align = labelPos.align;\n      align === 'right' && (position[0] -= width);\n      align === 'center' && (position[0] -= width / 2);\n      var verticalAlign = labelPos.verticalAlign;\n      verticalAlign === 'bottom' && (position[1] -= height);\n      verticalAlign === 'middle' && (position[1] -= height / 2); // Not overflow ec container\n\n      confineInContainer(position, width, height, api);\n      var bgColor = labelModel.get('backgroundColor');\n\n      if (!bgColor || bgColor === 'auto') {\n        bgColor = axisModel.get(['axisLine', 'lineStyle', 'color']);\n      }\n\n      elOption.label = {\n        // shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},\n        x: position[0],\n        y: position[1],\n        style: createTextStyle(labelModel, {\n          text: text,\n          font: font,\n          fill: labelModel.getTextColor(),\n          padding: paddings,\n          backgroundColor: bgColor\n        }),\n        // Lable should be over axisPointer.\n        z2: 10\n      };\n    } // Do not overflow ec container\n\n    function confineInContainer(position, width, height, api) {\n      var viewWidth = api.getWidth();\n      var viewHeight = api.getHeight();\n      position[0] = Math.min(position[0] + width, viewWidth) - width;\n      position[1] = Math.min(position[1] + height, viewHeight) - height;\n      position[0] = Math.max(position[0], 0);\n      position[1] = Math.max(position[1], 0);\n    }\n\n    function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {\n      value = axis.scale.parse(value);\n      var text = axis.scale.getLabel({\n        value: value\n      }, {\n        // If `precision` is set, width can be fixed (like '12.00500'), which\n        // helps to debounce when when moving label.\n        precision: opt.precision\n      });\n      var formatter = opt.formatter;\n\n      if (formatter) {\n        var params_1 = {\n          value: getAxisRawValue(axis, {\n            value: value\n          }),\n          axisDimension: axis.dim,\n          axisIndex: axis.index,\n          seriesData: []\n        };\n        each(seriesDataIndices, function (idxItem) {\n          var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);\n          var dataIndex = idxItem.dataIndexInside;\n          var dataParams = series && series.getDataParams(dataIndex);\n          dataParams && params_1.seriesData.push(dataParams);\n        });\n\n        if (isString(formatter)) {\n          text = formatter.replace('{value}', text);\n        } else if (isFunction(formatter)) {\n          text = formatter(params_1);\n        }\n      }\n\n      return text;\n    }\n    function getTransformedPosition(axis, value, layoutInfo) {\n      var transform = create$1();\n      rotate(transform, transform, layoutInfo.rotation);\n      translate(transform, transform, layoutInfo.position);\n      return applyTransform$1([axis.dataToCoord(value), (layoutInfo.labelOffset || 0) + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)], transform);\n    }\n    function buildCartesianSingleLabelElOption(value, elOption, layoutInfo, axisModel, axisPointerModel, api) {\n      // @ts-ignore\n      var textLayout = AxisBuilder.innerTextLayout(layoutInfo.rotation, 0, layoutInfo.labelDirection);\n      layoutInfo.labelMargin = axisPointerModel.get(['label', 'margin']);\n      buildLabelElOption(elOption, axisModel, axisPointerModel, api, {\n        position: getTransformedPosition(axisModel.axis, value, layoutInfo),\n        align: textLayout.textAlign,\n        verticalAlign: textLayout.textVerticalAlign\n      });\n    }\n    function makeLineShape(p1, p2, xDimIndex) {\n      xDimIndex = xDimIndex || 0;\n      return {\n        x1: p1[xDimIndex],\n        y1: p1[1 - xDimIndex],\n        x2: p2[xDimIndex],\n        y2: p2[1 - xDimIndex]\n      };\n    }\n    function makeRectShape(xy, wh, xDimIndex) {\n      xDimIndex = xDimIndex || 0;\n      return {\n        x: xy[xDimIndex],\n        y: xy[1 - xDimIndex],\n        width: wh[xDimIndex],\n        height: wh[1 - xDimIndex]\n      };\n    }\n\n    var CartesianAxisPointer =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisPointer, _super);\n\n      function CartesianAxisPointer() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n      /**\n       * @override\n       */\n\n\n      CartesianAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {\n        var axis = axisModel.axis;\n        var grid = axis.grid;\n        var axisPointerType = axisPointerModel.get('type');\n        var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();\n        var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true));\n\n        if (axisPointerType && axisPointerType !== 'none') {\n          var elStyle = buildElStyle(axisPointerModel);\n          var pointerOption = pointerShapeBuilder[axisPointerType](axis, pixelValue, otherExtent);\n          pointerOption.style = elStyle;\n          elOption.graphicKey = pointerOption.type;\n          elOption.pointer = pointerOption;\n        }\n\n        var layoutInfo = layout$1(grid.model, axisModel);\n        buildCartesianSingleLabelElOption( // @ts-ignore\n        value, elOption, layoutInfo, axisModel, axisPointerModel, api);\n      };\n      /**\n       * @override\n       */\n\n\n      CartesianAxisPointer.prototype.getHandleTransform = function (value, axisModel, axisPointerModel) {\n        var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, {\n          labelInside: false\n        }); // @ts-ignore\n\n        layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']);\n        var pos = getTransformedPosition(axisModel.axis, value, layoutInfo);\n        return {\n          x: pos[0],\n          y: pos[1],\n          rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)\n        };\n      };\n      /**\n       * @override\n       */\n\n\n      CartesianAxisPointer.prototype.updateHandleTransform = function (transform, delta, axisModel, axisPointerModel) {\n        var axis = axisModel.axis;\n        var grid = axis.grid;\n        var axisExtent = axis.getGlobalExtent(true);\n        var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();\n        var dimIndex = axis.dim === 'x' ? 0 : 1;\n        var currPosition = [transform.x, transform.y];\n        currPosition[dimIndex] += delta[dimIndex];\n        currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);\n        currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);\n        var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;\n        var cursorPoint = [cursorOtherValue, cursorOtherValue];\n        cursorPoint[dimIndex] = currPosition[dimIndex]; // Make tooltip do not overlap axisPointer and in the middle of the grid.\n\n        var tooltipOptions = [{\n          verticalAlign: 'middle'\n        }, {\n          align: 'center'\n        }];\n        return {\n          x: currPosition[0],\n          y: currPosition[1],\n          rotation: transform.rotation,\n          cursorPoint: cursorPoint,\n          tooltipOption: tooltipOptions[dimIndex]\n        };\n      };\n\n      return CartesianAxisPointer;\n    }(BaseAxisPointer);\n\n    function getCartesian(grid, axis) {\n      var opt = {};\n      opt[axis.dim + 'AxisIndex'] = axis.index;\n      return grid.getCartesian(opt);\n    }\n\n    var pointerShapeBuilder = {\n      line: function (axis, pixelValue, otherExtent) {\n        var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getAxisDimIndex(axis));\n        return {\n          type: 'Line',\n          subPixelOptimize: true,\n          shape: targetShape\n        };\n      },\n      shadow: function (axis, pixelValue, otherExtent) {\n        var bandWidth = Math.max(1, axis.getBandWidth());\n        var span = otherExtent[1] - otherExtent[0];\n        return {\n          type: 'Rect',\n          shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getAxisDimIndex(axis))\n        };\n      }\n    };\n\n    function getAxisDimIndex(axis) {\n      return axis.dim === 'x' ? 0 : 1;\n    }\n\n    var AxisPointerModel =\n    /** @class */\n    function (_super) {\n      __extends(AxisPointerModel, _super);\n\n      function AxisPointerModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AxisPointerModel.type;\n        return _this;\n      }\n\n      AxisPointerModel.type = 'axisPointer';\n      AxisPointerModel.defaultOption = {\n        // 'auto' means that show when triggered by tooltip or handle.\n        show: 'auto',\n        // zlevel: 0,\n        z: 50,\n        type: 'line',\n        // axispointer triggered by tootip determine snap automatically,\n        // see `modelHelper`.\n        snap: false,\n        triggerTooltip: true,\n        value: null,\n        status: null,\n        link: [],\n        // Do not set 'auto' here, otherwise global animation: false\n        // will not effect at this axispointer.\n        animation: null,\n        animationDurationUpdate: 200,\n        lineStyle: {\n          color: '#B9BEC9',\n          width: 1,\n          type: 'dashed'\n        },\n        shadowStyle: {\n          color: 'rgba(210,219,238,0.2)'\n        },\n        label: {\n          show: true,\n          formatter: null,\n          precision: 'auto',\n          margin: 3,\n          color: '#fff',\n          padding: [5, 7, 5, 7],\n          backgroundColor: 'auto',\n          borderColor: null,\n          borderWidth: 0,\n          borderRadius: 3\n        },\n        handle: {\n          show: false,\n          // eslint-disable-next-line\n          icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z',\n          size: 45,\n          // handle margin is from symbol center to axis, which is stable when circular move.\n          margin: 50,\n          // color: '#1b8bbd'\n          // color: '#2f4554'\n          color: '#333',\n          shadowBlur: 3,\n          shadowColor: '#aaa',\n          shadowOffsetX: 0,\n          shadowOffsetY: 2,\n          // For mobile performance\n          throttle: 40\n        }\n      };\n      return AxisPointerModel;\n    }(ComponentModel);\n\n    var inner$8 = makeInner();\n    var each$3 = each;\n    /**\n     * @param {string} key\n     * @param {module:echarts/ExtensionAPI} api\n     * @param {Function} handler\n     *      param: {string} currTrigger\n     *      param: {Array.<number>} point\n     */\n\n    function register(key, api, handler) {\n      if (env.node) {\n        return;\n      }\n\n      var zr = api.getZr();\n      inner$8(zr).records || (inner$8(zr).records = {});\n      initGlobalListeners(zr, api);\n      var record = inner$8(zr).records[key] || (inner$8(zr).records[key] = {});\n      record.handler = handler;\n    }\n\n    function initGlobalListeners(zr, api) {\n      if (inner$8(zr).initialized) {\n        return;\n      }\n\n      inner$8(zr).initialized = true;\n      useHandler('click', curry(doEnter, 'click'));\n      useHandler('mousemove', curry(doEnter, 'mousemove')); // useHandler('mouseout', onLeave);\n\n      useHandler('globalout', onLeave);\n\n      function useHandler(eventType, cb) {\n        zr.on(eventType, function (e) {\n          var dis = makeDispatchAction(api);\n          each$3(inner$8(zr).records, function (record) {\n            record && cb(record, e, dis.dispatchAction);\n          });\n          dispatchTooltipFinally(dis.pendings, api);\n        });\n      }\n    }\n\n    function dispatchTooltipFinally(pendings, api) {\n      var showLen = pendings.showTip.length;\n      var hideLen = pendings.hideTip.length;\n      var actuallyPayload;\n\n      if (showLen) {\n        actuallyPayload = pendings.showTip[showLen - 1];\n      } else if (hideLen) {\n        actuallyPayload = pendings.hideTip[hideLen - 1];\n      }\n\n      if (actuallyPayload) {\n        actuallyPayload.dispatchAction = null;\n        api.dispatchAction(actuallyPayload);\n      }\n    }\n\n    function onLeave(record, e, dispatchAction) {\n      record.handler('leave', null, dispatchAction);\n    }\n\n    function doEnter(currTrigger, record, e, dispatchAction) {\n      record.handler(currTrigger, e, dispatchAction);\n    }\n\n    function makeDispatchAction(api) {\n      var pendings = {\n        showTip: [],\n        hideTip: []\n      }; // FIXME\n      // better approach?\n      // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,\n      // which may be conflict, (axisPointer call showTip but tooltip call hideTip);\n      // So we have to add \"final stage\" to merge those dispatched actions.\n\n      var dispatchAction = function (payload) {\n        var pendingList = pendings[payload.type];\n\n        if (pendingList) {\n          pendingList.push(payload);\n        } else {\n          payload.dispatchAction = dispatchAction;\n          api.dispatchAction(payload);\n        }\n      };\n\n      return {\n        dispatchAction: dispatchAction,\n        pendings: pendings\n      };\n    }\n\n    function unregister(key, api) {\n      if (env.node) {\n        return;\n      }\n\n      var zr = api.getZr();\n      var record = (inner$8(zr).records || {})[key];\n\n      if (record) {\n        inner$8(zr).records[key] = null;\n      }\n    }\n\n    var AxisPointerView =\n    /** @class */\n    function (_super) {\n      __extends(AxisPointerView, _super);\n\n      function AxisPointerView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AxisPointerView.type;\n        return _this;\n      }\n\n      AxisPointerView.prototype.render = function (globalAxisPointerModel, ecModel, api) {\n        var globalTooltipModel = ecModel.getComponent('tooltip');\n        var triggerOn = globalAxisPointerModel.get('triggerOn') || globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click'; // Register global listener in AxisPointerView to enable\n        // AxisPointerView to be independent to Tooltip.\n\n        register('axisPointer', api, function (currTrigger, e, dispatchAction) {\n          // If 'none', it is not controlled by mouse totally.\n          if (triggerOn !== 'none' && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)) {\n            dispatchAction({\n              type: 'updateAxisPointer',\n              currTrigger: currTrigger,\n              x: e && e.offsetX,\n              y: e && e.offsetY\n            });\n          }\n        });\n      };\n\n      AxisPointerView.prototype.remove = function (ecModel, api) {\n        unregister('axisPointer', api);\n      };\n\n      AxisPointerView.prototype.dispose = function (ecModel, api) {\n        unregister('axisPointer', api);\n      };\n\n      AxisPointerView.type = 'axisPointer';\n      return AxisPointerView;\n    }(ComponentView);\n\n    /**\n     * @param finder contains {seriesIndex, dataIndex, dataIndexInside}\n     * @param ecModel\n     * @return  {point: [x, y], el: ...} point Will not be null.\n     */\n\n    function findPointFromSeries(finder, ecModel) {\n      var point = [];\n      var seriesIndex = finder.seriesIndex;\n      var seriesModel;\n\n      if (seriesIndex == null || !(seriesModel = ecModel.getSeriesByIndex(seriesIndex))) {\n        return {\n          point: []\n        };\n      }\n\n      var data = seriesModel.getData();\n      var dataIndex = queryDataIndex(data, finder);\n\n      if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) {\n        return {\n          point: []\n        };\n      }\n\n      var el = data.getItemGraphicEl(dataIndex);\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (seriesModel.getTooltipPosition) {\n        point = seriesModel.getTooltipPosition(dataIndex) || [];\n      } else if (coordSys && coordSys.dataToPoint) {\n        if (finder.isStacked) {\n          var baseAxis = coordSys.getBaseAxis();\n          var valueAxis = coordSys.getOtherAxis(baseAxis);\n          var valueAxisDim = valueAxis.dim;\n          var baseAxisDim = baseAxis.dim;\n          var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;\n          var baseDim = data.mapDimension(baseAxisDim);\n          var stackedData = [];\n          stackedData[baseDataOffset] = data.get(baseDim, dataIndex);\n          stackedData[1 - baseDataOffset] = data.get(data.getCalculationInfo('stackResultDimension'), dataIndex);\n          point = coordSys.dataToPoint(stackedData) || [];\n        } else {\n          point = coordSys.dataToPoint(data.getValues(map(coordSys.dimensions, function (dim) {\n            return data.mapDimension(dim);\n          }), dataIndex)) || [];\n        }\n      } else if (el) {\n        // Use graphic bounding rect\n        var rect = el.getBoundingRect().clone();\n        rect.applyTransform(el.transform);\n        point = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      }\n\n      return {\n        point: point,\n        el: el\n      };\n    }\n\n    var inner$9 = makeInner();\n    /**\n     * Basic logic: check all axis, if they do not demand show/highlight,\n     * then hide/downplay them.\n     *\n     * @return content of event obj for echarts.connect.\n     */\n\n    function axisTrigger(payload, ecModel, api) {\n      var currTrigger = payload.currTrigger;\n      var point = [payload.x, payload.y];\n      var finder = payload;\n      var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);\n      var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; // Pending\n      // See #6121. But we are not able to reproduce it yet.\n\n      if (!coordSysAxesInfo) {\n        return;\n      }\n\n      if (illegalPoint(point)) {\n        // Used in the default behavior of `connection`: use the sample seriesIndex\n        // and dataIndex. And also used in the tooltipView trigger.\n        point = findPointFromSeries({\n          seriesIndex: finder.seriesIndex,\n          // Do not use dataIndexInside from other ec instance.\n          // FIXME: auto detect it?\n          dataIndex: finder.dataIndex\n        }, ecModel).point;\n      }\n\n      var isIllegalPoint = illegalPoint(point); // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).\n      // Notice: In this case, it is difficult to get the `point` (which is necessary to show\n      // tooltip, so if point is not given, we just use the point found by sample seriesIndex\n      // and dataIndex.\n\n      var inputAxesInfo = finder.axesInfo;\n      var axesInfo = coordSysAxesInfo.axesInfo;\n      var shouldHide = currTrigger === 'leave' || illegalPoint(point);\n      var outputPayload = {};\n      var showValueMap = {};\n      var dataByCoordSys = {\n        list: [],\n        map: {}\n      };\n      var updaters = {\n        showPointer: curry(showPointer, showValueMap),\n        showTooltip: curry(showTooltip, dataByCoordSys)\n      }; // Process for triggered axes.\n\n      each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {\n        // If a point given, it must be contained by the coordinate system.\n        var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);\n        each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {\n          var axis = axisInfo.axis;\n          var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); // If no inputAxesInfo, no axis is restricted.\n\n          if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {\n            var val = inputAxisInfo && inputAxisInfo.value;\n\n            if (val == null && !isIllegalPoint) {\n              val = axis.pointToData(point);\n            }\n\n            val != null && processOnAxis(axisInfo, val, updaters, false, outputPayload);\n          }\n        });\n      }); // Process for linked axes.\n\n      var linkTriggers = {};\n      each(axesInfo, function (tarAxisInfo, tarKey) {\n        var linkGroup = tarAxisInfo.linkGroup; // If axis has been triggered in the previous stage, it should not be triggered by link.\n\n        if (linkGroup && !showValueMap[tarKey]) {\n          each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {\n            var srcValItem = showValueMap[srcKey]; // If srcValItem exist, source axis is triggered, so link to target axis.\n\n            if (srcAxisInfo !== tarAxisInfo && srcValItem) {\n              var val = srcValItem.value;\n              linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo))));\n              linkTriggers[tarAxisInfo.key] = val;\n            }\n          });\n        }\n      });\n      each(linkTriggers, function (val, tarKey) {\n        processOnAxis(axesInfo[tarKey], val, updaters, true, outputPayload);\n      });\n      updateModelActually(showValueMap, axesInfo, outputPayload);\n      dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);\n      dispatchHighDownActually(axesInfo, dispatchAction, api);\n      return outputPayload;\n    }\n\n    function processOnAxis(axisInfo, newValue, updaters, noSnap, outputFinder) {\n      var axis = axisInfo.axis;\n\n      if (axis.scale.isBlank() || !axis.containData(newValue)) {\n        return;\n      }\n\n      if (!axisInfo.involveSeries) {\n        updaters.showPointer(axisInfo, newValue);\n        return;\n      } // Heavy calculation. So put it after axis.containData checking.\n\n\n      var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);\n      var payloadBatch = payloadInfo.payloadBatch;\n      var snapToValue = payloadInfo.snapToValue; // Fill content of event obj for echarts.connect.\n      // By default use the first involved series data as a sample to connect.\n\n      if (payloadBatch[0] && outputFinder.seriesIndex == null) {\n        extend(outputFinder, payloadBatch[0]);\n      } // If no linkSource input, this process is for collecting link\n      // target, where snap should not be accepted.\n\n\n      if (!noSnap && axisInfo.snap) {\n        if (axis.containData(snapToValue) && snapToValue != null) {\n          newValue = snapToValue;\n        }\n      }\n\n      updaters.showPointer(axisInfo, newValue, payloadBatch); // Tooltip should always be snapToValue, otherwise there will be\n      // incorrect \"axis value ~ series value\" mapping displayed in tooltip.\n\n      updaters.showTooltip(axisInfo, payloadInfo, snapToValue);\n    }\n\n    function buildPayloadsBySeries(value, axisInfo) {\n      var axis = axisInfo.axis;\n      var dim = axis.dim;\n      var snapToValue = value;\n      var payloadBatch = [];\n      var minDist = Number.MAX_VALUE;\n      var minDiff = -1;\n      each(axisInfo.seriesModels, function (series, idx) {\n        var dataDim = series.getData().mapDimensionsAll(dim);\n        var seriesNestestValue;\n        var dataIndices;\n\n        if (series.getAxisTooltipData) {\n          var result = series.getAxisTooltipData(dataDim, value, axis);\n          dataIndices = result.dataIndices;\n          seriesNestestValue = result.nestestValue;\n        } else {\n          dataIndices = series.getData().indicesOfNearest(dataDim[0], value, // Add a threshold to avoid find the wrong dataIndex\n          // when data length is not same.\n          // false,\n          axis.type === 'category' ? 0.5 : null);\n\n          if (!dataIndices.length) {\n            return;\n          }\n\n          seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);\n        }\n\n        if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {\n          return;\n        }\n\n        var diff = value - seriesNestestValue;\n        var dist = Math.abs(diff); // Consider category case\n\n        if (dist <= minDist) {\n          if (dist < minDist || diff >= 0 && minDiff < 0) {\n            minDist = dist;\n            minDiff = diff;\n            snapToValue = seriesNestestValue;\n            payloadBatch.length = 0;\n          }\n\n          each(dataIndices, function (dataIndex) {\n            payloadBatch.push({\n              seriesIndex: series.seriesIndex,\n              dataIndexInside: dataIndex,\n              dataIndex: series.getData().getRawIndex(dataIndex)\n            });\n          });\n        }\n      });\n      return {\n        payloadBatch: payloadBatch,\n        snapToValue: snapToValue\n      };\n    }\n\n    function showPointer(showValueMap, axisInfo, value, payloadBatch) {\n      showValueMap[axisInfo.key] = {\n        value: value,\n        payloadBatch: payloadBatch\n      };\n    }\n\n    function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {\n      var payloadBatch = payloadInfo.payloadBatch;\n      var axis = axisInfo.axis;\n      var axisModel = axis.model;\n      var axisPointerModel = axisInfo.axisPointerModel; // If no data, do not create anything in dataByCoordSys,\n      // whose length will be used to judge whether dispatch action.\n\n      if (!axisInfo.triggerTooltip || !payloadBatch.length) {\n        return;\n      }\n\n      var coordSysModel = axisInfo.coordSys.model;\n      var coordSysKey = makeKey(coordSysModel);\n      var coordSysItem = dataByCoordSys.map[coordSysKey];\n\n      if (!coordSysItem) {\n        coordSysItem = dataByCoordSys.map[coordSysKey] = {\n          coordSysId: coordSysModel.id,\n          coordSysIndex: coordSysModel.componentIndex,\n          coordSysType: coordSysModel.type,\n          coordSysMainType: coordSysModel.mainType,\n          dataByAxis: []\n        };\n        dataByCoordSys.list.push(coordSysItem);\n      }\n\n      coordSysItem.dataByAxis.push({\n        axisDim: axis.dim,\n        axisIndex: axisModel.componentIndex,\n        axisType: axisModel.type,\n        axisId: axisModel.id,\n        value: value,\n        // Caustion: viewHelper.getValueLabel is actually on \"view stage\", which\n        // depends that all models have been updated. So it should not be performed\n        // here. Considering axisPointerModel used here is volatile, which is hard\n        // to be retrieve in TooltipView, we prepare parameters here.\n        valueLabelOpt: {\n          precision: axisPointerModel.get(['label', 'precision']),\n          formatter: axisPointerModel.get(['label', 'formatter'])\n        },\n        seriesDataIndices: payloadBatch.slice()\n      });\n    }\n\n    function updateModelActually(showValueMap, axesInfo, outputPayload) {\n      var outputAxesInfo = outputPayload.axesInfo = []; // Basic logic: If no 'show' required, 'hide' this axisPointer.\n\n      each(axesInfo, function (axisInfo, key) {\n        var option = axisInfo.axisPointerModel.option;\n        var valItem = showValueMap[key];\n\n        if (valItem) {\n          !axisInfo.useHandle && (option.status = 'show');\n          option.value = valItem.value; // For label formatter param and highlight.\n\n          option.seriesDataIndices = (valItem.payloadBatch || []).slice();\n        } // When always show (e.g., handle used), remain\n        // original value and status.\n        else {\n            // If hide, value still need to be set, consider\n            // click legend to toggle axis blank.\n            !axisInfo.useHandle && (option.status = 'hide');\n          } // If status is 'hide', should be no info in payload.\n\n\n        option.status === 'show' && outputAxesInfo.push({\n          axisDim: axisInfo.axis.dim,\n          axisIndex: axisInfo.axis.model.componentIndex,\n          value: option.value\n        });\n      });\n    }\n\n    function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {\n      // Basic logic: If no showTip required, hideTip will be dispatched.\n      if (illegalPoint(point) || !dataByCoordSys.list.length) {\n        dispatchAction({\n          type: 'hideTip'\n        });\n        return;\n      } // In most case only one axis (or event one series is used). It is\n      // convinient to fetch payload.seriesIndex and payload.dataIndex\n      // dirtectly. So put the first seriesIndex and dataIndex of the first\n      // axis on the payload.\n\n\n      var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};\n      dispatchAction({\n        type: 'showTip',\n        escapeConnect: true,\n        x: point[0],\n        y: point[1],\n        tooltipOption: payload.tooltipOption,\n        position: payload.position,\n        dataIndexInside: sampleItem.dataIndexInside,\n        dataIndex: sampleItem.dataIndex,\n        seriesIndex: sampleItem.seriesIndex,\n        dataByCoordSys: dataByCoordSys.list\n      });\n    }\n\n    function dispatchHighDownActually(axesInfo, dispatchAction, api) {\n      // FIXME\n      // highlight status modification shoule be a stage of main process?\n      // (Consider confilct (e.g., legend and axisPointer) and setOption)\n      var zr = api.getZr();\n      var highDownKey = 'axisPointerLastHighlights';\n      var lastHighlights = inner$9(zr)[highDownKey] || {};\n      var newHighlights = inner$9(zr)[highDownKey] = {}; // Update highlight/downplay status according to axisPointer model.\n      // Build hash map and remove duplicate incidentally.\n\n      each(axesInfo, function (axisInfo, key) {\n        var option = axisInfo.axisPointerModel.option;\n        option.status === 'show' && each(option.seriesDataIndices, function (batchItem) {\n          var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;\n          newHighlights[key] = batchItem;\n        });\n      }); // Diff.\n\n      var toHighlight = [];\n      var toDownplay = [];\n      each(lastHighlights, function (batchItem, key) {\n        !newHighlights[key] && toDownplay.push(batchItem);\n      });\n      each(newHighlights, function (batchItem, key) {\n        !lastHighlights[key] && toHighlight.push(batchItem);\n      });\n      toDownplay.length && api.dispatchAction({\n        type: 'downplay',\n        escapeConnect: true,\n        // Not blur others when highlight in axisPointer.\n        notBlur: true,\n        batch: toDownplay\n      });\n      toHighlight.length && api.dispatchAction({\n        type: 'highlight',\n        escapeConnect: true,\n        // Not blur others when highlight in axisPointer.\n        notBlur: true,\n        batch: toHighlight\n      });\n    }\n\n    function findInputAxisInfo(inputAxesInfo, axisInfo) {\n      for (var i = 0; i < (inputAxesInfo || []).length; i++) {\n        var inputAxisInfo = inputAxesInfo[i];\n\n        if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) {\n          return inputAxisInfo;\n        }\n      }\n    }\n\n    function makeMapperParam(axisInfo) {\n      var axisModel = axisInfo.axis.model;\n      var item = {};\n      var dim = item.axisDim = axisInfo.axis.dim;\n      item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;\n      item.axisName = item[dim + 'AxisName'] = axisModel.name;\n      item.axisId = item[dim + 'AxisId'] = axisModel.id;\n      return item;\n    }\n\n    function illegalPoint(point) {\n      return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);\n    }\n\n    function install$7(registers) {\n      // CartesianAxisPointer is not supposed to be required here. But consider\n      // echarts.simple.js and online build tooltip, which only require gridSimple,\n      // CartesianAxisPointer should be able to required somewhere.\n      AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer);\n      registers.registerComponentModel(AxisPointerModel);\n      registers.registerComponentView(AxisPointerView);\n      registers.registerPreprocessor(function (option) {\n        // Always has a global axisPointerModel for default setting.\n        if (option) {\n          (!option.axisPointer || option.axisPointer.length === 0) && (option.axisPointer = {});\n          var link = option.axisPointer.link; // Normalize to array to avoid object mergin. But if link\n          // is not set, remain null/undefined, otherwise it will\n          // override existent link setting.\n\n          if (link && !isArray(link)) {\n            option.axisPointer.link = [link];\n          }\n        }\n      }); // This process should proformed after coordinate systems created\n      // and series data processed. So put it on statistic processing stage.\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) {\n        // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.\n        // allAxesInfo should be updated when setOption performed.\n        ecModel.getComponent('axisPointer').coordSysAxesInfo = collect(ecModel, api);\n      }); // Broadcast to all views.\n\n      registers.registerAction({\n        type: 'updateAxisPointer',\n        event: 'updateAxisPointer',\n        update: ':updateAxisPointer'\n      }, axisTrigger);\n    }\n\n    function install$8(registers) {\n      use(install$5);\n      use(install$7);\n    }\n\n    function setKeyInfoToNewElOption(resultItem, newElOption) {\n      var existElOption = resultItem.existing; // Set id and type after id assigned.\n\n      newElOption.id = resultItem.keyInfo.id;\n      !newElOption.type && existElOption && (newElOption.type = existElOption.type); // Set parent id if not specified\n\n      if (newElOption.parentId == null) {\n        var newElParentOption = newElOption.parentOption;\n\n        if (newElParentOption) {\n          newElOption.parentId = newElParentOption.id;\n        } else if (existElOption) {\n          newElOption.parentId = existElOption.parentId;\n        }\n      } // Clear\n\n\n      newElOption.parentOption = null;\n    }\n\n    function isSetLoc(obj, props) {\n      var isSet;\n      each(props, function (prop) {\n        obj[prop] != null && obj[prop] !== 'auto' && (isSet = true);\n      });\n      return isSet;\n    }\n\n    function mergeNewElOptionToExist(existList, index, newElOption) {\n      // Update existing options, for `getOption` feature.\n      var newElOptCopy = extend({}, newElOption);\n      var existElOption = existList[index];\n      var $action = newElOption.$action || 'merge';\n\n      if ($action === 'merge') {\n        if (existElOption) {\n          if (\"development\" !== 'production') {\n            var newType = newElOption.type;\n            assert(!newType || existElOption.type === newType, 'Please set $action: \"replace\" to change `type`');\n          } // We can ensure that newElOptCopy and existElOption are not\n          // the same object, so `merge` will not change newElOptCopy.\n\n\n          merge(existElOption, newElOptCopy, true); // Rigid body, use ignoreSize.\n\n          mergeLayoutParam(existElOption, newElOptCopy, {\n            ignoreSize: true\n          }); // Will be used in render.\n\n          copyLayoutParams(newElOption, existElOption); // Copy transition info to new option so it can be used in the transition.\n          // DO IT AFTER merge\n\n          copyTransitionInfo(newElOption, existElOption);\n          copyTransitionInfo(newElOption, existElOption, 'shape');\n          copyTransitionInfo(newElOption, existElOption, 'style');\n          copyTransitionInfo(newElOption, existElOption, 'extra'); // Copy clipPath\n\n          newElOption.clipPath = existElOption.clipPath;\n        } else {\n          existList[index] = newElOptCopy;\n        }\n      } else if ($action === 'replace') {\n        existList[index] = newElOptCopy;\n      } else if ($action === 'remove') {\n        // null will be cleaned later.\n        existElOption && (existList[index] = null);\n      }\n    }\n\n    var TRANSITION_PROPS_TO_COPY = ['transition', 'enterFrom', 'leaveTo'];\n    var ROOT_TRANSITION_PROPS_TO_COPY = TRANSITION_PROPS_TO_COPY.concat(['enterAnimation', 'updateAnimation', 'leaveAnimation']);\n\n    function copyTransitionInfo(target, source, targetProp) {\n      if (targetProp) {\n        if (!target[targetProp] && source[targetProp]) {\n          // TODO avoid creating this empty object when there is no transition configuration.\n          target[targetProp] = {};\n        }\n\n        target = target[targetProp];\n        source = source[targetProp];\n      }\n\n      if (!target || !source) {\n        return;\n      }\n\n      var props = targetProp ? TRANSITION_PROPS_TO_COPY : ROOT_TRANSITION_PROPS_TO_COPY;\n\n      for (var i = 0; i < props.length; i++) {\n        var prop = props[i];\n\n        if (target[prop] == null && source[prop] != null) {\n          target[prop] = source[prop];\n        }\n      }\n    }\n\n    function setLayoutInfoToExist(existItem, newElOption) {\n      if (!existItem) {\n        return;\n      }\n\n      existItem.hv = newElOption.hv = [// Rigid body, don't care about `width`.\n      isSetLoc(newElOption, ['left', 'right']), // Rigid body, don't care about `height`.\n      isSetLoc(newElOption, ['top', 'bottom'])]; // Give default group size. Otherwise layout error may occur.\n\n      if (existItem.type === 'group') {\n        var existingGroupOpt = existItem;\n        var newGroupOpt = newElOption;\n        existingGroupOpt.width == null && (existingGroupOpt.width = newGroupOpt.width = 0);\n        existingGroupOpt.height == null && (existingGroupOpt.height = newGroupOpt.height = 0);\n      }\n    }\n\n    var GraphicComponentModel =\n    /** @class */\n    function (_super) {\n      __extends(GraphicComponentModel, _super);\n\n      function GraphicComponentModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GraphicComponentModel.type;\n        _this.preventAutoZ = true;\n        return _this;\n      }\n\n      GraphicComponentModel.prototype.mergeOption = function (option, ecModel) {\n        // Prevent default merge to elements\n        var elements = this.option.elements;\n        this.option.elements = null;\n\n        _super.prototype.mergeOption.call(this, option, ecModel);\n\n        this.option.elements = elements;\n      };\n\n      GraphicComponentModel.prototype.optionUpdated = function (newOption, isInit) {\n        var thisOption = this.option;\n        var newList = (isInit ? thisOption : newOption).elements;\n        var existList = thisOption.elements = isInit ? [] : thisOption.elements;\n        var flattenedList = [];\n\n        this._flatten(newList, flattenedList, null);\n\n        var mappingResult = mappingToExists(existList, flattenedList, 'normalMerge'); // Clear elOptionsToUpdate\n\n        var elOptionsToUpdate = this._elOptionsToUpdate = [];\n        each(mappingResult, function (resultItem, index) {\n          var newElOption = resultItem.newOption;\n\n          if (\"development\" !== 'production') {\n            assert(isObject(newElOption) || resultItem.existing, 'Empty graphic option definition');\n          }\n\n          if (!newElOption) {\n            return;\n          }\n\n          elOptionsToUpdate.push(newElOption);\n          setKeyInfoToNewElOption(resultItem, newElOption);\n          mergeNewElOptionToExist(existList, index, newElOption);\n          setLayoutInfoToExist(existList[index], newElOption);\n        }, this); // Clean\n\n        thisOption.elements = filter(existList, function (item) {\n          // $action should be volatile, otherwise option gotten from\n          // `getOption` will contain unexpected $action.\n          item && delete item.$action;\n          return item != null;\n        });\n      };\n      /**\n       * Convert\n       * [{\n       *  type: 'group',\n       *  id: 'xx',\n       *  children: [{type: 'circle'}, {type: 'polygon'}]\n       * }]\n       * to\n       * [\n       *  {type: 'group', id: 'xx'},\n       *  {type: 'circle', parentId: 'xx'},\n       *  {type: 'polygon', parentId: 'xx'}\n       * ]\n       */\n\n\n      GraphicComponentModel.prototype._flatten = function (optionList, result, parentOption) {\n        each(optionList, function (option) {\n          if (!option) {\n            return;\n          }\n\n          if (parentOption) {\n            option.parentOption = parentOption;\n          }\n\n          result.push(option);\n          var children = option.children; // here we don't judge if option.type is `group`\n          // when new option doesn't provide `type`, it will cause that the children can't be updated.\n\n          if (children && children.length) {\n            this._flatten(children, result, option);\n          } // Deleting for JSON output, and for not affecting group creation.\n\n\n          delete option.children;\n        }, this);\n      }; // FIXME\n      // Pass to view using payload? setOption has a payload?\n\n\n      GraphicComponentModel.prototype.useElOptionsToUpdate = function () {\n        var els = this._elOptionsToUpdate; // Clear to avoid render duplicately when zooming.\n\n        this._elOptionsToUpdate = null;\n        return els;\n      };\n\n      GraphicComponentModel.type = 'graphic';\n      GraphicComponentModel.defaultOption = {\n        elements: [] // parentId: null\n\n      };\n      return GraphicComponentModel;\n    }(ComponentModel);\n\n    /**\n     * Whether need to call `convertEC4CompatibleStyle`.\n     */\n\n    function isEC4CompatibleStyle(style, elType, hasOwnTextContentOption, hasOwnTextConfig) {\n      // Since echarts5, `RectText` is separated from its host element and style.text\n      // does not exist any more. The compat work brings some extra burden on performance.\n      // So we provide:\n      // `legacy: true` force make compat.\n      // `legacy: false`, force do not compat.\n      // `legacy` not set: auto detect whether legacy.\n      //     But in this case we do not compat (difficult to detect and rare case):\n      //     Becuse custom series and graphic component support \"merge\", users may firstly\n      //     only set `textStrokeWidth` style or secondly only set `text`.\n      return style && (style.legacy || style.legacy !== false && !hasOwnTextContentOption && !hasOwnTextConfig && elType !== 'tspan' // Difficult to detect whether legacy for a \"text\" el.\n      && (elType === 'text' || hasOwn(style, 'text')));\n    }\n    /**\n     * `EC4CompatibleStyle` is style that might be in echarts4 format or echarts5 format.\n     * @param hostStyle The properties might be modified.\n     * @return If be text el, `textContentStyle` and `textConfig` will not be returned.\n     *         Otherwise a `textContentStyle` and `textConfig` will be created, whose props area\n     *         retried from the `hostStyle`.\n     */\n\n    function convertFromEC4CompatibleStyle(hostStyle, elType, isNormal) {\n      var srcStyle = hostStyle;\n      var textConfig;\n      var textContent;\n      var textContentStyle;\n\n      if (elType === 'text') {\n        textContentStyle = srcStyle;\n      } else {\n        textContentStyle = {};\n        hasOwn(srcStyle, 'text') && (textContentStyle.text = srcStyle.text);\n        hasOwn(srcStyle, 'rich') && (textContentStyle.rich = srcStyle.rich);\n        hasOwn(srcStyle, 'textFill') && (textContentStyle.fill = srcStyle.textFill);\n        hasOwn(srcStyle, 'textStroke') && (textContentStyle.stroke = srcStyle.textStroke);\n        hasOwn(srcStyle, 'fontFamily') && (textContentStyle.fontFamily = srcStyle.fontFamily);\n        hasOwn(srcStyle, 'fontSize') && (textContentStyle.fontSize = srcStyle.fontSize);\n        hasOwn(srcStyle, 'fontStyle') && (textContentStyle.fontStyle = srcStyle.fontStyle);\n        hasOwn(srcStyle, 'fontWeight') && (textContentStyle.fontWeight = srcStyle.fontWeight);\n        textContent = {\n          type: 'text',\n          style: textContentStyle,\n          // ec4 does not support rectText trigger.\n          // And when text position is different in normal and emphasis\n          // => hover text trigger emphasis;\n          // => text position changed, leave mouse pointer immediately;\n          // That might cause incorrect state.\n          silent: true\n        };\n        textConfig = {};\n        var hasOwnPos = hasOwn(srcStyle, 'textPosition');\n\n        if (isNormal) {\n          textConfig.position = hasOwnPos ? srcStyle.textPosition : 'inside';\n        } else {\n          hasOwnPos && (textConfig.position = srcStyle.textPosition);\n        }\n\n        hasOwn(srcStyle, 'textPosition') && (textConfig.position = srcStyle.textPosition);\n        hasOwn(srcStyle, 'textOffset') && (textConfig.offset = srcStyle.textOffset);\n        hasOwn(srcStyle, 'textRotation') && (textConfig.rotation = srcStyle.textRotation);\n        hasOwn(srcStyle, 'textDistance') && (textConfig.distance = srcStyle.textDistance);\n      }\n\n      convertEC4CompatibleRichItem(textContentStyle, hostStyle);\n      each(textContentStyle.rich, function (richItem) {\n        convertEC4CompatibleRichItem(richItem, richItem);\n      });\n      return {\n        textConfig: textConfig,\n        textContent: textContent\n      };\n    }\n    /**\n     * The result will be set to `out`.\n     */\n\n    function convertEC4CompatibleRichItem(out, richItem) {\n      if (!richItem) {\n        return;\n      } // (1) For simplicity, make textXXX properties (deprecated since ec5) has\n      // higher priority. For example, consider in ec4 `borderColor: 5, textBorderColor: 10`\n      // on a rect means `borderColor: 4` on the rect and `borderColor: 10` on an attached\n      // richText in ec5.\n      // (2) `out === richItem` if and only if `out` is text el or rich item.\n      // So we can overwrite existing props in `out` since textXXX has higher priority.\n\n\n      richItem.font = richItem.textFont || richItem.font;\n      hasOwn(richItem, 'textStrokeWidth') && (out.lineWidth = richItem.textStrokeWidth);\n      hasOwn(richItem, 'textAlign') && (out.align = richItem.textAlign);\n      hasOwn(richItem, 'textVerticalAlign') && (out.verticalAlign = richItem.textVerticalAlign);\n      hasOwn(richItem, 'textLineHeight') && (out.lineHeight = richItem.textLineHeight);\n      hasOwn(richItem, 'textWidth') && (out.width = richItem.textWidth);\n      hasOwn(richItem, 'textHeight') && (out.height = richItem.textHeight);\n      hasOwn(richItem, 'textBackgroundColor') && (out.backgroundColor = richItem.textBackgroundColor);\n      hasOwn(richItem, 'textPadding') && (out.padding = richItem.textPadding);\n      hasOwn(richItem, 'textBorderColor') && (out.borderColor = richItem.textBorderColor);\n      hasOwn(richItem, 'textBorderWidth') && (out.borderWidth = richItem.textBorderWidth);\n      hasOwn(richItem, 'textBorderRadius') && (out.borderRadius = richItem.textBorderRadius);\n      hasOwn(richItem, 'textBoxShadowColor') && (out.shadowColor = richItem.textBoxShadowColor);\n      hasOwn(richItem, 'textBoxShadowBlur') && (out.shadowBlur = richItem.textBoxShadowBlur);\n      hasOwn(richItem, 'textBoxShadowOffsetX') && (out.shadowOffsetX = richItem.textBoxShadowOffsetX);\n      hasOwn(richItem, 'textBoxShadowOffsetY') && (out.shadowOffsetY = richItem.textBoxShadowOffsetY);\n    }\n\n    var LEGACY_TRANSFORM_PROPS_MAP = {\n      position: ['x', 'y'],\n      scale: ['scaleX', 'scaleY'],\n      origin: ['originX', 'originY']\n    };\n    var LEGACY_TRANSFORM_PROPS = keys(LEGACY_TRANSFORM_PROPS_MAP);\n    var TRANSFORM_PROPS_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {\n      obj[key] = 1;\n      return obj;\n    }, {});\n    var transformPropNamesStr = TRANSFORMABLE_PROPS.join(', '); // '' means root\n\n    var ELEMENT_ANIMATABLE_PROPS = ['', 'style', 'shape', 'extra'];\n    var transitionInnerStore = makeInner();\n\n    function getElementAnimationConfig(animationType, el, elOption, parentModel, dataIndex) {\n      var animationProp = animationType + \"Animation\";\n      var config = getAnimationConfig(animationType, parentModel, dataIndex) || {};\n      var userDuring = transitionInnerStore(el).userDuring; // Only set when duration is > 0 and it's need to be animated.\n\n      if (config.duration > 0) {\n        // For simplicity, if during not specified, the previous during will not work any more.\n        config.during = userDuring ? bind(duringCall, {\n          el: el,\n          userDuring: userDuring\n        }) : null;\n        config.setToFinal = true;\n        config.scope = animationType;\n      }\n\n      extend(config, elOption[animationProp]);\n      return config;\n    }\n\n    function applyUpdateTransition(el, elOption, animatableModel, opts) {\n      opts = opts || {};\n      var dataIndex = opts.dataIndex,\n          isInit = opts.isInit,\n          clearStyle = opts.clearStyle;\n      var hasAnimation = animatableModel.isAnimationEnabled(); // Save the meta info for further morphing. Like apply on the sub morphing elements.\n\n      var store = transitionInnerStore(el);\n      var styleOpt = elOption.style;\n      store.userDuring = elOption.during;\n      var transFromProps = {};\n      var propsToSet = {};\n      prepareTransformAllPropsFinal(el, elOption, propsToSet);\n      prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet);\n      prepareShapeOrExtraAllPropsFinal('extra', elOption, propsToSet);\n\n      if (!isInit && hasAnimation) {\n        prepareTransformTransitionFrom(el, elOption, transFromProps);\n        prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps);\n        prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps);\n        prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps);\n      }\n\n      propsToSet.style = styleOpt;\n      applyPropsDirectly(el, propsToSet, clearStyle);\n      applyMiscProps(el, elOption);\n\n      if (hasAnimation) {\n        if (isInit) {\n          var enterFromProps_1 = {};\n          each(ELEMENT_ANIMATABLE_PROPS, function (propName) {\n            var prop = propName ? elOption[propName] : elOption;\n\n            if (prop && prop.enterFrom) {\n              if (propName) {\n                enterFromProps_1[propName] = enterFromProps_1[propName] || {};\n              }\n\n              extend(propName ? enterFromProps_1[propName] : enterFromProps_1, prop.enterFrom);\n            }\n          });\n          var config = getElementAnimationConfig('enter', el, elOption, animatableModel, dataIndex);\n\n          if (config.duration > 0) {\n            el.animateFrom(enterFromProps_1, config);\n          }\n        } else {\n          applyPropsTransition(el, elOption, dataIndex || 0, animatableModel, transFromProps);\n        }\n      } // Store leave to be used in leave transition.\n\n\n      updateLeaveTo(el, elOption);\n      styleOpt ? el.dirty() : el.markRedraw();\n    }\n    function updateLeaveTo(el, elOption) {\n      // Try merge to previous set leaveTo\n      var leaveToProps = transitionInnerStore(el).leaveToProps;\n\n      for (var i = 0; i < ELEMENT_ANIMATABLE_PROPS.length; i++) {\n        var propName = ELEMENT_ANIMATABLE_PROPS[i];\n        var prop = propName ? elOption[propName] : elOption;\n\n        if (prop && prop.leaveTo) {\n          if (!leaveToProps) {\n            leaveToProps = transitionInnerStore(el).leaveToProps = {};\n          }\n\n          if (propName) {\n            leaveToProps[propName] = leaveToProps[propName] || {};\n          }\n\n          extend(propName ? leaveToProps[propName] : leaveToProps, prop.leaveTo);\n        }\n      }\n    }\n    function applyLeaveTransition(el, elOption, animatableModel, onRemove) {\n      if (el) {\n        var parent_1 = el.parent;\n        var leaveToProps = transitionInnerStore(el).leaveToProps;\n\n        if (leaveToProps) {\n          // TODO TODO use leave after leaveAnimation in series is introduced\n          // TODO Data index?\n          var config = getElementAnimationConfig('update', el, elOption, animatableModel, 0);\n\n          config.done = function () {\n            parent_1.remove(el);\n            onRemove && onRemove();\n          };\n\n          el.animateTo(leaveToProps, config);\n        } else {\n          parent_1.remove(el);\n          onRemove && onRemove();\n        }\n      }\n    }\n    function isTransitionAll(transition) {\n      return transition === 'all';\n    }\n\n    function applyPropsDirectly(el, // Can be null/undefined\n    allPropsFinal, clearStyle) {\n      var styleOpt = allPropsFinal.style;\n\n      if (!el.isGroup && styleOpt) {\n        if (clearStyle) {\n          el.useStyle({}); // When style object changed, how to trade the existing animation?\n          // It is probably complicated and not needed to cover all the cases.\n          // But still need consider the case:\n          // (1) When using init animation on `style.opacity`, and before the animation\n          //     ended users triggers an update by mousewhel. At that time the init\n          //     animation should better be continued rather than terminated.\n          //     So after `useStyle` called, we should change the animation target manually\n          //     to continue the effect of the init animation.\n          // (2) PENDING: If the previous animation targeted at a `val1`, and currently we need\n          //     to update the value to `val2` and no animation declared, should be terminate\n          //     the previous animation or just modify the target of the animation?\n          //     Therotically That will happen not only on `style` but also on `shape` and\n          //     `transfrom` props. But we haven't handle this case at present yet.\n          // (3) PENDING: Is it proper to visit `animators` and `targetName`?\n\n          var animators = el.animators;\n\n          for (var i = 0; i < animators.length; i++) {\n            var animator = animators[i]; // targetName is the \"topKey\".\n\n            if (animator.targetName === 'style') {\n              animator.changeTarget(el.style);\n            }\n          }\n        }\n\n        el.setStyle(styleOpt);\n      }\n\n      if (allPropsFinal) {\n        // Not set style here.\n        allPropsFinal.style = null; // Set el to the final state firstly.\n\n        allPropsFinal && el.attr(allPropsFinal);\n        allPropsFinal.style = styleOpt;\n      }\n    }\n\n    function applyPropsTransition(el, elOption, dataIndex, model, // Can be null/undefined\n    transFromProps) {\n      if (transFromProps) {\n        var config = getElementAnimationConfig('update', el, elOption, model, dataIndex);\n\n        if (config.duration > 0) {\n          el.animateFrom(transFromProps, config);\n        }\n      }\n    }\n\n    function applyMiscProps(el, elOption) {\n      // Merge by default.\n      hasOwn(elOption, 'silent') && (el.silent = elOption.silent);\n      hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore);\n\n      if (el instanceof Displayable) {\n        hasOwn(elOption, 'invisible') && (el.invisible = elOption.invisible);\n      }\n\n      if (el instanceof Path) {\n        hasOwn(elOption, 'autoBatch') && (el.autoBatch = elOption.autoBatch);\n      }\n    } // Use it to avoid it be exposed to user.\n\n\n    var tmpDuringScope = {};\n    var transitionDuringAPI = {\n      // Usually other props do not need to be changed in animation during.\n      setTransform: function (key, val) {\n        if (\"development\" !== 'production') {\n          assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `setTransform`.');\n        }\n\n        tmpDuringScope.el[key] = val;\n        return this;\n      },\n      getTransform: function (key) {\n        if (\"development\" !== 'production') {\n          assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `getTransform`.');\n        }\n\n        return tmpDuringScope.el[key];\n      },\n      setShape: function (key, val) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var el = tmpDuringScope.el;\n        var shape = el.shape || (el.shape = {});\n        shape[key] = val;\n        el.dirtyShape && el.dirtyShape();\n        return this;\n      },\n      getShape: function (key) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var shape = tmpDuringScope.el.shape;\n\n        if (shape) {\n          return shape[key];\n        }\n      },\n      setStyle: function (key, val) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var el = tmpDuringScope.el;\n        var style = el.style;\n\n        if (style) {\n          if (\"development\" !== 'production') {\n            if (eqNaN(val)) {\n              warn('style.' + key + ' must not be assigned with NaN.');\n            }\n          }\n\n          style[key] = val;\n          el.dirtyStyle && el.dirtyStyle();\n        }\n\n        return this;\n      },\n      getStyle: function (key) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var style = tmpDuringScope.el.style;\n\n        if (style) {\n          return style[key];\n        }\n      },\n      setExtra: function (key, val) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var extra = tmpDuringScope.el.extra || (tmpDuringScope.el.extra = {});\n        extra[key] = val;\n        return this;\n      },\n      getExtra: function (key) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var extra = tmpDuringScope.el.extra;\n\n        if (extra) {\n          return extra[key];\n        }\n      }\n    };\n\n    function assertNotReserved(key) {\n      if (\"development\" !== 'production') {\n        if (key === 'transition' || key === 'enterFrom' || key === 'leaveTo') {\n          throw new Error('key must not be \"' + key + '\"');\n        }\n      }\n    }\n\n    function duringCall() {\n      // Do not provide \"percent\" until some requirements come.\n      // Because consider thies case:\n      // enterFrom: {x: 100, y: 30}, transition: 'x'.\n      // And enter duration is different from update duration.\n      // Thus it might be confused about the meaning of \"percent\" in during callback.\n      var scope = this;\n      var el = scope.el;\n\n      if (!el) {\n        return;\n      } // If el is remove from zr by reason like legend, during still need to called,\n      // because el will be added back to zr and the prop value should not be incorrect.\n\n\n      var latestUserDuring = transitionInnerStore(el).userDuring;\n      var scopeUserDuring = scope.userDuring; // Ensured a during is only called once in each animation frame.\n      // If a during is called multiple times in one frame, maybe some users' calculation logic\n      // might be wrong (not sure whether this usage exists).\n      // The case of a during might be called twice can be: by default there is a animator for\n      // 'x', 'y' when init. Before the init animation finished, call `setOption` to start\n      // another animators for 'style'/'shape'/'extra'.\n\n      if (latestUserDuring !== scopeUserDuring) {\n        // release\n        scope.el = scope.userDuring = null;\n        return;\n      }\n\n      tmpDuringScope.el = el; // Give no `this` to user in \"during\" calling.\n\n      scopeUserDuring(transitionDuringAPI); // FIXME: if in future meet the case that some prop will be both modified in `during` and `state`,\n      // consider the issue that the prop might be incorrect when return to \"normal\" state.\n    }\n\n    function prepareShapeOrExtraTransitionFrom(mainAttr, fromEl, elOption, transFromProps) {\n      var attrOpt = elOption[mainAttr];\n\n      if (!attrOpt) {\n        return;\n      }\n\n      var elPropsInAttr = fromEl[mainAttr];\n      var transFromPropsInAttr;\n\n      if (elPropsInAttr) {\n        var transition = elOption.transition;\n        var attrTransition = attrOpt.transition;\n\n        if (attrTransition) {\n          !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});\n\n          if (isTransitionAll(attrTransition)) {\n            extend(transFromPropsInAttr, elPropsInAttr);\n          } else {\n            var transitionKeys = normalizeToArray(attrTransition);\n\n            for (var i = 0; i < transitionKeys.length; i++) {\n              var key = transitionKeys[i];\n              var elVal = elPropsInAttr[key];\n              transFromPropsInAttr[key] = elVal;\n            }\n          }\n        } else if (isTransitionAll(transition) || indexOf(transition, mainAttr) >= 0) {\n          !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});\n          var elPropsInAttrKeys = keys(elPropsInAttr);\n\n          for (var i = 0; i < elPropsInAttrKeys.length; i++) {\n            var key = elPropsInAttrKeys[i];\n            var elVal = elPropsInAttr[key];\n\n            if (isNonStyleTransitionEnabled(attrOpt[key], elVal)) {\n              transFromPropsInAttr[key] = elVal;\n            }\n          }\n        }\n      }\n    }\n\n    function prepareShapeOrExtraAllPropsFinal(mainAttr, elOption, allProps) {\n      var attrOpt = elOption[mainAttr];\n\n      if (!attrOpt) {\n        return;\n      }\n\n      var allPropsInAttr = allProps[mainAttr] = {};\n      var keysInAttr = keys(attrOpt);\n\n      for (var i = 0; i < keysInAttr.length; i++) {\n        var key = keysInAttr[i]; // To avoid share one object with different element, and\n        // to avoid user modify the object inexpectedly, have to clone.\n\n        allPropsInAttr[key] = cloneValue(attrOpt[key]);\n      }\n    }\n\n    function prepareTransformTransitionFrom(el, elOption, transFromProps) {\n      var transition = elOption.transition;\n      var transitionKeys = isTransitionAll(transition) ? TRANSFORMABLE_PROPS : normalizeToArray(transition || []);\n\n      for (var i = 0; i < transitionKeys.length; i++) {\n        var key = transitionKeys[i];\n\n        if (key === 'style' || key === 'shape' || key === 'extra') {\n          continue;\n        }\n\n        var elVal = el[key];\n\n        if (\"development\" !== 'production') {\n          checkTransformPropRefer(key, 'el.transition');\n        } // Do not clone, animator will perform that clone.\n\n\n        transFromProps[key] = elVal;\n      }\n    }\n\n    function prepareTransformAllPropsFinal(el, elOption, allProps) {\n      for (var i = 0; i < LEGACY_TRANSFORM_PROPS.length; i++) {\n        var legacyName = LEGACY_TRANSFORM_PROPS[i];\n        var xyName = LEGACY_TRANSFORM_PROPS_MAP[legacyName];\n        var legacyArr = elOption[legacyName];\n\n        if (legacyArr) {\n          allProps[xyName[0]] = legacyArr[0];\n          allProps[xyName[1]] = legacyArr[1];\n        }\n      }\n\n      for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {\n        var key = TRANSFORMABLE_PROPS[i];\n\n        if (elOption[key] != null) {\n          allProps[key] = elOption[key];\n        }\n      }\n    }\n\n    function prepareStyleTransitionFrom(fromEl, elOption, styleOpt, transFromProps) {\n      if (!styleOpt) {\n        return;\n      }\n\n      var fromElStyle = fromEl.style;\n      var transFromStyleProps;\n\n      if (fromElStyle) {\n        var styleTransition = styleOpt.transition;\n        var elTransition = elOption.transition;\n\n        if (styleTransition && !isTransitionAll(styleTransition)) {\n          var transitionKeys = normalizeToArray(styleTransition);\n          !transFromStyleProps && (transFromStyleProps = transFromProps.style = {});\n\n          for (var i = 0; i < transitionKeys.length; i++) {\n            var key = transitionKeys[i];\n            var elVal = fromElStyle[key]; // Do not clone, see `checkNonStyleTansitionRefer`.\n\n            transFromStyleProps[key] = elVal;\n          }\n        } else if (fromEl.getAnimationStyleProps && (isTransitionAll(elTransition) || isTransitionAll(styleTransition) || indexOf(elTransition, 'style') >= 0)) {\n          var animationProps = fromEl.getAnimationStyleProps();\n          var animationStyleProps = animationProps ? animationProps.style : null;\n\n          if (animationStyleProps) {\n            !transFromStyleProps && (transFromStyleProps = transFromProps.style = {});\n            var styleKeys = keys(styleOpt);\n\n            for (var i = 0; i < styleKeys.length; i++) {\n              var key = styleKeys[i];\n\n              if (animationStyleProps[key]) {\n                var elVal = fromElStyle[key];\n                transFromStyleProps[key] = elVal;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    function isNonStyleTransitionEnabled(optVal, elVal) {\n      // The same as `checkNonStyleTansitionRefer`.\n      return !isArrayLike(optVal) ? optVal != null && isFinite(optVal) : optVal !== elVal;\n    }\n\n    var checkTransformPropRefer;\n\n    if (\"development\" !== 'production') {\n      checkTransformPropRefer = function (key, usedIn) {\n        if (!hasOwn(TRANSFORM_PROPS_MAP, key)) {\n          warn('Prop `' + key + '` is not a permitted in `' + usedIn + '`. ' + 'Only `' + keys(TRANSFORM_PROPS_MAP).join('`, `') + '` are permitted.');\n        }\n      };\n    }\n\n    var getStateToRestore = makeInner();\n    var KEYFRAME_EXCLUDE_KEYS = ['percent', 'easing', 'shape', 'style', 'extra'];\n    /**\n     * Stop previous keyframe animation and restore the attributes.\n     * Avoid new keyframe animation starts with wrong internal state when the percent: 0 is not set.\n     */\n\n    function stopPreviousKeyframeAnimationAndRestore(el) {\n      // Stop previous keyframe animation.\n      el.stopAnimation('keyframe'); // Restore\n\n      el.attr(getStateToRestore(el));\n    }\n    function applyKeyframeAnimation(el, animationOpts, animatableModel) {\n      if (!animatableModel.isAnimationEnabled() || !animationOpts) {\n        return;\n      }\n\n      if (isArray(animationOpts)) {\n        each(animationOpts, function (singleAnimationOpts) {\n          applyKeyframeAnimation(el, singleAnimationOpts, animatableModel);\n        });\n        return;\n      }\n\n      var keyframes = animationOpts.keyframes;\n      var duration = animationOpts.duration;\n\n      if (animatableModel && duration == null) {\n        // Default to use duration of config.\n        // NOTE: animation config from payload will be ignored because they are mainly for transitions.\n        var config = getAnimationConfig('enter', animatableModel, 0);\n        duration = config && config.duration;\n      }\n\n      if (!keyframes || !duration) {\n        return;\n      }\n\n      var stateToRestore = getStateToRestore(el);\n      each(ELEMENT_ANIMATABLE_PROPS, function (targetPropName) {\n        if (targetPropName && !el[targetPropName]) {\n          return;\n        }\n\n        var animator;\n        var endFrameIsSet = false; // Sort keyframes by percent.\n\n        keyframes.sort(function (a, b) {\n          return a.percent - b.percent;\n        });\n        each(keyframes, function (kf) {\n          // Stop current animation.\n          var animators = el.animators;\n          var kfValues = targetPropName ? kf[targetPropName] : kf;\n\n          if (\"development\" !== 'production') {\n            if (kf.percent >= 1) {\n              endFrameIsSet = true;\n            }\n          }\n\n          if (!kfValues) {\n            return;\n          }\n\n          var propKeys = keys(kfValues);\n\n          if (!targetPropName) {\n            // PENDING performance?\n            propKeys = filter(propKeys, function (key) {\n              return indexOf(KEYFRAME_EXCLUDE_KEYS, key) < 0;\n            });\n          }\n\n          if (!propKeys.length) {\n            return;\n          }\n\n          if (!animator) {\n            animator = el.animate(targetPropName, animationOpts.loop, true);\n            animator.scope = 'keyframe';\n          }\n\n          for (var i = 0; i < animators.length; i++) {\n            // Stop all other animation that is not keyframe.\n            if (animators[i] !== animator && animators[i].targetName === animator.targetName) {\n              animators[i].stopTracks(propKeys);\n            }\n          }\n\n          targetPropName && (stateToRestore[targetPropName] = stateToRestore[targetPropName] || {});\n          var savedTarget = targetPropName ? stateToRestore[targetPropName] : stateToRestore;\n          each(propKeys, function (key) {\n            // Save original value.\n            savedTarget[key] = ((targetPropName ? el[targetPropName] : el) || {})[key];\n          });\n          animator.whenWithKeys(duration * kf.percent, kfValues, propKeys, kf.easing);\n        });\n\n        if (!animator) {\n          return;\n        }\n\n        if (\"development\" !== 'production') {\n          if (!endFrameIsSet) {\n            warn('End frame with percent: 1 is missing in the keyframeAnimation.', true);\n          }\n        }\n\n        animator.delay(animationOpts.delay || 0).duration(duration).start(animationOpts.easing);\n      });\n    }\n\n    var nonShapeGraphicElements = {\n      // Reserved but not supported in graphic component.\n      path: null,\n      compoundPath: null,\n      // Supported in graphic component.\n      group: Group,\n      image: ZRImage,\n      text: ZRText\n    };\n    var inner$a = makeInner(); // ------------------------\n    // View\n    // ------------------------\n\n    var GraphicComponentView =\n    /** @class */\n    function (_super) {\n      __extends(GraphicComponentView, _super);\n\n      function GraphicComponentView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GraphicComponentView.type;\n        return _this;\n      }\n\n      GraphicComponentView.prototype.init = function () {\n        this._elMap = createHashMap();\n      };\n\n      GraphicComponentView.prototype.render = function (graphicModel, ecModel, api) {\n        // Having leveraged between use cases and algorithm complexity, a very\n        // simple layout mechanism is used:\n        // The size(width/height) can be determined by itself or its parent (not\n        // implemented yet), but can not by its children. (Top-down travel)\n        // The location(x/y) can be determined by the bounding rect of itself\n        // (can including its descendants or not) and the size of its parent.\n        // (Bottom-up travel)\n        // When `chart.clear()` or `chart.setOption({...}, true)` with the same id,\n        // view will be reused.\n        if (graphicModel !== this._lastGraphicModel) {\n          this._clear();\n        }\n\n        this._lastGraphicModel = graphicModel;\n\n        this._updateElements(graphicModel);\n\n        this._relocate(graphicModel, api);\n      };\n      /**\n       * Update graphic elements.\n       */\n\n\n      GraphicComponentView.prototype._updateElements = function (graphicModel) {\n        var elOptionsToUpdate = graphicModel.useElOptionsToUpdate();\n\n        if (!elOptionsToUpdate) {\n          return;\n        }\n\n        var elMap = this._elMap;\n        var rootGroup = this.group;\n        var globalZ = graphicModel.get('z');\n        var globalZLevel = graphicModel.get('zlevel'); // Top-down tranverse to assign graphic settings to each elements.\n\n        each(elOptionsToUpdate, function (elOption) {\n          var id = convertOptionIdName(elOption.id, null);\n          var elExisting = id != null ? elMap.get(id) : null;\n          var parentId = convertOptionIdName(elOption.parentId, null);\n          var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup;\n          var elType = elOption.type;\n          var elOptionStyle = elOption.style;\n\n          if (elType === 'text' && elOptionStyle) {\n            // In top/bottom mode, textVerticalAlign should not be used, which cause\n            // inaccurately locating.\n            if (elOption.hv && elOption.hv[1]) {\n              elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = elOptionStyle.verticalAlign = elOptionStyle.align = null;\n            }\n          }\n\n          var textContentOption = elOption.textContent;\n          var textConfig = elOption.textConfig;\n\n          if (elOptionStyle && isEC4CompatibleStyle(elOptionStyle, elType, !!textConfig, !!textContentOption)) {\n            var convertResult = convertFromEC4CompatibleStyle(elOptionStyle, elType, true);\n\n            if (!textConfig && convertResult.textConfig) {\n              textConfig = elOption.textConfig = convertResult.textConfig;\n            }\n\n            if (!textContentOption && convertResult.textContent) {\n              textContentOption = convertResult.textContent;\n            }\n          } // Remove unnecessary props to avoid potential problems.\n\n\n          var elOptionCleaned = getCleanedElOption(elOption); // For simple, do not support parent change, otherwise reorder is needed.\n\n          if (\"development\" !== 'production') {\n            elExisting && assert(targetElParent === elExisting.parent, 'Changing parent is not supported.');\n          }\n\n          var $action = elOption.$action || 'merge';\n          var isMerge = $action === 'merge';\n          var isReplace = $action === 'replace';\n\n          if (isMerge) {\n            var isInit = !elExisting;\n            var el_1 = elExisting;\n\n            if (isInit) {\n              el_1 = createEl(id, targetElParent, elOption.type, elMap);\n            } else {\n              el_1 && (inner$a(el_1).isNew = false); // Stop and restore before update any other attributes.\n\n              stopPreviousKeyframeAnimationAndRestore(el_1);\n            }\n\n            if (el_1) {\n              applyUpdateTransition(el_1, elOptionCleaned, graphicModel, {\n                isInit: isInit\n              });\n              updateCommonAttrs(el_1, elOption, globalZ, globalZLevel);\n            }\n          } else if (isReplace) {\n            removeEl(elExisting, elOption, elMap, graphicModel);\n            var el_2 = createEl(id, targetElParent, elOption.type, elMap);\n\n            if (el_2) {\n              applyUpdateTransition(el_2, elOptionCleaned, graphicModel, {\n                isInit: true\n              });\n              updateCommonAttrs(el_2, elOption, globalZ, globalZLevel);\n            }\n          } else if ($action === 'remove') {\n            updateLeaveTo(elExisting, elOption);\n            removeEl(elExisting, elOption, elMap, graphicModel);\n          }\n\n          var el = elMap.get(id);\n\n          if (el && textContentOption) {\n            if (isMerge) {\n              var textContentExisting = el.getTextContent();\n              textContentExisting ? textContentExisting.attr(textContentOption) : el.setTextContent(new ZRText(textContentOption));\n            } else if (isReplace) {\n              el.setTextContent(new ZRText(textContentOption));\n            }\n          }\n\n          if (el) {\n            var clipPathOption = elOption.clipPath;\n\n            if (clipPathOption) {\n              var clipPathType = clipPathOption.type;\n              var clipPath = void 0;\n              var isInit = false;\n\n              if (isMerge) {\n                var oldClipPath = el.getClipPath();\n                isInit = !oldClipPath || inner$a(oldClipPath).type !== clipPathType;\n                clipPath = isInit ? newEl(clipPathType) : oldClipPath;\n              } else if (isReplace) {\n                isInit = true;\n                clipPath = newEl(clipPathType);\n              }\n\n              el.setClipPath(clipPath);\n              applyUpdateTransition(clipPath, clipPathOption, graphicModel, {\n                isInit: isInit\n              });\n              applyKeyframeAnimation(clipPath, clipPathOption.keyframeAnimation, graphicModel);\n            }\n\n            var elInner = inner$a(el);\n            el.setTextConfig(textConfig);\n            elInner.option = elOption;\n            setEventData(el, graphicModel, elOption);\n            setTooltipConfig({\n              el: el,\n              componentModel: graphicModel,\n              itemName: el.name,\n              itemTooltipOption: elOption.tooltip\n            });\n            applyKeyframeAnimation(el, elOption.keyframeAnimation, graphicModel);\n          }\n        });\n      };\n      /**\n       * Locate graphic elements.\n       */\n\n\n      GraphicComponentView.prototype._relocate = function (graphicModel, api) {\n        var elOptions = graphicModel.option.elements;\n        var rootGroup = this.group;\n        var elMap = this._elMap;\n        var apiWidth = api.getWidth();\n        var apiHeight = api.getHeight();\n        var xy = ['x', 'y']; // Top-down to calculate percentage width/height of group\n\n        for (var i = 0; i < elOptions.length; i++) {\n          var elOption = elOptions[i];\n          var id = convertOptionIdName(elOption.id, null);\n          var el = id != null ? elMap.get(id) : null;\n\n          if (!el || !el.isGroup) {\n            continue;\n          }\n\n          var parentEl = el.parent;\n          var isParentRoot = parentEl === rootGroup; // Like 'position:absolut' in css, default 0.\n\n          var elInner = inner$a(el);\n          var parentElInner = inner$a(parentEl);\n          elInner.width = parsePercent$1(elInner.option.width, isParentRoot ? apiWidth : parentElInner.width) || 0;\n          elInner.height = parsePercent$1(elInner.option.height, isParentRoot ? apiHeight : parentElInner.height) || 0;\n        } // Bottom-up tranvese all elements (consider ec resize) to locate elements.\n\n\n        for (var i = elOptions.length - 1; i >= 0; i--) {\n          var elOption = elOptions[i];\n          var id = convertOptionIdName(elOption.id, null);\n          var el = id != null ? elMap.get(id) : null;\n\n          if (!el) {\n            continue;\n          }\n\n          var parentEl = el.parent;\n          var parentElInner = inner$a(parentEl);\n          var containerInfo = parentEl === rootGroup ? {\n            width: apiWidth,\n            height: apiHeight\n          } : {\n            width: parentElInner.width,\n            height: parentElInner.height\n          }; // PENDING\n          // Currently, when `bounding: 'all'`, the union bounding rect of the group\n          // does not include the rect of [0, 0, group.width, group.height], which\n          // is probably weird for users. Should we make a break change for it?\n\n          var layoutPos = {};\n          var layouted = positionElement(el, elOption, containerInfo, null, {\n            hv: elOption.hv,\n            boundingMode: elOption.bounding\n          }, layoutPos);\n\n          if (!inner$a(el).isNew && layouted) {\n            var transition = elOption.transition;\n            var animatePos = {};\n\n            for (var k = 0; k < xy.length; k++) {\n              var key = xy[k];\n              var val = layoutPos[key];\n\n              if (transition && (isTransitionAll(transition) || indexOf(transition, key) >= 0)) {\n                animatePos[key] = val;\n              } else {\n                el[key] = val;\n              }\n            }\n\n            updateProps(el, animatePos, graphicModel, 0);\n          } else {\n            el.attr(layoutPos);\n          }\n        }\n      };\n      /**\n       * Clear all elements.\n       */\n\n\n      GraphicComponentView.prototype._clear = function () {\n        var _this = this;\n\n        var elMap = this._elMap;\n        elMap.each(function (el) {\n          removeEl(el, inner$a(el).option, elMap, _this._lastGraphicModel);\n        });\n        this._elMap = createHashMap();\n      };\n\n      GraphicComponentView.prototype.dispose = function () {\n        this._clear();\n      };\n\n      GraphicComponentView.type = 'graphic';\n      return GraphicComponentView;\n    }(ComponentView);\n\n    function newEl(graphicType) {\n      if (\"development\" !== 'production') {\n        assert(graphicType, 'graphic type MUST be set');\n      }\n\n      var Clz = hasOwn(nonShapeGraphicElements, graphicType) // Those graphic elements are not shapes. They should not be\n      // overwritten by users, so do them first.\n      ? nonShapeGraphicElements[graphicType] : getShapeClass(graphicType);\n\n      if (\"development\" !== 'production') {\n        assert(Clz, \"graphic type \" + graphicType + \" can not be found\");\n      }\n\n      var el = new Clz({});\n      inner$a(el).type = graphicType;\n      return el;\n    }\n\n    function createEl(id, targetElParent, graphicType, elMap) {\n      var el = newEl(graphicType);\n      targetElParent.add(el);\n      elMap.set(id, el);\n      inner$a(el).id = id;\n      inner$a(el).isNew = true;\n      return el;\n    }\n\n    function removeEl(elExisting, elOption, elMap, graphicModel) {\n      var existElParent = elExisting && elExisting.parent;\n\n      if (existElParent) {\n        elExisting.type === 'group' && elExisting.traverse(function (el) {\n          removeEl(el, elOption, elMap, graphicModel);\n        });\n        applyLeaveTransition(elExisting, elOption, graphicModel);\n        elMap.removeKey(inner$a(elExisting).id);\n      }\n    }\n\n    function updateCommonAttrs(el, elOption, defaultZ, defaultZlevel) {\n      if (!el.isGroup) {\n        each([['cursor', Displayable.prototype.cursor], // We should not support configure z and zlevel in the element level.\n        // But seems we didn't limit it previously. So here still use it to avoid breaking.\n        ['zlevel', defaultZlevel || 0], ['z', defaultZ || 0], // z2 must not be null/undefined, otherwise sort error may occur.\n        ['z2', 0]], function (item) {\n          var prop = item[0];\n\n          if (hasOwn(elOption, prop)) {\n            el[prop] = retrieve2(elOption[prop], item[1]);\n          } else if (el[prop] == null) {\n            el[prop] = item[1];\n          }\n        });\n      }\n\n      each(keys(elOption), function (key) {\n        // Assign event handlers.\n        // PENDING: should enumerate all event names or use pattern matching?\n        if (key.indexOf('on') === 0) {\n          var val = elOption[key];\n          el[key] = isFunction(val) ? val : null;\n        }\n      });\n\n      if (hasOwn(elOption, 'draggable')) {\n        el.draggable = elOption.draggable;\n      } // Other attributes\n\n\n      elOption.name != null && (el.name = elOption.name);\n      elOption.id != null && (el.id = elOption.id);\n    } // Remove unnecessary props to avoid potential problems.\n\n\n    function getCleanedElOption(elOption) {\n      elOption = extend({}, elOption);\n      each(['id', 'parentId', '$action', 'hv', 'bounding', 'textContent', 'clipPath'].concat(LOCATION_PARAMS), function (name) {\n        delete elOption[name];\n      });\n      return elOption;\n    }\n\n    function setEventData(el, graphicModel, elOption) {\n      var eventData = getECData(el).eventData; // Simple optimize for large amount of elements that no need event.\n\n      if (!el.silent && !el.ignore && !eventData) {\n        eventData = getECData(el).eventData = {\n          componentType: 'graphic',\n          componentIndex: graphicModel.componentIndex,\n          name: el.name\n        };\n      } // `elOption.info` enables user to mount some info on\n      // elements and use them in event handlers.\n\n\n      if (eventData) {\n        eventData.info = elOption.info;\n      }\n    }\n\n    function install$9(registers) {\n      registers.registerComponentModel(GraphicComponentModel);\n      registers.registerComponentView(GraphicComponentView);\n      registers.registerPreprocessor(function (option) {\n        var graphicOption = option.graphic; // Convert\n        // {graphic: [{left: 10, type: 'circle'}, ...]}\n        // or\n        // {graphic: {left: 10, type: 'circle'}}\n        // to\n        // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]}\n\n        if (isArray(graphicOption)) {\n          if (!graphicOption[0] || !graphicOption[0].elements) {\n            option.graphic = [{\n              elements: graphicOption\n            }];\n          } else {\n            // Only one graphic instance can be instantiated. (We don't\n            // want that too many views are created in echarts._viewMap.)\n            option.graphic = [option.graphic[0]];\n          }\n        } else if (graphicOption && !graphicOption.elements) {\n          option.graphic = [{\n            elements: [graphicOption]\n          }];\n        }\n      });\n    }\n\n    var DATA_ZOOM_AXIS_DIMENSIONS = ['x', 'y', 'radius', 'angle', 'single']; // Supported coords.\n    // FIXME: polar has been broken (but rarely used).\n\n    var SERIES_COORDS = ['cartesian2d', 'polar', 'singleAxis'];\n    function isCoordSupported(seriesModel) {\n      var coordType = seriesModel.get('coordinateSystem');\n      return indexOf(SERIES_COORDS, coordType) >= 0;\n    }\n    function getAxisMainType(axisDim) {\n      if (\"development\" !== 'production') {\n        assert(axisDim);\n      }\n\n      return axisDim + 'Axis';\n    }\n    /**\n     * If two dataZoomModels has the same axis controlled, we say that they are 'linked'.\n     * This function finds all linked dataZoomModels start from the given payload.\n     */\n\n    function findEffectedDataZooms(ecModel, payload) {\n      // Key: `DataZoomAxisDimension`\n      var axisRecords = createHashMap();\n      var effectedModels = []; // Key: uid of dataZoomModel\n\n      var effectedModelMap = createHashMap(); // Find the dataZooms specified by payload.\n\n      ecModel.eachComponent({\n        mainType: 'dataZoom',\n        query: payload\n      }, function (dataZoomModel) {\n        if (!effectedModelMap.get(dataZoomModel.uid)) {\n          addToEffected(dataZoomModel);\n        }\n      }); // Start from the given dataZoomModels, travel the graph to find\n      // all of the linked dataZoom models.\n\n      var foundNewLink;\n\n      do {\n        foundNewLink = false;\n        ecModel.eachComponent('dataZoom', processSingle);\n      } while (foundNewLink);\n\n      function processSingle(dataZoomModel) {\n        if (!effectedModelMap.get(dataZoomModel.uid) && isLinked(dataZoomModel)) {\n          addToEffected(dataZoomModel);\n          foundNewLink = true;\n        }\n      }\n\n      function addToEffected(dataZoom) {\n        effectedModelMap.set(dataZoom.uid, true);\n        effectedModels.push(dataZoom);\n        markAxisControlled(dataZoom);\n      }\n\n      function isLinked(dataZoomModel) {\n        var isLink = false;\n        dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n          var axisIdxArr = axisRecords.get(axisDim);\n\n          if (axisIdxArr && axisIdxArr[axisIndex]) {\n            isLink = true;\n          }\n        });\n        return isLink;\n      }\n\n      function markAxisControlled(dataZoomModel) {\n        dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n          (axisRecords.get(axisDim) || axisRecords.set(axisDim, []))[axisIndex] = true;\n        });\n      }\n\n      return effectedModels;\n    }\n    /**\n     * Find the first target coordinate system.\n     * Available after model built.\n     *\n     * @return Like {\n     *                  grid: [\n     *                      {model: coord0, axisModels: [axis1, axis3], coordIndex: 1},\n     *                      {model: coord1, axisModels: [axis0, axis2], coordIndex: 0},\n     *                      ...\n     *                  ],  // cartesians must not be null/undefined.\n     *                  polar: [\n     *                      {model: coord0, axisModels: [axis4], coordIndex: 0},\n     *                      ...\n     *                  ],  // polars must not be null/undefined.\n     *                  singleAxis: [\n     *                      {model: coord0, axisModels: [], coordIndex: 0}\n     *                  ]\n     *              }\n     */\n\n    function collectReferCoordSysModelInfo(dataZoomModel) {\n      var ecModel = dataZoomModel.ecModel;\n      var coordSysInfoWrap = {\n        infoList: [],\n        infoMap: createHashMap()\n      };\n      dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n        var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n\n        if (!axisModel) {\n          return;\n        }\n\n        var coordSysModel = axisModel.getCoordSysModel();\n\n        if (!coordSysModel) {\n          return;\n        }\n\n        var coordSysUid = coordSysModel.uid;\n        var coordSysInfo = coordSysInfoWrap.infoMap.get(coordSysUid);\n\n        if (!coordSysInfo) {\n          coordSysInfo = {\n            model: coordSysModel,\n            axisModels: []\n          };\n          coordSysInfoWrap.infoList.push(coordSysInfo);\n          coordSysInfoWrap.infoMap.set(coordSysUid, coordSysInfo);\n        }\n\n        coordSysInfo.axisModels.push(axisModel);\n      });\n      return coordSysInfoWrap;\n    }\n\n    var DataZoomAxisInfo =\n    /** @class */\n    function () {\n      function DataZoomAxisInfo() {\n        this.indexList = [];\n        this.indexMap = [];\n      }\n\n      DataZoomAxisInfo.prototype.add = function (axisCmptIdx) {\n        // Remove duplication.\n        if (!this.indexMap[axisCmptIdx]) {\n          this.indexList.push(axisCmptIdx);\n          this.indexMap[axisCmptIdx] = true;\n        }\n      };\n\n      return DataZoomAxisInfo;\n    }();\n\n    var DataZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(DataZoomModel, _super);\n\n      function DataZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = DataZoomModel.type;\n        _this._autoThrottle = true;\n        _this._noTarget = true;\n        /**\n         * It is `[rangeModeForMin, rangeModeForMax]`.\n         * The optional values for `rangeMode`:\n         * + `'value'` mode: the axis extent will always be determined by\n         *     `dataZoom.startValue` and `dataZoom.endValue`, despite\n         *     how data like and how `axis.min` and `axis.max` are.\n         * + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`,\n         *     where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`,\n         *     and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`.\n         *     Axis extent will be determined by the result of the percent of `[dMin, dMax]`.\n         *\n         * For example, when users are using dynamic data (update data periodically via `setOption`),\n         * if in `'value`' mode, the window will be kept in a fixed value range despite how\n         * data are appended, while if in `'percent'` mode, whe window range will be changed alone with\n         * the appended data (suppose `axis.min` and `axis.max` are not specified).\n         */\n\n        _this._rangePropMode = ['percent', 'percent'];\n        return _this;\n      }\n\n      DataZoomModel.prototype.init = function (option, parentModel, ecModel) {\n        var inputRawOption = retrieveRawOption(option);\n        /**\n         * Suppose a \"main process\" start at the point that model prepared (that is,\n         * model initialized or merged or method called in `action`).\n         * We should keep the `main process` idempotent, that is, given a set of values\n         * on `option`, we get the same result.\n         *\n         * But sometimes, values on `option` will be updated for providing users\n         * a \"final calculated value\" (`dataZoomProcessor` will do that). Those value\n         * should not be the base/input of the `main process`.\n         *\n         * So in that case we should save and keep the input of the `main process`\n         * separately, called `settledOption`.\n         *\n         * For example, consider the case:\n         * (Step_1) brush zoom the grid by `toolbox.dataZoom`,\n         *     where the original input `option.startValue`, `option.endValue` are earsed by\n         *     calculated value.\n         * (Step)2) click the legend to hide and show a series,\n         *     where the new range is calculated by the earsed `startValue` and `endValue`,\n         *     which brings incorrect result.\n         */\n\n        this.settledOption = inputRawOption;\n        this.mergeDefaultAndTheme(option, ecModel);\n\n        this._doInit(inputRawOption);\n      };\n\n      DataZoomModel.prototype.mergeOption = function (newOption) {\n        var inputRawOption = retrieveRawOption(newOption); // FIX #2591\n\n        merge(this.option, newOption, true);\n        merge(this.settledOption, inputRawOption, true);\n\n        this._doInit(inputRawOption);\n      };\n\n      DataZoomModel.prototype._doInit = function (inputRawOption) {\n        var thisOption = this.option;\n\n        this._setDefaultThrottle(inputRawOption);\n\n        this._updateRangeUse(inputRawOption);\n\n        var settledOption = this.settledOption;\n        each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {\n          // start/end has higher priority over startValue/endValue if they\n          // both set, but we should make chart.setOption({endValue: 1000})\n          // effective, rather than chart.setOption({endValue: 1000, end: null}).\n          if (this._rangePropMode[index] === 'value') {\n            thisOption[names[0]] = settledOption[names[0]] = null;\n          } // Otherwise do nothing and use the merge result.\n\n        }, this);\n\n        this._resetTarget();\n      };\n\n      DataZoomModel.prototype._resetTarget = function () {\n        var optionOrient = this.get('orient', true);\n        var targetAxisIndexMap = this._targetAxisInfoMap = createHashMap();\n\n        var hasAxisSpecified = this._fillSpecifiedTargetAxis(targetAxisIndexMap);\n\n        if (hasAxisSpecified) {\n          this._orient = optionOrient || this._makeAutoOrientByTargetAxis();\n        } else {\n          this._orient = optionOrient || 'horizontal';\n\n          this._fillAutoTargetAxisByOrient(targetAxisIndexMap, this._orient);\n        }\n\n        this._noTarget = true;\n        targetAxisIndexMap.each(function (axisInfo) {\n          if (axisInfo.indexList.length) {\n            this._noTarget = false;\n          }\n        }, this);\n      };\n\n      DataZoomModel.prototype._fillSpecifiedTargetAxis = function (targetAxisIndexMap) {\n        var hasAxisSpecified = false;\n        each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) {\n          var refering = this.getReferringComponents(getAxisMainType(axisDim), MULTIPLE_REFERRING); // When user set axisIndex as a empty array, we think that user specify axisIndex\n          // but do not want use auto mode. Because empty array may be encountered when\n          // some error occurred.\n\n          if (!refering.specified) {\n            return;\n          }\n\n          hasAxisSpecified = true;\n          var axisInfo = new DataZoomAxisInfo();\n          each(refering.models, function (axisModel) {\n            axisInfo.add(axisModel.componentIndex);\n          });\n          targetAxisIndexMap.set(axisDim, axisInfo);\n        }, this);\n        return hasAxisSpecified;\n      };\n\n      DataZoomModel.prototype._fillAutoTargetAxisByOrient = function (targetAxisIndexMap, orient) {\n        var ecModel = this.ecModel;\n        var needAuto = true; // Find axis that parallel to dataZoom as default.\n\n        if (needAuto) {\n          var axisDim = orient === 'vertical' ? 'y' : 'x';\n          var axisModels = ecModel.findComponents({\n            mainType: axisDim + 'Axis'\n          });\n          setParallelAxis(axisModels, axisDim);\n        } // Find axis that parallel to dataZoom as default.\n\n\n        if (needAuto) {\n          var axisModels = ecModel.findComponents({\n            mainType: 'singleAxis',\n            filter: function (axisModel) {\n              return axisModel.get('orient', true) === orient;\n            }\n          });\n          setParallelAxis(axisModels, 'single');\n        }\n\n        function setParallelAxis(axisModels, axisDim) {\n          // At least use the first parallel axis as the target axis.\n          var axisModel = axisModels[0];\n\n          if (!axisModel) {\n            return;\n          }\n\n          var axisInfo = new DataZoomAxisInfo();\n          axisInfo.add(axisModel.componentIndex);\n          targetAxisIndexMap.set(axisDim, axisInfo);\n          needAuto = false; // Find parallel axes in the same grid.\n\n          if (axisDim === 'x' || axisDim === 'y') {\n            var gridModel_1 = axisModel.getReferringComponents('grid', SINGLE_REFERRING).models[0];\n            gridModel_1 && each(axisModels, function (axModel) {\n              if (axisModel.componentIndex !== axModel.componentIndex && gridModel_1 === axModel.getReferringComponents('grid', SINGLE_REFERRING).models[0]) {\n                axisInfo.add(axModel.componentIndex);\n              }\n            });\n          }\n        }\n\n        if (needAuto) {\n          // If no parallel axis, find the first category axis as default. (Also consider polar).\n          each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) {\n            if (!needAuto) {\n              return;\n            }\n\n            var axisModels = ecModel.findComponents({\n              mainType: getAxisMainType(axisDim),\n              filter: function (axisModel) {\n                return axisModel.get('type', true) === 'category';\n              }\n            });\n\n            if (axisModels[0]) {\n              var axisInfo = new DataZoomAxisInfo();\n              axisInfo.add(axisModels[0].componentIndex);\n              targetAxisIndexMap.set(axisDim, axisInfo);\n              needAuto = false;\n            }\n          }, this);\n        }\n      };\n\n      DataZoomModel.prototype._makeAutoOrientByTargetAxis = function () {\n        var dim; // Find the first axis\n\n        this.eachTargetAxis(function (axisDim) {\n          !dim && (dim = axisDim);\n        }, this);\n        return dim === 'y' ? 'vertical' : 'horizontal';\n      };\n\n      DataZoomModel.prototype._setDefaultThrottle = function (inputRawOption) {\n        // When first time user set throttle, auto throttle ends.\n        if (inputRawOption.hasOwnProperty('throttle')) {\n          this._autoThrottle = false;\n        }\n\n        if (this._autoThrottle) {\n          var globalOption = this.ecModel.option;\n          this.option.throttle = globalOption.animation && globalOption.animationDurationUpdate > 0 ? 100 : 20;\n        }\n      };\n\n      DataZoomModel.prototype._updateRangeUse = function (inputRawOption) {\n        var rangePropMode = this._rangePropMode;\n        var rangeModeInOption = this.get('rangeMode');\n        each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {\n          var percentSpecified = inputRawOption[names[0]] != null;\n          var valueSpecified = inputRawOption[names[1]] != null;\n\n          if (percentSpecified && !valueSpecified) {\n            rangePropMode[index] = 'percent';\n          } else if (!percentSpecified && valueSpecified) {\n            rangePropMode[index] = 'value';\n          } else if (rangeModeInOption) {\n            rangePropMode[index] = rangeModeInOption[index];\n          } else if (percentSpecified) {\n            // percentSpecified && valueSpecified\n            rangePropMode[index] = 'percent';\n          } // else remain its original setting.\n\n        });\n      };\n\n      DataZoomModel.prototype.noTarget = function () {\n        return this._noTarget;\n      };\n\n      DataZoomModel.prototype.getFirstTargetAxisModel = function () {\n        var firstAxisModel;\n        this.eachTargetAxis(function (axisDim, axisIndex) {\n          if (firstAxisModel == null) {\n            firstAxisModel = this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n          }\n        }, this);\n        return firstAxisModel;\n      };\n      /**\n       * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel\n       */\n\n\n      DataZoomModel.prototype.eachTargetAxis = function (callback, context) {\n        this._targetAxisInfoMap.each(function (axisInfo, axisDim) {\n          each(axisInfo.indexList, function (axisIndex) {\n            callback.call(context, axisDim, axisIndex);\n          });\n        });\n      };\n      /**\n       * @return If not found, return null/undefined.\n       */\n\n\n      DataZoomModel.prototype.getAxisProxy = function (axisDim, axisIndex) {\n        var axisModel = this.getAxisModel(axisDim, axisIndex);\n\n        if (axisModel) {\n          return axisModel.__dzAxisProxy;\n        }\n      };\n      /**\n       * @return If not found, return null/undefined.\n       */\n\n\n      DataZoomModel.prototype.getAxisModel = function (axisDim, axisIndex) {\n        if (\"development\" !== 'production') {\n          assert(axisDim && axisIndex != null);\n        }\n\n        var axisInfo = this._targetAxisInfoMap.get(axisDim);\n\n        if (axisInfo && axisInfo.indexMap[axisIndex]) {\n          return this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n        }\n      };\n      /**\n       * If not specified, set to undefined.\n       */\n\n\n      DataZoomModel.prototype.setRawRange = function (opt) {\n        var thisOption = this.option;\n        var settledOption = this.settledOption;\n        each([['start', 'startValue'], ['end', 'endValue']], function (names) {\n          // Consider the pair <start, startValue>:\n          // If one has value and the other one is `null/undefined`, we both set them\n          // to `settledOption`. This strategy enables the feature to clear the original\n          // value in `settledOption` to `null/undefined`.\n          // But if both of them are `null/undefined`, we do not set them to `settledOption`\n          // and keep `settledOption` with the original value. This strategy enables users to\n          // only set <end or endValue> but not set <start or startValue> when calling\n          // `dispatchAction`.\n          // The pair <end, endValue> is treated in the same way.\n          if (opt[names[0]] != null || opt[names[1]] != null) {\n            thisOption[names[0]] = settledOption[names[0]] = opt[names[0]];\n            thisOption[names[1]] = settledOption[names[1]] = opt[names[1]];\n          }\n        }, this);\n\n        this._updateRangeUse(opt);\n      };\n\n      DataZoomModel.prototype.setCalculatedRange = function (opt) {\n        var option = this.option;\n        each(['start', 'startValue', 'end', 'endValue'], function (name) {\n          option[name] = opt[name];\n        });\n      };\n\n      DataZoomModel.prototype.getPercentRange = function () {\n        var axisProxy = this.findRepresentativeAxisProxy();\n\n        if (axisProxy) {\n          return axisProxy.getDataPercentWindow();\n        }\n      };\n      /**\n       * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);\n       *\n       * @return [startValue, endValue] value can only be '-' or finite number.\n       */\n\n\n      DataZoomModel.prototype.getValueRange = function (axisDim, axisIndex) {\n        if (axisDim == null && axisIndex == null) {\n          var axisProxy = this.findRepresentativeAxisProxy();\n\n          if (axisProxy) {\n            return axisProxy.getDataValueWindow();\n          }\n        } else {\n          return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow();\n        }\n      };\n      /**\n       * @param axisModel If axisModel given, find axisProxy\n       *      corresponding to the axisModel\n       */\n\n\n      DataZoomModel.prototype.findRepresentativeAxisProxy = function (axisModel) {\n        if (axisModel) {\n          return axisModel.__dzAxisProxy;\n        } // Find the first hosted axisProxy\n\n\n        var firstProxy;\n\n        var axisDimList = this._targetAxisInfoMap.keys();\n\n        for (var i = 0; i < axisDimList.length; i++) {\n          var axisDim = axisDimList[i];\n\n          var axisInfo = this._targetAxisInfoMap.get(axisDim);\n\n          for (var j = 0; j < axisInfo.indexList.length; j++) {\n            var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]);\n\n            if (proxy.hostedBy(this)) {\n              return proxy;\n            }\n\n            if (!firstProxy) {\n              firstProxy = proxy;\n            }\n          }\n        } // If no hosted proxy found, still need to return a proxy.\n        // This case always happens in toolbox dataZoom, where axes are all hosted by\n        // other dataZooms.\n\n\n        return firstProxy;\n      };\n\n      DataZoomModel.prototype.getRangePropMode = function () {\n        return this._rangePropMode.slice();\n      };\n\n      DataZoomModel.prototype.getOrient = function () {\n        if (\"development\" !== 'production') {\n          // Should not be called before initialized.\n          assert(this._orient);\n        }\n\n        return this._orient;\n      };\n\n      DataZoomModel.type = 'dataZoom';\n      DataZoomModel.dependencies = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series', 'toolbox'];\n      DataZoomModel.defaultOption = {\n        // zlevel: 0,\n        z: 4,\n        filterMode: 'filter',\n        start: 0,\n        end: 100\n      };\n      return DataZoomModel;\n    }(ComponentModel);\n    /**\n     * Retrieve those raw params from option, which will be cached separately,\n     * because they will be overwritten by normalized/calculated values in the main\n     * process.\n     */\n\n\n    function retrieveRawOption(option) {\n      var ret = {};\n      each(['start', 'end', 'startValue', 'endValue', 'throttle'], function (name) {\n        option.hasOwnProperty(name) && (ret[name] = option[name]);\n      });\n      return ret;\n    }\n\n    var SelectDataZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(SelectDataZoomModel, _super);\n\n      function SelectDataZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SelectDataZoomModel.type;\n        return _this;\n      }\n\n      SelectDataZoomModel.type = 'dataZoom.select';\n      return SelectDataZoomModel;\n    }(DataZoomModel);\n\n    var DataZoomView =\n    /** @class */\n    function (_super) {\n      __extends(DataZoomView, _super);\n\n      function DataZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = DataZoomView.type;\n        return _this;\n      }\n\n      DataZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) {\n        this.dataZoomModel = dataZoomModel;\n        this.ecModel = ecModel;\n        this.api = api;\n      };\n\n      DataZoomView.type = 'dataZoom';\n      return DataZoomView;\n    }(ComponentView);\n\n    var SelectDataZoomView =\n    /** @class */\n    function (_super) {\n      __extends(SelectDataZoomView, _super);\n\n      function SelectDataZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SelectDataZoomView.type;\n        return _this;\n      }\n\n      SelectDataZoomView.type = 'dataZoom.select';\n      return SelectDataZoomView;\n    }(DataZoomView);\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * Calculate slider move result.\n     * Usage:\n     * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as\n     * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`.\n     * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`.\n     *\n     * @param delta Move length.\n     * @param handleEnds handleEnds[0] can be bigger then handleEnds[1].\n     *              handleEnds will be modified in this method.\n     * @param extent handleEnds is restricted by extent.\n     *              extent[0] should less or equals than extent[1].\n     * @param handleIndex Can be 'all', means that both move the two handleEnds.\n     * @param minSpan The range of dataZoom can not be smaller than that.\n     *              If not set, handle0 and cross handle1. If set as a non-negative\n     *              number (including `0`), handles will push each other when reaching\n     *              the minSpan.\n     * @param maxSpan The range of dataZoom can not be larger than that.\n     * @return The input handleEnds.\n     */\n    function sliderMove(delta, handleEnds, extent, handleIndex, minSpan, maxSpan) {\n      delta = delta || 0;\n      var extentSpan = extent[1] - extent[0]; // Notice maxSpan and minSpan can be null/undefined.\n\n      if (minSpan != null) {\n        minSpan = restrict(minSpan, [0, extentSpan]);\n      }\n\n      if (maxSpan != null) {\n        maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0);\n      }\n\n      if (handleIndex === 'all') {\n        var handleSpan = Math.abs(handleEnds[1] - handleEnds[0]);\n        handleSpan = restrict(handleSpan, [0, extentSpan]);\n        minSpan = maxSpan = restrict(handleSpan, [minSpan, maxSpan]);\n        handleIndex = 0;\n      }\n\n      handleEnds[0] = restrict(handleEnds[0], extent);\n      handleEnds[1] = restrict(handleEnds[1], extent);\n      var originalDistSign = getSpanSign(handleEnds, handleIndex);\n      handleEnds[handleIndex] += delta; // Restrict in extent.\n\n      var extentMinSpan = minSpan || 0;\n      var realExtent = extent.slice();\n      originalDistSign.sign < 0 ? realExtent[0] += extentMinSpan : realExtent[1] -= extentMinSpan;\n      handleEnds[handleIndex] = restrict(handleEnds[handleIndex], realExtent); // Expand span.\n\n      var currDistSign;\n      currDistSign = getSpanSign(handleEnds, handleIndex);\n\n      if (minSpan != null && (currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan)) {\n        // If minSpan exists, 'cross' is forbidden.\n        handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan;\n      } // Shrink span.\n\n\n      currDistSign = getSpanSign(handleEnds, handleIndex);\n\n      if (maxSpan != null && currDistSign.span > maxSpan) {\n        handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan;\n      }\n\n      return handleEnds;\n    }\n\n    function getSpanSign(handleEnds, handleIndex) {\n      var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex]; // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0]\n      // is at left of handleEnds[1] for non-cross case.\n\n      return {\n        span: Math.abs(dist),\n        sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1\n      };\n    }\n\n    function restrict(value, extend) {\n      return Math.min(extend[1] != null ? extend[1] : Infinity, Math.max(extend[0] != null ? extend[0] : -Infinity, value));\n    }\n\n    var each$4 = each;\n    var asc$1 = asc;\n    /**\n     * Operate single axis.\n     * One axis can only operated by one axis operator.\n     * Different dataZoomModels may be defined to operate the same axis.\n     * (i.e. 'inside' data zoom and 'slider' data zoom components)\n     * So dataZoomModels share one axisProxy in that case.\n     */\n\n    var AxisProxy =\n    /** @class */\n    function () {\n      function AxisProxy(dimName, axisIndex, dataZoomModel, ecModel) {\n        this._dimName = dimName;\n        this._axisIndex = axisIndex;\n        this.ecModel = ecModel;\n        this._dataZoomModel = dataZoomModel; // /**\n        //  * @readOnly\n        //  * @private\n        //  */\n        // this.hasSeriesStacked;\n      }\n      /**\n       * Whether the axisProxy is hosted by dataZoomModel.\n       */\n\n\n      AxisProxy.prototype.hostedBy = function (dataZoomModel) {\n        return this._dataZoomModel === dataZoomModel;\n      };\n      /**\n       * @return Value can only be NaN or finite value.\n       */\n\n\n      AxisProxy.prototype.getDataValueWindow = function () {\n        return this._valueWindow.slice();\n      };\n      /**\n       * @return {Array.<number>}\n       */\n\n\n      AxisProxy.prototype.getDataPercentWindow = function () {\n        return this._percentWindow.slice();\n      };\n\n      AxisProxy.prototype.getTargetSeriesModels = function () {\n        var seriesModels = [];\n        this.ecModel.eachSeries(function (seriesModel) {\n          if (isCoordSupported(seriesModel)) {\n            var axisMainType = getAxisMainType(this._dimName);\n            var axisModel = seriesModel.getReferringComponents(axisMainType, SINGLE_REFERRING).models[0];\n\n            if (axisModel && this._axisIndex === axisModel.componentIndex) {\n              seriesModels.push(seriesModel);\n            }\n          }\n        }, this);\n        return seriesModels;\n      };\n\n      AxisProxy.prototype.getAxisModel = function () {\n        return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex);\n      };\n\n      AxisProxy.prototype.getMinMaxSpan = function () {\n        return clone(this._minMaxSpan);\n      };\n      /**\n       * Only calculate by given range and this._dataExtent, do not change anything.\n       */\n\n\n      AxisProxy.prototype.calculateDataWindow = function (opt) {\n        var dataExtent = this._dataExtent;\n        var axisModel = this.getAxisModel();\n        var scale = axisModel.axis.scale;\n\n        var rangePropMode = this._dataZoomModel.getRangePropMode();\n\n        var percentExtent = [0, 100];\n        var percentWindow = [];\n        var valueWindow = [];\n        var hasPropModeValue;\n        each$4(['start', 'end'], function (prop, idx) {\n          var boundPercent = opt[prop];\n          var boundValue = opt[prop + 'Value']; // Notice: dataZoom is based either on `percentProp` ('start', 'end') or\n          // on `valueProp` ('startValue', 'endValue'). (They are based on the data extent\n          // but not min/max of axis, which will be calculated by data window then).\n          // The former one is suitable for cases that a dataZoom component controls multiple\n          // axes with different unit or extent, and the latter one is suitable for accurate\n          // zoom by pixel (e.g., in dataZoomSelect).\n          // we use `getRangePropMode()` to mark which prop is used. `rangePropMode` is updated\n          // only when setOption or dispatchAction, otherwise it remains its original value.\n          // (Why not only record `percentProp` and always map to `valueProp`? Because\n          // the map `valueProp` -> `percentProp` -> `valueProp` probably not the original\n          // `valueProp`. consider two axes constrolled by one dataZoom. They have different\n          // data extent. All of values that are overflow the `dataExtent` will be calculated\n          // to percent '100%').\n\n          if (rangePropMode[idx] === 'percent') {\n            boundPercent == null && (boundPercent = percentExtent[idx]); // Use scale.parse to math round for category or time axis.\n\n            boundValue = scale.parse(linearMap(boundPercent, percentExtent, dataExtent));\n          } else {\n            hasPropModeValue = true;\n            boundValue = boundValue == null ? dataExtent[idx] : scale.parse(boundValue); // Calculating `percent` from `value` may be not accurate, because\n            // This calculation can not be inversed, because all of values that\n            // are overflow the `dataExtent` will be calculated to percent '100%'\n\n            boundPercent = linearMap(boundValue, dataExtent, percentExtent);\n          } // valueWindow[idx] = round(boundValue);\n          // percentWindow[idx] = round(boundPercent);\n          // fallback to extent start/end when parsed value or percent is invalid\n\n\n          valueWindow[idx] = boundValue == null || isNaN(boundValue) ? dataExtent[idx] : boundValue;\n          percentWindow[idx] = boundPercent == null || isNaN(boundPercent) ? percentExtent[idx] : boundPercent;\n        });\n        asc$1(valueWindow);\n        asc$1(percentWindow); // The windows from user calling of `dispatchAction` might be out of the extent,\n        // or do not obey the `min/maxSpan`, `min/maxValueSpan`. But we don't restrict window\n        // by `zoomLock` here, because we see `zoomLock` just as a interaction constraint,\n        // where API is able to initialize/modify the window size even though `zoomLock`\n        // specified.\n\n        var spans = this._minMaxSpan;\n        hasPropModeValue ? restrictSet(valueWindow, percentWindow, dataExtent, percentExtent, false) : restrictSet(percentWindow, valueWindow, percentExtent, dataExtent, true);\n\n        function restrictSet(fromWindow, toWindow, fromExtent, toExtent, toValue) {\n          var suffix = toValue ? 'Span' : 'ValueSpan';\n          sliderMove(0, fromWindow, fromExtent, 'all', spans['min' + suffix], spans['max' + suffix]);\n\n          for (var i = 0; i < 2; i++) {\n            toWindow[i] = linearMap(fromWindow[i], fromExtent, toExtent, true);\n            toValue && (toWindow[i] = scale.parse(toWindow[i]));\n          }\n        }\n\n        return {\n          valueWindow: valueWindow,\n          percentWindow: percentWindow\n        };\n      };\n      /**\n       * Notice: reset should not be called before series.restoreData() is called,\n       * so it is recommended to be called in \"process stage\" but not \"model init\n       * stage\".\n       */\n\n\n      AxisProxy.prototype.reset = function (dataZoomModel) {\n        if (dataZoomModel !== this._dataZoomModel) {\n          return;\n        }\n\n        var targetSeries = this.getTargetSeriesModels(); // Culculate data window and data extent, and record them.\n\n        this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries); // `calculateDataWindow` uses min/maxSpan.\n\n        this._updateMinMaxSpan();\n\n        var dataWindow = this.calculateDataWindow(dataZoomModel.settledOption);\n        this._valueWindow = dataWindow.valueWindow;\n        this._percentWindow = dataWindow.percentWindow; // Update axis setting then.\n\n        this._setAxisModel();\n      };\n\n      AxisProxy.prototype.filterData = function (dataZoomModel, api) {\n        if (dataZoomModel !== this._dataZoomModel) {\n          return;\n        }\n\n        var axisDim = this._dimName;\n        var seriesModels = this.getTargetSeriesModels();\n        var filterMode = dataZoomModel.get('filterMode');\n        var valueWindow = this._valueWindow;\n\n        if (filterMode === 'none') {\n          return;\n        } // FIXME\n        // Toolbox may has dataZoom injected. And if there are stacked bar chart\n        // with NaN data, NaN will be filtered and stack will be wrong.\n        // So we need to force the mode to be set empty.\n        // In fect, it is not a big deal that do not support filterMode-'filter'\n        // when using toolbox#dataZoom, utill tooltip#dataZoom support \"single axis\n        // selection\" some day, which might need \"adapt to data extent on the\n        // otherAxis\", which is disabled by filterMode-'empty'.\n        // But currently, stack has been fixed to based on value but not index,\n        // so this is not an issue any more.\n        // let otherAxisModel = this.getOtherAxisModel();\n        // if (dataZoomModel.get('$fromToolbox')\n        //     && otherAxisModel\n        //     && otherAxisModel.hasSeriesStacked\n        // ) {\n        //     filterMode = 'empty';\n        // }\n        // TODO\n        // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet.\n\n\n        each$4(seriesModels, function (seriesModel) {\n          var seriesData = seriesModel.getData();\n          var dataDims = seriesData.mapDimensionsAll(axisDim);\n\n          if (!dataDims.length) {\n            return;\n          }\n\n          if (filterMode === 'weakFilter') {\n            var store_1 = seriesData.getStore();\n            var dataDimIndices_1 = map(dataDims, function (dim) {\n              return seriesData.getDimensionIndex(dim);\n            }, seriesData);\n            seriesData.filterSelf(function (dataIndex) {\n              var leftOut;\n              var rightOut;\n              var hasValue;\n\n              for (var i = 0; i < dataDims.length; i++) {\n                var value = store_1.get(dataDimIndices_1[i], dataIndex);\n                var thisHasValue = !isNaN(value);\n                var thisLeftOut = value < valueWindow[0];\n                var thisRightOut = value > valueWindow[1];\n\n                if (thisHasValue && !thisLeftOut && !thisRightOut) {\n                  return true;\n                }\n\n                thisHasValue && (hasValue = true);\n                thisLeftOut && (leftOut = true);\n                thisRightOut && (rightOut = true);\n              } // If both left out and right out, do not filter.\n\n\n              return hasValue && leftOut && rightOut;\n            });\n          } else {\n            each$4(dataDims, function (dim) {\n              if (filterMode === 'empty') {\n                seriesModel.setData(seriesData = seriesData.map(dim, function (value) {\n                  return !isInWindow(value) ? NaN : value;\n                }));\n              } else {\n                var range = {};\n                range[dim] = valueWindow; // console.time('select');\n\n                seriesData.selectRange(range); // console.timeEnd('select');\n              }\n            });\n          }\n\n          each$4(dataDims, function (dim) {\n            seriesData.setApproximateExtent(valueWindow, dim);\n          });\n        });\n\n        function isInWindow(value) {\n          return value >= valueWindow[0] && value <= valueWindow[1];\n        }\n      };\n\n      AxisProxy.prototype._updateMinMaxSpan = function () {\n        var minMaxSpan = this._minMaxSpan = {};\n        var dataZoomModel = this._dataZoomModel;\n        var dataExtent = this._dataExtent;\n        each$4(['min', 'max'], function (minMax) {\n          var percentSpan = dataZoomModel.get(minMax + 'Span');\n          var valueSpan = dataZoomModel.get(minMax + 'ValueSpan');\n          valueSpan != null && (valueSpan = this.getAxisModel().axis.scale.parse(valueSpan)); // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan\n\n          if (valueSpan != null) {\n            percentSpan = linearMap(dataExtent[0] + valueSpan, dataExtent, [0, 100], true);\n          } else if (percentSpan != null) {\n            valueSpan = linearMap(percentSpan, [0, 100], dataExtent, true) - dataExtent[0];\n          }\n\n          minMaxSpan[minMax + 'Span'] = percentSpan;\n          minMaxSpan[minMax + 'ValueSpan'] = valueSpan;\n        }, this);\n      };\n\n      AxisProxy.prototype._setAxisModel = function () {\n        var axisModel = this.getAxisModel();\n        var percentWindow = this._percentWindow;\n        var valueWindow = this._valueWindow;\n\n        if (!percentWindow) {\n          return;\n        } // [0, 500]: arbitrary value, guess axis extent.\n\n\n        var precision = getPixelPrecision(valueWindow, [0, 500]);\n        precision = Math.min(precision, 20); // For value axis, if min/max/scale are not set, we just use the extent obtained\n        // by series data, which may be a little different from the extent calculated by\n        // `axisHelper.getScaleExtent`. But the different just affects the experience a\n        // little when zooming. So it will not be fixed until some users require it strongly.\n\n        var rawExtentInfo = axisModel.axis.scale.rawExtentInfo;\n\n        if (percentWindow[0] !== 0) {\n          rawExtentInfo.setDeterminedMinMax('min', +valueWindow[0].toFixed(precision));\n        }\n\n        if (percentWindow[1] !== 100) {\n          rawExtentInfo.setDeterminedMinMax('max', +valueWindow[1].toFixed(precision));\n        }\n\n        rawExtentInfo.freeze();\n      };\n\n      return AxisProxy;\n    }();\n\n    function calculateDataExtent(axisProxy, axisDim, seriesModels) {\n      var dataExtent = [Infinity, -Infinity];\n      each$4(seriesModels, function (seriesModel) {\n        unionAxisExtentFromData(dataExtent, seriesModel.getData(), axisDim);\n      }); // It is important to get \"consistent\" extent when more then one axes is\n      // controlled by a `dataZoom`, otherwise those axes will not be synchronized\n      // when zooming. But it is difficult to know what is \"consistent\", considering\n      // axes have different type or even different meanings (For example, two\n      // time axes are used to compare data of the same date in different years).\n      // So basically dataZoom just obtains extent by series.data (in category axis\n      // extent can be obtained from axis.data).\n      // Nevertheless, user can set min/max/scale on axes to make extent of axes\n      // consistent.\n\n      var axisModel = axisProxy.getAxisModel();\n      var rawExtentResult = ensureScaleRawExtentInfo(axisModel.axis.scale, axisModel, dataExtent).calculate();\n      return [rawExtentResult.min, rawExtentResult.max];\n    }\n\n    var dataZoomProcessor = {\n      // `dataZoomProcessor` will only be performed in needed series. Consider if\n      // there is a line series and a pie series, it is better not to update the\n      // line series if only pie series is needed to be updated.\n      getTargetSeries: function (ecModel) {\n        function eachAxisModel(cb) {\n          ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n            dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n              var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n              cb(axisDim, axisIndex, axisModel, dataZoomModel);\n            });\n          });\n        } // FIXME: it brings side-effect to `getTargetSeries`.\n        // Prepare axis proxies.\n\n\n        eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {\n          // dispose all last axis proxy, in case that some axis are deleted.\n          axisModel.__dzAxisProxy = null;\n        });\n        var proxyList = [];\n        eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {\n          // Different dataZooms may constrol the same axis. In that case,\n          // an axisProxy serves both of them.\n          if (!axisModel.__dzAxisProxy) {\n            // Use the first dataZoomModel as the main model of axisProxy.\n            axisModel.__dzAxisProxy = new AxisProxy(axisDim, axisIndex, dataZoomModel, ecModel);\n            proxyList.push(axisModel.__dzAxisProxy);\n          }\n        });\n        var seriesModelMap = createHashMap();\n        each(proxyList, function (axisProxy) {\n          each(axisProxy.getTargetSeriesModels(), function (seriesModel) {\n            seriesModelMap.set(seriesModel.uid, seriesModel);\n          });\n        });\n        return seriesModelMap;\n      },\n      // Consider appendData, where filter should be performed. Because data process is\n      // in block mode currently, it is not need to worry about that the overallProgress\n      // execute every frame.\n      overallReset: function (ecModel, api) {\n        ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n          // We calculate window and reset axis here but not in model\n          // init stage and not after action dispatch handler, because\n          // reset should be called after seriesData.restoreData.\n          dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n            dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel);\n          }); // Caution: data zoom filtering is order sensitive when using\n          // percent range and no min/max/scale set on axis.\n          // For example, we have dataZoom definition:\n          // [\n          //      {xAxisIndex: 0, start: 30, end: 70},\n          //      {yAxisIndex: 0, start: 20, end: 80}\n          // ]\n          // In this case, [20, 80] of y-dataZoom should be based on data\n          // that have filtered by x-dataZoom using range of [30, 70],\n          // but should not be based on full raw data. Thus sliding\n          // x-dataZoom will change both ranges of xAxis and yAxis,\n          // while sliding y-dataZoom will only change the range of yAxis.\n          // So we should filter x-axis after reset x-axis immediately,\n          // and then reset y-axis and filter y-axis.\n\n          dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n            dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api);\n          });\n        });\n        ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n          // Fullfill all of the range props so that user\n          // is able to get them from chart.getOption().\n          var axisProxy = dataZoomModel.findRepresentativeAxisProxy();\n\n          if (axisProxy) {\n            var percentRange = axisProxy.getDataPercentWindow();\n            var valueRange = axisProxy.getDataValueWindow();\n            dataZoomModel.setCalculatedRange({\n              start: percentRange[0],\n              end: percentRange[1],\n              startValue: valueRange[0],\n              endValue: valueRange[1]\n            });\n          }\n        });\n      }\n    };\n\n    function installDataZoomAction(registers) {\n      registers.registerAction('dataZoom', function (payload, ecModel) {\n        var effectedModels = findEffectedDataZooms(ecModel, payload);\n        each(effectedModels, function (dataZoomModel) {\n          dataZoomModel.setRawRange({\n            start: payload.start,\n            end: payload.end,\n            startValue: payload.startValue,\n            endValue: payload.endValue\n          });\n        });\n      });\n    }\n\n    var installed = false;\n    function installCommon(registers) {\n      if (installed) {\n        return;\n      }\n\n      installed = true;\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, dataZoomProcessor);\n      installDataZoomAction(registers);\n      registers.registerSubTypeDefaulter('dataZoom', function () {\n        // Default 'slider' when no type specified.\n        return 'slider';\n      });\n    }\n\n    function install$a(registers) {\n      registers.registerComponentModel(SelectDataZoomModel);\n      registers.registerComponentView(SelectDataZoomView);\n      installCommon(registers);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    var ToolboxFeature =\n    /** @class */\n    function () {\n      function ToolboxFeature() {}\n\n      return ToolboxFeature;\n    }();\n    var features = {};\n    function registerFeature(name, ctor) {\n      features[name] = ctor;\n    }\n    function getFeature(name) {\n      return features[name];\n    }\n\n    var ToolboxModel =\n    /** @class */\n    function (_super) {\n      __extends(ToolboxModel, _super);\n\n      function ToolboxModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ToolboxModel.type;\n        return _this;\n      }\n\n      ToolboxModel.prototype.optionUpdated = function () {\n        _super.prototype.optionUpdated.apply(this, arguments);\n\n        var ecModel = this.ecModel;\n        each(this.option.feature, function (featureOpt, featureName) {\n          var Feature = getFeature(featureName);\n\n          if (Feature) {\n            if (Feature.getDefaultOption) {\n              Feature.defaultOption = Feature.getDefaultOption(ecModel);\n            }\n\n            merge(featureOpt, Feature.defaultOption);\n          }\n        });\n      };\n\n      ToolboxModel.type = 'toolbox';\n      ToolboxModel.layoutMode = {\n        type: 'box',\n        ignoreSize: true\n      };\n      ToolboxModel.defaultOption = {\n        show: true,\n        z: 6,\n        // zlevel: 0,\n        orient: 'horizontal',\n        left: 'right',\n        top: 'top',\n        // right\n        // bottom\n        backgroundColor: 'transparent',\n        borderColor: '#ccc',\n        borderRadius: 0,\n        borderWidth: 0,\n        padding: 5,\n        itemSize: 15,\n        itemGap: 8,\n        showTitle: true,\n        iconStyle: {\n          borderColor: '#666',\n          color: 'none'\n        },\n        emphasis: {\n          iconStyle: {\n            borderColor: '#3E98C5'\n          }\n        },\n        // textStyle: {},\n        // feature\n        tooltip: {\n          show: false,\n          position: 'bottom'\n        }\n      };\n      return ToolboxModel;\n    }(ComponentModel);\n\n    /**\n     * Layout list like component.\n     * It will box layout each items in group of component and then position the whole group in the viewport\n     * @param {module:zrender/group/Group} group\n     * @param {module:echarts/model/Component} componentModel\n     * @param {module:echarts/ExtensionAPI}\n     */\n\n    function layout$2(group, componentModel, api) {\n      var boxLayoutParams = componentModel.getBoxLayoutParams();\n      var padding = componentModel.get('padding');\n      var viewportSize = {\n        width: api.getWidth(),\n        height: api.getHeight()\n      };\n      var rect = getLayoutRect(boxLayoutParams, viewportSize, padding);\n      box(componentModel.get('orient'), group, componentModel.get('itemGap'), rect.width, rect.height);\n      positionElement(group, boxLayoutParams, viewportSize, padding);\n    }\n    function makeBackground(rect, componentModel) {\n      var padding = normalizeCssArray$1(componentModel.get('padding'));\n      var style = componentModel.getItemStyle(['color', 'opacity']);\n      style.fill = componentModel.get('backgroundColor');\n      rect = new Rect({\n        shape: {\n          x: rect.x - padding[3],\n          y: rect.y - padding[0],\n          width: rect.width + padding[1] + padding[3],\n          height: rect.height + padding[0] + padding[2],\n          r: componentModel.get('borderRadius')\n        },\n        style: style,\n        silent: true,\n        z2: -1\n      }); // FIXME\n      // `subPixelOptimizeRect` may bring some gap between edge of viewpart\n      // and background rect when setting like `left: 0`, `top: 0`.\n      // graphic.subPixelOptimizeRect(rect);\n\n      return rect;\n    }\n\n    var ToolboxView =\n    /** @class */\n    function (_super) {\n      __extends(ToolboxView, _super);\n\n      function ToolboxView() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      ToolboxView.prototype.render = function (toolboxModel, ecModel, api, payload) {\n        var group = this.group;\n        group.removeAll();\n\n        if (!toolboxModel.get('show')) {\n          return;\n        }\n\n        var itemSize = +toolboxModel.get('itemSize');\n        var isVertical = toolboxModel.get('orient') === 'vertical';\n        var featureOpts = toolboxModel.get('feature') || {};\n        var features = this._features || (this._features = {});\n        var featureNames = [];\n        each(featureOpts, function (opt, name) {\n          featureNames.push(name);\n        });\n        new DataDiffer(this._featureNames || [], featureNames).add(processFeature).update(processFeature).remove(curry(processFeature, null)).execute(); // Keep for diff.\n\n        this._featureNames = featureNames;\n\n        function processFeature(newIndex, oldIndex) {\n          var featureName = featureNames[newIndex];\n          var oldName = featureNames[oldIndex];\n          var featureOpt = featureOpts[featureName];\n          var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel);\n          var feature; // FIX#11236, merge feature title from MagicType newOption. TODO: consider seriesIndex ?\n\n          if (payload && payload.newTitle != null && payload.featureName === featureName) {\n            featureOpt.title = payload.newTitle;\n          }\n\n          if (featureName && !oldName) {\n            // Create\n            if (isUserFeatureName(featureName)) {\n              feature = {\n                onclick: featureModel.option.onclick,\n                featureName: featureName\n              };\n            } else {\n              var Feature = getFeature(featureName);\n\n              if (!Feature) {\n                return;\n              }\n\n              feature = new Feature();\n            }\n\n            features[featureName] = feature;\n          } else {\n            feature = features[oldName]; // If feature does not exsit.\n\n            if (!feature) {\n              return;\n            }\n          }\n\n          feature.uid = getUID('toolbox-feature');\n          feature.model = featureModel;\n          feature.ecModel = ecModel;\n          feature.api = api;\n          var isToolboxFeature = feature instanceof ToolboxFeature;\n\n          if (!featureName && oldName) {\n            isToolboxFeature && feature.dispose && feature.dispose(ecModel, api);\n            return;\n          }\n\n          if (!featureModel.get('show') || isToolboxFeature && feature.unusable) {\n            isToolboxFeature && feature.remove && feature.remove(ecModel, api);\n            return;\n          }\n\n          createIconPaths(featureModel, feature, featureName);\n\n          featureModel.setIconStatus = function (iconName, status) {\n            var option = this.option;\n            var iconPaths = this.iconPaths;\n            option.iconStatus = option.iconStatus || {};\n            option.iconStatus[iconName] = status;\n\n            if (iconPaths[iconName]) {\n              (status === 'emphasis' ? enterEmphasis : leaveEmphasis)(iconPaths[iconName]);\n            }\n          };\n\n          if (feature instanceof ToolboxFeature) {\n            if (feature.render) {\n              feature.render(featureModel, ecModel, api, payload);\n            }\n          }\n        }\n\n        function createIconPaths(featureModel, feature, featureName) {\n          var iconStyleModel = featureModel.getModel('iconStyle');\n          var iconStyleEmphasisModel = featureModel.getModel(['emphasis', 'iconStyle']); // If one feature has mutiple icon. they are orginaized as\n          // {\n          //     icon: {\n          //         foo: '',\n          //         bar: ''\n          //     },\n          //     title: {\n          //         foo: '',\n          //         bar: ''\n          //     }\n          // }\n\n          var icons = feature instanceof ToolboxFeature && feature.getIcons ? feature.getIcons() : featureModel.get('icon');\n          var titles = featureModel.get('title') || {};\n          var iconsMap;\n          var titlesMap;\n\n          if (isString(icons)) {\n            iconsMap = {};\n            iconsMap[featureName] = icons;\n          } else {\n            iconsMap = icons;\n          }\n\n          if (isString(titles)) {\n            titlesMap = {};\n            titlesMap[featureName] = titles;\n          } else {\n            titlesMap = titles;\n          }\n\n          var iconPaths = featureModel.iconPaths = {};\n          each(iconsMap, function (iconStr, iconName) {\n            var path = createIcon(iconStr, {}, {\n              x: -itemSize / 2,\n              y: -itemSize / 2,\n              width: itemSize,\n              height: itemSize\n            }); // TODO handling image\n\n            path.setStyle(iconStyleModel.getItemStyle());\n            var pathEmphasisState = path.ensureState('emphasis');\n            pathEmphasisState.style = iconStyleEmphasisModel.getItemStyle(); // Text position calculation\n\n            var textContent = new ZRText({\n              style: {\n                text: titlesMap[iconName],\n                align: iconStyleEmphasisModel.get('textAlign'),\n                borderRadius: iconStyleEmphasisModel.get('textBorderRadius'),\n                padding: iconStyleEmphasisModel.get('textPadding'),\n                fill: null\n              },\n              ignore: true\n            });\n            path.setTextContent(textContent);\n            setTooltipConfig({\n              el: path,\n              componentModel: toolboxModel,\n              itemName: iconName,\n              formatterParamsExtra: {\n                title: titlesMap[iconName]\n              }\n            });\n            path.__title = titlesMap[iconName];\n            path.on('mouseover', function () {\n              // Should not reuse above hoverStyle, which might be modified.\n              var hoverStyle = iconStyleEmphasisModel.getItemStyle();\n              var defaultTextPosition = isVertical ? toolboxModel.get('right') == null && toolboxModel.get('left') !== 'right' ? 'right' : 'left' : toolboxModel.get('bottom') == null && toolboxModel.get('top') !== 'bottom' ? 'bottom' : 'top';\n              textContent.setStyle({\n                fill: iconStyleEmphasisModel.get('textFill') || hoverStyle.fill || hoverStyle.stroke || '#000',\n                backgroundColor: iconStyleEmphasisModel.get('textBackgroundColor')\n              });\n              path.setTextConfig({\n                position: iconStyleEmphasisModel.get('textPosition') || defaultTextPosition\n              });\n              textContent.ignore = !toolboxModel.get('showTitle'); // Use enterEmphasis and leaveEmphasis provide by ec.\n              // There are flags managed by the echarts.\n\n              api.enterEmphasis(this);\n            }).on('mouseout', function () {\n              if (featureModel.get(['iconStatus', iconName]) !== 'emphasis') {\n                api.leaveEmphasis(this);\n              }\n\n              textContent.hide();\n            });\n            (featureModel.get(['iconStatus', iconName]) === 'emphasis' ? enterEmphasis : leaveEmphasis)(path);\n            group.add(path);\n            path.on('click', bind(feature.onclick, feature, ecModel, api, iconName));\n            iconPaths[iconName] = path;\n          });\n        }\n\n        layout$2(group, toolboxModel, api); // Render background after group is layout\n        // FIXME\n\n        group.add(makeBackground(group.getBoundingRect(), toolboxModel)); // Adjust icon title positions to avoid them out of screen\n\n        isVertical || group.eachChild(function (icon) {\n          var titleText = icon.__title; // const hoverStyle = icon.hoverStyle;\n          // TODO simplify code?\n\n          var emphasisState = icon.ensureState('emphasis');\n          var emphasisTextConfig = emphasisState.textConfig || (emphasisState.textConfig = {});\n          var textContent = icon.getTextContent();\n          var emphasisTextState = textContent && textContent.ensureState('emphasis'); // May be background element\n\n          if (emphasisTextState && !isFunction(emphasisTextState) && titleText) {\n            var emphasisTextStyle = emphasisTextState.style || (emphasisTextState.style = {});\n            var rect = getBoundingRect(titleText, ZRText.makeFont(emphasisTextStyle));\n            var offsetX = icon.x + group.x;\n            var offsetY = icon.y + group.y + itemSize;\n            var needPutOnTop = false;\n\n            if (offsetY + rect.height > api.getHeight()) {\n              emphasisTextConfig.position = 'top';\n              needPutOnTop = true;\n            }\n\n            var topOffset = needPutOnTop ? -5 - rect.height : itemSize + 10;\n\n            if (offsetX + rect.width / 2 > api.getWidth()) {\n              emphasisTextConfig.position = ['100%', topOffset];\n              emphasisTextStyle.align = 'right';\n            } else if (offsetX - rect.width / 2 < 0) {\n              emphasisTextConfig.position = [0, topOffset];\n              emphasisTextStyle.align = 'left';\n            }\n          }\n        });\n      };\n\n      ToolboxView.prototype.updateView = function (toolboxModel, ecModel, api, payload) {\n        each(this._features, function (feature) {\n          feature instanceof ToolboxFeature && feature.updateView && feature.updateView(feature.model, ecModel, api, payload);\n        });\n      }; // updateLayout(toolboxModel, ecModel, api, payload) {\n      //     zrUtil.each(this._features, function (feature) {\n      //         feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload);\n      //     });\n      // },\n\n\n      ToolboxView.prototype.remove = function (ecModel, api) {\n        each(this._features, function (feature) {\n          feature instanceof ToolboxFeature && feature.remove && feature.remove(ecModel, api);\n        });\n        this.group.removeAll();\n      };\n\n      ToolboxView.prototype.dispose = function (ecModel, api) {\n        each(this._features, function (feature) {\n          feature instanceof ToolboxFeature && feature.dispose && feature.dispose(ecModel, api);\n        });\n      };\n\n      ToolboxView.type = 'toolbox';\n      return ToolboxView;\n    }(ComponentView);\n\n    function isUserFeatureName(featureName) {\n      return featureName.indexOf('my') === 0;\n    }\n\n    /* global window, document */\n\n    var SaveAsImage =\n    /** @class */\n    function (_super) {\n      __extends(SaveAsImage, _super);\n\n      function SaveAsImage() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      SaveAsImage.prototype.onclick = function (ecModel, api) {\n        var model = this.model;\n        var title = model.get('name') || ecModel.get('title.0.text') || 'echarts';\n        var isSvg = api.getZr().painter.getType() === 'svg';\n        var type = isSvg ? 'svg' : model.get('type', true) || 'png';\n        var url = api.getConnectedDataURL({\n          type: type,\n          backgroundColor: model.get('backgroundColor', true) || ecModel.get('backgroundColor') || '#fff',\n          connectedBackgroundColor: model.get('connectedBackgroundColor'),\n          excludeComponents: model.get('excludeComponents'),\n          pixelRatio: model.get('pixelRatio')\n        });\n        var browser = env.browser; // Chrome, Firefox, New Edge\n\n        if (isFunction(MouseEvent) && (browser.newEdge || !browser.ie && !browser.edge)) {\n          var $a = document.createElement('a');\n          $a.download = title + '.' + type;\n          $a.target = '_blank';\n          $a.href = url;\n          var evt = new MouseEvent('click', {\n            // some micro front-end framework， window maybe is a Proxy\n            view: document.defaultView,\n            bubbles: true,\n            cancelable: false\n          });\n          $a.dispatchEvent(evt);\n        } // IE or old Edge\n        else {\n            // @ts-ignore\n            if (window.navigator.msSaveOrOpenBlob || isSvg) {\n              var parts = url.split(','); // data:[<mime type>][;charset=<charset>][;base64],<encoded data>\n\n              var base64Encoded = parts[0].indexOf('base64') > -1;\n              var bstr = isSvg // should decode the svg data uri first\n              ? decodeURIComponent(parts[1]) : parts[1]; // only `atob` when the data uri is encoded with base64\n              // otherwise, like `svg` data uri exported by zrender,\n              // there will be an error, for it's not encoded with base64.\n              // (just a url-encoded string through `encodeURIComponent`)\n\n              base64Encoded && (bstr = window.atob(bstr));\n              var filename = title + '.' + type; // @ts-ignore\n\n              if (window.navigator.msSaveOrOpenBlob) {\n                var n = bstr.length;\n                var u8arr = new Uint8Array(n);\n\n                while (n--) {\n                  u8arr[n] = bstr.charCodeAt(n);\n                }\n\n                var blob = new Blob([u8arr]); // @ts-ignore\n\n                window.navigator.msSaveOrOpenBlob(blob, filename);\n              } else {\n                var frame = document.createElement('iframe');\n                document.body.appendChild(frame);\n                var cw = frame.contentWindow;\n                var doc = cw.document;\n                doc.open('image/svg+xml', 'replace');\n                doc.write(bstr);\n                doc.close();\n                cw.focus();\n                doc.execCommand('SaveAs', true, filename);\n                document.body.removeChild(frame);\n              }\n            } else {\n              var lang = model.get('lang');\n              var html = '' + '<body style=\"margin:0;\">' + '<img src=\"' + url + '\" style=\"max-width:100%;\" title=\"' + (lang && lang[0] || '') + '\" />' + '</body>';\n              var tab = window.open();\n              tab.document.write(html);\n              tab.document.title = title;\n            }\n          }\n      };\n\n      SaveAsImage.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0',\n          title: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'title']),\n          type: 'png',\n          // Default use option.backgroundColor\n          // backgroundColor: '#fff',\n          connectedBackgroundColor: '#fff',\n          name: '',\n          excludeComponents: ['toolbox'],\n          // use current pixel ratio of device by default\n          // pixelRatio: 1,\n          lang: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'lang'])\n        };\n        return defaultOption;\n      };\n\n      return SaveAsImage;\n    }(ToolboxFeature);\n\n    var INNER_STACK_KEYWORD = '__ec_magicType_stack__';\n    var radioTypes = [['line', 'bar'], ['stack']];\n\n    var MagicType =\n    /** @class */\n    function (_super) {\n      __extends(MagicType, _super);\n\n      function MagicType() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      MagicType.prototype.getIcons = function () {\n        var model = this.model;\n        var availableIcons = model.get('icon');\n        var icons = {};\n        each(model.get('type'), function (type) {\n          if (availableIcons[type]) {\n            icons[type] = availableIcons[type];\n          }\n        });\n        return icons;\n      };\n\n      MagicType.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          type: [],\n          // Icon group\n          icon: {\n            line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4',\n            bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7',\n            // eslint-disable-next-line\n            stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z' // jshint ignore:line\n\n          },\n          // `line`, `bar`, `stack`, `tiled`\n          title: ecModel.getLocaleModel().get(['toolbox', 'magicType', 'title']),\n          option: {},\n          seriesIndex: {}\n        };\n        return defaultOption;\n      };\n\n      MagicType.prototype.onclick = function (ecModel, api, type) {\n        var model = this.model;\n        var seriesIndex = model.get(['seriesIndex', type]); // Not supported magicType\n\n        if (!seriesOptGenreator[type]) {\n          return;\n        }\n\n        var newOption = {\n          series: []\n        };\n\n        var generateNewSeriesTypes = function (seriesModel) {\n          var seriesType = seriesModel.subType;\n          var seriesId = seriesModel.id;\n          var newSeriesOpt = seriesOptGenreator[type](seriesType, seriesId, seriesModel, model);\n\n          if (newSeriesOpt) {\n            // PENDING If merge original option?\n            defaults(newSeriesOpt, seriesModel.option);\n            newOption.series.push(newSeriesOpt);\n          } // Modify boundaryGap\n\n\n          var coordSys = seriesModel.coordinateSystem;\n\n          if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) {\n            var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n\n            if (categoryAxis) {\n              var axisDim = categoryAxis.dim;\n              var axisType = axisDim + 'Axis';\n              var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0];\n              var axisIndex = axisModel.componentIndex;\n              newOption[axisType] = newOption[axisType] || [];\n\n              for (var i = 0; i <= axisIndex; i++) {\n                newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {};\n              }\n\n              newOption[axisType][axisIndex].boundaryGap = type === 'bar';\n            }\n          }\n        };\n\n        each(radioTypes, function (radio) {\n          if (indexOf(radio, type) >= 0) {\n            each(radio, function (item) {\n              model.setIconStatus(item, 'normal');\n            });\n          }\n        });\n        model.setIconStatus(type, 'emphasis');\n        ecModel.eachComponent({\n          mainType: 'series',\n          query: seriesIndex == null ? null : {\n            seriesIndex: seriesIndex\n          }\n        }, generateNewSeriesTypes);\n        var newTitle;\n        var currentType = type; // Change title of stack\n\n        if (type === 'stack') {\n          // use titles in model instead of ecModel\n          // as stack and tiled appears in pair, just flip them\n          // no need of checking stack state\n          newTitle = merge({\n            stack: model.option.title.tiled,\n            tiled: model.option.title.stack\n          }, model.option.title);\n\n          if (model.get(['iconStatus', type]) !== 'emphasis') {\n            currentType = 'tiled';\n          }\n        }\n\n        api.dispatchAction({\n          type: 'changeMagicType',\n          currentType: currentType,\n          newOption: newOption,\n          newTitle: newTitle,\n          featureName: 'magicType'\n        });\n      };\n\n      return MagicType;\n    }(ToolboxFeature);\n\n    var seriesOptGenreator = {\n      'line': function (seriesType, seriesId, seriesModel, model) {\n        if (seriesType === 'bar') {\n          return merge({\n            id: seriesId,\n            type: 'line',\n            // Preserve data related option\n            data: seriesModel.get('data'),\n            stack: seriesModel.get('stack'),\n            markPoint: seriesModel.get('markPoint'),\n            markLine: seriesModel.get('markLine')\n          }, model.get(['option', 'line']) || {}, true);\n        }\n      },\n      'bar': function (seriesType, seriesId, seriesModel, model) {\n        if (seriesType === 'line') {\n          return merge({\n            id: seriesId,\n            type: 'bar',\n            // Preserve data related option\n            data: seriesModel.get('data'),\n            stack: seriesModel.get('stack'),\n            markPoint: seriesModel.get('markPoint'),\n            markLine: seriesModel.get('markLine')\n          }, model.get(['option', 'bar']) || {}, true);\n        }\n      },\n      'stack': function (seriesType, seriesId, seriesModel, model) {\n        var isStack = seriesModel.get('stack') === INNER_STACK_KEYWORD;\n\n        if (seriesType === 'line' || seriesType === 'bar') {\n          model.setIconStatus('stack', isStack ? 'normal' : 'emphasis');\n          return merge({\n            id: seriesId,\n            stack: isStack ? '' : INNER_STACK_KEYWORD\n          }, model.get(['option', 'stack']) || {}, true);\n        }\n      }\n    }; // TODO: SELF REGISTERED.\n\n    registerAction({\n      type: 'changeMagicType',\n      event: 'magicTypeChanged',\n      update: 'prepareAndUpdate'\n    }, function (payload, ecModel) {\n      ecModel.mergeOption(payload.newOption);\n    });\n\n    /* global document */\n\n    var BLOCK_SPLITER = new Array(60).join('-');\n    var ITEM_SPLITER = '\\t';\n    /**\n     * Group series into two types\n     *  1. on category axis, like line, bar\n     *  2. others, like scatter, pie\n     */\n\n    function groupSeries(ecModel) {\n      var seriesGroupByCategoryAxis = {};\n      var otherSeries = [];\n      var meta = [];\n      ecModel.eachRawSeries(function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) {\n          // TODO: TYPE Consider polar? Include polar may increase unecessary bundle size.\n          var baseAxis = coordSys.getBaseAxis();\n\n          if (baseAxis.type === 'category') {\n            var key = baseAxis.dim + '_' + baseAxis.index;\n\n            if (!seriesGroupByCategoryAxis[key]) {\n              seriesGroupByCategoryAxis[key] = {\n                categoryAxis: baseAxis,\n                valueAxis: coordSys.getOtherAxis(baseAxis),\n                series: []\n              };\n              meta.push({\n                axisDim: baseAxis.dim,\n                axisIndex: baseAxis.index\n              });\n            }\n\n            seriesGroupByCategoryAxis[key].series.push(seriesModel);\n          } else {\n            otherSeries.push(seriesModel);\n          }\n        } else {\n          otherSeries.push(seriesModel);\n        }\n      });\n      return {\n        seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,\n        other: otherSeries,\n        meta: meta\n      };\n    }\n    /**\n     * Assemble content of series on cateogory axis\n     * @inner\n     */\n\n\n    function assembleSeriesWithCategoryAxis(groups) {\n      var tables = [];\n      each(groups, function (group, key) {\n        var categoryAxis = group.categoryAxis;\n        var valueAxis = group.valueAxis;\n        var valueAxisDim = valueAxis.dim;\n        var headers = [' '].concat(map(group.series, function (series) {\n          return series.name;\n        })); // @ts-ignore TODO Polar\n\n        var columns = [categoryAxis.model.getCategories()];\n        each(group.series, function (series) {\n          var rawData = series.getRawData();\n          columns.push(series.getRawData().mapArray(rawData.mapDimension(valueAxisDim), function (val) {\n            return val;\n          }));\n        }); // Assemble table content\n\n        var lines = [headers.join(ITEM_SPLITER)];\n\n        for (var i = 0; i < columns[0].length; i++) {\n          var items = [];\n\n          for (var j = 0; j < columns.length; j++) {\n            items.push(columns[j][i]);\n          }\n\n          lines.push(items.join(ITEM_SPLITER));\n        }\n\n        tables.push(lines.join('\\n'));\n      });\n      return tables.join('\\n\\n' + BLOCK_SPLITER + '\\n\\n');\n    }\n    /**\n     * Assemble content of other series\n     */\n\n\n    function assembleOtherSeries(series) {\n      return map(series, function (series) {\n        var data = series.getRawData();\n        var lines = [series.name];\n        var vals = [];\n        data.each(data.dimensions, function () {\n          var argLen = arguments.length;\n          var dataIndex = arguments[argLen - 1];\n          var name = data.getName(dataIndex);\n\n          for (var i = 0; i < argLen - 1; i++) {\n            vals[i] = arguments[i];\n          }\n\n          lines.push((name ? name + ITEM_SPLITER : '') + vals.join(ITEM_SPLITER));\n        });\n        return lines.join('\\n');\n      }).join('\\n\\n' + BLOCK_SPLITER + '\\n\\n');\n    }\n\n    function getContentFromModel(ecModel) {\n      var result = groupSeries(ecModel);\n      return {\n        value: filter([assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), assembleOtherSeries(result.other)], function (str) {\n          return !!str.replace(/[\\n\\t\\s]/g, '');\n        }).join('\\n\\n' + BLOCK_SPLITER + '\\n\\n'),\n        meta: result.meta\n      };\n    }\n\n    function trim$1(str) {\n      return str.replace(/^\\s\\s*/, '').replace(/\\s\\s*$/, '');\n    }\n    /**\n     * If a block is tsv format\n     */\n\n\n    function isTSVFormat(block) {\n      // Simple method to find out if a block is tsv format\n      var firstLine = block.slice(0, block.indexOf('\\n'));\n\n      if (firstLine.indexOf(ITEM_SPLITER) >= 0) {\n        return true;\n      }\n    }\n\n    var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g');\n    /**\n     * @param {string} tsv\n     * @return {Object}\n     */\n\n    function parseTSVContents(tsv) {\n      var tsvLines = tsv.split(/\\n+/g);\n      var headers = trim$1(tsvLines.shift()).split(itemSplitRegex);\n      var categories = [];\n      var series = map(headers, function (header) {\n        return {\n          name: header,\n          data: []\n        };\n      });\n\n      for (var i = 0; i < tsvLines.length; i++) {\n        var items = trim$1(tsvLines[i]).split(itemSplitRegex);\n        categories.push(items.shift());\n\n        for (var j = 0; j < items.length; j++) {\n          series[j] && (series[j].data[i] = items[j]);\n        }\n      }\n\n      return {\n        series: series,\n        categories: categories\n      };\n    }\n\n    function parseListContents(str) {\n      var lines = str.split(/\\n+/g);\n      var seriesName = trim$1(lines.shift());\n      var data = [];\n\n      for (var i = 0; i < lines.length; i++) {\n        // if line is empty, ignore it.\n        // there is a case that a user forgot to delete `\\n`.\n        var line = trim$1(lines[i]);\n\n        if (!line) {\n          continue;\n        }\n\n        var items = line.split(itemSplitRegex);\n        var name_1 = '';\n        var value = void 0;\n        var hasName = false;\n\n        if (isNaN(items[0])) {\n          // First item is name\n          hasName = true;\n          name_1 = items[0];\n          items = items.slice(1);\n          data[i] = {\n            name: name_1,\n            value: []\n          };\n          value = data[i].value;\n        } else {\n          value = data[i] = [];\n        }\n\n        for (var j = 0; j < items.length; j++) {\n          value.push(+items[j]);\n        }\n\n        if (value.length === 1) {\n          hasName ? data[i].value = value[0] : data[i] = value[0];\n        }\n      }\n\n      return {\n        name: seriesName,\n        data: data\n      };\n    }\n\n    function parseContents(str, blockMetaList) {\n      var blocks = str.split(new RegExp('\\n*' + BLOCK_SPLITER + '\\n*', 'g'));\n      var newOption = {\n        series: []\n      };\n      each(blocks, function (block, idx) {\n        if (isTSVFormat(block)) {\n          var result = parseTSVContents(block);\n          var blockMeta = blockMetaList[idx];\n          var axisKey = blockMeta.axisDim + 'Axis';\n\n          if (blockMeta) {\n            newOption[axisKey] = newOption[axisKey] || [];\n            newOption[axisKey][blockMeta.axisIndex] = {\n              data: result.categories\n            };\n            newOption.series = newOption.series.concat(result.series);\n          }\n        } else {\n          var result = parseListContents(block);\n          newOption.series.push(result);\n        }\n      });\n      return newOption;\n    }\n\n    var DataView =\n    /** @class */\n    function (_super) {\n      __extends(DataView, _super);\n\n      function DataView() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      DataView.prototype.onclick = function (ecModel, api) {\n        // FIXME: better way?\n        setTimeout(function () {\n          api.dispatchAction({\n            type: 'hideTip'\n          });\n        });\n        var container = api.getDom();\n        var model = this.model;\n\n        if (this._dom) {\n          container.removeChild(this._dom);\n        }\n\n        var root = document.createElement('div'); // use padding to avoid 5px whitespace\n\n        root.style.cssText = 'position:absolute;top:0;bottom:0;left:0;right:0;padding:5px';\n        root.style.backgroundColor = model.get('backgroundColor') || '#fff'; // Create elements\n\n        var header = document.createElement('h4');\n        var lang = model.get('lang') || [];\n        header.innerHTML = lang[0] || model.get('title');\n        header.style.cssText = 'margin:10px 20px';\n        header.style.color = model.get('textColor');\n        var viewMain = document.createElement('div');\n        var textarea = document.createElement('textarea');\n        viewMain.style.cssText = 'overflow:auto';\n        var optionToContent = model.get('optionToContent');\n        var contentToOption = model.get('contentToOption');\n        var result = getContentFromModel(ecModel);\n\n        if (isFunction(optionToContent)) {\n          var htmlOrDom = optionToContent(api.getOption());\n\n          if (isString(htmlOrDom)) {\n            viewMain.innerHTML = htmlOrDom;\n          } else if (isDom(htmlOrDom)) {\n            viewMain.appendChild(htmlOrDom);\n          }\n        } else {\n          // Use default textarea\n          textarea.readOnly = model.get('readOnly');\n          var style = textarea.style; // eslint-disable-next-line max-len\n\n          style.cssText = 'display:block;width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;resize:none;box-sizing:border-box;outline:none';\n          style.color = model.get('textColor');\n          style.borderColor = model.get('textareaBorderColor');\n          style.backgroundColor = model.get('textareaColor');\n          textarea.value = result.value;\n          viewMain.appendChild(textarea);\n        }\n\n        var blockMetaList = result.meta;\n        var buttonContainer = document.createElement('div');\n        buttonContainer.style.cssText = 'position:absolute;bottom:5px;left:0;right:0'; // eslint-disable-next-line max-len\n\n        var buttonStyle = 'float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px';\n        var closeButton = document.createElement('div');\n        var refreshButton = document.createElement('div');\n        buttonStyle += ';background-color:' + model.get('buttonColor');\n        buttonStyle += ';color:' + model.get('buttonTextColor');\n        var self = this;\n\n        function close() {\n          container.removeChild(root);\n          self._dom = null;\n        }\n\n        addEventListener(closeButton, 'click', close);\n        addEventListener(refreshButton, 'click', function () {\n          if (contentToOption == null && optionToContent != null || contentToOption != null && optionToContent == null) {\n            if (\"development\" !== 'production') {\n              // eslint-disable-next-line\n              warn('It seems you have just provided one of `contentToOption` and `optionToContent` functions but missed the other one. Data change is ignored.');\n            }\n\n            close();\n            return;\n          }\n\n          var newOption;\n\n          try {\n            if (isFunction(contentToOption)) {\n              newOption = contentToOption(viewMain, api.getOption());\n            } else {\n              newOption = parseContents(textarea.value, blockMetaList);\n            }\n          } catch (e) {\n            close();\n            throw new Error('Data view format error ' + e);\n          }\n\n          if (newOption) {\n            api.dispatchAction({\n              type: 'changeDataView',\n              newOption: newOption\n            });\n          }\n\n          close();\n        });\n        closeButton.innerHTML = lang[1];\n        refreshButton.innerHTML = lang[2];\n        refreshButton.style.cssText = closeButton.style.cssText = buttonStyle;\n        !model.get('readOnly') && buttonContainer.appendChild(refreshButton);\n        buttonContainer.appendChild(closeButton);\n        root.appendChild(header);\n        root.appendChild(viewMain);\n        root.appendChild(buttonContainer);\n        viewMain.style.height = container.clientHeight - 80 + 'px';\n        container.appendChild(root);\n        this._dom = root;\n      };\n\n      DataView.prototype.remove = function (ecModel, api) {\n        this._dom && api.getDom().removeChild(this._dom);\n      };\n\n      DataView.prototype.dispose = function (ecModel, api) {\n        this.remove(ecModel, api);\n      };\n\n      DataView.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          readOnly: false,\n          optionToContent: null,\n          contentToOption: null,\n          // eslint-disable-next-line\n          icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',\n          title: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'title']),\n          lang: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'lang']),\n          backgroundColor: '#fff',\n          textColor: '#000',\n          textareaColor: '#fff',\n          textareaBorderColor: '#333',\n          buttonColor: '#c23531',\n          buttonTextColor: '#fff'\n        };\n        return defaultOption;\n      };\n\n      return DataView;\n    }(ToolboxFeature);\n    /**\n     * @inner\n     */\n\n\n    function tryMergeDataOption(newData, originalData) {\n      return map(newData, function (newVal, idx) {\n        var original = originalData && originalData[idx];\n\n        if (isObject(original) && !isArray(original)) {\n          var newValIsObject = isObject(newVal) && !isArray(newVal);\n\n          if (!newValIsObject) {\n            newVal = {\n              value: newVal\n            };\n          } // original data has name but new data has no name\n\n\n          var shouldDeleteName = original.name != null && newVal.name == null; // Original data has option\n\n          newVal = defaults(newVal, original);\n          shouldDeleteName && delete newVal.name;\n          return newVal;\n        } else {\n          return newVal;\n        }\n      });\n    } // TODO: SELF REGISTERED.\n\n\n    registerAction({\n      type: 'changeDataView',\n      event: 'dataViewChanged',\n      update: 'prepareAndUpdate'\n    }, function (payload, ecModel) {\n      var newSeriesOptList = [];\n      each(payload.newOption.series, function (seriesOpt) {\n        var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];\n\n        if (!seriesModel) {\n          // New created series\n          // Geuss the series type\n          newSeriesOptList.push(extend({\n            // Default is scatter\n            type: 'scatter'\n          }, seriesOpt));\n        } else {\n          var originalData = seriesModel.get('data');\n          newSeriesOptList.push({\n            name: seriesOpt.name,\n            data: tryMergeDataOption(seriesOpt.data, originalData)\n          });\n        }\n      });\n      ecModel.mergeOption(defaults({\n        series: newSeriesOptList\n      }, payload.newOption));\n    });\n\n    var each$5 = each;\n    var inner$b = makeInner();\n    /**\n     * @param ecModel\n     * @param newSnapshot key is dataZoomId\n     */\n\n    function push(ecModel, newSnapshot) {\n      var storedSnapshots = getStoreSnapshots(ecModel); // If previous dataZoom can not be found,\n      // complete an range with current range.\n\n      each$5(newSnapshot, function (batchItem, dataZoomId) {\n        var i = storedSnapshots.length - 1;\n\n        for (; i >= 0; i--) {\n          var snapshot = storedSnapshots[i];\n\n          if (snapshot[dataZoomId]) {\n            break;\n          }\n        }\n\n        if (i < 0) {\n          // No origin range set, create one by current range.\n          var dataZoomModel = ecModel.queryComponents({\n            mainType: 'dataZoom',\n            subType: 'select',\n            id: dataZoomId\n          })[0];\n\n          if (dataZoomModel) {\n            var percentRange = dataZoomModel.getPercentRange();\n            storedSnapshots[0][dataZoomId] = {\n              dataZoomId: dataZoomId,\n              start: percentRange[0],\n              end: percentRange[1]\n            };\n          }\n        }\n      });\n      storedSnapshots.push(newSnapshot);\n    }\n    function pop(ecModel) {\n      var storedSnapshots = getStoreSnapshots(ecModel);\n      var head = storedSnapshots[storedSnapshots.length - 1];\n      storedSnapshots.length > 1 && storedSnapshots.pop(); // Find top for all dataZoom.\n\n      var snapshot = {};\n      each$5(head, function (batchItem, dataZoomId) {\n        for (var i = storedSnapshots.length - 1; i >= 0; i--) {\n          batchItem = storedSnapshots[i][dataZoomId];\n\n          if (batchItem) {\n            snapshot[dataZoomId] = batchItem;\n            break;\n          }\n        }\n      });\n      return snapshot;\n    }\n    function clear$1(ecModel) {\n      inner$b(ecModel).snapshots = null;\n    }\n    function count(ecModel) {\n      return getStoreSnapshots(ecModel).length;\n    }\n    /**\n     * History length of each dataZoom may be different.\n     * this._history[0] is used to store origin range.\n     */\n\n    function getStoreSnapshots(ecModel) {\n      var store = inner$b(ecModel);\n\n      if (!store.snapshots) {\n        store.snapshots = [{}];\n      }\n\n      return store.snapshots;\n    }\n\n    var RestoreOption =\n    /** @class */\n    function (_super) {\n      __extends(RestoreOption, _super);\n\n      function RestoreOption() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      RestoreOption.prototype.onclick = function (ecModel, api) {\n        clear$1(ecModel);\n        api.dispatchAction({\n          type: 'restore',\n          from: this.uid\n        });\n      };\n\n      RestoreOption.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          // eslint-disable-next-line\n          icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5',\n          title: ecModel.getLocaleModel().get(['toolbox', 'restore', 'title'])\n        };\n        return defaultOption;\n      };\n\n      return RestoreOption;\n    }(ToolboxFeature); // TODO: SELF REGISTERED.\n\n\n    registerAction({\n      type: 'restore',\n      event: 'restore',\n      update: 'prepareAndUpdate'\n    }, function (payload, ecModel) {\n      ecModel.resetOption('recreate');\n    });\n\n    var ATTR = '\\0_ec_interaction_mutex';\n    function take(zr, resourceKey, userKey) {\n      var store = getStore(zr);\n      store[resourceKey] = userKey;\n    }\n    function release(zr, resourceKey, userKey) {\n      var store = getStore(zr);\n      var uKey = store[resourceKey];\n\n      if (uKey === userKey) {\n        store[resourceKey] = null;\n      }\n    }\n    function isTaken(zr, resourceKey) {\n      return !!getStore(zr)[resourceKey];\n    }\n\n    function getStore(zr) {\n      return zr[ATTR] || (zr[ATTR] = {});\n    }\n    /**\n     * payload: {\n     *     type: 'takeGlobalCursor',\n     *     key: 'dataZoomSelect', or 'brush', or ...,\n     *         If no userKey, release global cursor.\n     * }\n     */\n    // TODO: SELF REGISTERED.\n\n\n    registerAction({\n      type: 'takeGlobalCursor',\n      event: 'globalCursorTaken',\n      update: 'update'\n    }, noop);\n\n    var BRUSH_PANEL_GLOBAL = true;\n    var mathMin$7 = Math.min;\n    var mathMax$7 = Math.max;\n    var mathPow$2 = Math.pow;\n    var COVER_Z = 10000;\n    var UNSELECT_THRESHOLD = 6;\n    var MIN_RESIZE_LINE_WIDTH = 6;\n    var MUTEX_RESOURCE_KEY = 'globalPan';\n    var DIRECTION_MAP = {\n      w: [0, 0],\n      e: [0, 1],\n      n: [1, 0],\n      s: [1, 1]\n    };\n    var CURSOR_MAP = {\n      w: 'ew',\n      e: 'ew',\n      n: 'ns',\n      s: 'ns',\n      ne: 'nesw',\n      sw: 'nesw',\n      nw: 'nwse',\n      se: 'nwse'\n    };\n    var DEFAULT_BRUSH_OPT = {\n      brushStyle: {\n        lineWidth: 2,\n        stroke: 'rgba(210,219,238,0.3)',\n        fill: '#D2DBEE'\n      },\n      transformable: true,\n      brushMode: 'single',\n      removeOnClick: false\n    };\n    var baseUID = 0;\n    /**\n     * params:\n     *     areas: Array.<Array>, coord relates to container group,\n     *                             If no container specified, to global.\n     *     opt {\n     *         isEnd: boolean,\n     *         removeOnClick: boolean\n     *     }\n     */\n\n    var BrushController =\n    /** @class */\n    function (_super) {\n      __extends(BrushController, _super);\n\n      function BrushController(zr) {\n        var _this = _super.call(this) || this;\n        /**\n         * @internal\n         */\n\n\n        _this._track = [];\n        /**\n         * @internal\n         */\n\n        _this._covers = [];\n        _this._handlers = {};\n\n        if (\"development\" !== 'production') {\n          assert(zr);\n        }\n\n        _this._zr = zr;\n        _this.group = new Group();\n        _this._uid = 'brushController_' + baseUID++;\n        each(pointerHandlers, function (handler, eventName) {\n          this._handlers[eventName] = bind(handler, this);\n        }, _this);\n        return _this;\n      }\n      /**\n       * If set to `false`, select disabled.\n       */\n\n\n      BrushController.prototype.enableBrush = function (brushOption) {\n        if (\"development\" !== 'production') {\n          assert(this._mounted);\n        }\n\n        this._brushType && this._doDisableBrush();\n        brushOption.brushType && this._doEnableBrush(brushOption);\n        return this;\n      };\n\n      BrushController.prototype._doEnableBrush = function (brushOption) {\n        var zr = this._zr; // Consider roam, which takes globalPan too.\n\n        if (!this._enableGlobalPan) {\n          take(zr, MUTEX_RESOURCE_KEY, this._uid);\n        }\n\n        each(this._handlers, function (handler, eventName) {\n          zr.on(eventName, handler);\n        });\n        this._brushType = brushOption.brushType;\n        this._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true);\n      };\n\n      BrushController.prototype._doDisableBrush = function () {\n        var zr = this._zr;\n        release(zr, MUTEX_RESOURCE_KEY, this._uid);\n        each(this._handlers, function (handler, eventName) {\n          zr.off(eventName, handler);\n        });\n        this._brushType = this._brushOption = null;\n      };\n      /**\n       * @param panelOpts If not pass, it is global brush.\n       */\n\n\n      BrushController.prototype.setPanels = function (panelOpts) {\n        if (panelOpts && panelOpts.length) {\n          var panels_1 = this._panels = {};\n          each(panelOpts, function (panelOpts) {\n            panels_1[panelOpts.panelId] = clone(panelOpts);\n          });\n        } else {\n          this._panels = null;\n        }\n\n        return this;\n      };\n\n      BrushController.prototype.mount = function (opt) {\n        opt = opt || {};\n\n        if (\"development\" !== 'production') {\n          this._mounted = true; // should be at first.\n        }\n\n        this._enableGlobalPan = opt.enableGlobalPan;\n        var thisGroup = this.group;\n\n        this._zr.add(thisGroup);\n\n        thisGroup.attr({\n          x: opt.x || 0,\n          y: opt.y || 0,\n          rotation: opt.rotation || 0,\n          scaleX: opt.scaleX || 1,\n          scaleY: opt.scaleY || 1\n        });\n        this._transform = thisGroup.getLocalTransform();\n        return this;\n      }; // eachCover(cb, context): void {\n      //     each(this._covers, cb, context);\n      // }\n\n      /**\n       * Update covers.\n       * @param coverConfigList\n       *        If coverConfigList is null/undefined, all covers removed.\n       */\n\n\n      BrushController.prototype.updateCovers = function (coverConfigList) {\n        if (\"development\" !== 'production') {\n          assert(this._mounted);\n        }\n\n        coverConfigList = map(coverConfigList, function (coverConfig) {\n          return merge(clone(DEFAULT_BRUSH_OPT), coverConfig, true);\n        });\n        var tmpIdPrefix = '\\0-brush-index-';\n        var oldCovers = this._covers;\n        var newCovers = this._covers = [];\n        var controller = this;\n        var creatingCover = this._creatingCover;\n        new DataDiffer(oldCovers, coverConfigList, oldGetKey, getKey).add(addOrUpdate).update(addOrUpdate).remove(remove).execute();\n        return this;\n\n        function getKey(brushOption, index) {\n          return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + '-' + brushOption.brushType;\n        }\n\n        function oldGetKey(cover, index) {\n          return getKey(cover.__brushOption, index);\n        }\n\n        function addOrUpdate(newIndex, oldIndex) {\n          var newBrushInternal = coverConfigList[newIndex]; // Consider setOption in event listener of brushSelect,\n          // where updating cover when creating should be forbiden.\n\n          if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {\n            newCovers[newIndex] = oldCovers[oldIndex];\n          } else {\n            var cover = newCovers[newIndex] = oldIndex != null ? (oldCovers[oldIndex].__brushOption = newBrushInternal, oldCovers[oldIndex]) : endCreating(controller, createCover(controller, newBrushInternal));\n            updateCoverAfterCreation(controller, cover);\n          }\n        }\n\n        function remove(oldIndex) {\n          if (oldCovers[oldIndex] !== creatingCover) {\n            controller.group.remove(oldCovers[oldIndex]);\n          }\n        }\n      };\n\n      BrushController.prototype.unmount = function () {\n        if (\"development\" !== 'production') {\n          if (!this._mounted) {\n            return;\n          }\n        }\n\n        this.enableBrush(false); // container may 'removeAll' outside.\n\n        clearCovers(this);\n\n        this._zr.remove(this.group);\n\n        if (\"development\" !== 'production') {\n          this._mounted = false; // should be at last.\n        }\n\n        return this;\n      };\n\n      BrushController.prototype.dispose = function () {\n        this.unmount();\n        this.off();\n      };\n\n      return BrushController;\n    }(Eventful);\n\n    function createCover(controller, brushOption) {\n      var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);\n      cover.__brushOption = brushOption;\n      updateZ(cover, brushOption);\n      controller.group.add(cover);\n      return cover;\n    }\n\n    function endCreating(controller, creatingCover) {\n      var coverRenderer = getCoverRenderer(creatingCover);\n\n      if (coverRenderer.endCreating) {\n        coverRenderer.endCreating(controller, creatingCover);\n        updateZ(creatingCover, creatingCover.__brushOption);\n      }\n\n      return creatingCover;\n    }\n\n    function updateCoverShape(controller, cover) {\n      var brushOption = cover.__brushOption;\n      getCoverRenderer(cover).updateCoverShape(controller, cover, brushOption.range, brushOption);\n    }\n\n    function updateZ(cover, brushOption) {\n      var z = brushOption.z;\n      z == null && (z = COVER_Z);\n      cover.traverse(function (el) {\n        el.z = z;\n        el.z2 = z; // Consider in given container.\n      });\n    }\n\n    function updateCoverAfterCreation(controller, cover) {\n      getCoverRenderer(cover).updateCommon(controller, cover);\n      updateCoverShape(controller, cover);\n    }\n\n    function getCoverRenderer(cover) {\n      return coverRenderers[cover.__brushOption.brushType];\n    } // return target panel or `true` (means global panel)\n\n\n    function getPanelByPoint(controller, e, localCursorPoint) {\n      var panels = controller._panels;\n\n      if (!panels) {\n        return BRUSH_PANEL_GLOBAL; // Global panel\n      }\n\n      var panel;\n      var transform = controller._transform;\n      each(panels, function (pn) {\n        pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn);\n      });\n      return panel;\n    } // Return a panel or true\n\n\n    function getPanelByCover(controller, cover) {\n      var panels = controller._panels;\n\n      if (!panels) {\n        return BRUSH_PANEL_GLOBAL; // Global panel\n      }\n\n      var panelId = cover.__brushOption.panelId; // User may give cover without coord sys info,\n      // which is then treated as global panel.\n\n      return panelId != null ? panels[panelId] : BRUSH_PANEL_GLOBAL;\n    }\n\n    function clearCovers(controller) {\n      var covers = controller._covers;\n      var originalLength = covers.length;\n      each(covers, function (cover) {\n        controller.group.remove(cover);\n      }, controller);\n      covers.length = 0;\n      return !!originalLength;\n    }\n\n    function trigger(controller, opt) {\n      var areas = map(controller._covers, function (cover) {\n        var brushOption = cover.__brushOption;\n        var range = clone(brushOption.range);\n        return {\n          brushType: brushOption.brushType,\n          panelId: brushOption.panelId,\n          range: range\n        };\n      });\n      controller.trigger('brush', {\n        areas: areas,\n        isEnd: !!opt.isEnd,\n        removeOnClick: !!opt.removeOnClick\n      });\n    }\n\n    function shouldShowCover(controller) {\n      var track = controller._track;\n\n      if (!track.length) {\n        return false;\n      }\n\n      var p2 = track[track.length - 1];\n      var p1 = track[0];\n      var dx = p2[0] - p1[0];\n      var dy = p2[1] - p1[1];\n      var dist = mathPow$2(dx * dx + dy * dy, 0.5);\n      return dist > UNSELECT_THRESHOLD;\n    }\n\n    function getTrackEnds(track) {\n      var tail = track.length - 1;\n      tail < 0 && (tail = 0);\n      return [track[0], track[tail]];\n    }\n\n    function createBaseRectCover(rectRangeConverter, controller, brushOption, edgeNameSequences) {\n      var cover = new Group();\n      cover.add(new Rect({\n        name: 'main',\n        style: makeStyle(brushOption),\n        silent: true,\n        draggable: true,\n        cursor: 'move',\n        drift: curry(driftRect, rectRangeConverter, controller, cover, ['n', 's', 'w', 'e']),\n        ondragend: curry(trigger, controller, {\n          isEnd: true\n        })\n      }));\n      each(edgeNameSequences, function (nameSequence) {\n        cover.add(new Rect({\n          name: nameSequence.join(''),\n          style: {\n            opacity: 0\n          },\n          draggable: true,\n          silent: true,\n          invisible: true,\n          drift: curry(driftRect, rectRangeConverter, controller, cover, nameSequence),\n          ondragend: curry(trigger, controller, {\n            isEnd: true\n          })\n        }));\n      });\n      return cover;\n    }\n\n    function updateBaseRect(controller, cover, localRange, brushOption) {\n      var lineWidth = brushOption.brushStyle.lineWidth || 0;\n      var handleSize = mathMax$7(lineWidth, MIN_RESIZE_LINE_WIDTH);\n      var x = localRange[0][0];\n      var y = localRange[1][0];\n      var xa = x - lineWidth / 2;\n      var ya = y - lineWidth / 2;\n      var x2 = localRange[0][1];\n      var y2 = localRange[1][1];\n      var x2a = x2 - handleSize + lineWidth / 2;\n      var y2a = y2 - handleSize + lineWidth / 2;\n      var width = x2 - x;\n      var height = y2 - y;\n      var widtha = width + lineWidth;\n      var heighta = height + lineWidth;\n      updateRectShape(controller, cover, 'main', x, y, width, height);\n\n      if (brushOption.transformable) {\n        updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);\n        updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);\n        updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);\n        updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);\n        updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);\n        updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);\n        updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);\n        updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);\n      }\n    }\n\n    function updateCommon(controller, cover) {\n      var brushOption = cover.__brushOption;\n      var transformable = brushOption.transformable;\n      var mainEl = cover.childAt(0);\n      mainEl.useStyle(makeStyle(brushOption));\n      mainEl.attr({\n        silent: !transformable,\n        cursor: transformable ? 'move' : 'default'\n      });\n      each([['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']], function (nameSequence) {\n        var el = cover.childOfName(nameSequence.join(''));\n        var globalDir = nameSequence.length === 1 ? getGlobalDirection1(controller, nameSequence[0]) : getGlobalDirection2(controller, nameSequence);\n        el && el.attr({\n          silent: !transformable,\n          invisible: !transformable,\n          cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null\n        });\n      });\n    }\n\n    function updateRectShape(controller, cover, name, x, y, w, h) {\n      var el = cover.childOfName(name);\n      el && el.setShape(pointsToRect(clipByPanel(controller, cover, [[x, y], [x + w, y + h]])));\n    }\n\n    function makeStyle(brushOption) {\n      return defaults({\n        strokeNoScale: true\n      }, brushOption.brushStyle);\n    }\n\n    function formatRectRange(x, y, x2, y2) {\n      var min = [mathMin$7(x, x2), mathMin$7(y, y2)];\n      var max = [mathMax$7(x, x2), mathMax$7(y, y2)];\n      return [[min[0], max[0]], [min[1], max[1]] // y range\n      ];\n    }\n\n    function getTransform$1(controller) {\n      return getTransform(controller.group);\n    }\n\n    function getGlobalDirection1(controller, localDirName) {\n      var map = {\n        w: 'left',\n        e: 'right',\n        n: 'top',\n        s: 'bottom'\n      };\n      var inverseMap = {\n        left: 'w',\n        right: 'e',\n        top: 'n',\n        bottom: 's'\n      };\n      var dir = transformDirection(map[localDirName], getTransform$1(controller));\n      return inverseMap[dir];\n    }\n\n    function getGlobalDirection2(controller, localDirNameSeq) {\n      var globalDir = [getGlobalDirection1(controller, localDirNameSeq[0]), getGlobalDirection1(controller, localDirNameSeq[1])];\n      (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();\n      return globalDir.join('');\n    }\n\n    function driftRect(rectRangeConverter, controller, cover, dirNameSequence, dx, dy) {\n      var brushOption = cover.__brushOption;\n      var rectRange = rectRangeConverter.toRectRange(brushOption.range);\n      var localDelta = toLocalDelta(controller, dx, dy);\n      each(dirNameSequence, function (dirName) {\n        var ind = DIRECTION_MAP[dirName];\n        rectRange[ind[0]][ind[1]] += localDelta[ind[0]];\n      });\n      brushOption.range = rectRangeConverter.fromRectRange(formatRectRange(rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]));\n      updateCoverAfterCreation(controller, cover);\n      trigger(controller, {\n        isEnd: false\n      });\n    }\n\n    function driftPolygon(controller, cover, dx, dy) {\n      var range = cover.__brushOption.range;\n      var localDelta = toLocalDelta(controller, dx, dy);\n      each(range, function (point) {\n        point[0] += localDelta[0];\n        point[1] += localDelta[1];\n      });\n      updateCoverAfterCreation(controller, cover);\n      trigger(controller, {\n        isEnd: false\n      });\n    }\n\n    function toLocalDelta(controller, dx, dy) {\n      var thisGroup = controller.group;\n      var localD = thisGroup.transformCoordToLocal(dx, dy);\n      var localZero = thisGroup.transformCoordToLocal(0, 0);\n      return [localD[0] - localZero[0], localD[1] - localZero[1]];\n    }\n\n    function clipByPanel(controller, cover, data) {\n      var panel = getPanelByCover(controller, cover);\n      return panel && panel !== BRUSH_PANEL_GLOBAL ? panel.clipPath(data, controller._transform) : clone(data);\n    }\n\n    function pointsToRect(points) {\n      var xmin = mathMin$7(points[0][0], points[1][0]);\n      var ymin = mathMin$7(points[0][1], points[1][1]);\n      var xmax = mathMax$7(points[0][0], points[1][0]);\n      var ymax = mathMax$7(points[0][1], points[1][1]);\n      return {\n        x: xmin,\n        y: ymin,\n        width: xmax - xmin,\n        height: ymax - ymin\n      };\n    }\n\n    function resetCursor(controller, e, localCursorPoint) {\n      if ( // Check active\n      !controller._brushType // resetCursor should be always called when mouse is in zr area,\n      // but not called when mouse is out of zr area to avoid bad influence\n      // if `mousemove`, `mouseup` are triggered from `document` event.\n      || isOutsideZrArea(controller, e.offsetX, e.offsetY)) {\n        return;\n      }\n\n      var zr = controller._zr;\n      var covers = controller._covers;\n      var currPanel = getPanelByPoint(controller, e, localCursorPoint); // Check whether in covers.\n\n      if (!controller._dragging) {\n        for (var i = 0; i < covers.length; i++) {\n          var brushOption = covers[i].__brushOption;\n\n          if (currPanel && (currPanel === BRUSH_PANEL_GLOBAL || brushOption.panelId === currPanel.panelId) && coverRenderers[brushOption.brushType].contain(covers[i], localCursorPoint[0], localCursorPoint[1])) {\n            // Use cursor style set on cover.\n            return;\n          }\n        }\n      }\n\n      currPanel && zr.setCursorStyle('crosshair');\n    }\n\n    function preventDefault(e) {\n      var rawE = e.event;\n      rawE.preventDefault && rawE.preventDefault();\n    }\n\n    function mainShapeContain(cover, x, y) {\n      return cover.childOfName('main').contain(x, y);\n    }\n\n    function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {\n      var creatingCover = controller._creatingCover;\n      var panel = controller._creatingPanel;\n      var thisBrushOption = controller._brushOption;\n      var eventParams;\n\n      controller._track.push(localCursorPoint.slice());\n\n      if (shouldShowCover(controller) || creatingCover) {\n        if (panel && !creatingCover) {\n          thisBrushOption.brushMode === 'single' && clearCovers(controller);\n          var brushOption = clone(thisBrushOption);\n          brushOption.brushType = determineBrushType(brushOption.brushType, panel);\n          brushOption.panelId = panel === BRUSH_PANEL_GLOBAL ? null : panel.panelId;\n          creatingCover = controller._creatingCover = createCover(controller, brushOption);\n\n          controller._covers.push(creatingCover);\n        }\n\n        if (creatingCover) {\n          var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];\n          var coverBrushOption = creatingCover.__brushOption;\n          coverBrushOption.range = coverRenderer.getCreatingRange(clipByPanel(controller, creatingCover, controller._track));\n\n          if (isEnd) {\n            endCreating(controller, creatingCover);\n            coverRenderer.updateCommon(controller, creatingCover);\n          }\n\n          updateCoverShape(controller, creatingCover);\n          eventParams = {\n            isEnd: isEnd\n          };\n        }\n      } else if (isEnd && thisBrushOption.brushMode === 'single' && thisBrushOption.removeOnClick) {\n        // Help user to remove covers easily, only by a tiny drag, in 'single' mode.\n        // But a single click do not clear covers, because user may have casual\n        // clicks (for example, click on other component and do not expect covers\n        // disappear).\n        // Only some cover removed, trigger action, but not every click trigger action.\n        if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) {\n          eventParams = {\n            isEnd: isEnd,\n            removeOnClick: true\n          };\n        }\n      }\n\n      return eventParams;\n    }\n\n    function determineBrushType(brushType, panel) {\n      if (brushType === 'auto') {\n        if (\"development\" !== 'production') {\n          assert(panel && panel.defaultBrushType, 'MUST have defaultBrushType when brushType is \"atuo\"');\n        }\n\n        return panel.defaultBrushType;\n      }\n\n      return brushType;\n    }\n\n    var pointerHandlers = {\n      mousedown: function (e) {\n        if (this._dragging) {\n          // In case some browser do not support globalOut,\n          // and release mouse out side the browser.\n          handleDragEnd(this, e);\n        } else if (!e.target || !e.target.draggable) {\n          preventDefault(e);\n          var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);\n          this._creatingCover = null;\n          var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint);\n\n          if (panel) {\n            this._dragging = true;\n            this._track = [localCursorPoint.slice()];\n          }\n        }\n      },\n      mousemove: function (e) {\n        var x = e.offsetX;\n        var y = e.offsetY;\n        var localCursorPoint = this.group.transformCoordToLocal(x, y);\n        resetCursor(this, e, localCursorPoint);\n\n        if (this._dragging) {\n          preventDefault(e);\n          var eventParams = updateCoverByMouse(this, e, localCursorPoint, false);\n          eventParams && trigger(this, eventParams);\n        }\n      },\n      mouseup: function (e) {\n        handleDragEnd(this, e);\n      }\n    };\n\n    function handleDragEnd(controller, e) {\n      if (controller._dragging) {\n        preventDefault(e);\n        var x = e.offsetX;\n        var y = e.offsetY;\n        var localCursorPoint = controller.group.transformCoordToLocal(x, y);\n        var eventParams = updateCoverByMouse(controller, e, localCursorPoint, true);\n        controller._dragging = false;\n        controller._track = [];\n        controller._creatingCover = null; // trigger event shoule be at final, after procedure will be nested.\n\n        eventParams && trigger(controller, eventParams);\n      }\n    }\n\n    function isOutsideZrArea(controller, x, y) {\n      var zr = controller._zr;\n      return x < 0 || x > zr.getWidth() || y < 0 || y > zr.getHeight();\n    }\n    /**\n     * key: brushType\n     */\n\n\n    var coverRenderers = {\n      lineX: getLineRenderer(0),\n      lineY: getLineRenderer(1),\n      rect: {\n        createCover: function (controller, brushOption) {\n          function returnInput(range) {\n            return range;\n          }\n\n          return createBaseRectCover({\n            toRectRange: returnInput,\n            fromRectRange: returnInput\n          }, controller, brushOption, [['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']]);\n        },\n        getCreatingRange: function (localTrack) {\n          var ends = getTrackEnds(localTrack);\n          return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);\n        },\n        updateCoverShape: function (controller, cover, localRange, brushOption) {\n          updateBaseRect(controller, cover, localRange, brushOption);\n        },\n        updateCommon: updateCommon,\n        contain: mainShapeContain\n      },\n      polygon: {\n        createCover: function (controller, brushOption) {\n          var cover = new Group(); // Do not use graphic.Polygon because graphic.Polyline do not close the\n          // border of the shape when drawing, which is a better experience for user.\n\n          cover.add(new Polyline({\n            name: 'main',\n            style: makeStyle(brushOption),\n            silent: true\n          }));\n          return cover;\n        },\n        getCreatingRange: function (localTrack) {\n          return localTrack;\n        },\n        endCreating: function (controller, cover) {\n          cover.remove(cover.childAt(0)); // Use graphic.Polygon close the shape.\n\n          cover.add(new Polygon({\n            name: 'main',\n            draggable: true,\n            drift: curry(driftPolygon, controller, cover),\n            ondragend: curry(trigger, controller, {\n              isEnd: true\n            })\n          }));\n        },\n        updateCoverShape: function (controller, cover, localRange, brushOption) {\n          cover.childAt(0).setShape({\n            points: clipByPanel(controller, cover, localRange)\n          });\n        },\n        updateCommon: updateCommon,\n        contain: mainShapeContain\n      }\n    };\n\n    function getLineRenderer(xyIndex) {\n      return {\n        createCover: function (controller, brushOption) {\n          return createBaseRectCover({\n            toRectRange: function (range) {\n              var rectRange = [range, [0, 100]];\n              xyIndex && rectRange.reverse();\n              return rectRange;\n            },\n            fromRectRange: function (rectRange) {\n              return rectRange[xyIndex];\n            }\n          }, controller, brushOption, [[['w'], ['e']], [['n'], ['s']]][xyIndex]);\n        },\n        getCreatingRange: function (localTrack) {\n          var ends = getTrackEnds(localTrack);\n          var min = mathMin$7(ends[0][xyIndex], ends[1][xyIndex]);\n          var max = mathMax$7(ends[0][xyIndex], ends[1][xyIndex]);\n          return [min, max];\n        },\n        updateCoverShape: function (controller, cover, localRange, brushOption) {\n          var otherExtent; // If brushWidth not specified, fit the panel.\n\n          var panel = getPanelByCover(controller, cover);\n\n          if (panel !== BRUSH_PANEL_GLOBAL && panel.getLinearBrushOtherExtent) {\n            otherExtent = panel.getLinearBrushOtherExtent(xyIndex);\n          } else {\n            var zr = controller._zr;\n            otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]];\n          }\n\n          var rectRange = [localRange, otherExtent];\n          xyIndex && rectRange.reverse();\n          updateBaseRect(controller, cover, rectRange, brushOption);\n        },\n        updateCommon: updateCommon,\n        contain: mainShapeContain\n      };\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var IRRELEVANT_EXCLUDES = {\n      'axisPointer': 1,\n      'tooltip': 1,\n      'brush': 1\n    };\n    /**\n     * Avoid that: mouse click on a elements that is over geo or graph,\n     * but roam is triggered.\n     */\n\n    function onIrrelevantElement(e, api, targetCoordSysModel) {\n      var model = api.getComponentByElement(e.topTarget); // If model is axisModel, it works only if it is injected with coordinateSystem.\n\n      var coordSys = model && model.coordinateSystem;\n      return model && model !== targetCoordSysModel && !IRRELEVANT_EXCLUDES.hasOwnProperty(model.mainType) && coordSys && coordSys.model !== targetCoordSysModel;\n    }\n\n    function makeRectPanelClipPath(rect) {\n      rect = normalizeRect(rect);\n      return function (localPoints) {\n        return clipPointsByRect(localPoints, rect);\n      };\n    }\n    function makeLinearBrushOtherExtent(rect, specifiedXYIndex) {\n      rect = normalizeRect(rect);\n      return function (xyIndex) {\n        var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex;\n        var brushWidth = idx ? rect.width : rect.height;\n        var base = idx ? rect.x : rect.y;\n        return [base, base + (brushWidth || 0)];\n      };\n    }\n    function makeRectIsTargetByCursor(rect, api, targetModel) {\n      var boundingRect = normalizeRect(rect);\n      return function (e, localCursorPoint) {\n        return boundingRect.contain(localCursorPoint[0], localCursorPoint[1]) && !onIrrelevantElement(e, api, targetModel);\n      };\n    } // Consider width/height is negative.\n\n    function normalizeRect(rect) {\n      return BoundingRect.create(rect);\n    }\n\n    // how to genarialize to more coordinate systems.\n\n    var INCLUDE_FINDER_MAIN_TYPES = ['grid', 'xAxis', 'yAxis', 'geo', 'graph', 'polar', 'radiusAxis', 'angleAxis', 'bmap'];\n\n    var BrushTargetManager =\n    /** @class */\n    function () {\n      /**\n       * @param finder contains Index/Id/Name of xAxis/yAxis/geo/grid\n       *        Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}\n       * @param opt.include include coordinate system types.\n       */\n      function BrushTargetManager(finder, ecModel, opt) {\n        var _this = this;\n\n        this._targetInfoList = [];\n        var foundCpts = parseFinder$1(ecModel, finder);\n        each(targetInfoBuilders, function (builder, type) {\n          if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {\n            builder(foundCpts, _this._targetInfoList);\n          }\n        });\n      }\n\n      BrushTargetManager.prototype.setOutputRanges = function (areas, ecModel) {\n        this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {\n          (area.coordRanges || (area.coordRanges = [])).push(coordRange); // area.coordRange is the first of area.coordRanges\n\n          if (!area.coordRange) {\n            area.coordRange = coordRange; // In 'category' axis, coord to pixel is not reversible, so we can not\n            // rebuild range by coordRange accrately, which may bring trouble when\n            // brushing only one item. So we use __rangeOffset to rebuilding range\n            // by coordRange. And this it only used in brush component so it is no\n            // need to be adapted to coordRanges.\n\n            var result = coordConvert[area.brushType](0, coordSys, coordRange);\n            area.__rangeOffset = {\n              offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),\n              xyMinMax: result.xyMinMax\n            };\n          }\n        });\n        return areas;\n      };\n\n      BrushTargetManager.prototype.matchOutputRanges = function (areas, ecModel, cb) {\n        each(areas, function (area) {\n          var targetInfo = this.findTargetInfo(area, ecModel);\n\n          if (targetInfo && targetInfo !== true) {\n            each(targetInfo.coordSyses, function (coordSys) {\n              var result = coordConvert[area.brushType](1, coordSys, area.range, true);\n              cb(area, result.values, coordSys, ecModel);\n            });\n          }\n        }, this);\n      };\n      /**\n       * the `areas` is `BrushModel.areas`.\n       * Called in layout stage.\n       * convert `area.coordRange` to global range and set panelId to `area.range`.\n       */\n\n\n      BrushTargetManager.prototype.setInputRanges = function (areas, ecModel) {\n        each(areas, function (area) {\n          var targetInfo = this.findTargetInfo(area, ecModel);\n\n          if (\"development\" !== 'production') {\n            assert(!targetInfo || targetInfo === true || area.coordRange, 'coordRange must be specified when coord index specified.');\n            assert(!targetInfo || targetInfo !== true || area.range, 'range must be specified in global brush.');\n          }\n\n          area.range = area.range || []; // convert coordRange to global range and set panelId.\n\n          if (targetInfo && targetInfo !== true) {\n            area.panelId = targetInfo.panelId; // (1) area.range shoule always be calculate from coordRange but does\n            // not keep its original value, for the sake of the dataZoom scenario,\n            // where area.coordRange remains unchanged but area.range may be changed.\n            // (2) Only support converting one coordRange to pixel range in brush\n            // component. So do not consider `coordRanges`.\n            // (3) About __rangeOffset, see comment above.\n\n            var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);\n            var rangeOffset = area.__rangeOffset;\n            area.range = rangeOffset ? diffProcessor[area.brushType](result.values, rangeOffset.offset, getScales(result.xyMinMax, rangeOffset.xyMinMax)) : result.values;\n          }\n        }, this);\n      };\n\n      BrushTargetManager.prototype.makePanelOpts = function (api, getDefaultBrushType) {\n        return map(this._targetInfoList, function (targetInfo) {\n          var rect = targetInfo.getPanelRect();\n          return {\n            panelId: targetInfo.panelId,\n            defaultBrushType: getDefaultBrushType ? getDefaultBrushType(targetInfo) : null,\n            clipPath: makeRectPanelClipPath(rect),\n            isTargetByCursor: makeRectIsTargetByCursor(rect, api, targetInfo.coordSysModel),\n            getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect)\n          };\n        });\n      };\n\n      BrushTargetManager.prototype.controlSeries = function (area, seriesModel, ecModel) {\n        // Check whether area is bound in coord, and series do not belong to that coord.\n        // If do not do this check, some brush (like lineX) will controll all axes.\n        var targetInfo = this.findTargetInfo(area, ecModel);\n        return targetInfo === true || targetInfo && indexOf(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0;\n      };\n      /**\n       * If return Object, a coord found.\n       * If reutrn true, global found.\n       * Otherwise nothing found.\n       */\n\n\n      BrushTargetManager.prototype.findTargetInfo = function (area, ecModel) {\n        var targetInfoList = this._targetInfoList;\n        var foundCpts = parseFinder$1(ecModel, area);\n\n        for (var i = 0; i < targetInfoList.length; i++) {\n          var targetInfo = targetInfoList[i];\n          var areaPanelId = area.panelId;\n\n          if (areaPanelId) {\n            if (targetInfo.panelId === areaPanelId) {\n              return targetInfo;\n            }\n          } else {\n            for (var j = 0; j < targetInfoMatchers.length; j++) {\n              if (targetInfoMatchers[j](foundCpts, targetInfo)) {\n                return targetInfo;\n              }\n            }\n          }\n        }\n\n        return true;\n      };\n\n      return BrushTargetManager;\n    }();\n\n    function formatMinMax(minMax) {\n      minMax[0] > minMax[1] && minMax.reverse();\n      return minMax;\n    }\n\n    function parseFinder$1(ecModel, finder) {\n      return parseFinder(ecModel, finder, {\n        includeMainTypes: INCLUDE_FINDER_MAIN_TYPES\n      });\n    }\n\n    var targetInfoBuilders = {\n      grid: function (foundCpts, targetInfoList) {\n        var xAxisModels = foundCpts.xAxisModels;\n        var yAxisModels = foundCpts.yAxisModels;\n        var gridModels = foundCpts.gridModels; // Remove duplicated.\n\n        var gridModelMap = createHashMap();\n        var xAxesHas = {};\n        var yAxesHas = {};\n\n        if (!xAxisModels && !yAxisModels && !gridModels) {\n          return;\n        }\n\n        each(xAxisModels, function (axisModel) {\n          var gridModel = axisModel.axis.grid.model;\n          gridModelMap.set(gridModel.id, gridModel);\n          xAxesHas[gridModel.id] = true;\n        });\n        each(yAxisModels, function (axisModel) {\n          var gridModel = axisModel.axis.grid.model;\n          gridModelMap.set(gridModel.id, gridModel);\n          yAxesHas[gridModel.id] = true;\n        });\n        each(gridModels, function (gridModel) {\n          gridModelMap.set(gridModel.id, gridModel);\n          xAxesHas[gridModel.id] = true;\n          yAxesHas[gridModel.id] = true;\n        });\n        gridModelMap.each(function (gridModel) {\n          var grid = gridModel.coordinateSystem;\n          var cartesians = [];\n          each(grid.getCartesians(), function (cartesian, index) {\n            if (indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0 || indexOf(yAxisModels, cartesian.getAxis('y').model) >= 0) {\n              cartesians.push(cartesian);\n            }\n          });\n          targetInfoList.push({\n            panelId: 'grid--' + gridModel.id,\n            gridModel: gridModel,\n            coordSysModel: gridModel,\n            // Use the first one as the representitive coordSys.\n            coordSys: cartesians[0],\n            coordSyses: cartesians,\n            getPanelRect: panelRectBuilders.grid,\n            xAxisDeclared: xAxesHas[gridModel.id],\n            yAxisDeclared: yAxesHas[gridModel.id]\n          });\n        });\n      },\n      geo: function (foundCpts, targetInfoList) {\n        each(foundCpts.geoModels, function (geoModel) {\n          var coordSys = geoModel.coordinateSystem;\n          targetInfoList.push({\n            panelId: 'geo--' + geoModel.id,\n            geoModel: geoModel,\n            coordSysModel: geoModel,\n            coordSys: coordSys,\n            coordSyses: [coordSys],\n            getPanelRect: panelRectBuilders.geo\n          });\n        });\n      }\n    };\n    var targetInfoMatchers = [// grid\n    function (foundCpts, targetInfo) {\n      var xAxisModel = foundCpts.xAxisModel;\n      var yAxisModel = foundCpts.yAxisModel;\n      var gridModel = foundCpts.gridModel;\n      !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);\n      !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);\n      return gridModel && gridModel === targetInfo.gridModel;\n    }, // geo\n    function (foundCpts, targetInfo) {\n      var geoModel = foundCpts.geoModel;\n      return geoModel && geoModel === targetInfo.geoModel;\n    }];\n    var panelRectBuilders = {\n      grid: function () {\n        // grid is not Transformable.\n        return this.coordSys.master.getRect().clone();\n      },\n      geo: function () {\n        var coordSys = this.coordSys;\n        var rect = coordSys.getBoundingRect().clone(); // geo roam and zoom transform\n\n        rect.applyTransform(getTransform(coordSys));\n        return rect;\n      }\n    };\n    var coordConvert = {\n      lineX: curry(axisConvert, 0),\n      lineY: curry(axisConvert, 1),\n      rect: function (to, coordSys, rangeOrCoordRange, clamp) {\n        var xminymin = to ? coordSys.pointToData([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp);\n        var xmaxymax = to ? coordSys.pointToData([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp);\n        var values = [formatMinMax([xminymin[0], xmaxymax[0]]), formatMinMax([xminymin[1], xmaxymax[1]])];\n        return {\n          values: values,\n          xyMinMax: values\n        };\n      },\n      polygon: function (to, coordSys, rangeOrCoordRange, clamp) {\n        var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];\n        var values = map(rangeOrCoordRange, function (item) {\n          var p = to ? coordSys.pointToData(item, clamp) : coordSys.dataToPoint(item, clamp);\n          xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);\n          xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);\n          xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]);\n          xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]);\n          return p;\n        });\n        return {\n          values: values,\n          xyMinMax: xyMinMax\n        };\n      }\n    };\n\n    function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {\n      if (\"development\" !== 'production') {\n        assert(coordSys.type === 'cartesian2d', 'lineX/lineY brush is available only in cartesian2d.');\n      }\n\n      var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);\n      var values = formatMinMax(map([0, 1], function (i) {\n        return to ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]), true) : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));\n      }));\n      var xyMinMax = [];\n      xyMinMax[axisNameIndex] = values;\n      xyMinMax[1 - axisNameIndex] = [NaN, NaN];\n      return {\n        values: values,\n        xyMinMax: xyMinMax\n      };\n    }\n\n    var diffProcessor = {\n      lineX: curry(axisDiffProcessor, 0),\n      lineY: curry(axisDiffProcessor, 1),\n      rect: function (values, refer, scales) {\n        return [[values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]], [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]];\n      },\n      polygon: function (values, refer, scales) {\n        return map(values, function (item, idx) {\n          return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];\n        });\n      }\n    };\n\n    function axisDiffProcessor(axisNameIndex, values, refer, scales) {\n      return [values[0] - scales[axisNameIndex] * refer[0], values[1] - scales[axisNameIndex] * refer[1]];\n    } // We have to process scale caused by dataZoom manually,\n    // although it might be not accurate.\n    // Return [0~1, 0~1]\n\n\n    function getScales(xyMinMaxCurr, xyMinMaxOrigin) {\n      var sizeCurr = getSize$1(xyMinMaxCurr);\n      var sizeOrigin = getSize$1(xyMinMaxOrigin);\n      var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];\n      isNaN(scales[0]) && (scales[0] = 1);\n      isNaN(scales[1]) && (scales[1] = 1);\n      return scales;\n    }\n\n    function getSize$1(xyMinMax) {\n      return xyMinMax ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] : [NaN, NaN];\n    }\n\n    var each$6 = each;\n    var DATA_ZOOM_ID_BASE = makeInternalComponentId('toolbox-dataZoom_');\n\n    var DataZoomFeature =\n    /** @class */\n    function (_super) {\n      __extends(DataZoomFeature, _super);\n\n      function DataZoomFeature() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      DataZoomFeature.prototype.render = function (featureModel, ecModel, api, payload) {\n        if (!this._brushController) {\n          this._brushController = new BrushController(api.getZr());\n\n          this._brushController.on('brush', bind(this._onBrush, this)).mount();\n        }\n\n        updateZoomBtnStatus(featureModel, ecModel, this, payload, api);\n        updateBackBtnStatus(featureModel, ecModel);\n      };\n\n      DataZoomFeature.prototype.onclick = function (ecModel, api, type) {\n        handlers[type].call(this);\n      };\n\n      DataZoomFeature.prototype.remove = function (ecModel, api) {\n        this._brushController && this._brushController.unmount();\n      };\n\n      DataZoomFeature.prototype.dispose = function (ecModel, api) {\n        this._brushController && this._brushController.dispose();\n      };\n\n      DataZoomFeature.prototype._onBrush = function (eventParam) {\n        var areas = eventParam.areas;\n\n        if (!eventParam.isEnd || !areas.length) {\n          return;\n        }\n\n        var snapshot = {};\n        var ecModel = this.ecModel;\n\n        this._brushController.updateCovers([]); // remove cover\n\n\n        var brushTargetManager = new BrushTargetManager(makeAxisFinder(this.model), ecModel, {\n          include: ['grid']\n        });\n        brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {\n          if (coordSys.type !== 'cartesian2d') {\n            return;\n          }\n\n          var brushType = area.brushType;\n\n          if (brushType === 'rect') {\n            setBatch('x', coordSys, coordRange[0]);\n            setBatch('y', coordSys, coordRange[1]);\n          } else {\n            setBatch({\n              lineX: 'x',\n              lineY: 'y'\n            }[brushType], coordSys, coordRange);\n          }\n        });\n        push(ecModel, snapshot);\n\n        this._dispatchZoomAction(snapshot);\n\n        function setBatch(dimName, coordSys, minMax) {\n          var axis = coordSys.getAxis(dimName);\n          var axisModel = axis.model;\n          var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); // Restrict range.\n\n          var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan();\n\n          if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) {\n            minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan);\n          }\n\n          dataZoomModel && (snapshot[dataZoomModel.id] = {\n            dataZoomId: dataZoomModel.id,\n            startValue: minMax[0],\n            endValue: minMax[1]\n          });\n        }\n\n        function findDataZoom(dimName, axisModel, ecModel) {\n          var found;\n          ecModel.eachComponent({\n            mainType: 'dataZoom',\n            subType: 'select'\n          }, function (dzModel) {\n            var has = dzModel.getAxisModel(dimName, axisModel.componentIndex);\n            has && (found = dzModel);\n          });\n          return found;\n        }\n      };\n\n      DataZoomFeature.prototype._dispatchZoomAction = function (snapshot) {\n        var batch = []; // Convert from hash map to array.\n\n        each$6(snapshot, function (batchItem, dataZoomId) {\n          batch.push(clone(batchItem));\n        });\n        batch.length && this.api.dispatchAction({\n          type: 'dataZoom',\n          from: this.uid,\n          batch: batch\n        });\n      };\n\n      DataZoomFeature.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          filterMode: 'filter',\n          // Icon group\n          icon: {\n            zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1',\n            back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26'\n          },\n          // `zoom`, `back`\n          title: ecModel.getLocaleModel().get(['toolbox', 'dataZoom', 'title']),\n          brushStyle: {\n            borderWidth: 0,\n            color: 'rgba(210,219,238,0.2)'\n          }\n        };\n        return defaultOption;\n      };\n\n      return DataZoomFeature;\n    }(ToolboxFeature);\n\n    var handlers = {\n      zoom: function () {\n        var nextActive = !this._isZoomActive;\n        this.api.dispatchAction({\n          type: 'takeGlobalCursor',\n          key: 'dataZoomSelect',\n          dataZoomSelectActive: nextActive\n        });\n      },\n      back: function () {\n        this._dispatchZoomAction(pop(this.ecModel));\n      }\n    };\n\n    function makeAxisFinder(dzFeatureModel) {\n      var setting = {\n        xAxisIndex: dzFeatureModel.get('xAxisIndex', true),\n        yAxisIndex: dzFeatureModel.get('yAxisIndex', true),\n        xAxisId: dzFeatureModel.get('xAxisId', true),\n        yAxisId: dzFeatureModel.get('yAxisId', true)\n      }; // If both `xAxisIndex` `xAxisId` not set, it means 'all'.\n      // If both `yAxisIndex` `yAxisId` not set, it means 'all'.\n      // Some old cases set like this below to close yAxis control but leave xAxis control:\n      // `{ feature: { dataZoom: { yAxisIndex: false } }`.\n\n      if (setting.xAxisIndex == null && setting.xAxisId == null) {\n        setting.xAxisIndex = 'all';\n      }\n\n      if (setting.yAxisIndex == null && setting.yAxisId == null) {\n        setting.yAxisIndex = 'all';\n      }\n\n      return setting;\n    }\n\n    function updateBackBtnStatus(featureModel, ecModel) {\n      featureModel.setIconStatus('back', count(ecModel) > 1 ? 'emphasis' : 'normal');\n    }\n\n    function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) {\n      var zoomActive = view._isZoomActive;\n\n      if (payload && payload.type === 'takeGlobalCursor') {\n        zoomActive = payload.key === 'dataZoomSelect' ? payload.dataZoomSelectActive : false;\n      }\n\n      view._isZoomActive = zoomActive;\n      featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal');\n      var brushTargetManager = new BrushTargetManager(makeAxisFinder(featureModel), ecModel, {\n        include: ['grid']\n      });\n      var panels = brushTargetManager.makePanelOpts(api, function (targetInfo) {\n        return targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared ? 'lineX' : !targetInfo.xAxisDeclared && targetInfo.yAxisDeclared ? 'lineY' : 'rect';\n      });\n\n      view._brushController.setPanels(panels).enableBrush(zoomActive && panels.length ? {\n        brushType: 'auto',\n        brushStyle: featureModel.getModel('brushStyle').getItemStyle()\n      } : false);\n    }\n\n    registerInternalOptionCreator('dataZoom', function (ecModel) {\n      var toolboxModel = ecModel.getComponent('toolbox', 0);\n      var featureDataZoomPath = ['feature', 'dataZoom'];\n\n      if (!toolboxModel || toolboxModel.get(featureDataZoomPath) == null) {\n        return;\n      }\n\n      var dzFeatureModel = toolboxModel.getModel(featureDataZoomPath);\n      var dzOptions = [];\n      var finder = makeAxisFinder(dzFeatureModel);\n      var finderResult = parseFinder(ecModel, finder);\n      each$6(finderResult.xAxisModels, function (axisModel) {\n        return buildInternalOptions(axisModel, 'xAxis', 'xAxisIndex');\n      });\n      each$6(finderResult.yAxisModels, function (axisModel) {\n        return buildInternalOptions(axisModel, 'yAxis', 'yAxisIndex');\n      });\n\n      function buildInternalOptions(axisModel, axisMainType, axisIndexPropName) {\n        var axisIndex = axisModel.componentIndex;\n        var newOpt = {\n          type: 'select',\n          $fromToolbox: true,\n          // Default to be filter\n          filterMode: dzFeatureModel.get('filterMode', true) || 'filter',\n          // Id for merge mapping.\n          id: DATA_ZOOM_ID_BASE + axisMainType + axisIndex\n        };\n        newOpt[axisIndexPropName] = axisIndex;\n        dzOptions.push(newOpt);\n      }\n\n      return dzOptions;\n    });\n\n    function install$b(registers) {\n      registers.registerComponentModel(ToolboxModel);\n      registers.registerComponentView(ToolboxView);\n      registerFeature('saveAsImage', SaveAsImage);\n      registerFeature('magicType', MagicType);\n      registerFeature('dataView', DataView);\n      registerFeature('dataZoom', DataZoomFeature);\n      registerFeature('restore', RestoreOption);\n      use(install$a);\n    }\n\n    var TooltipModel =\n    /** @class */\n    function (_super) {\n      __extends(TooltipModel, _super);\n\n      function TooltipModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TooltipModel.type;\n        return _this;\n      }\n\n      TooltipModel.type = 'tooltip';\n      TooltipModel.dependencies = ['axisPointer'];\n      TooltipModel.defaultOption = {\n        // zlevel: 0,\n        z: 60,\n        show: true,\n        // tooltip main content\n        showContent: true,\n        // 'trigger' only works on coordinate system.\n        // 'item' | 'axis' | 'none'\n        trigger: 'item',\n        // 'click' | 'mousemove' | 'none'\n        triggerOn: 'mousemove|click',\n        alwaysShowContent: false,\n        displayMode: 'single',\n        renderMode: 'auto',\n        // whether restraint content inside viewRect.\n        // If renderMode: 'richText', default true.\n        // If renderMode: 'html', defaut false (for backward compat).\n        confine: null,\n        showDelay: 0,\n        hideDelay: 100,\n        // Animation transition time, unit is second\n        transitionDuration: 0.4,\n        enterable: false,\n        backgroundColor: '#fff',\n        // box shadow\n        shadowBlur: 10,\n        shadowColor: 'rgba(0, 0, 0, .2)',\n        shadowOffsetX: 1,\n        shadowOffsetY: 2,\n        // tooltip border radius, unit is px, default is 4\n        borderRadius: 4,\n        // tooltip border width, unit is px, default is 0 (no border)\n        borderWidth: 1,\n        // Tooltip inside padding, default is 5 for all direction\n        // Array is allowed to set up, right, bottom, left, same with css\n        // The default value: See `tooltip/tooltipMarkup.ts#getPaddingFromTooltipModel`.\n        padding: null,\n        // Extra css text\n        extraCssText: '',\n        // axis indicator, trigger by axis\n        axisPointer: {\n          // default is line\n          // legal values: 'line' | 'shadow' | 'cross'\n          type: 'line',\n          // Valid when type is line, appoint tooltip line locate on which line. Optional\n          // legal values: 'x' | 'y' | 'angle' | 'radius' | 'auto'\n          // default is 'auto', chose the axis which type is category.\n          // for multiply y axis, cartesian coord chose x axis, polar chose angle axis\n          axis: 'auto',\n          animation: 'auto',\n          animationDurationUpdate: 200,\n          animationEasingUpdate: 'exponentialOut',\n          crossStyle: {\n            color: '#999',\n            width: 1,\n            type: 'dashed',\n            // TODO formatter\n            textStyle: {}\n          } // lineStyle and shadowStyle should not be specified here,\n          // otherwise it will always override those styles on option.axisPointer.\n\n        },\n        textStyle: {\n          color: '#666',\n          fontSize: 14\n        }\n      };\n      return TooltipModel;\n    }(ComponentModel);\n\n    /* global document */\n\n    function shouldTooltipConfine(tooltipModel) {\n      var confineOption = tooltipModel.get('confine');\n      return confineOption != null ? !!confineOption // In richText mode, the outside part can not be visible.\n      : tooltipModel.get('renderMode') === 'richText';\n    }\n\n    function testStyle(styleProps) {\n      if (!env.domSupported) {\n        return;\n      }\n\n      var style = document.documentElement.style;\n\n      for (var i = 0, len = styleProps.length; i < len; i++) {\n        if (styleProps[i] in style) {\n          return styleProps[i];\n        }\n      }\n    }\n\n    var TRANSFORM_VENDOR = testStyle(['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);\n    var TRANSITION_VENDOR = testStyle(['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);\n    function toCSSVendorPrefix(styleVendor, styleProp) {\n      if (!styleVendor) {\n        return styleProp;\n      }\n\n      styleProp = toCamelCase(styleProp, true);\n      var idx = styleVendor.indexOf(styleProp);\n      styleVendor = idx === -1 ? styleProp : \"-\" + styleVendor.slice(0, idx) + \"-\" + styleProp;\n      return styleVendor.toLowerCase();\n    }\n    function getComputedStyle(el, style) {\n      var stl = el.currentStyle || document.defaultView && document.defaultView.getComputedStyle(el);\n      return stl ? style ? stl[style] : stl : null;\n    }\n\n    /* global document, window */\n\n    var CSS_TRANSITION_VENDOR = toCSSVendorPrefix(TRANSITION_VENDOR, 'transition');\n    var CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(TRANSFORM_VENDOR, 'transform'); // eslint-disable-next-line\n\n    var gCssText = \"position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;\" + (env.transform3dSupported ? 'will-change:transform;' : '');\n\n    function mirrorPos(pos) {\n      pos = pos === 'left' ? 'right' : pos === 'right' ? 'left' : pos === 'top' ? 'bottom' : 'top';\n      return pos;\n    }\n\n    function assembleArrow(tooltipModel, borderColor, arrowPosition) {\n      if (!isString(arrowPosition) || arrowPosition === 'inside') {\n        return '';\n      }\n\n      var backgroundColor = tooltipModel.get('backgroundColor');\n      var borderWidth = tooltipModel.get('borderWidth');\n      borderColor = convertToColorString(borderColor);\n      var arrowPos = mirrorPos(arrowPosition);\n      var arrowSize = Math.max(Math.round(borderWidth) * 1.5, 6);\n      var positionStyle = '';\n      var transformStyle = CSS_TRANSFORM_VENDOR + ':';\n      var rotateDeg;\n\n      if (indexOf(['left', 'right'], arrowPos) > -1) {\n        positionStyle += 'top:50%';\n        transformStyle += \"translateY(-50%) rotate(\" + (rotateDeg = arrowPos === 'left' ? -225 : -45) + \"deg)\";\n      } else {\n        positionStyle += 'left:50%';\n        transformStyle += \"translateX(-50%) rotate(\" + (rotateDeg = arrowPos === 'top' ? 225 : 45) + \"deg)\";\n      }\n\n      var rotateRadian = rotateDeg * Math.PI / 180;\n      var arrowWH = arrowSize + borderWidth;\n      var rotatedWH = arrowWH * Math.abs(Math.cos(rotateRadian)) + arrowWH * Math.abs(Math.sin(rotateRadian));\n      var arrowOffset = Math.round(((rotatedWH - Math.SQRT2 * borderWidth) / 2 + Math.SQRT2 * borderWidth - (rotatedWH - arrowWH) / 2) * 100) / 100;\n      positionStyle += \";\" + arrowPos + \":-\" + arrowOffset + \"px\";\n      var borderStyle = borderColor + \" solid \" + borderWidth + \"px;\";\n      var styleCss = [\"position:absolute;width:\" + arrowSize + \"px;height:\" + arrowSize + \"px;z-index:-1;\", positionStyle + \";\" + transformStyle + \";\", \"border-bottom:\" + borderStyle, \"border-right:\" + borderStyle, \"background-color:\" + backgroundColor + \";\"];\n      return \"<div style=\\\"\" + styleCss.join('') + \"\\\"></div>\";\n    }\n\n    function assembleTransition(duration, onlyFade) {\n      var transitionCurve = 'cubic-bezier(0.23,1,0.32,1)';\n      var transitionOption = \" \" + duration / 2 + \"s \" + transitionCurve;\n      var transitionText = \"opacity\" + transitionOption + \",visibility\" + transitionOption;\n\n      if (!onlyFade) {\n        transitionOption = \" \" + duration + \"s \" + transitionCurve;\n        transitionText += env.transformSupported ? \",\" + CSS_TRANSFORM_VENDOR + transitionOption : \",left\" + transitionOption + \",top\" + transitionOption;\n      }\n\n      return CSS_TRANSITION_VENDOR + ':' + transitionText;\n    }\n\n    function assembleTransform(x, y, toString) {\n      // If using float on style, the final width of the dom might\n      // keep changing slightly while mouse move. So `toFixed(0)` them.\n      var x0 = x.toFixed(0) + 'px';\n      var y0 = y.toFixed(0) + 'px'; // not support transform, use `left` and `top` instead.\n\n      if (!env.transformSupported) {\n        return toString ? \"top:\" + y0 + \";left:\" + x0 + \";\" : [['top', y0], ['left', x0]];\n      } // support transform\n\n\n      var is3d = env.transform3dSupported;\n      var translate = \"translate\" + (is3d ? '3d' : '') + \"(\" + x0 + \",\" + y0 + (is3d ? ',0' : '') + \")\";\n      return toString ? 'top:0;left:0;' + CSS_TRANSFORM_VENDOR + ':' + translate + ';' : [['top', 0], ['left', 0], [TRANSFORM_VENDOR, translate]];\n    }\n    /**\n     * @param {Object} textStyle\n     * @return {string}\n     * @inner\n     */\n\n\n    function assembleFont(textStyleModel) {\n      var cssText = [];\n      var fontSize = textStyleModel.get('fontSize');\n      var color = textStyleModel.getTextColor();\n      color && cssText.push('color:' + color);\n      cssText.push('font:' + textStyleModel.getFont());\n      fontSize // @ts-ignore, leave it to the tooltip refactor.\n      && cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');\n      var shadowColor = textStyleModel.get('textShadowColor');\n      var shadowBlur = textStyleModel.get('textShadowBlur') || 0;\n      var shadowOffsetX = textStyleModel.get('textShadowOffsetX') || 0;\n      var shadowOffsetY = textStyleModel.get('textShadowOffsetY') || 0;\n      shadowColor && shadowBlur && cssText.push('text-shadow:' + shadowOffsetX + 'px ' + shadowOffsetY + 'px ' + shadowBlur + 'px ' + shadowColor);\n      each(['decoration', 'align'], function (name) {\n        var val = textStyleModel.get(name);\n        val && cssText.push('text-' + name + ':' + val);\n      });\n      return cssText.join(';');\n    }\n\n    function assembleCssText(tooltipModel, enableTransition, onlyFade) {\n      var cssText = [];\n      var transitionDuration = tooltipModel.get('transitionDuration');\n      var backgroundColor = tooltipModel.get('backgroundColor');\n      var shadowBlur = tooltipModel.get('shadowBlur');\n      var shadowColor = tooltipModel.get('shadowColor');\n      var shadowOffsetX = tooltipModel.get('shadowOffsetX');\n      var shadowOffsetY = tooltipModel.get('shadowOffsetY');\n      var textStyleModel = tooltipModel.getModel('textStyle');\n      var padding = getPaddingFromTooltipModel(tooltipModel, 'html');\n      var boxShadow = shadowOffsetX + \"px \" + shadowOffsetY + \"px \" + shadowBlur + \"px \" + shadowColor;\n      cssText.push('box-shadow:' + boxShadow); // Animation transition. Do not animate when transitionDuration is 0.\n\n      enableTransition && transitionDuration && cssText.push(assembleTransition(transitionDuration, onlyFade));\n\n      if (backgroundColor) {\n        cssText.push('background-color:' + backgroundColor);\n      } // Border style\n\n\n      each(['width', 'color', 'radius'], function (name) {\n        var borderName = 'border-' + name;\n        var camelCase = toCamelCase(borderName);\n        var val = tooltipModel.get(camelCase);\n        val != null && cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));\n      }); // Text style\n\n      cssText.push(assembleFont(textStyleModel)); // Padding\n\n      if (padding != null) {\n        cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px');\n      }\n\n      return cssText.join(';') + ';';\n    } // If not able to make, do not modify the input `out`.\n\n\n    function makeStyleCoord(out, zr, appendToBody, zrX, zrY) {\n      var zrPainter = zr && zr.painter;\n\n      if (appendToBody) {\n        var zrViewportRoot = zrPainter && zrPainter.getViewportRoot();\n\n        if (zrViewportRoot) {\n          // Some APPs might use scale on body, so we support CSS transform here.\n          transformLocalCoord(out, zrViewportRoot, document.body, zrX, zrY);\n        }\n      } else {\n        out[0] = zrX;\n        out[1] = zrY; // xy should be based on canvas root. But tooltipContent is\n        // the sibling of canvas root. So padding of ec container\n        // should be considered here.\n\n        var viewportRootOffset = zrPainter && zrPainter.getViewportRootOffset();\n\n        if (viewportRootOffset) {\n          out[0] += viewportRootOffset.offsetLeft;\n          out[1] += viewportRootOffset.offsetTop;\n        }\n      }\n\n      out[2] = out[0] / zr.getWidth();\n      out[3] = out[1] / zr.getHeight();\n    }\n\n    var TooltipHTMLContent =\n    /** @class */\n    function () {\n      function TooltipHTMLContent(container, api, opt) {\n        this._show = false;\n        this._styleCoord = [0, 0, 0, 0];\n        this._enterable = true;\n        this._firstShow = true;\n        this._longHide = true;\n\n        if (env.wxa) {\n          return null;\n        }\n\n        var el = document.createElement('div'); // TODO: TYPE\n\n        el.domBelongToZr = true;\n        this.el = el;\n        var zr = this._zr = api.getZr();\n        var appendToBody = this._appendToBody = opt && opt.appendToBody;\n        makeStyleCoord(this._styleCoord, zr, appendToBody, api.getWidth() / 2, api.getHeight() / 2);\n\n        if (appendToBody) {\n          document.body.appendChild(el);\n        } else {\n          container.appendChild(el);\n        }\n\n        this._container = container; // FIXME\n        // Is it needed to trigger zr event manually if\n        // the browser do not support `pointer-events: none`.\n\n        var self = this;\n\n        el.onmouseenter = function () {\n          // clear the timeout in hideLater and keep showing tooltip\n          if (self._enterable) {\n            clearTimeout(self._hideTimeout);\n            self._show = true;\n          }\n\n          self._inContent = true;\n        };\n\n        el.onmousemove = function (e) {\n          e = e || window.event;\n\n          if (!self._enterable) {\n            // `pointer-events: none` is set to tooltip content div\n            // if `enterable` is set as `false`, and `el.onmousemove`\n            // can not be triggered. But in browser that do not\n            // support `pointer-events`, we need to do this:\n            // Try trigger zrender event to avoid mouse\n            // in and out shape too frequently\n            var handler = zr.handler;\n            var zrViewportRoot = zr.painter.getViewportRoot();\n            normalizeEvent(zrViewportRoot, e, true);\n            handler.dispatch('mousemove', e);\n          }\n        };\n\n        el.onmouseleave = function () {\n          // set `_inContent` to `false` before `hideLater`\n          self._inContent = false;\n\n          if (self._enterable) {\n            if (self._show) {\n              self.hideLater(self._hideDelay);\n            }\n          }\n        };\n      }\n      /**\n       * Update when tooltip is rendered\n       */\n\n\n      TooltipHTMLContent.prototype.update = function (tooltipModel) {\n        // FIXME\n        // Move this logic to ec main?\n        var container = this._container;\n        var position = getComputedStyle(container, 'position');\n        var domStyle = container.style;\n\n        if (domStyle.position !== 'absolute' && position !== 'absolute') {\n          domStyle.position = 'relative';\n        } // move tooltip if chart resized\n\n\n        var alwaysShowContent = tooltipModel.get('alwaysShowContent');\n        alwaysShowContent && this._moveIfResized(); // update className\n\n        this.el.className = tooltipModel.get('className') || ''; // Hide the tooltip\n        // PENDING\n        // this.hide();\n      };\n\n      TooltipHTMLContent.prototype.show = function (tooltipModel, nearPointColor) {\n        clearTimeout(this._hideTimeout);\n        clearTimeout(this._longHideTimeout);\n        var el = this.el;\n        var style = el.style;\n        var styleCoord = this._styleCoord;\n\n        if (!el.innerHTML) {\n          style.display = 'none';\n        } else {\n          style.cssText = gCssText + assembleCssText(tooltipModel, !this._firstShow, this._longHide) // initial transform\n          + assembleTransform(styleCoord[0], styleCoord[1], true) + (\"border-color:\" + convertToColorString(nearPointColor) + \";\") + (tooltipModel.get('extraCssText') || '') // If mouse occasionally move over the tooltip, a mouseout event will be\n          // triggered by canvas, and cause some unexpectable result like dragging\n          // stop, \"unfocusAdjacency\". Here `pointer-events: none` is used to solve\n          // it. Although it is not supported by IE8~IE10, fortunately it is a rare\n          // scenario.\n          + (\";pointer-events:\" + (this._enterable ? 'auto' : 'none'));\n        }\n\n        this._show = true;\n        this._firstShow = false;\n        this._longHide = false;\n      };\n\n      TooltipHTMLContent.prototype.setContent = function (content, markers, tooltipModel, borderColor, arrowPosition) {\n        var el = this.el;\n\n        if (content == null) {\n          el.innerHTML = '';\n          return;\n        }\n\n        var arrow = '';\n\n        if (isString(arrowPosition) && tooltipModel.get('trigger') === 'item' && !shouldTooltipConfine(tooltipModel)) {\n          arrow = assembleArrow(tooltipModel, borderColor, arrowPosition);\n        }\n\n        if (isString(content)) {\n          el.innerHTML = content + arrow;\n        } else if (content) {\n          // Clear previous\n          el.innerHTML = '';\n\n          if (!isArray(content)) {\n            content = [content];\n          }\n\n          for (var i = 0; i < content.length; i++) {\n            if (isDom(content[i]) && content[i].parentNode !== el) {\n              el.appendChild(content[i]);\n            }\n          } // no arrow if empty\n\n\n          if (arrow && el.childNodes.length) {\n            // no need to create a new parent element, but it's not supported by IE 10 and older.\n            // const arrowEl = document.createRange().createContextualFragment(arrow);\n            var arrowEl = document.createElement('div');\n            arrowEl.innerHTML = arrow;\n            el.appendChild(arrowEl);\n          }\n        }\n      };\n\n      TooltipHTMLContent.prototype.setEnterable = function (enterable) {\n        this._enterable = enterable;\n      };\n\n      TooltipHTMLContent.prototype.getSize = function () {\n        var el = this.el;\n        return [el.offsetWidth, el.offsetHeight];\n      };\n\n      TooltipHTMLContent.prototype.moveTo = function (zrX, zrY) {\n        var styleCoord = this._styleCoord;\n        makeStyleCoord(styleCoord, this._zr, this._appendToBody, zrX, zrY);\n\n        if (styleCoord[0] != null && styleCoord[1] != null) {\n          var style_1 = this.el.style;\n          var transforms = assembleTransform(styleCoord[0], styleCoord[1]);\n          each(transforms, function (transform) {\n            style_1[transform[0]] = transform[1];\n          });\n        }\n      };\n      /**\n       * when `alwaysShowContent` is true,\n       * move the tooltip after chart resized\n       */\n\n\n      TooltipHTMLContent.prototype._moveIfResized = function () {\n        // The ratio of left to width\n        var ratioX = this._styleCoord[2]; // The ratio of top to height\n\n        var ratioY = this._styleCoord[3];\n        this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight());\n      };\n\n      TooltipHTMLContent.prototype.hide = function () {\n        var _this = this;\n\n        var style = this.el.style;\n        style.visibility = 'hidden';\n        style.opacity = '0';\n        env.transform3dSupported && (style.willChange = '');\n        this._show = false;\n        this._longHideTimeout = setTimeout(function () {\n          return _this._longHide = true;\n        }, 500);\n      };\n\n      TooltipHTMLContent.prototype.hideLater = function (time) {\n        if (this._show && !(this._inContent && this._enterable)) {\n          if (time) {\n            this._hideDelay = time; // Set show false to avoid invoke hideLater multiple times\n\n            this._show = false;\n            this._hideTimeout = setTimeout(bind(this.hide, this), time);\n          } else {\n            this.hide();\n          }\n        }\n      };\n\n      TooltipHTMLContent.prototype.isShow = function () {\n        return this._show;\n      };\n\n      TooltipHTMLContent.prototype.dispose = function () {\n        this.el.parentNode.removeChild(this.el);\n      };\n\n      return TooltipHTMLContent;\n    }();\n\n    var TooltipRichContent =\n    /** @class */\n    function () {\n      function TooltipRichContent(api) {\n        this._show = false;\n        this._styleCoord = [0, 0, 0, 0];\n        this._enterable = true;\n        this._zr = api.getZr();\n        makeStyleCoord$1(this._styleCoord, this._zr, api.getWidth() / 2, api.getHeight() / 2);\n      }\n      /**\n       * Update when tooltip is rendered\n       */\n\n\n      TooltipRichContent.prototype.update = function (tooltipModel) {\n        var alwaysShowContent = tooltipModel.get('alwaysShowContent');\n        alwaysShowContent && this._moveIfResized();\n      };\n\n      TooltipRichContent.prototype.show = function () {\n        if (this._hideTimeout) {\n          clearTimeout(this._hideTimeout);\n        }\n\n        this.el.show();\n        this._show = true;\n      };\n      /**\n       * Set tooltip content\n       */\n\n\n      TooltipRichContent.prototype.setContent = function (content, markupStyleCreator, tooltipModel, borderColor, arrowPosition) {\n        var _this = this;\n\n        if (isObject(content)) {\n          throwError(\"development\" !== 'production' ? 'Passing DOM nodes as content is not supported in richText tooltip!' : '');\n        }\n\n        if (this.el) {\n          this._zr.remove(this.el);\n        }\n\n        var textStyleModel = tooltipModel.getModel('textStyle');\n        this.el = new ZRText({\n          style: {\n            rich: markupStyleCreator.richTextStyles,\n            text: content,\n            lineHeight: 22,\n            borderWidth: 1,\n            borderColor: borderColor,\n            textShadowColor: textStyleModel.get('textShadowColor'),\n            fill: tooltipModel.get(['textStyle', 'color']),\n            padding: getPaddingFromTooltipModel(tooltipModel, 'richText'),\n            verticalAlign: 'top',\n            align: 'left'\n          },\n          z: tooltipModel.get('z')\n        });\n        each(['backgroundColor', 'borderRadius', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'], function (propName) {\n          _this.el.style[propName] = tooltipModel.get(propName);\n        });\n        each(['textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'], function (propName) {\n          _this.el.style[propName] = textStyleModel.get(propName) || 0;\n        });\n\n        this._zr.add(this.el);\n\n        var self = this;\n        this.el.on('mouseover', function () {\n          // clear the timeout in hideLater and keep showing tooltip\n          if (self._enterable) {\n            clearTimeout(self._hideTimeout);\n            self._show = true;\n          }\n\n          self._inContent = true;\n        });\n        this.el.on('mouseout', function () {\n          if (self._enterable) {\n            if (self._show) {\n              self.hideLater(self._hideDelay);\n            }\n          }\n\n          self._inContent = false;\n        });\n      };\n\n      TooltipRichContent.prototype.setEnterable = function (enterable) {\n        this._enterable = enterable;\n      };\n\n      TooltipRichContent.prototype.getSize = function () {\n        var el = this.el;\n        var bounding = this.el.getBoundingRect(); // bounding rect does not include shadow. For renderMode richText,\n        // if overflow, it will be cut. So calculate them accurately.\n\n        var shadowOuterSize = calcShadowOuterSize(el.style);\n        return [bounding.width + shadowOuterSize.left + shadowOuterSize.right, bounding.height + shadowOuterSize.top + shadowOuterSize.bottom];\n      };\n\n      TooltipRichContent.prototype.moveTo = function (x, y) {\n        var el = this.el;\n\n        if (el) {\n          var styleCoord = this._styleCoord;\n          makeStyleCoord$1(styleCoord, this._zr, x, y);\n          x = styleCoord[0];\n          y = styleCoord[1];\n          var style = el.style;\n          var borderWidth = mathMaxWith0(style.borderWidth || 0);\n          var shadowOuterSize = calcShadowOuterSize(style); // rich text x, y do not include border.\n\n          el.x = x + borderWidth + shadowOuterSize.left;\n          el.y = y + borderWidth + shadowOuterSize.top;\n          el.markRedraw();\n        }\n      };\n      /**\n       * when `alwaysShowContent` is true,\n       * move the tooltip after chart resized\n       */\n\n\n      TooltipRichContent.prototype._moveIfResized = function () {\n        // The ratio of left to width\n        var ratioX = this._styleCoord[2]; // The ratio of top to height\n\n        var ratioY = this._styleCoord[3];\n        this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight());\n      };\n\n      TooltipRichContent.prototype.hide = function () {\n        if (this.el) {\n          this.el.hide();\n        }\n\n        this._show = false;\n      };\n\n      TooltipRichContent.prototype.hideLater = function (time) {\n        if (this._show && !(this._inContent && this._enterable)) {\n          if (time) {\n            this._hideDelay = time; // Set show false to avoid invoke hideLater multiple times\n\n            this._show = false;\n            this._hideTimeout = setTimeout(bind(this.hide, this), time);\n          } else {\n            this.hide();\n          }\n        }\n      };\n\n      TooltipRichContent.prototype.isShow = function () {\n        return this._show;\n      };\n\n      TooltipRichContent.prototype.dispose = function () {\n        this._zr.remove(this.el);\n      };\n\n      return TooltipRichContent;\n    }();\n\n    function mathMaxWith0(val) {\n      return Math.max(0, val);\n    }\n\n    function calcShadowOuterSize(style) {\n      var shadowBlur = mathMaxWith0(style.shadowBlur || 0);\n      var shadowOffsetX = mathMaxWith0(style.shadowOffsetX || 0);\n      var shadowOffsetY = mathMaxWith0(style.shadowOffsetY || 0);\n      return {\n        left: mathMaxWith0(shadowBlur - shadowOffsetX),\n        right: mathMaxWith0(shadowBlur + shadowOffsetX),\n        top: mathMaxWith0(shadowBlur - shadowOffsetY),\n        bottom: mathMaxWith0(shadowBlur + shadowOffsetY)\n      };\n    }\n\n    function makeStyleCoord$1(out, zr, zrX, zrY) {\n      out[0] = zrX;\n      out[1] = zrY;\n      out[2] = out[0] / zr.getWidth();\n      out[3] = out[1] / zr.getHeight();\n    }\n\n    var proxyRect = new Rect({\n      shape: {\n        x: -1,\n        y: -1,\n        width: 2,\n        height: 2\n      }\n    });\n\n    var TooltipView =\n    /** @class */\n    function (_super) {\n      __extends(TooltipView, _super);\n\n      function TooltipView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TooltipView.type;\n        return _this;\n      }\n\n      TooltipView.prototype.init = function (ecModel, api) {\n        if (env.node || !api.getDom()) {\n          return;\n        }\n\n        var tooltipModel = ecModel.getComponent('tooltip');\n        var renderMode = this._renderMode = getTooltipRenderMode(tooltipModel.get('renderMode'));\n        this._tooltipContent = renderMode === 'richText' ? new TooltipRichContent(api) : new TooltipHTMLContent(api.getDom(), api, {\n          appendToBody: tooltipModel.get('appendToBody', true)\n        });\n      };\n\n      TooltipView.prototype.render = function (tooltipModel, ecModel, api) {\n        if (env.node || !api.getDom()) {\n          return;\n        } // Reset\n\n\n        this.group.removeAll();\n        this._tooltipModel = tooltipModel;\n        this._ecModel = ecModel;\n        this._api = api;\n        /**\n         * @private\n         * @type {boolean}\n         */\n\n        this._alwaysShowContent = tooltipModel.get('alwaysShowContent');\n        var tooltipContent = this._tooltipContent;\n        tooltipContent.update(tooltipModel);\n        tooltipContent.setEnterable(tooltipModel.get('enterable'));\n\n        this._initGlobalListener();\n\n        this._keepShow(); // PENDING\n        // `mousemove` event will be triggered very frequently when the mouse moves fast,\n        // which causes that the `updatePosition` function was also called frequently.\n        // In Chrome with devtools open and Firefox, tooltip looks laggy and shakes. See #14695 #16101\n        // To avoid frequent triggering,\n        // consider throttling it in 50ms when transition is enabled\n\n\n        if (this._renderMode !== 'richText' && tooltipModel.get('transitionDuration')) {\n          createOrUpdate(this, '_updatePosition', 50, 'fixRate');\n        } else {\n          clear(this, '_updatePosition');\n        }\n      };\n\n      TooltipView.prototype._initGlobalListener = function () {\n        var tooltipModel = this._tooltipModel;\n        var triggerOn = tooltipModel.get('triggerOn');\n        register('itemTooltip', this._api, bind(function (currTrigger, e, dispatchAction) {\n          // If 'none', it is not controlled by mouse totally.\n          if (triggerOn !== 'none') {\n            if (triggerOn.indexOf(currTrigger) >= 0) {\n              this._tryShow(e, dispatchAction);\n            } else if (currTrigger === 'leave') {\n              this._hide(dispatchAction);\n            }\n          }\n        }, this));\n      };\n\n      TooltipView.prototype._keepShow = function () {\n        var tooltipModel = this._tooltipModel;\n        var ecModel = this._ecModel;\n        var api = this._api;\n        var triggerOn = tooltipModel.get('triggerOn'); // Try to keep the tooltip show when refreshing\n\n        if (this._lastX != null && this._lastY != null // When user is willing to control tooltip totally using API,\n        // self.manuallyShowTip({x, y}) might cause tooltip hide,\n        // which is not expected.\n        && triggerOn !== 'none' && triggerOn !== 'click') {\n          var self_1 = this;\n          clearTimeout(this._refreshUpdateTimeout);\n          this._refreshUpdateTimeout = setTimeout(function () {\n            // Show tip next tick after other charts are rendered\n            // In case highlight action has wrong result\n            // FIXME\n            !api.isDisposed() && self_1.manuallyShowTip(tooltipModel, ecModel, api, {\n              x: self_1._lastX,\n              y: self_1._lastY,\n              dataByCoordSys: self_1._lastDataByCoordSys\n            });\n          });\n        }\n      };\n      /**\n       * Show tip manually by\n       * dispatchAction({\n       *     type: 'showTip',\n       *     x: 10,\n       *     y: 10\n       * });\n       * Or\n       * dispatchAction({\n       *      type: 'showTip',\n       *      seriesIndex: 0,\n       *      dataIndex or dataIndexInside or name\n       * });\n       *\n       *  TODO Batch\n       */\n\n\n      TooltipView.prototype.manuallyShowTip = function (tooltipModel, ecModel, api, payload) {\n        if (payload.from === this.uid || env.node || !api.getDom()) {\n          return;\n        }\n\n        var dispatchAction = makeDispatchAction$1(payload, api); // Reset ticket\n\n        this._ticket = ''; // When triggered from axisPointer.\n\n        var dataByCoordSys = payload.dataByCoordSys;\n        var cmptRef = findComponentReference(payload, ecModel, api);\n\n        if (cmptRef) {\n          var rect = cmptRef.el.getBoundingRect().clone();\n          rect.applyTransform(cmptRef.el.transform);\n\n          this._tryShow({\n            offsetX: rect.x + rect.width / 2,\n            offsetY: rect.y + rect.height / 2,\n            target: cmptRef.el,\n            position: payload.position,\n            // When manully trigger, the mouse is not on the el, so we'd better to\n            // position tooltip on the bottom of the el and display arrow is possible.\n            positionDefault: 'bottom'\n          }, dispatchAction);\n        } else if (payload.tooltip && payload.x != null && payload.y != null) {\n          var el = proxyRect;\n          el.x = payload.x;\n          el.y = payload.y;\n          el.update();\n          getECData(el).tooltipConfig = {\n            name: null,\n            option: payload.tooltip\n          }; // Manually show tooltip while view is not using zrender elements.\n\n          this._tryShow({\n            offsetX: payload.x,\n            offsetY: payload.y,\n            target: el\n          }, dispatchAction);\n        } else if (dataByCoordSys) {\n          this._tryShow({\n            offsetX: payload.x,\n            offsetY: payload.y,\n            position: payload.position,\n            dataByCoordSys: dataByCoordSys,\n            tooltipOption: payload.tooltipOption\n          }, dispatchAction);\n        } else if (payload.seriesIndex != null) {\n          if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) {\n            return;\n          }\n\n          var pointInfo = findPointFromSeries(payload, ecModel);\n          var cx = pointInfo.point[0];\n          var cy = pointInfo.point[1];\n\n          if (cx != null && cy != null) {\n            this._tryShow({\n              offsetX: cx,\n              offsetY: cy,\n              target: pointInfo.el,\n              position: payload.position,\n              // When manully trigger, the mouse is not on the el, so we'd better to\n              // position tooltip on the bottom of the el and display arrow is possible.\n              positionDefault: 'bottom'\n            }, dispatchAction);\n          }\n        } else if (payload.x != null && payload.y != null) {\n          // FIXME\n          // should wrap dispatchAction like `axisPointer/globalListener` ?\n          api.dispatchAction({\n            type: 'updateAxisPointer',\n            x: payload.x,\n            y: payload.y\n          });\n\n          this._tryShow({\n            offsetX: payload.x,\n            offsetY: payload.y,\n            position: payload.position,\n            target: api.getZr().findHover(payload.x, payload.y).target\n          }, dispatchAction);\n        }\n      };\n\n      TooltipView.prototype.manuallyHideTip = function (tooltipModel, ecModel, api, payload) {\n        var tooltipContent = this._tooltipContent;\n\n        if (!this._alwaysShowContent && this._tooltipModel) {\n          tooltipContent.hideLater(this._tooltipModel.get('hideDelay'));\n        }\n\n        this._lastX = this._lastY = this._lastDataByCoordSys = null;\n\n        if (payload.from !== this.uid) {\n          this._hide(makeDispatchAction$1(payload, api));\n        }\n      }; // Be compatible with previous design, that is, when tooltip.type is 'axis' and\n      // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer\n      // and tooltip.\n\n\n      TooltipView.prototype._manuallyAxisShowTip = function (tooltipModel, ecModel, api, payload) {\n        var seriesIndex = payload.seriesIndex;\n        var dataIndex = payload.dataIndex; // @ts-ignore\n\n        var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;\n\n        if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {\n          return;\n        }\n\n        var seriesModel = ecModel.getSeriesByIndex(seriesIndex);\n\n        if (!seriesModel) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var tooltipCascadedModel = buildTooltipModel([data.getItemModel(dataIndex), seriesModel, (seriesModel.coordinateSystem || {}).model], this._tooltipModel);\n\n        if (tooltipCascadedModel.get('trigger') !== 'axis') {\n          return;\n        }\n\n        api.dispatchAction({\n          type: 'updateAxisPointer',\n          seriesIndex: seriesIndex,\n          dataIndex: dataIndex,\n          position: payload.position\n        });\n        return true;\n      };\n\n      TooltipView.prototype._tryShow = function (e, dispatchAction) {\n        var el = e.target;\n        var tooltipModel = this._tooltipModel;\n\n        if (!tooltipModel) {\n          return;\n        } // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed\n\n\n        this._lastX = e.offsetX;\n        this._lastY = e.offsetY;\n        var dataByCoordSys = e.dataByCoordSys;\n\n        if (dataByCoordSys && dataByCoordSys.length) {\n          this._showAxisTooltip(dataByCoordSys, e);\n        } else if (el) {\n          this._lastDataByCoordSys = null;\n          var seriesDispatcher_1;\n          var cmptDispatcher_1;\n          findEventDispatcher(el, function (target) {\n            // Always show item tooltip if mouse is on the element with dataIndex\n            if (getECData(target).dataIndex != null) {\n              seriesDispatcher_1 = target;\n              return true;\n            } // Tooltip provided directly. Like legend.\n\n\n            if (getECData(target).tooltipConfig != null) {\n              cmptDispatcher_1 = target;\n              return true;\n            }\n          }, true);\n\n          if (seriesDispatcher_1) {\n            this._showSeriesItemTooltip(e, seriesDispatcher_1, dispatchAction);\n          } else if (cmptDispatcher_1) {\n            this._showComponentItemTooltip(e, cmptDispatcher_1, dispatchAction);\n          } else {\n            this._hide(dispatchAction);\n          }\n        } else {\n          this._lastDataByCoordSys = null;\n\n          this._hide(dispatchAction);\n        }\n      };\n\n      TooltipView.prototype._showOrMove = function (tooltipModel, cb) {\n        // showDelay is used in this case: tooltip.enterable is set\n        // as true. User intent to move mouse into tooltip and click\n        // something. `showDelay` makes it easier to enter the content\n        // but tooltip do not move immediately.\n        var delay = tooltipModel.get('showDelay');\n        cb = bind(cb, this);\n        clearTimeout(this._showTimout);\n        delay > 0 ? this._showTimout = setTimeout(cb, delay) : cb();\n      };\n\n      TooltipView.prototype._showAxisTooltip = function (dataByCoordSys, e) {\n        var ecModel = this._ecModel;\n        var globalTooltipModel = this._tooltipModel;\n        var point = [e.offsetX, e.offsetY];\n        var singleTooltipModel = buildTooltipModel([e.tooltipOption], globalTooltipModel);\n        var renderMode = this._renderMode;\n        var cbParamsList = [];\n        var articleMarkup = createTooltipMarkup('section', {\n          blocks: [],\n          noHeader: true\n        }); // Only for legacy: `Serise['formatTooltip']` returns a string.\n\n        var markupTextArrLegacy = [];\n        var markupStyleCreator = new TooltipMarkupStyleCreator();\n        each(dataByCoordSys, function (itemCoordSys) {\n          each(itemCoordSys.dataByAxis, function (axisItem) {\n            var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex);\n            var axisValue = axisItem.value;\n\n            if (!axisModel || axisValue == null) {\n              return;\n            }\n\n            var axisValueLabel = getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt);\n            var axisSectionMarkup = createTooltipMarkup('section', {\n              header: axisValueLabel,\n              noHeader: !trim(axisValueLabel),\n              sortBlocks: true,\n              blocks: []\n            });\n            articleMarkup.blocks.push(axisSectionMarkup);\n            each(axisItem.seriesDataIndices, function (idxItem) {\n              var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);\n              var dataIndex = idxItem.dataIndexInside;\n              var cbParams = series.getDataParams(dataIndex); // Can't find data.\n\n              if (cbParams.dataIndex < 0) {\n                return;\n              }\n\n              cbParams.axisDim = axisItem.axisDim;\n              cbParams.axisIndex = axisItem.axisIndex;\n              cbParams.axisType = axisItem.axisType;\n              cbParams.axisId = axisItem.axisId;\n              cbParams.axisValue = getAxisRawValue(axisModel.axis, {\n                value: axisValue\n              });\n              cbParams.axisValueLabel = axisValueLabel; // Pre-create marker style for makers. Users can assemble richText\n              // text in `formatter` callback and use those markers style.\n\n              cbParams.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(cbParams.color), renderMode);\n              var seriesTooltipResult = normalizeTooltipFormatResult(series.formatTooltip(dataIndex, true, null));\n              var frag = seriesTooltipResult.frag;\n\n              if (frag) {\n                var valueFormatter = buildTooltipModel([series], globalTooltipModel).get('valueFormatter');\n                axisSectionMarkup.blocks.push(valueFormatter ? extend({\n                  valueFormatter: valueFormatter\n                }, frag) : frag);\n              }\n\n              if (seriesTooltipResult.text) {\n                markupTextArrLegacy.push(seriesTooltipResult.text);\n              }\n\n              cbParamsList.push(cbParams);\n            });\n          });\n        }); // In most cases, the second axis is displays upper on the first one.\n        // So we reverse it to look better.\n\n        articleMarkup.blocks.reverse();\n        markupTextArrLegacy.reverse();\n        var positionExpr = e.position;\n        var orderMode = singleTooltipModel.get('order');\n        var builtMarkupText = buildTooltipMarkup(articleMarkup, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), singleTooltipModel.get('textStyle'));\n        builtMarkupText && markupTextArrLegacy.unshift(builtMarkupText);\n        var blockBreak = renderMode === 'richText' ? '\\n\\n' : '<br/>';\n        var allMarkupText = markupTextArrLegacy.join(blockBreak);\n\n        this._showOrMove(singleTooltipModel, function () {\n          if (this._updateContentNotChangedOnAxis(dataByCoordSys, cbParamsList)) {\n            this._updatePosition(singleTooltipModel, positionExpr, point[0], point[1], this._tooltipContent, cbParamsList);\n          } else {\n            this._showTooltipContent(singleTooltipModel, allMarkupText, cbParamsList, Math.random() + '', point[0], point[1], positionExpr, null, markupStyleCreator);\n          }\n        }); // Do not trigger events here, because this branch only be entered\n        // from dispatchAction.\n\n      };\n\n      TooltipView.prototype._showSeriesItemTooltip = function (e, dispatcher, dispatchAction) {\n        var ecModel = this._ecModel;\n        var ecData = getECData(dispatcher); // Use dataModel in element if possible\n        // Used when mouseover on a element like markPoint or edge\n        // In which case, the data is not main data in series.\n\n        var seriesIndex = ecData.seriesIndex;\n        var seriesModel = ecModel.getSeriesByIndex(seriesIndex); // For example, graph link.\n\n        var dataModel = ecData.dataModel || seriesModel;\n        var dataIndex = ecData.dataIndex;\n        var dataType = ecData.dataType;\n        var data = dataModel.getData(dataType);\n        var renderMode = this._renderMode;\n        var positionDefault = e.positionDefault;\n        var tooltipModel = buildTooltipModel([data.getItemModel(dataIndex), dataModel, seriesModel && (seriesModel.coordinateSystem || {}).model], this._tooltipModel, positionDefault ? {\n          position: positionDefault\n        } : null);\n        var tooltipTrigger = tooltipModel.get('trigger');\n\n        if (tooltipTrigger != null && tooltipTrigger !== 'item') {\n          return;\n        }\n\n        var params = dataModel.getDataParams(dataIndex, dataType);\n        var markupStyleCreator = new TooltipMarkupStyleCreator(); // Pre-create marker style for makers. Users can assemble richText\n        // text in `formatter` callback and use those markers style.\n\n        params.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(params.color), renderMode);\n        var seriesTooltipResult = normalizeTooltipFormatResult(dataModel.formatTooltip(dataIndex, false, dataType));\n        var orderMode = tooltipModel.get('order');\n        var valueFormatter = tooltipModel.get('valueFormatter');\n        var frag = seriesTooltipResult.frag;\n        var markupText = frag ? buildTooltipMarkup(valueFormatter ? extend({\n          valueFormatter: valueFormatter\n        }, frag) : frag, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), tooltipModel.get('textStyle')) : seriesTooltipResult.text;\n        var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;\n\n        this._showOrMove(tooltipModel, function () {\n          this._showTooltipContent(tooltipModel, markupText, params, asyncTicket, e.offsetX, e.offsetY, e.position, e.target, markupStyleCreator);\n        }); // FIXME\n        // duplicated showtip if manuallyShowTip is called from dispatchAction.\n\n\n        dispatchAction({\n          type: 'showTip',\n          dataIndexInside: dataIndex,\n          dataIndex: data.getRawIndex(dataIndex),\n          seriesIndex: seriesIndex,\n          from: this.uid\n        });\n      };\n\n      TooltipView.prototype._showComponentItemTooltip = function (e, el, dispatchAction) {\n        var ecData = getECData(el);\n        var tooltipConfig = ecData.tooltipConfig;\n        var tooltipOpt = tooltipConfig.option || {};\n\n        if (isString(tooltipOpt)) {\n          var content = tooltipOpt;\n          tooltipOpt = {\n            content: content,\n            // Fixed formatter\n            formatter: content\n          };\n        }\n\n        var tooltipModelCascade = [tooltipOpt];\n\n        var cmpt = this._ecModel.getComponent(ecData.componentMainType, ecData.componentIndex);\n\n        if (cmpt) {\n          tooltipModelCascade.push(cmpt);\n        } // In most cases, component tooltip formatter has different params with series tooltip formatter,\n        // so that they can not share the same formatter. Since the global tooltip formatter is used for series\n        // by convension, we do not use it as the default formatter for component.\n\n\n        tooltipModelCascade.push({\n          formatter: tooltipOpt.content\n        });\n        var positionDefault = e.positionDefault;\n        var subTooltipModel = buildTooltipModel(tooltipModelCascade, this._tooltipModel, positionDefault ? {\n          position: positionDefault\n        } : null);\n        var defaultHtml = subTooltipModel.get('content');\n        var asyncTicket = Math.random() + ''; // PENDING: this case do not support richText style yet.\n\n        var markupStyleCreator = new TooltipMarkupStyleCreator(); // Do not check whether `trigger` is 'none' here, because `trigger`\n        // only works on coordinate system. In fact, we have not found case\n        // that requires setting `trigger` nothing on component yet.\n\n        this._showOrMove(subTooltipModel, function () {\n          // Use formatterParams from element defined in component\n          // Avoid users modify it.\n          var formatterParams = clone(subTooltipModel.get('formatterParams') || {});\n\n          this._showTooltipContent(subTooltipModel, defaultHtml, formatterParams, asyncTicket, e.offsetX, e.offsetY, e.position, el, markupStyleCreator);\n        }); // If not dispatch showTip, tip may be hide triggered by axis.\n\n\n        dispatchAction({\n          type: 'showTip',\n          from: this.uid\n        });\n      };\n\n      TooltipView.prototype._showTooltipContent = function ( // Use Model<TooltipOption> insteadof TooltipModel because this model may be from series or other options.\n      // Instead of top level tooltip.\n      tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el, markupStyleCreator) {\n        // Reset ticket\n        this._ticket = '';\n\n        if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) {\n          return;\n        }\n\n        var tooltipContent = this._tooltipContent;\n        tooltipContent.setEnterable(tooltipModel.get('enterable'));\n        var formatter = tooltipModel.get('formatter');\n        positionExpr = positionExpr || tooltipModel.get('position');\n        var html = defaultHtml;\n\n        var nearPoint = this._getNearestPoint([x, y], params, tooltipModel.get('trigger'), tooltipModel.get('borderColor'));\n\n        var nearPointColor = nearPoint.color;\n\n        if (formatter) {\n          if (isString(formatter)) {\n            var useUTC = tooltipModel.ecModel.get('useUTC');\n            var params0 = isArray(params) ? params[0] : params;\n            var isTimeAxis = params0 && params0.axisType && params0.axisType.indexOf('time') >= 0;\n            html = formatter;\n\n            if (isTimeAxis) {\n              html = format(params0.axisValue, html, useUTC);\n            }\n\n            html = formatTpl(html, params, true);\n          } else if (isFunction(formatter)) {\n            var callback = bind(function (cbTicket, html) {\n              if (cbTicket === this._ticket) {\n                tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);\n\n                this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);\n              }\n            }, this);\n            this._ticket = asyncTicket;\n            html = formatter(params, asyncTicket, callback);\n          } else {\n            html = formatter;\n          }\n        }\n\n        tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);\n        tooltipContent.show(tooltipModel, nearPointColor);\n\n        this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);\n      };\n\n      TooltipView.prototype._getNearestPoint = function (point, tooltipDataParams, trigger, borderColor) {\n        if (trigger === 'axis' || isArray(tooltipDataParams)) {\n          return {\n            color: borderColor || (this._renderMode === 'html' ? '#fff' : 'none')\n          };\n        }\n\n        if (!isArray(tooltipDataParams)) {\n          return {\n            color: borderColor || tooltipDataParams.color || tooltipDataParams.borderColor\n          };\n        }\n      };\n\n      TooltipView.prototype._updatePosition = function (tooltipModel, positionExpr, x, // Mouse x\n      y, // Mouse y\n      content, params, el) {\n        var viewWidth = this._api.getWidth();\n\n        var viewHeight = this._api.getHeight();\n\n        positionExpr = positionExpr || tooltipModel.get('position');\n        var contentSize = content.getSize();\n        var align = tooltipModel.get('align');\n        var vAlign = tooltipModel.get('verticalAlign');\n        var rect = el && el.getBoundingRect().clone();\n        el && rect.applyTransform(el.transform);\n\n        if (isFunction(positionExpr)) {\n          // Callback of position can be an array or a string specify the position\n          positionExpr = positionExpr([x, y], params, content.el, rect, {\n            viewSize: [viewWidth, viewHeight],\n            contentSize: contentSize.slice()\n          });\n        }\n\n        if (isArray(positionExpr)) {\n          x = parsePercent$1(positionExpr[0], viewWidth);\n          y = parsePercent$1(positionExpr[1], viewHeight);\n        } else if (isObject(positionExpr)) {\n          var boxLayoutPosition = positionExpr;\n          boxLayoutPosition.width = contentSize[0];\n          boxLayoutPosition.height = contentSize[1];\n          var layoutRect = getLayoutRect(boxLayoutPosition, {\n            width: viewWidth,\n            height: viewHeight\n          });\n          x = layoutRect.x;\n          y = layoutRect.y;\n          align = null; // When positionExpr is left/top/right/bottom,\n          // align and verticalAlign will not work.\n\n          vAlign = null;\n        } // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element\n        else if (isString(positionExpr) && el) {\n            var pos = calcTooltipPosition(positionExpr, rect, contentSize, tooltipModel.get('borderWidth'));\n            x = pos[0];\n            y = pos[1];\n          } else {\n            var pos = refixTooltipPosition(x, y, content, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20);\n            x = pos[0];\n            y = pos[1];\n          }\n\n        align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0);\n        vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);\n\n        if (shouldTooltipConfine(tooltipModel)) {\n          var pos = confineTooltipPosition(x, y, content, viewWidth, viewHeight);\n          x = pos[0];\n          y = pos[1];\n        }\n\n        content.moveTo(x, y);\n      }; // FIXME\n      // Should we remove this but leave this to user?\n\n\n      TooltipView.prototype._updateContentNotChangedOnAxis = function (dataByCoordSys, cbParamsList) {\n        var lastCoordSys = this._lastDataByCoordSys;\n        var lastCbParamsList = this._cbParamsList;\n        var contentNotChanged = !!lastCoordSys && lastCoordSys.length === dataByCoordSys.length;\n        contentNotChanged && each(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {\n          var lastDataByAxis = lastItemCoordSys.dataByAxis || [];\n          var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};\n          var thisDataByAxis = thisItemCoordSys.dataByAxis || [];\n          contentNotChanged = contentNotChanged && lastDataByAxis.length === thisDataByAxis.length;\n          contentNotChanged && each(lastDataByAxis, function (lastItem, indexAxis) {\n            var thisItem = thisDataByAxis[indexAxis] || {};\n            var lastIndices = lastItem.seriesDataIndices || [];\n            var newIndices = thisItem.seriesDataIndices || [];\n            contentNotChanged = contentNotChanged && lastItem.value === thisItem.value && lastItem.axisType === thisItem.axisType && lastItem.axisId === thisItem.axisId && lastIndices.length === newIndices.length;\n            contentNotChanged && each(lastIndices, function (lastIdxItem, j) {\n              var newIdxItem = newIndices[j];\n              contentNotChanged = contentNotChanged && lastIdxItem.seriesIndex === newIdxItem.seriesIndex && lastIdxItem.dataIndex === newIdxItem.dataIndex;\n            }); // check is cbParams data value changed\n\n            lastCbParamsList && each(lastItem.seriesDataIndices, function (idxItem) {\n              var seriesIdx = idxItem.seriesIndex;\n              var cbParams = cbParamsList[seriesIdx];\n              var lastCbParams = lastCbParamsList[seriesIdx];\n\n              if (cbParams && lastCbParams && lastCbParams.data !== cbParams.data) {\n                contentNotChanged = false;\n              }\n            });\n          });\n        });\n        this._lastDataByCoordSys = dataByCoordSys;\n        this._cbParamsList = cbParamsList;\n        return !!contentNotChanged;\n      };\n\n      TooltipView.prototype._hide = function (dispatchAction) {\n        // Do not directly hideLater here, because this behavior may be prevented\n        // in dispatchAction when showTip is dispatched.\n        // FIXME\n        // duplicated hideTip if manuallyHideTip is called from dispatchAction.\n        this._lastDataByCoordSys = null;\n        dispatchAction({\n          type: 'hideTip',\n          from: this.uid\n        });\n      };\n\n      TooltipView.prototype.dispose = function (ecModel, api) {\n        if (env.node || !api.getDom()) {\n          return;\n        }\n\n        clear(this, '_updatePosition');\n\n        this._tooltipContent.dispose();\n\n        unregister('itemTooltip', api);\n      };\n\n      TooltipView.type = 'tooltip';\n      return TooltipView;\n    }(ComponentView);\n    /**\n     * From top to bottom. (the last one should be globalTooltipModel);\n     */\n\n\n    function buildTooltipModel(modelCascade, globalTooltipModel, defaultTooltipOption) {\n      // Last is always tooltip model.\n      var ecModel = globalTooltipModel.ecModel;\n      var resultModel;\n\n      if (defaultTooltipOption) {\n        resultModel = new Model(defaultTooltipOption, ecModel, ecModel);\n        resultModel = new Model(globalTooltipModel.option, resultModel, ecModel);\n      } else {\n        resultModel = globalTooltipModel;\n      }\n\n      for (var i = modelCascade.length - 1; i >= 0; i--) {\n        var tooltipOpt = modelCascade[i];\n\n        if (tooltipOpt) {\n          if (tooltipOpt instanceof Model) {\n            tooltipOpt = tooltipOpt.get('tooltip', true);\n          } // In each data item tooltip can be simply write:\n          // {\n          //  value: 10,\n          //  tooltip: 'Something you need to know'\n          // }\n\n\n          if (isString(tooltipOpt)) {\n            tooltipOpt = {\n              formatter: tooltipOpt\n            };\n          }\n\n          if (tooltipOpt) {\n            resultModel = new Model(tooltipOpt, resultModel, ecModel);\n          }\n        }\n      }\n\n      return resultModel;\n    }\n\n    function makeDispatchAction$1(payload, api) {\n      return payload.dispatchAction || bind(api.dispatchAction, api);\n    }\n\n    function refixTooltipPosition(x, y, content, viewWidth, viewHeight, gapH, gapV) {\n      var size = content.getSize();\n      var width = size[0];\n      var height = size[1];\n\n      if (gapH != null) {\n        // Add extra 2 pixels for this case:\n        // At present the \"values\" in defaut tooltip are using CSS `float: right`.\n        // When the right edge of the tooltip box is on the right side of the\n        // viewport, the `float` layout might push the \"values\" to the second line.\n        if (x + width + gapH + 2 > viewWidth) {\n          x -= width + gapH;\n        } else {\n          x += gapH;\n        }\n      }\n\n      if (gapV != null) {\n        if (y + height + gapV > viewHeight) {\n          y -= height + gapV;\n        } else {\n          y += gapV;\n        }\n      }\n\n      return [x, y];\n    }\n\n    function confineTooltipPosition(x, y, content, viewWidth, viewHeight) {\n      var size = content.getSize();\n      var width = size[0];\n      var height = size[1];\n      x = Math.min(x + width, viewWidth) - width;\n      y = Math.min(y + height, viewHeight) - height;\n      x = Math.max(x, 0);\n      y = Math.max(y, 0);\n      return [x, y];\n    }\n\n    function calcTooltipPosition(position, rect, contentSize, borderWidth) {\n      var domWidth = contentSize[0];\n      var domHeight = contentSize[1];\n      var offset = Math.ceil(Math.SQRT2 * borderWidth) + 8;\n      var x = 0;\n      var y = 0;\n      var rectWidth = rect.width;\n      var rectHeight = rect.height;\n\n      switch (position) {\n        case 'inside':\n          x = rect.x + rectWidth / 2 - domWidth / 2;\n          y = rect.y + rectHeight / 2 - domHeight / 2;\n          break;\n\n        case 'top':\n          x = rect.x + rectWidth / 2 - domWidth / 2;\n          y = rect.y - domHeight - offset;\n          break;\n\n        case 'bottom':\n          x = rect.x + rectWidth / 2 - domWidth / 2;\n          y = rect.y + rectHeight + offset;\n          break;\n\n        case 'left':\n          x = rect.x - domWidth - offset;\n          y = rect.y + rectHeight / 2 - domHeight / 2;\n          break;\n\n        case 'right':\n          x = rect.x + rectWidth + offset;\n          y = rect.y + rectHeight / 2 - domHeight / 2;\n      }\n\n      return [x, y];\n    }\n\n    function isCenterAlign(align) {\n      return align === 'center' || align === 'middle';\n    }\n    /**\n     * Find target component by payload like:\n     * ```js\n     * { legendId: 'some_id', name: 'xxx' }\n     * { toolboxIndex: 1, name: 'xxx' }\n     * { geoName: 'some_name', name: 'xxx' }\n     * ```\n     * PENDING: at present only\n     *\n     * If not found, return null/undefined.\n     */\n\n\n    function findComponentReference(payload, ecModel, api) {\n      var queryOptionMap = preParseFinder(payload).queryOptionMap;\n      var componentMainType = queryOptionMap.keys()[0];\n\n      if (!componentMainType || componentMainType === 'series') {\n        return;\n      }\n\n      var queryResult = queryReferringComponents(ecModel, componentMainType, queryOptionMap.get(componentMainType), {\n        useDefault: false,\n        enableAll: false,\n        enableNone: false\n      });\n      var model = queryResult.models[0];\n\n      if (!model) {\n        return;\n      }\n\n      var view = api.getViewOfComponentModel(model);\n      var el;\n      view.group.traverse(function (subEl) {\n        var tooltipConfig = getECData(subEl).tooltipConfig;\n\n        if (tooltipConfig && tooltipConfig.name === payload.name) {\n          el = subEl;\n          return true; // stop\n        }\n      });\n\n      if (el) {\n        return {\n          componentMainType: componentMainType,\n          componentIndex: model.componentIndex,\n          el: el\n        };\n      }\n    }\n\n    function install$c(registers) {\n      use(install$7);\n      registers.registerComponentModel(TooltipModel);\n      registers.registerComponentView(TooltipView);\n      /**\n       * @action\n       * @property {string} type\n       * @property {number} seriesIndex\n       * @property {number} dataIndex\n       * @property {number} [x]\n       * @property {number} [y]\n       */\n\n      registers.registerAction({\n        type: 'showTip',\n        event: 'showTip',\n        update: 'tooltip:manuallyShowTip'\n      }, noop);\n      registers.registerAction({\n        type: 'hideTip',\n        event: 'hideTip',\n        update: 'tooltip:manuallyHideTip'\n      }, noop);\n    }\n\n    var TitleModel =\n    /** @class */\n    function (_super) {\n      __extends(TitleModel, _super);\n\n      function TitleModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TitleModel.type;\n        _this.layoutMode = {\n          type: 'box',\n          ignoreSize: true\n        };\n        return _this;\n      }\n\n      TitleModel.type = 'title';\n      TitleModel.defaultOption = {\n        // zlevel: 0,\n        z: 6,\n        show: true,\n        text: '',\n        target: 'blank',\n        subtext: '',\n        subtarget: 'blank',\n        left: 0,\n        top: 0,\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderColor: '#ccc',\n        borderWidth: 0,\n        padding: 5,\n        itemGap: 10,\n        textStyle: {\n          fontSize: 18,\n          fontWeight: 'bold',\n          color: '#464646'\n        },\n        subtextStyle: {\n          fontSize: 12,\n          color: '#6E7079'\n        }\n      };\n      return TitleModel;\n    }(ComponentModel); // View\n\n\n    var TitleView =\n    /** @class */\n    function (_super) {\n      __extends(TitleView, _super);\n\n      function TitleView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TitleView.type;\n        return _this;\n      }\n\n      TitleView.prototype.render = function (titleModel, ecModel, api) {\n        this.group.removeAll();\n\n        if (!titleModel.get('show')) {\n          return;\n        }\n\n        var group = this.group;\n        var textStyleModel = titleModel.getModel('textStyle');\n        var subtextStyleModel = titleModel.getModel('subtextStyle');\n        var textAlign = titleModel.get('textAlign');\n        var textVerticalAlign = retrieve2(titleModel.get('textBaseline'), titleModel.get('textVerticalAlign'));\n        var textEl = new ZRText({\n          style: createTextStyle(textStyleModel, {\n            text: titleModel.get('text'),\n            fill: textStyleModel.getTextColor()\n          }, {\n            disableBox: true\n          }),\n          z2: 10\n        });\n        var textRect = textEl.getBoundingRect();\n        var subText = titleModel.get('subtext');\n        var subTextEl = new ZRText({\n          style: createTextStyle(subtextStyleModel, {\n            text: subText,\n            fill: subtextStyleModel.getTextColor(),\n            y: textRect.height + titleModel.get('itemGap'),\n            verticalAlign: 'top'\n          }, {\n            disableBox: true\n          }),\n          z2: 10\n        });\n        var link = titleModel.get('link');\n        var sublink = titleModel.get('sublink');\n        var triggerEvent = titleModel.get('triggerEvent', true);\n        textEl.silent = !link && !triggerEvent;\n        subTextEl.silent = !sublink && !triggerEvent;\n\n        if (link) {\n          textEl.on('click', function () {\n            windowOpen(link, '_' + titleModel.get('target'));\n          });\n        }\n\n        if (sublink) {\n          subTextEl.on('click', function () {\n            windowOpen(sublink, '_' + titleModel.get('subtarget'));\n          });\n        }\n\n        getECData(textEl).eventData = getECData(subTextEl).eventData = triggerEvent ? {\n          componentType: 'title',\n          componentIndex: titleModel.componentIndex\n        } : null;\n        group.add(textEl);\n        subText && group.add(subTextEl); // If no subText, but add subTextEl, there will be an empty line.\n\n        var groupRect = group.getBoundingRect();\n        var layoutOption = titleModel.getBoxLayoutParams();\n        layoutOption.width = groupRect.width;\n        layoutOption.height = groupRect.height;\n        var layoutRect = getLayoutRect(layoutOption, {\n          width: api.getWidth(),\n          height: api.getHeight()\n        }, titleModel.get('padding')); // Adjust text align based on position\n\n        if (!textAlign) {\n          // Align left if title is on the left. center and right is same\n          textAlign = titleModel.get('left') || titleModel.get('right'); // @ts-ignore\n\n          if (textAlign === 'middle') {\n            textAlign = 'center';\n          } // Adjust layout by text align\n\n\n          if (textAlign === 'right') {\n            layoutRect.x += layoutRect.width;\n          } else if (textAlign === 'center') {\n            layoutRect.x += layoutRect.width / 2;\n          }\n        }\n\n        if (!textVerticalAlign) {\n          textVerticalAlign = titleModel.get('top') || titleModel.get('bottom'); // @ts-ignore\n\n          if (textVerticalAlign === 'center') {\n            textVerticalAlign = 'middle';\n          }\n\n          if (textVerticalAlign === 'bottom') {\n            layoutRect.y += layoutRect.height;\n          } else if (textVerticalAlign === 'middle') {\n            layoutRect.y += layoutRect.height / 2;\n          }\n\n          textVerticalAlign = textVerticalAlign || 'top';\n        }\n\n        group.x = layoutRect.x;\n        group.y = layoutRect.y;\n        group.markRedraw();\n        var alignStyle = {\n          align: textAlign,\n          verticalAlign: textVerticalAlign\n        };\n        textEl.setStyle(alignStyle);\n        subTextEl.setStyle(alignStyle); // Render background\n        // Get groupRect again because textAlign has been changed\n\n        groupRect = group.getBoundingRect();\n        var padding = layoutRect.margin;\n        var style = titleModel.getItemStyle(['color', 'opacity']);\n        style.fill = titleModel.get('backgroundColor');\n        var rect = new Rect({\n          shape: {\n            x: groupRect.x - padding[3],\n            y: groupRect.y - padding[0],\n            width: groupRect.width + padding[1] + padding[3],\n            height: groupRect.height + padding[0] + padding[2],\n            r: titleModel.get('borderRadius')\n          },\n          style: style,\n          subPixelOptimize: true,\n          silent: true\n        });\n        group.add(rect);\n      };\n\n      TitleView.type = 'title';\n      return TitleView;\n    }(ComponentView);\n\n    function install$d(registers) {\n      registers.registerComponentModel(TitleModel);\n      registers.registerComponentView(TitleView);\n    }\n\n    function checkMarkerInSeries(seriesOpts, markerType) {\n      if (!seriesOpts) {\n        return false;\n      }\n\n      var seriesOptArr = isArray(seriesOpts) ? seriesOpts : [seriesOpts];\n\n      for (var idx = 0; idx < seriesOptArr.length; idx++) {\n        if (seriesOptArr[idx] && seriesOptArr[idx][markerType]) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    function fillLabel(opt) {\n      defaultEmphasis(opt, 'label', ['show']);\n    } // { [componentType]: MarkerModel }\n\n\n    var inner$c = makeInner();\n\n    var MarkerModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkerModel, _super);\n\n      function MarkerModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkerModel.type;\n        /**\n         * If marker model is created by self from series\n         */\n\n        _this.createdBySelf = false;\n        return _this;\n      }\n      /**\n       * @overrite\n       */\n\n\n      MarkerModel.prototype.init = function (option, parentModel, ecModel) {\n        if (\"development\" !== 'production') {\n          if (this.type === 'marker') {\n            throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.');\n          }\n        }\n\n        this.mergeDefaultAndTheme(option, ecModel);\n\n        this._mergeOption(option, ecModel, false, true);\n      };\n\n      MarkerModel.prototype.isAnimationEnabled = function () {\n        if (env.node) {\n          return false;\n        }\n\n        var hostSeries = this.__hostSeries;\n        return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled();\n      };\n      /**\n       * @overrite\n       */\n\n\n      MarkerModel.prototype.mergeOption = function (newOpt, ecModel) {\n        this._mergeOption(newOpt, ecModel, false, false);\n      };\n\n      MarkerModel.prototype._mergeOption = function (newOpt, ecModel, createdBySelf, isInit) {\n        var componentType = this.mainType;\n\n        if (!createdBySelf) {\n          ecModel.eachSeries(function (seriesModel) {\n            // mainType can be markPoint, markLine, markArea\n            var markerOpt = seriesModel.get(this.mainType, true);\n            var markerModel = inner$c(seriesModel)[componentType];\n\n            if (!markerOpt || !markerOpt.data) {\n              inner$c(seriesModel)[componentType] = null;\n              return;\n            }\n\n            if (!markerModel) {\n              if (isInit) {\n                // Default label emphasis `position` and `show`\n                fillLabel(markerOpt);\n              }\n\n              each(markerOpt.data, function (item) {\n                // FIXME Overwrite fillLabel method ?\n                if (item instanceof Array) {\n                  fillLabel(item[0]);\n                  fillLabel(item[1]);\n                } else {\n                  fillLabel(item);\n                }\n              });\n              markerModel = this.createMarkerModelFromSeries(markerOpt, this, ecModel); // markerModel = new ImplementedMarkerModel(\n              //     markerOpt, this, ecModel\n              // );\n\n              extend(markerModel, {\n                mainType: this.mainType,\n                // Use the same series index and name\n                seriesIndex: seriesModel.seriesIndex,\n                name: seriesModel.name,\n                createdBySelf: true\n              });\n              markerModel.__hostSeries = seriesModel;\n            } else {\n              markerModel._mergeOption(markerOpt, ecModel, true);\n            }\n\n            inner$c(seriesModel)[componentType] = markerModel;\n          }, this);\n        }\n      };\n\n      MarkerModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        var data = this.getData();\n        var value = this.getRawValue(dataIndex);\n        var itemName = data.getName(dataIndex);\n        return createTooltipMarkup('section', {\n          header: this.name,\n          blocks: [createTooltipMarkup('nameValue', {\n            name: itemName,\n            value: value,\n            noName: !itemName,\n            noValue: value == null\n          })]\n        });\n      };\n\n      MarkerModel.prototype.getData = function () {\n        return this._data;\n      };\n\n      MarkerModel.prototype.setData = function (data) {\n        this._data = data;\n      };\n\n      MarkerModel.getMarkerModelFromSeries = function (seriesModel, // Support three types of markers. Strict check.\n      componentType) {\n        return inner$c(seriesModel)[componentType];\n      };\n\n      MarkerModel.type = 'marker';\n      MarkerModel.dependencies = ['series', 'grid', 'polar', 'geo'];\n      return MarkerModel;\n    }(ComponentModel);\n\n    mixin(MarkerModel, DataFormatMixin.prototype);\n\n    var MarkPointModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkPointModel, _super);\n\n      function MarkPointModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkPointModel.type;\n        return _this;\n      }\n\n      MarkPointModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n        return new MarkPointModel(markerOpt, masterMarkerModel, ecModel);\n      };\n\n      MarkPointModel.type = 'markPoint';\n      MarkPointModel.defaultOption = {\n        // zlevel: 0,\n        z: 5,\n        symbol: 'pin',\n        symbolSize: 50,\n        // symbolRotate: 0,\n        // symbolOffset: [0, 0]\n        tooltip: {\n          trigger: 'item'\n        },\n        label: {\n          show: true,\n          position: 'inside'\n        },\n        itemStyle: {\n          borderWidth: 2\n        },\n        emphasis: {\n          label: {\n            show: true\n          }\n        }\n      };\n      return MarkPointModel;\n    }(MarkerModel);\n\n    function hasXOrY(item) {\n      return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y)));\n    }\n\n    function hasXAndY(item) {\n      return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y));\n    }\n\n    function markerTypeCalculatorWithExtent(markerType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex) {\n      var coordArr = [];\n      var stacked = isDimensionStacked(data, targetDataDim\n      /* , otherDataDim */\n      );\n      var calcDataDim = stacked ? data.getCalculationInfo('stackResultDimension') : targetDataDim;\n      var value = numCalculate(data, calcDataDim, markerType);\n      var dataIndex = data.indicesOfNearest(calcDataDim, value)[0];\n      coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex);\n      coordArr[targetCoordIndex] = data.get(calcDataDim, dataIndex);\n      var coordArrValue = data.get(targetDataDim, dataIndex); // Make it simple, do not visit all stacked value to count precision.\n\n      var precision = getPrecision(data.get(targetDataDim, dataIndex));\n      precision = Math.min(precision, 20);\n\n      if (precision >= 0) {\n        coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision);\n      }\n\n      return [coordArr, coordArrValue];\n    } // TODO Specified percent\n\n\n    var markerTypeCalculator = {\n      min: curry(markerTypeCalculatorWithExtent, 'min'),\n      max: curry(markerTypeCalculatorWithExtent, 'max'),\n      average: curry(markerTypeCalculatorWithExtent, 'average'),\n      median: curry(markerTypeCalculatorWithExtent, 'median')\n    };\n    /**\n     * Transform markPoint data item to format used in List by do the following\n     * 1. Calculate statistic like `max`, `min`, `average`\n     * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array\n     */\n\n    function dataTransform(seriesModel, item) {\n      if (!item) {\n        return;\n      }\n\n      var data = seriesModel.getData();\n      var coordSys = seriesModel.coordinateSystem;\n      var dims = coordSys.dimensions; // 1. If not specify the position with pixel directly\n      // 2. If `coord` is not a data array. Which uses `xAxis`,\n      // `yAxis` to specify the coord on each dimension\n      // parseFloat first because item.x and item.y can be percent string like '20%'\n\n      if (!hasXAndY(item) && !isArray(item.coord) && coordSys) {\n        var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); // Clone the option\n        // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value\n\n        item = clone(item);\n\n        if (item.type && markerTypeCalculator[item.type] && axisInfo.baseAxis && axisInfo.valueAxis) {\n          var otherCoordIndex = indexOf(dims, axisInfo.baseAxis.dim);\n          var targetCoordIndex = indexOf(dims, axisInfo.valueAxis.dim);\n          var coordInfo = markerTypeCalculator[item.type](data, axisInfo.baseDataDim, axisInfo.valueDataDim, otherCoordIndex, targetCoordIndex);\n          item.coord = coordInfo[0]; // Force to use the value of calculated value.\n          // let item use the value without stack.\n\n          item.value = coordInfo[1];\n        } else {\n          // FIXME Only has one of xAxis and yAxis.\n          item.coord = [item.xAxis != null ? item.xAxis : item.radiusAxis, item.yAxis != null ? item.yAxis : item.angleAxis];\n        }\n      } // x y is provided\n\n\n      if (item.coord == null) {\n        item.coord = [];\n      } else {\n        // Each coord support max, min, average\n        var coord = item.coord;\n\n        for (var i = 0; i < 2; i++) {\n          if (markerTypeCalculator[coord[i]]) {\n            coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]);\n          }\n        }\n      }\n\n      return item;\n    }\n    function getAxisInfo$1(item, data, coordSys, seriesModel) {\n      var ret = {};\n\n      if (item.valueIndex != null || item.valueDim != null) {\n        ret.valueDataDim = item.valueIndex != null ? data.getDimension(item.valueIndex) : item.valueDim;\n        ret.valueAxis = coordSys.getAxis(dataDimToCoordDim(seriesModel, ret.valueDataDim));\n        ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis);\n        ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);\n      } else {\n        ret.baseAxis = seriesModel.getBaseAxis();\n        ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis);\n        ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);\n        ret.valueDataDim = data.mapDimension(ret.valueAxis.dim);\n      }\n\n      return ret;\n    }\n\n    function dataDimToCoordDim(seriesModel, dataDim) {\n      var dimItem = seriesModel.getData().getDimensionInfo(dataDim);\n      return dimItem && dimItem.coordDim;\n    }\n    /**\n     * Filter data which is out of coordinateSystem range\n     * [dataFilter description]\n     */\n\n\n    function dataFilter$1( // Currently only polar and cartesian has containData.\n    coordSys, item) {\n      // Always return true if there is no coordSys\n      return coordSys && coordSys.containData && item.coord && !hasXOrY(item) ? coordSys.containData(item.coord) : true;\n    }\n    function zoneFilter( // Currently only polar and cartesian has containData.\n    coordSys, item1, item2) {\n      // Always return true if there is no coordSys\n      return coordSys && coordSys.containZone && item1.coord && item2.coord && !hasXOrY(item1) && !hasXOrY(item2) ? coordSys.containZone(item1.coord, item2.coord) : true;\n    }\n    function createMarkerDimValueGetter(inCoordSys, dims) {\n      return inCoordSys ? function (item, dimName, dataIndex, dimIndex) {\n        var rawVal = dimIndex < 2 // x, y, radius, angle\n        ? item.coord && item.coord[dimIndex] : item.value;\n        return parseDataValue(rawVal, dims[dimIndex]);\n      } : function (item, dimName, dataIndex, dimIndex) {\n        return parseDataValue(item.value, dims[dimIndex]);\n      };\n    }\n    function numCalculate(data, valueDataDim, type) {\n      if (type === 'average') {\n        var sum_1 = 0;\n        var count_1 = 0;\n        data.each(valueDataDim, function (val, idx) {\n          if (!isNaN(val)) {\n            sum_1 += val;\n            count_1++;\n          }\n        });\n        return sum_1 / count_1;\n      } else if (type === 'median') {\n        return data.getMedian(valueDataDim);\n      } else {\n        // max & min\n        return data.getDataExtent(valueDataDim)[type === 'max' ? 1 : 0];\n      }\n    }\n\n    var inner$d = makeInner();\n\n    var MarkerView =\n    /** @class */\n    function (_super) {\n      __extends(MarkerView, _super);\n\n      function MarkerView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkerView.type;\n        return _this;\n      }\n\n      MarkerView.prototype.init = function () {\n        this.markerGroupMap = createHashMap();\n      };\n\n      MarkerView.prototype.render = function (markerModel, ecModel, api) {\n        var _this = this;\n\n        var markerGroupMap = this.markerGroupMap;\n        markerGroupMap.each(function (item) {\n          inner$d(item).keep = false;\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type);\n          markerModel && _this.renderSeries(seriesModel, markerModel, ecModel, api);\n        });\n        markerGroupMap.each(function (item) {\n          !inner$d(item).keep && _this.group.remove(item.group);\n        });\n      };\n\n      MarkerView.prototype.markKeep = function (drawGroup) {\n        inner$d(drawGroup).keep = true;\n      };\n\n      MarkerView.prototype.toggleBlurSeries = function (seriesModelList, isBlur) {\n        var _this = this;\n\n        each(seriesModelList, function (seriesModel) {\n          var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type);\n\n          if (markerModel) {\n            var data = markerModel.getData();\n            data.eachItemGraphicEl(function (el) {\n              if (el) {\n                isBlur ? enterBlur(el) : leaveBlur(el);\n              }\n            });\n          }\n        });\n      };\n\n      MarkerView.type = 'marker';\n      return MarkerView;\n    }(ComponentView);\n\n    function updateMarkerLayout(mpData, seriesModel, api) {\n      var coordSys = seriesModel.coordinateSystem;\n      mpData.each(function (idx) {\n        var itemModel = mpData.getItemModel(idx);\n        var point;\n        var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());\n        var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());\n\n        if (!isNaN(xPx) && !isNaN(yPx)) {\n          point = [xPx, yPx];\n        } // Chart like bar may have there own marker positioning logic\n        else if (seriesModel.getMarkerPosition) {\n            // Use the getMarkerPosition\n            point = seriesModel.getMarkerPosition(mpData.getValues(mpData.dimensions, idx));\n          } else if (coordSys) {\n            var x = mpData.get(coordSys.dimensions[0], idx);\n            var y = mpData.get(coordSys.dimensions[1], idx);\n            point = coordSys.dataToPoint([x, y]);\n          } // Use x, y if has any\n\n\n        if (!isNaN(xPx)) {\n          point[0] = xPx;\n        }\n\n        if (!isNaN(yPx)) {\n          point[1] = yPx;\n        }\n\n        mpData.setItemLayout(idx, point);\n      });\n    }\n\n    var MarkPointView =\n    /** @class */\n    function (_super) {\n      __extends(MarkPointView, _super);\n\n      function MarkPointView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkPointView.type;\n        return _this;\n      }\n\n      MarkPointView.prototype.updateTransform = function (markPointModel, ecModel, api) {\n        ecModel.eachSeries(function (seriesModel) {\n          var mpModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markPoint');\n\n          if (mpModel) {\n            updateMarkerLayout(mpModel.getData(), seriesModel, api);\n            this.markerGroupMap.get(seriesModel.id).updateLayout();\n          }\n        }, this);\n      };\n\n      MarkPointView.prototype.renderSeries = function (seriesModel, mpModel, ecModel, api) {\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesId = seriesModel.id;\n        var seriesData = seriesModel.getData();\n        var symbolDrawMap = this.markerGroupMap;\n        var symbolDraw = symbolDrawMap.get(seriesId) || symbolDrawMap.set(seriesId, new SymbolDraw());\n        var mpData = createData(coordSys, seriesModel, mpModel); // FIXME\n\n        mpModel.setData(mpData);\n        updateMarkerLayout(mpModel.getData(), seriesModel, api);\n        mpData.each(function (idx) {\n          var itemModel = mpData.getItemModel(idx);\n          var symbol = itemModel.getShallow('symbol');\n          var symbolSize = itemModel.getShallow('symbolSize');\n          var symbolRotate = itemModel.getShallow('symbolRotate');\n          var symbolOffset = itemModel.getShallow('symbolOffset');\n          var symbolKeepAspect = itemModel.getShallow('symbolKeepAspect'); // TODO: refactor needed: single data item should not support callback function\n\n          if (isFunction(symbol) || isFunction(symbolSize) || isFunction(symbolRotate) || isFunction(symbolOffset)) {\n            var rawIdx = mpModel.getRawValue(idx);\n            var dataParams = mpModel.getDataParams(idx);\n\n            if (isFunction(symbol)) {\n              symbol = symbol(rawIdx, dataParams);\n            }\n\n            if (isFunction(symbolSize)) {\n              // FIXME 这里不兼容 ECharts 2.x，2.x 貌似参数是整个数据？\n              symbolSize = symbolSize(rawIdx, dataParams);\n            }\n\n            if (isFunction(symbolRotate)) {\n              symbolRotate = symbolRotate(rawIdx, dataParams);\n            }\n\n            if (isFunction(symbolOffset)) {\n              symbolOffset = symbolOffset(rawIdx, dataParams);\n            }\n          }\n\n          var style = itemModel.getModel('itemStyle').getItemStyle();\n          var color = getVisualFromData(seriesData, 'color');\n\n          if (!style.fill) {\n            style.fill = color;\n          }\n\n          mpData.setItemVisual(idx, {\n            symbol: symbol,\n            symbolSize: symbolSize,\n            symbolRotate: symbolRotate,\n            symbolOffset: symbolOffset,\n            symbolKeepAspect: symbolKeepAspect,\n            style: style\n          });\n        }); // TODO Text are wrong\n\n        symbolDraw.updateData(mpData);\n        this.group.add(symbolDraw.group); // Set host model for tooltip\n        // FIXME\n\n        mpData.eachItemGraphicEl(function (el) {\n          el.traverse(function (child) {\n            getECData(child).dataModel = mpModel;\n          });\n        });\n        this.markKeep(symbolDraw);\n        symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent');\n      };\n\n      MarkPointView.type = 'markPoint';\n      return MarkPointView;\n    }(MarkerView);\n\n    function createData(coordSys, seriesModel, mpModel) {\n      var coordDimsInfos;\n\n      if (coordSys) {\n        coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {\n          var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n          return extend(extend({}, info), {\n            name: coordDim,\n            // DON'T use ordinalMeta to parse and collect ordinal.\n            ordinalMeta: null\n          });\n        });\n      } else {\n        coordDimsInfos = [{\n          name: 'value',\n          type: 'float'\n        }];\n      }\n\n      var mpData = new SeriesData(coordDimsInfos, mpModel);\n      var dataOpt = map(mpModel.get('data'), curry(dataTransform, seriesModel));\n\n      if (coordSys) {\n        dataOpt = filter(dataOpt, curry(dataFilter$1, coordSys));\n      }\n\n      var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos);\n      mpData.initData(dataOpt, null, dimValueGetter);\n      return mpData;\n    }\n\n    function install$e(registers) {\n      registers.registerComponentModel(MarkPointModel);\n      registers.registerComponentView(MarkPointView);\n      registers.registerPreprocessor(function (opt) {\n        if (checkMarkerInSeries(opt.series, 'markPoint')) {\n          // Make sure markPoint component is enabled\n          opt.markPoint = opt.markPoint || {};\n        }\n      });\n    }\n\n    var MarkLineModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkLineModel, _super);\n\n      function MarkLineModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkLineModel.type;\n        return _this;\n      }\n\n      MarkLineModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n        return new MarkLineModel(markerOpt, masterMarkerModel, ecModel);\n      };\n\n      MarkLineModel.type = 'markLine';\n      MarkLineModel.defaultOption = {\n        // zlevel: 0,\n        z: 5,\n        symbol: ['circle', 'arrow'],\n        symbolSize: [8, 16],\n        // symbolRotate: 0,\n        symbolOffset: 0,\n        precision: 2,\n        tooltip: {\n          trigger: 'item'\n        },\n        label: {\n          show: true,\n          position: 'end',\n          distance: 5\n        },\n        lineStyle: {\n          type: 'dashed'\n        },\n        emphasis: {\n          label: {\n            show: true\n          },\n          lineStyle: {\n            width: 3\n          }\n        },\n        animationEasing: 'linear'\n      };\n      return MarkLineModel;\n    }(MarkerModel);\n\n    var straightLineProto = Line.prototype;\n    var bezierCurveProto = BezierCurve.prototype;\n\n    var StraightLineShape =\n    /** @class */\n    function () {\n      function StraightLineShape() {\n        // Start point\n        this.x1 = 0;\n        this.y1 = 0; // End point\n\n        this.x2 = 0;\n        this.y2 = 0;\n        this.percent = 1;\n      }\n\n      return StraightLineShape;\n    }();\n\n    var CurveShape =\n    /** @class */\n    function (_super) {\n      __extends(CurveShape, _super);\n\n      function CurveShape() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return CurveShape;\n    }(StraightLineShape);\n\n    function isStraightLine(shape) {\n      return isNaN(+shape.cpx1) || isNaN(+shape.cpy1);\n    }\n\n    var ECLinePath =\n    /** @class */\n    function (_super) {\n      __extends(ECLinePath, _super);\n\n      function ECLinePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-line';\n        return _this;\n      }\n\n      ECLinePath.prototype.getDefaultStyle = function () {\n        return {\n          stroke: '#000',\n          fill: null\n        };\n      };\n\n      ECLinePath.prototype.getDefaultShape = function () {\n        return new StraightLineShape();\n      };\n\n      ECLinePath.prototype.buildPath = function (ctx, shape) {\n        if (isStraightLine(shape)) {\n          straightLineProto.buildPath.call(this, ctx, shape);\n        } else {\n          bezierCurveProto.buildPath.call(this, ctx, shape);\n        }\n      };\n\n      ECLinePath.prototype.pointAt = function (t) {\n        if (isStraightLine(this.shape)) {\n          return straightLineProto.pointAt.call(this, t);\n        } else {\n          return bezierCurveProto.pointAt.call(this, t);\n        }\n      };\n\n      ECLinePath.prototype.tangentAt = function (t) {\n        var shape = this.shape;\n        var p = isStraightLine(shape) ? [shape.x2 - shape.x1, shape.y2 - shape.y1] : bezierCurveProto.tangentAt.call(this, t);\n        return normalize(p, p);\n      };\n\n      return ECLinePath;\n    }(Path);\n\n    var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];\n\n    function makeSymbolTypeKey(symbolCategory) {\n      return '_' + symbolCategory + 'Type';\n    }\n    /**\n     * @inner\n     */\n\n\n    function createSymbol$1(name, lineData, idx) {\n      var symbolType = lineData.getItemVisual(idx, name);\n\n      if (!symbolType || symbolType === 'none') {\n        return;\n      }\n\n      var symbolSize = lineData.getItemVisual(idx, name + 'Size');\n      var symbolRotate = lineData.getItemVisual(idx, name + 'Rotate');\n      var symbolOffset = lineData.getItemVisual(idx, name + 'Offset');\n      var symbolKeepAspect = lineData.getItemVisual(idx, name + 'KeepAspect');\n      var symbolSizeArr = normalizeSymbolSize(symbolSize);\n      var symbolOffsetArr = normalizeSymbolOffset(symbolOffset || 0, symbolSizeArr);\n      var symbolPath = createSymbol(symbolType, -symbolSizeArr[0] / 2 + symbolOffsetArr[0], -symbolSizeArr[1] / 2 + symbolOffsetArr[1], symbolSizeArr[0], symbolSizeArr[1], null, symbolKeepAspect);\n      symbolPath.__specifiedRotation = symbolRotate == null || isNaN(symbolRotate) ? void 0 : +symbolRotate * Math.PI / 180 || 0;\n      symbolPath.name = name;\n      return symbolPath;\n    }\n\n    function createLine(points) {\n      var line = new ECLinePath({\n        name: 'line',\n        subPixelOptimize: true\n      });\n      setLinePoints(line.shape, points);\n      return line;\n    }\n\n    function setLinePoints(targetShape, points) {\n      targetShape.x1 = points[0][0];\n      targetShape.y1 = points[0][1];\n      targetShape.x2 = points[1][0];\n      targetShape.y2 = points[1][1];\n      targetShape.percent = 1;\n      var cp1 = points[2];\n\n      if (cp1) {\n        targetShape.cpx1 = cp1[0];\n        targetShape.cpy1 = cp1[1];\n      } else {\n        targetShape.cpx1 = NaN;\n        targetShape.cpy1 = NaN;\n      }\n    }\n\n    var Line$1 =\n    /** @class */\n    function (_super) {\n      __extends(Line, _super);\n\n      function Line(lineData, idx, seriesScope) {\n        var _this = _super.call(this) || this;\n\n        _this._createLine(lineData, idx, seriesScope);\n\n        return _this;\n      }\n\n      Line.prototype._createLine = function (lineData, idx, seriesScope) {\n        var seriesModel = lineData.hostModel;\n        var linePoints = lineData.getItemLayout(idx);\n        var line = createLine(linePoints);\n        line.shape.percent = 0;\n        initProps(line, {\n          shape: {\n            percent: 1\n          }\n        }, seriesModel, idx);\n        this.add(line);\n        each(SYMBOL_CATEGORIES, function (symbolCategory) {\n          var symbol = createSymbol$1(symbolCategory, lineData, idx); // symbols must added after line to make sure\n          // it will be updated after line#update.\n          // Or symbol position and rotation update in line#beforeUpdate will be one frame slow\n\n          this.add(symbol);\n          this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);\n        }, this);\n\n        this._updateCommonStl(lineData, idx, seriesScope);\n      }; // TODO More strict on the List type in parameters?\n\n\n      Line.prototype.updateData = function (lineData, idx, seriesScope) {\n        var seriesModel = lineData.hostModel;\n        var line = this.childOfName('line');\n        var linePoints = lineData.getItemLayout(idx);\n        var target = {\n          shape: {}\n        };\n        setLinePoints(target.shape, linePoints);\n        updateProps(line, target, seriesModel, idx);\n        each(SYMBOL_CATEGORIES, function (symbolCategory) {\n          var symbolType = lineData.getItemVisual(idx, symbolCategory);\n          var key = makeSymbolTypeKey(symbolCategory); // Symbol changed\n\n          if (this[key] !== symbolType) {\n            this.remove(this.childOfName(symbolCategory));\n            var symbol = createSymbol$1(symbolCategory, lineData, idx);\n            this.add(symbol);\n          }\n\n          this[key] = symbolType;\n        }, this);\n\n        this._updateCommonStl(lineData, idx, seriesScope);\n      };\n\n      Line.prototype.getLinePath = function () {\n        return this.childAt(0);\n      };\n\n      Line.prototype._updateCommonStl = function (lineData, idx, seriesScope) {\n        var seriesModel = lineData.hostModel;\n        var line = this.childOfName('line');\n        var emphasisLineStyle = seriesScope && seriesScope.emphasisLineStyle;\n        var blurLineStyle = seriesScope && seriesScope.blurLineStyle;\n        var selectLineStyle = seriesScope && seriesScope.selectLineStyle;\n        var labelStatesModels = seriesScope && seriesScope.labelStatesModels;\n        var emphasisDisabled = seriesScope && seriesScope.emphasisDisabled;\n        var focus = seriesScope && seriesScope.focus;\n        var blurScope = seriesScope && seriesScope.blurScope; // Optimization for large dataset\n\n        if (!seriesScope || lineData.hasItemOption) {\n          var itemModel = lineData.getItemModel(idx);\n          var emphasisModel = itemModel.getModel('emphasis');\n          emphasisLineStyle = emphasisModel.getModel('lineStyle').getLineStyle();\n          blurLineStyle = itemModel.getModel(['blur', 'lineStyle']).getLineStyle();\n          selectLineStyle = itemModel.getModel(['select', 'lineStyle']).getLineStyle();\n          emphasisDisabled = emphasisModel.get('disabled');\n          focus = emphasisModel.get('focus');\n          blurScope = emphasisModel.get('blurScope');\n          labelStatesModels = getLabelStatesModels(itemModel);\n        }\n\n        var lineStyle = lineData.getItemVisual(idx, 'style');\n        var visualColor = lineStyle.stroke;\n        line.useStyle(lineStyle);\n        line.style.fill = null;\n        line.style.strokeNoScale = true;\n        line.ensureState('emphasis').style = emphasisLineStyle;\n        line.ensureState('blur').style = blurLineStyle;\n        line.ensureState('select').style = selectLineStyle; // Update symbol\n\n        each(SYMBOL_CATEGORIES, function (symbolCategory) {\n          var symbol = this.childOfName(symbolCategory);\n\n          if (symbol) {\n            // Share opacity and color with line.\n            symbol.setColor(visualColor);\n            symbol.style.opacity = lineStyle.opacity;\n\n            for (var i = 0; i < SPECIAL_STATES.length; i++) {\n              var stateName = SPECIAL_STATES[i];\n              var lineState = line.getState(stateName);\n\n              if (lineState) {\n                var lineStateStyle = lineState.style || {};\n                var state = symbol.ensureState(stateName);\n                var stateStyle = state.style || (state.style = {});\n\n                if (lineStateStyle.stroke != null) {\n                  stateStyle[symbol.__isEmptyBrush ? 'stroke' : 'fill'] = lineStateStyle.stroke;\n                }\n\n                if (lineStateStyle.opacity != null) {\n                  stateStyle.opacity = lineStateStyle.opacity;\n                }\n              }\n            }\n\n            symbol.markRedraw();\n          }\n        }, this);\n        var rawVal = seriesModel.getRawValue(idx);\n        setLabelStyle(this, labelStatesModels, {\n          labelDataIndex: idx,\n          labelFetcher: {\n            getFormattedLabel: function (dataIndex, stateName) {\n              return seriesModel.getFormattedLabel(dataIndex, stateName, lineData.dataType);\n            }\n          },\n          inheritColor: visualColor || '#000',\n          defaultOpacity: lineStyle.opacity,\n          defaultText: (rawVal == null ? lineData.getName(idx) : isFinite(rawVal) ? round(rawVal) : rawVal) + ''\n        });\n        var label = this.getTextContent(); // Always set `textStyle` even if `normalStyle.text` is null, because default\n        // values have to be set on `normalStyle`.\n\n        if (label) {\n          var labelNormalModel = labelStatesModels.normal;\n          label.__align = label.style.align;\n          label.__verticalAlign = label.style.verticalAlign; // 'start', 'middle', 'end'\n\n          label.__position = labelNormalModel.get('position') || 'middle';\n          var distance = labelNormalModel.get('distance');\n\n          if (!isArray(distance)) {\n            distance = [distance, distance];\n          }\n\n          label.__labelDistance = distance;\n        }\n\n        this.setTextConfig({\n          position: null,\n          local: true,\n          inside: false // Can't be inside for stroke element.\n\n        });\n        toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n      };\n\n      Line.prototype.highlight = function () {\n        enterEmphasis(this);\n      };\n\n      Line.prototype.downplay = function () {\n        leaveEmphasis(this);\n      };\n\n      Line.prototype.updateLayout = function (lineData, idx) {\n        this.setLinePoints(lineData.getItemLayout(idx));\n      };\n\n      Line.prototype.setLinePoints = function (points) {\n        var linePath = this.childOfName('line');\n        setLinePoints(linePath.shape, points);\n        linePath.dirty();\n      };\n\n      Line.prototype.beforeUpdate = function () {\n        var lineGroup = this;\n        var symbolFrom = lineGroup.childOfName('fromSymbol');\n        var symbolTo = lineGroup.childOfName('toSymbol');\n        var label = lineGroup.getTextContent(); // Quick reject\n\n        if (!symbolFrom && !symbolTo && (!label || label.ignore)) {\n          return;\n        }\n\n        var invScale = 1;\n        var parentNode = this.parent;\n\n        while (parentNode) {\n          if (parentNode.scaleX) {\n            invScale /= parentNode.scaleX;\n          }\n\n          parentNode = parentNode.parent;\n        }\n\n        var line = lineGroup.childOfName('line'); // If line not changed\n        // FIXME Parent scale changed\n\n        if (!this.__dirty && !line.__dirty) {\n          return;\n        }\n\n        var percent = line.shape.percent;\n        var fromPos = line.pointAt(0);\n        var toPos = line.pointAt(percent);\n        var d = sub([], toPos, fromPos);\n        normalize(d, d);\n\n        function setSymbolRotation(symbol, percent) {\n          // Fix #12388\n          // when symbol is set to be 'arrow' in markLine,\n          // symbolRotate value will be ignored, and compulsively use tangent angle.\n          // rotate by default if symbol rotation is not specified\n          var specifiedRotation = symbol.__specifiedRotation;\n\n          if (specifiedRotation == null) {\n            var tangent = line.tangentAt(percent);\n            symbol.attr('rotation', (percent === 1 ? -1 : 1) * Math.PI / 2 - Math.atan2(tangent[1], tangent[0]));\n          } else {\n            symbol.attr('rotation', specifiedRotation);\n          }\n        }\n\n        if (symbolFrom) {\n          symbolFrom.setPosition(fromPos);\n          setSymbolRotation(symbolFrom, 0);\n          symbolFrom.scaleX = symbolFrom.scaleY = invScale * percent;\n          symbolFrom.markRedraw();\n        }\n\n        if (symbolTo) {\n          symbolTo.setPosition(toPos);\n          setSymbolRotation(symbolTo, 1);\n          symbolTo.scaleX = symbolTo.scaleY = invScale * percent;\n          symbolTo.markRedraw();\n        }\n\n        if (label && !label.ignore) {\n          label.x = label.y = 0;\n          label.originX = label.originY = 0;\n          var textAlign = void 0;\n          var textVerticalAlign = void 0;\n          var distance = label.__labelDistance;\n          var distanceX = distance[0] * invScale;\n          var distanceY = distance[1] * invScale;\n          var halfPercent = percent / 2;\n          var tangent = line.tangentAt(halfPercent);\n          var n = [tangent[1], -tangent[0]];\n          var cp = line.pointAt(halfPercent);\n\n          if (n[1] > 0) {\n            n[0] = -n[0];\n            n[1] = -n[1];\n          }\n\n          var dir = tangent[0] < 0 ? -1 : 1;\n\n          if (label.__position !== 'start' && label.__position !== 'end') {\n            var rotation = -Math.atan2(tangent[1], tangent[0]);\n\n            if (toPos[0] < fromPos[0]) {\n              rotation = Math.PI + rotation;\n            }\n\n            label.rotation = rotation;\n          }\n\n          var dy = void 0;\n\n          switch (label.__position) {\n            case 'insideStartTop':\n            case 'insideMiddleTop':\n            case 'insideEndTop':\n            case 'middle':\n              dy = -distanceY;\n              textVerticalAlign = 'bottom';\n              break;\n\n            case 'insideStartBottom':\n            case 'insideMiddleBottom':\n            case 'insideEndBottom':\n              dy = distanceY;\n              textVerticalAlign = 'top';\n              break;\n\n            default:\n              dy = 0;\n              textVerticalAlign = 'middle';\n          }\n\n          switch (label.__position) {\n            case 'end':\n              label.x = d[0] * distanceX + toPos[0];\n              label.y = d[1] * distanceY + toPos[1];\n              textAlign = d[0] > 0.8 ? 'left' : d[0] < -0.8 ? 'right' : 'center';\n              textVerticalAlign = d[1] > 0.8 ? 'top' : d[1] < -0.8 ? 'bottom' : 'middle';\n              break;\n\n            case 'start':\n              label.x = -d[0] * distanceX + fromPos[0];\n              label.y = -d[1] * distanceY + fromPos[1];\n              textAlign = d[0] > 0.8 ? 'right' : d[0] < -0.8 ? 'left' : 'center';\n              textVerticalAlign = d[1] > 0.8 ? 'bottom' : d[1] < -0.8 ? 'top' : 'middle';\n              break;\n\n            case 'insideStartTop':\n            case 'insideStart':\n            case 'insideStartBottom':\n              label.x = distanceX * dir + fromPos[0];\n              label.y = fromPos[1] + dy;\n              textAlign = tangent[0] < 0 ? 'right' : 'left';\n              label.originX = -distanceX * dir;\n              label.originY = -dy;\n              break;\n\n            case 'insideMiddleTop':\n            case 'insideMiddle':\n            case 'insideMiddleBottom':\n            case 'middle':\n              label.x = cp[0];\n              label.y = cp[1] + dy;\n              textAlign = 'center';\n              label.originY = -dy;\n              break;\n\n            case 'insideEndTop':\n            case 'insideEnd':\n            case 'insideEndBottom':\n              label.x = -distanceX * dir + toPos[0];\n              label.y = toPos[1] + dy;\n              textAlign = tangent[0] >= 0 ? 'right' : 'left';\n              label.originX = distanceX * dir;\n              label.originY = -dy;\n              break;\n          }\n\n          label.scaleX = label.scaleY = invScale;\n          label.setStyle({\n            // Use the user specified text align and baseline first\n            verticalAlign: label.__verticalAlign || textVerticalAlign,\n            align: label.__align || textAlign\n          });\n        }\n      };\n\n      return Line;\n    }(Group);\n\n    var LineDraw =\n    /** @class */\n    function () {\n      function LineDraw(LineCtor) {\n        this.group = new Group();\n        this._LineCtor = LineCtor || Line$1;\n      }\n\n      LineDraw.prototype.updateData = function (lineData) {\n        var _this = this; // Remove progressive els.\n\n\n        this._progressiveEls = null;\n        var lineDraw = this;\n        var group = lineDraw.group;\n        var oldLineData = lineDraw._lineData;\n        lineDraw._lineData = lineData; // There is no oldLineData only when first rendering or switching from\n        // stream mode to normal mode, where previous elements should be removed.\n\n        if (!oldLineData) {\n          group.removeAll();\n        }\n\n        var seriesScope = makeSeriesScope$1(lineData);\n        lineData.diff(oldLineData).add(function (idx) {\n          _this._doAdd(lineData, idx, seriesScope);\n        }).update(function (newIdx, oldIdx) {\n          _this._doUpdate(oldLineData, lineData, oldIdx, newIdx, seriesScope);\n        }).remove(function (idx) {\n          group.remove(oldLineData.getItemGraphicEl(idx));\n        }).execute();\n      };\n\n      LineDraw.prototype.updateLayout = function () {\n        var lineData = this._lineData; // Do not support update layout in incremental mode.\n\n        if (!lineData) {\n          return;\n        }\n\n        lineData.eachItemGraphicEl(function (el, idx) {\n          el.updateLayout(lineData, idx);\n        }, this);\n      };\n\n      LineDraw.prototype.incrementalPrepareUpdate = function (lineData) {\n        this._seriesScope = makeSeriesScope$1(lineData);\n        this._lineData = null;\n        this.group.removeAll();\n      };\n\n      LineDraw.prototype.incrementalUpdate = function (taskParams, lineData) {\n        this._progressiveEls = [];\n\n        function updateIncrementalAndHover(el) {\n          if (!el.isGroup && !isEffectObject(el)) {\n            el.incremental = true;\n            el.ensureState('emphasis').hoverLayer = true;\n          }\n        }\n\n        for (var idx = taskParams.start; idx < taskParams.end; idx++) {\n          var itemLayout = lineData.getItemLayout(idx);\n\n          if (lineNeedsDraw(itemLayout)) {\n            var el = new this._LineCtor(lineData, idx, this._seriesScope);\n            el.traverse(updateIncrementalAndHover);\n            this.group.add(el);\n            lineData.setItemGraphicEl(idx, el);\n\n            this._progressiveEls.push(el);\n          }\n        }\n      };\n\n      LineDraw.prototype.remove = function () {\n        this.group.removeAll();\n      };\n\n      LineDraw.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      LineDraw.prototype._doAdd = function (lineData, idx, seriesScope) {\n        var itemLayout = lineData.getItemLayout(idx);\n\n        if (!lineNeedsDraw(itemLayout)) {\n          return;\n        }\n\n        var el = new this._LineCtor(lineData, idx, seriesScope);\n        lineData.setItemGraphicEl(idx, el);\n        this.group.add(el);\n      };\n\n      LineDraw.prototype._doUpdate = function (oldLineData, newLineData, oldIdx, newIdx, seriesScope) {\n        var itemEl = oldLineData.getItemGraphicEl(oldIdx);\n\n        if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {\n          this.group.remove(itemEl);\n          return;\n        }\n\n        if (!itemEl) {\n          itemEl = new this._LineCtor(newLineData, newIdx, seriesScope);\n        } else {\n          itemEl.updateData(newLineData, newIdx, seriesScope);\n        }\n\n        newLineData.setItemGraphicEl(newIdx, itemEl);\n        this.group.add(itemEl);\n      };\n\n      return LineDraw;\n    }();\n\n    function isEffectObject(el) {\n      return el.animators && el.animators.length > 0;\n    }\n\n    function makeSeriesScope$1(lineData) {\n      var hostModel = lineData.hostModel;\n      var emphasisModel = hostModel.getModel('emphasis');\n      return {\n        lineStyle: hostModel.getModel('lineStyle').getLineStyle(),\n        emphasisLineStyle: emphasisModel.getModel(['lineStyle']).getLineStyle(),\n        blurLineStyle: hostModel.getModel(['blur', 'lineStyle']).getLineStyle(),\n        selectLineStyle: hostModel.getModel(['select', 'lineStyle']).getLineStyle(),\n        emphasisDisabled: emphasisModel.get('disabled'),\n        blurScope: emphasisModel.get('blurScope'),\n        focus: emphasisModel.get('focus'),\n        labelStatesModels: getLabelStatesModels(hostModel)\n      };\n    }\n\n    function isPointNaN(pt) {\n      return isNaN(pt[0]) || isNaN(pt[1]);\n    }\n\n    function lineNeedsDraw(pts) {\n      return pts && !isPointNaN(pts[0]) && !isPointNaN(pts[1]);\n    }\n\n    var inner$e = makeInner();\n\n    var markLineTransform = function (seriesModel, coordSys, mlModel, item) {\n      var data = seriesModel.getData();\n      var itemArray;\n\n      if (!isArray(item)) {\n        // Special type markLine like 'min', 'max', 'average', 'median'\n        var mlType = item.type;\n\n        if (mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median' // In case\n        // data: [{\n        //   yAxis: 10\n        // }]\n        || item.xAxis != null || item.yAxis != null) {\n          var valueAxis = void 0;\n          var value = void 0;\n\n          if (item.yAxis != null || item.xAxis != null) {\n            valueAxis = coordSys.getAxis(item.yAxis != null ? 'y' : 'x');\n            value = retrieve(item.yAxis, item.xAxis);\n          } else {\n            var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel);\n            valueAxis = axisInfo.valueAxis;\n            var valueDataDim = getStackedDimension(data, axisInfo.valueDataDim);\n            value = numCalculate(data, valueDataDim, mlType);\n          }\n\n          var valueIndex = valueAxis.dim === 'x' ? 0 : 1;\n          var baseIndex = 1 - valueIndex; // Normized to 2d data with start and end point\n\n          var mlFrom = clone(item);\n          var mlTo = {\n            coord: []\n          };\n          mlFrom.type = null;\n          mlFrom.coord = [];\n          mlFrom.coord[baseIndex] = -Infinity;\n          mlTo.coord[baseIndex] = Infinity;\n          var precision = mlModel.get('precision');\n\n          if (precision >= 0 && isNumber(value)) {\n            value = +value.toFixed(Math.min(precision, 20));\n          }\n\n          mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;\n          itemArray = [mlFrom, mlTo, {\n            type: mlType,\n            valueIndex: item.valueIndex,\n            // Force to use the value of calculated value.\n            value: value\n          }];\n        } else {\n          // Invalid data\n          if (\"development\" !== 'production') {\n            logError('Invalid markLine data.');\n          }\n\n          itemArray = [];\n        }\n      } else {\n        itemArray = item;\n      }\n\n      var normalizedItem = [dataTransform(seriesModel, itemArray[0]), dataTransform(seriesModel, itemArray[1]), extend({}, itemArray[2])]; // Avoid line data type is extended by from(to) data type\n\n      normalizedItem[2].type = normalizedItem[2].type || null; // Merge from option and to option into line option\n\n      merge(normalizedItem[2], normalizedItem[0]);\n      merge(normalizedItem[2], normalizedItem[1]);\n      return normalizedItem;\n    };\n\n    function isInfinity(val) {\n      return !isNaN(val) && !isFinite(val);\n    } // If a markLine has one dim\n\n\n    function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {\n      var otherDimIndex = 1 - dimIndex;\n      var dimName = coordSys.dimensions[dimIndex];\n      return isInfinity(fromCoord[otherDimIndex]) && isInfinity(toCoord[otherDimIndex]) && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);\n    }\n\n    function markLineFilter(coordSys, item) {\n      if (coordSys.type === 'cartesian2d') {\n        var fromCoord = item[0].coord;\n        var toCoord = item[1].coord; // In case\n        // {\n        //  markLine: {\n        //    data: [{ yAxis: 2 }]\n        //  }\n        // }\n\n        if (fromCoord && toCoord && (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))) {\n          return true;\n        }\n      }\n\n      return dataFilter$1(coordSys, item[0]) && dataFilter$1(coordSys, item[1]);\n    }\n\n    function updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api) {\n      var coordSys = seriesModel.coordinateSystem;\n      var itemModel = data.getItemModel(idx);\n      var point;\n      var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());\n      var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());\n\n      if (!isNaN(xPx) && !isNaN(yPx)) {\n        point = [xPx, yPx];\n      } else {\n        // Chart like bar may have there own marker positioning logic\n        if (seriesModel.getMarkerPosition) {\n          // Use the getMarkerPosition\n          point = seriesModel.getMarkerPosition(data.getValues(data.dimensions, idx));\n        } else {\n          var dims = coordSys.dimensions;\n          var x = data.get(dims[0], idx);\n          var y = data.get(dims[1], idx);\n          point = coordSys.dataToPoint([x, y]);\n        } // Expand line to the edge of grid if value on one axis is Inifnity\n        // In case\n        //  markLine: {\n        //    data: [{\n        //      yAxis: 2\n        //      // or\n        //      type: 'average'\n        //    }]\n        //  }\n\n\n        if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n          // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug\n          var xAxis = coordSys.getAxis('x');\n          var yAxis = coordSys.getAxis('y');\n          var dims = coordSys.dimensions;\n\n          if (isInfinity(data.get(dims[0], idx))) {\n            point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);\n          } else if (isInfinity(data.get(dims[1], idx))) {\n            point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);\n          }\n        } // Use x, y if has any\n\n\n        if (!isNaN(xPx)) {\n          point[0] = xPx;\n        }\n\n        if (!isNaN(yPx)) {\n          point[1] = yPx;\n        }\n      }\n\n      data.setItemLayout(idx, point);\n    }\n\n    var MarkLineView =\n    /** @class */\n    function (_super) {\n      __extends(MarkLineView, _super);\n\n      function MarkLineView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkLineView.type;\n        return _this;\n      }\n\n      MarkLineView.prototype.updateTransform = function (markLineModel, ecModel, api) {\n        ecModel.eachSeries(function (seriesModel) {\n          var mlModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markLine');\n\n          if (mlModel) {\n            var mlData_1 = mlModel.getData();\n            var fromData_1 = inner$e(mlModel).from;\n            var toData_1 = inner$e(mlModel).to; // Update visual and layout of from symbol and to symbol\n\n            fromData_1.each(function (idx) {\n              updateSingleMarkerEndLayout(fromData_1, idx, true, seriesModel, api);\n              updateSingleMarkerEndLayout(toData_1, idx, false, seriesModel, api);\n            }); // Update layout of line\n\n            mlData_1.each(function (idx) {\n              mlData_1.setItemLayout(idx, [fromData_1.getItemLayout(idx), toData_1.getItemLayout(idx)]);\n            });\n            this.markerGroupMap.get(seriesModel.id).updateLayout();\n          }\n        }, this);\n      };\n\n      MarkLineView.prototype.renderSeries = function (seriesModel, mlModel, ecModel, api) {\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesId = seriesModel.id;\n        var seriesData = seriesModel.getData();\n        var lineDrawMap = this.markerGroupMap;\n        var lineDraw = lineDrawMap.get(seriesId) || lineDrawMap.set(seriesId, new LineDraw());\n        this.group.add(lineDraw.group);\n        var mlData = createList$1(coordSys, seriesModel, mlModel);\n        var fromData = mlData.from;\n        var toData = mlData.to;\n        var lineData = mlData.line;\n        inner$e(mlModel).from = fromData;\n        inner$e(mlModel).to = toData; // Line data for tooltip and formatter\n\n        mlModel.setData(lineData); // TODO\n        // Functionally, `symbolSize` & `symbolOffset` can also be 2D array now.\n        // But the related logic and type definition are not finished yet.\n        // Finish it if required\n\n        var symbolType = mlModel.get('symbol');\n        var symbolSize = mlModel.get('symbolSize');\n        var symbolRotate = mlModel.get('symbolRotate');\n        var symbolOffset = mlModel.get('symbolOffset'); // TODO: support callback function like markPoint\n\n        if (!isArray(symbolType)) {\n          symbolType = [symbolType, symbolType];\n        }\n\n        if (!isArray(symbolSize)) {\n          symbolSize = [symbolSize, symbolSize];\n        }\n\n        if (!isArray(symbolRotate)) {\n          symbolRotate = [symbolRotate, symbolRotate];\n        }\n\n        if (!isArray(symbolOffset)) {\n          symbolOffset = [symbolOffset, symbolOffset];\n        } // Update visual and layout of from symbol and to symbol\n\n\n        mlData.from.each(function (idx) {\n          updateDataVisualAndLayout(fromData, idx, true);\n          updateDataVisualAndLayout(toData, idx, false);\n        }); // Update visual and layout of line\n\n        lineData.each(function (idx) {\n          var lineStyle = lineData.getItemModel(idx).getModel('lineStyle').getLineStyle(); // lineData.setItemVisual(idx, {\n          //     color: lineColor || fromData.getItemVisual(idx, 'color')\n          // });\n\n          lineData.setItemLayout(idx, [fromData.getItemLayout(idx), toData.getItemLayout(idx)]);\n\n          if (lineStyle.stroke == null) {\n            lineStyle.stroke = fromData.getItemVisual(idx, 'style').fill;\n          }\n\n          lineData.setItemVisual(idx, {\n            fromSymbolKeepAspect: fromData.getItemVisual(idx, 'symbolKeepAspect'),\n            fromSymbolOffset: fromData.getItemVisual(idx, 'symbolOffset'),\n            fromSymbolRotate: fromData.getItemVisual(idx, 'symbolRotate'),\n            fromSymbolSize: fromData.getItemVisual(idx, 'symbolSize'),\n            fromSymbol: fromData.getItemVisual(idx, 'symbol'),\n            toSymbolKeepAspect: toData.getItemVisual(idx, 'symbolKeepAspect'),\n            toSymbolOffset: toData.getItemVisual(idx, 'symbolOffset'),\n            toSymbolRotate: toData.getItemVisual(idx, 'symbolRotate'),\n            toSymbolSize: toData.getItemVisual(idx, 'symbolSize'),\n            toSymbol: toData.getItemVisual(idx, 'symbol'),\n            style: lineStyle\n          });\n        });\n        lineDraw.updateData(lineData); // Set host model for tooltip\n        // FIXME\n\n        mlData.line.eachItemGraphicEl(function (el) {\n          getECData(el).dataModel = mlModel;\n          el.traverse(function (child) {\n            getECData(child).dataModel = mlModel;\n          });\n        });\n\n        function updateDataVisualAndLayout(data, idx, isFrom) {\n          var itemModel = data.getItemModel(idx);\n          updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api);\n          var style = itemModel.getModel('itemStyle').getItemStyle();\n\n          if (style.fill == null) {\n            style.fill = getVisualFromData(seriesData, 'color');\n          }\n\n          data.setItemVisual(idx, {\n            symbolKeepAspect: itemModel.get('symbolKeepAspect'),\n            // `0` should be considered as a valid value, so use `retrieve2` instead of `||`\n            symbolOffset: retrieve2(itemModel.get('symbolOffset', true), symbolOffset[isFrom ? 0 : 1]),\n            symbolRotate: retrieve2(itemModel.get('symbolRotate', true), symbolRotate[isFrom ? 0 : 1]),\n            // TODO: when 2d array is supported, it should ignore parent\n            symbolSize: retrieve2(itemModel.get('symbolSize'), symbolSize[isFrom ? 0 : 1]),\n            symbol: retrieve2(itemModel.get('symbol', true), symbolType[isFrom ? 0 : 1]),\n            style: style\n          });\n        }\n\n        this.markKeep(lineDraw);\n        lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');\n      };\n\n      MarkLineView.type = 'markLine';\n      return MarkLineView;\n    }(MarkerView);\n\n    function createList$1(coordSys, seriesModel, mlModel) {\n      var coordDimsInfos;\n\n      if (coordSys) {\n        coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {\n          var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n          return extend(extend({}, info), {\n            name: coordDim,\n            // DON'T use ordinalMeta to parse and collect ordinal.\n            ordinalMeta: null\n          });\n        });\n      } else {\n        coordDimsInfos = [{\n          name: 'value',\n          type: 'float'\n        }];\n      }\n\n      var fromData = new SeriesData(coordDimsInfos, mlModel);\n      var toData = new SeriesData(coordDimsInfos, mlModel); // No dimensions\n\n      var lineData = new SeriesData([], mlModel);\n      var optData = map(mlModel.get('data'), curry(markLineTransform, seriesModel, coordSys, mlModel));\n\n      if (coordSys) {\n        optData = filter(optData, curry(markLineFilter, coordSys));\n      }\n\n      var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos);\n      fromData.initData(map(optData, function (item) {\n        return item[0];\n      }), null, dimValueGetter);\n      toData.initData(map(optData, function (item) {\n        return item[1];\n      }), null, dimValueGetter);\n      lineData.initData(map(optData, function (item) {\n        return item[2];\n      }));\n      lineData.hasItemOption = true;\n      return {\n        from: fromData,\n        to: toData,\n        line: lineData\n      };\n    }\n\n    function install$f(registers) {\n      registers.registerComponentModel(MarkLineModel);\n      registers.registerComponentView(MarkLineView);\n      registers.registerPreprocessor(function (opt) {\n        if (checkMarkerInSeries(opt.series, 'markLine')) {\n          // Make sure markLine component is enabled\n          opt.markLine = opt.markLine || {};\n        }\n      });\n    }\n\n    var MarkAreaModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkAreaModel, _super);\n\n      function MarkAreaModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkAreaModel.type;\n        return _this;\n      }\n\n      MarkAreaModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n        return new MarkAreaModel(markerOpt, masterMarkerModel, ecModel);\n      };\n\n      MarkAreaModel.type = 'markArea';\n      MarkAreaModel.defaultOption = {\n        // zlevel: 0,\n        // PENDING\n        z: 1,\n        tooltip: {\n          trigger: 'item'\n        },\n        // markArea should fixed on the coordinate system\n        animation: false,\n        label: {\n          show: true,\n          position: 'top'\n        },\n        itemStyle: {\n          // color and borderColor default to use color from series\n          // color: 'auto'\n          // borderColor: 'auto'\n          borderWidth: 0\n        },\n        emphasis: {\n          label: {\n            show: true,\n            position: 'top'\n          }\n        }\n      };\n      return MarkAreaModel;\n    }(MarkerModel);\n\n    var inner$f = makeInner();\n\n    var markAreaTransform = function (seriesModel, coordSys, maModel, item) {\n      // item may be null\n      var item0 = item[0];\n      var item1 = item[1];\n\n      if (!item0 || !item1) {\n        return;\n      }\n\n      var lt = dataTransform(seriesModel, item0);\n      var rb = dataTransform(seriesModel, item1); // FIXME make sure lt is less than rb\n\n      var ltCoord = lt.coord;\n      var rbCoord = rb.coord;\n      ltCoord[0] = retrieve(ltCoord[0], -Infinity);\n      ltCoord[1] = retrieve(ltCoord[1], -Infinity);\n      rbCoord[0] = retrieve(rbCoord[0], Infinity);\n      rbCoord[1] = retrieve(rbCoord[1], Infinity); // Merge option into one\n\n      var result = mergeAll([{}, lt, rb]);\n      result.coord = [lt.coord, rb.coord];\n      result.x0 = lt.x;\n      result.y0 = lt.y;\n      result.x1 = rb.x;\n      result.y1 = rb.y;\n      return result;\n    };\n\n    function isInfinity$1(val) {\n      return !isNaN(val) && !isFinite(val);\n    } // If a markArea has one dim\n\n\n    function ifMarkAreaHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {\n      var otherDimIndex = 1 - dimIndex;\n      return isInfinity$1(fromCoord[otherDimIndex]) && isInfinity$1(toCoord[otherDimIndex]);\n    }\n\n    function markAreaFilter(coordSys, item) {\n      var fromCoord = item.coord[0];\n      var toCoord = item.coord[1];\n      var item0 = {\n        coord: fromCoord,\n        x: item.x0,\n        y: item.y0\n      };\n      var item1 = {\n        coord: toCoord,\n        x: item.x1,\n        y: item.y1\n      };\n\n      if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n        // In case\n        // {\n        //  markArea: {\n        //    data: [{ yAxis: 2 }]\n        //  }\n        // }\n        if (fromCoord && toCoord && (ifMarkAreaHasOnlyDim(1, fromCoord, toCoord) || ifMarkAreaHasOnlyDim(0, fromCoord, toCoord))) {\n          return true;\n        } // Directly returning true may also do the work,\n        // because markArea will not be shown automatically\n        // when it's not included in coordinate system.\n        // But filtering ahead can avoid keeping rendering markArea\n        // when there are too many of them.\n\n\n        return zoneFilter(coordSys, item0, item1);\n      }\n\n      return dataFilter$1(coordSys, item0) || dataFilter$1(coordSys, item1);\n    } // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0']\n\n\n    function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) {\n      var coordSys = seriesModel.coordinateSystem;\n      var itemModel = data.getItemModel(idx);\n      var point;\n      var xPx = parsePercent$1(itemModel.get(dims[0]), api.getWidth());\n      var yPx = parsePercent$1(itemModel.get(dims[1]), api.getHeight());\n\n      if (!isNaN(xPx) && !isNaN(yPx)) {\n        point = [xPx, yPx];\n      } else {\n        // Chart like bar may have there own marker positioning logic\n        if (seriesModel.getMarkerPosition) {\n          // Consider the case that user input the right-bottom point first\n          // Pick the larger x and y as 'x1' and 'y1'\n          var pointValue0 = data.getValues(['x0', 'y0'], idx);\n          var pointValue1 = data.getValues(['x1', 'y1'], idx);\n          var clampPointValue0 = coordSys.clampData(pointValue0);\n          var clampPointValue1 = coordSys.clampData(pointValue1);\n          var pointValue = [];\n\n          if (dims[0] === 'x0') {\n            pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue1[0] : pointValue0[0];\n          } else {\n            pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue0[0] : pointValue1[0];\n          }\n\n          if (dims[1] === 'y0') {\n            pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue1[1] : pointValue0[1];\n          } else {\n            pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue0[1] : pointValue1[1];\n          } // Use the getMarkerPosition\n\n\n          point = seriesModel.getMarkerPosition(pointValue, dims, true);\n        } else {\n          var x = data.get(dims[0], idx);\n          var y = data.get(dims[1], idx);\n          var pt = [x, y];\n          coordSys.clampData && coordSys.clampData(pt, pt);\n          point = coordSys.dataToPoint(pt, true);\n        }\n\n        if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n          // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug\n          var xAxis = coordSys.getAxis('x');\n          var yAxis = coordSys.getAxis('y');\n          var x = data.get(dims[0], idx);\n          var y = data.get(dims[1], idx);\n\n          if (isInfinity$1(x)) {\n            point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]);\n          } else if (isInfinity$1(y)) {\n            point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]);\n          }\n        } // Use x, y if has any\n\n\n        if (!isNaN(xPx)) {\n          point[0] = xPx;\n        }\n\n        if (!isNaN(yPx)) {\n          point[1] = yPx;\n        }\n      }\n\n      return point;\n    }\n\n    var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']];\n\n    var MarkAreaView =\n    /** @class */\n    function (_super) {\n      __extends(MarkAreaView, _super);\n\n      function MarkAreaView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkAreaView.type;\n        return _this;\n      }\n\n      MarkAreaView.prototype.updateTransform = function (markAreaModel, ecModel, api) {\n        ecModel.eachSeries(function (seriesModel) {\n          var maModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markArea');\n\n          if (maModel) {\n            var areaData_1 = maModel.getData();\n            areaData_1.each(function (idx) {\n              var points = map(dimPermutations, function (dim) {\n                return getSingleMarkerEndPoint(areaData_1, idx, dim, seriesModel, api);\n              }); // Layout\n\n              areaData_1.setItemLayout(idx, points);\n              var el = areaData_1.getItemGraphicEl(idx);\n              el.setShape('points', points);\n            });\n          }\n        }, this);\n      };\n\n      MarkAreaView.prototype.renderSeries = function (seriesModel, maModel, ecModel, api) {\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesId = seriesModel.id;\n        var seriesData = seriesModel.getData();\n        var areaGroupMap = this.markerGroupMap;\n        var polygonGroup = areaGroupMap.get(seriesId) || areaGroupMap.set(seriesId, {\n          group: new Group()\n        });\n        this.group.add(polygonGroup.group);\n        this.markKeep(polygonGroup);\n        var areaData = createList$2(coordSys, seriesModel, maModel); // Line data for tooltip and formatter\n\n        maModel.setData(areaData); // Update visual and layout of line\n\n        areaData.each(function (idx) {\n          // Layout\n          var points = map(dimPermutations, function (dim) {\n            return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);\n          });\n          var xAxisScale = coordSys.getAxis('x').scale;\n          var yAxisScale = coordSys.getAxis('y').scale;\n          var xAxisExtent = xAxisScale.getExtent();\n          var yAxisExtent = yAxisScale.getExtent();\n          var xPointExtent = [xAxisScale.parse(areaData.get('x0', idx)), xAxisScale.parse(areaData.get('x1', idx))];\n          var yPointExtent = [yAxisScale.parse(areaData.get('y0', idx)), yAxisScale.parse(areaData.get('y1', idx))];\n          asc(xPointExtent);\n          asc(yPointExtent);\n          var overlapped = !(xAxisExtent[0] > xPointExtent[1] || xAxisExtent[1] < xPointExtent[0] || yAxisExtent[0] > yPointExtent[1] || yAxisExtent[1] < yPointExtent[0]); // If none of the area is inside coordSys, allClipped is set to be true\n          // in layout so that label will not be displayed. See #12591\n\n          var allClipped = !overlapped;\n          areaData.setItemLayout(idx, {\n            points: points,\n            allClipped: allClipped\n          });\n          var style = areaData.getItemModel(idx).getModel('itemStyle').getItemStyle();\n          var color$1 = getVisualFromData(seriesData, 'color');\n\n          if (!style.fill) {\n            style.fill = color$1;\n\n            if (isString(style.fill)) {\n              style.fill = modifyAlpha(style.fill, 0.4);\n            }\n          }\n\n          if (!style.stroke) {\n            style.stroke = color$1;\n          } // Visual\n\n\n          areaData.setItemVisual(idx, 'style', style);\n        });\n        areaData.diff(inner$f(polygonGroup).data).add(function (idx) {\n          var layout = areaData.getItemLayout(idx);\n\n          if (!layout.allClipped) {\n            var polygon = new Polygon({\n              shape: {\n                points: layout.points\n              }\n            });\n            areaData.setItemGraphicEl(idx, polygon);\n            polygonGroup.group.add(polygon);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var polygon = inner$f(polygonGroup).data.getItemGraphicEl(oldIdx);\n          var layout = areaData.getItemLayout(newIdx);\n\n          if (!layout.allClipped) {\n            if (polygon) {\n              updateProps(polygon, {\n                shape: {\n                  points: layout.points\n                }\n              }, maModel, newIdx);\n            } else {\n              polygon = new Polygon({\n                shape: {\n                  points: layout.points\n                }\n              });\n            }\n\n            areaData.setItemGraphicEl(newIdx, polygon);\n            polygonGroup.group.add(polygon);\n          } else if (polygon) {\n            polygonGroup.group.remove(polygon);\n          }\n        }).remove(function (idx) {\n          var polygon = inner$f(polygonGroup).data.getItemGraphicEl(idx);\n          polygonGroup.group.remove(polygon);\n        }).execute();\n        areaData.eachItemGraphicEl(function (polygon, idx) {\n          var itemModel = areaData.getItemModel(idx);\n          var style = areaData.getItemVisual(idx, 'style');\n          polygon.useStyle(areaData.getItemVisual(idx, 'style'));\n          setLabelStyle(polygon, getLabelStatesModels(itemModel), {\n            labelFetcher: maModel,\n            labelDataIndex: idx,\n            defaultText: areaData.getName(idx) || '',\n            inheritColor: isString(style.fill) ? modifyAlpha(style.fill, 1) : '#000'\n          });\n          setStatesStylesFromModel(polygon, itemModel);\n          toggleHoverEmphasis(polygon, null, null, itemModel.get(['emphasis', 'disabled']));\n          getECData(polygon).dataModel = maModel;\n        });\n        inner$f(polygonGroup).data = areaData;\n        polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent');\n      };\n\n      MarkAreaView.type = 'markArea';\n      return MarkAreaView;\n    }(MarkerView);\n\n    function createList$2(coordSys, seriesModel, maModel) {\n      var areaData;\n      var dataDims;\n      var dims = ['x0', 'y0', 'x1', 'y1'];\n\n      if (coordSys) {\n        var coordDimsInfos_1 = map(coordSys && coordSys.dimensions, function (coordDim) {\n          var data = seriesModel.getData();\n          var info = data.getDimensionInfo(data.mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n          return extend(extend({}, info), {\n            name: coordDim,\n            // DON'T use ordinalMeta to parse and collect ordinal.\n            ordinalMeta: null\n          });\n        });\n        dataDims = map(dims, function (dim, idx) {\n          return {\n            name: dim,\n            type: coordDimsInfos_1[idx % 2].type\n          };\n        });\n        areaData = new SeriesData(dataDims, maModel);\n      } else {\n        dataDims = [{\n          name: 'value',\n          type: 'float'\n        }];\n        areaData = new SeriesData(dataDims, maModel);\n      }\n\n      var optData = map(maModel.get('data'), curry(markAreaTransform, seriesModel, coordSys, maModel));\n\n      if (coordSys) {\n        optData = filter(optData, curry(markAreaFilter, coordSys));\n      }\n\n      var dimValueGetter = coordSys ? function (item, dimName, dataIndex, dimIndex) {\n        // TODO should convert to ParsedValue?\n        var rawVal = item.coord[Math.floor(dimIndex / 2)][dimIndex % 2];\n        return parseDataValue(rawVal, dataDims[dimIndex]);\n      } : function (item, dimName, dataIndex, dimIndex) {\n        return parseDataValue(item.value, dataDims[dimIndex]);\n      };\n      areaData.initData(optData, null, dimValueGetter);\n      areaData.hasItemOption = true;\n      return areaData;\n    }\n\n    function install$g(registers) {\n      registers.registerComponentModel(MarkAreaModel);\n      registers.registerComponentView(MarkAreaView);\n      registers.registerPreprocessor(function (opt) {\n        if (checkMarkerInSeries(opt.series, 'markArea')) {\n          // Make sure markArea component is enabled\n          opt.markArea = opt.markArea || {};\n        }\n      });\n    }\n\n    var getDefaultSelectorOptions = function (ecModel, type) {\n      if (type === 'all') {\n        return {\n          type: 'all',\n          title: ecModel.getLocaleModel().get(['legend', 'selector', 'all'])\n        };\n      } else if (type === 'inverse') {\n        return {\n          type: 'inverse',\n          title: ecModel.getLocaleModel().get(['legend', 'selector', 'inverse'])\n        };\n      }\n    };\n\n    var LegendModel =\n    /** @class */\n    function (_super) {\n      __extends(LegendModel, _super);\n\n      function LegendModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LegendModel.type;\n        _this.layoutMode = {\n          type: 'box',\n          // legend.width/height are maxWidth/maxHeight actually,\n          // whereas real width/height is calculated by its content.\n          // (Setting {left: 10, right: 10} does not make sense).\n          // So consider the case:\n          // `setOption({legend: {left: 10});`\n          // then `setOption({legend: {right: 10});`\n          // The previous `left` should be cleared by setting `ignoreSize`.\n          ignoreSize: true\n        };\n        return _this;\n      }\n\n      LegendModel.prototype.init = function (option, parentModel, ecModel) {\n        this.mergeDefaultAndTheme(option, ecModel);\n        option.selected = option.selected || {};\n\n        this._updateSelector(option);\n      };\n\n      LegendModel.prototype.mergeOption = function (option, ecModel) {\n        _super.prototype.mergeOption.call(this, option, ecModel);\n\n        this._updateSelector(option);\n      };\n\n      LegendModel.prototype._updateSelector = function (option) {\n        var selector = option.selector;\n        var ecModel = this.ecModel;\n\n        if (selector === true) {\n          selector = option.selector = ['all', 'inverse'];\n        }\n\n        if (isArray(selector)) {\n          each(selector, function (item, index) {\n            isString(item) && (item = {\n              type: item\n            });\n            selector[index] = merge(item, getDefaultSelectorOptions(ecModel, item.type));\n          });\n        }\n      };\n\n      LegendModel.prototype.optionUpdated = function () {\n        this._updateData(this.ecModel);\n\n        var legendData = this._data; // If selectedMode is single, try to select one\n\n        if (legendData[0] && this.get('selectedMode') === 'single') {\n          var hasSelected = false; // If has any selected in option.selected\n\n          for (var i = 0; i < legendData.length; i++) {\n            var name_1 = legendData[i].get('name');\n\n            if (this.isSelected(name_1)) {\n              // Force to unselect others\n              this.select(name_1);\n              hasSelected = true;\n              break;\n            }\n          } // Try select the first if selectedMode is single\n\n\n          !hasSelected && this.select(legendData[0].get('name'));\n        }\n      };\n\n      LegendModel.prototype._updateData = function (ecModel) {\n        var potentialData = [];\n        var availableNames = [];\n        ecModel.eachRawSeries(function (seriesModel) {\n          var seriesName = seriesModel.name;\n          availableNames.push(seriesName);\n          var isPotential;\n\n          if (seriesModel.legendVisualProvider) {\n            var provider = seriesModel.legendVisualProvider;\n            var names = provider.getAllNames();\n\n            if (!ecModel.isSeriesFiltered(seriesModel)) {\n              availableNames = availableNames.concat(names);\n            }\n\n            if (names.length) {\n              potentialData = potentialData.concat(names);\n            } else {\n              isPotential = true;\n            }\n          } else {\n            isPotential = true;\n          }\n\n          if (isPotential && isNameSpecified(seriesModel)) {\n            potentialData.push(seriesModel.name);\n          }\n        });\n        /**\n         * @type {Array.<string>}\n         * @private\n         */\n\n        this._availableNames = availableNames; // If legend.data is not specified in option, use availableNames as data,\n        // which is convenient for user preparing option.\n\n        var rawData = this.get('data') || potentialData;\n        var legendNameMap = createHashMap();\n        var legendData = map(rawData, function (dataItem) {\n          // Can be string or number\n          if (isString(dataItem) || isNumber(dataItem)) {\n            dataItem = {\n              name: dataItem\n            };\n          }\n\n          if (legendNameMap.get(dataItem.name)) {\n            // remove legend name duplicate\n            return null;\n          }\n\n          legendNameMap.set(dataItem.name, true);\n          return new Model(dataItem, this, this.ecModel);\n        }, this);\n        /**\n         * @type {Array.<module:echarts/model/Model>}\n         * @private\n         */\n\n        this._data = filter(legendData, function (item) {\n          return !!item;\n        });\n      };\n\n      LegendModel.prototype.getData = function () {\n        return this._data;\n      };\n\n      LegendModel.prototype.select = function (name) {\n        var selected = this.option.selected;\n        var selectedMode = this.get('selectedMode');\n\n        if (selectedMode === 'single') {\n          var data = this._data;\n          each(data, function (dataItem) {\n            selected[dataItem.get('name')] = false;\n          });\n        }\n\n        selected[name] = true;\n      };\n\n      LegendModel.prototype.unSelect = function (name) {\n        if (this.get('selectedMode') !== 'single') {\n          this.option.selected[name] = false;\n        }\n      };\n\n      LegendModel.prototype.toggleSelected = function (name) {\n        var selected = this.option.selected; // Default is true\n\n        if (!selected.hasOwnProperty(name)) {\n          selected[name] = true;\n        }\n\n        this[selected[name] ? 'unSelect' : 'select'](name);\n      };\n\n      LegendModel.prototype.allSelect = function () {\n        var data = this._data;\n        var selected = this.option.selected;\n        each(data, function (dataItem) {\n          selected[dataItem.get('name', true)] = true;\n        });\n      };\n\n      LegendModel.prototype.inverseSelect = function () {\n        var data = this._data;\n        var selected = this.option.selected;\n        each(data, function (dataItem) {\n          var name = dataItem.get('name', true); // Initially, default value is true\n\n          if (!selected.hasOwnProperty(name)) {\n            selected[name] = true;\n          }\n\n          selected[name] = !selected[name];\n        });\n      };\n\n      LegendModel.prototype.isSelected = function (name) {\n        var selected = this.option.selected;\n        return !(selected.hasOwnProperty(name) && !selected[name]) && indexOf(this._availableNames, name) >= 0;\n      };\n\n      LegendModel.prototype.getOrient = function () {\n        return this.get('orient') === 'vertical' ? {\n          index: 1,\n          name: 'vertical'\n        } : {\n          index: 0,\n          name: 'horizontal'\n        };\n      };\n\n      LegendModel.type = 'legend.plain';\n      LegendModel.dependencies = ['series'];\n      LegendModel.defaultOption = {\n        // zlevel: 0,\n        z: 4,\n        show: true,\n        orient: 'horizontal',\n        left: 'center',\n        // right: 'center',\n        top: 0,\n        // bottom: null,\n        align: 'auto',\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderColor: '#ccc',\n        borderRadius: 0,\n        borderWidth: 0,\n        padding: 5,\n        itemGap: 10,\n        itemWidth: 25,\n        itemHeight: 14,\n        symbolRotate: 'inherit',\n        symbolKeepAspect: true,\n        inactiveColor: '#ccc',\n        inactiveBorderColor: '#ccc',\n        inactiveBorderWidth: 'auto',\n        itemStyle: {\n          color: 'inherit',\n          opacity: 'inherit',\n          borderColor: 'inherit',\n          borderWidth: 'auto',\n          borderCap: 'inherit',\n          borderJoin: 'inherit',\n          borderDashOffset: 'inherit',\n          borderMiterLimit: 'inherit'\n        },\n        lineStyle: {\n          width: 'auto',\n          color: 'inherit',\n          inactiveColor: '#ccc',\n          inactiveWidth: 2,\n          opacity: 'inherit',\n          type: 'inherit',\n          cap: 'inherit',\n          join: 'inherit',\n          dashOffset: 'inherit',\n          miterLimit: 'inherit'\n        },\n        textStyle: {\n          color: '#333'\n        },\n        selectedMode: true,\n        selector: false,\n        selectorLabel: {\n          show: true,\n          borderRadius: 10,\n          padding: [3, 5, 3, 5],\n          fontSize: 12,\n          fontFamily: 'sans-serif',\n          color: '#666',\n          borderWidth: 1,\n          borderColor: '#666'\n        },\n        emphasis: {\n          selectorLabel: {\n            show: true,\n            color: '#eee',\n            backgroundColor: '#666'\n          }\n        },\n        selectorPosition: 'auto',\n        selectorItemGap: 7,\n        selectorButtonGap: 10,\n        tooltip: {\n          show: false\n        }\n      };\n      return LegendModel;\n    }(ComponentModel);\n\n    var curry$1 = curry;\n    var each$7 = each;\n    var Group$1 = Group;\n\n    var LegendView =\n    /** @class */\n    function (_super) {\n      __extends(LegendView, _super);\n\n      function LegendView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LegendView.type;\n        _this.newlineDisabled = false;\n        return _this;\n      }\n\n      LegendView.prototype.init = function () {\n        this.group.add(this._contentGroup = new Group$1());\n        this.group.add(this._selectorGroup = new Group$1());\n        this._isFirstRender = true;\n      };\n      /**\n       * @protected\n       */\n\n\n      LegendView.prototype.getContentGroup = function () {\n        return this._contentGroup;\n      };\n      /**\n       * @protected\n       */\n\n\n      LegendView.prototype.getSelectorGroup = function () {\n        return this._selectorGroup;\n      };\n      /**\n       * @override\n       */\n\n\n      LegendView.prototype.render = function (legendModel, ecModel, api) {\n        var isFirstRender = this._isFirstRender;\n        this._isFirstRender = false;\n        this.resetInner();\n\n        if (!legendModel.get('show', true)) {\n          return;\n        }\n\n        var itemAlign = legendModel.get('align');\n        var orient = legendModel.get('orient');\n\n        if (!itemAlign || itemAlign === 'auto') {\n          itemAlign = legendModel.get('left') === 'right' && orient === 'vertical' ? 'right' : 'left';\n        } // selector has been normalized to an array in model\n\n\n        var selector = legendModel.get('selector', true);\n        var selectorPosition = legendModel.get('selectorPosition', true);\n\n        if (selector && (!selectorPosition || selectorPosition === 'auto')) {\n          selectorPosition = orient === 'horizontal' ? 'end' : 'start';\n        }\n\n        this.renderInner(itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition); // Perform layout.\n\n        var positionInfo = legendModel.getBoxLayoutParams();\n        var viewportSize = {\n          width: api.getWidth(),\n          height: api.getHeight()\n        };\n        var padding = legendModel.get('padding');\n        var maxSize = getLayoutRect(positionInfo, viewportSize, padding);\n        var mainRect = this.layoutInner(legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition); // Place mainGroup, based on the calculated `mainRect`.\n\n        var layoutRect = getLayoutRect(defaults({\n          width: mainRect.width,\n          height: mainRect.height\n        }, positionInfo), viewportSize, padding);\n        this.group.x = layoutRect.x - mainRect.x;\n        this.group.y = layoutRect.y - mainRect.y;\n        this.group.markRedraw(); // Render background after group is layout.\n\n        this.group.add(this._backgroundEl = makeBackground(mainRect, legendModel));\n      };\n\n      LegendView.prototype.resetInner = function () {\n        this.getContentGroup().removeAll();\n        this._backgroundEl && this.group.remove(this._backgroundEl);\n        this.getSelectorGroup().removeAll();\n      };\n\n      LegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {\n        var contentGroup = this.getContentGroup();\n        var legendDrawnMap = createHashMap();\n        var selectMode = legendModel.get('selectedMode');\n        var excludeSeriesId = [];\n        ecModel.eachRawSeries(function (seriesModel) {\n          !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id);\n        });\n        each$7(legendModel.getData(), function (legendItemModel, dataIndex) {\n          var name = legendItemModel.get('name'); // Use empty string or \\n as a newline string\n\n          if (!this.newlineDisabled && (name === '' || name === '\\n')) {\n            var g = new Group$1(); // @ts-ignore\n\n            g.newline = true;\n            contentGroup.add(g);\n            return;\n          } // Representitive series.\n\n\n          var seriesModel = ecModel.getSeriesByName(name)[0];\n\n          if (legendDrawnMap.get(name)) {\n            // Have been drawn\n            return;\n          } // Legend to control series.\n\n\n          if (seriesModel) {\n            var data = seriesModel.getData();\n            var lineVisualStyle = data.getVisual('legendLineStyle') || {};\n            var legendIcon = data.getVisual('legendIcon');\n            /**\n             * `data.getVisual('style')` may be the color from the register\n             * in series. For example, for line series,\n             */\n\n            var style = data.getVisual('style');\n\n            var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, style, legendIcon, selectMode, api);\n\n            itemGroup.on('click', curry$1(dispatchSelectAction, name, null, api, excludeSeriesId)).on('mouseover', curry$1(dispatchHighlightAction, seriesModel.name, null, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, seriesModel.name, null, api, excludeSeriesId));\n            legendDrawnMap.set(name, true);\n          } else {\n            // Legend to control data. In pie and funnel.\n            ecModel.eachRawSeries(function (seriesModel) {\n              // In case multiple series has same data name\n              if (legendDrawnMap.get(name)) {\n                return;\n              }\n\n              if (seriesModel.legendVisualProvider) {\n                var provider = seriesModel.legendVisualProvider;\n\n                if (!provider.containName(name)) {\n                  return;\n                }\n\n                var idx = provider.indexOfName(name);\n                var style = provider.getItemVisual(idx, 'style');\n                var legendIcon = provider.getItemVisual(idx, 'legendIcon');\n                var colorArr = parse(style.fill); // Color may be set to transparent in visualMap when data is out of range.\n                // Do not show nothing.\n\n                if (colorArr && colorArr[3] === 0) {\n                  colorArr[3] = 0.2; // TODO color is set to 0, 0, 0, 0. Should show correct RGBA\n\n                  style = extend(extend({}, style), {\n                    fill: stringify(colorArr, 'rgba')\n                  });\n                }\n\n                var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, {}, style, legendIcon, selectMode, api); // FIXME: consider different series has items with the same name.\n\n\n                itemGroup.on('click', curry$1(dispatchSelectAction, null, name, api, excludeSeriesId)) // Should not specify the series name, consider legend controls\n                // more than one pie series.\n                .on('mouseover', curry$1(dispatchHighlightAction, null, name, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, null, name, api, excludeSeriesId));\n                legendDrawnMap.set(name, true);\n              }\n            }, this);\n          }\n\n          if (\"development\" !== 'production') {\n            if (!legendDrawnMap.get(name)) {\n              console.warn(name + ' series not exists. Legend data should be same with series name or data name.');\n            }\n          }\n        }, this);\n\n        if (selector) {\n          this._createSelector(selector, legendModel, api, orient, selectorPosition);\n        }\n      };\n\n      LegendView.prototype._createSelector = function (selector, legendModel, api, orient, selectorPosition) {\n        var selectorGroup = this.getSelectorGroup();\n        each$7(selector, function createSelectorButton(selectorItem) {\n          var type = selectorItem.type;\n          var labelText = new ZRText({\n            style: {\n              x: 0,\n              y: 0,\n              align: 'center',\n              verticalAlign: 'middle'\n            },\n            onclick: function () {\n              api.dispatchAction({\n                type: type === 'all' ? 'legendAllSelect' : 'legendInverseSelect'\n              });\n            }\n          });\n          selectorGroup.add(labelText);\n          var labelModel = legendModel.getModel('selectorLabel');\n          var emphasisLabelModel = legendModel.getModel(['emphasis', 'selectorLabel']);\n          setLabelStyle(labelText, {\n            normal: labelModel,\n            emphasis: emphasisLabelModel\n          }, {\n            defaultText: selectorItem.title\n          });\n          enableHoverEmphasis(labelText);\n        });\n      };\n\n      LegendView.prototype._createItem = function (seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, itemVisualStyle, legendIcon, selectMode, api) {\n        var drawType = seriesModel.visualDrawType;\n        var itemWidth = legendModel.get('itemWidth');\n        var itemHeight = legendModel.get('itemHeight');\n        var isSelected = legendModel.isSelected(name);\n        var iconRotate = legendItemModel.get('symbolRotate');\n        var symbolKeepAspect = legendItemModel.get('symbolKeepAspect');\n        var legendIconType = legendItemModel.get('icon');\n        legendIcon = legendIconType || legendIcon || 'roundRect';\n        var style = getLegendStyle(legendIcon, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api);\n        var itemGroup = new Group$1();\n        var textStyleModel = legendItemModel.getModel('textStyle');\n\n        if (isFunction(seriesModel.getLegendIcon) && (!legendIconType || legendIconType === 'inherit')) {\n          // Series has specific way to define legend icon\n          itemGroup.add(seriesModel.getLegendIcon({\n            itemWidth: itemWidth,\n            itemHeight: itemHeight,\n            icon: legendIcon,\n            iconRotate: iconRotate,\n            itemStyle: style.itemStyle,\n            lineStyle: style.lineStyle,\n            symbolKeepAspect: symbolKeepAspect\n          }));\n        } else {\n          // Use default legend icon policy for most series\n          var rotate = legendIconType === 'inherit' && seriesModel.getData().getVisual('symbol') ? iconRotate === 'inherit' ? seriesModel.getData().getVisual('symbolRotate') : iconRotate : 0; // No rotation for no icon\n\n          itemGroup.add(getDefaultLegendIcon({\n            itemWidth: itemWidth,\n            itemHeight: itemHeight,\n            icon: legendIcon,\n            iconRotate: rotate,\n            itemStyle: style.itemStyle,\n            lineStyle: style.lineStyle,\n            symbolKeepAspect: symbolKeepAspect\n          }));\n        }\n\n        var textX = itemAlign === 'left' ? itemWidth + 5 : -5;\n        var textAlign = itemAlign;\n        var formatter = legendModel.get('formatter');\n        var content = name;\n\n        if (isString(formatter) && formatter) {\n          content = formatter.replace('{name}', name != null ? name : '');\n        } else if (isFunction(formatter)) {\n          content = formatter(name);\n        }\n\n        var inactiveColor = legendItemModel.get('inactiveColor');\n        itemGroup.add(new ZRText({\n          style: createTextStyle(textStyleModel, {\n            text: content,\n            x: textX,\n            y: itemHeight / 2,\n            fill: isSelected ? textStyleModel.getTextColor() : inactiveColor,\n            align: textAlign,\n            verticalAlign: 'middle'\n          })\n        })); // Add a invisible rect to increase the area of mouse hover\n\n        var hitRect = new Rect({\n          shape: itemGroup.getBoundingRect(),\n          invisible: true\n        });\n        var tooltipModel = legendItemModel.getModel('tooltip');\n\n        if (tooltipModel.get('show')) {\n          setTooltipConfig({\n            el: hitRect,\n            componentModel: legendModel,\n            itemName: name,\n            itemTooltipOption: tooltipModel.option\n          });\n        }\n\n        itemGroup.add(hitRect);\n        itemGroup.eachChild(function (child) {\n          child.silent = true;\n        });\n        hitRect.silent = !selectMode;\n        this.getContentGroup().add(itemGroup);\n        enableHoverEmphasis(itemGroup); // @ts-ignore\n\n        itemGroup.__legendDataIndex = dataIndex;\n        return itemGroup;\n      };\n\n      LegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {\n        var contentGroup = this.getContentGroup();\n        var selectorGroup = this.getSelectorGroup(); // Place items in contentGroup.\n\n        box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), maxSize.width, maxSize.height);\n        var contentRect = contentGroup.getBoundingRect();\n        var contentPos = [-contentRect.x, -contentRect.y];\n        selectorGroup.markRedraw();\n        contentGroup.markRedraw();\n\n        if (selector) {\n          // Place buttons in selectorGroup\n          box( // Buttons in selectorGroup always layout horizontally\n          'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));\n          var selectorRect = selectorGroup.getBoundingRect();\n          var selectorPos = [-selectorRect.x, -selectorRect.y];\n          var selectorButtonGap = legendModel.get('selectorButtonGap', true);\n          var orientIdx = legendModel.getOrient().index;\n          var wh = orientIdx === 0 ? 'width' : 'height';\n          var hw = orientIdx === 0 ? 'height' : 'width';\n          var yx = orientIdx === 0 ? 'y' : 'x';\n\n          if (selectorPosition === 'end') {\n            selectorPos[orientIdx] += contentRect[wh] + selectorButtonGap;\n          } else {\n            contentPos[orientIdx] += selectorRect[wh] + selectorButtonGap;\n          } // Always align selector to content as 'middle'\n\n\n          selectorPos[1 - orientIdx] += contentRect[hw] / 2 - selectorRect[hw] / 2;\n          selectorGroup.x = selectorPos[0];\n          selectorGroup.y = selectorPos[1];\n          contentGroup.x = contentPos[0];\n          contentGroup.y = contentPos[1];\n          var mainRect = {\n            x: 0,\n            y: 0\n          };\n          mainRect[wh] = contentRect[wh] + selectorButtonGap + selectorRect[wh];\n          mainRect[hw] = Math.max(contentRect[hw], selectorRect[hw]);\n          mainRect[yx] = Math.min(0, selectorRect[yx] + selectorPos[1 - orientIdx]);\n          return mainRect;\n        } else {\n          contentGroup.x = contentPos[0];\n          contentGroup.y = contentPos[1];\n          return this.group.getBoundingRect();\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      LegendView.prototype.remove = function () {\n        this.getContentGroup().removeAll();\n        this._isFirstRender = true;\n      };\n\n      LegendView.type = 'legend.plain';\n      return LegendView;\n    }(ComponentView);\n\n    function getLegendStyle(iconType, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api) {\n      /**\n       * Use series style if is inherit;\n       * elsewise, use legend style\n       */\n      function handleCommonProps(style, visualStyle) {\n        // If lineStyle.width is 'auto', it is set to be 2 if series has border\n        if (style.lineWidth === 'auto') {\n          style.lineWidth = visualStyle.lineWidth > 0 ? 2 : 0;\n        }\n\n        each$7(style, function (propVal, propName) {\n          style[propName] === 'inherit' && (style[propName] = visualStyle[propName]);\n        });\n      } // itemStyle\n\n\n      var itemStyleModel = legendItemModel.getModel('itemStyle');\n      var itemStyle = itemStyleModel.getItemStyle();\n      var iconBrushType = iconType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke';\n      var decalStyle = itemStyleModel.getShallow('decal');\n      itemStyle.decal = !decalStyle || decalStyle === 'inherit' ? itemVisualStyle.decal : createOrUpdatePatternFromDecal(decalStyle, api);\n\n      if (itemStyle.fill === 'inherit') {\n        /**\n         * Series with visualDrawType as 'stroke' should have\n         * series stroke as legend fill\n         */\n        itemStyle.fill = itemVisualStyle[drawType];\n      }\n\n      if (itemStyle.stroke === 'inherit') {\n        /**\n         * icon type with \"emptyXXX\" should use fill color\n         * in visual style\n         */\n        itemStyle.stroke = itemVisualStyle[iconBrushType];\n      }\n\n      if (itemStyle.opacity === 'inherit') {\n        /**\n         * Use lineStyle.opacity if drawType is stroke\n         */\n        itemStyle.opacity = (drawType === 'fill' ? itemVisualStyle : lineVisualStyle).opacity;\n      }\n\n      handleCommonProps(itemStyle, itemVisualStyle); // lineStyle\n\n      var legendLineModel = legendItemModel.getModel('lineStyle');\n      var lineStyle = legendLineModel.getLineStyle();\n      handleCommonProps(lineStyle, lineVisualStyle); // Fix auto color to real color\n\n      itemStyle.fill === 'auto' && (itemStyle.fill = itemVisualStyle.fill);\n      itemStyle.stroke === 'auto' && (itemStyle.stroke = itemVisualStyle.fill);\n      lineStyle.stroke === 'auto' && (lineStyle.stroke = itemVisualStyle.fill);\n\n      if (!isSelected) {\n        var borderWidth = legendItemModel.get('inactiveBorderWidth');\n        /**\n         * Since stroke is set to be inactiveBorderColor, it may occur that\n         * there is no border in series but border in legend, so we need to\n         * use border only when series has border if is set to be auto\n         */\n\n        var visualHasBorder = itemStyle[iconBrushType];\n        itemStyle.lineWidth = borderWidth === 'auto' ? itemVisualStyle.lineWidth > 0 && visualHasBorder ? 2 : 0 : itemStyle.lineWidth;\n        itemStyle.fill = legendItemModel.get('inactiveColor');\n        itemStyle.stroke = legendItemModel.get('inactiveBorderColor');\n        lineStyle.stroke = legendLineModel.get('inactiveColor');\n        lineStyle.lineWidth = legendLineModel.get('inactiveWidth');\n      }\n\n      return {\n        itemStyle: itemStyle,\n        lineStyle: lineStyle\n      };\n    }\n\n    function getDefaultLegendIcon(opt) {\n      var symboType = opt.icon || 'roundRect';\n      var icon = createSymbol(symboType, 0, 0, opt.itemWidth, opt.itemHeight, opt.itemStyle.fill, opt.symbolKeepAspect);\n      icon.setStyle(opt.itemStyle);\n      icon.rotation = (opt.iconRotate || 0) * Math.PI / 180;\n      icon.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);\n\n      if (symboType.indexOf('empty') > -1) {\n        icon.style.stroke = icon.style.fill;\n        icon.style.fill = '#fff';\n        icon.style.lineWidth = 2;\n      }\n\n      return icon;\n    }\n\n    function dispatchSelectAction(seriesName, dataName, api, excludeSeriesId) {\n      // downplay before unselect\n      dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId);\n      api.dispatchAction({\n        type: 'legendToggleSelect',\n        name: seriesName != null ? seriesName : dataName\n      }); // highlight after select\n      // TODO highlight immediately may cause animation loss.\n\n      dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId);\n    }\n\n    function isUseHoverLayer(api) {\n      var list = api.getZr().storage.getDisplayList();\n      var emphasisState;\n      var i = 0;\n      var len = list.length;\n\n      while (i < len && !(emphasisState = list[i].states.emphasis)) {\n        i++;\n      }\n\n      return emphasisState && emphasisState.hoverLayer;\n    }\n\n    function dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId) {\n      // If element hover will move to a hoverLayer.\n      if (!isUseHoverLayer(api)) {\n        api.dispatchAction({\n          type: 'highlight',\n          seriesName: seriesName,\n          name: dataName,\n          excludeSeriesId: excludeSeriesId\n        });\n      }\n    }\n\n    function dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId) {\n      // If element hover will move to a hoverLayer.\n      if (!isUseHoverLayer(api)) {\n        api.dispatchAction({\n          type: 'downplay',\n          seriesName: seriesName,\n          name: dataName,\n          excludeSeriesId: excludeSeriesId\n        });\n      }\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function legendFilter(ecModel) {\n      var legendModels = ecModel.findComponents({\n        mainType: 'legend'\n      });\n\n      if (legendModels && legendModels.length) {\n        ecModel.filterSeries(function (series) {\n          // If in any legend component the status is not selected.\n          // Because in legend series is assumed selected when it is not in the legend data.\n          for (var i = 0; i < legendModels.length; i++) {\n            if (!legendModels[i].isSelected(series.name)) {\n              return false;\n            }\n          }\n\n          return true;\n        });\n      }\n    }\n\n    function legendSelectActionHandler(methodName, payload, ecModel) {\n      var selectedMap = {};\n      var isToggleSelect = methodName === 'toggleSelected';\n      var isSelected; // Update all legend components\n\n      ecModel.eachComponent('legend', function (legendModel) {\n        if (isToggleSelect && isSelected != null) {\n          // Force other legend has same selected status\n          // Or the first is toggled to true and other are toggled to false\n          // In the case one legend has some item unSelected in option. And if other legend\n          // doesn't has the item, they will assume it is selected.\n          legendModel[isSelected ? 'select' : 'unSelect'](payload.name);\n        } else if (methodName === 'allSelect' || methodName === 'inverseSelect') {\n          legendModel[methodName]();\n        } else {\n          legendModel[methodName](payload.name);\n          isSelected = legendModel.isSelected(payload.name);\n        }\n\n        var legendData = legendModel.getData();\n        each(legendData, function (model) {\n          var name = model.get('name'); // Wrap element\n\n          if (name === '\\n' || name === '') {\n            return;\n          }\n\n          var isItemSelected = legendModel.isSelected(name);\n\n          if (selectedMap.hasOwnProperty(name)) {\n            // Unselected if any legend is unselected\n            selectedMap[name] = selectedMap[name] && isItemSelected;\n          } else {\n            selectedMap[name] = isItemSelected;\n          }\n        });\n      }); // Return the event explicitly\n\n      return methodName === 'allSelect' || methodName === 'inverseSelect' ? {\n        selected: selectedMap\n      } : {\n        name: payload.name,\n        selected: selectedMap\n      };\n    }\n\n    function installLegendAction(registers) {\n      /**\n       * @event legendToggleSelect\n       * @type {Object}\n       * @property {string} type 'legendToggleSelect'\n       * @property {string} [from]\n       * @property {string} name Series name or data item name\n       */\n      registers.registerAction('legendToggleSelect', 'legendselectchanged', curry(legendSelectActionHandler, 'toggleSelected'));\n      registers.registerAction('legendAllSelect', 'legendselectall', curry(legendSelectActionHandler, 'allSelect'));\n      registers.registerAction('legendInverseSelect', 'legendinverseselect', curry(legendSelectActionHandler, 'inverseSelect'));\n      /**\n       * @event legendSelect\n       * @type {Object}\n       * @property {string} type 'legendSelect'\n       * @property {string} name Series name or data item name\n       */\n\n      registers.registerAction('legendSelect', 'legendselected', curry(legendSelectActionHandler, 'select'));\n      /**\n       * @event legendUnSelect\n       * @type {Object}\n       * @property {string} type 'legendUnSelect'\n       * @property {string} name Series name or data item name\n       */\n\n      registers.registerAction('legendUnSelect', 'legendunselected', curry(legendSelectActionHandler, 'unSelect'));\n    }\n\n    function install$h(registers) {\n      registers.registerComponentModel(LegendModel);\n      registers.registerComponentView(LegendView);\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.SERIES_FILTER, legendFilter);\n      registers.registerSubTypeDefaulter('legend', function () {\n        return 'plain';\n      });\n      installLegendAction(registers);\n    }\n\n    var ScrollableLegendModel =\n    /** @class */\n    function (_super) {\n      __extends(ScrollableLegendModel, _super);\n\n      function ScrollableLegendModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScrollableLegendModel.type;\n        return _this;\n      }\n      /**\n       * @param {number} scrollDataIndex\n       */\n\n\n      ScrollableLegendModel.prototype.setScrollDataIndex = function (scrollDataIndex) {\n        this.option.scrollDataIndex = scrollDataIndex;\n      };\n\n      ScrollableLegendModel.prototype.init = function (option, parentModel, ecModel) {\n        var inputPositionParams = getLayoutParams(option);\n\n        _super.prototype.init.call(this, option, parentModel, ecModel);\n\n        mergeAndNormalizeLayoutParams(this, option, inputPositionParams);\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendModel.prototype.mergeOption = function (option, ecModel) {\n        _super.prototype.mergeOption.call(this, option, ecModel);\n\n        mergeAndNormalizeLayoutParams(this, this.option, option);\n      };\n\n      ScrollableLegendModel.type = 'legend.scroll';\n      ScrollableLegendModel.defaultOption = inheritDefaultOption(LegendModel.defaultOption, {\n        scrollDataIndex: 0,\n        pageButtonItemGap: 5,\n        pageButtonGap: null,\n        pageButtonPosition: 'end',\n        pageFormatter: '{current}/{total}',\n        pageIcons: {\n          horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'],\n          vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z']\n        },\n        pageIconColor: '#2f4554',\n        pageIconInactiveColor: '#aaa',\n        pageIconSize: 15,\n        pageTextStyle: {\n          color: '#333'\n        },\n        animationDurationUpdate: 800\n      });\n      return ScrollableLegendModel;\n    }(LegendModel);\n\n    function mergeAndNormalizeLayoutParams(legendModel, target, raw) {\n      var orient = legendModel.getOrient();\n      var ignoreSize = [1, 1];\n      ignoreSize[orient.index] = 0;\n      mergeLayoutParam(target, raw, {\n        type: 'box',\n        ignoreSize: !!ignoreSize\n      });\n    }\n\n    var Group$2 = Group;\n    var WH = ['width', 'height'];\n    var XY = ['x', 'y'];\n\n    var ScrollableLegendView =\n    /** @class */\n    function (_super) {\n      __extends(ScrollableLegendView, _super);\n\n      function ScrollableLegendView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScrollableLegendView.type;\n        _this.newlineDisabled = true;\n        _this._currentIndex = 0;\n        return _this;\n      }\n\n      ScrollableLegendView.prototype.init = function () {\n        _super.prototype.init.call(this);\n\n        this.group.add(this._containerGroup = new Group$2());\n\n        this._containerGroup.add(this.getContentGroup());\n\n        this.group.add(this._controllerGroup = new Group$2());\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendView.prototype.resetInner = function () {\n        _super.prototype.resetInner.call(this);\n\n        this._controllerGroup.removeAll();\n\n        this._containerGroup.removeClipPath();\n\n        this._containerGroup.__rectSize = null;\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {\n        var self = this; // Render content items.\n\n        _super.prototype.renderInner.call(this, itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition);\n\n        var controllerGroup = this._controllerGroup; // FIXME: support be 'auto' adapt to size number text length,\n        // e.g., '3/12345' should not overlap with the control arrow button.\n\n        var pageIconSize = legendModel.get('pageIconSize', true);\n        var pageIconSizeArr = isArray(pageIconSize) ? pageIconSize : [pageIconSize, pageIconSize];\n        createPageButton('pagePrev', 0);\n        var pageTextStyleModel = legendModel.getModel('pageTextStyle');\n        controllerGroup.add(new ZRText({\n          name: 'pageText',\n          style: {\n            // Placeholder to calculate a proper layout.\n            text: 'xx/xx',\n            fill: pageTextStyleModel.getTextColor(),\n            font: pageTextStyleModel.getFont(),\n            verticalAlign: 'middle',\n            align: 'center'\n          },\n          silent: true\n        }));\n        createPageButton('pageNext', 1);\n\n        function createPageButton(name, iconIdx) {\n          var pageDataIndexName = name + 'DataIndex';\n          var icon = createIcon(legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], {\n            // Buttons will be created in each render, so we do not need\n            // to worry about avoiding using legendModel kept in scope.\n            onclick: bind(self._pageGo, self, pageDataIndexName, legendModel, api)\n          }, {\n            x: -pageIconSizeArr[0] / 2,\n            y: -pageIconSizeArr[1] / 2,\n            width: pageIconSizeArr[0],\n            height: pageIconSizeArr[1]\n          });\n          icon.name = name;\n          controllerGroup.add(icon);\n        }\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {\n        var selectorGroup = this.getSelectorGroup();\n        var orientIdx = legendModel.getOrient().index;\n        var wh = WH[orientIdx];\n        var xy = XY[orientIdx];\n        var hw = WH[1 - orientIdx];\n        var yx = XY[1 - orientIdx];\n        selector && box( // Buttons in selectorGroup always layout horizontally\n        'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));\n        var selectorButtonGap = legendModel.get('selectorButtonGap', true);\n        var selectorRect = selectorGroup.getBoundingRect();\n        var selectorPos = [-selectorRect.x, -selectorRect.y];\n        var processMaxSize = clone(maxSize);\n        selector && (processMaxSize[wh] = maxSize[wh] - selectorRect[wh] - selectorButtonGap);\n\n        var mainRect = this._layoutContentAndController(legendModel, isFirstRender, processMaxSize, orientIdx, wh, hw, yx, xy);\n\n        if (selector) {\n          if (selectorPosition === 'end') {\n            selectorPos[orientIdx] += mainRect[wh] + selectorButtonGap;\n          } else {\n            var offset = selectorRect[wh] + selectorButtonGap;\n            selectorPos[orientIdx] -= offset;\n            mainRect[xy] -= offset;\n          }\n\n          mainRect[wh] += selectorRect[wh] + selectorButtonGap;\n          selectorPos[1 - orientIdx] += mainRect[yx] + mainRect[hw] / 2 - selectorRect[hw] / 2;\n          mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw]);\n          mainRect[yx] = Math.min(mainRect[yx], selectorRect[yx] + selectorPos[1 - orientIdx]);\n          selectorGroup.x = selectorPos[0];\n          selectorGroup.y = selectorPos[1];\n          selectorGroup.markRedraw();\n        }\n\n        return mainRect;\n      };\n\n      ScrollableLegendView.prototype._layoutContentAndController = function (legendModel, isFirstRender, maxSize, orientIdx, wh, hw, yx, xy) {\n        var contentGroup = this.getContentGroup();\n        var containerGroup = this._containerGroup;\n        var controllerGroup = this._controllerGroup; // Place items in contentGroup.\n\n        box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), !orientIdx ? null : maxSize.width, orientIdx ? null : maxSize.height);\n        box( // Buttons in controller are layout always horizontally.\n        'horizontal', controllerGroup, legendModel.get('pageButtonItemGap', true));\n        var contentRect = contentGroup.getBoundingRect();\n        var controllerRect = controllerGroup.getBoundingRect();\n        var showController = this._showController = contentRect[wh] > maxSize[wh]; // In case that the inner elements of contentGroup layout do not based on [0, 0]\n\n        var contentPos = [-contentRect.x, -contentRect.y]; // Remain contentPos when scroll animation perfroming.\n        // If first rendering, `contentGroup.position` is [0, 0], which\n        // does not make sense and may cause unexepcted animation if adopted.\n\n        if (!isFirstRender) {\n          contentPos[orientIdx] = contentGroup[xy];\n        } // Layout container group based on 0.\n\n\n        var containerPos = [0, 0];\n        var controllerPos = [-controllerRect.x, -controllerRect.y];\n        var pageButtonGap = retrieve2(legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)); // Place containerGroup and controllerGroup and contentGroup.\n\n        if (showController) {\n          var pageButtonPosition = legendModel.get('pageButtonPosition', true); // controller is on the right / bottom.\n\n          if (pageButtonPosition === 'end') {\n            controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];\n          } // controller is on the left / top.\n          else {\n              containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;\n            }\n        } // Always align controller to content as 'middle'.\n\n\n        controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;\n        contentGroup.setPosition(contentPos);\n        containerGroup.setPosition(containerPos);\n        controllerGroup.setPosition(controllerPos); // Calculate `mainRect` and set `clipPath`.\n        // mainRect should not be calculated by `this.group.getBoundingRect()`\n        // for sake of the overflow.\n\n        var mainRect = {\n          x: 0,\n          y: 0\n        }; // Consider content may be overflow (should be clipped).\n\n        mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];\n        mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.\n\n        mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);\n        containerGroup.__rectSize = maxSize[wh];\n\n        if (showController) {\n          var clipShape = {\n            x: 0,\n            y: 0\n          };\n          clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);\n          clipShape[hw] = mainRect[hw];\n          containerGroup.setClipPath(new Rect({\n            shape: clipShape\n          })); // Consider content may be larger than container, container rect\n          // can not be obtained from `containerGroup.getBoundingRect()`.\n\n          containerGroup.__rectSize = clipShape[wh];\n        } else {\n          // Do not remove or ignore controller. Keep them set as placeholders.\n          controllerGroup.eachChild(function (child) {\n            child.attr({\n              invisible: true,\n              silent: true\n            });\n          });\n        } // Content translate animation.\n\n\n        var pageInfo = this._getPageInfo(legendModel);\n\n        pageInfo.pageIndex != null && updateProps(contentGroup, {\n          x: pageInfo.contentPosition[0],\n          y: pageInfo.contentPosition[1]\n        }, // When switch from \"show controller\" to \"not show controller\", view should be\n        // updated immediately without animation, otherwise causes weird effect.\n        showController ? legendModel : null);\n\n        this._updatePageInfoView(legendModel, pageInfo);\n\n        return mainRect;\n      };\n\n      ScrollableLegendView.prototype._pageGo = function (to, legendModel, api) {\n        var scrollDataIndex = this._getPageInfo(legendModel)[to];\n\n        scrollDataIndex != null && api.dispatchAction({\n          type: 'legendScroll',\n          scrollDataIndex: scrollDataIndex,\n          legendId: legendModel.id\n        });\n      };\n\n      ScrollableLegendView.prototype._updatePageInfoView = function (legendModel, pageInfo) {\n        var controllerGroup = this._controllerGroup;\n        each(['pagePrev', 'pageNext'], function (name) {\n          var key = name + 'DataIndex';\n          var canJump = pageInfo[key] != null;\n          var icon = controllerGroup.childOfName(name);\n\n          if (icon) {\n            icon.setStyle('fill', canJump ? legendModel.get('pageIconColor', true) : legendModel.get('pageIconInactiveColor', true));\n            icon.cursor = canJump ? 'pointer' : 'default';\n          }\n        });\n        var pageText = controllerGroup.childOfName('pageText');\n        var pageFormatter = legendModel.get('pageFormatter');\n        var pageIndex = pageInfo.pageIndex;\n        var current = pageIndex != null ? pageIndex + 1 : 0;\n        var total = pageInfo.pageCount;\n        pageText && pageFormatter && pageText.setStyle('text', isString(pageFormatter) ? pageFormatter.replace('{current}', current == null ? '' : current + '').replace('{total}', total == null ? '' : total + '') : pageFormatter({\n          current: current,\n          total: total\n        }));\n      };\n      /**\n       *  contentPosition: Array.<number>, null when data item not found.\n       *  pageIndex: number, null when data item not found.\n       *  pageCount: number, always be a number, can be 0.\n       *  pagePrevDataIndex: number, null when no previous page.\n       *  pageNextDataIndex: number, null when no next page.\n       * }\n       */\n\n\n      ScrollableLegendView.prototype._getPageInfo = function (legendModel) {\n        var scrollDataIndex = legendModel.get('scrollDataIndex', true);\n        var contentGroup = this.getContentGroup();\n        var containerRectSize = this._containerGroup.__rectSize;\n        var orientIdx = legendModel.getOrient().index;\n        var wh = WH[orientIdx];\n        var xy = XY[orientIdx];\n\n        var targetItemIndex = this._findTargetItemIndex(scrollDataIndex);\n\n        var children = contentGroup.children();\n        var targetItem = children[targetItemIndex];\n        var itemCount = children.length;\n        var pCount = !itemCount ? 0 : 1;\n        var result = {\n          contentPosition: [contentGroup.x, contentGroup.y],\n          pageCount: pCount,\n          pageIndex: pCount - 1,\n          pagePrevDataIndex: null,\n          pageNextDataIndex: null\n        };\n\n        if (!targetItem) {\n          return result;\n        }\n\n        var targetItemInfo = getItemInfo(targetItem);\n        result.contentPosition[orientIdx] = -targetItemInfo.s; // Strategy:\n        // (1) Always align based on the left/top most item.\n        // (2) It is user-friendly that the last item shown in the\n        // current window is shown at the begining of next window.\n        // Otherwise if half of the last item is cut by the window,\n        // it will have no chance to display entirely.\n        // (3) Consider that item size probably be different, we\n        // have calculate pageIndex by size rather than item index,\n        // and we can not get page index directly by division.\n        // (4) The window is to narrow to contain more than\n        // one item, we should make sure that the page can be fliped.\n\n        for (var i = targetItemIndex + 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i <= itemCount; ++i) {\n          currItemInfo = getItemInfo(children[i]);\n\n          if ( // Half of the last item is out of the window.\n          !currItemInfo && winEndItemInfo.e > winStartItemInfo.s + containerRectSize || // If the current item does not intersect with the window, the new page\n          // can be started at the current item or the last item.\n          currItemInfo && !intersect(currItemInfo, winStartItemInfo.s)) {\n            if (winEndItemInfo.i > winStartItemInfo.i) {\n              winStartItemInfo = winEndItemInfo;\n            } else {\n              // e.g., when page size is smaller than item size.\n              winStartItemInfo = currItemInfo;\n            }\n\n            if (winStartItemInfo) {\n              if (result.pageNextDataIndex == null) {\n                result.pageNextDataIndex = winStartItemInfo.i;\n              }\n\n              ++result.pageCount;\n            }\n          }\n\n          winEndItemInfo = currItemInfo;\n        }\n\n        for (var i = targetItemIndex - 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i >= -1; --i) {\n          currItemInfo = getItemInfo(children[i]);\n\n          if ( // If the the end item does not intersect with the window started\n          // from the current item, a page can be settled.\n          (!currItemInfo || !intersect(winEndItemInfo, currItemInfo.s)) && // e.g., when page size is smaller than item size.\n          winStartItemInfo.i < winEndItemInfo.i) {\n            winEndItemInfo = winStartItemInfo;\n\n            if (result.pagePrevDataIndex == null) {\n              result.pagePrevDataIndex = winStartItemInfo.i;\n            }\n\n            ++result.pageCount;\n            ++result.pageIndex;\n          }\n\n          winStartItemInfo = currItemInfo;\n        }\n\n        return result;\n\n        function getItemInfo(el) {\n          if (el) {\n            var itemRect = el.getBoundingRect();\n            var start = itemRect[xy] + el[xy];\n            return {\n              s: start,\n              e: start + itemRect[wh],\n              i: el.__legendDataIndex\n            };\n          }\n        }\n\n        function intersect(itemInfo, winStart) {\n          return itemInfo.e >= winStart && itemInfo.s <= winStart + containerRectSize;\n        }\n      };\n\n      ScrollableLegendView.prototype._findTargetItemIndex = function (targetDataIndex) {\n        if (!this._showController) {\n          return 0;\n        }\n\n        var index;\n        var contentGroup = this.getContentGroup();\n        var defaultIndex;\n        contentGroup.eachChild(function (child, idx) {\n          var legendDataIdx = child.__legendDataIndex; // FIXME\n          // If the given targetDataIndex (from model) is illegal,\n          // we use defaultIndex. But the index on the legend model and\n          // action payload is still illegal. That case will not be\n          // changed until some scenario requires.\n\n          if (defaultIndex == null && legendDataIdx != null) {\n            defaultIndex = idx;\n          }\n\n          if (legendDataIdx === targetDataIndex) {\n            index = idx;\n          }\n        });\n        return index != null ? index : defaultIndex;\n      };\n\n      ScrollableLegendView.type = 'legend.scroll';\n      return ScrollableLegendView;\n    }(LegendView);\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function installScrollableLegendAction(registers) {\n      /**\n       * @event legendScroll\n       * @type {Object}\n       * @property {string} type 'legendScroll'\n       * @property {string} scrollDataIndex\n       */\n      registers.registerAction('legendScroll', 'legendscroll', function (payload, ecModel) {\n        var scrollDataIndex = payload.scrollDataIndex;\n        scrollDataIndex != null && ecModel.eachComponent({\n          mainType: 'legend',\n          subType: 'scroll',\n          query: payload\n        }, function (legendModel) {\n          legendModel.setScrollDataIndex(scrollDataIndex);\n        });\n      });\n    }\n\n    function install$i(registers) {\n      use(install$h);\n      registers.registerComponentModel(ScrollableLegendModel);\n      registers.registerComponentView(ScrollableLegendView);\n      installScrollableLegendAction(registers);\n    }\n\n    function install$j(registers) {\n      use(install$h);\n      use(install$i);\n    }\n\n    var InsideZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(InsideZoomModel, _super);\n\n      function InsideZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = InsideZoomModel.type;\n        return _this;\n      }\n\n      InsideZoomModel.type = 'dataZoom.inside';\n      InsideZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, {\n        disabled: false,\n        zoomLock: false,\n        zoomOnMouseWheel: true,\n        moveOnMouseMove: true,\n        moveOnMouseWheel: false,\n        preventDefaultMouseMove: true\n      });\n      return InsideZoomModel;\n    }(DataZoomModel);\n\n    var RoamController =\n    /** @class */\n    function (_super) {\n      __extends(RoamController, _super);\n\n      function RoamController(zr) {\n        var _this = _super.call(this) || this;\n\n        _this._zr = zr; // Avoid two roamController bind the same handler\n\n        var mousedownHandler = bind(_this._mousedownHandler, _this);\n        var mousemoveHandler = bind(_this._mousemoveHandler, _this);\n        var mouseupHandler = bind(_this._mouseupHandler, _this);\n        var mousewheelHandler = bind(_this._mousewheelHandler, _this);\n        var pinchHandler = bind(_this._pinchHandler, _this);\n        /**\n         * Notice: only enable needed types. For example, if 'zoom'\n         * is not needed, 'zoom' should not be enabled, otherwise\n         * default mousewheel behaviour (scroll page) will be disabled.\n         */\n\n        _this.enable = function (controlType, opt) {\n          // Disable previous first\n          this.disable();\n          this._opt = defaults(clone(opt) || {}, {\n            zoomOnMouseWheel: true,\n            moveOnMouseMove: true,\n            // By default, wheel do not trigger move.\n            moveOnMouseWheel: false,\n            preventDefaultMouseMove: true\n          });\n\n          if (controlType == null) {\n            controlType = true;\n          }\n\n          if (controlType === true || controlType === 'move' || controlType === 'pan') {\n            zr.on('mousedown', mousedownHandler);\n            zr.on('mousemove', mousemoveHandler);\n            zr.on('mouseup', mouseupHandler);\n          }\n\n          if (controlType === true || controlType === 'scale' || controlType === 'zoom') {\n            zr.on('mousewheel', mousewheelHandler);\n            zr.on('pinch', pinchHandler);\n          }\n        };\n\n        _this.disable = function () {\n          zr.off('mousedown', mousedownHandler);\n          zr.off('mousemove', mousemoveHandler);\n          zr.off('mouseup', mouseupHandler);\n          zr.off('mousewheel', mousewheelHandler);\n          zr.off('pinch', pinchHandler);\n        };\n\n        return _this;\n      }\n\n      RoamController.prototype.isDragging = function () {\n        return this._dragging;\n      };\n\n      RoamController.prototype.isPinching = function () {\n        return this._pinching;\n      };\n\n      RoamController.prototype.setPointerChecker = function (pointerChecker) {\n        this.pointerChecker = pointerChecker;\n      };\n\n      RoamController.prototype.dispose = function () {\n        this.disable();\n      };\n\n      RoamController.prototype._mousedownHandler = function (e) {\n        if (isMiddleOrRightButtonOnMouseUpDown(e)) {\n          return;\n        }\n\n        var el = e.target;\n\n        while (el) {\n          if (el.draggable) {\n            return;\n          } // check if host is draggable\n\n\n          el = el.__hostTarget || el.parent;\n        }\n\n        var x = e.offsetX;\n        var y = e.offsetY; // Only check on mosedown, but not mousemove.\n        // Mouse can be out of target when mouse moving.\n\n        if (this.pointerChecker && this.pointerChecker(e, x, y)) {\n          this._x = x;\n          this._y = y;\n          this._dragging = true;\n        }\n      };\n\n      RoamController.prototype._mousemoveHandler = function (e) {\n        if (!this._dragging || !isAvailableBehavior('moveOnMouseMove', e, this._opt) || e.gestureEvent === 'pinch' || isTaken(this._zr, 'globalPan')) {\n          return;\n        }\n\n        var x = e.offsetX;\n        var y = e.offsetY;\n        var oldX = this._x;\n        var oldY = this._y;\n        var dx = x - oldX;\n        var dy = y - oldY;\n        this._x = x;\n        this._y = y;\n        this._opt.preventDefaultMouseMove && stop(e.event);\n        trigger$1(this, 'pan', 'moveOnMouseMove', e, {\n          dx: dx,\n          dy: dy,\n          oldX: oldX,\n          oldY: oldY,\n          newX: x,\n          newY: y,\n          isAvailableBehavior: null\n        });\n      };\n\n      RoamController.prototype._mouseupHandler = function (e) {\n        if (!isMiddleOrRightButtonOnMouseUpDown(e)) {\n          this._dragging = false;\n        }\n      };\n\n      RoamController.prototype._mousewheelHandler = function (e) {\n        var shouldZoom = isAvailableBehavior('zoomOnMouseWheel', e, this._opt);\n        var shouldMove = isAvailableBehavior('moveOnMouseWheel', e, this._opt);\n        var wheelDelta = e.wheelDelta;\n        var absWheelDeltaDelta = Math.abs(wheelDelta);\n        var originX = e.offsetX;\n        var originY = e.offsetY; // wheelDelta maybe -0 in chrome mac.\n\n        if (wheelDelta === 0 || !shouldZoom && !shouldMove) {\n          return;\n        } // If both `shouldZoom` and `shouldMove` is true, trigger\n        // their event both, and the final behavior is determined\n        // by event listener themselves.\n\n\n        if (shouldZoom) {\n          // Convenience:\n          // Mac and VM Windows on Mac: scroll up: zoom out.\n          // Windows: scroll up: zoom in.\n          // FIXME: Should do more test in different environment.\n          // wheelDelta is too complicated in difference nvironment\n          // (https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel),\n          // although it has been normallized by zrender.\n          // wheelDelta of mouse wheel is bigger than touch pad.\n          var factor = absWheelDeltaDelta > 3 ? 1.4 : absWheelDeltaDelta > 1 ? 1.2 : 1.1;\n          var scale = wheelDelta > 0 ? factor : 1 / factor;\n          checkPointerAndTrigger(this, 'zoom', 'zoomOnMouseWheel', e, {\n            scale: scale,\n            originX: originX,\n            originY: originY,\n            isAvailableBehavior: null\n          });\n        }\n\n        if (shouldMove) {\n          // FIXME: Should do more test in different environment.\n          var absDelta = Math.abs(wheelDelta); // wheelDelta of mouse wheel is bigger than touch pad.\n\n          var scrollDelta = (wheelDelta > 0 ? 1 : -1) * (absDelta > 3 ? 0.4 : absDelta > 1 ? 0.15 : 0.05);\n          checkPointerAndTrigger(this, 'scrollMove', 'moveOnMouseWheel', e, {\n            scrollDelta: scrollDelta,\n            originX: originX,\n            originY: originY,\n            isAvailableBehavior: null\n          });\n        }\n      };\n\n      RoamController.prototype._pinchHandler = function (e) {\n        if (isTaken(this._zr, 'globalPan')) {\n          return;\n        }\n\n        var scale = e.pinchScale > 1 ? 1.1 : 1 / 1.1;\n        checkPointerAndTrigger(this, 'zoom', null, e, {\n          scale: scale,\n          originX: e.pinchX,\n          originY: e.pinchY,\n          isAvailableBehavior: null\n        });\n      };\n\n      return RoamController;\n    }(Eventful);\n\n    function checkPointerAndTrigger(controller, eventName, behaviorToCheck, e, contollerEvent) {\n      if (controller.pointerChecker && controller.pointerChecker(e, contollerEvent.originX, contollerEvent.originY)) {\n        // When mouse is out of roamController rect,\n        // default befavoius should not be be disabled, otherwise\n        // page sliding is disabled, contrary to expectation.\n        stop(e.event);\n        trigger$1(controller, eventName, behaviorToCheck, e, contollerEvent);\n      }\n    }\n\n    function trigger$1(controller, eventName, behaviorToCheck, e, contollerEvent) {\n      // Also provide behavior checker for event listener, for some case that\n      // multiple components share one listener.\n      contollerEvent.isAvailableBehavior = bind(isAvailableBehavior, null, behaviorToCheck, e); // TODO should not have type issue.\n\n      controller.trigger(eventName, contollerEvent);\n    } // settings: {\n    //     zoomOnMouseWheel\n    //     moveOnMouseMove\n    //     moveOnMouseWheel\n    // }\n    // The value can be: true / false / 'shift' / 'ctrl' / 'alt'.\n\n\n    function isAvailableBehavior(behaviorToCheck, e, settings) {\n      var setting = settings[behaviorToCheck];\n      return !behaviorToCheck || setting && (!isString(setting) || e.event[setting + 'Key']);\n    }\n\n    var inner$g = makeInner();\n    function setViewInfoToCoordSysRecord(api, dataZoomModel, getRange) {\n      inner$g(api).coordSysRecordMap.each(function (coordSysRecord) {\n        var dzInfo = coordSysRecord.dataZoomInfoMap.get(dataZoomModel.uid);\n\n        if (dzInfo) {\n          dzInfo.getRange = getRange;\n        }\n      });\n    }\n    function disposeCoordSysRecordIfNeeded(api, dataZoomModel) {\n      var coordSysRecordMap = inner$g(api).coordSysRecordMap;\n      var coordSysKeyArr = coordSysRecordMap.keys();\n\n      for (var i = 0; i < coordSysKeyArr.length; i++) {\n        var coordSysKey = coordSysKeyArr[i];\n        var coordSysRecord = coordSysRecordMap.get(coordSysKey);\n        var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap;\n\n        if (dataZoomInfoMap) {\n          var dzUid = dataZoomModel.uid;\n          var dzInfo = dataZoomInfoMap.get(dzUid);\n\n          if (dzInfo) {\n            dataZoomInfoMap.removeKey(dzUid);\n\n            if (!dataZoomInfoMap.keys().length) {\n              disposeCoordSysRecord(coordSysRecordMap, coordSysRecord);\n            }\n          }\n        }\n      }\n    }\n\n    function disposeCoordSysRecord(coordSysRecordMap, coordSysRecord) {\n      if (coordSysRecord) {\n        coordSysRecordMap.removeKey(coordSysRecord.model.uid);\n        var controller = coordSysRecord.controller;\n        controller && controller.dispose();\n      }\n    }\n\n    function createCoordSysRecord(api, coordSysModel) {\n      // These init props will never change after record created.\n      var coordSysRecord = {\n        model: coordSysModel,\n        containsPoint: curry(containsPoint, coordSysModel),\n        dispatchAction: curry(dispatchAction, api),\n        dataZoomInfoMap: null,\n        controller: null\n      }; // Must not do anything depends on coordSysRecord outside the event handler here,\n      // because coordSysRecord not completed yet.\n\n      var controller = coordSysRecord.controller = new RoamController(api.getZr());\n      each(['pan', 'zoom', 'scrollMove'], function (eventName) {\n        controller.on(eventName, function (event) {\n          var batch = [];\n          coordSysRecord.dataZoomInfoMap.each(function (dzInfo) {\n            // Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove,\n            // moveOnMouseWheel, ...) enabled.\n            if (!event.isAvailableBehavior(dzInfo.model.option)) {\n              return;\n            }\n\n            var method = (dzInfo.getRange || {})[eventName];\n            var range = method && method(dzInfo.dzReferCoordSysInfo, coordSysRecord.model.mainType, coordSysRecord.controller, event);\n            !dzInfo.model.get('disabled', true) && range && batch.push({\n              dataZoomId: dzInfo.model.id,\n              start: range[0],\n              end: range[1]\n            });\n          });\n          batch.length && coordSysRecord.dispatchAction(batch);\n        });\n      });\n      return coordSysRecord;\n    }\n    /**\n     * This action will be throttled.\n     */\n\n\n    function dispatchAction(api, batch) {\n      if (!api.isDisposed()) {\n        api.dispatchAction({\n          type: 'dataZoom',\n          animation: {\n            easing: 'cubicOut',\n            duration: 100\n          },\n          batch: batch\n        });\n      }\n    }\n\n    function containsPoint(coordSysModel, e, x, y) {\n      return coordSysModel.coordinateSystem.containPoint([x, y]);\n    }\n    /**\n     * Merge roamController settings when multiple dataZooms share one roamController.\n     */\n\n\n    function mergeControllerParams(dataZoomInfoMap) {\n      var controlType; // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated\n      // as string, it is probably revert to reserved word by compress tool. See #7411.\n\n      var prefix = 'type_';\n      var typePriority = {\n        'type_true': 2,\n        'type_move': 1,\n        'type_false': 0,\n        'type_undefined': -1\n      };\n      var preventDefaultMouseMove = true;\n      dataZoomInfoMap.each(function (dataZoomInfo) {\n        var dataZoomModel = dataZoomInfo.model;\n        var oneType = dataZoomModel.get('disabled', true) ? false : dataZoomModel.get('zoomLock', true) ? 'move' : true;\n\n        if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) {\n          controlType = oneType;\n        } // Prevent default move event by default. If one false, do not prevent. Otherwise\n        // users may be confused why it does not work when multiple insideZooms exist.\n\n\n        preventDefaultMouseMove = preventDefaultMouseMove && dataZoomModel.get('preventDefaultMouseMove', true);\n      });\n      return {\n        controlType: controlType,\n        opt: {\n          // RoamController will enable all of these functionalities,\n          // and the final behavior is determined by its event listener\n          // provided by each inside zoom.\n          zoomOnMouseWheel: true,\n          moveOnMouseMove: true,\n          moveOnMouseWheel: true,\n          preventDefaultMouseMove: !!preventDefaultMouseMove\n        }\n      };\n    }\n\n    function installDataZoomRoamProcessor(registers) {\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, function (ecModel, api) {\n        var apiInner = inner$g(api);\n        var coordSysRecordMap = apiInner.coordSysRecordMap || (apiInner.coordSysRecordMap = createHashMap());\n        coordSysRecordMap.each(function (coordSysRecord) {\n          // `coordSysRecordMap` always exists (because it holds the `roam controller`, which should\n          // better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow.\n          coordSysRecord.dataZoomInfoMap = null;\n        });\n        ecModel.eachComponent({\n          mainType: 'dataZoom',\n          subType: 'inside'\n        }, function (dataZoomModel) {\n          var dzReferCoordSysWrap = collectReferCoordSysModelInfo(dataZoomModel);\n          each(dzReferCoordSysWrap.infoList, function (dzCoordSysInfo) {\n            var coordSysUid = dzCoordSysInfo.model.uid;\n            var coordSysRecord = coordSysRecordMap.get(coordSysUid) || coordSysRecordMap.set(coordSysUid, createCoordSysRecord(api, dzCoordSysInfo.model));\n            var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap || (coordSysRecord.dataZoomInfoMap = createHashMap()); // Notice these props might be changed each time for a single dataZoomModel.\n\n            dataZoomInfoMap.set(dataZoomModel.uid, {\n              dzReferCoordSysInfo: dzCoordSysInfo,\n              model: dataZoomModel,\n              getRange: null\n            });\n          });\n        }); // (1) Merge dataZoom settings for each coord sys and set to the roam controller.\n        // (2) Clear coord sys if not refered by any dataZoom.\n\n        coordSysRecordMap.each(function (coordSysRecord) {\n          var controller = coordSysRecord.controller;\n          var firstDzInfo;\n          var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap;\n\n          if (dataZoomInfoMap) {\n            var firstDzKey = dataZoomInfoMap.keys()[0];\n\n            if (firstDzKey != null) {\n              firstDzInfo = dataZoomInfoMap.get(firstDzKey);\n            }\n          }\n\n          if (!firstDzInfo) {\n            disposeCoordSysRecord(coordSysRecordMap, coordSysRecord);\n            return;\n          }\n\n          var controllerParams = mergeControllerParams(dataZoomInfoMap);\n          controller.enable(controllerParams.controlType, controllerParams.opt);\n          controller.setPointerChecker(coordSysRecord.containsPoint);\n          createOrUpdate(coordSysRecord, 'dispatchAction', firstDzInfo.model.get('throttle', true), 'fixRate');\n        });\n      });\n    }\n\n    var InsideZoomView =\n    /** @class */\n    function (_super) {\n      __extends(InsideZoomView, _super);\n\n      function InsideZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataZoom.inside';\n        return _this;\n      }\n\n      InsideZoomView.prototype.render = function (dataZoomModel, ecModel, api) {\n        _super.prototype.render.apply(this, arguments);\n\n        if (dataZoomModel.noTarget()) {\n          this._clear();\n\n          return;\n        } // Hence the `throttle` util ensures to preserve command order,\n        // here simply updating range all the time will not cause missing\n        // any of the the roam change.\n\n\n        this.range = dataZoomModel.getPercentRange(); // Reset controllers.\n\n        setViewInfoToCoordSysRecord(api, dataZoomModel, {\n          pan: bind(getRangeHandlers.pan, this),\n          zoom: bind(getRangeHandlers.zoom, this),\n          scrollMove: bind(getRangeHandlers.scrollMove, this)\n        });\n      };\n\n      InsideZoomView.prototype.dispose = function () {\n        this._clear();\n\n        _super.prototype.dispose.apply(this, arguments);\n      };\n\n      InsideZoomView.prototype._clear = function () {\n        disposeCoordSysRecordIfNeeded(this.api, this.dataZoomModel);\n        this.range = null;\n      };\n\n      InsideZoomView.type = 'dataZoom.inside';\n      return InsideZoomView;\n    }(DataZoomView);\n\n    var getRangeHandlers = {\n      zoom: function (coordSysInfo, coordSysMainType, controller, e) {\n        var lastRange = this.range;\n        var range = lastRange.slice(); // Calculate transform by the first axis.\n\n        var axisModel = coordSysInfo.axisModels[0];\n\n        if (!axisModel) {\n          return;\n        }\n\n        var directionInfo = getDirectionInfo[coordSysMainType](null, [e.originX, e.originY], axisModel, controller, coordSysInfo);\n        var percentPoint = (directionInfo.signal > 0 ? directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel : directionInfo.pixel - directionInfo.pixelStart) / directionInfo.pixelLength * (range[1] - range[0]) + range[0];\n        var scale = Math.max(1 / e.scale, 0);\n        range[0] = (range[0] - percentPoint) * scale + percentPoint;\n        range[1] = (range[1] - percentPoint) * scale + percentPoint; // Restrict range.\n\n        var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();\n        sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);\n        this.range = range;\n\n        if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {\n          return range;\n        }\n      },\n      pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) {\n        var directionInfo = getDirectionInfo[coordSysMainType]([e.oldX, e.oldY], [e.newX, e.newY], axisModel, controller, coordSysInfo);\n        return directionInfo.signal * (range[1] - range[0]) * directionInfo.pixel / directionInfo.pixelLength;\n      }),\n      scrollMove: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) {\n        var directionInfo = getDirectionInfo[coordSysMainType]([0, 0], [e.scrollDelta, e.scrollDelta], axisModel, controller, coordSysInfo);\n        return directionInfo.signal * (range[1] - range[0]) * e.scrollDelta;\n      })\n    };\n\n    function makeMover(getPercentDelta) {\n      return function (coordSysInfo, coordSysMainType, controller, e) {\n        var lastRange = this.range;\n        var range = lastRange.slice(); // Calculate transform by the first axis.\n\n        var axisModel = coordSysInfo.axisModels[0];\n\n        if (!axisModel) {\n          return;\n        }\n\n        var percentDelta = getPercentDelta(range, axisModel, coordSysInfo, coordSysMainType, controller, e);\n        sliderMove(percentDelta, range, [0, 100], 'all');\n        this.range = range;\n\n        if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {\n          return range;\n        }\n      };\n    }\n\n    var getDirectionInfo = {\n      grid: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n        var axis = axisModel.axis;\n        var ret = {};\n        var rect = coordSysInfo.model.coordinateSystem.getRect();\n        oldPoint = oldPoint || [0, 0];\n\n        if (axis.dim === 'x') {\n          ret.pixel = newPoint[0] - oldPoint[0];\n          ret.pixelLength = rect.width;\n          ret.pixelStart = rect.x;\n          ret.signal = axis.inverse ? 1 : -1;\n        } else {\n          // axis.dim === 'y'\n          ret.pixel = newPoint[1] - oldPoint[1];\n          ret.pixelLength = rect.height;\n          ret.pixelStart = rect.y;\n          ret.signal = axis.inverse ? -1 : 1;\n        }\n\n        return ret;\n      },\n      polar: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n        var axis = axisModel.axis;\n        var ret = {};\n        var polar = coordSysInfo.model.coordinateSystem;\n        var radiusExtent = polar.getRadiusAxis().getExtent();\n        var angleExtent = polar.getAngleAxis().getExtent();\n        oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0];\n        newPoint = polar.pointToCoord(newPoint);\n\n        if (axisModel.mainType === 'radiusAxis') {\n          ret.pixel = newPoint[0] - oldPoint[0]; // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]);\n          // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]);\n\n          ret.pixelLength = radiusExtent[1] - radiusExtent[0];\n          ret.pixelStart = radiusExtent[0];\n          ret.signal = axis.inverse ? 1 : -1;\n        } else {\n          // 'angleAxis'\n          ret.pixel = newPoint[1] - oldPoint[1]; // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]);\n          // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]);\n\n          ret.pixelLength = angleExtent[1] - angleExtent[0];\n          ret.pixelStart = angleExtent[0];\n          ret.signal = axis.inverse ? -1 : 1;\n        }\n\n        return ret;\n      },\n      singleAxis: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n        var axis = axisModel.axis;\n        var rect = coordSysInfo.model.coordinateSystem.getRect();\n        var ret = {};\n        oldPoint = oldPoint || [0, 0];\n\n        if (axis.orient === 'horizontal') {\n          ret.pixel = newPoint[0] - oldPoint[0];\n          ret.pixelLength = rect.width;\n          ret.pixelStart = rect.x;\n          ret.signal = axis.inverse ? 1 : -1;\n        } else {\n          // 'vertical'\n          ret.pixel = newPoint[1] - oldPoint[1];\n          ret.pixelLength = rect.height;\n          ret.pixelStart = rect.y;\n          ret.signal = axis.inverse ? -1 : 1;\n        }\n\n        return ret;\n      }\n    };\n\n    function install$k(registers) {\n      installCommon(registers);\n      registers.registerComponentModel(InsideZoomModel);\n      registers.registerComponentView(InsideZoomView);\n      installDataZoomRoamProcessor(registers);\n    }\n\n    var SliderZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(SliderZoomModel, _super);\n\n      function SliderZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SliderZoomModel.type;\n        return _this;\n      }\n\n      SliderZoomModel.type = 'dataZoom.slider';\n      SliderZoomModel.layoutMode = 'box';\n      SliderZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, {\n        show: true,\n        // deault value can only be drived in view stage.\n        right: 'ph',\n        top: 'ph',\n        width: 'ph',\n        height: 'ph',\n        left: null,\n        bottom: null,\n        borderColor: '#d2dbee',\n        borderRadius: 3,\n        backgroundColor: 'rgba(47,69,84,0)',\n        // dataBackgroundColor: '#ddd',\n        dataBackground: {\n          lineStyle: {\n            color: '#d2dbee',\n            width: 0.5\n          },\n          areaStyle: {\n            color: '#d2dbee',\n            opacity: 0.2\n          }\n        },\n        selectedDataBackground: {\n          lineStyle: {\n            color: '#8fb0f7',\n            width: 0.5\n          },\n          areaStyle: {\n            color: '#8fb0f7',\n            opacity: 0.2\n          }\n        },\n        // Color of selected window.\n        fillerColor: 'rgba(135,175,274,0.2)',\n        handleIcon: 'path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z',\n        // Percent of the slider height\n        handleSize: '100%',\n        handleStyle: {\n          color: '#fff',\n          borderColor: '#ACB8D1'\n        },\n        moveHandleSize: 7,\n        moveHandleIcon: 'path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z',\n        moveHandleStyle: {\n          color: '#D2DBEE',\n          opacity: 0.7\n        },\n        showDetail: true,\n        showDataShadow: 'auto',\n        realtime: true,\n        zoomLock: false,\n        textStyle: {\n          color: '#6E7079'\n        },\n        brushSelect: true,\n        brushStyle: {\n          color: 'rgba(135,175,274,0.15)'\n        },\n        emphasis: {\n          handleStyle: {\n            borderColor: '#8FB0F7'\n          },\n          moveHandleStyle: {\n            color: '#8FB0F7'\n          }\n        }\n      });\n      return SliderZoomModel;\n    }(DataZoomModel);\n\n    var Rect$1 = Rect; // Constants\n\n    var DEFAULT_LOCATION_EDGE_GAP = 7;\n    var DEFAULT_FRAME_BORDER_WIDTH = 1;\n    var DEFAULT_FILLER_SIZE = 30;\n    var DEFAULT_MOVE_HANDLE_SIZE = 7;\n    var HORIZONTAL = 'horizontal';\n    var VERTICAL = 'vertical';\n    var LABEL_GAP = 5;\n    var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];\n    var REALTIME_ANIMATION_CONFIG = {\n      easing: 'cubicOut',\n      duration: 100,\n      delay: 0\n    };\n\n    var SliderZoomView =\n    /** @class */\n    function (_super) {\n      __extends(SliderZoomView, _super);\n\n      function SliderZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SliderZoomView.type;\n        _this._displayables = {};\n        return _this;\n      }\n\n      SliderZoomView.prototype.init = function (ecModel, api) {\n        this.api = api; // A unique handler for each dataZoom component\n\n        this._onBrush = bind(this._onBrush, this);\n        this._onBrushEnd = bind(this._onBrushEnd, this);\n      };\n\n      SliderZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) {\n        _super.prototype.render.apply(this, arguments);\n\n        createOrUpdate(this, '_dispatchZoomAction', dataZoomModel.get('throttle'), 'fixRate');\n        this._orient = dataZoomModel.getOrient();\n\n        if (dataZoomModel.get('show') === false) {\n          this.group.removeAll();\n          return;\n        }\n\n        if (dataZoomModel.noTarget()) {\n          this._clear();\n\n          this.group.removeAll();\n          return;\n        } // Notice: this._resetInterval() should not be executed when payload.type\n        // is 'dataZoom', origin this._range should be maintained, otherwise 'pan'\n        // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,\n\n\n        if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) {\n          this._buildView();\n        }\n\n        this._updateView();\n      };\n\n      SliderZoomView.prototype.dispose = function () {\n        this._clear();\n\n        _super.prototype.dispose.apply(this, arguments);\n      };\n\n      SliderZoomView.prototype._clear = function () {\n        clear(this, '_dispatchZoomAction');\n        var zr = this.api.getZr();\n        zr.off('mousemove', this._onBrush);\n        zr.off('mouseup', this._onBrushEnd);\n      };\n\n      SliderZoomView.prototype._buildView = function () {\n        var thisGroup = this.group;\n        thisGroup.removeAll();\n        this._brushing = false;\n        this._displayables.brushRect = null;\n\n        this._resetLocation();\n\n        this._resetInterval();\n\n        var barGroup = this._displayables.sliderGroup = new Group();\n\n        this._renderBackground();\n\n        this._renderHandle();\n\n        this._renderDataShadow();\n\n        thisGroup.add(barGroup);\n\n        this._positionGroup();\n      };\n\n      SliderZoomView.prototype._resetLocation = function () {\n        var dataZoomModel = this.dataZoomModel;\n        var api = this.api;\n        var showMoveHandle = dataZoomModel.get('brushSelect');\n        var moveHandleSize = showMoveHandle ? DEFAULT_MOVE_HANDLE_SIZE : 0; // If some of x/y/width/height are not specified,\n        // auto-adapt according to target grid.\n\n        var coordRect = this._findCoordRect();\n\n        var ecSize = {\n          width: api.getWidth(),\n          height: api.getHeight()\n        }; // Default align by coordinate system rect.\n\n        var positionInfo = this._orient === HORIZONTAL ? {\n          // Why using 'right', because right should be used in vertical,\n          // and it is better to be consistent for dealing with position param merge.\n          right: ecSize.width - coordRect.x - coordRect.width,\n          top: ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP - moveHandleSize,\n          width: coordRect.width,\n          height: DEFAULT_FILLER_SIZE\n        } : {\n          right: DEFAULT_LOCATION_EDGE_GAP,\n          top: coordRect.y,\n          width: DEFAULT_FILLER_SIZE,\n          height: coordRect.height\n        }; // Do not write back to option and replace value 'ph', because\n        // the 'ph' value should be recalculated when resize.\n\n        var layoutParams = getLayoutParams(dataZoomModel.option); // Replace the placeholder value.\n\n        each(['right', 'top', 'width', 'height'], function (name) {\n          if (layoutParams[name] === 'ph') {\n            layoutParams[name] = positionInfo[name];\n          }\n        });\n        var layoutRect = getLayoutRect(layoutParams, ecSize);\n        this._location = {\n          x: layoutRect.x,\n          y: layoutRect.y\n        };\n        this._size = [layoutRect.width, layoutRect.height];\n        this._orient === VERTICAL && this._size.reverse();\n      };\n\n      SliderZoomView.prototype._positionGroup = function () {\n        var thisGroup = this.group;\n        var location = this._location;\n        var orient = this._orient; // Just use the first axis to determine mapping.\n\n        var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel();\n        var inverse = targetAxisModel && targetAxisModel.get('inverse');\n        var sliderGroup = this._displayables.sliderGroup;\n        var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse; // Transform barGroup.\n\n        sliderGroup.attr(orient === HORIZONTAL && !inverse ? {\n          scaleY: otherAxisInverse ? 1 : -1,\n          scaleX: 1\n        } : orient === HORIZONTAL && inverse ? {\n          scaleY: otherAxisInverse ? 1 : -1,\n          scaleX: -1\n        } : orient === VERTICAL && !inverse ? {\n          scaleY: otherAxisInverse ? -1 : 1,\n          scaleX: 1,\n          rotation: Math.PI / 2\n        } // Dont use Math.PI, considering shadow direction.\n        : {\n          scaleY: otherAxisInverse ? -1 : 1,\n          scaleX: -1,\n          rotation: Math.PI / 2\n        }); // Position barGroup\n\n        var rect = thisGroup.getBoundingRect([sliderGroup]);\n        thisGroup.x = location.x - rect.x;\n        thisGroup.y = location.y - rect.y;\n        thisGroup.markRedraw();\n      };\n\n      SliderZoomView.prototype._getViewExtent = function () {\n        return [0, this._size[0]];\n      };\n\n      SliderZoomView.prototype._renderBackground = function () {\n        var dataZoomModel = this.dataZoomModel;\n        var size = this._size;\n        var barGroup = this._displayables.sliderGroup;\n        var brushSelect = dataZoomModel.get('brushSelect');\n        barGroup.add(new Rect$1({\n          silent: true,\n          shape: {\n            x: 0,\n            y: 0,\n            width: size[0],\n            height: size[1]\n          },\n          style: {\n            fill: dataZoomModel.get('backgroundColor')\n          },\n          z2: -40\n        })); // Click panel, over shadow, below handles.\n\n        var clickPanel = new Rect$1({\n          shape: {\n            x: 0,\n            y: 0,\n            width: size[0],\n            height: size[1]\n          },\n          style: {\n            fill: 'transparent'\n          },\n          z2: 0,\n          onclick: bind(this._onClickPanel, this)\n        });\n        var zr = this.api.getZr();\n\n        if (brushSelect) {\n          clickPanel.on('mousedown', this._onBrushStart, this);\n          clickPanel.cursor = 'crosshair';\n          zr.on('mousemove', this._onBrush);\n          zr.on('mouseup', this._onBrushEnd);\n        } else {\n          zr.off('mousemove', this._onBrush);\n          zr.off('mouseup', this._onBrushEnd);\n        }\n\n        barGroup.add(clickPanel);\n      };\n\n      SliderZoomView.prototype._renderDataShadow = function () {\n        var info = this._dataShadowInfo = this._prepareDataShadowInfo();\n\n        this._displayables.dataShadowSegs = [];\n\n        if (!info) {\n          return;\n        }\n\n        var size = this._size;\n        var oldSize = this._shadowSize || [];\n        var seriesModel = info.series;\n        var data = seriesModel.getRawData();\n        var candlestickDim = seriesModel.getShadowDim && seriesModel.getShadowDim();\n        var otherDim = candlestickDim && data.getDimensionInfo(candlestickDim) ? seriesModel.getShadowDim() // @see candlestick\n        : info.otherDim;\n\n        if (otherDim == null) {\n          return;\n        }\n\n        var polygonPts = this._shadowPolygonPts;\n        var polylinePts = this._shadowPolylinePts; // Not re-render if data doesn't change.\n\n        if (data !== this._shadowData || otherDim !== this._shadowDim || size[0] !== oldSize[0] || size[1] !== oldSize[1]) {\n          var otherDataExtent_1 = data.getDataExtent(otherDim); // Nice extent.\n\n          var otherOffset = (otherDataExtent_1[1] - otherDataExtent_1[0]) * 0.3;\n          otherDataExtent_1 = [otherDataExtent_1[0] - otherOffset, otherDataExtent_1[1] + otherOffset];\n          var otherShadowExtent_1 = [0, size[1]];\n          var thisShadowExtent = [0, size[0]];\n          var areaPoints_1 = [[size[0], 0], [0, 0]];\n          var linePoints_1 = [];\n          var step_1 = thisShadowExtent[1] / (data.count() - 1);\n          var thisCoord_1 = 0; // Optimize for large data shadow\n\n          var stride_1 = Math.round(data.count() / size[0]);\n          var lastIsEmpty_1;\n          data.each([otherDim], function (value, index) {\n            if (stride_1 > 0 && index % stride_1) {\n              thisCoord_1 += step_1;\n              return;\n            } // FIXME\n            // Should consider axis.min/axis.max when drawing dataShadow.\n            // FIXME\n            // 应该使用统一的空判断？还是在list里进行空判断？\n\n\n            var isEmpty = value == null || isNaN(value) || value === ''; // See #4235.\n\n            var otherCoord = isEmpty ? 0 : linearMap(value, otherDataExtent_1, otherShadowExtent_1, true); // Attempt to draw data shadow precisely when there are empty value.\n\n            if (isEmpty && !lastIsEmpty_1 && index) {\n              areaPoints_1.push([areaPoints_1[areaPoints_1.length - 1][0], 0]);\n              linePoints_1.push([linePoints_1[linePoints_1.length - 1][0], 0]);\n            } else if (!isEmpty && lastIsEmpty_1) {\n              areaPoints_1.push([thisCoord_1, 0]);\n              linePoints_1.push([thisCoord_1, 0]);\n            }\n\n            areaPoints_1.push([thisCoord_1, otherCoord]);\n            linePoints_1.push([thisCoord_1, otherCoord]);\n            thisCoord_1 += step_1;\n            lastIsEmpty_1 = isEmpty;\n          });\n          polygonPts = this._shadowPolygonPts = areaPoints_1;\n          polylinePts = this._shadowPolylinePts = linePoints_1;\n        }\n\n        this._shadowData = data;\n        this._shadowDim = otherDim;\n        this._shadowSize = [size[0], size[1]];\n        var dataZoomModel = this.dataZoomModel;\n\n        function createDataShadowGroup(isSelectedArea) {\n          var model = dataZoomModel.getModel(isSelectedArea ? 'selectedDataBackground' : 'dataBackground');\n          var group = new Group();\n          var polygon = new Polygon({\n            shape: {\n              points: polygonPts\n            },\n            segmentIgnoreThreshold: 1,\n            style: model.getModel('areaStyle').getAreaStyle(),\n            silent: true,\n            z2: -20\n          });\n          var polyline = new Polyline({\n            shape: {\n              points: polylinePts\n            },\n            segmentIgnoreThreshold: 1,\n            style: model.getModel('lineStyle').getLineStyle(),\n            silent: true,\n            z2: -19\n          });\n          group.add(polygon);\n          group.add(polyline);\n          return group;\n        } // let dataBackgroundModel = dataZoomModel.getModel('dataBackground');\n\n\n        for (var i = 0; i < 3; i++) {\n          var group = createDataShadowGroup(i === 1);\n\n          this._displayables.sliderGroup.add(group);\n\n          this._displayables.dataShadowSegs.push(group);\n        }\n      };\n\n      SliderZoomView.prototype._prepareDataShadowInfo = function () {\n        var dataZoomModel = this.dataZoomModel;\n        var showDataShadow = dataZoomModel.get('showDataShadow');\n\n        if (showDataShadow === false) {\n          return;\n        } // Find a representative series.\n\n\n        var result;\n        var ecModel = this.ecModel;\n        dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n          var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels();\n          each(seriesModels, function (seriesModel) {\n            if (result) {\n              return;\n            }\n\n            if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) {\n              return;\n            }\n\n            var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis;\n            var otherDim = getOtherDim(axisDim);\n            var otherAxisInverse;\n            var coordSys = seriesModel.coordinateSystem;\n\n            if (otherDim != null && coordSys.getOtherAxis) {\n              otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;\n            }\n\n            otherDim = seriesModel.getData().mapDimension(otherDim);\n            result = {\n              thisAxis: thisAxis,\n              series: seriesModel,\n              thisDim: axisDim,\n              otherDim: otherDim,\n              otherAxisInverse: otherAxisInverse\n            };\n          }, this);\n        }, this);\n        return result;\n      };\n\n      SliderZoomView.prototype._renderHandle = function () {\n        var thisGroup = this.group;\n        var displayables = this._displayables;\n        var handles = displayables.handles = [null, null];\n        var handleLabels = displayables.handleLabels = [null, null];\n        var sliderGroup = this._displayables.sliderGroup;\n        var size = this._size;\n        var dataZoomModel = this.dataZoomModel;\n        var api = this.api;\n        var borderRadius = dataZoomModel.get('borderRadius') || 0;\n        var brushSelect = dataZoomModel.get('brushSelect');\n        var filler = displayables.filler = new Rect$1({\n          silent: brushSelect,\n          style: {\n            fill: dataZoomModel.get('fillerColor')\n          },\n          textConfig: {\n            position: 'inside'\n          }\n        });\n        sliderGroup.add(filler); // Frame border.\n\n        sliderGroup.add(new Rect$1({\n          silent: true,\n          subPixelOptimize: true,\n          shape: {\n            x: 0,\n            y: 0,\n            width: size[0],\n            height: size[1],\n            r: borderRadius\n          },\n          style: {\n            // deprecated option\n            stroke: dataZoomModel.get('dataBackgroundColor') || dataZoomModel.get('borderColor'),\n            lineWidth: DEFAULT_FRAME_BORDER_WIDTH,\n            fill: 'rgba(0,0,0,0)'\n          }\n        })); // Left and right handle to resize\n\n        each([0, 1], function (handleIndex) {\n          var iconStr = dataZoomModel.get('handleIcon');\n\n          if (!symbolBuildProxies[iconStr] && iconStr.indexOf('path://') < 0 && iconStr.indexOf('image://') < 0) {\n            // Compatitable with the old icon parsers. Which can use a path string without path://\n            iconStr = 'path://' + iconStr;\n\n            if (\"development\" !== 'production') {\n              deprecateLog('handleIcon now needs \\'path://\\' prefix when using a path string');\n            }\n          }\n\n          var path = createSymbol(iconStr, -1, 0, 2, 2, null, true);\n          path.attr({\n            cursor: getCursor(this._orient),\n            draggable: true,\n            drift: bind(this._onDragMove, this, handleIndex),\n            ondragend: bind(this._onDragEnd, this),\n            onmouseover: bind(this._showDataInfo, this, true),\n            onmouseout: bind(this._showDataInfo, this, false),\n            z2: 5\n          });\n          var bRect = path.getBoundingRect();\n          var handleSize = dataZoomModel.get('handleSize');\n          this._handleHeight = parsePercent$1(handleSize, this._size[1]);\n          this._handleWidth = bRect.width / bRect.height * this._handleHeight;\n          path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle());\n          path.style.strokeNoScale = true;\n          path.rectHover = true;\n          path.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'handleStyle']).getItemStyle();\n          enableHoverEmphasis(path);\n          var handleColor = dataZoomModel.get('handleColor'); // deprecated option\n          // Compatitable with previous version\n\n          if (handleColor != null) {\n            path.style.fill = handleColor;\n          }\n\n          sliderGroup.add(handles[handleIndex] = path);\n          var textStyleModel = dataZoomModel.getModel('textStyle');\n          thisGroup.add(handleLabels[handleIndex] = new ZRText({\n            silent: true,\n            invisible: true,\n            style: createTextStyle(textStyleModel, {\n              x: 0,\n              y: 0,\n              text: '',\n              verticalAlign: 'middle',\n              align: 'center',\n              fill: textStyleModel.getTextColor(),\n              font: textStyleModel.getFont()\n            }),\n            z2: 10\n          }));\n        }, this); // Handle to move. Only visible when brushSelect is set true.\n\n        var actualMoveZone = filler;\n\n        if (brushSelect) {\n          var moveHandleHeight = parsePercent$1(dataZoomModel.get('moveHandleSize'), size[1]);\n          var moveHandle_1 = displayables.moveHandle = new Rect({\n            style: dataZoomModel.getModel('moveHandleStyle').getItemStyle(),\n            silent: true,\n            shape: {\n              r: [0, 0, 2, 2],\n              y: size[1] - 0.5,\n              height: moveHandleHeight\n            }\n          });\n          var iconSize = moveHandleHeight * 0.8;\n          var moveHandleIcon = displayables.moveHandleIcon = createSymbol(dataZoomModel.get('moveHandleIcon'), -iconSize / 2, -iconSize / 2, iconSize, iconSize, '#fff', true);\n          moveHandleIcon.silent = true;\n          moveHandleIcon.y = size[1] + moveHandleHeight / 2 - 0.5;\n          moveHandle_1.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'moveHandleStyle']).getItemStyle();\n          var moveZoneExpandSize = Math.min(size[1] / 2, Math.max(moveHandleHeight, 10));\n          actualMoveZone = displayables.moveZone = new Rect({\n            invisible: true,\n            shape: {\n              y: size[1] - moveZoneExpandSize,\n              height: moveHandleHeight + moveZoneExpandSize\n            }\n          });\n          actualMoveZone.on('mouseover', function () {\n            api.enterEmphasis(moveHandle_1);\n          }).on('mouseout', function () {\n            api.leaveEmphasis(moveHandle_1);\n          });\n          sliderGroup.add(moveHandle_1);\n          sliderGroup.add(moveHandleIcon);\n          sliderGroup.add(actualMoveZone);\n        }\n\n        actualMoveZone.attr({\n          draggable: true,\n          cursor: getCursor(this._orient),\n          drift: bind(this._onDragMove, this, 'all'),\n          ondragstart: bind(this._showDataInfo, this, true),\n          ondragend: bind(this._onDragEnd, this),\n          onmouseover: bind(this._showDataInfo, this, true),\n          onmouseout: bind(this._showDataInfo, this, false)\n        });\n      };\n\n      SliderZoomView.prototype._resetInterval = function () {\n        var range = this._range = this.dataZoomModel.getPercentRange();\n\n        var viewExtent = this._getViewExtent();\n\n        this._handleEnds = [linearMap(range[0], [0, 100], viewExtent, true), linearMap(range[1], [0, 100], viewExtent, true)];\n      };\n\n      SliderZoomView.prototype._updateInterval = function (handleIndex, delta) {\n        var dataZoomModel = this.dataZoomModel;\n        var handleEnds = this._handleEnds;\n\n        var viewExtend = this._getViewExtent();\n\n        var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();\n        var percentExtent = [0, 100];\n        sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null);\n        var lastRange = this._range;\n        var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]);\n        return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];\n      };\n\n      SliderZoomView.prototype._updateView = function (nonRealtime) {\n        var displaybles = this._displayables;\n        var handleEnds = this._handleEnds;\n        var handleInterval = asc(handleEnds.slice());\n        var size = this._size;\n        each([0, 1], function (handleIndex) {\n          // Handles\n          var handle = displaybles.handles[handleIndex];\n          var handleHeight = this._handleHeight;\n          handle.attr({\n            scaleX: handleHeight / 2,\n            scaleY: handleHeight / 2,\n            // This is a trick, by adding an extra tiny offset to let the default handle's end point align to the drag window.\n            // NOTE: It may affect some custom shapes a bit. But we prefer to have better result by default.\n            x: handleEnds[handleIndex] + (handleIndex ? -1 : 1),\n            y: size[1] / 2 - handleHeight / 2\n          });\n        }, this); // Filler\n\n        displaybles.filler.setShape({\n          x: handleInterval[0],\n          y: 0,\n          width: handleInterval[1] - handleInterval[0],\n          height: size[1]\n        });\n        var viewExtent = {\n          x: handleInterval[0],\n          width: handleInterval[1] - handleInterval[0]\n        }; // Move handle\n\n        if (displaybles.moveHandle) {\n          displaybles.moveHandle.setShape(viewExtent);\n          displaybles.moveZone.setShape(viewExtent); // Force update path on the invisible object\n\n          displaybles.moveZone.getBoundingRect();\n          displaybles.moveHandleIcon && displaybles.moveHandleIcon.attr('x', viewExtent.x + viewExtent.width / 2);\n        } // update clip path of shadow.\n\n\n        var dataShadowSegs = displaybles.dataShadowSegs;\n        var segIntervals = [0, handleInterval[0], handleInterval[1], size[0]];\n\n        for (var i = 0; i < dataShadowSegs.length; i++) {\n          var segGroup = dataShadowSegs[i];\n          var clipPath = segGroup.getClipPath();\n\n          if (!clipPath) {\n            clipPath = new Rect();\n            segGroup.setClipPath(clipPath);\n          }\n\n          clipPath.setShape({\n            x: segIntervals[i],\n            y: 0,\n            width: segIntervals[i + 1] - segIntervals[i],\n            height: size[1]\n          });\n        }\n\n        this._updateDataInfo(nonRealtime);\n      };\n\n      SliderZoomView.prototype._updateDataInfo = function (nonRealtime) {\n        var dataZoomModel = this.dataZoomModel;\n        var displaybles = this._displayables;\n        var handleLabels = displaybles.handleLabels;\n        var orient = this._orient;\n        var labelTexts = ['', '']; // FIXME\n        // date型，支持formatter，autoformatter（ec2 date.getAutoFormatter）\n\n        if (dataZoomModel.get('showDetail')) {\n          var axisProxy = dataZoomModel.findRepresentativeAxisProxy();\n\n          if (axisProxy) {\n            var axis = axisProxy.getAxisModel().axis;\n            var range = this._range;\n            var dataInterval = nonRealtime // See #4434, data and axis are not processed and reset yet in non-realtime mode.\n            ? axisProxy.calculateDataWindow({\n              start: range[0],\n              end: range[1]\n            }).valueWindow : axisProxy.getDataValueWindow();\n            labelTexts = [this._formatLabel(dataInterval[0], axis), this._formatLabel(dataInterval[1], axis)];\n          }\n        }\n\n        var orderedHandleEnds = asc(this._handleEnds.slice());\n        setLabel.call(this, 0);\n        setLabel.call(this, 1);\n\n        function setLabel(handleIndex) {\n          // Label\n          // Text should not transform by barGroup.\n          // Ignore handlers transform\n          var barTransform = getTransform(displaybles.handles[handleIndex].parent, this.group);\n          var direction = transformDirection(handleIndex === 0 ? 'right' : 'left', barTransform);\n          var offset = this._handleWidth / 2 + LABEL_GAP;\n          var textPoint = applyTransform$1([orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), this._size[1] / 2], barTransform);\n          handleLabels[handleIndex].setStyle({\n            x: textPoint[0],\n            y: textPoint[1],\n            verticalAlign: orient === HORIZONTAL ? 'middle' : direction,\n            align: orient === HORIZONTAL ? direction : 'center',\n            text: labelTexts[handleIndex]\n          });\n        }\n      };\n\n      SliderZoomView.prototype._formatLabel = function (value, axis) {\n        var dataZoomModel = this.dataZoomModel;\n        var labelFormatter = dataZoomModel.get('labelFormatter');\n        var labelPrecision = dataZoomModel.get('labelPrecision');\n\n        if (labelPrecision == null || labelPrecision === 'auto') {\n          labelPrecision = axis.getPixelPrecision();\n        }\n\n        var valueStr = value == null || isNaN(value) ? '' // FIXME Glue code\n        : axis.type === 'category' || axis.type === 'time' ? axis.scale.getLabel({\n          value: Math.round(value)\n        }) // param of toFixed should less then 20.\n        : value.toFixed(Math.min(labelPrecision, 20));\n        return isFunction(labelFormatter) ? labelFormatter(value, valueStr) : isString(labelFormatter) ? labelFormatter.replace('{value}', valueStr) : valueStr;\n      };\n      /**\n       * @param showOrHide true: show, false: hide\n       */\n\n\n      SliderZoomView.prototype._showDataInfo = function (showOrHide) {\n        // Always show when drgging.\n        showOrHide = this._dragging || showOrHide;\n        var displayables = this._displayables;\n        var handleLabels = displayables.handleLabels;\n        handleLabels[0].attr('invisible', !showOrHide);\n        handleLabels[1].attr('invisible', !showOrHide); // Highlight move handle\n\n        displayables.moveHandle && this.api[showOrHide ? 'enterEmphasis' : 'leaveEmphasis'](displayables.moveHandle, 1);\n      };\n\n      SliderZoomView.prototype._onDragMove = function (handleIndex, dx, dy, event) {\n        this._dragging = true; // For mobile device, prevent screen slider on the button.\n\n        stop(event.event); // Transform dx, dy to bar coordination.\n\n        var barTransform = this._displayables.sliderGroup.getLocalTransform();\n\n        var vertex = applyTransform$1([dx, dy], barTransform, true);\n\n        var changed = this._updateInterval(handleIndex, vertex[0]);\n\n        var realtime = this.dataZoomModel.get('realtime');\n\n        this._updateView(!realtime); // Avoid dispatch dataZoom repeatly but range not changed,\n        // which cause bad visual effect when progressive enabled.\n\n\n        changed && realtime && this._dispatchZoomAction(true);\n      };\n\n      SliderZoomView.prototype._onDragEnd = function () {\n        this._dragging = false;\n\n        this._showDataInfo(false); // While in realtime mode and stream mode, dispatch action when\n        // drag end will cause the whole view rerender, which is unnecessary.\n\n\n        var realtime = this.dataZoomModel.get('realtime');\n        !realtime && this._dispatchZoomAction(false);\n      };\n\n      SliderZoomView.prototype._onClickPanel = function (e) {\n        var size = this._size;\n\n        var localPoint = this._displayables.sliderGroup.transformCoordToLocal(e.offsetX, e.offsetY);\n\n        if (localPoint[0] < 0 || localPoint[0] > size[0] || localPoint[1] < 0 || localPoint[1] > size[1]) {\n          return;\n        }\n\n        var handleEnds = this._handleEnds;\n        var center = (handleEnds[0] + handleEnds[1]) / 2;\n\n        var changed = this._updateInterval('all', localPoint[0] - center);\n\n        this._updateView();\n\n        changed && this._dispatchZoomAction(false);\n      };\n\n      SliderZoomView.prototype._onBrushStart = function (e) {\n        var x = e.offsetX;\n        var y = e.offsetY;\n        this._brushStart = new Point(x, y);\n        this._brushing = true;\n        this._brushStartTime = +new Date(); // this._updateBrushRect(x, y);\n      };\n\n      SliderZoomView.prototype._onBrushEnd = function (e) {\n        if (!this._brushing) {\n          return;\n        }\n\n        var brushRect = this._displayables.brushRect;\n        this._brushing = false;\n\n        if (!brushRect) {\n          return;\n        }\n\n        brushRect.attr('ignore', true);\n        var brushShape = brushRect.shape;\n        var brushEndTime = +new Date(); // console.log(brushEndTime - this._brushStartTime);\n\n        if (brushEndTime - this._brushStartTime < 200 && Math.abs(brushShape.width) < 5) {\n          // Will treat it as a click\n          return;\n        }\n\n        var viewExtend = this._getViewExtent();\n\n        var percentExtent = [0, 100];\n        this._range = asc([linearMap(brushShape.x, viewExtend, percentExtent, true), linearMap(brushShape.x + brushShape.width, viewExtend, percentExtent, true)]);\n        this._handleEnds = [brushShape.x, brushShape.x + brushShape.width];\n\n        this._updateView();\n\n        this._dispatchZoomAction(false);\n      };\n\n      SliderZoomView.prototype._onBrush = function (e) {\n        if (this._brushing) {\n          // For mobile device, prevent screen slider on the button.\n          stop(e.event);\n\n          this._updateBrushRect(e.offsetX, e.offsetY);\n        }\n      };\n\n      SliderZoomView.prototype._updateBrushRect = function (mouseX, mouseY) {\n        var displayables = this._displayables;\n        var dataZoomModel = this.dataZoomModel;\n        var brushRect = displayables.brushRect;\n\n        if (!brushRect) {\n          brushRect = displayables.brushRect = new Rect$1({\n            silent: true,\n            style: dataZoomModel.getModel('brushStyle').getItemStyle()\n          });\n          displayables.sliderGroup.add(brushRect);\n        }\n\n        brushRect.attr('ignore', false);\n        var brushStart = this._brushStart;\n        var sliderGroup = this._displayables.sliderGroup;\n        var endPoint = sliderGroup.transformCoordToLocal(mouseX, mouseY);\n        var startPoint = sliderGroup.transformCoordToLocal(brushStart.x, brushStart.y);\n        var size = this._size;\n        endPoint[0] = Math.max(Math.min(size[0], endPoint[0]), 0);\n        brushRect.setShape({\n          x: startPoint[0],\n          y: 0,\n          width: endPoint[0] - startPoint[0],\n          height: size[1]\n        });\n      };\n      /**\n       * This action will be throttled.\n       */\n\n\n      SliderZoomView.prototype._dispatchZoomAction = function (realtime) {\n        var range = this._range;\n        this.api.dispatchAction({\n          type: 'dataZoom',\n          from: this.uid,\n          dataZoomId: this.dataZoomModel.id,\n          animation: realtime ? REALTIME_ANIMATION_CONFIG : null,\n          start: range[0],\n          end: range[1]\n        });\n      };\n\n      SliderZoomView.prototype._findCoordRect = function () {\n        // Find the grid coresponding to the first axis referred by dataZoom.\n        var rect;\n        var coordSysInfoList = collectReferCoordSysModelInfo(this.dataZoomModel).infoList;\n\n        if (!rect && coordSysInfoList.length) {\n          var coordSys = coordSysInfoList[0].model.coordinateSystem;\n          rect = coordSys.getRect && coordSys.getRect();\n        }\n\n        if (!rect) {\n          var width = this.api.getWidth();\n          var height = this.api.getHeight();\n          rect = {\n            x: width * 0.2,\n            y: height * 0.2,\n            width: width * 0.6,\n            height: height * 0.6\n          };\n        }\n\n        return rect;\n      };\n\n      SliderZoomView.type = 'dataZoom.slider';\n      return SliderZoomView;\n    }(DataZoomView);\n\n    function getOtherDim(thisDim) {\n      // FIXME\n      // 这个逻辑和getOtherAxis里一致，但是写在这里是否不好\n      var map = {\n        x: 'y',\n        y: 'x',\n        radius: 'angle',\n        angle: 'radius'\n      };\n      return map[thisDim];\n    }\n\n    function getCursor(orient) {\n      return orient === 'vertical' ? 'ns-resize' : 'ew-resize';\n    }\n\n    function install$l(registers) {\n      registers.registerComponentModel(SliderZoomModel);\n      registers.registerComponentView(SliderZoomView);\n      installCommon(registers);\n    }\n\n    function install$m(registers) {\n      use(install$k);\n      use(install$l); // Do not install './dataZoomSelect',\n      // since it only work for toolbox dataZoom.\n    }\n\n    var DEFAULT_OPTION = {\n      label: {\n        enabled: true\n      },\n      decal: {\n        show: false\n      }\n    };\n    var inner$h = makeInner();\n    var decalPaletteScope = {};\n    function ariaVisual(ecModel, api) {\n      var ariaModel = ecModel.getModel('aria'); // See \"area enabled\" detection code in `GlobalModel.ts`.\n\n      if (!ariaModel.get('enabled')) {\n        return;\n      }\n\n      var defaultOption = clone(DEFAULT_OPTION);\n      merge(defaultOption.label, ecModel.getLocaleModel().get('aria'), false);\n      merge(ariaModel.option, defaultOption, false);\n      setDecal();\n      setLabel();\n\n      function setDecal() {\n        var decalModel = ariaModel.getModel('decal');\n        var useDecal = decalModel.get('show');\n\n        if (useDecal) {\n          // Each type of series use one scope.\n          // Pie and funnel are using different scopes.\n          var paletteScopeGroupByType_1 = createHashMap();\n          ecModel.eachSeries(function (seriesModel) {\n            if (seriesModel.isColorBySeries()) {\n              return;\n            }\n\n            var decalScope = paletteScopeGroupByType_1.get(seriesModel.type);\n\n            if (!decalScope) {\n              decalScope = {};\n              paletteScopeGroupByType_1.set(seriesModel.type, decalScope);\n            }\n\n            inner$h(seriesModel).scope = decalScope;\n          });\n          ecModel.eachRawSeries(function (seriesModel) {\n            if (ecModel.isSeriesFiltered(seriesModel)) {\n              return;\n            }\n\n            if (isFunction(seriesModel.enableAriaDecal)) {\n              // Let series define how to use decal palette on data\n              seriesModel.enableAriaDecal();\n              return;\n            }\n\n            var data = seriesModel.getData();\n\n            if (!seriesModel.isColorBySeries()) {\n              var dataAll_1 = seriesModel.getRawData();\n              var idxMap_1 = {};\n              var decalScope_1 = inner$h(seriesModel).scope;\n              data.each(function (idx) {\n                var rawIdx = data.getRawIndex(idx);\n                idxMap_1[rawIdx] = idx;\n              });\n              var dataCount_1 = dataAll_1.count();\n              dataAll_1.each(function (rawIdx) {\n                var idx = idxMap_1[rawIdx];\n                var name = dataAll_1.getName(rawIdx) || rawIdx + '';\n                var paletteDecal = getDecalFromPalette(seriesModel.ecModel, name, decalScope_1, dataCount_1);\n                var specifiedDecal = data.getItemVisual(idx, 'decal');\n                data.setItemVisual(idx, 'decal', mergeDecal(specifiedDecal, paletteDecal));\n              });\n            } else {\n              var paletteDecal = getDecalFromPalette(seriesModel.ecModel, seriesModel.name, decalPaletteScope, ecModel.getSeriesCount());\n              var specifiedDecal = data.getVisual('decal');\n              data.setVisual('decal', mergeDecal(specifiedDecal, paletteDecal));\n            }\n\n            function mergeDecal(specifiedDecal, paletteDecal) {\n              // Merge decal from palette to decal from itemStyle.\n              // User do not need to specify all of the decal props.\n              var resultDecal = specifiedDecal ? extend(extend({}, paletteDecal), specifiedDecal) : paletteDecal;\n              resultDecal.dirty = true;\n              return resultDecal;\n            }\n          });\n        }\n      }\n\n      function setLabel() {\n        var labelLocale = ecModel.getLocaleModel().get('aria');\n        var labelModel = ariaModel.getModel('label');\n        labelModel.option = defaults(labelModel.option, labelLocale);\n\n        if (!labelModel.get('enabled')) {\n          return;\n        }\n\n        var dom = api.getZr().dom;\n\n        if (labelModel.get('description')) {\n          dom.setAttribute('aria-label', labelModel.get('description'));\n          return;\n        }\n\n        var seriesCnt = ecModel.getSeriesCount();\n        var maxDataCnt = labelModel.get(['data', 'maxCount']) || 10;\n        var maxSeriesCnt = labelModel.get(['series', 'maxCount']) || 10;\n        var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt);\n        var ariaLabel;\n\n        if (seriesCnt < 1) {\n          // No series, no aria label\n          return;\n        } else {\n          var title = getTitle();\n\n          if (title) {\n            var withTitle = labelModel.get(['general', 'withTitle']);\n            ariaLabel = replace(withTitle, {\n              title: title\n            });\n          } else {\n            ariaLabel = labelModel.get(['general', 'withoutTitle']);\n          }\n\n          var seriesLabels_1 = [];\n          var prefix = seriesCnt > 1 ? labelModel.get(['series', 'multiple', 'prefix']) : labelModel.get(['series', 'single', 'prefix']);\n          ariaLabel += replace(prefix, {\n            seriesCount: seriesCnt\n          });\n          ecModel.eachSeries(function (seriesModel, idx) {\n            if (idx < displaySeriesCnt) {\n              var seriesLabel = void 0;\n              var seriesName = seriesModel.get('name');\n              var withName = seriesName ? 'withName' : 'withoutName';\n              seriesLabel = seriesCnt > 1 ? labelModel.get(['series', 'multiple', withName]) : labelModel.get(['series', 'single', withName]);\n              seriesLabel = replace(seriesLabel, {\n                seriesId: seriesModel.seriesIndex,\n                seriesName: seriesModel.get('name'),\n                seriesType: getSeriesTypeName(seriesModel.subType)\n              });\n              var data = seriesModel.getData();\n\n              if (data.count() > maxDataCnt) {\n                // Show part of data\n                var partialLabel = labelModel.get(['data', 'partialData']);\n                seriesLabel += replace(partialLabel, {\n                  displayCnt: maxDataCnt\n                });\n              } else {\n                seriesLabel += labelModel.get(['data', 'allData']);\n              }\n\n              var middleSeparator_1 = labelModel.get(['data', 'separator', 'middle']);\n              var endSeparator_1 = labelModel.get(['data', 'separator', 'end']);\n              var dataLabels = [];\n\n              for (var i = 0; i < data.count(); i++) {\n                if (i < maxDataCnt) {\n                  var name_1 = data.getName(i);\n                  var value = data.getValues(i);\n                  var dataLabel = labelModel.get(['data', name_1 ? 'withName' : 'withoutName']);\n                  dataLabels.push(replace(dataLabel, {\n                    name: name_1,\n                    value: value.join(middleSeparator_1)\n                  }));\n                }\n              }\n\n              seriesLabel += dataLabels.join(middleSeparator_1) + endSeparator_1;\n              seriesLabels_1.push(seriesLabel);\n            }\n          });\n          var separatorModel = labelModel.getModel(['series', 'multiple', 'separator']);\n          var middleSeparator = separatorModel.get('middle');\n          var endSeparator = separatorModel.get('end');\n          ariaLabel += seriesLabels_1.join(middleSeparator) + endSeparator;\n          dom.setAttribute('aria-label', ariaLabel);\n        }\n      }\n\n      function replace(str, keyValues) {\n        if (!isString(str)) {\n          return str;\n        }\n\n        var result = str;\n        each(keyValues, function (value, key) {\n          result = result.replace(new RegExp('\\\\{\\\\s*' + key + '\\\\s*\\\\}', 'g'), value);\n        });\n        return result;\n      }\n\n      function getTitle() {\n        var title = ecModel.get('title');\n\n        if (title && title.length) {\n          title = title[0];\n        }\n\n        return title && title.text;\n      }\n\n      function getSeriesTypeName(type) {\n        return ecModel.getLocaleModel().get(['series', 'typeNames'])[type] || '自定义图';\n      }\n    }\n\n    function ariaPreprocessor(option) {\n      if (!option || !option.aria) {\n        return;\n      }\n\n      var aria = option.aria; // aria.show is deprecated and should use aria.enabled instead\n\n      if (aria.show != null) {\n        aria.enabled = aria.show;\n      }\n\n      aria.label = aria.label || {}; // move description, general, series, data to be under aria.label\n\n      each(['description', 'general', 'series', 'data'], function (name) {\n        if (aria[name] != null) {\n          aria.label[name] = aria[name];\n        }\n      });\n    }\n\n    function install$n(registers) {\n      registers.registerPreprocessor(ariaPreprocessor);\n      registers.registerVisual(registers.PRIORITY.VISUAL.ARIA, ariaVisual);\n    }\n\n    var DatasetModel =\n    /** @class */\n    function (_super) {\n      __extends(DatasetModel, _super);\n\n      function DatasetModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataset';\n        return _this;\n      }\n\n      DatasetModel.prototype.init = function (option, parentModel, ecModel) {\n        _super.prototype.init.call(this, option, parentModel, ecModel);\n\n        this._sourceManager = new SourceManager(this);\n        disableTransformOptionMerge(this);\n      };\n\n      DatasetModel.prototype.mergeOption = function (newOption, ecModel) {\n        _super.prototype.mergeOption.call(this, newOption, ecModel);\n\n        disableTransformOptionMerge(this);\n      };\n\n      DatasetModel.prototype.optionUpdated = function () {\n        this._sourceManager.dirty();\n      };\n\n      DatasetModel.prototype.getSourceManager = function () {\n        return this._sourceManager;\n      };\n\n      DatasetModel.type = 'dataset';\n      DatasetModel.defaultOption = {\n        seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN\n      };\n      return DatasetModel;\n    }(ComponentModel);\n\n    var DatasetView =\n    /** @class */\n    function (_super) {\n      __extends(DatasetView, _super);\n\n      function DatasetView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataset';\n        return _this;\n      }\n\n      DatasetView.type = 'dataset';\n      return DatasetView;\n    }(ComponentView);\n\n    function install$o(registers) {\n      registers.registerComponentModel(DatasetModel);\n      registers.registerComponentView(DatasetView);\n    }\n\n    use([install$1]);\n    use([install]);\n    use([install$2, install$3, install$4, install$6]);\n    use([install$9, install$c, install$7, install$j, install$8, install$d, install$e, install$f, install$g, install$m, install$b, install$n, install$o]);\n\n    exports.Axis = Axis;\n    exports.ChartView = ChartView;\n    exports.ComponentModel = ComponentModel;\n    exports.ComponentView = ComponentView;\n    exports.List = SeriesData;\n    exports.Model = Model;\n    exports.PRIORITY = PRIORITY;\n    exports.SeriesModel = SeriesModel;\n    exports.color = color;\n    exports.connect = connect;\n    exports.dataTool = dataTool;\n    exports.dependencies = dependencies;\n    exports.disConnect = disConnect;\n    exports.disconnect = disconnect;\n    exports.dispose = dispose$1;\n    exports.env = env;\n    exports.extendChartView = extendChartView;\n    exports.extendComponentModel = extendComponentModel;\n    exports.extendComponentView = extendComponentView;\n    exports.extendSeriesModel = extendSeriesModel;\n    exports.format = format$1;\n    exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;\n    exports.getInstanceByDom = getInstanceByDom;\n    exports.getInstanceById = getInstanceById;\n    exports.getMap = getMap;\n    exports.graphic = graphic$1;\n    exports.helper = helper;\n    exports.init = init$1;\n    exports.innerDrawElementOnCanvas = brushSingle;\n    exports.matrix = matrix;\n    exports.number = number;\n    exports.parseGeoJSON = parseGeoJSON;\n    exports.parseGeoJson = parseGeoJSON;\n    exports.registerAction = registerAction;\n    exports.registerCoordinateSystem = registerCoordinateSystem;\n    exports.registerLayout = registerLayout;\n    exports.registerLoading = registerLoading;\n    exports.registerLocale = registerLocale;\n    exports.registerMap = registerMap;\n    exports.registerPostInit = registerPostInit;\n    exports.registerPostUpdate = registerPostUpdate;\n    exports.registerPreprocessor = registerPreprocessor;\n    exports.registerProcessor = registerProcessor;\n    exports.registerTheme = registerTheme;\n    exports.registerTransform = registerTransform;\n    exports.registerUpdateLifecycle = registerUpdateLifecycle;\n    exports.registerVisual = registerVisual;\n    exports.setCanvasCreator = setCanvasCreator;\n    exports.setPlatformAPI = setPlatformAPI;\n    exports.throttle = throttle;\n    exports.time = time;\n    exports.use = use;\n    exports.util = util$1;\n    exports.vector = vector;\n    exports.version = version$1;\n    exports.zrUtil = util;\n    exports.zrender = zrender;\n\n    Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=echarts.common.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/echarts/echarts.esm.js",
    "content": "\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n/*! *****************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise */\n\nvar extendStatics = function(d, b) {\n    extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n    return extendStatics(d, b);\n};\n\nfunction __extends(d, b) {\n    if (typeof b !== \"function\" && b !== null)\n        throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n    extendStatics(d, b);\n    function __() { this.constructor = d; }\n    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nvar Browser = (function () {\n    function Browser() {\n        this.firefox = false;\n        this.ie = false;\n        this.edge = false;\n        this.newEdge = false;\n        this.weChat = false;\n    }\n    return Browser;\n}());\nvar Env = (function () {\n    function Env() {\n        this.browser = new Browser();\n        this.node = false;\n        this.wxa = false;\n        this.worker = false;\n        this.svgSupported = false;\n        this.touchEventsSupported = false;\n        this.pointerEventsSupported = false;\n        this.domSupported = false;\n        this.transformSupported = false;\n        this.transform3dSupported = false;\n        this.hasGlobalWindow = typeof window !== 'undefined';\n    }\n    return Env;\n}());\nvar env = new Env();\nif (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {\n    env.wxa = true;\n    env.touchEventsSupported = true;\n}\nelse if (typeof document === 'undefined' && typeof self !== 'undefined') {\n    env.worker = true;\n}\nelse if (typeof navigator === 'undefined') {\n    env.node = true;\n    env.svgSupported = true;\n}\nelse {\n    detect(navigator.userAgent, env);\n}\nfunction detect(ua, env) {\n    var browser = env.browser;\n    var firefox = ua.match(/Firefox\\/([\\d.]+)/);\n    var ie = ua.match(/MSIE\\s([\\d.]+)/)\n        || ua.match(/Trident\\/.+?rv:(([\\d.]+))/);\n    var edge = ua.match(/Edge?\\/([\\d.]+)/);\n    var weChat = (/micromessenger/i).test(ua);\n    if (firefox) {\n        browser.firefox = true;\n        browser.version = firefox[1];\n    }\n    if (ie) {\n        browser.ie = true;\n        browser.version = ie[1];\n    }\n    if (edge) {\n        browser.edge = true;\n        browser.version = edge[1];\n        browser.newEdge = +edge[1].split('.')[0] > 18;\n    }\n    if (weChat) {\n        browser.weChat = true;\n    }\n    env.svgSupported = typeof SVGRect !== 'undefined';\n    env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge;\n    env.pointerEventsSupported = 'onpointerdown' in window\n        && (browser.edge || (browser.ie && +browser.version >= 11));\n    env.domSupported = typeof document !== 'undefined';\n    var style = document.documentElement.style;\n    env.transform3dSupported = ((browser.ie && 'transition' in style)\n        || browser.edge\n        || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix()))\n        || 'MozPerspective' in style)\n        && !('OTransition' in style);\n    env.transformSupported = env.transform3dSupported\n        || (browser.ie && +browser.version >= 9);\n}\n\nvar DEFAULT_FONT_SIZE = 12;\nvar DEFAULT_FONT_FAMILY = 'sans-serif';\nvar DEFAULT_FONT = DEFAULT_FONT_SIZE + \"px \" + DEFAULT_FONT_FAMILY;\nvar OFFSET = 20;\nvar SCALE = 100;\nvar defaultWidthMapStr = \"007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\\\\\WQb\\\\0FWLg\\\\bWb\\\\WQ\\\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\\\FFF5.5N\";\nfunction getTextWidthMap(mapStr) {\n    var map = {};\n    if (typeof JSON === 'undefined') {\n        return map;\n    }\n    for (var i = 0; i < mapStr.length; i++) {\n        var char = String.fromCharCode(i + 32);\n        var size = (mapStr.charCodeAt(i) - OFFSET) / SCALE;\n        map[char] = size;\n    }\n    return map;\n}\nvar DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr);\nvar platformApi = {\n    createCanvas: function () {\n        return typeof document !== 'undefined'\n            && document.createElement('canvas');\n    },\n    measureText: (function () {\n        var _ctx;\n        var _cachedFont;\n        return function (text, font) {\n            if (!_ctx) {\n                var canvas = platformApi.createCanvas();\n                _ctx = canvas && canvas.getContext('2d');\n            }\n            if (_ctx) {\n                if (_cachedFont !== font) {\n                    _cachedFont = _ctx.font = font || DEFAULT_FONT;\n                }\n                return _ctx.measureText(text);\n            }\n            else {\n                text = text || '';\n                font = font || DEFAULT_FONT;\n                var res = /(\\d+)px/.exec(font);\n                var fontSize = res && +res[1] || DEFAULT_FONT_SIZE;\n                var width = 0;\n                if (font.indexOf('mono') >= 0) {\n                    width = fontSize * text.length;\n                }\n                else {\n                    for (var i = 0; i < text.length; i++) {\n                        var preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]];\n                        width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize);\n                    }\n                }\n                return { width: width };\n            }\n        };\n    })(),\n    loadImage: function (src, onload, onerror) {\n        var image = new Image();\n        image.onload = onload;\n        image.onerror = onerror;\n        image.src = src;\n        return image;\n    }\n};\nfunction setPlatformAPI(newPlatformApis) {\n    for (var key in platformApi) {\n        if (newPlatformApis[key]) {\n            platformApi[key] = newPlatformApis[key];\n        }\n    }\n}\n\nvar BUILTIN_OBJECT = reduce([\n    'Function',\n    'RegExp',\n    'Date',\n    'Error',\n    'CanvasGradient',\n    'CanvasPattern',\n    'Image',\n    'Canvas'\n], function (obj, val) {\n    obj['[object ' + val + ']'] = true;\n    return obj;\n}, {});\nvar TYPED_ARRAY = reduce([\n    'Int8',\n    'Uint8',\n    'Uint8Clamped',\n    'Int16',\n    'Uint16',\n    'Int32',\n    'Uint32',\n    'Float32',\n    'Float64'\n], function (obj, val) {\n    obj['[object ' + val + 'Array]'] = true;\n    return obj;\n}, {});\nvar objToString = Object.prototype.toString;\nvar arrayProto = Array.prototype;\nvar nativeForEach = arrayProto.forEach;\nvar nativeFilter = arrayProto.filter;\nvar nativeSlice = arrayProto.slice;\nvar nativeMap = arrayProto.map;\nvar ctorFunction = function () { }.constructor;\nvar protoFunction = ctorFunction ? ctorFunction.prototype : null;\nvar protoKey = '__proto__';\nvar idStart = 0x0907;\nfunction guid() {\n    return idStart++;\n}\nfunction logError() {\n    var args = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        args[_i] = arguments[_i];\n    }\n    if (typeof console !== 'undefined') {\n        console.error.apply(console, args);\n    }\n}\nfunction clone(source) {\n    if (source == null || typeof source !== 'object') {\n        return source;\n    }\n    var result = source;\n    var typeStr = objToString.call(source);\n    if (typeStr === '[object Array]') {\n        if (!isPrimitive(source)) {\n            result = [];\n            for (var i = 0, len = source.length; i < len; i++) {\n                result[i] = clone(source[i]);\n            }\n        }\n    }\n    else if (TYPED_ARRAY[typeStr]) {\n        if (!isPrimitive(source)) {\n            var Ctor = source.constructor;\n            if (Ctor.from) {\n                result = Ctor.from(source);\n            }\n            else {\n                result = new Ctor(source.length);\n                for (var i = 0, len = source.length; i < len; i++) {\n                    result[i] = source[i];\n                }\n            }\n        }\n    }\n    else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {\n        result = {};\n        for (var key in source) {\n            if (source.hasOwnProperty(key) && key !== protoKey) {\n                result[key] = clone(source[key]);\n            }\n        }\n    }\n    return result;\n}\nfunction merge(target, source, overwrite) {\n    if (!isObject(source) || !isObject(target)) {\n        return overwrite ? clone(source) : target;\n    }\n    for (var key in source) {\n        if (source.hasOwnProperty(key) && key !== protoKey) {\n            var targetProp = target[key];\n            var sourceProp = source[key];\n            if (isObject(sourceProp)\n                && isObject(targetProp)\n                && !isArray(sourceProp)\n                && !isArray(targetProp)\n                && !isDom(sourceProp)\n                && !isDom(targetProp)\n                && !isBuiltInObject(sourceProp)\n                && !isBuiltInObject(targetProp)\n                && !isPrimitive(sourceProp)\n                && !isPrimitive(targetProp)) {\n                merge(targetProp, sourceProp, overwrite);\n            }\n            else if (overwrite || !(key in target)) {\n                target[key] = clone(source[key]);\n            }\n        }\n    }\n    return target;\n}\nfunction mergeAll(targetAndSources, overwrite) {\n    var result = targetAndSources[0];\n    for (var i = 1, len = targetAndSources.length; i < len; i++) {\n        result = merge(result, targetAndSources[i], overwrite);\n    }\n    return result;\n}\nfunction extend(target, source) {\n    if (Object.assign) {\n        Object.assign(target, source);\n    }\n    else {\n        for (var key in source) {\n            if (source.hasOwnProperty(key) && key !== protoKey) {\n                target[key] = source[key];\n            }\n        }\n    }\n    return target;\n}\nfunction defaults(target, source, overlay) {\n    var keysArr = keys(source);\n    for (var i = 0; i < keysArr.length; i++) {\n        var key = keysArr[i];\n        if ((overlay ? source[key] != null : target[key] == null)) {\n            target[key] = source[key];\n        }\n    }\n    return target;\n}\nvar createCanvas = platformApi.createCanvas;\nfunction indexOf(array, value) {\n    if (array) {\n        if (array.indexOf) {\n            return array.indexOf(value);\n        }\n        for (var i = 0, len = array.length; i < len; i++) {\n            if (array[i] === value) {\n                return i;\n            }\n        }\n    }\n    return -1;\n}\nfunction inherits(clazz, baseClazz) {\n    var clazzPrototype = clazz.prototype;\n    function F() { }\n    F.prototype = baseClazz.prototype;\n    clazz.prototype = new F();\n    for (var prop in clazzPrototype) {\n        if (clazzPrototype.hasOwnProperty(prop)) {\n            clazz.prototype[prop] = clazzPrototype[prop];\n        }\n    }\n    clazz.prototype.constructor = clazz;\n    clazz.superClass = baseClazz;\n}\nfunction mixin(target, source, override) {\n    target = 'prototype' in target ? target.prototype : target;\n    source = 'prototype' in source ? source.prototype : source;\n    if (Object.getOwnPropertyNames) {\n        var keyList = Object.getOwnPropertyNames(source);\n        for (var i = 0; i < keyList.length; i++) {\n            var key = keyList[i];\n            if (key !== 'constructor') {\n                if ((override ? source[key] != null : target[key] == null)) {\n                    target[key] = source[key];\n                }\n            }\n        }\n    }\n    else {\n        defaults(target, source, override);\n    }\n}\nfunction isArrayLike(data) {\n    if (!data) {\n        return false;\n    }\n    if (typeof data === 'string') {\n        return false;\n    }\n    return typeof data.length === 'number';\n}\nfunction each(arr, cb, context) {\n    if (!(arr && cb)) {\n        return;\n    }\n    if (arr.forEach && arr.forEach === nativeForEach) {\n        arr.forEach(cb, context);\n    }\n    else if (arr.length === +arr.length) {\n        for (var i = 0, len = arr.length; i < len; i++) {\n            cb.call(context, arr[i], i, arr);\n        }\n    }\n    else {\n        for (var key in arr) {\n            if (arr.hasOwnProperty(key)) {\n                cb.call(context, arr[key], key, arr);\n            }\n        }\n    }\n}\nfunction map(arr, cb, context) {\n    if (!arr) {\n        return [];\n    }\n    if (!cb) {\n        return slice(arr);\n    }\n    if (arr.map && arr.map === nativeMap) {\n        return arr.map(cb, context);\n    }\n    else {\n        var result = [];\n        for (var i = 0, len = arr.length; i < len; i++) {\n            result.push(cb.call(context, arr[i], i, arr));\n        }\n        return result;\n    }\n}\nfunction reduce(arr, cb, memo, context) {\n    if (!(arr && cb)) {\n        return;\n    }\n    for (var i = 0, len = arr.length; i < len; i++) {\n        memo = cb.call(context, memo, arr[i], i, arr);\n    }\n    return memo;\n}\nfunction filter(arr, cb, context) {\n    if (!arr) {\n        return [];\n    }\n    if (!cb) {\n        return slice(arr);\n    }\n    if (arr.filter && arr.filter === nativeFilter) {\n        return arr.filter(cb, context);\n    }\n    else {\n        var result = [];\n        for (var i = 0, len = arr.length; i < len; i++) {\n            if (cb.call(context, arr[i], i, arr)) {\n                result.push(arr[i]);\n            }\n        }\n        return result;\n    }\n}\nfunction find(arr, cb, context) {\n    if (!(arr && cb)) {\n        return;\n    }\n    for (var i = 0, len = arr.length; i < len; i++) {\n        if (cb.call(context, arr[i], i, arr)) {\n            return arr[i];\n        }\n    }\n}\nfunction keys(obj) {\n    if (!obj) {\n        return [];\n    }\n    if (Object.keys) {\n        return Object.keys(obj);\n    }\n    var keyList = [];\n    for (var key in obj) {\n        if (obj.hasOwnProperty(key)) {\n            keyList.push(key);\n        }\n    }\n    return keyList;\n}\nfunction bindPolyfill(func, context) {\n    var args = [];\n    for (var _i = 2; _i < arguments.length; _i++) {\n        args[_i - 2] = arguments[_i];\n    }\n    return function () {\n        return func.apply(context, args.concat(nativeSlice.call(arguments)));\n    };\n}\nvar bind = (protoFunction && isFunction(protoFunction.bind))\n    ? protoFunction.call.bind(protoFunction.bind)\n    : bindPolyfill;\nfunction curry(func) {\n    var args = [];\n    for (var _i = 1; _i < arguments.length; _i++) {\n        args[_i - 1] = arguments[_i];\n    }\n    return function () {\n        return func.apply(this, args.concat(nativeSlice.call(arguments)));\n    };\n}\nfunction isArray(value) {\n    if (Array.isArray) {\n        return Array.isArray(value);\n    }\n    return objToString.call(value) === '[object Array]';\n}\nfunction isFunction(value) {\n    return typeof value === 'function';\n}\nfunction isString(value) {\n    return typeof value === 'string';\n}\nfunction isStringSafe(value) {\n    return objToString.call(value) === '[object String]';\n}\nfunction isNumber(value) {\n    return typeof value === 'number';\n}\nfunction isObject(value) {\n    var type = typeof value;\n    return type === 'function' || (!!value && type === 'object');\n}\nfunction isBuiltInObject(value) {\n    return !!BUILTIN_OBJECT[objToString.call(value)];\n}\nfunction isTypedArray(value) {\n    return !!TYPED_ARRAY[objToString.call(value)];\n}\nfunction isDom(value) {\n    return typeof value === 'object'\n        && typeof value.nodeType === 'number'\n        && typeof value.ownerDocument === 'object';\n}\nfunction isGradientObject(value) {\n    return value.colorStops != null;\n}\nfunction isImagePatternObject(value) {\n    return value.image != null;\n}\nfunction isRegExp(value) {\n    return objToString.call(value) === '[object RegExp]';\n}\nfunction eqNaN(value) {\n    return value !== value;\n}\nfunction retrieve() {\n    var args = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        args[_i] = arguments[_i];\n    }\n    for (var i = 0, len = args.length; i < len; i++) {\n        if (args[i] != null) {\n            return args[i];\n        }\n    }\n}\nfunction retrieve2(value0, value1) {\n    return value0 != null\n        ? value0\n        : value1;\n}\nfunction retrieve3(value0, value1, value2) {\n    return value0 != null\n        ? value0\n        : value1 != null\n            ? value1\n            : value2;\n}\nfunction slice(arr) {\n    var args = [];\n    for (var _i = 1; _i < arguments.length; _i++) {\n        args[_i - 1] = arguments[_i];\n    }\n    return nativeSlice.apply(arr, args);\n}\nfunction normalizeCssArray(val) {\n    if (typeof (val) === 'number') {\n        return [val, val, val, val];\n    }\n    var len = val.length;\n    if (len === 2) {\n        return [val[0], val[1], val[0], val[1]];\n    }\n    else if (len === 3) {\n        return [val[0], val[1], val[2], val[1]];\n    }\n    return val;\n}\nfunction assert(condition, message) {\n    if (!condition) {\n        throw new Error(message);\n    }\n}\nfunction trim(str) {\n    if (str == null) {\n        return null;\n    }\n    else if (typeof str.trim === 'function') {\n        return str.trim();\n    }\n    else {\n        return str.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n    }\n}\nvar primitiveKey = '__ec_primitive__';\nfunction setAsPrimitive(obj) {\n    obj[primitiveKey] = true;\n}\nfunction isPrimitive(obj) {\n    return obj[primitiveKey];\n}\nvar MapPolyfill = (function () {\n    function MapPolyfill() {\n        this.data = {};\n    }\n    MapPolyfill.prototype[\"delete\"] = function (key) {\n        var existed = this.has(key);\n        if (existed) {\n            delete this.data[key];\n        }\n        return existed;\n    };\n    MapPolyfill.prototype.has = function (key) {\n        return this.data.hasOwnProperty(key);\n    };\n    MapPolyfill.prototype.get = function (key) {\n        return this.data[key];\n    };\n    MapPolyfill.prototype.set = function (key, value) {\n        this.data[key] = value;\n        return this;\n    };\n    MapPolyfill.prototype.keys = function () {\n        return keys(this.data);\n    };\n    MapPolyfill.prototype.forEach = function (callback) {\n        var data = this.data;\n        for (var key in data) {\n            if (data.hasOwnProperty(key)) {\n                callback(data[key], key);\n            }\n        }\n    };\n    return MapPolyfill;\n}());\nvar isNativeMapSupported = typeof Map === 'function';\nfunction maybeNativeMap() {\n    return (isNativeMapSupported ? new Map() : new MapPolyfill());\n}\nvar HashMap = (function () {\n    function HashMap(obj) {\n        var isArr = isArray(obj);\n        this.data = maybeNativeMap();\n        var thisMap = this;\n        (obj instanceof HashMap)\n            ? obj.each(visit)\n            : (obj && each(obj, visit));\n        function visit(value, key) {\n            isArr ? thisMap.set(value, key) : thisMap.set(key, value);\n        }\n    }\n    HashMap.prototype.hasKey = function (key) {\n        return this.data.has(key);\n    };\n    HashMap.prototype.get = function (key) {\n        return this.data.get(key);\n    };\n    HashMap.prototype.set = function (key, value) {\n        this.data.set(key, value);\n        return value;\n    };\n    HashMap.prototype.each = function (cb, context) {\n        this.data.forEach(function (value, key) {\n            cb.call(context, value, key);\n        });\n    };\n    HashMap.prototype.keys = function () {\n        var keys = this.data.keys();\n        return isNativeMapSupported\n            ? Array.from(keys)\n            : keys;\n    };\n    HashMap.prototype.removeKey = function (key) {\n        this.data[\"delete\"](key);\n    };\n    return HashMap;\n}());\nfunction createHashMap(obj) {\n    return new HashMap(obj);\n}\nfunction concatArray(a, b) {\n    var newArray = new a.constructor(a.length + b.length);\n    for (var i = 0; i < a.length; i++) {\n        newArray[i] = a[i];\n    }\n    var offset = a.length;\n    for (var i = 0; i < b.length; i++) {\n        newArray[i + offset] = b[i];\n    }\n    return newArray;\n}\nfunction createObject(proto, properties) {\n    var obj;\n    if (Object.create) {\n        obj = Object.create(proto);\n    }\n    else {\n        var StyleCtor = function () { };\n        StyleCtor.prototype = proto;\n        obj = new StyleCtor();\n    }\n    if (properties) {\n        extend(obj, properties);\n    }\n    return obj;\n}\nfunction disableUserSelect(dom) {\n    var domStyle = dom.style;\n    domStyle.webkitUserSelect = 'none';\n    domStyle.userSelect = 'none';\n    domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)';\n    domStyle['-webkit-touch-callout'] = 'none';\n}\nfunction hasOwn(own, prop) {\n    return own.hasOwnProperty(prop);\n}\nfunction noop() { }\nvar RADIAN_TO_DEGREE = 180 / Math.PI;\n\nvar util = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    guid: guid,\n    logError: logError,\n    clone: clone,\n    merge: merge,\n    mergeAll: mergeAll,\n    extend: extend,\n    defaults: defaults,\n    createCanvas: createCanvas,\n    indexOf: indexOf,\n    inherits: inherits,\n    mixin: mixin,\n    isArrayLike: isArrayLike,\n    each: each,\n    map: map,\n    reduce: reduce,\n    filter: filter,\n    find: find,\n    keys: keys,\n    bind: bind,\n    curry: curry,\n    isArray: isArray,\n    isFunction: isFunction,\n    isString: isString,\n    isStringSafe: isStringSafe,\n    isNumber: isNumber,\n    isObject: isObject,\n    isBuiltInObject: isBuiltInObject,\n    isTypedArray: isTypedArray,\n    isDom: isDom,\n    isGradientObject: isGradientObject,\n    isImagePatternObject: isImagePatternObject,\n    isRegExp: isRegExp,\n    eqNaN: eqNaN,\n    retrieve: retrieve,\n    retrieve2: retrieve2,\n    retrieve3: retrieve3,\n    slice: slice,\n    normalizeCssArray: normalizeCssArray,\n    assert: assert,\n    trim: trim,\n    setAsPrimitive: setAsPrimitive,\n    isPrimitive: isPrimitive,\n    HashMap: HashMap,\n    createHashMap: createHashMap,\n    concatArray: concatArray,\n    createObject: createObject,\n    disableUserSelect: disableUserSelect,\n    hasOwn: hasOwn,\n    noop: noop,\n    RADIAN_TO_DEGREE: RADIAN_TO_DEGREE\n});\n\nfunction create(x, y) {\n    if (x == null) {\n        x = 0;\n    }\n    if (y == null) {\n        y = 0;\n    }\n    return [x, y];\n}\nfunction copy(out, v) {\n    out[0] = v[0];\n    out[1] = v[1];\n    return out;\n}\nfunction clone$1(v) {\n    return [v[0], v[1]];\n}\nfunction set(out, a, b) {\n    out[0] = a;\n    out[1] = b;\n    return out;\n}\nfunction add(out, v1, v2) {\n    out[0] = v1[0] + v2[0];\n    out[1] = v1[1] + v2[1];\n    return out;\n}\nfunction scaleAndAdd(out, v1, v2, a) {\n    out[0] = v1[0] + v2[0] * a;\n    out[1] = v1[1] + v2[1] * a;\n    return out;\n}\nfunction sub(out, v1, v2) {\n    out[0] = v1[0] - v2[0];\n    out[1] = v1[1] - v2[1];\n    return out;\n}\nfunction len(v) {\n    return Math.sqrt(lenSquare(v));\n}\nvar length = len;\nfunction lenSquare(v) {\n    return v[0] * v[0] + v[1] * v[1];\n}\nvar lengthSquare = lenSquare;\nfunction mul(out, v1, v2) {\n    out[0] = v1[0] * v2[0];\n    out[1] = v1[1] * v2[1];\n    return out;\n}\nfunction div(out, v1, v2) {\n    out[0] = v1[0] / v2[0];\n    out[1] = v1[1] / v2[1];\n    return out;\n}\nfunction dot(v1, v2) {\n    return v1[0] * v2[0] + v1[1] * v2[1];\n}\nfunction scale(out, v, s) {\n    out[0] = v[0] * s;\n    out[1] = v[1] * s;\n    return out;\n}\nfunction normalize(out, v) {\n    var d = len(v);\n    if (d === 0) {\n        out[0] = 0;\n        out[1] = 0;\n    }\n    else {\n        out[0] = v[0] / d;\n        out[1] = v[1] / d;\n    }\n    return out;\n}\nfunction distance(v1, v2) {\n    return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0])\n        + (v1[1] - v2[1]) * (v1[1] - v2[1]));\n}\nvar dist = distance;\nfunction distanceSquare(v1, v2) {\n    return (v1[0] - v2[0]) * (v1[0] - v2[0])\n        + (v1[1] - v2[1]) * (v1[1] - v2[1]);\n}\nvar distSquare = distanceSquare;\nfunction negate(out, v) {\n    out[0] = -v[0];\n    out[1] = -v[1];\n    return out;\n}\nfunction lerp(out, v1, v2, t) {\n    out[0] = v1[0] + t * (v2[0] - v1[0]);\n    out[1] = v1[1] + t * (v2[1] - v1[1]);\n    return out;\n}\nfunction applyTransform(out, v, m) {\n    var x = v[0];\n    var y = v[1];\n    out[0] = m[0] * x + m[2] * y + m[4];\n    out[1] = m[1] * x + m[3] * y + m[5];\n    return out;\n}\nfunction min(out, v1, v2) {\n    out[0] = Math.min(v1[0], v2[0]);\n    out[1] = Math.min(v1[1], v2[1]);\n    return out;\n}\nfunction max(out, v1, v2) {\n    out[0] = Math.max(v1[0], v2[0]);\n    out[1] = Math.max(v1[1], v2[1]);\n    return out;\n}\n\nvar vector = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    create: create,\n    copy: copy,\n    clone: clone$1,\n    set: set,\n    add: add,\n    scaleAndAdd: scaleAndAdd,\n    sub: sub,\n    len: len,\n    length: length,\n    lenSquare: lenSquare,\n    lengthSquare: lengthSquare,\n    mul: mul,\n    div: div,\n    dot: dot,\n    scale: scale,\n    normalize: normalize,\n    distance: distance,\n    dist: dist,\n    distanceSquare: distanceSquare,\n    distSquare: distSquare,\n    negate: negate,\n    lerp: lerp,\n    applyTransform: applyTransform,\n    min: min,\n    max: max\n});\n\nvar Param = (function () {\n    function Param(target, e) {\n        this.target = target;\n        this.topTarget = e && e.topTarget;\n    }\n    return Param;\n}());\nvar Draggable = (function () {\n    function Draggable(handler) {\n        this.handler = handler;\n        handler.on('mousedown', this._dragStart, this);\n        handler.on('mousemove', this._drag, this);\n        handler.on('mouseup', this._dragEnd, this);\n    }\n    Draggable.prototype._dragStart = function (e) {\n        var draggingTarget = e.target;\n        while (draggingTarget && !draggingTarget.draggable) {\n            draggingTarget = draggingTarget.parent || draggingTarget.__hostTarget;\n        }\n        if (draggingTarget) {\n            this._draggingTarget = draggingTarget;\n            draggingTarget.dragging = true;\n            this._x = e.offsetX;\n            this._y = e.offsetY;\n            this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragstart', e.event);\n        }\n    };\n    Draggable.prototype._drag = function (e) {\n        var draggingTarget = this._draggingTarget;\n        if (draggingTarget) {\n            var x = e.offsetX;\n            var y = e.offsetY;\n            var dx = x - this._x;\n            var dy = y - this._y;\n            this._x = x;\n            this._y = y;\n            draggingTarget.drift(dx, dy, e);\n            this.handler.dispatchToElement(new Param(draggingTarget, e), 'drag', e.event);\n            var dropTarget = this.handler.findHover(x, y, draggingTarget).target;\n            var lastDropTarget = this._dropTarget;\n            this._dropTarget = dropTarget;\n            if (draggingTarget !== dropTarget) {\n                if (lastDropTarget && dropTarget !== lastDropTarget) {\n                    this.handler.dispatchToElement(new Param(lastDropTarget, e), 'dragleave', e.event);\n                }\n                if (dropTarget && dropTarget !== lastDropTarget) {\n                    this.handler.dispatchToElement(new Param(dropTarget, e), 'dragenter', e.event);\n                }\n            }\n        }\n    };\n    Draggable.prototype._dragEnd = function (e) {\n        var draggingTarget = this._draggingTarget;\n        if (draggingTarget) {\n            draggingTarget.dragging = false;\n        }\n        this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragend', e.event);\n        if (this._dropTarget) {\n            this.handler.dispatchToElement(new Param(this._dropTarget, e), 'drop', e.event);\n        }\n        this._draggingTarget = null;\n        this._dropTarget = null;\n    };\n    return Draggable;\n}());\n\nvar Eventful = (function () {\n    function Eventful(eventProcessors) {\n        if (eventProcessors) {\n            this._$eventProcessor = eventProcessors;\n        }\n    }\n    Eventful.prototype.on = function (event, query, handler, context) {\n        if (!this._$handlers) {\n            this._$handlers = {};\n        }\n        var _h = this._$handlers;\n        if (typeof query === 'function') {\n            context = handler;\n            handler = query;\n            query = null;\n        }\n        if (!handler || !event) {\n            return this;\n        }\n        var eventProcessor = this._$eventProcessor;\n        if (query != null && eventProcessor && eventProcessor.normalizeQuery) {\n            query = eventProcessor.normalizeQuery(query);\n        }\n        if (!_h[event]) {\n            _h[event] = [];\n        }\n        for (var i = 0; i < _h[event].length; i++) {\n            if (_h[event][i].h === handler) {\n                return this;\n            }\n        }\n        var wrap = {\n            h: handler,\n            query: query,\n            ctx: (context || this),\n            callAtLast: handler.zrEventfulCallAtLast\n        };\n        var lastIndex = _h[event].length - 1;\n        var lastWrap = _h[event][lastIndex];\n        (lastWrap && lastWrap.callAtLast)\n            ? _h[event].splice(lastIndex, 0, wrap)\n            : _h[event].push(wrap);\n        return this;\n    };\n    Eventful.prototype.isSilent = function (eventName) {\n        var _h = this._$handlers;\n        return !_h || !_h[eventName] || !_h[eventName].length;\n    };\n    Eventful.prototype.off = function (eventType, handler) {\n        var _h = this._$handlers;\n        if (!_h) {\n            return this;\n        }\n        if (!eventType) {\n            this._$handlers = {};\n            return this;\n        }\n        if (handler) {\n            if (_h[eventType]) {\n                var newList = [];\n                for (var i = 0, l = _h[eventType].length; i < l; i++) {\n                    if (_h[eventType][i].h !== handler) {\n                        newList.push(_h[eventType][i]);\n                    }\n                }\n                _h[eventType] = newList;\n            }\n            if (_h[eventType] && _h[eventType].length === 0) {\n                delete _h[eventType];\n            }\n        }\n        else {\n            delete _h[eventType];\n        }\n        return this;\n    };\n    Eventful.prototype.trigger = function (eventType) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        if (!this._$handlers) {\n            return this;\n        }\n        var _h = this._$handlers[eventType];\n        var eventProcessor = this._$eventProcessor;\n        if (_h) {\n            var argLen = args.length;\n            var len = _h.length;\n            for (var i = 0; i < len; i++) {\n                var hItem = _h[i];\n                if (eventProcessor\n                    && eventProcessor.filter\n                    && hItem.query != null\n                    && !eventProcessor.filter(eventType, hItem.query)) {\n                    continue;\n                }\n                switch (argLen) {\n                    case 0:\n                        hItem.h.call(hItem.ctx);\n                        break;\n                    case 1:\n                        hItem.h.call(hItem.ctx, args[0]);\n                        break;\n                    case 2:\n                        hItem.h.call(hItem.ctx, args[0], args[1]);\n                        break;\n                    default:\n                        hItem.h.apply(hItem.ctx, args);\n                        break;\n                }\n            }\n        }\n        eventProcessor && eventProcessor.afterTrigger\n            && eventProcessor.afterTrigger(eventType);\n        return this;\n    };\n    Eventful.prototype.triggerWithContext = function (type) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        if (!this._$handlers) {\n            return this;\n        }\n        var _h = this._$handlers[type];\n        var eventProcessor = this._$eventProcessor;\n        if (_h) {\n            var argLen = args.length;\n            var ctx = args[argLen - 1];\n            var len = _h.length;\n            for (var i = 0; i < len; i++) {\n                var hItem = _h[i];\n                if (eventProcessor\n                    && eventProcessor.filter\n                    && hItem.query != null\n                    && !eventProcessor.filter(type, hItem.query)) {\n                    continue;\n                }\n                switch (argLen) {\n                    case 0:\n                        hItem.h.call(ctx);\n                        break;\n                    case 1:\n                        hItem.h.call(ctx, args[0]);\n                        break;\n                    case 2:\n                        hItem.h.call(ctx, args[0], args[1]);\n                        break;\n                    default:\n                        hItem.h.apply(ctx, args.slice(1, argLen - 1));\n                        break;\n                }\n            }\n        }\n        eventProcessor && eventProcessor.afterTrigger\n            && eventProcessor.afterTrigger(type);\n        return this;\n    };\n    return Eventful;\n}());\n\nvar LN2 = Math.log(2);\nfunction determinant(rows, rank, rowStart, rowMask, colMask, detCache) {\n    var cacheKey = rowMask + '-' + colMask;\n    var fullRank = rows.length;\n    if (detCache.hasOwnProperty(cacheKey)) {\n        return detCache[cacheKey];\n    }\n    if (rank === 1) {\n        var colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2);\n        return rows[rowStart][colStart];\n    }\n    var subRowMask = rowMask | (1 << rowStart);\n    var subRowStart = rowStart + 1;\n    while (rowMask & (1 << subRowStart)) {\n        subRowStart++;\n    }\n    var sum = 0;\n    for (var j = 0, colLocalIdx = 0; j < fullRank; j++) {\n        var colTag = 1 << j;\n        if (!(colTag & colMask)) {\n            sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j]\n                * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache);\n            colLocalIdx++;\n        }\n    }\n    detCache[cacheKey] = sum;\n    return sum;\n}\nfunction buildTransformer(src, dest) {\n    var mA = [\n        [src[0], src[1], 1, 0, 0, 0, -dest[0] * src[0], -dest[0] * src[1]],\n        [0, 0, 0, src[0], src[1], 1, -dest[1] * src[0], -dest[1] * src[1]],\n        [src[2], src[3], 1, 0, 0, 0, -dest[2] * src[2], -dest[2] * src[3]],\n        [0, 0, 0, src[2], src[3], 1, -dest[3] * src[2], -dest[3] * src[3]],\n        [src[4], src[5], 1, 0, 0, 0, -dest[4] * src[4], -dest[4] * src[5]],\n        [0, 0, 0, src[4], src[5], 1, -dest[5] * src[4], -dest[5] * src[5]],\n        [src[6], src[7], 1, 0, 0, 0, -dest[6] * src[6], -dest[6] * src[7]],\n        [0, 0, 0, src[6], src[7], 1, -dest[7] * src[6], -dest[7] * src[7]]\n    ];\n    var detCache = {};\n    var det = determinant(mA, 8, 0, 0, 0, detCache);\n    if (det === 0) {\n        return;\n    }\n    var vh = [];\n    for (var i = 0; i < 8; i++) {\n        for (var j = 0; j < 8; j++) {\n            vh[j] == null && (vh[j] = 0);\n            vh[j] += ((i + j) % 2 ? -1 : 1)\n                * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache)\n                / det * dest[i];\n        }\n    }\n    return function (out, srcPointX, srcPointY) {\n        var pk = srcPointX * vh[6] + srcPointY * vh[7] + 1;\n        out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk;\n        out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk;\n    };\n}\n\nvar EVENT_SAVED_PROP = '___zrEVENTSAVED';\nvar _calcOut = [];\nfunction transformLocalCoord(out, elFrom, elTarget, inX, inY) {\n    return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true)\n        && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]);\n}\nfunction transformCoordWithViewport(out, el, inX, inY, inverse) {\n    if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) {\n        var saved = el[EVENT_SAVED_PROP] || (el[EVENT_SAVED_PROP] = {});\n        var markers = prepareCoordMarkers(el, saved);\n        var transformer = preparePointerTransformer(markers, saved, inverse);\n        if (transformer) {\n            transformer(out, inX, inY);\n            return true;\n        }\n    }\n    return false;\n}\nfunction prepareCoordMarkers(el, saved) {\n    var markers = saved.markers;\n    if (markers) {\n        return markers;\n    }\n    markers = saved.markers = [];\n    var propLR = ['left', 'right'];\n    var propTB = ['top', 'bottom'];\n    for (var i = 0; i < 4; i++) {\n        var marker = document.createElement('div');\n        var stl = marker.style;\n        var idxLR = i % 2;\n        var idxTB = (i >> 1) % 2;\n        stl.cssText = [\n            'position: absolute',\n            'visibility: hidden',\n            'padding: 0',\n            'margin: 0',\n            'border-width: 0',\n            'user-select: none',\n            'width:0',\n            'height:0',\n            propLR[idxLR] + ':0',\n            propTB[idxTB] + ':0',\n            propLR[1 - idxLR] + ':auto',\n            propTB[1 - idxTB] + ':auto',\n            ''\n        ].join('!important;');\n        el.appendChild(marker);\n        markers.push(marker);\n    }\n    return markers;\n}\nfunction preparePointerTransformer(markers, saved, inverse) {\n    var transformerName = inverse ? 'invTrans' : 'trans';\n    var transformer = saved[transformerName];\n    var oldSrcCoords = saved.srcCoords;\n    var srcCoords = [];\n    var destCoords = [];\n    var oldCoordTheSame = true;\n    for (var i = 0; i < 4; i++) {\n        var rect = markers[i].getBoundingClientRect();\n        var ii = 2 * i;\n        var x = rect.left;\n        var y = rect.top;\n        srcCoords.push(x, y);\n        oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1];\n        destCoords.push(markers[i].offsetLeft, markers[i].offsetTop);\n    }\n    return (oldCoordTheSame && transformer)\n        ? transformer\n        : (saved.srcCoords = srcCoords,\n            saved[transformerName] = inverse\n                ? buildTransformer(destCoords, srcCoords)\n                : buildTransformer(srcCoords, destCoords));\n}\nfunction isCanvasEl(el) {\n    return el.nodeName.toUpperCase() === 'CANVAS';\n}\nvar replaceReg = /([&<>\"'])/g;\nvar replaceMap = {\n    '&': '&amp;',\n    '<': '&lt;',\n    '>': '&gt;',\n    '\"': '&quot;',\n    '\\'': '&#39;'\n};\nfunction encodeHTML(source) {\n    return source == null\n        ? ''\n        : (source + '').replace(replaceReg, function (str, c) {\n            return replaceMap[c];\n        });\n}\n\nvar MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;\nvar _calcOut$1 = [];\nvar firefoxNotSupportOffsetXY = env.browser.firefox\n    && +env.browser.version.split('.')[0] < 39;\nfunction clientToLocal(el, e, out, calculate) {\n    out = out || {};\n    if (calculate) {\n        calculateZrXY(el, e, out);\n    }\n    else if (firefoxNotSupportOffsetXY\n        && e.layerX != null\n        && e.layerX !== e.offsetX) {\n        out.zrX = e.layerX;\n        out.zrY = e.layerY;\n    }\n    else if (e.offsetX != null) {\n        out.zrX = e.offsetX;\n        out.zrY = e.offsetY;\n    }\n    else {\n        calculateZrXY(el, e, out);\n    }\n    return out;\n}\nfunction calculateZrXY(el, e, out) {\n    if (env.domSupported && el.getBoundingClientRect) {\n        var ex = e.clientX;\n        var ey = e.clientY;\n        if (isCanvasEl(el)) {\n            var box = el.getBoundingClientRect();\n            out.zrX = ex - box.left;\n            out.zrY = ey - box.top;\n            return;\n        }\n        else {\n            if (transformCoordWithViewport(_calcOut$1, el, ex, ey)) {\n                out.zrX = _calcOut$1[0];\n                out.zrY = _calcOut$1[1];\n                return;\n            }\n        }\n    }\n    out.zrX = out.zrY = 0;\n}\nfunction getNativeEvent(e) {\n    return e\n        || window.event;\n}\nfunction normalizeEvent(el, e, calculate) {\n    e = getNativeEvent(e);\n    if (e.zrX != null) {\n        return e;\n    }\n    var eventType = e.type;\n    var isTouch = eventType && eventType.indexOf('touch') >= 0;\n    if (!isTouch) {\n        clientToLocal(el, e, e, calculate);\n        var wheelDelta = getWheelDeltaMayPolyfill(e);\n        e.zrDelta = wheelDelta ? wheelDelta / 120 : -(e.detail || 0) / 3;\n    }\n    else {\n        var touch = eventType !== 'touchend'\n            ? e.targetTouches[0]\n            : e.changedTouches[0];\n        touch && clientToLocal(el, touch, e, calculate);\n    }\n    var button = e.button;\n    if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {\n        e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n    }\n    return e;\n}\nfunction getWheelDeltaMayPolyfill(e) {\n    var rawWheelDelta = e.wheelDelta;\n    if (rawWheelDelta) {\n        return rawWheelDelta;\n    }\n    var deltaX = e.deltaX;\n    var deltaY = e.deltaY;\n    if (deltaX == null || deltaY == null) {\n        return rawWheelDelta;\n    }\n    var delta = deltaY !== 0 ? Math.abs(deltaY) : Math.abs(deltaX);\n    var sign = deltaY > 0 ? -1\n        : deltaY < 0 ? 1\n            : deltaX > 0 ? -1\n                : 1;\n    return 3 * delta * sign;\n}\nfunction addEventListener(el, name, handler, opt) {\n    el.addEventListener(name, handler, opt);\n}\nfunction removeEventListener(el, name, handler, opt) {\n    el.removeEventListener(name, handler, opt);\n}\nvar stop = function (e) {\n    e.preventDefault();\n    e.stopPropagation();\n    e.cancelBubble = true;\n};\nfunction isMiddleOrRightButtonOnMouseUpDown(e) {\n    return e.which === 2 || e.which === 3;\n}\n\nvar GestureMgr = (function () {\n    function GestureMgr() {\n        this._track = [];\n    }\n    GestureMgr.prototype.recognize = function (event, target, root) {\n        this._doTrack(event, target, root);\n        return this._recognize(event);\n    };\n    GestureMgr.prototype.clear = function () {\n        this._track.length = 0;\n        return this;\n    };\n    GestureMgr.prototype._doTrack = function (event, target, root) {\n        var touches = event.touches;\n        if (!touches) {\n            return;\n        }\n        var trackItem = {\n            points: [],\n            touches: [],\n            target: target,\n            event: event\n        };\n        for (var i = 0, len = touches.length; i < len; i++) {\n            var touch = touches[i];\n            var pos = clientToLocal(root, touch, {});\n            trackItem.points.push([pos.zrX, pos.zrY]);\n            trackItem.touches.push(touch);\n        }\n        this._track.push(trackItem);\n    };\n    GestureMgr.prototype._recognize = function (event) {\n        for (var eventName in recognizers) {\n            if (recognizers.hasOwnProperty(eventName)) {\n                var gestureInfo = recognizers[eventName](this._track, event);\n                if (gestureInfo) {\n                    return gestureInfo;\n                }\n            }\n        }\n    };\n    return GestureMgr;\n}());\nfunction dist$1(pointPair) {\n    var dx = pointPair[1][0] - pointPair[0][0];\n    var dy = pointPair[1][1] - pointPair[0][1];\n    return Math.sqrt(dx * dx + dy * dy);\n}\nfunction center(pointPair) {\n    return [\n        (pointPair[0][0] + pointPair[1][0]) / 2,\n        (pointPair[0][1] + pointPair[1][1]) / 2\n    ];\n}\nvar recognizers = {\n    pinch: function (tracks, event) {\n        var trackLen = tracks.length;\n        if (!trackLen) {\n            return;\n        }\n        var pinchEnd = (tracks[trackLen - 1] || {}).points;\n        var pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd;\n        if (pinchPre\n            && pinchPre.length > 1\n            && pinchEnd\n            && pinchEnd.length > 1) {\n            var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);\n            !isFinite(pinchScale) && (pinchScale = 1);\n            event.pinchScale = pinchScale;\n            var pinchCenter = center(pinchEnd);\n            event.pinchX = pinchCenter[0];\n            event.pinchY = pinchCenter[1];\n            return {\n                type: 'pinch',\n                target: tracks[0].target,\n                event: event\n            };\n        }\n    }\n};\n\nfunction create$1() {\n    return [1, 0, 0, 1, 0, 0];\n}\nfunction identity(out) {\n    out[0] = 1;\n    out[1] = 0;\n    out[2] = 0;\n    out[3] = 1;\n    out[4] = 0;\n    out[5] = 0;\n    return out;\n}\nfunction copy$1(out, m) {\n    out[0] = m[0];\n    out[1] = m[1];\n    out[2] = m[2];\n    out[3] = m[3];\n    out[4] = m[4];\n    out[5] = m[5];\n    return out;\n}\nfunction mul$1(out, m1, m2) {\n    var out0 = m1[0] * m2[0] + m1[2] * m2[1];\n    var out1 = m1[1] * m2[0] + m1[3] * m2[1];\n    var out2 = m1[0] * m2[2] + m1[2] * m2[3];\n    var out3 = m1[1] * m2[2] + m1[3] * m2[3];\n    var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];\n    var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];\n    out[0] = out0;\n    out[1] = out1;\n    out[2] = out2;\n    out[3] = out3;\n    out[4] = out4;\n    out[5] = out5;\n    return out;\n}\nfunction translate(out, a, v) {\n    out[0] = a[0];\n    out[1] = a[1];\n    out[2] = a[2];\n    out[3] = a[3];\n    out[4] = a[4] + v[0];\n    out[5] = a[5] + v[1];\n    return out;\n}\nfunction rotate(out, a, rad) {\n    var aa = a[0];\n    var ac = a[2];\n    var atx = a[4];\n    var ab = a[1];\n    var ad = a[3];\n    var aty = a[5];\n    var st = Math.sin(rad);\n    var ct = Math.cos(rad);\n    out[0] = aa * ct + ab * st;\n    out[1] = -aa * st + ab * ct;\n    out[2] = ac * ct + ad * st;\n    out[3] = -ac * st + ct * ad;\n    out[4] = ct * atx + st * aty;\n    out[5] = ct * aty - st * atx;\n    return out;\n}\nfunction scale$1(out, a, v) {\n    var vx = v[0];\n    var vy = v[1];\n    out[0] = a[0] * vx;\n    out[1] = a[1] * vy;\n    out[2] = a[2] * vx;\n    out[3] = a[3] * vy;\n    out[4] = a[4] * vx;\n    out[5] = a[5] * vy;\n    return out;\n}\nfunction invert(out, a) {\n    var aa = a[0];\n    var ac = a[2];\n    var atx = a[4];\n    var ab = a[1];\n    var ad = a[3];\n    var aty = a[5];\n    var det = aa * ad - ab * ac;\n    if (!det) {\n        return null;\n    }\n    det = 1.0 / det;\n    out[0] = ad * det;\n    out[1] = -ab * det;\n    out[2] = -ac * det;\n    out[3] = aa * det;\n    out[4] = (ac * aty - ad * atx) * det;\n    out[5] = (ab * atx - aa * aty) * det;\n    return out;\n}\nfunction clone$2(a) {\n    var b = create$1();\n    copy$1(b, a);\n    return b;\n}\n\nvar matrix = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    create: create$1,\n    identity: identity,\n    copy: copy$1,\n    mul: mul$1,\n    translate: translate,\n    rotate: rotate,\n    scale: scale$1,\n    invert: invert,\n    clone: clone$2\n});\n\nvar Point = (function () {\n    function Point(x, y) {\n        this.x = x || 0;\n        this.y = y || 0;\n    }\n    Point.prototype.copy = function (other) {\n        this.x = other.x;\n        this.y = other.y;\n        return this;\n    };\n    Point.prototype.clone = function () {\n        return new Point(this.x, this.y);\n    };\n    Point.prototype.set = function (x, y) {\n        this.x = x;\n        this.y = y;\n        return this;\n    };\n    Point.prototype.equal = function (other) {\n        return other.x === this.x && other.y === this.y;\n    };\n    Point.prototype.add = function (other) {\n        this.x += other.x;\n        this.y += other.y;\n        return this;\n    };\n    Point.prototype.scale = function (scalar) {\n        this.x *= scalar;\n        this.y *= scalar;\n    };\n    Point.prototype.scaleAndAdd = function (other, scalar) {\n        this.x += other.x * scalar;\n        this.y += other.y * scalar;\n    };\n    Point.prototype.sub = function (other) {\n        this.x -= other.x;\n        this.y -= other.y;\n        return this;\n    };\n    Point.prototype.dot = function (other) {\n        return this.x * other.x + this.y * other.y;\n    };\n    Point.prototype.len = function () {\n        return Math.sqrt(this.x * this.x + this.y * this.y);\n    };\n    Point.prototype.lenSquare = function () {\n        return this.x * this.x + this.y * this.y;\n    };\n    Point.prototype.normalize = function () {\n        var len = this.len();\n        this.x /= len;\n        this.y /= len;\n        return this;\n    };\n    Point.prototype.distance = function (other) {\n        var dx = this.x - other.x;\n        var dy = this.y - other.y;\n        return Math.sqrt(dx * dx + dy * dy);\n    };\n    Point.prototype.distanceSquare = function (other) {\n        var dx = this.x - other.x;\n        var dy = this.y - other.y;\n        return dx * dx + dy * dy;\n    };\n    Point.prototype.negate = function () {\n        this.x = -this.x;\n        this.y = -this.y;\n        return this;\n    };\n    Point.prototype.transform = function (m) {\n        if (!m) {\n            return;\n        }\n        var x = this.x;\n        var y = this.y;\n        this.x = m[0] * x + m[2] * y + m[4];\n        this.y = m[1] * x + m[3] * y + m[5];\n        return this;\n    };\n    Point.prototype.toArray = function (out) {\n        out[0] = this.x;\n        out[1] = this.y;\n        return out;\n    };\n    Point.prototype.fromArray = function (input) {\n        this.x = input[0];\n        this.y = input[1];\n    };\n    Point.set = function (p, x, y) {\n        p.x = x;\n        p.y = y;\n    };\n    Point.copy = function (p, p2) {\n        p.x = p2.x;\n        p.y = p2.y;\n    };\n    Point.len = function (p) {\n        return Math.sqrt(p.x * p.x + p.y * p.y);\n    };\n    Point.lenSquare = function (p) {\n        return p.x * p.x + p.y * p.y;\n    };\n    Point.dot = function (p0, p1) {\n        return p0.x * p1.x + p0.y * p1.y;\n    };\n    Point.add = function (out, p0, p1) {\n        out.x = p0.x + p1.x;\n        out.y = p0.y + p1.y;\n    };\n    Point.sub = function (out, p0, p1) {\n        out.x = p0.x - p1.x;\n        out.y = p0.y - p1.y;\n    };\n    Point.scale = function (out, p0, scalar) {\n        out.x = p0.x * scalar;\n        out.y = p0.y * scalar;\n    };\n    Point.scaleAndAdd = function (out, p0, p1, scalar) {\n        out.x = p0.x + p1.x * scalar;\n        out.y = p0.y + p1.y * scalar;\n    };\n    Point.lerp = function (out, p0, p1, t) {\n        var onet = 1 - t;\n        out.x = onet * p0.x + t * p1.x;\n        out.y = onet * p0.y + t * p1.y;\n    };\n    return Point;\n}());\n\nvar mathMin = Math.min;\nvar mathMax = Math.max;\nvar lt = new Point();\nvar rb = new Point();\nvar lb = new Point();\nvar rt = new Point();\nvar minTv = new Point();\nvar maxTv = new Point();\nvar BoundingRect = (function () {\n    function BoundingRect(x, y, width, height) {\n        if (width < 0) {\n            x = x + width;\n            width = -width;\n        }\n        if (height < 0) {\n            y = y + height;\n            height = -height;\n        }\n        this.x = x;\n        this.y = y;\n        this.width = width;\n        this.height = height;\n    }\n    BoundingRect.prototype.union = function (other) {\n        var x = mathMin(other.x, this.x);\n        var y = mathMin(other.y, this.y);\n        if (isFinite(this.x) && isFinite(this.width)) {\n            this.width = mathMax(other.x + other.width, this.x + this.width) - x;\n        }\n        else {\n            this.width = other.width;\n        }\n        if (isFinite(this.y) && isFinite(this.height)) {\n            this.height = mathMax(other.y + other.height, this.y + this.height) - y;\n        }\n        else {\n            this.height = other.height;\n        }\n        this.x = x;\n        this.y = y;\n    };\n    BoundingRect.prototype.applyTransform = function (m) {\n        BoundingRect.applyTransform(this, this, m);\n    };\n    BoundingRect.prototype.calculateTransform = function (b) {\n        var a = this;\n        var sx = b.width / a.width;\n        var sy = b.height / a.height;\n        var m = create$1();\n        translate(m, m, [-a.x, -a.y]);\n        scale$1(m, m, [sx, sy]);\n        translate(m, m, [b.x, b.y]);\n        return m;\n    };\n    BoundingRect.prototype.intersect = function (b, mtv) {\n        if (!b) {\n            return false;\n        }\n        if (!(b instanceof BoundingRect)) {\n            b = BoundingRect.create(b);\n        }\n        var a = this;\n        var ax0 = a.x;\n        var ax1 = a.x + a.width;\n        var ay0 = a.y;\n        var ay1 = a.y + a.height;\n        var bx0 = b.x;\n        var bx1 = b.x + b.width;\n        var by0 = b.y;\n        var by1 = b.y + b.height;\n        var overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);\n        if (mtv) {\n            var dMin = Infinity;\n            var dMax = 0;\n            var d0 = Math.abs(ax1 - bx0);\n            var d1 = Math.abs(bx1 - ax0);\n            var d2 = Math.abs(ay1 - by0);\n            var d3 = Math.abs(by1 - ay0);\n            var dx = Math.min(d0, d1);\n            var dy = Math.min(d2, d3);\n            if (ax1 < bx0 || bx1 < ax0) {\n                if (dx > dMax) {\n                    dMax = dx;\n                    if (d0 < d1) {\n                        Point.set(maxTv, -d0, 0);\n                    }\n                    else {\n                        Point.set(maxTv, d1, 0);\n                    }\n                }\n            }\n            else {\n                if (dx < dMin) {\n                    dMin = dx;\n                    if (d0 < d1) {\n                        Point.set(minTv, d0, 0);\n                    }\n                    else {\n                        Point.set(minTv, -d1, 0);\n                    }\n                }\n            }\n            if (ay1 < by0 || by1 < ay0) {\n                if (dy > dMax) {\n                    dMax = dy;\n                    if (d2 < d3) {\n                        Point.set(maxTv, 0, -d2);\n                    }\n                    else {\n                        Point.set(maxTv, 0, d3);\n                    }\n                }\n            }\n            else {\n                if (dx < dMin) {\n                    dMin = dx;\n                    if (d2 < d3) {\n                        Point.set(minTv, 0, d2);\n                    }\n                    else {\n                        Point.set(minTv, 0, -d3);\n                    }\n                }\n            }\n        }\n        if (mtv) {\n            Point.copy(mtv, overlap ? minTv : maxTv);\n        }\n        return overlap;\n    };\n    BoundingRect.prototype.contain = function (x, y) {\n        var rect = this;\n        return x >= rect.x\n            && x <= (rect.x + rect.width)\n            && y >= rect.y\n            && y <= (rect.y + rect.height);\n    };\n    BoundingRect.prototype.clone = function () {\n        return new BoundingRect(this.x, this.y, this.width, this.height);\n    };\n    BoundingRect.prototype.copy = function (other) {\n        BoundingRect.copy(this, other);\n    };\n    BoundingRect.prototype.plain = function () {\n        return {\n            x: this.x,\n            y: this.y,\n            width: this.width,\n            height: this.height\n        };\n    };\n    BoundingRect.prototype.isFinite = function () {\n        return isFinite(this.x)\n            && isFinite(this.y)\n            && isFinite(this.width)\n            && isFinite(this.height);\n    };\n    BoundingRect.prototype.isZero = function () {\n        return this.width === 0 || this.height === 0;\n    };\n    BoundingRect.create = function (rect) {\n        return new BoundingRect(rect.x, rect.y, rect.width, rect.height);\n    };\n    BoundingRect.copy = function (target, source) {\n        target.x = source.x;\n        target.y = source.y;\n        target.width = source.width;\n        target.height = source.height;\n    };\n    BoundingRect.applyTransform = function (target, source, m) {\n        if (!m) {\n            if (target !== source) {\n                BoundingRect.copy(target, source);\n            }\n            return;\n        }\n        if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) {\n            var sx = m[0];\n            var sy = m[3];\n            var tx = m[4];\n            var ty = m[5];\n            target.x = source.x * sx + tx;\n            target.y = source.y * sy + ty;\n            target.width = source.width * sx;\n            target.height = source.height * sy;\n            if (target.width < 0) {\n                target.x += target.width;\n                target.width = -target.width;\n            }\n            if (target.height < 0) {\n                target.y += target.height;\n                target.height = -target.height;\n            }\n            return;\n        }\n        lt.x = lb.x = source.x;\n        lt.y = rt.y = source.y;\n        rb.x = rt.x = source.x + source.width;\n        rb.y = lb.y = source.y + source.height;\n        lt.transform(m);\n        rt.transform(m);\n        rb.transform(m);\n        lb.transform(m);\n        target.x = mathMin(lt.x, rb.x, lb.x, rt.x);\n        target.y = mathMin(lt.y, rb.y, lb.y, rt.y);\n        var maxX = mathMax(lt.x, rb.x, lb.x, rt.x);\n        var maxY = mathMax(lt.y, rb.y, lb.y, rt.y);\n        target.width = maxX - target.x;\n        target.height = maxY - target.y;\n    };\n    return BoundingRect;\n}());\n\nvar SILENT = 'silent';\nfunction makeEventPacket(eveType, targetInfo, event) {\n    return {\n        type: eveType,\n        event: event,\n        target: targetInfo.target,\n        topTarget: targetInfo.topTarget,\n        cancelBubble: false,\n        offsetX: event.zrX,\n        offsetY: event.zrY,\n        gestureEvent: event.gestureEvent,\n        pinchX: event.pinchX,\n        pinchY: event.pinchY,\n        pinchScale: event.pinchScale,\n        wheelDelta: event.zrDelta,\n        zrByTouch: event.zrByTouch,\n        which: event.which,\n        stop: stopEvent\n    };\n}\nfunction stopEvent() {\n    stop(this.event);\n}\nvar EmptyProxy = (function (_super) {\n    __extends(EmptyProxy, _super);\n    function EmptyProxy() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n        _this.handler = null;\n        return _this;\n    }\n    EmptyProxy.prototype.dispose = function () { };\n    EmptyProxy.prototype.setCursor = function () { };\n    return EmptyProxy;\n}(Eventful));\nvar HoveredResult = (function () {\n    function HoveredResult(x, y) {\n        this.x = x;\n        this.y = y;\n    }\n    return HoveredResult;\n}());\nvar handlerNames = [\n    'click', 'dblclick', 'mousewheel', 'mouseout',\n    'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n];\nvar tmpRect = new BoundingRect(0, 0, 0, 0);\nvar Handler = (function (_super) {\n    __extends(Handler, _super);\n    function Handler(storage, painter, proxy, painterRoot, pointerSize) {\n        var _this = _super.call(this) || this;\n        _this._hovered = new HoveredResult(0, 0);\n        _this.storage = storage;\n        _this.painter = painter;\n        _this.painterRoot = painterRoot;\n        _this._pointerSize = pointerSize;\n        proxy = proxy || new EmptyProxy();\n        _this.proxy = null;\n        _this.setHandlerProxy(proxy);\n        _this._draggingMgr = new Draggable(_this);\n        return _this;\n    }\n    Handler.prototype.setHandlerProxy = function (proxy) {\n        if (this.proxy) {\n            this.proxy.dispose();\n        }\n        if (proxy) {\n            each(handlerNames, function (name) {\n                proxy.on && proxy.on(name, this[name], this);\n            }, this);\n            proxy.handler = this;\n        }\n        this.proxy = proxy;\n    };\n    Handler.prototype.mousemove = function (event) {\n        var x = event.zrX;\n        var y = event.zrY;\n        var isOutside = isOutsideBoundary(this, x, y);\n        var lastHovered = this._hovered;\n        var lastHoveredTarget = lastHovered.target;\n        if (lastHoveredTarget && !lastHoveredTarget.__zr) {\n            lastHovered = this.findHover(lastHovered.x, lastHovered.y);\n            lastHoveredTarget = lastHovered.target;\n        }\n        var hovered = this._hovered = isOutside ? new HoveredResult(x, y) : this.findHover(x, y);\n        var hoveredTarget = hovered.target;\n        var proxy = this.proxy;\n        proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');\n        if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {\n            this.dispatchToElement(lastHovered, 'mouseout', event);\n        }\n        this.dispatchToElement(hovered, 'mousemove', event);\n        if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {\n            this.dispatchToElement(hovered, 'mouseover', event);\n        }\n    };\n    Handler.prototype.mouseout = function (event) {\n        var eventControl = event.zrEventControl;\n        if (eventControl !== 'only_globalout') {\n            this.dispatchToElement(this._hovered, 'mouseout', event);\n        }\n        if (eventControl !== 'no_globalout') {\n            this.trigger('globalout', { type: 'globalout', event: event });\n        }\n    };\n    Handler.prototype.resize = function () {\n        this._hovered = new HoveredResult(0, 0);\n    };\n    Handler.prototype.dispatch = function (eventName, eventArgs) {\n        var handler = this[eventName];\n        handler && handler.call(this, eventArgs);\n    };\n    Handler.prototype.dispose = function () {\n        this.proxy.dispose();\n        this.storage = null;\n        this.proxy = null;\n        this.painter = null;\n    };\n    Handler.prototype.setCursorStyle = function (cursorStyle) {\n        var proxy = this.proxy;\n        proxy.setCursor && proxy.setCursor(cursorStyle);\n    };\n    Handler.prototype.dispatchToElement = function (targetInfo, eventName, event) {\n        targetInfo = targetInfo || {};\n        var el = targetInfo.target;\n        if (el && el.silent) {\n            return;\n        }\n        var eventKey = ('on' + eventName);\n        var eventPacket = makeEventPacket(eventName, targetInfo, event);\n        while (el) {\n            el[eventKey]\n                && (eventPacket.cancelBubble = !!el[eventKey].call(el, eventPacket));\n            el.trigger(eventName, eventPacket);\n            el = el.__hostTarget ? el.__hostTarget : el.parent;\n            if (eventPacket.cancelBubble) {\n                break;\n            }\n        }\n        if (!eventPacket.cancelBubble) {\n            this.trigger(eventName, eventPacket);\n            if (this.painter && this.painter.eachOtherLayer) {\n                this.painter.eachOtherLayer(function (layer) {\n                    if (typeof (layer[eventKey]) === 'function') {\n                        layer[eventKey].call(layer, eventPacket);\n                    }\n                    if (layer.trigger) {\n                        layer.trigger(eventName, eventPacket);\n                    }\n                });\n            }\n        }\n    };\n    Handler.prototype.findHover = function (x, y, exclude) {\n        var list = this.storage.getDisplayList();\n        var out = new HoveredResult(x, y);\n        setHoverTarget(list, out, x, y, exclude);\n        if (this._pointerSize && !out.target) {\n            var candidates = [];\n            var pointerSize = this._pointerSize;\n            var targetSizeHalf = pointerSize / 2;\n            var pointerRect = new BoundingRect(x - targetSizeHalf, y - targetSizeHalf, pointerSize, pointerSize);\n            for (var i = list.length - 1; i >= 0; i--) {\n                var el = list[i];\n                if (el !== exclude\n                    && !el.ignore\n                    && !el.ignoreCoarsePointer\n                    && (!el.parent || !el.parent.ignoreCoarsePointer)) {\n                    tmpRect.copy(el.getBoundingRect());\n                    if (el.transform) {\n                        tmpRect.applyTransform(el.transform);\n                    }\n                    if (tmpRect.intersect(pointerRect)) {\n                        candidates.push(el);\n                    }\n                }\n            }\n            if (candidates.length) {\n                var rStep = 4;\n                var thetaStep = Math.PI / 12;\n                var PI2 = Math.PI * 2;\n                for (var r = 0; r < targetSizeHalf; r += rStep) {\n                    for (var theta = 0; theta < PI2; theta += thetaStep) {\n                        var x1 = x + r * Math.cos(theta);\n                        var y1 = y + r * Math.sin(theta);\n                        setHoverTarget(candidates, out, x1, y1, exclude);\n                        if (out.target) {\n                            return out;\n                        }\n                    }\n                }\n            }\n        }\n        return out;\n    };\n    Handler.prototype.processGesture = function (event, stage) {\n        if (!this._gestureMgr) {\n            this._gestureMgr = new GestureMgr();\n        }\n        var gestureMgr = this._gestureMgr;\n        stage === 'start' && gestureMgr.clear();\n        var gestureInfo = gestureMgr.recognize(event, this.findHover(event.zrX, event.zrY, null).target, this.proxy.dom);\n        stage === 'end' && gestureMgr.clear();\n        if (gestureInfo) {\n            var type = gestureInfo.type;\n            event.gestureEvent = type;\n            var res = new HoveredResult();\n            res.target = gestureInfo.target;\n            this.dispatchToElement(res, type, gestureInfo.event);\n        }\n    };\n    return Handler;\n}(Eventful));\neach(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {\n    Handler.prototype[name] = function (event) {\n        var x = event.zrX;\n        var y = event.zrY;\n        var isOutside = isOutsideBoundary(this, x, y);\n        var hovered;\n        var hoveredTarget;\n        if (name !== 'mouseup' || !isOutside) {\n            hovered = this.findHover(x, y);\n            hoveredTarget = hovered.target;\n        }\n        if (name === 'mousedown') {\n            this._downEl = hoveredTarget;\n            this._downPoint = [event.zrX, event.zrY];\n            this._upEl = hoveredTarget;\n        }\n        else if (name === 'mouseup') {\n            this._upEl = hoveredTarget;\n        }\n        else if (name === 'click') {\n            if (this._downEl !== this._upEl\n                || !this._downPoint\n                || dist(this._downPoint, [event.zrX, event.zrY]) > 4) {\n                return;\n            }\n            this._downPoint = null;\n        }\n        this.dispatchToElement(hovered, name, event);\n    };\n});\nfunction isHover(displayable, x, y) {\n    if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {\n        var el = displayable;\n        var isSilent = void 0;\n        var ignoreClip = false;\n        while (el) {\n            if (el.ignoreClip) {\n                ignoreClip = true;\n            }\n            if (!ignoreClip) {\n                var clipPath = el.getClipPath();\n                if (clipPath && !clipPath.contain(x, y)) {\n                    return false;\n                }\n                if (el.silent) {\n                    isSilent = true;\n                }\n            }\n            var hostEl = el.__hostTarget;\n            el = hostEl ? hostEl : el.parent;\n        }\n        return isSilent ? SILENT : true;\n    }\n    return false;\n}\nfunction setHoverTarget(list, out, x, y, exclude) {\n    for (var i = list.length - 1; i >= 0; i--) {\n        var el = list[i];\n        var hoverCheckResult = void 0;\n        if (el !== exclude\n            && !el.ignore\n            && (hoverCheckResult = isHover(el, x, y))) {\n            !out.topTarget && (out.topTarget = el);\n            if (hoverCheckResult !== SILENT) {\n                out.target = el;\n                break;\n            }\n        }\n    }\n}\nfunction isOutsideBoundary(handlerInstance, x, y) {\n    var painter = handlerInstance.painter;\n    return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight();\n}\n\nvar DEFAULT_MIN_MERGE = 32;\nvar DEFAULT_MIN_GALLOPING = 7;\nfunction minRunLength(n) {\n    var r = 0;\n    while (n >= DEFAULT_MIN_MERGE) {\n        r |= n & 1;\n        n >>= 1;\n    }\n    return n + r;\n}\nfunction makeAscendingRun(array, lo, hi, compare) {\n    var runHi = lo + 1;\n    if (runHi === hi) {\n        return 1;\n    }\n    if (compare(array[runHi++], array[lo]) < 0) {\n        while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {\n            runHi++;\n        }\n        reverseRun(array, lo, runHi);\n    }\n    else {\n        while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {\n            runHi++;\n        }\n    }\n    return runHi - lo;\n}\nfunction reverseRun(array, lo, hi) {\n    hi--;\n    while (lo < hi) {\n        var t = array[lo];\n        array[lo++] = array[hi];\n        array[hi--] = t;\n    }\n}\nfunction binaryInsertionSort(array, lo, hi, start, compare) {\n    if (start === lo) {\n        start++;\n    }\n    for (; start < hi; start++) {\n        var pivot = array[start];\n        var left = lo;\n        var right = start;\n        var mid;\n        while (left < right) {\n            mid = left + right >>> 1;\n            if (compare(pivot, array[mid]) < 0) {\n                right = mid;\n            }\n            else {\n                left = mid + 1;\n            }\n        }\n        var n = start - left;\n        switch (n) {\n            case 3:\n                array[left + 3] = array[left + 2];\n            case 2:\n                array[left + 2] = array[left + 1];\n            case 1:\n                array[left + 1] = array[left];\n                break;\n            default:\n                while (n > 0) {\n                    array[left + n] = array[left + n - 1];\n                    n--;\n                }\n        }\n        array[left] = pivot;\n    }\n}\nfunction gallopLeft(value, array, start, length, hint, compare) {\n    var lastOffset = 0;\n    var maxOffset = 0;\n    var offset = 1;\n    if (compare(value, array[start + hint]) > 0) {\n        maxOffset = length - hint;\n        while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {\n            lastOffset = offset;\n            offset = (offset << 1) + 1;\n            if (offset <= 0) {\n                offset = maxOffset;\n            }\n        }\n        if (offset > maxOffset) {\n            offset = maxOffset;\n        }\n        lastOffset += hint;\n        offset += hint;\n    }\n    else {\n        maxOffset = hint + 1;\n        while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {\n            lastOffset = offset;\n            offset = (offset << 1) + 1;\n            if (offset <= 0) {\n                offset = maxOffset;\n            }\n        }\n        if (offset > maxOffset) {\n            offset = maxOffset;\n        }\n        var tmp = lastOffset;\n        lastOffset = hint - offset;\n        offset = hint - tmp;\n    }\n    lastOffset++;\n    while (lastOffset < offset) {\n        var m = lastOffset + (offset - lastOffset >>> 1);\n        if (compare(value, array[start + m]) > 0) {\n            lastOffset = m + 1;\n        }\n        else {\n            offset = m;\n        }\n    }\n    return offset;\n}\nfunction gallopRight(value, array, start, length, hint, compare) {\n    var lastOffset = 0;\n    var maxOffset = 0;\n    var offset = 1;\n    if (compare(value, array[start + hint]) < 0) {\n        maxOffset = hint + 1;\n        while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {\n            lastOffset = offset;\n            offset = (offset << 1) + 1;\n            if (offset <= 0) {\n                offset = maxOffset;\n            }\n        }\n        if (offset > maxOffset) {\n            offset = maxOffset;\n        }\n        var tmp = lastOffset;\n        lastOffset = hint - offset;\n        offset = hint - tmp;\n    }\n    else {\n        maxOffset = length - hint;\n        while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {\n            lastOffset = offset;\n            offset = (offset << 1) + 1;\n            if (offset <= 0) {\n                offset = maxOffset;\n            }\n        }\n        if (offset > maxOffset) {\n            offset = maxOffset;\n        }\n        lastOffset += hint;\n        offset += hint;\n    }\n    lastOffset++;\n    while (lastOffset < offset) {\n        var m = lastOffset + (offset - lastOffset >>> 1);\n        if (compare(value, array[start + m]) < 0) {\n            offset = m;\n        }\n        else {\n            lastOffset = m + 1;\n        }\n    }\n    return offset;\n}\nfunction TimSort(array, compare) {\n    var minGallop = DEFAULT_MIN_GALLOPING;\n    var length = 0;\n    var runStart;\n    var runLength;\n    var stackSize = 0;\n    length = array.length;\n    var tmp = [];\n    runStart = [];\n    runLength = [];\n    function pushRun(_runStart, _runLength) {\n        runStart[stackSize] = _runStart;\n        runLength[stackSize] = _runLength;\n        stackSize += 1;\n    }\n    function mergeRuns() {\n        while (stackSize > 1) {\n            var n = stackSize - 2;\n            if ((n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1])\n                || (n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1])) {\n                if (runLength[n - 1] < runLength[n + 1]) {\n                    n--;\n                }\n            }\n            else if (runLength[n] > runLength[n + 1]) {\n                break;\n            }\n            mergeAt(n);\n        }\n    }\n    function forceMergeRuns() {\n        while (stackSize > 1) {\n            var n = stackSize - 2;\n            if (n > 0 && runLength[n - 1] < runLength[n + 1]) {\n                n--;\n            }\n            mergeAt(n);\n        }\n    }\n    function mergeAt(i) {\n        var start1 = runStart[i];\n        var length1 = runLength[i];\n        var start2 = runStart[i + 1];\n        var length2 = runLength[i + 1];\n        runLength[i] = length1 + length2;\n        if (i === stackSize - 3) {\n            runStart[i + 1] = runStart[i + 2];\n            runLength[i + 1] = runLength[i + 2];\n        }\n        stackSize--;\n        var k = gallopRight(array[start2], array, start1, length1, 0, compare);\n        start1 += k;\n        length1 -= k;\n        if (length1 === 0) {\n            return;\n        }\n        length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);\n        if (length2 === 0) {\n            return;\n        }\n        if (length1 <= length2) {\n            mergeLow(start1, length1, start2, length2);\n        }\n        else {\n            mergeHigh(start1, length1, start2, length2);\n        }\n    }\n    function mergeLow(start1, length1, start2, length2) {\n        var i = 0;\n        for (i = 0; i < length1; i++) {\n            tmp[i] = array[start1 + i];\n        }\n        var cursor1 = 0;\n        var cursor2 = start2;\n        var dest = start1;\n        array[dest++] = array[cursor2++];\n        if (--length2 === 0) {\n            for (i = 0; i < length1; i++) {\n                array[dest + i] = tmp[cursor1 + i];\n            }\n            return;\n        }\n        if (length1 === 1) {\n            for (i = 0; i < length2; i++) {\n                array[dest + i] = array[cursor2 + i];\n            }\n            array[dest + length2] = tmp[cursor1];\n            return;\n        }\n        var _minGallop = minGallop;\n        var count1;\n        var count2;\n        var exit;\n        while (1) {\n            count1 = 0;\n            count2 = 0;\n            exit = false;\n            do {\n                if (compare(array[cursor2], tmp[cursor1]) < 0) {\n                    array[dest++] = array[cursor2++];\n                    count2++;\n                    count1 = 0;\n                    if (--length2 === 0) {\n                        exit = true;\n                        break;\n                    }\n                }\n                else {\n                    array[dest++] = tmp[cursor1++];\n                    count1++;\n                    count2 = 0;\n                    if (--length1 === 1) {\n                        exit = true;\n                        break;\n                    }\n                }\n            } while ((count1 | count2) < _minGallop);\n            if (exit) {\n                break;\n            }\n            do {\n                count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);\n                if (count1 !== 0) {\n                    for (i = 0; i < count1; i++) {\n                        array[dest + i] = tmp[cursor1 + i];\n                    }\n                    dest += count1;\n                    cursor1 += count1;\n                    length1 -= count1;\n                    if (length1 <= 1) {\n                        exit = true;\n                        break;\n                    }\n                }\n                array[dest++] = array[cursor2++];\n                if (--length2 === 0) {\n                    exit = true;\n                    break;\n                }\n                count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);\n                if (count2 !== 0) {\n                    for (i = 0; i < count2; i++) {\n                        array[dest + i] = array[cursor2 + i];\n                    }\n                    dest += count2;\n                    cursor2 += count2;\n                    length2 -= count2;\n                    if (length2 === 0) {\n                        exit = true;\n                        break;\n                    }\n                }\n                array[dest++] = tmp[cursor1++];\n                if (--length1 === 1) {\n                    exit = true;\n                    break;\n                }\n                _minGallop--;\n            } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n            if (exit) {\n                break;\n            }\n            if (_minGallop < 0) {\n                _minGallop = 0;\n            }\n            _minGallop += 2;\n        }\n        minGallop = _minGallop;\n        minGallop < 1 && (minGallop = 1);\n        if (length1 === 1) {\n            for (i = 0; i < length2; i++) {\n                array[dest + i] = array[cursor2 + i];\n            }\n            array[dest + length2] = tmp[cursor1];\n        }\n        else if (length1 === 0) {\n            throw new Error();\n        }\n        else {\n            for (i = 0; i < length1; i++) {\n                array[dest + i] = tmp[cursor1 + i];\n            }\n        }\n    }\n    function mergeHigh(start1, length1, start2, length2) {\n        var i = 0;\n        for (i = 0; i < length2; i++) {\n            tmp[i] = array[start2 + i];\n        }\n        var cursor1 = start1 + length1 - 1;\n        var cursor2 = length2 - 1;\n        var dest = start2 + length2 - 1;\n        var customCursor = 0;\n        var customDest = 0;\n        array[dest--] = array[cursor1--];\n        if (--length1 === 0) {\n            customCursor = dest - (length2 - 1);\n            for (i = 0; i < length2; i++) {\n                array[customCursor + i] = tmp[i];\n            }\n            return;\n        }\n        if (length2 === 1) {\n            dest -= length1;\n            cursor1 -= length1;\n            customDest = dest + 1;\n            customCursor = cursor1 + 1;\n            for (i = length1 - 1; i >= 0; i--) {\n                array[customDest + i] = array[customCursor + i];\n            }\n            array[dest] = tmp[cursor2];\n            return;\n        }\n        var _minGallop = minGallop;\n        while (true) {\n            var count1 = 0;\n            var count2 = 0;\n            var exit = false;\n            do {\n                if (compare(tmp[cursor2], array[cursor1]) < 0) {\n                    array[dest--] = array[cursor1--];\n                    count1++;\n                    count2 = 0;\n                    if (--length1 === 0) {\n                        exit = true;\n                        break;\n                    }\n                }\n                else {\n                    array[dest--] = tmp[cursor2--];\n                    count2++;\n                    count1 = 0;\n                    if (--length2 === 1) {\n                        exit = true;\n                        break;\n                    }\n                }\n            } while ((count1 | count2) < _minGallop);\n            if (exit) {\n                break;\n            }\n            do {\n                count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);\n                if (count1 !== 0) {\n                    dest -= count1;\n                    cursor1 -= count1;\n                    length1 -= count1;\n                    customDest = dest + 1;\n                    customCursor = cursor1 + 1;\n                    for (i = count1 - 1; i >= 0; i--) {\n                        array[customDest + i] = array[customCursor + i];\n                    }\n                    if (length1 === 0) {\n                        exit = true;\n                        break;\n                    }\n                }\n                array[dest--] = tmp[cursor2--];\n                if (--length2 === 1) {\n                    exit = true;\n                    break;\n                }\n                count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);\n                if (count2 !== 0) {\n                    dest -= count2;\n                    cursor2 -= count2;\n                    length2 -= count2;\n                    customDest = dest + 1;\n                    customCursor = cursor2 + 1;\n                    for (i = 0; i < count2; i++) {\n                        array[customDest + i] = tmp[customCursor + i];\n                    }\n                    if (length2 <= 1) {\n                        exit = true;\n                        break;\n                    }\n                }\n                array[dest--] = array[cursor1--];\n                if (--length1 === 0) {\n                    exit = true;\n                    break;\n                }\n                _minGallop--;\n            } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n            if (exit) {\n                break;\n            }\n            if (_minGallop < 0) {\n                _minGallop = 0;\n            }\n            _minGallop += 2;\n        }\n        minGallop = _minGallop;\n        if (minGallop < 1) {\n            minGallop = 1;\n        }\n        if (length2 === 1) {\n            dest -= length1;\n            cursor1 -= length1;\n            customDest = dest + 1;\n            customCursor = cursor1 + 1;\n            for (i = length1 - 1; i >= 0; i--) {\n                array[customDest + i] = array[customCursor + i];\n            }\n            array[dest] = tmp[cursor2];\n        }\n        else if (length2 === 0) {\n            throw new Error();\n        }\n        else {\n            customCursor = dest - (length2 - 1);\n            for (i = 0; i < length2; i++) {\n                array[customCursor + i] = tmp[i];\n            }\n        }\n    }\n    return {\n        mergeRuns: mergeRuns,\n        forceMergeRuns: forceMergeRuns,\n        pushRun: pushRun\n    };\n}\nfunction sort(array, compare, lo, hi) {\n    if (!lo) {\n        lo = 0;\n    }\n    if (!hi) {\n        hi = array.length;\n    }\n    var remaining = hi - lo;\n    if (remaining < 2) {\n        return;\n    }\n    var runLength = 0;\n    if (remaining < DEFAULT_MIN_MERGE) {\n        runLength = makeAscendingRun(array, lo, hi, compare);\n        binaryInsertionSort(array, lo, hi, lo + runLength, compare);\n        return;\n    }\n    var ts = TimSort(array, compare);\n    var minRun = minRunLength(remaining);\n    do {\n        runLength = makeAscendingRun(array, lo, hi, compare);\n        if (runLength < minRun) {\n            var force = remaining;\n            if (force > minRun) {\n                force = minRun;\n            }\n            binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);\n            runLength = force;\n        }\n        ts.pushRun(lo, runLength);\n        ts.mergeRuns();\n        remaining -= runLength;\n        lo += runLength;\n    } while (remaining !== 0);\n    ts.forceMergeRuns();\n}\n\nvar REDRAW_BIT = 1;\nvar STYLE_CHANGED_BIT = 2;\nvar SHAPE_CHANGED_BIT = 4;\n\nvar invalidZErrorLogged = false;\nfunction logInvalidZError() {\n    if (invalidZErrorLogged) {\n        return;\n    }\n    invalidZErrorLogged = true;\n    console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors');\n}\nfunction shapeCompareFunc(a, b) {\n    if (a.zlevel === b.zlevel) {\n        if (a.z === b.z) {\n            return a.z2 - b.z2;\n        }\n        return a.z - b.z;\n    }\n    return a.zlevel - b.zlevel;\n}\nvar Storage = (function () {\n    function Storage() {\n        this._roots = [];\n        this._displayList = [];\n        this._displayListLen = 0;\n        this.displayableSortFunc = shapeCompareFunc;\n    }\n    Storage.prototype.traverse = function (cb, context) {\n        for (var i = 0; i < this._roots.length; i++) {\n            this._roots[i].traverse(cb, context);\n        }\n    };\n    Storage.prototype.getDisplayList = function (update, includeIgnore) {\n        includeIgnore = includeIgnore || false;\n        var displayList = this._displayList;\n        if (update || !displayList.length) {\n            this.updateDisplayList(includeIgnore);\n        }\n        return displayList;\n    };\n    Storage.prototype.updateDisplayList = function (includeIgnore) {\n        this._displayListLen = 0;\n        var roots = this._roots;\n        var displayList = this._displayList;\n        for (var i = 0, len = roots.length; i < len; i++) {\n            this._updateAndAddDisplayable(roots[i], null, includeIgnore);\n        }\n        displayList.length = this._displayListLen;\n        sort(displayList, shapeCompareFunc);\n    };\n    Storage.prototype._updateAndAddDisplayable = function (el, clipPaths, includeIgnore) {\n        if (el.ignore && !includeIgnore) {\n            return;\n        }\n        el.beforeUpdate();\n        el.update();\n        el.afterUpdate();\n        var userSetClipPath = el.getClipPath();\n        if (el.ignoreClip) {\n            clipPaths = null;\n        }\n        else if (userSetClipPath) {\n            if (clipPaths) {\n                clipPaths = clipPaths.slice();\n            }\n            else {\n                clipPaths = [];\n            }\n            var currentClipPath = userSetClipPath;\n            var parentClipPath = el;\n            while (currentClipPath) {\n                currentClipPath.parent = parentClipPath;\n                currentClipPath.updateTransform();\n                clipPaths.push(currentClipPath);\n                parentClipPath = currentClipPath;\n                currentClipPath = currentClipPath.getClipPath();\n            }\n        }\n        if (el.childrenRef) {\n            var children = el.childrenRef();\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                if (el.__dirty) {\n                    child.__dirty |= REDRAW_BIT;\n                }\n                this._updateAndAddDisplayable(child, clipPaths, includeIgnore);\n            }\n            el.__dirty = 0;\n        }\n        else {\n            var disp = el;\n            if (clipPaths && clipPaths.length) {\n                disp.__clipPaths = clipPaths;\n            }\n            else if (disp.__clipPaths && disp.__clipPaths.length > 0) {\n                disp.__clipPaths = [];\n            }\n            if (isNaN(disp.z)) {\n                logInvalidZError();\n                disp.z = 0;\n            }\n            if (isNaN(disp.z2)) {\n                logInvalidZError();\n                disp.z2 = 0;\n            }\n            if (isNaN(disp.zlevel)) {\n                logInvalidZError();\n                disp.zlevel = 0;\n            }\n            this._displayList[this._displayListLen++] = disp;\n        }\n        var decalEl = el.getDecalElement && el.getDecalElement();\n        if (decalEl) {\n            this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore);\n        }\n        var textGuide = el.getTextGuideLine();\n        if (textGuide) {\n            this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore);\n        }\n        var textEl = el.getTextContent();\n        if (textEl) {\n            this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore);\n        }\n    };\n    Storage.prototype.addRoot = function (el) {\n        if (el.__zr && el.__zr.storage === this) {\n            return;\n        }\n        this._roots.push(el);\n    };\n    Storage.prototype.delRoot = function (el) {\n        if (el instanceof Array) {\n            for (var i = 0, l = el.length; i < l; i++) {\n                this.delRoot(el[i]);\n            }\n            return;\n        }\n        var idx = indexOf(this._roots, el);\n        if (idx >= 0) {\n            this._roots.splice(idx, 1);\n        }\n    };\n    Storage.prototype.delAllRoots = function () {\n        this._roots = [];\n        this._displayList = [];\n        this._displayListLen = 0;\n        return;\n    };\n    Storage.prototype.getRoots = function () {\n        return this._roots;\n    };\n    Storage.prototype.dispose = function () {\n        this._displayList = null;\n        this._roots = null;\n    };\n    return Storage;\n}());\n\nvar requestAnimationFrame;\nrequestAnimationFrame = (env.hasGlobalWindow\n    && ((window.requestAnimationFrame && window.requestAnimationFrame.bind(window))\n        || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))\n        || window.mozRequestAnimationFrame\n        || window.webkitRequestAnimationFrame)) || function (func) {\n    return setTimeout(func, 16);\n};\nvar requestAnimationFrame$1 = requestAnimationFrame;\n\nvar easingFuncs = {\n    linear: function (k) {\n        return k;\n    },\n    quadraticIn: function (k) {\n        return k * k;\n    },\n    quadraticOut: function (k) {\n        return k * (2 - k);\n    },\n    quadraticInOut: function (k) {\n        if ((k *= 2) < 1) {\n            return 0.5 * k * k;\n        }\n        return -0.5 * (--k * (k - 2) - 1);\n    },\n    cubicIn: function (k) {\n        return k * k * k;\n    },\n    cubicOut: function (k) {\n        return --k * k * k + 1;\n    },\n    cubicInOut: function (k) {\n        if ((k *= 2) < 1) {\n            return 0.5 * k * k * k;\n        }\n        return 0.5 * ((k -= 2) * k * k + 2);\n    },\n    quarticIn: function (k) {\n        return k * k * k * k;\n    },\n    quarticOut: function (k) {\n        return 1 - (--k * k * k * k);\n    },\n    quarticInOut: function (k) {\n        if ((k *= 2) < 1) {\n            return 0.5 * k * k * k * k;\n        }\n        return -0.5 * ((k -= 2) * k * k * k - 2);\n    },\n    quinticIn: function (k) {\n        return k * k * k * k * k;\n    },\n    quinticOut: function (k) {\n        return --k * k * k * k * k + 1;\n    },\n    quinticInOut: function (k) {\n        if ((k *= 2) < 1) {\n            return 0.5 * k * k * k * k * k;\n        }\n        return 0.5 * ((k -= 2) * k * k * k * k + 2);\n    },\n    sinusoidalIn: function (k) {\n        return 1 - Math.cos(k * Math.PI / 2);\n    },\n    sinusoidalOut: function (k) {\n        return Math.sin(k * Math.PI / 2);\n    },\n    sinusoidalInOut: function (k) {\n        return 0.5 * (1 - Math.cos(Math.PI * k));\n    },\n    exponentialIn: function (k) {\n        return k === 0 ? 0 : Math.pow(1024, k - 1);\n    },\n    exponentialOut: function (k) {\n        return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);\n    },\n    exponentialInOut: function (k) {\n        if (k === 0) {\n            return 0;\n        }\n        if (k === 1) {\n            return 1;\n        }\n        if ((k *= 2) < 1) {\n            return 0.5 * Math.pow(1024, k - 1);\n        }\n        return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);\n    },\n    circularIn: function (k) {\n        return 1 - Math.sqrt(1 - k * k);\n    },\n    circularOut: function (k) {\n        return Math.sqrt(1 - (--k * k));\n    },\n    circularInOut: function (k) {\n        if ((k *= 2) < 1) {\n            return -0.5 * (Math.sqrt(1 - k * k) - 1);\n        }\n        return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);\n    },\n    elasticIn: function (k) {\n        var s;\n        var a = 0.1;\n        var p = 0.4;\n        if (k === 0) {\n            return 0;\n        }\n        if (k === 1) {\n            return 1;\n        }\n        if (!a || a < 1) {\n            a = 1;\n            s = p / 4;\n        }\n        else {\n            s = p * Math.asin(1 / a) / (2 * Math.PI);\n        }\n        return -(a * Math.pow(2, 10 * (k -= 1))\n            * Math.sin((k - s) * (2 * Math.PI) / p));\n    },\n    elasticOut: function (k) {\n        var s;\n        var a = 0.1;\n        var p = 0.4;\n        if (k === 0) {\n            return 0;\n        }\n        if (k === 1) {\n            return 1;\n        }\n        if (!a || a < 1) {\n            a = 1;\n            s = p / 4;\n        }\n        else {\n            s = p * Math.asin(1 / a) / (2 * Math.PI);\n        }\n        return (a * Math.pow(2, -10 * k)\n            * Math.sin((k - s) * (2 * Math.PI) / p) + 1);\n    },\n    elasticInOut: function (k) {\n        var s;\n        var a = 0.1;\n        var p = 0.4;\n        if (k === 0) {\n            return 0;\n        }\n        if (k === 1) {\n            return 1;\n        }\n        if (!a || a < 1) {\n            a = 1;\n            s = p / 4;\n        }\n        else {\n            s = p * Math.asin(1 / a) / (2 * Math.PI);\n        }\n        if ((k *= 2) < 1) {\n            return -0.5 * (a * Math.pow(2, 10 * (k -= 1))\n                * Math.sin((k - s) * (2 * Math.PI) / p));\n        }\n        return a * Math.pow(2, -10 * (k -= 1))\n            * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;\n    },\n    backIn: function (k) {\n        var s = 1.70158;\n        return k * k * ((s + 1) * k - s);\n    },\n    backOut: function (k) {\n        var s = 1.70158;\n        return --k * k * ((s + 1) * k + s) + 1;\n    },\n    backInOut: function (k) {\n        var s = 1.70158 * 1.525;\n        if ((k *= 2) < 1) {\n            return 0.5 * (k * k * ((s + 1) * k - s));\n        }\n        return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);\n    },\n    bounceIn: function (k) {\n        return 1 - easingFuncs.bounceOut(1 - k);\n    },\n    bounceOut: function (k) {\n        if (k < (1 / 2.75)) {\n            return 7.5625 * k * k;\n        }\n        else if (k < (2 / 2.75)) {\n            return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;\n        }\n        else if (k < (2.5 / 2.75)) {\n            return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;\n        }\n        else {\n            return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;\n        }\n    },\n    bounceInOut: function (k) {\n        if (k < 0.5) {\n            return easingFuncs.bounceIn(k * 2) * 0.5;\n        }\n        return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5;\n    }\n};\n\nvar mathPow = Math.pow;\nvar mathSqrt = Math.sqrt;\nvar EPSILON = 1e-8;\nvar EPSILON_NUMERIC = 1e-4;\nvar THREE_SQRT = mathSqrt(3);\nvar ONE_THIRD = 1 / 3;\nvar _v0 = create();\nvar _v1 = create();\nvar _v2 = create();\nfunction isAroundZero(val) {\n    return val > -EPSILON && val < EPSILON;\n}\nfunction isNotAroundZero(val) {\n    return val > EPSILON || val < -EPSILON;\n}\nfunction cubicAt(p0, p1, p2, p3, t) {\n    var onet = 1 - t;\n    return onet * onet * (onet * p0 + 3 * t * p1)\n        + t * t * (t * p3 + 3 * onet * p2);\n}\nfunction cubicDerivativeAt(p0, p1, p2, p3, t) {\n    var onet = 1 - t;\n    return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet\n        + (p3 - p2) * t * t);\n}\nfunction cubicRootAt(p0, p1, p2, p3, val, roots) {\n    var a = p3 + 3 * (p1 - p2) - p0;\n    var b = 3 * (p2 - p1 * 2 + p0);\n    var c = 3 * (p1 - p0);\n    var d = p0 - val;\n    var A = b * b - 3 * a * c;\n    var B = b * c - 9 * a * d;\n    var C = c * c - 3 * b * d;\n    var n = 0;\n    if (isAroundZero(A) && isAroundZero(B)) {\n        if (isAroundZero(b)) {\n            roots[0] = 0;\n        }\n        else {\n            var t1 = -c / b;\n            if (t1 >= 0 && t1 <= 1) {\n                roots[n++] = t1;\n            }\n        }\n    }\n    else {\n        var disc = B * B - 4 * A * C;\n        if (isAroundZero(disc)) {\n            var K = B / A;\n            var t1 = -b / a + K;\n            var t2 = -K / 2;\n            if (t1 >= 0 && t1 <= 1) {\n                roots[n++] = t1;\n            }\n            if (t2 >= 0 && t2 <= 1) {\n                roots[n++] = t2;\n            }\n        }\n        else if (disc > 0) {\n            var discSqrt = mathSqrt(disc);\n            var Y1 = A * b + 1.5 * a * (-B + discSqrt);\n            var Y2 = A * b + 1.5 * a * (-B - discSqrt);\n            if (Y1 < 0) {\n                Y1 = -mathPow(-Y1, ONE_THIRD);\n            }\n            else {\n                Y1 = mathPow(Y1, ONE_THIRD);\n            }\n            if (Y2 < 0) {\n                Y2 = -mathPow(-Y2, ONE_THIRD);\n            }\n            else {\n                Y2 = mathPow(Y2, ONE_THIRD);\n            }\n            var t1 = (-b - (Y1 + Y2)) / (3 * a);\n            if (t1 >= 0 && t1 <= 1) {\n                roots[n++] = t1;\n            }\n        }\n        else {\n            var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));\n            var theta = Math.acos(T) / 3;\n            var ASqrt = mathSqrt(A);\n            var tmp = Math.cos(theta);\n            var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);\n            var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);\n            var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);\n            if (t1 >= 0 && t1 <= 1) {\n                roots[n++] = t1;\n            }\n            if (t2 >= 0 && t2 <= 1) {\n                roots[n++] = t2;\n            }\n            if (t3 >= 0 && t3 <= 1) {\n                roots[n++] = t3;\n            }\n        }\n    }\n    return n;\n}\nfunction cubicExtrema(p0, p1, p2, p3, extrema) {\n    var b = 6 * p2 - 12 * p1 + 6 * p0;\n    var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;\n    var c = 3 * p1 - 3 * p0;\n    var n = 0;\n    if (isAroundZero(a)) {\n        if (isNotAroundZero(b)) {\n            var t1 = -c / b;\n            if (t1 >= 0 && t1 <= 1) {\n                extrema[n++] = t1;\n            }\n        }\n    }\n    else {\n        var disc = b * b - 4 * a * c;\n        if (isAroundZero(disc)) {\n            extrema[0] = -b / (2 * a);\n        }\n        else if (disc > 0) {\n            var discSqrt = mathSqrt(disc);\n            var t1 = (-b + discSqrt) / (2 * a);\n            var t2 = (-b - discSqrt) / (2 * a);\n            if (t1 >= 0 && t1 <= 1) {\n                extrema[n++] = t1;\n            }\n            if (t2 >= 0 && t2 <= 1) {\n                extrema[n++] = t2;\n            }\n        }\n    }\n    return n;\n}\nfunction cubicSubdivide(p0, p1, p2, p3, t, out) {\n    var p01 = (p1 - p0) * t + p0;\n    var p12 = (p2 - p1) * t + p1;\n    var p23 = (p3 - p2) * t + p2;\n    var p012 = (p12 - p01) * t + p01;\n    var p123 = (p23 - p12) * t + p12;\n    var p0123 = (p123 - p012) * t + p012;\n    out[0] = p0;\n    out[1] = p01;\n    out[2] = p012;\n    out[3] = p0123;\n    out[4] = p0123;\n    out[5] = p123;\n    out[6] = p23;\n    out[7] = p3;\n}\nfunction cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) {\n    var t;\n    var interval = 0.005;\n    var d = Infinity;\n    var prev;\n    var next;\n    var d1;\n    var d2;\n    _v0[0] = x;\n    _v0[1] = y;\n    for (var _t = 0; _t < 1; _t += 0.05) {\n        _v1[0] = cubicAt(x0, x1, x2, x3, _t);\n        _v1[1] = cubicAt(y0, y1, y2, y3, _t);\n        d1 = distSquare(_v0, _v1);\n        if (d1 < d) {\n            t = _t;\n            d = d1;\n        }\n    }\n    d = Infinity;\n    for (var i = 0; i < 32; i++) {\n        if (interval < EPSILON_NUMERIC) {\n            break;\n        }\n        prev = t - interval;\n        next = t + interval;\n        _v1[0] = cubicAt(x0, x1, x2, x3, prev);\n        _v1[1] = cubicAt(y0, y1, y2, y3, prev);\n        d1 = distSquare(_v1, _v0);\n        if (prev >= 0 && d1 < d) {\n            t = prev;\n            d = d1;\n        }\n        else {\n            _v2[0] = cubicAt(x0, x1, x2, x3, next);\n            _v2[1] = cubicAt(y0, y1, y2, y3, next);\n            d2 = distSquare(_v2, _v0);\n            if (next <= 1 && d2 < d) {\n                t = next;\n                d = d2;\n            }\n            else {\n                interval *= 0.5;\n            }\n        }\n    }\n    if (out) {\n        out[0] = cubicAt(x0, x1, x2, x3, t);\n        out[1] = cubicAt(y0, y1, y2, y3, t);\n    }\n    return mathSqrt(d);\n}\nfunction cubicLength(x0, y0, x1, y1, x2, y2, x3, y3, iteration) {\n    var px = x0;\n    var py = y0;\n    var d = 0;\n    var step = 1 / iteration;\n    for (var i = 1; i <= iteration; i++) {\n        var t = i * step;\n        var x = cubicAt(x0, x1, x2, x3, t);\n        var y = cubicAt(y0, y1, y2, y3, t);\n        var dx = x - px;\n        var dy = y - py;\n        d += Math.sqrt(dx * dx + dy * dy);\n        px = x;\n        py = y;\n    }\n    return d;\n}\nfunction quadraticAt(p0, p1, p2, t) {\n    var onet = 1 - t;\n    return onet * (onet * p0 + 2 * t * p1) + t * t * p2;\n}\nfunction quadraticDerivativeAt(p0, p1, p2, t) {\n    return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));\n}\nfunction quadraticRootAt(p0, p1, p2, val, roots) {\n    var a = p0 - 2 * p1 + p2;\n    var b = 2 * (p1 - p0);\n    var c = p0 - val;\n    var n = 0;\n    if (isAroundZero(a)) {\n        if (isNotAroundZero(b)) {\n            var t1 = -c / b;\n            if (t1 >= 0 && t1 <= 1) {\n                roots[n++] = t1;\n            }\n        }\n    }\n    else {\n        var disc = b * b - 4 * a * c;\n        if (isAroundZero(disc)) {\n            var t1 = -b / (2 * a);\n            if (t1 >= 0 && t1 <= 1) {\n                roots[n++] = t1;\n            }\n        }\n        else if (disc > 0) {\n            var discSqrt = mathSqrt(disc);\n            var t1 = (-b + discSqrt) / (2 * a);\n            var t2 = (-b - discSqrt) / (2 * a);\n            if (t1 >= 0 && t1 <= 1) {\n                roots[n++] = t1;\n            }\n            if (t2 >= 0 && t2 <= 1) {\n                roots[n++] = t2;\n            }\n        }\n    }\n    return n;\n}\nfunction quadraticExtremum(p0, p1, p2) {\n    var divider = p0 + p2 - 2 * p1;\n    if (divider === 0) {\n        return 0.5;\n    }\n    else {\n        return (p0 - p1) / divider;\n    }\n}\nfunction quadraticSubdivide(p0, p1, p2, t, out) {\n    var p01 = (p1 - p0) * t + p0;\n    var p12 = (p2 - p1) * t + p1;\n    var p012 = (p12 - p01) * t + p01;\n    out[0] = p0;\n    out[1] = p01;\n    out[2] = p012;\n    out[3] = p012;\n    out[4] = p12;\n    out[5] = p2;\n}\nfunction quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) {\n    var t;\n    var interval = 0.005;\n    var d = Infinity;\n    _v0[0] = x;\n    _v0[1] = y;\n    for (var _t = 0; _t < 1; _t += 0.05) {\n        _v1[0] = quadraticAt(x0, x1, x2, _t);\n        _v1[1] = quadraticAt(y0, y1, y2, _t);\n        var d1 = distSquare(_v0, _v1);\n        if (d1 < d) {\n            t = _t;\n            d = d1;\n        }\n    }\n    d = Infinity;\n    for (var i = 0; i < 32; i++) {\n        if (interval < EPSILON_NUMERIC) {\n            break;\n        }\n        var prev = t - interval;\n        var next = t + interval;\n        _v1[0] = quadraticAt(x0, x1, x2, prev);\n        _v1[1] = quadraticAt(y0, y1, y2, prev);\n        var d1 = distSquare(_v1, _v0);\n        if (prev >= 0 && d1 < d) {\n            t = prev;\n            d = d1;\n        }\n        else {\n            _v2[0] = quadraticAt(x0, x1, x2, next);\n            _v2[1] = quadraticAt(y0, y1, y2, next);\n            var d2 = distSquare(_v2, _v0);\n            if (next <= 1 && d2 < d) {\n                t = next;\n                d = d2;\n            }\n            else {\n                interval *= 0.5;\n            }\n        }\n    }\n    if (out) {\n        out[0] = quadraticAt(x0, x1, x2, t);\n        out[1] = quadraticAt(y0, y1, y2, t);\n    }\n    return mathSqrt(d);\n}\nfunction quadraticLength(x0, y0, x1, y1, x2, y2, iteration) {\n    var px = x0;\n    var py = y0;\n    var d = 0;\n    var step = 1 / iteration;\n    for (var i = 1; i <= iteration; i++) {\n        var t = i * step;\n        var x = quadraticAt(x0, x1, x2, t);\n        var y = quadraticAt(y0, y1, y2, t);\n        var dx = x - px;\n        var dy = y - py;\n        d += Math.sqrt(dx * dx + dy * dy);\n        px = x;\n        py = y;\n    }\n    return d;\n}\n\nvar regexp = /cubic-bezier\\(([0-9,\\.e ]+)\\)/;\nfunction createCubicEasingFunc(cubicEasingStr) {\n    var cubic = cubicEasingStr && regexp.exec(cubicEasingStr);\n    if (cubic) {\n        var points = cubic[1].split(',');\n        var a_1 = +trim(points[0]);\n        var b_1 = +trim(points[1]);\n        var c_1 = +trim(points[2]);\n        var d_1 = +trim(points[3]);\n        if (isNaN(a_1 + b_1 + c_1 + d_1)) {\n            return;\n        }\n        var roots_1 = [];\n        return function (p) {\n            return p <= 0\n                ? 0 : p >= 1\n                ? 1\n                : cubicRootAt(0, a_1, c_1, 1, p, roots_1) && cubicAt(0, b_1, d_1, 1, roots_1[0]);\n        };\n    }\n}\n\nvar Clip = (function () {\n    function Clip(opts) {\n        this._inited = false;\n        this._startTime = 0;\n        this._pausedTime = 0;\n        this._paused = false;\n        this._life = opts.life || 1000;\n        this._delay = opts.delay || 0;\n        this.loop = opts.loop || false;\n        this.onframe = opts.onframe || noop;\n        this.ondestroy = opts.ondestroy || noop;\n        this.onrestart = opts.onrestart || noop;\n        opts.easing && this.setEasing(opts.easing);\n    }\n    Clip.prototype.step = function (globalTime, deltaTime) {\n        if (!this._inited) {\n            this._startTime = globalTime + this._delay;\n            this._inited = true;\n        }\n        if (this._paused) {\n            this._pausedTime += deltaTime;\n            return;\n        }\n        var life = this._life;\n        var elapsedTime = globalTime - this._startTime - this._pausedTime;\n        var percent = elapsedTime / life;\n        if (percent < 0) {\n            percent = 0;\n        }\n        percent = Math.min(percent, 1);\n        var easingFunc = this.easingFunc;\n        var schedule = easingFunc ? easingFunc(percent) : percent;\n        this.onframe(schedule);\n        if (percent === 1) {\n            if (this.loop) {\n                var remainder = elapsedTime % life;\n                this._startTime = globalTime - remainder;\n                this._pausedTime = 0;\n                this.onrestart();\n            }\n            else {\n                return true;\n            }\n        }\n        return false;\n    };\n    Clip.prototype.pause = function () {\n        this._paused = true;\n    };\n    Clip.prototype.resume = function () {\n        this._paused = false;\n    };\n    Clip.prototype.setEasing = function (easing) {\n        this.easing = easing;\n        this.easingFunc = isFunction(easing)\n            ? easing\n            : easingFuncs[easing] || createCubicEasingFunc(easing);\n    };\n    return Clip;\n}());\n\nvar Entry = (function () {\n    function Entry(val) {\n        this.value = val;\n    }\n    return Entry;\n}());\nvar LinkedList = (function () {\n    function LinkedList() {\n        this._len = 0;\n    }\n    LinkedList.prototype.insert = function (val) {\n        var entry = new Entry(val);\n        this.insertEntry(entry);\n        return entry;\n    };\n    LinkedList.prototype.insertEntry = function (entry) {\n        if (!this.head) {\n            this.head = this.tail = entry;\n        }\n        else {\n            this.tail.next = entry;\n            entry.prev = this.tail;\n            entry.next = null;\n            this.tail = entry;\n        }\n        this._len++;\n    };\n    LinkedList.prototype.remove = function (entry) {\n        var prev = entry.prev;\n        var next = entry.next;\n        if (prev) {\n            prev.next = next;\n        }\n        else {\n            this.head = next;\n        }\n        if (next) {\n            next.prev = prev;\n        }\n        else {\n            this.tail = prev;\n        }\n        entry.next = entry.prev = null;\n        this._len--;\n    };\n    LinkedList.prototype.len = function () {\n        return this._len;\n    };\n    LinkedList.prototype.clear = function () {\n        this.head = this.tail = null;\n        this._len = 0;\n    };\n    return LinkedList;\n}());\nvar LRU = (function () {\n    function LRU(maxSize) {\n        this._list = new LinkedList();\n        this._maxSize = 10;\n        this._map = {};\n        this._maxSize = maxSize;\n    }\n    LRU.prototype.put = function (key, value) {\n        var list = this._list;\n        var map = this._map;\n        var removed = null;\n        if (map[key] == null) {\n            var len = list.len();\n            var entry = this._lastRemovedEntry;\n            if (len >= this._maxSize && len > 0) {\n                var leastUsedEntry = list.head;\n                list.remove(leastUsedEntry);\n                delete map[leastUsedEntry.key];\n                removed = leastUsedEntry.value;\n                this._lastRemovedEntry = leastUsedEntry;\n            }\n            if (entry) {\n                entry.value = value;\n            }\n            else {\n                entry = new Entry(value);\n            }\n            entry.key = key;\n            list.insertEntry(entry);\n            map[key] = entry;\n        }\n        return removed;\n    };\n    LRU.prototype.get = function (key) {\n        var entry = this._map[key];\n        var list = this._list;\n        if (entry != null) {\n            if (entry !== list.tail) {\n                list.remove(entry);\n                list.insertEntry(entry);\n            }\n            return entry.value;\n        }\n    };\n    LRU.prototype.clear = function () {\n        this._list.clear();\n        this._map = {};\n    };\n    LRU.prototype.len = function () {\n        return this._list.len();\n    };\n    return LRU;\n}());\n\nvar kCSSColorTable = {\n    'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1],\n    'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1],\n    'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1],\n    'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1],\n    'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1],\n    'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1],\n    'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1],\n    'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1],\n    'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1],\n    'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1],\n    'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1],\n    'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1],\n    'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1],\n    'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1],\n    'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1],\n    'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1],\n    'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1],\n    'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1],\n    'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1],\n    'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1],\n    'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1],\n    'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1],\n    'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1],\n    'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1],\n    'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1],\n    'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1],\n    'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1],\n    'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1],\n    'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1],\n    'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1],\n    'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1],\n    'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1],\n    'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1],\n    'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1],\n    'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1],\n    'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1],\n    'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1],\n    'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1],\n    'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1],\n    'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1],\n    'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1],\n    'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1],\n    'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1],\n    'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1],\n    'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1],\n    'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1],\n    'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1],\n    'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1],\n    'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1],\n    'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1],\n    'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1],\n    'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1],\n    'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1],\n    'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1],\n    'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1],\n    'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1],\n    'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1],\n    'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1],\n    'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1],\n    'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1],\n    'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1],\n    'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1],\n    'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1],\n    'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1],\n    'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1],\n    'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1],\n    'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1],\n    'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1],\n    'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1],\n    'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1],\n    'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1],\n    'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1],\n    'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1],\n    'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1]\n};\nfunction clampCssByte(i) {\n    i = Math.round(i);\n    return i < 0 ? 0 : i > 255 ? 255 : i;\n}\nfunction clampCssAngle(i) {\n    i = Math.round(i);\n    return i < 0 ? 0 : i > 360 ? 360 : i;\n}\nfunction clampCssFloat(f) {\n    return f < 0 ? 0 : f > 1 ? 1 : f;\n}\nfunction parseCssInt(val) {\n    var str = val;\n    if (str.length && str.charAt(str.length - 1) === '%') {\n        return clampCssByte(parseFloat(str) / 100 * 255);\n    }\n    return clampCssByte(parseInt(str, 10));\n}\nfunction parseCssFloat(val) {\n    var str = val;\n    if (str.length && str.charAt(str.length - 1) === '%') {\n        return clampCssFloat(parseFloat(str) / 100);\n    }\n    return clampCssFloat(parseFloat(str));\n}\nfunction cssHueToRgb(m1, m2, h) {\n    if (h < 0) {\n        h += 1;\n    }\n    else if (h > 1) {\n        h -= 1;\n    }\n    if (h * 6 < 1) {\n        return m1 + (m2 - m1) * h * 6;\n    }\n    if (h * 2 < 1) {\n        return m2;\n    }\n    if (h * 3 < 2) {\n        return m1 + (m2 - m1) * (2 / 3 - h) * 6;\n    }\n    return m1;\n}\nfunction lerpNumber(a, b, p) {\n    return a + (b - a) * p;\n}\nfunction setRgba(out, r, g, b, a) {\n    out[0] = r;\n    out[1] = g;\n    out[2] = b;\n    out[3] = a;\n    return out;\n}\nfunction copyRgba(out, a) {\n    out[0] = a[0];\n    out[1] = a[1];\n    out[2] = a[2];\n    out[3] = a[3];\n    return out;\n}\nvar colorCache = new LRU(20);\nvar lastRemovedArr = null;\nfunction putToCache(colorStr, rgbaArr) {\n    if (lastRemovedArr) {\n        copyRgba(lastRemovedArr, rgbaArr);\n    }\n    lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));\n}\nfunction parse(colorStr, rgbaArr) {\n    if (!colorStr) {\n        return;\n    }\n    rgbaArr = rgbaArr || [];\n    var cached = colorCache.get(colorStr);\n    if (cached) {\n        return copyRgba(rgbaArr, cached);\n    }\n    colorStr = colorStr + '';\n    var str = colorStr.replace(/ /g, '').toLowerCase();\n    if (str in kCSSColorTable) {\n        copyRgba(rgbaArr, kCSSColorTable[str]);\n        putToCache(colorStr, rgbaArr);\n        return rgbaArr;\n    }\n    var strLen = str.length;\n    if (str.charAt(0) === '#') {\n        if (strLen === 4 || strLen === 5) {\n            var iv = parseInt(str.slice(1, 4), 16);\n            if (!(iv >= 0 && iv <= 0xfff)) {\n                setRgba(rgbaArr, 0, 0, 0, 1);\n                return;\n            }\n            setRgba(rgbaArr, ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), (iv & 0xf0) | ((iv & 0xf0) >> 4), (iv & 0xf) | ((iv & 0xf) << 4), strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1);\n            putToCache(colorStr, rgbaArr);\n            return rgbaArr;\n        }\n        else if (strLen === 7 || strLen === 9) {\n            var iv = parseInt(str.slice(1, 7), 16);\n            if (!(iv >= 0 && iv <= 0xffffff)) {\n                setRgba(rgbaArr, 0, 0, 0, 1);\n                return;\n            }\n            setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1);\n            putToCache(colorStr, rgbaArr);\n            return rgbaArr;\n        }\n        return;\n    }\n    var op = str.indexOf('(');\n    var ep = str.indexOf(')');\n    if (op !== -1 && ep + 1 === strLen) {\n        var fname = str.substr(0, op);\n        var params = str.substr(op + 1, ep - (op + 1)).split(',');\n        var alpha = 1;\n        switch (fname) {\n            case 'rgba':\n                if (params.length !== 4) {\n                    return params.length === 3\n                        ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1)\n                        : setRgba(rgbaArr, 0, 0, 0, 1);\n                }\n                alpha = parseCssFloat(params.pop());\n            case 'rgb':\n                if (params.length >= 3) {\n                    setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), params.length === 3 ? alpha : parseCssFloat(params[3]));\n                    putToCache(colorStr, rgbaArr);\n                    return rgbaArr;\n                }\n                else {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n            case 'hsla':\n                if (params.length !== 4) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                params[3] = parseCssFloat(params[3]);\n                hsla2rgba(params, rgbaArr);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            case 'hsl':\n                if (params.length !== 3) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                hsla2rgba(params, rgbaArr);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            default:\n                return;\n        }\n    }\n    setRgba(rgbaArr, 0, 0, 0, 1);\n    return;\n}\nfunction hsla2rgba(hsla, rgba) {\n    var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360;\n    var s = parseCssFloat(hsla[1]);\n    var l = parseCssFloat(hsla[2]);\n    var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n    var m1 = l * 2 - m2;\n    rgba = rgba || [];\n    setRgba(rgba, clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), clampCssByte(cssHueToRgb(m1, m2, h) * 255), clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 1);\n    if (hsla.length === 4) {\n        rgba[3] = hsla[3];\n    }\n    return rgba;\n}\nfunction rgba2hsla(rgba) {\n    if (!rgba) {\n        return;\n    }\n    var R = rgba[0] / 255;\n    var G = rgba[1] / 255;\n    var B = rgba[2] / 255;\n    var vMin = Math.min(R, G, B);\n    var vMax = Math.max(R, G, B);\n    var delta = vMax - vMin;\n    var L = (vMax + vMin) / 2;\n    var H;\n    var S;\n    if (delta === 0) {\n        H = 0;\n        S = 0;\n    }\n    else {\n        if (L < 0.5) {\n            S = delta / (vMax + vMin);\n        }\n        else {\n            S = delta / (2 - vMax - vMin);\n        }\n        var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;\n        var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;\n        var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;\n        if (R === vMax) {\n            H = deltaB - deltaG;\n        }\n        else if (G === vMax) {\n            H = (1 / 3) + deltaR - deltaB;\n        }\n        else if (B === vMax) {\n            H = (2 / 3) + deltaG - deltaR;\n        }\n        if (H < 0) {\n            H += 1;\n        }\n        if (H > 1) {\n            H -= 1;\n        }\n    }\n    var hsla = [H * 360, S, L];\n    if (rgba[3] != null) {\n        hsla.push(rgba[3]);\n    }\n    return hsla;\n}\nfunction lift(color, level) {\n    var colorArr = parse(color);\n    if (colorArr) {\n        for (var i = 0; i < 3; i++) {\n            if (level < 0) {\n                colorArr[i] = colorArr[i] * (1 - level) | 0;\n            }\n            else {\n                colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;\n            }\n            if (colorArr[i] > 255) {\n                colorArr[i] = 255;\n            }\n            else if (colorArr[i] < 0) {\n                colorArr[i] = 0;\n            }\n        }\n        return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');\n    }\n}\nfunction toHex(color) {\n    var colorArr = parse(color);\n    if (colorArr) {\n        return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);\n    }\n}\nfunction fastLerp(normalizedValue, colors, out) {\n    if (!(colors && colors.length)\n        || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n        return;\n    }\n    out = out || [];\n    var value = normalizedValue * (colors.length - 1);\n    var leftIndex = Math.floor(value);\n    var rightIndex = Math.ceil(value);\n    var leftColor = colors[leftIndex];\n    var rightColor = colors[rightIndex];\n    var dv = value - leftIndex;\n    out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));\n    out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));\n    out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));\n    out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));\n    return out;\n}\nvar fastMapToColor = fastLerp;\nfunction lerp$1(normalizedValue, colors, fullOutput) {\n    if (!(colors && colors.length)\n        || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n        return;\n    }\n    var value = normalizedValue * (colors.length - 1);\n    var leftIndex = Math.floor(value);\n    var rightIndex = Math.ceil(value);\n    var leftColor = parse(colors[leftIndex]);\n    var rightColor = parse(colors[rightIndex]);\n    var dv = value - leftIndex;\n    var color = stringify([\n        clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),\n        clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),\n        clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),\n        clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))\n    ], 'rgba');\n    return fullOutput\n        ? {\n            color: color,\n            leftIndex: leftIndex,\n            rightIndex: rightIndex,\n            value: value\n        }\n        : color;\n}\nvar mapToColor = lerp$1;\nfunction modifyHSL(color, h, s, l) {\n    var colorArr = parse(color);\n    if (color) {\n        colorArr = rgba2hsla(colorArr);\n        h != null && (colorArr[0] = clampCssAngle(h));\n        s != null && (colorArr[1] = parseCssFloat(s));\n        l != null && (colorArr[2] = parseCssFloat(l));\n        return stringify(hsla2rgba(colorArr), 'rgba');\n    }\n}\nfunction modifyAlpha(color, alpha) {\n    var colorArr = parse(color);\n    if (colorArr && alpha != null) {\n        colorArr[3] = clampCssFloat(alpha);\n        return stringify(colorArr, 'rgba');\n    }\n}\nfunction stringify(arrColor, type) {\n    if (!arrColor || !arrColor.length) {\n        return;\n    }\n    var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];\n    if (type === 'rgba' || type === 'hsva' || type === 'hsla') {\n        colorStr += ',' + arrColor[3];\n    }\n    return type + '(' + colorStr + ')';\n}\nfunction lum(color, backgroundLum) {\n    var arr = parse(color);\n    return arr\n        ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255\n            + (1 - arr[3]) * backgroundLum\n        : 0;\n}\nfunction random() {\n    return stringify([\n        Math.round(Math.random() * 255),\n        Math.round(Math.random() * 255),\n        Math.round(Math.random() * 255)\n    ], 'rgb');\n}\n\nvar color = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    parse: parse,\n    lift: lift,\n    toHex: toHex,\n    fastLerp: fastLerp,\n    fastMapToColor: fastMapToColor,\n    lerp: lerp$1,\n    mapToColor: mapToColor,\n    modifyHSL: modifyHSL,\n    modifyAlpha: modifyAlpha,\n    stringify: stringify,\n    lum: lum,\n    random: random\n});\n\nvar mathRound = Math.round;\nfunction normalizeColor(color) {\n    var opacity;\n    if (!color || color === 'transparent') {\n        color = 'none';\n    }\n    else if (typeof color === 'string' && color.indexOf('rgba') > -1) {\n        var arr = parse(color);\n        if (arr) {\n            color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')';\n            opacity = arr[3];\n        }\n    }\n    return {\n        color: color,\n        opacity: opacity == null ? 1 : opacity\n    };\n}\nvar EPSILON$1 = 1e-4;\nfunction isAroundZero$1(transform) {\n    return transform < EPSILON$1 && transform > -EPSILON$1;\n}\nfunction round3(transform) {\n    return mathRound(transform * 1e3) / 1e3;\n}\nfunction round4(transform) {\n    return mathRound(transform * 1e4) / 1e4;\n}\nfunction getMatrixStr(m) {\n    return 'matrix('\n        + round3(m[0]) + ','\n        + round3(m[1]) + ','\n        + round3(m[2]) + ','\n        + round3(m[3]) + ','\n        + round4(m[4]) + ','\n        + round4(m[5])\n        + ')';\n}\nvar TEXT_ALIGN_TO_ANCHOR = {\n    left: 'start',\n    right: 'end',\n    center: 'middle',\n    middle: 'middle'\n};\nfunction adjustTextY(y, lineHeight, textBaseline) {\n    if (textBaseline === 'top') {\n        y += lineHeight / 2;\n    }\n    else if (textBaseline === 'bottom') {\n        y -= lineHeight / 2;\n    }\n    return y;\n}\nfunction hasShadow(style) {\n    return style\n        && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY);\n}\nfunction getShadowKey(displayable) {\n    var style = displayable.style;\n    var globalScale = displayable.getGlobalScale();\n    return [\n        style.shadowColor,\n        (style.shadowBlur || 0).toFixed(2),\n        (style.shadowOffsetX || 0).toFixed(2),\n        (style.shadowOffsetY || 0).toFixed(2),\n        globalScale[0],\n        globalScale[1]\n    ].join(',');\n}\nfunction isImagePattern(val) {\n    return val && (!!val.image);\n}\nfunction isSVGPattern(val) {\n    return val && (!!val.svgElement);\n}\nfunction isPattern(val) {\n    return isImagePattern(val) || isSVGPattern(val);\n}\nfunction isLinearGradient(val) {\n    return val.type === 'linear';\n}\nfunction isRadialGradient(val) {\n    return val.type === 'radial';\n}\nfunction isGradient(val) {\n    return val && (val.type === 'linear'\n        || val.type === 'radial');\n}\nfunction getIdURL(id) {\n    return \"url(#\" + id + \")\";\n}\nfunction getPathPrecision(el) {\n    var scale = el.getGlobalScale();\n    var size = Math.max(scale[0], scale[1]);\n    return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1);\n}\nfunction getSRTTransformString(transform) {\n    var x = transform.x || 0;\n    var y = transform.y || 0;\n    var rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE;\n    var scaleX = retrieve2(transform.scaleX, 1);\n    var scaleY = retrieve2(transform.scaleY, 1);\n    var skewX = transform.skewX || 0;\n    var skewY = transform.skewY || 0;\n    var res = [];\n    if (x || y) {\n        res.push(\"translate(\" + x + \"px,\" + y + \"px)\");\n    }\n    if (rotation) {\n        res.push(\"rotate(\" + rotation + \")\");\n    }\n    if (scaleX !== 1 || scaleY !== 1) {\n        res.push(\"scale(\" + scaleX + \",\" + scaleY + \")\");\n    }\n    if (skewX || skewY) {\n        res.push(\"skew(\" + mathRound(skewX * RADIAN_TO_DEGREE) + \"deg, \" + mathRound(skewY * RADIAN_TO_DEGREE) + \"deg)\");\n    }\n    return res.join(' ');\n}\nvar encodeBase64 = (function () {\n    if (env.hasGlobalWindow && isFunction(window.btoa)) {\n        return function (str) {\n            return window.btoa(unescape(encodeURIComponent(str)));\n        };\n    }\n    if (typeof Buffer !== 'undefined') {\n        return function (str) {\n            return Buffer.from(str).toString('base64');\n        };\n    }\n    return function (str) {\n        if (\"development\" !== 'production') {\n            logError('Base64 isn\\'t natively supported in the current environment.');\n        }\n        return null;\n    };\n})();\n\nvar arraySlice = Array.prototype.slice;\nfunction interpolateNumber(p0, p1, percent) {\n    return (p1 - p0) * percent + p0;\n}\nfunction interpolate1DArray(out, p0, p1, percent) {\n    var len = p0.length;\n    for (var i = 0; i < len; i++) {\n        out[i] = interpolateNumber(p0[i], p1[i], percent);\n    }\n    return out;\n}\nfunction interpolate2DArray(out, p0, p1, percent) {\n    var len = p0.length;\n    var len2 = len && p0[0].length;\n    for (var i = 0; i < len; i++) {\n        if (!out[i]) {\n            out[i] = [];\n        }\n        for (var j = 0; j < len2; j++) {\n            out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent);\n        }\n    }\n    return out;\n}\nfunction add1DArray(out, p0, p1, sign) {\n    var len = p0.length;\n    for (var i = 0; i < len; i++) {\n        out[i] = p0[i] + p1[i] * sign;\n    }\n    return out;\n}\nfunction add2DArray(out, p0, p1, sign) {\n    var len = p0.length;\n    var len2 = len && p0[0].length;\n    for (var i = 0; i < len; i++) {\n        if (!out[i]) {\n            out[i] = [];\n        }\n        for (var j = 0; j < len2; j++) {\n            out[i][j] = p0[i][j] + p1[i][j] * sign;\n        }\n    }\n    return out;\n}\nfunction fillColorStops(val0, val1) {\n    var len0 = val0.length;\n    var len1 = val1.length;\n    var shorterArr = len0 > len1 ? val1 : val0;\n    var shorterLen = Math.min(len0, len1);\n    var last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 };\n    for (var i = shorterLen; i < Math.max(len0, len1); i++) {\n        shorterArr.push({\n            offset: last.offset,\n            color: last.color.slice()\n        });\n    }\n}\nfunction fillArray(val0, val1, arrDim) {\n    var arr0 = val0;\n    var arr1 = val1;\n    if (!arr0.push || !arr1.push) {\n        return;\n    }\n    var arr0Len = arr0.length;\n    var arr1Len = arr1.length;\n    if (arr0Len !== arr1Len) {\n        var isPreviousLarger = arr0Len > arr1Len;\n        if (isPreviousLarger) {\n            arr0.length = arr1Len;\n        }\n        else {\n            for (var i = arr0Len; i < arr1Len; i++) {\n                arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]));\n            }\n        }\n    }\n    var len2 = arr0[0] && arr0[0].length;\n    for (var i = 0; i < arr0.length; i++) {\n        if (arrDim === 1) {\n            if (isNaN(arr0[i])) {\n                arr0[i] = arr1[i];\n            }\n        }\n        else {\n            for (var j = 0; j < len2; j++) {\n                if (isNaN(arr0[i][j])) {\n                    arr0[i][j] = arr1[i][j];\n                }\n            }\n        }\n    }\n}\nfunction cloneValue(value) {\n    if (isArrayLike(value)) {\n        var len = value.length;\n        if (isArrayLike(value[0])) {\n            var ret = [];\n            for (var i = 0; i < len; i++) {\n                ret.push(arraySlice.call(value[i]));\n            }\n            return ret;\n        }\n        return arraySlice.call(value);\n    }\n    return value;\n}\nfunction rgba2String(rgba) {\n    rgba[0] = Math.floor(rgba[0]) || 0;\n    rgba[1] = Math.floor(rgba[1]) || 0;\n    rgba[2] = Math.floor(rgba[2]) || 0;\n    rgba[3] = rgba[3] == null ? 1 : rgba[3];\n    return 'rgba(' + rgba.join(',') + ')';\n}\nfunction guessArrayDim(value) {\n    return isArrayLike(value && value[0]) ? 2 : 1;\n}\nvar VALUE_TYPE_NUMBER = 0;\nvar VALUE_TYPE_1D_ARRAY = 1;\nvar VALUE_TYPE_2D_ARRAY = 2;\nvar VALUE_TYPE_COLOR = 3;\nvar VALUE_TYPE_LINEAR_GRADIENT = 4;\nvar VALUE_TYPE_RADIAL_GRADIENT = 5;\nvar VALUE_TYPE_UNKOWN = 6;\nfunction isGradientValueType(valType) {\n    return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT;\n}\nfunction isArrayValueType(valType) {\n    return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY;\n}\nvar tmpRgba = [0, 0, 0, 0];\nvar Track = (function () {\n    function Track(propName) {\n        this.keyframes = [];\n        this.discrete = false;\n        this._invalid = false;\n        this._needsSort = false;\n        this._lastFr = 0;\n        this._lastFrP = 0;\n        this.propName = propName;\n    }\n    Track.prototype.isFinished = function () {\n        return this._finished;\n    };\n    Track.prototype.setFinished = function () {\n        this._finished = true;\n        if (this._additiveTrack) {\n            this._additiveTrack.setFinished();\n        }\n    };\n    Track.prototype.needsAnimate = function () {\n        return this.keyframes.length >= 1;\n    };\n    Track.prototype.getAdditiveTrack = function () {\n        return this._additiveTrack;\n    };\n    Track.prototype.addKeyframe = function (time, rawValue, easing) {\n        this._needsSort = true;\n        var keyframes = this.keyframes;\n        var len = keyframes.length;\n        var discrete = false;\n        var valType = VALUE_TYPE_UNKOWN;\n        var value = rawValue;\n        if (isArrayLike(rawValue)) {\n            var arrayDim = guessArrayDim(rawValue);\n            valType = arrayDim;\n            if (arrayDim === 1 && !isNumber(rawValue[0])\n                || arrayDim === 2 && !isNumber(rawValue[0][0])) {\n                discrete = true;\n            }\n        }\n        else {\n            if (isNumber(rawValue) && !eqNaN(rawValue)) {\n                valType = VALUE_TYPE_NUMBER;\n            }\n            else if (isString(rawValue)) {\n                if (!isNaN(+rawValue)) {\n                    valType = VALUE_TYPE_NUMBER;\n                }\n                else {\n                    var colorArray = parse(rawValue);\n                    if (colorArray) {\n                        value = colorArray;\n                        valType = VALUE_TYPE_COLOR;\n                    }\n                }\n            }\n            else if (isGradientObject(rawValue)) {\n                var parsedGradient = extend({}, value);\n                parsedGradient.colorStops = map(rawValue.colorStops, function (colorStop) { return ({\n                    offset: colorStop.offset,\n                    color: parse(colorStop.color)\n                }); });\n                if (isLinearGradient(rawValue)) {\n                    valType = VALUE_TYPE_LINEAR_GRADIENT;\n                }\n                else if (isRadialGradient(rawValue)) {\n                    valType = VALUE_TYPE_RADIAL_GRADIENT;\n                }\n                value = parsedGradient;\n            }\n        }\n        if (len === 0) {\n            this.valType = valType;\n        }\n        else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) {\n            discrete = true;\n        }\n        this.discrete = this.discrete || discrete;\n        var kf = {\n            time: time,\n            value: value,\n            rawValue: rawValue,\n            percent: 0\n        };\n        if (easing) {\n            kf.easing = easing;\n            kf.easingFunc = isFunction(easing)\n                ? easing\n                : easingFuncs[easing] || createCubicEasingFunc(easing);\n        }\n        keyframes.push(kf);\n        return kf;\n    };\n    Track.prototype.prepare = function (maxTime, additiveTrack) {\n        var kfs = this.keyframes;\n        if (this._needsSort) {\n            kfs.sort(function (a, b) {\n                return a.time - b.time;\n            });\n        }\n        var valType = this.valType;\n        var kfsLen = kfs.length;\n        var lastKf = kfs[kfsLen - 1];\n        var isDiscrete = this.discrete;\n        var isArr = isArrayValueType(valType);\n        var isGradient = isGradientValueType(valType);\n        for (var i = 0; i < kfsLen; i++) {\n            var kf = kfs[i];\n            var value = kf.value;\n            var lastValue = lastKf.value;\n            kf.percent = kf.time / maxTime;\n            if (!isDiscrete) {\n                if (isArr && i !== kfsLen - 1) {\n                    fillArray(value, lastValue, valType);\n                }\n                else if (isGradient) {\n                    fillColorStops(value.colorStops, lastValue.colorStops);\n                }\n            }\n        }\n        if (!isDiscrete\n            && valType !== VALUE_TYPE_RADIAL_GRADIENT\n            && additiveTrack\n            && this.needsAnimate()\n            && additiveTrack.needsAnimate()\n            && valType === additiveTrack.valType\n            && !additiveTrack._finished) {\n            this._additiveTrack = additiveTrack;\n            var startValue = kfs[0].value;\n            for (var i = 0; i < kfsLen; i++) {\n                if (valType === VALUE_TYPE_NUMBER) {\n                    kfs[i].additiveValue = kfs[i].value - startValue;\n                }\n                else if (valType === VALUE_TYPE_COLOR) {\n                    kfs[i].additiveValue =\n                        add1DArray([], kfs[i].value, startValue, -1);\n                }\n                else if (isArrayValueType(valType)) {\n                    kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY\n                        ? add1DArray([], kfs[i].value, startValue, -1)\n                        : add2DArray([], kfs[i].value, startValue, -1);\n                }\n            }\n        }\n    };\n    Track.prototype.step = function (target, percent) {\n        if (this._finished) {\n            return;\n        }\n        if (this._additiveTrack && this._additiveTrack._finished) {\n            this._additiveTrack = null;\n        }\n        var isAdditive = this._additiveTrack != null;\n        var valueKey = isAdditive ? 'additiveValue' : 'value';\n        var valType = this.valType;\n        var keyframes = this.keyframes;\n        var kfsNum = keyframes.length;\n        var propName = this.propName;\n        var isValueColor = valType === VALUE_TYPE_COLOR;\n        var frameIdx;\n        var lastFrame = this._lastFr;\n        var mathMin = Math.min;\n        var frame;\n        var nextFrame;\n        if (kfsNum === 1) {\n            frame = nextFrame = keyframes[0];\n        }\n        else {\n            if (percent < 0) {\n                frameIdx = 0;\n            }\n            else if (percent < this._lastFrP) {\n                var start = mathMin(lastFrame + 1, kfsNum - 1);\n                for (frameIdx = start; frameIdx >= 0; frameIdx--) {\n                    if (keyframes[frameIdx].percent <= percent) {\n                        break;\n                    }\n                }\n                frameIdx = mathMin(frameIdx, kfsNum - 2);\n            }\n            else {\n                for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) {\n                    if (keyframes[frameIdx].percent > percent) {\n                        break;\n                    }\n                }\n                frameIdx = mathMin(frameIdx - 1, kfsNum - 2);\n            }\n            nextFrame = keyframes[frameIdx + 1];\n            frame = keyframes[frameIdx];\n        }\n        if (!(frame && nextFrame)) {\n            return;\n        }\n        this._lastFr = frameIdx;\n        this._lastFrP = percent;\n        var interval = (nextFrame.percent - frame.percent);\n        var w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1);\n        if (nextFrame.easingFunc) {\n            w = nextFrame.easingFunc(w);\n        }\n        var targetArr = isAdditive ? this._additiveValue\n            : (isValueColor ? tmpRgba : target[propName]);\n        if ((isArrayValueType(valType) || isValueColor) && !targetArr) {\n            targetArr = this._additiveValue = [];\n        }\n        if (this.discrete) {\n            target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue;\n        }\n        else if (isArrayValueType(valType)) {\n            valType === VALUE_TYPE_1D_ARRAY\n                ? interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w)\n                : interpolate2DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n        }\n        else if (isGradientValueType(valType)) {\n            var val = frame[valueKey];\n            var nextVal_1 = nextFrame[valueKey];\n            var isLinearGradient_1 = valType === VALUE_TYPE_LINEAR_GRADIENT;\n            target[propName] = {\n                type: isLinearGradient_1 ? 'linear' : 'radial',\n                x: interpolateNumber(val.x, nextVal_1.x, w),\n                y: interpolateNumber(val.y, nextVal_1.y, w),\n                colorStops: map(val.colorStops, function (colorStop, idx) {\n                    var nextColorStop = nextVal_1.colorStops[idx];\n                    return {\n                        offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w),\n                        color: rgba2String(interpolate1DArray([], colorStop.color, nextColorStop.color, w))\n                    };\n                }),\n                global: nextVal_1.global\n            };\n            if (isLinearGradient_1) {\n                target[propName].x2 = interpolateNumber(val.x2, nextVal_1.x2, w);\n                target[propName].y2 = interpolateNumber(val.y2, nextVal_1.y2, w);\n            }\n            else {\n                target[propName].r = interpolateNumber(val.r, nextVal_1.r, w);\n            }\n        }\n        else if (isValueColor) {\n            interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n            if (!isAdditive) {\n                target[propName] = rgba2String(targetArr);\n            }\n        }\n        else {\n            var value = interpolateNumber(frame[valueKey], nextFrame[valueKey], w);\n            if (isAdditive) {\n                this._additiveValue = value;\n            }\n            else {\n                target[propName] = value;\n            }\n        }\n        if (isAdditive) {\n            this._addToTarget(target);\n        }\n    };\n    Track.prototype._addToTarget = function (target) {\n        var valType = this.valType;\n        var propName = this.propName;\n        var additiveValue = this._additiveValue;\n        if (valType === VALUE_TYPE_NUMBER) {\n            target[propName] = target[propName] + additiveValue;\n        }\n        else if (valType === VALUE_TYPE_COLOR) {\n            parse(target[propName], tmpRgba);\n            add1DArray(tmpRgba, tmpRgba, additiveValue, 1);\n            target[propName] = rgba2String(tmpRgba);\n        }\n        else if (valType === VALUE_TYPE_1D_ARRAY) {\n            add1DArray(target[propName], target[propName], additiveValue, 1);\n        }\n        else if (valType === VALUE_TYPE_2D_ARRAY) {\n            add2DArray(target[propName], target[propName], additiveValue, 1);\n        }\n    };\n    return Track;\n}());\nvar Animator = (function () {\n    function Animator(target, loop, allowDiscreteAnimation, additiveTo) {\n        this._tracks = {};\n        this._trackKeys = [];\n        this._maxTime = 0;\n        this._started = 0;\n        this._clip = null;\n        this._target = target;\n        this._loop = loop;\n        if (loop && additiveTo) {\n            logError('Can\\' use additive animation on looped animation.');\n            return;\n        }\n        this._additiveAnimators = additiveTo;\n        this._allowDiscrete = allowDiscreteAnimation;\n    }\n    Animator.prototype.getMaxTime = function () {\n        return this._maxTime;\n    };\n    Animator.prototype.getDelay = function () {\n        return this._delay;\n    };\n    Animator.prototype.getLoop = function () {\n        return this._loop;\n    };\n    Animator.prototype.getTarget = function () {\n        return this._target;\n    };\n    Animator.prototype.changeTarget = function (target) {\n        this._target = target;\n    };\n    Animator.prototype.when = function (time, props, easing) {\n        return this.whenWithKeys(time, props, keys(props), easing);\n    };\n    Animator.prototype.whenWithKeys = function (time, props, propNames, easing) {\n        var tracks = this._tracks;\n        for (var i = 0; i < propNames.length; i++) {\n            var propName = propNames[i];\n            var track = tracks[propName];\n            if (!track) {\n                track = tracks[propName] = new Track(propName);\n                var initialValue = void 0;\n                var additiveTrack = this._getAdditiveTrack(propName);\n                if (additiveTrack) {\n                    var addtiveTrackKfs = additiveTrack.keyframes;\n                    var lastFinalKf = addtiveTrackKfs[addtiveTrackKfs.length - 1];\n                    initialValue = lastFinalKf && lastFinalKf.value;\n                    if (additiveTrack.valType === VALUE_TYPE_COLOR && initialValue) {\n                        initialValue = rgba2String(initialValue);\n                    }\n                }\n                else {\n                    initialValue = this._target[propName];\n                }\n                if (initialValue == null) {\n                    continue;\n                }\n                if (time > 0) {\n                    track.addKeyframe(0, cloneValue(initialValue), easing);\n                }\n                this._trackKeys.push(propName);\n            }\n            track.addKeyframe(time, cloneValue(props[propName]), easing);\n        }\n        this._maxTime = Math.max(this._maxTime, time);\n        return this;\n    };\n    Animator.prototype.pause = function () {\n        this._clip.pause();\n        this._paused = true;\n    };\n    Animator.prototype.resume = function () {\n        this._clip.resume();\n        this._paused = false;\n    };\n    Animator.prototype.isPaused = function () {\n        return !!this._paused;\n    };\n    Animator.prototype.duration = function (duration) {\n        this._maxTime = duration;\n        this._force = true;\n        return this;\n    };\n    Animator.prototype._doneCallback = function () {\n        this._setTracksFinished();\n        this._clip = null;\n        var doneList = this._doneCbs;\n        if (doneList) {\n            var len = doneList.length;\n            for (var i = 0; i < len; i++) {\n                doneList[i].call(this);\n            }\n        }\n    };\n    Animator.prototype._abortedCallback = function () {\n        this._setTracksFinished();\n        var animation = this.animation;\n        var abortedList = this._abortedCbs;\n        if (animation) {\n            animation.removeClip(this._clip);\n        }\n        this._clip = null;\n        if (abortedList) {\n            for (var i = 0; i < abortedList.length; i++) {\n                abortedList[i].call(this);\n            }\n        }\n    };\n    Animator.prototype._setTracksFinished = function () {\n        var tracks = this._tracks;\n        var tracksKeys = this._trackKeys;\n        for (var i = 0; i < tracksKeys.length; i++) {\n            tracks[tracksKeys[i]].setFinished();\n        }\n    };\n    Animator.prototype._getAdditiveTrack = function (trackName) {\n        var additiveTrack;\n        var additiveAnimators = this._additiveAnimators;\n        if (additiveAnimators) {\n            for (var i = 0; i < additiveAnimators.length; i++) {\n                var track = additiveAnimators[i].getTrack(trackName);\n                if (track) {\n                    additiveTrack = track;\n                }\n            }\n        }\n        return additiveTrack;\n    };\n    Animator.prototype.start = function (easing) {\n        if (this._started > 0) {\n            return;\n        }\n        this._started = 1;\n        var self = this;\n        var tracks = [];\n        var maxTime = this._maxTime || 0;\n        for (var i = 0; i < this._trackKeys.length; i++) {\n            var propName = this._trackKeys[i];\n            var track = this._tracks[propName];\n            var additiveTrack = this._getAdditiveTrack(propName);\n            var kfs = track.keyframes;\n            var kfsNum = kfs.length;\n            track.prepare(maxTime, additiveTrack);\n            if (track.needsAnimate()) {\n                if (!this._allowDiscrete && track.discrete) {\n                    var lastKf = kfs[kfsNum - 1];\n                    if (lastKf) {\n                        self._target[track.propName] = lastKf.rawValue;\n                    }\n                    track.setFinished();\n                }\n                else {\n                    tracks.push(track);\n                }\n            }\n        }\n        if (tracks.length || this._force) {\n            var clip = new Clip({\n                life: maxTime,\n                loop: this._loop,\n                delay: this._delay || 0,\n                onframe: function (percent) {\n                    self._started = 2;\n                    var additiveAnimators = self._additiveAnimators;\n                    if (additiveAnimators) {\n                        var stillHasAdditiveAnimator = false;\n                        for (var i = 0; i < additiveAnimators.length; i++) {\n                            if (additiveAnimators[i]._clip) {\n                                stillHasAdditiveAnimator = true;\n                                break;\n                            }\n                        }\n                        if (!stillHasAdditiveAnimator) {\n                            self._additiveAnimators = null;\n                        }\n                    }\n                    for (var i = 0; i < tracks.length; i++) {\n                        tracks[i].step(self._target, percent);\n                    }\n                    var onframeList = self._onframeCbs;\n                    if (onframeList) {\n                        for (var i = 0; i < onframeList.length; i++) {\n                            onframeList[i](self._target, percent);\n                        }\n                    }\n                },\n                ondestroy: function () {\n                    self._doneCallback();\n                }\n            });\n            this._clip = clip;\n            if (this.animation) {\n                this.animation.addClip(clip);\n            }\n            if (easing) {\n                clip.setEasing(easing);\n            }\n        }\n        else {\n            this._doneCallback();\n        }\n        return this;\n    };\n    Animator.prototype.stop = function (forwardToLast) {\n        if (!this._clip) {\n            return;\n        }\n        var clip = this._clip;\n        if (forwardToLast) {\n            clip.onframe(1);\n        }\n        this._abortedCallback();\n    };\n    Animator.prototype.delay = function (time) {\n        this._delay = time;\n        return this;\n    };\n    Animator.prototype.during = function (cb) {\n        if (cb) {\n            if (!this._onframeCbs) {\n                this._onframeCbs = [];\n            }\n            this._onframeCbs.push(cb);\n        }\n        return this;\n    };\n    Animator.prototype.done = function (cb) {\n        if (cb) {\n            if (!this._doneCbs) {\n                this._doneCbs = [];\n            }\n            this._doneCbs.push(cb);\n        }\n        return this;\n    };\n    Animator.prototype.aborted = function (cb) {\n        if (cb) {\n            if (!this._abortedCbs) {\n                this._abortedCbs = [];\n            }\n            this._abortedCbs.push(cb);\n        }\n        return this;\n    };\n    Animator.prototype.getClip = function () {\n        return this._clip;\n    };\n    Animator.prototype.getTrack = function (propName) {\n        return this._tracks[propName];\n    };\n    Animator.prototype.getTracks = function () {\n        var _this = this;\n        return map(this._trackKeys, function (key) { return _this._tracks[key]; });\n    };\n    Animator.prototype.stopTracks = function (propNames, forwardToLast) {\n        if (!propNames.length || !this._clip) {\n            return true;\n        }\n        var tracks = this._tracks;\n        var tracksKeys = this._trackKeys;\n        for (var i = 0; i < propNames.length; i++) {\n            var track = tracks[propNames[i]];\n            if (track && !track.isFinished()) {\n                if (forwardToLast) {\n                    track.step(this._target, 1);\n                }\n                else if (this._started === 1) {\n                    track.step(this._target, 0);\n                }\n                track.setFinished();\n            }\n        }\n        var allAborted = true;\n        for (var i = 0; i < tracksKeys.length; i++) {\n            if (!tracks[tracksKeys[i]].isFinished()) {\n                allAborted = false;\n                break;\n            }\n        }\n        if (allAborted) {\n            this._abortedCallback();\n        }\n        return allAborted;\n    };\n    Animator.prototype.saveTo = function (target, trackKeys, firstOrLast) {\n        if (!target) {\n            return;\n        }\n        trackKeys = trackKeys || this._trackKeys;\n        for (var i = 0; i < trackKeys.length; i++) {\n            var propName = trackKeys[i];\n            var track = this._tracks[propName];\n            if (!track || track.isFinished()) {\n                continue;\n            }\n            var kfs = track.keyframes;\n            var kf = kfs[firstOrLast ? 0 : kfs.length - 1];\n            if (kf) {\n                target[propName] = cloneValue(kf.rawValue);\n            }\n        }\n    };\n    Animator.prototype.__changeFinalValue = function (finalProps, trackKeys) {\n        trackKeys = trackKeys || keys(finalProps);\n        for (var i = 0; i < trackKeys.length; i++) {\n            var propName = trackKeys[i];\n            var track = this._tracks[propName];\n            if (!track) {\n                continue;\n            }\n            var kfs = track.keyframes;\n            if (kfs.length > 1) {\n                var lastKf = kfs.pop();\n                track.addKeyframe(lastKf.time, finalProps[propName]);\n                track.prepare(this._maxTime, track.getAdditiveTrack());\n            }\n        }\n    };\n    return Animator;\n}());\n\nfunction getTime() {\n    return new Date().getTime();\n}\nvar Animation = (function (_super) {\n    __extends(Animation, _super);\n    function Animation(opts) {\n        var _this = _super.call(this) || this;\n        _this._running = false;\n        _this._time = 0;\n        _this._pausedTime = 0;\n        _this._pauseStart = 0;\n        _this._paused = false;\n        opts = opts || {};\n        _this.stage = opts.stage || {};\n        return _this;\n    }\n    Animation.prototype.addClip = function (clip) {\n        if (clip.animation) {\n            this.removeClip(clip);\n        }\n        if (!this._head) {\n            this._head = this._tail = clip;\n        }\n        else {\n            this._tail.next = clip;\n            clip.prev = this._tail;\n            clip.next = null;\n            this._tail = clip;\n        }\n        clip.animation = this;\n    };\n    Animation.prototype.addAnimator = function (animator) {\n        animator.animation = this;\n        var clip = animator.getClip();\n        if (clip) {\n            this.addClip(clip);\n        }\n    };\n    Animation.prototype.removeClip = function (clip) {\n        if (!clip.animation) {\n            return;\n        }\n        var prev = clip.prev;\n        var next = clip.next;\n        if (prev) {\n            prev.next = next;\n        }\n        else {\n            this._head = next;\n        }\n        if (next) {\n            next.prev = prev;\n        }\n        else {\n            this._tail = prev;\n        }\n        clip.next = clip.prev = clip.animation = null;\n    };\n    Animation.prototype.removeAnimator = function (animator) {\n        var clip = animator.getClip();\n        if (clip) {\n            this.removeClip(clip);\n        }\n        animator.animation = null;\n    };\n    Animation.prototype.update = function (notTriggerFrameAndStageUpdate) {\n        var time = getTime() - this._pausedTime;\n        var delta = time - this._time;\n        var clip = this._head;\n        while (clip) {\n            var nextClip = clip.next;\n            var finished = clip.step(time, delta);\n            if (finished) {\n                clip.ondestroy();\n                this.removeClip(clip);\n                clip = nextClip;\n            }\n            else {\n                clip = nextClip;\n            }\n        }\n        this._time = time;\n        if (!notTriggerFrameAndStageUpdate) {\n            this.trigger('frame', delta);\n            this.stage.update && this.stage.update();\n        }\n    };\n    Animation.prototype._startLoop = function () {\n        var self = this;\n        this._running = true;\n        function step() {\n            if (self._running) {\n                requestAnimationFrame$1(step);\n                !self._paused && self.update();\n            }\n        }\n        requestAnimationFrame$1(step);\n    };\n    Animation.prototype.start = function () {\n        if (this._running) {\n            return;\n        }\n        this._time = getTime();\n        this._pausedTime = 0;\n        this._startLoop();\n    };\n    Animation.prototype.stop = function () {\n        this._running = false;\n    };\n    Animation.prototype.pause = function () {\n        if (!this._paused) {\n            this._pauseStart = getTime();\n            this._paused = true;\n        }\n    };\n    Animation.prototype.resume = function () {\n        if (this._paused) {\n            this._pausedTime += getTime() - this._pauseStart;\n            this._paused = false;\n        }\n    };\n    Animation.prototype.clear = function () {\n        var clip = this._head;\n        while (clip) {\n            var nextClip = clip.next;\n            clip.prev = clip.next = clip.animation = null;\n            clip = nextClip;\n        }\n        this._head = this._tail = null;\n    };\n    Animation.prototype.isFinished = function () {\n        return this._head == null;\n    };\n    Animation.prototype.animate = function (target, options) {\n        options = options || {};\n        this.start();\n        var animator = new Animator(target, options.loop);\n        this.addAnimator(animator);\n        return animator;\n    };\n    return Animation;\n}(Eventful));\n\nvar TOUCH_CLICK_DELAY = 300;\nvar globalEventSupported = env.domSupported;\nvar localNativeListenerNames = (function () {\n    var mouseHandlerNames = [\n        'click', 'dblclick', 'mousewheel', 'wheel', 'mouseout',\n        'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n    ];\n    var touchHandlerNames = [\n        'touchstart', 'touchend', 'touchmove'\n    ];\n    var pointerEventNameMap = {\n        pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1\n    };\n    var pointerHandlerNames = map(mouseHandlerNames, function (name) {\n        var nm = name.replace('mouse', 'pointer');\n        return pointerEventNameMap.hasOwnProperty(nm) ? nm : name;\n    });\n    return {\n        mouse: mouseHandlerNames,\n        touch: touchHandlerNames,\n        pointer: pointerHandlerNames\n    };\n})();\nvar globalNativeListenerNames = {\n    mouse: ['mousemove', 'mouseup'],\n    pointer: ['pointermove', 'pointerup']\n};\nvar wheelEventSupported = false;\nfunction isPointerFromTouch(event) {\n    var pointerType = event.pointerType;\n    return pointerType === 'pen' || pointerType === 'touch';\n}\nfunction setTouchTimer(scope) {\n    scope.touching = true;\n    if (scope.touchTimer != null) {\n        clearTimeout(scope.touchTimer);\n        scope.touchTimer = null;\n    }\n    scope.touchTimer = setTimeout(function () {\n        scope.touching = false;\n        scope.touchTimer = null;\n    }, 700);\n}\nfunction markTouch(event) {\n    event && (event.zrByTouch = true);\n}\nfunction normalizeGlobalEvent(instance, event) {\n    return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true);\n}\nfunction isLocalEl(instance, el) {\n    var elTmp = el;\n    var isLocal = false;\n    while (elTmp && elTmp.nodeType !== 9\n        && !(isLocal = elTmp.domBelongToZr\n            || (elTmp !== el && elTmp === instance.painterRoot))) {\n        elTmp = elTmp.parentNode;\n    }\n    return isLocal;\n}\nvar FakeGlobalEvent = (function () {\n    function FakeGlobalEvent(instance, event) {\n        this.stopPropagation = noop;\n        this.stopImmediatePropagation = noop;\n        this.preventDefault = noop;\n        this.type = event.type;\n        this.target = this.currentTarget = instance.dom;\n        this.pointerType = event.pointerType;\n        this.clientX = event.clientX;\n        this.clientY = event.clientY;\n    }\n    return FakeGlobalEvent;\n}());\nvar localDOMHandlers = {\n    mousedown: function (event) {\n        event = normalizeEvent(this.dom, event);\n        this.__mayPointerCapture = [event.zrX, event.zrY];\n        this.trigger('mousedown', event);\n    },\n    mousemove: function (event) {\n        event = normalizeEvent(this.dom, event);\n        var downPoint = this.__mayPointerCapture;\n        if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) {\n            this.__togglePointerCapture(true);\n        }\n        this.trigger('mousemove', event);\n    },\n    mouseup: function (event) {\n        event = normalizeEvent(this.dom, event);\n        this.__togglePointerCapture(false);\n        this.trigger('mouseup', event);\n    },\n    mouseout: function (event) {\n        event = normalizeEvent(this.dom, event);\n        var element = event.toElement || event.relatedTarget;\n        if (!isLocalEl(this, element)) {\n            if (this.__pointerCapturing) {\n                event.zrEventControl = 'no_globalout';\n            }\n            this.trigger('mouseout', event);\n        }\n    },\n    wheel: function (event) {\n        wheelEventSupported = true;\n        event = normalizeEvent(this.dom, event);\n        this.trigger('mousewheel', event);\n    },\n    mousewheel: function (event) {\n        if (wheelEventSupported) {\n            return;\n        }\n        event = normalizeEvent(this.dom, event);\n        this.trigger('mousewheel', event);\n    },\n    touchstart: function (event) {\n        event = normalizeEvent(this.dom, event);\n        markTouch(event);\n        this.__lastTouchMoment = new Date();\n        this.handler.processGesture(event, 'start');\n        localDOMHandlers.mousemove.call(this, event);\n        localDOMHandlers.mousedown.call(this, event);\n    },\n    touchmove: function (event) {\n        event = normalizeEvent(this.dom, event);\n        markTouch(event);\n        this.handler.processGesture(event, 'change');\n        localDOMHandlers.mousemove.call(this, event);\n    },\n    touchend: function (event) {\n        event = normalizeEvent(this.dom, event);\n        markTouch(event);\n        this.handler.processGesture(event, 'end');\n        localDOMHandlers.mouseup.call(this, event);\n        if (+new Date() - (+this.__lastTouchMoment) < TOUCH_CLICK_DELAY) {\n            localDOMHandlers.click.call(this, event);\n        }\n    },\n    pointerdown: function (event) {\n        localDOMHandlers.mousedown.call(this, event);\n    },\n    pointermove: function (event) {\n        if (!isPointerFromTouch(event)) {\n            localDOMHandlers.mousemove.call(this, event);\n        }\n    },\n    pointerup: function (event) {\n        localDOMHandlers.mouseup.call(this, event);\n    },\n    pointerout: function (event) {\n        if (!isPointerFromTouch(event)) {\n            localDOMHandlers.mouseout.call(this, event);\n        }\n    }\n};\neach(['click', 'dblclick', 'contextmenu'], function (name) {\n    localDOMHandlers[name] = function (event) {\n        event = normalizeEvent(this.dom, event);\n        this.trigger(name, event);\n    };\n});\nvar globalDOMHandlers = {\n    pointermove: function (event) {\n        if (!isPointerFromTouch(event)) {\n            globalDOMHandlers.mousemove.call(this, event);\n        }\n    },\n    pointerup: function (event) {\n        globalDOMHandlers.mouseup.call(this, event);\n    },\n    mousemove: function (event) {\n        this.trigger('mousemove', event);\n    },\n    mouseup: function (event) {\n        var pointerCaptureReleasing = this.__pointerCapturing;\n        this.__togglePointerCapture(false);\n        this.trigger('mouseup', event);\n        if (pointerCaptureReleasing) {\n            event.zrEventControl = 'only_globalout';\n            this.trigger('mouseout', event);\n        }\n    }\n};\nfunction mountLocalDOMEventListeners(instance, scope) {\n    var domHandlers = scope.domHandlers;\n    if (env.pointerEventsSupported) {\n        each(localNativeListenerNames.pointer, function (nativeEventName) {\n            mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                domHandlers[nativeEventName].call(instance, event);\n            });\n        });\n    }\n    else {\n        if (env.touchEventsSupported) {\n            each(localNativeListenerNames.touch, function (nativeEventName) {\n                mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                    domHandlers[nativeEventName].call(instance, event);\n                    setTouchTimer(scope);\n                });\n            });\n        }\n        each(localNativeListenerNames.mouse, function (nativeEventName) {\n            mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                event = getNativeEvent(event);\n                if (!scope.touching) {\n                    domHandlers[nativeEventName].call(instance, event);\n                }\n            });\n        });\n    }\n}\nfunction mountGlobalDOMEventListeners(instance, scope) {\n    if (env.pointerEventsSupported) {\n        each(globalNativeListenerNames.pointer, mount);\n    }\n    else if (!env.touchEventsSupported) {\n        each(globalNativeListenerNames.mouse, mount);\n    }\n    function mount(nativeEventName) {\n        function nativeEventListener(event) {\n            event = getNativeEvent(event);\n            if (!isLocalEl(instance, event.target)) {\n                event = normalizeGlobalEvent(instance, event);\n                scope.domHandlers[nativeEventName].call(instance, event);\n            }\n        }\n        mountSingleDOMEventListener(scope, nativeEventName, nativeEventListener, { capture: true });\n    }\n}\nfunction mountSingleDOMEventListener(scope, nativeEventName, listener, opt) {\n    scope.mounted[nativeEventName] = listener;\n    scope.listenerOpts[nativeEventName] = opt;\n    addEventListener(scope.domTarget, nativeEventName, listener, opt);\n}\nfunction unmountDOMEventListeners(scope) {\n    var mounted = scope.mounted;\n    for (var nativeEventName in mounted) {\n        if (mounted.hasOwnProperty(nativeEventName)) {\n            removeEventListener(scope.domTarget, nativeEventName, mounted[nativeEventName], scope.listenerOpts[nativeEventName]);\n        }\n    }\n    scope.mounted = {};\n}\nvar DOMHandlerScope = (function () {\n    function DOMHandlerScope(domTarget, domHandlers) {\n        this.mounted = {};\n        this.listenerOpts = {};\n        this.touching = false;\n        this.domTarget = domTarget;\n        this.domHandlers = domHandlers;\n    }\n    return DOMHandlerScope;\n}());\nvar HandlerDomProxy = (function (_super) {\n    __extends(HandlerDomProxy, _super);\n    function HandlerDomProxy(dom, painterRoot) {\n        var _this = _super.call(this) || this;\n        _this.__pointerCapturing = false;\n        _this.dom = dom;\n        _this.painterRoot = painterRoot;\n        _this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers);\n        if (globalEventSupported) {\n            _this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers);\n        }\n        mountLocalDOMEventListeners(_this, _this._localHandlerScope);\n        return _this;\n    }\n    HandlerDomProxy.prototype.dispose = function () {\n        unmountDOMEventListeners(this._localHandlerScope);\n        if (globalEventSupported) {\n            unmountDOMEventListeners(this._globalHandlerScope);\n        }\n    };\n    HandlerDomProxy.prototype.setCursor = function (cursorStyle) {\n        this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');\n    };\n    HandlerDomProxy.prototype.__togglePointerCapture = function (isPointerCapturing) {\n        this.__mayPointerCapture = null;\n        if (globalEventSupported\n            && ((+this.__pointerCapturing) ^ (+isPointerCapturing))) {\n            this.__pointerCapturing = isPointerCapturing;\n            var globalHandlerScope = this._globalHandlerScope;\n            isPointerCapturing\n                ? mountGlobalDOMEventListeners(this, globalHandlerScope)\n                : unmountDOMEventListeners(globalHandlerScope);\n        }\n    };\n    return HandlerDomProxy;\n}(Eventful));\n\nvar dpr = 1;\nif (env.hasGlobalWindow) {\n    dpr = Math.max(window.devicePixelRatio\n        || (window.screen && window.screen.deviceXDPI / window.screen.logicalXDPI)\n        || 1, 1);\n}\nvar devicePixelRatio = dpr;\nvar DARK_MODE_THRESHOLD = 0.4;\nvar DARK_LABEL_COLOR = '#333';\nvar LIGHT_LABEL_COLOR = '#ccc';\nvar LIGHTER_LABEL_COLOR = '#eee';\n\nvar mIdentity = identity;\nvar EPSILON$2 = 5e-5;\nfunction isNotAroundZero$1(val) {\n    return val > EPSILON$2 || val < -EPSILON$2;\n}\nvar scaleTmp = [];\nvar tmpTransform = [];\nvar originTransform = create$1();\nvar abs = Math.abs;\nvar Transformable = (function () {\n    function Transformable() {\n    }\n    Transformable.prototype.getLocalTransform = function (m) {\n        return Transformable.getLocalTransform(this, m);\n    };\n    Transformable.prototype.setPosition = function (arr) {\n        this.x = arr[0];\n        this.y = arr[1];\n    };\n    Transformable.prototype.setScale = function (arr) {\n        this.scaleX = arr[0];\n        this.scaleY = arr[1];\n    };\n    Transformable.prototype.setSkew = function (arr) {\n        this.skewX = arr[0];\n        this.skewY = arr[1];\n    };\n    Transformable.prototype.setOrigin = function (arr) {\n        this.originX = arr[0];\n        this.originY = arr[1];\n    };\n    Transformable.prototype.needLocalTransform = function () {\n        return isNotAroundZero$1(this.rotation)\n            || isNotAroundZero$1(this.x)\n            || isNotAroundZero$1(this.y)\n            || isNotAroundZero$1(this.scaleX - 1)\n            || isNotAroundZero$1(this.scaleY - 1)\n            || isNotAroundZero$1(this.skewX)\n            || isNotAroundZero$1(this.skewY);\n    };\n    Transformable.prototype.updateTransform = function () {\n        var parentTransform = this.parent && this.parent.transform;\n        var needLocalTransform = this.needLocalTransform();\n        var m = this.transform;\n        if (!(needLocalTransform || parentTransform)) {\n            m && mIdentity(m);\n            return;\n        }\n        m = m || create$1();\n        if (needLocalTransform) {\n            this.getLocalTransform(m);\n        }\n        else {\n            mIdentity(m);\n        }\n        if (parentTransform) {\n            if (needLocalTransform) {\n                mul$1(m, parentTransform, m);\n            }\n            else {\n                copy$1(m, parentTransform);\n            }\n        }\n        this.transform = m;\n        this._resolveGlobalScaleRatio(m);\n    };\n    Transformable.prototype._resolveGlobalScaleRatio = function (m) {\n        var globalScaleRatio = this.globalScaleRatio;\n        if (globalScaleRatio != null && globalScaleRatio !== 1) {\n            this.getGlobalScale(scaleTmp);\n            var relX = scaleTmp[0] < 0 ? -1 : 1;\n            var relY = scaleTmp[1] < 0 ? -1 : 1;\n            var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;\n            var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;\n            m[0] *= sx;\n            m[1] *= sx;\n            m[2] *= sy;\n            m[3] *= sy;\n        }\n        this.invTransform = this.invTransform || create$1();\n        invert(this.invTransform, m);\n    };\n    Transformable.prototype.getComputedTransform = function () {\n        var transformNode = this;\n        var ancestors = [];\n        while (transformNode) {\n            ancestors.push(transformNode);\n            transformNode = transformNode.parent;\n        }\n        while (transformNode = ancestors.pop()) {\n            transformNode.updateTransform();\n        }\n        return this.transform;\n    };\n    Transformable.prototype.setLocalTransform = function (m) {\n        if (!m) {\n            return;\n        }\n        var sx = m[0] * m[0] + m[1] * m[1];\n        var sy = m[2] * m[2] + m[3] * m[3];\n        var rotation = Math.atan2(m[1], m[0]);\n        var shearX = Math.PI / 2 + rotation - Math.atan2(m[3], m[2]);\n        sy = Math.sqrt(sy) * Math.cos(shearX);\n        sx = Math.sqrt(sx);\n        this.skewX = shearX;\n        this.skewY = 0;\n        this.rotation = -rotation;\n        this.x = +m[4];\n        this.y = +m[5];\n        this.scaleX = sx;\n        this.scaleY = sy;\n        this.originX = 0;\n        this.originY = 0;\n    };\n    Transformable.prototype.decomposeTransform = function () {\n        if (!this.transform) {\n            return;\n        }\n        var parent = this.parent;\n        var m = this.transform;\n        if (parent && parent.transform) {\n            mul$1(tmpTransform, parent.invTransform, m);\n            m = tmpTransform;\n        }\n        var ox = this.originX;\n        var oy = this.originY;\n        if (ox || oy) {\n            originTransform[4] = ox;\n            originTransform[5] = oy;\n            mul$1(tmpTransform, m, originTransform);\n            tmpTransform[4] -= ox;\n            tmpTransform[5] -= oy;\n            m = tmpTransform;\n        }\n        this.setLocalTransform(m);\n    };\n    Transformable.prototype.getGlobalScale = function (out) {\n        var m = this.transform;\n        out = out || [];\n        if (!m) {\n            out[0] = 1;\n            out[1] = 1;\n            return out;\n        }\n        out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);\n        out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);\n        if (m[0] < 0) {\n            out[0] = -out[0];\n        }\n        if (m[3] < 0) {\n            out[1] = -out[1];\n        }\n        return out;\n    };\n    Transformable.prototype.transformCoordToLocal = function (x, y) {\n        var v2 = [x, y];\n        var invTransform = this.invTransform;\n        if (invTransform) {\n            applyTransform(v2, v2, invTransform);\n        }\n        return v2;\n    };\n    Transformable.prototype.transformCoordToGlobal = function (x, y) {\n        var v2 = [x, y];\n        var transform = this.transform;\n        if (transform) {\n            applyTransform(v2, v2, transform);\n        }\n        return v2;\n    };\n    Transformable.prototype.getLineScale = function () {\n        var m = this.transform;\n        return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10\n            ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))\n            : 1;\n    };\n    Transformable.prototype.copyTransform = function (source) {\n        copyTransform(this, source);\n    };\n    Transformable.getLocalTransform = function (target, m) {\n        m = m || [];\n        var ox = target.originX || 0;\n        var oy = target.originY || 0;\n        var sx = target.scaleX;\n        var sy = target.scaleY;\n        var ax = target.anchorX;\n        var ay = target.anchorY;\n        var rotation = target.rotation || 0;\n        var x = target.x;\n        var y = target.y;\n        var skewX = target.skewX ? Math.tan(target.skewX) : 0;\n        var skewY = target.skewY ? Math.tan(-target.skewY) : 0;\n        if (ox || oy || ax || ay) {\n            var dx = ox + ax;\n            var dy = oy + ay;\n            m[4] = -dx * sx - skewX * dy * sy;\n            m[5] = -dy * sy - skewY * dx * sx;\n        }\n        else {\n            m[4] = m[5] = 0;\n        }\n        m[0] = sx;\n        m[3] = sy;\n        m[1] = skewY * sx;\n        m[2] = skewX * sy;\n        rotation && rotate(m, m, rotation);\n        m[4] += ox + x;\n        m[5] += oy + y;\n        return m;\n    };\n    Transformable.initDefaultProps = (function () {\n        var proto = Transformable.prototype;\n        proto.scaleX =\n            proto.scaleY =\n                proto.globalScaleRatio = 1;\n        proto.x =\n            proto.y =\n                proto.originX =\n                    proto.originY =\n                        proto.skewX =\n                            proto.skewY =\n                                proto.rotation =\n                                    proto.anchorX =\n                                        proto.anchorY = 0;\n    })();\n    return Transformable;\n}());\nvar TRANSFORMABLE_PROPS = [\n    'x', 'y', 'originX', 'originY', 'anchorX', 'anchorY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY'\n];\nfunction copyTransform(target, source) {\n    for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {\n        var propName = TRANSFORMABLE_PROPS[i];\n        target[propName] = source[propName];\n    }\n}\n\nvar textWidthCache = {};\nfunction getWidth(text, font) {\n    font = font || DEFAULT_FONT;\n    var cacheOfFont = textWidthCache[font];\n    if (!cacheOfFont) {\n        cacheOfFont = textWidthCache[font] = new LRU(500);\n    }\n    var width = cacheOfFont.get(text);\n    if (width == null) {\n        width = platformApi.measureText(text, font).width;\n        cacheOfFont.put(text, width);\n    }\n    return width;\n}\nfunction innerGetBoundingRect(text, font, textAlign, textBaseline) {\n    var width = getWidth(text, font);\n    var height = getLineHeight(font);\n    var x = adjustTextX(0, width, textAlign);\n    var y = adjustTextY$1(0, height, textBaseline);\n    var rect = new BoundingRect(x, y, width, height);\n    return rect;\n}\nfunction getBoundingRect(text, font, textAlign, textBaseline) {\n    var textLines = ((text || '') + '').split('\\n');\n    var len = textLines.length;\n    if (len === 1) {\n        return innerGetBoundingRect(textLines[0], font, textAlign, textBaseline);\n    }\n    else {\n        var uniondRect = new BoundingRect(0, 0, 0, 0);\n        for (var i = 0; i < textLines.length; i++) {\n            var rect = innerGetBoundingRect(textLines[i], font, textAlign, textBaseline);\n            i === 0 ? uniondRect.copy(rect) : uniondRect.union(rect);\n        }\n        return uniondRect;\n    }\n}\nfunction adjustTextX(x, width, textAlign) {\n    if (textAlign === 'right') {\n        x -= width;\n    }\n    else if (textAlign === 'center') {\n        x -= width / 2;\n    }\n    return x;\n}\nfunction adjustTextY$1(y, height, verticalAlign) {\n    if (verticalAlign === 'middle') {\n        y -= height / 2;\n    }\n    else if (verticalAlign === 'bottom') {\n        y -= height;\n    }\n    return y;\n}\nfunction getLineHeight(font) {\n    return getWidth('国', font);\n}\nfunction parsePercent(value, maxValue) {\n    if (typeof value === 'string') {\n        if (value.lastIndexOf('%') >= 0) {\n            return parseFloat(value) / 100 * maxValue;\n        }\n        return parseFloat(value);\n    }\n    return value;\n}\nfunction calculateTextPosition(out, opts, rect) {\n    var textPosition = opts.position || 'inside';\n    var distance = opts.distance != null ? opts.distance : 5;\n    var height = rect.height;\n    var width = rect.width;\n    var halfHeight = height / 2;\n    var x = rect.x;\n    var y = rect.y;\n    var textAlign = 'left';\n    var textVerticalAlign = 'top';\n    if (textPosition instanceof Array) {\n        x += parsePercent(textPosition[0], rect.width);\n        y += parsePercent(textPosition[1], rect.height);\n        textAlign = null;\n        textVerticalAlign = null;\n    }\n    else {\n        switch (textPosition) {\n            case 'left':\n                x -= distance;\n                y += halfHeight;\n                textAlign = 'right';\n                textVerticalAlign = 'middle';\n                break;\n            case 'right':\n                x += distance + width;\n                y += halfHeight;\n                textVerticalAlign = 'middle';\n                break;\n            case 'top':\n                x += width / 2;\n                y -= distance;\n                textAlign = 'center';\n                textVerticalAlign = 'bottom';\n                break;\n            case 'bottom':\n                x += width / 2;\n                y += height + distance;\n                textAlign = 'center';\n                break;\n            case 'inside':\n                x += width / 2;\n                y += halfHeight;\n                textAlign = 'center';\n                textVerticalAlign = 'middle';\n                break;\n            case 'insideLeft':\n                x += distance;\n                y += halfHeight;\n                textVerticalAlign = 'middle';\n                break;\n            case 'insideRight':\n                x += width - distance;\n                y += halfHeight;\n                textAlign = 'right';\n                textVerticalAlign = 'middle';\n                break;\n            case 'insideTop':\n                x += width / 2;\n                y += distance;\n                textAlign = 'center';\n                break;\n            case 'insideBottom':\n                x += width / 2;\n                y += height - distance;\n                textAlign = 'center';\n                textVerticalAlign = 'bottom';\n                break;\n            case 'insideTopLeft':\n                x += distance;\n                y += distance;\n                break;\n            case 'insideTopRight':\n                x += width - distance;\n                y += distance;\n                textAlign = 'right';\n                break;\n            case 'insideBottomLeft':\n                x += distance;\n                y += height - distance;\n                textVerticalAlign = 'bottom';\n                break;\n            case 'insideBottomRight':\n                x += width - distance;\n                y += height - distance;\n                textAlign = 'right';\n                textVerticalAlign = 'bottom';\n                break;\n        }\n    }\n    out = out || {};\n    out.x = x;\n    out.y = y;\n    out.align = textAlign;\n    out.verticalAlign = textVerticalAlign;\n    return out;\n}\n\nvar PRESERVED_NORMAL_STATE = '__zr_normal__';\nvar PRIMARY_STATES_KEYS = TRANSFORMABLE_PROPS.concat(['ignore']);\nvar DEFAULT_ANIMATABLE_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {\n    obj[key] = true;\n    return obj;\n}, { ignore: false });\nvar tmpTextPosCalcRes = {};\nvar tmpBoundingRect = new BoundingRect(0, 0, 0, 0);\nvar Element = (function () {\n    function Element(props) {\n        this.id = guid();\n        this.animators = [];\n        this.currentStates = [];\n        this.states = {};\n        this._init(props);\n    }\n    Element.prototype._init = function (props) {\n        this.attr(props);\n    };\n    Element.prototype.drift = function (dx, dy, e) {\n        switch (this.draggable) {\n            case 'horizontal':\n                dy = 0;\n                break;\n            case 'vertical':\n                dx = 0;\n                break;\n        }\n        var m = this.transform;\n        if (!m) {\n            m = this.transform = [1, 0, 0, 1, 0, 0];\n        }\n        m[4] += dx;\n        m[5] += dy;\n        this.decomposeTransform();\n        this.markRedraw();\n    };\n    Element.prototype.beforeUpdate = function () { };\n    Element.prototype.afterUpdate = function () { };\n    Element.prototype.update = function () {\n        this.updateTransform();\n        if (this.__dirty) {\n            this.updateInnerText();\n        }\n    };\n    Element.prototype.updateInnerText = function (forceUpdate) {\n        var textEl = this._textContent;\n        if (textEl && (!textEl.ignore || forceUpdate)) {\n            if (!this.textConfig) {\n                this.textConfig = {};\n            }\n            var textConfig = this.textConfig;\n            var isLocal = textConfig.local;\n            var innerTransformable = textEl.innerTransformable;\n            var textAlign = void 0;\n            var textVerticalAlign = void 0;\n            var textStyleChanged = false;\n            innerTransformable.parent = isLocal ? this : null;\n            var innerOrigin = false;\n            innerTransformable.copyTransform(textEl);\n            if (textConfig.position != null) {\n                var layoutRect = tmpBoundingRect;\n                if (textConfig.layoutRect) {\n                    layoutRect.copy(textConfig.layoutRect);\n                }\n                else {\n                    layoutRect.copy(this.getBoundingRect());\n                }\n                if (!isLocal) {\n                    layoutRect.applyTransform(this.transform);\n                }\n                if (this.calculateTextPosition) {\n                    this.calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                }\n                else {\n                    calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                }\n                innerTransformable.x = tmpTextPosCalcRes.x;\n                innerTransformable.y = tmpTextPosCalcRes.y;\n                textAlign = tmpTextPosCalcRes.align;\n                textVerticalAlign = tmpTextPosCalcRes.verticalAlign;\n                var textOrigin = textConfig.origin;\n                if (textOrigin && textConfig.rotation != null) {\n                    var relOriginX = void 0;\n                    var relOriginY = void 0;\n                    if (textOrigin === 'center') {\n                        relOriginX = layoutRect.width * 0.5;\n                        relOriginY = layoutRect.height * 0.5;\n                    }\n                    else {\n                        relOriginX = parsePercent(textOrigin[0], layoutRect.width);\n                        relOriginY = parsePercent(textOrigin[1], layoutRect.height);\n                    }\n                    innerOrigin = true;\n                    innerTransformable.originX = -innerTransformable.x + relOriginX + (isLocal ? 0 : layoutRect.x);\n                    innerTransformable.originY = -innerTransformable.y + relOriginY + (isLocal ? 0 : layoutRect.y);\n                }\n            }\n            if (textConfig.rotation != null) {\n                innerTransformable.rotation = textConfig.rotation;\n            }\n            var textOffset = textConfig.offset;\n            if (textOffset) {\n                innerTransformable.x += textOffset[0];\n                innerTransformable.y += textOffset[1];\n                if (!innerOrigin) {\n                    innerTransformable.originX = -textOffset[0];\n                    innerTransformable.originY = -textOffset[1];\n                }\n            }\n            var isInside = textConfig.inside == null\n                ? (typeof textConfig.position === 'string' && textConfig.position.indexOf('inside') >= 0)\n                : textConfig.inside;\n            var innerTextDefaultStyle = this._innerTextDefaultStyle || (this._innerTextDefaultStyle = {});\n            var textFill = void 0;\n            var textStroke = void 0;\n            var autoStroke = void 0;\n            if (isInside && this.canBeInsideText()) {\n                textFill = textConfig.insideFill;\n                textStroke = textConfig.insideStroke;\n                if (textFill == null || textFill === 'auto') {\n                    textFill = this.getInsideTextFill();\n                }\n                if (textStroke == null || textStroke === 'auto') {\n                    textStroke = this.getInsideTextStroke(textFill);\n                    autoStroke = true;\n                }\n            }\n            else {\n                textFill = textConfig.outsideFill;\n                textStroke = textConfig.outsideStroke;\n                if (textFill == null || textFill === 'auto') {\n                    textFill = this.getOutsideFill();\n                }\n                if (textStroke == null || textStroke === 'auto') {\n                    textStroke = this.getOutsideStroke(textFill);\n                    autoStroke = true;\n                }\n            }\n            textFill = textFill || '#000';\n            if (textFill !== innerTextDefaultStyle.fill\n                || textStroke !== innerTextDefaultStyle.stroke\n                || autoStroke !== innerTextDefaultStyle.autoStroke\n                || textAlign !== innerTextDefaultStyle.align\n                || textVerticalAlign !== innerTextDefaultStyle.verticalAlign) {\n                textStyleChanged = true;\n                innerTextDefaultStyle.fill = textFill;\n                innerTextDefaultStyle.stroke = textStroke;\n                innerTextDefaultStyle.autoStroke = autoStroke;\n                innerTextDefaultStyle.align = textAlign;\n                innerTextDefaultStyle.verticalAlign = textVerticalAlign;\n                textEl.setDefaultTextStyle(innerTextDefaultStyle);\n            }\n            textEl.__dirty |= REDRAW_BIT;\n            if (textStyleChanged) {\n                textEl.dirtyStyle(true);\n            }\n        }\n    };\n    Element.prototype.canBeInsideText = function () {\n        return true;\n    };\n    Element.prototype.getInsideTextFill = function () {\n        return '#fff';\n    };\n    Element.prototype.getInsideTextStroke = function (textFill) {\n        return '#000';\n    };\n    Element.prototype.getOutsideFill = function () {\n        return this.__zr && this.__zr.isDarkMode() ? LIGHT_LABEL_COLOR : DARK_LABEL_COLOR;\n    };\n    Element.prototype.getOutsideStroke = function (textFill) {\n        var backgroundColor = this.__zr && this.__zr.getBackgroundColor();\n        var colorArr = typeof backgroundColor === 'string' && parse(backgroundColor);\n        if (!colorArr) {\n            colorArr = [255, 255, 255, 1];\n        }\n        var alpha = colorArr[3];\n        var isDark = this.__zr.isDarkMode();\n        for (var i = 0; i < 3; i++) {\n            colorArr[i] = colorArr[i] * alpha + (isDark ? 0 : 255) * (1 - alpha);\n        }\n        colorArr[3] = 1;\n        return stringify(colorArr, 'rgba');\n    };\n    Element.prototype.traverse = function (cb, context) { };\n    Element.prototype.attrKV = function (key, value) {\n        if (key === 'textConfig') {\n            this.setTextConfig(value);\n        }\n        else if (key === 'textContent') {\n            this.setTextContent(value);\n        }\n        else if (key === 'clipPath') {\n            this.setClipPath(value);\n        }\n        else if (key === 'extra') {\n            this.extra = this.extra || {};\n            extend(this.extra, value);\n        }\n        else {\n            this[key] = value;\n        }\n    };\n    Element.prototype.hide = function () {\n        this.ignore = true;\n        this.markRedraw();\n    };\n    Element.prototype.show = function () {\n        this.ignore = false;\n        this.markRedraw();\n    };\n    Element.prototype.attr = function (keyOrObj, value) {\n        if (typeof keyOrObj === 'string') {\n            this.attrKV(keyOrObj, value);\n        }\n        else if (isObject(keyOrObj)) {\n            var obj = keyOrObj;\n            var keysArr = keys(obj);\n            for (var i = 0; i < keysArr.length; i++) {\n                var key = keysArr[i];\n                this.attrKV(key, keyOrObj[key]);\n            }\n        }\n        this.markRedraw();\n        return this;\n    };\n    Element.prototype.saveCurrentToNormalState = function (toState) {\n        this._innerSaveToNormal(toState);\n        var normalState = this._normalState;\n        for (var i = 0; i < this.animators.length; i++) {\n            var animator = this.animators[i];\n            var fromStateTransition = animator.__fromStateTransition;\n            if (animator.getLoop() || fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) {\n                continue;\n            }\n            var targetName = animator.targetName;\n            var target = targetName\n                ? normalState[targetName] : normalState;\n            animator.saveTo(target);\n        }\n    };\n    Element.prototype._innerSaveToNormal = function (toState) {\n        var normalState = this._normalState;\n        if (!normalState) {\n            normalState = this._normalState = {};\n        }\n        if (toState.textConfig && !normalState.textConfig) {\n            normalState.textConfig = this.textConfig;\n        }\n        this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS);\n    };\n    Element.prototype._savePrimaryToNormal = function (toState, normalState, primaryKeys) {\n        for (var i = 0; i < primaryKeys.length; i++) {\n            var key = primaryKeys[i];\n            if (toState[key] != null && !(key in normalState)) {\n                normalState[key] = this[key];\n            }\n        }\n    };\n    Element.prototype.hasState = function () {\n        return this.currentStates.length > 0;\n    };\n    Element.prototype.getState = function (name) {\n        return this.states[name];\n    };\n    Element.prototype.ensureState = function (name) {\n        var states = this.states;\n        if (!states[name]) {\n            states[name] = {};\n        }\n        return states[name];\n    };\n    Element.prototype.clearStates = function (noAnimation) {\n        this.useState(PRESERVED_NORMAL_STATE, false, noAnimation);\n    };\n    Element.prototype.useState = function (stateName, keepCurrentStates, noAnimation, forceUseHoverLayer) {\n        var toNormalState = stateName === PRESERVED_NORMAL_STATE;\n        var hasStates = this.hasState();\n        if (!hasStates && toNormalState) {\n            return;\n        }\n        var currentStates = this.currentStates;\n        var animationCfg = this.stateTransition;\n        if (indexOf(currentStates, stateName) >= 0 && (keepCurrentStates || currentStates.length === 1)) {\n            return;\n        }\n        var state;\n        if (this.stateProxy && !toNormalState) {\n            state = this.stateProxy(stateName);\n        }\n        if (!state) {\n            state = (this.states && this.states[stateName]);\n        }\n        if (!state && !toNormalState) {\n            logError(\"State \" + stateName + \" not exists.\");\n            return;\n        }\n        if (!toNormalState) {\n            this.saveCurrentToNormalState(state);\n        }\n        var useHoverLayer = !!((state && state.hoverLayer) || forceUseHoverLayer);\n        if (useHoverLayer) {\n            this._toggleHoverLayerFlag(true);\n        }\n        this._applyStateObj(stateName, state, this._normalState, keepCurrentStates, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n        var textContent = this._textContent;\n        var textGuide = this._textGuide;\n        if (textContent) {\n            textContent.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n        }\n        if (textGuide) {\n            textGuide.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n        }\n        if (toNormalState) {\n            this.currentStates = [];\n            this._normalState = {};\n        }\n        else {\n            if (!keepCurrentStates) {\n                this.currentStates = [stateName];\n            }\n            else {\n                this.currentStates.push(stateName);\n            }\n        }\n        this._updateAnimationTargets();\n        this.markRedraw();\n        if (!useHoverLayer && this.__inHover) {\n            this._toggleHoverLayerFlag(false);\n            this.__dirty &= ~REDRAW_BIT;\n        }\n        return state;\n    };\n    Element.prototype.useStates = function (states, noAnimation, forceUseHoverLayer) {\n        if (!states.length) {\n            this.clearStates();\n        }\n        else {\n            var stateObjects = [];\n            var currentStates = this.currentStates;\n            var len = states.length;\n            var notChange = len === currentStates.length;\n            if (notChange) {\n                for (var i = 0; i < len; i++) {\n                    if (states[i] !== currentStates[i]) {\n                        notChange = false;\n                        break;\n                    }\n                }\n            }\n            if (notChange) {\n                return;\n            }\n            for (var i = 0; i < len; i++) {\n                var stateName = states[i];\n                var stateObj = void 0;\n                if (this.stateProxy) {\n                    stateObj = this.stateProxy(stateName, states);\n                }\n                if (!stateObj) {\n                    stateObj = this.states[stateName];\n                }\n                if (stateObj) {\n                    stateObjects.push(stateObj);\n                }\n            }\n            var lastStateObj = stateObjects[len - 1];\n            var useHoverLayer = !!((lastStateObj && lastStateObj.hoverLayer) || forceUseHoverLayer);\n            if (useHoverLayer) {\n                this._toggleHoverLayerFlag(true);\n            }\n            var mergedState = this._mergeStates(stateObjects);\n            var animationCfg = this.stateTransition;\n            this.saveCurrentToNormalState(mergedState);\n            this._applyStateObj(states.join(','), mergedState, this._normalState, false, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n            var textContent = this._textContent;\n            var textGuide = this._textGuide;\n            if (textContent) {\n                textContent.useStates(states, noAnimation, useHoverLayer);\n            }\n            if (textGuide) {\n                textGuide.useStates(states, noAnimation, useHoverLayer);\n            }\n            this._updateAnimationTargets();\n            this.currentStates = states.slice();\n            this.markRedraw();\n            if (!useHoverLayer && this.__inHover) {\n                this._toggleHoverLayerFlag(false);\n                this.__dirty &= ~REDRAW_BIT;\n            }\n        }\n    };\n    Element.prototype._updateAnimationTargets = function () {\n        for (var i = 0; i < this.animators.length; i++) {\n            var animator = this.animators[i];\n            if (animator.targetName) {\n                animator.changeTarget(this[animator.targetName]);\n            }\n        }\n    };\n    Element.prototype.removeState = function (state) {\n        var idx = indexOf(this.currentStates, state);\n        if (idx >= 0) {\n            var currentStates = this.currentStates.slice();\n            currentStates.splice(idx, 1);\n            this.useStates(currentStates);\n        }\n    };\n    Element.prototype.replaceState = function (oldState, newState, forceAdd) {\n        var currentStates = this.currentStates.slice();\n        var idx = indexOf(currentStates, oldState);\n        var newStateExists = indexOf(currentStates, newState) >= 0;\n        if (idx >= 0) {\n            if (!newStateExists) {\n                currentStates[idx] = newState;\n            }\n            else {\n                currentStates.splice(idx, 1);\n            }\n        }\n        else if (forceAdd && !newStateExists) {\n            currentStates.push(newState);\n        }\n        this.useStates(currentStates);\n    };\n    Element.prototype.toggleState = function (state, enable) {\n        if (enable) {\n            this.useState(state, true);\n        }\n        else {\n            this.removeState(state);\n        }\n    };\n    Element.prototype._mergeStates = function (states) {\n        var mergedState = {};\n        var mergedTextConfig;\n        for (var i = 0; i < states.length; i++) {\n            var state = states[i];\n            extend(mergedState, state);\n            if (state.textConfig) {\n                mergedTextConfig = mergedTextConfig || {};\n                extend(mergedTextConfig, state.textConfig);\n            }\n        }\n        if (mergedTextConfig) {\n            mergedState.textConfig = mergedTextConfig;\n        }\n        return mergedState;\n    };\n    Element.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n        var needsRestoreToNormal = !(state && keepCurrentStates);\n        if (state && state.textConfig) {\n            this.textConfig = extend({}, keepCurrentStates ? this.textConfig : normalState.textConfig);\n            extend(this.textConfig, state.textConfig);\n        }\n        else if (needsRestoreToNormal) {\n            if (normalState.textConfig) {\n                this.textConfig = normalState.textConfig;\n            }\n        }\n        var transitionTarget = {};\n        var hasTransition = false;\n        for (var i = 0; i < PRIMARY_STATES_KEYS.length; i++) {\n            var key = PRIMARY_STATES_KEYS[i];\n            var propNeedsTransition = transition && DEFAULT_ANIMATABLE_MAP[key];\n            if (state && state[key] != null) {\n                if (propNeedsTransition) {\n                    hasTransition = true;\n                    transitionTarget[key] = state[key];\n                }\n                else {\n                    this[key] = state[key];\n                }\n            }\n            else if (needsRestoreToNormal) {\n                if (normalState[key] != null) {\n                    if (propNeedsTransition) {\n                        hasTransition = true;\n                        transitionTarget[key] = normalState[key];\n                    }\n                    else {\n                        this[key] = normalState[key];\n                    }\n                }\n            }\n        }\n        if (!transition) {\n            for (var i = 0; i < this.animators.length; i++) {\n                var animator = this.animators[i];\n                var targetName = animator.targetName;\n                if (!animator.getLoop()) {\n                    animator.__changeFinalValue(targetName\n                        ? (state || normalState)[targetName]\n                        : (state || normalState));\n                }\n            }\n        }\n        if (hasTransition) {\n            this._transitionState(stateName, transitionTarget, animationCfg);\n        }\n    };\n    Element.prototype._attachComponent = function (componentEl) {\n        if (componentEl.__zr && !componentEl.__hostTarget) {\n            if (\"development\" !== 'production') {\n                throw new Error('Text element has been added to zrender.');\n            }\n            return;\n        }\n        if (componentEl === this) {\n            if (\"development\" !== 'production') {\n                throw new Error('Recursive component attachment.');\n            }\n            return;\n        }\n        var zr = this.__zr;\n        if (zr) {\n            componentEl.addSelfToZr(zr);\n        }\n        componentEl.__zr = zr;\n        componentEl.__hostTarget = this;\n    };\n    Element.prototype._detachComponent = function (componentEl) {\n        if (componentEl.__zr) {\n            componentEl.removeSelfFromZr(componentEl.__zr);\n        }\n        componentEl.__zr = null;\n        componentEl.__hostTarget = null;\n    };\n    Element.prototype.getClipPath = function () {\n        return this._clipPath;\n    };\n    Element.prototype.setClipPath = function (clipPath) {\n        if (this._clipPath && this._clipPath !== clipPath) {\n            this.removeClipPath();\n        }\n        this._attachComponent(clipPath);\n        this._clipPath = clipPath;\n        this.markRedraw();\n    };\n    Element.prototype.removeClipPath = function () {\n        var clipPath = this._clipPath;\n        if (clipPath) {\n            this._detachComponent(clipPath);\n            this._clipPath = null;\n            this.markRedraw();\n        }\n    };\n    Element.prototype.getTextContent = function () {\n        return this._textContent;\n    };\n    Element.prototype.setTextContent = function (textEl) {\n        var previousTextContent = this._textContent;\n        if (previousTextContent === textEl) {\n            return;\n        }\n        if (previousTextContent && previousTextContent !== textEl) {\n            this.removeTextContent();\n        }\n        if (\"development\" !== 'production') {\n            if (textEl.__zr && !textEl.__hostTarget) {\n                throw new Error('Text element has been added to zrender.');\n            }\n        }\n        textEl.innerTransformable = new Transformable();\n        this._attachComponent(textEl);\n        this._textContent = textEl;\n        this.markRedraw();\n    };\n    Element.prototype.setTextConfig = function (cfg) {\n        if (!this.textConfig) {\n            this.textConfig = {};\n        }\n        extend(this.textConfig, cfg);\n        this.markRedraw();\n    };\n    Element.prototype.removeTextConfig = function () {\n        this.textConfig = null;\n        this.markRedraw();\n    };\n    Element.prototype.removeTextContent = function () {\n        var textEl = this._textContent;\n        if (textEl) {\n            textEl.innerTransformable = null;\n            this._detachComponent(textEl);\n            this._textContent = null;\n            this._innerTextDefaultStyle = null;\n            this.markRedraw();\n        }\n    };\n    Element.prototype.getTextGuideLine = function () {\n        return this._textGuide;\n    };\n    Element.prototype.setTextGuideLine = function (guideLine) {\n        if (this._textGuide && this._textGuide !== guideLine) {\n            this.removeTextGuideLine();\n        }\n        this._attachComponent(guideLine);\n        this._textGuide = guideLine;\n        this.markRedraw();\n    };\n    Element.prototype.removeTextGuideLine = function () {\n        var textGuide = this._textGuide;\n        if (textGuide) {\n            this._detachComponent(textGuide);\n            this._textGuide = null;\n            this.markRedraw();\n        }\n    };\n    Element.prototype.markRedraw = function () {\n        this.__dirty |= REDRAW_BIT;\n        var zr = this.__zr;\n        if (zr) {\n            if (this.__inHover) {\n                zr.refreshHover();\n            }\n            else {\n                zr.refresh();\n            }\n        }\n        if (this.__hostTarget) {\n            this.__hostTarget.markRedraw();\n        }\n    };\n    Element.prototype.dirty = function () {\n        this.markRedraw();\n    };\n    Element.prototype._toggleHoverLayerFlag = function (inHover) {\n        this.__inHover = inHover;\n        var textContent = this._textContent;\n        var textGuide = this._textGuide;\n        if (textContent) {\n            textContent.__inHover = inHover;\n        }\n        if (textGuide) {\n            textGuide.__inHover = inHover;\n        }\n    };\n    Element.prototype.addSelfToZr = function (zr) {\n        if (this.__zr === zr) {\n            return;\n        }\n        this.__zr = zr;\n        var animators = this.animators;\n        if (animators) {\n            for (var i = 0; i < animators.length; i++) {\n                zr.animation.addAnimator(animators[i]);\n            }\n        }\n        if (this._clipPath) {\n            this._clipPath.addSelfToZr(zr);\n        }\n        if (this._textContent) {\n            this._textContent.addSelfToZr(zr);\n        }\n        if (this._textGuide) {\n            this._textGuide.addSelfToZr(zr);\n        }\n    };\n    Element.prototype.removeSelfFromZr = function (zr) {\n        if (!this.__zr) {\n            return;\n        }\n        this.__zr = null;\n        var animators = this.animators;\n        if (animators) {\n            for (var i = 0; i < animators.length; i++) {\n                zr.animation.removeAnimator(animators[i]);\n            }\n        }\n        if (this._clipPath) {\n            this._clipPath.removeSelfFromZr(zr);\n        }\n        if (this._textContent) {\n            this._textContent.removeSelfFromZr(zr);\n        }\n        if (this._textGuide) {\n            this._textGuide.removeSelfFromZr(zr);\n        }\n    };\n    Element.prototype.animate = function (key, loop, allowDiscreteAnimation) {\n        var target = key ? this[key] : this;\n        if (\"development\" !== 'production') {\n            if (!target) {\n                logError('Property \"'\n                    + key\n                    + '\" is not existed in element '\n                    + this.id);\n                return;\n            }\n        }\n        var animator = new Animator(target, loop, allowDiscreteAnimation);\n        key && (animator.targetName = key);\n        this.addAnimator(animator, key);\n        return animator;\n    };\n    Element.prototype.addAnimator = function (animator, key) {\n        var zr = this.__zr;\n        var el = this;\n        animator.during(function () {\n            el.updateDuringAnimation(key);\n        }).done(function () {\n            var animators = el.animators;\n            var idx = indexOf(animators, animator);\n            if (idx >= 0) {\n                animators.splice(idx, 1);\n            }\n        });\n        this.animators.push(animator);\n        if (zr) {\n            zr.animation.addAnimator(animator);\n        }\n        zr && zr.wakeUp();\n    };\n    Element.prototype.updateDuringAnimation = function (key) {\n        this.markRedraw();\n    };\n    Element.prototype.stopAnimation = function (scope, forwardToLast) {\n        var animators = this.animators;\n        var len = animators.length;\n        var leftAnimators = [];\n        for (var i = 0; i < len; i++) {\n            var animator = animators[i];\n            if (!scope || scope === animator.scope) {\n                animator.stop(forwardToLast);\n            }\n            else {\n                leftAnimators.push(animator);\n            }\n        }\n        this.animators = leftAnimators;\n        return this;\n    };\n    Element.prototype.animateTo = function (target, cfg, animationProps) {\n        animateTo(this, target, cfg, animationProps);\n    };\n    Element.prototype.animateFrom = function (target, cfg, animationProps) {\n        animateTo(this, target, cfg, animationProps, true);\n    };\n    Element.prototype._transitionState = function (stateName, target, cfg, animationProps) {\n        var animators = animateTo(this, target, cfg, animationProps);\n        for (var i = 0; i < animators.length; i++) {\n            animators[i].__fromStateTransition = stateName;\n        }\n    };\n    Element.prototype.getBoundingRect = function () {\n        return null;\n    };\n    Element.prototype.getPaintRect = function () {\n        return null;\n    };\n    Element.initDefaultProps = (function () {\n        var elProto = Element.prototype;\n        elProto.type = 'element';\n        elProto.name = '';\n        elProto.ignore =\n            elProto.silent =\n                elProto.isGroup =\n                    elProto.draggable =\n                        elProto.dragging =\n                            elProto.ignoreClip =\n                                elProto.__inHover = false;\n        elProto.__dirty = REDRAW_BIT;\n        var logs = {};\n        function logDeprecatedError(key, xKey, yKey) {\n            if (!logs[key + xKey + yKey]) {\n                console.warn(\"DEPRECATED: '\" + key + \"' has been deprecated. use '\" + xKey + \"', '\" + yKey + \"' instead\");\n                logs[key + xKey + yKey] = true;\n            }\n        }\n        function createLegacyProperty(key, privateKey, xKey, yKey) {\n            Object.defineProperty(elProto, key, {\n                get: function () {\n                    if (\"development\" !== 'production') {\n                        logDeprecatedError(key, xKey, yKey);\n                    }\n                    if (!this[privateKey]) {\n                        var pos = this[privateKey] = [];\n                        enhanceArray(this, pos);\n                    }\n                    return this[privateKey];\n                },\n                set: function (pos) {\n                    if (\"development\" !== 'production') {\n                        logDeprecatedError(key, xKey, yKey);\n                    }\n                    this[xKey] = pos[0];\n                    this[yKey] = pos[1];\n                    this[privateKey] = pos;\n                    enhanceArray(this, pos);\n                }\n            });\n            function enhanceArray(self, pos) {\n                Object.defineProperty(pos, 0, {\n                    get: function () {\n                        return self[xKey];\n                    },\n                    set: function (val) {\n                        self[xKey] = val;\n                    }\n                });\n                Object.defineProperty(pos, 1, {\n                    get: function () {\n                        return self[yKey];\n                    },\n                    set: function (val) {\n                        self[yKey] = val;\n                    }\n                });\n            }\n        }\n        if (Object.defineProperty) {\n            createLegacyProperty('position', '_legacyPos', 'x', 'y');\n            createLegacyProperty('scale', '_legacyScale', 'scaleX', 'scaleY');\n            createLegacyProperty('origin', '_legacyOrigin', 'originX', 'originY');\n        }\n    })();\n    return Element;\n}());\nmixin(Element, Eventful);\nmixin(Element, Transformable);\nfunction animateTo(animatable, target, cfg, animationProps, reverse) {\n    cfg = cfg || {};\n    var animators = [];\n    animateToShallow(animatable, '', animatable, target, cfg, animationProps, animators, reverse);\n    var finishCount = animators.length;\n    var doneHappened = false;\n    var cfgDone = cfg.done;\n    var cfgAborted = cfg.aborted;\n    var doneCb = function () {\n        doneHappened = true;\n        finishCount--;\n        if (finishCount <= 0) {\n            doneHappened\n                ? (cfgDone && cfgDone())\n                : (cfgAborted && cfgAborted());\n        }\n    };\n    var abortedCb = function () {\n        finishCount--;\n        if (finishCount <= 0) {\n            doneHappened\n                ? (cfgDone && cfgDone())\n                : (cfgAborted && cfgAborted());\n        }\n    };\n    if (!finishCount) {\n        cfgDone && cfgDone();\n    }\n    if (animators.length > 0 && cfg.during) {\n        animators[0].during(function (target, percent) {\n            cfg.during(percent);\n        });\n    }\n    for (var i = 0; i < animators.length; i++) {\n        var animator = animators[i];\n        if (doneCb) {\n            animator.done(doneCb);\n        }\n        if (abortedCb) {\n            animator.aborted(abortedCb);\n        }\n        if (cfg.force) {\n            animator.duration(cfg.duration);\n        }\n        animator.start(cfg.easing);\n    }\n    return animators;\n}\nfunction copyArrShallow(source, target, len) {\n    for (var i = 0; i < len; i++) {\n        source[i] = target[i];\n    }\n}\nfunction is2DArray(value) {\n    return isArrayLike(value[0]);\n}\nfunction copyValue(target, source, key) {\n    if (isArrayLike(source[key])) {\n        if (!isArrayLike(target[key])) {\n            target[key] = [];\n        }\n        if (isTypedArray(source[key])) {\n            var len = source[key].length;\n            if (target[key].length !== len) {\n                target[key] = new (source[key].constructor)(len);\n                copyArrShallow(target[key], source[key], len);\n            }\n        }\n        else {\n            var sourceArr = source[key];\n            var targetArr = target[key];\n            var len0 = sourceArr.length;\n            if (is2DArray(sourceArr)) {\n                var len1 = sourceArr[0].length;\n                for (var i = 0; i < len0; i++) {\n                    if (!targetArr[i]) {\n                        targetArr[i] = Array.prototype.slice.call(sourceArr[i]);\n                    }\n                    else {\n                        copyArrShallow(targetArr[i], sourceArr[i], len1);\n                    }\n                }\n            }\n            else {\n                copyArrShallow(targetArr, sourceArr, len0);\n            }\n            targetArr.length = sourceArr.length;\n        }\n    }\n    else {\n        target[key] = source[key];\n    }\n}\nfunction isValueSame(val1, val2) {\n    return val1 === val2\n        || isArrayLike(val1) && isArrayLike(val2) && is1DArraySame(val1, val2);\n}\nfunction is1DArraySame(arr0, arr1) {\n    var len = arr0.length;\n    if (len !== arr1.length) {\n        return false;\n    }\n    for (var i = 0; i < len; i++) {\n        if (arr0[i] !== arr1[i]) {\n            return false;\n        }\n    }\n    return true;\n}\nfunction animateToShallow(animatable, topKey, animateObj, target, cfg, animationProps, animators, reverse) {\n    var targetKeys = keys(target);\n    var duration = cfg.duration;\n    var delay = cfg.delay;\n    var additive = cfg.additive;\n    var setToFinal = cfg.setToFinal;\n    var animateAll = !isObject(animationProps);\n    var existsAnimators = animatable.animators;\n    var animationKeys = [];\n    for (var k = 0; k < targetKeys.length; k++) {\n        var innerKey = targetKeys[k];\n        var targetVal = target[innerKey];\n        if (targetVal != null && animateObj[innerKey] != null\n            && (animateAll || animationProps[innerKey])) {\n            if (isObject(targetVal)\n                && !isArrayLike(targetVal)\n                && !isGradientObject(targetVal)) {\n                if (topKey) {\n                    if (!reverse) {\n                        animateObj[innerKey] = targetVal;\n                        animatable.updateDuringAnimation(topKey);\n                    }\n                    continue;\n                }\n                animateToShallow(animatable, innerKey, animateObj[innerKey], targetVal, cfg, animationProps && animationProps[innerKey], animators, reverse);\n            }\n            else {\n                animationKeys.push(innerKey);\n            }\n        }\n        else if (!reverse) {\n            animateObj[innerKey] = targetVal;\n            animatable.updateDuringAnimation(topKey);\n            animationKeys.push(innerKey);\n        }\n    }\n    var keyLen = animationKeys.length;\n    if (!additive && keyLen) {\n        for (var i = 0; i < existsAnimators.length; i++) {\n            var animator = existsAnimators[i];\n            if (animator.targetName === topKey) {\n                var allAborted = animator.stopTracks(animationKeys);\n                if (allAborted) {\n                    var idx = indexOf(existsAnimators, animator);\n                    existsAnimators.splice(idx, 1);\n                }\n            }\n        }\n    }\n    if (!cfg.force) {\n        animationKeys = filter(animationKeys, function (key) { return !isValueSame(target[key], animateObj[key]); });\n        keyLen = animationKeys.length;\n    }\n    if (keyLen > 0\n        || (cfg.force && !animators.length)) {\n        var revertedSource = void 0;\n        var reversedTarget = void 0;\n        var sourceClone = void 0;\n        if (reverse) {\n            reversedTarget = {};\n            if (setToFinal) {\n                revertedSource = {};\n            }\n            for (var i = 0; i < keyLen; i++) {\n                var innerKey = animationKeys[i];\n                reversedTarget[innerKey] = animateObj[innerKey];\n                if (setToFinal) {\n                    revertedSource[innerKey] = target[innerKey];\n                }\n                else {\n                    animateObj[innerKey] = target[innerKey];\n                }\n            }\n        }\n        else if (setToFinal) {\n            sourceClone = {};\n            for (var i = 0; i < keyLen; i++) {\n                var innerKey = animationKeys[i];\n                sourceClone[innerKey] = cloneValue(animateObj[innerKey]);\n                copyValue(animateObj, target, innerKey);\n            }\n        }\n        var animator = new Animator(animateObj, false, false, additive ? filter(existsAnimators, function (animator) { return animator.targetName === topKey; }) : null);\n        animator.targetName = topKey;\n        if (cfg.scope) {\n            animator.scope = cfg.scope;\n        }\n        if (setToFinal && revertedSource) {\n            animator.whenWithKeys(0, revertedSource, animationKeys);\n        }\n        if (sourceClone) {\n            animator.whenWithKeys(0, sourceClone, animationKeys);\n        }\n        animator.whenWithKeys(duration == null ? 500 : duration, reverse ? reversedTarget : target, animationKeys).delay(delay || 0);\n        animatable.addAnimator(animator, topKey);\n        animators.push(animator);\n    }\n}\n\nvar Group = (function (_super) {\n    __extends(Group, _super);\n    function Group(opts) {\n        var _this = _super.call(this) || this;\n        _this.isGroup = true;\n        _this._children = [];\n        _this.attr(opts);\n        return _this;\n    }\n    Group.prototype.childrenRef = function () {\n        return this._children;\n    };\n    Group.prototype.children = function () {\n        return this._children.slice();\n    };\n    Group.prototype.childAt = function (idx) {\n        return this._children[idx];\n    };\n    Group.prototype.childOfName = function (name) {\n        var children = this._children;\n        for (var i = 0; i < children.length; i++) {\n            if (children[i].name === name) {\n                return children[i];\n            }\n        }\n    };\n    Group.prototype.childCount = function () {\n        return this._children.length;\n    };\n    Group.prototype.add = function (child) {\n        if (child) {\n            if (child !== this && child.parent !== this) {\n                this._children.push(child);\n                this._doAdd(child);\n            }\n            if (\"development\" !== 'production') {\n                if (child.__hostTarget) {\n                    throw 'This elemenet has been used as an attachment';\n                }\n            }\n        }\n        return this;\n    };\n    Group.prototype.addBefore = function (child, nextSibling) {\n        if (child && child !== this && child.parent !== this\n            && nextSibling && nextSibling.parent === this) {\n            var children = this._children;\n            var idx = children.indexOf(nextSibling);\n            if (idx >= 0) {\n                children.splice(idx, 0, child);\n                this._doAdd(child);\n            }\n        }\n        return this;\n    };\n    Group.prototype.replace = function (oldChild, newChild) {\n        var idx = indexOf(this._children, oldChild);\n        if (idx >= 0) {\n            this.replaceAt(newChild, idx);\n        }\n        return this;\n    };\n    Group.prototype.replaceAt = function (child, index) {\n        var children = this._children;\n        var old = children[index];\n        if (child && child !== this && child.parent !== this && child !== old) {\n            children[index] = child;\n            old.parent = null;\n            var zr = this.__zr;\n            if (zr) {\n                old.removeSelfFromZr(zr);\n            }\n            this._doAdd(child);\n        }\n        return this;\n    };\n    Group.prototype._doAdd = function (child) {\n        if (child.parent) {\n            child.parent.remove(child);\n        }\n        child.parent = this;\n        var zr = this.__zr;\n        if (zr && zr !== child.__zr) {\n            child.addSelfToZr(zr);\n        }\n        zr && zr.refresh();\n    };\n    Group.prototype.remove = function (child) {\n        var zr = this.__zr;\n        var children = this._children;\n        var idx = indexOf(children, child);\n        if (idx < 0) {\n            return this;\n        }\n        children.splice(idx, 1);\n        child.parent = null;\n        if (zr) {\n            child.removeSelfFromZr(zr);\n        }\n        zr && zr.refresh();\n        return this;\n    };\n    Group.prototype.removeAll = function () {\n        var children = this._children;\n        var zr = this.__zr;\n        for (var i = 0; i < children.length; i++) {\n            var child = children[i];\n            if (zr) {\n                child.removeSelfFromZr(zr);\n            }\n            child.parent = null;\n        }\n        children.length = 0;\n        return this;\n    };\n    Group.prototype.eachChild = function (cb, context) {\n        var children = this._children;\n        for (var i = 0; i < children.length; i++) {\n            var child = children[i];\n            cb.call(context, child, i);\n        }\n        return this;\n    };\n    Group.prototype.traverse = function (cb, context) {\n        for (var i = 0; i < this._children.length; i++) {\n            var child = this._children[i];\n            var stopped = cb.call(context, child);\n            if (child.isGroup && !stopped) {\n                child.traverse(cb, context);\n            }\n        }\n        return this;\n    };\n    Group.prototype.addSelfToZr = function (zr) {\n        _super.prototype.addSelfToZr.call(this, zr);\n        for (var i = 0; i < this._children.length; i++) {\n            var child = this._children[i];\n            child.addSelfToZr(zr);\n        }\n    };\n    Group.prototype.removeSelfFromZr = function (zr) {\n        _super.prototype.removeSelfFromZr.call(this, zr);\n        for (var i = 0; i < this._children.length; i++) {\n            var child = this._children[i];\n            child.removeSelfFromZr(zr);\n        }\n    };\n    Group.prototype.getBoundingRect = function (includeChildren) {\n        var tmpRect = new BoundingRect(0, 0, 0, 0);\n        var children = includeChildren || this._children;\n        var tmpMat = [];\n        var rect = null;\n        for (var i = 0; i < children.length; i++) {\n            var child = children[i];\n            if (child.ignore || child.invisible) {\n                continue;\n            }\n            var childRect = child.getBoundingRect();\n            var transform = child.getLocalTransform(tmpMat);\n            if (transform) {\n                BoundingRect.applyTransform(tmpRect, childRect, transform);\n                rect = rect || tmpRect.clone();\n                rect.union(tmpRect);\n            }\n            else {\n                rect = rect || childRect.clone();\n                rect.union(childRect);\n            }\n        }\n        return rect || tmpRect;\n    };\n    return Group;\n}(Element));\nGroup.prototype.type = 'group';\n\n/*!\n* ZRender, a high performance 2d drawing library.\n*\n* Copyright (c) 2013, Baidu Inc.\n* All rights reserved.\n*\n* LICENSE\n* https://github.com/ecomfe/zrender/blob/master/LICENSE.txt\n*/\nvar painterCtors = {};\nvar instances = {};\nfunction delInstance(id) {\n    delete instances[id];\n}\nfunction isDarkMode(backgroundColor) {\n    if (!backgroundColor) {\n        return false;\n    }\n    if (typeof backgroundColor === 'string') {\n        return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD;\n    }\n    else if (backgroundColor.colorStops) {\n        var colorStops = backgroundColor.colorStops;\n        var totalLum = 0;\n        var len = colorStops.length;\n        for (var i = 0; i < len; i++) {\n            totalLum += lum(colorStops[i].color, 1);\n        }\n        totalLum /= len;\n        return totalLum < DARK_MODE_THRESHOLD;\n    }\n    return false;\n}\nvar ZRender = (function () {\n    function ZRender(id, dom, opts) {\n        var _this = this;\n        this._sleepAfterStill = 10;\n        this._stillFrameAccum = 0;\n        this._needsRefresh = true;\n        this._needsRefreshHover = true;\n        this._darkMode = false;\n        opts = opts || {};\n        this.dom = dom;\n        this.id = id;\n        var storage = new Storage();\n        var rendererType = opts.renderer || 'canvas';\n        if (!painterCtors[rendererType]) {\n            rendererType = keys(painterCtors)[0];\n        }\n        if (\"development\" !== 'production') {\n            if (!painterCtors[rendererType]) {\n                throw new Error(\"Renderer '\" + rendererType + \"' is not imported. Please import it first.\");\n            }\n        }\n        opts.useDirtyRect = opts.useDirtyRect == null\n            ? false\n            : opts.useDirtyRect;\n        var painter = new painterCtors[rendererType](dom, storage, opts, id);\n        var ssrMode = opts.ssr || painter.ssrOnly;\n        this.storage = storage;\n        this.painter = painter;\n        var handerProxy = (!env.node && !env.worker && !ssrMode)\n            ? new HandlerDomProxy(painter.getViewportRoot(), painter.root)\n            : null;\n        var useCoarsePointer = opts.useCoarsePointer;\n        var usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto')\n            ? env.touchEventsSupported\n            : !!useCoarsePointer;\n        var defaultPointerSize = 44;\n        var pointerSize;\n        if (usePointerSize) {\n            pointerSize = retrieve2(opts.pointerSize, defaultPointerSize);\n        }\n        this.handler = new Handler(storage, painter, handerProxy, painter.root, pointerSize);\n        this.animation = new Animation({\n            stage: {\n                update: ssrMode ? null : function () { return _this._flush(true); }\n            }\n        });\n        if (!ssrMode) {\n            this.animation.start();\n        }\n    }\n    ZRender.prototype.add = function (el) {\n        if (!el) {\n            return;\n        }\n        this.storage.addRoot(el);\n        el.addSelfToZr(this);\n        this.refresh();\n    };\n    ZRender.prototype.remove = function (el) {\n        if (!el) {\n            return;\n        }\n        this.storage.delRoot(el);\n        el.removeSelfFromZr(this);\n        this.refresh();\n    };\n    ZRender.prototype.configLayer = function (zLevel, config) {\n        if (this.painter.configLayer) {\n            this.painter.configLayer(zLevel, config);\n        }\n        this.refresh();\n    };\n    ZRender.prototype.setBackgroundColor = function (backgroundColor) {\n        if (this.painter.setBackgroundColor) {\n            this.painter.setBackgroundColor(backgroundColor);\n        }\n        this.refresh();\n        this._backgroundColor = backgroundColor;\n        this._darkMode = isDarkMode(backgroundColor);\n    };\n    ZRender.prototype.getBackgroundColor = function () {\n        return this._backgroundColor;\n    };\n    ZRender.prototype.setDarkMode = function (darkMode) {\n        this._darkMode = darkMode;\n    };\n    ZRender.prototype.isDarkMode = function () {\n        return this._darkMode;\n    };\n    ZRender.prototype.refreshImmediately = function (fromInside) {\n        if (!fromInside) {\n            this.animation.update(true);\n        }\n        this._needsRefresh = false;\n        this.painter.refresh();\n        this._needsRefresh = false;\n    };\n    ZRender.prototype.refresh = function () {\n        this._needsRefresh = true;\n        this.animation.start();\n    };\n    ZRender.prototype.flush = function () {\n        this._flush(false);\n    };\n    ZRender.prototype._flush = function (fromInside) {\n        var triggerRendered;\n        var start = getTime();\n        if (this._needsRefresh) {\n            triggerRendered = true;\n            this.refreshImmediately(fromInside);\n        }\n        if (this._needsRefreshHover) {\n            triggerRendered = true;\n            this.refreshHoverImmediately();\n        }\n        var end = getTime();\n        if (triggerRendered) {\n            this._stillFrameAccum = 0;\n            this.trigger('rendered', {\n                elapsedTime: end - start\n            });\n        }\n        else if (this._sleepAfterStill > 0) {\n            this._stillFrameAccum++;\n            if (this._stillFrameAccum > this._sleepAfterStill) {\n                this.animation.stop();\n            }\n        }\n    };\n    ZRender.prototype.setSleepAfterStill = function (stillFramesCount) {\n        this._sleepAfterStill = stillFramesCount;\n    };\n    ZRender.prototype.wakeUp = function () {\n        this.animation.start();\n        this._stillFrameAccum = 0;\n    };\n    ZRender.prototype.refreshHover = function () {\n        this._needsRefreshHover = true;\n    };\n    ZRender.prototype.refreshHoverImmediately = function () {\n        this._needsRefreshHover = false;\n        if (this.painter.refreshHover && this.painter.getType() === 'canvas') {\n            this.painter.refreshHover();\n        }\n    };\n    ZRender.prototype.resize = function (opts) {\n        opts = opts || {};\n        this.painter.resize(opts.width, opts.height);\n        this.handler.resize();\n    };\n    ZRender.prototype.clearAnimation = function () {\n        this.animation.clear();\n    };\n    ZRender.prototype.getWidth = function () {\n        return this.painter.getWidth();\n    };\n    ZRender.prototype.getHeight = function () {\n        return this.painter.getHeight();\n    };\n    ZRender.prototype.setCursorStyle = function (cursorStyle) {\n        this.handler.setCursorStyle(cursorStyle);\n    };\n    ZRender.prototype.findHover = function (x, y) {\n        return this.handler.findHover(x, y);\n    };\n    ZRender.prototype.on = function (eventName, eventHandler, context) {\n        this.handler.on(eventName, eventHandler, context);\n        return this;\n    };\n    ZRender.prototype.off = function (eventName, eventHandler) {\n        this.handler.off(eventName, eventHandler);\n    };\n    ZRender.prototype.trigger = function (eventName, event) {\n        this.handler.trigger(eventName, event);\n    };\n    ZRender.prototype.clear = function () {\n        var roots = this.storage.getRoots();\n        for (var i = 0; i < roots.length; i++) {\n            if (roots[i] instanceof Group) {\n                roots[i].removeSelfFromZr(this);\n            }\n        }\n        this.storage.delAllRoots();\n        this.painter.clear();\n    };\n    ZRender.prototype.dispose = function () {\n        this.animation.stop();\n        this.clear();\n        this.storage.dispose();\n        this.painter.dispose();\n        this.handler.dispose();\n        this.animation =\n            this.storage =\n                this.painter =\n                    this.handler = null;\n        delInstance(this.id);\n    };\n    return ZRender;\n}());\nfunction init(dom, opts) {\n    var zr = new ZRender(guid(), dom, opts);\n    instances[zr.id] = zr;\n    return zr;\n}\nfunction dispose(zr) {\n    zr.dispose();\n}\nfunction disposeAll() {\n    for (var key in instances) {\n        if (instances.hasOwnProperty(key)) {\n            instances[key].dispose();\n        }\n    }\n    instances = {};\n}\nfunction getInstance(id) {\n    return instances[id];\n}\nfunction registerPainter(name, Ctor) {\n    painterCtors[name] = Ctor;\n}\nvar version = '5.4.1';\n\nvar zrender = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    init: init,\n    dispose: dispose,\n    disposeAll: disposeAll,\n    getInstance: getInstance,\n    registerPainter: registerPainter,\n    version: version\n});\n\nvar RADIAN_EPSILON = 1e-4; // Although chrome already enlarge this number to 100 for `toFixed`, but\n// we sill follow the spec for compatibility.\n\nvar ROUND_SUPPORTED_PRECISION_MAX = 20;\n\nfunction _trim(str) {\n  return str.replace(/^\\s+|\\s+$/g, '');\n}\n/**\n * Linear mapping a value from domain to range\n * @param  val\n * @param  domain Domain extent domain[0] can be bigger than domain[1]\n * @param  range  Range extent range[0] can be bigger than range[1]\n * @param  clamp Default to be false\n */\n\n\nfunction linearMap(val, domain, range, clamp) {\n  var d0 = domain[0];\n  var d1 = domain[1];\n  var r0 = range[0];\n  var r1 = range[1];\n  var subDomain = d1 - d0;\n  var subRange = r1 - r0;\n\n  if (subDomain === 0) {\n    return subRange === 0 ? r0 : (r0 + r1) / 2;\n  } // Avoid accuracy problem in edge, such as\n  // 146.39 - 62.83 === 83.55999999999999.\n  // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError\n  // It is a little verbose for efficiency considering this method\n  // is a hotspot.\n\n\n  if (clamp) {\n    if (subDomain > 0) {\n      if (val <= d0) {\n        return r0;\n      } else if (val >= d1) {\n        return r1;\n      }\n    } else {\n      if (val >= d0) {\n        return r0;\n      } else if (val <= d1) {\n        return r1;\n      }\n    }\n  } else {\n    if (val === d0) {\n      return r0;\n    }\n\n    if (val === d1) {\n      return r1;\n    }\n  }\n\n  return (val - d0) / subDomain * subRange + r0;\n}\n/**\n * Convert a percent string to absolute number.\n * Returns NaN if percent is not a valid string or number\n */\n\nfunction parsePercent$1(percent, all) {\n  switch (percent) {\n    case 'center':\n    case 'middle':\n      percent = '50%';\n      break;\n\n    case 'left':\n    case 'top':\n      percent = '0%';\n      break;\n\n    case 'right':\n    case 'bottom':\n      percent = '100%';\n      break;\n  }\n\n  if (isString(percent)) {\n    if (_trim(percent).match(/%$/)) {\n      return parseFloat(percent) / 100 * all;\n    }\n\n    return parseFloat(percent);\n  }\n\n  return percent == null ? NaN : +percent;\n}\nfunction round(x, precision, returnStr) {\n  if (precision == null) {\n    precision = 10;\n  } // Avoid range error\n\n\n  precision = Math.min(Math.max(0, precision), ROUND_SUPPORTED_PRECISION_MAX); // PENDING: 1.005.toFixed(2) is '1.00' rather than '1.01'\n\n  x = (+x).toFixed(precision);\n  return returnStr ? x : +x;\n}\n/**\n * Inplacd asc sort arr.\n * The input arr will be modified.\n */\n\nfunction asc(arr) {\n  arr.sort(function (a, b) {\n    return a - b;\n  });\n  return arr;\n}\n/**\n * Get precision.\n */\n\nfunction getPrecision(val) {\n  val = +val;\n\n  if (isNaN(val)) {\n    return 0;\n  } // It is much faster than methods converting number to string as follows\n  //      let tmp = val.toString();\n  //      return tmp.length - 1 - tmp.indexOf('.');\n  // especially when precision is low\n  // Notice:\n  // (1) If the loop count is over about 20, it is slower than `getPrecisionSafe`.\n  //     (see https://jsbench.me/2vkpcekkvw/1)\n  // (2) If the val is less than for example 1e-15, the result may be incorrect.\n  //     (see test/ut/spec/util/number.test.ts `getPrecision_equal_random`)\n\n\n  if (val > 1e-14) {\n    var e = 1;\n\n    for (var i = 0; i < 15; i++, e *= 10) {\n      if (Math.round(val * e) / e === val) {\n        return i;\n      }\n    }\n  }\n\n  return getPrecisionSafe(val);\n}\n/**\n * Get precision with slow but safe method\n */\n\nfunction getPrecisionSafe(val) {\n  // toLowerCase for: '3.4E-12'\n  var str = val.toString().toLowerCase(); // Consider scientific notation: '3.4e-12' '3.4e+12'\n\n  var eIndex = str.indexOf('e');\n  var exp = eIndex > 0 ? +str.slice(eIndex + 1) : 0;\n  var significandPartLen = eIndex > 0 ? eIndex : str.length;\n  var dotIndex = str.indexOf('.');\n  var decimalPartLen = dotIndex < 0 ? 0 : significandPartLen - 1 - dotIndex;\n  return Math.max(0, decimalPartLen - exp);\n}\n/**\n * Minimal dicernible data precisioin according to a single pixel.\n */\n\nfunction getPixelPrecision(dataExtent, pixelExtent) {\n  var log = Math.log;\n  var LN10 = Math.LN10;\n  var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);\n  var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); // toFixed() digits argument must be between 0 and 20.\n\n  var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);\n  return !isFinite(precision) ? 20 : precision;\n}\n/**\n * Get a data of given precision, assuring the sum of percentages\n * in valueList is 1.\n * The largest remainder method is used.\n * https://en.wikipedia.org/wiki/Largest_remainder_method\n *\n * @param valueList a list of all data\n * @param idx index of the data to be processed in valueList\n * @param precision integer number showing digits of precision\n * @return percent ranging from 0 to 100\n */\n\nfunction getPercentWithPrecision(valueList, idx, precision) {\n  if (!valueList[idx]) {\n    return 0;\n  }\n\n  var seats = getPercentSeats(valueList, precision);\n  return seats[idx] || 0;\n}\n/**\n * Get a data of given precision, assuring the sum of percentages\n * in valueList is 1.\n * The largest remainder method is used.\n * https://en.wikipedia.org/wiki/Largest_remainder_method\n *\n * @param valueList a list of all data\n * @param precision integer number showing digits of precision\n * @return {Array<number>}\n */\n\nfunction getPercentSeats(valueList, precision) {\n  var sum = reduce(valueList, function (acc, val) {\n    return acc + (isNaN(val) ? 0 : val);\n  }, 0);\n\n  if (sum === 0) {\n    return [];\n  }\n\n  var digits = Math.pow(10, precision);\n  var votesPerQuota = map(valueList, function (val) {\n    return (isNaN(val) ? 0 : val) / sum * digits * 100;\n  });\n  var targetSeats = digits * 100;\n  var seats = map(votesPerQuota, function (votes) {\n    // Assign automatic seats.\n    return Math.floor(votes);\n  });\n  var currentSum = reduce(seats, function (acc, val) {\n    return acc + val;\n  }, 0);\n  var remainder = map(votesPerQuota, function (votes, idx) {\n    return votes - seats[idx];\n  }); // Has remainding votes.\n\n  while (currentSum < targetSeats) {\n    // Find next largest remainder.\n    var max = Number.NEGATIVE_INFINITY;\n    var maxId = null;\n\n    for (var i = 0, len = remainder.length; i < len; ++i) {\n      if (remainder[i] > max) {\n        max = remainder[i];\n        maxId = i;\n      }\n    } // Add a vote to max remainder.\n\n\n    ++seats[maxId];\n    remainder[maxId] = 0;\n    ++currentSum;\n  }\n\n  return map(seats, function (seat) {\n    return seat / digits;\n  });\n}\n/**\n * Solve the floating point adding problem like 0.1 + 0.2 === 0.30000000000000004\n * See <http://0.30000000000000004.com/>\n */\n\nfunction addSafe(val0, val1) {\n  var maxPrecision = Math.max(getPrecision(val0), getPrecision(val1)); // const multiplier = Math.pow(10, maxPrecision);\n  // return (Math.round(val0 * multiplier) + Math.round(val1 * multiplier)) / multiplier;\n\n  var sum = val0 + val1; // // PENDING: support more?\n\n  return maxPrecision > ROUND_SUPPORTED_PRECISION_MAX ? sum : round(sum, maxPrecision);\n} // Number.MAX_SAFE_INTEGER, ie do not support.\n\nvar MAX_SAFE_INTEGER = 9007199254740991;\n/**\n * To 0 - 2 * PI, considering negative radian.\n */\n\nfunction remRadian(radian) {\n  var pi2 = Math.PI * 2;\n  return (radian % pi2 + pi2) % pi2;\n}\n/**\n * @param {type} radian\n * @return {boolean}\n */\n\nfunction isRadianAroundZero(val) {\n  return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;\n} // eslint-disable-next-line\n\nvar TIME_REG = /^(?:(\\d{4})(?:[-\\/](\\d{1,2})(?:[-\\/](\\d{1,2})(?:[T ](\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2})(?:[.,](\\d+))?)?)?(Z|[\\+\\-]\\d\\d:?\\d\\d)?)?)?)?)?$/; // jshint ignore:line\n\n/**\n * @param value valid type: number | string | Date, otherwise return `new Date(NaN)`\n *   These values can be accepted:\n *   + An instance of Date, represent a time in its own time zone.\n *   + Or string in a subset of ISO 8601, only including:\n *     + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',\n *     + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',\n *     + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',\n *     all of which will be treated as local time if time zone is not specified\n *     (see <https://momentjs.com/>).\n *   + Or other string format, including (all of which will be treated as local time):\n *     '2012', '2012-3-1', '2012/3/1', '2012/03/01',\n *     '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'\n *   + a timestamp, which represent a time in UTC.\n * @return date Never be null/undefined. If invalid, return `new Date(NaN)`.\n */\n\nfunction parseDate(value) {\n  if (value instanceof Date) {\n    return value;\n  } else if (isString(value)) {\n    // Different browsers parse date in different way, so we parse it manually.\n    // Some other issues:\n    // new Date('1970-01-01') is UTC,\n    // new Date('1970/01/01') and new Date('1970-1-01') is local.\n    // See issue #3623\n    var match = TIME_REG.exec(value);\n\n    if (!match) {\n      // return Invalid Date.\n      return new Date(NaN);\n    } // Use local time when no timezone offset is specified.\n\n\n    if (!match[8]) {\n      // match[n] can only be string or undefined.\n      // But take care of '12' + 1 => '121'.\n      return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0);\n    } // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,\n    // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).\n    // For example, system timezone is set as \"Time Zone: America/Toronto\",\n    // then these code will get different result:\n    // `new Date(1478411999999).getTimezoneOffset();  // get 240`\n    // `new Date(1478412000000).getTimezoneOffset();  // get 300`\n    // So we should not use `new Date`, but use `Date.UTC`.\n    else {\n        var hour = +match[4] || 0;\n\n        if (match[8].toUpperCase() !== 'Z') {\n          hour -= +match[8].slice(0, 3);\n        }\n\n        return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0));\n      }\n  } else if (value == null) {\n    return new Date(NaN);\n  }\n\n  return new Date(Math.round(value));\n}\n/**\n * Quantity of a number. e.g. 0.1, 1, 10, 100\n *\n * @param val\n * @return\n */\n\nfunction quantity(val) {\n  return Math.pow(10, quantityExponent(val));\n}\n/**\n * Exponent of the quantity of a number\n * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3\n *\n * @param val non-negative value\n * @return\n */\n\nfunction quantityExponent(val) {\n  if (val === 0) {\n    return 0;\n  }\n\n  var exp = Math.floor(Math.log(val) / Math.LN10);\n  /**\n   * exp is expected to be the rounded-down result of the base-10 log of val.\n   * But due to the precision loss with Math.log(val), we need to restore it\n   * using 10^exp to make sure we can get val back from exp. #11249\n   */\n\n  if (val / Math.pow(10, exp) >= 10) {\n    exp++;\n  }\n\n  return exp;\n}\n/**\n * find a “nice” number approximately equal to x. Round the number if round = true,\n * take ceiling if round = false. The primary observation is that the “nicest”\n * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.\n *\n * See \"Nice Numbers for Graph Labels\" of Graphic Gems.\n *\n * @param  val Non-negative value.\n * @param  round\n * @return Niced number\n */\n\nfunction nice(val, round) {\n  var exponent = quantityExponent(val);\n  var exp10 = Math.pow(10, exponent);\n  var f = val / exp10; // 1 <= f < 10\n\n  var nf;\n\n  if (round) {\n    if (f < 1.5) {\n      nf = 1;\n    } else if (f < 2.5) {\n      nf = 2;\n    } else if (f < 4) {\n      nf = 3;\n    } else if (f < 7) {\n      nf = 5;\n    } else {\n      nf = 10;\n    }\n  } else {\n    if (f < 1) {\n      nf = 1;\n    } else if (f < 2) {\n      nf = 2;\n    } else if (f < 3) {\n      nf = 3;\n    } else if (f < 5) {\n      nf = 5;\n    } else {\n      nf = 10;\n    }\n  }\n\n  val = nf * exp10; // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).\n  // 20 is the uppper bound of toFixed.\n\n  return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;\n}\n/**\n * This code was copied from \"d3.js\"\n * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.\n * See the license statement at the head of this file.\n * @param ascArr\n */\n\nfunction quantile(ascArr, p) {\n  var H = (ascArr.length - 1) * p + 1;\n  var h = Math.floor(H);\n  var v = +ascArr[h - 1];\n  var e = H - h;\n  return e ? v + e * (ascArr[h] - v) : v;\n}\n/**\n * Order intervals asc, and split them when overlap.\n * expect(numberUtil.reformIntervals([\n *     {interval: [18, 62], close: [1, 1]},\n *     {interval: [-Infinity, -70], close: [0, 0]},\n *     {interval: [-70, -26], close: [1, 1]},\n *     {interval: [-26, 18], close: [1, 1]},\n *     {interval: [62, 150], close: [1, 1]},\n *     {interval: [106, 150], close: [1, 1]},\n *     {interval: [150, Infinity], close: [0, 0]}\n * ])).toEqual([\n *     {interval: [-Infinity, -70], close: [0, 0]},\n *     {interval: [-70, -26], close: [1, 1]},\n *     {interval: [-26, 18], close: [0, 1]},\n *     {interval: [18, 62], close: [0, 1]},\n *     {interval: [62, 150], close: [0, 1]},\n *     {interval: [150, Infinity], close: [0, 0]}\n * ]);\n * @param list, where `close` mean open or close\n *        of the interval, and Infinity can be used.\n * @return The origin list, which has been reformed.\n */\n\nfunction reformIntervals(list) {\n  list.sort(function (a, b) {\n    return littleThan(a, b, 0) ? -1 : 1;\n  });\n  var curr = -Infinity;\n  var currClose = 1;\n\n  for (var i = 0; i < list.length;) {\n    var interval = list[i].interval;\n    var close_1 = list[i].close;\n\n    for (var lg = 0; lg < 2; lg++) {\n      if (interval[lg] <= curr) {\n        interval[lg] = curr;\n        close_1[lg] = !lg ? 1 - currClose : 1;\n      }\n\n      curr = interval[lg];\n      currClose = close_1[lg];\n    }\n\n    if (interval[0] === interval[1] && close_1[0] * close_1[1] !== 1) {\n      list.splice(i, 1);\n    } else {\n      i++;\n    }\n  }\n\n  return list;\n\n  function littleThan(a, b, lg) {\n    return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1));\n  }\n}\n/**\n * [Numeric is defined as]:\n *     `parseFloat(val) == val`\n * For example:\n * numeric:\n *     typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity,\n *     and they rounded by white-spaces or line-terminal like ' -123 \\n ' (see es spec)\n * not-numeric:\n *     null, undefined, [], {}, true, false, 'NaN', NaN, '123ab',\n *     empty string, string with only white-spaces or line-terminal (see es spec),\n *     0x12, '0x12', '-0x12', 012, '012', '-012',\n *     non-string, ...\n *\n * @test See full test cases in `test/ut/spec/util/number.js`.\n * @return Must be a typeof number. If not numeric, return NaN.\n */\n\nfunction numericToNumber(val) {\n  var valFloat = parseFloat(val);\n  return valFloat == val // eslint-disable-line eqeqeq\n  && (valFloat !== 0 || !isString(val) || val.indexOf('x') <= 0) // For case ' 0x0 '.\n  ? valFloat : NaN;\n}\n/**\n * Definition of \"numeric\": see `numericToNumber`.\n */\n\nfunction isNumeric(val) {\n  return !isNaN(numericToNumber(val));\n}\n/**\n * Use random base to prevent users hard code depending on\n * this auto generated marker id.\n * @return An positive integer.\n */\n\nfunction getRandomIdBase() {\n  return Math.round(Math.random() * 9);\n}\n/**\n * Get the greatest common divisor.\n *\n * @param {number} a one number\n * @param {number} b the other number\n */\n\nfunction getGreatestCommonDividor(a, b) {\n  if (b === 0) {\n    return a;\n  }\n\n  return getGreatestCommonDividor(b, a % b);\n}\n/**\n * Get the least common multiple.\n *\n * @param {number} a one number\n * @param {number} b the other number\n */\n\nfunction getLeastCommonMultiple(a, b) {\n  if (a == null) {\n    return b;\n  }\n\n  if (b == null) {\n    return a;\n  }\n\n  return a * b / getGreatestCommonDividor(a, b);\n}\n\nvar ECHARTS_PREFIX = '[ECharts] ';\nvar storedLogs = {};\nvar hasConsole = typeof console !== 'undefined' // eslint-disable-next-line\n&& console.warn && console.log;\n\nfunction outputLog(type, str, onlyOnce) {\n  if (hasConsole) {\n    if (onlyOnce) {\n      if (storedLogs[str]) {\n        return;\n      }\n\n      storedLogs[str] = true;\n    } // eslint-disable-next-line\n\n\n    console[type](ECHARTS_PREFIX + str);\n  }\n}\n\nfunction log(str, onlyOnce) {\n  outputLog('log', str, onlyOnce);\n}\nfunction warn(str, onlyOnce) {\n  outputLog('warn', str, onlyOnce);\n}\nfunction error(str, onlyOnce) {\n  outputLog('error', str, onlyOnce);\n}\nfunction deprecateLog(str) {\n  if (\"development\" !== 'production') {\n    // Not display duplicate message.\n    outputLog('warn', 'DEPRECATED: ' + str, true);\n  }\n}\nfunction deprecateReplaceLog(oldOpt, newOpt, scope) {\n  if (\"development\" !== 'production') {\n    deprecateLog((scope ? \"[\" + scope + \"]\" : '') + (oldOpt + \" is deprecated, use \" + newOpt + \" instead.\"));\n  }\n}\n/**\n * If in __DEV__ environment, get console printable message for users hint.\n * Parameters are separated by ' '.\n * @usage\n * makePrintable('This is an error on', someVar, someObj);\n *\n * @param hintInfo anything about the current execution context to hint users.\n * @throws Error\n */\n\nfunction makePrintable() {\n  var hintInfo = [];\n\n  for (var _i = 0; _i < arguments.length; _i++) {\n    hintInfo[_i] = arguments[_i];\n  }\n\n  var msg = '';\n\n  if (\"development\" !== 'production') {\n    // Fuzzy stringify for print.\n    // This code only exist in dev environment.\n    var makePrintableStringIfPossible_1 = function (val) {\n      return val === void 0 ? 'undefined' : val === Infinity ? 'Infinity' : val === -Infinity ? '-Infinity' : eqNaN(val) ? 'NaN' : val instanceof Date ? 'Date(' + val.toISOString() + ')' : isFunction(val) ? 'function () { ... }' : isRegExp(val) ? val + '' : null;\n    };\n\n    msg = map(hintInfo, function (arg) {\n      if (isString(arg)) {\n        // Print without quotation mark for some statement.\n        return arg;\n      } else {\n        var printableStr = makePrintableStringIfPossible_1(arg);\n\n        if (printableStr != null) {\n          return printableStr;\n        } else if (typeof JSON !== 'undefined' && JSON.stringify) {\n          try {\n            return JSON.stringify(arg, function (n, val) {\n              var printableStr = makePrintableStringIfPossible_1(val);\n              return printableStr == null ? val : printableStr;\n            }); // In most cases the info object is small, so do not line break.\n          } catch (err) {\n            return '?';\n          }\n        } else {\n          return '?';\n        }\n      }\n    }).join(' ');\n  }\n\n  return msg;\n}\n/**\n * @throws Error\n */\n\nfunction throwError(msg) {\n  throw new Error(msg);\n}\n\nfunction interpolateNumber$1(p0, p1, percent) {\n  return (p1 - p0) * percent + p0;\n}\n/**\n * Make the name displayable. But we should\n * make sure it is not duplicated with user\n * specified name, so use '\\0';\n */\n\n\nvar DUMMY_COMPONENT_NAME_PREFIX = 'series\\0';\nvar INTERNAL_COMPONENT_ID_PREFIX = '\\0_ec_\\0';\n/**\n * If value is not array, then translate it to array.\n * @param  {*} value\n * @return {Array} [value] or value\n */\n\nfunction normalizeToArray(value) {\n  return value instanceof Array ? value : value == null ? [] : [value];\n}\n/**\n * Sync default option between normal and emphasis like `position` and `show`\n * In case some one will write code like\n *     label: {\n *          show: false,\n *          position: 'outside',\n *          fontSize: 18\n *     },\n *     emphasis: {\n *          label: { show: true }\n *     }\n */\n\nfunction defaultEmphasis(opt, key, subOpts) {\n  // Caution: performance sensitive.\n  if (opt) {\n    opt[key] = opt[key] || {};\n    opt.emphasis = opt.emphasis || {};\n    opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal\n\n    for (var i = 0, len = subOpts.length; i < len; i++) {\n      var subOptName = subOpts[i];\n\n      if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) {\n        opt.emphasis[key][subOptName] = opt[key][subOptName];\n      }\n    }\n  }\n}\nvar TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([\n//     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',\n//     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',\n//     // FIXME: deprecated, check and remove it.\n//     'textStyle'\n// ]);\n\n/**\n * The method does not ensure performance.\n * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n * This helper method retrieves value from data.\n */\n\nfunction getDataItemValue(dataItem) {\n  return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;\n}\n/**\n * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n * This helper method determine if dataItem has extra option besides value\n */\n\nfunction isDataItemOption(dataItem) {\n  return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array\n  // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));\n}\n/**\n * Mapping to existings for merge.\n *\n * Mode \"normalMege\":\n *     The mapping result (merge result) will keep the order of the existing\n *     component, rather than the order of new option. Because we should ensure\n *     some specified index reference (like xAxisIndex) keep work.\n *     And in most cases, \"merge option\" is used to update partial option but not\n *     be expected to change the order.\n *\n * Mode \"replaceMege\":\n *     (1) Only the id mapped components will be merged.\n *     (2) Other existing components (except internal components) will be removed.\n *     (3) Other new options will be used to create new component.\n *     (4) The index of the existing components will not be modified.\n *     That means their might be \"hole\" after the removal.\n *     The new components are created first at those available index.\n *\n * Mode \"replaceAll\":\n *     This mode try to support that reproduce an echarts instance from another\n *     echarts instance (via `getOption`) in some simple cases.\n *     In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`,\n *     which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is,\n *     the \"hole\" in `newCmptOptions` will also be kept.\n *     On the contrary, other modes try best to eliminate holes.\n *     PENDING: This is an experimental mode yet.\n *\n * @return See the comment of <MappingResult>.\n */\n\nfunction mappingToExists(existings, newCmptOptions, mode) {\n  var isNormalMergeMode = mode === 'normalMerge';\n  var isReplaceMergeMode = mode === 'replaceMerge';\n  var isReplaceAllMode = mode === 'replaceAll';\n  existings = existings || [];\n  newCmptOptions = (newCmptOptions || []).slice();\n  var existingIdIdxMap = createHashMap(); // Validate id and name on user input option.\n\n  each(newCmptOptions, function (cmptOption, index) {\n    if (!isObject(cmptOption)) {\n      newCmptOptions[index] = null;\n      return;\n    }\n\n    if (\"development\" !== 'production') {\n      // There is some legacy case that name is set as `false`.\n      // But should work normally rather than throw error.\n      if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) {\n        warnInvalidateIdOrName(cmptOption.id);\n      }\n\n      if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) {\n        warnInvalidateIdOrName(cmptOption.name);\n      }\n    }\n  });\n  var result = prepareResult(existings, existingIdIdxMap, mode);\n\n  if (isNormalMergeMode || isReplaceMergeMode) {\n    mappingById(result, existings, existingIdIdxMap, newCmptOptions);\n  }\n\n  if (isNormalMergeMode) {\n    mappingByName(result, newCmptOptions);\n  }\n\n  if (isNormalMergeMode || isReplaceMergeMode) {\n    mappingByIndex(result, newCmptOptions, isReplaceMergeMode);\n  } else if (isReplaceAllMode) {\n    mappingInReplaceAllMode(result, newCmptOptions);\n  }\n\n  makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the\n  // forEach will omit those items and result in incorrect result.\n\n  return result;\n}\n\nfunction prepareResult(existings, existingIdIdxMap, mode) {\n  var result = [];\n\n  if (mode === 'replaceAll') {\n    return result;\n  } // Do not use native `map` to in case that the array `existings`\n  // contains elided items, which will be omitted.\n\n\n  for (var index = 0; index < existings.length; index++) {\n    var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined.\n\n    if (existing && existing.id != null) {\n      existingIdIdxMap.set(existing.id, index);\n    } // For non-internal-componnets:\n    //     Mode \"normalMerge\": all existings kept.\n    //     Mode \"replaceMerge\": all existing removed unless mapped by id.\n    // For internal-components:\n    //     go with \"replaceMerge\" approach in both mode.\n\n\n    result.push({\n      existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing,\n      newOption: null,\n      keyInfo: null,\n      brandNew: null\n    });\n  }\n\n  return result;\n}\n\nfunction mappingById(result, existings, existingIdIdxMap, newCmptOptions) {\n  // Mapping by id if specified.\n  each(newCmptOptions, function (cmptOption, index) {\n    if (!cmptOption || cmptOption.id == null) {\n      return;\n    }\n\n    var optionId = makeComparableKey(cmptOption.id);\n    var existingIdx = existingIdIdxMap.get(optionId);\n\n    if (existingIdx != null) {\n      var resultItem = result[existingIdx];\n      assert(!resultItem.newOption, 'Duplicated option on id \"' + optionId + '\".');\n      resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to\n      // the existings rather than creating new component model.\n\n      resultItem.existing = existings[existingIdx];\n      newCmptOptions[index] = null;\n    }\n  });\n}\n\nfunction mappingByName(result, newCmptOptions) {\n  // Mapping by name if specified.\n  each(newCmptOptions, function (cmptOption, index) {\n    if (!cmptOption || cmptOption.name == null) {\n      return;\n    }\n\n    for (var i = 0; i < result.length; i++) {\n      var existing = result[i].existing;\n\n      if (!result[i].newOption // Consider name: two map to one.\n      // Can not match when both ids existing but different.\n      && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {\n        result[i].newOption = cmptOption;\n        newCmptOptions[index] = null;\n        return;\n      }\n    }\n  });\n}\n\nfunction mappingByIndex(result, newCmptOptions, brandNew) {\n  each(newCmptOptions, function (cmptOption) {\n    if (!cmptOption) {\n      return;\n    } // Find the first place that not mapped by id and not internal component (consider the \"hole\").\n\n\n    var resultItem;\n    var nextIdx = 0;\n\n    while ( // Be `!resultItem` only when `nextIdx >= result.length`.\n    (resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because\n    // after mapping performed, model will always be assigned with an id if user not given.\n    // After that all models have id.\n    // (2) If new option has id, it can only set to a hole or append to the last. It should\n    // not be merged to the existings with different id. Because id should not be overwritten.\n    // (3) Name can be overwritten, because axis use name as 'show label text'.\n    resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode \"replaceMerge\", here no not-mapped-non-internal-existing.\n    resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {\n      nextIdx++;\n    }\n\n    if (resultItem) {\n      resultItem.newOption = cmptOption;\n      resultItem.brandNew = brandNew;\n    } else {\n      result.push({\n        newOption: cmptOption,\n        brandNew: brandNew,\n        existing: null,\n        keyInfo: null\n      });\n    }\n\n    nextIdx++;\n  });\n}\n\nfunction mappingInReplaceAllMode(result, newCmptOptions) {\n  each(newCmptOptions, function (cmptOption) {\n    // The feature \"reproduce\" requires \"hole\" will also reproduced\n    // in case that component index referring are broken.\n    result.push({\n      newOption: cmptOption,\n      brandNew: true,\n      existing: null,\n      keyInfo: null\n    });\n  });\n}\n/**\n * Make id and name for mapping result (result of mappingToExists)\n * into `keyInfo` field.\n */\n\n\nfunction makeIdAndName(mapResult) {\n  // We use this id to hash component models and view instances\n  // in echarts. id can be specified by user, or auto generated.\n  // The id generation rule ensures new view instance are able\n  // to mapped to old instance when setOption are called in\n  // no-merge mode. So we generate model id by name and plus\n  // type in view id.\n  // name can be duplicated among components, which is convenient\n  // to specify multi components (like series) by one name.\n  // Ensure that each id is distinct.\n  var idMap = createHashMap();\n  each(mapResult, function (item) {\n    var existing = item.existing;\n    existing && idMap.set(existing.id, item);\n  });\n  each(mapResult, function (item) {\n    var opt = item.newOption; // Force ensure id not duplicated.\n\n    assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));\n    opt && opt.id != null && idMap.set(opt.id, item);\n    !item.keyInfo && (item.keyInfo = {});\n  }); // Make name and id.\n\n  each(mapResult, function (item, index) {\n    var existing = item.existing;\n    var opt = item.newOption;\n    var keyInfo = item.keyInfo;\n\n    if (!isObject(opt)) {\n      return;\n    } // Name can be overwritten. Consider case: axis.name = '20km'.\n    // But id generated by name will not be changed, which affect\n    // only in that case: setOption with 'not merge mode' and view\n    // instance will be recreated, which can be accepted.\n\n\n    keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid that different series has the same name,\n    // because name may be used like in color pallet.\n    : DUMMY_COMPONENT_NAME_PREFIX + index;\n\n    if (existing) {\n      keyInfo.id = makeComparableKey(existing.id);\n    } else if (opt.id != null) {\n      keyInfo.id = makeComparableKey(opt.id);\n    } else {\n      // Consider this situatoin:\n      //  optionA: [{name: 'a'}, {name: 'a'}, {..}]\n      //  optionB [{..}, {name: 'a'}, {name: 'a'}]\n      // Series with the same name between optionA and optionB\n      // should be mapped.\n      var idNum = 0;\n\n      do {\n        keyInfo.id = '\\0' + keyInfo.name + '\\0' + idNum++;\n      } while (idMap.get(keyInfo.id));\n    }\n\n    idMap.set(keyInfo.id, item);\n  });\n}\n\nfunction keyExistAndEqual(attr, obj1, obj2) {\n  var key1 = convertOptionIdName(obj1[attr], null);\n  var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number.\n\n  return key1 != null && key2 != null && key1 === key2;\n}\n/**\n * @return return null if not exist.\n */\n\n\nfunction makeComparableKey(val) {\n  if (\"development\" !== 'production') {\n    if (val == null) {\n      throw new Error();\n    }\n  }\n\n  return convertOptionIdName(val, '');\n}\n\nfunction convertOptionIdName(idOrName, defaultValue) {\n  if (idOrName == null) {\n    return defaultValue;\n  }\n\n  return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue;\n}\n\nfunction warnInvalidateIdOrName(idOrName) {\n  if (\"development\" !== 'production') {\n    warn('`' + idOrName + '` is invalid id or name. Must be a string or number.');\n  }\n}\n\nfunction isValidIdOrName(idOrName) {\n  return isStringSafe(idOrName) || isNumeric(idOrName);\n}\n\nfunction isNameSpecified(componentModel) {\n  var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.\n\n  return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));\n}\n/**\n * @public\n * @param {Object} cmptOption\n * @return {boolean}\n */\n\nfunction isComponentIdInternal(cmptOption) {\n  return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;\n}\nfunction makeInternalComponentId(idSuffix) {\n  return INTERNAL_COMPONENT_ID_PREFIX + idSuffix;\n}\nfunction setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) {\n  // Set mainType and complete subType.\n  each(mappingResult, function (item) {\n    var newOption = item.newOption;\n\n    if (isObject(newOption)) {\n      item.keyInfo.mainType = mainType;\n      item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor);\n    }\n  });\n}\n\nfunction determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) {\n  var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.\n  : componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType\n\n  return subType;\n}\n/**\n * A helper for removing duplicate items between batchA and batchB,\n * and in themselves, and categorize by series.\n *\n * @param batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n * @param batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n * @return result: [resultBatchA, resultBatchB]\n */\n\n\nfunction compressBatches(batchA, batchB) {\n  var mapA = {};\n  var mapB = {};\n  makeMap(batchA || [], mapA);\n  makeMap(batchB || [], mapB, mapA);\n  return [mapToArray(mapA), mapToArray(mapB)];\n\n  function makeMap(sourceBatch, map, otherMap) {\n    for (var i = 0, len = sourceBatch.length; i < len; i++) {\n      var seriesId = convertOptionIdName(sourceBatch[i].seriesId, null);\n\n      if (seriesId == null) {\n        return;\n      }\n\n      var dataIndices = normalizeToArray(sourceBatch[i].dataIndex);\n      var otherDataIndices = otherMap && otherMap[seriesId];\n\n      for (var j = 0, lenj = dataIndices.length; j < lenj; j++) {\n        var dataIndex = dataIndices[j];\n\n        if (otherDataIndices && otherDataIndices[dataIndex]) {\n          otherDataIndices[dataIndex] = null;\n        } else {\n          (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1;\n        }\n      }\n    }\n  }\n\n  function mapToArray(map, isData) {\n    var result = [];\n\n    for (var i in map) {\n      if (map.hasOwnProperty(i) && map[i] != null) {\n        if (isData) {\n          result.push(+i);\n        } else {\n          var dataIndices = mapToArray(map[i], true);\n          dataIndices.length && result.push({\n            seriesId: i,\n            dataIndex: dataIndices\n          });\n        }\n      }\n    }\n\n    return result;\n  }\n}\n/**\n * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name\n *                         each of which can be Array or primary type.\n * @return dataIndex If not found, return undefined/null.\n */\n\nfunction queryDataIndex(data, payload) {\n  if (payload.dataIndexInside != null) {\n    return payload.dataIndexInside;\n  } else if (payload.dataIndex != null) {\n    return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) {\n      return data.indexOfRawIndex(value);\n    }) : data.indexOfRawIndex(payload.dataIndex);\n  } else if (payload.name != null) {\n    return isArray(payload.name) ? map(payload.name, function (value) {\n      return data.indexOfName(value);\n    }) : data.indexOfName(payload.name);\n  }\n}\n/**\n * Enable property storage to any host object.\n * Notice: Serialization is not supported.\n *\n * For example:\n * let inner = zrUitl.makeInner();\n *\n * function some1(hostObj) {\n *      inner(hostObj).someProperty = 1212;\n *      ...\n * }\n * function some2() {\n *      let fields = inner(this);\n *      fields.someProperty1 = 1212;\n *      fields.someProperty2 = 'xx';\n *      ...\n * }\n *\n * @return {Function}\n */\n\nfunction makeInner() {\n  var key = '__ec_inner_' + innerUniqueIndex++;\n  return function (hostObj) {\n    return hostObj[key] || (hostObj[key] = {});\n  };\n}\nvar innerUniqueIndex = getRandomIdBase();\n/**\n * The same behavior as `component.getReferringComponents`.\n */\n\nfunction parseFinder(ecModel, finderInput, opt) {\n  var _a = preParseFinder(finderInput, opt),\n      mainTypeSpecified = _a.mainTypeSpecified,\n      queryOptionMap = _a.queryOptionMap,\n      others = _a.others;\n\n  var result = others;\n  var defaultMainType = opt ? opt.defaultMainType : null;\n\n  if (!mainTypeSpecified && defaultMainType) {\n    queryOptionMap.set(defaultMainType, {});\n  }\n\n  queryOptionMap.each(function (queryOption, mainType) {\n    var queryResult = queryReferringComponents(ecModel, mainType, queryOption, {\n      useDefault: defaultMainType === mainType,\n      enableAll: opt && opt.enableAll != null ? opt.enableAll : true,\n      enableNone: opt && opt.enableNone != null ? opt.enableNone : true\n    });\n    result[mainType + 'Models'] = queryResult.models;\n    result[mainType + 'Model'] = queryResult.models[0];\n  });\n  return result;\n}\nfunction preParseFinder(finderInput, opt) {\n  var finder;\n\n  if (isString(finderInput)) {\n    var obj = {};\n    obj[finderInput + 'Index'] = 0;\n    finder = obj;\n  } else {\n    finder = finderInput;\n  }\n\n  var queryOptionMap = createHashMap();\n  var others = {};\n  var mainTypeSpecified = false;\n  each(finder, function (value, key) {\n    // Exclude 'dataIndex' and other illgal keys.\n    if (key === 'dataIndex' || key === 'dataIndexInside') {\n      others[key] = value;\n      return;\n    }\n\n    var parsedKey = key.match(/^(\\w+)(Index|Id|Name)$/) || [];\n    var mainType = parsedKey[1];\n    var queryType = (parsedKey[2] || '').toLowerCase();\n\n    if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) {\n      return;\n    }\n\n    mainTypeSpecified = mainTypeSpecified || !!mainType;\n    var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {});\n    queryOption[queryType] = value;\n  });\n  return {\n    mainTypeSpecified: mainTypeSpecified,\n    queryOptionMap: queryOptionMap,\n    others: others\n  };\n}\nvar SINGLE_REFERRING = {\n  useDefault: true,\n  enableAll: false,\n  enableNone: false\n};\nvar MULTIPLE_REFERRING = {\n  useDefault: false,\n  enableAll: true,\n  enableNone: true\n};\nfunction queryReferringComponents(ecModel, mainType, userOption, opt) {\n  opt = opt || SINGLE_REFERRING;\n  var indexOption = userOption.index;\n  var idOption = userOption.id;\n  var nameOption = userOption.name;\n  var result = {\n    models: null,\n    specified: indexOption != null || idOption != null || nameOption != null\n  };\n\n  if (!result.specified) {\n    // Use the first as default if `useDefault`.\n    var firstCmpt = void 0;\n    result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : [];\n    return result;\n  }\n\n  if (indexOption === 'none' || indexOption === false) {\n    assert(opt.enableNone, '`\"none\"` or `false` is not a valid value on index option.');\n    result.models = [];\n    return result;\n  } // `queryComponents` will return all components if\n  // both all of index/id/name are null/undefined.\n\n\n  if (indexOption === 'all') {\n    assert(opt.enableAll, '`\"all\"` is not a valid value on index option.');\n    indexOption = idOption = nameOption = null;\n  }\n\n  result.models = ecModel.queryComponents({\n    mainType: mainType,\n    index: indexOption,\n    id: idOption,\n    name: nameOption\n  });\n  return result;\n}\nfunction setAttribute(dom, key, value) {\n  dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value;\n}\nfunction getAttribute(dom, key) {\n  return dom.getAttribute ? dom.getAttribute(key) : dom[key];\n}\nfunction getTooltipRenderMode(renderModeOption) {\n  if (renderModeOption === 'auto') {\n    // Using html when `document` exists, use richText otherwise\n    return env.domSupported ? 'html' : 'richText';\n  } else {\n    return renderModeOption || 'html';\n  }\n}\n/**\n * Group a list by key.\n */\n\nfunction groupData(array, getKey // return key\n) {\n  var buckets = createHashMap();\n  var keys = [];\n  each(array, function (item) {\n    var key = getKey(item);\n    (buckets.get(key) || (keys.push(key), buckets.set(key, []))).push(item);\n  });\n  return {\n    keys: keys,\n    buckets: buckets\n  };\n}\n/**\n * Interpolate raw values of a series with percent\n *\n * @param data         data\n * @param labelModel   label model of the text element\n * @param sourceValue  start value. May be null/undefined when init.\n * @param targetValue  end value\n * @param percent      0~1 percentage; 0 uses start value while 1 uses end value\n * @return             interpolated values\n *                     If `sourceValue` and `targetValue` are `number`, return `number`.\n *                     If `sourceValue` and `targetValue` are `string`, return `string`.\n *                     If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`.\n *                     Other cases do not supported.\n */\n\nfunction interpolateRawValues(data, precision, sourceValue, targetValue, percent) {\n  var isAutoPrecision = precision == null || precision === 'auto';\n\n  if (targetValue == null) {\n    return targetValue;\n  }\n\n  if (isNumber(targetValue)) {\n    var value = interpolateNumber$1(sourceValue || 0, targetValue, percent);\n    return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision);\n  } else if (isString(targetValue)) {\n    return percent < 1 ? sourceValue : targetValue;\n  } else {\n    var interpolated = [];\n    var leftArr = sourceValue;\n    var rightArr = targetValue;\n    var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length);\n\n    for (var i = 0; i < length_1; ++i) {\n      var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims\n\n      if (info && info.type === 'ordinal') {\n        // In init, there is no `sourceValue`, but should better not to get undefined result.\n        interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i];\n      } else {\n        var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0;\n        var rightVal = rightArr[i];\n        var value = interpolateNumber$1(leftVal, rightVal, percent);\n        interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision);\n      }\n    }\n\n    return interpolated;\n  }\n}\n\nvar TYPE_DELIMITER = '.';\nvar IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';\nvar IS_EXTENDED_CLASS = '___EC__EXTENDED_CLASS___';\n/**\n * Notice, parseClassType('') should returns {main: '', sub: ''}\n * @public\n */\n\nfunction parseClassType(componentType) {\n  var ret = {\n    main: '',\n    sub: ''\n  };\n\n  if (componentType) {\n    var typeArr = componentType.split(TYPE_DELIMITER);\n    ret.main = typeArr[0] || '';\n    ret.sub = typeArr[1] || '';\n  }\n\n  return ret;\n}\n/**\n * @public\n */\n\nfunction checkClassType(componentType) {\n  assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType \"' + componentType + '\" illegal');\n}\n\nfunction isExtendedClass(clz) {\n  return !!(clz && clz[IS_EXTENDED_CLASS]);\n}\n/**\n * Implements `ExtendableConstructor` for `rootClz`.\n *\n * @usage\n * ```ts\n * class Xxx {}\n * type XxxConstructor = typeof Xxx & ExtendableConstructor\n * enableClassExtend(Xxx as XxxConstructor);\n * ```\n */\n\nfunction enableClassExtend(rootClz, mandatoryMethods) {\n  rootClz.$constructor = rootClz; // FIXME: not necessary?\n\n  rootClz.extend = function (proto) {\n    if (\"development\" !== 'production') {\n      each(mandatoryMethods, function (method) {\n        if (!proto[method]) {\n          console.warn('Method `' + method + '` should be implemented' + (proto.type ? ' in ' + proto.type : '') + '.');\n        }\n      });\n    }\n\n    var superClass = this;\n    var ExtendedClass;\n\n    if (isESClass(superClass)) {\n      ExtendedClass =\n      /** @class */\n      function (_super) {\n        __extends(class_1, _super);\n\n        function class_1() {\n          return _super.apply(this, arguments) || this;\n        }\n\n        return class_1;\n      }(superClass);\n    } else {\n      // For backward compat, we both support ts class inheritance and this\n      // \"extend\" approach.\n      // The constructor should keep the same behavior as ts class inheritance:\n      // If this constructor/$constructor is not declared, auto invoke the super\n      // constructor.\n      // If this constructor/$constructor is declared, it is responsible for\n      // calling the super constructor.\n      ExtendedClass = function () {\n        (proto.$constructor || superClass).apply(this, arguments);\n      };\n\n      inherits(ExtendedClass, this);\n    }\n\n    extend(ExtendedClass.prototype, proto);\n    ExtendedClass[IS_EXTENDED_CLASS] = true;\n    ExtendedClass.extend = this.extend;\n    ExtendedClass.superCall = superCall;\n    ExtendedClass.superApply = superApply;\n    ExtendedClass.superClass = superClass;\n    return ExtendedClass;\n  };\n}\n\nfunction isESClass(fn) {\n  return isFunction(fn) && /^class\\s/.test(Function.prototype.toString.call(fn));\n}\n/**\n * A work around to both support ts extend and this extend mechanism.\n * on sub-class.\n * @usage\n * ```ts\n * class Component { ... }\n * classUtil.enableClassExtend(Component);\n * classUtil.enableClassManagement(Component, {registerWhenExtend: true});\n *\n * class Series extends Component { ... }\n * // Without calling `markExtend`, `registerWhenExtend` will not work.\n * Component.markExtend(Series);\n * ```\n */\n\n\nfunction mountExtend(SubClz, SupperClz) {\n  SubClz.extend = SupperClz.extend;\n} // A random offset.\n\nvar classBase = Math.round(Math.random() * 10);\n/**\n * Implements `CheckableConstructor` for `target`.\n * Can not use instanceof, consider different scope by\n * cross domain or es module import in ec extensions.\n * Mount a method \"isInstance()\" to Clz.\n *\n * @usage\n * ```ts\n * class Xxx {}\n * type XxxConstructor = typeof Xxx & CheckableConstructor;\n * enableClassCheck(Xxx as XxxConstructor)\n * ```\n */\n\nfunction enableClassCheck(target) {\n  var classAttr = ['__\\0is_clz', classBase++].join('_');\n  target.prototype[classAttr] = true;\n\n  if (\"development\" !== 'production') {\n    assert(!target.isInstance, 'The method \"is\" can not be defined.');\n  }\n\n  target.isInstance = function (obj) {\n    return !!(obj && obj[classAttr]);\n  };\n} // superCall should have class info, which can not be fetched from 'this'.\n// Consider this case:\n// class A has method f,\n// class B inherits class A, overrides method f, f call superApply('f'),\n// class C inherits class B, does not override method f,\n// then when method of class C is called, dead loop occurred.\n\nfunction superCall(context, methodName) {\n  var args = [];\n\n  for (var _i = 2; _i < arguments.length; _i++) {\n    args[_i - 2] = arguments[_i];\n  }\n\n  return this.superClass.prototype[methodName].apply(context, args);\n}\n\nfunction superApply(context, methodName, args) {\n  return this.superClass.prototype[methodName].apply(context, args);\n}\n/**\n * Implements `ClassManager` for `target`\n *\n * @usage\n * ```ts\n * class Xxx {}\n * type XxxConstructor = typeof Xxx & ClassManager\n * enableClassManagement(Xxx as XxxConstructor);\n * ```\n */\n\n\nfunction enableClassManagement(target) {\n  /**\n   * Component model classes\n   * key: componentType,\n   * value:\n   *     componentClass, when componentType is 'a'\n   *     or Object.<subKey, componentClass>, when componentType is 'a.b'\n   */\n  var storage = {};\n\n  target.registerClass = function (clz) {\n    // `type` should not be a \"instance member\".\n    // If using TS class, should better declared as `static type = 'series.pie'`.\n    // otherwise users have to mount `type` on prototype manually.\n    // For backward compat and enable instance visit type via `this.type`,\n    // we still support fetch `type` from prototype.\n    var componentFullType = clz.type || clz.prototype.type;\n\n    if (componentFullType) {\n      checkClassType(componentFullType); // If only static type declared, we assign it to prototype mandatorily.\n\n      clz.prototype.type = componentFullType;\n      var componentTypeInfo = parseClassType(componentFullType);\n\n      if (!componentTypeInfo.sub) {\n        if (\"development\" !== 'production') {\n          if (storage[componentTypeInfo.main]) {\n            console.warn(componentTypeInfo.main + ' exists.');\n          }\n        }\n\n        storage[componentTypeInfo.main] = clz;\n      } else if (componentTypeInfo.sub !== IS_CONTAINER) {\n        var container = makeContainer(componentTypeInfo);\n        container[componentTypeInfo.sub] = clz;\n      }\n    }\n\n    return clz;\n  };\n\n  target.getClass = function (mainType, subType, throwWhenNotFound) {\n    var clz = storage[mainType];\n\n    if (clz && clz[IS_CONTAINER]) {\n      clz = subType ? clz[subType] : null;\n    }\n\n    if (throwWhenNotFound && !clz) {\n      throw new Error(!subType ? mainType + '.' + 'type should be specified.' : 'Component ' + mainType + '.' + (subType || '') + ' is used but not imported.');\n    }\n\n    return clz;\n  };\n\n  target.getClassesByMainType = function (componentType) {\n    var componentTypeInfo = parseClassType(componentType);\n    var result = [];\n    var obj = storage[componentTypeInfo.main];\n\n    if (obj && obj[IS_CONTAINER]) {\n      each(obj, function (o, type) {\n        type !== IS_CONTAINER && result.push(o);\n      });\n    } else {\n      result.push(obj);\n    }\n\n    return result;\n  };\n\n  target.hasClass = function (componentType) {\n    // Just consider componentType.main.\n    var componentTypeInfo = parseClassType(componentType);\n    return !!storage[componentTypeInfo.main];\n  };\n  /**\n   * @return Like ['aa', 'bb'], but can not be ['aa.xx']\n   */\n\n\n  target.getAllClassMainTypes = function () {\n    var types = [];\n    each(storage, function (obj, type) {\n      types.push(type);\n    });\n    return types;\n  };\n  /**\n   * If a main type is container and has sub types\n   */\n\n\n  target.hasSubTypes = function (componentType) {\n    var componentTypeInfo = parseClassType(componentType);\n    var obj = storage[componentTypeInfo.main];\n    return obj && obj[IS_CONTAINER];\n  };\n\n  function makeContainer(componentTypeInfo) {\n    var container = storage[componentTypeInfo.main];\n\n    if (!container || !container[IS_CONTAINER]) {\n      container = storage[componentTypeInfo.main] = {};\n      container[IS_CONTAINER] = true;\n    }\n\n    return container;\n  }\n} // /**\n//  * @param {string|Array.<string>} properties\n//  */\n// export function setReadOnly(obj, properties) {\n// FIXME It seems broken in IE8 simulation of IE11\n// if (!zrUtil.isArray(properties)) {\n//     properties = properties != null ? [properties] : [];\n// }\n// zrUtil.each(properties, function (prop) {\n//     let value = obj[prop];\n//     Object.defineProperty\n//         && Object.defineProperty(obj, prop, {\n//             value: value, writable: false\n//         });\n//     zrUtil.isArray(obj[prop])\n//         && Object.freeze\n//         && Object.freeze(obj[prop]);\n// });\n// }\n\nfunction makeStyleMapper(properties, ignoreParent) {\n  // Normalize\n  for (var i = 0; i < properties.length; i++) {\n    if (!properties[i][1]) {\n      properties[i][1] = properties[i][0];\n    }\n  }\n\n  ignoreParent = ignoreParent || false;\n  return function (model, excludes, includes) {\n    var style = {};\n\n    for (var i = 0; i < properties.length; i++) {\n      var propName = properties[i][1];\n\n      if (excludes && indexOf(excludes, propName) >= 0 || includes && indexOf(includes, propName) < 0) {\n        continue;\n      }\n\n      var val = model.getShallow(propName, ignoreParent);\n\n      if (val != null) {\n        style[properties[i][0]] = val;\n      }\n    } // TODO Text or image?\n\n\n    return style;\n  };\n}\n\nvar AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n// So do not transfer decal directly.\n];\nvar getAreaStyle = makeStyleMapper(AREA_STYLE_KEY_MAP);\n\nvar AreaStyleMixin =\n/** @class */\nfunction () {\n  function AreaStyleMixin() {}\n\n  AreaStyleMixin.prototype.getAreaStyle = function (excludes, includes) {\n    return getAreaStyle(this, excludes, includes);\n  };\n\n  return AreaStyleMixin;\n}();\n\nvar globalImageCache = new LRU(50);\nfunction findExistImage(newImageOrSrc) {\n    if (typeof newImageOrSrc === 'string') {\n        var cachedImgObj = globalImageCache.get(newImageOrSrc);\n        return cachedImgObj && cachedImgObj.image;\n    }\n    else {\n        return newImageOrSrc;\n    }\n}\nfunction createOrUpdateImage(newImageOrSrc, image, hostEl, onload, cbPayload) {\n    if (!newImageOrSrc) {\n        return image;\n    }\n    else if (typeof newImageOrSrc === 'string') {\n        if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {\n            return image;\n        }\n        var cachedImgObj = globalImageCache.get(newImageOrSrc);\n        var pendingWrap = { hostEl: hostEl, cb: onload, cbPayload: cbPayload };\n        if (cachedImgObj) {\n            image = cachedImgObj.image;\n            !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);\n        }\n        else {\n            image = platformApi.loadImage(newImageOrSrc, imageOnLoad, imageOnLoad);\n            image.__zrImageSrc = newImageOrSrc;\n            globalImageCache.put(newImageOrSrc, image.__cachedImgObj = {\n                image: image,\n                pending: [pendingWrap]\n            });\n        }\n        return image;\n    }\n    else {\n        return newImageOrSrc;\n    }\n}\nfunction imageOnLoad() {\n    var cachedImgObj = this.__cachedImgObj;\n    this.onload = this.onerror = this.__cachedImgObj = null;\n    for (var i = 0; i < cachedImgObj.pending.length; i++) {\n        var pendingWrap = cachedImgObj.pending[i];\n        var cb = pendingWrap.cb;\n        cb && cb(this, pendingWrap.cbPayload);\n        pendingWrap.hostEl.dirty();\n    }\n    cachedImgObj.pending.length = 0;\n}\nfunction isImageReady(image) {\n    return image && image.width && image.height;\n}\n\nvar STYLE_REG = /\\{([a-zA-Z0-9_]+)\\|([^}]*)\\}/g;\nfunction truncateText(text, containerWidth, font, ellipsis, options) {\n    if (!containerWidth) {\n        return '';\n    }\n    var textLines = (text + '').split('\\n');\n    options = prepareTruncateOptions(containerWidth, font, ellipsis, options);\n    for (var i = 0, len = textLines.length; i < len; i++) {\n        textLines[i] = truncateSingleLine(textLines[i], options);\n    }\n    return textLines.join('\\n');\n}\nfunction prepareTruncateOptions(containerWidth, font, ellipsis, options) {\n    options = options || {};\n    var preparedOpts = extend({}, options);\n    preparedOpts.font = font;\n    ellipsis = retrieve2(ellipsis, '...');\n    preparedOpts.maxIterations = retrieve2(options.maxIterations, 2);\n    var minChar = preparedOpts.minChar = retrieve2(options.minChar, 0);\n    preparedOpts.cnCharWidth = getWidth('国', font);\n    var ascCharWidth = preparedOpts.ascCharWidth = getWidth('a', font);\n    preparedOpts.placeholder = retrieve2(options.placeholder, '');\n    var contentWidth = containerWidth = Math.max(0, containerWidth - 1);\n    for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {\n        contentWidth -= ascCharWidth;\n    }\n    var ellipsisWidth = getWidth(ellipsis, font);\n    if (ellipsisWidth > contentWidth) {\n        ellipsis = '';\n        ellipsisWidth = 0;\n    }\n    contentWidth = containerWidth - ellipsisWidth;\n    preparedOpts.ellipsis = ellipsis;\n    preparedOpts.ellipsisWidth = ellipsisWidth;\n    preparedOpts.contentWidth = contentWidth;\n    preparedOpts.containerWidth = containerWidth;\n    return preparedOpts;\n}\nfunction truncateSingleLine(textLine, options) {\n    var containerWidth = options.containerWidth;\n    var font = options.font;\n    var contentWidth = options.contentWidth;\n    if (!containerWidth) {\n        return '';\n    }\n    var lineWidth = getWidth(textLine, font);\n    if (lineWidth <= containerWidth) {\n        return textLine;\n    }\n    for (var j = 0;; j++) {\n        if (lineWidth <= contentWidth || j >= options.maxIterations) {\n            textLine += options.ellipsis;\n            break;\n        }\n        var subLength = j === 0\n            ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)\n            : lineWidth > 0\n                ? Math.floor(textLine.length * contentWidth / lineWidth)\n                : 0;\n        textLine = textLine.substr(0, subLength);\n        lineWidth = getWidth(textLine, font);\n    }\n    if (textLine === '') {\n        textLine = options.placeholder;\n    }\n    return textLine;\n}\nfunction estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {\n    var width = 0;\n    var i = 0;\n    for (var len = text.length; i < len && width < contentWidth; i++) {\n        var charCode = text.charCodeAt(i);\n        width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;\n    }\n    return i;\n}\nfunction parsePlainText(text, style) {\n    text != null && (text += '');\n    var overflow = style.overflow;\n    var padding = style.padding;\n    var font = style.font;\n    var truncate = overflow === 'truncate';\n    var calculatedLineHeight = getLineHeight(font);\n    var lineHeight = retrieve2(style.lineHeight, calculatedLineHeight);\n    var bgColorDrawn = !!(style.backgroundColor);\n    var truncateLineOverflow = style.lineOverflow === 'truncate';\n    var width = style.width;\n    var lines;\n    if (width != null && (overflow === 'break' || overflow === 'breakAll')) {\n        lines = text ? wrapText(text, style.font, width, overflow === 'breakAll', 0).lines : [];\n    }\n    else {\n        lines = text ? text.split('\\n') : [];\n    }\n    var contentHeight = lines.length * lineHeight;\n    var height = retrieve2(style.height, contentHeight);\n    if (contentHeight > height && truncateLineOverflow) {\n        var lineCount = Math.floor(height / lineHeight);\n        lines = lines.slice(0, lineCount);\n    }\n    if (text && truncate && width != null) {\n        var options = prepareTruncateOptions(width, font, style.ellipsis, {\n            minChar: style.truncateMinChar,\n            placeholder: style.placeholder\n        });\n        for (var i = 0; i < lines.length; i++) {\n            lines[i] = truncateSingleLine(lines[i], options);\n        }\n    }\n    var outerHeight = height;\n    var contentWidth = 0;\n    for (var i = 0; i < lines.length; i++) {\n        contentWidth = Math.max(getWidth(lines[i], font), contentWidth);\n    }\n    if (width == null) {\n        width = contentWidth;\n    }\n    var outerWidth = contentWidth;\n    if (padding) {\n        outerHeight += padding[0] + padding[2];\n        outerWidth += padding[1] + padding[3];\n        width += padding[1] + padding[3];\n    }\n    if (bgColorDrawn) {\n        outerWidth = width;\n    }\n    return {\n        lines: lines,\n        height: height,\n        outerWidth: outerWidth,\n        outerHeight: outerHeight,\n        lineHeight: lineHeight,\n        calculatedLineHeight: calculatedLineHeight,\n        contentWidth: contentWidth,\n        contentHeight: contentHeight,\n        width: width\n    };\n}\nvar RichTextToken = (function () {\n    function RichTextToken() {\n    }\n    return RichTextToken;\n}());\nvar RichTextLine = (function () {\n    function RichTextLine(tokens) {\n        this.tokens = [];\n        if (tokens) {\n            this.tokens = tokens;\n        }\n    }\n    return RichTextLine;\n}());\nvar RichTextContentBlock = (function () {\n    function RichTextContentBlock() {\n        this.width = 0;\n        this.height = 0;\n        this.contentWidth = 0;\n        this.contentHeight = 0;\n        this.outerWidth = 0;\n        this.outerHeight = 0;\n        this.lines = [];\n    }\n    return RichTextContentBlock;\n}());\nfunction parseRichText(text, style) {\n    var contentBlock = new RichTextContentBlock();\n    text != null && (text += '');\n    if (!text) {\n        return contentBlock;\n    }\n    var topWidth = style.width;\n    var topHeight = style.height;\n    var overflow = style.overflow;\n    var wrapInfo = (overflow === 'break' || overflow === 'breakAll') && topWidth != null\n        ? { width: topWidth, accumWidth: 0, breakAll: overflow === 'breakAll' }\n        : null;\n    var lastIndex = STYLE_REG.lastIndex = 0;\n    var result;\n    while ((result = STYLE_REG.exec(text)) != null) {\n        var matchedIndex = result.index;\n        if (matchedIndex > lastIndex) {\n            pushTokens(contentBlock, text.substring(lastIndex, matchedIndex), style, wrapInfo);\n        }\n        pushTokens(contentBlock, result[2], style, wrapInfo, result[1]);\n        lastIndex = STYLE_REG.lastIndex;\n    }\n    if (lastIndex < text.length) {\n        pushTokens(contentBlock, text.substring(lastIndex, text.length), style, wrapInfo);\n    }\n    var pendingList = [];\n    var calculatedHeight = 0;\n    var calculatedWidth = 0;\n    var stlPadding = style.padding;\n    var truncate = overflow === 'truncate';\n    var truncateLine = style.lineOverflow === 'truncate';\n    function finishLine(line, lineWidth, lineHeight) {\n        line.width = lineWidth;\n        line.lineHeight = lineHeight;\n        calculatedHeight += lineHeight;\n        calculatedWidth = Math.max(calculatedWidth, lineWidth);\n    }\n    outer: for (var i = 0; i < contentBlock.lines.length; i++) {\n        var line = contentBlock.lines[i];\n        var lineHeight = 0;\n        var lineWidth = 0;\n        for (var j = 0; j < line.tokens.length; j++) {\n            var token = line.tokens[j];\n            var tokenStyle = token.styleName && style.rich[token.styleName] || {};\n            var textPadding = token.textPadding = tokenStyle.padding;\n            var paddingH = textPadding ? textPadding[1] + textPadding[3] : 0;\n            var font = token.font = tokenStyle.font || style.font;\n            token.contentHeight = getLineHeight(font);\n            var tokenHeight = retrieve2(tokenStyle.height, token.contentHeight);\n            token.innerHeight = tokenHeight;\n            textPadding && (tokenHeight += textPadding[0] + textPadding[2]);\n            token.height = tokenHeight;\n            token.lineHeight = retrieve3(tokenStyle.lineHeight, style.lineHeight, tokenHeight);\n            token.align = tokenStyle && tokenStyle.align || style.align;\n            token.verticalAlign = tokenStyle && tokenStyle.verticalAlign || 'middle';\n            if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) {\n                if (j > 0) {\n                    line.tokens = line.tokens.slice(0, j);\n                    finishLine(line, lineWidth, lineHeight);\n                    contentBlock.lines = contentBlock.lines.slice(0, i + 1);\n                }\n                else {\n                    contentBlock.lines = contentBlock.lines.slice(0, i);\n                }\n                break outer;\n            }\n            var styleTokenWidth = tokenStyle.width;\n            var tokenWidthNotSpecified = styleTokenWidth == null || styleTokenWidth === 'auto';\n            if (typeof styleTokenWidth === 'string' && styleTokenWidth.charAt(styleTokenWidth.length - 1) === '%') {\n                token.percentWidth = styleTokenWidth;\n                pendingList.push(token);\n                token.contentWidth = getWidth(token.text, font);\n            }\n            else {\n                if (tokenWidthNotSpecified) {\n                    var textBackgroundColor = tokenStyle.backgroundColor;\n                    var bgImg = textBackgroundColor && textBackgroundColor.image;\n                    if (bgImg) {\n                        bgImg = findExistImage(bgImg);\n                        if (isImageReady(bgImg)) {\n                            token.width = Math.max(token.width, bgImg.width * tokenHeight / bgImg.height);\n                        }\n                    }\n                }\n                var remainTruncWidth = truncate && topWidth != null\n                    ? topWidth - lineWidth : null;\n                if (remainTruncWidth != null && remainTruncWidth < token.width) {\n                    if (!tokenWidthNotSpecified || remainTruncWidth < paddingH) {\n                        token.text = '';\n                        token.width = token.contentWidth = 0;\n                    }\n                    else {\n                        token.text = truncateText(token.text, remainTruncWidth - paddingH, font, style.ellipsis, { minChar: style.truncateMinChar });\n                        token.width = token.contentWidth = getWidth(token.text, font);\n                    }\n                }\n                else {\n                    token.contentWidth = getWidth(token.text, font);\n                }\n            }\n            token.width += paddingH;\n            lineWidth += token.width;\n            tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));\n        }\n        finishLine(line, lineWidth, lineHeight);\n    }\n    contentBlock.outerWidth = contentBlock.width = retrieve2(topWidth, calculatedWidth);\n    contentBlock.outerHeight = contentBlock.height = retrieve2(topHeight, calculatedHeight);\n    contentBlock.contentHeight = calculatedHeight;\n    contentBlock.contentWidth = calculatedWidth;\n    if (stlPadding) {\n        contentBlock.outerWidth += stlPadding[1] + stlPadding[3];\n        contentBlock.outerHeight += stlPadding[0] + stlPadding[2];\n    }\n    for (var i = 0; i < pendingList.length; i++) {\n        var token = pendingList[i];\n        var percentWidth = token.percentWidth;\n        token.width = parseInt(percentWidth, 10) / 100 * contentBlock.width;\n    }\n    return contentBlock;\n}\nfunction pushTokens(block, str, style, wrapInfo, styleName) {\n    var isEmptyStr = str === '';\n    var tokenStyle = styleName && style.rich[styleName] || {};\n    var lines = block.lines;\n    var font = tokenStyle.font || style.font;\n    var newLine = false;\n    var strLines;\n    var linesWidths;\n    if (wrapInfo) {\n        var tokenPadding = tokenStyle.padding;\n        var tokenPaddingH = tokenPadding ? tokenPadding[1] + tokenPadding[3] : 0;\n        if (tokenStyle.width != null && tokenStyle.width !== 'auto') {\n            var outerWidth_1 = parsePercent(tokenStyle.width, wrapInfo.width) + tokenPaddingH;\n            if (lines.length > 0) {\n                if (outerWidth_1 + wrapInfo.accumWidth > wrapInfo.width) {\n                    strLines = str.split('\\n');\n                    newLine = true;\n                }\n            }\n            wrapInfo.accumWidth = outerWidth_1;\n        }\n        else {\n            var res = wrapText(str, font, wrapInfo.width, wrapInfo.breakAll, wrapInfo.accumWidth);\n            wrapInfo.accumWidth = res.accumWidth + tokenPaddingH;\n            linesWidths = res.linesWidths;\n            strLines = res.lines;\n        }\n    }\n    else {\n        strLines = str.split('\\n');\n    }\n    for (var i = 0; i < strLines.length; i++) {\n        var text = strLines[i];\n        var token = new RichTextToken();\n        token.styleName = styleName;\n        token.text = text;\n        token.isLineHolder = !text && !isEmptyStr;\n        if (typeof tokenStyle.width === 'number') {\n            token.width = tokenStyle.width;\n        }\n        else {\n            token.width = linesWidths\n                ? linesWidths[i]\n                : getWidth(text, font);\n        }\n        if (!i && !newLine) {\n            var tokens = (lines[lines.length - 1] || (lines[0] = new RichTextLine())).tokens;\n            var tokensLen = tokens.length;\n            (tokensLen === 1 && tokens[0].isLineHolder)\n                ? (tokens[0] = token)\n                : ((text || !tokensLen || isEmptyStr) && tokens.push(token));\n        }\n        else {\n            lines.push(new RichTextLine([token]));\n        }\n    }\n}\nfunction isLatin(ch) {\n    var code = ch.charCodeAt(0);\n    return code >= 0x21 && code <= 0x17F;\n}\nvar breakCharMap = reduce(',&?/;] '.split(''), function (obj, ch) {\n    obj[ch] = true;\n    return obj;\n}, {});\nfunction isWordBreakChar(ch) {\n    if (isLatin(ch)) {\n        if (breakCharMap[ch]) {\n            return true;\n        }\n        return false;\n    }\n    return true;\n}\nfunction wrapText(text, font, lineWidth, isBreakAll, lastAccumWidth) {\n    var lines = [];\n    var linesWidths = [];\n    var line = '';\n    var currentWord = '';\n    var currentWordWidth = 0;\n    var accumWidth = 0;\n    for (var i = 0; i < text.length; i++) {\n        var ch = text.charAt(i);\n        if (ch === '\\n') {\n            if (currentWord) {\n                line += currentWord;\n                accumWidth += currentWordWidth;\n            }\n            lines.push(line);\n            linesWidths.push(accumWidth);\n            line = '';\n            currentWord = '';\n            currentWordWidth = 0;\n            accumWidth = 0;\n            continue;\n        }\n        var chWidth = getWidth(ch, font);\n        var inWord = isBreakAll ? false : !isWordBreakChar(ch);\n        if (!lines.length\n            ? lastAccumWidth + accumWidth + chWidth > lineWidth\n            : accumWidth + chWidth > lineWidth) {\n            if (!accumWidth) {\n                if (inWord) {\n                    lines.push(currentWord);\n                    linesWidths.push(currentWordWidth);\n                    currentWord = ch;\n                    currentWordWidth = chWidth;\n                }\n                else {\n                    lines.push(ch);\n                    linesWidths.push(chWidth);\n                }\n            }\n            else if (line || currentWord) {\n                if (inWord) {\n                    if (!line) {\n                        line = currentWord;\n                        currentWord = '';\n                        currentWordWidth = 0;\n                        accumWidth = currentWordWidth;\n                    }\n                    lines.push(line);\n                    linesWidths.push(accumWidth - currentWordWidth);\n                    currentWord += ch;\n                    currentWordWidth += chWidth;\n                    line = '';\n                    accumWidth = currentWordWidth;\n                }\n                else {\n                    if (currentWord) {\n                        line += currentWord;\n                        currentWord = '';\n                        currentWordWidth = 0;\n                    }\n                    lines.push(line);\n                    linesWidths.push(accumWidth);\n                    line = ch;\n                    accumWidth = chWidth;\n                }\n            }\n            continue;\n        }\n        accumWidth += chWidth;\n        if (inWord) {\n            currentWord += ch;\n            currentWordWidth += chWidth;\n        }\n        else {\n            if (currentWord) {\n                line += currentWord;\n                currentWord = '';\n                currentWordWidth = 0;\n            }\n            line += ch;\n        }\n    }\n    if (!lines.length && !line) {\n        line = text;\n        currentWord = '';\n        currentWordWidth = 0;\n    }\n    if (currentWord) {\n        line += currentWord;\n    }\n    if (line) {\n        lines.push(line);\n        linesWidths.push(accumWidth);\n    }\n    if (lines.length === 1) {\n        accumWidth += lastAccumWidth;\n    }\n    return {\n        accumWidth: accumWidth,\n        lines: lines,\n        linesWidths: linesWidths\n    };\n}\n\nvar STYLE_MAGIC_KEY = '__zr_style_' + Math.round((Math.random() * 10));\nvar DEFAULT_COMMON_STYLE = {\n    shadowBlur: 0,\n    shadowOffsetX: 0,\n    shadowOffsetY: 0,\n    shadowColor: '#000',\n    opacity: 1,\n    blend: 'source-over'\n};\nvar DEFAULT_COMMON_ANIMATION_PROPS = {\n    style: {\n        shadowBlur: true,\n        shadowOffsetX: true,\n        shadowOffsetY: true,\n        shadowColor: true,\n        opacity: true\n    }\n};\nDEFAULT_COMMON_STYLE[STYLE_MAGIC_KEY] = true;\nvar PRIMARY_STATES_KEYS$1 = ['z', 'z2', 'invisible'];\nvar PRIMARY_STATES_KEYS_IN_HOVER_LAYER = ['invisible'];\nvar Displayable = (function (_super) {\n    __extends(Displayable, _super);\n    function Displayable(props) {\n        return _super.call(this, props) || this;\n    }\n    Displayable.prototype._init = function (props) {\n        var keysArr = keys(props);\n        for (var i = 0; i < keysArr.length; i++) {\n            var key = keysArr[i];\n            if (key === 'style') {\n                this.useStyle(props[key]);\n            }\n            else {\n                _super.prototype.attrKV.call(this, key, props[key]);\n            }\n        }\n        if (!this.style) {\n            this.useStyle({});\n        }\n    };\n    Displayable.prototype.beforeBrush = function () { };\n    Displayable.prototype.afterBrush = function () { };\n    Displayable.prototype.innerBeforeBrush = function () { };\n    Displayable.prototype.innerAfterBrush = function () { };\n    Displayable.prototype.shouldBePainted = function (viewWidth, viewHeight, considerClipPath, considerAncestors) {\n        var m = this.transform;\n        if (this.ignore\n            || this.invisible\n            || this.style.opacity === 0\n            || (this.culling\n                && isDisplayableCulled(this, viewWidth, viewHeight))\n            || (m && !m[0] && !m[3])) {\n            return false;\n        }\n        if (considerClipPath && this.__clipPaths) {\n            for (var i = 0; i < this.__clipPaths.length; ++i) {\n                if (this.__clipPaths[i].isZeroArea()) {\n                    return false;\n                }\n            }\n        }\n        if (considerAncestors && this.parent) {\n            var parent_1 = this.parent;\n            while (parent_1) {\n                if (parent_1.ignore) {\n                    return false;\n                }\n                parent_1 = parent_1.parent;\n            }\n        }\n        return true;\n    };\n    Displayable.prototype.contain = function (x, y) {\n        return this.rectContain(x, y);\n    };\n    Displayable.prototype.traverse = function (cb, context) {\n        cb.call(context, this);\n    };\n    Displayable.prototype.rectContain = function (x, y) {\n        var coord = this.transformCoordToLocal(x, y);\n        var rect = this.getBoundingRect();\n        return rect.contain(coord[0], coord[1]);\n    };\n    Displayable.prototype.getPaintRect = function () {\n        var rect = this._paintRect;\n        if (!this._paintRect || this.__dirty) {\n            var transform = this.transform;\n            var elRect = this.getBoundingRect();\n            var style = this.style;\n            var shadowSize = style.shadowBlur || 0;\n            var shadowOffsetX = style.shadowOffsetX || 0;\n            var shadowOffsetY = style.shadowOffsetY || 0;\n            rect = this._paintRect || (this._paintRect = new BoundingRect(0, 0, 0, 0));\n            if (transform) {\n                BoundingRect.applyTransform(rect, elRect, transform);\n            }\n            else {\n                rect.copy(elRect);\n            }\n            if (shadowSize || shadowOffsetX || shadowOffsetY) {\n                rect.width += shadowSize * 2 + Math.abs(shadowOffsetX);\n                rect.height += shadowSize * 2 + Math.abs(shadowOffsetY);\n                rect.x = Math.min(rect.x, rect.x + shadowOffsetX - shadowSize);\n                rect.y = Math.min(rect.y, rect.y + shadowOffsetY - shadowSize);\n            }\n            var tolerance = this.dirtyRectTolerance;\n            if (!rect.isZero()) {\n                rect.x = Math.floor(rect.x - tolerance);\n                rect.y = Math.floor(rect.y - tolerance);\n                rect.width = Math.ceil(rect.width + 1 + tolerance * 2);\n                rect.height = Math.ceil(rect.height + 1 + tolerance * 2);\n            }\n        }\n        return rect;\n    };\n    Displayable.prototype.setPrevPaintRect = function (paintRect) {\n        if (paintRect) {\n            this._prevPaintRect = this._prevPaintRect || new BoundingRect(0, 0, 0, 0);\n            this._prevPaintRect.copy(paintRect);\n        }\n        else {\n            this._prevPaintRect = null;\n        }\n    };\n    Displayable.prototype.getPrevPaintRect = function () {\n        return this._prevPaintRect;\n    };\n    Displayable.prototype.animateStyle = function (loop) {\n        return this.animate('style', loop);\n    };\n    Displayable.prototype.updateDuringAnimation = function (targetKey) {\n        if (targetKey === 'style') {\n            this.dirtyStyle();\n        }\n        else {\n            this.markRedraw();\n        }\n    };\n    Displayable.prototype.attrKV = function (key, value) {\n        if (key !== 'style') {\n            _super.prototype.attrKV.call(this, key, value);\n        }\n        else {\n            if (!this.style) {\n                this.useStyle(value);\n            }\n            else {\n                this.setStyle(value);\n            }\n        }\n    };\n    Displayable.prototype.setStyle = function (keyOrObj, value) {\n        if (typeof keyOrObj === 'string') {\n            this.style[keyOrObj] = value;\n        }\n        else {\n            extend(this.style, keyOrObj);\n        }\n        this.dirtyStyle();\n        return this;\n    };\n    Displayable.prototype.dirtyStyle = function (notRedraw) {\n        if (!notRedraw) {\n            this.markRedraw();\n        }\n        this.__dirty |= STYLE_CHANGED_BIT;\n        if (this._rect) {\n            this._rect = null;\n        }\n    };\n    Displayable.prototype.dirty = function () {\n        this.dirtyStyle();\n    };\n    Displayable.prototype.styleChanged = function () {\n        return !!(this.__dirty & STYLE_CHANGED_BIT);\n    };\n    Displayable.prototype.styleUpdated = function () {\n        this.__dirty &= ~STYLE_CHANGED_BIT;\n    };\n    Displayable.prototype.createStyle = function (obj) {\n        return createObject(DEFAULT_COMMON_STYLE, obj);\n    };\n    Displayable.prototype.useStyle = function (obj) {\n        if (!obj[STYLE_MAGIC_KEY]) {\n            obj = this.createStyle(obj);\n        }\n        if (this.__inHover) {\n            this.__hoverStyle = obj;\n        }\n        else {\n            this.style = obj;\n        }\n        this.dirtyStyle();\n    };\n    Displayable.prototype.isStyleObject = function (obj) {\n        return obj[STYLE_MAGIC_KEY];\n    };\n    Displayable.prototype._innerSaveToNormal = function (toState) {\n        _super.prototype._innerSaveToNormal.call(this, toState);\n        var normalState = this._normalState;\n        if (toState.style && !normalState.style) {\n            normalState.style = this._mergeStyle(this.createStyle(), this.style);\n        }\n        this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS$1);\n    };\n    Displayable.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n        _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n        var needsRestoreToNormal = !(state && keepCurrentStates);\n        var targetStyle;\n        if (state && state.style) {\n            if (transition) {\n                if (keepCurrentStates) {\n                    targetStyle = state.style;\n                }\n                else {\n                    targetStyle = this._mergeStyle(this.createStyle(), normalState.style);\n                    this._mergeStyle(targetStyle, state.style);\n                }\n            }\n            else {\n                targetStyle = this._mergeStyle(this.createStyle(), keepCurrentStates ? this.style : normalState.style);\n                this._mergeStyle(targetStyle, state.style);\n            }\n        }\n        else if (needsRestoreToNormal) {\n            targetStyle = normalState.style;\n        }\n        if (targetStyle) {\n            if (transition) {\n                var sourceStyle = this.style;\n                this.style = this.createStyle(needsRestoreToNormal ? {} : sourceStyle);\n                if (needsRestoreToNormal) {\n                    var changedKeys = keys(sourceStyle);\n                    for (var i = 0; i < changedKeys.length; i++) {\n                        var key = changedKeys[i];\n                        if (key in targetStyle) {\n                            targetStyle[key] = targetStyle[key];\n                            this.style[key] = sourceStyle[key];\n                        }\n                    }\n                }\n                var targetKeys = keys(targetStyle);\n                for (var i = 0; i < targetKeys.length; i++) {\n                    var key = targetKeys[i];\n                    this.style[key] = this.style[key];\n                }\n                this._transitionState(stateName, {\n                    style: targetStyle\n                }, animationCfg, this.getAnimationStyleProps());\n            }\n            else {\n                this.useStyle(targetStyle);\n            }\n        }\n        var statesKeys = this.__inHover ? PRIMARY_STATES_KEYS_IN_HOVER_LAYER : PRIMARY_STATES_KEYS$1;\n        for (var i = 0; i < statesKeys.length; i++) {\n            var key = statesKeys[i];\n            if (state && state[key] != null) {\n                this[key] = state[key];\n            }\n            else if (needsRestoreToNormal) {\n                if (normalState[key] != null) {\n                    this[key] = normalState[key];\n                }\n            }\n        }\n    };\n    Displayable.prototype._mergeStates = function (states) {\n        var mergedState = _super.prototype._mergeStates.call(this, states);\n        var mergedStyle;\n        for (var i = 0; i < states.length; i++) {\n            var state = states[i];\n            if (state.style) {\n                mergedStyle = mergedStyle || {};\n                this._mergeStyle(mergedStyle, state.style);\n            }\n        }\n        if (mergedStyle) {\n            mergedState.style = mergedStyle;\n        }\n        return mergedState;\n    };\n    Displayable.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n        extend(targetStyle, sourceStyle);\n        return targetStyle;\n    };\n    Displayable.prototype.getAnimationStyleProps = function () {\n        return DEFAULT_COMMON_ANIMATION_PROPS;\n    };\n    Displayable.initDefaultProps = (function () {\n        var dispProto = Displayable.prototype;\n        dispProto.type = 'displayable';\n        dispProto.invisible = false;\n        dispProto.z = 0;\n        dispProto.z2 = 0;\n        dispProto.zlevel = 0;\n        dispProto.culling = false;\n        dispProto.cursor = 'pointer';\n        dispProto.rectHover = false;\n        dispProto.incremental = false;\n        dispProto._rect = null;\n        dispProto.dirtyRectTolerance = 0;\n        dispProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT;\n    })();\n    return Displayable;\n}(Element));\nvar tmpRect$1 = new BoundingRect(0, 0, 0, 0);\nvar viewRect = new BoundingRect(0, 0, 0, 0);\nfunction isDisplayableCulled(el, width, height) {\n    tmpRect$1.copy(el.getBoundingRect());\n    if (el.transform) {\n        tmpRect$1.applyTransform(el.transform);\n    }\n    viewRect.width = width;\n    viewRect.height = height;\n    return !tmpRect$1.intersect(viewRect);\n}\n\nvar mathMin$1 = Math.min;\nvar mathMax$1 = Math.max;\nvar mathSin = Math.sin;\nvar mathCos = Math.cos;\nvar PI2 = Math.PI * 2;\nvar start = create();\nvar end = create();\nvar extremity = create();\nfunction fromPoints(points, min, max) {\n    if (points.length === 0) {\n        return;\n    }\n    var p = points[0];\n    var left = p[0];\n    var right = p[0];\n    var top = p[1];\n    var bottom = p[1];\n    for (var i = 1; i < points.length; i++) {\n        p = points[i];\n        left = mathMin$1(left, p[0]);\n        right = mathMax$1(right, p[0]);\n        top = mathMin$1(top, p[1]);\n        bottom = mathMax$1(bottom, p[1]);\n    }\n    min[0] = left;\n    min[1] = top;\n    max[0] = right;\n    max[1] = bottom;\n}\nfunction fromLine(x0, y0, x1, y1, min, max) {\n    min[0] = mathMin$1(x0, x1);\n    min[1] = mathMin$1(y0, y1);\n    max[0] = mathMax$1(x0, x1);\n    max[1] = mathMax$1(y0, y1);\n}\nvar xDim = [];\nvar yDim = [];\nfunction fromCubic(x0, y0, x1, y1, x2, y2, x3, y3, min, max) {\n    var cubicExtrema$1 = cubicExtrema;\n    var cubicAt$1 = cubicAt;\n    var n = cubicExtrema$1(x0, x1, x2, x3, xDim);\n    min[0] = Infinity;\n    min[1] = Infinity;\n    max[0] = -Infinity;\n    max[1] = -Infinity;\n    for (var i = 0; i < n; i++) {\n        var x = cubicAt$1(x0, x1, x2, x3, xDim[i]);\n        min[0] = mathMin$1(x, min[0]);\n        max[0] = mathMax$1(x, max[0]);\n    }\n    n = cubicExtrema$1(y0, y1, y2, y3, yDim);\n    for (var i = 0; i < n; i++) {\n        var y = cubicAt$1(y0, y1, y2, y3, yDim[i]);\n        min[1] = mathMin$1(y, min[1]);\n        max[1] = mathMax$1(y, max[1]);\n    }\n    min[0] = mathMin$1(x0, min[0]);\n    max[0] = mathMax$1(x0, max[0]);\n    min[0] = mathMin$1(x3, min[0]);\n    max[0] = mathMax$1(x3, max[0]);\n    min[1] = mathMin$1(y0, min[1]);\n    max[1] = mathMax$1(y0, max[1]);\n    min[1] = mathMin$1(y3, min[1]);\n    max[1] = mathMax$1(y3, max[1]);\n}\nfunction fromQuadratic(x0, y0, x1, y1, x2, y2, min, max) {\n    var quadraticExtremum$1 = quadraticExtremum;\n    var quadraticAt$1 = quadraticAt;\n    var tx = mathMax$1(mathMin$1(quadraticExtremum$1(x0, x1, x2), 1), 0);\n    var ty = mathMax$1(mathMin$1(quadraticExtremum$1(y0, y1, y2), 1), 0);\n    var x = quadraticAt$1(x0, x1, x2, tx);\n    var y = quadraticAt$1(y0, y1, y2, ty);\n    min[0] = mathMin$1(x0, x2, x);\n    min[1] = mathMin$1(y0, y2, y);\n    max[0] = mathMax$1(x0, x2, x);\n    max[1] = mathMax$1(y0, y2, y);\n}\nfunction fromArc(x, y, rx, ry, startAngle, endAngle, anticlockwise, min$1, max$1) {\n    var vec2Min = min;\n    var vec2Max = max;\n    var diff = Math.abs(startAngle - endAngle);\n    if (diff % PI2 < 1e-4 && diff > 1e-4) {\n        min$1[0] = x - rx;\n        min$1[1] = y - ry;\n        max$1[0] = x + rx;\n        max$1[1] = y + ry;\n        return;\n    }\n    start[0] = mathCos(startAngle) * rx + x;\n    start[1] = mathSin(startAngle) * ry + y;\n    end[0] = mathCos(endAngle) * rx + x;\n    end[1] = mathSin(endAngle) * ry + y;\n    vec2Min(min$1, start, end);\n    vec2Max(max$1, start, end);\n    startAngle = startAngle % (PI2);\n    if (startAngle < 0) {\n        startAngle = startAngle + PI2;\n    }\n    endAngle = endAngle % (PI2);\n    if (endAngle < 0) {\n        endAngle = endAngle + PI2;\n    }\n    if (startAngle > endAngle && !anticlockwise) {\n        endAngle += PI2;\n    }\n    else if (startAngle < endAngle && anticlockwise) {\n        startAngle += PI2;\n    }\n    if (anticlockwise) {\n        var tmp = endAngle;\n        endAngle = startAngle;\n        startAngle = tmp;\n    }\n    for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {\n        if (angle > startAngle) {\n            extremity[0] = mathCos(angle) * rx + x;\n            extremity[1] = mathSin(angle) * ry + y;\n            vec2Min(min$1, extremity, min$1);\n            vec2Max(max$1, extremity, max$1);\n        }\n    }\n}\n\nvar CMD = {\n    M: 1,\n    L: 2,\n    C: 3,\n    Q: 4,\n    A: 5,\n    Z: 6,\n    R: 7\n};\nvar tmpOutX = [];\nvar tmpOutY = [];\nvar min$1 = [];\nvar max$1 = [];\nvar min2 = [];\nvar max2 = [];\nvar mathMin$2 = Math.min;\nvar mathMax$2 = Math.max;\nvar mathCos$1 = Math.cos;\nvar mathSin$1 = Math.sin;\nvar mathAbs = Math.abs;\nvar PI = Math.PI;\nvar PI2$1 = PI * 2;\nvar hasTypedArray = typeof Float32Array !== 'undefined';\nvar tmpAngles = [];\nfunction modPI2(radian) {\n    var n = Math.round(radian / PI * 1e8) / 1e8;\n    return (n % 2) * PI;\n}\nfunction normalizeArcAngles(angles, anticlockwise) {\n    var newStartAngle = modPI2(angles[0]);\n    if (newStartAngle < 0) {\n        newStartAngle += PI2$1;\n    }\n    var delta = newStartAngle - angles[0];\n    var newEndAngle = angles[1];\n    newEndAngle += delta;\n    if (!anticlockwise && newEndAngle - newStartAngle >= PI2$1) {\n        newEndAngle = newStartAngle + PI2$1;\n    }\n    else if (anticlockwise && newStartAngle - newEndAngle >= PI2$1) {\n        newEndAngle = newStartAngle - PI2$1;\n    }\n    else if (!anticlockwise && newStartAngle > newEndAngle) {\n        newEndAngle = newStartAngle + (PI2$1 - modPI2(newStartAngle - newEndAngle));\n    }\n    else if (anticlockwise && newStartAngle < newEndAngle) {\n        newEndAngle = newStartAngle - (PI2$1 - modPI2(newEndAngle - newStartAngle));\n    }\n    angles[0] = newStartAngle;\n    angles[1] = newEndAngle;\n}\nvar PathProxy = (function () {\n    function PathProxy(notSaveData) {\n        this.dpr = 1;\n        this._xi = 0;\n        this._yi = 0;\n        this._x0 = 0;\n        this._y0 = 0;\n        this._len = 0;\n        if (notSaveData) {\n            this._saveData = false;\n        }\n        if (this._saveData) {\n            this.data = [];\n        }\n    }\n    PathProxy.prototype.increaseVersion = function () {\n        this._version++;\n    };\n    PathProxy.prototype.getVersion = function () {\n        return this._version;\n    };\n    PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) {\n        segmentIgnoreThreshold = segmentIgnoreThreshold || 0;\n        if (segmentIgnoreThreshold > 0) {\n            this._ux = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sx) || 0;\n            this._uy = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sy) || 0;\n        }\n    };\n    PathProxy.prototype.setDPR = function (dpr) {\n        this.dpr = dpr;\n    };\n    PathProxy.prototype.setContext = function (ctx) {\n        this._ctx = ctx;\n    };\n    PathProxy.prototype.getContext = function () {\n        return this._ctx;\n    };\n    PathProxy.prototype.beginPath = function () {\n        this._ctx && this._ctx.beginPath();\n        this.reset();\n        return this;\n    };\n    PathProxy.prototype.reset = function () {\n        if (this._saveData) {\n            this._len = 0;\n        }\n        if (this._pathSegLen) {\n            this._pathSegLen = null;\n            this._pathLen = 0;\n        }\n        this._version++;\n    };\n    PathProxy.prototype.moveTo = function (x, y) {\n        this._drawPendingPt();\n        this.addData(CMD.M, x, y);\n        this._ctx && this._ctx.moveTo(x, y);\n        this._x0 = x;\n        this._y0 = y;\n        this._xi = x;\n        this._yi = y;\n        return this;\n    };\n    PathProxy.prototype.lineTo = function (x, y) {\n        var dx = mathAbs(x - this._xi);\n        var dy = mathAbs(y - this._yi);\n        var exceedUnit = dx > this._ux || dy > this._uy;\n        this.addData(CMD.L, x, y);\n        if (this._ctx && exceedUnit) {\n            this._ctx.lineTo(x, y);\n        }\n        if (exceedUnit) {\n            this._xi = x;\n            this._yi = y;\n            this._pendingPtDist = 0;\n        }\n        else {\n            var d2 = dx * dx + dy * dy;\n            if (d2 > this._pendingPtDist) {\n                this._pendingPtX = x;\n                this._pendingPtY = y;\n                this._pendingPtDist = d2;\n            }\n        }\n        return this;\n    };\n    PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) {\n        this._drawPendingPt();\n        this.addData(CMD.C, x1, y1, x2, y2, x3, y3);\n        if (this._ctx) {\n            this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n        }\n        this._xi = x3;\n        this._yi = y3;\n        return this;\n    };\n    PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) {\n        this._drawPendingPt();\n        this.addData(CMD.Q, x1, y1, x2, y2);\n        if (this._ctx) {\n            this._ctx.quadraticCurveTo(x1, y1, x2, y2);\n        }\n        this._xi = x2;\n        this._yi = y2;\n        return this;\n    };\n    PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n        this._drawPendingPt();\n        tmpAngles[0] = startAngle;\n        tmpAngles[1] = endAngle;\n        normalizeArcAngles(tmpAngles, anticlockwise);\n        startAngle = tmpAngles[0];\n        endAngle = tmpAngles[1];\n        var delta = endAngle - startAngle;\n        this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 0, anticlockwise ? 0 : 1);\n        this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n        this._xi = mathCos$1(endAngle) * r + cx;\n        this._yi = mathSin$1(endAngle) * r + cy;\n        return this;\n    };\n    PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) {\n        this._drawPendingPt();\n        if (this._ctx) {\n            this._ctx.arcTo(x1, y1, x2, y2, radius);\n        }\n        return this;\n    };\n    PathProxy.prototype.rect = function (x, y, w, h) {\n        this._drawPendingPt();\n        this._ctx && this._ctx.rect(x, y, w, h);\n        this.addData(CMD.R, x, y, w, h);\n        return this;\n    };\n    PathProxy.prototype.closePath = function () {\n        this._drawPendingPt();\n        this.addData(CMD.Z);\n        var ctx = this._ctx;\n        var x0 = this._x0;\n        var y0 = this._y0;\n        if (ctx) {\n            ctx.closePath();\n        }\n        this._xi = x0;\n        this._yi = y0;\n        return this;\n    };\n    PathProxy.prototype.fill = function (ctx) {\n        ctx && ctx.fill();\n        this.toStatic();\n    };\n    PathProxy.prototype.stroke = function (ctx) {\n        ctx && ctx.stroke();\n        this.toStatic();\n    };\n    PathProxy.prototype.len = function () {\n        return this._len;\n    };\n    PathProxy.prototype.setData = function (data) {\n        var len = data.length;\n        if (!(this.data && this.data.length === len) && hasTypedArray) {\n            this.data = new Float32Array(len);\n        }\n        for (var i = 0; i < len; i++) {\n            this.data[i] = data[i];\n        }\n        this._len = len;\n    };\n    PathProxy.prototype.appendPath = function (path) {\n        if (!(path instanceof Array)) {\n            path = [path];\n        }\n        var len = path.length;\n        var appendSize = 0;\n        var offset = this._len;\n        for (var i = 0; i < len; i++) {\n            appendSize += path[i].len();\n        }\n        if (hasTypedArray && (this.data instanceof Float32Array)) {\n            this.data = new Float32Array(offset + appendSize);\n        }\n        for (var i = 0; i < len; i++) {\n            var appendPathData = path[i].data;\n            for (var k = 0; k < appendPathData.length; k++) {\n                this.data[offset++] = appendPathData[k];\n            }\n        }\n        this._len = offset;\n    };\n    PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) {\n        if (!this._saveData) {\n            return;\n        }\n        var data = this.data;\n        if (this._len + arguments.length > data.length) {\n            this._expandData();\n            data = this.data;\n        }\n        for (var i = 0; i < arguments.length; i++) {\n            data[this._len++] = arguments[i];\n        }\n    };\n    PathProxy.prototype._drawPendingPt = function () {\n        if (this._pendingPtDist > 0) {\n            this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY);\n            this._pendingPtDist = 0;\n        }\n    };\n    PathProxy.prototype._expandData = function () {\n        if (!(this.data instanceof Array)) {\n            var newData = [];\n            for (var i = 0; i < this._len; i++) {\n                newData[i] = this.data[i];\n            }\n            this.data = newData;\n        }\n    };\n    PathProxy.prototype.toStatic = function () {\n        if (!this._saveData) {\n            return;\n        }\n        this._drawPendingPt();\n        var data = this.data;\n        if (data instanceof Array) {\n            data.length = this._len;\n            if (hasTypedArray && this._len > 11) {\n                this.data = new Float32Array(data);\n            }\n        }\n    };\n    PathProxy.prototype.getBoundingRect = function () {\n        min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;\n        max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;\n        var data = this.data;\n        var xi = 0;\n        var yi = 0;\n        var x0 = 0;\n        var y0 = 0;\n        var i;\n        for (i = 0; i < this._len;) {\n            var cmd = data[i++];\n            var isFirst = i === 1;\n            if (isFirst) {\n                xi = data[i];\n                yi = data[i + 1];\n                x0 = xi;\n                y0 = yi;\n            }\n            switch (cmd) {\n                case CMD.M:\n                    xi = x0 = data[i++];\n                    yi = y0 = data[i++];\n                    min2[0] = x0;\n                    min2[1] = y0;\n                    max2[0] = x0;\n                    max2[1] = y0;\n                    break;\n                case CMD.L:\n                    fromLine(xi, yi, data[i], data[i + 1], min2, max2);\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD.C:\n                    fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD.Q:\n                    fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD.A:\n                    var cx = data[i++];\n                    var cy = data[i++];\n                    var rx = data[i++];\n                    var ry = data[i++];\n                    var startAngle = data[i++];\n                    var endAngle = data[i++] + startAngle;\n                    i += 1;\n                    var anticlockwise = !data[i++];\n                    if (isFirst) {\n                        x0 = mathCos$1(startAngle) * rx + cx;\n                        y0 = mathSin$1(startAngle) * ry + cy;\n                    }\n                    fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);\n                    xi = mathCos$1(endAngle) * rx + cx;\n                    yi = mathSin$1(endAngle) * ry + cy;\n                    break;\n                case CMD.R:\n                    x0 = xi = data[i++];\n                    y0 = yi = data[i++];\n                    var width = data[i++];\n                    var height = data[i++];\n                    fromLine(x0, y0, x0 + width, y0 + height, min2, max2);\n                    break;\n                case CMD.Z:\n                    xi = x0;\n                    yi = y0;\n                    break;\n            }\n            min(min$1, min$1, min2);\n            max(max$1, max$1, max2);\n        }\n        if (i === 0) {\n            min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;\n        }\n        return new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]);\n    };\n    PathProxy.prototype._calculateLength = function () {\n        var data = this.data;\n        var len = this._len;\n        var ux = this._ux;\n        var uy = this._uy;\n        var xi = 0;\n        var yi = 0;\n        var x0 = 0;\n        var y0 = 0;\n        if (!this._pathSegLen) {\n            this._pathSegLen = [];\n        }\n        var pathSegLen = this._pathSegLen;\n        var pathTotalLen = 0;\n        var segCount = 0;\n        for (var i = 0; i < len;) {\n            var cmd = data[i++];\n            var isFirst = i === 1;\n            if (isFirst) {\n                xi = data[i];\n                yi = data[i + 1];\n                x0 = xi;\n                y0 = yi;\n            }\n            var l = -1;\n            switch (cmd) {\n                case CMD.M:\n                    xi = x0 = data[i++];\n                    yi = y0 = data[i++];\n                    break;\n                case CMD.L: {\n                    var x2 = data[i++];\n                    var y2 = data[i++];\n                    var dx = x2 - xi;\n                    var dy = y2 - yi;\n                    if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) {\n                        l = Math.sqrt(dx * dx + dy * dy);\n                        xi = x2;\n                        yi = y2;\n                    }\n                    break;\n                }\n                case CMD.C: {\n                    var x1 = data[i++];\n                    var y1 = data[i++];\n                    var x2 = data[i++];\n                    var y2 = data[i++];\n                    var x3 = data[i++];\n                    var y3 = data[i++];\n                    l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10);\n                    xi = x3;\n                    yi = y3;\n                    break;\n                }\n                case CMD.Q: {\n                    var x1 = data[i++];\n                    var y1 = data[i++];\n                    var x2 = data[i++];\n                    var y2 = data[i++];\n                    l = quadraticLength(xi, yi, x1, y1, x2, y2, 10);\n                    xi = x2;\n                    yi = y2;\n                    break;\n                }\n                case CMD.A:\n                    var cx = data[i++];\n                    var cy = data[i++];\n                    var rx = data[i++];\n                    var ry = data[i++];\n                    var startAngle = data[i++];\n                    var delta = data[i++];\n                    var endAngle = delta + startAngle;\n                    i += 1;\n                    var anticlockwise = !data[i++];\n                    if (isFirst) {\n                        x0 = mathCos$1(startAngle) * rx + cx;\n                        y0 = mathSin$1(startAngle) * ry + cy;\n                    }\n                    l = mathMax$2(rx, ry) * mathMin$2(PI2$1, Math.abs(delta));\n                    xi = mathCos$1(endAngle) * rx + cx;\n                    yi = mathSin$1(endAngle) * ry + cy;\n                    break;\n                case CMD.R: {\n                    x0 = xi = data[i++];\n                    y0 = yi = data[i++];\n                    var width = data[i++];\n                    var height = data[i++];\n                    l = width * 2 + height * 2;\n                    break;\n                }\n                case CMD.Z: {\n                    var dx = x0 - xi;\n                    var dy = y0 - yi;\n                    l = Math.sqrt(dx * dx + dy * dy);\n                    xi = x0;\n                    yi = y0;\n                    break;\n                }\n            }\n            if (l >= 0) {\n                pathSegLen[segCount++] = l;\n                pathTotalLen += l;\n            }\n        }\n        this._pathLen = pathTotalLen;\n        return pathTotalLen;\n    };\n    PathProxy.prototype.rebuildPath = function (ctx, percent) {\n        var d = this.data;\n        var ux = this._ux;\n        var uy = this._uy;\n        var len = this._len;\n        var x0;\n        var y0;\n        var xi;\n        var yi;\n        var x;\n        var y;\n        var drawPart = percent < 1;\n        var pathSegLen;\n        var pathTotalLen;\n        var accumLength = 0;\n        var segCount = 0;\n        var displayedLength;\n        var pendingPtDist = 0;\n        var pendingPtX;\n        var pendingPtY;\n        if (drawPart) {\n            if (!this._pathSegLen) {\n                this._calculateLength();\n            }\n            pathSegLen = this._pathSegLen;\n            pathTotalLen = this._pathLen;\n            displayedLength = percent * pathTotalLen;\n            if (!displayedLength) {\n                return;\n            }\n        }\n        lo: for (var i = 0; i < len;) {\n            var cmd = d[i++];\n            var isFirst = i === 1;\n            if (isFirst) {\n                xi = d[i];\n                yi = d[i + 1];\n                x0 = xi;\n                y0 = yi;\n            }\n            if (cmd !== CMD.L && pendingPtDist > 0) {\n                ctx.lineTo(pendingPtX, pendingPtY);\n                pendingPtDist = 0;\n            }\n            switch (cmd) {\n                case CMD.M:\n                    x0 = xi = d[i++];\n                    y0 = yi = d[i++];\n                    ctx.moveTo(xi, yi);\n                    break;\n                case CMD.L: {\n                    x = d[i++];\n                    y = d[i++];\n                    var dx = mathAbs(x - xi);\n                    var dy = mathAbs(y - yi);\n                    if (dx > ux || dy > uy) {\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.lineTo(x, y);\n                        xi = x;\n                        yi = y;\n                        pendingPtDist = 0;\n                    }\n                    else {\n                        var d2 = dx * dx + dy * dy;\n                        if (d2 > pendingPtDist) {\n                            pendingPtX = x;\n                            pendingPtY = y;\n                            pendingPtDist = d2;\n                        }\n                    }\n                    break;\n                }\n                case CMD.C: {\n                    var x1 = d[i++];\n                    var y1 = d[i++];\n                    var x2 = d[i++];\n                    var y2 = d[i++];\n                    var x3 = d[i++];\n                    var y3 = d[i++];\n                    if (drawPart) {\n                        var l = pathSegLen[segCount++];\n                        if (accumLength + l > displayedLength) {\n                            var t = (displayedLength - accumLength) / l;\n                            cubicSubdivide(xi, x1, x2, x3, t, tmpOutX);\n                            cubicSubdivide(yi, y1, y2, y3, t, tmpOutY);\n                            ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]);\n                            break lo;\n                        }\n                        accumLength += l;\n                    }\n                    ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n                    xi = x3;\n                    yi = y3;\n                    break;\n                }\n                case CMD.Q: {\n                    var x1 = d[i++];\n                    var y1 = d[i++];\n                    var x2 = d[i++];\n                    var y2 = d[i++];\n                    if (drawPart) {\n                        var l = pathSegLen[segCount++];\n                        if (accumLength + l > displayedLength) {\n                            var t = (displayedLength - accumLength) / l;\n                            quadraticSubdivide(xi, x1, x2, t, tmpOutX);\n                            quadraticSubdivide(yi, y1, y2, t, tmpOutY);\n                            ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]);\n                            break lo;\n                        }\n                        accumLength += l;\n                    }\n                    ctx.quadraticCurveTo(x1, y1, x2, y2);\n                    xi = x2;\n                    yi = y2;\n                    break;\n                }\n                case CMD.A:\n                    var cx = d[i++];\n                    var cy = d[i++];\n                    var rx = d[i++];\n                    var ry = d[i++];\n                    var startAngle = d[i++];\n                    var delta = d[i++];\n                    var psi = d[i++];\n                    var anticlockwise = !d[i++];\n                    var r = (rx > ry) ? rx : ry;\n                    var isEllipse = mathAbs(rx - ry) > 1e-3;\n                    var endAngle = startAngle + delta;\n                    var breakBuild = false;\n                    if (drawPart) {\n                        var l = pathSegLen[segCount++];\n                        if (accumLength + l > displayedLength) {\n                            endAngle = startAngle + delta * (displayedLength - accumLength) / l;\n                            breakBuild = true;\n                        }\n                        accumLength += l;\n                    }\n                    if (isEllipse && ctx.ellipse) {\n                        ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise);\n                    }\n                    else {\n                        ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n                    }\n                    if (breakBuild) {\n                        break lo;\n                    }\n                    if (isFirst) {\n                        x0 = mathCos$1(startAngle) * rx + cx;\n                        y0 = mathSin$1(startAngle) * ry + cy;\n                    }\n                    xi = mathCos$1(endAngle) * rx + cx;\n                    yi = mathSin$1(endAngle) * ry + cy;\n                    break;\n                case CMD.R:\n                    x0 = xi = d[i];\n                    y0 = yi = d[i + 1];\n                    x = d[i++];\n                    y = d[i++];\n                    var width = d[i++];\n                    var height = d[i++];\n                    if (drawPart) {\n                        var l = pathSegLen[segCount++];\n                        if (accumLength + l > displayedLength) {\n                            var d_1 = displayedLength - accumLength;\n                            ctx.moveTo(x, y);\n                            ctx.lineTo(x + mathMin$2(d_1, width), y);\n                            d_1 -= width;\n                            if (d_1 > 0) {\n                                ctx.lineTo(x + width, y + mathMin$2(d_1, height));\n                            }\n                            d_1 -= height;\n                            if (d_1 > 0) {\n                                ctx.lineTo(x + mathMax$2(width - d_1, 0), y + height);\n                            }\n                            d_1 -= width;\n                            if (d_1 > 0) {\n                                ctx.lineTo(x, y + mathMax$2(height - d_1, 0));\n                            }\n                            break lo;\n                        }\n                        accumLength += l;\n                    }\n                    ctx.rect(x, y, width, height);\n                    break;\n                case CMD.Z:\n                    if (drawPart) {\n                        var l = pathSegLen[segCount++];\n                        if (accumLength + l > displayedLength) {\n                            var t = (displayedLength - accumLength) / l;\n                            ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t);\n                            break lo;\n                        }\n                        accumLength += l;\n                    }\n                    ctx.closePath();\n                    xi = x0;\n                    yi = y0;\n            }\n        }\n    };\n    PathProxy.prototype.clone = function () {\n        var newProxy = new PathProxy();\n        var data = this.data;\n        newProxy.data = data.slice ? data.slice()\n            : Array.prototype.slice.call(data);\n        newProxy._len = this._len;\n        return newProxy;\n    };\n    PathProxy.CMD = CMD;\n    PathProxy.initDefaultProps = (function () {\n        var proto = PathProxy.prototype;\n        proto._saveData = true;\n        proto._ux = 0;\n        proto._uy = 0;\n        proto._pendingPtDist = 0;\n        proto._version = 0;\n    })();\n    return PathProxy;\n}());\n\nfunction containStroke(x0, y0, x1, y1, lineWidth, x, y) {\n    if (lineWidth === 0) {\n        return false;\n    }\n    var _l = lineWidth;\n    var _a = 0;\n    var _b = x0;\n    if ((y > y0 + _l && y > y1 + _l)\n        || (y < y0 - _l && y < y1 - _l)\n        || (x > x0 + _l && x > x1 + _l)\n        || (x < x0 - _l && x < x1 - _l)) {\n        return false;\n    }\n    if (x0 !== x1) {\n        _a = (y0 - y1) / (x0 - x1);\n        _b = (x0 * y1 - x1 * y0) / (x0 - x1);\n    }\n    else {\n        return Math.abs(x - x0) <= _l / 2;\n    }\n    var tmp = _a * x - y + _b;\n    var _s = tmp * tmp / (_a * _a + 1);\n    return _s <= _l / 2 * _l / 2;\n}\n\nfunction containStroke$1(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {\n    if (lineWidth === 0) {\n        return false;\n    }\n    var _l = lineWidth;\n    if ((y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)\n        || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)\n        || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)\n        || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)) {\n        return false;\n    }\n    var d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null);\n    return d <= _l / 2;\n}\n\nfunction containStroke$2(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {\n    if (lineWidth === 0) {\n        return false;\n    }\n    var _l = lineWidth;\n    if ((y > y0 + _l && y > y1 + _l && y > y2 + _l)\n        || (y < y0 - _l && y < y1 - _l && y < y2 - _l)\n        || (x > x0 + _l && x > x1 + _l && x > x2 + _l)\n        || (x < x0 - _l && x < x1 - _l && x < x2 - _l)) {\n        return false;\n    }\n    var d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null);\n    return d <= _l / 2;\n}\n\nvar PI2$2 = Math.PI * 2;\nfunction normalizeRadian(angle) {\n    angle %= PI2$2;\n    if (angle < 0) {\n        angle += PI2$2;\n    }\n    return angle;\n}\n\nvar PI2$3 = Math.PI * 2;\nfunction containStroke$3(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) {\n    if (lineWidth === 0) {\n        return false;\n    }\n    var _l = lineWidth;\n    x -= cx;\n    y -= cy;\n    var d = Math.sqrt(x * x + y * y);\n    if ((d - _l > r) || (d + _l < r)) {\n        return false;\n    }\n    if (Math.abs(startAngle - endAngle) % PI2$3 < 1e-4) {\n        return true;\n    }\n    if (anticlockwise) {\n        var tmp = startAngle;\n        startAngle = normalizeRadian(endAngle);\n        endAngle = normalizeRadian(tmp);\n    }\n    else {\n        startAngle = normalizeRadian(startAngle);\n        endAngle = normalizeRadian(endAngle);\n    }\n    if (startAngle > endAngle) {\n        endAngle += PI2$3;\n    }\n    var angle = Math.atan2(y, x);\n    if (angle < 0) {\n        angle += PI2$3;\n    }\n    return (angle >= startAngle && angle <= endAngle)\n        || (angle + PI2$3 >= startAngle && angle + PI2$3 <= endAngle);\n}\n\nfunction windingLine(x0, y0, x1, y1, x, y) {\n    if ((y > y0 && y > y1) || (y < y0 && y < y1)) {\n        return 0;\n    }\n    if (y1 === y0) {\n        return 0;\n    }\n    var t = (y - y0) / (y1 - y0);\n    var dir = y1 < y0 ? 1 : -1;\n    if (t === 1 || t === 0) {\n        dir = y1 < y0 ? 0.5 : -0.5;\n    }\n    var x_ = t * (x1 - x0) + x0;\n    return x_ === x ? Infinity : x_ > x ? dir : 0;\n}\n\nvar CMD$1 = PathProxy.CMD;\nvar PI2$4 = Math.PI * 2;\nvar EPSILON$3 = 1e-4;\nfunction isAroundEqual(a, b) {\n    return Math.abs(a - b) < EPSILON$3;\n}\nvar roots = [-1, -1, -1];\nvar extrema = [-1, -1];\nfunction swapExtrema() {\n    var tmp = extrema[0];\n    extrema[0] = extrema[1];\n    extrema[1] = tmp;\n}\nfunction windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {\n    if ((y > y0 && y > y1 && y > y2 && y > y3)\n        || (y < y0 && y < y1 && y < y2 && y < y3)) {\n        return 0;\n    }\n    var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);\n    if (nRoots === 0) {\n        return 0;\n    }\n    else {\n        var w = 0;\n        var nExtrema = -1;\n        var y0_ = void 0;\n        var y1_ = void 0;\n        for (var i = 0; i < nRoots; i++) {\n            var t = roots[i];\n            var unit = (t === 0 || t === 1) ? 0.5 : 1;\n            var x_ = cubicAt(x0, x1, x2, x3, t);\n            if (x_ < x) {\n                continue;\n            }\n            if (nExtrema < 0) {\n                nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);\n                if (extrema[1] < extrema[0] && nExtrema > 1) {\n                    swapExtrema();\n                }\n                y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);\n                if (nExtrema > 1) {\n                    y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);\n                }\n            }\n            if (nExtrema === 2) {\n                if (t < extrema[0]) {\n                    w += y0_ < y0 ? unit : -unit;\n                }\n                else if (t < extrema[1]) {\n                    w += y1_ < y0_ ? unit : -unit;\n                }\n                else {\n                    w += y3 < y1_ ? unit : -unit;\n                }\n            }\n            else {\n                if (t < extrema[0]) {\n                    w += y0_ < y0 ? unit : -unit;\n                }\n                else {\n                    w += y3 < y0_ ? unit : -unit;\n                }\n            }\n        }\n        return w;\n    }\n}\nfunction windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {\n    if ((y > y0 && y > y1 && y > y2)\n        || (y < y0 && y < y1 && y < y2)) {\n        return 0;\n    }\n    var nRoots = quadraticRootAt(y0, y1, y2, y, roots);\n    if (nRoots === 0) {\n        return 0;\n    }\n    else {\n        var t = quadraticExtremum(y0, y1, y2);\n        if (t >= 0 && t <= 1) {\n            var w = 0;\n            var y_ = quadraticAt(y0, y1, y2, t);\n            for (var i = 0; i < nRoots; i++) {\n                var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;\n                var x_ = quadraticAt(x0, x1, x2, roots[i]);\n                if (x_ < x) {\n                    continue;\n                }\n                if (roots[i] < t) {\n                    w += y_ < y0 ? unit : -unit;\n                }\n                else {\n                    w += y2 < y_ ? unit : -unit;\n                }\n            }\n            return w;\n        }\n        else {\n            var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;\n            var x_ = quadraticAt(x0, x1, x2, roots[0]);\n            if (x_ < x) {\n                return 0;\n            }\n            return y2 < y0 ? unit : -unit;\n        }\n    }\n}\nfunction windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {\n    y -= cy;\n    if (y > r || y < -r) {\n        return 0;\n    }\n    var tmp = Math.sqrt(r * r - y * y);\n    roots[0] = -tmp;\n    roots[1] = tmp;\n    var dTheta = Math.abs(startAngle - endAngle);\n    if (dTheta < 1e-4) {\n        return 0;\n    }\n    if (dTheta >= PI2$4 - 1e-4) {\n        startAngle = 0;\n        endAngle = PI2$4;\n        var dir = anticlockwise ? 1 : -1;\n        if (x >= roots[0] + cx && x <= roots[1] + cx) {\n            return dir;\n        }\n        else {\n            return 0;\n        }\n    }\n    if (startAngle > endAngle) {\n        var tmp_1 = startAngle;\n        startAngle = endAngle;\n        endAngle = tmp_1;\n    }\n    if (startAngle < 0) {\n        startAngle += PI2$4;\n        endAngle += PI2$4;\n    }\n    var w = 0;\n    for (var i = 0; i < 2; i++) {\n        var x_ = roots[i];\n        if (x_ + cx > x) {\n            var angle = Math.atan2(y, x_);\n            var dir = anticlockwise ? 1 : -1;\n            if (angle < 0) {\n                angle = PI2$4 + angle;\n            }\n            if ((angle >= startAngle && angle <= endAngle)\n                || (angle + PI2$4 >= startAngle && angle + PI2$4 <= endAngle)) {\n                if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {\n                    dir = -dir;\n                }\n                w += dir;\n            }\n        }\n    }\n    return w;\n}\nfunction containPath(path, lineWidth, isStroke, x, y) {\n    var data = path.data;\n    var len = path.len();\n    var w = 0;\n    var xi = 0;\n    var yi = 0;\n    var x0 = 0;\n    var y0 = 0;\n    var x1;\n    var y1;\n    for (var i = 0; i < len;) {\n        var cmd = data[i++];\n        var isFirst = i === 1;\n        if (cmd === CMD$1.M && i > 1) {\n            if (!isStroke) {\n                w += windingLine(xi, yi, x0, y0, x, y);\n            }\n        }\n        if (isFirst) {\n            xi = data[i];\n            yi = data[i + 1];\n            x0 = xi;\n            y0 = yi;\n        }\n        switch (cmd) {\n            case CMD$1.M:\n                x0 = data[i++];\n                y0 = data[i++];\n                xi = x0;\n                yi = y0;\n                break;\n            case CMD$1.L:\n                if (isStroke) {\n                    if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {\n                        return true;\n                    }\n                }\n                else {\n                    w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;\n                }\n                xi = data[i++];\n                yi = data[i++];\n                break;\n            case CMD$1.C:\n                if (isStroke) {\n                    if (containStroke$1(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                        return true;\n                    }\n                }\n                else {\n                    w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                }\n                xi = data[i++];\n                yi = data[i++];\n                break;\n            case CMD$1.Q:\n                if (isStroke) {\n                    if (containStroke$2(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                        return true;\n                    }\n                }\n                else {\n                    w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                }\n                xi = data[i++];\n                yi = data[i++];\n                break;\n            case CMD$1.A:\n                var cx = data[i++];\n                var cy = data[i++];\n                var rx = data[i++];\n                var ry = data[i++];\n                var theta = data[i++];\n                var dTheta = data[i++];\n                i += 1;\n                var anticlockwise = !!(1 - data[i++]);\n                x1 = Math.cos(theta) * rx + cx;\n                y1 = Math.sin(theta) * ry + cy;\n                if (!isFirst) {\n                    w += windingLine(xi, yi, x1, y1, x, y);\n                }\n                else {\n                    x0 = x1;\n                    y0 = y1;\n                }\n                var _x = (x - cx) * ry / rx + cx;\n                if (isStroke) {\n                    if (containStroke$3(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {\n                        return true;\n                    }\n                }\n                else {\n                    w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);\n                }\n                xi = Math.cos(theta + dTheta) * rx + cx;\n                yi = Math.sin(theta + dTheta) * ry + cy;\n                break;\n            case CMD$1.R:\n                x0 = xi = data[i++];\n                y0 = yi = data[i++];\n                var width = data[i++];\n                var height = data[i++];\n                x1 = x0 + width;\n                y1 = y0 + height;\n                if (isStroke) {\n                    if (containStroke(x0, y0, x1, y0, lineWidth, x, y)\n                        || containStroke(x1, y0, x1, y1, lineWidth, x, y)\n                        || containStroke(x1, y1, x0, y1, lineWidth, x, y)\n                        || containStroke(x0, y1, x0, y0, lineWidth, x, y)) {\n                        return true;\n                    }\n                }\n                else {\n                    w += windingLine(x1, y0, x1, y1, x, y);\n                    w += windingLine(x0, y1, x0, y0, x, y);\n                }\n                break;\n            case CMD$1.Z:\n                if (isStroke) {\n                    if (containStroke(xi, yi, x0, y0, lineWidth, x, y)) {\n                        return true;\n                    }\n                }\n                else {\n                    w += windingLine(xi, yi, x0, y0, x, y);\n                }\n                xi = x0;\n                yi = y0;\n                break;\n        }\n    }\n    if (!isStroke && !isAroundEqual(yi, y0)) {\n        w += windingLine(xi, yi, x0, y0, x, y) || 0;\n    }\n    return w !== 0;\n}\nfunction contain(pathProxy, x, y) {\n    return containPath(pathProxy, 0, false, x, y);\n}\nfunction containStroke$4(pathProxy, lineWidth, x, y) {\n    return containPath(pathProxy, lineWidth, true, x, y);\n}\n\nvar DEFAULT_PATH_STYLE = defaults({\n    fill: '#000',\n    stroke: null,\n    strokePercent: 1,\n    fillOpacity: 1,\n    strokeOpacity: 1,\n    lineDashOffset: 0,\n    lineWidth: 1,\n    lineCap: 'butt',\n    miterLimit: 10,\n    strokeNoScale: false,\n    strokeFirst: false\n}, DEFAULT_COMMON_STYLE);\nvar DEFAULT_PATH_ANIMATION_PROPS = {\n    style: defaults({\n        fill: true,\n        stroke: true,\n        strokePercent: true,\n        fillOpacity: true,\n        strokeOpacity: true,\n        lineDashOffset: true,\n        lineWidth: true,\n        miterLimit: true\n    }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n};\nvar pathCopyParams = TRANSFORMABLE_PROPS.concat(['invisible',\n    'culling', 'z', 'z2', 'zlevel', 'parent'\n]);\nvar Path = (function (_super) {\n    __extends(Path, _super);\n    function Path(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Path.prototype.update = function () {\n        var _this = this;\n        _super.prototype.update.call(this);\n        var style = this.style;\n        if (style.decal) {\n            var decalEl = this._decalEl = this._decalEl || new Path();\n            if (decalEl.buildPath === Path.prototype.buildPath) {\n                decalEl.buildPath = function (ctx) {\n                    _this.buildPath(ctx, _this.shape);\n                };\n            }\n            decalEl.silent = true;\n            var decalElStyle = decalEl.style;\n            for (var key in style) {\n                if (decalElStyle[key] !== style[key]) {\n                    decalElStyle[key] = style[key];\n                }\n            }\n            decalElStyle.fill = style.fill ? style.decal : null;\n            decalElStyle.decal = null;\n            decalElStyle.shadowColor = null;\n            style.strokeFirst && (decalElStyle.stroke = null);\n            for (var i = 0; i < pathCopyParams.length; ++i) {\n                decalEl[pathCopyParams[i]] = this[pathCopyParams[i]];\n            }\n            decalEl.__dirty |= REDRAW_BIT;\n        }\n        else if (this._decalEl) {\n            this._decalEl = null;\n        }\n    };\n    Path.prototype.getDecalElement = function () {\n        return this._decalEl;\n    };\n    Path.prototype._init = function (props) {\n        var keysArr = keys(props);\n        this.shape = this.getDefaultShape();\n        var defaultStyle = this.getDefaultStyle();\n        if (defaultStyle) {\n            this.useStyle(defaultStyle);\n        }\n        for (var i = 0; i < keysArr.length; i++) {\n            var key = keysArr[i];\n            var value = props[key];\n            if (key === 'style') {\n                if (!this.style) {\n                    this.useStyle(value);\n                }\n                else {\n                    extend(this.style, value);\n                }\n            }\n            else if (key === 'shape') {\n                extend(this.shape, value);\n            }\n            else {\n                _super.prototype.attrKV.call(this, key, value);\n            }\n        }\n        if (!this.style) {\n            this.useStyle({});\n        }\n    };\n    Path.prototype.getDefaultStyle = function () {\n        return null;\n    };\n    Path.prototype.getDefaultShape = function () {\n        return {};\n    };\n    Path.prototype.canBeInsideText = function () {\n        return this.hasFill();\n    };\n    Path.prototype.getInsideTextFill = function () {\n        var pathFill = this.style.fill;\n        if (pathFill !== 'none') {\n            if (isString(pathFill)) {\n                var fillLum = lum(pathFill, 0);\n                if (fillLum > 0.5) {\n                    return DARK_LABEL_COLOR;\n                }\n                else if (fillLum > 0.2) {\n                    return LIGHTER_LABEL_COLOR;\n                }\n                return LIGHT_LABEL_COLOR;\n            }\n            else if (pathFill) {\n                return LIGHT_LABEL_COLOR;\n            }\n        }\n        return DARK_LABEL_COLOR;\n    };\n    Path.prototype.getInsideTextStroke = function (textFill) {\n        var pathFill = this.style.fill;\n        if (isString(pathFill)) {\n            var zr = this.__zr;\n            var isDarkMode = !!(zr && zr.isDarkMode());\n            var isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD;\n            if (isDarkMode === isDarkLabel) {\n                return pathFill;\n            }\n        }\n    };\n    Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { };\n    Path.prototype.pathUpdated = function () {\n        this.__dirty &= ~SHAPE_CHANGED_BIT;\n    };\n    Path.prototype.getUpdatedPathProxy = function (inBatch) {\n        !this.path && this.createPathProxy();\n        this.path.beginPath();\n        this.buildPath(this.path, this.shape, inBatch);\n        return this.path;\n    };\n    Path.prototype.createPathProxy = function () {\n        this.path = new PathProxy(false);\n    };\n    Path.prototype.hasStroke = function () {\n        var style = this.style;\n        var stroke = style.stroke;\n        return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n    };\n    Path.prototype.hasFill = function () {\n        var style = this.style;\n        var fill = style.fill;\n        return fill != null && fill !== 'none';\n    };\n    Path.prototype.getBoundingRect = function () {\n        var rect = this._rect;\n        var style = this.style;\n        var needsUpdateRect = !rect;\n        if (needsUpdateRect) {\n            var firstInvoke = false;\n            if (!this.path) {\n                firstInvoke = true;\n                this.createPathProxy();\n            }\n            var path = this.path;\n            if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) {\n                path.beginPath();\n                this.buildPath(path, this.shape, false);\n                this.pathUpdated();\n            }\n            rect = path.getBoundingRect();\n        }\n        this._rect = rect;\n        if (this.hasStroke() && this.path && this.path.len() > 0) {\n            var rectStroke = this._rectStroke || (this._rectStroke = rect.clone());\n            if (this.__dirty || needsUpdateRect) {\n                rectStroke.copy(rect);\n                var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                var w = style.lineWidth;\n                if (!this.hasFill()) {\n                    var strokeContainThreshold = this.strokeContainThreshold;\n                    w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold);\n                }\n                if (lineScale > 1e-10) {\n                    rectStroke.width += w / lineScale;\n                    rectStroke.height += w / lineScale;\n                    rectStroke.x -= w / lineScale / 2;\n                    rectStroke.y -= w / lineScale / 2;\n                }\n            }\n            return rectStroke;\n        }\n        return rect;\n    };\n    Path.prototype.contain = function (x, y) {\n        var localPos = this.transformCoordToLocal(x, y);\n        var rect = this.getBoundingRect();\n        var style = this.style;\n        x = localPos[0];\n        y = localPos[1];\n        if (rect.contain(x, y)) {\n            var pathProxy = this.path;\n            if (this.hasStroke()) {\n                var lineWidth = style.lineWidth;\n                var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                if (lineScale > 1e-10) {\n                    if (!this.hasFill()) {\n                        lineWidth = Math.max(lineWidth, this.strokeContainThreshold);\n                    }\n                    if (containStroke$4(pathProxy, lineWidth / lineScale, x, y)) {\n                        return true;\n                    }\n                }\n            }\n            if (this.hasFill()) {\n                return contain(pathProxy, x, y);\n            }\n        }\n        return false;\n    };\n    Path.prototype.dirtyShape = function () {\n        this.__dirty |= SHAPE_CHANGED_BIT;\n        if (this._rect) {\n            this._rect = null;\n        }\n        if (this._decalEl) {\n            this._decalEl.dirtyShape();\n        }\n        this.markRedraw();\n    };\n    Path.prototype.dirty = function () {\n        this.dirtyStyle();\n        this.dirtyShape();\n    };\n    Path.prototype.animateShape = function (loop) {\n        return this.animate('shape', loop);\n    };\n    Path.prototype.updateDuringAnimation = function (targetKey) {\n        if (targetKey === 'style') {\n            this.dirtyStyle();\n        }\n        else if (targetKey === 'shape') {\n            this.dirtyShape();\n        }\n        else {\n            this.markRedraw();\n        }\n    };\n    Path.prototype.attrKV = function (key, value) {\n        if (key === 'shape') {\n            this.setShape(value);\n        }\n        else {\n            _super.prototype.attrKV.call(this, key, value);\n        }\n    };\n    Path.prototype.setShape = function (keyOrObj, value) {\n        var shape = this.shape;\n        if (!shape) {\n            shape = this.shape = {};\n        }\n        if (typeof keyOrObj === 'string') {\n            shape[keyOrObj] = value;\n        }\n        else {\n            extend(shape, keyOrObj);\n        }\n        this.dirtyShape();\n        return this;\n    };\n    Path.prototype.shapeChanged = function () {\n        return !!(this.__dirty & SHAPE_CHANGED_BIT);\n    };\n    Path.prototype.createStyle = function (obj) {\n        return createObject(DEFAULT_PATH_STYLE, obj);\n    };\n    Path.prototype._innerSaveToNormal = function (toState) {\n        _super.prototype._innerSaveToNormal.call(this, toState);\n        var normalState = this._normalState;\n        if (toState.shape && !normalState.shape) {\n            normalState.shape = extend({}, this.shape);\n        }\n    };\n    Path.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n        _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n        var needsRestoreToNormal = !(state && keepCurrentStates);\n        var targetShape;\n        if (state && state.shape) {\n            if (transition) {\n                if (keepCurrentStates) {\n                    targetShape = state.shape;\n                }\n                else {\n                    targetShape = extend({}, normalState.shape);\n                    extend(targetShape, state.shape);\n                }\n            }\n            else {\n                targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape);\n                extend(targetShape, state.shape);\n            }\n        }\n        else if (needsRestoreToNormal) {\n            targetShape = normalState.shape;\n        }\n        if (targetShape) {\n            if (transition) {\n                this.shape = extend({}, this.shape);\n                var targetShapePrimaryProps = {};\n                var shapeKeys = keys(targetShape);\n                for (var i = 0; i < shapeKeys.length; i++) {\n                    var key = shapeKeys[i];\n                    if (typeof targetShape[key] === 'object') {\n                        this.shape[key] = targetShape[key];\n                    }\n                    else {\n                        targetShapePrimaryProps[key] = targetShape[key];\n                    }\n                }\n                this._transitionState(stateName, {\n                    shape: targetShapePrimaryProps\n                }, animationCfg);\n            }\n            else {\n                this.shape = targetShape;\n                this.dirtyShape();\n            }\n        }\n    };\n    Path.prototype._mergeStates = function (states) {\n        var mergedState = _super.prototype._mergeStates.call(this, states);\n        var mergedShape;\n        for (var i = 0; i < states.length; i++) {\n            var state = states[i];\n            if (state.shape) {\n                mergedShape = mergedShape || {};\n                this._mergeStyle(mergedShape, state.shape);\n            }\n        }\n        if (mergedShape) {\n            mergedState.shape = mergedShape;\n        }\n        return mergedState;\n    };\n    Path.prototype.getAnimationStyleProps = function () {\n        return DEFAULT_PATH_ANIMATION_PROPS;\n    };\n    Path.prototype.isZeroArea = function () {\n        return false;\n    };\n    Path.extend = function (defaultProps) {\n        var Sub = (function (_super) {\n            __extends(Sub, _super);\n            function Sub(opts) {\n                var _this = _super.call(this, opts) || this;\n                defaultProps.init && defaultProps.init.call(_this, opts);\n                return _this;\n            }\n            Sub.prototype.getDefaultStyle = function () {\n                return clone(defaultProps.style);\n            };\n            Sub.prototype.getDefaultShape = function () {\n                return clone(defaultProps.shape);\n            };\n            return Sub;\n        }(Path));\n        for (var key in defaultProps) {\n            if (typeof defaultProps[key] === 'function') {\n                Sub.prototype[key] = defaultProps[key];\n            }\n        }\n        return Sub;\n    };\n    Path.initDefaultProps = (function () {\n        var pathProto = Path.prototype;\n        pathProto.type = 'path';\n        pathProto.strokeContainThreshold = 5;\n        pathProto.segmentIgnoreThreshold = 0;\n        pathProto.subPixelOptimize = false;\n        pathProto.autoBatch = false;\n        pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT;\n    })();\n    return Path;\n}(Displayable));\n\nvar DEFAULT_TSPAN_STYLE = defaults({\n    strokeFirst: true,\n    font: DEFAULT_FONT,\n    x: 0,\n    y: 0,\n    textAlign: 'left',\n    textBaseline: 'top',\n    miterLimit: 2\n}, DEFAULT_PATH_STYLE);\nvar TSpan = (function (_super) {\n    __extends(TSpan, _super);\n    function TSpan() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    TSpan.prototype.hasStroke = function () {\n        var style = this.style;\n        var stroke = style.stroke;\n        return stroke != null && stroke !== 'none' && style.lineWidth > 0;\n    };\n    TSpan.prototype.hasFill = function () {\n        var style = this.style;\n        var fill = style.fill;\n        return fill != null && fill !== 'none';\n    };\n    TSpan.prototype.createStyle = function (obj) {\n        return createObject(DEFAULT_TSPAN_STYLE, obj);\n    };\n    TSpan.prototype.setBoundingRect = function (rect) {\n        this._rect = rect;\n    };\n    TSpan.prototype.getBoundingRect = function () {\n        var style = this.style;\n        if (!this._rect) {\n            var text = style.text;\n            text != null ? (text += '') : (text = '');\n            var rect = getBoundingRect(text, style.font, style.textAlign, style.textBaseline);\n            rect.x += style.x || 0;\n            rect.y += style.y || 0;\n            if (this.hasStroke()) {\n                var w = style.lineWidth;\n                rect.x -= w / 2;\n                rect.y -= w / 2;\n                rect.width += w;\n                rect.height += w;\n            }\n            this._rect = rect;\n        }\n        return this._rect;\n    };\n    TSpan.initDefaultProps = (function () {\n        var tspanProto = TSpan.prototype;\n        tspanProto.dirtyRectTolerance = 10;\n    })();\n    return TSpan;\n}(Displayable));\nTSpan.prototype.type = 'tspan';\n\nvar DEFAULT_IMAGE_STYLE = defaults({\n    x: 0,\n    y: 0\n}, DEFAULT_COMMON_STYLE);\nvar DEFAULT_IMAGE_ANIMATION_PROPS = {\n    style: defaults({\n        x: true,\n        y: true,\n        width: true,\n        height: true,\n        sx: true,\n        sy: true,\n        sWidth: true,\n        sHeight: true\n    }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n};\nfunction isImageLike(source) {\n    return !!(source\n        && typeof source !== 'string'\n        && source.width && source.height);\n}\nvar ZRImage = (function (_super) {\n    __extends(ZRImage, _super);\n    function ZRImage() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    ZRImage.prototype.createStyle = function (obj) {\n        return createObject(DEFAULT_IMAGE_STYLE, obj);\n    };\n    ZRImage.prototype._getSize = function (dim) {\n        var style = this.style;\n        var size = style[dim];\n        if (size != null) {\n            return size;\n        }\n        var imageSource = isImageLike(style.image)\n            ? style.image : this.__image;\n        if (!imageSource) {\n            return 0;\n        }\n        var otherDim = dim === 'width' ? 'height' : 'width';\n        var otherDimSize = style[otherDim];\n        if (otherDimSize == null) {\n            return imageSource[dim];\n        }\n        else {\n            return imageSource[dim] / imageSource[otherDim] * otherDimSize;\n        }\n    };\n    ZRImage.prototype.getWidth = function () {\n        return this._getSize('width');\n    };\n    ZRImage.prototype.getHeight = function () {\n        return this._getSize('height');\n    };\n    ZRImage.prototype.getAnimationStyleProps = function () {\n        return DEFAULT_IMAGE_ANIMATION_PROPS;\n    };\n    ZRImage.prototype.getBoundingRect = function () {\n        var style = this.style;\n        if (!this._rect) {\n            this._rect = new BoundingRect(style.x || 0, style.y || 0, this.getWidth(), this.getHeight());\n        }\n        return this._rect;\n    };\n    return ZRImage;\n}(Displayable));\nZRImage.prototype.type = 'image';\n\nfunction buildPath(ctx, shape) {\n    var x = shape.x;\n    var y = shape.y;\n    var width = shape.width;\n    var height = shape.height;\n    var r = shape.r;\n    var r1;\n    var r2;\n    var r3;\n    var r4;\n    if (width < 0) {\n        x = x + width;\n        width = -width;\n    }\n    if (height < 0) {\n        y = y + height;\n        height = -height;\n    }\n    if (typeof r === 'number') {\n        r1 = r2 = r3 = r4 = r;\n    }\n    else if (r instanceof Array) {\n        if (r.length === 1) {\n            r1 = r2 = r3 = r4 = r[0];\n        }\n        else if (r.length === 2) {\n            r1 = r3 = r[0];\n            r2 = r4 = r[1];\n        }\n        else if (r.length === 3) {\n            r1 = r[0];\n            r2 = r4 = r[1];\n            r3 = r[2];\n        }\n        else {\n            r1 = r[0];\n            r2 = r[1];\n            r3 = r[2];\n            r4 = r[3];\n        }\n    }\n    else {\n        r1 = r2 = r3 = r4 = 0;\n    }\n    var total;\n    if (r1 + r2 > width) {\n        total = r1 + r2;\n        r1 *= width / total;\n        r2 *= width / total;\n    }\n    if (r3 + r4 > width) {\n        total = r3 + r4;\n        r3 *= width / total;\n        r4 *= width / total;\n    }\n    if (r2 + r3 > height) {\n        total = r2 + r3;\n        r2 *= height / total;\n        r3 *= height / total;\n    }\n    if (r1 + r4 > height) {\n        total = r1 + r4;\n        r1 *= height / total;\n        r4 *= height / total;\n    }\n    ctx.moveTo(x + r1, y);\n    ctx.lineTo(x + width - r2, y);\n    r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0);\n    ctx.lineTo(x + width, y + height - r3);\n    r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2);\n    ctx.lineTo(x + r4, y + height);\n    r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI);\n    ctx.lineTo(x, y + r1);\n    r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5);\n}\n\nvar round$1 = Math.round;\nfunction subPixelOptimizeLine(outputShape, inputShape, style) {\n    if (!inputShape) {\n        return;\n    }\n    var x1 = inputShape.x1;\n    var x2 = inputShape.x2;\n    var y1 = inputShape.y1;\n    var y2 = inputShape.y2;\n    outputShape.x1 = x1;\n    outputShape.x2 = x2;\n    outputShape.y1 = y1;\n    outputShape.y2 = y2;\n    var lineWidth = style && style.lineWidth;\n    if (!lineWidth) {\n        return outputShape;\n    }\n    if (round$1(x1 * 2) === round$1(x2 * 2)) {\n        outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true);\n    }\n    if (round$1(y1 * 2) === round$1(y2 * 2)) {\n        outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true);\n    }\n    return outputShape;\n}\nfunction subPixelOptimizeRect(outputShape, inputShape, style) {\n    if (!inputShape) {\n        return;\n    }\n    var originX = inputShape.x;\n    var originY = inputShape.y;\n    var originWidth = inputShape.width;\n    var originHeight = inputShape.height;\n    outputShape.x = originX;\n    outputShape.y = originY;\n    outputShape.width = originWidth;\n    outputShape.height = originHeight;\n    var lineWidth = style && style.lineWidth;\n    if (!lineWidth) {\n        return outputShape;\n    }\n    outputShape.x = subPixelOptimize(originX, lineWidth, true);\n    outputShape.y = subPixelOptimize(originY, lineWidth, true);\n    outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1);\n    outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1);\n    return outputShape;\n}\nfunction subPixelOptimize(position, lineWidth, positiveOrNegative) {\n    if (!lineWidth) {\n        return position;\n    }\n    var doubledPosition = round$1(position * 2);\n    return (doubledPosition + round$1(lineWidth)) % 2 === 0\n        ? doubledPosition / 2\n        : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;\n}\n\nvar RectShape = (function () {\n    function RectShape() {\n        this.x = 0;\n        this.y = 0;\n        this.width = 0;\n        this.height = 0;\n    }\n    return RectShape;\n}());\nvar subPixelOptimizeOutputShape = {};\nvar Rect = (function (_super) {\n    __extends(Rect, _super);\n    function Rect(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Rect.prototype.getDefaultShape = function () {\n        return new RectShape();\n    };\n    Rect.prototype.buildPath = function (ctx, shape) {\n        var x;\n        var y;\n        var width;\n        var height;\n        if (this.subPixelOptimize) {\n            var optimizedShape = subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style);\n            x = optimizedShape.x;\n            y = optimizedShape.y;\n            width = optimizedShape.width;\n            height = optimizedShape.height;\n            optimizedShape.r = shape.r;\n            shape = optimizedShape;\n        }\n        else {\n            x = shape.x;\n            y = shape.y;\n            width = shape.width;\n            height = shape.height;\n        }\n        if (!shape.r) {\n            ctx.rect(x, y, width, height);\n        }\n        else {\n            buildPath(ctx, shape);\n        }\n    };\n    Rect.prototype.isZeroArea = function () {\n        return !this.shape.width || !this.shape.height;\n    };\n    return Rect;\n}(Path));\nRect.prototype.type = 'rect';\n\nvar DEFAULT_RICH_TEXT_COLOR = {\n    fill: '#000'\n};\nvar DEFAULT_STROKE_LINE_WIDTH = 2;\nvar DEFAULT_TEXT_ANIMATION_PROPS = {\n    style: defaults({\n        fill: true,\n        stroke: true,\n        fillOpacity: true,\n        strokeOpacity: true,\n        lineWidth: true,\n        fontSize: true,\n        lineHeight: true,\n        width: true,\n        height: true,\n        textShadowColor: true,\n        textShadowBlur: true,\n        textShadowOffsetX: true,\n        textShadowOffsetY: true,\n        backgroundColor: true,\n        padding: true,\n        borderColor: true,\n        borderWidth: true,\n        borderRadius: true\n    }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n};\nvar ZRText = (function (_super) {\n    __extends(ZRText, _super);\n    function ZRText(opts) {\n        var _this = _super.call(this) || this;\n        _this.type = 'text';\n        _this._children = [];\n        _this._defaultStyle = DEFAULT_RICH_TEXT_COLOR;\n        _this.attr(opts);\n        return _this;\n    }\n    ZRText.prototype.childrenRef = function () {\n        return this._children;\n    };\n    ZRText.prototype.update = function () {\n        _super.prototype.update.call(this);\n        if (this.styleChanged()) {\n            this._updateSubTexts();\n        }\n        for (var i = 0; i < this._children.length; i++) {\n            var child = this._children[i];\n            child.zlevel = this.zlevel;\n            child.z = this.z;\n            child.z2 = this.z2;\n            child.culling = this.culling;\n            child.cursor = this.cursor;\n            child.invisible = this.invisible;\n        }\n    };\n    ZRText.prototype.updateTransform = function () {\n        var innerTransformable = this.innerTransformable;\n        if (innerTransformable) {\n            innerTransformable.updateTransform();\n            if (innerTransformable.transform) {\n                this.transform = innerTransformable.transform;\n            }\n        }\n        else {\n            _super.prototype.updateTransform.call(this);\n        }\n    };\n    ZRText.prototype.getLocalTransform = function (m) {\n        var innerTransformable = this.innerTransformable;\n        return innerTransformable\n            ? innerTransformable.getLocalTransform(m)\n            : _super.prototype.getLocalTransform.call(this, m);\n    };\n    ZRText.prototype.getComputedTransform = function () {\n        if (this.__hostTarget) {\n            this.__hostTarget.getComputedTransform();\n            this.__hostTarget.updateInnerText(true);\n        }\n        return _super.prototype.getComputedTransform.call(this);\n    };\n    ZRText.prototype._updateSubTexts = function () {\n        this._childCursor = 0;\n        normalizeTextStyle(this.style);\n        this.style.rich\n            ? this._updateRichTexts()\n            : this._updatePlainTexts();\n        this._children.length = this._childCursor;\n        this.styleUpdated();\n    };\n    ZRText.prototype.addSelfToZr = function (zr) {\n        _super.prototype.addSelfToZr.call(this, zr);\n        for (var i = 0; i < this._children.length; i++) {\n            this._children[i].__zr = zr;\n        }\n    };\n    ZRText.prototype.removeSelfFromZr = function (zr) {\n        _super.prototype.removeSelfFromZr.call(this, zr);\n        for (var i = 0; i < this._children.length; i++) {\n            this._children[i].__zr = null;\n        }\n    };\n    ZRText.prototype.getBoundingRect = function () {\n        if (this.styleChanged()) {\n            this._updateSubTexts();\n        }\n        if (!this._rect) {\n            var tmpRect = new BoundingRect(0, 0, 0, 0);\n            var children = this._children;\n            var tmpMat = [];\n            var rect = null;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                var childRect = child.getBoundingRect();\n                var transform = child.getLocalTransform(tmpMat);\n                if (transform) {\n                    tmpRect.copy(childRect);\n                    tmpRect.applyTransform(transform);\n                    rect = rect || tmpRect.clone();\n                    rect.union(tmpRect);\n                }\n                else {\n                    rect = rect || childRect.clone();\n                    rect.union(childRect);\n                }\n            }\n            this._rect = rect || tmpRect;\n        }\n        return this._rect;\n    };\n    ZRText.prototype.setDefaultTextStyle = function (defaultTextStyle) {\n        this._defaultStyle = defaultTextStyle || DEFAULT_RICH_TEXT_COLOR;\n    };\n    ZRText.prototype.setTextContent = function (textContent) {\n        if (\"development\" !== 'production') {\n            throw new Error('Can\\'t attach text on another text');\n        }\n    };\n    ZRText.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n        if (!sourceStyle) {\n            return targetStyle;\n        }\n        var sourceRich = sourceStyle.rich;\n        var targetRich = targetStyle.rich || (sourceRich && {});\n        extend(targetStyle, sourceStyle);\n        if (sourceRich && targetRich) {\n            this._mergeRich(targetRich, sourceRich);\n            targetStyle.rich = targetRich;\n        }\n        else if (targetRich) {\n            targetStyle.rich = targetRich;\n        }\n        return targetStyle;\n    };\n    ZRText.prototype._mergeRich = function (targetRich, sourceRich) {\n        var richNames = keys(sourceRich);\n        for (var i = 0; i < richNames.length; i++) {\n            var richName = richNames[i];\n            targetRich[richName] = targetRich[richName] || {};\n            extend(targetRich[richName], sourceRich[richName]);\n        }\n    };\n    ZRText.prototype.getAnimationStyleProps = function () {\n        return DEFAULT_TEXT_ANIMATION_PROPS;\n    };\n    ZRText.prototype._getOrCreateChild = function (Ctor) {\n        var child = this._children[this._childCursor];\n        if (!child || !(child instanceof Ctor)) {\n            child = new Ctor();\n        }\n        this._children[this._childCursor++] = child;\n        child.__zr = this.__zr;\n        child.parent = this;\n        return child;\n    };\n    ZRText.prototype._updatePlainTexts = function () {\n        var style = this.style;\n        var textFont = style.font || DEFAULT_FONT;\n        var textPadding = style.padding;\n        var text = getStyleText(style);\n        var contentBlock = parsePlainText(text, style);\n        var needDrawBg = needDrawBackground(style);\n        var bgColorDrawn = !!(style.backgroundColor);\n        var outerHeight = contentBlock.outerHeight;\n        var outerWidth = contentBlock.outerWidth;\n        var contentWidth = contentBlock.contentWidth;\n        var textLines = contentBlock.lines;\n        var lineHeight = contentBlock.lineHeight;\n        var defaultStyle = this._defaultStyle;\n        var baseX = style.x || 0;\n        var baseY = style.y || 0;\n        var textAlign = style.align || defaultStyle.align || 'left';\n        var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign || 'top';\n        var textX = baseX;\n        var textY = adjustTextY$1(baseY, contentBlock.contentHeight, verticalAlign);\n        if (needDrawBg || textPadding) {\n            var boxX = adjustTextX(baseX, outerWidth, textAlign);\n            var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign);\n            needDrawBg && this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n        }\n        textY += lineHeight / 2;\n        if (textPadding) {\n            textX = getTextXForPadding(baseX, textAlign, textPadding);\n            if (verticalAlign === 'top') {\n                textY += textPadding[0];\n            }\n            else if (verticalAlign === 'bottom') {\n                textY -= textPadding[2];\n            }\n        }\n        var defaultLineWidth = 0;\n        var useDefaultFill = false;\n        var textFill = getFill('fill' in style\n            ? style.fill\n            : (useDefaultFill = true, defaultStyle.fill));\n        var textStroke = getStroke('stroke' in style\n            ? style.stroke\n            : (!bgColorDrawn\n                && (!defaultStyle.autoStroke || useDefaultFill))\n                ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                : null);\n        var hasShadow = style.textShadowBlur > 0;\n        var fixedBoundingRect = style.width != null\n            && (style.overflow === 'truncate' || style.overflow === 'break' || style.overflow === 'breakAll');\n        var calculatedLineHeight = contentBlock.calculatedLineHeight;\n        for (var i = 0; i < textLines.length; i++) {\n            var el = this._getOrCreateChild(TSpan);\n            var subElStyle = el.createStyle();\n            el.useStyle(subElStyle);\n            subElStyle.text = textLines[i];\n            subElStyle.x = textX;\n            subElStyle.y = textY;\n            if (textAlign) {\n                subElStyle.textAlign = textAlign;\n            }\n            subElStyle.textBaseline = 'middle';\n            subElStyle.opacity = style.opacity;\n            subElStyle.strokeFirst = true;\n            if (hasShadow) {\n                subElStyle.shadowBlur = style.textShadowBlur || 0;\n                subElStyle.shadowColor = style.textShadowColor || 'transparent';\n                subElStyle.shadowOffsetX = style.textShadowOffsetX || 0;\n                subElStyle.shadowOffsetY = style.textShadowOffsetY || 0;\n            }\n            subElStyle.stroke = textStroke;\n            subElStyle.fill = textFill;\n            if (textStroke) {\n                subElStyle.lineWidth = style.lineWidth || defaultLineWidth;\n                subElStyle.lineDash = style.lineDash;\n                subElStyle.lineDashOffset = style.lineDashOffset || 0;\n            }\n            subElStyle.font = textFont;\n            setSeparateFont(subElStyle, style);\n            textY += lineHeight;\n            if (fixedBoundingRect) {\n                el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, style.width, subElStyle.textAlign), adjustTextY$1(subElStyle.y, calculatedLineHeight, subElStyle.textBaseline), contentWidth, calculatedLineHeight));\n            }\n        }\n    };\n    ZRText.prototype._updateRichTexts = function () {\n        var style = this.style;\n        var text = getStyleText(style);\n        var contentBlock = parseRichText(text, style);\n        var contentWidth = contentBlock.width;\n        var outerWidth = contentBlock.outerWidth;\n        var outerHeight = contentBlock.outerHeight;\n        var textPadding = style.padding;\n        var baseX = style.x || 0;\n        var baseY = style.y || 0;\n        var defaultStyle = this._defaultStyle;\n        var textAlign = style.align || defaultStyle.align;\n        var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign;\n        var boxX = adjustTextX(baseX, outerWidth, textAlign);\n        var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign);\n        var xLeft = boxX;\n        var lineTop = boxY;\n        if (textPadding) {\n            xLeft += textPadding[3];\n            lineTop += textPadding[0];\n        }\n        var xRight = xLeft + contentWidth;\n        if (needDrawBackground(style)) {\n            this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n        }\n        var bgColorDrawn = !!(style.backgroundColor);\n        for (var i = 0; i < contentBlock.lines.length; i++) {\n            var line = contentBlock.lines[i];\n            var tokens = line.tokens;\n            var tokenCount = tokens.length;\n            var lineHeight = line.lineHeight;\n            var remainedWidth = line.width;\n            var leftIndex = 0;\n            var lineXLeft = xLeft;\n            var lineXRight = xRight;\n            var rightIndex = tokenCount - 1;\n            var token = void 0;\n            while (leftIndex < tokenCount\n                && (token = tokens[leftIndex], !token.align || token.align === 'left')) {\n                this._placeToken(token, style, lineHeight, lineTop, lineXLeft, 'left', bgColorDrawn);\n                remainedWidth -= token.width;\n                lineXLeft += token.width;\n                leftIndex++;\n            }\n            while (rightIndex >= 0\n                && (token = tokens[rightIndex], token.align === 'right')) {\n                this._placeToken(token, style, lineHeight, lineTop, lineXRight, 'right', bgColorDrawn);\n                remainedWidth -= token.width;\n                lineXRight -= token.width;\n                rightIndex--;\n            }\n            lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - remainedWidth) / 2;\n            while (leftIndex <= rightIndex) {\n                token = tokens[leftIndex];\n                this._placeToken(token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center', bgColorDrawn);\n                lineXLeft += token.width;\n                leftIndex++;\n            }\n            lineTop += lineHeight;\n        }\n    };\n    ZRText.prototype._placeToken = function (token, style, lineHeight, lineTop, x, textAlign, parentBgColorDrawn) {\n        var tokenStyle = style.rich[token.styleName] || {};\n        tokenStyle.text = token.text;\n        var verticalAlign = token.verticalAlign;\n        var y = lineTop + lineHeight / 2;\n        if (verticalAlign === 'top') {\n            y = lineTop + token.height / 2;\n        }\n        else if (verticalAlign === 'bottom') {\n            y = lineTop + lineHeight - token.height / 2;\n        }\n        var needDrawBg = !token.isLineHolder && needDrawBackground(tokenStyle);\n        needDrawBg && this._renderBackground(tokenStyle, style, textAlign === 'right'\n            ? x - token.width\n            : textAlign === 'center'\n                ? x - token.width / 2\n                : x, y - token.height / 2, token.width, token.height);\n        var bgColorDrawn = !!tokenStyle.backgroundColor;\n        var textPadding = token.textPadding;\n        if (textPadding) {\n            x = getTextXForPadding(x, textAlign, textPadding);\n            y -= token.height / 2 - textPadding[0] - token.innerHeight / 2;\n        }\n        var el = this._getOrCreateChild(TSpan);\n        var subElStyle = el.createStyle();\n        el.useStyle(subElStyle);\n        var defaultStyle = this._defaultStyle;\n        var useDefaultFill = false;\n        var defaultLineWidth = 0;\n        var textFill = getFill('fill' in tokenStyle ? tokenStyle.fill\n            : 'fill' in style ? style.fill\n                : (useDefaultFill = true, defaultStyle.fill));\n        var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke\n            : 'stroke' in style ? style.stroke\n                : (!bgColorDrawn\n                    && !parentBgColorDrawn\n                    && (!defaultStyle.autoStroke || useDefaultFill)) ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                    : null);\n        var hasShadow = tokenStyle.textShadowBlur > 0\n            || style.textShadowBlur > 0;\n        subElStyle.text = token.text;\n        subElStyle.x = x;\n        subElStyle.y = y;\n        if (hasShadow) {\n            subElStyle.shadowBlur = tokenStyle.textShadowBlur || style.textShadowBlur || 0;\n            subElStyle.shadowColor = tokenStyle.textShadowColor || style.textShadowColor || 'transparent';\n            subElStyle.shadowOffsetX = tokenStyle.textShadowOffsetX || style.textShadowOffsetX || 0;\n            subElStyle.shadowOffsetY = tokenStyle.textShadowOffsetY || style.textShadowOffsetY || 0;\n        }\n        subElStyle.textAlign = textAlign;\n        subElStyle.textBaseline = 'middle';\n        subElStyle.font = token.font || DEFAULT_FONT;\n        subElStyle.opacity = retrieve3(tokenStyle.opacity, style.opacity, 1);\n        setSeparateFont(subElStyle, tokenStyle);\n        if (textStroke) {\n            subElStyle.lineWidth = retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultLineWidth);\n            subElStyle.lineDash = retrieve2(tokenStyle.lineDash, style.lineDash);\n            subElStyle.lineDashOffset = style.lineDashOffset || 0;\n            subElStyle.stroke = textStroke;\n        }\n        if (textFill) {\n            subElStyle.fill = textFill;\n        }\n        var textWidth = token.contentWidth;\n        var textHeight = token.contentHeight;\n        el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, textWidth, subElStyle.textAlign), adjustTextY$1(subElStyle.y, textHeight, subElStyle.textBaseline), textWidth, textHeight));\n    };\n    ZRText.prototype._renderBackground = function (style, topStyle, x, y, width, height) {\n        var textBackgroundColor = style.backgroundColor;\n        var textBorderWidth = style.borderWidth;\n        var textBorderColor = style.borderColor;\n        var isImageBg = textBackgroundColor && textBackgroundColor.image;\n        var isPlainOrGradientBg = textBackgroundColor && !isImageBg;\n        var textBorderRadius = style.borderRadius;\n        var self = this;\n        var rectEl;\n        var imgEl;\n        if (isPlainOrGradientBg || style.lineHeight || (textBorderWidth && textBorderColor)) {\n            rectEl = this._getOrCreateChild(Rect);\n            rectEl.useStyle(rectEl.createStyle());\n            rectEl.style.fill = null;\n            var rectShape = rectEl.shape;\n            rectShape.x = x;\n            rectShape.y = y;\n            rectShape.width = width;\n            rectShape.height = height;\n            rectShape.r = textBorderRadius;\n            rectEl.dirtyShape();\n        }\n        if (isPlainOrGradientBg) {\n            var rectStyle = rectEl.style;\n            rectStyle.fill = textBackgroundColor || null;\n            rectStyle.fillOpacity = retrieve2(style.fillOpacity, 1);\n        }\n        else if (isImageBg) {\n            imgEl = this._getOrCreateChild(ZRImage);\n            imgEl.onload = function () {\n                self.dirtyStyle();\n            };\n            var imgStyle = imgEl.style;\n            imgStyle.image = textBackgroundColor.image;\n            imgStyle.x = x;\n            imgStyle.y = y;\n            imgStyle.width = width;\n            imgStyle.height = height;\n        }\n        if (textBorderWidth && textBorderColor) {\n            var rectStyle = rectEl.style;\n            rectStyle.lineWidth = textBorderWidth;\n            rectStyle.stroke = textBorderColor;\n            rectStyle.strokeOpacity = retrieve2(style.strokeOpacity, 1);\n            rectStyle.lineDash = style.borderDash;\n            rectStyle.lineDashOffset = style.borderDashOffset || 0;\n            rectEl.strokeContainThreshold = 0;\n            if (rectEl.hasFill() && rectEl.hasStroke()) {\n                rectStyle.strokeFirst = true;\n                rectStyle.lineWidth *= 2;\n            }\n        }\n        var commonStyle = (rectEl || imgEl).style;\n        commonStyle.shadowBlur = style.shadowBlur || 0;\n        commonStyle.shadowColor = style.shadowColor || 'transparent';\n        commonStyle.shadowOffsetX = style.shadowOffsetX || 0;\n        commonStyle.shadowOffsetY = style.shadowOffsetY || 0;\n        commonStyle.opacity = retrieve3(style.opacity, topStyle.opacity, 1);\n    };\n    ZRText.makeFont = function (style) {\n        var font = '';\n        if (hasSeparateFont(style)) {\n            font = [\n                style.fontStyle,\n                style.fontWeight,\n                parseFontSize(style.fontSize),\n                style.fontFamily || 'sans-serif'\n            ].join(' ');\n        }\n        return font && trim(font) || style.textFont || style.font;\n    };\n    return ZRText;\n}(Displayable));\nvar VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 };\nvar VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 };\nvar FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];\nfunction parseFontSize(fontSize) {\n    if (typeof fontSize === 'string'\n        && (fontSize.indexOf('px') !== -1\n            || fontSize.indexOf('rem') !== -1\n            || fontSize.indexOf('em') !== -1)) {\n        return fontSize;\n    }\n    else if (!isNaN(+fontSize)) {\n        return fontSize + 'px';\n    }\n    else {\n        return DEFAULT_FONT_SIZE + 'px';\n    }\n}\nfunction setSeparateFont(targetStyle, sourceStyle) {\n    for (var i = 0; i < FONT_PARTS.length; i++) {\n        var fontProp = FONT_PARTS[i];\n        var val = sourceStyle[fontProp];\n        if (val != null) {\n            targetStyle[fontProp] = val;\n        }\n    }\n}\nfunction hasSeparateFont(style) {\n    return style.fontSize != null || style.fontFamily || style.fontWeight;\n}\nfunction normalizeTextStyle(style) {\n    normalizeStyle(style);\n    each(style.rich, normalizeStyle);\n    return style;\n}\nfunction normalizeStyle(style) {\n    if (style) {\n        style.font = ZRText.makeFont(style);\n        var textAlign = style.align;\n        textAlign === 'middle' && (textAlign = 'center');\n        style.align = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left';\n        var verticalAlign = style.verticalAlign;\n        verticalAlign === 'center' && (verticalAlign = 'middle');\n        style.verticalAlign = (verticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[verticalAlign]) ? verticalAlign : 'top';\n        var textPadding = style.padding;\n        if (textPadding) {\n            style.padding = normalizeCssArray(style.padding);\n        }\n    }\n}\nfunction getStroke(stroke, lineWidth) {\n    return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')\n        ? null\n        : (stroke.image || stroke.colorStops)\n            ? '#000'\n            : stroke;\n}\nfunction getFill(fill) {\n    return (fill == null || fill === 'none')\n        ? null\n        : (fill.image || fill.colorStops)\n            ? '#000'\n            : fill;\n}\nfunction getTextXForPadding(x, textAlign, textPadding) {\n    return textAlign === 'right'\n        ? (x - textPadding[1])\n        : textAlign === 'center'\n            ? (x + textPadding[3] / 2 - textPadding[1] / 2)\n            : (x + textPadding[3]);\n}\nfunction getStyleText(style) {\n    var text = style.text;\n    text != null && (text += '');\n    return text;\n}\nfunction needDrawBackground(style) {\n    return !!(style.backgroundColor\n        || style.lineHeight\n        || (style.borderWidth && style.borderColor));\n}\n\nvar getECData = makeInner();\nvar setCommonECData = function (seriesIndex, dataType, dataIdx, el) {\n  if (el) {\n    var ecData = getECData(el); // Add data index and series index for indexing the data by element\n    // Useful in tooltip\n\n    ecData.dataIndex = dataIdx;\n    ecData.dataType = dataType;\n    ecData.seriesIndex = seriesIndex; // TODO: not store dataIndex on children.\n\n    if (el.type === 'group') {\n      el.traverse(function (child) {\n        var childECData = getECData(child);\n        childECData.seriesIndex = seriesIndex;\n        childECData.dataIndex = dataIdx;\n        childECData.dataType = dataType;\n      });\n    }\n  }\n};\n\nvar _highlightNextDigit = 1;\nvar _highlightKeyMap = {};\nvar getSavedStates = makeInner();\nvar getComponentStates = makeInner();\nvar HOVER_STATE_NORMAL = 0;\nvar HOVER_STATE_BLUR = 1;\nvar HOVER_STATE_EMPHASIS = 2;\nvar SPECIAL_STATES = ['emphasis', 'blur', 'select'];\nvar DISPLAY_STATES = ['normal', 'emphasis', 'blur', 'select'];\nvar Z2_EMPHASIS_LIFT = 10;\nvar Z2_SELECT_LIFT = 9;\nvar HIGHLIGHT_ACTION_TYPE = 'highlight';\nvar DOWNPLAY_ACTION_TYPE = 'downplay';\nvar SELECT_ACTION_TYPE = 'select';\nvar UNSELECT_ACTION_TYPE = 'unselect';\nvar TOGGLE_SELECT_ACTION_TYPE = 'toggleSelect';\n\nfunction hasFillOrStroke(fillOrStroke) {\n  return fillOrStroke != null && fillOrStroke !== 'none';\n} // Most lifted color are duplicated.\n\n\nvar liftedColorCache = new LRU(100);\n\nfunction liftColor(color$1) {\n  if (isString(color$1)) {\n    var liftedColor = liftedColorCache.get(color$1);\n\n    if (!liftedColor) {\n      liftedColor = lift(color$1, -0.1);\n      liftedColorCache.put(color$1, liftedColor);\n    }\n\n    return liftedColor;\n  } else if (isGradientObject(color$1)) {\n    var ret = extend({}, color$1);\n    ret.colorStops = map(color$1.colorStops, function (stop) {\n      return {\n        offset: stop.offset,\n        color: lift(stop.color, -0.1)\n      };\n    });\n    return ret;\n  } // Change nothing.\n\n\n  return color$1;\n}\n\nfunction doChangeHoverState(el, stateName, hoverStateEnum) {\n  if (el.onHoverStateChange && (el.hoverState || 0) !== hoverStateEnum) {\n    el.onHoverStateChange(stateName);\n  }\n\n  el.hoverState = hoverStateEnum;\n}\n\nfunction singleEnterEmphasis(el) {\n  // Only mark the flag.\n  // States will be applied in the echarts.ts in next frame.\n  doChangeHoverState(el, 'emphasis', HOVER_STATE_EMPHASIS);\n}\n\nfunction singleLeaveEmphasis(el) {\n  // Only mark the flag.\n  // States will be applied in the echarts.ts in next frame.\n  if (el.hoverState === HOVER_STATE_EMPHASIS) {\n    doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n  }\n}\n\nfunction singleEnterBlur(el) {\n  doChangeHoverState(el, 'blur', HOVER_STATE_BLUR);\n}\n\nfunction singleLeaveBlur(el) {\n  if (el.hoverState === HOVER_STATE_BLUR) {\n    doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n  }\n}\n\nfunction singleEnterSelect(el) {\n  el.selected = true;\n}\n\nfunction singleLeaveSelect(el) {\n  el.selected = false;\n}\n\nfunction updateElementState(el, updater, commonParam) {\n  updater(el, commonParam);\n}\n\nfunction traverseUpdateState(el, updater, commonParam) {\n  updateElementState(el, updater, commonParam);\n  el.isGroup && el.traverse(function (child) {\n    updateElementState(child, updater, commonParam);\n  });\n}\n\nfunction setStatesFlag(el, stateName) {\n  switch (stateName) {\n    case 'emphasis':\n      el.hoverState = HOVER_STATE_EMPHASIS;\n      break;\n\n    case 'normal':\n      el.hoverState = HOVER_STATE_NORMAL;\n      break;\n\n    case 'blur':\n      el.hoverState = HOVER_STATE_BLUR;\n      break;\n\n    case 'select':\n      el.selected = true;\n  }\n}\n\nfunction getFromStateStyle(el, props, toStateName, defaultValue) {\n  var style = el.style;\n  var fromState = {};\n\n  for (var i = 0; i < props.length; i++) {\n    var propName = props[i];\n    var val = style[propName];\n    fromState[propName] = val == null ? defaultValue && defaultValue[propName] : val;\n  }\n\n  for (var i = 0; i < el.animators.length; i++) {\n    var animator = el.animators[i];\n\n    if (animator.__fromStateTransition // Don't consider the animation to emphasis state.\n    && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') {\n      animator.saveTo(fromState, props);\n    }\n  }\n\n  return fromState;\n}\n\nfunction createEmphasisDefaultState(el, stateName, targetStates, state) {\n  var hasSelect = targetStates && indexOf(targetStates, 'select') >= 0;\n  var cloned = false;\n\n  if (el instanceof Path) {\n    var store = getSavedStates(el);\n    var fromFill = hasSelect ? store.selectFill || store.normalFill : store.normalFill;\n    var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke;\n\n    if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) {\n      state = state || {};\n      var emphasisStyle = state.style || {}; // inherit case\n\n      if (emphasisStyle.fill === 'inherit') {\n        cloned = true;\n        state = extend({}, state);\n        emphasisStyle = extend({}, emphasisStyle);\n        emphasisStyle.fill = fromFill;\n      } // Apply default color lift\n      else if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) {\n          cloned = true; // Not modify the original value.\n\n          state = extend({}, state);\n          emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times.\n\n          emphasisStyle.fill = liftColor(fromFill);\n        } // Not highlight stroke if fill has been highlighted.\n        else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) {\n            if (!cloned) {\n              state = extend({}, state);\n              emphasisStyle = extend({}, emphasisStyle);\n            }\n\n            emphasisStyle.stroke = liftColor(fromStroke);\n          }\n\n      state.style = emphasisStyle;\n    }\n  }\n\n  if (state) {\n    // TODO Share with textContent?\n    if (state.z2 == null) {\n      if (!cloned) {\n        state = extend({}, state);\n      }\n\n      var z2EmphasisLift = el.z2EmphasisLift;\n      state.z2 = el.z2 + (z2EmphasisLift != null ? z2EmphasisLift : Z2_EMPHASIS_LIFT);\n    }\n  }\n\n  return state;\n}\n\nfunction createSelectDefaultState(el, stateName, state) {\n  // const hasSelect = indexOf(el.currentStates, stateName) >= 0;\n  if (state) {\n    // TODO Share with textContent?\n    if (state.z2 == null) {\n      state = extend({}, state);\n      var z2SelectLift = el.z2SelectLift;\n      state.z2 = el.z2 + (z2SelectLift != null ? z2SelectLift : Z2_SELECT_LIFT);\n    }\n  }\n\n  return state;\n}\n\nfunction createBlurDefaultState(el, stateName, state) {\n  var hasBlur = indexOf(el.currentStates, stateName) >= 0;\n  var currentOpacity = el.style.opacity;\n  var fromState = !hasBlur ? getFromStateStyle(el, ['opacity'], stateName, {\n    opacity: 1\n  }) : null;\n  state = state || {};\n  var blurStyle = state.style || {};\n\n  if (blurStyle.opacity == null) {\n    // clone state\n    state = extend({}, state);\n    blurStyle = extend({\n      // Already being applied 'emphasis'. DON'T mul opacity multiple times.\n      opacity: hasBlur ? currentOpacity : fromState.opacity * 0.1\n    }, blurStyle);\n    state.style = blurStyle;\n  }\n\n  return state;\n}\n\nfunction elementStateProxy(stateName, targetStates) {\n  var state = this.states[stateName];\n\n  if (this.style) {\n    if (stateName === 'emphasis') {\n      return createEmphasisDefaultState(this, stateName, targetStates, state);\n    } else if (stateName === 'blur') {\n      return createBlurDefaultState(this, stateName, state);\n    } else if (stateName === 'select') {\n      return createSelectDefaultState(this, stateName, state);\n    }\n  }\n\n  return state;\n}\n/**\n * Set hover style (namely \"emphasis style\") of element.\n * @param el Should not be `zrender/graphic/Group`.\n * @param focus 'self' | 'selfInSeries' | 'series'\n */\n\n\nfunction setDefaultStateProxy(el) {\n  el.stateProxy = elementStateProxy;\n  var textContent = el.getTextContent();\n  var textGuide = el.getTextGuideLine();\n\n  if (textContent) {\n    textContent.stateProxy = elementStateProxy;\n  }\n\n  if (textGuide) {\n    textGuide.stateProxy = elementStateProxy;\n  }\n}\nfunction enterEmphasisWhenMouseOver(el, e) {\n  !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n  && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis);\n}\nfunction leaveEmphasisWhenMouseOut(el, e) {\n  !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n  && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis);\n}\nfunction enterEmphasis(el, highlightDigit) {\n  el.__highByOuter |= 1 << (highlightDigit || 0);\n  traverseUpdateState(el, singleEnterEmphasis);\n}\nfunction leaveEmphasis(el, highlightDigit) {\n  !(el.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdateState(el, singleLeaveEmphasis);\n}\nfunction enterBlur(el) {\n  traverseUpdateState(el, singleEnterBlur);\n}\nfunction leaveBlur(el) {\n  traverseUpdateState(el, singleLeaveBlur);\n}\nfunction enterSelect(el) {\n  traverseUpdateState(el, singleEnterSelect);\n}\nfunction leaveSelect(el) {\n  traverseUpdateState(el, singleLeaveSelect);\n}\n\nfunction shouldSilent(el, e) {\n  return el.__highDownSilentOnTouch && e.zrByTouch;\n}\n\nfunction allLeaveBlur(api) {\n  var model = api.getModel();\n  var leaveBlurredSeries = [];\n  var allComponentViews = [];\n  model.eachComponent(function (componentType, componentModel) {\n    var componentStates = getComponentStates(componentModel);\n    var isSeries = componentType === 'series';\n    var view = isSeries ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel);\n    !isSeries && allComponentViews.push(view);\n\n    if (componentStates.isBlured) {\n      // Leave blur anyway\n      view.group.traverse(function (child) {\n        singleLeaveBlur(child);\n      });\n      isSeries && leaveBlurredSeries.push(componentModel);\n    }\n\n    componentStates.isBlured = false;\n  });\n  each(allComponentViews, function (view) {\n    if (view && view.toggleBlurSeries) {\n      view.toggleBlurSeries(leaveBlurredSeries, false, model);\n    }\n  });\n}\nfunction blurSeries(targetSeriesIndex, focus, blurScope, api) {\n  var ecModel = api.getModel();\n  blurScope = blurScope || 'coordinateSystem';\n\n  function leaveBlurOfIndices(data, dataIndices) {\n    for (var i = 0; i < dataIndices.length; i++) {\n      var itemEl = data.getItemGraphicEl(dataIndices[i]);\n      itemEl && leaveBlur(itemEl);\n    }\n  }\n\n  if (targetSeriesIndex == null) {\n    return;\n  }\n\n  if (!focus || focus === 'none') {\n    return;\n  }\n\n  var targetSeriesModel = ecModel.getSeriesByIndex(targetSeriesIndex);\n  var targetCoordSys = targetSeriesModel.coordinateSystem;\n\n  if (targetCoordSys && targetCoordSys.master) {\n    targetCoordSys = targetCoordSys.master;\n  }\n\n  var blurredSeries = [];\n  ecModel.eachSeries(function (seriesModel) {\n    var sameSeries = targetSeriesModel === seriesModel;\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (coordSys && coordSys.master) {\n      coordSys = coordSys.master;\n    }\n\n    var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries; // If there is no coordinate system. use sameSeries instead.\n\n    if (!( // Not blur other series if blurScope series\n    blurScope === 'series' && !sameSeries // Not blur other coordinate system if blurScope is coordinateSystem\n    || blurScope === 'coordinateSystem' && !sameCoordSys // Not blur self series if focus is series.\n    || focus === 'series' && sameSeries // TODO blurScope: coordinate system\n    )) {\n      var view = api.getViewOfSeriesModel(seriesModel);\n      view.group.traverse(function (child) {\n        singleEnterBlur(child);\n      });\n\n      if (isArrayLike(focus)) {\n        leaveBlurOfIndices(seriesModel.getData(), focus);\n      } else if (isObject(focus)) {\n        var dataTypes = keys(focus);\n\n        for (var d = 0; d < dataTypes.length; d++) {\n          leaveBlurOfIndices(seriesModel.getData(dataTypes[d]), focus[dataTypes[d]]);\n        }\n      }\n\n      blurredSeries.push(seriesModel);\n      getComponentStates(seriesModel).isBlured = true;\n    }\n  });\n  ecModel.eachComponent(function (componentType, componentModel) {\n    if (componentType === 'series') {\n      return;\n    }\n\n    var view = api.getViewOfComponentModel(componentModel);\n\n    if (view && view.toggleBlurSeries) {\n      view.toggleBlurSeries(blurredSeries, true, ecModel);\n    }\n  });\n}\nfunction blurComponent(componentMainType, componentIndex, api) {\n  if (componentMainType == null || componentIndex == null) {\n    return;\n  }\n\n  var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n  if (!componentModel) {\n    return;\n  }\n\n  getComponentStates(componentModel).isBlured = true;\n  var view = api.getViewOfComponentModel(componentModel);\n\n  if (!view || !view.focusBlurEnabled) {\n    return;\n  }\n\n  view.group.traverse(function (child) {\n    singleEnterBlur(child);\n  });\n}\nfunction blurSeriesFromHighlightPayload(seriesModel, payload, api) {\n  var seriesIndex = seriesModel.seriesIndex;\n  var data = seriesModel.getData(payload.dataType);\n\n  if (!data) {\n    if (\"development\" !== 'production') {\n      error(\"Unknown dataType \" + payload.dataType);\n    }\n\n    return;\n  }\n\n  var dataIndex = queryDataIndex(data, payload); // Pick the first one if there is multiple/none exists.\n\n  dataIndex = (isArray(dataIndex) ? dataIndex[0] : dataIndex) || 0;\n  var el = data.getItemGraphicEl(dataIndex);\n\n  if (!el) {\n    var count = data.count();\n    var current = 0; // If data on dataIndex is NaN.\n\n    while (!el && current < count) {\n      el = data.getItemGraphicEl(current++);\n    }\n  }\n\n  if (el) {\n    var ecData = getECData(el);\n    blurSeries(seriesIndex, ecData.focus, ecData.blurScope, api);\n  } else {\n    // If there is no element put on the data. Try getting it from raw option\n    // TODO Should put it on seriesModel?\n    var focus_1 = seriesModel.get(['emphasis', 'focus']);\n    var blurScope = seriesModel.get(['emphasis', 'blurScope']);\n\n    if (focus_1 != null) {\n      blurSeries(seriesIndex, focus_1, blurScope, api);\n    }\n  }\n}\nfunction findComponentHighDownDispatchers(componentMainType, componentIndex, name, api) {\n  var ret = {\n    focusSelf: false,\n    dispatchers: null\n  };\n\n  if (componentMainType == null || componentMainType === 'series' || componentIndex == null || name == null) {\n    return ret;\n  }\n\n  var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n  if (!componentModel) {\n    return ret;\n  }\n\n  var view = api.getViewOfComponentModel(componentModel);\n\n  if (!view || !view.findHighDownDispatchers) {\n    return ret;\n  }\n\n  var dispatchers = view.findHighDownDispatchers(name); // At presnet, the component (like Geo) only blur inside itself.\n  // So we do not use `blurScope` in component.\n\n  var focusSelf;\n\n  for (var i = 0; i < dispatchers.length; i++) {\n    if (\"development\" !== 'production' && !isHighDownDispatcher(dispatchers[i])) {\n      error('param should be highDownDispatcher');\n    }\n\n    if (getECData(dispatchers[i]).focus === 'self') {\n      focusSelf = true;\n      break;\n    }\n  }\n\n  return {\n    focusSelf: focusSelf,\n    dispatchers: dispatchers\n  };\n}\nfunction handleGlobalMouseOverForHighDown(dispatcher, e, api) {\n  if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n    error('param should be highDownDispatcher');\n  }\n\n  var ecData = getECData(dispatcher);\n\n  var _a = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api),\n      dispatchers = _a.dispatchers,\n      focusSelf = _a.focusSelf; // If `findHighDownDispatchers` is supported on the component,\n  // highlight/downplay elements with the same name.\n\n\n  if (dispatchers) {\n    if (focusSelf) {\n      blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n    }\n\n    each(dispatchers, function (dispatcher) {\n      return enterEmphasisWhenMouseOver(dispatcher, e);\n    });\n  } else {\n    // Try blur all in the related series. Then emphasis the hoverred.\n    // TODO. progressive mode.\n    blurSeries(ecData.seriesIndex, ecData.focus, ecData.blurScope, api);\n\n    if (ecData.focus === 'self') {\n      blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n    } // Other than series, component that not support `findHighDownDispatcher` will\n    // also use it. But in this case, highlight/downplay are only supported in\n    // mouse hover but not in dispatchAction.\n\n\n    enterEmphasisWhenMouseOver(dispatcher, e);\n  }\n}\nfunction handleGlobalMouseOutForHighDown(dispatcher, e, api) {\n  if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n    error('param should be highDownDispatcher');\n  }\n\n  allLeaveBlur(api);\n  var ecData = getECData(dispatcher);\n  var dispatchers = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api).dispatchers;\n\n  if (dispatchers) {\n    each(dispatchers, function (dispatcher) {\n      return leaveEmphasisWhenMouseOut(dispatcher, e);\n    });\n  } else {\n    leaveEmphasisWhenMouseOut(dispatcher, e);\n  }\n}\nfunction toggleSelectionFromPayload(seriesModel, payload, api) {\n  if (!isSelectChangePayload(payload)) {\n    return;\n  }\n\n  var dataType = payload.dataType;\n  var data = seriesModel.getData(dataType);\n  var dataIndex = queryDataIndex(data, payload);\n\n  if (!isArray(dataIndex)) {\n    dataIndex = [dataIndex];\n  }\n\n  seriesModel[payload.type === TOGGLE_SELECT_ACTION_TYPE ? 'toggleSelect' : payload.type === SELECT_ACTION_TYPE ? 'select' : 'unselect'](dataIndex, dataType);\n}\nfunction updateSeriesElementSelection(seriesModel) {\n  var allData = seriesModel.getAllData();\n  each(allData, function (_a) {\n    var data = _a.data,\n        type = _a.type;\n    data.eachItemGraphicEl(function (el, idx) {\n      seriesModel.isSelected(idx, type) ? enterSelect(el) : leaveSelect(el);\n    });\n  });\n}\nfunction getAllSelectedIndices(ecModel) {\n  var ret = [];\n  ecModel.eachSeries(function (seriesModel) {\n    var allData = seriesModel.getAllData();\n    each(allData, function (_a) {\n      var data = _a.data,\n          type = _a.type;\n      var dataIndices = seriesModel.getSelectedDataIndices();\n\n      if (dataIndices.length > 0) {\n        var item = {\n          dataIndex: dataIndices,\n          seriesIndex: seriesModel.seriesIndex\n        };\n\n        if (type != null) {\n          item.dataType = type;\n        }\n\n        ret.push(item);\n      }\n    });\n  });\n  return ret;\n}\n/**\n * Enable the function that mouseover will trigger the emphasis state.\n *\n * NOTE:\n * This function should be used on the element with dataIndex, seriesIndex.\n *\n */\n\nfunction enableHoverEmphasis(el, focus, blurScope) {\n  setAsHighDownDispatcher(el, true);\n  traverseUpdateState(el, setDefaultStateProxy);\n  enableHoverFocus(el, focus, blurScope);\n}\nfunction disableHoverEmphasis(el) {\n  setAsHighDownDispatcher(el, false);\n}\nfunction toggleHoverEmphasis(el, focus, blurScope, isDisabled) {\n  isDisabled ? disableHoverEmphasis(el) : enableHoverEmphasis(el, focus, blurScope);\n}\nfunction enableHoverFocus(el, focus, blurScope) {\n  var ecData = getECData(el);\n\n  if (focus != null) {\n    // TODO dataIndex may be set after this function. This check is not useful.\n    // if (ecData.dataIndex == null) {\n    //     if (__DEV__) {\n    //         console.warn('focus can only been set on element with dataIndex');\n    //     }\n    // }\n    // else {\n    ecData.focus = focus;\n    ecData.blurScope = blurScope; // }\n  } else if (ecData.focus) {\n    ecData.focus = null;\n  }\n}\nvar OTHER_STATES = ['emphasis', 'blur', 'select'];\nvar defaultStyleGetterMap = {\n  itemStyle: 'getItemStyle',\n  lineStyle: 'getLineStyle',\n  areaStyle: 'getAreaStyle'\n};\n/**\n * Set emphasis/blur/selected states of element.\n */\n\nfunction setStatesStylesFromModel(el, itemModel, styleType, // default itemStyle\ngetter) {\n  styleType = styleType || 'itemStyle';\n\n  for (var i = 0; i < OTHER_STATES.length; i++) {\n    var stateName = OTHER_STATES[i];\n    var model = itemModel.getModel([stateName, styleType]);\n    var state = el.ensureState(stateName); // Let it throw error if getterType is not found.\n\n    state.style = getter ? getter(model) : model[defaultStyleGetterMap[styleType]]();\n  }\n}\n/**\n *\n * Set element as highlight / downplay dispatcher.\n * It will be checked when element received mouseover event or from highlight action.\n * It's in change of all highlight/downplay behavior of it's children.\n *\n * @param el\n * @param el.highDownSilentOnTouch\n *        In touch device, mouseover event will be trigger on touchstart event\n *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can\n *        conveniently use hoverStyle when tap on touch screen without additional\n *        code for compatibility.\n *        But if the chart/component has select feature, which usually also use\n *        hoverStyle, there might be conflict between 'select-highlight' and\n *        'hover-highlight' especially when roam is enabled (see geo for example).\n *        In this case, `highDownSilentOnTouch` should be used to disable\n *        hover-highlight on touch device.\n * @param asDispatcher If `false`, do not set as \"highDownDispatcher\".\n */\n\nfunction setAsHighDownDispatcher(el, asDispatcher) {\n  var disable = asDispatcher === false;\n  var extendedEl = el; // Make `highDownSilentOnTouch` and `onStateChange` only work after\n  // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.\n\n  if (el.highDownSilentOnTouch) {\n    extendedEl.__highDownSilentOnTouch = el.highDownSilentOnTouch;\n  } // Simple optimize, since this method might be\n  // called for each elements of a group in some cases.\n\n\n  if (!disable || extendedEl.__highDownDispatcher) {\n    // Emphasis, normal can be triggered manually by API or other components like hover link.\n    // el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent);\n    // Also keep previous record.\n    extendedEl.__highByOuter = extendedEl.__highByOuter || 0;\n    extendedEl.__highDownDispatcher = !disable;\n  }\n}\nfunction isHighDownDispatcher(el) {\n  return !!(el && el.__highDownDispatcher);\n}\n/**\n * Enable component highlight/downplay features:\n * + hover link (within the same name)\n * + focus blur in component\n */\n\nfunction enableComponentHighDownFeatures(el, componentModel, componentHighDownName) {\n  var ecData = getECData(el);\n  ecData.componentMainType = componentModel.mainType;\n  ecData.componentIndex = componentModel.componentIndex;\n  ecData.componentHighDownName = componentHighDownName;\n}\n/**\n * Support highlight/downplay record on each elements.\n * For the case: hover highlight/downplay (legend, visualMap, ...) and\n * user triggered highlight/downplay should not conflict.\n * Only all of the highlightDigit cleared, return to normal.\n * @param {string} highlightKey\n * @return {number} highlightDigit\n */\n\nfunction getHighlightDigit(highlightKey) {\n  var highlightDigit = _highlightKeyMap[highlightKey];\n\n  if (highlightDigit == null && _highlightNextDigit <= 32) {\n    highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++;\n  }\n\n  return highlightDigit;\n}\nfunction isSelectChangePayload(payload) {\n  var payloadType = payload.type;\n  return payloadType === SELECT_ACTION_TYPE || payloadType === UNSELECT_ACTION_TYPE || payloadType === TOGGLE_SELECT_ACTION_TYPE;\n}\nfunction isHighDownPayload(payload) {\n  var payloadType = payload.type;\n  return payloadType === HIGHLIGHT_ACTION_TYPE || payloadType === DOWNPLAY_ACTION_TYPE;\n}\nfunction savePathStates(el) {\n  var store = getSavedStates(el);\n  store.normalFill = el.style.fill;\n  store.normalStroke = el.style.stroke;\n  var selectState = el.states.select || {};\n  store.selectFill = selectState.style && selectState.style.fill || null;\n  store.selectStroke = selectState.style && selectState.style.stroke || null;\n}\n\nvar CMD$2 = PathProxy.CMD;\nvar points = [[], [], []];\nvar mathSqrt$1 = Math.sqrt;\nvar mathAtan2 = Math.atan2;\nfunction transformPath(path, m) {\n    if (!m) {\n        return;\n    }\n    var data = path.data;\n    var len = path.len();\n    var cmd;\n    var nPoint;\n    var i;\n    var j;\n    var k;\n    var p;\n    var M = CMD$2.M;\n    var C = CMD$2.C;\n    var L = CMD$2.L;\n    var R = CMD$2.R;\n    var A = CMD$2.A;\n    var Q = CMD$2.Q;\n    for (i = 0, j = 0; i < len;) {\n        cmd = data[i++];\n        j = i;\n        nPoint = 0;\n        switch (cmd) {\n            case M:\n                nPoint = 1;\n                break;\n            case L:\n                nPoint = 1;\n                break;\n            case C:\n                nPoint = 3;\n                break;\n            case Q:\n                nPoint = 2;\n                break;\n            case A:\n                var x = m[4];\n                var y = m[5];\n                var sx = mathSqrt$1(m[0] * m[0] + m[1] * m[1]);\n                var sy = mathSqrt$1(m[2] * m[2] + m[3] * m[3]);\n                var angle = mathAtan2(-m[1] / sy, m[0] / sx);\n                data[i] *= sx;\n                data[i++] += x;\n                data[i] *= sy;\n                data[i++] += y;\n                data[i++] *= sx;\n                data[i++] *= sy;\n                data[i++] += angle;\n                data[i++] += angle;\n                i += 2;\n                j = i;\n                break;\n            case R:\n                p[0] = data[i++];\n                p[1] = data[i++];\n                applyTransform(p, p, m);\n                data[j++] = p[0];\n                data[j++] = p[1];\n                p[0] += data[i++];\n                p[1] += data[i++];\n                applyTransform(p, p, m);\n                data[j++] = p[0];\n                data[j++] = p[1];\n        }\n        for (k = 0; k < nPoint; k++) {\n            var p_1 = points[k];\n            p_1[0] = data[i++];\n            p_1[1] = data[i++];\n            applyTransform(p_1, p_1, m);\n            data[j++] = p_1[0];\n            data[j++] = p_1[1];\n        }\n    }\n    path.increaseVersion();\n}\n\nvar mathSqrt$2 = Math.sqrt;\nvar mathSin$2 = Math.sin;\nvar mathCos$2 = Math.cos;\nvar PI$1 = Math.PI;\nfunction vMag(v) {\n    return Math.sqrt(v[0] * v[0] + v[1] * v[1]);\n}\nfunction vRatio(u, v) {\n    return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));\n}\nfunction vAngle(u, v) {\n    return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)\n        * Math.acos(vRatio(u, v));\n}\nfunction processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {\n    var psi = psiDeg * (PI$1 / 180.0);\n    var xp = mathCos$2(psi) * (x1 - x2) / 2.0\n        + mathSin$2(psi) * (y1 - y2) / 2.0;\n    var yp = -1 * mathSin$2(psi) * (x1 - x2) / 2.0\n        + mathCos$2(psi) * (y1 - y2) / 2.0;\n    var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);\n    if (lambda > 1) {\n        rx *= mathSqrt$2(lambda);\n        ry *= mathSqrt$2(lambda);\n    }\n    var f = (fa === fs ? -1 : 1)\n        * mathSqrt$2((((rx * rx) * (ry * ry))\n            - ((rx * rx) * (yp * yp))\n            - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)\n            + (ry * ry) * (xp * xp))) || 0;\n    var cxp = f * rx * yp / ry;\n    var cyp = f * -ry * xp / rx;\n    var cx = (x1 + x2) / 2.0\n        + mathCos$2(psi) * cxp\n        - mathSin$2(psi) * cyp;\n    var cy = (y1 + y2) / 2.0\n        + mathSin$2(psi) * cxp\n        + mathCos$2(psi) * cyp;\n    var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);\n    var u = [(xp - cxp) / rx, (yp - cyp) / ry];\n    var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];\n    var dTheta = vAngle(u, v);\n    if (vRatio(u, v) <= -1) {\n        dTheta = PI$1;\n    }\n    if (vRatio(u, v) >= 1) {\n        dTheta = 0;\n    }\n    if (dTheta < 0) {\n        var n = Math.round(dTheta / PI$1 * 1e6) / 1e6;\n        dTheta = PI$1 * 2 + (n % 2) * PI$1;\n    }\n    path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);\n}\nvar commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig;\nvar numberReg = /-?([0-9]*\\.)?[0-9]+([eE]-?[0-9]+)?/g;\nfunction createPathProxyFromString(data) {\n    var path = new PathProxy();\n    if (!data) {\n        return path;\n    }\n    var cpx = 0;\n    var cpy = 0;\n    var subpathX = cpx;\n    var subpathY = cpy;\n    var prevCmd;\n    var CMD = PathProxy.CMD;\n    var cmdList = data.match(commandReg);\n    if (!cmdList) {\n        return path;\n    }\n    for (var l = 0; l < cmdList.length; l++) {\n        var cmdText = cmdList[l];\n        var cmdStr = cmdText.charAt(0);\n        var cmd = void 0;\n        var p = cmdText.match(numberReg) || [];\n        var pLen = p.length;\n        for (var i = 0; i < pLen; i++) {\n            p[i] = parseFloat(p[i]);\n        }\n        var off = 0;\n        while (off < pLen) {\n            var ctlPtx = void 0;\n            var ctlPty = void 0;\n            var rx = void 0;\n            var ry = void 0;\n            var psi = void 0;\n            var fa = void 0;\n            var fs = void 0;\n            var x1 = cpx;\n            var y1 = cpy;\n            var len = void 0;\n            var pathData = void 0;\n            switch (cmdStr) {\n                case 'l':\n                    cpx += p[off++];\n                    cpy += p[off++];\n                    cmd = CMD.L;\n                    path.addData(cmd, cpx, cpy);\n                    break;\n                case 'L':\n                    cpx = p[off++];\n                    cpy = p[off++];\n                    cmd = CMD.L;\n                    path.addData(cmd, cpx, cpy);\n                    break;\n                case 'm':\n                    cpx += p[off++];\n                    cpy += p[off++];\n                    cmd = CMD.M;\n                    path.addData(cmd, cpx, cpy);\n                    subpathX = cpx;\n                    subpathY = cpy;\n                    cmdStr = 'l';\n                    break;\n                case 'M':\n                    cpx = p[off++];\n                    cpy = p[off++];\n                    cmd = CMD.M;\n                    path.addData(cmd, cpx, cpy);\n                    subpathX = cpx;\n                    subpathY = cpy;\n                    cmdStr = 'L';\n                    break;\n                case 'h':\n                    cpx += p[off++];\n                    cmd = CMD.L;\n                    path.addData(cmd, cpx, cpy);\n                    break;\n                case 'H':\n                    cpx = p[off++];\n                    cmd = CMD.L;\n                    path.addData(cmd, cpx, cpy);\n                    break;\n                case 'v':\n                    cpy += p[off++];\n                    cmd = CMD.L;\n                    path.addData(cmd, cpx, cpy);\n                    break;\n                case 'V':\n                    cpy = p[off++];\n                    cmd = CMD.L;\n                    path.addData(cmd, cpx, cpy);\n                    break;\n                case 'C':\n                    cmd = CMD.C;\n                    path.addData(cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]);\n                    cpx = p[off - 2];\n                    cpy = p[off - 1];\n                    break;\n                case 'c':\n                    cmd = CMD.C;\n                    path.addData(cmd, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy);\n                    cpx += p[off - 2];\n                    cpy += p[off - 1];\n                    break;\n                case 'S':\n                    ctlPtx = cpx;\n                    ctlPty = cpy;\n                    len = path.len();\n                    pathData = path.data;\n                    if (prevCmd === CMD.C) {\n                        ctlPtx += cpx - pathData[len - 4];\n                        ctlPty += cpy - pathData[len - 3];\n                    }\n                    cmd = CMD.C;\n                    x1 = p[off++];\n                    y1 = p[off++];\n                    cpx = p[off++];\n                    cpy = p[off++];\n                    path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                    break;\n                case 's':\n                    ctlPtx = cpx;\n                    ctlPty = cpy;\n                    len = path.len();\n                    pathData = path.data;\n                    if (prevCmd === CMD.C) {\n                        ctlPtx += cpx - pathData[len - 4];\n                        ctlPty += cpy - pathData[len - 3];\n                    }\n                    cmd = CMD.C;\n                    x1 = cpx + p[off++];\n                    y1 = cpy + p[off++];\n                    cpx += p[off++];\n                    cpy += p[off++];\n                    path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                    break;\n                case 'Q':\n                    x1 = p[off++];\n                    y1 = p[off++];\n                    cpx = p[off++];\n                    cpy = p[off++];\n                    cmd = CMD.Q;\n                    path.addData(cmd, x1, y1, cpx, cpy);\n                    break;\n                case 'q':\n                    x1 = p[off++] + cpx;\n                    y1 = p[off++] + cpy;\n                    cpx += p[off++];\n                    cpy += p[off++];\n                    cmd = CMD.Q;\n                    path.addData(cmd, x1, y1, cpx, cpy);\n                    break;\n                case 'T':\n                    ctlPtx = cpx;\n                    ctlPty = cpy;\n                    len = path.len();\n                    pathData = path.data;\n                    if (prevCmd === CMD.Q) {\n                        ctlPtx += cpx - pathData[len - 4];\n                        ctlPty += cpy - pathData[len - 3];\n                    }\n                    cpx = p[off++];\n                    cpy = p[off++];\n                    cmd = CMD.Q;\n                    path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                    break;\n                case 't':\n                    ctlPtx = cpx;\n                    ctlPty = cpy;\n                    len = path.len();\n                    pathData = path.data;\n                    if (prevCmd === CMD.Q) {\n                        ctlPtx += cpx - pathData[len - 4];\n                        ctlPty += cpy - pathData[len - 3];\n                    }\n                    cpx += p[off++];\n                    cpy += p[off++];\n                    cmd = CMD.Q;\n                    path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                    break;\n                case 'A':\n                    rx = p[off++];\n                    ry = p[off++];\n                    psi = p[off++];\n                    fa = p[off++];\n                    fs = p[off++];\n                    x1 = cpx, y1 = cpy;\n                    cpx = p[off++];\n                    cpy = p[off++];\n                    cmd = CMD.A;\n                    processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                    break;\n                case 'a':\n                    rx = p[off++];\n                    ry = p[off++];\n                    psi = p[off++];\n                    fa = p[off++];\n                    fs = p[off++];\n                    x1 = cpx, y1 = cpy;\n                    cpx += p[off++];\n                    cpy += p[off++];\n                    cmd = CMD.A;\n                    processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                    break;\n            }\n        }\n        if (cmdStr === 'z' || cmdStr === 'Z') {\n            cmd = CMD.Z;\n            path.addData(cmd);\n            cpx = subpathX;\n            cpy = subpathY;\n        }\n        prevCmd = cmd;\n    }\n    path.toStatic();\n    return path;\n}\nvar SVGPath = (function (_super) {\n    __extends(SVGPath, _super);\n    function SVGPath() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    SVGPath.prototype.applyTransform = function (m) { };\n    return SVGPath;\n}(Path));\nfunction isPathProxy(path) {\n    return path.setData != null;\n}\nfunction createPathOptions(str, opts) {\n    var pathProxy = createPathProxyFromString(str);\n    var innerOpts = extend({}, opts);\n    innerOpts.buildPath = function (path) {\n        if (isPathProxy(path)) {\n            path.setData(pathProxy.data);\n            var ctx = path.getContext();\n            if (ctx) {\n                path.rebuildPath(ctx, 1);\n            }\n        }\n        else {\n            var ctx = path;\n            pathProxy.rebuildPath(ctx, 1);\n        }\n    };\n    innerOpts.applyTransform = function (m) {\n        transformPath(pathProxy, m);\n        this.dirtyShape();\n    };\n    return innerOpts;\n}\nfunction createFromString(str, opts) {\n    return new SVGPath(createPathOptions(str, opts));\n}\nfunction extendFromString(str, defaultOpts) {\n    var innerOpts = createPathOptions(str, defaultOpts);\n    var Sub = (function (_super) {\n        __extends(Sub, _super);\n        function Sub(opts) {\n            var _this = _super.call(this, opts) || this;\n            _this.applyTransform = innerOpts.applyTransform;\n            _this.buildPath = innerOpts.buildPath;\n            return _this;\n        }\n        return Sub;\n    }(SVGPath));\n    return Sub;\n}\nfunction mergePath(pathEls, opts) {\n    var pathList = [];\n    var len = pathEls.length;\n    for (var i = 0; i < len; i++) {\n        var pathEl = pathEls[i];\n        pathList.push(pathEl.getUpdatedPathProxy(true));\n    }\n    var pathBundle = new Path(opts);\n    pathBundle.createPathProxy();\n    pathBundle.buildPath = function (path) {\n        if (isPathProxy(path)) {\n            path.appendPath(pathList);\n            var ctx = path.getContext();\n            if (ctx) {\n                path.rebuildPath(ctx, 1);\n            }\n        }\n    };\n    return pathBundle;\n}\nfunction clonePath(sourcePath, opts) {\n    opts = opts || {};\n    var path = new Path();\n    if (sourcePath.shape) {\n        path.setShape(sourcePath.shape);\n    }\n    path.setStyle(sourcePath.style);\n    if (opts.bakeTransform) {\n        transformPath(path.path, sourcePath.getComputedTransform());\n    }\n    else {\n        if (opts.toLocal) {\n            path.setLocalTransform(sourcePath.getComputedTransform());\n        }\n        else {\n            path.copyTransform(sourcePath);\n        }\n    }\n    path.buildPath = sourcePath.buildPath;\n    path.applyTransform = path.applyTransform;\n    path.z = sourcePath.z;\n    path.z2 = sourcePath.z2;\n    path.zlevel = sourcePath.zlevel;\n    return path;\n}\n\nvar CircleShape = (function () {\n    function CircleShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.r = 0;\n    }\n    return CircleShape;\n}());\nvar Circle = (function (_super) {\n    __extends(Circle, _super);\n    function Circle(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Circle.prototype.getDefaultShape = function () {\n        return new CircleShape();\n    };\n    Circle.prototype.buildPath = function (ctx, shape) {\n        ctx.moveTo(shape.cx + shape.r, shape.cy);\n        ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);\n    };\n    return Circle;\n}(Path));\nCircle.prototype.type = 'circle';\n\nvar EllipseShape = (function () {\n    function EllipseShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.rx = 0;\n        this.ry = 0;\n    }\n    return EllipseShape;\n}());\nvar Ellipse = (function (_super) {\n    __extends(Ellipse, _super);\n    function Ellipse(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Ellipse.prototype.getDefaultShape = function () {\n        return new EllipseShape();\n    };\n    Ellipse.prototype.buildPath = function (ctx, shape) {\n        var k = 0.5522848;\n        var x = shape.cx;\n        var y = shape.cy;\n        var a = shape.rx;\n        var b = shape.ry;\n        var ox = a * k;\n        var oy = b * k;\n        ctx.moveTo(x - a, y);\n        ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);\n        ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);\n        ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);\n        ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);\n        ctx.closePath();\n    };\n    return Ellipse;\n}(Path));\nEllipse.prototype.type = 'ellipse';\n\nvar PI$2 = Math.PI;\nvar PI2$5 = PI$2 * 2;\nvar mathSin$3 = Math.sin;\nvar mathCos$3 = Math.cos;\nvar mathACos = Math.acos;\nvar mathATan2 = Math.atan2;\nvar mathAbs$1 = Math.abs;\nvar mathSqrt$3 = Math.sqrt;\nvar mathMax$3 = Math.max;\nvar mathMin$3 = Math.min;\nvar e = 1e-4;\nfunction intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n    var dx10 = x1 - x0;\n    var dy10 = y1 - y0;\n    var dx32 = x3 - x2;\n    var dy32 = y3 - y2;\n    var t = dy32 * dx10 - dx32 * dy10;\n    if (t * t < e) {\n        return;\n    }\n    t = (dx32 * (y0 - y2) - dy32 * (x0 - x2)) / t;\n    return [x0 + t * dx10, y0 + t * dy10];\n}\nfunction computeCornerTangents(x0, y0, x1, y1, radius, cr, clockwise) {\n    var x01 = x0 - x1;\n    var y01 = y0 - y1;\n    var lo = (clockwise ? cr : -cr) / mathSqrt$3(x01 * x01 + y01 * y01);\n    var ox = lo * y01;\n    var oy = -lo * x01;\n    var x11 = x0 + ox;\n    var y11 = y0 + oy;\n    var x10 = x1 + ox;\n    var y10 = y1 + oy;\n    var x00 = (x11 + x10) / 2;\n    var y00 = (y11 + y10) / 2;\n    var dx = x10 - x11;\n    var dy = y10 - y11;\n    var d2 = dx * dx + dy * dy;\n    var r = radius - cr;\n    var s = x11 * y10 - x10 * y11;\n    var d = (dy < 0 ? -1 : 1) * mathSqrt$3(mathMax$3(0, r * r * d2 - s * s));\n    var cx0 = (s * dy - dx * d) / d2;\n    var cy0 = (-s * dx - dy * d) / d2;\n    var cx1 = (s * dy + dx * d) / d2;\n    var cy1 = (-s * dx + dy * d) / d2;\n    var dx0 = cx0 - x00;\n    var dy0 = cy0 - y00;\n    var dx1 = cx1 - x00;\n    var dy1 = cy1 - y00;\n    if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) {\n        cx0 = cx1;\n        cy0 = cy1;\n    }\n    return {\n        cx: cx0,\n        cy: cy0,\n        x0: -ox,\n        y0: -oy,\n        x1: cx0 * (radius / r - 1),\n        y1: cy0 * (radius / r - 1)\n    };\n}\nfunction normalizeCornerRadius(cr) {\n    var arr;\n    if (isArray(cr)) {\n        var len = cr.length;\n        if (!len) {\n            return cr;\n        }\n        if (len === 1) {\n            arr = [cr[0], cr[0], 0, 0];\n        }\n        else if (len === 2) {\n            arr = [cr[0], cr[0], cr[1], cr[1]];\n        }\n        else if (len === 3) {\n            arr = cr.concat(cr[2]);\n        }\n        else {\n            arr = cr;\n        }\n    }\n    else {\n        arr = [cr, cr, cr, cr];\n    }\n    return arr;\n}\nfunction buildPath$1(ctx, shape) {\n    var _a;\n    var radius = mathMax$3(shape.r, 0);\n    var innerRadius = mathMax$3(shape.r0 || 0, 0);\n    var hasRadius = radius > 0;\n    var hasInnerRadius = innerRadius > 0;\n    if (!hasRadius && !hasInnerRadius) {\n        return;\n    }\n    if (!hasRadius) {\n        radius = innerRadius;\n        innerRadius = 0;\n    }\n    if (innerRadius > radius) {\n        var tmp = radius;\n        radius = innerRadius;\n        innerRadius = tmp;\n    }\n    var startAngle = shape.startAngle, endAngle = shape.endAngle;\n    if (isNaN(startAngle) || isNaN(endAngle)) {\n        return;\n    }\n    var cx = shape.cx, cy = shape.cy;\n    var clockwise = !!shape.clockwise;\n    var arc = mathAbs$1(endAngle - startAngle);\n    var mod = arc > PI2$5 && arc % PI2$5;\n    mod > e && (arc = mod);\n    if (!(radius > e)) {\n        ctx.moveTo(cx, cy);\n    }\n    else if (arc > PI2$5 - e) {\n        ctx.moveTo(cx + radius * mathCos$3(startAngle), cy + radius * mathSin$3(startAngle));\n        ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n        if (innerRadius > e) {\n            ctx.moveTo(cx + innerRadius * mathCos$3(endAngle), cy + innerRadius * mathSin$3(endAngle));\n            ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n        }\n    }\n    else {\n        var icrStart = void 0;\n        var icrEnd = void 0;\n        var ocrStart = void 0;\n        var ocrEnd = void 0;\n        var ocrs = void 0;\n        var ocre = void 0;\n        var icrs = void 0;\n        var icre = void 0;\n        var ocrMax = void 0;\n        var icrMax = void 0;\n        var limitedOcrMax = void 0;\n        var limitedIcrMax = void 0;\n        var xre = void 0;\n        var yre = void 0;\n        var xirs = void 0;\n        var yirs = void 0;\n        var xrs = radius * mathCos$3(startAngle);\n        var yrs = radius * mathSin$3(startAngle);\n        var xire = innerRadius * mathCos$3(endAngle);\n        var yire = innerRadius * mathSin$3(endAngle);\n        var hasArc = arc > e;\n        if (hasArc) {\n            var cornerRadius = shape.cornerRadius;\n            if (cornerRadius) {\n                _a = normalizeCornerRadius(cornerRadius), icrStart = _a[0], icrEnd = _a[1], ocrStart = _a[2], ocrEnd = _a[3];\n            }\n            var halfRd = mathAbs$1(radius - innerRadius) / 2;\n            ocrs = mathMin$3(halfRd, ocrStart);\n            ocre = mathMin$3(halfRd, ocrEnd);\n            icrs = mathMin$3(halfRd, icrStart);\n            icre = mathMin$3(halfRd, icrEnd);\n            limitedOcrMax = ocrMax = mathMax$3(ocrs, ocre);\n            limitedIcrMax = icrMax = mathMax$3(icrs, icre);\n            if (ocrMax > e || icrMax > e) {\n                xre = radius * mathCos$3(endAngle);\n                yre = radius * mathSin$3(endAngle);\n                xirs = innerRadius * mathCos$3(startAngle);\n                yirs = innerRadius * mathSin$3(startAngle);\n                if (arc < PI$2) {\n                    var it_1 = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire);\n                    if (it_1) {\n                        var x0 = xrs - it_1[0];\n                        var y0 = yrs - it_1[1];\n                        var x1 = xre - it_1[0];\n                        var y1 = yre - it_1[1];\n                        var a = 1 / mathSin$3(mathACos((x0 * x1 + y0 * y1) / (mathSqrt$3(x0 * x0 + y0 * y0) * mathSqrt$3(x1 * x1 + y1 * y1))) / 2);\n                        var b = mathSqrt$3(it_1[0] * it_1[0] + it_1[1] * it_1[1]);\n                        limitedOcrMax = mathMin$3(ocrMax, (radius - b) / (a + 1));\n                        limitedIcrMax = mathMin$3(icrMax, (innerRadius - b) / (a - 1));\n                    }\n                }\n            }\n        }\n        if (!hasArc) {\n            ctx.moveTo(cx + xrs, cy + yrs);\n        }\n        else if (limitedOcrMax > e) {\n            var crStart = mathMin$3(ocrStart, limitedOcrMax);\n            var crEnd = mathMin$3(ocrEnd, limitedOcrMax);\n            var ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, crStart, clockwise);\n            var ct1 = computeCornerTangents(xre, yre, xire, yire, radius, crEnd, clockwise);\n            ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n            if (limitedOcrMax < ocrMax && crStart === crEnd) {\n                ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n            }\n            else {\n                crStart > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise);\n                crEnd > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n            }\n        }\n        else {\n            ctx.moveTo(cx + xrs, cy + yrs);\n            ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n        }\n        if (!(innerRadius > e) || !hasArc) {\n            ctx.lineTo(cx + xire, cy + yire);\n        }\n        else if (limitedIcrMax > e) {\n            var crStart = mathMin$3(icrStart, limitedIcrMax);\n            var crEnd = mathMin$3(icrEnd, limitedIcrMax);\n            var ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -crEnd, clockwise);\n            var ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -crStart, clockwise);\n            ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n            if (limitedIcrMax < icrMax && crStart === crEnd) {\n                ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n            }\n            else {\n                crEnd > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise);\n                crStart > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n            }\n        }\n        else {\n            ctx.lineTo(cx + xire, cy + yire);\n            ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n        }\n    }\n    ctx.closePath();\n}\n\nvar SectorShape = (function () {\n    function SectorShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.r0 = 0;\n        this.r = 0;\n        this.startAngle = 0;\n        this.endAngle = Math.PI * 2;\n        this.clockwise = true;\n        this.cornerRadius = 0;\n    }\n    return SectorShape;\n}());\nvar Sector = (function (_super) {\n    __extends(Sector, _super);\n    function Sector(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Sector.prototype.getDefaultShape = function () {\n        return new SectorShape();\n    };\n    Sector.prototype.buildPath = function (ctx, shape) {\n        buildPath$1(ctx, shape);\n    };\n    Sector.prototype.isZeroArea = function () {\n        return this.shape.startAngle === this.shape.endAngle\n            || this.shape.r === this.shape.r0;\n    };\n    return Sector;\n}(Path));\nSector.prototype.type = 'sector';\n\nvar RingShape = (function () {\n    function RingShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.r = 0;\n        this.r0 = 0;\n    }\n    return RingShape;\n}());\nvar Ring = (function (_super) {\n    __extends(Ring, _super);\n    function Ring(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Ring.prototype.getDefaultShape = function () {\n        return new RingShape();\n    };\n    Ring.prototype.buildPath = function (ctx, shape) {\n        var x = shape.cx;\n        var y = shape.cy;\n        var PI2 = Math.PI * 2;\n        ctx.moveTo(x + shape.r, y);\n        ctx.arc(x, y, shape.r, 0, PI2, false);\n        ctx.moveTo(x + shape.r0, y);\n        ctx.arc(x, y, shape.r0, 0, PI2, true);\n    };\n    return Ring;\n}(Path));\nRing.prototype.type = 'ring';\n\nfunction smoothBezier(points, smooth, isLoop, constraint) {\n    var cps = [];\n    var v = [];\n    var v1 = [];\n    var v2 = [];\n    var prevPoint;\n    var nextPoint;\n    var min$1;\n    var max$1;\n    if (constraint) {\n        min$1 = [Infinity, Infinity];\n        max$1 = [-Infinity, -Infinity];\n        for (var i = 0, len = points.length; i < len; i++) {\n            min(min$1, min$1, points[i]);\n            max(max$1, max$1, points[i]);\n        }\n        min(min$1, min$1, constraint[0]);\n        max(max$1, max$1, constraint[1]);\n    }\n    for (var i = 0, len = points.length; i < len; i++) {\n        var point = points[i];\n        if (isLoop) {\n            prevPoint = points[i ? i - 1 : len - 1];\n            nextPoint = points[(i + 1) % len];\n        }\n        else {\n            if (i === 0 || i === len - 1) {\n                cps.push(clone$1(points[i]));\n                continue;\n            }\n            else {\n                prevPoint = points[i - 1];\n                nextPoint = points[i + 1];\n            }\n        }\n        sub(v, nextPoint, prevPoint);\n        scale(v, v, smooth);\n        var d0 = distance(point, prevPoint);\n        var d1 = distance(point, nextPoint);\n        var sum = d0 + d1;\n        if (sum !== 0) {\n            d0 /= sum;\n            d1 /= sum;\n        }\n        scale(v1, v, -d0);\n        scale(v2, v, d1);\n        var cp0 = add([], point, v1);\n        var cp1 = add([], point, v2);\n        if (constraint) {\n            max(cp0, cp0, min$1);\n            min(cp0, cp0, max$1);\n            max(cp1, cp1, min$1);\n            min(cp1, cp1, max$1);\n        }\n        cps.push(cp0);\n        cps.push(cp1);\n    }\n    if (isLoop) {\n        cps.push(cps.shift());\n    }\n    return cps;\n}\n\nfunction buildPath$2(ctx, shape, closePath) {\n    var smooth = shape.smooth;\n    var points = shape.points;\n    if (points && points.length >= 2) {\n        if (smooth) {\n            var controlPoints = smoothBezier(points, smooth, closePath, shape.smoothConstraint);\n            ctx.moveTo(points[0][0], points[0][1]);\n            var len = points.length;\n            for (var i = 0; i < (closePath ? len : len - 1); i++) {\n                var cp1 = controlPoints[i * 2];\n                var cp2 = controlPoints[i * 2 + 1];\n                var p = points[(i + 1) % len];\n                ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]);\n            }\n        }\n        else {\n            ctx.moveTo(points[0][0], points[0][1]);\n            for (var i = 1, l = points.length; i < l; i++) {\n                ctx.lineTo(points[i][0], points[i][1]);\n            }\n        }\n        closePath && ctx.closePath();\n    }\n}\n\nvar PolygonShape = (function () {\n    function PolygonShape() {\n        this.points = null;\n        this.smooth = 0;\n        this.smoothConstraint = null;\n    }\n    return PolygonShape;\n}());\nvar Polygon = (function (_super) {\n    __extends(Polygon, _super);\n    function Polygon(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Polygon.prototype.getDefaultShape = function () {\n        return new PolygonShape();\n    };\n    Polygon.prototype.buildPath = function (ctx, shape) {\n        buildPath$2(ctx, shape, true);\n    };\n    return Polygon;\n}(Path));\nPolygon.prototype.type = 'polygon';\n\nvar PolylineShape = (function () {\n    function PolylineShape() {\n        this.points = null;\n        this.percent = 1;\n        this.smooth = 0;\n        this.smoothConstraint = null;\n    }\n    return PolylineShape;\n}());\nvar Polyline = (function (_super) {\n    __extends(Polyline, _super);\n    function Polyline(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Polyline.prototype.getDefaultStyle = function () {\n        return {\n            stroke: '#000',\n            fill: null\n        };\n    };\n    Polyline.prototype.getDefaultShape = function () {\n        return new PolylineShape();\n    };\n    Polyline.prototype.buildPath = function (ctx, shape) {\n        buildPath$2(ctx, shape, false);\n    };\n    return Polyline;\n}(Path));\nPolyline.prototype.type = 'polyline';\n\nvar subPixelOptimizeOutputShape$1 = {};\nvar LineShape = (function () {\n    function LineShape() {\n        this.x1 = 0;\n        this.y1 = 0;\n        this.x2 = 0;\n        this.y2 = 0;\n        this.percent = 1;\n    }\n    return LineShape;\n}());\nvar Line = (function (_super) {\n    __extends(Line, _super);\n    function Line(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Line.prototype.getDefaultStyle = function () {\n        return {\n            stroke: '#000',\n            fill: null\n        };\n    };\n    Line.prototype.getDefaultShape = function () {\n        return new LineShape();\n    };\n    Line.prototype.buildPath = function (ctx, shape) {\n        var x1;\n        var y1;\n        var x2;\n        var y2;\n        if (this.subPixelOptimize) {\n            var optimizedShape = subPixelOptimizeLine(subPixelOptimizeOutputShape$1, shape, this.style);\n            x1 = optimizedShape.x1;\n            y1 = optimizedShape.y1;\n            x2 = optimizedShape.x2;\n            y2 = optimizedShape.y2;\n        }\n        else {\n            x1 = shape.x1;\n            y1 = shape.y1;\n            x2 = shape.x2;\n            y2 = shape.y2;\n        }\n        var percent = shape.percent;\n        if (percent === 0) {\n            return;\n        }\n        ctx.moveTo(x1, y1);\n        if (percent < 1) {\n            x2 = x1 * (1 - percent) + x2 * percent;\n            y2 = y1 * (1 - percent) + y2 * percent;\n        }\n        ctx.lineTo(x2, y2);\n    };\n    Line.prototype.pointAt = function (p) {\n        var shape = this.shape;\n        return [\n            shape.x1 * (1 - p) + shape.x2 * p,\n            shape.y1 * (1 - p) + shape.y2 * p\n        ];\n    };\n    return Line;\n}(Path));\nLine.prototype.type = 'line';\n\nvar out = [];\nvar BezierCurveShape = (function () {\n    function BezierCurveShape() {\n        this.x1 = 0;\n        this.y1 = 0;\n        this.x2 = 0;\n        this.y2 = 0;\n        this.cpx1 = 0;\n        this.cpy1 = 0;\n        this.percent = 1;\n    }\n    return BezierCurveShape;\n}());\nfunction someVectorAt(shape, t, isTangent) {\n    var cpx2 = shape.cpx2;\n    var cpy2 = shape.cpy2;\n    if (cpx2 != null || cpy2 != null) {\n        return [\n            (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),\n            (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)\n        ];\n    }\n    else {\n        return [\n            (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),\n            (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)\n        ];\n    }\n}\nvar BezierCurve = (function (_super) {\n    __extends(BezierCurve, _super);\n    function BezierCurve(opts) {\n        return _super.call(this, opts) || this;\n    }\n    BezierCurve.prototype.getDefaultStyle = function () {\n        return {\n            stroke: '#000',\n            fill: null\n        };\n    };\n    BezierCurve.prototype.getDefaultShape = function () {\n        return new BezierCurveShape();\n    };\n    BezierCurve.prototype.buildPath = function (ctx, shape) {\n        var x1 = shape.x1;\n        var y1 = shape.y1;\n        var x2 = shape.x2;\n        var y2 = shape.y2;\n        var cpx1 = shape.cpx1;\n        var cpy1 = shape.cpy1;\n        var cpx2 = shape.cpx2;\n        var cpy2 = shape.cpy2;\n        var percent = shape.percent;\n        if (percent === 0) {\n            return;\n        }\n        ctx.moveTo(x1, y1);\n        if (cpx2 == null || cpy2 == null) {\n            if (percent < 1) {\n                quadraticSubdivide(x1, cpx1, x2, percent, out);\n                cpx1 = out[1];\n                x2 = out[2];\n                quadraticSubdivide(y1, cpy1, y2, percent, out);\n                cpy1 = out[1];\n                y2 = out[2];\n            }\n            ctx.quadraticCurveTo(cpx1, cpy1, x2, y2);\n        }\n        else {\n            if (percent < 1) {\n                cubicSubdivide(x1, cpx1, cpx2, x2, percent, out);\n                cpx1 = out[1];\n                cpx2 = out[2];\n                x2 = out[3];\n                cubicSubdivide(y1, cpy1, cpy2, y2, percent, out);\n                cpy1 = out[1];\n                cpy2 = out[2];\n                y2 = out[3];\n            }\n            ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2);\n        }\n    };\n    BezierCurve.prototype.pointAt = function (t) {\n        return someVectorAt(this.shape, t, false);\n    };\n    BezierCurve.prototype.tangentAt = function (t) {\n        var p = someVectorAt(this.shape, t, true);\n        return normalize(p, p);\n    };\n    return BezierCurve;\n}(Path));\nBezierCurve.prototype.type = 'bezier-curve';\n\nvar ArcShape = (function () {\n    function ArcShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.r = 0;\n        this.startAngle = 0;\n        this.endAngle = Math.PI * 2;\n        this.clockwise = true;\n    }\n    return ArcShape;\n}());\nvar Arc = (function (_super) {\n    __extends(Arc, _super);\n    function Arc(opts) {\n        return _super.call(this, opts) || this;\n    }\n    Arc.prototype.getDefaultStyle = function () {\n        return {\n            stroke: '#000',\n            fill: null\n        };\n    };\n    Arc.prototype.getDefaultShape = function () {\n        return new ArcShape();\n    };\n    Arc.prototype.buildPath = function (ctx, shape) {\n        var x = shape.cx;\n        var y = shape.cy;\n        var r = Math.max(shape.r, 0);\n        var startAngle = shape.startAngle;\n        var endAngle = shape.endAngle;\n        var clockwise = shape.clockwise;\n        var unitX = Math.cos(startAngle);\n        var unitY = Math.sin(startAngle);\n        ctx.moveTo(unitX * r + x, unitY * r + y);\n        ctx.arc(x, y, r, startAngle, endAngle, !clockwise);\n    };\n    return Arc;\n}(Path));\nArc.prototype.type = 'arc';\n\nvar CompoundPath = (function (_super) {\n    __extends(CompoundPath, _super);\n    function CompoundPath() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n        _this.type = 'compound';\n        return _this;\n    }\n    CompoundPath.prototype._updatePathDirty = function () {\n        var paths = this.shape.paths;\n        var dirtyPath = this.shapeChanged();\n        for (var i = 0; i < paths.length; i++) {\n            dirtyPath = dirtyPath || paths[i].shapeChanged();\n        }\n        if (dirtyPath) {\n            this.dirtyShape();\n        }\n    };\n    CompoundPath.prototype.beforeBrush = function () {\n        this._updatePathDirty();\n        var paths = this.shape.paths || [];\n        var scale = this.getGlobalScale();\n        for (var i = 0; i < paths.length; i++) {\n            if (!paths[i].path) {\n                paths[i].createPathProxy();\n            }\n            paths[i].path.setScale(scale[0], scale[1], paths[i].segmentIgnoreThreshold);\n        }\n    };\n    CompoundPath.prototype.buildPath = function (ctx, shape) {\n        var paths = shape.paths || [];\n        for (var i = 0; i < paths.length; i++) {\n            paths[i].buildPath(ctx, paths[i].shape, true);\n        }\n    };\n    CompoundPath.prototype.afterBrush = function () {\n        var paths = this.shape.paths || [];\n        for (var i = 0; i < paths.length; i++) {\n            paths[i].pathUpdated();\n        }\n    };\n    CompoundPath.prototype.getBoundingRect = function () {\n        this._updatePathDirty.call(this);\n        return Path.prototype.getBoundingRect.call(this);\n    };\n    return CompoundPath;\n}(Path));\n\nvar Gradient = (function () {\n    function Gradient(colorStops) {\n        this.colorStops = colorStops || [];\n    }\n    Gradient.prototype.addColorStop = function (offset, color) {\n        this.colorStops.push({\n            offset: offset,\n            color: color\n        });\n    };\n    return Gradient;\n}());\n\nvar LinearGradient = (function (_super) {\n    __extends(LinearGradient, _super);\n    function LinearGradient(x, y, x2, y2, colorStops, globalCoord) {\n        var _this = _super.call(this, colorStops) || this;\n        _this.x = x == null ? 0 : x;\n        _this.y = y == null ? 0 : y;\n        _this.x2 = x2 == null ? 1 : x2;\n        _this.y2 = y2 == null ? 0 : y2;\n        _this.type = 'linear';\n        _this.global = globalCoord || false;\n        return _this;\n    }\n    return LinearGradient;\n}(Gradient));\n\nvar RadialGradient = (function (_super) {\n    __extends(RadialGradient, _super);\n    function RadialGradient(x, y, r, colorStops, globalCoord) {\n        var _this = _super.call(this, colorStops) || this;\n        _this.x = x == null ? 0.5 : x;\n        _this.y = y == null ? 0.5 : y;\n        _this.r = r == null ? 0.5 : r;\n        _this.type = 'radial';\n        _this.global = globalCoord || false;\n        return _this;\n    }\n    return RadialGradient;\n}(Gradient));\n\nvar extent = [0, 0];\nvar extent2 = [0, 0];\nvar minTv$1 = new Point();\nvar maxTv$1 = new Point();\nvar OrientedBoundingRect = (function () {\n    function OrientedBoundingRect(rect, transform) {\n        this._corners = [];\n        this._axes = [];\n        this._origin = [0, 0];\n        for (var i = 0; i < 4; i++) {\n            this._corners[i] = new Point();\n        }\n        for (var i = 0; i < 2; i++) {\n            this._axes[i] = new Point();\n        }\n        if (rect) {\n            this.fromBoundingRect(rect, transform);\n        }\n    }\n    OrientedBoundingRect.prototype.fromBoundingRect = function (rect, transform) {\n        var corners = this._corners;\n        var axes = this._axes;\n        var x = rect.x;\n        var y = rect.y;\n        var x2 = x + rect.width;\n        var y2 = y + rect.height;\n        corners[0].set(x, y);\n        corners[1].set(x2, y);\n        corners[2].set(x2, y2);\n        corners[3].set(x, y2);\n        if (transform) {\n            for (var i = 0; i < 4; i++) {\n                corners[i].transform(transform);\n            }\n        }\n        Point.sub(axes[0], corners[1], corners[0]);\n        Point.sub(axes[1], corners[3], corners[0]);\n        axes[0].normalize();\n        axes[1].normalize();\n        for (var i = 0; i < 2; i++) {\n            this._origin[i] = axes[i].dot(corners[0]);\n        }\n    };\n    OrientedBoundingRect.prototype.intersect = function (other, mtv) {\n        var overlapped = true;\n        var noMtv = !mtv;\n        minTv$1.set(Infinity, Infinity);\n        maxTv$1.set(0, 0);\n        if (!this._intersectCheckOneSide(this, other, minTv$1, maxTv$1, noMtv, 1)) {\n            overlapped = false;\n            if (noMtv) {\n                return overlapped;\n            }\n        }\n        if (!this._intersectCheckOneSide(other, this, minTv$1, maxTv$1, noMtv, -1)) {\n            overlapped = false;\n            if (noMtv) {\n                return overlapped;\n            }\n        }\n        if (!noMtv) {\n            Point.copy(mtv, overlapped ? minTv$1 : maxTv$1);\n        }\n        return overlapped;\n    };\n    OrientedBoundingRect.prototype._intersectCheckOneSide = function (self, other, minTv, maxTv, noMtv, inverse) {\n        var overlapped = true;\n        for (var i = 0; i < 2; i++) {\n            var axis = this._axes[i];\n            this._getProjMinMaxOnAxis(i, self._corners, extent);\n            this._getProjMinMaxOnAxis(i, other._corners, extent2);\n            if (extent[1] < extent2[0] || extent[0] > extent2[1]) {\n                overlapped = false;\n                if (noMtv) {\n                    return overlapped;\n                }\n                var dist0 = Math.abs(extent2[0] - extent[1]);\n                var dist1 = Math.abs(extent[0] - extent2[1]);\n                if (Math.min(dist0, dist1) > maxTv.len()) {\n                    if (dist0 < dist1) {\n                        Point.scale(maxTv, axis, -dist0 * inverse);\n                    }\n                    else {\n                        Point.scale(maxTv, axis, dist1 * inverse);\n                    }\n                }\n            }\n            else if (minTv) {\n                var dist0 = Math.abs(extent2[0] - extent[1]);\n                var dist1 = Math.abs(extent[0] - extent2[1]);\n                if (Math.min(dist0, dist1) < minTv.len()) {\n                    if (dist0 < dist1) {\n                        Point.scale(minTv, axis, dist0 * inverse);\n                    }\n                    else {\n                        Point.scale(minTv, axis, -dist1 * inverse);\n                    }\n                }\n            }\n        }\n        return overlapped;\n    };\n    OrientedBoundingRect.prototype._getProjMinMaxOnAxis = function (dim, corners, out) {\n        var axis = this._axes[dim];\n        var origin = this._origin;\n        var proj = corners[0].dot(axis) + origin[dim];\n        var min = proj;\n        var max = proj;\n        for (var i = 1; i < corners.length; i++) {\n            var proj_1 = corners[i].dot(axis) + origin[dim];\n            min = Math.min(proj_1, min);\n            max = Math.max(proj_1, max);\n        }\n        out[0] = min;\n        out[1] = max;\n    };\n    return OrientedBoundingRect;\n}());\n\nvar m = [];\nvar IncrementalDisplayable = (function (_super) {\n    __extends(IncrementalDisplayable, _super);\n    function IncrementalDisplayable() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n        _this.notClear = true;\n        _this.incremental = true;\n        _this._displayables = [];\n        _this._temporaryDisplayables = [];\n        _this._cursor = 0;\n        return _this;\n    }\n    IncrementalDisplayable.prototype.traverse = function (cb, context) {\n        cb.call(context, this);\n    };\n    IncrementalDisplayable.prototype.useStyle = function () {\n        this.style = {};\n    };\n    IncrementalDisplayable.prototype.getCursor = function () {\n        return this._cursor;\n    };\n    IncrementalDisplayable.prototype.innerAfterBrush = function () {\n        this._cursor = this._displayables.length;\n    };\n    IncrementalDisplayable.prototype.clearDisplaybles = function () {\n        this._displayables = [];\n        this._temporaryDisplayables = [];\n        this._cursor = 0;\n        this.markRedraw();\n        this.notClear = false;\n    };\n    IncrementalDisplayable.prototype.clearTemporalDisplayables = function () {\n        this._temporaryDisplayables = [];\n    };\n    IncrementalDisplayable.prototype.addDisplayable = function (displayable, notPersistent) {\n        if (notPersistent) {\n            this._temporaryDisplayables.push(displayable);\n        }\n        else {\n            this._displayables.push(displayable);\n        }\n        this.markRedraw();\n    };\n    IncrementalDisplayable.prototype.addDisplayables = function (displayables, notPersistent) {\n        notPersistent = notPersistent || false;\n        for (var i = 0; i < displayables.length; i++) {\n            this.addDisplayable(displayables[i], notPersistent);\n        }\n    };\n    IncrementalDisplayable.prototype.getDisplayables = function () {\n        return this._displayables;\n    };\n    IncrementalDisplayable.prototype.getTemporalDisplayables = function () {\n        return this._temporaryDisplayables;\n    };\n    IncrementalDisplayable.prototype.eachPendingDisplayable = function (cb) {\n        for (var i = this._cursor; i < this._displayables.length; i++) {\n            cb && cb(this._displayables[i]);\n        }\n        for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n            cb && cb(this._temporaryDisplayables[i]);\n        }\n    };\n    IncrementalDisplayable.prototype.update = function () {\n        this.updateTransform();\n        for (var i = this._cursor; i < this._displayables.length; i++) {\n            var displayable = this._displayables[i];\n            displayable.parent = this;\n            displayable.update();\n            displayable.parent = null;\n        }\n        for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n            var displayable = this._temporaryDisplayables[i];\n            displayable.parent = this;\n            displayable.update();\n            displayable.parent = null;\n        }\n    };\n    IncrementalDisplayable.prototype.getBoundingRect = function () {\n        if (!this._rect) {\n            var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);\n            for (var i = 0; i < this._displayables.length; i++) {\n                var displayable = this._displayables[i];\n                var childRect = displayable.getBoundingRect().clone();\n                if (displayable.needLocalTransform()) {\n                    childRect.applyTransform(displayable.getLocalTransform(m));\n                }\n                rect.union(childRect);\n            }\n            this._rect = rect;\n        }\n        return this._rect;\n    };\n    IncrementalDisplayable.prototype.contain = function (x, y) {\n        var localPos = this.transformCoordToLocal(x, y);\n        var rect = this.getBoundingRect();\n        if (rect.contain(localPos[0], localPos[1])) {\n            for (var i = 0; i < this._displayables.length; i++) {\n                var displayable = this._displayables[i];\n                if (displayable.contain(x, y)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    };\n    return IncrementalDisplayable;\n}(Displayable));\n\nvar transitionStore = makeInner();\n/**\n * Return null if animation is disabled.\n */\n\nfunction getAnimationConfig(animationType, animatableModel, dataIndex, // Extra opts can override the option in animatable model.\nextraOpts, // TODO It's only for pictorial bar now.\nextraDelayParams) {\n  var animationPayload; // Check if there is global animation configuration from dataZoom/resize can override the config in option.\n  // If animation is enabled. Will use this animation config in payload.\n  // If animation is disabled. Just ignore it.\n\n  if (animatableModel && animatableModel.ecModel) {\n    var updatePayload = animatableModel.ecModel.getUpdatePayload();\n    animationPayload = updatePayload && updatePayload.animation;\n  }\n\n  var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();\n  var isUpdate = animationType === 'update';\n\n  if (animationEnabled) {\n    var duration = void 0;\n    var easing = void 0;\n    var delay = void 0;\n\n    if (extraOpts) {\n      duration = retrieve2(extraOpts.duration, 200);\n      easing = retrieve2(extraOpts.easing, 'cubicOut');\n      delay = 0;\n    } else {\n      duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration');\n      easing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing');\n      delay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay');\n    } // animation from payload has highest priority.\n\n\n    if (animationPayload) {\n      animationPayload.duration != null && (duration = animationPayload.duration);\n      animationPayload.easing != null && (easing = animationPayload.easing);\n      animationPayload.delay != null && (delay = animationPayload.delay);\n    }\n\n    if (isFunction(delay)) {\n      delay = delay(dataIndex, extraDelayParams);\n    }\n\n    if (isFunction(duration)) {\n      duration = duration(dataIndex);\n    }\n\n    var config = {\n      duration: duration || 0,\n      delay: delay,\n      easing: easing\n    };\n    return config;\n  } else {\n    return null;\n  }\n}\n\nfunction animateOrSetProps(animationType, el, props, animatableModel, dataIndex, cb, during) {\n  var isFrom = false;\n  var removeOpt;\n\n  if (isFunction(dataIndex)) {\n    during = cb;\n    cb = dataIndex;\n    dataIndex = null;\n  } else if (isObject(dataIndex)) {\n    cb = dataIndex.cb;\n    during = dataIndex.during;\n    isFrom = dataIndex.isFrom;\n    removeOpt = dataIndex.removeOpt;\n    dataIndex = dataIndex.dataIndex;\n  }\n\n  var isRemove = animationType === 'leave';\n\n  if (!isRemove) {\n    // Must stop the remove animation.\n    el.stopAnimation('leave');\n  }\n\n  var animationConfig = getAnimationConfig(animationType, animatableModel, dataIndex, isRemove ? removeOpt || {} : null, animatableModel && animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);\n\n  if (animationConfig && animationConfig.duration > 0) {\n    var duration = animationConfig.duration;\n    var animationDelay = animationConfig.delay;\n    var animationEasing = animationConfig.easing;\n    var animateConfig = {\n      duration: duration,\n      delay: animationDelay || 0,\n      easing: animationEasing,\n      done: cb,\n      force: !!cb || !!during,\n      // Set to final state in update/init animation.\n      // So the post processing based on the path shape can be done correctly.\n      setToFinal: !isRemove,\n      scope: animationType,\n      during: during\n    };\n    isFrom ? el.animateFrom(props, animateConfig) : el.animateTo(props, animateConfig);\n  } else {\n    el.stopAnimation(); // If `isFrom`, the props is the \"from\" props.\n\n    !isFrom && el.attr(props); // Call during at least once.\n\n    during && during(1);\n    cb && cb();\n  }\n}\n/**\n * Update graphic element properties with or without animation according to the\n * configuration in series.\n *\n * Caution: this method will stop previous animation.\n * So do not use this method to one element twice before\n * animation starts, unless you know what you are doing.\n * @example\n *     graphic.updateProps(el, {\n *         position: [100, 100]\n *     }, seriesModel, dataIndex, function () { console.log('Animation done!'); });\n *     // Or\n *     graphic.updateProps(el, {\n *         position: [100, 100]\n *     }, seriesModel, function () { console.log('Animation done!'); });\n */\n\n\nfunction updateProps(el, props, // TODO: TYPE AnimatableModel\nanimatableModel, dataIndex, cb, during) {\n  animateOrSetProps('update', el, props, animatableModel, dataIndex, cb, during);\n}\n/**\n * Init graphic element properties with or without animation according to the\n * configuration in series.\n *\n * Caution: this method will stop previous animation.\n * So do not use this method to one element twice before\n * animation starts, unless you know what you are doing.\n */\n\nfunction initProps(el, props, animatableModel, dataIndex, cb, during) {\n  animateOrSetProps('enter', el, props, animatableModel, dataIndex, cb, during);\n}\n/**\n * If element is removed.\n * It can determine if element is having remove animation.\n */\n\nfunction isElementRemoved(el) {\n  if (!el.__zr) {\n    return true;\n  }\n\n  for (var i = 0; i < el.animators.length; i++) {\n    var animator = el.animators[i];\n\n    if (animator.scope === 'leave') {\n      return true;\n    }\n  }\n\n  return false;\n}\n/**\n * Remove graphic element\n */\n\nfunction removeElement(el, props, animatableModel, dataIndex, cb, during) {\n  // Don't do remove animation twice.\n  if (isElementRemoved(el)) {\n    return;\n  }\n\n  animateOrSetProps('leave', el, props, animatableModel, dataIndex, cb, during);\n}\n\nfunction fadeOutDisplayable(el, animatableModel, dataIndex, done) {\n  el.removeTextContent();\n  el.removeTextGuideLine();\n  removeElement(el, {\n    style: {\n      opacity: 0\n    }\n  }, animatableModel, dataIndex, done);\n}\n\nfunction removeElementWithFadeOut(el, animatableModel, dataIndex) {\n  function doRemove() {\n    el.parent && el.parent.remove(el);\n  } // Hide label and labelLine first\n  // TODO Also use fade out animation?\n\n\n  if (!el.isGroup) {\n    fadeOutDisplayable(el, animatableModel, dataIndex, doRemove);\n  } else {\n    el.traverse(function (disp) {\n      if (!disp.isGroup) {\n        // Can invoke doRemove multiple times.\n        fadeOutDisplayable(disp, animatableModel, dataIndex, doRemove);\n      }\n    });\n  }\n}\n/**\n * Save old style for style transition in universalTransition module.\n * It's used when element will be reused in each render.\n * For chart like map, heatmap, which will always create new element.\n * We don't need to save this because universalTransition can get old style from the old element\n */\n\nfunction saveOldStyle(el) {\n  transitionStore(el).oldStyle = el.style;\n}\nfunction getOldStyle(el) {\n  return transitionStore(el).oldStyle;\n}\n\nvar mathMax$4 = Math.max;\nvar mathMin$4 = Math.min;\nvar _customShapeMap = {};\n/**\n * Extend shape with parameters\n */\n\nfunction extendShape(opts) {\n  return Path.extend(opts);\n}\nvar extendPathFromString = extendFromString;\n/**\n * Extend path\n */\n\nfunction extendPath(pathData, opts) {\n  return extendPathFromString(pathData, opts);\n}\n/**\n * Register a user defined shape.\n * The shape class can be fetched by `getShapeClass`\n * This method will overwrite the registered shapes, including\n * the registered built-in shapes, if using the same `name`.\n * The shape can be used in `custom series` and\n * `graphic component` by declaring `{type: name}`.\n *\n * @param name\n * @param ShapeClass Can be generated by `extendShape`.\n */\n\nfunction registerShape(name, ShapeClass) {\n  _customShapeMap[name] = ShapeClass;\n}\n/**\n * Find shape class registered by `registerShape`. Usually used in\n * fetching user defined shape.\n *\n * [Caution]:\n * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared\n * to use user registered shapes.\n * Because the built-in shape (see `getBuiltInShape`) will be registered by\n * `registerShape` by default. That enables users to get both built-in\n * shapes as well as the shapes belonging to themsleves. But users can overwrite\n * the built-in shapes by using names like 'circle', 'rect' via calling\n * `registerShape`. So the echarts inner featrues should not fetch shapes from here\n * in case that it is overwritten by users, except that some features, like\n * `custom series`, `graphic component`, do it deliberately.\n *\n * (2) In the features like `custom series`, `graphic component`, the user input\n * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic\n * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names\n * are reserved names, that is, if some user registers a shape named `'image'`,\n * the shape will not be used. If we intending to add some more reserved names\n * in feature, that might bring break changes (disable some existing user shape\n * names). But that case probably rarely happens. So we don't make more mechanism\n * to resolve this issue here.\n *\n * @param name\n * @return The shape class. If not found, return nothing.\n */\n\nfunction getShapeClass(name) {\n  if (_customShapeMap.hasOwnProperty(name)) {\n    return _customShapeMap[name];\n  }\n}\n/**\n * Create a path element from path data string\n * @param pathData\n * @param opts\n * @param rect\n * @param layout 'center' or 'cover' default to be cover\n */\n\nfunction makePath(pathData, opts, rect, layout) {\n  var path = createFromString(pathData, opts);\n\n  if (rect) {\n    if (layout === 'center') {\n      rect = centerGraphic(rect, path.getBoundingRect());\n    }\n\n    resizePath(path, rect);\n  }\n\n  return path;\n}\n/**\n * Create a image element from image url\n * @param imageUrl image url\n * @param opts options\n * @param rect constrain rect\n * @param layout 'center' or 'cover'. Default to be 'cover'\n */\n\nfunction makeImage(imageUrl, rect, layout) {\n  var zrImg = new ZRImage({\n    style: {\n      image: imageUrl,\n      x: rect.x,\n      y: rect.y,\n      width: rect.width,\n      height: rect.height\n    },\n    onload: function (img) {\n      if (layout === 'center') {\n        var boundingRect = {\n          width: img.width,\n          height: img.height\n        };\n        zrImg.setStyle(centerGraphic(rect, boundingRect));\n      }\n    }\n  });\n  return zrImg;\n}\n/**\n * Get position of centered element in bounding box.\n *\n * @param  rect         element local bounding box\n * @param  boundingRect constraint bounding box\n * @return element position containing x, y, width, and height\n */\n\nfunction centerGraphic(rect, boundingRect) {\n  // Set rect to center, keep width / height ratio.\n  var aspect = boundingRect.width / boundingRect.height;\n  var width = rect.height * aspect;\n  var height;\n\n  if (width <= rect.width) {\n    height = rect.height;\n  } else {\n    width = rect.width;\n    height = width / aspect;\n  }\n\n  var cx = rect.x + rect.width / 2;\n  var cy = rect.y + rect.height / 2;\n  return {\n    x: cx - width / 2,\n    y: cy - height / 2,\n    width: width,\n    height: height\n  };\n}\n\nvar mergePath$1 = mergePath;\n/**\n * Resize a path to fit the rect\n * @param path\n * @param rect\n */\n\nfunction resizePath(path, rect) {\n  if (!path.applyTransform) {\n    return;\n  }\n\n  var pathRect = path.getBoundingRect();\n  var m = pathRect.calculateTransform(rect);\n  path.applyTransform(m);\n}\n/**\n * Sub pixel optimize line for canvas\n */\n\nfunction subPixelOptimizeLine$1(shape, lineWidth) {\n  subPixelOptimizeLine(shape, shape, {\n    lineWidth: lineWidth\n  });\n  return shape;\n}\n/**\n * Sub pixel optimize rect for canvas\n */\n\nfunction subPixelOptimizeRect$1(param) {\n  subPixelOptimizeRect(param.shape, param.shape, param.style);\n  return param;\n}\n/**\n * Sub pixel optimize for canvas\n *\n * @param position Coordinate, such as x, y\n * @param lineWidth Should be nonnegative integer.\n * @param positiveOrNegative Default false (negative).\n * @return Optimized position.\n */\n\nvar subPixelOptimize$1 = subPixelOptimize;\n/**\n * Get transform matrix of target (param target),\n * in coordinate of its ancestor (param ancestor)\n *\n * @param target\n * @param [ancestor]\n */\n\nfunction getTransform(target, ancestor) {\n  var mat = identity([]);\n\n  while (target && target !== ancestor) {\n    mul$1(mat, target.getLocalTransform(), mat);\n    target = target.parent;\n  }\n\n  return mat;\n}\n/**\n * Apply transform to an vertex.\n * @param target [x, y]\n * @param transform Can be:\n *      + Transform matrix: like [1, 0, 0, 1, 0, 0]\n *      + {position, rotation, scale}, the same as `zrender/Transformable`.\n * @param invert Whether use invert matrix.\n * @return [x, y]\n */\n\nfunction applyTransform$1(target, transform, invert$1) {\n  if (transform && !isArrayLike(transform)) {\n    transform = Transformable.getLocalTransform(transform);\n  }\n\n  if (invert$1) {\n    transform = invert([], transform);\n  }\n\n  return applyTransform([], target, transform);\n}\n/**\n * @param direction 'left' 'right' 'top' 'bottom'\n * @param transform Transform matrix: like [1, 0, 0, 1, 0, 0]\n * @param invert Whether use invert matrix.\n * @return Transformed direction. 'left' 'right' 'top' 'bottom'\n */\n\nfunction transformDirection(direction, transform, invert) {\n  // Pick a base, ensure that transform result will not be (0, 0).\n  var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]);\n  var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]);\n  var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0];\n  vertex = applyTransform$1(vertex, transform, invert);\n  return Math.abs(vertex[0]) > Math.abs(vertex[1]) ? vertex[0] > 0 ? 'right' : 'left' : vertex[1] > 0 ? 'bottom' : 'top';\n}\n\nfunction isNotGroup(el) {\n  return !el.isGroup;\n}\n\nfunction isPath(el) {\n  return el.shape != null;\n}\n/**\n * Apply group transition animation from g1 to g2.\n * If no animatableModel, no animation.\n */\n\n\nfunction groupTransition(g1, g2, animatableModel) {\n  if (!g1 || !g2) {\n    return;\n  }\n\n  function getElMap(g) {\n    var elMap = {};\n    g.traverse(function (el) {\n      if (isNotGroup(el) && el.anid) {\n        elMap[el.anid] = el;\n      }\n    });\n    return elMap;\n  }\n\n  function getAnimatableProps(el) {\n    var obj = {\n      x: el.x,\n      y: el.y,\n      rotation: el.rotation\n    };\n\n    if (isPath(el)) {\n      obj.shape = extend({}, el.shape);\n    }\n\n    return obj;\n  }\n\n  var elMap1 = getElMap(g1);\n  g2.traverse(function (el) {\n    if (isNotGroup(el) && el.anid) {\n      var oldEl = elMap1[el.anid];\n\n      if (oldEl) {\n        var newProp = getAnimatableProps(el);\n        el.attr(getAnimatableProps(oldEl));\n        updateProps(el, newProp, animatableModel, getECData(el).dataIndex);\n      }\n    }\n  });\n}\nfunction clipPointsByRect(points, rect) {\n  // FIXME: This way might be incorrect when graphic clipped by a corner\n  // and when element has a border.\n  return map(points, function (point) {\n    var x = point[0];\n    x = mathMax$4(x, rect.x);\n    x = mathMin$4(x, rect.x + rect.width);\n    var y = point[1];\n    y = mathMax$4(y, rect.y);\n    y = mathMin$4(y, rect.y + rect.height);\n    return [x, y];\n  });\n}\n/**\n * Return a new clipped rect. If rect size are negative, return undefined.\n */\n\nfunction clipRectByRect(targetRect, rect) {\n  var x = mathMax$4(targetRect.x, rect.x);\n  var x2 = mathMin$4(targetRect.x + targetRect.width, rect.x + rect.width);\n  var y = mathMax$4(targetRect.y, rect.y);\n  var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border,\n  // should be painted. So return undefined.\n\n  if (x2 >= x && y2 >= y) {\n    return {\n      x: x,\n      y: y,\n      width: x2 - x,\n      height: y2 - y\n    };\n  }\n}\nfunction createIcon(iconStr, // Support 'image://' or 'path://' or direct svg path.\nopt, rect) {\n  var innerOpts = extend({\n    rectHover: true\n  }, opt);\n  var style = innerOpts.style = {\n    strokeNoScale: true\n  };\n  rect = rect || {\n    x: -1,\n    y: -1,\n    width: 2,\n    height: 2\n  };\n\n  if (iconStr) {\n    return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), defaults(style, rect), new ZRImage(innerOpts)) : makePath(iconStr.replace('path://', ''), innerOpts, rect, 'center');\n  }\n}\n/**\n * Return `true` if the given line (line `a`) and the given polygon\n * are intersect.\n * Note that we do not count colinear as intersect here because no\n * requirement for that. We could do that if required in future.\n */\n\nfunction linePolygonIntersect(a1x, a1y, a2x, a2y, points) {\n  for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) {\n    var p = points[i];\n\n    if (lineLineIntersect(a1x, a1y, a2x, a2y, p[0], p[1], p2[0], p2[1])) {\n      return true;\n    }\n\n    p2 = p;\n  }\n}\n/**\n * Return `true` if the given two lines (line `a` and line `b`)\n * are intersect.\n * Note that we do not count colinear as intersect here because no\n * requirement for that. We could do that if required in future.\n */\n\nfunction lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {\n  // let `vec_m` to be `vec_a2 - vec_a1` and `vec_n` to be `vec_b2 - vec_b1`.\n  var mx = a2x - a1x;\n  var my = a2y - a1y;\n  var nx = b2x - b1x;\n  var ny = b2y - b1y; // `vec_m` and `vec_n` are parallel iff\n  //     existing `k` such that `vec_m = k · vec_n`, equivalent to `vec_m X vec_n = 0`.\n\n  var nmCrossProduct = crossProduct2d(nx, ny, mx, my);\n\n  if (nearZero(nmCrossProduct)) {\n    return false;\n  } // `vec_m` and `vec_n` are intersect iff\n  //     existing `p` and `q` in [0, 1] such that `vec_a1 + p * vec_m = vec_b1 + q * vec_n`,\n  //     such that `q = ((vec_a1 - vec_b1) X vec_m) / (vec_n X vec_m)`\n  //           and `p = ((vec_a1 - vec_b1) X vec_n) / (vec_n X vec_m)`.\n\n\n  var b1a1x = a1x - b1x;\n  var b1a1y = a1y - b1y;\n  var q = crossProduct2d(b1a1x, b1a1y, mx, my) / nmCrossProduct;\n\n  if (q < 0 || q > 1) {\n    return false;\n  }\n\n  var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct;\n\n  if (p < 0 || p > 1) {\n    return false;\n  }\n\n  return true;\n}\n/**\n * Cross product of 2-dimension vector.\n */\n\nfunction crossProduct2d(x1, y1, x2, y2) {\n  return x1 * y2 - x2 * y1;\n}\n\nfunction nearZero(val) {\n  return val <= 1e-6 && val >= -1e-6;\n}\n\nfunction setTooltipConfig(opt) {\n  var itemTooltipOption = opt.itemTooltipOption;\n  var componentModel = opt.componentModel;\n  var itemName = opt.itemName;\n  var itemTooltipOptionObj = isString(itemTooltipOption) ? {\n    formatter: itemTooltipOption\n  } : itemTooltipOption;\n  var mainType = componentModel.mainType;\n  var componentIndex = componentModel.componentIndex;\n  var formatterParams = {\n    componentType: mainType,\n    name: itemName,\n    $vars: ['name']\n  };\n  formatterParams[mainType + 'Index'] = componentIndex;\n  var formatterParamsExtra = opt.formatterParamsExtra;\n\n  if (formatterParamsExtra) {\n    each(keys(formatterParamsExtra), function (key) {\n      if (!hasOwn(formatterParams, key)) {\n        formatterParams[key] = formatterParamsExtra[key];\n        formatterParams.$vars.push(key);\n      }\n    });\n  }\n\n  var ecData = getECData(opt.el);\n  ecData.componentMainType = mainType;\n  ecData.componentIndex = componentIndex;\n  ecData.tooltipConfig = {\n    name: itemName,\n    option: defaults({\n      content: itemName,\n      formatterParams: formatterParams\n    }, itemTooltipOptionObj)\n  };\n}\n\nfunction traverseElement(el, cb) {\n  var stopped; // TODO\n  // Polyfill for fixing zrender group traverse don't visit it's root issue.\n\n  if (el.isGroup) {\n    stopped = cb(el);\n  }\n\n  if (!stopped) {\n    el.traverse(cb);\n  }\n}\n\nfunction traverseElements(els, cb) {\n  if (els) {\n    if (isArray(els)) {\n      for (var i = 0; i < els.length; i++) {\n        traverseElement(els[i], cb);\n      }\n    } else {\n      traverseElement(els, cb);\n    }\n  }\n} // Register built-in shapes. These shapes might be overwritten\n// by users, although we do not recommend that.\n\nregisterShape('circle', Circle);\nregisterShape('ellipse', Ellipse);\nregisterShape('sector', Sector);\nregisterShape('ring', Ring);\nregisterShape('polygon', Polygon);\nregisterShape('polyline', Polyline);\nregisterShape('rect', Rect);\nregisterShape('line', Line);\nregisterShape('bezierCurve', BezierCurve);\nregisterShape('arc', Arc);\n\nvar graphic = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    updateProps: updateProps,\n    initProps: initProps,\n    removeElement: removeElement,\n    removeElementWithFadeOut: removeElementWithFadeOut,\n    isElementRemoved: isElementRemoved,\n    extendShape: extendShape,\n    extendPath: extendPath,\n    registerShape: registerShape,\n    getShapeClass: getShapeClass,\n    makePath: makePath,\n    makeImage: makeImage,\n    mergePath: mergePath$1,\n    resizePath: resizePath,\n    subPixelOptimizeLine: subPixelOptimizeLine$1,\n    subPixelOptimizeRect: subPixelOptimizeRect$1,\n    subPixelOptimize: subPixelOptimize$1,\n    getTransform: getTransform,\n    applyTransform: applyTransform$1,\n    transformDirection: transformDirection,\n    groupTransition: groupTransition,\n    clipPointsByRect: clipPointsByRect,\n    clipRectByRect: clipRectByRect,\n    createIcon: createIcon,\n    linePolygonIntersect: linePolygonIntersect,\n    lineLineIntersect: lineLineIntersect,\n    setTooltipConfig: setTooltipConfig,\n    traverseElements: traverseElements,\n    Group: Group,\n    Image: ZRImage,\n    Text: ZRText,\n    Circle: Circle,\n    Ellipse: Ellipse,\n    Sector: Sector,\n    Ring: Ring,\n    Polygon: Polygon,\n    Polyline: Polyline,\n    Rect: Rect,\n    Line: Line,\n    BezierCurve: BezierCurve,\n    Arc: Arc,\n    IncrementalDisplayable: IncrementalDisplayable,\n    CompoundPath: CompoundPath,\n    LinearGradient: LinearGradient,\n    RadialGradient: RadialGradient,\n    BoundingRect: BoundingRect,\n    OrientedBoundingRect: OrientedBoundingRect,\n    Point: Point,\n    Path: Path\n});\n\nvar EMPTY_OBJ = {};\nfunction setLabelText(label, labelTexts) {\n  for (var i = 0; i < SPECIAL_STATES.length; i++) {\n    var stateName = SPECIAL_STATES[i];\n    var text = labelTexts[stateName];\n    var state = label.ensureState(stateName);\n    state.style = state.style || {};\n    state.style.text = text;\n  }\n\n  var oldStates = label.currentStates.slice();\n  label.clearStates(true);\n  label.setStyle({\n    text: labelTexts.normal\n  });\n  label.useStates(oldStates, true);\n}\n\nfunction getLabelText(opt, stateModels, interpolatedValue) {\n  var labelFetcher = opt.labelFetcher;\n  var labelDataIndex = opt.labelDataIndex;\n  var labelDimIndex = opt.labelDimIndex;\n  var normalModel = stateModels.normal;\n  var baseText;\n\n  if (labelFetcher) {\n    baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, normalModel && normalModel.get('formatter'), interpolatedValue != null ? {\n      interpolatedValue: interpolatedValue\n    } : null);\n  }\n\n  if (baseText == null) {\n    baseText = isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt, interpolatedValue) : opt.defaultText;\n  }\n\n  var statesText = {\n    normal: baseText\n  };\n\n  for (var i = 0; i < SPECIAL_STATES.length; i++) {\n    var stateName = SPECIAL_STATES[i];\n    var stateModel = stateModels[stateName];\n    statesText[stateName] = retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, stateName, null, labelDimIndex, stateModel && stateModel.get('formatter')) : null, baseText);\n  }\n\n  return statesText;\n}\n\nfunction setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified // TODO specified position?\n) {\n  opt = opt || EMPTY_OBJ;\n  var isSetOnText = targetEl instanceof ZRText;\n  var needsCreateText = false;\n\n  for (var i = 0; i < DISPLAY_STATES.length; i++) {\n    var stateModel = labelStatesModels[DISPLAY_STATES[i]];\n\n    if (stateModel && stateModel.getShallow('show')) {\n      needsCreateText = true;\n      break;\n    }\n  }\n\n  var textContent = isSetOnText ? targetEl : targetEl.getTextContent();\n\n  if (needsCreateText) {\n    if (!isSetOnText) {\n      // Reuse the previous\n      if (!textContent) {\n        textContent = new ZRText();\n        targetEl.setTextContent(textContent);\n      } // Use same state proxy\n\n\n      if (targetEl.stateProxy) {\n        textContent.stateProxy = targetEl.stateProxy;\n      }\n    }\n\n    var labelStatesTexts = getLabelText(opt, labelStatesModels);\n    var normalModel = labelStatesModels.normal;\n    var showNormal = !!normalModel.getShallow('show');\n    var normalStyle = createTextStyle(normalModel, stateSpecified && stateSpecified.normal, opt, false, !isSetOnText);\n    normalStyle.text = labelStatesTexts.normal;\n\n    if (!isSetOnText) {\n      // Always create new\n      targetEl.setTextConfig(createTextConfig(normalModel, opt, false));\n    }\n\n    for (var i = 0; i < SPECIAL_STATES.length; i++) {\n      var stateName = SPECIAL_STATES[i];\n      var stateModel = labelStatesModels[stateName];\n\n      if (stateModel) {\n        var stateObj = textContent.ensureState(stateName);\n        var stateShow = !!retrieve2(stateModel.getShallow('show'), showNormal);\n\n        if (stateShow !== showNormal) {\n          stateObj.ignore = !stateShow;\n        }\n\n        stateObj.style = createTextStyle(stateModel, stateSpecified && stateSpecified[stateName], opt, true, !isSetOnText);\n        stateObj.style.text = labelStatesTexts[stateName];\n\n        if (!isSetOnText) {\n          var targetElEmphasisState = targetEl.ensureState(stateName);\n          targetElEmphasisState.textConfig = createTextConfig(stateModel, opt, true);\n        }\n      }\n    } // PENDING: if there is many requirements that emphasis position\n    // need to be different from normal position, we might consider\n    // auto silent is those cases.\n\n\n    textContent.silent = !!normalModel.getShallow('silent'); // Keep x and y\n\n    if (textContent.style.x != null) {\n      normalStyle.x = textContent.style.x;\n    }\n\n    if (textContent.style.y != null) {\n      normalStyle.y = textContent.style.y;\n    }\n\n    textContent.ignore = !showNormal; // Always create new style.\n\n    textContent.useStyle(normalStyle);\n    textContent.dirty();\n\n    if (opt.enableTextSetter) {\n      labelInner(textContent).setLabelText = function (interpolatedValue) {\n        var labelStatesTexts = getLabelText(opt, labelStatesModels, interpolatedValue);\n        setLabelText(textContent, labelStatesTexts);\n      };\n    }\n  } else if (textContent) {\n    // Not display rich text.\n    textContent.ignore = true;\n  }\n\n  targetEl.dirty();\n}\nfunction getLabelStatesModels(itemModel, labelName) {\n  labelName = labelName || 'label';\n  var statesModels = {\n    normal: itemModel.getModel(labelName)\n  };\n\n  for (var i = 0; i < SPECIAL_STATES.length; i++) {\n    var stateName = SPECIAL_STATES[i];\n    statesModels[stateName] = itemModel.getModel([stateName, labelName]);\n  }\n\n  return statesModels;\n}\n/**\n * Set basic textStyle properties.\n */\n\nfunction createTextStyle(textStyleModel, specifiedTextStyle, // Fixed style in the code. Can't be set by model.\nopt, isNotNormal, isAttached // If text is attached on an element. If so, auto color will handling in zrender.\n) {\n  var textStyle = {};\n  setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached);\n  specifiedTextStyle && extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\n\n  return textStyle;\n}\nfunction createTextConfig(textStyleModel, opt, isNotNormal) {\n  opt = opt || {};\n  var textConfig = {};\n  var labelPosition;\n  var labelRotate = textStyleModel.getShallow('rotate');\n  var labelDistance = retrieve2(textStyleModel.getShallow('distance'), isNotNormal ? null : 5);\n  var labelOffset = textStyleModel.getShallow('offset');\n  labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used\n  // in bar series, and magric type should be considered.\n\n  labelPosition === 'outside' && (labelPosition = opt.defaultOutsidePosition || 'top');\n\n  if (labelPosition != null) {\n    textConfig.position = labelPosition;\n  }\n\n  if (labelOffset != null) {\n    textConfig.offset = labelOffset;\n  }\n\n  if (labelRotate != null) {\n    labelRotate *= Math.PI / 180;\n    textConfig.rotation = labelRotate;\n  }\n\n  if (labelDistance != null) {\n    textConfig.distance = labelDistance;\n  } // fill and auto is determined by the color of path fill if it's not specified by developers.\n\n\n  textConfig.outsideFill = textStyleModel.get('color') === 'inherit' ? opt.inheritColor || null : 'auto';\n  return textConfig;\n}\n/**\n * The uniform entry of set text style, that is, retrieve style definitions\n * from `model` and set to `textStyle` object.\n *\n * Never in merge mode, but in overwrite mode, that is, all of the text style\n * properties will be set. (Consider the states of normal and emphasis and\n * default value can be adopted, merge would make the logic too complicated\n * to manage.)\n */\n\nfunction setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached) {\n  // Consider there will be abnormal when merge hover style to normal style if given default value.\n  opt = opt || EMPTY_OBJ;\n  var ecModel = textStyleModel.ecModel;\n  var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:\n  // {\n  //     data: [{\n  //         value: 12,\n  //         label: {\n  //             rich: {\n  //                 // no 'a' here but using parent 'a'.\n  //             }\n  //         }\n  //     }],\n  //     rich: {\n  //         a: { ... }\n  //     }\n  // }\n\n  var richItemNames = getRichItemNames(textStyleModel);\n  var richResult;\n\n  if (richItemNames) {\n    richResult = {};\n\n    for (var name_1 in richItemNames) {\n      if (richItemNames.hasOwnProperty(name_1)) {\n        // Cascade is supported in rich.\n        var richTextStyle = textStyleModel.getModel(['rich', name_1]); // In rich, never `disableBox`.\n        // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,\n        // the default color `'blue'` will not be adopted if no color declared in `rich`.\n        // That might confuses users. So probably we should put `textStyleModel` as the\n        // root ancestor of the `richTextStyle`. But that would be a break change.\n\n        setTokenTextStyle(richResult[name_1] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true);\n      }\n    }\n  }\n\n  if (richResult) {\n    textStyle.rich = richResult;\n  }\n\n  var overflow = textStyleModel.get('overflow');\n\n  if (overflow) {\n    textStyle.overflow = overflow;\n  }\n\n  var margin = textStyleModel.get('minMargin');\n\n  if (margin != null) {\n    textStyle.margin = margin;\n  }\n\n  setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false);\n} // Consider case:\n// {\n//     data: [{\n//         value: 12,\n//         label: {\n//             rich: {\n//                 // no 'a' here but using parent 'a'.\n//             }\n//         }\n//     }],\n//     rich: {\n//         a: { ... }\n//     }\n// }\n// TODO TextStyleModel\n\n\nfunction getRichItemNames(textStyleModel) {\n  // Use object to remove duplicated names.\n  var richItemNameMap;\n\n  while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {\n    var rich = (textStyleModel.option || EMPTY_OBJ).rich;\n\n    if (rich) {\n      richItemNameMap = richItemNameMap || {};\n      var richKeys = keys(rich);\n\n      for (var i = 0; i < richKeys.length; i++) {\n        var richKey = richKeys[i];\n        richItemNameMap[richKey] = 1;\n      }\n    }\n\n    textStyleModel = textStyleModel.parentModel;\n  }\n\n  return richItemNameMap;\n}\n\nvar TEXT_PROPS_WITH_GLOBAL = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'];\nvar TEXT_PROPS_SELF = ['align', 'lineHeight', 'width', 'height', 'tag', 'verticalAlign'];\nvar TEXT_PROPS_BOX = ['padding', 'borderWidth', 'borderRadius', 'borderDashOffset', 'backgroundColor', 'borderColor', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\n\nfunction setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, isBlock, inRich) {\n  // In merge mode, default value should not be given.\n  globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ;\n  var inheritColor = opt && opt.inheritColor;\n  var fillColor = textStyleModel.getShallow('color');\n  var strokeColor = textStyleModel.getShallow('textBorderColor');\n  var opacity = retrieve2(textStyleModel.getShallow('opacity'), globalTextStyle.opacity);\n\n  if (fillColor === 'inherit' || fillColor === 'auto') {\n    if (\"development\" !== 'production') {\n      if (fillColor === 'auto') {\n        deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n      }\n    }\n\n    if (inheritColor) {\n      fillColor = inheritColor;\n    } else {\n      fillColor = null;\n    }\n  }\n\n  if (strokeColor === 'inherit' || strokeColor === 'auto') {\n    if (\"development\" !== 'production') {\n      if (strokeColor === 'auto') {\n        deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n      }\n    }\n\n    if (inheritColor) {\n      strokeColor = inheritColor;\n    } else {\n      strokeColor = null;\n    }\n  }\n\n  if (!isAttached) {\n    // Only use default global textStyle.color if text is individual.\n    // Otherwise it will use the strategy of attached text color because text may be on a path.\n    fillColor = fillColor || globalTextStyle.color;\n    strokeColor = strokeColor || globalTextStyle.textBorderColor;\n  }\n\n  if (fillColor != null) {\n    textStyle.fill = fillColor;\n  }\n\n  if (strokeColor != null) {\n    textStyle.stroke = strokeColor;\n  }\n\n  var textBorderWidth = retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth);\n\n  if (textBorderWidth != null) {\n    textStyle.lineWidth = textBorderWidth;\n  }\n\n  var textBorderType = retrieve2(textStyleModel.getShallow('textBorderType'), globalTextStyle.textBorderType);\n\n  if (textBorderType != null) {\n    textStyle.lineDash = textBorderType;\n  }\n\n  var textBorderDashOffset = retrieve2(textStyleModel.getShallow('textBorderDashOffset'), globalTextStyle.textBorderDashOffset);\n\n  if (textBorderDashOffset != null) {\n    textStyle.lineDashOffset = textBorderDashOffset;\n  }\n\n  if (!isNotNormal && opacity == null && !inRich) {\n    opacity = opt && opt.defaultOpacity;\n  }\n\n  if (opacity != null) {\n    textStyle.opacity = opacity;\n  } // TODO\n\n\n  if (!isNotNormal && !isAttached) {\n    // Set default finally.\n    if (textStyle.fill == null && opt.inheritColor) {\n      textStyle.fill = opt.inheritColor;\n    }\n  } // Do not use `getFont` here, because merge should be supported, where\n  // part of these properties may be changed in emphasis style, and the\n  // others should remain their original value got from normal style.\n\n\n  for (var i = 0; i < TEXT_PROPS_WITH_GLOBAL.length; i++) {\n    var key = TEXT_PROPS_WITH_GLOBAL[i];\n    var val = retrieve2(textStyleModel.getShallow(key), globalTextStyle[key]);\n\n    if (val != null) {\n      textStyle[key] = val;\n    }\n  }\n\n  for (var i = 0; i < TEXT_PROPS_SELF.length; i++) {\n    var key = TEXT_PROPS_SELF[i];\n    var val = textStyleModel.getShallow(key);\n\n    if (val != null) {\n      textStyle[key] = val;\n    }\n  }\n\n  if (textStyle.verticalAlign == null) {\n    var baseline = textStyleModel.getShallow('baseline');\n\n    if (baseline != null) {\n      textStyle.verticalAlign = baseline;\n    }\n  }\n\n  if (!isBlock || !opt.disableBox) {\n    for (var i = 0; i < TEXT_PROPS_BOX.length; i++) {\n      var key = TEXT_PROPS_BOX[i];\n      var val = textStyleModel.getShallow(key);\n\n      if (val != null) {\n        textStyle[key] = val;\n      }\n    }\n\n    var borderType = textStyleModel.getShallow('borderType');\n\n    if (borderType != null) {\n      textStyle.borderDash = borderType;\n    }\n\n    if ((textStyle.backgroundColor === 'auto' || textStyle.backgroundColor === 'inherit') && inheritColor) {\n      if (\"development\" !== 'production') {\n        if (textStyle.backgroundColor === 'auto') {\n          deprecateReplaceLog('backgroundColor: \\'auto\\'', 'backgroundColor: \\'inherit\\'');\n        }\n      }\n\n      textStyle.backgroundColor = inheritColor;\n    }\n\n    if ((textStyle.borderColor === 'auto' || textStyle.borderColor === 'inherit') && inheritColor) {\n      if (\"development\" !== 'production') {\n        if (textStyle.borderColor === 'auto') {\n          deprecateReplaceLog('borderColor: \\'auto\\'', 'borderColor: \\'inherit\\'');\n        }\n      }\n\n      textStyle.borderColor = inheritColor;\n    }\n  }\n}\n\nfunction getFont(opt, ecModel) {\n  var gTextStyleModel = ecModel && ecModel.getModel('textStyle');\n  return trim([// FIXME in node-canvas fontWeight is before fontStyle\n  opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));\n}\nvar labelInner = makeInner();\nfunction setLabelValueAnimation(label, labelStatesModels, value, getDefaultText) {\n  if (!label) {\n    return;\n  }\n\n  var obj = labelInner(label);\n  obj.prevValue = obj.value;\n  obj.value = value;\n  var normalLabelModel = labelStatesModels.normal;\n  obj.valueAnimation = normalLabelModel.get('valueAnimation');\n\n  if (obj.valueAnimation) {\n    obj.precision = normalLabelModel.get('precision');\n    obj.defaultInterpolatedText = getDefaultText;\n    obj.statesModels = labelStatesModels;\n  }\n}\nfunction animateLabelValue(textEl, dataIndex, data, animatableModel, labelFetcher) {\n  var labelInnerStore = labelInner(textEl);\n\n  if (!labelInnerStore.valueAnimation || labelInnerStore.prevValue === labelInnerStore.value) {\n    // Value not changed, no new label animation\n    return;\n  }\n\n  var defaultInterpolatedText = labelInnerStore.defaultInterpolatedText; // Consider the case that being animating, do not use the `obj.value`,\n  // Otherwise it will jump to the `obj.value` when this new animation started.\n\n  var currValue = retrieve2(labelInnerStore.interpolatedValue, labelInnerStore.prevValue);\n  var targetValue = labelInnerStore.value;\n\n  function during(percent) {\n    var interpolated = interpolateRawValues(data, labelInnerStore.precision, currValue, targetValue, percent);\n    labelInnerStore.interpolatedValue = percent === 1 ? null : interpolated;\n    var labelText = getLabelText({\n      labelDataIndex: dataIndex,\n      labelFetcher: labelFetcher,\n      defaultText: defaultInterpolatedText ? defaultInterpolatedText(interpolated) : interpolated + ''\n    }, labelInnerStore.statesModels, interpolated);\n    setLabelText(textEl, labelText);\n  }\n\n  textEl.percent = 0;\n  (labelInnerStore.prevValue == null ? initProps : updateProps)(textEl, {\n    // percent is used to prevent animation from being aborted #15916\n    percent: 1\n  }, animatableModel, dataIndex, null, during);\n}\n\nvar PATH_COLOR = ['textStyle', 'color'];\nvar textStyleParams = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'padding', 'lineHeight', 'rich', 'width', 'height', 'overflow']; // TODO Performance improvement?\n\nvar tmpText = new ZRText();\n\nvar TextStyleMixin =\n/** @class */\nfunction () {\n  function TextStyleMixin() {}\n  /**\n   * Get color property or get color from option.textStyle.color\n   */\n  // TODO Callback\n\n\n  TextStyleMixin.prototype.getTextColor = function (isEmphasis) {\n    var ecModel = this.ecModel;\n    return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null);\n  };\n  /**\n   * Create font string from fontStyle, fontWeight, fontSize, fontFamily\n   * @return {string}\n   */\n\n\n  TextStyleMixin.prototype.getFont = function () {\n    return getFont({\n      fontStyle: this.getShallow('fontStyle'),\n      fontWeight: this.getShallow('fontWeight'),\n      fontSize: this.getShallow('fontSize'),\n      fontFamily: this.getShallow('fontFamily')\n    }, this.ecModel);\n  };\n\n  TextStyleMixin.prototype.getTextRect = function (text) {\n    var style = {\n      text: text,\n      verticalAlign: this.getShallow('verticalAlign') || this.getShallow('baseline')\n    };\n\n    for (var i = 0; i < textStyleParams.length; i++) {\n      style[textStyleParams[i]] = this.getShallow(textStyleParams[i]);\n    }\n\n    tmpText.useStyle(style);\n    tmpText.update();\n    return tmpText.getBoundingRect();\n  };\n\n  return TextStyleMixin;\n}();\n\nvar LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n// So do not transfer decal directly.\n];\nvar getLineStyle = makeStyleMapper(LINE_STYLE_KEY_MAP);\n\nvar LineStyleMixin =\n/** @class */\nfunction () {\n  function LineStyleMixin() {}\n\n  LineStyleMixin.prototype.getLineStyle = function (excludes) {\n    return getLineStyle(this, excludes);\n  };\n\n  return LineStyleMixin;\n}();\n\nvar ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n// So do not transfer decal directly.\n];\nvar getItemStyle = makeStyleMapper(ITEM_STYLE_KEY_MAP);\n\nvar ItemStyleMixin =\n/** @class */\nfunction () {\n  function ItemStyleMixin() {}\n\n  ItemStyleMixin.prototype.getItemStyle = function (excludes, includes) {\n    return getItemStyle(this, excludes, includes);\n  };\n\n  return ItemStyleMixin;\n}();\n\nvar Model =\n/** @class */\nfunction () {\n  function Model(option, parentModel, ecModel) {\n    this.parentModel = parentModel;\n    this.ecModel = ecModel;\n    this.option = option; // Simple optimization\n    // if (this.init) {\n    //     if (arguments.length <= 4) {\n    //         this.init(option, parentModel, ecModel, extraOpt);\n    //     }\n    //     else {\n    //         this.init.apply(this, arguments);\n    //     }\n    // }\n  }\n\n  Model.prototype.init = function (option, parentModel, ecModel) {\n    var rest = [];\n\n    for (var _i = 3; _i < arguments.length; _i++) {\n      rest[_i - 3] = arguments[_i];\n    }\n  };\n  /**\n   * Merge the input option to me.\n   */\n\n\n  Model.prototype.mergeOption = function (option, ecModel) {\n    merge(this.option, option, true);\n  }; // `path` can be 'a.b.c', so the return value type have to be `ModelOption`\n  // TODO: TYPE strict key check?\n  // get(path: string | string[], ignoreParent?: boolean): ModelOption;\n\n\n  Model.prototype.get = function (path, ignoreParent) {\n    if (path == null) {\n      return this.option;\n    }\n\n    return this._doGet(this.parsePath(path), !ignoreParent && this.parentModel);\n  };\n\n  Model.prototype.getShallow = function (key, ignoreParent) {\n    var option = this.option;\n    var val = option == null ? option : option[key];\n\n    if (val == null && !ignoreParent) {\n      var parentModel = this.parentModel;\n\n      if (parentModel) {\n        // FIXME:TS do not know how to make it works\n        val = parentModel.getShallow(key);\n      }\n    }\n\n    return val;\n  }; // `path` can be 'a.b.c', so the return value type have to be `Model<ModelOption>`\n  // getModel(path: string | string[], parentModel?: Model): Model;\n  // TODO 'a.b.c' is deprecated\n\n\n  Model.prototype.getModel = function (path, parentModel) {\n    var hasPath = path != null;\n    var pathFinal = hasPath ? this.parsePath(path) : null;\n    var obj = hasPath ? this._doGet(pathFinal) : this.option;\n    parentModel = parentModel || this.parentModel && this.parentModel.getModel(this.resolveParentPath(pathFinal));\n    return new Model(obj, parentModel, this.ecModel);\n  };\n  /**\n   * If model has option\n   */\n\n\n  Model.prototype.isEmpty = function () {\n    return this.option == null;\n  };\n\n  Model.prototype.restoreData = function () {}; // Pending\n\n\n  Model.prototype.clone = function () {\n    var Ctor = this.constructor;\n    return new Ctor(clone(this.option));\n  }; // setReadOnly(properties): void {\n  // clazzUtil.setReadOnly(this, properties);\n  // }\n  // If path is null/undefined, return null/undefined.\n\n\n  Model.prototype.parsePath = function (path) {\n    if (typeof path === 'string') {\n      return path.split('.');\n    }\n\n    return path;\n  }; // Resolve path for parent. Perhaps useful when parent use a different property.\n  // Default to be a identity resolver.\n  // Can be modified to a different resolver.\n\n\n  Model.prototype.resolveParentPath = function (path) {\n    return path;\n  }; // FIXME:TS check whether put this method here\n\n\n  Model.prototype.isAnimationEnabled = function () {\n    if (!env.node && this.option) {\n      if (this.option.animation != null) {\n        return !!this.option.animation;\n      } else if (this.parentModel) {\n        return this.parentModel.isAnimationEnabled();\n      }\n    }\n  };\n\n  Model.prototype._doGet = function (pathArr, parentModel) {\n    var obj = this.option;\n\n    if (!pathArr) {\n      return obj;\n    }\n\n    for (var i = 0; i < pathArr.length; i++) {\n      // Ignore empty\n      if (!pathArr[i]) {\n        continue;\n      } // obj could be number/string/... (like 0)\n\n\n      obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null;\n\n      if (obj == null) {\n        break;\n      }\n    }\n\n    if (obj == null && parentModel) {\n      obj = parentModel._doGet(this.resolveParentPath(pathArr), parentModel.parentModel);\n    }\n\n    return obj;\n  };\n\n  return Model;\n}();\n\nenableClassExtend(Model);\nenableClassCheck(Model);\nmixin(Model, LineStyleMixin);\nmixin(Model, ItemStyleMixin);\nmixin(Model, AreaStyleMixin);\nmixin(Model, TextStyleMixin);\n\nvar base = Math.round(Math.random() * 10);\n/**\n * @public\n * @param {string} type\n * @return {string}\n */\n\nfunction getUID(type) {\n  // Considering the case of crossing js context,\n  // use Math.random to make id as unique as possible.\n  return [type || '', base++].join('_');\n}\n/**\n * Implements `SubTypeDefaulterManager` for `target`.\n */\n\nfunction enableSubTypeDefaulter(target) {\n  var subTypeDefaulters = {};\n\n  target.registerSubTypeDefaulter = function (componentType, defaulter) {\n    var componentTypeInfo = parseClassType(componentType);\n    subTypeDefaulters[componentTypeInfo.main] = defaulter;\n  };\n\n  target.determineSubType = function (componentType, option) {\n    var type = option.type;\n\n    if (!type) {\n      var componentTypeMain = parseClassType(componentType).main;\n\n      if (target.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {\n        type = subTypeDefaulters[componentTypeMain](option);\n      }\n    }\n\n    return type;\n  };\n}\n/**\n * Implements `TopologicalTravelable<any>` for `entity`.\n *\n * Topological travel on Activity Network (Activity On Vertices).\n * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].\n * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.\n * If there is circular dependencey, Error will be thrown.\n */\n\nfunction enableTopologicalTravel(entity, dependencyGetter) {\n  /**\n   * @param targetNameList Target Component type list.\n   *                       Can be ['aa', 'bb', 'aa.xx']\n   * @param fullNameList By which we can build dependency graph.\n   * @param callback Params: componentType, dependencies.\n   * @param context Scope of callback.\n   */\n  entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {\n    if (!targetNameList.length) {\n      return;\n    }\n\n    var result = makeDepndencyGraph(fullNameList);\n    var graph = result.graph;\n    var noEntryList = result.noEntryList;\n    var targetNameSet = {};\n    each(targetNameList, function (name) {\n      targetNameSet[name] = true;\n    });\n\n    while (noEntryList.length) {\n      var currComponentType = noEntryList.pop();\n      var currVertex = graph[currComponentType];\n      var isInTargetNameSet = !!targetNameSet[currComponentType];\n\n      if (isInTargetNameSet) {\n        callback.call(context, currComponentType, currVertex.originalDeps.slice());\n        delete targetNameSet[currComponentType];\n      }\n\n      each(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge);\n    }\n\n    each(targetNameSet, function () {\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        errMsg = makePrintable('Circular dependency may exists: ', targetNameSet, targetNameList, fullNameList);\n      }\n\n      throw new Error(errMsg);\n    });\n\n    function removeEdge(succComponentType) {\n      graph[succComponentType].entryCount--;\n\n      if (graph[succComponentType].entryCount === 0) {\n        noEntryList.push(succComponentType);\n      }\n    } // Consider this case: legend depends on series, and we call\n    // chart.setOption({series: [...]}), where only series is in option.\n    // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will\n    // not be called, but only sereis.mergeOption is called. Thus legend\n    // have no chance to update its local record about series (like which\n    // name of series is available in legend).\n\n\n    function removeEdgeAndAdd(succComponentType) {\n      targetNameSet[succComponentType] = true;\n      removeEdge(succComponentType);\n    }\n  };\n\n  function makeDepndencyGraph(fullNameList) {\n    var graph = {};\n    var noEntryList = [];\n    each(fullNameList, function (name) {\n      var thisItem = createDependencyGraphItem(graph, name);\n      var originalDeps = thisItem.originalDeps = dependencyGetter(name);\n      var availableDeps = getAvailableDependencies(originalDeps, fullNameList);\n      thisItem.entryCount = availableDeps.length;\n\n      if (thisItem.entryCount === 0) {\n        noEntryList.push(name);\n      }\n\n      each(availableDeps, function (dependentName) {\n        if (indexOf(thisItem.predecessor, dependentName) < 0) {\n          thisItem.predecessor.push(dependentName);\n        }\n\n        var thatItem = createDependencyGraphItem(graph, dependentName);\n\n        if (indexOf(thatItem.successor, dependentName) < 0) {\n          thatItem.successor.push(name);\n        }\n      });\n    });\n    return {\n      graph: graph,\n      noEntryList: noEntryList\n    };\n  }\n\n  function createDependencyGraphItem(graph, name) {\n    if (!graph[name]) {\n      graph[name] = {\n        predecessor: [],\n        successor: []\n      };\n    }\n\n    return graph[name];\n  }\n\n  function getAvailableDependencies(originalDeps, fullNameList) {\n    var availableDeps = [];\n    each(originalDeps, function (dep) {\n      indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);\n    });\n    return availableDeps;\n  }\n}\nfunction inheritDefaultOption(superOption, subOption) {\n  // See also `model/Component.ts#getDefaultOption`\n  return merge(merge({}, superOption, true), subOption, true);\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n/**\n * Language: English.\n */\nvar langEN = {\n  time: {\n    month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n    monthAbbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n    dayOfWeek: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],\n    dayOfWeekAbbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']\n  },\n  legend: {\n    selector: {\n      all: 'All',\n      inverse: 'Inv'\n    }\n  },\n  toolbox: {\n    brush: {\n      title: {\n        rect: 'Box Select',\n        polygon: 'Lasso Select',\n        lineX: 'Horizontally Select',\n        lineY: 'Vertically Select',\n        keep: 'Keep Selections',\n        clear: 'Clear Selections'\n      }\n    },\n    dataView: {\n      title: 'Data View',\n      lang: ['Data View', 'Close', 'Refresh']\n    },\n    dataZoom: {\n      title: {\n        zoom: 'Zoom',\n        back: 'Zoom Reset'\n      }\n    },\n    magicType: {\n      title: {\n        line: 'Switch to Line Chart',\n        bar: 'Switch to Bar Chart',\n        stack: 'Stack',\n        tiled: 'Tile'\n      }\n    },\n    restore: {\n      title: 'Restore'\n    },\n    saveAsImage: {\n      title: 'Save as Image',\n      lang: ['Right Click to Save Image']\n    }\n  },\n  series: {\n    typeNames: {\n      pie: 'Pie chart',\n      bar: 'Bar chart',\n      line: 'Line chart',\n      scatter: 'Scatter plot',\n      effectScatter: 'Ripple scatter plot',\n      radar: 'Radar chart',\n      tree: 'Tree',\n      treemap: 'Treemap',\n      boxplot: 'Boxplot',\n      candlestick: 'Candlestick',\n      k: 'K line chart',\n      heatmap: 'Heat map',\n      map: 'Map',\n      parallel: 'Parallel coordinate map',\n      lines: 'Line graph',\n      graph: 'Relationship graph',\n      sankey: 'Sankey diagram',\n      funnel: 'Funnel chart',\n      gauge: 'Gauge',\n      pictorialBar: 'Pictorial bar',\n      themeRiver: 'Theme River Map',\n      sunburst: 'Sunburst'\n    }\n  },\n  aria: {\n    general: {\n      withTitle: 'This is a chart about \"{title}\"',\n      withoutTitle: 'This is a chart'\n    },\n    series: {\n      single: {\n        prefix: '',\n        withName: ' with type {seriesType} named {seriesName}.',\n        withoutName: ' with type {seriesType}.'\n      },\n      multiple: {\n        prefix: '. It consists of {seriesCount} series count.',\n        withName: ' The {seriesId} series is a {seriesType} representing {seriesName}.',\n        withoutName: ' The {seriesId} series is a {seriesType}.',\n        separator: {\n          middle: '',\n          end: ''\n        }\n      }\n    },\n    data: {\n      allData: 'The data is as follows: ',\n      partialData: 'The first {displayCnt} items are: ',\n      withName: 'the data for {name} is {value}',\n      withoutName: '{value}',\n      separator: {\n        middle: ', ',\n        end: '. '\n      }\n    }\n  }\n};\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\nvar langZH = {\n  time: {\n    month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],\n    monthAbbr: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],\n    dayOfWeek: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],\n    dayOfWeekAbbr: ['日', '一', '二', '三', '四', '五', '六']\n  },\n  legend: {\n    selector: {\n      all: '全选',\n      inverse: '反选'\n    }\n  },\n  toolbox: {\n    brush: {\n      title: {\n        rect: '矩形选择',\n        polygon: '圈选',\n        lineX: '横向选择',\n        lineY: '纵向选择',\n        keep: '保持选择',\n        clear: '清除选择'\n      }\n    },\n    dataView: {\n      title: '数据视图',\n      lang: ['数据视图', '关闭', '刷新']\n    },\n    dataZoom: {\n      title: {\n        zoom: '区域缩放',\n        back: '区域缩放还原'\n      }\n    },\n    magicType: {\n      title: {\n        line: '切换为折线图',\n        bar: '切换为柱状图',\n        stack: '切换为堆叠',\n        tiled: '切换为平铺'\n      }\n    },\n    restore: {\n      title: '还原'\n    },\n    saveAsImage: {\n      title: '保存为图片',\n      lang: ['右键另存为图片']\n    }\n  },\n  series: {\n    typeNames: {\n      pie: '饼图',\n      bar: '柱状图',\n      line: '折线图',\n      scatter: '散点图',\n      effectScatter: '涟漪散点图',\n      radar: '雷达图',\n      tree: '树图',\n      treemap: '矩形树图',\n      boxplot: '箱型图',\n      candlestick: 'K线图',\n      k: 'K线图',\n      heatmap: '热力图',\n      map: '地图',\n      parallel: '平行坐标图',\n      lines: '线图',\n      graph: '关系图',\n      sankey: '桑基图',\n      funnel: '漏斗图',\n      gauge: '仪表盘图',\n      pictorialBar: '象形柱图',\n      themeRiver: '主题河流图',\n      sunburst: '旭日图'\n    }\n  },\n  aria: {\n    general: {\n      withTitle: '这是一个关于“{title}”的图表。',\n      withoutTitle: '这是一个图表，'\n    },\n    series: {\n      single: {\n        prefix: '',\n        withName: '图表类型是{seriesType}，表示{seriesName}。',\n        withoutName: '图表类型是{seriesType}。'\n      },\n      multiple: {\n        prefix: '它由{seriesCount}个图表系列组成。',\n        withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType}，',\n        withoutName: '第{seriesId}个系列是一个{seriesType}，',\n        separator: {\n          middle: '；',\n          end: '。'\n        }\n      }\n    },\n    data: {\n      allData: '其数据是——',\n      partialData: '其中，前{displayCnt}项是——',\n      withName: '{name}的数据是{value}',\n      withoutName: '{value}',\n      separator: {\n        middle: '，',\n        end: ''\n      }\n    }\n  }\n};\n\nvar LOCALE_ZH = 'ZH';\nvar LOCALE_EN = 'EN';\nvar DEFAULT_LOCALE = LOCALE_EN;\nvar localeStorage = {};\nvar localeModels = {};\nvar SYSTEM_LANG = !env.domSupported ? DEFAULT_LOCALE : function () {\n  var langStr = (\n  /* eslint-disable-next-line */\n  document.documentElement.lang || navigator.language || navigator.browserLanguage).toUpperCase();\n  return langStr.indexOf(LOCALE_ZH) > -1 ? LOCALE_ZH : DEFAULT_LOCALE;\n}();\nfunction registerLocale(locale, localeObj) {\n  locale = locale.toUpperCase();\n  localeModels[locale] = new Model(localeObj);\n  localeStorage[locale] = localeObj;\n} // export function getLocale(locale: string) {\n//     return localeStorage[locale];\n// }\n\nfunction createLocaleObject(locale) {\n  if (isString(locale)) {\n    var localeObj = localeStorage[locale.toUpperCase()] || {};\n\n    if (locale === LOCALE_ZH || locale === LOCALE_EN) {\n      return clone(localeObj);\n    } else {\n      return merge(clone(localeObj), clone(localeStorage[DEFAULT_LOCALE]), false);\n    }\n  } else {\n    return merge(clone(locale), clone(localeStorage[DEFAULT_LOCALE]), false);\n  }\n}\nfunction getLocaleModel(lang) {\n  return localeModels[lang];\n}\nfunction getDefaultLocaleModel() {\n  return localeModels[DEFAULT_LOCALE];\n} // Default locale\n\nregisterLocale(LOCALE_EN, langEN);\nregisterLocale(LOCALE_ZH, langZH);\n\nvar ONE_SECOND = 1000;\nvar ONE_MINUTE = ONE_SECOND * 60;\nvar ONE_HOUR = ONE_MINUTE * 60;\nvar ONE_DAY = ONE_HOUR * 24;\nvar ONE_YEAR = ONE_DAY * 365;\nvar defaultLeveledFormatter = {\n  year: '{yyyy}',\n  month: '{MMM}',\n  day: '{d}',\n  hour: '{HH}:{mm}',\n  minute: '{HH}:{mm}',\n  second: '{HH}:{mm}:{ss}',\n  millisecond: '{HH}:{mm}:{ss} {SSS}',\n  none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}'\n};\nvar fullDayFormatter = '{yyyy}-{MM}-{dd}';\nvar fullLeveledFormatter = {\n  year: '{yyyy}',\n  month: '{yyyy}-{MM}',\n  day: fullDayFormatter,\n  hour: fullDayFormatter + ' ' + defaultLeveledFormatter.hour,\n  minute: fullDayFormatter + ' ' + defaultLeveledFormatter.minute,\n  second: fullDayFormatter + ' ' + defaultLeveledFormatter.second,\n  millisecond: defaultLeveledFormatter.none\n};\nvar primaryTimeUnits = ['year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond'];\nvar timeUnits = ['year', 'half-year', 'quarter', 'month', 'week', 'half-week', 'day', 'half-day', 'quarter-day', 'hour', 'minute', 'second', 'millisecond'];\nfunction pad(str, len) {\n  str += '';\n  return '0000'.substr(0, len - str.length) + str;\n}\nfunction getPrimaryTimeUnit(timeUnit) {\n  switch (timeUnit) {\n    case 'half-year':\n    case 'quarter':\n      return 'month';\n\n    case 'week':\n    case 'half-week':\n      return 'day';\n\n    case 'half-day':\n    case 'quarter-day':\n      return 'hour';\n\n    default:\n      // year, minutes, second, milliseconds\n      return timeUnit;\n  }\n}\nfunction isPrimaryTimeUnit(timeUnit) {\n  return timeUnit === getPrimaryTimeUnit(timeUnit);\n}\nfunction getDefaultFormatPrecisionOfInterval(timeUnit) {\n  switch (timeUnit) {\n    case 'year':\n    case 'month':\n      return 'day';\n\n    case 'millisecond':\n      return 'millisecond';\n\n    default:\n      // Also for day, hour, minute, second\n      return 'second';\n  }\n}\nfunction format( // Note: The result based on `isUTC` are totally different, which can not be just simply\n// substituted by the result without `isUTC`. So we make the param `isUTC` mandatory.\ntime, template, isUTC, lang) {\n  var date = parseDate(time);\n  var y = date[fullYearGetterName(isUTC)]();\n  var M = date[monthGetterName(isUTC)]() + 1;\n  var q = Math.floor((M - 1) / 3) + 1;\n  var d = date[dateGetterName(isUTC)]();\n  var e = date['get' + (isUTC ? 'UTC' : '') + 'Day']();\n  var H = date[hoursGetterName(isUTC)]();\n  var h = (H - 1) % 12 + 1;\n  var m = date[minutesGetterName(isUTC)]();\n  var s = date[secondsGetterName(isUTC)]();\n  var S = date[millisecondsGetterName(isUTC)]();\n  var localeModel = lang instanceof Model ? lang : getLocaleModel(lang || SYSTEM_LANG) || getDefaultLocaleModel();\n  var timeModel = localeModel.getModel('time');\n  var month = timeModel.get('month');\n  var monthAbbr = timeModel.get('monthAbbr');\n  var dayOfWeek = timeModel.get('dayOfWeek');\n  var dayOfWeekAbbr = timeModel.get('dayOfWeekAbbr');\n  return (template || '').replace(/{yyyy}/g, y + '').replace(/{yy}/g, y % 100 + '').replace(/{Q}/g, q + '').replace(/{MMMM}/g, month[M - 1]).replace(/{MMM}/g, monthAbbr[M - 1]).replace(/{MM}/g, pad(M, 2)).replace(/{M}/g, M + '').replace(/{dd}/g, pad(d, 2)).replace(/{d}/g, d + '').replace(/{eeee}/g, dayOfWeek[e]).replace(/{ee}/g, dayOfWeekAbbr[e]).replace(/{e}/g, e + '').replace(/{HH}/g, pad(H, 2)).replace(/{H}/g, H + '').replace(/{hh}/g, pad(h + '', 2)).replace(/{h}/g, h + '').replace(/{mm}/g, pad(m, 2)).replace(/{m}/g, m + '').replace(/{ss}/g, pad(s, 2)).replace(/{s}/g, s + '').replace(/{SSS}/g, pad(S, 3)).replace(/{S}/g, S + '');\n}\nfunction leveledFormat(tick, idx, formatter, lang, isUTC) {\n  var template = null;\n\n  if (isString(formatter)) {\n    // Single formatter for all units at all levels\n    template = formatter;\n  } else if (isFunction(formatter)) {\n    // Callback formatter\n    template = formatter(tick.value, idx, {\n      level: tick.level\n    });\n  } else {\n    var defaults$1 = extend({}, defaultLeveledFormatter);\n\n    if (tick.level > 0) {\n      for (var i = 0; i < primaryTimeUnits.length; ++i) {\n        defaults$1[primaryTimeUnits[i]] = \"{primary|\" + defaults$1[primaryTimeUnits[i]] + \"}\";\n      }\n    }\n\n    var mergedFormatter = formatter ? formatter.inherit === false ? formatter // Use formatter with bigger units\n    : defaults(formatter, defaults$1) : defaults$1;\n    var unit = getUnitFromValue(tick.value, isUTC);\n\n    if (mergedFormatter[unit]) {\n      template = mergedFormatter[unit];\n    } else if (mergedFormatter.inherit) {\n      // Unit formatter is not defined and should inherit from bigger units\n      var targetId = timeUnits.indexOf(unit);\n\n      for (var i = targetId - 1; i >= 0; --i) {\n        if (mergedFormatter[unit]) {\n          template = mergedFormatter[unit];\n          break;\n        }\n      }\n\n      template = template || defaults$1.none;\n    }\n\n    if (isArray(template)) {\n      var levelId = tick.level == null ? 0 : tick.level >= 0 ? tick.level : template.length + tick.level;\n      levelId = Math.min(levelId, template.length - 1);\n      template = template[levelId];\n    }\n  }\n\n  return format(new Date(tick.value), template, isUTC, lang);\n}\nfunction getUnitFromValue(value, isUTC) {\n  var date = parseDate(value);\n  var M = date[monthGetterName(isUTC)]() + 1;\n  var d = date[dateGetterName(isUTC)]();\n  var h = date[hoursGetterName(isUTC)]();\n  var m = date[minutesGetterName(isUTC)]();\n  var s = date[secondsGetterName(isUTC)]();\n  var S = date[millisecondsGetterName(isUTC)]();\n  var isSecond = S === 0;\n  var isMinute = isSecond && s === 0;\n  var isHour = isMinute && m === 0;\n  var isDay = isHour && h === 0;\n  var isMonth = isDay && d === 1;\n  var isYear = isMonth && M === 1;\n\n  if (isYear) {\n    return 'year';\n  } else if (isMonth) {\n    return 'month';\n  } else if (isDay) {\n    return 'day';\n  } else if (isHour) {\n    return 'hour';\n  } else if (isMinute) {\n    return 'minute';\n  } else if (isSecond) {\n    return 'second';\n  } else {\n    return 'millisecond';\n  }\n}\nfunction getUnitValue(value, unit, isUTC) {\n  var date = isNumber(value) ? parseDate(value) : value;\n  unit = unit || getUnitFromValue(value, isUTC);\n\n  switch (unit) {\n    case 'year':\n      return date[fullYearGetterName(isUTC)]();\n\n    case 'half-year':\n      return date[monthGetterName(isUTC)]() >= 6 ? 1 : 0;\n\n    case 'quarter':\n      return Math.floor((date[monthGetterName(isUTC)]() + 1) / 4);\n\n    case 'month':\n      return date[monthGetterName(isUTC)]();\n\n    case 'day':\n      return date[dateGetterName(isUTC)]();\n\n    case 'half-day':\n      return date[hoursGetterName(isUTC)]() / 24;\n\n    case 'hour':\n      return date[hoursGetterName(isUTC)]();\n\n    case 'minute':\n      return date[minutesGetterName(isUTC)]();\n\n    case 'second':\n      return date[secondsGetterName(isUTC)]();\n\n    case 'millisecond':\n      return date[millisecondsGetterName(isUTC)]();\n  }\n}\nfunction fullYearGetterName(isUTC) {\n  return isUTC ? 'getUTCFullYear' : 'getFullYear';\n}\nfunction monthGetterName(isUTC) {\n  return isUTC ? 'getUTCMonth' : 'getMonth';\n}\nfunction dateGetterName(isUTC) {\n  return isUTC ? 'getUTCDate' : 'getDate';\n}\nfunction hoursGetterName(isUTC) {\n  return isUTC ? 'getUTCHours' : 'getHours';\n}\nfunction minutesGetterName(isUTC) {\n  return isUTC ? 'getUTCMinutes' : 'getMinutes';\n}\nfunction secondsGetterName(isUTC) {\n  return isUTC ? 'getUTCSeconds' : 'getSeconds';\n}\nfunction millisecondsGetterName(isUTC) {\n  return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds';\n}\nfunction fullYearSetterName(isUTC) {\n  return isUTC ? 'setUTCFullYear' : 'setFullYear';\n}\nfunction monthSetterName(isUTC) {\n  return isUTC ? 'setUTCMonth' : 'setMonth';\n}\nfunction dateSetterName(isUTC) {\n  return isUTC ? 'setUTCDate' : 'setDate';\n}\nfunction hoursSetterName(isUTC) {\n  return isUTC ? 'setUTCHours' : 'setHours';\n}\nfunction minutesSetterName(isUTC) {\n  return isUTC ? 'setUTCMinutes' : 'setMinutes';\n}\nfunction secondsSetterName(isUTC) {\n  return isUTC ? 'setUTCSeconds' : 'setSeconds';\n}\nfunction millisecondsSetterName(isUTC) {\n  return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds';\n}\n\nfunction getTextRect(text, font, align, verticalAlign, padding, rich, truncate, lineHeight) {\n  var textEl = new ZRText({\n    style: {\n      text: text,\n      font: font,\n      align: align,\n      verticalAlign: verticalAlign,\n      padding: padding,\n      rich: rich,\n      overflow: truncate ? 'truncate' : null,\n      lineHeight: lineHeight\n    }\n  });\n  return textEl.getBoundingRect();\n}\n\n/**\n * Add a comma each three digit.\n */\n\nfunction addCommas(x) {\n  if (!isNumeric(x)) {\n    return isString(x) ? x : '-';\n  }\n\n  var parts = (x + '').split('.');\n  return parts[0].replace(/(\\d{1,3})(?=(?:\\d{3})+(?!\\d))/g, '$1,') + (parts.length > 1 ? '.' + parts[1] : '');\n}\nfunction toCamelCase(str, upperCaseFirst) {\n  str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {\n    return group1.toUpperCase();\n  });\n\n  if (upperCaseFirst && str) {\n    str = str.charAt(0).toUpperCase() + str.slice(1);\n  }\n\n  return str;\n}\nvar normalizeCssArray$1 = normalizeCssArray;\n/**\n * Make value user readable for tooltip and label.\n * \"User readable\":\n *     Try to not print programmer-specific text like NaN, Infinity, null, undefined.\n *     Avoid to display an empty string, which users can not recognize there is\n *     a value and it might look like a bug.\n */\n\nfunction makeValueReadable(value, valueType, useUTC) {\n  var USER_READABLE_DEFUALT_TIME_PATTERN = '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}';\n\n  function stringToUserReadable(str) {\n    return str && trim(str) ? str : '-';\n  }\n\n  function isNumberUserReadable(num) {\n    return !!(num != null && !isNaN(num) && isFinite(num));\n  }\n\n  var isTypeTime = valueType === 'time';\n  var isValueDate = value instanceof Date;\n\n  if (isTypeTime || isValueDate) {\n    var date = isTypeTime ? parseDate(value) : value;\n\n    if (!isNaN(+date)) {\n      return format(date, USER_READABLE_DEFUALT_TIME_PATTERN, useUTC);\n    } else if (isValueDate) {\n      return '-';\n    } // In other cases, continue to try to display the value in the following code.\n\n  }\n\n  if (valueType === 'ordinal') {\n    return isStringSafe(value) ? stringToUserReadable(value) : isNumber(value) ? isNumberUserReadable(value) ? value + '' : '-' : '-';\n  } // By default.\n\n\n  var numericResult = numericToNumber(value);\n  return isNumberUserReadable(numericResult) ? addCommas(numericResult) : isStringSafe(value) ? stringToUserReadable(value) : typeof value === 'boolean' ? value + '' : '-';\n}\nvar TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];\n\nvar wrapVar = function (varName, seriesIdx) {\n  return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';\n};\n/**\n * Template formatter\n * @param {Array.<Object>|Object} paramsList\n */\n\n\nfunction formatTpl(tpl, paramsList, encode) {\n  if (!isArray(paramsList)) {\n    paramsList = [paramsList];\n  }\n\n  var seriesLen = paramsList.length;\n\n  if (!seriesLen) {\n    return '';\n  }\n\n  var $vars = paramsList[0].$vars || [];\n\n  for (var i = 0; i < $vars.length; i++) {\n    var alias = TPL_VAR_ALIAS[i];\n    tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0));\n  }\n\n  for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {\n    for (var k = 0; k < $vars.length; k++) {\n      var val = paramsList[seriesIdx][$vars[k]];\n      tpl = tpl.replace(wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val);\n    }\n  }\n\n  return tpl;\n}\n/**\n * simple Template formatter\n */\n\nfunction formatTplSimple(tpl, param, encode) {\n  each(param, function (value, key) {\n    tpl = tpl.replace('{' + key + '}', encode ? encodeHTML(value) : value);\n  });\n  return tpl;\n}\nfunction getTooltipMarker(inOpt, extraCssText) {\n  var opt = isString(inOpt) ? {\n    color: inOpt,\n    extraCssText: extraCssText\n  } : inOpt || {};\n  var color = opt.color;\n  var type = opt.type;\n  extraCssText = opt.extraCssText;\n  var renderMode = opt.renderMode || 'html';\n\n  if (!color) {\n    return '';\n  }\n\n  if (renderMode === 'html') {\n    return type === 'subItem' ? '<span style=\"display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' + 'border-radius:4px;width:4px;height:4px;background-color:' // Only support string\n    + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>' : '<span style=\"display:inline-block;margin-right:4px;' + 'border-radius:10px;width:10px;height:10px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>';\n  } else {\n    // Should better not to auto generate style name by auto-increment number here.\n    // Because this util is usually called in tooltip formatter, which is probably\n    // called repeatedly when mouse move and the auto-increment number increases fast.\n    // Users can make their own style name by theirselves, make it unique and readable.\n    var markerId = opt.markerId || 'markerX';\n    return {\n      renderMode: renderMode,\n      content: '{' + markerId + '|}  ',\n      style: type === 'subItem' ? {\n        width: 4,\n        height: 4,\n        borderRadius: 2,\n        backgroundColor: color\n      } : {\n        width: 10,\n        height: 10,\n        borderRadius: 5,\n        backgroundColor: color\n      }\n    };\n  }\n}\n/**\n * @deprecated Use `time/format` instead.\n * ISO Date format\n * @param {string} tpl\n * @param {number} value\n * @param {boolean} [isUTC=false] Default in local time.\n *           see `module:echarts/scale/Time`\n *           and `module:echarts/util/number#parseDate`.\n * @inner\n */\n\nfunction formatTime(tpl, value, isUTC) {\n  if (\"development\" !== 'production') {\n    deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format');\n  }\n\n  if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year') {\n    tpl = 'MM-dd\\nyyyy';\n  }\n\n  var date = parseDate(value);\n  var getUTC = isUTC ? 'getUTC' : 'get';\n  var y = date[getUTC + 'FullYear']();\n  var M = date[getUTC + 'Month']() + 1;\n  var d = date[getUTC + 'Date']();\n  var h = date[getUTC + 'Hours']();\n  var m = date[getUTC + 'Minutes']();\n  var s = date[getUTC + 'Seconds']();\n  var S = date[getUTC + 'Milliseconds']();\n  tpl = tpl.replace('MM', pad(M, 2)).replace('M', M).replace('yyyy', y).replace('yy', pad(y % 100 + '', 2)).replace('dd', pad(d, 2)).replace('d', d).replace('hh', pad(h, 2)).replace('h', h).replace('mm', pad(m, 2)).replace('m', m).replace('ss', pad(s, 2)).replace('s', s).replace('SSS', pad(S, 3));\n  return tpl;\n}\n/**\n * Capital first\n * @param {string} str\n * @return {string}\n */\n\nfunction capitalFirst(str) {\n  return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;\n}\n/**\n * @return Never be null/undefined.\n */\n\nfunction convertToColorString(color, defaultColor) {\n  defaultColor = defaultColor || 'transparent';\n  return isString(color) ? color : isObject(color) ? color.colorStops && (color.colorStops[0] || {}).color || defaultColor : defaultColor;\n}\n/**\n * open new tab\n * @param link url\n * @param target blank or self\n */\n\nfunction windowOpen(link, target) {\n  /* global window */\n  if (target === '_blank' || target === 'blank') {\n    var blank = window.open();\n    blank.opener = null;\n    blank.location.href = link;\n  } else {\n    window.open(link, target);\n  }\n}\n\nvar each$1 = each;\n/**\n * @public\n */\n\nvar LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];\n/**\n * @public\n */\n\nvar HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];\n\nfunction boxLayout(orient, group, gap, maxWidth, maxHeight) {\n  var x = 0;\n  var y = 0;\n\n  if (maxWidth == null) {\n    maxWidth = Infinity;\n  }\n\n  if (maxHeight == null) {\n    maxHeight = Infinity;\n  }\n\n  var currentLineMaxSize = 0;\n  group.eachChild(function (child, idx) {\n    var rect = child.getBoundingRect();\n    var nextChild = group.childAt(idx + 1);\n    var nextChildRect = nextChild && nextChild.getBoundingRect();\n    var nextX;\n    var nextY;\n\n    if (orient === 'horizontal') {\n      var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);\n      nextX = x + moveX; // Wrap when width exceeds maxWidth or meet a `newline` group\n      // FIXME compare before adding gap?\n\n      if (nextX > maxWidth || child.newline) {\n        x = 0;\n        nextX = moveX;\n        y += currentLineMaxSize + gap;\n        currentLineMaxSize = rect.height;\n      } else {\n        // FIXME: consider rect.y is not `0`?\n        currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);\n      }\n    } else {\n      var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);\n      nextY = y + moveY; // Wrap when width exceeds maxHeight or meet a `newline` group\n\n      if (nextY > maxHeight || child.newline) {\n        x += currentLineMaxSize + gap;\n        y = 0;\n        nextY = moveY;\n        currentLineMaxSize = rect.width;\n      } else {\n        currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);\n      }\n    }\n\n    if (child.newline) {\n      return;\n    }\n\n    child.x = x;\n    child.y = y;\n    child.markRedraw();\n    orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;\n  });\n}\n/**\n * VBox or HBox layouting\n * @param {string} orient\n * @param {module:zrender/graphic/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */\n\n\nvar box = boxLayout;\n/**\n * VBox layouting\n * @param {module:zrender/graphic/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */\n\nvar vbox = curry(boxLayout, 'vertical');\n/**\n * HBox layouting\n * @param {module:zrender/graphic/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */\n\nvar hbox = curry(boxLayout, 'horizontal');\n/**\n * If x or x2 is not specified or 'center' 'left' 'right',\n * the width would be as long as possible.\n * If y or y2 is not specified or 'middle' 'top' 'bottom',\n * the height would be as long as possible.\n */\n\nfunction getAvailableSize(positionInfo, containerRect, margin) {\n  var containerWidth = containerRect.width;\n  var containerHeight = containerRect.height;\n  var x = parsePercent$1(positionInfo.left, containerWidth);\n  var y = parsePercent$1(positionInfo.top, containerHeight);\n  var x2 = parsePercent$1(positionInfo.right, containerWidth);\n  var y2 = parsePercent$1(positionInfo.bottom, containerHeight);\n  (isNaN(x) || isNaN(parseFloat(positionInfo.left))) && (x = 0);\n  (isNaN(x2) || isNaN(parseFloat(positionInfo.right))) && (x2 = containerWidth);\n  (isNaN(y) || isNaN(parseFloat(positionInfo.top))) && (y = 0);\n  (isNaN(y2) || isNaN(parseFloat(positionInfo.bottom))) && (y2 = containerHeight);\n  margin = normalizeCssArray$1(margin || 0);\n  return {\n    width: Math.max(x2 - x - margin[1] - margin[3], 0),\n    height: Math.max(y2 - y - margin[0] - margin[2], 0)\n  };\n}\n/**\n * Parse position info.\n */\n\nfunction getLayoutRect(positionInfo, containerRect, margin) {\n  margin = normalizeCssArray$1(margin || 0);\n  var containerWidth = containerRect.width;\n  var containerHeight = containerRect.height;\n  var left = parsePercent$1(positionInfo.left, containerWidth);\n  var top = parsePercent$1(positionInfo.top, containerHeight);\n  var right = parsePercent$1(positionInfo.right, containerWidth);\n  var bottom = parsePercent$1(positionInfo.bottom, containerHeight);\n  var width = parsePercent$1(positionInfo.width, containerWidth);\n  var height = parsePercent$1(positionInfo.height, containerHeight);\n  var verticalMargin = margin[2] + margin[0];\n  var horizontalMargin = margin[1] + margin[3];\n  var aspect = positionInfo.aspect; // If width is not specified, calculate width from left and right\n\n  if (isNaN(width)) {\n    width = containerWidth - right - horizontalMargin - left;\n  }\n\n  if (isNaN(height)) {\n    height = containerHeight - bottom - verticalMargin - top;\n  }\n\n  if (aspect != null) {\n    // If width and height are not given\n    // 1. Graph should not exceeds the container\n    // 2. Aspect must be keeped\n    // 3. Graph should take the space as more as possible\n    // FIXME\n    // Margin is not considered, because there is no case that both\n    // using margin and aspect so far.\n    if (isNaN(width) && isNaN(height)) {\n      if (aspect > containerWidth / containerHeight) {\n        width = containerWidth * 0.8;\n      } else {\n        height = containerHeight * 0.8;\n      }\n    } // Calculate width or height with given aspect\n\n\n    if (isNaN(width)) {\n      width = aspect * height;\n    }\n\n    if (isNaN(height)) {\n      height = width / aspect;\n    }\n  } // If left is not specified, calculate left from right and width\n\n\n  if (isNaN(left)) {\n    left = containerWidth - right - width - horizontalMargin;\n  }\n\n  if (isNaN(top)) {\n    top = containerHeight - bottom - height - verticalMargin;\n  } // Align left and top\n\n\n  switch (positionInfo.left || positionInfo.right) {\n    case 'center':\n      left = containerWidth / 2 - width / 2 - margin[3];\n      break;\n\n    case 'right':\n      left = containerWidth - width - horizontalMargin;\n      break;\n  }\n\n  switch (positionInfo.top || positionInfo.bottom) {\n    case 'middle':\n    case 'center':\n      top = containerHeight / 2 - height / 2 - margin[0];\n      break;\n\n    case 'bottom':\n      top = containerHeight - height - verticalMargin;\n      break;\n  } // If something is wrong and left, top, width, height are calculated as NaN\n\n\n  left = left || 0;\n  top = top || 0;\n\n  if (isNaN(width)) {\n    // Width may be NaN if only one value is given except width\n    width = containerWidth - horizontalMargin - left - (right || 0);\n  }\n\n  if (isNaN(height)) {\n    // Height may be NaN if only one value is given except height\n    height = containerHeight - verticalMargin - top - (bottom || 0);\n  }\n\n  var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);\n  rect.margin = margin;\n  return rect;\n}\n/**\n * Position a zr element in viewport\n *  Group position is specified by either\n *  {left, top}, {right, bottom}\n *  If all properties exists, right and bottom will be igonred.\n *\n * Logic:\n *     1. Scale (against origin point in parent coord)\n *     2. Rotate (against origin point in parent coord)\n *     3. Translate (with el.position by this method)\n * So this method only fixes the last step 'Translate', which does not affect\n * scaling and rotating.\n *\n * If be called repeatedly with the same input el, the same result will be gotten.\n *\n * Return true if the layout happened.\n *\n * @param el Should have `getBoundingRect` method.\n * @param positionInfo\n * @param positionInfo.left\n * @param positionInfo.top\n * @param positionInfo.right\n * @param positionInfo.bottom\n * @param positionInfo.width Only for opt.boundingModel: 'raw'\n * @param positionInfo.height Only for opt.boundingModel: 'raw'\n * @param containerRect\n * @param margin\n * @param opt\n * @param opt.hv Only horizontal or only vertical. Default to be [1, 1]\n * @param opt.boundingMode\n *        Specify how to calculate boundingRect when locating.\n *        'all': Position the boundingRect that is transformed and uioned\n *               both itself and its descendants.\n *               This mode simplies confine the elements in the bounding\n *               of their container (e.g., using 'right: 0').\n *        'raw': Position the boundingRect that is not transformed and only itself.\n *               This mode is useful when you want a element can overflow its\n *               container. (Consider a rotated circle needs to be located in a corner.)\n *               In this mode positionInfo.width/height can only be number.\n */\n\nfunction positionElement(el, positionInfo, containerRect, margin, opt, out) {\n  var h = !opt || !opt.hv || opt.hv[0];\n  var v = !opt || !opt.hv || opt.hv[1];\n  var boundingMode = opt && opt.boundingMode || 'all';\n  out = out || el;\n  out.x = el.x;\n  out.y = el.y;\n\n  if (!h && !v) {\n    return false;\n  }\n\n  var rect;\n\n  if (boundingMode === 'raw') {\n    rect = el.type === 'group' ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) : el.getBoundingRect();\n  } else {\n    rect = el.getBoundingRect();\n\n    if (el.needLocalTransform()) {\n      var transform = el.getLocalTransform(); // Notice: raw rect may be inner object of el,\n      // which should not be modified.\n\n      rect = rect.clone();\n      rect.applyTransform(transform);\n    }\n  } // The real width and height can not be specified but calculated by the given el.\n\n\n  var layoutRect = getLayoutRect(defaults({\n    width: rect.width,\n    height: rect.height\n  }, positionInfo), containerRect, margin); // Because 'tranlate' is the last step in transform\n  // (see zrender/core/Transformable#getLocalTransform),\n  // we can just only modify el.position to get final result.\n\n  var dx = h ? layoutRect.x - rect.x : 0;\n  var dy = v ? layoutRect.y - rect.y : 0;\n\n  if (boundingMode === 'raw') {\n    out.x = dx;\n    out.y = dy;\n  } else {\n    out.x += dx;\n    out.y += dy;\n  }\n\n  if (out === el) {\n    el.markRedraw();\n  }\n\n  return true;\n}\n/**\n * @param option Contains some of the properties in HV_NAMES.\n * @param hvIdx 0: horizontal; 1: vertical.\n */\n\nfunction sizeCalculable(option, hvIdx) {\n  return option[HV_NAMES[hvIdx][0]] != null || option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null;\n}\nfunction fetchLayoutMode(ins) {\n  var layoutMode = ins.layoutMode || ins.constructor.layoutMode;\n  return isObject(layoutMode) ? layoutMode : layoutMode ? {\n    type: layoutMode\n  } : null;\n}\n/**\n * Consider Case:\n * When default option has {left: 0, width: 100}, and we set {right: 0}\n * through setOption or media query, using normal zrUtil.merge will cause\n * {right: 0} does not take effect.\n *\n * @example\n * ComponentModel.extend({\n *     init: function () {\n *         ...\n *         let inputPositionParams = layout.getLayoutParams(option);\n *         this.mergeOption(inputPositionParams);\n *     },\n *     mergeOption: function (newOption) {\n *         newOption && zrUtil.merge(thisOption, newOption, true);\n *         layout.mergeLayoutParam(thisOption, newOption);\n *     }\n * });\n *\n * @param targetOption\n * @param newOption\n * @param opt\n */\n\nfunction mergeLayoutParam(targetOption, newOption, opt) {\n  var ignoreSize = opt && opt.ignoreSize;\n  !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);\n  var hResult = merge(HV_NAMES[0], 0);\n  var vResult = merge(HV_NAMES[1], 1);\n  copy(HV_NAMES[0], targetOption, hResult);\n  copy(HV_NAMES[1], targetOption, vResult);\n\n  function merge(names, hvIdx) {\n    var newParams = {};\n    var newValueCount = 0;\n    var merged = {};\n    var mergedValueCount = 0;\n    var enoughParamNumber = 2;\n    each$1(names, function (name) {\n      merged[name] = targetOption[name];\n    });\n    each$1(names, function (name) {\n      // Consider case: newOption.width is null, which is\n      // set by user for removing width setting.\n      hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);\n      hasValue(newParams, name) && newValueCount++;\n      hasValue(merged, name) && mergedValueCount++;\n    });\n\n    if (ignoreSize[hvIdx]) {\n      // Only one of left/right is premitted to exist.\n      if (hasValue(newOption, names[1])) {\n        merged[names[2]] = null;\n      } else if (hasValue(newOption, names[2])) {\n        merged[names[1]] = null;\n      }\n\n      return merged;\n    } // Case: newOption: {width: ..., right: ...},\n    // or targetOption: {right: ...} and newOption: {width: ...},\n    // There is no conflict when merged only has params count\n    // little than enoughParamNumber.\n\n\n    if (mergedValueCount === enoughParamNumber || !newValueCount) {\n      return merged;\n    } // Case: newOption: {width: ..., right: ...},\n    // Than we can make sure user only want those two, and ignore\n    // all origin params in targetOption.\n    else if (newValueCount >= enoughParamNumber) {\n        return newParams;\n      } else {\n        // Chose another param from targetOption by priority.\n        for (var i = 0; i < names.length; i++) {\n          var name_1 = names[i];\n\n          if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) {\n            newParams[name_1] = targetOption[name_1];\n            break;\n          }\n        }\n\n        return newParams;\n      }\n  }\n\n  function hasProp(obj, name) {\n    return obj.hasOwnProperty(name);\n  }\n\n  function hasValue(obj, name) {\n    return obj[name] != null && obj[name] !== 'auto';\n  }\n\n  function copy(names, target, source) {\n    each$1(names, function (name) {\n      target[name] = source[name];\n    });\n  }\n}\n/**\n * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n */\n\nfunction getLayoutParams(source) {\n  return copyLayoutParams({}, source);\n}\n/**\n * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n * @param {Object} source\n * @return {Object} Result contains those props.\n */\n\nfunction copyLayoutParams(target, source) {\n  source && target && each$1(LOCATION_PARAMS, function (name) {\n    source.hasOwnProperty(name) && (target[name] = source[name]);\n  });\n  return target;\n}\n\nvar inner = makeInner();\n\nvar ComponentModel =\n/** @class */\nfunction (_super) {\n  __extends(ComponentModel, _super);\n\n  function ComponentModel(option, parentModel, ecModel) {\n    var _this = _super.call(this, option, parentModel, ecModel) || this;\n\n    _this.uid = getUID('ec_cpt_model');\n    return _this;\n  }\n\n  ComponentModel.prototype.init = function (option, parentModel, ecModel) {\n    this.mergeDefaultAndTheme(option, ecModel);\n  };\n\n  ComponentModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n    var layoutMode = fetchLayoutMode(this);\n    var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n    var themeModel = ecModel.getTheme();\n    merge(option, themeModel.get(this.mainType));\n    merge(option, this.getDefaultOption());\n\n    if (layoutMode) {\n      mergeLayoutParam(option, inputPositionParams, layoutMode);\n    }\n  };\n\n  ComponentModel.prototype.mergeOption = function (option, ecModel) {\n    merge(this.option, option, true);\n    var layoutMode = fetchLayoutMode(this);\n\n    if (layoutMode) {\n      mergeLayoutParam(this.option, option, layoutMode);\n    }\n  };\n  /**\n   * Called immediately after `init` or `mergeOption` of this instance called.\n   */\n\n\n  ComponentModel.prototype.optionUpdated = function (newCptOption, isInit) {};\n  /**\n   * [How to declare defaultOption]:\n   *\n   * (A) If using class declaration in typescript (since echarts 5):\n   * ```ts\n   * import {ComponentOption} from '../model/option.js';\n   * export interface XxxOption extends ComponentOption {\n   *     aaa: number\n   * }\n   * export class XxxModel extends Component {\n   *     static type = 'xxx';\n   *     static defaultOption: XxxOption = {\n   *         aaa: 123\n   *     }\n   * }\n   * Component.registerClass(XxxModel);\n   * ```\n   * ```ts\n   * import {inheritDefaultOption} from '../util/component.js';\n   * import {XxxModel, XxxOption} from './XxxModel.js';\n   * export interface XxxSubOption extends XxxOption {\n   *     bbb: number\n   * }\n   * class XxxSubModel extends XxxModel {\n   *     static defaultOption: XxxSubOption = inheritDefaultOption(XxxModel.defaultOption, {\n   *         bbb: 456\n   *     })\n   *     fn() {\n   *         let opt = this.getDefaultOption();\n   *         // opt is {aaa: 123, bbb: 456}\n   *     }\n   * }\n   * ```\n   *\n   * (B) If using class extend (previous approach in echarts 3 & 4):\n   * ```js\n   * let XxxComponent = Component.extend({\n   *     defaultOption: {\n   *         xx: 123\n   *     }\n   * })\n   * ```\n   * ```js\n   * let XxxSubComponent = XxxComponent.extend({\n   *     defaultOption: {\n   *         yy: 456\n   *     },\n   *     fn: function () {\n   *         let opt = this.getDefaultOption();\n   *         // opt is {xx: 123, yy: 456}\n   *     }\n   * })\n   * ```\n   */\n\n\n  ComponentModel.prototype.getDefaultOption = function () {\n    var ctor = this.constructor; // If using class declaration, it is different to travel super class\n    // in legacy env and auto merge defaultOption. So if using class\n    // declaration, defaultOption should be merged manually.\n\n    if (!isExtendedClass(ctor)) {\n      // When using ts class, defaultOption must be declared as static.\n      return ctor.defaultOption;\n    } // FIXME: remove this approach?\n\n\n    var fields = inner(this);\n\n    if (!fields.defaultOption) {\n      var optList = [];\n      var clz = ctor;\n\n      while (clz) {\n        var opt = clz.prototype.defaultOption;\n        opt && optList.push(opt);\n        clz = clz.superClass;\n      }\n\n      var defaultOption = {};\n\n      for (var i = optList.length - 1; i >= 0; i--) {\n        defaultOption = merge(defaultOption, optList[i], true);\n      }\n\n      fields.defaultOption = defaultOption;\n    }\n\n    return fields.defaultOption;\n  };\n  /**\n   * Notice: always force to input param `useDefault` in case that forget to consider it.\n   * The same behavior as `modelUtil.parseFinder`.\n   *\n   * @param useDefault In many cases like series refer axis and axis refer grid,\n   *        If axis index / axis id not specified, use the first target as default.\n   *        In other cases like dataZoom refer axis, if not specified, measn no refer.\n   */\n\n\n  ComponentModel.prototype.getReferringComponents = function (mainType, opt) {\n    var indexKey = mainType + 'Index';\n    var idKey = mainType + 'Id';\n    return queryReferringComponents(this.ecModel, mainType, {\n      index: this.get(indexKey, true),\n      id: this.get(idKey, true)\n    }, opt);\n  };\n\n  ComponentModel.prototype.getBoxLayoutParams = function () {\n    // Consider itself having box layout configs.\n    var boxLayoutModel = this;\n    return {\n      left: boxLayoutModel.get('left'),\n      top: boxLayoutModel.get('top'),\n      right: boxLayoutModel.get('right'),\n      bottom: boxLayoutModel.get('bottom'),\n      width: boxLayoutModel.get('width'),\n      height: boxLayoutModel.get('height')\n    };\n  };\n  /**\n   * Get key for zlevel.\n   * If developers don't configure zlevel. We will assign zlevel to series based on the key.\n   * For example, lines with trail effect and progressive series will in an individual zlevel.\n   */\n\n\n  ComponentModel.prototype.getZLevelKey = function () {\n    return '';\n  };\n\n  ComponentModel.prototype.setZLevel = function (zlevel) {\n    this.option.zlevel = zlevel;\n  };\n\n  ComponentModel.protoInitialize = function () {\n    var proto = ComponentModel.prototype;\n    proto.type = 'component';\n    proto.id = '';\n    proto.name = '';\n    proto.mainType = '';\n    proto.subType = '';\n    proto.componentIndex = 0;\n  }();\n\n  return ComponentModel;\n}(Model);\n\nmountExtend(ComponentModel, Model);\nenableClassManagement(ComponentModel);\nenableSubTypeDefaulter(ComponentModel);\nenableTopologicalTravel(ComponentModel, getDependencies);\n\nfunction getDependencies(componentType) {\n  var deps = [];\n  each(ComponentModel.getClassesByMainType(componentType), function (clz) {\n    deps = deps.concat(clz.dependencies || clz.prototype.dependencies || []);\n  }); // Ensure main type.\n\n  deps = map(deps, function (type) {\n    return parseClassType(type).main;\n  }); // Hack dataset for convenience.\n\n  if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {\n    deps.unshift('dataset');\n  }\n\n  return deps;\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar platform = ''; // Navigator not exists in node\n\nif (typeof navigator !== 'undefined') {\n  /* global navigator */\n  platform = navigator.platform || '';\n}\n\nvar decalColor = 'rgba(0, 0, 0, 0.2)';\nvar globalDefault = {\n  darkMode: 'auto',\n  // backgroundColor: 'rgba(0,0,0,0)',\n  colorBy: 'series',\n  color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],\n  gradientColor: ['#f6efa6', '#d88273', '#bf444c'],\n  aria: {\n    decal: {\n      decals: [{\n        color: decalColor,\n        dashArrayX: [1, 0],\n        dashArrayY: [2, 5],\n        symbolSize: 1,\n        rotation: Math.PI / 6\n      }, {\n        color: decalColor,\n        symbol: 'circle',\n        dashArrayX: [[8, 8], [0, 8, 8, 0]],\n        dashArrayY: [6, 0],\n        symbolSize: 0.8\n      }, {\n        color: decalColor,\n        dashArrayX: [1, 0],\n        dashArrayY: [4, 3],\n        rotation: -Math.PI / 4\n      }, {\n        color: decalColor,\n        dashArrayX: [[6, 6], [0, 6, 6, 0]],\n        dashArrayY: [6, 0]\n      }, {\n        color: decalColor,\n        dashArrayX: [[1, 0], [1, 6]],\n        dashArrayY: [1, 0, 6, 0],\n        rotation: Math.PI / 4\n      }, {\n        color: decalColor,\n        symbol: 'triangle',\n        dashArrayX: [[9, 9], [0, 9, 9, 0]],\n        dashArrayY: [7, 2],\n        symbolSize: 0.75\n      }]\n    }\n  },\n  // If xAxis and yAxis declared, grid is created by default.\n  // grid: {},\n  textStyle: {\n    // color: '#000',\n    // decoration: 'none',\n    // PENDING\n    fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',\n    // fontFamily: 'Arial, Verdana, sans-serif',\n    fontSize: 12,\n    fontStyle: 'normal',\n    fontWeight: 'normal'\n  },\n  // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/\n  // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n  // Default is source-over\n  blendMode: null,\n  stateAnimation: {\n    duration: 300,\n    easing: 'cubicOut'\n  },\n  animation: 'auto',\n  animationDuration: 1000,\n  animationDurationUpdate: 500,\n  animationEasing: 'cubicInOut',\n  animationEasingUpdate: 'cubicInOut',\n  animationThreshold: 2000,\n  // Configuration for progressive/incremental rendering\n  progressiveThreshold: 3000,\n  progressive: 400,\n  // Threshold of if use single hover layer to optimize.\n  // It is recommended that `hoverLayerThreshold` is equivalent to or less than\n  // `progressiveThreshold`, otherwise hover will cause restart of progressive,\n  // which is unexpected.\n  // see example <echarts/test/heatmap-large.html>.\n  hoverLayerThreshold: 3000,\n  // See: module:echarts/scale/Time\n  useUTC: false\n};\n\nvar VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName']);\nvar SOURCE_FORMAT_ORIGINAL = 'original';\nvar SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';\nvar SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';\nvar SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';\nvar SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';\nvar SOURCE_FORMAT_UNKNOWN = 'unknown';\nvar SERIES_LAYOUT_BY_COLUMN = 'column';\nvar SERIES_LAYOUT_BY_ROW = 'row';\n\nvar BE_ORDINAL = {\n  Must: 1,\n  Might: 2,\n  Not: 3 // Other cases\n\n};\nvar innerGlobalModel = makeInner();\n/**\n * MUST be called before mergeOption of all series.\n */\n\nfunction resetSourceDefaulter(ecModel) {\n  // `datasetMap` is used to make default encode.\n  innerGlobalModel(ecModel).datasetMap = createHashMap();\n}\n/**\n * [The strategy of the arrengment of data dimensions for dataset]:\n * \"value way\": all axes are non-category axes. So series one by one take\n *     several (the number is coordSysDims.length) dimensions from dataset.\n *     The result of data arrengment of data dimensions like:\n *     | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y |\n * \"category way\": at least one axis is category axis. So the the first data\n *     dimension is always mapped to the first category axis and shared by\n *     all of the series. The other data dimensions are taken by series like\n *     \"value way\" does.\n *     The result of data arrengment of data dimensions like:\n *     | ser_shared_x | ser0_y | ser1_y | ser2_y |\n *\n * @return encode Never be `null/undefined`.\n */\n\nfunction makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) {\n  var encode = {};\n  var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n  if (!datasetModel || !coordDimensions) {\n    return encode;\n  }\n\n  var encodeItemName = [];\n  var encodeSeriesName = [];\n  var ecModel = seriesModel.ecModel;\n  var datasetMap = innerGlobalModel(ecModel).datasetMap;\n  var key = datasetModel.uid + '_' + source.seriesLayoutBy;\n  var baseCategoryDimIndex;\n  var categoryWayValueDimStart;\n  coordDimensions = coordDimensions.slice();\n  each(coordDimensions, function (coordDimInfoLoose, coordDimIdx) {\n    var coordDimInfo = isObject(coordDimInfoLoose) ? coordDimInfoLoose : coordDimensions[coordDimIdx] = {\n      name: coordDimInfoLoose\n    };\n\n    if (coordDimInfo.type === 'ordinal' && baseCategoryDimIndex == null) {\n      baseCategoryDimIndex = coordDimIdx;\n      categoryWayValueDimStart = getDataDimCountOnCoordDim(coordDimInfo);\n    }\n\n    encode[coordDimInfo.name] = [];\n  });\n  var datasetRecord = datasetMap.get(key) || datasetMap.set(key, {\n    categoryWayDim: categoryWayValueDimStart,\n    valueWayDim: 0\n  }); // TODO\n  // Auto detect first time axis and do arrangement.\n\n  each(coordDimensions, function (coordDimInfo, coordDimIdx) {\n    var coordDimName = coordDimInfo.name;\n    var count = getDataDimCountOnCoordDim(coordDimInfo); // In value way.\n\n    if (baseCategoryDimIndex == null) {\n      var start = datasetRecord.valueWayDim;\n      pushDim(encode[coordDimName], start, count);\n      pushDim(encodeSeriesName, start, count);\n      datasetRecord.valueWayDim += count; // ??? TODO give a better default series name rule?\n      // especially when encode x y specified.\n      // consider: when multiple series share one dimension\n      // category axis, series name should better use\n      // the other dimension name. On the other hand, use\n      // both dimensions name.\n    } // In category way, the first category axis.\n    else if (baseCategoryDimIndex === coordDimIdx) {\n        pushDim(encode[coordDimName], 0, count);\n        pushDim(encodeItemName, 0, count);\n      } // In category way, the other axis.\n      else {\n          var start = datasetRecord.categoryWayDim;\n          pushDim(encode[coordDimName], start, count);\n          pushDim(encodeSeriesName, start, count);\n          datasetRecord.categoryWayDim += count;\n        }\n  });\n\n  function pushDim(dimIdxArr, idxFrom, idxCount) {\n    for (var i = 0; i < idxCount; i++) {\n      dimIdxArr.push(idxFrom + i);\n    }\n  }\n\n  function getDataDimCountOnCoordDim(coordDimInfo) {\n    var dimsDef = coordDimInfo.dimsDef;\n    return dimsDef ? dimsDef.length : 1;\n  }\n\n  encodeItemName.length && (encode.itemName = encodeItemName);\n  encodeSeriesName.length && (encode.seriesName = encodeSeriesName);\n  return encode;\n}\n/**\n * Work for data like [{name: ..., value: ...}, ...].\n *\n * @return encode Never be `null/undefined`.\n */\n\nfunction makeSeriesEncodeForNameBased(seriesModel, source, dimCount) {\n  var encode = {};\n  var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n  if (!datasetModel) {\n    return encode;\n  }\n\n  var sourceFormat = source.sourceFormat;\n  var dimensionsDefine = source.dimensionsDefine;\n  var potentialNameDimIndex;\n\n  if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n    each(dimensionsDefine, function (dim, idx) {\n      if ((isObject(dim) ? dim.name : dim) === 'name') {\n        potentialNameDimIndex = idx;\n      }\n    });\n  }\n\n  var idxResult = function () {\n    var idxRes0 = {};\n    var idxRes1 = {};\n    var guessRecords = []; // 5 is an experience value.\n\n    for (var i = 0, len = Math.min(5, dimCount); i < len; i++) {\n      var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i);\n      guessRecords.push(guessResult);\n      var isPureNumber = guessResult === BE_ORDINAL.Not; // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim,\n      // and then find a name dim with the priority:\n      // \"BE_ORDINAL.Might|BE_ORDINAL.Must\" > \"other dim\" > \"the value dim itself\".\n\n      if (isPureNumber && idxRes0.v == null && i !== potentialNameDimIndex) {\n        idxRes0.v = i;\n      }\n\n      if (idxRes0.n == null || idxRes0.n === idxRes0.v || !isPureNumber && guessRecords[idxRes0.n] === BE_ORDINAL.Not) {\n        idxRes0.n = i;\n      }\n\n      if (fulfilled(idxRes0) && guessRecords[idxRes0.n] !== BE_ORDINAL.Not) {\n        return idxRes0;\n      } // [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not),\n      // find the first BE_ORDINAL.Might as the value dim,\n      // and then find a name dim with the priority:\n      // \"other dim\" > \"the value dim itself\".\n      // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be\n      // treated as number.\n\n\n      if (!isPureNumber) {\n        if (guessResult === BE_ORDINAL.Might && idxRes1.v == null && i !== potentialNameDimIndex) {\n          idxRes1.v = i;\n        }\n\n        if (idxRes1.n == null || idxRes1.n === idxRes1.v) {\n          idxRes1.n = i;\n        }\n      }\n    }\n\n    function fulfilled(idxResult) {\n      return idxResult.v != null && idxResult.n != null;\n    }\n\n    return fulfilled(idxRes0) ? idxRes0 : fulfilled(idxRes1) ? idxRes1 : null;\n  }();\n\n  if (idxResult) {\n    encode.value = [idxResult.v]; // `potentialNameDimIndex` has highest priority.\n\n    var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; // By default, label uses itemName in charts.\n    // So we don't set encodeLabel here.\n\n    encode.itemName = [nameDimIndex];\n    encode.seriesName = [nameDimIndex];\n  }\n\n  return encode;\n}\n/**\n * @return If return null/undefined, indicate that should not use datasetModel.\n */\n\nfunction querySeriesUpstreamDatasetModel(seriesModel) {\n  // Caution: consider the scenario:\n  // A dataset is declared and a series is not expected to use the dataset,\n  // and at the beginning `setOption({series: { noData })` (just prepare other\n  // option but no data), then `setOption({series: {data: [...]}); In this case,\n  // the user should set an empty array to avoid that dataset is used by default.\n  var thisData = seriesModel.get('data', true);\n\n  if (!thisData) {\n    return queryReferringComponents(seriesModel.ecModel, 'dataset', {\n      index: seriesModel.get('datasetIndex', true),\n      id: seriesModel.get('datasetId', true)\n    }, SINGLE_REFERRING).models[0];\n  }\n}\n/**\n * @return Always return an array event empty.\n */\n\nfunction queryDatasetUpstreamDatasetModels(datasetModel) {\n  // Only these attributes declared, we by defualt reference to `datasetIndex: 0`.\n  // Otherwise, no reference.\n  if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true)) {\n    return [];\n  }\n\n  return queryReferringComponents(datasetModel.ecModel, 'dataset', {\n    index: datasetModel.get('fromDatasetIndex', true),\n    id: datasetModel.get('fromDatasetId', true)\n  }, SINGLE_REFERRING).models;\n}\n/**\n * The rule should not be complex, otherwise user might not\n * be able to known where the data is wrong.\n * The code is ugly, but how to make it neat?\n */\n\nfunction guessOrdinal(source, dimIndex) {\n  return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex);\n} // dimIndex may be overflow source data.\n// return {BE_ORDINAL}\n\nfunction doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) {\n  var result; // Experience value.\n\n  var maxLoop = 5;\n\n  if (isTypedArray(data)) {\n    return BE_ORDINAL.Not;\n  } // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine\n  // always exists in source.\n\n\n  var dimName;\n  var dimType;\n\n  if (dimensionsDefine) {\n    var dimDefItem = dimensionsDefine[dimIndex];\n\n    if (isObject(dimDefItem)) {\n      dimName = dimDefItem.name;\n      dimType = dimDefItem.type;\n    } else if (isString(dimDefItem)) {\n      dimName = dimDefItem;\n    }\n  }\n\n  if (dimType != null) {\n    return dimType === 'ordinal' ? BE_ORDINAL.Must : BE_ORDINAL.Not;\n  }\n\n  if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n    var dataArrayRows = data;\n\n    if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n      var sample = dataArrayRows[dimIndex];\n\n      for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {\n        if ((result = detectValue(sample[startIndex + i])) != null) {\n          return result;\n        }\n      }\n    } else {\n      for (var i = 0; i < dataArrayRows.length && i < maxLoop; i++) {\n        var row = dataArrayRows[startIndex + i];\n\n        if (row && (result = detectValue(row[dimIndex])) != null) {\n          return result;\n        }\n      }\n    }\n  } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n    var dataObjectRows = data;\n\n    if (!dimName) {\n      return BE_ORDINAL.Not;\n    }\n\n    for (var i = 0; i < dataObjectRows.length && i < maxLoop; i++) {\n      var item = dataObjectRows[i];\n\n      if (item && (result = detectValue(item[dimName])) != null) {\n        return result;\n      }\n    }\n  } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n    var dataKeyedColumns = data;\n\n    if (!dimName) {\n      return BE_ORDINAL.Not;\n    }\n\n    var sample = dataKeyedColumns[dimName];\n\n    if (!sample || isTypedArray(sample)) {\n      return BE_ORDINAL.Not;\n    }\n\n    for (var i = 0; i < sample.length && i < maxLoop; i++) {\n      if ((result = detectValue(sample[i])) != null) {\n        return result;\n      }\n    }\n  } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n    var dataOriginal = data;\n\n    for (var i = 0; i < dataOriginal.length && i < maxLoop; i++) {\n      var item = dataOriginal[i];\n      var val = getDataItemValue(item);\n\n      if (!isArray(val)) {\n        return BE_ORDINAL.Not;\n      }\n\n      if ((result = detectValue(val[dimIndex])) != null) {\n        return result;\n      }\n    }\n  }\n\n  function detectValue(val) {\n    var beStr = isString(val); // Consider usage convenience, '1', '2' will be treated as \"number\".\n    // `isFinit('')` get `true`.\n\n    if (val != null && isFinite(val) && val !== '') {\n      return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not;\n    } else if (beStr && val !== '-') {\n      return BE_ORDINAL.Must;\n    }\n  }\n\n  return BE_ORDINAL.Not;\n}\n\nvar internalOptionCreatorMap = createHashMap();\nfunction registerInternalOptionCreator(mainType, creator) {\n  assert(internalOptionCreatorMap.get(mainType) == null && creator);\n  internalOptionCreatorMap.set(mainType, creator);\n}\nfunction concatInternalOptions(ecModel, mainType, newCmptOptionList) {\n  var internalOptionCreator = internalOptionCreatorMap.get(mainType);\n\n  if (!internalOptionCreator) {\n    return newCmptOptionList;\n  }\n\n  var internalOptions = internalOptionCreator(ecModel);\n\n  if (!internalOptions) {\n    return newCmptOptionList;\n  }\n\n  if (\"development\" !== 'production') {\n    for (var i = 0; i < internalOptions.length; i++) {\n      assert(isComponentIdInternal(internalOptions[i]));\n    }\n  }\n\n  return newCmptOptionList.concat(internalOptions);\n}\n\nvar innerColor = makeInner();\nvar innerDecal = makeInner();\n\nvar PaletteMixin =\n/** @class */\nfunction () {\n  function PaletteMixin() {}\n\n  PaletteMixin.prototype.getColorFromPalette = function (name, scope, requestNum) {\n    var defaultPalette = normalizeToArray(this.get('color', true));\n    var layeredPalette = this.get('colorLayer', true);\n    return getFromPalette(this, innerColor, defaultPalette, layeredPalette, name, scope, requestNum);\n  };\n\n  PaletteMixin.prototype.clearColorPalette = function () {\n    clearPalette(this, innerColor);\n  };\n\n  return PaletteMixin;\n}();\n\nfunction getDecalFromPalette(ecModel, name, scope, requestNum) {\n  var defaultDecals = normalizeToArray(ecModel.get(['aria', 'decal', 'decals']));\n  return getFromPalette(ecModel, innerDecal, defaultDecals, null, name, scope, requestNum);\n}\n\nfunction getNearestPalette(palettes, requestColorNum) {\n  var paletteNum = palettes.length; // TODO palettes must be in order\n\n  for (var i = 0; i < paletteNum; i++) {\n    if (palettes[i].length > requestColorNum) {\n      return palettes[i];\n    }\n  }\n\n  return palettes[paletteNum - 1];\n}\n/**\n * @param name MUST NOT be null/undefined. Otherwise call this function\n *             twise with the same parameters will get different result.\n * @param scope default this.\n * @return Can be null/undefined\n */\n\n\nfunction getFromPalette(that, inner, defaultPalette, layeredPalette, name, scope, requestNum) {\n  scope = scope || that;\n  var scopeFields = inner(scope);\n  var paletteIdx = scopeFields.paletteIdx || 0;\n  var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; // Use `hasOwnProperty` to avoid conflict with Object.prototype.\n\n  if (paletteNameMap.hasOwnProperty(name)) {\n    return paletteNameMap[name];\n  }\n\n  var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum); // In case can't find in layered color palette.\n\n  palette = palette || defaultPalette;\n\n  if (!palette || !palette.length) {\n    return;\n  }\n\n  var pickedPaletteItem = palette[paletteIdx];\n\n  if (name) {\n    paletteNameMap[name] = pickedPaletteItem;\n  }\n\n  scopeFields.paletteIdx = (paletteIdx + 1) % palette.length;\n  return pickedPaletteItem;\n}\n\nfunction clearPalette(that, inner) {\n  inner(that).paletteIdx = 0;\n  inner(that).paletteNameMap = {};\n}\n\n// Internal method names:\n// -----------------------\n\nvar reCreateSeriesIndices;\nvar assertSeriesInitialized;\nvar initBase;\nvar OPTION_INNER_KEY = '\\0_ec_inner';\nvar OPTION_INNER_VALUE = 1;\nvar BUITIN_COMPONENTS_MAP = {\n  grid: 'GridComponent',\n  polar: 'PolarComponent',\n  geo: 'GeoComponent',\n  singleAxis: 'SingleAxisComponent',\n  parallel: 'ParallelComponent',\n  calendar: 'CalendarComponent',\n  graphic: 'GraphicComponent',\n  toolbox: 'ToolboxComponent',\n  tooltip: 'TooltipComponent',\n  axisPointer: 'AxisPointerComponent',\n  brush: 'BrushComponent',\n  title: 'TitleComponent',\n  timeline: 'TimelineComponent',\n  markPoint: 'MarkPointComponent',\n  markLine: 'MarkLineComponent',\n  markArea: 'MarkAreaComponent',\n  legend: 'LegendComponent',\n  dataZoom: 'DataZoomComponent',\n  visualMap: 'VisualMapComponent',\n  // aria: 'AriaComponent',\n  // dataset: 'DatasetComponent',\n  // Dependencies\n  xAxis: 'GridComponent',\n  yAxis: 'GridComponent',\n  angleAxis: 'PolarComponent',\n  radiusAxis: 'PolarComponent'\n};\nvar BUILTIN_CHARTS_MAP = {\n  line: 'LineChart',\n  bar: 'BarChart',\n  pie: 'PieChart',\n  scatter: 'ScatterChart',\n  radar: 'RadarChart',\n  map: 'MapChart',\n  tree: 'TreeChart',\n  treemap: 'TreemapChart',\n  graph: 'GraphChart',\n  gauge: 'GaugeChart',\n  funnel: 'FunnelChart',\n  parallel: 'ParallelChart',\n  sankey: 'SankeyChart',\n  boxplot: 'BoxplotChart',\n  candlestick: 'CandlestickChart',\n  effectScatter: 'EffectScatterChart',\n  lines: 'LinesChart',\n  heatmap: 'HeatmapChart',\n  pictorialBar: 'PictorialBarChart',\n  themeRiver: 'ThemeRiverChart',\n  sunburst: 'SunburstChart',\n  custom: 'CustomChart'\n};\nvar componetsMissingLogPrinted = {};\n\nfunction checkMissingComponents(option) {\n  each(option, function (componentOption, mainType) {\n    if (!ComponentModel.hasClass(mainType)) {\n      var componentImportName = BUITIN_COMPONENTS_MAP[mainType];\n\n      if (componentImportName && !componetsMissingLogPrinted[componentImportName]) {\n        error(\"Component \" + mainType + \" is used but not imported.\\nimport { \" + componentImportName + \" } from 'echarts/components';\\necharts.use([\" + componentImportName + \"]);\");\n        componetsMissingLogPrinted[componentImportName] = true;\n      }\n    }\n  });\n}\n\nvar GlobalModel =\n/** @class */\nfunction (_super) {\n  __extends(GlobalModel, _super);\n\n  function GlobalModel() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) {\n    theme = theme || {};\n    this.option = null; // Mark as not initialized.\n\n    this._theme = new Model(theme);\n    this._locale = new Model(locale);\n    this._optionManager = optionManager;\n  };\n\n  GlobalModel.prototype.setOption = function (option, opts, optionPreprocessorFuncs) {\n    if (\"development\" !== 'production') {\n      assert(option != null, 'option is null/undefined');\n      assert(option[OPTION_INNER_KEY] !== OPTION_INNER_VALUE, 'please use chart.getOption()');\n    }\n\n    var innerOpt = normalizeSetOptionInput(opts);\n\n    this._optionManager.setOption(option, optionPreprocessorFuncs, innerOpt);\n\n    this._resetOption(null, innerOpt);\n  };\n  /**\n   * @param type null/undefined: reset all.\n   *        'recreate': force recreate all.\n   *        'timeline': only reset timeline option\n   *        'media': only reset media query option\n   * @return Whether option changed.\n   */\n\n\n  GlobalModel.prototype.resetOption = function (type, opt) {\n    return this._resetOption(type, normalizeSetOptionInput(opt));\n  };\n\n  GlobalModel.prototype._resetOption = function (type, opt) {\n    var optionChanged = false;\n    var optionManager = this._optionManager;\n\n    if (!type || type === 'recreate') {\n      var baseOption = optionManager.mountOption(type === 'recreate');\n\n      if (\"development\" !== 'production') {\n        checkMissingComponents(baseOption);\n      }\n\n      if (!this.option || type === 'recreate') {\n        initBase(this, baseOption);\n      } else {\n        this.restoreData();\n\n        this._mergeOption(baseOption, opt);\n      }\n\n      optionChanged = true;\n    }\n\n    if (type === 'timeline' || type === 'media') {\n      this.restoreData();\n    } // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`,\n    // it should better not have the same props with `MediaUnit['option']`.\n    // Because either `option2` or `MediaUnit['option']` will be always merged to \"current option\"\n    // rather than original \"baseOption\". If they both override a prop, the result might be\n    // unexpected when media state changed after `setOption` called.\n    // If we really need to modify a props in each `MediaUnit['option']`, use the full version\n    // (`{baseOption, media}`) in `setOption`.\n    // For `timeline`, the case is the same.\n\n\n    if (!type || type === 'recreate' || type === 'timeline') {\n      var timelineOption = optionManager.getTimelineOption(this);\n\n      if (timelineOption) {\n        optionChanged = true;\n\n        this._mergeOption(timelineOption, opt);\n      }\n    }\n\n    if (!type || type === 'recreate' || type === 'media') {\n      var mediaOptions = optionManager.getMediaOption(this);\n\n      if (mediaOptions.length) {\n        each(mediaOptions, function (mediaOption) {\n          optionChanged = true;\n\n          this._mergeOption(mediaOption, opt);\n        }, this);\n      }\n    }\n\n    return optionChanged;\n  };\n\n  GlobalModel.prototype.mergeOption = function (option) {\n    this._mergeOption(option, null);\n  };\n\n  GlobalModel.prototype._mergeOption = function (newOption, opt) {\n    var option = this.option;\n    var componentsMap = this._componentsMap;\n    var componentsCount = this._componentsCount;\n    var newCmptTypes = [];\n    var newCmptTypeMap = createHashMap();\n    var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap;\n    resetSourceDefaulter(this); // If no component class, merge directly.\n    // For example: color, animaiton options, etc.\n\n    each(newOption, function (componentOption, mainType) {\n      if (componentOption == null) {\n        return;\n      }\n\n      if (!ComponentModel.hasClass(mainType)) {\n        // globalSettingTask.dirty();\n        option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true);\n      } else if (mainType) {\n        newCmptTypes.push(mainType);\n        newCmptTypeMap.set(mainType, true);\n      }\n    });\n\n    if (replaceMergeMainTypeMap) {\n      // If there is a mainType `xxx` in `replaceMerge` but not declared in option,\n      // we trade it as it is declared in option as `{xxx: []}`. Because:\n      // (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`.\n      // (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`.\n      replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) {\n        if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) {\n          newCmptTypes.push(mainTypeInReplaceMerge);\n          newCmptTypeMap.set(mainTypeInReplaceMerge, true);\n        }\n      });\n    }\n\n    ComponentModel.topologicalTravel(newCmptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this);\n\n    function visitComponent(mainType) {\n      var newCmptOptionList = concatInternalOptions(this, mainType, normalizeToArray(newOption[mainType]));\n      var oldCmptList = componentsMap.get(mainType);\n      var mergeMode = // `!oldCmptList` means init. See the comment in `mappingToExists`\n      !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge';\n      var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode); // Set mainType and complete subType.\n\n      setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); // Empty it before the travel, in order to prevent `this._componentsMap`\n      // from being used in the `init`/`mergeOption`/`optionUpdated` of some\n      // components, which is probably incorrect logic.\n\n      option[mainType] = null;\n      componentsMap.set(mainType, null);\n      componentsCount.set(mainType, 0);\n      var optionsByMainType = [];\n      var cmptsByMainType = [];\n      var cmptsCountByMainType = 0;\n      var tooltipExists;\n      var tooltipWarningLogged;\n      each(mappingResult, function (resultItem, index) {\n        var componentModel = resultItem.existing;\n        var newCmptOption = resultItem.newOption;\n\n        if (!newCmptOption) {\n          if (componentModel) {\n            // Consider where is no new option and should be merged using {},\n            // see removeEdgeAndAdd in topologicalTravel and\n            // ComponentModel.getAllClassMainTypes.\n            componentModel.mergeOption({}, this);\n            componentModel.optionUpdated({}, false);\n          } // If no both `resultItem.exist` and `resultItem.option`,\n          // either it is in `replaceMerge` and not matched by any id,\n          // or it has been removed in previous `replaceMerge` and left a \"hole\" in this component index.\n\n        } else {\n          var isSeriesType = mainType === 'series';\n          var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, !isSeriesType // Give a more detailed warn later if series don't exists\n          );\n\n          if (!ComponentModelClass) {\n            if (\"development\" !== 'production') {\n              var subType = resultItem.keyInfo.subType;\n              var seriesImportName = BUILTIN_CHARTS_MAP[subType];\n\n              if (!componetsMissingLogPrinted[subType]) {\n                componetsMissingLogPrinted[subType] = true;\n\n                if (seriesImportName) {\n                  error(\"Series \" + subType + \" is used but not imported.\\nimport { \" + seriesImportName + \" } from 'echarts/charts';\\necharts.use([\" + seriesImportName + \"]);\");\n                } else {\n                  error(\"Unknown series \" + subType);\n                }\n              }\n            }\n\n            return;\n          } // TODO Before multiple tooltips get supported, we do this check to avoid unexpected exception.\n\n\n          if (mainType === 'tooltip') {\n            if (tooltipExists) {\n              if (\"development\" !== 'production') {\n                if (!tooltipWarningLogged) {\n                  warn('Currently only one tooltip component is allowed.');\n                  tooltipWarningLogged = true;\n                }\n              }\n\n              return;\n            }\n\n            tooltipExists = true;\n          }\n\n          if (componentModel && componentModel.constructor === ComponentModelClass) {\n            componentModel.name = resultItem.keyInfo.name; // componentModel.settingTask && componentModel.settingTask.dirty();\n\n            componentModel.mergeOption(newCmptOption, this);\n            componentModel.optionUpdated(newCmptOption, false);\n          } else {\n            // PENDING Global as parent ?\n            var extraOpt = extend({\n              componentIndex: index\n            }, resultItem.keyInfo);\n            componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); // Assign `keyInfo`\n\n            extend(componentModel, extraOpt);\n\n            if (resultItem.brandNew) {\n              componentModel.__requireNewView = true;\n            }\n\n            componentModel.init(newCmptOption, this, this); // Call optionUpdated after init.\n            // newCmptOption has been used as componentModel.option\n            // and may be merged with theme and default, so pass null\n            // to avoid confusion.\n\n            componentModel.optionUpdated(null, true);\n          }\n        }\n\n        if (componentModel) {\n          optionsByMainType.push(componentModel.option);\n          cmptsByMainType.push(componentModel);\n          cmptsCountByMainType++;\n        } else {\n          // Always do assign to avoid elided item in array.\n          optionsByMainType.push(void 0);\n          cmptsByMainType.push(void 0);\n        }\n      }, this);\n      option[mainType] = optionsByMainType;\n      componentsMap.set(mainType, cmptsByMainType);\n      componentsCount.set(mainType, cmptsCountByMainType); // Backup series for filtering.\n\n      if (mainType === 'series') {\n        reCreateSeriesIndices(this);\n      }\n    } // If no series declared, ensure `_seriesIndices` initialized.\n\n\n    if (!this._seriesIndices) {\n      reCreateSeriesIndices(this);\n    }\n  };\n  /**\n   * Get option for output (cloned option and inner info removed)\n   */\n\n\n  GlobalModel.prototype.getOption = function () {\n    var option = clone(this.option);\n    each(option, function (optInMainType, mainType) {\n      if (ComponentModel.hasClass(mainType)) {\n        var opts = normalizeToArray(optInMainType); // Inner cmpts need to be removed.\n        // Inner cmpts might not be at last since ec5.0, but still\n        // compatible for users: if inner cmpt at last, splice the returned array.\n\n        var realLen = opts.length;\n        var metNonInner = false;\n\n        for (var i = realLen - 1; i >= 0; i--) {\n          // Remove options with inner id.\n          if (opts[i] && !isComponentIdInternal(opts[i])) {\n            metNonInner = true;\n          } else {\n            opts[i] = null;\n            !metNonInner && realLen--;\n          }\n        }\n\n        opts.length = realLen;\n        option[mainType] = opts;\n      }\n    });\n    delete option[OPTION_INNER_KEY];\n    return option;\n  };\n\n  GlobalModel.prototype.getTheme = function () {\n    return this._theme;\n  };\n\n  GlobalModel.prototype.getLocaleModel = function () {\n    return this._locale;\n  };\n\n  GlobalModel.prototype.setUpdatePayload = function (payload) {\n    this._payload = payload;\n  };\n\n  GlobalModel.prototype.getUpdatePayload = function () {\n    return this._payload;\n  };\n  /**\n   * @param idx If not specified, return the first one.\n   */\n\n\n  GlobalModel.prototype.getComponent = function (mainType, idx) {\n    var list = this._componentsMap.get(mainType);\n\n    if (list) {\n      var cmpt = list[idx || 0];\n\n      if (cmpt) {\n        return cmpt;\n      } else if (idx == null) {\n        for (var i = 0; i < list.length; i++) {\n          if (list[i]) {\n            return list[i];\n          }\n        }\n      }\n    }\n  };\n  /**\n   * @return Never be null/undefined.\n   */\n\n\n  GlobalModel.prototype.queryComponents = function (condition) {\n    var mainType = condition.mainType;\n\n    if (!mainType) {\n      return [];\n    }\n\n    var index = condition.index;\n    var id = condition.id;\n    var name = condition.name;\n\n    var cmpts = this._componentsMap.get(mainType);\n\n    if (!cmpts || !cmpts.length) {\n      return [];\n    }\n\n    var result;\n\n    if (index != null) {\n      result = [];\n      each(normalizeToArray(index), function (idx) {\n        cmpts[idx] && result.push(cmpts[idx]);\n      });\n    } else if (id != null) {\n      result = queryByIdOrName('id', id, cmpts);\n    } else if (name != null) {\n      result = queryByIdOrName('name', name, cmpts);\n    } else {\n      // Return all non-empty components in that mainType\n      result = filter(cmpts, function (cmpt) {\n        return !!cmpt;\n      });\n    }\n\n    return filterBySubType(result, condition);\n  };\n  /**\n   * The interface is different from queryComponents,\n   * which is convenient for inner usage.\n   *\n   * @usage\n   * let result = findComponents(\n   *     {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}\n   * );\n   * let result = findComponents(\n   *     {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}\n   * );\n   * let result = findComponents(\n   *     {mainType: 'series',\n   *     filter: function (model, index) {...}}\n   * );\n   * // result like [component0, componnet1, ...]\n   */\n\n\n  GlobalModel.prototype.findComponents = function (condition) {\n    var query = condition.query;\n    var mainType = condition.mainType;\n    var queryCond = getQueryCond(query);\n    var result = queryCond ? this.queryComponents(queryCond) // Retrieve all non-empty components.\n    : filter(this._componentsMap.get(mainType), function (cmpt) {\n      return !!cmpt;\n    });\n    return doFilter(filterBySubType(result, condition));\n\n    function getQueryCond(q) {\n      var indexAttr = mainType + 'Index';\n      var idAttr = mainType + 'Id';\n      var nameAttr = mainType + 'Name';\n      return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? {\n        mainType: mainType,\n        // subType will be filtered finally.\n        index: q[indexAttr],\n        id: q[idAttr],\n        name: q[nameAttr]\n      } : null;\n    }\n\n    function doFilter(res) {\n      return condition.filter ? filter(res, condition.filter) : res;\n    }\n  };\n\n  GlobalModel.prototype.eachComponent = function (mainType, cb, context) {\n    var componentsMap = this._componentsMap;\n\n    if (isFunction(mainType)) {\n      var ctxForAll_1 = cb;\n      var cbForAll_1 = mainType;\n      componentsMap.each(function (cmpts, componentType) {\n        for (var i = 0; cmpts && i < cmpts.length; i++) {\n          var cmpt = cmpts[i];\n          cmpt && cbForAll_1.call(ctxForAll_1, componentType, cmpt, cmpt.componentIndex);\n        }\n      });\n    } else {\n      var cmpts = isString(mainType) ? componentsMap.get(mainType) : isObject(mainType) ? this.findComponents(mainType) : null;\n\n      for (var i = 0; cmpts && i < cmpts.length; i++) {\n        var cmpt = cmpts[i];\n        cmpt && cb.call(context, cmpt, cmpt.componentIndex);\n      }\n    }\n  };\n  /**\n   * Get series list before filtered by name.\n   */\n\n\n  GlobalModel.prototype.getSeriesByName = function (name) {\n    var nameStr = convertOptionIdName(name, null);\n    return filter(this._componentsMap.get('series'), function (oneSeries) {\n      return !!oneSeries && nameStr != null && oneSeries.name === nameStr;\n    });\n  };\n  /**\n   * Get series list before filtered by index.\n   */\n\n\n  GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) {\n    return this._componentsMap.get('series')[seriesIndex];\n  };\n  /**\n   * Get series list before filtered by type.\n   * FIXME: rename to getRawSeriesByType?\n   */\n\n\n  GlobalModel.prototype.getSeriesByType = function (subType) {\n    return filter(this._componentsMap.get('series'), function (oneSeries) {\n      return !!oneSeries && oneSeries.subType === subType;\n    });\n  };\n  /**\n   * Get all series before filtered.\n   */\n\n\n  GlobalModel.prototype.getSeries = function () {\n    return filter(this._componentsMap.get('series'), function (oneSeries) {\n      return !!oneSeries;\n    });\n  };\n  /**\n   * Count series before filtered.\n   */\n\n\n  GlobalModel.prototype.getSeriesCount = function () {\n    return this._componentsCount.get('series');\n  };\n  /**\n   * After filtering, series may be different\n   * from raw series.\n   */\n\n\n  GlobalModel.prototype.eachSeries = function (cb, context) {\n    assertSeriesInitialized(this);\n    each(this._seriesIndices, function (rawSeriesIndex) {\n      var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n      cb.call(context, series, rawSeriesIndex);\n    }, this);\n  };\n  /**\n   * Iterate raw series before filtered.\n   *\n   * @param {Function} cb\n   * @param {*} context\n   */\n\n\n  GlobalModel.prototype.eachRawSeries = function (cb, context) {\n    each(this._componentsMap.get('series'), function (series) {\n      series && cb.call(context, series, series.componentIndex);\n    });\n  };\n  /**\n   * After filtering, series may be different.\n   * from raw series.\n   */\n\n\n  GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) {\n    assertSeriesInitialized(this);\n    each(this._seriesIndices, function (rawSeriesIndex) {\n      var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n      if (series.subType === subType) {\n        cb.call(context, series, rawSeriesIndex);\n      }\n    }, this);\n  };\n  /**\n   * Iterate raw series before filtered of given type.\n   */\n\n\n  GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) {\n    return each(this.getSeriesByType(subType), cb, context);\n  };\n\n  GlobalModel.prototype.isSeriesFiltered = function (seriesModel) {\n    assertSeriesInitialized(this);\n    return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;\n  };\n\n  GlobalModel.prototype.getCurrentSeriesIndices = function () {\n    return (this._seriesIndices || []).slice();\n  };\n\n  GlobalModel.prototype.filterSeries = function (cb, context) {\n    assertSeriesInitialized(this);\n    var newSeriesIndices = [];\n    each(this._seriesIndices, function (seriesRawIdx) {\n      var series = this._componentsMap.get('series')[seriesRawIdx];\n\n      cb.call(context, series, seriesRawIdx) && newSeriesIndices.push(seriesRawIdx);\n    }, this);\n    this._seriesIndices = newSeriesIndices;\n    this._seriesIndicesMap = createHashMap(newSeriesIndices);\n  };\n\n  GlobalModel.prototype.restoreData = function (payload) {\n    reCreateSeriesIndices(this);\n    var componentsMap = this._componentsMap;\n    var componentTypes = [];\n    componentsMap.each(function (components, componentType) {\n      if (ComponentModel.hasClass(componentType)) {\n        componentTypes.push(componentType);\n      }\n    });\n    ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType) {\n      each(componentsMap.get(componentType), function (component) {\n        if (component && (componentType !== 'series' || !isNotTargetSeries(component, payload))) {\n          component.restoreData();\n        }\n      });\n    });\n  };\n\n  GlobalModel.internalField = function () {\n    reCreateSeriesIndices = function (ecModel) {\n      var seriesIndices = ecModel._seriesIndices = [];\n      each(ecModel._componentsMap.get('series'), function (series) {\n        // series may have been removed by `replaceMerge`.\n        series && seriesIndices.push(series.componentIndex);\n      });\n      ecModel._seriesIndicesMap = createHashMap(seriesIndices);\n    };\n\n    assertSeriesInitialized = function (ecModel) {\n      // Components that use _seriesIndices should depends on series component,\n      // which make sure that their initialization is after series.\n      if (\"development\" !== 'production') {\n        if (!ecModel._seriesIndices) {\n          throw new Error('Option should contains series.');\n        }\n      }\n    };\n\n    initBase = function (ecModel, baseOption) {\n      // Using OPTION_INNER_KEY to mark that this option cannot be used outside,\n      // i.e. `chart.setOption(chart.getModel().option);` is forbidden.\n      ecModel.option = {};\n      ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; // Init with series: [], in case of calling findSeries method\n      // before series initialized.\n\n      ecModel._componentsMap = createHashMap({\n        series: []\n      });\n      ecModel._componentsCount = createHashMap(); // If user spefied `option.aria`, aria will be enable. This detection should be\n      // performed before theme and globalDefault merge.\n\n      var airaOption = baseOption.aria;\n\n      if (isObject(airaOption) && airaOption.enabled == null) {\n        airaOption.enabled = true;\n      }\n\n      mergeTheme(baseOption, ecModel._theme.option); // TODO Needs clone when merging to the unexisted property\n\n      merge(baseOption, globalDefault, false);\n\n      ecModel._mergeOption(baseOption, null);\n    };\n  }();\n\n  return GlobalModel;\n}(Model);\n\nfunction isNotTargetSeries(seriesModel, payload) {\n  if (payload) {\n    var index = payload.seriesIndex;\n    var id = payload.seriesId;\n    var name_1 = payload.seriesName;\n    return index != null && seriesModel.componentIndex !== index || id != null && seriesModel.id !== id || name_1 != null && seriesModel.name !== name_1;\n  }\n}\n\nfunction mergeTheme(option, theme) {\n  // PENDING\n  // NOT use `colorLayer` in theme if option has `color`\n  var notMergeColorLayer = option.color && !option.colorLayer;\n  each(theme, function (themeItem, name) {\n    if (name === 'colorLayer' && notMergeColorLayer) {\n      return;\n    } // If it is component model mainType, the model handles that merge later.\n    // otherwise, merge them here.\n\n\n    if (!ComponentModel.hasClass(name)) {\n      if (typeof themeItem === 'object') {\n        option[name] = !option[name] ? clone(themeItem) : merge(option[name], themeItem, false);\n      } else {\n        if (option[name] == null) {\n          option[name] = themeItem;\n        }\n      }\n    }\n  });\n}\n\nfunction queryByIdOrName(attr, idOrName, cmpts) {\n  // Here is a break from echarts4: string and number are\n  // treated as equal.\n  if (isArray(idOrName)) {\n    var keyMap_1 = createHashMap();\n    each(idOrName, function (idOrNameItem) {\n      if (idOrNameItem != null) {\n        var idName = convertOptionIdName(idOrNameItem, null);\n        idName != null && keyMap_1.set(idOrNameItem, true);\n      }\n    });\n    return filter(cmpts, function (cmpt) {\n      return cmpt && keyMap_1.get(cmpt[attr]);\n    });\n  } else {\n    var idName_1 = convertOptionIdName(idOrName, null);\n    return filter(cmpts, function (cmpt) {\n      return cmpt && idName_1 != null && cmpt[attr] === idName_1;\n    });\n  }\n}\n\nfunction filterBySubType(components, condition) {\n  // Using hasOwnProperty for restrict. Consider\n  // subType is undefined in user payload.\n  return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) {\n    return cmpt && cmpt.subType === condition.subType;\n  }) : components;\n}\n\nfunction normalizeSetOptionInput(opts) {\n  var replaceMergeMainTypeMap = createHashMap();\n  opts && each(normalizeToArray(opts.replaceMerge), function (mainType) {\n    if (\"development\" !== 'production') {\n      assert(ComponentModel.hasClass(mainType), '\"' + mainType + '\" is not valid component main type in \"replaceMerge\"');\n    }\n\n    replaceMergeMainTypeMap.set(mainType, true);\n  });\n  return {\n    replaceMergeMainTypeMap: replaceMergeMainTypeMap\n  };\n}\n\nmixin(GlobalModel, PaletteMixin);\n\nvar availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isSSR', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', // 'getModel',\n'getOption', // 'getViewOfComponentModel',\n// 'getViewOfSeriesModel',\n'getId', 'updateLabelLayout'];\n\nvar ExtensionAPI =\n/** @class */\nfunction () {\n  function ExtensionAPI(ecInstance) {\n    each(availableMethods, function (methodName) {\n      this[methodName] = bind(ecInstance[methodName], ecInstance);\n    }, this);\n  }\n\n  return ExtensionAPI;\n}();\n\nvar coordinateSystemCreators = {};\n\nvar CoordinateSystemManager =\n/** @class */\nfunction () {\n  function CoordinateSystemManager() {\n    this._coordinateSystems = [];\n  }\n\n  CoordinateSystemManager.prototype.create = function (ecModel, api) {\n    var coordinateSystems = [];\n    each(coordinateSystemCreators, function (creator, type) {\n      var list = creator.create(ecModel, api);\n      coordinateSystems = coordinateSystems.concat(list || []);\n    });\n    this._coordinateSystems = coordinateSystems;\n  };\n\n  CoordinateSystemManager.prototype.update = function (ecModel, api) {\n    each(this._coordinateSystems, function (coordSys) {\n      coordSys.update && coordSys.update(ecModel, api);\n    });\n  };\n\n  CoordinateSystemManager.prototype.getCoordinateSystems = function () {\n    return this._coordinateSystems.slice();\n  };\n\n  CoordinateSystemManager.register = function (type, creator) {\n    coordinateSystemCreators[type] = creator;\n  };\n\n  CoordinateSystemManager.get = function (type) {\n    return coordinateSystemCreators[type];\n  };\n\n  return CoordinateSystemManager;\n}();\n\nvar QUERY_REG = /^(min|max)?(.+)$/; // Key: mainType\n// type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>;\n\n/**\n * TERM EXPLANATIONS:\n * See `ECOption` and `ECUnitOption` in `src/util/types.ts`.\n */\n\nvar OptionManager =\n/** @class */\nfunction () {\n  // timeline.notMerge is not supported in ec3. Firstly there is rearly\n  // case that notMerge is needed. Secondly supporting 'notMerge' requires\n  // rawOption cloned and backuped when timeline changed, which does no\n  // good to performance. What's more, that both timeline and setOption\n  // method supply 'notMerge' brings complex and some problems.\n  // Consider this case:\n  // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);\n  // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);\n  function OptionManager(api) {\n    this._timelineOptions = [];\n    this._mediaList = [];\n    /**\n     * -1, means default.\n     * empty means no media.\n     */\n\n    this._currentMediaIndices = [];\n    this._api = api;\n  }\n\n  OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) {\n    if (rawOption) {\n      // That set dat primitive is dangerous if user reuse the data when setOption again.\n      each(normalizeToArray(rawOption.series), function (series) {\n        series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);\n      });\n      each(normalizeToArray(rawOption.dataset), function (dataset) {\n        dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source);\n      });\n    } // Caution: some series modify option data, if do not clone,\n    // it should ensure that the repeat modify correctly\n    // (create a new object when modify itself).\n\n\n    rawOption = clone(rawOption); // FIXME\n    // If some property is set in timeline options or media option but\n    // not set in baseOption, a warning should be given.\n\n    var optionBackup = this._optionBackup;\n    var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup);\n    this._newBaseOption = newParsedOption.baseOption; // For setOption at second time (using merge mode);\n\n    if (optionBackup) {\n      // FIXME\n      // the restore merge solution is essentially incorrect.\n      // the mapping can not be 100% consistent with ecModel, which probably brings\n      // potential bug!\n      // The first merge is delayed, because in most cases, users do not call `setOption` twice.\n      // let fakeCmptsMap = this._fakeCmptsMap;\n      // if (!fakeCmptsMap) {\n      //     fakeCmptsMap = this._fakeCmptsMap = createHashMap();\n      //     mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null);\n      // }\n      // mergeToBackupOption(\n      //     fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt\n      // );\n      // For simplicity, timeline options and media options do not support merge,\n      // that is, if you `setOption` twice and both has timeline options, the latter\n      // timeline options will not be merged to the former, but just substitute them.\n      if (newParsedOption.timelineOptions.length) {\n        optionBackup.timelineOptions = newParsedOption.timelineOptions;\n      }\n\n      if (newParsedOption.mediaList.length) {\n        optionBackup.mediaList = newParsedOption.mediaList;\n      }\n\n      if (newParsedOption.mediaDefault) {\n        optionBackup.mediaDefault = newParsedOption.mediaDefault;\n      }\n    } else {\n      this._optionBackup = newParsedOption;\n    }\n  };\n\n  OptionManager.prototype.mountOption = function (isRecreate) {\n    var optionBackup = this._optionBackup;\n    this._timelineOptions = optionBackup.timelineOptions;\n    this._mediaList = optionBackup.mediaList;\n    this._mediaDefault = optionBackup.mediaDefault;\n    this._currentMediaIndices = [];\n    return clone(isRecreate // this._optionBackup.baseOption, which is created at the first `setOption`\n    // called, and is merged into every new option by inner method `mergeToBackupOption`\n    // each time `setOption` called, can be only used in `isRecreate`, because\n    // its reliability is under suspicion. In other cases option merge is\n    // performed by `model.mergeOption`.\n    ? optionBackup.baseOption : this._newBaseOption);\n  };\n\n  OptionManager.prototype.getTimelineOption = function (ecModel) {\n    var option;\n    var timelineOptions = this._timelineOptions;\n\n    if (timelineOptions.length) {\n      // getTimelineOption can only be called after ecModel inited,\n      // so we can get currentIndex from timelineModel.\n      var timelineModel = ecModel.getComponent('timeline');\n\n      if (timelineModel) {\n        option = clone( // FIXME:TS as TimelineModel or quivlant interface\n        timelineOptions[timelineModel.getCurrentIndex()]);\n      }\n    }\n\n    return option;\n  };\n\n  OptionManager.prototype.getMediaOption = function (ecModel) {\n    var ecWidth = this._api.getWidth();\n\n    var ecHeight = this._api.getHeight();\n\n    var mediaList = this._mediaList;\n    var mediaDefault = this._mediaDefault;\n    var indices = [];\n    var result = []; // No media defined.\n\n    if (!mediaList.length && !mediaDefault) {\n      return result;\n    } // Multi media may be applied, the latter defined media has higher priority.\n\n\n    for (var i = 0, len = mediaList.length; i < len; i++) {\n      if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {\n        indices.push(i);\n      }\n    } // FIXME\n    // Whether mediaDefault should force users to provide? Otherwise\n    // the change by media query can not be recorvered.\n\n\n    if (!indices.length && mediaDefault) {\n      indices = [-1];\n    }\n\n    if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {\n      result = map(indices, function (index) {\n        return clone(index === -1 ? mediaDefault.option : mediaList[index].option);\n      });\n    } // Otherwise return nothing.\n\n\n    this._currentMediaIndices = indices;\n    return result;\n  };\n\n  return OptionManager;\n}();\n/**\n * [RAW_OPTION_PATTERNS]\n * (Note: \"series: []\" represents all other props in `ECUnitOption`)\n *\n * (1) No prop \"baseOption\" declared:\n * Root option is used as \"baseOption\" (except prop \"options\" and \"media\").\n * ```js\n * option = {\n *     series: [],\n *     timeline: {},\n *     options: [],\n * };\n * option = {\n *     series: [],\n *     media: {},\n * };\n * option = {\n *     series: [],\n *     timeline: {},\n *     options: [],\n *     media: {},\n * }\n * ```\n *\n * (2) Prop \"baseOption\" declared:\n * If \"baseOption\" declared, `ECUnitOption` props can only be declared\n * inside \"baseOption\" except prop \"timeline\" (compat ec2).\n * ```js\n * option = {\n *     baseOption: {\n *         timeline: {},\n *         series: [],\n *     },\n *     options: []\n * };\n * option = {\n *     baseOption: {\n *         series: [],\n *     },\n *     media: []\n * };\n * option = {\n *     baseOption: {\n *         timeline: {},\n *         series: [],\n *     },\n *     options: []\n *     media: []\n * };\n * option = {\n *     // ec3 compat ec2: allow (only) `timeline` declared\n *     // outside baseOption. Keep this setting for compat.\n *     timeline: {},\n *     baseOption: {\n *         series: [],\n *     },\n *     options: [],\n *     media: []\n * };\n * ```\n */\n\n\nfunction parseRawOption( // `rawOption` May be modified\nrawOption, optionPreprocessorFuncs, isNew) {\n  var mediaList = [];\n  var mediaDefault;\n  var baseOption;\n  var declaredBaseOption = rawOption.baseOption; // Compatible with ec2, [RAW_OPTION_PATTERNS] above.\n\n  var timelineOnRoot = rawOption.timeline;\n  var timelineOptionsOnRoot = rawOption.options;\n  var mediaOnRoot = rawOption.media;\n  var hasMedia = !!rawOption.media;\n  var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline);\n\n  if (declaredBaseOption) {\n    baseOption = declaredBaseOption; // For merge option.\n\n    if (!baseOption.timeline) {\n      baseOption.timeline = timelineOnRoot;\n    }\n  } // For convenience, enable to use the root option as the `baseOption`:\n  // `{ ...normalOptionProps, media: [{ ... }, { ... }] }`\n  else {\n      if (hasTimeline || hasMedia) {\n        rawOption.options = rawOption.media = null;\n      }\n\n      baseOption = rawOption;\n    }\n\n  if (hasMedia) {\n    if (isArray(mediaOnRoot)) {\n      each(mediaOnRoot, function (singleMedia) {\n        if (\"development\" !== 'production') {\n          // Real case of wrong config.\n          if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) {\n            error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }');\n          }\n        }\n\n        if (singleMedia && singleMedia.option) {\n          if (singleMedia.query) {\n            mediaList.push(singleMedia);\n          } else if (!mediaDefault) {\n            // Use the first media default.\n            mediaDefault = singleMedia;\n          }\n        }\n      });\n    } else {\n      if (\"development\" !== 'production') {\n        // Real case of wrong config.\n        error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }');\n      }\n    }\n  }\n\n  doPreprocess(baseOption);\n  each(timelineOptionsOnRoot, function (option) {\n    return doPreprocess(option);\n  });\n  each(mediaList, function (media) {\n    return doPreprocess(media.option);\n  });\n\n  function doPreprocess(option) {\n    each(optionPreprocessorFuncs, function (preProcess) {\n      preProcess(option, isNew);\n    });\n  }\n\n  return {\n    baseOption: baseOption,\n    timelineOptions: timelineOptionsOnRoot || [],\n    mediaDefault: mediaDefault,\n    mediaList: mediaList\n  };\n}\n/**\n * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>\n * Support: width, height, aspectRatio\n * Can use max or min as prefix.\n */\n\n\nfunction applyMediaQuery(query, ecWidth, ecHeight) {\n  var realMap = {\n    width: ecWidth,\n    height: ecHeight,\n    aspectratio: ecWidth / ecHeight // lower case for convenience.\n\n  };\n  var applicable = true;\n  each(query, function (value, attr) {\n    var matched = attr.match(QUERY_REG);\n\n    if (!matched || !matched[1] || !matched[2]) {\n      return;\n    }\n\n    var operator = matched[1];\n    var realAttr = matched[2].toLowerCase();\n\n    if (!compare(realMap[realAttr], value, operator)) {\n      applicable = false;\n    }\n  });\n  return applicable;\n}\n\nfunction compare(real, expect, operator) {\n  if (operator === 'min') {\n    return real >= expect;\n  } else if (operator === 'max') {\n    return real <= expect;\n  } else {\n    // Equals\n    return real === expect;\n  }\n}\n\nfunction indicesEquals(indices1, indices2) {\n  // indices is always order by asc and has only finite number.\n  return indices1.join(',') === indices2.join(',');\n}\n\nvar each$2 = each;\nvar isObject$1 = isObject;\nvar POSSIBLE_STYLES = ['areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', 'chordStyle', 'label', 'labelLine'];\n\nfunction compatEC2ItemStyle(opt) {\n  var itemStyleOpt = opt && opt.itemStyle;\n\n  if (!itemStyleOpt) {\n    return;\n  }\n\n  for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {\n    var styleName = POSSIBLE_STYLES[i];\n    var normalItemStyleOpt = itemStyleOpt.normal;\n    var emphasisItemStyleOpt = itemStyleOpt.emphasis;\n\n    if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {\n      if (\"development\" !== 'production') {\n        deprecateReplaceLog(\"itemStyle.normal.\" + styleName, styleName);\n      }\n\n      opt[styleName] = opt[styleName] || {};\n\n      if (!opt[styleName].normal) {\n        opt[styleName].normal = normalItemStyleOpt[styleName];\n      } else {\n        merge(opt[styleName].normal, normalItemStyleOpt[styleName]);\n      }\n\n      normalItemStyleOpt[styleName] = null;\n    }\n\n    if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {\n      if (\"development\" !== 'production') {\n        deprecateReplaceLog(\"itemStyle.emphasis.\" + styleName, \"emphasis.\" + styleName);\n      }\n\n      opt[styleName] = opt[styleName] || {};\n\n      if (!opt[styleName].emphasis) {\n        opt[styleName].emphasis = emphasisItemStyleOpt[styleName];\n      } else {\n        merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);\n      }\n\n      emphasisItemStyleOpt[styleName] = null;\n    }\n  }\n}\n\nfunction convertNormalEmphasis(opt, optType, useExtend) {\n  if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) {\n    var normalOpt = opt[optType].normal;\n    var emphasisOpt = opt[optType].emphasis;\n\n    if (normalOpt) {\n      if (\"development\" !== 'production') {\n        // eslint-disable-next-line max-len\n        deprecateLog(\"'normal' hierarchy in \" + optType + \" has been removed since 4.0. All style properties are configured in \" + optType + \" directly now.\");\n      } // Timeline controlStyle has other properties besides normal and emphasis\n\n\n      if (useExtend) {\n        opt[optType].normal = opt[optType].emphasis = null;\n        defaults(opt[optType], normalOpt);\n      } else {\n        opt[optType] = normalOpt;\n      }\n    }\n\n    if (emphasisOpt) {\n      if (\"development\" !== 'production') {\n        deprecateLog(optType + \".emphasis has been changed to emphasis.\" + optType + \" since 4.0\");\n      }\n\n      opt.emphasis = opt.emphasis || {};\n      opt.emphasis[optType] = emphasisOpt; // Also compat the case user mix the style and focus together in ec3 style\n      // for example: { itemStyle: { normal: {}, emphasis: {focus, shadowBlur} } }\n\n      if (emphasisOpt.focus) {\n        opt.emphasis.focus = emphasisOpt.focus;\n      }\n\n      if (emphasisOpt.blurScope) {\n        opt.emphasis.blurScope = emphasisOpt.blurScope;\n      }\n    }\n  }\n}\n\nfunction removeEC3NormalStatus(opt) {\n  convertNormalEmphasis(opt, 'itemStyle');\n  convertNormalEmphasis(opt, 'lineStyle');\n  convertNormalEmphasis(opt, 'areaStyle');\n  convertNormalEmphasis(opt, 'label');\n  convertNormalEmphasis(opt, 'labelLine'); // treemap\n\n  convertNormalEmphasis(opt, 'upperLabel'); // graph\n\n  convertNormalEmphasis(opt, 'edgeLabel');\n}\n\nfunction compatTextStyle(opt, propName) {\n  // Check whether is not object (string\\null\\undefined ...)\n  var labelOptSingle = isObject$1(opt) && opt[propName];\n  var textStyle = isObject$1(labelOptSingle) && labelOptSingle.textStyle;\n\n  if (textStyle) {\n    if (\"development\" !== 'production') {\n      // eslint-disable-next-line max-len\n      deprecateLog(\"textStyle hierarchy in \" + propName + \" has been removed since 4.0. All textStyle properties are configured in \" + propName + \" directly now.\");\n    }\n\n    for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {\n      var textPropName = TEXT_STYLE_OPTIONS[i];\n\n      if (textStyle.hasOwnProperty(textPropName)) {\n        labelOptSingle[textPropName] = textStyle[textPropName];\n      }\n    }\n  }\n}\n\nfunction compatEC3CommonStyles(opt) {\n  if (opt) {\n    removeEC3NormalStatus(opt);\n    compatTextStyle(opt, 'label');\n    opt.emphasis && compatTextStyle(opt.emphasis, 'label');\n  }\n}\n\nfunction processSeries(seriesOpt) {\n  if (!isObject$1(seriesOpt)) {\n    return;\n  }\n\n  compatEC2ItemStyle(seriesOpt);\n  removeEC3NormalStatus(seriesOpt);\n  compatTextStyle(seriesOpt, 'label'); // treemap\n\n  compatTextStyle(seriesOpt, 'upperLabel'); // graph\n\n  compatTextStyle(seriesOpt, 'edgeLabel');\n\n  if (seriesOpt.emphasis) {\n    compatTextStyle(seriesOpt.emphasis, 'label'); // treemap\n\n    compatTextStyle(seriesOpt.emphasis, 'upperLabel'); // graph\n\n    compatTextStyle(seriesOpt.emphasis, 'edgeLabel');\n  }\n\n  var markPoint = seriesOpt.markPoint;\n\n  if (markPoint) {\n    compatEC2ItemStyle(markPoint);\n    compatEC3CommonStyles(markPoint);\n  }\n\n  var markLine = seriesOpt.markLine;\n\n  if (markLine) {\n    compatEC2ItemStyle(markLine);\n    compatEC3CommonStyles(markLine);\n  }\n\n  var markArea = seriesOpt.markArea;\n\n  if (markArea) {\n    compatEC3CommonStyles(markArea);\n  }\n\n  var data = seriesOpt.data; // Break with ec3: if `setOption` again, there may be no `type` in option,\n  // then the backward compat based on option type will not be performed.\n\n  if (seriesOpt.type === 'graph') {\n    data = data || seriesOpt.nodes;\n    var edgeData = seriesOpt.links || seriesOpt.edges;\n\n    if (edgeData && !isTypedArray(edgeData)) {\n      for (var i = 0; i < edgeData.length; i++) {\n        compatEC3CommonStyles(edgeData[i]);\n      }\n    }\n\n    each(seriesOpt.categories, function (opt) {\n      removeEC3NormalStatus(opt);\n    });\n  }\n\n  if (data && !isTypedArray(data)) {\n    for (var i = 0; i < data.length; i++) {\n      compatEC3CommonStyles(data[i]);\n    }\n  } // mark point data\n\n\n  markPoint = seriesOpt.markPoint;\n\n  if (markPoint && markPoint.data) {\n    var mpData = markPoint.data;\n\n    for (var i = 0; i < mpData.length; i++) {\n      compatEC3CommonStyles(mpData[i]);\n    }\n  } // mark line data\n\n\n  markLine = seriesOpt.markLine;\n\n  if (markLine && markLine.data) {\n    var mlData = markLine.data;\n\n    for (var i = 0; i < mlData.length; i++) {\n      if (isArray(mlData[i])) {\n        compatEC3CommonStyles(mlData[i][0]);\n        compatEC3CommonStyles(mlData[i][1]);\n      } else {\n        compatEC3CommonStyles(mlData[i]);\n      }\n    }\n  } // Series\n\n\n  if (seriesOpt.type === 'gauge') {\n    compatTextStyle(seriesOpt, 'axisLabel');\n    compatTextStyle(seriesOpt, 'title');\n    compatTextStyle(seriesOpt, 'detail');\n  } else if (seriesOpt.type === 'treemap') {\n    convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle');\n    each(seriesOpt.levels, function (opt) {\n      removeEC3NormalStatus(opt);\n    });\n  } else if (seriesOpt.type === 'tree') {\n    removeEC3NormalStatus(seriesOpt.leaves);\n  } // sunburst starts from ec4, so it does not need to compat levels.\n\n}\n\nfunction toArr(o) {\n  return isArray(o) ? o : o ? [o] : [];\n}\n\nfunction toObj(o) {\n  return (isArray(o) ? o[0] : o) || {};\n}\n\nfunction globalCompatStyle(option, isTheme) {\n  each$2(toArr(option.series), function (seriesOpt) {\n    isObject$1(seriesOpt) && processSeries(seriesOpt);\n  });\n  var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];\n  isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');\n  each$2(axes, function (axisName) {\n    each$2(toArr(option[axisName]), function (axisOpt) {\n      if (axisOpt) {\n        compatTextStyle(axisOpt, 'axisLabel');\n        compatTextStyle(axisOpt.axisPointer, 'label');\n      }\n    });\n  });\n  each$2(toArr(option.parallel), function (parallelOpt) {\n    var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;\n    compatTextStyle(parallelAxisDefault, 'axisLabel');\n    compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');\n  });\n  each$2(toArr(option.calendar), function (calendarOpt) {\n    convertNormalEmphasis(calendarOpt, 'itemStyle');\n    compatTextStyle(calendarOpt, 'dayLabel');\n    compatTextStyle(calendarOpt, 'monthLabel');\n    compatTextStyle(calendarOpt, 'yearLabel');\n  }); // radar.name.textStyle\n\n  each$2(toArr(option.radar), function (radarOpt) {\n    compatTextStyle(radarOpt, 'name'); // Use axisName instead of name because component has name property\n\n    if (radarOpt.name && radarOpt.axisName == null) {\n      radarOpt.axisName = radarOpt.name;\n      delete radarOpt.name;\n\n      if (\"development\" !== 'production') {\n        deprecateLog('name property in radar component has been changed to axisName');\n      }\n    }\n\n    if (radarOpt.nameGap != null && radarOpt.axisNameGap == null) {\n      radarOpt.axisNameGap = radarOpt.nameGap;\n      delete radarOpt.nameGap;\n\n      if (\"development\" !== 'production') {\n        deprecateLog('nameGap property in radar component has been changed to axisNameGap');\n      }\n    }\n\n    if (\"development\" !== 'production') {\n      each$2(radarOpt.indicator, function (indicatorOpt) {\n        if (indicatorOpt.text) {\n          deprecateReplaceLog('text', 'name', 'radar.indicator');\n        }\n      });\n    }\n  });\n  each$2(toArr(option.geo), function (geoOpt) {\n    if (isObject$1(geoOpt)) {\n      compatEC3CommonStyles(geoOpt);\n      each$2(toArr(geoOpt.regions), function (regionObj) {\n        compatEC3CommonStyles(regionObj);\n      });\n    }\n  });\n  each$2(toArr(option.timeline), function (timelineOpt) {\n    compatEC3CommonStyles(timelineOpt);\n    convertNormalEmphasis(timelineOpt, 'label');\n    convertNormalEmphasis(timelineOpt, 'itemStyle');\n    convertNormalEmphasis(timelineOpt, 'controlStyle', true);\n    var data = timelineOpt.data;\n    isArray(data) && each(data, function (item) {\n      if (isObject(item)) {\n        convertNormalEmphasis(item, 'label');\n        convertNormalEmphasis(item, 'itemStyle');\n      }\n    });\n  });\n  each$2(toArr(option.toolbox), function (toolboxOpt) {\n    convertNormalEmphasis(toolboxOpt, 'iconStyle');\n    each$2(toolboxOpt.feature, function (featureOpt) {\n      convertNormalEmphasis(featureOpt, 'iconStyle');\n    });\n  });\n  compatTextStyle(toObj(option.axisPointer), 'label');\n  compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); // Clean logs\n  // storedLogs = {};\n}\n\nfunction get(opt, path) {\n  var pathArr = path.split(',');\n  var obj = opt;\n\n  for (var i = 0; i < pathArr.length; i++) {\n    obj = obj && obj[pathArr[i]];\n\n    if (obj == null) {\n      break;\n    }\n  }\n\n  return obj;\n}\n\nfunction set$1(opt, path, val, overwrite) {\n  var pathArr = path.split(',');\n  var obj = opt;\n  var key;\n  var i = 0;\n\n  for (; i < pathArr.length - 1; i++) {\n    key = pathArr[i];\n\n    if (obj[key] == null) {\n      obj[key] = {};\n    }\n\n    obj = obj[key];\n  }\n\n  if (overwrite || obj[pathArr[i]] == null) {\n    obj[pathArr[i]] = val;\n  }\n}\n\nfunction compatLayoutProperties(option) {\n  option && each(LAYOUT_PROPERTIES, function (prop) {\n    if (prop[0] in option && !(prop[1] in option)) {\n      option[prop[1]] = option[prop[0]];\n    }\n  });\n}\n\nvar LAYOUT_PROPERTIES = [['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']];\nvar COMPATITABLE_COMPONENTS = ['grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'];\nvar BAR_ITEM_STYLE_MAP = [['borderRadius', 'barBorderRadius'], ['borderColor', 'barBorderColor'], ['borderWidth', 'barBorderWidth']];\n\nfunction compatBarItemStyle(option) {\n  var itemStyle = option && option.itemStyle;\n\n  if (itemStyle) {\n    for (var i = 0; i < BAR_ITEM_STYLE_MAP.length; i++) {\n      var oldName = BAR_ITEM_STYLE_MAP[i][1];\n      var newName = BAR_ITEM_STYLE_MAP[i][0];\n\n      if (itemStyle[oldName] != null) {\n        itemStyle[newName] = itemStyle[oldName];\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog(oldName, newName);\n        }\n      }\n    }\n  }\n}\n\nfunction compatPieLabel(option) {\n  if (!option) {\n    return;\n  }\n\n  if (option.alignTo === 'edge' && option.margin != null && option.edgeDistance == null) {\n    if (\"development\" !== 'production') {\n      deprecateReplaceLog('label.margin', 'label.edgeDistance', 'pie');\n    }\n\n    option.edgeDistance = option.margin;\n  }\n}\n\nfunction compatSunburstState(option) {\n  if (!option) {\n    return;\n  }\n\n  if (option.downplay && !option.blur) {\n    option.blur = option.downplay;\n\n    if (\"development\" !== 'production') {\n      deprecateReplaceLog('downplay', 'blur', 'sunburst');\n    }\n  }\n}\n\nfunction compatGraphFocus(option) {\n  if (!option) {\n    return;\n  }\n\n  if (option.focusNodeAdjacency != null) {\n    option.emphasis = option.emphasis || {};\n\n    if (option.emphasis.focus == null) {\n      if (\"development\" !== 'production') {\n        deprecateReplaceLog('focusNodeAdjacency', 'emphasis: { focus: \\'adjacency\\'}', 'graph/sankey');\n      }\n\n      option.emphasis.focus = 'adjacency';\n    }\n  }\n}\n\nfunction traverseTree(data, cb) {\n  if (data) {\n    for (var i = 0; i < data.length; i++) {\n      cb(data[i]);\n      data[i] && traverseTree(data[i].children, cb);\n    }\n  }\n}\n\nfunction globalBackwardCompat(option, isTheme) {\n  globalCompatStyle(option, isTheme); // Make sure series array for model initialization.\n\n  option.series = normalizeToArray(option.series);\n  each(option.series, function (seriesOpt) {\n    if (!isObject(seriesOpt)) {\n      return;\n    }\n\n    var seriesType = seriesOpt.type;\n\n    if (seriesType === 'line') {\n      if (seriesOpt.clipOverflow != null) {\n        seriesOpt.clip = seriesOpt.clipOverflow;\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('clipOverflow', 'clip', 'line');\n        }\n      }\n    } else if (seriesType === 'pie' || seriesType === 'gauge') {\n      if (seriesOpt.clockWise != null) {\n        seriesOpt.clockwise = seriesOpt.clockWise;\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('clockWise', 'clockwise');\n        }\n      }\n\n      compatPieLabel(seriesOpt.label);\n      var data = seriesOpt.data;\n\n      if (data && !isTypedArray(data)) {\n        for (var i = 0; i < data.length; i++) {\n          compatPieLabel(data[i]);\n        }\n      }\n\n      if (seriesOpt.hoverOffset != null) {\n        seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n        if (seriesOpt.emphasis.scaleSize = null) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog('hoverOffset', 'emphasis.scaleSize');\n          }\n\n          seriesOpt.emphasis.scaleSize = seriesOpt.hoverOffset;\n        }\n      }\n    } else if (seriesType === 'gauge') {\n      var pointerColor = get(seriesOpt, 'pointer.color');\n      pointerColor != null && set$1(seriesOpt, 'itemStyle.color', pointerColor);\n    } else if (seriesType === 'bar') {\n      compatBarItemStyle(seriesOpt);\n      compatBarItemStyle(seriesOpt.backgroundStyle);\n      compatBarItemStyle(seriesOpt.emphasis);\n      var data = seriesOpt.data;\n\n      if (data && !isTypedArray(data)) {\n        for (var i = 0; i < data.length; i++) {\n          if (typeof data[i] === 'object') {\n            compatBarItemStyle(data[i]);\n            compatBarItemStyle(data[i] && data[i].emphasis);\n          }\n        }\n      }\n    } else if (seriesType === 'sunburst') {\n      var highlightPolicy = seriesOpt.highlightPolicy;\n\n      if (highlightPolicy) {\n        seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n        if (!seriesOpt.emphasis.focus) {\n          seriesOpt.emphasis.focus = highlightPolicy;\n\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog('highlightPolicy', 'emphasis.focus', 'sunburst');\n          }\n        }\n      }\n\n      compatSunburstState(seriesOpt);\n      traverseTree(seriesOpt.data, compatSunburstState);\n    } else if (seriesType === 'graph' || seriesType === 'sankey') {\n      compatGraphFocus(seriesOpt); // TODO nodes, edges?\n    } else if (seriesType === 'map') {\n      if (seriesOpt.mapType && !seriesOpt.map) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('mapType', 'map', 'map');\n        }\n\n        seriesOpt.map = seriesOpt.mapType;\n      }\n\n      if (seriesOpt.mapLocation) {\n        if (\"development\" !== 'production') {\n          deprecateLog('`mapLocation` is not used anymore.');\n        }\n\n        defaults(seriesOpt, seriesOpt.mapLocation);\n      }\n    }\n\n    if (seriesOpt.hoverAnimation != null) {\n      seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n      if (seriesOpt.emphasis && seriesOpt.emphasis.scale == null) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('hoverAnimation', 'emphasis.scale');\n        }\n\n        seriesOpt.emphasis.scale = seriesOpt.hoverAnimation;\n      }\n    }\n\n    compatLayoutProperties(seriesOpt);\n  }); // dataRange has changed to visualMap\n\n  if (option.dataRange) {\n    option.visualMap = option.dataRange;\n  }\n\n  each(COMPATITABLE_COMPONENTS, function (componentName) {\n    var options = option[componentName];\n\n    if (options) {\n      if (!isArray(options)) {\n        options = [options];\n      }\n\n      each(options, function (option) {\n        compatLayoutProperties(option);\n      });\n    }\n  });\n}\n\n//     data processing stage is blocked in stream.\n//     See <module:echarts/stream/Scheduler#performDataProcessorTasks>\n// (2) Only register once when import repeatedly.\n//     Should be executed after series is filtered and before stack calculation.\n\nfunction dataStack(ecModel) {\n  var stackInfoMap = createHashMap();\n  ecModel.eachSeries(function (seriesModel) {\n    var stack = seriesModel.get('stack'); // Compatible: when `stack` is set as '', do not stack.\n\n    if (stack) {\n      var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []);\n      var data = seriesModel.getData();\n      var stackInfo = {\n        // Used for calculate axis extent automatically.\n        // TODO: Type getCalculationInfo return more specific type?\n        stackResultDimension: data.getCalculationInfo('stackResultDimension'),\n        stackedOverDimension: data.getCalculationInfo('stackedOverDimension'),\n        stackedDimension: data.getCalculationInfo('stackedDimension'),\n        stackedByDimension: data.getCalculationInfo('stackedByDimension'),\n        isStackedByIndex: data.getCalculationInfo('isStackedByIndex'),\n        data: data,\n        seriesModel: seriesModel\n      }; // If stacked on axis that do not support data stack.\n\n      if (!stackInfo.stackedDimension || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)) {\n        return;\n      }\n\n      stackInfoList.length && data.setCalculationInfo('stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel);\n      stackInfoList.push(stackInfo);\n    }\n  });\n  stackInfoMap.each(calculateStack);\n}\n\nfunction calculateStack(stackInfoList) {\n  each(stackInfoList, function (targetStackInfo, idxInStack) {\n    var resultVal = [];\n    var resultNaN = [NaN, NaN];\n    var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension];\n    var targetData = targetStackInfo.data;\n    var isStackedByIndex = targetStackInfo.isStackedByIndex;\n    var stackStrategy = targetStackInfo.seriesModel.get('stackStrategy') || 'samesign'; // Should not write on raw data, because stack series model list changes\n    // depending on legend selection.\n\n    targetData.modify(dims, function (v0, v1, dataIndex) {\n      var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); // Consider `connectNulls` of line area, if value is NaN, stackedOver\n      // should also be NaN, to draw a appropriate belt area.\n\n      if (isNaN(sum)) {\n        return resultNaN;\n      }\n\n      var byValue;\n      var stackedDataRawIndex;\n\n      if (isStackedByIndex) {\n        stackedDataRawIndex = targetData.getRawIndex(dataIndex);\n      } else {\n        byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex);\n      } // If stackOver is NaN, chart view will render point on value start.\n\n\n      var stackedOver = NaN;\n\n      for (var j = idxInStack - 1; j >= 0; j--) {\n        var stackInfo = stackInfoList[j]; // Has been optimized by inverted indices on `stackedByDimension`.\n\n        if (!isStackedByIndex) {\n          stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue);\n        }\n\n        if (stackedDataRawIndex >= 0) {\n          var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); // Considering positive stack, negative stack and empty data\n\n          if (stackStrategy === 'all' // single stack group\n          || stackStrategy === 'positive' && val > 0 || stackStrategy === 'negative' && val < 0 || stackStrategy === 'samesign' && sum >= 0 && val > 0 // All positive stack\n          || stackStrategy === 'samesign' && sum <= 0 && val < 0 // All negative stack\n          ) {\n              // The sum has to be very small to be affected by the\n              // floating arithmetic problem. An incorrect result will probably\n              // cause axis min/max to be filtered incorrectly.\n              sum = addSafe(sum, val);\n              stackedOver = val;\n              break;\n            }\n        }\n      }\n\n      resultVal[0] = sum;\n      resultVal[1] = stackedOver;\n      return resultVal;\n    });\n  });\n}\n\nvar SourceImpl =\n/** @class */\nfunction () {\n  function SourceImpl(fields) {\n    this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []);\n    this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; // Visit config\n\n    this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;\n    this.startIndex = fields.startIndex || 0;\n    this.dimensionsDetectedCount = fields.dimensionsDetectedCount;\n    this.metaRawOption = fields.metaRawOption;\n    var dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine;\n\n    if (dimensionsDefine) {\n      for (var i = 0; i < dimensionsDefine.length; i++) {\n        var dim = dimensionsDefine[i];\n\n        if (dim.type == null) {\n          if (guessOrdinal(this, i) === BE_ORDINAL.Must) {\n            dim.type = 'ordinal';\n          }\n        }\n      }\n    }\n  }\n\n  return SourceImpl;\n}();\n\nfunction isSourceInstance(val) {\n  return val instanceof SourceImpl;\n}\n/**\n * Create a source from option.\n * NOTE: Created source is immutable. Don't change any properties in it.\n */\n\nfunction createSource(sourceData, thisMetaRawOption, // can be null. If not provided, auto detect it from `sourceData`.\nsourceFormat) {\n  sourceFormat = sourceFormat || detectSourceFormat(sourceData);\n  var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy;\n  var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions);\n  var source = new SourceImpl({\n    data: sourceData,\n    sourceFormat: sourceFormat,\n    seriesLayoutBy: seriesLayoutBy,\n    dimensionsDefine: determined.dimensionsDefine,\n    startIndex: determined.startIndex,\n    dimensionsDetectedCount: determined.dimensionsDetectedCount,\n    metaRawOption: clone(thisMetaRawOption)\n  });\n  return source;\n}\n/**\n * Wrap original series data for some compatibility cases.\n */\n\nfunction createSourceFromSeriesDataOption(data) {\n  return new SourceImpl({\n    data: data,\n    sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL\n  });\n}\n/**\n * Clone source but excludes source data.\n */\n\nfunction cloneSourceShallow(source) {\n  return new SourceImpl({\n    data: source.data,\n    sourceFormat: source.sourceFormat,\n    seriesLayoutBy: source.seriesLayoutBy,\n    dimensionsDefine: clone(source.dimensionsDefine),\n    startIndex: source.startIndex,\n    dimensionsDetectedCount: source.dimensionsDetectedCount\n  });\n}\n/**\n * Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`.\n */\n\nfunction detectSourceFormat(data) {\n  var sourceFormat = SOURCE_FORMAT_UNKNOWN;\n\n  if (isTypedArray(data)) {\n    sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;\n  } else if (isArray(data)) {\n    // FIXME Whether tolerate null in top level array?\n    if (data.length === 0) {\n      sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n    }\n\n    for (var i = 0, len = data.length; i < len; i++) {\n      var item = data[i];\n\n      if (item == null) {\n        continue;\n      } else if (isArray(item)) {\n        sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n        break;\n      } else if (isObject(item)) {\n        sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;\n        break;\n      }\n    }\n  } else if (isObject(data)) {\n    for (var key in data) {\n      if (hasOwn(data, key) && isArrayLike(data[key])) {\n        sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;\n        break;\n      }\n    }\n  }\n\n  return sourceFormat;\n}\n/**\n * Determine the source definitions from data standalone dimensions definitions\n * are not specified.\n */\n\nfunction determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, // standalone raw dimensions definition, like:\n// {\n//     dimensions: ['aa', 'bb', { name: 'cc', type: 'time' }]\n// }\n// in `dataset` or `series`\ndimensionsDefine) {\n  var dimensionsDetectedCount;\n  var startIndex; // PENDING: Could data be null/undefined here?\n  // currently, if `dataset.source` not specified, error thrown.\n  // if `series.data` not specified, nothing rendered without error thrown.\n  // Should test these cases.\n\n  if (!data) {\n    return {\n      dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n      startIndex: startIndex,\n      dimensionsDetectedCount: dimensionsDetectedCount\n    };\n  }\n\n  if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n    var dataArrayRows = data; // Rule: Most of the first line are string: it is header.\n    // Caution: consider a line with 5 string and 1 number,\n    // it still can not be sure it is a head, because the\n    // 5 string may be 5 values of category columns.\n\n    if (sourceHeader === 'auto' || sourceHeader == null) {\n      arrayRowsTravelFirst(function (val) {\n        // '-' is regarded as null/undefined.\n        if (val != null && val !== '-') {\n          if (isString(val)) {\n            startIndex == null && (startIndex = 1);\n          } else {\n            startIndex = 0;\n          }\n        } // 10 is an experience number, avoid long loop.\n\n      }, seriesLayoutBy, dataArrayRows, 10);\n    } else {\n      startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0;\n    }\n\n    if (!dimensionsDefine && startIndex === 1) {\n      dimensionsDefine = [];\n      arrayRowsTravelFirst(function (val, index) {\n        dimensionsDefine[index] = val != null ? val + '' : '';\n      }, seriesLayoutBy, dataArrayRows, Infinity);\n    }\n\n    dimensionsDetectedCount = dimensionsDefine ? dimensionsDefine.length : seriesLayoutBy === SERIES_LAYOUT_BY_ROW ? dataArrayRows.length : dataArrayRows[0] ? dataArrayRows[0].length : null;\n  } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n    if (!dimensionsDefine) {\n      dimensionsDefine = objectRowsCollectDimensions(data);\n    }\n  } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n    if (!dimensionsDefine) {\n      dimensionsDefine = [];\n      each(data, function (colArr, key) {\n        dimensionsDefine.push(key);\n      });\n    }\n  } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n    var value0 = getDataItemValue(data[0]);\n    dimensionsDetectedCount = isArray(value0) && value0.length || 1;\n  } else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n    if (\"development\" !== 'production') {\n      assert(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.');\n    }\n  }\n\n  return {\n    startIndex: startIndex,\n    dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n    dimensionsDetectedCount: dimensionsDetectedCount\n  };\n}\n\nfunction objectRowsCollectDimensions(data) {\n  var firstIndex = 0;\n  var obj;\n\n  while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line\n\n\n  if (obj) {\n    var dimensions_1 = [];\n    each(obj, function (value, key) {\n      dimensions_1.push(key);\n    });\n    return dimensions_1;\n  }\n} // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],\n// which is reasonable. But dimension name is duplicated.\n// Returns undefined or an array contains only object without null/undefined or string.\n\n\nfunction normalizeDimensionsOption(dimensionsDefine) {\n  if (!dimensionsDefine) {\n    // The meaning of null/undefined is different from empty array.\n    return;\n  }\n\n  var nameMap = createHashMap();\n  return map(dimensionsDefine, function (rawItem, index) {\n    rawItem = isObject(rawItem) ? rawItem : {\n      name: rawItem\n    }; // Other fields will be discarded.\n\n    var item = {\n      name: rawItem.name,\n      displayName: rawItem.displayName,\n      type: rawItem.type\n    }; // User can set null in dimensions.\n    // We don't auto specify name, otherwise a given name may\n    // cause it to be referred unexpectedly.\n\n    if (item.name == null) {\n      return item;\n    } // Also consider number form like 2012.\n\n\n    item.name += ''; // User may also specify displayName.\n    // displayName will always exists except user not\n    // specified or dim name is not specified or detected.\n    // (A auto generated dim name will not be used as\n    // displayName).\n\n    if (item.displayName == null) {\n      item.displayName = item.name;\n    }\n\n    var exist = nameMap.get(item.name);\n\n    if (!exist) {\n      nameMap.set(item.name, {\n        count: 1\n      });\n    } else {\n      item.name += '-' + exist.count++;\n    }\n\n    return item;\n  });\n}\n\nfunction arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {\n  if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n    for (var i = 0; i < data.length && i < maxLoop; i++) {\n      cb(data[i] ? data[i][0] : null, i);\n    }\n  } else {\n    var value0 = data[0] || [];\n\n    for (var i = 0; i < value0.length && i < maxLoop; i++) {\n      cb(value0[i], i);\n    }\n  }\n}\n\nfunction shouldRetrieveDataByName(source) {\n  var sourceFormat = source.sourceFormat;\n  return sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS;\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar _a, _b, _c; // TODO\nvar providerMethods;\nvar mountMethods;\n/**\n * If normal array used, mutable chunk size is supported.\n * If typed array used, chunk size must be fixed.\n */\n\nvar DefaultDataProvider =\n/** @class */\nfunction () {\n  function DefaultDataProvider(sourceParam, dimSize) {\n    // let source: Source;\n    var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam; // declare source is Source;\n\n    this._source = source;\n    var data = this._data = source.data; // Typed array. TODO IE10+?\n\n    if (source.sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n      if (\"development\" !== 'production') {\n        if (dimSize == null) {\n          throw new Error('Typed array data must specify dimension size');\n        }\n      }\n\n      this._offset = 0;\n      this._dimSize = dimSize;\n      this._data = data;\n    }\n\n    mountMethods(this, data, source);\n  }\n\n  DefaultDataProvider.prototype.getSource = function () {\n    return this._source;\n  };\n\n  DefaultDataProvider.prototype.count = function () {\n    return 0;\n  };\n\n  DefaultDataProvider.prototype.getItem = function (idx, out) {\n    return;\n  };\n\n  DefaultDataProvider.prototype.appendData = function (newData) {};\n\n  DefaultDataProvider.prototype.clean = function () {};\n\n  DefaultDataProvider.protoInitialize = function () {\n    // PENDING: To avoid potential incompat (e.g., prototype\n    // is visited somewhere), still init them on prototype.\n    var proto = DefaultDataProvider.prototype;\n    proto.pure = false;\n    proto.persistent = true;\n  }();\n\n  DefaultDataProvider.internalField = function () {\n    var _a;\n\n    mountMethods = function (provider, data, source) {\n      var sourceFormat = source.sourceFormat;\n      var seriesLayoutBy = source.seriesLayoutBy;\n      var startIndex = source.startIndex;\n      var dimsDef = source.dimensionsDefine;\n      var methods = providerMethods[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n      if (\"development\" !== 'production') {\n        assert(methods, 'Invalide sourceFormat: ' + sourceFormat);\n      }\n\n      extend(provider, methods);\n\n      if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n        provider.getItem = getItemForTypedArray;\n        provider.count = countForTypedArray;\n        provider.fillStorage = fillStorageForTypedArray;\n      } else {\n        var rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy);\n        provider.getItem = bind(rawItemGetter, null, data, startIndex, dimsDef);\n        var rawCounter = getRawSourceDataCounter(sourceFormat, seriesLayoutBy);\n        provider.count = bind(rawCounter, null, data, startIndex, dimsDef);\n      }\n    };\n\n    var getItemForTypedArray = function (idx, out) {\n      idx = idx - this._offset;\n      out = out || [];\n      var data = this._data;\n      var dimSize = this._dimSize;\n      var offset = dimSize * idx;\n\n      for (var i = 0; i < dimSize; i++) {\n        out[i] = data[offset + i];\n      }\n\n      return out;\n    };\n\n    var fillStorageForTypedArray = function (start, end, storage, extent) {\n      var data = this._data;\n      var dimSize = this._dimSize;\n\n      for (var dim = 0; dim < dimSize; dim++) {\n        var dimExtent = extent[dim];\n        var min = dimExtent[0] == null ? Infinity : dimExtent[0];\n        var max = dimExtent[1] == null ? -Infinity : dimExtent[1];\n        var count = end - start;\n        var arr = storage[dim];\n\n        for (var i = 0; i < count; i++) {\n          // appendData with TypedArray will always do replace in provider.\n          var val = data[i * dimSize + dim];\n          arr[start + i] = val;\n          val < min && (min = val);\n          val > max && (max = val);\n        }\n\n        dimExtent[0] = min;\n        dimExtent[1] = max;\n      }\n    };\n\n    var countForTypedArray = function () {\n      return this._data ? this._data.length / this._dimSize : 0;\n    };\n\n    providerMethods = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = {\n      pure: true,\n      appendData: appendDataSimply\n    }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = {\n      pure: true,\n      appendData: function () {\n        throw new Error('Do not support appendData when set seriesLayoutBy: \"row\".');\n      }\n    }, _a[SOURCE_FORMAT_OBJECT_ROWS] = {\n      pure: true,\n      appendData: appendDataSimply\n    }, _a[SOURCE_FORMAT_KEYED_COLUMNS] = {\n      pure: true,\n      appendData: function (newData) {\n        var data = this._data;\n        each(newData, function (newCol, key) {\n          var oldCol = data[key] || (data[key] = []);\n\n          for (var i = 0; i < (newCol || []).length; i++) {\n            oldCol.push(newCol[i]);\n          }\n        });\n      }\n    }, _a[SOURCE_FORMAT_ORIGINAL] = {\n      appendData: appendDataSimply\n    }, _a[SOURCE_FORMAT_TYPED_ARRAY] = {\n      persistent: false,\n      pure: true,\n      appendData: function (newData) {\n        if (\"development\" !== 'production') {\n          assert(isTypedArray(newData), 'Added data must be TypedArray if data in initialization is TypedArray');\n        }\n\n        this._data = newData;\n      },\n      // Clean self if data is already used.\n      clean: function () {\n        // PENDING\n        this._offset += this.count();\n        this._data = null;\n      }\n    }, _a);\n\n    function appendDataSimply(newData) {\n      for (var i = 0; i < newData.length; i++) {\n        this._data.push(newData[i]);\n      }\n    }\n  }();\n\n  return DefaultDataProvider;\n}();\n\nvar getItemSimply = function (rawData, startIndex, dimsDef, idx) {\n  return rawData[idx];\n};\n\nvar rawSourceItemGetterMap = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef, idx) {\n  return rawData[idx + startIndex];\n}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx, out) {\n  idx += startIndex;\n  var item = out || [];\n  var data = rawData;\n\n  for (var i = 0; i < data.length; i++) {\n    var row = data[i];\n    item[i] = row ? row[idx] : null;\n  }\n\n  return item;\n}, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx, out) {\n  var item = out || [];\n\n  for (var i = 0; i < dimsDef.length; i++) {\n    var dimName = dimsDef[i].name;\n\n    if (\"development\" !== 'production') {\n      if (dimName == null) {\n        throw new Error();\n      }\n    }\n\n    var col = rawData[dimName];\n    item[i] = col ? col[idx] : null;\n  }\n\n  return item;\n}, _a[SOURCE_FORMAT_ORIGINAL] = getItemSimply, _a);\nfunction getRawSourceItemGetter(sourceFormat, seriesLayoutBy) {\n  var method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n  if (\"development\" !== 'production') {\n    assert(method, 'Do not support get item on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n  }\n\n  return method;\n}\n\nvar countSimply = function (rawData, startIndex, dimsDef) {\n  return rawData.length;\n};\n\nvar rawSourceDataCounterMap = (_b = {}, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef) {\n  return Math.max(0, rawData.length - startIndex);\n}, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef) {\n  var row = rawData[0];\n  return row ? Math.max(0, row.length - startIndex) : 0;\n}, _b[SOURCE_FORMAT_OBJECT_ROWS] = countSimply, _b[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef) {\n  var dimName = dimsDef[0].name;\n\n  if (\"development\" !== 'production') {\n    if (dimName == null) {\n      throw new Error();\n    }\n  }\n\n  var col = rawData[dimName];\n  return col ? col.length : 0;\n}, _b[SOURCE_FORMAT_ORIGINAL] = countSimply, _b);\nfunction getRawSourceDataCounter(sourceFormat, seriesLayoutBy) {\n  var method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n  if (\"development\" !== 'production') {\n    assert(method, 'Do not support count on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n  }\n\n  return method;\n}\n\nvar getRawValueSimply = function (dataItem, dimIndex, property) {\n  return dataItem[dimIndex];\n};\n\nvar rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, property) {\n  return dataItem[property];\n}, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, property) {\n  // FIXME: In some case (markpoint in geo (geo-map.html)),\n  // dataItem is {coord: [...]}\n  var value = getDataItemValue(dataItem);\n  return !(value instanceof Array) ? value : value[dimIndex];\n}, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c);\nfunction getRawSourceValueGetter(sourceFormat) {\n  var method = rawSourceValueGetterMap[sourceFormat];\n\n  if (\"development\" !== 'production') {\n    assert(method, 'Do not support get value on \"' + sourceFormat + '\".');\n  }\n\n  return method;\n}\n\nfunction getMethodMapKey(sourceFormat, seriesLayoutBy) {\n  return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + seriesLayoutBy : sourceFormat;\n} // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,\n// Consider persistent.\n// Caution: why use raw value to display on label or tooltip?\n// A reason is to avoid format. For example time value we do not know\n// how to format is expected. More over, if stack is used, calculated\n// value may be 0.91000000001, which have brings trouble to display.\n// TODO: consider how to treat null/undefined/NaN when display?\n\n\nfunction retrieveRawValue(data, dataIndex, // If dimIndex is null/undefined, return OptionDataItem.\n// Otherwise, return OptionDataValue.\ndim) {\n  if (!data) {\n    return;\n  } // Consider data may be not persistent.\n\n\n  var dataItem = data.getRawDataItem(dataIndex);\n\n  if (dataItem == null) {\n    return;\n  }\n\n  var store = data.getStore();\n  var sourceFormat = store.getSource().sourceFormat;\n\n  if (dim != null) {\n    var dimIndex = data.getDimensionIndex(dim);\n    var property = store.getDimensionProperty(dimIndex);\n    return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, property);\n  } else {\n    var result = dataItem;\n\n    if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n      result = getDataItemValue(dataItem);\n    }\n\n    return result;\n  }\n}\n\nvar DIMENSION_LABEL_REG = /\\{@(.+?)\\}/g;\n\nvar DataFormatMixin =\n/** @class */\nfunction () {\n  function DataFormatMixin() {}\n  /**\n   * Get params for formatter\n   */\n\n\n  DataFormatMixin.prototype.getDataParams = function (dataIndex, dataType) {\n    var data = this.getData(dataType);\n    var rawValue = this.getRawValue(dataIndex, dataType);\n    var rawDataIndex = data.getRawIndex(dataIndex);\n    var name = data.getName(dataIndex);\n    var itemOpt = data.getRawDataItem(dataIndex);\n    var style = data.getItemVisual(dataIndex, 'style');\n    var color = style && style[data.getItemVisual(dataIndex, 'drawType') || 'fill'];\n    var borderColor = style && style.stroke;\n    var mainType = this.mainType;\n    var isSeries = mainType === 'series';\n    var userOutput = data.userOutput && data.userOutput.get();\n    return {\n      componentType: mainType,\n      componentSubType: this.subType,\n      componentIndex: this.componentIndex,\n      seriesType: isSeries ? this.subType : null,\n      seriesIndex: this.seriesIndex,\n      seriesId: isSeries ? this.id : null,\n      seriesName: isSeries ? this.name : null,\n      name: name,\n      dataIndex: rawDataIndex,\n      data: itemOpt,\n      dataType: dataType,\n      value: rawValue,\n      color: color,\n      borderColor: borderColor,\n      dimensionNames: userOutput ? userOutput.fullDimensions : null,\n      encode: userOutput ? userOutput.encode : null,\n      // Param name list for mapping `a`, `b`, `c`, `d`, `e`\n      $vars: ['seriesName', 'name', 'value']\n    };\n  };\n  /**\n   * Format label\n   * @param dataIndex\n   * @param status 'normal' by default\n   * @param dataType\n   * @param labelDimIndex Only used in some chart that\n   *        use formatter in different dimensions, like radar.\n   * @param formatter Formatter given outside.\n   * @return return null/undefined if no formatter\n   */\n\n\n  DataFormatMixin.prototype.getFormattedLabel = function (dataIndex, status, dataType, labelDimIndex, formatter, extendParams) {\n    status = status || 'normal';\n    var data = this.getData(dataType);\n    var params = this.getDataParams(dataIndex, dataType);\n\n    if (extendParams) {\n      params.value = extendParams.interpolatedValue;\n    }\n\n    if (labelDimIndex != null && isArray(params.value)) {\n      params.value = params.value[labelDimIndex];\n    }\n\n    if (!formatter) {\n      var itemModel = data.getItemModel(dataIndex); // @ts-ignore\n\n      formatter = itemModel.get(status === 'normal' ? ['label', 'formatter'] : [status, 'label', 'formatter']);\n    }\n\n    if (isFunction(formatter)) {\n      params.status = status;\n      params.dimensionIndex = labelDimIndex;\n      return formatter(params);\n    } else if (isString(formatter)) {\n      var str = formatTpl(formatter, params); // Support 'aaa{@[3]}bbb{@product}ccc'.\n      // Do not support '}' in dim name util have to.\n\n      return str.replace(DIMENSION_LABEL_REG, function (origin, dimStr) {\n        var len = dimStr.length;\n        var dimLoose = dimStr;\n\n        if (dimLoose.charAt(0) === '[' && dimLoose.charAt(len - 1) === ']') {\n          dimLoose = +dimLoose.slice(1, len - 1); // Also support: '[]' => 0\n\n          if (\"development\" !== 'production') {\n            if (isNaN(dimLoose)) {\n              error(\"Invalide label formatter: @\" + dimStr + \", only support @[0], @[1], @[2], ...\");\n            }\n          }\n        }\n\n        var val = retrieveRawValue(data, dataIndex, dimLoose);\n\n        if (extendParams && isArray(extendParams.interpolatedValue)) {\n          var dimIndex = data.getDimensionIndex(dimLoose);\n\n          if (dimIndex >= 0) {\n            val = extendParams.interpolatedValue[dimIndex];\n          }\n        }\n\n        return val != null ? val + '' : '';\n      });\n    }\n  };\n  /**\n   * Get raw value in option\n   */\n\n\n  DataFormatMixin.prototype.getRawValue = function (idx, dataType) {\n    return retrieveRawValue(this.getData(dataType), idx);\n  };\n  /**\n   * Should be implemented.\n   * @param {number} dataIndex\n   * @param {boolean} [multipleSeries=false]\n   * @param {string} [dataType]\n   */\n\n\n  DataFormatMixin.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    // Empty function\n    return;\n  };\n\n  return DataFormatMixin;\n}();\n// but guess little chance has been used outside. Do we need to backward\n// compat it?\n// type TooltipFormatResultLegacyObject = {\n//     // `html` means the markup language text, either in 'html' or 'richText'.\n//     // The name `html` is not appropriate because in 'richText' it is not a HTML\n//     // string. But still support it for backward compatibility.\n//     html: string;\n//     markers: Dictionary<ColorString>;\n// };\n\n/**\n * For backward compat, normalize the return from `formatTooltip`.\n */\n\nfunction normalizeTooltipFormatResult(result) {\n  var markupText; // let markers: Dictionary<ColorString>;\n\n  var markupFragment;\n\n  if (isObject(result)) {\n    if (result.type) {\n      markupFragment = result;\n    } else {\n      if (\"development\" !== 'production') {\n        console.warn('The return type of `formatTooltip` is not supported: ' + makePrintable(result));\n      }\n    } // else {\n    //     markupText = (result as TooltipFormatResultLegacyObject).html;\n    //     markers = (result as TooltipFormatResultLegacyObject).markers;\n    //     if (markersExisting) {\n    //         markers = zrUtil.merge(markersExisting, markers);\n    //     }\n    // }\n\n  } else {\n    markupText = result;\n  }\n\n  return {\n    text: markupText,\n    // markers: markers || markersExisting,\n    frag: markupFragment\n  };\n}\n\n/**\n * @param {Object} define\n * @return See the return of `createTask`.\n */\n\nfunction createTask(define) {\n  return new Task(define);\n}\n\nvar Task =\n/** @class */\nfunction () {\n  function Task(define) {\n    define = define || {};\n    this._reset = define.reset;\n    this._plan = define.plan;\n    this._count = define.count;\n    this._onDirty = define.onDirty;\n    this._dirty = true;\n  }\n  /**\n   * @param step Specified step.\n   * @param skip Skip customer perform call.\n   * @param modBy Sampling window size.\n   * @param modDataCount Sampling count.\n   * @return whether unfinished.\n   */\n\n\n  Task.prototype.perform = function (performArgs) {\n    var upTask = this._upstream;\n    var skip = performArgs && performArgs.skip; // TODO some refactor.\n    // Pull data. Must pull data each time, because context.data\n    // may be updated by Series.setData.\n\n    if (this._dirty && upTask) {\n      var context = this.context;\n      context.data = context.outputData = upTask.context.outputData;\n    }\n\n    if (this.__pipeline) {\n      this.__pipeline.currentTask = this;\n    }\n\n    var planResult;\n\n    if (this._plan && !skip) {\n      planResult = this._plan(this.context);\n    } // Support sharding by mod, which changes the render sequence and makes the rendered graphic\n    // elements uniformed distributed when progress, especially when moving or zooming.\n\n\n    var lastModBy = normalizeModBy(this._modBy);\n    var lastModDataCount = this._modDataCount || 0;\n    var modBy = normalizeModBy(performArgs && performArgs.modBy);\n    var modDataCount = performArgs && performArgs.modDataCount || 0;\n\n    if (lastModBy !== modBy || lastModDataCount !== modDataCount) {\n      planResult = 'reset';\n    }\n\n    function normalizeModBy(val) {\n      !(val >= 1) && (val = 1); // jshint ignore:line\n\n      return val;\n    }\n\n    var forceFirstProgress;\n\n    if (this._dirty || planResult === 'reset') {\n      this._dirty = false;\n      forceFirstProgress = this._doReset(skip);\n    }\n\n    this._modBy = modBy;\n    this._modDataCount = modDataCount;\n    var step = performArgs && performArgs.step;\n\n    if (upTask) {\n      if (\"development\" !== 'production') {\n        assert(upTask._outputDueEnd != null);\n      }\n\n      this._dueEnd = upTask._outputDueEnd;\n    } // DataTask or overallTask\n    else {\n        if (\"development\" !== 'production') {\n          assert(!this._progress || this._count);\n        }\n\n        this._dueEnd = this._count ? this._count(this.context) : Infinity;\n      } // Note: Stubs, that its host overall task let it has progress, has progress.\n    // If no progress, pass index from upstream to downstream each time plan called.\n\n\n    if (this._progress) {\n      var start = this._dueIndex;\n      var end = Math.min(step != null ? this._dueIndex + step : Infinity, this._dueEnd);\n\n      if (!skip && (forceFirstProgress || start < end)) {\n        var progress = this._progress;\n\n        if (isArray(progress)) {\n          for (var i = 0; i < progress.length; i++) {\n            this._doProgress(progress[i], start, end, modBy, modDataCount);\n          }\n        } else {\n          this._doProgress(progress, start, end, modBy, modDataCount);\n        }\n      }\n\n      this._dueIndex = end; // If no `outputDueEnd`, assume that output data and\n      // input data is the same, so use `dueIndex` as `outputDueEnd`.\n\n      var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end;\n\n      if (\"development\" !== 'production') {\n        // ??? Can not rollback.\n        assert(outputDueEnd >= this._outputDueEnd);\n      }\n\n      this._outputDueEnd = outputDueEnd;\n    } else {\n      // (1) Some overall task has no progress.\n      // (2) Stubs, that its host overall task do not let it has progress, has no progress.\n      // This should always be performed so it can be passed to downstream.\n      this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd;\n    }\n\n    return this.unfinished();\n  };\n\n  Task.prototype.dirty = function () {\n    this._dirty = true;\n    this._onDirty && this._onDirty(this.context);\n  };\n\n  Task.prototype._doProgress = function (progress, start, end, modBy, modDataCount) {\n    iterator.reset(start, end, modBy, modDataCount);\n    this._callingProgress = progress;\n\n    this._callingProgress({\n      start: start,\n      end: end,\n      count: end - start,\n      next: iterator.next\n    }, this.context);\n  };\n\n  Task.prototype._doReset = function (skip) {\n    this._dueIndex = this._outputDueEnd = this._dueEnd = 0;\n    this._settedOutputEnd = null;\n    var progress;\n    var forceFirstProgress;\n\n    if (!skip && this._reset) {\n      progress = this._reset(this.context);\n\n      if (progress && progress.progress) {\n        forceFirstProgress = progress.forceFirstProgress;\n        progress = progress.progress;\n      } // To simplify no progress checking, array must has item.\n\n\n      if (isArray(progress) && !progress.length) {\n        progress = null;\n      }\n    }\n\n    this._progress = progress;\n    this._modBy = this._modDataCount = null;\n    var downstream = this._downstream;\n    downstream && downstream.dirty();\n    return forceFirstProgress;\n  };\n\n  Task.prototype.unfinished = function () {\n    return this._progress && this._dueIndex < this._dueEnd;\n  };\n  /**\n   * @param downTask The downstream task.\n   * @return The downstream task.\n   */\n\n\n  Task.prototype.pipe = function (downTask) {\n    if (\"development\" !== 'production') {\n      assert(downTask && !downTask._disposed && downTask !== this);\n    } // If already downstream, do not dirty downTask.\n\n\n    if (this._downstream !== downTask || this._dirty) {\n      this._downstream = downTask;\n      downTask._upstream = this;\n      downTask.dirty();\n    }\n  };\n\n  Task.prototype.dispose = function () {\n    if (this._disposed) {\n      return;\n    }\n\n    this._upstream && (this._upstream._downstream = null);\n    this._downstream && (this._downstream._upstream = null);\n    this._dirty = false;\n    this._disposed = true;\n  };\n\n  Task.prototype.getUpstream = function () {\n    return this._upstream;\n  };\n\n  Task.prototype.getDownstream = function () {\n    return this._downstream;\n  };\n\n  Task.prototype.setOutputEnd = function (end) {\n    // This only happens in dataTask, dataZoom, map, currently.\n    // where dataZoom do not set end each time, but only set\n    // when reset. So we should record the set end, in case\n    // that the stub of dataZoom perform again and earse the\n    // set end by upstream.\n    this._outputDueEnd = this._settedOutputEnd = end;\n  };\n\n  return Task;\n}();\n\nvar iterator = function () {\n  var end;\n  var current;\n  var modBy;\n  var modDataCount;\n  var winCount;\n  var it = {\n    reset: function (s, e, sStep, sCount) {\n      current = s;\n      end = e;\n      modBy = sStep;\n      modDataCount = sCount;\n      winCount = Math.ceil(modDataCount / modBy);\n      it.next = modBy > 1 && modDataCount > 0 ? modNext : sequentialNext;\n    }\n  };\n  return it;\n\n  function sequentialNext() {\n    return current < end ? current++ : null;\n  }\n\n  function modNext() {\n    var dataIndex = current % winCount * modBy + Math.ceil(current / winCount);\n    var result = current >= end ? null : dataIndex < modDataCount ? dataIndex // If modDataCount is smaller than data.count() (consider `appendData` case),\n    // Use normal linear rendering mode.\n    : current;\n    current++;\n    return result;\n  }\n}(); // -----------------------------------------------------------------------------\n// For stream debug (Should be commented out after used!)\n// @usage: printTask(this, 'begin');\n// @usage: printTask(this, null, {someExtraProp});\n// @usage: Use `__idxInPipeline` as conditional breakpiont.\n//\n// window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void {\n//     window.ecTaskUID == null && (window.ecTaskUID = 0);\n//     task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);\n//     task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);\n//     let props = [];\n//     if (task.__pipeline) {\n//         let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;\n//         props.push({text: '__idxInPipeline/total', value: val});\n//     } else {\n//         let stubCount = 0;\n//         task.agentStubMap.each(() => stubCount++);\n//         props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});\n//     }\n//     props.push({text: 'uid', value: task.uidDebug});\n//     if (task.__pipeline) {\n//         props.push({text: 'pipelineId', value: task.__pipeline.id});\n//         task.agent && props.push(\n//             {text: 'stubFor', value: task.agent.uidDebug}\n//         );\n//     }\n//     props.push(\n//         {text: 'dirty', value: task._dirty},\n//         {text: 'dueIndex', value: task._dueIndex},\n//         {text: 'dueEnd', value: task._dueEnd},\n//         {text: 'outputDueEnd', value: task._outputDueEnd}\n//     );\n//     if (extra) {\n//         Object.keys(extra).forEach(key => {\n//             props.push({text: key, value: extra[key]});\n//         });\n//     }\n//     let args = ['color: blue'];\n//     let msg = `%c[${prefix || 'T'}] %c` + props.map(item => (\n//         args.push('color: green', 'color: red'),\n//         `${item.text}: %c${item.value}`\n//     )).join('%c, ');\n//     console.log.apply(console, [msg].concat(args));\n//     // console.log(this);\n// };\n// window.printPipeline = function (task: any, prefix: string) {\n//     const pipeline = task.__pipeline;\n//     let currTask = pipeline.head;\n//     while (currTask) {\n//         window.printTask(currTask, prefix);\n//         currTask = currTask._downstream;\n//     }\n// };\n// window.showChain = function (chainHeadTask) {\n//     var chain = [];\n//     var task = chainHeadTask;\n//     while (task) {\n//         chain.push({\n//             task: task,\n//             up: task._upstream,\n//             down: task._downstream,\n//             idxInPipeline: task.__idxInPipeline\n//         });\n//         task = task._downstream;\n//     }\n//     return chain;\n// };\n// window.findTaskInChain = function (task, chainHeadTask) {\n//     let chain = window.showChain(chainHeadTask);\n//     let result = [];\n//     for (let i = 0; i < chain.length; i++) {\n//         let chainItem = chain[i];\n//         if (chainItem.task === task) {\n//             result.push(i);\n//         }\n//     }\n//     return result;\n// };\n// window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) {\n//     let chainA = window.showChain(chainHeadTaskA);\n//     for (let i = 0; i < chainA.length; i++) {\n//         console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB));\n//     }\n// };\n\n/**\n * Convert raw the value in to inner value in List.\n *\n * [Performance sensitive]\n *\n * [Caution]: this is the key logic of user value parser.\n * For backward compatibility, do not modify it until you have to!\n */\n\nfunction parseDataValue(value, // For high performance, do not omit the second param.\nopt) {\n  // Performance sensitive.\n  var dimType = opt && opt.type;\n\n  if (dimType === 'ordinal') {\n    // If given value is a category string\n    return value;\n  }\n\n  if (dimType === 'time' // spead up when using timestamp\n  && !isNumber(value) && value != null && value !== '-') {\n    value = +parseDate(value);\n  } // dimType defaults 'number'.\n  // If dimType is not ordinal and value is null or undefined or NaN or '-',\n  // parse to NaN.\n  // number-like string (like ' 123 ') can be converted to a number.\n  // where null/undefined or other string will be converted to NaN.\n\n\n  return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN\n  // If object, also parse to NaN\n  : +value;\n}\nvar valueParserMap = createHashMap({\n  'number': function (val) {\n    // Do not use `numericToNumber` here. We have `numericToNumber` by default.\n    // Here the number parser can have loose rule:\n    // enable to cut suffix: \"120px\" => 120, \"14%\" => 14.\n    return parseFloat(val);\n  },\n  'time': function (val) {\n    // return timestamp.\n    return +parseDate(val);\n  },\n  'trim': function (val) {\n    return isString(val) ? trim(val) : val;\n  }\n});\nfunction getRawValueParser(type) {\n  return valueParserMap.get(type);\n}\nvar ORDER_COMPARISON_OP_MAP = {\n  lt: function (lval, rval) {\n    return lval < rval;\n  },\n  lte: function (lval, rval) {\n    return lval <= rval;\n  },\n  gt: function (lval, rval) {\n    return lval > rval;\n  },\n  gte: function (lval, rval) {\n    return lval >= rval;\n  }\n};\n\nvar FilterOrderComparator =\n/** @class */\nfunction () {\n  function FilterOrderComparator(op, rval) {\n    if (!isNumber(rval)) {\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        errMsg = 'rvalue of \"<\", \">\", \"<=\", \">=\" can only be number in filter.';\n      }\n\n      throwError(errMsg);\n    }\n\n    this._opFn = ORDER_COMPARISON_OP_MAP[op];\n    this._rvalFloat = numericToNumber(rval);\n  } // Performance sensitive.\n\n\n  FilterOrderComparator.prototype.evaluate = function (lval) {\n    // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.\n    return isNumber(lval) ? this._opFn(lval, this._rvalFloat) : this._opFn(numericToNumber(lval), this._rvalFloat);\n  };\n\n  return FilterOrderComparator;\n}();\n\nvar SortOrderComparator =\n/** @class */\nfunction () {\n  /**\n   * @param order by default: 'asc'\n   * @param incomparable by default: Always on the tail.\n   *        That is, if 'asc' => 'max', if 'desc' => 'min'\n   *        See the definition of \"incomparable\" in [SORT_COMPARISON_RULE].\n   */\n  function SortOrderComparator(order, incomparable) {\n    var isDesc = order === 'desc';\n    this._resultLT = isDesc ? 1 : -1;\n\n    if (incomparable == null) {\n      incomparable = isDesc ? 'min' : 'max';\n    }\n\n    this._incomparable = incomparable === 'min' ? -Infinity : Infinity;\n  } // See [SORT_COMPARISON_RULE].\n  // Performance sensitive.\n\n\n  SortOrderComparator.prototype.evaluate = function (lval, rval) {\n    // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.\n    var lvalFloat = isNumber(lval) ? lval : numericToNumber(lval);\n    var rvalFloat = isNumber(rval) ? rval : numericToNumber(rval);\n    var lvalNotNumeric = isNaN(lvalFloat);\n    var rvalNotNumeric = isNaN(rvalFloat);\n\n    if (lvalNotNumeric) {\n      lvalFloat = this._incomparable;\n    }\n\n    if (rvalNotNumeric) {\n      rvalFloat = this._incomparable;\n    }\n\n    if (lvalNotNumeric && rvalNotNumeric) {\n      var lvalIsStr = isString(lval);\n      var rvalIsStr = isString(rval);\n\n      if (lvalIsStr) {\n        lvalFloat = rvalIsStr ? lval : 0;\n      }\n\n      if (rvalIsStr) {\n        rvalFloat = lvalIsStr ? rval : 0;\n      }\n    }\n\n    return lvalFloat < rvalFloat ? this._resultLT : lvalFloat > rvalFloat ? -this._resultLT : 0;\n  };\n\n  return SortOrderComparator;\n}();\n\nvar FilterEqualityComparator =\n/** @class */\nfunction () {\n  function FilterEqualityComparator(isEq, rval) {\n    this._rval = rval;\n    this._isEQ = isEq;\n    this._rvalTypeof = typeof rval;\n    this._rvalFloat = numericToNumber(rval);\n  } // Performance sensitive.\n\n\n  FilterEqualityComparator.prototype.evaluate = function (lval) {\n    var eqResult = lval === this._rval;\n\n    if (!eqResult) {\n      var lvalTypeof = typeof lval;\n\n      if (lvalTypeof !== this._rvalTypeof && (lvalTypeof === 'number' || this._rvalTypeof === 'number')) {\n        eqResult = numericToNumber(lval) === this._rvalFloat;\n      }\n    }\n\n    return this._isEQ ? eqResult : !eqResult;\n  };\n\n  return FilterEqualityComparator;\n}();\n/**\n * [FILTER_COMPARISON_RULE]\n * `lt`|`lte`|`gt`|`gte`:\n * + rval must be a number. And lval will be converted to number (`numericToNumber`) to compare.\n * `eq`:\n * + If same type, compare with `===`.\n * + If there is one number, convert to number (`numericToNumber`) to compare.\n * + Else return `false`.\n * `ne`:\n * + Not `eq`.\n *\n *\n * [SORT_COMPARISON_RULE]\n * All the values are grouped into three categories:\n * + \"numeric\" (number and numeric string)\n * + \"non-numeric-string\" (string that excluding numeric string)\n * + \"others\"\n * \"numeric\" vs \"numeric\": values are ordered by number order.\n * \"non-numeric-string\" vs \"non-numeric-string\": values are ordered by ES spec (#sec-abstract-relational-comparison).\n * \"others\" vs \"others\": do not change order (always return 0).\n * \"numeric\" vs \"non-numeric-string\": \"non-numeric-string\" is treated as \"incomparable\".\n * \"number\" vs \"others\": \"others\" is treated as \"incomparable\".\n * \"non-numeric-string\" vs \"others\": \"others\" is treated as \"incomparable\".\n * \"incomparable\" will be seen as -Infinity or Infinity (depends on the settings).\n * MEMO:\n *   Non-numeric string sort makes sense when we need to put the items with the same tag together.\n *   But if we support string sort, we still need to avoid the misleading like `'2' > '12'`,\n *   So we treat \"numeric-string\" sorted by number order rather than string comparison.\n *\n *\n * [CHECK_LIST_OF_THE_RULE_DESIGN]\n * + Do not support string comparison until required. And also need to\n *   avoid the misleading of \"2\" > \"12\".\n * + Should avoid the misleading case:\n *   `\" 22 \" gte \"22\"` is `true` but `\" 22 \" eq \"22\"` is `false`.\n * + JS bad case should be avoided: null <= 0, [] <= 0, ' ' <= 0, ...\n * + Only \"numeric\" can be converted to comparable number, otherwise converted to NaN.\n *   See `util/number.ts#numericToNumber`.\n *\n * @return If `op` is not `RelationalOperator`, return null;\n */\n\n\nfunction createFilterComparator(op, rval) {\n  return op === 'eq' || op === 'ne' ? new FilterEqualityComparator(op === 'eq', rval) : hasOwn(ORDER_COMPARISON_OP_MAP, op) ? new FilterOrderComparator(op, rval) : null;\n}\n\n/**\n * TODO: disable writable.\n * This structure will be exposed to users.\n */\n\nvar ExternalSource =\n/** @class */\nfunction () {\n  function ExternalSource() {}\n\n  ExternalSource.prototype.getRawData = function () {\n    // Only built-in transform available.\n    throw new Error('not supported');\n  };\n\n  ExternalSource.prototype.getRawDataItem = function (dataIndex) {\n    // Only built-in transform available.\n    throw new Error('not supported');\n  };\n\n  ExternalSource.prototype.cloneRawData = function () {\n    return;\n  };\n  /**\n   * @return If dimension not found, return null/undefined.\n   */\n\n\n  ExternalSource.prototype.getDimensionInfo = function (dim) {\n    return;\n  };\n  /**\n   * dimensions defined if and only if either:\n   * (a) dataset.dimensions are declared.\n   * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).\n   * If dimensions are defined, `dimensionInfoAll` is corresponding to\n   * the defined dimensions.\n   * Otherwise, `dimensionInfoAll` is determined by data columns.\n   * @return Always return an array (even empty array).\n   */\n\n\n  ExternalSource.prototype.cloneAllDimensionInfo = function () {\n    return;\n  };\n\n  ExternalSource.prototype.count = function () {\n    return;\n  };\n  /**\n   * Only support by dimension index.\n   * No need to support by dimension name in transform function,\n   * because transform function is not case-specific, no need to use name literally.\n   */\n\n\n  ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {\n    return;\n  };\n\n  ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {\n    return;\n  };\n\n  ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {\n    return parseDataValue(rawVal, dimInfo);\n  };\n\n  return ExternalSource;\n}();\n\nfunction createExternalSource(internalSource, externalTransform) {\n  var extSource = new ExternalSource();\n  var data = internalSource.data;\n  var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;\n  var sourceHeaderCount = internalSource.startIndex;\n  var errMsg = '';\n\n  if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {\n    // For the logic simplicity in transformer, only 'culumn' is\n    // supported in data transform. Otherwise, the `dimensionsDefine`\n    // might be detected by 'row', which probably confuses users.\n    if (\"development\" !== 'production') {\n      errMsg = '`seriesLayoutBy` of upstream dataset can only be \"column\" in data transform.';\n    }\n\n    throwError(errMsg);\n  } // [MEMO]\n  // Create a new dimensions structure for exposing.\n  // Do not expose all dimension info to users directly.\n  // Because the dimension is probably auto detected from data and not might reliable.\n  // Should not lead the transformers to think that is reliable and return it.\n  // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n\n\n  var dimensions = [];\n  var dimsByName = {};\n  var dimsDef = internalSource.dimensionsDefine;\n\n  if (dimsDef) {\n    each(dimsDef, function (dimDef, idx) {\n      var name = dimDef.name;\n      var dimDefExt = {\n        index: idx,\n        name: name,\n        displayName: dimDef.displayName\n      };\n      dimensions.push(dimDefExt); // Users probably do not specify dimension name. For simplicity, data transform\n      // does not generate dimension name.\n\n      if (name != null) {\n        // Dimension name should not be duplicated.\n        // For simplicity, data transform forbids name duplication, do not generate\n        // new name like module `completeDimensions.ts` did, but just tell users.\n        var errMsg_1 = '';\n\n        if (hasOwn(dimsByName, name)) {\n          if (\"development\" !== 'production') {\n            errMsg_1 = 'dimension name \"' + name + '\" duplicated.';\n          }\n\n          throwError(errMsg_1);\n        }\n\n        dimsByName[name] = dimDefExt;\n      }\n    });\n  } // If dimension definitions are not defined and can not be detected.\n  // e.g., pure data `[[11, 22], ...]`.\n  else {\n      for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {\n        // Do not generete name or anything others. The consequence process in\n        // `transform` or `series` probably have there own name generation strategry.\n        dimensions.push({\n          index: i\n        });\n      }\n    } // Implement public methods:\n\n\n  var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n\n  if (externalTransform.__isBuiltIn) {\n    extSource.getRawDataItem = function (dataIndex) {\n      return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n    };\n\n    extSource.getRawData = bind(getRawData, null, internalSource);\n  }\n\n  extSource.cloneRawData = bind(cloneRawData, null, internalSource);\n  var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n  extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);\n  var rawValueGetter = getRawSourceValueGetter(sourceFormat);\n\n  extSource.retrieveValue = function (dataIndex, dimIndex) {\n    var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n    return retrieveValueFromItem(rawItem, dimIndex);\n  };\n\n  var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {\n    if (dataItem == null) {\n      return;\n    }\n\n    var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item.\n\n    if (dimDef) {\n      return rawValueGetter(dataItem, dimIndex, dimDef.name);\n    }\n  };\n\n  extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);\n  extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);\n  return extSource;\n}\n\nfunction getRawData(upstream) {\n  var sourceFormat = upstream.sourceFormat;\n\n  if (!isSupportedSourceFormat(sourceFormat)) {\n    var errMsg = '';\n\n    if (\"development\" !== 'production') {\n      errMsg = '`getRawData` is not supported in source format ' + sourceFormat;\n    }\n\n    throwError(errMsg);\n  }\n\n  return upstream.data;\n}\n\nfunction cloneRawData(upstream) {\n  var sourceFormat = upstream.sourceFormat;\n  var data = upstream.data;\n\n  if (!isSupportedSourceFormat(sourceFormat)) {\n    var errMsg = '';\n\n    if (\"development\" !== 'production') {\n      errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;\n    }\n\n    throwError(errMsg);\n  }\n\n  if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n    var result = [];\n\n    for (var i = 0, len = data.length; i < len; i++) {\n      // Not strictly clone for performance\n      result.push(data[i].slice());\n    }\n\n    return result;\n  } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n    var result = [];\n\n    for (var i = 0, len = data.length; i < len; i++) {\n      // Not strictly clone for performance\n      result.push(extend({}, data[i]));\n    }\n\n    return result;\n  }\n}\n\nfunction getDimensionInfo(dimensions, dimsByName, dim) {\n  if (dim == null) {\n    return;\n  } // Keep the same logic as `List::getDimension` did.\n\n\n  if (isNumber(dim) // If being a number-like string but not being defined a dimension name.\n  || !isNaN(dim) && !hasOwn(dimsByName, dim)) {\n    return dimensions[dim];\n  } else if (hasOwn(dimsByName, dim)) {\n    return dimsByName[dim];\n  }\n}\n\nfunction cloneAllDimensionInfo(dimensions) {\n  return clone(dimensions);\n}\n\nvar externalTransformMap = createHashMap();\nfunction registerExternalTransform(externalTransform) {\n  externalTransform = clone(externalTransform);\n  var type = externalTransform.type;\n  var errMsg = '';\n\n  if (!type) {\n    if (\"development\" !== 'production') {\n      errMsg = 'Must have a `type` when `registerTransform`.';\n    }\n\n    throwError(errMsg);\n  }\n\n  var typeParsed = type.split(':');\n\n  if (typeParsed.length !== 2) {\n    if (\"development\" !== 'production') {\n      errMsg = 'Name must include namespace like \"ns:regression\".';\n    }\n\n    throwError(errMsg);\n  } // Namespace 'echarts:xxx' is official namespace, where the transforms should\n  // be called directly via 'xxx' rather than 'echarts:xxx'.\n\n\n  var isBuiltIn = false;\n\n  if (typeParsed[0] === 'echarts') {\n    type = typeParsed[1];\n    isBuiltIn = true;\n  }\n\n  externalTransform.__isBuiltIn = isBuiltIn;\n  externalTransformMap.set(type, externalTransform);\n}\nfunction applyDataTransform(rawTransOption, sourceList, infoForPrint) {\n  var pipedTransOption = normalizeToArray(rawTransOption);\n  var pipeLen = pipedTransOption.length;\n  var errMsg = '';\n\n  if (!pipeLen) {\n    if (\"development\" !== 'production') {\n      errMsg = 'If `transform` declared, it should at least contain one transform.';\n    }\n\n    throwError(errMsg);\n  }\n\n  for (var i = 0, len = pipeLen; i < len; i++) {\n    var transOption = pipedTransOption[i];\n    sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one.\n    // piped transform only support single output, except the last one.\n\n    if (i !== len - 1) {\n      sourceList.length = Math.max(sourceList.length, 1);\n    }\n  }\n\n  return sourceList;\n}\n\nfunction applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform.\npipeIndex) {\n  var errMsg = '';\n\n  if (!upSourceList.length) {\n    if (\"development\" !== 'production') {\n      errMsg = 'Must have at least one upstream dataset.';\n    }\n\n    throwError(errMsg);\n  }\n\n  if (!isObject(transOption)) {\n    if (\"development\" !== 'production') {\n      errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';\n    }\n\n    throwError(errMsg);\n  }\n\n  var transType = transOption.type;\n  var externalTransform = externalTransformMap.get(transType);\n\n  if (!externalTransform) {\n    if (\"development\" !== 'production') {\n      errMsg = 'Can not find transform on type \"' + transType + '\".';\n    }\n\n    throwError(errMsg);\n  } // Prepare source\n\n\n  var extUpSourceList = map(upSourceList, function (upSource) {\n    return createExternalSource(upSource, externalTransform);\n  });\n  var resultList = normalizeToArray(externalTransform.transform({\n    upstream: extUpSourceList[0],\n    upstreamList: extUpSourceList,\n    config: clone(transOption.config)\n  }));\n\n  if (\"development\" !== 'production') {\n    if (transOption.print) {\n      var printStrArr = map(resultList, function (extSource) {\n        var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';\n        return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\\n');\n      }).join('\\n');\n      log(printStrArr);\n    }\n  }\n\n  return map(resultList, function (result, resultIndex) {\n    var errMsg = '';\n\n    if (!isObject(result)) {\n      if (\"development\" !== 'production') {\n        errMsg = 'A transform should not return some empty results.';\n      }\n\n      throwError(errMsg);\n    }\n\n    if (!result.data) {\n      if (\"development\" !== 'production') {\n        errMsg = 'Transform result data should be not be null or undefined';\n      }\n\n      throwError(errMsg);\n    }\n\n    var sourceFormat = detectSourceFormat(result.data);\n\n    if (!isSupportedSourceFormat(sourceFormat)) {\n      if (\"development\" !== 'production') {\n        errMsg = 'Transform result data should be array rows or object rows.';\n      }\n\n      throwError(errMsg);\n    }\n\n    var resultMetaRawOption;\n    var firstUpSource = upSourceList[0];\n    /**\n     * Intuitively, the end users known the content of the original `dataset.source`,\n     * calucating the transform result in mind.\n     * Suppose the original `dataset.source` is:\n     * ```js\n     * [\n     *     ['product', '2012', '2013', '2014', '2015'],\n     *     ['AAA', 41.1, 30.4, 65.1, 53.3],\n     *     ['BBB', 86.5, 92.1, 85.7, 83.1],\n     *     ['CCC', 24.1, 67.2, 79.5, 86.4]\n     * ]\n     * ```\n     * The dimension info have to be detected from the source data.\n     * Some of the transformers (like filter, sort) will follow the dimension info\n     * of upstream, while others use new dimensions (like aggregate).\n     * Transformer can output a field `dimensions` to define the its own output dimensions.\n     * We also allow transformers to ignore the output `dimensions` field, and\n     * inherit the upstream dimensions definition. It can reduce the burden of handling\n     * dimensions in transformers.\n     *\n     * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n     */\n\n    if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different\n    // dimensions definitions. We do not inherit anything from upstream.\n    && !result.dimensions) {\n      var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result, because:\n      // (1) The returned data always does not contain header line and can not be used\n      // as dimension-detection. In this case we can not use \"detected dimensions\" of\n      // upstream directly, because it might be detected based on different `seriesLayoutBy`.\n      // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.\n      // So the original detected header should be add to the result, otherwise they can not be read.\n\n      if (startIndex) {\n        result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);\n      }\n\n      resultMetaRawOption = {\n        seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n        sourceHeader: startIndex,\n        dimensions: firstUpSource.metaRawOption.dimensions\n      };\n    } else {\n      resultMetaRawOption = {\n        seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n        sourceHeader: 0,\n        dimensions: result.dimensions\n      };\n    }\n\n    return createSource(result.data, resultMetaRawOption, null);\n  });\n}\n\nfunction isSupportedSourceFormat(sourceFormat) {\n  return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;\n}\n\nvar UNDEFINED = 'undefined';\n/* global Float64Array, Int32Array, Uint32Array, Uint16Array */\n// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is\n// different from the Ctor of typed array.\n\nvar CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;\nvar CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;\nvar CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;\nvar CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array;\n/**\n * Multi dimensional data store\n */\n\nvar dataCtors = {\n  'float': CtorFloat64Array,\n  'int': CtorInt32Array,\n  // Ordinal data type can be string or int\n  'ordinal': Array,\n  'number': Array,\n  'time': CtorFloat64Array\n};\nvar defaultDimValueGetters;\n\nfunction getIndicesCtor(rawCount) {\n  // The possible max value in this._indicies is always this._rawCount despite of filtering.\n  return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;\n}\n\nfunction getInitialExtent() {\n  return [Infinity, -Infinity];\n}\n\nfunction cloneChunk(originalChunk) {\n  var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array.\n\n  return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);\n}\n\nfunction prepareStore(store, dimIdx, dimType, end, append) {\n  var DataCtor = dataCtors[dimType || 'float'];\n\n  if (append) {\n    var oldStore = store[dimIdx];\n    var oldLen = oldStore && oldStore.length;\n\n    if (!(oldLen === end)) {\n      var newStore = new DataCtor(end); // The cost of the copy is probably inconsiderable\n      // within the initial chunkSize.\n\n      for (var j = 0; j < oldLen; j++) {\n        newStore[j] = oldStore[j];\n      }\n\n      store[dimIdx] = newStore;\n    }\n  } else {\n    store[dimIdx] = new DataCtor(end);\n  }\n}\n/**\n * Basically, DataStore API keep immutable.\n */\n\nvar DataStore =\n/** @class */\nfunction () {\n  function DataStore() {\n    this._chunks = []; // It will not be calculated util needed.\n\n    this._rawExtent = [];\n    this._extent = [];\n    this._count = 0;\n    this._rawCount = 0;\n    this._calcDimNameToIdx = createHashMap();\n  }\n  /**\n   * Initialize from data\n   */\n\n\n  DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {\n    if (\"development\" !== 'production') {\n      assert(isFunction(provider.getItem) && isFunction(provider.count), 'Invalid data provider.');\n    }\n\n    this._provider = provider; // Clear\n\n    this._chunks = [];\n    this._indices = null;\n    this.getRawIndex = this._getRawIdxIdentity;\n    var source = provider.getSource();\n    var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; // Default dim value getter\n\n    this._dimValueGetter = dimValueGetter || defaultGetter; // Reset raw extent.\n\n    this._rawExtent = [];\n    var willRetrieveDataByName = shouldRetrieveDataByName(source);\n    this._dimensions = map(inputDimensions, function (dim) {\n      if (\"development\" !== 'production') {\n        if (willRetrieveDataByName) {\n          assert(dim.property != null);\n        }\n      }\n\n      return {\n        // Only pick these two props. Not leak other properties like orderMeta.\n        type: dim.type,\n        property: dim.property\n      };\n    });\n\n    this._initDataFromProvider(0, provider.count());\n  };\n\n  DataStore.prototype.getProvider = function () {\n    return this._provider;\n  };\n  /**\n   * Caution: even when a `source` instance owned by a series, the created data store\n   * may still be shared by different sereis (the source hash does not use all `source`\n   * props, see `sourceManager`). In this case, the `source` props that are not used in\n   * hash (like `source.dimensionDefine`) probably only belongs to a certain series and\n   * thus should not be fetch here.\n   */\n\n\n  DataStore.prototype.getSource = function () {\n    return this._provider.getSource();\n  };\n  /**\n   * @caution Only used in dataStack.\n   */\n\n\n  DataStore.prototype.ensureCalculationDimension = function (dimName, type) {\n    var calcDimNameToIdx = this._calcDimNameToIdx;\n    var dimensions = this._dimensions;\n    var calcDimIdx = calcDimNameToIdx.get(dimName);\n\n    if (calcDimIdx != null) {\n      if (dimensions[calcDimIdx].type === type) {\n        return calcDimIdx;\n      }\n    } else {\n      calcDimIdx = dimensions.length;\n    }\n\n    dimensions[calcDimIdx] = {\n      type: type\n    };\n    calcDimNameToIdx.set(dimName, calcDimIdx);\n    this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);\n    this._rawExtent[calcDimIdx] = getInitialExtent();\n    return calcDimIdx;\n  };\n\n  DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) {\n    var chunk = this._chunks[dimIdx];\n    var dim = this._dimensions[dimIdx];\n    var rawExtents = this._rawExtent;\n    var offset = dim.ordinalOffset || 0;\n    var len = chunk.length;\n\n    if (offset === 0) {\n      // We need to reset the rawExtent if collect is from start.\n      // Because this dimension may be guessed as number and calcuating a wrong extent.\n      rawExtents[dimIdx] = getInitialExtent();\n    }\n\n    var dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData\n\n    for (var i = offset; i < len; i++) {\n      var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]);\n\n      if (!isNaN(val)) {\n        dimRawExtent[0] = Math.min(val, dimRawExtent[0]);\n        dimRawExtent[1] = Math.max(val, dimRawExtent[1]);\n      }\n    }\n\n    dim.ordinalMeta = ordinalMeta;\n    dim.ordinalOffset = len;\n    dim.type = 'ordinal'; // Force to be ordinal\n  };\n\n  DataStore.prototype.getOrdinalMeta = function (dimIdx) {\n    var dimInfo = this._dimensions[dimIdx];\n    var ordinalMeta = dimInfo.ordinalMeta;\n    return ordinalMeta;\n  };\n\n  DataStore.prototype.getDimensionProperty = function (dimIndex) {\n    var item = this._dimensions[dimIndex];\n    return item && item.property;\n  };\n  /**\n   * Caution: Can be only called on raw data (before `this._indices` created).\n   */\n\n\n  DataStore.prototype.appendData = function (data) {\n    if (\"development\" !== 'production') {\n      assert(!this._indices, 'appendData can only be called on raw data.');\n    }\n\n    var provider = this._provider;\n    var start = this.count();\n    provider.appendData(data);\n    var end = provider.count();\n\n    if (!provider.persistent) {\n      end += start;\n    }\n\n    if (start < end) {\n      this._initDataFromProvider(start, end, true);\n    }\n\n    return [start, end];\n  };\n\n  DataStore.prototype.appendValues = function (values, minFillLen) {\n    var chunks = this._chunks;\n    var dimensions = this._dimensions;\n    var dimLen = dimensions.length;\n    var rawExtent = this._rawExtent;\n    var start = this.count();\n    var end = start + Math.max(values.length, minFillLen || 0);\n\n    for (var i = 0; i < dimLen; i++) {\n      var dim = dimensions[i];\n      prepareStore(chunks, i, dim.type, end, true);\n    }\n\n    var emptyDataItem = [];\n\n    for (var idx = start; idx < end; idx++) {\n      var sourceIdx = idx - start; // Store the data by dimensions\n\n      for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n        var dim = dimensions[dimIdx];\n        var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx);\n        chunks[dimIdx][idx] = val;\n        var dimRawExtent = rawExtent[dimIdx];\n        val < dimRawExtent[0] && (dimRawExtent[0] = val);\n        val > dimRawExtent[1] && (dimRawExtent[1] = val);\n      }\n    }\n\n    this._rawCount = this._count = end;\n    return {\n      start: start,\n      end: end\n    };\n  };\n\n  DataStore.prototype._initDataFromProvider = function (start, end, append) {\n    var provider = this._provider;\n    var chunks = this._chunks;\n    var dimensions = this._dimensions;\n    var dimLen = dimensions.length;\n    var rawExtent = this._rawExtent;\n    var dimNames = map(dimensions, function (dim) {\n      return dim.property;\n    });\n\n    for (var i = 0; i < dimLen; i++) {\n      var dim = dimensions[i];\n\n      if (!rawExtent[i]) {\n        rawExtent[i] = getInitialExtent();\n      }\n\n      prepareStore(chunks, i, dim.type, end, append);\n    }\n\n    if (provider.fillStorage) {\n      provider.fillStorage(start, end, chunks, rawExtent);\n    } else {\n      var dataItem = [];\n\n      for (var idx = start; idx < end; idx++) {\n        // NOTICE: Try not to write things into dataItem\n        dataItem = provider.getItem(idx, dataItem); // Each data item is value\n        // [1, 2]\n        // 2\n        // Bar chart, line chart which uses category axis\n        // only gives the 'y' value. 'x' value is the indices of category\n        // Use a tempValue to normalize the value to be a (x, y) value\n        // Store the data by dimensions\n\n        for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n          var dimStorage = chunks[dimIdx]; // PENDING NULL is empty or zero\n\n          var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx);\n\n          dimStorage[idx] = val;\n          var dimRawExtent = rawExtent[dimIdx];\n          val < dimRawExtent[0] && (dimRawExtent[0] = val);\n          val > dimRawExtent[1] && (dimRawExtent[1] = val);\n        }\n      }\n    }\n\n    if (!provider.persistent && provider.clean) {\n      // Clean unused data if data source is typed array.\n      provider.clean();\n    }\n\n    this._rawCount = this._count = end; // Reset data extent\n\n    this._extent = [];\n  };\n\n  DataStore.prototype.count = function () {\n    return this._count;\n  };\n  /**\n   * Get value. Return NaN if idx is out of range.\n   */\n\n\n  DataStore.prototype.get = function (dim, idx) {\n    if (!(idx >= 0 && idx < this._count)) {\n      return NaN;\n    }\n\n    var dimStore = this._chunks[dim];\n    return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;\n  };\n\n  DataStore.prototype.getValues = function (dimensions, idx) {\n    var values = [];\n    var dimArr = [];\n\n    if (idx == null) {\n      idx = dimensions; // TODO get all from store?\n\n      dimensions = []; // All dimensions\n\n      for (var i = 0; i < this._dimensions.length; i++) {\n        dimArr.push(i);\n      }\n    } else {\n      dimArr = dimensions;\n    }\n\n    for (var i = 0, len = dimArr.length; i < len; i++) {\n      values.push(this.get(dimArr[i], idx));\n    }\n\n    return values;\n  };\n  /**\n   * @param dim concrete dim\n   */\n\n\n  DataStore.prototype.getByRawIndex = function (dim, rawIdx) {\n    if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {\n      return NaN;\n    }\n\n    var dimStore = this._chunks[dim];\n    return dimStore ? dimStore[rawIdx] : NaN;\n  };\n  /**\n   * Get sum of data in one dimension\n   */\n\n\n  DataStore.prototype.getSum = function (dim) {\n    var dimData = this._chunks[dim];\n    var sum = 0;\n\n    if (dimData) {\n      for (var i = 0, len = this.count(); i < len; i++) {\n        var value = this.get(dim, i);\n\n        if (!isNaN(value)) {\n          sum += value;\n        }\n      }\n    }\n\n    return sum;\n  };\n  /**\n   * Get median of data in one dimension\n   */\n\n\n  DataStore.prototype.getMedian = function (dim) {\n    var dimDataArray = []; // map all data of one dimension\n\n    this.each([dim], function (val) {\n      if (!isNaN(val)) {\n        dimDataArray.push(val);\n      }\n    }); // TODO\n    // Use quick select?\n\n    var sortedDimDataArray = dimDataArray.sort(function (a, b) {\n      return a - b;\n    });\n    var len = this.count(); // calculate median\n\n    return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;\n  };\n  /**\n   * Retrieve the index with given raw data index.\n   */\n\n\n  DataStore.prototype.indexOfRawIndex = function (rawIndex) {\n    if (rawIndex >= this._rawCount || rawIndex < 0) {\n      return -1;\n    }\n\n    if (!this._indices) {\n      return rawIndex;\n    } // Indices are ascending\n\n\n    var indices = this._indices; // If rawIndex === dataIndex\n\n    var rawDataIndex = indices[rawIndex];\n\n    if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {\n      return rawIndex;\n    }\n\n    var left = 0;\n    var right = this._count - 1;\n\n    while (left <= right) {\n      var mid = (left + right) / 2 | 0;\n\n      if (indices[mid] < rawIndex) {\n        left = mid + 1;\n      } else if (indices[mid] > rawIndex) {\n        right = mid - 1;\n      } else {\n        return mid;\n      }\n    }\n\n    return -1;\n  };\n  /**\n   * Retrieve the index of nearest value.\n   * @param dim\n   * @param value\n   * @param [maxDistance=Infinity]\n   * @return If and only if multiple indices have\n   *         the same value, they are put to the result.\n   */\n\n\n  DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n    var chunks = this._chunks;\n    var dimData = chunks[dim];\n    var nearestIndices = [];\n\n    if (!dimData) {\n      return nearestIndices;\n    }\n\n    if (maxDistance == null) {\n      maxDistance = Infinity;\n    }\n\n    var minDist = Infinity;\n    var minDiff = -1;\n    var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/SeriesData.js`.\n\n    for (var i = 0, len = this.count(); i < len; i++) {\n      var dataIndex = this.getRawIndex(i);\n      var diff = value - dimData[dataIndex];\n      var dist = Math.abs(diff);\n\n      if (dist <= maxDistance) {\n        // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,\n        // we'd better not push both of them to `nearestIndices`, otherwise it is easy to\n        // get more than one item in `nearestIndices` (more specifically, in `tooltip`).\n        // So we chose the one that `diff >= 0` in this csae.\n        // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them\n        // should be push to `nearestIndices`.\n        if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) {\n          minDist = dist;\n          minDiff = diff;\n          nearestIndicesLen = 0;\n        }\n\n        if (diff === minDiff) {\n          nearestIndices[nearestIndicesLen++] = i;\n        }\n      }\n    }\n\n    nearestIndices.length = nearestIndicesLen;\n    return nearestIndices;\n  };\n\n  DataStore.prototype.getIndices = function () {\n    var newIndices;\n    var indices = this._indices;\n\n    if (indices) {\n      var Ctor = indices.constructor;\n      var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.\n\n      if (Ctor === Array) {\n        newIndices = new Ctor(thisCount);\n\n        for (var i = 0; i < thisCount; i++) {\n          newIndices[i] = indices[i];\n        }\n      } else {\n        newIndices = new Ctor(indices.buffer, 0, thisCount);\n      }\n    } else {\n      var Ctor = getIndicesCtor(this._rawCount);\n      newIndices = new Ctor(this.count());\n\n      for (var i = 0; i < newIndices.length; i++) {\n        newIndices[i] = i;\n      }\n    }\n\n    return newIndices;\n  };\n  /**\n   * Data filter.\n   */\n\n\n  DataStore.prototype.filter = function (dims, cb) {\n    if (!this._count) {\n      return this;\n    }\n\n    var newStore = this.clone();\n    var count = newStore.count();\n    var Ctor = getIndicesCtor(newStore._rawCount);\n    var newIndices = new Ctor(count);\n    var value = [];\n    var dimSize = dims.length;\n    var offset = 0;\n    var dim0 = dims[0];\n    var chunks = newStore._chunks;\n\n    for (var i = 0; i < count; i++) {\n      var keep = void 0;\n      var rawIdx = newStore.getRawIndex(i); // Simple optimization\n\n      if (dimSize === 0) {\n        keep = cb(i);\n      } else if (dimSize === 1) {\n        var val = chunks[dim0][rawIdx];\n        keep = cb(val, i);\n      } else {\n        var k = 0;\n\n        for (; k < dimSize; k++) {\n          value[k] = chunks[dims[k]][rawIdx];\n        }\n\n        value[k] = i;\n        keep = cb.apply(null, value);\n      }\n\n      if (keep) {\n        newIndices[offset++] = rawIdx;\n      }\n    } // Set indices after filtered.\n\n\n    if (offset < count) {\n      newStore._indices = newIndices;\n    }\n\n    newStore._count = offset; // Reset data extent\n\n    newStore._extent = [];\n\n    newStore._updateGetRawIdx();\n\n    return newStore;\n  };\n  /**\n   * Select data in range. (For optimization of filter)\n   * (Manually inline code, support 5 million data filtering in data zoom.)\n   */\n\n\n  DataStore.prototype.selectRange = function (range) {\n    var newStore = this.clone();\n    var len = newStore._count;\n\n    if (!len) {\n      return this;\n    }\n\n    var dims = keys(range);\n    var dimSize = dims.length;\n\n    if (!dimSize) {\n      return this;\n    }\n\n    var originalCount = newStore.count();\n    var Ctor = getIndicesCtor(newStore._rawCount);\n    var newIndices = new Ctor(originalCount);\n    var offset = 0;\n    var dim0 = dims[0];\n    var min = range[dim0][0];\n    var max = range[dim0][1];\n    var storeArr = newStore._chunks;\n    var quickFinished = false;\n\n    if (!newStore._indices) {\n      // Extreme optimization for common case. About 2x faster in chrome.\n      var idx = 0;\n\n      if (dimSize === 1) {\n        var dimStorage = storeArr[dims[0]];\n\n        for (var i = 0; i < len; i++) {\n          var val = dimStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty\n          // value indicates the line should be broken. But for the case like\n          // scatter plot, a data item with empty value will not be rendered,\n          // but the axis extent may be effected if some other dim of the data\n          // item has value. Fortunately it is not a significant negative effect.\n\n          if (val >= min && val <= max || isNaN(val)) {\n            newIndices[offset++] = idx;\n          }\n\n          idx++;\n        }\n\n        quickFinished = true;\n      } else if (dimSize === 2) {\n        var dimStorage = storeArr[dims[0]];\n        var dimStorage2 = storeArr[dims[1]];\n        var min2 = range[dims[1]][0];\n        var max2 = range[dims[1]][1];\n\n        for (var i = 0; i < len; i++) {\n          var val = dimStorage[i];\n          var val2 = dimStorage2[i]; // Do not filter NaN, see comment above.\n\n          if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {\n            newIndices[offset++] = idx;\n          }\n\n          idx++;\n        }\n\n        quickFinished = true;\n      }\n    }\n\n    if (!quickFinished) {\n      if (dimSize === 1) {\n        for (var i = 0; i < originalCount; i++) {\n          var rawIndex = newStore.getRawIndex(i);\n          var val = storeArr[dims[0]][rawIndex]; // Do not filter NaN, see comment above.\n\n          if (val >= min && val <= max || isNaN(val)) {\n            newIndices[offset++] = rawIndex;\n          }\n        }\n      } else {\n        for (var i = 0; i < originalCount; i++) {\n          var keep = true;\n          var rawIndex = newStore.getRawIndex(i);\n\n          for (var k = 0; k < dimSize; k++) {\n            var dimk = dims[k];\n            var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above.\n\n            if (val < range[dimk][0] || val > range[dimk][1]) {\n              keep = false;\n            }\n          }\n\n          if (keep) {\n            newIndices[offset++] = newStore.getRawIndex(i);\n          }\n        }\n      }\n    } // Set indices after filtered.\n\n\n    if (offset < originalCount) {\n      newStore._indices = newIndices;\n    }\n\n    newStore._count = offset; // Reset data extent\n\n    newStore._extent = [];\n\n    newStore._updateGetRawIdx();\n\n    return newStore;\n  }; // /**\n  //  * Data mapping to a plain array\n  //  */\n  // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {\n  //     const result: any[] = [];\n  //     this.each(dims, function () {\n  //         result.push(cb && (cb as MapArrayCb).apply(null, arguments));\n  //     });\n  //     return result;\n  // }\n\n  /**\n   * Data mapping to a new List with given dimensions\n   */\n\n\n  DataStore.prototype.map = function (dims, cb) {\n    // TODO only clone picked chunks.\n    var target = this.clone(dims);\n\n    this._updateDims(target, dims, cb);\n\n    return target;\n  };\n  /**\n   * @caution Danger!! Only used in dataStack.\n   */\n\n\n  DataStore.prototype.modify = function (dims, cb) {\n    this._updateDims(this, dims, cb);\n  };\n\n  DataStore.prototype._updateDims = function (target, dims, cb) {\n    var targetChunks = target._chunks;\n    var tmpRetValue = [];\n    var dimSize = dims.length;\n    var dataCount = target.count();\n    var values = [];\n    var rawExtent = target._rawExtent;\n\n    for (var i = 0; i < dims.length; i++) {\n      rawExtent[dims[i]] = getInitialExtent();\n    }\n\n    for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {\n      var rawIndex = target.getRawIndex(dataIndex);\n\n      for (var k = 0; k < dimSize; k++) {\n        values[k] = targetChunks[dims[k]][rawIndex];\n      }\n\n      values[dimSize] = dataIndex;\n      var retValue = cb && cb.apply(null, values);\n\n      if (retValue != null) {\n        // a number or string (in oridinal dimension)?\n        if (typeof retValue !== 'object') {\n          tmpRetValue[0] = retValue;\n          retValue = tmpRetValue;\n        }\n\n        for (var i = 0; i < retValue.length; i++) {\n          var dim = dims[i];\n          var val = retValue[i];\n          var rawExtentOnDim = rawExtent[dim];\n          var dimStore = targetChunks[dim];\n\n          if (dimStore) {\n            dimStore[rawIndex] = val;\n          }\n\n          if (val < rawExtentOnDim[0]) {\n            rawExtentOnDim[0] = val;\n          }\n\n          if (val > rawExtentOnDim[1]) {\n            rawExtentOnDim[1] = val;\n          }\n        }\n      }\n    }\n  };\n  /**\n   * Large data down sampling using largest-triangle-three-buckets\n   * @param {string} valueDimension\n   * @param {number} targetCount\n   */\n\n\n  DataStore.prototype.lttbDownSample = function (valueDimension, rate) {\n    var target = this.clone([valueDimension], true);\n    var targetStorage = target._chunks;\n    var dimStore = targetStorage[valueDimension];\n    var len = this.count();\n    var sampledIndex = 0;\n    var frameSize = Math.floor(1 / rate);\n    var currentRawIndex = this.getRawIndex(0);\n    var maxArea;\n    var area;\n    var nextRawIndex;\n    var newIndices = new (getIndicesCtor(this._rawCount))(Math.min((Math.ceil(len / frameSize) + 2) * 2, len)); // First frame use the first data.\n\n    newIndices[sampledIndex++] = currentRawIndex;\n\n    for (var i = 1; i < len - 1; i += frameSize) {\n      var nextFrameStart = Math.min(i + frameSize, len - 1);\n      var nextFrameEnd = Math.min(i + frameSize * 2, len);\n      var avgX = (nextFrameEnd + nextFrameStart) / 2;\n      var avgY = 0;\n\n      for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {\n        var rawIndex = this.getRawIndex(idx);\n        var y = dimStore[rawIndex];\n\n        if (isNaN(y)) {\n          continue;\n        }\n\n        avgY += y;\n      }\n\n      avgY /= nextFrameEnd - nextFrameStart;\n      var frameStart = i;\n      var frameEnd = Math.min(i + frameSize, len);\n      var pointAX = i - 1;\n      var pointAY = dimStore[currentRawIndex];\n      maxArea = -1;\n      nextRawIndex = frameStart;\n      var firstNaNIndex = -1;\n      var countNaN = 0; // Find a point from current frame that construct a triangel with largest area with previous selected point\n      // And the average of next frame.\n\n      for (var idx = frameStart; idx < frameEnd; idx++) {\n        var rawIndex = this.getRawIndex(idx);\n        var y = dimStore[rawIndex];\n\n        if (isNaN(y)) {\n          countNaN++;\n\n          if (firstNaNIndex < 0) {\n            firstNaNIndex = rawIndex;\n          }\n\n          continue;\n        } // Calculate triangle area over three buckets\n\n\n        area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));\n\n        if (area > maxArea) {\n          maxArea = area;\n          nextRawIndex = rawIndex; // Next a is this b\n        }\n      }\n\n      if (countNaN > 0 && countNaN < frameEnd - frameStart) {\n        // Append first NaN point in every bucket.\n        // It is necessary to ensure the correct order of indices.\n        newIndices[sampledIndex++] = Math.min(firstNaNIndex, nextRawIndex);\n        nextRawIndex = Math.max(firstNaNIndex, nextRawIndex);\n      }\n\n      newIndices[sampledIndex++] = nextRawIndex;\n      currentRawIndex = nextRawIndex; // This a is the next a (chosen b)\n    } // First frame use the last data.\n\n\n    newIndices[sampledIndex++] = this.getRawIndex(len - 1);\n    target._count = sampledIndex;\n    target._indices = newIndices;\n    target.getRawIndex = this._getRawIdx;\n    return target;\n  };\n  /**\n   * Large data down sampling on given dimension\n   * @param sampleIndex Sample index for name and id\n   */\n\n\n  DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n    var target = this.clone([dimension], true);\n    var targetStorage = target._chunks;\n    var frameValues = [];\n    var frameSize = Math.floor(1 / rate);\n    var dimStore = targetStorage[dimension];\n    var len = this.count();\n    var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent();\n    var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize));\n    var offset = 0;\n\n    for (var i = 0; i < len; i += frameSize) {\n      // Last frame\n      if (frameSize > len - i) {\n        frameSize = len - i;\n        frameValues.length = frameSize;\n      }\n\n      for (var k = 0; k < frameSize; k++) {\n        var dataIdx = this.getRawIndex(i + k);\n        frameValues[k] = dimStore[dataIdx];\n      }\n\n      var value = sampleValue(frameValues);\n      var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data\n\n      dimStore[sampleFrameIdx] = value;\n\n      if (value < rawExtentOnDim[0]) {\n        rawExtentOnDim[0] = value;\n      }\n\n      if (value > rawExtentOnDim[1]) {\n        rawExtentOnDim[1] = value;\n      }\n\n      newIndices[offset++] = sampleFrameIdx;\n    }\n\n    target._count = offset;\n    target._indices = newIndices;\n\n    target._updateGetRawIdx();\n\n    return target;\n  };\n  /**\n   * Data iteration\n   * @param ctx default this\n   * @example\n   *  list.each('x', function (x, idx) {});\n   *  list.each(['x', 'y'], function (x, y, idx) {});\n   *  list.each(function (idx) {})\n   */\n\n\n  DataStore.prototype.each = function (dims, cb) {\n    if (!this._count) {\n      return;\n    }\n\n    var dimSize = dims.length;\n    var chunks = this._chunks;\n\n    for (var i = 0, len = this.count(); i < len; i++) {\n      var rawIdx = this.getRawIndex(i); // Simple optimization\n\n      switch (dimSize) {\n        case 0:\n          cb(i);\n          break;\n\n        case 1:\n          cb(chunks[dims[0]][rawIdx], i);\n          break;\n\n        case 2:\n          cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);\n          break;\n\n        default:\n          var k = 0;\n          var value = [];\n\n          for (; k < dimSize; k++) {\n            value[k] = chunks[dims[k]][rawIdx];\n          } // Index\n\n\n          value[k] = i;\n          cb.apply(null, value);\n      }\n    }\n  };\n  /**\n   * Get extent of data in one dimension\n   */\n\n\n  DataStore.prototype.getDataExtent = function (dim) {\n    // Make sure use concrete dim as cache name.\n    var dimData = this._chunks[dim];\n    var initialExtent = getInitialExtent();\n\n    if (!dimData) {\n      return initialExtent;\n    } // Make more strict checkings to ensure hitting cache.\n\n\n    var currEnd = this.count(); // Consider the most cases when using data zoom, `getDataExtent`\n    // happened before filtering. We cache raw extent, which is not\n    // necessary to be cleared and recalculated when restore data.\n\n    var useRaw = !this._indices;\n    var dimExtent;\n\n    if (useRaw) {\n      return this._rawExtent[dim].slice();\n    }\n\n    dimExtent = this._extent[dim];\n\n    if (dimExtent) {\n      return dimExtent.slice();\n    }\n\n    dimExtent = initialExtent;\n    var min = dimExtent[0];\n    var max = dimExtent[1];\n\n    for (var i = 0; i < currEnd; i++) {\n      var rawIdx = this.getRawIndex(i);\n      var value = dimData[rawIdx];\n      value < min && (min = value);\n      value > max && (max = value);\n    }\n\n    dimExtent = [min, max];\n    this._extent[dim] = dimExtent;\n    return dimExtent;\n  };\n  /**\n   * Get raw data item\n   */\n\n\n  DataStore.prototype.getRawDataItem = function (idx) {\n    var rawIdx = this.getRawIndex(idx);\n\n    if (!this._provider.persistent) {\n      var val = [];\n      var chunks = this._chunks;\n\n      for (var i = 0; i < chunks.length; i++) {\n        val.push(chunks[i][rawIdx]);\n      }\n\n      return val;\n    } else {\n      return this._provider.getItem(rawIdx);\n    }\n  };\n  /**\n   * Clone shallow.\n   *\n   * @param clonedDims Determine which dims to clone. Will share the data if not specified.\n   */\n\n\n  DataStore.prototype.clone = function (clonedDims, ignoreIndices) {\n    var target = new DataStore();\n    var chunks = this._chunks;\n    var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {\n      obj[dimIdx] = true;\n      return obj;\n    }, {});\n\n    if (clonedDimsMap) {\n      for (var i = 0; i < chunks.length; i++) {\n        // Not clone if dim is not picked.\n        target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);\n      }\n    } else {\n      target._chunks = chunks;\n    }\n\n    this._copyCommonProps(target);\n\n    if (!ignoreIndices) {\n      target._indices = this._cloneIndices();\n    }\n\n    target._updateGetRawIdx();\n\n    return target;\n  };\n\n  DataStore.prototype._copyCommonProps = function (target) {\n    target._count = this._count;\n    target._rawCount = this._rawCount;\n    target._provider = this._provider;\n    target._dimensions = this._dimensions;\n    target._extent = clone(this._extent);\n    target._rawExtent = clone(this._rawExtent);\n  };\n\n  DataStore.prototype._cloneIndices = function () {\n    if (this._indices) {\n      var Ctor = this._indices.constructor;\n      var indices = void 0;\n\n      if (Ctor === Array) {\n        var thisCount = this._indices.length;\n        indices = new Ctor(thisCount);\n\n        for (var i = 0; i < thisCount; i++) {\n          indices[i] = this._indices[i];\n        }\n      } else {\n        indices = new Ctor(this._indices);\n      }\n\n      return indices;\n    }\n\n    return null;\n  };\n\n  DataStore.prototype._getRawIdxIdentity = function (idx) {\n    return idx;\n  };\n\n  DataStore.prototype._getRawIdx = function (idx) {\n    if (idx < this._count && idx >= 0) {\n      return this._indices[idx];\n    }\n\n    return -1;\n  };\n\n  DataStore.prototype._updateGetRawIdx = function () {\n    this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;\n  };\n\n  DataStore.internalField = function () {\n    function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {\n      return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);\n    }\n\n    defaultDimValueGetters = {\n      arrayRows: getDimValueSimply,\n      objectRows: function (dataItem, property, dataIndex, dimIndex) {\n        return parseDataValue(dataItem[property], this._dimensions[dimIndex]);\n      },\n      keyedColumns: getDimValueSimply,\n      original: function (dataItem, property, dataIndex, dimIndex) {\n        // Performance sensitive, do not use modelUtil.getDataItemValue.\n        // If dataItem is an plain object with no value field, the let `value`\n        // will be assigned with the object, but it will be tread correctly\n        // in the `convertValue`.\n        var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);\n        return parseDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.\n        : value, this._dimensions[dimIndex]);\n      },\n      typedArray: function (dataItem, property, dataIndex, dimIndex) {\n        return dataItem[dimIndex];\n      }\n    };\n  }();\n\n  return DataStore;\n}();\n\n/**\n * [REQUIREMENT_MEMO]:\n * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option.\n * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and\n * `root-dataset`. Them on `series` has higher priority.\n * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might\n * confuse users: whether those props indicate how to visit the upstream source or visit\n * the transform result source, and some transforms has nothing to do with these props,\n * and some transforms might have multiple upstream.\n * (3) Transforms should specify `metaRawOption` in each output, just like they can be\n * declared in `root-dataset`.\n * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms.\n * That is for reducing complexity in transforms.\n * PENDING: Whether to provide transposition transform?\n *\n * [IMPLEMENTAION_MEMO]:\n * \"sourceVisitConfig\" are calculated from `metaRawOption` and `data`.\n * They will not be calculated until `source` is about to be visited (to prevent from\n * duplicate calcuation). `source` is visited only in series and input to transforms.\n *\n * [DIMENSION_INHERIT_RULE]:\n * By default the dimensions are inherited from ancestors, unless a transform return\n * a new dimensions definition.\n * Consider the case:\n * ```js\n * dataset: [{\n *     source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...]\n * }, {\n *     transform: { type: 'filter', ... }\n * }]\n * dataset: [{\n *     dimension: ['Product', 'Sales', 'Prise'],\n *     source: [ ['Cookies', 321, 44.21], ...]\n * }, {\n *     transform: { type: 'filter', ... }\n * }]\n * ```\n * The two types of option should have the same behavior after transform.\n *\n *\n * [SCENARIO]:\n * (1) Provide source data directly:\n * ```js\n * series: {\n *     encode: {...},\n *     dimensions: [...]\n *     seriesLayoutBy: 'row',\n *     data: [[...]]\n * }\n * ```\n * (2) Series refer to dataset.\n * ```js\n * series: [{\n *     encode: {...}\n *     // Ignore datasetIndex means `datasetIndex: 0`\n *     // and the dimensions defination in dataset is used\n * }, {\n *     encode: {...},\n *     seriesLayoutBy: 'column',\n *     datasetIndex: 1\n * }]\n * ```\n * (3) dataset transform\n * ```js\n * dataset: [{\n *     source: [...]\n * }, {\n *     source: [...]\n * }, {\n *     // By default from 0.\n *     transform: { type: 'filter', config: {...} }\n * }, {\n *     // Piped.\n *     transform: [\n *         { type: 'filter', config: {...} },\n *         { type: 'sort', config: {...} }\n *     ]\n * }, {\n *     id: 'regressionData',\n *     fromDatasetIndex: 1,\n *     // Third-party transform\n *     transform: { type: 'ecStat:regression', config: {...} }\n * }, {\n *     // retrieve the extra result.\n *     id: 'regressionFormula',\n *     fromDatasetId: 'regressionData',\n *     fromTransformResult: 1\n * }]\n * ```\n */\n\nvar SourceManager =\n/** @class */\nfunction () {\n  function SourceManager(sourceHost) {\n    // Cached source. Do not repeat calculating if not dirty.\n    this._sourceList = [];\n    this._storeList = []; // version sign of each upstream source manager.\n\n    this._upstreamSignList = [];\n    this._versionSignBase = 0;\n    this._dirty = true;\n    this._sourceHost = sourceHost;\n  }\n  /**\n   * Mark dirty.\n   */\n\n\n  SourceManager.prototype.dirty = function () {\n    this._setLocalSource([], []);\n\n    this._storeList = [];\n    this._dirty = true;\n  };\n\n  SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {\n    this._sourceList = sourceList;\n    this._upstreamSignList = upstreamSignList;\n    this._versionSignBase++;\n\n    if (this._versionSignBase > 9e10) {\n      this._versionSignBase = 0;\n    }\n  };\n  /**\n   * For detecting whether the upstream source is dirty, so that\n   * the local cached source (in `_sourceList`) should be discarded.\n   */\n\n\n  SourceManager.prototype._getVersionSign = function () {\n    return this._sourceHost.uid + '_' + this._versionSignBase;\n  };\n  /**\n   * Always return a source instance. Otherwise throw error.\n   */\n\n\n  SourceManager.prototype.prepareSource = function () {\n    // For the case that call `setOption` multiple time but no data changed,\n    // cache the result source to prevent from repeating transform.\n    if (this._isDirty()) {\n      this._createSource();\n\n      this._dirty = false;\n    }\n  };\n\n  SourceManager.prototype._createSource = function () {\n    this._setLocalSource([], []);\n\n    var sourceHost = this._sourceHost;\n\n    var upSourceMgrList = this._getUpstreamSourceManagers();\n\n    var hasUpstream = !!upSourceMgrList.length;\n    var resultSourceList;\n    var upstreamSignList;\n\n    if (isSeries(sourceHost)) {\n      var seriesModel = sourceHost;\n      var data = void 0;\n      var sourceFormat = void 0;\n      var upSource = void 0; // Has upstream dataset\n\n      if (hasUpstream) {\n        var upSourceMgr = upSourceMgrList[0];\n        upSourceMgr.prepareSource();\n        upSource = upSourceMgr.getSource();\n        data = upSource.data;\n        sourceFormat = upSource.sourceFormat;\n        upstreamSignList = [upSourceMgr._getVersionSign()];\n      } // Series data is from own.\n      else {\n          data = seriesModel.get('data', true);\n          sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;\n          upstreamSignList = [];\n        } // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root.\n\n\n      var newMetaRawOption = this._getSourceMetaRawOption() || {};\n      var upMetaRawOption = upSource && upSource.metaRawOption || {};\n      var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null;\n      var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); // Note here we should not use `upSource.dimensionsDefine`. Consider the case:\n      // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`,\n      // but series need `seriesLayoutBy: 'row'`.\n\n      var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); // We share source with dataset as much as possible\n      // to avoid extra memory cost of high dimensional data.\n\n      var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions;\n      resultSourceList = needsCreateSource ? [createSource(data, {\n        seriesLayoutBy: seriesLayoutBy,\n        sourceHeader: sourceHeader,\n        dimensions: dimensions\n      }, sourceFormat)] : [];\n    } else {\n      var datasetModel = sourceHost; // Has upstream dataset.\n\n      if (hasUpstream) {\n        var result = this._applyTransform(upSourceMgrList);\n\n        resultSourceList = result.sourceList;\n        upstreamSignList = result.upstreamSignList;\n      } // Is root dataset.\n      else {\n          var sourceData = datasetModel.get('source', true);\n          resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)];\n          upstreamSignList = [];\n        }\n    }\n\n    if (\"development\" !== 'production') {\n      assert(resultSourceList && upstreamSignList);\n    }\n\n    this._setLocalSource(resultSourceList, upstreamSignList);\n  };\n\n  SourceManager.prototype._applyTransform = function (upMgrList) {\n    var datasetModel = this._sourceHost;\n    var transformOption = datasetModel.get('transform', true);\n    var fromTransformResult = datasetModel.get('fromTransformResult', true);\n\n    if (\"development\" !== 'production') {\n      assert(fromTransformResult != null || transformOption != null);\n    }\n\n    if (fromTransformResult != null) {\n      var errMsg = '';\n\n      if (upMgrList.length !== 1) {\n        if (\"development\" !== 'production') {\n          errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset';\n        }\n\n        doThrow(errMsg);\n      }\n    }\n\n    var sourceList;\n    var upSourceList = [];\n    var upstreamSignList = [];\n    each(upMgrList, function (upMgr) {\n      upMgr.prepareSource();\n      var upSource = upMgr.getSource(fromTransformResult || 0);\n      var errMsg = '';\n\n      if (fromTransformResult != null && !upSource) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult;\n        }\n\n        doThrow(errMsg);\n      }\n\n      upSourceList.push(upSource);\n      upstreamSignList.push(upMgr._getVersionSign());\n    });\n\n    if (transformOption) {\n      sourceList = applyDataTransform(transformOption, upSourceList, {\n        datasetIndex: datasetModel.componentIndex\n      });\n    } else if (fromTransformResult != null) {\n      sourceList = [cloneSourceShallow(upSourceList[0])];\n    }\n\n    return {\n      sourceList: sourceList,\n      upstreamSignList: upstreamSignList\n    };\n  };\n\n  SourceManager.prototype._isDirty = function () {\n    if (this._dirty) {\n      return true;\n    } // All sourceList is from the some upstream.\n\n\n    var upSourceMgrList = this._getUpstreamSourceManagers();\n\n    for (var i = 0; i < upSourceMgrList.length; i++) {\n      var upSrcMgr = upSourceMgrList[i];\n\n      if ( // Consider the case that there is ancestor diry, call it recursively.\n      // The performance is probably not an issue because usually the chain is not long.\n      upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {\n        return true;\n      }\n    }\n  };\n  /**\n   * @param sourceIndex By default 0, means \"main source\".\n   *                    In most cases there is only one source.\n   */\n\n\n  SourceManager.prototype.getSource = function (sourceIndex) {\n    sourceIndex = sourceIndex || 0;\n    var source = this._sourceList[sourceIndex];\n\n    if (!source) {\n      // Series may share source instance with dataset.\n      var upSourceMgrList = this._getUpstreamSourceManagers();\n\n      return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex);\n    }\n\n    return source;\n  };\n  /**\n   *\n   * Get a data store which can be shared across series.\n   * Only available for series.\n   *\n   * @param seriesDimRequest Dimensions that are generated in series.\n   *        Should have been sorted by `storeDimIndex` asc.\n   */\n\n\n  SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) {\n    if (\"development\" !== 'production') {\n      assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.');\n    }\n\n    var schema = seriesDimRequest.makeStoreSchema();\n    return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash);\n  };\n\n  SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) {\n    // TODO Can use other sourceIndex?\n    var sourceIndex = 0;\n    var storeList = this._storeList;\n    var cachedStoreMap = storeList[sourceIndex];\n\n    if (!cachedStoreMap) {\n      cachedStoreMap = storeList[sourceIndex] = {};\n    }\n\n    var cachedStore = cachedStoreMap[sourceReadKey];\n\n    if (!cachedStore) {\n      var upSourceMgr = this._getUpstreamSourceManagers()[0];\n\n      if (isSeries(this._sourceHost) && upSourceMgr) {\n        cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey);\n      } else {\n        cachedStore = new DataStore(); // Always create store from source of series.\n\n        cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims);\n      }\n\n      cachedStoreMap[sourceReadKey] = cachedStore;\n    }\n\n    return cachedStore;\n  };\n  /**\n   * PENDING: Is it fast enough?\n   * If no upstream, return empty array.\n   */\n\n\n  SourceManager.prototype._getUpstreamSourceManagers = function () {\n    // Always get the relationship from the raw option.\n    // Do not cache the link of the dependency graph, so that\n    // there is no need to update them when change happens.\n    var sourceHost = this._sourceHost;\n\n    if (isSeries(sourceHost)) {\n      var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);\n      return !datasetModel ? [] : [datasetModel.getSourceManager()];\n    } else {\n      return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {\n        return datasetModel.getSourceManager();\n      });\n    }\n  };\n\n  SourceManager.prototype._getSourceMetaRawOption = function () {\n    var sourceHost = this._sourceHost;\n    var seriesLayoutBy;\n    var sourceHeader;\n    var dimensions;\n\n    if (isSeries(sourceHost)) {\n      seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);\n      sourceHeader = sourceHost.get('sourceHeader', true);\n      dimensions = sourceHost.get('dimensions', true);\n    } // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them.\n    else if (!this._getUpstreamSourceManagers().length) {\n        var model = sourceHost;\n        seriesLayoutBy = model.get('seriesLayoutBy', true);\n        sourceHeader = model.get('sourceHeader', true);\n        dimensions = model.get('dimensions', true);\n      }\n\n    return {\n      seriesLayoutBy: seriesLayoutBy,\n      sourceHeader: sourceHeader,\n      dimensions: dimensions\n    };\n  };\n\n  return SourceManager;\n}();\n// disable the transform merge, but do not disable transform clone from rawOption.\n\nfunction disableTransformOptionMerge(datasetModel) {\n  var transformOption = datasetModel.option.transform;\n  transformOption && setAsPrimitive(datasetModel.option.transform);\n}\n\nfunction isSeries(sourceHost) {\n  // Avoid circular dependency with Series.ts\n  return sourceHost.mainType === 'series';\n}\n\nfunction doThrow(errMsg) {\n  throw new Error(errMsg);\n}\n\nvar TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; // TODO: more textStyle option\n\nfunction getTooltipTextStyle(textStyle, renderMode) {\n  var nameFontColor = textStyle.color || '#6e7079';\n  var nameFontSize = textStyle.fontSize || 12;\n  var nameFontWeight = textStyle.fontWeight || '400';\n  var valueFontColor = textStyle.color || '#464646';\n  var valueFontSize = textStyle.fontSize || 14;\n  var valueFontWeight = textStyle.fontWeight || '900';\n\n  if (renderMode === 'html') {\n    // `textStyle` is probably from user input, should be encoded to reduce security risk.\n    return {\n      // eslint-disable-next-line max-len\n      nameStyle: \"font-size:\" + encodeHTML(nameFontSize + '') + \"px;color:\" + encodeHTML(nameFontColor) + \";font-weight:\" + encodeHTML(nameFontWeight + ''),\n      // eslint-disable-next-line max-len\n      valueStyle: \"font-size:\" + encodeHTML(valueFontSize + '') + \"px;color:\" + encodeHTML(valueFontColor) + \";font-weight:\" + encodeHTML(valueFontWeight + '')\n    };\n  } else {\n    return {\n      nameStyle: {\n        fontSize: nameFontSize,\n        fill: nameFontColor,\n        fontWeight: nameFontWeight\n      },\n      valueStyle: {\n        fontSize: valueFontSize,\n        fill: valueFontColor,\n        fontWeight: valueFontWeight\n      }\n    };\n  }\n} // See `TooltipMarkupLayoutIntent['innerGapLevel']`.\n// (value from UI design)\n\n\nvar HTML_GAPS = [0, 10, 20, 30];\nvar RICH_TEXT_GAPS = ['', '\\n', '\\n\\n', '\\n\\n\\n']; // eslint-disable-next-line max-len\n\nfunction createTooltipMarkup(type, option) {\n  option.type = type;\n  return option;\n}\n\nfunction isSectionFragment(frag) {\n  return frag.type === 'section';\n}\n\nfunction getBuilder(frag) {\n  return isSectionFragment(frag) ? buildSection : buildNameValue;\n}\n\nfunction getBlockGapLevel(frag) {\n  if (isSectionFragment(frag)) {\n    var gapLevel_1 = 0;\n    var subBlockLen = frag.blocks.length;\n    var hasInnerGap_1 = subBlockLen > 1 || subBlockLen > 0 && !frag.noHeader;\n    each(frag.blocks, function (subBlock) {\n      var subGapLevel = getBlockGapLevel(subBlock); // If the some of the sub-blocks have some gaps (like 10px) inside, this block\n      // should use a larger gap (like 20px) to distinguish those sub-blocks.\n\n      if (subGapLevel >= gapLevel_1) {\n        gapLevel_1 = subGapLevel + +(hasInnerGap_1 && ( // 0 always can not be readable gap level.\n        !subGapLevel // If no header, always keep the sub gap level. Otherwise\n        // look weird in case `multipleSeries`.\n        || isSectionFragment(subBlock) && !subBlock.noHeader));\n      }\n    });\n    return gapLevel_1;\n  }\n\n  return 0;\n}\n\nfunction buildSection(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {\n  var noHeader = fragment.noHeader;\n  var gaps = getGap(getBlockGapLevel(fragment));\n  var subMarkupTextList = [];\n  var subBlocks = fragment.blocks || [];\n  assert(!subBlocks || isArray(subBlocks));\n  subBlocks = subBlocks || [];\n  var orderMode = ctx.orderMode;\n\n  if (fragment.sortBlocks && orderMode) {\n    subBlocks = subBlocks.slice();\n    var orderMap = {\n      valueAsc: 'asc',\n      valueDesc: 'desc'\n    };\n\n    if (hasOwn(orderMap, orderMode)) {\n      var comparator_1 = new SortOrderComparator(orderMap[orderMode], null);\n      subBlocks.sort(function (a, b) {\n        return comparator_1.evaluate(a.sortParam, b.sortParam);\n      });\n    } // FIXME 'seriesDesc' necessary?\n    else if (orderMode === 'seriesDesc') {\n        subBlocks.reverse();\n      }\n  }\n\n  each(subBlocks, function (subBlock, idx) {\n    var valueFormatter = fragment.valueFormatter;\n    var subMarkupText = getBuilder(subBlock)( // Inherit valueFormatter\n    valueFormatter ? extend(extend({}, ctx), {\n      valueFormatter: valueFormatter\n    }) : ctx, subBlock, idx > 0 ? gaps.html : 0, toolTipTextStyle);\n    subMarkupText != null && subMarkupTextList.push(subMarkupText);\n  });\n  var subMarkupText = ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), noHeader ? topMarginForOuterGap : gaps.html);\n\n  if (noHeader) {\n    return subMarkupText;\n  }\n\n  var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC);\n  var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle;\n\n  if (ctx.renderMode === 'richText') {\n    return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText;\n  } else {\n    return wrapBlockHTML(\"<div style=\\\"\" + nameStyle + \";\" + TOOLTIP_LINE_HEIGHT_CSS + \";\\\">\" + encodeHTML(displayableHeader) + '</div>' + subMarkupText, topMarginForOuterGap);\n  }\n}\n\nfunction buildNameValue(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {\n  var renderMode = ctx.renderMode;\n  var noName = fragment.noName;\n  var noValue = fragment.noValue;\n  var noMarker = !fragment.markerType;\n  var name = fragment.name;\n  var useUTC = ctx.useUTC;\n\n  var valueFormatter = fragment.valueFormatter || ctx.valueFormatter || function (value) {\n    value = isArray(value) ? value : [value];\n    return map(value, function (val, idx) {\n      return makeValueReadable(val, isArray(valueTypeOption) ? valueTypeOption[idx] : valueTypeOption, useUTC);\n    });\n  };\n\n  if (noName && noValue) {\n    return;\n  }\n\n  var markerStr = noMarker ? '' : ctx.markupStyleCreator.makeTooltipMarker(fragment.markerType, fragment.markerColor || '#333', renderMode);\n  var readableName = noName ? '' : makeValueReadable(name, 'ordinal', useUTC);\n  var valueTypeOption = fragment.valueType;\n  var readableValueList = noValue ? [] : valueFormatter(fragment.value);\n  var valueAlignRight = !noMarker || !noName; // It little weird if only value next to marker but far from marker.\n\n  var valueCloseToMarker = !noMarker && noName;\n\n  var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),\n      nameStyle = _a.nameStyle,\n      valueStyle = _a.valueStyle;\n\n  return renderMode === 'richText' ? (noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameRichText(ctx, readableName, nameStyle)) // Value has commas inside, so use ' ' as delimiter for multiple values.\n  + (noValue ? '' : wrapInlineValueRichText(ctx, readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)) : wrapBlockHTML((noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameHTML(readableName, !noMarker, nameStyle)) + (noValue ? '' : wrapInlineValueHTML(readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)), topMarginForOuterGap);\n}\n/**\n * @return markupText. null/undefined means no content.\n */\n\n\nfunction buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {\n  if (!fragment) {\n    return;\n  }\n\n  var builder = getBuilder(fragment);\n  var ctx = {\n    useUTC: useUTC,\n    renderMode: renderMode,\n    orderMode: orderMode,\n    markupStyleCreator: markupStyleCreator,\n    valueFormatter: fragment.valueFormatter\n  };\n  return builder(ctx, fragment, 0, toolTipTextStyle);\n}\n\nfunction getGap(gapLevel) {\n  return {\n    html: HTML_GAPS[gapLevel],\n    richText: RICH_TEXT_GAPS[gapLevel]\n  };\n}\n\nfunction wrapBlockHTML(encodedContent, topGap) {\n  var clearfix = '<div style=\"clear:both\"></div>';\n  var marginCSS = \"margin: \" + topGap + \"px 0 0\";\n  return \"<div style=\\\"\" + marginCSS + \";\" + TOOLTIP_LINE_HEIGHT_CSS + \";\\\">\" + encodedContent + clearfix + '</div>';\n}\n\nfunction wrapInlineNameHTML(name, leftHasMarker, style) {\n  var marginCss = leftHasMarker ? 'margin-left:2px' : '';\n  return \"<span style=\\\"\" + style + \";\" + marginCss + \"\\\">\" + encodeHTML(name) + '</span>';\n}\n\nfunction wrapInlineValueHTML(valueList, alignRight, valueCloseToMarker, style) {\n  // Do not too close to marker, considering there are multiple values separated by spaces.\n  var paddingStr = valueCloseToMarker ? '10px' : '20px';\n  var alignCSS = alignRight ? \"float:right;margin-left:\" + paddingStr : '';\n  valueList = isArray(valueList) ? valueList : [valueList];\n  return \"<span style=\\\"\" + alignCSS + \";\" + style + \"\\\">\" // Value has commas inside, so use '  ' as delimiter for multiple values.\n  + map(valueList, function (value) {\n    return encodeHTML(value);\n  }).join('&nbsp;&nbsp;') + '</span>';\n}\n\nfunction wrapInlineNameRichText(ctx, name, style) {\n  return ctx.markupStyleCreator.wrapRichTextStyle(name, style);\n}\n\nfunction wrapInlineValueRichText(ctx, values, alignRight, valueCloseToMarker, style) {\n  var styles = [style];\n  var paddingLeft = valueCloseToMarker ? 10 : 20;\n  alignRight && styles.push({\n    padding: [0, 0, 0, paddingLeft],\n    align: 'right'\n  }); // Value has commas inside, so use '  ' as delimiter for multiple values.\n\n  return ctx.markupStyleCreator.wrapRichTextStyle(isArray(values) ? values.join('  ') : values, styles);\n}\n\nfunction retrieveVisualColorForTooltipMarker(series, dataIndex) {\n  var style = series.getData().getItemVisual(dataIndex, 'style');\n  var color = style[series.visualDrawType];\n  return convertToColorString(color);\n}\nfunction getPaddingFromTooltipModel(model, renderMode) {\n  var padding = model.get('padding');\n  return padding != null ? padding // We give slightly different to look pretty.\n  : renderMode === 'richText' ? [8, 10] : 10;\n}\n/**\n * The major feature is generate styles for `renderMode: 'richText'`.\n * But it also serves `renderMode: 'html'` to provide\n * \"renderMode-independent\" API.\n */\n\nvar TooltipMarkupStyleCreator =\n/** @class */\nfunction () {\n  function TooltipMarkupStyleCreator() {\n    this.richTextStyles = {}; // Notice that \"generate a style name\" usuall happens repeatly when mouse moving and\n    // displaying a tooltip. So we put the `_nextStyleNameId` as a member of each creator\n    // rather than static shared by all creators (which will cause it increase to fast).\n\n    this._nextStyleNameId = getRandomIdBase();\n  }\n\n  TooltipMarkupStyleCreator.prototype._generateStyleName = function () {\n    return '__EC_aUTo_' + this._nextStyleNameId++;\n  };\n\n  TooltipMarkupStyleCreator.prototype.makeTooltipMarker = function (markerType, colorStr, renderMode) {\n    var markerId = renderMode === 'richText' ? this._generateStyleName() : null;\n    var marker = getTooltipMarker({\n      color: colorStr,\n      type: markerType,\n      renderMode: renderMode,\n      markerId: markerId\n    });\n\n    if (isString(marker)) {\n      return marker;\n    } else {\n      if (\"development\" !== 'production') {\n        assert(markerId);\n      }\n\n      this.richTextStyles[markerId] = marker.style;\n      return marker.content;\n    }\n  };\n  /**\n   * @usage\n   * ```ts\n   * const styledText = markupStyleCreator.wrapRichTextStyle([\n   *     // The styles will be auto merged.\n   *     {\n   *         fontSize: 12,\n   *         color: 'blue'\n   *     },\n   *     {\n   *         padding: 20\n   *     }\n   * ]);\n   * ```\n   */\n\n\n  TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {\n    var finalStl = {};\n\n    if (isArray(styles)) {\n      each(styles, function (stl) {\n        return extend(finalStl, stl);\n      });\n    } else {\n      extend(finalStl, styles);\n    }\n\n    var styleName = this._generateStyleName();\n\n    this.richTextStyles[styleName] = finalStl;\n    return \"{\" + styleName + \"|\" + text + \"}\";\n  };\n\n  return TooltipMarkupStyleCreator;\n}();\n\nfunction defaultSeriesFormatTooltip(opt) {\n  var series = opt.series;\n  var dataIndex = opt.dataIndex;\n  var multipleSeries = opt.multipleSeries;\n  var data = series.getData();\n  var tooltipDims = data.mapDimensionsAll('defaultedTooltip');\n  var tooltipDimLen = tooltipDims.length;\n  var value = series.getRawValue(dataIndex);\n  var isValueArr = isArray(value);\n  var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip.\n\n  var inlineValue;\n  var inlineValueType;\n  var subBlocks;\n  var sortParam;\n\n  if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) {\n    var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);\n    inlineValue = formatArrResult.inlineValues;\n    inlineValueType = formatArrResult.inlineValueTypes;\n    subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases.\n\n    sortParam = formatArrResult.inlineValues[0];\n  } else if (tooltipDimLen) {\n    var dimInfo = data.getDimensionInfo(tooltipDims[0]);\n    sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]);\n    inlineValueType = dimInfo.type;\n  } else {\n    sortParam = inlineValue = isValueArr ? value[0] : value;\n  } // Do not show generated series name. It might not be readable.\n\n\n  var seriesNameSpecified = isNameSpecified(series);\n  var seriesName = seriesNameSpecified && series.name || '';\n  var itemName = data.getName(dataIndex);\n  var inlineName = multipleSeries ? seriesName : itemName;\n  return createTooltipMarkup('section', {\n    header: seriesName,\n    // When series name not specified, do not show a header line with only '-'.\n    // This case alway happen in tooltip.trigger: 'item'.\n    noHeader: multipleSeries || !seriesNameSpecified,\n    sortParam: sortParam,\n    blocks: [createTooltipMarkup('nameValue', {\n      markerType: 'item',\n      markerColor: markerColor,\n      // Do not mix display seriesName and itemName in one tooltip,\n      // which might confuses users.\n      name: inlineName,\n      // name dimension might be auto assigned, where the name might\n      // be not readable. So we check trim here.\n      noName: !trim(inlineName),\n      value: inlineValue,\n      valueType: inlineValueType\n    })].concat(subBlocks || [])\n  });\n}\n\nfunction formatTooltipArrayValue(value, series, dataIndex, tooltipDims, colorStr) {\n  // check: category-no-encode-has-axis-data in dataset.html\n  var data = series.getData();\n  var isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) {\n    var dimItem = data.getDimensionInfo(idx);\n    return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null;\n  }, false);\n  var inlineValues = [];\n  var inlineValueTypes = [];\n  var blocks = [];\n  tooltipDims.length ? each(tooltipDims, function (dim) {\n    setEachItem(retrieveRawValue(data, dataIndex, dim), dim);\n  }) // By default, all dims is used on tooltip.\n  : each(value, setEachItem);\n\n  function setEachItem(val, dim) {\n    var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip.\n\n    if (!dimInfo || dimInfo.otherDims.tooltip === false) {\n      return;\n    }\n\n    if (isValueMultipleLine) {\n      blocks.push(createTooltipMarkup('nameValue', {\n        markerType: 'subItem',\n        markerColor: colorStr,\n        name: dimInfo.displayName,\n        value: val,\n        valueType: dimInfo.type\n      }));\n    } else {\n      inlineValues.push(val);\n      inlineValueTypes.push(dimInfo.type);\n    }\n  }\n\n  return {\n    inlineValues: inlineValues,\n    inlineValueTypes: inlineValueTypes,\n    blocks: blocks\n  };\n}\n\nvar inner$1 = makeInner();\n\nfunction getSelectionKey(data, dataIndex) {\n  return data.getName(dataIndex) || data.getId(dataIndex);\n}\n\nvar SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled';\n\nvar SeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(SeriesModel, _super);\n\n  function SeriesModel() {\n    // [Caution]: Because this class or desecendants can be used as `XXX.extend(subProto)`,\n    // the class members must not be initialized in constructor or declaration place.\n    // Otherwise there is bad case:\n    //   class A {xxx = 1;}\n    //   enableClassExtend(A);\n    //   class B extends A {}\n    //   var C = B.extend({xxx: 5});\n    //   var c = new C();\n    //   console.log(c.xxx); // expect 5 but always 1.\n    var _this = _super !== null && _super.apply(this, arguments) || this; // ---------------------------------------\n    // Props about data selection\n    // ---------------------------------------\n\n\n    _this._selectedDataIndicesMap = {};\n    return _this;\n  }\n\n  SeriesModel.prototype.init = function (option, parentModel, ecModel) {\n    this.seriesIndex = this.componentIndex;\n    this.dataTask = createTask({\n      count: dataTaskCount,\n      reset: dataTaskReset\n    });\n    this.dataTask.context = {\n      model: this\n    };\n    this.mergeDefaultAndTheme(option, ecModel);\n    var sourceManager = inner$1(this).sourceManager = new SourceManager(this);\n    sourceManager.prepareSource();\n    var data = this.getInitialData(option, ecModel);\n    wrapData(data, this);\n    this.dataTask.context.data = data;\n\n    if (\"development\" !== 'production') {\n      assert(data, 'getInitialData returned invalid data.');\n    }\n\n    inner$1(this).dataBeforeProcessed = data; // If we reverse the order (make data firstly, and then make\n    // dataBeforeProcessed by cloneShallow), cloneShallow will\n    // cause data.graph.data !== data when using\n    // module:echarts/data/Graph or module:echarts/data/Tree.\n    // See module:echarts/data/helper/linkSeriesData\n    // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model\n    // init or merge stage, because the data can be restored. So we do not `restoreData`\n    // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.\n    // Call `seriesModel.getRawData()` instead.\n    // this.restoreData();\n\n    autoSeriesName(this);\n\n    this._initSelectedMapFromData(data);\n  };\n  /**\n   * Util for merge default and theme to option\n   */\n\n\n  SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n    var layoutMode = fetchLayoutMode(this);\n    var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme.\n    // But if name duplicate between series subType\n    // (for example: parallel) add component mainType,\n    // add suffix 'Series'.\n\n    var themeSubType = this.subType;\n\n    if (ComponentModel.hasClass(themeSubType)) {\n      themeSubType += 'Series';\n    }\n\n    merge(option, ecModel.getTheme().get(this.subType));\n    merge(option, this.getDefaultOption()); // Default label emphasis `show`\n\n    defaultEmphasis(option, 'label', ['show']);\n    this.fillDataTextStyle(option.data);\n\n    if (layoutMode) {\n      mergeLayoutParam(option, inputPositionParams, layoutMode);\n    }\n  };\n\n  SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {\n    // this.settingTask.dirty();\n    newSeriesOption = merge(this.option, newSeriesOption, true);\n    this.fillDataTextStyle(newSeriesOption.data);\n    var layoutMode = fetchLayoutMode(this);\n\n    if (layoutMode) {\n      mergeLayoutParam(this.option, newSeriesOption, layoutMode);\n    }\n\n    var sourceManager = inner$1(this).sourceManager;\n    sourceManager.dirty();\n    sourceManager.prepareSource();\n    var data = this.getInitialData(newSeriesOption, ecModel);\n    wrapData(data, this);\n    this.dataTask.dirty();\n    this.dataTask.context.data = data;\n    inner$1(this).dataBeforeProcessed = data;\n    autoSeriesName(this);\n\n    this._initSelectedMapFromData(data);\n  };\n\n  SeriesModel.prototype.fillDataTextStyle = function (data) {\n    // Default data label emphasis `show`\n    // FIXME Tree structure data ?\n    // FIXME Performance ?\n    if (data && !isTypedArray(data)) {\n      var props = ['show'];\n\n      for (var i = 0; i < data.length; i++) {\n        if (data[i] && data[i].label) {\n          defaultEmphasis(data[i], 'label', props);\n        }\n      }\n    }\n  };\n  /**\n   * Init a data structure from data related option in series\n   * Must be overridden.\n   */\n\n\n  SeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return;\n  };\n  /**\n   * Append data to list\n   */\n\n\n  SeriesModel.prototype.appendData = function (params) {\n    // FIXME ???\n    // (1) If data from dataset, forbidden append.\n    // (2) support append data of dataset.\n    var data = this.getRawData();\n    data.appendData(params.data);\n  };\n  /**\n   * Consider some method like `filter`, `map` need make new data,\n   * We should make sure that `seriesModel.getData()` get correct\n   * data in the stream procedure. So we fetch data from upstream\n   * each time `task.perform` called.\n   */\n\n\n  SeriesModel.prototype.getData = function (dataType) {\n    var task = getCurrentTask(this);\n\n    if (task) {\n      var data = task.context.data;\n      return dataType == null ? data : data.getLinkedData(dataType);\n    } else {\n      // When series is not alive (that may happen when click toolbox\n      // restore or setOption with not merge mode), series data may\n      // be still need to judge animation or something when graphic\n      // elements want to know whether fade out.\n      return inner$1(this).data;\n    }\n  };\n\n  SeriesModel.prototype.getAllData = function () {\n    var mainData = this.getData();\n    return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{\n      data: mainData\n    }];\n  };\n\n  SeriesModel.prototype.setData = function (data) {\n    var task = getCurrentTask(this);\n\n    if (task) {\n      var context = task.context; // Consider case: filter, data sample.\n      // FIXME:TS never used, so comment it\n      // if (context.data !== data && task.modifyOutputEnd) {\n      //     task.setOutputEnd(data.count());\n      // }\n\n      context.outputData = data; // Caution: setData should update context.data,\n      // Because getData may be called multiply in a\n      // single stage and expect to get the data just\n      // set. (For example, AxisProxy, x y both call\n      // getData and setDate sequentially).\n      // So the context.data should be fetched from\n      // upstream each time when a stage starts to be\n      // performed.\n\n      if (task !== this.dataTask) {\n        context.data = data;\n      }\n    }\n\n    inner$1(this).data = data;\n  };\n\n  SeriesModel.prototype.getEncode = function () {\n    var encode = this.get('encode', true);\n\n    if (encode) {\n      return createHashMap(encode);\n    }\n  };\n\n  SeriesModel.prototype.getSourceManager = function () {\n    return inner$1(this).sourceManager;\n  };\n\n  SeriesModel.prototype.getSource = function () {\n    return this.getSourceManager().getSource();\n  };\n  /**\n   * Get data before processed\n   */\n\n\n  SeriesModel.prototype.getRawData = function () {\n    return inner$1(this).dataBeforeProcessed;\n  };\n\n  SeriesModel.prototype.getColorBy = function () {\n    var colorBy = this.get('colorBy');\n    return colorBy || 'series';\n  };\n\n  SeriesModel.prototype.isColorBySeries = function () {\n    return this.getColorBy() === 'series';\n  };\n  /**\n   * Get base axis if has coordinate system and has axis.\n   * By default use coordSys.getBaseAxis();\n   * Can be overridden for some chart.\n   * @return {type} description\n   */\n\n\n  SeriesModel.prototype.getBaseAxis = function () {\n    var coordSys = this.coordinateSystem; // @ts-ignore\n\n    return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();\n  };\n  /**\n   * Default tooltip formatter\n   *\n   * @param dataIndex\n   * @param multipleSeries\n   * @param dataType\n   * @param renderMode valid values: 'html'(by default) and 'richText'.\n   *        'html' is used for rendering tooltip in extra DOM form, and the result\n   *        string is used as DOM HTML content.\n   *        'richText' is used for rendering tooltip in rich text form, for those where\n   *        DOM operation is not supported.\n   * @return formatted tooltip with `html` and `markers`\n   *        Notice: The override method can also return string\n   */\n\n\n  SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    return defaultSeriesFormatTooltip({\n      series: this,\n      dataIndex: dataIndex,\n      multipleSeries: multipleSeries\n    });\n  };\n\n  SeriesModel.prototype.isAnimationEnabled = function () {\n    var ecModel = this.ecModel; // Disable animation if using echarts in node but not give ssr flag.\n    // In ssr mode, renderToString will generate svg with css animation.\n\n    if (env.node && !(ecModel && ecModel.ssr)) {\n      return false;\n    }\n\n    var animationEnabled = this.getShallow('animation');\n\n    if (animationEnabled) {\n      if (this.getData().count() > this.getShallow('animationThreshold')) {\n        animationEnabled = false;\n      }\n    }\n\n    return !!animationEnabled;\n  };\n\n  SeriesModel.prototype.restoreData = function () {\n    this.dataTask.dirty();\n  };\n\n  SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {\n    var ecModel = this.ecModel; // PENDING\n\n    var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);\n\n    if (!color) {\n      color = ecModel.getColorFromPalette(name, scope, requestColorNum);\n    }\n\n    return color;\n  };\n  /**\n   * Use `data.mapDimensionsAll(coordDim)` instead.\n   * @deprecated\n   */\n\n\n  SeriesModel.prototype.coordDimToDataDim = function (coordDim) {\n    return this.getRawData().mapDimensionsAll(coordDim);\n  };\n  /**\n   * Get progressive rendering count each step\n   */\n\n\n  SeriesModel.prototype.getProgressive = function () {\n    return this.get('progressive');\n  };\n  /**\n   * Get progressive rendering count each step\n   */\n\n\n  SeriesModel.prototype.getProgressiveThreshold = function () {\n    return this.get('progressiveThreshold');\n  }; // PENGING If selectedMode is null ?\n\n\n  SeriesModel.prototype.select = function (innerDataIndices, dataType) {\n    this._innerSelect(this.getData(dataType), innerDataIndices);\n  };\n\n  SeriesModel.prototype.unselect = function (innerDataIndices, dataType) {\n    var selectedMap = this.option.selectedMap;\n\n    if (!selectedMap) {\n      return;\n    }\n\n    var selectedMode = this.option.selectedMode;\n    var data = this.getData(dataType);\n\n    if (selectedMode === 'series' || selectedMap === 'all') {\n      this.option.selectedMap = {};\n      this._selectedDataIndicesMap = {};\n      return;\n    }\n\n    for (var i = 0; i < innerDataIndices.length; i++) {\n      var dataIndex = innerDataIndices[i];\n      var nameOrId = getSelectionKey(data, dataIndex);\n      selectedMap[nameOrId] = false;\n      this._selectedDataIndicesMap[nameOrId] = -1;\n    }\n  };\n\n  SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) {\n    var tmpArr = [];\n\n    for (var i = 0; i < innerDataIndices.length; i++) {\n      tmpArr[0] = innerDataIndices[i];\n      this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType);\n    }\n  };\n\n  SeriesModel.prototype.getSelectedDataIndices = function () {\n    if (this.option.selectedMap === 'all') {\n      return [].slice.call(this.getData().getIndices());\n    }\n\n    var selectedDataIndicesMap = this._selectedDataIndicesMap;\n    var nameOrIds = keys(selectedDataIndicesMap);\n    var dataIndices = [];\n\n    for (var i = 0; i < nameOrIds.length; i++) {\n      var dataIndex = selectedDataIndicesMap[nameOrIds[i]];\n\n      if (dataIndex >= 0) {\n        dataIndices.push(dataIndex);\n      }\n    }\n\n    return dataIndices;\n  };\n\n  SeriesModel.prototype.isSelected = function (dataIndex, dataType) {\n    var selectedMap = this.option.selectedMap;\n\n    if (!selectedMap) {\n      return false;\n    }\n\n    var data = this.getData(dataType);\n    return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) && !data.getItemModel(dataIndex).get(['select', 'disabled']);\n  };\n\n  SeriesModel.prototype.isUniversalTransitionEnabled = function () {\n    if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) {\n      return true;\n    }\n\n    var universalTransitionOpt = this.option.universalTransition; // Quick reject\n\n    if (!universalTransitionOpt) {\n      return false;\n    }\n\n    if (universalTransitionOpt === true) {\n      return true;\n    } // Can be simply 'universalTransition: true'\n\n\n    return universalTransitionOpt && universalTransitionOpt.enabled;\n  };\n\n  SeriesModel.prototype._innerSelect = function (data, innerDataIndices) {\n    var _a, _b;\n\n    var option = this.option;\n    var selectedMode = option.selectedMode;\n    var len = innerDataIndices.length;\n\n    if (!selectedMode || !len) {\n      return;\n    }\n\n    if (selectedMode === 'series') {\n      option.selectedMap = 'all';\n    } else if (selectedMode === 'multiple') {\n      if (!isObject(option.selectedMap)) {\n        option.selectedMap = {};\n      }\n\n      var selectedMap = option.selectedMap;\n\n      for (var i = 0; i < len; i++) {\n        var dataIndex = innerDataIndices[i]; // TODO different types of data share same object.\n\n        var nameOrId = getSelectionKey(data, dataIndex);\n        selectedMap[nameOrId] = true;\n        this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex);\n      }\n    } else if (selectedMode === 'single' || selectedMode === true) {\n      var lastDataIndex = innerDataIndices[len - 1];\n      var nameOrId = getSelectionKey(data, lastDataIndex);\n      option.selectedMap = (_a = {}, _a[nameOrId] = true, _a);\n      this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b);\n    }\n  };\n\n  SeriesModel.prototype._initSelectedMapFromData = function (data) {\n    // Ignore select info in data if selectedMap exists.\n    // NOTE It's only for legacy usage. edge data is not supported.\n    if (this.option.selectedMap) {\n      return;\n    }\n\n    var dataIndices = [];\n\n    if (data.hasItemOption) {\n      data.each(function (idx) {\n        var rawItem = data.getRawDataItem(idx);\n\n        if (rawItem && rawItem.selected) {\n          dataIndices.push(idx);\n        }\n      });\n    }\n\n    if (dataIndices.length > 0) {\n      this._innerSelect(data, dataIndices);\n    }\n  }; // /**\n  //  * @see {module:echarts/stream/Scheduler}\n  //  */\n  // abstract pipeTask: null\n\n\n  SeriesModel.registerClass = function (clz) {\n    return ComponentModel.registerClass(clz);\n  };\n\n  SeriesModel.protoInitialize = function () {\n    var proto = SeriesModel.prototype;\n    proto.type = 'series.__base__';\n    proto.seriesIndex = 0;\n    proto.ignoreStyleOnData = false;\n    proto.hasSymbolVisual = false;\n    proto.defaultSymbol = 'circle'; // Make sure the values can be accessed!\n\n    proto.visualStyleAccessPath = 'itemStyle';\n    proto.visualDrawType = 'fill';\n  }();\n\n  return SeriesModel;\n}(ComponentModel);\n\nmixin(SeriesModel, DataFormatMixin);\nmixin(SeriesModel, PaletteMixin);\nmountExtend(SeriesModel, ComponentModel);\n/**\n * MUST be called after `prepareSource` called\n * Here we need to make auto series, especially for auto legend. But we\n * do not modify series.name in option to avoid side effects.\n */\n\nfunction autoSeriesName(seriesModel) {\n  // User specified name has higher priority, otherwise it may cause\n  // series can not be queried unexpectedly.\n  var name = seriesModel.name;\n\n  if (!isNameSpecified(seriesModel)) {\n    seriesModel.name = getSeriesAutoName(seriesModel) || name;\n  }\n}\n\nfunction getSeriesAutoName(seriesModel) {\n  var data = seriesModel.getRawData();\n  var dataDims = data.mapDimensionsAll('seriesName');\n  var nameArr = [];\n  each(dataDims, function (dataDim) {\n    var dimInfo = data.getDimensionInfo(dataDim);\n    dimInfo.displayName && nameArr.push(dimInfo.displayName);\n  });\n  return nameArr.join(' ');\n}\n\nfunction dataTaskCount(context) {\n  return context.model.getRawData().count();\n}\n\nfunction dataTaskReset(context) {\n  var seriesModel = context.model;\n  seriesModel.setData(seriesModel.getRawData().cloneShallow());\n  return dataTaskProgress;\n}\n\nfunction dataTaskProgress(param, context) {\n  // Avoid repead cloneShallow when data just created in reset.\n  if (context.outputData && param.end > context.outputData.count()) {\n    context.model.getRawData().cloneShallow(context.outputData);\n  }\n} // TODO refactor\n\n\nfunction wrapData(data, seriesModel) {\n  each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {\n    data.wrapMethod(methodName, curry(onDataChange, seriesModel));\n  });\n}\n\nfunction onDataChange(seriesModel, newList) {\n  var task = getCurrentTask(seriesModel);\n\n  if (task) {\n    // Consider case: filter, selectRange\n    task.setOutputEnd((newList || this).count());\n  }\n\n  return newList;\n}\n\nfunction getCurrentTask(seriesModel) {\n  var scheduler = (seriesModel.ecModel || {}).scheduler;\n  var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);\n\n  if (pipeline) {\n    // When pipline finished, the currrentTask keep the last\n    // task (renderTask).\n    var task = pipeline.currentTask;\n\n    if (task) {\n      var agentStubMap = task.agentStubMap;\n\n      if (agentStubMap) {\n        task = agentStubMap.get(seriesModel.uid);\n      }\n    }\n\n    return task;\n  }\n}\n\nvar ComponentView =\n/** @class */\nfunction () {\n  function ComponentView() {\n    this.group = new Group();\n    this.uid = getUID('viewComponent');\n  }\n\n  ComponentView.prototype.init = function (ecModel, api) {};\n\n  ComponentView.prototype.render = function (model, ecModel, api, payload) {};\n\n  ComponentView.prototype.dispose = function (ecModel, api) {};\n\n  ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;\n  };\n\n  ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing;\n  };\n\n  ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing;\n  };\n  /**\n   * Hook for toggle blur target series.\n   * Can be used in marker for blur or leave blur the markers\n   */\n\n\n  ComponentView.prototype.toggleBlurSeries = function (seriesModels, isBlur, ecModel) {// Do nothing;\n  };\n  /**\n   * Traverse the new rendered elements.\n   *\n   * It will traverse the new added element in progressive rendering.\n   * And traverse all in normal rendering.\n   */\n\n\n  ComponentView.prototype.eachRendered = function (cb) {\n    var group = this.group;\n\n    if (group) {\n      group.traverse(cb);\n    }\n  };\n\n  return ComponentView;\n}();\nenableClassExtend(ComponentView);\nenableClassManagement(ComponentView);\n\n/**\n * @return {string} If large mode changed, return string 'reset';\n */\n\nfunction createRenderPlanner() {\n  var inner = makeInner();\n  return function (seriesModel) {\n    var fields = inner(seriesModel);\n    var pipelineContext = seriesModel.pipelineContext;\n    var originalLarge = !!fields.large;\n    var originalProgressive = !!fields.progressiveRender; // FIXME: if the planner works on a filtered series, `pipelineContext` does not\n    // exists. See #11611 . Probably we need to modify this structure, see the comment\n    // on `performRawSeries` in `Schedular.js`.\n\n    var large = fields.large = !!(pipelineContext && pipelineContext.large);\n    var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender);\n    return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset';\n  };\n}\n\nvar inner$2 = makeInner();\nvar renderPlanner = createRenderPlanner();\n\nvar ChartView =\n/** @class */\nfunction () {\n  function ChartView() {\n    this.group = new Group();\n    this.uid = getUID('viewChart');\n    this.renderTask = createTask({\n      plan: renderTaskPlan,\n      reset: renderTaskReset\n    });\n    this.renderTask.context = {\n      view: this\n    };\n  }\n\n  ChartView.prototype.init = function (ecModel, api) {};\n\n  ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {\n    if (\"development\" !== 'production') {\n      throw new Error('render method must been implemented');\n    }\n  };\n  /**\n   * Highlight series or specified data item.\n   */\n\n\n  ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n    var data = seriesModel.getData(payload && payload.dataType);\n\n    if (!data) {\n      if (\"development\" !== 'production') {\n        error(\"Unknown dataType \" + payload.dataType);\n      }\n\n      return;\n    }\n\n    toggleHighlight(data, payload, 'emphasis');\n  };\n  /**\n   * Downplay series or specified data item.\n   */\n\n\n  ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n    var data = seriesModel.getData(payload && payload.dataType);\n\n    if (!data) {\n      if (\"development\" !== 'production') {\n        error(\"Unknown dataType \" + payload.dataType);\n      }\n\n      return;\n    }\n\n    toggleHighlight(data, payload, 'normal');\n  };\n  /**\n   * Remove self.\n   */\n\n\n  ChartView.prototype.remove = function (ecModel, api) {\n    this.group.removeAll();\n  };\n  /**\n   * Dispose self.\n   */\n\n\n  ChartView.prototype.dispose = function (ecModel, api) {};\n\n  ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {\n    this.render(seriesModel, ecModel, api, payload);\n  }; // FIXME never used?\n\n\n  ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {\n    this.render(seriesModel, ecModel, api, payload);\n  }; // FIXME never used?\n\n\n  ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {\n    this.render(seriesModel, ecModel, api, payload);\n  };\n  /**\n   * Traverse the new rendered elements.\n   *\n   * It will traverse the new added element in progressive rendering.\n   * And traverse all in normal rendering.\n   */\n\n\n  ChartView.prototype.eachRendered = function (cb) {\n    traverseElements(this.group, cb);\n  };\n\n  ChartView.markUpdateMethod = function (payload, methodName) {\n    inner$2(payload).updateMethod = methodName;\n  };\n\n  ChartView.protoInitialize = function () {\n    var proto = ChartView.prototype;\n    proto.type = 'chart';\n  }();\n\n  return ChartView;\n}();\n/**\n * Set state of single element\n */\n\nfunction elSetState(el, state, highlightDigit) {\n  if (el && isHighDownDispatcher(el)) {\n    (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit);\n  }\n}\n\nfunction toggleHighlight(data, payload, state) {\n  var dataIndex = queryDataIndex(data, payload);\n  var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null;\n\n  if (dataIndex != null) {\n    each(normalizeToArray(dataIndex), function (dataIdx) {\n      elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);\n    });\n  } else {\n    data.eachItemGraphicEl(function (el) {\n      elSetState(el, state, highlightDigit);\n    });\n  }\n}\n\nenableClassExtend(ChartView, ['dispose']);\nenableClassManagement(ChartView);\n\nfunction renderTaskPlan(context) {\n  return renderPlanner(context.model);\n}\n\nfunction renderTaskReset(context) {\n  var seriesModel = context.model;\n  var ecModel = context.ecModel;\n  var api = context.api;\n  var payload = context.payload; // FIXME: remove updateView updateVisual\n\n  var progressiveRender = seriesModel.pipelineContext.progressiveRender;\n  var view = context.view;\n  var updateMethod = payload && inner$2(payload).updateMethod;\n  var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount\n  // is less than progressive threshold.\n  : 'render';\n\n  if (methodName !== 'render') {\n    view[methodName](seriesModel, ecModel, api, payload);\n  }\n\n  return progressMethodMap[methodName];\n}\n\nvar progressMethodMap = {\n  incrementalPrepareRender: {\n    progress: function (params, context) {\n      context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload);\n    }\n  },\n  render: {\n    // Put view.render in `progress` to support appendData. But in this case\n    // view.render should not be called in reset, otherwise it will be called\n    // twise. Use `forceFirstProgress` to make sure that view.render is called\n    // in any cases.\n    forceFirstProgress: true,\n    progress: function (params, context) {\n      context.view.render(context.model, context.ecModel, context.api, context.payload);\n    }\n  }\n};\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar ORIGIN_METHOD = '\\0__throttleOriginMethod';\nvar RATE = '\\0__throttleRate';\nvar THROTTLE_TYPE = '\\0__throttleType';\n/**\n * @public\n * @param {(Function)} fn\n * @param {number} [delay=0] Unit: ms.\n * @param {boolean} [debounce=false]\n *        true: If call interval less than `delay`, only the last call works.\n *        false: If call interval less than `delay, call works on fixed rate.\n * @return {(Function)} throttled fn.\n */\n\nfunction throttle(fn, delay, debounce) {\n  var currCall;\n  var lastCall = 0;\n  var lastExec = 0;\n  var timer = null;\n  var diff;\n  var scope;\n  var args;\n  var debounceNextCall;\n  delay = delay || 0;\n\n  function exec() {\n    lastExec = new Date().getTime();\n    timer = null;\n    fn.apply(scope, args || []);\n  }\n\n  var cb = function () {\n    var cbArgs = [];\n\n    for (var _i = 0; _i < arguments.length; _i++) {\n      cbArgs[_i] = arguments[_i];\n    }\n\n    currCall = new Date().getTime();\n    scope = this;\n    args = cbArgs;\n    var thisDelay = debounceNextCall || delay;\n    var thisDebounce = debounceNextCall || debounce;\n    debounceNextCall = null;\n    diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;\n    clearTimeout(timer); // Here we should make sure that: the `exec` SHOULD NOT be called later\n    // than a new call of `cb`, that is, preserving the command order. Consider\n    // calculating \"scale rate\" when roaming as an example. When a call of `cb`\n    // happens, either the `exec` is called dierectly, or the call is delayed.\n    // But the delayed call should never be later than next call of `cb`. Under\n    // this assurance, we can simply update view state each time `dispatchAction`\n    // triggered by user roaming, but not need to add extra code to avoid the\n    // state being \"rolled-back\".\n\n    if (thisDebounce) {\n      timer = setTimeout(exec, thisDelay);\n    } else {\n      if (diff >= 0) {\n        exec();\n      } else {\n        timer = setTimeout(exec, -diff);\n      }\n    }\n\n    lastCall = currCall;\n  };\n  /**\n   * Clear throttle.\n   * @public\n   */\n\n\n  cb.clear = function () {\n    if (timer) {\n      clearTimeout(timer);\n      timer = null;\n    }\n  };\n  /**\n   * Enable debounce once.\n   */\n\n\n  cb.debounceNextCall = function (debounceDelay) {\n    debounceNextCall = debounceDelay;\n  };\n\n  return cb;\n}\n/**\n * Create throttle method or update throttle rate.\n *\n * @example\n * ComponentView.prototype.render = function () {\n *     ...\n *     throttle.createOrUpdate(\n *         this,\n *         '_dispatchAction',\n *         this.model.get('throttle'),\n *         'fixRate'\n *     );\n * };\n * ComponentView.prototype.remove = function () {\n *     throttle.clear(this, '_dispatchAction');\n * };\n * ComponentView.prototype.dispose = function () {\n *     throttle.clear(this, '_dispatchAction');\n * };\n *\n */\n\nfunction createOrUpdate(obj, fnAttr, rate, throttleType) {\n  var fn = obj[fnAttr];\n\n  if (!fn) {\n    return;\n  }\n\n  var originFn = fn[ORIGIN_METHOD] || fn;\n  var lastThrottleType = fn[THROTTLE_TYPE];\n  var lastRate = fn[RATE];\n\n  if (lastRate !== rate || lastThrottleType !== throttleType) {\n    if (rate == null || !throttleType) {\n      return obj[fnAttr] = originFn;\n    }\n\n    fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce');\n    fn[ORIGIN_METHOD] = originFn;\n    fn[THROTTLE_TYPE] = throttleType;\n    fn[RATE] = rate;\n  }\n\n  return fn;\n}\n/**\n * Clear throttle. Example see throttle.createOrUpdate.\n */\n\nfunction clear(obj, fnAttr) {\n  var fn = obj[fnAttr];\n\n  if (fn && fn[ORIGIN_METHOD]) {\n    // Clear throttle\n    fn.clear && fn.clear();\n    obj[fnAttr] = fn[ORIGIN_METHOD];\n  }\n}\n\nvar inner$3 = makeInner();\nvar defaultStyleMappers = {\n  itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true),\n  lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true)\n};\nvar defaultColorKey = {\n  lineStyle: 'stroke',\n  itemStyle: 'fill'\n};\n\nfunction getStyleMapper(seriesModel, stylePath) {\n  var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath];\n\n  if (!styleMapper) {\n    console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n    return defaultStyleMappers.itemStyle;\n  }\n\n  return styleMapper;\n}\n\nfunction getDefaultColorKey(seriesModel, stylePath) {\n  // return defaultColorKey[stylePath] ||\n  var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath];\n\n  if (!colorKey) {\n    console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n    return 'fill';\n  }\n\n  return colorKey;\n}\n\nvar seriesStyleTask = {\n  createOnAllSeries: true,\n  performRawSeries: true,\n  reset: function (seriesModel, ecModel) {\n    var data = seriesModel.getData();\n    var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n    var styleModel = seriesModel.getModel(stylePath);\n    var getStyle = getStyleMapper(seriesModel, stylePath);\n    var globalStyle = getStyle(styleModel);\n    var decalOption = styleModel.getShallow('decal');\n\n    if (decalOption) {\n      data.setVisual('decal', decalOption);\n      decalOption.dirty = true;\n    } // TODO\n\n\n    var colorKey = getDefaultColorKey(seriesModel, stylePath);\n    var color = globalStyle[colorKey]; // TODO style callback\n\n    var colorCallback = isFunction(color) ? color : null;\n    var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; // Get from color palette by default.\n\n    if (!globalStyle[colorKey] || colorCallback || hasAutoColor) {\n      // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT\n      // make it effect palette. Because some scenarios users need to make some series\n      // transparent or as background, which should better not effect the palette.\n      var colorPalette = seriesModel.getColorFromPalette( // TODO series count changed.\n      seriesModel.name, null, ecModel.getSeriesCount());\n\n      if (!globalStyle[colorKey]) {\n        globalStyle[colorKey] = colorPalette;\n        data.setVisual('colorFromPalette', true);\n      }\n\n      globalStyle.fill = globalStyle.fill === 'auto' || isFunction(globalStyle.fill) ? colorPalette : globalStyle.fill;\n      globalStyle.stroke = globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke) ? colorPalette : globalStyle.stroke;\n    }\n\n    data.setVisual('style', globalStyle);\n    data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded\n\n    if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) {\n      data.setVisual('colorFromPalette', false);\n      return {\n        dataEach: function (data, idx) {\n          var dataParams = seriesModel.getDataParams(idx);\n          var itemStyle = extend({}, globalStyle);\n          itemStyle[colorKey] = colorCallback(dataParams);\n          data.setItemVisual(idx, 'style', itemStyle);\n        }\n      };\n    }\n  }\n};\nvar sharedModel = new Model();\nvar dataStyleTask = {\n  createOnAllSeries: true,\n  performRawSeries: true,\n  reset: function (seriesModel, ecModel) {\n    if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) {\n      return;\n    }\n\n    var data = seriesModel.getData();\n    var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n    var getStyle = getStyleMapper(seriesModel, stylePath);\n    var colorKey = data.getVisual('drawType');\n    return {\n      dataEach: data.hasItemOption ? function (data, idx) {\n        // Not use getItemModel for performance considuration\n        var rawItem = data.getRawDataItem(idx);\n\n        if (rawItem && rawItem[stylePath]) {\n          sharedModel.option = rawItem[stylePath];\n          var style = getStyle(sharedModel);\n          var existsStyle = data.ensureUniqueItemVisual(idx, 'style');\n          extend(existsStyle, style);\n\n          if (sharedModel.option.decal) {\n            data.setItemVisual(idx, 'decal', sharedModel.option.decal);\n            sharedModel.option.decal.dirty = true;\n          }\n\n          if (colorKey in style) {\n            data.setItemVisual(idx, 'colorFromPalette', false);\n          }\n        }\n      } : null\n    };\n  }\n}; // Pick color from palette for the data which has not been set with color yet.\n// Note: do not support stream rendering. No such cases yet.\n\nvar dataColorPaletteTask = {\n  performRawSeries: true,\n  overallReset: function (ecModel) {\n    // Each type of series uses one scope.\n    // Pie and funnel are using different scopes.\n    var paletteScopeGroupByType = createHashMap();\n    ecModel.eachSeries(function (seriesModel) {\n      var colorBy = seriesModel.getColorBy();\n\n      if (seriesModel.isColorBySeries()) {\n        return;\n      }\n\n      var key = seriesModel.type + '-' + colorBy;\n      var colorScope = paletteScopeGroupByType.get(key);\n\n      if (!colorScope) {\n        colorScope = {};\n        paletteScopeGroupByType.set(key, colorScope);\n      }\n\n      inner$3(seriesModel).scope = colorScope;\n    });\n    ecModel.eachSeries(function (seriesModel) {\n      if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) {\n        return;\n      }\n\n      var dataAll = seriesModel.getRawData();\n      var idxMap = {};\n      var data = seriesModel.getData();\n      var colorScope = inner$3(seriesModel).scope;\n      var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';\n      var colorKey = getDefaultColorKey(seriesModel, stylePath);\n      data.each(function (idx) {\n        var rawIdx = data.getRawIndex(idx);\n        idxMap[rawIdx] = idx;\n      }); // Iterate on data before filtered. To make sure color from palette can be\n      // Consistent when toggling legend.\n\n      dataAll.each(function (rawIdx) {\n        var idx = idxMap[rawIdx];\n        var fromPalette = data.getItemVisual(idx, 'colorFromPalette'); // Get color from palette for each data only when the color is inherited from series color, which is\n        // also picked from color palette. So following situation is not in the case:\n        // 1. series.itemStyle.color is set\n        // 2. color is encoded by visualMap\n\n        if (fromPalette) {\n          var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n          var name_1 = dataAll.getName(rawIdx) || rawIdx + '';\n          var dataCount = dataAll.count();\n          itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount);\n        }\n      });\n    });\n  }\n};\n\nvar PI$3 = Math.PI;\n/**\n * @param {module:echarts/ExtensionAPI} api\n * @param {Object} [opts]\n * @param {string} [opts.text]\n * @param {string} [opts.color]\n * @param {string} [opts.textColor]\n * @return {module:zrender/Element}\n */\n\nfunction defaultLoading(api, opts) {\n  opts = opts || {};\n  defaults(opts, {\n    text: 'loading',\n    textColor: '#000',\n    fontSize: 12,\n    fontWeight: 'normal',\n    fontStyle: 'normal',\n    fontFamily: 'sans-serif',\n    maskColor: 'rgba(255, 255, 255, 0.8)',\n    showSpinner: true,\n    color: '#5470c6',\n    spinnerRadius: 10,\n    lineWidth: 5,\n    zlevel: 0\n  });\n  var group = new Group();\n  var mask = new Rect({\n    style: {\n      fill: opts.maskColor\n    },\n    zlevel: opts.zlevel,\n    z: 10000\n  });\n  group.add(mask);\n  var textContent = new ZRText({\n    style: {\n      text: opts.text,\n      fill: opts.textColor,\n      fontSize: opts.fontSize,\n      fontWeight: opts.fontWeight,\n      fontStyle: opts.fontStyle,\n      fontFamily: opts.fontFamily\n    },\n    zlevel: opts.zlevel,\n    z: 10001\n  });\n  var labelRect = new Rect({\n    style: {\n      fill: 'none'\n    },\n    textContent: textContent,\n    textConfig: {\n      position: 'right',\n      distance: 10\n    },\n    zlevel: opts.zlevel,\n    z: 10001\n  });\n  group.add(labelRect);\n  var arc;\n\n  if (opts.showSpinner) {\n    arc = new Arc({\n      shape: {\n        startAngle: -PI$3 / 2,\n        endAngle: -PI$3 / 2 + 0.1,\n        r: opts.spinnerRadius\n      },\n      style: {\n        stroke: opts.color,\n        lineCap: 'round',\n        lineWidth: opts.lineWidth\n      },\n      zlevel: opts.zlevel,\n      z: 10001\n    });\n    arc.animateShape(true).when(1000, {\n      endAngle: PI$3 * 3 / 2\n    }).start('circularInOut');\n    arc.animateShape(true).when(1000, {\n      startAngle: PI$3 * 3 / 2\n    }).delay(300).start('circularInOut');\n    group.add(arc);\n  } // Inject resize\n\n\n  group.resize = function () {\n    var textWidth = textContent.getBoundingRect().width;\n    var r = opts.showSpinner ? opts.spinnerRadius : 0; // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2\n    // textDistance needs to be calculated when both animation and text exist\n\n    var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) // only show the text\n    + (opts.showSpinner ? 0 : textWidth / 2) // only show the spinner\n    + (textWidth ? 0 : r);\n    var cy = api.getHeight() / 2;\n    opts.showSpinner && arc.setShape({\n      cx: cx,\n      cy: cy\n    });\n    labelRect.setShape({\n      x: cx - r,\n      y: cy - r,\n      width: r * 2,\n      height: r * 2\n    });\n    mask.setShape({\n      x: 0,\n      y: 0,\n      width: api.getWidth(),\n      height: api.getHeight()\n    });\n  };\n\n  group.resize();\n  return group;\n}\n\nvar Scheduler =\n/** @class */\nfunction () {\n  function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {\n    // key: handlerUID\n    this._stageTaskMap = createHashMap();\n    this.ecInstance = ecInstance;\n    this.api = api; // Fix current processors in case that in some rear cases that\n    // processors might be registered after echarts instance created.\n    // Register processors incrementally for a echarts instance is\n    // not supported by this stream architecture.\n\n    dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();\n    visualHandlers = this._visualHandlers = visualHandlers.slice();\n    this._allHandlers = dataProcessorHandlers.concat(visualHandlers);\n  }\n\n  Scheduler.prototype.restoreData = function (ecModel, payload) {\n    // TODO: Only restore needed series and components, but not all components.\n    // Currently `restoreData` of all of the series and component will be called.\n    // But some independent components like `title`, `legend`, `graphic`, `toolbox`,\n    // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`,\n    // and some components like coordinate system, axes, dataZoom, visualMap only\n    // need their target series refresh.\n    // (1) If we are implementing this feature some day, we should consider these cases:\n    // if a data processor depends on a component (e.g., dataZoomProcessor depends\n    // on the settings of `dataZoom`), it should be re-performed if the component\n    // is modified by `setOption`.\n    // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`,\n    // it should be re-performed when the result array of `getTargetSeries` changed.\n    // We use `dependencies` to cover these issues.\n    // (3) How to update target series when coordinate system related components modified.\n    // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty,\n    // and this case all of the tasks will be set as dirty.\n    ecModel.restoreData(payload); // Theoretically an overall task not only depends on each of its target series, but also\n    // depends on all of the series.\n    // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks\n    // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure\n    // that the overall task is set as dirty and to be performed, otherwise it probably cause\n    // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it\n    // probably cause state chaos (consider `dataZoomProcessor`).\n\n    this._stageTaskMap.each(function (taskRecord) {\n      var overallTask = taskRecord.overallTask;\n      overallTask && overallTask.dirty();\n    });\n  }; // If seriesModel provided, incremental threshold is check by series data.\n\n\n  Scheduler.prototype.getPerformArgs = function (task, isBlock) {\n    // For overall task\n    if (!task.__pipeline) {\n      return;\n    }\n\n    var pipeline = this._pipelineMap.get(task.__pipeline.id);\n\n    var pCtx = pipeline.context;\n    var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex;\n    var step = incremental ? pipeline.step : null;\n    var modDataCount = pCtx && pCtx.modDataCount;\n    var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null;\n    return {\n      step: step,\n      modBy: modBy,\n      modDataCount: modDataCount\n    };\n  };\n\n  Scheduler.prototype.getPipeline = function (pipelineId) {\n    return this._pipelineMap.get(pipelineId);\n  };\n  /**\n   * Current, progressive rendering starts from visual and layout.\n   * Always detect render mode in the same stage, avoiding that incorrect\n   * detection caused by data filtering.\n   * Caution:\n   * `updateStreamModes` use `seriesModel.getData()`.\n   */\n\n\n  Scheduler.prototype.updateStreamModes = function (seriesModel, view) {\n    var pipeline = this._pipelineMap.get(seriesModel.uid);\n\n    var data = seriesModel.getData();\n    var dataLen = data.count(); // `progressiveRender` means that can render progressively in each\n    // animation frame. Note that some types of series do not provide\n    // `view.incrementalPrepareRender` but support `chart.appendData`. We\n    // use the term `incremental` but not `progressive` to describe the\n    // case that `chart.appendData`.\n\n    var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;\n    var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.\n    // see `test/candlestick-large3.html`\n\n    var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;\n    seriesModel.pipelineContext = pipeline.context = {\n      progressiveRender: progressiveRender,\n      modDataCount: modDataCount,\n      large: large\n    };\n  };\n\n  Scheduler.prototype.restorePipelines = function (ecModel) {\n    var scheduler = this;\n    var pipelineMap = scheduler._pipelineMap = createHashMap();\n    ecModel.eachSeries(function (seriesModel) {\n      var progressive = seriesModel.getProgressive();\n      var pipelineId = seriesModel.uid;\n      pipelineMap.set(pipelineId, {\n        id: pipelineId,\n        head: null,\n        tail: null,\n        threshold: seriesModel.getProgressiveThreshold(),\n        progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),\n        blockIndex: -1,\n        step: Math.round(progressive || 700),\n        count: 0\n      });\n\n      scheduler._pipe(seriesModel, seriesModel.dataTask);\n    });\n  };\n\n  Scheduler.prototype.prepareStageTasks = function () {\n    var stageTaskMap = this._stageTaskMap;\n    var ecModel = this.api.getModel();\n    var api = this.api;\n    each(this._allHandlers, function (handler) {\n      var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {});\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        // Currently do not need to support to sepecify them both.\n        errMsg = '\"reset\" and \"overallReset\" must not be both specified.';\n      }\n\n      assert(!(handler.reset && handler.overallReset), errMsg);\n      handler.reset && this._createSeriesStageTask(handler, record, ecModel, api);\n      handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api);\n    }, this);\n  };\n\n  Scheduler.prototype.prepareView = function (view, model, ecModel, api) {\n    var renderTask = view.renderTask;\n    var context = renderTask.context;\n    context.model = model;\n    context.ecModel = ecModel;\n    context.api = api;\n    renderTask.__block = !view.incrementalPrepareRender;\n\n    this._pipe(model, renderTask);\n  };\n\n  Scheduler.prototype.performDataProcessorTasks = function (ecModel, payload) {\n    // If we do not use `block` here, it should be considered when to update modes.\n    this._performStageTasks(this._dataProcessorHandlers, ecModel, payload, {\n      block: true\n    });\n  };\n\n  Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) {\n    this._performStageTasks(this._visualHandlers, ecModel, payload, opt);\n  };\n\n  Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) {\n    opt = opt || {};\n    var unfinished = false;\n    var scheduler = this;\n    each(stageHandlers, function (stageHandler, idx) {\n      if (opt.visualType && opt.visualType !== stageHandler.visualType) {\n        return;\n      }\n\n      var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);\n\n      var seriesTaskMap = stageHandlerRecord.seriesTaskMap;\n      var overallTask = stageHandlerRecord.overallTask;\n\n      if (overallTask) {\n        var overallNeedDirty_1;\n        var agentStubMap = overallTask.agentStubMap;\n        agentStubMap.each(function (stub) {\n          if (needSetDirty(opt, stub)) {\n            stub.dirty();\n            overallNeedDirty_1 = true;\n          }\n        });\n        overallNeedDirty_1 && overallTask.dirty();\n        scheduler.updatePayload(overallTask, payload);\n        var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block); // Execute stubs firstly, which may set the overall task dirty,\n        // then execute the overall task. And stub will call seriesModel.setData,\n        // which ensures that in the overallTask seriesModel.getData() will not\n        // return incorrect data.\n\n        agentStubMap.each(function (stub) {\n          stub.perform(performArgs_1);\n        });\n\n        if (overallTask.perform(performArgs_1)) {\n          unfinished = true;\n        }\n      } else if (seriesTaskMap) {\n        seriesTaskMap.each(function (task, pipelineId) {\n          if (needSetDirty(opt, task)) {\n            task.dirty();\n          }\n\n          var performArgs = scheduler.getPerformArgs(task, opt.block); // FIXME\n          // if intending to declare `performRawSeries` in handlers, only\n          // stream-independent (specifically, data item independent) operations can be\n          // performed. Because if a series is filtered, most of the tasks will not\n          // be performed. A stream-dependent operation probably cause wrong biz logic.\n          // Perhaps we should not provide a separate callback for this case instead\n          // of providing the config `performRawSeries`. The stream-dependent operations\n          // and stream-independent operations should better not be mixed.\n\n          performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);\n          scheduler.updatePayload(task, payload);\n\n          if (task.perform(performArgs)) {\n            unfinished = true;\n          }\n        });\n      }\n    });\n\n    function needSetDirty(opt, task) {\n      return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));\n    }\n\n    this.unfinished = unfinished || this.unfinished;\n  };\n\n  Scheduler.prototype.performSeriesTasks = function (ecModel) {\n    var unfinished;\n    ecModel.eachSeries(function (seriesModel) {\n      // Progress to the end for dataInit and dataRestore.\n      unfinished = seriesModel.dataTask.perform() || unfinished;\n    });\n    this.unfinished = unfinished || this.unfinished;\n  };\n\n  Scheduler.prototype.plan = function () {\n    // Travel pipelines, check block.\n    this._pipelineMap.each(function (pipeline) {\n      var task = pipeline.tail;\n\n      do {\n        if (task.__block) {\n          pipeline.blockIndex = task.__idxInPipeline;\n          break;\n        }\n\n        task = task.getUpstream();\n      } while (task);\n    });\n  };\n\n  Scheduler.prototype.updatePayload = function (task, payload) {\n    payload !== 'remain' && (task.context.payload = payload);\n  };\n\n  Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n    var scheduler = this;\n    var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; // The count of stages are totally about only several dozen, so\n    // do not need to reuse the map.\n\n    var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap();\n    var seriesType = stageHandler.seriesType;\n    var getTargetSeries = stageHandler.getTargetSeries; // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,\n    // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,\n    // it works but it may cause other irrelevant charts blocked.\n\n    if (stageHandler.createOnAllSeries) {\n      ecModel.eachRawSeries(create);\n    } else if (seriesType) {\n      ecModel.eachRawSeriesByType(seriesType, create);\n    } else if (getTargetSeries) {\n      getTargetSeries(ecModel, api).each(create);\n    }\n\n    function create(seriesModel) {\n      var pipelineId = seriesModel.uid; // Init tasks for each seriesModel only once.\n      // Reuse original task instance.\n\n      var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({\n        plan: seriesTaskPlan,\n        reset: seriesTaskReset,\n        count: seriesTaskCount\n      }));\n      task.context = {\n        model: seriesModel,\n        ecModel: ecModel,\n        api: api,\n        // PENDING: `useClearVisual` not used?\n        useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,\n        plan: stageHandler.plan,\n        reset: stageHandler.reset,\n        scheduler: scheduler\n      };\n\n      scheduler._pipe(seriesModel, task);\n    }\n  };\n\n  Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n    var scheduler = this;\n    var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage.\n    || createTask({\n      reset: overallTaskReset\n    });\n    overallTask.context = {\n      ecModel: ecModel,\n      api: api,\n      overallReset: stageHandler.overallReset,\n      scheduler: scheduler\n    };\n    var oldAgentStubMap = overallTask.agentStubMap; // The count of stages are totally about only several dozen, so\n    // do not need to reuse the map.\n\n    var newAgentStubMap = overallTask.agentStubMap = createHashMap();\n    var seriesType = stageHandler.seriesType;\n    var getTargetSeries = stageHandler.getTargetSeries;\n    var overallProgress = true;\n    var shouldOverallTaskDirty = false; // FIXME:TS never used, so comment it\n    // let modifyOutputEnd = stageHandler.modifyOutputEnd;\n    // An overall task with seriesType detected or has `getTargetSeries`, we add\n    // stub in each pipelines, it will set the overall task dirty when the pipeline\n    // progress. Moreover, to avoid call the overall task each frame (too frequent),\n    // we set the pipeline block.\n\n    var errMsg = '';\n\n    if (\"development\" !== 'production') {\n      errMsg = '\"createOnAllSeries\" is not supported for \"overallReset\", ' + 'because it will block all streams.';\n    }\n\n    assert(!stageHandler.createOnAllSeries, errMsg);\n\n    if (seriesType) {\n      ecModel.eachRawSeriesByType(seriesType, createStub);\n    } else if (getTargetSeries) {\n      getTargetSeries(ecModel, api).each(createStub);\n    } // Otherwise, (usually it is legacy case), the overall task will only be\n    // executed when upstream is dirty. Otherwise the progressive rendering of all\n    // pipelines will be disabled unexpectedly. But it still needs stubs to receive\n    // dirty info from upstream.\n    else {\n        overallProgress = false;\n        each(ecModel.getSeries(), createStub);\n      }\n\n    function createStub(seriesModel) {\n      var pipelineId = seriesModel.uid;\n      var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || ( // When the result of `getTargetSeries` changed, the overallTask\n      // should be set as dirty and re-performed.\n      shouldOverallTaskDirty = true, createTask({\n        reset: stubReset,\n        onDirty: stubOnDirty\n      })));\n      stub.context = {\n        model: seriesModel,\n        overallProgress: overallProgress // FIXME:TS never used, so comment it\n        // modifyOutputEnd: modifyOutputEnd\n\n      };\n      stub.agent = overallTask;\n      stub.__block = overallProgress;\n\n      scheduler._pipe(seriesModel, stub);\n    }\n\n    if (shouldOverallTaskDirty) {\n      overallTask.dirty();\n    }\n  };\n\n  Scheduler.prototype._pipe = function (seriesModel, task) {\n    var pipelineId = seriesModel.uid;\n\n    var pipeline = this._pipelineMap.get(pipelineId);\n\n    !pipeline.head && (pipeline.head = task);\n    pipeline.tail && pipeline.tail.pipe(task);\n    pipeline.tail = task;\n    task.__idxInPipeline = pipeline.count++;\n    task.__pipeline = pipeline;\n  };\n\n  Scheduler.wrapStageHandler = function (stageHandler, visualType) {\n    if (isFunction(stageHandler)) {\n      stageHandler = {\n        overallReset: stageHandler,\n        seriesType: detectSeriseType(stageHandler)\n      };\n    }\n\n    stageHandler.uid = getUID('stageHandler');\n    visualType && (stageHandler.visualType = visualType);\n    return stageHandler;\n  };\n  return Scheduler;\n}();\n\nfunction overallTaskReset(context) {\n  context.overallReset(context.ecModel, context.api, context.payload);\n}\n\nfunction stubReset(context) {\n  return context.overallProgress && stubProgress;\n}\n\nfunction stubProgress() {\n  this.agent.dirty();\n  this.getDownstream().dirty();\n}\n\nfunction stubOnDirty() {\n  this.agent && this.agent.dirty();\n}\n\nfunction seriesTaskPlan(context) {\n  return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null;\n}\n\nfunction seriesTaskReset(context) {\n  if (context.useClearVisual) {\n    context.data.clearAllVisual();\n  }\n\n  var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload));\n  return resetDefines.length > 1 ? map(resetDefines, function (v, idx) {\n    return makeSeriesTaskProgress(idx);\n  }) : singleSeriesTaskProgress;\n}\n\nvar singleSeriesTaskProgress = makeSeriesTaskProgress(0);\n\nfunction makeSeriesTaskProgress(resetDefineIdx) {\n  return function (params, context) {\n    var data = context.data;\n    var resetDefine = context.resetDefines[resetDefineIdx];\n\n    if (resetDefine && resetDefine.dataEach) {\n      for (var i = params.start; i < params.end; i++) {\n        resetDefine.dataEach(data, i);\n      }\n    } else if (resetDefine && resetDefine.progress) {\n      resetDefine.progress(params, data);\n    }\n  };\n}\n\nfunction seriesTaskCount(context) {\n  return context.data.count();\n}\n/**\n * Only some legacy stage handlers (usually in echarts extensions) are pure function.\n * To ensure that they can work normally, they should work in block mode, that is,\n * they should not be started util the previous tasks finished. So they cause the\n * progressive rendering disabled. We try to detect the series type, to narrow down\n * the block range to only the series type they concern, but not all series.\n */\n\n\nfunction detectSeriseType(legacyFunc) {\n  seriesType = null;\n\n  try {\n    // Assume there is no async when calling `eachSeriesByType`.\n    legacyFunc(ecModelMock, apiMock);\n  } catch (e) {}\n\n  return seriesType;\n}\n\nvar ecModelMock = {};\nvar apiMock = {};\nvar seriesType;\nmockMethods(ecModelMock, GlobalModel);\nmockMethods(apiMock, ExtensionAPI);\n\necModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {\n  seriesType = type;\n};\n\necModelMock.eachComponent = function (cond) {\n  if (cond.mainType === 'series' && cond.subType) {\n    seriesType = cond.subType;\n  }\n};\n\nfunction mockMethods(target, Clz) {\n  /* eslint-disable */\n  for (var name_1 in Clz.prototype) {\n    // Do not use hasOwnProperty\n    target[name_1] = noop;\n  }\n  /* eslint-enable */\n\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];\nvar lightTheme = {\n  color: colorAll,\n  colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll]\n};\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar contrastColor = '#B9B8CE';\nvar backgroundColor = '#100C2A';\n\nvar axisCommon = function () {\n  return {\n    axisLine: {\n      lineStyle: {\n        color: contrastColor\n      }\n    },\n    splitLine: {\n      lineStyle: {\n        color: '#484753'\n      }\n    },\n    splitArea: {\n      areaStyle: {\n        color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)']\n      }\n    },\n    minorSplitLine: {\n      lineStyle: {\n        color: '#20203B'\n      }\n    }\n  };\n};\n\nvar colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff'];\nvar theme = {\n  darkMode: true,\n  color: colorPalette,\n  backgroundColor: backgroundColor,\n  axisPointer: {\n    lineStyle: {\n      color: '#817f91'\n    },\n    crossStyle: {\n      color: '#817f91'\n    },\n    label: {\n      // TODO Contrast of label backgorundColor\n      color: '#fff'\n    }\n  },\n  legend: {\n    textStyle: {\n      color: contrastColor\n    }\n  },\n  textStyle: {\n    color: contrastColor\n  },\n  title: {\n    textStyle: {\n      color: '#EEF1FA'\n    },\n    subtextStyle: {\n      color: '#B9B8CE'\n    }\n  },\n  toolbox: {\n    iconStyle: {\n      borderColor: contrastColor\n    }\n  },\n  dataZoom: {\n    borderColor: '#71708A',\n    textStyle: {\n      color: contrastColor\n    },\n    brushStyle: {\n      color: 'rgba(135,163,206,0.3)'\n    },\n    handleStyle: {\n      color: '#353450',\n      borderColor: '#C5CBE3'\n    },\n    moveHandleStyle: {\n      color: '#B0B6C3',\n      opacity: 0.3\n    },\n    fillerColor: 'rgba(135,163,206,0.2)',\n    emphasis: {\n      handleStyle: {\n        borderColor: '#91B7F2',\n        color: '#4D587D'\n      },\n      moveHandleStyle: {\n        color: '#636D9A',\n        opacity: 0.7\n      }\n    },\n    dataBackground: {\n      lineStyle: {\n        color: '#71708A',\n        width: 1\n      },\n      areaStyle: {\n        color: '#71708A'\n      }\n    },\n    selectedDataBackground: {\n      lineStyle: {\n        color: '#87A3CE'\n      },\n      areaStyle: {\n        color: '#87A3CE'\n      }\n    }\n  },\n  visualMap: {\n    textStyle: {\n      color: contrastColor\n    }\n  },\n  timeline: {\n    lineStyle: {\n      color: contrastColor\n    },\n    label: {\n      color: contrastColor\n    },\n    controlStyle: {\n      color: contrastColor,\n      borderColor: contrastColor\n    }\n  },\n  calendar: {\n    itemStyle: {\n      color: backgroundColor\n    },\n    dayLabel: {\n      color: contrastColor\n    },\n    monthLabel: {\n      color: contrastColor\n    },\n    yearLabel: {\n      color: contrastColor\n    }\n  },\n  timeAxis: axisCommon(),\n  logAxis: axisCommon(),\n  valueAxis: axisCommon(),\n  categoryAxis: axisCommon(),\n  line: {\n    symbol: 'circle'\n  },\n  graph: {\n    color: colorPalette\n  },\n  gauge: {\n    title: {\n      color: contrastColor\n    },\n    axisLine: {\n      lineStyle: {\n        color: [[1, 'rgba(207,212,219,0.2)']]\n      }\n    },\n    axisLabel: {\n      color: contrastColor\n    },\n    detail: {\n      color: '#EEF1FA'\n    }\n  },\n  candlestick: {\n    itemStyle: {\n      color: '#f64e56',\n      color0: '#54ea92',\n      borderColor: '#f64e56',\n      borderColor0: '#54ea92' // borderColor: '#ca2824',\n      // borderColor0: '#09a443'\n\n    }\n  }\n};\ntheme.categoryAxis.splitLine.show = false;\n\n/**\n * Usage of query:\n * `chart.on('click', query, handler);`\n * The `query` can be:\n * + The component type query string, only `mainType` or `mainType.subType`,\n *   like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.\n * + The component query object, like:\n *   `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,\n *   `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.\n * + The data query object, like:\n *   `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.\n * + The other query object (cmponent customized query), like:\n *   `{element: 'some'}` (only available in custom series).\n *\n * Caveat: If a prop in the `query` object is `null/undefined`, it is the\n * same as there is no such prop in the `query` object.\n */\n\nvar ECEventProcessor =\n/** @class */\nfunction () {\n  function ECEventProcessor() {}\n\n  ECEventProcessor.prototype.normalizeQuery = function (query) {\n    var cptQuery = {};\n    var dataQuery = {};\n    var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.\n\n    if (isString(query)) {\n      var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.\n\n      cptQuery.mainType = condCptType.main || null;\n      cptQuery.subType = condCptType.sub || null;\n    } // `query` is an object, convert to {mainType, index, name, id}.\n    else {\n        // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,\n        // can not be used in `compomentModel.filterForExposedEvent`.\n        var suffixes_1 = ['Index', 'Name', 'Id'];\n        var dataKeys_1 = {\n          name: 1,\n          dataIndex: 1,\n          dataType: 1\n        };\n        each(query, function (val, key) {\n          var reserved = false;\n\n          for (var i = 0; i < suffixes_1.length; i++) {\n            var propSuffix = suffixes_1[i];\n            var suffixPos = key.lastIndexOf(propSuffix);\n\n            if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {\n              var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.\n\n              if (mainType !== 'data') {\n                cptQuery.mainType = mainType;\n                cptQuery[propSuffix.toLowerCase()] = val;\n                reserved = true;\n              }\n            }\n          }\n\n          if (dataKeys_1.hasOwnProperty(key)) {\n            dataQuery[key] = val;\n            reserved = true;\n          }\n\n          if (!reserved) {\n            otherQuery[key] = val;\n          }\n        });\n      }\n\n    return {\n      cptQuery: cptQuery,\n      dataQuery: dataQuery,\n      otherQuery: otherQuery\n    };\n  };\n\n  ECEventProcessor.prototype.filter = function (eventType, query) {\n    // They should be assigned before each trigger call.\n    var eventInfo = this.eventInfo;\n\n    if (!eventInfo) {\n      return true;\n    }\n\n    var targetEl = eventInfo.targetEl;\n    var packedEvent = eventInfo.packedEvent;\n    var model = eventInfo.model;\n    var view = eventInfo.view; // For event like 'globalout'.\n\n    if (!model || !view) {\n      return true;\n    }\n\n    var cptQuery = query.cptQuery;\n    var dataQuery = query.dataQuery;\n    return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent));\n\n    function check(query, host, prop, propOnHost) {\n      return query[prop] == null || host[propOnHost || prop] === query[prop];\n    }\n  };\n\n  ECEventProcessor.prototype.afterTrigger = function () {\n    // Make sure the eventInfo won't be used in next trigger.\n    this.eventInfo = null;\n  };\n\n  return ECEventProcessor;\n}();\n\nvar SYMBOL_PROPS_WITH_CB = ['symbol', 'symbolSize', 'symbolRotate', 'symbolOffset'];\nvar SYMBOL_PROPS = SYMBOL_PROPS_WITH_CB.concat(['symbolKeepAspect']); // Encoding visual for all series include which is filtered for legend drawing\n\nvar seriesSymbolTask = {\n  createOnAllSeries: true,\n  // For legend.\n  performRawSeries: true,\n  reset: function (seriesModel, ecModel) {\n    var data = seriesModel.getData();\n\n    if (seriesModel.legendIcon) {\n      data.setVisual('legendIcon', seriesModel.legendIcon);\n    }\n\n    if (!seriesModel.hasSymbolVisual) {\n      return;\n    }\n\n    var symbolOptions = {};\n    var symbolOptionsCb = {};\n    var hasCallback = false;\n\n    for (var i = 0; i < SYMBOL_PROPS_WITH_CB.length; i++) {\n      var symbolPropName = SYMBOL_PROPS_WITH_CB[i];\n      var val = seriesModel.get(symbolPropName);\n\n      if (isFunction(val)) {\n        hasCallback = true;\n        symbolOptionsCb[symbolPropName] = val;\n      } else {\n        symbolOptions[symbolPropName] = val;\n      }\n    }\n\n    symbolOptions.symbol = symbolOptions.symbol || seriesModel.defaultSymbol;\n    data.setVisual(extend({\n      legendIcon: seriesModel.legendIcon || symbolOptions.symbol,\n      symbolKeepAspect: seriesModel.get('symbolKeepAspect')\n    }, symbolOptions)); // Only visible series has each data be visual encoded\n\n    if (ecModel.isSeriesFiltered(seriesModel)) {\n      return;\n    }\n\n    var symbolPropsCb = keys(symbolOptionsCb);\n\n    function dataEach(data, idx) {\n      var rawValue = seriesModel.getRawValue(idx);\n      var params = seriesModel.getDataParams(idx);\n\n      for (var i = 0; i < symbolPropsCb.length; i++) {\n        var symbolPropName = symbolPropsCb[i];\n        data.setItemVisual(idx, symbolPropName, symbolOptionsCb[symbolPropName](rawValue, params));\n      }\n    }\n\n    return {\n      dataEach: hasCallback ? dataEach : null\n    };\n  }\n};\nvar dataSymbolTask = {\n  createOnAllSeries: true,\n  // For legend.\n  performRawSeries: true,\n  reset: function (seriesModel, ecModel) {\n    if (!seriesModel.hasSymbolVisual) {\n      return;\n    } // Only visible series has each data be visual encoded\n\n\n    if (ecModel.isSeriesFiltered(seriesModel)) {\n      return;\n    }\n\n    var data = seriesModel.getData();\n\n    function dataEach(data, idx) {\n      var itemModel = data.getItemModel(idx);\n\n      for (var i = 0; i < SYMBOL_PROPS.length; i++) {\n        var symbolPropName = SYMBOL_PROPS[i];\n        var val = itemModel.getShallow(symbolPropName, true);\n\n        if (val != null) {\n          data.setItemVisual(idx, symbolPropName, val);\n        }\n      }\n    }\n\n    return {\n      dataEach: data.hasItemOption ? dataEach : null\n    };\n  }\n};\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction getItemVisualFromData(data, dataIndex, key) {\n  switch (key) {\n    case 'color':\n      var style = data.getItemVisual(dataIndex, 'style');\n      return style[data.getVisual('drawType')];\n\n    case 'opacity':\n      return data.getItemVisual(dataIndex, 'style').opacity;\n\n    case 'symbol':\n    case 'symbolSize':\n    case 'liftZ':\n      return data.getItemVisual(dataIndex, key);\n\n    default:\n      if (\"development\" !== 'production') {\n        console.warn(\"Unknown visual type \" + key);\n      }\n\n  }\n}\nfunction getVisualFromData(data, key) {\n  switch (key) {\n    case 'color':\n      var style = data.getVisual('style');\n      return style[data.getVisual('drawType')];\n\n    case 'opacity':\n      return data.getVisual('style').opacity;\n\n    case 'symbol':\n    case 'symbolSize':\n    case 'liftZ':\n      return data.getVisual(key);\n\n    default:\n      if (\"development\" !== 'production') {\n        console.warn(\"Unknown visual type \" + key);\n      }\n\n  }\n}\nfunction setItemVisualFromData(data, dataIndex, key, value) {\n  switch (key) {\n    case 'color':\n      // Make sure not sharing style object.\n      var style = data.ensureUniqueItemVisual(dataIndex, 'style');\n      style[data.getVisual('drawType')] = value; // Mark the color has been changed, not from palette anymore\n\n      data.setItemVisual(dataIndex, 'colorFromPalette', false);\n      break;\n\n    case 'opacity':\n      data.ensureUniqueItemVisual(dataIndex, 'style').opacity = value;\n      break;\n\n    case 'symbol':\n    case 'symbolSize':\n    case 'liftZ':\n      data.setItemVisual(dataIndex, key, value);\n      break;\n\n    default:\n      if (\"development\" !== 'production') {\n        console.warn(\"Unknown visual type \" + key);\n      }\n\n  }\n}\n\n// Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect\n\nfunction createLegacyDataSelectAction(seriesType, ecRegisterAction) {\n  function getSeriesIndices(ecModel, payload) {\n    var seriesIndices = [];\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: seriesType,\n      query: payload\n    }, function (seriesModel) {\n      seriesIndices.push(seriesModel.seriesIndex);\n    });\n    return seriesIndices;\n  }\n\n  each([[seriesType + 'ToggleSelect', 'toggleSelect'], [seriesType + 'Select', 'select'], [seriesType + 'UnSelect', 'unselect']], function (eventsMap) {\n    ecRegisterAction(eventsMap[0], function (payload, ecModel, api) {\n      payload = extend({}, payload);\n\n      if (\"development\" !== 'production') {\n        deprecateReplaceLog(payload.type, eventsMap[1]);\n      }\n\n      api.dispatchAction(extend(payload, {\n        type: eventsMap[1],\n        seriesIndex: getSeriesIndices(ecModel, payload)\n      }));\n    });\n  });\n}\n\nfunction handleSeriesLegacySelectEvents(type, eventPostfix, ecIns, ecModel, payload) {\n  var legacyEventName = type + eventPostfix;\n\n  if (!ecIns.isSilent(legacyEventName)) {\n    if (\"development\" !== 'production') {\n      deprecateLog(\"event \" + legacyEventName + \" is deprecated.\");\n    }\n\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'pie'\n    }, function (seriesModel) {\n      var seriesIndex = seriesModel.seriesIndex;\n      var selectedMap = seriesModel.option.selectedMap;\n      var selected = payload.selected;\n\n      for (var i = 0; i < selected.length; i++) {\n        if (selected[i].seriesIndex === seriesIndex) {\n          var data = seriesModel.getData();\n          var dataIndex = queryDataIndex(data, payload.fromActionPayload);\n          ecIns.trigger(legacyEventName, {\n            type: legacyEventName,\n            seriesId: seriesModel.id,\n            name: isArray(dataIndex) ? data.getName(dataIndex[0]) : data.getName(dataIndex),\n            selected: isString(selectedMap) ? selectedMap : extend({}, selectedMap)\n          });\n        }\n      }\n    });\n  }\n}\n\nfunction handleLegacySelectEvents(messageCenter, ecIns, api) {\n  messageCenter.on('selectchanged', function (params) {\n    var ecModel = api.getModel();\n\n    if (params.isFromClick) {\n      handleSeriesLegacySelectEvents('map', 'selectchanged', ecIns, ecModel, params);\n      handleSeriesLegacySelectEvents('pie', 'selectchanged', ecIns, ecModel, params);\n    } else if (params.fromAction === 'select') {\n      handleSeriesLegacySelectEvents('map', 'selected', ecIns, ecModel, params);\n      handleSeriesLegacySelectEvents('pie', 'selected', ecIns, ecModel, params);\n    } else if (params.fromAction === 'unselect') {\n      handleSeriesLegacySelectEvents('map', 'unselected', ecIns, ecModel, params);\n      handleSeriesLegacySelectEvents('pie', 'unselected', ecIns, ecModel, params);\n    }\n  });\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction findEventDispatcher(target, det, returnFirstMatch) {\n  var found;\n\n  while (target) {\n    if (det(target)) {\n      found = target;\n\n      if (returnFirstMatch) {\n        break;\n      }\n    }\n\n    target = target.__hostTarget || target.parent;\n  }\n\n  return found;\n}\n\nvar wmUniqueIndex = Math.round(Math.random() * 9);\nvar supportDefineProperty = typeof Object.defineProperty === 'function';\nvar WeakMap = (function () {\n    function WeakMap() {\n        this._id = '__ec_inner_' + wmUniqueIndex++;\n    }\n    WeakMap.prototype.get = function (key) {\n        return this._guard(key)[this._id];\n    };\n    WeakMap.prototype.set = function (key, value) {\n        var target = this._guard(key);\n        if (supportDefineProperty) {\n            Object.defineProperty(target, this._id, {\n                value: value,\n                enumerable: false,\n                configurable: true\n            });\n        }\n        else {\n            target[this._id] = value;\n        }\n        return this;\n    };\n    WeakMap.prototype[\"delete\"] = function (key) {\n        if (this.has(key)) {\n            delete this._guard(key)[this._id];\n            return true;\n        }\n        return false;\n    };\n    WeakMap.prototype.has = function (key) {\n        return !!this._guard(key)[this._id];\n    };\n    WeakMap.prototype._guard = function (key) {\n        if (key !== Object(key)) {\n            throw TypeError('Value of WeakMap is not a non-null object.');\n        }\n        return key;\n    };\n    return WeakMap;\n}());\n\n/**\n * Triangle shape\n * @inner\n */\n\nvar Triangle = Path.extend({\n  type: 'triangle',\n  shape: {\n    cx: 0,\n    cy: 0,\n    width: 0,\n    height: 0\n  },\n  buildPath: function (path, shape) {\n    var cx = shape.cx;\n    var cy = shape.cy;\n    var width = shape.width / 2;\n    var height = shape.height / 2;\n    path.moveTo(cx, cy - height);\n    path.lineTo(cx + width, cy + height);\n    path.lineTo(cx - width, cy + height);\n    path.closePath();\n  }\n});\n/**\n * Diamond shape\n * @inner\n */\n\nvar Diamond = Path.extend({\n  type: 'diamond',\n  shape: {\n    cx: 0,\n    cy: 0,\n    width: 0,\n    height: 0\n  },\n  buildPath: function (path, shape) {\n    var cx = shape.cx;\n    var cy = shape.cy;\n    var width = shape.width / 2;\n    var height = shape.height / 2;\n    path.moveTo(cx, cy - height);\n    path.lineTo(cx + width, cy);\n    path.lineTo(cx, cy + height);\n    path.lineTo(cx - width, cy);\n    path.closePath();\n  }\n});\n/**\n * Pin shape\n * @inner\n */\n\nvar Pin = Path.extend({\n  type: 'pin',\n  shape: {\n    // x, y on the cusp\n    x: 0,\n    y: 0,\n    width: 0,\n    height: 0\n  },\n  buildPath: function (path, shape) {\n    var x = shape.x;\n    var y = shape.y;\n    var w = shape.width / 5 * 3; // Height must be larger than width\n\n    var h = Math.max(w, shape.height);\n    var r = w / 2; // Dist on y with tangent point and circle center\n\n    var dy = r * r / (h - r);\n    var cy = y - h + r + dy;\n    var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center\n\n    var dx = Math.cos(angle) * r;\n    var tanX = Math.sin(angle);\n    var tanY = Math.cos(angle);\n    var cpLen = r * 0.6;\n    var cpLen2 = r * 0.7;\n    path.moveTo(x - dx, cy + dy);\n    path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);\n    path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y);\n    path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy);\n    path.closePath();\n  }\n});\n/**\n * Arrow shape\n * @inner\n */\n\nvar Arrow = Path.extend({\n  type: 'arrow',\n  shape: {\n    x: 0,\n    y: 0,\n    width: 0,\n    height: 0\n  },\n  buildPath: function (ctx, shape) {\n    var height = shape.height;\n    var width = shape.width;\n    var x = shape.x;\n    var y = shape.y;\n    var dx = width / 3 * 2;\n    ctx.moveTo(x, y);\n    ctx.lineTo(x + dx, y + height);\n    ctx.lineTo(x, y + height / 4 * 3);\n    ctx.lineTo(x - dx, y + height);\n    ctx.lineTo(x, y);\n    ctx.closePath();\n  }\n});\n/**\n * Map of path constructors\n */\n// TODO Use function to build symbol path.\n\nvar symbolCtors = {\n  line: Line,\n  rect: Rect,\n  roundRect: Rect,\n  square: Rect,\n  circle: Circle,\n  diamond: Diamond,\n  pin: Pin,\n  arrow: Arrow,\n  triangle: Triangle\n};\nvar symbolShapeMakers = {\n  line: function (x, y, w, h, shape) {\n    shape.x1 = x;\n    shape.y1 = y + h / 2;\n    shape.x2 = x + w;\n    shape.y2 = y + h / 2;\n  },\n  rect: function (x, y, w, h, shape) {\n    shape.x = x;\n    shape.y = y;\n    shape.width = w;\n    shape.height = h;\n  },\n  roundRect: function (x, y, w, h, shape) {\n    shape.x = x;\n    shape.y = y;\n    shape.width = w;\n    shape.height = h;\n    shape.r = Math.min(w, h) / 4;\n  },\n  square: function (x, y, w, h, shape) {\n    var size = Math.min(w, h);\n    shape.x = x;\n    shape.y = y;\n    shape.width = size;\n    shape.height = size;\n  },\n  circle: function (x, y, w, h, shape) {\n    // Put circle in the center of square\n    shape.cx = x + w / 2;\n    shape.cy = y + h / 2;\n    shape.r = Math.min(w, h) / 2;\n  },\n  diamond: function (x, y, w, h, shape) {\n    shape.cx = x + w / 2;\n    shape.cy = y + h / 2;\n    shape.width = w;\n    shape.height = h;\n  },\n  pin: function (x, y, w, h, shape) {\n    shape.x = x + w / 2;\n    shape.y = y + h / 2;\n    shape.width = w;\n    shape.height = h;\n  },\n  arrow: function (x, y, w, h, shape) {\n    shape.x = x + w / 2;\n    shape.y = y + h / 2;\n    shape.width = w;\n    shape.height = h;\n  },\n  triangle: function (x, y, w, h, shape) {\n    shape.cx = x + w / 2;\n    shape.cy = y + h / 2;\n    shape.width = w;\n    shape.height = h;\n  }\n};\nvar symbolBuildProxies = {};\neach(symbolCtors, function (Ctor, name) {\n  symbolBuildProxies[name] = new Ctor();\n});\nvar SymbolClz = Path.extend({\n  type: 'symbol',\n  shape: {\n    symbolType: '',\n    x: 0,\n    y: 0,\n    width: 0,\n    height: 0\n  },\n  calculateTextPosition: function (out, config, rect) {\n    var res = calculateTextPosition(out, config, rect);\n    var shape = this.shape;\n\n    if (shape && shape.symbolType === 'pin' && config.position === 'inside') {\n      res.y = rect.y + rect.height * 0.4;\n    }\n\n    return res;\n  },\n  buildPath: function (ctx, shape, inBundle) {\n    var symbolType = shape.symbolType;\n\n    if (symbolType !== 'none') {\n      var proxySymbol = symbolBuildProxies[symbolType];\n\n      if (!proxySymbol) {\n        // Default rect\n        symbolType = 'rect';\n        proxySymbol = symbolBuildProxies[symbolType];\n      }\n\n      symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape);\n      proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);\n    }\n  }\n}); // Provide setColor helper method to avoid determine if set the fill or stroke outside\n\nfunction symbolPathSetColor(color, innerColor) {\n  if (this.type !== 'image') {\n    var symbolStyle = this.style;\n\n    if (this.__isEmptyBrush) {\n      symbolStyle.stroke = color;\n      symbolStyle.fill = innerColor || '#fff'; // TODO Same width with lineStyle in LineView\n\n      symbolStyle.lineWidth = 2;\n    } else if (this.shape.symbolType === 'line') {\n      symbolStyle.stroke = color;\n    } else {\n      symbolStyle.fill = color;\n    }\n\n    this.markRedraw();\n  }\n}\n/**\n * Create a symbol element with given symbol configuration: shape, x, y, width, height, color\n */\n\n\nfunction createSymbol(symbolType, x, y, w, h, color, // whether to keep the ratio of w/h,\nkeepAspect) {\n  // TODO Support image object, DynamicImage.\n  var isEmpty = symbolType.indexOf('empty') === 0;\n\n  if (isEmpty) {\n    symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);\n  }\n\n  var symbolPath;\n\n  if (symbolType.indexOf('image://') === 0) {\n    symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n  } else if (symbolType.indexOf('path://') === 0) {\n    symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n  } else {\n    symbolPath = new SymbolClz({\n      shape: {\n        symbolType: symbolType,\n        x: x,\n        y: y,\n        width: w,\n        height: h\n      }\n    });\n  }\n\n  symbolPath.__isEmptyBrush = isEmpty; // TODO Should deprecate setColor\n\n  symbolPath.setColor = symbolPathSetColor;\n\n  if (color) {\n    symbolPath.setColor(color);\n  }\n\n  return symbolPath;\n}\nfunction normalizeSymbolSize(symbolSize) {\n  if (!isArray(symbolSize)) {\n    symbolSize = [+symbolSize, +symbolSize];\n  }\n\n  return [symbolSize[0] || 0, symbolSize[1] || 0];\n}\nfunction normalizeSymbolOffset(symbolOffset, symbolSize) {\n  if (symbolOffset == null) {\n    return;\n  }\n\n  if (!isArray(symbolOffset)) {\n    symbolOffset = [symbolOffset, symbolOffset];\n  }\n\n  return [parsePercent$1(symbolOffset[0], symbolSize[0]) || 0, parsePercent$1(retrieve2(symbolOffset[1], symbolOffset[0]), symbolSize[1]) || 0];\n}\n\nfunction isSafeNum(num) {\n    return isFinite(num);\n}\nfunction createLinearGradient(ctx, obj, rect) {\n    var x = obj.x == null ? 0 : obj.x;\n    var x2 = obj.x2 == null ? 1 : obj.x2;\n    var y = obj.y == null ? 0 : obj.y;\n    var y2 = obj.y2 == null ? 0 : obj.y2;\n    if (!obj.global) {\n        x = x * rect.width + rect.x;\n        x2 = x2 * rect.width + rect.x;\n        y = y * rect.height + rect.y;\n        y2 = y2 * rect.height + rect.y;\n    }\n    x = isSafeNum(x) ? x : 0;\n    x2 = isSafeNum(x2) ? x2 : 1;\n    y = isSafeNum(y) ? y : 0;\n    y2 = isSafeNum(y2) ? y2 : 0;\n    var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);\n    return canvasGradient;\n}\nfunction createRadialGradient(ctx, obj, rect) {\n    var width = rect.width;\n    var height = rect.height;\n    var min = Math.min(width, height);\n    var x = obj.x == null ? 0.5 : obj.x;\n    var y = obj.y == null ? 0.5 : obj.y;\n    var r = obj.r == null ? 0.5 : obj.r;\n    if (!obj.global) {\n        x = x * width + rect.x;\n        y = y * height + rect.y;\n        r = r * min;\n    }\n    x = isSafeNum(x) ? x : 0.5;\n    y = isSafeNum(y) ? y : 0.5;\n    r = r >= 0 && isSafeNum(r) ? r : 0.5;\n    var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);\n    return canvasGradient;\n}\nfunction getCanvasGradient(ctx, obj, rect) {\n    var canvasGradient = obj.type === 'radial'\n        ? createRadialGradient(ctx, obj, rect)\n        : createLinearGradient(ctx, obj, rect);\n    var colorStops = obj.colorStops;\n    for (var i = 0; i < colorStops.length; i++) {\n        canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color);\n    }\n    return canvasGradient;\n}\nfunction isClipPathChanged(clipPaths, prevClipPaths) {\n    if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) {\n        return false;\n    }\n    if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {\n        return true;\n    }\n    for (var i = 0; i < clipPaths.length; i++) {\n        if (clipPaths[i] !== prevClipPaths[i]) {\n            return true;\n        }\n    }\n    return false;\n}\nfunction parseInt10(val) {\n    return parseInt(val, 10);\n}\nfunction getSize(root, whIdx, opts) {\n    var wh = ['width', 'height'][whIdx];\n    var cwh = ['clientWidth', 'clientHeight'][whIdx];\n    var plt = ['paddingLeft', 'paddingTop'][whIdx];\n    var prb = ['paddingRight', 'paddingBottom'][whIdx];\n    if (opts[wh] != null && opts[wh] !== 'auto') {\n        return parseFloat(opts[wh]);\n    }\n    var stl = document.defaultView.getComputedStyle(root);\n    return ((root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))\n        - (parseInt10(stl[plt]) || 0)\n        - (parseInt10(stl[prb]) || 0)) | 0;\n}\n\nfunction normalizeLineDash(lineType, lineWidth) {\n    if (!lineType || lineType === 'solid' || !(lineWidth > 0)) {\n        return null;\n    }\n    return lineType === 'dashed'\n        ? [4 * lineWidth, 2 * lineWidth]\n        : lineType === 'dotted'\n            ? [lineWidth]\n            : isNumber(lineType)\n                ? [lineType] : isArray(lineType) ? lineType : null;\n}\nfunction getLineDash(el) {\n    var style = el.style;\n    var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);\n    var lineDashOffset = style.lineDashOffset;\n    if (lineDash) {\n        var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;\n        if (lineScale_1 && lineScale_1 !== 1) {\n            lineDash = map(lineDash, function (rawVal) {\n                return rawVal / lineScale_1;\n            });\n            lineDashOffset /= lineScale_1;\n        }\n    }\n    return [lineDash, lineDashOffset];\n}\n\nvar pathProxyForDraw = new PathProxy(true);\nfunction styleHasStroke(style) {\n    var stroke = style.stroke;\n    return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n}\nfunction isValidStrokeFillStyle(strokeOrFill) {\n    return typeof strokeOrFill === 'string' && strokeOrFill !== 'none';\n}\nfunction styleHasFill(style) {\n    var fill = style.fill;\n    return fill != null && fill !== 'none';\n}\nfunction doFillPath(ctx, style) {\n    if (style.fillOpacity != null && style.fillOpacity !== 1) {\n        var originalGlobalAlpha = ctx.globalAlpha;\n        ctx.globalAlpha = style.fillOpacity * style.opacity;\n        ctx.fill();\n        ctx.globalAlpha = originalGlobalAlpha;\n    }\n    else {\n        ctx.fill();\n    }\n}\nfunction doStrokePath(ctx, style) {\n    if (style.strokeOpacity != null && style.strokeOpacity !== 1) {\n        var originalGlobalAlpha = ctx.globalAlpha;\n        ctx.globalAlpha = style.strokeOpacity * style.opacity;\n        ctx.stroke();\n        ctx.globalAlpha = originalGlobalAlpha;\n    }\n    else {\n        ctx.stroke();\n    }\n}\nfunction createCanvasPattern(ctx, pattern, el) {\n    var image = createOrUpdateImage(pattern.image, pattern.__image, el);\n    if (isImageReady(image)) {\n        var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat');\n        if (typeof DOMMatrix === 'function'\n            && canvasPattern\n            && canvasPattern.setTransform) {\n            var matrix = new DOMMatrix();\n            matrix.translateSelf((pattern.x || 0), (pattern.y || 0));\n            matrix.rotateSelf(0, 0, (pattern.rotation || 0) * RADIAN_TO_DEGREE);\n            matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1));\n            canvasPattern.setTransform(matrix);\n        }\n        return canvasPattern;\n    }\n}\nfunction brushPath(ctx, el, style, inBatch) {\n    var _a;\n    var hasStroke = styleHasStroke(style);\n    var hasFill = styleHasFill(style);\n    var strokePercent = style.strokePercent;\n    var strokePart = strokePercent < 1;\n    var firstDraw = !el.path;\n    if ((!el.silent || strokePart) && firstDraw) {\n        el.createPathProxy();\n    }\n    var path = el.path || pathProxyForDraw;\n    var dirtyFlag = el.__dirty;\n    if (!inBatch) {\n        var fill = style.fill;\n        var stroke = style.stroke;\n        var hasFillGradient = hasFill && !!fill.colorStops;\n        var hasStrokeGradient = hasStroke && !!stroke.colorStops;\n        var hasFillPattern = hasFill && !!fill.image;\n        var hasStrokePattern = hasStroke && !!stroke.image;\n        var fillGradient = void 0;\n        var strokeGradient = void 0;\n        var fillPattern = void 0;\n        var strokePattern = void 0;\n        var rect = void 0;\n        if (hasFillGradient || hasStrokeGradient) {\n            rect = el.getBoundingRect();\n        }\n        if (hasFillGradient) {\n            fillGradient = dirtyFlag\n                ? getCanvasGradient(ctx, fill, rect)\n                : el.__canvasFillGradient;\n            el.__canvasFillGradient = fillGradient;\n        }\n        if (hasStrokeGradient) {\n            strokeGradient = dirtyFlag\n                ? getCanvasGradient(ctx, stroke, rect)\n                : el.__canvasStrokeGradient;\n            el.__canvasStrokeGradient = strokeGradient;\n        }\n        if (hasFillPattern) {\n            fillPattern = (dirtyFlag || !el.__canvasFillPattern)\n                ? createCanvasPattern(ctx, fill, el)\n                : el.__canvasFillPattern;\n            el.__canvasFillPattern = fillPattern;\n        }\n        if (hasStrokePattern) {\n            strokePattern = (dirtyFlag || !el.__canvasStrokePattern)\n                ? createCanvasPattern(ctx, stroke, el)\n                : el.__canvasStrokePattern;\n            el.__canvasStrokePattern = fillPattern;\n        }\n        if (hasFillGradient) {\n            ctx.fillStyle = fillGradient;\n        }\n        else if (hasFillPattern) {\n            if (fillPattern) {\n                ctx.fillStyle = fillPattern;\n            }\n            else {\n                hasFill = false;\n            }\n        }\n        if (hasStrokeGradient) {\n            ctx.strokeStyle = strokeGradient;\n        }\n        else if (hasStrokePattern) {\n            if (strokePattern) {\n                ctx.strokeStyle = strokePattern;\n            }\n            else {\n                hasStroke = false;\n            }\n        }\n    }\n    var scale = el.getGlobalScale();\n    path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold);\n    var lineDash;\n    var lineDashOffset;\n    if (ctx.setLineDash && style.lineDash) {\n        _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n    }\n    var needsRebuild = true;\n    if (firstDraw || (dirtyFlag & SHAPE_CHANGED_BIT)) {\n        path.setDPR(ctx.dpr);\n        if (strokePart) {\n            path.setContext(null);\n        }\n        else {\n            path.setContext(ctx);\n            needsRebuild = false;\n        }\n        path.reset();\n        el.buildPath(path, el.shape, inBatch);\n        path.toStatic();\n        el.pathUpdated();\n    }\n    if (needsRebuild) {\n        path.rebuildPath(ctx, strokePart ? strokePercent : 1);\n    }\n    if (lineDash) {\n        ctx.setLineDash(lineDash);\n        ctx.lineDashOffset = lineDashOffset;\n    }\n    if (!inBatch) {\n        if (style.strokeFirst) {\n            if (hasStroke) {\n                doStrokePath(ctx, style);\n            }\n            if (hasFill) {\n                doFillPath(ctx, style);\n            }\n        }\n        else {\n            if (hasFill) {\n                doFillPath(ctx, style);\n            }\n            if (hasStroke) {\n                doStrokePath(ctx, style);\n            }\n        }\n    }\n    if (lineDash) {\n        ctx.setLineDash([]);\n    }\n}\nfunction brushImage(ctx, el, style) {\n    var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload);\n    if (!image || !isImageReady(image)) {\n        return;\n    }\n    var x = style.x || 0;\n    var y = style.y || 0;\n    var width = el.getWidth();\n    var height = el.getHeight();\n    var aspect = image.width / image.height;\n    if (width == null && height != null) {\n        width = height * aspect;\n    }\n    else if (height == null && width != null) {\n        height = width / aspect;\n    }\n    else if (width == null && height == null) {\n        width = image.width;\n        height = image.height;\n    }\n    if (style.sWidth && style.sHeight) {\n        var sx = style.sx || 0;\n        var sy = style.sy || 0;\n        ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height);\n    }\n    else if (style.sx && style.sy) {\n        var sx = style.sx;\n        var sy = style.sy;\n        var sWidth = width - sx;\n        var sHeight = height - sy;\n        ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height);\n    }\n    else {\n        ctx.drawImage(image, x, y, width, height);\n    }\n}\nfunction brushText(ctx, el, style) {\n    var _a;\n    var text = style.text;\n    text != null && (text += '');\n    if (text) {\n        ctx.font = style.font || DEFAULT_FONT;\n        ctx.textAlign = style.textAlign;\n        ctx.textBaseline = style.textBaseline;\n        var lineDash = void 0;\n        var lineDashOffset = void 0;\n        if (ctx.setLineDash && style.lineDash) {\n            _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n        }\n        if (lineDash) {\n            ctx.setLineDash(lineDash);\n            ctx.lineDashOffset = lineDashOffset;\n        }\n        if (style.strokeFirst) {\n            if (styleHasStroke(style)) {\n                ctx.strokeText(text, style.x, style.y);\n            }\n            if (styleHasFill(style)) {\n                ctx.fillText(text, style.x, style.y);\n            }\n        }\n        else {\n            if (styleHasFill(style)) {\n                ctx.fillText(text, style.x, style.y);\n            }\n            if (styleHasStroke(style)) {\n                ctx.strokeText(text, style.x, style.y);\n            }\n        }\n        if (lineDash) {\n            ctx.setLineDash([]);\n        }\n    }\n}\nvar SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\nvar STROKE_PROPS = [\n    ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]\n];\nfunction bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) {\n    var styleChanged = false;\n    if (!forceSetAll) {\n        prevStyle = prevStyle || {};\n        if (style === prevStyle) {\n            return false;\n        }\n    }\n    if (forceSetAll || style.opacity !== prevStyle.opacity) {\n        flushPathDrawn(ctx, scope);\n        styleChanged = true;\n        var opacity = Math.max(Math.min(style.opacity, 1), 0);\n        ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity;\n    }\n    if (forceSetAll || style.blend !== prevStyle.blend) {\n        if (!styleChanged) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n        }\n        ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend;\n    }\n    for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) {\n        var propName = SHADOW_NUMBER_PROPS[i];\n        if (forceSetAll || style[propName] !== prevStyle[propName]) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx[propName] = ctx.dpr * (style[propName] || 0);\n        }\n    }\n    if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) {\n        if (!styleChanged) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n        }\n        ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor;\n    }\n    return styleChanged;\n}\nfunction bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) {\n    var style = getStyle(el, scope.inHover);\n    var prevStyle = forceSetAll\n        ? null\n        : (prevEl && getStyle(prevEl, scope.inHover) || {});\n    if (style === prevStyle) {\n        return false;\n    }\n    var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope);\n    if (forceSetAll || style.fill !== prevStyle.fill) {\n        if (!styleChanged) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n        }\n        isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill);\n    }\n    if (forceSetAll || style.stroke !== prevStyle.stroke) {\n        if (!styleChanged) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n        }\n        isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke);\n    }\n    if (forceSetAll || style.opacity !== prevStyle.opacity) {\n        if (!styleChanged) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n        }\n        ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;\n    }\n    if (el.hasStroke()) {\n        var lineWidth = style.lineWidth;\n        var newLineWidth = lineWidth / ((style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1);\n        if (ctx.lineWidth !== newLineWidth) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.lineWidth = newLineWidth;\n        }\n    }\n    for (var i = 0; i < STROKE_PROPS.length; i++) {\n        var prop = STROKE_PROPS[i];\n        var propName = prop[0];\n        if (forceSetAll || style[propName] !== prevStyle[propName]) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx[propName] = style[propName] || prop[1];\n        }\n    }\n    return styleChanged;\n}\nfunction bindImageStyle(ctx, el, prevEl, forceSetAll, scope) {\n    return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope);\n}\nfunction setContextTransform(ctx, el) {\n    var m = el.transform;\n    var dpr = ctx.dpr || 1;\n    if (m) {\n        ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);\n    }\n    else {\n        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n    }\n}\nfunction updateClipStatus(clipPaths, ctx, scope) {\n    var allClipped = false;\n    for (var i = 0; i < clipPaths.length; i++) {\n        var clipPath = clipPaths[i];\n        allClipped = allClipped || clipPath.isZeroArea();\n        setContextTransform(ctx, clipPath);\n        ctx.beginPath();\n        clipPath.buildPath(ctx, clipPath.shape);\n        ctx.clip();\n    }\n    scope.allClipped = allClipped;\n}\nfunction isTransformChanged(m0, m1) {\n    if (m0 && m1) {\n        return m0[0] !== m1[0]\n            || m0[1] !== m1[1]\n            || m0[2] !== m1[2]\n            || m0[3] !== m1[3]\n            || m0[4] !== m1[4]\n            || m0[5] !== m1[5];\n    }\n    else if (!m0 && !m1) {\n        return false;\n    }\n    return true;\n}\nvar DRAW_TYPE_PATH = 1;\nvar DRAW_TYPE_IMAGE = 2;\nvar DRAW_TYPE_TEXT = 3;\nvar DRAW_TYPE_INCREMENTAL = 4;\nfunction canPathBatch(style) {\n    var hasFill = styleHasFill(style);\n    var hasStroke = styleHasStroke(style);\n    return !(style.lineDash\n        || !(+hasFill ^ +hasStroke)\n        || (hasFill && typeof style.fill !== 'string')\n        || (hasStroke && typeof style.stroke !== 'string')\n        || style.strokePercent < 1\n        || style.strokeOpacity < 1\n        || style.fillOpacity < 1);\n}\nfunction flushPathDrawn(ctx, scope) {\n    scope.batchFill && ctx.fill();\n    scope.batchStroke && ctx.stroke();\n    scope.batchFill = '';\n    scope.batchStroke = '';\n}\nfunction getStyle(el, inHover) {\n    return inHover ? (el.__hoverStyle || el.style) : el.style;\n}\nfunction brushSingle(ctx, el) {\n    brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true);\n}\nfunction brush(ctx, el, scope, isLast) {\n    var m = el.transform;\n    if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) {\n        el.__dirty &= ~REDRAW_BIT;\n        el.__isRendered = false;\n        return;\n    }\n    var clipPaths = el.__clipPaths;\n    var prevElClipPaths = scope.prevElClipPaths;\n    var forceSetTransform = false;\n    var forceSetStyle = false;\n    if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) {\n        if (prevElClipPaths && prevElClipPaths.length) {\n            flushPathDrawn(ctx, scope);\n            ctx.restore();\n            forceSetStyle = forceSetTransform = true;\n            scope.prevElClipPaths = null;\n            scope.allClipped = false;\n            scope.prevEl = null;\n        }\n        if (clipPaths && clipPaths.length) {\n            flushPathDrawn(ctx, scope);\n            ctx.save();\n            updateClipStatus(clipPaths, ctx, scope);\n            forceSetTransform = true;\n        }\n        scope.prevElClipPaths = clipPaths;\n    }\n    if (scope.allClipped) {\n        el.__isRendered = false;\n        return;\n    }\n    el.beforeBrush && el.beforeBrush();\n    el.innerBeforeBrush();\n    var prevEl = scope.prevEl;\n    if (!prevEl) {\n        forceSetStyle = forceSetTransform = true;\n    }\n    var canBatchPath = el instanceof Path\n        && el.autoBatch\n        && canPathBatch(el.style);\n    if (forceSetTransform || isTransformChanged(m, prevEl.transform)) {\n        flushPathDrawn(ctx, scope);\n        setContextTransform(ctx, el);\n    }\n    else if (!canBatchPath) {\n        flushPathDrawn(ctx, scope);\n    }\n    var style = getStyle(el, scope.inHover);\n    if (el instanceof Path) {\n        if (scope.lastDrawType !== DRAW_TYPE_PATH) {\n            forceSetStyle = true;\n            scope.lastDrawType = DRAW_TYPE_PATH;\n        }\n        bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n        if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) {\n            ctx.beginPath();\n        }\n        brushPath(ctx, el, style, canBatchPath);\n        if (canBatchPath) {\n            scope.batchFill = style.fill || '';\n            scope.batchStroke = style.stroke || '';\n        }\n    }\n    else {\n        if (el instanceof TSpan) {\n            if (scope.lastDrawType !== DRAW_TYPE_TEXT) {\n                forceSetStyle = true;\n                scope.lastDrawType = DRAW_TYPE_TEXT;\n            }\n            bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n            brushText(ctx, el, style);\n        }\n        else if (el instanceof ZRImage) {\n            if (scope.lastDrawType !== DRAW_TYPE_IMAGE) {\n                forceSetStyle = true;\n                scope.lastDrawType = DRAW_TYPE_IMAGE;\n            }\n            bindImageStyle(ctx, el, prevEl, forceSetStyle, scope);\n            brushImage(ctx, el, style);\n        }\n        else if (el.getTemporalDisplayables) {\n            if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) {\n                forceSetStyle = true;\n                scope.lastDrawType = DRAW_TYPE_INCREMENTAL;\n            }\n            brushIncremental(ctx, el, scope);\n        }\n    }\n    if (canBatchPath && isLast) {\n        flushPathDrawn(ctx, scope);\n    }\n    el.innerAfterBrush();\n    el.afterBrush && el.afterBrush();\n    scope.prevEl = el;\n    el.__dirty = 0;\n    el.__isRendered = true;\n}\nfunction brushIncremental(ctx, el, scope) {\n    var displayables = el.getDisplayables();\n    var temporalDisplayables = el.getTemporalDisplayables();\n    ctx.save();\n    var innerScope = {\n        prevElClipPaths: null,\n        prevEl: null,\n        allClipped: false,\n        viewWidth: scope.viewWidth,\n        viewHeight: scope.viewHeight,\n        inHover: scope.inHover\n    };\n    var i;\n    var len;\n    for (i = el.getCursor(), len = displayables.length; i < len; i++) {\n        var displayable = displayables[i];\n        displayable.beforeBrush && displayable.beforeBrush();\n        displayable.innerBeforeBrush();\n        brush(ctx, displayable, innerScope, i === len - 1);\n        displayable.innerAfterBrush();\n        displayable.afterBrush && displayable.afterBrush();\n        innerScope.prevEl = displayable;\n    }\n    for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) {\n        var displayable = temporalDisplayables[i_1];\n        displayable.beforeBrush && displayable.beforeBrush();\n        displayable.innerBeforeBrush();\n        brush(ctx, displayable, innerScope, i_1 === len_1 - 1);\n        displayable.innerAfterBrush();\n        displayable.afterBrush && displayable.afterBrush();\n        innerScope.prevEl = displayable;\n    }\n    el.clearTemporalDisplayables();\n    el.notClear = true;\n    ctx.restore();\n}\n\nvar decalMap = new WeakMap();\nvar decalCache = new LRU(100);\nvar decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight'];\n/**\n * Create or update pattern image from decal options\n *\n * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal\n * @return {Pattern} pattern with generated image, null if no decal\n */\n\nfunction createOrUpdatePatternFromDecal(decalObject, api) {\n  if (decalObject === 'none') {\n    return null;\n  }\n\n  var dpr = api.getDevicePixelRatio();\n  var zr = api.getZr();\n  var isSVG = zr.painter.type === 'svg';\n\n  if (decalObject.dirty) {\n    decalMap[\"delete\"](decalObject);\n  }\n\n  var oldPattern = decalMap.get(decalObject);\n\n  if (oldPattern) {\n    return oldPattern;\n  }\n\n  var decalOpt = defaults(decalObject, {\n    symbol: 'rect',\n    symbolSize: 1,\n    symbolKeepAspect: true,\n    color: 'rgba(0, 0, 0, 0.2)',\n    backgroundColor: null,\n    dashArrayX: 5,\n    dashArrayY: 5,\n    rotation: 0,\n    maxTileWidth: 512,\n    maxTileHeight: 512\n  });\n\n  if (decalOpt.backgroundColor === 'none') {\n    decalOpt.backgroundColor = null;\n  }\n\n  var pattern = {\n    repeat: 'repeat'\n  };\n  setPatternnSource(pattern);\n  pattern.rotation = decalOpt.rotation;\n  pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr;\n  decalMap.set(decalObject, pattern);\n  decalObject.dirty = false;\n  return pattern;\n\n  function setPatternnSource(pattern) {\n    var keys = [dpr];\n    var isValidKey = true;\n\n    for (var i = 0; i < decalKeys.length; ++i) {\n      var value = decalOpt[decalKeys[i]];\n\n      if (value != null && !isArray(value) && !isString(value) && !isNumber(value) && typeof value !== 'boolean') {\n        isValidKey = false;\n        break;\n      }\n\n      keys.push(value);\n    }\n\n    var cacheKey;\n\n    if (isValidKey) {\n      cacheKey = keys.join(',') + (isSVG ? '-svg' : '');\n      var cache = decalCache.get(cacheKey);\n\n      if (cache) {\n        isSVG ? pattern.svgElement = cache : pattern.image = cache;\n      }\n    }\n\n    var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX);\n    var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY);\n    var symbolArray = normalizeSymbolArray(decalOpt.symbol);\n    var lineBlockLengthsX = getLineBlockLengthX(dashArrayX);\n    var lineBlockLengthY = getLineBlockLengthY(dashArrayY);\n    var canvas = !isSVG && platformApi.createCanvas();\n    var svgRoot = isSVG && {\n      tag: 'g',\n      attrs: {},\n      key: 'dcl',\n      children: []\n    };\n    var pSize = getPatternSize();\n    var ctx;\n\n    if (canvas) {\n      canvas.width = pSize.width * dpr;\n      canvas.height = pSize.height * dpr;\n      ctx = canvas.getContext('2d');\n    }\n\n    brushDecal();\n\n    if (isValidKey) {\n      decalCache.put(cacheKey, canvas || svgRoot);\n    }\n\n    pattern.image = canvas;\n    pattern.svgElement = svgRoot;\n    pattern.svgWidth = pSize.width;\n    pattern.svgHeight = pSize.height;\n    /**\n     * Get minimum length that can make a repeatable pattern.\n     *\n     * @return {Object} pattern width and height\n     */\n\n    function getPatternSize() {\n      /**\n       * For example, if dash is [[3, 2], [2, 1]] for X, it looks like\n       * |---  ---  ---  ---  --- ...\n       * |-- -- -- -- -- -- -- -- ...\n       * |---  ---  ---  ---  --- ...\n       * |-- -- -- -- -- -- -- -- ...\n       * So the minimum length of X is 15,\n       * which is the least common multiple of `3 + 2` and `2 + 1`\n       * |---  ---  ---  |---  --- ...\n       * |-- -- -- -- -- |-- -- -- ...\n       */\n      var width = 1;\n\n      for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) {\n        width = getLeastCommonMultiple(width, lineBlockLengthsX[i]);\n      }\n\n      var symbolRepeats = 1;\n\n      for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) {\n        symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length);\n      }\n\n      width *= symbolRepeats;\n      var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length;\n\n      if (\"development\" !== 'production') {\n        var warn = function (attrName) {\n          /* eslint-disable-next-line */\n          console.warn(\"Calculated decal size is greater than \" + attrName + \" due to decal option settings so \" + attrName + \" is used for the decal size. Please consider changing the decal option to make a smaller decal or set \" + attrName + \" to be larger to avoid incontinuity.\");\n        };\n\n        if (width > decalOpt.maxTileWidth) {\n          warn('maxTileWidth');\n        }\n\n        if (height > decalOpt.maxTileHeight) {\n          warn('maxTileHeight');\n        }\n      }\n\n      return {\n        width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)),\n        height: Math.max(1, Math.min(height, decalOpt.maxTileHeight))\n      };\n    }\n\n    function brushDecal() {\n      if (ctx) {\n        ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n        if (decalOpt.backgroundColor) {\n          ctx.fillStyle = decalOpt.backgroundColor;\n          ctx.fillRect(0, 0, canvas.width, canvas.height);\n        }\n      }\n\n      var ySum = 0;\n\n      for (var i = 0; i < dashArrayY.length; ++i) {\n        ySum += dashArrayY[i];\n      }\n\n      if (ySum <= 0) {\n        // dashArrayY is 0, draw nothing\n        return;\n      }\n\n      var y = -lineBlockLengthY;\n      var yId = 0;\n      var yIdTotal = 0;\n      var xId0 = 0;\n\n      while (y < pSize.height) {\n        if (yId % 2 === 0) {\n          var symbolYId = yIdTotal / 2 % symbolArray.length;\n          var x = 0;\n          var xId1 = 0;\n          var xId1Total = 0;\n\n          while (x < pSize.width * 2) {\n            var xSum = 0;\n\n            for (var i = 0; i < dashArrayX[xId0].length; ++i) {\n              xSum += dashArrayX[xId0][i];\n            }\n\n            if (xSum <= 0) {\n              // Skip empty line\n              break;\n            } // E.g., [15, 5, 20, 5] draws only for 15 and 20\n\n\n            if (xId1 % 2 === 0) {\n              var size = (1 - decalOpt.symbolSize) * 0.5;\n              var left = x + dashArrayX[xId0][xId1] * size;\n              var top_1 = y + dashArrayY[yId] * size;\n              var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize;\n              var height = dashArrayY[yId] * decalOpt.symbolSize;\n              var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length;\n              brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]);\n            }\n\n            x += dashArrayX[xId0][xId1];\n            ++xId1Total;\n            ++xId1;\n\n            if (xId1 === dashArrayX[xId0].length) {\n              xId1 = 0;\n            }\n          }\n\n          ++xId0;\n\n          if (xId0 === dashArrayX.length) {\n            xId0 = 0;\n          }\n        }\n\n        y += dashArrayY[yId];\n        ++yIdTotal;\n        ++yId;\n\n        if (yId === dashArrayY.length) {\n          yId = 0;\n        }\n      }\n\n      function brushSymbol(x, y, width, height, symbolType) {\n        var scale = isSVG ? 1 : dpr;\n        var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect);\n\n        if (isSVG) {\n          var symbolVNode = zr.painter.renderOneToVNode(symbol);\n\n          if (symbolVNode) {\n            svgRoot.children.push(symbolVNode);\n          }\n        } else {\n          // Paint to canvas for all other renderers.\n          brushSingle(ctx, symbol);\n        }\n      }\n    }\n  }\n}\n/**\n * Convert symbol array into normalized array\n *\n * @param {string | (string | string[])[]} symbol symbol input\n * @return {string[][]} normolized symbol array\n */\n\nfunction normalizeSymbolArray(symbol) {\n  if (!symbol || symbol.length === 0) {\n    return [['rect']];\n  }\n\n  if (isString(symbol)) {\n    return [[symbol]];\n  }\n\n  var isAllString = true;\n\n  for (var i = 0; i < symbol.length; ++i) {\n    if (!isString(symbol[i])) {\n      isAllString = false;\n      break;\n    }\n  }\n\n  if (isAllString) {\n    return normalizeSymbolArray([symbol]);\n  }\n\n  var result = [];\n\n  for (var i = 0; i < symbol.length; ++i) {\n    if (isString(symbol[i])) {\n      result.push([symbol[i]]);\n    } else {\n      result.push(symbol[i]);\n    }\n  }\n\n  return result;\n}\n/**\n * Convert dash input into dashArray\n *\n * @param {DecalDashArrayX} dash dash input\n * @return {number[][]} normolized dash array\n */\n\n\nfunction normalizeDashArrayX(dash) {\n  if (!dash || dash.length === 0) {\n    return [[0, 0]];\n  }\n\n  if (isNumber(dash)) {\n    var dashValue = Math.ceil(dash);\n    return [[dashValue, dashValue]];\n  }\n  /**\n   * [20, 5] should be normalized into [[20, 5]],\n   * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]]\n   */\n\n\n  var isAllNumber = true;\n\n  for (var i = 0; i < dash.length; ++i) {\n    if (!isNumber(dash[i])) {\n      isAllNumber = false;\n      break;\n    }\n  }\n\n  if (isAllNumber) {\n    return normalizeDashArrayX([dash]);\n  }\n\n  var result = [];\n\n  for (var i = 0; i < dash.length; ++i) {\n    if (isNumber(dash[i])) {\n      var dashValue = Math.ceil(dash[i]);\n      result.push([dashValue, dashValue]);\n    } else {\n      var dashValue = map(dash[i], function (n) {\n        return Math.ceil(n);\n      });\n\n      if (dashValue.length % 2 === 1) {\n        // [4, 2, 1] means |----  -    -- |----  -    -- |\n        // so normalize it to be [4, 2, 1, 4, 2, 1]\n        result.push(dashValue.concat(dashValue));\n      } else {\n        result.push(dashValue);\n      }\n    }\n  }\n\n  return result;\n}\n/**\n * Convert dash input into dashArray\n *\n * @param {DecalDashArrayY} dash dash input\n * @return {number[]} normolized dash array\n */\n\n\nfunction normalizeDashArrayY(dash) {\n  if (!dash || typeof dash === 'object' && dash.length === 0) {\n    return [0, 0];\n  }\n\n  if (isNumber(dash)) {\n    var dashValue_1 = Math.ceil(dash);\n    return [dashValue_1, dashValue_1];\n  }\n\n  var dashValue = map(dash, function (n) {\n    return Math.ceil(n);\n  });\n  return dash.length % 2 ? dashValue.concat(dashValue) : dashValue;\n}\n/**\n * Get block length of each line. A block is the length of dash line and space.\n * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after\n * that, so the block length of this line is 5.\n *\n * @param {number[][]} dash dash array of X or Y\n * @return {number[]} block length of each line\n */\n\n\nfunction getLineBlockLengthX(dash) {\n  return map(dash, function (line) {\n    return getLineBlockLengthY(line);\n  });\n}\n\nfunction getLineBlockLengthY(dash) {\n  var blockLength = 0;\n\n  for (var i = 0; i < dash.length; ++i) {\n    blockLength += dash[i];\n  }\n\n  if (dash.length % 2 === 1) {\n    // [4, 2, 1] means |----  -    -- |----  -    -- |\n    // So total length is (4 + 2 + 1) * 2\n    return blockLength * 2;\n  }\n\n  return blockLength;\n}\n\nfunction decalVisual(ecModel, api) {\n  ecModel.eachRawSeries(function (seriesModel) {\n    if (ecModel.isSeriesFiltered(seriesModel)) {\n      return;\n    }\n\n    var data = seriesModel.getData();\n\n    if (data.hasItemVisual()) {\n      data.each(function (idx) {\n        var decal = data.getItemVisual(idx, 'decal');\n\n        if (decal) {\n          var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n          itemStyle.decal = createOrUpdatePatternFromDecal(decal, api);\n        }\n      });\n    }\n\n    var decal = data.getVisual('decal');\n\n    if (decal) {\n      var style = data.getVisual('style');\n      style.decal = createOrUpdatePatternFromDecal(decal, api);\n    }\n  });\n}\n\nvar lifecycle = new Eventful();\n\n// The implementations will be registered when installing the component.\n// Avoid these code being bundled to the core module.\n\nvar implsStore = {}; // TODO Type\n\nfunction registerImpl(name, impl) {\n  if (\"development\" !== 'production') {\n    if (implsStore[name]) {\n      error(\"Already has an implementation of \" + name + \".\");\n    }\n  }\n\n  implsStore[name] = impl;\n}\nfunction getImpl(name) {\n  if (\"development\" !== 'production') {\n    if (!implsStore[name]) {\n      error(\"Implementation of \" + name + \" doesn't exists.\");\n    }\n  }\n\n  return implsStore[name];\n}\n\nvar version$1 = '5.4.1';\nvar dependencies = {\n  zrender: '5.4.1'\n};\nvar TEST_FRAME_REMAIN_TIME = 1;\nvar PRIORITY_PROCESSOR_SERIES_FILTER = 800; // Some data processors depends on the stack result dimension (to calculate data extent).\n// So data stack stage should be in front of data processing stage.\n\nvar PRIORITY_PROCESSOR_DATASTACK = 900; // \"Data filter\" will block the stream, so it should be\n// put at the beginning of data processing.\n\nvar PRIORITY_PROCESSOR_FILTER = 1000;\nvar PRIORITY_PROCESSOR_DEFAULT = 2000;\nvar PRIORITY_PROCESSOR_STATISTIC = 5000;\nvar PRIORITY_VISUAL_LAYOUT = 1000;\nvar PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100;\nvar PRIORITY_VISUAL_GLOBAL = 2000;\nvar PRIORITY_VISUAL_CHART = 3000;\nvar PRIORITY_VISUAL_COMPONENT = 4000; // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to\n// overwrite the viusal result of component (like `visualMap`)\n// using data item specific setting (like itemStyle.xxx on data item)\n\nvar PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on\n// visual result like `symbolSize`.\n\nvar PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600;\nvar PRIORITY_VISUAL_BRUSH = 5000;\nvar PRIORITY_VISUAL_ARIA = 6000;\nvar PRIORITY_VISUAL_DECAL = 7000;\nvar PRIORITY = {\n  PROCESSOR: {\n    FILTER: PRIORITY_PROCESSOR_FILTER,\n    SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER,\n    STATISTIC: PRIORITY_PROCESSOR_STATISTIC\n  },\n  VISUAL: {\n    LAYOUT: PRIORITY_VISUAL_LAYOUT,\n    PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT,\n    GLOBAL: PRIORITY_VISUAL_GLOBAL,\n    CHART: PRIORITY_VISUAL_CHART,\n    POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT,\n    COMPONENT: PRIORITY_VISUAL_COMPONENT,\n    BRUSH: PRIORITY_VISUAL_BRUSH,\n    CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM,\n    ARIA: PRIORITY_VISUAL_ARIA,\n    DECAL: PRIORITY_VISUAL_DECAL\n  }\n}; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,\n// where they must not be invoked nestedly, except the only case: invoke\n// dispatchAction with updateMethod \"none\" in main process.\n// This flag is used to carry out this rule.\n// All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).\n\nvar IN_MAIN_PROCESS_KEY = '__flagInMainProcess';\nvar PENDING_UPDATE = '__pendingUpdate';\nvar STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus';\nvar ACTION_REG = /^[a-zA-Z0-9_]+$/;\nvar CONNECT_STATUS_KEY = '__connectUpdateStatus';\nvar CONNECT_STATUS_PENDING = 0;\nvar CONNECT_STATUS_UPDATING = 1;\nvar CONNECT_STATUS_UPDATED = 2;\n\nfunction createRegisterEventWithLowercaseECharts(method) {\n  return function () {\n    var args = [];\n\n    for (var _i = 0; _i < arguments.length; _i++) {\n      args[_i] = arguments[_i];\n    }\n\n    if (this.isDisposed()) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    return toLowercaseNameAndCallEventful(this, method, args);\n  };\n}\n\nfunction createRegisterEventWithLowercaseMessageCenter(method) {\n  return function () {\n    var args = [];\n\n    for (var _i = 0; _i < arguments.length; _i++) {\n      args[_i] = arguments[_i];\n    }\n\n    return toLowercaseNameAndCallEventful(this, method, args);\n  };\n}\n\nfunction toLowercaseNameAndCallEventful(host, method, args) {\n  // `args[0]` is event name. Event name is all lowercase.\n  args[0] = args[0] && args[0].toLowerCase();\n  return Eventful.prototype[method].apply(host, args);\n}\n\nvar MessageCenter =\n/** @class */\nfunction (_super) {\n  __extends(MessageCenter, _super);\n\n  function MessageCenter() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  return MessageCenter;\n}(Eventful);\n\nvar messageCenterProto = MessageCenter.prototype;\nmessageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on');\nmessageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); // ---------------------------------------\n// Internal method names for class ECharts\n// ---------------------------------------\n\nvar prepare;\nvar prepareView;\nvar updateDirectly;\nvar updateMethods;\nvar doConvertPixel;\nvar updateStreamModes;\nvar doDispatchAction;\nvar flushPendingActions;\nvar triggerUpdatedEvent;\nvar bindRenderedEvent;\nvar bindMouseEvent;\nvar render;\nvar renderComponents;\nvar renderSeries;\nvar createExtensionAPI;\nvar enableConnect;\nvar markStatusToUpdate;\nvar applyChangedStates;\n\nvar ECharts =\n/** @class */\nfunction (_super) {\n  __extends(ECharts, _super);\n\n  function ECharts(dom, // Theme name or themeOption.\n  theme, opts) {\n    var _this = _super.call(this, new ECEventProcessor()) || this;\n\n    _this._chartsViews = [];\n    _this._chartsMap = {};\n    _this._componentsViews = [];\n    _this._componentsMap = {}; // Can't dispatch action during rendering procedure\n\n    _this._pendingActions = [];\n    opts = opts || {}; // Get theme by name\n\n    if (isString(theme)) {\n      theme = themeStorage[theme];\n    }\n\n    _this._dom = dom;\n    var defaultRenderer = 'canvas';\n    var defaultCoarsePointer = 'auto';\n    var defaultUseDirtyRect = false;\n\n    if (\"development\" !== 'production') {\n      var root =\n      /* eslint-disable-next-line */\n      env.hasGlobalWindow ? window : global;\n      defaultRenderer = root.__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;\n      defaultCoarsePointer = retrieve2(root.__ECHARTS__DEFAULT__COARSE_POINTER, defaultCoarsePointer);\n      var devUseDirtyRect = root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__;\n      defaultUseDirtyRect = devUseDirtyRect == null ? defaultUseDirtyRect : devUseDirtyRect;\n    }\n\n    var zr = _this._zr = init(dom, {\n      renderer: opts.renderer || defaultRenderer,\n      devicePixelRatio: opts.devicePixelRatio,\n      width: opts.width,\n      height: opts.height,\n      ssr: opts.ssr,\n      useDirtyRect: retrieve2(opts.useDirtyRect, defaultUseDirtyRect),\n      useCoarsePointer: retrieve2(opts.useCoarsePointer, defaultCoarsePointer),\n      pointerSize: opts.pointerSize\n    });\n    _this._ssr = opts.ssr; // Expect 60 fps.\n\n    _this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);\n    theme = clone(theme);\n    theme && globalBackwardCompat(theme, true);\n    _this._theme = theme;\n    _this._locale = createLocaleObject(opts.locale || SYSTEM_LANG);\n    _this._coordSysMgr = new CoordinateSystemManager();\n    var api = _this._api = createExtensionAPI(_this); // Sort on demand\n\n    function prioritySortFunc(a, b) {\n      return a.__prio - b.__prio;\n    }\n\n    sort(visualFuncs, prioritySortFunc);\n    sort(dataProcessorFuncs, prioritySortFunc);\n    _this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs);\n    _this._messageCenter = new MessageCenter(); // Init mouse events\n\n    _this._initEvents(); // In case some people write `window.onresize = chart.resize`\n\n\n    _this.resize = bind(_this.resize, _this);\n    zr.animation.on('frame', _this._onframe, _this);\n    bindRenderedEvent(zr, _this);\n    bindMouseEvent(zr, _this); // ECharts instance can be used as value.\n\n    setAsPrimitive(_this);\n    return _this;\n  }\n\n  ECharts.prototype._onframe = function () {\n    if (this._disposed) {\n      return;\n    }\n\n    applyChangedStates(this);\n    var scheduler = this._scheduler; // Lazy update\n\n    if (this[PENDING_UPDATE]) {\n      var silent = this[PENDING_UPDATE].silent;\n      this[IN_MAIN_PROCESS_KEY] = true;\n\n      try {\n        prepare(this);\n        updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams);\n      } catch (e) {\n        this[IN_MAIN_PROCESS_KEY] = false;\n        this[PENDING_UPDATE] = null;\n        throw e;\n      } // At present, in each frame, zrender performs:\n      //   (1) animation step forward.\n      //   (2) trigger('frame') (where this `_onframe` is called)\n      //   (3) zrender flush (render).\n      // If we do nothing here, since we use `setToFinal: true`, the step (3) above\n      // will render the final state of the elements before the real animation started.\n\n\n      this._zr.flush();\n\n      this[IN_MAIN_PROCESS_KEY] = false;\n      this[PENDING_UPDATE] = null;\n      flushPendingActions.call(this, silent);\n      triggerUpdatedEvent.call(this, silent);\n    } // Avoid do both lazy update and progress in one frame.\n    else if (scheduler.unfinished) {\n        // Stream progress.\n        var remainTime = TEST_FRAME_REMAIN_TIME;\n        var ecModel = this._model;\n        var api = this._api;\n        scheduler.unfinished = false;\n\n        do {\n          var startTime = +new Date();\n          scheduler.performSeriesTasks(ecModel); // Currently dataProcessorFuncs do not check threshold.\n\n          scheduler.performDataProcessorTasks(ecModel);\n          updateStreamModes(this, ecModel); // Do not update coordinate system here. Because that coord system update in\n          // each frame is not a good user experience. So we follow the rule that\n          // the extent of the coordinate system is determined in the first frame (the\n          // frame is executed immediately after task reset.\n          // this._coordSysMgr.update(ecModel, api);\n          // console.log('--- ec frame visual ---', remainTime);\n\n          scheduler.performVisualTasks(ecModel);\n          renderSeries(this, this._model, api, 'remain', {});\n          remainTime -= +new Date() - startTime;\n        } while (remainTime > 0 && scheduler.unfinished); // Call flush explicitly for trigger finished event.\n\n\n        if (!scheduler.unfinished) {\n          this._zr.flush();\n        } // Else, zr flushing be ensue within the same frame,\n        // because zr flushing is after onframe event.\n\n      }\n  };\n\n  ECharts.prototype.getDom = function () {\n    return this._dom;\n  };\n\n  ECharts.prototype.getId = function () {\n    return this.id;\n  };\n\n  ECharts.prototype.getZr = function () {\n    return this._zr;\n  };\n\n  ECharts.prototype.isSSR = function () {\n    return this._ssr;\n  };\n  /* eslint-disable-next-line */\n\n\n  ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) {\n    if (this[IN_MAIN_PROCESS_KEY]) {\n      if (\"development\" !== 'production') {\n        error('`setOption` should not be called during main process.');\n      }\n\n      return;\n    }\n\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    var silent;\n    var replaceMerge;\n    var transitionOpt;\n\n    if (isObject(notMerge)) {\n      lazyUpdate = notMerge.lazyUpdate;\n      silent = notMerge.silent;\n      replaceMerge = notMerge.replaceMerge;\n      transitionOpt = notMerge.transition;\n      notMerge = notMerge.notMerge;\n    }\n\n    this[IN_MAIN_PROCESS_KEY] = true;\n\n    if (!this._model || notMerge) {\n      var optionManager = new OptionManager(this._api);\n      var theme = this._theme;\n      var ecModel = this._model = new GlobalModel();\n      ecModel.scheduler = this._scheduler;\n      ecModel.ssr = this._ssr;\n      ecModel.init(null, null, null, theme, this._locale, optionManager);\n    }\n\n    this._model.setOption(option, {\n      replaceMerge: replaceMerge\n    }, optionPreprocessorFuncs);\n\n    var updateParams = {\n      seriesTransition: transitionOpt,\n      optionChanged: true\n    };\n\n    if (lazyUpdate) {\n      this[PENDING_UPDATE] = {\n        silent: silent,\n        updateParams: updateParams\n      };\n      this[IN_MAIN_PROCESS_KEY] = false; // `setOption(option, {lazyMode: true})` may be called when zrender has been slept.\n      // It should wake it up to make sure zrender start to render at the next frame.\n\n      this.getZr().wakeUp();\n    } else {\n      try {\n        prepare(this);\n        updateMethods.update.call(this, null, updateParams);\n      } catch (e) {\n        this[PENDING_UPDATE] = null;\n        this[IN_MAIN_PROCESS_KEY] = false;\n        throw e;\n      } // Ensure zr refresh sychronously, and then pixel in canvas can be\n      // fetched after `setOption`.\n\n\n      if (!this._ssr) {\n        // not use flush when using ssr mode.\n        this._zr.flush();\n      }\n\n      this[PENDING_UPDATE] = null;\n      this[IN_MAIN_PROCESS_KEY] = false;\n      flushPendingActions.call(this, silent);\n      triggerUpdatedEvent.call(this, silent);\n    }\n  };\n  /**\n   * @deprecated\n   */\n\n\n  ECharts.prototype.setTheme = function () {\n    deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0');\n  }; // We don't want developers to use getModel directly.\n\n\n  ECharts.prototype.getModel = function () {\n    return this._model;\n  };\n\n  ECharts.prototype.getOption = function () {\n    return this._model && this._model.getOption();\n  };\n\n  ECharts.prototype.getWidth = function () {\n    return this._zr.getWidth();\n  };\n\n  ECharts.prototype.getHeight = function () {\n    return this._zr.getHeight();\n  };\n\n  ECharts.prototype.getDevicePixelRatio = function () {\n    return this._zr.painter.dpr\n    /* eslint-disable-next-line */\n    || env.hasGlobalWindow && window.devicePixelRatio || 1;\n  };\n  /**\n   * Get canvas which has all thing rendered\n   * @deprecated Use renderToCanvas instead.\n   */\n\n\n  ECharts.prototype.getRenderedCanvas = function (opts) {\n    if (\"development\" !== 'production') {\n      deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas');\n    }\n\n    return this.renderToCanvas(opts);\n  };\n\n  ECharts.prototype.renderToCanvas = function (opts) {\n    opts = opts || {};\n    var painter = this._zr.painter;\n\n    if (\"development\" !== 'production') {\n      if (painter.type !== 'canvas') {\n        throw new Error('renderToCanvas can only be used in the canvas renderer.');\n      }\n    }\n\n    return painter.getRenderedCanvas({\n      backgroundColor: opts.backgroundColor || this._model.get('backgroundColor'),\n      pixelRatio: opts.pixelRatio || this.getDevicePixelRatio()\n    });\n  };\n\n  ECharts.prototype.renderToSVGString = function (opts) {\n    opts = opts || {};\n    var painter = this._zr.painter;\n\n    if (\"development\" !== 'production') {\n      if (painter.type !== 'svg') {\n        throw new Error('renderToSVGString can only be used in the svg renderer.');\n      }\n    }\n\n    return painter.renderToString({\n      useViewBox: opts.useViewBox\n    });\n  };\n  /**\n   * Get svg data url\n   */\n\n\n  ECharts.prototype.getSvgDataURL = function () {\n    if (!env.svgSupported) {\n      return;\n    }\n\n    var zr = this._zr;\n    var list = zr.storage.getDisplayList(); // Stop animations\n\n    each(list, function (el) {\n      el.stopAnimation(null, true);\n    });\n    return zr.painter.toDataURL();\n  };\n\n  ECharts.prototype.getDataURL = function (opts) {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    opts = opts || {};\n    var excludeComponents = opts.excludeComponents;\n    var ecModel = this._model;\n    var excludesComponentViews = [];\n    var self = this;\n    each(excludeComponents, function (componentType) {\n      ecModel.eachComponent({\n        mainType: componentType\n      }, function (component) {\n        var view = self._componentsMap[component.__viewId];\n\n        if (!view.group.ignore) {\n          excludesComponentViews.push(view);\n          view.group.ignore = true;\n        }\n      });\n    });\n    var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() : this.renderToCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));\n    each(excludesComponentViews, function (view) {\n      view.group.ignore = false;\n    });\n    return url;\n  };\n\n  ECharts.prototype.getConnectedDataURL = function (opts) {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    var isSvg = opts.type === 'svg';\n    var groupId = this.group;\n    var mathMin = Math.min;\n    var mathMax = Math.max;\n    var MAX_NUMBER = Infinity;\n\n    if (connectedGroups[groupId]) {\n      var left_1 = MAX_NUMBER;\n      var top_1 = MAX_NUMBER;\n      var right_1 = -MAX_NUMBER;\n      var bottom_1 = -MAX_NUMBER;\n      var canvasList_1 = [];\n      var dpr_1 = opts && opts.pixelRatio || this.getDevicePixelRatio();\n      each(instances$1, function (chart, id) {\n        if (chart.group === groupId) {\n          var canvas = isSvg ? chart.getZr().painter.getSvgDom().innerHTML : chart.renderToCanvas(clone(opts));\n          var boundingRect = chart.getDom().getBoundingClientRect();\n          left_1 = mathMin(boundingRect.left, left_1);\n          top_1 = mathMin(boundingRect.top, top_1);\n          right_1 = mathMax(boundingRect.right, right_1);\n          bottom_1 = mathMax(boundingRect.bottom, bottom_1);\n          canvasList_1.push({\n            dom: canvas,\n            left: boundingRect.left,\n            top: boundingRect.top\n          });\n        }\n      });\n      left_1 *= dpr_1;\n      top_1 *= dpr_1;\n      right_1 *= dpr_1;\n      bottom_1 *= dpr_1;\n      var width = right_1 - left_1;\n      var height = bottom_1 - top_1;\n      var targetCanvas = platformApi.createCanvas();\n      var zr_1 = init(targetCanvas, {\n        renderer: isSvg ? 'svg' : 'canvas'\n      });\n      zr_1.resize({\n        width: width,\n        height: height\n      });\n\n      if (isSvg) {\n        var content_1 = '';\n        each(canvasList_1, function (item) {\n          var x = item.left - left_1;\n          var y = item.top - top_1;\n          content_1 += '<g transform=\"translate(' + x + ',' + y + ')\">' + item.dom + '</g>';\n        });\n        zr_1.painter.getSvgRoot().innerHTML = content_1;\n\n        if (opts.connectedBackgroundColor) {\n          zr_1.painter.setBackgroundColor(opts.connectedBackgroundColor);\n        }\n\n        zr_1.refreshImmediately();\n        return zr_1.painter.toDataURL();\n      } else {\n        // Background between the charts\n        if (opts.connectedBackgroundColor) {\n          zr_1.add(new Rect({\n            shape: {\n              x: 0,\n              y: 0,\n              width: width,\n              height: height\n            },\n            style: {\n              fill: opts.connectedBackgroundColor\n            }\n          }));\n        }\n\n        each(canvasList_1, function (item) {\n          var img = new ZRImage({\n            style: {\n              x: item.left * dpr_1 - left_1,\n              y: item.top * dpr_1 - top_1,\n              image: item.dom\n            }\n          });\n          zr_1.add(img);\n        });\n        zr_1.refreshImmediately();\n        return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));\n      }\n    } else {\n      return this.getDataURL(opts);\n    }\n  };\n\n  ECharts.prototype.convertToPixel = function (finder, value) {\n    return doConvertPixel(this, 'convertToPixel', finder, value);\n  };\n\n  ECharts.prototype.convertFromPixel = function (finder, value) {\n    return doConvertPixel(this, 'convertFromPixel', finder, value);\n  };\n  /**\n   * Is the specified coordinate systems or components contain the given pixel point.\n   * @param {Array|number} value\n   * @return {boolean} result\n   */\n\n\n  ECharts.prototype.containPixel = function (finder, value) {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    var ecModel = this._model;\n    var result;\n    var findResult = parseFinder(ecModel, finder);\n    each(findResult, function (models, key) {\n      key.indexOf('Models') >= 0 && each(models, function (model) {\n        var coordSys = model.coordinateSystem;\n\n        if (coordSys && coordSys.containPoint) {\n          result = result || !!coordSys.containPoint(value);\n        } else if (key === 'seriesModels') {\n          var view = this._chartsMap[model.__viewId];\n\n          if (view && view.containPoint) {\n            result = result || view.containPoint(value, model);\n          } else {\n            if (\"development\" !== 'production') {\n              warn(key + ': ' + (view ? 'The found component do not support containPoint.' : 'No view mapping to the found component.'));\n            }\n          }\n        } else {\n          if (\"development\" !== 'production') {\n            warn(key + ': containPoint is not supported');\n          }\n        }\n      }, this);\n    }, this);\n    return !!result;\n  };\n  /**\n   * Get visual from series or data.\n   * @param finder\n   *        If string, e.g., 'series', means {seriesIndex: 0}.\n   *        If Object, could contain some of these properties below:\n   *        {\n   *            seriesIndex / seriesId / seriesName,\n   *            dataIndex / dataIndexInside\n   *        }\n   *        If dataIndex is not specified, series visual will be fetched,\n   *        but not data item visual.\n   *        If all of seriesIndex, seriesId, seriesName are not specified,\n   *        visual will be fetched from first series.\n   * @param visualType 'color', 'symbol', 'symbolSize'\n   */\n\n\n  ECharts.prototype.getVisual = function (finder, visualType) {\n    var ecModel = this._model;\n    var parsedFinder = parseFinder(ecModel, finder, {\n      defaultMainType: 'series'\n    });\n    var seriesModel = parsedFinder.seriesModel;\n\n    if (\"development\" !== 'production') {\n      if (!seriesModel) {\n        warn('There is no specified series model');\n      }\n    }\n\n    var data = seriesModel.getData();\n    var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') ? parsedFinder.dataIndexInside : parsedFinder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(parsedFinder.dataIndex) : null;\n    return dataIndexInside != null ? getItemVisualFromData(data, dataIndexInside, visualType) : getVisualFromData(data, visualType);\n  };\n  /**\n   * Get view of corresponding component model\n   */\n\n\n  ECharts.prototype.getViewOfComponentModel = function (componentModel) {\n    return this._componentsMap[componentModel.__viewId];\n  };\n  /**\n   * Get view of corresponding series model\n   */\n\n\n  ECharts.prototype.getViewOfSeriesModel = function (seriesModel) {\n    return this._chartsMap[seriesModel.__viewId];\n  };\n\n  ECharts.prototype._initEvents = function () {\n    var _this = this;\n\n    each(MOUSE_EVENT_NAMES, function (eveName) {\n      var handler = function (e) {\n        var ecModel = _this.getModel();\n\n        var el = e.target;\n        var params;\n        var isGlobalOut = eveName === 'globalout'; // no e.target when 'globalout'.\n\n        if (isGlobalOut) {\n          params = {};\n        } else {\n          el && findEventDispatcher(el, function (parent) {\n            var ecData = getECData(parent);\n\n            if (ecData && ecData.dataIndex != null) {\n              var dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex);\n              params = dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType) || {};\n              return true;\n            } // If element has custom eventData of components\n            else if (ecData.eventData) {\n                params = extend({}, ecData.eventData);\n                return true;\n              }\n          }, true);\n        } // Contract: if params prepared in mouse event,\n        // these properties must be specified:\n        // {\n        //    componentType: string (component main type)\n        //    componentIndex: number\n        // }\n        // Otherwise event query can not work.\n\n\n        if (params) {\n          var componentType = params.componentType;\n          var componentIndex = params.componentIndex; // Special handling for historic reason: when trigger by\n          // markLine/markPoint/markArea, the componentType is\n          // 'markLine'/'markPoint'/'markArea', but we should better\n          // enable them to be queried by seriesIndex, since their\n          // option is set in each series.\n\n          if (componentType === 'markLine' || componentType === 'markPoint' || componentType === 'markArea') {\n            componentType = 'series';\n            componentIndex = params.seriesIndex;\n          }\n\n          var model = componentType && componentIndex != null && ecModel.getComponent(componentType, componentIndex);\n          var view = model && _this[model.mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId];\n\n          if (\"development\" !== 'production') {\n            // `event.componentType` and `event[componentTpype + 'Index']` must not\n            // be missed, otherwise there is no way to distinguish source component.\n            // See `dataFormat.getDataParams`.\n            if (!isGlobalOut && !(model && view)) {\n              warn('model or view can not be found by params');\n            }\n          }\n\n          params.event = e;\n          params.type = eveName;\n          _this._$eventProcessor.eventInfo = {\n            targetEl: el,\n            packedEvent: params,\n            model: model,\n            view: view\n          };\n\n          _this.trigger(eveName, params);\n        }\n      }; // Consider that some component (like tooltip, brush, ...)\n      // register zr event handler, but user event handler might\n      // do anything, such as call `setOption` or `dispatchAction`,\n      // which probably update any of the content and probably\n      // cause problem if it is called previous other inner handlers.\n\n\n      handler.zrEventfulCallAtLast = true;\n\n      _this._zr.on(eveName, handler, _this);\n    });\n    each(eventActionMap, function (actionType, eventType) {\n      _this._messageCenter.on(eventType, function (event) {\n        this.trigger(eventType, event);\n      }, _this);\n    }); // Extra events\n    // TODO register?\n\n    each(['selectchanged'], function (eventType) {\n      _this._messageCenter.on(eventType, function (event) {\n        this.trigger(eventType, event);\n      }, _this);\n    });\n    handleLegacySelectEvents(this._messageCenter, this, this._api);\n  };\n\n  ECharts.prototype.isDisposed = function () {\n    return this._disposed;\n  };\n\n  ECharts.prototype.clear = function () {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    this.setOption({\n      series: []\n    }, true);\n  };\n\n  ECharts.prototype.dispose = function () {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    this._disposed = true;\n    var dom = this.getDom();\n\n    if (dom) {\n      setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');\n    }\n\n    var chart = this;\n    var api = chart._api;\n    var ecModel = chart._model;\n    each(chart._componentsViews, function (component) {\n      component.dispose(ecModel, api);\n    });\n    each(chart._chartsViews, function (chart) {\n      chart.dispose(ecModel, api);\n    }); // Dispose after all views disposed\n\n    chart._zr.dispose(); // Set properties to null.\n    // To reduce the memory cost in case the top code still holds this instance unexpectedly.\n\n\n    chart._dom = chart._model = chart._chartsMap = chart._componentsMap = chart._chartsViews = chart._componentsViews = chart._scheduler = chart._api = chart._zr = chart._throttledZrFlush = chart._theme = chart._coordSysMgr = chart._messageCenter = null;\n    delete instances$1[chart.id];\n  };\n  /**\n   * Resize the chart\n   */\n\n\n  ECharts.prototype.resize = function (opts) {\n    if (this[IN_MAIN_PROCESS_KEY]) {\n      if (\"development\" !== 'production') {\n        error('`resize` should not be called during main process.');\n      }\n\n      return;\n    }\n\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    this._zr.resize(opts);\n\n    var ecModel = this._model; // Resize loading effect\n\n    this._loadingFX && this._loadingFX.resize();\n\n    if (!ecModel) {\n      return;\n    }\n\n    var needPrepare = ecModel.resetOption('media');\n    var silent = opts && opts.silent; // There is some real cases that:\n    // chart.setOption(option, { lazyUpdate: true });\n    // chart.resize();\n\n    if (this[PENDING_UPDATE]) {\n      if (silent == null) {\n        silent = this[PENDING_UPDATE].silent;\n      }\n\n      needPrepare = true;\n      this[PENDING_UPDATE] = null;\n    }\n\n    this[IN_MAIN_PROCESS_KEY] = true;\n\n    try {\n      needPrepare && prepare(this);\n      updateMethods.update.call(this, {\n        type: 'resize',\n        animation: extend({\n          // Disable animation\n          duration: 0\n        }, opts && opts.animation)\n      });\n    } catch (e) {\n      this[IN_MAIN_PROCESS_KEY] = false;\n      throw e;\n    }\n\n    this[IN_MAIN_PROCESS_KEY] = false;\n    flushPendingActions.call(this, silent);\n    triggerUpdatedEvent.call(this, silent);\n  };\n\n  ECharts.prototype.showLoading = function (name, cfg) {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    if (isObject(name)) {\n      cfg = name;\n      name = '';\n    }\n\n    name = name || 'default';\n    this.hideLoading();\n\n    if (!loadingEffects[name]) {\n      if (\"development\" !== 'production') {\n        warn('Loading effects ' + name + ' not exists.');\n      }\n\n      return;\n    }\n\n    var el = loadingEffects[name](this._api, cfg);\n    var zr = this._zr;\n    this._loadingFX = el;\n    zr.add(el);\n  };\n  /**\n   * Hide loading effect\n   */\n\n\n  ECharts.prototype.hideLoading = function () {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    this._loadingFX && this._zr.remove(this._loadingFX);\n    this._loadingFX = null;\n  };\n\n  ECharts.prototype.makeActionFromEvent = function (eventObj) {\n    var payload = extend({}, eventObj);\n    payload.type = eventActionMap[eventObj.type];\n    return payload;\n  };\n  /**\n   * @param opt If pass boolean, means opt.silent\n   * @param opt.silent Default `false`. Whether trigger events.\n   * @param opt.flush Default `undefined`.\n   *        true: Flush immediately, and then pixel in canvas can be fetched\n   *            immediately. Caution: it might affect performance.\n   *        false: Not flush.\n   *        undefined: Auto decide whether perform flush.\n   */\n\n\n  ECharts.prototype.dispatchAction = function (payload, opt) {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    if (!isObject(opt)) {\n      opt = {\n        silent: !!opt\n      };\n    }\n\n    if (!actions[payload.type]) {\n      return;\n    } // Avoid dispatch action before setOption. Especially in `connect`.\n\n\n    if (!this._model) {\n      return;\n    } // May dispatchAction in rendering procedure\n\n\n    if (this[IN_MAIN_PROCESS_KEY]) {\n      this._pendingActions.push(payload);\n\n      return;\n    }\n\n    var silent = opt.silent;\n    doDispatchAction.call(this, payload, silent);\n    var flush = opt.flush;\n\n    if (flush) {\n      this._zr.flush();\n    } else if (flush !== false && env.browser.weChat) {\n      // In WeChat embedded browser, `requestAnimationFrame` and `setInterval`\n      // hang when sliding page (on touch event), which cause that zr does not\n      // refresh until user interaction finished, which is not expected.\n      // But `dispatchAction` may be called too frequently when pan on touch\n      // screen, which impacts performance if do not throttle them.\n      this._throttledZrFlush();\n    }\n\n    flushPendingActions.call(this, silent);\n    triggerUpdatedEvent.call(this, silent);\n  };\n\n  ECharts.prototype.updateLabelLayout = function () {\n    lifecycle.trigger('series:layoutlabels', this._model, this._api, {\n      // Not adding series labels.\n      // TODO\n      updatedSeries: []\n    });\n  };\n\n  ECharts.prototype.appendData = function (params) {\n    if (this._disposed) {\n      disposedWarning(this.id);\n      return;\n    }\n\n    var seriesIndex = params.seriesIndex;\n    var ecModel = this.getModel();\n    var seriesModel = ecModel.getSeriesByIndex(seriesIndex);\n\n    if (\"development\" !== 'production') {\n      assert(params.data && seriesModel);\n    }\n\n    seriesModel.appendData(params); // Note: `appendData` does not support that update extent of coordinate\n    // system, util some scenario require that. In the expected usage of\n    // `appendData`, the initial extent of coordinate system should better\n    // be fixed by axis `min`/`max` setting or initial data, otherwise if\n    // the extent changed while `appendData`, the location of the painted\n    // graphic elements have to be changed, which make the usage of\n    // `appendData` meaningless.\n\n    this._scheduler.unfinished = true;\n    this.getZr().wakeUp();\n  }; // A work around for no `internal` modifier in ts yet but\n  // need to strictly hide private methods to JS users.\n\n\n  ECharts.internalField = function () {\n    prepare = function (ecIns) {\n      var scheduler = ecIns._scheduler;\n      scheduler.restorePipelines(ecIns._model);\n      scheduler.prepareStageTasks();\n      prepareView(ecIns, true);\n      prepareView(ecIns, false);\n      scheduler.plan();\n    };\n    /**\n     * Prepare view instances of charts and components\n     */\n\n\n    prepareView = function (ecIns, isComponent) {\n      var ecModel = ecIns._model;\n      var scheduler = ecIns._scheduler;\n      var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;\n      var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;\n      var zr = ecIns._zr;\n      var api = ecIns._api;\n\n      for (var i = 0; i < viewList.length; i++) {\n        viewList[i].__alive = false;\n      }\n\n      isComponent ? ecModel.eachComponent(function (componentType, model) {\n        componentType !== 'series' && doPrepare(model);\n      }) : ecModel.eachSeries(doPrepare);\n\n      function doPrepare(model) {\n        // By default view will be reused if possible for the case that `setOption` with \"notMerge\"\n        // mode and need to enable transition animation. (Usually, when they have the same id, or\n        // especially no id but have the same type & name & index. See the `model.id` generation\n        // rule in `makeIdAndName` and `viewId` generation rule here).\n        // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that\n        // the new model has nothing to do with the old model.\n        var requireNewView = model.__requireNewView; // This command should not work twice.\n\n        model.__requireNewView = false; // Consider: id same and type changed.\n\n        var viewId = '_ec_' + model.id + '_' + model.type;\n        var view = !requireNewView && viewMap[viewId];\n\n        if (!view) {\n          var classType = parseClassType(model.type);\n          var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : // FIXME:TS\n          // (ChartView as ChartViewConstructor).getClass('series', classType.sub)\n          // For backward compat, still support a chart type declared as only subType\n          // like \"liquidfill\", but recommend \"series.liquidfill\"\n          // But need a base class to make a type series.\n          ChartView.getClass(classType.sub);\n\n          if (\"development\" !== 'production') {\n            assert(Clazz, classType.sub + ' does not exist.');\n          }\n\n          view = new Clazz();\n          view.init(ecModel, api);\n          viewMap[viewId] = view;\n          viewList.push(view);\n          zr.add(view.group);\n        }\n\n        model.__viewId = view.__id = viewId;\n        view.__alive = true;\n        view.__model = model;\n        view.group.__ecComponentInfo = {\n          mainType: model.mainType,\n          index: model.componentIndex\n        };\n        !isComponent && scheduler.prepareView(view, model, ecModel, api);\n      }\n\n      for (var i = 0; i < viewList.length;) {\n        var view = viewList[i];\n\n        if (!view.__alive) {\n          !isComponent && view.renderTask.dispose();\n          zr.remove(view.group);\n          view.dispose(ecModel, api);\n          viewList.splice(i, 1);\n\n          if (viewMap[view.__id] === view) {\n            delete viewMap[view.__id];\n          }\n\n          view.__id = view.group.__ecComponentInfo = null;\n        } else {\n          i++;\n        }\n      }\n    };\n\n    updateDirectly = function (ecIns, method, payload, mainType, subType) {\n      var ecModel = ecIns._model;\n      ecModel.setUpdatePayload(payload); // broadcast\n\n      if (!mainType) {\n        // FIXME\n        // Chart will not be update directly here, except set dirty.\n        // But there is no such scenario now.\n        each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView);\n        return;\n      }\n\n      var query = {};\n      query[mainType + 'Id'] = payload[mainType + 'Id'];\n      query[mainType + 'Index'] = payload[mainType + 'Index'];\n      query[mainType + 'Name'] = payload[mainType + 'Name'];\n      var condition = {\n        mainType: mainType,\n        query: query\n      };\n      subType && (condition.subType = subType); // subType may be '' by parseClassType;\n\n      var excludeSeriesId = payload.excludeSeriesId;\n      var excludeSeriesIdMap;\n\n      if (excludeSeriesId != null) {\n        excludeSeriesIdMap = createHashMap();\n        each(normalizeToArray(excludeSeriesId), function (id) {\n          var modelId = convertOptionIdName(id, null);\n\n          if (modelId != null) {\n            excludeSeriesIdMap.set(modelId, true);\n          }\n        });\n      } // If dispatchAction before setOption, do nothing.\n\n\n      ecModel && ecModel.eachComponent(condition, function (model) {\n        var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n        if (isExcluded) {\n          return;\n        }\n\n        if (isHighDownPayload(payload)) {\n          if (model instanceof SeriesModel) {\n            if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur && !model.get(['emphasis', 'disabled'])) {\n              blurSeriesFromHighlightPayload(model, payload, ecIns._api);\n            }\n          } else {\n            var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api),\n                focusSelf = _a.focusSelf,\n                dispatchers = _a.dispatchers;\n\n            if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) {\n              blurComponent(model.mainType, model.componentIndex, ecIns._api);\n            } // PENDING:\n            // Whether to put this \"enter emphasis\" code in `ComponentView`,\n            // which will be the same as `ChartView` but might be not necessary\n            // and will be far from this logic.\n\n\n            if (dispatchers) {\n              each(dispatchers, function (dispatcher) {\n                payload.type === HIGHLIGHT_ACTION_TYPE ? enterEmphasis(dispatcher) : leaveEmphasis(dispatcher);\n              });\n            }\n          }\n        } else if (isSelectChangePayload(payload)) {\n          // TODO geo\n          if (model instanceof SeriesModel) {\n            toggleSelectionFromPayload(model, payload, ecIns._api);\n            updateSeriesElementSelection(model);\n            markStatusToUpdate(ecIns);\n          }\n        }\n      }, ecIns);\n      ecModel && ecModel.eachComponent(condition, function (model) {\n        var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n        if (isExcluded) {\n          return;\n        }\n        callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]);\n      }, ecIns);\n\n      function callView(view) {\n        view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload);\n      }\n    };\n\n    updateMethods = {\n      prepareAndUpdate: function (payload) {\n        prepare(this);\n        updateMethods.update.call(this, payload, {\n          // Needs to mark option changed if newOption is given.\n          // It's from MagicType.\n          // TODO If use a separate flag optionChanged in payload?\n          optionChanged: payload.newOption != null\n        });\n      },\n      update: function (payload, updateParams) {\n        var ecModel = this._model;\n        var api = this._api;\n        var zr = this._zr;\n        var coordSysMgr = this._coordSysMgr;\n        var scheduler = this._scheduler; // update before setOption\n\n        if (!ecModel) {\n          return;\n        }\n\n        ecModel.setUpdatePayload(payload);\n        scheduler.restoreData(ecModel, payload);\n        scheduler.performSeriesTasks(ecModel); // TODO\n        // Save total ecModel here for undo/redo (after restoring data and before processing data).\n        // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.\n        // Create new coordinate system each update\n        // In LineView may save the old coordinate system and use it to get the original point.\n\n        coordSysMgr.create(ecModel, api);\n        scheduler.performDataProcessorTasks(ecModel, payload); // Current stream render is not supported in data process. So we can update\n        // stream modes after data processing, where the filtered data is used to\n        // determine whether to use progressive rendering.\n\n        updateStreamModes(this, ecModel); // We update stream modes before coordinate system updated, then the modes info\n        // can be fetched when coord sys updating (consider the barGrid extent fix). But\n        // the drawback is the full coord info can not be fetched. Fortunately this full\n        // coord is not required in stream mode updater currently.\n\n        coordSysMgr.update(ecModel, api);\n        clearColorPalette(ecModel);\n        scheduler.performVisualTasks(ecModel, payload);\n        render(this, ecModel, api, payload, updateParams); // Set background\n\n        var backgroundColor = ecModel.get('backgroundColor') || 'transparent';\n        var darkMode = ecModel.get('darkMode');\n        zr.setBackgroundColor(backgroundColor); // Force set dark mode.\n\n        if (darkMode != null && darkMode !== 'auto') {\n          zr.setDarkMode(darkMode);\n        }\n\n        lifecycle.trigger('afterupdate', ecModel, api);\n      },\n      updateTransform: function (payload) {\n        var _this = this;\n\n        var ecModel = this._model;\n        var api = this._api; // update before setOption\n\n        if (!ecModel) {\n          return;\n        }\n\n        ecModel.setUpdatePayload(payload); // ChartView.markUpdateMethod(payload, 'updateTransform');\n\n        var componentDirtyList = [];\n        ecModel.eachComponent(function (componentType, componentModel) {\n          if (componentType === 'series') {\n            return;\n          }\n\n          var componentView = _this.getViewOfComponentModel(componentModel);\n\n          if (componentView && componentView.__alive) {\n            if (componentView.updateTransform) {\n              var result = componentView.updateTransform(componentModel, ecModel, api, payload);\n              result && result.update && componentDirtyList.push(componentView);\n            } else {\n              componentDirtyList.push(componentView);\n            }\n          }\n        });\n        var seriesDirtyMap = createHashMap();\n        ecModel.eachSeries(function (seriesModel) {\n          var chartView = _this._chartsMap[seriesModel.__viewId];\n\n          if (chartView.updateTransform) {\n            var result = chartView.updateTransform(seriesModel, ecModel, api, payload);\n            result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);\n          } else {\n            seriesDirtyMap.set(seriesModel.uid, 1);\n          }\n        });\n        clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n        // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);\n\n        this._scheduler.performVisualTasks(ecModel, payload, {\n          setDirty: true,\n          dirtyMap: seriesDirtyMap\n        }); // Currently, not call render of components. Geo render cost a lot.\n        // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);\n\n\n        renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap);\n        lifecycle.trigger('afterupdate', ecModel, api);\n      },\n      updateView: function (payload) {\n        var ecModel = this._model; // update before setOption\n\n        if (!ecModel) {\n          return;\n        }\n\n        ecModel.setUpdatePayload(payload);\n        ChartView.markUpdateMethod(payload, 'updateView');\n        clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n        this._scheduler.performVisualTasks(ecModel, payload, {\n          setDirty: true\n        });\n\n        render(this, ecModel, this._api, payload, {});\n        lifecycle.trigger('afterupdate', ecModel, this._api);\n      },\n      updateVisual: function (payload) {\n        // updateMethods.update.call(this, payload);\n        var _this = this;\n\n        var ecModel = this._model; // update before setOption\n\n        if (!ecModel) {\n          return;\n        }\n\n        ecModel.setUpdatePayload(payload); // clear all visual\n\n        ecModel.eachSeries(function (seriesModel) {\n          seriesModel.getData().clearAllVisual();\n        }); // Perform visual\n\n        ChartView.markUpdateMethod(payload, 'updateVisual');\n        clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n        this._scheduler.performVisualTasks(ecModel, payload, {\n          visualType: 'visual',\n          setDirty: true\n        });\n\n        ecModel.eachComponent(function (componentType, componentModel) {\n          if (componentType !== 'series') {\n            var componentView = _this.getViewOfComponentModel(componentModel);\n\n            componentView && componentView.__alive && componentView.updateVisual(componentModel, ecModel, _this._api, payload);\n          }\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          var chartView = _this._chartsMap[seriesModel.__viewId];\n          chartView.updateVisual(seriesModel, ecModel, _this._api, payload);\n        });\n        lifecycle.trigger('afterupdate', ecModel, this._api);\n      },\n      updateLayout: function (payload) {\n        updateMethods.update.call(this, payload);\n      }\n    };\n\n    doConvertPixel = function (ecIns, methodName, finder, value) {\n      if (ecIns._disposed) {\n        disposedWarning(ecIns.id);\n        return;\n      }\n\n      var ecModel = ecIns._model;\n\n      var coordSysList = ecIns._coordSysMgr.getCoordinateSystems();\n\n      var result;\n      var parsedFinder = parseFinder(ecModel, finder);\n\n      for (var i = 0; i < coordSysList.length; i++) {\n        var coordSys = coordSysList[i];\n\n        if (coordSys[methodName] && (result = coordSys[methodName](ecModel, parsedFinder, value)) != null) {\n          return result;\n        }\n      }\n\n      if (\"development\" !== 'production') {\n        warn('No coordinate system that supports ' + methodName + ' found by the given finder.');\n      }\n    };\n\n    updateStreamModes = function (ecIns, ecModel) {\n      var chartsMap = ecIns._chartsMap;\n      var scheduler = ecIns._scheduler;\n      ecModel.eachSeries(function (seriesModel) {\n        scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);\n      });\n    };\n\n    doDispatchAction = function (payload, silent) {\n      var _this = this;\n\n      var ecModel = this.getModel();\n      var payloadType = payload.type;\n      var escapeConnect = payload.escapeConnect;\n      var actionWrap = actions[payloadType];\n      var actionInfo = actionWrap.actionInfo;\n      var cptTypeTmp = (actionInfo.update || 'update').split(':');\n      var updateMethod = cptTypeTmp.pop();\n      var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]);\n      this[IN_MAIN_PROCESS_KEY] = true;\n      var payloads = [payload];\n      var batched = false; // Batch action\n\n      if (payload.batch) {\n        batched = true;\n        payloads = map(payload.batch, function (item) {\n          item = defaults(extend({}, item), payload);\n          item.batch = null;\n          return item;\n        });\n      }\n\n      var eventObjBatch = [];\n      var eventObj;\n      var isSelectChange = isSelectChangePayload(payload);\n      var isHighDown = isHighDownPayload(payload); // Only leave blur once if there are multiple batches.\n\n      if (isHighDown) {\n        allLeaveBlur(this._api);\n      }\n\n      each(payloads, function (batchItem) {\n        // Action can specify the event by return it.\n        eventObj = actionWrap.action(batchItem, _this._model, _this._api); // Emit event outside\n\n        eventObj = eventObj || extend({}, batchItem); // Convert type to eventType\n\n        eventObj.type = actionInfo.event || eventObj.type;\n        eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.\n\n        if (isHighDown) {\n          var _a = preParseFinder(payload),\n              queryOptionMap = _a.queryOptionMap,\n              mainTypeSpecified = _a.mainTypeSpecified;\n\n          var componentMainType = mainTypeSpecified ? queryOptionMap.keys()[0] : 'series';\n          updateDirectly(_this, updateMethod, batchItem, componentMainType);\n          markStatusToUpdate(_this);\n        } else if (isSelectChange) {\n          // At present `dispatchAction({ type: 'select', ... })` is not supported on components.\n          // geo still use 'geoselect'.\n          updateDirectly(_this, updateMethod, batchItem, 'series');\n          markStatusToUpdate(_this);\n        } else if (cptType) {\n          updateDirectly(_this, updateMethod, batchItem, cptType.main, cptType.sub);\n        }\n      });\n\n      if (updateMethod !== 'none' && !isHighDown && !isSelectChange && !cptType) {\n        try {\n          // Still dirty\n          if (this[PENDING_UPDATE]) {\n            prepare(this);\n            updateMethods.update.call(this, payload);\n            this[PENDING_UPDATE] = null;\n          } else {\n            updateMethods[updateMethod].call(this, payload);\n          }\n        } catch (e) {\n          this[IN_MAIN_PROCESS_KEY] = false;\n          throw e;\n        }\n      } // Follow the rule of action batch\n\n\n      if (batched) {\n        eventObj = {\n          type: actionInfo.event || payloadType,\n          escapeConnect: escapeConnect,\n          batch: eventObjBatch\n        };\n      } else {\n        eventObj = eventObjBatch[0];\n      }\n\n      this[IN_MAIN_PROCESS_KEY] = false;\n\n      if (!silent) {\n        var messageCenter = this._messageCenter;\n        messageCenter.trigger(eventObj.type, eventObj); // Extra triggered 'selectchanged' event\n\n        if (isSelectChange) {\n          var newObj = {\n            type: 'selectchanged',\n            escapeConnect: escapeConnect,\n            selected: getAllSelectedIndices(ecModel),\n            isFromClick: payload.isFromClick || false,\n            fromAction: payload.type,\n            fromActionPayload: payload\n          };\n          messageCenter.trigger(newObj.type, newObj);\n        }\n      }\n    };\n\n    flushPendingActions = function (silent) {\n      var pendingActions = this._pendingActions;\n\n      while (pendingActions.length) {\n        var payload = pendingActions.shift();\n        doDispatchAction.call(this, payload, silent);\n      }\n    };\n\n    triggerUpdatedEvent = function (silent) {\n      !silent && this.trigger('updated');\n    };\n    /**\n     * Event `rendered` is triggered when zr\n     * rendered. It is useful for realtime\n     * snapshot (reflect animation).\n     *\n     * Event `finished` is triggered when:\n     * (1) zrender rendering finished.\n     * (2) initial animation finished.\n     * (3) progressive rendering finished.\n     * (4) no pending action.\n     * (5) no delayed setOption needs to be processed.\n     */\n\n\n    bindRenderedEvent = function (zr, ecIns) {\n      zr.on('rendered', function (params) {\n        ecIns.trigger('rendered', params); // The `finished` event should not be triggered repeatedly,\n        // so it should only be triggered when rendering indeed happens\n        // in zrender. (Consider the case that dipatchAction is keep\n        // triggering when mouse move).\n\n        if ( // Although zr is dirty if initial animation is not finished\n        // and this checking is called on frame, we also check\n        // animation finished for robustness.\n        zr.animation.isFinished() && !ecIns[PENDING_UPDATE] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) {\n          ecIns.trigger('finished');\n        }\n      });\n    };\n\n    bindMouseEvent = function (zr, ecIns) {\n      zr.on('mouseover', function (e) {\n        var el = e.target;\n        var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n        if (dispatcher) {\n          handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api);\n          markStatusToUpdate(ecIns);\n        }\n      }).on('mouseout', function (e) {\n        var el = e.target;\n        var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n        if (dispatcher) {\n          handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api);\n          markStatusToUpdate(ecIns);\n        }\n      }).on('click', function (e) {\n        var el = e.target;\n        var dispatcher = findEventDispatcher(el, function (target) {\n          return getECData(target).dataIndex != null;\n        }, true);\n\n        if (dispatcher) {\n          var actionType = dispatcher.selected ? 'unselect' : 'select';\n          var ecData = getECData(dispatcher);\n\n          ecIns._api.dispatchAction({\n            type: actionType,\n            dataType: ecData.dataType,\n            dataIndexInside: ecData.dataIndex,\n            seriesIndex: ecData.seriesIndex,\n            isFromClick: true\n          });\n        }\n      });\n    };\n\n    function clearColorPalette(ecModel) {\n      ecModel.clearColorPalette();\n      ecModel.eachSeries(function (seriesModel) {\n        seriesModel.clearColorPalette();\n      });\n    }\n\n    function allocateZlevels(ecModel) {\n      var componentZLevels = [];\n      var seriesZLevels = [];\n      var hasSeperateZLevel = false;\n      ecModel.eachComponent(function (componentType, componentModel) {\n        var zlevel = componentModel.get('zlevel') || 0;\n        var z = componentModel.get('z') || 0;\n        var zlevelKey = componentModel.getZLevelKey();\n        hasSeperateZLevel = hasSeperateZLevel || !!zlevelKey;\n        (componentType === 'series' ? seriesZLevels : componentZLevels).push({\n          zlevel: zlevel,\n          z: z,\n          idx: componentModel.componentIndex,\n          type: componentType,\n          key: zlevelKey\n        });\n      });\n\n      if (hasSeperateZLevel) {\n        // Series after component\n        var zLevels = componentZLevels.concat(seriesZLevels);\n        var lastSeriesZLevel_1;\n        var lastSeriesKey_1;\n        sort(zLevels, function (a, b) {\n          if (a.zlevel === b.zlevel) {\n            return a.z - b.z;\n          }\n\n          return a.zlevel - b.zlevel;\n        });\n        each(zLevels, function (item) {\n          var componentModel = ecModel.getComponent(item.type, item.idx);\n          var zlevel = item.zlevel;\n          var key = item.key;\n\n          if (lastSeriesZLevel_1 != null) {\n            zlevel = Math.max(lastSeriesZLevel_1, zlevel);\n          }\n\n          if (key) {\n            if (zlevel === lastSeriesZLevel_1 && key !== lastSeriesKey_1) {\n              zlevel++;\n            }\n\n            lastSeriesKey_1 = key;\n          } else if (lastSeriesKey_1) {\n            if (zlevel === lastSeriesZLevel_1) {\n              zlevel++;\n            }\n\n            lastSeriesKey_1 = '';\n          }\n\n          lastSeriesZLevel_1 = zlevel;\n          componentModel.setZLevel(zlevel);\n        });\n      }\n    }\n\n    render = function (ecIns, ecModel, api, payload, updateParams) {\n      allocateZlevels(ecModel);\n      renderComponents(ecIns, ecModel, api, payload, updateParams);\n      each(ecIns._chartsViews, function (chart) {\n        chart.__alive = false;\n      });\n      renderSeries(ecIns, ecModel, api, payload, updateParams); // Remove groups of unrendered charts\n\n      each(ecIns._chartsViews, function (chart) {\n        if (!chart.__alive) {\n          chart.remove(ecModel, api);\n        }\n      });\n    };\n\n    renderComponents = function (ecIns, ecModel, api, payload, updateParams, dirtyList) {\n      each(dirtyList || ecIns._componentsViews, function (componentView) {\n        var componentModel = componentView.__model;\n        clearStates(componentModel, componentView);\n        componentView.render(componentModel, ecModel, api, payload);\n        updateZ(componentModel, componentView);\n        updateStates(componentModel, componentView);\n      });\n    };\n    /**\n     * Render each chart and component\n     */\n\n\n    renderSeries = function (ecIns, ecModel, api, payload, updateParams, dirtyMap) {\n      // Render all charts\n      var scheduler = ecIns._scheduler;\n      updateParams = extend(updateParams || {}, {\n        updatedSeries: ecModel.getSeries()\n      }); // TODO progressive?\n\n      lifecycle.trigger('series:beforeupdate', ecModel, api, updateParams);\n      var unfinished = false;\n      ecModel.eachSeries(function (seriesModel) {\n        var chartView = ecIns._chartsMap[seriesModel.__viewId];\n        chartView.__alive = true;\n        var renderTask = chartView.renderTask;\n        scheduler.updatePayload(renderTask, payload); // TODO states on marker.\n\n        clearStates(seriesModel, chartView);\n\n        if (dirtyMap && dirtyMap.get(seriesModel.uid)) {\n          renderTask.dirty();\n        }\n\n        if (renderTask.perform(scheduler.getPerformArgs(renderTask))) {\n          unfinished = true;\n        }\n\n        chartView.group.silent = !!seriesModel.get('silent'); // Should not call markRedraw on group, because it will disable zrender\n        // incremental render (always render from the __startIndex each frame)\n        // chartView.group.markRedraw();\n\n        updateBlend(seriesModel, chartView);\n        updateSeriesElementSelection(seriesModel);\n      });\n      scheduler.unfinished = unfinished || scheduler.unfinished;\n      lifecycle.trigger('series:layoutlabels', ecModel, api, updateParams); // transition after label is layouted.\n\n      lifecycle.trigger('series:transition', ecModel, api, updateParams);\n      ecModel.eachSeries(function (seriesModel) {\n        var chartView = ecIns._chartsMap[seriesModel.__viewId]; // Update Z after labels updated. Before applying states.\n\n        updateZ(seriesModel, chartView); // NOTE: Update states after label is updated.\n        // label should be in normal status when layouting.\n\n        updateStates(seriesModel, chartView);\n      }); // If use hover layer\n\n      updateHoverLayerStatus(ecIns, ecModel);\n      lifecycle.trigger('series:afterupdate', ecModel, api, updateParams);\n    };\n\n    markStatusToUpdate = function (ecIns) {\n      ecIns[STATUS_NEEDS_UPDATE_KEY] = true; // Wake up zrender if it's sleep. Let it update states in the next frame.\n\n      ecIns.getZr().wakeUp();\n    };\n\n    applyChangedStates = function (ecIns) {\n      if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) {\n        return;\n      }\n\n      ecIns.getZr().storage.traverse(function (el) {\n        // Not applied on removed elements, it may still in fading.\n        if (isElementRemoved(el)) {\n          return;\n        }\n\n        applyElementStates(el);\n      });\n      ecIns[STATUS_NEEDS_UPDATE_KEY] = false;\n    };\n\n    function applyElementStates(el) {\n      var newStates = [];\n      var oldStates = el.currentStates; // Keep other states.\n\n      for (var i = 0; i < oldStates.length; i++) {\n        var stateName = oldStates[i];\n\n        if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) {\n          newStates.push(stateName);\n        }\n      } // Only use states when it's exists.\n\n\n      if (el.selected && el.states.select) {\n        newStates.push('select');\n      }\n\n      if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) {\n        newStates.push('emphasis');\n      } else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) {\n        newStates.push('blur');\n      }\n\n      el.useStates(newStates);\n    }\n\n    function updateHoverLayerStatus(ecIns, ecModel) {\n      var zr = ecIns._zr;\n      var storage = zr.storage;\n      var elCount = 0;\n      storage.traverse(function (el) {\n        if (!el.isGroup) {\n          elCount++;\n        }\n      });\n\n      if (elCount > ecModel.get('hoverLayerThreshold') && !env.node && !env.worker) {\n        ecModel.eachSeries(function (seriesModel) {\n          if (seriesModel.preventUsingHoverLayer) {\n            return;\n          }\n\n          var chartView = ecIns._chartsMap[seriesModel.__viewId];\n\n          if (chartView.__alive) {\n            chartView.eachRendered(function (el) {\n              if (el.states.emphasis) {\n                el.states.emphasis.hoverLayer = true;\n              }\n            });\n          }\n        });\n      }\n    }\n    /**\n     * Update chart and blend.\n     */\n\n    function updateBlend(seriesModel, chartView) {\n      var blendMode = seriesModel.get('blendMode') || null;\n      chartView.eachRendered(function (el) {\n        // FIXME marker and other components\n        if (!el.isGroup) {\n          // DON'T mark the element dirty. In case element is incremental and don't want to rerender.\n          el.style.blend = blendMode;\n        }\n      });\n    }\n\n    function updateZ(model, view) {\n      if (model.preventAutoZ) {\n        return;\n      }\n\n      var z = model.get('z') || 0;\n      var zlevel = model.get('zlevel') || 0; // Set z and zlevel\n\n      view.eachRendered(function (el) {\n        doUpdateZ(el, z, zlevel, -Infinity); // Don't traverse the children because it has been traversed in _updateZ.\n\n        return true;\n      });\n    }\n\n    function doUpdateZ(el, z, zlevel, maxZ2) {\n      // Group may also have textContent\n      var label = el.getTextContent();\n      var labelLine = el.getTextGuideLine();\n      var isGroup = el.isGroup;\n\n      if (isGroup) {\n        // set z & zlevel of children elements of Group\n        var children = el.childrenRef();\n\n        for (var i = 0; i < children.length; i++) {\n          maxZ2 = Math.max(doUpdateZ(children[i], z, zlevel, maxZ2), maxZ2);\n        }\n      } else {\n        // not Group\n        el.z = z;\n        el.zlevel = zlevel;\n        maxZ2 = Math.max(el.z2, maxZ2);\n      } // always set z and zlevel if label/labelLine exists\n\n\n      if (label) {\n        label.z = z;\n        label.zlevel = zlevel; // lift z2 of text content\n        // TODO if el.emphasis.z2 is spcefied, what about textContent.\n\n        isFinite(maxZ2) && (label.z2 = maxZ2 + 2);\n      }\n\n      if (labelLine) {\n        var textGuideLineConfig = el.textGuideLineConfig;\n        labelLine.z = z;\n        labelLine.zlevel = zlevel;\n        isFinite(maxZ2) && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1));\n      }\n\n      return maxZ2;\n    } // Clear states without animation.\n    // TODO States on component.\n\n\n    function clearStates(model, view) {\n      view.eachRendered(function (el) {\n        // Not applied on removed elements, it may still in fading.\n        if (isElementRemoved(el)) {\n          return;\n        }\n\n        var textContent = el.getTextContent();\n        var textGuide = el.getTextGuideLine();\n\n        if (el.stateTransition) {\n          el.stateTransition = null;\n        }\n\n        if (textContent && textContent.stateTransition) {\n          textContent.stateTransition = null;\n        }\n\n        if (textGuide && textGuide.stateTransition) {\n          textGuide.stateTransition = null;\n        } // TODO If el is incremental.\n\n\n        if (el.hasState()) {\n          el.prevStates = el.currentStates;\n          el.clearStates();\n        } else if (el.prevStates) {\n          el.prevStates = null;\n        }\n      });\n    }\n\n    function updateStates(model, view) {\n      var stateAnimationModel = model.getModel('stateAnimation');\n      var enableAnimation = model.isAnimationEnabled();\n      var duration = stateAnimationModel.get('duration');\n      var stateTransition = duration > 0 ? {\n        duration: duration,\n        delay: stateAnimationModel.get('delay'),\n        easing: stateAnimationModel.get('easing') // additive: stateAnimationModel.get('additive')\n\n      } : null;\n      view.eachRendered(function (el) {\n        if (el.states && el.states.emphasis) {\n          // Not applied on removed elements, it may still in fading.\n          if (isElementRemoved(el)) {\n            return;\n          }\n\n          if (el instanceof Path) {\n            savePathStates(el);\n          } // Only updated on changed element. In case element is incremental and don't want to rerender.\n          // TODO, a more proper way?\n\n\n          if (el.__dirty) {\n            var prevStates = el.prevStates; // Restore states without animation\n\n            if (prevStates) {\n              el.useStates(prevStates);\n            }\n          } // Update state transition and enable animation again.\n\n\n          if (enableAnimation) {\n            el.stateTransition = stateTransition;\n            var textContent = el.getTextContent();\n            var textGuide = el.getTextGuideLine(); // TODO Is it necessary to animate label?\n\n            if (textContent) {\n              textContent.stateTransition = stateTransition;\n            }\n\n            if (textGuide) {\n              textGuide.stateTransition = stateTransition;\n            }\n          } // Use highlighted and selected flag to toggle states.\n\n\n          if (el.__dirty) {\n            applyElementStates(el);\n          }\n        }\n      });\n    }\n\n    createExtensionAPI = function (ecIns) {\n      return new (\n      /** @class */\n      function (_super) {\n        __extends(class_1, _super);\n\n        function class_1() {\n          return _super !== null && _super.apply(this, arguments) || this;\n        }\n\n        class_1.prototype.getCoordinateSystems = function () {\n          return ecIns._coordSysMgr.getCoordinateSystems();\n        };\n\n        class_1.prototype.getComponentByElement = function (el) {\n          while (el) {\n            var modelInfo = el.__ecComponentInfo;\n\n            if (modelInfo != null) {\n              return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index);\n            }\n\n            el = el.parent;\n          }\n        };\n\n        class_1.prototype.enterEmphasis = function (el, highlightDigit) {\n          enterEmphasis(el, highlightDigit);\n          markStatusToUpdate(ecIns);\n        };\n\n        class_1.prototype.leaveEmphasis = function (el, highlightDigit) {\n          leaveEmphasis(el, highlightDigit);\n          markStatusToUpdate(ecIns);\n        };\n\n        class_1.prototype.enterBlur = function (el) {\n          enterBlur(el);\n          markStatusToUpdate(ecIns);\n        };\n\n        class_1.prototype.leaveBlur = function (el) {\n          leaveBlur(el);\n          markStatusToUpdate(ecIns);\n        };\n\n        class_1.prototype.enterSelect = function (el) {\n          enterSelect(el);\n          markStatusToUpdate(ecIns);\n        };\n\n        class_1.prototype.leaveSelect = function (el) {\n          leaveSelect(el);\n          markStatusToUpdate(ecIns);\n        };\n\n        class_1.prototype.getModel = function () {\n          return ecIns.getModel();\n        };\n\n        class_1.prototype.getViewOfComponentModel = function (componentModel) {\n          return ecIns.getViewOfComponentModel(componentModel);\n        };\n\n        class_1.prototype.getViewOfSeriesModel = function (seriesModel) {\n          return ecIns.getViewOfSeriesModel(seriesModel);\n        };\n\n        return class_1;\n      }(ExtensionAPI))(ecIns);\n    };\n\n    enableConnect = function (chart) {\n      function updateConnectedChartsStatus(charts, status) {\n        for (var i = 0; i < charts.length; i++) {\n          var otherChart = charts[i];\n          otherChart[CONNECT_STATUS_KEY] = status;\n        }\n      }\n\n      each(eventActionMap, function (actionType, eventType) {\n        chart._messageCenter.on(eventType, function (event) {\n          if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) {\n            if (event && event.escapeConnect) {\n              return;\n            }\n\n            var action_1 = chart.makeActionFromEvent(event);\n            var otherCharts_1 = [];\n            each(instances$1, function (otherChart) {\n              if (otherChart !== chart && otherChart.group === chart.group) {\n                otherCharts_1.push(otherChart);\n              }\n            });\n            updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_PENDING);\n            each(otherCharts_1, function (otherChart) {\n              if (otherChart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_UPDATING) {\n                otherChart.dispatchAction(action_1);\n              }\n            });\n            updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_UPDATED);\n          }\n        });\n      });\n    };\n  }();\n\n  return ECharts;\n}(Eventful);\n\nvar echartsProto = ECharts.prototype;\nechartsProto.on = createRegisterEventWithLowercaseECharts('on');\nechartsProto.off = createRegisterEventWithLowercaseECharts('off');\n/**\n * @deprecated\n */\n// @ts-ignore\n\nechartsProto.one = function (eventName, cb, ctx) {\n  var self = this;\n  deprecateLog('ECharts#one is deprecated.');\n\n  function wrapped() {\n    var args2 = [];\n\n    for (var _i = 0; _i < arguments.length; _i++) {\n      args2[_i] = arguments[_i];\n    }\n\n    cb && cb.apply && cb.apply(this, args2); // @ts-ignore\n\n    self.off(eventName, wrapped);\n  }\n\n  this.on.call(this, eventName, wrapped, ctx);\n};\n\nvar MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];\n\nfunction disposedWarning(id) {\n  if (\"development\" !== 'production') {\n    warn('Instance ' + id + ' has been disposed');\n  }\n}\n\nvar actions = {};\n/**\n * Map eventType to actionType\n */\n\nvar eventActionMap = {};\nvar dataProcessorFuncs = [];\nvar optionPreprocessorFuncs = [];\nvar visualFuncs = [];\nvar themeStorage = {};\nvar loadingEffects = {};\nvar instances$1 = {};\nvar connectedGroups = {};\nvar idBase = +new Date() - 0;\nvar groupIdBase = +new Date() - 0;\nvar DOM_ATTRIBUTE_KEY = '_echarts_instance_';\n/**\n * @param opts.devicePixelRatio Use window.devicePixelRatio by default\n * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart.\n * @param opts.width Use clientWidth of the input `dom` by default.\n *        Can be 'auto' (the same as null/undefined)\n * @param opts.height Use clientHeight of the input `dom` by default.\n *        Can be 'auto' (the same as null/undefined)\n * @param opts.locale Specify the locale.\n * @param opts.useDirtyRect Enable dirty rectangle rendering or not.\n */\n\nfunction init$1(dom, theme, opts) {\n  var isClient = !(opts && opts.ssr);\n\n  if (isClient) {\n    if (\"development\" !== 'production') {\n      if (!dom) {\n        throw new Error('Initialize failed: invalid dom.');\n      }\n    }\n\n    var existInstance = getInstanceByDom(dom);\n\n    if (existInstance) {\n      if (\"development\" !== 'production') {\n        warn('There is a chart instance already initialized on the dom.');\n      }\n\n      return existInstance;\n    }\n\n    if (\"development\" !== 'production') {\n      if (isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth && (!opts || opts.width == null) || !dom.clientHeight && (!opts || opts.height == null))) {\n        warn('Can\\'t get DOM width or height. Please check ' + 'dom.clientWidth and dom.clientHeight. They should not be 0.' + 'For example, you may need to call this in the callback ' + 'of window.onload.');\n      }\n    }\n  }\n\n  var chart = new ECharts(dom, theme, opts);\n  chart.id = 'ec_' + idBase++;\n  instances$1[chart.id] = chart;\n  isClient && setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);\n  enableConnect(chart);\n  lifecycle.trigger('afterinit', chart);\n  return chart;\n}\n/**\n * @usage\n * (A)\n * ```js\n * let chart1 = echarts.init(dom1);\n * let chart2 = echarts.init(dom2);\n * chart1.group = 'xxx';\n * chart2.group = 'xxx';\n * echarts.connect('xxx');\n * ```\n * (B)\n * ```js\n * let chart1 = echarts.init(dom1);\n * let chart2 = echarts.init(dom2);\n * echarts.connect('xxx', [chart1, chart2]);\n * ```\n */\n\nfunction connect(groupId) {\n  // Is array of charts\n  if (isArray(groupId)) {\n    var charts = groupId;\n    groupId = null; // If any chart has group\n\n    each(charts, function (chart) {\n      if (chart.group != null) {\n        groupId = chart.group;\n      }\n    });\n    groupId = groupId || 'g_' + groupIdBase++;\n    each(charts, function (chart) {\n      chart.group = groupId;\n    });\n  }\n\n  connectedGroups[groupId] = true;\n  return groupId;\n}\n/**\n * @deprecated\n */\n\nfunction disConnect(groupId) {\n  connectedGroups[groupId] = false;\n}\n/**\n * Alias and backward compatibility\n */\n\nvar disconnect = disConnect;\n/**\n * Dispose a chart instance\n */\n\nfunction dispose$1(chart) {\n  if (isString(chart)) {\n    chart = instances$1[chart];\n  } else if (!(chart instanceof ECharts)) {\n    // Try to treat as dom\n    chart = getInstanceByDom(chart);\n  }\n\n  if (chart instanceof ECharts && !chart.isDisposed()) {\n    chart.dispose();\n  }\n}\nfunction getInstanceByDom(dom) {\n  return instances$1[getAttribute(dom, DOM_ATTRIBUTE_KEY)];\n}\nfunction getInstanceById(key) {\n  return instances$1[key];\n}\n/**\n * Register theme\n */\n\nfunction registerTheme(name, theme) {\n  themeStorage[name] = theme;\n}\n/**\n * Register option preprocessor\n */\n\nfunction registerPreprocessor(preprocessorFunc) {\n  if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) {\n    optionPreprocessorFuncs.push(preprocessorFunc);\n  }\n}\nfunction registerProcessor(priority, processor) {\n  normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT);\n}\n/**\n * Register postIniter\n * @param {Function} postInitFunc\n */\n\nfunction registerPostInit(postInitFunc) {\n  registerUpdateLifecycle('afterinit', postInitFunc);\n}\n/**\n * Register postUpdater\n * @param {Function} postUpdateFunc\n */\n\nfunction registerPostUpdate(postUpdateFunc) {\n  registerUpdateLifecycle('afterupdate', postUpdateFunc);\n}\nfunction registerUpdateLifecycle(name, cb) {\n  lifecycle.on(name, cb);\n}\nfunction registerAction(actionInfo, eventName, action) {\n  if (isFunction(eventName)) {\n    action = eventName;\n    eventName = '';\n  }\n\n  var actionType = isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = {\n    event: eventName\n  }][0]; // Event name is all lowercase\n\n  actionInfo.event = (actionInfo.event || actionType).toLowerCase();\n  eventName = actionInfo.event;\n\n  if (eventActionMap[eventName]) {\n    // Already registered.\n    return;\n  } // Validate action type and event name.\n\n\n  assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));\n\n  if (!actions[actionType]) {\n    actions[actionType] = {\n      action: action,\n      actionInfo: actionInfo\n    };\n  }\n\n  eventActionMap[eventName] = actionType;\n}\nfunction registerCoordinateSystem(type, coordSysCreator) {\n  CoordinateSystemManager.register(type, coordSysCreator);\n}\n/**\n * Get dimensions of specified coordinate system.\n * @param {string} type\n * @return {Array.<string|Object>}\n */\n\nfunction getCoordinateSystemDimensions(type) {\n  var coordSysCreator = CoordinateSystemManager.get(type);\n\n  if (coordSysCreator) {\n    return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice();\n  }\n}\n\nfunction registerLayout(priority, layoutTask) {\n  normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');\n}\n\nfunction registerVisual(priority, visualTask) {\n  normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');\n}\nvar registeredTasks = [];\n\nfunction normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {\n  if (isFunction(priority) || isObject(priority)) {\n    fn = priority;\n    priority = defaultPriority;\n  }\n\n  if (\"development\" !== 'production') {\n    if (isNaN(priority) || priority == null) {\n      throw new Error('Illegal priority');\n    } // Check duplicate\n\n\n    each(targetList, function (wrap) {\n      assert(wrap.__raw !== fn);\n    });\n  } // Already registered\n\n\n  if (indexOf(registeredTasks, fn) >= 0) {\n    return;\n  }\n\n  registeredTasks.push(fn);\n  var stageHandler = Scheduler.wrapStageHandler(fn, visualType);\n  stageHandler.__prio = priority;\n  stageHandler.__raw = fn;\n  targetList.push(stageHandler);\n}\n\nfunction registerLoading(name, loadingFx) {\n  loadingEffects[name] = loadingFx;\n}\n/**\n * ZRender need a canvas context to do measureText.\n * But in node environment canvas may be created by node-canvas.\n * So we need to specify how to create a canvas instead of using document.createElement('canvas')\n *\n *\n * @deprecated use setPlatformAPI({ createCanvas }) instead.\n *\n * @example\n *     let Canvas = require('canvas');\n *     let echarts = require('echarts');\n *     echarts.setCanvasCreator(function () {\n *         // Small size is enough.\n *         return new Canvas(32, 32);\n *     });\n */\n\nfunction setCanvasCreator(creator) {\n  if (\"development\" !== 'production') {\n    deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.');\n  }\n\n  setPlatformAPI({\n    createCanvas: creator\n  });\n}\n/**\n * The parameters and usage: see `geoSourceManager.registerMap`.\n * Compatible with previous `echarts.registerMap`.\n */\n\nfunction registerMap(mapName, geoJson, specialAreas) {\n  var registerMap = getImpl('registerMap');\n  registerMap && registerMap(mapName, geoJson, specialAreas);\n}\nfunction getMap(mapName) {\n  var getMap = getImpl('getMap');\n  return getMap && getMap(mapName);\n}\nvar registerTransform = registerExternalTransform;\n/**\n * Globa dispatchAction to a specified chart instance.\n */\n// export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters<ECharts['dispatchAction']>[1]) {\n//     if (!payload || !payload.chartId) {\n//         // Must have chartId to find chart\n//         return;\n//     }\n//     const chart = instances[payload.chartId];\n//     if (chart) {\n//         chart.dispatchAction(payload, opt);\n//     }\n// }\n// Builtin global visual\n\nregisterVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask);\nregisterVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask);\nregisterVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataColorPaletteTask);\nregisterVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask);\nregisterVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask);\nregisterVisual(PRIORITY_VISUAL_DECAL, decalVisual);\nregisterPreprocessor(globalBackwardCompat);\nregisterProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack);\nregisterLoading('default', defaultLoading); // Default actions\n\nregisterAction({\n  type: HIGHLIGHT_ACTION_TYPE,\n  event: HIGHLIGHT_ACTION_TYPE,\n  update: HIGHLIGHT_ACTION_TYPE\n}, noop);\nregisterAction({\n  type: DOWNPLAY_ACTION_TYPE,\n  event: DOWNPLAY_ACTION_TYPE,\n  update: DOWNPLAY_ACTION_TYPE\n}, noop);\nregisterAction({\n  type: SELECT_ACTION_TYPE,\n  event: SELECT_ACTION_TYPE,\n  update: SELECT_ACTION_TYPE\n}, noop);\nregisterAction({\n  type: UNSELECT_ACTION_TYPE,\n  event: UNSELECT_ACTION_TYPE,\n  update: UNSELECT_ACTION_TYPE\n}, noop);\nregisterAction({\n  type: TOGGLE_SELECT_ACTION_TYPE,\n  event: TOGGLE_SELECT_ACTION_TYPE,\n  update: TOGGLE_SELECT_ACTION_TYPE\n}, noop); // Default theme\n\nregisterTheme('light', lightTheme);\nregisterTheme('dark', theme); // For backward compatibility, where the namespace `dataTool` will\n// be mounted on `echarts` is the extension `dataTool` is imported.\n\nvar dataTool = {};\n\nvar extensions = [];\nvar extensionRegisters = {\n  registerPreprocessor: registerPreprocessor,\n  registerProcessor: registerProcessor,\n  registerPostInit: registerPostInit,\n  registerPostUpdate: registerPostUpdate,\n  registerUpdateLifecycle: registerUpdateLifecycle,\n  registerAction: registerAction,\n  registerCoordinateSystem: registerCoordinateSystem,\n  registerLayout: registerLayout,\n  registerVisual: registerVisual,\n  registerTransform: registerTransform,\n  registerLoading: registerLoading,\n  registerMap: registerMap,\n  registerImpl: registerImpl,\n  PRIORITY: PRIORITY,\n  ComponentModel: ComponentModel,\n  ComponentView: ComponentView,\n  SeriesModel: SeriesModel,\n  ChartView: ChartView,\n  // TODO Use ComponentModel and SeriesModel instead of Constructor\n  registerComponentModel: function (ComponentModelClass) {\n    ComponentModel.registerClass(ComponentModelClass);\n  },\n  registerComponentView: function (ComponentViewClass) {\n    ComponentView.registerClass(ComponentViewClass);\n  },\n  registerSeriesModel: function (SeriesModelClass) {\n    SeriesModel.registerClass(SeriesModelClass);\n  },\n  registerChartView: function (ChartViewClass) {\n    ChartView.registerClass(ChartViewClass);\n  },\n  registerSubTypeDefaulter: function (componentType, defaulter) {\n    ComponentModel.registerSubTypeDefaulter(componentType, defaulter);\n  },\n  registerPainter: function (painterType, PainterCtor) {\n    registerPainter(painterType, PainterCtor);\n  }\n};\nfunction use(ext) {\n  if (isArray(ext)) {\n    // use([ChartLine, ChartBar]);\n    each(ext, function (singleExt) {\n      use(singleExt);\n    });\n    return;\n  }\n\n  if (indexOf(extensions, ext) >= 0) {\n    return;\n  }\n\n  extensions.push(ext);\n\n  if (isFunction(ext)) {\n    ext = {\n      install: ext\n    };\n  }\n\n  ext.install(extensionRegisters);\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction dataIndexMapValueLength(valNumOrArrLengthMoreThan2) {\n  return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1;\n}\n\nfunction defaultKeyGetter(item) {\n  return item;\n}\n\nvar DataDiffer =\n/** @class */\nfunction () {\n  /**\n   * @param context Can be visited by this.context in callback.\n   */\n  function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'.\n  diffMode) {\n    this._old = oldArr;\n    this._new = newArr;\n    this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;\n    this._newKeyGetter = newKeyGetter || defaultKeyGetter; // Visible in callback via `this.context`;\n\n    this.context = context;\n    this._diffModeMultiple = diffMode === 'multiple';\n  }\n  /**\n   * Callback function when add a data\n   */\n\n\n  DataDiffer.prototype.add = function (func) {\n    this._add = func;\n    return this;\n  };\n  /**\n   * Callback function when update a data\n   */\n\n\n  DataDiffer.prototype.update = function (func) {\n    this._update = func;\n    return this;\n  };\n  /**\n   * Callback function when update a data and only work in `cbMode: 'byKey'`.\n   */\n\n\n  DataDiffer.prototype.updateManyToOne = function (func) {\n    this._updateManyToOne = func;\n    return this;\n  };\n  /**\n   * Callback function when update a data and only work in `cbMode: 'byKey'`.\n   */\n\n\n  DataDiffer.prototype.updateOneToMany = function (func) {\n    this._updateOneToMany = func;\n    return this;\n  };\n  /**\n   * Callback function when update a data and only work in `cbMode: 'byKey'`.\n   */\n\n\n  DataDiffer.prototype.updateManyToMany = function (func) {\n    this._updateManyToMany = func;\n    return this;\n  };\n  /**\n   * Callback function when remove a data\n   */\n\n\n  DataDiffer.prototype.remove = function (func) {\n    this._remove = func;\n    return this;\n  };\n\n  DataDiffer.prototype.execute = function () {\n    this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne']();\n  };\n\n  DataDiffer.prototype._executeOneToOne = function () {\n    var oldArr = this._old;\n    var newArr = this._new;\n    var newDataIndexMap = {};\n    var oldDataKeyArr = new Array(oldArr.length);\n    var newDataKeyArr = new Array(newArr.length);\n\n    this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter');\n\n    this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n    for (var i = 0; i < oldArr.length; i++) {\n      var oldKey = oldDataKeyArr[i];\n      var newIdxMapVal = newDataIndexMap[oldKey];\n      var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); // idx can never be empty array here. see 'set null' logic below.\n\n      if (newIdxMapValLen > 1) {\n        // Consider there is duplicate key (for example, use dataItem.name as key).\n        // We should make sure every item in newArr and oldArr can be visited.\n        var newIdx = newIdxMapVal.shift();\n\n        if (newIdxMapVal.length === 1) {\n          newDataIndexMap[oldKey] = newIdxMapVal[0];\n        }\n\n        this._update && this._update(newIdx, i);\n      } else if (newIdxMapValLen === 1) {\n        newDataIndexMap[oldKey] = null;\n        this._update && this._update(newIdxMapVal, i);\n      } else {\n        this._remove && this._remove(i);\n      }\n    }\n\n    this._performRestAdd(newDataKeyArr, newDataIndexMap);\n  };\n  /**\n   * For example, consider the case:\n   * oldData: [o0, o1, o2, o3, o4, o5, o6, o7],\n   * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],\n   * Where:\n   *     o0, o1, n0 has key 'a' (many to one)\n   *     o5, n4, n5, n6 has key 'b' (one to many)\n   *     o2, n1 has key 'c' (one to one)\n   *     n2, n3 has key 'd' (add)\n   *     o3, o4 has key 'e' (remove)\n   *     o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)\n   * Then:\n   *     (The order of the following directives are not ensured.)\n   *     this._updateManyToOne(n0, [o0, o1]);\n   *     this._updateOneToMany([n4, n5, n6], o5);\n   *     this._update(n1, o2);\n   *     this._remove(o3);\n   *     this._remove(o4);\n   *     this._remove(o6);\n   *     this._remove(o7);\n   *     this._add(n2);\n   *     this._add(n3);\n   *     this._add(n7);\n   *     this._add(n8);\n   */\n\n\n  DataDiffer.prototype._executeMultiple = function () {\n    var oldArr = this._old;\n    var newArr = this._new;\n    var oldDataIndexMap = {};\n    var newDataIndexMap = {};\n    var oldDataKeyArr = [];\n    var newDataKeyArr = [];\n\n    this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');\n\n    this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n    for (var i = 0; i < oldDataKeyArr.length; i++) {\n      var oldKey = oldDataKeyArr[i];\n      var oldIdxMapVal = oldDataIndexMap[oldKey];\n      var newIdxMapVal = newDataIndexMap[oldKey];\n      var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal);\n      var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n      if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {\n        this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal);\n        newDataIndexMap[oldKey] = null;\n      } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {\n        this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal);\n        newDataIndexMap[oldKey] = null;\n      } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {\n        this._update && this._update(newIdxMapVal, oldIdxMapVal);\n        newDataIndexMap[oldKey] = null;\n      } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {\n        this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal);\n        newDataIndexMap[oldKey] = null;\n      } else if (oldIdxMapValLen > 1) {\n        for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {\n          this._remove && this._remove(oldIdxMapVal[i_1]);\n        }\n      } else {\n        this._remove && this._remove(oldIdxMapVal);\n      }\n    }\n\n    this._performRestAdd(newDataKeyArr, newDataIndexMap);\n  };\n\n  DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) {\n    for (var i = 0; i < newDataKeyArr.length; i++) {\n      var newKey = newDataKeyArr[i];\n      var newIdxMapVal = newDataIndexMap[newKey];\n      var idxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n      if (idxMapValLen > 1) {\n        for (var j = 0; j < idxMapValLen; j++) {\n          this._add && this._add(newIdxMapVal[j]);\n        }\n      } else if (idxMapValLen === 1) {\n        this._add && this._add(newIdxMapVal);\n      } // Support both `newDataKeyArr` are duplication removed or not removed.\n\n\n      newDataIndexMap[newKey] = null;\n    }\n  };\n\n  DataDiffer.prototype._initIndexMap = function (arr, // Can be null.\n  map, // In 'byKey', the output `keyArr` is duplication removed.\n  // In 'byIndex', the output `keyArr` is not duplication removed and\n  //     its indices are accurately corresponding to `arr`.\n  keyArr, keyGetterName) {\n    var cbModeMultiple = this._diffModeMultiple;\n\n    for (var i = 0; i < arr.length; i++) {\n      // Add prefix to avoid conflict with Object.prototype.\n      var key = '_ec_' + this[keyGetterName](arr[i], i);\n\n      if (!cbModeMultiple) {\n        keyArr[i] = key;\n      }\n\n      if (!map) {\n        continue;\n      }\n\n      var idxMapVal = map[key];\n      var idxMapValLen = dataIndexMapValueLength(idxMapVal);\n\n      if (idxMapValLen === 0) {\n        // Simple optimize: in most cases, one index has one key,\n        // do not need array.\n        map[key] = i;\n\n        if (cbModeMultiple) {\n          keyArr.push(key);\n        }\n      } else if (idxMapValLen === 1) {\n        map[key] = [idxMapVal, i];\n      } else {\n        idxMapVal.push(i);\n      }\n    }\n  };\n\n  return DataDiffer;\n}();\n\nvar DimensionUserOuput =\n/** @class */\nfunction () {\n  function DimensionUserOuput(encode, dimRequest) {\n    this._encode = encode;\n    this._schema = dimRequest;\n  }\n\n  DimensionUserOuput.prototype.get = function () {\n    return {\n      // Do not generate full dimension name until fist used.\n      fullDimensions: this._getFullDimensionNames(),\n      encode: this._encode\n    };\n  };\n  /**\n   * Get all data store dimension names.\n   * Theoretically a series data store is defined both by series and used dataset (if any).\n   * If some dimensions are omitted for performance reason in `this.dimensions`,\n   * the dimension name may not be auto-generated if user does not specify a dimension name.\n   * In this case, the dimension name is `null`/`undefined`.\n   */\n\n\n  DimensionUserOuput.prototype._getFullDimensionNames = function () {\n    if (!this._cachedDimNames) {\n      this._cachedDimNames = this._schema ? this._schema.makeOutputDimensionNames() : [];\n    }\n\n    return this._cachedDimNames;\n  };\n\n  return DimensionUserOuput;\n}();\nfunction summarizeDimensions(data, schema) {\n  var summary = {};\n  var encode = summary.encode = {};\n  var notExtraCoordDimMap = createHashMap();\n  var defaultedLabel = [];\n  var defaultedTooltip = [];\n  var userOutputEncode = {};\n  each(data.dimensions, function (dimName) {\n    var dimItem = data.getDimensionInfo(dimName);\n    var coordDim = dimItem.coordDim;\n\n    if (coordDim) {\n      if (\"development\" !== 'production') {\n        assert(VISUAL_DIMENSIONS.get(coordDim) == null);\n      }\n\n      var coordDimIndex = dimItem.coordDimIndex;\n      getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName;\n\n      if (!dimItem.isExtraCoord) {\n        notExtraCoordDimMap.set(coordDim, 1); // Use the last coord dim (and label friendly) as default label,\n        // because when dataset is used, it is hard to guess which dimension\n        // can be value dimension. If both show x, y on label is not look good,\n        // and conventionally y axis is focused more.\n\n        if (mayLabelDimType(dimItem.type)) {\n          defaultedLabel[0] = dimName;\n        } // User output encode do not contain generated coords.\n        // And it only has index. User can use index to retrieve value from the raw item array.\n\n\n        getOrCreateEncodeArr(userOutputEncode, coordDim)[coordDimIndex] = data.getDimensionIndex(dimItem.name);\n      }\n\n      if (dimItem.defaultTooltip) {\n        defaultedTooltip.push(dimName);\n      }\n    }\n\n    VISUAL_DIMENSIONS.each(function (v, otherDim) {\n      var encodeArr = getOrCreateEncodeArr(encode, otherDim);\n      var dimIndex = dimItem.otherDims[otherDim];\n\n      if (dimIndex != null && dimIndex !== false) {\n        encodeArr[dimIndex] = dimItem.name;\n      }\n    });\n  });\n  var dataDimsOnCoord = [];\n  var encodeFirstDimNotExtra = {};\n  notExtraCoordDimMap.each(function (v, coordDim) {\n    var dimArr = encode[coordDim];\n    encodeFirstDimNotExtra[coordDim] = dimArr[0]; // Not necessary to remove duplicate, because a data\n    // dim canot on more than one coordDim.\n\n    dataDimsOnCoord = dataDimsOnCoord.concat(dimArr);\n  });\n  summary.dataDimsOnCoord = dataDimsOnCoord;\n  summary.dataDimIndicesOnCoord = map(dataDimsOnCoord, function (dimName) {\n    return data.getDimensionInfo(dimName).storeDimIndex;\n  });\n  summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra;\n  var encodeLabel = encode.label; // FIXME `encode.label` is not recommended, because formatter cannot be set\n  // in this way. Use label.formatter instead. Maybe remove this approach someday.\n\n  if (encodeLabel && encodeLabel.length) {\n    defaultedLabel = encodeLabel.slice();\n  }\n\n  var encodeTooltip = encode.tooltip;\n\n  if (encodeTooltip && encodeTooltip.length) {\n    defaultedTooltip = encodeTooltip.slice();\n  } else if (!defaultedTooltip.length) {\n    defaultedTooltip = defaultedLabel.slice();\n  }\n\n  encode.defaultedLabel = defaultedLabel;\n  encode.defaultedTooltip = defaultedTooltip;\n  summary.userOutput = new DimensionUserOuput(userOutputEncode, schema);\n  return summary;\n}\n\nfunction getOrCreateEncodeArr(encode, dim) {\n  if (!encode.hasOwnProperty(dim)) {\n    encode[dim] = [];\n  }\n\n  return encode[dim];\n} // FIXME:TS should be type `AxisType`\n\n\nfunction getDimensionTypeByAxis(axisType) {\n  return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float';\n}\n\nfunction mayLabelDimType(dimType) {\n  // In most cases, ordinal and time do not suitable for label.\n  // Ordinal info can be displayed on axis. Time is too long.\n  return !(dimType === 'ordinal' || dimType === 'time');\n} // function findTheLastDimMayLabel(data) {\n//     // Get last value dim\n//     let dimensions = data.dimensions.slice();\n//     let valueType;\n//     let valueDim;\n//     while (dimensions.length && (\n//         valueDim = dimensions.pop(),\n//         valueType = data.getDimensionInfo(valueDim).type,\n//         valueType === 'ordinal' || valueType === 'time'\n//     )) {} // jshint ignore:line\n//     return valueDim;\n// }\n\nvar SeriesDimensionDefine =\n/** @class */\nfunction () {\n  /**\n   * @param opt All of the fields will be shallow copied.\n   */\n  function SeriesDimensionDefine(opt) {\n    /**\n     * The format of `otherDims` is:\n     * ```js\n     * {\n     *     tooltip?: number\n     *     label?: number\n     *     itemName?: number\n     *     seriesName?: number\n     * }\n     * ```\n     *\n     * A `series.encode` can specified these fields:\n     * ```js\n     * encode: {\n     *     // \"3, 1, 5\" is the index of data dimension.\n     *     tooltip: [3, 1, 5],\n     *     label: [0, 3],\n     *     ...\n     * }\n     * ```\n     * `otherDims` is the parse result of the `series.encode` above, like:\n     * ```js\n     * // Suppose the index of this data dimension is `3`.\n     * this.otherDims = {\n     *     // `3` is at the index `0` of the `encode.tooltip`\n     *     tooltip: 0,\n     *     // `3` is at the index `1` of the `encode.label`\n     *     label: 1\n     * };\n     * ```\n     *\n     * This prop should never be `null`/`undefined` after initialized.\n     */\n    this.otherDims = {};\n\n    if (opt != null) {\n      extend(this, opt);\n    }\n  }\n\n  return SeriesDimensionDefine;\n}();\n\nvar inner$4 = makeInner();\nvar dimTypeShort = {\n  float: 'f',\n  int: 'i',\n  ordinal: 'o',\n  number: 'n',\n  time: 't'\n};\n/**\n * Represents the dimension requirement of a series.\n *\n * NOTICE:\n * When there are too many dimensions in dataset and many series, only the used dimensions\n * (i.e., used by coord sys and declared in `series.encode`) are add to `dimensionDefineList`.\n * But users may query data by other unused dimension names.\n * In this case, users can only query data if and only if they have defined dimension names\n * via ec option, so we provide `getDimensionIndexFromSource`, which only query them from\n * `source` dimensions.\n */\n\nvar SeriesDataSchema =\n/** @class */\nfunction () {\n  function SeriesDataSchema(opt) {\n    this.dimensions = opt.dimensions;\n    this._dimOmitted = opt.dimensionOmitted;\n    this.source = opt.source;\n    this._fullDimCount = opt.fullDimensionCount;\n\n    this._updateDimOmitted(opt.dimensionOmitted);\n  }\n\n  SeriesDataSchema.prototype.isDimensionOmitted = function () {\n    return this._dimOmitted;\n  };\n\n  SeriesDataSchema.prototype._updateDimOmitted = function (dimensionOmitted) {\n    this._dimOmitted = dimensionOmitted;\n\n    if (!dimensionOmitted) {\n      return;\n    }\n\n    if (!this._dimNameMap) {\n      this._dimNameMap = ensureSourceDimNameMap(this.source);\n    }\n  };\n  /**\n   * @caution Can only be used when `dimensionOmitted: true`.\n   *\n   * Get index by user defined dimension name (i.e., not internal generate name).\n   * That is, get index from `dimensionsDefine`.\n   * If no `dimensionsDefine`, or no name get, return -1.\n   */\n\n\n  SeriesDataSchema.prototype.getSourceDimensionIndex = function (dimName) {\n    return retrieve2(this._dimNameMap.get(dimName), -1);\n  };\n  /**\n   * @caution Can only be used when `dimensionOmitted: true`.\n   *\n   * Notice: may return `null`/`undefined` if user not specify dimension names.\n   */\n\n\n  SeriesDataSchema.prototype.getSourceDimension = function (dimIndex) {\n    var dimensionsDefine = this.source.dimensionsDefine;\n\n    if (dimensionsDefine) {\n      return dimensionsDefine[dimIndex];\n    }\n  };\n\n  SeriesDataSchema.prototype.makeStoreSchema = function () {\n    var dimCount = this._fullDimCount;\n    var willRetrieveDataByName = shouldRetrieveDataByName(this.source);\n    var makeHashStrict = !shouldOmitUnusedDimensions(dimCount); // If source don't have dimensions or series don't omit unsed dimensions.\n    // Generate from seriesDimList directly\n\n    var dimHash = '';\n    var dims = [];\n\n    for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < dimCount; fullDimIdx++) {\n      var property = void 0;\n      var type = void 0;\n      var ordinalMeta = void 0;\n      var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n      if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n        property = willRetrieveDataByName ? seriesDimDef.name : null;\n        type = seriesDimDef.type;\n        ordinalMeta = seriesDimDef.ordinalMeta;\n        seriesDimIdx++;\n      } else {\n        var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n        if (sourceDimDef) {\n          property = willRetrieveDataByName ? sourceDimDef.name : null;\n          type = sourceDimDef.type;\n        }\n      }\n\n      dims.push({\n        property: property,\n        type: type,\n        ordinalMeta: ordinalMeta\n      }); // If retrieving data by index,\n      //   use <index, type, ordinalMeta> to determine whether data can be shared.\n      //   (Because in this case there might be no dimension name defined in dataset, but indices always exists).\n      //   (Indices are always 0, 1, 2, ..., so we can ignore them to shorten the hash).\n      // Otherwise if retrieving data by property name (like `data: [{aa: 123, bb: 765}, ...]`),\n      //   use <property, type, ordinalMeta> in hash.\n\n      if (willRetrieveDataByName && property != null // For data stack, we have make sure each series has its own dim on this store.\n      // So we do not add property to hash to make sure they can share this store.\n      && (!seriesDimDef || !seriesDimDef.isCalculationCoord)) {\n        dimHash += makeHashStrict // Use escape character '`' in case that property name contains '$'.\n        ? property.replace(/\\`/g, '`1').replace(/\\$/g, '`2') // For better performance, when there are large dimensions, tolerant this defects that hardly meet.\n        : property;\n      }\n\n      dimHash += '$';\n      dimHash += dimTypeShort[type] || 'f';\n\n      if (ordinalMeta) {\n        dimHash += ordinalMeta.uid;\n      }\n\n      dimHash += '$';\n    } // Source from endpoint(usually series) will be read differently\n    // when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different.\n    // So we use this three props as key.\n\n\n    var source = this.source;\n    var hash = [source.seriesLayoutBy, source.startIndex, dimHash].join('$$');\n    return {\n      dimensions: dims,\n      hash: hash\n    };\n  };\n\n  SeriesDataSchema.prototype.makeOutputDimensionNames = function () {\n    var result = [];\n\n    for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < this._fullDimCount; fullDimIdx++) {\n      var name_1 = void 0;\n      var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n      if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n        if (!seriesDimDef.isCalculationCoord) {\n          name_1 = seriesDimDef.name;\n        }\n\n        seriesDimIdx++;\n      } else {\n        var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n        if (sourceDimDef) {\n          name_1 = sourceDimDef.name;\n        }\n      }\n\n      result.push(name_1);\n    }\n\n    return result;\n  };\n\n  SeriesDataSchema.prototype.appendCalculationDimension = function (dimDef) {\n    this.dimensions.push(dimDef);\n    dimDef.isCalculationCoord = true;\n    this._fullDimCount++; // If append dimension on a data store, consider the store\n    // might be shared by different series, series dimensions not\n    // really map to store dimensions.\n\n    this._updateDimOmitted(true);\n  };\n\n  return SeriesDataSchema;\n}();\nfunction isSeriesDataSchema(schema) {\n  return schema instanceof SeriesDataSchema;\n}\nfunction createDimNameMap(dimsDef) {\n  var dataDimNameMap = createHashMap();\n\n  for (var i = 0; i < (dimsDef || []).length; i++) {\n    var dimDefItemRaw = dimsDef[i];\n    var userDimName = isObject(dimDefItemRaw) ? dimDefItemRaw.name : dimDefItemRaw;\n\n    if (userDimName != null && dataDimNameMap.get(userDimName) == null) {\n      dataDimNameMap.set(userDimName, i);\n    }\n  }\n\n  return dataDimNameMap;\n}\nfunction ensureSourceDimNameMap(source) {\n  var innerSource = inner$4(source);\n  return innerSource.dimNameMap || (innerSource.dimNameMap = createDimNameMap(source.dimensionsDefine));\n}\nfunction shouldOmitUnusedDimensions(dimCount) {\n  return dimCount > 30;\n}\n\nvar isObject$2 = isObject;\nvar map$1 = map;\nvar CtorInt32Array$1 = typeof Int32Array === 'undefined' ? Array : Int32Array; // Use prefix to avoid index to be the same as otherIdList[idx],\n// which will cause weird update animation.\n\nvar ID_PREFIX = 'e\\0\\0';\nvar INDEX_NOT_FOUND = -1; // type SeriesDimensionIndex = DimensionIndex;\n\nvar TRANSFERABLE_PROPERTIES = ['hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', '_dimSummary', 'userOutput', '_rawData', '_dimValueGetter', '_nameDimIdx', '_idDimIdx', '_nameRepeatCount'];\nvar CLONE_PROPERTIES = ['_approximateExtent']; // -----------------------------\n// Internal method declarations:\n// -----------------------------\n\nvar prepareInvertedIndex;\nvar getId;\nvar getIdNameFromStore;\nvar normalizeDimensions;\nvar transferProperties;\nvar cloneListForMapAndSample;\nvar makeIdFromName;\n\nvar SeriesData =\n/** @class */\nfunction () {\n  /**\n   * @param dimensionsInput.dimensions\n   *        For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].\n   *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius\n   */\n  function SeriesData(dimensionsInput, hostModel) {\n    this.type = 'list';\n    this._dimOmitted = false;\n    this._nameList = [];\n    this._idList = []; // Models of data option is stored sparse for optimizing memory cost\n    // Never used yet (not used yet).\n    // private _optionModels: Model[] = [];\n    // Global visual properties after visual coding\n\n    this._visual = {}; // Global layout properties.\n\n    this._layout = {}; // Item visual properties after visual coding\n\n    this._itemVisuals = []; // Item layout properties after layout\n\n    this._itemLayouts = []; // Graphic elements\n\n    this._graphicEls = []; // key: dim, value: extent\n\n    this._approximateExtent = {};\n    this._calculationInfo = {}; // Having detected that there is data item is non primitive type\n    // (in type `OptionDataItemObject`).\n    // Like `data: [ { value: xx, itemStyle: {...} }, ...]`\n    // At present it only happen in `SOURCE_FORMAT_ORIGINAL`.\n\n    this.hasItemOption = false; // Methods that create a new list based on this list should be listed here.\n    // Notice that those method should `RETURN` the new list.\n\n    this.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map']; // Methods that change indices of this list should be listed here.\n\n    this.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];\n    this.DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'];\n    var dimensions;\n    var assignStoreDimIdx = false;\n\n    if (isSeriesDataSchema(dimensionsInput)) {\n      dimensions = dimensionsInput.dimensions;\n      this._dimOmitted = dimensionsInput.isDimensionOmitted();\n      this._schema = dimensionsInput;\n    } else {\n      assignStoreDimIdx = true;\n      dimensions = dimensionsInput;\n    }\n\n    dimensions = dimensions || ['x', 'y'];\n    var dimensionInfos = {};\n    var dimensionNames = [];\n    var invertedIndicesMap = {};\n    var needsHasOwn = false;\n    var emptyObj = {};\n\n    for (var i = 0; i < dimensions.length; i++) {\n      // Use the original dimensions[i], where other flag props may exists.\n      var dimInfoInput = dimensions[i];\n      var dimensionInfo = isString(dimInfoInput) ? new SeriesDimensionDefine({\n        name: dimInfoInput\n      }) : !(dimInfoInput instanceof SeriesDimensionDefine) ? new SeriesDimensionDefine(dimInfoInput) : dimInfoInput;\n      var dimensionName = dimensionInfo.name;\n      dimensionInfo.type = dimensionInfo.type || 'float';\n\n      if (!dimensionInfo.coordDim) {\n        dimensionInfo.coordDim = dimensionName;\n        dimensionInfo.coordDimIndex = 0;\n      }\n\n      var otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {};\n      dimensionNames.push(dimensionName);\n      dimensionInfos[dimensionName] = dimensionInfo;\n\n      if (emptyObj[dimensionName] != null) {\n        needsHasOwn = true;\n      }\n\n      if (dimensionInfo.createInvertedIndices) {\n        invertedIndicesMap[dimensionName] = [];\n      }\n\n      if (otherDims.itemName === 0) {\n        this._nameDimIdx = i;\n      }\n\n      if (otherDims.itemId === 0) {\n        this._idDimIdx = i;\n      }\n\n      if (\"development\" !== 'production') {\n        assert(assignStoreDimIdx || dimensionInfo.storeDimIndex >= 0);\n      }\n\n      if (assignStoreDimIdx) {\n        dimensionInfo.storeDimIndex = i;\n      }\n    }\n\n    this.dimensions = dimensionNames;\n    this._dimInfos = dimensionInfos;\n\n    this._initGetDimensionInfo(needsHasOwn);\n\n    this.hostModel = hostModel;\n    this._invertedIndicesMap = invertedIndicesMap;\n\n    if (this._dimOmitted) {\n      var dimIdxToName_1 = this._dimIdxToName = createHashMap();\n      each(dimensionNames, function (dimName) {\n        dimIdxToName_1.set(dimensionInfos[dimName].storeDimIndex, dimName);\n      });\n    }\n  }\n  /**\n   *\n   * Get concrete dimension name by dimension name or dimension index.\n   * If input a dimension name, do not validate whether the dimension name exits.\n   *\n   * @caution\n   * @param dim Must make sure the dimension is `SeriesDimensionLoose`.\n   * Because only those dimensions will have auto-generated dimension names if not\n   * have a user-specified name, and other dimensions will get a return of null/undefined.\n   *\n   * @notice Because of this reason, should better use `getDimensionIndex` instead, for examples:\n   * ```js\n   * const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx);\n   * ```\n   *\n   * @return Concrete dim name.\n   */\n\n\n  SeriesData.prototype.getDimension = function (dim) {\n    var dimIdx = this._recognizeDimIndex(dim);\n\n    if (dimIdx == null) {\n      return dim;\n    }\n\n    dimIdx = dim;\n\n    if (!this._dimOmitted) {\n      return this.dimensions[dimIdx];\n    } // Retrieve from series dimension definition because it probably contains\n    // generated dimension name (like 'x', 'y').\n\n\n    var dimName = this._dimIdxToName.get(dimIdx);\n\n    if (dimName != null) {\n      return dimName;\n    }\n\n    var sourceDimDef = this._schema.getSourceDimension(dimIdx);\n\n    if (sourceDimDef) {\n      return sourceDimDef.name;\n    }\n  };\n  /**\n   * Get dimension index in data store. Return -1 if not found.\n   * Can be used to index value from getRawValue.\n   */\n\n\n  SeriesData.prototype.getDimensionIndex = function (dim) {\n    var dimIdx = this._recognizeDimIndex(dim);\n\n    if (dimIdx != null) {\n      return dimIdx;\n    }\n\n    if (dim == null) {\n      return -1;\n    }\n\n    var dimInfo = this._getDimInfo(dim);\n\n    return dimInfo ? dimInfo.storeDimIndex : this._dimOmitted ? this._schema.getSourceDimensionIndex(dim) : -1;\n  };\n  /**\n   * The meanings of the input parameter `dim`:\n   *\n   * + If dim is a number (e.g., `1`), it means the index of the dimension.\n   *   For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.\n   * + If dim is a number-like string (e.g., `\"1\"`):\n   *     + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`,\n   *        it means that concrete name.\n   *     + If not, it will be converted to a number, which means the index of the dimension.\n   *        (why? because of the backward compatibility. We have been tolerating number-like string in\n   *        dimension setting, although now it seems that it is not a good idea.)\n   *     For example, `visualMap[i].dimension: \"1\"` is the same meaning as `visualMap[i].dimension: 1`,\n   *     if no dimension name is defined as `\"1\"`.\n   * + If dim is a not-number-like string, it means the concrete dim name.\n   *   For example, it can be be default name `\"x\"`, `\"y\"`, `\"z\"`, `\"lng\"`, `\"lat\"`, `\"angle\"`, `\"radius\"`,\n   *   or customized in `dimensions` property of option like `\"age\"`.\n   *\n   * @return recognized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`).\n   */\n\n\n  SeriesData.prototype._recognizeDimIndex = function (dim) {\n    if (isNumber(dim) // If being a number-like string but not being defined as a dimension name.\n    || dim != null && !isNaN(dim) && !this._getDimInfo(dim) && (!this._dimOmitted || this._schema.getSourceDimensionIndex(dim) < 0)) {\n      return +dim;\n    }\n  };\n\n  SeriesData.prototype._getStoreDimIndex = function (dim) {\n    var dimIdx = this.getDimensionIndex(dim);\n\n    if (\"development\" !== 'production') {\n      if (dimIdx == null) {\n        throw new Error('Unknown dimension ' + dim);\n      }\n    }\n\n    return dimIdx;\n  };\n  /**\n   * Get type and calculation info of particular dimension\n   * @param dim\n   *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius\n   *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'\n   */\n\n\n  SeriesData.prototype.getDimensionInfo = function (dim) {\n    // Do not clone, because there may be categories in dimInfo.\n    return this._getDimInfo(this.getDimension(dim));\n  };\n\n  SeriesData.prototype._initGetDimensionInfo = function (needsHasOwn) {\n    var dimensionInfos = this._dimInfos;\n    this._getDimInfo = needsHasOwn ? function (dimName) {\n      return dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined;\n    } : function (dimName) {\n      return dimensionInfos[dimName];\n    };\n  };\n  /**\n   * concrete dimension name list on coord.\n   */\n\n\n  SeriesData.prototype.getDimensionsOnCoord = function () {\n    return this._dimSummary.dataDimsOnCoord.slice();\n  };\n\n  SeriesData.prototype.mapDimension = function (coordDim, idx) {\n    var dimensionsSummary = this._dimSummary;\n\n    if (idx == null) {\n      return dimensionsSummary.encodeFirstDimNotExtra[coordDim];\n    }\n\n    var dims = dimensionsSummary.encode[coordDim];\n    return dims ? dims[idx] : null;\n  };\n\n  SeriesData.prototype.mapDimensionsAll = function (coordDim) {\n    var dimensionsSummary = this._dimSummary;\n    var dims = dimensionsSummary.encode[coordDim];\n    return (dims || []).slice();\n  };\n\n  SeriesData.prototype.getStore = function () {\n    return this._store;\n  };\n  /**\n   * Initialize from data\n   * @param data source or data or data store.\n   * @param nameList The name of a datum is used on data diff and\n   *        default label/tooltip.\n   *        A name can be specified in encode.itemName,\n   *        or dataItem.name (only for series option data),\n   *        or provided in nameList from outside.\n   */\n\n\n  SeriesData.prototype.initData = function (data, nameList, dimValueGetter) {\n    var _this = this;\n\n    var store;\n\n    if (data instanceof DataStore) {\n      store = data;\n    }\n\n    if (!store) {\n      var dimensions = this.dimensions;\n      var provider = isSourceInstance(data) || isArrayLike(data) ? new DefaultDataProvider(data, dimensions.length) : data;\n      store = new DataStore();\n      var dimensionInfos = map$1(dimensions, function (dimName) {\n        return {\n          type: _this._dimInfos[dimName].type,\n          property: dimName\n        };\n      });\n      store.initData(provider, dimensionInfos, dimValueGetter);\n    }\n\n    this._store = store; // Reset\n\n    this._nameList = (nameList || []).slice();\n    this._idList = [];\n    this._nameRepeatCount = {};\n\n    this._doInit(0, store.count()); // Cache summary info for fast visit. See \"dimensionHelper\".\n    // Needs to be initialized after store is prepared.\n\n\n    this._dimSummary = summarizeDimensions(this, this._schema);\n    this.userOutput = this._dimSummary.userOutput;\n  };\n  /**\n   * Caution: Can be only called on raw data (before `this._indices` created).\n   */\n\n\n  SeriesData.prototype.appendData = function (data) {\n    var range = this._store.appendData(data);\n\n    this._doInit(range[0], range[1]);\n  };\n  /**\n   * Caution: Can be only called on raw data (before `this._indices` created).\n   * This method does not modify `rawData` (`dataProvider`), but only\n   * add values to store.\n   *\n   * The final count will be increased by `Math.max(values.length, names.length)`.\n   *\n   * @param values That is the SourceType: 'arrayRows', like\n   *        [\n   *            [12, 33, 44],\n   *            [NaN, 43, 1],\n   *            ['-', 'asdf', 0]\n   *        ]\n   *        Each item is exactly corresponding to a dimension.\n   */\n\n\n  SeriesData.prototype.appendValues = function (values, names) {\n    var _a = this._store.appendValues(values, names.length),\n        start = _a.start,\n        end = _a.end;\n\n    var shouldMakeIdFromName = this._shouldMakeIdFromName();\n\n    this._updateOrdinalMeta();\n\n    if (names) {\n      for (var idx = start; idx < end; idx++) {\n        var sourceIdx = idx - start;\n        this._nameList[idx] = names[sourceIdx];\n\n        if (shouldMakeIdFromName) {\n          makeIdFromName(this, idx);\n        }\n      }\n    }\n  };\n\n  SeriesData.prototype._updateOrdinalMeta = function () {\n    var store = this._store;\n    var dimensions = this.dimensions;\n\n    for (var i = 0; i < dimensions.length; i++) {\n      var dimInfo = this._dimInfos[dimensions[i]];\n\n      if (dimInfo.ordinalMeta) {\n        store.collectOrdinalMeta(dimInfo.storeDimIndex, dimInfo.ordinalMeta);\n      }\n    }\n  };\n\n  SeriesData.prototype._shouldMakeIdFromName = function () {\n    var provider = this._store.getProvider();\n\n    return this._idDimIdx == null && provider.getSource().sourceFormat !== SOURCE_FORMAT_TYPED_ARRAY && !provider.fillStorage;\n  };\n\n  SeriesData.prototype._doInit = function (start, end) {\n    if (start >= end) {\n      return;\n    }\n\n    var store = this._store;\n    var provider = store.getProvider();\n\n    this._updateOrdinalMeta();\n\n    var nameList = this._nameList;\n    var idList = this._idList;\n    var sourceFormat = provider.getSource().sourceFormat;\n    var isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL; // Each data item is value\n    // [1, 2]\n    // 2\n    // Bar chart, line chart which uses category axis\n    // only gives the 'y' value. 'x' value is the indices of category\n    // Use a tempValue to normalize the value to be a (x, y) value\n    // If dataItem is {name: ...} or {id: ...}, it has highest priority.\n    // This kind of ids and names are always stored `_nameList` and `_idList`.\n\n    if (isFormatOriginal && !provider.pure) {\n      var sharedDataItem = [];\n\n      for (var idx = start; idx < end; idx++) {\n        // NOTICE: Try not to write things into dataItem\n        var dataItem = provider.getItem(idx, sharedDataItem);\n\n        if (!this.hasItemOption && isDataItemOption(dataItem)) {\n          this.hasItemOption = true;\n        }\n\n        if (dataItem) {\n          var itemName = dataItem.name;\n\n          if (nameList[idx] == null && itemName != null) {\n            nameList[idx] = convertOptionIdName(itemName, null);\n          }\n\n          var itemId = dataItem.id;\n\n          if (idList[idx] == null && itemId != null) {\n            idList[idx] = convertOptionIdName(itemId, null);\n          }\n        }\n      }\n    }\n\n    if (this._shouldMakeIdFromName()) {\n      for (var idx = start; idx < end; idx++) {\n        makeIdFromName(this, idx);\n      }\n    }\n\n    prepareInvertedIndex(this);\n  };\n  /**\n   * PENDING: In fact currently this function is only used to short-circuit\n   * the calling of `scale.unionExtentFromData` when data have been filtered by modules\n   * like \"dataZoom\". `scale.unionExtentFromData` is used to calculate data extent for series on\n   * an axis, but if a \"axis related data filter module\" is used, the extent of the axis have\n   * been fixed and no need to calling `scale.unionExtentFromData` actually.\n   * But if we add \"custom data filter\" in future, which is not \"axis related\", this method may\n   * be still needed.\n   *\n   * Optimize for the scenario that data is filtered by a given extent.\n   * Consider that if data amount is more than hundreds of thousand,\n   * extent calculation will cost more than 10ms and the cache will\n   * be erased because of the filtering.\n   */\n\n\n  SeriesData.prototype.getApproximateExtent = function (dim) {\n    return this._approximateExtent[dim] || this._store.getDataExtent(this._getStoreDimIndex(dim));\n  };\n  /**\n   * Calculate extent on a filtered data might be time consuming.\n   * Approximate extent is only used for: calculate extent of filtered data outside.\n   */\n\n\n  SeriesData.prototype.setApproximateExtent = function (extent, dim) {\n    dim = this.getDimension(dim);\n    this._approximateExtent[dim] = extent.slice();\n  };\n\n  SeriesData.prototype.getCalculationInfo = function (key) {\n    return this._calculationInfo[key];\n  };\n\n  SeriesData.prototype.setCalculationInfo = function (key, value) {\n    isObject$2(key) ? extend(this._calculationInfo, key) : this._calculationInfo[key] = value;\n  };\n  /**\n   * @return Never be null/undefined. `number` will be converted to string. Because:\n   * In most cases, name is used in display, where returning a string is more convenient.\n   * In other cases, name is used in query (see `indexOfName`), where we can keep the\n   * rule that name `2` equals to name `'2'`.\n   */\n\n\n  SeriesData.prototype.getName = function (idx) {\n    var rawIndex = this.getRawIndex(idx);\n    var name = this._nameList[rawIndex];\n\n    if (name == null && this._nameDimIdx != null) {\n      name = getIdNameFromStore(this, this._nameDimIdx, rawIndex);\n    }\n\n    if (name == null) {\n      name = '';\n    }\n\n    return name;\n  };\n\n  SeriesData.prototype._getCategory = function (dimIdx, idx) {\n    var ordinal = this._store.get(dimIdx, idx);\n\n    var ordinalMeta = this._store.getOrdinalMeta(dimIdx);\n\n    if (ordinalMeta) {\n      return ordinalMeta.categories[ordinal];\n    }\n\n    return ordinal;\n  };\n  /**\n   * @return Never null/undefined. `number` will be converted to string. Because:\n   * In all cases having encountered at present, id is used in making diff comparison, which\n   * are usually based on hash map. We can keep the rule that the internal id are always string\n   * (treat `2` is the same as `'2'`) to make the related logic simple.\n   */\n\n\n  SeriesData.prototype.getId = function (idx) {\n    return getId(this, this.getRawIndex(idx));\n  };\n\n  SeriesData.prototype.count = function () {\n    return this._store.count();\n  };\n  /**\n   * Get value. Return NaN if idx is out of range.\n   *\n   * @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead.\n   */\n\n\n  SeriesData.prototype.get = function (dim, idx) {\n    var store = this._store;\n    var dimInfo = this._dimInfos[dim];\n\n    if (dimInfo) {\n      return store.get(dimInfo.storeDimIndex, idx);\n    }\n  };\n  /**\n   * @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead.\n   */\n\n\n  SeriesData.prototype.getByRawIndex = function (dim, rawIdx) {\n    var store = this._store;\n    var dimInfo = this._dimInfos[dim];\n\n    if (dimInfo) {\n      return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx);\n    }\n  };\n\n  SeriesData.prototype.getIndices = function () {\n    return this._store.getIndices();\n  };\n\n  SeriesData.prototype.getDataExtent = function (dim) {\n    return this._store.getDataExtent(this._getStoreDimIndex(dim));\n  };\n\n  SeriesData.prototype.getSum = function (dim) {\n    return this._store.getSum(this._getStoreDimIndex(dim));\n  };\n\n  SeriesData.prototype.getMedian = function (dim) {\n    return this._store.getMedian(this._getStoreDimIndex(dim));\n  };\n\n  SeriesData.prototype.getValues = function (dimensions, idx) {\n    var _this = this;\n\n    var store = this._store;\n    return isArray(dimensions) ? store.getValues(map$1(dimensions, function (dim) {\n      return _this._getStoreDimIndex(dim);\n    }), idx) : store.getValues(dimensions);\n  };\n  /**\n   * If value is NaN. Including '-'\n   * Only check the coord dimensions.\n   */\n\n\n  SeriesData.prototype.hasValue = function (idx) {\n    var dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord;\n\n    for (var i = 0, len = dataDimIndicesOnCoord.length; i < len; i++) {\n      // Ordinal type originally can be string or number.\n      // But when an ordinal type is used on coord, it can\n      // not be string but only number. So we can also use isNaN.\n      if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx))) {\n        return false;\n      }\n    }\n\n    return true;\n  };\n  /**\n   * Retrieve the index with given name\n   */\n\n\n  SeriesData.prototype.indexOfName = function (name) {\n    for (var i = 0, len = this._store.count(); i < len; i++) {\n      if (this.getName(i) === name) {\n        return i;\n      }\n    }\n\n    return -1;\n  };\n\n  SeriesData.prototype.getRawIndex = function (idx) {\n    return this._store.getRawIndex(idx);\n  };\n\n  SeriesData.prototype.indexOfRawIndex = function (rawIndex) {\n    return this._store.indexOfRawIndex(rawIndex);\n  };\n  /**\n   * Only support the dimension which inverted index created.\n   * Do not support other cases until required.\n   * @param dim concrete dim\n   * @param value ordinal index\n   * @return rawIndex\n   */\n\n\n  SeriesData.prototype.rawIndexOf = function (dim, value) {\n    var invertedIndices = dim && this._invertedIndicesMap[dim];\n\n    if (\"development\" !== 'production') {\n      if (!invertedIndices) {\n        throw new Error('Do not supported yet');\n      }\n    }\n\n    var rawIndex = invertedIndices[value];\n\n    if (rawIndex == null || isNaN(rawIndex)) {\n      return INDEX_NOT_FOUND;\n    }\n\n    return rawIndex;\n  };\n  /**\n   * Retrieve the index of nearest value\n   * @param dim\n   * @param value\n   * @param [maxDistance=Infinity]\n   * @return If and only if multiple indices has\n   *         the same value, they are put to the result.\n   */\n\n\n  SeriesData.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n    return this._store.indicesOfNearest(this._getStoreDimIndex(dim), value, maxDistance);\n  };\n\n  SeriesData.prototype.each = function (dims, cb, ctx) {\n\n    if (isFunction(dims)) {\n      ctx = cb;\n      cb = dims;\n      dims = [];\n    } // ctxCompat just for compat echarts3\n\n\n    var fCtx = ctx || this;\n    var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n\n    this._store.each(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n  };\n\n  SeriesData.prototype.filterSelf = function (dims, cb, ctx) {\n\n    if (isFunction(dims)) {\n      ctx = cb;\n      cb = dims;\n      dims = [];\n    } // ctxCompat just for compat echarts3\n\n\n    var fCtx = ctx || this;\n    var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n    this._store = this._store.filter(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n    return this;\n  };\n  /**\n   * Select data in range. (For optimization of filter)\n   * (Manually inline code, support 5 million data filtering in data zoom.)\n   */\n\n\n  SeriesData.prototype.selectRange = function (range) {\n\n    var _this = this;\n\n    var innerRange = {};\n    var dims = keys(range);\n    each(dims, function (dim) {\n      var dimIdx = _this._getStoreDimIndex(dim);\n\n      innerRange[dimIdx] = range[dim];\n    });\n    this._store = this._store.selectRange(innerRange);\n    return this;\n  };\n  /* eslint-enable max-len */\n\n\n  SeriesData.prototype.mapArray = function (dims, cb, ctx) {\n\n    if (isFunction(dims)) {\n      ctx = cb;\n      cb = dims;\n      dims = [];\n    } // ctxCompat just for compat echarts3\n\n\n    ctx = ctx || this;\n    var result = [];\n    this.each(dims, function () {\n      result.push(cb && cb.apply(this, arguments));\n    }, ctx);\n    return result;\n  };\n\n  SeriesData.prototype.map = function (dims, cb, ctx, ctxCompat) {\n\n    var fCtx = ctx || ctxCompat || this;\n    var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n    var list = cloneListForMapAndSample(this);\n    list._store = this._store.map(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n    return list;\n  };\n\n  SeriesData.prototype.modify = function (dims, cb, ctx, ctxCompat) {\n    var _this = this; // ctxCompat just for compat echarts3\n\n\n    var fCtx = ctx || ctxCompat || this;\n\n    if (\"development\" !== 'production') {\n      each(normalizeDimensions(dims), function (dim) {\n        var dimInfo = _this.getDimensionInfo(dim);\n\n        if (!dimInfo.isCalculationCoord) {\n          console.error('Danger: only stack dimension can be modified');\n        }\n      });\n    }\n\n    var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); // If do shallow clone here, if there are too many stacked series,\n    // it still cost lots of memory, because `_store.dimensions` are not shared.\n    // We should consider there probably be shallow clone happen in each series\n    // in consequent filter/map.\n\n    this._store.modify(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n  };\n  /**\n   * Large data down sampling on given dimension\n   * @param sampleIndex Sample index for name and id\n   */\n\n\n  SeriesData.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n    var list = cloneListForMapAndSample(this);\n    list._store = this._store.downSample(this._getStoreDimIndex(dimension), rate, sampleValue, sampleIndex);\n    return list;\n  };\n  /**\n   * Large data down sampling using largest-triangle-three-buckets\n   * @param {string} valueDimension\n   * @param {number} targetCount\n   */\n\n\n  SeriesData.prototype.lttbDownSample = function (valueDimension, rate) {\n    var list = cloneListForMapAndSample(this);\n    list._store = this._store.lttbDownSample(this._getStoreDimIndex(valueDimension), rate);\n    return list;\n  };\n\n  SeriesData.prototype.getRawDataItem = function (idx) {\n    return this._store.getRawDataItem(idx);\n  };\n  /**\n   * Get model of one data item.\n   */\n  // TODO: Type of data item\n\n\n  SeriesData.prototype.getItemModel = function (idx) {\n    var hostModel = this.hostModel;\n    var dataItem = this.getRawDataItem(idx);\n    return new Model(dataItem, hostModel, hostModel && hostModel.ecModel);\n  };\n  /**\n   * Create a data differ\n   */\n\n\n  SeriesData.prototype.diff = function (otherList) {\n    var thisList = this;\n    return new DataDiffer(otherList ? otherList.getStore().getIndices() : [], this.getStore().getIndices(), function (idx) {\n      return getId(otherList, idx);\n    }, function (idx) {\n      return getId(thisList, idx);\n    });\n  };\n  /**\n   * Get visual property.\n   */\n\n\n  SeriesData.prototype.getVisual = function (key) {\n    var visual = this._visual;\n    return visual && visual[key];\n  };\n\n  SeriesData.prototype.setVisual = function (kvObj, val) {\n    this._visual = this._visual || {};\n\n    if (isObject$2(kvObj)) {\n      extend(this._visual, kvObj);\n    } else {\n      this._visual[kvObj] = val;\n    }\n  };\n  /**\n   * Get visual property of single data item\n   */\n  // eslint-disable-next-line\n\n\n  SeriesData.prototype.getItemVisual = function (idx, key) {\n    var itemVisual = this._itemVisuals[idx];\n    var val = itemVisual && itemVisual[key];\n\n    if (val == null) {\n      // Use global visual property\n      return this.getVisual(key);\n    }\n\n    return val;\n  };\n  /**\n   * If exists visual property of single data item\n   */\n\n\n  SeriesData.prototype.hasItemVisual = function () {\n    return this._itemVisuals.length > 0;\n  };\n  /**\n   * Make sure itemVisual property is unique\n   */\n  // TODO: use key to save visual to reduce memory.\n\n\n  SeriesData.prototype.ensureUniqueItemVisual = function (idx, key) {\n    var itemVisuals = this._itemVisuals;\n    var itemVisual = itemVisuals[idx];\n\n    if (!itemVisual) {\n      itemVisual = itemVisuals[idx] = {};\n    }\n\n    var val = itemVisual[key];\n\n    if (val == null) {\n      val = this.getVisual(key); // TODO Performance?\n\n      if (isArray(val)) {\n        val = val.slice();\n      } else if (isObject$2(val)) {\n        val = extend({}, val);\n      }\n\n      itemVisual[key] = val;\n    }\n\n    return val;\n  }; // eslint-disable-next-line\n\n\n  SeriesData.prototype.setItemVisual = function (idx, key, value) {\n    var itemVisual = this._itemVisuals[idx] || {};\n    this._itemVisuals[idx] = itemVisual;\n\n    if (isObject$2(key)) {\n      extend(itemVisual, key);\n    } else {\n      itemVisual[key] = value;\n    }\n  };\n  /**\n   * Clear itemVisuals and list visual.\n   */\n\n\n  SeriesData.prototype.clearAllVisual = function () {\n    this._visual = {};\n    this._itemVisuals = [];\n  };\n\n  SeriesData.prototype.setLayout = function (key, val) {\n    isObject$2(key) ? extend(this._layout, key) : this._layout[key] = val;\n  };\n  /**\n   * Get layout property.\n   */\n\n\n  SeriesData.prototype.getLayout = function (key) {\n    return this._layout[key];\n  };\n  /**\n   * Get layout of single data item\n   */\n\n\n  SeriesData.prototype.getItemLayout = function (idx) {\n    return this._itemLayouts[idx];\n  };\n  /**\n   * Set layout of single data item\n   */\n\n\n  SeriesData.prototype.setItemLayout = function (idx, layout, merge) {\n    this._itemLayouts[idx] = merge ? extend(this._itemLayouts[idx] || {}, layout) : layout;\n  };\n  /**\n   * Clear all layout of single data item\n   */\n\n\n  SeriesData.prototype.clearItemLayouts = function () {\n    this._itemLayouts.length = 0;\n  };\n  /**\n   * Set graphic element relative to data. It can be set as null\n   */\n\n\n  SeriesData.prototype.setItemGraphicEl = function (idx, el) {\n    var seriesIndex = this.hostModel && this.hostModel.seriesIndex;\n    setCommonECData(seriesIndex, this.dataType, idx, el);\n    this._graphicEls[idx] = el;\n  };\n\n  SeriesData.prototype.getItemGraphicEl = function (idx) {\n    return this._graphicEls[idx];\n  };\n\n  SeriesData.prototype.eachItemGraphicEl = function (cb, context) {\n    each(this._graphicEls, function (el, idx) {\n      if (el) {\n        cb && cb.call(context, el, idx);\n      }\n    });\n  };\n  /**\n   * Shallow clone a new list except visual and layout properties, and graph elements.\n   * New list only change the indices.\n   */\n\n\n  SeriesData.prototype.cloneShallow = function (list) {\n    if (!list) {\n      list = new SeriesData(this._schema ? this._schema : map$1(this.dimensions, this._getDimInfo, this), this.hostModel);\n    }\n\n    transferProperties(list, this);\n    list._store = this._store;\n    return list;\n  };\n  /**\n   * Wrap some method to add more feature\n   */\n\n\n  SeriesData.prototype.wrapMethod = function (methodName, injectFunction) {\n    var originalMethod = this[methodName];\n\n    if (!isFunction(originalMethod)) {\n      return;\n    }\n\n    this.__wrappedMethods = this.__wrappedMethods || [];\n\n    this.__wrappedMethods.push(methodName);\n\n    this[methodName] = function () {\n      var res = originalMethod.apply(this, arguments);\n      return injectFunction.apply(this, [res].concat(slice(arguments)));\n    };\n  }; // ----------------------------------------------------------\n  // A work around for internal method visiting private member.\n  // ----------------------------------------------------------\n\n\n  SeriesData.internalField = function () {\n    prepareInvertedIndex = function (data) {\n      var invertedIndicesMap = data._invertedIndicesMap;\n      each(invertedIndicesMap, function (invertedIndices, dim) {\n        var dimInfo = data._dimInfos[dim]; // Currently, only dimensions that has ordinalMeta can create inverted indices.\n\n        var ordinalMeta = dimInfo.ordinalMeta;\n        var store = data._store;\n\n        if (ordinalMeta) {\n          invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array$1(ordinalMeta.categories.length); // The default value of TypedArray is 0. To avoid miss\n          // mapping to 0, we should set it as INDEX_NOT_FOUND.\n\n          for (var i = 0; i < invertedIndices.length; i++) {\n            invertedIndices[i] = INDEX_NOT_FOUND;\n          }\n\n          for (var i = 0; i < store.count(); i++) {\n            // Only support the case that all values are distinct.\n            invertedIndices[store.get(dimInfo.storeDimIndex, i)] = i;\n          }\n        }\n      });\n    };\n\n    getIdNameFromStore = function (data, dimIdx, idx) {\n      return convertOptionIdName(data._getCategory(dimIdx, idx), null);\n    };\n    /**\n     * @see the comment of `List['getId']`.\n     */\n\n\n    getId = function (data, rawIndex) {\n      var id = data._idList[rawIndex];\n\n      if (id == null && data._idDimIdx != null) {\n        id = getIdNameFromStore(data, data._idDimIdx, rawIndex);\n      }\n\n      if (id == null) {\n        id = ID_PREFIX + rawIndex;\n      }\n\n      return id;\n    };\n\n    normalizeDimensions = function (dimensions) {\n      if (!isArray(dimensions)) {\n        dimensions = dimensions != null ? [dimensions] : [];\n      }\n\n      return dimensions;\n    };\n    /**\n     * Data in excludeDimensions is copied, otherwise transferred.\n     */\n\n\n    cloneListForMapAndSample = function (original) {\n      var list = new SeriesData(original._schema ? original._schema : map$1(original.dimensions, original._getDimInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked\n\n      transferProperties(list, original);\n      return list;\n    };\n\n    transferProperties = function (target, source) {\n      each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {\n        if (source.hasOwnProperty(propName)) {\n          target[propName] = source[propName];\n        }\n      });\n      target.__wrappedMethods = source.__wrappedMethods;\n      each(CLONE_PROPERTIES, function (propName) {\n        target[propName] = clone(source[propName]);\n      });\n      target._calculationInfo = extend({}, source._calculationInfo);\n    };\n\n    makeIdFromName = function (data, idx) {\n      var nameList = data._nameList;\n      var idList = data._idList;\n      var nameDimIdx = data._nameDimIdx;\n      var idDimIdx = data._idDimIdx;\n      var name = nameList[idx];\n      var id = idList[idx];\n\n      if (name == null && nameDimIdx != null) {\n        nameList[idx] = name = getIdNameFromStore(data, nameDimIdx, idx);\n      }\n\n      if (id == null && idDimIdx != null) {\n        idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx);\n      }\n\n      if (id == null && name != null) {\n        var nameRepeatCount = data._nameRepeatCount;\n        var nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1;\n        id = name;\n\n        if (nmCnt > 1) {\n          id += '__ec__' + nmCnt;\n        }\n\n        idList[idx] = id;\n      }\n    };\n  }();\n\n  return SeriesData;\n}();\n\n/**\n * For outside usage compat (like echarts-gl are using it).\n */\n\nfunction createDimensions(source, opt) {\n  return prepareSeriesDataSchema(source, opt).dimensions;\n}\n/**\n * This method builds the relationship between:\n * + \"what the coord sys or series requires (see `coordDimensions`)\",\n * + \"what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)\"\n * + \"what the data source provids (see `source`)\".\n *\n * Some guess strategy will be adapted if user does not define something.\n * If no 'value' dimension specified, the first no-named dimension will be\n * named as 'value'.\n *\n * @return The results are always sorted by `storeDimIndex` asc.\n */\n\nfunction prepareSeriesDataSchema( // TODO: TYPE completeDimensions type\nsource, opt) {\n  if (!isSourceInstance(source)) {\n    source = createSourceFromSeriesDataOption(source);\n  }\n\n  opt = opt || {};\n  var sysDims = opt.coordDimensions || [];\n  var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || [];\n  var coordDimNameMap = createHashMap();\n  var resultList = [];\n  var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount); // Try to ignore unused dimensions if sharing a high dimension datastore\n  // 30 is an experience value.\n\n  var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount);\n  var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine;\n  var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef);\n  var encodeDef = opt.encodeDefine;\n\n  if (!encodeDef && opt.encodeDefaulter) {\n    encodeDef = opt.encodeDefaulter(source, dimCount);\n  }\n\n  var encodeDefMap = createHashMap(encodeDef);\n  var indicesMap = new CtorInt32Array(dimCount);\n\n  for (var i = 0; i < indicesMap.length; i++) {\n    indicesMap[i] = -1;\n  }\n\n  function getResultItem(dimIdx) {\n    var idx = indicesMap[dimIdx];\n\n    if (idx < 0) {\n      var dimDefItemRaw = dimsDef[dimIdx];\n      var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : {\n        name: dimDefItemRaw\n      };\n      var resultItem = new SeriesDimensionDefine();\n      var userDimName = dimDefItem.name;\n\n      if (userDimName != null && dataDimNameMap.get(userDimName) != null) {\n        // Only if `series.dimensions` is defined in option\n        // displayName, will be set, and dimension will be displayed vertically in\n        // tooltip by default.\n        resultItem.name = resultItem.displayName = userDimName;\n      }\n\n      dimDefItem.type != null && (resultItem.type = dimDefItem.type);\n      dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);\n      var newIdx = resultList.length;\n      indicesMap[dimIdx] = newIdx;\n      resultItem.storeDimIndex = dimIdx;\n      resultList.push(resultItem);\n      return resultItem;\n    }\n\n    return resultList[idx];\n  }\n\n  if (!omitUnusedDimensions) {\n    for (var i = 0; i < dimCount; i++) {\n      getResultItem(i);\n    }\n  } // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.\n\n\n  encodeDefMap.each(function (dataDimsRaw, coordDim) {\n    var dataDims = normalizeToArray(dataDimsRaw).slice(); // Note: It is allowed that `dataDims.length` is `0`, e.g., options is\n    // `{encode: {x: -1, y: 1}}`. Should not filter anything in\n    // this case.\n\n    if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) {\n      encodeDefMap.set(coordDim, false);\n      return;\n    }\n\n    var validDataDims = encodeDefMap.set(coordDim, []);\n    each(dataDims, function (resultDimIdxOrName, idx) {\n      // The input resultDimIdx can be dim name or index.\n      var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName;\n\n      if (resultDimIdx != null && resultDimIdx < dimCount) {\n        validDataDims[idx] = resultDimIdx;\n        applyDim(getResultItem(resultDimIdx), coordDim, idx);\n      }\n    });\n  }); // Apply templates and default order from `sysDims`.\n\n  var availDimIdx = 0;\n  each(sysDims, function (sysDimItemRaw) {\n    var coordDim;\n    var sysDimItemDimsDef;\n    var sysDimItemOtherDims;\n    var sysDimItem;\n\n    if (isString(sysDimItemRaw)) {\n      coordDim = sysDimItemRaw;\n      sysDimItem = {};\n    } else {\n      sysDimItem = sysDimItemRaw;\n      coordDim = sysDimItem.name;\n      var ordinalMeta = sysDimItem.ordinalMeta;\n      sysDimItem.ordinalMeta = null;\n      sysDimItem = extend({}, sysDimItem);\n      sysDimItem.ordinalMeta = ordinalMeta; // `coordDimIndex` should not be set directly.\n\n      sysDimItemDimsDef = sysDimItem.dimsDef;\n      sysDimItemOtherDims = sysDimItem.otherDims;\n      sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null;\n    }\n\n    var dataDims = encodeDefMap.get(coordDim); // negative resultDimIdx means no need to mapping.\n\n    if (dataDims === false) {\n      return;\n    }\n\n    dataDims = normalizeToArray(dataDims); // dimensions provides default dim sequences.\n\n    if (!dataDims.length) {\n      for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {\n        while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) {\n          availDimIdx++;\n        }\n\n        availDimIdx < dimCount && dataDims.push(availDimIdx++);\n      }\n    } // Apply templates.\n\n\n    each(dataDims, function (resultDimIdx, coordDimIndex) {\n      var resultItem = getResultItem(resultDimIdx); // Coordinate system has a higher priority on dim type than source.\n\n      if (isUsingSourceDimensionsDef && sysDimItem.type != null) {\n        resultItem.type = sysDimItem.type;\n      }\n\n      applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);\n\n      if (resultItem.name == null && sysDimItemDimsDef) {\n        var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];\n        !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {\n          name: sysDimItemDimsDefItem\n        });\n        resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;\n        resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;\n      } // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}\n\n\n      sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);\n    });\n  });\n\n  function applyDim(resultItem, coordDim, coordDimIndex) {\n    if (VISUAL_DIMENSIONS.get(coordDim) != null) {\n      resultItem.otherDims[coordDim] = coordDimIndex;\n    } else {\n      resultItem.coordDim = coordDim;\n      resultItem.coordDimIndex = coordDimIndex;\n      coordDimNameMap.set(coordDim, true);\n    }\n  } // Make sure the first extra dim is 'value'.\n\n\n  var generateCoord = opt.generateCoord;\n  var generateCoordCount = opt.generateCoordCount;\n  var fromZero = generateCoordCount != null;\n  generateCoordCount = generateCoord ? generateCoordCount || 1 : 0;\n  var extra = generateCoord || 'value';\n\n  function ifNoNameFillWithCoordName(resultItem) {\n    if (resultItem.name == null) {\n      // Duplication will be removed in the next step.\n      resultItem.name = resultItem.coordDim;\n    }\n  } // Set dim `name` and other `coordDim` and other props.\n\n\n  if (!omitUnusedDimensions) {\n    for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {\n      var resultItem = getResultItem(resultDimIdx);\n      var coordDim = resultItem.coordDim;\n\n      if (coordDim == null) {\n        // TODO no need to generate coordDim for isExtraCoord?\n        resultItem.coordDim = genCoordDimName(extra, coordDimNameMap, fromZero);\n        resultItem.coordDimIndex = 0; // Series specified generateCoord is using out.\n\n        if (!generateCoord || generateCoordCount <= 0) {\n          resultItem.isExtraCoord = true;\n        }\n\n        generateCoordCount--;\n      }\n\n      ifNoNameFillWithCoordName(resultItem);\n\n      if (resultItem.type == null && (guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must // Consider the case:\n      // {\n      //    dataset: {source: [\n      //        ['2001', 123],\n      //        ['2002', 456],\n      //        ...\n      //        ['The others', 987],\n      //    ]},\n      //    series: {type: 'pie'}\n      // }\n      // The first column should better be treated as a \"ordinal\" although it\n      // might not be detected as an \"ordinal\" by `guessOrdinal`.\n      || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) {\n        resultItem.type = 'ordinal';\n      }\n    }\n  } else {\n    each(resultList, function (resultItem) {\n      // PENDING: guessOrdinal or let user specify type: 'ordinal' manually?\n      ifNoNameFillWithCoordName(resultItem);\n    }); // Sort dimensions: there are some rule that use the last dim as label,\n    // and for some latter travel process easier.\n\n    resultList.sort(function (item0, item1) {\n      return item0.storeDimIndex - item1.storeDimIndex;\n    });\n  }\n\n  removeDuplication(resultList);\n  return new SeriesDataSchema({\n    source: source,\n    dimensions: resultList,\n    fullDimensionCount: dimCount,\n    dimensionOmitted: omitUnusedDimensions\n  });\n}\n\nfunction removeDuplication(result) {\n  var duplicationMap = createHashMap();\n\n  for (var i = 0; i < result.length; i++) {\n    var dim = result[i];\n    var dimOriginalName = dim.name;\n    var count = duplicationMap.get(dimOriginalName) || 0;\n\n    if (count > 0) {\n      // Starts from 0.\n      dim.name = dimOriginalName + (count - 1);\n    }\n\n    count++;\n    duplicationMap.set(dimOriginalName, count);\n  }\n} // ??? TODO\n// Originally detect dimCount by data[0]. Should we\n// optimize it to only by sysDims and dimensions and encode.\n// So only necessary dims will be initialized.\n// But\n// (1) custom series should be considered. where other dims\n// may be visited.\n// (2) sometimes user need to calculate bubble size or use visualMap\n// on other dimensions besides coordSys needed.\n// So, dims that is not used by system, should be shared in data store?\n\n\nfunction getDimCount(source, sysDims, dimsDef, optDimCount) {\n  // Note that the result dimCount should not small than columns count\n  // of data, otherwise `dataDimNameMap` checking will be incorrect.\n  var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0);\n  each(sysDims, function (sysDimItem) {\n    var sysDimItemDimsDef;\n\n    if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) {\n      dimCount = Math.max(dimCount, sysDimItemDimsDef.length);\n    }\n  });\n  return dimCount;\n}\n\nfunction genCoordDimName(name, map, fromZero) {\n  if (fromZero || map.hasKey(name)) {\n    var i = 0;\n\n    while (map.hasKey(name + i)) {\n      i++;\n    }\n\n    name += i;\n  }\n\n  map.set(name, true);\n  return name;\n}\n\n/**\n * @class\n * For example:\n * {\n *     coordSysName: 'cartesian2d',\n *     coordSysDims: ['x', 'y', ...],\n *     axisMap: HashMap({\n *         x: xAxisModel,\n *         y: yAxisModel\n *     }),\n *     categoryAxisMap: HashMap({\n *         x: xAxisModel,\n *         y: undefined\n *     }),\n *     // The index of the first category axis in `coordSysDims`.\n *     // `null/undefined` means no category axis exists.\n *     firstCategoryDimIndex: 1,\n *     // To replace user specified encode.\n * }\n */\n\nvar CoordSysInfo =\n/** @class */\nfunction () {\n  function CoordSysInfo(coordSysName) {\n    this.coordSysDims = [];\n    this.axisMap = createHashMap();\n    this.categoryAxisMap = createHashMap();\n    this.coordSysName = coordSysName;\n  }\n\n  return CoordSysInfo;\n}();\n\nfunction getCoordSysInfoBySeries(seriesModel) {\n  var coordSysName = seriesModel.get('coordinateSystem');\n  var result = new CoordSysInfo(coordSysName);\n  var fetch = fetchers[coordSysName];\n\n  if (fetch) {\n    fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);\n    return result;\n  }\n}\nvar fetchers = {\n  cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {\n    var xAxisModel = seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n    var yAxisModel = seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n\n    if (\"development\" !== 'production') {\n      if (!xAxisModel) {\n        throw new Error('xAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('xAxisId'), 0) + '\" not found');\n      }\n\n      if (!yAxisModel) {\n        throw new Error('yAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('yAxisId'), 0) + '\" not found');\n      }\n    }\n\n    result.coordSysDims = ['x', 'y'];\n    axisMap.set('x', xAxisModel);\n    axisMap.set('y', yAxisModel);\n\n    if (isCategory(xAxisModel)) {\n      categoryAxisMap.set('x', xAxisModel);\n      result.firstCategoryDimIndex = 0;\n    }\n\n    if (isCategory(yAxisModel)) {\n      categoryAxisMap.set('y', yAxisModel);\n      result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n    }\n  },\n  singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {\n    var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n\n    if (\"development\" !== 'production') {\n      if (!singleAxisModel) {\n        throw new Error('singleAxis should be specified.');\n      }\n    }\n\n    result.coordSysDims = ['single'];\n    axisMap.set('single', singleAxisModel);\n\n    if (isCategory(singleAxisModel)) {\n      categoryAxisMap.set('single', singleAxisModel);\n      result.firstCategoryDimIndex = 0;\n    }\n  },\n  polar: function (seriesModel, result, axisMap, categoryAxisMap) {\n    var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n    var radiusAxisModel = polarModel.findAxisModel('radiusAxis');\n    var angleAxisModel = polarModel.findAxisModel('angleAxis');\n\n    if (\"development\" !== 'production') {\n      if (!angleAxisModel) {\n        throw new Error('angleAxis option not found');\n      }\n\n      if (!radiusAxisModel) {\n        throw new Error('radiusAxis option not found');\n      }\n    }\n\n    result.coordSysDims = ['radius', 'angle'];\n    axisMap.set('radius', radiusAxisModel);\n    axisMap.set('angle', angleAxisModel);\n\n    if (isCategory(radiusAxisModel)) {\n      categoryAxisMap.set('radius', radiusAxisModel);\n      result.firstCategoryDimIndex = 0;\n    }\n\n    if (isCategory(angleAxisModel)) {\n      categoryAxisMap.set('angle', angleAxisModel);\n      result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n    }\n  },\n  geo: function (seriesModel, result, axisMap, categoryAxisMap) {\n    result.coordSysDims = ['lng', 'lat'];\n  },\n  parallel: function (seriesModel, result, axisMap, categoryAxisMap) {\n    var ecModel = seriesModel.ecModel;\n    var parallelModel = ecModel.getComponent('parallel', seriesModel.get('parallelIndex'));\n    var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice();\n    each(parallelModel.parallelAxisIndex, function (axisIndex, index) {\n      var axisModel = ecModel.getComponent('parallelAxis', axisIndex);\n      var axisDim = coordSysDims[index];\n      axisMap.set(axisDim, axisModel);\n\n      if (isCategory(axisModel)) {\n        categoryAxisMap.set(axisDim, axisModel);\n\n        if (result.firstCategoryDimIndex == null) {\n          result.firstCategoryDimIndex = index;\n        }\n      }\n    });\n  }\n};\n\nfunction isCategory(axisModel) {\n  return axisModel.get('type') === 'category';\n}\n\n/**\n * Note that it is too complicated to support 3d stack by value\n * (have to create two-dimension inverted index), so in 3d case\n * we just support that stacked by index.\n *\n * @param seriesModel\n * @param dimensionsInput The same as the input of <module:echarts/data/SeriesData>.\n *        The input will be modified.\n * @param opt\n * @param opt.stackedCoordDimension Specify a coord dimension if needed.\n * @param opt.byIndex=false\n * @return calculationInfo\n * {\n *     stackedDimension: string\n *     stackedByDimension: string\n *     isStackedByIndex: boolean\n *     stackedOverDimension: string\n *     stackResultDimension: string\n * }\n */\n\nfunction enableDataStack(seriesModel, dimensionsInput, opt) {\n  opt = opt || {};\n  var byIndex = opt.byIndex;\n  var stackedCoordDimension = opt.stackedCoordDimension;\n  var dimensionDefineList;\n  var schema;\n  var store;\n\n  if (isLegacyDimensionsInput(dimensionsInput)) {\n    dimensionDefineList = dimensionsInput;\n  } else {\n    schema = dimensionsInput.schema;\n    dimensionDefineList = schema.dimensions;\n    store = dimensionsInput.store;\n  } // Compatibal: when `stack` is set as '', do not stack.\n\n\n  var mayStack = !!(seriesModel && seriesModel.get('stack'));\n  var stackedByDimInfo;\n  var stackedDimInfo;\n  var stackResultDimension;\n  var stackedOverDimension;\n  each(dimensionDefineList, function (dimensionInfo, index) {\n    if (isString(dimensionInfo)) {\n      dimensionDefineList[index] = dimensionInfo = {\n        name: dimensionInfo\n      };\n    }\n\n    if (mayStack && !dimensionInfo.isExtraCoord) {\n      // Find the first ordinal dimension as the stackedByDimInfo.\n      if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) {\n        stackedByDimInfo = dimensionInfo;\n      } // Find the first stackable dimension as the stackedDimInfo.\n\n\n      if (!stackedDimInfo && dimensionInfo.type !== 'ordinal' && dimensionInfo.type !== 'time' && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)) {\n        stackedDimInfo = dimensionInfo;\n      }\n    }\n  });\n\n  if (stackedDimInfo && !byIndex && !stackedByDimInfo) {\n    // Compatible with previous design, value axis (time axis) only stack by index.\n    // It may make sense if the user provides elaborately constructed data.\n    byIndex = true;\n  } // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`.\n  // That put stack logic in List is for using conveniently in echarts extensions, but it\n  // might not be a good way.\n\n\n  if (stackedDimInfo) {\n    // Use a weird name that not duplicated with other names.\n    // Also need to use seriesModel.id as postfix because different\n    // series may share same data store. The stack dimension needs to be distinguished.\n    stackResultDimension = '__\\0ecstackresult_' + seriesModel.id;\n    stackedOverDimension = '__\\0ecstackedover_' + seriesModel.id; // Create inverted index to fast query index by value.\n\n    if (stackedByDimInfo) {\n      stackedByDimInfo.createInvertedIndices = true;\n    }\n\n    var stackedDimCoordDim_1 = stackedDimInfo.coordDim;\n    var stackedDimType = stackedDimInfo.type;\n    var stackedDimCoordIndex_1 = 0;\n    each(dimensionDefineList, function (dimensionInfo) {\n      if (dimensionInfo.coordDim === stackedDimCoordDim_1) {\n        stackedDimCoordIndex_1++;\n      }\n    });\n    var stackedOverDimensionDefine = {\n      name: stackResultDimension,\n      coordDim: stackedDimCoordDim_1,\n      coordDimIndex: stackedDimCoordIndex_1,\n      type: stackedDimType,\n      isExtraCoord: true,\n      isCalculationCoord: true,\n      storeDimIndex: dimensionDefineList.length\n    };\n    var stackResultDimensionDefine = {\n      name: stackedOverDimension,\n      // This dimension contains stack base (generally, 0), so do not set it as\n      // `stackedDimCoordDim` to avoid extent calculation, consider log scale.\n      coordDim: stackedOverDimension,\n      coordDimIndex: stackedDimCoordIndex_1 + 1,\n      type: stackedDimType,\n      isExtraCoord: true,\n      isCalculationCoord: true,\n      storeDimIndex: dimensionDefineList.length + 1\n    };\n\n    if (schema) {\n      if (store) {\n        stackedOverDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackedOverDimension, stackedDimType);\n        stackResultDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackResultDimension, stackedDimType);\n      }\n\n      schema.appendCalculationDimension(stackedOverDimensionDefine);\n      schema.appendCalculationDimension(stackResultDimensionDefine);\n    } else {\n      dimensionDefineList.push(stackedOverDimensionDefine);\n      dimensionDefineList.push(stackResultDimensionDefine);\n    }\n  }\n\n  return {\n    stackedDimension: stackedDimInfo && stackedDimInfo.name,\n    stackedByDimension: stackedByDimInfo && stackedByDimInfo.name,\n    isStackedByIndex: byIndex,\n    stackedOverDimension: stackedOverDimension,\n    stackResultDimension: stackResultDimension\n  };\n}\n\nfunction isLegacyDimensionsInput(dimensionsInput) {\n  return !isSeriesDataSchema(dimensionsInput.schema);\n}\n\nfunction isDimensionStacked(data, stackedDim) {\n  // Each single series only maps to one pair of axis. So we do not need to\n  // check stackByDim, whatever stacked by a dimension or stacked by index.\n  return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension');\n}\nfunction getStackedDimension(data, targetDim) {\n  return isDimensionStacked(data, targetDim) ? data.getCalculationInfo('stackResultDimension') : targetDim;\n}\n\nfunction getCoordSysDimDefs(seriesModel, coordSysInfo) {\n  var coordSysName = seriesModel.get('coordinateSystem');\n  var registeredCoordSys = CoordinateSystemManager.get(coordSysName);\n  var coordSysDimDefs;\n\n  if (coordSysInfo && coordSysInfo.coordSysDims) {\n    coordSysDimDefs = map(coordSysInfo.coordSysDims, function (dim) {\n      var dimInfo = {\n        name: dim\n      };\n      var axisModel = coordSysInfo.axisMap.get(dim);\n\n      if (axisModel) {\n        var axisType = axisModel.get('type');\n        dimInfo.type = getDimensionTypeByAxis(axisType);\n      }\n\n      return dimInfo;\n    });\n  }\n\n  if (!coordSysDimDefs) {\n    // Get dimensions from registered coordinate system\n    coordSysDimDefs = registeredCoordSys && (registeredCoordSys.getDimensionsInfo ? registeredCoordSys.getDimensionsInfo() : registeredCoordSys.dimensions.slice()) || ['x', 'y'];\n  }\n\n  return coordSysDimDefs;\n}\n\nfunction injectOrdinalMeta(dimInfoList, createInvertedIndices, coordSysInfo) {\n  var firstCategoryDimIndex;\n  var hasNameEncode;\n  coordSysInfo && each(dimInfoList, function (dimInfo, dimIndex) {\n    var coordDim = dimInfo.coordDim;\n    var categoryAxisModel = coordSysInfo.categoryAxisMap.get(coordDim);\n\n    if (categoryAxisModel) {\n      if (firstCategoryDimIndex == null) {\n        firstCategoryDimIndex = dimIndex;\n      }\n\n      dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta();\n\n      if (createInvertedIndices) {\n        dimInfo.createInvertedIndices = true;\n      }\n    }\n\n    if (dimInfo.otherDims.itemName != null) {\n      hasNameEncode = true;\n    }\n  });\n\n  if (!hasNameEncode && firstCategoryDimIndex != null) {\n    dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0;\n  }\n\n  return firstCategoryDimIndex;\n}\n/**\n * Caution: there are side effects to `sourceManager` in this method.\n * Should better only be called in `Series['getInitialData']`.\n */\n\n\nfunction createSeriesData(sourceRaw, seriesModel, opt) {\n  opt = opt || {};\n  var sourceManager = seriesModel.getSourceManager();\n  var source;\n  var isOriginalSource = false;\n\n  if (sourceRaw) {\n    isOriginalSource = true;\n    source = createSourceFromSeriesDataOption(sourceRaw);\n  } else {\n    source = sourceManager.getSource(); // Is series.data. not dataset.\n\n    isOriginalSource = source.sourceFormat === SOURCE_FORMAT_ORIGINAL;\n  }\n\n  var coordSysInfo = getCoordSysInfoBySeries(seriesModel);\n  var coordSysDimDefs = getCoordSysDimDefs(seriesModel, coordSysInfo);\n  var useEncodeDefaulter = opt.useEncodeDefaulter;\n  var encodeDefaulter = isFunction(useEncodeDefaulter) ? useEncodeDefaulter : useEncodeDefaulter ? curry(makeSeriesEncodeForAxisCoordSys, coordSysDimDefs, seriesModel) : null;\n  var createDimensionOptions = {\n    coordDimensions: coordSysDimDefs,\n    generateCoord: opt.generateCoord,\n    encodeDefine: seriesModel.getEncode(),\n    encodeDefaulter: encodeDefaulter,\n    canOmitUnusedDimensions: !isOriginalSource\n  };\n  var schema = prepareSeriesDataSchema(source, createDimensionOptions);\n  var firstCategoryDimIndex = injectOrdinalMeta(schema.dimensions, opt.createInvertedIndices, coordSysInfo);\n  var store = !isOriginalSource ? sourceManager.getSharedDataStore(schema) : null;\n  var stackCalculationInfo = enableDataStack(seriesModel, {\n    schema: schema,\n    store: store\n  });\n  var data = new SeriesData(schema, seriesModel);\n  data.setCalculationInfo(stackCalculationInfo);\n  var dimValueGetter = firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source) ? function (itemOpt, dimName, dataIndex, dimIndex) {\n    // Use dataIndex as ordinal value in categoryAxis\n    return dimIndex === firstCategoryDimIndex ? dataIndex : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);\n  } : null;\n  data.hasItemOption = false;\n  data.initData( // Try to reuse the data store in sourceManager if using dataset.\n  isOriginalSource ? source : store, null, dimValueGetter);\n  return data;\n}\n\nfunction isNeedCompleteOrdinalData(source) {\n  if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n    var sampleItem = firstDataNotNull(source.data || []);\n    return !isArray(getDataItemValue(sampleItem));\n  }\n}\n\nfunction firstDataNotNull(arr) {\n  var i = 0;\n\n  while (i < arr.length && arr[i] == null) {\n    i++;\n  }\n\n  return arr[i];\n}\n\nvar Scale =\n/** @class */\nfunction () {\n  function Scale(setting) {\n    this._setting = setting || {};\n    this._extent = [Infinity, -Infinity];\n  }\n\n  Scale.prototype.getSetting = function (name) {\n    return this._setting[name];\n  };\n  /**\n   * Set extent from data\n   */\n\n\n  Scale.prototype.unionExtent = function (other) {\n    var extent = this._extent;\n    other[0] < extent[0] && (extent[0] = other[0]);\n    other[1] > extent[1] && (extent[1] = other[1]); // not setExtent because in log axis it may transformed to power\n    // this.setExtent(extent[0], extent[1]);\n  };\n  /**\n   * Set extent from data\n   */\n\n\n  Scale.prototype.unionExtentFromData = function (data, dim) {\n    this.unionExtent(data.getApproximateExtent(dim));\n  };\n  /**\n   * Get extent\n   *\n   * Extent is always in increase order.\n   */\n\n\n  Scale.prototype.getExtent = function () {\n    return this._extent.slice();\n  };\n  /**\n   * Set extent\n   */\n\n\n  Scale.prototype.setExtent = function (start, end) {\n    var thisExtent = this._extent;\n\n    if (!isNaN(start)) {\n      thisExtent[0] = start;\n    }\n\n    if (!isNaN(end)) {\n      thisExtent[1] = end;\n    }\n  };\n  /**\n   * If value is in extent range\n   */\n\n\n  Scale.prototype.isInExtentRange = function (value) {\n    return this._extent[0] <= value && this._extent[1] >= value;\n  };\n  /**\n   * When axis extent depends on data and no data exists,\n   * axis ticks should not be drawn, which is named 'blank'.\n   */\n\n\n  Scale.prototype.isBlank = function () {\n    return this._isBlank;\n  };\n  /**\n   * When axis extent depends on data and no data exists,\n   * axis ticks should not be drawn, which is named 'blank'.\n   */\n\n\n  Scale.prototype.setBlank = function (isBlank) {\n    this._isBlank = isBlank;\n  };\n\n  return Scale;\n}();\n\nenableClassManagement(Scale);\n\nvar uidBase = 0;\n\nvar OrdinalMeta =\n/** @class */\nfunction () {\n  function OrdinalMeta(opt) {\n    this.categories = opt.categories || [];\n    this._needCollect = opt.needCollect;\n    this._deduplication = opt.deduplication;\n    this.uid = ++uidBase;\n  }\n\n  OrdinalMeta.createByAxisModel = function (axisModel) {\n    var option = axisModel.option;\n    var data = option.data;\n    var categories = data && map(data, getName);\n    return new OrdinalMeta({\n      categories: categories,\n      needCollect: !categories,\n      // deduplication is default in axis.\n      deduplication: option.dedplication !== false\n    });\n  };\n\n  OrdinalMeta.prototype.getOrdinal = function (category) {\n    // @ts-ignore\n    return this._getOrCreateMap().get(category);\n  };\n  /**\n   * @return The ordinal. If not found, return NaN.\n   */\n\n\n  OrdinalMeta.prototype.parseAndCollect = function (category) {\n    var index;\n    var needCollect = this._needCollect; // The value of category dim can be the index of the given category set.\n    // This feature is only supported when !needCollect, because we should\n    // consider a common case: a value is 2017, which is a number but is\n    // expected to be tread as a category. This case usually happen in dataset,\n    // where it happent to be no need of the index feature.\n\n    if (!isString(category) && !needCollect) {\n      return category;\n    } // Optimize for the scenario:\n    // category is ['2012-01-01', '2012-01-02', ...], where the input\n    // data has been ensured not duplicate and is large data.\n    // Notice, if a dataset dimension provide categroies, usually echarts\n    // should remove duplication except user tell echarts dont do that\n    // (set axis.deduplication = false), because echarts do not know whether\n    // the values in the category dimension has duplication (consider the\n    // parallel-aqi example)\n\n\n    if (needCollect && !this._deduplication) {\n      index = this.categories.length;\n      this.categories[index] = category;\n      return index;\n    }\n\n    var map = this._getOrCreateMap(); // @ts-ignore\n\n\n    index = map.get(category);\n\n    if (index == null) {\n      if (needCollect) {\n        index = this.categories.length;\n        this.categories[index] = category; // @ts-ignore\n\n        map.set(category, index);\n      } else {\n        index = NaN;\n      }\n    }\n\n    return index;\n  }; // Consider big data, do not create map until needed.\n\n\n  OrdinalMeta.prototype._getOrCreateMap = function () {\n    return this._map || (this._map = createHashMap(this.categories));\n  };\n\n  return OrdinalMeta;\n}();\n\nfunction getName(obj) {\n  if (isObject(obj) && obj.value != null) {\n    return obj.value;\n  } else {\n    return obj + '';\n  }\n}\n\nfunction isValueNice(val) {\n  var exp10 = Math.pow(10, quantityExponent(Math.abs(val)));\n  var f = Math.abs(val / exp10);\n  return f === 0 || f === 1 || f === 2 || f === 3 || f === 5;\n}\nfunction isIntervalOrLogScale(scale) {\n  return scale.type === 'interval' || scale.type === 'log';\n}\n/**\n * @param extent Both extent[0] and extent[1] should be valid number.\n *               Should be extent[0] < extent[1].\n * @param splitNumber splitNumber should be >= 1.\n */\n\nfunction intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {\n  var result = {};\n  var span = extent[1] - extent[0];\n  var interval = result.interval = nice(span / splitNumber, true);\n\n  if (minInterval != null && interval < minInterval) {\n    interval = result.interval = minInterval;\n  }\n\n  if (maxInterval != null && interval > maxInterval) {\n    interval = result.interval = maxInterval;\n  } // Tow more digital for tick.\n\n\n  var precision = result.intervalPrecision = getIntervalPrecision(interval); // Niced extent inside original extent\n\n  var niceTickExtent = result.niceTickExtent = [round(Math.ceil(extent[0] / interval) * interval, precision), round(Math.floor(extent[1] / interval) * interval, precision)];\n  fixExtent(niceTickExtent, extent);\n  return result;\n}\nfunction increaseInterval(interval) {\n  var exp10 = Math.pow(10, quantityExponent(interval)); // Increase interval\n\n  var f = interval / exp10;\n\n  if (!f) {\n    f = 1;\n  } else if (f === 2) {\n    f = 3;\n  } else if (f === 3) {\n    f = 5;\n  } else {\n    // f is 1 or 5\n    f *= 2;\n  }\n\n  return round(f * exp10);\n}\n/**\n * @return interval precision\n */\n\nfunction getIntervalPrecision(interval) {\n  // Tow more digital for tick.\n  return getPrecision(interval) + 2;\n}\n\nfunction clamp(niceTickExtent, idx, extent) {\n  niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);\n} // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.\n\n\nfunction fixExtent(niceTickExtent, extent) {\n  !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);\n  !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);\n  clamp(niceTickExtent, 0, extent);\n  clamp(niceTickExtent, 1, extent);\n\n  if (niceTickExtent[0] > niceTickExtent[1]) {\n    niceTickExtent[0] = niceTickExtent[1];\n  }\n}\nfunction contain$1(val, extent) {\n  return val >= extent[0] && val <= extent[1];\n}\nfunction normalize$1(val, extent) {\n  if (extent[1] === extent[0]) {\n    return 0.5;\n  }\n\n  return (val - extent[0]) / (extent[1] - extent[0]);\n}\nfunction scale$2(val, extent) {\n  return val * (extent[1] - extent[0]) + extent[0];\n}\n\nvar OrdinalScale =\n/** @class */\nfunction (_super) {\n  __extends(OrdinalScale, _super);\n\n  function OrdinalScale(setting) {\n    var _this = _super.call(this, setting) || this;\n\n    _this.type = 'ordinal';\n\n    var ordinalMeta = _this.getSetting('ordinalMeta'); // Caution: Should not use instanceof, consider ec-extensions using\n    // import approach to get OrdinalMeta class.\n\n\n    if (!ordinalMeta) {\n      ordinalMeta = new OrdinalMeta({});\n    }\n\n    if (isArray(ordinalMeta)) {\n      ordinalMeta = new OrdinalMeta({\n        categories: map(ordinalMeta, function (item) {\n          return isObject(item) ? item.value : item;\n        })\n      });\n    }\n\n    _this._ordinalMeta = ordinalMeta;\n    _this._extent = _this.getSetting('extent') || [0, ordinalMeta.categories.length - 1];\n    return _this;\n  }\n\n  OrdinalScale.prototype.parse = function (val) {\n    // Caution: Math.round(null) will return `0` rather than `NaN`\n    if (val == null) {\n      return NaN;\n    }\n\n    return isString(val) ? this._ordinalMeta.getOrdinal(val) // val might be float.\n    : Math.round(val);\n  };\n\n  OrdinalScale.prototype.contain = function (rank) {\n    rank = this.parse(rank);\n    return contain$1(rank, this._extent) && this._ordinalMeta.categories[rank] != null;\n  };\n  /**\n   * Normalize given rank or name to linear [0, 1]\n   * @param val raw ordinal number.\n   * @return normalized value in [0, 1].\n   */\n\n\n  OrdinalScale.prototype.normalize = function (val) {\n    val = this._getTickNumber(this.parse(val));\n    return normalize$1(val, this._extent);\n  };\n  /**\n   * @param val normalized value in [0, 1].\n   * @return raw ordinal number.\n   */\n\n\n  OrdinalScale.prototype.scale = function (val) {\n    val = Math.round(scale$2(val, this._extent));\n    return this.getRawOrdinalNumber(val);\n  };\n\n  OrdinalScale.prototype.getTicks = function () {\n    var ticks = [];\n    var extent = this._extent;\n    var rank = extent[0];\n\n    while (rank <= extent[1]) {\n      ticks.push({\n        value: rank\n      });\n      rank++;\n    }\n\n    return ticks;\n  };\n\n  OrdinalScale.prototype.getMinorTicks = function (splitNumber) {\n    // Not support.\n    return;\n  };\n  /**\n   * @see `Ordinal['_ordinalNumbersByTick']`\n   */\n\n\n  OrdinalScale.prototype.setSortInfo = function (info) {\n    if (info == null) {\n      this._ordinalNumbersByTick = this._ticksByOrdinalNumber = null;\n      return;\n    }\n\n    var infoOrdinalNumbers = info.ordinalNumbers;\n    var ordinalsByTick = this._ordinalNumbersByTick = [];\n    var ticksByOrdinal = this._ticksByOrdinalNumber = []; // Unnecessary support negative tick in `realtimeSort`.\n\n    var tickNum = 0;\n    var allCategoryLen = this._ordinalMeta.categories.length;\n\n    for (var len = Math.min(allCategoryLen, infoOrdinalNumbers.length); tickNum < len; ++tickNum) {\n      var ordinalNumber = infoOrdinalNumbers[tickNum];\n      ordinalsByTick[tickNum] = ordinalNumber;\n      ticksByOrdinal[ordinalNumber] = tickNum;\n    } // Handle that `series.data` only covers part of the `axis.category.data`.\n\n\n    var unusedOrdinal = 0;\n\n    for (; tickNum < allCategoryLen; ++tickNum) {\n      while (ticksByOrdinal[unusedOrdinal] != null) {\n        unusedOrdinal++;\n      }\n      ordinalsByTick.push(unusedOrdinal);\n      ticksByOrdinal[unusedOrdinal] = tickNum;\n    }\n  };\n\n  OrdinalScale.prototype._getTickNumber = function (ordinal) {\n    var ticksByOrdinalNumber = this._ticksByOrdinalNumber; // also support ordinal out of range of `ordinalMeta.categories.length`,\n    // where ordinal numbers are used as tick value directly.\n\n    return ticksByOrdinalNumber && ordinal >= 0 && ordinal < ticksByOrdinalNumber.length ? ticksByOrdinalNumber[ordinal] : ordinal;\n  };\n  /**\n   * @usage\n   * ```js\n   * const ordinalNumber = ordinalScale.getRawOrdinalNumber(tickVal);\n   *\n   * // case0\n   * const rawOrdinalValue = axisModel.getCategories()[ordinalNumber];\n   * // case1\n   * const rawOrdinalValue = this._ordinalMeta.categories[ordinalNumber];\n   * // case2\n   * const coord = axis.dataToCoord(ordinalNumber);\n   * ```\n   *\n   * @param {OrdinalNumber} tickNumber index of display\n   */\n\n\n  OrdinalScale.prototype.getRawOrdinalNumber = function (tickNumber) {\n    var ordinalNumbersByTick = this._ordinalNumbersByTick; // tickNumber may be out of range, e.g., when axis max is larger than `ordinalMeta.categories.length`.,\n    // where ordinal numbers are used as tick value directly.\n\n    return ordinalNumbersByTick && tickNumber >= 0 && tickNumber < ordinalNumbersByTick.length ? ordinalNumbersByTick[tickNumber] : tickNumber;\n  };\n  /**\n   * Get item on tick\n   */\n\n\n  OrdinalScale.prototype.getLabel = function (tick) {\n    if (!this.isBlank()) {\n      var ordinalNumber = this.getRawOrdinalNumber(tick.value);\n      var cateogry = this._ordinalMeta.categories[ordinalNumber]; // Note that if no data, ordinalMeta.categories is an empty array.\n      // Return empty if it's not exist.\n\n      return cateogry == null ? '' : cateogry + '';\n    }\n  };\n\n  OrdinalScale.prototype.count = function () {\n    return this._extent[1] - this._extent[0] + 1;\n  };\n\n  OrdinalScale.prototype.unionExtentFromData = function (data, dim) {\n    this.unionExtent(data.getApproximateExtent(dim));\n  };\n  /**\n   * @override\n   * If value is in extent range\n   */\n\n\n  OrdinalScale.prototype.isInExtentRange = function (value) {\n    value = this._getTickNumber(value);\n    return this._extent[0] <= value && this._extent[1] >= value;\n  };\n\n  OrdinalScale.prototype.getOrdinalMeta = function () {\n    return this._ordinalMeta;\n  };\n\n  OrdinalScale.prototype.calcNiceTicks = function () {};\n\n  OrdinalScale.prototype.calcNiceExtent = function () {};\n\n  OrdinalScale.type = 'ordinal';\n  return OrdinalScale;\n}(Scale);\n\nScale.registerClass(OrdinalScale);\n\nvar roundNumber = round;\n\nvar IntervalScale =\n/** @class */\nfunction (_super) {\n  __extends(IntervalScale, _super);\n\n  function IntervalScale() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = 'interval'; // Step is calculated in adjustExtent.\n\n    _this._interval = 0;\n    _this._intervalPrecision = 2;\n    return _this;\n  }\n\n  IntervalScale.prototype.parse = function (val) {\n    return val;\n  };\n\n  IntervalScale.prototype.contain = function (val) {\n    return contain$1(val, this._extent);\n  };\n\n  IntervalScale.prototype.normalize = function (val) {\n    return normalize$1(val, this._extent);\n  };\n\n  IntervalScale.prototype.scale = function (val) {\n    return scale$2(val, this._extent);\n  };\n\n  IntervalScale.prototype.setExtent = function (start, end) {\n    var thisExtent = this._extent; // start,end may be a Number like '25',so...\n\n    if (!isNaN(start)) {\n      thisExtent[0] = parseFloat(start);\n    }\n\n    if (!isNaN(end)) {\n      thisExtent[1] = parseFloat(end);\n    }\n  };\n\n  IntervalScale.prototype.unionExtent = function (other) {\n    var extent = this._extent;\n    other[0] < extent[0] && (extent[0] = other[0]);\n    other[1] > extent[1] && (extent[1] = other[1]); // unionExtent may called by it's sub classes\n\n    this.setExtent(extent[0], extent[1]);\n  };\n\n  IntervalScale.prototype.getInterval = function () {\n    return this._interval;\n  };\n\n  IntervalScale.prototype.setInterval = function (interval) {\n    this._interval = interval; // Dropped auto calculated niceExtent and use user-set extent.\n    // We assume user wants to set both interval, min, max to get a better result.\n\n    this._niceExtent = this._extent.slice();\n    this._intervalPrecision = getIntervalPrecision(interval);\n  };\n  /**\n   * @param expandToNicedExtent Whether expand the ticks to niced extent.\n   */\n\n\n  IntervalScale.prototype.getTicks = function (expandToNicedExtent) {\n    var interval = this._interval;\n    var extent = this._extent;\n    var niceTickExtent = this._niceExtent;\n    var intervalPrecision = this._intervalPrecision;\n    var ticks = []; // If interval is 0, return [];\n\n    if (!interval) {\n      return ticks;\n    } // Consider this case: using dataZoom toolbox, zoom and zoom.\n\n\n    var safeLimit = 10000;\n\n    if (extent[0] < niceTickExtent[0]) {\n      if (expandToNicedExtent) {\n        ticks.push({\n          value: roundNumber(niceTickExtent[0] - interval, intervalPrecision)\n        });\n      } else {\n        ticks.push({\n          value: extent[0]\n        });\n      }\n    }\n\n    var tick = niceTickExtent[0];\n\n    while (tick <= niceTickExtent[1]) {\n      ticks.push({\n        value: tick\n      }); // Avoid rounding error\n\n      tick = roundNumber(tick + interval, intervalPrecision);\n\n      if (tick === ticks[ticks.length - 1].value) {\n        // Consider out of safe float point, e.g.,\n        // -3711126.9907707 + 2e-10 === -3711126.9907707\n        break;\n      }\n\n      if (ticks.length > safeLimit) {\n        return [];\n      }\n    } // Consider this case: the last item of ticks is smaller\n    // than niceTickExtent[1] and niceTickExtent[1] === extent[1].\n\n\n    var lastNiceTick = ticks.length ? ticks[ticks.length - 1].value : niceTickExtent[1];\n\n    if (extent[1] > lastNiceTick) {\n      if (expandToNicedExtent) {\n        ticks.push({\n          value: roundNumber(lastNiceTick + interval, intervalPrecision)\n        });\n      } else {\n        ticks.push({\n          value: extent[1]\n        });\n      }\n    }\n\n    return ticks;\n  };\n\n  IntervalScale.prototype.getMinorTicks = function (splitNumber) {\n    var ticks = this.getTicks(true);\n    var minorTicks = [];\n    var extent = this.getExtent();\n\n    for (var i = 1; i < ticks.length; i++) {\n      var nextTick = ticks[i];\n      var prevTick = ticks[i - 1];\n      var count = 0;\n      var minorTicksGroup = [];\n      var interval = nextTick.value - prevTick.value;\n      var minorInterval = interval / splitNumber;\n\n      while (count < splitNumber - 1) {\n        var minorTick = roundNumber(prevTick.value + (count + 1) * minorInterval); // For the first and last interval. The count may be less than splitNumber.\n\n        if (minorTick > extent[0] && minorTick < extent[1]) {\n          minorTicksGroup.push(minorTick);\n        }\n\n        count++;\n      }\n\n      minorTicks.push(minorTicksGroup);\n    }\n\n    return minorTicks;\n  };\n  /**\n   * @param opt.precision If 'auto', use nice presision.\n   * @param opt.pad returns 1.50 but not 1.5 if precision is 2.\n   */\n\n\n  IntervalScale.prototype.getLabel = function (data, opt) {\n    if (data == null) {\n      return '';\n    }\n\n    var precision = opt && opt.precision;\n\n    if (precision == null) {\n      precision = getPrecision(data.value) || 0;\n    } else if (precision === 'auto') {\n      // Should be more precise then tick.\n      precision = this._intervalPrecision;\n    } // (1) If `precision` is set, 12.005 should be display as '12.00500'.\n    // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.\n\n\n    var dataNum = roundNumber(data.value, precision, true);\n    return addCommas(dataNum);\n  };\n  /**\n   * @param splitNumber By default `5`.\n   */\n\n\n  IntervalScale.prototype.calcNiceTicks = function (splitNumber, minInterval, maxInterval) {\n    splitNumber = splitNumber || 5;\n    var extent = this._extent;\n    var span = extent[1] - extent[0];\n\n    if (!isFinite(span)) {\n      return;\n    } // User may set axis min 0 and data are all negative\n    // FIXME If it needs to reverse ?\n\n\n    if (span < 0) {\n      span = -span;\n      extent.reverse();\n    }\n\n    var result = intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval);\n    this._intervalPrecision = result.intervalPrecision;\n    this._interval = result.interval;\n    this._niceExtent = result.niceTickExtent;\n  };\n\n  IntervalScale.prototype.calcNiceExtent = function (opt) {\n    var extent = this._extent; // If extent start and end are same, expand them\n\n    if (extent[0] === extent[1]) {\n      if (extent[0] !== 0) {\n        // Expand extent\n        // Note that extents can be both negative. See #13154\n        var expandSize = Math.abs(extent[0]); // In the fowllowing case\n        //      Axis has been fixed max 100\n        //      Plus data are all 100 and axis extent are [100, 100].\n        // Extend to the both side will cause expanded max is larger than fixed max.\n        // So only expand to the smaller side.\n\n        if (!opt.fixMax) {\n          extent[1] += expandSize / 2;\n          extent[0] -= expandSize / 2;\n        } else {\n          extent[0] -= expandSize / 2;\n        }\n      } else {\n        extent[1] = 1;\n      }\n    }\n\n    var span = extent[1] - extent[0]; // If there are no data and extent are [Infinity, -Infinity]\n\n    if (!isFinite(span)) {\n      extent[0] = 0;\n      extent[1] = 1;\n    }\n\n    this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // let extent = this._extent;\n\n    var interval = this._interval;\n\n    if (!opt.fixMin) {\n      extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);\n    }\n\n    if (!opt.fixMax) {\n      extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);\n    }\n  };\n\n  IntervalScale.prototype.setNiceExtent = function (min, max) {\n    this._niceExtent = [min, max];\n  };\n\n  IntervalScale.type = 'interval';\n  return IntervalScale;\n}(Scale);\n\nScale.registerClass(IntervalScale);\n\n/* global Float32Array */\n\nvar supportFloat32Array = typeof Float32Array !== 'undefined';\nvar Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array;\nfunction createFloat32Array(arg) {\n  if (isArray(arg)) {\n    // Return self directly if don't support TypedArray.\n    return supportFloat32Array ? new Float32Array(arg) : arg;\n  } // Else is number\n\n\n  return new Float32ArrayCtor(arg);\n}\n\nvar STACK_PREFIX = '__ec_stack_';\n\nfunction getSeriesStackId(seriesModel) {\n  return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;\n}\n\nfunction getAxisKey(axis) {\n  return axis.dim + axis.index;\n}\n/**\n * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined.\n */\n\n\nfunction getLayoutOnAxis(opt) {\n  var params = [];\n  var baseAxis = opt.axis;\n  var axisKey = 'axis0';\n\n  if (baseAxis.type !== 'category') {\n    return;\n  }\n\n  var bandWidth = baseAxis.getBandWidth();\n\n  for (var i = 0; i < opt.count || 0; i++) {\n    params.push(defaults({\n      bandWidth: bandWidth,\n      axisKey: axisKey,\n      stackId: STACK_PREFIX + i\n    }, opt));\n  }\n\n  var widthAndOffsets = doCalBarWidthAndOffset(params);\n  var result = [];\n\n  for (var i = 0; i < opt.count; i++) {\n    var item = widthAndOffsets[axisKey][STACK_PREFIX + i];\n    item.offsetCenter = item.offset + item.width / 2;\n    result.push(item);\n  }\n\n  return result;\n}\nfunction prepareLayoutBarSeries(seriesType, ecModel) {\n  var seriesModels = [];\n  ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n    // Check series coordinate, do layout for cartesian2d only\n    if (isOnCartesian(seriesModel)) {\n      seriesModels.push(seriesModel);\n    }\n  });\n  return seriesModels;\n}\n/**\n * Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent\n * values.\n * This works for time axes, value axes, and log axes.\n * For a single time axis, return value is in the form like\n * {'x_0': [1000000]}.\n * The value of 1000000 is in milliseconds.\n */\n\nfunction getValueAxesMinGaps(barSeries) {\n  /**\n   * Map from axis.index to values.\n   * For a single time axis, axisValues is in the form like\n   * {'x_0': [1495555200000, 1495641600000, 1495728000000]}.\n   * Items in axisValues[x], e.g. 1495555200000, are time values of all\n   * series.\n   */\n  var axisValues = {};\n  each(barSeries, function (seriesModel) {\n    var cartesian = seriesModel.coordinateSystem;\n    var baseAxis = cartesian.getBaseAxis();\n\n    if (baseAxis.type !== 'time' && baseAxis.type !== 'value') {\n      return;\n    }\n\n    var data = seriesModel.getData();\n    var key = baseAxis.dim + '_' + baseAxis.index;\n    var dimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n    var store = data.getStore();\n\n    for (var i = 0, cnt = store.count(); i < cnt; ++i) {\n      var value = store.get(dimIdx, i);\n\n      if (!axisValues[key]) {\n        // No previous data for the axis\n        axisValues[key] = [value];\n      } else {\n        // No value in previous series\n        axisValues[key].push(value);\n      } // Ignore duplicated time values in the same axis\n\n    }\n  });\n  var axisMinGaps = {};\n\n  for (var key in axisValues) {\n    if (axisValues.hasOwnProperty(key)) {\n      var valuesInAxis = axisValues[key];\n\n      if (valuesInAxis) {\n        // Sort axis values into ascending order to calculate gaps\n        valuesInAxis.sort(function (a, b) {\n          return a - b;\n        });\n        var min = null;\n\n        for (var j = 1; j < valuesInAxis.length; ++j) {\n          var delta = valuesInAxis[j] - valuesInAxis[j - 1];\n\n          if (delta > 0) {\n            // Ignore 0 delta because they are of the same axis value\n            min = min === null ? delta : Math.min(min, delta);\n          }\n        } // Set to null if only have one data\n\n\n        axisMinGaps[key] = min;\n      }\n    }\n  }\n\n  return axisMinGaps;\n}\n\nfunction makeColumnLayout(barSeries) {\n  var axisMinGaps = getValueAxesMinGaps(barSeries);\n  var seriesInfoList = [];\n  each(barSeries, function (seriesModel) {\n    var cartesian = seriesModel.coordinateSystem;\n    var baseAxis = cartesian.getBaseAxis();\n    var axisExtent = baseAxis.getExtent();\n    var bandWidth;\n\n    if (baseAxis.type === 'category') {\n      bandWidth = baseAxis.getBandWidth();\n    } else if (baseAxis.type === 'value' || baseAxis.type === 'time') {\n      var key = baseAxis.dim + '_' + baseAxis.index;\n      var minGap = axisMinGaps[key];\n      var extentSpan = Math.abs(axisExtent[1] - axisExtent[0]);\n      var scale = baseAxis.scale.getExtent();\n      var scaleSpan = Math.abs(scale[1] - scale[0]);\n      bandWidth = minGap ? extentSpan / scaleSpan * minGap : extentSpan; // When there is only one data value\n    } else {\n      var data = seriesModel.getData();\n      bandWidth = Math.abs(axisExtent[1] - axisExtent[0]) / data.count();\n    }\n\n    var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth);\n    var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth);\n    var barMinWidth = parsePercent$1( // barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis,\n    // the auto-calculated bar width might be less than 0.5 / 1.\n    seriesModel.get('barMinWidth') || (isInLargeMode(seriesModel) ? 0.5 : 1), bandWidth);\n    var barGap = seriesModel.get('barGap');\n    var barCategoryGap = seriesModel.get('barCategoryGap');\n    seriesInfoList.push({\n      bandWidth: bandWidth,\n      barWidth: barWidth,\n      barMaxWidth: barMaxWidth,\n      barMinWidth: barMinWidth,\n      barGap: barGap,\n      barCategoryGap: barCategoryGap,\n      axisKey: getAxisKey(baseAxis),\n      stackId: getSeriesStackId(seriesModel)\n    });\n  });\n  return doCalBarWidthAndOffset(seriesInfoList);\n}\n\nfunction doCalBarWidthAndOffset(seriesInfoList) {\n  // Columns info on each category axis. Key is cartesian name\n  var columnsMap = {};\n  each(seriesInfoList, function (seriesInfo, idx) {\n    var axisKey = seriesInfo.axisKey;\n    var bandWidth = seriesInfo.bandWidth;\n    var columnsOnAxis = columnsMap[axisKey] || {\n      bandWidth: bandWidth,\n      remainedWidth: bandWidth,\n      autoWidthCount: 0,\n      categoryGap: null,\n      gap: '20%',\n      stacks: {}\n    };\n    var stacks = columnsOnAxis.stacks;\n    columnsMap[axisKey] = columnsOnAxis;\n    var stackId = seriesInfo.stackId;\n\n    if (!stacks[stackId]) {\n      columnsOnAxis.autoWidthCount++;\n    }\n\n    stacks[stackId] = stacks[stackId] || {\n      width: 0,\n      maxWidth: 0\n    }; // Caution: In a single coordinate system, these barGrid attributes\n    // will be shared by series. Consider that they have default values,\n    // only the attributes set on the last series will work.\n    // Do not change this fact unless there will be a break change.\n\n    var barWidth = seriesInfo.barWidth;\n\n    if (barWidth && !stacks[stackId].width) {\n      // See #6312, do not restrict width.\n      stacks[stackId].width = barWidth;\n      barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);\n      columnsOnAxis.remainedWidth -= barWidth;\n    }\n\n    var barMaxWidth = seriesInfo.barMaxWidth;\n    barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);\n    var barMinWidth = seriesInfo.barMinWidth;\n    barMinWidth && (stacks[stackId].minWidth = barMinWidth);\n    var barGap = seriesInfo.barGap;\n    barGap != null && (columnsOnAxis.gap = barGap);\n    var barCategoryGap = seriesInfo.barCategoryGap;\n    barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap);\n  });\n  var result = {};\n  each(columnsMap, function (columnsOnAxis, coordSysName) {\n    result[coordSysName] = {};\n    var stacks = columnsOnAxis.stacks;\n    var bandWidth = columnsOnAxis.bandWidth;\n    var categoryGapPercent = columnsOnAxis.categoryGap;\n\n    if (categoryGapPercent == null) {\n      var columnCount = keys(stacks).length; // More columns in one group\n      // the spaces between group is smaller. Or the column will be too thin.\n\n      categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%';\n    }\n\n    var categoryGap = parsePercent$1(categoryGapPercent, bandWidth);\n    var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);\n    var remainedWidth = columnsOnAxis.remainedWidth;\n    var autoWidthCount = columnsOnAxis.autoWidthCount;\n    var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n    autoWidth = Math.max(autoWidth, 0); // Find if any auto calculated bar exceeded maxBarWidth\n\n    each(stacks, function (column) {\n      var maxWidth = column.maxWidth;\n      var minWidth = column.minWidth;\n\n      if (!column.width) {\n        var finalWidth = autoWidth;\n\n        if (maxWidth && maxWidth < finalWidth) {\n          finalWidth = Math.min(maxWidth, remainedWidth);\n        } // `minWidth` has higher priority. `minWidth` decide that whether the\n        // bar is able to be visible. So `minWidth` should not be restricted\n        // by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In\n        // the extreme cases for `value` axis, bars are allowed to overlap\n        // with each other if `minWidth` specified.\n\n\n        if (minWidth && minWidth > finalWidth) {\n          finalWidth = minWidth;\n        }\n\n        if (finalWidth !== autoWidth) {\n          column.width = finalWidth;\n          remainedWidth -= finalWidth + barGapPercent * finalWidth;\n          autoWidthCount--;\n        }\n      } else {\n        // `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as\n        // CSS does. Because barWidth can be a percent value, where\n        // `barMaxWidth` can be used to restrict the final width.\n        var finalWidth = column.width;\n\n        if (maxWidth) {\n          finalWidth = Math.min(finalWidth, maxWidth);\n        } // `minWidth` has higher priority, as described above\n\n\n        if (minWidth) {\n          finalWidth = Math.max(finalWidth, minWidth);\n        }\n\n        column.width = finalWidth;\n        remainedWidth -= finalWidth + barGapPercent * finalWidth;\n        autoWidthCount--;\n      }\n    }); // Recalculate width again\n\n    autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n    autoWidth = Math.max(autoWidth, 0);\n    var widthSum = 0;\n    var lastColumn;\n    each(stacks, function (column, idx) {\n      if (!column.width) {\n        column.width = autoWidth;\n      }\n\n      lastColumn = column;\n      widthSum += column.width * (1 + barGapPercent);\n    });\n\n    if (lastColumn) {\n      widthSum -= lastColumn.width * barGapPercent;\n    }\n\n    var offset = -widthSum / 2;\n    each(stacks, function (column, stackId) {\n      result[coordSysName][stackId] = result[coordSysName][stackId] || {\n        bandWidth: bandWidth,\n        offset: offset,\n        width: column.width\n      };\n      offset += column.width * (1 + barGapPercent);\n    });\n  });\n  return result;\n}\n\nfunction retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) {\n  if (barWidthAndOffset && axis) {\n    var result = barWidthAndOffset[getAxisKey(axis)];\n\n    if (result != null && seriesModel != null) {\n      return result[getSeriesStackId(seriesModel)];\n    }\n\n    return result;\n  }\n}\nfunction layout(seriesType, ecModel) {\n  var seriesModels = prepareLayoutBarSeries(seriesType, ecModel);\n  var barWidthAndOffset = makeColumnLayout(seriesModels);\n  each(seriesModels, function (seriesModel) {\n    var data = seriesModel.getData();\n    var cartesian = seriesModel.coordinateSystem;\n    var baseAxis = cartesian.getBaseAxis();\n    var stackId = getSeriesStackId(seriesModel);\n    var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];\n    var columnOffset = columnLayoutInfo.offset;\n    var columnWidth = columnLayoutInfo.width;\n    data.setLayout({\n      bandWidth: columnLayoutInfo.bandWidth,\n      offset: columnOffset,\n      size: columnWidth\n    });\n  });\n} // TODO: Do not support stack in large mode yet.\n\nfunction createProgressiveLayout(seriesType) {\n  return {\n    seriesType: seriesType,\n    plan: createRenderPlanner(),\n    reset: function (seriesModel) {\n      if (!isOnCartesian(seriesModel)) {\n        return;\n      }\n\n      var data = seriesModel.getData();\n      var cartesian = seriesModel.coordinateSystem;\n      var baseAxis = cartesian.getBaseAxis();\n      var valueAxis = cartesian.getOtherAxis(baseAxis);\n      var valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim));\n      var baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n      var drawBackground = seriesModel.get('showBackground', true);\n      var valueDim = data.mapDimension(valueAxis.dim);\n      var stackResultDim = data.getCalculationInfo('stackResultDimension');\n      var stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries');\n      var isValueAxisH = valueAxis.isHorizontal();\n      var valueAxisStart = getValueAxisStart(baseAxis, valueAxis);\n      var isLarge = isInLargeMode(seriesModel);\n      var barMinHeight = seriesModel.get('barMinHeight') || 0;\n      var stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim); // Layout info.\n\n      var columnWidth = data.getLayout('size');\n      var columnOffset = data.getLayout('offset');\n      return {\n        progress: function (params, data) {\n          var count = params.count;\n          var largePoints = isLarge && createFloat32Array(count * 3);\n          var largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3);\n          var largeDataIndices = isLarge && createFloat32Array(count);\n          var coordLayout = cartesian.master.getRect();\n          var bgSize = isValueAxisH ? coordLayout.width : coordLayout.height;\n          var dataIndex;\n          var store = data.getStore();\n          var idxOffset = 0;\n\n          while ((dataIndex = params.next()) != null) {\n            var value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex);\n            var baseValue = store.get(baseDimIdx, dataIndex);\n            var baseCoord = valueAxisStart;\n            var startValue = void 0; // Because of the barMinHeight, we can not use the value in\n            // stackResultDimension directly.\n\n            if (stacked) {\n              startValue = +value - store.get(valueDimIdx, dataIndex);\n            }\n\n            var x = void 0;\n            var y = void 0;\n            var width = void 0;\n            var height = void 0;\n\n            if (isValueAxisH) {\n              var coord = cartesian.dataToPoint([value, baseValue]);\n\n              if (stacked) {\n                var startCoord = cartesian.dataToPoint([startValue, baseValue]);\n                baseCoord = startCoord[0];\n              }\n\n              x = baseCoord;\n              y = coord[1] + columnOffset;\n              width = coord[0] - baseCoord;\n              height = columnWidth;\n\n              if (Math.abs(width) < barMinHeight) {\n                width = (width < 0 ? -1 : 1) * barMinHeight;\n              }\n            } else {\n              var coord = cartesian.dataToPoint([baseValue, value]);\n\n              if (stacked) {\n                var startCoord = cartesian.dataToPoint([baseValue, startValue]);\n                baseCoord = startCoord[1];\n              }\n\n              x = coord[0] + columnOffset;\n              y = baseCoord;\n              width = columnWidth;\n              height = coord[1] - baseCoord;\n\n              if (Math.abs(height) < barMinHeight) {\n                // Include zero to has a positive bar\n                height = (height <= 0 ? -1 : 1) * barMinHeight;\n              }\n            }\n\n            if (!isLarge) {\n              data.setItemLayout(dataIndex, {\n                x: x,\n                y: y,\n                width: width,\n                height: height\n              });\n            } else {\n              largePoints[idxOffset] = x;\n              largePoints[idxOffset + 1] = y;\n              largePoints[idxOffset + 2] = isValueAxisH ? width : height;\n\n              if (largeBackgroundPoints) {\n                largeBackgroundPoints[idxOffset] = isValueAxisH ? coordLayout.x : x;\n                largeBackgroundPoints[idxOffset + 1] = isValueAxisH ? y : coordLayout.y;\n                largeBackgroundPoints[idxOffset + 2] = bgSize;\n              }\n\n              largeDataIndices[dataIndex] = dataIndex;\n            }\n\n            idxOffset += 3;\n          }\n\n          if (isLarge) {\n            data.setLayout({\n              largePoints: largePoints,\n              largeDataIndices: largeDataIndices,\n              largeBackgroundPoints: largeBackgroundPoints,\n              valueAxisHorizontal: isValueAxisH\n            });\n          }\n        }\n      };\n    }\n  };\n}\n\nfunction isOnCartesian(seriesModel) {\n  return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d';\n}\n\nfunction isInLargeMode(seriesModel) {\n  return seriesModel.pipelineContext && seriesModel.pipelineContext.large;\n} // See cases in `test/bar-start.html` and `#7412`, `#8747`.\n\n\nfunction getValueAxisStart(baseAxis, valueAxis) {\n  return valueAxis.toGlobalCoord(valueAxis.dataToCoord(valueAxis.type === 'log' ? 1 : 0));\n}\n\nvar bisect = function (a, x, lo, hi) {\n  while (lo < hi) {\n    var mid = lo + hi >>> 1;\n\n    if (a[mid][1] < x) {\n      lo = mid + 1;\n    } else {\n      hi = mid;\n    }\n  }\n\n  return lo;\n};\n\nvar TimeScale =\n/** @class */\nfunction (_super) {\n  __extends(TimeScale, _super);\n\n  function TimeScale(settings) {\n    var _this = _super.call(this, settings) || this;\n\n    _this.type = 'time';\n    return _this;\n  }\n  /**\n   * Get label is mainly for other components like dataZoom, tooltip.\n   */\n\n\n  TimeScale.prototype.getLabel = function (tick) {\n    var useUTC = this.getSetting('useUTC');\n    return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale'));\n  };\n\n  TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) {\n    var isUTC = this.getSetting('useUTC');\n    var lang = this.getSetting('locale');\n    return leveledFormat(tick, idx, labelFormatter, lang, isUTC);\n  };\n  /**\n   * @override\n   */\n\n\n  TimeScale.prototype.getTicks = function () {\n    var interval = this._interval;\n    var extent = this._extent;\n    var ticks = []; // If interval is 0, return [];\n\n    if (!interval) {\n      return ticks;\n    }\n\n    ticks.push({\n      value: extent[0],\n      level: 0\n    });\n    var useUTC = this.getSetting('useUTC');\n    var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent);\n    ticks = ticks.concat(innerTicks);\n    ticks.push({\n      value: extent[1],\n      level: 0\n    });\n    return ticks;\n  };\n\n  TimeScale.prototype.calcNiceExtent = function (opt) {\n    var extent = this._extent; // If extent start and end are same, expand them\n\n    if (extent[0] === extent[1]) {\n      // Expand extent\n      extent[0] -= ONE_DAY;\n      extent[1] += ONE_DAY;\n    } // If there are no data and extent are [Infinity, -Infinity]\n\n\n    if (extent[1] === -Infinity && extent[0] === Infinity) {\n      var d = new Date();\n      extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());\n      extent[0] = extent[1] - ONE_DAY;\n    }\n\n    this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);\n  };\n\n  TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) {\n    approxTickNum = approxTickNum || 10;\n    var extent = this._extent;\n    var span = extent[1] - extent[0];\n    this._approxInterval = span / approxTickNum;\n\n    if (minInterval != null && this._approxInterval < minInterval) {\n      this._approxInterval = minInterval;\n    }\n\n    if (maxInterval != null && this._approxInterval > maxInterval) {\n      this._approxInterval = maxInterval;\n    }\n\n    var scaleIntervalsLen = scaleIntervals.length;\n    var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1); // Interval that can be used to calculate ticks\n\n    this._interval = scaleIntervals[idx][1]; // Min level used when picking ticks from top down.\n    // We check one more level to avoid the ticks are to sparse in some case.\n\n    this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0];\n  };\n\n  TimeScale.prototype.parse = function (val) {\n    // val might be float.\n    return isNumber(val) ? val : +parseDate(val);\n  };\n\n  TimeScale.prototype.contain = function (val) {\n    return contain$1(this.parse(val), this._extent);\n  };\n\n  TimeScale.prototype.normalize = function (val) {\n    return normalize$1(this.parse(val), this._extent);\n  };\n\n  TimeScale.prototype.scale = function (val) {\n    return scale$2(val, this._extent);\n  };\n\n  TimeScale.type = 'time';\n  return TimeScale;\n}(IntervalScale);\n/**\n * This implementation was originally copied from \"d3.js\"\n * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n */\n\n\nvar scaleIntervals = [// Format                           interval\n['second', ONE_SECOND], ['minute', ONE_MINUTE], ['hour', ONE_HOUR], ['quarter-day', ONE_HOUR * 6], ['half-day', ONE_HOUR * 12], ['day', ONE_DAY * 1.2], ['half-week', ONE_DAY * 3.5], ['week', ONE_DAY * 7], ['month', ONE_DAY * 31], ['quarter', ONE_DAY * 95], ['half-year', ONE_YEAR / 2], ['year', ONE_YEAR] // 1Y\n];\n\nfunction isUnitValueSame(unit, valueA, valueB, isUTC) {\n  var dateA = parseDate(valueA);\n  var dateB = parseDate(valueB);\n\n  var isSame = function (unit) {\n    return getUnitValue(dateA, unit, isUTC) === getUnitValue(dateB, unit, isUTC);\n  };\n\n  var isSameYear = function () {\n    return isSame('year');\n  }; // const isSameHalfYear = () => isSameYear() && isSame('half-year');\n  // const isSameQuater = () => isSameYear() && isSame('quarter');\n\n\n  var isSameMonth = function () {\n    return isSameYear() && isSame('month');\n  };\n\n  var isSameDay = function () {\n    return isSameMonth() && isSame('day');\n  }; // const isSameHalfDay = () => isSameDay() && isSame('half-day');\n\n\n  var isSameHour = function () {\n    return isSameDay() && isSame('hour');\n  };\n\n  var isSameMinute = function () {\n    return isSameHour() && isSame('minute');\n  };\n\n  var isSameSecond = function () {\n    return isSameMinute() && isSame('second');\n  };\n\n  var isSameMilliSecond = function () {\n    return isSameSecond() && isSame('millisecond');\n  };\n\n  switch (unit) {\n    case 'year':\n      return isSameYear();\n\n    case 'month':\n      return isSameMonth();\n\n    case 'day':\n      return isSameDay();\n\n    case 'hour':\n      return isSameHour();\n\n    case 'minute':\n      return isSameMinute();\n\n    case 'second':\n      return isSameSecond();\n\n    case 'millisecond':\n      return isSameMilliSecond();\n  }\n} // const primaryUnitGetters = {\n//     year: fullYearGetterName(),\n//     month: monthGetterName(),\n//     day: dateGetterName(),\n//     hour: hoursGetterName(),\n//     minute: minutesGetterName(),\n//     second: secondsGetterName(),\n//     millisecond: millisecondsGetterName()\n// };\n// const primaryUnitUTCGetters = {\n//     year: fullYearGetterName(true),\n//     month: monthGetterName(true),\n//     day: dateGetterName(true),\n//     hour: hoursGetterName(true),\n//     minute: minutesGetterName(true),\n//     second: secondsGetterName(true),\n//     millisecond: millisecondsGetterName(true)\n// };\n// function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) {\n//     step = step || 1;\n//     switch (getPrimaryTimeUnit(unitName)) {\n//         case 'year':\n//             date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step);\n//             break;\n//         case 'month':\n//             date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step);\n//             break;\n//         case 'day':\n//             date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step);\n//             break;\n//         case 'hour':\n//             date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step);\n//             break;\n//         case 'minute':\n//             date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step);\n//             break;\n//         case 'second':\n//             date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step);\n//             break;\n//         case 'millisecond':\n//             date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step);\n//             break;\n//     }\n//     return date.getTime();\n// }\n// const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]];\n// const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]];\n// const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]];\n\n\nfunction getDateInterval(approxInterval, daysInMonth) {\n  approxInterval /= ONE_DAY;\n  return approxInterval > 16 ? 16 // Math.floor(daysInMonth / 2) + 1  // In this case we only want one tick between two months.\n  : approxInterval > 7.5 ? 7 // TODO week 7 or day 8?\n  : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1;\n}\n\nfunction getMonthInterval(approxInterval) {\n  var APPROX_ONE_MONTH = 30 * ONE_DAY;\n  approxInterval /= APPROX_ONE_MONTH;\n  return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1;\n}\n\nfunction getHourInterval(approxInterval) {\n  approxInterval /= ONE_HOUR;\n  return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1;\n}\n\nfunction getMinutesAndSecondsInterval(approxInterval, isMinutes) {\n  approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND;\n  return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1;\n}\n\nfunction getMillisecondsInterval(approxInterval) {\n  return nice(approxInterval, true);\n}\n\nfunction getFirstTimestampOfUnit(date, unitName, isUTC) {\n  var outDate = new Date(date);\n\n  switch (getPrimaryTimeUnit(unitName)) {\n    case 'year':\n    case 'month':\n      outDate[monthSetterName(isUTC)](0);\n\n    case 'day':\n      outDate[dateSetterName(isUTC)](1);\n\n    case 'hour':\n      outDate[hoursSetterName(isUTC)](0);\n\n    case 'minute':\n      outDate[minutesSetterName(isUTC)](0);\n\n    case 'second':\n      outDate[secondsSetterName(isUTC)](0);\n      outDate[millisecondsSetterName(isUTC)](0);\n  }\n\n  return outDate.getTime();\n}\n\nfunction getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent) {\n  var safeLimit = 10000;\n  var unitNames = timeUnits;\n  var iter = 0;\n\n  function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) {\n    var date = new Date(minTimestamp);\n    var dateTime = minTimestamp;\n    var d = date[getMethodName](); // if (isDate) {\n    //     d -= 1; // Starts with 0;   PENDING\n    // }\n\n    while (dateTime < maxTimestamp && dateTime <= extent[1]) {\n      out.push({\n        value: dateTime\n      });\n      d += interval;\n      date[setMethodName](d);\n      dateTime = date.getTime();\n    } // This extra tick is for calcuating ticks of next level. Will not been added to the final result\n\n\n    out.push({\n      value: dateTime,\n      notAdd: true\n    });\n  }\n\n  function addLevelTicks(unitName, lastLevelTicks, levelTicks) {\n    var newAddedTicks = [];\n    var isFirstLevel = !lastLevelTicks.length;\n\n    if (isUnitValueSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) {\n      return;\n    }\n\n    if (isFirstLevel) {\n      lastLevelTicks = [{\n        // TODO Optimize. Not include so may ticks.\n        value: getFirstTimestampOfUnit(new Date(extent[0]), unitName, isUTC)\n      }, {\n        value: extent[1]\n      }];\n    }\n\n    for (var i = 0; i < lastLevelTicks.length - 1; i++) {\n      var startTick = lastLevelTicks[i].value;\n      var endTick = lastLevelTicks[i + 1].value;\n\n      if (startTick === endTick) {\n        continue;\n      }\n\n      var interval = void 0;\n      var getterName = void 0;\n      var setterName = void 0;\n      var isDate = false;\n\n      switch (unitName) {\n        case 'year':\n          interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365));\n          getterName = fullYearGetterName(isUTC);\n          setterName = fullYearSetterName(isUTC);\n          break;\n\n        case 'half-year':\n        case 'quarter':\n        case 'month':\n          interval = getMonthInterval(approxInterval);\n          getterName = monthGetterName(isUTC);\n          setterName = monthSetterName(isUTC);\n          break;\n\n        case 'week': // PENDING If week is added. Ignore day.\n\n        case 'half-week':\n        case 'day':\n          interval = getDateInterval(approxInterval); // Use 32 days and let interval been 16\n\n          getterName = dateGetterName(isUTC);\n          setterName = dateSetterName(isUTC);\n          isDate = true;\n          break;\n\n        case 'half-day':\n        case 'quarter-day':\n        case 'hour':\n          interval = getHourInterval(approxInterval);\n          getterName = hoursGetterName(isUTC);\n          setterName = hoursSetterName(isUTC);\n          break;\n\n        case 'minute':\n          interval = getMinutesAndSecondsInterval(approxInterval, true);\n          getterName = minutesGetterName(isUTC);\n          setterName = minutesSetterName(isUTC);\n          break;\n\n        case 'second':\n          interval = getMinutesAndSecondsInterval(approxInterval, false);\n          getterName = secondsGetterName(isUTC);\n          setterName = secondsSetterName(isUTC);\n          break;\n\n        case 'millisecond':\n          interval = getMillisecondsInterval(approxInterval);\n          getterName = millisecondsGetterName(isUTC);\n          setterName = millisecondsSetterName(isUTC);\n          break;\n      }\n\n      addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks);\n\n      if (unitName === 'year' && levelTicks.length > 1 && i === 0) {\n        // Add nearest years to the left extent.\n        levelTicks.unshift({\n          value: levelTicks[0].value - interval\n        });\n      }\n    }\n\n    for (var i = 0; i < newAddedTicks.length; i++) {\n      levelTicks.push(newAddedTicks[i]);\n    } // newAddedTicks.length && console.log(unitName, newAddedTicks);\n\n\n    return newAddedTicks;\n  }\n\n  var levelsTicks = [];\n  var currentLevelTicks = [];\n  var tickCount = 0;\n  var lastLevelTickCount = 0;\n\n  for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) {\n    var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]);\n\n    if (!isPrimaryTimeUnit(unitNames[i])) {\n      // TODO\n      continue;\n    }\n\n    addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks);\n    var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null;\n\n    if (primaryTimeUnit !== nextPrimaryTimeUnit) {\n      if (currentLevelTicks.length) {\n        lastLevelTickCount = tickCount; // Remove the duplicate so the tick count can be precisely.\n\n        currentLevelTicks.sort(function (a, b) {\n          return a.value - b.value;\n        });\n        var levelTicksRemoveDuplicated = [];\n\n        for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) {\n          var tickValue = currentLevelTicks[i_1].value;\n\n          if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) {\n            levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]);\n\n            if (tickValue >= extent[0] && tickValue <= extent[1]) {\n              tickCount++;\n            }\n          }\n        }\n\n        var targetTickNum = (extent[1] - extent[0]) / approxInterval; // Added too much in this level and not too less in last level\n\n        if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) {\n          break;\n        } // Only treat primary time unit as one level.\n\n\n        levelsTicks.push(levelTicksRemoveDuplicated);\n\n        if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) {\n          break;\n        }\n      } // Reset if next unitName is primary\n\n\n      currentLevelTicks = [];\n    }\n  }\n\n  if (\"development\" !== 'production') {\n    if (iter >= safeLimit) {\n      warn('Exceed safe limit.');\n    }\n  }\n\n  var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) {\n    return filter(levelTicks, function (tick) {\n      return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd;\n    });\n  }), function (levelTicks) {\n    return levelTicks.length > 0;\n  });\n  var ticks = [];\n  var maxLevel = levelsTicksInExtent.length - 1;\n\n  for (var i = 0; i < levelsTicksInExtent.length; ++i) {\n    var levelTicks = levelsTicksInExtent[i];\n\n    for (var k = 0; k < levelTicks.length; ++k) {\n      ticks.push({\n        value: levelTicks[k].value,\n        level: maxLevel - i\n      });\n    }\n  }\n\n  ticks.sort(function (a, b) {\n    return a.value - b.value;\n  }); // Remove duplicates\n\n  var result = [];\n\n  for (var i = 0; i < ticks.length; ++i) {\n    if (i === 0 || ticks[i].value !== ticks[i - 1].value) {\n      result.push(ticks[i]);\n    }\n  }\n\n  return result;\n}\n\nScale.registerClass(TimeScale);\n\nvar scaleProto = Scale.prototype; // FIXME:TS refactor: not good to call it directly with `this`?\n\nvar intervalScaleProto = IntervalScale.prototype;\nvar roundingErrorFix = round;\nvar mathFloor = Math.floor;\nvar mathCeil = Math.ceil;\nvar mathPow$1 = Math.pow;\nvar mathLog = Math.log;\n\nvar LogScale =\n/** @class */\nfunction (_super) {\n  __extends(LogScale, _super);\n\n  function LogScale() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = 'log';\n    _this.base = 10;\n    _this._originalScale = new IntervalScale(); // FIXME:TS actually used by `IntervalScale`\n\n    _this._interval = 0;\n    return _this;\n  }\n  /**\n   * @param Whether expand the ticks to niced extent.\n   */\n\n\n  LogScale.prototype.getTicks = function (expandToNicedExtent) {\n    var originalScale = this._originalScale;\n    var extent = this._extent;\n    var originalExtent = originalScale.getExtent();\n    var ticks = intervalScaleProto.getTicks.call(this, expandToNicedExtent);\n    return map(ticks, function (tick) {\n      var val = tick.value;\n      var powVal = round(mathPow$1(this.base, val)); // Fix #4158\n\n      powVal = val === extent[0] && this._fixMin ? fixRoundingError(powVal, originalExtent[0]) : powVal;\n      powVal = val === extent[1] && this._fixMax ? fixRoundingError(powVal, originalExtent[1]) : powVal;\n      return {\n        value: powVal\n      };\n    }, this);\n  };\n\n  LogScale.prototype.setExtent = function (start, end) {\n    var base = mathLog(this.base); // log(-Infinity) is NaN, so safe guard here\n\n    start = mathLog(Math.max(0, start)) / base;\n    end = mathLog(Math.max(0, end)) / base;\n    intervalScaleProto.setExtent.call(this, start, end);\n  };\n  /**\n   * @return {number} end\n   */\n\n\n  LogScale.prototype.getExtent = function () {\n    var base = this.base;\n    var extent = scaleProto.getExtent.call(this);\n    extent[0] = mathPow$1(base, extent[0]);\n    extent[1] = mathPow$1(base, extent[1]); // Fix #4158\n\n    var originalScale = this._originalScale;\n    var originalExtent = originalScale.getExtent();\n    this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));\n    this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));\n    return extent;\n  };\n\n  LogScale.prototype.unionExtent = function (extent) {\n    this._originalScale.unionExtent(extent);\n\n    var base = this.base;\n    extent[0] = mathLog(extent[0]) / mathLog(base);\n    extent[1] = mathLog(extent[1]) / mathLog(base);\n    scaleProto.unionExtent.call(this, extent);\n  };\n\n  LogScale.prototype.unionExtentFromData = function (data, dim) {\n    // TODO\n    // filter value that <= 0\n    this.unionExtent(data.getApproximateExtent(dim));\n  };\n  /**\n   * Update interval and extent of intervals for nice ticks\n   * @param approxTickNum default 10 Given approx tick number\n   */\n\n\n  LogScale.prototype.calcNiceTicks = function (approxTickNum) {\n    approxTickNum = approxTickNum || 10;\n    var extent = this._extent;\n    var span = extent[1] - extent[0];\n\n    if (span === Infinity || span <= 0) {\n      return;\n    }\n\n    var interval = quantity(span);\n    var err = approxTickNum / span * interval; // Filter ticks to get closer to the desired count.\n\n    if (err <= 0.5) {\n      interval *= 10;\n    } // Interval should be integer\n\n\n    while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {\n      interval *= 10;\n    }\n\n    var niceExtent = [round(mathCeil(extent[0] / interval) * interval), round(mathFloor(extent[1] / interval) * interval)];\n    this._interval = interval;\n    this._niceExtent = niceExtent;\n  };\n\n  LogScale.prototype.calcNiceExtent = function (opt) {\n    intervalScaleProto.calcNiceExtent.call(this, opt);\n    this._fixMin = opt.fixMin;\n    this._fixMax = opt.fixMax;\n  };\n\n  LogScale.prototype.parse = function (val) {\n    return val;\n  };\n\n  LogScale.prototype.contain = function (val) {\n    val = mathLog(val) / mathLog(this.base);\n    return contain$1(val, this._extent);\n  };\n\n  LogScale.prototype.normalize = function (val) {\n    val = mathLog(val) / mathLog(this.base);\n    return normalize$1(val, this._extent);\n  };\n\n  LogScale.prototype.scale = function (val) {\n    val = scale$2(val, this._extent);\n    return mathPow$1(this.base, val);\n  };\n\n  LogScale.type = 'log';\n  return LogScale;\n}(Scale);\n\nvar proto = LogScale.prototype;\nproto.getMinorTicks = intervalScaleProto.getMinorTicks;\nproto.getLabel = intervalScaleProto.getLabel;\n\nfunction fixRoundingError(val, originalVal) {\n  return roundingErrorFix(val, getPrecision(originalVal));\n}\n\nScale.registerClass(LogScale);\n\nvar ScaleRawExtentInfo =\n/** @class */\nfunction () {\n  function ScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\n  originalExtent) {\n    this._prepareParams(scale, model, originalExtent);\n  }\n  /**\n   * Parameters depending on outside (like model, user callback)\n   * are prepared and fixed here.\n   */\n\n\n  ScaleRawExtentInfo.prototype._prepareParams = function (scale, model, // Usually: data extent from all series on this axis.\n  dataExtent) {\n    if (dataExtent[1] < dataExtent[0]) {\n      dataExtent = [NaN, NaN];\n    }\n\n    this._dataMin = dataExtent[0];\n    this._dataMax = dataExtent[1];\n    var isOrdinal = this._isOrdinal = scale.type === 'ordinal';\n    this._needCrossZero = scale.type === 'interval' && model.getNeedCrossZero && model.getNeedCrossZero();\n    var modelMinRaw = this._modelMinRaw = model.get('min', true);\n\n    if (isFunction(modelMinRaw)) {\n      // This callback always provides users the full data extent (before data is filtered).\n      this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw({\n        min: dataExtent[0],\n        max: dataExtent[1]\n      }));\n    } else if (modelMinRaw !== 'dataMin') {\n      this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw);\n    }\n\n    var modelMaxRaw = this._modelMaxRaw = model.get('max', true);\n\n    if (isFunction(modelMaxRaw)) {\n      // This callback always provides users the full data extent (before data is filtered).\n      this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw({\n        min: dataExtent[0],\n        max: dataExtent[1]\n      }));\n    } else if (modelMaxRaw !== 'dataMax') {\n      this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw);\n    }\n\n    if (isOrdinal) {\n      // FIXME: there is a flaw here: if there is no \"block\" data processor like `dataZoom`,\n      // and progressive rendering is using, here the category result might just only contain\n      // the processed chunk rather than the entire result.\n      this._axisDataLen = model.getCategories().length;\n    } else {\n      var boundaryGap = model.get('boundaryGap');\n      var boundaryGapArr = isArray(boundaryGap) ? boundaryGap : [boundaryGap || 0, boundaryGap || 0];\n\n      if (typeof boundaryGapArr[0] === 'boolean' || typeof boundaryGapArr[1] === 'boolean') {\n        if (\"development\" !== 'production') {\n          console.warn('Boolean type for boundaryGap is only ' + 'allowed for ordinal axis. Please use string in ' + 'percentage instead, e.g., \"20%\". Currently, ' + 'boundaryGap is set to be 0.');\n        }\n\n        this._boundaryGapInner = [0, 0];\n      } else {\n        this._boundaryGapInner = [parsePercent(boundaryGapArr[0], 1), parsePercent(boundaryGapArr[1], 1)];\n      }\n    }\n  };\n  /**\n   * Calculate extent by prepared parameters.\n   * This method has no external dependency and can be called duplicatedly,\n   * getting the same result.\n   * If parameters changed, should call this method to recalcuate.\n   */\n\n\n  ScaleRawExtentInfo.prototype.calculate = function () {\n    // Notice: When min/max is not set (that is, when there are null/undefined,\n    // which is the most common case), these cases should be ensured:\n    // (1) For 'ordinal', show all axis.data.\n    // (2) For others:\n    //      + `boundaryGap` is applied (if min/max set, boundaryGap is\n    //      disabled).\n    //      + If `needCrossZero`, min/max should be zero, otherwise, min/max should\n    //      be the result that originalExtent enlarged by boundaryGap.\n    // (3) If no data, it should be ensured that `scale.setBlank` is set.\n    var isOrdinal = this._isOrdinal;\n    var dataMin = this._dataMin;\n    var dataMax = this._dataMax;\n    var axisDataLen = this._axisDataLen;\n    var boundaryGapInner = this._boundaryGapInner;\n    var span = !isOrdinal ? dataMax - dataMin || Math.abs(dataMin) : null; // Currently if a `'value'` axis model min is specified as 'dataMin'/'dataMax',\n    // `boundaryGap` will not be used. It's the different from specifying as `null`/`undefined`.\n\n    var min = this._modelMinRaw === 'dataMin' ? dataMin : this._modelMinNum;\n    var max = this._modelMaxRaw === 'dataMax' ? dataMax : this._modelMaxNum; // If `_modelMinNum`/`_modelMaxNum` is `null`/`undefined`, should not be fixed.\n\n    var minFixed = min != null;\n    var maxFixed = max != null;\n\n    if (min == null) {\n      min = isOrdinal ? axisDataLen ? 0 : NaN : dataMin - boundaryGapInner[0] * span;\n    }\n\n    if (max == null) {\n      max = isOrdinal ? axisDataLen ? axisDataLen - 1 : NaN : dataMax + boundaryGapInner[1] * span;\n    }\n\n    (min == null || !isFinite(min)) && (min = NaN);\n    (max == null || !isFinite(max)) && (max = NaN);\n    var isBlank = eqNaN(min) || eqNaN(max) || isOrdinal && !axisDataLen; // If data extent modified, need to recalculated to ensure cross zero.\n\n    if (this._needCrossZero) {\n      // Axis is over zero and min is not set\n      if (min > 0 && max > 0 && !minFixed) {\n        min = 0; // minFixed = true;\n      } // Axis is under zero and max is not set\n\n\n      if (min < 0 && max < 0 && !maxFixed) {\n        max = 0; // maxFixed = true;\n      } // PENDING:\n      // When `needCrossZero` and all data is positive/negative, should it be ensured\n      // that the results processed by boundaryGap are positive/negative?\n      // If so, here `minFixed`/`maxFixed` need to be set.\n\n    }\n\n    var determinedMin = this._determinedMin;\n    var determinedMax = this._determinedMax;\n\n    if (determinedMin != null) {\n      min = determinedMin;\n      minFixed = true;\n    }\n\n    if (determinedMax != null) {\n      max = determinedMax;\n      maxFixed = true;\n    } // Ensure min/max be finite number or NaN here. (not to be null/undefined)\n    // `NaN` means min/max axis is blank.\n\n\n    return {\n      min: min,\n      max: max,\n      minFixed: minFixed,\n      maxFixed: maxFixed,\n      isBlank: isBlank\n    };\n  };\n\n  ScaleRawExtentInfo.prototype.modifyDataMinMax = function (minMaxName, val) {\n    if (\"development\" !== 'production') {\n      assert(!this.frozen);\n    }\n\n    this[DATA_MIN_MAX_ATTR[minMaxName]] = val;\n  };\n\n  ScaleRawExtentInfo.prototype.setDeterminedMinMax = function (minMaxName, val) {\n    var attr = DETERMINED_MIN_MAX_ATTR[minMaxName];\n\n    if (\"development\" !== 'production') {\n      assert(!this.frozen // Earse them usually means logic flaw.\n      && this[attr] == null);\n    }\n\n    this[attr] = val;\n  };\n\n  ScaleRawExtentInfo.prototype.freeze = function () {\n    // @ts-ignore\n    this.frozen = true;\n  };\n\n  return ScaleRawExtentInfo;\n}();\nvar DETERMINED_MIN_MAX_ATTR = {\n  min: '_determinedMin',\n  max: '_determinedMax'\n};\nvar DATA_MIN_MAX_ATTR = {\n  min: '_dataMin',\n  max: '_dataMax'\n};\n/**\n * Get scale min max and related info only depends on model settings.\n * This method can be called after coordinate system created.\n * For example, in data processing stage.\n *\n * Scale extent info probably be required multiple times during a workflow.\n * For example:\n * (1) `dataZoom` depends it to get the axis extent in \"100%\" state.\n * (2) `processor/extentCalculator` depends it to make sure whether axis extent is specified.\n * (3) `coordSys.update` use it to finally decide the scale extent.\n * But the callback of `min`/`max` should not be called multiple times.\n * The code below should not be implemented repeatedly either.\n * So we cache the result in the scale instance, which will be recreated at the beginning\n * of the workflow (because `scale` instance will be recreated each round of the workflow).\n */\n\nfunction ensureScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\noriginalExtent) {\n  // Do not permit to recreate.\n  var rawExtentInfo = scale.rawExtentInfo;\n\n  if (rawExtentInfo) {\n    return rawExtentInfo;\n  }\n\n  rawExtentInfo = new ScaleRawExtentInfo(scale, model, originalExtent); // @ts-ignore\n\n  scale.rawExtentInfo = rawExtentInfo;\n  return rawExtentInfo;\n}\nfunction parseAxisModelMinMax(scale, minMax) {\n  return minMax == null ? null : eqNaN(minMax) ? NaN : scale.parse(minMax);\n}\n\n/**\n * Get axis scale extent before niced.\n * Item of returned array can only be number (including Infinity and NaN).\n *\n * Caution:\n * Precondition of calling this method:\n * The scale extent has been initialized using series data extent via\n * `scale.setExtent` or `scale.unionExtentFromData`;\n */\n\nfunction getScaleExtent(scale, model) {\n  var scaleType = scale.type;\n  var rawExtentResult = ensureScaleRawExtentInfo(scale, model, scale.getExtent()).calculate();\n  scale.setBlank(rawExtentResult.isBlank);\n  var min = rawExtentResult.min;\n  var max = rawExtentResult.max; // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis\n  // is base axis\n  // FIXME\n  // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly.\n  // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent?\n  //     Should not depend on series type `bar`?\n  // (3) Fix that might overlap when using dataZoom.\n  // (4) Consider other chart types using `barGrid`?\n  // See #6728, #4862, `test/bar-overflow-time-plot.html`\n\n  var ecModel = model.ecModel;\n\n  if (ecModel && scaleType === 'time'\n  /* || scaleType === 'interval' */\n  ) {\n    var barSeriesModels = prepareLayoutBarSeries('bar', ecModel);\n    var isBaseAxisAndHasBarSeries_1 = false;\n    each(barSeriesModels, function (seriesModel) {\n      isBaseAxisAndHasBarSeries_1 = isBaseAxisAndHasBarSeries_1 || seriesModel.getBaseAxis() === model.axis;\n    });\n\n    if (isBaseAxisAndHasBarSeries_1) {\n      // Calculate placement of bars on axis. TODO should be decoupled\n      // with barLayout\n      var barWidthAndOffset = makeColumnLayout(barSeriesModels); // Adjust axis min and max to account for overflow\n\n      var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset);\n      min = adjustedScale.min;\n      max = adjustedScale.max;\n    }\n  }\n\n  return {\n    extent: [min, max],\n    // \"fix\" means \"fixed\", the value should not be\n    // changed in the subsequent steps.\n    fixMin: rawExtentResult.minFixed,\n    fixMax: rawExtentResult.maxFixed\n  };\n}\n\nfunction adjustScaleForOverflow(min, max, model, // Only support cartesian coord yet.\nbarWidthAndOffset) {\n  // Get Axis Length\n  var axisExtent = model.axis.getExtent();\n  var axisLength = axisExtent[1] - axisExtent[0]; // Get bars on current base axis and calculate min and max overflow\n\n  var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis);\n\n  if (barsOnCurrentAxis === undefined) {\n    return {\n      min: min,\n      max: max\n    };\n  }\n\n  var minOverflow = Infinity;\n  each(barsOnCurrentAxis, function (item) {\n    minOverflow = Math.min(item.offset, minOverflow);\n  });\n  var maxOverflow = -Infinity;\n  each(barsOnCurrentAxis, function (item) {\n    maxOverflow = Math.max(item.offset + item.width, maxOverflow);\n  });\n  minOverflow = Math.abs(minOverflow);\n  maxOverflow = Math.abs(maxOverflow);\n  var totalOverFlow = minOverflow + maxOverflow; // Calculate required buffer based on old range and overflow\n\n  var oldRange = max - min;\n  var oldRangePercentOfNew = 1 - (minOverflow + maxOverflow) / axisLength;\n  var overflowBuffer = oldRange / oldRangePercentOfNew - oldRange;\n  max += overflowBuffer * (maxOverflow / totalOverFlow);\n  min -= overflowBuffer * (minOverflow / totalOverFlow);\n  return {\n    min: min,\n    max: max\n  };\n} // Precondition of calling this method:\n// The scale extent has been initialized using series data extent via\n// `scale.setExtent` or `scale.unionExtentFromData`;\n\n\nfunction niceScaleExtent(scale, inModel) {\n  var model = inModel;\n  var extentInfo = getScaleExtent(scale, model);\n  var extent = extentInfo.extent;\n  var splitNumber = model.get('splitNumber');\n\n  if (scale instanceof LogScale) {\n    scale.base = model.get('logBase');\n  }\n\n  var scaleType = scale.type;\n  var interval = model.get('interval');\n  var isIntervalOrTime = scaleType === 'interval' || scaleType === 'time';\n  scale.setExtent(extent[0], extent[1]);\n  scale.calcNiceExtent({\n    splitNumber: splitNumber,\n    fixMin: extentInfo.fixMin,\n    fixMax: extentInfo.fixMax,\n    minInterval: isIntervalOrTime ? model.get('minInterval') : null,\n    maxInterval: isIntervalOrTime ? model.get('maxInterval') : null\n  }); // If some one specified the min, max. And the default calculated interval\n  // is not good enough. He can specify the interval. It is often appeared\n  // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard\n  // to be 60.\n  // FIXME\n\n  if (interval != null) {\n    scale.setInterval && scale.setInterval(interval);\n  }\n}\n/**\n * @param axisType Default retrieve from model.type\n */\n\nfunction createScaleByModel(model, axisType) {\n  axisType = axisType || model.get('type');\n\n  if (axisType) {\n    switch (axisType) {\n      // Buildin scale\n      case 'category':\n        return new OrdinalScale({\n          ordinalMeta: model.getOrdinalMeta ? model.getOrdinalMeta() : model.getCategories(),\n          extent: [Infinity, -Infinity]\n        });\n\n      case 'time':\n        return new TimeScale({\n          locale: model.ecModel.getLocaleModel(),\n          useUTC: model.ecModel.get('useUTC')\n        });\n\n      default:\n        // case 'value'/'interval', 'log', or others.\n        return new (Scale.getClass(axisType) || IntervalScale)();\n    }\n  }\n}\n/**\n * Check if the axis cross 0\n */\n\nfunction ifAxisCrossZero(axis) {\n  var dataExtent = axis.scale.getExtent();\n  var min = dataExtent[0];\n  var max = dataExtent[1];\n  return !(min > 0 && max > 0 || min < 0 && max < 0);\n}\n/**\n * @param axis\n * @return Label formatter function.\n *         param: {number} tickValue,\n *         param: {number} idx, the index in all ticks.\n *                         If category axis, this param is not required.\n *         return: {string} label string.\n */\n\nfunction makeLabelFormatter(axis) {\n  var labelFormatter = axis.getLabelModel().get('formatter');\n  var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null;\n\n  if (axis.scale.type === 'time') {\n    return function (tpl) {\n      return function (tick, idx) {\n        return axis.scale.getFormattedLabel(tick, idx, tpl);\n      };\n    }(labelFormatter);\n  } else if (isString(labelFormatter)) {\n    return function (tpl) {\n      return function (tick) {\n        // For category axis, get raw value; for numeric axis,\n        // get formatted label like '1,333,444'.\n        var label = axis.scale.getLabel(tick);\n        var text = tpl.replace('{value}', label != null ? label : '');\n        return text;\n      };\n    }(labelFormatter);\n  } else if (isFunction(labelFormatter)) {\n    return function (cb) {\n      return function (tick, idx) {\n        // The original intention of `idx` is \"the index of the tick in all ticks\".\n        // But the previous implementation of category axis do not consider the\n        // `axisLabel.interval`, which cause that, for example, the `interval` is\n        // `1`, then the ticks \"name5\", \"name7\", \"name9\" are displayed, where the\n        // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep\n        // the definition here for back compatibility.\n        if (categoryTickStart != null) {\n          idx = tick.value - categoryTickStart;\n        }\n\n        return cb(getAxisRawValue(axis, tick), idx, tick.level != null ? {\n          level: tick.level\n        } : null);\n      };\n    }(labelFormatter);\n  } else {\n    return function (tick) {\n      return axis.scale.getLabel(tick);\n    };\n  }\n}\nfunction getAxisRawValue(axis, tick) {\n  // In category axis with data zoom, tick is not the original\n  // index of axis.data. So tick should not be exposed to user\n  // in category axis.\n  return axis.type === 'category' ? axis.scale.getLabel(tick) : tick.value;\n}\n/**\n * @param axis\n * @return Be null/undefined if no labels.\n */\n\nfunction estimateLabelUnionRect(axis) {\n  var axisModel = axis.model;\n  var scale = axis.scale;\n\n  if (!axisModel.get(['axisLabel', 'show']) || scale.isBlank()) {\n    return;\n  }\n\n  var realNumberScaleTicks;\n  var tickCount;\n  var categoryScaleExtent = scale.getExtent(); // Optimize for large category data, avoid call `getTicks()`.\n\n  if (scale instanceof OrdinalScale) {\n    tickCount = scale.count();\n  } else {\n    realNumberScaleTicks = scale.getTicks();\n    tickCount = realNumberScaleTicks.length;\n  }\n\n  var axisLabelModel = axis.getLabelModel();\n  var labelFormatter = makeLabelFormatter(axis);\n  var rect;\n  var step = 1; // Simple optimization for large amount of labels\n\n  if (tickCount > 40) {\n    step = Math.ceil(tickCount / 40);\n  }\n\n  for (var i = 0; i < tickCount; i += step) {\n    var tick = realNumberScaleTicks ? realNumberScaleTicks[i] : {\n      value: categoryScaleExtent[0] + i\n    };\n    var label = labelFormatter(tick, i);\n    var unrotatedSingleRect = axisLabelModel.getTextRect(label);\n    var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);\n    rect ? rect.union(singleRect) : rect = singleRect;\n  }\n\n  return rect;\n}\n\nfunction rotateTextRect(textRect, rotate) {\n  var rotateRadians = rotate * Math.PI / 180;\n  var beforeWidth = textRect.width;\n  var beforeHeight = textRect.height;\n  var afterWidth = beforeWidth * Math.abs(Math.cos(rotateRadians)) + Math.abs(beforeHeight * Math.sin(rotateRadians));\n  var afterHeight = beforeWidth * Math.abs(Math.sin(rotateRadians)) + Math.abs(beforeHeight * Math.cos(rotateRadians));\n  var rotatedRect = new BoundingRect(textRect.x, textRect.y, afterWidth, afterHeight);\n  return rotatedRect;\n}\n/**\n * @param model axisLabelModel or axisTickModel\n * @return {number|String} Can be null|'auto'|number|function\n */\n\n\nfunction getOptionCategoryInterval(model) {\n  var interval = model.get('interval');\n  return interval == null ? 'auto' : interval;\n}\n/**\n * Set `categoryInterval` as 0 implicitly indicates that\n * show all labels regardless of overlap.\n * @param {Object} axis axisModel.axis\n */\n\nfunction shouldShowAllLabels(axis) {\n  return axis.type === 'category' && getOptionCategoryInterval(axis.getLabelModel()) === 0;\n}\nfunction getDataDimensionsOnAxis(data, axisDim) {\n  // Remove duplicated dat dimensions caused by `getStackedDimension`.\n  var dataDimMap = {}; // Currently `mapDimensionsAll` will contain stack result dimension ('__\\0ecstackresult').\n  // PENDING: is it reasonable? Do we need to remove the original dim from \"coord dim\" since\n  // there has been stacked result dim?\n\n  each(data.mapDimensionsAll(axisDim), function (dataDim) {\n    // For example, the extent of the original dimension\n    // is [0.1, 0.5], the extent of the `stackResultDimension`\n    // is [7, 9], the final extent should NOT include [0.1, 0.5],\n    // because there is no graphic corresponding to [0.1, 0.5].\n    // See the case in `test/area-stack.html` `main1`, where area line\n    // stack needs `yAxis` not start from 0.\n    dataDimMap[getStackedDimension(data, dataDim)] = true;\n  });\n  return keys(dataDimMap);\n}\nfunction unionAxisExtentFromData(dataExtent, data, axisDim) {\n  if (data) {\n    each(getDataDimensionsOnAxis(data, axisDim), function (dim) {\n      var seriesExtent = data.getApproximateExtent(dim);\n      seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]);\n      seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]);\n    });\n  }\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nvar AxisModelCommonMixin =\n/** @class */\nfunction () {\n  function AxisModelCommonMixin() {}\n\n  AxisModelCommonMixin.prototype.getNeedCrossZero = function () {\n    var option = this.option;\n    return !option.scale;\n  };\n  /**\n   * Should be implemented by each axis model if necessary.\n   * @return coordinate system model\n   */\n\n\n  AxisModelCommonMixin.prototype.getCoordSysModel = function () {\n    return;\n  };\n\n  return AxisModelCommonMixin;\n}();\n\n/**\n * Create a multi dimension List structure from seriesModel.\n */\n\nfunction createList(seriesModel) {\n  return createSeriesData(null, seriesModel);\n} // export function createGraph(seriesModel) {\nvar dataStack$1 = {\n  isDimensionStacked: isDimensionStacked,\n  enableDataStack: enableDataStack,\n  getStackedDimension: getStackedDimension\n};\n/**\n * Create scale\n * @param {Array.<number>} dataExtent\n * @param {Object|module:echarts/Model} option If `optoin.type`\n *        is secified, it can only be `'value'` currently.\n */\n\nfunction createScale(dataExtent, option) {\n  var axisModel = option;\n\n  if (!(option instanceof Model)) {\n    axisModel = new Model(option); // FIXME\n    // Currently AxisModelCommonMixin has nothing to do with the\n    // the requirements of `axisHelper.createScaleByModel`. For\n    // example the methods `getCategories` and `getOrdinalMeta`\n    // are required for `'category'` axis, and ecModel is required\n    // for `'time'` axis. But occasionally echarts-gl happened\n    // to only use `'value'` axis.\n    // zrUtil.mixin(axisModel, AxisModelCommonMixin);\n  }\n\n  var scale = createScaleByModel(axisModel);\n  scale.setExtent(dataExtent[0], dataExtent[1]);\n  niceScaleExtent(scale, axisModel);\n  return scale;\n}\n/**\n * Mixin common methods to axis model,\n *\n * Include methods\n * `getFormattedLabels() => Array.<string>`\n * `getCategories() => Array.<string>`\n * `getMin(origin: boolean) => number`\n * `getMax(origin: boolean) => number`\n * `getNeedCrossZero() => boolean`\n */\n\nfunction mixinAxisModelCommonMethods(Model) {\n  mixin(Model, AxisModelCommonMixin);\n}\nfunction createTextStyle$1(textStyleModel, opts) {\n  opts = opts || {};\n  return createTextStyle(textStyleModel, null, null, opts.state !== 'normal');\n}\n\nvar helper = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    createList: createList,\n    getLayoutRect: getLayoutRect,\n    dataStack: dataStack$1,\n    createScale: createScale,\n    mixinAxisModelCommonMethods: mixinAxisModelCommonMethods,\n    getECData: getECData,\n    createTextStyle: createTextStyle$1,\n    createDimensions: createDimensions,\n    createSymbol: createSymbol,\n    enableHoverEmphasis: enableHoverEmphasis\n});\n\nvar EPSILON$4 = 1e-8;\nfunction isAroundEqual$1(a, b) {\n    return Math.abs(a - b) < EPSILON$4;\n}\nfunction contain$2(points, x, y) {\n    var w = 0;\n    var p = points[0];\n    if (!p) {\n        return false;\n    }\n    for (var i = 1; i < points.length; i++) {\n        var p2 = points[i];\n        w += windingLine(p[0], p[1], p2[0], p2[1], x, y);\n        p = p2;\n    }\n    var p0 = points[0];\n    if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) {\n        w += windingLine(p[0], p[1], p0[0], p0[1], x, y);\n    }\n    return w !== 0;\n}\n\nvar TMP_TRANSFORM = [];\n\nfunction transformPoints(points, transform) {\n  for (var p = 0; p < points.length; p++) {\n    applyTransform(points[p], points[p], transform);\n  }\n}\n\nfunction updateBBoxFromPoints(points, min$1, max$1, projection) {\n  for (var i = 0; i < points.length; i++) {\n    var p = points[i];\n\n    if (projection) {\n      // projection may return null point.\n      p = projection.project(p);\n    }\n\n    if (p && isFinite(p[0]) && isFinite(p[1])) {\n      min(min$1, min$1, p);\n      max(max$1, max$1, p);\n    }\n  }\n}\n\nfunction centroid(points) {\n  var signedArea = 0;\n  var cx = 0;\n  var cy = 0;\n  var len = points.length;\n  var x0 = points[len - 1][0];\n  var y0 = points[len - 1][1]; // Polygon should been closed.\n\n  for (var i = 0; i < len; i++) {\n    var x1 = points[i][0];\n    var y1 = points[i][1];\n    var a = x0 * y1 - x1 * y0;\n    signedArea += a;\n    cx += (x0 + x1) * a;\n    cy += (y0 + y1) * a;\n    x0 = x1;\n    y0 = y1;\n  }\n\n  return signedArea ? [cx / signedArea / 3, cy / signedArea / 3, signedArea] : [points[0][0] || 0, points[0][1] || 0];\n}\n\nvar Region =\n/** @class */\nfunction () {\n  function Region(name) {\n    this.name = name;\n  }\n\n  Region.prototype.setCenter = function (center) {\n    this._center = center;\n  };\n  /**\n   * Get center point in data unit. That is,\n   * for GeoJSONRegion, the unit is lat/lng,\n   * for GeoSVGRegion, the unit is SVG local coord.\n   */\n\n\n  Region.prototype.getCenter = function () {\n    var center = this._center;\n\n    if (!center) {\n      // In most cases there are no need to calculate this center.\n      // So calculate only when called.\n      center = this._center = this.calcCenter();\n    }\n\n    return center;\n  };\n\n  return Region;\n}();\n\nvar GeoJSONPolygonGeometry =\n/** @class */\nfunction () {\n  function GeoJSONPolygonGeometry(exterior, interiors) {\n    this.type = 'polygon';\n    this.exterior = exterior;\n    this.interiors = interiors;\n  }\n\n  return GeoJSONPolygonGeometry;\n}();\n\nvar GeoJSONLineStringGeometry =\n/** @class */\nfunction () {\n  function GeoJSONLineStringGeometry(points) {\n    this.type = 'linestring';\n    this.points = points;\n  }\n\n  return GeoJSONLineStringGeometry;\n}();\n\nvar GeoJSONRegion =\n/** @class */\nfunction (_super) {\n  __extends(GeoJSONRegion, _super);\n\n  function GeoJSONRegion(name, geometries, cp) {\n    var _this = _super.call(this, name) || this;\n\n    _this.type = 'geoJSON';\n    _this.geometries = geometries;\n    _this._center = cp && [cp[0], cp[1]];\n    return _this;\n  }\n\n  GeoJSONRegion.prototype.calcCenter = function () {\n    var geometries = this.geometries;\n    var largestGeo;\n    var largestGeoSize = 0;\n\n    for (var i = 0; i < geometries.length; i++) {\n      var geo = geometries[i];\n      var exterior = geo.exterior; // Simple trick to use points count instead of polygon area as region size.\n      // Ignore linestring\n\n      var size = exterior && exterior.length;\n\n      if (size > largestGeoSize) {\n        largestGeo = geo;\n        largestGeoSize = size;\n      }\n    }\n\n    if (largestGeo) {\n      return centroid(largestGeo.exterior);\n    } // from bounding rect by default.\n\n\n    var rect = this.getBoundingRect();\n    return [rect.x + rect.width / 2, rect.y + rect.height / 2];\n  };\n\n  GeoJSONRegion.prototype.getBoundingRect = function (projection) {\n    var rect = this._rect; // Always recalculate if using projection.\n\n    if (rect && !projection) {\n      return rect;\n    }\n\n    var min = [Infinity, Infinity];\n    var max = [-Infinity, -Infinity];\n    var geometries = this.geometries;\n    each(geometries, function (geo) {\n      if (geo.type === 'polygon') {\n        // Doesn't consider hole\n        updateBBoxFromPoints(geo.exterior, min, max, projection);\n      } else {\n        each(geo.points, function (points) {\n          updateBBoxFromPoints(points, min, max, projection);\n        });\n      }\n    }); // Normalie invalid bounding.\n\n    if (!(isFinite(min[0]) && isFinite(min[1]) && isFinite(max[0]) && isFinite(max[1]))) {\n      min[0] = min[1] = max[0] = max[1] = 0;\n    }\n\n    rect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n\n    if (!projection) {\n      this._rect = rect;\n    }\n\n    return rect;\n  };\n\n  GeoJSONRegion.prototype.contain = function (coord) {\n    var rect = this.getBoundingRect();\n    var geometries = this.geometries;\n\n    if (!rect.contain(coord[0], coord[1])) {\n      return false;\n    }\n\n    loopGeo: for (var i = 0, len = geometries.length; i < len; i++) {\n      var geo = geometries[i]; // Only support polygon.\n\n      if (geo.type !== 'polygon') {\n        continue;\n      }\n\n      var exterior = geo.exterior;\n      var interiors = geo.interiors;\n\n      if (contain$2(exterior, coord[0], coord[1])) {\n        // Not in the region if point is in the hole.\n        for (var k = 0; k < (interiors ? interiors.length : 0); k++) {\n          if (contain$2(interiors[k], coord[0], coord[1])) {\n            continue loopGeo;\n          }\n        }\n\n        return true;\n      }\n    }\n\n    return false;\n  };\n  /**\n   * Transform the raw coords to target bounding.\n   * @param x\n   * @param y\n   * @param width\n   * @param height\n   */\n\n\n  GeoJSONRegion.prototype.transformTo = function (x, y, width, height) {\n    var rect = this.getBoundingRect();\n    var aspect = rect.width / rect.height;\n\n    if (!width) {\n      width = aspect * height;\n    } else if (!height) {\n      height = width / aspect;\n    }\n\n    var target = new BoundingRect(x, y, width, height);\n    var transform = rect.calculateTransform(target);\n    var geometries = this.geometries;\n\n    for (var i = 0; i < geometries.length; i++) {\n      var geo = geometries[i];\n\n      if (geo.type === 'polygon') {\n        transformPoints(geo.exterior, transform);\n        each(geo.interiors, function (interior) {\n          transformPoints(interior, transform);\n        });\n      } else {\n        each(geo.points, function (points) {\n          transformPoints(points, transform);\n        });\n      }\n    }\n\n    rect = this._rect;\n    rect.copy(target); // Update center\n\n    this._center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n  };\n\n  GeoJSONRegion.prototype.cloneShallow = function (name) {\n    name == null && (name = this.name);\n    var newRegion = new GeoJSONRegion(name, this.geometries, this._center);\n    newRegion._rect = this._rect;\n    newRegion.transformTo = null; // Simply avoid to be called.\n\n    return newRegion;\n  };\n\n  return GeoJSONRegion;\n}(Region);\n\nvar GeoSVGRegion =\n/** @class */\nfunction (_super) {\n  __extends(GeoSVGRegion, _super);\n\n  function GeoSVGRegion(name, elOnlyForCalculate) {\n    var _this = _super.call(this, name) || this;\n\n    _this.type = 'geoSVG';\n    _this._elOnlyForCalculate = elOnlyForCalculate;\n    return _this;\n  }\n\n  GeoSVGRegion.prototype.calcCenter = function () {\n    var el = this._elOnlyForCalculate;\n    var rect = el.getBoundingRect();\n    var center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n    var mat = identity(TMP_TRANSFORM);\n    var target = el;\n\n    while (target && !target.isGeoSVGGraphicRoot) {\n      mul$1(mat, target.getLocalTransform(), mat);\n      target = target.parent;\n    }\n\n    invert(mat, mat);\n    applyTransform(center, center, mat);\n    return center;\n  };\n\n  return GeoSVGRegion;\n}(Region);\n\nfunction decode(json) {\n  if (!json.UTF8Encoding) {\n    return json;\n  }\n\n  var jsonCompressed = json;\n  var encodeScale = jsonCompressed.UTF8Scale;\n\n  if (encodeScale == null) {\n    encodeScale = 1024;\n  }\n\n  var features = jsonCompressed.features;\n  each(features, function (feature) {\n    var geometry = feature.geometry;\n    var encodeOffsets = geometry.encodeOffsets;\n    var coordinates = geometry.coordinates; // Geometry may be appeded manually in the script after json loaded.\n    // In this case this geometry is usually not encoded.\n\n    if (!encodeOffsets) {\n      return;\n    }\n\n    switch (geometry.type) {\n      case 'LineString':\n        geometry.coordinates = decodeRing(coordinates, encodeOffsets, encodeScale);\n        break;\n\n      case 'Polygon':\n        decodeRings(coordinates, encodeOffsets, encodeScale);\n        break;\n\n      case 'MultiLineString':\n        decodeRings(coordinates, encodeOffsets, encodeScale);\n        break;\n\n      case 'MultiPolygon':\n        each(coordinates, function (rings, idx) {\n          return decodeRings(rings, encodeOffsets[idx], encodeScale);\n        });\n    }\n  }); // Has been decoded\n\n  jsonCompressed.UTF8Encoding = false;\n  return jsonCompressed;\n}\n\nfunction decodeRings(rings, encodeOffsets, encodeScale) {\n  for (var c = 0; c < rings.length; c++) {\n    rings[c] = decodeRing(rings[c], encodeOffsets[c], encodeScale);\n  }\n}\n\nfunction decodeRing(coordinate, encodeOffsets, encodeScale) {\n  var result = [];\n  var prevX = encodeOffsets[0];\n  var prevY = encodeOffsets[1];\n\n  for (var i = 0; i < coordinate.length; i += 2) {\n    var x = coordinate.charCodeAt(i) - 64;\n    var y = coordinate.charCodeAt(i + 1) - 64; // ZigZag decoding\n\n    x = x >> 1 ^ -(x & 1);\n    y = y >> 1 ^ -(y & 1); // Delta deocding\n\n    x += prevX;\n    y += prevY;\n    prevX = x;\n    prevY = y; // Dequantize\n\n    result.push([x / encodeScale, y / encodeScale]);\n  }\n\n  return result;\n}\n\nfunction parseGeoJSON(geoJson, nameProperty) {\n  geoJson = decode(geoJson);\n  return map(filter(geoJson.features, function (featureObj) {\n    // Output of mapshaper may have geometry null\n    return featureObj.geometry && featureObj.properties && featureObj.geometry.coordinates.length > 0;\n  }), function (featureObj) {\n    var properties = featureObj.properties;\n    var geo = featureObj.geometry;\n    var geometries = [];\n\n    switch (geo.type) {\n      case 'Polygon':\n        var coordinates = geo.coordinates; // According to the GeoJSON specification.\n        // First must be exterior, and the rest are all interior(holes).\n\n        geometries.push(new GeoJSONPolygonGeometry(coordinates[0], coordinates.slice(1)));\n        break;\n\n      case 'MultiPolygon':\n        each(geo.coordinates, function (item) {\n          if (item[0]) {\n            geometries.push(new GeoJSONPolygonGeometry(item[0], item.slice(1)));\n          }\n        });\n        break;\n\n      case 'LineString':\n        geometries.push(new GeoJSONLineStringGeometry([geo.coordinates]));\n        break;\n\n      case 'MultiLineString':\n        geometries.push(new GeoJSONLineStringGeometry(geo.coordinates));\n    }\n\n    var region = new GeoJSONRegion(properties[nameProperty || 'name'], geometries, properties.cp);\n    region.properties = properties;\n    return region;\n  });\n}\n\nvar number = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    linearMap: linearMap,\n    round: round,\n    asc: asc,\n    getPrecision: getPrecision,\n    getPrecisionSafe: getPrecisionSafe,\n    getPixelPrecision: getPixelPrecision,\n    getPercentWithPrecision: getPercentWithPrecision,\n    MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,\n    remRadian: remRadian,\n    isRadianAroundZero: isRadianAroundZero,\n    parseDate: parseDate,\n    quantity: quantity,\n    quantityExponent: quantityExponent,\n    nice: nice,\n    quantile: quantile,\n    reformIntervals: reformIntervals,\n    isNumeric: isNumeric,\n    numericToNumber: numericToNumber\n});\n\nvar time = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    parse: parseDate,\n    format: format\n});\n\nvar graphic$1 = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    extendShape: extendShape,\n    extendPath: extendPath,\n    makePath: makePath,\n    makeImage: makeImage,\n    mergePath: mergePath$1,\n    resizePath: resizePath,\n    createIcon: createIcon,\n    updateProps: updateProps,\n    initProps: initProps,\n    getTransform: getTransform,\n    clipPointsByRect: clipPointsByRect,\n    clipRectByRect: clipRectByRect,\n    registerShape: registerShape,\n    getShapeClass: getShapeClass,\n    Group: Group,\n    Image: ZRImage,\n    Text: ZRText,\n    Circle: Circle,\n    Ellipse: Ellipse,\n    Sector: Sector,\n    Ring: Ring,\n    Polygon: Polygon,\n    Polyline: Polyline,\n    Rect: Rect,\n    Line: Line,\n    BezierCurve: BezierCurve,\n    Arc: Arc,\n    IncrementalDisplayable: IncrementalDisplayable,\n    CompoundPath: CompoundPath,\n    LinearGradient: LinearGradient,\n    RadialGradient: RadialGradient,\n    BoundingRect: BoundingRect\n});\n\nvar format$1 = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    addCommas: addCommas,\n    toCamelCase: toCamelCase,\n    normalizeCssArray: normalizeCssArray$1,\n    encodeHTML: encodeHTML,\n    formatTpl: formatTpl,\n    getTooltipMarker: getTooltipMarker,\n    formatTime: formatTime,\n    capitalFirst: capitalFirst,\n    truncateText: truncateText,\n    getTextRect: getTextRect\n});\n\nvar util$1 = /*#__PURE__*/Object.freeze({\n    __proto__: null,\n    map: map,\n    each: each,\n    indexOf: indexOf,\n    inherits: inherits,\n    reduce: reduce,\n    filter: filter,\n    bind: bind,\n    curry: curry,\n    isArray: isArray,\n    isString: isString,\n    isObject: isObject,\n    isFunction: isFunction,\n    extend: extend,\n    defaults: defaults,\n    clone: clone,\n    merge: merge\n});\n\nvar inner$5 = makeInner();\nfunction createAxisLabels(axis) {\n  // Only ordinal scale support tick interval\n  return axis.type === 'category' ? makeCategoryLabels(axis) : makeRealNumberLabels(axis);\n}\n/**\n * @param {module:echats/coord/Axis} axis\n * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea.\n * @return {Object} {\n *     ticks: Array.<number>\n *     tickCategoryInterval: number\n * }\n */\n\nfunction createAxisTicks(axis, tickModel) {\n  // Only ordinal scale support tick interval\n  return axis.type === 'category' ? makeCategoryTicks(axis, tickModel) : {\n    ticks: map(axis.scale.getTicks(), function (tick) {\n      return tick.value;\n    })\n  };\n}\n\nfunction makeCategoryLabels(axis) {\n  var labelModel = axis.getLabelModel();\n  var result = makeCategoryLabelsActually(axis, labelModel);\n  return !labelModel.get('show') || axis.scale.isBlank() ? {\n    labels: [],\n    labelCategoryInterval: result.labelCategoryInterval\n  } : result;\n}\n\nfunction makeCategoryLabelsActually(axis, labelModel) {\n  var labelsCache = getListCache(axis, 'labels');\n  var optionLabelInterval = getOptionCategoryInterval(labelModel);\n  var result = listCacheGet(labelsCache, optionLabelInterval);\n\n  if (result) {\n    return result;\n  }\n\n  var labels;\n  var numericLabelInterval;\n\n  if (isFunction(optionLabelInterval)) {\n    labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval);\n  } else {\n    numericLabelInterval = optionLabelInterval === 'auto' ? makeAutoCategoryInterval(axis) : optionLabelInterval;\n    labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval);\n  } // Cache to avoid calling interval function repeatedly.\n\n\n  return listCacheSet(labelsCache, optionLabelInterval, {\n    labels: labels,\n    labelCategoryInterval: numericLabelInterval\n  });\n}\n\nfunction makeCategoryTicks(axis, tickModel) {\n  var ticksCache = getListCache(axis, 'ticks');\n  var optionTickInterval = getOptionCategoryInterval(tickModel);\n  var result = listCacheGet(ticksCache, optionTickInterval);\n\n  if (result) {\n    return result;\n  }\n\n  var ticks;\n  var tickCategoryInterval; // Optimize for the case that large category data and no label displayed,\n  // we should not return all ticks.\n\n  if (!tickModel.get('show') || axis.scale.isBlank()) {\n    ticks = [];\n  }\n\n  if (isFunction(optionTickInterval)) {\n    ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true);\n  } // Always use label interval by default despite label show. Consider this\n  // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows\n  // labels. `splitLine` and `axisTick` should be consistent in this case.\n  else if (optionTickInterval === 'auto') {\n      var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel());\n      tickCategoryInterval = labelsResult.labelCategoryInterval;\n      ticks = map(labelsResult.labels, function (labelItem) {\n        return labelItem.tickValue;\n      });\n    } else {\n      tickCategoryInterval = optionTickInterval;\n      ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true);\n    } // Cache to avoid calling interval function repeatedly.\n\n\n  return listCacheSet(ticksCache, optionTickInterval, {\n    ticks: ticks,\n    tickCategoryInterval: tickCategoryInterval\n  });\n}\n\nfunction makeRealNumberLabels(axis) {\n  var ticks = axis.scale.getTicks();\n  var labelFormatter = makeLabelFormatter(axis);\n  return {\n    labels: map(ticks, function (tick, idx) {\n      return {\n        level: tick.level,\n        formattedLabel: labelFormatter(tick, idx),\n        rawLabel: axis.scale.getLabel(tick),\n        tickValue: tick.value\n      };\n    })\n  };\n}\n\nfunction getListCache(axis, prop) {\n  // Because key can be a function, and cache size always is small, we use array cache.\n  return inner$5(axis)[prop] || (inner$5(axis)[prop] = []);\n}\n\nfunction listCacheGet(cache, key) {\n  for (var i = 0; i < cache.length; i++) {\n    if (cache[i].key === key) {\n      return cache[i].value;\n    }\n  }\n}\n\nfunction listCacheSet(cache, key, value) {\n  cache.push({\n    key: key,\n    value: value\n  });\n  return value;\n}\n\nfunction makeAutoCategoryInterval(axis) {\n  var result = inner$5(axis).autoInterval;\n  return result != null ? result : inner$5(axis).autoInterval = axis.calculateCategoryInterval();\n}\n/**\n * Calculate interval for category axis ticks and labels.\n * To get precise result, at least one of `getRotate` and `isHorizontal`\n * should be implemented in axis.\n */\n\n\nfunction calculateCategoryInterval(axis) {\n  var params = fetchAutoCategoryIntervalCalculationParams(axis);\n  var labelFormatter = makeLabelFormatter(axis);\n  var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI;\n  var ordinalScale = axis.scale;\n  var ordinalExtent = ordinalScale.getExtent(); // Providing this method is for optimization:\n  // avoid generating a long array by `getTicks`\n  // in large category data case.\n\n  var tickCount = ordinalScale.count();\n\n  if (ordinalExtent[1] - ordinalExtent[0] < 1) {\n    return 0;\n  }\n\n  var step = 1; // Simple optimization. Empirical value: tick count should less than 40.\n\n  if (tickCount > 40) {\n    step = Math.max(1, Math.floor(tickCount / 40));\n  }\n\n  var tickValue = ordinalExtent[0];\n  var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);\n  var unitW = Math.abs(unitSpan * Math.cos(rotation));\n  var unitH = Math.abs(unitSpan * Math.sin(rotation));\n  var maxW = 0;\n  var maxH = 0; // Caution: Performance sensitive for large category data.\n  // Consider dataZoom, we should make appropriate step to avoid O(n) loop.\n\n  for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n    var width = 0;\n    var height = 0; // Not precise, do not consider align and vertical align\n    // and each distance from axis line yet.\n\n    var rect = getBoundingRect(labelFormatter({\n      value: tickValue\n    }), params.font, 'center', 'top'); // Magic number\n\n    width = rect.width * 1.3;\n    height = rect.height * 1.3; // Min size, void long loop.\n\n    maxW = Math.max(maxW, width, 7);\n    maxH = Math.max(maxH, height, 7);\n  }\n\n  var dw = maxW / unitW;\n  var dh = maxH / unitH; // 0/0 is NaN, 1/0 is Infinity.\n\n  isNaN(dw) && (dw = Infinity);\n  isNaN(dh) && (dh = Infinity);\n  var interval = Math.max(0, Math.floor(Math.min(dw, dh)));\n  var cache = inner$5(axis.model);\n  var axisExtent = axis.getExtent();\n  var lastAutoInterval = cache.lastAutoInterval;\n  var lastTickCount = cache.lastTickCount; // Use cache to keep interval stable while moving zoom window,\n  // otherwise the calculated interval might jitter when the zoom\n  // window size is close to the interval-changing size.\n  // For example, if all of the axis labels are `a, b, c, d, e, f, g`.\n  // The jitter will cause that sometimes the displayed labels are\n  // `a, d, g` (interval: 2) sometimes `a, c, e`(interval: 1).\n\n  if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 // Always choose the bigger one, otherwise the critical\n  // point is not the same when zooming in or zooming out.\n  && lastAutoInterval > interval // If the axis change is caused by chart resize, the cache should not\n  // be used. Otherwise some hidden labels might not be shown again.\n  && cache.axisExtent0 === axisExtent[0] && cache.axisExtent1 === axisExtent[1]) {\n    interval = lastAutoInterval;\n  } // Only update cache if cache not used, otherwise the\n  // changing of interval is too insensitive.\n  else {\n      cache.lastTickCount = tickCount;\n      cache.lastAutoInterval = interval;\n      cache.axisExtent0 = axisExtent[0];\n      cache.axisExtent1 = axisExtent[1];\n    }\n\n  return interval;\n}\n\nfunction fetchAutoCategoryIntervalCalculationParams(axis) {\n  var labelModel = axis.getLabelModel();\n  return {\n    axisRotate: axis.getRotate ? axis.getRotate() : axis.isHorizontal && !axis.isHorizontal() ? 90 : 0,\n    labelRotate: labelModel.get('rotate') || 0,\n    font: labelModel.getFont()\n  };\n}\n\nfunction makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) {\n  var labelFormatter = makeLabelFormatter(axis);\n  var ordinalScale = axis.scale;\n  var ordinalExtent = ordinalScale.getExtent();\n  var labelModel = axis.getLabelModel();\n  var result = []; // TODO: axisType: ordinalTime, pick the tick from each month/day/year/...\n\n  var step = Math.max((categoryInterval || 0) + 1, 1);\n  var startTick = ordinalExtent[0];\n  var tickCount = ordinalScale.count(); // Calculate start tick based on zero if possible to keep label consistent\n  // while zooming and moving while interval > 0. Otherwise the selection\n  // of displayable ticks and symbols probably keep changing.\n  // 3 is empirical value.\n\n  if (startTick !== 0 && step > 1 && tickCount / step > 2) {\n    startTick = Math.round(Math.ceil(startTick / step) * step);\n  } // (1) Only add min max label here but leave overlap checking\n  // to render stage, which also ensure the returned list\n  // suitable for splitLine and splitArea rendering.\n  // (2) Scales except category always contain min max label so\n  // do not need to perform this process.\n\n\n  var showAllLabel = shouldShowAllLabels(axis);\n  var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel;\n  var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel;\n\n  if (includeMinLabel && startTick !== ordinalExtent[0]) {\n    addItem(ordinalExtent[0]);\n  } // Optimize: avoid generating large array by `ordinalScale.getTicks()`.\n\n\n  var tickValue = startTick;\n\n  for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n    addItem(tickValue);\n  }\n\n  if (includeMaxLabel && tickValue - step !== ordinalExtent[1]) {\n    addItem(ordinalExtent[1]);\n  }\n\n  function addItem(tickValue) {\n    var tickObj = {\n      value: tickValue\n    };\n    result.push(onlyTick ? tickValue : {\n      formattedLabel: labelFormatter(tickObj),\n      rawLabel: ordinalScale.getLabel(tickObj),\n      tickValue: tickValue\n    });\n  }\n\n  return result;\n}\n\nfunction makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) {\n  var ordinalScale = axis.scale;\n  var labelFormatter = makeLabelFormatter(axis);\n  var result = [];\n  each(ordinalScale.getTicks(), function (tick) {\n    var rawLabel = ordinalScale.getLabel(tick);\n    var tickValue = tick.value;\n\n    if (categoryInterval(tick.value, rawLabel)) {\n      result.push(onlyTick ? tickValue : {\n        formattedLabel: labelFormatter(tick),\n        rawLabel: rawLabel,\n        tickValue: tickValue\n      });\n    }\n  });\n  return result;\n}\n\nvar NORMALIZED_EXTENT = [0, 1];\n/**\n * Base class of Axis.\n */\n\nvar Axis =\n/** @class */\nfunction () {\n  function Axis(dim, scale, extent) {\n    this.onBand = false;\n    this.inverse = false;\n    this.dim = dim;\n    this.scale = scale;\n    this._extent = extent || [0, 0];\n  }\n  /**\n   * If axis extent contain given coord\n   */\n\n\n  Axis.prototype.contain = function (coord) {\n    var extent = this._extent;\n    var min = Math.min(extent[0], extent[1]);\n    var max = Math.max(extent[0], extent[1]);\n    return coord >= min && coord <= max;\n  };\n  /**\n   * If axis extent contain given data\n   */\n\n\n  Axis.prototype.containData = function (data) {\n    return this.scale.contain(data);\n  };\n  /**\n   * Get coord extent.\n   */\n\n\n  Axis.prototype.getExtent = function () {\n    return this._extent.slice();\n  };\n  /**\n   * Get precision used for formatting\n   */\n\n\n  Axis.prototype.getPixelPrecision = function (dataExtent) {\n    return getPixelPrecision(dataExtent || this.scale.getExtent(), this._extent);\n  };\n  /**\n   * Set coord extent\n   */\n\n\n  Axis.prototype.setExtent = function (start, end) {\n    var extent = this._extent;\n    extent[0] = start;\n    extent[1] = end;\n  };\n  /**\n   * Convert data to coord. Data is the rank if it has an ordinal scale\n   */\n\n\n  Axis.prototype.dataToCoord = function (data, clamp) {\n    var extent = this._extent;\n    var scale = this.scale;\n    data = scale.normalize(data);\n\n    if (this.onBand && scale.type === 'ordinal') {\n      extent = extent.slice();\n      fixExtentWithBands(extent, scale.count());\n    }\n\n    return linearMap(data, NORMALIZED_EXTENT, extent, clamp);\n  };\n  /**\n   * Convert coord to data. Data is the rank if it has an ordinal scale\n   */\n\n\n  Axis.prototype.coordToData = function (coord, clamp) {\n    var extent = this._extent;\n    var scale = this.scale;\n\n    if (this.onBand && scale.type === 'ordinal') {\n      extent = extent.slice();\n      fixExtentWithBands(extent, scale.count());\n    }\n\n    var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp);\n    return this.scale.scale(t);\n  };\n  /**\n   * Convert pixel point to data in axis\n   */\n\n\n  Axis.prototype.pointToData = function (point, clamp) {\n    // Should be implemented in derived class if necessary.\n    return;\n  };\n  /**\n   * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`,\n   * `axis.getTicksCoords` considers `onBand`, which is used by\n   * `boundaryGap:true` of category axis and splitLine and splitArea.\n   * @param opt.tickModel default: axis.model.getModel('axisTick')\n   * @param opt.clamp If `true`, the first and the last\n   *        tick must be at the axis end points. Otherwise, clip ticks\n   *        that outside the axis extent.\n   */\n\n\n  Axis.prototype.getTicksCoords = function (opt) {\n    opt = opt || {};\n    var tickModel = opt.tickModel || this.getTickModel();\n    var result = createAxisTicks(this, tickModel);\n    var ticks = result.ticks;\n    var ticksCoords = map(ticks, function (tickVal) {\n      return {\n        coord: this.dataToCoord(this.scale.type === 'ordinal' ? this.scale.getRawOrdinalNumber(tickVal) : tickVal),\n        tickValue: tickVal\n      };\n    }, this);\n    var alignWithLabel = tickModel.get('alignWithLabel');\n    fixOnBandTicksCoords(this, ticksCoords, alignWithLabel, opt.clamp);\n    return ticksCoords;\n  };\n\n  Axis.prototype.getMinorTicksCoords = function () {\n    if (this.scale.type === 'ordinal') {\n      // Category axis doesn't support minor ticks\n      return [];\n    }\n\n    var minorTickModel = this.model.getModel('minorTick');\n    var splitNumber = minorTickModel.get('splitNumber'); // Protection.\n\n    if (!(splitNumber > 0 && splitNumber < 100)) {\n      splitNumber = 5;\n    }\n\n    var minorTicks = this.scale.getMinorTicks(splitNumber);\n    var minorTicksCoords = map(minorTicks, function (minorTicksGroup) {\n      return map(minorTicksGroup, function (minorTick) {\n        return {\n          coord: this.dataToCoord(minorTick),\n          tickValue: minorTick\n        };\n      }, this);\n    }, this);\n    return minorTicksCoords;\n  };\n\n  Axis.prototype.getViewLabels = function () {\n    return createAxisLabels(this).labels;\n  };\n\n  Axis.prototype.getLabelModel = function () {\n    return this.model.getModel('axisLabel');\n  };\n  /**\n   * Notice here we only get the default tick model. For splitLine\n   * or splitArea, we should pass the splitLineModel or splitAreaModel\n   * manually when calling `getTicksCoords`.\n   * In GL, this method may be overridden to:\n   * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`\n   */\n\n\n  Axis.prototype.getTickModel = function () {\n    return this.model.getModel('axisTick');\n  };\n  /**\n   * Get width of band\n   */\n\n\n  Axis.prototype.getBandWidth = function () {\n    var axisExtent = this._extent;\n    var dataExtent = this.scale.getExtent();\n    var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); // Fix #2728, avoid NaN when only one data.\n\n    len === 0 && (len = 1);\n    var size = Math.abs(axisExtent[1] - axisExtent[0]);\n    return Math.abs(size) / len;\n  };\n  /**\n   * Only be called in category axis.\n   * Can be overridden, consider other axes like in 3D.\n   * @return Auto interval for cateogry axis tick and label\n   */\n\n\n  Axis.prototype.calculateCategoryInterval = function () {\n    return calculateCategoryInterval(this);\n  };\n\n  return Axis;\n}();\n\nfunction fixExtentWithBands(extent, nTick) {\n  var size = extent[1] - extent[0];\n  var len = nTick;\n  var margin = size / len / 2;\n  extent[0] += margin;\n  extent[1] -= margin;\n} // If axis has labels [1, 2, 3, 4]. Bands on the axis are\n// |---1---|---2---|---3---|---4---|.\n// So the displayed ticks and splitLine/splitArea should between\n// each data item, otherwise cause misleading (e.g., split tow bars\n// of a single data item when there are two bar series).\n// Also consider if tickCategoryInterval > 0 and onBand, ticks and\n// splitLine/spliteArea should layout appropriately corresponding\n// to displayed labels. (So we should not use `getBandWidth` in this\n// case).\n\n\nfunction fixOnBandTicksCoords(axis, ticksCoords, alignWithLabel, clamp) {\n  var ticksLen = ticksCoords.length;\n\n  if (!axis.onBand || alignWithLabel || !ticksLen) {\n    return;\n  }\n\n  var axisExtent = axis.getExtent();\n  var last;\n  var diffSize;\n\n  if (ticksLen === 1) {\n    ticksCoords[0].coord = axisExtent[0];\n    last = ticksCoords[1] = {\n      coord: axisExtent[0]\n    };\n  } else {\n    var crossLen = ticksCoords[ticksLen - 1].tickValue - ticksCoords[0].tickValue;\n    var shift_1 = (ticksCoords[ticksLen - 1].coord - ticksCoords[0].coord) / crossLen;\n    each(ticksCoords, function (ticksItem) {\n      ticksItem.coord -= shift_1 / 2;\n    });\n    var dataExtent = axis.scale.getExtent();\n    diffSize = 1 + dataExtent[1] - ticksCoords[ticksLen - 1].tickValue;\n    last = {\n      coord: ticksCoords[ticksLen - 1].coord + shift_1 * diffSize\n    };\n    ticksCoords.push(last);\n  }\n\n  var inverse = axisExtent[0] > axisExtent[1]; // Handling clamp.\n\n  if (littleThan(ticksCoords[0].coord, axisExtent[0])) {\n    clamp ? ticksCoords[0].coord = axisExtent[0] : ticksCoords.shift();\n  }\n\n  if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) {\n    ticksCoords.unshift({\n      coord: axisExtent[0]\n    });\n  }\n\n  if (littleThan(axisExtent[1], last.coord)) {\n    clamp ? last.coord = axisExtent[1] : ticksCoords.pop();\n  }\n\n  if (clamp && littleThan(last.coord, axisExtent[1])) {\n    ticksCoords.push({\n      coord: axisExtent[1]\n    });\n  }\n\n  function littleThan(a, b) {\n    // Avoid rounding error cause calculated tick coord different with extent.\n    // It may cause an extra unnecessary tick added.\n    a = round(a);\n    b = round(b);\n    return inverse ? a > b : a < b;\n  }\n}\n\n// Should use `ComponentModel.extend` or `class XXXX extend ComponentModel` to create class.\n// Then use `registerComponentModel` in `install` parameter when `use` this extension. For example:\n// class Bar3DModel extends ComponentModel {}\n// export function install(registers) { registers.registerComponentModel(Bar3DModel); }\n// echarts.use(install);\n\nfunction extendComponentModel(proto) {\n  var Model = ComponentModel.extend(proto);\n  ComponentModel.registerClass(Model);\n  return Model;\n}\nfunction extendComponentView(proto) {\n  var View = ComponentView.extend(proto);\n  ComponentView.registerClass(View);\n  return View;\n}\nfunction extendSeriesModel(proto) {\n  var Model = SeriesModel.extend(proto);\n  SeriesModel.registerClass(Model);\n  return Model;\n}\nfunction extendChartView(proto) {\n  var View = ChartView.extend(proto);\n  ChartView.registerClass(View);\n  return View;\n}\n\nvar PI2$6 = Math.PI * 2;\nvar CMD$3 = PathProxy.CMD;\nvar DEFAULT_SEARCH_SPACE = ['top', 'right', 'bottom', 'left'];\n\nfunction getCandidateAnchor(pos, distance, rect, outPt, outDir) {\n  var width = rect.width;\n  var height = rect.height;\n\n  switch (pos) {\n    case 'top':\n      outPt.set(rect.x + width / 2, rect.y - distance);\n      outDir.set(0, -1);\n      break;\n\n    case 'bottom':\n      outPt.set(rect.x + width / 2, rect.y + height + distance);\n      outDir.set(0, 1);\n      break;\n\n    case 'left':\n      outPt.set(rect.x - distance, rect.y + height / 2);\n      outDir.set(-1, 0);\n      break;\n\n    case 'right':\n      outPt.set(rect.x + width + distance, rect.y + height / 2);\n      outDir.set(1, 0);\n      break;\n  }\n}\n\nfunction projectPointToArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y, out) {\n  x -= cx;\n  y -= cy;\n  var d = Math.sqrt(x * x + y * y);\n  x /= d;\n  y /= d; // Intersect point.\n\n  var ox = x * r + cx;\n  var oy = y * r + cy;\n\n  if (Math.abs(startAngle - endAngle) % PI2$6 < 1e-4) {\n    // Is a circle\n    out[0] = ox;\n    out[1] = oy;\n    return d - r;\n  }\n\n  if (anticlockwise) {\n    var tmp = startAngle;\n    startAngle = normalizeRadian(endAngle);\n    endAngle = normalizeRadian(tmp);\n  } else {\n    startAngle = normalizeRadian(startAngle);\n    endAngle = normalizeRadian(endAngle);\n  }\n\n  if (startAngle > endAngle) {\n    endAngle += PI2$6;\n  }\n\n  var angle = Math.atan2(y, x);\n\n  if (angle < 0) {\n    angle += PI2$6;\n  }\n\n  if (angle >= startAngle && angle <= endAngle || angle + PI2$6 >= startAngle && angle + PI2$6 <= endAngle) {\n    // Project point is on the arc.\n    out[0] = ox;\n    out[1] = oy;\n    return d - r;\n  }\n\n  var x1 = r * Math.cos(startAngle) + cx;\n  var y1 = r * Math.sin(startAngle) + cy;\n  var x2 = r * Math.cos(endAngle) + cx;\n  var y2 = r * Math.sin(endAngle) + cy;\n  var d1 = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);\n  var d2 = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y);\n\n  if (d1 < d2) {\n    out[0] = x1;\n    out[1] = y1;\n    return Math.sqrt(d1);\n  } else {\n    out[0] = x2;\n    out[1] = y2;\n    return Math.sqrt(d2);\n  }\n}\n\nfunction projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) {\n  var dx = x - x1;\n  var dy = y - y1;\n  var dx1 = x2 - x1;\n  var dy1 = y2 - y1;\n  var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n  dx1 /= lineLen;\n  dy1 /= lineLen; // dot product\n\n  var projectedLen = dx * dx1 + dy * dy1;\n  var t = projectedLen / lineLen;\n\n  if (limitToEnds) {\n    t = Math.min(Math.max(t, 0), 1);\n  }\n\n  t *= lineLen;\n  var ox = out[0] = x1 + t * dx1;\n  var oy = out[1] = y1 + t * dy1;\n  return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));\n}\n\nfunction projectPointToRect(x1, y1, width, height, x, y, out) {\n  if (width < 0) {\n    x1 = x1 + width;\n    width = -width;\n  }\n\n  if (height < 0) {\n    y1 = y1 + height;\n    height = -height;\n  }\n\n  var x2 = x1 + width;\n  var y2 = y1 + height;\n  var ox = out[0] = Math.min(Math.max(x, x1), x2);\n  var oy = out[1] = Math.min(Math.max(y, y1), y2);\n  return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));\n}\n\nvar tmpPt = [];\n\nfunction nearestPointOnRect(pt, rect, out) {\n  var dist = projectPointToRect(rect.x, rect.y, rect.width, rect.height, pt.x, pt.y, tmpPt);\n  out.set(tmpPt[0], tmpPt[1]);\n  return dist;\n}\n/**\n * Calculate min distance corresponding point.\n * This method won't evaluate if point is in the path.\n */\n\n\nfunction nearestPointOnPath(pt, path, out) {\n  var xi = 0;\n  var yi = 0;\n  var x0 = 0;\n  var y0 = 0;\n  var x1;\n  var y1;\n  var minDist = Infinity;\n  var data = path.data;\n  var x = pt.x;\n  var y = pt.y;\n\n  for (var i = 0; i < data.length;) {\n    var cmd = data[i++];\n\n    if (i === 1) {\n      xi = data[i];\n      yi = data[i + 1];\n      x0 = xi;\n      y0 = yi;\n    }\n\n    var d = minDist;\n\n    switch (cmd) {\n      case CMD$3.M:\n        // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点\n        // 在 closePath 的时候使用\n        x0 = data[i++];\n        y0 = data[i++];\n        xi = x0;\n        yi = y0;\n        break;\n\n      case CMD$3.L:\n        d = projectPointToLine(xi, yi, data[i], data[i + 1], x, y, tmpPt, true);\n        xi = data[i++];\n        yi = data[i++];\n        break;\n\n      case CMD$3.C:\n        d = cubicProjectPoint(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt);\n        xi = data[i++];\n        yi = data[i++];\n        break;\n\n      case CMD$3.Q:\n        d = quadraticProjectPoint(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt);\n        xi = data[i++];\n        yi = data[i++];\n        break;\n\n      case CMD$3.A:\n        // TODO Arc 判断的开销比较大\n        var cx = data[i++];\n        var cy = data[i++];\n        var rx = data[i++];\n        var ry = data[i++];\n        var theta = data[i++];\n        var dTheta = data[i++]; // TODO Arc 旋转\n\n        i += 1;\n        var anticlockwise = !!(1 - data[i++]);\n        x1 = Math.cos(theta) * rx + cx;\n        y1 = Math.sin(theta) * ry + cy; // 不是直接使用 arc 命令\n\n        if (i <= 1) {\n          // 第一个命令起点还未定义\n          x0 = x1;\n          y0 = y1;\n        } // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放\n\n\n        var _x = (x - cx) * ry / rx + cx;\n\n        d = projectPointToArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y, tmpPt);\n        xi = Math.cos(theta + dTheta) * rx + cx;\n        yi = Math.sin(theta + dTheta) * ry + cy;\n        break;\n\n      case CMD$3.R:\n        x0 = xi = data[i++];\n        y0 = yi = data[i++];\n        var width = data[i++];\n        var height = data[i++];\n        d = projectPointToRect(x0, y0, width, height, x, y, tmpPt);\n        break;\n\n      case CMD$3.Z:\n        d = projectPointToLine(xi, yi, x0, y0, x, y, tmpPt, true);\n        xi = x0;\n        yi = y0;\n        break;\n    }\n\n    if (d < minDist) {\n      minDist = d;\n      out.set(tmpPt[0], tmpPt[1]);\n    }\n  }\n\n  return minDist;\n} // Temporal variable for intermediate usage.\n\n\nvar pt0 = new Point();\nvar pt1 = new Point();\nvar pt2 = new Point();\nvar dir = new Point();\nvar dir2 = new Point();\n/**\n * Calculate a proper guide line based on the label position and graphic element definition\n * @param label\n * @param labelRect\n * @param target\n * @param targetRect\n */\n\nfunction updateLabelLinePoints(target, labelLineModel) {\n  if (!target) {\n    return;\n  }\n\n  var labelLine = target.getTextGuideLine();\n  var label = target.getTextContent(); // Needs to create text guide in each charts.\n\n  if (!(label && labelLine)) {\n    return;\n  }\n\n  var labelGuideConfig = target.textGuideLineConfig || {};\n  var points = [[0, 0], [0, 0], [0, 0]];\n  var searchSpace = labelGuideConfig.candidates || DEFAULT_SEARCH_SPACE;\n  var labelRect = label.getBoundingRect().clone();\n  labelRect.applyTransform(label.getComputedTransform());\n  var minDist = Infinity;\n  var anchorPoint = labelGuideConfig.anchor;\n  var targetTransform = target.getComputedTransform();\n  var targetInversedTransform = targetTransform && invert([], targetTransform);\n  var len = labelLineModel.get('length2') || 0;\n\n  if (anchorPoint) {\n    pt2.copy(anchorPoint);\n  }\n\n  for (var i = 0; i < searchSpace.length; i++) {\n    var candidate = searchSpace[i];\n    getCandidateAnchor(candidate, 0, labelRect, pt0, dir);\n    Point.scaleAndAdd(pt1, pt0, dir, len); // Transform to target coord space.\n\n    pt1.transform(targetInversedTransform); // Note: getBoundingRect will ensure the `path` being created.\n\n    var boundingRect = target.getBoundingRect();\n    var dist = anchorPoint ? anchorPoint.distance(pt1) : target instanceof Path ? nearestPointOnPath(pt1, target.path, pt2) : nearestPointOnRect(pt1, boundingRect, pt2); // TODO pt2 is in the path\n\n    if (dist < minDist) {\n      minDist = dist; // Transform back to global space.\n\n      pt1.transform(targetTransform);\n      pt2.transform(targetTransform);\n      pt2.toArray(points[0]);\n      pt1.toArray(points[1]);\n      pt0.toArray(points[2]);\n    }\n  }\n\n  limitTurnAngle(points, labelLineModel.get('minTurnAngle'));\n  labelLine.setShape({\n    points: points\n  });\n} // Temporal variable for the limitTurnAngle function\n\nvar tmpArr = [];\nvar tmpProjPoint = new Point();\n/**\n * Reduce the line segment attached to the label to limit the turn angle between two segments.\n * @param linePoints\n * @param minTurnAngle Radian of minimum turn angle. 0 - 180\n */\n\nfunction limitTurnAngle(linePoints, minTurnAngle) {\n  if (!(minTurnAngle <= 180 && minTurnAngle > 0)) {\n    return;\n  }\n\n  minTurnAngle = minTurnAngle / 180 * Math.PI; // The line points can be\n  //      /pt1----pt2 (label)\n  //     /\n  // pt0/\n\n  pt0.fromArray(linePoints[0]);\n  pt1.fromArray(linePoints[1]);\n  pt2.fromArray(linePoints[2]);\n  Point.sub(dir, pt0, pt1);\n  Point.sub(dir2, pt2, pt1);\n  var len1 = dir.len();\n  var len2 = dir2.len();\n\n  if (len1 < 1e-3 || len2 < 1e-3) {\n    return;\n  }\n\n  dir.scale(1 / len1);\n  dir2.scale(1 / len2);\n  var angleCos = dir.dot(dir2);\n  var minTurnAngleCos = Math.cos(minTurnAngle);\n\n  if (minTurnAngleCos < angleCos) {\n    // Smaller than minTurnAngle\n    // Calculate project point of pt0 on pt1-pt2\n    var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n    tmpProjPoint.fromArray(tmpArr); // Calculate new projected length with limited minTurnAngle and get the new connect point\n\n    tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n    var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n    if (isNaN(t)) {\n      return;\n    }\n\n    if (t < 0) {\n      Point.copy(tmpProjPoint, pt1);\n    } else if (t > 1) {\n      Point.copy(tmpProjPoint, pt2);\n    }\n\n    tmpProjPoint.toArray(linePoints[1]);\n  }\n}\n/**\n * Limit the angle of line and the surface\n * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite\n */\n\nfunction limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) {\n  if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {\n    return;\n  }\n\n  maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI;\n  pt0.fromArray(linePoints[0]);\n  pt1.fromArray(linePoints[1]);\n  pt2.fromArray(linePoints[2]);\n  Point.sub(dir, pt1, pt0);\n  Point.sub(dir2, pt2, pt1);\n  var len1 = dir.len();\n  var len2 = dir2.len();\n\n  if (len1 < 1e-3 || len2 < 1e-3) {\n    return;\n  }\n\n  dir.scale(1 / len1);\n  dir2.scale(1 / len2);\n  var angleCos = dir.dot(surfaceNormal);\n  var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle);\n\n  if (angleCos < maxSurfaceAngleCos) {\n    // Calculate project point of pt0 on pt1-pt2\n    var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n    tmpProjPoint.fromArray(tmpArr);\n    var HALF_PI = Math.PI / 2;\n    var angle2 = Math.acos(dir2.dot(surfaceNormal));\n    var newAngle = HALF_PI + angle2 - maxSurfaceAngle;\n\n    if (newAngle >= HALF_PI) {\n      // parallel\n      Point.copy(tmpProjPoint, pt2);\n    } else {\n      // Calculate new projected length with limited minTurnAngle and get the new connect point\n      tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n      var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n      if (isNaN(t)) {\n        return;\n      }\n\n      if (t < 0) {\n        Point.copy(tmpProjPoint, pt1);\n      } else if (t > 1) {\n        Point.copy(tmpProjPoint, pt2);\n      }\n    }\n\n    tmpProjPoint.toArray(linePoints[1]);\n  }\n}\n\nfunction setLabelLineState(labelLine, ignore, stateName, stateModel) {\n  var isNormal = stateName === 'normal';\n  var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); // Make sure display.\n\n  stateObj.ignore = ignore; // Set smooth\n\n  var smooth = stateModel.get('smooth');\n\n  if (smooth && smooth === true) {\n    smooth = 0.3;\n  }\n\n  stateObj.shape = stateObj.shape || {};\n\n  if (smooth > 0) {\n    stateObj.shape.smooth = smooth;\n  }\n\n  var styleObj = stateModel.getModel('lineStyle').getLineStyle();\n  isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj;\n}\n\nfunction buildLabelLinePath(path, shape) {\n  var smooth = shape.smooth;\n  var points = shape.points;\n\n  if (!points) {\n    return;\n  }\n\n  path.moveTo(points[0][0], points[0][1]);\n\n  if (smooth > 0 && points.length >= 3) {\n    var len1 = dist(points[0], points[1]);\n    var len2 = dist(points[1], points[2]);\n\n    if (!len1 || !len2) {\n      path.lineTo(points[1][0], points[1][1]);\n      path.lineTo(points[2][0], points[2][1]);\n      return;\n    }\n\n    var moveLen = Math.min(len1, len2) * smooth;\n    var midPoint0 = lerp([], points[1], points[0], moveLen / len1);\n    var midPoint2 = lerp([], points[1], points[2], moveLen / len2);\n    var midPoint1 = lerp([], midPoint0, midPoint2, 0.5);\n    path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]);\n    path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]);\n  } else {\n    for (var i = 1; i < points.length; i++) {\n      path.lineTo(points[i][0], points[i][1]);\n    }\n  }\n}\n/**\n * Create a label line if necessary and set it's style.\n */\n\n\nfunction setLabelLineStyle(targetEl, statesModels, defaultStyle) {\n  var labelLine = targetEl.getTextGuideLine();\n  var label = targetEl.getTextContent();\n\n  if (!label) {\n    // Not show label line if there is no label.\n    if (labelLine) {\n      targetEl.removeTextGuideLine();\n    }\n\n    return;\n  }\n\n  var normalModel = statesModels.normal;\n  var showNormal = normalModel.get('show');\n  var labelIgnoreNormal = label.ignore;\n\n  for (var i = 0; i < DISPLAY_STATES.length; i++) {\n    var stateName = DISPLAY_STATES[i];\n    var stateModel = statesModels[stateName];\n    var isNormal = stateName === 'normal';\n\n    if (stateModel) {\n      var stateShow = stateModel.get('show');\n      var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal);\n\n      if (isLabelIgnored // Not show when label is not shown in this state.\n      || !retrieve2(stateShow, showNormal) // Use normal state by default if not set.\n      ) {\n          var stateObj = isNormal ? labelLine : labelLine && labelLine.states[stateName];\n\n          if (stateObj) {\n            stateObj.ignore = true;\n          }\n\n          continue;\n        } // Create labelLine if not exists\n\n\n      if (!labelLine) {\n        labelLine = new Polyline();\n        targetEl.setTextGuideLine(labelLine); // Reset state of normal because it's new created.\n        // NOTE: NORMAL should always been the first!\n\n        if (!isNormal && (labelIgnoreNormal || !showNormal)) {\n          setLabelLineState(labelLine, true, 'normal', statesModels.normal);\n        } // Use same state proxy.\n\n\n        if (targetEl.stateProxy) {\n          labelLine.stateProxy = targetEl.stateProxy;\n        }\n      }\n\n      setLabelLineState(labelLine, false, stateName, stateModel);\n    }\n  }\n\n  if (labelLine) {\n    defaults(labelLine.style, defaultStyle); // Not fill.\n\n    labelLine.style.fill = null;\n    var showAbove = normalModel.get('showAbove');\n    var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {};\n    labelLineConfig.showAbove = showAbove || false; // Custom the buildPath.\n\n    labelLine.buildPath = buildLabelLinePath;\n  }\n}\nfunction getLabelLineStatesModels(itemModel, labelLineName) {\n  labelLineName = labelLineName || 'labelLine';\n  var statesModels = {\n    normal: itemModel.getModel(labelLineName)\n  };\n\n  for (var i = 0; i < SPECIAL_STATES.length; i++) {\n    var stateName = SPECIAL_STATES[i];\n    statesModels[stateName] = itemModel.getModel([stateName, labelLineName]);\n  }\n\n  return statesModels;\n}\n\nfunction prepareLayoutList(input) {\n  var list = [];\n\n  for (var i = 0; i < input.length; i++) {\n    var rawItem = input[i];\n\n    if (rawItem.defaultAttr.ignore) {\n      continue;\n    }\n\n    var label = rawItem.label;\n    var transform = label.getComputedTransform(); // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el.\n\n    var localRect = label.getBoundingRect();\n    var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5;\n    var minMargin = label.style.margin || 0;\n    var globalRect = localRect.clone();\n    globalRect.applyTransform(transform);\n    globalRect.x -= minMargin / 2;\n    globalRect.y -= minMargin / 2;\n    globalRect.width += minMargin;\n    globalRect.height += minMargin;\n    var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null;\n    list.push({\n      label: label,\n      labelLine: rawItem.labelLine,\n      rect: globalRect,\n      localRect: localRect,\n      obb: obb,\n      priority: rawItem.priority,\n      defaultAttr: rawItem.defaultAttr,\n      layoutOption: rawItem.computedLayoutOption,\n      axisAligned: isAxisAligned,\n      transform: transform\n    });\n  }\n\n  return list;\n}\n\nfunction shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) {\n  var len = list.length;\n\n  if (len < 2) {\n    return;\n  }\n\n  list.sort(function (a, b) {\n    return a.rect[xyDim] - b.rect[xyDim];\n  });\n  var lastPos = 0;\n  var delta;\n  var adjusted = false;\n  var totalShifts = 0;\n\n  for (var i = 0; i < len; i++) {\n    var item = list[i];\n    var rect = item.rect;\n    delta = rect[xyDim] - lastPos;\n\n    if (delta < 0) {\n      // shiftForward(i, len, -delta);\n      rect[xyDim] -= delta;\n      item.label[xyDim] -= delta;\n      adjusted = true;\n    }\n\n    var shift = Math.max(-delta, 0);\n    totalShifts += shift;\n    lastPos = rect[xyDim] + rect[sizeDim];\n  }\n\n  if (totalShifts > 0 && balanceShift) {\n    // Shift back to make the distribution more equally.\n    shiftList(-totalShifts / len, 0, len);\n  } // TODO bleedMargin?\n\n\n  var first = list[0];\n  var last = list[len - 1];\n  var minGap;\n  var maxGap;\n  updateMinMaxGap(); // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds.\n\n  minGap < 0 && squeezeGaps(-minGap, 0.8);\n  maxGap < 0 && squeezeGaps(maxGap, 0.8);\n  updateMinMaxGap();\n  takeBoundsGap(minGap, maxGap, 1);\n  takeBoundsGap(maxGap, minGap, -1); // Handle bailout when there is not enough space.\n\n  updateMinMaxGap();\n\n  if (minGap < 0) {\n    squeezeWhenBailout(-minGap);\n  }\n\n  if (maxGap < 0) {\n    squeezeWhenBailout(maxGap);\n  }\n\n  function updateMinMaxGap() {\n    minGap = first.rect[xyDim] - minBound;\n    maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim];\n  }\n\n  function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) {\n    if (gapThisBound < 0) {\n      // Move from other gap if can.\n      var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound);\n\n      if (moveFromMaxGap > 0) {\n        shiftList(moveFromMaxGap * moveDir, 0, len);\n        var remained = moveFromMaxGap + gapThisBound;\n\n        if (remained < 0) {\n          squeezeGaps(-remained * moveDir, 1);\n        }\n      } else {\n        squeezeGaps(-gapThisBound * moveDir, 1);\n      }\n    }\n  }\n\n  function shiftList(delta, start, end) {\n    if (delta !== 0) {\n      adjusted = true;\n    }\n\n    for (var i = start; i < end; i++) {\n      var item = list[i];\n      var rect = item.rect;\n      rect[xyDim] += delta;\n      item.label[xyDim] += delta;\n    }\n  } // Squeeze gaps if the labels exceed margin.\n\n\n  function squeezeGaps(delta, maxSqeezePercent) {\n    var gaps = [];\n    var totalGaps = 0;\n\n    for (var i = 1; i < len; i++) {\n      var prevItemRect = list[i - 1].rect;\n      var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0);\n      gaps.push(gap);\n      totalGaps += gap;\n    }\n\n    if (!totalGaps) {\n      return;\n    }\n\n    var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent);\n\n    if (delta > 0) {\n      for (var i = 0; i < len - 1; i++) {\n        // Distribute the shift delta to all gaps.\n        var movement = gaps[i] * squeezePercent; // Forward\n\n        shiftList(movement, 0, i + 1);\n      }\n    } else {\n      // Backward\n      for (var i = len - 1; i > 0; i--) {\n        // Distribute the shift delta to all gaps.\n        var movement = gaps[i - 1] * squeezePercent;\n        shiftList(-movement, i, len);\n      }\n    }\n  }\n  /**\n   * Squeeze to allow overlap if there is no more space available.\n   * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds.\n   */\n\n\n  function squeezeWhenBailout(delta) {\n    var dir = delta < 0 ? -1 : 1;\n    delta = Math.abs(delta);\n    var moveForEachLabel = Math.ceil(delta / (len - 1));\n\n    for (var i = 0; i < len - 1; i++) {\n      if (dir > 0) {\n        // Forward\n        shiftList(moveForEachLabel, 0, i + 1);\n      } else {\n        // Backward\n        shiftList(-moveForEachLabel, len - i - 1, len);\n      }\n\n      delta -= moveForEachLabel;\n\n      if (delta <= 0) {\n        return;\n      }\n    }\n  }\n\n  return adjusted;\n}\n/**\n * Adjust labels on x direction to avoid overlap.\n */\n\n\nfunction shiftLayoutOnX(list, leftBound, rightBound, // If average the shifts on all labels and add them to 0\n// TODO: Not sure if should enable it.\n// Pros: The angle of lines will distribute more equally\n// Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly.\nbalanceShift) {\n  return shiftLayout(list, 'x', 'width', leftBound, rightBound, balanceShift);\n}\n/**\n * Adjust labels on y direction to avoid overlap.\n */\n\nfunction shiftLayoutOnY(list, topBound, bottomBound, // If average the shifts on all labels and add them to 0\nbalanceShift) {\n  return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift);\n}\nfunction hideOverlap(labelList) {\n  var displayedLabels = []; // TODO, render overflow visible first, put in the displayedLabels.\n\n  labelList.sort(function (a, b) {\n    return b.priority - a.priority;\n  });\n  var globalRect = new BoundingRect(0, 0, 0, 0);\n\n  function hideEl(el) {\n    if (!el.ignore) {\n      // Show on emphasis.\n      var emphasisState = el.ensureState('emphasis');\n\n      if (emphasisState.ignore == null) {\n        emphasisState.ignore = false;\n      }\n    }\n\n    el.ignore = true;\n  }\n\n  for (var i = 0; i < labelList.length; i++) {\n    var labelItem = labelList[i];\n    var isAxisAligned = labelItem.axisAligned;\n    var localRect = labelItem.localRect;\n    var transform = labelItem.transform;\n    var label = labelItem.label;\n    var labelLine = labelItem.labelLine;\n    globalRect.copy(labelItem.rect); // Add a threshold because layout may be aligned precisely.\n\n    globalRect.width -= 0.1;\n    globalRect.height -= 0.1;\n    globalRect.x += 0.05;\n    globalRect.y += 0.05;\n    var obb = labelItem.obb;\n    var overlapped = false;\n\n    for (var j = 0; j < displayedLabels.length; j++) {\n      var existsTextCfg = displayedLabels[j]; // Fast rejection.\n\n      if (!globalRect.intersect(existsTextCfg.rect)) {\n        continue;\n      }\n\n      if (isAxisAligned && existsTextCfg.axisAligned) {\n        // Is overlapped\n        overlapped = true;\n        break;\n      }\n\n      if (!existsTextCfg.obb) {\n        // If self is not axis aligned. But other is.\n        existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform);\n      }\n\n      if (!obb) {\n        // If self is axis aligned. But other is not.\n        obb = new OrientedBoundingRect(localRect, transform);\n      }\n\n      if (obb.intersect(existsTextCfg.obb)) {\n        overlapped = true;\n        break;\n      }\n    } // TODO Callback to determine if this overlap should be handled?\n\n\n    if (overlapped) {\n      hideEl(label);\n      labelLine && hideEl(labelLine);\n    } else {\n      label.attr('ignore', labelItem.defaultAttr.ignore);\n      labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore);\n      displayedLabels.push(labelItem);\n    }\n  }\n}\n\nfunction cloneArr(points) {\n  if (points) {\n    var newPoints = [];\n\n    for (var i = 0; i < points.length; i++) {\n      newPoints.push(points[i].slice());\n    }\n\n    return newPoints;\n  }\n}\n\nfunction prepareLayoutCallbackParams(labelItem, hostEl) {\n  var label = labelItem.label;\n  var labelLine = hostEl && hostEl.getTextGuideLine();\n  return {\n    dataIndex: labelItem.dataIndex,\n    dataType: labelItem.dataType,\n    seriesIndex: labelItem.seriesModel.seriesIndex,\n    text: labelItem.label.style.text,\n    rect: labelItem.hostRect,\n    labelRect: labelItem.rect,\n    // x: labelAttr.x,\n    // y: labelAttr.y,\n    align: label.style.align,\n    verticalAlign: label.style.verticalAlign,\n    labelLinePoints: cloneArr(labelLine && labelLine.shape.points)\n  };\n}\n\nvar LABEL_OPTION_TO_STYLE_KEYS = ['align', 'verticalAlign', 'width', 'height', 'fontSize'];\nvar dummyTransformable = new Transformable();\nvar labelLayoutInnerStore = makeInner();\nvar labelLineAnimationStore = makeInner();\n\nfunction extendWithKeys(target, source, keys) {\n  for (var i = 0; i < keys.length; i++) {\n    var key = keys[i];\n\n    if (source[key] != null) {\n      target[key] = source[key];\n    }\n  }\n}\n\nvar LABEL_LAYOUT_PROPS = ['x', 'y', 'rotation'];\n\nvar LabelManager =\n/** @class */\nfunction () {\n  function LabelManager() {\n    this._labelList = [];\n    this._chartViewList = [];\n  }\n\n  LabelManager.prototype.clearLabels = function () {\n    this._labelList = [];\n    this._chartViewList = [];\n  };\n  /**\n   * Add label to manager\n   */\n\n\n  LabelManager.prototype._addLabel = function (dataIndex, dataType, seriesModel, label, layoutOption) {\n    var labelStyle = label.style;\n    var hostEl = label.__hostTarget;\n    var textConfig = hostEl.textConfig || {}; // TODO: If label is in other state.\n\n    var labelTransform = label.getComputedTransform();\n    var labelRect = label.getBoundingRect().plain();\n    BoundingRect.applyTransform(labelRect, labelRect, labelTransform);\n\n    if (labelTransform) {\n      dummyTransformable.setLocalTransform(labelTransform);\n    } else {\n      // Identity transform.\n      dummyTransformable.x = dummyTransformable.y = dummyTransformable.rotation = dummyTransformable.originX = dummyTransformable.originY = 0;\n      dummyTransformable.scaleX = dummyTransformable.scaleY = 1;\n    }\n\n    var host = label.__hostTarget;\n    var hostRect;\n\n    if (host) {\n      hostRect = host.getBoundingRect().plain();\n      var transform = host.getComputedTransform();\n      BoundingRect.applyTransform(hostRect, hostRect, transform);\n    }\n\n    var labelGuide = hostRect && host.getTextGuideLine();\n\n    this._labelList.push({\n      label: label,\n      labelLine: labelGuide,\n      seriesModel: seriesModel,\n      dataIndex: dataIndex,\n      dataType: dataType,\n      layoutOption: layoutOption,\n      computedLayoutOption: null,\n      rect: labelRect,\n      hostRect: hostRect,\n      // Label with lower priority will be hidden when overlapped\n      // Use rect size as default priority\n      priority: hostRect ? hostRect.width * hostRect.height : 0,\n      // Save default label attributes.\n      // For restore if developers want get back to default value in callback.\n      defaultAttr: {\n        ignore: label.ignore,\n        labelGuideIgnore: labelGuide && labelGuide.ignore,\n        x: dummyTransformable.x,\n        y: dummyTransformable.y,\n        scaleX: dummyTransformable.scaleX,\n        scaleY: dummyTransformable.scaleY,\n        rotation: dummyTransformable.rotation,\n        style: {\n          x: labelStyle.x,\n          y: labelStyle.y,\n          align: labelStyle.align,\n          verticalAlign: labelStyle.verticalAlign,\n          width: labelStyle.width,\n          height: labelStyle.height,\n          fontSize: labelStyle.fontSize\n        },\n        cursor: label.cursor,\n        attachedPos: textConfig.position,\n        attachedRot: textConfig.rotation\n      }\n    });\n  };\n\n  LabelManager.prototype.addLabelsOfSeries = function (chartView) {\n    var _this = this;\n\n    this._chartViewList.push(chartView);\n\n    var seriesModel = chartView.__model;\n    var layoutOption = seriesModel.get('labelLayout');\n    /**\n     * Ignore layouting if it's not specified anything.\n     */\n\n    if (!(isFunction(layoutOption) || keys(layoutOption).length)) {\n      return;\n    }\n\n    chartView.group.traverse(function (child) {\n      if (child.ignore) {\n        return true; // Stop traverse descendants.\n      } // Only support label being hosted on graphic elements.\n\n\n      var textEl = child.getTextContent();\n      var ecData = getECData(child); // Can only attach the text on the element with dataIndex\n\n      if (textEl && !textEl.disableLabelLayout) {\n        _this._addLabel(ecData.dataIndex, ecData.dataType, seriesModel, textEl, layoutOption);\n      }\n    });\n  };\n\n  LabelManager.prototype.updateLayoutConfig = function (api) {\n    var width = api.getWidth();\n    var height = api.getHeight();\n\n    function createDragHandler(el, labelLineModel) {\n      return function () {\n        updateLabelLinePoints(el, labelLineModel);\n      };\n    }\n\n    for (var i = 0; i < this._labelList.length; i++) {\n      var labelItem = this._labelList[i];\n      var label = labelItem.label;\n      var hostEl = label.__hostTarget;\n      var defaultLabelAttr = labelItem.defaultAttr;\n      var layoutOption = void 0; // TODO A global layout option?\n\n      if (isFunction(labelItem.layoutOption)) {\n        layoutOption = labelItem.layoutOption(prepareLayoutCallbackParams(labelItem, hostEl));\n      } else {\n        layoutOption = labelItem.layoutOption;\n      }\n\n      layoutOption = layoutOption || {};\n      labelItem.computedLayoutOption = layoutOption;\n      var degreeToRadian = Math.PI / 180; // TODO hostEl should always exists.\n      // Or label should not have parent because the x, y is all in global space.\n\n      if (hostEl) {\n        hostEl.setTextConfig({\n          // Force to set local false.\n          local: false,\n          // Ignore position and rotation config on the host el if x or y is changed.\n          position: layoutOption.x != null || layoutOption.y != null ? null : defaultLabelAttr.attachedPos,\n          // Ignore rotation config on the host el if rotation is changed.\n          rotation: layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.attachedRot,\n          offset: [layoutOption.dx || 0, layoutOption.dy || 0]\n        });\n      }\n\n      var needsUpdateLabelLine = false;\n\n      if (layoutOption.x != null) {\n        // TODO width of chart view.\n        label.x = parsePercent$1(layoutOption.x, width);\n        label.setStyle('x', 0); // Ignore movement in style. TODO: origin.\n\n        needsUpdateLabelLine = true;\n      } else {\n        label.x = defaultLabelAttr.x;\n        label.setStyle('x', defaultLabelAttr.style.x);\n      }\n\n      if (layoutOption.y != null) {\n        // TODO height of chart view.\n        label.y = parsePercent$1(layoutOption.y, height);\n        label.setStyle('y', 0); // Ignore movement in style.\n\n        needsUpdateLabelLine = true;\n      } else {\n        label.y = defaultLabelAttr.y;\n        label.setStyle('y', defaultLabelAttr.style.y);\n      }\n\n      if (layoutOption.labelLinePoints) {\n        var guideLine = hostEl.getTextGuideLine();\n\n        if (guideLine) {\n          guideLine.setShape({\n            points: layoutOption.labelLinePoints\n          }); // Not update\n\n          needsUpdateLabelLine = false;\n        }\n      }\n\n      var labelLayoutStore = labelLayoutInnerStore(label);\n      labelLayoutStore.needsUpdateLabelLine = needsUpdateLabelLine;\n      label.rotation = layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.rotation;\n      label.scaleX = defaultLabelAttr.scaleX;\n      label.scaleY = defaultLabelAttr.scaleY;\n\n      for (var k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) {\n        var key = LABEL_OPTION_TO_STYLE_KEYS[k];\n        label.setStyle(key, layoutOption[key] != null ? layoutOption[key] : defaultLabelAttr.style[key]);\n      }\n\n      if (layoutOption.draggable) {\n        label.draggable = true;\n        label.cursor = 'move';\n\n        if (hostEl) {\n          var hostModel = labelItem.seriesModel;\n\n          if (labelItem.dataIndex != null) {\n            var data = labelItem.seriesModel.getData(labelItem.dataType);\n            hostModel = data.getItemModel(labelItem.dataIndex);\n          }\n\n          label.on('drag', createDragHandler(hostEl, hostModel.getModel('labelLine')));\n        }\n      } else {\n        // TODO Other drag functions?\n        label.off('drag');\n        label.cursor = defaultLabelAttr.cursor;\n      }\n    }\n  };\n\n  LabelManager.prototype.layout = function (api) {\n    var width = api.getWidth();\n    var height = api.getHeight();\n    var labelList = prepareLayoutList(this._labelList);\n    var labelsNeedsAdjustOnX = filter(labelList, function (item) {\n      return item.layoutOption.moveOverlap === 'shiftX';\n    });\n    var labelsNeedsAdjustOnY = filter(labelList, function (item) {\n      return item.layoutOption.moveOverlap === 'shiftY';\n    });\n    shiftLayoutOnX(labelsNeedsAdjustOnX, 0, width);\n    shiftLayoutOnY(labelsNeedsAdjustOnY, 0, height);\n    var labelsNeedsHideOverlap = filter(labelList, function (item) {\n      return item.layoutOption.hideOverlap;\n    });\n    hideOverlap(labelsNeedsHideOverlap);\n  };\n  /**\n   * Process all labels. Not only labels with layoutOption.\n   */\n\n\n  LabelManager.prototype.processLabelsOverall = function () {\n    var _this = this;\n\n    each(this._chartViewList, function (chartView) {\n      var seriesModel = chartView.__model;\n      var ignoreLabelLineUpdate = chartView.ignoreLabelLineUpdate;\n      var animationEnabled = seriesModel.isAnimationEnabled();\n      chartView.group.traverse(function (child) {\n        if (child.ignore && !child.forceLabelAnimation) {\n          return true; // Stop traverse descendants.\n        }\n\n        var needsUpdateLabelLine = !ignoreLabelLineUpdate;\n        var label = child.getTextContent();\n\n        if (!needsUpdateLabelLine && label) {\n          needsUpdateLabelLine = labelLayoutInnerStore(label).needsUpdateLabelLine;\n        }\n\n        if (needsUpdateLabelLine) {\n          _this._updateLabelLine(child, seriesModel);\n        }\n\n        if (animationEnabled) {\n          _this._animateLabels(child, seriesModel);\n        }\n      });\n    });\n  };\n\n  LabelManager.prototype._updateLabelLine = function (el, seriesModel) {\n    // Only support label being hosted on graphic elements.\n    var textEl = el.getTextContent(); // Update label line style.\n\n    var ecData = getECData(el);\n    var dataIndex = ecData.dataIndex; // Only support labelLine on the labels represent data.\n\n    if (textEl && dataIndex != null) {\n      var data = seriesModel.getData(ecData.dataType);\n      var itemModel = data.getItemModel(dataIndex);\n      var defaultStyle = {};\n      var visualStyle = data.getItemVisual(dataIndex, 'style');\n      var visualType = data.getVisual('drawType'); // Default to be same with main color\n\n      defaultStyle.stroke = visualStyle[visualType];\n      var labelLineModel = itemModel.getModel('labelLine');\n      setLabelLineStyle(el, getLabelLineStatesModels(itemModel), defaultStyle);\n      updateLabelLinePoints(el, labelLineModel);\n    }\n  };\n\n  LabelManager.prototype._animateLabels = function (el, seriesModel) {\n    var textEl = el.getTextContent();\n    var guideLine = el.getTextGuideLine(); // Animate\n\n    if (textEl // `forceLabelAnimation` has the highest priority\n    && (el.forceLabelAnimation || !textEl.ignore && !textEl.invisible && !el.disableLabelAnimation && !isElementRemoved(el))) {\n      var layoutStore = labelLayoutInnerStore(textEl);\n      var oldLayout = layoutStore.oldLayout;\n      var ecData = getECData(el);\n      var dataIndex = ecData.dataIndex;\n      var newProps = {\n        x: textEl.x,\n        y: textEl.y,\n        rotation: textEl.rotation\n      };\n      var data = seriesModel.getData(ecData.dataType);\n\n      if (!oldLayout) {\n        textEl.attr(newProps); // Disable fade in animation if value animation is enabled.\n\n        if (!labelInner(textEl).valueAnimation) {\n          var oldOpacity = retrieve2(textEl.style.opacity, 1); // Fade in animation\n\n          textEl.style.opacity = 0;\n          initProps(textEl, {\n            style: {\n              opacity: oldOpacity\n            }\n          }, seriesModel, dataIndex);\n        }\n      } else {\n        textEl.attr(oldLayout); // Make sure the animation from is in the right status.\n\n        var prevStates = el.prevStates;\n\n        if (prevStates) {\n          if (indexOf(prevStates, 'select') >= 0) {\n            textEl.attr(layoutStore.oldLayoutSelect);\n          }\n\n          if (indexOf(prevStates, 'emphasis') >= 0) {\n            textEl.attr(layoutStore.oldLayoutEmphasis);\n          }\n        }\n\n        updateProps(textEl, newProps, seriesModel, dataIndex);\n      }\n\n      layoutStore.oldLayout = newProps;\n\n      if (textEl.states.select) {\n        var layoutSelect = layoutStore.oldLayoutSelect = {};\n        extendWithKeys(layoutSelect, newProps, LABEL_LAYOUT_PROPS);\n        extendWithKeys(layoutSelect, textEl.states.select, LABEL_LAYOUT_PROPS);\n      }\n\n      if (textEl.states.emphasis) {\n        var layoutEmphasis = layoutStore.oldLayoutEmphasis = {};\n        extendWithKeys(layoutEmphasis, newProps, LABEL_LAYOUT_PROPS);\n        extendWithKeys(layoutEmphasis, textEl.states.emphasis, LABEL_LAYOUT_PROPS);\n      }\n\n      animateLabelValue(textEl, dataIndex, data, seriesModel, seriesModel);\n    }\n\n    if (guideLine && !guideLine.ignore && !guideLine.invisible) {\n      var layoutStore = labelLineAnimationStore(guideLine);\n      var oldLayout = layoutStore.oldLayout;\n      var newLayout = {\n        points: guideLine.shape.points\n      };\n\n      if (!oldLayout) {\n        guideLine.setShape(newLayout);\n        guideLine.style.strokePercent = 0;\n        initProps(guideLine, {\n          style: {\n            strokePercent: 1\n          }\n        }, seriesModel);\n      } else {\n        guideLine.attr({\n          shape: oldLayout\n        });\n        updateProps(guideLine, {\n          shape: newLayout\n        }, seriesModel);\n      }\n\n      layoutStore.oldLayout = newLayout;\n    }\n  };\n\n  return LabelManager;\n}();\n\nvar getLabelManager = makeInner();\nfunction installLabelLayout(registers) {\n  registers.registerUpdateLifecycle('series:beforeupdate', function (ecModel, api, params) {\n    // TODO api provide an namespace that can save stuff per instance\n    var labelManager = getLabelManager(api).labelManager;\n\n    if (!labelManager) {\n      labelManager = getLabelManager(api).labelManager = new LabelManager();\n    }\n\n    labelManager.clearLabels();\n  });\n  registers.registerUpdateLifecycle('series:layoutlabels', function (ecModel, api, params) {\n    var labelManager = getLabelManager(api).labelManager;\n    params.updatedSeries.forEach(function (series) {\n      labelManager.addLabelsOfSeries(api.getViewOfSeriesModel(series));\n    });\n    labelManager.updateLayoutConfig(api);\n    labelManager.layout(api);\n    labelManager.processLabelsOverall();\n  });\n}\n\nvar mathSin$4 = Math.sin;\nvar mathCos$4 = Math.cos;\nvar PI$4 = Math.PI;\nvar PI2$7 = Math.PI * 2;\nvar degree = 180 / PI$4;\nvar SVGPathRebuilder = (function () {\n    function SVGPathRebuilder() {\n    }\n    SVGPathRebuilder.prototype.reset = function (precision) {\n        this._start = true;\n        this._d = [];\n        this._str = '';\n        this._p = Math.pow(10, precision || 4);\n    };\n    SVGPathRebuilder.prototype.moveTo = function (x, y) {\n        this._add('M', x, y);\n    };\n    SVGPathRebuilder.prototype.lineTo = function (x, y) {\n        this._add('L', x, y);\n    };\n    SVGPathRebuilder.prototype.bezierCurveTo = function (x, y, x2, y2, x3, y3) {\n        this._add('C', x, y, x2, y2, x3, y3);\n    };\n    SVGPathRebuilder.prototype.quadraticCurveTo = function (x, y, x2, y2) {\n        this._add('Q', x, y, x2, y2);\n    };\n    SVGPathRebuilder.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n        this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise);\n    };\n    SVGPathRebuilder.prototype.ellipse = function (cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise) {\n        var dTheta = endAngle - startAngle;\n        var clockwise = !anticlockwise;\n        var dThetaPositive = Math.abs(dTheta);\n        var isCircle = isAroundZero$1(dThetaPositive - PI2$7)\n            || (clockwise ? dTheta >= PI2$7 : -dTheta >= PI2$7);\n        var unifiedTheta = dTheta > 0 ? dTheta % PI2$7 : (dTheta % PI2$7 + PI2$7);\n        var large = false;\n        if (isCircle) {\n            large = true;\n        }\n        else if (isAroundZero$1(dThetaPositive)) {\n            large = false;\n        }\n        else {\n            large = (unifiedTheta >= PI$4) === !!clockwise;\n        }\n        var x0 = cx + rx * mathCos$4(startAngle);\n        var y0 = cy + ry * mathSin$4(startAngle);\n        if (this._start) {\n            this._add('M', x0, y0);\n        }\n        var xRot = Math.round(psi * degree);\n        if (isCircle) {\n            var p = 1 / this._p;\n            var dTheta_1 = (clockwise ? 1 : -1) * (PI2$7 - p);\n            this._add('A', rx, ry, xRot, 1, +clockwise, cx + rx * mathCos$4(startAngle + dTheta_1), cy + ry * mathSin$4(startAngle + dTheta_1));\n            if (p > 1e-2) {\n                this._add('A', rx, ry, xRot, 0, +clockwise, x0, y0);\n            }\n        }\n        else {\n            var x = cx + rx * mathCos$4(endAngle);\n            var y = cy + ry * mathSin$4(endAngle);\n            this._add('A', rx, ry, xRot, +large, +clockwise, x, y);\n        }\n    };\n    SVGPathRebuilder.prototype.rect = function (x, y, w, h) {\n        this._add('M', x, y);\n        this._add('l', w, 0);\n        this._add('l', 0, h);\n        this._add('l', -w, 0);\n        this._add('Z');\n    };\n    SVGPathRebuilder.prototype.closePath = function () {\n        if (this._d.length > 0) {\n            this._add('Z');\n        }\n    };\n    SVGPathRebuilder.prototype._add = function (cmd, a, b, c, d, e, f, g, h) {\n        var vals = [];\n        var p = this._p;\n        for (var i = 1; i < arguments.length; i++) {\n            var val = arguments[i];\n            if (isNaN(val)) {\n                this._invalid = true;\n                return;\n            }\n            vals.push(Math.round(val * p) / p);\n        }\n        this._d.push(cmd + vals.join(' '));\n        this._start = cmd === 'Z';\n    };\n    SVGPathRebuilder.prototype.generateStr = function () {\n        this._str = this._invalid ? '' : this._d.join('');\n        this._d = [];\n    };\n    SVGPathRebuilder.prototype.getStr = function () {\n        return this._str;\n    };\n    return SVGPathRebuilder;\n}());\n\nvar NONE = 'none';\nvar mathRound$1 = Math.round;\nfunction pathHasFill(style) {\n    var fill = style.fill;\n    return fill != null && fill !== NONE;\n}\nfunction pathHasStroke(style) {\n    var stroke = style.stroke;\n    return stroke != null && stroke !== NONE;\n}\nvar strokeProps = ['lineCap', 'miterLimit', 'lineJoin'];\nvar svgStrokeProps = map(strokeProps, function (prop) { return \"stroke-\" + prop.toLowerCase(); });\nfunction mapStyleToAttrs(updateAttr, style, el, forceUpdate) {\n    var opacity = style.opacity == null ? 1 : style.opacity;\n    if (el instanceof ZRImage) {\n        updateAttr('opacity', opacity);\n        return;\n    }\n    if (pathHasFill(style)) {\n        var fill = normalizeColor(style.fill);\n        updateAttr('fill', fill.color);\n        var fillOpacity = style.fillOpacity != null\n            ? style.fillOpacity * fill.opacity * opacity\n            : fill.opacity * opacity;\n        if (forceUpdate || fillOpacity < 1) {\n            updateAttr('fill-opacity', fillOpacity);\n        }\n    }\n    else {\n        updateAttr('fill', NONE);\n    }\n    if (pathHasStroke(style)) {\n        var stroke = normalizeColor(style.stroke);\n        updateAttr('stroke', stroke.color);\n        var strokeScale = style.strokeNoScale\n            ? el.getLineScale()\n            : 1;\n        var strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0);\n        var strokeOpacity = style.strokeOpacity != null\n            ? style.strokeOpacity * stroke.opacity * opacity\n            : stroke.opacity * opacity;\n        var strokeFirst = style.strokeFirst;\n        if (forceUpdate || strokeWidth !== 1) {\n            updateAttr('stroke-width', strokeWidth);\n        }\n        if (forceUpdate || strokeFirst) {\n            updateAttr('paint-order', strokeFirst ? 'stroke' : 'fill');\n        }\n        if (forceUpdate || strokeOpacity < 1) {\n            updateAttr('stroke-opacity', strokeOpacity);\n        }\n        if (style.lineDash) {\n            var _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n            if (lineDash) {\n                lineDashOffset = mathRound$1(lineDashOffset || 0);\n                updateAttr('stroke-dasharray', lineDash.join(','));\n                if (lineDashOffset || forceUpdate) {\n                    updateAttr('stroke-dashoffset', lineDashOffset);\n                }\n            }\n        }\n        else if (forceUpdate) {\n            updateAttr('stroke-dasharray', NONE);\n        }\n        for (var i = 0; i < strokeProps.length; i++) {\n            var propName = strokeProps[i];\n            if (forceUpdate || style[propName] !== DEFAULT_PATH_STYLE[propName]) {\n                var val = style[propName] || DEFAULT_PATH_STYLE[propName];\n                val && updateAttr(svgStrokeProps[i], val);\n            }\n        }\n    }\n    else if (forceUpdate) {\n        updateAttr('stroke', NONE);\n    }\n}\n\nvar SVGNS = 'http://www.w3.org/2000/svg';\nvar XLINKNS = 'http://www.w3.org/1999/xlink';\nvar XMLNS = 'http://www.w3.org/2000/xmlns/';\nvar XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';\nfunction createElement(name) {\n    return document.createElementNS(SVGNS, name);\n}\nfunction createVNode(tag, key, attrs, children, text) {\n    return {\n        tag: tag,\n        attrs: attrs || {},\n        children: children,\n        text: text,\n        key: key\n    };\n}\nfunction createElementOpen(name, attrs) {\n    var attrsStr = [];\n    if (attrs) {\n        for (var key in attrs) {\n            var val = attrs[key];\n            var part = key;\n            if (val === false) {\n                continue;\n            }\n            else if (val !== true && val != null) {\n                part += \"=\\\"\" + val + \"\\\"\";\n            }\n            attrsStr.push(part);\n        }\n    }\n    return \"<\" + name + \" \" + attrsStr.join(' ') + \">\";\n}\nfunction createElementClose(name) {\n    return \"</\" + name + \">\";\n}\nfunction vNodeToString(el, opts) {\n    opts = opts || {};\n    var S = opts.newline ? '\\n' : '';\n    function convertElToString(el) {\n        var children = el.children, tag = el.tag, attrs = el.attrs;\n        return createElementOpen(tag, attrs)\n            + encodeHTML(el.text)\n            + (children ? \"\" + S + map(children, function (child) { return convertElToString(child); }).join(S) + S : '')\n            + createElementClose(tag);\n    }\n    return convertElToString(el);\n}\nfunction getCssString(selectorNodes, animationNodes, opts) {\n    opts = opts || {};\n    var S = opts.newline ? '\\n' : '';\n    var bracketBegin = \" {\" + S;\n    var bracketEnd = S + \"}\";\n    var selectors = map(keys(selectorNodes), function (className) {\n        return className + bracketBegin + map(keys(selectorNodes[className]), function (attrName) {\n            return attrName + \":\" + selectorNodes[className][attrName] + \";\";\n        }).join(S) + bracketEnd;\n    }).join(S);\n    var animations = map(keys(animationNodes), function (animationName) {\n        return \"@keyframes \" + animationName + bracketBegin + map(keys(animationNodes[animationName]), function (percent) {\n            return percent + bracketBegin + map(keys(animationNodes[animationName][percent]), function (attrName) {\n                var val = animationNodes[animationName][percent][attrName];\n                if (attrName === 'd') {\n                    val = \"path(\\\"\" + val + \"\\\")\";\n                }\n                return attrName + \":\" + val + \";\";\n            }).join(S) + bracketEnd;\n        }).join(S) + bracketEnd;\n    }).join(S);\n    if (!selectors && !animations) {\n        return '';\n    }\n    return ['<![CDATA[', selectors, animations, ']]>'].join(S);\n}\nfunction createBrushScope(zrId) {\n    return {\n        zrId: zrId,\n        shadowCache: {},\n        patternCache: {},\n        gradientCache: {},\n        clipPathCache: {},\n        defs: {},\n        cssNodes: {},\n        cssAnims: {},\n        cssClassIdx: 0,\n        cssAnimIdx: 0,\n        shadowIdx: 0,\n        gradientIdx: 0,\n        patternIdx: 0,\n        clipPathIdx: 0\n    };\n}\nfunction createSVGVNode(width, height, children, useViewBox) {\n    return createVNode('svg', 'root', {\n        'width': width,\n        'height': height,\n        'xmlns': SVGNS,\n        'xmlns:xlink': XLINKNS,\n        'version': '1.1',\n        'baseProfile': 'full',\n        'viewBox': useViewBox ? \"0 0 \" + width + \" \" + height : false\n    }, children);\n}\n\nvar EASING_MAP = {\n    cubicIn: '0.32,0,0.67,0',\n    cubicOut: '0.33,1,0.68,1',\n    cubicInOut: '0.65,0,0.35,1',\n    quadraticIn: '0.11,0,0.5,0',\n    quadraticOut: '0.5,1,0.89,1',\n    quadraticInOut: '0.45,0,0.55,1',\n    quarticIn: '0.5,0,0.75,0',\n    quarticOut: '0.25,1,0.5,1',\n    quarticInOut: '0.76,0,0.24,1',\n    quinticIn: '0.64,0,0.78,0',\n    quinticOut: '0.22,1,0.36,1',\n    quinticInOut: '0.83,0,0.17,1',\n    sinusoidalIn: '0.12,0,0.39,0',\n    sinusoidalOut: '0.61,1,0.88,1',\n    sinusoidalInOut: '0.37,0,0.63,1',\n    exponentialIn: '0.7,0,0.84,0',\n    exponentialOut: '0.16,1,0.3,1',\n    exponentialInOut: '0.87,0,0.13,1',\n    circularIn: '0.55,0,1,0.45',\n    circularOut: '0,0.55,0.45,1',\n    circularInOut: '0.85,0,0.15,1'\n};\nvar transformOriginKey = 'transform-origin';\nfunction buildPathString(el, kfShape, path) {\n    var shape = extend({}, el.shape);\n    extend(shape, kfShape);\n    el.buildPath(path, shape);\n    var svgPathBuilder = new SVGPathRebuilder();\n    svgPathBuilder.reset(getPathPrecision(el));\n    path.rebuildPath(svgPathBuilder, 1);\n    svgPathBuilder.generateStr();\n    return svgPathBuilder.getStr();\n}\nfunction setTransformOrigin(target, transform) {\n    var originX = transform.originX, originY = transform.originY;\n    if (originX || originY) {\n        target[transformOriginKey] = originX + \"px \" + originY + \"px\";\n    }\n}\nvar ANIMATE_STYLE_MAP = {\n    fill: 'fill',\n    opacity: 'opacity',\n    lineWidth: 'stroke-width',\n    lineDashOffset: 'stroke-dashoffset'\n};\nfunction addAnimation(cssAnim, scope) {\n    var animationName = scope.zrId + '-ani-' + scope.cssAnimIdx++;\n    scope.cssAnims[animationName] = cssAnim;\n    return animationName;\n}\nfunction createCompoundPathCSSAnimation(el, attrs, scope) {\n    var paths = el.shape.paths;\n    var composedAnim = {};\n    var cssAnimationCfg;\n    var cssAnimationName;\n    each(paths, function (path) {\n        var subScope = createBrushScope(scope.zrId);\n        subScope.animation = true;\n        createCSSAnimation(path, {}, subScope, true);\n        var cssAnims = subScope.cssAnims;\n        var cssNodes = subScope.cssNodes;\n        var animNames = keys(cssAnims);\n        var len = animNames.length;\n        if (!len) {\n            return;\n        }\n        cssAnimationName = animNames[len - 1];\n        var lastAnim = cssAnims[cssAnimationName];\n        for (var percent in lastAnim) {\n            var kf = lastAnim[percent];\n            composedAnim[percent] = composedAnim[percent] || { d: '' };\n            composedAnim[percent].d += kf.d || '';\n        }\n        for (var className in cssNodes) {\n            var val = cssNodes[className].animation;\n            if (val.indexOf(cssAnimationName) >= 0) {\n                cssAnimationCfg = val;\n            }\n        }\n    });\n    if (!cssAnimationCfg) {\n        return;\n    }\n    attrs.d = false;\n    var animationName = addAnimation(composedAnim, scope);\n    return cssAnimationCfg.replace(cssAnimationName, animationName);\n}\nfunction getEasingFunc(easing) {\n    return isString(easing)\n        ? EASING_MAP[easing]\n            ? \"cubic-bezier(\" + EASING_MAP[easing] + \")\"\n            : createCubicEasingFunc(easing) ? easing : ''\n        : '';\n}\nfunction createCSSAnimation(el, attrs, scope, onlyShape) {\n    var animators = el.animators;\n    var len = animators.length;\n    var cssAnimations = [];\n    if (el instanceof CompoundPath) {\n        var animationCfg = createCompoundPathCSSAnimation(el, attrs, scope);\n        if (animationCfg) {\n            cssAnimations.push(animationCfg);\n        }\n        else if (!len) {\n            return;\n        }\n    }\n    else if (!len) {\n        return;\n    }\n    var groupAnimators = {};\n    for (var i = 0; i < len; i++) {\n        var animator = animators[i];\n        var cfgArr = [animator.getMaxTime() / 1000 + 's'];\n        var easing = getEasingFunc(animator.getClip().easing);\n        var delay = animator.getDelay();\n        if (easing) {\n            cfgArr.push(easing);\n        }\n        else {\n            cfgArr.push('linear');\n        }\n        if (delay) {\n            cfgArr.push(delay / 1000 + 's');\n        }\n        if (animator.getLoop()) {\n            cfgArr.push('infinite');\n        }\n        var cfg = cfgArr.join(' ');\n        groupAnimators[cfg] = groupAnimators[cfg] || [cfg, []];\n        groupAnimators[cfg][1].push(animator);\n    }\n    function createSingleCSSAnimation(groupAnimator) {\n        var animators = groupAnimator[1];\n        var len = animators.length;\n        var transformKfs = {};\n        var shapeKfs = {};\n        var finalKfs = {};\n        var animationTimingFunctionAttrName = 'animation-timing-function';\n        function saveAnimatorTrackToCssKfs(animator, cssKfs, toCssAttrName) {\n            var tracks = animator.getTracks();\n            var maxTime = animator.getMaxTime();\n            for (var k = 0; k < tracks.length; k++) {\n                var track = tracks[k];\n                if (track.needsAnimate()) {\n                    var kfs = track.keyframes;\n                    var attrName = track.propName;\n                    toCssAttrName && (attrName = toCssAttrName(attrName));\n                    if (attrName) {\n                        for (var i = 0; i < kfs.length; i++) {\n                            var kf = kfs[i];\n                            var percent = Math.round(kf.time / maxTime * 100) + '%';\n                            var kfEasing = getEasingFunc(kf.easing);\n                            var rawValue = kf.rawValue;\n                            if (isString(rawValue) || isNumber(rawValue)) {\n                                cssKfs[percent] = cssKfs[percent] || {};\n                                cssKfs[percent][attrName] = kf.rawValue;\n                                if (kfEasing) {\n                                    cssKfs[percent][animationTimingFunctionAttrName] = kfEasing;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        for (var i = 0; i < len; i++) {\n            var animator = animators[i];\n            var targetProp = animator.targetName;\n            if (!targetProp) {\n                !onlyShape && saveAnimatorTrackToCssKfs(animator, transformKfs);\n            }\n            else if (targetProp === 'shape') {\n                saveAnimatorTrackToCssKfs(animator, shapeKfs);\n            }\n        }\n        for (var percent in transformKfs) {\n            var transform = {};\n            copyTransform(transform, el);\n            extend(transform, transformKfs[percent]);\n            var str = getSRTTransformString(transform);\n            var timingFunction = transformKfs[percent][animationTimingFunctionAttrName];\n            finalKfs[percent] = str ? {\n                transform: str\n            } : {};\n            setTransformOrigin(finalKfs[percent], transform);\n            if (timingFunction) {\n                finalKfs[percent][animationTimingFunctionAttrName] = timingFunction;\n            }\n        }\n        var path;\n        var canAnimateShape = true;\n        for (var percent in shapeKfs) {\n            finalKfs[percent] = finalKfs[percent] || {};\n            var isFirst = !path;\n            var timingFunction = shapeKfs[percent][animationTimingFunctionAttrName];\n            if (isFirst) {\n                path = new PathProxy();\n            }\n            var len_1 = path.len();\n            path.reset();\n            finalKfs[percent].d = buildPathString(el, shapeKfs[percent], path);\n            var newLen = path.len();\n            if (!isFirst && len_1 !== newLen) {\n                canAnimateShape = false;\n                break;\n            }\n            if (timingFunction) {\n                finalKfs[percent][animationTimingFunctionAttrName] = timingFunction;\n            }\n        }\n        if (!canAnimateShape) {\n            for (var percent in finalKfs) {\n                delete finalKfs[percent].d;\n            }\n        }\n        if (!onlyShape) {\n            for (var i = 0; i < len; i++) {\n                var animator = animators[i];\n                var targetProp = animator.targetName;\n                if (targetProp === 'style') {\n                    saveAnimatorTrackToCssKfs(animator, finalKfs, function (propName) { return ANIMATE_STYLE_MAP[propName]; });\n                }\n            }\n        }\n        var percents = keys(finalKfs);\n        var allTransformOriginSame = true;\n        var transformOrigin;\n        for (var i = 1; i < percents.length; i++) {\n            var p0 = percents[i - 1];\n            var p1 = percents[i];\n            if (finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) {\n                allTransformOriginSame = false;\n                break;\n            }\n            transformOrigin = finalKfs[p0][transformOriginKey];\n        }\n        if (allTransformOriginSame && transformOrigin) {\n            for (var percent in finalKfs) {\n                if (finalKfs[percent][transformOriginKey]) {\n                    delete finalKfs[percent][transformOriginKey];\n                }\n            }\n            attrs[transformOriginKey] = transformOrigin;\n        }\n        if (filter(percents, function (percent) { return keys(finalKfs[percent]).length > 0; }).length) {\n            var animationName = addAnimation(finalKfs, scope);\n            return animationName + \" \" + groupAnimator[0] + \" both\";\n        }\n    }\n    for (var key in groupAnimators) {\n        var animationCfg = createSingleCSSAnimation(groupAnimators[key]);\n        if (animationCfg) {\n            cssAnimations.push(animationCfg);\n        }\n    }\n    if (cssAnimations.length) {\n        var className = scope.zrId + '-cls-' + scope.cssClassIdx++;\n        scope.cssNodes['.' + className] = {\n            animation: cssAnimations.join(',')\n        };\n        attrs[\"class\"] = className;\n    }\n}\n\nvar round$2 = Math.round;\nfunction isImageLike$1(val) {\n    return val && isString(val.src);\n}\nfunction isCanvasLike(val) {\n    return val && isFunction(val.toDataURL);\n}\nfunction setStyleAttrs(attrs, style, el, scope) {\n    mapStyleToAttrs(function (key, val) {\n        var isFillStroke = key === 'fill' || key === 'stroke';\n        if (isFillStroke && isGradient(val)) {\n            setGradient(style, attrs, key, scope);\n        }\n        else if (isFillStroke && isPattern(val)) {\n            setPattern(el, attrs, key, scope);\n        }\n        else {\n            attrs[key] = val;\n        }\n    }, style, el, false);\n    setShadow(el, attrs, scope);\n}\nfunction noRotateScale(m) {\n    return isAroundZero$1(m[0] - 1)\n        && isAroundZero$1(m[1])\n        && isAroundZero$1(m[2])\n        && isAroundZero$1(m[3] - 1);\n}\nfunction noTranslate(m) {\n    return isAroundZero$1(m[4]) && isAroundZero$1(m[5]);\n}\nfunction setTransform(attrs, m, compress) {\n    if (m && !(noTranslate(m) && noRotateScale(m))) {\n        var mul = compress ? 10 : 1e4;\n        attrs.transform = noRotateScale(m)\n            ? \"translate(\" + round$2(m[4] * mul) / mul + \" \" + round$2(m[5] * mul) / mul + \")\" : getMatrixStr(m);\n    }\n}\nfunction convertPolyShape(shape, attrs, mul) {\n    var points = shape.points;\n    var strArr = [];\n    for (var i = 0; i < points.length; i++) {\n        strArr.push(round$2(points[i][0] * mul) / mul);\n        strArr.push(round$2(points[i][1] * mul) / mul);\n    }\n    attrs.points = strArr.join(' ');\n}\nfunction validatePolyShape(shape) {\n    return !shape.smooth;\n}\nfunction createAttrsConvert(desc) {\n    var normalizedDesc = map(desc, function (item) {\n        return (typeof item === 'string' ? [item, item] : item);\n    });\n    return function (shape, attrs, mul) {\n        for (var i = 0; i < normalizedDesc.length; i++) {\n            var item = normalizedDesc[i];\n            var val = shape[item[0]];\n            if (val != null) {\n                attrs[item[1]] = round$2(val * mul) / mul;\n            }\n        }\n    };\n}\nvar builtinShapesDef = {\n    circle: [createAttrsConvert(['cx', 'cy', 'r'])],\n    polyline: [convertPolyShape, validatePolyShape],\n    polygon: [convertPolyShape, validatePolyShape]\n};\nfunction hasShapeAnimation(el) {\n    var animators = el.animators;\n    for (var i = 0; i < animators.length; i++) {\n        if (animators[i].targetName === 'shape') {\n            return true;\n        }\n    }\n    return false;\n}\nfunction brushSVGPath(el, scope) {\n    var style = el.style;\n    var shape = el.shape;\n    var builtinShpDef = builtinShapesDef[el.type];\n    var attrs = {};\n    var needsAnimate = scope.animation;\n    var svgElType = 'path';\n    var strokePercent = el.style.strokePercent;\n    var precision = (scope.compress && getPathPrecision(el)) || 4;\n    if (builtinShpDef\n        && !scope.willUpdate\n        && !(builtinShpDef[1] && !builtinShpDef[1](shape))\n        && !(needsAnimate && hasShapeAnimation(el))\n        && !(strokePercent < 1)) {\n        svgElType = el.type;\n        var mul = Math.pow(10, precision);\n        builtinShpDef[0](shape, attrs, mul);\n    }\n    else {\n        var needBuildPath = !el.path || el.shapeChanged();\n        if (!el.path) {\n            el.createPathProxy();\n        }\n        var path = el.path;\n        if (needBuildPath) {\n            path.beginPath();\n            el.buildPath(path, el.shape);\n            el.pathUpdated();\n        }\n        var pathVersion = path.getVersion();\n        var elExt = el;\n        var svgPathBuilder = elExt.__svgPathBuilder;\n        if (elExt.__svgPathVersion !== pathVersion\n            || !svgPathBuilder\n            || strokePercent !== elExt.__svgPathStrokePercent) {\n            if (!svgPathBuilder) {\n                svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder();\n            }\n            svgPathBuilder.reset(precision);\n            path.rebuildPath(svgPathBuilder, strokePercent);\n            svgPathBuilder.generateStr();\n            elExt.__svgPathVersion = pathVersion;\n            elExt.__svgPathStrokePercent = strokePercent;\n        }\n        attrs.d = svgPathBuilder.getStr();\n    }\n    setTransform(attrs, el.transform);\n    setStyleAttrs(attrs, style, el, scope);\n    scope.animation && createCSSAnimation(el, attrs, scope);\n    return createVNode(svgElType, el.id + '', attrs);\n}\nfunction brushSVGImage(el, scope) {\n    var style = el.style;\n    var image = style.image;\n    if (image && !isString(image)) {\n        if (isImageLike$1(image)) {\n            image = image.src;\n        }\n        else if (isCanvasLike(image)) {\n            image = image.toDataURL();\n        }\n    }\n    if (!image) {\n        return;\n    }\n    var x = style.x || 0;\n    var y = style.y || 0;\n    var dw = style.width;\n    var dh = style.height;\n    var attrs = {\n        href: image,\n        width: dw,\n        height: dh\n    };\n    if (x) {\n        attrs.x = x;\n    }\n    if (y) {\n        attrs.y = y;\n    }\n    setTransform(attrs, el.transform);\n    setStyleAttrs(attrs, style, el, scope);\n    scope.animation && createCSSAnimation(el, attrs, scope);\n    return createVNode('image', el.id + '', attrs);\n}\nfunction brushSVGTSpan(el, scope) {\n    var style = el.style;\n    var text = style.text;\n    text != null && (text += '');\n    if (!text || isNaN(style.x) || isNaN(style.y)) {\n        return;\n    }\n    var font = style.font || DEFAULT_FONT;\n    var x = style.x || 0;\n    var y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline);\n    var textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign]\n        || style.textAlign;\n    var attrs = {\n        'dominant-baseline': 'central',\n        'text-anchor': textAlign\n    };\n    if (hasSeparateFont(style)) {\n        var separatedFontStr = '';\n        var fontStyle = style.fontStyle;\n        var fontSize = parseFontSize(style.fontSize);\n        if (!parseFloat(fontSize)) {\n            return;\n        }\n        var fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY;\n        var fontWeight = style.fontWeight;\n        separatedFontStr += \"font-size:\" + fontSize + \";font-family:\" + fontFamily + \";\";\n        if (fontStyle && fontStyle !== 'normal') {\n            separatedFontStr += \"font-style:\" + fontStyle + \";\";\n        }\n        if (fontWeight && fontWeight !== 'normal') {\n            separatedFontStr += \"font-weight:\" + fontWeight + \";\";\n        }\n        attrs.style = separatedFontStr;\n    }\n    else {\n        attrs.style = \"font: \" + font;\n    }\n    if (text.match(/\\s/)) {\n        attrs['xml:space'] = 'preserve';\n    }\n    if (x) {\n        attrs.x = x;\n    }\n    if (y) {\n        attrs.y = y;\n    }\n    setTransform(attrs, el.transform);\n    setStyleAttrs(attrs, style, el, scope);\n    scope.animation && createCSSAnimation(el, attrs, scope);\n    return createVNode('text', el.id + '', attrs, undefined, text);\n}\nfunction brush$1(el, scope) {\n    if (el instanceof Path) {\n        return brushSVGPath(el, scope);\n    }\n    else if (el instanceof ZRImage) {\n        return brushSVGImage(el, scope);\n    }\n    else if (el instanceof TSpan) {\n        return brushSVGTSpan(el, scope);\n    }\n}\nfunction setShadow(el, attrs, scope) {\n    var style = el.style;\n    if (hasShadow(style)) {\n        var shadowKey = getShadowKey(el);\n        var shadowCache = scope.shadowCache;\n        var shadowId = shadowCache[shadowKey];\n        if (!shadowId) {\n            var globalScale = el.getGlobalScale();\n            var scaleX = globalScale[0];\n            var scaleY = globalScale[1];\n            if (!scaleX || !scaleY) {\n                return;\n            }\n            var offsetX = style.shadowOffsetX || 0;\n            var offsetY = style.shadowOffsetY || 0;\n            var blur_1 = style.shadowBlur;\n            var _a = normalizeColor(style.shadowColor), opacity = _a.opacity, color = _a.color;\n            var stdDx = blur_1 / 2 / scaleX;\n            var stdDy = blur_1 / 2 / scaleY;\n            var stdDeviation = stdDx + ' ' + stdDy;\n            shadowId = scope.zrId + '-s' + scope.shadowIdx++;\n            scope.defs[shadowId] = createVNode('filter', shadowId, {\n                'id': shadowId,\n                'x': '-100%',\n                'y': '-100%',\n                'width': '300%',\n                'height': '300%'\n            }, [\n                createVNode('feDropShadow', '', {\n                    'dx': offsetX / scaleX,\n                    'dy': offsetY / scaleY,\n                    'stdDeviation': stdDeviation,\n                    'flood-color': color,\n                    'flood-opacity': opacity\n                })\n            ]);\n            shadowCache[shadowKey] = shadowId;\n        }\n        attrs.filter = getIdURL(shadowId);\n    }\n}\nfunction setGradient(style, attrs, target, scope) {\n    var val = style[target];\n    var gradientTag;\n    var gradientAttrs = {\n        'gradientUnits': val.global\n            ? 'userSpaceOnUse'\n            : 'objectBoundingBox'\n    };\n    if (isLinearGradient(val)) {\n        gradientTag = 'linearGradient';\n        gradientAttrs.x1 = val.x;\n        gradientAttrs.y1 = val.y;\n        gradientAttrs.x2 = val.x2;\n        gradientAttrs.y2 = val.y2;\n    }\n    else if (isRadialGradient(val)) {\n        gradientTag = 'radialGradient';\n        gradientAttrs.cx = retrieve2(val.x, 0.5);\n        gradientAttrs.cy = retrieve2(val.y, 0.5);\n        gradientAttrs.r = retrieve2(val.r, 0.5);\n    }\n    else {\n        if (\"development\" !== 'production') {\n            logError('Illegal gradient type.');\n        }\n        return;\n    }\n    var colors = val.colorStops;\n    var colorStops = [];\n    for (var i = 0, len = colors.length; i < len; ++i) {\n        var offset = round4(colors[i].offset) * 100 + '%';\n        var stopColor = colors[i].color;\n        var _a = normalizeColor(stopColor), color = _a.color, opacity = _a.opacity;\n        var stopsAttrs = {\n            'offset': offset\n        };\n        stopsAttrs['stop-color'] = color;\n        if (opacity < 1) {\n            stopsAttrs['stop-opacity'] = opacity;\n        }\n        colorStops.push(createVNode('stop', i + '', stopsAttrs));\n    }\n    var gradientVNode = createVNode(gradientTag, '', gradientAttrs, colorStops);\n    var gradientKey = vNodeToString(gradientVNode);\n    var gradientCache = scope.gradientCache;\n    var gradientId = gradientCache[gradientKey];\n    if (!gradientId) {\n        gradientId = scope.zrId + '-g' + scope.gradientIdx++;\n        gradientCache[gradientKey] = gradientId;\n        gradientAttrs.id = gradientId;\n        scope.defs[gradientId] = createVNode(gradientTag, gradientId, gradientAttrs, colorStops);\n    }\n    attrs[target] = getIdURL(gradientId);\n}\nfunction setPattern(el, attrs, target, scope) {\n    var val = el.style[target];\n    var boundingRect = el.getBoundingRect();\n    var patternAttrs = {};\n    var repeat = val.repeat;\n    var noRepeat = repeat === 'no-repeat';\n    var repeatX = repeat === 'repeat-x';\n    var repeatY = repeat === 'repeat-y';\n    var child;\n    if (isImagePattern(val)) {\n        var imageWidth_1 = val.imageWidth;\n        var imageHeight_1 = val.imageHeight;\n        var imageSrc = void 0;\n        var patternImage = val.image;\n        if (isString(patternImage)) {\n            imageSrc = patternImage;\n        }\n        else if (isImageLike$1(patternImage)) {\n            imageSrc = patternImage.src;\n        }\n        else if (isCanvasLike(patternImage)) {\n            imageSrc = patternImage.toDataURL();\n        }\n        if (typeof Image === 'undefined') {\n            var errMsg = 'Image width/height must been given explictly in svg-ssr renderer.';\n            assert(imageWidth_1, errMsg);\n            assert(imageHeight_1, errMsg);\n        }\n        else if (imageWidth_1 == null || imageHeight_1 == null) {\n            var setSizeToVNode_1 = function (vNode, img) {\n                if (vNode) {\n                    var svgEl = vNode.elm;\n                    var width = imageWidth_1 || img.width;\n                    var height = imageHeight_1 || img.height;\n                    if (vNode.tag === 'pattern') {\n                        if (repeatX) {\n                            height = 1;\n                            width /= boundingRect.width;\n                        }\n                        else if (repeatY) {\n                            width = 1;\n                            height /= boundingRect.height;\n                        }\n                    }\n                    vNode.attrs.width = width;\n                    vNode.attrs.height = height;\n                    if (svgEl) {\n                        svgEl.setAttribute('width', width);\n                        svgEl.setAttribute('height', height);\n                    }\n                }\n            };\n            var createdImage = createOrUpdateImage(imageSrc, null, el, function (img) {\n                noRepeat || setSizeToVNode_1(patternVNode, img);\n                setSizeToVNode_1(child, img);\n            });\n            if (createdImage && createdImage.width && createdImage.height) {\n                imageWidth_1 = imageWidth_1 || createdImage.width;\n                imageHeight_1 = imageHeight_1 || createdImage.height;\n            }\n        }\n        child = createVNode('image', 'img', {\n            href: imageSrc,\n            width: imageWidth_1,\n            height: imageHeight_1\n        });\n        patternAttrs.width = imageWidth_1;\n        patternAttrs.height = imageHeight_1;\n    }\n    else if (val.svgElement) {\n        child = clone(val.svgElement);\n        patternAttrs.width = val.svgWidth;\n        patternAttrs.height = val.svgHeight;\n    }\n    if (!child) {\n        return;\n    }\n    var patternWidth;\n    var patternHeight;\n    if (noRepeat) {\n        patternWidth = patternHeight = 1;\n    }\n    else if (repeatX) {\n        patternHeight = 1;\n        patternWidth = patternAttrs.width / boundingRect.width;\n    }\n    else if (repeatY) {\n        patternWidth = 1;\n        patternHeight = patternAttrs.height / boundingRect.height;\n    }\n    else {\n        patternAttrs.patternUnits = 'userSpaceOnUse';\n    }\n    if (patternWidth != null && !isNaN(patternWidth)) {\n        patternAttrs.width = patternWidth;\n    }\n    if (patternHeight != null && !isNaN(patternHeight)) {\n        patternAttrs.height = patternHeight;\n    }\n    var patternTransform = getSRTTransformString(val);\n    patternTransform && (patternAttrs.patternTransform = patternTransform);\n    var patternVNode = createVNode('pattern', '', patternAttrs, [child]);\n    var patternKey = vNodeToString(patternVNode);\n    var patternCache = scope.patternCache;\n    var patternId = patternCache[patternKey];\n    if (!patternId) {\n        patternId = scope.zrId + '-p' + scope.patternIdx++;\n        patternCache[patternKey] = patternId;\n        patternAttrs.id = patternId;\n        patternVNode = scope.defs[patternId] = createVNode('pattern', patternId, patternAttrs, [child]);\n    }\n    attrs[target] = getIdURL(patternId);\n}\nfunction setClipPath(clipPath, attrs, scope) {\n    var clipPathCache = scope.clipPathCache, defs = scope.defs;\n    var clipPathId = clipPathCache[clipPath.id];\n    if (!clipPathId) {\n        clipPathId = scope.zrId + '-c' + scope.clipPathIdx++;\n        var clipPathAttrs = {\n            id: clipPathId\n        };\n        clipPathCache[clipPath.id] = clipPathId;\n        defs[clipPathId] = createVNode('clipPath', clipPathId, clipPathAttrs, [brushSVGPath(clipPath, scope)]);\n    }\n    attrs['clip-path'] = getIdURL(clipPathId);\n}\n\nfunction createTextNode(text) {\n    return document.createTextNode(text);\n}\nfunction insertBefore(parentNode, newNode, referenceNode) {\n    parentNode.insertBefore(newNode, referenceNode);\n}\nfunction removeChild(node, child) {\n    node.removeChild(child);\n}\nfunction appendChild(node, child) {\n    node.appendChild(child);\n}\nfunction parentNode(node) {\n    return node.parentNode;\n}\nfunction nextSibling(node) {\n    return node.nextSibling;\n}\nfunction setTextContent(node, text) {\n    node.textContent = text;\n}\n\nvar colonChar = 58;\nvar xChar = 120;\nvar emptyNode = createVNode('', '');\nfunction isUndef(s) {\n    return s === undefined;\n}\nfunction isDef(s) {\n    return s !== undefined;\n}\nfunction createKeyToOldIdx(children, beginIdx, endIdx) {\n    var map = {};\n    for (var i = beginIdx; i <= endIdx; ++i) {\n        var key = children[i].key;\n        if (key !== undefined) {\n            if (\"development\" !== 'production') {\n                if (map[key] != null) {\n                    console.error(\"Duplicate key \" + key);\n                }\n            }\n            map[key] = i;\n        }\n    }\n    return map;\n}\nfunction sameVnode(vnode1, vnode2) {\n    var isSameKey = vnode1.key === vnode2.key;\n    var isSameTag = vnode1.tag === vnode2.tag;\n    return isSameTag && isSameKey;\n}\nfunction createElm(vnode) {\n    var i;\n    var children = vnode.children;\n    var tag = vnode.tag;\n    if (isDef(tag)) {\n        var elm = (vnode.elm = createElement(tag));\n        updateAttrs(emptyNode, vnode);\n        if (isArray(children)) {\n            for (i = 0; i < children.length; ++i) {\n                var ch = children[i];\n                if (ch != null) {\n                    appendChild(elm, createElm(ch));\n                }\n            }\n        }\n        else if (isDef(vnode.text) && !isObject(vnode.text)) {\n            appendChild(elm, createTextNode(vnode.text));\n        }\n    }\n    else {\n        vnode.elm = createTextNode(vnode.text);\n    }\n    return vnode.elm;\n}\nfunction addVnodes(parentElm, before, vnodes, startIdx, endIdx) {\n    for (; startIdx <= endIdx; ++startIdx) {\n        var ch = vnodes[startIdx];\n        if (ch != null) {\n            insertBefore(parentElm, createElm(ch), before);\n        }\n    }\n}\nfunction removeVnodes(parentElm, vnodes, startIdx, endIdx) {\n    for (; startIdx <= endIdx; ++startIdx) {\n        var ch = vnodes[startIdx];\n        if (ch != null) {\n            if (isDef(ch.tag)) {\n                var parent_1 = parentNode(ch.elm);\n                removeChild(parent_1, ch.elm);\n            }\n            else {\n                removeChild(parentElm, ch.elm);\n            }\n        }\n    }\n}\nfunction updateAttrs(oldVnode, vnode) {\n    var key;\n    var elm = vnode.elm;\n    var oldAttrs = oldVnode && oldVnode.attrs || {};\n    var attrs = vnode.attrs || {};\n    if (oldAttrs === attrs) {\n        return;\n    }\n    for (key in attrs) {\n        var cur = attrs[key];\n        var old = oldAttrs[key];\n        if (old !== cur) {\n            if (cur === true) {\n                elm.setAttribute(key, '');\n            }\n            else if (cur === false) {\n                elm.removeAttribute(key);\n            }\n            else {\n                if (key.charCodeAt(0) !== xChar) {\n                    elm.setAttribute(key, cur);\n                }\n                else if (key === 'xmlns:xlink' || key === 'xmlns') {\n                    elm.setAttributeNS(XMLNS, key, cur);\n                }\n                else if (key.charCodeAt(3) === colonChar) {\n                    elm.setAttributeNS(XML_NAMESPACE, key, cur);\n                }\n                else if (key.charCodeAt(5) === colonChar) {\n                    elm.setAttributeNS(XLINKNS, key, cur);\n                }\n                else {\n                    elm.setAttribute(key, cur);\n                }\n            }\n        }\n    }\n    for (key in oldAttrs) {\n        if (!(key in attrs)) {\n            elm.removeAttribute(key);\n        }\n    }\n}\nfunction updateChildren(parentElm, oldCh, newCh) {\n    var oldStartIdx = 0;\n    var newStartIdx = 0;\n    var oldEndIdx = oldCh.length - 1;\n    var oldStartVnode = oldCh[0];\n    var oldEndVnode = oldCh[oldEndIdx];\n    var newEndIdx = newCh.length - 1;\n    var newStartVnode = newCh[0];\n    var newEndVnode = newCh[newEndIdx];\n    var oldKeyToIdx;\n    var idxInOld;\n    var elmToMove;\n    var before;\n    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {\n        if (oldStartVnode == null) {\n            oldStartVnode = oldCh[++oldStartIdx];\n        }\n        else if (oldEndVnode == null) {\n            oldEndVnode = oldCh[--oldEndIdx];\n        }\n        else if (newStartVnode == null) {\n            newStartVnode = newCh[++newStartIdx];\n        }\n        else if (newEndVnode == null) {\n            newEndVnode = newCh[--newEndIdx];\n        }\n        else if (sameVnode(oldStartVnode, newStartVnode)) {\n            patchVnode(oldStartVnode, newStartVnode);\n            oldStartVnode = oldCh[++oldStartIdx];\n            newStartVnode = newCh[++newStartIdx];\n        }\n        else if (sameVnode(oldEndVnode, newEndVnode)) {\n            patchVnode(oldEndVnode, newEndVnode);\n            oldEndVnode = oldCh[--oldEndIdx];\n            newEndVnode = newCh[--newEndIdx];\n        }\n        else if (sameVnode(oldStartVnode, newEndVnode)) {\n            patchVnode(oldStartVnode, newEndVnode);\n            insertBefore(parentElm, oldStartVnode.elm, nextSibling(oldEndVnode.elm));\n            oldStartVnode = oldCh[++oldStartIdx];\n            newEndVnode = newCh[--newEndIdx];\n        }\n        else if (sameVnode(oldEndVnode, newStartVnode)) {\n            patchVnode(oldEndVnode, newStartVnode);\n            insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);\n            oldEndVnode = oldCh[--oldEndIdx];\n            newStartVnode = newCh[++newStartIdx];\n        }\n        else {\n            if (isUndef(oldKeyToIdx)) {\n                oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);\n            }\n            idxInOld = oldKeyToIdx[newStartVnode.key];\n            if (isUndef(idxInOld)) {\n                insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm);\n            }\n            else {\n                elmToMove = oldCh[idxInOld];\n                if (elmToMove.tag !== newStartVnode.tag) {\n                    insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm);\n                }\n                else {\n                    patchVnode(elmToMove, newStartVnode);\n                    oldCh[idxInOld] = undefined;\n                    insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);\n                }\n            }\n            newStartVnode = newCh[++newStartIdx];\n        }\n    }\n    if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) {\n        if (oldStartIdx > oldEndIdx) {\n            before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;\n            addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx);\n        }\n        else {\n            removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);\n        }\n    }\n}\nfunction patchVnode(oldVnode, vnode) {\n    var elm = (vnode.elm = oldVnode.elm);\n    var oldCh = oldVnode.children;\n    var ch = vnode.children;\n    if (oldVnode === vnode) {\n        return;\n    }\n    updateAttrs(oldVnode, vnode);\n    if (isUndef(vnode.text)) {\n        if (isDef(oldCh) && isDef(ch)) {\n            if (oldCh !== ch) {\n                updateChildren(elm, oldCh, ch);\n            }\n        }\n        else if (isDef(ch)) {\n            if (isDef(oldVnode.text)) {\n                setTextContent(elm, '');\n            }\n            addVnodes(elm, null, ch, 0, ch.length - 1);\n        }\n        else if (isDef(oldCh)) {\n            removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n        }\n        else if (isDef(oldVnode.text)) {\n            setTextContent(elm, '');\n        }\n    }\n    else if (oldVnode.text !== vnode.text) {\n        if (isDef(oldCh)) {\n            removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n        }\n        setTextContent(elm, vnode.text);\n    }\n}\nfunction patch(oldVnode, vnode) {\n    if (sameVnode(oldVnode, vnode)) {\n        patchVnode(oldVnode, vnode);\n    }\n    else {\n        var elm = oldVnode.elm;\n        var parent_2 = parentNode(elm);\n        createElm(vnode);\n        if (parent_2 !== null) {\n            insertBefore(parent_2, vnode.elm, nextSibling(elm));\n            removeVnodes(parent_2, [oldVnode], 0, 0);\n        }\n    }\n    return vnode;\n}\n\nvar svgId = 0;\nvar SVGPainter = (function () {\n    function SVGPainter(root, storage, opts) {\n        this.type = 'svg';\n        this.refreshHover = createMethodNotSupport('refreshHover');\n        this.configLayer = createMethodNotSupport('configLayer');\n        this.storage = storage;\n        this._opts = opts = extend({}, opts);\n        this.root = root;\n        this._id = 'zr' + svgId++;\n        this._oldVNode = createSVGVNode(opts.width, opts.height);\n        if (root && !opts.ssr) {\n            var viewport = this._viewport = document.createElement('div');\n            viewport.style.cssText = 'position:relative;overflow:hidden';\n            var svgDom = this._svgDom = this._oldVNode.elm = createElement('svg');\n            updateAttrs(null, this._oldVNode);\n            viewport.appendChild(svgDom);\n            root.appendChild(viewport);\n        }\n        this.resize(opts.width, opts.height);\n    }\n    SVGPainter.prototype.getType = function () {\n        return this.type;\n    };\n    SVGPainter.prototype.getViewportRoot = function () {\n        return this._viewport;\n    };\n    SVGPainter.prototype.getViewportRootOffset = function () {\n        var viewportRoot = this.getViewportRoot();\n        if (viewportRoot) {\n            return {\n                offsetLeft: viewportRoot.offsetLeft || 0,\n                offsetTop: viewportRoot.offsetTop || 0\n            };\n        }\n    };\n    SVGPainter.prototype.getSvgDom = function () {\n        return this._svgDom;\n    };\n    SVGPainter.prototype.refresh = function () {\n        if (this.root) {\n            var vnode = this.renderToVNode({\n                willUpdate: true\n            });\n            vnode.attrs.style = 'position:absolute;left:0;top:0;user-select:none';\n            patch(this._oldVNode, vnode);\n            this._oldVNode = vnode;\n        }\n    };\n    SVGPainter.prototype.renderOneToVNode = function (el) {\n        return brush$1(el, createBrushScope(this._id));\n    };\n    SVGPainter.prototype.renderToVNode = function (opts) {\n        opts = opts || {};\n        var list = this.storage.getDisplayList(true);\n        var width = this._width;\n        var height = this._height;\n        var scope = createBrushScope(this._id);\n        scope.animation = opts.animation;\n        scope.willUpdate = opts.willUpdate;\n        scope.compress = opts.compress;\n        var children = [];\n        var bgVNode = this._bgVNode = createBackgroundVNode(width, height, this._backgroundColor, scope);\n        bgVNode && children.push(bgVNode);\n        var mainVNode = !opts.compress\n            ? (this._mainVNode = createVNode('g', 'main', {}, [])) : null;\n        this._paintList(list, scope, mainVNode ? mainVNode.children : children);\n        mainVNode && children.push(mainVNode);\n        var defs = map(keys(scope.defs), function (id) { return scope.defs[id]; });\n        if (defs.length) {\n            children.push(createVNode('defs', 'defs', {}, defs));\n        }\n        if (opts.animation) {\n            var animationCssStr = getCssString(scope.cssNodes, scope.cssAnims, { newline: true });\n            if (animationCssStr) {\n                var styleNode = createVNode('style', 'stl', {}, [], animationCssStr);\n                children.push(styleNode);\n            }\n        }\n        return createSVGVNode(width, height, children, opts.useViewBox);\n    };\n    SVGPainter.prototype.renderToString = function (opts) {\n        opts = opts || {};\n        return vNodeToString(this.renderToVNode({\n            animation: retrieve2(opts.cssAnimation, true),\n            willUpdate: false,\n            compress: true,\n            useViewBox: retrieve2(opts.useViewBox, true)\n        }), { newline: true });\n    };\n    SVGPainter.prototype.setBackgroundColor = function (backgroundColor) {\n        this._backgroundColor = backgroundColor;\n    };\n    SVGPainter.prototype.getSvgRoot = function () {\n        return this._mainVNode && this._mainVNode.elm;\n    };\n    SVGPainter.prototype._paintList = function (list, scope, out) {\n        var listLen = list.length;\n        var clipPathsGroupsStack = [];\n        var clipPathsGroupsStackDepth = 0;\n        var currentClipPathGroup;\n        var prevClipPaths;\n        var clipGroupNodeIdx = 0;\n        for (var i = 0; i < listLen; i++) {\n            var displayable = list[i];\n            if (!displayable.invisible) {\n                var clipPaths = displayable.__clipPaths;\n                var len = clipPaths && clipPaths.length || 0;\n                var prevLen = prevClipPaths && prevClipPaths.length || 0;\n                var lca = void 0;\n                for (lca = Math.max(len - 1, prevLen - 1); lca >= 0; lca--) {\n                    if (clipPaths && prevClipPaths\n                        && clipPaths[lca] === prevClipPaths[lca]) {\n                        break;\n                    }\n                }\n                for (var i_1 = prevLen - 1; i_1 > lca; i_1--) {\n                    clipPathsGroupsStackDepth--;\n                    currentClipPathGroup = clipPathsGroupsStack[clipPathsGroupsStackDepth - 1];\n                }\n                for (var i_2 = lca + 1; i_2 < len; i_2++) {\n                    var groupAttrs = {};\n                    setClipPath(clipPaths[i_2], groupAttrs, scope);\n                    var g = createVNode('g', 'clip-g-' + clipGroupNodeIdx++, groupAttrs, []);\n                    (currentClipPathGroup ? currentClipPathGroup.children : out).push(g);\n                    clipPathsGroupsStack[clipPathsGroupsStackDepth++] = g;\n                    currentClipPathGroup = g;\n                }\n                prevClipPaths = clipPaths;\n                var ret = brush$1(displayable, scope);\n                if (ret) {\n                    (currentClipPathGroup ? currentClipPathGroup.children : out).push(ret);\n                }\n            }\n        }\n    };\n    SVGPainter.prototype.resize = function (width, height) {\n        var opts = this._opts;\n        var root = this.root;\n        var viewport = this._viewport;\n        width != null && (opts.width = width);\n        height != null && (opts.height = height);\n        if (root && viewport) {\n            viewport.style.display = 'none';\n            width = getSize(root, 0, opts);\n            height = getSize(root, 1, opts);\n            viewport.style.display = '';\n        }\n        if (this._width !== width || this._height !== height) {\n            this._width = width;\n            this._height = height;\n            if (viewport) {\n                var viewportStyle = viewport.style;\n                viewportStyle.width = width + 'px';\n                viewportStyle.height = height + 'px';\n            }\n            if (!isPattern(this._backgroundColor)) {\n                var svgDom = this._svgDom;\n                if (svgDom) {\n                    svgDom.setAttribute('width', width);\n                    svgDom.setAttribute('height', height);\n                }\n                var bgEl = this._bgVNode && this._bgVNode.elm;\n                if (bgEl) {\n                    bgEl.setAttribute('width', width);\n                    bgEl.setAttribute('height', height);\n                }\n            }\n            else {\n                this.refresh();\n            }\n        }\n    };\n    SVGPainter.prototype.getWidth = function () {\n        return this._width;\n    };\n    SVGPainter.prototype.getHeight = function () {\n        return this._height;\n    };\n    SVGPainter.prototype.dispose = function () {\n        if (this.root) {\n            this.root.innerHTML = '';\n        }\n        this._svgDom =\n            this._viewport =\n                this.storage =\n                    this._oldVNode =\n                        this._bgVNode =\n                            this._mainVNode = null;\n    };\n    SVGPainter.prototype.clear = function () {\n        if (this._svgDom) {\n            this._svgDom.innerHTML = null;\n        }\n        this._oldVNode = null;\n    };\n    SVGPainter.prototype.toDataURL = function (base64) {\n        var str = this.renderToString();\n        var prefix = 'data:image/svg+xml;';\n        if (base64) {\n            str = encodeBase64(str);\n            return str && prefix + 'base64,' + str;\n        }\n        return prefix + 'charset=UTF-8,' + encodeURIComponent(str);\n    };\n    return SVGPainter;\n}());\nfunction createMethodNotSupport(method) {\n    return function () {\n        if (\"development\" !== 'production') {\n            logError('In SVG mode painter not support method \"' + method + '\"');\n        }\n    };\n}\nfunction createBackgroundVNode(width, height, backgroundColor, scope) {\n    var bgVNode;\n    if (backgroundColor && backgroundColor !== 'none') {\n        bgVNode = createVNode('rect', 'bg', {\n            width: width,\n            height: height,\n            x: '0',\n            y: '0',\n            id: '0'\n        });\n        if (isGradient(backgroundColor)) {\n            setGradient({ fill: backgroundColor }, bgVNode.attrs, 'fill', scope);\n        }\n        else if (isPattern(backgroundColor)) {\n            setPattern({\n                style: {\n                    fill: backgroundColor\n                },\n                dirty: noop,\n                getBoundingRect: function () { return ({ width: width, height: height }); }\n            }, bgVNode.attrs, 'fill', scope);\n        }\n        else {\n            var _a = normalizeColor(backgroundColor), color = _a.color, opacity = _a.opacity;\n            bgVNode.attrs.fill = color;\n            opacity < 1 && (bgVNode.attrs['fill-opacity'] = opacity);\n        }\n    }\n    return bgVNode;\n}\n\nfunction install(registers) {\n  registers.registerPainter('svg', SVGPainter);\n}\n\nfunction createDom(id, painter, dpr) {\n    var newDom = platformApi.createCanvas();\n    var width = painter.getWidth();\n    var height = painter.getHeight();\n    var newDomStyle = newDom.style;\n    if (newDomStyle) {\n        newDomStyle.position = 'absolute';\n        newDomStyle.left = '0';\n        newDomStyle.top = '0';\n        newDomStyle.width = width + 'px';\n        newDomStyle.height = height + 'px';\n        newDom.setAttribute('data-zr-dom-id', id);\n    }\n    newDom.width = width * dpr;\n    newDom.height = height * dpr;\n    return newDom;\n}\nvar Layer = (function (_super) {\n    __extends(Layer, _super);\n    function Layer(id, painter, dpr) {\n        var _this = _super.call(this) || this;\n        _this.motionBlur = false;\n        _this.lastFrameAlpha = 0.7;\n        _this.dpr = 1;\n        _this.virtual = false;\n        _this.config = {};\n        _this.incremental = false;\n        _this.zlevel = 0;\n        _this.maxRepaintRectCount = 5;\n        _this.__dirty = true;\n        _this.__firstTimePaint = true;\n        _this.__used = false;\n        _this.__drawIndex = 0;\n        _this.__startIndex = 0;\n        _this.__endIndex = 0;\n        _this.__prevStartIndex = null;\n        _this.__prevEndIndex = null;\n        var dom;\n        dpr = dpr || devicePixelRatio;\n        if (typeof id === 'string') {\n            dom = createDom(id, painter, dpr);\n        }\n        else if (isObject(id)) {\n            dom = id;\n            id = dom.id;\n        }\n        _this.id = id;\n        _this.dom = dom;\n        var domStyle = dom.style;\n        if (domStyle) {\n            disableUserSelect(dom);\n            dom.onselectstart = function () { return false; };\n            domStyle.padding = '0';\n            domStyle.margin = '0';\n            domStyle.borderWidth = '0';\n        }\n        _this.painter = painter;\n        _this.dpr = dpr;\n        return _this;\n    }\n    Layer.prototype.getElementCount = function () {\n        return this.__endIndex - this.__startIndex;\n    };\n    Layer.prototype.afterBrush = function () {\n        this.__prevStartIndex = this.__startIndex;\n        this.__prevEndIndex = this.__endIndex;\n    };\n    Layer.prototype.initContext = function () {\n        this.ctx = this.dom.getContext('2d');\n        this.ctx.dpr = this.dpr;\n    };\n    Layer.prototype.setUnpainted = function () {\n        this.__firstTimePaint = true;\n    };\n    Layer.prototype.createBackBuffer = function () {\n        var dpr = this.dpr;\n        this.domBack = createDom('back-' + this.id, this.painter, dpr);\n        this.ctxBack = this.domBack.getContext('2d');\n        if (dpr !== 1) {\n            this.ctxBack.scale(dpr, dpr);\n        }\n    };\n    Layer.prototype.createRepaintRects = function (displayList, prevList, viewWidth, viewHeight) {\n        if (this.__firstTimePaint) {\n            this.__firstTimePaint = false;\n            return null;\n        }\n        var mergedRepaintRects = [];\n        var maxRepaintRectCount = this.maxRepaintRectCount;\n        var full = false;\n        var pendingRect = new BoundingRect(0, 0, 0, 0);\n        function addRectToMergePool(rect) {\n            if (!rect.isFinite() || rect.isZero()) {\n                return;\n            }\n            if (mergedRepaintRects.length === 0) {\n                var boundingRect = new BoundingRect(0, 0, 0, 0);\n                boundingRect.copy(rect);\n                mergedRepaintRects.push(boundingRect);\n            }\n            else {\n                var isMerged = false;\n                var minDeltaArea = Infinity;\n                var bestRectToMergeIdx = 0;\n                for (var i = 0; i < mergedRepaintRects.length; ++i) {\n                    var mergedRect = mergedRepaintRects[i];\n                    if (mergedRect.intersect(rect)) {\n                        var pendingRect_1 = new BoundingRect(0, 0, 0, 0);\n                        pendingRect_1.copy(mergedRect);\n                        pendingRect_1.union(rect);\n                        mergedRepaintRects[i] = pendingRect_1;\n                        isMerged = true;\n                        break;\n                    }\n                    else if (full) {\n                        pendingRect.copy(rect);\n                        pendingRect.union(mergedRect);\n                        var aArea = rect.width * rect.height;\n                        var bArea = mergedRect.width * mergedRect.height;\n                        var pendingArea = pendingRect.width * pendingRect.height;\n                        var deltaArea = pendingArea - aArea - bArea;\n                        if (deltaArea < minDeltaArea) {\n                            minDeltaArea = deltaArea;\n                            bestRectToMergeIdx = i;\n                        }\n                    }\n                }\n                if (full) {\n                    mergedRepaintRects[bestRectToMergeIdx].union(rect);\n                    isMerged = true;\n                }\n                if (!isMerged) {\n                    var boundingRect = new BoundingRect(0, 0, 0, 0);\n                    boundingRect.copy(rect);\n                    mergedRepaintRects.push(boundingRect);\n                }\n                if (!full) {\n                    full = mergedRepaintRects.length >= maxRepaintRectCount;\n                }\n            }\n        }\n        for (var i = this.__startIndex; i < this.__endIndex; ++i) {\n            var el = displayList[i];\n            if (el) {\n                var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n                var prevRect = el.__isRendered && ((el.__dirty & REDRAW_BIT) || !shouldPaint)\n                    ? el.getPrevPaintRect()\n                    : null;\n                if (prevRect) {\n                    addRectToMergePool(prevRect);\n                }\n                var curRect = shouldPaint && ((el.__dirty & REDRAW_BIT) || !el.__isRendered)\n                    ? el.getPaintRect()\n                    : null;\n                if (curRect) {\n                    addRectToMergePool(curRect);\n                }\n            }\n        }\n        for (var i = this.__prevStartIndex; i < this.__prevEndIndex; ++i) {\n            var el = prevList[i];\n            var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n            if (el && (!shouldPaint || !el.__zr) && el.__isRendered) {\n                var prevRect = el.getPrevPaintRect();\n                if (prevRect) {\n                    addRectToMergePool(prevRect);\n                }\n            }\n        }\n        var hasIntersections;\n        do {\n            hasIntersections = false;\n            for (var i = 0; i < mergedRepaintRects.length;) {\n                if (mergedRepaintRects[i].isZero()) {\n                    mergedRepaintRects.splice(i, 1);\n                    continue;\n                }\n                for (var j = i + 1; j < mergedRepaintRects.length;) {\n                    if (mergedRepaintRects[i].intersect(mergedRepaintRects[j])) {\n                        hasIntersections = true;\n                        mergedRepaintRects[i].union(mergedRepaintRects[j]);\n                        mergedRepaintRects.splice(j, 1);\n                    }\n                    else {\n                        j++;\n                    }\n                }\n                i++;\n            }\n        } while (hasIntersections);\n        this._paintRects = mergedRepaintRects;\n        return mergedRepaintRects;\n    };\n    Layer.prototype.debugGetPaintRects = function () {\n        return (this._paintRects || []).slice();\n    };\n    Layer.prototype.resize = function (width, height) {\n        var dpr = this.dpr;\n        var dom = this.dom;\n        var domStyle = dom.style;\n        var domBack = this.domBack;\n        if (domStyle) {\n            domStyle.width = width + 'px';\n            domStyle.height = height + 'px';\n        }\n        dom.width = width * dpr;\n        dom.height = height * dpr;\n        if (domBack) {\n            domBack.width = width * dpr;\n            domBack.height = height * dpr;\n            if (dpr !== 1) {\n                this.ctxBack.scale(dpr, dpr);\n            }\n        }\n    };\n    Layer.prototype.clear = function (clearAll, clearColor, repaintRects) {\n        var dom = this.dom;\n        var ctx = this.ctx;\n        var width = dom.width;\n        var height = dom.height;\n        clearColor = clearColor || this.clearColor;\n        var haveMotionBLur = this.motionBlur && !clearAll;\n        var lastFrameAlpha = this.lastFrameAlpha;\n        var dpr = this.dpr;\n        var self = this;\n        if (haveMotionBLur) {\n            if (!this.domBack) {\n                this.createBackBuffer();\n            }\n            this.ctxBack.globalCompositeOperation = 'copy';\n            this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr);\n        }\n        var domBack = this.domBack;\n        function doClear(x, y, width, height) {\n            ctx.clearRect(x, y, width, height);\n            if (clearColor && clearColor !== 'transparent') {\n                var clearColorGradientOrPattern = void 0;\n                if (isGradientObject(clearColor)) {\n                    var shouldCache = clearColor.global || (clearColor.__width === width\n                        && clearColor.__height === height);\n                    clearColorGradientOrPattern = shouldCache\n                        && clearColor.__canvasGradient\n                        || getCanvasGradient(ctx, clearColor, {\n                            x: 0,\n                            y: 0,\n                            width: width,\n                            height: height\n                        });\n                    clearColor.__canvasGradient = clearColorGradientOrPattern;\n                    clearColor.__width = width;\n                    clearColor.__height = height;\n                }\n                else if (isImagePatternObject(clearColor)) {\n                    clearColor.scaleX = clearColor.scaleX || dpr;\n                    clearColor.scaleY = clearColor.scaleY || dpr;\n                    clearColorGradientOrPattern = createCanvasPattern(ctx, clearColor, {\n                        dirty: function () {\n                            self.setUnpainted();\n                            self.__painter.refresh();\n                        }\n                    });\n                }\n                ctx.save();\n                ctx.fillStyle = clearColorGradientOrPattern || clearColor;\n                ctx.fillRect(x, y, width, height);\n                ctx.restore();\n            }\n            if (haveMotionBLur) {\n                ctx.save();\n                ctx.globalAlpha = lastFrameAlpha;\n                ctx.drawImage(domBack, x, y, width, height);\n                ctx.restore();\n            }\n        }\n        if (!repaintRects || haveMotionBLur) {\n            doClear(0, 0, width, height);\n        }\n        else if (repaintRects.length) {\n            each(repaintRects, function (rect) {\n                doClear(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n            });\n        }\n    };\n    return Layer;\n}(Eventful));\n\nvar HOVER_LAYER_ZLEVEL = 1e5;\nvar CANVAS_ZLEVEL = 314159;\nvar EL_AFTER_INCREMENTAL_INC = 0.01;\nvar INCREMENTAL_INC = 0.001;\nfunction isLayerValid(layer) {\n    if (!layer) {\n        return false;\n    }\n    if (layer.__builtin__) {\n        return true;\n    }\n    if (typeof (layer.resize) !== 'function'\n        || typeof (layer.refresh) !== 'function') {\n        return false;\n    }\n    return true;\n}\nfunction createRoot(width, height) {\n    var domRoot = document.createElement('div');\n    domRoot.style.cssText = [\n        'position:relative',\n        'width:' + width + 'px',\n        'height:' + height + 'px',\n        'padding:0',\n        'margin:0',\n        'border-width:0'\n    ].join(';') + ';';\n    return domRoot;\n}\nvar CanvasPainter = (function () {\n    function CanvasPainter(root, storage, opts, id) {\n        this.type = 'canvas';\n        this._zlevelList = [];\n        this._prevDisplayList = [];\n        this._layers = {};\n        this._layerConfig = {};\n        this._needsManuallyCompositing = false;\n        this.type = 'canvas';\n        var singleCanvas = !root.nodeName\n            || root.nodeName.toUpperCase() === 'CANVAS';\n        this._opts = opts = extend({}, opts || {});\n        this.dpr = opts.devicePixelRatio || devicePixelRatio;\n        this._singleCanvas = singleCanvas;\n        this.root = root;\n        var rootStyle = root.style;\n        if (rootStyle) {\n            disableUserSelect(root);\n            root.innerHTML = '';\n        }\n        this.storage = storage;\n        var zlevelList = this._zlevelList;\n        this._prevDisplayList = [];\n        var layers = this._layers;\n        if (!singleCanvas) {\n            this._width = getSize(root, 0, opts);\n            this._height = getSize(root, 1, opts);\n            var domRoot = this._domRoot = createRoot(this._width, this._height);\n            root.appendChild(domRoot);\n        }\n        else {\n            var rootCanvas = root;\n            var width = rootCanvas.width;\n            var height = rootCanvas.height;\n            if (opts.width != null) {\n                width = opts.width;\n            }\n            if (opts.height != null) {\n                height = opts.height;\n            }\n            this.dpr = opts.devicePixelRatio || 1;\n            rootCanvas.width = width * this.dpr;\n            rootCanvas.height = height * this.dpr;\n            this._width = width;\n            this._height = height;\n            var mainLayer = new Layer(rootCanvas, this, this.dpr);\n            mainLayer.__builtin__ = true;\n            mainLayer.initContext();\n            layers[CANVAS_ZLEVEL] = mainLayer;\n            mainLayer.zlevel = CANVAS_ZLEVEL;\n            zlevelList.push(CANVAS_ZLEVEL);\n            this._domRoot = root;\n        }\n    }\n    CanvasPainter.prototype.getType = function () {\n        return 'canvas';\n    };\n    CanvasPainter.prototype.isSingleCanvas = function () {\n        return this._singleCanvas;\n    };\n    CanvasPainter.prototype.getViewportRoot = function () {\n        return this._domRoot;\n    };\n    CanvasPainter.prototype.getViewportRootOffset = function () {\n        var viewportRoot = this.getViewportRoot();\n        if (viewportRoot) {\n            return {\n                offsetLeft: viewportRoot.offsetLeft || 0,\n                offsetTop: viewportRoot.offsetTop || 0\n            };\n        }\n    };\n    CanvasPainter.prototype.refresh = function (paintAll) {\n        var list = this.storage.getDisplayList(true);\n        var prevList = this._prevDisplayList;\n        var zlevelList = this._zlevelList;\n        this._redrawId = Math.random();\n        this._paintList(list, prevList, paintAll, this._redrawId);\n        for (var i = 0; i < zlevelList.length; i++) {\n            var z = zlevelList[i];\n            var layer = this._layers[z];\n            if (!layer.__builtin__ && layer.refresh) {\n                var clearColor = i === 0 ? this._backgroundColor : null;\n                layer.refresh(clearColor);\n            }\n        }\n        if (this._opts.useDirtyRect) {\n            this._prevDisplayList = list.slice();\n        }\n        return this;\n    };\n    CanvasPainter.prototype.refreshHover = function () {\n        this._paintHoverList(this.storage.getDisplayList(false));\n    };\n    CanvasPainter.prototype._paintHoverList = function (list) {\n        var len = list.length;\n        var hoverLayer = this._hoverlayer;\n        hoverLayer && hoverLayer.clear();\n        if (!len) {\n            return;\n        }\n        var scope = {\n            inHover: true,\n            viewWidth: this._width,\n            viewHeight: this._height\n        };\n        var ctx;\n        for (var i = 0; i < len; i++) {\n            var el = list[i];\n            if (el.__inHover) {\n                if (!hoverLayer) {\n                    hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);\n                }\n                if (!ctx) {\n                    ctx = hoverLayer.ctx;\n                    ctx.save();\n                }\n                brush(ctx, el, scope, i === len - 1);\n            }\n        }\n        if (ctx) {\n            ctx.restore();\n        }\n    };\n    CanvasPainter.prototype.getHoverLayer = function () {\n        return this.getLayer(HOVER_LAYER_ZLEVEL);\n    };\n    CanvasPainter.prototype.paintOne = function (ctx, el) {\n        brushSingle(ctx, el);\n    };\n    CanvasPainter.prototype._paintList = function (list, prevList, paintAll, redrawId) {\n        if (this._redrawId !== redrawId) {\n            return;\n        }\n        paintAll = paintAll || false;\n        this._updateLayerStatus(list);\n        var _a = this._doPaintList(list, prevList, paintAll), finished = _a.finished, needsRefreshHover = _a.needsRefreshHover;\n        if (this._needsManuallyCompositing) {\n            this._compositeManually();\n        }\n        if (needsRefreshHover) {\n            this._paintHoverList(list);\n        }\n        if (!finished) {\n            var self_1 = this;\n            requestAnimationFrame$1(function () {\n                self_1._paintList(list, prevList, paintAll, redrawId);\n            });\n        }\n        else {\n            this.eachLayer(function (layer) {\n                layer.afterBrush && layer.afterBrush();\n            });\n        }\n    };\n    CanvasPainter.prototype._compositeManually = function () {\n        var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;\n        var width = this._domRoot.width;\n        var height = this._domRoot.height;\n        ctx.clearRect(0, 0, width, height);\n        this.eachBuiltinLayer(function (layer) {\n            if (layer.virtual) {\n                ctx.drawImage(layer.dom, 0, 0, width, height);\n            }\n        });\n    };\n    CanvasPainter.prototype._doPaintList = function (list, prevList, paintAll) {\n        var _this = this;\n        var layerList = [];\n        var useDirtyRect = this._opts.useDirtyRect;\n        for (var zi = 0; zi < this._zlevelList.length; zi++) {\n            var zlevel = this._zlevelList[zi];\n            var layer = this._layers[zlevel];\n            if (layer.__builtin__\n                && layer !== this._hoverlayer\n                && (layer.__dirty || paintAll)) {\n                layerList.push(layer);\n            }\n        }\n        var finished = true;\n        var needsRefreshHover = false;\n        var _loop_1 = function (k) {\n            var layer = layerList[k];\n            var ctx = layer.ctx;\n            var repaintRects = useDirtyRect\n                && layer.createRepaintRects(list, prevList, this_1._width, this_1._height);\n            var start = paintAll ? layer.__startIndex : layer.__drawIndex;\n            var useTimer = !paintAll && layer.incremental && Date.now;\n            var startTime = useTimer && Date.now();\n            var clearColor = layer.zlevel === this_1._zlevelList[0]\n                ? this_1._backgroundColor : null;\n            if (layer.__startIndex === layer.__endIndex) {\n                layer.clear(false, clearColor, repaintRects);\n            }\n            else if (start === layer.__startIndex) {\n                var firstEl = list[start];\n                if (!firstEl.incremental || !firstEl.notClear || paintAll) {\n                    layer.clear(false, clearColor, repaintRects);\n                }\n            }\n            if (start === -1) {\n                console.error('For some unknown reason. drawIndex is -1');\n                start = layer.__startIndex;\n            }\n            var i;\n            var repaint = function (repaintRect) {\n                var scope = {\n                    inHover: false,\n                    allClipped: false,\n                    prevEl: null,\n                    viewWidth: _this._width,\n                    viewHeight: _this._height\n                };\n                for (i = start; i < layer.__endIndex; i++) {\n                    var el = list[i];\n                    if (el.__inHover) {\n                        needsRefreshHover = true;\n                    }\n                    _this._doPaintEl(el, layer, useDirtyRect, repaintRect, scope, i === layer.__endIndex - 1);\n                    if (useTimer) {\n                        var dTime = Date.now() - startTime;\n                        if (dTime > 15) {\n                            break;\n                        }\n                    }\n                }\n                if (scope.prevElClipPaths) {\n                    ctx.restore();\n                }\n            };\n            if (repaintRects) {\n                if (repaintRects.length === 0) {\n                    i = layer.__endIndex;\n                }\n                else {\n                    var dpr = this_1.dpr;\n                    for (var r = 0; r < repaintRects.length; ++r) {\n                        var rect = repaintRects[r];\n                        ctx.save();\n                        ctx.beginPath();\n                        ctx.rect(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n                        ctx.clip();\n                        repaint(rect);\n                        ctx.restore();\n                    }\n                }\n            }\n            else {\n                ctx.save();\n                repaint();\n                ctx.restore();\n            }\n            layer.__drawIndex = i;\n            if (layer.__drawIndex < layer.__endIndex) {\n                finished = false;\n            }\n        };\n        var this_1 = this;\n        for (var k = 0; k < layerList.length; k++) {\n            _loop_1(k);\n        }\n        if (env.wxa) {\n            each(this._layers, function (layer) {\n                if (layer && layer.ctx && layer.ctx.draw) {\n                    layer.ctx.draw();\n                }\n            });\n        }\n        return {\n            finished: finished,\n            needsRefreshHover: needsRefreshHover\n        };\n    };\n    CanvasPainter.prototype._doPaintEl = function (el, currentLayer, useDirtyRect, repaintRect, scope, isLast) {\n        var ctx = currentLayer.ctx;\n        if (useDirtyRect) {\n            var paintRect = el.getPaintRect();\n            if (!repaintRect || paintRect && paintRect.intersect(repaintRect)) {\n                brush(ctx, el, scope, isLast);\n                el.setPrevPaintRect(paintRect);\n            }\n        }\n        else {\n            brush(ctx, el, scope, isLast);\n        }\n    };\n    CanvasPainter.prototype.getLayer = function (zlevel, virtual) {\n        if (this._singleCanvas && !this._needsManuallyCompositing) {\n            zlevel = CANVAS_ZLEVEL;\n        }\n        var layer = this._layers[zlevel];\n        if (!layer) {\n            layer = new Layer('zr_' + zlevel, this, this.dpr);\n            layer.zlevel = zlevel;\n            layer.__builtin__ = true;\n            if (this._layerConfig[zlevel]) {\n                merge(layer, this._layerConfig[zlevel], true);\n            }\n            else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) {\n                merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true);\n            }\n            if (virtual) {\n                layer.virtual = virtual;\n            }\n            this.insertLayer(zlevel, layer);\n            layer.initContext();\n        }\n        return layer;\n    };\n    CanvasPainter.prototype.insertLayer = function (zlevel, layer) {\n        var layersMap = this._layers;\n        var zlevelList = this._zlevelList;\n        var len = zlevelList.length;\n        var domRoot = this._domRoot;\n        var prevLayer = null;\n        var i = -1;\n        if (layersMap[zlevel]) {\n            if (\"development\" !== 'production') {\n                logError('ZLevel ' + zlevel + ' has been used already');\n            }\n            return;\n        }\n        if (!isLayerValid(layer)) {\n            if (\"development\" !== 'production') {\n                logError('Layer of zlevel ' + zlevel + ' is not valid');\n            }\n            return;\n        }\n        if (len > 0 && zlevel > zlevelList[0]) {\n            for (i = 0; i < len - 1; i++) {\n                if (zlevelList[i] < zlevel\n                    && zlevelList[i + 1] > zlevel) {\n                    break;\n                }\n            }\n            prevLayer = layersMap[zlevelList[i]];\n        }\n        zlevelList.splice(i + 1, 0, zlevel);\n        layersMap[zlevel] = layer;\n        if (!layer.virtual) {\n            if (prevLayer) {\n                var prevDom = prevLayer.dom;\n                if (prevDom.nextSibling) {\n                    domRoot.insertBefore(layer.dom, prevDom.nextSibling);\n                }\n                else {\n                    domRoot.appendChild(layer.dom);\n                }\n            }\n            else {\n                if (domRoot.firstChild) {\n                    domRoot.insertBefore(layer.dom, domRoot.firstChild);\n                }\n                else {\n                    domRoot.appendChild(layer.dom);\n                }\n            }\n        }\n        layer.__painter = this;\n    };\n    CanvasPainter.prototype.eachLayer = function (cb, context) {\n        var zlevelList = this._zlevelList;\n        for (var i = 0; i < zlevelList.length; i++) {\n            var z = zlevelList[i];\n            cb.call(context, this._layers[z], z);\n        }\n    };\n    CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) {\n        var zlevelList = this._zlevelList;\n        for (var i = 0; i < zlevelList.length; i++) {\n            var z = zlevelList[i];\n            var layer = this._layers[z];\n            if (layer.__builtin__) {\n                cb.call(context, layer, z);\n            }\n        }\n    };\n    CanvasPainter.prototype.eachOtherLayer = function (cb, context) {\n        var zlevelList = this._zlevelList;\n        for (var i = 0; i < zlevelList.length; i++) {\n            var z = zlevelList[i];\n            var layer = this._layers[z];\n            if (!layer.__builtin__) {\n                cb.call(context, layer, z);\n            }\n        }\n    };\n    CanvasPainter.prototype.getLayers = function () {\n        return this._layers;\n    };\n    CanvasPainter.prototype._updateLayerStatus = function (list) {\n        this.eachBuiltinLayer(function (layer, z) {\n            layer.__dirty = layer.__used = false;\n        });\n        function updatePrevLayer(idx) {\n            if (prevLayer) {\n                if (prevLayer.__endIndex !== idx) {\n                    prevLayer.__dirty = true;\n                }\n                prevLayer.__endIndex = idx;\n            }\n        }\n        if (this._singleCanvas) {\n            for (var i_1 = 1; i_1 < list.length; i_1++) {\n                var el = list[i_1];\n                if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) {\n                    this._needsManuallyCompositing = true;\n                    break;\n                }\n            }\n        }\n        var prevLayer = null;\n        var incrementalLayerCount = 0;\n        var prevZlevel;\n        var i;\n        for (i = 0; i < list.length; i++) {\n            var el = list[i];\n            var zlevel = el.zlevel;\n            var layer = void 0;\n            if (prevZlevel !== zlevel) {\n                prevZlevel = zlevel;\n                incrementalLayerCount = 0;\n            }\n            if (el.incremental) {\n                layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);\n                layer.incremental = true;\n                incrementalLayerCount = 1;\n            }\n            else {\n                layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);\n            }\n            if (!layer.__builtin__) {\n                logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);\n            }\n            if (layer !== prevLayer) {\n                layer.__used = true;\n                if (layer.__startIndex !== i) {\n                    layer.__dirty = true;\n                }\n                layer.__startIndex = i;\n                if (!layer.incremental) {\n                    layer.__drawIndex = i;\n                }\n                else {\n                    layer.__drawIndex = -1;\n                }\n                updatePrevLayer(i);\n                prevLayer = layer;\n            }\n            if ((el.__dirty & REDRAW_BIT) && !el.__inHover) {\n                layer.__dirty = true;\n                if (layer.incremental && layer.__drawIndex < 0) {\n                    layer.__drawIndex = i;\n                }\n            }\n        }\n        updatePrevLayer(i);\n        this.eachBuiltinLayer(function (layer, z) {\n            if (!layer.__used && layer.getElementCount() > 0) {\n                layer.__dirty = true;\n                layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;\n            }\n            if (layer.__dirty && layer.__drawIndex < 0) {\n                layer.__drawIndex = layer.__startIndex;\n            }\n        });\n    };\n    CanvasPainter.prototype.clear = function () {\n        this.eachBuiltinLayer(this._clearLayer);\n        return this;\n    };\n    CanvasPainter.prototype._clearLayer = function (layer) {\n        layer.clear();\n    };\n    CanvasPainter.prototype.setBackgroundColor = function (backgroundColor) {\n        this._backgroundColor = backgroundColor;\n        each(this._layers, function (layer) {\n            layer.setUnpainted();\n        });\n    };\n    CanvasPainter.prototype.configLayer = function (zlevel, config) {\n        if (config) {\n            var layerConfig = this._layerConfig;\n            if (!layerConfig[zlevel]) {\n                layerConfig[zlevel] = config;\n            }\n            else {\n                merge(layerConfig[zlevel], config, true);\n            }\n            for (var i = 0; i < this._zlevelList.length; i++) {\n                var _zlevel = this._zlevelList[i];\n                if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {\n                    var layer = this._layers[_zlevel];\n                    merge(layer, layerConfig[zlevel], true);\n                }\n            }\n        }\n    };\n    CanvasPainter.prototype.delLayer = function (zlevel) {\n        var layers = this._layers;\n        var zlevelList = this._zlevelList;\n        var layer = layers[zlevel];\n        if (!layer) {\n            return;\n        }\n        layer.dom.parentNode.removeChild(layer.dom);\n        delete layers[zlevel];\n        zlevelList.splice(indexOf(zlevelList, zlevel), 1);\n    };\n    CanvasPainter.prototype.resize = function (width, height) {\n        if (!this._domRoot.style) {\n            if (width == null || height == null) {\n                return;\n            }\n            this._width = width;\n            this._height = height;\n            this.getLayer(CANVAS_ZLEVEL).resize(width, height);\n        }\n        else {\n            var domRoot = this._domRoot;\n            domRoot.style.display = 'none';\n            var opts = this._opts;\n            var root = this.root;\n            width != null && (opts.width = width);\n            height != null && (opts.height = height);\n            width = getSize(root, 0, opts);\n            height = getSize(root, 1, opts);\n            domRoot.style.display = '';\n            if (this._width !== width || height !== this._height) {\n                domRoot.style.width = width + 'px';\n                domRoot.style.height = height + 'px';\n                for (var id in this._layers) {\n                    if (this._layers.hasOwnProperty(id)) {\n                        this._layers[id].resize(width, height);\n                    }\n                }\n                this.refresh(true);\n            }\n            this._width = width;\n            this._height = height;\n        }\n        return this;\n    };\n    CanvasPainter.prototype.clearLayer = function (zlevel) {\n        var layer = this._layers[zlevel];\n        if (layer) {\n            layer.clear();\n        }\n    };\n    CanvasPainter.prototype.dispose = function () {\n        this.root.innerHTML = '';\n        this.root =\n            this.storage =\n                this._domRoot =\n                    this._layers = null;\n    };\n    CanvasPainter.prototype.getRenderedCanvas = function (opts) {\n        opts = opts || {};\n        if (this._singleCanvas && !this._compositeManually) {\n            return this._layers[CANVAS_ZLEVEL].dom;\n        }\n        var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);\n        imageLayer.initContext();\n        imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);\n        var ctx = imageLayer.ctx;\n        if (opts.pixelRatio <= this.dpr) {\n            this.refresh();\n            var width_1 = imageLayer.dom.width;\n            var height_1 = imageLayer.dom.height;\n            this.eachLayer(function (layer) {\n                if (layer.__builtin__) {\n                    ctx.drawImage(layer.dom, 0, 0, width_1, height_1);\n                }\n                else if (layer.renderToCanvas) {\n                    ctx.save();\n                    layer.renderToCanvas(ctx);\n                    ctx.restore();\n                }\n            });\n        }\n        else {\n            var scope = {\n                inHover: false,\n                viewWidth: this._width,\n                viewHeight: this._height\n            };\n            var displayList = this.storage.getDisplayList(true);\n            for (var i = 0, len = displayList.length; i < len; i++) {\n                var el = displayList[i];\n                brush(ctx, el, scope, i === len - 1);\n            }\n        }\n        return imageLayer.dom;\n    };\n    CanvasPainter.prototype.getWidth = function () {\n        return this._width;\n    };\n    CanvasPainter.prototype.getHeight = function () {\n        return this._height;\n    };\n    return CanvasPainter;\n}());\n\nfunction install$1(registers) {\n  registers.registerPainter('canvas', CanvasPainter);\n}\n\nvar LineSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(LineSeriesModel, _super);\n\n  function LineSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = LineSeriesModel.type;\n    _this.hasSymbolVisual = true;\n    return _this;\n  }\n\n  LineSeriesModel.prototype.getInitialData = function (option) {\n    if (\"development\" !== 'production') {\n      var coordSys = option.coordinateSystem;\n\n      if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {\n        throw new Error('Line not support coordinateSystem besides cartesian and polar');\n      }\n    }\n\n    return createSeriesData(null, this, {\n      useEncodeDefaulter: true\n    });\n  };\n\n  LineSeriesModel.prototype.getLegendIcon = function (opt) {\n    var group = new Group();\n    var line = createSymbol('line', 0, opt.itemHeight / 2, opt.itemWidth, 0, opt.lineStyle.stroke, false);\n    group.add(line);\n    line.setStyle(opt.lineStyle);\n    var visualType = this.getData().getVisual('symbol');\n    var visualRotate = this.getData().getVisual('symbolRotate');\n    var symbolType = visualType === 'none' ? 'circle' : visualType; // Symbol size is 80% when there is a line\n\n    var size = opt.itemHeight * 0.8;\n    var symbol = createSymbol(symbolType, (opt.itemWidth - size) / 2, (opt.itemHeight - size) / 2, size, size, opt.itemStyle.fill);\n    group.add(symbol);\n    symbol.setStyle(opt.itemStyle);\n    var symbolRotate = opt.iconRotate === 'inherit' ? visualRotate : opt.iconRotate || 0;\n    symbol.rotation = symbolRotate * Math.PI / 180;\n    symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);\n\n    if (symbolType.indexOf('empty') > -1) {\n      symbol.style.stroke = symbol.style.fill;\n      symbol.style.fill = '#fff';\n      symbol.style.lineWidth = 2;\n    }\n\n    return group;\n  };\n\n  LineSeriesModel.type = 'series.line';\n  LineSeriesModel.dependencies = ['grid', 'polar'];\n  LineSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 3,\n    coordinateSystem: 'cartesian2d',\n    legendHoverLink: true,\n    clip: true,\n    label: {\n      position: 'top'\n    },\n    // itemStyle: {\n    // },\n    endLabel: {\n      show: false,\n      valueAnimation: true,\n      distance: 8\n    },\n    lineStyle: {\n      width: 2,\n      type: 'solid'\n    },\n    emphasis: {\n      scale: true\n    },\n    // areaStyle: {\n    // origin of areaStyle. Valid values:\n    // `'auto'/null/undefined`: from axisLine to data\n    // `'start'`: from min to data\n    // `'end'`: from data to max\n    // origin: 'auto'\n    // },\n    // false, 'start', 'end', 'middle'\n    step: false,\n    // Disabled if step is true\n    smooth: false,\n    smoothMonotone: null,\n    symbol: 'emptyCircle',\n    symbolSize: 4,\n    symbolRotate: null,\n    showSymbol: true,\n    // `false`: follow the label interval strategy.\n    // `true`: show all symbols.\n    // `'auto'`: If possible, show all symbols, otherwise\n    //           follow the label interval strategy.\n    showAllSymbol: 'auto',\n    // Whether to connect break point.\n    connectNulls: false,\n    // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'.\n    sampling: 'none',\n    animationEasing: 'linear',\n    // Disable progressive\n    progressive: 0,\n    hoverLayerThreshold: Infinity,\n    universalTransition: {\n      divideShape: 'clone'\n    },\n    triggerLineEvent: false\n  };\n  return LineSeriesModel;\n}(SeriesModel);\n\n/**\n * @return label string. Not null/undefined\n */\n\nfunction getDefaultLabel(data, dataIndex) {\n  var labelDims = data.mapDimensionsAll('defaultedLabel');\n  var len = labelDims.length; // Simple optimization (in lots of cases, label dims length is 1)\n\n  if (len === 1) {\n    var rawVal = retrieveRawValue(data, dataIndex, labelDims[0]);\n    return rawVal != null ? rawVal + '' : null;\n  } else if (len) {\n    var vals = [];\n\n    for (var i = 0; i < labelDims.length; i++) {\n      vals.push(retrieveRawValue(data, dataIndex, labelDims[i]));\n    }\n\n    return vals.join(' ');\n  }\n}\nfunction getDefaultInterpolatedLabel(data, interpolatedValue) {\n  var labelDims = data.mapDimensionsAll('defaultedLabel');\n\n  if (!isArray(interpolatedValue)) {\n    return interpolatedValue + '';\n  }\n\n  var vals = [];\n\n  for (var i = 0; i < labelDims.length; i++) {\n    var dimIndex = data.getDimensionIndex(labelDims[i]);\n\n    if (dimIndex >= 0) {\n      vals.push(interpolatedValue[dimIndex]);\n    }\n  }\n\n  return vals.join(' ');\n}\n\nvar Symbol =\n/** @class */\nfunction (_super) {\n  __extends(Symbol, _super);\n\n  function Symbol(data, idx, seriesScope, opts) {\n    var _this = _super.call(this) || this;\n\n    _this.updateData(data, idx, seriesScope, opts);\n\n    return _this;\n  }\n\n  Symbol.prototype._createSymbol = function (symbolType, data, idx, symbolSize, keepAspect) {\n    // Remove paths created before\n    this.removeAll(); // let symbolPath = createSymbol(\n    //     symbolType, -0.5, -0.5, 1, 1, color\n    // );\n    // If width/height are set too small (e.g., set to 1) on ios10\n    // and macOS Sierra, a circle stroke become a rect, no matter what\n    // the scale is set. So we set width/height as 2. See #4150.\n\n    var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, null, keepAspect);\n    symbolPath.attr({\n      z2: 100,\n      culling: true,\n      scaleX: symbolSize[0] / 2,\n      scaleY: symbolSize[1] / 2\n    }); // Rewrite drift method\n\n    symbolPath.drift = driftSymbol;\n    this._symbolType = symbolType;\n    this.add(symbolPath);\n  };\n  /**\n   * Stop animation\n   * @param {boolean} toLastFrame\n   */\n\n\n  Symbol.prototype.stopSymbolAnimation = function (toLastFrame) {\n    this.childAt(0).stopAnimation(null, toLastFrame);\n  };\n\n  Symbol.prototype.getSymbolType = function () {\n    return this._symbolType;\n  };\n  /**\n   * FIXME:\n   * Caution: This method breaks the encapsulation of this module,\n   * but it indeed brings convenience. So do not use the method\n   * unless you detailedly know all the implements of `Symbol`,\n   * especially animation.\n   *\n   * Get symbol path element.\n   */\n\n\n  Symbol.prototype.getSymbolPath = function () {\n    return this.childAt(0);\n  };\n  /**\n   * Highlight symbol\n   */\n\n\n  Symbol.prototype.highlight = function () {\n    enterEmphasis(this.childAt(0));\n  };\n  /**\n   * Downplay symbol\n   */\n\n\n  Symbol.prototype.downplay = function () {\n    leaveEmphasis(this.childAt(0));\n  };\n  /**\n   * @param {number} zlevel\n   * @param {number} z\n   */\n\n\n  Symbol.prototype.setZ = function (zlevel, z) {\n    var symbolPath = this.childAt(0);\n    symbolPath.zlevel = zlevel;\n    symbolPath.z = z;\n  };\n\n  Symbol.prototype.setDraggable = function (draggable, hasCursorOption) {\n    var symbolPath = this.childAt(0);\n    symbolPath.draggable = draggable;\n    symbolPath.cursor = !hasCursorOption && draggable ? 'move' : symbolPath.cursor;\n  };\n  /**\n   * Update symbol properties\n   */\n\n\n  Symbol.prototype.updateData = function (data, idx, seriesScope, opts) {\n    this.silent = false;\n    var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';\n    var seriesModel = data.hostModel;\n    var symbolSize = Symbol.getSymbolSize(data, idx);\n    var isInit = symbolType !== this._symbolType;\n    var disableAnimation = opts && opts.disableAnimation;\n\n    if (isInit) {\n      var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect');\n\n      this._createSymbol(symbolType, data, idx, symbolSize, keepAspect);\n    } else {\n      var symbolPath = this.childAt(0);\n      symbolPath.silent = false;\n      var target = {\n        scaleX: symbolSize[0] / 2,\n        scaleY: symbolSize[1] / 2\n      };\n      disableAnimation ? symbolPath.attr(target) : updateProps(symbolPath, target, seriesModel, idx);\n      saveOldStyle(symbolPath);\n    }\n\n    this._updateCommon(data, idx, symbolSize, seriesScope, opts);\n\n    if (isInit) {\n      var symbolPath = this.childAt(0);\n\n      if (!disableAnimation) {\n        var target = {\n          scaleX: this._sizeX,\n          scaleY: this._sizeY,\n          style: {\n            // Always fadeIn. Because it has fadeOut animation when symbol is removed..\n            opacity: symbolPath.style.opacity\n          }\n        };\n        symbolPath.scaleX = symbolPath.scaleY = 0;\n        symbolPath.style.opacity = 0;\n        initProps(symbolPath, target, seriesModel, idx);\n      }\n    }\n\n    if (disableAnimation) {\n      // Must stop leave transition manually if don't call initProps or updateProps.\n      this.childAt(0).stopAnimation('leave');\n    }\n  };\n\n  Symbol.prototype._updateCommon = function (data, idx, symbolSize, seriesScope, opts) {\n    var symbolPath = this.childAt(0);\n    var seriesModel = data.hostModel;\n    var emphasisItemStyle;\n    var blurItemStyle;\n    var selectItemStyle;\n    var focus;\n    var blurScope;\n    var emphasisDisabled;\n    var labelStatesModels;\n    var hoverScale;\n    var cursorStyle;\n\n    if (seriesScope) {\n      emphasisItemStyle = seriesScope.emphasisItemStyle;\n      blurItemStyle = seriesScope.blurItemStyle;\n      selectItemStyle = seriesScope.selectItemStyle;\n      focus = seriesScope.focus;\n      blurScope = seriesScope.blurScope;\n      labelStatesModels = seriesScope.labelStatesModels;\n      hoverScale = seriesScope.hoverScale;\n      cursorStyle = seriesScope.cursorStyle;\n      emphasisDisabled = seriesScope.emphasisDisabled;\n    }\n\n    if (!seriesScope || data.hasItemOption) {\n      var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx);\n      var emphasisModel = itemModel.getModel('emphasis');\n      emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n      selectItemStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();\n      blurItemStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n      focus = emphasisModel.get('focus');\n      blurScope = emphasisModel.get('blurScope');\n      emphasisDisabled = emphasisModel.get('disabled');\n      labelStatesModels = getLabelStatesModels(itemModel);\n      hoverScale = emphasisModel.getShallow('scale');\n      cursorStyle = itemModel.getShallow('cursor');\n    }\n\n    var symbolRotate = data.getItemVisual(idx, 'symbolRotate');\n    symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);\n    var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize);\n\n    if (symbolOffset) {\n      symbolPath.x = symbolOffset[0];\n      symbolPath.y = symbolOffset[1];\n    }\n\n    cursorStyle && symbolPath.attr('cursor', cursorStyle);\n    var symbolStyle = data.getItemVisual(idx, 'style');\n    var visualColor = symbolStyle.fill;\n\n    if (symbolPath instanceof ZRImage) {\n      var pathStyle = symbolPath.style;\n      symbolPath.useStyle(extend({\n        // TODO other properties like x, y ?\n        image: pathStyle.image,\n        x: pathStyle.x,\n        y: pathStyle.y,\n        width: pathStyle.width,\n        height: pathStyle.height\n      }, symbolStyle));\n    } else {\n      if (symbolPath.__isEmptyBrush) {\n        // fill and stroke will be swapped if it's empty.\n        // So we cloned a new style to avoid it affecting the original style in visual storage.\n        // TODO Better implementation. No empty logic!\n        symbolPath.useStyle(extend({}, symbolStyle));\n      } else {\n        symbolPath.useStyle(symbolStyle);\n      } // Disable decal because symbol scale will been applied on the decal.\n\n\n      symbolPath.style.decal = null;\n      symbolPath.setColor(visualColor, opts && opts.symbolInnerColor);\n      symbolPath.style.strokeNoScale = true;\n    }\n\n    var liftZ = data.getItemVisual(idx, 'liftZ');\n    var z2Origin = this._z2;\n\n    if (liftZ != null) {\n      if (z2Origin == null) {\n        this._z2 = symbolPath.z2;\n        symbolPath.z2 += liftZ;\n      }\n    } else if (z2Origin != null) {\n      symbolPath.z2 = z2Origin;\n      this._z2 = null;\n    }\n\n    var useNameLabel = opts && opts.useNameLabel;\n    setLabelStyle(symbolPath, labelStatesModels, {\n      labelFetcher: seriesModel,\n      labelDataIndex: idx,\n      defaultText: getLabelDefaultText,\n      inheritColor: visualColor,\n      defaultOpacity: symbolStyle.opacity\n    }); // Do not execute util needed.\n\n    function getLabelDefaultText(idx) {\n      return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);\n    }\n\n    this._sizeX = symbolSize[0] / 2;\n    this._sizeY = symbolSize[1] / 2;\n    var emphasisState = symbolPath.ensureState('emphasis');\n    emphasisState.style = emphasisItemStyle;\n    symbolPath.ensureState('select').style = selectItemStyle;\n    symbolPath.ensureState('blur').style = blurItemStyle; // null / undefined / true means to use default strategy.\n    // 0 / false / negative number / NaN / Infinity means no scale.\n\n    var scaleRatio = hoverScale == null || hoverScale === true ? Math.max(1.1, 3 / this._sizeY) // PENDING: restrict hoverScale > 1? It seems unreasonable to scale down\n    : isFinite(hoverScale) && hoverScale > 0 ? +hoverScale : 1; // always set scale to allow resetting\n\n    emphasisState.scaleX = this._sizeX * scaleRatio;\n    emphasisState.scaleY = this._sizeY * scaleRatio;\n    this.setSymbolScale(1);\n    toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n  };\n\n  Symbol.prototype.setSymbolScale = function (scale) {\n    this.scaleX = this.scaleY = scale;\n  };\n\n  Symbol.prototype.fadeOut = function (cb, seriesModel, opt) {\n    var symbolPath = this.childAt(0);\n    var dataIndex = getECData(this).dataIndex;\n    var animationOpt = opt && opt.animation; // Avoid mistaken hover when fading out\n\n    this.silent = symbolPath.silent = true; // Not show text when animating\n\n    if (opt && opt.fadeLabel) {\n      var textContent = symbolPath.getTextContent();\n\n      if (textContent) {\n        removeElement(textContent, {\n          style: {\n            opacity: 0\n          }\n        }, seriesModel, {\n          dataIndex: dataIndex,\n          removeOpt: animationOpt,\n          cb: function () {\n            symbolPath.removeTextContent();\n          }\n        });\n      }\n    } else {\n      symbolPath.removeTextContent();\n    }\n\n    removeElement(symbolPath, {\n      style: {\n        opacity: 0\n      },\n      scaleX: 0,\n      scaleY: 0\n    }, seriesModel, {\n      dataIndex: dataIndex,\n      cb: cb,\n      removeOpt: animationOpt\n    });\n  };\n\n  Symbol.getSymbolSize = function (data, idx) {\n    return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n  };\n\n  return Symbol;\n}(Group);\n\nfunction driftSymbol(dx, dy) {\n  this.parent.drift(dx, dy);\n}\n\nfunction symbolNeedsDraw(data, point, idx, opt) {\n  return point && !isNaN(point[0]) && !isNaN(point[1]) && !(opt.isIgnore && opt.isIgnore(idx)) // We do not set clipShape on group, because it will cut part of\n  // the symbol element shape. We use the same clip shape here as\n  // the line clip.\n  && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) && data.getItemVisual(idx, 'symbol') !== 'none';\n}\n\nfunction normalizeUpdateOpt(opt) {\n  if (opt != null && !isObject(opt)) {\n    opt = {\n      isIgnore: opt\n    };\n  }\n\n  return opt || {};\n}\n\nfunction makeSeriesScope(data) {\n  var seriesModel = data.hostModel;\n  var emphasisModel = seriesModel.getModel('emphasis');\n  return {\n    emphasisItemStyle: emphasisModel.getModel('itemStyle').getItemStyle(),\n    blurItemStyle: seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(),\n    selectItemStyle: seriesModel.getModel(['select', 'itemStyle']).getItemStyle(),\n    focus: emphasisModel.get('focus'),\n    blurScope: emphasisModel.get('blurScope'),\n    emphasisDisabled: emphasisModel.get('disabled'),\n    hoverScale: emphasisModel.get('scale'),\n    labelStatesModels: getLabelStatesModels(seriesModel),\n    cursorStyle: seriesModel.get('cursor')\n  };\n}\n\nvar SymbolDraw =\n/** @class */\nfunction () {\n  function SymbolDraw(SymbolCtor) {\n    this.group = new Group();\n    this._SymbolCtor = SymbolCtor || Symbol;\n  }\n  /**\n   * Update symbols draw by new data\n   */\n\n\n  SymbolDraw.prototype.updateData = function (data, opt) {\n    // Remove progressive els.\n    this._progressiveEls = null;\n    opt = normalizeUpdateOpt(opt);\n    var group = this.group;\n    var seriesModel = data.hostModel;\n    var oldData = this._data;\n    var SymbolCtor = this._SymbolCtor;\n    var disableAnimation = opt.disableAnimation;\n    var seriesScope = makeSeriesScope(data);\n    var symbolUpdateOpt = {\n      disableAnimation: disableAnimation\n    };\n\n    var getSymbolPoint = opt.getSymbolPoint || function (idx) {\n      return data.getItemLayout(idx);\n    }; // There is no oldLineData only when first rendering or switching from\n    // stream mode to normal mode, where previous elements should be removed.\n\n\n    if (!oldData) {\n      group.removeAll();\n    }\n\n    data.diff(oldData).add(function (newIdx) {\n      var point = getSymbolPoint(newIdx);\n\n      if (symbolNeedsDraw(data, point, newIdx, opt)) {\n        var symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n        symbolEl.setPosition(point);\n        data.setItemGraphicEl(newIdx, symbolEl);\n        group.add(symbolEl);\n      }\n    }).update(function (newIdx, oldIdx) {\n      var symbolEl = oldData.getItemGraphicEl(oldIdx);\n      var point = getSymbolPoint(newIdx);\n\n      if (!symbolNeedsDraw(data, point, newIdx, opt)) {\n        group.remove(symbolEl);\n        return;\n      }\n\n      var newSymbolType = data.getItemVisual(newIdx, 'symbol') || 'circle';\n      var oldSymbolType = symbolEl && symbolEl.getSymbolType && symbolEl.getSymbolType();\n\n      if (!symbolEl // Create a new if symbol type changed.\n      || oldSymbolType && oldSymbolType !== newSymbolType) {\n        group.remove(symbolEl);\n        symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n        symbolEl.setPosition(point);\n      } else {\n        symbolEl.updateData(data, newIdx, seriesScope, symbolUpdateOpt);\n        var target = {\n          x: point[0],\n          y: point[1]\n        };\n        disableAnimation ? symbolEl.attr(target) : updateProps(symbolEl, target, seriesModel);\n      } // Add back\n\n\n      group.add(symbolEl);\n      data.setItemGraphicEl(newIdx, symbolEl);\n    }).remove(function (oldIdx) {\n      var el = oldData.getItemGraphicEl(oldIdx);\n      el && el.fadeOut(function () {\n        group.remove(el);\n      }, seriesModel);\n    }).execute();\n    this._getSymbolPoint = getSymbolPoint;\n    this._data = data;\n  };\n\n  SymbolDraw.prototype.updateLayout = function () {\n    var _this = this;\n\n    var data = this._data;\n\n    if (data) {\n      // Not use animation\n      data.eachItemGraphicEl(function (el, idx) {\n        var point = _this._getSymbolPoint(idx);\n\n        el.setPosition(point);\n        el.markRedraw();\n      });\n    }\n  };\n\n  SymbolDraw.prototype.incrementalPrepareUpdate = function (data) {\n    this._seriesScope = makeSeriesScope(data);\n    this._data = null;\n    this.group.removeAll();\n  };\n  /**\n   * Update symbols draw by new data\n   */\n\n  SymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {\n    // Clear\n    this._progressiveEls = [];\n    opt = normalizeUpdateOpt(opt);\n\n    function updateIncrementalAndHover(el) {\n      if (!el.isGroup) {\n        el.incremental = true;\n        el.ensureState('emphasis').hoverLayer = true;\n      }\n    }\n\n    for (var idx = taskParams.start; idx < taskParams.end; idx++) {\n      var point = data.getItemLayout(idx);\n\n      if (symbolNeedsDraw(data, point, idx, opt)) {\n        var el = new this._SymbolCtor(data, idx, this._seriesScope);\n        el.traverse(updateIncrementalAndHover);\n        el.setPosition(point);\n        this.group.add(el);\n        data.setItemGraphicEl(idx, el);\n\n        this._progressiveEls.push(el);\n      }\n    }\n  };\n\n  SymbolDraw.prototype.eachRendered = function (cb) {\n    traverseElements(this._progressiveEls || this.group, cb);\n  };\n\n  SymbolDraw.prototype.remove = function (enableAnimation) {\n    var group = this.group;\n    var data = this._data; // Incremental model do not have this._data.\n\n    if (data && enableAnimation) {\n      data.eachItemGraphicEl(function (el) {\n        el.fadeOut(function () {\n          group.remove(el);\n        }, data.hostModel);\n      });\n    } else {\n      group.removeAll();\n    }\n  };\n  return SymbolDraw;\n}();\n\nfunction prepareDataCoordInfo(coordSys, data, valueOrigin) {\n  var baseAxis = coordSys.getBaseAxis();\n  var valueAxis = coordSys.getOtherAxis(baseAxis);\n  var valueStart = getValueStart(valueAxis, valueOrigin);\n  var baseAxisDim = baseAxis.dim;\n  var valueAxisDim = valueAxis.dim;\n  var valueDim = data.mapDimension(valueAxisDim);\n  var baseDim = data.mapDimension(baseAxisDim);\n  var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;\n  var dims = map(coordSys.dimensions, function (coordDim) {\n    return data.mapDimension(coordDim);\n  });\n  var stacked = false;\n  var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n  if (isDimensionStacked(data, dims[0]\n  /* , dims[1] */\n  )) {\n    // jshint ignore:line\n    stacked = true;\n    dims[0] = stackResultDim;\n  }\n\n  if (isDimensionStacked(data, dims[1]\n  /* , dims[0] */\n  )) {\n    // jshint ignore:line\n    stacked = true;\n    dims[1] = stackResultDim;\n  }\n\n  return {\n    dataDimsForPoint: dims,\n    valueStart: valueStart,\n    valueAxisDim: valueAxisDim,\n    baseAxisDim: baseAxisDim,\n    stacked: !!stacked,\n    valueDim: valueDim,\n    baseDim: baseDim,\n    baseDataOffset: baseDataOffset,\n    stackedOverDimension: data.getCalculationInfo('stackedOverDimension')\n  };\n}\n\nfunction getValueStart(valueAxis, valueOrigin) {\n  var valueStart = 0;\n  var extent = valueAxis.scale.getExtent();\n\n  if (valueOrigin === 'start') {\n    valueStart = extent[0];\n  } else if (valueOrigin === 'end') {\n    valueStart = extent[1];\n  } // If origin is specified as a number, use it as\n  // valueStart directly\n  else if (isNumber(valueOrigin) && !isNaN(valueOrigin)) {\n      valueStart = valueOrigin;\n    } // auto\n    else {\n        // Both positive\n        if (extent[0] > 0) {\n          valueStart = extent[0];\n        } // Both negative\n        else if (extent[1] < 0) {\n            valueStart = extent[1];\n          } // If is one positive, and one negative, onZero shall be true\n\n      }\n\n  return valueStart;\n}\n\nfunction getStackedOnPoint(dataCoordInfo, coordSys, data, idx) {\n  var value = NaN;\n\n  if (dataCoordInfo.stacked) {\n    value = data.get(data.getCalculationInfo('stackedOverDimension'), idx);\n  }\n\n  if (isNaN(value)) {\n    value = dataCoordInfo.valueStart;\n  }\n\n  var baseDataOffset = dataCoordInfo.baseDataOffset;\n  var stackedData = [];\n  stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx);\n  stackedData[1 - baseDataOffset] = value;\n  return coordSys.dataToPoint(stackedData);\n}\n\nfunction diffData(oldData, newData) {\n  var diffResult = [];\n  newData.diff(oldData).add(function (idx) {\n    diffResult.push({\n      cmd: '+',\n      idx: idx\n    });\n  }).update(function (newIdx, oldIdx) {\n    diffResult.push({\n      cmd: '=',\n      idx: oldIdx,\n      idx1: newIdx\n    });\n  }).remove(function (idx) {\n    diffResult.push({\n      cmd: '-',\n      idx: idx\n    });\n  }).execute();\n  return diffResult;\n}\n\nfunction lineAnimationDiff(oldData, newData, oldStackedOnPoints, newStackedOnPoints, oldCoordSys, newCoordSys, oldValueOrigin, newValueOrigin) {\n  var diff = diffData(oldData, newData); // let newIdList = newData.mapArray(newData.getId);\n  // let oldIdList = oldData.mapArray(oldData.getId);\n  // convertToIntId(newIdList, oldIdList);\n  // // FIXME One data ?\n  // diff = arrayDiff(oldIdList, newIdList);\n\n  var currPoints = [];\n  var nextPoints = []; // Points for stacking base line\n\n  var currStackedPoints = [];\n  var nextStackedPoints = [];\n  var status = [];\n  var sortedIndices = [];\n  var rawIndices = [];\n  var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); // const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin);\n\n  var oldPoints = oldData.getLayout('points') || [];\n  var newPoints = newData.getLayout('points') || [];\n\n  for (var i = 0; i < diff.length; i++) {\n    var diffItem = diff[i];\n    var pointAdded = true;\n    var oldIdx2 = void 0;\n    var newIdx2 = void 0; // FIXME, animation is not so perfect when dataZoom window moves fast\n    // Which is in case remvoing or add more than one data in the tail or head\n\n    switch (diffItem.cmd) {\n      case '=':\n        oldIdx2 = diffItem.idx * 2;\n        newIdx2 = diffItem.idx1 * 2;\n        var currentX = oldPoints[oldIdx2];\n        var currentY = oldPoints[oldIdx2 + 1];\n        var nextX = newPoints[newIdx2];\n        var nextY = newPoints[newIdx2 + 1]; // If previous data is NaN, use next point directly\n\n        if (isNaN(currentX) || isNaN(currentY)) {\n          currentX = nextX;\n          currentY = nextY;\n        }\n\n        currPoints.push(currentX, currentY);\n        nextPoints.push(nextX, nextY);\n        currStackedPoints.push(oldStackedOnPoints[oldIdx2], oldStackedOnPoints[oldIdx2 + 1]);\n        nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n        rawIndices.push(newData.getRawIndex(diffItem.idx1));\n        break;\n\n      case '+':\n        var newIdx = diffItem.idx;\n        var newDataDimsForPoint = newDataOldCoordInfo.dataDimsForPoint;\n        var oldPt = oldCoordSys.dataToPoint([newData.get(newDataDimsForPoint[0], newIdx), newData.get(newDataDimsForPoint[1], newIdx)]);\n        newIdx2 = newIdx * 2;\n        currPoints.push(oldPt[0], oldPt[1]);\n        nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1]);\n        var stackedOnPoint = getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, newIdx);\n        currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1]);\n        nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n        rawIndices.push(newData.getRawIndex(newIdx));\n        break;\n\n      case '-':\n        pointAdded = false;\n    } // Original indices\n\n\n    if (pointAdded) {\n      status.push(diffItem);\n      sortedIndices.push(sortedIndices.length);\n    }\n  } // Diff result may be crossed if all items are changed\n  // Sort by data index\n\n\n  sortedIndices.sort(function (a, b) {\n    return rawIndices[a] - rawIndices[b];\n  });\n  var len = currPoints.length;\n  var sortedCurrPoints = createFloat32Array(len);\n  var sortedNextPoints = createFloat32Array(len);\n  var sortedCurrStackedPoints = createFloat32Array(len);\n  var sortedNextStackedPoints = createFloat32Array(len);\n  var sortedStatus = [];\n\n  for (var i = 0; i < sortedIndices.length; i++) {\n    var idx = sortedIndices[i];\n    var i2 = i * 2;\n    var idx2 = idx * 2;\n    sortedCurrPoints[i2] = currPoints[idx2];\n    sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1];\n    sortedNextPoints[i2] = nextPoints[idx2];\n    sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1];\n    sortedCurrStackedPoints[i2] = currStackedPoints[idx2];\n    sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1];\n    sortedNextStackedPoints[i2] = nextStackedPoints[idx2];\n    sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1];\n    sortedStatus[i] = status[idx];\n  }\n\n  return {\n    current: sortedCurrPoints,\n    next: sortedNextPoints,\n    stackedOnCurrent: sortedCurrStackedPoints,\n    stackedOnNext: sortedNextStackedPoints,\n    status: sortedStatus\n  };\n}\n\nvar mathMin$5 = Math.min;\nvar mathMax$5 = Math.max;\n\nfunction isPointNull(x, y) {\n  return isNaN(x) || isNaN(y);\n}\n/**\n * Draw smoothed line in non-monotone, in may cause undesired curve in extreme\n * situations. This should be used when points are non-monotone neither in x or\n * y dimension.\n */\n\n\nfunction drawSegment(ctx, points, start, segLen, allLen, dir, smooth, smoothMonotone, connectNulls) {\n  var prevX;\n  var prevY;\n  var cpx0;\n  var cpy0;\n  var cpx1;\n  var cpy1;\n  var idx = start;\n  var k = 0;\n\n  for (; k < segLen; k++) {\n    var x = points[idx * 2];\n    var y = points[idx * 2 + 1];\n\n    if (idx >= allLen || idx < 0) {\n      break;\n    }\n\n    if (isPointNull(x, y)) {\n      if (connectNulls) {\n        idx += dir;\n        continue;\n      }\n\n      break;\n    }\n\n    if (idx === start) {\n      ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y);\n      cpx0 = x;\n      cpy0 = y;\n    } else {\n      var dx = x - prevX;\n      var dy = y - prevY; // Ignore tiny segment.\n\n      if (dx * dx + dy * dy < 0.5) {\n        idx += dir;\n        continue;\n      }\n\n      if (smooth > 0) {\n        var nextIdx = idx + dir;\n        var nextX = points[nextIdx * 2];\n        var nextY = points[nextIdx * 2 + 1]; // Ignore duplicate point\n\n        while (nextX === x && nextY === y && k < segLen) {\n          k++;\n          nextIdx += dir;\n          idx += dir;\n          nextX = points[nextIdx * 2];\n          nextY = points[nextIdx * 2 + 1];\n          x = points[idx * 2];\n          y = points[idx * 2 + 1];\n          dx = x - prevX;\n          dy = y - prevY;\n        }\n\n        var tmpK = k + 1;\n\n        if (connectNulls) {\n          // Find next point not null\n          while (isPointNull(nextX, nextY) && tmpK < segLen) {\n            tmpK++;\n            nextIdx += dir;\n            nextX = points[nextIdx * 2];\n            nextY = points[nextIdx * 2 + 1];\n          }\n        }\n\n        var ratioNextSeg = 0.5;\n        var vx = 0;\n        var vy = 0;\n        var nextCpx0 = void 0;\n        var nextCpy0 = void 0; // Is last point\n\n        if (tmpK >= segLen || isPointNull(nextX, nextY)) {\n          cpx1 = x;\n          cpy1 = y;\n        } else {\n          vx = nextX - prevX;\n          vy = nextY - prevY;\n          var dx0 = x - prevX;\n          var dx1 = nextX - x;\n          var dy0 = y - prevY;\n          var dy1 = nextY - y;\n          var lenPrevSeg = void 0;\n          var lenNextSeg = void 0;\n\n          if (smoothMonotone === 'x') {\n            lenPrevSeg = Math.abs(dx0);\n            lenNextSeg = Math.abs(dx1);\n            var dir_1 = vx > 0 ? 1 : -1;\n            cpx1 = x - dir_1 * lenPrevSeg * smooth;\n            cpy1 = y;\n            nextCpx0 = x + dir_1 * lenNextSeg * smooth;\n            nextCpy0 = y;\n          } else if (smoothMonotone === 'y') {\n            lenPrevSeg = Math.abs(dy0);\n            lenNextSeg = Math.abs(dy1);\n            var dir_2 = vy > 0 ? 1 : -1;\n            cpx1 = x;\n            cpy1 = y - dir_2 * lenPrevSeg * smooth;\n            nextCpx0 = x;\n            nextCpy0 = y + dir_2 * lenNextSeg * smooth;\n          } else {\n            lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0);\n            lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1); // Use ratio of seg length\n\n            ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);\n            cpx1 = x - vx * smooth * (1 - ratioNextSeg);\n            cpy1 = y - vy * smooth * (1 - ratioNextSeg); // cp0 of next segment\n\n            nextCpx0 = x + vx * smooth * ratioNextSeg;\n            nextCpy0 = y + vy * smooth * ratioNextSeg; // Smooth constraint between point and next point.\n            // Avoid exceeding extreme after smoothing.\n\n            nextCpx0 = mathMin$5(nextCpx0, mathMax$5(nextX, x));\n            nextCpy0 = mathMin$5(nextCpy0, mathMax$5(nextY, y));\n            nextCpx0 = mathMax$5(nextCpx0, mathMin$5(nextX, x));\n            nextCpy0 = mathMax$5(nextCpy0, mathMin$5(nextY, y)); // Reclaculate cp1 based on the adjusted cp0 of next seg.\n\n            vx = nextCpx0 - x;\n            vy = nextCpy0 - y;\n            cpx1 = x - vx * lenPrevSeg / lenNextSeg;\n            cpy1 = y - vy * lenPrevSeg / lenNextSeg; // Smooth constraint between point and prev point.\n            // Avoid exceeding extreme after smoothing.\n\n            cpx1 = mathMin$5(cpx1, mathMax$5(prevX, x));\n            cpy1 = mathMin$5(cpy1, mathMax$5(prevY, y));\n            cpx1 = mathMax$5(cpx1, mathMin$5(prevX, x));\n            cpy1 = mathMax$5(cpy1, mathMin$5(prevY, y)); // Adjust next cp0 again.\n\n            vx = x - cpx1;\n            vy = y - cpy1;\n            nextCpx0 = x + vx * lenNextSeg / lenPrevSeg;\n            nextCpy0 = y + vy * lenNextSeg / lenPrevSeg;\n          }\n        }\n\n        ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y);\n        cpx0 = nextCpx0;\n        cpy0 = nextCpy0;\n      } else {\n        ctx.lineTo(x, y);\n      }\n    }\n\n    prevX = x;\n    prevY = y;\n    idx += dir;\n  }\n\n  return k;\n}\n\nvar ECPolylineShape =\n/** @class */\nfunction () {\n  function ECPolylineShape() {\n    this.smooth = 0;\n    this.smoothConstraint = true;\n  }\n\n  return ECPolylineShape;\n}();\n\nvar ECPolyline =\n/** @class */\nfunction (_super) {\n  __extends(ECPolyline, _super);\n\n  function ECPolyline(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'ec-polyline';\n    return _this;\n  }\n\n  ECPolyline.prototype.getDefaultStyle = function () {\n    return {\n      stroke: '#000',\n      fill: null\n    };\n  };\n\n  ECPolyline.prototype.getDefaultShape = function () {\n    return new ECPolylineShape();\n  };\n\n  ECPolyline.prototype.buildPath = function (ctx, shape) {\n    var points = shape.points;\n    var i = 0;\n    var len = points.length / 2; // const result = getBoundingBox(points, shape.smoothConstraint);\n\n    if (shape.connectNulls) {\n      // Must remove first and last null values avoid draw error in polygon\n      for (; len > 0; len--) {\n        if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n          break;\n        }\n      }\n\n      for (; i < len; i++) {\n        if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n          break;\n        }\n      }\n    }\n\n    while (i < len) {\n      i += drawSegment(ctx, points, i, len, len, 1, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1;\n    }\n  };\n\n  ECPolyline.prototype.getPointOn = function (xOrY, dim) {\n    if (!this.path) {\n      this.createPathProxy();\n      this.buildPath(this.path, this.shape);\n    }\n\n    var path = this.path;\n    var data = path.data;\n    var CMD = PathProxy.CMD;\n    var x0;\n    var y0;\n    var isDimX = dim === 'x';\n    var roots = [];\n\n    for (var i = 0; i < data.length;) {\n      var cmd = data[i++];\n      var x = void 0;\n      var y = void 0;\n      var x2 = void 0;\n      var y2 = void 0;\n      var x3 = void 0;\n      var y3 = void 0;\n      var t = void 0;\n\n      switch (cmd) {\n        case CMD.M:\n          x0 = data[i++];\n          y0 = data[i++];\n          break;\n\n        case CMD.L:\n          x = data[i++];\n          y = data[i++];\n          t = isDimX ? (xOrY - x0) / (x - x0) : (xOrY - y0) / (y - y0);\n\n          if (t <= 1 && t >= 0) {\n            var val = isDimX ? (y - y0) * t + y0 : (x - x0) * t + x0;\n            return isDimX ? [xOrY, val] : [val, xOrY];\n          }\n\n          x0 = x;\n          y0 = y;\n          break;\n\n        case CMD.C:\n          x = data[i++];\n          y = data[i++];\n          x2 = data[i++];\n          y2 = data[i++];\n          x3 = data[i++];\n          y3 = data[i++];\n          var nRoot = isDimX ? cubicRootAt(x0, x, x2, x3, xOrY, roots) : cubicRootAt(y0, y, y2, y3, xOrY, roots);\n\n          if (nRoot > 0) {\n            for (var i_1 = 0; i_1 < nRoot; i_1++) {\n              var t_1 = roots[i_1];\n\n              if (t_1 <= 1 && t_1 >= 0) {\n                var val = isDimX ? cubicAt(y0, y, y2, y3, t_1) : cubicAt(x0, x, x2, x3, t_1);\n                return isDimX ? [xOrY, val] : [val, xOrY];\n              }\n            }\n          }\n\n          x0 = x3;\n          y0 = y3;\n          break;\n      }\n    }\n  };\n\n  return ECPolyline;\n}(Path);\n\nvar ECPolygonShape =\n/** @class */\nfunction (_super) {\n  __extends(ECPolygonShape, _super);\n\n  function ECPolygonShape() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  return ECPolygonShape;\n}(ECPolylineShape);\n\nvar ECPolygon =\n/** @class */\nfunction (_super) {\n  __extends(ECPolygon, _super);\n\n  function ECPolygon(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'ec-polygon';\n    return _this;\n  }\n\n  ECPolygon.prototype.getDefaultShape = function () {\n    return new ECPolygonShape();\n  };\n\n  ECPolygon.prototype.buildPath = function (ctx, shape) {\n    var points = shape.points;\n    var stackedOnPoints = shape.stackedOnPoints;\n    var i = 0;\n    var len = points.length / 2;\n    var smoothMonotone = shape.smoothMonotone;\n\n    if (shape.connectNulls) {\n      // Must remove first and last null values avoid draw error in polygon\n      for (; len > 0; len--) {\n        if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n          break;\n        }\n      }\n\n      for (; i < len; i++) {\n        if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n          break;\n        }\n      }\n    }\n\n    while (i < len) {\n      var k = drawSegment(ctx, points, i, len, len, 1, shape.smooth, smoothMonotone, shape.connectNulls);\n      drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls);\n      i += k + 1;\n      ctx.closePath();\n    }\n  };\n\n  return ECPolygon;\n}(Path);\n\nfunction createGridClipPath(cartesian, hasAnimation, seriesModel, done, during) {\n  var rect = cartesian.getArea();\n  var x = rect.x;\n  var y = rect.y;\n  var width = rect.width;\n  var height = rect.height;\n  var lineWidth = seriesModel.get(['lineStyle', 'width']) || 2; // Expand the clip path a bit to avoid the border is clipped and looks thinner\n\n  x -= lineWidth / 2;\n  y -= lineWidth / 2;\n  width += lineWidth;\n  height += lineWidth; // fix: https://github.com/apache/incubator-echarts/issues/11369\n\n  x = Math.floor(x);\n  width = Math.round(width);\n  var clipPath = new Rect({\n    shape: {\n      x: x,\n      y: y,\n      width: width,\n      height: height\n    }\n  });\n\n  if (hasAnimation) {\n    var baseAxis = cartesian.getBaseAxis();\n    var isHorizontal = baseAxis.isHorizontal();\n    var isAxisInversed = baseAxis.inverse;\n\n    if (isHorizontal) {\n      if (isAxisInversed) {\n        clipPath.shape.x += width;\n      }\n\n      clipPath.shape.width = 0;\n    } else {\n      if (!isAxisInversed) {\n        clipPath.shape.y += height;\n      }\n\n      clipPath.shape.height = 0;\n    }\n\n    var duringCb = isFunction(during) ? function (percent) {\n      during(percent, clipPath);\n    } : null;\n    initProps(clipPath, {\n      shape: {\n        width: width,\n        height: height,\n        x: x,\n        y: y\n      }\n    }, seriesModel, null, done, duringCb);\n  }\n\n  return clipPath;\n}\n\nfunction createPolarClipPath(polar, hasAnimation, seriesModel) {\n  var sectorArea = polar.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n\n  var r0 = round(sectorArea.r0, 1);\n  var r = round(sectorArea.r, 1);\n  var clipPath = new Sector({\n    shape: {\n      cx: round(polar.cx, 1),\n      cy: round(polar.cy, 1),\n      r0: r0,\n      r: r,\n      startAngle: sectorArea.startAngle,\n      endAngle: sectorArea.endAngle,\n      clockwise: sectorArea.clockwise\n    }\n  });\n\n  if (hasAnimation) {\n    var isRadial = polar.getBaseAxis().dim === 'angle';\n\n    if (isRadial) {\n      clipPath.shape.endAngle = sectorArea.startAngle;\n    } else {\n      clipPath.shape.r = r0;\n    }\n\n    initProps(clipPath, {\n      shape: {\n        endAngle: sectorArea.endAngle,\n        r: r\n      }\n    }, seriesModel);\n  }\n\n  return clipPath;\n}\n\nfunction createClipPath(coordSys, hasAnimation, seriesModel, done, during) {\n  if (!coordSys) {\n    return null;\n  } else if (coordSys.type === 'polar') {\n    return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n  } else if (coordSys.type === 'cartesian2d') {\n    return createGridClipPath(coordSys, hasAnimation, seriesModel, done, during);\n  }\n\n  return null;\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction isCoordinateSystemType(coordSys, type) {\n  return coordSys.type === type;\n}\n\nfunction isPointsSame(points1, points2) {\n  if (points1.length !== points2.length) {\n    return;\n  }\n\n  for (var i = 0; i < points1.length; i++) {\n    if (points1[i] !== points2[i]) {\n      return;\n    }\n  }\n\n  return true;\n}\n\nfunction bboxFromPoints(points) {\n  var minX = Infinity;\n  var minY = Infinity;\n  var maxX = -Infinity;\n  var maxY = -Infinity;\n\n  for (var i = 0; i < points.length;) {\n    var x = points[i++];\n    var y = points[i++];\n\n    if (!isNaN(x)) {\n      minX = Math.min(x, minX);\n      maxX = Math.max(x, maxX);\n    }\n\n    if (!isNaN(y)) {\n      minY = Math.min(y, minY);\n      maxY = Math.max(y, maxY);\n    }\n  }\n\n  return [[minX, minY], [maxX, maxY]];\n}\n\nfunction getBoundingDiff(points1, points2) {\n  var _a = bboxFromPoints(points1),\n      min1 = _a[0],\n      max1 = _a[1];\n\n  var _b = bboxFromPoints(points2),\n      min2 = _b[0],\n      max2 = _b[1]; // Get a max value from each corner of two boundings.\n\n\n  return Math.max(Math.abs(min1[0] - min2[0]), Math.abs(min1[1] - min2[1]), Math.abs(max1[0] - max2[0]), Math.abs(max1[1] - max2[1]));\n}\n\nfunction getSmooth(smooth) {\n  return isNumber(smooth) ? smooth : smooth ? 0.5 : 0;\n}\n\nfunction getStackedOnPoints(coordSys, data, dataCoordInfo) {\n  if (!dataCoordInfo.valueDim) {\n    return [];\n  }\n\n  var len = data.count();\n  var points = createFloat32Array(len * 2);\n\n  for (var idx = 0; idx < len; idx++) {\n    var pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx);\n    points[idx * 2] = pt[0];\n    points[idx * 2 + 1] = pt[1];\n  }\n\n  return points;\n}\n\nfunction turnPointsIntoStep(points, coordSys, stepTurnAt, connectNulls) {\n  var baseAxis = coordSys.getBaseAxis();\n  var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;\n  var stepPoints = [];\n  var i = 0;\n  var stepPt = [];\n  var pt = [];\n  var nextPt = [];\n  var filteredPoints = [];\n\n  if (connectNulls) {\n    for (i = 0; i < points.length; i += 2) {\n      if (!isNaN(points[i]) && !isNaN(points[i + 1])) {\n        filteredPoints.push(points[i], points[i + 1]);\n      }\n    }\n\n    points = filteredPoints;\n  }\n\n  for (i = 0; i < points.length - 2; i += 2) {\n    nextPt[0] = points[i + 2];\n    nextPt[1] = points[i + 3];\n    pt[0] = points[i];\n    pt[1] = points[i + 1];\n    stepPoints.push(pt[0], pt[1]);\n\n    switch (stepTurnAt) {\n      case 'end':\n        stepPt[baseIndex] = nextPt[baseIndex];\n        stepPt[1 - baseIndex] = pt[1 - baseIndex];\n        stepPoints.push(stepPt[0], stepPt[1]);\n        break;\n\n      case 'middle':\n        var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;\n        var stepPt2 = [];\n        stepPt[baseIndex] = stepPt2[baseIndex] = middle;\n        stepPt[1 - baseIndex] = pt[1 - baseIndex];\n        stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];\n        stepPoints.push(stepPt[0], stepPt[1]);\n        stepPoints.push(stepPt2[0], stepPt2[1]);\n        break;\n\n      default:\n        // default is start\n        stepPt[baseIndex] = pt[baseIndex];\n        stepPt[1 - baseIndex] = nextPt[1 - baseIndex];\n        stepPoints.push(stepPt[0], stepPt[1]);\n    }\n  } // Last points\n\n\n  stepPoints.push(points[i++], points[i++]);\n  return stepPoints;\n}\n/**\n * Clip color stops to edge. Avoid creating too large gradients.\n * Which may lead to blurry when GPU acceleration is enabled. See #15680\n *\n * The stops has been sorted from small to large.\n */\n\n\nfunction clipColorStops(colorStops, maxSize) {\n  var newColorStops = [];\n  var len = colorStops.length; // coord will always < 0 in prevOutOfRangeColorStop.\n\n  var prevOutOfRangeColorStop;\n  var prevInRangeColorStop;\n\n  function lerpStop(stop0, stop1, clippedCoord) {\n    var coord0 = stop0.coord;\n    var p = (clippedCoord - coord0) / (stop1.coord - coord0);\n    var color = lerp$1(p, [stop0.color, stop1.color]);\n    return {\n      coord: clippedCoord,\n      color: color\n    };\n  }\n\n  for (var i = 0; i < len; i++) {\n    var stop_1 = colorStops[i];\n    var coord = stop_1.coord;\n\n    if (coord < 0) {\n      prevOutOfRangeColorStop = stop_1;\n    } else if (coord > maxSize) {\n      if (prevInRangeColorStop) {\n        newColorStops.push(lerpStop(prevInRangeColorStop, stop_1, maxSize));\n      } else if (prevOutOfRangeColorStop) {\n        // If there are two stops and coord range is between these two stops\n        newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0), lerpStop(prevOutOfRangeColorStop, stop_1, maxSize));\n      } // All following stop will be out of range. So just ignore them.\n\n\n      break;\n    } else {\n      if (prevOutOfRangeColorStop) {\n        newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0)); // Reset\n\n        prevOutOfRangeColorStop = null;\n      }\n\n      newColorStops.push(stop_1);\n      prevInRangeColorStop = stop_1;\n    }\n  }\n\n  return newColorStops;\n}\n\nfunction getVisualGradient(data, coordSys, api) {\n  var visualMetaList = data.getVisual('visualMeta');\n\n  if (!visualMetaList || !visualMetaList.length || !data.count()) {\n    // When data.count() is 0, gradient range can not be calculated.\n    return;\n  }\n\n  if (coordSys.type !== 'cartesian2d') {\n    if (\"development\" !== 'production') {\n      console.warn('Visual map on line style is only supported on cartesian2d.');\n    }\n\n    return;\n  }\n\n  var coordDim;\n  var visualMeta;\n\n  for (var i = visualMetaList.length - 1; i >= 0; i--) {\n    var dimInfo = data.getDimensionInfo(visualMetaList[i].dimension);\n    coordDim = dimInfo && dimInfo.coordDim; // Can only be x or y\n\n    if (coordDim === 'x' || coordDim === 'y') {\n      visualMeta = visualMetaList[i];\n      break;\n    }\n  }\n\n  if (!visualMeta) {\n    if (\"development\" !== 'production') {\n      console.warn('Visual map on line style only support x or y dimension.');\n    }\n\n    return;\n  } // If the area to be rendered is bigger than area defined by LinearGradient,\n  // the canvas spec prescribes that the color of the first stop and the last\n  // stop should be used. But if two stops are added at offset 0, in effect\n  // browsers use the color of the second stop to render area outside\n  // LinearGradient. So we can only infinitesimally extend area defined in\n  // LinearGradient to render `outerColors`.\n\n\n  var axis = coordSys.getAxis(coordDim); // dataToCoord mapping may not be linear, but must be monotonic.\n\n  var colorStops = map(visualMeta.stops, function (stop) {\n    // offset will be calculated later.\n    return {\n      coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),\n      color: stop.color\n    };\n  });\n  var stopLen = colorStops.length;\n  var outerColors = visualMeta.outerColors.slice();\n\n  if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {\n    colorStops.reverse();\n    outerColors.reverse();\n  }\n\n  var colorStopsInRange = clipColorStops(colorStops, coordDim === 'x' ? api.getWidth() : api.getHeight());\n  var inRangeStopLen = colorStopsInRange.length;\n\n  if (!inRangeStopLen && stopLen) {\n    // All stops are out of range. All will be the same color.\n    return colorStops[0].coord < 0 ? outerColors[1] ? outerColors[1] : colorStops[stopLen - 1].color : outerColors[0] ? outerColors[0] : colorStops[0].color;\n  }\n\n  var tinyExtent = 10; // Arbitrary value: 10px\n\n  var minCoord = colorStopsInRange[0].coord - tinyExtent;\n  var maxCoord = colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent;\n  var coordSpan = maxCoord - minCoord;\n\n  if (coordSpan < 1e-3) {\n    return 'transparent';\n  }\n\n  each(colorStopsInRange, function (stop) {\n    stop.offset = (stop.coord - minCoord) / coordSpan;\n  });\n  colorStopsInRange.push({\n    // NOTE: inRangeStopLen may still be 0 if stoplen is zero.\n    offset: inRangeStopLen ? colorStopsInRange[inRangeStopLen - 1].offset : 0.5,\n    color: outerColors[1] || 'transparent'\n  });\n  colorStopsInRange.unshift({\n    offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5,\n    color: outerColors[0] || 'transparent'\n  });\n  var gradient = new LinearGradient(0, 0, 0, 0, colorStopsInRange, true);\n  gradient[coordDim] = minCoord;\n  gradient[coordDim + '2'] = maxCoord;\n  return gradient;\n}\n\nfunction getIsIgnoreFunc(seriesModel, data, coordSys) {\n  var showAllSymbol = seriesModel.get('showAllSymbol');\n  var isAuto = showAllSymbol === 'auto';\n\n  if (showAllSymbol && !isAuto) {\n    return;\n  }\n\n  var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n\n  if (!categoryAxis) {\n    return;\n  } // Note that category label interval strategy might bring some weird effect\n  // in some scenario: users may wonder why some of the symbols are not\n  // displayed. So we show all symbols as possible as we can.\n\n\n  if (isAuto // Simplify the logic, do not determine label overlap here.\n  && canShowAllSymbolForCategory(categoryAxis, data)) {\n    return;\n  } // Otherwise follow the label interval strategy on category axis.\n\n\n  var categoryDataDim = data.mapDimension(categoryAxis.dim);\n  var labelMap = {};\n  each(categoryAxis.getViewLabels(), function (labelItem) {\n    var ordinalNumber = categoryAxis.scale.getRawOrdinalNumber(labelItem.tickValue);\n    labelMap[ordinalNumber] = 1;\n  });\n  return function (dataIndex) {\n    return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex));\n  };\n}\n\nfunction canShowAllSymbolForCategory(categoryAxis, data) {\n  // In most cases, line is monotonous on category axis, and the label size\n  // is close with each other. So we check the symbol size and some of the\n  // label size alone with the category axis to estimate whether all symbol\n  // can be shown without overlap.\n  var axisExtent = categoryAxis.getExtent();\n  var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count();\n  isNaN(availSize) && (availSize = 0); // 0/0 is NaN.\n  // Sampling some points, max 5.\n\n  var dataLen = data.count();\n  var step = Math.max(1, Math.round(dataLen / 5));\n\n  for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) {\n    if (Symbol.getSymbolSize(data, dataIndex // Only for cartesian, where `isHorizontal` exists.\n    )[categoryAxis.isHorizontal() ? 1 : 0] // Empirical number\n    * 1.5 > availSize) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nfunction isPointNull$1(x, y) {\n  return isNaN(x) || isNaN(y);\n}\n\nfunction getLastIndexNotNull(points) {\n  var len = points.length / 2;\n\n  for (; len > 0; len--) {\n    if (!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])) {\n      break;\n    }\n  }\n\n  return len - 1;\n}\n\nfunction getPointAtIndex(points, idx) {\n  return [points[idx * 2], points[idx * 2 + 1]];\n}\n\nfunction getIndexRange(points, xOrY, dim) {\n  var len = points.length / 2;\n  var dimIdx = dim === 'x' ? 0 : 1;\n  var a;\n  var b;\n  var prevIndex = 0;\n  var nextIndex = -1;\n\n  for (var i = 0; i < len; i++) {\n    b = points[i * 2 + dimIdx];\n\n    if (isNaN(b) || isNaN(points[i * 2 + 1 - dimIdx])) {\n      continue;\n    }\n\n    if (i === 0) {\n      a = b;\n      continue;\n    }\n\n    if (a <= xOrY && b >= xOrY || a >= xOrY && b <= xOrY) {\n      nextIndex = i;\n      break;\n    }\n\n    prevIndex = i;\n    a = b;\n  }\n\n  return {\n    range: [prevIndex, nextIndex],\n    t: (xOrY - a) / (b - a)\n  };\n}\n\nfunction anyStateShowEndLabel(seriesModel) {\n  if (seriesModel.get(['endLabel', 'show'])) {\n    return true;\n  }\n\n  for (var i = 0; i < SPECIAL_STATES.length; i++) {\n    if (seriesModel.get([SPECIAL_STATES[i], 'endLabel', 'show'])) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nfunction createLineClipPath(lineView, coordSys, hasAnimation, seriesModel) {\n  if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n    var endLabelModel_1 = seriesModel.getModel('endLabel');\n    var valueAnimation_1 = endLabelModel_1.get('valueAnimation');\n    var data_1 = seriesModel.getData();\n    var labelAnimationRecord_1 = {\n      lastFrameIndex: 0\n    };\n    var during = anyStateShowEndLabel(seriesModel) ? function (percent, clipRect) {\n      lineView._endLabelOnDuring(percent, clipRect, data_1, labelAnimationRecord_1, valueAnimation_1, endLabelModel_1, coordSys);\n    } : null;\n    var isHorizontal = coordSys.getBaseAxis().isHorizontal();\n    var clipPath = createGridClipPath(coordSys, hasAnimation, seriesModel, function () {\n      var endLabel = lineView._endLabel;\n\n      if (endLabel && hasAnimation) {\n        if (labelAnimationRecord_1.originalX != null) {\n          endLabel.attr({\n            x: labelAnimationRecord_1.originalX,\n            y: labelAnimationRecord_1.originalY\n          });\n        }\n      }\n    }, during); // Expand clip shape to avoid clipping when line value exceeds axis\n\n    if (!seriesModel.get('clip', true)) {\n      var rectShape = clipPath.shape;\n      var expandSize = Math.max(rectShape.width, rectShape.height);\n\n      if (isHorizontal) {\n        rectShape.y -= expandSize;\n        rectShape.height += expandSize * 2;\n      } else {\n        rectShape.x -= expandSize;\n        rectShape.width += expandSize * 2;\n      }\n    } // Set to the final frame. To make sure label layout is right.\n\n\n    if (during) {\n      during(1, clipPath);\n    }\n\n    return clipPath;\n  } else {\n    if (\"development\" !== 'production') {\n      if (seriesModel.get(['endLabel', 'show'])) {\n        console.warn('endLabel is not supported for lines in polar systems.');\n      }\n    }\n\n    return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n  }\n}\n\nfunction getEndLabelStateSpecified(endLabelModel, coordSys) {\n  var baseAxis = coordSys.getBaseAxis();\n  var isHorizontal = baseAxis.isHorizontal();\n  var isBaseInversed = baseAxis.inverse;\n  var align = isHorizontal ? isBaseInversed ? 'right' : 'left' : 'center';\n  var verticalAlign = isHorizontal ? 'middle' : isBaseInversed ? 'top' : 'bottom';\n  return {\n    normal: {\n      align: endLabelModel.get('align') || align,\n      verticalAlign: endLabelModel.get('verticalAlign') || verticalAlign\n    }\n  };\n}\n\nvar LineView =\n/** @class */\nfunction (_super) {\n  __extends(LineView, _super);\n\n  function LineView() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  LineView.prototype.init = function () {\n    var lineGroup = new Group();\n    var symbolDraw = new SymbolDraw();\n    this.group.add(symbolDraw.group);\n    this._symbolDraw = symbolDraw;\n    this._lineGroup = lineGroup;\n  };\n\n  LineView.prototype.render = function (seriesModel, ecModel, api) {\n    var _this = this;\n\n    var coordSys = seriesModel.coordinateSystem;\n    var group = this.group;\n    var data = seriesModel.getData();\n    var lineStyleModel = seriesModel.getModel('lineStyle');\n    var areaStyleModel = seriesModel.getModel('areaStyle');\n    var points = data.getLayout('points') || [];\n    var isCoordSysPolar = coordSys.type === 'polar';\n    var prevCoordSys = this._coordSys;\n    var symbolDraw = this._symbolDraw;\n    var polyline = this._polyline;\n    var polygon = this._polygon;\n    var lineGroup = this._lineGroup;\n    var hasAnimation = seriesModel.get('animation');\n    var isAreaChart = !areaStyleModel.isEmpty();\n    var valueOrigin = areaStyleModel.get('origin');\n    var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin);\n    var stackedOnPoints = isAreaChart && getStackedOnPoints(coordSys, data, dataCoordInfo);\n    var showSymbol = seriesModel.get('showSymbol');\n    var connectNulls = seriesModel.get('connectNulls');\n    var isIgnoreFunc = showSymbol && !isCoordSysPolar && getIsIgnoreFunc(seriesModel, data, coordSys); // Remove temporary symbols\n\n    var oldData = this._data;\n    oldData && oldData.eachItemGraphicEl(function (el, idx) {\n      if (el.__temp) {\n        group.remove(el);\n        oldData.setItemGraphicEl(idx, null);\n      }\n    }); // Remove previous created symbols if showSymbol changed to false\n\n    if (!showSymbol) {\n      symbolDraw.remove();\n    }\n\n    group.add(lineGroup); // FIXME step not support polar\n\n    var step = !isCoordSysPolar ? seriesModel.get('step') : false;\n    var clipShapeForSymbol;\n\n    if (coordSys && coordSys.getArea && seriesModel.get('clip', true)) {\n      clipShapeForSymbol = coordSys.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n      // See #7913 and `test/dataZoom-clip.html`.\n\n      if (clipShapeForSymbol.width != null) {\n        clipShapeForSymbol.x -= 0.1;\n        clipShapeForSymbol.y -= 0.1;\n        clipShapeForSymbol.width += 0.2;\n        clipShapeForSymbol.height += 0.2;\n      } else if (clipShapeForSymbol.r0) {\n        clipShapeForSymbol.r0 -= 0.5;\n        clipShapeForSymbol.r += 0.5;\n      }\n    }\n\n    this._clipShapeForSymbol = clipShapeForSymbol;\n    var visualColor = getVisualGradient(data, coordSys, api) || data.getVisual('style')[data.getVisual('drawType')]; // Initialization animation or coordinate system changed\n\n    if (!(polyline && prevCoordSys.type === coordSys.type && step === this._step)) {\n      showSymbol && symbolDraw.updateData(data, {\n        isIgnore: isIgnoreFunc,\n        clipShape: clipShapeForSymbol,\n        disableAnimation: true,\n        getSymbolPoint: function (idx) {\n          return [points[idx * 2], points[idx * 2 + 1]];\n        }\n      });\n      hasAnimation && this._initSymbolLabelAnimation(data, coordSys, clipShapeForSymbol);\n\n      if (step) {\n        // TODO If stacked series is not step\n        points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n        if (stackedOnPoints) {\n          stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n        }\n      }\n\n      polyline = this._newPolyline(points);\n\n      if (isAreaChart) {\n        polygon = this._newPolygon(points, stackedOnPoints);\n      } // If areaStyle is removed\n      else if (polygon) {\n          lineGroup.remove(polygon);\n          polygon = this._polygon = null;\n        } // NOTE: Must update _endLabel before setClipPath.\n\n\n      if (!isCoordSysPolar) {\n        this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n      }\n\n      lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n    } else {\n      if (isAreaChart && !polygon) {\n        // If areaStyle is added\n        polygon = this._newPolygon(points, stackedOnPoints);\n      } else if (polygon && !isAreaChart) {\n        // If areaStyle is removed\n        lineGroup.remove(polygon);\n        polygon = this._polygon = null;\n      } // NOTE: Must update _endLabel before setClipPath.\n\n\n      if (!isCoordSysPolar) {\n        this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n      } // Update clipPath\n\n\n      var oldClipPath = lineGroup.getClipPath();\n\n      if (oldClipPath) {\n        var newClipPath = createLineClipPath(this, coordSys, false, seriesModel);\n        initProps(oldClipPath, {\n          shape: newClipPath.shape\n        }, seriesModel);\n      } else {\n        lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n      } // Always update, or it is wrong in the case turning on legend\n      // because points are not changed.\n\n\n      showSymbol && symbolDraw.updateData(data, {\n        isIgnore: isIgnoreFunc,\n        clipShape: clipShapeForSymbol,\n        disableAnimation: true,\n        getSymbolPoint: function (idx) {\n          return [points[idx * 2], points[idx * 2 + 1]];\n        }\n      }); // In the case data zoom triggered refreshing frequently\n      // Data may not change if line has a category axis. So it should animate nothing.\n\n      if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) || !isPointsSame(this._points, points)) {\n        if (hasAnimation) {\n          this._doUpdateAnimation(data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls);\n        } else {\n          // Not do it in update with animation\n          if (step) {\n            // TODO If stacked series is not step\n            points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n            if (stackedOnPoints) {\n              stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n            }\n          }\n\n          polyline.setShape({\n            points: points\n          });\n          polygon && polygon.setShape({\n            points: points,\n            stackedOnPoints: stackedOnPoints\n          });\n        }\n      }\n    }\n\n    var emphasisModel = seriesModel.getModel('emphasis');\n    var focus = emphasisModel.get('focus');\n    var blurScope = emphasisModel.get('blurScope');\n    var emphasisDisabled = emphasisModel.get('disabled');\n    polyline.useStyle(defaults( // Use color in lineStyle first\n    lineStyleModel.getLineStyle(), {\n      fill: 'none',\n      stroke: visualColor,\n      lineJoin: 'bevel'\n    }));\n    setStatesStylesFromModel(polyline, seriesModel, 'lineStyle');\n\n    if (polyline.style.lineWidth > 0 && seriesModel.get(['emphasis', 'lineStyle', 'width']) === 'bolder') {\n      var emphasisLineStyle = polyline.getState('emphasis').style;\n      emphasisLineStyle.lineWidth = +polyline.style.lineWidth + 1;\n    } // Needs seriesIndex for focus\n\n\n    getECData(polyline).seriesIndex = seriesModel.seriesIndex;\n    toggleHoverEmphasis(polyline, focus, blurScope, emphasisDisabled);\n    var smooth = getSmooth(seriesModel.get('smooth'));\n    var smoothMonotone = seriesModel.get('smoothMonotone');\n    polyline.setShape({\n      smooth: smooth,\n      smoothMonotone: smoothMonotone,\n      connectNulls: connectNulls\n    });\n\n    if (polygon) {\n      var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');\n      var stackedOnSmooth = 0;\n      polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), {\n        fill: visualColor,\n        opacity: 0.7,\n        lineJoin: 'bevel',\n        decal: data.getVisual('style').decal\n      }));\n\n      if (stackedOnSeries) {\n        stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));\n      }\n\n      polygon.setShape({\n        smooth: smooth,\n        stackedOnSmooth: stackedOnSmooth,\n        smoothMonotone: smoothMonotone,\n        connectNulls: connectNulls\n      });\n      setStatesStylesFromModel(polygon, seriesModel, 'areaStyle'); // Needs seriesIndex for focus\n\n      getECData(polygon).seriesIndex = seriesModel.seriesIndex;\n      toggleHoverEmphasis(polygon, focus, blurScope, emphasisDisabled);\n    }\n\n    var changePolyState = function (toState) {\n      _this._changePolyState(toState);\n    };\n\n    data.eachItemGraphicEl(function (el) {\n      // Switch polyline / polygon state if element changed its state.\n      el && (el.onHoverStateChange = changePolyState);\n    });\n    this._polyline.onHoverStateChange = changePolyState;\n    this._data = data; // Save the coordinate system for transition animation when data changed\n\n    this._coordSys = coordSys;\n    this._stackedOnPoints = stackedOnPoints;\n    this._points = points;\n    this._step = step;\n    this._valueOrigin = valueOrigin;\n\n    if (seriesModel.get('triggerLineEvent')) {\n      this.packEventData(seriesModel, polyline);\n      polygon && this.packEventData(seriesModel, polygon);\n    }\n  };\n\n  LineView.prototype.packEventData = function (seriesModel, el) {\n    getECData(el).eventData = {\n      componentType: 'series',\n      componentSubType: 'line',\n      componentIndex: seriesModel.componentIndex,\n      seriesIndex: seriesModel.seriesIndex,\n      seriesName: seriesModel.name,\n      seriesType: 'line'\n    };\n  };\n\n  LineView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n    var data = seriesModel.getData();\n    var dataIndex = queryDataIndex(data, payload);\n\n    this._changePolyState('emphasis');\n\n    if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {\n      var points = data.getLayout('points');\n      var symbol = data.getItemGraphicEl(dataIndex);\n\n      if (!symbol) {\n        // Create a temporary symbol if it is not exists\n        var x = points[dataIndex * 2];\n        var y = points[dataIndex * 2 + 1];\n\n        if (isNaN(x) || isNaN(y)) {\n          // Null data\n          return;\n        } // fix #11360: shouldn't draw symbol outside clipShapeForSymbol\n\n\n        if (this._clipShapeForSymbol && !this._clipShapeForSymbol.contain(x, y)) {\n          return;\n        }\n\n        var zlevel = seriesModel.get('zlevel') || 0;\n        var z = seriesModel.get('z') || 0;\n        symbol = new Symbol(data, dataIndex);\n        symbol.x = x;\n        symbol.y = y;\n        symbol.setZ(zlevel, z); // ensure label text of the temporary symbol is in front of line and area polygon\n\n        var symbolLabel = symbol.getSymbolPath().getTextContent();\n\n        if (symbolLabel) {\n          symbolLabel.zlevel = zlevel;\n          symbolLabel.z = z;\n          symbolLabel.z2 = this._polyline.z2 + 1;\n        }\n\n        symbol.__temp = true;\n        data.setItemGraphicEl(dataIndex, symbol); // Stop scale animation\n\n        symbol.stopSymbolAnimation(true);\n        this.group.add(symbol);\n      }\n\n      symbol.highlight();\n    } else {\n      // Highlight whole series\n      ChartView.prototype.highlight.call(this, seriesModel, ecModel, api, payload);\n    }\n  };\n\n  LineView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n    var data = seriesModel.getData();\n    var dataIndex = queryDataIndex(data, payload);\n\n    this._changePolyState('normal');\n\n    if (dataIndex != null && dataIndex >= 0) {\n      var symbol = data.getItemGraphicEl(dataIndex);\n\n      if (symbol) {\n        if (symbol.__temp) {\n          data.setItemGraphicEl(dataIndex, null);\n          this.group.remove(symbol);\n        } else {\n          symbol.downplay();\n        }\n      }\n    } else {\n      // FIXME\n      // can not downplay completely.\n      // Downplay whole series\n      ChartView.prototype.downplay.call(this, seriesModel, ecModel, api, payload);\n    }\n  };\n\n  LineView.prototype._changePolyState = function (toState) {\n    var polygon = this._polygon;\n    setStatesFlag(this._polyline, toState);\n    polygon && setStatesFlag(polygon, toState);\n  };\n\n  LineView.prototype._newPolyline = function (points) {\n    var polyline = this._polyline; // Remove previous created polyline\n\n    if (polyline) {\n      this._lineGroup.remove(polyline);\n    }\n\n    polyline = new ECPolyline({\n      shape: {\n        points: points\n      },\n      segmentIgnoreThreshold: 2,\n      z2: 10\n    });\n\n    this._lineGroup.add(polyline);\n\n    this._polyline = polyline;\n    return polyline;\n  };\n\n  LineView.prototype._newPolygon = function (points, stackedOnPoints) {\n    var polygon = this._polygon; // Remove previous created polygon\n\n    if (polygon) {\n      this._lineGroup.remove(polygon);\n    }\n\n    polygon = new ECPolygon({\n      shape: {\n        points: points,\n        stackedOnPoints: stackedOnPoints\n      },\n      segmentIgnoreThreshold: 2\n    });\n\n    this._lineGroup.add(polygon);\n\n    this._polygon = polygon;\n    return polygon;\n  };\n\n  LineView.prototype._initSymbolLabelAnimation = function (data, coordSys, clipShape) {\n    var isHorizontalOrRadial;\n    var isCoordSysPolar;\n    var baseAxis = coordSys.getBaseAxis();\n    var isAxisInverse = baseAxis.inverse;\n\n    if (coordSys.type === 'cartesian2d') {\n      isHorizontalOrRadial = baseAxis.isHorizontal();\n      isCoordSysPolar = false;\n    } else if (coordSys.type === 'polar') {\n      isHorizontalOrRadial = baseAxis.dim === 'angle';\n      isCoordSysPolar = true;\n    }\n\n    var seriesModel = data.hostModel;\n    var seriesDuration = seriesModel.get('animationDuration');\n\n    if (isFunction(seriesDuration)) {\n      seriesDuration = seriesDuration(null);\n    }\n\n    var seriesDalay = seriesModel.get('animationDelay') || 0;\n    var seriesDalayValue = isFunction(seriesDalay) ? seriesDalay(null) : seriesDalay;\n    data.eachItemGraphicEl(function (symbol, idx) {\n      var el = symbol;\n\n      if (el) {\n        var point = [symbol.x, symbol.y];\n        var start = void 0;\n        var end = void 0;\n        var current = void 0;\n\n        if (clipShape) {\n          if (isCoordSysPolar) {\n            var polarClip = clipShape;\n            var coord = coordSys.pointToCoord(point);\n\n            if (isHorizontalOrRadial) {\n              start = polarClip.startAngle;\n              end = polarClip.endAngle;\n              current = -coord[1] / 180 * Math.PI;\n            } else {\n              start = polarClip.r0;\n              end = polarClip.r;\n              current = coord[0];\n            }\n          } else {\n            var gridClip = clipShape;\n\n            if (isHorizontalOrRadial) {\n              start = gridClip.x;\n              end = gridClip.x + gridClip.width;\n              current = symbol.x;\n            } else {\n              start = gridClip.y + gridClip.height;\n              end = gridClip.y;\n              current = symbol.y;\n            }\n          }\n        }\n\n        var ratio = end === start ? 0 : (current - start) / (end - start);\n\n        if (isAxisInverse) {\n          ratio = 1 - ratio;\n        }\n\n        var delay = isFunction(seriesDalay) ? seriesDalay(idx) : seriesDuration * ratio + seriesDalayValue;\n        var symbolPath = el.getSymbolPath();\n        var text = symbolPath.getTextContent();\n        el.attr({\n          scaleX: 0,\n          scaleY: 0\n        });\n        el.animateTo({\n          scaleX: 1,\n          scaleY: 1\n        }, {\n          duration: 200,\n          setToFinal: true,\n          delay: delay\n        });\n\n        if (text) {\n          text.animateFrom({\n            style: {\n              opacity: 0\n            }\n          }, {\n            duration: 300,\n            delay: delay\n          });\n        }\n\n        symbolPath.disableLabelAnimation = true;\n      }\n    });\n  };\n\n  LineView.prototype._initOrUpdateEndLabel = function (seriesModel, coordSys, inheritColor) {\n    var endLabelModel = seriesModel.getModel('endLabel');\n\n    if (anyStateShowEndLabel(seriesModel)) {\n      var data_2 = seriesModel.getData();\n      var polyline = this._polyline; // series may be filtered.\n\n      var points = data_2.getLayout('points');\n\n      if (!points) {\n        polyline.removeTextContent();\n        this._endLabel = null;\n        return;\n      }\n\n      var endLabel = this._endLabel;\n\n      if (!endLabel) {\n        endLabel = this._endLabel = new ZRText({\n          z2: 200 // should be higher than item symbol\n\n        });\n        endLabel.ignoreClip = true;\n        polyline.setTextContent(this._endLabel);\n        polyline.disableLabelAnimation = true;\n      } // Find last non-NaN data to display data\n\n\n      var dataIndex = getLastIndexNotNull(points);\n\n      if (dataIndex >= 0) {\n        setLabelStyle(polyline, getLabelStatesModels(seriesModel, 'endLabel'), {\n          inheritColor: inheritColor,\n          labelFetcher: seriesModel,\n          labelDataIndex: dataIndex,\n          defaultText: function (dataIndex, opt, interpolatedValue) {\n            return interpolatedValue != null ? getDefaultInterpolatedLabel(data_2, interpolatedValue) : getDefaultLabel(data_2, dataIndex);\n          },\n          enableTextSetter: true\n        }, getEndLabelStateSpecified(endLabelModel, coordSys));\n        polyline.textConfig.position = null;\n      }\n    } else if (this._endLabel) {\n      this._polyline.removeTextContent();\n\n      this._endLabel = null;\n    }\n  };\n\n  LineView.prototype._endLabelOnDuring = function (percent, clipRect, data, animationRecord, valueAnimation, endLabelModel, coordSys) {\n    var endLabel = this._endLabel;\n    var polyline = this._polyline;\n\n    if (endLabel) {\n      // NOTE: Don't remove percent < 1. percent === 1 means the first frame during render.\n      // The label is not prepared at this time.\n      if (percent < 1 && animationRecord.originalX == null) {\n        animationRecord.originalX = endLabel.x;\n        animationRecord.originalY = endLabel.y;\n      }\n\n      var points = data.getLayout('points');\n      var seriesModel = data.hostModel;\n      var connectNulls = seriesModel.get('connectNulls');\n      var precision = endLabelModel.get('precision');\n      var distance = endLabelModel.get('distance') || 0;\n      var baseAxis = coordSys.getBaseAxis();\n      var isHorizontal = baseAxis.isHorizontal();\n      var isBaseInversed = baseAxis.inverse;\n      var clipShape = clipRect.shape;\n      var xOrY = isBaseInversed ? isHorizontal ? clipShape.x : clipShape.y + clipShape.height : isHorizontal ? clipShape.x + clipShape.width : clipShape.y;\n      var distanceX = (isHorizontal ? distance : 0) * (isBaseInversed ? -1 : 1);\n      var distanceY = (isHorizontal ? 0 : -distance) * (isBaseInversed ? -1 : 1);\n      var dim = isHorizontal ? 'x' : 'y';\n      var dataIndexRange = getIndexRange(points, xOrY, dim);\n      var indices = dataIndexRange.range;\n      var diff = indices[1] - indices[0];\n      var value = void 0;\n\n      if (diff >= 1) {\n        // diff > 1 && connectNulls, which is on the null data.\n        if (diff > 1 && !connectNulls) {\n          var pt = getPointAtIndex(points, indices[0]);\n          endLabel.attr({\n            x: pt[0] + distanceX,\n            y: pt[1] + distanceY\n          });\n          valueAnimation && (value = seriesModel.getRawValue(indices[0]));\n        } else {\n          var pt = polyline.getPointOn(xOrY, dim);\n          pt && endLabel.attr({\n            x: pt[0] + distanceX,\n            y: pt[1] + distanceY\n          });\n          var startValue = seriesModel.getRawValue(indices[0]);\n          var endValue = seriesModel.getRawValue(indices[1]);\n          valueAnimation && (value = interpolateRawValues(data, precision, startValue, endValue, dataIndexRange.t));\n        }\n\n        animationRecord.lastFrameIndex = indices[0];\n      } else {\n        // If diff <= 0, which is the range is not found(Include NaN)\n        // Choose the first point or last point.\n        var idx = percent === 1 || animationRecord.lastFrameIndex > 0 ? indices[0] : 0;\n        var pt = getPointAtIndex(points, idx);\n        valueAnimation && (value = seriesModel.getRawValue(idx));\n        endLabel.attr({\n          x: pt[0] + distanceX,\n          y: pt[1] + distanceY\n        });\n      }\n\n      if (valueAnimation) {\n        labelInner(endLabel).setLabelText(value);\n      }\n    }\n  };\n  /**\n   * @private\n   */\n  // FIXME Two value axis\n\n\n  LineView.prototype._doUpdateAnimation = function (data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls) {\n    var polyline = this._polyline;\n    var polygon = this._polygon;\n    var seriesModel = data.hostModel;\n    var diff = lineAnimationDiff(this._data, data, this._stackedOnPoints, stackedOnPoints, this._coordSys, coordSys, this._valueOrigin);\n    var current = diff.current;\n    var stackedOnCurrent = diff.stackedOnCurrent;\n    var next = diff.next;\n    var stackedOnNext = diff.stackedOnNext;\n\n    if (step) {\n      // TODO If stacked series is not step\n      current = turnPointsIntoStep(diff.current, coordSys, step, connectNulls);\n      stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step, connectNulls);\n      next = turnPointsIntoStep(diff.next, coordSys, step, connectNulls);\n      stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step, connectNulls);\n    } // Don't apply animation if diff is large.\n    // For better result and avoid memory explosion problems like\n    // https://github.com/apache/incubator-echarts/issues/12229\n\n\n    if (getBoundingDiff(current, next) > 3000 || polygon && getBoundingDiff(stackedOnCurrent, stackedOnNext) > 3000) {\n      polyline.stopAnimation();\n      polyline.setShape({\n        points: next\n      });\n\n      if (polygon) {\n        polygon.stopAnimation();\n        polygon.setShape({\n          points: next,\n          stackedOnPoints: stackedOnNext\n        });\n      }\n\n      return;\n    }\n\n    polyline.shape.__points = diff.current;\n    polyline.shape.points = current;\n    var target = {\n      shape: {\n        points: next\n      }\n    }; // Also animate the original points.\n    // If points reference is changed when turning into step line.\n\n    if (diff.current !== current) {\n      target.shape.__points = diff.next;\n    } // Stop previous animation.\n\n\n    polyline.stopAnimation();\n    updateProps(polyline, target, seriesModel);\n\n    if (polygon) {\n      polygon.setShape({\n        // Reuse the points with polyline.\n        points: current,\n        stackedOnPoints: stackedOnCurrent\n      });\n      polygon.stopAnimation();\n      updateProps(polygon, {\n        shape: {\n          stackedOnPoints: stackedOnNext\n        }\n      }, seriesModel); // If use attr directly in updateProps.\n\n      if (polyline.shape.points !== polygon.shape.points) {\n        polygon.shape.points = polyline.shape.points;\n      }\n    }\n\n    var updatedDataInfo = [];\n    var diffStatus = diff.status;\n\n    for (var i = 0; i < diffStatus.length; i++) {\n      var cmd = diffStatus[i].cmd;\n\n      if (cmd === '=') {\n        var el = data.getItemGraphicEl(diffStatus[i].idx1);\n\n        if (el) {\n          updatedDataInfo.push({\n            el: el,\n            ptIdx: i // Index of points\n\n          });\n        }\n      }\n    }\n\n    if (polyline.animators && polyline.animators.length) {\n      polyline.animators[0].during(function () {\n        polygon && polygon.dirtyShape();\n        var points = polyline.shape.__points;\n\n        for (var i = 0; i < updatedDataInfo.length; i++) {\n          var el = updatedDataInfo[i].el;\n          var offset = updatedDataInfo[i].ptIdx * 2;\n          el.x = points[offset];\n          el.y = points[offset + 1];\n          el.markRedraw();\n        }\n      });\n    }\n  };\n\n  LineView.prototype.remove = function (ecModel) {\n    var group = this.group;\n    var oldData = this._data;\n\n    this._lineGroup.removeAll();\n\n    this._symbolDraw.remove(true); // Remove temporary created elements when highlighting\n\n\n    oldData && oldData.eachItemGraphicEl(function (el, idx) {\n      if (el.__temp) {\n        group.remove(el);\n        oldData.setItemGraphicEl(idx, null);\n      }\n    });\n    this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._endLabel = this._data = null;\n  };\n\n  LineView.type = 'line';\n  return LineView;\n}(ChartView);\n\nfunction pointsLayout(seriesType, forceStoreInTypedArray) {\n  return {\n    seriesType: seriesType,\n    plan: createRenderPlanner(),\n    reset: function (seriesModel) {\n      var data = seriesModel.getData();\n      var coordSys = seriesModel.coordinateSystem;\n      var pipelineContext = seriesModel.pipelineContext;\n      var useTypedArray = forceStoreInTypedArray || pipelineContext.large;\n\n      if (!coordSys) {\n        return;\n      }\n\n      var dims = map(coordSys.dimensions, function (dim) {\n        return data.mapDimension(dim);\n      }).slice(0, 2);\n      var dimLen = dims.length;\n      var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n      if (isDimensionStacked(data, dims[0])) {\n        dims[0] = stackResultDim;\n      }\n\n      if (isDimensionStacked(data, dims[1])) {\n        dims[1] = stackResultDim;\n      }\n\n      var store = data.getStore();\n      var dimIdx0 = data.getDimensionIndex(dims[0]);\n      var dimIdx1 = data.getDimensionIndex(dims[1]);\n      return dimLen && {\n        progress: function (params, data) {\n          var segCount = params.end - params.start;\n          var points = useTypedArray && createFloat32Array(segCount * dimLen);\n          var tmpIn = [];\n          var tmpOut = [];\n\n          for (var i = params.start, offset = 0; i < params.end; i++) {\n            var point = void 0;\n\n            if (dimLen === 1) {\n              var x = store.get(dimIdx0, i); // NOTE: Make sure the second parameter is null to use default strategy.\n\n              point = coordSys.dataToPoint(x, null, tmpOut);\n            } else {\n              tmpIn[0] = store.get(dimIdx0, i);\n              tmpIn[1] = store.get(dimIdx1, i); // Let coordinate system to handle the NaN data.\n\n              point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n            }\n\n            if (useTypedArray) {\n              points[offset++] = point[0];\n              points[offset++] = point[1];\n            } else {\n              data.setItemLayout(i, point.slice());\n            }\n          }\n\n          useTypedArray && data.setLayout('points', points);\n        }\n      };\n    }\n  };\n}\n\nvar samplers = {\n  average: function (frame) {\n    var sum = 0;\n    var count = 0;\n\n    for (var i = 0; i < frame.length; i++) {\n      if (!isNaN(frame[i])) {\n        sum += frame[i];\n        count++;\n      }\n    } // Return NaN if count is 0\n\n\n    return count === 0 ? NaN : sum / count;\n  },\n  sum: function (frame) {\n    var sum = 0;\n\n    for (var i = 0; i < frame.length; i++) {\n      // Ignore NaN\n      sum += frame[i] || 0;\n    }\n\n    return sum;\n  },\n  max: function (frame) {\n    var max = -Infinity;\n\n    for (var i = 0; i < frame.length; i++) {\n      frame[i] > max && (max = frame[i]);\n    } // NaN will cause illegal axis extent.\n\n\n    return isFinite(max) ? max : NaN;\n  },\n  min: function (frame) {\n    var min = Infinity;\n\n    for (var i = 0; i < frame.length; i++) {\n      frame[i] < min && (min = frame[i]);\n    } // NaN will cause illegal axis extent.\n\n\n    return isFinite(min) ? min : NaN;\n  },\n  // TODO\n  // Median\n  nearest: function (frame) {\n    return frame[0];\n  }\n};\n\nvar indexSampler = function (frame) {\n  return Math.round(frame.length / 2);\n};\n\nfunction dataSample(seriesType) {\n  return {\n    seriesType: seriesType,\n    // FIXME:TS never used, so comment it\n    // modifyOutputEnd: true,\n    reset: function (seriesModel, ecModel, api) {\n      var data = seriesModel.getData();\n      var sampling = seriesModel.get('sampling');\n      var coordSys = seriesModel.coordinateSystem;\n      var count = data.count(); // Only cartesian2d support down sampling. Disable it when there is few data.\n\n      if (count > 10 && coordSys.type === 'cartesian2d' && sampling) {\n        var baseAxis = coordSys.getBaseAxis();\n        var valueAxis = coordSys.getOtherAxis(baseAxis);\n        var extent = baseAxis.getExtent();\n        var dpr = api.getDevicePixelRatio(); // Coordinste system has been resized\n\n        var size = Math.abs(extent[1] - extent[0]) * (dpr || 1);\n        var rate = Math.round(count / size);\n\n        if (isFinite(rate) && rate > 1) {\n          if (sampling === 'lttb') {\n            seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate));\n          }\n\n          var sampler = void 0;\n\n          if (isString(sampling)) {\n            sampler = samplers[sampling];\n          } else if (isFunction(sampling)) {\n            sampler = sampling;\n          }\n\n          if (sampler) {\n            // Only support sample the first dim mapped from value axis.\n            seriesModel.setData(data.downSample(data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler));\n          }\n        }\n      }\n    }\n  };\n}\n\nfunction install$2(registers) {\n  registers.registerChartView(LineView);\n  registers.registerSeriesModel(LineSeriesModel);\n  registers.registerLayout(pointsLayout('line', true));\n  registers.registerVisual({\n    seriesType: 'line',\n    reset: function (seriesModel) {\n      var data = seriesModel.getData(); // Visual coding for legend\n\n      var lineStyle = seriesModel.getModel('lineStyle').getLineStyle();\n\n      if (lineStyle && !lineStyle.stroke) {\n        // Fill in visual should be palette color if\n        // has color callback\n        lineStyle.stroke = data.getVisual('style').fill;\n      }\n\n      data.setVisual('legendLineStyle', lineStyle);\n    }\n  }); // Down sample after filter\n\n  registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('line'));\n}\n\nvar BaseBarSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(BaseBarSeriesModel, _super);\n\n  function BaseBarSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = BaseBarSeriesModel.type;\n    return _this;\n  }\n\n  BaseBarSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesData(null, this, {\n      useEncodeDefaulter: true\n    });\n  };\n\n  BaseBarSeriesModel.prototype.getMarkerPosition = function (value, dims, startingAtTick) {\n    var coordSys = this.coordinateSystem;\n\n    if (coordSys && coordSys.clampData) {\n      // PENDING if clamp ?\n      var pt_1 = coordSys.dataToPoint(coordSys.clampData(value));\n\n      if (startingAtTick) {\n        each(coordSys.getAxes(), function (axis, idx) {\n          // If axis type is category, use tick coords instead\n          if (axis.type === 'category') {\n            var tickCoords = axis.getTicksCoords();\n            var tickIdx = coordSys.clampData(value)[idx]; // The index of rightmost tick of markArea is 1 larger than x1/y1 index\n\n            if (dims && (dims[idx] === 'x1' || dims[idx] === 'y1')) {\n              tickIdx += 1;\n            }\n\n            tickIdx > tickCoords.length - 1 && (tickIdx = tickCoords.length - 1);\n            tickIdx < 0 && (tickIdx = 0);\n            tickCoords[tickIdx] && (pt_1[idx] = axis.toGlobalCoord(tickCoords[tickIdx].coord));\n          }\n        });\n      } else {\n        var data = this.getData();\n        var offset = data.getLayout('offset');\n        var size = data.getLayout('size');\n        var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;\n        pt_1[offsetIndex] += offset + size / 2;\n      }\n\n      return pt_1;\n    }\n\n    return [NaN, NaN];\n  };\n\n  BaseBarSeriesModel.type = 'series.__base_bar__';\n  BaseBarSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    coordinateSystem: 'cartesian2d',\n    legendHoverLink: true,\n    // stack: null\n    // Cartesian coordinate system\n    // xAxisIndex: 0,\n    // yAxisIndex: 0,\n    barMinHeight: 0,\n    barMinAngle: 0,\n    // cursor: null,\n    large: false,\n    largeThreshold: 400,\n    progressive: 3e3,\n    progressiveChunkMode: 'mod'\n  };\n  return BaseBarSeriesModel;\n}(SeriesModel);\n\nSeriesModel.registerClass(BaseBarSeriesModel);\n\nvar BarSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(BarSeriesModel, _super);\n\n  function BarSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = BarSeriesModel.type;\n    return _this;\n  }\n\n  BarSeriesModel.prototype.getInitialData = function () {\n    return createSeriesData(null, this, {\n      useEncodeDefaulter: true,\n      createInvertedIndices: !!this.get('realtimeSort', true) || null\n    });\n  };\n  /**\n   * @override\n   */\n\n\n  BarSeriesModel.prototype.getProgressive = function () {\n    // Do not support progressive in normal mode.\n    return this.get('large') ? this.get('progressive') : false;\n  };\n  /**\n   * @override\n   */\n\n\n  BarSeriesModel.prototype.getProgressiveThreshold = function () {\n    // Do not support progressive in normal mode.\n    var progressiveThreshold = this.get('progressiveThreshold');\n    var largeThreshold = this.get('largeThreshold');\n\n    if (largeThreshold > progressiveThreshold) {\n      progressiveThreshold = largeThreshold;\n    }\n\n    return progressiveThreshold;\n  };\n\n  BarSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n    return selectors.rect(data.getItemLayout(dataIndex));\n  };\n\n  BarSeriesModel.type = 'series.bar';\n  BarSeriesModel.dependencies = ['grid', 'polar'];\n  BarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, {\n    // If clipped\n    // Only available on cartesian2d\n    clip: true,\n    roundCap: false,\n    showBackground: false,\n    backgroundStyle: {\n      color: 'rgba(180, 180, 180, 0.2)',\n      borderColor: null,\n      borderWidth: 0,\n      borderType: 'solid',\n      borderRadius: 0,\n      shadowBlur: 0,\n      shadowColor: null,\n      shadowOffsetX: 0,\n      shadowOffsetY: 0,\n      opacity: 1\n    },\n    select: {\n      itemStyle: {\n        borderColor: '#212121'\n      }\n    },\n    realtimeSort: false\n  });\n  return BarSeriesModel;\n}(BaseBarSeriesModel);\n\n/**\n * Sausage: similar to sector, but have half circle on both sides\n */\n\nvar SausageShape =\n/** @class */\nfunction () {\n  function SausageShape() {\n    this.cx = 0;\n    this.cy = 0;\n    this.r0 = 0;\n    this.r = 0;\n    this.startAngle = 0;\n    this.endAngle = Math.PI * 2;\n    this.clockwise = true;\n  }\n\n  return SausageShape;\n}();\n\nvar SausagePath =\n/** @class */\nfunction (_super) {\n  __extends(SausagePath, _super);\n\n  function SausagePath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'sausage';\n    return _this;\n  }\n\n  SausagePath.prototype.getDefaultShape = function () {\n    return new SausageShape();\n  };\n\n  SausagePath.prototype.buildPath = function (ctx, shape) {\n    var cx = shape.cx;\n    var cy = shape.cy;\n    var r0 = Math.max(shape.r0 || 0, 0);\n    var r = Math.max(shape.r, 0);\n    var dr = (r - r0) * 0.5;\n    var rCenter = r0 + dr;\n    var startAngle = shape.startAngle;\n    var endAngle = shape.endAngle;\n    var clockwise = shape.clockwise;\n    var PI2 = Math.PI * 2;\n    var lessThanCircle = clockwise ? endAngle - startAngle < PI2 : startAngle - endAngle < PI2;\n\n    if (!lessThanCircle) {\n      // Normalize angles\n      startAngle = endAngle - (clockwise ? PI2 : -PI2);\n    }\n\n    var unitStartX = Math.cos(startAngle);\n    var unitStartY = Math.sin(startAngle);\n    var unitEndX = Math.cos(endAngle);\n    var unitEndY = Math.sin(endAngle);\n\n    if (lessThanCircle) {\n      ctx.moveTo(unitStartX * r0 + cx, unitStartY * r0 + cy);\n      ctx.arc(unitStartX * rCenter + cx, unitStartY * rCenter + cy, dr, -Math.PI + startAngle, startAngle, !clockwise);\n    } else {\n      ctx.moveTo(unitStartX * r + cx, unitStartY * r + cy);\n    }\n\n    ctx.arc(cx, cy, r, startAngle, endAngle, !clockwise);\n    ctx.arc(unitEndX * rCenter + cx, unitEndY * rCenter + cy, dr, endAngle - Math.PI * 2, endAngle - Math.PI, !clockwise);\n\n    if (r0 !== 0) {\n      ctx.arc(cx, cy, r0, endAngle, startAngle, clockwise);\n    } // ctx.closePath();\n\n  };\n\n  return SausagePath;\n}(Path);\n\nfunction createSectorCalculateTextPosition(positionMapping, opts) {\n  opts = opts || {};\n  var isRoundCap = opts.isRoundCap;\n  return function (out, opts, boundingRect) {\n    var textPosition = opts.position;\n\n    if (!textPosition || textPosition instanceof Array) {\n      return calculateTextPosition(out, opts, boundingRect);\n    }\n\n    var mappedSectorPosition = positionMapping(textPosition);\n    var distance = opts.distance != null ? opts.distance : 5;\n    var sector = this.shape;\n    var cx = sector.cx;\n    var cy = sector.cy;\n    var r = sector.r;\n    var r0 = sector.r0;\n    var middleR = (r + r0) / 2;\n    var startAngle = sector.startAngle;\n    var endAngle = sector.endAngle;\n    var middleAngle = (startAngle + endAngle) / 2;\n    var extraDist = isRoundCap ? Math.abs(r - r0) / 2 : 0;\n    var mathCos = Math.cos;\n    var mathSin = Math.sin; // base position: top-left\n\n    var x = cx + r * mathCos(startAngle);\n    var y = cy + r * mathSin(startAngle);\n    var textAlign = 'left';\n    var textVerticalAlign = 'top';\n\n    switch (mappedSectorPosition) {\n      case 'startArc':\n        x = cx + (r0 - distance) * mathCos(middleAngle);\n        y = cy + (r0 - distance) * mathSin(middleAngle);\n        textAlign = 'center';\n        textVerticalAlign = 'top';\n        break;\n\n      case 'insideStartArc':\n        x = cx + (r0 + distance) * mathCos(middleAngle);\n        y = cy + (r0 + distance) * mathSin(middleAngle);\n        textAlign = 'center';\n        textVerticalAlign = 'bottom';\n        break;\n\n      case 'startAngle':\n        x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, distance + extraDist, false);\n        y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, distance + extraDist, false);\n        textAlign = 'right';\n        textVerticalAlign = 'middle';\n        break;\n\n      case 'insideStartAngle':\n        x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, -distance + extraDist, false);\n        y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, -distance + extraDist, false);\n        textAlign = 'left';\n        textVerticalAlign = 'middle';\n        break;\n\n      case 'middle':\n        x = cx + middleR * mathCos(middleAngle);\n        y = cy + middleR * mathSin(middleAngle);\n        textAlign = 'center';\n        textVerticalAlign = 'middle';\n        break;\n\n      case 'endArc':\n        x = cx + (r + distance) * mathCos(middleAngle);\n        y = cy + (r + distance) * mathSin(middleAngle);\n        textAlign = 'center';\n        textVerticalAlign = 'bottom';\n        break;\n\n      case 'insideEndArc':\n        x = cx + (r - distance) * mathCos(middleAngle);\n        y = cy + (r - distance) * mathSin(middleAngle);\n        textAlign = 'center';\n        textVerticalAlign = 'top';\n        break;\n\n      case 'endAngle':\n        x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, distance + extraDist, true);\n        y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, distance + extraDist, true);\n        textAlign = 'left';\n        textVerticalAlign = 'middle';\n        break;\n\n      case 'insideEndAngle':\n        x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, -distance + extraDist, true);\n        y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, -distance + extraDist, true);\n        textAlign = 'right';\n        textVerticalAlign = 'middle';\n        break;\n\n      default:\n        return calculateTextPosition(out, opts, boundingRect);\n    }\n\n    out = out || {};\n    out.x = x;\n    out.y = y;\n    out.align = textAlign;\n    out.verticalAlign = textVerticalAlign;\n    return out;\n  };\n}\nfunction setSectorTextRotation(sector, textPosition, positionMapping, rotateType) {\n  if (isNumber(rotateType)) {\n    // user-set rotation\n    sector.setTextConfig({\n      rotation: rotateType\n    });\n    return;\n  } else if (isArray(textPosition)) {\n    // user-set position, use 0 as auto rotation\n    sector.setTextConfig({\n      rotation: 0\n    });\n    return;\n  }\n\n  var shape = sector.shape;\n  var startAngle = shape.clockwise ? shape.startAngle : shape.endAngle;\n  var endAngle = shape.clockwise ? shape.endAngle : shape.startAngle;\n  var middleAngle = (startAngle + endAngle) / 2;\n  var anchorAngle;\n  var mappedSectorPosition = positionMapping(textPosition);\n\n  switch (mappedSectorPosition) {\n    case 'startArc':\n    case 'insideStartArc':\n    case 'middle':\n    case 'insideEndArc':\n    case 'endArc':\n      anchorAngle = middleAngle;\n      break;\n\n    case 'startAngle':\n    case 'insideStartAngle':\n      anchorAngle = startAngle;\n      break;\n\n    case 'endAngle':\n    case 'insideEndAngle':\n      anchorAngle = endAngle;\n      break;\n\n    default:\n      sector.setTextConfig({\n        rotation: 0\n      });\n      return;\n  }\n\n  var rotate = Math.PI * 1.5 - anchorAngle;\n  /**\n   * TODO: labels with rotate > Math.PI / 2 should be rotate another\n   * half round flipped to increase readability. However, only middle\n   * position supports this for now, because in other positions, the\n   * anchor point is not at the center of the text, so the positions\n   * after rotating is not as expected.\n   */\n\n  if (mappedSectorPosition === 'middle' && rotate > Math.PI / 2 && rotate < Math.PI * 1.5) {\n    rotate -= Math.PI;\n  }\n\n  sector.setTextConfig({\n    rotation: rotate\n  });\n}\n\nfunction adjustAngleDistanceX(angle, distance, isEnd) {\n  return distance * Math.sin(angle) * (isEnd ? -1 : 1);\n}\n\nfunction adjustAngleDistanceY(angle, distance, isEnd) {\n  return distance * Math.cos(angle) * (isEnd ? 1 : -1);\n}\n\nvar mathMax$6 = Math.max;\nvar mathMin$6 = Math.min;\n\nfunction getClipArea(coord, data) {\n  var coordSysClipArea = coord.getArea && coord.getArea();\n\n  if (isCoordinateSystemType(coord, 'cartesian2d')) {\n    var baseAxis = coord.getBaseAxis(); // When boundaryGap is false or using time axis. bar may exceed the grid.\n    // We should not clip this part.\n    // See test/bar2.html\n\n    if (baseAxis.type !== 'category' || !baseAxis.onBand) {\n      var expandWidth = data.getLayout('bandWidth');\n\n      if (baseAxis.isHorizontal()) {\n        coordSysClipArea.x -= expandWidth;\n        coordSysClipArea.width += expandWidth * 2;\n      } else {\n        coordSysClipArea.y -= expandWidth;\n        coordSysClipArea.height += expandWidth * 2;\n      }\n    }\n  }\n\n  return coordSysClipArea;\n}\n\nvar BarView =\n/** @class */\nfunction (_super) {\n  __extends(BarView, _super);\n\n  function BarView() {\n    var _this = _super.call(this) || this;\n\n    _this.type = BarView.type;\n    _this._isFirstFrame = true;\n    return _this;\n  }\n\n  BarView.prototype.render = function (seriesModel, ecModel, api, payload) {\n    this._model = seriesModel;\n\n    this._removeOnRenderedListener(api);\n\n    this._updateDrawMode(seriesModel);\n\n    var coordinateSystemType = seriesModel.get('coordinateSystem');\n\n    if (coordinateSystemType === 'cartesian2d' || coordinateSystemType === 'polar') {\n      // Clear previously rendered progressive elements.\n      this._progressiveEls = null;\n      this._isLargeDraw ? this._renderLarge(seriesModel, ecModel, api) : this._renderNormal(seriesModel, ecModel, api, payload);\n    } else if (\"development\" !== 'production') {\n      warn('Only cartesian2d and polar supported for bar.');\n    }\n  };\n\n  BarView.prototype.incrementalPrepareRender = function (seriesModel) {\n    this._clear();\n\n    this._updateDrawMode(seriesModel); // incremental also need to clip, otherwise might be overlow.\n    // But must not set clip in each frame, otherwise all of the children will be marked redraw.\n\n\n    this._updateLargeClip(seriesModel);\n  };\n\n  BarView.prototype.incrementalRender = function (params, seriesModel) {\n    // Reset\n    this._progressiveEls = []; // Do not support progressive in normal mode.\n\n    this._incrementalRenderLarge(params, seriesModel);\n  };\n\n  BarView.prototype.eachRendered = function (cb) {\n    traverseElements(this._progressiveEls || this.group, cb);\n  };\n\n  BarView.prototype._updateDrawMode = function (seriesModel) {\n    var isLargeDraw = seriesModel.pipelineContext.large;\n\n    if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) {\n      this._isLargeDraw = isLargeDraw;\n\n      this._clear();\n    }\n  };\n\n  BarView.prototype._renderNormal = function (seriesModel, ecModel, api, payload) {\n    var group = this.group;\n    var data = seriesModel.getData();\n    var oldData = this._data;\n    var coord = seriesModel.coordinateSystem;\n    var baseAxis = coord.getBaseAxis();\n    var isHorizontalOrRadial;\n\n    if (coord.type === 'cartesian2d') {\n      isHorizontalOrRadial = baseAxis.isHorizontal();\n    } else if (coord.type === 'polar') {\n      isHorizontalOrRadial = baseAxis.dim === 'angle';\n    }\n\n    var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;\n    var realtimeSortCfg = shouldRealtimeSort(seriesModel, coord);\n\n    if (realtimeSortCfg) {\n      this._enableRealtimeSort(realtimeSortCfg, data, api);\n    }\n\n    var needsClip = seriesModel.get('clip', true) || realtimeSortCfg;\n    var coordSysClipArea = getClipArea(coord, data); // If there is clipPath created in large mode. Remove it.\n\n    group.removeClipPath(); // We don't use clipPath in normal mode because we needs a perfect animation\n    // And don't want the label are clipped.\n\n    var roundCap = seriesModel.get('roundCap', true);\n    var drawBackground = seriesModel.get('showBackground', true);\n    var backgroundModel = seriesModel.getModel('backgroundStyle');\n    var barBorderRadius = backgroundModel.get('borderRadius') || 0;\n    var bgEls = [];\n    var oldBgEls = this._backgroundEls;\n    var isInitSort = payload && payload.isInitSort;\n    var isChangeOrder = payload && payload.type === 'changeAxisOrder';\n\n    function createBackground(dataIndex) {\n      var bgLayout = getLayout[coord.type](data, dataIndex);\n      var bgEl = createBackgroundEl(coord, isHorizontalOrRadial, bgLayout);\n      bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n      if (coord.type === 'cartesian2d') {\n        bgEl.setShape('r', barBorderRadius);\n      }\n\n      bgEls[dataIndex] = bgEl;\n      return bgEl;\n    }\n    data.diff(oldData).add(function (dataIndex) {\n      var itemModel = data.getItemModel(dataIndex);\n      var layout = getLayout[coord.type](data, dataIndex, itemModel);\n\n      if (drawBackground) {\n        createBackground(dataIndex);\n      } // If dataZoom in filteMode: 'empty', the baseValue can be set as NaN in \"axisProxy\".\n\n\n      if (!data.hasValue(dataIndex) || !isValidLayout[coord.type](layout)) {\n        return;\n      }\n\n      var isClipped = false;\n\n      if (needsClip) {\n        // Clip will modify the layout params.\n        // And return a boolean to determine if the shape are fully clipped.\n        isClipped = clip[coord.type](coordSysClipArea, layout);\n      }\n\n      var el = elementCreator[coord.type](seriesModel, data, dataIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, false, roundCap);\n\n      if (realtimeSortCfg) {\n        /**\n         * Force label animation because even if the element is\n         * ignored because it's clipped, it may not be clipped after\n         * changing order. Then, if not using forceLabelAnimation,\n         * the label animation was never started, in which case,\n         * the label will be the final value and doesn't have label\n         * animation.\n         */\n        el.forceLabelAnimation = true;\n      }\n\n      updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n\n      if (isInitSort) {\n        el.attr({\n          shape: layout\n        });\n      } else if (realtimeSortCfg) {\n        updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, dataIndex, isHorizontalOrRadial, false, false);\n      } else {\n        initProps(el, {\n          shape: layout\n        }, seriesModel, dataIndex);\n      }\n\n      data.setItemGraphicEl(dataIndex, el);\n      group.add(el);\n      el.ignore = isClipped;\n    }).update(function (newIndex, oldIndex) {\n      var itemModel = data.getItemModel(newIndex);\n      var layout = getLayout[coord.type](data, newIndex, itemModel);\n\n      if (drawBackground) {\n        var bgEl = void 0;\n\n        if (oldBgEls.length === 0) {\n          bgEl = createBackground(oldIndex);\n        } else {\n          bgEl = oldBgEls[oldIndex];\n          bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n          if (coord.type === 'cartesian2d') {\n            bgEl.setShape('r', barBorderRadius);\n          }\n\n          bgEls[newIndex] = bgEl;\n        }\n\n        var bgLayout = getLayout[coord.type](data, newIndex);\n        var shape = createBackgroundShape(isHorizontalOrRadial, bgLayout, coord);\n        updateProps(bgEl, {\n          shape: shape\n        }, animationModel, newIndex);\n      }\n\n      var el = oldData.getItemGraphicEl(oldIndex);\n\n      if (!data.hasValue(newIndex) || !isValidLayout[coord.type](layout)) {\n        group.remove(el);\n        return;\n      }\n\n      var isClipped = false;\n\n      if (needsClip) {\n        isClipped = clip[coord.type](coordSysClipArea, layout);\n\n        if (isClipped) {\n          group.remove(el);\n        }\n      }\n\n      if (!el) {\n        el = elementCreator[coord.type](seriesModel, data, newIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, !!el, roundCap);\n      } else {\n        saveOldStyle(el);\n      }\n\n      if (realtimeSortCfg) {\n        el.forceLabelAnimation = true;\n      }\n\n      if (isChangeOrder) {\n        var textEl = el.getTextContent();\n\n        if (textEl) {\n          var labelInnerStore = labelInner(textEl);\n\n          if (labelInnerStore.prevValue != null) {\n            /**\n             * Set preValue to be value so that no new label\n             * should be started, otherwise, it will take a full\n             * `animationDurationUpdate` time to finish the\n             * animation, which is not expected.\n             */\n            labelInnerStore.prevValue = labelInnerStore.value;\n          }\n        }\n      } // Not change anything if only order changed.\n      // Especially not change label.\n      else {\n          updateStyle(el, data, newIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n        }\n\n      if (isInitSort) {\n        el.attr({\n          shape: layout\n        });\n      } else if (realtimeSortCfg) {\n        updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, newIndex, isHorizontalOrRadial, true, isChangeOrder);\n      } else {\n        updateProps(el, {\n          shape: layout\n        }, seriesModel, newIndex, null);\n      }\n\n      data.setItemGraphicEl(newIndex, el);\n      el.ignore = isClipped;\n      group.add(el);\n    }).remove(function (dataIndex) {\n      var el = oldData.getItemGraphicEl(dataIndex);\n      el && removeElementWithFadeOut(el, seriesModel, dataIndex);\n    }).execute();\n    var bgGroup = this._backgroundGroup || (this._backgroundGroup = new Group());\n    bgGroup.removeAll();\n\n    for (var i = 0; i < bgEls.length; ++i) {\n      bgGroup.add(bgEls[i]);\n    }\n\n    group.add(bgGroup);\n    this._backgroundEls = bgEls;\n    this._data = data;\n  };\n\n  BarView.prototype._renderLarge = function (seriesModel, ecModel, api) {\n    this._clear();\n\n    createLarge(seriesModel, this.group);\n\n    this._updateLargeClip(seriesModel);\n  };\n\n  BarView.prototype._incrementalRenderLarge = function (params, seriesModel) {\n    this._removeBackground();\n\n    createLarge(seriesModel, this.group, this._progressiveEls, true);\n  };\n\n  BarView.prototype._updateLargeClip = function (seriesModel) {\n    // Use clipPath in large mode.\n    var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel);\n    var group = this.group;\n\n    if (clipPath) {\n      group.setClipPath(clipPath);\n    } else {\n      group.removeClipPath();\n    }\n  };\n\n  BarView.prototype._enableRealtimeSort = function (realtimeSortCfg, data, api) {\n    var _this = this; // If no data in the first frame, wait for data to initSort\n\n\n    if (!data.count()) {\n      return;\n    }\n\n    var baseAxis = realtimeSortCfg.baseAxis;\n\n    if (this._isFirstFrame) {\n      this._dispatchInitSort(data, realtimeSortCfg, api);\n\n      this._isFirstFrame = false;\n    } else {\n      var orderMapping_1 = function (idx) {\n        var el = data.getItemGraphicEl(idx);\n        var shape = el && el.shape;\n        return shape && // The result should be consistent with the initial sort by data value.\n        // Do not support the case that both positive and negative exist.\n        Math.abs(baseAxis.isHorizontal() ? shape.height : shape.width) // If data is NaN, shape.xxx may be NaN, so use || 0 here in case\n        || 0;\n      };\n\n      this._onRendered = function () {\n        _this._updateSortWithinSameData(data, orderMapping_1, baseAxis, api);\n      };\n\n      api.getZr().on('rendered', this._onRendered);\n    }\n  };\n\n  BarView.prototype._dataSort = function (data, baseAxis, orderMapping) {\n    var info = [];\n    data.each(data.mapDimension(baseAxis.dim), function (ordinalNumber, dataIdx) {\n      var mappedValue = orderMapping(dataIdx);\n      mappedValue = mappedValue == null ? NaN : mappedValue;\n      info.push({\n        dataIndex: dataIdx,\n        mappedValue: mappedValue,\n        ordinalNumber: ordinalNumber\n      });\n    });\n    info.sort(function (a, b) {\n      // If NaN, it will be treated as min val.\n      return b.mappedValue - a.mappedValue;\n    });\n    return {\n      ordinalNumbers: map(info, function (item) {\n        return item.ordinalNumber;\n      })\n    };\n  };\n\n  BarView.prototype._isOrderChangedWithinSameData = function (data, orderMapping, baseAxis) {\n    var scale = baseAxis.scale;\n    var ordinalDataDim = data.mapDimension(baseAxis.dim);\n    var lastValue = Number.MAX_VALUE;\n\n    for (var tickNum = 0, len = scale.getOrdinalMeta().categories.length; tickNum < len; ++tickNum) {\n      var rawIdx = data.rawIndexOf(ordinalDataDim, scale.getRawOrdinalNumber(tickNum));\n      var value = rawIdx < 0 // If some tick have no bar, the tick will be treated as min.\n      ? Number.MIN_VALUE // PENDING: if dataZoom on baseAxis exits, is it a performance issue?\n      : orderMapping(data.indexOfRawIndex(rawIdx));\n\n      if (value > lastValue) {\n        return true;\n      }\n\n      lastValue = value;\n    }\n\n    return false;\n  };\n  /*\n   * Consider the case when A and B changed order, whose representing\n   * bars are both out of sight, we don't wish to trigger reorder action\n   * as long as the order in the view doesn't change.\n   */\n\n\n  BarView.prototype._isOrderDifferentInView = function (orderInfo, baseAxis) {\n    var scale = baseAxis.scale;\n    var extent = scale.getExtent();\n    var tickNum = Math.max(0, extent[0]);\n    var tickMax = Math.min(extent[1], scale.getOrdinalMeta().categories.length - 1);\n\n    for (; tickNum <= tickMax; ++tickNum) {\n      if (orderInfo.ordinalNumbers[tickNum] !== scale.getRawOrdinalNumber(tickNum)) {\n        return true;\n      }\n    }\n  };\n\n  BarView.prototype._updateSortWithinSameData = function (data, orderMapping, baseAxis, api) {\n    if (!this._isOrderChangedWithinSameData(data, orderMapping, baseAxis)) {\n      return;\n    }\n\n    var sortInfo = this._dataSort(data, baseAxis, orderMapping);\n\n    if (this._isOrderDifferentInView(sortInfo, baseAxis)) {\n      this._removeOnRenderedListener(api);\n\n      api.dispatchAction({\n        type: 'changeAxisOrder',\n        componentType: baseAxis.dim + 'Axis',\n        axisId: baseAxis.index,\n        sortInfo: sortInfo\n      });\n    }\n  };\n\n  BarView.prototype._dispatchInitSort = function (data, realtimeSortCfg, api) {\n    var baseAxis = realtimeSortCfg.baseAxis;\n\n    var sortResult = this._dataSort(data, baseAxis, function (dataIdx) {\n      return data.get(data.mapDimension(realtimeSortCfg.otherAxis.dim), dataIdx);\n    });\n\n    api.dispatchAction({\n      type: 'changeAxisOrder',\n      componentType: baseAxis.dim + 'Axis',\n      isInitSort: true,\n      axisId: baseAxis.index,\n      sortInfo: sortResult\n    });\n  };\n\n  BarView.prototype.remove = function (ecModel, api) {\n    this._clear(this._model);\n\n    this._removeOnRenderedListener(api);\n  };\n\n  BarView.prototype.dispose = function (ecModel, api) {\n    this._removeOnRenderedListener(api);\n  };\n\n  BarView.prototype._removeOnRenderedListener = function (api) {\n    if (this._onRendered) {\n      api.getZr().off('rendered', this._onRendered);\n      this._onRendered = null;\n    }\n  };\n\n  BarView.prototype._clear = function (model) {\n    var group = this.group;\n    var data = this._data;\n\n    if (model && model.isAnimationEnabled() && data && !this._isLargeDraw) {\n      this._removeBackground();\n\n      this._backgroundEls = [];\n      data.eachItemGraphicEl(function (el) {\n        removeElementWithFadeOut(el, model, getECData(el).dataIndex);\n      });\n    } else {\n      group.removeAll();\n    }\n\n    this._data = null;\n    this._isFirstFrame = true;\n  };\n\n  BarView.prototype._removeBackground = function () {\n    this.group.remove(this._backgroundGroup);\n    this._backgroundGroup = null;\n  };\n\n  BarView.type = 'bar';\n  return BarView;\n}(ChartView);\n\nvar clip = {\n  cartesian2d: function (coordSysBoundingRect, layout) {\n    var signWidth = layout.width < 0 ? -1 : 1;\n    var signHeight = layout.height < 0 ? -1 : 1; // Needs positive width and height\n\n    if (signWidth < 0) {\n      layout.x += layout.width;\n      layout.width = -layout.width;\n    }\n\n    if (signHeight < 0) {\n      layout.y += layout.height;\n      layout.height = -layout.height;\n    }\n\n    var coordSysX2 = coordSysBoundingRect.x + coordSysBoundingRect.width;\n    var coordSysY2 = coordSysBoundingRect.y + coordSysBoundingRect.height;\n    var x = mathMax$6(layout.x, coordSysBoundingRect.x);\n    var x2 = mathMin$6(layout.x + layout.width, coordSysX2);\n    var y = mathMax$6(layout.y, coordSysBoundingRect.y);\n    var y2 = mathMin$6(layout.y + layout.height, coordSysY2);\n    var xClipped = x2 < x;\n    var yClipped = y2 < y; // When xClipped or yClipped, the element will be marked as `ignore`.\n    // But we should also place the element at the edge of the coord sys bounding rect.\n    // Because if data changed and the bar shows again, its transition animation\n    // will begin at this place.\n\n    layout.x = xClipped && x > coordSysX2 ? x2 : x;\n    layout.y = yClipped && y > coordSysY2 ? y2 : y;\n    layout.width = xClipped ? 0 : x2 - x;\n    layout.height = yClipped ? 0 : y2 - y; // Reverse back\n\n    if (signWidth < 0) {\n      layout.x += layout.width;\n      layout.width = -layout.width;\n    }\n\n    if (signHeight < 0) {\n      layout.y += layout.height;\n      layout.height = -layout.height;\n    }\n\n    return xClipped || yClipped;\n  },\n  polar: function (coordSysClipArea, layout) {\n    var signR = layout.r0 <= layout.r ? 1 : -1; // Make sure r is larger than r0\n\n    if (signR < 0) {\n      var tmp = layout.r;\n      layout.r = layout.r0;\n      layout.r0 = tmp;\n    }\n\n    var r = mathMin$6(layout.r, coordSysClipArea.r);\n    var r0 = mathMax$6(layout.r0, coordSysClipArea.r0);\n    layout.r = r;\n    layout.r0 = r0;\n    var clipped = r - r0 < 0; // Reverse back\n\n    if (signR < 0) {\n      var tmp = layout.r;\n      layout.r = layout.r0;\n      layout.r0 = tmp;\n    }\n\n    return clipped;\n  }\n};\nvar elementCreator = {\n  cartesian2d: function (seriesModel, data, newIndex, layout, isHorizontal, animationModel, axisModel, isUpdate, roundCap) {\n    var rect = new Rect({\n      shape: extend({}, layout),\n      z2: 1\n    });\n    rect.__dataIndex = newIndex;\n    rect.name = 'item';\n\n    if (animationModel) {\n      var rectShape = rect.shape;\n      var animateProperty = isHorizontal ? 'height' : 'width';\n      rectShape[animateProperty] = 0;\n    }\n\n    return rect;\n  },\n  polar: function (seriesModel, data, newIndex, layout, isRadial, animationModel, axisModel, isUpdate, roundCap) {\n    var ShapeClass = !isRadial && roundCap ? SausagePath : Sector;\n    var sector = new ShapeClass({\n      shape: layout,\n      z2: 1\n    });\n    sector.name = 'item';\n    var positionMap = createPolarPositionMapping(isRadial);\n    sector.calculateTextPosition = createSectorCalculateTextPosition(positionMap, {\n      isRoundCap: ShapeClass === SausagePath\n    }); // Animation\n\n    if (animationModel) {\n      var sectorShape = sector.shape;\n      var animateProperty = isRadial ? 'r' : 'endAngle';\n      var animateTarget = {};\n      sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;\n      animateTarget[animateProperty] = layout[animateProperty];\n      (isUpdate ? updateProps : initProps)(sector, {\n        shape: animateTarget // __value: typeof dataValue === 'string' ? parseInt(dataValue, 10) : dataValue\n\n      }, animationModel);\n    }\n\n    return sector;\n  }\n};\n\nfunction shouldRealtimeSort(seriesModel, coordSys) {\n  var realtimeSortOption = seriesModel.get('realtimeSort', true);\n  var baseAxis = coordSys.getBaseAxis();\n\n  if (\"development\" !== 'production') {\n    if (realtimeSortOption) {\n      if (baseAxis.type !== 'category') {\n        warn('`realtimeSort` will not work because this bar series is not based on a category axis.');\n      }\n\n      if (coordSys.type !== 'cartesian2d') {\n        warn('`realtimeSort` will not work because this bar series is not on cartesian2d.');\n      }\n    }\n  }\n\n  if (realtimeSortOption && baseAxis.type === 'category' && coordSys.type === 'cartesian2d') {\n    return {\n      baseAxis: baseAxis,\n      otherAxis: coordSys.getOtherAxis(baseAxis)\n    };\n  }\n}\n\nfunction updateRealtimeAnimation(realtimeSortCfg, seriesAnimationModel, el, layout, newIndex, isHorizontal, isUpdate, isChangeOrder) {\n  var seriesTarget;\n  var axisTarget;\n\n  if (isHorizontal) {\n    axisTarget = {\n      x: layout.x,\n      width: layout.width\n    };\n    seriesTarget = {\n      y: layout.y,\n      height: layout.height\n    };\n  } else {\n    axisTarget = {\n      y: layout.y,\n      height: layout.height\n    };\n    seriesTarget = {\n      x: layout.x,\n      width: layout.width\n    };\n  }\n\n  if (!isChangeOrder) {\n    // Keep the original growth animation if only axis order changed.\n    // Not start a new animation.\n    (isUpdate ? updateProps : initProps)(el, {\n      shape: seriesTarget\n    }, seriesAnimationModel, newIndex, null);\n  }\n\n  var axisAnimationModel = seriesAnimationModel ? realtimeSortCfg.baseAxis.model : null;\n  (isUpdate ? updateProps : initProps)(el, {\n    shape: axisTarget\n  }, axisAnimationModel, newIndex);\n}\n\nfunction checkPropertiesNotValid(obj, props) {\n  for (var i = 0; i < props.length; i++) {\n    if (!isFinite(obj[props[i]])) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nvar rectPropties = ['x', 'y', 'width', 'height'];\nvar polarPropties = ['cx', 'cy', 'r', 'startAngle', 'endAngle'];\nvar isValidLayout = {\n  cartesian2d: function (layout) {\n    return !checkPropertiesNotValid(layout, rectPropties);\n  },\n  polar: function (layout) {\n    return !checkPropertiesNotValid(layout, polarPropties);\n  }\n};\nvar getLayout = {\n  // itemModel is only used to get borderWidth, which is not needed\n  // when calculating bar background layout.\n  cartesian2d: function (data, dataIndex, itemModel) {\n    var layout = data.getItemLayout(dataIndex);\n    var fixedLineWidth = itemModel ? getLineWidth(itemModel, layout) : 0; // fix layout with lineWidth\n\n    var signX = layout.width > 0 ? 1 : -1;\n    var signY = layout.height > 0 ? 1 : -1;\n    return {\n      x: layout.x + signX * fixedLineWidth / 2,\n      y: layout.y + signY * fixedLineWidth / 2,\n      width: layout.width - signX * fixedLineWidth,\n      height: layout.height - signY * fixedLineWidth\n    };\n  },\n  polar: function (data, dataIndex, itemModel) {\n    var layout = data.getItemLayout(dataIndex);\n    return {\n      cx: layout.cx,\n      cy: layout.cy,\n      r0: layout.r0,\n      r: layout.r,\n      startAngle: layout.startAngle,\n      endAngle: layout.endAngle,\n      clockwise: layout.clockwise\n    };\n  }\n};\n\nfunction isZeroOnPolar(layout) {\n  return layout.startAngle != null && layout.endAngle != null && layout.startAngle === layout.endAngle;\n}\n\nfunction createPolarPositionMapping(isRadial) {\n  return function (isRadial) {\n    var arcOrAngle = isRadial ? 'Arc' : 'Angle';\n    return function (position) {\n      switch (position) {\n        case 'start':\n        case 'insideStart':\n        case 'end':\n        case 'insideEnd':\n          return position + arcOrAngle;\n\n        default:\n          return position;\n      }\n    };\n  }(isRadial);\n}\n\nfunction updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, isPolar) {\n  var style = data.getItemVisual(dataIndex, 'style');\n\n  if (!isPolar) {\n    el.setShape('r', itemModel.get(['itemStyle', 'borderRadius']) || 0);\n  }\n\n  el.useStyle(style);\n  var cursorStyle = itemModel.getShallow('cursor');\n  cursorStyle && el.attr('cursor', cursorStyle);\n  var labelPositionOutside = isPolar ? isHorizontalOrRadial ? layout.r >= layout.r0 ? 'endArc' : 'startArc' : layout.endAngle >= layout.startAngle ? 'endAngle' : 'startAngle' : isHorizontalOrRadial ? layout.height >= 0 ? 'bottom' : 'top' : layout.width >= 0 ? 'right' : 'left';\n  var labelStatesModels = getLabelStatesModels(itemModel);\n  setLabelStyle(el, labelStatesModels, {\n    labelFetcher: seriesModel,\n    labelDataIndex: dataIndex,\n    defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),\n    inheritColor: style.fill,\n    defaultOpacity: style.opacity,\n    defaultOutsidePosition: labelPositionOutside\n  });\n  var label = el.getTextContent();\n\n  if (isPolar && label) {\n    var position = itemModel.get(['label', 'position']);\n    el.textConfig.inside = position === 'middle' ? true : null;\n    setSectorTextRotation(el, position === 'outside' ? labelPositionOutside : position, createPolarPositionMapping(isHorizontalOrRadial), itemModel.get(['label', 'rotate']));\n  }\n\n  setLabelValueAnimation(label, labelStatesModels, seriesModel.getRawValue(dataIndex), function (value) {\n    return getDefaultInterpolatedLabel(data, value);\n  });\n  var emphasisModel = itemModel.getModel(['emphasis']);\n  toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n  setStatesStylesFromModel(el, itemModel);\n\n  if (isZeroOnPolar(layout)) {\n    el.style.fill = 'none';\n    el.style.stroke = 'none';\n    each(el.states, function (state) {\n      if (state.style) {\n        state.style.fill = state.style.stroke = 'none';\n      }\n    });\n  }\n} // In case width or height are too small.\n\n\nfunction getLineWidth(itemModel, rawLayout) {\n  // Has no border.\n  var borderColor = itemModel.get(['itemStyle', 'borderColor']);\n\n  if (!borderColor || borderColor === 'none') {\n    return 0;\n  }\n\n  var lineWidth = itemModel.get(['itemStyle', 'borderWidth']) || 0; // width or height may be NaN for empty data\n\n  var width = isNaN(rawLayout.width) ? Number.MAX_VALUE : Math.abs(rawLayout.width);\n  var height = isNaN(rawLayout.height) ? Number.MAX_VALUE : Math.abs(rawLayout.height);\n  return Math.min(lineWidth, width, height);\n}\n\nvar LagePathShape =\n/** @class */\nfunction () {\n  function LagePathShape() {}\n\n  return LagePathShape;\n}();\n\nvar LargePath =\n/** @class */\nfunction (_super) {\n  __extends(LargePath, _super);\n\n  function LargePath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'largeBar';\n    return _this;\n  }\n\n  LargePath.prototype.getDefaultShape = function () {\n    return new LagePathShape();\n  };\n\n  LargePath.prototype.buildPath = function (ctx, shape) {\n    // Drawing lines is more efficient than drawing\n    // a whole line or drawing rects.\n    var points = shape.points;\n    var baseDimIdx = this.baseDimIdx;\n    var valueDimIdx = 1 - this.baseDimIdx;\n    var startPoint = [];\n    var size = [];\n    var barWidth = this.barWidth;\n\n    for (var i = 0; i < points.length; i += 3) {\n      size[baseDimIdx] = barWidth;\n      size[valueDimIdx] = points[i + 2];\n      startPoint[baseDimIdx] = points[i + baseDimIdx];\n      startPoint[valueDimIdx] = points[i + valueDimIdx];\n      ctx.rect(startPoint[0], startPoint[1], size[0], size[1]);\n    }\n  };\n\n  return LargePath;\n}(Path);\n\nfunction createLarge(seriesModel, group, progressiveEls, incremental) {\n  // TODO support polar\n  var data = seriesModel.getData();\n  var baseDimIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0;\n  var largeDataIndices = data.getLayout('largeDataIndices');\n  var barWidth = data.getLayout('size');\n  var backgroundModel = seriesModel.getModel('backgroundStyle');\n  var bgPoints = data.getLayout('largeBackgroundPoints');\n\n  if (bgPoints) {\n    var bgEl = new LargePath({\n      shape: {\n        points: bgPoints\n      },\n      incremental: !!incremental,\n      silent: true,\n      z2: 0\n    });\n    bgEl.baseDimIdx = baseDimIdx;\n    bgEl.largeDataIndices = largeDataIndices;\n    bgEl.barWidth = barWidth;\n    bgEl.useStyle(backgroundModel.getItemStyle());\n    group.add(bgEl);\n    progressiveEls && progressiveEls.push(bgEl);\n  }\n\n  var el = new LargePath({\n    shape: {\n      points: data.getLayout('largePoints')\n    },\n    incremental: !!incremental,\n    ignoreCoarsePointer: true,\n    z2: 1\n  });\n  el.baseDimIdx = baseDimIdx;\n  el.largeDataIndices = largeDataIndices;\n  el.barWidth = barWidth;\n  group.add(el);\n  el.useStyle(data.getVisual('style')); // Enable tooltip and user mouse/touch event handlers.\n\n  getECData(el).seriesIndex = seriesModel.seriesIndex;\n\n  if (!seriesModel.get('silent')) {\n    el.on('mousedown', largePathUpdateDataIndex);\n    el.on('mousemove', largePathUpdateDataIndex);\n  }\n\n  progressiveEls && progressiveEls.push(el);\n} // Use throttle to avoid frequently traverse to find dataIndex.\n\n\nvar largePathUpdateDataIndex = throttle(function (event) {\n  var largePath = this;\n  var dataIndex = largePathFindDataIndex(largePath, event.offsetX, event.offsetY);\n  getECData(largePath).dataIndex = dataIndex >= 0 ? dataIndex : null;\n}, 30, false);\n\nfunction largePathFindDataIndex(largePath, x, y) {\n  var baseDimIdx = largePath.baseDimIdx;\n  var valueDimIdx = 1 - baseDimIdx;\n  var points = largePath.shape.points;\n  var largeDataIndices = largePath.largeDataIndices;\n  var startPoint = [];\n  var size = [];\n  var barWidth = largePath.barWidth;\n\n  for (var i = 0, len = points.length / 3; i < len; i++) {\n    var ii = i * 3;\n    size[baseDimIdx] = barWidth;\n    size[valueDimIdx] = points[ii + 2];\n    startPoint[baseDimIdx] = points[ii + baseDimIdx];\n    startPoint[valueDimIdx] = points[ii + valueDimIdx];\n\n    if (size[valueDimIdx] < 0) {\n      startPoint[valueDimIdx] += size[valueDimIdx];\n      size[valueDimIdx] = -size[valueDimIdx];\n    }\n\n    if (x >= startPoint[0] && x <= startPoint[0] + size[0] && y >= startPoint[1] && y <= startPoint[1] + size[1]) {\n      return largeDataIndices[i];\n    }\n  }\n\n  return -1;\n}\n\nfunction createBackgroundShape(isHorizontalOrRadial, layout, coord) {\n  if (isCoordinateSystemType(coord, 'cartesian2d')) {\n    var rectShape = layout;\n    var coordLayout = coord.getArea();\n    return {\n      x: isHorizontalOrRadial ? rectShape.x : coordLayout.x,\n      y: isHorizontalOrRadial ? coordLayout.y : rectShape.y,\n      width: isHorizontalOrRadial ? rectShape.width : coordLayout.width,\n      height: isHorizontalOrRadial ? coordLayout.height : rectShape.height\n    };\n  } else {\n    var coordLayout = coord.getArea();\n    var sectorShape = layout;\n    return {\n      cx: coordLayout.cx,\n      cy: coordLayout.cy,\n      r0: isHorizontalOrRadial ? coordLayout.r0 : sectorShape.r0,\n      r: isHorizontalOrRadial ? coordLayout.r : sectorShape.r,\n      startAngle: isHorizontalOrRadial ? sectorShape.startAngle : 0,\n      endAngle: isHorizontalOrRadial ? sectorShape.endAngle : Math.PI * 2\n    };\n  }\n}\n\nfunction createBackgroundEl(coord, isHorizontalOrRadial, layout) {\n  var ElementClz = coord.type === 'polar' ? Sector : Rect;\n  return new ElementClz({\n    shape: createBackgroundShape(isHorizontalOrRadial, layout, coord),\n    silent: true,\n    z2: 0\n  });\n}\n\nfunction install$3(registers) {\n  registers.registerChartView(BarView);\n  registers.registerSeriesModel(BarSeriesModel);\n  registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'bar')); // Do layout after other overall layout, which can prepare some information.\n\n  registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('bar')); // Down sample after filter\n\n  registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('bar'));\n  /**\n   * @payload\n   * @property {string} [componentType=series]\n   * @property {number} [dx]\n   * @property {number} [dy]\n   * @property {number} [zoom]\n   * @property {number} [originX]\n   * @property {number} [originY]\n   */\n\n  registers.registerAction({\n    type: 'changeAxisOrder',\n    event: 'changeAxisOrder',\n    update: 'update'\n  }, function (payload, ecModel) {\n    var componentType = payload.componentType || 'series';\n    ecModel.eachComponent({\n      mainType: componentType,\n      query: payload\n    }, function (componentModel) {\n      if (payload.sortInfo) {\n        componentModel.axis.setCategorySortInfo(payload.sortInfo);\n      }\n    });\n  });\n}\n\nvar PI2$8 = Math.PI * 2;\nvar RADIAN = Math.PI / 180;\n\nfunction getViewRect(seriesModel, api) {\n  return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n    width: api.getWidth(),\n    height: api.getHeight()\n  });\n}\n\nfunction getBasicPieLayout(seriesModel, api) {\n  var viewRect = getViewRect(seriesModel, api); // center can be string or number when coordinateSystem is specified\n\n  var center = seriesModel.get('center');\n  var radius = seriesModel.get('radius');\n\n  if (!isArray(radius)) {\n    radius = [0, radius];\n  }\n\n  var width = parsePercent$1(viewRect.width, api.getWidth());\n  var height = parsePercent$1(viewRect.height, api.getHeight());\n  var size = Math.min(width, height);\n  var r0 = parsePercent$1(radius[0], size / 2);\n  var r = parsePercent$1(radius[1], size / 2);\n  var cx;\n  var cy;\n  var coordSys = seriesModel.coordinateSystem;\n\n  if (coordSys) {\n    // percentage is not allowed when coordinate system is specified\n    var point = coordSys.dataToPoint(center);\n    cx = point[0] || 0;\n    cy = point[1] || 0;\n  } else {\n    if (!isArray(center)) {\n      center = [center, center];\n    }\n\n    cx = parsePercent$1(center[0], width) + viewRect.x;\n    cy = parsePercent$1(center[1], height) + viewRect.y;\n  }\n\n  return {\n    cx: cx,\n    cy: cy,\n    r0: r0,\n    r: r\n  };\n}\nfunction pieLayout(seriesType, ecModel, api) {\n  ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n    var data = seriesModel.getData();\n    var valueDim = data.mapDimension('value');\n    var viewRect = getViewRect(seriesModel, api);\n\n    var _a = getBasicPieLayout(seriesModel, api),\n        cx = _a.cx,\n        cy = _a.cy,\n        r = _a.r,\n        r0 = _a.r0;\n\n    var startAngle = -seriesModel.get('startAngle') * RADIAN;\n    var minAngle = seriesModel.get('minAngle') * RADIAN;\n    var validDataCount = 0;\n    data.each(valueDim, function (value) {\n      !isNaN(value) && validDataCount++;\n    });\n    var sum = data.getSum(valueDim); // Sum may be 0\n\n    var unitRadian = Math.PI / (sum || validDataCount) * 2;\n    var clockwise = seriesModel.get('clockwise');\n    var roseType = seriesModel.get('roseType');\n    var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); // [0...max]\n\n    var extent = data.getDataExtent(valueDim);\n    extent[0] = 0; // In the case some sector angle is smaller than minAngle\n\n    var restAngle = PI2$8;\n    var valueSumLargerThanMinAngle = 0;\n    var currentAngle = startAngle;\n    var dir = clockwise ? 1 : -1;\n    data.setLayout({\n      viewRect: viewRect,\n      r: r\n    });\n    data.each(valueDim, function (value, idx) {\n      var angle;\n\n      if (isNaN(value)) {\n        data.setItemLayout(idx, {\n          angle: NaN,\n          startAngle: NaN,\n          endAngle: NaN,\n          clockwise: clockwise,\n          cx: cx,\n          cy: cy,\n          r0: r0,\n          r: roseType ? NaN : r\n        });\n        return;\n      } // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样？\n\n\n      if (roseType !== 'area') {\n        angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian;\n      } else {\n        angle = PI2$8 / validDataCount;\n      }\n\n      if (angle < minAngle) {\n        angle = minAngle;\n        restAngle -= minAngle;\n      } else {\n        valueSumLargerThanMinAngle += value;\n      }\n\n      var endAngle = currentAngle + dir * angle;\n      data.setItemLayout(idx, {\n        angle: angle,\n        startAngle: currentAngle,\n        endAngle: endAngle,\n        clockwise: clockwise,\n        cx: cx,\n        cy: cy,\n        r0: r0,\n        r: roseType ? linearMap(value, extent, [r0, r]) : r\n      });\n      currentAngle = endAngle;\n    }); // Some sector is constrained by minAngle\n    // Rest sectors needs recalculate angle\n\n    if (restAngle < PI2$8 && validDataCount) {\n      // Average the angle if rest angle is not enough after all angles is\n      // Constrained by minAngle\n      if (restAngle <= 1e-3) {\n        var angle_1 = PI2$8 / validDataCount;\n        data.each(valueDim, function (value, idx) {\n          if (!isNaN(value)) {\n            var layout_1 = data.getItemLayout(idx);\n            layout_1.angle = angle_1;\n            layout_1.startAngle = startAngle + dir * idx * angle_1;\n            layout_1.endAngle = startAngle + dir * (idx + 1) * angle_1;\n          }\n        });\n      } else {\n        unitRadian = restAngle / valueSumLargerThanMinAngle;\n        currentAngle = startAngle;\n        data.each(valueDim, function (value, idx) {\n          if (!isNaN(value)) {\n            var layout_2 = data.getItemLayout(idx);\n            var angle = layout_2.angle === minAngle ? minAngle : value * unitRadian;\n            layout_2.startAngle = currentAngle;\n            layout_2.endAngle = currentAngle + dir * angle;\n            currentAngle += dir * angle;\n          }\n        });\n      }\n    }\n  });\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction dataFilter(seriesType) {\n  return {\n    seriesType: seriesType,\n    reset: function (seriesModel, ecModel) {\n      var legendModels = ecModel.findComponents({\n        mainType: 'legend'\n      });\n\n      if (!legendModels || !legendModels.length) {\n        return;\n      }\n\n      var data = seriesModel.getData();\n      data.filterSelf(function (idx) {\n        var name = data.getName(idx); // If in any legend component the status is not selected.\n\n        for (var i = 0; i < legendModels.length; i++) {\n          // @ts-ignore FIXME: LegendModel\n          if (!legendModels[i].isSelected(name)) {\n            return false;\n          }\n        }\n\n        return true;\n      });\n    }\n  };\n}\n\nvar RADIAN$1 = Math.PI / 180;\n\nfunction adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight, viewLeft, viewTop, farthestX) {\n  if (list.length < 2) {\n    return;\n  }\n\n  function recalculateXOnSemiToAlignOnEllipseCurve(semi) {\n    var rB = semi.rB;\n    var rB2 = rB * rB;\n\n    for (var i = 0; i < semi.list.length; i++) {\n      var item = semi.list[i];\n      var dy = Math.abs(item.label.y - cy); // horizontal r is always same with original r because x is not changed.\n\n      var rA = r + item.len;\n      var rA2 = rA * rA; // Use ellipse implicit function to calculate x\n\n      var dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2);\n      var newX = cx + (dx + item.len2) * dir;\n      var deltaX = newX - item.label.x;\n      var newTargetWidth = item.targetTextWidth - deltaX * dir; // text x is changed, so need to recalculate width.\n\n      constrainTextWidth(item, newTargetWidth, true);\n      item.label.x = newX;\n    }\n  } // Adjust X based on the shifted y. Make tight labels aligned on an ellipse curve.\n\n\n  function recalculateX(items) {\n    // Extremes of\n    var topSemi = {\n      list: [],\n      maxY: 0\n    };\n    var bottomSemi = {\n      list: [],\n      maxY: 0\n    };\n\n    for (var i = 0; i < items.length; i++) {\n      if (items[i].labelAlignTo !== 'none') {\n        continue;\n      }\n\n      var item = items[i];\n      var semi = item.label.y > cy ? bottomSemi : topSemi;\n      var dy = Math.abs(item.label.y - cy);\n\n      if (dy >= semi.maxY) {\n        var dx = item.label.x - cx - item.len2 * dir; // horizontal r is always same with original r because x is not changed.\n\n        var rA = r + item.len; // Canculate rB based on the topest / bottemest label.\n\n        var rB = Math.abs(dx) < rA ? Math.sqrt(dy * dy / (1 - dx * dx / rA / rA)) : rA;\n        semi.rB = rB;\n        semi.maxY = dy;\n      }\n\n      semi.list.push(item);\n    }\n\n    recalculateXOnSemiToAlignOnEllipseCurve(topSemi);\n    recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi);\n  }\n\n  var len = list.length;\n\n  for (var i = 0; i < len; i++) {\n    if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') {\n      var dx = list[i].label.x - farthestX;\n      list[i].linePoints[1][0] += dx;\n      list[i].label.x = farthestX;\n    }\n  }\n\n  if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) {\n    recalculateX(list);\n  }\n}\n\nfunction avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop) {\n  var leftList = [];\n  var rightList = [];\n  var leftmostX = Number.MAX_VALUE;\n  var rightmostX = -Number.MAX_VALUE;\n\n  for (var i = 0; i < labelLayoutList.length; i++) {\n    var label = labelLayoutList[i].label;\n\n    if (isPositionCenter(labelLayoutList[i])) {\n      continue;\n    }\n\n    if (label.x < cx) {\n      leftmostX = Math.min(leftmostX, label.x);\n      leftList.push(labelLayoutList[i]);\n    } else {\n      rightmostX = Math.max(rightmostX, label.x);\n      rightList.push(labelLayoutList[i]);\n    }\n  }\n\n  for (var i = 0; i < labelLayoutList.length; i++) {\n    var layout = labelLayoutList[i];\n\n    if (!isPositionCenter(layout) && layout.linePoints) {\n      if (layout.labelStyleWidth != null) {\n        continue;\n      }\n\n      var label = layout.label;\n      var linePoints = layout.linePoints;\n      var targetTextWidth = void 0;\n\n      if (layout.labelAlignTo === 'edge') {\n        if (label.x < cx) {\n          targetTextWidth = linePoints[2][0] - layout.labelDistance - viewLeft - layout.edgeDistance;\n        } else {\n          targetTextWidth = viewLeft + viewWidth - layout.edgeDistance - linePoints[2][0] - layout.labelDistance;\n        }\n      } else if (layout.labelAlignTo === 'labelLine') {\n        if (label.x < cx) {\n          targetTextWidth = leftmostX - viewLeft - layout.bleedMargin;\n        } else {\n          targetTextWidth = viewLeft + viewWidth - rightmostX - layout.bleedMargin;\n        }\n      } else {\n        if (label.x < cx) {\n          targetTextWidth = label.x - viewLeft - layout.bleedMargin;\n        } else {\n          targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin;\n        }\n      }\n\n      layout.targetTextWidth = targetTextWidth;\n      constrainTextWidth(layout, targetTextWidth);\n    }\n  }\n\n  adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX);\n  adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);\n\n  for (var i = 0; i < labelLayoutList.length; i++) {\n    var layout = labelLayoutList[i];\n\n    if (!isPositionCenter(layout) && layout.linePoints) {\n      var label = layout.label;\n      var linePoints = layout.linePoints;\n      var isAlignToEdge = layout.labelAlignTo === 'edge';\n      var padding = label.style.padding;\n      var paddingH = padding ? padding[1] + padding[3] : 0; // textRect.width already contains paddingH if bgColor is set\n\n      var extraPaddingH = label.style.backgroundColor ? 0 : paddingH;\n      var realTextWidth = layout.rect.width + extraPaddingH;\n      var dist = linePoints[1][0] - linePoints[2][0];\n\n      if (isAlignToEdge) {\n        if (label.x < cx) {\n          linePoints[2][0] = viewLeft + layout.edgeDistance + realTextWidth + layout.labelDistance;\n        } else {\n          linePoints[2][0] = viewLeft + viewWidth - layout.edgeDistance - realTextWidth - layout.labelDistance;\n        }\n      } else {\n        if (label.x < cx) {\n          linePoints[2][0] = label.x + layout.labelDistance;\n        } else {\n          linePoints[2][0] = label.x - layout.labelDistance;\n        }\n\n        linePoints[1][0] = linePoints[2][0] + dist;\n      }\n\n      linePoints[1][1] = linePoints[2][1] = label.y;\n    }\n  }\n}\n/**\n * Set max width of each label, and then wrap each label to the max width.\n *\n * @param layout label layout\n * @param availableWidth max width for the label to display\n * @param forceRecalculate recaculate the text layout even if the current width\n * is smaller than `availableWidth`. This is useful when the text was previously\n * wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in\n * which case, previous wrapping should be redo.\n */\n\n\nfunction constrainTextWidth(layout, availableWidth, forceRecalculate) {\n  if (forceRecalculate === void 0) {\n    forceRecalculate = false;\n  }\n\n  if (layout.labelStyleWidth != null) {\n    // User-defined style.width has the highest priority.\n    return;\n  }\n\n  var label = layout.label;\n  var style = label.style;\n  var textRect = layout.rect;\n  var bgColor = style.backgroundColor;\n  var padding = style.padding;\n  var paddingH = padding ? padding[1] + padding[3] : 0;\n  var overflow = style.overflow; // textRect.width already contains paddingH if bgColor is set\n\n  var oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH);\n\n  if (availableWidth < oldOuterWidth || forceRecalculate) {\n    var oldHeight = textRect.height;\n\n    if (overflow && overflow.match('break')) {\n      // Temporarily set background to be null to calculate\n      // the bounding box without background.\n      label.setStyle('backgroundColor', null); // Set constraining width\n\n      label.setStyle('width', availableWidth - paddingH); // This is the real bounding box of the text without padding.\n\n      var innerRect = label.getBoundingRect();\n      label.setStyle('width', Math.ceil(innerRect.width));\n      label.setStyle('backgroundColor', bgColor);\n    } else {\n      var availableInnerWidth = availableWidth - paddingH;\n      var newWidth = availableWidth < oldOuterWidth // Current text is too wide, use `availableWidth` as max width.\n      ? availableInnerWidth : // Current available width is enough, but the text may have\n      // already been wrapped with a smaller available width.\n      forceRecalculate ? availableInnerWidth > layout.unconstrainedWidth // Current available is larger than text width,\n      // so don't constrain width (otherwise it may have\n      // empty space in the background).\n      ? null // Current available is smaller than text width, so\n      // use the current available width as constraining\n      // width.\n      : availableInnerWidth : // Current available width is enough, so no need to\n      // constrain.\n      null;\n      label.setStyle('width', newWidth);\n    }\n\n    var newRect = label.getBoundingRect();\n    textRect.width = newRect.width;\n    var margin = (label.style.margin || 0) + 2.1;\n    textRect.height = newRect.height + margin;\n    textRect.y -= (textRect.height - oldHeight) / 2;\n  }\n}\n\nfunction isPositionCenter(sectorShape) {\n  // Not change x for center label\n  return sectorShape.position === 'center';\n}\n\nfunction pieLabelLayout(seriesModel) {\n  var data = seriesModel.getData();\n  var labelLayoutList = [];\n  var cx;\n  var cy;\n  var hasLabelRotate = false;\n  var minShowLabelRadian = (seriesModel.get('minShowLabelAngle') || 0) * RADIAN$1;\n  var viewRect = data.getLayout('viewRect');\n  var r = data.getLayout('r');\n  var viewWidth = viewRect.width;\n  var viewLeft = viewRect.x;\n  var viewTop = viewRect.y;\n  var viewHeight = viewRect.height;\n\n  function setNotShow(el) {\n    el.ignore = true;\n  }\n\n  function isLabelShown(label) {\n    if (!label.ignore) {\n      return true;\n    }\n\n    for (var key in label.states) {\n      if (label.states[key].ignore === false) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  data.each(function (idx) {\n    var sector = data.getItemGraphicEl(idx);\n    var sectorShape = sector.shape;\n    var label = sector.getTextContent();\n    var labelLine = sector.getTextGuideLine();\n    var itemModel = data.getItemModel(idx);\n    var labelModel = itemModel.getModel('label'); // Use position in normal or emphasis\n\n    var labelPosition = labelModel.get('position') || itemModel.get(['emphasis', 'label', 'position']);\n    var labelDistance = labelModel.get('distanceToLabelLine');\n    var labelAlignTo = labelModel.get('alignTo');\n    var edgeDistance = parsePercent$1(labelModel.get('edgeDistance'), viewWidth);\n    var bleedMargin = labelModel.get('bleedMargin');\n    var labelLineModel = itemModel.getModel('labelLine');\n    var labelLineLen = labelLineModel.get('length');\n    labelLineLen = parsePercent$1(labelLineLen, viewWidth);\n    var labelLineLen2 = labelLineModel.get('length2');\n    labelLineLen2 = parsePercent$1(labelLineLen2, viewWidth);\n\n    if (Math.abs(sectorShape.endAngle - sectorShape.startAngle) < minShowLabelRadian) {\n      each(label.states, setNotShow);\n      label.ignore = true;\n\n      if (labelLine) {\n        each(labelLine.states, setNotShow);\n        labelLine.ignore = true;\n      }\n\n      return;\n    }\n\n    if (!isLabelShown(label)) {\n      return;\n    }\n\n    var midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2;\n    var nx = Math.cos(midAngle);\n    var ny = Math.sin(midAngle);\n    var textX;\n    var textY;\n    var linePoints;\n    var textAlign;\n    cx = sectorShape.cx;\n    cy = sectorShape.cy;\n    var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';\n\n    if (labelPosition === 'center') {\n      textX = sectorShape.cx;\n      textY = sectorShape.cy;\n      textAlign = 'center';\n    } else {\n      var x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * nx : sectorShape.r * nx) + cx;\n      var y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * ny : sectorShape.r * ny) + cy;\n      textX = x1 + nx * 3;\n      textY = y1 + ny * 3;\n\n      if (!isLabelInside) {\n        // For roseType\n        var x2 = x1 + nx * (labelLineLen + r - sectorShape.r);\n        var y2 = y1 + ny * (labelLineLen + r - sectorShape.r);\n        var x3 = x2 + (nx < 0 ? -1 : 1) * labelLineLen2;\n        var y3 = y2;\n\n        if (labelAlignTo === 'edge') {\n          // Adjust textX because text align of edge is opposite\n          textX = nx < 0 ? viewLeft + edgeDistance : viewLeft + viewWidth - edgeDistance;\n        } else {\n          textX = x3 + (nx < 0 ? -labelDistance : labelDistance);\n        }\n\n        textY = y3;\n        linePoints = [[x1, y1], [x2, y2], [x3, y3]];\n      }\n\n      textAlign = isLabelInside ? 'center' : labelAlignTo === 'edge' ? nx > 0 ? 'right' : 'left' : nx > 0 ? 'left' : 'right';\n    }\n\n    var PI = Math.PI;\n    var labelRotate = 0;\n    var rotate = labelModel.get('rotate');\n\n    if (isNumber(rotate)) {\n      labelRotate = rotate * (PI / 180);\n    } else if (labelPosition === 'center') {\n      labelRotate = 0;\n    } else if (rotate === 'radial' || rotate === true) {\n      var radialAngle = nx < 0 ? -midAngle + PI : -midAngle;\n      labelRotate = radialAngle;\n    } else if (rotate === 'tangential' && labelPosition !== 'outside' && labelPosition !== 'outer') {\n      var rad = Math.atan2(nx, ny);\n\n      if (rad < 0) {\n        rad = PI * 2 + rad;\n      }\n\n      var isDown = ny > 0;\n\n      if (isDown) {\n        rad = PI + rad;\n      }\n\n      labelRotate = rad - PI;\n    }\n\n    hasLabelRotate = !!labelRotate;\n    label.x = textX;\n    label.y = textY;\n    label.rotation = labelRotate;\n    label.setStyle({\n      verticalAlign: 'middle'\n    }); // Not sectorShape the inside label\n\n    if (!isLabelInside) {\n      var textRect = label.getBoundingRect().clone();\n      textRect.applyTransform(label.getComputedTransform()); // Text has a default 1px stroke. Exclude this.\n\n      var margin = (label.style.margin || 0) + 2.1;\n      textRect.y -= margin / 2;\n      textRect.height += margin;\n      labelLayoutList.push({\n        label: label,\n        labelLine: labelLine,\n        position: labelPosition,\n        len: labelLineLen,\n        len2: labelLineLen2,\n        minTurnAngle: labelLineModel.get('minTurnAngle'),\n        maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'),\n        surfaceNormal: new Point(nx, ny),\n        linePoints: linePoints,\n        textAlign: textAlign,\n        labelDistance: labelDistance,\n        labelAlignTo: labelAlignTo,\n        edgeDistance: edgeDistance,\n        bleedMargin: bleedMargin,\n        rect: textRect,\n        unconstrainedWidth: textRect.width,\n        labelStyleWidth: label.style.width\n      });\n    } else {\n      label.setStyle({\n        align: textAlign\n      });\n      var selectState = label.states.select;\n\n      if (selectState) {\n        selectState.x += label.x;\n        selectState.y += label.y;\n      }\n    }\n\n    sector.setTextConfig({\n      inside: isLabelInside\n    });\n  });\n\n  if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {\n    avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop);\n  }\n\n  for (var i = 0; i < labelLayoutList.length; i++) {\n    var layout = labelLayoutList[i];\n    var label = layout.label;\n    var labelLine = layout.labelLine;\n    var notShowLabel = isNaN(label.x) || isNaN(label.y);\n\n    if (label) {\n      label.setStyle({\n        align: layout.textAlign\n      });\n\n      if (notShowLabel) {\n        each(label.states, setNotShow);\n        label.ignore = true;\n      }\n\n      var selectState = label.states.select;\n\n      if (selectState) {\n        selectState.x += label.x;\n        selectState.y += label.y;\n      }\n    }\n\n    if (labelLine) {\n      var linePoints = layout.linePoints;\n\n      if (notShowLabel || !linePoints) {\n        each(labelLine.states, setNotShow);\n        labelLine.ignore = true;\n      } else {\n        limitTurnAngle(linePoints, layout.minTurnAngle);\n        limitSurfaceAngle(linePoints, layout.surfaceNormal, layout.maxSurfaceAngle);\n        labelLine.setShape({\n          points: linePoints\n        }); // Set the anchor to the midpoint of sector\n\n        label.__hostTarget.textGuideLineConfig = {\n          anchor: new Point(linePoints[0][0], linePoints[0][1])\n        };\n      }\n    }\n  }\n}\n\nfunction getSectorCornerRadius(model, shape, zeroIfNull) {\n  var cornerRadius = model.get('borderRadius');\n\n  if (cornerRadius == null) {\n    return zeroIfNull ? {\n      cornerRadius: 0\n    } : null;\n  }\n\n  if (!isArray(cornerRadius)) {\n    cornerRadius = [cornerRadius, cornerRadius, cornerRadius, cornerRadius];\n  }\n\n  var dr = Math.abs(shape.r || 0 - shape.r0 || 0);\n  return {\n    cornerRadius: map(cornerRadius, function (cr) {\n      return parsePercent(cr, dr);\n    })\n  };\n}\n\n/**\n * Piece of pie including Sector, Label, LabelLine\n */\n\nvar PiePiece =\n/** @class */\nfunction (_super) {\n  __extends(PiePiece, _super);\n\n  function PiePiece(data, idx, startAngle) {\n    var _this = _super.call(this) || this;\n\n    _this.z2 = 2;\n    var text = new ZRText();\n\n    _this.setTextContent(text);\n\n    _this.updateData(data, idx, startAngle, true);\n\n    return _this;\n  }\n\n  PiePiece.prototype.updateData = function (data, idx, startAngle, firstCreate) {\n    var sector = this;\n    var seriesModel = data.hostModel;\n    var itemModel = data.getItemModel(idx);\n    var emphasisModel = itemModel.getModel('emphasis');\n    var layout = data.getItemLayout(idx); // cornerRadius & innerCornerRadius doesn't exist in the item layout. Use `0` if null value is specified.\n    // see `setItemLayout` in `pieLayout.ts`.\n\n    var sectorShape = extend(getSectorCornerRadius(itemModel.getModel('itemStyle'), layout, true), layout); // Ignore NaN data.\n\n    if (isNaN(sectorShape.startAngle)) {\n      // Use NaN shape to avoid drawing shape.\n      sector.setShape(sectorShape);\n      return;\n    }\n\n    if (firstCreate) {\n      sector.setShape(sectorShape);\n      var animationType = seriesModel.getShallow('animationType');\n\n      if (seriesModel.ecModel.ssr) {\n        // Use scale animation in SSR mode(opacity?)\n        // Because CSS SVG animation doesn't support very customized shape animation.\n        initProps(sector, {\n          scaleX: 0,\n          scaleY: 0\n        }, seriesModel, {\n          dataIndex: idx,\n          isFrom: true\n        });\n        sector.originX = sectorShape.cx;\n        sector.originY = sectorShape.cy;\n      } else if (animationType === 'scale') {\n        sector.shape.r = layout.r0;\n        initProps(sector, {\n          shape: {\n            r: layout.r\n          }\n        }, seriesModel, idx);\n      } // Expansion\n      else {\n          if (startAngle != null) {\n            sector.setShape({\n              startAngle: startAngle,\n              endAngle: startAngle\n            });\n            initProps(sector, {\n              shape: {\n                startAngle: layout.startAngle,\n                endAngle: layout.endAngle\n              }\n            }, seriesModel, idx);\n          } else {\n            sector.shape.endAngle = layout.startAngle;\n            updateProps(sector, {\n              shape: {\n                endAngle: layout.endAngle\n              }\n            }, seriesModel, idx);\n          }\n        }\n    } else {\n      saveOldStyle(sector); // Transition animation from the old shape\n\n      updateProps(sector, {\n        shape: sectorShape\n      }, seriesModel, idx);\n    }\n\n    sector.useStyle(data.getItemVisual(idx, 'style'));\n    setStatesStylesFromModel(sector, itemModel);\n    var midAngle = (layout.startAngle + layout.endAngle) / 2;\n    var offset = seriesModel.get('selectedOffset');\n    var dx = Math.cos(midAngle) * offset;\n    var dy = Math.sin(midAngle) * offset;\n    var cursorStyle = itemModel.getShallow('cursor');\n    cursorStyle && sector.attr('cursor', cursorStyle);\n\n    this._updateLabel(seriesModel, data, idx);\n\n    sector.ensureState('emphasis').shape = extend({\n      r: layout.r + (emphasisModel.get('scale') ? emphasisModel.get('scaleSize') || 0 : 0)\n    }, getSectorCornerRadius(emphasisModel.getModel('itemStyle'), layout));\n    extend(sector.ensureState('select'), {\n      x: dx,\n      y: dy,\n      shape: getSectorCornerRadius(itemModel.getModel(['select', 'itemStyle']), layout)\n    });\n    extend(sector.ensureState('blur'), {\n      shape: getSectorCornerRadius(itemModel.getModel(['blur', 'itemStyle']), layout)\n    });\n    var labelLine = sector.getTextGuideLine();\n    var labelText = sector.getTextContent();\n    labelLine && extend(labelLine.ensureState('select'), {\n      x: dx,\n      y: dy\n    }); // TODO: needs dx, dy in zrender?\n\n    extend(labelText.ensureState('select'), {\n      x: dx,\n      y: dy\n    });\n    toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n  };\n\n  PiePiece.prototype._updateLabel = function (seriesModel, data, idx) {\n    var sector = this;\n    var itemModel = data.getItemModel(idx);\n    var labelLineModel = itemModel.getModel('labelLine');\n    var style = data.getItemVisual(idx, 'style');\n    var visualColor = style && style.fill;\n    var visualOpacity = style && style.opacity;\n    setLabelStyle(sector, getLabelStatesModels(itemModel), {\n      labelFetcher: data.hostModel,\n      labelDataIndex: idx,\n      inheritColor: visualColor,\n      defaultOpacity: visualOpacity,\n      defaultText: seriesModel.getFormattedLabel(idx, 'normal') || data.getName(idx)\n    });\n    var labelText = sector.getTextContent(); // Set textConfig on sector.\n\n    sector.setTextConfig({\n      // reset position, rotation\n      position: null,\n      rotation: null\n    }); // Make sure update style on labelText after setLabelStyle.\n    // Because setLabelStyle will replace a new style on it.\n\n    labelText.attr({\n      z2: 10\n    });\n    var labelPosition = seriesModel.get(['label', 'position']);\n\n    if (labelPosition !== 'outside' && labelPosition !== 'outer') {\n      sector.removeTextGuideLine();\n    } else {\n      var polyline = this.getTextGuideLine();\n\n      if (!polyline) {\n        polyline = new Polyline();\n        this.setTextGuideLine(polyline);\n      } // Default use item visual color\n\n\n      setLabelLineStyle(this, getLabelLineStatesModels(itemModel), {\n        stroke: visualColor,\n        opacity: retrieve3(labelLineModel.get(['lineStyle', 'opacity']), visualOpacity, 1)\n      });\n    }\n  };\n\n  return PiePiece;\n}(Sector); // Pie view\n\n\nvar PieView =\n/** @class */\nfunction (_super) {\n  __extends(PieView, _super);\n\n  function PieView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.ignoreLabelLineUpdate = true;\n    return _this;\n  }\n\n  PieView.prototype.render = function (seriesModel, ecModel, api, payload) {\n    var data = seriesModel.getData();\n    var oldData = this._data;\n    var group = this.group;\n    var startAngle; // First render\n\n    if (!oldData && data.count() > 0) {\n      var shape = data.getItemLayout(0);\n\n      for (var s = 1; isNaN(shape && shape.startAngle) && s < data.count(); ++s) {\n        shape = data.getItemLayout(s);\n      }\n\n      if (shape) {\n        startAngle = shape.startAngle;\n      }\n    } // remove empty-circle if it exists\n\n\n    if (this._emptyCircleSector) {\n      group.remove(this._emptyCircleSector);\n    } // when all data are filtered, show lightgray empty circle\n\n\n    if (data.count() === 0 && seriesModel.get('showEmptyCircle')) {\n      var sector = new Sector({\n        shape: getBasicPieLayout(seriesModel, api)\n      });\n      sector.useStyle(seriesModel.getModel('emptyCircleStyle').getItemStyle());\n      this._emptyCircleSector = sector;\n      group.add(sector);\n    }\n\n    data.diff(oldData).add(function (idx) {\n      var piePiece = new PiePiece(data, idx, startAngle);\n      data.setItemGraphicEl(idx, piePiece);\n      group.add(piePiece);\n    }).update(function (newIdx, oldIdx) {\n      var piePiece = oldData.getItemGraphicEl(oldIdx);\n      piePiece.updateData(data, newIdx, startAngle);\n      piePiece.off('click');\n      group.add(piePiece);\n      data.setItemGraphicEl(newIdx, piePiece);\n    }).remove(function (idx) {\n      var piePiece = oldData.getItemGraphicEl(idx);\n      removeElementWithFadeOut(piePiece, seriesModel, idx);\n    }).execute();\n    pieLabelLayout(seriesModel); // Always use initial animation.\n\n    if (seriesModel.get('animationTypeUpdate') !== 'expansion') {\n      this._data = data;\n    }\n  };\n\n  PieView.prototype.dispose = function () {};\n\n  PieView.prototype.containPoint = function (point, seriesModel) {\n    var data = seriesModel.getData();\n    var itemLayout = data.getItemLayout(0);\n\n    if (itemLayout) {\n      var dx = point[0] - itemLayout.cx;\n      var dy = point[1] - itemLayout.cy;\n      var radius = Math.sqrt(dx * dx + dy * dy);\n      return radius <= itemLayout.r && radius >= itemLayout.r0;\n    }\n  };\n\n  PieView.type = 'pie';\n  return PieView;\n}(ChartView);\n\n/**\n * [Usage]:\n * (1)\n * createListSimply(seriesModel, ['value']);\n * (2)\n * createListSimply(seriesModel, {\n *     coordDimensions: ['value'],\n *     dimensionsCount: 5\n * });\n */\n\nfunction createSeriesDataSimply(seriesModel, opt, nameList) {\n  opt = isArray(opt) && {\n    coordDimensions: opt\n  } || extend({\n    encodeDefine: seriesModel.getEncode()\n  }, opt);\n  var source = seriesModel.getSource();\n  var dimensions = prepareSeriesDataSchema(source, opt).dimensions;\n  var list = new SeriesData(dimensions, seriesModel);\n  list.initData(source, nameList);\n  return list;\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n/**\n * LegendVisualProvider is an bridge that pick encoded color from data and\n * provide to the legend component.\n */\nvar LegendVisualProvider =\n/** @class */\nfunction () {\n  function LegendVisualProvider( // Function to get data after filtered. It stores all the encoding info\n  getDataWithEncodedVisual, // Function to get raw data before filtered.\n  getRawData) {\n    this._getDataWithEncodedVisual = getDataWithEncodedVisual;\n    this._getRawData = getRawData;\n  }\n\n  LegendVisualProvider.prototype.getAllNames = function () {\n    var rawData = this._getRawData(); // We find the name from the raw data. In case it's filtered by the legend component.\n    // Normally, the name can be found in rawData, but can't be found in filtered data will display as gray.\n\n\n    return rawData.mapArray(rawData.getName);\n  };\n\n  LegendVisualProvider.prototype.containName = function (name) {\n    var rawData = this._getRawData();\n\n    return rawData.indexOfName(name) >= 0;\n  };\n\n  LegendVisualProvider.prototype.indexOfName = function (name) {\n    // Only get data when necessary.\n    // Because LegendVisualProvider constructor may be new in the stage that data is not prepared yet.\n    // Invoking Series#getData immediately will throw an error.\n    var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n    return dataWithEncodedVisual.indexOfName(name);\n  };\n\n  LegendVisualProvider.prototype.getItemVisual = function (dataIndex, key) {\n    // Get encoded visual properties from final filtered data.\n    var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n    return dataWithEncodedVisual.getItemVisual(dataIndex, key);\n  };\n\n  return LegendVisualProvider;\n}();\n\nvar innerData = makeInner();\n\nvar PieSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(PieSeriesModel, _super);\n\n  function PieSeriesModel() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n  /**\n   * @overwrite\n   */\n\n\n  PieSeriesModel.prototype.init = function (option) {\n    _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n    // Use a function instead of direct access because data reference may changed\n\n\n    this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n\n    this._defaultLabelLine(option);\n  };\n  /**\n   * @overwrite\n   */\n\n\n  PieSeriesModel.prototype.mergeOption = function () {\n    _super.prototype.mergeOption.apply(this, arguments);\n  };\n  /**\n   * @overwrite\n   */\n\n\n  PieSeriesModel.prototype.getInitialData = function () {\n    return createSeriesDataSimply(this, {\n      coordDimensions: ['value'],\n      encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n    });\n  };\n  /**\n   * @overwrite\n   */\n\n\n  PieSeriesModel.prototype.getDataParams = function (dataIndex) {\n    var data = this.getData(); // update seats when data is changed\n\n    var dataInner = innerData(data);\n    var seats = dataInner.seats;\n\n    if (!seats) {\n      var valueList_1 = [];\n      data.each(data.mapDimension('value'), function (value) {\n        valueList_1.push(value);\n      });\n      seats = dataInner.seats = getPercentSeats(valueList_1, data.hostModel.get('percentPrecision'));\n    }\n\n    var params = _super.prototype.getDataParams.call(this, dataIndex); // seats may be empty when sum is 0\n\n\n    params.percent = seats[dataIndex] || 0;\n    params.$vars.push('percent');\n    return params;\n  };\n\n  PieSeriesModel.prototype._defaultLabelLine = function (option) {\n    // Extend labelLine emphasis\n    defaultEmphasis(option, 'labelLine', ['show']);\n    var labelLineNormalOpt = option.labelLine;\n    var labelLineEmphasisOpt = option.emphasis.labelLine; // Not show label line if `label.normal.show = false`\n\n    labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show;\n    labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show;\n  };\n\n  PieSeriesModel.type = 'series.pie';\n  PieSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    legendHoverLink: true,\n    colorBy: 'data',\n    // 默认全局居中\n    center: ['50%', '50%'],\n    radius: [0, '75%'],\n    // 默认顺时针\n    clockwise: true,\n    startAngle: 90,\n    // 最小角度改为0\n    minAngle: 0,\n    // If the angle of a sector less than `minShowLabelAngle`,\n    // the label will not be displayed.\n    minShowLabelAngle: 0,\n    // 选中时扇区偏移量\n    selectedOffset: 10,\n    // 选择模式，默认关闭，可选single，multiple\n    // selectedMode: false,\n    // 南丁格尔玫瑰图模式，'radius'（半径） | 'area'（面积）\n    // roseType: null,\n    percentPrecision: 2,\n    // If still show when all data zero.\n    stillShowZeroSum: true,\n    // cursor: null,\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0,\n    width: null,\n    height: null,\n    label: {\n      // color: 'inherit',\n      // If rotate around circle\n      rotate: 0,\n      show: true,\n      overflow: 'truncate',\n      // 'outer', 'inside', 'center'\n      position: 'outer',\n      // 'none', 'labelLine', 'edge'. Works only when position is 'outer'\n      alignTo: 'none',\n      // Closest distance between label and chart edge.\n      // Works only position is 'outer' and alignTo is 'edge'.\n      edgeDistance: '25%',\n      // Works only position is 'outer' and alignTo is not 'edge'.\n      bleedMargin: 10,\n      // Distance between text and label line.\n      distanceToLabelLine: 5 // formatter: 标签文本格式器，同 tooltip.formatter，不支持异步回调\n      // 默认使用全局文本样式，详见 textStyle\n      // distance: 当position为inner时有效，为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数\n\n    },\n    // Enabled when label.normal.position is 'outer'\n    labelLine: {\n      show: true,\n      // 引导线两段中的第一段长度\n      length: 15,\n      // 引导线两段中的第二段长度\n      length2: 15,\n      smooth: false,\n      minTurnAngle: 90,\n      maxSurfaceAngle: 90,\n      lineStyle: {\n        // color: 各异,\n        width: 1,\n        type: 'solid'\n      }\n    },\n    itemStyle: {\n      borderWidth: 1,\n      borderJoin: 'round'\n    },\n    showEmptyCircle: true,\n    emptyCircleStyle: {\n      color: 'lightgray',\n      opacity: 1\n    },\n    labelLayout: {\n      // Hide the overlapped label.\n      hideOverlap: true\n    },\n    emphasis: {\n      scale: true,\n      scaleSize: 5\n    },\n    // If use strategy to avoid label overlapping\n    avoidLabelOverlap: true,\n    // Animation type. Valid values: expansion, scale\n    animationType: 'expansion',\n    animationDuration: 1000,\n    // Animation type when update. Valid values: transition, expansion\n    animationTypeUpdate: 'transition',\n    animationEasingUpdate: 'cubicInOut',\n    animationDurationUpdate: 500,\n    animationEasing: 'cubicInOut'\n  };\n  return PieSeriesModel;\n}(SeriesModel);\n\nfunction negativeDataFilter(seriesType) {\n  return {\n    seriesType: seriesType,\n    reset: function (seriesModel, ecModel) {\n      var data = seriesModel.getData();\n      data.filterSelf(function (idx) {\n        // handle negative value condition\n        var valueDim = data.mapDimension('value');\n        var curValue = data.get(valueDim, idx);\n\n        if (isNumber(curValue) && !isNaN(curValue) && curValue < 0) {\n          return false;\n        }\n\n        return true;\n      });\n    }\n  };\n}\n\nfunction install$4(registers) {\n  registers.registerChartView(PieView);\n  registers.registerSeriesModel(PieSeriesModel);\n  createLegacyDataSelectAction('pie', registers.registerAction);\n  registers.registerLayout(curry(pieLayout, 'pie'));\n  registers.registerProcessor(dataFilter('pie'));\n  registers.registerProcessor(negativeDataFilter('pie'));\n}\n\nvar ScatterSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(ScatterSeriesModel, _super);\n\n  function ScatterSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ScatterSeriesModel.type;\n    _this.hasSymbolVisual = true;\n    return _this;\n  }\n\n  ScatterSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesData(null, this, {\n      useEncodeDefaulter: true\n    });\n  };\n\n  ScatterSeriesModel.prototype.getProgressive = function () {\n    var progressive = this.option.progressive;\n\n    if (progressive == null) {\n      // PENDING\n      return this.option.large ? 5e3 : this.get('progressive');\n    }\n\n    return progressive;\n  };\n\n  ScatterSeriesModel.prototype.getProgressiveThreshold = function () {\n    var progressiveThreshold = this.option.progressiveThreshold;\n\n    if (progressiveThreshold == null) {\n      // PENDING\n      return this.option.large ? 1e4 : this.get('progressiveThreshold');\n    }\n\n    return progressiveThreshold;\n  };\n\n  ScatterSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n    return selectors.point(data.getItemLayout(dataIndex));\n  };\n\n  ScatterSeriesModel.prototype.getZLevelKey = function () {\n    // Each progressive series has individual key.\n    return this.getData().count() > this.getProgressiveThreshold() ? this.id : '';\n  };\n\n  ScatterSeriesModel.type = 'series.scatter';\n  ScatterSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];\n  ScatterSeriesModel.defaultOption = {\n    coordinateSystem: 'cartesian2d',\n    // zlevel: 0,\n    z: 2,\n    legendHoverLink: true,\n    symbolSize: 10,\n    // symbolRotate: null,  // 图形旋转控制\n    large: false,\n    // Available when large is true\n    largeThreshold: 2000,\n    // cursor: null,\n    itemStyle: {\n      opacity: 0.8 // color: 各异\n\n    },\n    emphasis: {\n      scale: true\n    },\n    // If clip the overflow graphics\n    // Works on cartesian / polar series\n    clip: true,\n    select: {\n      itemStyle: {\n        borderColor: '#212121'\n      }\n    },\n    universalTransition: {\n      divideShape: 'clone'\n    } // progressive: null\n\n  };\n  return ScatterSeriesModel;\n}(SeriesModel);\n\nvar BOOST_SIZE_THRESHOLD = 4;\n\nvar LargeSymbolPathShape =\n/** @class */\nfunction () {\n  function LargeSymbolPathShape() {}\n\n  return LargeSymbolPathShape;\n}();\n\nvar LargeSymbolPath =\n/** @class */\nfunction (_super) {\n  __extends(LargeSymbolPath, _super);\n\n  function LargeSymbolPath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this._off = 0;\n    _this.hoverDataIdx = -1;\n    return _this;\n  }\n\n  LargeSymbolPath.prototype.getDefaultShape = function () {\n    return new LargeSymbolPathShape();\n  };\n\n  LargeSymbolPath.prototype.reset = function () {\n    this.notClear = false;\n    this._off = 0;\n  };\n\n  LargeSymbolPath.prototype.buildPath = function (path, shape) {\n    var points = shape.points;\n    var size = shape.size;\n    var symbolProxy = this.symbolProxy;\n    var symbolProxyShape = symbolProxy.shape;\n    var ctx = path.getContext ? path.getContext() : path;\n    var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD;\n    var softClipShape = this.softClipShape;\n    var i; // Do draw in afterBrush.\n\n    if (canBoost) {\n      this._ctx = ctx;\n      return;\n    }\n\n    this._ctx = null;\n\n    for (i = this._off; i < points.length;) {\n      var x = points[i++];\n      var y = points[i++];\n\n      if (isNaN(x) || isNaN(y)) {\n        continue;\n      }\n\n      if (softClipShape && !softClipShape.contain(x, y)) {\n        continue;\n      }\n\n      symbolProxyShape.x = x - size[0] / 2;\n      symbolProxyShape.y = y - size[1] / 2;\n      symbolProxyShape.width = size[0];\n      symbolProxyShape.height = size[1];\n      symbolProxy.buildPath(path, symbolProxyShape, true);\n    }\n\n    if (this.incremental) {\n      this._off = i;\n      this.notClear = true;\n    }\n  };\n\n  LargeSymbolPath.prototype.afterBrush = function () {\n    var shape = this.shape;\n    var points = shape.points;\n    var size = shape.size;\n    var ctx = this._ctx;\n    var softClipShape = this.softClipShape;\n    var i;\n\n    if (!ctx) {\n      return;\n    } // PENDING If style or other canvas status changed?\n\n\n    for (i = this._off; i < points.length;) {\n      var x = points[i++];\n      var y = points[i++];\n\n      if (isNaN(x) || isNaN(y)) {\n        continue;\n      }\n\n      if (softClipShape && !softClipShape.contain(x, y)) {\n        continue;\n      } // fillRect is faster than building a rect path and draw.\n      // And it support light globalCompositeOperation.\n\n\n      ctx.fillRect(x - size[0] / 2, y - size[1] / 2, size[0], size[1]);\n    }\n\n    if (this.incremental) {\n      this._off = i;\n      this.notClear = true;\n    }\n  };\n\n  LargeSymbolPath.prototype.findDataIndex = function (x, y) {\n    // TODO ???\n    // Consider transform\n    var shape = this.shape;\n    var points = shape.points;\n    var size = shape.size;\n    var w = Math.max(size[0], 4);\n    var h = Math.max(size[1], 4); // Not consider transform\n    // Treat each element as a rect\n    // top down traverse\n\n    for (var idx = points.length / 2 - 1; idx >= 0; idx--) {\n      var i = idx * 2;\n      var x0 = points[i] - w / 2;\n      var y0 = points[i + 1] - h / 2;\n\n      if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) {\n        return idx;\n      }\n    }\n\n    return -1;\n  };\n\n  LargeSymbolPath.prototype.contain = function (x, y) {\n    var localPos = this.transformCoordToLocal(x, y);\n    var rect = this.getBoundingRect();\n    x = localPos[0];\n    y = localPos[1];\n\n    if (rect.contain(x, y)) {\n      // Cache found data index.\n      var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y);\n      return dataIdx >= 0;\n    }\n\n    this.hoverDataIdx = -1;\n    return false;\n  };\n\n  LargeSymbolPath.prototype.getBoundingRect = function () {\n    // Ignore stroke for large symbol draw.\n    var rect = this._rect;\n\n    if (!rect) {\n      var shape = this.shape;\n      var points = shape.points;\n      var size = shape.size;\n      var w = size[0];\n      var h = size[1];\n      var minX = Infinity;\n      var minY = Infinity;\n      var maxX = -Infinity;\n      var maxY = -Infinity;\n\n      for (var i = 0; i < points.length;) {\n        var x = points[i++];\n        var y = points[i++];\n        minX = Math.min(x, minX);\n        maxX = Math.max(x, maxX);\n        minY = Math.min(y, minY);\n        maxY = Math.max(y, maxY);\n      }\n\n      rect = this._rect = new BoundingRect(minX - w / 2, minY - h / 2, maxX - minX + w, maxY - minY + h);\n    }\n\n    return rect;\n  };\n\n  return LargeSymbolPath;\n}(Path);\n\nvar LargeSymbolDraw =\n/** @class */\nfunction () {\n  function LargeSymbolDraw() {\n    this.group = new Group();\n  }\n  /**\n   * Update symbols draw by new data\n   */\n\n\n  LargeSymbolDraw.prototype.updateData = function (data, opt) {\n    this._clear();\n\n    var symbolEl = this._create();\n\n    symbolEl.setShape({\n      points: data.getLayout('points')\n    });\n\n    this._setCommon(symbolEl, data, opt);\n  };\n\n  LargeSymbolDraw.prototype.updateLayout = function (data) {\n    var points = data.getLayout('points');\n    this.group.eachChild(function (child) {\n      if (child.startIndex != null) {\n        var len = (child.endIndex - child.startIndex) * 2;\n        var byteOffset = child.startIndex * 4 * 2;\n        points = new Float32Array(points.buffer, byteOffset, len);\n      }\n\n      child.setShape('points', points); // Reset draw cursor.\n\n      child.reset();\n    });\n  };\n\n  LargeSymbolDraw.prototype.incrementalPrepareUpdate = function (data) {\n    this._clear();\n  };\n\n  LargeSymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {\n    var lastAdded = this._newAdded[0];\n    var points = data.getLayout('points');\n    var oldPoints = lastAdded && lastAdded.shape.points; // Merging the exists. Each element has 1e4 points.\n    // Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization)\n\n    if (oldPoints && oldPoints.length < 2e4) {\n      var oldLen = oldPoints.length;\n      var newPoints = new Float32Array(oldLen + points.length); // Concat two array\n\n      newPoints.set(oldPoints);\n      newPoints.set(points, oldLen); // Update endIndex\n\n      lastAdded.endIndex = taskParams.end;\n      lastAdded.setShape({\n        points: newPoints\n      });\n    } else {\n      // Clear\n      this._newAdded = [];\n\n      var symbolEl = this._create();\n\n      symbolEl.startIndex = taskParams.start;\n      symbolEl.endIndex = taskParams.end;\n      symbolEl.incremental = true;\n      symbolEl.setShape({\n        points: points\n      });\n\n      this._setCommon(symbolEl, data, opt);\n    }\n  };\n\n  LargeSymbolDraw.prototype.eachRendered = function (cb) {\n    this._newAdded[0] && cb(this._newAdded[0]);\n  };\n\n  LargeSymbolDraw.prototype._create = function () {\n    var symbolEl = new LargeSymbolPath({\n      cursor: 'default'\n    });\n    symbolEl.ignoreCoarsePointer = true;\n    this.group.add(symbolEl);\n\n    this._newAdded.push(symbolEl);\n\n    return symbolEl;\n  };\n\n  LargeSymbolDraw.prototype._setCommon = function (symbolEl, data, opt) {\n    var hostModel = data.hostModel;\n    opt = opt || {};\n    var size = data.getVisual('symbolSize');\n    symbolEl.setShape('size', size instanceof Array ? size : [size, size]);\n    symbolEl.softClipShape = opt.clipShape || null; // Create symbolProxy to build path for each data\n\n    symbolEl.symbolProxy = createSymbol(data.getVisual('symbol'), 0, 0, 0, 0); // Use symbolProxy setColor method\n\n    symbolEl.setColor = symbolEl.symbolProxy.setColor;\n    var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD;\n    symbolEl.useStyle( // Draw shadow when doing fillRect is extremely slow.\n    hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color']));\n    var globalStyle = data.getVisual('style');\n    var visualColor = globalStyle && globalStyle.fill;\n\n    if (visualColor) {\n      symbolEl.setColor(visualColor);\n    }\n\n    var ecData = getECData(symbolEl); // Enable tooltip\n    // PENDING May have performance issue when path is extremely large\n\n    ecData.seriesIndex = hostModel.seriesIndex;\n    symbolEl.on('mousemove', function (e) {\n      ecData.dataIndex = null;\n      var dataIndex = symbolEl.hoverDataIdx;\n\n      if (dataIndex >= 0) {\n        // Provide dataIndex for tooltip\n        ecData.dataIndex = dataIndex + (symbolEl.startIndex || 0);\n      }\n    });\n  };\n\n  LargeSymbolDraw.prototype.remove = function () {\n    this._clear();\n  };\n\n  LargeSymbolDraw.prototype._clear = function () {\n    this._newAdded = [];\n    this.group.removeAll();\n  };\n\n  return LargeSymbolDraw;\n}();\n\nvar ScatterView =\n/** @class */\nfunction (_super) {\n  __extends(ScatterView, _super);\n\n  function ScatterView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ScatterView.type;\n    return _this;\n  }\n\n  ScatterView.prototype.render = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n\n    var symbolDraw = this._updateSymbolDraw(data, seriesModel);\n\n    symbolDraw.updateData(data, {\n      // TODO\n      // If this parameter should be a shape or a bounding volume\n      // shape will be more general.\n      // But bounding volume like bounding rect will be much faster in the contain calculation\n      clipShape: this._getClipShape(seriesModel)\n    });\n    this._finished = true;\n  };\n\n  ScatterView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n\n    var symbolDraw = this._updateSymbolDraw(data, seriesModel);\n\n    symbolDraw.incrementalPrepareUpdate(data);\n    this._finished = false;\n  };\n\n  ScatterView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) {\n    this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData(), {\n      clipShape: this._getClipShape(seriesModel)\n    });\n\n    this._finished = taskParams.end === seriesModel.getData().count();\n  };\n\n  ScatterView.prototype.updateTransform = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData(); // Must mark group dirty and make sure the incremental layer will be cleared\n    // PENDING\n\n    this.group.dirty();\n\n    if (!this._finished || data.count() > 1e4) {\n      return {\n        update: true\n      };\n    } else {\n      var res = pointsLayout('').reset(seriesModel, ecModel, api);\n\n      if (res.progress) {\n        res.progress({\n          start: 0,\n          end: data.count(),\n          count: data.count()\n        }, data);\n      }\n\n      this._symbolDraw.updateLayout(data);\n    }\n  };\n\n  ScatterView.prototype.eachRendered = function (cb) {\n    this._symbolDraw && this._symbolDraw.eachRendered(cb);\n  };\n\n  ScatterView.prototype._getClipShape = function (seriesModel) {\n    var coordSys = seriesModel.coordinateSystem;\n    var clipArea = coordSys && coordSys.getArea && coordSys.getArea();\n    return seriesModel.get('clip', true) ? clipArea : null;\n  };\n\n  ScatterView.prototype._updateSymbolDraw = function (data, seriesModel) {\n    var symbolDraw = this._symbolDraw;\n    var pipelineContext = seriesModel.pipelineContext;\n    var isLargeDraw = pipelineContext.large;\n\n    if (!symbolDraw || isLargeDraw !== this._isLargeDraw) {\n      symbolDraw && symbolDraw.remove();\n      symbolDraw = this._symbolDraw = isLargeDraw ? new LargeSymbolDraw() : new SymbolDraw();\n      this._isLargeDraw = isLargeDraw;\n      this.group.removeAll();\n    }\n\n    this.group.add(symbolDraw.group);\n    return symbolDraw;\n  };\n\n  ScatterView.prototype.remove = function (ecModel, api) {\n    this._symbolDraw && this._symbolDraw.remove(true);\n    this._symbolDraw = null;\n  };\n\n  ScatterView.prototype.dispose = function () {};\n\n  ScatterView.type = 'scatter';\n  return ScatterView;\n}(ChartView);\n\nvar GridModel =\n/** @class */\nfunction (_super) {\n  __extends(GridModel, _super);\n\n  function GridModel() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  GridModel.type = 'grid';\n  GridModel.dependencies = ['xAxis', 'yAxis'];\n  GridModel.layoutMode = 'box';\n  GridModel.defaultOption = {\n    show: false,\n    // zlevel: 0,\n    z: 0,\n    left: '10%',\n    top: 60,\n    right: '10%',\n    bottom: 70,\n    // If grid size contain label\n    containLabel: false,\n    // width: {totalWidth} - left - right,\n    // height: {totalHeight} - top - bottom,\n    backgroundColor: 'rgba(0,0,0,0)',\n    borderWidth: 1,\n    borderColor: '#ccc'\n  };\n  return GridModel;\n}(ComponentModel);\n\nvar CartesianAxisModel =\n/** @class */\nfunction (_super) {\n  __extends(CartesianAxisModel, _super);\n\n  function CartesianAxisModel() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  CartesianAxisModel.prototype.getCoordSysModel = function () {\n    return this.getReferringComponents('grid', SINGLE_REFERRING).models[0];\n  };\n\n  CartesianAxisModel.type = 'cartesian2dAxis';\n  return CartesianAxisModel;\n}(ComponentModel);\nmixin(CartesianAxisModel, AxisModelCommonMixin);\n\nvar defaultOption = {\n  show: true,\n  // zlevel: 0,\n  z: 0,\n  // Inverse the axis.\n  inverse: false,\n  // Axis name displayed.\n  name: '',\n  // 'start' | 'middle' | 'end'\n  nameLocation: 'end',\n  // By degree. By default auto rotate by nameLocation.\n  nameRotate: null,\n  nameTruncate: {\n    maxWidth: null,\n    ellipsis: '...',\n    placeholder: '.'\n  },\n  // Use global text style by default.\n  nameTextStyle: {},\n  // The gap between axisName and axisLine.\n  nameGap: 15,\n  // Default `false` to support tooltip.\n  silent: false,\n  // Default `false` to avoid legacy user event listener fail.\n  triggerEvent: false,\n  tooltip: {\n    show: false\n  },\n  axisPointer: {},\n  axisLine: {\n    show: true,\n    onZero: true,\n    onZeroAxisIndex: null,\n    lineStyle: {\n      color: '#6E7079',\n      width: 1,\n      type: 'solid'\n    },\n    // The arrow at both ends the the axis.\n    symbol: ['none', 'none'],\n    symbolSize: [10, 15]\n  },\n  axisTick: {\n    show: true,\n    // Whether axisTick is inside the grid or outside the grid.\n    inside: false,\n    // The length of axisTick.\n    length: 5,\n    lineStyle: {\n      width: 1\n    }\n  },\n  axisLabel: {\n    show: true,\n    // Whether axisLabel is inside the grid or outside the grid.\n    inside: false,\n    rotate: 0,\n    // true | false | null/undefined (auto)\n    showMinLabel: null,\n    // true | false | null/undefined (auto)\n    showMaxLabel: null,\n    margin: 8,\n    // formatter: null,\n    fontSize: 12\n  },\n  splitLine: {\n    show: true,\n    lineStyle: {\n      color: ['#E0E6F1'],\n      width: 1,\n      type: 'solid'\n    }\n  },\n  splitArea: {\n    show: false,\n    areaStyle: {\n      color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)']\n    }\n  }\n};\nvar categoryAxis = merge({\n  // The gap at both ends of the axis. For categoryAxis, boolean.\n  boundaryGap: true,\n  // Set false to faster category collection.\n  deduplication: null,\n  // splitArea: {\n  // show: false\n  // },\n  splitLine: {\n    show: false\n  },\n  axisTick: {\n    // If tick is align with label when boundaryGap is true\n    alignWithLabel: false,\n    interval: 'auto'\n  },\n  axisLabel: {\n    interval: 'auto'\n  }\n}, defaultOption);\nvar valueAxis = merge({\n  boundaryGap: [0, 0],\n  axisLine: {\n    // Not shown when other axis is categoryAxis in cartesian\n    show: 'auto'\n  },\n  axisTick: {\n    // Not shown when other axis is categoryAxis in cartesian\n    show: 'auto'\n  },\n  // TODO\n  // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]\n  splitNumber: 5,\n  minorTick: {\n    // Minor tick, not available for cateogry axis.\n    show: false,\n    // Split number of minor ticks. The value should be in range of (0, 100)\n    splitNumber: 5,\n    // Length of minor tick\n    length: 3,\n    // Line style\n    lineStyle: {// Default to be same with axisTick\n    }\n  },\n  minorSplitLine: {\n    show: false,\n    lineStyle: {\n      color: '#F4F7FD',\n      width: 1\n    }\n  }\n}, defaultOption);\nvar timeAxis = merge({\n  splitNumber: 6,\n  axisLabel: {\n    // To eliminate labels that are not nice\n    showMinLabel: false,\n    showMaxLabel: false,\n    rich: {\n      primary: {\n        fontWeight: 'bold'\n      }\n    }\n  },\n  splitLine: {\n    show: false\n  }\n}, valueAxis);\nvar logAxis = defaults({\n  logBase: 10\n}, valueAxis);\nvar axisDefault = {\n  category: categoryAxis,\n  value: valueAxis,\n  time: timeAxis,\n  log: logAxis\n};\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar AXIS_TYPES = {\n  value: 1,\n  category: 1,\n  time: 1,\n  log: 1\n};\n\n/**\n * Generate sub axis model class\n * @param axisName 'x' 'y' 'radius' 'angle' 'parallel' ...\n */\n\nfunction axisModelCreator(registers, axisName, BaseAxisModelClass, extraDefaultOption) {\n  each(AXIS_TYPES, function (v, axisType) {\n    var defaultOption = merge(merge({}, axisDefault[axisType], true), extraDefaultOption, true);\n\n    var AxisModel =\n    /** @class */\n    function (_super) {\n      __extends(AxisModel, _super);\n\n      function AxisModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = axisName + 'Axis.' + axisType;\n        return _this;\n      }\n\n      AxisModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n        var layoutMode = fetchLayoutMode(this);\n        var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n        var themeModel = ecModel.getTheme();\n        merge(option, themeModel.get(axisType + 'Axis'));\n        merge(option, this.getDefaultOption());\n        option.type = getAxisType(option);\n\n        if (layoutMode) {\n          mergeLayoutParam(option, inputPositionParams, layoutMode);\n        }\n      };\n\n      AxisModel.prototype.optionUpdated = function () {\n        var thisOption = this.option;\n\n        if (thisOption.type === 'category') {\n          this.__ordinalMeta = OrdinalMeta.createByAxisModel(this);\n        }\n      };\n      /**\n       * Should not be called before all of 'getInitailData' finished.\n       * Because categories are collected during initializing data.\n       */\n\n\n      AxisModel.prototype.getCategories = function (rawData) {\n        var option = this.option; // FIXME\n        // warning if called before all of 'getInitailData' finished.\n\n        if (option.type === 'category') {\n          if (rawData) {\n            return option.data;\n          }\n\n          return this.__ordinalMeta.categories;\n        }\n      };\n\n      AxisModel.prototype.getOrdinalMeta = function () {\n        return this.__ordinalMeta;\n      };\n\n      AxisModel.type = axisName + 'Axis.' + axisType;\n      AxisModel.defaultOption = defaultOption;\n      return AxisModel;\n    }(BaseAxisModelClass);\n\n    registers.registerComponentModel(AxisModel);\n  });\n  registers.registerSubTypeDefaulter(axisName + 'Axis', getAxisType);\n}\n\nfunction getAxisType(option) {\n  // Default axis with data is category axis\n  return option.type || (option.data ? 'category' : 'value');\n}\n\nvar Cartesian =\n/** @class */\nfunction () {\n  function Cartesian(name) {\n    this.type = 'cartesian';\n    this._dimList = [];\n    this._axes = {};\n    this.name = name || '';\n  }\n\n  Cartesian.prototype.getAxis = function (dim) {\n    return this._axes[dim];\n  };\n\n  Cartesian.prototype.getAxes = function () {\n    return map(this._dimList, function (dim) {\n      return this._axes[dim];\n    }, this);\n  };\n\n  Cartesian.prototype.getAxesByScale = function (scaleType) {\n    scaleType = scaleType.toLowerCase();\n    return filter(this.getAxes(), function (axis) {\n      return axis.scale.type === scaleType;\n    });\n  };\n\n  Cartesian.prototype.addAxis = function (axis) {\n    var dim = axis.dim;\n    this._axes[dim] = axis;\n\n    this._dimList.push(dim);\n  };\n\n  return Cartesian;\n}();\n\nvar cartesian2DDimensions = ['x', 'y'];\n\nfunction canCalculateAffineTransform(scale) {\n  return scale.type === 'interval' || scale.type === 'time';\n}\n\nvar Cartesian2D =\n/** @class */\nfunction (_super) {\n  __extends(Cartesian2D, _super);\n\n  function Cartesian2D() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = 'cartesian2d';\n    _this.dimensions = cartesian2DDimensions;\n    return _this;\n  }\n  /**\n   * Calculate an affine transform matrix if two axes are time or value.\n   * It's mainly for accelartion on the large time series data.\n   */\n\n\n  Cartesian2D.prototype.calcAffineTransform = function () {\n    this._transform = this._invTransform = null;\n    var xAxisScale = this.getAxis('x').scale;\n    var yAxisScale = this.getAxis('y').scale;\n\n    if (!canCalculateAffineTransform(xAxisScale) || !canCalculateAffineTransform(yAxisScale)) {\n      return;\n    }\n\n    var xScaleExtent = xAxisScale.getExtent();\n    var yScaleExtent = yAxisScale.getExtent();\n    var start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]]);\n    var end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]]);\n    var xScaleSpan = xScaleExtent[1] - xScaleExtent[0];\n    var yScaleSpan = yScaleExtent[1] - yScaleExtent[0];\n\n    if (!xScaleSpan || !yScaleSpan) {\n      return;\n    } // Accelerate data to point calculation on the special large time series data.\n\n\n    var scaleX = (end[0] - start[0]) / xScaleSpan;\n    var scaleY = (end[1] - start[1]) / yScaleSpan;\n    var translateX = start[0] - xScaleExtent[0] * scaleX;\n    var translateY = start[1] - yScaleExtent[0] * scaleY;\n    var m = this._transform = [scaleX, 0, 0, scaleY, translateX, translateY];\n    this._invTransform = invert([], m);\n  };\n  /**\n   * Base axis will be used on stacking.\n   */\n\n\n  Cartesian2D.prototype.getBaseAxis = function () {\n    return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAxis('x');\n  };\n\n  Cartesian2D.prototype.containPoint = function (point) {\n    var axisX = this.getAxis('x');\n    var axisY = this.getAxis('y');\n    return axisX.contain(axisX.toLocalCoord(point[0])) && axisY.contain(axisY.toLocalCoord(point[1]));\n  };\n\n  Cartesian2D.prototype.containData = function (data) {\n    return this.getAxis('x').containData(data[0]) && this.getAxis('y').containData(data[1]);\n  };\n\n  Cartesian2D.prototype.containZone = function (data1, data2) {\n    var zoneDiag1 = this.dataToPoint(data1);\n    var zoneDiag2 = this.dataToPoint(data2);\n    var area = this.getArea();\n    var zone = new BoundingRect(zoneDiag1[0], zoneDiag1[1], zoneDiag2[0] - zoneDiag1[0], zoneDiag2[1] - zoneDiag1[1]);\n    return area.intersect(zone);\n  };\n\n  Cartesian2D.prototype.dataToPoint = function (data, clamp, out) {\n    out = out || [];\n    var xVal = data[0];\n    var yVal = data[1]; // Fast path\n\n    if (this._transform // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated.\n    && xVal != null && isFinite(xVal) && yVal != null && isFinite(yVal)) {\n      return applyTransform(out, data, this._transform);\n    }\n\n    var xAxis = this.getAxis('x');\n    var yAxis = this.getAxis('y');\n    out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal, clamp));\n    out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal, clamp));\n    return out;\n  };\n\n  Cartesian2D.prototype.clampData = function (data, out) {\n    var xScale = this.getAxis('x').scale;\n    var yScale = this.getAxis('y').scale;\n    var xAxisExtent = xScale.getExtent();\n    var yAxisExtent = yScale.getExtent();\n    var x = xScale.parse(data[0]);\n    var y = yScale.parse(data[1]);\n    out = out || [];\n    out[0] = Math.min(Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), Math.max(xAxisExtent[0], xAxisExtent[1]));\n    out[1] = Math.min(Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), Math.max(yAxisExtent[0], yAxisExtent[1]));\n    return out;\n  };\n\n  Cartesian2D.prototype.pointToData = function (point, clamp) {\n    var out = [];\n\n    if (this._invTransform) {\n      return applyTransform(out, point, this._invTransform);\n    }\n\n    var xAxis = this.getAxis('x');\n    var yAxis = this.getAxis('y');\n    out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp);\n    out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp);\n    return out;\n  };\n\n  Cartesian2D.prototype.getOtherAxis = function (axis) {\n    return this.getAxis(axis.dim === 'x' ? 'y' : 'x');\n  };\n  /**\n   * Get rect area of cartesian.\n   * Area will have a contain function to determine if a point is in the coordinate system.\n   */\n\n\n  Cartesian2D.prototype.getArea = function () {\n    var xExtent = this.getAxis('x').getGlobalExtent();\n    var yExtent = this.getAxis('y').getGlobalExtent();\n    var x = Math.min(xExtent[0], xExtent[1]);\n    var y = Math.min(yExtent[0], yExtent[1]);\n    var width = Math.max(xExtent[0], xExtent[1]) - x;\n    var height = Math.max(yExtent[0], yExtent[1]) - y;\n    return new BoundingRect(x, y, width, height);\n  };\n\n  return Cartesian2D;\n}(Cartesian);\n\nvar Axis2D =\n/** @class */\nfunction (_super) {\n  __extends(Axis2D, _super);\n\n  function Axis2D(dim, scale, coordExtent, axisType, position) {\n    var _this = _super.call(this, dim, scale, coordExtent) || this;\n    /**\n     * Index of axis, can be used as key\n     * Injected outside.\n     */\n\n\n    _this.index = 0;\n    _this.type = axisType || 'value';\n    _this.position = position || 'bottom';\n    return _this;\n  }\n\n  Axis2D.prototype.isHorizontal = function () {\n    var position = this.position;\n    return position === 'top' || position === 'bottom';\n  };\n  /**\n   * Each item cooresponds to this.getExtent(), which\n   * means globalExtent[0] may greater than globalExtent[1],\n   * unless `asc` is input.\n   *\n   * @param {boolean} [asc]\n   * @return {Array.<number>}\n   */\n\n\n  Axis2D.prototype.getGlobalExtent = function (asc) {\n    var ret = this.getExtent();\n    ret[0] = this.toGlobalCoord(ret[0]);\n    ret[1] = this.toGlobalCoord(ret[1]);\n    asc && ret[0] > ret[1] && ret.reverse();\n    return ret;\n  };\n\n  Axis2D.prototype.pointToData = function (point, clamp) {\n    return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);\n  };\n  /**\n   * Set ordinalSortInfo\n   * @param info new OrdinalSortInfo\n   */\n\n\n  Axis2D.prototype.setCategorySortInfo = function (info) {\n    if (this.type !== 'category') {\n      return false;\n    }\n\n    this.model.option.categorySortInfo = info;\n    this.scale.setSortInfo(info);\n  };\n\n  return Axis2D;\n}(Axis);\n\n/**\n * Can only be called after coordinate system creation stage.\n * (Can be called before coordinate system update stage).\n */\n\nfunction layout$1(gridModel, axisModel, opt) {\n  opt = opt || {};\n  var grid = gridModel.coordinateSystem;\n  var axis = axisModel.axis;\n  var layout = {};\n  var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0];\n  var rawAxisPosition = axis.position;\n  var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition;\n  var axisDim = axis.dim;\n  var rect = grid.getRect();\n  var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];\n  var idx = {\n    left: 0,\n    right: 1,\n    top: 0,\n    bottom: 1,\n    onZero: 2\n  };\n  var axisOffset = axisModel.get('offset') || 0;\n  var posBound = axisDim === 'x' ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];\n\n  if (otherAxisOnZeroOf) {\n    var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0));\n    posBound[idx.onZero] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);\n  } // Axis position\n\n\n  layout.position = [axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]]; // Axis rotation\n\n  layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); // Tick and label direction, x y is axisDim\n\n  var dirMap = {\n    top: -1,\n    bottom: 1,\n    left: -1,\n    right: 1\n  };\n  layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];\n  layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0;\n\n  if (axisModel.get(['axisTick', 'inside'])) {\n    layout.tickDirection = -layout.tickDirection;\n  }\n\n  if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) {\n    layout.labelDirection = -layout.labelDirection;\n  } // Special label rotation\n\n\n  var labelRotate = axisModel.get(['axisLabel', 'rotate']);\n  layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; // Over splitLine and splitArea\n\n  layout.z2 = 1;\n  return layout;\n}\nfunction isCartesian2DSeries(seriesModel) {\n  return seriesModel.get('coordinateSystem') === 'cartesian2d';\n}\nfunction findAxisModels(seriesModel) {\n  var axisModelMap = {\n    xAxisModel: null,\n    yAxisModel: null\n  };\n  each(axisModelMap, function (v, key) {\n    var axisType = key.replace(/Model$/, '');\n    var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0];\n\n    if (\"development\" !== 'production') {\n      if (!axisModel) {\n        throw new Error(axisType + ' \"' + retrieve3(seriesModel.get(axisType + 'Index'), seriesModel.get(axisType + 'Id'), 0) + '\" not found');\n      }\n    }\n\n    axisModelMap[key] = axisModel;\n  });\n  return axisModelMap;\n}\n\nvar mathLog$1 = Math.log;\nfunction alignScaleTicks(scale, axisModel, alignToScale) {\n  var intervalScaleProto = IntervalScale.prototype; // NOTE: There is a precondition for log scale  here:\n  // In log scale we store _interval and _extent of exponent value.\n  // So if we use the method of InternalScale to set/get these data.\n  // It process the exponent value, which is linear and what we want here.\n\n  var alignToTicks = intervalScaleProto.getTicks.call(alignToScale);\n  var alignToNicedTicks = intervalScaleProto.getTicks.call(alignToScale, true);\n  var alignToSplitNumber = alignToTicks.length - 1;\n  var alignToInterval = intervalScaleProto.getInterval.call(alignToScale);\n  var scaleExtent = getScaleExtent(scale, axisModel);\n  var rawExtent = scaleExtent.extent;\n  var isMinFixed = scaleExtent.fixMin;\n  var isMaxFixed = scaleExtent.fixMax;\n\n  if (scale.type === 'log') {\n    var logBase = mathLog$1(scale.base);\n    rawExtent = [mathLog$1(rawExtent[0]) / logBase, mathLog$1(rawExtent[1]) / logBase];\n  }\n\n  scale.setExtent(rawExtent[0], rawExtent[1]);\n  scale.calcNiceExtent({\n    splitNumber: alignToSplitNumber,\n    fixMin: isMinFixed,\n    fixMax: isMaxFixed\n  });\n  var extent = intervalScaleProto.getExtent.call(scale); // Need to update the rawExtent.\n  // Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax'\n\n  if (isMinFixed) {\n    rawExtent[0] = extent[0];\n  }\n\n  if (isMaxFixed) {\n    rawExtent[1] = extent[1];\n  }\n\n  var interval = intervalScaleProto.getInterval.call(scale);\n  var min = rawExtent[0];\n  var max = rawExtent[1];\n\n  if (isMinFixed && isMaxFixed) {\n    // User set min, max, divide to get new interval\n    interval = (max - min) / alignToSplitNumber;\n  } else if (isMinFixed) {\n    max = rawExtent[0] + interval * alignToSplitNumber; // User set min, expand extent on the other side\n\n    while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])) {\n      interval = increaseInterval(interval);\n      max = rawExtent[0] + interval * alignToSplitNumber;\n    }\n  } else if (isMaxFixed) {\n    // User set max, expand extent on the other side\n    min = rawExtent[1] - interval * alignToSplitNumber;\n\n    while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])) {\n      interval = increaseInterval(interval);\n      min = rawExtent[1] - interval * alignToSplitNumber;\n    }\n  } else {\n    var nicedSplitNumber = scale.getTicks().length - 1;\n\n    if (nicedSplitNumber > alignToSplitNumber) {\n      interval = increaseInterval(interval);\n    }\n\n    var range = interval * alignToSplitNumber;\n    max = Math.ceil(rawExtent[1] / interval) * interval;\n    min = round(max - range); // Not change the result that crossing zero.\n\n    if (min < 0 && rawExtent[0] >= 0) {\n      min = 0;\n      max = round(range);\n    } else if (max > 0 && rawExtent[1] <= 0) {\n      max = 0;\n      min = -round(range);\n    }\n  } // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale\n\n\n  var t0 = (alignToTicks[0].value - alignToNicedTicks[0].value) / alignToInterval;\n  var t1 = (alignToTicks[alignToSplitNumber].value - alignToNicedTicks[alignToSplitNumber].value) / alignToInterval; // NOTE: Must in setExtent -> setInterval -> setNiceExtent order.\n\n  intervalScaleProto.setExtent.call(scale, min + interval * t0, max + interval * t1);\n  intervalScaleProto.setInterval.call(scale, interval);\n\n  if (t0 || t1) {\n    intervalScaleProto.setNiceExtent.call(scale, min + interval, max - interval);\n  }\n\n  if (\"development\" !== 'production') {\n    var ticks = intervalScaleProto.getTicks.call(scale);\n\n    if (ticks[1] && (!isValueNice(interval) || getPrecisionSafe(ticks[1].value) > getPrecisionSafe(interval))) {\n      warn( // eslint-disable-next-line\n      \"The ticks may be not readable when set min: \" + axisModel.get('min') + \", max: \" + axisModel.get('max') + \" and alignTicks: true\");\n    }\n  }\n}\n\nvar Grid =\n/** @class */\nfunction () {\n  function Grid(gridModel, ecModel, api) {\n    // FIXME:TS where used (different from registered type 'cartesian2d')?\n    this.type = 'grid';\n    this._coordsMap = {};\n    this._coordsList = [];\n    this._axesMap = {};\n    this._axesList = [];\n    this.axisPointerEnabled = true;\n    this.dimensions = cartesian2DDimensions;\n\n    this._initCartesian(gridModel, ecModel, api);\n\n    this.model = gridModel;\n  }\n\n  Grid.prototype.getRect = function () {\n    return this._rect;\n  };\n\n  Grid.prototype.update = function (ecModel, api) {\n    var axesMap = this._axesMap;\n\n    this._updateScale(ecModel, this.model);\n\n    function updateAxisTicks(axes) {\n      var alignTo; // Axis is added in order of axisIndex.\n\n      var axesIndices = keys(axes);\n      var len = axesIndices.length;\n\n      if (!len) {\n        return;\n      }\n\n      var axisNeedsAlign = []; // Process once and calculate the ticks for those don't use alignTicks.\n\n      for (var i = len - 1; i >= 0; i--) {\n        var idx = +axesIndices[i]; // Convert to number.\n\n        var axis = axes[idx];\n        var model = axis.model;\n        var scale = axis.scale;\n\n        if ( // Only value and log axis without interval support alignTicks.\n        isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) {\n          axisNeedsAlign.push(axis);\n        } else {\n          niceScaleExtent(scale, model);\n\n          if (isIntervalOrLogScale(scale)) {\n            // Can only align to interval or log axis.\n            alignTo = axis;\n          }\n        }\n      }\n      // PENDING. Should we find the axis that both set interval, min, max and align to this one?\n\n      if (axisNeedsAlign.length) {\n        if (!alignTo) {\n          alignTo = axisNeedsAlign.pop();\n          niceScaleExtent(alignTo.scale, alignTo.model);\n        }\n\n        each(axisNeedsAlign, function (axis) {\n          alignScaleTicks(axis.scale, axis.model, alignTo.scale);\n        });\n      }\n    }\n\n    updateAxisTicks(axesMap.x);\n    updateAxisTicks(axesMap.y); // Key: axisDim_axisIndex, value: boolean, whether onZero target.\n\n    var onZeroRecords = {};\n    each(axesMap.x, function (xAxis) {\n      fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords);\n    });\n    each(axesMap.y, function (yAxis) {\n      fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords);\n    }); // Resize again if containLabel is enabled\n    // FIXME It may cause getting wrong grid size in data processing stage\n\n    this.resize(this.model, api);\n  };\n  /**\n   * Resize the grid\n   */\n\n\n  Grid.prototype.resize = function (gridModel, api, ignoreContainLabel) {\n    var boxLayoutParams = gridModel.getBoxLayoutParams();\n    var isContainLabel = !ignoreContainLabel && gridModel.get('containLabel');\n    var gridRect = getLayoutRect(boxLayoutParams, {\n      width: api.getWidth(),\n      height: api.getHeight()\n    });\n    this._rect = gridRect;\n    var axesList = this._axesList;\n    adjustAxes(); // Minus label size\n\n    if (isContainLabel) {\n      each(axesList, function (axis) {\n        if (!axis.model.get(['axisLabel', 'inside'])) {\n          var labelUnionRect = estimateLabelUnionRect(axis);\n\n          if (labelUnionRect) {\n            var dim = axis.isHorizontal() ? 'height' : 'width';\n            var margin = axis.model.get(['axisLabel', 'margin']);\n            gridRect[dim] -= labelUnionRect[dim] + margin;\n\n            if (axis.position === 'top') {\n              gridRect.y += labelUnionRect.height + margin;\n            } else if (axis.position === 'left') {\n              gridRect.x += labelUnionRect.width + margin;\n            }\n          }\n        }\n      });\n      adjustAxes();\n    }\n\n    each(this._coordsList, function (coord) {\n      // Calculate affine matrix to accelerate the data to point transform.\n      // If all the axes scales are time or value.\n      coord.calcAffineTransform();\n    });\n\n    function adjustAxes() {\n      each(axesList, function (axis) {\n        var isHorizontal = axis.isHorizontal();\n        var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];\n        var idx = axis.inverse ? 1 : 0;\n        axis.setExtent(extent[idx], extent[1 - idx]);\n        updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);\n      });\n    }\n  };\n\n  Grid.prototype.getAxis = function (dim, axisIndex) {\n    var axesMapOnDim = this._axesMap[dim];\n\n    if (axesMapOnDim != null) {\n      return axesMapOnDim[axisIndex || 0];\n    }\n  };\n\n  Grid.prototype.getAxes = function () {\n    return this._axesList.slice();\n  };\n\n  Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) {\n    if (xAxisIndex != null && yAxisIndex != null) {\n      var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n      return this._coordsMap[key];\n    }\n\n    if (isObject(xAxisIndex)) {\n      yAxisIndex = xAxisIndex.yAxisIndex;\n      xAxisIndex = xAxisIndex.xAxisIndex;\n    }\n\n    for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {\n      if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {\n        return coordList[i];\n      }\n    }\n  };\n\n  Grid.prototype.getCartesians = function () {\n    return this._coordsList.slice();\n  };\n  /**\n   * @implements\n   */\n\n\n  Grid.prototype.convertToPixel = function (ecModel, finder, value) {\n    var target = this._findConvertTarget(finder);\n\n    return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;\n  };\n  /**\n   * @implements\n   */\n\n\n  Grid.prototype.convertFromPixel = function (ecModel, finder, value) {\n    var target = this._findConvertTarget(finder);\n\n    return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;\n  };\n\n  Grid.prototype._findConvertTarget = function (finder) {\n    var seriesModel = finder.seriesModel;\n    var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n    var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n    var gridModel = finder.gridModel;\n    var coordsList = this._coordsList;\n    var cartesian;\n    var axis;\n\n    if (seriesModel) {\n      cartesian = seriesModel.coordinateSystem;\n      indexOf(coordsList, cartesian) < 0 && (cartesian = null);\n    } else if (xAxisModel && yAxisModel) {\n      cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n    } else if (xAxisModel) {\n      axis = this.getAxis('x', xAxisModel.componentIndex);\n    } else if (yAxisModel) {\n      axis = this.getAxis('y', yAxisModel.componentIndex);\n    } // Lowest priority.\n    else if (gridModel) {\n        var grid = gridModel.coordinateSystem;\n\n        if (grid === this) {\n          cartesian = this._coordsList[0];\n        }\n      }\n\n    return {\n      cartesian: cartesian,\n      axis: axis\n    };\n  };\n  /**\n   * @implements\n   */\n\n\n  Grid.prototype.containPoint = function (point) {\n    var coord = this._coordsList[0];\n\n    if (coord) {\n      return coord.containPoint(point);\n    }\n  };\n  /**\n   * Initialize cartesian coordinate systems\n   */\n\n\n  Grid.prototype._initCartesian = function (gridModel, ecModel, api) {\n    var _this = this;\n\n    var grid = this;\n    var axisPositionUsed = {\n      left: false,\n      right: false,\n      top: false,\n      bottom: false\n    };\n    var axesMap = {\n      x: {},\n      y: {}\n    };\n    var axesCount = {\n      x: 0,\n      y: 0\n    }; // Create axis\n\n    ecModel.eachComponent('xAxis', createAxisCreator('x'), this);\n    ecModel.eachComponent('yAxis', createAxisCreator('y'), this);\n\n    if (!axesCount.x || !axesCount.y) {\n      // Roll back when there no either x or y axis\n      this._axesMap = {};\n      this._axesList = [];\n      return;\n    }\n\n    this._axesMap = axesMap; // Create cartesian2d\n\n    each(axesMap.x, function (xAxis, xAxisIndex) {\n      each(axesMap.y, function (yAxis, yAxisIndex) {\n        var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n        var cartesian = new Cartesian2D(key);\n        cartesian.master = _this;\n        cartesian.model = gridModel;\n        _this._coordsMap[key] = cartesian;\n\n        _this._coordsList.push(cartesian);\n\n        cartesian.addAxis(xAxis);\n        cartesian.addAxis(yAxis);\n      });\n    });\n\n    function createAxisCreator(dimName) {\n      return function (axisModel, idx) {\n        if (!isAxisUsedInTheGrid(axisModel, gridModel)) {\n          return;\n        }\n\n        var axisPosition = axisModel.get('position');\n\n        if (dimName === 'x') {\n          // Fix position\n          if (axisPosition !== 'top' && axisPosition !== 'bottom') {\n            // Default bottom of X\n            axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom';\n          }\n        } else {\n          // Fix position\n          if (axisPosition !== 'left' && axisPosition !== 'right') {\n            // Default left of Y\n            axisPosition = axisPositionUsed.left ? 'right' : 'left';\n          }\n        }\n\n        axisPositionUsed[axisPosition] = true;\n        var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);\n        var isCategory = axis.type === 'category';\n        axis.onBand = isCategory && axisModel.get('boundaryGap');\n        axis.inverse = axisModel.get('inverse'); // Inject axis into axisModel\n\n        axisModel.axis = axis; // Inject axisModel into axis\n\n        axis.model = axisModel; // Inject grid info axis\n\n        axis.grid = grid; // Index of axis, can be used as key\n\n        axis.index = idx;\n\n        grid._axesList.push(axis);\n\n        axesMap[dimName][idx] = axis;\n        axesCount[dimName]++;\n      };\n    }\n  };\n  /**\n   * Update cartesian properties from series.\n   */\n\n\n  Grid.prototype._updateScale = function (ecModel, gridModel) {\n    // Reset scale\n    each(this._axesList, function (axis) {\n      axis.scale.setExtent(Infinity, -Infinity);\n\n      if (axis.type === 'category') {\n        var categorySortInfo = axis.model.get('categorySortInfo');\n        axis.scale.setSortInfo(categorySortInfo);\n      }\n    });\n    ecModel.eachSeries(function (seriesModel) {\n      if (isCartesian2DSeries(seriesModel)) {\n        var axesModelMap = findAxisModels(seriesModel);\n        var xAxisModel = axesModelMap.xAxisModel;\n        var yAxisModel = axesModelMap.yAxisModel;\n\n        if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) {\n          return;\n        }\n\n        var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n        var data = seriesModel.getData();\n        var xAxis = cartesian.getAxis('x');\n        var yAxis = cartesian.getAxis('y');\n        unionExtent(data, xAxis);\n        unionExtent(data, yAxis);\n      }\n    }, this);\n\n    function unionExtent(data, axis) {\n      each(getDataDimensionsOnAxis(data, axis.dim), function (dim) {\n        axis.scale.unionExtentFromData(data, dim);\n      });\n    }\n  };\n  /**\n   * @param dim 'x' or 'y' or 'auto' or null/undefined\n   */\n\n\n  Grid.prototype.getTooltipAxes = function (dim) {\n    var baseAxes = [];\n    var otherAxes = [];\n    each(this.getCartesians(), function (cartesian) {\n      var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();\n      var otherAxis = cartesian.getOtherAxis(baseAxis);\n      indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);\n      indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);\n    });\n    return {\n      baseAxes: baseAxes,\n      otherAxes: otherAxes\n    };\n  };\n\n  Grid.create = function (ecModel, api) {\n    var grids = [];\n    ecModel.eachComponent('grid', function (gridModel, idx) {\n      var grid = new Grid(gridModel, ecModel, api);\n      grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize\n      // should be performed in create stage.\n\n      grid.resize(gridModel, api, true);\n      gridModel.coordinateSystem = grid;\n      grids.push(grid);\n    }); // Inject the coordinateSystems into seriesModel\n\n    ecModel.eachSeries(function (seriesModel) {\n      if (!isCartesian2DSeries(seriesModel)) {\n        return;\n      }\n\n      var axesModelMap = findAxisModels(seriesModel);\n      var xAxisModel = axesModelMap.xAxisModel;\n      var yAxisModel = axesModelMap.yAxisModel;\n      var gridModel = xAxisModel.getCoordSysModel();\n\n      if (\"development\" !== 'production') {\n        if (!gridModel) {\n          throw new Error('Grid \"' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '\" not found');\n        }\n\n        if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {\n          throw new Error('xAxis and yAxis must use the same grid');\n        }\n      }\n\n      var grid = gridModel.coordinateSystem;\n      seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n    });\n    return grids;\n  }; // For deciding which dimensions to use when creating list data\n\n\n  Grid.dimensions = cartesian2DDimensions;\n  return Grid;\n}();\n/**\n * Check if the axis is used in the specified grid.\n */\n\n\nfunction isAxisUsedInTheGrid(axisModel, gridModel) {\n  return axisModel.getCoordSysModel() === gridModel;\n}\n\nfunction fixAxisOnZero(axesMap, otherAxisDim, axis, // Key: see `getOnZeroRecordKey`\nonZeroRecords) {\n  axis.getAxesOnZeroOf = function () {\n    // TODO: onZero of multiple axes.\n    return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : [];\n  }; // onZero can not be enabled in these two situations:\n  // 1. When any other axis is a category axis.\n  // 2. When no axis is cross 0 point.\n\n\n  var otherAxes = axesMap[otherAxisDim];\n  var otherAxisOnZeroOf;\n  var axisModel = axis.model;\n  var onZero = axisModel.get(['axisLine', 'onZero']);\n  var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']);\n\n  if (!onZero) {\n    return;\n  } // If target axis is specified.\n\n\n  if (onZeroAxisIndex != null) {\n    if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {\n      otherAxisOnZeroOf = otherAxes[onZeroAxisIndex];\n    }\n  } else {\n    // Find the first available other axis.\n    for (var idx in otherAxes) {\n      if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx]) // Consider that two Y axes on one value axis,\n      // if both onZero, the two Y axes overlap.\n      && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) {\n        otherAxisOnZeroOf = otherAxes[idx];\n        break;\n      }\n    }\n  }\n\n  if (otherAxisOnZeroOf) {\n    onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true;\n  }\n\n  function getOnZeroRecordKey(axis) {\n    return axis.dim + '_' + axis.index;\n  }\n}\n\nfunction canOnZeroToAxis(axis) {\n  return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);\n}\n\nfunction updateAxisTransform(axis, coordBase) {\n  var axisExtent = axis.getExtent();\n  var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform\n\n  axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {\n    return coord + coordBase;\n  } : function (coord) {\n    return axisExtentSum - coord + coordBase;\n  };\n  axis.toLocalCoord = axis.dim === 'x' ? function (coord) {\n    return coord - coordBase;\n  } : function (coord) {\n    return axisExtentSum - coord + coordBase;\n  };\n}\n\nvar PI$5 = Math.PI;\n/**\n * A final axis is translated and rotated from a \"standard axis\".\n * So opt.position and opt.rotation is required.\n *\n * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],\n * for example: (0, 0) ------------> (0, 50)\n *\n * nameDirection or tickDirection or labelDirection is 1 means tick\n * or label is below the standard axis, whereas is -1 means above\n * the standard axis. labelOffset means offset between label and axis,\n * which is useful when 'onZero', where axisLabel is in the grid and\n * label in outside grid.\n *\n * Tips: like always,\n * positive rotation represents anticlockwise, and negative rotation\n * represents clockwise.\n * The direction of position coordinate is the same as the direction\n * of screen coordinate.\n *\n * Do not need to consider axis 'inverse', which is auto processed by\n * axis extent.\n */\n\nvar AxisBuilder =\n/** @class */\nfunction () {\n  function AxisBuilder(axisModel, opt) {\n    this.group = new Group();\n    this.opt = opt;\n    this.axisModel = axisModel; // Default value\n\n    defaults(opt, {\n      labelOffset: 0,\n      nameDirection: 1,\n      tickDirection: 1,\n      labelDirection: 1,\n      silent: true,\n      handleAutoShown: function () {\n        return true;\n      }\n    }); // FIXME Not use a seperate text group?\n\n    var transformGroup = new Group({\n      x: opt.position[0],\n      y: opt.position[1],\n      rotation: opt.rotation\n    }); // this.group.add(transformGroup);\n    // this._transformGroup = transformGroup;\n\n    transformGroup.updateTransform();\n    this._transformGroup = transformGroup;\n  }\n\n  AxisBuilder.prototype.hasBuilder = function (name) {\n    return !!builders[name];\n  };\n\n  AxisBuilder.prototype.add = function (name) {\n    builders[name](this.opt, this.axisModel, this.group, this._transformGroup);\n  };\n\n  AxisBuilder.prototype.getGroup = function () {\n    return this.group;\n  };\n\n  AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {\n    var rotationDiff = remRadian(textRotation - axisRotation);\n    var textAlign;\n    var textVerticalAlign;\n\n    if (isRadianAroundZero(rotationDiff)) {\n      // Label is parallel with axis line.\n      textVerticalAlign = direction > 0 ? 'top' : 'bottom';\n      textAlign = 'center';\n    } else if (isRadianAroundZero(rotationDiff - PI$5)) {\n      // Label is inverse parallel with axis line.\n      textVerticalAlign = direction > 0 ? 'bottom' : 'top';\n      textAlign = 'center';\n    } else {\n      textVerticalAlign = 'middle';\n\n      if (rotationDiff > 0 && rotationDiff < PI$5) {\n        textAlign = direction > 0 ? 'right' : 'left';\n      } else {\n        textAlign = direction > 0 ? 'left' : 'right';\n      }\n    }\n\n    return {\n      rotation: rotationDiff,\n      textAlign: textAlign,\n      textVerticalAlign: textVerticalAlign\n    };\n  };\n\n  AxisBuilder.makeAxisEventDataBase = function (axisModel) {\n    var eventData = {\n      componentType: axisModel.mainType,\n      componentIndex: axisModel.componentIndex\n    };\n    eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;\n    return eventData;\n  };\n\n  AxisBuilder.isLabelSilent = function (axisModel) {\n    var tooltipOpt = axisModel.get('tooltip');\n    return axisModel.get('silent') // Consider mouse cursor, add these restrictions.\n    || !(axisModel.get('triggerEvent') || tooltipOpt && tooltipOpt.show);\n  };\n\n  return AxisBuilder;\n}();\nvar builders = {\n  axisLine: function (opt, axisModel, group, transformGroup) {\n    var shown = axisModel.get(['axisLine', 'show']);\n\n    if (shown === 'auto' && opt.handleAutoShown) {\n      shown = opt.handleAutoShown('axisLine');\n    }\n\n    if (!shown) {\n      return;\n    }\n\n    var extent = axisModel.axis.getExtent();\n    var matrix = transformGroup.transform;\n    var pt1 = [extent[0], 0];\n    var pt2 = [extent[1], 0];\n    var inverse = pt1[0] > pt2[0];\n\n    if (matrix) {\n      applyTransform(pt1, pt1, matrix);\n      applyTransform(pt2, pt2, matrix);\n    }\n\n    var lineStyle = extend({\n      lineCap: 'round'\n    }, axisModel.getModel(['axisLine', 'lineStyle']).getLineStyle());\n    var line = new Line({\n      shape: {\n        x1: pt1[0],\n        y1: pt1[1],\n        x2: pt2[0],\n        y2: pt2[1]\n      },\n      style: lineStyle,\n      strokeContainThreshold: opt.strokeContainThreshold || 5,\n      silent: true,\n      z2: 1\n    });\n    subPixelOptimizeLine$1(line.shape, line.style.lineWidth);\n    line.anid = 'line';\n    group.add(line);\n    var arrows = axisModel.get(['axisLine', 'symbol']);\n\n    if (arrows != null) {\n      var arrowSize = axisModel.get(['axisLine', 'symbolSize']);\n\n      if (isString(arrows)) {\n        // Use the same arrow for start and end point\n        arrows = [arrows, arrows];\n      }\n\n      if (isString(arrowSize) || isNumber(arrowSize)) {\n        // Use the same size for width and height\n        arrowSize = [arrowSize, arrowSize];\n      }\n\n      var arrowOffset = normalizeSymbolOffset(axisModel.get(['axisLine', 'symbolOffset']) || 0, arrowSize);\n      var symbolWidth_1 = arrowSize[0];\n      var symbolHeight_1 = arrowSize[1];\n      each([{\n        rotate: opt.rotation + Math.PI / 2,\n        offset: arrowOffset[0],\n        r: 0\n      }, {\n        rotate: opt.rotation - Math.PI / 2,\n        offset: arrowOffset[1],\n        r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1]))\n      }], function (point, index) {\n        if (arrows[index] !== 'none' && arrows[index] != null) {\n          var symbol = createSymbol(arrows[index], -symbolWidth_1 / 2, -symbolHeight_1 / 2, symbolWidth_1, symbolHeight_1, lineStyle.stroke, true); // Calculate arrow position with offset\n\n          var r = point.r + point.offset;\n          var pt = inverse ? pt2 : pt1;\n          symbol.attr({\n            rotation: point.rotate,\n            x: pt[0] + r * Math.cos(opt.rotation),\n            y: pt[1] - r * Math.sin(opt.rotation),\n            silent: true,\n            z2: 11\n          });\n          group.add(symbol);\n        }\n      });\n    }\n  },\n  axisTickLabel: function (opt, axisModel, group, transformGroup) {\n    var ticksEls = buildAxisMajorTicks(group, transformGroup, axisModel, opt);\n    var labelEls = buildAxisLabel(group, transformGroup, axisModel, opt);\n    fixMinMaxLabelShow(axisModel, labelEls, ticksEls);\n    buildAxisMinorTicks(group, transformGroup, axisModel, opt.tickDirection); // This bit fixes the label overlap issue for the time chart.\n    // See https://github.com/apache/echarts/issues/14266 for more.\n\n    if (axisModel.get(['axisLabel', 'hideOverlap'])) {\n      var labelList = prepareLayoutList(map(labelEls, function (label) {\n        return {\n          label: label,\n          priority: label.z2,\n          defaultAttr: {\n            ignore: label.ignore\n          }\n        };\n      }));\n      hideOverlap(labelList);\n    }\n  },\n  axisName: function (opt, axisModel, group, transformGroup) {\n    var name = retrieve(opt.axisName, axisModel.get('name'));\n\n    if (!name) {\n      return;\n    }\n\n    var nameLocation = axisModel.get('nameLocation');\n    var nameDirection = opt.nameDirection;\n    var textStyleModel = axisModel.getModel('nameTextStyle');\n    var gap = axisModel.get('nameGap') || 0;\n    var extent = axisModel.axis.getExtent();\n    var gapSignal = extent[0] > extent[1] ? -1 : 1;\n    var pos = [nameLocation === 'start' ? extent[0] - gapSignal * gap : nameLocation === 'end' ? extent[1] + gapSignal * gap : (extent[0] + extent[1]) / 2, // Reuse labelOffset.\n    isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0];\n    var labelLayout;\n    var nameRotation = axisModel.get('nameRotate');\n\n    if (nameRotation != null) {\n      nameRotation = nameRotation * PI$5 / 180; // To radian.\n    }\n\n    var axisNameAvailableWidth;\n\n    if (isNameLocationCenter(nameLocation)) {\n      labelLayout = AxisBuilder.innerTextLayout(opt.rotation, nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.\n      nameDirection);\n    } else {\n      labelLayout = endTextLayout(opt.rotation, nameLocation, nameRotation || 0, extent);\n      axisNameAvailableWidth = opt.axisNameAvailableWidth;\n\n      if (axisNameAvailableWidth != null) {\n        axisNameAvailableWidth = Math.abs(axisNameAvailableWidth / Math.sin(labelLayout.rotation));\n        !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);\n      }\n    }\n\n    var textFont = textStyleModel.getFont();\n    var truncateOpt = axisModel.get('nameTruncate', true) || {};\n    var ellipsis = truncateOpt.ellipsis;\n    var maxWidth = retrieve(opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth);\n    var textEl = new ZRText({\n      x: pos[0],\n      y: pos[1],\n      rotation: labelLayout.rotation,\n      silent: AxisBuilder.isLabelSilent(axisModel),\n      style: createTextStyle(textStyleModel, {\n        text: name,\n        font: textFont,\n        overflow: 'truncate',\n        width: maxWidth,\n        ellipsis: ellipsis,\n        fill: textStyleModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']),\n        align: textStyleModel.get('align') || labelLayout.textAlign,\n        verticalAlign: textStyleModel.get('verticalAlign') || labelLayout.textVerticalAlign\n      }),\n      z2: 1\n    });\n    setTooltipConfig({\n      el: textEl,\n      componentModel: axisModel,\n      itemName: name\n    });\n    textEl.__fullText = name; // Id for animation\n\n    textEl.anid = 'name';\n\n    if (axisModel.get('triggerEvent')) {\n      var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n      eventData.targetType = 'axisName';\n      eventData.name = name;\n      getECData(textEl).eventData = eventData;\n    } // FIXME\n\n\n    transformGroup.add(textEl);\n    textEl.updateTransform();\n    group.add(textEl);\n    textEl.decomposeTransform();\n  }\n};\n\nfunction endTextLayout(rotation, textPosition, textRotate, extent) {\n  var rotationDiff = remRadian(textRotate - rotation);\n  var textAlign;\n  var textVerticalAlign;\n  var inverse = extent[0] > extent[1];\n  var onLeft = textPosition === 'start' && !inverse || textPosition !== 'start' && inverse;\n\n  if (isRadianAroundZero(rotationDiff - PI$5 / 2)) {\n    textVerticalAlign = onLeft ? 'bottom' : 'top';\n    textAlign = 'center';\n  } else if (isRadianAroundZero(rotationDiff - PI$5 * 1.5)) {\n    textVerticalAlign = onLeft ? 'top' : 'bottom';\n    textAlign = 'center';\n  } else {\n    textVerticalAlign = 'middle';\n\n    if (rotationDiff < PI$5 * 1.5 && rotationDiff > PI$5 / 2) {\n      textAlign = onLeft ? 'left' : 'right';\n    } else {\n      textAlign = onLeft ? 'right' : 'left';\n    }\n  }\n\n  return {\n    rotation: rotationDiff,\n    textAlign: textAlign,\n    textVerticalAlign: textVerticalAlign\n  };\n}\n\nfunction fixMinMaxLabelShow(axisModel, labelEls, tickEls) {\n  if (shouldShowAllLabels(axisModel.axis)) {\n    return;\n  } // If min or max are user set, we need to check\n  // If the tick on min(max) are overlap on their neighbour tick\n  // If they are overlapped, we need to hide the min(max) tick label\n\n\n  var showMinLabel = axisModel.get(['axisLabel', 'showMinLabel']);\n  var showMaxLabel = axisModel.get(['axisLabel', 'showMaxLabel']); // FIXME\n  // Have not consider onBand yet, where tick els is more than label els.\n\n  labelEls = labelEls || [];\n  tickEls = tickEls || [];\n  var firstLabel = labelEls[0];\n  var nextLabel = labelEls[1];\n  var lastLabel = labelEls[labelEls.length - 1];\n  var prevLabel = labelEls[labelEls.length - 2];\n  var firstTick = tickEls[0];\n  var nextTick = tickEls[1];\n  var lastTick = tickEls[tickEls.length - 1];\n  var prevTick = tickEls[tickEls.length - 2];\n\n  if (showMinLabel === false) {\n    ignoreEl(firstLabel);\n    ignoreEl(firstTick);\n  } else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {\n    if (showMinLabel) {\n      ignoreEl(nextLabel);\n      ignoreEl(nextTick);\n    } else {\n      ignoreEl(firstLabel);\n      ignoreEl(firstTick);\n    }\n  }\n\n  if (showMaxLabel === false) {\n    ignoreEl(lastLabel);\n    ignoreEl(lastTick);\n  } else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {\n    if (showMaxLabel) {\n      ignoreEl(prevLabel);\n      ignoreEl(prevTick);\n    } else {\n      ignoreEl(lastLabel);\n      ignoreEl(lastTick);\n    }\n  }\n}\n\nfunction ignoreEl(el) {\n  el && (el.ignore = true);\n}\n\nfunction isTwoLabelOverlapped(current, next) {\n  // current and next has the same rotation.\n  var firstRect = current && current.getBoundingRect().clone();\n  var nextRect = next && next.getBoundingRect().clone();\n\n  if (!firstRect || !nextRect) {\n    return;\n  } // When checking intersect of two rotated labels, we use mRotationBack\n  // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.\n\n\n  var mRotationBack = identity([]);\n  rotate(mRotationBack, mRotationBack, -current.rotation);\n  firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));\n  nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));\n  return firstRect.intersect(nextRect);\n}\n\nfunction isNameLocationCenter(nameLocation) {\n  return nameLocation === 'middle' || nameLocation === 'center';\n}\n\nfunction createTicks(ticksCoords, tickTransform, tickEndCoord, tickLineStyle, anidPrefix) {\n  var tickEls = [];\n  var pt1 = [];\n  var pt2 = [];\n\n  for (var i = 0; i < ticksCoords.length; i++) {\n    var tickCoord = ticksCoords[i].coord;\n    pt1[0] = tickCoord;\n    pt1[1] = 0;\n    pt2[0] = tickCoord;\n    pt2[1] = tickEndCoord;\n\n    if (tickTransform) {\n      applyTransform(pt1, pt1, tickTransform);\n      applyTransform(pt2, pt2, tickTransform);\n    } // Tick line, Not use group transform to have better line draw\n\n\n    var tickEl = new Line({\n      shape: {\n        x1: pt1[0],\n        y1: pt1[1],\n        x2: pt2[0],\n        y2: pt2[1]\n      },\n      style: tickLineStyle,\n      z2: 2,\n      autoBatch: true,\n      silent: true\n    });\n    subPixelOptimizeLine$1(tickEl.shape, tickEl.style.lineWidth);\n    tickEl.anid = anidPrefix + '_' + ticksCoords[i].tickValue;\n    tickEls.push(tickEl);\n  }\n\n  return tickEls;\n}\n\nfunction buildAxisMajorTicks(group, transformGroup, axisModel, opt) {\n  var axis = axisModel.axis;\n  var tickModel = axisModel.getModel('axisTick');\n  var shown = tickModel.get('show');\n\n  if (shown === 'auto' && opt.handleAutoShown) {\n    shown = opt.handleAutoShown('axisTick');\n  }\n\n  if (!shown || axis.scale.isBlank()) {\n    return;\n  }\n\n  var lineStyleModel = tickModel.getModel('lineStyle');\n  var tickEndCoord = opt.tickDirection * tickModel.get('length');\n  var ticksCoords = axis.getTicksCoords();\n  var ticksEls = createTicks(ticksCoords, transformGroup.transform, tickEndCoord, defaults(lineStyleModel.getLineStyle(), {\n    stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n  }), 'ticks');\n\n  for (var i = 0; i < ticksEls.length; i++) {\n    group.add(ticksEls[i]);\n  }\n\n  return ticksEls;\n}\n\nfunction buildAxisMinorTicks(group, transformGroup, axisModel, tickDirection) {\n  var axis = axisModel.axis;\n  var minorTickModel = axisModel.getModel('minorTick');\n\n  if (!minorTickModel.get('show') || axis.scale.isBlank()) {\n    return;\n  }\n\n  var minorTicksCoords = axis.getMinorTicksCoords();\n\n  if (!minorTicksCoords.length) {\n    return;\n  }\n\n  var lineStyleModel = minorTickModel.getModel('lineStyle');\n  var tickEndCoord = tickDirection * minorTickModel.get('length');\n  var minorTickLineStyle = defaults(lineStyleModel.getLineStyle(), defaults(axisModel.getModel('axisTick').getLineStyle(), {\n    stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n  }));\n\n  for (var i = 0; i < minorTicksCoords.length; i++) {\n    var minorTicksEls = createTicks(minorTicksCoords[i], transformGroup.transform, tickEndCoord, minorTickLineStyle, 'minorticks_' + i);\n\n    for (var k = 0; k < minorTicksEls.length; k++) {\n      group.add(minorTicksEls[k]);\n    }\n  }\n}\n\nfunction buildAxisLabel(group, transformGroup, axisModel, opt) {\n  var axis = axisModel.axis;\n  var show = retrieve(opt.axisLabelShow, axisModel.get(['axisLabel', 'show']));\n\n  if (!show || axis.scale.isBlank()) {\n    return;\n  }\n\n  var labelModel = axisModel.getModel('axisLabel');\n  var labelMargin = labelModel.get('margin');\n  var labels = axis.getViewLabels(); // Special label rotate.\n\n  var labelRotation = (retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) * PI$5 / 180;\n  var labelLayout = AxisBuilder.innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);\n  var rawCategoryData = axisModel.getCategories && axisModel.getCategories(true);\n  var labelEls = [];\n  var silent = AxisBuilder.isLabelSilent(axisModel);\n  var triggerEvent = axisModel.get('triggerEvent');\n  each(labels, function (labelItem, index) {\n    var tickValue = axis.scale.type === 'ordinal' ? axis.scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue;\n    var formattedLabel = labelItem.formattedLabel;\n    var rawLabel = labelItem.rawLabel;\n    var itemLabelModel = labelModel;\n\n    if (rawCategoryData && rawCategoryData[tickValue]) {\n      var rawCategoryItem = rawCategoryData[tickValue];\n\n      if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) {\n        itemLabelModel = new Model(rawCategoryItem.textStyle, labelModel, axisModel.ecModel);\n      }\n    }\n\n    var textColor = itemLabelModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']);\n    var tickCoord = axis.dataToCoord(tickValue);\n    var textEl = new ZRText({\n      x: tickCoord,\n      y: opt.labelOffset + opt.labelDirection * labelMargin,\n      rotation: labelLayout.rotation,\n      silent: silent,\n      z2: 10 + (labelItem.level || 0),\n      style: createTextStyle(itemLabelModel, {\n        text: formattedLabel,\n        align: itemLabelModel.getShallow('align', true) || labelLayout.textAlign,\n        verticalAlign: itemLabelModel.getShallow('verticalAlign', true) || itemLabelModel.getShallow('baseline', true) || labelLayout.textVerticalAlign,\n        fill: isFunction(textColor) ? textColor( // (1) In category axis with data zoom, tick is not the original\n        // index of axis.data. So tick should not be exposed to user\n        // in category axis.\n        // (2) Compatible with previous version, which always use formatted label as\n        // input. But in interval scale the formatted label is like '223,445', which\n        // maked user repalce ','. So we modify it to return original val but remain\n        // it as 'string' to avoid error in replacing.\n        axis.type === 'category' ? rawLabel : axis.type === 'value' ? tickValue + '' : tickValue, index) : textColor\n      })\n    });\n    textEl.anid = 'label_' + tickValue; // Pack data for mouse event\n\n    if (triggerEvent) {\n      var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n      eventData.targetType = 'axisLabel';\n      eventData.value = rawLabel;\n      eventData.tickIndex = index;\n\n      if (axis.type === 'category') {\n        eventData.dataIndex = tickValue;\n      }\n\n      getECData(textEl).eventData = eventData;\n    } // FIXME\n\n\n    transformGroup.add(textEl);\n    textEl.updateTransform();\n    labelEls.push(textEl);\n    group.add(textEl);\n    textEl.decomposeTransform();\n  });\n  return labelEls;\n}\n\n// allAxesInfo should be updated when setOption performed.\n\nfunction collect(ecModel, api) {\n  var result = {\n    /**\n     * key: makeKey(axis.model)\n     * value: {\n     *      axis,\n     *      coordSys,\n     *      axisPointerModel,\n     *      triggerTooltip,\n     *      involveSeries,\n     *      snap,\n     *      seriesModels,\n     *      seriesDataCount\n     * }\n     */\n    axesInfo: {},\n    seriesInvolved: false,\n\n    /**\n     * key: makeKey(coordSys.model)\n     * value: Object: key makeKey(axis.model), value: axisInfo\n     */\n    coordSysAxesInfo: {},\n    coordSysMap: {}\n  };\n  collectAxesInfo(result, ecModel, api); // Check seriesInvolved for performance, in case too many series in some chart.\n\n  result.seriesInvolved && collectSeriesInfo(result, ecModel);\n  return result;\n}\n\nfunction collectAxesInfo(result, ecModel, api) {\n  var globalTooltipModel = ecModel.getComponent('tooltip');\n  var globalAxisPointerModel = ecModel.getComponent('axisPointer'); // links can only be set on global.\n\n  var linksOption = globalAxisPointerModel.get('link', true) || [];\n  var linkGroups = []; // Collect axes info.\n\n  each(api.getCoordinateSystems(), function (coordSys) {\n    // Some coordinate system do not support axes, like geo.\n    if (!coordSys.axisPointerEnabled) {\n      return;\n    }\n\n    var coordSysKey = makeKey(coordSys.model);\n    var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};\n    result.coordSysMap[coordSysKey] = coordSys; // Set tooltip (like 'cross') is a convienent way to show axisPointer\n    // for user. So we enable seting tooltip on coordSys model.\n\n    var coordSysModel = coordSys.model;\n    var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);\n    each(coordSys.getAxes(), curry(saveTooltipAxisInfo, false, null)); // If axis tooltip used, choose tooltip axis for each coordSys.\n    // Notice this case: coordSys is `grid` but not `cartesian2D` here.\n\n    if (coordSys.getTooltipAxes && globalTooltipModel // If tooltip.showContent is set as false, tooltip will not\n    // show but axisPointer will show as normal.\n    && baseTooltipModel.get('show')) {\n      // Compatible with previous logic. But series.tooltip.trigger: 'axis'\n      // or series.data[n].tooltip.trigger: 'axis' are not support any more.\n      var triggerAxis = baseTooltipModel.get('trigger') === 'axis';\n      var cross = baseTooltipModel.get(['axisPointer', 'type']) === 'cross';\n      var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get(['axisPointer', 'axis']));\n\n      if (triggerAxis || cross) {\n        each(tooltipAxes.baseAxes, curry(saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis));\n      }\n\n      if (cross) {\n        each(tooltipAxes.otherAxes, curry(saveTooltipAxisInfo, 'cross', false));\n      }\n    } // fromTooltip: true | false | 'cross'\n    // triggerTooltip: true | false | null\n\n\n    function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {\n      var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);\n      var axisPointerShow = axisPointerModel.get('show');\n\n      if (!axisPointerShow || axisPointerShow === 'auto' && !fromTooltip && !isHandleTrigger(axisPointerModel)) {\n        return;\n      }\n\n      if (triggerTooltip == null) {\n        triggerTooltip = axisPointerModel.get('triggerTooltip');\n      }\n\n      axisPointerModel = fromTooltip ? makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) : axisPointerModel;\n      var snap = axisPointerModel.get('snap');\n      var axisKey = makeKey(axis.model);\n      var involveSeries = triggerTooltip || snap || axis.type === 'category'; // If result.axesInfo[key] exist, override it (tooltip has higher priority).\n\n      var axisInfo = result.axesInfo[axisKey] = {\n        key: axisKey,\n        axis: axis,\n        coordSys: coordSys,\n        axisPointerModel: axisPointerModel,\n        triggerTooltip: triggerTooltip,\n        involveSeries: involveSeries,\n        snap: snap,\n        useHandle: isHandleTrigger(axisPointerModel),\n        seriesModels: [],\n        linkGroup: null\n      };\n      axesInfoInCoordSys[axisKey] = axisInfo;\n      result.seriesInvolved = result.seriesInvolved || involveSeries;\n      var groupIndex = getLinkGroupIndex(linksOption, axis);\n\n      if (groupIndex != null) {\n        var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {\n          axesInfo: {}\n        });\n        linkGroup.axesInfo[axisKey] = axisInfo;\n        linkGroup.mapper = linksOption[groupIndex].mapper;\n        axisInfo.linkGroup = linkGroup;\n      }\n    }\n  });\n}\n\nfunction makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) {\n  var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');\n  var fields = ['type', 'snap', 'lineStyle', 'shadowStyle', 'label', 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'];\n  var volatileOption = {};\n  each(fields, function (field) {\n    volatileOption[field] = clone(tooltipAxisPointerModel.get(field));\n  }); // category axis do not auto snap, otherwise some tick that do not\n  // has value can not be hovered. value/time/log axis default snap if\n  // triggered from tooltip and trigger tooltip.\n\n  volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; // Compatibel with previous behavior, tooltip axis do not show label by default.\n  // Only these properties can be overrided from tooltip to axisPointer.\n\n  if (tooltipAxisPointerModel.get('type') === 'cross') {\n    volatileOption.type = 'line';\n  }\n\n  var labelOption = volatileOption.label || (volatileOption.label = {}); // Follow the convention, do not show label when triggered by tooltip by default.\n\n  labelOption.show == null && (labelOption.show = false);\n\n  if (fromTooltip === 'cross') {\n    // When 'cross', both axes show labels.\n    var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get(['label', 'show']);\n    labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true; // If triggerTooltip, this is a base axis, which should better not use cross style\n    // (cross style is dashed by default)\n\n    if (!triggerTooltip) {\n      var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');\n      crossStyle && defaults(labelOption, crossStyle.textStyle);\n    }\n  }\n\n  return axis.model.getModel('axisPointer', new Model(volatileOption, globalAxisPointerModel, ecModel));\n}\n\nfunction collectSeriesInfo(result, ecModel) {\n  // Prepare data for axis trigger\n  ecModel.eachSeries(function (seriesModel) {\n    // Notice this case: this coordSys is `cartesian2D` but not `grid`.\n    var coordSys = seriesModel.coordinateSystem;\n    var seriesTooltipTrigger = seriesModel.get(['tooltip', 'trigger'], true);\n    var seriesTooltipShow = seriesModel.get(['tooltip', 'show'], true);\n\n    if (!coordSys || seriesTooltipTrigger === 'none' || seriesTooltipTrigger === false || seriesTooltipTrigger === 'item' || seriesTooltipShow === false || seriesModel.get(['axisPointer', 'show'], true) === false) {\n      return;\n    }\n\n    each(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {\n      var axis = axisInfo.axis;\n\n      if (coordSys.getAxis(axis.dim) === axis) {\n        axisInfo.seriesModels.push(seriesModel);\n        axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);\n        axisInfo.seriesDataCount += seriesModel.getData().count();\n      }\n    });\n  });\n}\n/**\n * For example:\n * {\n *     axisPointer: {\n *         links: [{\n *             xAxisIndex: [2, 4],\n *             yAxisIndex: 'all'\n *         }, {\n *             xAxisId: ['a5', 'a7'],\n *             xAxisName: 'xxx'\n *         }]\n *     }\n * }\n */\n\n\nfunction getLinkGroupIndex(linksOption, axis) {\n  var axisModel = axis.model;\n  var dim = axis.dim;\n\n  for (var i = 0; i < linksOption.length; i++) {\n    var linkOption = linksOption[i] || {};\n\n    if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)) {\n      return i;\n    }\n  }\n}\n\nfunction checkPropInLink(linkPropValue, axisPropValue) {\n  return linkPropValue === 'all' || isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0 || linkPropValue === axisPropValue;\n}\n\nfunction fixValue(axisModel) {\n  var axisInfo = getAxisInfo(axisModel);\n\n  if (!axisInfo) {\n    return;\n  }\n\n  var axisPointerModel = axisInfo.axisPointerModel;\n  var scale = axisInfo.axis.scale;\n  var option = axisPointerModel.option;\n  var status = axisPointerModel.get('status');\n  var value = axisPointerModel.get('value'); // Parse init value for category and time axis.\n\n  if (value != null) {\n    value = scale.parse(value);\n  }\n\n  var useHandle = isHandleTrigger(axisPointerModel); // If `handle` used, `axisPointer` will always be displayed, so value\n  // and status should be initialized.\n\n  if (status == null) {\n    option.status = useHandle ? 'show' : 'hide';\n  }\n\n  var extent = scale.getExtent().slice();\n  extent[0] > extent[1] && extent.reverse();\n\n  if ( // Pick a value on axis when initializing.\n  value == null // If both `handle` and `dataZoom` are used, value may be out of axis extent,\n  // where we should re-pick a value to keep `handle` displaying normally.\n  || value > extent[1]) {\n    // Make handle displayed on the end of the axis when init, which looks better.\n    value = extent[1];\n  }\n\n  if (value < extent[0]) {\n    value = extent[0];\n  }\n\n  option.value = value;\n\n  if (useHandle) {\n    option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';\n  }\n}\nfunction getAxisInfo(axisModel) {\n  var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;\n  return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];\n}\nfunction getAxisPointerModel(axisModel) {\n  var axisInfo = getAxisInfo(axisModel);\n  return axisInfo && axisInfo.axisPointerModel;\n}\n\nfunction isHandleTrigger(axisPointerModel) {\n  return !!axisPointerModel.get(['handle', 'show']);\n}\n/**\n * @param {module:echarts/model/Model} model\n * @return {string} unique key\n */\n\n\nfunction makeKey(model) {\n  return model.type + '||' + model.id;\n}\n\nvar axisPointerClazz = {};\n/**\n * Base class of AxisView.\n */\n\nvar AxisView =\n/** @class */\nfunction (_super) {\n  __extends(AxisView, _super);\n\n  function AxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = AxisView.type;\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  AxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n    // FIXME\n    // This process should proformed after coordinate systems updated\n    // (axis scale updated), and should be performed each time update.\n    // So put it here temporarily, although it is not appropriate to\n    // put a model-writing procedure in `view`.\n    this.axisPointerClass && fixValue(axisModel);\n\n    _super.prototype.render.apply(this, arguments);\n\n    this._doUpdateAxisPointerClass(axisModel, api, true);\n  };\n  /**\n   * Action handler.\n   */\n\n\n  AxisView.prototype.updateAxisPointer = function (axisModel, ecModel, api, payload) {\n    this._doUpdateAxisPointerClass(axisModel, api, false);\n  };\n  /**\n   * @override\n   */\n\n\n  AxisView.prototype.remove = function (ecModel, api) {\n    var axisPointer = this._axisPointer;\n    axisPointer && axisPointer.remove(api);\n  };\n  /**\n   * @override\n   */\n\n\n  AxisView.prototype.dispose = function (ecModel, api) {\n    this._disposeAxisPointer(api);\n\n    _super.prototype.dispose.apply(this, arguments);\n  };\n\n  AxisView.prototype._doUpdateAxisPointerClass = function (axisModel, api, forceRender) {\n    var Clazz = AxisView.getAxisPointerClass(this.axisPointerClass);\n\n    if (!Clazz) {\n      return;\n    }\n\n    var axisPointerModel = getAxisPointerModel(axisModel);\n    axisPointerModel ? (this._axisPointer || (this._axisPointer = new Clazz())).render(axisModel, axisPointerModel, api, forceRender) : this._disposeAxisPointer(api);\n  };\n\n  AxisView.prototype._disposeAxisPointer = function (api) {\n    this._axisPointer && this._axisPointer.dispose(api);\n    this._axisPointer = null;\n  };\n\n  AxisView.registerAxisPointerClass = function (type, clazz) {\n    if (\"development\" !== 'production') {\n      if (axisPointerClazz[type]) {\n        throw new Error('axisPointer ' + type + ' exists');\n      }\n    }\n\n    axisPointerClazz[type] = clazz;\n  };\n\n  AxisView.getAxisPointerClass = function (type) {\n    return type && axisPointerClazz[type];\n  };\n  AxisView.type = 'axis';\n  return AxisView;\n}(ComponentView);\n\nvar inner$6 = makeInner();\nfunction rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel) {\n  var axis = axisModel.axis;\n\n  if (axis.scale.isBlank()) {\n    return;\n  } // TODO: TYPE\n\n\n  var splitAreaModel = axisModel.getModel('splitArea');\n  var areaStyleModel = splitAreaModel.getModel('areaStyle');\n  var areaColors = areaStyleModel.get('color');\n  var gridRect = gridModel.coordinateSystem.getRect();\n  var ticksCoords = axis.getTicksCoords({\n    tickModel: splitAreaModel,\n    clamp: true\n  });\n\n  if (!ticksCoords.length) {\n    return;\n  } // For Making appropriate splitArea animation, the color and anid\n  // should be corresponding to previous one if possible.\n\n\n  var areaColorsLen = areaColors.length;\n  var lastSplitAreaColors = inner$6(axisView).splitAreaColors;\n  var newSplitAreaColors = createHashMap();\n  var colorIndex = 0;\n\n  if (lastSplitAreaColors) {\n    for (var i = 0; i < ticksCoords.length; i++) {\n      var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue);\n\n      if (cIndex != null) {\n        colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen;\n        break;\n      }\n    }\n  }\n\n  var prev = axis.toGlobalCoord(ticksCoords[0].coord);\n  var areaStyle = areaStyleModel.getAreaStyle();\n  areaColors = isArray(areaColors) ? areaColors : [areaColors];\n\n  for (var i = 1; i < ticksCoords.length; i++) {\n    var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n    var x = void 0;\n    var y = void 0;\n    var width = void 0;\n    var height = void 0;\n\n    if (axis.isHorizontal()) {\n      x = prev;\n      y = gridRect.y;\n      width = tickCoord - x;\n      height = gridRect.height;\n      prev = x + width;\n    } else {\n      x = gridRect.x;\n      y = prev;\n      width = gridRect.width;\n      height = tickCoord - y;\n      prev = y + height;\n    }\n\n    var tickValue = ticksCoords[i - 1].tickValue;\n    tickValue != null && newSplitAreaColors.set(tickValue, colorIndex);\n    axisGroup.add(new Rect({\n      anid: tickValue != null ? 'area_' + tickValue : null,\n      shape: {\n        x: x,\n        y: y,\n        width: width,\n        height: height\n      },\n      style: defaults({\n        fill: areaColors[colorIndex]\n      }, areaStyle),\n      autoBatch: true,\n      silent: true\n    }));\n    colorIndex = (colorIndex + 1) % areaColorsLen;\n  }\n\n  inner$6(axisView).splitAreaColors = newSplitAreaColors;\n}\nfunction rectCoordAxisHandleRemove(axisView) {\n  inner$6(axisView).splitAreaColors = null;\n}\n\nvar axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName'];\nvar selfBuilderAttrs = ['splitArea', 'splitLine', 'minorSplitLine'];\n\nvar CartesianAxisView =\n/** @class */\nfunction (_super) {\n  __extends(CartesianAxisView, _super);\n\n  function CartesianAxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CartesianAxisView.type;\n    _this.axisPointerClass = 'CartesianAxisPointer';\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  CartesianAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n    this.group.removeAll();\n    var oldAxisGroup = this._axisGroup;\n    this._axisGroup = new Group();\n    this.group.add(this._axisGroup);\n\n    if (!axisModel.get('show')) {\n      return;\n    }\n\n    var gridModel = axisModel.getCoordSysModel();\n    var layout = layout$1(gridModel, axisModel);\n    var axisBuilder = new AxisBuilder(axisModel, extend({\n      handleAutoShown: function (elementType) {\n        var cartesians = gridModel.coordinateSystem.getCartesians();\n\n        for (var i = 0; i < cartesians.length; i++) {\n          if (isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) {\n            // Still show axis tick or axisLine if other axis is value / log\n            return true;\n          }\n        } // Not show axisTick or axisLine if other axis is category / time\n\n\n        return false;\n      }\n    }, layout));\n    each(axisBuilderAttrs, axisBuilder.add, axisBuilder);\n\n    this._axisGroup.add(axisBuilder.getGroup());\n\n    each(selfBuilderAttrs, function (name) {\n      if (axisModel.get([name, 'show'])) {\n        axisElementBuilders[name](this, this._axisGroup, axisModel, gridModel);\n      }\n    }, this); // THIS is a special case for bar racing chart.\n    // Update the axis label from the natural initial layout to\n    // sorted layout should has no animation.\n\n    var isInitialSortFromBarRacing = payload && payload.type === 'changeAxisOrder' && payload.isInitSort;\n\n    if (!isInitialSortFromBarRacing) {\n      groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n    }\n\n    _super.prototype.render.call(this, axisModel, ecModel, api, payload);\n  };\n\n  CartesianAxisView.prototype.remove = function () {\n    rectCoordAxisHandleRemove(this);\n  };\n\n  CartesianAxisView.type = 'cartesianAxis';\n  return CartesianAxisView;\n}(AxisView);\n\nvar axisElementBuilders = {\n  splitLine: function (axisView, axisGroup, axisModel, gridModel) {\n    var axis = axisModel.axis;\n\n    if (axis.scale.isBlank()) {\n      return;\n    }\n\n    var splitLineModel = axisModel.getModel('splitLine');\n    var lineStyleModel = splitLineModel.getModel('lineStyle');\n    var lineColors = lineStyleModel.get('color');\n    lineColors = isArray(lineColors) ? lineColors : [lineColors];\n    var gridRect = gridModel.coordinateSystem.getRect();\n    var isHorizontal = axis.isHorizontal();\n    var lineCount = 0;\n    var ticksCoords = axis.getTicksCoords({\n      tickModel: splitLineModel\n    });\n    var p1 = [];\n    var p2 = [];\n    var lineStyle = lineStyleModel.getLineStyle();\n\n    for (var i = 0; i < ticksCoords.length; i++) {\n      var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n\n      if (isHorizontal) {\n        p1[0] = tickCoord;\n        p1[1] = gridRect.y;\n        p2[0] = tickCoord;\n        p2[1] = gridRect.y + gridRect.height;\n      } else {\n        p1[0] = gridRect.x;\n        p1[1] = tickCoord;\n        p2[0] = gridRect.x + gridRect.width;\n        p2[1] = tickCoord;\n      }\n\n      var colorIndex = lineCount++ % lineColors.length;\n      var tickValue = ticksCoords[i].tickValue;\n      var line = new Line({\n        anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null,\n        autoBatch: true,\n        shape: {\n          x1: p1[0],\n          y1: p1[1],\n          x2: p2[0],\n          y2: p2[1]\n        },\n        style: defaults({\n          stroke: lineColors[colorIndex]\n        }, lineStyle),\n        silent: true\n      });\n      subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n      axisGroup.add(line);\n    }\n  },\n  minorSplitLine: function (axisView, axisGroup, axisModel, gridModel) {\n    var axis = axisModel.axis;\n    var minorSplitLineModel = axisModel.getModel('minorSplitLine');\n    var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n    var gridRect = gridModel.coordinateSystem.getRect();\n    var isHorizontal = axis.isHorizontal();\n    var minorTicksCoords = axis.getMinorTicksCoords();\n\n    if (!minorTicksCoords.length) {\n      return;\n    }\n\n    var p1 = [];\n    var p2 = [];\n    var lineStyle = lineStyleModel.getLineStyle();\n\n    for (var i = 0; i < minorTicksCoords.length; i++) {\n      for (var k = 0; k < minorTicksCoords[i].length; k++) {\n        var tickCoord = axis.toGlobalCoord(minorTicksCoords[i][k].coord);\n\n        if (isHorizontal) {\n          p1[0] = tickCoord;\n          p1[1] = gridRect.y;\n          p2[0] = tickCoord;\n          p2[1] = gridRect.y + gridRect.height;\n        } else {\n          p1[0] = gridRect.x;\n          p1[1] = tickCoord;\n          p2[0] = gridRect.x + gridRect.width;\n          p2[1] = tickCoord;\n        }\n\n        var line = new Line({\n          anid: 'minor_line_' + minorTicksCoords[i][k].tickValue,\n          autoBatch: true,\n          shape: {\n            x1: p1[0],\n            y1: p1[1],\n            x2: p2[0],\n            y2: p2[1]\n          },\n          style: lineStyle,\n          silent: true\n        });\n        subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n        axisGroup.add(line);\n      }\n    }\n  },\n  splitArea: function (axisView, axisGroup, axisModel, gridModel) {\n    rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel);\n  }\n};\n\nvar CartesianXAxisView =\n/** @class */\nfunction (_super) {\n  __extends(CartesianXAxisView, _super);\n\n  function CartesianXAxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CartesianXAxisView.type;\n    return _this;\n  }\n\n  CartesianXAxisView.type = 'xAxis';\n  return CartesianXAxisView;\n}(CartesianAxisView);\n\nvar CartesianYAxisView =\n/** @class */\nfunction (_super) {\n  __extends(CartesianYAxisView, _super);\n\n  function CartesianYAxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CartesianXAxisView.type;\n    return _this;\n  }\n\n  CartesianYAxisView.type = 'yAxis';\n  return CartesianYAxisView;\n}(CartesianAxisView);\n\nvar GridView =\n/** @class */\nfunction (_super) {\n  __extends(GridView, _super);\n\n  function GridView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = 'grid';\n    return _this;\n  }\n\n  GridView.prototype.render = function (gridModel, ecModel) {\n    this.group.removeAll();\n\n    if (gridModel.get('show')) {\n      this.group.add(new Rect({\n        shape: gridModel.coordinateSystem.getRect(),\n        style: defaults({\n          fill: gridModel.get('backgroundColor')\n        }, gridModel.getItemStyle()),\n        silent: true,\n        z2: -1\n      }));\n    }\n  };\n\n  GridView.type = 'grid';\n  return GridView;\n}(ComponentView);\n\nvar extraOption = {\n  // gridIndex: 0,\n  // gridId: '',\n  offset: 0\n};\nfunction install$5(registers) {\n  registers.registerComponentView(GridView);\n  registers.registerComponentModel(GridModel);\n  registers.registerCoordinateSystem('cartesian2d', Grid);\n  axisModelCreator(registers, 'x', CartesianAxisModel, extraOption);\n  axisModelCreator(registers, 'y', CartesianAxisModel, extraOption);\n  registers.registerComponentView(CartesianXAxisView);\n  registers.registerComponentView(CartesianYAxisView);\n  registers.registerPreprocessor(function (option) {\n    // Only create grid when need\n    if (option.xAxis && option.yAxis && !option.grid) {\n      option.grid = {};\n    }\n  });\n}\n\nfunction install$6(registers) {\n  // In case developer forget to include grid component\n  use(install$5);\n  registers.registerSeriesModel(ScatterSeriesModel);\n  registers.registerChartView(ScatterView);\n  registers.registerLayout(pointsLayout('scatter'));\n}\n\nfunction radarLayout(ecModel) {\n  ecModel.eachSeriesByType('radar', function (seriesModel) {\n    var data = seriesModel.getData();\n    var points = [];\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (!coordSys) {\n      return;\n    }\n\n    var axes = coordSys.getIndicatorAxes();\n    each(axes, function (axis, axisIndex) {\n      data.each(data.mapDimension(axes[axisIndex].dim), function (val, dataIndex) {\n        points[dataIndex] = points[dataIndex] || [];\n        var point = coordSys.dataToPoint(val, axisIndex);\n        points[dataIndex][axisIndex] = isValidPoint(point) ? point : getValueMissingPoint(coordSys);\n      });\n    }); // Close polygon\n\n    data.each(function (idx) {\n      // TODO\n      // Is it appropriate to connect to the next data when some data is missing?\n      // Or, should trade it like `connectNull` in line chart?\n      var firstPoint = find(points[idx], function (point) {\n        return isValidPoint(point);\n      }) || getValueMissingPoint(coordSys); // Copy the first actual point to the end of the array\n\n      points[idx].push(firstPoint.slice());\n      data.setItemLayout(idx, points[idx]);\n    });\n  });\n}\n\nfunction isValidPoint(point) {\n  return !isNaN(point[0]) && !isNaN(point[1]);\n}\n\nfunction getValueMissingPoint(coordSys) {\n  // It is error-prone to input [NaN, NaN] into polygon, polygon.\n  // (probably cause problem when refreshing or animating)\n  return [coordSys.cx, coordSys.cy];\n}\n\nfunction radarBackwardCompat(option) {\n  var polarOptArr = option.polar;\n\n  if (polarOptArr) {\n    if (!isArray(polarOptArr)) {\n      polarOptArr = [polarOptArr];\n    }\n\n    var polarNotRadar_1 = [];\n    each(polarOptArr, function (polarOpt, idx) {\n      if (polarOpt.indicator) {\n        if (polarOpt.type && !polarOpt.shape) {\n          polarOpt.shape = polarOpt.type;\n        }\n\n        option.radar = option.radar || [];\n\n        if (!isArray(option.radar)) {\n          option.radar = [option.radar];\n        }\n\n        option.radar.push(polarOpt);\n      } else {\n        polarNotRadar_1.push(polarOpt);\n      }\n    });\n    option.polar = polarNotRadar_1;\n  }\n\n  each(option.series, function (seriesOpt) {\n    if (seriesOpt && seriesOpt.type === 'radar' && seriesOpt.polarIndex) {\n      seriesOpt.radarIndex = seriesOpt.polarIndex;\n    }\n  });\n}\n\nvar RadarView =\n/** @class */\nfunction (_super) {\n  __extends(RadarView, _super);\n\n  function RadarView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = RadarView.type;\n    return _this;\n  }\n\n  RadarView.prototype.render = function (seriesModel, ecModel, api) {\n    var polar = seriesModel.coordinateSystem;\n    var group = this.group;\n    var data = seriesModel.getData();\n    var oldData = this._data;\n\n    function createSymbol$1(data, idx) {\n      var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';\n\n      if (symbolType === 'none') {\n        return;\n      }\n\n      var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n      var symbolPath = createSymbol(symbolType, -1, -1, 2, 2);\n      var symbolRotate = data.getItemVisual(idx, 'symbolRotate') || 0;\n      symbolPath.attr({\n        style: {\n          strokeNoScale: true\n        },\n        z2: 100,\n        scaleX: symbolSize[0] / 2,\n        scaleY: symbolSize[1] / 2,\n        rotation: symbolRotate * Math.PI / 180 || 0\n      });\n      return symbolPath;\n    }\n\n    function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) {\n      // Simply rerender all\n      symbolGroup.removeAll();\n\n      for (var i = 0; i < newPoints.length - 1; i++) {\n        var symbolPath = createSymbol$1(data, idx);\n\n        if (symbolPath) {\n          symbolPath.__dimIdx = i;\n\n          if (oldPoints[i]) {\n            symbolPath.setPosition(oldPoints[i]);\n            graphic[isInit ? 'initProps' : 'updateProps'](symbolPath, {\n              x: newPoints[i][0],\n              y: newPoints[i][1]\n            }, seriesModel, idx);\n          } else {\n            symbolPath.setPosition(newPoints[i]);\n          }\n\n          symbolGroup.add(symbolPath);\n        }\n      }\n    }\n\n    function getInitialPoints(points) {\n      return map(points, function (pt) {\n        return [polar.cx, polar.cy];\n      });\n    }\n\n    data.diff(oldData).add(function (idx) {\n      var points = data.getItemLayout(idx);\n\n      if (!points) {\n        return;\n      }\n\n      var polygon = new Polygon();\n      var polyline = new Polyline();\n      var target = {\n        shape: {\n          points: points\n        }\n      };\n      polygon.shape.points = getInitialPoints(points);\n      polyline.shape.points = getInitialPoints(points);\n      initProps(polygon, target, seriesModel, idx);\n      initProps(polyline, target, seriesModel, idx);\n      var itemGroup = new Group();\n      var symbolGroup = new Group();\n      itemGroup.add(polyline);\n      itemGroup.add(polygon);\n      itemGroup.add(symbolGroup);\n      updateSymbols(polyline.shape.points, points, symbolGroup, data, idx, true);\n      data.setItemGraphicEl(idx, itemGroup);\n    }).update(function (newIdx, oldIdx) {\n      var itemGroup = oldData.getItemGraphicEl(oldIdx);\n      var polyline = itemGroup.childAt(0);\n      var polygon = itemGroup.childAt(1);\n      var symbolGroup = itemGroup.childAt(2);\n      var target = {\n        shape: {\n          points: data.getItemLayout(newIdx)\n        }\n      };\n\n      if (!target.shape.points) {\n        return;\n      }\n\n      updateSymbols(polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false);\n      saveOldStyle(polygon);\n      saveOldStyle(polyline);\n      updateProps(polyline, target, seriesModel);\n      updateProps(polygon, target, seriesModel);\n      data.setItemGraphicEl(newIdx, itemGroup);\n    }).remove(function (idx) {\n      group.remove(oldData.getItemGraphicEl(idx));\n    }).execute();\n    data.eachItemGraphicEl(function (itemGroup, idx) {\n      var itemModel = data.getItemModel(idx);\n      var polyline = itemGroup.childAt(0);\n      var polygon = itemGroup.childAt(1);\n      var symbolGroup = itemGroup.childAt(2); // Radar uses the visual encoded from itemStyle.\n\n      var itemStyle = data.getItemVisual(idx, 'style');\n      var color = itemStyle.fill;\n      group.add(itemGroup);\n      polyline.useStyle(defaults(itemModel.getModel('lineStyle').getLineStyle(), {\n        fill: 'none',\n        stroke: color\n      }));\n      setStatesStylesFromModel(polyline, itemModel, 'lineStyle');\n      setStatesStylesFromModel(polygon, itemModel, 'areaStyle');\n      var areaStyleModel = itemModel.getModel('areaStyle');\n      var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty();\n      polygon.ignore = polygonIgnore;\n      each(['emphasis', 'select', 'blur'], function (stateName) {\n        var stateModel = itemModel.getModel([stateName, 'areaStyle']);\n        var stateIgnore = stateModel.isEmpty() && stateModel.parentModel.isEmpty(); // Won't be ignore if normal state is not ignore.\n\n        polygon.ensureState(stateName).ignore = stateIgnore && polygonIgnore;\n      });\n      polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), {\n        fill: color,\n        opacity: 0.7,\n        decal: itemStyle.decal\n      }));\n      var emphasisModel = itemModel.getModel('emphasis');\n      var itemHoverStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n      symbolGroup.eachChild(function (symbolPath) {\n        if (symbolPath instanceof ZRImage) {\n          var pathStyle = symbolPath.style;\n          symbolPath.useStyle(extend({\n            // TODO other properties like x, y ?\n            image: pathStyle.image,\n            x: pathStyle.x,\n            y: pathStyle.y,\n            width: pathStyle.width,\n            height: pathStyle.height\n          }, itemStyle));\n        } else {\n          symbolPath.useStyle(itemStyle);\n          symbolPath.setColor(color);\n          symbolPath.style.strokeNoScale = true;\n        }\n\n        var pathEmphasisState = symbolPath.ensureState('emphasis');\n        pathEmphasisState.style = clone(itemHoverStyle);\n        var defaultText = data.getStore().get(data.getDimensionIndex(symbolPath.__dimIdx), idx);\n        (defaultText == null || isNaN(defaultText)) && (defaultText = '');\n        setLabelStyle(symbolPath, getLabelStatesModels(itemModel), {\n          labelFetcher: data.hostModel,\n          labelDataIndex: idx,\n          labelDimIndex: symbolPath.__dimIdx,\n          defaultText: defaultText,\n          inheritColor: color,\n          defaultOpacity: itemStyle.opacity\n        });\n      });\n      toggleHoverEmphasis(itemGroup, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n    });\n    this._data = data;\n  };\n\n  RadarView.prototype.remove = function () {\n    this.group.removeAll();\n    this._data = null;\n  };\n\n  RadarView.type = 'radar';\n  return RadarView;\n}(ChartView);\n\nvar RadarSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(RadarSeriesModel, _super);\n\n  function RadarSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = RadarSeriesModel.type;\n    _this.hasSymbolVisual = true;\n    return _this;\n  } // Overwrite\n\n\n  RadarSeriesModel.prototype.init = function (option) {\n    _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n    // Use a function instead of direct access because data reference may changed\n\n\n    this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n  };\n\n  RadarSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesDataSimply(this, {\n      generateCoord: 'indicator_',\n      generateCoordCount: Infinity\n    });\n  };\n\n  RadarSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    var data = this.getData();\n    var coordSys = this.coordinateSystem;\n    var indicatorAxes = coordSys.getIndicatorAxes();\n    var name = this.getData().getName(dataIndex);\n    var nameToDisplay = name === '' ? this.name : name;\n    var markerColor = retrieveVisualColorForTooltipMarker(this, dataIndex);\n    return createTooltipMarkup('section', {\n      header: nameToDisplay,\n      sortBlocks: true,\n      blocks: map(indicatorAxes, function (axis) {\n        var val = data.get(data.mapDimension(axis.dim), dataIndex);\n        return createTooltipMarkup('nameValue', {\n          markerType: 'subItem',\n          markerColor: markerColor,\n          name: axis.name,\n          value: val,\n          sortParam: val\n        });\n      })\n    });\n  };\n\n  RadarSeriesModel.prototype.getTooltipPosition = function (dataIndex) {\n    if (dataIndex != null) {\n      var data_1 = this.getData();\n      var coordSys = this.coordinateSystem;\n      var values = data_1.getValues(map(coordSys.dimensions, function (dim) {\n        return data_1.mapDimension(dim);\n      }), dataIndex);\n\n      for (var i = 0, len = values.length; i < len; i++) {\n        if (!isNaN(values[i])) {\n          var indicatorAxes = coordSys.getIndicatorAxes();\n          return coordSys.coordToPoint(indicatorAxes[i].dataToCoord(values[i]), i);\n        }\n      }\n    }\n  };\n\n  RadarSeriesModel.type = 'series.radar';\n  RadarSeriesModel.dependencies = ['radar'];\n  RadarSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    colorBy: 'data',\n    coordinateSystem: 'radar',\n    legendHoverLink: true,\n    radarIndex: 0,\n    lineStyle: {\n      width: 2,\n      type: 'solid',\n      join: 'round'\n    },\n    label: {\n      position: 'top'\n    },\n    // areaStyle: {\n    // },\n    // itemStyle: {}\n    symbolSize: 8 // symbolRotate: null\n\n  };\n  return RadarSeriesModel;\n}(SeriesModel);\n\nvar valueAxisDefault = axisDefault.value;\n\nfunction defaultsShow(opt, show) {\n  return defaults({\n    show: show\n  }, opt);\n}\n\nvar RadarModel =\n/** @class */\nfunction (_super) {\n  __extends(RadarModel, _super);\n\n  function RadarModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = RadarModel.type;\n    return _this;\n  }\n\n  RadarModel.prototype.optionUpdated = function () {\n    var boundaryGap = this.get('boundaryGap');\n    var splitNumber = this.get('splitNumber');\n    var scale = this.get('scale');\n    var axisLine = this.get('axisLine');\n    var axisTick = this.get('axisTick'); // let axisType = this.get('axisType');\n\n    var axisLabel = this.get('axisLabel');\n    var nameTextStyle = this.get('axisName');\n    var showName = this.get(['axisName', 'show']);\n    var nameFormatter = this.get(['axisName', 'formatter']);\n    var nameGap = this.get('axisNameGap');\n    var triggerEvent = this.get('triggerEvent');\n    var indicatorModels = map(this.get('indicator') || [], function (indicatorOpt) {\n      // PENDING\n      if (indicatorOpt.max != null && indicatorOpt.max > 0 && !indicatorOpt.min) {\n        indicatorOpt.min = 0;\n      } else if (indicatorOpt.min != null && indicatorOpt.min < 0 && !indicatorOpt.max) {\n        indicatorOpt.max = 0;\n      }\n\n      var iNameTextStyle = nameTextStyle;\n\n      if (indicatorOpt.color != null) {\n        iNameTextStyle = defaults({\n          color: indicatorOpt.color\n        }, nameTextStyle);\n      } // Use same configuration\n\n\n      var innerIndicatorOpt = merge(clone(indicatorOpt), {\n        boundaryGap: boundaryGap,\n        splitNumber: splitNumber,\n        scale: scale,\n        axisLine: axisLine,\n        axisTick: axisTick,\n        // axisType: axisType,\n        axisLabel: axisLabel,\n        // Compatible with 2 and use text\n        name: indicatorOpt.text,\n        showName: showName,\n        nameLocation: 'end',\n        nameGap: nameGap,\n        // min: 0,\n        nameTextStyle: iNameTextStyle,\n        triggerEvent: triggerEvent\n      }, false);\n\n      if (isString(nameFormatter)) {\n        var indName = innerIndicatorOpt.name;\n        innerIndicatorOpt.name = nameFormatter.replace('{value}', indName != null ? indName : '');\n      } else if (isFunction(nameFormatter)) {\n        innerIndicatorOpt.name = nameFormatter(innerIndicatorOpt.name, innerIndicatorOpt);\n      }\n\n      var model = new Model(innerIndicatorOpt, null, this.ecModel);\n      mixin(model, AxisModelCommonMixin.prototype); // For triggerEvent.\n\n      model.mainType = 'radar';\n      model.componentIndex = this.componentIndex;\n      return model;\n    }, this);\n    this._indicatorModels = indicatorModels;\n  };\n\n  RadarModel.prototype.getIndicatorModels = function () {\n    return this._indicatorModels;\n  };\n\n  RadarModel.type = 'radar';\n  RadarModel.defaultOption = {\n    // zlevel: 0,\n    z: 0,\n    center: ['50%', '50%'],\n    radius: '75%',\n    startAngle: 90,\n    axisName: {\n      show: true // formatter: null\n      // textStyle: {}\n\n    },\n    boundaryGap: [0, 0],\n    splitNumber: 5,\n    axisNameGap: 15,\n    scale: false,\n    // Polygon or circle\n    shape: 'polygon',\n    axisLine: merge({\n      lineStyle: {\n        color: '#bbb'\n      }\n    }, valueAxisDefault.axisLine),\n    axisLabel: defaultsShow(valueAxisDefault.axisLabel, false),\n    axisTick: defaultsShow(valueAxisDefault.axisTick, false),\n    // axisType: 'value',\n    splitLine: defaultsShow(valueAxisDefault.splitLine, true),\n    splitArea: defaultsShow(valueAxisDefault.splitArea, true),\n    // {text, min, max}\n    indicator: []\n  };\n  return RadarModel;\n}(ComponentModel);\n\nvar axisBuilderAttrs$1 = ['axisLine', 'axisTickLabel', 'axisName'];\n\nvar RadarView$1 =\n/** @class */\nfunction (_super) {\n  __extends(RadarView, _super);\n\n  function RadarView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = RadarView.type;\n    return _this;\n  }\n\n  RadarView.prototype.render = function (radarModel, ecModel, api) {\n    var group = this.group;\n    group.removeAll();\n\n    this._buildAxes(radarModel);\n\n    this._buildSplitLineAndArea(radarModel);\n  };\n\n  RadarView.prototype._buildAxes = function (radarModel) {\n    var radar = radarModel.coordinateSystem;\n    var indicatorAxes = radar.getIndicatorAxes();\n    var axisBuilders = map(indicatorAxes, function (indicatorAxis) {\n      var axisName = indicatorAxis.model.get('showName') ? indicatorAxis.name : ''; // hide name\n\n      var axisBuilder = new AxisBuilder(indicatorAxis.model, {\n        axisName: axisName,\n        position: [radar.cx, radar.cy],\n        rotation: indicatorAxis.angle,\n        labelDirection: -1,\n        tickDirection: -1,\n        nameDirection: 1\n      });\n      return axisBuilder;\n    });\n    each(axisBuilders, function (axisBuilder) {\n      each(axisBuilderAttrs$1, axisBuilder.add, axisBuilder);\n      this.group.add(axisBuilder.getGroup());\n    }, this);\n  };\n\n  RadarView.prototype._buildSplitLineAndArea = function (radarModel) {\n    var radar = radarModel.coordinateSystem;\n    var indicatorAxes = radar.getIndicatorAxes();\n\n    if (!indicatorAxes.length) {\n      return;\n    }\n\n    var shape = radarModel.get('shape');\n    var splitLineModel = radarModel.getModel('splitLine');\n    var splitAreaModel = radarModel.getModel('splitArea');\n    var lineStyleModel = splitLineModel.getModel('lineStyle');\n    var areaStyleModel = splitAreaModel.getModel('areaStyle');\n    var showSplitLine = splitLineModel.get('show');\n    var showSplitArea = splitAreaModel.get('show');\n    var splitLineColors = lineStyleModel.get('color');\n    var splitAreaColors = areaStyleModel.get('color');\n    var splitLineColorsArr = isArray(splitLineColors) ? splitLineColors : [splitLineColors];\n    var splitAreaColorsArr = isArray(splitAreaColors) ? splitAreaColors : [splitAreaColors];\n    var splitLines = [];\n    var splitAreas = [];\n\n    function getColorIndex(areaOrLine, areaOrLineColorList, idx) {\n      var colorIndex = idx % areaOrLineColorList.length;\n      areaOrLine[colorIndex] = areaOrLine[colorIndex] || [];\n      return colorIndex;\n    }\n\n    if (shape === 'circle') {\n      var ticksRadius = indicatorAxes[0].getTicksCoords();\n      var cx = radar.cx;\n      var cy = radar.cy;\n\n      for (var i = 0; i < ticksRadius.length; i++) {\n        if (showSplitLine) {\n          var colorIndex = getColorIndex(splitLines, splitLineColorsArr, i);\n          splitLines[colorIndex].push(new Circle({\n            shape: {\n              cx: cx,\n              cy: cy,\n              r: ticksRadius[i].coord\n            }\n          }));\n        }\n\n        if (showSplitArea && i < ticksRadius.length - 1) {\n          var colorIndex = getColorIndex(splitAreas, splitAreaColorsArr, i);\n          splitAreas[colorIndex].push(new Ring({\n            shape: {\n              cx: cx,\n              cy: cy,\n              r0: ticksRadius[i].coord,\n              r: ticksRadius[i + 1].coord\n            }\n          }));\n        }\n      }\n    } // Polyyon\n    else {\n        var realSplitNumber_1;\n        var axesTicksPoints = map(indicatorAxes, function (indicatorAxis, idx) {\n          var ticksCoords = indicatorAxis.getTicksCoords();\n          realSplitNumber_1 = realSplitNumber_1 == null ? ticksCoords.length - 1 : Math.min(ticksCoords.length - 1, realSplitNumber_1);\n          return map(ticksCoords, function (tickCoord) {\n            return radar.coordToPoint(tickCoord.coord, idx);\n          });\n        });\n        var prevPoints = [];\n\n        for (var i = 0; i <= realSplitNumber_1; i++) {\n          var points = [];\n\n          for (var j = 0; j < indicatorAxes.length; j++) {\n            points.push(axesTicksPoints[j][i]);\n          } // Close\n\n\n          if (points[0]) {\n            points.push(points[0].slice());\n          } else {\n            if (\"development\" !== 'production') {\n              console.error('Can\\'t draw value axis ' + i);\n            }\n          }\n\n          if (showSplitLine) {\n            var colorIndex = getColorIndex(splitLines, splitLineColorsArr, i);\n            splitLines[colorIndex].push(new Polyline({\n              shape: {\n                points: points\n              }\n            }));\n          }\n\n          if (showSplitArea && prevPoints) {\n            var colorIndex = getColorIndex(splitAreas, splitAreaColorsArr, i - 1);\n            splitAreas[colorIndex].push(new Polygon({\n              shape: {\n                points: points.concat(prevPoints)\n              }\n            }));\n          }\n\n          prevPoints = points.slice().reverse();\n        }\n      }\n\n    var lineStyle = lineStyleModel.getLineStyle();\n    var areaStyle = areaStyleModel.getAreaStyle(); // Add splitArea before splitLine\n\n    each(splitAreas, function (splitAreas, idx) {\n      this.group.add(mergePath$1(splitAreas, {\n        style: defaults({\n          stroke: 'none',\n          fill: splitAreaColorsArr[idx % splitAreaColorsArr.length]\n        }, areaStyle),\n        silent: true\n      }));\n    }, this);\n    each(splitLines, function (splitLines, idx) {\n      this.group.add(mergePath$1(splitLines, {\n        style: defaults({\n          fill: 'none',\n          stroke: splitLineColorsArr[idx % splitLineColorsArr.length]\n        }, lineStyle),\n        silent: true\n      }));\n    }, this);\n  };\n\n  RadarView.type = 'radar';\n  return RadarView;\n}(ComponentView);\n\nvar IndicatorAxis =\n/** @class */\nfunction (_super) {\n  __extends(IndicatorAxis, _super);\n\n  function IndicatorAxis(dim, scale, radiusExtent) {\n    var _this = _super.call(this, dim, scale, radiusExtent) || this;\n\n    _this.type = 'value';\n    _this.angle = 0;\n    _this.name = '';\n    return _this;\n  }\n\n  return IndicatorAxis;\n}(Axis);\n\nvar Radar =\n/** @class */\nfunction () {\n  function Radar(radarModel, ecModel, api) {\n    /**\n     *\n     * Radar dimensions\n     */\n    this.dimensions = [];\n    this._model = radarModel;\n    this._indicatorAxes = map(radarModel.getIndicatorModels(), function (indicatorModel, idx) {\n      var dim = 'indicator_' + idx;\n      var indicatorAxis = new IndicatorAxis(dim, new IntervalScale() // (indicatorModel.get('axisType') === 'log') ? new LogScale() : new IntervalScale()\n      );\n      indicatorAxis.name = indicatorModel.get('name'); // Inject model and axis\n\n      indicatorAxis.model = indicatorModel;\n      indicatorModel.axis = indicatorAxis;\n      this.dimensions.push(dim);\n      return indicatorAxis;\n    }, this);\n    this.resize(radarModel, api);\n  }\n\n  Radar.prototype.getIndicatorAxes = function () {\n    return this._indicatorAxes;\n  };\n\n  Radar.prototype.dataToPoint = function (value, indicatorIndex) {\n    var indicatorAxis = this._indicatorAxes[indicatorIndex];\n    return this.coordToPoint(indicatorAxis.dataToCoord(value), indicatorIndex);\n  }; // TODO: API should be coordToPoint([coord, indicatorIndex])\n\n\n  Radar.prototype.coordToPoint = function (coord, indicatorIndex) {\n    var indicatorAxis = this._indicatorAxes[indicatorIndex];\n    var angle = indicatorAxis.angle;\n    var x = this.cx + coord * Math.cos(angle);\n    var y = this.cy - coord * Math.sin(angle);\n    return [x, y];\n  };\n\n  Radar.prototype.pointToData = function (pt) {\n    var dx = pt[0] - this.cx;\n    var dy = pt[1] - this.cy;\n    var radius = Math.sqrt(dx * dx + dy * dy);\n    dx /= radius;\n    dy /= radius;\n    var radian = Math.atan2(-dy, dx); // Find the closest angle\n    // FIXME index can calculated directly\n\n    var minRadianDiff = Infinity;\n    var closestAxis;\n    var closestAxisIdx = -1;\n\n    for (var i = 0; i < this._indicatorAxes.length; i++) {\n      var indicatorAxis = this._indicatorAxes[i];\n      var diff = Math.abs(radian - indicatorAxis.angle);\n\n      if (diff < minRadianDiff) {\n        closestAxis = indicatorAxis;\n        closestAxisIdx = i;\n        minRadianDiff = diff;\n      }\n    }\n\n    return [closestAxisIdx, +(closestAxis && closestAxis.coordToData(radius))];\n  };\n\n  Radar.prototype.resize = function (radarModel, api) {\n    var center = radarModel.get('center');\n    var viewWidth = api.getWidth();\n    var viewHeight = api.getHeight();\n    var viewSize = Math.min(viewWidth, viewHeight) / 2;\n    this.cx = parsePercent$1(center[0], viewWidth);\n    this.cy = parsePercent$1(center[1], viewHeight);\n    this.startAngle = radarModel.get('startAngle') * Math.PI / 180; // radius may be single value like `20`, `'80%'`, or array like `[10, '80%']`\n\n    var radius = radarModel.get('radius');\n\n    if (isString(radius) || isNumber(radius)) {\n      radius = [0, radius];\n    }\n\n    this.r0 = parsePercent$1(radius[0], viewSize);\n    this.r = parsePercent$1(radius[1], viewSize);\n    each(this._indicatorAxes, function (indicatorAxis, idx) {\n      indicatorAxis.setExtent(this.r0, this.r);\n      var angle = this.startAngle + idx * Math.PI * 2 / this._indicatorAxes.length; // Normalize to [-PI, PI]\n\n      angle = Math.atan2(Math.sin(angle), Math.cos(angle));\n      indicatorAxis.angle = angle;\n    }, this);\n  };\n\n  Radar.prototype.update = function (ecModel, api) {\n    var indicatorAxes = this._indicatorAxes;\n    var radarModel = this._model;\n    each(indicatorAxes, function (indicatorAxis) {\n      indicatorAxis.scale.setExtent(Infinity, -Infinity);\n    });\n    ecModel.eachSeriesByType('radar', function (radarSeries, idx) {\n      if (radarSeries.get('coordinateSystem') !== 'radar' // @ts-ignore\n      || ecModel.getComponent('radar', radarSeries.get('radarIndex')) !== radarModel) {\n        return;\n      }\n\n      var data = radarSeries.getData();\n      each(indicatorAxes, function (indicatorAxis) {\n        indicatorAxis.scale.unionExtentFromData(data, data.mapDimension(indicatorAxis.dim));\n      });\n    }, this);\n    var splitNumber = radarModel.get('splitNumber');\n    var dummyScale = new IntervalScale();\n    dummyScale.setExtent(0, splitNumber);\n    dummyScale.setInterval(1); // Force all the axis fixing the maxSplitNumber.\n\n    each(indicatorAxes, function (indicatorAxis, idx) {\n      alignScaleTicks(indicatorAxis.scale, indicatorAxis.model, dummyScale);\n    });\n  };\n\n  Radar.prototype.convertToPixel = function (ecModel, finder, value) {\n    console.warn('Not implemented.');\n    return null;\n  };\n\n  Radar.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n    console.warn('Not implemented.');\n    return null;\n  };\n\n  Radar.prototype.containPoint = function (point) {\n    console.warn('Not implemented.');\n    return false;\n  };\n\n  Radar.create = function (ecModel, api) {\n    var radarList = [];\n    ecModel.eachComponent('radar', function (radarModel) {\n      var radar = new Radar(radarModel, ecModel, api);\n      radarList.push(radar);\n      radarModel.coordinateSystem = radar;\n    });\n    ecModel.eachSeriesByType('radar', function (radarSeries) {\n      if (radarSeries.get('coordinateSystem') === 'radar') {\n        // Inject coordinate system\n        // @ts-ignore\n        radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0];\n      }\n    });\n    return radarList;\n  };\n  /**\n   * Radar dimensions is based on the data\n   */\n\n\n  Radar.dimensions = [];\n  return Radar;\n}();\n\nfunction install$7(registers) {\n  registers.registerCoordinateSystem('radar', Radar);\n  registers.registerComponentModel(RadarModel);\n  registers.registerComponentView(RadarView$1);\n  registers.registerVisual({\n    seriesType: 'radar',\n    reset: function (seriesModel) {\n      var data = seriesModel.getData(); // itemVisual symbol is for selected data\n\n      data.each(function (idx) {\n        data.setItemVisual(idx, 'legendIcon', 'roundRect');\n      }); // visual is for unselected data\n\n      data.setVisual('legendIcon', 'roundRect');\n    }\n  });\n}\n\nfunction install$8(registers) {\n  use(install$7);\n  registers.registerChartView(RadarView);\n  registers.registerSeriesModel(RadarSeriesModel);\n  registers.registerLayout(radarLayout);\n  registers.registerProcessor(dataFilter('radar'));\n  registers.registerPreprocessor(radarBackwardCompat);\n}\n\nvar ATTR = '\\0_ec_interaction_mutex';\nfunction take(zr, resourceKey, userKey) {\n  var store = getStore(zr);\n  store[resourceKey] = userKey;\n}\nfunction release(zr, resourceKey, userKey) {\n  var store = getStore(zr);\n  var uKey = store[resourceKey];\n\n  if (uKey === userKey) {\n    store[resourceKey] = null;\n  }\n}\nfunction isTaken(zr, resourceKey) {\n  return !!getStore(zr)[resourceKey];\n}\n\nfunction getStore(zr) {\n  return zr[ATTR] || (zr[ATTR] = {});\n}\n/**\n * payload: {\n *     type: 'takeGlobalCursor',\n *     key: 'dataZoomSelect', or 'brush', or ...,\n *         If no userKey, release global cursor.\n * }\n */\n// TODO: SELF REGISTERED.\n\n\nregisterAction({\n  type: 'takeGlobalCursor',\n  event: 'globalCursorTaken',\n  update: 'update'\n}, noop);\n\nvar RoamController =\n/** @class */\nfunction (_super) {\n  __extends(RoamController, _super);\n\n  function RoamController(zr) {\n    var _this = _super.call(this) || this;\n\n    _this._zr = zr; // Avoid two roamController bind the same handler\n\n    var mousedownHandler = bind(_this._mousedownHandler, _this);\n    var mousemoveHandler = bind(_this._mousemoveHandler, _this);\n    var mouseupHandler = bind(_this._mouseupHandler, _this);\n    var mousewheelHandler = bind(_this._mousewheelHandler, _this);\n    var pinchHandler = bind(_this._pinchHandler, _this);\n    /**\n     * Notice: only enable needed types. For example, if 'zoom'\n     * is not needed, 'zoom' should not be enabled, otherwise\n     * default mousewheel behaviour (scroll page) will be disabled.\n     */\n\n    _this.enable = function (controlType, opt) {\n      // Disable previous first\n      this.disable();\n      this._opt = defaults(clone(opt) || {}, {\n        zoomOnMouseWheel: true,\n        moveOnMouseMove: true,\n        // By default, wheel do not trigger move.\n        moveOnMouseWheel: false,\n        preventDefaultMouseMove: true\n      });\n\n      if (controlType == null) {\n        controlType = true;\n      }\n\n      if (controlType === true || controlType === 'move' || controlType === 'pan') {\n        zr.on('mousedown', mousedownHandler);\n        zr.on('mousemove', mousemoveHandler);\n        zr.on('mouseup', mouseupHandler);\n      }\n\n      if (controlType === true || controlType === 'scale' || controlType === 'zoom') {\n        zr.on('mousewheel', mousewheelHandler);\n        zr.on('pinch', pinchHandler);\n      }\n    };\n\n    _this.disable = function () {\n      zr.off('mousedown', mousedownHandler);\n      zr.off('mousemove', mousemoveHandler);\n      zr.off('mouseup', mouseupHandler);\n      zr.off('mousewheel', mousewheelHandler);\n      zr.off('pinch', pinchHandler);\n    };\n\n    return _this;\n  }\n\n  RoamController.prototype.isDragging = function () {\n    return this._dragging;\n  };\n\n  RoamController.prototype.isPinching = function () {\n    return this._pinching;\n  };\n\n  RoamController.prototype.setPointerChecker = function (pointerChecker) {\n    this.pointerChecker = pointerChecker;\n  };\n\n  RoamController.prototype.dispose = function () {\n    this.disable();\n  };\n\n  RoamController.prototype._mousedownHandler = function (e) {\n    if (isMiddleOrRightButtonOnMouseUpDown(e)) {\n      return;\n    }\n\n    var el = e.target;\n\n    while (el) {\n      if (el.draggable) {\n        return;\n      } // check if host is draggable\n\n\n      el = el.__hostTarget || el.parent;\n    }\n\n    var x = e.offsetX;\n    var y = e.offsetY; // Only check on mosedown, but not mousemove.\n    // Mouse can be out of target when mouse moving.\n\n    if (this.pointerChecker && this.pointerChecker(e, x, y)) {\n      this._x = x;\n      this._y = y;\n      this._dragging = true;\n    }\n  };\n\n  RoamController.prototype._mousemoveHandler = function (e) {\n    if (!this._dragging || !isAvailableBehavior('moveOnMouseMove', e, this._opt) || e.gestureEvent === 'pinch' || isTaken(this._zr, 'globalPan')) {\n      return;\n    }\n\n    var x = e.offsetX;\n    var y = e.offsetY;\n    var oldX = this._x;\n    var oldY = this._y;\n    var dx = x - oldX;\n    var dy = y - oldY;\n    this._x = x;\n    this._y = y;\n    this._opt.preventDefaultMouseMove && stop(e.event);\n    trigger(this, 'pan', 'moveOnMouseMove', e, {\n      dx: dx,\n      dy: dy,\n      oldX: oldX,\n      oldY: oldY,\n      newX: x,\n      newY: y,\n      isAvailableBehavior: null\n    });\n  };\n\n  RoamController.prototype._mouseupHandler = function (e) {\n    if (!isMiddleOrRightButtonOnMouseUpDown(e)) {\n      this._dragging = false;\n    }\n  };\n\n  RoamController.prototype._mousewheelHandler = function (e) {\n    var shouldZoom = isAvailableBehavior('zoomOnMouseWheel', e, this._opt);\n    var shouldMove = isAvailableBehavior('moveOnMouseWheel', e, this._opt);\n    var wheelDelta = e.wheelDelta;\n    var absWheelDeltaDelta = Math.abs(wheelDelta);\n    var originX = e.offsetX;\n    var originY = e.offsetY; // wheelDelta maybe -0 in chrome mac.\n\n    if (wheelDelta === 0 || !shouldZoom && !shouldMove) {\n      return;\n    } // If both `shouldZoom` and `shouldMove` is true, trigger\n    // their event both, and the final behavior is determined\n    // by event listener themselves.\n\n\n    if (shouldZoom) {\n      // Convenience:\n      // Mac and VM Windows on Mac: scroll up: zoom out.\n      // Windows: scroll up: zoom in.\n      // FIXME: Should do more test in different environment.\n      // wheelDelta is too complicated in difference nvironment\n      // (https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel),\n      // although it has been normallized by zrender.\n      // wheelDelta of mouse wheel is bigger than touch pad.\n      var factor = absWheelDeltaDelta > 3 ? 1.4 : absWheelDeltaDelta > 1 ? 1.2 : 1.1;\n      var scale = wheelDelta > 0 ? factor : 1 / factor;\n      checkPointerAndTrigger(this, 'zoom', 'zoomOnMouseWheel', e, {\n        scale: scale,\n        originX: originX,\n        originY: originY,\n        isAvailableBehavior: null\n      });\n    }\n\n    if (shouldMove) {\n      // FIXME: Should do more test in different environment.\n      var absDelta = Math.abs(wheelDelta); // wheelDelta of mouse wheel is bigger than touch pad.\n\n      var scrollDelta = (wheelDelta > 0 ? 1 : -1) * (absDelta > 3 ? 0.4 : absDelta > 1 ? 0.15 : 0.05);\n      checkPointerAndTrigger(this, 'scrollMove', 'moveOnMouseWheel', e, {\n        scrollDelta: scrollDelta,\n        originX: originX,\n        originY: originY,\n        isAvailableBehavior: null\n      });\n    }\n  };\n\n  RoamController.prototype._pinchHandler = function (e) {\n    if (isTaken(this._zr, 'globalPan')) {\n      return;\n    }\n\n    var scale = e.pinchScale > 1 ? 1.1 : 1 / 1.1;\n    checkPointerAndTrigger(this, 'zoom', null, e, {\n      scale: scale,\n      originX: e.pinchX,\n      originY: e.pinchY,\n      isAvailableBehavior: null\n    });\n  };\n\n  return RoamController;\n}(Eventful);\n\nfunction checkPointerAndTrigger(controller, eventName, behaviorToCheck, e, contollerEvent) {\n  if (controller.pointerChecker && controller.pointerChecker(e, contollerEvent.originX, contollerEvent.originY)) {\n    // When mouse is out of roamController rect,\n    // default befavoius should not be be disabled, otherwise\n    // page sliding is disabled, contrary to expectation.\n    stop(e.event);\n    trigger(controller, eventName, behaviorToCheck, e, contollerEvent);\n  }\n}\n\nfunction trigger(controller, eventName, behaviorToCheck, e, contollerEvent) {\n  // Also provide behavior checker for event listener, for some case that\n  // multiple components share one listener.\n  contollerEvent.isAvailableBehavior = bind(isAvailableBehavior, null, behaviorToCheck, e); // TODO should not have type issue.\n\n  controller.trigger(eventName, contollerEvent);\n} // settings: {\n//     zoomOnMouseWheel\n//     moveOnMouseMove\n//     moveOnMouseWheel\n// }\n// The value can be: true / false / 'shift' / 'ctrl' / 'alt'.\n\n\nfunction isAvailableBehavior(behaviorToCheck, e, settings) {\n  var setting = settings[behaviorToCheck];\n  return !behaviorToCheck || setting && (!isString(setting) || e.event[setting + 'Key']);\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n/**\n * For geo and graph.\n */\nfunction updateViewOnPan(controllerHost, dx, dy) {\n  var target = controllerHost.target;\n  target.x += dx;\n  target.y += dy;\n  target.dirty();\n}\n/**\n * For geo and graph.\n */\n\nfunction updateViewOnZoom(controllerHost, zoomDelta, zoomX, zoomY) {\n  var target = controllerHost.target;\n  var zoomLimit = controllerHost.zoomLimit;\n  var newZoom = controllerHost.zoom = controllerHost.zoom || 1;\n  newZoom *= zoomDelta;\n\n  if (zoomLimit) {\n    var zoomMin = zoomLimit.min || 0;\n    var zoomMax = zoomLimit.max || Infinity;\n    newZoom = Math.max(Math.min(zoomMax, newZoom), zoomMin);\n  }\n\n  var zoomScale = newZoom / controllerHost.zoom;\n  controllerHost.zoom = newZoom; // Keep the mouse center when scaling\n\n  target.x -= (zoomX - target.x) * (zoomScale - 1);\n  target.y -= (zoomY - target.y) * (zoomScale - 1);\n  target.scaleX *= zoomScale;\n  target.scaleY *= zoomScale;\n  target.dirty();\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar IRRELEVANT_EXCLUDES = {\n  'axisPointer': 1,\n  'tooltip': 1,\n  'brush': 1\n};\n/**\n * Avoid that: mouse click on a elements that is over geo or graph,\n * but roam is triggered.\n */\n\nfunction onIrrelevantElement(e, api, targetCoordSysModel) {\n  var model = api.getComponentByElement(e.topTarget); // If model is axisModel, it works only if it is injected with coordinateSystem.\n\n  var coordSys = model && model.coordinateSystem;\n  return model && model !== targetCoordSysModel && !IRRELEVANT_EXCLUDES.hasOwnProperty(model.mainType) && coordSys && coordSys.model !== targetCoordSysModel;\n}\n\nfunction parseXML(svg) {\n    if (isString(svg)) {\n        var parser = new DOMParser();\n        svg = parser.parseFromString(svg, 'text/xml');\n    }\n    var svgNode = svg;\n    if (svgNode.nodeType === 9) {\n        svgNode = svgNode.firstChild;\n    }\n    while (svgNode.nodeName.toLowerCase() !== 'svg' || svgNode.nodeType !== 1) {\n        svgNode = svgNode.nextSibling;\n    }\n    return svgNode;\n}\n\nvar nodeParsers;\nvar INHERITABLE_STYLE_ATTRIBUTES_MAP = {\n    'fill': 'fill',\n    'stroke': 'stroke',\n    'stroke-width': 'lineWidth',\n    'opacity': 'opacity',\n    'fill-opacity': 'fillOpacity',\n    'stroke-opacity': 'strokeOpacity',\n    'stroke-dasharray': 'lineDash',\n    'stroke-dashoffset': 'lineDashOffset',\n    'stroke-linecap': 'lineCap',\n    'stroke-linejoin': 'lineJoin',\n    'stroke-miterlimit': 'miterLimit',\n    'font-family': 'fontFamily',\n    'font-size': 'fontSize',\n    'font-style': 'fontStyle',\n    'font-weight': 'fontWeight',\n    'text-anchor': 'textAlign',\n    'visibility': 'visibility',\n    'display': 'display'\n};\nvar INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS = keys(INHERITABLE_STYLE_ATTRIBUTES_MAP);\nvar SELF_STYLE_ATTRIBUTES_MAP = {\n    'alignment-baseline': 'textBaseline',\n    'stop-color': 'stopColor'\n};\nvar SELF_STYLE_ATTRIBUTES_MAP_KEYS = keys(SELF_STYLE_ATTRIBUTES_MAP);\nvar SVGParser = (function () {\n    function SVGParser() {\n        this._defs = {};\n        this._root = null;\n    }\n    SVGParser.prototype.parse = function (xml, opt) {\n        opt = opt || {};\n        var svg = parseXML(xml);\n        if (\"development\" !== 'production') {\n            if (!svg) {\n                throw new Error('Illegal svg');\n            }\n        }\n        this._defsUsePending = [];\n        var root = new Group();\n        this._root = root;\n        var named = [];\n        var viewBox = svg.getAttribute('viewBox') || '';\n        var width = parseFloat((svg.getAttribute('width') || opt.width));\n        var height = parseFloat((svg.getAttribute('height') || opt.height));\n        isNaN(width) && (width = null);\n        isNaN(height) && (height = null);\n        parseAttributes(svg, root, null, true, false);\n        var child = svg.firstChild;\n        while (child) {\n            this._parseNode(child, root, named, null, false, false);\n            child = child.nextSibling;\n        }\n        applyDefs(this._defs, this._defsUsePending);\n        this._defsUsePending = [];\n        var viewBoxRect;\n        var viewBoxTransform;\n        if (viewBox) {\n            var viewBoxArr = splitNumberSequence(viewBox);\n            if (viewBoxArr.length >= 4) {\n                viewBoxRect = {\n                    x: parseFloat((viewBoxArr[0] || 0)),\n                    y: parseFloat((viewBoxArr[1] || 0)),\n                    width: parseFloat(viewBoxArr[2]),\n                    height: parseFloat(viewBoxArr[3])\n                };\n            }\n        }\n        if (viewBoxRect && width != null && height != null) {\n            viewBoxTransform = makeViewBoxTransform(viewBoxRect, { x: 0, y: 0, width: width, height: height });\n            if (!opt.ignoreViewBox) {\n                var elRoot = root;\n                root = new Group();\n                root.add(elRoot);\n                elRoot.scaleX = elRoot.scaleY = viewBoxTransform.scale;\n                elRoot.x = viewBoxTransform.x;\n                elRoot.y = viewBoxTransform.y;\n            }\n        }\n        if (!opt.ignoreRootClip && width != null && height != null) {\n            root.setClipPath(new Rect({\n                shape: { x: 0, y: 0, width: width, height: height }\n            }));\n        }\n        return {\n            root: root,\n            width: width,\n            height: height,\n            viewBoxRect: viewBoxRect,\n            viewBoxTransform: viewBoxTransform,\n            named: named\n        };\n    };\n    SVGParser.prototype._parseNode = function (xmlNode, parentGroup, named, namedFrom, isInDefs, isInText) {\n        var nodeName = xmlNode.nodeName.toLowerCase();\n        var el;\n        var namedFromForSub = namedFrom;\n        if (nodeName === 'defs') {\n            isInDefs = true;\n        }\n        if (nodeName === 'text') {\n            isInText = true;\n        }\n        if (nodeName === 'defs' || nodeName === 'switch') {\n            el = parentGroup;\n        }\n        else {\n            if (!isInDefs) {\n                var parser_1 = nodeParsers[nodeName];\n                if (parser_1 && hasOwn(nodeParsers, nodeName)) {\n                    el = parser_1.call(this, xmlNode, parentGroup);\n                    var nameAttr = xmlNode.getAttribute('name');\n                    if (nameAttr) {\n                        var newNamed = {\n                            name: nameAttr,\n                            namedFrom: null,\n                            svgNodeTagLower: nodeName,\n                            el: el\n                        };\n                        named.push(newNamed);\n                        if (nodeName === 'g') {\n                            namedFromForSub = newNamed;\n                        }\n                    }\n                    else if (namedFrom) {\n                        named.push({\n                            name: namedFrom.name,\n                            namedFrom: namedFrom,\n                            svgNodeTagLower: nodeName,\n                            el: el\n                        });\n                    }\n                    parentGroup.add(el);\n                }\n            }\n            var parser = paintServerParsers[nodeName];\n            if (parser && hasOwn(paintServerParsers, nodeName)) {\n                var def = parser.call(this, xmlNode);\n                var id = xmlNode.getAttribute('id');\n                if (id) {\n                    this._defs[id] = def;\n                }\n            }\n        }\n        if (el && el.isGroup) {\n            var child = xmlNode.firstChild;\n            while (child) {\n                if (child.nodeType === 1) {\n                    this._parseNode(child, el, named, namedFromForSub, isInDefs, isInText);\n                }\n                else if (child.nodeType === 3 && isInText) {\n                    this._parseText(child, el);\n                }\n                child = child.nextSibling;\n            }\n        }\n    };\n    SVGParser.prototype._parseText = function (xmlNode, parentGroup) {\n        var text = new TSpan({\n            style: {\n                text: xmlNode.textContent\n            },\n            silent: true,\n            x: this._textX || 0,\n            y: this._textY || 0\n        });\n        inheritStyle(parentGroup, text);\n        parseAttributes(xmlNode, text, this._defsUsePending, false, false);\n        applyTextAlignment(text, parentGroup);\n        var textStyle = text.style;\n        var fontSize = textStyle.fontSize;\n        if (fontSize && fontSize < 9) {\n            textStyle.fontSize = 9;\n            text.scaleX *= fontSize / 9;\n            text.scaleY *= fontSize / 9;\n        }\n        var font = (textStyle.fontSize || textStyle.fontFamily) && [\n            textStyle.fontStyle,\n            textStyle.fontWeight,\n            (textStyle.fontSize || 12) + 'px',\n            textStyle.fontFamily || 'sans-serif'\n        ].join(' ');\n        textStyle.font = font;\n        var rect = text.getBoundingRect();\n        this._textX += rect.width;\n        parentGroup.add(text);\n        return text;\n    };\n    SVGParser.internalField = (function () {\n        nodeParsers = {\n            'g': function (xmlNode, parentGroup) {\n                var g = new Group();\n                inheritStyle(parentGroup, g);\n                parseAttributes(xmlNode, g, this._defsUsePending, false, false);\n                return g;\n            },\n            'rect': function (xmlNode, parentGroup) {\n                var rect = new Rect();\n                inheritStyle(parentGroup, rect);\n                parseAttributes(xmlNode, rect, this._defsUsePending, false, false);\n                rect.setShape({\n                    x: parseFloat(xmlNode.getAttribute('x') || '0'),\n                    y: parseFloat(xmlNode.getAttribute('y') || '0'),\n                    width: parseFloat(xmlNode.getAttribute('width') || '0'),\n                    height: parseFloat(xmlNode.getAttribute('height') || '0')\n                });\n                rect.silent = true;\n                return rect;\n            },\n            'circle': function (xmlNode, parentGroup) {\n                var circle = new Circle();\n                inheritStyle(parentGroup, circle);\n                parseAttributes(xmlNode, circle, this._defsUsePending, false, false);\n                circle.setShape({\n                    cx: parseFloat(xmlNode.getAttribute('cx') || '0'),\n                    cy: parseFloat(xmlNode.getAttribute('cy') || '0'),\n                    r: parseFloat(xmlNode.getAttribute('r') || '0')\n                });\n                circle.silent = true;\n                return circle;\n            },\n            'line': function (xmlNode, parentGroup) {\n                var line = new Line();\n                inheritStyle(parentGroup, line);\n                parseAttributes(xmlNode, line, this._defsUsePending, false, false);\n                line.setShape({\n                    x1: parseFloat(xmlNode.getAttribute('x1') || '0'),\n                    y1: parseFloat(xmlNode.getAttribute('y1') || '0'),\n                    x2: parseFloat(xmlNode.getAttribute('x2') || '0'),\n                    y2: parseFloat(xmlNode.getAttribute('y2') || '0')\n                });\n                line.silent = true;\n                return line;\n            },\n            'ellipse': function (xmlNode, parentGroup) {\n                var ellipse = new Ellipse();\n                inheritStyle(parentGroup, ellipse);\n                parseAttributes(xmlNode, ellipse, this._defsUsePending, false, false);\n                ellipse.setShape({\n                    cx: parseFloat(xmlNode.getAttribute('cx') || '0'),\n                    cy: parseFloat(xmlNode.getAttribute('cy') || '0'),\n                    rx: parseFloat(xmlNode.getAttribute('rx') || '0'),\n                    ry: parseFloat(xmlNode.getAttribute('ry') || '0')\n                });\n                ellipse.silent = true;\n                return ellipse;\n            },\n            'polygon': function (xmlNode, parentGroup) {\n                var pointsStr = xmlNode.getAttribute('points');\n                var pointsArr;\n                if (pointsStr) {\n                    pointsArr = parsePoints(pointsStr);\n                }\n                var polygon = new Polygon({\n                    shape: {\n                        points: pointsArr || []\n                    },\n                    silent: true\n                });\n                inheritStyle(parentGroup, polygon);\n                parseAttributes(xmlNode, polygon, this._defsUsePending, false, false);\n                return polygon;\n            },\n            'polyline': function (xmlNode, parentGroup) {\n                var pointsStr = xmlNode.getAttribute('points');\n                var pointsArr;\n                if (pointsStr) {\n                    pointsArr = parsePoints(pointsStr);\n                }\n                var polyline = new Polyline({\n                    shape: {\n                        points: pointsArr || []\n                    },\n                    silent: true\n                });\n                inheritStyle(parentGroup, polyline);\n                parseAttributes(xmlNode, polyline, this._defsUsePending, false, false);\n                return polyline;\n            },\n            'image': function (xmlNode, parentGroup) {\n                var img = new ZRImage();\n                inheritStyle(parentGroup, img);\n                parseAttributes(xmlNode, img, this._defsUsePending, false, false);\n                img.setStyle({\n                    image: xmlNode.getAttribute('xlink:href') || xmlNode.getAttribute('href'),\n                    x: +xmlNode.getAttribute('x'),\n                    y: +xmlNode.getAttribute('y'),\n                    width: +xmlNode.getAttribute('width'),\n                    height: +xmlNode.getAttribute('height')\n                });\n                img.silent = true;\n                return img;\n            },\n            'text': function (xmlNode, parentGroup) {\n                var x = xmlNode.getAttribute('x') || '0';\n                var y = xmlNode.getAttribute('y') || '0';\n                var dx = xmlNode.getAttribute('dx') || '0';\n                var dy = xmlNode.getAttribute('dy') || '0';\n                this._textX = parseFloat(x) + parseFloat(dx);\n                this._textY = parseFloat(y) + parseFloat(dy);\n                var g = new Group();\n                inheritStyle(parentGroup, g);\n                parseAttributes(xmlNode, g, this._defsUsePending, false, true);\n                return g;\n            },\n            'tspan': function (xmlNode, parentGroup) {\n                var x = xmlNode.getAttribute('x');\n                var y = xmlNode.getAttribute('y');\n                if (x != null) {\n                    this._textX = parseFloat(x);\n                }\n                if (y != null) {\n                    this._textY = parseFloat(y);\n                }\n                var dx = xmlNode.getAttribute('dx') || '0';\n                var dy = xmlNode.getAttribute('dy') || '0';\n                var g = new Group();\n                inheritStyle(parentGroup, g);\n                parseAttributes(xmlNode, g, this._defsUsePending, false, true);\n                this._textX += parseFloat(dx);\n                this._textY += parseFloat(dy);\n                return g;\n            },\n            'path': function (xmlNode, parentGroup) {\n                var d = xmlNode.getAttribute('d') || '';\n                var path = createFromString(d);\n                inheritStyle(parentGroup, path);\n                parseAttributes(xmlNode, path, this._defsUsePending, false, false);\n                path.silent = true;\n                return path;\n            }\n        };\n    })();\n    return SVGParser;\n}());\nvar paintServerParsers = {\n    'lineargradient': function (xmlNode) {\n        var x1 = parseInt(xmlNode.getAttribute('x1') || '0', 10);\n        var y1 = parseInt(xmlNode.getAttribute('y1') || '0', 10);\n        var x2 = parseInt(xmlNode.getAttribute('x2') || '10', 10);\n        var y2 = parseInt(xmlNode.getAttribute('y2') || '0', 10);\n        var gradient = new LinearGradient(x1, y1, x2, y2);\n        parsePaintServerUnit(xmlNode, gradient);\n        parseGradientColorStops(xmlNode, gradient);\n        return gradient;\n    },\n    'radialgradient': function (xmlNode) {\n        var cx = parseInt(xmlNode.getAttribute('cx') || '0', 10);\n        var cy = parseInt(xmlNode.getAttribute('cy') || '0', 10);\n        var r = parseInt(xmlNode.getAttribute('r') || '0', 10);\n        var gradient = new RadialGradient(cx, cy, r);\n        parsePaintServerUnit(xmlNode, gradient);\n        parseGradientColorStops(xmlNode, gradient);\n        return gradient;\n    }\n};\nfunction parsePaintServerUnit(xmlNode, gradient) {\n    var gradientUnits = xmlNode.getAttribute('gradientUnits');\n    if (gradientUnits === 'userSpaceOnUse') {\n        gradient.global = true;\n    }\n}\nfunction parseGradientColorStops(xmlNode, gradient) {\n    var stop = xmlNode.firstChild;\n    while (stop) {\n        if (stop.nodeType === 1\n            && stop.nodeName.toLocaleLowerCase() === 'stop') {\n            var offsetStr = stop.getAttribute('offset');\n            var offset = void 0;\n            if (offsetStr && offsetStr.indexOf('%') > 0) {\n                offset = parseInt(offsetStr, 10) / 100;\n            }\n            else if (offsetStr) {\n                offset = parseFloat(offsetStr);\n            }\n            else {\n                offset = 0;\n            }\n            var styleVals = {};\n            parseInlineStyle(stop, styleVals, styleVals);\n            var stopColor = styleVals.stopColor\n                || stop.getAttribute('stop-color')\n                || '#000000';\n            gradient.colorStops.push({\n                offset: offset,\n                color: stopColor\n            });\n        }\n        stop = stop.nextSibling;\n    }\n}\nfunction inheritStyle(parent, child) {\n    if (parent && parent.__inheritedStyle) {\n        if (!child.__inheritedStyle) {\n            child.__inheritedStyle = {};\n        }\n        defaults(child.__inheritedStyle, parent.__inheritedStyle);\n    }\n}\nfunction parsePoints(pointsString) {\n    var list = splitNumberSequence(pointsString);\n    var points = [];\n    for (var i = 0; i < list.length; i += 2) {\n        var x = parseFloat(list[i]);\n        var y = parseFloat(list[i + 1]);\n        points.push([x, y]);\n    }\n    return points;\n}\nfunction parseAttributes(xmlNode, el, defsUsePending, onlyInlineStyle, isTextGroup) {\n    var disp = el;\n    var inheritedStyle = disp.__inheritedStyle = disp.__inheritedStyle || {};\n    var selfStyle = {};\n    if (xmlNode.nodeType === 1) {\n        parseTransformAttribute(xmlNode, el);\n        parseInlineStyle(xmlNode, inheritedStyle, selfStyle);\n        if (!onlyInlineStyle) {\n            parseAttributeStyle(xmlNode, inheritedStyle, selfStyle);\n        }\n    }\n    disp.style = disp.style || {};\n    if (inheritedStyle.fill != null) {\n        disp.style.fill = getFillStrokeStyle(disp, 'fill', inheritedStyle.fill, defsUsePending);\n    }\n    if (inheritedStyle.stroke != null) {\n        disp.style.stroke = getFillStrokeStyle(disp, 'stroke', inheritedStyle.stroke, defsUsePending);\n    }\n    each([\n        'lineWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'miterLimit', 'fontSize'\n    ], function (propName) {\n        if (inheritedStyle[propName] != null) {\n            disp.style[propName] = parseFloat(inheritedStyle[propName]);\n        }\n    });\n    each([\n        'lineDashOffset', 'lineCap', 'lineJoin', 'fontWeight', 'fontFamily', 'fontStyle', 'textAlign'\n    ], function (propName) {\n        if (inheritedStyle[propName] != null) {\n            disp.style[propName] = inheritedStyle[propName];\n        }\n    });\n    if (isTextGroup) {\n        disp.__selfStyle = selfStyle;\n    }\n    if (inheritedStyle.lineDash) {\n        disp.style.lineDash = map(splitNumberSequence(inheritedStyle.lineDash), function (str) {\n            return parseFloat(str);\n        });\n    }\n    if (inheritedStyle.visibility === 'hidden' || inheritedStyle.visibility === 'collapse') {\n        disp.invisible = true;\n    }\n    if (inheritedStyle.display === 'none') {\n        disp.ignore = true;\n    }\n}\nfunction applyTextAlignment(text, parentGroup) {\n    var parentSelfStyle = parentGroup.__selfStyle;\n    if (parentSelfStyle) {\n        var textBaseline = parentSelfStyle.textBaseline;\n        var zrTextBaseline = textBaseline;\n        if (!textBaseline || textBaseline === 'auto') {\n            zrTextBaseline = 'alphabetic';\n        }\n        else if (textBaseline === 'baseline') {\n            zrTextBaseline = 'alphabetic';\n        }\n        else if (textBaseline === 'before-edge' || textBaseline === 'text-before-edge') {\n            zrTextBaseline = 'top';\n        }\n        else if (textBaseline === 'after-edge' || textBaseline === 'text-after-edge') {\n            zrTextBaseline = 'bottom';\n        }\n        else if (textBaseline === 'central' || textBaseline === 'mathematical') {\n            zrTextBaseline = 'middle';\n        }\n        text.style.textBaseline = zrTextBaseline;\n    }\n    var parentInheritedStyle = parentGroup.__inheritedStyle;\n    if (parentInheritedStyle) {\n        var textAlign = parentInheritedStyle.textAlign;\n        var zrTextAlign = textAlign;\n        if (textAlign) {\n            if (textAlign === 'middle') {\n                zrTextAlign = 'center';\n            }\n            text.style.textAlign = zrTextAlign;\n        }\n    }\n}\nvar urlRegex = /^url\\(\\s*#(.*?)\\)/;\nfunction getFillStrokeStyle(el, method, str, defsUsePending) {\n    var urlMatch = str && str.match(urlRegex);\n    if (urlMatch) {\n        var url = trim(urlMatch[1]);\n        defsUsePending.push([el, method, url]);\n        return;\n    }\n    if (str === 'none') {\n        str = null;\n    }\n    return str;\n}\nfunction applyDefs(defs, defsUsePending) {\n    for (var i = 0; i < defsUsePending.length; i++) {\n        var item = defsUsePending[i];\n        item[0].style[item[1]] = defs[item[2]];\n    }\n}\nvar numberReg$1 = /-?([0-9]*\\.)?[0-9]+([eE]-?[0-9]+)?/g;\nfunction splitNumberSequence(rawStr) {\n    return rawStr.match(numberReg$1) || [];\n}\nvar transformRegex = /(translate|scale|rotate|skewX|skewY|matrix)\\(([\\-\\s0-9\\.eE,]*)\\)/g;\nvar DEGREE_TO_ANGLE = Math.PI / 180;\nfunction parseTransformAttribute(xmlNode, node) {\n    var transform = xmlNode.getAttribute('transform');\n    if (transform) {\n        transform = transform.replace(/,/g, ' ');\n        var transformOps_1 = [];\n        var mt = null;\n        transform.replace(transformRegex, function (str, type, value) {\n            transformOps_1.push(type, value);\n            return '';\n        });\n        for (var i = transformOps_1.length - 1; i > 0; i -= 2) {\n            var value = transformOps_1[i];\n            var type = transformOps_1[i - 1];\n            var valueArr = splitNumberSequence(value);\n            mt = mt || create$1();\n            switch (type) {\n                case 'translate':\n                    translate(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || '0')]);\n                    break;\n                case 'scale':\n                    scale$1(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || valueArr[0])]);\n                    break;\n                case 'rotate':\n                    rotate(mt, mt, -parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);\n                    break;\n                case 'skewX':\n                    var sx = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);\n                    mul$1(mt, [1, 0, sx, 1, 0, 0], mt);\n                    break;\n                case 'skewY':\n                    var sy = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);\n                    mul$1(mt, [1, sy, 0, 1, 0, 0], mt);\n                    break;\n                case 'matrix':\n                    mt[0] = parseFloat(valueArr[0]);\n                    mt[1] = parseFloat(valueArr[1]);\n                    mt[2] = parseFloat(valueArr[2]);\n                    mt[3] = parseFloat(valueArr[3]);\n                    mt[4] = parseFloat(valueArr[4]);\n                    mt[5] = parseFloat(valueArr[5]);\n                    break;\n            }\n        }\n        node.setLocalTransform(mt);\n    }\n}\nvar styleRegex = /([^\\s:;]+)\\s*:\\s*([^:;]+)/g;\nfunction parseInlineStyle(xmlNode, inheritableStyleResult, selfStyleResult) {\n    var style = xmlNode.getAttribute('style');\n    if (!style) {\n        return;\n    }\n    styleRegex.lastIndex = 0;\n    var styleRegResult;\n    while ((styleRegResult = styleRegex.exec(style)) != null) {\n        var svgStlAttr = styleRegResult[1];\n        var zrInheritableStlAttr = hasOwn(INHERITABLE_STYLE_ATTRIBUTES_MAP, svgStlAttr)\n            ? INHERITABLE_STYLE_ATTRIBUTES_MAP[svgStlAttr]\n            : null;\n        if (zrInheritableStlAttr) {\n            inheritableStyleResult[zrInheritableStlAttr] = styleRegResult[2];\n        }\n        var zrSelfStlAttr = hasOwn(SELF_STYLE_ATTRIBUTES_MAP, svgStlAttr)\n            ? SELF_STYLE_ATTRIBUTES_MAP[svgStlAttr]\n            : null;\n        if (zrSelfStlAttr) {\n            selfStyleResult[zrSelfStlAttr] = styleRegResult[2];\n        }\n    }\n}\nfunction parseAttributeStyle(xmlNode, inheritableStyleResult, selfStyleResult) {\n    for (var i = 0; i < INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) {\n        var svgAttrName = INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS[i];\n        var attrValue = xmlNode.getAttribute(svgAttrName);\n        if (attrValue != null) {\n            inheritableStyleResult[INHERITABLE_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue;\n        }\n    }\n    for (var i = 0; i < SELF_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) {\n        var svgAttrName = SELF_STYLE_ATTRIBUTES_MAP_KEYS[i];\n        var attrValue = xmlNode.getAttribute(svgAttrName);\n        if (attrValue != null) {\n            selfStyleResult[SELF_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue;\n        }\n    }\n}\nfunction makeViewBoxTransform(viewBoxRect, boundingRect) {\n    var scaleX = boundingRect.width / viewBoxRect.width;\n    var scaleY = boundingRect.height / viewBoxRect.height;\n    var scale = Math.min(scaleX, scaleY);\n    return {\n        scale: scale,\n        x: -(viewBoxRect.x + viewBoxRect.width / 2) * scale + (boundingRect.x + boundingRect.width / 2),\n        y: -(viewBoxRect.y + viewBoxRect.height / 2) * scale + (boundingRect.y + boundingRect.height / 2)\n    };\n}\nfunction parseSVG(xml, opt) {\n    var parser = new SVGParser();\n    return parser.parse(xml, opt);\n}\n\n/**\n * \"region available\" means that: enable users to set attribute `name=\"xxx\"` on those tags\n * to make it be a region.\n * 1. region styles and its label styles can be defined in echarts opton:\n * ```js\n * geo: {\n *     regions: [{\n *         name: 'xxx',\n *         itemStyle: { ... },\n *         label: { ... }\n *     }, {\n *         ...\n *     },\n *     ...]\n * };\n * ```\n * 2. name can be duplicated in different SVG tag. All of the tags with the same name share\n * a region option. For exampel if there are two <path> representing two lung lobes. They have\n * no common parents but both of them need to display label \"lung\" inside.\n */\n\nvar REGION_AVAILABLE_SVG_TAG_MAP = createHashMap(['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path', // <text> <tspan> are also enabled because some SVG might paint text itself,\n// but still need to trigger events or tooltip.\n'text', 'tspan', // <g> is also enabled because this case: if multiple tags share one name\n// and need label displayed, every tags will display the name, which is not\n// expected. So we can put them into a <g name=\"xxx\">. Thereby only one label\n// displayed and located based on the bounding rect of the <g>.\n'g']);\n\nvar GeoSVGResource =\n/** @class */\nfunction () {\n  function GeoSVGResource(mapName, svg) {\n    this.type = 'geoSVG'; // All used graphics. key: hostKey, value: root\n\n    this._usedGraphicMap = createHashMap(); // All unused graphics.\n\n    this._freedGraphics = [];\n    this._mapName = mapName; // Only perform parse to XML object here, which might be time\n    // consiming for large SVG.\n    // Although convert XML to zrender element is also time consiming,\n    // if we do it here, the clone of zrender elements has to be\n    // required. So we do it once for each geo instance, util real\n    // performance issues call for optimizing it.\n\n    this._parsedXML = parseXML(svg);\n  }\n\n  GeoSVGResource.prototype.load = function ()\n  /* nameMap: NameMap */\n  {\n    // In the \"load\" stage, graphic need to be built to\n    // get boundingRect for geo coordinate system.\n    var firstGraphic = this._firstGraphic; // Create the return data structure only when first graphic created.\n    // Because they will be used in geo coordinate system update stage,\n    // and `regions` will be mounted at `geo` coordinate system,\n    // in which there is no \"view\" info, so that it should better not to\n    // make references to graphic elements.\n\n    if (!firstGraphic) {\n      firstGraphic = this._firstGraphic = this._buildGraphic(this._parsedXML);\n\n      this._freedGraphics.push(firstGraphic);\n\n      this._boundingRect = this._firstGraphic.boundingRect.clone(); // PENDING: `nameMap` will not be supported until some real requirement come.\n      // if (nameMap) {\n      //     named = applyNameMap(named, nameMap);\n      // }\n\n      var _a = createRegions(firstGraphic.named),\n          regions = _a.regions,\n          regionsMap = _a.regionsMap;\n\n      this._regions = regions;\n      this._regionsMap = regionsMap;\n    }\n\n    return {\n      boundingRect: this._boundingRect,\n      regions: this._regions,\n      regionsMap: this._regionsMap\n    };\n  };\n\n  GeoSVGResource.prototype._buildGraphic = function (svgXML) {\n    var result;\n    var rootFromParse;\n\n    try {\n      result = svgXML && parseSVG(svgXML, {\n        ignoreViewBox: true,\n        ignoreRootClip: true\n      }) || {};\n      rootFromParse = result.root;\n      assert(rootFromParse != null);\n    } catch (e) {\n      throw new Error('Invalid svg format\\n' + e.message);\n    } // Note: we keep the covenant that the root has no transform. So always add an extra root.\n\n\n    var root = new Group();\n    root.add(rootFromParse);\n    root.isGeoSVGGraphicRoot = true; // [THE_RULE_OF_VIEWPORT_AND_VIEWBOX]\n    //\n    // Consider: `<svg width=\"...\" height=\"...\" viewBox=\"...\">`\n    // - the `width/height` we call it `svgWidth/svgHeight` for short.\n    // - `(0, 0, svgWidth, svgHeight)` defines the viewport of the SVG, or say,\n    //   \"viewport boundingRect\", or `boundingRect` for short.\n    // - `viewBox` defines the transform from the real content ot the viewport.\n    //   `viewBox` has the same unit as the content of SVG.\n    //   If `viewBox` exists, a transform is defined, so the unit of `svgWidth/svgHeight` become\n    //   different from the content of SVG. Otherwise, they are the same.\n    //\n    // If both `svgWidth/svgHeight/viewBox` are specified in a SVG file, the transform rule will be:\n    // 0. `boundingRect` is `(0, 0, svgWidth, svgHeight)`. Set it to Geo['_rect'] (View['_rect']).\n    // 1. Make a transform from `viewBox` to `boundingRect`.\n    //    Note: only support `preserveAspectRatio 'xMidYMid'` here. That is, this transform will preserve\n    //    the aspect ratio.\n    // 2. Make a transform from boundingRect to Geo['_viewRect'] (View['_viewRect'])\n    //    (`Geo`/`View` will do this job).\n    //    Note: this transform might not preserve aspect radio, which depending on how users specify\n    //    viewRect in echarts option (e.g., `geo.left/top/width/height` will not preserve aspect ratio,\n    //    but `geo.layoutCenter/layoutSize` will preserve aspect ratio).\n    //\n    // If `svgWidth/svgHeight` not specified, we use `viewBox` as the `boundingRect` to make the SVG\n    // layout look good.\n    //\n    // If neither `svgWidth/svgHeight` nor `viewBox` are not specified, we calculate the boundingRect\n    // of the SVG content and use them to make SVG layout look good.\n\n    var svgWidth = result.width;\n    var svgHeight = result.height;\n    var viewBoxRect = result.viewBoxRect;\n    var boundingRect = this._boundingRect;\n\n    if (!boundingRect) {\n      var bRectX = void 0;\n      var bRectY = void 0;\n      var bRectWidth = void 0;\n      var bRectHeight = void 0;\n\n      if (svgWidth != null) {\n        bRectX = 0;\n        bRectWidth = svgWidth;\n      } else if (viewBoxRect) {\n        bRectX = viewBoxRect.x;\n        bRectWidth = viewBoxRect.width;\n      }\n\n      if (svgHeight != null) {\n        bRectY = 0;\n        bRectHeight = svgHeight;\n      } else if (viewBoxRect) {\n        bRectY = viewBoxRect.y;\n        bRectHeight = viewBoxRect.height;\n      } // If both viewBox and svgWidth/svgHeight not specified,\n      // we have to determine how to layout those element to make them look good.\n\n\n      if (bRectX == null || bRectY == null) {\n        var calculatedBoundingRect = rootFromParse.getBoundingRect();\n\n        if (bRectX == null) {\n          bRectX = calculatedBoundingRect.x;\n          bRectWidth = calculatedBoundingRect.width;\n        }\n\n        if (bRectY == null) {\n          bRectY = calculatedBoundingRect.y;\n          bRectHeight = calculatedBoundingRect.height;\n        }\n      }\n\n      boundingRect = this._boundingRect = new BoundingRect(bRectX, bRectY, bRectWidth, bRectHeight);\n    }\n\n    if (viewBoxRect) {\n      var viewBoxTransform = makeViewBoxTransform(viewBoxRect, boundingRect); // Only support `preserveAspectRatio 'xMidYMid'`\n\n      rootFromParse.scaleX = rootFromParse.scaleY = viewBoxTransform.scale;\n      rootFromParse.x = viewBoxTransform.x;\n      rootFromParse.y = viewBoxTransform.y;\n    } // SVG needs to clip based on `viewBox`. And some SVG files really rely on this feature.\n    // They do not strictly confine all of the content inside a display rect, but deliberately\n    // use a `viewBox` to define a displayable rect.\n    // PENDING:\n    // The drawback of the `setClipPath` here is: the region label (genereted by echarts) near the\n    // edge might also be clipped, because region labels are put as `textContent` of the SVG path.\n\n\n    root.setClipPath(new Rect({\n      shape: boundingRect.plain()\n    }));\n    var named = [];\n    each(result.named, function (namedItem) {\n      if (REGION_AVAILABLE_SVG_TAG_MAP.get(namedItem.svgNodeTagLower) != null) {\n        named.push(namedItem);\n        setSilent(namedItem.el);\n      }\n    });\n    return {\n      root: root,\n      boundingRect: boundingRect,\n      named: named\n    };\n  };\n  /**\n   * Consider:\n   * (1) One graphic element can not be shared by different `geoView` running simultaneously.\n   *     Notice, also need to consider multiple echarts instances share a `mapRecord`.\n   * (2) Converting SVG to graphic elements is time consuming.\n   * (3) In the current architecture, `load` should be called frequently to get boundingRect,\n   *     and it is called without view info.\n   * So we maintain graphic elements in this module, and enables `view` to use/return these\n   * graphics from/to the pool with it's uid.\n   */\n\n\n  GeoSVGResource.prototype.useGraphic = function (hostKey\n  /* , nameMap: NameMap */\n  ) {\n    var usedRootMap = this._usedGraphicMap;\n    var svgGraphic = usedRootMap.get(hostKey);\n\n    if (svgGraphic) {\n      return svgGraphic;\n    }\n\n    svgGraphic = this._freedGraphics.pop() // use the first boundingRect to avoid duplicated boundingRect calculation.\n    || this._buildGraphic(this._parsedXML);\n    usedRootMap.set(hostKey, svgGraphic); // PENDING: `nameMap` will not be supported until some real requirement come.\n    // `nameMap` can only be obtained from echarts option.\n    // The original `named` must not be modified.\n    // if (nameMap) {\n    //     svgGraphic = extend({}, svgGraphic);\n    //     svgGraphic.named = applyNameMap(svgGraphic.named, nameMap);\n    // }\n\n    return svgGraphic;\n  };\n\n  GeoSVGResource.prototype.freeGraphic = function (hostKey) {\n    var usedRootMap = this._usedGraphicMap;\n    var svgGraphic = usedRootMap.get(hostKey);\n\n    if (svgGraphic) {\n      usedRootMap.removeKey(hostKey);\n\n      this._freedGraphics.push(svgGraphic);\n    }\n  };\n\n  return GeoSVGResource;\n}();\n\nfunction setSilent(el) {\n  // Only named element has silent: false, other elements should\n  // act as background and has no user interaction.\n  el.silent = false; // text|tspan will be converted to group.\n\n  if (el.isGroup) {\n    el.traverse(function (child) {\n      child.silent = false;\n    });\n  }\n}\n\nfunction createRegions(named) {\n  var regions = [];\n  var regionsMap = createHashMap(); // Create resions only for the first graphic.\n\n  each(named, function (namedItem) {\n    // Region has feature to calculate center for tooltip or other features.\n    // If there is a <g name=\"xxx\">, the center should be the center of the\n    // bounding rect of the g.\n    if (namedItem.namedFrom != null) {\n      return;\n    }\n\n    var region = new GeoSVGRegion(namedItem.name, namedItem.el); // PENDING: if `nameMap` supported, this region can not be mounted on\n    // `this`, but can only be created each time `load()` called.\n\n    regions.push(region); // PENDING: if multiple tag named with the same name, only one will be\n    // found by `_regionsMap`. `_regionsMap` is used to find a coordinate\n    // by name. We use `region.getCenter()` as the coordinate.\n\n    regionsMap.set(namedItem.name, region);\n  });\n  return {\n    regions: regions,\n    regionsMap: regionsMap\n  };\n} // PENDING: `nameMap` will not be supported until some real requirement come.\n// /**\n//  * Use the alias in geoNameMap.\n//  * The input `named` must not be modified.\n//  */\n// function applyNameMap(\n//     named: GeoSVGGraphicRecord['named'],\n//     nameMap: NameMap\n// ): GeoSVGGraphicRecord['named'] {\n//     const result = [] as GeoSVGGraphicRecord['named'];\n//     for (let i = 0; i < named.length; i++) {\n//         let regionGraphic = named[i];\n//         const name = regionGraphic.name;\n//         if (nameMap && nameMap.hasOwnProperty(name)) {\n//             regionGraphic = extend({}, regionGraphic);\n//             regionGraphic.name = name;\n//         }\n//         result.push(regionGraphic);\n//     }\n//     return result;\n// }\n\nvar geoCoord = [126, 25];\nvar nanhaiName = '南海诸岛';\nvar points$1 = [[[0, 3.5], [7, 11.2], [15, 11.9], [30, 7], [42, 0.7], [52, 0.7], [56, 7.7], [59, 0.7], [64, 0.7], [64, 0], [5, 0], [0, 3.5]], [[13, 16.1], [19, 14.7], [16, 21.7], [11, 23.1], [13, 16.1]], [[12, 32.2], [14, 38.5], [15, 38.5], [13, 32.2], [12, 32.2]], [[16, 47.6], [12, 53.2], [13, 53.2], [18, 47.6], [16, 47.6]], [[6, 64.4], [8, 70], [9, 70], [8, 64.4], [6, 64.4]], [[23, 82.6], [29, 79.8], [30, 79.8], [25, 82.6], [23, 82.6]], [[37, 70.7], [43, 62.3], [44, 62.3], [39, 70.7], [37, 70.7]], [[48, 51.1], [51, 45.5], [53, 45.5], [50, 51.1], [48, 51.1]], [[51, 35], [51, 28.7], [53, 28.7], [53, 35], [51, 35]], [[52, 22.4], [55, 17.5], [56, 17.5], [53, 22.4], [52, 22.4]], [[58, 12.6], [62, 7], [63, 7], [60, 12.6], [58, 12.6]], [[0, 3.5], [0, 93.1], [64, 93.1], [64, 0], [63, 0], [63, 92.4], [1, 92.4], [1, 3.5], [0, 3.5]]];\n\nfor (var i = 0; i < points$1.length; i++) {\n  for (var k = 0; k < points$1[i].length; k++) {\n    points$1[i][k][0] /= 10.5;\n    points$1[i][k][1] /= -10.5 / 0.75;\n    points$1[i][k][0] += geoCoord[0];\n    points$1[i][k][1] += geoCoord[1];\n  }\n}\n\nfunction fixNanhai(mapType, regions) {\n  if (mapType === 'china') {\n    for (var i = 0; i < regions.length; i++) {\n      // Already exists.\n      if (regions[i].name === nanhaiName) {\n        return;\n      }\n    }\n\n    regions.push(new GeoJSONRegion(nanhaiName, map(points$1, function (exterior) {\n      return {\n        type: 'polygon',\n        exterior: exterior\n      };\n    }), geoCoord));\n  }\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar coordsOffsetMap = {\n  '南海诸岛': [32, 80],\n  // 全国\n  '广东': [0, -10],\n  '香港': [10, 5],\n  '澳门': [-10, 10],\n  // '北京': [-10, 0],\n  '天津': [5, 5]\n};\nfunction fixTextCoords(mapType, region) {\n  if (mapType === 'china') {\n    var coordFix = coordsOffsetMap[region.name];\n\n    if (coordFix) {\n      var cp = region.getCenter();\n      cp[0] += coordFix[0] / 10.5;\n      cp[1] += -coordFix[1] / (10.5 / 0.75);\n      region.setCenter(cp);\n    }\n  }\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n// Fix for 钓鱼岛\n// let Region = require('../Region');\n// let zrUtil = require('zrender/lib/core/util');\n// let geoCoord = [126, 25];\nvar points$2 = [[[123.45165252685547, 25.73527164402261], [123.49731445312499, 25.73527164402261], [123.49731445312499, 25.750734064600884], [123.45165252685547, 25.750734064600884], [123.45165252685547, 25.73527164402261]]];\nfunction fixDiaoyuIsland(mapType, region) {\n  if (mapType === 'china' && region.name === '台湾') {\n    region.geometries.push({\n      type: 'polygon',\n      exterior: points$2[0]\n    });\n  }\n}\n\nvar DEFAULT_NAME_PROPERTY = 'name';\n\nvar GeoJSONResource =\n/** @class */\nfunction () {\n  function GeoJSONResource(mapName, geoJSON, specialAreas) {\n    this.type = 'geoJSON';\n    this._parsedMap = createHashMap();\n    this._mapName = mapName;\n    this._specialAreas = specialAreas; // PENDING: delay the parse to the first usage to rapid up the FMP?\n\n    this._geoJSON = parseInput(geoJSON);\n  }\n  /**\n   * @param nameMap can be null/undefined\n   * @param nameProperty can be null/undefined\n   */\n\n\n  GeoJSONResource.prototype.load = function (nameMap, nameProperty) {\n    nameProperty = nameProperty || DEFAULT_NAME_PROPERTY;\n\n    var parsed = this._parsedMap.get(nameProperty);\n\n    if (!parsed) {\n      var rawRegions = this._parseToRegions(nameProperty);\n\n      parsed = this._parsedMap.set(nameProperty, {\n        regions: rawRegions,\n        boundingRect: calculateBoundingRect(rawRegions)\n      });\n    }\n\n    var regionsMap = createHashMap();\n    var finalRegions = [];\n    each(parsed.regions, function (region) {\n      var regionName = region.name; // Try use the alias in geoNameMap\n\n      if (nameMap && hasOwn(nameMap, regionName)) {\n        region = region.cloneShallow(regionName = nameMap[regionName]);\n      }\n\n      finalRegions.push(region);\n      regionsMap.set(regionName, region);\n    });\n    return {\n      regions: finalRegions,\n      boundingRect: parsed.boundingRect || new BoundingRect(0, 0, 0, 0),\n      regionsMap: regionsMap\n    };\n  };\n\n  GeoJSONResource.prototype._parseToRegions = function (nameProperty) {\n    var mapName = this._mapName;\n    var geoJSON = this._geoJSON;\n    var rawRegions; // https://jsperf.com/try-catch-performance-overhead\n\n    try {\n      rawRegions = geoJSON ? parseGeoJSON(geoJSON, nameProperty) : [];\n    } catch (e) {\n      throw new Error('Invalid geoJson format\\n' + e.message);\n    }\n\n    fixNanhai(mapName, rawRegions);\n    each(rawRegions, function (region) {\n      var regionName = region.name;\n      fixTextCoords(mapName, region);\n      fixDiaoyuIsland(mapName, region); // Some area like Alaska in USA map needs to be tansformed\n      // to look better\n\n      var specialArea = this._specialAreas && this._specialAreas[regionName];\n\n      if (specialArea) {\n        region.transformTo(specialArea.left, specialArea.top, specialArea.width, specialArea.height);\n      }\n    }, this);\n    return rawRegions;\n  };\n  /**\n   * Only for exporting to users.\n   * **MUST NOT** used internally.\n   */\n\n\n  GeoJSONResource.prototype.getMapForUser = function () {\n    return {\n      // For backward compatibility, use geoJson\n      // PENDING: it has been returning them without clone.\n      // do we need to avoid outsite modification?\n      geoJson: this._geoJSON,\n      geoJSON: this._geoJSON,\n      specialAreas: this._specialAreas\n    };\n  };\n\n  return GeoJSONResource;\n}();\n\nfunction calculateBoundingRect(regions) {\n  var rect;\n\n  for (var i = 0; i < regions.length; i++) {\n    var regionRect = regions[i].getBoundingRect();\n    rect = rect || regionRect.clone();\n    rect.union(regionRect);\n  }\n\n  return rect;\n}\n\nfunction parseInput(source) {\n  return !isString(source) ? source : typeof JSON !== 'undefined' && JSON.parse ? JSON.parse(source) : new Function('return (' + source + ');')();\n}\n\nvar storage = createHashMap();\nvar geoSourceManager = {\n  /**\n   * Compatible with previous `echarts.registerMap`.\n   *\n   * @usage\n   * ```js\n   *\n   * echarts.registerMap('USA', geoJson, specialAreas);\n   *\n   * echarts.registerMap('USA', {\n   *     geoJson: geoJson,\n   *     specialAreas: {...}\n   * });\n   * echarts.registerMap('USA', {\n   *     geoJSON: geoJson,\n   *     specialAreas: {...}\n   * });\n   *\n   * echarts.registerMap('airport', {\n   *     svg: svg\n   * }\n   * ```\n   *\n   * Note:\n   * Do not support that register multiple geoJSON or SVG\n   * one map name. Because different geoJSON and SVG have\n   * different unit. It's not easy to make sure how those\n   * units are mapping/normalize.\n   * If intending to use multiple geoJSON or SVG, we can\n   * use multiple geo coordinate system.\n   */\n  registerMap: function (mapName, rawDef, rawSpecialAreas) {\n    if (rawDef.svg) {\n      var resource = new GeoSVGResource(mapName, rawDef.svg);\n      storage.set(mapName, resource);\n    } else {\n      // Recommend:\n      //     echarts.registerMap('eu', { geoJSON: xxx, specialAreas: xxx });\n      // Backward compatibility:\n      //     echarts.registerMap('eu', geoJSON, specialAreas);\n      //     echarts.registerMap('eu', { geoJson: xxx, specialAreas: xxx });\n      var geoJSON = rawDef.geoJson || rawDef.geoJSON;\n\n      if (geoJSON && !rawDef.features) {\n        rawSpecialAreas = rawDef.specialAreas;\n      } else {\n        geoJSON = rawDef;\n      }\n\n      var resource = new GeoJSONResource(mapName, geoJSON, rawSpecialAreas);\n      storage.set(mapName, resource);\n    }\n  },\n  getGeoResource: function (mapName) {\n    return storage.get(mapName);\n  },\n\n  /**\n   * Only for exporting to users.\n   * **MUST NOT** used internally.\n   */\n  getMapForUser: function (mapName) {\n    var resource = storage.get(mapName); // Do not support return SVG until some real requirement come.\n\n    return resource && resource.type === 'geoJSON' && resource.getMapForUser();\n  },\n  load: function (mapName, nameMap, nameProperty) {\n    var resource = storage.get(mapName);\n\n    if (!resource) {\n      if (\"development\" !== 'production') {\n        console.error('Map ' + mapName + ' not exists. The GeoJSON of the map must be provided.');\n      }\n\n      return;\n    }\n\n    return resource.load(nameMap, nameProperty);\n  }\n};\n\n/**\n * Only these tags enable use `itemStyle` if they are named in SVG.\n * Other tags like <text> <tspan> <image> might not suitable for `itemStyle`.\n * They will not be considered to be styled until some requirements come.\n */\n\nvar OPTION_STYLE_ENABLED_TAGS = ['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path'];\nvar OPTION_STYLE_ENABLED_TAG_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS);\nvar STATE_TRIGGER_TAG_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g']));\nvar LABEL_HOST_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g']));\nvar mapLabelRaw = makeInner();\n\nfunction getFixedItemStyle(model) {\n  var itemStyle = model.getItemStyle();\n  var areaColor = model.get('areaColor'); // If user want the color not to be changed when hover,\n  // they should both set areaColor and color to be null.\n\n  if (areaColor != null) {\n    itemStyle.fill = areaColor;\n  }\n\n  return itemStyle;\n} // Only stroke can be used for line.\n// Using fill in style if stroke not exits.\n// TODO Not sure yet. Perhaps a separate `lineStyle`?\n\n\nfunction fixLineStyle(styleHost) {\n  var style = styleHost.style;\n\n  if (style) {\n    style.stroke = style.stroke || style.fill;\n    style.fill = null;\n  }\n}\n\nvar MapDraw =\n/** @class */\nfunction () {\n  function MapDraw(api) {\n    var group = new Group();\n    this.uid = getUID('ec_map_draw');\n    this._controller = new RoamController(api.getZr());\n    this._controllerHost = {\n      target: group\n    };\n    this.group = group;\n    group.add(this._regionsGroup = new Group());\n    group.add(this._svgGroup = new Group());\n  }\n\n  MapDraw.prototype.draw = function (mapOrGeoModel, ecModel, api, fromView, payload) {\n    var isGeo = mapOrGeoModel.mainType === 'geo'; // Map series has data. GEO model that controlled by map series\n    // will be assigned with map data. Other GEO model has no data.\n\n    var data = mapOrGeoModel.getData && mapOrGeoModel.getData();\n    isGeo && ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'map'\n    }, function (mapSeries) {\n      if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) {\n        data = mapSeries.getData();\n      }\n    });\n    var geo = mapOrGeoModel.coordinateSystem;\n    var regionsGroup = this._regionsGroup;\n    var group = this.group;\n    var transformInfo = geo.getTransformInfo();\n    var transformInfoRaw = transformInfo.raw;\n    var transformInfoRoam = transformInfo.roam; // No animation when first draw or in action\n\n    var isFirstDraw = !regionsGroup.childAt(0) || payload;\n\n    if (isFirstDraw) {\n      group.x = transformInfoRoam.x;\n      group.y = transformInfoRoam.y;\n      group.scaleX = transformInfoRoam.scaleX;\n      group.scaleY = transformInfoRoam.scaleY;\n      group.dirty();\n    } else {\n      updateProps(group, transformInfoRoam, mapOrGeoModel);\n    }\n\n    var isVisualEncodedByVisualMap = data && data.getVisual('visualMeta') && data.getVisual('visualMeta').length > 0;\n    var viewBuildCtx = {\n      api: api,\n      geo: geo,\n      mapOrGeoModel: mapOrGeoModel,\n      data: data,\n      isVisualEncodedByVisualMap: isVisualEncodedByVisualMap,\n      isGeo: isGeo,\n      transformInfoRaw: transformInfoRaw\n    };\n\n    if (geo.resourceType === 'geoJSON') {\n      this._buildGeoJSON(viewBuildCtx);\n    } else if (geo.resourceType === 'geoSVG') {\n      this._buildSVG(viewBuildCtx);\n    }\n\n    this._updateController(mapOrGeoModel, ecModel, api);\n\n    this._updateMapSelectHandler(mapOrGeoModel, regionsGroup, api, fromView);\n  };\n\n  MapDraw.prototype._buildGeoJSON = function (viewBuildCtx) {\n    var regionsGroupByName = this._regionsGroupByName = createHashMap();\n    var regionsInfoByName = createHashMap();\n    var regionsGroup = this._regionsGroup;\n    var transformInfoRaw = viewBuildCtx.transformInfoRaw;\n    var mapOrGeoModel = viewBuildCtx.mapOrGeoModel;\n    var data = viewBuildCtx.data;\n    var projection = viewBuildCtx.geo.projection;\n    var projectionStream = projection && projection.stream;\n\n    function transformPoint(point, project) {\n      if (project) {\n        // projection may return null point.\n        point = project(point);\n      }\n\n      return point && [point[0] * transformInfoRaw.scaleX + transformInfoRaw.x, point[1] * transformInfoRaw.scaleY + transformInfoRaw.y];\n    }\n\n    function transformPolygonPoints(inPoints) {\n      var outPoints = []; // If projectionStream is provided. Use it instead of single point project.\n\n      var project = !projectionStream && projection && projection.project;\n\n      for (var i = 0; i < inPoints.length; ++i) {\n        var newPt = transformPoint(inPoints[i], project);\n        newPt && outPoints.push(newPt);\n      }\n\n      return outPoints;\n    }\n\n    function getPolyShape(points) {\n      return {\n        shape: {\n          points: transformPolygonPoints(points)\n        }\n      };\n    }\n\n    regionsGroup.removeAll(); // Only when the resource is GeoJSON, there is `geo.regions`.\n\n    each(viewBuildCtx.geo.regions, function (region) {\n      var regionName = region.name; // Consider in GeoJson properties.name may be duplicated, for example,\n      // there is multiple region named \"United Kindom\" or \"France\" (so many\n      // colonies). And it is not appropriate to merge them in geo, which\n      // will make them share the same label and bring trouble in label\n      // location calculation.\n\n      var regionGroup = regionsGroupByName.get(regionName);\n\n      var _a = regionsInfoByName.get(regionName) || {},\n          dataIdx = _a.dataIdx,\n          regionModel = _a.regionModel;\n\n      if (!regionGroup) {\n        regionGroup = regionsGroupByName.set(regionName, new Group());\n        regionsGroup.add(regionGroup);\n        dataIdx = data ? data.indexOfName(regionName) : null;\n        regionModel = viewBuildCtx.isGeo ? mapOrGeoModel.getRegionModel(regionName) : data ? data.getItemModel(dataIdx) : null;\n        regionsInfoByName.set(regionName, {\n          dataIdx: dataIdx,\n          regionModel: regionModel\n        });\n      }\n\n      var polygonSubpaths = [];\n      var polylineSubpaths = [];\n      each(region.geometries, function (geometry) {\n        // Polygon and MultiPolygon\n        if (geometry.type === 'polygon') {\n          var polys = [geometry.exterior].concat(geometry.interiors || []);\n\n          if (projectionStream) {\n            polys = projectPolys(polys, projectionStream);\n          }\n\n          each(polys, function (poly) {\n            polygonSubpaths.push(new Polygon(getPolyShape(poly)));\n          });\n        } // LineString and MultiLineString\n        else {\n            var points = geometry.points;\n\n            if (projectionStream) {\n              points = projectPolys(points, projectionStream, true);\n            }\n\n            each(points, function (points) {\n              polylineSubpaths.push(new Polyline(getPolyShape(points)));\n            });\n          }\n      });\n      var centerPt = transformPoint(region.getCenter(), projection && projection.project);\n\n      function createCompoundPath(subpaths, isLine) {\n        if (!subpaths.length) {\n          return;\n        }\n\n        var compoundPath = new CompoundPath({\n          culling: true,\n          segmentIgnoreThreshold: 1,\n          shape: {\n            paths: subpaths\n          }\n        });\n        regionGroup.add(compoundPath);\n        applyOptionStyleForRegion(viewBuildCtx, compoundPath, dataIdx, regionModel);\n        resetLabelForRegion(viewBuildCtx, compoundPath, regionName, regionModel, mapOrGeoModel, dataIdx, centerPt);\n\n        if (isLine) {\n          fixLineStyle(compoundPath);\n          each(compoundPath.states, fixLineStyle);\n        }\n      }\n\n      createCompoundPath(polygonSubpaths);\n      createCompoundPath(polylineSubpaths, true);\n    }); // Ensure children have been added to `regionGroup` before calling them.\n\n    regionsGroupByName.each(function (regionGroup, regionName) {\n      var _a = regionsInfoByName.get(regionName),\n          dataIdx = _a.dataIdx,\n          regionModel = _a.regionModel;\n\n      resetEventTriggerForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel, dataIdx);\n      resetTooltipForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel);\n      resetStateTriggerForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel);\n    }, this);\n  };\n\n  MapDraw.prototype._buildSVG = function (viewBuildCtx) {\n    var mapName = viewBuildCtx.geo.map;\n    var transformInfoRaw = viewBuildCtx.transformInfoRaw;\n    this._svgGroup.x = transformInfoRaw.x;\n    this._svgGroup.y = transformInfoRaw.y;\n    this._svgGroup.scaleX = transformInfoRaw.scaleX;\n    this._svgGroup.scaleY = transformInfoRaw.scaleY;\n\n    if (this._svgResourceChanged(mapName)) {\n      this._freeSVG();\n\n      this._useSVG(mapName);\n    }\n\n    var svgDispatcherMap = this._svgDispatcherMap = createHashMap();\n    var focusSelf = false;\n    each(this._svgGraphicRecord.named, function (namedItem) {\n      // Note that we also allow different elements have the same name.\n      // For example, a glyph of a city and the label of the city have\n      // the same name and their tooltip info can be defined in a single\n      // region option.\n      var regionName = namedItem.name;\n      var mapOrGeoModel = viewBuildCtx.mapOrGeoModel;\n      var data = viewBuildCtx.data;\n      var svgNodeTagLower = namedItem.svgNodeTagLower;\n      var el = namedItem.el;\n      var dataIdx = data ? data.indexOfName(regionName) : null;\n      var regionModel = mapOrGeoModel.getRegionModel(regionName);\n\n      if (OPTION_STYLE_ENABLED_TAG_MAP.get(svgNodeTagLower) != null && el instanceof Displayable) {\n        applyOptionStyleForRegion(viewBuildCtx, el, dataIdx, regionModel);\n      }\n\n      if (el instanceof Displayable) {\n        el.culling = true;\n      } // We do not know how the SVG like so we'd better not to change z2.\n      // Otherwise it might bring some unexpected result. For example,\n      // an area hovered that make some inner city can not be clicked.\n\n\n      el.z2EmphasisLift = 0; // If self named:\n\n      if (!namedItem.namedFrom) {\n        // label should batter to be displayed based on the center of <g>\n        // if it is named rather than displayed on each child.\n        if (LABEL_HOST_MAP.get(svgNodeTagLower) != null) {\n          resetLabelForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx, null);\n        }\n\n        resetEventTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx);\n        resetTooltipForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel);\n\n        if (STATE_TRIGGER_TAG_MAP.get(svgNodeTagLower) != null) {\n          var focus_1 = resetStateTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel);\n\n          if (focus_1 === 'self') {\n            focusSelf = true;\n          }\n\n          var els = svgDispatcherMap.get(regionName) || svgDispatcherMap.set(regionName, []);\n          els.push(el);\n        }\n      }\n    }, this);\n\n    this._enableBlurEntireSVG(focusSelf, viewBuildCtx);\n  };\n\n  MapDraw.prototype._enableBlurEntireSVG = function (focusSelf, viewBuildCtx) {\n    // It's a little complicated to support blurring the entire geoSVG in series-map.\n    // So do not suport it until some requirements come.\n    // At present, in series-map, only regions can be blurred.\n    if (focusSelf && viewBuildCtx.isGeo) {\n      var blurStyle = viewBuildCtx.mapOrGeoModel.getModel(['blur', 'itemStyle']).getItemStyle(); // Only suport `opacity` here. Because not sure that other props are suitable for\n      // all of the elements generated by SVG (especially for Text/TSpan/Image/... ).\n\n      var opacity_1 = blurStyle.opacity;\n\n      this._svgGraphicRecord.root.traverse(function (el) {\n        if (!el.isGroup) {\n          // PENDING: clear those settings to SVG elements when `_freeSVG`.\n          // (Currently it happen not to be needed.)\n          setDefaultStateProxy(el);\n          var style = el.ensureState('blur').style || {}; // Do not overwrite the region style that already set from region option.\n\n          if (style.opacity == null && opacity_1 != null) {\n            style.opacity = opacity_1;\n          } // If `ensureState('blur').style = {}`, there will be default opacity.\n          // Enable `stateTransition` (animation).\n\n\n          el.ensureState('emphasis');\n        }\n      });\n    }\n  };\n\n  MapDraw.prototype.remove = function () {\n    this._regionsGroup.removeAll();\n\n    this._regionsGroupByName = null;\n\n    this._svgGroup.removeAll();\n\n    this._freeSVG();\n\n    this._controller.dispose();\n\n    this._controllerHost = null;\n  };\n\n  MapDraw.prototype.findHighDownDispatchers = function (name, geoModel) {\n    if (name == null) {\n      return [];\n    }\n\n    var geo = geoModel.coordinateSystem;\n\n    if (geo.resourceType === 'geoJSON') {\n      var regionsGroupByName = this._regionsGroupByName;\n\n      if (regionsGroupByName) {\n        var regionGroup = regionsGroupByName.get(name);\n        return regionGroup ? [regionGroup] : [];\n      }\n    } else if (geo.resourceType === 'geoSVG') {\n      return this._svgDispatcherMap && this._svgDispatcherMap.get(name) || [];\n    }\n  };\n\n  MapDraw.prototype._svgResourceChanged = function (mapName) {\n    return this._svgMapName !== mapName;\n  };\n\n  MapDraw.prototype._useSVG = function (mapName) {\n    var resource = geoSourceManager.getGeoResource(mapName);\n\n    if (resource && resource.type === 'geoSVG') {\n      var svgGraphic = resource.useGraphic(this.uid);\n\n      this._svgGroup.add(svgGraphic.root);\n\n      this._svgGraphicRecord = svgGraphic;\n      this._svgMapName = mapName;\n    }\n  };\n\n  MapDraw.prototype._freeSVG = function () {\n    var mapName = this._svgMapName;\n\n    if (mapName == null) {\n      return;\n    }\n\n    var resource = geoSourceManager.getGeoResource(mapName);\n\n    if (resource && resource.type === 'geoSVG') {\n      resource.freeGraphic(this.uid);\n    }\n\n    this._svgGraphicRecord = null;\n    this._svgDispatcherMap = null;\n\n    this._svgGroup.removeAll();\n\n    this._svgMapName = null;\n  };\n\n  MapDraw.prototype._updateController = function (mapOrGeoModel, ecModel, api) {\n    var geo = mapOrGeoModel.coordinateSystem;\n    var controller = this._controller;\n    var controllerHost = this._controllerHost; // @ts-ignore FIXME:TS\n\n    controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit');\n    controllerHost.zoom = geo.getZoom(); // roamType is will be set default true if it is null\n    // @ts-ignore FIXME:TS\n\n    controller.enable(mapOrGeoModel.get('roam') || false);\n    var mainType = mapOrGeoModel.mainType;\n\n    function makeActionBase() {\n      var action = {\n        type: 'geoRoam',\n        componentType: mainType\n      };\n      action[mainType + 'Id'] = mapOrGeoModel.id;\n      return action;\n    }\n\n    controller.off('pan').on('pan', function (e) {\n      this._mouseDownFlag = false;\n      updateViewOnPan(controllerHost, e.dx, e.dy);\n      api.dispatchAction(extend(makeActionBase(), {\n        dx: e.dx,\n        dy: e.dy,\n        animation: {\n          duration: 0\n        }\n      }));\n    }, this);\n    controller.off('zoom').on('zoom', function (e) {\n      this._mouseDownFlag = false;\n      updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);\n      api.dispatchAction(extend(makeActionBase(), {\n        zoom: e.scale,\n        originX: e.originX,\n        originY: e.originY,\n        animation: {\n          duration: 0\n        }\n      }));\n    }, this);\n    controller.setPointerChecker(function (e, x, y) {\n      return geo.containPoint([x, y]) && !onIrrelevantElement(e, api, mapOrGeoModel);\n    });\n  };\n  /**\n   * FIXME: this is a temporarily workaround.\n   * When `geoRoam` the elements need to be reset in `MapView['render']`, because the props like\n   * `ignore` might have been modified by `LabelManager`, and `LabelManager#addLabelsOfSeries`\n   * will subsequently cache `defaultAttr` like `ignore`. If do not do this reset, the modified\n   * props will have no chance to be restored.\n   * Note: This reset should be after `clearStates` in `renderSeries` because `useStates` in\n   * `renderSeries` will cache the modified `ignore` to `el._normalState`.\n   * TODO:\n   * Use clone/immutable in `LabelManager`?\n   */\n\n\n  MapDraw.prototype.resetForLabelLayout = function () {\n    this.group.traverse(function (el) {\n      var label = el.getTextContent();\n\n      if (label) {\n        label.ignore = mapLabelRaw(label).ignore;\n      }\n    });\n  };\n\n  MapDraw.prototype._updateMapSelectHandler = function (mapOrGeoModel, regionsGroup, api, fromView) {\n    var mapDraw = this;\n    regionsGroup.off('mousedown');\n    regionsGroup.off('click'); // @ts-ignore FIXME:TS resolve type conflict\n\n    if (mapOrGeoModel.get('selectedMode')) {\n      regionsGroup.on('mousedown', function () {\n        mapDraw._mouseDownFlag = true;\n      });\n      regionsGroup.on('click', function (e) {\n        if (!mapDraw._mouseDownFlag) {\n          return;\n        }\n\n        mapDraw._mouseDownFlag = false;\n      });\n    }\n  };\n\n  return MapDraw;\n}();\n\nfunction applyOptionStyleForRegion(viewBuildCtx, el, dataIndex, regionModel) {\n  // All of the path are using `itemStyle`, because\n  // (1) Some SVG also use fill on polyline (The different between\n  // polyline and polygon is \"open\" or \"close\" but not fill or not).\n  // (2) For the common props like opacity, if some use itemStyle\n  // and some use `lineStyle`, it might confuse users.\n  // (3) Most SVG use <path>, where can not detect wether draw a \"line\"\n  // or a filled shape, so use `itemStyle` for <path>.\n  var normalStyleModel = regionModel.getModel('itemStyle');\n  var emphasisStyleModel = regionModel.getModel(['emphasis', 'itemStyle']);\n  var blurStyleModel = regionModel.getModel(['blur', 'itemStyle']);\n  var selectStyleModel = regionModel.getModel(['select', 'itemStyle']); // NOTE: DONT use 'style' in visual when drawing map.\n  // This component is used for drawing underlying map for both geo component and map series.\n\n  var normalStyle = getFixedItemStyle(normalStyleModel);\n  var emphasisStyle = getFixedItemStyle(emphasisStyleModel);\n  var selectStyle = getFixedItemStyle(selectStyleModel);\n  var blurStyle = getFixedItemStyle(blurStyleModel); // Update the itemStyle if has data visual\n\n  var data = viewBuildCtx.data;\n\n  if (data) {\n    // Only visual color of each item will be used. It can be encoded by visualMap\n    // But visual color of series is used in symbol drawing\n    // Visual color for each series is for the symbol draw\n    var style = data.getItemVisual(dataIndex, 'style');\n    var decal = data.getItemVisual(dataIndex, 'decal');\n\n    if (viewBuildCtx.isVisualEncodedByVisualMap && style.fill) {\n      normalStyle.fill = style.fill;\n    }\n\n    if (decal) {\n      normalStyle.decal = createOrUpdatePatternFromDecal(decal, viewBuildCtx.api);\n    }\n  } // SVG text, tspan and image can be named but not supporeted\n  // to be styled by region option yet.\n\n\n  el.setStyle(normalStyle);\n  el.style.strokeNoScale = true;\n  el.ensureState('emphasis').style = emphasisStyle;\n  el.ensureState('select').style = selectStyle;\n  el.ensureState('blur').style = blurStyle; // Enable blur\n\n  setDefaultStateProxy(el);\n}\n\nfunction resetLabelForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, // Exist only if `viewBuildCtx.data` exists.\ndataIdx, // If labelXY not provided, use `textConfig.position: 'inside'`\nlabelXY) {\n  var data = viewBuildCtx.data;\n  var isGeo = viewBuildCtx.isGeo;\n  var isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx));\n  var itemLayout = data && data.getItemLayout(dataIdx); // In the following cases label will be drawn\n  // 1. In map series and data value is NaN\n  // 2. In geo component\n  // 3. Region has no series legendIcon, which will be add a showLabel flag in mapSymbolLayout\n\n  if (isGeo || isDataNaN || itemLayout && itemLayout.showLabel) {\n    var query = !isGeo ? dataIdx : regionName;\n    var labelFetcher = void 0; // Consider dataIdx not found.\n\n    if (!data || dataIdx >= 0) {\n      labelFetcher = mapOrGeoModel;\n    }\n\n    var specifiedTextOpt = labelXY ? {\n      normal: {\n        align: 'center',\n        verticalAlign: 'middle'\n      }\n    } : null; // Caveat: must be called after `setDefaultStateProxy(el);` called.\n    // because textContent will be assign with `el.stateProxy` inside.\n\n    setLabelStyle(el, getLabelStatesModels(regionModel), {\n      labelFetcher: labelFetcher,\n      labelDataIndex: query,\n      defaultText: regionName\n    }, specifiedTextOpt);\n    var textEl = el.getTextContent();\n\n    if (textEl) {\n      mapLabelRaw(textEl).ignore = textEl.ignore;\n\n      if (el.textConfig && labelXY) {\n        // Compute a relative offset based on the el bounding rect.\n        var rect = el.getBoundingRect().clone(); // Need to make sure the percent position base on the same rect in normal and\n        // emphasis state. Otherwise if using boundingRect of el, but the emphasis state\n        // has borderWidth (even 0.5px), the text position will be changed obviously\n        // if the position is very big like ['1234%', '1345%'].\n\n        el.textConfig.layoutRect = rect;\n        el.textConfig.position = [(labelXY[0] - rect.x) / rect.width * 100 + '%', (labelXY[1] - rect.y) / rect.height * 100 + '%'];\n      }\n    } // PENDING:\n    // If labelLayout is enabled (test/label-layout.html), el.dataIndex should be specified.\n    // But el.dataIndex is also used to determine whether user event should be triggered,\n    // where el.seriesIndex or el.dataModel must be specified. At present for a single el\n    // there is not case that \"only label layout enabled but user event disabled\", so here\n    // we depends `resetEventTriggerForRegion` to do the job of setting `el.dataIndex`.\n\n\n    el.disableLabelAnimation = true;\n  } else {\n    el.removeTextContent();\n    el.removeTextConfig();\n    el.disableLabelAnimation = null;\n  }\n}\n\nfunction resetEventTriggerForRegion(viewBuildCtx, eventTrigger, regionName, regionModel, mapOrGeoModel, // Exist only if `viewBuildCtx.data` exists.\ndataIdx) {\n  // setItemGraphicEl, setHoverStyle after all polygons and labels\n  // are added to the regionGroup\n  if (viewBuildCtx.data) {\n    // FIXME: when series-map use a SVG map, and there are duplicated name specified\n    // on different SVG elements, after `data.setItemGraphicEl(...)`:\n    // (1) all of them will be mounted with `dataIndex`, `seriesIndex`, so that tooltip\n    // can be triggered only mouse hover. That's correct.\n    // (2) only the last element will be kept in `data`, so that if trigger tooltip\n    // by `dispatchAction`, only the last one can be found and triggered. That might be\n    // not correct. We will fix it in future if anyone demanding that.\n    viewBuildCtx.data.setItemGraphicEl(dataIdx, eventTrigger);\n  } // series-map will not trigger \"geoselectchange\" no matter it is\n  // based on a declared geo component. Because series-map will\n  // trigger \"selectchange\". If it trigger both the two events,\n  // If users call `chart.dispatchAction({type: 'toggleSelect'})`,\n  // it not easy to also fire event \"geoselectchanged\".\n  else {\n      // Package custom mouse event for geo component\n      getECData(eventTrigger).eventData = {\n        componentType: 'geo',\n        componentIndex: mapOrGeoModel.componentIndex,\n        geoIndex: mapOrGeoModel.componentIndex,\n        name: regionName,\n        region: regionModel && regionModel.option || {}\n      };\n    }\n}\n\nfunction resetTooltipForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel) {\n  if (!viewBuildCtx.data) {\n    setTooltipConfig({\n      el: el,\n      componentModel: mapOrGeoModel,\n      itemName: regionName,\n      // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n      itemTooltipOption: regionModel.get('tooltip')\n    });\n  }\n}\n\nfunction resetStateTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel) {\n  // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n  el.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode'); // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n\n  var emphasisModel = regionModel.getModel('emphasis');\n  var focus = emphasisModel.get('focus');\n  toggleHoverEmphasis(el, focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n\n  if (viewBuildCtx.isGeo) {\n    enableComponentHighDownFeatures(el, mapOrGeoModel, regionName);\n  }\n\n  return focus;\n}\n\nfunction projectPolys(rings, // Polygons include exterior and interiors. Or polylines.\ncreateStream, isLine) {\n  var polygons = [];\n  var curPoly;\n\n  function startPolygon() {\n    curPoly = [];\n  }\n\n  function endPolygon() {\n    if (curPoly.length) {\n      polygons.push(curPoly);\n      curPoly = [];\n    }\n  }\n\n  var stream = createStream({\n    polygonStart: startPolygon,\n    polygonEnd: endPolygon,\n    lineStart: startPolygon,\n    lineEnd: endPolygon,\n    point: function (x, y) {\n      // May have NaN values from stream.\n      if (isFinite(x) && isFinite(y)) {\n        curPoly.push([x, y]);\n      }\n    },\n    sphere: function () {}\n  });\n  !isLine && stream.polygonStart();\n  each(rings, function (ring) {\n    stream.lineStart();\n\n    for (var i = 0; i < ring.length; i++) {\n      stream.point(ring[i][0], ring[i][1]);\n    }\n\n    stream.lineEnd();\n  });\n  !isLine && stream.polygonEnd();\n  return polygons;\n}\n // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n\nvar MapView =\n/** @class */\nfunction (_super) {\n  __extends(MapView, _super);\n\n  function MapView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MapView.type;\n    return _this;\n  }\n\n  MapView.prototype.render = function (mapModel, ecModel, api, payload) {\n    // Not render if it is an toggleSelect action from self\n    if (payload && payload.type === 'mapToggleSelect' && payload.from === this.uid) {\n      return;\n    }\n\n    var group = this.group;\n    group.removeAll();\n\n    if (mapModel.getHostGeoModel()) {\n      return;\n    }\n\n    if (this._mapDraw && payload && payload.type === 'geoRoam') {\n      this._mapDraw.resetForLabelLayout();\n    } // Not update map if it is an roam action from self\n\n\n    if (!(payload && payload.type === 'geoRoam' && payload.componentType === 'series' && payload.seriesId === mapModel.id)) {\n      if (mapModel.needsDrawMap) {\n        var mapDraw = this._mapDraw || new MapDraw(api);\n        group.add(mapDraw.group);\n        mapDraw.draw(mapModel, ecModel, api, this, payload);\n        this._mapDraw = mapDraw;\n      } else {\n        // Remove drawn map\n        this._mapDraw && this._mapDraw.remove();\n        this._mapDraw = null;\n      }\n    } else {\n      var mapDraw = this._mapDraw;\n      mapDraw && group.add(mapDraw.group);\n    }\n\n    mapModel.get('showLegendSymbol') && ecModel.getComponent('legend') && this._renderSymbols(mapModel, ecModel, api);\n  };\n\n  MapView.prototype.remove = function () {\n    this._mapDraw && this._mapDraw.remove();\n    this._mapDraw = null;\n    this.group.removeAll();\n  };\n\n  MapView.prototype.dispose = function () {\n    this._mapDraw && this._mapDraw.remove();\n    this._mapDraw = null;\n  };\n\n  MapView.prototype._renderSymbols = function (mapModel, ecModel, api) {\n    var originalData = mapModel.originalData;\n    var group = this.group;\n    originalData.each(originalData.mapDimension('value'), function (value, originalDataIndex) {\n      if (isNaN(value)) {\n        return;\n      }\n\n      var layout = originalData.getItemLayout(originalDataIndex);\n\n      if (!layout || !layout.point) {\n        // Not exists in map\n        return;\n      }\n\n      var point = layout.point;\n      var offset = layout.offset;\n      var circle = new Circle({\n        style: {\n          // Because the special of map draw.\n          // Which needs statistic of multiple series and draw on one map.\n          // And each series also need a symbol with legend color\n          //\n          // Layout and visual are put one the different data\n          // TODO\n          fill: mapModel.getData().getVisual('style').fill\n        },\n        shape: {\n          cx: point[0] + offset * 9,\n          cy: point[1],\n          r: 3\n        },\n        silent: true,\n        // Do not overlap the first series, on which labels are displayed.\n        z2: 8 + (!offset ? Z2_EMPHASIS_LIFT + 1 : 0)\n      }); // Only the series that has the first value on the same region is in charge of rendering the label.\n      // But consider the case:\n      // series: [\n      //     {id: 'X', type: 'map', map: 'm', {data: [{name: 'A', value: 11}, {name: 'B', {value: 22}]},\n      //     {id: 'Y', type: 'map', map: 'm', {data: [{name: 'A', value: 21}, {name: 'C', {value: 33}]}\n      // ]\n      // The offset `0` of item `A` is at series `X`, but of item `C` is at series `Y`.\n      // For backward compatibility, we follow the rule that render label `A` by the\n      // settings on series `X` but render label `C` by the settings on series `Y`.\n\n      if (!offset) {\n        var fullData = mapModel.mainSeries.getData();\n        var name_1 = originalData.getName(originalDataIndex);\n        var fullIndex_1 = fullData.indexOfName(name_1);\n        var itemModel = originalData.getItemModel(originalDataIndex);\n        var labelModel = itemModel.getModel('label');\n        var regionGroup = fullData.getItemGraphicEl(fullIndex_1); // `getFormattedLabel` needs to use `getData` inside. Here\n        // `mapModel.getData()` is shallow cloned from `mainSeries.getData()`.\n        // FIXME\n        // If this is not the `mainSeries`, the item model (like label formatter)\n        // set on original data item will never get. But it has been working\n        // like that from the beginning, and this scenario is rarely encountered.\n        // So it won't be fixed until we have to.\n\n        setLabelStyle(circle, getLabelStatesModels(itemModel), {\n          labelFetcher: {\n            getFormattedLabel: function (idx, state) {\n              return mapModel.getFormattedLabel(fullIndex_1, state);\n            }\n          },\n          defaultText: name_1\n        });\n        circle.disableLabelAnimation = true;\n\n        if (!labelModel.get('position')) {\n          circle.setTextConfig({\n            position: 'bottom'\n          });\n        }\n\n        regionGroup.onHoverStateChange = function (toState) {\n          setStatesFlag(circle, toState);\n        };\n      }\n\n      group.add(circle);\n    });\n  };\n\n  MapView.type = 'map';\n  return MapView;\n}(ChartView);\n\nvar MapSeries =\n/** @class */\nfunction (_super) {\n  __extends(MapSeries, _super);\n\n  function MapSeries() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MapSeries.type; // Only first map series of same mapType will drawMap.\n\n    _this.needsDrawMap = false; // Group of all map series with same mapType\n\n    _this.seriesGroup = [];\n\n    _this.getTooltipPosition = function (dataIndex) {\n      if (dataIndex != null) {\n        var name_1 = this.getData().getName(dataIndex);\n        var geo = this.coordinateSystem;\n        var region = geo.getRegion(name_1);\n        return region && geo.dataToPoint(region.getCenter());\n      }\n    };\n\n    return _this;\n  }\n\n  MapSeries.prototype.getInitialData = function (option) {\n    var data = createSeriesDataSimply(this, {\n      coordDimensions: ['value'],\n      encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n    });\n    var dataNameMap = createHashMap();\n    var toAppendNames = [];\n\n    for (var i = 0, len = data.count(); i < len; i++) {\n      var name_2 = data.getName(i);\n      dataNameMap.set(name_2, true);\n    }\n\n    var geoSource = geoSourceManager.load(this.getMapType(), this.option.nameMap, this.option.nameProperty);\n    each(geoSource.regions, function (region) {\n      var name = region.name;\n\n      if (!dataNameMap.get(name)) {\n        toAppendNames.push(name);\n      }\n    }); // Complete data with missing regions. The consequent processes (like visual\n    // map and render) can not be performed without a \"full data\". For example,\n    // find `dataIndex` by name.\n\n    data.appendValues([], toAppendNames);\n    return data;\n  };\n  /**\n   * If no host geo model, return null, which means using a\n   * inner exclusive geo model.\n   */\n\n\n  MapSeries.prototype.getHostGeoModel = function () {\n    var geoIndex = this.option.geoIndex;\n    return geoIndex != null ? this.ecModel.getComponent('geo', geoIndex) : null;\n  };\n\n  MapSeries.prototype.getMapType = function () {\n    return (this.getHostGeoModel() || this).option.map;\n  }; // _fillOption(option, mapName) {\n  // Shallow clone\n  // option = zrUtil.extend({}, option);\n  // option.data = geoCreator.getFilledRegions(option.data, mapName, option.nameMap);\n  // return option;\n  // }\n\n\n  MapSeries.prototype.getRawValue = function (dataIndex) {\n    // Use value stored in data instead because it is calculated from multiple series\n    // FIXME Provide all value of multiple series ?\n    var data = this.getData();\n    return data.get(data.mapDimension('value'), dataIndex);\n  };\n  /**\n   * Get model of region\n   */\n\n\n  MapSeries.prototype.getRegionModel = function (regionName) {\n    var data = this.getData();\n    return data.getItemModel(data.indexOfName(regionName));\n  };\n  /**\n   * Map tooltip formatter\n   */\n\n\n  MapSeries.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    // FIXME orignalData and data is a bit confusing\n    var data = this.getData();\n    var value = this.getRawValue(dataIndex);\n    var name = data.getName(dataIndex);\n    var seriesGroup = this.seriesGroup;\n    var seriesNames = [];\n\n    for (var i = 0; i < seriesGroup.length; i++) {\n      var otherIndex = seriesGroup[i].originalData.indexOfName(name);\n      var valueDim = data.mapDimension('value');\n\n      if (!isNaN(seriesGroup[i].originalData.get(valueDim, otherIndex))) {\n        seriesNames.push(seriesGroup[i].name);\n      }\n    }\n\n    return createTooltipMarkup('section', {\n      header: seriesNames.join(', '),\n      noHeader: !seriesNames.length,\n      blocks: [createTooltipMarkup('nameValue', {\n        name: name,\n        value: value\n      })]\n    });\n  };\n\n  MapSeries.prototype.setZoom = function (zoom) {\n    this.option.zoom = zoom;\n  };\n\n  MapSeries.prototype.setCenter = function (center) {\n    this.option.center = center;\n  };\n\n  MapSeries.prototype.getLegendIcon = function (opt) {\n    var iconType = opt.icon || 'roundRect';\n    var icon = createSymbol(iconType, 0, 0, opt.itemWidth, opt.itemHeight, opt.itemStyle.fill);\n    icon.setStyle(opt.itemStyle); // Map do not use itemStyle.borderWidth as border width\n\n    icon.style.stroke = 'none'; // No rotation because no series visual symbol for map\n\n    if (iconType.indexOf('empty') > -1) {\n      icon.style.stroke = icon.style.fill;\n      icon.style.fill = '#fff';\n      icon.style.lineWidth = 2;\n    }\n\n    return icon;\n  };\n\n  MapSeries.type = 'series.map';\n  MapSeries.dependencies = ['geo'];\n  MapSeries.layoutMode = 'box';\n  MapSeries.defaultOption = {\n    // 一级层叠\n    // zlevel: 0,\n    // 二级层叠\n    z: 2,\n    coordinateSystem: 'geo',\n    // map should be explicitly specified since ec3.\n    map: '',\n    // If `geoIndex` is not specified, a exclusive geo will be\n    // created. Otherwise use the specified geo component, and\n    // `map` and `mapType` are ignored.\n    // geoIndex: 0,\n    // 'center' | 'left' | 'right' | 'x%' | {number}\n    left: 'center',\n    // 'center' | 'top' | 'bottom' | 'x%' | {number}\n    top: 'center',\n    // right\n    // bottom\n    // width:\n    // height\n    // Aspect is width / height. Inited to be geoJson bbox aspect\n    // This parameter is used for scale this aspect\n    // Default value:\n    // for geoSVG source: 1,\n    // for geoJSON source: 0.75.\n    aspectScale: null,\n    // Layout with center and size\n    // If you want to put map in a fixed size box with right aspect ratio\n    // This two properties may be more convenient.\n    // layoutCenter: [50%, 50%]\n    // layoutSize: 100\n    showLegendSymbol: true,\n    // Define left-top, right-bottom coords to control view\n    // For example, [ [180, 90], [-180, -90] ],\n    // higher priority than center and zoom\n    boundingCoords: null,\n    // Default on center of map\n    center: null,\n    zoom: 1,\n    scaleLimit: null,\n    selectedMode: true,\n    label: {\n      show: false,\n      color: '#000'\n    },\n    // scaleLimit: null,\n    itemStyle: {\n      borderWidth: 0.5,\n      borderColor: '#444',\n      areaColor: '#eee'\n    },\n    emphasis: {\n      label: {\n        show: true,\n        color: 'rgb(100,0,0)'\n      },\n      itemStyle: {\n        areaColor: 'rgba(255,215,0,0.8)'\n      }\n    },\n    select: {\n      label: {\n        show: true,\n        color: 'rgb(100,0,0)'\n      },\n      itemStyle: {\n        color: 'rgba(255,215,0,0.8)'\n      }\n    },\n    nameProperty: 'name'\n  };\n  return MapSeries;\n}(SeriesModel);\n\nfunction dataStatistics(datas, statisticType) {\n  var dataNameMap = {};\n  each(datas, function (data) {\n    data.each(data.mapDimension('value'), function (value, idx) {\n      // Add prefix to avoid conflict with Object.prototype.\n      var mapKey = 'ec-' + data.getName(idx);\n      dataNameMap[mapKey] = dataNameMap[mapKey] || [];\n\n      if (!isNaN(value)) {\n        dataNameMap[mapKey].push(value);\n      }\n    });\n  });\n  return datas[0].map(datas[0].mapDimension('value'), function (value, idx) {\n    var mapKey = 'ec-' + datas[0].getName(idx);\n    var sum = 0;\n    var min = Infinity;\n    var max = -Infinity;\n    var len = dataNameMap[mapKey].length;\n\n    for (var i = 0; i < len; i++) {\n      min = Math.min(min, dataNameMap[mapKey][i]);\n      max = Math.max(max, dataNameMap[mapKey][i]);\n      sum += dataNameMap[mapKey][i];\n    }\n\n    var result;\n\n    if (statisticType === 'min') {\n      result = min;\n    } else if (statisticType === 'max') {\n      result = max;\n    } else if (statisticType === 'average') {\n      result = sum / len;\n    } else {\n      result = sum;\n    }\n\n    return len === 0 ? NaN : result;\n  });\n}\n\nfunction mapDataStatistic(ecModel) {\n  var seriesGroups = {};\n  ecModel.eachSeriesByType('map', function (seriesModel) {\n    var hostGeoModel = seriesModel.getHostGeoModel();\n    var key = hostGeoModel ? 'o' + hostGeoModel.id : 'i' + seriesModel.getMapType();\n    (seriesGroups[key] = seriesGroups[key] || []).push(seriesModel);\n  });\n  each(seriesGroups, function (seriesList, key) {\n    var data = dataStatistics(map(seriesList, function (seriesModel) {\n      return seriesModel.getData();\n    }), seriesList[0].get('mapValueCalculation'));\n\n    for (var i = 0; i < seriesList.length; i++) {\n      seriesList[i].originalData = seriesList[i].getData();\n    } // FIXME Put where?\n\n\n    for (var i = 0; i < seriesList.length; i++) {\n      seriesList[i].seriesGroup = seriesList;\n      seriesList[i].needsDrawMap = i === 0 && !seriesList[i].getHostGeoModel();\n      seriesList[i].setData(data.cloneShallow());\n      seriesList[i].mainSeries = seriesList[0];\n    }\n  });\n}\n\nfunction mapSymbolLayout(ecModel) {\n  var processedMapType = {};\n  ecModel.eachSeriesByType('map', function (mapSeries) {\n    var mapType = mapSeries.getMapType();\n\n    if (mapSeries.getHostGeoModel() || processedMapType[mapType]) {\n      return;\n    }\n\n    var mapSymbolOffsets = {};\n    each(mapSeries.seriesGroup, function (subMapSeries) {\n      var geo = subMapSeries.coordinateSystem;\n      var data = subMapSeries.originalData;\n\n      if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) {\n        data.each(data.mapDimension('value'), function (value, idx) {\n          var name = data.getName(idx);\n          var region = geo.getRegion(name); // If input series.data is [11, 22, '-'/null/undefined, 44],\n          // it will be filled with NaN: [11, 22, NaN, 44] and NaN will\n          // not be drawn. So here must validate if value is NaN.\n\n          if (!region || isNaN(value)) {\n            return;\n          }\n\n          var offset = mapSymbolOffsets[name] || 0;\n          var point = geo.dataToPoint(region.getCenter());\n          mapSymbolOffsets[name] = offset + 1;\n          data.setItemLayout(idx, {\n            point: point,\n            offset: offset\n          });\n        });\n      }\n    }); // Show label of those region not has legendIcon (which is offset 0)\n\n    var data = mapSeries.getData();\n    data.each(function (idx) {\n      var name = data.getName(idx);\n      var layout = data.getItemLayout(idx) || {};\n      layout.showLabel = !mapSymbolOffsets[name];\n      data.setItemLayout(idx, layout);\n    });\n    processedMapType[mapType] = true;\n  });\n}\n\nvar v2ApplyTransform = applyTransform;\n\nvar View =\n/** @class */\nfunction (_super) {\n  __extends(View, _super);\n\n  function View(name) {\n    var _this = _super.call(this) || this;\n\n    _this.type = 'view';\n    _this.dimensions = ['x', 'y'];\n    /**\n     * Represents the transform brought by roam/zoom.\n     * If `View['_viewRect']` applies roam transform,\n     * we can get the final displayed rect.\n     */\n\n    _this._roamTransformable = new Transformable();\n    /**\n     * Represents the transform from `View['_rect']` to `View['_viewRect']`.\n     */\n\n    _this._rawTransformable = new Transformable();\n    _this.name = name;\n    return _this;\n  }\n\n  View.prototype.setBoundingRect = function (x, y, width, height) {\n    this._rect = new BoundingRect(x, y, width, height);\n    return this._rect;\n  };\n  /**\n   * @return {module:zrender/core/BoundingRect}\n   */\n\n\n  View.prototype.getBoundingRect = function () {\n    return this._rect;\n  };\n\n  View.prototype.setViewRect = function (x, y, width, height) {\n    this._transformTo(x, y, width, height);\n\n    this._viewRect = new BoundingRect(x, y, width, height);\n  };\n  /**\n   * Transformed to particular position and size\n   */\n\n\n  View.prototype._transformTo = function (x, y, width, height) {\n    var rect = this.getBoundingRect();\n    var rawTransform = this._rawTransformable;\n    rawTransform.transform = rect.calculateTransform(new BoundingRect(x, y, width, height));\n    var rawParent = rawTransform.parent;\n    rawTransform.parent = null;\n    rawTransform.decomposeTransform();\n    rawTransform.parent = rawParent;\n\n    this._updateTransform();\n  };\n  /**\n   * Set center of view\n   */\n\n\n  View.prototype.setCenter = function (centerCoord, api) {\n    if (!centerCoord) {\n      return;\n    }\n\n    this._center = [parsePercent$1(centerCoord[0], api.getWidth()), parsePercent$1(centerCoord[1], api.getHeight())];\n\n    this._updateCenterAndZoom();\n  };\n\n  View.prototype.setZoom = function (zoom) {\n    zoom = zoom || 1;\n    var zoomLimit = this.zoomLimit;\n\n    if (zoomLimit) {\n      if (zoomLimit.max != null) {\n        zoom = Math.min(zoomLimit.max, zoom);\n      }\n\n      if (zoomLimit.min != null) {\n        zoom = Math.max(zoomLimit.min, zoom);\n      }\n    }\n\n    this._zoom = zoom;\n\n    this._updateCenterAndZoom();\n  };\n  /**\n   * Get default center without roam\n   */\n\n\n  View.prototype.getDefaultCenter = function () {\n    // Rect before any transform\n    var rawRect = this.getBoundingRect();\n    var cx = rawRect.x + rawRect.width / 2;\n    var cy = rawRect.y + rawRect.height / 2;\n    return [cx, cy];\n  };\n\n  View.prototype.getCenter = function () {\n    return this._center || this.getDefaultCenter();\n  };\n\n  View.prototype.getZoom = function () {\n    return this._zoom || 1;\n  };\n\n  View.prototype.getRoamTransform = function () {\n    return this._roamTransformable.getLocalTransform();\n  };\n  /**\n   * Remove roam\n   */\n\n\n  View.prototype._updateCenterAndZoom = function () {\n    // Must update after view transform updated\n    var rawTransformMatrix = this._rawTransformable.getLocalTransform();\n\n    var roamTransform = this._roamTransformable;\n    var defaultCenter = this.getDefaultCenter();\n    var center = this.getCenter();\n    var zoom = this.getZoom();\n    center = applyTransform([], center, rawTransformMatrix);\n    defaultCenter = applyTransform([], defaultCenter, rawTransformMatrix);\n    roamTransform.originX = center[0];\n    roamTransform.originY = center[1];\n    roamTransform.x = defaultCenter[0] - center[0];\n    roamTransform.y = defaultCenter[1] - center[1];\n    roamTransform.scaleX = roamTransform.scaleY = zoom;\n\n    this._updateTransform();\n  };\n  /**\n   * Update transform props on `this` based on the current\n   * `this._roamTransformable` and `this._rawTransformable`.\n   */\n\n\n  View.prototype._updateTransform = function () {\n    var roamTransformable = this._roamTransformable;\n    var rawTransformable = this._rawTransformable;\n    rawTransformable.parent = roamTransformable;\n    roamTransformable.updateTransform();\n    rawTransformable.updateTransform();\n    copy$1(this.transform || (this.transform = []), rawTransformable.transform || create$1());\n    this._rawTransform = rawTransformable.getLocalTransform();\n    this.invTransform = this.invTransform || [];\n    invert(this.invTransform, this.transform);\n    this.decomposeTransform();\n  };\n\n  View.prototype.getTransformInfo = function () {\n    var rawTransformable = this._rawTransformable;\n    var roamTransformable = this._roamTransformable; // Because roamTransformabel has `originX/originY` modified,\n    // but the caller of `getTransformInfo` can not handle `originX/originY`,\n    // so need to recalculate them.\n\n    var dummyTransformable = new Transformable();\n    dummyTransformable.transform = roamTransformable.transform;\n    dummyTransformable.decomposeTransform();\n    return {\n      roam: {\n        x: dummyTransformable.x,\n        y: dummyTransformable.y,\n        scaleX: dummyTransformable.scaleX,\n        scaleY: dummyTransformable.scaleY\n      },\n      raw: {\n        x: rawTransformable.x,\n        y: rawTransformable.y,\n        scaleX: rawTransformable.scaleX,\n        scaleY: rawTransformable.scaleY\n      }\n    };\n  };\n\n  View.prototype.getViewRect = function () {\n    return this._viewRect;\n  };\n  /**\n   * Get view rect after roam transform\n   */\n\n\n  View.prototype.getViewRectAfterRoam = function () {\n    var rect = this.getBoundingRect().clone();\n    rect.applyTransform(this.transform);\n    return rect;\n  };\n  /**\n   * Convert a single (lon, lat) data item to (x, y) point.\n   */\n\n\n  View.prototype.dataToPoint = function (data, noRoam, out) {\n    var transform = noRoam ? this._rawTransform : this.transform;\n    out = out || [];\n    return transform ? v2ApplyTransform(out, data, transform) : copy(out, data);\n  };\n  /**\n   * Convert a (x, y) point to (lon, lat) data\n   */\n\n\n  View.prototype.pointToData = function (point) {\n    var invTransform = this.invTransform;\n    return invTransform ? v2ApplyTransform([], point, invTransform) : [point[0], point[1]];\n  };\n\n  View.prototype.convertToPixel = function (ecModel, finder, value) {\n    var coordSys = getCoordSys(finder);\n    return coordSys === this ? coordSys.dataToPoint(value) : null;\n  };\n\n  View.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n    var coordSys = getCoordSys(finder);\n    return coordSys === this ? coordSys.pointToData(pixel) : null;\n  };\n  /**\n   * @implements\n   */\n\n\n  View.prototype.containPoint = function (point) {\n    return this.getViewRectAfterRoam().contain(point[0], point[1]);\n  };\n\n  View.dimensions = ['x', 'y'];\n  return View;\n}(Transformable);\n\nfunction getCoordSys(finder) {\n  var seriesModel = finder.seriesModel;\n  return seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph.\n}\n\nvar GEO_DEFAULT_PARAMS = {\n  'geoJSON': {\n    aspectScale: 0.75,\n    invertLongitute: true\n  },\n  'geoSVG': {\n    aspectScale: 1,\n    invertLongitute: false\n  }\n};\nvar geo2DDimensions = ['lng', 'lat'];\n\nvar Geo =\n/** @class */\nfunction (_super) {\n  __extends(Geo, _super);\n\n  function Geo(name, map, opt) {\n    var _this = _super.call(this, name) || this;\n\n    _this.dimensions = geo2DDimensions;\n    _this.type = 'geo'; // Only store specified name coord via `addGeoCoord`.\n\n    _this._nameCoordMap = createHashMap();\n    _this.map = map;\n    var projection = opt.projection;\n    var source = geoSourceManager.load(map, opt.nameMap, opt.nameProperty);\n    var resource = geoSourceManager.getGeoResource(map);\n    var resourceType = _this.resourceType = resource ? resource.type : null;\n    var regions = _this.regions = source.regions;\n    var defaultParams = GEO_DEFAULT_PARAMS[resource.type];\n    _this._regionsMap = source.regionsMap;\n    _this.regions = source.regions;\n\n    if (\"development\" !== 'production' && projection) {\n      // Do some check\n      if (resourceType === 'geoSVG') {\n        if (\"development\" !== 'production') {\n          warn(\"Map \" + map + \" with SVG source can't use projection. Only GeoJSON source supports projection.\");\n        }\n\n        projection = null;\n      }\n\n      if (!(projection.project && projection.unproject)) {\n        if (\"development\" !== 'production') {\n          warn('project and unproject must be both provided in the projeciton.');\n        }\n\n        projection = null;\n      }\n    }\n\n    _this.projection = projection;\n    var boundingRect;\n\n    if (projection) {\n      // Can't reuse the raw bounding rect\n      for (var i = 0; i < regions.length; i++) {\n        var regionRect = regions[i].getBoundingRect(projection);\n        boundingRect = boundingRect || regionRect.clone();\n        boundingRect.union(regionRect);\n      }\n    } else {\n      boundingRect = source.boundingRect;\n    }\n\n    _this.setBoundingRect(boundingRect.x, boundingRect.y, boundingRect.width, boundingRect.height); // aspectScale and invertLongitute actually is the parameters default raw projection.\n    // So we ignore them if projection is given.\n    // Ignore default aspect scale if projection exits.\n\n\n    _this.aspectScale = projection ? 1 : retrieve2(opt.aspectScale, defaultParams.aspectScale); // Not invert longitude if projection exits.\n\n    _this._invertLongitute = projection ? false : defaultParams.invertLongitute;\n    return _this;\n  }\n\n  Geo.prototype._transformTo = function (x, y, width, height) {\n    var rect = this.getBoundingRect();\n    var invertLongitute = this._invertLongitute;\n    rect = rect.clone();\n\n    if (invertLongitute) {\n      // Longitude is inverted.\n      rect.y = -rect.y - rect.height;\n    }\n\n    var rawTransformable = this._rawTransformable;\n    rawTransformable.transform = rect.calculateTransform(new BoundingRect(x, y, width, height));\n    var rawParent = rawTransformable.parent;\n    rawTransformable.parent = null;\n    rawTransformable.decomposeTransform();\n    rawTransformable.parent = rawParent;\n\n    if (invertLongitute) {\n      rawTransformable.scaleY = -rawTransformable.scaleY;\n    }\n\n    this._updateTransform();\n  };\n\n  Geo.prototype.getRegion = function (name) {\n    return this._regionsMap.get(name);\n  };\n\n  Geo.prototype.getRegionByCoord = function (coord) {\n    var regions = this.regions;\n\n    for (var i = 0; i < regions.length; i++) {\n      var region = regions[i];\n\n      if (region.type === 'geoJSON' && region.contain(coord)) {\n        return regions[i];\n      }\n    }\n  };\n  /**\n   * Add geoCoord for indexing by name\n   */\n\n\n  Geo.prototype.addGeoCoord = function (name, geoCoord) {\n    this._nameCoordMap.set(name, geoCoord);\n  };\n  /**\n   * Get geoCoord by name\n   */\n\n\n  Geo.prototype.getGeoCoord = function (name) {\n    var region = this._regionsMap.get(name); // Calculate center only on demand.\n\n\n    return this._nameCoordMap.get(name) || region && region.getCenter();\n  };\n\n  Geo.prototype.dataToPoint = function (data, noRoam, out) {\n    if (isString(data)) {\n      // Map area name to geoCoord\n      data = this.getGeoCoord(data);\n    }\n\n    if (data) {\n      var projection = this.projection;\n\n      if (projection) {\n        // projection may return null point.\n        data = projection.project(data);\n      }\n\n      return data && this.projectedToPoint(data, noRoam, out);\n    }\n  };\n\n  Geo.prototype.pointToData = function (point) {\n    var projection = this.projection;\n\n    if (projection) {\n      // projection may return null point.\n      point = projection.unproject(point);\n    }\n\n    return point && this.pointToProjected(point);\n  };\n  /**\n   * Point to projected data. Same with pointToData when projection is used.\n   */\n\n\n  Geo.prototype.pointToProjected = function (point) {\n    return _super.prototype.pointToData.call(this, point);\n  };\n\n  Geo.prototype.projectedToPoint = function (projected, noRoam, out) {\n    return _super.prototype.dataToPoint.call(this, projected, noRoam, out);\n  };\n\n  Geo.prototype.convertToPixel = function (ecModel, finder, value) {\n    var coordSys = getCoordSys$1(finder);\n    return coordSys === this ? coordSys.dataToPoint(value) : null;\n  };\n\n  Geo.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n    var coordSys = getCoordSys$1(finder);\n    return coordSys === this ? coordSys.pointToData(pixel) : null;\n  };\n\n  return Geo;\n}(View);\nmixin(Geo, View);\n\nfunction getCoordSys$1(finder) {\n  var geoModel = finder.geoModel;\n  var seriesModel = finder.seriesModel;\n  return geoModel ? geoModel.coordinateSystem : seriesModel ? seriesModel.coordinateSystem // For map series.\n  || (seriesModel.getReferringComponents('geo', SINGLE_REFERRING).models[0] || {}).coordinateSystem : null;\n}\n\n/**\n * Resize method bound to the geo\n */\n\nfunction resizeGeo(geoModel, api) {\n  var boundingCoords = geoModel.get('boundingCoords');\n\n  if (boundingCoords != null) {\n    var leftTop_1 = boundingCoords[0];\n    var rightBottom_1 = boundingCoords[1];\n\n    if (!(isFinite(leftTop_1[0]) && isFinite(leftTop_1[1]) && isFinite(rightBottom_1[0]) && isFinite(rightBottom_1[1]))) {\n      if (\"development\" !== 'production') {\n        console.error('Invalid boundingCoords');\n      }\n    } else {\n      // Sample around the lng/lat rect and use projection to calculate actual bounding rect.\n      var projection_1 = this.projection;\n\n      if (projection_1) {\n        var xMin = leftTop_1[0];\n        var yMin = leftTop_1[1];\n        var xMax = rightBottom_1[0];\n        var yMax = rightBottom_1[1];\n        leftTop_1 = [Infinity, Infinity];\n        rightBottom_1 = [-Infinity, -Infinity]; // TODO better way?\n\n        var sampleLine = function (x0, y0, x1, y1) {\n          var dx = x1 - x0;\n          var dy = y1 - y0;\n\n          for (var i = 0; i <= 100; i++) {\n            var p = i / 100;\n            var pt = projection_1.project([x0 + dx * p, y0 + dy * p]);\n            min(leftTop_1, leftTop_1, pt);\n            max(rightBottom_1, rightBottom_1, pt);\n          }\n        }; // Top\n\n\n        sampleLine(xMin, yMin, xMax, yMin); // Right\n\n        sampleLine(xMax, yMin, xMax, yMax); // Bottom\n\n        sampleLine(xMax, yMax, xMin, yMax); // Left\n\n        sampleLine(xMin, yMax, xMax, yMin);\n      }\n\n      this.setBoundingRect(leftTop_1[0], leftTop_1[1], rightBottom_1[0] - leftTop_1[0], rightBottom_1[1] - leftTop_1[1]);\n    }\n  }\n\n  var rect = this.getBoundingRect();\n  var centerOption = geoModel.get('layoutCenter');\n  var sizeOption = geoModel.get('layoutSize');\n  var viewWidth = api.getWidth();\n  var viewHeight = api.getHeight();\n  var aspect = rect.width / rect.height * this.aspectScale;\n  var useCenterAndSize = false;\n  var center;\n  var size;\n\n  if (centerOption && sizeOption) {\n    center = [parsePercent$1(centerOption[0], viewWidth), parsePercent$1(centerOption[1], viewHeight)];\n    size = parsePercent$1(sizeOption, Math.min(viewWidth, viewHeight));\n\n    if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) {\n      useCenterAndSize = true;\n    } else {\n      if (\"development\" !== 'production') {\n        console.warn('Given layoutCenter or layoutSize data are invalid. Use left/top/width/height instead.');\n      }\n    }\n  }\n\n  var viewRect;\n\n  if (useCenterAndSize) {\n    viewRect = {};\n\n    if (aspect > 1) {\n      // Width is same with size\n      viewRect.width = size;\n      viewRect.height = size / aspect;\n    } else {\n      viewRect.height = size;\n      viewRect.width = size * aspect;\n    }\n\n    viewRect.y = center[1] - viewRect.height / 2;\n    viewRect.x = center[0] - viewRect.width / 2;\n  } else {\n    // Use left/top/width/height\n    var boxLayoutOption = geoModel.getBoxLayoutParams();\n    boxLayoutOption.aspect = aspect;\n    viewRect = getLayoutRect(boxLayoutOption, {\n      width: viewWidth,\n      height: viewHeight\n    });\n  }\n\n  this.setViewRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);\n  this.setCenter(geoModel.get('center'), api);\n  this.setZoom(geoModel.get('zoom'));\n} // Back compat for ECharts2, where the coord map is set on map series:\n// {type: 'map', geoCoord: {'cityA': [116.46,39.92], 'cityA': [119.12,24.61]}},\n\n\nfunction setGeoCoords(geo, model) {\n  each(model.get('geoCoord'), function (geoCoord, name) {\n    geo.addGeoCoord(name, geoCoord);\n  });\n}\n\nvar GeoCreator =\n/** @class */\nfunction () {\n  function GeoCreator() {\n    // For deciding which dimensions to use when creating list data\n    this.dimensions = geo2DDimensions;\n  }\n\n  GeoCreator.prototype.create = function (ecModel, api) {\n    var geoList = [];\n\n    function getCommonGeoProperties(model) {\n      return {\n        nameProperty: model.get('nameProperty'),\n        aspectScale: model.get('aspectScale'),\n        projection: model.get('projection')\n      };\n    } // FIXME Create each time may be slow\n\n\n    ecModel.eachComponent('geo', function (geoModel, idx) {\n      var mapName = geoModel.get('map');\n      var geo = new Geo(mapName + idx, mapName, extend({\n        nameMap: geoModel.get('nameMap')\n      }, getCommonGeoProperties(geoModel)));\n      geo.zoomLimit = geoModel.get('scaleLimit');\n      geoList.push(geo); // setGeoCoords(geo, geoModel);\n\n      geoModel.coordinateSystem = geo;\n      geo.model = geoModel; // Inject resize method\n\n      geo.resize = resizeGeo;\n      geo.resize(geoModel, api);\n    });\n    ecModel.eachSeries(function (seriesModel) {\n      var coordSys = seriesModel.get('coordinateSystem');\n\n      if (coordSys === 'geo') {\n        var geoIndex = seriesModel.get('geoIndex') || 0;\n        seriesModel.coordinateSystem = geoList[geoIndex];\n      }\n    }); // If has map series\n\n    var mapModelGroupBySeries = {};\n    ecModel.eachSeriesByType('map', function (seriesModel) {\n      if (!seriesModel.getHostGeoModel()) {\n        var mapType = seriesModel.getMapType();\n        mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || [];\n        mapModelGroupBySeries[mapType].push(seriesModel);\n      }\n    });\n    each(mapModelGroupBySeries, function (mapSeries, mapType) {\n      var nameMapList = map(mapSeries, function (singleMapSeries) {\n        return singleMapSeries.get('nameMap');\n      });\n      var geo = new Geo(mapType, mapType, extend({\n        nameMap: mergeAll(nameMapList)\n      }, getCommonGeoProperties(mapSeries[0])));\n      geo.zoomLimit = retrieve.apply(null, map(mapSeries, function (singleMapSeries) {\n        return singleMapSeries.get('scaleLimit');\n      }));\n      geoList.push(geo); // Inject resize method\n\n      geo.resize = resizeGeo;\n      geo.resize(mapSeries[0], api);\n      each(mapSeries, function (singleMapSeries) {\n        singleMapSeries.coordinateSystem = geo;\n        setGeoCoords(geo, singleMapSeries);\n      });\n    });\n    return geoList;\n  };\n  /**\n   * Fill given regions array\n   */\n\n\n  GeoCreator.prototype.getFilledRegions = function (originRegionArr, mapName, nameMap, nameProperty) {\n    // Not use the original\n    var regionsArr = (originRegionArr || []).slice();\n    var dataNameMap = createHashMap();\n\n    for (var i = 0; i < regionsArr.length; i++) {\n      dataNameMap.set(regionsArr[i].name, regionsArr[i]);\n    }\n\n    var source = geoSourceManager.load(mapName, nameMap, nameProperty);\n    each(source.regions, function (region) {\n      var name = region.name;\n      !dataNameMap.get(name) && regionsArr.push({\n        name: name\n      });\n    });\n    return regionsArr;\n  };\n\n  return GeoCreator;\n}();\n\nvar geoCreator = new GeoCreator();\n\nvar GeoModel =\n/** @class */\nfunction (_super) {\n  __extends(GeoModel, _super);\n\n  function GeoModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GeoModel.type;\n    return _this;\n  }\n\n  GeoModel.prototype.init = function (option, parentModel, ecModel) {\n    var source = geoSourceManager.getGeoResource(option.map);\n\n    if (source && source.type === 'geoJSON') {\n      var itemStyle = option.itemStyle = option.itemStyle || {};\n\n      if (!('color' in itemStyle)) {\n        itemStyle.color = '#eee';\n      }\n    }\n\n    this.mergeDefaultAndTheme(option, ecModel); // Default label emphasis `show`\n\n    defaultEmphasis(option, 'label', ['show']);\n  };\n\n  GeoModel.prototype.optionUpdated = function () {\n    var _this = this;\n\n    var option = this.option;\n    option.regions = geoCreator.getFilledRegions(option.regions, option.map, option.nameMap, option.nameProperty);\n    var selectedMap = {};\n    this._optionModelMap = reduce(option.regions || [], function (optionModelMap, regionOpt) {\n      var regionName = regionOpt.name;\n\n      if (regionName) {\n        optionModelMap.set(regionName, new Model(regionOpt, _this, _this.ecModel));\n\n        if (regionOpt.selected) {\n          selectedMap[regionName] = true;\n        }\n      }\n\n      return optionModelMap;\n    }, createHashMap());\n\n    if (!option.selectedMap) {\n      option.selectedMap = selectedMap;\n    }\n  };\n  /**\n   * Get model of region.\n   */\n\n\n  GeoModel.prototype.getRegionModel = function (name) {\n    return this._optionModelMap.get(name) || new Model(null, this, this.ecModel);\n  };\n  /**\n   * Format label\n   * @param name Region name\n   */\n\n\n  GeoModel.prototype.getFormattedLabel = function (name, status) {\n    var regionModel = this.getRegionModel(name);\n    var formatter = status === 'normal' ? regionModel.get(['label', 'formatter']) : regionModel.get(['emphasis', 'label', 'formatter']);\n    var params = {\n      name: name\n    };\n\n    if (isFunction(formatter)) {\n      params.status = status;\n      return formatter(params);\n    } else if (isString(formatter)) {\n      return formatter.replace('{a}', name != null ? name : '');\n    }\n  };\n\n  GeoModel.prototype.setZoom = function (zoom) {\n    this.option.zoom = zoom;\n  };\n\n  GeoModel.prototype.setCenter = function (center) {\n    this.option.center = center;\n  }; // PENGING If selectedMode is null ?\n\n\n  GeoModel.prototype.select = function (name) {\n    var option = this.option;\n    var selectedMode = option.selectedMode;\n\n    if (!selectedMode) {\n      return;\n    }\n\n    if (selectedMode !== 'multiple') {\n      option.selectedMap = null;\n    }\n\n    var selectedMap = option.selectedMap || (option.selectedMap = {});\n    selectedMap[name] = true;\n  };\n\n  GeoModel.prototype.unSelect = function (name) {\n    var selectedMap = this.option.selectedMap;\n\n    if (selectedMap) {\n      selectedMap[name] = false;\n    }\n  };\n\n  GeoModel.prototype.toggleSelected = function (name) {\n    this[this.isSelected(name) ? 'unSelect' : 'select'](name);\n  };\n\n  GeoModel.prototype.isSelected = function (name) {\n    var selectedMap = this.option.selectedMap;\n    return !!(selectedMap && selectedMap[name]);\n  };\n\n  GeoModel.type = 'geo';\n  GeoModel.layoutMode = 'box';\n  GeoModel.defaultOption = {\n    // zlevel: 0,\n    z: 0,\n    show: true,\n    left: 'center',\n    top: 'center',\n    // Default value:\n    // for geoSVG source: 1,\n    // for geoJSON source: 0.75.\n    aspectScale: null,\n    // /// Layout with center and size\n    // If you want to put map in a fixed size box with right aspect ratio\n    // This two properties may be more convenient\n    // layoutCenter: [50%, 50%]\n    // layoutSize: 100\n    silent: false,\n    // Map type\n    map: '',\n    // Define left-top, right-bottom coords to control view\n    // For example, [ [180, 90], [-180, -90] ]\n    boundingCoords: null,\n    // Default on center of map\n    center: null,\n    zoom: 1,\n    scaleLimit: null,\n    // selectedMode: false\n    label: {\n      show: false,\n      color: '#000'\n    },\n    itemStyle: {\n      borderWidth: 0.5,\n      borderColor: '#444' // Default color:\n      // + geoJSON: #eee\n      // + geoSVG: null (use SVG original `fill`)\n      // color: '#eee'\n\n    },\n    emphasis: {\n      label: {\n        show: true,\n        color: 'rgb(100,0,0)'\n      },\n      itemStyle: {\n        color: 'rgba(255,215,0,0.8)'\n      }\n    },\n    select: {\n      label: {\n        show: true,\n        color: 'rgb(100,0,0)'\n      },\n      itemStyle: {\n        color: 'rgba(255,215,0,0.8)'\n      }\n    },\n    regions: [] // tooltip: {\n    //     show: false\n    // }\n\n  };\n  return GeoModel;\n}(ComponentModel);\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction getCenterCoord(view, point) {\n  // Use projected coord as center because it's linear.\n  return view.pointToProjected ? view.pointToProjected(point) : view.pointToData(point);\n}\n\nfunction updateCenterAndZoom(view, payload, zoomLimit, api) {\n  var previousZoom = view.getZoom();\n  var center = view.getCenter();\n  var zoom = payload.zoom;\n  var point = view.projectedToPoint ? view.projectedToPoint(center) : view.dataToPoint(center);\n\n  if (payload.dx != null && payload.dy != null) {\n    point[0] -= payload.dx;\n    point[1] -= payload.dy;\n    view.setCenter(getCenterCoord(view, point), api);\n  }\n\n  if (zoom != null) {\n    if (zoomLimit) {\n      var zoomMin = zoomLimit.min || 0;\n      var zoomMax = zoomLimit.max || Infinity;\n      zoom = Math.max(Math.min(previousZoom * zoom, zoomMax), zoomMin) / previousZoom;\n    } // Zoom on given point(originX, originY)\n\n\n    view.scaleX *= zoom;\n    view.scaleY *= zoom;\n    var fixX = (payload.originX - view.x) * (zoom - 1);\n    var fixY = (payload.originY - view.y) * (zoom - 1);\n    view.x -= fixX;\n    view.y -= fixY;\n    view.updateTransform(); // Get the new center\n\n    view.setCenter(getCenterCoord(view, point), api);\n    view.setZoom(zoom * previousZoom);\n  }\n\n  return {\n    center: view.getCenter(),\n    zoom: view.getZoom()\n  };\n}\n\nvar GeoView =\n/** @class */\nfunction (_super) {\n  __extends(GeoView, _super);\n\n  function GeoView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GeoView.type;\n    _this.focusBlurEnabled = true;\n    return _this;\n  }\n\n  GeoView.prototype.init = function (ecModel, api) {\n    this._api = api;\n  };\n\n  GeoView.prototype.render = function (geoModel, ecModel, api, payload) {\n    this._model = geoModel;\n\n    if (!geoModel.get('show')) {\n      this._mapDraw && this._mapDraw.remove();\n      this._mapDraw = null;\n      return;\n    }\n\n    if (!this._mapDraw) {\n      this._mapDraw = new MapDraw(api);\n    }\n\n    var mapDraw = this._mapDraw;\n    mapDraw.draw(geoModel, ecModel, api, this, payload);\n    mapDraw.group.on('click', this._handleRegionClick, this);\n    mapDraw.group.silent = geoModel.get('silent');\n    this.group.add(mapDraw.group);\n    this.updateSelectStatus(geoModel, ecModel, api);\n  };\n\n  GeoView.prototype._handleRegionClick = function (e) {\n    var eventData;\n    findEventDispatcher(e.target, function (current) {\n      return (eventData = getECData(current).eventData) != null;\n    }, true);\n\n    if (eventData) {\n      this._api.dispatchAction({\n        type: 'geoToggleSelect',\n        geoId: this._model.id,\n        name: eventData.name\n      });\n    }\n  };\n\n  GeoView.prototype.updateSelectStatus = function (model, ecModel, api) {\n    var _this = this;\n\n    this._mapDraw.group.traverse(function (node) {\n      var eventData = getECData(node).eventData;\n\n      if (eventData) {\n        _this._model.isSelected(eventData.name) ? api.enterSelect(node) : api.leaveSelect(node); // No need to traverse children.\n\n        return true;\n      }\n    });\n  };\n\n  GeoView.prototype.findHighDownDispatchers = function (name) {\n    return this._mapDraw && this._mapDraw.findHighDownDispatchers(name, this._model);\n  };\n\n  GeoView.prototype.dispose = function () {\n    this._mapDraw && this._mapDraw.remove();\n  };\n\n  GeoView.type = 'geo';\n  return GeoView;\n}(ComponentView);\n\nfunction registerMap$1(mapName, geoJson, specialAreas) {\n  geoSourceManager.registerMap(mapName, geoJson, specialAreas);\n}\n\nfunction install$9(registers) {\n  registers.registerCoordinateSystem('geo', geoCreator);\n  registers.registerComponentModel(GeoModel);\n  registers.registerComponentView(GeoView);\n  registers.registerImpl('registerMap', registerMap$1);\n  registers.registerImpl('getMap', function (mapName) {\n    return geoSourceManager.getMapForUser(mapName);\n  });\n\n  function makeAction(method, actionInfo) {\n    actionInfo.update = 'geo:updateSelectStatus';\n    registers.registerAction(actionInfo, function (payload, ecModel) {\n      var selected = {};\n      var allSelected = [];\n      ecModel.eachComponent({\n        mainType: 'geo',\n        query: payload\n      }, function (geoModel) {\n        geoModel[method](payload.name);\n        var geo = geoModel.coordinateSystem;\n        each(geo.regions, function (region) {\n          selected[region.name] = geoModel.isSelected(region.name) || false;\n        }); // Notice: there might be duplicated name in different regions.\n\n        var names = [];\n        each(selected, function (v, name) {\n          selected[name] && names.push(name);\n        });\n        allSelected.push({\n          geoIndex: geoModel.componentIndex,\n          // Use singular, the same naming convention as the event `selectchanged`.\n          name: names\n        });\n      });\n      return {\n        selected: selected,\n        allSelected: allSelected,\n        name: payload.name\n      };\n    });\n  }\n\n  makeAction('toggleSelected', {\n    type: 'geoToggleSelect',\n    event: 'geoselectchanged'\n  });\n  makeAction('select', {\n    type: 'geoSelect',\n    event: 'geoselected'\n  });\n  makeAction('unSelect', {\n    type: 'geoUnSelect',\n    event: 'geounselected'\n  });\n  /**\n   * @payload\n   * @property {string} [componentType=series]\n   * @property {number} [dx]\n   * @property {number} [dy]\n   * @property {number} [zoom]\n   * @property {number} [originX]\n   * @property {number} [originY]\n   */\n\n  registers.registerAction({\n    type: 'geoRoam',\n    event: 'geoRoam',\n    update: 'updateTransform'\n  }, function (payload, ecModel, api) {\n    var componentType = payload.componentType || 'series';\n    ecModel.eachComponent({\n      mainType: componentType,\n      query: payload\n    }, function (componentModel) {\n      var geo = componentModel.coordinateSystem;\n\n      if (geo.type !== 'geo') {\n        return;\n      }\n\n      var res = updateCenterAndZoom(geo, payload, componentModel.get('scaleLimit'), api);\n      componentModel.setCenter && componentModel.setCenter(res.center);\n      componentModel.setZoom && componentModel.setZoom(res.zoom); // All map series with same `map` use the same geo coordinate system\n      // So the center and zoom must be in sync. Include the series not selected by legend\n\n      if (componentType === 'series') {\n        each(componentModel.seriesGroup, function (seriesModel) {\n          seriesModel.setCenter(res.center);\n          seriesModel.setZoom(res.zoom);\n        });\n      }\n    });\n  });\n}\n\nfunction install$a(registers) {\n  use(install$9);\n  registers.registerChartView(MapView);\n  registers.registerSeriesModel(MapSeries);\n  registers.registerLayout(mapSymbolLayout);\n  registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, mapDataStatistic);\n  createLegacyDataSelectAction('map', registers.registerAction);\n}\n\n/**\n * Initialize all computational message for following algorithm.\n */\n\nfunction init$2(inRoot) {\n  var root = inRoot;\n  root.hierNode = {\n    defaultAncestor: null,\n    ancestor: root,\n    prelim: 0,\n    modifier: 0,\n    change: 0,\n    shift: 0,\n    i: 0,\n    thread: null\n  };\n  var nodes = [root];\n  var node;\n  var children;\n\n  while (node = nodes.pop()) {\n    // jshint ignore:line\n    children = node.children;\n\n    if (node.isExpand && children.length) {\n      var n = children.length;\n\n      for (var i = n - 1; i >= 0; i--) {\n        var child = children[i];\n        child.hierNode = {\n          defaultAncestor: null,\n          ancestor: child,\n          prelim: 0,\n          modifier: 0,\n          change: 0,\n          shift: 0,\n          i: i,\n          thread: null\n        };\n        nodes.push(child);\n      }\n    }\n  }\n}\n/**\n * The implementation of this function was originally copied from \"d3.js\"\n * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n *\n * Computes a preliminary x coordinate for node. Before that, this function is\n * applied recursively to the children of node, as well as the function\n * apportion(). After spacing out the children by calling executeShifts(), the\n * node is placed to the midpoint of its outermost children.\n */\n\nfunction firstWalk(node, separation) {\n  var children = node.isExpand ? node.children : [];\n  var siblings = node.parentNode.children;\n  var subtreeW = node.hierNode.i ? siblings[node.hierNode.i - 1] : null;\n\n  if (children.length) {\n    executeShifts(node);\n    var midPoint = (children[0].hierNode.prelim + children[children.length - 1].hierNode.prelim) / 2;\n\n    if (subtreeW) {\n      node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW);\n      node.hierNode.modifier = node.hierNode.prelim - midPoint;\n    } else {\n      node.hierNode.prelim = midPoint;\n    }\n  } else if (subtreeW) {\n    node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW);\n  }\n\n  node.parentNode.hierNode.defaultAncestor = apportion(node, subtreeW, node.parentNode.hierNode.defaultAncestor || siblings[0], separation);\n}\n/**\n * The implementation of this function was originally copied from \"d3.js\"\n * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n *\n * Computes all real x-coordinates by summing up the modifiers recursively.\n */\n\nfunction secondWalk(node) {\n  var nodeX = node.hierNode.prelim + node.parentNode.hierNode.modifier;\n  node.setLayout({\n    x: nodeX\n  }, true);\n  node.hierNode.modifier += node.parentNode.hierNode.modifier;\n}\nfunction separation(cb) {\n  return arguments.length ? cb : defaultSeparation;\n}\n/**\n * Transform the common coordinate to radial coordinate.\n */\n\nfunction radialCoordinate(rad, r) {\n  rad -= Math.PI / 2;\n  return {\n    x: r * Math.cos(rad),\n    y: r * Math.sin(rad)\n  };\n}\n/**\n * Get the layout position of the whole view.\n */\n\nfunction getViewRect$1(seriesModel, api) {\n  return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n    width: api.getWidth(),\n    height: api.getHeight()\n  });\n}\n/**\n * All other shifts, applied to the smaller subtrees between w- and w+, are\n * performed by this function.\n *\n * The implementation of this function was originally copied from \"d3.js\"\n * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n */\n\nfunction executeShifts(node) {\n  var children = node.children;\n  var n = children.length;\n  var shift = 0;\n  var change = 0;\n\n  while (--n >= 0) {\n    var child = children[n];\n    child.hierNode.prelim += shift;\n    child.hierNode.modifier += shift;\n    change += child.hierNode.change;\n    shift += child.hierNode.shift + change;\n  }\n}\n/**\n * The implementation of this function was originally copied from \"d3.js\"\n * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n *\n * The core of the algorithm. Here, a new subtree is combined with the\n * previous subtrees. Threads are used to traverse the inside and outside\n * contours of the left and right subtree up to the highest common level.\n * Whenever two nodes of the inside contours conflict, we compute the left\n * one of the greatest uncommon ancestors using the function nextAncestor()\n * and call moveSubtree() to shift the subtree and prepare the shifts of\n * smaller subtrees. Finally, we add a new thread (if necessary).\n */\n\n\nfunction apportion(subtreeV, subtreeW, ancestor, separation) {\n  if (subtreeW) {\n    var nodeOutRight = subtreeV;\n    var nodeInRight = subtreeV;\n    var nodeOutLeft = nodeInRight.parentNode.children[0];\n    var nodeInLeft = subtreeW;\n    var sumOutRight = nodeOutRight.hierNode.modifier;\n    var sumInRight = nodeInRight.hierNode.modifier;\n    var sumOutLeft = nodeOutLeft.hierNode.modifier;\n    var sumInLeft = nodeInLeft.hierNode.modifier;\n\n    while (nodeInLeft = nextRight(nodeInLeft), nodeInRight = nextLeft(nodeInRight), nodeInLeft && nodeInRight) {\n      nodeOutRight = nextRight(nodeOutRight);\n      nodeOutLeft = nextLeft(nodeOutLeft);\n      nodeOutRight.hierNode.ancestor = subtreeV;\n      var shift = nodeInLeft.hierNode.prelim + sumInLeft - nodeInRight.hierNode.prelim - sumInRight + separation(nodeInLeft, nodeInRight);\n\n      if (shift > 0) {\n        moveSubtree(nextAncestor(nodeInLeft, subtreeV, ancestor), subtreeV, shift);\n        sumInRight += shift;\n        sumOutRight += shift;\n      }\n\n      sumInLeft += nodeInLeft.hierNode.modifier;\n      sumInRight += nodeInRight.hierNode.modifier;\n      sumOutRight += nodeOutRight.hierNode.modifier;\n      sumOutLeft += nodeOutLeft.hierNode.modifier;\n    }\n\n    if (nodeInLeft && !nextRight(nodeOutRight)) {\n      nodeOutRight.hierNode.thread = nodeInLeft;\n      nodeOutRight.hierNode.modifier += sumInLeft - sumOutRight;\n    }\n\n    if (nodeInRight && !nextLeft(nodeOutLeft)) {\n      nodeOutLeft.hierNode.thread = nodeInRight;\n      nodeOutLeft.hierNode.modifier += sumInRight - sumOutLeft;\n      ancestor = subtreeV;\n    }\n  }\n\n  return ancestor;\n}\n/**\n * This function is used to traverse the right contour of a subtree.\n * It returns the rightmost child of node or the thread of node. The function\n * returns null if and only if node is on the highest depth of its subtree.\n */\n\n\nfunction nextRight(node) {\n  var children = node.children;\n  return children.length && node.isExpand ? children[children.length - 1] : node.hierNode.thread;\n}\n/**\n * This function is used to traverse the left contour of a subtree (or a subforest).\n * It returns the leftmost child of node or the thread of node. The function\n * returns null if and only if node is on the highest depth of its subtree.\n */\n\n\nfunction nextLeft(node) {\n  var children = node.children;\n  return children.length && node.isExpand ? children[0] : node.hierNode.thread;\n}\n/**\n * If nodeInLeft’s ancestor is a sibling of node, returns nodeInLeft’s ancestor.\n * Otherwise, returns the specified ancestor.\n */\n\n\nfunction nextAncestor(nodeInLeft, node, ancestor) {\n  return nodeInLeft.hierNode.ancestor.parentNode === node.parentNode ? nodeInLeft.hierNode.ancestor : ancestor;\n}\n/**\n * The implementation of this function was originally copied from \"d3.js\"\n * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n *\n * Shifts the current subtree rooted at wr.\n * This is done by increasing prelim(w+) and modifier(w+) by shift.\n */\n\n\nfunction moveSubtree(wl, wr, shift) {\n  var change = shift / (wr.hierNode.i - wl.hierNode.i);\n  wr.hierNode.change -= change;\n  wr.hierNode.shift += shift;\n  wr.hierNode.modifier += shift;\n  wr.hierNode.prelim += shift;\n  wl.hierNode.change += change;\n}\n/**\n * The implementation of this function was originally copied from \"d3.js\"\n * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n */\n\n\nfunction defaultSeparation(node1, node2) {\n  return node1.parentNode === node2.parentNode ? 1 : 2;\n}\n\nvar TreeEdgeShape =\n/** @class */\nfunction () {\n  function TreeEdgeShape() {\n    this.parentPoint = [];\n    this.childPoints = [];\n  }\n\n  return TreeEdgeShape;\n}();\n\nvar TreePath =\n/** @class */\nfunction (_super) {\n  __extends(TreePath, _super);\n\n  function TreePath(opts) {\n    return _super.call(this, opts) || this;\n  }\n\n  TreePath.prototype.getDefaultStyle = function () {\n    return {\n      stroke: '#000',\n      fill: null\n    };\n  };\n\n  TreePath.prototype.getDefaultShape = function () {\n    return new TreeEdgeShape();\n  };\n\n  TreePath.prototype.buildPath = function (ctx, shape) {\n    var childPoints = shape.childPoints;\n    var childLen = childPoints.length;\n    var parentPoint = shape.parentPoint;\n    var firstChildPos = childPoints[0];\n    var lastChildPos = childPoints[childLen - 1];\n\n    if (childLen === 1) {\n      ctx.moveTo(parentPoint[0], parentPoint[1]);\n      ctx.lineTo(firstChildPos[0], firstChildPos[1]);\n      return;\n    }\n\n    var orient = shape.orient;\n    var forkDim = orient === 'TB' || orient === 'BT' ? 0 : 1;\n    var otherDim = 1 - forkDim;\n    var forkPosition = parsePercent$1(shape.forkPosition, 1);\n    var tmpPoint = [];\n    tmpPoint[forkDim] = parentPoint[forkDim];\n    tmpPoint[otherDim] = parentPoint[otherDim] + (lastChildPos[otherDim] - parentPoint[otherDim]) * forkPosition;\n    ctx.moveTo(parentPoint[0], parentPoint[1]);\n    ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n    ctx.moveTo(firstChildPos[0], firstChildPos[1]);\n    tmpPoint[forkDim] = firstChildPos[forkDim];\n    ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n    tmpPoint[forkDim] = lastChildPos[forkDim];\n    ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n    ctx.lineTo(lastChildPos[0], lastChildPos[1]);\n\n    for (var i = 1; i < childLen - 1; i++) {\n      var point = childPoints[i];\n      ctx.moveTo(point[0], point[1]);\n      tmpPoint[forkDim] = point[forkDim];\n      ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n    }\n  };\n\n  return TreePath;\n}(Path);\n\nvar TreeView =\n/** @class */\nfunction (_super) {\n  __extends(TreeView, _super);\n\n  function TreeView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TreeView.type;\n    _this._mainGroup = new Group();\n    return _this;\n  }\n\n  TreeView.prototype.init = function (ecModel, api) {\n    this._controller = new RoamController(api.getZr());\n    this._controllerHost = {\n      target: this.group\n    };\n    this.group.add(this._mainGroup);\n  };\n\n  TreeView.prototype.render = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n    var layoutInfo = seriesModel.layoutInfo;\n    var group = this._mainGroup;\n    var layout = seriesModel.get('layout');\n\n    if (layout === 'radial') {\n      group.x = layoutInfo.x + layoutInfo.width / 2;\n      group.y = layoutInfo.y + layoutInfo.height / 2;\n    } else {\n      group.x = layoutInfo.x;\n      group.y = layoutInfo.y;\n    }\n\n    this._updateViewCoordSys(seriesModel, api);\n\n    this._updateController(seriesModel, ecModel, api);\n\n    var oldData = this._data;\n    data.diff(oldData).add(function (newIdx) {\n      if (symbolNeedsDraw$1(data, newIdx)) {\n        // Create node and edge\n        updateNode(data, newIdx, null, group, seriesModel);\n      }\n    }).update(function (newIdx, oldIdx) {\n      var symbolEl = oldData.getItemGraphicEl(oldIdx);\n\n      if (!symbolNeedsDraw$1(data, newIdx)) {\n        symbolEl && removeNode(oldData, oldIdx, symbolEl, group, seriesModel);\n        return;\n      } // Update node and edge\n\n\n      updateNode(data, newIdx, symbolEl, group, seriesModel);\n    }).remove(function (oldIdx) {\n      var symbolEl = oldData.getItemGraphicEl(oldIdx); // When remove a collapsed node of subtree, since the collapsed\n      // node haven't been initialized with a symbol element,\n      // you can't found it's symbol element through index.\n      // so if we want to remove the symbol element we should insure\n      // that the symbol element is not null.\n\n      if (symbolEl) {\n        removeNode(oldData, oldIdx, symbolEl, group, seriesModel);\n      }\n    }).execute();\n    this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');\n\n    this._updateNodeAndLinkScale(seriesModel);\n\n    if (seriesModel.get('expandAndCollapse') === true) {\n      data.eachItemGraphicEl(function (el, dataIndex) {\n        el.off('click').on('click', function () {\n          api.dispatchAction({\n            type: 'treeExpandAndCollapse',\n            seriesId: seriesModel.id,\n            dataIndex: dataIndex\n          });\n        });\n      });\n    }\n\n    this._data = data;\n  };\n\n  TreeView.prototype._updateViewCoordSys = function (seriesModel, api) {\n    var data = seriesModel.getData();\n    var points = [];\n    data.each(function (idx) {\n      var layout = data.getItemLayout(idx);\n\n      if (layout && !isNaN(layout.x) && !isNaN(layout.y)) {\n        points.push([+layout.x, +layout.y]);\n      }\n    });\n    var min = [];\n    var max = [];\n    fromPoints(points, min, max); // If don't Store min max when collapse the root node after roam,\n    // the root node will disappear.\n\n    var oldMin = this._min;\n    var oldMax = this._max; // If width or height is 0\n\n    if (max[0] - min[0] === 0) {\n      min[0] = oldMin ? oldMin[0] : min[0] - 1;\n      max[0] = oldMax ? oldMax[0] : max[0] + 1;\n    }\n\n    if (max[1] - min[1] === 0) {\n      min[1] = oldMin ? oldMin[1] : min[1] - 1;\n      max[1] = oldMax ? oldMax[1] : max[1] + 1;\n    }\n\n    var viewCoordSys = seriesModel.coordinateSystem = new View();\n    viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');\n    viewCoordSys.setBoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n    viewCoordSys.setCenter(seriesModel.get('center'), api);\n    viewCoordSys.setZoom(seriesModel.get('zoom')); // Here we use viewCoordSys just for computing the 'position' and 'scale' of the group\n\n    this.group.attr({\n      x: viewCoordSys.x,\n      y: viewCoordSys.y,\n      scaleX: viewCoordSys.scaleX,\n      scaleY: viewCoordSys.scaleY\n    });\n    this._min = min;\n    this._max = max;\n  };\n\n  TreeView.prototype._updateController = function (seriesModel, ecModel, api) {\n    var _this = this;\n\n    var controller = this._controller;\n    var controllerHost = this._controllerHost;\n    var group = this.group;\n    controller.setPointerChecker(function (e, x, y) {\n      var rect = group.getBoundingRect();\n      rect.applyTransform(group.transform);\n      return rect.contain(x, y) && !onIrrelevantElement(e, api, seriesModel);\n    });\n    controller.enable(seriesModel.get('roam'));\n    controllerHost.zoomLimit = seriesModel.get('scaleLimit');\n    controllerHost.zoom = seriesModel.coordinateSystem.getZoom();\n    controller.off('pan').off('zoom').on('pan', function (e) {\n      updateViewOnPan(controllerHost, e.dx, e.dy);\n      api.dispatchAction({\n        seriesId: seriesModel.id,\n        type: 'treeRoam',\n        dx: e.dx,\n        dy: e.dy\n      });\n    }).on('zoom', function (e) {\n      updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);\n      api.dispatchAction({\n        seriesId: seriesModel.id,\n        type: 'treeRoam',\n        zoom: e.scale,\n        originX: e.originX,\n        originY: e.originY\n      });\n\n      _this._updateNodeAndLinkScale(seriesModel); // Only update label layout on zoom\n\n\n      api.updateLabelLayout();\n    });\n  };\n\n  TreeView.prototype._updateNodeAndLinkScale = function (seriesModel) {\n    var data = seriesModel.getData();\n\n    var nodeScale = this._getNodeGlobalScale(seriesModel);\n\n    data.eachItemGraphicEl(function (el, idx) {\n      el.setSymbolScale(nodeScale);\n    });\n  };\n\n  TreeView.prototype._getNodeGlobalScale = function (seriesModel) {\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (coordSys.type !== 'view') {\n      return 1;\n    }\n\n    var nodeScaleRatio = this._nodeScaleRatio;\n    var groupZoom = coordSys.scaleX || 1; // Scale node when zoom changes\n\n    var roamZoom = coordSys.getZoom();\n    var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;\n    return nodeScale / groupZoom;\n  };\n\n  TreeView.prototype.dispose = function () {\n    this._controller && this._controller.dispose();\n    this._controllerHost = null;\n  };\n\n  TreeView.prototype.remove = function () {\n    this._mainGroup.removeAll();\n\n    this._data = null;\n  };\n\n  TreeView.type = 'tree';\n  return TreeView;\n}(ChartView);\n\nfunction symbolNeedsDraw$1(data, dataIndex) {\n  var layout = data.getItemLayout(dataIndex);\n  return layout && !isNaN(layout.x) && !isNaN(layout.y);\n}\n\nfunction updateNode(data, dataIndex, symbolEl, group, seriesModel) {\n  var isInit = !symbolEl;\n  var node = data.tree.getNodeByDataIndex(dataIndex);\n  var itemModel = node.getModel();\n  var visualColor = node.getVisual('style').fill;\n  var symbolInnerColor = node.isExpand === false && node.children.length !== 0 ? visualColor : '#fff';\n  var virtualRoot = data.tree.root;\n  var source = node.parentNode === virtualRoot ? node : node.parentNode || node;\n  var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex);\n  var sourceLayout = source.getLayout();\n  var sourceOldLayout = sourceSymbolEl ? {\n    x: sourceSymbolEl.__oldX,\n    y: sourceSymbolEl.__oldY,\n    rawX: sourceSymbolEl.__radialOldRawX,\n    rawY: sourceSymbolEl.__radialOldRawY\n  } : sourceLayout;\n  var targetLayout = node.getLayout();\n\n  if (isInit) {\n    symbolEl = new Symbol(data, dataIndex, null, {\n      symbolInnerColor: symbolInnerColor,\n      useNameLabel: true\n    });\n    symbolEl.x = sourceOldLayout.x;\n    symbolEl.y = sourceOldLayout.y;\n  } else {\n    symbolEl.updateData(data, dataIndex, null, {\n      symbolInnerColor: symbolInnerColor,\n      useNameLabel: true\n    });\n  }\n\n  symbolEl.__radialOldRawX = symbolEl.__radialRawX;\n  symbolEl.__radialOldRawY = symbolEl.__radialRawY;\n  symbolEl.__radialRawX = targetLayout.rawX;\n  symbolEl.__radialRawY = targetLayout.rawY;\n  group.add(symbolEl);\n  data.setItemGraphicEl(dataIndex, symbolEl);\n  symbolEl.__oldX = symbolEl.x;\n  symbolEl.__oldY = symbolEl.y;\n  updateProps(symbolEl, {\n    x: targetLayout.x,\n    y: targetLayout.y\n  }, seriesModel);\n  var symbolPath = symbolEl.getSymbolPath();\n\n  if (seriesModel.get('layout') === 'radial') {\n    var realRoot = virtualRoot.children[0];\n    var rootLayout = realRoot.getLayout();\n    var length_1 = realRoot.children.length;\n    var rad = void 0;\n    var isLeft = void 0;\n\n    if (targetLayout.x === rootLayout.x && node.isExpand === true && realRoot.children.length) {\n      var center = {\n        x: (realRoot.children[0].getLayout().x + realRoot.children[length_1 - 1].getLayout().x) / 2,\n        y: (realRoot.children[0].getLayout().y + realRoot.children[length_1 - 1].getLayout().y) / 2\n      };\n      rad = Math.atan2(center.y - rootLayout.y, center.x - rootLayout.x);\n\n      if (rad < 0) {\n        rad = Math.PI * 2 + rad;\n      }\n\n      isLeft = center.x < rootLayout.x;\n\n      if (isLeft) {\n        rad = rad - Math.PI;\n      }\n    } else {\n      rad = Math.atan2(targetLayout.y - rootLayout.y, targetLayout.x - rootLayout.x);\n\n      if (rad < 0) {\n        rad = Math.PI * 2 + rad;\n      }\n\n      if (node.children.length === 0 || node.children.length !== 0 && node.isExpand === false) {\n        isLeft = targetLayout.x < rootLayout.x;\n\n        if (isLeft) {\n          rad = rad - Math.PI;\n        }\n      } else {\n        isLeft = targetLayout.x > rootLayout.x;\n\n        if (!isLeft) {\n          rad = rad - Math.PI;\n        }\n      }\n    }\n\n    var textPosition = isLeft ? 'left' : 'right';\n    var normalLabelModel = itemModel.getModel('label');\n    var rotate = normalLabelModel.get('rotate');\n    var labelRotateRadian = rotate * (Math.PI / 180);\n    var textContent = symbolPath.getTextContent();\n\n    if (textContent) {\n      symbolPath.setTextConfig({\n        position: normalLabelModel.get('position') || textPosition,\n        rotation: rotate == null ? -rad : labelRotateRadian,\n        origin: 'center'\n      });\n      textContent.setStyle('verticalAlign', 'middle');\n    }\n  } // Handle status\n\n\n  var focus = itemModel.get(['emphasis', 'focus']);\n  var focusDataIndices = focus === 'relative' ? concatArray(node.getAncestorsIndices(), node.getDescendantIndices()) : focus === 'ancestor' ? node.getAncestorsIndices() : focus === 'descendant' ? node.getDescendantIndices() : null;\n\n  if (focusDataIndices) {\n    // Modify the focus to data indices.\n    getECData(symbolEl).focus = focusDataIndices;\n  }\n\n  drawEdge(seriesModel, node, virtualRoot, symbolEl, sourceOldLayout, sourceLayout, targetLayout, group);\n\n  if (symbolEl.__edge) {\n    symbolEl.onHoverStateChange = function (toState) {\n      if (toState !== 'blur') {\n        // NOTE: Ensure the parent elements will been blurred firstly.\n        // According to the return of getAncestorsIndices and getDescendantIndices\n        // TODO: A bit tricky.\n        var parentEl = node.parentNode && data.getItemGraphicEl(node.parentNode.dataIndex);\n\n        if (!(parentEl && parentEl.hoverState === HOVER_STATE_BLUR)) {\n          setStatesFlag(symbolEl.__edge, toState);\n        }\n      }\n    };\n  }\n}\n\nfunction drawEdge(seriesModel, node, virtualRoot, symbolEl, sourceOldLayout, sourceLayout, targetLayout, group) {\n  var itemModel = node.getModel();\n  var edgeShape = seriesModel.get('edgeShape');\n  var layout = seriesModel.get('layout');\n  var orient = seriesModel.getOrient();\n  var curvature = seriesModel.get(['lineStyle', 'curveness']);\n  var edgeForkPosition = seriesModel.get('edgeForkPosition');\n  var lineStyle = itemModel.getModel('lineStyle').getLineStyle();\n  var edge = symbolEl.__edge; // curve edge from node -> parent\n  // polyline edge from node -> children\n\n  if (edgeShape === 'curve') {\n    if (node.parentNode && node.parentNode !== virtualRoot) {\n      if (!edge) {\n        edge = symbolEl.__edge = new BezierCurve({\n          shape: getEdgeShape(layout, orient, curvature, sourceOldLayout, sourceOldLayout)\n        });\n      }\n\n      updateProps(edge, {\n        shape: getEdgeShape(layout, orient, curvature, sourceLayout, targetLayout)\n      }, seriesModel);\n    }\n  } else if (edgeShape === 'polyline') {\n    if (layout === 'orthogonal') {\n      if (node !== virtualRoot && node.children && node.children.length !== 0 && node.isExpand === true) {\n        var children = node.children;\n        var childPoints = [];\n\n        for (var i = 0; i < children.length; i++) {\n          var childLayout = children[i].getLayout();\n          childPoints.push([childLayout.x, childLayout.y]);\n        }\n\n        if (!edge) {\n          edge = symbolEl.__edge = new TreePath({\n            shape: {\n              parentPoint: [targetLayout.x, targetLayout.y],\n              childPoints: [[targetLayout.x, targetLayout.y]],\n              orient: orient,\n              forkPosition: edgeForkPosition\n            }\n          });\n        }\n\n        updateProps(edge, {\n          shape: {\n            parentPoint: [targetLayout.x, targetLayout.y],\n            childPoints: childPoints\n          }\n        }, seriesModel);\n      }\n    } else {\n      if (\"development\" !== 'production') {\n        throw new Error('The polyline edgeShape can only be used in orthogonal layout');\n      }\n    }\n  } // show all edge when edgeShape is 'curve', filter node `isExpand` is false when edgeShape is 'polyline'\n\n\n  if (edge && !(edgeShape === 'polyline' && !node.isExpand)) {\n    edge.useStyle(defaults({\n      strokeNoScale: true,\n      fill: null\n    }, lineStyle));\n    setStatesStylesFromModel(edge, itemModel, 'lineStyle');\n    setDefaultStateProxy(edge);\n    group.add(edge);\n  }\n}\n\nfunction removeNodeEdge(node, data, group, seriesModel, removeAnimationOpt) {\n  var virtualRoot = data.tree.root;\n\n  var _a = getSourceNode(virtualRoot, node),\n      source = _a.source,\n      sourceLayout = _a.sourceLayout;\n\n  var symbolEl = data.getItemGraphicEl(node.dataIndex);\n\n  if (!symbolEl) {\n    return;\n  }\n\n  var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex);\n  var sourceEdge = sourceSymbolEl.__edge; // 1. when expand the sub tree, delete the children node should delete the edge of\n  // the source at the same time. because the polyline edge shape is only owned by the source.\n  // 2.when the node is the only children of the source, delete the node should delete the edge of\n  // the source at the same time. the same reason as above.\n\n  var edge = symbolEl.__edge || (source.isExpand === false || source.children.length === 1 ? sourceEdge : undefined);\n  var edgeShape = seriesModel.get('edgeShape');\n  var layoutOpt = seriesModel.get('layout');\n  var orient = seriesModel.get('orient');\n  var curvature = seriesModel.get(['lineStyle', 'curveness']);\n\n  if (edge) {\n    if (edgeShape === 'curve') {\n      removeElement(edge, {\n        shape: getEdgeShape(layoutOpt, orient, curvature, sourceLayout, sourceLayout),\n        style: {\n          opacity: 0\n        }\n      }, seriesModel, {\n        cb: function () {\n          group.remove(edge);\n        },\n        removeOpt: removeAnimationOpt\n      });\n    } else if (edgeShape === 'polyline' && seriesModel.get('layout') === 'orthogonal') {\n      removeElement(edge, {\n        shape: {\n          parentPoint: [sourceLayout.x, sourceLayout.y],\n          childPoints: [[sourceLayout.x, sourceLayout.y]]\n        },\n        style: {\n          opacity: 0\n        }\n      }, seriesModel, {\n        cb: function () {\n          group.remove(edge);\n        },\n        removeOpt: removeAnimationOpt\n      });\n    }\n  }\n}\n\nfunction getSourceNode(virtualRoot, node) {\n  var source = node.parentNode === virtualRoot ? node : node.parentNode || node;\n  var sourceLayout;\n\n  while (sourceLayout = source.getLayout(), sourceLayout == null) {\n    source = source.parentNode === virtualRoot ? source : source.parentNode || source;\n  }\n\n  return {\n    source: source,\n    sourceLayout: sourceLayout\n  };\n}\n\nfunction removeNode(data, dataIndex, symbolEl, group, seriesModel) {\n  var node = data.tree.getNodeByDataIndex(dataIndex);\n  var virtualRoot = data.tree.root;\n  var sourceLayout = getSourceNode(virtualRoot, node).sourceLayout; // Use same duration and easing with update to have more consistent animation.\n\n  var removeAnimationOpt = {\n    duration: seriesModel.get('animationDurationUpdate'),\n    easing: seriesModel.get('animationEasingUpdate')\n  };\n  removeElement(symbolEl, {\n    x: sourceLayout.x + 1,\n    y: sourceLayout.y + 1\n  }, seriesModel, {\n    cb: function () {\n      group.remove(symbolEl);\n      data.setItemGraphicEl(dataIndex, null);\n    },\n    removeOpt: removeAnimationOpt\n  });\n  symbolEl.fadeOut(null, data.hostModel, {\n    fadeLabel: true,\n    animation: removeAnimationOpt\n  }); // remove edge as parent node\n\n  node.children.forEach(function (childNode) {\n    removeNodeEdge(childNode, data, group, seriesModel, removeAnimationOpt);\n  }); // remove edge as child node\n\n  removeNodeEdge(node, data, group, seriesModel, removeAnimationOpt);\n}\n\nfunction getEdgeShape(layoutOpt, orient, curvature, sourceLayout, targetLayout) {\n  var cpx1;\n  var cpy1;\n  var cpx2;\n  var cpy2;\n  var x1;\n  var x2;\n  var y1;\n  var y2;\n\n  if (layoutOpt === 'radial') {\n    x1 = sourceLayout.rawX;\n    y1 = sourceLayout.rawY;\n    x2 = targetLayout.rawX;\n    y2 = targetLayout.rawY;\n    var radialCoor1 = radialCoordinate(x1, y1);\n    var radialCoor2 = radialCoordinate(x1, y1 + (y2 - y1) * curvature);\n    var radialCoor3 = radialCoordinate(x2, y2 + (y1 - y2) * curvature);\n    var radialCoor4 = radialCoordinate(x2, y2);\n    return {\n      x1: radialCoor1.x || 0,\n      y1: radialCoor1.y || 0,\n      x2: radialCoor4.x || 0,\n      y2: radialCoor4.y || 0,\n      cpx1: radialCoor2.x || 0,\n      cpy1: radialCoor2.y || 0,\n      cpx2: radialCoor3.x || 0,\n      cpy2: radialCoor3.y || 0\n    };\n  } else {\n    x1 = sourceLayout.x;\n    y1 = sourceLayout.y;\n    x2 = targetLayout.x;\n    y2 = targetLayout.y;\n\n    if (orient === 'LR' || orient === 'RL') {\n      cpx1 = x1 + (x2 - x1) * curvature;\n      cpy1 = y1;\n      cpx2 = x2 + (x1 - x2) * curvature;\n      cpy2 = y2;\n    }\n\n    if (orient === 'TB' || orient === 'BT') {\n      cpx1 = x1;\n      cpy1 = y1 + (y2 - y1) * curvature;\n      cpx2 = x2;\n      cpy2 = y2 + (y1 - y2) * curvature;\n    }\n  }\n\n  return {\n    x1: x1,\n    y1: y1,\n    x2: x2,\n    y2: y2,\n    cpx1: cpx1,\n    cpy1: cpy1,\n    cpx2: cpx2,\n    cpy2: cpy2\n  };\n}\n\nvar inner$7 = makeInner();\n\nfunction linkSeriesData(opt) {\n  var mainData = opt.mainData;\n  var datas = opt.datas;\n\n  if (!datas) {\n    datas = {\n      main: mainData\n    };\n    opt.datasAttr = {\n      main: 'data'\n    };\n  }\n\n  opt.datas = opt.mainData = null;\n  linkAll(mainData, datas, opt); // Porxy data original methods.\n\n  each(datas, function (data) {\n    each(mainData.TRANSFERABLE_METHODS, function (methodName) {\n      data.wrapMethod(methodName, curry(transferInjection, opt));\n    });\n  }); // Beyond transfer, additional features should be added to `cloneShallow`.\n\n  mainData.wrapMethod('cloneShallow', curry(cloneShallowInjection, opt)); // Only mainData trigger change, because struct.update may trigger\n  // another changable methods, which may bring about dead lock.\n\n  each(mainData.CHANGABLE_METHODS, function (methodName) {\n    mainData.wrapMethod(methodName, curry(changeInjection, opt));\n  }); // Make sure datas contains mainData.\n\n  assert(datas[mainData.dataType] === mainData);\n}\n\nfunction transferInjection(opt, res) {\n  if (isMainData(this)) {\n    // Transfer datas to new main data.\n    var datas = extend({}, inner$7(this).datas);\n    datas[this.dataType] = res;\n    linkAll(res, datas, opt);\n  } else {\n    // Modify the reference in main data to point newData.\n    linkSingle(res, this.dataType, inner$7(this).mainData, opt);\n  }\n\n  return res;\n}\n\nfunction changeInjection(opt, res) {\n  opt.struct && opt.struct.update();\n  return res;\n}\n\nfunction cloneShallowInjection(opt, res) {\n  // cloneShallow, which brings about some fragilities, may be inappropriate\n  // to be exposed as an API. So for implementation simplicity we can make\n  // the restriction that cloneShallow of not-mainData should not be invoked\n  // outside, but only be invoked here.\n  each(inner$7(res).datas, function (data, dataType) {\n    data !== res && linkSingle(data.cloneShallow(), dataType, res, opt);\n  });\n  return res;\n}\n/**\n * Supplement method to List.\n *\n * @public\n * @param [dataType] If not specified, return mainData.\n */\n\n\nfunction getLinkedData(dataType) {\n  var mainData = inner$7(this).mainData;\n  return dataType == null || mainData == null ? mainData : inner$7(mainData).datas[dataType];\n}\n/**\n * Get list of all linked data\n */\n\n\nfunction getLinkedDataAll() {\n  var mainData = inner$7(this).mainData;\n  return mainData == null ? [{\n    data: mainData\n  }] : map(keys(inner$7(mainData).datas), function (type) {\n    return {\n      type: type,\n      data: inner$7(mainData).datas[type]\n    };\n  });\n}\n\nfunction isMainData(data) {\n  return inner$7(data).mainData === data;\n}\n\nfunction linkAll(mainData, datas, opt) {\n  inner$7(mainData).datas = {};\n  each(datas, function (data, dataType) {\n    linkSingle(data, dataType, mainData, opt);\n  });\n}\n\nfunction linkSingle(data, dataType, mainData, opt) {\n  inner$7(mainData).datas[dataType] = data;\n  inner$7(data).mainData = mainData;\n  data.dataType = dataType;\n\n  if (opt.struct) {\n    data[opt.structAttr] = opt.struct;\n    opt.struct[opt.datasAttr[dataType]] = data;\n  } // Supplement method.\n\n\n  data.getLinkedData = getLinkedData;\n  data.getLinkedDataAll = getLinkedDataAll;\n}\n\nvar TreeNode =\n/** @class */\nfunction () {\n  function TreeNode(name, hostTree) {\n    this.depth = 0;\n    this.height = 0;\n    /**\n     * Reference to list item.\n     * Do not persistent dataIndex outside,\n     * besause it may be changed by list.\n     * If dataIndex -1,\n     * this node is logical deleted (filtered) in list.\n     */\n\n    this.dataIndex = -1;\n    this.children = [];\n    this.viewChildren = [];\n    this.isExpand = false;\n    this.name = name || '';\n    this.hostTree = hostTree;\n  }\n  /**\n   * The node is removed.\n   */\n\n\n  TreeNode.prototype.isRemoved = function () {\n    return this.dataIndex < 0;\n  };\n\n  TreeNode.prototype.eachNode = function (options, cb, context) {\n    if (isFunction(options)) {\n      context = cb;\n      cb = options;\n      options = null;\n    }\n\n    options = options || {};\n\n    if (isString(options)) {\n      options = {\n        order: options\n      };\n    }\n\n    var order = options.order || 'preorder';\n    var children = this[options.attr || 'children'];\n    var suppressVisitSub;\n    order === 'preorder' && (suppressVisitSub = cb.call(context, this));\n\n    for (var i = 0; !suppressVisitSub && i < children.length; i++) {\n      children[i].eachNode(options, cb, context);\n    }\n\n    order === 'postorder' && cb.call(context, this);\n  };\n  /**\n   * Update depth and height of this subtree.\n   */\n\n\n  TreeNode.prototype.updateDepthAndHeight = function (depth) {\n    var height = 0;\n    this.depth = depth;\n\n    for (var i = 0; i < this.children.length; i++) {\n      var child = this.children[i];\n      child.updateDepthAndHeight(depth + 1);\n\n      if (child.height > height) {\n        height = child.height;\n      }\n    }\n\n    this.height = height + 1;\n  };\n\n  TreeNode.prototype.getNodeById = function (id) {\n    if (this.getId() === id) {\n      return this;\n    }\n\n    for (var i = 0, children = this.children, len = children.length; i < len; i++) {\n      var res = children[i].getNodeById(id);\n\n      if (res) {\n        return res;\n      }\n    }\n  };\n\n  TreeNode.prototype.contains = function (node) {\n    if (node === this) {\n      return true;\n    }\n\n    for (var i = 0, children = this.children, len = children.length; i < len; i++) {\n      var res = children[i].contains(node);\n\n      if (res) {\n        return res;\n      }\n    }\n  };\n  /**\n   * @param includeSelf Default false.\n   * @return order: [root, child, grandchild, ...]\n   */\n\n\n  TreeNode.prototype.getAncestors = function (includeSelf) {\n    var ancestors = [];\n    var node = includeSelf ? this : this.parentNode;\n\n    while (node) {\n      ancestors.push(node);\n      node = node.parentNode;\n    }\n\n    ancestors.reverse();\n    return ancestors;\n  };\n\n  TreeNode.prototype.getAncestorsIndices = function () {\n    var indices = [];\n    var currNode = this;\n\n    while (currNode) {\n      indices.push(currNode.dataIndex);\n      currNode = currNode.parentNode;\n    }\n\n    indices.reverse();\n    return indices;\n  };\n\n  TreeNode.prototype.getDescendantIndices = function () {\n    var indices = [];\n    this.eachNode(function (childNode) {\n      indices.push(childNode.dataIndex);\n    });\n    return indices;\n  };\n\n  TreeNode.prototype.getValue = function (dimension) {\n    var data = this.hostTree.data;\n    return data.getStore().get(data.getDimensionIndex(dimension || 'value'), this.dataIndex);\n  };\n\n  TreeNode.prototype.setLayout = function (layout, merge) {\n    this.dataIndex >= 0 && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge);\n  };\n  /**\n   * @return {Object} layout\n   */\n\n\n  TreeNode.prototype.getLayout = function () {\n    return this.hostTree.data.getItemLayout(this.dataIndex);\n  }; // @depcrecated\n  // getModel<T = unknown, S extends keyof T = keyof T>(path: S): Model<T[S]>\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n\n\n  TreeNode.prototype.getModel = function (path) {\n    if (this.dataIndex < 0) {\n      return;\n    }\n\n    var hostTree = this.hostTree;\n    var itemModel = hostTree.data.getItemModel(this.dataIndex);\n    return itemModel.getModel(path);\n  }; // TODO: TYPE More specific model\n\n\n  TreeNode.prototype.getLevelModel = function () {\n    return (this.hostTree.levelModels || [])[this.depth];\n  };\n\n  TreeNode.prototype.setVisual = function (key, value) {\n    this.dataIndex >= 0 && this.hostTree.data.setItemVisual(this.dataIndex, key, value);\n  };\n  /**\n   * Get item visual\n   * FIXME: make return type better\n   */\n\n\n  TreeNode.prototype.getVisual = function (key) {\n    return this.hostTree.data.getItemVisual(this.dataIndex, key);\n  };\n\n  TreeNode.prototype.getRawIndex = function () {\n    return this.hostTree.data.getRawIndex(this.dataIndex);\n  };\n\n  TreeNode.prototype.getId = function () {\n    return this.hostTree.data.getId(this.dataIndex);\n  };\n  /**\n   * index in parent's children\n   */\n\n\n  TreeNode.prototype.getChildIndex = function () {\n    if (this.parentNode) {\n      var children = this.parentNode.children;\n\n      for (var i = 0; i < children.length; ++i) {\n        if (children[i] === this) {\n          return i;\n        }\n      }\n\n      return -1;\n    }\n\n    return -1;\n  };\n  /**\n   * if this is an ancestor of another node\n   *\n   * @param node another node\n   * @return if is ancestor\n   */\n\n\n  TreeNode.prototype.isAncestorOf = function (node) {\n    var parent = node.parentNode;\n\n    while (parent) {\n      if (parent === this) {\n        return true;\n      }\n\n      parent = parent.parentNode;\n    }\n\n    return false;\n  };\n  /**\n   * if this is an descendant of another node\n   *\n   * @param node another node\n   * @return if is descendant\n   */\n\n\n  TreeNode.prototype.isDescendantOf = function (node) {\n    return node !== this && node.isAncestorOf(this);\n  };\n\n  return TreeNode;\n}();\n\nvar Tree =\n/** @class */\nfunction () {\n  function Tree(hostModel) {\n    this.type = 'tree';\n    this._nodes = [];\n    this.hostModel = hostModel;\n  }\n\n  Tree.prototype.eachNode = function (options, cb, context) {\n    this.root.eachNode(options, cb, context);\n  };\n\n  Tree.prototype.getNodeByDataIndex = function (dataIndex) {\n    var rawIndex = this.data.getRawIndex(dataIndex);\n    return this._nodes[rawIndex];\n  };\n\n  Tree.prototype.getNodeById = function (name) {\n    return this.root.getNodeById(name);\n  };\n  /**\n   * Update item available by list,\n   * when list has been performed options like 'filterSelf' or 'map'.\n   */\n\n\n  Tree.prototype.update = function () {\n    var data = this.data;\n    var nodes = this._nodes;\n\n    for (var i = 0, len = nodes.length; i < len; i++) {\n      nodes[i].dataIndex = -1;\n    }\n\n    for (var i = 0, len = data.count(); i < len; i++) {\n      nodes[data.getRawIndex(i)].dataIndex = i;\n    }\n  };\n  /**\n   * Clear all layouts\n   */\n\n\n  Tree.prototype.clearLayouts = function () {\n    this.data.clearItemLayouts();\n  };\n  /**\n   * data node format:\n   * {\n   *     name: ...\n   *     value: ...\n   *     children: [\n   *         {\n   *             name: ...\n   *             value: ...\n   *             children: ...\n   *         },\n   *         ...\n   *     ]\n   * }\n   */\n\n\n  Tree.createTree = function (dataRoot, hostModel, beforeLink) {\n    var tree = new Tree(hostModel);\n    var listData = [];\n    var dimMax = 1;\n    buildHierarchy(dataRoot);\n\n    function buildHierarchy(dataNode, parentNode) {\n      var value = dataNode.value;\n      dimMax = Math.max(dimMax, isArray(value) ? value.length : 1);\n      listData.push(dataNode);\n      var node = new TreeNode(convertOptionIdName(dataNode.name, ''), tree);\n      parentNode ? addChild(node, parentNode) : tree.root = node;\n\n      tree._nodes.push(node);\n\n      var children = dataNode.children;\n\n      if (children) {\n        for (var i = 0; i < children.length; i++) {\n          buildHierarchy(children[i], node);\n        }\n      }\n    }\n\n    tree.root.updateDepthAndHeight(0);\n    var dimensions = prepareSeriesDataSchema(listData, {\n      coordDimensions: ['value'],\n      dimensionsCount: dimMax\n    }).dimensions;\n    var list = new SeriesData(dimensions, hostModel);\n    list.initData(listData);\n    beforeLink && beforeLink(list);\n    linkSeriesData({\n      mainData: list,\n      struct: tree,\n      structAttr: 'tree'\n    });\n    tree.update();\n    return tree;\n  };\n\n  return Tree;\n}();\n/**\n * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote,\n * so this function is not ready and not necessary to be public.\n */\n\n\nfunction addChild(child, node) {\n  var children = node.children;\n\n  if (child.parentNode === node) {\n    return;\n  }\n\n  children.push(child);\n  child.parentNode = node;\n}\n\nfunction retrieveTargetInfo(payload, validPayloadTypes, seriesModel) {\n  if (payload && indexOf(validPayloadTypes, payload.type) >= 0) {\n    var root = seriesModel.getData().tree.root;\n    var targetNode = payload.targetNode;\n\n    if (isString(targetNode)) {\n      targetNode = root.getNodeById(targetNode);\n    }\n\n    if (targetNode && root.contains(targetNode)) {\n      return {\n        node: targetNode\n      };\n    }\n\n    var targetNodeId = payload.targetNodeId;\n\n    if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) {\n      return {\n        node: targetNode\n      };\n    }\n  }\n} // Not includes the given node at the last item.\n\nfunction getPathToRoot(node) {\n  var path = [];\n\n  while (node) {\n    node = node.parentNode;\n    node && path.push(node);\n  }\n\n  return path.reverse();\n}\nfunction aboveViewRoot(viewRoot, node) {\n  var viewPath = getPathToRoot(viewRoot);\n  return indexOf(viewPath, node) >= 0;\n} // From root to the input node (the input node will be included).\n\nfunction wrapTreePathInfo(node, seriesModel) {\n  var treePathInfo = [];\n\n  while (node) {\n    var nodeDataIndex = node.dataIndex;\n    treePathInfo.push({\n      name: node.name,\n      dataIndex: nodeDataIndex,\n      value: seriesModel.getRawValue(nodeDataIndex)\n    });\n    node = node.parentNode;\n  }\n\n  treePathInfo.reverse();\n  return treePathInfo;\n}\n\nvar TreeSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(TreeSeriesModel, _super);\n\n  function TreeSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.hasSymbolVisual = true; // Do it self.\n\n    _this.ignoreStyleOnData = true;\n    return _this;\n  }\n  /**\n   * Init a tree data structure from data in option series\n   */\n\n\n  TreeSeriesModel.prototype.getInitialData = function (option) {\n    // create a virtual root\n    var root = {\n      name: option.name,\n      children: option.data\n    };\n    var leaves = option.leaves || {};\n    var leavesModel = new Model(leaves, this, this.ecModel);\n    var tree = Tree.createTree(root, this, beforeLink);\n\n    function beforeLink(nodeData) {\n      nodeData.wrapMethod('getItemModel', function (model, idx) {\n        var node = tree.getNodeByDataIndex(idx);\n\n        if (!(node && node.children.length && node.isExpand)) {\n          model.parentModel = leavesModel;\n        }\n\n        return model;\n      });\n    }\n\n    var treeDepth = 0;\n    tree.eachNode('preorder', function (node) {\n      if (node.depth > treeDepth) {\n        treeDepth = node.depth;\n      }\n    });\n    var expandAndCollapse = option.expandAndCollapse;\n    var expandTreeDepth = expandAndCollapse && option.initialTreeDepth >= 0 ? option.initialTreeDepth : treeDepth;\n    tree.root.eachNode('preorder', function (node) {\n      var item = node.hostTree.data.getRawDataItem(node.dataIndex); // Add item.collapsed != null, because users can collapse node original in the series.data.\n\n      node.isExpand = item && item.collapsed != null ? !item.collapsed : node.depth <= expandTreeDepth;\n    });\n    return tree.data;\n  };\n  /**\n   * Make the configuration 'orient' backward compatibly, with 'horizontal = LR', 'vertical = TB'.\n   * @returns {string} orient\n   */\n\n\n  TreeSeriesModel.prototype.getOrient = function () {\n    var orient = this.get('orient');\n\n    if (orient === 'horizontal') {\n      orient = 'LR';\n    } else if (orient === 'vertical') {\n      orient = 'TB';\n    }\n\n    return orient;\n  };\n\n  TreeSeriesModel.prototype.setZoom = function (zoom) {\n    this.option.zoom = zoom;\n  };\n\n  TreeSeriesModel.prototype.setCenter = function (center) {\n    this.option.center = center;\n  };\n\n  TreeSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    var tree = this.getData().tree;\n    var realRoot = tree.root.children[0];\n    var node = tree.getNodeByDataIndex(dataIndex);\n    var value = node.getValue();\n    var name = node.name;\n\n    while (node && node !== realRoot) {\n      name = node.parentNode.name + '.' + name;\n      node = node.parentNode;\n    }\n\n    return createTooltipMarkup('nameValue', {\n      name: name,\n      value: value,\n      noValue: isNaN(value) || value == null\n    });\n  }; // Add tree path to tooltip param\n\n\n  TreeSeriesModel.prototype.getDataParams = function (dataIndex) {\n    var params = _super.prototype.getDataParams.apply(this, arguments);\n\n    var node = this.getData().tree.getNodeByDataIndex(dataIndex);\n    params.treeAncestors = wrapTreePathInfo(node, this);\n    params.collapsed = !node.isExpand;\n    return params;\n  };\n\n  TreeSeriesModel.type = 'series.tree'; // can support the position parameters 'left', 'top','right','bottom', 'width',\n  // 'height' in the setOption() with 'merge' mode normal.\n\n  TreeSeriesModel.layoutMode = 'box';\n  TreeSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    coordinateSystem: 'view',\n    // the position of the whole view\n    left: '12%',\n    top: '12%',\n    right: '12%',\n    bottom: '12%',\n    // the layout of the tree, two value can be selected, 'orthogonal' or 'radial'\n    layout: 'orthogonal',\n    // value can be 'polyline'\n    edgeShape: 'curve',\n    edgeForkPosition: '50%',\n    // true | false | 'move' | 'scale', see module:component/helper/RoamController.\n    roam: false,\n    // Symbol size scale ratio in roam\n    nodeScaleRatio: 0.4,\n    // Default on center of graph\n    center: null,\n    zoom: 1,\n    orient: 'LR',\n    symbol: 'emptyCircle',\n    symbolSize: 7,\n    expandAndCollapse: true,\n    initialTreeDepth: 2,\n    lineStyle: {\n      color: '#ccc',\n      width: 1.5,\n      curveness: 0.5\n    },\n    itemStyle: {\n      color: 'lightsteelblue',\n      // borderColor: '#c23531',\n      borderWidth: 1.5\n    },\n    label: {\n      show: true\n    },\n    animationEasing: 'linear',\n    animationDuration: 700,\n    animationDurationUpdate: 500\n  };\n  return TreeSeriesModel;\n}(SeriesModel);\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n/**\n * Traverse the tree from bottom to top and do something\n */\nfunction eachAfter(root, callback, separation) {\n  var nodes = [root];\n  var next = [];\n  var node;\n\n  while (node = nodes.pop()) {\n    // jshint ignore:line\n    next.push(node);\n\n    if (node.isExpand) {\n      var children = node.children;\n\n      if (children.length) {\n        for (var i = 0; i < children.length; i++) {\n          nodes.push(children[i]);\n        }\n      }\n    }\n  }\n\n  while (node = next.pop()) {\n    // jshint ignore:line\n    callback(node, separation);\n  }\n}\n/**\n * Traverse the tree from top to bottom and do something\n */\n\n\nfunction eachBefore(root, callback) {\n  var nodes = [root];\n  var node;\n\n  while (node = nodes.pop()) {\n    // jshint ignore:line\n    callback(node);\n\n    if (node.isExpand) {\n      var children = node.children;\n\n      if (children.length) {\n        for (var i = children.length - 1; i >= 0; i--) {\n          nodes.push(children[i]);\n        }\n      }\n    }\n  }\n}\n\nfunction treeLayout(ecModel, api) {\n  ecModel.eachSeriesByType('tree', function (seriesModel) {\n    commonLayout(seriesModel, api);\n  });\n}\n\nfunction commonLayout(seriesModel, api) {\n  var layoutInfo = getViewRect$1(seriesModel, api);\n  seriesModel.layoutInfo = layoutInfo;\n  var layout = seriesModel.get('layout');\n  var width = 0;\n  var height = 0;\n  var separation$1 = null;\n\n  if (layout === 'radial') {\n    width = 2 * Math.PI;\n    height = Math.min(layoutInfo.height, layoutInfo.width) / 2;\n    separation$1 = separation(function (node1, node2) {\n      return (node1.parentNode === node2.parentNode ? 1 : 2) / node1.depth;\n    });\n  } else {\n    width = layoutInfo.width;\n    height = layoutInfo.height;\n    separation$1 = separation();\n  }\n\n  var virtualRoot = seriesModel.getData().tree.root;\n  var realRoot = virtualRoot.children[0];\n\n  if (realRoot) {\n    init$2(virtualRoot);\n    eachAfter(realRoot, firstWalk, separation$1);\n    virtualRoot.hierNode.modifier = -realRoot.hierNode.prelim;\n    eachBefore(realRoot, secondWalk);\n    var left_1 = realRoot;\n    var right_1 = realRoot;\n    var bottom_1 = realRoot;\n    eachBefore(realRoot, function (node) {\n      var x = node.getLayout().x;\n\n      if (x < left_1.getLayout().x) {\n        left_1 = node;\n      }\n\n      if (x > right_1.getLayout().x) {\n        right_1 = node;\n      }\n\n      if (node.depth > bottom_1.depth) {\n        bottom_1 = node;\n      }\n    });\n    var delta = left_1 === right_1 ? 1 : separation$1(left_1, right_1) / 2;\n    var tx_1 = delta - left_1.getLayout().x;\n    var kx_1 = 0;\n    var ky_1 = 0;\n    var coorX_1 = 0;\n    var coorY_1 = 0;\n\n    if (layout === 'radial') {\n      kx_1 = width / (right_1.getLayout().x + delta + tx_1); // here we use (node.depth - 1), bucause the real root's depth is 1\n\n      ky_1 = height / (bottom_1.depth - 1 || 1);\n      eachBefore(realRoot, function (node) {\n        coorX_1 = (node.getLayout().x + tx_1) * kx_1;\n        coorY_1 = (node.depth - 1) * ky_1;\n        var finalCoor = radialCoordinate(coorX_1, coorY_1);\n        node.setLayout({\n          x: finalCoor.x,\n          y: finalCoor.y,\n          rawX: coorX_1,\n          rawY: coorY_1\n        }, true);\n      });\n    } else {\n      var orient_1 = seriesModel.getOrient();\n\n      if (orient_1 === 'RL' || orient_1 === 'LR') {\n        ky_1 = height / (right_1.getLayout().x + delta + tx_1);\n        kx_1 = width / (bottom_1.depth - 1 || 1);\n        eachBefore(realRoot, function (node) {\n          coorY_1 = (node.getLayout().x + tx_1) * ky_1;\n          coorX_1 = orient_1 === 'LR' ? (node.depth - 1) * kx_1 : width - (node.depth - 1) * kx_1;\n          node.setLayout({\n            x: coorX_1,\n            y: coorY_1\n          }, true);\n        });\n      } else if (orient_1 === 'TB' || orient_1 === 'BT') {\n        kx_1 = width / (right_1.getLayout().x + delta + tx_1);\n        ky_1 = height / (bottom_1.depth - 1 || 1);\n        eachBefore(realRoot, function (node) {\n          coorX_1 = (node.getLayout().x + tx_1) * kx_1;\n          coorY_1 = orient_1 === 'TB' ? (node.depth - 1) * ky_1 : height - (node.depth - 1) * ky_1;\n          node.setLayout({\n            x: coorX_1,\n            y: coorY_1\n          }, true);\n        });\n      }\n    }\n  }\n}\n\nfunction treeVisual(ecModel) {\n  ecModel.eachSeriesByType('tree', function (seriesModel) {\n    var data = seriesModel.getData();\n    var tree = data.tree;\n    tree.eachNode(function (node) {\n      var model = node.getModel(); // TODO Optimize\n\n      var style = model.getModel('itemStyle').getItemStyle();\n      var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style');\n      extend(existsStyle, style);\n    });\n  });\n}\n\nfunction installTreeAction(registers) {\n  registers.registerAction({\n    type: 'treeExpandAndCollapse',\n    event: 'treeExpandAndCollapse',\n    update: 'update'\n  }, function (payload, ecModel) {\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'tree',\n      query: payload\n    }, function (seriesModel) {\n      var dataIndex = payload.dataIndex;\n      var tree = seriesModel.getData().tree;\n      var node = tree.getNodeByDataIndex(dataIndex);\n      node.isExpand = !node.isExpand;\n    });\n  });\n  registers.registerAction({\n    type: 'treeRoam',\n    event: 'treeRoam',\n    // Here we set 'none' instead of 'update', because roam action\n    // just need to update the transform matrix without having to recalculate\n    // the layout. So don't need to go through the whole update process, such\n    // as 'dataPrcocess', 'coordSystemUpdate', 'layout' and so on.\n    update: 'none'\n  }, function (payload, ecModel, api) {\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'tree',\n      query: payload\n    }, function (seriesModel) {\n      var coordSys = seriesModel.coordinateSystem;\n      var res = updateCenterAndZoom(coordSys, payload, undefined, api);\n      seriesModel.setCenter && seriesModel.setCenter(res.center);\n      seriesModel.setZoom && seriesModel.setZoom(res.zoom);\n    });\n  });\n}\n\nfunction install$b(registers) {\n  registers.registerChartView(TreeView);\n  registers.registerSeriesModel(TreeSeriesModel);\n  registers.registerLayout(treeLayout);\n  registers.registerVisual(treeVisual);\n  installTreeAction(registers);\n}\n\nvar actionTypes = ['treemapZoomToNode', 'treemapRender', 'treemapMove'];\nfunction installTreemapAction(registers) {\n  for (var i = 0; i < actionTypes.length; i++) {\n    registers.registerAction({\n      type: actionTypes[i],\n      update: 'updateView'\n    }, noop);\n  }\n\n  registers.registerAction({\n    type: 'treemapRootToNode',\n    update: 'updateView'\n  }, function (payload, ecModel) {\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'treemap',\n      query: payload\n    }, handleRootToNode);\n\n    function handleRootToNode(model, index) {\n      var types = ['treemapZoomToNode', 'treemapRootToNode'];\n      var targetInfo = retrieveTargetInfo(payload, types, model);\n\n      if (targetInfo) {\n        var originViewRoot = model.getViewRoot();\n\n        if (originViewRoot) {\n          payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) ? 'rollUp' : 'drillDown';\n        }\n\n        model.resetViewRoot(targetInfo.node);\n      }\n    }\n  });\n}\n\nfunction enableAriaDecalForTree(seriesModel) {\n  var data = seriesModel.getData();\n  var tree = data.tree;\n  var decalPaletteScope = {};\n  tree.eachNode(function (node) {\n    // Use decal of level 1 node\n    var current = node;\n\n    while (current && current.depth > 1) {\n      current = current.parentNode;\n    }\n\n    var decal = getDecalFromPalette(seriesModel.ecModel, current.name || current.dataIndex + '', decalPaletteScope);\n    node.setVisual('decal', decal);\n  });\n}\n\nvar TreemapSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(TreemapSeriesModel, _super);\n\n  function TreemapSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TreemapSeriesModel.type;\n    _this.preventUsingHoverLayer = true;\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  TreemapSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    // Create a virtual root.\n    var root = {\n      name: option.name,\n      children: option.data\n    };\n    completeTreeValue(root);\n    var levels = option.levels || []; // Used in \"visual priority\" in `treemapVisual.js`.\n    // This way is a little tricky, must satisfy the precondition:\n    //   1. There is no `treeNode.getModel('itemStyle.xxx')` used.\n    //   2. The `Model.prototype.getModel()` will not use any clone-like way.\n\n    var designatedVisualItemStyle = this.designatedVisualItemStyle = {};\n    var designatedVisualModel = new Model({\n      itemStyle: designatedVisualItemStyle\n    }, this, ecModel);\n    levels = option.levels = setDefault(levels, ecModel);\n    var levelModels = map(levels || [], function (levelDefine) {\n      return new Model(levelDefine, designatedVisualModel, ecModel);\n    }, this); // Make sure always a new tree is created when setOption,\n    // in TreemapView, we check whether oldTree === newTree\n    // to choose mappings approach among old shapes and new shapes.\n\n    var tree = Tree.createTree(root, this, beforeLink);\n\n    function beforeLink(nodeData) {\n      nodeData.wrapMethod('getItemModel', function (model, idx) {\n        var node = tree.getNodeByDataIndex(idx);\n        var levelModel = node ? levelModels[node.depth] : null; // If no levelModel, we also need `designatedVisualModel`.\n\n        model.parentModel = levelModel || designatedVisualModel;\n        return model;\n      });\n    }\n\n    return tree.data;\n  };\n\n  TreemapSeriesModel.prototype.optionUpdated = function () {\n    this.resetViewRoot();\n  };\n  /**\n   * @override\n   * @param {number} dataIndex\n   * @param {boolean} [mutipleSeries=false]\n   */\n\n\n  TreemapSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    var data = this.getData();\n    var value = this.getRawValue(dataIndex);\n    var name = data.getName(dataIndex);\n    return createTooltipMarkup('nameValue', {\n      name: name,\n      value: value\n    });\n  };\n  /**\n   * Add tree path to tooltip param\n   *\n   * @override\n   * @param {number} dataIndex\n   * @return {Object}\n   */\n\n\n  TreemapSeriesModel.prototype.getDataParams = function (dataIndex) {\n    var params = _super.prototype.getDataParams.apply(this, arguments);\n\n    var node = this.getData().tree.getNodeByDataIndex(dataIndex);\n    params.treeAncestors = wrapTreePathInfo(node, this); // compatitable the previous code.\n\n    params.treePathInfo = params.treeAncestors;\n    return params;\n  };\n  /**\n   * @public\n   * @param {Object} layoutInfo {\n   *                                x: containerGroup x\n   *                                y: containerGroup y\n   *                                width: containerGroup width\n   *                                height: containerGroup height\n   *                            }\n   */\n\n\n  TreemapSeriesModel.prototype.setLayoutInfo = function (layoutInfo) {\n    /**\n     * @readOnly\n     * @type {Object}\n     */\n    this.layoutInfo = this.layoutInfo || {};\n    extend(this.layoutInfo, layoutInfo);\n  };\n  /**\n   * @param  {string} id\n   * @return {number} index\n   */\n\n\n  TreemapSeriesModel.prototype.mapIdToIndex = function (id) {\n    // A feature is implemented:\n    // index is monotone increasing with the sequence of\n    // input id at the first time.\n    // This feature can make sure that each data item and its\n    // mapped color have the same index between data list and\n    // color list at the beginning, which is useful for user\n    // to adjust data-color mapping.\n\n    /**\n     * @private\n     * @type {Object}\n     */\n    var idIndexMap = this._idIndexMap;\n\n    if (!idIndexMap) {\n      idIndexMap = this._idIndexMap = createHashMap();\n      /**\n       * @private\n       * @type {number}\n       */\n\n      this._idIndexMapCount = 0;\n    }\n\n    var index = idIndexMap.get(id);\n\n    if (index == null) {\n      idIndexMap.set(id, index = this._idIndexMapCount++);\n    }\n\n    return index;\n  };\n\n  TreemapSeriesModel.prototype.getViewRoot = function () {\n    return this._viewRoot;\n  };\n\n  TreemapSeriesModel.prototype.resetViewRoot = function (viewRoot) {\n    viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot;\n    var root = this.getRawData().tree.root;\n\n    if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) {\n      this._viewRoot = root;\n    }\n  };\n\n  TreemapSeriesModel.prototype.enableAriaDecal = function () {\n    enableAriaDecalForTree(this);\n  };\n\n  TreemapSeriesModel.type = 'series.treemap';\n  TreemapSeriesModel.layoutMode = 'box';\n  TreemapSeriesModel.defaultOption = {\n    // Disable progressive rendering\n    progressive: 0,\n    // size: ['80%', '80%'],            // deprecated, compatible with ec2.\n    left: 'center',\n    top: 'middle',\n    width: '80%',\n    height: '80%',\n    sort: true,\n    clipWindow: 'origin',\n    squareRatio: 0.5 * (1 + Math.sqrt(5)),\n    leafDepth: null,\n    drillDownIcon: '▶',\n    // to align specialized icon. ▷▶❒❐▼✚\n    zoomToNodeRatio: 0.32 * 0.32,\n    roam: true,\n    nodeClick: 'zoomToNode',\n    animation: true,\n    animationDurationUpdate: 900,\n    animationEasing: 'quinticInOut',\n    breadcrumb: {\n      show: true,\n      height: 22,\n      left: 'center',\n      top: 'bottom',\n      // right\n      // bottom\n      emptyItemWidth: 25,\n      itemStyle: {\n        color: 'rgba(0,0,0,0.7)',\n        textStyle: {\n          color: '#fff'\n        }\n      },\n      emphasis: {\n        itemStyle: {\n          color: 'rgba(0,0,0,0.9)' // '#5793f3',\n\n        }\n      }\n    },\n    label: {\n      show: true,\n      // Do not use textDistance, for ellipsis rect just the same as treemap node rect.\n      distance: 0,\n      padding: 5,\n      position: 'inside',\n      // formatter: null,\n      color: '#fff',\n      overflow: 'truncate' // align\n      // verticalAlign\n\n    },\n    upperLabel: {\n      show: false,\n      position: [0, '50%'],\n      height: 20,\n      // formatter: null,\n      // color: '#fff',\n      overflow: 'truncate',\n      // align: null,\n      verticalAlign: 'middle'\n    },\n    itemStyle: {\n      color: null,\n      colorAlpha: null,\n      colorSaturation: null,\n      borderWidth: 0,\n      gapWidth: 0,\n      borderColor: '#fff',\n      borderColorSaturation: null // If specified, borderColor will be ineffective, and the\n      // border color is evaluated by color of current node and\n      // borderColorSaturation.\n\n    },\n    emphasis: {\n      upperLabel: {\n        show: true,\n        position: [0, '50%'],\n        overflow: 'truncate',\n        verticalAlign: 'middle'\n      }\n    },\n    visualDimension: 0,\n    visualMin: null,\n    visualMax: null,\n    color: [],\n    // level[n].color (if necessary).\n    // + Specify color list of each level. level[0].color would be global\n    // color list if not specified. (see method `setDefault`).\n    // + But set as a empty array to forbid fetch color from global palette\n    // when using nodeModel.get('color'), otherwise nodes on deep level\n    // will always has color palette set and are not able to inherit color\n    // from parent node.\n    // + TreemapSeries.color can not be set as 'none', otherwise effect\n    // legend color fetching (see seriesColor.js).\n    colorAlpha: null,\n    colorSaturation: null,\n    colorMappingBy: 'index',\n    visibleMin: 10,\n    // be rendered. Only works when sort is 'asc' or 'desc'.\n    childrenVisibleMin: null,\n    // grandchildren will not show.\n    // Why grandchildren? If not grandchildren but children,\n    // some siblings show children and some not,\n    // the appearance may be mess and not consistent,\n    levels: [] // Each item: {\n    //     visibleMin, itemStyle, visualDimension, label\n    // }\n\n  };\n  return TreemapSeriesModel;\n}(SeriesModel);\n/**\n * @param {Object} dataNode\n */\n\n\nfunction completeTreeValue(dataNode) {\n  // Postorder travel tree.\n  // If value of none-leaf node is not set,\n  // calculate it by suming up the value of all children.\n  var sum = 0;\n  each(dataNode.children, function (child) {\n    completeTreeValue(child);\n    var childValue = child.value;\n    isArray(childValue) && (childValue = childValue[0]);\n    sum += childValue;\n  });\n  var thisValue = dataNode.value;\n\n  if (isArray(thisValue)) {\n    thisValue = thisValue[0];\n  }\n\n  if (thisValue == null || isNaN(thisValue)) {\n    thisValue = sum;\n  } // Value should not less than 0.\n\n\n  if (thisValue < 0) {\n    thisValue = 0;\n  }\n\n  isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue;\n}\n/**\n * set default to level configuration\n */\n\n\nfunction setDefault(levels, ecModel) {\n  var globalColorList = normalizeToArray(ecModel.get('color'));\n  var globalDecalList = normalizeToArray(ecModel.get(['aria', 'decal', 'decals']));\n\n  if (!globalColorList) {\n    return;\n  }\n\n  levels = levels || [];\n  var hasColorDefine;\n  var hasDecalDefine;\n  each(levels, function (levelDefine) {\n    var model = new Model(levelDefine);\n    var modelColor = model.get('color');\n    var modelDecal = model.get('decal');\n\n    if (model.get(['itemStyle', 'color']) || modelColor && modelColor !== 'none') {\n      hasColorDefine = true;\n    }\n\n    if (model.get(['itemStyle', 'decal']) || modelDecal && modelDecal !== 'none') {\n      hasDecalDefine = true;\n    }\n  });\n  var level0 = levels[0] || (levels[0] = {});\n\n  if (!hasColorDefine) {\n    level0.color = globalColorList.slice();\n  }\n\n  if (!hasDecalDefine && globalDecalList) {\n    level0.decal = globalDecalList.slice();\n  }\n\n  return levels;\n}\n\nvar TEXT_PADDING = 8;\nvar ITEM_GAP = 8;\nvar ARRAY_LENGTH = 5;\n\nvar Breadcrumb =\n/** @class */\nfunction () {\n  function Breadcrumb(containerGroup) {\n    this.group = new Group();\n    containerGroup.add(this.group);\n  }\n\n  Breadcrumb.prototype.render = function (seriesModel, api, targetNode, onSelect) {\n    var model = seriesModel.getModel('breadcrumb');\n    var thisGroup = this.group;\n    thisGroup.removeAll();\n\n    if (!model.get('show') || !targetNode) {\n      return;\n    }\n\n    var normalStyleModel = model.getModel('itemStyle');\n    var emphasisModel = model.getModel('emphasis');\n    var textStyleModel = normalStyleModel.getModel('textStyle');\n    var emphasisTextStyleModel = emphasisModel.getModel(['itemStyle', 'textStyle']);\n    var layoutParam = {\n      pos: {\n        left: model.get('left'),\n        right: model.get('right'),\n        top: model.get('top'),\n        bottom: model.get('bottom')\n      },\n      box: {\n        width: api.getWidth(),\n        height: api.getHeight()\n      },\n      emptyItemWidth: model.get('emptyItemWidth'),\n      totalWidth: 0,\n      renderList: []\n    };\n\n    this._prepare(targetNode, layoutParam, textStyleModel);\n\n    this._renderContent(seriesModel, layoutParam, normalStyleModel, emphasisModel, textStyleModel, emphasisTextStyleModel, onSelect);\n\n    positionElement(thisGroup, layoutParam.pos, layoutParam.box);\n  };\n  /**\n   * Prepare render list and total width\n   * @private\n   */\n\n\n  Breadcrumb.prototype._prepare = function (targetNode, layoutParam, textStyleModel) {\n    for (var node = targetNode; node; node = node.parentNode) {\n      var text = convertOptionIdName(node.getModel().get('name'), '');\n      var textRect = textStyleModel.getTextRect(text);\n      var itemWidth = Math.max(textRect.width + TEXT_PADDING * 2, layoutParam.emptyItemWidth);\n      layoutParam.totalWidth += itemWidth + ITEM_GAP;\n      layoutParam.renderList.push({\n        node: node,\n        text: text,\n        width: itemWidth\n      });\n    }\n  };\n  /**\n   * @private\n   */\n\n\n  Breadcrumb.prototype._renderContent = function (seriesModel, layoutParam, normalStyleModel, emphasisModel, textStyleModel, emphasisTextStyleModel, onSelect) {\n    // Start rendering.\n    var lastX = 0;\n    var emptyItemWidth = layoutParam.emptyItemWidth;\n    var height = seriesModel.get(['breadcrumb', 'height']);\n    var availableSize = getAvailableSize(layoutParam.pos, layoutParam.box);\n    var totalWidth = layoutParam.totalWidth;\n    var renderList = layoutParam.renderList;\n    var emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n\n    for (var i = renderList.length - 1; i >= 0; i--) {\n      var item = renderList[i];\n      var itemNode = item.node;\n      var itemWidth = item.width;\n      var text = item.text; // Hdie text and shorten width if necessary.\n\n      if (totalWidth > availableSize.width) {\n        totalWidth -= itemWidth - emptyItemWidth;\n        itemWidth = emptyItemWidth;\n        text = null;\n      }\n\n      var el = new Polygon({\n        shape: {\n          points: makeItemPoints(lastX, 0, itemWidth, height, i === renderList.length - 1, i === 0)\n        },\n        style: defaults(normalStyleModel.getItemStyle(), {\n          lineJoin: 'bevel'\n        }),\n        textContent: new ZRText({\n          style: createTextStyle(textStyleModel, {\n            text: text\n          })\n        }),\n        textConfig: {\n          position: 'inside'\n        },\n        z2: Z2_EMPHASIS_LIFT * 1e4,\n        onclick: curry(onSelect, itemNode)\n      });\n      el.disableLabelAnimation = true;\n      el.getTextContent().ensureState('emphasis').style = createTextStyle(emphasisTextStyleModel, {\n        text: text\n      });\n      el.ensureState('emphasis').style = emphasisItemStyle;\n      toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      this.group.add(el);\n      packEventData(el, seriesModel, itemNode);\n      lastX += itemWidth + ITEM_GAP;\n    }\n  };\n\n  Breadcrumb.prototype.remove = function () {\n    this.group.removeAll();\n  };\n\n  return Breadcrumb;\n}();\n\nfunction makeItemPoints(x, y, itemWidth, itemHeight, head, tail) {\n  var points = [[head ? x : x - ARRAY_LENGTH, y], [x + itemWidth, y], [x + itemWidth, y + itemHeight], [head ? x : x - ARRAY_LENGTH, y + itemHeight]];\n  !tail && points.splice(2, 0, [x + itemWidth + ARRAY_LENGTH, y + itemHeight / 2]);\n  !head && points.push([x, y + itemHeight / 2]);\n  return points;\n} // Package custom mouse event.\n\n\nfunction packEventData(el, seriesModel, itemNode) {\n  getECData(el).eventData = {\n    componentType: 'series',\n    componentSubType: 'treemap',\n    componentIndex: seriesModel.componentIndex,\n    seriesIndex: seriesModel.seriesIndex,\n    seriesName: seriesModel.name,\n    seriesType: 'treemap',\n    selfType: 'breadcrumb',\n    nodeData: {\n      dataIndex: itemNode && itemNode.dataIndex,\n      name: itemNode && itemNode.name\n    },\n    treePathInfo: itemNode && wrapTreePathInfo(itemNode, seriesModel)\n  };\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n/**\n * Animate multiple elements with a single done-callback.\n *\n * @example\n *  animation\n *      .createWrap()\n *      .add(el1, {x: 10, y: 10})\n *      .add(el2, {shape: {width: 500}, style: {fill: 'red'}}, 400)\n *      .done(function () { // done })\n *      .start('cubicOut');\n */\nvar AnimationWrap =\n/** @class */\nfunction () {\n  function AnimationWrap() {\n    this._storage = [];\n    this._elExistsMap = {};\n  }\n  /**\n   * Caution: a el can only be added once, otherwise 'done'\n   * might not be called. This method checks this (by el.id),\n   * suppresses adding and returns false when existing el found.\n   *\n   * @return Whether adding succeeded.\n   */\n\n\n  AnimationWrap.prototype.add = function (el, target, duration, delay, easing) {\n    if (this._elExistsMap[el.id]) {\n      return false;\n    }\n\n    this._elExistsMap[el.id] = true;\n\n    this._storage.push({\n      el: el,\n      target: target,\n      duration: duration,\n      delay: delay,\n      easing: easing\n    });\n\n    return true;\n  };\n  /**\n   * Only execute when animation done/aborted.\n   */\n\n\n  AnimationWrap.prototype.finished = function (callback) {\n    this._finishedCallback = callback;\n    return this;\n  };\n  /**\n   * Will stop exist animation firstly.\n   */\n\n\n  AnimationWrap.prototype.start = function () {\n    var _this = this;\n\n    var count = this._storage.length;\n\n    var checkTerminate = function () {\n      count--;\n\n      if (count <= 0) {\n        // Guard.\n        _this._storage.length = 0;\n        _this._elExistsMap = {};\n        _this._finishedCallback && _this._finishedCallback();\n      }\n    };\n\n    for (var i = 0, len = this._storage.length; i < len; i++) {\n      var item = this._storage[i];\n      item.el.animateTo(item.target, {\n        duration: item.duration,\n        delay: item.delay,\n        easing: item.easing,\n        setToFinal: true,\n        done: checkTerminate,\n        aborted: checkTerminate\n      });\n    }\n\n    return this;\n  };\n\n  return AnimationWrap;\n}();\n\nfunction createWrap() {\n  return new AnimationWrap();\n}\n\nvar Group$1 = Group;\nvar Rect$1 = Rect;\nvar DRAG_THRESHOLD = 3;\nvar PATH_LABEL_NOAMAL = 'label';\nvar PATH_UPPERLABEL_NORMAL = 'upperLabel'; // Should larger than emphasis states lift z\n\nvar Z2_BASE = Z2_EMPHASIS_LIFT * 10; // Should bigger than every z2.\n\nvar Z2_BG = Z2_EMPHASIS_LIFT * 2;\nvar Z2_CONTENT = Z2_EMPHASIS_LIFT * 3;\nvar getStateItemStyle = makeStyleMapper([['fill', 'color'], // `borderColor` and `borderWidth` has been occupied,\n// so use `stroke` to indicate the stroke of the rect.\n['stroke', 'strokeColor'], ['lineWidth', 'strokeWidth'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n// So do not transfer decal directly.\n]);\n\nvar getItemStyleNormal = function (model) {\n  // Normal style props should include emphasis style props.\n  var itemStyle = getStateItemStyle(model); // Clear styles set by emphasis.\n\n  itemStyle.stroke = itemStyle.fill = itemStyle.lineWidth = null;\n  return itemStyle;\n};\n\nvar inner$8 = makeInner();\n\nvar TreemapView =\n/** @class */\nfunction (_super) {\n  __extends(TreemapView, _super);\n\n  function TreemapView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TreemapView.type;\n    _this._state = 'ready';\n    _this._storage = createStorage();\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  TreemapView.prototype.render = function (seriesModel, ecModel, api, payload) {\n    var models = ecModel.findComponents({\n      mainType: 'series',\n      subType: 'treemap',\n      query: payload\n    });\n\n    if (indexOf(models, seriesModel) < 0) {\n      return;\n    }\n\n    this.seriesModel = seriesModel;\n    this.api = api;\n    this.ecModel = ecModel;\n    var types = ['treemapZoomToNode', 'treemapRootToNode'];\n    var targetInfo = retrieveTargetInfo(payload, types, seriesModel);\n    var payloadType = payload && payload.type;\n    var layoutInfo = seriesModel.layoutInfo;\n    var isInit = !this._oldTree;\n    var thisStorage = this._storage; // Mark new root when action is treemapRootToNode.\n\n    var reRoot = payloadType === 'treemapRootToNode' && targetInfo && thisStorage ? {\n      rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()],\n      direction: payload.direction\n    } : null;\n\n    var containerGroup = this._giveContainerGroup(layoutInfo);\n\n    var hasAnimation = seriesModel.get('animation');\n\n    var renderResult = this._doRender(containerGroup, seriesModel, reRoot);\n\n    hasAnimation && !isInit && (!payloadType || payloadType === 'treemapZoomToNode' || payloadType === 'treemapRootToNode') ? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot) : renderResult.renderFinally();\n\n    this._resetController(api);\n\n    this._renderBreadcrumb(seriesModel, api, targetInfo);\n  };\n\n  TreemapView.prototype._giveContainerGroup = function (layoutInfo) {\n    var containerGroup = this._containerGroup;\n\n    if (!containerGroup) {\n      // FIXME\n      // 加一层containerGroup是为了clip，但是现在clip功能并没有实现。\n      containerGroup = this._containerGroup = new Group$1();\n\n      this._initEvents(containerGroup);\n\n      this.group.add(containerGroup);\n    }\n\n    containerGroup.x = layoutInfo.x;\n    containerGroup.y = layoutInfo.y;\n    return containerGroup;\n  };\n\n  TreemapView.prototype._doRender = function (containerGroup, seriesModel, reRoot) {\n    var thisTree = seriesModel.getData().tree;\n    var oldTree = this._oldTree; // Clear last shape records.\n\n    var lastsForAnimation = createStorage();\n    var thisStorage = createStorage();\n    var oldStorage = this._storage;\n    var willInvisibleEls = [];\n\n    function doRenderNode(thisNode, oldNode, parentGroup, depth) {\n      return renderNode(seriesModel, thisStorage, oldStorage, reRoot, lastsForAnimation, willInvisibleEls, thisNode, oldNode, parentGroup, depth);\n    } // Notice: When thisTree and oldTree are the same tree (see list.cloneShallow),\n    // the oldTree is actually losted, so we cannot find all of the old graphic\n    // elements from tree. So we use this strategy: make element storage, move\n    // from old storage to new storage, clear old storage.\n\n\n    dualTravel(thisTree.root ? [thisTree.root] : [], oldTree && oldTree.root ? [oldTree.root] : [], containerGroup, thisTree === oldTree || !oldTree, 0); // Process all removing.\n\n    var willDeleteEls = clearStorage(oldStorage);\n    this._oldTree = thisTree;\n    this._storage = thisStorage;\n    return {\n      lastsForAnimation: lastsForAnimation,\n      willDeleteEls: willDeleteEls,\n      renderFinally: renderFinally\n    };\n\n    function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) {\n      // When 'render' is triggered by action,\n      // 'this' and 'old' may be the same tree,\n      // we use rawIndex in that case.\n      if (sameTree) {\n        oldViewChildren = thisViewChildren;\n        each(thisViewChildren, function (child, index) {\n          !child.isRemoved() && processNode(index, index);\n        });\n      } // Diff hierarchically (diff only in each subtree, but not whole).\n      // because, consistency of view is important.\n      else {\n          new DataDiffer(oldViewChildren, thisViewChildren, getKey, getKey).add(processNode).update(processNode).remove(curry(processNode, null)).execute();\n        }\n\n      function getKey(node) {\n        // Identify by name or raw index.\n        return node.getId();\n      }\n\n      function processNode(newIndex, oldIndex) {\n        var thisNode = newIndex != null ? thisViewChildren[newIndex] : null;\n        var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null;\n        var group = doRenderNode(thisNode, oldNode, parentGroup, depth);\n        group && dualTravel(thisNode && thisNode.viewChildren || [], oldNode && oldNode.viewChildren || [], group, sameTree, depth + 1);\n      }\n    }\n\n    function clearStorage(storage) {\n      var willDeleteEls = createStorage();\n      storage && each(storage, function (store, storageName) {\n        var delEls = willDeleteEls[storageName];\n        each(store, function (el) {\n          el && (delEls.push(el), inner$8(el).willDelete = true);\n        });\n      });\n      return willDeleteEls;\n    }\n\n    function renderFinally() {\n      each(willDeleteEls, function (els) {\n        each(els, function (el) {\n          el.parent && el.parent.remove(el);\n        });\n      });\n      each(willInvisibleEls, function (el) {\n        el.invisible = true; // Setting invisible is for optimizing, so no need to set dirty,\n        // just mark as invisible.\n\n        el.dirty();\n      });\n    }\n  };\n\n  TreemapView.prototype._doAnimation = function (containerGroup, renderResult, seriesModel, reRoot) {\n    var durationOption = seriesModel.get('animationDurationUpdate');\n    var easingOption = seriesModel.get('animationEasing'); // TODO: do not support function until necessary.\n\n    var duration = (isFunction(durationOption) ? 0 : durationOption) || 0;\n    var easing = (isFunction(easingOption) ? null : easingOption) || 'cubicOut';\n    var animationWrap = createWrap(); // Make delete animations.\n\n    each(renderResult.willDeleteEls, function (store, storageName) {\n      each(store, function (el, rawIndex) {\n        if (el.invisible) {\n          return;\n        }\n\n        var parent = el.parent; // Always has parent, and parent is nodeGroup.\n\n        var target;\n        var innerStore = inner$8(parent);\n\n        if (reRoot && reRoot.direction === 'drillDown') {\n          target = parent === reRoot.rootNodeGroup // This is the content element of view root.\n          // Only `content` will enter this branch, because\n          // `background` and `nodeGroup` will not be deleted.\n          ? {\n            shape: {\n              x: 0,\n              y: 0,\n              width: innerStore.nodeWidth,\n              height: innerStore.nodeHeight\n            },\n            style: {\n              opacity: 0\n            }\n          } // Others.\n          : {\n            style: {\n              opacity: 0\n            }\n          };\n        } else {\n          var targetX = 0;\n          var targetY = 0;\n\n          if (!innerStore.willDelete) {\n            // Let node animate to right-bottom corner, cooperating with fadeout,\n            // which is appropriate for user understanding.\n            // Divided by 2 for reRoot rolling up effect.\n            targetX = innerStore.nodeWidth / 2;\n            targetY = innerStore.nodeHeight / 2;\n          }\n\n          target = storageName === 'nodeGroup' ? {\n            x: targetX,\n            y: targetY,\n            style: {\n              opacity: 0\n            }\n          } : {\n            shape: {\n              x: targetX,\n              y: targetY,\n              width: 0,\n              height: 0\n            },\n            style: {\n              opacity: 0\n            }\n          };\n        } // TODO: do not support delay until necessary.\n\n\n        target && animationWrap.add(el, target, duration, 0, easing);\n      });\n    }); // Make other animations\n\n    each(this._storage, function (store, storageName) {\n      each(store, function (el, rawIndex) {\n        var last = renderResult.lastsForAnimation[storageName][rawIndex];\n        var target = {};\n\n        if (!last) {\n          return;\n        }\n\n        if (el instanceof Group) {\n          if (last.oldX != null) {\n            target.x = el.x;\n            target.y = el.y;\n            el.x = last.oldX;\n            el.y = last.oldY;\n          }\n        } else {\n          if (last.oldShape) {\n            target.shape = extend({}, el.shape);\n            el.setShape(last.oldShape);\n          }\n\n          if (last.fadein) {\n            el.setStyle('opacity', 0);\n            target.style = {\n              opacity: 1\n            };\n          } // When animation is stopped for succedent animation starting,\n          // el.style.opacity might not be 1\n          else if (el.style.opacity !== 1) {\n              target.style = {\n                opacity: 1\n              };\n            }\n        }\n\n        animationWrap.add(el, target, duration, 0, easing);\n      });\n    }, this);\n    this._state = 'animating';\n    animationWrap.finished(bind(function () {\n      this._state = 'ready';\n      renderResult.renderFinally();\n    }, this)).start();\n  };\n\n  TreemapView.prototype._resetController = function (api) {\n    var controller = this._controller; // Init controller.\n\n    if (!controller) {\n      controller = this._controller = new RoamController(api.getZr());\n      controller.enable(this.seriesModel.get('roam'));\n      controller.on('pan', bind(this._onPan, this));\n      controller.on('zoom', bind(this._onZoom, this));\n    }\n\n    var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight());\n    controller.setPointerChecker(function (e, x, y) {\n      return rect.contain(x, y);\n    });\n  };\n\n  TreemapView.prototype._clearController = function () {\n    var controller = this._controller;\n\n    if (controller) {\n      controller.dispose();\n      controller = null;\n    }\n  };\n\n  TreemapView.prototype._onPan = function (e) {\n    if (this._state !== 'animating' && (Math.abs(e.dx) > DRAG_THRESHOLD || Math.abs(e.dy) > DRAG_THRESHOLD)) {\n      // These param must not be cached.\n      var root = this.seriesModel.getData().tree.root;\n\n      if (!root) {\n        return;\n      }\n\n      var rootLayout = root.getLayout();\n\n      if (!rootLayout) {\n        return;\n      }\n\n      this.api.dispatchAction({\n        type: 'treemapMove',\n        from: this.uid,\n        seriesId: this.seriesModel.id,\n        rootRect: {\n          x: rootLayout.x + e.dx,\n          y: rootLayout.y + e.dy,\n          width: rootLayout.width,\n          height: rootLayout.height\n        }\n      });\n    }\n  };\n\n  TreemapView.prototype._onZoom = function (e) {\n    var mouseX = e.originX;\n    var mouseY = e.originY;\n\n    if (this._state !== 'animating') {\n      // These param must not be cached.\n      var root = this.seriesModel.getData().tree.root;\n\n      if (!root) {\n        return;\n      }\n\n      var rootLayout = root.getLayout();\n\n      if (!rootLayout) {\n        return;\n      }\n\n      var rect = new BoundingRect(rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height);\n      var layoutInfo = this.seriesModel.layoutInfo; // Transform mouse coord from global to containerGroup.\n\n      mouseX -= layoutInfo.x;\n      mouseY -= layoutInfo.y; // Scale root bounding rect.\n\n      var m = create$1();\n      translate(m, m, [-mouseX, -mouseY]);\n      scale$1(m, m, [e.scale, e.scale]);\n      translate(m, m, [mouseX, mouseY]);\n      rect.applyTransform(m);\n      this.api.dispatchAction({\n        type: 'treemapRender',\n        from: this.uid,\n        seriesId: this.seriesModel.id,\n        rootRect: {\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height\n        }\n      });\n    }\n  };\n\n  TreemapView.prototype._initEvents = function (containerGroup) {\n    var _this = this;\n\n    containerGroup.on('click', function (e) {\n      if (_this._state !== 'ready') {\n        return;\n      }\n\n      var nodeClick = _this.seriesModel.get('nodeClick', true);\n\n      if (!nodeClick) {\n        return;\n      }\n\n      var targetInfo = _this.findTarget(e.offsetX, e.offsetY);\n\n      if (!targetInfo) {\n        return;\n      }\n\n      var node = targetInfo.node;\n\n      if (node.getLayout().isLeafRoot) {\n        _this._rootToNode(targetInfo);\n      } else {\n        if (nodeClick === 'zoomToNode') {\n          _this._zoomToNode(targetInfo);\n        } else if (nodeClick === 'link') {\n          var itemModel = node.hostTree.data.getItemModel(node.dataIndex);\n          var link = itemModel.get('link', true);\n          var linkTarget = itemModel.get('target', true) || 'blank';\n          link && windowOpen(link, linkTarget);\n        }\n      }\n    }, this);\n  };\n\n  TreemapView.prototype._renderBreadcrumb = function (seriesModel, api, targetInfo) {\n    var _this = this;\n\n    if (!targetInfo) {\n      targetInfo = seriesModel.get('leafDepth', true) != null ? {\n        node: seriesModel.getViewRoot()\n      } // FIXME\n      // better way?\n      // Find breadcrumb tail on center of containerGroup.\n      : this.findTarget(api.getWidth() / 2, api.getHeight() / 2);\n\n      if (!targetInfo) {\n        targetInfo = {\n          node: seriesModel.getData().tree.root\n        };\n      }\n    }\n\n    (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group))).render(seriesModel, api, targetInfo.node, function (node) {\n      if (_this._state !== 'animating') {\n        aboveViewRoot(seriesModel.getViewRoot(), node) ? _this._rootToNode({\n          node: node\n        }) : _this._zoomToNode({\n          node: node\n        });\n      }\n    });\n  };\n  /**\n   * @override\n   */\n\n\n  TreemapView.prototype.remove = function () {\n    this._clearController();\n\n    this._containerGroup && this._containerGroup.removeAll();\n    this._storage = createStorage();\n    this._state = 'ready';\n    this._breadcrumb && this._breadcrumb.remove();\n  };\n\n  TreemapView.prototype.dispose = function () {\n    this._clearController();\n  };\n\n  TreemapView.prototype._zoomToNode = function (targetInfo) {\n    this.api.dispatchAction({\n      type: 'treemapZoomToNode',\n      from: this.uid,\n      seriesId: this.seriesModel.id,\n      targetNode: targetInfo.node\n    });\n  };\n\n  TreemapView.prototype._rootToNode = function (targetInfo) {\n    this.api.dispatchAction({\n      type: 'treemapRootToNode',\n      from: this.uid,\n      seriesId: this.seriesModel.id,\n      targetNode: targetInfo.node\n    });\n  };\n  /**\n   * @public\n   * @param {number} x Global coord x.\n   * @param {number} y Global coord y.\n   * @return {Object} info If not found, return undefined;\n   * @return {number} info.node Target node.\n   * @return {number} info.offsetX x refer to target node.\n   * @return {number} info.offsetY y refer to target node.\n   */\n\n\n  TreemapView.prototype.findTarget = function (x, y) {\n    var targetInfo;\n    var viewRoot = this.seriesModel.getViewRoot();\n    viewRoot.eachNode({\n      attr: 'viewChildren',\n      order: 'preorder'\n    }, function (node) {\n      var bgEl = this._storage.background[node.getRawIndex()]; // If invisible, there might be no element.\n\n\n      if (bgEl) {\n        var point = bgEl.transformCoordToLocal(x, y);\n        var shape = bgEl.shape; // For performance consideration, don't use 'getBoundingRect'.\n\n        if (shape.x <= point[0] && point[0] <= shape.x + shape.width && shape.y <= point[1] && point[1] <= shape.y + shape.height) {\n          targetInfo = {\n            node: node,\n            offsetX: point[0],\n            offsetY: point[1]\n          };\n        } else {\n          return false; // Suppress visit subtree.\n        }\n      }\n    }, this);\n    return targetInfo;\n  };\n\n  TreemapView.type = 'treemap';\n  return TreemapView;\n}(ChartView);\n/**\n * @inner\n */\n\n\nfunction createStorage() {\n  return {\n    nodeGroup: [],\n    background: [],\n    content: []\n  };\n}\n/**\n * @inner\n * @return Return undefined means do not travel further.\n */\n\n\nfunction renderNode(seriesModel, thisStorage, oldStorage, reRoot, lastsForAnimation, willInvisibleEls, thisNode, oldNode, parentGroup, depth) {\n  // Whether under viewRoot.\n  if (!thisNode) {\n    // Deleting nodes will be performed finally. This method just find\n    // element from old storage, or create new element, set them to new\n    // storage, and set styles.\n    return;\n  } // -------------------------------------------------------------------\n  // Start of closure variables available in \"Procedures in renderNode\".\n\n\n  var thisLayout = thisNode.getLayout();\n  var data = seriesModel.getData();\n  var nodeModel = thisNode.getModel(); // Only for enabling highlight/downplay. Clear firstly.\n  // Because some node will not be rendered.\n\n  data.setItemGraphicEl(thisNode.dataIndex, null);\n\n  if (!thisLayout || !thisLayout.isInView) {\n    return;\n  }\n\n  var thisWidth = thisLayout.width;\n  var thisHeight = thisLayout.height;\n  var borderWidth = thisLayout.borderWidth;\n  var thisInvisible = thisLayout.invisible;\n  var thisRawIndex = thisNode.getRawIndex();\n  var oldRawIndex = oldNode && oldNode.getRawIndex();\n  var thisViewChildren = thisNode.viewChildren;\n  var upperHeight = thisLayout.upperHeight;\n  var isParent = thisViewChildren && thisViewChildren.length;\n  var itemStyleNormalModel = nodeModel.getModel('itemStyle');\n  var itemStyleEmphasisModel = nodeModel.getModel(['emphasis', 'itemStyle']);\n  var itemStyleBlurModel = nodeModel.getModel(['blur', 'itemStyle']);\n  var itemStyleSelectModel = nodeModel.getModel(['select', 'itemStyle']);\n  var borderRadius = itemStyleNormalModel.get('borderRadius') || 0; // End of closure ariables available in \"Procedures in renderNode\".\n  // -----------------------------------------------------------------\n  // Node group\n\n  var group = giveGraphic('nodeGroup', Group$1);\n\n  if (!group) {\n    return;\n  }\n\n  parentGroup.add(group); // x,y are not set when el is above view root.\n\n  group.x = thisLayout.x || 0;\n  group.y = thisLayout.y || 0;\n  group.markRedraw();\n  inner$8(group).nodeWidth = thisWidth;\n  inner$8(group).nodeHeight = thisHeight;\n\n  if (thisLayout.isAboveViewRoot) {\n    return group;\n  } // Background\n\n\n  var bg = giveGraphic('background', Rect$1, depth, Z2_BG);\n  bg && renderBackground(group, bg, isParent && thisLayout.upperLabelHeight);\n  var emphasisModel = nodeModel.getModel('emphasis');\n  var focus = emphasisModel.get('focus');\n  var blurScope = emphasisModel.get('blurScope');\n  var isDisabled = emphasisModel.get('disabled');\n  var focusOrIndices = focus === 'ancestor' ? thisNode.getAncestorsIndices() : focus === 'descendant' ? thisNode.getDescendantIndices() : focus; // No children, render content.\n\n  if (isParent) {\n    // Because of the implementation about \"traverse\" in graphic hover style, we\n    // can not set hover listener on the \"group\" of non-leaf node. Otherwise the\n    // hover event from the descendents will be listenered.\n    if (isHighDownDispatcher(group)) {\n      setAsHighDownDispatcher(group, false);\n    }\n\n    if (bg) {\n      setAsHighDownDispatcher(bg, !isDisabled); // Only for enabling highlight/downplay.\n\n      data.setItemGraphicEl(thisNode.dataIndex, bg);\n      enableHoverFocus(bg, focusOrIndices, blurScope);\n    }\n  } else {\n    var content = giveGraphic('content', Rect$1, depth, Z2_CONTENT);\n    content && renderContent(group, content);\n    bg.disableMorphing = true;\n\n    if (bg && isHighDownDispatcher(bg)) {\n      setAsHighDownDispatcher(bg, false);\n    }\n\n    setAsHighDownDispatcher(group, !isDisabled); // Only for enabling highlight/downplay.\n\n    data.setItemGraphicEl(thisNode.dataIndex, group);\n    enableHoverFocus(group, focusOrIndices, blurScope);\n  }\n\n  return group; // ----------------------------\n  // | Procedures in renderNode |\n  // ----------------------------\n\n  function renderBackground(group, bg, useUpperLabel) {\n    var ecData = getECData(bg); // For tooltip.\n\n    ecData.dataIndex = thisNode.dataIndex;\n    ecData.seriesIndex = seriesModel.seriesIndex;\n    bg.setShape({\n      x: 0,\n      y: 0,\n      width: thisWidth,\n      height: thisHeight,\n      r: borderRadius\n    });\n\n    if (thisInvisible) {\n      // If invisible, do not set visual, otherwise the element will\n      // change immediately before animation. We think it is OK to\n      // remain its origin color when moving out of the view window.\n      processInvisible(bg);\n    } else {\n      bg.invisible = false;\n      var style = thisNode.getVisual('style');\n      var visualBorderColor = style.stroke;\n      var normalStyle = getItemStyleNormal(itemStyleNormalModel);\n      normalStyle.fill = visualBorderColor;\n      var emphasisStyle = getStateItemStyle(itemStyleEmphasisModel);\n      emphasisStyle.fill = itemStyleEmphasisModel.get('borderColor');\n      var blurStyle = getStateItemStyle(itemStyleBlurModel);\n      blurStyle.fill = itemStyleBlurModel.get('borderColor');\n      var selectStyle = getStateItemStyle(itemStyleSelectModel);\n      selectStyle.fill = itemStyleSelectModel.get('borderColor');\n\n      if (useUpperLabel) {\n        var upperLabelWidth = thisWidth - 2 * borderWidth;\n        prepareText( // PENDING: convert ZRColor to ColorString for text.\n        bg, visualBorderColor, style.opacity, {\n          x: borderWidth,\n          y: 0,\n          width: upperLabelWidth,\n          height: upperHeight\n        });\n      } // For old bg.\n      else {\n          bg.removeTextContent();\n        }\n\n      bg.setStyle(normalStyle);\n      bg.ensureState('emphasis').style = emphasisStyle;\n      bg.ensureState('blur').style = blurStyle;\n      bg.ensureState('select').style = selectStyle;\n      setDefaultStateProxy(bg);\n    }\n\n    group.add(bg);\n  }\n\n  function renderContent(group, content) {\n    var ecData = getECData(content); // For tooltip.\n\n    ecData.dataIndex = thisNode.dataIndex;\n    ecData.seriesIndex = seriesModel.seriesIndex;\n    var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0);\n    var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0);\n    content.culling = true;\n    content.setShape({\n      x: borderWidth,\n      y: borderWidth,\n      width: contentWidth,\n      height: contentHeight,\n      r: borderRadius\n    });\n\n    if (thisInvisible) {\n      // If invisible, do not set visual, otherwise the element will\n      // change immediately before animation. We think it is OK to\n      // remain its origin color when moving out of the view window.\n      processInvisible(content);\n    } else {\n      content.invisible = false;\n      var nodeStyle = thisNode.getVisual('style');\n      var visualColor = nodeStyle.fill;\n      var normalStyle = getItemStyleNormal(itemStyleNormalModel);\n      normalStyle.fill = visualColor;\n      normalStyle.decal = nodeStyle.decal;\n      var emphasisStyle = getStateItemStyle(itemStyleEmphasisModel);\n      var blurStyle = getStateItemStyle(itemStyleBlurModel);\n      var selectStyle = getStateItemStyle(itemStyleSelectModel); // PENDING: convert ZRColor to ColorString for text.\n\n      prepareText(content, visualColor, nodeStyle.opacity, null);\n      content.setStyle(normalStyle);\n      content.ensureState('emphasis').style = emphasisStyle;\n      content.ensureState('blur').style = blurStyle;\n      content.ensureState('select').style = selectStyle;\n      setDefaultStateProxy(content);\n    }\n\n    group.add(content);\n  }\n\n  function processInvisible(element) {\n    // Delay invisible setting utill animation finished,\n    // avoid element vanish suddenly before animation.\n    !element.invisible && willInvisibleEls.push(element);\n  }\n\n  function prepareText(rectEl, visualColor, visualOpacity, // Can be null/undefined\n  upperLabelRect) {\n    var normalLabelModel = nodeModel.getModel(upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL);\n    var defaultText = convertOptionIdName(nodeModel.get('name'), null);\n    var isShow = normalLabelModel.getShallow('show');\n    setLabelStyle(rectEl, getLabelStatesModels(nodeModel, upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL), {\n      defaultText: isShow ? defaultText : null,\n      inheritColor: visualColor,\n      defaultOpacity: visualOpacity,\n      labelFetcher: seriesModel,\n      labelDataIndex: thisNode.dataIndex\n    });\n    var textEl = rectEl.getTextContent();\n\n    if (!textEl) {\n      return;\n    }\n\n    var textStyle = textEl.style;\n    var textPadding = normalizeCssArray(textStyle.padding || 0);\n\n    if (upperLabelRect) {\n      rectEl.setTextConfig({\n        layoutRect: upperLabelRect\n      });\n      textEl.disableLabelLayout = true;\n    }\n\n    textEl.beforeUpdate = function () {\n      var width = Math.max((upperLabelRect ? upperLabelRect.width : rectEl.shape.width) - textPadding[1] - textPadding[3], 0);\n      var height = Math.max((upperLabelRect ? upperLabelRect.height : rectEl.shape.height) - textPadding[0] - textPadding[2], 0);\n\n      if (textStyle.width !== width || textStyle.height !== height) {\n        textEl.setStyle({\n          width: width,\n          height: height\n        });\n      }\n    };\n\n    textStyle.truncateMinChar = 2;\n    textStyle.lineOverflow = 'truncate';\n    addDrillDownIcon(textStyle, upperLabelRect, thisLayout);\n    var textEmphasisState = textEl.getState('emphasis');\n    addDrillDownIcon(textEmphasisState ? textEmphasisState.style : null, upperLabelRect, thisLayout);\n  }\n\n  function addDrillDownIcon(style, upperLabelRect, thisLayout) {\n    var text = style ? style.text : null;\n\n    if (!upperLabelRect && thisLayout.isLeafRoot && text != null) {\n      var iconChar = seriesModel.get('drillDownIcon', true);\n      style.text = iconChar ? iconChar + ' ' + text : text;\n    }\n  }\n\n  function giveGraphic(storageName, Ctor, depth, z) {\n    var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex];\n    var lasts = lastsForAnimation[storageName];\n\n    if (element) {\n      // Remove from oldStorage\n      oldStorage[storageName][oldRawIndex] = null;\n      prepareAnimationWhenHasOld(lasts, element);\n    } // If invisible and no old element, do not create new element (for optimizing).\n    else if (!thisInvisible) {\n        element = new Ctor();\n\n        if (element instanceof Displayable) {\n          element.z2 = calculateZ2(depth, z);\n        }\n\n        prepareAnimationWhenNoOld(lasts, element);\n      } // Set to thisStorage\n\n\n    return thisStorage[storageName][thisRawIndex] = element;\n  }\n\n  function prepareAnimationWhenHasOld(lasts, element) {\n    var lastCfg = lasts[thisRawIndex] = {};\n\n    if (element instanceof Group$1) {\n      lastCfg.oldX = element.x;\n      lastCfg.oldY = element.y;\n    } else {\n      lastCfg.oldShape = extend({}, element.shape);\n    }\n  } // If a element is new, we need to find the animation start point carefully,\n  // otherwise it will looks strange when 'zoomToNode'.\n\n\n  function prepareAnimationWhenNoOld(lasts, element) {\n    var lastCfg = lasts[thisRawIndex] = {};\n    var parentNode = thisNode.parentNode;\n    var isGroup = element instanceof Group;\n\n    if (parentNode && (!reRoot || reRoot.direction === 'drillDown')) {\n      var parentOldX = 0;\n      var parentOldY = 0; // New nodes appear from right-bottom corner in 'zoomToNode' animation.\n      // For convenience, get old bounding rect from background.\n\n      var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()];\n\n      if (!reRoot && parentOldBg && parentOldBg.oldShape) {\n        parentOldX = parentOldBg.oldShape.width;\n        parentOldY = parentOldBg.oldShape.height;\n      } // When no parent old shape found, its parent is new too,\n      // so we can just use {x:0, y:0}.\n\n\n      if (isGroup) {\n        lastCfg.oldX = 0;\n        lastCfg.oldY = parentOldY;\n      } else {\n        lastCfg.oldShape = {\n          x: parentOldX,\n          y: parentOldY,\n          width: 0,\n          height: 0\n        };\n      }\n    } // Fade in, user can be aware that these nodes are new.\n\n\n    lastCfg.fadein = !isGroup;\n  }\n} // We cannot set all background with the same z, because the behaviour of\n// drill down and roll up differ background creation sequence from tree\n// hierarchy sequence, which cause lower background elements to overlap\n// upper ones. So we calculate z based on depth.\n// Moreover, we try to shrink down z interval to [0, 1] to avoid that\n// treemap with large z overlaps other components.\n\n\nfunction calculateZ2(depth, z2InLevel) {\n  return depth * Z2_BASE + z2InLevel;\n}\n\nvar each$3 = each;\nvar isObject$3 = isObject;\nvar CATEGORY_DEFAULT_VISUAL_INDEX = -1;\n\nvar VisualMapping =\n/** @class */\nfunction () {\n  function VisualMapping(option) {\n    var mappingMethod = option.mappingMethod;\n    var visualType = option.type;\n    var thisOption = this.option = clone(option);\n    this.type = visualType;\n    this.mappingMethod = mappingMethod;\n    this._normalizeData = normalizers[mappingMethod];\n    var visualHandler = VisualMapping.visualHandlers[visualType];\n    this.applyVisual = visualHandler.applyVisual;\n    this.getColorMapper = visualHandler.getColorMapper;\n    this._normalizedToVisual = visualHandler._normalizedToVisual[mappingMethod];\n\n    if (mappingMethod === 'piecewise') {\n      normalizeVisualRange(thisOption);\n      preprocessForPiecewise(thisOption);\n    } else if (mappingMethod === 'category') {\n      thisOption.categories ? preprocessForSpecifiedCategory(thisOption) // categories is ordinal when thisOption.categories not specified,\n      // which need no more preprocess except normalize visual.\n      : normalizeVisualRange(thisOption, true);\n    } else {\n      // mappingMethod === 'linear' or 'fixed'\n      assert(mappingMethod !== 'linear' || thisOption.dataExtent);\n      normalizeVisualRange(thisOption);\n    }\n  }\n\n  VisualMapping.prototype.mapValueToVisual = function (value) {\n    var normalized = this._normalizeData(value);\n\n    return this._normalizedToVisual(normalized, value);\n  };\n\n  VisualMapping.prototype.getNormalizer = function () {\n    return bind(this._normalizeData, this);\n  };\n  /**\n   * List available visual types.\n   *\n   * @public\n   * @return {Array.<string>}\n   */\n\n\n  VisualMapping.listVisualTypes = function () {\n    return keys(VisualMapping.visualHandlers);\n  }; // /**\n  //  * @public\n  //  */\n  // static addVisualHandler(name, handler) {\n  //     visualHandlers[name] = handler;\n  // }\n\n  /**\n   * @public\n   */\n\n\n  VisualMapping.isValidType = function (visualType) {\n    return VisualMapping.visualHandlers.hasOwnProperty(visualType);\n  };\n  /**\n   * Convenient method.\n   * Visual can be Object or Array or primary type.\n   */\n\n\n  VisualMapping.eachVisual = function (visual, callback, context) {\n    if (isObject(visual)) {\n      each(visual, callback, context);\n    } else {\n      callback.call(context, visual);\n    }\n  };\n\n  VisualMapping.mapVisual = function (visual, callback, context) {\n    var isPrimary;\n    var newVisual = isArray(visual) ? [] : isObject(visual) ? {} : (isPrimary = true, null);\n    VisualMapping.eachVisual(visual, function (v, key) {\n      var newVal = callback.call(context, v, key);\n      isPrimary ? newVisual = newVal : newVisual[key] = newVal;\n    });\n    return newVisual;\n  };\n  /**\n   * Retrieve visual properties from given object.\n   */\n\n\n  VisualMapping.retrieveVisuals = function (obj) {\n    var ret = {};\n    var hasVisual;\n    obj && each$3(VisualMapping.visualHandlers, function (h, visualType) {\n      if (obj.hasOwnProperty(visualType)) {\n        ret[visualType] = obj[visualType];\n        hasVisual = true;\n      }\n    });\n    return hasVisual ? ret : null;\n  };\n  /**\n   * Give order to visual types, considering colorSaturation, colorAlpha depends on color.\n   *\n   * @public\n   * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...}\n   *                                     IF Array, like: ['color', 'symbol', 'colorSaturation']\n   * @return {Array.<string>} Sorted visual types.\n   */\n\n\n  VisualMapping.prepareVisualTypes = function (visualTypes) {\n    if (isArray(visualTypes)) {\n      visualTypes = visualTypes.slice();\n    } else if (isObject$3(visualTypes)) {\n      var types_1 = [];\n      each$3(visualTypes, function (item, type) {\n        types_1.push(type);\n      });\n      visualTypes = types_1;\n    } else {\n      return [];\n    }\n\n    visualTypes.sort(function (type1, type2) {\n      // color should be front of colorSaturation, colorAlpha, ...\n      // symbol and symbolSize do not matter.\n      return type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0 ? 1 : -1;\n    });\n    return visualTypes;\n  };\n  /**\n   * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'.\n   * Other visuals are only depends on themself.\n   */\n\n\n  VisualMapping.dependsOn = function (visualType1, visualType2) {\n    return visualType2 === 'color' ? !!(visualType1 && visualType1.indexOf(visualType2) === 0) : visualType1 === visualType2;\n  };\n  /**\n   * @param value\n   * @param pieceList [{value: ..., interval: [min, max]}, ...]\n   *                         Always from small to big.\n   * @param findClosestWhenOutside Default to be false\n   * @return index\n   */\n\n\n  VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) {\n    var possibleI;\n    var abs = Infinity; // value has the higher priority.\n\n    for (var i = 0, len = pieceList.length; i < len; i++) {\n      var pieceValue = pieceList[i].value;\n\n      if (pieceValue != null) {\n        if (pieceValue === value // FIXME\n        // It is supposed to compare value according to value type of dimension,\n        // but currently value type can exactly be string or number.\n        // Compromise for numeric-like string (like '12'), especially\n        // in the case that visualMap.categories is ['22', '33'].\n        || isString(pieceValue) && pieceValue === value + '') {\n          return i;\n        }\n\n        findClosestWhenOutside && updatePossible(pieceValue, i);\n      }\n    }\n\n    for (var i = 0, len = pieceList.length; i < len; i++) {\n      var piece = pieceList[i];\n      var interval = piece.interval;\n      var close_1 = piece.close;\n\n      if (interval) {\n        if (interval[0] === -Infinity) {\n          if (littleThan(close_1[1], value, interval[1])) {\n            return i;\n          }\n        } else if (interval[1] === Infinity) {\n          if (littleThan(close_1[0], interval[0], value)) {\n            return i;\n          }\n        } else if (littleThan(close_1[0], interval[0], value) && littleThan(close_1[1], value, interval[1])) {\n          return i;\n        }\n\n        findClosestWhenOutside && updatePossible(interval[0], i);\n        findClosestWhenOutside && updatePossible(interval[1], i);\n      }\n    }\n\n    if (findClosestWhenOutside) {\n      return value === Infinity ? pieceList.length - 1 : value === -Infinity ? 0 : possibleI;\n    }\n\n    function updatePossible(val, index) {\n      var newAbs = Math.abs(val - value);\n\n      if (newAbs < abs) {\n        abs = newAbs;\n        possibleI = index;\n      }\n    }\n  };\n\n  VisualMapping.visualHandlers = {\n    color: {\n      applyVisual: makeApplyVisual('color'),\n      getColorMapper: function () {\n        var thisOption = this.option;\n        return bind(thisOption.mappingMethod === 'category' ? function (value, isNormalized) {\n          !isNormalized && (value = this._normalizeData(value));\n          return doMapCategory.call(this, value);\n        } : function (value, isNormalized, out) {\n          // If output rgb array\n          // which will be much faster and useful in pixel manipulation\n          var returnRGBArray = !!out;\n          !isNormalized && (value = this._normalizeData(value));\n          out = fastLerp(value, thisOption.parsedVisual, out);\n          return returnRGBArray ? out : stringify(out, 'rgba');\n        }, this);\n      },\n      _normalizedToVisual: {\n        linear: function (normalized) {\n          return stringify(fastLerp(normalized, this.option.parsedVisual), 'rgba');\n        },\n        category: doMapCategory,\n        piecewise: function (normalized, value) {\n          var result = getSpecifiedVisual.call(this, value);\n\n          if (result == null) {\n            result = stringify(fastLerp(normalized, this.option.parsedVisual), 'rgba');\n          }\n\n          return result;\n        },\n        fixed: doMapFixed\n      }\n    },\n    colorHue: makePartialColorVisualHandler(function (color$1, value) {\n      return modifyHSL(color$1, value);\n    }),\n    colorSaturation: makePartialColorVisualHandler(function (color$1, value) {\n      return modifyHSL(color$1, null, value);\n    }),\n    colorLightness: makePartialColorVisualHandler(function (color$1, value) {\n      return modifyHSL(color$1, null, null, value);\n    }),\n    colorAlpha: makePartialColorVisualHandler(function (color$1, value) {\n      return modifyAlpha(color$1, value);\n    }),\n    decal: {\n      applyVisual: makeApplyVisual('decal'),\n      _normalizedToVisual: {\n        linear: null,\n        category: doMapCategory,\n        piecewise: null,\n        fixed: null\n      }\n    },\n    opacity: {\n      applyVisual: makeApplyVisual('opacity'),\n      _normalizedToVisual: createNormalizedToNumericVisual([0, 1])\n    },\n    liftZ: {\n      applyVisual: makeApplyVisual('liftZ'),\n      _normalizedToVisual: {\n        linear: doMapFixed,\n        category: doMapFixed,\n        piecewise: doMapFixed,\n        fixed: doMapFixed\n      }\n    },\n    symbol: {\n      applyVisual: function (value, getter, setter) {\n        var symbolCfg = this.mapValueToVisual(value);\n        setter('symbol', symbolCfg);\n      },\n      _normalizedToVisual: {\n        linear: doMapToArray,\n        category: doMapCategory,\n        piecewise: function (normalized, value) {\n          var result = getSpecifiedVisual.call(this, value);\n\n          if (result == null) {\n            result = doMapToArray.call(this, normalized);\n          }\n\n          return result;\n        },\n        fixed: doMapFixed\n      }\n    },\n    symbolSize: {\n      applyVisual: makeApplyVisual('symbolSize'),\n      _normalizedToVisual: createNormalizedToNumericVisual([0, 1])\n    }\n  };\n  return VisualMapping;\n}();\n\nfunction preprocessForPiecewise(thisOption) {\n  var pieceList = thisOption.pieceList;\n  thisOption.hasSpecialVisual = false;\n  each(pieceList, function (piece, index) {\n    piece.originIndex = index; // piece.visual is \"result visual value\" but not\n    // a visual range, so it does not need to be normalized.\n\n    if (piece.visual != null) {\n      thisOption.hasSpecialVisual = true;\n    }\n  });\n}\n\nfunction preprocessForSpecifiedCategory(thisOption) {\n  // Hash categories.\n  var categories = thisOption.categories;\n  var categoryMap = thisOption.categoryMap = {};\n  var visual = thisOption.visual;\n  each$3(categories, function (cate, index) {\n    categoryMap[cate] = index;\n  }); // Process visual map input.\n\n  if (!isArray(visual)) {\n    var visualArr_1 = [];\n\n    if (isObject(visual)) {\n      each$3(visual, function (v, cate) {\n        var index = categoryMap[cate];\n        visualArr_1[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v;\n      });\n    } else {\n      // Is primary type, represents default visual.\n      visualArr_1[CATEGORY_DEFAULT_VISUAL_INDEX] = visual;\n    }\n\n    visual = setVisualToOption(thisOption, visualArr_1);\n  } // Remove categories that has no visual,\n  // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX.\n\n\n  for (var i = categories.length - 1; i >= 0; i--) {\n    if (visual[i] == null) {\n      delete categoryMap[categories[i]];\n      categories.pop();\n    }\n  }\n}\n\nfunction normalizeVisualRange(thisOption, isCategory) {\n  var visual = thisOption.visual;\n  var visualArr = [];\n\n  if (isObject(visual)) {\n    each$3(visual, function (v) {\n      visualArr.push(v);\n    });\n  } else if (visual != null) {\n    visualArr.push(visual);\n  }\n\n  var doNotNeedPair = {\n    color: 1,\n    symbol: 1\n  };\n\n  if (!isCategory && visualArr.length === 1 && !doNotNeedPair.hasOwnProperty(thisOption.type)) {\n    // Do not care visualArr.length === 0, which is illegal.\n    visualArr[1] = visualArr[0];\n  }\n\n  setVisualToOption(thisOption, visualArr);\n}\n\nfunction makePartialColorVisualHandler(applyValue) {\n  return {\n    applyVisual: function (value, getter, setter) {\n      // Only used in HSL\n      var colorChannel = this.mapValueToVisual(value); // Must not be array value\n\n      setter('color', applyValue(getter('color'), colorChannel));\n    },\n    _normalizedToVisual: createNormalizedToNumericVisual([0, 1])\n  };\n}\n\nfunction doMapToArray(normalized) {\n  var visual = this.option.visual;\n  return visual[Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true))] || {}; // TODO {}?\n}\n\nfunction makeApplyVisual(visualType) {\n  return function (value, getter, setter) {\n    setter(visualType, this.mapValueToVisual(value));\n  };\n}\n\nfunction doMapCategory(normalized) {\n  var visual = this.option.visual;\n  return visual[this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX ? normalized % visual.length : normalized];\n}\n\nfunction doMapFixed() {\n  // visual will be convert to array.\n  return this.option.visual[0];\n}\n/**\n * Create mapped to numeric visual\n */\n\n\nfunction createNormalizedToNumericVisual(sourceExtent) {\n  return {\n    linear: function (normalized) {\n      return linearMap(normalized, sourceExtent, this.option.visual, true);\n    },\n    category: doMapCategory,\n    piecewise: function (normalized, value) {\n      var result = getSpecifiedVisual.call(this, value);\n\n      if (result == null) {\n        result = linearMap(normalized, sourceExtent, this.option.visual, true);\n      }\n\n      return result;\n    },\n    fixed: doMapFixed\n  };\n}\n\nfunction getSpecifiedVisual(value) {\n  var thisOption = this.option;\n  var pieceList = thisOption.pieceList;\n\n  if (thisOption.hasSpecialVisual) {\n    var pieceIndex = VisualMapping.findPieceIndex(value, pieceList);\n    var piece = pieceList[pieceIndex];\n\n    if (piece && piece.visual) {\n      return piece.visual[this.type];\n    }\n  }\n}\n\nfunction setVisualToOption(thisOption, visualArr) {\n  thisOption.visual = visualArr;\n\n  if (thisOption.type === 'color') {\n    thisOption.parsedVisual = map(visualArr, function (item) {\n      var color$1 = parse(item);\n\n      if (!color$1 && \"development\" !== 'production') {\n        warn(\"'\" + item + \"' is an illegal color, fallback to '#000000'\", true);\n      }\n\n      return color$1 || [0, 0, 0, 1];\n    });\n  }\n\n  return visualArr;\n}\n/**\n * Normalizers by mapping methods.\n */\n\n\nvar normalizers = {\n  linear: function (value) {\n    return linearMap(value, this.option.dataExtent, [0, 1], true);\n  },\n  piecewise: function (value) {\n    var pieceList = this.option.pieceList;\n    var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true);\n\n    if (pieceIndex != null) {\n      return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true);\n    }\n  },\n  category: function (value) {\n    var index = this.option.categories ? this.option.categoryMap[value] : value; // ordinal value\n\n    return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index;\n  },\n  fixed: noop\n};\n\nfunction littleThan(close, a, b) {\n  return close ? a <= b : a < b;\n}\n\nvar ITEM_STYLE_NORMAL = 'itemStyle';\nvar inner$9 = makeInner();\nvar treemapVisual = {\n  seriesType: 'treemap',\n  reset: function (seriesModel) {\n    var tree = seriesModel.getData().tree;\n    var root = tree.root;\n\n    if (root.isRemoved()) {\n      return;\n    }\n\n    travelTree(root, // Visual should calculate from tree root but not view root.\n    {}, seriesModel.getViewRoot().getAncestors(), seriesModel);\n  }\n};\n\nfunction travelTree(node, designatedVisual, viewRootAncestors, seriesModel) {\n  var nodeModel = node.getModel();\n  var nodeLayout = node.getLayout();\n  var data = node.hostTree.data; // Optimize\n\n  if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) {\n    return;\n  }\n\n  var nodeItemStyleModel = nodeModel.getModel(ITEM_STYLE_NORMAL);\n  var visuals = buildVisuals(nodeItemStyleModel, designatedVisual, seriesModel);\n  var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style'); // calculate border color\n\n  var borderColor = nodeItemStyleModel.get('borderColor');\n  var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation');\n  var thisNodeColor;\n\n  if (borderColorSaturation != null) {\n    // For performance, do not always execute 'calculateColor'.\n    thisNodeColor = calculateColor(visuals);\n    borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor);\n  }\n\n  existsStyle.stroke = borderColor;\n  var viewChildren = node.viewChildren;\n\n  if (!viewChildren || !viewChildren.length) {\n    thisNodeColor = calculateColor(visuals); // Apply visual to this node.\n\n    existsStyle.fill = thisNodeColor;\n  } else {\n    var mapping_1 = buildVisualMapping(node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren); // Designate visual to children.\n\n    each(viewChildren, function (child, index) {\n      // If higher than viewRoot, only ancestors of viewRoot is needed to visit.\n      if (child.depth >= viewRootAncestors.length || child === viewRootAncestors[child.depth]) {\n        var childVisual = mapVisual(nodeModel, visuals, child, index, mapping_1, seriesModel);\n        travelTree(child, childVisual, viewRootAncestors, seriesModel);\n      }\n    });\n  }\n}\n\nfunction buildVisuals(nodeItemStyleModel, designatedVisual, seriesModel) {\n  var visuals = extend({}, designatedVisual);\n  var designatedVisualItemStyle = seriesModel.designatedVisualItemStyle;\n  each(['color', 'colorAlpha', 'colorSaturation'], function (visualName) {\n    // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel\n    designatedVisualItemStyle[visualName] = designatedVisual[visualName];\n    var val = nodeItemStyleModel.get(visualName);\n    designatedVisualItemStyle[visualName] = null;\n    val != null && (visuals[visualName] = val);\n  });\n  return visuals;\n}\n\nfunction calculateColor(visuals) {\n  var color = getValueVisualDefine(visuals, 'color');\n\n  if (color) {\n    var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha');\n    var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation');\n\n    if (colorSaturation) {\n      color = modifyHSL(color, null, null, colorSaturation);\n    }\n\n    if (colorAlpha) {\n      color = modifyAlpha(color, colorAlpha);\n    }\n\n    return color;\n  }\n}\n\nfunction calculateBorderColor(borderColorSaturation, thisNodeColor) {\n  return thisNodeColor != null // Can only be string\n  ? modifyHSL(thisNodeColor, null, null, borderColorSaturation) : null;\n}\n\nfunction getValueVisualDefine(visuals, name) {\n  var value = visuals[name];\n\n  if (value != null && value !== 'none') {\n    return value;\n  }\n}\n\nfunction buildVisualMapping(node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren) {\n  if (!viewChildren || !viewChildren.length) {\n    return;\n  }\n\n  var rangeVisual = getRangeVisual(nodeModel, 'color') || visuals.color != null && visuals.color !== 'none' && (getRangeVisual(nodeModel, 'colorAlpha') || getRangeVisual(nodeModel, 'colorSaturation'));\n\n  if (!rangeVisual) {\n    return;\n  }\n\n  var visualMin = nodeModel.get('visualMin');\n  var visualMax = nodeModel.get('visualMax');\n  var dataExtent = nodeLayout.dataExtent.slice();\n  visualMin != null && visualMin < dataExtent[0] && (dataExtent[0] = visualMin);\n  visualMax != null && visualMax > dataExtent[1] && (dataExtent[1] = visualMax);\n  var colorMappingBy = nodeModel.get('colorMappingBy');\n  var opt = {\n    type: rangeVisual.name,\n    dataExtent: dataExtent,\n    visual: rangeVisual.range\n  };\n\n  if (opt.type === 'color' && (colorMappingBy === 'index' || colorMappingBy === 'id')) {\n    opt.mappingMethod = 'category';\n    opt.loop = true; // categories is ordinal, so do not set opt.categories.\n  } else {\n    opt.mappingMethod = 'linear';\n  }\n\n  var mapping = new VisualMapping(opt);\n  inner$9(mapping).drColorMappingBy = colorMappingBy;\n  return mapping;\n} // Notice: If we don't have the attribute 'colorRange', but only use\n// attribute 'color' to represent both concepts of 'colorRange' and 'color',\n// (It means 'colorRange' when 'color' is Array, means 'color' when not array),\n// this problem will be encountered:\n// If a level-1 node doesn't have children, and its siblings have children,\n// and colorRange is set on level-1, then the node cannot be colored.\n// So we separate 'colorRange' and 'color' to different attributes.\n\n\nfunction getRangeVisual(nodeModel, name) {\n  // 'colorRange', 'colorARange', 'colorSRange'.\n  // If not exists on this node, fetch from levels and series.\n  var range = nodeModel.get(name);\n  return isArray(range) && range.length ? {\n    name: name,\n    range: range\n  } : null;\n}\n\nfunction mapVisual(nodeModel, visuals, child, index, mapping, seriesModel) {\n  var childVisuals = extend({}, visuals);\n\n  if (mapping) {\n    // Only support color, colorAlpha, colorSaturation.\n    var mappingType = mapping.type;\n    var colorMappingBy = mappingType === 'color' && inner$9(mapping).drColorMappingBy;\n    var value = colorMappingBy === 'index' ? index : colorMappingBy === 'id' ? seriesModel.mapIdToIndex(child.getId()) : child.getValue(nodeModel.get('visualDimension'));\n    childVisuals[mappingType] = mapping.mapValueToVisual(value);\n  }\n\n  return childVisuals;\n}\n\nvar mathMax$7 = Math.max;\nvar mathMin$7 = Math.min;\nvar retrieveValue = retrieve;\nvar each$4 = each;\nvar PATH_BORDER_WIDTH = ['itemStyle', 'borderWidth'];\nvar PATH_GAP_WIDTH = ['itemStyle', 'gapWidth'];\nvar PATH_UPPER_LABEL_SHOW = ['upperLabel', 'show'];\nvar PATH_UPPER_LABEL_HEIGHT = ['upperLabel', 'height'];\n/**\n * @public\n */\n\nvar treemapLayout = {\n  seriesType: 'treemap',\n  reset: function (seriesModel, ecModel, api, payload) {\n    // Layout result in each node:\n    // {x, y, width, height, area, borderWidth}\n    var ecWidth = api.getWidth();\n    var ecHeight = api.getHeight();\n    var seriesOption = seriesModel.option;\n    var layoutInfo = getLayoutRect(seriesModel.getBoxLayoutParams(), {\n      width: api.getWidth(),\n      height: api.getHeight()\n    });\n    var size = seriesOption.size || []; // Compatible with ec2.\n\n    var containerWidth = parsePercent$1(retrieveValue(layoutInfo.width, size[0]), ecWidth);\n    var containerHeight = parsePercent$1(retrieveValue(layoutInfo.height, size[1]), ecHeight); // Fetch payload info.\n\n    var payloadType = payload && payload.type;\n    var types = ['treemapZoomToNode', 'treemapRootToNode'];\n    var targetInfo = retrieveTargetInfo(payload, types, seriesModel);\n    var rootRect = payloadType === 'treemapRender' || payloadType === 'treemapMove' ? payload.rootRect : null;\n    var viewRoot = seriesModel.getViewRoot();\n    var viewAbovePath = getPathToRoot(viewRoot);\n\n    if (payloadType !== 'treemapMove') {\n      var rootSize = payloadType === 'treemapZoomToNode' ? estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) : rootRect ? [rootRect.width, rootRect.height] : [containerWidth, containerHeight];\n      var sort_1 = seriesOption.sort;\n\n      if (sort_1 && sort_1 !== 'asc' && sort_1 !== 'desc') {\n        // Default to be desc order.\n        sort_1 = 'desc';\n      }\n\n      var options = {\n        squareRatio: seriesOption.squareRatio,\n        sort: sort_1,\n        leafDepth: seriesOption.leafDepth\n      }; // layout should be cleared because using updateView but not update.\n\n      viewRoot.hostTree.clearLayouts(); // TODO\n      // optimize: if out of view clip, do not layout.\n      // But take care that if do not render node out of view clip,\n      // how to calculate start po\n\n      var viewRootLayout_1 = {\n        x: 0,\n        y: 0,\n        width: rootSize[0],\n        height: rootSize[1],\n        area: rootSize[0] * rootSize[1]\n      };\n      viewRoot.setLayout(viewRootLayout_1);\n      squarify(viewRoot, options, false, 0); // Supplement layout.\n\n      viewRootLayout_1 = viewRoot.getLayout();\n      each$4(viewAbovePath, function (node, index) {\n        var childValue = (viewAbovePath[index + 1] || viewRoot).getValue();\n        node.setLayout(extend({\n          dataExtent: [childValue, childValue],\n          borderWidth: 0,\n          upperHeight: 0\n        }, viewRootLayout_1));\n      });\n    }\n\n    var treeRoot = seriesModel.getData().tree.root;\n    treeRoot.setLayout(calculateRootPosition(layoutInfo, rootRect, targetInfo), true);\n    seriesModel.setLayoutInfo(layoutInfo); // FIXME\n    // 现在没有clip功能，暂时取ec高宽。\n\n    prunning(treeRoot, // Transform to base element coordinate system.\n    new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight), viewAbovePath, viewRoot, 0);\n  }\n};\n/**\n * Layout treemap with squarify algorithm.\n * The original presentation of this algorithm\n * was made by Mark Bruls, Kees Huizing, and Jarke J. van Wijk\n * <https://graphics.ethz.ch/teaching/scivis_common/Literature/squarifiedTreeMaps.pdf>.\n * The implementation of this algorithm was originally copied from \"d3.js\"\n * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/layout/treemap.js>\n * with some modifications made for this program.\n * See the license statement at the head of this file.\n *\n * @protected\n * @param {module:echarts/data/Tree~TreeNode} node\n * @param {Object} options\n * @param {string} options.sort 'asc' or 'desc'\n * @param {number} options.squareRatio\n * @param {boolean} hideChildren\n * @param {number} depth\n */\n\nfunction squarify(node, options, hideChildren, depth) {\n  var width;\n  var height;\n\n  if (node.isRemoved()) {\n    return;\n  }\n\n  var thisLayout = node.getLayout();\n  width = thisLayout.width;\n  height = thisLayout.height; // Considering border and gap\n\n  var nodeModel = node.getModel();\n  var borderWidth = nodeModel.get(PATH_BORDER_WIDTH);\n  var halfGapWidth = nodeModel.get(PATH_GAP_WIDTH) / 2;\n  var upperLabelHeight = getUpperLabelHeight(nodeModel);\n  var upperHeight = Math.max(borderWidth, upperLabelHeight);\n  var layoutOffset = borderWidth - halfGapWidth;\n  var layoutOffsetUpper = upperHeight - halfGapWidth;\n  node.setLayout({\n    borderWidth: borderWidth,\n    upperHeight: upperHeight,\n    upperLabelHeight: upperLabelHeight\n  }, true);\n  width = mathMax$7(width - 2 * layoutOffset, 0);\n  height = mathMax$7(height - layoutOffset - layoutOffsetUpper, 0);\n  var totalArea = width * height;\n  var viewChildren = initChildren(node, nodeModel, totalArea, options, hideChildren, depth);\n\n  if (!viewChildren.length) {\n    return;\n  }\n\n  var rect = {\n    x: layoutOffset,\n    y: layoutOffsetUpper,\n    width: width,\n    height: height\n  };\n  var rowFixedLength = mathMin$7(width, height);\n  var best = Infinity; // the best row score so far\n\n  var row = [];\n  row.area = 0;\n\n  for (var i = 0, len = viewChildren.length; i < len;) {\n    var child = viewChildren[i];\n    row.push(child);\n    row.area += child.getLayout().area;\n    var score = worst(row, rowFixedLength, options.squareRatio); // continue with this orientation\n\n    if (score <= best) {\n      i++;\n      best = score;\n    } // abort, and try a different orientation\n    else {\n        row.area -= row.pop().getLayout().area;\n        position(row, rowFixedLength, rect, halfGapWidth, false);\n        rowFixedLength = mathMin$7(rect.width, rect.height);\n        row.length = row.area = 0;\n        best = Infinity;\n      }\n  }\n\n  if (row.length) {\n    position(row, rowFixedLength, rect, halfGapWidth, true);\n  }\n\n  if (!hideChildren) {\n    var childrenVisibleMin = nodeModel.get('childrenVisibleMin');\n\n    if (childrenVisibleMin != null && totalArea < childrenVisibleMin) {\n      hideChildren = true;\n    }\n  }\n\n  for (var i = 0, len = viewChildren.length; i < len; i++) {\n    squarify(viewChildren[i], options, hideChildren, depth + 1);\n  }\n}\n/**\n * Set area to each child, and calculate data extent for visual coding.\n */\n\n\nfunction initChildren(node, nodeModel, totalArea, options, hideChildren, depth) {\n  var viewChildren = node.children || [];\n  var orderBy = options.sort;\n  orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null);\n  var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth; // leafDepth has higher priority.\n\n  if (hideChildren && !overLeafDepth) {\n    return node.viewChildren = [];\n  } // Sort children, order by desc.\n\n\n  viewChildren = filter(viewChildren, function (child) {\n    return !child.isRemoved();\n  });\n  sort$1(viewChildren, orderBy);\n  var info = statistic(nodeModel, viewChildren, orderBy);\n\n  if (info.sum === 0) {\n    return node.viewChildren = [];\n  }\n\n  info.sum = filterByThreshold(nodeModel, totalArea, info.sum, orderBy, viewChildren);\n\n  if (info.sum === 0) {\n    return node.viewChildren = [];\n  } // Set area to each child.\n\n\n  for (var i = 0, len = viewChildren.length; i < len; i++) {\n    var area = viewChildren[i].getValue() / info.sum * totalArea; // Do not use setLayout({...}, true), because it is needed to clear last layout.\n\n    viewChildren[i].setLayout({\n      area: area\n    });\n  }\n\n  if (overLeafDepth) {\n    viewChildren.length && node.setLayout({\n      isLeafRoot: true\n    }, true);\n    viewChildren.length = 0;\n  }\n\n  node.viewChildren = viewChildren;\n  node.setLayout({\n    dataExtent: info.dataExtent\n  }, true);\n  return viewChildren;\n}\n/**\n * Consider 'visibleMin'. Modify viewChildren and get new sum.\n */\n\n\nfunction filterByThreshold(nodeModel, totalArea, sum, orderBy, orderedChildren) {\n  // visibleMin is not supported yet when no option.sort.\n  if (!orderBy) {\n    return sum;\n  }\n\n  var visibleMin = nodeModel.get('visibleMin');\n  var len = orderedChildren.length;\n  var deletePoint = len; // Always travel from little value to big value.\n\n  for (var i = len - 1; i >= 0; i--) {\n    var value = orderedChildren[orderBy === 'asc' ? len - i - 1 : i].getValue();\n\n    if (value / sum * totalArea < visibleMin) {\n      deletePoint = i;\n      sum -= value;\n    }\n  }\n\n  orderBy === 'asc' ? orderedChildren.splice(0, len - deletePoint) : orderedChildren.splice(deletePoint, len - deletePoint);\n  return sum;\n}\n/**\n * Sort\n */\n\n\nfunction sort$1(viewChildren, orderBy) {\n  if (orderBy) {\n    viewChildren.sort(function (a, b) {\n      var diff = orderBy === 'asc' ? a.getValue() - b.getValue() : b.getValue() - a.getValue();\n      return diff === 0 ? orderBy === 'asc' ? a.dataIndex - b.dataIndex : b.dataIndex - a.dataIndex : diff;\n    });\n  }\n\n  return viewChildren;\n}\n/**\n * Statistic\n */\n\n\nfunction statistic(nodeModel, children, orderBy) {\n  // Calculate sum.\n  var sum = 0;\n\n  for (var i = 0, len = children.length; i < len; i++) {\n    sum += children[i].getValue();\n  } // Statistic data extent for latter visual coding.\n  // Notice: data extent should be calculate based on raw children\n  // but not filtered view children, otherwise visual mapping will not\n  // be stable when zoom (where children is filtered by visibleMin).\n\n\n  var dimension = nodeModel.get('visualDimension');\n  var dataExtent; // The same as area dimension.\n\n  if (!children || !children.length) {\n    dataExtent = [NaN, NaN];\n  } else if (dimension === 'value' && orderBy) {\n    dataExtent = [children[children.length - 1].getValue(), children[0].getValue()];\n    orderBy === 'asc' && dataExtent.reverse();\n  } // Other dimension.\n  else {\n      dataExtent = [Infinity, -Infinity];\n      each$4(children, function (child) {\n        var value = child.getValue(dimension);\n        value < dataExtent[0] && (dataExtent[0] = value);\n        value > dataExtent[1] && (dataExtent[1] = value);\n      });\n    }\n\n  return {\n    sum: sum,\n    dataExtent: dataExtent\n  };\n}\n/**\n * Computes the score for the specified row,\n * as the worst aspect ratio.\n */\n\n\nfunction worst(row, rowFixedLength, ratio) {\n  var areaMax = 0;\n  var areaMin = Infinity;\n\n  for (var i = 0, area = void 0, len = row.length; i < len; i++) {\n    area = row[i].getLayout().area;\n\n    if (area) {\n      area < areaMin && (areaMin = area);\n      area > areaMax && (areaMax = area);\n    }\n  }\n\n  var squareArea = row.area * row.area;\n  var f = rowFixedLength * rowFixedLength * ratio;\n  return squareArea ? mathMax$7(f * areaMax / squareArea, squareArea / (f * areaMin)) : Infinity;\n}\n/**\n * Positions the specified row of nodes. Modifies `rect`.\n */\n\n\nfunction position(row, rowFixedLength, rect, halfGapWidth, flush) {\n  // When rowFixedLength === rect.width,\n  // it is horizontal subdivision,\n  // rowFixedLength is the width of the subdivision,\n  // rowOtherLength is the height of the subdivision,\n  // and nodes will be positioned from left to right.\n  // wh[idx0WhenH] means: when horizontal,\n  //      wh[idx0WhenH] => wh[0] => 'width'.\n  //      xy[idx1WhenH] => xy[1] => 'y'.\n  var idx0WhenH = rowFixedLength === rect.width ? 0 : 1;\n  var idx1WhenH = 1 - idx0WhenH;\n  var xy = ['x', 'y'];\n  var wh = ['width', 'height'];\n  var last = rect[xy[idx0WhenH]];\n  var rowOtherLength = rowFixedLength ? row.area / rowFixedLength : 0;\n\n  if (flush || rowOtherLength > rect[wh[idx1WhenH]]) {\n    rowOtherLength = rect[wh[idx1WhenH]]; // over+underflow\n  }\n\n  for (var i = 0, rowLen = row.length; i < rowLen; i++) {\n    var node = row[i];\n    var nodeLayout = {};\n    var step = rowOtherLength ? node.getLayout().area / rowOtherLength : 0;\n    var wh1 = nodeLayout[wh[idx1WhenH]] = mathMax$7(rowOtherLength - 2 * halfGapWidth, 0); // We use Math.max/min to avoid negative width/height when considering gap width.\n\n    var remain = rect[xy[idx0WhenH]] + rect[wh[idx0WhenH]] - last;\n    var modWH = i === rowLen - 1 || remain < step ? remain : step;\n    var wh0 = nodeLayout[wh[idx0WhenH]] = mathMax$7(modWH - 2 * halfGapWidth, 0);\n    nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin$7(halfGapWidth, wh1 / 2);\n    nodeLayout[xy[idx0WhenH]] = last + mathMin$7(halfGapWidth, wh0 / 2);\n    last += modWH;\n    node.setLayout(nodeLayout, true);\n  }\n\n  rect[xy[idx1WhenH]] += rowOtherLength;\n  rect[wh[idx1WhenH]] -= rowOtherLength;\n} // Return [containerWidth, containerHeight] as default.\n\n\nfunction estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) {\n  // If targetInfo.node exists, we zoom to the node,\n  // so estimate whole width and height by target node.\n  var currNode = (targetInfo || {}).node;\n  var defaultSize = [containerWidth, containerHeight];\n\n  if (!currNode || currNode === viewRoot) {\n    return defaultSize;\n  }\n\n  var parent;\n  var viewArea = containerWidth * containerHeight;\n  var area = viewArea * seriesModel.option.zoomToNodeRatio;\n\n  while (parent = currNode.parentNode) {\n    // jshint ignore:line\n    var sum = 0;\n    var siblings = parent.children;\n\n    for (var i = 0, len = siblings.length; i < len; i++) {\n      sum += siblings[i].getValue();\n    }\n\n    var currNodeValue = currNode.getValue();\n\n    if (currNodeValue === 0) {\n      return defaultSize;\n    }\n\n    area *= sum / currNodeValue; // Considering border, suppose aspect ratio is 1.\n\n    var parentModel = parent.getModel();\n    var borderWidth = parentModel.get(PATH_BORDER_WIDTH);\n    var upperHeight = Math.max(borderWidth, getUpperLabelHeight(parentModel));\n    area += 4 * borderWidth * borderWidth + (3 * borderWidth + upperHeight) * Math.pow(area, 0.5);\n    area > MAX_SAFE_INTEGER && (area = MAX_SAFE_INTEGER);\n    currNode = parent;\n  }\n\n  area < viewArea && (area = viewArea);\n  var scale = Math.pow(area / viewArea, 0.5);\n  return [containerWidth * scale, containerHeight * scale];\n} // Root position based on coord of containerGroup\n\n\nfunction calculateRootPosition(layoutInfo, rootRect, targetInfo) {\n  if (rootRect) {\n    return {\n      x: rootRect.x,\n      y: rootRect.y\n    };\n  }\n\n  var defaultPosition = {\n    x: 0,\n    y: 0\n  };\n\n  if (!targetInfo) {\n    return defaultPosition;\n  } // If targetInfo is fetched by 'retrieveTargetInfo',\n  // old tree and new tree are the same tree,\n  // so the node still exists and we can visit it.\n\n\n  var targetNode = targetInfo.node;\n  var layout = targetNode.getLayout();\n\n  if (!layout) {\n    return defaultPosition;\n  } // Transform coord from local to container.\n\n\n  var targetCenter = [layout.width / 2, layout.height / 2];\n  var node = targetNode;\n\n  while (node) {\n    var nodeLayout = node.getLayout();\n    targetCenter[0] += nodeLayout.x;\n    targetCenter[1] += nodeLayout.y;\n    node = node.parentNode;\n  }\n\n  return {\n    x: layoutInfo.width / 2 - targetCenter[0],\n    y: layoutInfo.height / 2 - targetCenter[1]\n  };\n} // Mark nodes visible for prunning when visual coding and rendering.\n// Prunning depends on layout and root position, so we have to do it after layout.\n\n\nfunction prunning(node, clipRect, viewAbovePath, viewRoot, depth) {\n  var nodeLayout = node.getLayout();\n  var nodeInViewAbovePath = viewAbovePath[depth];\n  var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node;\n\n  if (nodeInViewAbovePath && !isAboveViewRoot || depth === viewAbovePath.length && node !== viewRoot) {\n    return;\n  }\n\n  node.setLayout({\n    // isInView means: viewRoot sub tree + viewAbovePath\n    isInView: true,\n    // invisible only means: outside view clip so that the node can not\n    // see but still layout for animation preparation but not render.\n    invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout),\n    isAboveViewRoot: isAboveViewRoot\n  }, true); // Transform to child coordinate.\n\n  var childClipRect = new BoundingRect(clipRect.x - nodeLayout.x, clipRect.y - nodeLayout.y, clipRect.width, clipRect.height);\n  each$4(node.viewChildren || [], function (child) {\n    prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1);\n  });\n}\n\nfunction getUpperLabelHeight(model) {\n  return model.get(PATH_UPPER_LABEL_SHOW) ? model.get(PATH_UPPER_LABEL_HEIGHT) : 0;\n}\n\nfunction install$c(registers) {\n  registers.registerSeriesModel(TreemapSeriesModel);\n  registers.registerChartView(TreemapView);\n  registers.registerVisual(treemapVisual);\n  registers.registerLayout(treemapLayout);\n  installTreemapAction(registers);\n}\n\nfunction categoryFilter(ecModel) {\n  var legendModels = ecModel.findComponents({\n    mainType: 'legend'\n  });\n\n  if (!legendModels || !legendModels.length) {\n    return;\n  }\n\n  ecModel.eachSeriesByType('graph', function (graphSeries) {\n    var categoriesData = graphSeries.getCategoriesData();\n    var graph = graphSeries.getGraph();\n    var data = graph.data;\n    var categoryNames = categoriesData.mapArray(categoriesData.getName);\n    data.filterSelf(function (idx) {\n      var model = data.getItemModel(idx);\n      var category = model.getShallow('category');\n\n      if (category != null) {\n        if (isNumber(category)) {\n          category = categoryNames[category];\n        } // If in any legend component the status is not selected.\n\n\n        for (var i = 0; i < legendModels.length; i++) {\n          if (!legendModels[i].isSelected(category)) {\n            return false;\n          }\n        }\n      }\n\n      return true;\n    });\n  });\n}\n\nfunction categoryVisual(ecModel) {\n  var paletteScope = {};\n  ecModel.eachSeriesByType('graph', function (seriesModel) {\n    var categoriesData = seriesModel.getCategoriesData();\n    var data = seriesModel.getData();\n    var categoryNameIdxMap = {};\n    categoriesData.each(function (idx) {\n      var name = categoriesData.getName(idx); // Add prefix to avoid conflict with Object.prototype.\n\n      categoryNameIdxMap['ec-' + name] = idx;\n      var itemModel = categoriesData.getItemModel(idx);\n      var style = itemModel.getModel('itemStyle').getItemStyle();\n\n      if (!style.fill) {\n        // Get color from palette.\n        style.fill = seriesModel.getColorFromPalette(name, paletteScope);\n      }\n\n      categoriesData.setItemVisual(idx, 'style', style);\n      var symbolVisualList = ['symbol', 'symbolSize', 'symbolKeepAspect'];\n\n      for (var i = 0; i < symbolVisualList.length; i++) {\n        var symbolVisual = itemModel.getShallow(symbolVisualList[i], true);\n\n        if (symbolVisual != null) {\n          categoriesData.setItemVisual(idx, symbolVisualList[i], symbolVisual);\n        }\n      }\n    }); // Assign category color to visual\n\n    if (categoriesData.count()) {\n      data.each(function (idx) {\n        var model = data.getItemModel(idx);\n        var categoryIdx = model.getShallow('category');\n\n        if (categoryIdx != null) {\n          if (isString(categoryIdx)) {\n            categoryIdx = categoryNameIdxMap['ec-' + categoryIdx];\n          }\n\n          var categoryStyle = categoriesData.getItemVisual(categoryIdx, 'style');\n          var style = data.ensureUniqueItemVisual(idx, 'style');\n          extend(style, categoryStyle);\n          var visualList = ['symbol', 'symbolSize', 'symbolKeepAspect'];\n\n          for (var i = 0; i < visualList.length; i++) {\n            data.setItemVisual(idx, visualList[i], categoriesData.getItemVisual(categoryIdx, visualList[i]));\n          }\n        }\n      });\n    }\n  });\n}\n\nfunction normalize$2(a) {\n  if (!(a instanceof Array)) {\n    a = [a, a];\n  }\n\n  return a;\n}\n\nfunction graphEdgeVisual(ecModel) {\n  ecModel.eachSeriesByType('graph', function (seriesModel) {\n    var graph = seriesModel.getGraph();\n    var edgeData = seriesModel.getEdgeData();\n    var symbolType = normalize$2(seriesModel.get('edgeSymbol'));\n    var symbolSize = normalize$2(seriesModel.get('edgeSymbolSize')); // const colorQuery = ['lineStyle', 'color'] as const;\n    // const opacityQuery = ['lineStyle', 'opacity'] as const;\n\n    edgeData.setVisual('fromSymbol', symbolType && symbolType[0]);\n    edgeData.setVisual('toSymbol', symbolType && symbolType[1]);\n    edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);\n    edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]);\n    edgeData.setVisual('style', seriesModel.getModel('lineStyle').getLineStyle());\n    edgeData.each(function (idx) {\n      var itemModel = edgeData.getItemModel(idx);\n      var edge = graph.getEdgeByIndex(idx);\n      var symbolType = normalize$2(itemModel.getShallow('symbol', true));\n      var symbolSize = normalize$2(itemModel.getShallow('symbolSize', true)); // Edge visual must after node visual\n\n      var style = itemModel.getModel('lineStyle').getLineStyle();\n      var existsStyle = edgeData.ensureUniqueItemVisual(idx, 'style');\n      extend(existsStyle, style);\n\n      switch (existsStyle.stroke) {\n        case 'source':\n          {\n            var nodeStyle = edge.node1.getVisual('style');\n            existsStyle.stroke = nodeStyle && nodeStyle.fill;\n            break;\n          }\n\n        case 'target':\n          {\n            var nodeStyle = edge.node2.getVisual('style');\n            existsStyle.stroke = nodeStyle && nodeStyle.fill;\n            break;\n          }\n      }\n\n      symbolType[0] && edge.setVisual('fromSymbol', symbolType[0]);\n      symbolType[1] && edge.setVisual('toSymbol', symbolType[1]);\n      symbolSize[0] && edge.setVisual('fromSymbolSize', symbolSize[0]);\n      symbolSize[1] && edge.setVisual('toSymbolSize', symbolSize[1]);\n    });\n  });\n}\n\nvar KEY_DELIMITER = '-->';\n/**\n * params handler\n * @param {module:echarts/model/SeriesModel} seriesModel\n * @returns {*}\n */\n\nvar getAutoCurvenessParams = function (seriesModel) {\n  return seriesModel.get('autoCurveness') || null;\n};\n/**\n * Generate a list of edge curvatures, 20 is the default\n * @param {module:echarts/model/SeriesModel} seriesModel\n * @param {number} appendLength\n * @return  20 => [0, -0.2, 0.2, -0.4, 0.4, -0.6, 0.6, -0.8, 0.8, -1, 1, -1.2, 1.2, -1.4, 1.4, -1.6, 1.6, -1.8, 1.8, -2]\n */\n\n\nvar createCurveness = function (seriesModel, appendLength) {\n  var autoCurvenessParmas = getAutoCurvenessParams(seriesModel);\n  var length = 20;\n  var curvenessList = []; // handler the function set\n\n  if (isNumber(autoCurvenessParmas)) {\n    length = autoCurvenessParmas;\n  } else if (isArray(autoCurvenessParmas)) {\n    seriesModel.__curvenessList = autoCurvenessParmas;\n    return;\n  } // append length\n\n\n  if (appendLength > length) {\n    length = appendLength;\n  } // make sure the length is even\n\n\n  var len = length % 2 ? length + 2 : length + 3;\n  curvenessList = [];\n\n  for (var i = 0; i < len; i++) {\n    curvenessList.push((i % 2 ? i + 1 : i) / 10 * (i % 2 ? -1 : 1));\n  }\n\n  seriesModel.__curvenessList = curvenessList;\n};\n/**\n * Create different cache key data in the positive and negative directions, in order to set the curvature later\n * @param {number|string|module:echarts/data/Graph.Node} n1\n * @param {number|string|module:echarts/data/Graph.Node} n2\n * @param {module:echarts/model/SeriesModel} seriesModel\n * @returns {string} key\n */\n\n\nvar getKeyOfEdges = function (n1, n2, seriesModel) {\n  var source = [n1.id, n1.dataIndex].join('.');\n  var target = [n2.id, n2.dataIndex].join('.');\n  return [seriesModel.uid, source, target].join(KEY_DELIMITER);\n};\n/**\n * get opposite key\n * @param {string} key\n * @returns {string}\n */\n\n\nvar getOppositeKey = function (key) {\n  var keys = key.split(KEY_DELIMITER);\n  return [keys[0], keys[2], keys[1]].join(KEY_DELIMITER);\n};\n/**\n * get edgeMap with key\n * @param edge\n * @param {module:echarts/model/SeriesModel} seriesModel\n */\n\n\nvar getEdgeFromMap = function (edge, seriesModel) {\n  var key = getKeyOfEdges(edge.node1, edge.node2, seriesModel);\n  return seriesModel.__edgeMap[key];\n};\n/**\n * calculate all cases total length\n * @param edge\n * @param seriesModel\n * @returns {number}\n */\n\n\nvar getTotalLengthBetweenNodes = function (edge, seriesModel) {\n  var len = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node1, edge.node2, seriesModel), seriesModel);\n  var lenV = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node2, edge.node1, seriesModel), seriesModel);\n  return len + lenV;\n};\n/**\n *\n * @param key\n */\n\n\nvar getEdgeMapLengthWithKey = function (key, seriesModel) {\n  var edgeMap = seriesModel.__edgeMap;\n  return edgeMap[key] ? edgeMap[key].length : 0;\n};\n/**\n * Count the number of edges between the same two points, used to obtain the curvature table and the parity of the edge\n * @see /graph/GraphSeries.js@getInitialData\n * @param {module:echarts/model/SeriesModel} seriesModel\n */\n\n\nfunction initCurvenessList(seriesModel) {\n  if (!getAutoCurvenessParams(seriesModel)) {\n    return;\n  }\n\n  seriesModel.__curvenessList = [];\n  seriesModel.__edgeMap = {}; // calc the array of curveness List\n\n  createCurveness(seriesModel);\n}\n/**\n * set edgeMap with key\n * @param {number|string|module:echarts/data/Graph.Node} n1\n * @param {number|string|module:echarts/data/Graph.Node} n2\n * @param {module:echarts/model/SeriesModel} seriesModel\n * @param {number} index\n */\n\nfunction createEdgeMapForCurveness(n1, n2, seriesModel, index) {\n  if (!getAutoCurvenessParams(seriesModel)) {\n    return;\n  }\n\n  var key = getKeyOfEdges(n1, n2, seriesModel);\n  var edgeMap = seriesModel.__edgeMap;\n  var oppositeEdges = edgeMap[getOppositeKey(key)]; // set direction\n\n  if (edgeMap[key] && !oppositeEdges) {\n    edgeMap[key].isForward = true;\n  } else if (oppositeEdges && edgeMap[key]) {\n    oppositeEdges.isForward = true;\n    edgeMap[key].isForward = false;\n  }\n\n  edgeMap[key] = edgeMap[key] || [];\n  edgeMap[key].push(index);\n}\n/**\n * get curvature for edge\n * @param edge\n * @param {module:echarts/model/SeriesModel} seriesModel\n * @param index\n */\n\nfunction getCurvenessForEdge(edge, seriesModel, index, needReverse) {\n  var autoCurvenessParams = getAutoCurvenessParams(seriesModel);\n  var isArrayParam = isArray(autoCurvenessParams);\n\n  if (!autoCurvenessParams) {\n    return null;\n  }\n\n  var edgeArray = getEdgeFromMap(edge, seriesModel);\n\n  if (!edgeArray) {\n    return null;\n  }\n\n  var edgeIndex = -1;\n\n  for (var i = 0; i < edgeArray.length; i++) {\n    if (edgeArray[i] === index) {\n      edgeIndex = i;\n      break;\n    }\n  } // if totalLen is Longer createCurveness\n\n\n  var totalLen = getTotalLengthBetweenNodes(edge, seriesModel);\n  createCurveness(seriesModel, totalLen);\n  edge.lineStyle = edge.lineStyle || {}; // if is opposite edge, must set curvenss to opposite number\n\n  var curKey = getKeyOfEdges(edge.node1, edge.node2, seriesModel);\n  var curvenessList = seriesModel.__curvenessList; // if pass array no need parity\n\n  var parityCorrection = isArrayParam ? 0 : totalLen % 2 ? 0 : 1;\n\n  if (!edgeArray.isForward) {\n    // the opposite edge show outside\n    var oppositeKey = getOppositeKey(curKey);\n    var len = getEdgeMapLengthWithKey(oppositeKey, seriesModel);\n    var resValue = curvenessList[edgeIndex + len + parityCorrection]; // isNeedReverse, simple, force type need reverse the curveness in the junction of the forword and the opposite\n\n    if (needReverse) {\n      // set as array may make the parity handle with the len of opposite\n      if (isArrayParam) {\n        if (autoCurvenessParams && autoCurvenessParams[0] === 0) {\n          return (len + parityCorrection) % 2 ? resValue : -resValue;\n        } else {\n          return ((len % 2 ? 0 : 1) + parityCorrection) % 2 ? resValue : -resValue;\n        }\n      } else {\n        return (len + parityCorrection) % 2 ? resValue : -resValue;\n      }\n    } else {\n      return curvenessList[edgeIndex + len + parityCorrection];\n    }\n  } else {\n    return curvenessList[parityCorrection + edgeIndex];\n  }\n}\n\nfunction simpleLayout(seriesModel) {\n  var coordSys = seriesModel.coordinateSystem;\n\n  if (coordSys && coordSys.type !== 'view') {\n    return;\n  }\n\n  var graph = seriesModel.getGraph();\n  graph.eachNode(function (node) {\n    var model = node.getModel();\n    node.setLayout([+model.get('x'), +model.get('y')]);\n  });\n  simpleLayoutEdge(graph, seriesModel);\n}\nfunction simpleLayoutEdge(graph, seriesModel) {\n  graph.eachEdge(function (edge, index) {\n    var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), -getCurvenessForEdge(edge, seriesModel, index, true), 0);\n    var p1 = clone$1(edge.node1.getLayout());\n    var p2 = clone$1(edge.node2.getLayout());\n    var points = [p1, p2];\n\n    if (+curveness) {\n      points.push([(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness, (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness]);\n    }\n\n    edge.setLayout(points);\n  });\n}\n\nfunction graphSimpleLayout(ecModel, api) {\n  ecModel.eachSeriesByType('graph', function (seriesModel) {\n    var layout = seriesModel.get('layout');\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (coordSys && coordSys.type !== 'view') {\n      var data_1 = seriesModel.getData();\n      var dimensions_1 = [];\n      each(coordSys.dimensions, function (coordDim) {\n        dimensions_1 = dimensions_1.concat(data_1.mapDimensionsAll(coordDim));\n      });\n\n      for (var dataIndex = 0; dataIndex < data_1.count(); dataIndex++) {\n        var value = [];\n        var hasValue = false;\n\n        for (var i = 0; i < dimensions_1.length; i++) {\n          var val = data_1.get(dimensions_1[i], dataIndex);\n\n          if (!isNaN(val)) {\n            hasValue = true;\n          }\n\n          value.push(val);\n        }\n\n        if (hasValue) {\n          data_1.setItemLayout(dataIndex, coordSys.dataToPoint(value));\n        } else {\n          // Also {Array.<number>}, not undefined to avoid if...else... statement\n          data_1.setItemLayout(dataIndex, [NaN, NaN]);\n        }\n      }\n\n      simpleLayoutEdge(data_1.graph, seriesModel);\n    } else if (!layout || layout === 'none') {\n      simpleLayout(seriesModel);\n    }\n  });\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction getNodeGlobalScale(seriesModel) {\n  var coordSys = seriesModel.coordinateSystem;\n\n  if (coordSys.type !== 'view') {\n    return 1;\n  }\n\n  var nodeScaleRatio = seriesModel.option.nodeScaleRatio;\n  var groupZoom = coordSys.scaleX; // Scale node when zoom changes\n\n  var roamZoom = coordSys.getZoom();\n  var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;\n  return nodeScale / groupZoom;\n}\nfunction getSymbolSize(node) {\n  var symbolSize = node.getVisual('symbolSize');\n\n  if (symbolSize instanceof Array) {\n    symbolSize = (symbolSize[0] + symbolSize[1]) / 2;\n  }\n\n  return +symbolSize;\n}\n\nvar PI$6 = Math.PI;\nvar _symbolRadiansHalf = [];\n/**\n * `basedOn` can be:\n * 'value':\n *     This layout is not accurate and have same bad case. For example,\n *     if the min value is very smaller than the max value, the nodes\n *     with the min value probably overlap even though there is enough\n *     space to layout them. So we only use this approach in the as the\n *     init layout of the force layout.\n *     FIXME\n *     Probably we do not need this method any more but use\n *     `basedOn: 'symbolSize'` in force layout if\n *     delay its init operations to GraphView.\n * 'symbolSize':\n *     This approach work only if all of the symbol size calculated.\n *     That is, the progressive rendering is not applied to graph.\n *     FIXME\n *     If progressive rendering is applied to graph some day,\n *     probably we have to use `basedOn: 'value'`.\n */\n\nfunction circularLayout(seriesModel, basedOn, draggingNode, pointer) {\n  var coordSys = seriesModel.coordinateSystem;\n\n  if (coordSys && coordSys.type !== 'view') {\n    return;\n  }\n\n  var rect = coordSys.getBoundingRect();\n  var nodeData = seriesModel.getData();\n  var graph = nodeData.graph;\n  var cx = rect.width / 2 + rect.x;\n  var cy = rect.height / 2 + rect.y;\n  var r = Math.min(rect.width, rect.height) / 2;\n  var count = nodeData.count();\n  nodeData.setLayout({\n    cx: cx,\n    cy: cy\n  });\n\n  if (!count) {\n    return;\n  }\n\n  if (draggingNode) {\n    var _a = coordSys.pointToData(pointer),\n        tempX = _a[0],\n        tempY = _a[1];\n\n    var v = [tempX - cx, tempY - cy];\n    normalize(v, v);\n    scale(v, v, r);\n    draggingNode.setLayout([cx + v[0], cy + v[1]], true);\n    var circularRotateLabel = seriesModel.get(['circular', 'rotateLabel']);\n    rotateNodeLabel(draggingNode, circularRotateLabel, cx, cy);\n  }\n\n  _layoutNodesBasedOn[basedOn](seriesModel, graph, nodeData, r, cx, cy, count);\n\n  graph.eachEdge(function (edge, index) {\n    var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), getCurvenessForEdge(edge, seriesModel, index), 0);\n    var p1 = clone$1(edge.node1.getLayout());\n    var p2 = clone$1(edge.node2.getLayout());\n    var cp1;\n    var x12 = (p1[0] + p2[0]) / 2;\n    var y12 = (p1[1] + p2[1]) / 2;\n\n    if (+curveness) {\n      curveness *= 3;\n      cp1 = [cx * curveness + x12 * (1 - curveness), cy * curveness + y12 * (1 - curveness)];\n    }\n\n    edge.setLayout([p1, p2, cp1]);\n  });\n}\nvar _layoutNodesBasedOn = {\n  value: function (seriesModel, graph, nodeData, r, cx, cy, count) {\n    var angle = 0;\n    var sum = nodeData.getSum('value');\n    var unitAngle = Math.PI * 2 / (sum || count);\n    graph.eachNode(function (node) {\n      var value = node.getValue('value');\n      var radianHalf = unitAngle * (sum ? value : 1) / 2;\n      angle += radianHalf;\n      node.setLayout([r * Math.cos(angle) + cx, r * Math.sin(angle) + cy]);\n      angle += radianHalf;\n    });\n  },\n  symbolSize: function (seriesModel, graph, nodeData, r, cx, cy, count) {\n    var sumRadian = 0;\n    _symbolRadiansHalf.length = count;\n    var nodeScale = getNodeGlobalScale(seriesModel);\n    graph.eachNode(function (node) {\n      var symbolSize = getSymbolSize(node); // Normally this case will not happen, but we still add\n      // some the defensive code (2px is an arbitrary value).\n\n      isNaN(symbolSize) && (symbolSize = 2);\n      symbolSize < 0 && (symbolSize = 0);\n      symbolSize *= nodeScale;\n      var symbolRadianHalf = Math.asin(symbolSize / 2 / r); // when `symbolSize / 2` is bigger than `r`.\n\n      isNaN(symbolRadianHalf) && (symbolRadianHalf = PI$6 / 2);\n      _symbolRadiansHalf[node.dataIndex] = symbolRadianHalf;\n      sumRadian += symbolRadianHalf * 2;\n    });\n    var halfRemainRadian = (2 * PI$6 - sumRadian) / count / 2;\n    var angle = 0;\n    graph.eachNode(function (node) {\n      var radianHalf = halfRemainRadian + _symbolRadiansHalf[node.dataIndex];\n      angle += radianHalf; // init circular layout for\n      // 1. layout undefined node\n      // 2. not fixed node\n\n      (!node.getLayout() || !node.getLayout().fixed) && node.setLayout([r * Math.cos(angle) + cx, r * Math.sin(angle) + cy]);\n      angle += radianHalf;\n    });\n  }\n};\nfunction rotateNodeLabel(node, circularRotateLabel, cx, cy) {\n  var el = node.getGraphicEl(); // need to check if el exists. '-' value may not create node element.\n\n  if (!el) {\n    return;\n  }\n\n  var nodeModel = node.getModel();\n  var labelRotate = nodeModel.get(['label', 'rotate']) || 0;\n  var symbolPath = el.getSymbolPath();\n\n  if (circularRotateLabel) {\n    var pos = node.getLayout();\n    var rad = Math.atan2(pos[1] - cy, pos[0] - cx);\n\n    if (rad < 0) {\n      rad = Math.PI * 2 + rad;\n    }\n\n    var isLeft = pos[0] < cx;\n\n    if (isLeft) {\n      rad = rad - Math.PI;\n    }\n\n    var textPosition = isLeft ? 'left' : 'right';\n    symbolPath.setTextConfig({\n      rotation: -rad,\n      position: textPosition,\n      origin: 'center'\n    });\n    var emphasisState = symbolPath.ensureState('emphasis');\n    extend(emphasisState.textConfig || (emphasisState.textConfig = {}), {\n      position: textPosition\n    });\n  } else {\n    symbolPath.setTextConfig({\n      rotation: labelRotate *= Math.PI / 180\n    });\n  }\n}\n\nfunction graphCircularLayout(ecModel) {\n  ecModel.eachSeriesByType('graph', function (seriesModel) {\n    if (seriesModel.get('layout') === 'circular') {\n      circularLayout(seriesModel, 'symbolSize');\n    }\n  });\n}\n\nvar scaleAndAdd$1 = scaleAndAdd; // function adjacentNode(n, e) {\n//     return e.n1 === n ? e.n2 : e.n1;\n// }\n\nfunction forceLayout(inNodes, inEdges, opts) {\n  var nodes = inNodes;\n  var edges = inEdges;\n  var rect = opts.rect;\n  var width = rect.width;\n  var height = rect.height;\n  var center = [rect.x + width / 2, rect.y + height / 2]; // let scale = opts.scale || 1;\n\n  var gravity = opts.gravity == null ? 0.1 : opts.gravity; // for (let i = 0; i < edges.length; i++) {\n  //     let e = edges[i];\n  //     let n1 = e.n1;\n  //     let n2 = e.n2;\n  //     n1.edges = n1.edges || [];\n  //     n2.edges = n2.edges || [];\n  //     n1.edges.push(e);\n  //     n2.edges.push(e);\n  // }\n  // Init position\n\n  for (var i = 0; i < nodes.length; i++) {\n    var n = nodes[i];\n\n    if (!n.p) {\n      n.p = create(width * (Math.random() - 0.5) + center[0], height * (Math.random() - 0.5) + center[1]);\n    }\n\n    n.pp = clone$1(n.p);\n    n.edges = null;\n  } // Formula in 'Graph Drawing by Force-directed Placement'\n  // let k = scale * Math.sqrt(width * height / nodes.length);\n  // let k2 = k * k;\n\n\n  var initialFriction = opts.friction == null ? 0.6 : opts.friction;\n  var friction = initialFriction;\n  var beforeStepCallback;\n  var afterStepCallback;\n  return {\n    warmUp: function () {\n      friction = initialFriction * 0.8;\n    },\n    setFixed: function (idx) {\n      nodes[idx].fixed = true;\n    },\n    setUnfixed: function (idx) {\n      nodes[idx].fixed = false;\n    },\n\n    /**\n     * Before step hook\n     */\n    beforeStep: function (cb) {\n      beforeStepCallback = cb;\n    },\n\n    /**\n     * After step hook\n     */\n    afterStep: function (cb) {\n      afterStepCallback = cb;\n    },\n\n    /**\n     * Some formulas were originally copied from \"d3.js\"\n     * https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/layout/force.js\n     * with some modifications made for this project.\n     * See the license statement at the head of this file.\n     */\n    step: function (cb) {\n      beforeStepCallback && beforeStepCallback(nodes, edges);\n      var v12 = [];\n      var nLen = nodes.length;\n\n      for (var i = 0; i < edges.length; i++) {\n        var e = edges[i];\n\n        if (e.ignoreForceLayout) {\n          continue;\n        }\n\n        var n1 = e.n1;\n        var n2 = e.n2;\n        sub(v12, n2.p, n1.p);\n        var d = len(v12) - e.d;\n        var w = n2.w / (n1.w + n2.w);\n\n        if (isNaN(w)) {\n          w = 0;\n        }\n\n        normalize(v12, v12);\n        !n1.fixed && scaleAndAdd$1(n1.p, n1.p, v12, w * d * friction);\n        !n2.fixed && scaleAndAdd$1(n2.p, n2.p, v12, -(1 - w) * d * friction);\n      } // Gravity\n\n\n      for (var i = 0; i < nLen; i++) {\n        var n = nodes[i];\n\n        if (!n.fixed) {\n          sub(v12, center, n.p); // let d = vec2.len(v12);\n          // vec2.scale(v12, v12, 1 / d);\n          // let gravityFactor = gravity;\n\n          scaleAndAdd$1(n.p, n.p, v12, gravity * friction);\n        }\n      } // Repulsive\n      // PENDING\n\n\n      for (var i = 0; i < nLen; i++) {\n        var n1 = nodes[i];\n\n        for (var j = i + 1; j < nLen; j++) {\n          var n2 = nodes[j];\n          sub(v12, n2.p, n1.p);\n          var d = len(v12);\n\n          if (d === 0) {\n            // Random repulse\n            set(v12, Math.random() - 0.5, Math.random() - 0.5);\n            d = 1;\n          }\n\n          var repFact = (n1.rep + n2.rep) / d / d;\n          !n1.fixed && scaleAndAdd$1(n1.pp, n1.pp, v12, repFact);\n          !n2.fixed && scaleAndAdd$1(n2.pp, n2.pp, v12, -repFact);\n        }\n      }\n\n      var v = [];\n\n      for (var i = 0; i < nLen; i++) {\n        var n = nodes[i];\n\n        if (!n.fixed) {\n          sub(v, n.p, n.pp);\n          scaleAndAdd$1(n.p, n.p, v, friction);\n          copy(n.pp, n.p);\n        }\n      }\n\n      friction = friction * 0.992;\n      var finished = friction < 0.01;\n      afterStepCallback && afterStepCallback(nodes, edges, finished);\n      cb && cb(finished);\n    }\n  };\n}\n\nfunction graphForceLayout(ecModel) {\n  ecModel.eachSeriesByType('graph', function (graphSeries) {\n    var coordSys = graphSeries.coordinateSystem;\n\n    if (coordSys && coordSys.type !== 'view') {\n      return;\n    }\n\n    if (graphSeries.get('layout') === 'force') {\n      var preservedPoints_1 = graphSeries.preservedPoints || {};\n      var graph_1 = graphSeries.getGraph();\n      var nodeData_1 = graph_1.data;\n      var edgeData = graph_1.edgeData;\n      var forceModel = graphSeries.getModel('force');\n      var initLayout = forceModel.get('initLayout');\n\n      if (graphSeries.preservedPoints) {\n        nodeData_1.each(function (idx) {\n          var id = nodeData_1.getId(idx);\n          nodeData_1.setItemLayout(idx, preservedPoints_1[id] || [NaN, NaN]);\n        });\n      } else if (!initLayout || initLayout === 'none') {\n        simpleLayout(graphSeries);\n      } else if (initLayout === 'circular') {\n        circularLayout(graphSeries, 'value');\n      }\n\n      var nodeDataExtent_1 = nodeData_1.getDataExtent('value');\n      var edgeDataExtent_1 = edgeData.getDataExtent('value'); // let edgeDataExtent = edgeData.getDataExtent('value');\n\n      var repulsion = forceModel.get('repulsion');\n      var edgeLength = forceModel.get('edgeLength');\n      var repulsionArr_1 = isArray(repulsion) ? repulsion : [repulsion, repulsion];\n      var edgeLengthArr_1 = isArray(edgeLength) ? edgeLength : [edgeLength, edgeLength]; // Larger value has smaller length\n\n      edgeLengthArr_1 = [edgeLengthArr_1[1], edgeLengthArr_1[0]];\n      var nodes_1 = nodeData_1.mapArray('value', function (value, idx) {\n        var point = nodeData_1.getItemLayout(idx);\n        var rep = linearMap(value, nodeDataExtent_1, repulsionArr_1);\n\n        if (isNaN(rep)) {\n          rep = (repulsionArr_1[0] + repulsionArr_1[1]) / 2;\n        }\n\n        return {\n          w: rep,\n          rep: rep,\n          fixed: nodeData_1.getItemModel(idx).get('fixed'),\n          p: !point || isNaN(point[0]) || isNaN(point[1]) ? null : point\n        };\n      });\n      var edges = edgeData.mapArray('value', function (value, idx) {\n        var edge = graph_1.getEdgeByIndex(idx);\n        var d = linearMap(value, edgeDataExtent_1, edgeLengthArr_1);\n\n        if (isNaN(d)) {\n          d = (edgeLengthArr_1[0] + edgeLengthArr_1[1]) / 2;\n        }\n\n        var edgeModel = edge.getModel();\n        var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), -getCurvenessForEdge(edge, graphSeries, idx, true), 0);\n        return {\n          n1: nodes_1[edge.node1.dataIndex],\n          n2: nodes_1[edge.node2.dataIndex],\n          d: d,\n          curveness: curveness,\n          ignoreForceLayout: edgeModel.get('ignoreForceLayout')\n        };\n      }); // let coordSys = graphSeries.coordinateSystem;\n\n      var rect = coordSys.getBoundingRect();\n      var forceInstance = forceLayout(nodes_1, edges, {\n        rect: rect,\n        gravity: forceModel.get('gravity'),\n        friction: forceModel.get('friction')\n      });\n      forceInstance.beforeStep(function (nodes, edges) {\n        for (var i = 0, l = nodes.length; i < l; i++) {\n          if (nodes[i].fixed) {\n            // Write back to layout instance\n            copy(nodes[i].p, graph_1.getNodeByIndex(i).getLayout());\n          }\n        }\n      });\n      forceInstance.afterStep(function (nodes, edges, stopped) {\n        for (var i = 0, l = nodes.length; i < l; i++) {\n          if (!nodes[i].fixed) {\n            graph_1.getNodeByIndex(i).setLayout(nodes[i].p);\n          }\n\n          preservedPoints_1[nodeData_1.getId(i)] = nodes[i].p;\n        }\n\n        for (var i = 0, l = edges.length; i < l; i++) {\n          var e = edges[i];\n          var edge = graph_1.getEdgeByIndex(i);\n          var p1 = e.n1.p;\n          var p2 = e.n2.p;\n          var points = edge.getLayout();\n          points = points ? points.slice() : [];\n          points[0] = points[0] || [];\n          points[1] = points[1] || [];\n          copy(points[0], p1);\n          copy(points[1], p2);\n\n          if (+e.curveness) {\n            points[2] = [(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness, (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness];\n          }\n\n          edge.setLayout(points);\n        }\n      });\n      graphSeries.forceLayout = forceInstance;\n      graphSeries.preservedPoints = preservedPoints_1; // Step to get the layout\n\n      forceInstance.step();\n    } else {\n      // Remove prev injected forceLayout instance\n      graphSeries.forceLayout = null;\n    }\n  });\n}\n\nfunction getViewRect$2(seriesModel, api, aspect) {\n  var option = extend(seriesModel.getBoxLayoutParams(), {\n    aspect: aspect\n  });\n  return getLayoutRect(option, {\n    width: api.getWidth(),\n    height: api.getHeight()\n  });\n}\n\nfunction createViewCoordSys(ecModel, api) {\n  var viewList = [];\n  ecModel.eachSeriesByType('graph', function (seriesModel) {\n    var coordSysType = seriesModel.get('coordinateSystem');\n\n    if (!coordSysType || coordSysType === 'view') {\n      var data_1 = seriesModel.getData();\n      var positions = data_1.mapArray(function (idx) {\n        var itemModel = data_1.getItemModel(idx);\n        return [+itemModel.get('x'), +itemModel.get('y')];\n      });\n      var min = [];\n      var max = [];\n      fromPoints(positions, min, max); // If width or height is 0\n\n      if (max[0] - min[0] === 0) {\n        max[0] += 1;\n        min[0] -= 1;\n      }\n\n      if (max[1] - min[1] === 0) {\n        max[1] += 1;\n        min[1] -= 1;\n      }\n\n      var aspect = (max[0] - min[0]) / (max[1] - min[1]); // FIXME If get view rect after data processed?\n\n      var viewRect = getViewRect$2(seriesModel, api, aspect); // Position may be NaN, use view rect instead\n\n      if (isNaN(aspect)) {\n        min = [viewRect.x, viewRect.y];\n        max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height];\n      }\n\n      var bbWidth = max[0] - min[0];\n      var bbHeight = max[1] - min[1];\n      var viewWidth = viewRect.width;\n      var viewHeight = viewRect.height;\n      var viewCoordSys = seriesModel.coordinateSystem = new View();\n      viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');\n      viewCoordSys.setBoundingRect(min[0], min[1], bbWidth, bbHeight);\n      viewCoordSys.setViewRect(viewRect.x, viewRect.y, viewWidth, viewHeight); // Update roam info\n\n      viewCoordSys.setCenter(seriesModel.get('center'), api);\n      viewCoordSys.setZoom(seriesModel.get('zoom'));\n      viewList.push(viewCoordSys);\n    }\n  });\n  return viewList;\n}\n\nvar straightLineProto = Line.prototype;\nvar bezierCurveProto = BezierCurve.prototype;\n\nvar StraightLineShape =\n/** @class */\nfunction () {\n  function StraightLineShape() {\n    // Start point\n    this.x1 = 0;\n    this.y1 = 0; // End point\n\n    this.x2 = 0;\n    this.y2 = 0;\n    this.percent = 1;\n  }\n\n  return StraightLineShape;\n}();\n\nvar CurveShape =\n/** @class */\nfunction (_super) {\n  __extends(CurveShape, _super);\n\n  function CurveShape() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  return CurveShape;\n}(StraightLineShape);\n\nfunction isStraightLine(shape) {\n  return isNaN(+shape.cpx1) || isNaN(+shape.cpy1);\n}\n\nvar ECLinePath =\n/** @class */\nfunction (_super) {\n  __extends(ECLinePath, _super);\n\n  function ECLinePath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'ec-line';\n    return _this;\n  }\n\n  ECLinePath.prototype.getDefaultStyle = function () {\n    return {\n      stroke: '#000',\n      fill: null\n    };\n  };\n\n  ECLinePath.prototype.getDefaultShape = function () {\n    return new StraightLineShape();\n  };\n\n  ECLinePath.prototype.buildPath = function (ctx, shape) {\n    if (isStraightLine(shape)) {\n      straightLineProto.buildPath.call(this, ctx, shape);\n    } else {\n      bezierCurveProto.buildPath.call(this, ctx, shape);\n    }\n  };\n\n  ECLinePath.prototype.pointAt = function (t) {\n    if (isStraightLine(this.shape)) {\n      return straightLineProto.pointAt.call(this, t);\n    } else {\n      return bezierCurveProto.pointAt.call(this, t);\n    }\n  };\n\n  ECLinePath.prototype.tangentAt = function (t) {\n    var shape = this.shape;\n    var p = isStraightLine(shape) ? [shape.x2 - shape.x1, shape.y2 - shape.y1] : bezierCurveProto.tangentAt.call(this, t);\n    return normalize(p, p);\n  };\n\n  return ECLinePath;\n}(Path);\n\nvar SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];\n\nfunction makeSymbolTypeKey(symbolCategory) {\n  return '_' + symbolCategory + 'Type';\n}\n/**\n * @inner\n */\n\n\nfunction createSymbol$1(name, lineData, idx) {\n  var symbolType = lineData.getItemVisual(idx, name);\n\n  if (!symbolType || symbolType === 'none') {\n    return;\n  }\n\n  var symbolSize = lineData.getItemVisual(idx, name + 'Size');\n  var symbolRotate = lineData.getItemVisual(idx, name + 'Rotate');\n  var symbolOffset = lineData.getItemVisual(idx, name + 'Offset');\n  var symbolKeepAspect = lineData.getItemVisual(idx, name + 'KeepAspect');\n  var symbolSizeArr = normalizeSymbolSize(symbolSize);\n  var symbolOffsetArr = normalizeSymbolOffset(symbolOffset || 0, symbolSizeArr);\n  var symbolPath = createSymbol(symbolType, -symbolSizeArr[0] / 2 + symbolOffsetArr[0], -symbolSizeArr[1] / 2 + symbolOffsetArr[1], symbolSizeArr[0], symbolSizeArr[1], null, symbolKeepAspect);\n  symbolPath.__specifiedRotation = symbolRotate == null || isNaN(symbolRotate) ? void 0 : +symbolRotate * Math.PI / 180 || 0;\n  symbolPath.name = name;\n  return symbolPath;\n}\n\nfunction createLine(points) {\n  var line = new ECLinePath({\n    name: 'line',\n    subPixelOptimize: true\n  });\n  setLinePoints(line.shape, points);\n  return line;\n}\n\nfunction setLinePoints(targetShape, points) {\n  targetShape.x1 = points[0][0];\n  targetShape.y1 = points[0][1];\n  targetShape.x2 = points[1][0];\n  targetShape.y2 = points[1][1];\n  targetShape.percent = 1;\n  var cp1 = points[2];\n\n  if (cp1) {\n    targetShape.cpx1 = cp1[0];\n    targetShape.cpy1 = cp1[1];\n  } else {\n    targetShape.cpx1 = NaN;\n    targetShape.cpy1 = NaN;\n  }\n}\n\nvar Line$1 =\n/** @class */\nfunction (_super) {\n  __extends(Line, _super);\n\n  function Line(lineData, idx, seriesScope) {\n    var _this = _super.call(this) || this;\n\n    _this._createLine(lineData, idx, seriesScope);\n\n    return _this;\n  }\n\n  Line.prototype._createLine = function (lineData, idx, seriesScope) {\n    var seriesModel = lineData.hostModel;\n    var linePoints = lineData.getItemLayout(idx);\n    var line = createLine(linePoints);\n    line.shape.percent = 0;\n    initProps(line, {\n      shape: {\n        percent: 1\n      }\n    }, seriesModel, idx);\n    this.add(line);\n    each(SYMBOL_CATEGORIES, function (symbolCategory) {\n      var symbol = createSymbol$1(symbolCategory, lineData, idx); // symbols must added after line to make sure\n      // it will be updated after line#update.\n      // Or symbol position and rotation update in line#beforeUpdate will be one frame slow\n\n      this.add(symbol);\n      this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);\n    }, this);\n\n    this._updateCommonStl(lineData, idx, seriesScope);\n  }; // TODO More strict on the List type in parameters?\n\n\n  Line.prototype.updateData = function (lineData, idx, seriesScope) {\n    var seriesModel = lineData.hostModel;\n    var line = this.childOfName('line');\n    var linePoints = lineData.getItemLayout(idx);\n    var target = {\n      shape: {}\n    };\n    setLinePoints(target.shape, linePoints);\n    updateProps(line, target, seriesModel, idx);\n    each(SYMBOL_CATEGORIES, function (symbolCategory) {\n      var symbolType = lineData.getItemVisual(idx, symbolCategory);\n      var key = makeSymbolTypeKey(symbolCategory); // Symbol changed\n\n      if (this[key] !== symbolType) {\n        this.remove(this.childOfName(symbolCategory));\n        var symbol = createSymbol$1(symbolCategory, lineData, idx);\n        this.add(symbol);\n      }\n\n      this[key] = symbolType;\n    }, this);\n\n    this._updateCommonStl(lineData, idx, seriesScope);\n  };\n\n  Line.prototype.getLinePath = function () {\n    return this.childAt(0);\n  };\n\n  Line.prototype._updateCommonStl = function (lineData, idx, seriesScope) {\n    var seriesModel = lineData.hostModel;\n    var line = this.childOfName('line');\n    var emphasisLineStyle = seriesScope && seriesScope.emphasisLineStyle;\n    var blurLineStyle = seriesScope && seriesScope.blurLineStyle;\n    var selectLineStyle = seriesScope && seriesScope.selectLineStyle;\n    var labelStatesModels = seriesScope && seriesScope.labelStatesModels;\n    var emphasisDisabled = seriesScope && seriesScope.emphasisDisabled;\n    var focus = seriesScope && seriesScope.focus;\n    var blurScope = seriesScope && seriesScope.blurScope; // Optimization for large dataset\n\n    if (!seriesScope || lineData.hasItemOption) {\n      var itemModel = lineData.getItemModel(idx);\n      var emphasisModel = itemModel.getModel('emphasis');\n      emphasisLineStyle = emphasisModel.getModel('lineStyle').getLineStyle();\n      blurLineStyle = itemModel.getModel(['blur', 'lineStyle']).getLineStyle();\n      selectLineStyle = itemModel.getModel(['select', 'lineStyle']).getLineStyle();\n      emphasisDisabled = emphasisModel.get('disabled');\n      focus = emphasisModel.get('focus');\n      blurScope = emphasisModel.get('blurScope');\n      labelStatesModels = getLabelStatesModels(itemModel);\n    }\n\n    var lineStyle = lineData.getItemVisual(idx, 'style');\n    var visualColor = lineStyle.stroke;\n    line.useStyle(lineStyle);\n    line.style.fill = null;\n    line.style.strokeNoScale = true;\n    line.ensureState('emphasis').style = emphasisLineStyle;\n    line.ensureState('blur').style = blurLineStyle;\n    line.ensureState('select').style = selectLineStyle; // Update symbol\n\n    each(SYMBOL_CATEGORIES, function (symbolCategory) {\n      var symbol = this.childOfName(symbolCategory);\n\n      if (symbol) {\n        // Share opacity and color with line.\n        symbol.setColor(visualColor);\n        symbol.style.opacity = lineStyle.opacity;\n\n        for (var i = 0; i < SPECIAL_STATES.length; i++) {\n          var stateName = SPECIAL_STATES[i];\n          var lineState = line.getState(stateName);\n\n          if (lineState) {\n            var lineStateStyle = lineState.style || {};\n            var state = symbol.ensureState(stateName);\n            var stateStyle = state.style || (state.style = {});\n\n            if (lineStateStyle.stroke != null) {\n              stateStyle[symbol.__isEmptyBrush ? 'stroke' : 'fill'] = lineStateStyle.stroke;\n            }\n\n            if (lineStateStyle.opacity != null) {\n              stateStyle.opacity = lineStateStyle.opacity;\n            }\n          }\n        }\n\n        symbol.markRedraw();\n      }\n    }, this);\n    var rawVal = seriesModel.getRawValue(idx);\n    setLabelStyle(this, labelStatesModels, {\n      labelDataIndex: idx,\n      labelFetcher: {\n        getFormattedLabel: function (dataIndex, stateName) {\n          return seriesModel.getFormattedLabel(dataIndex, stateName, lineData.dataType);\n        }\n      },\n      inheritColor: visualColor || '#000',\n      defaultOpacity: lineStyle.opacity,\n      defaultText: (rawVal == null ? lineData.getName(idx) : isFinite(rawVal) ? round(rawVal) : rawVal) + ''\n    });\n    var label = this.getTextContent(); // Always set `textStyle` even if `normalStyle.text` is null, because default\n    // values have to be set on `normalStyle`.\n\n    if (label) {\n      var labelNormalModel = labelStatesModels.normal;\n      label.__align = label.style.align;\n      label.__verticalAlign = label.style.verticalAlign; // 'start', 'middle', 'end'\n\n      label.__position = labelNormalModel.get('position') || 'middle';\n      var distance = labelNormalModel.get('distance');\n\n      if (!isArray(distance)) {\n        distance = [distance, distance];\n      }\n\n      label.__labelDistance = distance;\n    }\n\n    this.setTextConfig({\n      position: null,\n      local: true,\n      inside: false // Can't be inside for stroke element.\n\n    });\n    toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n  };\n\n  Line.prototype.highlight = function () {\n    enterEmphasis(this);\n  };\n\n  Line.prototype.downplay = function () {\n    leaveEmphasis(this);\n  };\n\n  Line.prototype.updateLayout = function (lineData, idx) {\n    this.setLinePoints(lineData.getItemLayout(idx));\n  };\n\n  Line.prototype.setLinePoints = function (points) {\n    var linePath = this.childOfName('line');\n    setLinePoints(linePath.shape, points);\n    linePath.dirty();\n  };\n\n  Line.prototype.beforeUpdate = function () {\n    var lineGroup = this;\n    var symbolFrom = lineGroup.childOfName('fromSymbol');\n    var symbolTo = lineGroup.childOfName('toSymbol');\n    var label = lineGroup.getTextContent(); // Quick reject\n\n    if (!symbolFrom && !symbolTo && (!label || label.ignore)) {\n      return;\n    }\n\n    var invScale = 1;\n    var parentNode = this.parent;\n\n    while (parentNode) {\n      if (parentNode.scaleX) {\n        invScale /= parentNode.scaleX;\n      }\n\n      parentNode = parentNode.parent;\n    }\n\n    var line = lineGroup.childOfName('line'); // If line not changed\n    // FIXME Parent scale changed\n\n    if (!this.__dirty && !line.__dirty) {\n      return;\n    }\n\n    var percent = line.shape.percent;\n    var fromPos = line.pointAt(0);\n    var toPos = line.pointAt(percent);\n    var d = sub([], toPos, fromPos);\n    normalize(d, d);\n\n    function setSymbolRotation(symbol, percent) {\n      // Fix #12388\n      // when symbol is set to be 'arrow' in markLine,\n      // symbolRotate value will be ignored, and compulsively use tangent angle.\n      // rotate by default if symbol rotation is not specified\n      var specifiedRotation = symbol.__specifiedRotation;\n\n      if (specifiedRotation == null) {\n        var tangent = line.tangentAt(percent);\n        symbol.attr('rotation', (percent === 1 ? -1 : 1) * Math.PI / 2 - Math.atan2(tangent[1], tangent[0]));\n      } else {\n        symbol.attr('rotation', specifiedRotation);\n      }\n    }\n\n    if (symbolFrom) {\n      symbolFrom.setPosition(fromPos);\n      setSymbolRotation(symbolFrom, 0);\n      symbolFrom.scaleX = symbolFrom.scaleY = invScale * percent;\n      symbolFrom.markRedraw();\n    }\n\n    if (symbolTo) {\n      symbolTo.setPosition(toPos);\n      setSymbolRotation(symbolTo, 1);\n      symbolTo.scaleX = symbolTo.scaleY = invScale * percent;\n      symbolTo.markRedraw();\n    }\n\n    if (label && !label.ignore) {\n      label.x = label.y = 0;\n      label.originX = label.originY = 0;\n      var textAlign = void 0;\n      var textVerticalAlign = void 0;\n      var distance = label.__labelDistance;\n      var distanceX = distance[0] * invScale;\n      var distanceY = distance[1] * invScale;\n      var halfPercent = percent / 2;\n      var tangent = line.tangentAt(halfPercent);\n      var n = [tangent[1], -tangent[0]];\n      var cp = line.pointAt(halfPercent);\n\n      if (n[1] > 0) {\n        n[0] = -n[0];\n        n[1] = -n[1];\n      }\n\n      var dir = tangent[0] < 0 ? -1 : 1;\n\n      if (label.__position !== 'start' && label.__position !== 'end') {\n        var rotation = -Math.atan2(tangent[1], tangent[0]);\n\n        if (toPos[0] < fromPos[0]) {\n          rotation = Math.PI + rotation;\n        }\n\n        label.rotation = rotation;\n      }\n\n      var dy = void 0;\n\n      switch (label.__position) {\n        case 'insideStartTop':\n        case 'insideMiddleTop':\n        case 'insideEndTop':\n        case 'middle':\n          dy = -distanceY;\n          textVerticalAlign = 'bottom';\n          break;\n\n        case 'insideStartBottom':\n        case 'insideMiddleBottom':\n        case 'insideEndBottom':\n          dy = distanceY;\n          textVerticalAlign = 'top';\n          break;\n\n        default:\n          dy = 0;\n          textVerticalAlign = 'middle';\n      }\n\n      switch (label.__position) {\n        case 'end':\n          label.x = d[0] * distanceX + toPos[0];\n          label.y = d[1] * distanceY + toPos[1];\n          textAlign = d[0] > 0.8 ? 'left' : d[0] < -0.8 ? 'right' : 'center';\n          textVerticalAlign = d[1] > 0.8 ? 'top' : d[1] < -0.8 ? 'bottom' : 'middle';\n          break;\n\n        case 'start':\n          label.x = -d[0] * distanceX + fromPos[0];\n          label.y = -d[1] * distanceY + fromPos[1];\n          textAlign = d[0] > 0.8 ? 'right' : d[0] < -0.8 ? 'left' : 'center';\n          textVerticalAlign = d[1] > 0.8 ? 'bottom' : d[1] < -0.8 ? 'top' : 'middle';\n          break;\n\n        case 'insideStartTop':\n        case 'insideStart':\n        case 'insideStartBottom':\n          label.x = distanceX * dir + fromPos[0];\n          label.y = fromPos[1] + dy;\n          textAlign = tangent[0] < 0 ? 'right' : 'left';\n          label.originX = -distanceX * dir;\n          label.originY = -dy;\n          break;\n\n        case 'insideMiddleTop':\n        case 'insideMiddle':\n        case 'insideMiddleBottom':\n        case 'middle':\n          label.x = cp[0];\n          label.y = cp[1] + dy;\n          textAlign = 'center';\n          label.originY = -dy;\n          break;\n\n        case 'insideEndTop':\n        case 'insideEnd':\n        case 'insideEndBottom':\n          label.x = -distanceX * dir + toPos[0];\n          label.y = toPos[1] + dy;\n          textAlign = tangent[0] >= 0 ? 'right' : 'left';\n          label.originX = distanceX * dir;\n          label.originY = -dy;\n          break;\n      }\n\n      label.scaleX = label.scaleY = invScale;\n      label.setStyle({\n        // Use the user specified text align and baseline first\n        verticalAlign: label.__verticalAlign || textVerticalAlign,\n        align: label.__align || textAlign\n      });\n    }\n  };\n\n  return Line;\n}(Group);\n\nvar LineDraw =\n/** @class */\nfunction () {\n  function LineDraw(LineCtor) {\n    this.group = new Group();\n    this._LineCtor = LineCtor || Line$1;\n  }\n\n  LineDraw.prototype.updateData = function (lineData) {\n    var _this = this; // Remove progressive els.\n\n\n    this._progressiveEls = null;\n    var lineDraw = this;\n    var group = lineDraw.group;\n    var oldLineData = lineDraw._lineData;\n    lineDraw._lineData = lineData; // There is no oldLineData only when first rendering or switching from\n    // stream mode to normal mode, where previous elements should be removed.\n\n    if (!oldLineData) {\n      group.removeAll();\n    }\n\n    var seriesScope = makeSeriesScope$1(lineData);\n    lineData.diff(oldLineData).add(function (idx) {\n      _this._doAdd(lineData, idx, seriesScope);\n    }).update(function (newIdx, oldIdx) {\n      _this._doUpdate(oldLineData, lineData, oldIdx, newIdx, seriesScope);\n    }).remove(function (idx) {\n      group.remove(oldLineData.getItemGraphicEl(idx));\n    }).execute();\n  };\n\n  LineDraw.prototype.updateLayout = function () {\n    var lineData = this._lineData; // Do not support update layout in incremental mode.\n\n    if (!lineData) {\n      return;\n    }\n\n    lineData.eachItemGraphicEl(function (el, idx) {\n      el.updateLayout(lineData, idx);\n    }, this);\n  };\n\n  LineDraw.prototype.incrementalPrepareUpdate = function (lineData) {\n    this._seriesScope = makeSeriesScope$1(lineData);\n    this._lineData = null;\n    this.group.removeAll();\n  };\n\n  LineDraw.prototype.incrementalUpdate = function (taskParams, lineData) {\n    this._progressiveEls = [];\n\n    function updateIncrementalAndHover(el) {\n      if (!el.isGroup && !isEffectObject(el)) {\n        el.incremental = true;\n        el.ensureState('emphasis').hoverLayer = true;\n      }\n    }\n\n    for (var idx = taskParams.start; idx < taskParams.end; idx++) {\n      var itemLayout = lineData.getItemLayout(idx);\n\n      if (lineNeedsDraw(itemLayout)) {\n        var el = new this._LineCtor(lineData, idx, this._seriesScope);\n        el.traverse(updateIncrementalAndHover);\n        this.group.add(el);\n        lineData.setItemGraphicEl(idx, el);\n\n        this._progressiveEls.push(el);\n      }\n    }\n  };\n\n  LineDraw.prototype.remove = function () {\n    this.group.removeAll();\n  };\n\n  LineDraw.prototype.eachRendered = function (cb) {\n    traverseElements(this._progressiveEls || this.group, cb);\n  };\n\n  LineDraw.prototype._doAdd = function (lineData, idx, seriesScope) {\n    var itemLayout = lineData.getItemLayout(idx);\n\n    if (!lineNeedsDraw(itemLayout)) {\n      return;\n    }\n\n    var el = new this._LineCtor(lineData, idx, seriesScope);\n    lineData.setItemGraphicEl(idx, el);\n    this.group.add(el);\n  };\n\n  LineDraw.prototype._doUpdate = function (oldLineData, newLineData, oldIdx, newIdx, seriesScope) {\n    var itemEl = oldLineData.getItemGraphicEl(oldIdx);\n\n    if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {\n      this.group.remove(itemEl);\n      return;\n    }\n\n    if (!itemEl) {\n      itemEl = new this._LineCtor(newLineData, newIdx, seriesScope);\n    } else {\n      itemEl.updateData(newLineData, newIdx, seriesScope);\n    }\n\n    newLineData.setItemGraphicEl(newIdx, itemEl);\n    this.group.add(itemEl);\n  };\n\n  return LineDraw;\n}();\n\nfunction isEffectObject(el) {\n  return el.animators && el.animators.length > 0;\n}\n\nfunction makeSeriesScope$1(lineData) {\n  var hostModel = lineData.hostModel;\n  var emphasisModel = hostModel.getModel('emphasis');\n  return {\n    lineStyle: hostModel.getModel('lineStyle').getLineStyle(),\n    emphasisLineStyle: emphasisModel.getModel(['lineStyle']).getLineStyle(),\n    blurLineStyle: hostModel.getModel(['blur', 'lineStyle']).getLineStyle(),\n    selectLineStyle: hostModel.getModel(['select', 'lineStyle']).getLineStyle(),\n    emphasisDisabled: emphasisModel.get('disabled'),\n    blurScope: emphasisModel.get('blurScope'),\n    focus: emphasisModel.get('focus'),\n    labelStatesModels: getLabelStatesModels(hostModel)\n  };\n}\n\nfunction isPointNaN(pt) {\n  return isNaN(pt[0]) || isNaN(pt[1]);\n}\n\nfunction lineNeedsDraw(pts) {\n  return pts && !isPointNaN(pts[0]) && !isPointNaN(pts[1]);\n}\n\nvar v1 = [];\nvar v2 = [];\nvar v3 = [];\nvar quadraticAt$1 = quadraticAt;\nvar v2DistSquare = distSquare;\nvar mathAbs$2 = Math.abs;\n\nfunction intersectCurveCircle(curvePoints, center, radius) {\n  var p0 = curvePoints[0];\n  var p1 = curvePoints[1];\n  var p2 = curvePoints[2];\n  var d = Infinity;\n  var t;\n  var radiusSquare = radius * radius;\n  var interval = 0.1;\n\n  for (var _t = 0.1; _t <= 0.9; _t += 0.1) {\n    v1[0] = quadraticAt$1(p0[0], p1[0], p2[0], _t);\n    v1[1] = quadraticAt$1(p0[1], p1[1], p2[1], _t);\n    var diff = mathAbs$2(v2DistSquare(v1, center) - radiusSquare);\n\n    if (diff < d) {\n      d = diff;\n      t = _t;\n    }\n  } // Assume the segment is monotone，Find root through Bisection method\n  // At most 32 iteration\n\n\n  for (var i = 0; i < 32; i++) {\n    // let prev = t - interval;\n    var next = t + interval; // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev);\n    // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev);\n\n    v2[0] = quadraticAt$1(p0[0], p1[0], p2[0], t);\n    v2[1] = quadraticAt$1(p0[1], p1[1], p2[1], t);\n    v3[0] = quadraticAt$1(p0[0], p1[0], p2[0], next);\n    v3[1] = quadraticAt$1(p0[1], p1[1], p2[1], next);\n    var diff = v2DistSquare(v2, center) - radiusSquare;\n\n    if (mathAbs$2(diff) < 1e-2) {\n      break;\n    } // let prevDiff = v2DistSquare(v1, center) - radiusSquare;\n\n\n    var nextDiff = v2DistSquare(v3, center) - radiusSquare;\n    interval /= 2;\n\n    if (diff < 0) {\n      if (nextDiff >= 0) {\n        t = t + interval;\n      } else {\n        t = t - interval;\n      }\n    } else {\n      if (nextDiff >= 0) {\n        t = t - interval;\n      } else {\n        t = t + interval;\n      }\n    }\n  }\n\n  return t;\n} // Adjust edge to avoid\n\n\nfunction adjustEdge(graph, scale) {\n  var tmp0 = [];\n  var quadraticSubdivide$1 = quadraticSubdivide;\n  var pts = [[], [], []];\n  var pts2 = [[], []];\n  var v = [];\n  scale /= 2;\n  graph.eachEdge(function (edge, idx) {\n    var linePoints = edge.getLayout();\n    var fromSymbol = edge.getVisual('fromSymbol');\n    var toSymbol = edge.getVisual('toSymbol');\n\n    if (!linePoints.__original) {\n      linePoints.__original = [clone$1(linePoints[0]), clone$1(linePoints[1])];\n\n      if (linePoints[2]) {\n        linePoints.__original.push(clone$1(linePoints[2]));\n      }\n    }\n\n    var originalPoints = linePoints.__original; // Quadratic curve\n\n    if (linePoints[2] != null) {\n      copy(pts[0], originalPoints[0]);\n      copy(pts[1], originalPoints[2]);\n      copy(pts[2], originalPoints[1]);\n\n      if (fromSymbol && fromSymbol !== 'none') {\n        var symbolSize = getSymbolSize(edge.node1);\n        var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale); // Subdivide and get the second\n\n        quadraticSubdivide$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0);\n        pts[0][0] = tmp0[3];\n        pts[1][0] = tmp0[4];\n        quadraticSubdivide$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0);\n        pts[0][1] = tmp0[3];\n        pts[1][1] = tmp0[4];\n      }\n\n      if (toSymbol && toSymbol !== 'none') {\n        var symbolSize = getSymbolSize(edge.node2);\n        var t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale); // Subdivide and get the first\n\n        quadraticSubdivide$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0);\n        pts[1][0] = tmp0[1];\n        pts[2][0] = tmp0[2];\n        quadraticSubdivide$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0);\n        pts[1][1] = tmp0[1];\n        pts[2][1] = tmp0[2];\n      } // Copy back to layout\n\n\n      copy(linePoints[0], pts[0]);\n      copy(linePoints[1], pts[2]);\n      copy(linePoints[2], pts[1]);\n    } // Line\n    else {\n        copy(pts2[0], originalPoints[0]);\n        copy(pts2[1], originalPoints[1]);\n        sub(v, pts2[1], pts2[0]);\n        normalize(v, v);\n\n        if (fromSymbol && fromSymbol !== 'none') {\n          var symbolSize = getSymbolSize(edge.node1);\n          scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale);\n        }\n\n        if (toSymbol && toSymbol !== 'none') {\n          var symbolSize = getSymbolSize(edge.node2);\n          scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale);\n        }\n\n        copy(linePoints[0], pts2[0]);\n        copy(linePoints[1], pts2[1]);\n      }\n  });\n}\n\nfunction isViewCoordSys(coordSys) {\n  return coordSys.type === 'view';\n}\n\nvar GraphView =\n/** @class */\nfunction (_super) {\n  __extends(GraphView, _super);\n\n  function GraphView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GraphView.type;\n    return _this;\n  }\n\n  GraphView.prototype.init = function (ecModel, api) {\n    var symbolDraw = new SymbolDraw();\n    var lineDraw = new LineDraw();\n    var group = this.group;\n    this._controller = new RoamController(api.getZr());\n    this._controllerHost = {\n      target: group\n    };\n    group.add(symbolDraw.group);\n    group.add(lineDraw.group);\n    this._symbolDraw = symbolDraw;\n    this._lineDraw = lineDraw;\n    this._firstRender = true;\n  };\n\n  GraphView.prototype.render = function (seriesModel, ecModel, api) {\n    var _this = this;\n\n    var coordSys = seriesModel.coordinateSystem;\n    this._model = seriesModel;\n    var symbolDraw = this._symbolDraw;\n    var lineDraw = this._lineDraw;\n    var group = this.group;\n\n    if (isViewCoordSys(coordSys)) {\n      var groupNewProp = {\n        x: coordSys.x,\n        y: coordSys.y,\n        scaleX: coordSys.scaleX,\n        scaleY: coordSys.scaleY\n      };\n\n      if (this._firstRender) {\n        group.attr(groupNewProp);\n      } else {\n        updateProps(group, groupNewProp, seriesModel);\n      }\n    } // Fix edge contact point with node\n\n\n    adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));\n    var data = seriesModel.getData();\n    symbolDraw.updateData(data);\n    var edgeData = seriesModel.getEdgeData(); // TODO: TYPE\n\n    lineDraw.updateData(edgeData);\n\n    this._updateNodeAndLinkScale();\n\n    this._updateController(seriesModel, ecModel, api);\n\n    clearTimeout(this._layoutTimeout);\n    var forceLayout = seriesModel.forceLayout;\n    var layoutAnimation = seriesModel.get(['force', 'layoutAnimation']);\n\n    if (forceLayout) {\n      this._startForceLayoutIteration(forceLayout, layoutAnimation);\n    }\n\n    var layout = seriesModel.get('layout');\n    data.graph.eachNode(function (node) {\n      var idx = node.dataIndex;\n      var el = node.getGraphicEl();\n      var itemModel = node.getModel();\n\n      if (!el) {\n        return;\n      } // Update draggable\n\n\n      el.off('drag').off('dragend');\n      var draggable = itemModel.get('draggable');\n\n      if (draggable) {\n        el.on('drag', function (e) {\n          switch (layout) {\n            case 'force':\n              forceLayout.warmUp();\n              !_this._layouting && _this._startForceLayoutIteration(forceLayout, layoutAnimation);\n              forceLayout.setFixed(idx); // Write position back to layout\n\n              data.setItemLayout(idx, [el.x, el.y]);\n              break;\n\n            case 'circular':\n              data.setItemLayout(idx, [el.x, el.y]); // mark node fixed\n\n              node.setLayout({\n                fixed: true\n              }, true); // recalculate circular layout\n\n              circularLayout(seriesModel, 'symbolSize', node, [e.offsetX, e.offsetY]);\n\n              _this.updateLayout(seriesModel);\n\n              break;\n\n            case 'none':\n            default:\n              data.setItemLayout(idx, [el.x, el.y]); // update edge\n\n              simpleLayoutEdge(seriesModel.getGraph(), seriesModel);\n\n              _this.updateLayout(seriesModel);\n\n              break;\n          }\n        }).on('dragend', function () {\n          if (forceLayout) {\n            forceLayout.setUnfixed(idx);\n          }\n        });\n      }\n\n      el.setDraggable(draggable, !!itemModel.get('cursor'));\n      var focus = itemModel.get(['emphasis', 'focus']);\n\n      if (focus === 'adjacency') {\n        getECData(el).focus = node.getAdjacentDataIndices();\n      }\n    });\n    data.graph.eachEdge(function (edge) {\n      var el = edge.getGraphicEl();\n      var focus = edge.getModel().get(['emphasis', 'focus']);\n\n      if (!el) {\n        return;\n      }\n\n      if (focus === 'adjacency') {\n        getECData(el).focus = {\n          edge: [edge.dataIndex],\n          node: [edge.node1.dataIndex, edge.node2.dataIndex]\n        };\n      }\n    });\n    var circularRotateLabel = seriesModel.get('layout') === 'circular' && seriesModel.get(['circular', 'rotateLabel']);\n    var cx = data.getLayout('cx');\n    var cy = data.getLayout('cy');\n    data.graph.eachNode(function (node) {\n      rotateNodeLabel(node, circularRotateLabel, cx, cy);\n    });\n    this._firstRender = false;\n  };\n\n  GraphView.prototype.dispose = function () {\n    this._controller && this._controller.dispose();\n    this._controllerHost = null;\n  };\n\n  GraphView.prototype._startForceLayoutIteration = function (forceLayout, layoutAnimation) {\n    var self = this;\n\n    (function step() {\n      forceLayout.step(function (stopped) {\n        self.updateLayout(self._model);\n        (self._layouting = !stopped) && (layoutAnimation ? self._layoutTimeout = setTimeout(step, 16) : step());\n      });\n    })();\n  };\n\n  GraphView.prototype._updateController = function (seriesModel, ecModel, api) {\n    var _this = this;\n\n    var controller = this._controller;\n    var controllerHost = this._controllerHost;\n    var group = this.group;\n    controller.setPointerChecker(function (e, x, y) {\n      var rect = group.getBoundingRect();\n      rect.applyTransform(group.transform);\n      return rect.contain(x, y) && !onIrrelevantElement(e, api, seriesModel);\n    });\n\n    if (!isViewCoordSys(seriesModel.coordinateSystem)) {\n      controller.disable();\n      return;\n    }\n\n    controller.enable(seriesModel.get('roam'));\n    controllerHost.zoomLimit = seriesModel.get('scaleLimit');\n    controllerHost.zoom = seriesModel.coordinateSystem.getZoom();\n    controller.off('pan').off('zoom').on('pan', function (e) {\n      updateViewOnPan(controllerHost, e.dx, e.dy);\n      api.dispatchAction({\n        seriesId: seriesModel.id,\n        type: 'graphRoam',\n        dx: e.dx,\n        dy: e.dy\n      });\n    }).on('zoom', function (e) {\n      updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);\n      api.dispatchAction({\n        seriesId: seriesModel.id,\n        type: 'graphRoam',\n        zoom: e.scale,\n        originX: e.originX,\n        originY: e.originY\n      });\n\n      _this._updateNodeAndLinkScale();\n\n      adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));\n\n      _this._lineDraw.updateLayout(); // Only update label layout on zoom\n\n\n      api.updateLabelLayout();\n    });\n  };\n\n  GraphView.prototype._updateNodeAndLinkScale = function () {\n    var seriesModel = this._model;\n    var data = seriesModel.getData();\n    var nodeScale = getNodeGlobalScale(seriesModel);\n    data.eachItemGraphicEl(function (el, idx) {\n      el && el.setSymbolScale(nodeScale);\n    });\n  };\n\n  GraphView.prototype.updateLayout = function (seriesModel) {\n    adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));\n\n    this._symbolDraw.updateLayout();\n\n    this._lineDraw.updateLayout();\n  };\n\n  GraphView.prototype.remove = function (ecModel, api) {\n    this._symbolDraw && this._symbolDraw.remove();\n    this._lineDraw && this._lineDraw.remove();\n  };\n\n  GraphView.type = 'graph';\n  return GraphView;\n}(ChartView);\n\nfunction generateNodeKey(id) {\n  return '_EC_' + id;\n}\n\nvar Graph =\n/** @class */\nfunction () {\n  function Graph(directed) {\n    this.type = 'graph';\n    this.nodes = [];\n    this.edges = [];\n    this._nodesMap = {};\n    /**\n     * @type {Object.<string, module:echarts/data/Graph.Edge>}\n     * @private\n     */\n\n    this._edgesMap = {};\n    this._directed = directed || false;\n  }\n  /**\n   * If is directed graph\n   */\n\n\n  Graph.prototype.isDirected = function () {\n    return this._directed;\n  };\n  /**\n   * Add a new node\n   */\n\n  Graph.prototype.addNode = function (id, dataIndex) {\n    id = id == null ? '' + dataIndex : '' + id;\n    var nodesMap = this._nodesMap;\n\n    if (nodesMap[generateNodeKey(id)]) {\n      if (\"development\" !== 'production') {\n        console.error('Graph nodes have duplicate name or id');\n      }\n\n      return;\n    }\n\n    var node = new GraphNode(id, dataIndex);\n    node.hostGraph = this;\n    this.nodes.push(node);\n    nodesMap[generateNodeKey(id)] = node;\n    return node;\n  };\n  /**\n   * Get node by data index\n   */\n\n  Graph.prototype.getNodeByIndex = function (dataIndex) {\n    var rawIdx = this.data.getRawIndex(dataIndex);\n    return this.nodes[rawIdx];\n  };\n  /**\n   * Get node by id\n   */\n\n  Graph.prototype.getNodeById = function (id) {\n    return this._nodesMap[generateNodeKey(id)];\n  };\n  /**\n   * Add a new edge\n   */\n\n  Graph.prototype.addEdge = function (n1, n2, dataIndex) {\n    var nodesMap = this._nodesMap;\n    var edgesMap = this._edgesMap; // PNEDING\n\n    if (isNumber(n1)) {\n      n1 = this.nodes[n1];\n    }\n\n    if (isNumber(n2)) {\n      n2 = this.nodes[n2];\n    }\n\n    if (!(n1 instanceof GraphNode)) {\n      n1 = nodesMap[generateNodeKey(n1)];\n    }\n\n    if (!(n2 instanceof GraphNode)) {\n      n2 = nodesMap[generateNodeKey(n2)];\n    }\n\n    if (!n1 || !n2) {\n      return;\n    }\n\n    var key = n1.id + '-' + n2.id;\n    var edge = new GraphEdge(n1, n2, dataIndex);\n    edge.hostGraph = this;\n\n    if (this._directed) {\n      n1.outEdges.push(edge);\n      n2.inEdges.push(edge);\n    }\n\n    n1.edges.push(edge);\n\n    if (n1 !== n2) {\n      n2.edges.push(edge);\n    }\n\n    this.edges.push(edge);\n    edgesMap[key] = edge;\n    return edge;\n  };\n  /**\n   * Get edge by data index\n   */\n\n  Graph.prototype.getEdgeByIndex = function (dataIndex) {\n    var rawIdx = this.edgeData.getRawIndex(dataIndex);\n    return this.edges[rawIdx];\n  };\n  /**\n   * Get edge by two linked nodes\n   */\n\n  Graph.prototype.getEdge = function (n1, n2) {\n    if (n1 instanceof GraphNode) {\n      n1 = n1.id;\n    }\n\n    if (n2 instanceof GraphNode) {\n      n2 = n2.id;\n    }\n\n    var edgesMap = this._edgesMap;\n\n    if (this._directed) {\n      return edgesMap[n1 + '-' + n2];\n    } else {\n      return edgesMap[n1 + '-' + n2] || edgesMap[n2 + '-' + n1];\n    }\n  };\n  /**\n   * Iterate all nodes\n   */\n\n  Graph.prototype.eachNode = function (cb, context) {\n    var nodes = this.nodes;\n    var len = nodes.length;\n\n    for (var i = 0; i < len; i++) {\n      if (nodes[i].dataIndex >= 0) {\n        cb.call(context, nodes[i], i);\n      }\n    }\n  };\n  /**\n   * Iterate all edges\n   */\n\n  Graph.prototype.eachEdge = function (cb, context) {\n    var edges = this.edges;\n    var len = edges.length;\n\n    for (var i = 0; i < len; i++) {\n      if (edges[i].dataIndex >= 0 && edges[i].node1.dataIndex >= 0 && edges[i].node2.dataIndex >= 0) {\n        cb.call(context, edges[i], i);\n      }\n    }\n  };\n  /**\n   * Breadth first traverse\n   * Return true to stop traversing\n   */\n\n  Graph.prototype.breadthFirstTraverse = function (cb, startNode, direction, context) {\n    if (!(startNode instanceof GraphNode)) {\n      startNode = this._nodesMap[generateNodeKey(startNode)];\n    }\n\n    if (!startNode) {\n      return;\n    }\n\n    var edgeType = direction === 'out' ? 'outEdges' : direction === 'in' ? 'inEdges' : 'edges';\n\n    for (var i = 0; i < this.nodes.length; i++) {\n      this.nodes[i].__visited = false;\n    }\n\n    if (cb.call(context, startNode, null)) {\n      return;\n    }\n\n    var queue = [startNode];\n\n    while (queue.length) {\n      var currentNode = queue.shift();\n      var edges = currentNode[edgeType];\n\n      for (var i = 0; i < edges.length; i++) {\n        var e = edges[i];\n        var otherNode = e.node1 === currentNode ? e.node2 : e.node1;\n\n        if (!otherNode.__visited) {\n          if (cb.call(context, otherNode, currentNode)) {\n            // Stop traversing\n            return;\n          }\n\n          queue.push(otherNode);\n          otherNode.__visited = true;\n        }\n      }\n    }\n  };\n  // depthFirstTraverse(\n  //     cb, startNode, direction, context\n  // ) {\n  // };\n  // Filter update\n\n  Graph.prototype.update = function () {\n    var data = this.data;\n    var edgeData = this.edgeData;\n    var nodes = this.nodes;\n    var edges = this.edges;\n\n    for (var i = 0, len = nodes.length; i < len; i++) {\n      nodes[i].dataIndex = -1;\n    }\n\n    for (var i = 0, len = data.count(); i < len; i++) {\n      nodes[data.getRawIndex(i)].dataIndex = i;\n    }\n\n    edgeData.filterSelf(function (idx) {\n      var edge = edges[edgeData.getRawIndex(idx)];\n      return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0;\n    }); // Update edge\n\n    for (var i = 0, len = edges.length; i < len; i++) {\n      edges[i].dataIndex = -1;\n    }\n\n    for (var i = 0, len = edgeData.count(); i < len; i++) {\n      edges[edgeData.getRawIndex(i)].dataIndex = i;\n    }\n  };\n  /**\n   * @return {module:echarts/data/Graph}\n   */\n\n  Graph.prototype.clone = function () {\n    var graph = new Graph(this._directed);\n    var nodes = this.nodes;\n    var edges = this.edges;\n\n    for (var i = 0; i < nodes.length; i++) {\n      graph.addNode(nodes[i].id, nodes[i].dataIndex);\n    }\n\n    for (var i = 0; i < edges.length; i++) {\n      var e = edges[i];\n      graph.addEdge(e.node1.id, e.node2.id, e.dataIndex);\n    }\n\n    return graph;\n  };\n  return Graph;\n}();\n\nvar GraphNode =\n/** @class */\nfunction () {\n  function GraphNode(id, dataIndex) {\n    this.inEdges = [];\n    this.outEdges = [];\n    this.edges = [];\n    this.dataIndex = -1;\n    this.id = id == null ? '' : id;\n    this.dataIndex = dataIndex == null ? -1 : dataIndex;\n  }\n  /**\n   * @return {number}\n   */\n\n\n  GraphNode.prototype.degree = function () {\n    return this.edges.length;\n  };\n  /**\n   * @return {number}\n   */\n\n\n  GraphNode.prototype.inDegree = function () {\n    return this.inEdges.length;\n  };\n  /**\n  * @return {number}\n  */\n\n\n  GraphNode.prototype.outDegree = function () {\n    return this.outEdges.length;\n  };\n\n  GraphNode.prototype.getModel = function (path) {\n    if (this.dataIndex < 0) {\n      return;\n    }\n\n    var graph = this.hostGraph;\n    var itemModel = graph.data.getItemModel(this.dataIndex);\n    return itemModel.getModel(path);\n  };\n\n  GraphNode.prototype.getAdjacentDataIndices = function () {\n    var dataIndices = {\n      edge: [],\n      node: []\n    };\n\n    for (var i = 0; i < this.edges.length; i++) {\n      var adjacentEdge = this.edges[i];\n\n      if (adjacentEdge.dataIndex < 0) {\n        continue;\n      }\n\n      dataIndices.edge.push(adjacentEdge.dataIndex);\n      dataIndices.node.push(adjacentEdge.node1.dataIndex, adjacentEdge.node2.dataIndex);\n    }\n\n    return dataIndices;\n  };\n\n  return GraphNode;\n}();\n\nvar GraphEdge =\n/** @class */\nfunction () {\n  function GraphEdge(n1, n2, dataIndex) {\n    this.dataIndex = -1;\n    this.node1 = n1;\n    this.node2 = n2;\n    this.dataIndex = dataIndex == null ? -1 : dataIndex;\n  } // eslint-disable-next-line @typescript-eslint/no-unused-vars\n\n\n  GraphEdge.prototype.getModel = function (path) {\n    if (this.dataIndex < 0) {\n      return;\n    }\n\n    var graph = this.hostGraph;\n    var itemModel = graph.edgeData.getItemModel(this.dataIndex);\n    return itemModel.getModel(path);\n  };\n\n  GraphEdge.prototype.getAdjacentDataIndices = function () {\n    return {\n      edge: [this.dataIndex],\n      node: [this.node1.dataIndex, this.node2.dataIndex]\n    };\n  };\n\n  return GraphEdge;\n}();\n\nfunction createGraphDataProxyMixin(hostName, dataName) {\n  return {\n    /**\n     * @param Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.\n     */\n    getValue: function (dimension) {\n      var data = this[hostName][dataName];\n      return data.getStore().get(data.getDimensionIndex(dimension || 'value'), this.dataIndex);\n    },\n    // TODO: TYPE stricter type.\n    setVisual: function (key, value) {\n      this.dataIndex >= 0 && this[hostName][dataName].setItemVisual(this.dataIndex, key, value);\n    },\n    getVisual: function (key) {\n      return this[hostName][dataName].getItemVisual(this.dataIndex, key);\n    },\n    setLayout: function (layout, merge) {\n      this.dataIndex >= 0 && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge);\n    },\n    getLayout: function () {\n      return this[hostName][dataName].getItemLayout(this.dataIndex);\n    },\n    getGraphicEl: function () {\n      return this[hostName][dataName].getItemGraphicEl(this.dataIndex);\n    },\n    getRawIndex: function () {\n      return this[hostName][dataName].getRawIndex(this.dataIndex);\n    }\n  };\n}\nmixin(GraphNode, createGraphDataProxyMixin('hostGraph', 'data'));\nmixin(GraphEdge, createGraphDataProxyMixin('hostGraph', 'edgeData'));\n\nfunction createGraphFromNodeEdge(nodes, edges, seriesModel, directed, beforeLink) {\n  // ??? TODO\n  // support dataset?\n  var graph = new Graph(directed);\n\n  for (var i = 0; i < nodes.length; i++) {\n    graph.addNode(retrieve( // Id, name, dataIndex\n    nodes[i].id, nodes[i].name, i), i);\n  }\n\n  var linkNameList = [];\n  var validEdges = [];\n  var linkCount = 0;\n\n  for (var i = 0; i < edges.length; i++) {\n    var link = edges[i];\n    var source = link.source;\n    var target = link.target; // addEdge may fail when source or target not exists\n\n    if (graph.addEdge(source, target, linkCount)) {\n      validEdges.push(link);\n      linkNameList.push(retrieve(convertOptionIdName(link.id, null), source + ' > ' + target));\n      linkCount++;\n    }\n  }\n\n  var coordSys = seriesModel.get('coordinateSystem');\n  var nodeData;\n\n  if (coordSys === 'cartesian2d' || coordSys === 'polar') {\n    nodeData = createSeriesData(nodes, seriesModel);\n  } else {\n    var coordSysCtor = CoordinateSystemManager.get(coordSys);\n    var coordDimensions = coordSysCtor ? coordSysCtor.dimensions || [] : []; // FIXME: Some geo do not need `value` dimenson, whereas `calendar` needs\n    // `value` dimension, but graph need `value` dimension. It's better to\n    // uniform this behavior.\n\n    if (indexOf(coordDimensions, 'value') < 0) {\n      coordDimensions.concat(['value']);\n    }\n\n    var dimensions = prepareSeriesDataSchema(nodes, {\n      coordDimensions: coordDimensions,\n      encodeDefine: seriesModel.getEncode()\n    }).dimensions;\n    nodeData = new SeriesData(dimensions, seriesModel);\n    nodeData.initData(nodes);\n  }\n\n  var edgeData = new SeriesData(['value'], seriesModel);\n  edgeData.initData(validEdges, linkNameList);\n  beforeLink && beforeLink(nodeData, edgeData);\n  linkSeriesData({\n    mainData: nodeData,\n    struct: graph,\n    structAttr: 'graph',\n    datas: {\n      node: nodeData,\n      edge: edgeData\n    },\n    datasAttr: {\n      node: 'data',\n      edge: 'edgeData'\n    }\n  }); // Update dataIndex of nodes and edges because invalid edge may be removed\n\n  graph.update();\n  return graph;\n}\n\nvar GraphSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(GraphSeriesModel, _super);\n\n  function GraphSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GraphSeriesModel.type;\n    _this.hasSymbolVisual = true;\n    return _this;\n  }\n\n  GraphSeriesModel.prototype.init = function (option) {\n    _super.prototype.init.apply(this, arguments);\n\n    var self = this;\n\n    function getCategoriesData() {\n      return self._categoriesData;\n    } // Provide data for legend select\n\n\n    this.legendVisualProvider = new LegendVisualProvider(getCategoriesData, getCategoriesData);\n    this.fillDataTextStyle(option.edges || option.links);\n\n    this._updateCategoriesData();\n  };\n\n  GraphSeriesModel.prototype.mergeOption = function (option) {\n    _super.prototype.mergeOption.apply(this, arguments);\n\n    this.fillDataTextStyle(option.edges || option.links);\n\n    this._updateCategoriesData();\n  };\n\n  GraphSeriesModel.prototype.mergeDefaultAndTheme = function (option) {\n    _super.prototype.mergeDefaultAndTheme.apply(this, arguments);\n\n    defaultEmphasis(option, 'edgeLabel', ['show']);\n  };\n\n  GraphSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    var edges = option.edges || option.links || [];\n    var nodes = option.data || option.nodes || [];\n    var self = this;\n\n    if (nodes && edges) {\n      // auto curveness\n      initCurvenessList(this);\n      var graph = createGraphFromNodeEdge(nodes, edges, this, true, beforeLink);\n      each(graph.edges, function (edge) {\n        createEdgeMapForCurveness(edge.node1, edge.node2, this, edge.dataIndex);\n      }, this);\n      return graph.data;\n    }\n\n    function beforeLink(nodeData, edgeData) {\n      // Overwrite nodeData.getItemModel to\n      nodeData.wrapMethod('getItemModel', function (model) {\n        var categoriesModels = self._categoriesModels;\n        var categoryIdx = model.getShallow('category');\n        var categoryModel = categoriesModels[categoryIdx];\n\n        if (categoryModel) {\n          categoryModel.parentModel = model.parentModel;\n          model.parentModel = categoryModel;\n        }\n\n        return model;\n      }); // TODO Inherit resolveParentPath by default in Model#getModel?\n\n      var oldGetModel = Model.prototype.getModel;\n\n      function newGetModel(path, parentModel) {\n        var model = oldGetModel.call(this, path, parentModel);\n        model.resolveParentPath = resolveParentPath;\n        return model;\n      }\n\n      edgeData.wrapMethod('getItemModel', function (model) {\n        model.resolveParentPath = resolveParentPath;\n        model.getModel = newGetModel;\n        return model;\n      });\n\n      function resolveParentPath(pathArr) {\n        if (pathArr && (pathArr[0] === 'label' || pathArr[1] === 'label')) {\n          var newPathArr = pathArr.slice();\n\n          if (pathArr[0] === 'label') {\n            newPathArr[0] = 'edgeLabel';\n          } else if (pathArr[1] === 'label') {\n            newPathArr[1] = 'edgeLabel';\n          }\n\n          return newPathArr;\n        }\n\n        return pathArr;\n      }\n    }\n  };\n\n  GraphSeriesModel.prototype.getGraph = function () {\n    return this.getData().graph;\n  };\n\n  GraphSeriesModel.prototype.getEdgeData = function () {\n    return this.getGraph().edgeData;\n  };\n\n  GraphSeriesModel.prototype.getCategoriesData = function () {\n    return this._categoriesData;\n  };\n\n  GraphSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    if (dataType === 'edge') {\n      var nodeData = this.getData();\n      var params = this.getDataParams(dataIndex, dataType);\n      var edge = nodeData.graph.getEdgeByIndex(dataIndex);\n      var sourceName = nodeData.getName(edge.node1.dataIndex);\n      var targetName = nodeData.getName(edge.node2.dataIndex);\n      var nameArr = [];\n      sourceName != null && nameArr.push(sourceName);\n      targetName != null && nameArr.push(targetName);\n      return createTooltipMarkup('nameValue', {\n        name: nameArr.join(' > '),\n        value: params.value,\n        noValue: params.value == null\n      });\n    } // dataType === 'node' or empty\n\n\n    var nodeMarkup = defaultSeriesFormatTooltip({\n      series: this,\n      dataIndex: dataIndex,\n      multipleSeries: multipleSeries\n    });\n    return nodeMarkup;\n  };\n\n  GraphSeriesModel.prototype._updateCategoriesData = function () {\n    var categories = map(this.option.categories || [], function (category) {\n      // Data must has value\n      return category.value != null ? category : extend({\n        value: 0\n      }, category);\n    });\n    var categoriesData = new SeriesData(['value'], this);\n    categoriesData.initData(categories);\n    this._categoriesData = categoriesData;\n    this._categoriesModels = categoriesData.mapArray(function (idx) {\n      return categoriesData.getItemModel(idx);\n    });\n  };\n\n  GraphSeriesModel.prototype.setZoom = function (zoom) {\n    this.option.zoom = zoom;\n  };\n\n  GraphSeriesModel.prototype.setCenter = function (center) {\n    this.option.center = center;\n  };\n\n  GraphSeriesModel.prototype.isAnimationEnabled = function () {\n    return _super.prototype.isAnimationEnabled.call(this) // Not enable animation when do force layout\n    && !(this.get('layout') === 'force' && this.get(['force', 'layoutAnimation']));\n  };\n\n  GraphSeriesModel.type = 'series.graph';\n  GraphSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];\n  GraphSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    coordinateSystem: 'view',\n    // Default option for all coordinate systems\n    // xAxisIndex: 0,\n    // yAxisIndex: 0,\n    // polarIndex: 0,\n    // geoIndex: 0,\n    legendHoverLink: true,\n    layout: null,\n    // Configuration of circular layout\n    circular: {\n      rotateLabel: false\n    },\n    // Configuration of force directed layout\n    force: {\n      initLayout: null,\n      // Node repulsion. Can be an array to represent range.\n      repulsion: [0, 50],\n      gravity: 0.1,\n      // Initial friction\n      friction: 0.6,\n      // Edge length. Can be an array to represent range.\n      edgeLength: 30,\n      layoutAnimation: true\n    },\n    left: 'center',\n    top: 'center',\n    // right: null,\n    // bottom: null,\n    // width: '80%',\n    // height: '80%',\n    symbol: 'circle',\n    symbolSize: 10,\n    edgeSymbol: ['none', 'none'],\n    edgeSymbolSize: 10,\n    edgeLabel: {\n      position: 'middle',\n      distance: 5\n    },\n    draggable: false,\n    roam: false,\n    // Default on center of graph\n    center: null,\n    zoom: 1,\n    // Symbol size scale ratio in roam\n    nodeScaleRatio: 0.6,\n    // cursor: null,\n    // categories: [],\n    // data: []\n    // Or\n    // nodes: []\n    //\n    // links: []\n    // Or\n    // edges: []\n    label: {\n      show: false,\n      formatter: '{b}'\n    },\n    itemStyle: {},\n    lineStyle: {\n      color: '#aaa',\n      width: 1,\n      opacity: 0.5\n    },\n    emphasis: {\n      scale: true,\n      label: {\n        show: true\n      }\n    },\n    select: {\n      itemStyle: {\n        borderColor: '#212121'\n      }\n    }\n  };\n  return GraphSeriesModel;\n}(SeriesModel);\n\nvar actionInfo = {\n  type: 'graphRoam',\n  event: 'graphRoam',\n  update: 'none'\n};\nfunction install$d(registers) {\n  registers.registerChartView(GraphView);\n  registers.registerSeriesModel(GraphSeriesModel);\n  registers.registerProcessor(categoryFilter);\n  registers.registerVisual(categoryVisual);\n  registers.registerVisual(graphEdgeVisual);\n  registers.registerLayout(graphSimpleLayout);\n  registers.registerLayout(registers.PRIORITY.VISUAL.POST_CHART_LAYOUT, graphCircularLayout);\n  registers.registerLayout(graphForceLayout);\n  registers.registerCoordinateSystem('graphView', {\n    dimensions: View.dimensions,\n    create: createViewCoordSys\n  }); // Register legacy focus actions\n\n  registers.registerAction({\n    type: 'focusNodeAdjacency',\n    event: 'focusNodeAdjacency',\n    update: 'series:focusNodeAdjacency'\n  }, noop);\n  registers.registerAction({\n    type: 'unfocusNodeAdjacency',\n    event: 'unfocusNodeAdjacency',\n    update: 'series:unfocusNodeAdjacency'\n  }, noop); // Register roam action.\n\n  registers.registerAction(actionInfo, function (payload, ecModel, api) {\n    ecModel.eachComponent({\n      mainType: 'series',\n      query: payload\n    }, function (seriesModel) {\n      var coordSys = seriesModel.coordinateSystem;\n      var res = updateCenterAndZoom(coordSys, payload, undefined, api);\n      seriesModel.setCenter && seriesModel.setCenter(res.center);\n      seriesModel.setZoom && seriesModel.setZoom(res.zoom);\n    });\n  });\n}\n\nvar PointerShape =\n/** @class */\nfunction () {\n  function PointerShape() {\n    this.angle = 0;\n    this.width = 10;\n    this.r = 10;\n    this.x = 0;\n    this.y = 0;\n  }\n\n  return PointerShape;\n}();\n\nvar PointerPath =\n/** @class */\nfunction (_super) {\n  __extends(PointerPath, _super);\n\n  function PointerPath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'pointer';\n    return _this;\n  }\n\n  PointerPath.prototype.getDefaultShape = function () {\n    return new PointerShape();\n  };\n\n  PointerPath.prototype.buildPath = function (ctx, shape) {\n    var mathCos = Math.cos;\n    var mathSin = Math.sin;\n    var r = shape.r;\n    var width = shape.width;\n    var angle = shape.angle;\n    var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2);\n    var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2);\n    angle = shape.angle - Math.PI / 2;\n    ctx.moveTo(x, y);\n    ctx.lineTo(shape.x + mathCos(angle) * width, shape.y + mathSin(angle) * width);\n    ctx.lineTo(shape.x + mathCos(shape.angle) * r, shape.y + mathSin(shape.angle) * r);\n    ctx.lineTo(shape.x - mathCos(angle) * width, shape.y - mathSin(angle) * width);\n    ctx.lineTo(x, y);\n  };\n\n  return PointerPath;\n}(Path);\n\nfunction parsePosition(seriesModel, api) {\n  var center = seriesModel.get('center');\n  var width = api.getWidth();\n  var height = api.getHeight();\n  var size = Math.min(width, height);\n  var cx = parsePercent$1(center[0], api.getWidth());\n  var cy = parsePercent$1(center[1], api.getHeight());\n  var r = parsePercent$1(seriesModel.get('radius'), size / 2);\n  return {\n    cx: cx,\n    cy: cy,\n    r: r\n  };\n}\n\nfunction formatLabel(value, labelFormatter) {\n  var label = value == null ? '' : value + '';\n\n  if (labelFormatter) {\n    if (isString(labelFormatter)) {\n      label = labelFormatter.replace('{value}', label);\n    } else if (isFunction(labelFormatter)) {\n      label = labelFormatter(value);\n    }\n  }\n\n  return label;\n}\n\nvar GaugeView =\n/** @class */\nfunction (_super) {\n  __extends(GaugeView, _super);\n\n  function GaugeView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GaugeView.type;\n    return _this;\n  }\n\n  GaugeView.prototype.render = function (seriesModel, ecModel, api) {\n    this.group.removeAll();\n    var colorList = seriesModel.get(['axisLine', 'lineStyle', 'color']);\n    var posInfo = parsePosition(seriesModel, api);\n\n    this._renderMain(seriesModel, ecModel, api, colorList, posInfo);\n\n    this._data = seriesModel.getData();\n  };\n\n  GaugeView.prototype.dispose = function () {};\n\n  GaugeView.prototype._renderMain = function (seriesModel, ecModel, api, colorList, posInfo) {\n    var group = this.group;\n    var clockwise = seriesModel.get('clockwise');\n    var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI;\n    var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI;\n    var axisLineModel = seriesModel.getModel('axisLine');\n    var roundCap = axisLineModel.get('roundCap');\n    var MainPath = roundCap ? SausagePath : Sector;\n    var showAxis = axisLineModel.get('show');\n    var lineStyleModel = axisLineModel.getModel('lineStyle');\n    var axisLineWidth = lineStyleModel.get('width');\n    var angles = [startAngle, endAngle];\n    normalizeArcAngles(angles, !clockwise);\n    startAngle = angles[0];\n    endAngle = angles[1];\n    var angleRangeSpan = endAngle - startAngle;\n    var prevEndAngle = startAngle;\n    var sectors = [];\n\n    for (var i = 0; showAxis && i < colorList.length; i++) {\n      // Clamp\n      var percent = Math.min(Math.max(colorList[i][0], 0), 1);\n      endAngle = startAngle + angleRangeSpan * percent;\n      var sector = new MainPath({\n        shape: {\n          startAngle: prevEndAngle,\n          endAngle: endAngle,\n          cx: posInfo.cx,\n          cy: posInfo.cy,\n          clockwise: clockwise,\n          r0: posInfo.r - axisLineWidth,\n          r: posInfo.r\n        },\n        silent: true\n      });\n      sector.setStyle({\n        fill: colorList[i][1]\n      });\n      sector.setStyle(lineStyleModel.getLineStyle( // Because we use sector to simulate arc\n      // so the properties for stroking are useless\n      ['color', 'width']));\n      sectors.push(sector);\n      prevEndAngle = endAngle;\n    }\n\n    sectors.reverse();\n    each(sectors, function (sector) {\n      return group.add(sector);\n    });\n\n    var getColor = function (percent) {\n      // Less than 0\n      if (percent <= 0) {\n        return colorList[0][1];\n      }\n\n      var i;\n\n      for (i = 0; i < colorList.length; i++) {\n        if (colorList[i][0] >= percent && (i === 0 ? 0 : colorList[i - 1][0]) < percent) {\n          return colorList[i][1];\n        }\n      } // More than 1\n\n\n      return colorList[i - 1][1];\n    };\n\n    this._renderTicks(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth);\n\n    this._renderTitleAndDetail(seriesModel, ecModel, api, getColor, posInfo);\n\n    this._renderAnchor(seriesModel, posInfo);\n\n    this._renderPointer(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth);\n  };\n\n  GaugeView.prototype._renderTicks = function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth) {\n    var group = this.group;\n    var cx = posInfo.cx;\n    var cy = posInfo.cy;\n    var r = posInfo.r;\n    var minVal = +seriesModel.get('min');\n    var maxVal = +seriesModel.get('max');\n    var splitLineModel = seriesModel.getModel('splitLine');\n    var tickModel = seriesModel.getModel('axisTick');\n    var labelModel = seriesModel.getModel('axisLabel');\n    var splitNumber = seriesModel.get('splitNumber');\n    var subSplitNumber = tickModel.get('splitNumber');\n    var splitLineLen = parsePercent$1(splitLineModel.get('length'), r);\n    var tickLen = parsePercent$1(tickModel.get('length'), r);\n    var angle = startAngle;\n    var step = (endAngle - startAngle) / splitNumber;\n    var subStep = step / subSplitNumber;\n    var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle();\n    var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle();\n    var splitLineDistance = splitLineModel.get('distance');\n    var unitX;\n    var unitY;\n\n    for (var i = 0; i <= splitNumber; i++) {\n      unitX = Math.cos(angle);\n      unitY = Math.sin(angle); // Split line\n\n      if (splitLineModel.get('show')) {\n        var distance = splitLineDistance ? splitLineDistance + axisLineWidth : axisLineWidth;\n        var splitLine = new Line({\n          shape: {\n            x1: unitX * (r - distance) + cx,\n            y1: unitY * (r - distance) + cy,\n            x2: unitX * (r - splitLineLen - distance) + cx,\n            y2: unitY * (r - splitLineLen - distance) + cy\n          },\n          style: splitLineStyle,\n          silent: true\n        });\n\n        if (splitLineStyle.stroke === 'auto') {\n          splitLine.setStyle({\n            stroke: getColor(i / splitNumber)\n          });\n        }\n\n        group.add(splitLine);\n      } // Label\n\n\n      if (labelModel.get('show')) {\n        var distance = labelModel.get('distance') + splitLineDistance;\n        var label = formatLabel(round(i / splitNumber * (maxVal - minVal) + minVal), labelModel.get('formatter'));\n        var autoColor = getColor(i / splitNumber);\n        var textStyleX = unitX * (r - splitLineLen - distance) + cx;\n        var textStyleY = unitY * (r - splitLineLen - distance) + cy;\n        var rotateType = labelModel.get('rotate');\n        var rotate = 0;\n\n        if (rotateType === 'radial') {\n          rotate = -angle + 2 * Math.PI;\n\n          if (rotate > Math.PI / 2) {\n            rotate += Math.PI;\n          }\n        } else if (rotateType === 'tangential') {\n          rotate = -angle - Math.PI / 2;\n        } else if (isNumber(rotateType)) {\n          rotate = rotateType * Math.PI / 180;\n        }\n\n        if (rotate === 0) {\n          group.add(new ZRText({\n            style: createTextStyle(labelModel, {\n              text: label,\n              x: textStyleX,\n              y: textStyleY,\n              verticalAlign: unitY < -0.8 ? 'top' : unitY > 0.8 ? 'bottom' : 'middle',\n              align: unitX < -0.4 ? 'left' : unitX > 0.4 ? 'right' : 'center'\n            }, {\n              inheritColor: autoColor\n            }),\n            silent: true\n          }));\n        } else {\n          group.add(new ZRText({\n            style: createTextStyle(labelModel, {\n              text: label,\n              x: textStyleX,\n              y: textStyleY,\n              verticalAlign: 'middle',\n              align: 'center'\n            }, {\n              inheritColor: autoColor\n            }),\n            silent: true,\n            originX: textStyleX,\n            originY: textStyleY,\n            rotation: rotate\n          }));\n        }\n      } // Axis tick\n\n\n      if (tickModel.get('show') && i !== splitNumber) {\n        var distance = tickModel.get('distance');\n        distance = distance ? distance + axisLineWidth : axisLineWidth;\n\n        for (var j = 0; j <= subSplitNumber; j++) {\n          unitX = Math.cos(angle);\n          unitY = Math.sin(angle);\n          var tickLine = new Line({\n            shape: {\n              x1: unitX * (r - distance) + cx,\n              y1: unitY * (r - distance) + cy,\n              x2: unitX * (r - tickLen - distance) + cx,\n              y2: unitY * (r - tickLen - distance) + cy\n            },\n            silent: true,\n            style: tickLineStyle\n          });\n\n          if (tickLineStyle.stroke === 'auto') {\n            tickLine.setStyle({\n              stroke: getColor((i + j / subSplitNumber) / splitNumber)\n            });\n          }\n\n          group.add(tickLine);\n          angle += subStep;\n        }\n\n        angle -= subStep;\n      } else {\n        angle += step;\n      }\n    }\n  };\n\n  GaugeView.prototype._renderPointer = function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth) {\n    var group = this.group;\n    var oldData = this._data;\n    var oldProgressData = this._progressEls;\n    var progressList = [];\n    var showPointer = seriesModel.get(['pointer', 'show']);\n    var progressModel = seriesModel.getModel('progress');\n    var showProgress = progressModel.get('show');\n    var data = seriesModel.getData();\n    var valueDim = data.mapDimension('value');\n    var minVal = +seriesModel.get('min');\n    var maxVal = +seriesModel.get('max');\n    var valueExtent = [minVal, maxVal];\n    var angleExtent = [startAngle, endAngle];\n\n    function createPointer(idx, angle) {\n      var itemModel = data.getItemModel(idx);\n      var pointerModel = itemModel.getModel('pointer');\n      var pointerWidth = parsePercent$1(pointerModel.get('width'), posInfo.r);\n      var pointerLength = parsePercent$1(pointerModel.get('length'), posInfo.r);\n      var pointerStr = seriesModel.get(['pointer', 'icon']);\n      var pointerOffset = pointerModel.get('offsetCenter');\n      var pointerOffsetX = parsePercent$1(pointerOffset[0], posInfo.r);\n      var pointerOffsetY = parsePercent$1(pointerOffset[1], posInfo.r);\n      var pointerKeepAspect = pointerModel.get('keepAspect');\n      var pointer; // not exist icon type will be set 'rect'\n\n      if (pointerStr) {\n        pointer = createSymbol(pointerStr, pointerOffsetX - pointerWidth / 2, pointerOffsetY - pointerLength, pointerWidth, pointerLength, null, pointerKeepAspect);\n      } else {\n        pointer = new PointerPath({\n          shape: {\n            angle: -Math.PI / 2,\n            width: pointerWidth,\n            r: pointerLength,\n            x: pointerOffsetX,\n            y: pointerOffsetY\n          }\n        });\n      }\n\n      pointer.rotation = -(angle + Math.PI / 2);\n      pointer.x = posInfo.cx;\n      pointer.y = posInfo.cy;\n      return pointer;\n    }\n\n    function createProgress(idx, endAngle) {\n      var roundCap = progressModel.get('roundCap');\n      var ProgressPath = roundCap ? SausagePath : Sector;\n      var isOverlap = progressModel.get('overlap');\n      var progressWidth = isOverlap ? progressModel.get('width') : axisLineWidth / data.count();\n      var r0 = isOverlap ? posInfo.r - progressWidth : posInfo.r - (idx + 1) * progressWidth;\n      var r = isOverlap ? posInfo.r : posInfo.r - idx * progressWidth;\n      var progress = new ProgressPath({\n        shape: {\n          startAngle: startAngle,\n          endAngle: endAngle,\n          cx: posInfo.cx,\n          cy: posInfo.cy,\n          clockwise: clockwise,\n          r0: r0,\n          r: r\n        }\n      });\n      isOverlap && (progress.z2 = maxVal - data.get(valueDim, idx) % maxVal);\n      return progress;\n    }\n\n    if (showProgress || showPointer) {\n      data.diff(oldData).add(function (idx) {\n        var val = data.get(valueDim, idx);\n\n        if (showPointer) {\n          var pointer = createPointer(idx, startAngle); // TODO hide pointer on NaN value?\n\n          initProps(pointer, {\n            rotation: -((isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) + Math.PI / 2)\n          }, seriesModel);\n          group.add(pointer);\n          data.setItemGraphicEl(idx, pointer);\n        }\n\n        if (showProgress) {\n          var progress = createProgress(idx, startAngle);\n          var isClip = progressModel.get('clip');\n          initProps(progress, {\n            shape: {\n              endAngle: linearMap(val, valueExtent, angleExtent, isClip)\n            }\n          }, seriesModel);\n          group.add(progress); // Add data index and series index for indexing the data by element\n          // Useful in tooltip\n\n          setCommonECData(seriesModel.seriesIndex, data.dataType, idx, progress);\n          progressList[idx] = progress;\n        }\n      }).update(function (newIdx, oldIdx) {\n        var val = data.get(valueDim, newIdx);\n\n        if (showPointer) {\n          var previousPointer = oldData.getItemGraphicEl(oldIdx);\n          var previousRotate = previousPointer ? previousPointer.rotation : startAngle;\n          var pointer = createPointer(newIdx, previousRotate);\n          pointer.rotation = previousRotate;\n          updateProps(pointer, {\n            rotation: -((isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) + Math.PI / 2)\n          }, seriesModel);\n          group.add(pointer);\n          data.setItemGraphicEl(newIdx, pointer);\n        }\n\n        if (showProgress) {\n          var previousProgress = oldProgressData[oldIdx];\n          var previousEndAngle = previousProgress ? previousProgress.shape.endAngle : startAngle;\n          var progress = createProgress(newIdx, previousEndAngle);\n          var isClip = progressModel.get('clip');\n          updateProps(progress, {\n            shape: {\n              endAngle: linearMap(val, valueExtent, angleExtent, isClip)\n            }\n          }, seriesModel);\n          group.add(progress); // Add data index and series index for indexing the data by element\n          // Useful in tooltip\n\n          setCommonECData(seriesModel.seriesIndex, data.dataType, newIdx, progress);\n          progressList[newIdx] = progress;\n        }\n      }).execute();\n      data.each(function (idx) {\n        var itemModel = data.getItemModel(idx);\n        var emphasisModel = itemModel.getModel('emphasis');\n        var focus = emphasisModel.get('focus');\n        var blurScope = emphasisModel.get('blurScope');\n        var emphasisDisabled = emphasisModel.get('disabled');\n\n        if (showPointer) {\n          var pointer = data.getItemGraphicEl(idx);\n          var symbolStyle = data.getItemVisual(idx, 'style');\n          var visualColor = symbolStyle.fill;\n\n          if (pointer instanceof ZRImage) {\n            var pathStyle = pointer.style;\n            pointer.useStyle(extend({\n              image: pathStyle.image,\n              x: pathStyle.x,\n              y: pathStyle.y,\n              width: pathStyle.width,\n              height: pathStyle.height\n            }, symbolStyle));\n          } else {\n            pointer.useStyle(symbolStyle);\n            pointer.type !== 'pointer' && pointer.setColor(visualColor);\n          }\n\n          pointer.setStyle(itemModel.getModel(['pointer', 'itemStyle']).getItemStyle());\n\n          if (pointer.style.fill === 'auto') {\n            pointer.setStyle('fill', getColor(linearMap(data.get(valueDim, idx), valueExtent, [0, 1], true)));\n          }\n\n          pointer.z2EmphasisLift = 0;\n          setStatesStylesFromModel(pointer, itemModel);\n          toggleHoverEmphasis(pointer, focus, blurScope, emphasisDisabled);\n        }\n\n        if (showProgress) {\n          var progress = progressList[idx];\n          progress.useStyle(data.getItemVisual(idx, 'style'));\n          progress.setStyle(itemModel.getModel(['progress', 'itemStyle']).getItemStyle());\n          progress.z2EmphasisLift = 0;\n          setStatesStylesFromModel(progress, itemModel);\n          toggleHoverEmphasis(progress, focus, blurScope, emphasisDisabled);\n        }\n      });\n      this._progressEls = progressList;\n    }\n  };\n\n  GaugeView.prototype._renderAnchor = function (seriesModel, posInfo) {\n    var anchorModel = seriesModel.getModel('anchor');\n    var showAnchor = anchorModel.get('show');\n\n    if (showAnchor) {\n      var anchorSize = anchorModel.get('size');\n      var anchorType = anchorModel.get('icon');\n      var offsetCenter = anchorModel.get('offsetCenter');\n      var anchorKeepAspect = anchorModel.get('keepAspect');\n      var anchor = createSymbol(anchorType, posInfo.cx - anchorSize / 2 + parsePercent$1(offsetCenter[0], posInfo.r), posInfo.cy - anchorSize / 2 + parsePercent$1(offsetCenter[1], posInfo.r), anchorSize, anchorSize, null, anchorKeepAspect);\n      anchor.z2 = anchorModel.get('showAbove') ? 1 : 0;\n      anchor.setStyle(anchorModel.getModel('itemStyle').getItemStyle());\n      this.group.add(anchor);\n    }\n  };\n\n  GaugeView.prototype._renderTitleAndDetail = function (seriesModel, ecModel, api, getColor, posInfo) {\n    var _this = this;\n\n    var data = seriesModel.getData();\n    var valueDim = data.mapDimension('value');\n    var minVal = +seriesModel.get('min');\n    var maxVal = +seriesModel.get('max');\n    var contentGroup = new Group();\n    var newTitleEls = [];\n    var newDetailEls = [];\n    var hasAnimation = seriesModel.isAnimationEnabled();\n    var showPointerAbove = seriesModel.get(['pointer', 'showAbove']);\n    data.diff(this._data).add(function (idx) {\n      newTitleEls[idx] = new ZRText({\n        silent: true\n      });\n      newDetailEls[idx] = new ZRText({\n        silent: true\n      });\n    }).update(function (idx, oldIdx) {\n      newTitleEls[idx] = _this._titleEls[oldIdx];\n      newDetailEls[idx] = _this._detailEls[oldIdx];\n    }).execute();\n    data.each(function (idx) {\n      var itemModel = data.getItemModel(idx);\n      var value = data.get(valueDim, idx);\n      var itemGroup = new Group();\n      var autoColor = getColor(linearMap(value, [minVal, maxVal], [0, 1], true));\n      var itemTitleModel = itemModel.getModel('title');\n\n      if (itemTitleModel.get('show')) {\n        var titleOffsetCenter = itemTitleModel.get('offsetCenter');\n        var titleX = posInfo.cx + parsePercent$1(titleOffsetCenter[0], posInfo.r);\n        var titleY = posInfo.cy + parsePercent$1(titleOffsetCenter[1], posInfo.r);\n        var labelEl = newTitleEls[idx];\n        labelEl.attr({\n          z2: showPointerAbove ? 0 : 2,\n          style: createTextStyle(itemTitleModel, {\n            x: titleX,\n            y: titleY,\n            text: data.getName(idx),\n            align: 'center',\n            verticalAlign: 'middle'\n          }, {\n            inheritColor: autoColor\n          })\n        });\n        itemGroup.add(labelEl);\n      }\n\n      var itemDetailModel = itemModel.getModel('detail');\n\n      if (itemDetailModel.get('show')) {\n        var detailOffsetCenter = itemDetailModel.get('offsetCenter');\n        var detailX = posInfo.cx + parsePercent$1(detailOffsetCenter[0], posInfo.r);\n        var detailY = posInfo.cy + parsePercent$1(detailOffsetCenter[1], posInfo.r);\n        var width = parsePercent$1(itemDetailModel.get('width'), posInfo.r);\n        var height = parsePercent$1(itemDetailModel.get('height'), posInfo.r);\n        var detailColor = seriesModel.get(['progress', 'show']) ? data.getItemVisual(idx, 'style').fill : autoColor;\n        var labelEl = newDetailEls[idx];\n        var formatter_1 = itemDetailModel.get('formatter');\n        labelEl.attr({\n          z2: showPointerAbove ? 0 : 2,\n          style: createTextStyle(itemDetailModel, {\n            x: detailX,\n            y: detailY,\n            text: formatLabel(value, formatter_1),\n            width: isNaN(width) ? null : width,\n            height: isNaN(height) ? null : height,\n            align: 'center',\n            verticalAlign: 'middle'\n          }, {\n            inheritColor: detailColor\n          })\n        });\n        setLabelValueAnimation(labelEl, {\n          normal: itemDetailModel\n        }, value, function (value) {\n          return formatLabel(value, formatter_1);\n        });\n        hasAnimation && animateLabelValue(labelEl, idx, data, seriesModel, {\n          getFormattedLabel: function (labelDataIndex, status, dataType, labelDimIndex, fmt, extendParams) {\n            return formatLabel(extendParams ? extendParams.interpolatedValue : value, formatter_1);\n          }\n        });\n        itemGroup.add(labelEl);\n      }\n\n      contentGroup.add(itemGroup);\n    });\n    this.group.add(contentGroup);\n    this._titleEls = newTitleEls;\n    this._detailEls = newDetailEls;\n  };\n\n  GaugeView.type = 'gauge';\n  return GaugeView;\n}(ChartView);\n\nvar GaugeSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(GaugeSeriesModel, _super);\n\n  function GaugeSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GaugeSeriesModel.type;\n    _this.visualStyleAccessPath = 'itemStyle';\n    return _this;\n  }\n\n  GaugeSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesDataSimply(this, ['value']);\n  };\n\n  GaugeSeriesModel.type = 'series.gauge';\n  GaugeSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    colorBy: 'data',\n    // 默认全局居中\n    center: ['50%', '50%'],\n    legendHoverLink: true,\n    radius: '75%',\n    startAngle: 225,\n    endAngle: -45,\n    clockwise: true,\n    // 最小值\n    min: 0,\n    // 最大值\n    max: 100,\n    // 分割段数，默认为10\n    splitNumber: 10,\n    // 坐标轴线\n    axisLine: {\n      // 默认显示，属性show控制显示与否\n      show: true,\n      roundCap: false,\n      lineStyle: {\n        color: [[1, '#E6EBF8']],\n        width: 10\n      }\n    },\n    // 坐标轴线\n    progress: {\n      // 默认显示，属性show控制显示与否\n      show: false,\n      overlap: true,\n      width: 10,\n      roundCap: false,\n      clip: true\n    },\n    // 分隔线\n    splitLine: {\n      // 默认显示，属性show控制显示与否\n      show: true,\n      // 属性length控制线长\n      length: 10,\n      distance: 10,\n      // 属性lineStyle（详见lineStyle）控制线条样式\n      lineStyle: {\n        color: '#63677A',\n        width: 3,\n        type: 'solid'\n      }\n    },\n    // 坐标轴小标记\n    axisTick: {\n      // 属性show控制显示与否，默认不显示\n      show: true,\n      // 每份split细分多少段\n      splitNumber: 5,\n      // 属性length控制线长\n      length: 6,\n      distance: 10,\n      // 属性lineStyle控制线条样式\n      lineStyle: {\n        color: '#63677A',\n        width: 1,\n        type: 'solid'\n      }\n    },\n    axisLabel: {\n      show: true,\n      distance: 15,\n      // formatter: null,\n      color: '#464646',\n      fontSize: 12,\n      rotate: 0\n    },\n    pointer: {\n      icon: null,\n      offsetCenter: [0, 0],\n      show: true,\n      showAbove: true,\n      length: '60%',\n      width: 6,\n      keepAspect: false\n    },\n    anchor: {\n      show: false,\n      showAbove: false,\n      size: 6,\n      icon: 'circle',\n      offsetCenter: [0, 0],\n      keepAspect: false,\n      itemStyle: {\n        color: '#fff',\n        borderWidth: 0,\n        borderColor: '#5470c6'\n      }\n    },\n    title: {\n      show: true,\n      // x, y，单位px\n      offsetCenter: [0, '20%'],\n      // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n      color: '#464646',\n      fontSize: 16,\n      valueAnimation: false\n    },\n    detail: {\n      show: true,\n      backgroundColor: 'rgba(0,0,0,0)',\n      borderWidth: 0,\n      borderColor: '#ccc',\n      width: 100,\n      height: null,\n      padding: [5, 10],\n      // x, y，单位px\n      offsetCenter: [0, '40%'],\n      // formatter: null,\n      // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n      color: '#464646',\n      fontSize: 30,\n      fontWeight: 'bold',\n      lineHeight: 30,\n      valueAnimation: false\n    }\n  };\n  return GaugeSeriesModel;\n}(SeriesModel);\n\nfunction install$e(registers) {\n  registers.registerChartView(GaugeView);\n  registers.registerSeriesModel(GaugeSeriesModel);\n}\n\nvar opacityAccessPath = ['itemStyle', 'opacity'];\n/**\n * Piece of pie including Sector, Label, LabelLine\n */\n\nvar FunnelPiece =\n/** @class */\nfunction (_super) {\n  __extends(FunnelPiece, _super);\n\n  function FunnelPiece(data, idx) {\n    var _this = _super.call(this) || this;\n\n    var polygon = _this;\n    var labelLine = new Polyline();\n    var text = new ZRText();\n    polygon.setTextContent(text);\n\n    _this.setTextGuideLine(labelLine);\n\n    _this.updateData(data, idx, true);\n\n    return _this;\n  }\n\n  FunnelPiece.prototype.updateData = function (data, idx, firstCreate) {\n    var polygon = this;\n    var seriesModel = data.hostModel;\n    var itemModel = data.getItemModel(idx);\n    var layout = data.getItemLayout(idx);\n    var emphasisModel = itemModel.getModel('emphasis');\n    var opacity = itemModel.get(opacityAccessPath);\n    opacity = opacity == null ? 1 : opacity;\n\n    if (!firstCreate) {\n      saveOldStyle(polygon);\n    } // Update common style\n\n\n    polygon.useStyle(data.getItemVisual(idx, 'style'));\n    polygon.style.lineJoin = 'round';\n\n    if (firstCreate) {\n      polygon.setShape({\n        points: layout.points\n      });\n      polygon.style.opacity = 0;\n      initProps(polygon, {\n        style: {\n          opacity: opacity\n        }\n      }, seriesModel, idx);\n    } else {\n      updateProps(polygon, {\n        style: {\n          opacity: opacity\n        },\n        shape: {\n          points: layout.points\n        }\n      }, seriesModel, idx);\n    }\n\n    setStatesStylesFromModel(polygon, itemModel);\n\n    this._updateLabel(data, idx);\n\n    toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n  };\n\n  FunnelPiece.prototype._updateLabel = function (data, idx) {\n    var polygon = this;\n    var labelLine = this.getTextGuideLine();\n    var labelText = polygon.getTextContent();\n    var seriesModel = data.hostModel;\n    var itemModel = data.getItemModel(idx);\n    var layout = data.getItemLayout(idx);\n    var labelLayout = layout.label;\n    var style = data.getItemVisual(idx, 'style');\n    var visualColor = style.fill;\n    setLabelStyle( // position will not be used in setLabelStyle\n    labelText, getLabelStatesModels(itemModel), {\n      labelFetcher: data.hostModel,\n      labelDataIndex: idx,\n      defaultOpacity: style.opacity,\n      defaultText: data.getName(idx)\n    }, {\n      normal: {\n        align: labelLayout.textAlign,\n        verticalAlign: labelLayout.verticalAlign\n      }\n    });\n    polygon.setTextConfig({\n      local: true,\n      inside: !!labelLayout.inside,\n      insideStroke: visualColor,\n      // insideFill: 'auto',\n      outsideFill: visualColor\n    });\n    var linePoints = labelLayout.linePoints;\n    labelLine.setShape({\n      points: linePoints\n    });\n    polygon.textGuideLineConfig = {\n      anchor: linePoints ? new Point(linePoints[0][0], linePoints[0][1]) : null\n    }; // Make sure update style on labelText after setLabelStyle.\n    // Because setLabelStyle will replace a new style on it.\n\n    updateProps(labelText, {\n      style: {\n        x: labelLayout.x,\n        y: labelLayout.y\n      }\n    }, seriesModel, idx);\n    labelText.attr({\n      rotation: labelLayout.rotation,\n      originX: labelLayout.x,\n      originY: labelLayout.y,\n      z2: 10\n    });\n    setLabelLineStyle(polygon, getLabelLineStatesModels(itemModel), {\n      // Default use item visual color\n      stroke: visualColor\n    });\n  };\n\n  return FunnelPiece;\n}(Polygon);\n\nvar FunnelView =\n/** @class */\nfunction (_super) {\n  __extends(FunnelView, _super);\n\n  function FunnelView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = FunnelView.type;\n    _this.ignoreLabelLineUpdate = true;\n    return _this;\n  }\n\n  FunnelView.prototype.render = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n    var oldData = this._data;\n    var group = this.group;\n    data.diff(oldData).add(function (idx) {\n      var funnelPiece = new FunnelPiece(data, idx);\n      data.setItemGraphicEl(idx, funnelPiece);\n      group.add(funnelPiece);\n    }).update(function (newIdx, oldIdx) {\n      var piece = oldData.getItemGraphicEl(oldIdx);\n      piece.updateData(data, newIdx);\n      group.add(piece);\n      data.setItemGraphicEl(newIdx, piece);\n    }).remove(function (idx) {\n      var piece = oldData.getItemGraphicEl(idx);\n      removeElementWithFadeOut(piece, seriesModel, idx);\n    }).execute();\n    this._data = data;\n  };\n\n  FunnelView.prototype.remove = function () {\n    this.group.removeAll();\n    this._data = null;\n  };\n\n  FunnelView.prototype.dispose = function () {};\n\n  FunnelView.type = 'funnel';\n  return FunnelView;\n}(ChartView);\n\nvar FunnelSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(FunnelSeriesModel, _super);\n\n  function FunnelSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = FunnelSeriesModel.type;\n    return _this;\n  }\n\n  FunnelSeriesModel.prototype.init = function (option) {\n    _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n    // Use a function instead of direct access because data reference may changed\n\n\n    this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this)); // Extend labelLine emphasis\n\n    this._defaultLabelLine(option);\n  };\n\n  FunnelSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesDataSimply(this, {\n      coordDimensions: ['value'],\n      encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n    });\n  };\n\n  FunnelSeriesModel.prototype._defaultLabelLine = function (option) {\n    // Extend labelLine emphasis\n    defaultEmphasis(option, 'labelLine', ['show']);\n    var labelLineNormalOpt = option.labelLine;\n    var labelLineEmphasisOpt = option.emphasis.labelLine; // Not show label line if `label.normal.show = false`\n\n    labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show;\n    labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show;\n  }; // Overwrite\n\n\n  FunnelSeriesModel.prototype.getDataParams = function (dataIndex) {\n    var data = this.getData();\n\n    var params = _super.prototype.getDataParams.call(this, dataIndex);\n\n    var valueDim = data.mapDimension('value');\n    var sum = data.getSum(valueDim); // Percent is 0 if sum is 0\n\n    params.percent = !sum ? 0 : +(data.get(valueDim, dataIndex) / sum * 100).toFixed(2);\n    params.$vars.push('percent');\n    return params;\n  };\n\n  FunnelSeriesModel.type = 'series.funnel';\n  FunnelSeriesModel.defaultOption = {\n    // zlevel: 0,                  // 一级层叠\n    z: 2,\n    legendHoverLink: true,\n    colorBy: 'data',\n    left: 80,\n    top: 60,\n    right: 80,\n    bottom: 60,\n    // width: {totalWidth} - left - right,\n    // height: {totalHeight} - top - bottom,\n    // 默认取数据最小最大值\n    // min: 0,\n    // max: 100,\n    minSize: '0%',\n    maxSize: '100%',\n    sort: 'descending',\n    orient: 'vertical',\n    gap: 0,\n    funnelAlign: 'center',\n    label: {\n      show: true,\n      position: 'outer' // formatter: 标签文本格式器，同Tooltip.formatter，不支持异步回调\n\n    },\n    labelLine: {\n      show: true,\n      length: 20,\n      lineStyle: {\n        // color: 各异,\n        width: 1\n      }\n    },\n    itemStyle: {\n      // color: 各异,\n      borderColor: '#fff',\n      borderWidth: 1\n    },\n    emphasis: {\n      label: {\n        show: true\n      }\n    },\n    select: {\n      itemStyle: {\n        borderColor: '#212121'\n      }\n    }\n  };\n  return FunnelSeriesModel;\n}(SeriesModel);\n\nfunction getViewRect$3(seriesModel, api) {\n  return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n    width: api.getWidth(),\n    height: api.getHeight()\n  });\n}\n\nfunction getSortedIndices(data, sort) {\n  var valueDim = data.mapDimension('value');\n  var valueArr = data.mapArray(valueDim, function (val) {\n    return val;\n  });\n  var indices = [];\n  var isAscending = sort === 'ascending';\n\n  for (var i = 0, len = data.count(); i < len; i++) {\n    indices[i] = i;\n  } // Add custom sortable function & none sortable opetion by \"options.sort\"\n\n\n  if (isFunction(sort)) {\n    indices.sort(sort);\n  } else if (sort !== 'none') {\n    indices.sort(function (a, b) {\n      return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];\n    });\n  }\n\n  return indices;\n}\n\nfunction labelLayout(data) {\n  var seriesModel = data.hostModel;\n  var orient = seriesModel.get('orient');\n  data.each(function (idx) {\n    var itemModel = data.getItemModel(idx);\n    var labelModel = itemModel.getModel('label');\n    var labelPosition = labelModel.get('position');\n    var labelLineModel = itemModel.getModel('labelLine');\n    var layout = data.getItemLayout(idx);\n    var points = layout.points;\n    var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight';\n    var textAlign;\n    var textX;\n    var textY;\n    var linePoints;\n\n    if (isLabelInside) {\n      if (labelPosition === 'insideLeft') {\n        textX = (points[0][0] + points[3][0]) / 2 + 5;\n        textY = (points[0][1] + points[3][1]) / 2;\n        textAlign = 'left';\n      } else if (labelPosition === 'insideRight') {\n        textX = (points[1][0] + points[2][0]) / 2 - 5;\n        textY = (points[1][1] + points[2][1]) / 2;\n        textAlign = 'right';\n      } else {\n        textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;\n        textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;\n        textAlign = 'center';\n      }\n\n      linePoints = [[textX, textY], [textX, textY]];\n    } else {\n      var x1 = void 0;\n      var y1 = void 0;\n      var x2 = void 0;\n      var y2 = void 0;\n      var labelLineLen = labelLineModel.get('length');\n\n      if (\"development\" !== 'production') {\n        if (orient === 'vertical' && ['top', 'bottom'].indexOf(labelPosition) > -1) {\n          labelPosition = 'left';\n          console.warn('Position error: Funnel chart on vertical orient dose not support top and bottom.');\n        }\n\n        if (orient === 'horizontal' && ['left', 'right'].indexOf(labelPosition) > -1) {\n          labelPosition = 'bottom';\n          console.warn('Position error: Funnel chart on horizontal orient dose not support left and right.');\n        }\n      }\n\n      if (labelPosition === 'left') {\n        // Left side\n        x1 = (points[3][0] + points[0][0]) / 2;\n        y1 = (points[3][1] + points[0][1]) / 2;\n        x2 = x1 - labelLineLen;\n        textX = x2 - 5;\n        textAlign = 'right';\n      } else if (labelPosition === 'right') {\n        // Right side\n        x1 = (points[1][0] + points[2][0]) / 2;\n        y1 = (points[1][1] + points[2][1]) / 2;\n        x2 = x1 + labelLineLen;\n        textX = x2 + 5;\n        textAlign = 'left';\n      } else if (labelPosition === 'top') {\n        // Top side\n        x1 = (points[3][0] + points[0][0]) / 2;\n        y1 = (points[3][1] + points[0][1]) / 2;\n        y2 = y1 - labelLineLen;\n        textY = y2 - 5;\n        textAlign = 'center';\n      } else if (labelPosition === 'bottom') {\n        // Bottom side\n        x1 = (points[1][0] + points[2][0]) / 2;\n        y1 = (points[1][1] + points[2][1]) / 2;\n        y2 = y1 + labelLineLen;\n        textY = y2 + 5;\n        textAlign = 'center';\n      } else if (labelPosition === 'rightTop') {\n        // RightTop side\n        x1 = orient === 'horizontal' ? points[3][0] : points[1][0];\n        y1 = orient === 'horizontal' ? points[3][1] : points[1][1];\n\n        if (orient === 'horizontal') {\n          y2 = y1 - labelLineLen;\n          textY = y2 - 5;\n          textAlign = 'center';\n        } else {\n          x2 = x1 + labelLineLen;\n          textX = x2 + 5;\n          textAlign = 'top';\n        }\n      } else if (labelPosition === 'rightBottom') {\n        // RightBottom side\n        x1 = points[2][0];\n        y1 = points[2][1];\n\n        if (orient === 'horizontal') {\n          y2 = y1 + labelLineLen;\n          textY = y2 + 5;\n          textAlign = 'center';\n        } else {\n          x2 = x1 + labelLineLen;\n          textX = x2 + 5;\n          textAlign = 'bottom';\n        }\n      } else if (labelPosition === 'leftTop') {\n        // LeftTop side\n        x1 = points[0][0];\n        y1 = orient === 'horizontal' ? points[0][1] : points[1][1];\n\n        if (orient === 'horizontal') {\n          y2 = y1 - labelLineLen;\n          textY = y2 - 5;\n          textAlign = 'center';\n        } else {\n          x2 = x1 - labelLineLen;\n          textX = x2 - 5;\n          textAlign = 'right';\n        }\n      } else if (labelPosition === 'leftBottom') {\n        // LeftBottom side\n        x1 = orient === 'horizontal' ? points[1][0] : points[3][0];\n        y1 = orient === 'horizontal' ? points[1][1] : points[2][1];\n\n        if (orient === 'horizontal') {\n          y2 = y1 + labelLineLen;\n          textY = y2 + 5;\n          textAlign = 'center';\n        } else {\n          x2 = x1 - labelLineLen;\n          textX = x2 - 5;\n          textAlign = 'right';\n        }\n      } else {\n        // Right side or Bottom side\n        x1 = (points[1][0] + points[2][0]) / 2;\n        y1 = (points[1][1] + points[2][1]) / 2;\n\n        if (orient === 'horizontal') {\n          y2 = y1 + labelLineLen;\n          textY = y2 + 5;\n          textAlign = 'center';\n        } else {\n          x2 = x1 + labelLineLen;\n          textX = x2 + 5;\n          textAlign = 'left';\n        }\n      }\n\n      if (orient === 'horizontal') {\n        x2 = x1;\n        textX = x2;\n      } else {\n        y2 = y1;\n        textY = y2;\n      }\n\n      linePoints = [[x1, y1], [x2, y2]];\n    }\n\n    layout.label = {\n      linePoints: linePoints,\n      x: textX,\n      y: textY,\n      verticalAlign: 'middle',\n      textAlign: textAlign,\n      inside: isLabelInside\n    };\n  });\n}\n\nfunction funnelLayout(ecModel, api) {\n  ecModel.eachSeriesByType('funnel', function (seriesModel) {\n    var data = seriesModel.getData();\n    var valueDim = data.mapDimension('value');\n    var sort = seriesModel.get('sort');\n    var viewRect = getViewRect$3(seriesModel, api);\n    var orient = seriesModel.get('orient');\n    var viewWidth = viewRect.width;\n    var viewHeight = viewRect.height;\n    var indices = getSortedIndices(data, sort);\n    var x = viewRect.x;\n    var y = viewRect.y;\n    var sizeExtent = orient === 'horizontal' ? [parsePercent$1(seriesModel.get('minSize'), viewHeight), parsePercent$1(seriesModel.get('maxSize'), viewHeight)] : [parsePercent$1(seriesModel.get('minSize'), viewWidth), parsePercent$1(seriesModel.get('maxSize'), viewWidth)];\n    var dataExtent = data.getDataExtent(valueDim);\n    var min = seriesModel.get('min');\n    var max = seriesModel.get('max');\n\n    if (min == null) {\n      min = Math.min(dataExtent[0], 0);\n    }\n\n    if (max == null) {\n      max = dataExtent[1];\n    }\n\n    var funnelAlign = seriesModel.get('funnelAlign');\n    var gap = seriesModel.get('gap');\n    var viewSize = orient === 'horizontal' ? viewWidth : viewHeight;\n    var itemSize = (viewSize - gap * (data.count() - 1)) / data.count();\n\n    var getLinePoints = function (idx, offset) {\n      // End point index is data.count() and we assign it 0\n      if (orient === 'horizontal') {\n        var val_1 = data.get(valueDim, idx) || 0;\n        var itemHeight = linearMap(val_1, [min, max], sizeExtent, true);\n        var y0 = void 0;\n\n        switch (funnelAlign) {\n          case 'top':\n            y0 = y;\n            break;\n\n          case 'center':\n            y0 = y + (viewHeight - itemHeight) / 2;\n            break;\n\n          case 'bottom':\n            y0 = y + (viewHeight - itemHeight);\n            break;\n        }\n\n        return [[offset, y0], [offset, y0 + itemHeight]];\n      }\n\n      var val = data.get(valueDim, idx) || 0;\n      var itemWidth = linearMap(val, [min, max], sizeExtent, true);\n      var x0;\n\n      switch (funnelAlign) {\n        case 'left':\n          x0 = x;\n          break;\n\n        case 'center':\n          x0 = x + (viewWidth - itemWidth) / 2;\n          break;\n\n        case 'right':\n          x0 = x + viewWidth - itemWidth;\n          break;\n      }\n\n      return [[x0, offset], [x0 + itemWidth, offset]];\n    };\n\n    if (sort === 'ascending') {\n      // From bottom to top\n      itemSize = -itemSize;\n      gap = -gap;\n\n      if (orient === 'horizontal') {\n        x += viewWidth;\n      } else {\n        y += viewHeight;\n      }\n\n      indices = indices.reverse();\n    }\n\n    for (var i = 0; i < indices.length; i++) {\n      var idx = indices[i];\n      var nextIdx = indices[i + 1];\n      var itemModel = data.getItemModel(idx);\n\n      if (orient === 'horizontal') {\n        var width = itemModel.get(['itemStyle', 'width']);\n\n        if (width == null) {\n          width = itemSize;\n        } else {\n          width = parsePercent$1(width, viewWidth);\n\n          if (sort === 'ascending') {\n            width = -width;\n          }\n        }\n\n        var start = getLinePoints(idx, x);\n        var end = getLinePoints(nextIdx, x + width);\n        x += width + gap;\n        data.setItemLayout(idx, {\n          points: start.concat(end.slice().reverse())\n        });\n      } else {\n        var height = itemModel.get(['itemStyle', 'height']);\n\n        if (height == null) {\n          height = itemSize;\n        } else {\n          height = parsePercent$1(height, viewHeight);\n\n          if (sort === 'ascending') {\n            height = -height;\n          }\n        }\n\n        var start = getLinePoints(idx, y);\n        var end = getLinePoints(nextIdx, y + height);\n        y += height + gap;\n        data.setItemLayout(idx, {\n          points: start.concat(end.slice().reverse())\n        });\n      }\n    }\n\n    labelLayout(data);\n  });\n}\n\nfunction install$f(registers) {\n  registers.registerChartView(FunnelView);\n  registers.registerSeriesModel(FunnelSeriesModel);\n  registers.registerLayout(funnelLayout);\n  registers.registerProcessor(dataFilter('funnel'));\n}\n\nvar DEFAULT_SMOOTH = 0.3;\n\nvar ParallelView =\n/** @class */\nfunction (_super) {\n  __extends(ParallelView, _super);\n\n  function ParallelView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ParallelView.type;\n    _this._dataGroup = new Group();\n    _this._initialized = false;\n    return _this;\n  }\n\n  ParallelView.prototype.init = function () {\n    this.group.add(this._dataGroup);\n  };\n  /**\n   * @override\n   */\n\n\n  ParallelView.prototype.render = function (seriesModel, ecModel, api, payload) {\n    // Clear previously rendered progressive elements.\n    this._progressiveEls = null;\n    var dataGroup = this._dataGroup;\n    var data = seriesModel.getData();\n    var oldData = this._data;\n    var coordSys = seriesModel.coordinateSystem;\n    var dimensions = coordSys.dimensions;\n    var seriesScope = makeSeriesScope$2(seriesModel);\n    data.diff(oldData).add(add).update(update).remove(remove).execute();\n\n    function add(newDataIndex) {\n      var line = addEl(data, dataGroup, newDataIndex, dimensions, coordSys);\n      updateElCommon(line, data, newDataIndex, seriesScope);\n    }\n\n    function update(newDataIndex, oldDataIndex) {\n      var line = oldData.getItemGraphicEl(oldDataIndex);\n      var points = createLinePoints(data, newDataIndex, dimensions, coordSys);\n      data.setItemGraphicEl(newDataIndex, line);\n      updateProps(line, {\n        shape: {\n          points: points\n        }\n      }, seriesModel, newDataIndex);\n      saveOldStyle(line);\n      updateElCommon(line, data, newDataIndex, seriesScope);\n    }\n\n    function remove(oldDataIndex) {\n      var line = oldData.getItemGraphicEl(oldDataIndex);\n      dataGroup.remove(line);\n    } // First create\n\n\n    if (!this._initialized) {\n      this._initialized = true;\n      var clipPath = createGridClipShape(coordSys, seriesModel, function () {\n        // Callback will be invoked immediately if there is no animation\n        setTimeout(function () {\n          dataGroup.removeClipPath();\n        });\n      });\n      dataGroup.setClipPath(clipPath);\n    }\n\n    this._data = data;\n  };\n\n  ParallelView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n    this._initialized = true;\n    this._data = null;\n\n    this._dataGroup.removeAll();\n  };\n\n  ParallelView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) {\n    var data = seriesModel.getData();\n    var coordSys = seriesModel.coordinateSystem;\n    var dimensions = coordSys.dimensions;\n    var seriesScope = makeSeriesScope$2(seriesModel);\n    var progressiveEls = this._progressiveEls = [];\n\n    for (var dataIndex = taskParams.start; dataIndex < taskParams.end; dataIndex++) {\n      var line = addEl(data, this._dataGroup, dataIndex, dimensions, coordSys);\n      line.incremental = true;\n      updateElCommon(line, data, dataIndex, seriesScope);\n      progressiveEls.push(line);\n    }\n  };\n\n  ParallelView.prototype.remove = function () {\n    this._dataGroup && this._dataGroup.removeAll();\n    this._data = null;\n  };\n\n  ParallelView.type = 'parallel';\n  return ParallelView;\n}(ChartView);\n\nfunction createGridClipShape(coordSys, seriesModel, cb) {\n  var parallelModel = coordSys.model;\n  var rect = coordSys.getRect();\n  var rectEl = new Rect({\n    shape: {\n      x: rect.x,\n      y: rect.y,\n      width: rect.width,\n      height: rect.height\n    }\n  });\n  var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height';\n  rectEl.setShape(dim, 0);\n  initProps(rectEl, {\n    shape: {\n      width: rect.width,\n      height: rect.height\n    }\n  }, seriesModel, cb);\n  return rectEl;\n}\n\nfunction createLinePoints(data, dataIndex, dimensions, coordSys) {\n  var points = [];\n\n  for (var i = 0; i < dimensions.length; i++) {\n    var dimName = dimensions[i];\n    var value = data.get(data.mapDimension(dimName), dataIndex);\n\n    if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) {\n      points.push(coordSys.dataToPoint(value, dimName));\n    }\n  }\n\n  return points;\n}\n\nfunction addEl(data, dataGroup, dataIndex, dimensions, coordSys) {\n  var points = createLinePoints(data, dataIndex, dimensions, coordSys);\n  var line = new Polyline({\n    shape: {\n      points: points\n    },\n    // silent: true,\n    z2: 10\n  });\n  dataGroup.add(line);\n  data.setItemGraphicEl(dataIndex, line);\n  return line;\n}\n\nfunction makeSeriesScope$2(seriesModel) {\n  var smooth = seriesModel.get('smooth', true);\n  smooth === true && (smooth = DEFAULT_SMOOTH);\n  smooth = numericToNumber(smooth);\n  eqNaN(smooth) && (smooth = 0);\n  return {\n    smooth: smooth\n  };\n}\n\nfunction updateElCommon(el, data, dataIndex, seriesScope) {\n  el.useStyle(data.getItemVisual(dataIndex, 'style'));\n  el.style.fill = null;\n  el.setShape('smooth', seriesScope.smooth);\n  var itemModel = data.getItemModel(dataIndex);\n  var emphasisModel = itemModel.getModel('emphasis');\n  setStatesStylesFromModel(el, itemModel, 'lineStyle');\n  toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n} // function simpleDiff(oldData, newData, dimensions) {\n//     let oldLen;\n//     if (!oldData\n//         || !oldData.__plProgressive\n//         || (oldLen = oldData.count()) !== newData.count()\n//     ) {\n//         return true;\n//     }\n//     let dimLen = dimensions.length;\n//     for (let i = 0; i < oldLen; i++) {\n//         for (let j = 0; j < dimLen; j++) {\n//             if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) {\n//                 return true;\n//             }\n//         }\n//     }\n//     return false;\n// }\n// FIXME put in common util?\n\n\nfunction isEmptyValue(val, axisType) {\n  return axisType === 'category' ? val == null : val == null || isNaN(val); // axisType === 'value'\n}\n\nvar ParallelSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(ParallelSeriesModel, _super);\n\n  function ParallelSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ParallelSeriesModel.type;\n    _this.visualStyleAccessPath = 'lineStyle';\n    _this.visualDrawType = 'stroke';\n    return _this;\n  }\n\n  ParallelSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesData(null, this, {\n      useEncodeDefaulter: bind(makeDefaultEncode, null, this)\n    });\n  };\n  /**\n   * User can get data raw indices on 'axisAreaSelected' event received.\n   *\n   * @return Raw indices\n   */\n\n\n  ParallelSeriesModel.prototype.getRawIndicesByActiveState = function (activeState) {\n    var coordSys = this.coordinateSystem;\n    var data = this.getData();\n    var indices = [];\n    coordSys.eachActiveState(data, function (theActiveState, dataIndex) {\n      if (activeState === theActiveState) {\n        indices.push(data.getRawIndex(dataIndex));\n      }\n    });\n    return indices;\n  };\n\n  ParallelSeriesModel.type = 'series.parallel';\n  ParallelSeriesModel.dependencies = ['parallel'];\n  ParallelSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    coordinateSystem: 'parallel',\n    parallelIndex: 0,\n    label: {\n      show: false\n    },\n    inactiveOpacity: 0.05,\n    activeOpacity: 1,\n    lineStyle: {\n      width: 1,\n      opacity: 0.45,\n      type: 'solid'\n    },\n    emphasis: {\n      label: {\n        show: false\n      }\n    },\n    progressive: 500,\n    smooth: false,\n    animationEasing: 'linear'\n  };\n  return ParallelSeriesModel;\n}(SeriesModel);\n\nfunction makeDefaultEncode(seriesModel) {\n  // The mapping of parallelAxis dimension to data dimension can\n  // be specified in parallelAxis.option.dim. For example, if\n  // parallelAxis.option.dim is 'dim3', it mapping to the third\n  // dimension of data. But `data.encode` has higher priority.\n  // Moreover, parallelModel.dimension should not be regarded as data\n  // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6'];\n  var parallelModel = seriesModel.ecModel.getComponent('parallel', seriesModel.get('parallelIndex'));\n\n  if (!parallelModel) {\n    return;\n  }\n\n  var encodeDefine = {};\n  each(parallelModel.dimensions, function (axisDim) {\n    var dataDimIndex = convertDimNameToNumber(axisDim);\n    encodeDefine[axisDim] = dataDimIndex;\n  });\n  return encodeDefine;\n}\n\nfunction convertDimNameToNumber(dimName) {\n  return +dimName.replace('dim', '');\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar opacityAccessPath$1 = ['lineStyle', 'opacity'];\nvar parallelVisual = {\n  seriesType: 'parallel',\n  reset: function (seriesModel, ecModel) {\n    var coordSys = seriesModel.coordinateSystem;\n    var opacityMap = {\n      normal: seriesModel.get(['lineStyle', 'opacity']),\n      active: seriesModel.get('activeOpacity'),\n      inactive: seriesModel.get('inactiveOpacity')\n    };\n    return {\n      progress: function (params, data) {\n        coordSys.eachActiveState(data, function (activeState, dataIndex) {\n          var opacity = opacityMap[activeState];\n\n          if (activeState === 'normal' && data.hasItemOption) {\n            var itemOpacity = data.getItemModel(dataIndex).get(opacityAccessPath$1, true);\n            itemOpacity != null && (opacity = itemOpacity);\n          }\n\n          var existsStyle = data.ensureUniqueItemVisual(dataIndex, 'style');\n          existsStyle.opacity = opacity;\n        }, params.start, params.end);\n      }\n    };\n  }\n};\n\nfunction parallelPreprocessor(option) {\n  createParallelIfNeeded(option);\n  mergeAxisOptionFromParallel(option);\n}\n/**\n * Create a parallel coordinate if not exists.\n * @inner\n */\n\nfunction createParallelIfNeeded(option) {\n  if (option.parallel) {\n    return;\n  }\n\n  var hasParallelSeries = false;\n  each(option.series, function (seriesOpt) {\n    if (seriesOpt && seriesOpt.type === 'parallel') {\n      hasParallelSeries = true;\n    }\n  });\n\n  if (hasParallelSeries) {\n    option.parallel = [{}];\n  }\n}\n/**\n * Merge aixs definition from parallel option (if exists) to axis option.\n * @inner\n */\n\n\nfunction mergeAxisOptionFromParallel(option) {\n  var axes = normalizeToArray(option.parallelAxis);\n  each(axes, function (axisOption) {\n    if (!isObject(axisOption)) {\n      return;\n    }\n\n    var parallelIndex = axisOption.parallelIndex || 0;\n    var parallelOption = normalizeToArray(option.parallel)[parallelIndex];\n\n    if (parallelOption && parallelOption.parallelAxisDefault) {\n      merge(axisOption, parallelOption.parallelAxisDefault, false);\n    }\n  });\n}\n\nvar CLICK_THRESHOLD = 5; // > 4\n\nvar ParallelView$1 =\n/** @class */\nfunction (_super) {\n  __extends(ParallelView, _super);\n\n  function ParallelView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ParallelView.type;\n    return _this;\n  }\n\n  ParallelView.prototype.render = function (parallelModel, ecModel, api) {\n    this._model = parallelModel;\n    this._api = api;\n\n    if (!this._handlers) {\n      this._handlers = {};\n      each(handlers, function (handler, eventName) {\n        api.getZr().on(eventName, this._handlers[eventName] = bind(handler, this));\n      }, this);\n    }\n\n    createOrUpdate(this, '_throttledDispatchExpand', parallelModel.get('axisExpandRate'), 'fixRate');\n  };\n\n  ParallelView.prototype.dispose = function (ecModel, api) {\n    clear(this, '_throttledDispatchExpand');\n    each(this._handlers, function (handler, eventName) {\n      api.getZr().off(eventName, handler);\n    });\n    this._handlers = null;\n  };\n  /**\n   * @internal\n   * @param {Object} [opt] If null, cancel the last action triggering for debounce.\n   */\n\n\n  ParallelView.prototype._throttledDispatchExpand = function (opt) {\n    this._dispatchExpand(opt);\n  };\n  /**\n   * @internal\n   */\n\n\n  ParallelView.prototype._dispatchExpand = function (opt) {\n    opt && this._api.dispatchAction(extend({\n      type: 'parallelAxisExpand'\n    }, opt));\n  };\n\n  ParallelView.type = 'parallel';\n  return ParallelView;\n}(ComponentView);\n\nvar handlers = {\n  mousedown: function (e) {\n    if (checkTrigger(this, 'click')) {\n      this._mouseDownPoint = [e.offsetX, e.offsetY];\n    }\n  },\n  mouseup: function (e) {\n    var mouseDownPoint = this._mouseDownPoint;\n\n    if (checkTrigger(this, 'click') && mouseDownPoint) {\n      var point = [e.offsetX, e.offsetY];\n      var dist = Math.pow(mouseDownPoint[0] - point[0], 2) + Math.pow(mouseDownPoint[1] - point[1], 2);\n\n      if (dist > CLICK_THRESHOLD) {\n        return;\n      }\n\n      var result = this._model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX, e.offsetY]);\n\n      result.behavior !== 'none' && this._dispatchExpand({\n        axisExpandWindow: result.axisExpandWindow\n      });\n    }\n\n    this._mouseDownPoint = null;\n  },\n  mousemove: function (e) {\n    // Should do nothing when brushing.\n    if (this._mouseDownPoint || !checkTrigger(this, 'mousemove')) {\n      return;\n    }\n\n    var model = this._model;\n    var result = model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX, e.offsetY]);\n    var behavior = result.behavior;\n    behavior === 'jump' && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce'));\n\n    this._throttledDispatchExpand(behavior === 'none' ? null // Cancel the last trigger, in case that mouse slide out of the area quickly.\n    : {\n      axisExpandWindow: result.axisExpandWindow,\n      // Jumping uses animation, and sliding suppresses animation.\n      animation: behavior === 'jump' ? null : {\n        duration: 0 // Disable animation.\n\n      }\n    });\n  }\n};\n\nfunction checkTrigger(view, triggerOn) {\n  var model = view._model;\n  return model.get('axisExpandable') && model.get('axisExpandTriggerOn') === triggerOn;\n}\n\nvar ParallelModel =\n/** @class */\nfunction (_super) {\n  __extends(ParallelModel, _super);\n\n  function ParallelModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ParallelModel.type;\n    return _this;\n  }\n\n  ParallelModel.prototype.init = function () {\n    _super.prototype.init.apply(this, arguments);\n\n    this.mergeOption({});\n  };\n\n  ParallelModel.prototype.mergeOption = function (newOption) {\n    var thisOption = this.option;\n    newOption && merge(thisOption, newOption, true);\n\n    this._initDimensions();\n  };\n  /**\n   * Whether series or axis is in this coordinate system.\n   */\n\n\n  ParallelModel.prototype.contains = function (model, ecModel) {\n    var parallelIndex = model.get('parallelIndex');\n    return parallelIndex != null && ecModel.getComponent('parallel', parallelIndex) === this;\n  };\n\n  ParallelModel.prototype.setAxisExpand = function (opt) {\n    each(['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth', 'axisExpandWindow'], function (name) {\n      if (opt.hasOwnProperty(name)) {\n        // @ts-ignore FIXME: why \"never\" inferred in this.option[name]?\n        this.option[name] = opt[name];\n      }\n    }, this);\n  };\n\n  ParallelModel.prototype._initDimensions = function () {\n    var dimensions = this.dimensions = [];\n    var parallelAxisIndex = this.parallelAxisIndex = [];\n    var axisModels = filter(this.ecModel.queryComponents({\n      mainType: 'parallelAxis'\n    }), function (axisModel) {\n      // Can not use this.contains here, because\n      // initialization has not been completed yet.\n      return (axisModel.get('parallelIndex') || 0) === this.componentIndex;\n    }, this);\n    each(axisModels, function (axisModel) {\n      dimensions.push('dim' + axisModel.get('dim'));\n      parallelAxisIndex.push(axisModel.componentIndex);\n    });\n  };\n\n  ParallelModel.type = 'parallel';\n  ParallelModel.dependencies = ['parallelAxis'];\n  ParallelModel.layoutMode = 'box';\n  ParallelModel.defaultOption = {\n    // zlevel: 0,\n    z: 0,\n    left: 80,\n    top: 60,\n    right: 80,\n    bottom: 60,\n    // width: {totalWidth} - left - right,\n    // height: {totalHeight} - top - bottom,\n    layout: 'horizontal',\n    // FIXME\n    // naming?\n    axisExpandable: false,\n    axisExpandCenter: null,\n    axisExpandCount: 0,\n    axisExpandWidth: 50,\n    axisExpandRate: 17,\n    axisExpandDebounce: 50,\n    // [out, in, jumpTarget]. In percentage. If use [null, 0.05], null means full.\n    // Do not doc to user until necessary.\n    axisExpandSlideTriggerArea: [-0.15, 0.05, 0.4],\n    axisExpandTriggerOn: 'click',\n    parallelAxisDefault: null\n  };\n  return ParallelModel;\n}(ComponentModel);\n\nvar ParallelAxis =\n/** @class */\nfunction (_super) {\n  __extends(ParallelAxis, _super);\n\n  function ParallelAxis(dim, scale, coordExtent, axisType, axisIndex) {\n    var _this = _super.call(this, dim, scale, coordExtent) || this;\n\n    _this.type = axisType || 'value';\n    _this.axisIndex = axisIndex;\n    return _this;\n  }\n\n  ParallelAxis.prototype.isHorizontal = function () {\n    return this.coordinateSystem.getModel().get('layout') !== 'horizontal';\n  };\n\n  return ParallelAxis;\n}(Axis);\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n/**\n * Calculate slider move result.\n * Usage:\n * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as\n * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`.\n * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`.\n *\n * @param delta Move length.\n * @param handleEnds handleEnds[0] can be bigger then handleEnds[1].\n *              handleEnds will be modified in this method.\n * @param extent handleEnds is restricted by extent.\n *              extent[0] should less or equals than extent[1].\n * @param handleIndex Can be 'all', means that both move the two handleEnds.\n * @param minSpan The range of dataZoom can not be smaller than that.\n *              If not set, handle0 and cross handle1. If set as a non-negative\n *              number (including `0`), handles will push each other when reaching\n *              the minSpan.\n * @param maxSpan The range of dataZoom can not be larger than that.\n * @return The input handleEnds.\n */\nfunction sliderMove(delta, handleEnds, extent, handleIndex, minSpan, maxSpan) {\n  delta = delta || 0;\n  var extentSpan = extent[1] - extent[0]; // Notice maxSpan and minSpan can be null/undefined.\n\n  if (minSpan != null) {\n    minSpan = restrict(minSpan, [0, extentSpan]);\n  }\n\n  if (maxSpan != null) {\n    maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0);\n  }\n\n  if (handleIndex === 'all') {\n    var handleSpan = Math.abs(handleEnds[1] - handleEnds[0]);\n    handleSpan = restrict(handleSpan, [0, extentSpan]);\n    minSpan = maxSpan = restrict(handleSpan, [minSpan, maxSpan]);\n    handleIndex = 0;\n  }\n\n  handleEnds[0] = restrict(handleEnds[0], extent);\n  handleEnds[1] = restrict(handleEnds[1], extent);\n  var originalDistSign = getSpanSign(handleEnds, handleIndex);\n  handleEnds[handleIndex] += delta; // Restrict in extent.\n\n  var extentMinSpan = minSpan || 0;\n  var realExtent = extent.slice();\n  originalDistSign.sign < 0 ? realExtent[0] += extentMinSpan : realExtent[1] -= extentMinSpan;\n  handleEnds[handleIndex] = restrict(handleEnds[handleIndex], realExtent); // Expand span.\n\n  var currDistSign;\n  currDistSign = getSpanSign(handleEnds, handleIndex);\n\n  if (minSpan != null && (currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan)) {\n    // If minSpan exists, 'cross' is forbidden.\n    handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan;\n  } // Shrink span.\n\n\n  currDistSign = getSpanSign(handleEnds, handleIndex);\n\n  if (maxSpan != null && currDistSign.span > maxSpan) {\n    handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan;\n  }\n\n  return handleEnds;\n}\n\nfunction getSpanSign(handleEnds, handleIndex) {\n  var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex]; // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0]\n  // is at left of handleEnds[1] for non-cross case.\n\n  return {\n    span: Math.abs(dist),\n    sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1\n  };\n}\n\nfunction restrict(value, extend) {\n  return Math.min(extend[1] != null ? extend[1] : Infinity, Math.max(extend[0] != null ? extend[0] : -Infinity, value));\n}\n\nvar each$5 = each;\nvar mathMin$8 = Math.min;\nvar mathMax$8 = Math.max;\nvar mathFloor$1 = Math.floor;\nvar mathCeil$1 = Math.ceil;\nvar round$3 = round;\nvar PI$7 = Math.PI;\n\nvar Parallel =\n/** @class */\nfunction () {\n  function Parallel(parallelModel, ecModel, api) {\n    this.type = 'parallel';\n    /**\n     * key: dimension\n     */\n\n    this._axesMap = createHashMap();\n    /**\n     * key: dimension\n     * value: {position: [], rotation, }\n     */\n\n    this._axesLayout = {};\n    this.dimensions = parallelModel.dimensions;\n    this._model = parallelModel;\n\n    this._init(parallelModel, ecModel, api);\n  }\n\n  Parallel.prototype._init = function (parallelModel, ecModel, api) {\n    var dimensions = parallelModel.dimensions;\n    var parallelAxisIndex = parallelModel.parallelAxisIndex;\n    each$5(dimensions, function (dim, idx) {\n      var axisIndex = parallelAxisIndex[idx];\n      var axisModel = ecModel.getComponent('parallelAxis', axisIndex);\n\n      var axis = this._axesMap.set(dim, new ParallelAxis(dim, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisIndex));\n\n      var isCategory = axis.type === 'category';\n      axis.onBand = isCategory && axisModel.get('boundaryGap');\n      axis.inverse = axisModel.get('inverse'); // Injection\n\n      axisModel.axis = axis;\n      axis.model = axisModel;\n      axis.coordinateSystem = axisModel.coordinateSystem = this;\n    }, this);\n  };\n  /**\n   * Update axis scale after data processed\n   */\n\n\n  Parallel.prototype.update = function (ecModel, api) {\n    this._updateAxesFromSeries(this._model, ecModel);\n  };\n\n  Parallel.prototype.containPoint = function (point) {\n    var layoutInfo = this._makeLayoutInfo();\n\n    var axisBase = layoutInfo.axisBase;\n    var layoutBase = layoutInfo.layoutBase;\n    var pixelDimIndex = layoutInfo.pixelDimIndex;\n    var pAxis = point[1 - pixelDimIndex];\n    var pLayout = point[pixelDimIndex];\n    return pAxis >= axisBase && pAxis <= axisBase + layoutInfo.axisLength && pLayout >= layoutBase && pLayout <= layoutBase + layoutInfo.layoutLength;\n  };\n\n  Parallel.prototype.getModel = function () {\n    return this._model;\n  };\n  /**\n   * Update properties from series\n   */\n\n\n  Parallel.prototype._updateAxesFromSeries = function (parallelModel, ecModel) {\n    ecModel.eachSeries(function (seriesModel) {\n      if (!parallelModel.contains(seriesModel, ecModel)) {\n        return;\n      }\n\n      var data = seriesModel.getData();\n      each$5(this.dimensions, function (dim) {\n        var axis = this._axesMap.get(dim);\n\n        axis.scale.unionExtentFromData(data, data.mapDimension(dim));\n        niceScaleExtent(axis.scale, axis.model);\n      }, this);\n    }, this);\n  };\n  /**\n   * Resize the parallel coordinate system.\n   */\n\n\n  Parallel.prototype.resize = function (parallelModel, api) {\n    this._rect = getLayoutRect(parallelModel.getBoxLayoutParams(), {\n      width: api.getWidth(),\n      height: api.getHeight()\n    });\n\n    this._layoutAxes();\n  };\n\n  Parallel.prototype.getRect = function () {\n    return this._rect;\n  };\n\n  Parallel.prototype._makeLayoutInfo = function () {\n    var parallelModel = this._model;\n    var rect = this._rect;\n    var xy = ['x', 'y'];\n    var wh = ['width', 'height'];\n    var layout = parallelModel.get('layout');\n    var pixelDimIndex = layout === 'horizontal' ? 0 : 1;\n    var layoutLength = rect[wh[pixelDimIndex]];\n    var layoutExtent = [0, layoutLength];\n    var axisCount = this.dimensions.length;\n    var axisExpandWidth = restrict$1(parallelModel.get('axisExpandWidth'), layoutExtent);\n    var axisExpandCount = restrict$1(parallelModel.get('axisExpandCount') || 0, [0, axisCount]);\n    var axisExpandable = parallelModel.get('axisExpandable') && axisCount > 3 && axisCount > axisExpandCount && axisExpandCount > 1 && axisExpandWidth > 0 && layoutLength > 0; // `axisExpandWindow` is According to the coordinates of [0, axisExpandLength],\n    // for sake of consider the case that axisCollapseWidth is 0 (when screen is narrow),\n    // where collapsed axes should be overlapped.\n\n    var axisExpandWindow = parallelModel.get('axisExpandWindow');\n    var winSize;\n\n    if (!axisExpandWindow) {\n      winSize = restrict$1(axisExpandWidth * (axisExpandCount - 1), layoutExtent);\n      var axisExpandCenter = parallelModel.get('axisExpandCenter') || mathFloor$1(axisCount / 2);\n      axisExpandWindow = [axisExpandWidth * axisExpandCenter - winSize / 2];\n      axisExpandWindow[1] = axisExpandWindow[0] + winSize;\n    } else {\n      winSize = restrict$1(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent);\n      axisExpandWindow[1] = axisExpandWindow[0] + winSize;\n    }\n\n    var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount); // Avoid axisCollapseWidth is too small.\n\n    axisCollapseWidth < 3 && (axisCollapseWidth = 0); // Find the first and last indices > ewin[0] and < ewin[1].\n\n    var winInnerIndices = [mathFloor$1(round$3(axisExpandWindow[0] / axisExpandWidth, 1)) + 1, mathCeil$1(round$3(axisExpandWindow[1] / axisExpandWidth, 1)) - 1]; // Pos in ec coordinates.\n\n    var axisExpandWindow0Pos = axisCollapseWidth / axisExpandWidth * axisExpandWindow[0];\n    return {\n      layout: layout,\n      pixelDimIndex: pixelDimIndex,\n      layoutBase: rect[xy[pixelDimIndex]],\n      layoutLength: layoutLength,\n      axisBase: rect[xy[1 - pixelDimIndex]],\n      axisLength: rect[wh[1 - pixelDimIndex]],\n      axisExpandable: axisExpandable,\n      axisExpandWidth: axisExpandWidth,\n      axisCollapseWidth: axisCollapseWidth,\n      axisExpandWindow: axisExpandWindow,\n      axisCount: axisCount,\n      winInnerIndices: winInnerIndices,\n      axisExpandWindow0Pos: axisExpandWindow0Pos\n    };\n  };\n\n  Parallel.prototype._layoutAxes = function () {\n    var rect = this._rect;\n    var axes = this._axesMap;\n    var dimensions = this.dimensions;\n\n    var layoutInfo = this._makeLayoutInfo();\n\n    var layout = layoutInfo.layout;\n    axes.each(function (axis) {\n      var axisExtent = [0, layoutInfo.axisLength];\n      var idx = axis.inverse ? 1 : 0;\n      axis.setExtent(axisExtent[idx], axisExtent[1 - idx]);\n    });\n    each$5(dimensions, function (dim, idx) {\n      var posInfo = (layoutInfo.axisExpandable ? layoutAxisWithExpand : layoutAxisWithoutExpand)(idx, layoutInfo);\n      var positionTable = {\n        horizontal: {\n          x: posInfo.position,\n          y: layoutInfo.axisLength\n        },\n        vertical: {\n          x: 0,\n          y: posInfo.position\n        }\n      };\n      var rotationTable = {\n        horizontal: PI$7 / 2,\n        vertical: 0\n      };\n      var position = [positionTable[layout].x + rect.x, positionTable[layout].y + rect.y];\n      var rotation = rotationTable[layout];\n      var transform = create$1();\n      rotate(transform, transform, rotation);\n      translate(transform, transform, position); // TODO\n      // tick layout info\n      // TODO\n      // update dimensions info based on axis order.\n\n      this._axesLayout[dim] = {\n        position: position,\n        rotation: rotation,\n        transform: transform,\n        axisNameAvailableWidth: posInfo.axisNameAvailableWidth,\n        axisLabelShow: posInfo.axisLabelShow,\n        nameTruncateMaxWidth: posInfo.nameTruncateMaxWidth,\n        tickDirection: 1,\n        labelDirection: 1\n      };\n    }, this);\n  };\n  /**\n   * Get axis by dim.\n   */\n\n\n  Parallel.prototype.getAxis = function (dim) {\n    return this._axesMap.get(dim);\n  };\n  /**\n   * Convert a dim value of a single item of series data to Point.\n   */\n\n\n  Parallel.prototype.dataToPoint = function (value, dim) {\n    return this.axisCoordToPoint(this._axesMap.get(dim).dataToCoord(value), dim);\n  };\n  /**\n   * Travel data for one time, get activeState of each data item.\n   * @param start the start dataIndex that travel from.\n   * @param end the next dataIndex of the last dataIndex will be travel.\n   */\n\n\n  Parallel.prototype.eachActiveState = function (data, callback, start, end) {\n    start == null && (start = 0);\n    end == null && (end = data.count());\n    var axesMap = this._axesMap;\n    var dimensions = this.dimensions;\n    var dataDimensions = [];\n    var axisModels = [];\n    each(dimensions, function (axisDim) {\n      dataDimensions.push(data.mapDimension(axisDim));\n      axisModels.push(axesMap.get(axisDim).model);\n    });\n    var hasActiveSet = this.hasAxisBrushed();\n\n    for (var dataIndex = start; dataIndex < end; dataIndex++) {\n      var activeState = void 0;\n\n      if (!hasActiveSet) {\n        activeState = 'normal';\n      } else {\n        activeState = 'active';\n        var values = data.getValues(dataDimensions, dataIndex);\n\n        for (var j = 0, lenj = dimensions.length; j < lenj; j++) {\n          var state = axisModels[j].getActiveState(values[j]);\n\n          if (state === 'inactive') {\n            activeState = 'inactive';\n            break;\n          }\n        }\n      }\n\n      callback(activeState, dataIndex);\n    }\n  };\n  /**\n   * Whether has any activeSet.\n   */\n\n\n  Parallel.prototype.hasAxisBrushed = function () {\n    var dimensions = this.dimensions;\n    var axesMap = this._axesMap;\n    var hasActiveSet = false;\n\n    for (var j = 0, lenj = dimensions.length; j < lenj; j++) {\n      if (axesMap.get(dimensions[j]).model.getActiveState() !== 'normal') {\n        hasActiveSet = true;\n      }\n    }\n\n    return hasActiveSet;\n  };\n  /**\n   * Convert coords of each axis to Point.\n   *  Return point. For example: [10, 20]\n   */\n\n\n  Parallel.prototype.axisCoordToPoint = function (coord, dim) {\n    var axisLayout = this._axesLayout[dim];\n    return applyTransform$1([coord, 0], axisLayout.transform);\n  };\n  /**\n   * Get axis layout.\n   */\n\n\n  Parallel.prototype.getAxisLayout = function (dim) {\n    return clone(this._axesLayout[dim]);\n  };\n  /**\n   * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}.\n   */\n\n\n  Parallel.prototype.getSlidedAxisExpandWindow = function (point) {\n    var layoutInfo = this._makeLayoutInfo();\n\n    var pixelDimIndex = layoutInfo.pixelDimIndex;\n    var axisExpandWindow = layoutInfo.axisExpandWindow.slice();\n    var winSize = axisExpandWindow[1] - axisExpandWindow[0];\n    var extent = [0, layoutInfo.axisExpandWidth * (layoutInfo.axisCount - 1)]; // Out of the area of coordinate system.\n\n    if (!this.containPoint(point)) {\n      return {\n        behavior: 'none',\n        axisExpandWindow: axisExpandWindow\n      };\n    } // Convert the point from global to expand coordinates.\n\n\n    var pointCoord = point[pixelDimIndex] - layoutInfo.layoutBase - layoutInfo.axisExpandWindow0Pos; // For dragging operation convenience, the window should not be\n    // slided when mouse is the center area of the window.\n\n    var delta;\n    var behavior = 'slide';\n    var axisCollapseWidth = layoutInfo.axisCollapseWidth;\n\n    var triggerArea = this._model.get('axisExpandSlideTriggerArea'); // But consider touch device, jump is necessary.\n\n\n    var useJump = triggerArea[0] != null;\n\n    if (axisCollapseWidth) {\n      if (useJump && axisCollapseWidth && pointCoord < winSize * triggerArea[0]) {\n        behavior = 'jump';\n        delta = pointCoord - winSize * triggerArea[2];\n      } else if (useJump && axisCollapseWidth && pointCoord > winSize * (1 - triggerArea[0])) {\n        behavior = 'jump';\n        delta = pointCoord - winSize * (1 - triggerArea[2]);\n      } else {\n        (delta = pointCoord - winSize * triggerArea[1]) >= 0 && (delta = pointCoord - winSize * (1 - triggerArea[1])) <= 0 && (delta = 0);\n      }\n\n      delta *= layoutInfo.axisExpandWidth / axisCollapseWidth;\n      delta ? sliderMove(delta, axisExpandWindow, extent, 'all') // Avoid nonsense triger on mousemove.\n      : behavior = 'none';\n    } // When screen is too narrow, make it visible and slidable, although it is hard to interact.\n    else {\n        var winSize2 = axisExpandWindow[1] - axisExpandWindow[0];\n        var pos = extent[1] * pointCoord / winSize2;\n        axisExpandWindow = [mathMax$8(0, pos - winSize2 / 2)];\n        axisExpandWindow[1] = mathMin$8(extent[1], axisExpandWindow[0] + winSize2);\n        axisExpandWindow[0] = axisExpandWindow[1] - winSize2;\n      }\n\n    return {\n      axisExpandWindow: axisExpandWindow,\n      behavior: behavior\n    };\n  };\n\n  return Parallel;\n}();\n\nfunction restrict$1(len, extent) {\n  return mathMin$8(mathMax$8(len, extent[0]), extent[1]);\n}\n\nfunction layoutAxisWithoutExpand(axisIndex, layoutInfo) {\n  var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1);\n  return {\n    position: step * axisIndex,\n    axisNameAvailableWidth: step,\n    axisLabelShow: true\n  };\n}\n\nfunction layoutAxisWithExpand(axisIndex, layoutInfo) {\n  var layoutLength = layoutInfo.layoutLength;\n  var axisExpandWidth = layoutInfo.axisExpandWidth;\n  var axisCount = layoutInfo.axisCount;\n  var axisCollapseWidth = layoutInfo.axisCollapseWidth;\n  var winInnerIndices = layoutInfo.winInnerIndices;\n  var position;\n  var axisNameAvailableWidth = axisCollapseWidth;\n  var axisLabelShow = false;\n  var nameTruncateMaxWidth;\n\n  if (axisIndex < winInnerIndices[0]) {\n    position = axisIndex * axisCollapseWidth;\n    nameTruncateMaxWidth = axisCollapseWidth;\n  } else if (axisIndex <= winInnerIndices[1]) {\n    position = layoutInfo.axisExpandWindow0Pos + axisIndex * axisExpandWidth - layoutInfo.axisExpandWindow[0];\n    axisNameAvailableWidth = axisExpandWidth;\n    axisLabelShow = true;\n  } else {\n    position = layoutLength - (axisCount - 1 - axisIndex) * axisCollapseWidth;\n    nameTruncateMaxWidth = axisCollapseWidth;\n  }\n\n  return {\n    position: position,\n    axisNameAvailableWidth: axisNameAvailableWidth,\n    axisLabelShow: axisLabelShow,\n    nameTruncateMaxWidth: nameTruncateMaxWidth\n  };\n}\n\nfunction createParallelCoordSys(ecModel, api) {\n  var coordSysList = [];\n  ecModel.eachComponent('parallel', function (parallelModel, idx) {\n    var coordSys = new Parallel(parallelModel, ecModel, api);\n    coordSys.name = 'parallel_' + idx;\n    coordSys.resize(parallelModel, api);\n    parallelModel.coordinateSystem = coordSys;\n    coordSys.model = parallelModel;\n    coordSysList.push(coordSys);\n  }); // Inject the coordinateSystems into seriesModel\n\n  ecModel.eachSeries(function (seriesModel) {\n    if (seriesModel.get('coordinateSystem') === 'parallel') {\n      var parallelModel = seriesModel.getReferringComponents('parallel', SINGLE_REFERRING).models[0];\n      seriesModel.coordinateSystem = parallelModel.coordinateSystem;\n    }\n  });\n  return coordSysList;\n}\n\nvar parallelCoordSysCreator = {\n  create: createParallelCoordSys\n};\n\nvar ParallelAxisModel =\n/** @class */\nfunction (_super) {\n  __extends(ParallelAxisModel, _super);\n\n  function ParallelAxisModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ParallelAxisModel.type;\n    /**\n     * @readOnly\n     */\n\n    _this.activeIntervals = [];\n    return _this;\n  }\n\n  ParallelAxisModel.prototype.getAreaSelectStyle = function () {\n    return makeStyleMapper([['fill', 'color'], ['lineWidth', 'borderWidth'], ['stroke', 'borderColor'], ['width', 'width'], ['opacity', 'opacity'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ])(this.getModel('areaSelectStyle'));\n  };\n  /**\n   * The code of this feature is put on AxisModel but not ParallelAxis,\n   * because axisModel can be alive after echarts updating but instance of\n   * ParallelAxis having been disposed. this._activeInterval should be kept\n   * when action dispatched (i.e. legend click).\n   *\n   * @param intervals `interval.length === 0` means set all active.\n   */\n\n\n  ParallelAxisModel.prototype.setActiveIntervals = function (intervals) {\n    var activeIntervals = this.activeIntervals = clone(intervals); // Normalize\n\n    if (activeIntervals) {\n      for (var i = activeIntervals.length - 1; i >= 0; i--) {\n        asc(activeIntervals[i]);\n      }\n    }\n  };\n  /**\n   * @param value When only attempting detect whether 'no activeIntervals set',\n   *        `value` is not needed to be input.\n   */\n\n\n  ParallelAxisModel.prototype.getActiveState = function (value) {\n    var activeIntervals = this.activeIntervals;\n\n    if (!activeIntervals.length) {\n      return 'normal';\n    }\n\n    if (value == null || isNaN(+value)) {\n      return 'inactive';\n    } // Simple optimization\n\n\n    if (activeIntervals.length === 1) {\n      var interval = activeIntervals[0];\n\n      if (interval[0] <= value && value <= interval[1]) {\n        return 'active';\n      }\n    } else {\n      for (var i = 0, len = activeIntervals.length; i < len; i++) {\n        if (activeIntervals[i][0] <= value && value <= activeIntervals[i][1]) {\n          return 'active';\n        }\n      }\n    }\n\n    return 'inactive';\n  };\n\n  return ParallelAxisModel;\n}(ComponentModel);\n\nmixin(ParallelAxisModel, AxisModelCommonMixin);\n\nvar BRUSH_PANEL_GLOBAL = true;\nvar mathMin$9 = Math.min;\nvar mathMax$9 = Math.max;\nvar mathPow$2 = Math.pow;\nvar COVER_Z = 10000;\nvar UNSELECT_THRESHOLD = 6;\nvar MIN_RESIZE_LINE_WIDTH = 6;\nvar MUTEX_RESOURCE_KEY = 'globalPan';\nvar DIRECTION_MAP = {\n  w: [0, 0],\n  e: [0, 1],\n  n: [1, 0],\n  s: [1, 1]\n};\nvar CURSOR_MAP = {\n  w: 'ew',\n  e: 'ew',\n  n: 'ns',\n  s: 'ns',\n  ne: 'nesw',\n  sw: 'nesw',\n  nw: 'nwse',\n  se: 'nwse'\n};\nvar DEFAULT_BRUSH_OPT = {\n  brushStyle: {\n    lineWidth: 2,\n    stroke: 'rgba(210,219,238,0.3)',\n    fill: '#D2DBEE'\n  },\n  transformable: true,\n  brushMode: 'single',\n  removeOnClick: false\n};\nvar baseUID = 0;\n/**\n * params:\n *     areas: Array.<Array>, coord relates to container group,\n *                             If no container specified, to global.\n *     opt {\n *         isEnd: boolean,\n *         removeOnClick: boolean\n *     }\n */\n\nvar BrushController =\n/** @class */\nfunction (_super) {\n  __extends(BrushController, _super);\n\n  function BrushController(zr) {\n    var _this = _super.call(this) || this;\n    /**\n     * @internal\n     */\n\n\n    _this._track = [];\n    /**\n     * @internal\n     */\n\n    _this._covers = [];\n    _this._handlers = {};\n\n    if (\"development\" !== 'production') {\n      assert(zr);\n    }\n\n    _this._zr = zr;\n    _this.group = new Group();\n    _this._uid = 'brushController_' + baseUID++;\n    each(pointerHandlers, function (handler, eventName) {\n      this._handlers[eventName] = bind(handler, this);\n    }, _this);\n    return _this;\n  }\n  /**\n   * If set to `false`, select disabled.\n   */\n\n\n  BrushController.prototype.enableBrush = function (brushOption) {\n    if (\"development\" !== 'production') {\n      assert(this._mounted);\n    }\n\n    this._brushType && this._doDisableBrush();\n    brushOption.brushType && this._doEnableBrush(brushOption);\n    return this;\n  };\n\n  BrushController.prototype._doEnableBrush = function (brushOption) {\n    var zr = this._zr; // Consider roam, which takes globalPan too.\n\n    if (!this._enableGlobalPan) {\n      take(zr, MUTEX_RESOURCE_KEY, this._uid);\n    }\n\n    each(this._handlers, function (handler, eventName) {\n      zr.on(eventName, handler);\n    });\n    this._brushType = brushOption.brushType;\n    this._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true);\n  };\n\n  BrushController.prototype._doDisableBrush = function () {\n    var zr = this._zr;\n    release(zr, MUTEX_RESOURCE_KEY, this._uid);\n    each(this._handlers, function (handler, eventName) {\n      zr.off(eventName, handler);\n    });\n    this._brushType = this._brushOption = null;\n  };\n  /**\n   * @param panelOpts If not pass, it is global brush.\n   */\n\n\n  BrushController.prototype.setPanels = function (panelOpts) {\n    if (panelOpts && panelOpts.length) {\n      var panels_1 = this._panels = {};\n      each(panelOpts, function (panelOpts) {\n        panels_1[panelOpts.panelId] = clone(panelOpts);\n      });\n    } else {\n      this._panels = null;\n    }\n\n    return this;\n  };\n\n  BrushController.prototype.mount = function (opt) {\n    opt = opt || {};\n\n    if (\"development\" !== 'production') {\n      this._mounted = true; // should be at first.\n    }\n\n    this._enableGlobalPan = opt.enableGlobalPan;\n    var thisGroup = this.group;\n\n    this._zr.add(thisGroup);\n\n    thisGroup.attr({\n      x: opt.x || 0,\n      y: opt.y || 0,\n      rotation: opt.rotation || 0,\n      scaleX: opt.scaleX || 1,\n      scaleY: opt.scaleY || 1\n    });\n    this._transform = thisGroup.getLocalTransform();\n    return this;\n  }; // eachCover(cb, context): void {\n  //     each(this._covers, cb, context);\n  // }\n\n  /**\n   * Update covers.\n   * @param coverConfigList\n   *        If coverConfigList is null/undefined, all covers removed.\n   */\n\n\n  BrushController.prototype.updateCovers = function (coverConfigList) {\n    if (\"development\" !== 'production') {\n      assert(this._mounted);\n    }\n\n    coverConfigList = map(coverConfigList, function (coverConfig) {\n      return merge(clone(DEFAULT_BRUSH_OPT), coverConfig, true);\n    });\n    var tmpIdPrefix = '\\0-brush-index-';\n    var oldCovers = this._covers;\n    var newCovers = this._covers = [];\n    var controller = this;\n    var creatingCover = this._creatingCover;\n    new DataDiffer(oldCovers, coverConfigList, oldGetKey, getKey).add(addOrUpdate).update(addOrUpdate).remove(remove).execute();\n    return this;\n\n    function getKey(brushOption, index) {\n      return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + '-' + brushOption.brushType;\n    }\n\n    function oldGetKey(cover, index) {\n      return getKey(cover.__brushOption, index);\n    }\n\n    function addOrUpdate(newIndex, oldIndex) {\n      var newBrushInternal = coverConfigList[newIndex]; // Consider setOption in event listener of brushSelect,\n      // where updating cover when creating should be forbiden.\n\n      if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {\n        newCovers[newIndex] = oldCovers[oldIndex];\n      } else {\n        var cover = newCovers[newIndex] = oldIndex != null ? (oldCovers[oldIndex].__brushOption = newBrushInternal, oldCovers[oldIndex]) : endCreating(controller, createCover(controller, newBrushInternal));\n        updateCoverAfterCreation(controller, cover);\n      }\n    }\n\n    function remove(oldIndex) {\n      if (oldCovers[oldIndex] !== creatingCover) {\n        controller.group.remove(oldCovers[oldIndex]);\n      }\n    }\n  };\n\n  BrushController.prototype.unmount = function () {\n    if (\"development\" !== 'production') {\n      if (!this._mounted) {\n        return;\n      }\n    }\n\n    this.enableBrush(false); // container may 'removeAll' outside.\n\n    clearCovers(this);\n\n    this._zr.remove(this.group);\n\n    if (\"development\" !== 'production') {\n      this._mounted = false; // should be at last.\n    }\n\n    return this;\n  };\n\n  BrushController.prototype.dispose = function () {\n    this.unmount();\n    this.off();\n  };\n\n  return BrushController;\n}(Eventful);\n\nfunction createCover(controller, brushOption) {\n  var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);\n  cover.__brushOption = brushOption;\n  updateZ(cover, brushOption);\n  controller.group.add(cover);\n  return cover;\n}\n\nfunction endCreating(controller, creatingCover) {\n  var coverRenderer = getCoverRenderer(creatingCover);\n\n  if (coverRenderer.endCreating) {\n    coverRenderer.endCreating(controller, creatingCover);\n    updateZ(creatingCover, creatingCover.__brushOption);\n  }\n\n  return creatingCover;\n}\n\nfunction updateCoverShape(controller, cover) {\n  var brushOption = cover.__brushOption;\n  getCoverRenderer(cover).updateCoverShape(controller, cover, brushOption.range, brushOption);\n}\n\nfunction updateZ(cover, brushOption) {\n  var z = brushOption.z;\n  z == null && (z = COVER_Z);\n  cover.traverse(function (el) {\n    el.z = z;\n    el.z2 = z; // Consider in given container.\n  });\n}\n\nfunction updateCoverAfterCreation(controller, cover) {\n  getCoverRenderer(cover).updateCommon(controller, cover);\n  updateCoverShape(controller, cover);\n}\n\nfunction getCoverRenderer(cover) {\n  return coverRenderers[cover.__brushOption.brushType];\n} // return target panel or `true` (means global panel)\n\n\nfunction getPanelByPoint(controller, e, localCursorPoint) {\n  var panels = controller._panels;\n\n  if (!panels) {\n    return BRUSH_PANEL_GLOBAL; // Global panel\n  }\n\n  var panel;\n  var transform = controller._transform;\n  each(panels, function (pn) {\n    pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn);\n  });\n  return panel;\n} // Return a panel or true\n\n\nfunction getPanelByCover(controller, cover) {\n  var panels = controller._panels;\n\n  if (!panels) {\n    return BRUSH_PANEL_GLOBAL; // Global panel\n  }\n\n  var panelId = cover.__brushOption.panelId; // User may give cover without coord sys info,\n  // which is then treated as global panel.\n\n  return panelId != null ? panels[panelId] : BRUSH_PANEL_GLOBAL;\n}\n\nfunction clearCovers(controller) {\n  var covers = controller._covers;\n  var originalLength = covers.length;\n  each(covers, function (cover) {\n    controller.group.remove(cover);\n  }, controller);\n  covers.length = 0;\n  return !!originalLength;\n}\n\nfunction trigger$1(controller, opt) {\n  var areas = map(controller._covers, function (cover) {\n    var brushOption = cover.__brushOption;\n    var range = clone(brushOption.range);\n    return {\n      brushType: brushOption.brushType,\n      panelId: brushOption.panelId,\n      range: range\n    };\n  });\n  controller.trigger('brush', {\n    areas: areas,\n    isEnd: !!opt.isEnd,\n    removeOnClick: !!opt.removeOnClick\n  });\n}\n\nfunction shouldShowCover(controller) {\n  var track = controller._track;\n\n  if (!track.length) {\n    return false;\n  }\n\n  var p2 = track[track.length - 1];\n  var p1 = track[0];\n  var dx = p2[0] - p1[0];\n  var dy = p2[1] - p1[1];\n  var dist = mathPow$2(dx * dx + dy * dy, 0.5);\n  return dist > UNSELECT_THRESHOLD;\n}\n\nfunction getTrackEnds(track) {\n  var tail = track.length - 1;\n  tail < 0 && (tail = 0);\n  return [track[0], track[tail]];\n}\n\nfunction createBaseRectCover(rectRangeConverter, controller, brushOption, edgeNameSequences) {\n  var cover = new Group();\n  cover.add(new Rect({\n    name: 'main',\n    style: makeStyle(brushOption),\n    silent: true,\n    draggable: true,\n    cursor: 'move',\n    drift: curry(driftRect, rectRangeConverter, controller, cover, ['n', 's', 'w', 'e']),\n    ondragend: curry(trigger$1, controller, {\n      isEnd: true\n    })\n  }));\n  each(edgeNameSequences, function (nameSequence) {\n    cover.add(new Rect({\n      name: nameSequence.join(''),\n      style: {\n        opacity: 0\n      },\n      draggable: true,\n      silent: true,\n      invisible: true,\n      drift: curry(driftRect, rectRangeConverter, controller, cover, nameSequence),\n      ondragend: curry(trigger$1, controller, {\n        isEnd: true\n      })\n    }));\n  });\n  return cover;\n}\n\nfunction updateBaseRect(controller, cover, localRange, brushOption) {\n  var lineWidth = brushOption.brushStyle.lineWidth || 0;\n  var handleSize = mathMax$9(lineWidth, MIN_RESIZE_LINE_WIDTH);\n  var x = localRange[0][0];\n  var y = localRange[1][0];\n  var xa = x - lineWidth / 2;\n  var ya = y - lineWidth / 2;\n  var x2 = localRange[0][1];\n  var y2 = localRange[1][1];\n  var x2a = x2 - handleSize + lineWidth / 2;\n  var y2a = y2 - handleSize + lineWidth / 2;\n  var width = x2 - x;\n  var height = y2 - y;\n  var widtha = width + lineWidth;\n  var heighta = height + lineWidth;\n  updateRectShape(controller, cover, 'main', x, y, width, height);\n\n  if (brushOption.transformable) {\n    updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);\n    updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);\n    updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);\n    updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);\n    updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);\n    updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);\n    updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);\n    updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);\n  }\n}\n\nfunction updateCommon(controller, cover) {\n  var brushOption = cover.__brushOption;\n  var transformable = brushOption.transformable;\n  var mainEl = cover.childAt(0);\n  mainEl.useStyle(makeStyle(brushOption));\n  mainEl.attr({\n    silent: !transformable,\n    cursor: transformable ? 'move' : 'default'\n  });\n  each([['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']], function (nameSequence) {\n    var el = cover.childOfName(nameSequence.join(''));\n    var globalDir = nameSequence.length === 1 ? getGlobalDirection1(controller, nameSequence[0]) : getGlobalDirection2(controller, nameSequence);\n    el && el.attr({\n      silent: !transformable,\n      invisible: !transformable,\n      cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null\n    });\n  });\n}\n\nfunction updateRectShape(controller, cover, name, x, y, w, h) {\n  var el = cover.childOfName(name);\n  el && el.setShape(pointsToRect(clipByPanel(controller, cover, [[x, y], [x + w, y + h]])));\n}\n\nfunction makeStyle(brushOption) {\n  return defaults({\n    strokeNoScale: true\n  }, brushOption.brushStyle);\n}\n\nfunction formatRectRange(x, y, x2, y2) {\n  var min = [mathMin$9(x, x2), mathMin$9(y, y2)];\n  var max = [mathMax$9(x, x2), mathMax$9(y, y2)];\n  return [[min[0], max[0]], [min[1], max[1]] // y range\n  ];\n}\n\nfunction getTransform$1(controller) {\n  return getTransform(controller.group);\n}\n\nfunction getGlobalDirection1(controller, localDirName) {\n  var map = {\n    w: 'left',\n    e: 'right',\n    n: 'top',\n    s: 'bottom'\n  };\n  var inverseMap = {\n    left: 'w',\n    right: 'e',\n    top: 'n',\n    bottom: 's'\n  };\n  var dir = transformDirection(map[localDirName], getTransform$1(controller));\n  return inverseMap[dir];\n}\n\nfunction getGlobalDirection2(controller, localDirNameSeq) {\n  var globalDir = [getGlobalDirection1(controller, localDirNameSeq[0]), getGlobalDirection1(controller, localDirNameSeq[1])];\n  (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();\n  return globalDir.join('');\n}\n\nfunction driftRect(rectRangeConverter, controller, cover, dirNameSequence, dx, dy) {\n  var brushOption = cover.__brushOption;\n  var rectRange = rectRangeConverter.toRectRange(brushOption.range);\n  var localDelta = toLocalDelta(controller, dx, dy);\n  each(dirNameSequence, function (dirName) {\n    var ind = DIRECTION_MAP[dirName];\n    rectRange[ind[0]][ind[1]] += localDelta[ind[0]];\n  });\n  brushOption.range = rectRangeConverter.fromRectRange(formatRectRange(rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]));\n  updateCoverAfterCreation(controller, cover);\n  trigger$1(controller, {\n    isEnd: false\n  });\n}\n\nfunction driftPolygon(controller, cover, dx, dy) {\n  var range = cover.__brushOption.range;\n  var localDelta = toLocalDelta(controller, dx, dy);\n  each(range, function (point) {\n    point[0] += localDelta[0];\n    point[1] += localDelta[1];\n  });\n  updateCoverAfterCreation(controller, cover);\n  trigger$1(controller, {\n    isEnd: false\n  });\n}\n\nfunction toLocalDelta(controller, dx, dy) {\n  var thisGroup = controller.group;\n  var localD = thisGroup.transformCoordToLocal(dx, dy);\n  var localZero = thisGroup.transformCoordToLocal(0, 0);\n  return [localD[0] - localZero[0], localD[1] - localZero[1]];\n}\n\nfunction clipByPanel(controller, cover, data) {\n  var panel = getPanelByCover(controller, cover);\n  return panel && panel !== BRUSH_PANEL_GLOBAL ? panel.clipPath(data, controller._transform) : clone(data);\n}\n\nfunction pointsToRect(points) {\n  var xmin = mathMin$9(points[0][0], points[1][0]);\n  var ymin = mathMin$9(points[0][1], points[1][1]);\n  var xmax = mathMax$9(points[0][0], points[1][0]);\n  var ymax = mathMax$9(points[0][1], points[1][1]);\n  return {\n    x: xmin,\n    y: ymin,\n    width: xmax - xmin,\n    height: ymax - ymin\n  };\n}\n\nfunction resetCursor(controller, e, localCursorPoint) {\n  if ( // Check active\n  !controller._brushType // resetCursor should be always called when mouse is in zr area,\n  // but not called when mouse is out of zr area to avoid bad influence\n  // if `mousemove`, `mouseup` are triggered from `document` event.\n  || isOutsideZrArea(controller, e.offsetX, e.offsetY)) {\n    return;\n  }\n\n  var zr = controller._zr;\n  var covers = controller._covers;\n  var currPanel = getPanelByPoint(controller, e, localCursorPoint); // Check whether in covers.\n\n  if (!controller._dragging) {\n    for (var i = 0; i < covers.length; i++) {\n      var brushOption = covers[i].__brushOption;\n\n      if (currPanel && (currPanel === BRUSH_PANEL_GLOBAL || brushOption.panelId === currPanel.panelId) && coverRenderers[brushOption.brushType].contain(covers[i], localCursorPoint[0], localCursorPoint[1])) {\n        // Use cursor style set on cover.\n        return;\n      }\n    }\n  }\n\n  currPanel && zr.setCursorStyle('crosshair');\n}\n\nfunction preventDefault(e) {\n  var rawE = e.event;\n  rawE.preventDefault && rawE.preventDefault();\n}\n\nfunction mainShapeContain(cover, x, y) {\n  return cover.childOfName('main').contain(x, y);\n}\n\nfunction updateCoverByMouse(controller, e, localCursorPoint, isEnd) {\n  var creatingCover = controller._creatingCover;\n  var panel = controller._creatingPanel;\n  var thisBrushOption = controller._brushOption;\n  var eventParams;\n\n  controller._track.push(localCursorPoint.slice());\n\n  if (shouldShowCover(controller) || creatingCover) {\n    if (panel && !creatingCover) {\n      thisBrushOption.brushMode === 'single' && clearCovers(controller);\n      var brushOption = clone(thisBrushOption);\n      brushOption.brushType = determineBrushType(brushOption.brushType, panel);\n      brushOption.panelId = panel === BRUSH_PANEL_GLOBAL ? null : panel.panelId;\n      creatingCover = controller._creatingCover = createCover(controller, brushOption);\n\n      controller._covers.push(creatingCover);\n    }\n\n    if (creatingCover) {\n      var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];\n      var coverBrushOption = creatingCover.__brushOption;\n      coverBrushOption.range = coverRenderer.getCreatingRange(clipByPanel(controller, creatingCover, controller._track));\n\n      if (isEnd) {\n        endCreating(controller, creatingCover);\n        coverRenderer.updateCommon(controller, creatingCover);\n      }\n\n      updateCoverShape(controller, creatingCover);\n      eventParams = {\n        isEnd: isEnd\n      };\n    }\n  } else if (isEnd && thisBrushOption.brushMode === 'single' && thisBrushOption.removeOnClick) {\n    // Help user to remove covers easily, only by a tiny drag, in 'single' mode.\n    // But a single click do not clear covers, because user may have casual\n    // clicks (for example, click on other component and do not expect covers\n    // disappear).\n    // Only some cover removed, trigger action, but not every click trigger action.\n    if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) {\n      eventParams = {\n        isEnd: isEnd,\n        removeOnClick: true\n      };\n    }\n  }\n\n  return eventParams;\n}\n\nfunction determineBrushType(brushType, panel) {\n  if (brushType === 'auto') {\n    if (\"development\" !== 'production') {\n      assert(panel && panel.defaultBrushType, 'MUST have defaultBrushType when brushType is \"atuo\"');\n    }\n\n    return panel.defaultBrushType;\n  }\n\n  return brushType;\n}\n\nvar pointerHandlers = {\n  mousedown: function (e) {\n    if (this._dragging) {\n      // In case some browser do not support globalOut,\n      // and release mouse out side the browser.\n      handleDragEnd(this, e);\n    } else if (!e.target || !e.target.draggable) {\n      preventDefault(e);\n      var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);\n      this._creatingCover = null;\n      var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint);\n\n      if (panel) {\n        this._dragging = true;\n        this._track = [localCursorPoint.slice()];\n      }\n    }\n  },\n  mousemove: function (e) {\n    var x = e.offsetX;\n    var y = e.offsetY;\n    var localCursorPoint = this.group.transformCoordToLocal(x, y);\n    resetCursor(this, e, localCursorPoint);\n\n    if (this._dragging) {\n      preventDefault(e);\n      var eventParams = updateCoverByMouse(this, e, localCursorPoint, false);\n      eventParams && trigger$1(this, eventParams);\n    }\n  },\n  mouseup: function (e) {\n    handleDragEnd(this, e);\n  }\n};\n\nfunction handleDragEnd(controller, e) {\n  if (controller._dragging) {\n    preventDefault(e);\n    var x = e.offsetX;\n    var y = e.offsetY;\n    var localCursorPoint = controller.group.transformCoordToLocal(x, y);\n    var eventParams = updateCoverByMouse(controller, e, localCursorPoint, true);\n    controller._dragging = false;\n    controller._track = [];\n    controller._creatingCover = null; // trigger event shoule be at final, after procedure will be nested.\n\n    eventParams && trigger$1(controller, eventParams);\n  }\n}\n\nfunction isOutsideZrArea(controller, x, y) {\n  var zr = controller._zr;\n  return x < 0 || x > zr.getWidth() || y < 0 || y > zr.getHeight();\n}\n/**\n * key: brushType\n */\n\n\nvar coverRenderers = {\n  lineX: getLineRenderer(0),\n  lineY: getLineRenderer(1),\n  rect: {\n    createCover: function (controller, brushOption) {\n      function returnInput(range) {\n        return range;\n      }\n\n      return createBaseRectCover({\n        toRectRange: returnInput,\n        fromRectRange: returnInput\n      }, controller, brushOption, [['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']]);\n    },\n    getCreatingRange: function (localTrack) {\n      var ends = getTrackEnds(localTrack);\n      return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);\n    },\n    updateCoverShape: function (controller, cover, localRange, brushOption) {\n      updateBaseRect(controller, cover, localRange, brushOption);\n    },\n    updateCommon: updateCommon,\n    contain: mainShapeContain\n  },\n  polygon: {\n    createCover: function (controller, brushOption) {\n      var cover = new Group(); // Do not use graphic.Polygon because graphic.Polyline do not close the\n      // border of the shape when drawing, which is a better experience for user.\n\n      cover.add(new Polyline({\n        name: 'main',\n        style: makeStyle(brushOption),\n        silent: true\n      }));\n      return cover;\n    },\n    getCreatingRange: function (localTrack) {\n      return localTrack;\n    },\n    endCreating: function (controller, cover) {\n      cover.remove(cover.childAt(0)); // Use graphic.Polygon close the shape.\n\n      cover.add(new Polygon({\n        name: 'main',\n        draggable: true,\n        drift: curry(driftPolygon, controller, cover),\n        ondragend: curry(trigger$1, controller, {\n          isEnd: true\n        })\n      }));\n    },\n    updateCoverShape: function (controller, cover, localRange, brushOption) {\n      cover.childAt(0).setShape({\n        points: clipByPanel(controller, cover, localRange)\n      });\n    },\n    updateCommon: updateCommon,\n    contain: mainShapeContain\n  }\n};\n\nfunction getLineRenderer(xyIndex) {\n  return {\n    createCover: function (controller, brushOption) {\n      return createBaseRectCover({\n        toRectRange: function (range) {\n          var rectRange = [range, [0, 100]];\n          xyIndex && rectRange.reverse();\n          return rectRange;\n        },\n        fromRectRange: function (rectRange) {\n          return rectRange[xyIndex];\n        }\n      }, controller, brushOption, [[['w'], ['e']], [['n'], ['s']]][xyIndex]);\n    },\n    getCreatingRange: function (localTrack) {\n      var ends = getTrackEnds(localTrack);\n      var min = mathMin$9(ends[0][xyIndex], ends[1][xyIndex]);\n      var max = mathMax$9(ends[0][xyIndex], ends[1][xyIndex]);\n      return [min, max];\n    },\n    updateCoverShape: function (controller, cover, localRange, brushOption) {\n      var otherExtent; // If brushWidth not specified, fit the panel.\n\n      var panel = getPanelByCover(controller, cover);\n\n      if (panel !== BRUSH_PANEL_GLOBAL && panel.getLinearBrushOtherExtent) {\n        otherExtent = panel.getLinearBrushOtherExtent(xyIndex);\n      } else {\n        var zr = controller._zr;\n        otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]];\n      }\n\n      var rectRange = [localRange, otherExtent];\n      xyIndex && rectRange.reverse();\n      updateBaseRect(controller, cover, rectRange, brushOption);\n    },\n    updateCommon: updateCommon,\n    contain: mainShapeContain\n  };\n}\n\nfunction makeRectPanelClipPath(rect) {\n  rect = normalizeRect(rect);\n  return function (localPoints) {\n    return clipPointsByRect(localPoints, rect);\n  };\n}\nfunction makeLinearBrushOtherExtent(rect, specifiedXYIndex) {\n  rect = normalizeRect(rect);\n  return function (xyIndex) {\n    var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex;\n    var brushWidth = idx ? rect.width : rect.height;\n    var base = idx ? rect.x : rect.y;\n    return [base, base + (brushWidth || 0)];\n  };\n}\nfunction makeRectIsTargetByCursor(rect, api, targetModel) {\n  var boundingRect = normalizeRect(rect);\n  return function (e, localCursorPoint) {\n    return boundingRect.contain(localCursorPoint[0], localCursorPoint[1]) && !onIrrelevantElement(e, api, targetModel);\n  };\n} // Consider width/height is negative.\n\nfunction normalizeRect(rect) {\n  return BoundingRect.create(rect);\n}\n\nvar elementList = ['axisLine', 'axisTickLabel', 'axisName'];\n\nvar ParallelAxisView =\n/** @class */\nfunction (_super) {\n  __extends(ParallelAxisView, _super);\n\n  function ParallelAxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ParallelAxisView.type;\n    return _this;\n  }\n\n  ParallelAxisView.prototype.init = function (ecModel, api) {\n    _super.prototype.init.apply(this, arguments);\n\n    (this._brushController = new BrushController(api.getZr())).on('brush', bind(this._onBrush, this));\n  };\n\n  ParallelAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n    if (fromAxisAreaSelect(axisModel, ecModel, payload)) {\n      return;\n    }\n\n    this.axisModel = axisModel;\n    this.api = api;\n    this.group.removeAll();\n    var oldAxisGroup = this._axisGroup;\n    this._axisGroup = new Group();\n    this.group.add(this._axisGroup);\n\n    if (!axisModel.get('show')) {\n      return;\n    }\n\n    var coordSysModel = getCoordSysModel(axisModel, ecModel);\n    var coordSys = coordSysModel.coordinateSystem;\n    var areaSelectStyle = axisModel.getAreaSelectStyle();\n    var areaWidth = areaSelectStyle.width;\n    var dim = axisModel.axis.dim;\n    var axisLayout = coordSys.getAxisLayout(dim);\n    var builderOpt = extend({\n      strokeContainThreshold: areaWidth\n    }, axisLayout);\n    var axisBuilder = new AxisBuilder(axisModel, builderOpt);\n    each(elementList, axisBuilder.add, axisBuilder);\n\n    this._axisGroup.add(axisBuilder.getGroup());\n\n    this._refreshBrushController(builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api);\n\n    groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n  }; // /**\n  //  * @override\n  //  */\n  // updateVisual(axisModel, ecModel, api, payload) {\n  //     this._brushController && this._brushController\n  //         .updateCovers(getCoverInfoList(axisModel));\n  // }\n\n\n  ParallelAxisView.prototype._refreshBrushController = function (builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api) {\n    // After filtering, axis may change, select area needs to be update.\n    var extent = axisModel.axis.getExtent();\n    var extentLen = extent[1] - extent[0];\n    var extra = Math.min(30, Math.abs(extentLen) * 0.1); // Arbitrary value.\n    // width/height might be negative, which will be\n    // normalized in BoundingRect.\n\n    var rect = BoundingRect.create({\n      x: extent[0],\n      y: -areaWidth / 2,\n      width: extentLen,\n      height: areaWidth\n    });\n    rect.x -= extra;\n    rect.width += 2 * extra;\n\n    this._brushController.mount({\n      enableGlobalPan: true,\n      rotation: builderOpt.rotation,\n      x: builderOpt.position[0],\n      y: builderOpt.position[1]\n    }).setPanels([{\n      panelId: 'pl',\n      clipPath: makeRectPanelClipPath(rect),\n      isTargetByCursor: makeRectIsTargetByCursor(rect, api, coordSysModel),\n      getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect, 0)\n    }]).enableBrush({\n      brushType: 'lineX',\n      brushStyle: areaSelectStyle,\n      removeOnClick: true\n    }).updateCovers(getCoverInfoList(axisModel));\n  };\n\n  ParallelAxisView.prototype._onBrush = function (eventParam) {\n    var coverInfoList = eventParam.areas; // Do not cache these object, because the mey be changed.\n\n    var axisModel = this.axisModel;\n    var axis = axisModel.axis;\n    var intervals = map(coverInfoList, function (coverInfo) {\n      return [axis.coordToData(coverInfo.range[0], true), axis.coordToData(coverInfo.range[1], true)];\n    }); // If realtime is true, action is not dispatched on drag end, because\n    // the drag end emits the same params with the last drag move event,\n    // and may have some delay when using touch pad.\n\n    if (!axisModel.option.realtime === eventParam.isEnd || eventParam.removeOnClick) {\n      // jshint ignore:line\n      this.api.dispatchAction({\n        type: 'axisAreaSelect',\n        parallelAxisId: axisModel.id,\n        intervals: intervals\n      });\n    }\n  };\n\n  ParallelAxisView.prototype.dispose = function () {\n    this._brushController.dispose();\n  };\n\n  ParallelAxisView.type = 'parallelAxis';\n  return ParallelAxisView;\n}(ComponentView);\n\nfunction fromAxisAreaSelect(axisModel, ecModel, payload) {\n  return payload && payload.type === 'axisAreaSelect' && ecModel.findComponents({\n    mainType: 'parallelAxis',\n    query: payload\n  })[0] === axisModel;\n}\n\nfunction getCoverInfoList(axisModel) {\n  var axis = axisModel.axis;\n  return map(axisModel.activeIntervals, function (interval) {\n    return {\n      brushType: 'lineX',\n      panelId: 'pl',\n      range: [axis.dataToCoord(interval[0], true), axis.dataToCoord(interval[1], true)]\n    };\n  });\n}\n\nfunction getCoordSysModel(axisModel, ecModel) {\n  return ecModel.getComponent('parallel', axisModel.get('parallelIndex'));\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar actionInfo$1 = {\n  type: 'axisAreaSelect',\n  event: 'axisAreaSelected' // update: 'updateVisual'\n\n};\nfunction installParallelActions(registers) {\n  registers.registerAction(actionInfo$1, function (payload, ecModel) {\n    ecModel.eachComponent({\n      mainType: 'parallelAxis',\n      query: payload\n    }, function (parallelAxisModel) {\n      parallelAxisModel.axis.model.setActiveIntervals(payload.intervals);\n    });\n  });\n  /**\n   * @payload\n   */\n\n  registers.registerAction('parallelAxisExpand', function (payload, ecModel) {\n    ecModel.eachComponent({\n      mainType: 'parallel',\n      query: payload\n    }, function (parallelModel) {\n      parallelModel.setAxisExpand(payload);\n    });\n  });\n}\n\nvar defaultAxisOption = {\n  type: 'value',\n  areaSelectStyle: {\n    width: 20,\n    borderWidth: 1,\n    borderColor: 'rgba(160,197,232)',\n    color: 'rgba(160,197,232)',\n    opacity: 0.3\n  },\n  realtime: true,\n  z: 10\n};\nfunction install$g(registers) {\n  registers.registerComponentView(ParallelView$1);\n  registers.registerComponentModel(ParallelModel);\n  registers.registerCoordinateSystem('parallel', parallelCoordSysCreator);\n  registers.registerPreprocessor(parallelPreprocessor);\n  registers.registerComponentModel(ParallelAxisModel);\n  registers.registerComponentView(ParallelAxisView);\n  axisModelCreator(registers, 'parallel', ParallelAxisModel, defaultAxisOption);\n  installParallelActions(registers);\n}\n\nfunction install$h(registers) {\n  use(install$g);\n  registers.registerChartView(ParallelView);\n  registers.registerSeriesModel(ParallelSeriesModel);\n  registers.registerVisual(registers.PRIORITY.VISUAL.BRUSH, parallelVisual);\n}\n\nvar SankeyPathShape =\n/** @class */\nfunction () {\n  function SankeyPathShape() {\n    this.x1 = 0;\n    this.y1 = 0;\n    this.x2 = 0;\n    this.y2 = 0;\n    this.cpx1 = 0;\n    this.cpy1 = 0;\n    this.cpx2 = 0;\n    this.cpy2 = 0;\n    this.extent = 0;\n  }\n\n  return SankeyPathShape;\n}();\n\nvar SankeyPath =\n/** @class */\nfunction (_super) {\n  __extends(SankeyPath, _super);\n\n  function SankeyPath(opts) {\n    return _super.call(this, opts) || this;\n  }\n\n  SankeyPath.prototype.getDefaultShape = function () {\n    return new SankeyPathShape();\n  };\n\n  SankeyPath.prototype.buildPath = function (ctx, shape) {\n    var extent = shape.extent;\n    ctx.moveTo(shape.x1, shape.y1);\n    ctx.bezierCurveTo(shape.cpx1, shape.cpy1, shape.cpx2, shape.cpy2, shape.x2, shape.y2);\n\n    if (shape.orient === 'vertical') {\n      ctx.lineTo(shape.x2 + extent, shape.y2);\n      ctx.bezierCurveTo(shape.cpx2 + extent, shape.cpy2, shape.cpx1 + extent, shape.cpy1, shape.x1 + extent, shape.y1);\n    } else {\n      ctx.lineTo(shape.x2, shape.y2 + extent);\n      ctx.bezierCurveTo(shape.cpx2, shape.cpy2 + extent, shape.cpx1, shape.cpy1 + extent, shape.x1, shape.y1 + extent);\n    }\n\n    ctx.closePath();\n  };\n\n  SankeyPath.prototype.highlight = function () {\n    enterEmphasis(this);\n  };\n\n  SankeyPath.prototype.downplay = function () {\n    leaveEmphasis(this);\n  };\n\n  return SankeyPath;\n}(Path);\n\nvar SankeyView =\n/** @class */\nfunction (_super) {\n  __extends(SankeyView, _super);\n\n  function SankeyView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SankeyView.type;\n    _this._focusAdjacencyDisabled = false;\n    return _this;\n  }\n\n  SankeyView.prototype.render = function (seriesModel, ecModel, api) {\n    var sankeyView = this;\n    var graph = seriesModel.getGraph();\n    var group = this.group;\n    var layoutInfo = seriesModel.layoutInfo; // view width\n\n    var width = layoutInfo.width; // view height\n\n    var height = layoutInfo.height;\n    var nodeData = seriesModel.getData();\n    var edgeData = seriesModel.getData('edge');\n    var orient = seriesModel.get('orient');\n    this._model = seriesModel;\n    group.removeAll();\n    group.x = layoutInfo.x;\n    group.y = layoutInfo.y; // generate a bezire Curve for each edge\n\n    graph.eachEdge(function (edge) {\n      var curve = new SankeyPath();\n      var ecData = getECData(curve);\n      ecData.dataIndex = edge.dataIndex;\n      ecData.seriesIndex = seriesModel.seriesIndex;\n      ecData.dataType = 'edge';\n      var edgeModel = edge.getModel();\n      var lineStyleModel = edgeModel.getModel('lineStyle');\n      var curvature = lineStyleModel.get('curveness');\n      var n1Layout = edge.node1.getLayout();\n      var node1Model = edge.node1.getModel();\n      var dragX1 = node1Model.get('localX');\n      var dragY1 = node1Model.get('localY');\n      var n2Layout = edge.node2.getLayout();\n      var node2Model = edge.node2.getModel();\n      var dragX2 = node2Model.get('localX');\n      var dragY2 = node2Model.get('localY');\n      var edgeLayout = edge.getLayout();\n      var x1;\n      var y1;\n      var x2;\n      var y2;\n      var cpx1;\n      var cpy1;\n      var cpx2;\n      var cpy2;\n      curve.shape.extent = Math.max(1, edgeLayout.dy);\n      curve.shape.orient = orient;\n\n      if (orient === 'vertical') {\n        x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + edgeLayout.sy;\n        y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + n1Layout.dy;\n        x2 = (dragX2 != null ? dragX2 * width : n2Layout.x) + edgeLayout.ty;\n        y2 = dragY2 != null ? dragY2 * height : n2Layout.y;\n        cpx1 = x1;\n        cpy1 = y1 * (1 - curvature) + y2 * curvature;\n        cpx2 = x2;\n        cpy2 = y1 * curvature + y2 * (1 - curvature);\n      } else {\n        x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + n1Layout.dx;\n        y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + edgeLayout.sy;\n        x2 = dragX2 != null ? dragX2 * width : n2Layout.x;\n        y2 = (dragY2 != null ? dragY2 * height : n2Layout.y) + edgeLayout.ty;\n        cpx1 = x1 * (1 - curvature) + x2 * curvature;\n        cpy1 = y1;\n        cpx2 = x1 * curvature + x2 * (1 - curvature);\n        cpy2 = y2;\n      }\n\n      curve.setShape({\n        x1: x1,\n        y1: y1,\n        x2: x2,\n        y2: y2,\n        cpx1: cpx1,\n        cpy1: cpy1,\n        cpx2: cpx2,\n        cpy2: cpy2\n      });\n      curve.useStyle(lineStyleModel.getItemStyle()); // Special color, use source node color or target node color\n\n      switch (curve.style.fill) {\n        case 'source':\n          curve.style.fill = edge.node1.getVisual('color');\n          curve.style.decal = edge.node1.getVisual('style').decal;\n          break;\n\n        case 'target':\n          curve.style.fill = edge.node2.getVisual('color');\n          curve.style.decal = edge.node2.getVisual('style').decal;\n          break;\n\n        case 'gradient':\n          var sourceColor = edge.node1.getVisual('color');\n          var targetColor = edge.node2.getVisual('color');\n\n          if (isString(sourceColor) && isString(targetColor)) {\n            curve.style.fill = new LinearGradient(0, 0, +(orient === 'horizontal'), +(orient === 'vertical'), [{\n              color: sourceColor,\n              offset: 0\n            }, {\n              color: targetColor,\n              offset: 1\n            }]);\n          }\n\n      }\n\n      setLabelStyle(curve, getLabelStatesModels(edgeModel, 'edgeLabel'), {\n        labelFetcher: seriesModel,\n        labelDataIndex: edge.dataIndex,\n        defaultText: \"\" + edgeModel.get('value')\n      });\n      curve.setTextConfig({\n        position: 'inside'\n      });\n      var emphasisModel = edgeModel.getModel('emphasis');\n      setStatesStylesFromModel(curve, edgeModel, 'lineStyle', function (model) {\n        return model.getItemStyle();\n      });\n      group.add(curve);\n      edgeData.setItemGraphicEl(edge.dataIndex, curve);\n      var focus = emphasisModel.get('focus');\n      toggleHoverEmphasis(curve, focus === 'adjacency' ? edge.getAdjacentDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      getECData(curve).dataType = 'edge';\n    }); // Generate a rect for each node\n\n    graph.eachNode(function (node) {\n      var layout = node.getLayout();\n      var itemModel = node.getModel();\n      var dragX = itemModel.get('localX');\n      var dragY = itemModel.get('localY');\n      var emphasisModel = itemModel.getModel('emphasis');\n      var rect = new Rect({\n        shape: {\n          x: dragX != null ? dragX * width : layout.x,\n          y: dragY != null ? dragY * height : layout.y,\n          width: layout.dx,\n          height: layout.dy\n        },\n        style: itemModel.getModel('itemStyle').getItemStyle(),\n        z2: 10\n      });\n      setLabelStyle(rect, getLabelStatesModels(itemModel), {\n        labelFetcher: seriesModel,\n        labelDataIndex: node.dataIndex,\n        defaultText: node.id\n      });\n      rect.disableLabelAnimation = true;\n      rect.setStyle('fill', node.getVisual('color'));\n      rect.setStyle('decal', node.getVisual('style').decal);\n      setStatesStylesFromModel(rect, itemModel);\n      group.add(rect);\n      nodeData.setItemGraphicEl(node.dataIndex, rect);\n      getECData(rect).dataType = 'node';\n      var focus = emphasisModel.get('focus');\n      toggleHoverEmphasis(rect, focus === 'adjacency' ? node.getAdjacentDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n    });\n    nodeData.eachItemGraphicEl(function (el, dataIndex) {\n      var itemModel = nodeData.getItemModel(dataIndex);\n\n      if (itemModel.get('draggable')) {\n        el.drift = function (dx, dy) {\n          sankeyView._focusAdjacencyDisabled = true;\n          this.shape.x += dx;\n          this.shape.y += dy;\n          this.dirty();\n          api.dispatchAction({\n            type: 'dragNode',\n            seriesId: seriesModel.id,\n            dataIndex: nodeData.getRawIndex(dataIndex),\n            localX: this.shape.x / width,\n            localY: this.shape.y / height\n          });\n        };\n\n        el.ondragend = function () {\n          sankeyView._focusAdjacencyDisabled = false;\n        };\n\n        el.draggable = true;\n        el.cursor = 'move';\n      }\n    });\n\n    if (!this._data && seriesModel.isAnimationEnabled()) {\n      group.setClipPath(createGridClipShape$1(group.getBoundingRect(), seriesModel, function () {\n        group.removeClipPath();\n      }));\n    }\n\n    this._data = seriesModel.getData();\n  };\n\n  SankeyView.prototype.dispose = function () {};\n\n  SankeyView.type = 'sankey';\n  return SankeyView;\n}(ChartView); // Add animation to the view\n\n\nfunction createGridClipShape$1(rect, seriesModel, cb) {\n  var rectEl = new Rect({\n    shape: {\n      x: rect.x - 10,\n      y: rect.y - 10,\n      width: 0,\n      height: rect.height + 20\n    }\n  });\n  initProps(rectEl, {\n    shape: {\n      width: rect.width + 20\n    }\n  }, seriesModel, cb);\n  return rectEl;\n}\n\nvar SankeySeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(SankeySeriesModel, _super);\n\n  function SankeySeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SankeySeriesModel.type;\n    return _this;\n  }\n  /**\n   * Init a graph data structure from data in option series\n   */\n\n\n  SankeySeriesModel.prototype.getInitialData = function (option, ecModel) {\n    var links = option.edges || option.links;\n    var nodes = option.data || option.nodes;\n    var levels = option.levels;\n    this.levelModels = [];\n    var levelModels = this.levelModels;\n\n    for (var i = 0; i < levels.length; i++) {\n      if (levels[i].depth != null && levels[i].depth >= 0) {\n        levelModels[levels[i].depth] = new Model(levels[i], this, ecModel);\n      } else {\n        if (\"development\" !== 'production') {\n          throw new Error('levels[i].depth is mandatory and should be natural number');\n        }\n      }\n    }\n\n    if (nodes && links) {\n      var graph = createGraphFromNodeEdge(nodes, links, this, true, beforeLink);\n      return graph.data;\n    }\n\n    function beforeLink(nodeData, edgeData) {\n      nodeData.wrapMethod('getItemModel', function (model, idx) {\n        var seriesModel = model.parentModel;\n        var layout = seriesModel.getData().getItemLayout(idx);\n\n        if (layout) {\n          var nodeDepth = layout.depth;\n          var levelModel = seriesModel.levelModels[nodeDepth];\n\n          if (levelModel) {\n            model.parentModel = levelModel;\n          }\n        }\n\n        return model;\n      });\n      edgeData.wrapMethod('getItemModel', function (model, idx) {\n        var seriesModel = model.parentModel;\n        var edge = seriesModel.getGraph().getEdgeByIndex(idx);\n        var layout = edge.node1.getLayout();\n\n        if (layout) {\n          var depth = layout.depth;\n          var levelModel = seriesModel.levelModels[depth];\n\n          if (levelModel) {\n            model.parentModel = levelModel;\n          }\n        }\n\n        return model;\n      });\n    }\n  };\n\n  SankeySeriesModel.prototype.setNodePosition = function (dataIndex, localPosition) {\n    var nodes = this.option.data || this.option.nodes;\n    var dataItem = nodes[dataIndex];\n    dataItem.localX = localPosition[0];\n    dataItem.localY = localPosition[1];\n  };\n  /**\n   * Return the graphic data structure\n   *\n   * @return graphic data structure\n   */\n\n\n  SankeySeriesModel.prototype.getGraph = function () {\n    return this.getData().graph;\n  };\n  /**\n   * Get edge data of graphic data structure\n   *\n   * @return data structure of list\n   */\n\n\n  SankeySeriesModel.prototype.getEdgeData = function () {\n    return this.getGraph().edgeData;\n  };\n\n  SankeySeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    function noValue(val) {\n      return isNaN(val) || val == null;\n    } // dataType === 'node' or empty do not show tooltip by default\n\n\n    if (dataType === 'edge') {\n      var params = this.getDataParams(dataIndex, dataType);\n      var rawDataOpt = params.data;\n      var edgeValue = params.value;\n      var edgeName = rawDataOpt.source + ' -- ' + rawDataOpt.target;\n      return createTooltipMarkup('nameValue', {\n        name: edgeName,\n        value: edgeValue,\n        noValue: noValue(edgeValue)\n      });\n    } // dataType === 'node'\n    else {\n        var node = this.getGraph().getNodeByIndex(dataIndex);\n        var value = node.getLayout().value;\n        var name_1 = this.getDataParams(dataIndex, dataType).data.name;\n        return createTooltipMarkup('nameValue', {\n          name: name_1 != null ? name_1 + '' : null,\n          value: value,\n          noValue: noValue(value)\n        });\n      }\n  };\n\n  SankeySeriesModel.prototype.optionUpdated = function () {}; // Override Series.getDataParams()\n\n\n  SankeySeriesModel.prototype.getDataParams = function (dataIndex, dataType) {\n    var params = _super.prototype.getDataParams.call(this, dataIndex, dataType);\n\n    if (params.value == null && dataType === 'node') {\n      var node = this.getGraph().getNodeByIndex(dataIndex);\n      var nodeValue = node.getLayout().value;\n      params.value = nodeValue;\n    }\n\n    return params;\n  };\n\n  SankeySeriesModel.type = 'series.sankey';\n  SankeySeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    coordinateSystem: 'view',\n    left: '5%',\n    top: '5%',\n    right: '20%',\n    bottom: '5%',\n    orient: 'horizontal',\n    nodeWidth: 20,\n    nodeGap: 8,\n    draggable: true,\n    layoutIterations: 32,\n    label: {\n      show: true,\n      position: 'right',\n      fontSize: 12\n    },\n    edgeLabel: {\n      show: false,\n      fontSize: 12\n    },\n    levels: [],\n    nodeAlign: 'justify',\n    lineStyle: {\n      color: '#314656',\n      opacity: 0.2,\n      curveness: 0.5\n    },\n    emphasis: {\n      label: {\n        show: true\n      },\n      lineStyle: {\n        opacity: 0.5\n      }\n    },\n    select: {\n      itemStyle: {\n        borderColor: '#212121'\n      }\n    },\n    animationEasing: 'linear',\n    animationDuration: 1000\n  };\n  return SankeySeriesModel;\n}(SeriesModel);\n\nfunction sankeyLayout(ecModel, api) {\n  ecModel.eachSeriesByType('sankey', function (seriesModel) {\n    var nodeWidth = seriesModel.get('nodeWidth');\n    var nodeGap = seriesModel.get('nodeGap');\n    var layoutInfo = getViewRect$4(seriesModel, api);\n    seriesModel.layoutInfo = layoutInfo;\n    var width = layoutInfo.width;\n    var height = layoutInfo.height;\n    var graph = seriesModel.getGraph();\n    var nodes = graph.nodes;\n    var edges = graph.edges;\n    computeNodeValues(nodes);\n    var filteredNodes = filter(nodes, function (node) {\n      return node.getLayout().value === 0;\n    });\n    var iterations = filteredNodes.length !== 0 ? 0 : seriesModel.get('layoutIterations');\n    var orient = seriesModel.get('orient');\n    var nodeAlign = seriesModel.get('nodeAlign');\n    layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign);\n  });\n}\n/**\n * Get the layout position of the whole view\n */\n\nfunction getViewRect$4(seriesModel, api) {\n  return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n    width: api.getWidth(),\n    height: api.getHeight()\n  });\n}\n\nfunction layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign) {\n  computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign);\n  computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient);\n  computeEdgeDepths(nodes, orient);\n}\n/**\n * Compute the value of each node by summing the associated edge's value\n */\n\n\nfunction computeNodeValues(nodes) {\n  each(nodes, function (node) {\n    var value1 = sum(node.outEdges, getEdgeValue);\n    var value2 = sum(node.inEdges, getEdgeValue);\n    var nodeRawValue = node.getValue() || 0;\n    var value = Math.max(value1, value2, nodeRawValue);\n    node.setLayout({\n      value: value\n    }, true);\n  });\n}\n/**\n * Compute the x-position for each node.\n *\n * Here we use Kahn algorithm to detect cycle when we traverse\n * the node to computer the initial x position.\n */\n\n\nfunction computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign) {\n  // Used to mark whether the edge is deleted. if it is deleted,\n  // the value is 0, otherwise it is 1.\n  var remainEdges = []; // Storage each node's indegree.\n\n  var indegreeArr = []; // Used to storage the node with indegree is equal to 0.\n\n  var zeroIndegrees = [];\n  var nextTargetNode = [];\n  var x = 0; // let kx = 0;\n\n  for (var i = 0; i < edges.length; i++) {\n    remainEdges[i] = 1;\n  }\n\n  for (var i = 0; i < nodes.length; i++) {\n    indegreeArr[i] = nodes[i].inEdges.length;\n\n    if (indegreeArr[i] === 0) {\n      zeroIndegrees.push(nodes[i]);\n    }\n  }\n\n  var maxNodeDepth = -1; // Traversing nodes using topological sorting to calculate the\n  // horizontal(if orient === 'horizontal') or vertical(if orient === 'vertical')\n  // position of the nodes.\n\n  while (zeroIndegrees.length) {\n    for (var idx = 0; idx < zeroIndegrees.length; idx++) {\n      var node = zeroIndegrees[idx];\n      var item = node.hostGraph.data.getRawDataItem(node.dataIndex);\n      var isItemDepth = item.depth != null && item.depth >= 0;\n\n      if (isItemDepth && item.depth > maxNodeDepth) {\n        maxNodeDepth = item.depth;\n      }\n\n      node.setLayout({\n        depth: isItemDepth ? item.depth : x\n      }, true);\n      orient === 'vertical' ? node.setLayout({\n        dy: nodeWidth\n      }, true) : node.setLayout({\n        dx: nodeWidth\n      }, true);\n\n      for (var edgeIdx = 0; edgeIdx < node.outEdges.length; edgeIdx++) {\n        var edge = node.outEdges[edgeIdx];\n        var indexEdge = edges.indexOf(edge);\n        remainEdges[indexEdge] = 0;\n        var targetNode = edge.node2;\n        var nodeIndex = nodes.indexOf(targetNode);\n\n        if (--indegreeArr[nodeIndex] === 0 && nextTargetNode.indexOf(targetNode) < 0) {\n          nextTargetNode.push(targetNode);\n        }\n      }\n    }\n\n    ++x;\n    zeroIndegrees = nextTargetNode;\n    nextTargetNode = [];\n  }\n\n  for (var i = 0; i < remainEdges.length; i++) {\n    if (remainEdges[i] === 1) {\n      throw new Error('Sankey is a DAG, the original data has cycle!');\n    }\n  }\n\n  var maxDepth = maxNodeDepth > x - 1 ? maxNodeDepth : x - 1;\n\n  if (nodeAlign && nodeAlign !== 'left') {\n    adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth);\n  }\n\n  var kx = orient === 'vertical' ? (height - nodeWidth) / maxDepth : (width - nodeWidth) / maxDepth;\n  scaleNodeBreadths(nodes, kx, orient);\n}\n\nfunction isNodeDepth(node) {\n  var item = node.hostGraph.data.getRawDataItem(node.dataIndex);\n  return item.depth != null && item.depth >= 0;\n}\n\nfunction adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth) {\n  if (nodeAlign === 'right') {\n    var nextSourceNode = [];\n    var remainNodes = nodes;\n    var nodeHeight = 0;\n\n    while (remainNodes.length) {\n      for (var i = 0; i < remainNodes.length; i++) {\n        var node = remainNodes[i];\n        node.setLayout({\n          skNodeHeight: nodeHeight\n        }, true);\n\n        for (var j = 0; j < node.inEdges.length; j++) {\n          var edge = node.inEdges[j];\n\n          if (nextSourceNode.indexOf(edge.node1) < 0) {\n            nextSourceNode.push(edge.node1);\n          }\n        }\n      }\n\n      remainNodes = nextSourceNode;\n      nextSourceNode = [];\n      ++nodeHeight;\n    }\n\n    each(nodes, function (node) {\n      if (!isNodeDepth(node)) {\n        node.setLayout({\n          depth: Math.max(0, maxDepth - node.getLayout().skNodeHeight)\n        }, true);\n      }\n    });\n  } else if (nodeAlign === 'justify') {\n    moveSinksRight(nodes, maxDepth);\n  }\n}\n/**\n * All the node without outEgdes are assigned maximum x-position and\n *     be aligned in the last column.\n *\n * @param nodes.  node of sankey view.\n * @param maxDepth.  use to assign to node without outEdges as x-position.\n */\n\n\nfunction moveSinksRight(nodes, maxDepth) {\n  each(nodes, function (node) {\n    if (!isNodeDepth(node) && !node.outEdges.length) {\n      node.setLayout({\n        depth: maxDepth\n      }, true);\n    }\n  });\n}\n/**\n * Scale node x-position to the width\n *\n * @param nodes  node of sankey view\n * @param kx   multiple used to scale nodes\n */\n\n\nfunction scaleNodeBreadths(nodes, kx, orient) {\n  each(nodes, function (node) {\n    var nodeDepth = node.getLayout().depth * kx;\n    orient === 'vertical' ? node.setLayout({\n      y: nodeDepth\n    }, true) : node.setLayout({\n      x: nodeDepth\n    }, true);\n  });\n}\n/**\n * Using Gauss-Seidel iterations method to compute the node depth(y-position)\n *\n * @param nodes  node of sankey view\n * @param edges  edge of sankey view\n * @param height  the whole height of the area to draw the view\n * @param nodeGap  the vertical distance between two nodes\n *     in the same column.\n * @param iterations  the number of iterations for the algorithm\n */\n\n\nfunction computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient) {\n  var nodesByBreadth = prepareNodesByBreadth(nodes, orient);\n  initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient);\n  resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);\n\n  for (var alpha = 1; iterations > 0; iterations--) {\n    // 0.99 is a experience parameter, ensure that each iterations of\n    // changes as small as possible.\n    alpha *= 0.99;\n    relaxRightToLeft(nodesByBreadth, alpha, orient);\n    resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);\n    relaxLeftToRight(nodesByBreadth, alpha, orient);\n    resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);\n  }\n}\n\nfunction prepareNodesByBreadth(nodes, orient) {\n  var nodesByBreadth = [];\n  var keyAttr = orient === 'vertical' ? 'y' : 'x';\n  var groupResult = groupData(nodes, function (node) {\n    return node.getLayout()[keyAttr];\n  });\n  groupResult.keys.sort(function (a, b) {\n    return a - b;\n  });\n  each(groupResult.keys, function (key) {\n    nodesByBreadth.push(groupResult.buckets.get(key));\n  });\n  return nodesByBreadth;\n}\n/**\n * Compute the original y-position for each node\n */\n\n\nfunction initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient) {\n  var minKy = Infinity;\n  each(nodesByBreadth, function (nodes) {\n    var n = nodes.length;\n    var sum = 0;\n    each(nodes, function (node) {\n      sum += node.getLayout().value;\n    });\n    var ky = orient === 'vertical' ? (width - (n - 1) * nodeGap) / sum : (height - (n - 1) * nodeGap) / sum;\n\n    if (ky < minKy) {\n      minKy = ky;\n    }\n  });\n  each(nodesByBreadth, function (nodes) {\n    each(nodes, function (node, i) {\n      var nodeDy = node.getLayout().value * minKy;\n\n      if (orient === 'vertical') {\n        node.setLayout({\n          x: i\n        }, true);\n        node.setLayout({\n          dx: nodeDy\n        }, true);\n      } else {\n        node.setLayout({\n          y: i\n        }, true);\n        node.setLayout({\n          dy: nodeDy\n        }, true);\n      }\n    });\n  });\n  each(edges, function (edge) {\n    var edgeDy = +edge.getValue() * minKy;\n    edge.setLayout({\n      dy: edgeDy\n    }, true);\n  });\n}\n/**\n * Resolve the collision of initialized depth (y-position)\n */\n\n\nfunction resolveCollisions(nodesByBreadth, nodeGap, height, width, orient) {\n  var keyAttr = orient === 'vertical' ? 'x' : 'y';\n  each(nodesByBreadth, function (nodes) {\n    nodes.sort(function (a, b) {\n      return a.getLayout()[keyAttr] - b.getLayout()[keyAttr];\n    });\n    var nodeX;\n    var node;\n    var dy;\n    var y0 = 0;\n    var n = nodes.length;\n    var nodeDyAttr = orient === 'vertical' ? 'dx' : 'dy';\n\n    for (var i = 0; i < n; i++) {\n      node = nodes[i];\n      dy = y0 - node.getLayout()[keyAttr];\n\n      if (dy > 0) {\n        nodeX = node.getLayout()[keyAttr] + dy;\n        orient === 'vertical' ? node.setLayout({\n          x: nodeX\n        }, true) : node.setLayout({\n          y: nodeX\n        }, true);\n      }\n\n      y0 = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap;\n    }\n\n    var viewWidth = orient === 'vertical' ? width : height; // If the bottommost node goes outside the bounds, push it back up\n\n    dy = y0 - nodeGap - viewWidth;\n\n    if (dy > 0) {\n      nodeX = node.getLayout()[keyAttr] - dy;\n      orient === 'vertical' ? node.setLayout({\n        x: nodeX\n      }, true) : node.setLayout({\n        y: nodeX\n      }, true);\n      y0 = nodeX;\n\n      for (var i = n - 2; i >= 0; --i) {\n        node = nodes[i];\n        dy = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap - y0;\n\n        if (dy > 0) {\n          nodeX = node.getLayout()[keyAttr] - dy;\n          orient === 'vertical' ? node.setLayout({\n            x: nodeX\n          }, true) : node.setLayout({\n            y: nodeX\n          }, true);\n        }\n\n        y0 = node.getLayout()[keyAttr];\n      }\n    }\n  });\n}\n/**\n * Change the y-position of the nodes, except most the right side nodes\n * @param nodesByBreadth\n * @param alpha  parameter used to adjust the nodes y-position\n */\n\n\nfunction relaxRightToLeft(nodesByBreadth, alpha, orient) {\n  each(nodesByBreadth.slice().reverse(), function (nodes) {\n    each(nodes, function (node) {\n      if (node.outEdges.length) {\n        var y = sum(node.outEdges, weightedTarget, orient) / sum(node.outEdges, getEdgeValue);\n\n        if (isNaN(y)) {\n          var len = node.outEdges.length;\n          y = len ? sum(node.outEdges, centerTarget, orient) / len : 0;\n        }\n\n        if (orient === 'vertical') {\n          var nodeX = node.getLayout().x + (y - center$1(node, orient)) * alpha;\n          node.setLayout({\n            x: nodeX\n          }, true);\n        } else {\n          var nodeY = node.getLayout().y + (y - center$1(node, orient)) * alpha;\n          node.setLayout({\n            y: nodeY\n          }, true);\n        }\n      }\n    });\n  });\n}\n\nfunction weightedTarget(edge, orient) {\n  return center$1(edge.node2, orient) * edge.getValue();\n}\n\nfunction centerTarget(edge, orient) {\n  return center$1(edge.node2, orient);\n}\n\nfunction weightedSource(edge, orient) {\n  return center$1(edge.node1, orient) * edge.getValue();\n}\n\nfunction centerSource(edge, orient) {\n  return center$1(edge.node1, orient);\n}\n\nfunction center$1(node, orient) {\n  return orient === 'vertical' ? node.getLayout().x + node.getLayout().dx / 2 : node.getLayout().y + node.getLayout().dy / 2;\n}\n\nfunction getEdgeValue(edge) {\n  return edge.getValue();\n}\n\nfunction sum(array, cb, orient) {\n  var sum = 0;\n  var len = array.length;\n  var i = -1;\n\n  while (++i < len) {\n    var value = +cb(array[i], orient);\n\n    if (!isNaN(value)) {\n      sum += value;\n    }\n  }\n\n  return sum;\n}\n/**\n * Change the y-position of the nodes, except most the left side nodes\n */\n\n\nfunction relaxLeftToRight(nodesByBreadth, alpha, orient) {\n  each(nodesByBreadth, function (nodes) {\n    each(nodes, function (node) {\n      if (node.inEdges.length) {\n        var y = sum(node.inEdges, weightedSource, orient) / sum(node.inEdges, getEdgeValue);\n\n        if (isNaN(y)) {\n          var len = node.inEdges.length;\n          y = len ? sum(node.inEdges, centerSource, orient) / len : 0;\n        }\n\n        if (orient === 'vertical') {\n          var nodeX = node.getLayout().x + (y - center$1(node, orient)) * alpha;\n          node.setLayout({\n            x: nodeX\n          }, true);\n        } else {\n          var nodeY = node.getLayout().y + (y - center$1(node, orient)) * alpha;\n          node.setLayout({\n            y: nodeY\n          }, true);\n        }\n      }\n    });\n  });\n}\n/**\n * Compute the depth(y-position) of each edge\n */\n\n\nfunction computeEdgeDepths(nodes, orient) {\n  var keyAttr = orient === 'vertical' ? 'x' : 'y';\n  each(nodes, function (node) {\n    node.outEdges.sort(function (a, b) {\n      return a.node2.getLayout()[keyAttr] - b.node2.getLayout()[keyAttr];\n    });\n    node.inEdges.sort(function (a, b) {\n      return a.node1.getLayout()[keyAttr] - b.node1.getLayout()[keyAttr];\n    });\n  });\n  each(nodes, function (node) {\n    var sy = 0;\n    var ty = 0;\n    each(node.outEdges, function (edge) {\n      edge.setLayout({\n        sy: sy\n      }, true);\n      sy += edge.getLayout().dy;\n    });\n    each(node.inEdges, function (edge) {\n      edge.setLayout({\n        ty: ty\n      }, true);\n      ty += edge.getLayout().dy;\n    });\n  });\n}\n\nfunction sankeyVisual(ecModel) {\n  ecModel.eachSeriesByType('sankey', function (seriesModel) {\n    var graph = seriesModel.getGraph();\n    var nodes = graph.nodes;\n    var edges = graph.edges;\n\n    if (nodes.length) {\n      var minValue_1 = Infinity;\n      var maxValue_1 = -Infinity;\n      each(nodes, function (node) {\n        var nodeValue = node.getLayout().value;\n\n        if (nodeValue < minValue_1) {\n          minValue_1 = nodeValue;\n        }\n\n        if (nodeValue > maxValue_1) {\n          maxValue_1 = nodeValue;\n        }\n      });\n      each(nodes, function (node) {\n        var mapping = new VisualMapping({\n          type: 'color',\n          mappingMethod: 'linear',\n          dataExtent: [minValue_1, maxValue_1],\n          visual: seriesModel.get('color')\n        });\n        var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value);\n        var customColor = node.getModel().get(['itemStyle', 'color']);\n\n        if (customColor != null) {\n          node.setVisual('color', customColor);\n          node.setVisual('style', {\n            fill: customColor\n          });\n        } else {\n          node.setVisual('color', mapValueToColor);\n          node.setVisual('style', {\n            fill: mapValueToColor\n          });\n        }\n      });\n    }\n\n    if (edges.length) {\n      each(edges, function (edge) {\n        var edgeStyle = edge.getModel().get('lineStyle');\n        edge.setVisual('style', edgeStyle);\n      });\n    }\n  });\n}\n\nfunction install$i(registers) {\n  registers.registerChartView(SankeyView);\n  registers.registerSeriesModel(SankeySeriesModel);\n  registers.registerLayout(sankeyLayout);\n  registers.registerVisual(sankeyVisual);\n  registers.registerAction({\n    type: 'dragNode',\n    event: 'dragnode',\n    // here can only use 'update' now, other value is not support in echarts.\n    update: 'update'\n  }, function (payload, ecModel) {\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'sankey',\n      query: payload\n    }, function (seriesModel) {\n      seriesModel.setNodePosition(payload.dataIndex, [payload.localX, payload.localY]);\n    });\n  });\n}\n\nvar WhiskerBoxCommonMixin =\n/** @class */\nfunction () {\n  function WhiskerBoxCommonMixin() {}\n  /**\n   * @override\n   */\n\n\n  WhiskerBoxCommonMixin.prototype.getInitialData = function (option, ecModel) {\n    // When both types of xAxis and yAxis are 'value', layout is\n    // needed to be specified by user. Otherwise, layout can be\n    // judged by which axis is category.\n    var ordinalMeta;\n    var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex'));\n    var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex'));\n    var xAxisType = xAxisModel.get('type');\n    var yAxisType = yAxisModel.get('type');\n    var addOrdinal; // FIXME\n    // Consider time axis.\n\n    if (xAxisType === 'category') {\n      option.layout = 'horizontal';\n      ordinalMeta = xAxisModel.getOrdinalMeta();\n      addOrdinal = true;\n    } else if (yAxisType === 'category') {\n      option.layout = 'vertical';\n      ordinalMeta = yAxisModel.getOrdinalMeta();\n      addOrdinal = true;\n    } else {\n      option.layout = option.layout || 'horizontal';\n    }\n\n    var coordDims = ['x', 'y'];\n    var baseAxisDimIndex = option.layout === 'horizontal' ? 0 : 1;\n    var baseAxisDim = this._baseAxisDim = coordDims[baseAxisDimIndex];\n    var otherAxisDim = coordDims[1 - baseAxisDimIndex];\n    var axisModels = [xAxisModel, yAxisModel];\n    var baseAxisType = axisModels[baseAxisDimIndex].get('type');\n    var otherAxisType = axisModels[1 - baseAxisDimIndex].get('type');\n    var data = option.data; // Clone a new data for next setOption({}) usage.\n    // Avoid modifying current data will affect further update.\n\n    if (data && addOrdinal) {\n      var newOptionData_1 = [];\n      each(data, function (item, index) {\n        var newItem;\n\n        if (isArray(item)) {\n          newItem = item.slice(); // Modify current using data.\n\n          item.unshift(index);\n        } else if (isArray(item.value)) {\n          newItem = extend({}, item);\n          newItem.value = newItem.value.slice(); // Modify current using data.\n\n          item.value.unshift(index);\n        } else {\n          newItem = item;\n        }\n\n        newOptionData_1.push(newItem);\n      });\n      option.data = newOptionData_1;\n    }\n\n    var defaultValueDimensions = this.defaultValueDimensions;\n    var coordDimensions = [{\n      name: baseAxisDim,\n      type: getDimensionTypeByAxis(baseAxisType),\n      ordinalMeta: ordinalMeta,\n      otherDims: {\n        tooltip: false,\n        itemName: 0\n      },\n      dimsDef: ['base']\n    }, {\n      name: otherAxisDim,\n      type: getDimensionTypeByAxis(otherAxisType),\n      dimsDef: defaultValueDimensions.slice()\n    }];\n    return createSeriesDataSimply(this, {\n      coordDimensions: coordDimensions,\n      dimensionsCount: defaultValueDimensions.length + 1,\n      encodeDefaulter: curry(makeSeriesEncodeForAxisCoordSys, coordDimensions, this)\n    });\n  };\n  /**\n   * If horizontal, base axis is x, otherwise y.\n   * @override\n   */\n\n\n  WhiskerBoxCommonMixin.prototype.getBaseAxis = function () {\n    var dim = this._baseAxisDim;\n    return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis;\n  };\n\n  return WhiskerBoxCommonMixin;\n}();\n\nvar BoxplotSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(BoxplotSeriesModel, _super);\n\n  function BoxplotSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = BoxplotSeriesModel.type; // TODO\n    // box width represents group size, so dimension should have 'size'.\n\n    /**\n     * @see <https://en.wikipedia.org/wiki/Box_plot>\n     * The meanings of 'min' and 'max' depend on user,\n     * and echarts do not need to know it.\n     * @readOnly\n     */\n\n    _this.defaultValueDimensions = [{\n      name: 'min',\n      defaultTooltip: true\n    }, {\n      name: 'Q1',\n      defaultTooltip: true\n    }, {\n      name: 'median',\n      defaultTooltip: true\n    }, {\n      name: 'Q3',\n      defaultTooltip: true\n    }, {\n      name: 'max',\n      defaultTooltip: true\n    }];\n    _this.visualDrawType = 'stroke';\n    return _this;\n  }\n\n  BoxplotSeriesModel.type = 'series.boxplot';\n  BoxplotSeriesModel.dependencies = ['xAxis', 'yAxis', 'grid'];\n  BoxplotSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    coordinateSystem: 'cartesian2d',\n    legendHoverLink: true,\n    layout: null,\n    boxWidth: [7, 50],\n    itemStyle: {\n      color: '#fff',\n      borderWidth: 1\n    },\n    emphasis: {\n      scale: true,\n      itemStyle: {\n        borderWidth: 2,\n        shadowBlur: 5,\n        shadowOffsetX: 1,\n        shadowOffsetY: 1,\n        shadowColor: 'rgba(0,0,0,0.2)'\n      }\n    },\n    animationDuration: 800\n  };\n  return BoxplotSeriesModel;\n}(SeriesModel);\n\nmixin(BoxplotSeriesModel, WhiskerBoxCommonMixin, true);\n\nvar BoxplotView =\n/** @class */\nfunction (_super) {\n  __extends(BoxplotView, _super);\n\n  function BoxplotView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = BoxplotView.type;\n    return _this;\n  }\n\n  BoxplotView.prototype.render = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n    var group = this.group;\n    var oldData = this._data; // There is no old data only when first rendering or switching from\n    // stream mode to normal mode, where previous elements should be removed.\n\n    if (!this._data) {\n      group.removeAll();\n    }\n\n    var constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0;\n    data.diff(oldData).add(function (newIdx) {\n      if (data.hasValue(newIdx)) {\n        var itemLayout = data.getItemLayout(newIdx);\n        var symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true);\n        data.setItemGraphicEl(newIdx, symbolEl);\n        group.add(symbolEl);\n      }\n    }).update(function (newIdx, oldIdx) {\n      var symbolEl = oldData.getItemGraphicEl(oldIdx); // Empty data\n\n      if (!data.hasValue(newIdx)) {\n        group.remove(symbolEl);\n        return;\n      }\n\n      var itemLayout = data.getItemLayout(newIdx);\n\n      if (!symbolEl) {\n        symbolEl = createNormalBox(itemLayout, data, newIdx, constDim);\n      } else {\n        saveOldStyle(symbolEl);\n        updateNormalBoxData(itemLayout, symbolEl, data, newIdx);\n      }\n\n      group.add(symbolEl);\n      data.setItemGraphicEl(newIdx, symbolEl);\n    }).remove(function (oldIdx) {\n      var el = oldData.getItemGraphicEl(oldIdx);\n      el && group.remove(el);\n    }).execute();\n    this._data = data;\n  };\n\n  BoxplotView.prototype.remove = function (ecModel) {\n    var group = this.group;\n    var data = this._data;\n    this._data = null;\n    data && data.eachItemGraphicEl(function (el) {\n      el && group.remove(el);\n    });\n  };\n\n  BoxplotView.type = 'boxplot';\n  return BoxplotView;\n}(ChartView);\n\nvar BoxPathShape =\n/** @class */\nfunction () {\n  function BoxPathShape() {}\n\n  return BoxPathShape;\n}();\n\nvar BoxPath =\n/** @class */\nfunction (_super) {\n  __extends(BoxPath, _super);\n\n  function BoxPath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'boxplotBoxPath';\n    return _this;\n  }\n\n  BoxPath.prototype.getDefaultShape = function () {\n    return new BoxPathShape();\n  };\n\n  BoxPath.prototype.buildPath = function (ctx, shape) {\n    var ends = shape.points;\n    var i = 0;\n    ctx.moveTo(ends[i][0], ends[i][1]);\n    i++;\n\n    for (; i < 4; i++) {\n      ctx.lineTo(ends[i][0], ends[i][1]);\n    }\n\n    ctx.closePath();\n\n    for (; i < ends.length; i++) {\n      ctx.moveTo(ends[i][0], ends[i][1]);\n      i++;\n      ctx.lineTo(ends[i][0], ends[i][1]);\n    }\n  };\n\n  return BoxPath;\n}(Path);\n\nfunction createNormalBox(itemLayout, data, dataIndex, constDim, isInit) {\n  var ends = itemLayout.ends;\n  var el = new BoxPath({\n    shape: {\n      points: isInit ? transInit(ends, constDim, itemLayout) : ends\n    }\n  });\n  updateNormalBoxData(itemLayout, el, data, dataIndex, isInit);\n  return el;\n}\n\nfunction updateNormalBoxData(itemLayout, el, data, dataIndex, isInit) {\n  var seriesModel = data.hostModel;\n  var updateMethod = graphic[isInit ? 'initProps' : 'updateProps'];\n  updateMethod(el, {\n    shape: {\n      points: itemLayout.ends\n    }\n  }, seriesModel, dataIndex);\n  el.useStyle(data.getItemVisual(dataIndex, 'style'));\n  el.style.strokeNoScale = true;\n  el.z2 = 100;\n  var itemModel = data.getItemModel(dataIndex);\n  var emphasisModel = itemModel.getModel('emphasis');\n  setStatesStylesFromModel(el, itemModel);\n  toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n}\n\nfunction transInit(points, dim, itemLayout) {\n  return map(points, function (point) {\n    point = point.slice();\n    point[dim] = itemLayout.initBaseline;\n    return point;\n  });\n}\n\nvar each$6 = each;\nfunction boxplotLayout(ecModel) {\n  var groupResult = groupSeriesByAxis(ecModel);\n  each$6(groupResult, function (groupItem) {\n    var seriesModels = groupItem.seriesModels;\n\n    if (!seriesModels.length) {\n      return;\n    }\n\n    calculateBase(groupItem);\n    each$6(seriesModels, function (seriesModel, idx) {\n      layoutSingleSeries(seriesModel, groupItem.boxOffsetList[idx], groupItem.boxWidthList[idx]);\n    });\n  });\n}\n/**\n * Group series by axis.\n */\n\nfunction groupSeriesByAxis(ecModel) {\n  var result = [];\n  var axisList = [];\n  ecModel.eachSeriesByType('boxplot', function (seriesModel) {\n    var baseAxis = seriesModel.getBaseAxis();\n    var idx = indexOf(axisList, baseAxis);\n\n    if (idx < 0) {\n      idx = axisList.length;\n      axisList[idx] = baseAxis;\n      result[idx] = {\n        axis: baseAxis,\n        seriesModels: []\n      };\n    }\n\n    result[idx].seriesModels.push(seriesModel);\n  });\n  return result;\n}\n/**\n * Calculate offset and box width for each series.\n */\n\n\nfunction calculateBase(groupItem) {\n  var baseAxis = groupItem.axis;\n  var seriesModels = groupItem.seriesModels;\n  var seriesCount = seriesModels.length;\n  var boxWidthList = groupItem.boxWidthList = [];\n  var boxOffsetList = groupItem.boxOffsetList = [];\n  var boundList = [];\n  var bandWidth;\n\n  if (baseAxis.type === 'category') {\n    bandWidth = baseAxis.getBandWidth();\n  } else {\n    var maxDataCount_1 = 0;\n    each$6(seriesModels, function (seriesModel) {\n      maxDataCount_1 = Math.max(maxDataCount_1, seriesModel.getData().count());\n    });\n    var extent = baseAxis.getExtent();\n    bandWidth = Math.abs(extent[1] - extent[0]) / maxDataCount_1;\n  }\n\n  each$6(seriesModels, function (seriesModel) {\n    var boxWidthBound = seriesModel.get('boxWidth');\n\n    if (!isArray(boxWidthBound)) {\n      boxWidthBound = [boxWidthBound, boxWidthBound];\n    }\n\n    boundList.push([parsePercent$1(boxWidthBound[0], bandWidth) || 0, parsePercent$1(boxWidthBound[1], bandWidth) || 0]);\n  });\n  var availableWidth = bandWidth * 0.8 - 2;\n  var boxGap = availableWidth / seriesCount * 0.3;\n  var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount;\n  var base = boxWidth / 2 - availableWidth / 2;\n  each$6(seriesModels, function (seriesModel, idx) {\n    boxOffsetList.push(base);\n    base += boxGap + boxWidth;\n    boxWidthList.push(Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1]));\n  });\n}\n/**\n * Calculate points location for each series.\n */\n\n\nfunction layoutSingleSeries(seriesModel, offset, boxWidth) {\n  var coordSys = seriesModel.coordinateSystem;\n  var data = seriesModel.getData();\n  var halfWidth = boxWidth / 2;\n  var cDimIdx = seriesModel.get('layout') === 'horizontal' ? 0 : 1;\n  var vDimIdx = 1 - cDimIdx;\n  var coordDims = ['x', 'y'];\n  var cDim = data.mapDimension(coordDims[cDimIdx]);\n  var vDims = data.mapDimensionsAll(coordDims[vDimIdx]);\n\n  if (cDim == null || vDims.length < 5) {\n    return;\n  }\n\n  for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) {\n    var axisDimVal = data.get(cDim, dataIndex);\n    var median = getPoint(axisDimVal, vDims[2], dataIndex);\n    var end1 = getPoint(axisDimVal, vDims[0], dataIndex);\n    var end2 = getPoint(axisDimVal, vDims[1], dataIndex);\n    var end4 = getPoint(axisDimVal, vDims[3], dataIndex);\n    var end5 = getPoint(axisDimVal, vDims[4], dataIndex);\n    var ends = [];\n    addBodyEnd(ends, end2, false);\n    addBodyEnd(ends, end4, true);\n    ends.push(end1, end2, end5, end4);\n    layEndLine(ends, end1);\n    layEndLine(ends, end5);\n    layEndLine(ends, median);\n    data.setItemLayout(dataIndex, {\n      initBaseline: median[vDimIdx],\n      ends: ends\n    });\n  }\n\n  function getPoint(axisDimVal, dim, dataIndex) {\n    var val = data.get(dim, dataIndex);\n    var p = [];\n    p[cDimIdx] = axisDimVal;\n    p[vDimIdx] = val;\n    var point;\n\n    if (isNaN(axisDimVal) || isNaN(val)) {\n      point = [NaN, NaN];\n    } else {\n      point = coordSys.dataToPoint(p);\n      point[cDimIdx] += offset;\n    }\n\n    return point;\n  }\n\n  function addBodyEnd(ends, point, start) {\n    var point1 = point.slice();\n    var point2 = point.slice();\n    point1[cDimIdx] += halfWidth;\n    point2[cDimIdx] -= halfWidth;\n    start ? ends.push(point1, point2) : ends.push(point2, point1);\n  }\n\n  function layEndLine(ends, endCenter) {\n    var from = endCenter.slice();\n    var to = endCenter.slice();\n    from[cDimIdx] -= halfWidth;\n    to[cDimIdx] += halfWidth;\n    ends.push(from, to);\n  }\n}\n\n/**\n * See:\n *  <https://en.wikipedia.org/wiki/Box_plot#cite_note-frigge_hoaglin_iglewicz-2>\n *  <http://stat.ethz.ch/R-manual/R-devel/library/grDevices/html/boxplot.stats.html>\n *\n * Helper method for preparing data.\n *\n * @param rawData like\n *        [\n *            [12,232,443], (raw data set for the first box)\n *            [3843,5545,1232], (raw data set for the second box)\n *            ...\n *        ]\n * @param opt.boundIQR=1.5 Data less than min bound is outlier.\n *      default 1.5, means Q1 - 1.5 * (Q3 - Q1).\n *      If 'none'/0 passed, min bound will not be used.\n */\n\nfunction prepareBoxplotData(rawData, opt) {\n  opt = opt || {};\n  var boxData = [];\n  var outliers = [];\n  var boundIQR = opt.boundIQR;\n  var useExtreme = boundIQR === 'none' || boundIQR === 0;\n\n  for (var i = 0; i < rawData.length; i++) {\n    var ascList = asc(rawData[i].slice());\n    var Q1 = quantile(ascList, 0.25);\n    var Q2 = quantile(ascList, 0.5);\n    var Q3 = quantile(ascList, 0.75);\n    var min = ascList[0];\n    var max = ascList[ascList.length - 1];\n    var bound = (boundIQR == null ? 1.5 : boundIQR) * (Q3 - Q1);\n    var low = useExtreme ? min : Math.max(min, Q1 - bound);\n    var high = useExtreme ? max : Math.min(max, Q3 + bound);\n    var itemNameFormatter = opt.itemNameFormatter;\n    var itemName = isFunction(itemNameFormatter) ? itemNameFormatter({\n      value: i\n    }) : isString(itemNameFormatter) ? itemNameFormatter.replace('{value}', i + '') : i + '';\n    boxData.push([itemName, low, Q1, Q2, Q3, high]);\n\n    for (var j = 0; j < ascList.length; j++) {\n      var dataItem = ascList[j];\n\n      if (dataItem < low || dataItem > high) {\n        var outlier = [itemName, dataItem];\n        outliers.push(outlier);\n      }\n    }\n  }\n\n  return {\n    boxData: boxData,\n    outliers: outliers\n  };\n}\n\nvar boxplotTransform = {\n  type: 'echarts:boxplot',\n  transform: function transform(params) {\n    var upstream = params.upstream;\n\n    if (upstream.sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS) {\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        errMsg = makePrintable('source data is not applicable for this boxplot transform. Expect number[][].');\n      }\n\n      throwError(errMsg);\n    }\n\n    var result = prepareBoxplotData(upstream.getRawData(), params.config);\n    return [{\n      dimensions: ['ItemName', 'Low', 'Q1', 'Q2', 'Q3', 'High'],\n      data: result.boxData\n    }, {\n      data: result.outliers\n    }];\n  }\n};\n\nfunction install$j(registers) {\n  registers.registerSeriesModel(BoxplotSeriesModel);\n  registers.registerChartView(BoxplotView);\n  registers.registerLayout(boxplotLayout);\n  registers.registerTransform(boxplotTransform);\n}\n\nvar SKIP_PROPS = ['color', 'borderColor'];\n\nvar CandlestickView =\n/** @class */\nfunction (_super) {\n  __extends(CandlestickView, _super);\n\n  function CandlestickView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CandlestickView.type;\n    return _this;\n  }\n\n  CandlestickView.prototype.render = function (seriesModel, ecModel, api) {\n    // If there is clipPath created in large mode. Remove it.\n    this.group.removeClipPath(); // Clear previously rendered progressive elements.\n\n    this._progressiveEls = null;\n\n    this._updateDrawMode(seriesModel);\n\n    this._isLargeDraw ? this._renderLarge(seriesModel) : this._renderNormal(seriesModel);\n  };\n\n  CandlestickView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n    this._clear();\n\n    this._updateDrawMode(seriesModel);\n  };\n\n  CandlestickView.prototype.incrementalRender = function (params, seriesModel, ecModel, api) {\n    this._progressiveEls = [];\n    this._isLargeDraw ? this._incrementalRenderLarge(params, seriesModel) : this._incrementalRenderNormal(params, seriesModel);\n  };\n\n  CandlestickView.prototype.eachRendered = function (cb) {\n    traverseElements(this._progressiveEls || this.group, cb);\n  };\n\n  CandlestickView.prototype._updateDrawMode = function (seriesModel) {\n    var isLargeDraw = seriesModel.pipelineContext.large;\n\n    if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) {\n      this._isLargeDraw = isLargeDraw;\n\n      this._clear();\n    }\n  };\n\n  CandlestickView.prototype._renderNormal = function (seriesModel) {\n    var data = seriesModel.getData();\n    var oldData = this._data;\n    var group = this.group;\n    var isSimpleBox = data.getLayout('isSimpleBox');\n    var needsClip = seriesModel.get('clip', true);\n    var coord = seriesModel.coordinateSystem;\n    var clipArea = coord.getArea && coord.getArea(); // There is no old data only when first rendering or switching from\n    // stream mode to normal mode, where previous elements should be removed.\n\n    if (!this._data) {\n      group.removeAll();\n    }\n\n    data.diff(oldData).add(function (newIdx) {\n      if (data.hasValue(newIdx)) {\n        var itemLayout = data.getItemLayout(newIdx);\n\n        if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) {\n          return;\n        }\n\n        var el = createNormalBox$1(itemLayout, newIdx, true);\n        initProps(el, {\n          shape: {\n            points: itemLayout.ends\n          }\n        }, seriesModel, newIdx);\n        setBoxCommon(el, data, newIdx, isSimpleBox);\n        group.add(el);\n        data.setItemGraphicEl(newIdx, el);\n      }\n    }).update(function (newIdx, oldIdx) {\n      var el = oldData.getItemGraphicEl(oldIdx); // Empty data\n\n      if (!data.hasValue(newIdx)) {\n        group.remove(el);\n        return;\n      }\n\n      var itemLayout = data.getItemLayout(newIdx);\n\n      if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) {\n        group.remove(el);\n        return;\n      }\n\n      if (!el) {\n        el = createNormalBox$1(itemLayout);\n      } else {\n        updateProps(el, {\n          shape: {\n            points: itemLayout.ends\n          }\n        }, seriesModel, newIdx);\n        saveOldStyle(el);\n      }\n\n      setBoxCommon(el, data, newIdx, isSimpleBox);\n      group.add(el);\n      data.setItemGraphicEl(newIdx, el);\n    }).remove(function (oldIdx) {\n      var el = oldData.getItemGraphicEl(oldIdx);\n      el && group.remove(el);\n    }).execute();\n    this._data = data;\n  };\n\n  CandlestickView.prototype._renderLarge = function (seriesModel) {\n    this._clear();\n\n    createLarge$1(seriesModel, this.group);\n    var clipPath = seriesModel.get('clip', true) ? createClipPath(seriesModel.coordinateSystem, false, seriesModel) : null;\n\n    if (clipPath) {\n      this.group.setClipPath(clipPath);\n    } else {\n      this.group.removeClipPath();\n    }\n  };\n\n  CandlestickView.prototype._incrementalRenderNormal = function (params, seriesModel) {\n    var data = seriesModel.getData();\n    var isSimpleBox = data.getLayout('isSimpleBox');\n    var dataIndex;\n\n    while ((dataIndex = params.next()) != null) {\n      var itemLayout = data.getItemLayout(dataIndex);\n      var el = createNormalBox$1(itemLayout);\n      setBoxCommon(el, data, dataIndex, isSimpleBox);\n      el.incremental = true;\n      this.group.add(el);\n\n      this._progressiveEls.push(el);\n    }\n  };\n\n  CandlestickView.prototype._incrementalRenderLarge = function (params, seriesModel) {\n    createLarge$1(seriesModel, this.group, this._progressiveEls, true);\n  };\n\n  CandlestickView.prototype.remove = function (ecModel) {\n    this._clear();\n  };\n\n  CandlestickView.prototype._clear = function () {\n    this.group.removeAll();\n    this._data = null;\n  };\n\n  CandlestickView.type = 'candlestick';\n  return CandlestickView;\n}(ChartView);\n\nvar NormalBoxPathShape =\n/** @class */\nfunction () {\n  function NormalBoxPathShape() {}\n\n  return NormalBoxPathShape;\n}();\n\nvar NormalBoxPath =\n/** @class */\nfunction (_super) {\n  __extends(NormalBoxPath, _super);\n\n  function NormalBoxPath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'normalCandlestickBox';\n    return _this;\n  }\n\n  NormalBoxPath.prototype.getDefaultShape = function () {\n    return new NormalBoxPathShape();\n  };\n\n  NormalBoxPath.prototype.buildPath = function (ctx, shape) {\n    var ends = shape.points;\n\n    if (this.__simpleBox) {\n      ctx.moveTo(ends[4][0], ends[4][1]);\n      ctx.lineTo(ends[6][0], ends[6][1]);\n    } else {\n      ctx.moveTo(ends[0][0], ends[0][1]);\n      ctx.lineTo(ends[1][0], ends[1][1]);\n      ctx.lineTo(ends[2][0], ends[2][1]);\n      ctx.lineTo(ends[3][0], ends[3][1]);\n      ctx.closePath();\n      ctx.moveTo(ends[4][0], ends[4][1]);\n      ctx.lineTo(ends[5][0], ends[5][1]);\n      ctx.moveTo(ends[6][0], ends[6][1]);\n      ctx.lineTo(ends[7][0], ends[7][1]);\n    }\n  };\n\n  return NormalBoxPath;\n}(Path);\n\nfunction createNormalBox$1(itemLayout, dataIndex, isInit) {\n  var ends = itemLayout.ends;\n  return new NormalBoxPath({\n    shape: {\n      points: isInit ? transInit$1(ends, itemLayout) : ends\n    },\n    z2: 100\n  });\n}\n\nfunction isNormalBoxClipped(clipArea, itemLayout) {\n  var clipped = true;\n\n  for (var i = 0; i < itemLayout.ends.length; i++) {\n    // If any point are in the region.\n    if (clipArea.contain(itemLayout.ends[i][0], itemLayout.ends[i][1])) {\n      clipped = false;\n      break;\n    }\n  }\n\n  return clipped;\n}\n\nfunction setBoxCommon(el, data, dataIndex, isSimpleBox) {\n  var itemModel = data.getItemModel(dataIndex);\n  el.useStyle(data.getItemVisual(dataIndex, 'style'));\n  el.style.strokeNoScale = true;\n  el.__simpleBox = isSimpleBox;\n  setStatesStylesFromModel(el, itemModel);\n}\n\nfunction transInit$1(points, itemLayout) {\n  return map(points, function (point) {\n    point = point.slice();\n    point[1] = itemLayout.initBaseline;\n    return point;\n  });\n}\n\nvar LargeBoxPathShape =\n/** @class */\nfunction () {\n  function LargeBoxPathShape() {}\n\n  return LargeBoxPathShape;\n}();\n\nvar LargeBoxPath =\n/** @class */\nfunction (_super) {\n  __extends(LargeBoxPath, _super);\n\n  function LargeBoxPath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this.type = 'largeCandlestickBox';\n    return _this;\n  }\n\n  LargeBoxPath.prototype.getDefaultShape = function () {\n    return new LargeBoxPathShape();\n  };\n\n  LargeBoxPath.prototype.buildPath = function (ctx, shape) {\n    // Drawing lines is more efficient than drawing\n    // a whole line or drawing rects.\n    var points = shape.points;\n\n    for (var i = 0; i < points.length;) {\n      if (this.__sign === points[i++]) {\n        var x = points[i++];\n        ctx.moveTo(x, points[i++]);\n        ctx.lineTo(x, points[i++]);\n      } else {\n        i += 3;\n      }\n    }\n  };\n\n  return LargeBoxPath;\n}(Path);\n\nfunction createLarge$1(seriesModel, group, progressiveEls, incremental) {\n  var data = seriesModel.getData();\n  var largePoints = data.getLayout('largePoints');\n  var elP = new LargeBoxPath({\n    shape: {\n      points: largePoints\n    },\n    __sign: 1,\n    ignoreCoarsePointer: true\n  });\n  group.add(elP);\n  var elN = new LargeBoxPath({\n    shape: {\n      points: largePoints\n    },\n    __sign: -1,\n    ignoreCoarsePointer: true\n  });\n  group.add(elN);\n  var elDoji = new LargeBoxPath({\n    shape: {\n      points: largePoints\n    },\n    __sign: 0,\n    ignoreCoarsePointer: true\n  });\n  group.add(elDoji);\n  setLargeStyle(1, elP, seriesModel);\n  setLargeStyle(-1, elN, seriesModel);\n  setLargeStyle(0, elDoji, seriesModel);\n\n  if (incremental) {\n    elP.incremental = true;\n    elN.incremental = true;\n  }\n\n  if (progressiveEls) {\n    progressiveEls.push(elP, elN);\n  }\n}\n\nfunction setLargeStyle(sign, el, seriesModel, data) {\n  // TODO put in visual?\n  var borderColor = seriesModel.get(['itemStyle', sign > 0 ? 'borderColor' : 'borderColor0']) // Use color for border color by default.\n  || seriesModel.get(['itemStyle', sign > 0 ? 'color' : 'color0']);\n\n  if (sign === 0) {\n    borderColor = seriesModel.get(['itemStyle', 'borderColorDoji']);\n  } // Color must be excluded.\n  // Because symbol provide setColor individually to set fill and stroke\n\n\n  var itemStyle = seriesModel.getModel('itemStyle').getItemStyle(SKIP_PROPS);\n  el.useStyle(itemStyle);\n  el.style.fill = null;\n  el.style.stroke = borderColor;\n}\n\nvar CandlestickSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(CandlestickSeriesModel, _super);\n\n  function CandlestickSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CandlestickSeriesModel.type;\n    _this.defaultValueDimensions = [{\n      name: 'open',\n      defaultTooltip: true\n    }, {\n      name: 'close',\n      defaultTooltip: true\n    }, {\n      name: 'lowest',\n      defaultTooltip: true\n    }, {\n      name: 'highest',\n      defaultTooltip: true\n    }];\n    return _this;\n  }\n  /**\n   * Get dimension for shadow in dataZoom\n   * @return dimension name\n   */\n\n\n  CandlestickSeriesModel.prototype.getShadowDim = function () {\n    return 'open';\n  };\n\n  CandlestickSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n    var itemLayout = data.getItemLayout(dataIndex);\n    return itemLayout && selectors.rect(itemLayout.brushRect);\n  };\n\n  CandlestickSeriesModel.type = 'series.candlestick';\n  CandlestickSeriesModel.dependencies = ['xAxis', 'yAxis', 'grid'];\n  CandlestickSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    coordinateSystem: 'cartesian2d',\n    legendHoverLink: true,\n    // xAxisIndex: 0,\n    // yAxisIndex: 0,\n    layout: null,\n    clip: true,\n    itemStyle: {\n      color: '#eb5454',\n      color0: '#47b262',\n      borderColor: '#eb5454',\n      borderColor0: '#47b262',\n      borderColorDoji: null,\n      // borderColor: '#d24040',\n      // borderColor0: '#398f4f',\n      borderWidth: 1\n    },\n    emphasis: {\n      scale: true,\n      itemStyle: {\n        borderWidth: 2\n      }\n    },\n    barMaxWidth: null,\n    barMinWidth: null,\n    barWidth: null,\n    large: true,\n    largeThreshold: 600,\n    progressive: 3e3,\n    progressiveThreshold: 1e4,\n    progressiveChunkMode: 'mod',\n    animationEasing: 'linear',\n    animationDuration: 300\n  };\n  return CandlestickSeriesModel;\n}(SeriesModel);\n\nmixin(CandlestickSeriesModel, WhiskerBoxCommonMixin, true);\n\nfunction candlestickPreprocessor(option) {\n  if (!option || !isArray(option.series)) {\n    return;\n  } // Translate 'k' to 'candlestick'.\n\n\n  each(option.series, function (seriesItem) {\n    if (isObject(seriesItem) && seriesItem.type === 'k') {\n      seriesItem.type = 'candlestick';\n    }\n  });\n}\n\nvar positiveBorderColorQuery = ['itemStyle', 'borderColor'];\nvar negativeBorderColorQuery = ['itemStyle', 'borderColor0'];\nvar dojiBorderColorQuery = ['itemStyle', 'borderColorDoji'];\nvar positiveColorQuery = ['itemStyle', 'color'];\nvar negativeColorQuery = ['itemStyle', 'color0'];\nvar candlestickVisual = {\n  seriesType: 'candlestick',\n  plan: createRenderPlanner(),\n  // For legend.\n  performRawSeries: true,\n  reset: function (seriesModel, ecModel) {\n    function getColor(sign, model) {\n      return model.get(sign > 0 ? positiveColorQuery : negativeColorQuery);\n    }\n\n    function getBorderColor(sign, model) {\n      return model.get(sign === 0 ? dojiBorderColorQuery : sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery);\n    } // Only visible series has each data be visual encoded\n\n\n    if (ecModel.isSeriesFiltered(seriesModel)) {\n      return;\n    }\n\n    var isLargeRender = seriesModel.pipelineContext.large;\n    return !isLargeRender && {\n      progress: function (params, data) {\n        var dataIndex;\n\n        while ((dataIndex = params.next()) != null) {\n          var itemModel = data.getItemModel(dataIndex);\n          var sign = data.getItemLayout(dataIndex).sign;\n          var style = itemModel.getItemStyle();\n          style.fill = getColor(sign, itemModel);\n          style.stroke = getBorderColor(sign, itemModel) || style.fill;\n          var existsStyle = data.ensureUniqueItemVisual(dataIndex, 'style');\n          extend(existsStyle, style);\n        }\n      }\n    };\n  }\n};\n\nvar candlestickLayout = {\n  seriesType: 'candlestick',\n  plan: createRenderPlanner(),\n  reset: function (seriesModel) {\n    var coordSys = seriesModel.coordinateSystem;\n    var data = seriesModel.getData();\n    var candleWidth = calculateCandleWidth(seriesModel, data);\n    var cDimIdx = 0;\n    var vDimIdx = 1;\n    var coordDims = ['x', 'y'];\n    var cDimI = data.getDimensionIndex(data.mapDimension(coordDims[cDimIdx]));\n    var vDimsI = map(data.mapDimensionsAll(coordDims[vDimIdx]), data.getDimensionIndex, data);\n    var openDimI = vDimsI[0];\n    var closeDimI = vDimsI[1];\n    var lowestDimI = vDimsI[2];\n    var highestDimI = vDimsI[3];\n    data.setLayout({\n      candleWidth: candleWidth,\n      // The value is experimented visually.\n      isSimpleBox: candleWidth <= 1.3\n    });\n\n    if (cDimI < 0 || vDimsI.length < 4) {\n      return;\n    }\n\n    return {\n      progress: seriesModel.pipelineContext.large ? largeProgress : normalProgress\n    };\n\n    function normalProgress(params, data) {\n      var dataIndex;\n      var store = data.getStore();\n\n      while ((dataIndex = params.next()) != null) {\n        var axisDimVal = store.get(cDimI, dataIndex);\n        var openVal = store.get(openDimI, dataIndex);\n        var closeVal = store.get(closeDimI, dataIndex);\n        var lowestVal = store.get(lowestDimI, dataIndex);\n        var highestVal = store.get(highestDimI, dataIndex);\n        var ocLow = Math.min(openVal, closeVal);\n        var ocHigh = Math.max(openVal, closeVal);\n        var ocLowPoint = getPoint(ocLow, axisDimVal);\n        var ocHighPoint = getPoint(ocHigh, axisDimVal);\n        var lowestPoint = getPoint(lowestVal, axisDimVal);\n        var highestPoint = getPoint(highestVal, axisDimVal);\n        var ends = [];\n        addBodyEnd(ends, ocHighPoint, 0);\n        addBodyEnd(ends, ocLowPoint, 1);\n        ends.push(subPixelOptimizePoint(highestPoint), subPixelOptimizePoint(ocHighPoint), subPixelOptimizePoint(lowestPoint), subPixelOptimizePoint(ocLowPoint));\n        var itemModel = data.getItemModel(dataIndex);\n        var hasDojiColor = !!itemModel.get(['itemStyle', 'borderColorDoji']);\n        data.setItemLayout(dataIndex, {\n          sign: getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor),\n          initBaseline: openVal > closeVal ? ocHighPoint[vDimIdx] : ocLowPoint[vDimIdx],\n          ends: ends,\n          brushRect: makeBrushRect(lowestVal, highestVal, axisDimVal)\n        });\n      }\n\n      function getPoint(val, axisDimVal) {\n        var p = [];\n        p[cDimIdx] = axisDimVal;\n        p[vDimIdx] = val;\n        return isNaN(axisDimVal) || isNaN(val) ? [NaN, NaN] : coordSys.dataToPoint(p);\n      }\n\n      function addBodyEnd(ends, point, start) {\n        var point1 = point.slice();\n        var point2 = point.slice();\n        point1[cDimIdx] = subPixelOptimize$1(point1[cDimIdx] + candleWidth / 2, 1, false);\n        point2[cDimIdx] = subPixelOptimize$1(point2[cDimIdx] - candleWidth / 2, 1, true);\n        start ? ends.push(point1, point2) : ends.push(point2, point1);\n      }\n\n      function makeBrushRect(lowestVal, highestVal, axisDimVal) {\n        var pmin = getPoint(lowestVal, axisDimVal);\n        var pmax = getPoint(highestVal, axisDimVal);\n        pmin[cDimIdx] -= candleWidth / 2;\n        pmax[cDimIdx] -= candleWidth / 2;\n        return {\n          x: pmin[0],\n          y: pmin[1],\n          width:  candleWidth ,\n          height:  pmax[1] - pmin[1] \n        };\n      }\n\n      function subPixelOptimizePoint(point) {\n        point[cDimIdx] = subPixelOptimize$1(point[cDimIdx], 1);\n        return point;\n      }\n    }\n\n    function largeProgress(params, data) {\n      // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...]\n      var points = createFloat32Array(params.count * 4);\n      var offset = 0;\n      var point;\n      var tmpIn = [];\n      var tmpOut = [];\n      var dataIndex;\n      var store = data.getStore();\n      var hasDojiColor = !!seriesModel.get(['itemStyle', 'borderColorDoji']);\n\n      while ((dataIndex = params.next()) != null) {\n        var axisDimVal = store.get(cDimI, dataIndex);\n        var openVal = store.get(openDimI, dataIndex);\n        var closeVal = store.get(closeDimI, dataIndex);\n        var lowestVal = store.get(lowestDimI, dataIndex);\n        var highestVal = store.get(highestDimI, dataIndex);\n\n        if (isNaN(axisDimVal) || isNaN(lowestVal) || isNaN(highestVal)) {\n          points[offset++] = NaN;\n          offset += 3;\n          continue;\n        }\n\n        points[offset++] = getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor);\n        tmpIn[cDimIdx] = axisDimVal;\n        tmpIn[vDimIdx] = lowestVal;\n        point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n        points[offset++] = point ? point[0] : NaN;\n        points[offset++] = point ? point[1] : NaN;\n        tmpIn[vDimIdx] = highestVal;\n        point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n        points[offset++] = point ? point[1] : NaN;\n      }\n\n      data.setLayout('largePoints', points);\n    }\n  }\n};\n/**\n * Get the sign of a single data.\n *\n * @returns 0 for doji with hasDojiColor: true,\n *          1 for positive,\n *          -1 for negative.\n */\n\nfunction getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor) {\n  var sign;\n\n  if (openVal > closeVal) {\n    sign = -1;\n  } else if (openVal < closeVal) {\n    sign = 1;\n  } else {\n    sign = hasDojiColor // When doji color is set, use it instead of color/color0.\n    ? 0 : dataIndex > 0 // If close === open, compare with close of last record\n    ? store.get(closeDimI, dataIndex - 1) <= closeVal ? 1 : -1 : // No record of previous, set to be positive\n    1;\n  }\n\n  return sign;\n}\n\nfunction calculateCandleWidth(seriesModel, data) {\n  var baseAxis = seriesModel.getBaseAxis();\n  var extent;\n  var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : (extent = baseAxis.getExtent(), Math.abs(extent[1] - extent[0]) / data.count());\n  var barMaxWidth = parsePercent$1(retrieve2(seriesModel.get('barMaxWidth'), bandWidth), bandWidth);\n  var barMinWidth = parsePercent$1(retrieve2(seriesModel.get('barMinWidth'), 1), bandWidth);\n  var barWidth = seriesModel.get('barWidth');\n  return barWidth != null ? parsePercent$1(barWidth, bandWidth) // Put max outer to ensure bar visible in spite of overlap.\n  : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth);\n}\n\nfunction install$k(registers) {\n  registers.registerChartView(CandlestickView);\n  registers.registerSeriesModel(CandlestickSeriesModel);\n  registers.registerPreprocessor(candlestickPreprocessor);\n  registers.registerVisual(candlestickVisual);\n  registers.registerLayout(candlestickLayout);\n}\n\nfunction updateRipplePath(rippleGroup, effectCfg) {\n  var color = effectCfg.rippleEffectColor || effectCfg.color;\n  rippleGroup.eachChild(function (ripplePath) {\n    ripplePath.attr({\n      z: effectCfg.z,\n      zlevel: effectCfg.zlevel,\n      style: {\n        stroke: effectCfg.brushType === 'stroke' ? color : null,\n        fill: effectCfg.brushType === 'fill' ? color : null\n      }\n    });\n  });\n}\n\nvar EffectSymbol =\n/** @class */\nfunction (_super) {\n  __extends(EffectSymbol, _super);\n\n  function EffectSymbol(data, idx) {\n    var _this = _super.call(this) || this;\n\n    var symbol = new Symbol(data, idx);\n    var rippleGroup = new Group();\n\n    _this.add(symbol);\n\n    _this.add(rippleGroup);\n\n    _this.updateData(data, idx);\n\n    return _this;\n  }\n\n  EffectSymbol.prototype.stopEffectAnimation = function () {\n    this.childAt(1).removeAll();\n  };\n\n  EffectSymbol.prototype.startEffectAnimation = function (effectCfg) {\n    var symbolType = effectCfg.symbolType;\n    var color = effectCfg.color;\n    var rippleNumber = effectCfg.rippleNumber;\n    var rippleGroup = this.childAt(1);\n\n    for (var i = 0; i < rippleNumber; i++) {\n      // If width/height are set too small (e.g., set to 1) on ios10\n      // and macOS Sierra, a circle stroke become a rect, no matter what\n      // the scale is set. So we set width/height as 2. See #4136.\n      var ripplePath = createSymbol(symbolType, -1, -1, 2, 2, color);\n      ripplePath.attr({\n        style: {\n          strokeNoScale: true\n        },\n        z2: 99,\n        silent: true,\n        scaleX: 0.5,\n        scaleY: 0.5\n      });\n      var delay = -i / rippleNumber * effectCfg.period + effectCfg.effectOffset;\n      ripplePath.animate('', true).when(effectCfg.period, {\n        scaleX: effectCfg.rippleScale / 2,\n        scaleY: effectCfg.rippleScale / 2\n      }).delay(delay).start();\n      ripplePath.animateStyle(true).when(effectCfg.period, {\n        opacity: 0\n      }).delay(delay).start();\n      rippleGroup.add(ripplePath);\n    }\n\n    updateRipplePath(rippleGroup, effectCfg);\n  };\n  /**\n   * Update effect symbol\n   */\n\n\n  EffectSymbol.prototype.updateEffectAnimation = function (effectCfg) {\n    var oldEffectCfg = this._effectCfg;\n    var rippleGroup = this.childAt(1); // Must reinitialize effect if following configuration changed\n\n    var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale', 'rippleNumber'];\n\n    for (var i = 0; i < DIFFICULT_PROPS.length; i++) {\n      var propName = DIFFICULT_PROPS[i];\n\n      if (oldEffectCfg[propName] !== effectCfg[propName]) {\n        this.stopEffectAnimation();\n        this.startEffectAnimation(effectCfg);\n        return;\n      }\n    }\n\n    updateRipplePath(rippleGroup, effectCfg);\n  };\n  /**\n   * Highlight symbol\n   */\n\n\n  EffectSymbol.prototype.highlight = function () {\n    enterEmphasis(this);\n  };\n  /**\n   * Downplay symbol\n   */\n\n\n  EffectSymbol.prototype.downplay = function () {\n    leaveEmphasis(this);\n  };\n\n  EffectSymbol.prototype.getSymbolType = function () {\n    var symbol = this.childAt(0);\n    return symbol && symbol.getSymbolType();\n  };\n  /**\n   * Update symbol properties\n   */\n\n\n  EffectSymbol.prototype.updateData = function (data, idx) {\n    var _this = this;\n\n    var seriesModel = data.hostModel;\n    this.childAt(0).updateData(data, idx);\n    var rippleGroup = this.childAt(1);\n    var itemModel = data.getItemModel(idx);\n    var symbolType = data.getItemVisual(idx, 'symbol');\n    var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n    var symbolStyle = data.getItemVisual(idx, 'style');\n    var color = symbolStyle && symbolStyle.fill;\n    var emphasisModel = itemModel.getModel('emphasis');\n    rippleGroup.setScale(symbolSize);\n    rippleGroup.traverse(function (ripplePath) {\n      ripplePath.setStyle('fill', color);\n    });\n    var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize);\n\n    if (symbolOffset) {\n      rippleGroup.x = symbolOffset[0];\n      rippleGroup.y = symbolOffset[1];\n    }\n\n    var symbolRotate = data.getItemVisual(idx, 'symbolRotate');\n    rippleGroup.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;\n    var effectCfg = {};\n    effectCfg.showEffectOn = seriesModel.get('showEffectOn');\n    effectCfg.rippleScale = itemModel.get(['rippleEffect', 'scale']);\n    effectCfg.brushType = itemModel.get(['rippleEffect', 'brushType']);\n    effectCfg.period = itemModel.get(['rippleEffect', 'period']) * 1000;\n    effectCfg.effectOffset = idx / data.count();\n    effectCfg.z = seriesModel.getShallow('z') || 0;\n    effectCfg.zlevel = seriesModel.getShallow('zlevel') || 0;\n    effectCfg.symbolType = symbolType;\n    effectCfg.color = color;\n    effectCfg.rippleEffectColor = itemModel.get(['rippleEffect', 'color']);\n    effectCfg.rippleNumber = itemModel.get(['rippleEffect', 'number']);\n\n    if (effectCfg.showEffectOn === 'render') {\n      this._effectCfg ? this.updateEffectAnimation(effectCfg) : this.startEffectAnimation(effectCfg);\n      this._effectCfg = effectCfg;\n    } else {\n      // Not keep old effect config\n      this._effectCfg = null;\n      this.stopEffectAnimation();\n\n      this.onHoverStateChange = function (toState) {\n        if (toState === 'emphasis') {\n          if (effectCfg.showEffectOn !== 'render') {\n            _this.startEffectAnimation(effectCfg);\n          }\n        } else if (toState === 'normal') {\n          if (effectCfg.showEffectOn !== 'render') {\n            _this.stopEffectAnimation();\n          }\n        }\n      };\n    }\n\n    this._effectCfg = effectCfg;\n    toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n  };\n\n  EffectSymbol.prototype.fadeOut = function (cb) {\n    cb && cb();\n  };\n  return EffectSymbol;\n}(Group);\n\nvar EffectScatterView =\n/** @class */\nfunction (_super) {\n  __extends(EffectScatterView, _super);\n\n  function EffectScatterView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = EffectScatterView.type;\n    return _this;\n  }\n\n  EffectScatterView.prototype.init = function () {\n    this._symbolDraw = new SymbolDraw(EffectSymbol);\n  };\n\n  EffectScatterView.prototype.render = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n    var effectSymbolDraw = this._symbolDraw;\n    effectSymbolDraw.updateData(data, {\n      clipShape: this._getClipShape(seriesModel)\n    });\n    this.group.add(effectSymbolDraw.group);\n  };\n\n  EffectScatterView.prototype._getClipShape = function (seriesModel) {\n    var coordSys = seriesModel.coordinateSystem;\n    var clipArea = coordSys && coordSys.getArea && coordSys.getArea();\n    return seriesModel.get('clip', true) ? clipArea : null;\n  };\n\n  EffectScatterView.prototype.updateTransform = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n    this.group.dirty();\n    var res = pointsLayout('').reset(seriesModel, ecModel, api);\n\n    if (res.progress) {\n      res.progress({\n        start: 0,\n        end: data.count(),\n        count: data.count()\n      }, data);\n    }\n\n    this._symbolDraw.updateLayout();\n  };\n\n  EffectScatterView.prototype._updateGroupTransform = function (seriesModel) {\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (coordSys && coordSys.getRoamTransform) {\n      this.group.transform = clone$2(coordSys.getRoamTransform());\n      this.group.decomposeTransform();\n    }\n  };\n\n  EffectScatterView.prototype.remove = function (ecModel, api) {\n    this._symbolDraw && this._symbolDraw.remove(true);\n  };\n\n  EffectScatterView.type = 'effectScatter';\n  return EffectScatterView;\n}(ChartView);\n\nvar EffectScatterSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(EffectScatterSeriesModel, _super);\n\n  function EffectScatterSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = EffectScatterSeriesModel.type;\n    _this.hasSymbolVisual = true;\n    return _this;\n  }\n\n  EffectScatterSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesData(null, this, {\n      useEncodeDefaulter: true\n    });\n  };\n\n  EffectScatterSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n    return selectors.point(data.getItemLayout(dataIndex));\n  };\n\n  EffectScatterSeriesModel.type = 'series.effectScatter';\n  EffectScatterSeriesModel.dependencies = ['grid', 'polar'];\n  EffectScatterSeriesModel.defaultOption = {\n    coordinateSystem: 'cartesian2d',\n    // zlevel: 0,\n    z: 2,\n    legendHoverLink: true,\n    effectType: 'ripple',\n    progressive: 0,\n    // When to show the effect, option: 'render'|'emphasis'\n    showEffectOn: 'render',\n    clip: true,\n    // Ripple effect config\n    rippleEffect: {\n      period: 4,\n      // Scale of ripple\n      scale: 2.5,\n      // Brush type can be fill or stroke\n      brushType: 'fill',\n      // Ripple number\n      number: 3\n    },\n    universalTransition: {\n      divideShape: 'clone'\n    },\n    // Cartesian coordinate system\n    // xAxisIndex: 0,\n    // yAxisIndex: 0,\n    // Polar coordinate system\n    // polarIndex: 0,\n    // Geo coordinate system\n    // geoIndex: 0,\n    // symbol: null,        // 图形类型\n    symbolSize: 10 // 图形大小，半宽（半径）参数，当图形为方向或菱形则总宽度为symbolSize * 2\n    // symbolRotate: null,  // 图形旋转控制\n    // itemStyle: {\n    //     opacity: 1\n    // }\n\n  };\n  return EffectScatterSeriesModel;\n}(SeriesModel);\n\nfunction install$l(registers) {\n  registers.registerChartView(EffectScatterView);\n  registers.registerSeriesModel(EffectScatterSeriesModel);\n  registers.registerLayout(pointsLayout('effectScatter'));\n}\n\nvar EffectLine =\n/** @class */\nfunction (_super) {\n  __extends(EffectLine, _super);\n\n  function EffectLine(lineData, idx, seriesScope) {\n    var _this = _super.call(this) || this;\n\n    _this.add(_this.createLine(lineData, idx, seriesScope));\n\n    _this._updateEffectSymbol(lineData, idx);\n\n    return _this;\n  }\n\n  EffectLine.prototype.createLine = function (lineData, idx, seriesScope) {\n    return new Line$1(lineData, idx, seriesScope);\n  };\n\n  EffectLine.prototype._updateEffectSymbol = function (lineData, idx) {\n    var itemModel = lineData.getItemModel(idx);\n    var effectModel = itemModel.getModel('effect');\n    var size = effectModel.get('symbolSize');\n    var symbolType = effectModel.get('symbol');\n\n    if (!isArray(size)) {\n      size = [size, size];\n    }\n\n    var lineStyle = lineData.getItemVisual(idx, 'style');\n    var color = effectModel.get('color') || lineStyle && lineStyle.stroke;\n    var symbol = this.childAt(1);\n\n    if (this._symbolType !== symbolType) {\n      // Remove previous\n      this.remove(symbol);\n      symbol = createSymbol(symbolType, -0.5, -0.5, 1, 1, color);\n      symbol.z2 = 100;\n      symbol.culling = true;\n      this.add(symbol);\n    } // Symbol may be removed if loop is false\n\n\n    if (!symbol) {\n      return;\n    } // Shadow color is same with color in default\n\n\n    symbol.setStyle('shadowColor', color);\n    symbol.setStyle(effectModel.getItemStyle(['color']));\n    symbol.scaleX = size[0];\n    symbol.scaleY = size[1];\n    symbol.setColor(color);\n    this._symbolType = symbolType;\n    this._symbolScale = size;\n\n    this._updateEffectAnimation(lineData, effectModel, idx);\n  };\n\n  EffectLine.prototype._updateEffectAnimation = function (lineData, effectModel, idx) {\n    var symbol = this.childAt(1);\n\n    if (!symbol) {\n      return;\n    }\n\n    var points = lineData.getItemLayout(idx);\n    var period = effectModel.get('period') * 1000;\n    var loop = effectModel.get('loop');\n    var roundTrip = effectModel.get('roundTrip');\n    var constantSpeed = effectModel.get('constantSpeed');\n    var delayExpr = retrieve(effectModel.get('delay'), function (idx) {\n      return idx / lineData.count() * period / 3;\n    }); // Ignore when updating\n\n    symbol.ignore = true;\n\n    this._updateAnimationPoints(symbol, points);\n\n    if (constantSpeed > 0) {\n      period = this._getLineLength(symbol) / constantSpeed * 1000;\n    }\n\n    if (period !== this._period || loop !== this._loop || roundTrip !== this._roundTrip) {\n      symbol.stopAnimation();\n      var delayNum = void 0;\n\n      if (isFunction(delayExpr)) {\n        delayNum = delayExpr(idx);\n      } else {\n        delayNum = delayExpr;\n      }\n\n      if (symbol.__t > 0) {\n        delayNum = -period * symbol.__t;\n      }\n\n      this._animateSymbol(symbol, period, delayNum, loop, roundTrip);\n    }\n\n    this._period = period;\n    this._loop = loop;\n    this._roundTrip = roundTrip;\n  };\n\n  EffectLine.prototype._animateSymbol = function (symbol, period, delayNum, loop, roundTrip) {\n    if (period > 0) {\n      symbol.__t = 0;\n      var self_1 = this;\n      var animator = symbol.animate('', loop).when(roundTrip ? period * 2 : period, {\n        __t: roundTrip ? 2 : 1\n      }).delay(delayNum).during(function () {\n        self_1._updateSymbolPosition(symbol);\n      });\n\n      if (!loop) {\n        animator.done(function () {\n          self_1.remove(symbol);\n        });\n      }\n\n      animator.start();\n    }\n  };\n\n  EffectLine.prototype._getLineLength = function (symbol) {\n    // Not so accurate\n    return dist(symbol.__p1, symbol.__cp1) + dist(symbol.__cp1, symbol.__p2);\n  };\n\n  EffectLine.prototype._updateAnimationPoints = function (symbol, points) {\n    symbol.__p1 = points[0];\n    symbol.__p2 = points[1];\n    symbol.__cp1 = points[2] || [(points[0][0] + points[1][0]) / 2, (points[0][1] + points[1][1]) / 2];\n  };\n\n  EffectLine.prototype.updateData = function (lineData, idx, seriesScope) {\n    this.childAt(0).updateData(lineData, idx, seriesScope);\n\n    this._updateEffectSymbol(lineData, idx);\n  };\n\n  EffectLine.prototype._updateSymbolPosition = function (symbol) {\n    var p1 = symbol.__p1;\n    var p2 = symbol.__p2;\n    var cp1 = symbol.__cp1;\n    var t = symbol.__t < 1 ? symbol.__t : 2 - symbol.__t;\n    var pos = [symbol.x, symbol.y];\n    var lastPos = pos.slice();\n    var quadraticAt$1 = quadraticAt;\n    var quadraticDerivativeAt$1 = quadraticDerivativeAt;\n    pos[0] = quadraticAt$1(p1[0], cp1[0], p2[0], t);\n    pos[1] = quadraticAt$1(p1[1], cp1[1], p2[1], t); // Tangent\n\n    var tx = symbol.__t < 1 ? quadraticDerivativeAt$1(p1[0], cp1[0], p2[0], t) : quadraticDerivativeAt$1(p2[0], cp1[0], p1[0], 1 - t);\n    var ty = symbol.__t < 1 ? quadraticDerivativeAt$1(p1[1], cp1[1], p2[1], t) : quadraticDerivativeAt$1(p2[1], cp1[1], p1[1], 1 - t);\n    symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; // enable continuity trail for 'line', 'rect', 'roundRect' symbolType\n\n    if (this._symbolType === 'line' || this._symbolType === 'rect' || this._symbolType === 'roundRect') {\n      if (symbol.__lastT !== undefined && symbol.__lastT < symbol.__t) {\n        symbol.scaleY = dist(lastPos, pos) * 1.05; // make sure the last segment render within endPoint\n\n        if (t === 1) {\n          pos[0] = lastPos[0] + (pos[0] - lastPos[0]) / 2;\n          pos[1] = lastPos[1] + (pos[1] - lastPos[1]) / 2;\n        }\n      } else if (symbol.__lastT === 1) {\n        // After first loop, symbol.__t does NOT start with 0, so connect p1 to pos directly.\n        symbol.scaleY = 2 * dist(p1, pos);\n      } else {\n        symbol.scaleY = this._symbolScale[1];\n      }\n    }\n\n    symbol.__lastT = symbol.__t;\n    symbol.ignore = false;\n    symbol.x = pos[0];\n    symbol.y = pos[1];\n  };\n\n  EffectLine.prototype.updateLayout = function (lineData, idx) {\n    this.childAt(0).updateLayout(lineData, idx);\n    var effectModel = lineData.getItemModel(idx).getModel('effect');\n\n    this._updateEffectAnimation(lineData, effectModel, idx);\n  };\n\n  return EffectLine;\n}(Group);\n\nvar Polyline$1 =\n/** @class */\nfunction (_super) {\n  __extends(Polyline$1, _super);\n\n  function Polyline$1(lineData, idx, seriesScope) {\n    var _this = _super.call(this) || this;\n\n    _this._createPolyline(lineData, idx, seriesScope);\n\n    return _this;\n  }\n\n  Polyline$1.prototype._createPolyline = function (lineData, idx, seriesScope) {\n    // let seriesModel = lineData.hostModel;\n    var points = lineData.getItemLayout(idx);\n    var line = new Polyline({\n      shape: {\n        points: points\n      }\n    });\n    this.add(line);\n\n    this._updateCommonStl(lineData, idx, seriesScope);\n  };\n\n  Polyline$1.prototype.updateData = function (lineData, idx, seriesScope) {\n    var seriesModel = lineData.hostModel;\n    var line = this.childAt(0);\n    var target = {\n      shape: {\n        points: lineData.getItemLayout(idx)\n      }\n    };\n    updateProps(line, target, seriesModel, idx);\n\n    this._updateCommonStl(lineData, idx, seriesScope);\n  };\n\n  Polyline$1.prototype._updateCommonStl = function (lineData, idx, seriesScope) {\n    var line = this.childAt(0);\n    var itemModel = lineData.getItemModel(idx);\n    var emphasisLineStyle = seriesScope && seriesScope.emphasisLineStyle;\n    var focus = seriesScope && seriesScope.focus;\n    var blurScope = seriesScope && seriesScope.blurScope;\n    var emphasisDisabled = seriesScope && seriesScope.emphasisDisabled;\n\n    if (!seriesScope || lineData.hasItemOption) {\n      var emphasisModel = itemModel.getModel('emphasis');\n      emphasisLineStyle = emphasisModel.getModel('lineStyle').getLineStyle();\n      emphasisDisabled = emphasisModel.get('disabled');\n      focus = emphasisModel.get('focus');\n      blurScope = emphasisModel.get('blurScope');\n    }\n\n    line.useStyle(lineData.getItemVisual(idx, 'style'));\n    line.style.fill = null;\n    line.style.strokeNoScale = true;\n    var lineEmphasisState = line.ensureState('emphasis');\n    lineEmphasisState.style = emphasisLineStyle;\n    toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n  };\n\n  Polyline$1.prototype.updateLayout = function (lineData, idx) {\n    var polyline = this.childAt(0);\n    polyline.setShape('points', lineData.getItemLayout(idx));\n  };\n  return Polyline$1;\n}(Group);\n\nvar EffectPolyline =\n/** @class */\nfunction (_super) {\n  __extends(EffectPolyline, _super);\n\n  function EffectPolyline() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this._lastFrame = 0;\n    _this._lastFramePercent = 0;\n    return _this;\n  } // Override\n\n\n  EffectPolyline.prototype.createLine = function (lineData, idx, seriesScope) {\n    return new Polyline$1(lineData, idx, seriesScope);\n  };\n\n  EffectPolyline.prototype._updateAnimationPoints = function (symbol, points) {\n    this._points = points;\n    var accLenArr = [0];\n    var len = 0;\n\n    for (var i = 1; i < points.length; i++) {\n      var p1 = points[i - 1];\n      var p2 = points[i];\n      len += dist(p1, p2);\n      accLenArr.push(len);\n    }\n\n    if (len === 0) {\n      this._length = 0;\n      return;\n    }\n\n    for (var i = 0; i < accLenArr.length; i++) {\n      accLenArr[i] /= len;\n    }\n\n    this._offsets = accLenArr;\n    this._length = len;\n  };\n\n  EffectPolyline.prototype._getLineLength = function () {\n    return this._length;\n  };\n\n  EffectPolyline.prototype._updateSymbolPosition = function (symbol) {\n    var t = symbol.__t < 1 ? symbol.__t : 2 - symbol.__t;\n    var points = this._points;\n    var offsets = this._offsets;\n    var len = points.length;\n\n    if (!offsets) {\n      // Has length 0\n      return;\n    }\n\n    var lastFrame = this._lastFrame;\n    var frame;\n\n    if (t < this._lastFramePercent) {\n      // Start from the next frame\n      // PENDING start from lastFrame ?\n      var start = Math.min(lastFrame + 1, len - 1);\n\n      for (frame = start; frame >= 0; frame--) {\n        if (offsets[frame] <= t) {\n          break;\n        }\n      } // PENDING really need to do this ?\n\n\n      frame = Math.min(frame, len - 2);\n    } else {\n      for (frame = lastFrame; frame < len; frame++) {\n        if (offsets[frame] > t) {\n          break;\n        }\n      }\n\n      frame = Math.min(frame - 1, len - 2);\n    }\n\n    var p = (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]);\n    var p0 = points[frame];\n    var p1 = points[frame + 1];\n    symbol.x = p0[0] * (1 - p) + p * p1[0];\n    symbol.y = p0[1] * (1 - p) + p * p1[1];\n    var tx = symbol.__t < 1 ? p1[0] - p0[0] : p0[0] - p1[0];\n    var ty = symbol.__t < 1 ? p1[1] - p0[1] : p0[1] - p1[1];\n    symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2;\n    this._lastFrame = frame;\n    this._lastFramePercent = t;\n    symbol.ignore = false;\n  };\n  return EffectPolyline;\n}(EffectLine);\n\nvar LargeLinesPathShape =\n/** @class */\nfunction () {\n  function LargeLinesPathShape() {\n    this.polyline = false;\n    this.curveness = 0;\n    this.segs = [];\n  }\n\n  return LargeLinesPathShape;\n}();\n\nvar LargeLinesPath =\n/** @class */\nfunction (_super) {\n  __extends(LargeLinesPath, _super);\n\n  function LargeLinesPath(opts) {\n    var _this = _super.call(this, opts) || this;\n\n    _this._off = 0;\n    _this.hoverDataIdx = -1;\n    return _this;\n  }\n\n  LargeLinesPath.prototype.reset = function () {\n    this.notClear = false;\n    this._off = 0;\n  };\n\n  LargeLinesPath.prototype.getDefaultStyle = function () {\n    return {\n      stroke: '#000',\n      fill: null\n    };\n  };\n\n  LargeLinesPath.prototype.getDefaultShape = function () {\n    return new LargeLinesPathShape();\n  };\n\n  LargeLinesPath.prototype.buildPath = function (ctx, shape) {\n    var segs = shape.segs;\n    var curveness = shape.curveness;\n    var i;\n\n    if (shape.polyline) {\n      for (i = this._off; i < segs.length;) {\n        var count = segs[i++];\n\n        if (count > 0) {\n          ctx.moveTo(segs[i++], segs[i++]);\n\n          for (var k = 1; k < count; k++) {\n            ctx.lineTo(segs[i++], segs[i++]);\n          }\n        }\n      }\n    } else {\n      for (i = this._off; i < segs.length;) {\n        var x0 = segs[i++];\n        var y0 = segs[i++];\n        var x1 = segs[i++];\n        var y1 = segs[i++];\n        ctx.moveTo(x0, y0);\n\n        if (curveness > 0) {\n          var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;\n          var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;\n          ctx.quadraticCurveTo(x2, y2, x1, y1);\n        } else {\n          ctx.lineTo(x1, y1);\n        }\n      }\n    }\n\n    if (this.incremental) {\n      this._off = i;\n      this.notClear = true;\n    }\n  };\n\n  LargeLinesPath.prototype.findDataIndex = function (x, y) {\n    var shape = this.shape;\n    var segs = shape.segs;\n    var curveness = shape.curveness;\n    var lineWidth = this.style.lineWidth;\n\n    if (shape.polyline) {\n      var dataIndex = 0;\n\n      for (var i = 0; i < segs.length;) {\n        var count = segs[i++];\n\n        if (count > 0) {\n          var x0 = segs[i++];\n          var y0 = segs[i++];\n\n          for (var k = 1; k < count; k++) {\n            var x1 = segs[i++];\n            var y1 = segs[i++];\n\n            if (containStroke(x0, y0, x1, y1, lineWidth, x, y)) {\n              return dataIndex;\n            }\n          }\n        }\n\n        dataIndex++;\n      }\n    } else {\n      var dataIndex = 0;\n\n      for (var i = 0; i < segs.length;) {\n        var x0 = segs[i++];\n        var y0 = segs[i++];\n        var x1 = segs[i++];\n        var y1 = segs[i++];\n\n        if (curveness > 0) {\n          var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;\n          var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;\n\n          if (containStroke$2(x0, y0, x2, y2, x1, y1, lineWidth, x, y)) {\n            return dataIndex;\n          }\n        } else {\n          if (containStroke(x0, y0, x1, y1, lineWidth, x, y)) {\n            return dataIndex;\n          }\n        }\n\n        dataIndex++;\n      }\n    }\n\n    return -1;\n  };\n\n  LargeLinesPath.prototype.contain = function (x, y) {\n    var localPos = this.transformCoordToLocal(x, y);\n    var rect = this.getBoundingRect();\n    x = localPos[0];\n    y = localPos[1];\n\n    if (rect.contain(x, y)) {\n      // Cache found data index.\n      var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y);\n      return dataIdx >= 0;\n    }\n\n    this.hoverDataIdx = -1;\n    return false;\n  };\n\n  LargeLinesPath.prototype.getBoundingRect = function () {\n    // Ignore stroke for large symbol draw.\n    var rect = this._rect;\n\n    if (!rect) {\n      var shape = this.shape;\n      var points = shape.segs;\n      var minX = Infinity;\n      var minY = Infinity;\n      var maxX = -Infinity;\n      var maxY = -Infinity;\n\n      for (var i = 0; i < points.length;) {\n        var x = points[i++];\n        var y = points[i++];\n        minX = Math.min(x, minX);\n        maxX = Math.max(x, maxX);\n        minY = Math.min(y, minY);\n        maxY = Math.max(y, maxY);\n      }\n\n      rect = this._rect = new BoundingRect(minX, minY, maxX, maxY);\n    }\n\n    return rect;\n  };\n\n  return LargeLinesPath;\n}(Path);\n\nvar LargeLineDraw =\n/** @class */\nfunction () {\n  function LargeLineDraw() {\n    this.group = new Group();\n  }\n  /**\n   * Update symbols draw by new data\n   */\n\n\n  LargeLineDraw.prototype.updateData = function (data) {\n    this._clear();\n\n    var lineEl = this._create();\n\n    lineEl.setShape({\n      segs: data.getLayout('linesPoints')\n    });\n\n    this._setCommon(lineEl, data);\n  };\n  /**\n   * @override\n   */\n\n  LargeLineDraw.prototype.incrementalPrepareUpdate = function (data) {\n    this.group.removeAll();\n\n    this._clear();\n  };\n  /**\n   * @override\n   */\n\n  LargeLineDraw.prototype.incrementalUpdate = function (taskParams, data) {\n    var lastAdded = this._newAdded[0];\n    var linePoints = data.getLayout('linesPoints');\n    var oldSegs = lastAdded && lastAdded.shape.segs; // Merging the exists. Each element has 1e4 points.\n    // Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization)\n\n    if (oldSegs && oldSegs.length < 2e4) {\n      var oldLen = oldSegs.length;\n      var newSegs = new Float32Array(oldLen + linePoints.length); // Concat two array\n\n      newSegs.set(oldSegs);\n      newSegs.set(linePoints, oldLen);\n      lastAdded.setShape({\n        segs: newSegs\n      });\n    } else {\n      // Clear\n      this._newAdded = [];\n\n      var lineEl = this._create();\n\n      lineEl.incremental = true;\n      lineEl.setShape({\n        segs: linePoints\n      });\n\n      this._setCommon(lineEl, data);\n\n      lineEl.__startIndex = taskParams.start;\n    }\n  };\n  /**\n   * @override\n   */\n\n\n  LargeLineDraw.prototype.remove = function () {\n    this._clear();\n  };\n\n  LargeLineDraw.prototype.eachRendered = function (cb) {\n    this._newAdded[0] && cb(this._newAdded[0]);\n  };\n\n  LargeLineDraw.prototype._create = function () {\n    var lineEl = new LargeLinesPath({\n      cursor: 'default',\n      ignoreCoarsePointer: true\n    });\n\n    this._newAdded.push(lineEl);\n\n    this.group.add(lineEl);\n    return lineEl;\n  };\n\n  LargeLineDraw.prototype._setCommon = function (lineEl, data, isIncremental) {\n    var hostModel = data.hostModel;\n    lineEl.setShape({\n      polyline: hostModel.get('polyline'),\n      curveness: hostModel.get(['lineStyle', 'curveness'])\n    });\n    lineEl.useStyle(hostModel.getModel('lineStyle').getLineStyle());\n    lineEl.style.strokeNoScale = true;\n    var style = data.getVisual('style');\n\n    if (style && style.stroke) {\n      lineEl.setStyle('stroke', style.stroke);\n    }\n\n    lineEl.setStyle('fill', null);\n    var ecData = getECData(lineEl); // Enable tooltip\n    // PENDING May have performance issue when path is extremely large\n\n    ecData.seriesIndex = hostModel.seriesIndex;\n    lineEl.on('mousemove', function (e) {\n      ecData.dataIndex = null;\n      var dataIndex = lineEl.hoverDataIdx;\n\n      if (dataIndex > 0) {\n        // Provide dataIndex for tooltip\n        ecData.dataIndex = dataIndex + lineEl.__startIndex;\n      }\n    });\n  };\n\n  LargeLineDraw.prototype._clear = function () {\n    this._newAdded = [];\n    this.group.removeAll();\n  };\n  return LargeLineDraw;\n}();\n\nvar linesLayout = {\n  seriesType: 'lines',\n  plan: createRenderPlanner(),\n  reset: function (seriesModel) {\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (!coordSys) {\n      if (\"development\" !== 'production') {\n        error('The lines series must have a coordinate system.');\n      }\n\n      return;\n    }\n\n    var isPolyline = seriesModel.get('polyline');\n    var isLarge = seriesModel.pipelineContext.large;\n    return {\n      progress: function (params, lineData) {\n        var lineCoords = [];\n\n        if (isLarge) {\n          var points = void 0;\n          var segCount = params.end - params.start;\n\n          if (isPolyline) {\n            var totalCoordsCount = 0;\n\n            for (var i = params.start; i < params.end; i++) {\n              totalCoordsCount += seriesModel.getLineCoordsCount(i);\n            }\n\n            points = new Float32Array(segCount + totalCoordsCount * 2);\n          } else {\n            points = new Float32Array(segCount * 4);\n          }\n\n          var offset = 0;\n          var pt = [];\n\n          for (var i = params.start; i < params.end; i++) {\n            var len = seriesModel.getLineCoords(i, lineCoords);\n\n            if (isPolyline) {\n              points[offset++] = len;\n            }\n\n            for (var k = 0; k < len; k++) {\n              pt = coordSys.dataToPoint(lineCoords[k], false, pt);\n              points[offset++] = pt[0];\n              points[offset++] = pt[1];\n            }\n          }\n\n          lineData.setLayout('linesPoints', points);\n        } else {\n          for (var i = params.start; i < params.end; i++) {\n            var itemModel = lineData.getItemModel(i);\n            var len = seriesModel.getLineCoords(i, lineCoords);\n            var pts = [];\n\n            if (isPolyline) {\n              for (var j = 0; j < len; j++) {\n                pts.push(coordSys.dataToPoint(lineCoords[j]));\n              }\n            } else {\n              pts[0] = coordSys.dataToPoint(lineCoords[0]);\n              pts[1] = coordSys.dataToPoint(lineCoords[1]);\n              var curveness = itemModel.get(['lineStyle', 'curveness']);\n\n              if (+curveness) {\n                pts[2] = [(pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness, (pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness];\n              }\n            }\n\n            lineData.setItemLayout(i, pts);\n          }\n        }\n      }\n    };\n  }\n};\n\nvar LinesView =\n/** @class */\nfunction (_super) {\n  __extends(LinesView, _super);\n\n  function LinesView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = LinesView.type;\n    return _this;\n  }\n\n  LinesView.prototype.render = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n\n    var lineDraw = this._updateLineDraw(data, seriesModel);\n\n    var zlevel = seriesModel.get('zlevel');\n    var trailLength = seriesModel.get(['effect', 'trailLength']);\n    var zr = api.getZr(); // Avoid the drag cause ghost shadow\n    // FIXME Better way ?\n    // SVG doesn't support\n\n    var isSvg = zr.painter.getType() === 'svg';\n\n    if (!isSvg) {\n      zr.painter.getLayer(zlevel).clear(true);\n    } // Config layer with motion blur\n\n\n    if (this._lastZlevel != null && !isSvg) {\n      zr.configLayer(this._lastZlevel, {\n        motionBlur: false\n      });\n    }\n\n    if (this._showEffect(seriesModel) && trailLength > 0) {\n      if (!isSvg) {\n        zr.configLayer(zlevel, {\n          motionBlur: true,\n          lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0)\n        });\n      } else if (\"development\" !== 'production') {\n        console.warn('SVG render mode doesn\\'t support lines with trail effect');\n      }\n    }\n\n    lineDraw.updateData(data);\n    var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel);\n\n    if (clipPath) {\n      this.group.setClipPath(clipPath);\n    } else {\n      this.group.removeClipPath();\n    }\n\n    this._lastZlevel = zlevel;\n    this._finished = true;\n  };\n\n  LinesView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n\n    var lineDraw = this._updateLineDraw(data, seriesModel);\n\n    lineDraw.incrementalPrepareUpdate(data);\n\n    this._clearLayer(api);\n\n    this._finished = false;\n  };\n\n  LinesView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) {\n    this._lineDraw.incrementalUpdate(taskParams, seriesModel.getData());\n\n    this._finished = taskParams.end === seriesModel.getData().count();\n  };\n\n  LinesView.prototype.eachRendered = function (cb) {\n    this._lineDraw && this._lineDraw.eachRendered(cb);\n  };\n\n  LinesView.prototype.updateTransform = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n    var pipelineContext = seriesModel.pipelineContext;\n\n    if (!this._finished || pipelineContext.large || pipelineContext.progressiveRender) {\n      // TODO Don't have to do update in large mode. Only do it when there are millions of data.\n      return {\n        update: true\n      };\n    } else {\n      // TODO Use same logic with ScatterView.\n      // Manually update layout\n      var res = linesLayout.reset(seriesModel, ecModel, api);\n\n      if (res.progress) {\n        res.progress({\n          start: 0,\n          end: data.count(),\n          count: data.count()\n        }, data);\n      } // Not in large mode\n\n\n      this._lineDraw.updateLayout();\n\n      this._clearLayer(api);\n    }\n  };\n\n  LinesView.prototype._updateLineDraw = function (data, seriesModel) {\n    var lineDraw = this._lineDraw;\n\n    var hasEffect = this._showEffect(seriesModel);\n\n    var isPolyline = !!seriesModel.get('polyline');\n    var pipelineContext = seriesModel.pipelineContext;\n    var isLargeDraw = pipelineContext.large;\n\n    if (\"development\" !== 'production') {\n      if (hasEffect && isLargeDraw) {\n        console.warn('Large lines not support effect');\n      }\n    }\n\n    if (!lineDraw || hasEffect !== this._hasEffet || isPolyline !== this._isPolyline || isLargeDraw !== this._isLargeDraw) {\n      if (lineDraw) {\n        lineDraw.remove();\n      }\n\n      lineDraw = this._lineDraw = isLargeDraw ? new LargeLineDraw() : new LineDraw(isPolyline ? hasEffect ? EffectPolyline : Polyline$1 : hasEffect ? EffectLine : Line$1);\n      this._hasEffet = hasEffect;\n      this._isPolyline = isPolyline;\n      this._isLargeDraw = isLargeDraw;\n    }\n\n    this.group.add(lineDraw.group);\n    return lineDraw;\n  };\n\n  LinesView.prototype._showEffect = function (seriesModel) {\n    return !!seriesModel.get(['effect', 'show']);\n  };\n\n  LinesView.prototype._clearLayer = function (api) {\n    // Not use motion when dragging or zooming\n    var zr = api.getZr();\n    var isSvg = zr.painter.getType() === 'svg';\n\n    if (!isSvg && this._lastZlevel != null) {\n      zr.painter.getLayer(this._lastZlevel).clear(true);\n    }\n  };\n\n  LinesView.prototype.remove = function (ecModel, api) {\n    this._lineDraw && this._lineDraw.remove();\n    this._lineDraw = null; // Clear motion when lineDraw is removed\n\n    this._clearLayer(api);\n  };\n\n  LinesView.prototype.dispose = function (ecModel, api) {\n    this.remove(ecModel, api);\n  };\n\n  LinesView.type = 'lines';\n  return LinesView;\n}(ChartView);\n\nvar Uint32Arr = typeof Uint32Array === 'undefined' ? Array : Uint32Array;\nvar Float64Arr = typeof Float64Array === 'undefined' ? Array : Float64Array;\n\nfunction compatEc2(seriesOpt) {\n  var data = seriesOpt.data;\n\n  if (data && data[0] && data[0][0] && data[0][0].coord) {\n    if (\"development\" !== 'production') {\n      console.warn('Lines data configuration has been changed to' + ' { coords:[[1,2],[2,3]] }');\n    }\n\n    seriesOpt.data = map(data, function (itemOpt) {\n      var coords = [itemOpt[0].coord, itemOpt[1].coord];\n      var target = {\n        coords: coords\n      };\n\n      if (itemOpt[0].name) {\n        target.fromName = itemOpt[0].name;\n      }\n\n      if (itemOpt[1].name) {\n        target.toName = itemOpt[1].name;\n      }\n\n      return mergeAll([target, itemOpt[0], itemOpt[1]]);\n    });\n  }\n}\n\nvar LinesSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(LinesSeriesModel, _super);\n\n  function LinesSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = LinesSeriesModel.type;\n    _this.visualStyleAccessPath = 'lineStyle';\n    _this.visualDrawType = 'stroke';\n    return _this;\n  }\n\n  LinesSeriesModel.prototype.init = function (option) {\n    // The input data may be null/undefined.\n    option.data = option.data || []; // Not using preprocessor because mergeOption may not have series.type\n\n    compatEc2(option);\n\n    var result = this._processFlatCoordsArray(option.data);\n\n    this._flatCoords = result.flatCoords;\n    this._flatCoordsOffset = result.flatCoordsOffset;\n\n    if (result.flatCoords) {\n      option.data = new Float32Array(result.count);\n    }\n\n    _super.prototype.init.apply(this, arguments);\n  };\n\n  LinesSeriesModel.prototype.mergeOption = function (option) {\n    compatEc2(option);\n\n    if (option.data) {\n      // Only update when have option data to merge.\n      var result = this._processFlatCoordsArray(option.data);\n\n      this._flatCoords = result.flatCoords;\n      this._flatCoordsOffset = result.flatCoordsOffset;\n\n      if (result.flatCoords) {\n        option.data = new Float32Array(result.count);\n      }\n    }\n\n    _super.prototype.mergeOption.apply(this, arguments);\n  };\n\n  LinesSeriesModel.prototype.appendData = function (params) {\n    var result = this._processFlatCoordsArray(params.data);\n\n    if (result.flatCoords) {\n      if (!this._flatCoords) {\n        this._flatCoords = result.flatCoords;\n        this._flatCoordsOffset = result.flatCoordsOffset;\n      } else {\n        this._flatCoords = concatArray(this._flatCoords, result.flatCoords);\n        this._flatCoordsOffset = concatArray(this._flatCoordsOffset, result.flatCoordsOffset);\n      }\n\n      params.data = new Float32Array(result.count);\n    }\n\n    this.getRawData().appendData(params.data);\n  };\n\n  LinesSeriesModel.prototype._getCoordsFromItemModel = function (idx) {\n    var itemModel = this.getData().getItemModel(idx);\n    var coords = itemModel.option instanceof Array ? itemModel.option : itemModel.getShallow('coords');\n\n    if (\"development\" !== 'production') {\n      if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) {\n        throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.');\n      }\n    }\n\n    return coords;\n  };\n\n  LinesSeriesModel.prototype.getLineCoordsCount = function (idx) {\n    if (this._flatCoordsOffset) {\n      return this._flatCoordsOffset[idx * 2 + 1];\n    } else {\n      return this._getCoordsFromItemModel(idx).length;\n    }\n  };\n\n  LinesSeriesModel.prototype.getLineCoords = function (idx, out) {\n    if (this._flatCoordsOffset) {\n      var offset = this._flatCoordsOffset[idx * 2];\n      var len = this._flatCoordsOffset[idx * 2 + 1];\n\n      for (var i = 0; i < len; i++) {\n        out[i] = out[i] || [];\n        out[i][0] = this._flatCoords[offset + i * 2];\n        out[i][1] = this._flatCoords[offset + i * 2 + 1];\n      }\n\n      return len;\n    } else {\n      var coords = this._getCoordsFromItemModel(idx);\n\n      for (var i = 0; i < coords.length; i++) {\n        out[i] = out[i] || [];\n        out[i][0] = coords[i][0];\n        out[i][1] = coords[i][1];\n      }\n\n      return coords.length;\n    }\n  };\n\n  LinesSeriesModel.prototype._processFlatCoordsArray = function (data) {\n    var startOffset = 0;\n\n    if (this._flatCoords) {\n      startOffset = this._flatCoords.length;\n    } // Stored as a typed array. In format\n    // Points Count(2) | x | y | x | y | Points Count(3) | x |  y | x | y | x | y |\n\n\n    if (isNumber(data[0])) {\n      var len = data.length; // Store offset and len of each segment\n\n      var coordsOffsetAndLenStorage = new Uint32Arr(len);\n      var coordsStorage = new Float64Arr(len);\n      var coordsCursor = 0;\n      var offsetCursor = 0;\n      var dataCount = 0;\n\n      for (var i = 0; i < len;) {\n        dataCount++;\n        var count = data[i++]; // Offset\n\n        coordsOffsetAndLenStorage[offsetCursor++] = coordsCursor + startOffset; // Len\n\n        coordsOffsetAndLenStorage[offsetCursor++] = count;\n\n        for (var k = 0; k < count; k++) {\n          var x = data[i++];\n          var y = data[i++];\n          coordsStorage[coordsCursor++] = x;\n          coordsStorage[coordsCursor++] = y;\n\n          if (i > len) {\n            if (\"development\" !== 'production') {\n              throw new Error('Invalid data format.');\n            }\n          }\n        }\n      }\n\n      return {\n        flatCoordsOffset: new Uint32Array(coordsOffsetAndLenStorage.buffer, 0, offsetCursor),\n        flatCoords: coordsStorage,\n        count: dataCount\n      };\n    }\n\n    return {\n      flatCoordsOffset: null,\n      flatCoords: null,\n      count: data.length\n    };\n  };\n\n  LinesSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    if (\"development\" !== 'production') {\n      var CoordSys = CoordinateSystemManager.get(option.coordinateSystem);\n\n      if (!CoordSys) {\n        throw new Error('Unknown coordinate system ' + option.coordinateSystem);\n      }\n    }\n\n    var lineData = new SeriesData(['value'], this);\n    lineData.hasItemOption = false;\n    lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) {\n      // dataItem is simply coords\n      if (dataItem instanceof Array) {\n        return NaN;\n      } else {\n        lineData.hasItemOption = true;\n        var value = dataItem.value;\n\n        if (value != null) {\n          return value instanceof Array ? value[dimIndex] : value;\n        }\n      }\n    });\n    return lineData;\n  };\n\n  LinesSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    var data = this.getData();\n    var itemModel = data.getItemModel(dataIndex);\n    var name = itemModel.get('name');\n\n    if (name) {\n      return name;\n    }\n\n    var fromName = itemModel.get('fromName');\n    var toName = itemModel.get('toName');\n    var nameArr = [];\n    fromName != null && nameArr.push(fromName);\n    toName != null && nameArr.push(toName);\n    return createTooltipMarkup('nameValue', {\n      name: nameArr.join(' > ')\n    });\n  };\n\n  LinesSeriesModel.prototype.preventIncremental = function () {\n    return !!this.get(['effect', 'show']);\n  };\n\n  LinesSeriesModel.prototype.getProgressive = function () {\n    var progressive = this.option.progressive;\n\n    if (progressive == null) {\n      return this.option.large ? 1e4 : this.get('progressive');\n    }\n\n    return progressive;\n  };\n\n  LinesSeriesModel.prototype.getProgressiveThreshold = function () {\n    var progressiveThreshold = this.option.progressiveThreshold;\n\n    if (progressiveThreshold == null) {\n      return this.option.large ? 2e4 : this.get('progressiveThreshold');\n    }\n\n    return progressiveThreshold;\n  };\n\n  LinesSeriesModel.prototype.getZLevelKey = function () {\n    var effectModel = this.getModel('effect');\n    var trailLength = effectModel.get('trailLength');\n    return this.getData().count() > this.getProgressiveThreshold() // Each progressive series has individual key.\n    ? this.id : effectModel.get('show') && trailLength > 0 ? trailLength + '' : '';\n  };\n\n  LinesSeriesModel.type = 'series.lines';\n  LinesSeriesModel.dependencies = ['grid', 'polar', 'geo', 'calendar'];\n  LinesSeriesModel.defaultOption = {\n    coordinateSystem: 'geo',\n    // zlevel: 0,\n    z: 2,\n    legendHoverLink: true,\n    // Cartesian coordinate system\n    xAxisIndex: 0,\n    yAxisIndex: 0,\n    symbol: ['none', 'none'],\n    symbolSize: [10, 10],\n    // Geo coordinate system\n    geoIndex: 0,\n    effect: {\n      show: false,\n      period: 4,\n      constantSpeed: 0,\n      symbol: 'circle',\n      symbolSize: 3,\n      loop: true,\n      trailLength: 0.2\n    },\n    large: false,\n    // Available when large is true\n    largeThreshold: 2000,\n    polyline: false,\n    clip: true,\n    label: {\n      show: false,\n      position: 'end' // distance: 5,\n      // formatter: 标签文本格式器，同Tooltip.formatter，不支持异步回调\n\n    },\n    lineStyle: {\n      opacity: 0.5\n    }\n  };\n  return LinesSeriesModel;\n}(SeriesModel);\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction normalize$3(a) {\n  if (!(a instanceof Array)) {\n    a = [a, a];\n  }\n\n  return a;\n}\n\nvar linesVisual = {\n  seriesType: 'lines',\n  reset: function (seriesModel) {\n    var symbolType = normalize$3(seriesModel.get('symbol'));\n    var symbolSize = normalize$3(seriesModel.get('symbolSize'));\n    var data = seriesModel.getData();\n    data.setVisual('fromSymbol', symbolType && symbolType[0]);\n    data.setVisual('toSymbol', symbolType && symbolType[1]);\n    data.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);\n    data.setVisual('toSymbolSize', symbolSize && symbolSize[1]);\n\n    function dataEach(data, idx) {\n      var itemModel = data.getItemModel(idx);\n      var symbolType = normalize$3(itemModel.getShallow('symbol', true));\n      var symbolSize = normalize$3(itemModel.getShallow('symbolSize', true));\n      symbolType[0] && data.setItemVisual(idx, 'fromSymbol', symbolType[0]);\n      symbolType[1] && data.setItemVisual(idx, 'toSymbol', symbolType[1]);\n      symbolSize[0] && data.setItemVisual(idx, 'fromSymbolSize', symbolSize[0]);\n      symbolSize[1] && data.setItemVisual(idx, 'toSymbolSize', symbolSize[1]);\n    }\n\n    return {\n      dataEach: data.hasItemOption ? dataEach : null\n    };\n  }\n};\n\nfunction install$m(registers) {\n  registers.registerChartView(LinesView);\n  registers.registerSeriesModel(LinesSeriesModel);\n  registers.registerLayout(linesLayout);\n  registers.registerVisual(linesVisual);\n}\n\nvar GRADIENT_LEVELS = 256;\n\nvar HeatmapLayer =\n/** @class */\nfunction () {\n  function HeatmapLayer() {\n    this.blurSize = 30;\n    this.pointSize = 20;\n    this.maxOpacity = 1;\n    this.minOpacity = 0;\n    this._gradientPixels = {\n      inRange: null,\n      outOfRange: null\n    };\n    var canvas = platformApi.createCanvas();\n    this.canvas = canvas;\n  }\n  /**\n   * Renders Heatmap and returns the rendered canvas\n   * @param data array of data, each has x, y, value\n   * @param width canvas width\n   * @param height canvas height\n   */\n\n\n  HeatmapLayer.prototype.update = function (data, width, height, normalize, colorFunc, isInRange) {\n    var brush = this._getBrush();\n\n    var gradientInRange = this._getGradient(colorFunc, 'inRange');\n\n    var gradientOutOfRange = this._getGradient(colorFunc, 'outOfRange');\n\n    var r = this.pointSize + this.blurSize;\n    var canvas = this.canvas;\n    var ctx = canvas.getContext('2d');\n    var len = data.length;\n    canvas.width = width;\n    canvas.height = height;\n\n    for (var i = 0; i < len; ++i) {\n      var p = data[i];\n      var x = p[0];\n      var y = p[1];\n      var value = p[2]; // calculate alpha using value\n\n      var alpha = normalize(value); // draw with the circle brush with alpha\n\n      ctx.globalAlpha = alpha;\n      ctx.drawImage(brush, x - r, y - r);\n    }\n\n    if (!canvas.width || !canvas.height) {\n      // Avoid \"Uncaught DOMException: Failed to execute 'getImageData' on\n      // 'CanvasRenderingContext2D': The source height is 0.\"\n      return canvas;\n    } // colorize the canvas using alpha value and set with gradient\n\n\n    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n    var pixels = imageData.data;\n    var offset = 0;\n    var pixelLen = pixels.length;\n    var minOpacity = this.minOpacity;\n    var maxOpacity = this.maxOpacity;\n    var diffOpacity = maxOpacity - minOpacity;\n\n    while (offset < pixelLen) {\n      var alpha = pixels[offset + 3] / 256;\n      var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4; // Simple optimize to ignore the empty data\n\n      if (alpha > 0) {\n        var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange; // Any alpha > 0 will be mapped to [minOpacity, maxOpacity]\n\n        alpha > 0 && (alpha = alpha * diffOpacity + minOpacity);\n        pixels[offset++] = gradient[gradientOffset];\n        pixels[offset++] = gradient[gradientOffset + 1];\n        pixels[offset++] = gradient[gradientOffset + 2];\n        pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256;\n      } else {\n        offset += 4;\n      }\n    }\n\n    ctx.putImageData(imageData, 0, 0);\n    return canvas;\n  };\n  /**\n   * get canvas of a black circle brush used for canvas to draw later\n   */\n\n\n  HeatmapLayer.prototype._getBrush = function () {\n    var brushCanvas = this._brushCanvas || (this._brushCanvas = platformApi.createCanvas()); // set brush size\n\n    var r = this.pointSize + this.blurSize;\n    var d = r * 2;\n    brushCanvas.width = d;\n    brushCanvas.height = d;\n    var ctx = brushCanvas.getContext('2d');\n    ctx.clearRect(0, 0, d, d); // in order to render shadow without the distinct circle,\n    // draw the distinct circle in an invisible place,\n    // and use shadowOffset to draw shadow in the center of the canvas\n\n    ctx.shadowOffsetX = d;\n    ctx.shadowBlur = this.blurSize; // draw the shadow in black, and use alpha and shadow blur to generate\n    // color in color map\n\n    ctx.shadowColor = '#000'; // draw circle in the left to the canvas\n\n    ctx.beginPath();\n    ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true);\n    ctx.closePath();\n    ctx.fill();\n    return brushCanvas;\n  };\n  /**\n   * get gradient color map\n   * @private\n   */\n\n\n  HeatmapLayer.prototype._getGradient = function (colorFunc, state) {\n    var gradientPixels = this._gradientPixels;\n    var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4));\n    var color = [0, 0, 0, 0];\n    var off = 0;\n\n    for (var i = 0; i < 256; i++) {\n      colorFunc[state](i / 255, true, color);\n      pixelsSingleState[off++] = color[0];\n      pixelsSingleState[off++] = color[1];\n      pixelsSingleState[off++] = color[2];\n      pixelsSingleState[off++] = color[3];\n    }\n\n    return pixelsSingleState;\n  };\n\n  return HeatmapLayer;\n}();\n\nfunction getIsInPiecewiseRange(dataExtent, pieceList, selected) {\n  var dataSpan = dataExtent[1] - dataExtent[0];\n  pieceList = map(pieceList, function (piece) {\n    return {\n      interval: [(piece.interval[0] - dataExtent[0]) / dataSpan, (piece.interval[1] - dataExtent[0]) / dataSpan]\n    };\n  });\n  var len = pieceList.length;\n  var lastIndex = 0;\n  return function (val) {\n    var i; // Try to find in the location of the last found\n\n    for (i = lastIndex; i < len; i++) {\n      var interval = pieceList[i].interval;\n\n      if (interval[0] <= val && val <= interval[1]) {\n        lastIndex = i;\n        break;\n      }\n    }\n\n    if (i === len) {\n      // Not found, back interation\n      for (i = lastIndex - 1; i >= 0; i--) {\n        var interval = pieceList[i].interval;\n\n        if (interval[0] <= val && val <= interval[1]) {\n          lastIndex = i;\n          break;\n        }\n      }\n    }\n\n    return i >= 0 && i < len && selected[i];\n  };\n}\n\nfunction getIsInContinuousRange(dataExtent, range) {\n  var dataSpan = dataExtent[1] - dataExtent[0];\n  range = [(range[0] - dataExtent[0]) / dataSpan, (range[1] - dataExtent[0]) / dataSpan];\n  return function (val) {\n    return val >= range[0] && val <= range[1];\n  };\n}\n\nfunction isGeoCoordSys(coordSys) {\n  var dimensions = coordSys.dimensions; // Not use coordSys.type === 'geo' because coordSys maybe extended\n\n  return dimensions[0] === 'lng' && dimensions[1] === 'lat';\n}\n\nvar HeatmapView =\n/** @class */\nfunction (_super) {\n  __extends(HeatmapView, _super);\n\n  function HeatmapView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = HeatmapView.type;\n    return _this;\n  }\n\n  HeatmapView.prototype.render = function (seriesModel, ecModel, api) {\n    var visualMapOfThisSeries;\n    ecModel.eachComponent('visualMap', function (visualMap) {\n      visualMap.eachTargetSeries(function (targetSeries) {\n        if (targetSeries === seriesModel) {\n          visualMapOfThisSeries = visualMap;\n        }\n      });\n    });\n\n    if (\"development\" !== 'production') {\n      if (!visualMapOfThisSeries) {\n        throw new Error('Heatmap must use with visualMap');\n      }\n    } // Clear previously rendered progressive elements.\n\n\n    this._progressiveEls = null;\n    this.group.removeAll();\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar') {\n      this._renderOnCartesianAndCalendar(seriesModel, api, 0, seriesModel.getData().count());\n    } else if (isGeoCoordSys(coordSys)) {\n      this._renderOnGeo(coordSys, seriesModel, visualMapOfThisSeries, api);\n    }\n  };\n\n  HeatmapView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n    this.group.removeAll();\n  };\n\n  HeatmapView.prototype.incrementalRender = function (params, seriesModel, ecModel, api) {\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (coordSys) {\n      // geo does not support incremental rendering?\n      if (isGeoCoordSys(coordSys)) {\n        this.render(seriesModel, ecModel, api);\n      } else {\n        this._progressiveEls = [];\n\n        this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true);\n      }\n    }\n  };\n\n  HeatmapView.prototype.eachRendered = function (cb) {\n    traverseElements(this._progressiveEls || this.group, cb);\n  };\n\n  HeatmapView.prototype._renderOnCartesianAndCalendar = function (seriesModel, api, start, end, incremental) {\n    var coordSys = seriesModel.coordinateSystem;\n    var isCartesian2d = isCoordinateSystemType(coordSys, 'cartesian2d');\n    var width;\n    var height;\n    var xAxisExtent;\n    var yAxisExtent;\n\n    if (isCartesian2d) {\n      var xAxis = coordSys.getAxis('x');\n      var yAxis = coordSys.getAxis('y');\n\n      if (\"development\" !== 'production') {\n        if (!(xAxis.type === 'category' && yAxis.type === 'category')) {\n          throw new Error('Heatmap on cartesian must have two category axes');\n        }\n\n        if (!(xAxis.onBand && yAxis.onBand)) {\n          throw new Error('Heatmap on cartesian must have two axes with boundaryGap true');\n        }\n      } // add 0.5px to avoid the gaps\n\n\n      width = xAxis.getBandWidth() + .5;\n      height = yAxis.getBandWidth() + .5;\n      xAxisExtent = xAxis.scale.getExtent();\n      yAxisExtent = yAxis.scale.getExtent();\n    }\n\n    var group = this.group;\n    var data = seriesModel.getData();\n    var emphasisStyle = seriesModel.getModel(['emphasis', 'itemStyle']).getItemStyle();\n    var blurStyle = seriesModel.getModel(['blur', 'itemStyle']).getItemStyle();\n    var selectStyle = seriesModel.getModel(['select', 'itemStyle']).getItemStyle();\n    var borderRadius = seriesModel.get(['itemStyle', 'borderRadius']);\n    var labelStatesModels = getLabelStatesModels(seriesModel);\n    var emphasisModel = seriesModel.getModel('emphasis');\n    var focus = emphasisModel.get('focus');\n    var blurScope = emphasisModel.get('blurScope');\n    var emphasisDisabled = emphasisModel.get('disabled');\n    var dataDims = isCartesian2d ? [data.mapDimension('x'), data.mapDimension('y'), data.mapDimension('value')] : [data.mapDimension('time'), data.mapDimension('value')];\n\n    for (var idx = start; idx < end; idx++) {\n      var rect = void 0;\n      var style = data.getItemVisual(idx, 'style');\n\n      if (isCartesian2d) {\n        var dataDimX = data.get(dataDims[0], idx);\n        var dataDimY = data.get(dataDims[1], idx); // Ignore empty data and out of extent data\n\n        if (isNaN(data.get(dataDims[2], idx)) || isNaN(dataDimX) || isNaN(dataDimY) || dataDimX < xAxisExtent[0] || dataDimX > xAxisExtent[1] || dataDimY < yAxisExtent[0] || dataDimY > yAxisExtent[1]) {\n          continue;\n        }\n\n        var point = coordSys.dataToPoint([dataDimX, dataDimY]);\n        rect = new Rect({\n          shape: {\n            x: point[0] - width / 2,\n            y: point[1] - height / 2,\n            width: width,\n            height: height\n          },\n          style: style\n        });\n      } else {\n        // Ignore empty data\n        if (isNaN(data.get(dataDims[1], idx))) {\n          continue;\n        }\n\n        rect = new Rect({\n          z2: 1,\n          shape: coordSys.dataToRect([data.get(dataDims[0], idx)]).contentShape,\n          style: style\n        });\n      } // Optimization for large dataset\n\n\n      if (data.hasItemOption) {\n        var itemModel = data.getItemModel(idx);\n        var emphasisModel_1 = itemModel.getModel('emphasis');\n        emphasisStyle = emphasisModel_1.getModel('itemStyle').getItemStyle();\n        blurStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n        selectStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle(); // Each item value struct in the data would be firstly\n        // {\n        //     itemStyle: { borderRadius: [30, 30] },\n        //     value: [2022, 02, 22]\n        // }\n\n        borderRadius = itemModel.get(['itemStyle', 'borderRadius']);\n        focus = emphasisModel_1.get('focus');\n        blurScope = emphasisModel_1.get('blurScope');\n        emphasisDisabled = emphasisModel_1.get('disabled');\n        labelStatesModels = getLabelStatesModels(itemModel);\n      }\n\n      rect.shape.r = borderRadius;\n      var rawValue = seriesModel.getRawValue(idx);\n      var defaultText = '-';\n\n      if (rawValue && rawValue[2] != null) {\n        defaultText = rawValue[2] + '';\n      }\n\n      setLabelStyle(rect, labelStatesModels, {\n        labelFetcher: seriesModel,\n        labelDataIndex: idx,\n        defaultOpacity: style.opacity,\n        defaultText: defaultText\n      });\n      rect.ensureState('emphasis').style = emphasisStyle;\n      rect.ensureState('blur').style = blurStyle;\n      rect.ensureState('select').style = selectStyle;\n      toggleHoverEmphasis(rect, focus, blurScope, emphasisDisabled);\n      rect.incremental = incremental; // PENDING\n\n      if (incremental) {\n        // Rect must use hover layer if it's incremental.\n        rect.states.emphasis.hoverLayer = true;\n      }\n\n      group.add(rect);\n      data.setItemGraphicEl(idx, rect);\n\n      if (this._progressiveEls) {\n        this._progressiveEls.push(rect);\n      }\n    }\n  };\n\n  HeatmapView.prototype._renderOnGeo = function (geo, seriesModel, visualMapModel, api) {\n    var inRangeVisuals = visualMapModel.targetVisuals.inRange;\n    var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange; // if (!visualMapping) {\n    //     throw new Error('Data range must have color visuals');\n    // }\n\n    var data = seriesModel.getData();\n    var hmLayer = this._hmLayer || this._hmLayer || new HeatmapLayer();\n    hmLayer.blurSize = seriesModel.get('blurSize');\n    hmLayer.pointSize = seriesModel.get('pointSize');\n    hmLayer.minOpacity = seriesModel.get('minOpacity');\n    hmLayer.maxOpacity = seriesModel.get('maxOpacity');\n    var rect = geo.getViewRect().clone();\n    var roamTransform = geo.getRoamTransform();\n    rect.applyTransform(roamTransform); // Clamp on viewport\n\n    var x = Math.max(rect.x, 0);\n    var y = Math.max(rect.y, 0);\n    var x2 = Math.min(rect.width + rect.x, api.getWidth());\n    var y2 = Math.min(rect.height + rect.y, api.getHeight());\n    var width = x2 - x;\n    var height = y2 - y;\n    var dims = [data.mapDimension('lng'), data.mapDimension('lat'), data.mapDimension('value')];\n    var points = data.mapArray(dims, function (lng, lat, value) {\n      var pt = geo.dataToPoint([lng, lat]);\n      pt[0] -= x;\n      pt[1] -= y;\n      pt.push(value);\n      return pt;\n    });\n    var dataExtent = visualMapModel.getExtent();\n    var isInRange = visualMapModel.type === 'visualMap.continuous' ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) : getIsInPiecewiseRange(dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected);\n    hmLayer.update(points, width, height, inRangeVisuals.color.getNormalizer(), {\n      inRange: inRangeVisuals.color.getColorMapper(),\n      outOfRange: outOfRangeVisuals.color.getColorMapper()\n    }, isInRange);\n    var img = new ZRImage({\n      style: {\n        width: width,\n        height: height,\n        x: x,\n        y: y,\n        image: hmLayer.canvas\n      },\n      silent: true\n    });\n    this.group.add(img);\n  };\n\n  HeatmapView.type = 'heatmap';\n  return HeatmapView;\n}(ChartView);\n\nvar HeatmapSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(HeatmapSeriesModel, _super);\n\n  function HeatmapSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = HeatmapSeriesModel.type;\n    return _this;\n  }\n\n  HeatmapSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesData(null, this, {\n      generateCoord: 'value'\n    });\n  };\n\n  HeatmapSeriesModel.prototype.preventIncremental = function () {\n    var coordSysCreator = CoordinateSystemManager.get(this.get('coordinateSystem'));\n\n    if (coordSysCreator && coordSysCreator.dimensions) {\n      return coordSysCreator.dimensions[0] === 'lng' && coordSysCreator.dimensions[1] === 'lat';\n    }\n  };\n\n  HeatmapSeriesModel.type = 'series.heatmap';\n  HeatmapSeriesModel.dependencies = ['grid', 'geo', 'calendar'];\n  HeatmapSeriesModel.defaultOption = {\n    coordinateSystem: 'cartesian2d',\n    // zlevel: 0,\n    z: 2,\n    // Cartesian coordinate system\n    // xAxisIndex: 0,\n    // yAxisIndex: 0,\n    // Geo coordinate system\n    geoIndex: 0,\n    blurSize: 30,\n    pointSize: 20,\n    maxOpacity: 1,\n    minOpacity: 0,\n    select: {\n      itemStyle: {\n        borderColor: '#212121'\n      }\n    }\n  };\n  return HeatmapSeriesModel;\n}(SeriesModel);\n\nfunction install$n(registers) {\n  registers.registerChartView(HeatmapView);\n  registers.registerSeriesModel(HeatmapSeriesModel);\n}\n\nvar BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'borderWidth']; // index: +isHorizontal\n\nvar LAYOUT_ATTRS = [{\n  xy: 'x',\n  wh: 'width',\n  index: 0,\n  posDesc: ['left', 'right']\n}, {\n  xy: 'y',\n  wh: 'height',\n  index: 1,\n  posDesc: ['top', 'bottom']\n}];\nvar pathForLineWidth = new Circle();\n\nvar PictorialBarView =\n/** @class */\nfunction (_super) {\n  __extends(PictorialBarView, _super);\n\n  function PictorialBarView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = PictorialBarView.type;\n    return _this;\n  }\n\n  PictorialBarView.prototype.render = function (seriesModel, ecModel, api) {\n    var group = this.group;\n    var data = seriesModel.getData();\n    var oldData = this._data;\n    var cartesian = seriesModel.coordinateSystem;\n    var baseAxis = cartesian.getBaseAxis();\n    var isHorizontal = baseAxis.isHorizontal();\n    var coordSysRect = cartesian.master.getRect();\n    var opt = {\n      ecSize: {\n        width: api.getWidth(),\n        height: api.getHeight()\n      },\n      seriesModel: seriesModel,\n      coordSys: cartesian,\n      coordSysExtent: [[coordSysRect.x, coordSysRect.x + coordSysRect.width], [coordSysRect.y, coordSysRect.y + coordSysRect.height]],\n      isHorizontal: isHorizontal,\n      valueDim: LAYOUT_ATTRS[+isHorizontal],\n      categoryDim: LAYOUT_ATTRS[1 - +isHorizontal]\n    };\n    data.diff(oldData).add(function (dataIndex) {\n      if (!data.hasValue(dataIndex)) {\n        return;\n      }\n\n      var itemModel = getItemModel(data, dataIndex);\n      var symbolMeta = getSymbolMeta(data, dataIndex, itemModel, opt);\n      var bar = createBar(data, opt, symbolMeta);\n      data.setItemGraphicEl(dataIndex, bar);\n      group.add(bar);\n      updateCommon$1(bar, opt, symbolMeta);\n    }).update(function (newIndex, oldIndex) {\n      var bar = oldData.getItemGraphicEl(oldIndex);\n\n      if (!data.hasValue(newIndex)) {\n        group.remove(bar);\n        return;\n      }\n\n      var itemModel = getItemModel(data, newIndex);\n      var symbolMeta = getSymbolMeta(data, newIndex, itemModel, opt);\n      var pictorialShapeStr = getShapeStr(data, symbolMeta);\n\n      if (bar && pictorialShapeStr !== bar.__pictorialShapeStr) {\n        group.remove(bar);\n        data.setItemGraphicEl(newIndex, null);\n        bar = null;\n      }\n\n      if (bar) {\n        updateBar(bar, opt, symbolMeta);\n      } else {\n        bar = createBar(data, opt, symbolMeta, true);\n      }\n\n      data.setItemGraphicEl(newIndex, bar);\n      bar.__pictorialSymbolMeta = symbolMeta; // Add back\n\n      group.add(bar);\n      updateCommon$1(bar, opt, symbolMeta);\n    }).remove(function (dataIndex) {\n      var bar = oldData.getItemGraphicEl(dataIndex);\n      bar && removeBar(oldData, dataIndex, bar.__pictorialSymbolMeta.animationModel, bar);\n    }).execute();\n    this._data = data;\n    return this.group;\n  };\n\n  PictorialBarView.prototype.remove = function (ecModel, api) {\n    var group = this.group;\n    var data = this._data;\n\n    if (ecModel.get('animation')) {\n      if (data) {\n        data.eachItemGraphicEl(function (bar) {\n          removeBar(data, getECData(bar).dataIndex, ecModel, bar);\n        });\n      }\n    } else {\n      group.removeAll();\n    }\n  };\n\n  PictorialBarView.type = 'pictorialBar';\n  return PictorialBarView;\n}(ChartView); // Set or calculate default value about symbol, and calculate layout info.\n\n\nfunction getSymbolMeta(data, dataIndex, itemModel, opt) {\n  var layout = data.getItemLayout(dataIndex);\n  var symbolRepeat = itemModel.get('symbolRepeat');\n  var symbolClip = itemModel.get('symbolClip');\n  var symbolPosition = itemModel.get('symbolPosition') || 'start';\n  var symbolRotate = itemModel.get('symbolRotate');\n  var rotation = (symbolRotate || 0) * Math.PI / 180 || 0;\n  var symbolPatternSize = itemModel.get('symbolPatternSize') || 2;\n  var isAnimationEnabled = itemModel.isAnimationEnabled();\n  var symbolMeta = {\n    dataIndex: dataIndex,\n    layout: layout,\n    itemModel: itemModel,\n    symbolType: data.getItemVisual(dataIndex, 'symbol') || 'circle',\n    style: data.getItemVisual(dataIndex, 'style'),\n    symbolClip: symbolClip,\n    symbolRepeat: symbolRepeat,\n    symbolRepeatDirection: itemModel.get('symbolRepeatDirection'),\n    symbolPatternSize: symbolPatternSize,\n    rotation: rotation,\n    animationModel: isAnimationEnabled ? itemModel : null,\n    hoverScale: isAnimationEnabled && itemModel.get(['emphasis', 'scale']),\n    z2: itemModel.getShallow('z', true) || 0\n  };\n  prepareBarLength(itemModel, symbolRepeat, layout, opt, symbolMeta);\n  prepareSymbolSize(data, dataIndex, layout, symbolRepeat, symbolClip, symbolMeta.boundingLength, symbolMeta.pxSign, symbolPatternSize, opt, symbolMeta);\n  prepareLineWidth(itemModel, symbolMeta.symbolScale, rotation, opt, symbolMeta);\n  var symbolSize = symbolMeta.symbolSize;\n  var symbolOffset = normalizeSymbolOffset(itemModel.get('symbolOffset'), symbolSize);\n  prepareLayoutInfo(itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, symbolPosition, symbolMeta.valueLineWidth, symbolMeta.boundingLength, symbolMeta.repeatCutLength, opt, symbolMeta);\n  return symbolMeta;\n} // bar length can be negative.\n\n\nfunction prepareBarLength(itemModel, symbolRepeat, layout, opt, outputSymbolMeta) {\n  var valueDim = opt.valueDim;\n  var symbolBoundingData = itemModel.get('symbolBoundingData');\n  var valueAxis = opt.coordSys.getOtherAxis(opt.coordSys.getBaseAxis());\n  var zeroPx = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0));\n  var pxSignIdx = 1 - +(layout[valueDim.wh] <= 0);\n  var boundingLength;\n\n  if (isArray(symbolBoundingData)) {\n    var symbolBoundingExtent = [convertToCoordOnAxis(valueAxis, symbolBoundingData[0]) - zeroPx, convertToCoordOnAxis(valueAxis, symbolBoundingData[1]) - zeroPx];\n    symbolBoundingExtent[1] < symbolBoundingExtent[0] && symbolBoundingExtent.reverse();\n    boundingLength = symbolBoundingExtent[pxSignIdx];\n  } else if (symbolBoundingData != null) {\n    boundingLength = convertToCoordOnAxis(valueAxis, symbolBoundingData) - zeroPx;\n  } else if (symbolRepeat) {\n    boundingLength = opt.coordSysExtent[valueDim.index][pxSignIdx] - zeroPx;\n  } else {\n    boundingLength = layout[valueDim.wh];\n  }\n\n  outputSymbolMeta.boundingLength = boundingLength;\n\n  if (symbolRepeat) {\n    outputSymbolMeta.repeatCutLength = layout[valueDim.wh];\n  } // if 'pxSign' means sign of pixel,  it can't be zero, or symbolScale will be zero\n  // and when borderWidth be settled, the actual linewidth will be NaN\n\n\n  outputSymbolMeta.pxSign = boundingLength > 0 ? 1 : -1;\n}\n\nfunction convertToCoordOnAxis(axis, value) {\n  return axis.toGlobalCoord(axis.dataToCoord(axis.scale.parse(value)));\n} // Support ['100%', '100%']\n\n\nfunction prepareSymbolSize(data, dataIndex, layout, symbolRepeat, symbolClip, boundingLength, pxSign, symbolPatternSize, opt, outputSymbolMeta) {\n  var valueDim = opt.valueDim;\n  var categoryDim = opt.categoryDim;\n  var categorySize = Math.abs(layout[categoryDim.wh]);\n  var symbolSize = data.getItemVisual(dataIndex, 'symbolSize');\n  var parsedSymbolSize;\n\n  if (isArray(symbolSize)) {\n    parsedSymbolSize = symbolSize.slice();\n  } else {\n    if (symbolSize == null) {\n      // will parse to number below\n      parsedSymbolSize = ['100%', '100%'];\n    } else {\n      parsedSymbolSize = [symbolSize, symbolSize];\n    }\n  } // Note: percentage symbolSize (like '100%') do not consider lineWidth, because it is\n  // to complicated to calculate real percent value if considering scaled lineWidth.\n  // So the actual size will bigger than layout size if lineWidth is bigger than zero,\n  // which can be tolerated in pictorial chart.\n\n\n  parsedSymbolSize[categoryDim.index] = parsePercent$1(parsedSymbolSize[categoryDim.index], categorySize);\n  parsedSymbolSize[valueDim.index] = parsePercent$1(parsedSymbolSize[valueDim.index], symbolRepeat ? categorySize : Math.abs(boundingLength));\n  outputSymbolMeta.symbolSize = parsedSymbolSize; // If x or y is less than zero, show reversed shape.\n\n  var symbolScale = outputSymbolMeta.symbolScale = [parsedSymbolSize[0] / symbolPatternSize, parsedSymbolSize[1] / symbolPatternSize]; // Follow convention, 'right' and 'top' is the normal scale.\n\n  symbolScale[valueDim.index] *= (opt.isHorizontal ? -1 : 1) * pxSign;\n}\n\nfunction prepareLineWidth(itemModel, symbolScale, rotation, opt, outputSymbolMeta) {\n  // In symbols are drawn with scale, so do not need to care about the case that width\n  // or height are too small. But symbol use strokeNoScale, where acture lineWidth should\n  // be calculated.\n  var valueLineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;\n\n  if (valueLineWidth) {\n    pathForLineWidth.attr({\n      scaleX: symbolScale[0],\n      scaleY: symbolScale[1],\n      rotation: rotation\n    });\n    pathForLineWidth.updateTransform();\n    valueLineWidth /= pathForLineWidth.getLineScale();\n    valueLineWidth *= symbolScale[opt.valueDim.index];\n  }\n\n  outputSymbolMeta.valueLineWidth = valueLineWidth || 0;\n}\n\nfunction prepareLayoutInfo(itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, symbolPosition, valueLineWidth, boundingLength, repeatCutLength, opt, outputSymbolMeta) {\n  var categoryDim = opt.categoryDim;\n  var valueDim = opt.valueDim;\n  var pxSign = outputSymbolMeta.pxSign;\n  var unitLength = Math.max(symbolSize[valueDim.index] + valueLineWidth, 0);\n  var pathLen = unitLength; // Note: rotation will not effect the layout of symbols, because user may\n  // want symbols to rotate on its center, which should not be translated\n  // when rotating.\n\n  if (symbolRepeat) {\n    var absBoundingLength = Math.abs(boundingLength);\n    var symbolMargin = retrieve(itemModel.get('symbolMargin'), '15%') + '';\n    var hasEndGap = false;\n\n    if (symbolMargin.lastIndexOf('!') === symbolMargin.length - 1) {\n      hasEndGap = true;\n      symbolMargin = symbolMargin.slice(0, symbolMargin.length - 1);\n    }\n\n    var symbolMarginNumeric = parsePercent$1(symbolMargin, symbolSize[valueDim.index]);\n    var uLenWithMargin = Math.max(unitLength + symbolMarginNumeric * 2, 0); // When symbol margin is less than 0, margin at both ends will be subtracted\n    // to ensure that all of the symbols will not be overflow the given area.\n\n    var endFix = hasEndGap ? 0 : symbolMarginNumeric * 2; // Both final repeatTimes and final symbolMarginNumeric area calculated based on\n    // boundingLength.\n\n    var repeatSpecified = isNumeric(symbolRepeat);\n    var repeatTimes = repeatSpecified ? symbolRepeat : toIntTimes((absBoundingLength + endFix) / uLenWithMargin); // Adjust calculate margin, to ensure each symbol is displayed\n    // entirely in the given layout area.\n\n    var mDiff = absBoundingLength - repeatTimes * unitLength;\n    symbolMarginNumeric = mDiff / 2 / (hasEndGap ? repeatTimes : Math.max(repeatTimes - 1, 1));\n    uLenWithMargin = unitLength + symbolMarginNumeric * 2;\n    endFix = hasEndGap ? 0 : symbolMarginNumeric * 2; // Update repeatTimes when not all symbol will be shown.\n\n    if (!repeatSpecified && symbolRepeat !== 'fixed') {\n      repeatTimes = repeatCutLength ? toIntTimes((Math.abs(repeatCutLength) + endFix) / uLenWithMargin) : 0;\n    }\n\n    pathLen = repeatTimes * uLenWithMargin - endFix;\n    outputSymbolMeta.repeatTimes = repeatTimes;\n    outputSymbolMeta.symbolMargin = symbolMarginNumeric;\n  }\n\n  var sizeFix = pxSign * (pathLen / 2);\n  var pathPosition = outputSymbolMeta.pathPosition = [];\n  pathPosition[categoryDim.index] = layout[categoryDim.wh] / 2;\n  pathPosition[valueDim.index] = symbolPosition === 'start' ? sizeFix : symbolPosition === 'end' ? boundingLength - sizeFix : boundingLength / 2; // 'center'\n\n  if (symbolOffset) {\n    pathPosition[0] += symbolOffset[0];\n    pathPosition[1] += symbolOffset[1];\n  }\n\n  var bundlePosition = outputSymbolMeta.bundlePosition = [];\n  bundlePosition[categoryDim.index] = layout[categoryDim.xy];\n  bundlePosition[valueDim.index] = layout[valueDim.xy];\n  var barRectShape = outputSymbolMeta.barRectShape = extend({}, layout);\n  barRectShape[valueDim.wh] = pxSign * Math.max(Math.abs(layout[valueDim.wh]), Math.abs(pathPosition[valueDim.index] + sizeFix));\n  barRectShape[categoryDim.wh] = layout[categoryDim.wh];\n  var clipShape = outputSymbolMeta.clipShape = {}; // Consider that symbol may be overflow layout rect.\n\n  clipShape[categoryDim.xy] = -layout[categoryDim.xy];\n  clipShape[categoryDim.wh] = opt.ecSize[categoryDim.wh];\n  clipShape[valueDim.xy] = 0;\n  clipShape[valueDim.wh] = layout[valueDim.wh];\n}\n\nfunction createPath(symbolMeta) {\n  var symbolPatternSize = symbolMeta.symbolPatternSize;\n  var path = createSymbol( // Consider texture img, make a big size.\n  symbolMeta.symbolType, -symbolPatternSize / 2, -symbolPatternSize / 2, symbolPatternSize, symbolPatternSize);\n  path.attr({\n    culling: true\n  });\n  path.type !== 'image' && path.setStyle({\n    strokeNoScale: true\n  });\n  return path;\n}\n\nfunction createOrUpdateRepeatSymbols(bar, opt, symbolMeta, isUpdate) {\n  var bundle = bar.__pictorialBundle;\n  var symbolSize = symbolMeta.symbolSize;\n  var valueLineWidth = symbolMeta.valueLineWidth;\n  var pathPosition = symbolMeta.pathPosition;\n  var valueDim = opt.valueDim;\n  var repeatTimes = symbolMeta.repeatTimes || 0;\n  var index = 0;\n  var unit = symbolSize[opt.valueDim.index] + valueLineWidth + symbolMeta.symbolMargin * 2;\n  eachPath(bar, function (path) {\n    path.__pictorialAnimationIndex = index;\n    path.__pictorialRepeatTimes = repeatTimes;\n\n    if (index < repeatTimes) {\n      updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate);\n    } else {\n      updateAttr(path, null, {\n        scaleX: 0,\n        scaleY: 0\n      }, symbolMeta, isUpdate, function () {\n        bundle.remove(path);\n      });\n    } // updateHoverAnimation(path, symbolMeta);\n\n\n    index++;\n  });\n\n  for (; index < repeatTimes; index++) {\n    var path = createPath(symbolMeta);\n    path.__pictorialAnimationIndex = index;\n    path.__pictorialRepeatTimes = repeatTimes;\n    bundle.add(path);\n    var target = makeTarget(index);\n    updateAttr(path, {\n      x: target.x,\n      y: target.y,\n      scaleX: 0,\n      scaleY: 0\n    }, {\n      scaleX: target.scaleX,\n      scaleY: target.scaleY,\n      rotation: target.rotation\n    }, symbolMeta, isUpdate);\n  }\n\n  function makeTarget(index) {\n    var position = pathPosition.slice(); // (start && pxSign > 0) || (end && pxSign < 0): i = repeatTimes - index\n    // Otherwise: i = index;\n\n    var pxSign = symbolMeta.pxSign;\n    var i = index;\n\n    if (symbolMeta.symbolRepeatDirection === 'start' ? pxSign > 0 : pxSign < 0) {\n      i = repeatTimes - 1 - index;\n    }\n\n    position[valueDim.index] = unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index];\n    return {\n      x: position[0],\n      y: position[1],\n      scaleX: symbolMeta.symbolScale[0],\n      scaleY: symbolMeta.symbolScale[1],\n      rotation: symbolMeta.rotation\n    };\n  }\n}\n\nfunction createOrUpdateSingleSymbol(bar, opt, symbolMeta, isUpdate) {\n  var bundle = bar.__pictorialBundle;\n  var mainPath = bar.__pictorialMainPath;\n\n  if (!mainPath) {\n    mainPath = bar.__pictorialMainPath = createPath(symbolMeta);\n    bundle.add(mainPath);\n    updateAttr(mainPath, {\n      x: symbolMeta.pathPosition[0],\n      y: symbolMeta.pathPosition[1],\n      scaleX: 0,\n      scaleY: 0,\n      rotation: symbolMeta.rotation\n    }, {\n      scaleX: symbolMeta.symbolScale[0],\n      scaleY: symbolMeta.symbolScale[1]\n    }, symbolMeta, isUpdate);\n  } else {\n    updateAttr(mainPath, null, {\n      x: symbolMeta.pathPosition[0],\n      y: symbolMeta.pathPosition[1],\n      scaleX: symbolMeta.symbolScale[0],\n      scaleY: symbolMeta.symbolScale[1],\n      rotation: symbolMeta.rotation\n    }, symbolMeta, isUpdate);\n  }\n} // bar rect is used for label.\n\n\nfunction createOrUpdateBarRect(bar, symbolMeta, isUpdate) {\n  var rectShape = extend({}, symbolMeta.barRectShape);\n  var barRect = bar.__pictorialBarRect;\n\n  if (!barRect) {\n    barRect = bar.__pictorialBarRect = new Rect({\n      z2: 2,\n      shape: rectShape,\n      silent: true,\n      style: {\n        stroke: 'transparent',\n        fill: 'transparent',\n        lineWidth: 0\n      }\n    });\n    barRect.disableMorphing = true;\n    bar.add(barRect);\n  } else {\n    updateAttr(barRect, null, {\n      shape: rectShape\n    }, symbolMeta, isUpdate);\n  }\n}\n\nfunction createOrUpdateClip(bar, opt, symbolMeta, isUpdate) {\n  // If not clip, symbol will be remove and rebuilt.\n  if (symbolMeta.symbolClip) {\n    var clipPath = bar.__pictorialClipPath;\n    var clipShape = extend({}, symbolMeta.clipShape);\n    var valueDim = opt.valueDim;\n    var animationModel = symbolMeta.animationModel;\n    var dataIndex = symbolMeta.dataIndex;\n\n    if (clipPath) {\n      updateProps(clipPath, {\n        shape: clipShape\n      }, animationModel, dataIndex);\n    } else {\n      clipShape[valueDim.wh] = 0;\n      clipPath = new Rect({\n        shape: clipShape\n      });\n\n      bar.__pictorialBundle.setClipPath(clipPath);\n\n      bar.__pictorialClipPath = clipPath;\n      var target = {};\n      target[valueDim.wh] = symbolMeta.clipShape[valueDim.wh];\n      graphic[isUpdate ? 'updateProps' : 'initProps'](clipPath, {\n        shape: target\n      }, animationModel, dataIndex);\n    }\n  }\n}\n\nfunction getItemModel(data, dataIndex) {\n  var itemModel = data.getItemModel(dataIndex);\n  itemModel.getAnimationDelayParams = getAnimationDelayParams;\n  itemModel.isAnimationEnabled = isAnimationEnabled;\n  return itemModel;\n}\n\nfunction getAnimationDelayParams(path) {\n  // The order is the same as the z-order, see `symbolRepeatDiretion`.\n  return {\n    index: path.__pictorialAnimationIndex,\n    count: path.__pictorialRepeatTimes\n  };\n}\n\nfunction isAnimationEnabled() {\n  // `animation` prop can be set on itemModel in pictorial bar chart.\n  return this.parentModel.isAnimationEnabled() && !!this.getShallow('animation');\n}\n\nfunction createBar(data, opt, symbolMeta, isUpdate) {\n  // bar is the main element for each data.\n  var bar = new Group(); // bundle is used for location and clip.\n\n  var bundle = new Group();\n  bar.add(bundle);\n  bar.__pictorialBundle = bundle;\n  bundle.x = symbolMeta.bundlePosition[0];\n  bundle.y = symbolMeta.bundlePosition[1];\n\n  if (symbolMeta.symbolRepeat) {\n    createOrUpdateRepeatSymbols(bar, opt, symbolMeta);\n  } else {\n    createOrUpdateSingleSymbol(bar, opt, symbolMeta);\n  }\n\n  createOrUpdateBarRect(bar, symbolMeta, isUpdate);\n  createOrUpdateClip(bar, opt, symbolMeta, isUpdate);\n  bar.__pictorialShapeStr = getShapeStr(data, symbolMeta);\n  bar.__pictorialSymbolMeta = symbolMeta;\n  return bar;\n}\n\nfunction updateBar(bar, opt, symbolMeta) {\n  var animationModel = symbolMeta.animationModel;\n  var dataIndex = symbolMeta.dataIndex;\n  var bundle = bar.__pictorialBundle;\n  updateProps(bundle, {\n    x: symbolMeta.bundlePosition[0],\n    y: symbolMeta.bundlePosition[1]\n  }, animationModel, dataIndex);\n\n  if (symbolMeta.symbolRepeat) {\n    createOrUpdateRepeatSymbols(bar, opt, symbolMeta, true);\n  } else {\n    createOrUpdateSingleSymbol(bar, opt, symbolMeta, true);\n  }\n\n  createOrUpdateBarRect(bar, symbolMeta, true);\n  createOrUpdateClip(bar, opt, symbolMeta, true);\n}\n\nfunction removeBar(data, dataIndex, animationModel, bar) {\n  // Not show text when animating\n  var labelRect = bar.__pictorialBarRect;\n  labelRect && labelRect.removeTextContent();\n  var paths = [];\n  eachPath(bar, function (path) {\n    paths.push(path);\n  });\n  bar.__pictorialMainPath && paths.push(bar.__pictorialMainPath); // I do not find proper remove animation for clip yet.\n\n  bar.__pictorialClipPath && (animationModel = null);\n  each(paths, function (path) {\n    removeElement(path, {\n      scaleX: 0,\n      scaleY: 0\n    }, animationModel, dataIndex, function () {\n      bar.parent && bar.parent.remove(bar);\n    });\n  });\n  data.setItemGraphicEl(dataIndex, null);\n}\n\nfunction getShapeStr(data, symbolMeta) {\n  return [data.getItemVisual(symbolMeta.dataIndex, 'symbol') || 'none', !!symbolMeta.symbolRepeat, !!symbolMeta.symbolClip].join(':');\n}\n\nfunction eachPath(bar, cb, context) {\n  // Do not use Group#eachChild, because it do not support remove.\n  each(bar.__pictorialBundle.children(), function (el) {\n    el !== bar.__pictorialBarRect && cb.call(context, el);\n  });\n}\n\nfunction updateAttr(el, immediateAttrs, animationAttrs, symbolMeta, isUpdate, cb) {\n  immediateAttrs && el.attr(immediateAttrs); // when symbolCip used, only clip path has init animation, otherwise it would be weird effect.\n\n  if (symbolMeta.symbolClip && !isUpdate) {\n    animationAttrs && el.attr(animationAttrs);\n  } else {\n    animationAttrs && graphic[isUpdate ? 'updateProps' : 'initProps'](el, animationAttrs, symbolMeta.animationModel, symbolMeta.dataIndex, cb);\n  }\n}\n\nfunction updateCommon$1(bar, opt, symbolMeta) {\n  var dataIndex = symbolMeta.dataIndex;\n  var itemModel = symbolMeta.itemModel; // Color must be excluded.\n  // Because symbol provide setColor individually to set fill and stroke\n\n  var emphasisModel = itemModel.getModel('emphasis');\n  var emphasisStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n  var blurStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n  var selectStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();\n  var cursorStyle = itemModel.getShallow('cursor');\n  var focus = emphasisModel.get('focus');\n  var blurScope = emphasisModel.get('blurScope');\n  var hoverScale = emphasisModel.get('scale');\n  eachPath(bar, function (path) {\n    if (path instanceof ZRImage) {\n      var pathStyle = path.style;\n      path.useStyle(extend({\n        // TODO other properties like dx, dy ?\n        image: pathStyle.image,\n        x: pathStyle.x,\n        y: pathStyle.y,\n        width: pathStyle.width,\n        height: pathStyle.height\n      }, symbolMeta.style));\n    } else {\n      path.useStyle(symbolMeta.style);\n    }\n\n    var emphasisState = path.ensureState('emphasis');\n    emphasisState.style = emphasisStyle;\n\n    if (hoverScale) {\n      // NOTE: Must after scale is set after updateAttr\n      emphasisState.scaleX = path.scaleX * 1.1;\n      emphasisState.scaleY = path.scaleY * 1.1;\n    }\n\n    path.ensureState('blur').style = blurStyle;\n    path.ensureState('select').style = selectStyle;\n    cursorStyle && (path.cursor = cursorStyle);\n    path.z2 = symbolMeta.z2;\n  });\n  var barPositionOutside = opt.valueDim.posDesc[+(symbolMeta.boundingLength > 0)];\n  var barRect = bar.__pictorialBarRect;\n  setLabelStyle(barRect, getLabelStatesModels(itemModel), {\n    labelFetcher: opt.seriesModel,\n    labelDataIndex: dataIndex,\n    defaultText: getDefaultLabel(opt.seriesModel.getData(), dataIndex),\n    inheritColor: symbolMeta.style.fill,\n    defaultOpacity: symbolMeta.style.opacity,\n    defaultOutsidePosition: barPositionOutside\n  });\n  toggleHoverEmphasis(bar, focus, blurScope, emphasisModel.get('disabled'));\n}\n\nfunction toIntTimes(times) {\n  var roundedTimes = Math.round(times); // Escapse accurate error\n\n  return Math.abs(times - roundedTimes) < 1e-4 ? roundedTimes : Math.ceil(times);\n}\n\nvar PictorialBarSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(PictorialBarSeriesModel, _super);\n\n  function PictorialBarSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = PictorialBarSeriesModel.type;\n    _this.hasSymbolVisual = true;\n    _this.defaultSymbol = 'roundRect';\n    return _this;\n  }\n\n  PictorialBarSeriesModel.prototype.getInitialData = function (option) {\n    // Disable stack.\n    option.stack = null;\n    return _super.prototype.getInitialData.apply(this, arguments);\n  };\n\n  PictorialBarSeriesModel.type = 'series.pictorialBar';\n  PictorialBarSeriesModel.dependencies = ['grid'];\n  PictorialBarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, {\n    symbol: 'circle',\n    symbolSize: null,\n    symbolRotate: null,\n    symbolPosition: null,\n    symbolOffset: null,\n    symbolMargin: null,\n    symbolRepeat: false,\n    symbolRepeatDirection: 'end',\n    symbolClip: false,\n    symbolBoundingData: null,\n    symbolPatternSize: 400,\n    barGap: '-100%',\n    // z can be set in data item, which is z2 actually.\n    // Disable progressive\n    progressive: 0,\n    emphasis: {\n      // By default pictorialBar do not hover scale. Hover scale is not suitable\n      // for the case that both has foreground and background.\n      scale: false\n    },\n    select: {\n      itemStyle: {\n        borderColor: '#212121'\n      }\n    }\n  });\n  return PictorialBarSeriesModel;\n}(BaseBarSeriesModel);\n\nfunction install$o(registers) {\n  registers.registerChartView(PictorialBarView);\n  registers.registerSeriesModel(PictorialBarSeriesModel);\n  registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'pictorialBar')); // Do layout after other overall layout, which can prepare some information.\n\n  registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('pictorialBar'));\n}\n\nvar ThemeRiverView =\n/** @class */\nfunction (_super) {\n  __extends(ThemeRiverView, _super);\n\n  function ThemeRiverView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ThemeRiverView.type;\n    _this._layers = [];\n    return _this;\n  }\n\n  ThemeRiverView.prototype.render = function (seriesModel, ecModel, api) {\n    var data = seriesModel.getData();\n    var self = this;\n    var group = this.group;\n    var layersSeries = seriesModel.getLayerSeries();\n    var layoutInfo = data.getLayout('layoutInfo');\n    var rect = layoutInfo.rect;\n    var boundaryGap = layoutInfo.boundaryGap;\n    group.x = 0;\n    group.y = rect.y + boundaryGap[0];\n\n    function keyGetter(item) {\n      return item.name;\n    }\n\n    var dataDiffer = new DataDiffer(this._layersSeries || [], layersSeries, keyGetter, keyGetter);\n    var newLayersGroups = [];\n    dataDiffer.add(bind(process, this, 'add')).update(bind(process, this, 'update')).remove(bind(process, this, 'remove')).execute();\n\n    function process(status, idx, oldIdx) {\n      var oldLayersGroups = self._layers;\n\n      if (status === 'remove') {\n        group.remove(oldLayersGroups[idx]);\n        return;\n      }\n\n      var points0 = [];\n      var points1 = [];\n      var style;\n      var indices = layersSeries[idx].indices;\n      var j = 0;\n\n      for (; j < indices.length; j++) {\n        var layout = data.getItemLayout(indices[j]);\n        var x = layout.x;\n        var y0 = layout.y0;\n        var y = layout.y;\n        points0.push(x, y0);\n        points1.push(x, y0 + y);\n        style = data.getItemVisual(indices[j], 'style');\n      }\n\n      var polygon;\n      var textLayout = data.getItemLayout(indices[0]);\n      var labelModel = seriesModel.getModel('label');\n      var margin = labelModel.get('margin');\n      var emphasisModel = seriesModel.getModel('emphasis');\n\n      if (status === 'add') {\n        var layerGroup = newLayersGroups[idx] = new Group();\n        polygon = new ECPolygon({\n          shape: {\n            points: points0,\n            stackedOnPoints: points1,\n            smooth: 0.4,\n            stackedOnSmooth: 0.4,\n            smoothConstraint: false\n          },\n          z2: 0\n        });\n        layerGroup.add(polygon);\n        group.add(layerGroup);\n\n        if (seriesModel.isAnimationEnabled()) {\n          polygon.setClipPath(createGridClipShape$2(polygon.getBoundingRect(), seriesModel, function () {\n            polygon.removeClipPath();\n          }));\n        }\n      } else {\n        var layerGroup = oldLayersGroups[oldIdx];\n        polygon = layerGroup.childAt(0);\n        group.add(layerGroup);\n        newLayersGroups[idx] = layerGroup;\n        updateProps(polygon, {\n          shape: {\n            points: points0,\n            stackedOnPoints: points1\n          }\n        }, seriesModel);\n        saveOldStyle(polygon);\n      }\n\n      setLabelStyle(polygon, getLabelStatesModels(seriesModel), {\n        labelDataIndex: indices[j - 1],\n        defaultText: data.getName(indices[j - 1]),\n        inheritColor: style.fill\n      }, {\n        normal: {\n          verticalAlign: 'middle' // align: 'right'\n\n        }\n      });\n      polygon.setTextConfig({\n        position: null,\n        local: true\n      });\n      var labelEl = polygon.getTextContent(); // TODO More label position options.\n\n      if (labelEl) {\n        labelEl.x = textLayout.x - margin;\n        labelEl.y = textLayout.y0 + textLayout.y / 2;\n      }\n\n      polygon.useStyle(style);\n      data.setItemGraphicEl(idx, polygon);\n      setStatesStylesFromModel(polygon, seriesModel);\n      toggleHoverEmphasis(polygon, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n    }\n\n    this._layersSeries = layersSeries;\n    this._layers = newLayersGroups;\n  };\n\n  ThemeRiverView.type = 'themeRiver';\n  return ThemeRiverView;\n}(ChartView);\n\nfunction createGridClipShape$2(rect, seriesModel, cb) {\n  var rectEl = new Rect({\n    shape: {\n      x: rect.x - 10,\n      y: rect.y - 10,\n      width: 0,\n      height: rect.height + 20\n    }\n  });\n  initProps(rectEl, {\n    shape: {\n      x: rect.x - 50,\n      width: rect.width + 100,\n      height: rect.height + 20\n    }\n  }, seriesModel, cb);\n  return rectEl;\n}\n\nvar DATA_NAME_INDEX = 2;\n\nvar ThemeRiverSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(ThemeRiverSeriesModel, _super);\n\n  function ThemeRiverSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ThemeRiverSeriesModel.type;\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  ThemeRiverSeriesModel.prototype.init = function (option) {\n    // eslint-disable-next-line\n    _super.prototype.init.apply(this, arguments); // Put this function here is for the sake of consistency of code style.\n    // Enable legend selection for each data item\n    // Use a function instead of direct access because data reference may changed\n\n\n    this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n  };\n  /**\n   * If there is no value of a certain point in the time for some event,set it value to 0.\n   *\n   * @param {Array} data  initial data in the option\n   * @return {Array}\n   */\n\n\n  ThemeRiverSeriesModel.prototype.fixData = function (data) {\n    var rawDataLength = data.length;\n    /**\n     * Make sure every layer data get the same keys.\n     * The value index tells which layer has visited.\n     * {\n     *  2014/01/01: -1\n     * }\n     */\n\n    var timeValueKeys = {}; // grouped data by name\n\n    var groupResult = groupData(data, function (item) {\n      if (!timeValueKeys.hasOwnProperty(item[0] + '')) {\n        timeValueKeys[item[0] + ''] = -1;\n      }\n\n      return item[2];\n    });\n    var layerData = [];\n    groupResult.buckets.each(function (items, key) {\n      layerData.push({\n        name: key,\n        dataList: items\n      });\n    });\n    var layerNum = layerData.length;\n\n    for (var k = 0; k < layerNum; ++k) {\n      var name_1 = layerData[k].name;\n\n      for (var j = 0; j < layerData[k].dataList.length; ++j) {\n        var timeValue = layerData[k].dataList[j][0] + '';\n        timeValueKeys[timeValue] = k;\n      }\n\n      for (var timeValue in timeValueKeys) {\n        if (timeValueKeys.hasOwnProperty(timeValue) && timeValueKeys[timeValue] !== k) {\n          timeValueKeys[timeValue] = k;\n          data[rawDataLength] = [timeValue, 0, name_1];\n          rawDataLength++;\n        }\n      }\n    }\n\n    return data;\n  };\n  /**\n   * @override\n   * @param  option  the initial option that user gave\n   * @param  ecModel  the model object for themeRiver option\n   */\n\n\n  ThemeRiverSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    var singleAxisModel = this.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n    var axisType = singleAxisModel.get('type'); // filter the data item with the value of label is undefined\n\n    var filterData = filter(option.data, function (dataItem) {\n      return dataItem[2] !== undefined;\n    }); // ??? TODO design a stage to transfer data for themeRiver and lines?\n\n    var data = this.fixData(filterData || []);\n    var nameList = [];\n    var nameMap = this.nameMap = createHashMap();\n    var count = 0;\n\n    for (var i = 0; i < data.length; ++i) {\n      nameList.push(data[i][DATA_NAME_INDEX]);\n\n      if (!nameMap.get(data[i][DATA_NAME_INDEX])) {\n        nameMap.set(data[i][DATA_NAME_INDEX], count);\n        count++;\n      }\n    }\n\n    var dimensions = prepareSeriesDataSchema(data, {\n      coordDimensions: ['single'],\n      dimensionsDefine: [{\n        name: 'time',\n        type: getDimensionTypeByAxis(axisType)\n      }, {\n        name: 'value',\n        type: 'float'\n      }, {\n        name: 'name',\n        type: 'ordinal'\n      }],\n      encodeDefine: {\n        single: 0,\n        value: 1,\n        itemName: 2\n      }\n    }).dimensions;\n    var list = new SeriesData(dimensions, this);\n    list.initData(data);\n    return list;\n  };\n  /**\n   * The raw data is divided into multiple layers and each layer\n   *     has same name.\n   */\n\n\n  ThemeRiverSeriesModel.prototype.getLayerSeries = function () {\n    var data = this.getData();\n    var lenCount = data.count();\n    var indexArr = [];\n\n    for (var i = 0; i < lenCount; ++i) {\n      indexArr[i] = i;\n    }\n\n    var timeDim = data.mapDimension('single'); // data group by name\n\n    var groupResult = groupData(indexArr, function (index) {\n      return data.get('name', index);\n    });\n    var layerSeries = [];\n    groupResult.buckets.each(function (items, key) {\n      items.sort(function (index1, index2) {\n        return data.get(timeDim, index1) - data.get(timeDim, index2);\n      });\n      layerSeries.push({\n        name: key,\n        indices: items\n      });\n    });\n    return layerSeries;\n  };\n  /**\n   * Get data indices for show tooltip content\n   */\n\n\n  ThemeRiverSeriesModel.prototype.getAxisTooltipData = function (dim, value, baseAxis) {\n    if (!isArray(dim)) {\n      dim = dim ? [dim] : [];\n    }\n\n    var data = this.getData();\n    var layerSeries = this.getLayerSeries();\n    var indices = [];\n    var layerNum = layerSeries.length;\n    var nestestValue;\n\n    for (var i = 0; i < layerNum; ++i) {\n      var minDist = Number.MAX_VALUE;\n      var nearestIdx = -1;\n      var pointNum = layerSeries[i].indices.length;\n\n      for (var j = 0; j < pointNum; ++j) {\n        var theValue = data.get(dim[0], layerSeries[i].indices[j]);\n        var dist = Math.abs(theValue - value);\n\n        if (dist <= minDist) {\n          nestestValue = theValue;\n          minDist = dist;\n          nearestIdx = layerSeries[i].indices[j];\n        }\n      }\n\n      indices.push(nearestIdx);\n    }\n\n    return {\n      dataIndices: indices,\n      nestestValue: nestestValue\n    };\n  };\n\n  ThemeRiverSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    var data = this.getData();\n    var name = data.getName(dataIndex);\n    var value = data.get(data.mapDimension('value'), dataIndex);\n    return createTooltipMarkup('nameValue', {\n      name: name,\n      value: value\n    });\n  };\n\n  ThemeRiverSeriesModel.type = 'series.themeRiver';\n  ThemeRiverSeriesModel.dependencies = ['singleAxis'];\n  ThemeRiverSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    colorBy: 'data',\n    coordinateSystem: 'singleAxis',\n    // gap in axis's orthogonal orientation\n    boundaryGap: ['10%', '10%'],\n    // legendHoverLink: true,\n    singleAxisIndex: 0,\n    animationEasing: 'linear',\n    label: {\n      margin: 4,\n      show: true,\n      position: 'left',\n      fontSize: 11\n    },\n    emphasis: {\n      label: {\n        show: true\n      }\n    }\n  };\n  return ThemeRiverSeriesModel;\n}(SeriesModel);\n\nfunction themeRiverLayout(ecModel, api) {\n  ecModel.eachSeriesByType('themeRiver', function (seriesModel) {\n    var data = seriesModel.getData();\n    var single = seriesModel.coordinateSystem;\n    var layoutInfo = {}; // use the axis boundingRect for view\n\n    var rect = single.getRect();\n    layoutInfo.rect = rect;\n    var boundaryGap = seriesModel.get('boundaryGap');\n    var axis = single.getAxis();\n    layoutInfo.boundaryGap = boundaryGap;\n\n    if (axis.orient === 'horizontal') {\n      boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.height);\n      boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.height);\n      var height = rect.height - boundaryGap[0] - boundaryGap[1];\n      doThemeRiverLayout(data, seriesModel, height);\n    } else {\n      boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.width);\n      boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.width);\n      var width = rect.width - boundaryGap[0] - boundaryGap[1];\n      doThemeRiverLayout(data, seriesModel, width);\n    }\n\n    data.setLayout('layoutInfo', layoutInfo);\n  });\n}\n/**\n * The layout information about themeriver\n *\n * @param data  data in the series\n * @param seriesModel  the model object of themeRiver series\n * @param height  value used to compute every series height\n */\n\nfunction doThemeRiverLayout(data, seriesModel, height) {\n  if (!data.count()) {\n    return;\n  }\n\n  var coordSys = seriesModel.coordinateSystem; // the data in each layer are organized into a series.\n\n  var layerSeries = seriesModel.getLayerSeries(); // the points in each layer.\n\n  var timeDim = data.mapDimension('single');\n  var valueDim = data.mapDimension('value');\n  var layerPoints = map(layerSeries, function (singleLayer) {\n    return map(singleLayer.indices, function (idx) {\n      var pt = coordSys.dataToPoint(data.get(timeDim, idx));\n      pt[1] = data.get(valueDim, idx);\n      return pt;\n    });\n  });\n  var base = computeBaseline(layerPoints);\n  var baseLine = base.y0;\n  var ky = height / base.max; // set layout information for each item.\n\n  var n = layerSeries.length;\n  var m = layerSeries[0].indices.length;\n  var baseY0;\n\n  for (var j = 0; j < m; ++j) {\n    baseY0 = baseLine[j] * ky;\n    data.setItemLayout(layerSeries[0].indices[j], {\n      layerIndex: 0,\n      x: layerPoints[0][j][0],\n      y0: baseY0,\n      y: layerPoints[0][j][1] * ky\n    });\n\n    for (var i = 1; i < n; ++i) {\n      baseY0 += layerPoints[i - 1][j][1] * ky;\n      data.setItemLayout(layerSeries[i].indices[j], {\n        layerIndex: i,\n        x: layerPoints[i][j][0],\n        y0: baseY0,\n        y: layerPoints[i][j][1] * ky\n      });\n    }\n  }\n}\n/**\n * Compute the baseLine of the rawdata\n * Inspired by Lee Byron's paper Stacked Graphs - Geometry & Aesthetics\n *\n * @param  data  the points in each layer\n */\n\n\nfunction computeBaseline(data) {\n  var layerNum = data.length;\n  var pointNum = data[0].length;\n  var sums = [];\n  var y0 = [];\n  var max = 0;\n\n  for (var i = 0; i < pointNum; ++i) {\n    var temp = 0;\n\n    for (var j = 0; j < layerNum; ++j) {\n      temp += data[j][i][1];\n    }\n\n    if (temp > max) {\n      max = temp;\n    }\n\n    sums.push(temp);\n  }\n\n  for (var k = 0; k < pointNum; ++k) {\n    y0[k] = (max - sums[k]) / 2;\n  }\n\n  max = 0;\n\n  for (var l = 0; l < pointNum; ++l) {\n    var sum = sums[l] + y0[l];\n\n    if (sum > max) {\n      max = sum;\n    }\n  }\n\n  return {\n    y0: y0,\n    max: max\n  };\n}\n\nfunction install$p(registers) {\n  registers.registerChartView(ThemeRiverView);\n  registers.registerSeriesModel(ThemeRiverSeriesModel);\n  registers.registerLayout(themeRiverLayout);\n  registers.registerProcessor(dataFilter('themeRiver'));\n}\n\nvar DEFAULT_SECTOR_Z = 2;\nvar DEFAULT_TEXT_Z = 4;\n/**\n * Sunburstce of Sunburst including Sector, Label, LabelLine\n */\n\nvar SunburstPiece =\n/** @class */\nfunction (_super) {\n  __extends(SunburstPiece, _super);\n\n  function SunburstPiece(node, seriesModel, ecModel, api) {\n    var _this = _super.call(this) || this;\n\n    _this.z2 = DEFAULT_SECTOR_Z;\n    _this.textConfig = {\n      inside: true\n    };\n    getECData(_this).seriesIndex = seriesModel.seriesIndex;\n    var text = new ZRText({\n      z2: DEFAULT_TEXT_Z,\n      silent: node.getModel().get(['label', 'silent'])\n    });\n\n    _this.setTextContent(text);\n\n    _this.updateData(true, node, seriesModel, ecModel, api);\n\n    return _this;\n  }\n\n  SunburstPiece.prototype.updateData = function (firstCreate, node, // state: 'emphasis' | 'normal' | 'highlight' | 'downplay',\n  seriesModel, ecModel, api) {\n    this.node = node;\n    node.piece = this;\n    seriesModel = seriesModel || this._seriesModel;\n    ecModel = ecModel || this._ecModel;\n    var sector = this;\n    getECData(sector).dataIndex = node.dataIndex;\n    var itemModel = node.getModel();\n    var emphasisModel = itemModel.getModel('emphasis');\n    var layout = node.getLayout();\n    var sectorShape = extend({}, layout);\n    sectorShape.label = null;\n    var normalStyle = node.getVisual('style');\n    normalStyle.lineJoin = 'bevel';\n    var decal = node.getVisual('decal');\n\n    if (decal) {\n      normalStyle.decal = createOrUpdatePatternFromDecal(decal, api);\n    }\n\n    var cornerRadius = getSectorCornerRadius(itemModel.getModel('itemStyle'), sectorShape, true);\n    extend(sectorShape, cornerRadius);\n    each(SPECIAL_STATES, function (stateName) {\n      var state = sector.ensureState(stateName);\n      var itemStyleModel = itemModel.getModel([stateName, 'itemStyle']);\n      state.style = itemStyleModel.getItemStyle(); // border radius\n\n      var cornerRadius = getSectorCornerRadius(itemStyleModel, sectorShape);\n\n      if (cornerRadius) {\n        state.shape = cornerRadius;\n      }\n    });\n\n    if (firstCreate) {\n      sector.setShape(sectorShape);\n      sector.shape.r = layout.r0;\n      updateProps(sector, {\n        shape: {\n          r: layout.r\n        }\n      }, seriesModel, node.dataIndex);\n    } else {\n      // Disable animation for gradient since no interpolation method\n      // is supported for gradient\n      updateProps(sector, {\n        shape: sectorShape\n      }, seriesModel);\n      saveOldStyle(sector);\n    }\n\n    sector.useStyle(normalStyle);\n\n    this._updateLabel(seriesModel);\n\n    var cursorStyle = itemModel.getShallow('cursor');\n    cursorStyle && sector.attr('cursor', cursorStyle);\n    this._seriesModel = seriesModel || this._seriesModel;\n    this._ecModel = ecModel || this._ecModel;\n    var focus = emphasisModel.get('focus');\n    var focusOrIndices = focus === 'ancestor' ? node.getAncestorsIndices() : focus === 'descendant' ? node.getDescendantIndices() : focus;\n    toggleHoverEmphasis(this, focusOrIndices, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n  };\n\n  SunburstPiece.prototype._updateLabel = function (seriesModel) {\n    var _this = this;\n\n    var itemModel = this.node.getModel();\n    var normalLabelModel = itemModel.getModel('label');\n    var layout = this.node.getLayout();\n    var angle = layout.endAngle - layout.startAngle;\n    var midAngle = (layout.startAngle + layout.endAngle) / 2;\n    var dx = Math.cos(midAngle);\n    var dy = Math.sin(midAngle);\n    var sector = this;\n    var label = sector.getTextContent();\n    var dataIndex = this.node.dataIndex;\n    var labelMinAngle = normalLabelModel.get('minAngle') / 180 * Math.PI;\n    var isNormalShown = normalLabelModel.get('show') && !(labelMinAngle != null && Math.abs(angle) < labelMinAngle);\n    label.ignore = !isNormalShown; // TODO use setLabelStyle\n\n    each(DISPLAY_STATES, function (stateName) {\n      var labelStateModel = stateName === 'normal' ? itemModel.getModel('label') : itemModel.getModel([stateName, 'label']);\n      var isNormal = stateName === 'normal';\n      var state = isNormal ? label : label.ensureState(stateName);\n      var text = seriesModel.getFormattedLabel(dataIndex, stateName);\n\n      if (isNormal) {\n        text = text || _this.node.name;\n      }\n\n      state.style = createTextStyle(labelStateModel, {}, null, stateName !== 'normal', true);\n\n      if (text) {\n        state.style.text = text;\n      } // Not displaying text when angle is too small\n\n\n      var isShown = labelStateModel.get('show');\n\n      if (isShown != null && !isNormal) {\n        state.ignore = !isShown;\n      }\n\n      var labelPosition = getLabelAttr(labelStateModel, 'position');\n      var sectorState = isNormal ? sector : sector.states[stateName];\n      var labelColor = sectorState.style.fill;\n      sectorState.textConfig = {\n        outsideFill: labelStateModel.get('color') === 'inherit' ? labelColor : null,\n        inside: labelPosition !== 'outside'\n      };\n      var r;\n      var labelPadding = getLabelAttr(labelStateModel, 'distance') || 0;\n      var textAlign = getLabelAttr(labelStateModel, 'align');\n\n      if (labelPosition === 'outside') {\n        r = layout.r + labelPadding;\n        textAlign = midAngle > Math.PI / 2 ? 'right' : 'left';\n      } else {\n        if (!textAlign || textAlign === 'center') {\n          // Put label in the center if it's a circle\n          if (angle === 2 * Math.PI && layout.r0 === 0) {\n            r = 0;\n          } else {\n            r = (layout.r + layout.r0) / 2;\n          }\n\n          textAlign = 'center';\n        } else if (textAlign === 'left') {\n          r = layout.r0 + labelPadding;\n\n          if (midAngle > Math.PI / 2) {\n            textAlign = 'right';\n          }\n        } else if (textAlign === 'right') {\n          r = layout.r - labelPadding;\n\n          if (midAngle > Math.PI / 2) {\n            textAlign = 'left';\n          }\n        }\n      }\n\n      state.style.align = textAlign;\n      state.style.verticalAlign = getLabelAttr(labelStateModel, 'verticalAlign') || 'middle';\n      state.x = r * dx + layout.cx;\n      state.y = r * dy + layout.cy;\n      var rotateType = getLabelAttr(labelStateModel, 'rotate');\n      var rotate = 0;\n\n      if (rotateType === 'radial') {\n        rotate = -midAngle;\n\n        if (rotate < -Math.PI / 2) {\n          rotate += Math.PI;\n        }\n      } else if (rotateType === 'tangential') {\n        rotate = Math.PI / 2 - midAngle;\n\n        if (rotate > Math.PI / 2) {\n          rotate -= Math.PI;\n        } else if (rotate < -Math.PI / 2) {\n          rotate += Math.PI;\n        }\n      } else if (isNumber(rotateType)) {\n        rotate = rotateType * Math.PI / 180;\n      }\n\n      state.rotation = rotate;\n    });\n\n    function getLabelAttr(model, name) {\n      var stateAttr = model.get(name);\n\n      if (stateAttr == null) {\n        return normalLabelModel.get(name);\n      }\n\n      return stateAttr;\n    }\n\n    label.dirtyStyle();\n  };\n\n  return SunburstPiece;\n}(Sector);\n\nvar ROOT_TO_NODE_ACTION = 'sunburstRootToNode';\nvar HIGHLIGHT_ACTION = 'sunburstHighlight';\nvar UNHIGHLIGHT_ACTION = 'sunburstUnhighlight';\nfunction installSunburstAction(registers) {\n  registers.registerAction({\n    type: ROOT_TO_NODE_ACTION,\n    update: 'updateView'\n  }, function (payload, ecModel) {\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'sunburst',\n      query: payload\n    }, handleRootToNode);\n\n    function handleRootToNode(model, index) {\n      var targetInfo = retrieveTargetInfo(payload, [ROOT_TO_NODE_ACTION], model);\n\n      if (targetInfo) {\n        var originViewRoot = model.getViewRoot();\n\n        if (originViewRoot) {\n          payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) ? 'rollUp' : 'drillDown';\n        }\n\n        model.resetViewRoot(targetInfo.node);\n      }\n    }\n  });\n  registers.registerAction({\n    type: HIGHLIGHT_ACTION,\n    update: 'none'\n  }, function (payload, ecModel, api) {\n    // Clone\n    payload = extend({}, payload);\n    ecModel.eachComponent({\n      mainType: 'series',\n      subType: 'sunburst',\n      query: payload\n    }, handleHighlight);\n\n    function handleHighlight(model) {\n      var targetInfo = retrieveTargetInfo(payload, [HIGHLIGHT_ACTION], model);\n\n      if (targetInfo) {\n        payload.dataIndex = targetInfo.node.dataIndex;\n      }\n    }\n\n    if (\"development\" !== 'production') {\n      deprecateReplaceLog('sunburstHighlight', 'highlight');\n    } // Fast forward action\n\n\n    api.dispatchAction(extend(payload, {\n      type: 'highlight'\n    }));\n  });\n  registers.registerAction({\n    type: UNHIGHLIGHT_ACTION,\n    update: 'updateView'\n  }, function (payload, ecModel, api) {\n    payload = extend({}, payload);\n\n    if (\"development\" !== 'production') {\n      deprecateReplaceLog('sunburstUnhighlight', 'downplay');\n    }\n\n    api.dispatchAction(extend(payload, {\n      type: 'downplay'\n    }));\n  });\n}\n\nvar SunburstView =\n/** @class */\nfunction (_super) {\n  __extends(SunburstView, _super);\n\n  function SunburstView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SunburstView.type;\n    return _this;\n  }\n\n  SunburstView.prototype.render = function (seriesModel, ecModel, api, // @ts-ignore\n  payload) {\n    var self = this;\n    this.seriesModel = seriesModel;\n    this.api = api;\n    this.ecModel = ecModel;\n    var data = seriesModel.getData();\n    var virtualRoot = data.tree.root;\n    var newRoot = seriesModel.getViewRoot();\n    var group = this.group;\n    var renderLabelForZeroData = seriesModel.get('renderLabelForZeroData');\n    var newChildren = [];\n    newRoot.eachNode(function (node) {\n      newChildren.push(node);\n    });\n    var oldChildren = this._oldChildren || [];\n    dualTravel(newChildren, oldChildren);\n    renderRollUp(virtualRoot, newRoot);\n\n    this._initEvents();\n\n    this._oldChildren = newChildren;\n\n    function dualTravel(newChildren, oldChildren) {\n      if (newChildren.length === 0 && oldChildren.length === 0) {\n        return;\n      }\n\n      new DataDiffer(oldChildren, newChildren, getKey, getKey).add(processNode).update(processNode).remove(curry(processNode, null)).execute();\n\n      function getKey(node) {\n        return node.getId();\n      }\n\n      function processNode(newIdx, oldIdx) {\n        var newNode = newIdx == null ? null : newChildren[newIdx];\n        var oldNode = oldIdx == null ? null : oldChildren[oldIdx];\n        doRenderNode(newNode, oldNode);\n      }\n    }\n\n    function doRenderNode(newNode, oldNode) {\n      if (!renderLabelForZeroData && newNode && !newNode.getValue()) {\n        // Not render data with value 0\n        newNode = null;\n      }\n\n      if (newNode !== virtualRoot && oldNode !== virtualRoot) {\n        if (oldNode && oldNode.piece) {\n          if (newNode) {\n            // Update\n            oldNode.piece.updateData(false, newNode, seriesModel, ecModel, api); // For tooltip\n\n            data.setItemGraphicEl(newNode.dataIndex, oldNode.piece);\n          } else {\n            // Remove\n            removeNode(oldNode);\n          }\n        } else if (newNode) {\n          // Add\n          var piece = new SunburstPiece(newNode, seriesModel, ecModel, api);\n          group.add(piece); // For tooltip\n\n          data.setItemGraphicEl(newNode.dataIndex, piece);\n        }\n      }\n    }\n\n    function removeNode(node) {\n      if (!node) {\n        return;\n      }\n\n      if (node.piece) {\n        group.remove(node.piece);\n        node.piece = null;\n      }\n    }\n\n    function renderRollUp(virtualRoot, viewRoot) {\n      if (viewRoot.depth > 0) {\n        // Render\n        if (self.virtualPiece) {\n          // Update\n          self.virtualPiece.updateData(false, virtualRoot, seriesModel, ecModel, api);\n        } else {\n          // Add\n          self.virtualPiece = new SunburstPiece(virtualRoot, seriesModel, ecModel, api);\n          group.add(self.virtualPiece);\n        } // TODO event scope\n\n\n        viewRoot.piece.off('click');\n        self.virtualPiece.on('click', function (e) {\n          self._rootToNode(viewRoot.parentNode);\n        });\n      } else if (self.virtualPiece) {\n        // Remove\n        group.remove(self.virtualPiece);\n        self.virtualPiece = null;\n      }\n    }\n  };\n  /**\n   * @private\n   */\n\n\n  SunburstView.prototype._initEvents = function () {\n    var _this = this;\n\n    this.group.off('click');\n    this.group.on('click', function (e) {\n      var targetFound = false;\n\n      var viewRoot = _this.seriesModel.getViewRoot();\n\n      viewRoot.eachNode(function (node) {\n        if (!targetFound && node.piece && node.piece === e.target) {\n          var nodeClick = node.getModel().get('nodeClick');\n\n          if (nodeClick === 'rootToNode') {\n            _this._rootToNode(node);\n          } else if (nodeClick === 'link') {\n            var itemModel = node.getModel();\n            var link = itemModel.get('link');\n\n            if (link) {\n              var linkTarget = itemModel.get('target', true) || '_blank';\n              windowOpen(link, linkTarget);\n            }\n          }\n\n          targetFound = true;\n        }\n      });\n    });\n  };\n  /**\n   * @private\n   */\n\n\n  SunburstView.prototype._rootToNode = function (node) {\n    if (node !== this.seriesModel.getViewRoot()) {\n      this.api.dispatchAction({\n        type: ROOT_TO_NODE_ACTION,\n        from: this.uid,\n        seriesId: this.seriesModel.id,\n        targetNode: node\n      });\n    }\n  };\n  /**\n   * @implement\n   */\n\n\n  SunburstView.prototype.containPoint = function (point, seriesModel) {\n    var treeRoot = seriesModel.getData();\n    var itemLayout = treeRoot.getItemLayout(0);\n\n    if (itemLayout) {\n      var dx = point[0] - itemLayout.cx;\n      var dy = point[1] - itemLayout.cy;\n      var radius = Math.sqrt(dx * dx + dy * dy);\n      return radius <= itemLayout.r && radius >= itemLayout.r0;\n    }\n  };\n\n  SunburstView.type = 'sunburst';\n  return SunburstView;\n}(ChartView);\n\nvar SunburstSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(SunburstSeriesModel, _super);\n\n  function SunburstSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SunburstSeriesModel.type;\n    _this.ignoreStyleOnData = true;\n    return _this;\n  }\n\n  SunburstSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    // Create a virtual root.\n    var root = {\n      name: option.name,\n      children: option.data\n    };\n    completeTreeValue$1(root);\n    var levelModels = this._levelModels = map(option.levels || [], function (levelDefine) {\n      return new Model(levelDefine, this, ecModel);\n    }, this); // Make sure always a new tree is created when setOption,\n    // in TreemapView, we check whether oldTree === newTree\n    // to choose mappings approach among old shapes and new shapes.\n\n    var tree = Tree.createTree(root, this, beforeLink);\n\n    function beforeLink(nodeData) {\n      nodeData.wrapMethod('getItemModel', function (model, idx) {\n        var node = tree.getNodeByDataIndex(idx);\n        var levelModel = levelModels[node.depth];\n        levelModel && (model.parentModel = levelModel);\n        return model;\n      });\n    }\n\n    return tree.data;\n  };\n\n  SunburstSeriesModel.prototype.optionUpdated = function () {\n    this.resetViewRoot();\n  };\n  /*\n   * @override\n   */\n\n\n  SunburstSeriesModel.prototype.getDataParams = function (dataIndex) {\n    var params = _super.prototype.getDataParams.apply(this, arguments);\n\n    var node = this.getData().tree.getNodeByDataIndex(dataIndex);\n    params.treePathInfo = wrapTreePathInfo(node, this);\n    return params;\n  };\n\n  SunburstSeriesModel.prototype.getLevelModel = function (node) {\n    return this._levelModels && this._levelModels[node.depth];\n  };\n\n  SunburstSeriesModel.prototype.getViewRoot = function () {\n    return this._viewRoot;\n  };\n\n  SunburstSeriesModel.prototype.resetViewRoot = function (viewRoot) {\n    viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot;\n    var root = this.getRawData().tree.root;\n\n    if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) {\n      this._viewRoot = root;\n    }\n  };\n\n  SunburstSeriesModel.prototype.enableAriaDecal = function () {\n    enableAriaDecalForTree(this);\n  };\n\n  SunburstSeriesModel.type = 'series.sunburst';\n  SunburstSeriesModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    // 默认全局居中\n    center: ['50%', '50%'],\n    radius: [0, '75%'],\n    // 默认顺时针\n    clockwise: true,\n    startAngle: 90,\n    // 最小角度改为0\n    minAngle: 0,\n    // If still show when all data zero.\n    stillShowZeroSum: true,\n    // 'rootToNode', 'link', or false\n    nodeClick: 'rootToNode',\n    renderLabelForZeroData: false,\n    label: {\n      // could be: 'radial', 'tangential', or 'none'\n      rotate: 'radial',\n      show: true,\n      opacity: 1,\n      // 'left' is for inner side of inside, and 'right' is for outer\n      // side for inside\n      align: 'center',\n      position: 'inside',\n      distance: 5,\n      silent: true\n    },\n    itemStyle: {\n      borderWidth: 1,\n      borderColor: 'white',\n      borderType: 'solid',\n      shadowBlur: 0,\n      shadowColor: 'rgba(0, 0, 0, 0.2)',\n      shadowOffsetX: 0,\n      shadowOffsetY: 0,\n      opacity: 1\n    },\n    emphasis: {\n      focus: 'descendant'\n    },\n    blur: {\n      itemStyle: {\n        opacity: 0.2\n      },\n      label: {\n        opacity: 0.1\n      }\n    },\n    // Animation type can be expansion, scale.\n    animationType: 'expansion',\n    animationDuration: 1000,\n    animationDurationUpdate: 500,\n    data: [],\n\n    /**\n     * Sort order.\n     *\n     * Valid values: 'desc', 'asc', null, or callback function.\n     * 'desc' and 'asc' for descend and ascendant order;\n     * null for not sorting;\n     * example of callback function:\n     * function(nodeA, nodeB) {\n     *     return nodeA.getValue() - nodeB.getValue();\n     * }\n     */\n    sort: 'desc'\n  };\n  return SunburstSeriesModel;\n}(SeriesModel);\n\nfunction completeTreeValue$1(dataNode) {\n  // Postorder travel tree.\n  // If value of none-leaf node is not set,\n  // calculate it by suming up the value of all children.\n  var sum = 0;\n  each(dataNode.children, function (child) {\n    completeTreeValue$1(child);\n    var childValue = child.value; // TODO First value of array must be a number\n\n    isArray(childValue) && (childValue = childValue[0]);\n    sum += childValue;\n  });\n  var thisValue = dataNode.value;\n\n  if (isArray(thisValue)) {\n    thisValue = thisValue[0];\n  }\n\n  if (thisValue == null || isNaN(thisValue)) {\n    thisValue = sum;\n  } // Value should not less than 0.\n\n\n  if (thisValue < 0) {\n    thisValue = 0;\n  }\n\n  isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue;\n}\n\nvar RADIAN$2 = Math.PI / 180;\nfunction sunburstLayout(seriesType, ecModel, api) {\n  ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n    var center = seriesModel.get('center');\n    var radius = seriesModel.get('radius');\n\n    if (!isArray(radius)) {\n      radius = [0, radius];\n    }\n\n    if (!isArray(center)) {\n      center = [center, center];\n    }\n\n    var width = api.getWidth();\n    var height = api.getHeight();\n    var size = Math.min(width, height);\n    var cx = parsePercent$1(center[0], width);\n    var cy = parsePercent$1(center[1], height);\n    var r0 = parsePercent$1(radius[0], size / 2);\n    var r = parsePercent$1(radius[1], size / 2);\n    var startAngle = -seriesModel.get('startAngle') * RADIAN$2;\n    var minAngle = seriesModel.get('minAngle') * RADIAN$2;\n    var virtualRoot = seriesModel.getData().tree.root;\n    var treeRoot = seriesModel.getViewRoot();\n    var rootDepth = treeRoot.depth;\n    var sort = seriesModel.get('sort');\n\n    if (sort != null) {\n      initChildren$1(treeRoot, sort);\n    }\n\n    var validDataCount = 0;\n    each(treeRoot.children, function (child) {\n      !isNaN(child.getValue()) && validDataCount++;\n    });\n    var sum = treeRoot.getValue(); // Sum may be 0\n\n    var unitRadian = Math.PI / (sum || validDataCount) * 2;\n    var renderRollupNode = treeRoot.depth > 0;\n    var levels = treeRoot.height - (renderRollupNode ? -1 : 1);\n    var rPerLevel = (r - r0) / (levels || 1);\n    var clockwise = seriesModel.get('clockwise');\n    var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); // In the case some sector angle is smaller than minAngle\n    // let restAngle = PI2;\n    // let valueSumLargerThanMinAngle = 0;\n\n    var dir = clockwise ? 1 : -1;\n    /**\n     * Render a tree\n     * @return increased angle\n     */\n\n    var renderNode = function (node, startAngle) {\n      if (!node) {\n        return;\n      }\n\n      var endAngle = startAngle; // Render self\n\n      if (node !== virtualRoot) {\n        // Tree node is virtual, so it doesn't need to be drawn\n        var value = node.getValue();\n        var angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian;\n\n        if (angle < minAngle) {\n          angle = minAngle; // restAngle -= minAngle;\n        } // else {\n        //     valueSumLargerThanMinAngle += value;\n        // }\n\n\n        endAngle = startAngle + dir * angle;\n        var depth = node.depth - rootDepth - (renderRollupNode ? -1 : 1);\n        var rStart = r0 + rPerLevel * depth;\n        var rEnd = r0 + rPerLevel * (depth + 1);\n        var levelModel = seriesModel.getLevelModel(node);\n\n        if (levelModel) {\n          var r0_1 = levelModel.get('r0', true);\n          var r_1 = levelModel.get('r', true);\n          var radius_1 = levelModel.get('radius', true);\n\n          if (radius_1 != null) {\n            r0_1 = radius_1[0];\n            r_1 = radius_1[1];\n          }\n\n          r0_1 != null && (rStart = parsePercent$1(r0_1, size / 2));\n          r_1 != null && (rEnd = parsePercent$1(r_1, size / 2));\n        }\n\n        node.setLayout({\n          angle: angle,\n          startAngle: startAngle,\n          endAngle: endAngle,\n          clockwise: clockwise,\n          cx: cx,\n          cy: cy,\n          r0: rStart,\n          r: rEnd\n        });\n      } // Render children\n\n\n      if (node.children && node.children.length) {\n        // currentAngle = startAngle;\n        var siblingAngle_1 = 0;\n        each(node.children, function (node) {\n          siblingAngle_1 += renderNode(node, startAngle + siblingAngle_1);\n        });\n      }\n\n      return endAngle - startAngle;\n    }; // Virtual root node for roll up\n\n\n    if (renderRollupNode) {\n      var rStart = r0;\n      var rEnd = r0 + rPerLevel;\n      var angle = Math.PI * 2;\n      virtualRoot.setLayout({\n        angle: angle,\n        startAngle: startAngle,\n        endAngle: startAngle + angle,\n        clockwise: clockwise,\n        cx: cx,\n        cy: cy,\n        r0: rStart,\n        r: rEnd\n      });\n    }\n\n    renderNode(treeRoot, startAngle);\n  });\n}\n/**\n * Init node children by order and update visual\n */\n\nfunction initChildren$1(node, sortOrder) {\n  var children = node.children || [];\n  node.children = sort$2(children, sortOrder); // Init children recursively\n\n  if (children.length) {\n    each(node.children, function (child) {\n      initChildren$1(child, sortOrder);\n    });\n  }\n}\n/**\n * Sort children nodes\n *\n * @param {TreeNode[]}               children children of node to be sorted\n * @param {string | function | null} sort sort method\n *                                   See SunburstSeries.js for details.\n */\n\n\nfunction sort$2(children, sortOrder) {\n  if (isFunction(sortOrder)) {\n    var sortTargets = map(children, function (child, idx) {\n      var value = child.getValue();\n      return {\n        params: {\n          depth: child.depth,\n          height: child.height,\n          dataIndex: child.dataIndex,\n          getValue: function () {\n            return value;\n          }\n        },\n        index: idx\n      };\n    });\n    sortTargets.sort(function (a, b) {\n      return sortOrder(a.params, b.params);\n    });\n    return map(sortTargets, function (target) {\n      return children[target.index];\n    });\n  } else {\n    var isAsc_1 = sortOrder === 'asc';\n    return children.sort(function (a, b) {\n      var diff = (a.getValue() - b.getValue()) * (isAsc_1 ? 1 : -1);\n      return diff === 0 ? (a.dataIndex - b.dataIndex) * (isAsc_1 ? -1 : 1) : diff;\n    });\n  }\n}\n\nfunction sunburstVisual(ecModel) {\n  var paletteScope = {}; // Default color strategy\n\n  function pickColor(node, seriesModel, treeHeight) {\n    // Choose color from palette based on the first level.\n    var current = node;\n\n    while (current && current.depth > 1) {\n      current = current.parentNode;\n    }\n\n    var color = seriesModel.getColorFromPalette(current.name || current.dataIndex + '', paletteScope);\n\n    if (node.depth > 1 && isString(color)) {\n      // Lighter on the deeper level.\n      color = lift(color, (node.depth - 1) / (treeHeight - 1) * 0.5);\n    }\n\n    return color;\n  }\n\n  ecModel.eachSeriesByType('sunburst', function (seriesModel) {\n    var data = seriesModel.getData();\n    var tree = data.tree;\n    tree.eachNode(function (node) {\n      var model = node.getModel();\n      var style = model.getModel('itemStyle').getItemStyle();\n\n      if (!style.fill) {\n        style.fill = pickColor(node, seriesModel, tree.root.height);\n      }\n\n      var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style');\n      extend(existsStyle, style);\n    });\n  });\n}\n\nfunction install$q(registers) {\n  registers.registerChartView(SunburstView);\n  registers.registerSeriesModel(SunburstSeriesModel);\n  registers.registerLayout(curry(sunburstLayout, 'sunburst'));\n  registers.registerProcessor(curry(dataFilter, 'sunburst'));\n  registers.registerVisual(sunburstVisual);\n  installSunburstAction(registers);\n}\n\n// `visual('color') visual('borderColor')` is supported.\n\nvar STYLE_VISUAL_TYPE = {\n  color: 'fill',\n  borderColor: 'stroke'\n};\nvar NON_STYLE_VISUAL_PROPS = {\n  symbol: 1,\n  symbolSize: 1,\n  symbolKeepAspect: 1,\n  legendIcon: 1,\n  visualMeta: 1,\n  liftZ: 1,\n  decal: 1\n};\nvar customInnerStore = makeInner();\n\nvar CustomSeriesModel =\n/** @class */\nfunction (_super) {\n  __extends(CustomSeriesModel, _super);\n\n  function CustomSeriesModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CustomSeriesModel.type;\n    return _this;\n  }\n\n  CustomSeriesModel.prototype.optionUpdated = function () {\n    this.currentZLevel = this.get('zlevel', true);\n    this.currentZ = this.get('z', true);\n  };\n\n  CustomSeriesModel.prototype.getInitialData = function (option, ecModel) {\n    return createSeriesData(null, this);\n  };\n\n  CustomSeriesModel.prototype.getDataParams = function (dataIndex, dataType, el) {\n    var params = _super.prototype.getDataParams.call(this, dataIndex, dataType);\n\n    el && (params.info = customInnerStore(el).info);\n    return params;\n  };\n\n  CustomSeriesModel.type = 'series.custom';\n  CustomSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];\n  CustomSeriesModel.defaultOption = {\n    coordinateSystem: 'cartesian2d',\n    // zlevel: 0,\n    z: 2,\n    legendHoverLink: true,\n    // Custom series will not clip by default.\n    // Some case will use custom series to draw label\n    // For example https://echarts.apache.org/examples/en/editor.html?c=custom-gantt-flight\n    clip: false // Cartesian coordinate system\n    // xAxisIndex: 0,\n    // yAxisIndex: 0,\n    // Polar coordinate system\n    // polarIndex: 0,\n    // Geo coordinate system\n    // geoIndex: 0,\n\n  };\n  return CustomSeriesModel;\n}(SeriesModel);\n\nfunction dataToCoordSize(dataSize, dataItem) {\n  // dataItem is necessary in log axis.\n  dataItem = dataItem || [0, 0];\n  return map(['x', 'y'], function (dim, dimIdx) {\n    var axis = this.getAxis(dim);\n    var val = dataItem[dimIdx];\n    var halfSize = dataSize[dimIdx] / 2;\n    return axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));\n  }, this);\n}\n\nfunction cartesianPrepareCustom(coordSys) {\n  var rect = coordSys.master.getRect();\n  return {\n    coordSys: {\n      // The name exposed to user is always 'cartesian2d' but not 'grid'.\n      type: 'cartesian2d',\n      x: rect.x,\n      y: rect.y,\n      width: rect.width,\n      height: rect.height\n    },\n    api: {\n      coord: function (data) {\n        // do not provide \"out\" param\n        return coordSys.dataToPoint(data);\n      },\n      size: bind(dataToCoordSize, coordSys)\n    }\n  };\n}\n\nfunction dataToCoordSize$1(dataSize, dataItem) {\n  dataItem = dataItem || [0, 0];\n  return map([0, 1], function (dimIdx) {\n    var val = dataItem[dimIdx];\n    var halfSize = dataSize[dimIdx] / 2;\n    var p1 = [];\n    var p2 = [];\n    p1[dimIdx] = val - halfSize;\n    p2[dimIdx] = val + halfSize;\n    p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx];\n    return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]);\n  }, this);\n}\n\nfunction geoPrepareCustom(coordSys) {\n  var rect = coordSys.getBoundingRect();\n  return {\n    coordSys: {\n      type: 'geo',\n      x: rect.x,\n      y: rect.y,\n      width: rect.width,\n      height: rect.height,\n      zoom: coordSys.getZoom()\n    },\n    api: {\n      coord: function (data) {\n        // do not provide \"out\" and noRoam param,\n        // Compatible with this usage:\n        // echarts.util.map(item.points, api.coord)\n        return coordSys.dataToPoint(data);\n      },\n      size: bind(dataToCoordSize$1, coordSys)\n    }\n  };\n}\n\nfunction dataToCoordSize$2(dataSize, dataItem) {\n  // dataItem is necessary in log axis.\n  var axis = this.getAxis();\n  var val = dataItem instanceof Array ? dataItem[0] : dataItem;\n  var halfSize = (dataSize instanceof Array ? dataSize[0] : dataSize) / 2;\n  return axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));\n}\n\nfunction singlePrepareCustom(coordSys) {\n  var rect = coordSys.getRect();\n  return {\n    coordSys: {\n      type: 'singleAxis',\n      x: rect.x,\n      y: rect.y,\n      width: rect.width,\n      height: rect.height\n    },\n    api: {\n      coord: function (val) {\n        // do not provide \"out\" param\n        return coordSys.dataToPoint(val);\n      },\n      size: bind(dataToCoordSize$2, coordSys)\n    }\n  };\n}\n\nfunction dataToCoordSize$3(dataSize, dataItem) {\n  // dataItem is necessary in log axis.\n  dataItem = dataItem || [0, 0];\n  return map(['Radius', 'Angle'], function (dim, dimIdx) {\n    var getterName = 'get' + dim + 'Axis'; // TODO: TYPE Check Angle Axis\n\n    var axis = this[getterName]();\n    var val = dataItem[dimIdx];\n    var halfSize = dataSize[dimIdx] / 2;\n    var result = axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));\n\n    if (dim === 'Angle') {\n      result = result * Math.PI / 180;\n    }\n\n    return result;\n  }, this);\n}\n\nfunction polarPrepareCustom(coordSys) {\n  var radiusAxis = coordSys.getRadiusAxis();\n  var angleAxis = coordSys.getAngleAxis();\n  var radius = radiusAxis.getExtent();\n  radius[0] > radius[1] && radius.reverse();\n  return {\n    coordSys: {\n      type: 'polar',\n      cx: coordSys.cx,\n      cy: coordSys.cy,\n      r: radius[1],\n      r0: radius[0]\n    },\n    api: {\n      coord: function (data) {\n        var radius = radiusAxis.dataToRadius(data[0]);\n        var angle = angleAxis.dataToAngle(data[1]);\n        var coord = coordSys.coordToPoint([radius, angle]);\n        coord.push(radius, angle * Math.PI / 180);\n        return coord;\n      },\n      size: bind(dataToCoordSize$3, coordSys)\n    }\n  };\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction calendarPrepareCustom(coordSys) {\n  var rect = coordSys.getRect();\n  var rangeInfo = coordSys.getRangeInfo();\n  return {\n    coordSys: {\n      type: 'calendar',\n      x: rect.x,\n      y: rect.y,\n      width: rect.width,\n      height: rect.height,\n      cellWidth: coordSys.getCellWidth(),\n      cellHeight: coordSys.getCellHeight(),\n      rangeInfo: {\n        start: rangeInfo.start,\n        end: rangeInfo.end,\n        weeks: rangeInfo.weeks,\n        dayCount: rangeInfo.allDay\n      }\n    },\n    api: {\n      coord: function (data, clamp) {\n        return coordSys.dataToPoint(data, clamp);\n      }\n    }\n  };\n}\n\nvar deprecatedLogs = {};\n/**\n * Whether need to call `convertEC4CompatibleStyle`.\n */\n\nfunction isEC4CompatibleStyle(style, elType, hasOwnTextContentOption, hasOwnTextConfig) {\n  // Since echarts5, `RectText` is separated from its host element and style.text\n  // does not exist any more. The compat work brings some extra burden on performance.\n  // So we provide:\n  // `legacy: true` force make compat.\n  // `legacy: false`, force do not compat.\n  // `legacy` not set: auto detect whether legacy.\n  //     But in this case we do not compat (difficult to detect and rare case):\n  //     Becuse custom series and graphic component support \"merge\", users may firstly\n  //     only set `textStrokeWidth` style or secondly only set `text`.\n  return style && (style.legacy || style.legacy !== false && !hasOwnTextContentOption && !hasOwnTextConfig && elType !== 'tspan' // Difficult to detect whether legacy for a \"text\" el.\n  && (elType === 'text' || hasOwn(style, 'text')));\n}\n/**\n * `EC4CompatibleStyle` is style that might be in echarts4 format or echarts5 format.\n * @param hostStyle The properties might be modified.\n * @return If be text el, `textContentStyle` and `textConfig` will not be returned.\n *         Otherwise a `textContentStyle` and `textConfig` will be created, whose props area\n *         retried from the `hostStyle`.\n */\n\nfunction convertFromEC4CompatibleStyle(hostStyle, elType, isNormal) {\n  var srcStyle = hostStyle;\n  var textConfig;\n  var textContent;\n  var textContentStyle;\n\n  if (elType === 'text') {\n    textContentStyle = srcStyle;\n  } else {\n    textContentStyle = {};\n    hasOwn(srcStyle, 'text') && (textContentStyle.text = srcStyle.text);\n    hasOwn(srcStyle, 'rich') && (textContentStyle.rich = srcStyle.rich);\n    hasOwn(srcStyle, 'textFill') && (textContentStyle.fill = srcStyle.textFill);\n    hasOwn(srcStyle, 'textStroke') && (textContentStyle.stroke = srcStyle.textStroke);\n    hasOwn(srcStyle, 'fontFamily') && (textContentStyle.fontFamily = srcStyle.fontFamily);\n    hasOwn(srcStyle, 'fontSize') && (textContentStyle.fontSize = srcStyle.fontSize);\n    hasOwn(srcStyle, 'fontStyle') && (textContentStyle.fontStyle = srcStyle.fontStyle);\n    hasOwn(srcStyle, 'fontWeight') && (textContentStyle.fontWeight = srcStyle.fontWeight);\n    textContent = {\n      type: 'text',\n      style: textContentStyle,\n      // ec4 does not support rectText trigger.\n      // And when text position is different in normal and emphasis\n      // => hover text trigger emphasis;\n      // => text position changed, leave mouse pointer immediately;\n      // That might cause incorrect state.\n      silent: true\n    };\n    textConfig = {};\n    var hasOwnPos = hasOwn(srcStyle, 'textPosition');\n\n    if (isNormal) {\n      textConfig.position = hasOwnPos ? srcStyle.textPosition : 'inside';\n    } else {\n      hasOwnPos && (textConfig.position = srcStyle.textPosition);\n    }\n\n    hasOwn(srcStyle, 'textPosition') && (textConfig.position = srcStyle.textPosition);\n    hasOwn(srcStyle, 'textOffset') && (textConfig.offset = srcStyle.textOffset);\n    hasOwn(srcStyle, 'textRotation') && (textConfig.rotation = srcStyle.textRotation);\n    hasOwn(srcStyle, 'textDistance') && (textConfig.distance = srcStyle.textDistance);\n  }\n\n  convertEC4CompatibleRichItem(textContentStyle, hostStyle);\n  each(textContentStyle.rich, function (richItem) {\n    convertEC4CompatibleRichItem(richItem, richItem);\n  });\n  return {\n    textConfig: textConfig,\n    textContent: textContent\n  };\n}\n/**\n * The result will be set to `out`.\n */\n\nfunction convertEC4CompatibleRichItem(out, richItem) {\n  if (!richItem) {\n    return;\n  } // (1) For simplicity, make textXXX properties (deprecated since ec5) has\n  // higher priority. For example, consider in ec4 `borderColor: 5, textBorderColor: 10`\n  // on a rect means `borderColor: 4` on the rect and `borderColor: 10` on an attached\n  // richText in ec5.\n  // (2) `out === richItem` if and only if `out` is text el or rich item.\n  // So we can overwrite existing props in `out` since textXXX has higher priority.\n\n\n  richItem.font = richItem.textFont || richItem.font;\n  hasOwn(richItem, 'textStrokeWidth') && (out.lineWidth = richItem.textStrokeWidth);\n  hasOwn(richItem, 'textAlign') && (out.align = richItem.textAlign);\n  hasOwn(richItem, 'textVerticalAlign') && (out.verticalAlign = richItem.textVerticalAlign);\n  hasOwn(richItem, 'textLineHeight') && (out.lineHeight = richItem.textLineHeight);\n  hasOwn(richItem, 'textWidth') && (out.width = richItem.textWidth);\n  hasOwn(richItem, 'textHeight') && (out.height = richItem.textHeight);\n  hasOwn(richItem, 'textBackgroundColor') && (out.backgroundColor = richItem.textBackgroundColor);\n  hasOwn(richItem, 'textPadding') && (out.padding = richItem.textPadding);\n  hasOwn(richItem, 'textBorderColor') && (out.borderColor = richItem.textBorderColor);\n  hasOwn(richItem, 'textBorderWidth') && (out.borderWidth = richItem.textBorderWidth);\n  hasOwn(richItem, 'textBorderRadius') && (out.borderRadius = richItem.textBorderRadius);\n  hasOwn(richItem, 'textBoxShadowColor') && (out.shadowColor = richItem.textBoxShadowColor);\n  hasOwn(richItem, 'textBoxShadowBlur') && (out.shadowBlur = richItem.textBoxShadowBlur);\n  hasOwn(richItem, 'textBoxShadowOffsetX') && (out.shadowOffsetX = richItem.textBoxShadowOffsetX);\n  hasOwn(richItem, 'textBoxShadowOffsetY') && (out.shadowOffsetY = richItem.textBoxShadowOffsetY);\n}\n/**\n * Convert to pure echarts4 format style.\n * `itemStyle` will be modified, added with ec4 style properties from\n * `textStyle` and `textConfig`.\n *\n * [Caveat]: For simplicity, `insideRollback` in ec4 does not compat, where\n * `styleEmphasis: {textFill: 'red'}` will remove the normal auto added stroke.\n */\n\n\nfunction convertToEC4StyleForCustomSerise(itemStl, txStl, txCfg) {\n  var out = itemStl; // See `custom.ts`, a trick to set extra `textPosition` firstly.\n\n  out.textPosition = out.textPosition || txCfg.position || 'inside';\n  txCfg.offset != null && (out.textOffset = txCfg.offset);\n  txCfg.rotation != null && (out.textRotation = txCfg.rotation);\n  txCfg.distance != null && (out.textDistance = txCfg.distance);\n  var isInside = out.textPosition.indexOf('inside') >= 0;\n  var hostFill = itemStl.fill || '#000';\n  convertToEC4RichItem(out, txStl);\n  var textFillNotSet = out.textFill == null;\n\n  if (isInside) {\n    if (textFillNotSet) {\n      out.textFill = txCfg.insideFill || '#fff';\n      !out.textStroke && txCfg.insideStroke && (out.textStroke = txCfg.insideStroke);\n      !out.textStroke && (out.textStroke = hostFill);\n      out.textStrokeWidth == null && (out.textStrokeWidth = 2);\n    }\n  } else {\n    if (textFillNotSet) {\n      out.textFill = itemStl.fill || txCfg.outsideFill || '#000';\n    }\n\n    !out.textStroke && txCfg.outsideStroke && (out.textStroke = txCfg.outsideStroke);\n  }\n\n  out.text = txStl.text;\n  out.rich = txStl.rich;\n  each(txStl.rich, function (richItem) {\n    convertToEC4RichItem(richItem, richItem);\n  });\n  return out;\n}\n\nfunction convertToEC4RichItem(out, richItem) {\n  if (!richItem) {\n    return;\n  }\n\n  hasOwn(richItem, 'fill') && (out.textFill = richItem.fill);\n  hasOwn(richItem, 'stroke') && (out.textStroke = richItem.fill);\n  hasOwn(richItem, 'lineWidth') && (out.textStrokeWidth = richItem.lineWidth);\n  hasOwn(richItem, 'font') && (out.font = richItem.font);\n  hasOwn(richItem, 'fontStyle') && (out.fontStyle = richItem.fontStyle);\n  hasOwn(richItem, 'fontWeight') && (out.fontWeight = richItem.fontWeight);\n  hasOwn(richItem, 'fontSize') && (out.fontSize = richItem.fontSize);\n  hasOwn(richItem, 'fontFamily') && (out.fontFamily = richItem.fontFamily);\n  hasOwn(richItem, 'align') && (out.textAlign = richItem.align);\n  hasOwn(richItem, 'verticalAlign') && (out.textVerticalAlign = richItem.verticalAlign);\n  hasOwn(richItem, 'lineHeight') && (out.textLineHeight = richItem.lineHeight);\n  hasOwn(richItem, 'width') && (out.textWidth = richItem.width);\n  hasOwn(richItem, 'height') && (out.textHeight = richItem.height);\n  hasOwn(richItem, 'backgroundColor') && (out.textBackgroundColor = richItem.backgroundColor);\n  hasOwn(richItem, 'padding') && (out.textPadding = richItem.padding);\n  hasOwn(richItem, 'borderColor') && (out.textBorderColor = richItem.borderColor);\n  hasOwn(richItem, 'borderWidth') && (out.textBorderWidth = richItem.borderWidth);\n  hasOwn(richItem, 'borderRadius') && (out.textBorderRadius = richItem.borderRadius);\n  hasOwn(richItem, 'shadowColor') && (out.textBoxShadowColor = richItem.shadowColor);\n  hasOwn(richItem, 'shadowBlur') && (out.textBoxShadowBlur = richItem.shadowBlur);\n  hasOwn(richItem, 'shadowOffsetX') && (out.textBoxShadowOffsetX = richItem.shadowOffsetX);\n  hasOwn(richItem, 'shadowOffsetY') && (out.textBoxShadowOffsetY = richItem.shadowOffsetY);\n  hasOwn(richItem, 'textShadowColor') && (out.textShadowColor = richItem.textShadowColor);\n  hasOwn(richItem, 'textShadowBlur') && (out.textShadowBlur = richItem.textShadowBlur);\n  hasOwn(richItem, 'textShadowOffsetX') && (out.textShadowOffsetX = richItem.textShadowOffsetX);\n  hasOwn(richItem, 'textShadowOffsetY') && (out.textShadowOffsetY = richItem.textShadowOffsetY);\n}\n\nfunction warnDeprecated(deprecated, insteadApproach) {\n  if (\"development\" !== 'production') {\n    var key = deprecated + '^_^' + insteadApproach;\n\n    if (!deprecatedLogs[key]) {\n      console.warn(\"[ECharts] DEPRECATED: \\\"\" + deprecated + \"\\\" has been deprecated. \" + insteadApproach);\n      deprecatedLogs[key] = true;\n    }\n  }\n}\n\nvar LEGACY_TRANSFORM_PROPS_MAP = {\n  position: ['x', 'y'],\n  scale: ['scaleX', 'scaleY'],\n  origin: ['originX', 'originY']\n};\nvar LEGACY_TRANSFORM_PROPS = keys(LEGACY_TRANSFORM_PROPS_MAP);\nvar TRANSFORM_PROPS_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {\n  obj[key] = 1;\n  return obj;\n}, {});\nvar transformPropNamesStr = TRANSFORMABLE_PROPS.join(', '); // '' means root\n\nvar ELEMENT_ANIMATABLE_PROPS = ['', 'style', 'shape', 'extra'];\nvar transitionInnerStore = makeInner();\n\nfunction getElementAnimationConfig(animationType, el, elOption, parentModel, dataIndex) {\n  var animationProp = animationType + \"Animation\";\n  var config = getAnimationConfig(animationType, parentModel, dataIndex) || {};\n  var userDuring = transitionInnerStore(el).userDuring; // Only set when duration is > 0 and it's need to be animated.\n\n  if (config.duration > 0) {\n    // For simplicity, if during not specified, the previous during will not work any more.\n    config.during = userDuring ? bind(duringCall, {\n      el: el,\n      userDuring: userDuring\n    }) : null;\n    config.setToFinal = true;\n    config.scope = animationType;\n  }\n\n  extend(config, elOption[animationProp]);\n  return config;\n}\n\nfunction applyUpdateTransition(el, elOption, animatableModel, opts) {\n  opts = opts || {};\n  var dataIndex = opts.dataIndex,\n      isInit = opts.isInit,\n      clearStyle = opts.clearStyle;\n  var hasAnimation = animatableModel.isAnimationEnabled(); // Save the meta info for further morphing. Like apply on the sub morphing elements.\n\n  var store = transitionInnerStore(el);\n  var styleOpt = elOption.style;\n  store.userDuring = elOption.during;\n  var transFromProps = {};\n  var propsToSet = {};\n  prepareTransformAllPropsFinal(el, elOption, propsToSet);\n  prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet);\n  prepareShapeOrExtraAllPropsFinal('extra', elOption, propsToSet);\n\n  if (!isInit && hasAnimation) {\n    prepareTransformTransitionFrom(el, elOption, transFromProps);\n    prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps);\n    prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps);\n    prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps);\n  }\n\n  propsToSet.style = styleOpt;\n  applyPropsDirectly(el, propsToSet, clearStyle);\n  applyMiscProps(el, elOption);\n\n  if (hasAnimation) {\n    if (isInit) {\n      var enterFromProps_1 = {};\n      each(ELEMENT_ANIMATABLE_PROPS, function (propName) {\n        var prop = propName ? elOption[propName] : elOption;\n\n        if (prop && prop.enterFrom) {\n          if (propName) {\n            enterFromProps_1[propName] = enterFromProps_1[propName] || {};\n          }\n\n          extend(propName ? enterFromProps_1[propName] : enterFromProps_1, prop.enterFrom);\n        }\n      });\n      var config = getElementAnimationConfig('enter', el, elOption, animatableModel, dataIndex);\n\n      if (config.duration > 0) {\n        el.animateFrom(enterFromProps_1, config);\n      }\n    } else {\n      applyPropsTransition(el, elOption, dataIndex || 0, animatableModel, transFromProps);\n    }\n  } // Store leave to be used in leave transition.\n\n\n  updateLeaveTo(el, elOption);\n  styleOpt ? el.dirty() : el.markRedraw();\n}\nfunction updateLeaveTo(el, elOption) {\n  // Try merge to previous set leaveTo\n  var leaveToProps = transitionInnerStore(el).leaveToProps;\n\n  for (var i = 0; i < ELEMENT_ANIMATABLE_PROPS.length; i++) {\n    var propName = ELEMENT_ANIMATABLE_PROPS[i];\n    var prop = propName ? elOption[propName] : elOption;\n\n    if (prop && prop.leaveTo) {\n      if (!leaveToProps) {\n        leaveToProps = transitionInnerStore(el).leaveToProps = {};\n      }\n\n      if (propName) {\n        leaveToProps[propName] = leaveToProps[propName] || {};\n      }\n\n      extend(propName ? leaveToProps[propName] : leaveToProps, prop.leaveTo);\n    }\n  }\n}\nfunction applyLeaveTransition(el, elOption, animatableModel, onRemove) {\n  if (el) {\n    var parent_1 = el.parent;\n    var leaveToProps = transitionInnerStore(el).leaveToProps;\n\n    if (leaveToProps) {\n      // TODO TODO use leave after leaveAnimation in series is introduced\n      // TODO Data index?\n      var config = getElementAnimationConfig('update', el, elOption, animatableModel, 0);\n\n      config.done = function () {\n        parent_1.remove(el);\n        onRemove && onRemove();\n      };\n\n      el.animateTo(leaveToProps, config);\n    } else {\n      parent_1.remove(el);\n      onRemove && onRemove();\n    }\n  }\n}\nfunction isTransitionAll(transition) {\n  return transition === 'all';\n}\n\nfunction applyPropsDirectly(el, // Can be null/undefined\nallPropsFinal, clearStyle) {\n  var styleOpt = allPropsFinal.style;\n\n  if (!el.isGroup && styleOpt) {\n    if (clearStyle) {\n      el.useStyle({}); // When style object changed, how to trade the existing animation?\n      // It is probably complicated and not needed to cover all the cases.\n      // But still need consider the case:\n      // (1) When using init animation on `style.opacity`, and before the animation\n      //     ended users triggers an update by mousewhel. At that time the init\n      //     animation should better be continued rather than terminated.\n      //     So after `useStyle` called, we should change the animation target manually\n      //     to continue the effect of the init animation.\n      // (2) PENDING: If the previous animation targeted at a `val1`, and currently we need\n      //     to update the value to `val2` and no animation declared, should be terminate\n      //     the previous animation or just modify the target of the animation?\n      //     Therotically That will happen not only on `style` but also on `shape` and\n      //     `transfrom` props. But we haven't handle this case at present yet.\n      // (3) PENDING: Is it proper to visit `animators` and `targetName`?\n\n      var animators = el.animators;\n\n      for (var i = 0; i < animators.length; i++) {\n        var animator = animators[i]; // targetName is the \"topKey\".\n\n        if (animator.targetName === 'style') {\n          animator.changeTarget(el.style);\n        }\n      }\n    }\n\n    el.setStyle(styleOpt);\n  }\n\n  if (allPropsFinal) {\n    // Not set style here.\n    allPropsFinal.style = null; // Set el to the final state firstly.\n\n    allPropsFinal && el.attr(allPropsFinal);\n    allPropsFinal.style = styleOpt;\n  }\n}\n\nfunction applyPropsTransition(el, elOption, dataIndex, model, // Can be null/undefined\ntransFromProps) {\n  if (transFromProps) {\n    var config = getElementAnimationConfig('update', el, elOption, model, dataIndex);\n\n    if (config.duration > 0) {\n      el.animateFrom(transFromProps, config);\n    }\n  }\n}\n\nfunction applyMiscProps(el, elOption) {\n  // Merge by default.\n  hasOwn(elOption, 'silent') && (el.silent = elOption.silent);\n  hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore);\n\n  if (el instanceof Displayable) {\n    hasOwn(elOption, 'invisible') && (el.invisible = elOption.invisible);\n  }\n\n  if (el instanceof Path) {\n    hasOwn(elOption, 'autoBatch') && (el.autoBatch = elOption.autoBatch);\n  }\n} // Use it to avoid it be exposed to user.\n\n\nvar tmpDuringScope = {};\nvar transitionDuringAPI = {\n  // Usually other props do not need to be changed in animation during.\n  setTransform: function (key, val) {\n    if (\"development\" !== 'production') {\n      assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `setTransform`.');\n    }\n\n    tmpDuringScope.el[key] = val;\n    return this;\n  },\n  getTransform: function (key) {\n    if (\"development\" !== 'production') {\n      assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `getTransform`.');\n    }\n\n    return tmpDuringScope.el[key];\n  },\n  setShape: function (key, val) {\n    if (\"development\" !== 'production') {\n      assertNotReserved(key);\n    }\n\n    var el = tmpDuringScope.el;\n    var shape = el.shape || (el.shape = {});\n    shape[key] = val;\n    el.dirtyShape && el.dirtyShape();\n    return this;\n  },\n  getShape: function (key) {\n    if (\"development\" !== 'production') {\n      assertNotReserved(key);\n    }\n\n    var shape = tmpDuringScope.el.shape;\n\n    if (shape) {\n      return shape[key];\n    }\n  },\n  setStyle: function (key, val) {\n    if (\"development\" !== 'production') {\n      assertNotReserved(key);\n    }\n\n    var el = tmpDuringScope.el;\n    var style = el.style;\n\n    if (style) {\n      if (\"development\" !== 'production') {\n        if (eqNaN(val)) {\n          warn('style.' + key + ' must not be assigned with NaN.');\n        }\n      }\n\n      style[key] = val;\n      el.dirtyStyle && el.dirtyStyle();\n    }\n\n    return this;\n  },\n  getStyle: function (key) {\n    if (\"development\" !== 'production') {\n      assertNotReserved(key);\n    }\n\n    var style = tmpDuringScope.el.style;\n\n    if (style) {\n      return style[key];\n    }\n  },\n  setExtra: function (key, val) {\n    if (\"development\" !== 'production') {\n      assertNotReserved(key);\n    }\n\n    var extra = tmpDuringScope.el.extra || (tmpDuringScope.el.extra = {});\n    extra[key] = val;\n    return this;\n  },\n  getExtra: function (key) {\n    if (\"development\" !== 'production') {\n      assertNotReserved(key);\n    }\n\n    var extra = tmpDuringScope.el.extra;\n\n    if (extra) {\n      return extra[key];\n    }\n  }\n};\n\nfunction assertNotReserved(key) {\n  if (\"development\" !== 'production') {\n    if (key === 'transition' || key === 'enterFrom' || key === 'leaveTo') {\n      throw new Error('key must not be \"' + key + '\"');\n    }\n  }\n}\n\nfunction duringCall() {\n  // Do not provide \"percent\" until some requirements come.\n  // Because consider thies case:\n  // enterFrom: {x: 100, y: 30}, transition: 'x'.\n  // And enter duration is different from update duration.\n  // Thus it might be confused about the meaning of \"percent\" in during callback.\n  var scope = this;\n  var el = scope.el;\n\n  if (!el) {\n    return;\n  } // If el is remove from zr by reason like legend, during still need to called,\n  // because el will be added back to zr and the prop value should not be incorrect.\n\n\n  var latestUserDuring = transitionInnerStore(el).userDuring;\n  var scopeUserDuring = scope.userDuring; // Ensured a during is only called once in each animation frame.\n  // If a during is called multiple times in one frame, maybe some users' calculation logic\n  // might be wrong (not sure whether this usage exists).\n  // The case of a during might be called twice can be: by default there is a animator for\n  // 'x', 'y' when init. Before the init animation finished, call `setOption` to start\n  // another animators for 'style'/'shape'/'extra'.\n\n  if (latestUserDuring !== scopeUserDuring) {\n    // release\n    scope.el = scope.userDuring = null;\n    return;\n  }\n\n  tmpDuringScope.el = el; // Give no `this` to user in \"during\" calling.\n\n  scopeUserDuring(transitionDuringAPI); // FIXME: if in future meet the case that some prop will be both modified in `during` and `state`,\n  // consider the issue that the prop might be incorrect when return to \"normal\" state.\n}\n\nfunction prepareShapeOrExtraTransitionFrom(mainAttr, fromEl, elOption, transFromProps) {\n  var attrOpt = elOption[mainAttr];\n\n  if (!attrOpt) {\n    return;\n  }\n\n  var elPropsInAttr = fromEl[mainAttr];\n  var transFromPropsInAttr;\n\n  if (elPropsInAttr) {\n    var transition = elOption.transition;\n    var attrTransition = attrOpt.transition;\n\n    if (attrTransition) {\n      !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});\n\n      if (isTransitionAll(attrTransition)) {\n        extend(transFromPropsInAttr, elPropsInAttr);\n      } else {\n        var transitionKeys = normalizeToArray(attrTransition);\n\n        for (var i = 0; i < transitionKeys.length; i++) {\n          var key = transitionKeys[i];\n          var elVal = elPropsInAttr[key];\n          transFromPropsInAttr[key] = elVal;\n        }\n      }\n    } else if (isTransitionAll(transition) || indexOf(transition, mainAttr) >= 0) {\n      !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});\n      var elPropsInAttrKeys = keys(elPropsInAttr);\n\n      for (var i = 0; i < elPropsInAttrKeys.length; i++) {\n        var key = elPropsInAttrKeys[i];\n        var elVal = elPropsInAttr[key];\n\n        if (isNonStyleTransitionEnabled(attrOpt[key], elVal)) {\n          transFromPropsInAttr[key] = elVal;\n        }\n      }\n    }\n  }\n}\n\nfunction prepareShapeOrExtraAllPropsFinal(mainAttr, elOption, allProps) {\n  var attrOpt = elOption[mainAttr];\n\n  if (!attrOpt) {\n    return;\n  }\n\n  var allPropsInAttr = allProps[mainAttr] = {};\n  var keysInAttr = keys(attrOpt);\n\n  for (var i = 0; i < keysInAttr.length; i++) {\n    var key = keysInAttr[i]; // To avoid share one object with different element, and\n    // to avoid user modify the object inexpectedly, have to clone.\n\n    allPropsInAttr[key] = cloneValue(attrOpt[key]);\n  }\n}\n\nfunction prepareTransformTransitionFrom(el, elOption, transFromProps) {\n  var transition = elOption.transition;\n  var transitionKeys = isTransitionAll(transition) ? TRANSFORMABLE_PROPS : normalizeToArray(transition || []);\n\n  for (var i = 0; i < transitionKeys.length; i++) {\n    var key = transitionKeys[i];\n\n    if (key === 'style' || key === 'shape' || key === 'extra') {\n      continue;\n    }\n\n    var elVal = el[key];\n\n    if (\"development\" !== 'production') {\n      checkTransformPropRefer(key, 'el.transition');\n    } // Do not clone, animator will perform that clone.\n\n\n    transFromProps[key] = elVal;\n  }\n}\n\nfunction prepareTransformAllPropsFinal(el, elOption, allProps) {\n  for (var i = 0; i < LEGACY_TRANSFORM_PROPS.length; i++) {\n    var legacyName = LEGACY_TRANSFORM_PROPS[i];\n    var xyName = LEGACY_TRANSFORM_PROPS_MAP[legacyName];\n    var legacyArr = elOption[legacyName];\n\n    if (legacyArr) {\n      allProps[xyName[0]] = legacyArr[0];\n      allProps[xyName[1]] = legacyArr[1];\n    }\n  }\n\n  for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {\n    var key = TRANSFORMABLE_PROPS[i];\n\n    if (elOption[key] != null) {\n      allProps[key] = elOption[key];\n    }\n  }\n}\n\nfunction prepareStyleTransitionFrom(fromEl, elOption, styleOpt, transFromProps) {\n  if (!styleOpt) {\n    return;\n  }\n\n  var fromElStyle = fromEl.style;\n  var transFromStyleProps;\n\n  if (fromElStyle) {\n    var styleTransition = styleOpt.transition;\n    var elTransition = elOption.transition;\n\n    if (styleTransition && !isTransitionAll(styleTransition)) {\n      var transitionKeys = normalizeToArray(styleTransition);\n      !transFromStyleProps && (transFromStyleProps = transFromProps.style = {});\n\n      for (var i = 0; i < transitionKeys.length; i++) {\n        var key = transitionKeys[i];\n        var elVal = fromElStyle[key]; // Do not clone, see `checkNonStyleTansitionRefer`.\n\n        transFromStyleProps[key] = elVal;\n      }\n    } else if (fromEl.getAnimationStyleProps && (isTransitionAll(elTransition) || isTransitionAll(styleTransition) || indexOf(elTransition, 'style') >= 0)) {\n      var animationProps = fromEl.getAnimationStyleProps();\n      var animationStyleProps = animationProps ? animationProps.style : null;\n\n      if (animationStyleProps) {\n        !transFromStyleProps && (transFromStyleProps = transFromProps.style = {});\n        var styleKeys = keys(styleOpt);\n\n        for (var i = 0; i < styleKeys.length; i++) {\n          var key = styleKeys[i];\n\n          if (animationStyleProps[key]) {\n            var elVal = fromElStyle[key];\n            transFromStyleProps[key] = elVal;\n          }\n        }\n      }\n    }\n  }\n}\n\nfunction isNonStyleTransitionEnabled(optVal, elVal) {\n  // The same as `checkNonStyleTansitionRefer`.\n  return !isArrayLike(optVal) ? optVal != null && isFinite(optVal) : optVal !== elVal;\n}\n\nvar checkTransformPropRefer;\n\nif (\"development\" !== 'production') {\n  checkTransformPropRefer = function (key, usedIn) {\n    if (!hasOwn(TRANSFORM_PROPS_MAP, key)) {\n      warn('Prop `' + key + '` is not a permitted in `' + usedIn + '`. ' + 'Only `' + keys(TRANSFORM_PROPS_MAP).join('`, `') + '` are permitted.');\n    }\n  };\n}\n\nvar getStateToRestore = makeInner();\nvar KEYFRAME_EXCLUDE_KEYS = ['percent', 'easing', 'shape', 'style', 'extra'];\n/**\n * Stop previous keyframe animation and restore the attributes.\n * Avoid new keyframe animation starts with wrong internal state when the percent: 0 is not set.\n */\n\nfunction stopPreviousKeyframeAnimationAndRestore(el) {\n  // Stop previous keyframe animation.\n  el.stopAnimation('keyframe'); // Restore\n\n  el.attr(getStateToRestore(el));\n}\nfunction applyKeyframeAnimation(el, animationOpts, animatableModel) {\n  if (!animatableModel.isAnimationEnabled() || !animationOpts) {\n    return;\n  }\n\n  if (isArray(animationOpts)) {\n    each(animationOpts, function (singleAnimationOpts) {\n      applyKeyframeAnimation(el, singleAnimationOpts, animatableModel);\n    });\n    return;\n  }\n\n  var keyframes = animationOpts.keyframes;\n  var duration = animationOpts.duration;\n\n  if (animatableModel && duration == null) {\n    // Default to use duration of config.\n    // NOTE: animation config from payload will be ignored because they are mainly for transitions.\n    var config = getAnimationConfig('enter', animatableModel, 0);\n    duration = config && config.duration;\n  }\n\n  if (!keyframes || !duration) {\n    return;\n  }\n\n  var stateToRestore = getStateToRestore(el);\n  each(ELEMENT_ANIMATABLE_PROPS, function (targetPropName) {\n    if (targetPropName && !el[targetPropName]) {\n      return;\n    }\n\n    var animator;\n    var endFrameIsSet = false; // Sort keyframes by percent.\n\n    keyframes.sort(function (a, b) {\n      return a.percent - b.percent;\n    });\n    each(keyframes, function (kf) {\n      // Stop current animation.\n      var animators = el.animators;\n      var kfValues = targetPropName ? kf[targetPropName] : kf;\n\n      if (\"development\" !== 'production') {\n        if (kf.percent >= 1) {\n          endFrameIsSet = true;\n        }\n      }\n\n      if (!kfValues) {\n        return;\n      }\n\n      var propKeys = keys(kfValues);\n\n      if (!targetPropName) {\n        // PENDING performance?\n        propKeys = filter(propKeys, function (key) {\n          return indexOf(KEYFRAME_EXCLUDE_KEYS, key) < 0;\n        });\n      }\n\n      if (!propKeys.length) {\n        return;\n      }\n\n      if (!animator) {\n        animator = el.animate(targetPropName, animationOpts.loop, true);\n        animator.scope = 'keyframe';\n      }\n\n      for (var i = 0; i < animators.length; i++) {\n        // Stop all other animation that is not keyframe.\n        if (animators[i] !== animator && animators[i].targetName === animator.targetName) {\n          animators[i].stopTracks(propKeys);\n        }\n      }\n\n      targetPropName && (stateToRestore[targetPropName] = stateToRestore[targetPropName] || {});\n      var savedTarget = targetPropName ? stateToRestore[targetPropName] : stateToRestore;\n      each(propKeys, function (key) {\n        // Save original value.\n        savedTarget[key] = ((targetPropName ? el[targetPropName] : el) || {})[key];\n      });\n      animator.whenWithKeys(duration * kf.percent, kfValues, propKeys, kf.easing);\n    });\n\n    if (!animator) {\n      return;\n    }\n\n    if (\"development\" !== 'production') {\n      if (!endFrameIsSet) {\n        warn('End frame with percent: 1 is missing in the keyframeAnimation.', true);\n      }\n    }\n\n    animator.delay(animationOpts.delay || 0).duration(duration).start(animationOpts.easing);\n  });\n}\n\nvar EMPHASIS = 'emphasis';\nvar NORMAL = 'normal';\nvar BLUR = 'blur';\nvar SELECT = 'select';\nvar STATES = [NORMAL, EMPHASIS, BLUR, SELECT];\nvar PATH_ITEM_STYLE = {\n  normal: ['itemStyle'],\n  emphasis: [EMPHASIS, 'itemStyle'],\n  blur: [BLUR, 'itemStyle'],\n  select: [SELECT, 'itemStyle']\n};\nvar PATH_LABEL = {\n  normal: ['label'],\n  emphasis: [EMPHASIS, 'label'],\n  blur: [BLUR, 'label'],\n  select: [SELECT, 'label']\n};\nvar DEFAULT_TRANSITION = ['x', 'y']; // Use prefix to avoid index to be the same as el.name,\n// which will cause weird update animation.\n\nvar GROUP_DIFF_PREFIX = 'e\\0\\0';\nvar attachedTxInfoTmp = {\n  normal: {},\n  emphasis: {},\n  blur: {},\n  select: {}\n};\n/**\n * To reduce total package size of each coordinate systems, the modules `prepareCustom`\n * of each coordinate systems are not required by each coordinate systems directly, but\n * required by the module `custom`.\n *\n * prepareInfoForCustomSeries {Function}: optional\n *     @return {Object} {coordSys: {...}, api: {\n *         coord: function (data, clamp) {}, // return point in global.\n *         size: function (dataSize, dataItem) {} // return size of each axis in coordSys.\n *     }}\n */\n\nvar prepareCustoms = {\n  cartesian2d: cartesianPrepareCustom,\n  geo: geoPrepareCustom,\n  single: singlePrepareCustom,\n  polar: polarPrepareCustom,\n  calendar: calendarPrepareCustom\n};\n\nfunction isPath$1(el) {\n  return el instanceof Path;\n}\n\nfunction isDisplayable(el) {\n  return el instanceof Displayable;\n}\n\nfunction copyElement(sourceEl, targetEl) {\n  targetEl.copyTransform(sourceEl);\n\n  if (isDisplayable(targetEl) && isDisplayable(sourceEl)) {\n    targetEl.setStyle(sourceEl.style);\n    targetEl.z = sourceEl.z;\n    targetEl.z2 = sourceEl.z2;\n    targetEl.zlevel = sourceEl.zlevel;\n    targetEl.invisible = sourceEl.invisible;\n    targetEl.ignore = sourceEl.ignore;\n\n    if (isPath$1(targetEl) && isPath$1(sourceEl)) {\n      targetEl.setShape(sourceEl.shape);\n    }\n  }\n}\n\nvar CustomChartView =\n/** @class */\nfunction (_super) {\n  __extends(CustomChartView, _super);\n\n  function CustomChartView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CustomChartView.type;\n    return _this;\n  }\n\n  CustomChartView.prototype.render = function (customSeries, ecModel, api, payload) {\n    // Clear previously rendered progressive elements.\n    this._progressiveEls = null;\n    var oldData = this._data;\n    var data = customSeries.getData();\n    var group = this.group;\n    var renderItem = makeRenderItem(customSeries, data, ecModel, api);\n\n    if (!oldData) {\n      // Previous render is incremental render or first render.\n      // Needs remove the incremental rendered elements.\n      group.removeAll();\n    }\n\n    data.diff(oldData).add(function (newIdx) {\n      createOrUpdateItem(api, null, newIdx, renderItem(newIdx, payload), customSeries, group, data);\n    }).remove(function (oldIdx) {\n      var el = oldData.getItemGraphicEl(oldIdx);\n      el && applyLeaveTransition(el, customInnerStore(el).option, customSeries);\n    }).update(function (newIdx, oldIdx) {\n      var oldEl = oldData.getItemGraphicEl(oldIdx);\n      createOrUpdateItem(api, oldEl, newIdx, renderItem(newIdx, payload), customSeries, group, data);\n    }).execute(); // Do clipping\n\n    var clipPath = customSeries.get('clip', true) ? createClipPath(customSeries.coordinateSystem, false, customSeries) : null;\n\n    if (clipPath) {\n      group.setClipPath(clipPath);\n    } else {\n      group.removeClipPath();\n    }\n\n    this._data = data;\n  };\n\n  CustomChartView.prototype.incrementalPrepareRender = function (customSeries, ecModel, api) {\n    this.group.removeAll();\n    this._data = null;\n  };\n\n  CustomChartView.prototype.incrementalRender = function (params, customSeries, ecModel, api, payload) {\n    var data = customSeries.getData();\n    var renderItem = makeRenderItem(customSeries, data, ecModel, api);\n    var progressiveEls = this._progressiveEls = [];\n\n    function setIncrementalAndHoverLayer(el) {\n      if (!el.isGroup) {\n        el.incremental = true;\n        el.ensureState('emphasis').hoverLayer = true;\n      }\n    }\n\n    for (var idx = params.start; idx < params.end; idx++) {\n      var el = createOrUpdateItem(null, null, idx, renderItem(idx, payload), customSeries, this.group, data);\n\n      if (el) {\n        el.traverse(setIncrementalAndHoverLayer);\n        progressiveEls.push(el);\n      }\n    }\n  };\n\n  CustomChartView.prototype.eachRendered = function (cb) {\n    traverseElements(this._progressiveEls || this.group, cb);\n  };\n\n  CustomChartView.prototype.filterForExposedEvent = function (eventType, query, targetEl, packedEvent) {\n    var elementName = query.element;\n\n    if (elementName == null || targetEl.name === elementName) {\n      return true;\n    } // Enable to give a name on a group made by `renderItem`, and listen\n    // events that are triggered by its descendents.\n\n\n    while ((targetEl = targetEl.__hostTarget || targetEl.parent) && targetEl !== this.group) {\n      if (targetEl.name === elementName) {\n        return true;\n      }\n    }\n\n    return false;\n  };\n\n  CustomChartView.type = 'custom';\n  return CustomChartView;\n}(ChartView);\n\nfunction createEl(elOption) {\n  var graphicType = elOption.type;\n  var el; // Those graphic elements are not shapes. They should not be\n  // overwritten by users, so do them first.\n\n  if (graphicType === 'path') {\n    var shape = elOption.shape; // Using pathRect brings convenience to users sacle svg path.\n\n    var pathRect = shape.width != null && shape.height != null ? {\n      x: shape.x || 0,\n      y: shape.y || 0,\n      width: shape.width,\n      height: shape.height\n    } : null;\n    var pathData = getPathData(shape); // Path is also used for icon, so layout 'center' by default.\n\n    el = makePath(pathData, null, pathRect, shape.layout || 'center');\n    customInnerStore(el).customPathData = pathData;\n  } else if (graphicType === 'image') {\n    el = new ZRImage({});\n    customInnerStore(el).customImagePath = elOption.style.image;\n  } else if (graphicType === 'text') {\n    el = new ZRText({}); // customInnerStore(el).customText = (elOption.style as TextStyleProps).text;\n  } else if (graphicType === 'group') {\n    el = new Group();\n  } else if (graphicType === 'compoundPath') {\n    throw new Error('\"compoundPath\" is not supported yet.');\n  } else {\n    var Clz = getShapeClass(graphicType);\n\n    if (!Clz) {\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        errMsg = 'graphic type \"' + graphicType + '\" can not be found.';\n      }\n\n      throwError(errMsg);\n    }\n\n    el = new Clz();\n  }\n\n  customInnerStore(el).customGraphicType = graphicType;\n  el.name = elOption.name; // Compat ec4: the default z2 lift is 1. If changing the number,\n  // some cases probably be broken: hierarchy layout along z, like circle packing,\n  // where emphasis only intending to modify color/border rather than lift z2.\n\n  el.z2EmphasisLift = 1;\n  el.z2SelectLift = 1;\n  return el;\n}\n\nfunction updateElNormal( // Can be null/undefined\napi, el, dataIndex, elOption, attachedTxInfo, seriesModel, isInit) {\n  // Stop and restore before update any other attributes.\n  stopPreviousKeyframeAnimationAndRestore(el);\n  var txCfgOpt = attachedTxInfo && attachedTxInfo.normal.cfg;\n\n  if (txCfgOpt) {\n    // PENDING: whether use user object directly rather than clone?\n    // TODO:5.0 textConfig transition animation?\n    el.setTextConfig(txCfgOpt);\n  } // Default transition ['x', 'y']\n\n\n  if (elOption && elOption.transition == null) {\n    elOption.transition = DEFAULT_TRANSITION;\n  } // Do some normalization on style.\n\n\n  var styleOpt = elOption && elOption.style;\n\n  if (styleOpt) {\n    if (el.type === 'text') {\n      var textOptionStyle = styleOpt; // Compatible with ec4: if `textFill` or `textStroke` exists use them.\n\n      hasOwn(textOptionStyle, 'textFill') && (textOptionStyle.fill = textOptionStyle.textFill);\n      hasOwn(textOptionStyle, 'textStroke') && (textOptionStyle.stroke = textOptionStyle.textStroke);\n    }\n\n    var decalPattern = void 0;\n    var decalObj = isPath$1(el) ? styleOpt.decal : null;\n\n    if (api && decalObj) {\n      decalObj.dirty = true;\n      decalPattern = createOrUpdatePatternFromDecal(decalObj, api);\n    } // Always overwrite in case user specify this prop.\n\n\n    styleOpt.__decalPattern = decalPattern;\n  }\n\n  if (isDisplayable(el)) {\n    if (styleOpt) {\n      var decalPattern = styleOpt.__decalPattern;\n\n      if (decalPattern) {\n        styleOpt.decal = decalPattern;\n      }\n    }\n  }\n\n  applyUpdateTransition(el, elOption, seriesModel, {\n    dataIndex: dataIndex,\n    isInit: isInit,\n    clearStyle: true\n  });\n  applyKeyframeAnimation(el, elOption.keyframeAnimation, seriesModel);\n}\n\nfunction updateElOnState(state, el, elStateOpt, styleOpt, attachedTxInfo) {\n  var elDisplayable = el.isGroup ? null : el;\n  var txCfgOpt = attachedTxInfo && attachedTxInfo[state].cfg; // PENDING:5.0 support customize scale change and transition animation?\n\n  if (elDisplayable) {\n    // By default support auto lift color when hover whether `emphasis` specified.\n    var stateObj = elDisplayable.ensureState(state);\n\n    if (styleOpt === false) {\n      var existingEmphasisState = elDisplayable.getState(state);\n\n      if (existingEmphasisState) {\n        existingEmphasisState.style = null;\n      }\n    } else {\n      // style is needed to enable default emphasis.\n      stateObj.style = styleOpt || null;\n    } // If `elOption.styleEmphasis` or `elOption.emphasis.style` is `false`,\n    // remove hover style.\n    // If `elOption.textConfig` or `elOption.emphasis.textConfig` is null/undefined, it does not\n    // make sense. So for simplicity, we do not ditinguish `hasOwnProperty` and null/undefined.\n\n\n    if (txCfgOpt) {\n      stateObj.textConfig = txCfgOpt;\n    }\n\n    setDefaultStateProxy(elDisplayable);\n  }\n}\n\nfunction updateZ$1(el, elOption, seriesModel) {\n  // Group not support textContent and not support z yet.\n  if (el.isGroup) {\n    return;\n  }\n\n  var elDisplayable = el;\n  var currentZ = seriesModel.currentZ;\n  var currentZLevel = seriesModel.currentZLevel; // Always erase.\n\n  elDisplayable.z = currentZ;\n  elDisplayable.zlevel = currentZLevel; // z2 must not be null/undefined, otherwise sort error may occur.\n\n  var optZ2 = elOption.z2;\n  optZ2 != null && (elDisplayable.z2 = optZ2 || 0);\n\n  for (var i = 0; i < STATES.length; i++) {\n    updateZForEachState(elDisplayable, elOption, STATES[i]);\n  }\n}\n\nfunction updateZForEachState(elDisplayable, elOption, state) {\n  var isNormal = state === NORMAL;\n  var elStateOpt = isNormal ? elOption : retrieveStateOption(elOption, state);\n  var optZ2 = elStateOpt ? elStateOpt.z2 : null;\n  var stateObj;\n\n  if (optZ2 != null) {\n    // Do not `ensureState` until required.\n    stateObj = isNormal ? elDisplayable : elDisplayable.ensureState(state);\n    stateObj.z2 = optZ2 || 0;\n  }\n}\n\nfunction makeRenderItem(customSeries, data, ecModel, api) {\n  var renderItem = customSeries.get('renderItem');\n  var coordSys = customSeries.coordinateSystem;\n  var prepareResult = {};\n\n  if (coordSys) {\n    if (\"development\" !== 'production') {\n      assert(renderItem, 'series.render is required.');\n      assert(coordSys.prepareCustoms || prepareCustoms[coordSys.type], 'This coordSys does not support custom series.');\n    } // `coordSys.prepareCustoms` is used for external coord sys like bmap.\n\n\n    prepareResult = coordSys.prepareCustoms ? coordSys.prepareCustoms(coordSys) : prepareCustoms[coordSys.type](coordSys);\n  }\n\n  var userAPI = defaults({\n    getWidth: api.getWidth,\n    getHeight: api.getHeight,\n    getZr: api.getZr,\n    getDevicePixelRatio: api.getDevicePixelRatio,\n    value: value,\n    style: style,\n    ordinalRawValue: ordinalRawValue,\n    styleEmphasis: styleEmphasis,\n    visual: visual,\n    barLayout: barLayout,\n    currentSeriesIndices: currentSeriesIndices,\n    font: font\n  }, prepareResult.api || {});\n  var userParams = {\n    // The life cycle of context: current round of rendering.\n    // The global life cycle is probably not necessary, because\n    // user can store global status by themselves.\n    context: {},\n    seriesId: customSeries.id,\n    seriesName: customSeries.name,\n    seriesIndex: customSeries.seriesIndex,\n    coordSys: prepareResult.coordSys,\n    dataInsideLength: data.count(),\n    encode: wrapEncodeDef(customSeries.getData())\n  }; // If someday intending to refactor them to a class, should consider do not\n  // break change: currently these attribute member are encapsulated in a closure\n  // so that do not need to force user to call these method with a scope.\n  // Do not support call `api` asynchronously without dataIndexInside input.\n\n  var currDataIndexInside;\n  var currItemModel;\n  var currItemStyleModels = {};\n  var currLabelModels = {};\n  var seriesItemStyleModels = {};\n  var seriesLabelModels = {};\n\n  for (var i = 0; i < STATES.length; i++) {\n    var stateName = STATES[i];\n    seriesItemStyleModels[stateName] = customSeries.getModel(PATH_ITEM_STYLE[stateName]);\n    seriesLabelModels[stateName] = customSeries.getModel(PATH_LABEL[stateName]);\n  }\n\n  function getItemModel(dataIndexInside) {\n    return dataIndexInside === currDataIndexInside ? currItemModel || (currItemModel = data.getItemModel(dataIndexInside)) : data.getItemModel(dataIndexInside);\n  }\n\n  function getItemStyleModel(dataIndexInside, state) {\n    return !data.hasItemOption ? seriesItemStyleModels[state] : dataIndexInside === currDataIndexInside ? currItemStyleModels[state] || (currItemStyleModels[state] = getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state])) : getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state]);\n  }\n\n  function getLabelModel(dataIndexInside, state) {\n    return !data.hasItemOption ? seriesLabelModels[state] : dataIndexInside === currDataIndexInside ? currLabelModels[state] || (currLabelModels[state] = getItemModel(dataIndexInside).getModel(PATH_LABEL[state])) : getItemModel(dataIndexInside).getModel(PATH_LABEL[state]);\n  }\n\n  return function (dataIndexInside, payload) {\n    currDataIndexInside = dataIndexInside;\n    currItemModel = null;\n    currItemStyleModels = {};\n    currLabelModels = {};\n    return renderItem && renderItem(defaults({\n      dataIndexInside: dataIndexInside,\n      dataIndex: data.getRawIndex(dataIndexInside),\n      // Can be used for optimization when zoom or roam.\n      actionType: payload ? payload.type : null\n    }, userParams), userAPI);\n  };\n  /**\n   * @public\n   * @param dim by default 0.\n   * @param dataIndexInside by default `currDataIndexInside`.\n   */\n\n  function value(dim, dataIndexInside) {\n    dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n    return data.getStore().get(data.getDimensionIndex(dim || 0), dataIndexInside);\n  }\n  /**\n   * @public\n   * @param dim by default 0.\n   * @param dataIndexInside by default `currDataIndexInside`.\n   */\n\n\n  function ordinalRawValue(dim, dataIndexInside) {\n    dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n    dim = dim || 0;\n    var dimInfo = data.getDimensionInfo(dim);\n\n    if (!dimInfo) {\n      var dimIndex = data.getDimensionIndex(dim);\n      return dimIndex >= 0 ? data.getStore().get(dimIndex, dataIndexInside) : undefined;\n    }\n\n    var val = data.get(dimInfo.name, dataIndexInside);\n    var ordinalMeta = dimInfo && dimInfo.ordinalMeta;\n    return ordinalMeta ? ordinalMeta.categories[val] : val;\n  }\n  /**\n   * @deprecated The original intention of `api.style` is enable to set itemStyle\n   * like other series. But it is not necessary and not easy to give a strict definition\n   * of what it returns. And since echarts5 it needs to be make compat work. So\n   * deprecates it since echarts5.\n   *\n   * By default, `visual` is applied to style (to support visualMap).\n   * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`,\n   * it can be implemented as:\n   * `api.style({stroke: api.visual('color'), fill: null})`;\n   *\n   * [Compat]: since ec5, RectText has been separated from its hosts el.\n   * so `api.style()` will only return the style from `itemStyle` but not handle `label`\n   * any more. But `series.label` config is never published in doc.\n   * We still compat it in `api.style()`. But not encourage to use it and will still not\n   * to pulish it to doc.\n   * @public\n   * @param dataIndexInside by default `currDataIndexInside`.\n   */\n\n\n  function style(userProps, dataIndexInside) {\n    if (\"development\" !== 'production') {\n      warnDeprecated('api.style', 'Please write literal style directly instead.');\n    }\n\n    dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n    var style = data.getItemVisual(dataIndexInside, 'style');\n    var visualColor = style && style.fill;\n    var opacity = style && style.opacity;\n    var itemStyle = getItemStyleModel(dataIndexInside, NORMAL).getItemStyle();\n    visualColor != null && (itemStyle.fill = visualColor);\n    opacity != null && (itemStyle.opacity = opacity);\n    var opt = {\n      inheritColor: isString(visualColor) ? visualColor : '#000'\n    };\n    var labelModel = getLabelModel(dataIndexInside, NORMAL); // Now that the feature of \"auto adjust text fill/stroke\" has been migrated to zrender\n    // since ec5, we should set `isAttached` as `false` here and make compat in\n    // `convertToEC4StyleForCustomSerise`.\n\n    var textStyle = createTextStyle(labelModel, null, opt, false, true);\n    textStyle.text = labelModel.getShallow('show') ? retrieve2(customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null;\n    var textConfig = createTextConfig(labelModel, opt, false);\n    preFetchFromExtra(userProps, itemStyle);\n    itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig);\n    userProps && applyUserPropsAfter(itemStyle, userProps);\n    itemStyle.legacy = true;\n    return itemStyle;\n  }\n  /**\n   * @deprecated The reason see `api.style()`\n   * @public\n   * @param dataIndexInside by default `currDataIndexInside`.\n   */\n\n\n  function styleEmphasis(userProps, dataIndexInside) {\n    if (\"development\" !== 'production') {\n      warnDeprecated('api.styleEmphasis', 'Please write literal style directly instead.');\n    }\n\n    dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n    var itemStyle = getItemStyleModel(dataIndexInside, EMPHASIS).getItemStyle();\n    var labelModel = getLabelModel(dataIndexInside, EMPHASIS);\n    var textStyle = createTextStyle(labelModel, null, null, true, true);\n    textStyle.text = labelModel.getShallow('show') ? retrieve3(customSeries.getFormattedLabel(dataIndexInside, EMPHASIS), customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null;\n    var textConfig = createTextConfig(labelModel, null, true);\n    preFetchFromExtra(userProps, itemStyle);\n    itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig);\n    userProps && applyUserPropsAfter(itemStyle, userProps);\n    itemStyle.legacy = true;\n    return itemStyle;\n  }\n\n  function applyUserPropsAfter(itemStyle, extra) {\n    for (var key in extra) {\n      if (hasOwn(extra, key)) {\n        itemStyle[key] = extra[key];\n      }\n    }\n  }\n\n  function preFetchFromExtra(extra, itemStyle) {\n    // A trick to retrieve those props firstly, which are used to\n    // apply auto inside fill/stroke in `convertToEC4StyleForCustomSerise`.\n    // (It's not reasonable but only for a degree of compat)\n    if (extra) {\n      extra.textFill && (itemStyle.textFill = extra.textFill);\n      extra.textPosition && (itemStyle.textPosition = extra.textPosition);\n    }\n  }\n  /**\n   * @public\n   * @param dataIndexInside by default `currDataIndexInside`.\n   */\n\n\n  function visual(visualType, dataIndexInside) {\n    dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n\n    if (hasOwn(STYLE_VISUAL_TYPE, visualType)) {\n      var style_1 = data.getItemVisual(dataIndexInside, 'style');\n      return style_1 ? style_1[STYLE_VISUAL_TYPE[visualType]] : null;\n    } // Only support these visuals. Other visual might be inner tricky\n    // for performance (like `style`), do not expose to users.\n\n\n    if (hasOwn(NON_STYLE_VISUAL_PROPS, visualType)) {\n      return data.getItemVisual(dataIndexInside, visualType);\n    }\n  }\n  /**\n   * @public\n   * @return If not support, return undefined.\n   */\n\n\n  function barLayout(opt) {\n    if (coordSys.type === 'cartesian2d') {\n      var baseAxis = coordSys.getBaseAxis();\n      return getLayoutOnAxis(defaults({\n        axis: baseAxis\n      }, opt));\n    }\n  }\n  /**\n   * @public\n   */\n\n\n  function currentSeriesIndices() {\n    return ecModel.getCurrentSeriesIndices();\n  }\n  /**\n   * @public\n   * @return font string\n   */\n\n\n  function font(opt) {\n    return getFont(opt, ecModel);\n  }\n}\n\nfunction wrapEncodeDef(data) {\n  var encodeDef = {};\n  each(data.dimensions, function (dimName) {\n    var dimInfo = data.getDimensionInfo(dimName);\n\n    if (!dimInfo.isExtraCoord) {\n      var coordDim = dimInfo.coordDim;\n      var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || [];\n      dataDims[dimInfo.coordDimIndex] = data.getDimensionIndex(dimName);\n    }\n  });\n  return encodeDef;\n}\n\nfunction createOrUpdateItem(api, existsEl, dataIndex, elOption, seriesModel, group, data) {\n  // [Rule]\n  // If `renderItem` returns `null`/`undefined`/`false`, remove the previous el if existing.\n  //     (It seems that violate the \"merge\" principle, but most of users probably intuitively\n  //     regard \"return;\" as \"show nothing element whatever\", so make a exception to meet the\n  //     most cases.)\n  // The rule or \"merge\" see [STRATEGY_MERGE].\n  // If `elOption` is `null`/`undefined`/`false` (when `renderItem` returns nothing).\n  if (!elOption) {\n    group.remove(existsEl);\n    return;\n  }\n\n  var el = doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group);\n  el && data.setItemGraphicEl(dataIndex, el);\n  el && toggleHoverEmphasis(el, elOption.focus, elOption.blurScope, elOption.emphasisDisabled);\n  return el;\n}\n\nfunction doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group) {\n  if (\"development\" !== 'production') {\n    assert(elOption, 'should not have an null/undefined element setting');\n  }\n\n  var toBeReplacedIdx = -1;\n  var oldEl = existsEl;\n\n  if (existsEl && doesElNeedRecreate(existsEl, elOption, seriesModel) // || (\n  //     // PENDING: even in one-to-one mapping case, if el is marked as morph,\n  //     // do not sure whether the el will be mapped to another el with different\n  //     // hierarchy in Group tree. So always recreate el rather than reuse the el.\n  //     morphHelper && morphHelper.isOneToOneFrom(el)\n  // )\n  ) {\n    // Should keep at the original index, otherwise \"merge by index\" will be incorrect.\n    toBeReplacedIdx = indexOf(group.childrenRef(), existsEl);\n    existsEl = null;\n  }\n\n  var isInit = !existsEl;\n  var el = existsEl;\n\n  if (!el) {\n    el = createEl(elOption);\n\n    if (oldEl) {\n      copyElement(oldEl, el);\n    }\n  } else {\n    // FIMXE:NEXT unified clearState?\n    // If in some case the performance issue arised, consider\n    // do not clearState but update cached normal state directly.\n    el.clearStates();\n  } // Need to set morph: false explictly to disable automatically morphing.\n\n\n  if (elOption.morph === false) {\n    el.disableMorphing = true;\n  } else if (el.disableMorphing) {\n    el.disableMorphing = false;\n  }\n\n  attachedTxInfoTmp.normal.cfg = attachedTxInfoTmp.normal.conOpt = attachedTxInfoTmp.emphasis.cfg = attachedTxInfoTmp.emphasis.conOpt = attachedTxInfoTmp.blur.cfg = attachedTxInfoTmp.blur.conOpt = attachedTxInfoTmp.select.cfg = attachedTxInfoTmp.select.conOpt = null;\n  attachedTxInfoTmp.isLegacy = false;\n  doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfoTmp);\n  doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit);\n  updateElNormal(api, el, dataIndex, elOption, attachedTxInfoTmp, seriesModel, isInit); // `elOption.info` enables user to mount some info on\n  // elements and use them in event handlers.\n  // Update them only when user specified, otherwise, remain.\n\n  hasOwn(elOption, 'info') && (customInnerStore(el).info = elOption.info);\n\n  for (var i = 0; i < STATES.length; i++) {\n    var stateName = STATES[i];\n\n    if (stateName !== NORMAL) {\n      var otherStateOpt = retrieveStateOption(elOption, stateName);\n      var otherStyleOpt = retrieveStyleOptionOnState(elOption, otherStateOpt, stateName);\n      updateElOnState(stateName, el, otherStateOpt, otherStyleOpt, attachedTxInfoTmp);\n    }\n  }\n\n  updateZ$1(el, elOption, seriesModel);\n\n  if (elOption.type === 'group') {\n    mergeChildren(api, el, dataIndex, elOption, seriesModel);\n  }\n\n  if (toBeReplacedIdx >= 0) {\n    group.replaceAt(el, toBeReplacedIdx);\n  } else {\n    group.add(el);\n  }\n\n  return el;\n} // `el` must not be null/undefined.\n\n\nfunction doesElNeedRecreate(el, elOption, seriesModel) {\n  var elInner = customInnerStore(el);\n  var elOptionType = elOption.type;\n  var elOptionShape = elOption.shape;\n  var elOptionStyle = elOption.style;\n  return (// Always create new if universal transition is enabled.\n    // Because we do transition after render. It needs to know what old element is. Replacement will loose it.\n    seriesModel.isUniversalTransitionEnabled() // If `elOptionType` is `null`, follow the merge principle.\n    || elOptionType != null && elOptionType !== elInner.customGraphicType || elOptionType === 'path' && hasOwnPathData(elOptionShape) && getPathData(elOptionShape) !== elInner.customPathData || elOptionType === 'image' && hasOwn(elOptionStyle, 'image') && elOptionStyle.image !== elInner.customImagePath // // FIXME test and remove this restriction?\n    // || (elOptionType === 'text'\n    //     && hasOwn(elOptionStyle, 'text')\n    //     && (elOptionStyle as TextStyleProps).text !== elInner.customText\n    // )\n\n  );\n}\n\nfunction doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit) {\n  // Based on the \"merge\" principle, if no clipPath provided,\n  // do nothing. The exists clip will be totally removed only if\n  // `el.clipPath` is `false`. Otherwise it will be merged/replaced.\n  var clipPathOpt = elOption.clipPath;\n\n  if (clipPathOpt === false) {\n    if (el && el.getClipPath()) {\n      el.removeClipPath();\n    }\n  } else if (clipPathOpt) {\n    var clipPath = el.getClipPath();\n\n    if (clipPath && doesElNeedRecreate(clipPath, clipPathOpt, seriesModel)) {\n      clipPath = null;\n    }\n\n    if (!clipPath) {\n      clipPath = createEl(clipPathOpt);\n\n      if (\"development\" !== 'production') {\n        assert(isPath$1(clipPath), 'Only any type of `path` can be used in `clipPath`, rather than ' + clipPath.type + '.');\n      }\n\n      el.setClipPath(clipPath);\n    }\n\n    updateElNormal(null, clipPath, dataIndex, clipPathOpt, null, seriesModel, isInit);\n  } // If not define `clipPath` in option, do nothing unnecessary.\n\n}\n\nfunction doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfo) {\n  // Group does not support textContent temporarily until necessary.\n  if (el.isGroup) {\n    return;\n  } // Normal must be called before emphasis, for `isLegacy` detection.\n\n\n  processTxInfo(elOption, null, attachedTxInfo);\n  processTxInfo(elOption, EMPHASIS, attachedTxInfo); // If `elOption.textConfig` or `elOption.textContent` is null/undefined, it does not make sense.\n  // So for simplicity, if \"elOption hasOwnProperty of them but be null/undefined\", we do not\n  // trade them as set to null to el.\n  // Especially:\n  // `elOption.textContent: false` means remove textContent.\n  // `elOption.textContent.emphasis.style: false` means remove the style from emphasis state.\n\n  var txConOptNormal = attachedTxInfo.normal.conOpt;\n  var txConOptEmphasis = attachedTxInfo.emphasis.conOpt;\n  var txConOptBlur = attachedTxInfo.blur.conOpt;\n  var txConOptSelect = attachedTxInfo.select.conOpt;\n\n  if (txConOptNormal != null || txConOptEmphasis != null || txConOptSelect != null || txConOptBlur != null) {\n    var textContent = el.getTextContent();\n\n    if (txConOptNormal === false) {\n      textContent && el.removeTextContent();\n    } else {\n      txConOptNormal = attachedTxInfo.normal.conOpt = txConOptNormal || {\n        type: 'text'\n      };\n\n      if (!textContent) {\n        textContent = createEl(txConOptNormal);\n        el.setTextContent(textContent);\n      } else {\n        // If in some case the performance issue arised, consider\n        // do not clearState but update cached normal state directly.\n        textContent.clearStates();\n      }\n\n      updateElNormal(null, textContent, dataIndex, txConOptNormal, null, seriesModel, isInit);\n      var txConStlOptNormal = txConOptNormal && txConOptNormal.style;\n\n      for (var i = 0; i < STATES.length; i++) {\n        var stateName = STATES[i];\n\n        if (stateName !== NORMAL) {\n          var txConOptOtherState = attachedTxInfo[stateName].conOpt;\n          updateElOnState(stateName, textContent, txConOptOtherState, retrieveStyleOptionOnState(txConOptNormal, txConOptOtherState, stateName), null);\n        }\n      }\n\n      txConStlOptNormal ? textContent.dirty() : textContent.markRedraw();\n    }\n  }\n}\n\nfunction processTxInfo(elOption, state, attachedTxInfo) {\n  var stateOpt = !state ? elOption : retrieveStateOption(elOption, state);\n  var styleOpt = !state ? elOption.style : retrieveStyleOptionOnState(elOption, stateOpt, EMPHASIS);\n  var elType = elOption.type;\n  var txCfg = stateOpt ? stateOpt.textConfig : null;\n  var txConOptNormal = elOption.textContent;\n  var txConOpt = !txConOptNormal ? null : !state ? txConOptNormal : retrieveStateOption(txConOptNormal, state);\n\n  if (styleOpt && ( // Because emphasis style has little info to detect legacy,\n  // if normal is legacy, emphasis is trade as legacy.\n  attachedTxInfo.isLegacy || isEC4CompatibleStyle(styleOpt, elType, !!txCfg, !!txConOpt))) {\n    attachedTxInfo.isLegacy = true;\n    var convertResult = convertFromEC4CompatibleStyle(styleOpt, elType, !state); // Explicitly specified `textConfig` and `textContent` has higher priority than\n    // the ones generated by legacy style. Otherwise if users use them and `api.style`\n    // at the same time, they not both work and hardly to known why.\n\n    if (!txCfg && convertResult.textConfig) {\n      txCfg = convertResult.textConfig;\n    }\n\n    if (!txConOpt && convertResult.textContent) {\n      txConOpt = convertResult.textContent;\n    }\n  }\n\n  if (!state && txConOpt) {\n    var txConOptNormal_1 = txConOpt; // `textContent: {type: 'text'}`, the \"type\" is easy to be missing. So we tolerate it.\n\n    !txConOptNormal_1.type && (txConOptNormal_1.type = 'text');\n\n    if (\"development\" !== 'production') {\n      // Do not tolerate incorrcet type for forward compat.\n      assert(txConOptNormal_1.type === 'text', 'textContent.type must be \"text\"');\n    }\n  }\n\n  var info = !state ? attachedTxInfo.normal : attachedTxInfo[state];\n  info.cfg = txCfg;\n  info.conOpt = txConOpt;\n}\n\nfunction retrieveStateOption(elOption, state) {\n  return !state ? elOption : elOption ? elOption[state] : null;\n}\n\nfunction retrieveStyleOptionOnState(stateOptionNormal, stateOption, state) {\n  var style = stateOption && stateOption.style;\n\n  if (style == null && state === EMPHASIS && stateOptionNormal) {\n    style = stateOptionNormal.styleEmphasis;\n  }\n\n  return style;\n} // Usage:\n// (1) By default, `elOption.$mergeChildren` is `'byIndex'`, which indicates\n//     that the existing children will not be removed, and enables the feature\n//     that update some of the props of some of the children simply by construct\n//     the returned children of `renderItem` like:\n//     `var children = group.children = []; children[3] = {opacity: 0.5};`\n// (2) If `elOption.$mergeChildren` is `'byName'`, add/update/remove children\n//     by child.name. But that might be lower performance.\n// (3) If `elOption.$mergeChildren` is `false`, the existing children will be\n//     replaced totally.\n// (4) If `!elOption.children`, following the \"merge\" principle, nothing will\n//     happen.\n// (5) If `elOption.$mergeChildren` is not `false` neither `'byName'` and the\n//     `el` is a group, and if any of the new child is null, it means to remove\n//     the element at the same index, if exists. On the other hand, if the new\n//     child is and empty object `{}`, it means to keep the element not changed.\n//\n// For implementation simpleness, do not provide a direct way to remove single\n// child (otherwise the total indices of the children array have to be modified).\n// User can remove a single child by setting its `ignore` to `true`.\n\n\nfunction mergeChildren(api, el, dataIndex, elOption, seriesModel) {\n  var newChildren = elOption.children;\n  var newLen = newChildren ? newChildren.length : 0;\n  var mergeChildren = elOption.$mergeChildren; // `diffChildrenByName` has been deprecated.\n\n  var byName = mergeChildren === 'byName' || elOption.diffChildrenByName;\n  var notMerge = mergeChildren === false; // For better performance on roam update, only enter if necessary.\n\n  if (!newLen && !byName && !notMerge) {\n    return;\n  }\n\n  if (byName) {\n    diffGroupChildren({\n      api: api,\n      oldChildren: el.children() || [],\n      newChildren: newChildren || [],\n      dataIndex: dataIndex,\n      seriesModel: seriesModel,\n      group: el\n    });\n    return;\n  }\n\n  notMerge && el.removeAll(); // Mapping children of a group simply by index, which\n  // might be better performance.\n\n  var index = 0;\n\n  for (; index < newLen; index++) {\n    var newChild = newChildren[index];\n    var oldChild = el.childAt(index);\n\n    if (newChild) {\n      if (newChild.ignore == null) {\n        // The old child is set to be ignored if null (see comments\n        // below). So we need to set ignore to be false back.\n        newChild.ignore = false;\n      }\n\n      doCreateOrUpdateEl(api, oldChild, dataIndex, newChild, seriesModel, el);\n    } else {\n      if (\"development\" !== 'production') {\n        assert(oldChild, 'renderItem should not return a group containing elements' + ' as null/undefined/{} if they do not exist before.');\n      } // If the new element option is null, it means to remove the old\n      // element. But we cannot really remove the element from the group\n      // directly, because the element order may not be stable when this\n      // element is added back. So we set the element to be ignored.\n\n\n      oldChild.ignore = true;\n    }\n  }\n\n  for (var i = el.childCount() - 1; i >= index; i--) {\n    var child = el.childAt(i);\n    removeChildFromGroup(el, child, seriesModel);\n  }\n}\n\nfunction removeChildFromGroup(group, child, seriesModel) {\n  // Do not support leave elements that are not mentioned in the latest\n  // `renderItem` return. Otherwise users may not have a clear and simple\n  // concept that how to control all of the elements.\n  child && applyLeaveTransition(child, customInnerStore(group).option, seriesModel);\n}\n\nfunction diffGroupChildren(context) {\n  new DataDiffer(context.oldChildren, context.newChildren, getKey, getKey, context).add(processAddUpdate).update(processAddUpdate).remove(processRemove).execute();\n}\n\nfunction getKey(item, idx) {\n  var name = item && item.name;\n  return name != null ? name : GROUP_DIFF_PREFIX + idx;\n}\n\nfunction processAddUpdate(newIndex, oldIndex) {\n  var context = this.context;\n  var childOption = newIndex != null ? context.newChildren[newIndex] : null;\n  var child = oldIndex != null ? context.oldChildren[oldIndex] : null;\n  doCreateOrUpdateEl(context.api, child, context.dataIndex, childOption, context.seriesModel, context.group);\n}\n\nfunction processRemove(oldIndex) {\n  var context = this.context;\n  var child = context.oldChildren[oldIndex];\n  child && applyLeaveTransition(child, customInnerStore(child).option, context.seriesModel);\n}\n/**\n * @return SVG Path data.\n */\n\n\nfunction getPathData(shape) {\n  // \"d\" follows the SVG convention.\n  return shape && (shape.pathData || shape.d);\n}\n\nfunction hasOwnPathData(shape) {\n  return shape && (hasOwn(shape, 'pathData') || hasOwn(shape, 'd'));\n}\n\nfunction install$r(registers) {\n  registers.registerChartView(CustomChartView);\n  registers.registerSeriesModel(CustomSeriesModel);\n}\n\nvar inner$a = makeInner();\nvar clone$3 = clone;\nvar bind$1 = bind;\n/**\n * Base axis pointer class in 2D.\n */\n\nvar BaseAxisPointer =\n/** @class */\nfunction () {\n  function BaseAxisPointer() {\n    this._dragging = false;\n    /**\n     * In px, arbitrary value. Do not set too small,\n     * no animation is ok for most cases.\n     */\n\n    this.animationThreshold = 15;\n  }\n  /**\n   * @implement\n   */\n\n\n  BaseAxisPointer.prototype.render = function (axisModel, axisPointerModel, api, forceRender) {\n    var value = axisPointerModel.get('value');\n    var status = axisPointerModel.get('status'); // Bind them to `this`, not in closure, otherwise they will not\n    // be replaced when user calling setOption in not merge mode.\n\n    this._axisModel = axisModel;\n    this._axisPointerModel = axisPointerModel;\n    this._api = api; // Optimize: `render` will be called repeatly during mouse move.\n    // So it is power consuming if performing `render` each time,\n    // especially on mobile device.\n\n    if (!forceRender && this._lastValue === value && this._lastStatus === status) {\n      return;\n    }\n\n    this._lastValue = value;\n    this._lastStatus = status;\n    var group = this._group;\n    var handle = this._handle;\n\n    if (!status || status === 'hide') {\n      // Do not clear here, for animation better.\n      group && group.hide();\n      handle && handle.hide();\n      return;\n    }\n\n    group && group.show();\n    handle && handle.show(); // Otherwise status is 'show'\n\n    var elOption = {};\n    this.makeElOption(elOption, value, axisModel, axisPointerModel, api); // Enable change axis pointer type.\n\n    var graphicKey = elOption.graphicKey;\n\n    if (graphicKey !== this._lastGraphicKey) {\n      this.clear(api);\n    }\n\n    this._lastGraphicKey = graphicKey;\n    var moveAnimation = this._moveAnimation = this.determineAnimation(axisModel, axisPointerModel);\n\n    if (!group) {\n      group = this._group = new Group();\n      this.createPointerEl(group, elOption, axisModel, axisPointerModel);\n      this.createLabelEl(group, elOption, axisModel, axisPointerModel);\n      api.getZr().add(group);\n    } else {\n      var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation);\n      this.updatePointerEl(group, elOption, doUpdateProps);\n      this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);\n    }\n\n    updateMandatoryProps(group, axisPointerModel, true);\n\n    this._renderHandle(value);\n  };\n  /**\n   * @implement\n   */\n\n\n  BaseAxisPointer.prototype.remove = function (api) {\n    this.clear(api);\n  };\n  /**\n   * @implement\n   */\n\n\n  BaseAxisPointer.prototype.dispose = function (api) {\n    this.clear(api);\n  };\n  /**\n   * @protected\n   */\n\n\n  BaseAxisPointer.prototype.determineAnimation = function (axisModel, axisPointerModel) {\n    var animation = axisPointerModel.get('animation');\n    var axis = axisModel.axis;\n    var isCategoryAxis = axis.type === 'category';\n    var useSnap = axisPointerModel.get('snap'); // Value axis without snap always do not snap.\n\n    if (!useSnap && !isCategoryAxis) {\n      return false;\n    }\n\n    if (animation === 'auto' || animation == null) {\n      var animationThreshold = this.animationThreshold;\n\n      if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {\n        return true;\n      } // It is important to auto animation when snap used. Consider if there is\n      // a dataZoom, animation will be disabled when too many points exist, while\n      // it will be enabled for better visual effect when little points exist.\n\n\n      if (useSnap) {\n        var seriesDataCount = getAxisInfo(axisModel).seriesDataCount;\n        var axisExtent = axis.getExtent(); // Approximate band width\n\n        return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;\n      }\n\n      return false;\n    }\n\n    return animation === true;\n  };\n  /**\n   * add {pointer, label, graphicKey} to elOption\n   * @protected\n   */\n\n\n  BaseAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {// Shoule be implemenented by sub-class.\n  };\n  /**\n   * @protected\n   */\n\n\n  BaseAxisPointer.prototype.createPointerEl = function (group, elOption, axisModel, axisPointerModel) {\n    var pointerOption = elOption.pointer;\n\n    if (pointerOption) {\n      var pointerEl = inner$a(group).pointerEl = new graphic[pointerOption.type](clone$3(elOption.pointer));\n      group.add(pointerEl);\n    }\n  };\n  /**\n   * @protected\n   */\n\n\n  BaseAxisPointer.prototype.createLabelEl = function (group, elOption, axisModel, axisPointerModel) {\n    if (elOption.label) {\n      var labelEl = inner$a(group).labelEl = new ZRText(clone$3(elOption.label));\n      group.add(labelEl);\n      updateLabelShowHide(labelEl, axisPointerModel);\n    }\n  };\n  /**\n   * @protected\n   */\n\n\n  BaseAxisPointer.prototype.updatePointerEl = function (group, elOption, updateProps) {\n    var pointerEl = inner$a(group).pointerEl;\n\n    if (pointerEl && elOption.pointer) {\n      pointerEl.setStyle(elOption.pointer.style);\n      updateProps(pointerEl, {\n        shape: elOption.pointer.shape\n      });\n    }\n  };\n  /**\n   * @protected\n   */\n\n\n  BaseAxisPointer.prototype.updateLabelEl = function (group, elOption, updateProps, axisPointerModel) {\n    var labelEl = inner$a(group).labelEl;\n\n    if (labelEl) {\n      labelEl.setStyle(elOption.label.style);\n      updateProps(labelEl, {\n        // Consider text length change in vertical axis, animation should\n        // be used on shape, otherwise the effect will be weird.\n        // TODOTODO\n        // shape: elOption.label.shape,\n        x: elOption.label.x,\n        y: elOption.label.y\n      });\n      updateLabelShowHide(labelEl, axisPointerModel);\n    }\n  };\n  /**\n   * @private\n   */\n\n\n  BaseAxisPointer.prototype._renderHandle = function (value) {\n    if (this._dragging || !this.updateHandleTransform) {\n      return;\n    }\n\n    var axisPointerModel = this._axisPointerModel;\n\n    var zr = this._api.getZr();\n\n    var handle = this._handle;\n    var handleModel = axisPointerModel.getModel('handle');\n    var status = axisPointerModel.get('status');\n\n    if (!handleModel.get('show') || !status || status === 'hide') {\n      handle && zr.remove(handle);\n      this._handle = null;\n      return;\n    }\n\n    var isInit;\n\n    if (!this._handle) {\n      isInit = true;\n      handle = this._handle = createIcon(handleModel.get('icon'), {\n        cursor: 'move',\n        draggable: true,\n        onmousemove: function (e) {\n          // Fot mobile devicem, prevent screen slider on the button.\n          stop(e.event);\n        },\n        onmousedown: bind$1(this._onHandleDragMove, this, 0, 0),\n        drift: bind$1(this._onHandleDragMove, this),\n        ondragend: bind$1(this._onHandleDragEnd, this)\n      });\n      zr.add(handle);\n    }\n\n    updateMandatoryProps(handle, axisPointerModel, false); // update style\n\n    handle.setStyle(handleModel.getItemStyle(null, ['color', 'borderColor', 'borderWidth', 'opacity', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'])); // update position\n\n    var handleSize = handleModel.get('size');\n\n    if (!isArray(handleSize)) {\n      handleSize = [handleSize, handleSize];\n    }\n\n    handle.scaleX = handleSize[0] / 2;\n    handle.scaleY = handleSize[1] / 2;\n    createOrUpdate(this, '_doDispatchAxisPointer', handleModel.get('throttle') || 0, 'fixRate');\n\n    this._moveHandleToValue(value, isInit);\n  };\n\n  BaseAxisPointer.prototype._moveHandleToValue = function (value, isInit) {\n    updateProps$1(this._axisPointerModel, !isInit && this._moveAnimation, this._handle, getHandleTransProps(this.getHandleTransform(value, this._axisModel, this._axisPointerModel)));\n  };\n\n  BaseAxisPointer.prototype._onHandleDragMove = function (dx, dy) {\n    var handle = this._handle;\n\n    if (!handle) {\n      return;\n    }\n\n    this._dragging = true; // Persistent for throttle.\n\n    var trans = this.updateHandleTransform(getHandleTransProps(handle), [dx, dy], this._axisModel, this._axisPointerModel);\n    this._payloadInfo = trans;\n    handle.stopAnimation();\n    handle.attr(getHandleTransProps(trans));\n    inner$a(handle).lastProp = null;\n\n    this._doDispatchAxisPointer();\n  };\n  /**\n   * Throttled method.\n   */\n\n\n  BaseAxisPointer.prototype._doDispatchAxisPointer = function () {\n    var handle = this._handle;\n\n    if (!handle) {\n      return;\n    }\n\n    var payloadInfo = this._payloadInfo;\n    var axisModel = this._axisModel;\n\n    this._api.dispatchAction({\n      type: 'updateAxisPointer',\n      x: payloadInfo.cursorPoint[0],\n      y: payloadInfo.cursorPoint[1],\n      tooltipOption: payloadInfo.tooltipOption,\n      axesInfo: [{\n        axisDim: axisModel.axis.dim,\n        axisIndex: axisModel.componentIndex\n      }]\n    });\n  };\n\n  BaseAxisPointer.prototype._onHandleDragEnd = function () {\n    this._dragging = false;\n    var handle = this._handle;\n\n    if (!handle) {\n      return;\n    }\n\n    var value = this._axisPointerModel.get('value'); // Consider snap or categroy axis, handle may be not consistent with\n    // axisPointer. So move handle to align the exact value position when\n    // drag ended.\n\n\n    this._moveHandleToValue(value); // For the effect: tooltip will be shown when finger holding on handle\n    // button, and will be hidden after finger left handle button.\n\n\n    this._api.dispatchAction({\n      type: 'hideTip'\n    });\n  };\n  /**\n   * @private\n   */\n\n\n  BaseAxisPointer.prototype.clear = function (api) {\n    this._lastValue = null;\n    this._lastStatus = null;\n    var zr = api.getZr();\n    var group = this._group;\n    var handle = this._handle;\n\n    if (zr && group) {\n      this._lastGraphicKey = null;\n      group && zr.remove(group);\n      handle && zr.remove(handle);\n      this._group = null;\n      this._handle = null;\n      this._payloadInfo = null;\n    }\n\n    clear(this, '_doDispatchAxisPointer');\n  };\n  /**\n   * @protected\n   */\n\n\n  BaseAxisPointer.prototype.doClear = function () {// Implemented by sub-class if necessary.\n  };\n\n  BaseAxisPointer.prototype.buildLabel = function (xy, wh, xDimIndex) {\n    xDimIndex = xDimIndex || 0;\n    return {\n      x: xy[xDimIndex],\n      y: xy[1 - xDimIndex],\n      width: wh[xDimIndex],\n      height: wh[1 - xDimIndex]\n    };\n  };\n\n  return BaseAxisPointer;\n}();\n\nfunction updateProps$1(animationModel, moveAnimation, el, props) {\n  // Animation optimize.\n  if (!propsEqual(inner$a(el).lastProp, props)) {\n    inner$a(el).lastProp = props;\n    moveAnimation ? updateProps(el, props, animationModel) : (el.stopAnimation(), el.attr(props));\n  }\n}\n\nfunction propsEqual(lastProps, newProps) {\n  if (isObject(lastProps) && isObject(newProps)) {\n    var equals_1 = true;\n    each(newProps, function (item, key) {\n      equals_1 = equals_1 && propsEqual(lastProps[key], item);\n    });\n    return !!equals_1;\n  } else {\n    return lastProps === newProps;\n  }\n}\n\nfunction updateLabelShowHide(labelEl, axisPointerModel) {\n  labelEl[axisPointerModel.get(['label', 'show']) ? 'show' : 'hide']();\n}\n\nfunction getHandleTransProps(trans) {\n  return {\n    x: trans.x || 0,\n    y: trans.y || 0,\n    rotation: trans.rotation || 0\n  };\n}\n\nfunction updateMandatoryProps(group, axisPointerModel, silent) {\n  var z = axisPointerModel.get('z');\n  var zlevel = axisPointerModel.get('zlevel');\n  group && group.traverse(function (el) {\n    if (el.type !== 'group') {\n      z != null && (el.z = z);\n      zlevel != null && (el.zlevel = zlevel);\n      el.silent = silent;\n    }\n  });\n}\n\nfunction buildElStyle(axisPointerModel) {\n  var axisPointerType = axisPointerModel.get('type');\n  var styleModel = axisPointerModel.getModel(axisPointerType + 'Style');\n  var style;\n\n  if (axisPointerType === 'line') {\n    style = styleModel.getLineStyle();\n    style.fill = null;\n  } else if (axisPointerType === 'shadow') {\n    style = styleModel.getAreaStyle();\n    style.stroke = null;\n  }\n\n  return style;\n}\n/**\n * @param {Function} labelPos {align, verticalAlign, position}\n */\n\nfunction buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos) {\n  var value = axisPointerModel.get('value');\n  var text = getValueLabel(value, axisModel.axis, axisModel.ecModel, axisPointerModel.get('seriesDataIndices'), {\n    precision: axisPointerModel.get(['label', 'precision']),\n    formatter: axisPointerModel.get(['label', 'formatter'])\n  });\n  var labelModel = axisPointerModel.getModel('label');\n  var paddings = normalizeCssArray$1(labelModel.get('padding') || 0);\n  var font = labelModel.getFont();\n  var textRect = getBoundingRect(text, font);\n  var position = labelPos.position;\n  var width = textRect.width + paddings[1] + paddings[3];\n  var height = textRect.height + paddings[0] + paddings[2]; // Adjust by align.\n\n  var align = labelPos.align;\n  align === 'right' && (position[0] -= width);\n  align === 'center' && (position[0] -= width / 2);\n  var verticalAlign = labelPos.verticalAlign;\n  verticalAlign === 'bottom' && (position[1] -= height);\n  verticalAlign === 'middle' && (position[1] -= height / 2); // Not overflow ec container\n\n  confineInContainer(position, width, height, api);\n  var bgColor = labelModel.get('backgroundColor');\n\n  if (!bgColor || bgColor === 'auto') {\n    bgColor = axisModel.get(['axisLine', 'lineStyle', 'color']);\n  }\n\n  elOption.label = {\n    // shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},\n    x: position[0],\n    y: position[1],\n    style: createTextStyle(labelModel, {\n      text: text,\n      font: font,\n      fill: labelModel.getTextColor(),\n      padding: paddings,\n      backgroundColor: bgColor\n    }),\n    // Lable should be over axisPointer.\n    z2: 10\n  };\n} // Do not overflow ec container\n\nfunction confineInContainer(position, width, height, api) {\n  var viewWidth = api.getWidth();\n  var viewHeight = api.getHeight();\n  position[0] = Math.min(position[0] + width, viewWidth) - width;\n  position[1] = Math.min(position[1] + height, viewHeight) - height;\n  position[0] = Math.max(position[0], 0);\n  position[1] = Math.max(position[1], 0);\n}\n\nfunction getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {\n  value = axis.scale.parse(value);\n  var text = axis.scale.getLabel({\n    value: value\n  }, {\n    // If `precision` is set, width can be fixed (like '12.00500'), which\n    // helps to debounce when when moving label.\n    precision: opt.precision\n  });\n  var formatter = opt.formatter;\n\n  if (formatter) {\n    var params_1 = {\n      value: getAxisRawValue(axis, {\n        value: value\n      }),\n      axisDimension: axis.dim,\n      axisIndex: axis.index,\n      seriesData: []\n    };\n    each(seriesDataIndices, function (idxItem) {\n      var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);\n      var dataIndex = idxItem.dataIndexInside;\n      var dataParams = series && series.getDataParams(dataIndex);\n      dataParams && params_1.seriesData.push(dataParams);\n    });\n\n    if (isString(formatter)) {\n      text = formatter.replace('{value}', text);\n    } else if (isFunction(formatter)) {\n      text = formatter(params_1);\n    }\n  }\n\n  return text;\n}\nfunction getTransformedPosition(axis, value, layoutInfo) {\n  var transform = create$1();\n  rotate(transform, transform, layoutInfo.rotation);\n  translate(transform, transform, layoutInfo.position);\n  return applyTransform$1([axis.dataToCoord(value), (layoutInfo.labelOffset || 0) + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)], transform);\n}\nfunction buildCartesianSingleLabelElOption(value, elOption, layoutInfo, axisModel, axisPointerModel, api) {\n  // @ts-ignore\n  var textLayout = AxisBuilder.innerTextLayout(layoutInfo.rotation, 0, layoutInfo.labelDirection);\n  layoutInfo.labelMargin = axisPointerModel.get(['label', 'margin']);\n  buildLabelElOption(elOption, axisModel, axisPointerModel, api, {\n    position: getTransformedPosition(axisModel.axis, value, layoutInfo),\n    align: textLayout.textAlign,\n    verticalAlign: textLayout.textVerticalAlign\n  });\n}\nfunction makeLineShape(p1, p2, xDimIndex) {\n  xDimIndex = xDimIndex || 0;\n  return {\n    x1: p1[xDimIndex],\n    y1: p1[1 - xDimIndex],\n    x2: p2[xDimIndex],\n    y2: p2[1 - xDimIndex]\n  };\n}\nfunction makeRectShape(xy, wh, xDimIndex) {\n  xDimIndex = xDimIndex || 0;\n  return {\n    x: xy[xDimIndex],\n    y: xy[1 - xDimIndex],\n    width: wh[xDimIndex],\n    height: wh[1 - xDimIndex]\n  };\n}\nfunction makeSectorShape(cx, cy, r0, r, startAngle, endAngle) {\n  return {\n    cx: cx,\n    cy: cy,\n    r0: r0,\n    r: r,\n    startAngle: startAngle,\n    endAngle: endAngle,\n    clockwise: true\n  };\n}\n\nvar CartesianAxisPointer =\n/** @class */\nfunction (_super) {\n  __extends(CartesianAxisPointer, _super);\n\n  function CartesianAxisPointer() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n  /**\n   * @override\n   */\n\n\n  CartesianAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {\n    var axis = axisModel.axis;\n    var grid = axis.grid;\n    var axisPointerType = axisPointerModel.get('type');\n    var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();\n    var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true));\n\n    if (axisPointerType && axisPointerType !== 'none') {\n      var elStyle = buildElStyle(axisPointerModel);\n      var pointerOption = pointerShapeBuilder[axisPointerType](axis, pixelValue, otherExtent);\n      pointerOption.style = elStyle;\n      elOption.graphicKey = pointerOption.type;\n      elOption.pointer = pointerOption;\n    }\n\n    var layoutInfo = layout$1(grid.model, axisModel);\n    buildCartesianSingleLabelElOption( // @ts-ignore\n    value, elOption, layoutInfo, axisModel, axisPointerModel, api);\n  };\n  /**\n   * @override\n   */\n\n\n  CartesianAxisPointer.prototype.getHandleTransform = function (value, axisModel, axisPointerModel) {\n    var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, {\n      labelInside: false\n    }); // @ts-ignore\n\n    layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']);\n    var pos = getTransformedPosition(axisModel.axis, value, layoutInfo);\n    return {\n      x: pos[0],\n      y: pos[1],\n      rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)\n    };\n  };\n  /**\n   * @override\n   */\n\n\n  CartesianAxisPointer.prototype.updateHandleTransform = function (transform, delta, axisModel, axisPointerModel) {\n    var axis = axisModel.axis;\n    var grid = axis.grid;\n    var axisExtent = axis.getGlobalExtent(true);\n    var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();\n    var dimIndex = axis.dim === 'x' ? 0 : 1;\n    var currPosition = [transform.x, transform.y];\n    currPosition[dimIndex] += delta[dimIndex];\n    currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);\n    currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);\n    var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;\n    var cursorPoint = [cursorOtherValue, cursorOtherValue];\n    cursorPoint[dimIndex] = currPosition[dimIndex]; // Make tooltip do not overlap axisPointer and in the middle of the grid.\n\n    var tooltipOptions = [{\n      verticalAlign: 'middle'\n    }, {\n      align: 'center'\n    }];\n    return {\n      x: currPosition[0],\n      y: currPosition[1],\n      rotation: transform.rotation,\n      cursorPoint: cursorPoint,\n      tooltipOption: tooltipOptions[dimIndex]\n    };\n  };\n\n  return CartesianAxisPointer;\n}(BaseAxisPointer);\n\nfunction getCartesian(grid, axis) {\n  var opt = {};\n  opt[axis.dim + 'AxisIndex'] = axis.index;\n  return grid.getCartesian(opt);\n}\n\nvar pointerShapeBuilder = {\n  line: function (axis, pixelValue, otherExtent) {\n    var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getAxisDimIndex(axis));\n    return {\n      type: 'Line',\n      subPixelOptimize: true,\n      shape: targetShape\n    };\n  },\n  shadow: function (axis, pixelValue, otherExtent) {\n    var bandWidth = Math.max(1, axis.getBandWidth());\n    var span = otherExtent[1] - otherExtent[0];\n    return {\n      type: 'Rect',\n      shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getAxisDimIndex(axis))\n    };\n  }\n};\n\nfunction getAxisDimIndex(axis) {\n  return axis.dim === 'x' ? 0 : 1;\n}\n\nvar AxisPointerModel =\n/** @class */\nfunction (_super) {\n  __extends(AxisPointerModel, _super);\n\n  function AxisPointerModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = AxisPointerModel.type;\n    return _this;\n  }\n\n  AxisPointerModel.type = 'axisPointer';\n  AxisPointerModel.defaultOption = {\n    // 'auto' means that show when triggered by tooltip or handle.\n    show: 'auto',\n    // zlevel: 0,\n    z: 50,\n    type: 'line',\n    // axispointer triggered by tootip determine snap automatically,\n    // see `modelHelper`.\n    snap: false,\n    triggerTooltip: true,\n    value: null,\n    status: null,\n    link: [],\n    // Do not set 'auto' here, otherwise global animation: false\n    // will not effect at this axispointer.\n    animation: null,\n    animationDurationUpdate: 200,\n    lineStyle: {\n      color: '#B9BEC9',\n      width: 1,\n      type: 'dashed'\n    },\n    shadowStyle: {\n      color: 'rgba(210,219,238,0.2)'\n    },\n    label: {\n      show: true,\n      formatter: null,\n      precision: 'auto',\n      margin: 3,\n      color: '#fff',\n      padding: [5, 7, 5, 7],\n      backgroundColor: 'auto',\n      borderColor: null,\n      borderWidth: 0,\n      borderRadius: 3\n    },\n    handle: {\n      show: false,\n      // eslint-disable-next-line\n      icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z',\n      size: 45,\n      // handle margin is from symbol center to axis, which is stable when circular move.\n      margin: 50,\n      // color: '#1b8bbd'\n      // color: '#2f4554'\n      color: '#333',\n      shadowBlur: 3,\n      shadowColor: '#aaa',\n      shadowOffsetX: 0,\n      shadowOffsetY: 2,\n      // For mobile performance\n      throttle: 40\n    }\n  };\n  return AxisPointerModel;\n}(ComponentModel);\n\nvar inner$b = makeInner();\nvar each$7 = each;\n/**\n * @param {string} key\n * @param {module:echarts/ExtensionAPI} api\n * @param {Function} handler\n *      param: {string} currTrigger\n *      param: {Array.<number>} point\n */\n\nfunction register(key, api, handler) {\n  if (env.node) {\n    return;\n  }\n\n  var zr = api.getZr();\n  inner$b(zr).records || (inner$b(zr).records = {});\n  initGlobalListeners(zr, api);\n  var record = inner$b(zr).records[key] || (inner$b(zr).records[key] = {});\n  record.handler = handler;\n}\n\nfunction initGlobalListeners(zr, api) {\n  if (inner$b(zr).initialized) {\n    return;\n  }\n\n  inner$b(zr).initialized = true;\n  useHandler('click', curry(doEnter, 'click'));\n  useHandler('mousemove', curry(doEnter, 'mousemove')); // useHandler('mouseout', onLeave);\n\n  useHandler('globalout', onLeave);\n\n  function useHandler(eventType, cb) {\n    zr.on(eventType, function (e) {\n      var dis = makeDispatchAction(api);\n      each$7(inner$b(zr).records, function (record) {\n        record && cb(record, e, dis.dispatchAction);\n      });\n      dispatchTooltipFinally(dis.pendings, api);\n    });\n  }\n}\n\nfunction dispatchTooltipFinally(pendings, api) {\n  var showLen = pendings.showTip.length;\n  var hideLen = pendings.hideTip.length;\n  var actuallyPayload;\n\n  if (showLen) {\n    actuallyPayload = pendings.showTip[showLen - 1];\n  } else if (hideLen) {\n    actuallyPayload = pendings.hideTip[hideLen - 1];\n  }\n\n  if (actuallyPayload) {\n    actuallyPayload.dispatchAction = null;\n    api.dispatchAction(actuallyPayload);\n  }\n}\n\nfunction onLeave(record, e, dispatchAction) {\n  record.handler('leave', null, dispatchAction);\n}\n\nfunction doEnter(currTrigger, record, e, dispatchAction) {\n  record.handler(currTrigger, e, dispatchAction);\n}\n\nfunction makeDispatchAction(api) {\n  var pendings = {\n    showTip: [],\n    hideTip: []\n  }; // FIXME\n  // better approach?\n  // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,\n  // which may be conflict, (axisPointer call showTip but tooltip call hideTip);\n  // So we have to add \"final stage\" to merge those dispatched actions.\n\n  var dispatchAction = function (payload) {\n    var pendingList = pendings[payload.type];\n\n    if (pendingList) {\n      pendingList.push(payload);\n    } else {\n      payload.dispatchAction = dispatchAction;\n      api.dispatchAction(payload);\n    }\n  };\n\n  return {\n    dispatchAction: dispatchAction,\n    pendings: pendings\n  };\n}\n\nfunction unregister(key, api) {\n  if (env.node) {\n    return;\n  }\n\n  var zr = api.getZr();\n  var record = (inner$b(zr).records || {})[key];\n\n  if (record) {\n    inner$b(zr).records[key] = null;\n  }\n}\n\nvar AxisPointerView =\n/** @class */\nfunction (_super) {\n  __extends(AxisPointerView, _super);\n\n  function AxisPointerView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = AxisPointerView.type;\n    return _this;\n  }\n\n  AxisPointerView.prototype.render = function (globalAxisPointerModel, ecModel, api) {\n    var globalTooltipModel = ecModel.getComponent('tooltip');\n    var triggerOn = globalAxisPointerModel.get('triggerOn') || globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click'; // Register global listener in AxisPointerView to enable\n    // AxisPointerView to be independent to Tooltip.\n\n    register('axisPointer', api, function (currTrigger, e, dispatchAction) {\n      // If 'none', it is not controlled by mouse totally.\n      if (triggerOn !== 'none' && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)) {\n        dispatchAction({\n          type: 'updateAxisPointer',\n          currTrigger: currTrigger,\n          x: e && e.offsetX,\n          y: e && e.offsetY\n        });\n      }\n    });\n  };\n\n  AxisPointerView.prototype.remove = function (ecModel, api) {\n    unregister('axisPointer', api);\n  };\n\n  AxisPointerView.prototype.dispose = function (ecModel, api) {\n    unregister('axisPointer', api);\n  };\n\n  AxisPointerView.type = 'axisPointer';\n  return AxisPointerView;\n}(ComponentView);\n\n/**\n * @param finder contains {seriesIndex, dataIndex, dataIndexInside}\n * @param ecModel\n * @return  {point: [x, y], el: ...} point Will not be null.\n */\n\nfunction findPointFromSeries(finder, ecModel) {\n  var point = [];\n  var seriesIndex = finder.seriesIndex;\n  var seriesModel;\n\n  if (seriesIndex == null || !(seriesModel = ecModel.getSeriesByIndex(seriesIndex))) {\n    return {\n      point: []\n    };\n  }\n\n  var data = seriesModel.getData();\n  var dataIndex = queryDataIndex(data, finder);\n\n  if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) {\n    return {\n      point: []\n    };\n  }\n\n  var el = data.getItemGraphicEl(dataIndex);\n  var coordSys = seriesModel.coordinateSystem;\n\n  if (seriesModel.getTooltipPosition) {\n    point = seriesModel.getTooltipPosition(dataIndex) || [];\n  } else if (coordSys && coordSys.dataToPoint) {\n    if (finder.isStacked) {\n      var baseAxis = coordSys.getBaseAxis();\n      var valueAxis = coordSys.getOtherAxis(baseAxis);\n      var valueAxisDim = valueAxis.dim;\n      var baseAxisDim = baseAxis.dim;\n      var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;\n      var baseDim = data.mapDimension(baseAxisDim);\n      var stackedData = [];\n      stackedData[baseDataOffset] = data.get(baseDim, dataIndex);\n      stackedData[1 - baseDataOffset] = data.get(data.getCalculationInfo('stackResultDimension'), dataIndex);\n      point = coordSys.dataToPoint(stackedData) || [];\n    } else {\n      point = coordSys.dataToPoint(data.getValues(map(coordSys.dimensions, function (dim) {\n        return data.mapDimension(dim);\n      }), dataIndex)) || [];\n    }\n  } else if (el) {\n    // Use graphic bounding rect\n    var rect = el.getBoundingRect().clone();\n    rect.applyTransform(el.transform);\n    point = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n  }\n\n  return {\n    point: point,\n    el: el\n  };\n}\n\nvar inner$c = makeInner();\n/**\n * Basic logic: check all axis, if they do not demand show/highlight,\n * then hide/downplay them.\n *\n * @return content of event obj for echarts.connect.\n */\n\nfunction axisTrigger(payload, ecModel, api) {\n  var currTrigger = payload.currTrigger;\n  var point = [payload.x, payload.y];\n  var finder = payload;\n  var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);\n  var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; // Pending\n  // See #6121. But we are not able to reproduce it yet.\n\n  if (!coordSysAxesInfo) {\n    return;\n  }\n\n  if (illegalPoint(point)) {\n    // Used in the default behavior of `connection`: use the sample seriesIndex\n    // and dataIndex. And also used in the tooltipView trigger.\n    point = findPointFromSeries({\n      seriesIndex: finder.seriesIndex,\n      // Do not use dataIndexInside from other ec instance.\n      // FIXME: auto detect it?\n      dataIndex: finder.dataIndex\n    }, ecModel).point;\n  }\n\n  var isIllegalPoint = illegalPoint(point); // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).\n  // Notice: In this case, it is difficult to get the `point` (which is necessary to show\n  // tooltip, so if point is not given, we just use the point found by sample seriesIndex\n  // and dataIndex.\n\n  var inputAxesInfo = finder.axesInfo;\n  var axesInfo = coordSysAxesInfo.axesInfo;\n  var shouldHide = currTrigger === 'leave' || illegalPoint(point);\n  var outputPayload = {};\n  var showValueMap = {};\n  var dataByCoordSys = {\n    list: [],\n    map: {}\n  };\n  var updaters = {\n    showPointer: curry(showPointer, showValueMap),\n    showTooltip: curry(showTooltip, dataByCoordSys)\n  }; // Process for triggered axes.\n\n  each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {\n    // If a point given, it must be contained by the coordinate system.\n    var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);\n    each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {\n      var axis = axisInfo.axis;\n      var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); // If no inputAxesInfo, no axis is restricted.\n\n      if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {\n        var val = inputAxisInfo && inputAxisInfo.value;\n\n        if (val == null && !isIllegalPoint) {\n          val = axis.pointToData(point);\n        }\n\n        val != null && processOnAxis(axisInfo, val, updaters, false, outputPayload);\n      }\n    });\n  }); // Process for linked axes.\n\n  var linkTriggers = {};\n  each(axesInfo, function (tarAxisInfo, tarKey) {\n    var linkGroup = tarAxisInfo.linkGroup; // If axis has been triggered in the previous stage, it should not be triggered by link.\n\n    if (linkGroup && !showValueMap[tarKey]) {\n      each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {\n        var srcValItem = showValueMap[srcKey]; // If srcValItem exist, source axis is triggered, so link to target axis.\n\n        if (srcAxisInfo !== tarAxisInfo && srcValItem) {\n          var val = srcValItem.value;\n          linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo))));\n          linkTriggers[tarAxisInfo.key] = val;\n        }\n      });\n    }\n  });\n  each(linkTriggers, function (val, tarKey) {\n    processOnAxis(axesInfo[tarKey], val, updaters, true, outputPayload);\n  });\n  updateModelActually(showValueMap, axesInfo, outputPayload);\n  dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);\n  dispatchHighDownActually(axesInfo, dispatchAction, api);\n  return outputPayload;\n}\n\nfunction processOnAxis(axisInfo, newValue, updaters, noSnap, outputFinder) {\n  var axis = axisInfo.axis;\n\n  if (axis.scale.isBlank() || !axis.containData(newValue)) {\n    return;\n  }\n\n  if (!axisInfo.involveSeries) {\n    updaters.showPointer(axisInfo, newValue);\n    return;\n  } // Heavy calculation. So put it after axis.containData checking.\n\n\n  var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);\n  var payloadBatch = payloadInfo.payloadBatch;\n  var snapToValue = payloadInfo.snapToValue; // Fill content of event obj for echarts.connect.\n  // By default use the first involved series data as a sample to connect.\n\n  if (payloadBatch[0] && outputFinder.seriesIndex == null) {\n    extend(outputFinder, payloadBatch[0]);\n  } // If no linkSource input, this process is for collecting link\n  // target, where snap should not be accepted.\n\n\n  if (!noSnap && axisInfo.snap) {\n    if (axis.containData(snapToValue) && snapToValue != null) {\n      newValue = snapToValue;\n    }\n  }\n\n  updaters.showPointer(axisInfo, newValue, payloadBatch); // Tooltip should always be snapToValue, otherwise there will be\n  // incorrect \"axis value ~ series value\" mapping displayed in tooltip.\n\n  updaters.showTooltip(axisInfo, payloadInfo, snapToValue);\n}\n\nfunction buildPayloadsBySeries(value, axisInfo) {\n  var axis = axisInfo.axis;\n  var dim = axis.dim;\n  var snapToValue = value;\n  var payloadBatch = [];\n  var minDist = Number.MAX_VALUE;\n  var minDiff = -1;\n  each(axisInfo.seriesModels, function (series, idx) {\n    var dataDim = series.getData().mapDimensionsAll(dim);\n    var seriesNestestValue;\n    var dataIndices;\n\n    if (series.getAxisTooltipData) {\n      var result = series.getAxisTooltipData(dataDim, value, axis);\n      dataIndices = result.dataIndices;\n      seriesNestestValue = result.nestestValue;\n    } else {\n      dataIndices = series.getData().indicesOfNearest(dataDim[0], value, // Add a threshold to avoid find the wrong dataIndex\n      // when data length is not same.\n      // false,\n      axis.type === 'category' ? 0.5 : null);\n\n      if (!dataIndices.length) {\n        return;\n      }\n\n      seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);\n    }\n\n    if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {\n      return;\n    }\n\n    var diff = value - seriesNestestValue;\n    var dist = Math.abs(diff); // Consider category case\n\n    if (dist <= minDist) {\n      if (dist < minDist || diff >= 0 && minDiff < 0) {\n        minDist = dist;\n        minDiff = diff;\n        snapToValue = seriesNestestValue;\n        payloadBatch.length = 0;\n      }\n\n      each(dataIndices, function (dataIndex) {\n        payloadBatch.push({\n          seriesIndex: series.seriesIndex,\n          dataIndexInside: dataIndex,\n          dataIndex: series.getData().getRawIndex(dataIndex)\n        });\n      });\n    }\n  });\n  return {\n    payloadBatch: payloadBatch,\n    snapToValue: snapToValue\n  };\n}\n\nfunction showPointer(showValueMap, axisInfo, value, payloadBatch) {\n  showValueMap[axisInfo.key] = {\n    value: value,\n    payloadBatch: payloadBatch\n  };\n}\n\nfunction showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {\n  var payloadBatch = payloadInfo.payloadBatch;\n  var axis = axisInfo.axis;\n  var axisModel = axis.model;\n  var axisPointerModel = axisInfo.axisPointerModel; // If no data, do not create anything in dataByCoordSys,\n  // whose length will be used to judge whether dispatch action.\n\n  if (!axisInfo.triggerTooltip || !payloadBatch.length) {\n    return;\n  }\n\n  var coordSysModel = axisInfo.coordSys.model;\n  var coordSysKey = makeKey(coordSysModel);\n  var coordSysItem = dataByCoordSys.map[coordSysKey];\n\n  if (!coordSysItem) {\n    coordSysItem = dataByCoordSys.map[coordSysKey] = {\n      coordSysId: coordSysModel.id,\n      coordSysIndex: coordSysModel.componentIndex,\n      coordSysType: coordSysModel.type,\n      coordSysMainType: coordSysModel.mainType,\n      dataByAxis: []\n    };\n    dataByCoordSys.list.push(coordSysItem);\n  }\n\n  coordSysItem.dataByAxis.push({\n    axisDim: axis.dim,\n    axisIndex: axisModel.componentIndex,\n    axisType: axisModel.type,\n    axisId: axisModel.id,\n    value: value,\n    // Caustion: viewHelper.getValueLabel is actually on \"view stage\", which\n    // depends that all models have been updated. So it should not be performed\n    // here. Considering axisPointerModel used here is volatile, which is hard\n    // to be retrieve in TooltipView, we prepare parameters here.\n    valueLabelOpt: {\n      precision: axisPointerModel.get(['label', 'precision']),\n      formatter: axisPointerModel.get(['label', 'formatter'])\n    },\n    seriesDataIndices: payloadBatch.slice()\n  });\n}\n\nfunction updateModelActually(showValueMap, axesInfo, outputPayload) {\n  var outputAxesInfo = outputPayload.axesInfo = []; // Basic logic: If no 'show' required, 'hide' this axisPointer.\n\n  each(axesInfo, function (axisInfo, key) {\n    var option = axisInfo.axisPointerModel.option;\n    var valItem = showValueMap[key];\n\n    if (valItem) {\n      !axisInfo.useHandle && (option.status = 'show');\n      option.value = valItem.value; // For label formatter param and highlight.\n\n      option.seriesDataIndices = (valItem.payloadBatch || []).slice();\n    } // When always show (e.g., handle used), remain\n    // original value and status.\n    else {\n        // If hide, value still need to be set, consider\n        // click legend to toggle axis blank.\n        !axisInfo.useHandle && (option.status = 'hide');\n      } // If status is 'hide', should be no info in payload.\n\n\n    option.status === 'show' && outputAxesInfo.push({\n      axisDim: axisInfo.axis.dim,\n      axisIndex: axisInfo.axis.model.componentIndex,\n      value: option.value\n    });\n  });\n}\n\nfunction dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {\n  // Basic logic: If no showTip required, hideTip will be dispatched.\n  if (illegalPoint(point) || !dataByCoordSys.list.length) {\n    dispatchAction({\n      type: 'hideTip'\n    });\n    return;\n  } // In most case only one axis (or event one series is used). It is\n  // convinient to fetch payload.seriesIndex and payload.dataIndex\n  // dirtectly. So put the first seriesIndex and dataIndex of the first\n  // axis on the payload.\n\n\n  var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};\n  dispatchAction({\n    type: 'showTip',\n    escapeConnect: true,\n    x: point[0],\n    y: point[1],\n    tooltipOption: payload.tooltipOption,\n    position: payload.position,\n    dataIndexInside: sampleItem.dataIndexInside,\n    dataIndex: sampleItem.dataIndex,\n    seriesIndex: sampleItem.seriesIndex,\n    dataByCoordSys: dataByCoordSys.list\n  });\n}\n\nfunction dispatchHighDownActually(axesInfo, dispatchAction, api) {\n  // FIXME\n  // highlight status modification shoule be a stage of main process?\n  // (Consider confilct (e.g., legend and axisPointer) and setOption)\n  var zr = api.getZr();\n  var highDownKey = 'axisPointerLastHighlights';\n  var lastHighlights = inner$c(zr)[highDownKey] || {};\n  var newHighlights = inner$c(zr)[highDownKey] = {}; // Update highlight/downplay status according to axisPointer model.\n  // Build hash map and remove duplicate incidentally.\n\n  each(axesInfo, function (axisInfo, key) {\n    var option = axisInfo.axisPointerModel.option;\n    option.status === 'show' && each(option.seriesDataIndices, function (batchItem) {\n      var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;\n      newHighlights[key] = batchItem;\n    });\n  }); // Diff.\n\n  var toHighlight = [];\n  var toDownplay = [];\n  each(lastHighlights, function (batchItem, key) {\n    !newHighlights[key] && toDownplay.push(batchItem);\n  });\n  each(newHighlights, function (batchItem, key) {\n    !lastHighlights[key] && toHighlight.push(batchItem);\n  });\n  toDownplay.length && api.dispatchAction({\n    type: 'downplay',\n    escapeConnect: true,\n    // Not blur others when highlight in axisPointer.\n    notBlur: true,\n    batch: toDownplay\n  });\n  toHighlight.length && api.dispatchAction({\n    type: 'highlight',\n    escapeConnect: true,\n    // Not blur others when highlight in axisPointer.\n    notBlur: true,\n    batch: toHighlight\n  });\n}\n\nfunction findInputAxisInfo(inputAxesInfo, axisInfo) {\n  for (var i = 0; i < (inputAxesInfo || []).length; i++) {\n    var inputAxisInfo = inputAxesInfo[i];\n\n    if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) {\n      return inputAxisInfo;\n    }\n  }\n}\n\nfunction makeMapperParam(axisInfo) {\n  var axisModel = axisInfo.axis.model;\n  var item = {};\n  var dim = item.axisDim = axisInfo.axis.dim;\n  item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;\n  item.axisName = item[dim + 'AxisName'] = axisModel.name;\n  item.axisId = item[dim + 'AxisId'] = axisModel.id;\n  return item;\n}\n\nfunction illegalPoint(point) {\n  return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);\n}\n\nfunction install$s(registers) {\n  // CartesianAxisPointer is not supposed to be required here. But consider\n  // echarts.simple.js and online build tooltip, which only require gridSimple,\n  // CartesianAxisPointer should be able to required somewhere.\n  AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer);\n  registers.registerComponentModel(AxisPointerModel);\n  registers.registerComponentView(AxisPointerView);\n  registers.registerPreprocessor(function (option) {\n    // Always has a global axisPointerModel for default setting.\n    if (option) {\n      (!option.axisPointer || option.axisPointer.length === 0) && (option.axisPointer = {});\n      var link = option.axisPointer.link; // Normalize to array to avoid object mergin. But if link\n      // is not set, remain null/undefined, otherwise it will\n      // override existent link setting.\n\n      if (link && !isArray(link)) {\n        option.axisPointer.link = [link];\n      }\n    }\n  }); // This process should proformed after coordinate systems created\n  // and series data processed. So put it on statistic processing stage.\n\n  registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) {\n    // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.\n    // allAxesInfo should be updated when setOption performed.\n    ecModel.getComponent('axisPointer').coordSysAxesInfo = collect(ecModel, api);\n  }); // Broadcast to all views.\n\n  registers.registerAction({\n    type: 'updateAxisPointer',\n    event: 'updateAxisPointer',\n    update: ':updateAxisPointer'\n  }, axisTrigger);\n}\n\nfunction install$t(registers) {\n  use(install$5);\n  use(install$s);\n}\n\nvar PolarAxisPointer =\n/** @class */\nfunction (_super) {\n  __extends(PolarAxisPointer, _super);\n\n  function PolarAxisPointer() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n  /**\n   * @override\n   */\n\n\n  PolarAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {\n    var axis = axisModel.axis;\n\n    if (axis.dim === 'angle') {\n      this.animationThreshold = Math.PI / 18;\n    }\n\n    var polar = axis.polar;\n    var otherAxis = polar.getOtherAxis(axis);\n    var otherExtent = otherAxis.getExtent();\n    var coordValue = axis.dataToCoord(value);\n    var axisPointerType = axisPointerModel.get('type');\n\n    if (axisPointerType && axisPointerType !== 'none') {\n      var elStyle = buildElStyle(axisPointerModel);\n      var pointerOption = pointerShapeBuilder$1[axisPointerType](axis, polar, coordValue, otherExtent);\n      pointerOption.style = elStyle;\n      elOption.graphicKey = pointerOption.type;\n      elOption.pointer = pointerOption;\n    }\n\n    var labelMargin = axisPointerModel.get(['label', 'margin']);\n    var labelPos = getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin);\n    buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos);\n  };\n\n  return PolarAxisPointer;\n}(BaseAxisPointer);\n\nfunction getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin) {\n  var axis = axisModel.axis;\n  var coord = axis.dataToCoord(value);\n  var axisAngle = polar.getAngleAxis().getExtent()[0];\n  axisAngle = axisAngle / 180 * Math.PI;\n  var radiusExtent = polar.getRadiusAxis().getExtent();\n  var position;\n  var align;\n  var verticalAlign;\n\n  if (axis.dim === 'radius') {\n    var transform = create$1();\n    rotate(transform, transform, axisAngle);\n    translate(transform, transform, [polar.cx, polar.cy]);\n    position = applyTransform$1([coord, -labelMargin], transform);\n    var labelRotation = axisModel.getModel('axisLabel').get('rotate') || 0; // @ts-ignore\n\n    var labelLayout = AxisBuilder.innerTextLayout(axisAngle, labelRotation * Math.PI / 180, -1);\n    align = labelLayout.textAlign;\n    verticalAlign = labelLayout.textVerticalAlign;\n  } else {\n    // angle axis\n    var r = radiusExtent[1];\n    position = polar.coordToPoint([r + labelMargin, coord]);\n    var cx = polar.cx;\n    var cy = polar.cy;\n    align = Math.abs(position[0] - cx) / r < 0.3 ? 'center' : position[0] > cx ? 'left' : 'right';\n    verticalAlign = Math.abs(position[1] - cy) / r < 0.3 ? 'middle' : position[1] > cy ? 'top' : 'bottom';\n  }\n\n  return {\n    position: position,\n    align: align,\n    verticalAlign: verticalAlign\n  };\n}\n\nvar pointerShapeBuilder$1 = {\n  line: function (axis, polar, coordValue, otherExtent) {\n    return axis.dim === 'angle' ? {\n      type: 'Line',\n      shape: makeLineShape(polar.coordToPoint([otherExtent[0], coordValue]), polar.coordToPoint([otherExtent[1], coordValue]))\n    } : {\n      type: 'Circle',\n      shape: {\n        cx: polar.cx,\n        cy: polar.cy,\n        r: coordValue\n      }\n    };\n  },\n  shadow: function (axis, polar, coordValue, otherExtent) {\n    var bandWidth = Math.max(1, axis.getBandWidth());\n    var radian = Math.PI / 180;\n    return axis.dim === 'angle' ? {\n      type: 'Sector',\n      shape: makeSectorShape(polar.cx, polar.cy, otherExtent[0], otherExtent[1], // In ECharts y is negative if angle is positive\n      (-coordValue - bandWidth / 2) * radian, (-coordValue + bandWidth / 2) * radian)\n    } : {\n      type: 'Sector',\n      shape: makeSectorShape(polar.cx, polar.cy, coordValue - bandWidth / 2, coordValue + bandWidth / 2, 0, Math.PI * 2)\n    };\n  }\n};\n\nvar PolarModel =\n/** @class */\nfunction (_super) {\n  __extends(PolarModel, _super);\n\n  function PolarModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = PolarModel.type;\n    return _this;\n  }\n\n  PolarModel.prototype.findAxisModel = function (axisType) {\n    var foundAxisModel;\n    var ecModel = this.ecModel;\n    ecModel.eachComponent(axisType, function (axisModel) {\n      if (axisModel.getCoordSysModel() === this) {\n        foundAxisModel = axisModel;\n      }\n    }, this);\n    return foundAxisModel;\n  };\n\n  PolarModel.type = 'polar';\n  PolarModel.dependencies = ['radiusAxis', 'angleAxis'];\n  PolarModel.defaultOption = {\n    // zlevel: 0,\n    z: 0,\n    center: ['50%', '50%'],\n    radius: '80%'\n  };\n  return PolarModel;\n}(ComponentModel);\n\nvar PolarAxisModel =\n/** @class */\nfunction (_super) {\n  __extends(PolarAxisModel, _super);\n\n  function PolarAxisModel() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  PolarAxisModel.prototype.getCoordSysModel = function () {\n    return this.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n  };\n\n  PolarAxisModel.type = 'polarAxis';\n  return PolarAxisModel;\n}(ComponentModel);\n\nmixin(PolarAxisModel, AxisModelCommonMixin);\n\nvar AngleAxisModel =\n/** @class */\nfunction (_super) {\n  __extends(AngleAxisModel, _super);\n\n  function AngleAxisModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = AngleAxisModel.type;\n    return _this;\n  }\n\n  AngleAxisModel.type = 'angleAxis';\n  return AngleAxisModel;\n}(PolarAxisModel);\n\nvar RadiusAxisModel =\n/** @class */\nfunction (_super) {\n  __extends(RadiusAxisModel, _super);\n\n  function RadiusAxisModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = RadiusAxisModel.type;\n    return _this;\n  }\n\n  RadiusAxisModel.type = 'radiusAxis';\n  return RadiusAxisModel;\n}(PolarAxisModel);\n\nvar RadiusAxis =\n/** @class */\nfunction (_super) {\n  __extends(RadiusAxis, _super);\n\n  function RadiusAxis(scale, radiusExtent) {\n    return _super.call(this, 'radius', scale, radiusExtent) || this;\n  }\n\n  RadiusAxis.prototype.pointToData = function (point, clamp) {\n    return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1];\n  };\n\n  return RadiusAxis;\n}(Axis);\n\nRadiusAxis.prototype.dataToRadius = Axis.prototype.dataToCoord;\nRadiusAxis.prototype.radiusToData = Axis.prototype.coordToData;\n\nvar inner$d = makeInner();\n\nvar AngleAxis =\n/** @class */\nfunction (_super) {\n  __extends(AngleAxis, _super);\n\n  function AngleAxis(scale, angleExtent) {\n    return _super.call(this, 'angle', scale, angleExtent || [0, 360]) || this;\n  }\n\n  AngleAxis.prototype.pointToData = function (point, clamp) {\n    return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1];\n  };\n  /**\n   * Only be called in category axis.\n   * Angle axis uses text height to decide interval\n   *\n   * @override\n   * @return {number} Auto interval for cateogry axis tick and label\n   */\n\n\n  AngleAxis.prototype.calculateCategoryInterval = function () {\n    var axis = this;\n    var labelModel = axis.getLabelModel();\n    var ordinalScale = axis.scale;\n    var ordinalExtent = ordinalScale.getExtent(); // Providing this method is for optimization:\n    // avoid generating a long array by `getTicks`\n    // in large category data case.\n\n    var tickCount = ordinalScale.count();\n\n    if (ordinalExtent[1] - ordinalExtent[0] < 1) {\n      return 0;\n    }\n\n    var tickValue = ordinalExtent[0];\n    var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);\n    var unitH = Math.abs(unitSpan); // Not precise, just use height as text width\n    // and each distance from axis line yet.\n\n    var rect = getBoundingRect(tickValue == null ? '' : tickValue + '', labelModel.getFont(), 'center', 'top');\n    var maxH = Math.max(rect.height, 7);\n    var dh = maxH / unitH; // 0/0 is NaN, 1/0 is Infinity.\n\n    isNaN(dh) && (dh = Infinity);\n    var interval = Math.max(0, Math.floor(dh));\n    var cache = inner$d(axis.model);\n    var lastAutoInterval = cache.lastAutoInterval;\n    var lastTickCount = cache.lastTickCount; // Use cache to keep interval stable while moving zoom window,\n    // otherwise the calculated interval might jitter when the zoom\n    // window size is close to the interval-changing size.\n\n    if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 // Always choose the bigger one, otherwise the critical\n    // point is not the same when zooming in or zooming out.\n    && lastAutoInterval > interval) {\n      interval = lastAutoInterval;\n    } // Only update cache if cache not used, otherwise the\n    // changing of interval is too insensitive.\n    else {\n        cache.lastTickCount = tickCount;\n        cache.lastAutoInterval = interval;\n      }\n\n    return interval;\n  };\n\n  return AngleAxis;\n}(Axis);\n\nAngleAxis.prototype.dataToAngle = Axis.prototype.dataToCoord;\nAngleAxis.prototype.angleToData = Axis.prototype.coordToData;\n\nvar polarDimensions = ['radius', 'angle'];\n\nvar Polar =\n/** @class */\nfunction () {\n  function Polar(name) {\n    this.dimensions = polarDimensions;\n    this.type = 'polar';\n    /**\n     * x of polar center\n     */\n\n    this.cx = 0;\n    /**\n     * y of polar center\n     */\n\n    this.cy = 0;\n    this._radiusAxis = new RadiusAxis();\n    this._angleAxis = new AngleAxis();\n    this.axisPointerEnabled = true;\n    this.name = name || '';\n    this._radiusAxis.polar = this._angleAxis.polar = this;\n  }\n  /**\n   * If contain coord\n   */\n\n\n  Polar.prototype.containPoint = function (point) {\n    var coord = this.pointToCoord(point);\n    return this._radiusAxis.contain(coord[0]) && this._angleAxis.contain(coord[1]);\n  };\n  /**\n   * If contain data\n   */\n\n\n  Polar.prototype.containData = function (data) {\n    return this._radiusAxis.containData(data[0]) && this._angleAxis.containData(data[1]);\n  };\n\n  Polar.prototype.getAxis = function (dim) {\n    var key = '_' + dim + 'Axis';\n    return this[key];\n  };\n\n  Polar.prototype.getAxes = function () {\n    return [this._radiusAxis, this._angleAxis];\n  };\n  /**\n   * Get axes by type of scale\n   */\n\n\n  Polar.prototype.getAxesByScale = function (scaleType) {\n    var axes = [];\n    var angleAxis = this._angleAxis;\n    var radiusAxis = this._radiusAxis;\n    angleAxis.scale.type === scaleType && axes.push(angleAxis);\n    radiusAxis.scale.type === scaleType && axes.push(radiusAxis);\n    return axes;\n  };\n\n  Polar.prototype.getAngleAxis = function () {\n    return this._angleAxis;\n  };\n\n  Polar.prototype.getRadiusAxis = function () {\n    return this._radiusAxis;\n  };\n\n  Polar.prototype.getOtherAxis = function (axis) {\n    var angleAxis = this._angleAxis;\n    return axis === angleAxis ? this._radiusAxis : angleAxis;\n  };\n  /**\n   * Base axis will be used on stacking.\n   *\n   */\n\n\n  Polar.prototype.getBaseAxis = function () {\n    return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAngleAxis();\n  };\n\n  Polar.prototype.getTooltipAxes = function (dim) {\n    var baseAxis = dim != null && dim !== 'auto' ? this.getAxis(dim) : this.getBaseAxis();\n    return {\n      baseAxes: [baseAxis],\n      otherAxes: [this.getOtherAxis(baseAxis)]\n    };\n  };\n  /**\n   * Convert a single data item to (x, y) point.\n   * Parameter data is an array which the first element is radius and the second is angle\n   */\n\n\n  Polar.prototype.dataToPoint = function (data, clamp) {\n    return this.coordToPoint([this._radiusAxis.dataToRadius(data[0], clamp), this._angleAxis.dataToAngle(data[1], clamp)]);\n  };\n  /**\n   * Convert a (x, y) point to data\n   */\n\n\n  Polar.prototype.pointToData = function (point, clamp) {\n    var coord = this.pointToCoord(point);\n    return [this._radiusAxis.radiusToData(coord[0], clamp), this._angleAxis.angleToData(coord[1], clamp)];\n  };\n  /**\n   * Convert a (x, y) point to (radius, angle) coord\n   */\n\n\n  Polar.prototype.pointToCoord = function (point) {\n    var dx = point[0] - this.cx;\n    var dy = point[1] - this.cy;\n    var angleAxis = this.getAngleAxis();\n    var extent = angleAxis.getExtent();\n    var minAngle = Math.min(extent[0], extent[1]);\n    var maxAngle = Math.max(extent[0], extent[1]); // Fix fixed extent in polarCreator\n    // FIXME\n\n    angleAxis.inverse ? minAngle = maxAngle - 360 : maxAngle = minAngle + 360;\n    var radius = Math.sqrt(dx * dx + dy * dy);\n    dx /= radius;\n    dy /= radius;\n    var radian = Math.atan2(-dy, dx) / Math.PI * 180; // move to angleExtent\n\n    var dir = radian < minAngle ? 1 : -1;\n\n    while (radian < minAngle || radian > maxAngle) {\n      radian += dir * 360;\n    }\n\n    return [radius, radian];\n  };\n  /**\n   * Convert a (radius, angle) coord to (x, y) point\n   */\n\n\n  Polar.prototype.coordToPoint = function (coord) {\n    var radius = coord[0];\n    var radian = coord[1] / 180 * Math.PI;\n    var x = Math.cos(radian) * radius + this.cx; // Inverse the y\n\n    var y = -Math.sin(radian) * radius + this.cy;\n    return [x, y];\n  };\n  /**\n   * Get ring area of cartesian.\n   * Area will have a contain function to determine if a point is in the coordinate system.\n   */\n\n\n  Polar.prototype.getArea = function () {\n    var angleAxis = this.getAngleAxis();\n    var radiusAxis = this.getRadiusAxis();\n    var radiusExtent = radiusAxis.getExtent().slice();\n    radiusExtent[0] > radiusExtent[1] && radiusExtent.reverse();\n    var angleExtent = angleAxis.getExtent();\n    var RADIAN = Math.PI / 180;\n    return {\n      cx: this.cx,\n      cy: this.cy,\n      r0: radiusExtent[0],\n      r: radiusExtent[1],\n      startAngle: -angleExtent[0] * RADIAN,\n      endAngle: -angleExtent[1] * RADIAN,\n      clockwise: angleAxis.inverse,\n      contain: function (x, y) {\n        // It's a ring shape.\n        // Start angle and end angle don't matter\n        var dx = x - this.cx;\n        var dy = y - this.cy; // minus a tiny value 1e-4 to avoid being clipped unexpectedly\n\n        var d2 = dx * dx + dy * dy - 1e-4;\n        var r = this.r;\n        var r0 = this.r0;\n        return d2 <= r * r && d2 >= r0 * r0;\n      }\n    };\n  };\n\n  Polar.prototype.convertToPixel = function (ecModel, finder, value) {\n    var coordSys = getCoordSys$2(finder);\n    return coordSys === this ? this.dataToPoint(value) : null;\n  };\n\n  Polar.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n    var coordSys = getCoordSys$2(finder);\n    return coordSys === this ? this.pointToData(pixel) : null;\n  };\n\n  return Polar;\n}();\n\nfunction getCoordSys$2(finder) {\n  var seriesModel = finder.seriesModel;\n  var polarModel = finder.polarModel;\n  return polarModel && polarModel.coordinateSystem || seriesModel && seriesModel.coordinateSystem;\n}\n\n/**\n * Resize method bound to the polar\n */\n\nfunction resizePolar(polar, polarModel, api) {\n  var center = polarModel.get('center');\n  var width = api.getWidth();\n  var height = api.getHeight();\n  polar.cx = parsePercent$1(center[0], width);\n  polar.cy = parsePercent$1(center[1], height);\n  var radiusAxis = polar.getRadiusAxis();\n  var size = Math.min(width, height) / 2;\n  var radius = polarModel.get('radius');\n\n  if (radius == null) {\n    radius = [0, '100%'];\n  } else if (!isArray(radius)) {\n    // r0 = 0\n    radius = [0, radius];\n  }\n\n  var parsedRadius = [parsePercent$1(radius[0], size), parsePercent$1(radius[1], size)];\n  radiusAxis.inverse ? radiusAxis.setExtent(parsedRadius[1], parsedRadius[0]) : radiusAxis.setExtent(parsedRadius[0], parsedRadius[1]);\n}\n/**\n * Update polar\n */\n\n\nfunction updatePolarScale(ecModel, api) {\n  var polar = this;\n  var angleAxis = polar.getAngleAxis();\n  var radiusAxis = polar.getRadiusAxis(); // Reset scale\n\n  angleAxis.scale.setExtent(Infinity, -Infinity);\n  radiusAxis.scale.setExtent(Infinity, -Infinity);\n  ecModel.eachSeries(function (seriesModel) {\n    if (seriesModel.coordinateSystem === polar) {\n      var data_1 = seriesModel.getData();\n      each(getDataDimensionsOnAxis(data_1, 'radius'), function (dim) {\n        radiusAxis.scale.unionExtentFromData(data_1, dim);\n      });\n      each(getDataDimensionsOnAxis(data_1, 'angle'), function (dim) {\n        angleAxis.scale.unionExtentFromData(data_1, dim);\n      });\n    }\n  });\n  niceScaleExtent(angleAxis.scale, angleAxis.model);\n  niceScaleExtent(radiusAxis.scale, radiusAxis.model); // Fix extent of category angle axis\n\n  if (angleAxis.type === 'category' && !angleAxis.onBand) {\n    var extent = angleAxis.getExtent();\n    var diff = 360 / angleAxis.scale.count();\n    angleAxis.inverse ? extent[1] += diff : extent[1] -= diff;\n    angleAxis.setExtent(extent[0], extent[1]);\n  }\n}\n\nfunction isAngleAxisModel(axisModel) {\n  return axisModel.mainType === 'angleAxis';\n}\n/**\n * Set common axis properties\n */\n\n\nfunction setAxis(axis, axisModel) {\n  axis.type = axisModel.get('type');\n  axis.scale = createScaleByModel(axisModel);\n  axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category';\n  axis.inverse = axisModel.get('inverse');\n\n  if (isAngleAxisModel(axisModel)) {\n    axis.inverse = axis.inverse !== axisModel.get('clockwise');\n    var startAngle = axisModel.get('startAngle');\n    axis.setExtent(startAngle, startAngle + (axis.inverse ? -360 : 360));\n  } // Inject axis instance\n\n\n  axisModel.axis = axis;\n  axis.model = axisModel;\n}\n\nvar polarCreator = {\n  dimensions: polarDimensions,\n  create: function (ecModel, api) {\n    var polarList = [];\n    ecModel.eachComponent('polar', function (polarModel, idx) {\n      var polar = new Polar(idx + ''); // Inject resize and update method\n\n      polar.update = updatePolarScale;\n      var radiusAxis = polar.getRadiusAxis();\n      var angleAxis = polar.getAngleAxis();\n      var radiusAxisModel = polarModel.findAxisModel('radiusAxis');\n      var angleAxisModel = polarModel.findAxisModel('angleAxis');\n      setAxis(radiusAxis, radiusAxisModel);\n      setAxis(angleAxis, angleAxisModel);\n      resizePolar(polar, polarModel, api);\n      polarList.push(polar);\n      polarModel.coordinateSystem = polar;\n      polar.model = polarModel;\n    }); // Inject coordinateSystem to series\n\n    ecModel.eachSeries(function (seriesModel) {\n      if (seriesModel.get('coordinateSystem') === 'polar') {\n        var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!polarModel) {\n            throw new Error('Polar \"' + retrieve(seriesModel.get('polarIndex'), seriesModel.get('polarId'), 0) + '\" not found');\n          }\n        }\n\n        seriesModel.coordinateSystem = polarModel.coordinateSystem;\n      }\n    });\n    return polarList;\n  }\n};\n\nvar elementList$1 = ['axisLine', 'axisLabel', 'axisTick', 'minorTick', 'splitLine', 'minorSplitLine', 'splitArea'];\n\nfunction getAxisLineShape(polar, rExtent, angle) {\n  rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse());\n  var start = polar.coordToPoint([rExtent[0], angle]);\n  var end = polar.coordToPoint([rExtent[1], angle]);\n  return {\n    x1: start[0],\n    y1: start[1],\n    x2: end[0],\n    y2: end[1]\n  };\n}\n\nfunction getRadiusIdx(polar) {\n  var radiusAxis = polar.getRadiusAxis();\n  return radiusAxis.inverse ? 0 : 1;\n} // Remove the last tick which will overlap the first tick\n\n\nfunction fixAngleOverlap(list) {\n  var firstItem = list[0];\n  var lastItem = list[list.length - 1];\n\n  if (firstItem && lastItem && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4) {\n    list.pop();\n  }\n}\n\nvar AngleAxisView =\n/** @class */\nfunction (_super) {\n  __extends(AngleAxisView, _super);\n\n  function AngleAxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = AngleAxisView.type;\n    _this.axisPointerClass = 'PolarAxisPointer';\n    return _this;\n  }\n\n  AngleAxisView.prototype.render = function (angleAxisModel, ecModel) {\n    this.group.removeAll();\n\n    if (!angleAxisModel.get('show')) {\n      return;\n    }\n\n    var angleAxis = angleAxisModel.axis;\n    var polar = angleAxis.polar;\n    var radiusExtent = polar.getRadiusAxis().getExtent();\n    var ticksAngles = angleAxis.getTicksCoords();\n    var minorTickAngles = angleAxis.getMinorTicksCoords();\n    var labels = map(angleAxis.getViewLabels(), function (labelItem) {\n      labelItem = clone(labelItem);\n      var scale = angleAxis.scale;\n      var tickValue = scale.type === 'ordinal' ? scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue;\n      labelItem.coord = angleAxis.dataToCoord(tickValue);\n      return labelItem;\n    });\n    fixAngleOverlap(labels);\n    fixAngleOverlap(ticksAngles);\n    each(elementList$1, function (name) {\n      if (angleAxisModel.get([name, 'show']) && (!angleAxis.scale.isBlank() || name === 'axisLine')) {\n        angelAxisElementsBuilders[name](this.group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels);\n      }\n    }, this);\n  };\n\n  AngleAxisView.type = 'angleAxis';\n  return AngleAxisView;\n}(AxisView);\n\nvar angelAxisElementsBuilders = {\n  axisLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n    var lineStyleModel = angleAxisModel.getModel(['axisLine', 'lineStyle']); // extent id of the axis radius (r0 and r)\n\n    var rId = getRadiusIdx(polar);\n    var r0Id = rId ? 0 : 1;\n    var shape;\n\n    if (radiusExtent[r0Id] === 0) {\n      shape = new Circle({\n        shape: {\n          cx: polar.cx,\n          cy: polar.cy,\n          r: radiusExtent[rId]\n        },\n        style: lineStyleModel.getLineStyle(),\n        z2: 1,\n        silent: true\n      });\n    } else {\n      shape = new Ring({\n        shape: {\n          cx: polar.cx,\n          cy: polar.cy,\n          r: radiusExtent[rId],\n          r0: radiusExtent[r0Id]\n        },\n        style: lineStyleModel.getLineStyle(),\n        z2: 1,\n        silent: true\n      });\n    }\n\n    shape.style.fill = null;\n    group.add(shape);\n  },\n  axisTick: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n    var tickModel = angleAxisModel.getModel('axisTick');\n    var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length');\n    var radius = radiusExtent[getRadiusIdx(polar)];\n    var lines = map(ticksAngles, function (tickAngleItem) {\n      return new Line({\n        shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord)\n      });\n    });\n    group.add(mergePath$1(lines, {\n      style: defaults(tickModel.getModel('lineStyle').getLineStyle(), {\n        stroke: angleAxisModel.get(['axisLine', 'lineStyle', 'color'])\n      })\n    }));\n  },\n  minorTick: function (group, angleAxisModel, polar, tickAngles, minorTickAngles, radiusExtent) {\n    if (!minorTickAngles.length) {\n      return;\n    }\n\n    var tickModel = angleAxisModel.getModel('axisTick');\n    var minorTickModel = angleAxisModel.getModel('minorTick');\n    var tickLen = (tickModel.get('inside') ? -1 : 1) * minorTickModel.get('length');\n    var radius = radiusExtent[getRadiusIdx(polar)];\n    var lines = [];\n\n    for (var i = 0; i < minorTickAngles.length; i++) {\n      for (var k = 0; k < minorTickAngles[i].length; k++) {\n        lines.push(new Line({\n          shape: getAxisLineShape(polar, [radius, radius + tickLen], minorTickAngles[i][k].coord)\n        }));\n      }\n    }\n\n    group.add(mergePath$1(lines, {\n      style: defaults(minorTickModel.getModel('lineStyle').getLineStyle(), defaults(tickModel.getLineStyle(), {\n        stroke: angleAxisModel.get(['axisLine', 'lineStyle', 'color'])\n      }))\n    }));\n  },\n  axisLabel: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels) {\n    var rawCategoryData = angleAxisModel.getCategories(true);\n    var commonLabelModel = angleAxisModel.getModel('axisLabel');\n    var labelMargin = commonLabelModel.get('margin');\n    var triggerEvent = angleAxisModel.get('triggerEvent'); // Use length of ticksAngles because it may remove the last tick to avoid overlapping\n\n    each(labels, function (labelItem, idx) {\n      var labelModel = commonLabelModel;\n      var tickValue = labelItem.tickValue;\n      var r = radiusExtent[getRadiusIdx(polar)];\n      var p = polar.coordToPoint([r + labelMargin, labelItem.coord]);\n      var cx = polar.cx;\n      var cy = polar.cy;\n      var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3 ? 'center' : p[0] > cx ? 'left' : 'right';\n      var labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3 ? 'middle' : p[1] > cy ? 'top' : 'bottom';\n\n      if (rawCategoryData && rawCategoryData[tickValue]) {\n        var rawCategoryItem = rawCategoryData[tickValue];\n\n        if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) {\n          labelModel = new Model(rawCategoryItem.textStyle, commonLabelModel, commonLabelModel.ecModel);\n        }\n      }\n\n      var textEl = new ZRText({\n        silent: AxisBuilder.isLabelSilent(angleAxisModel),\n        style: createTextStyle(labelModel, {\n          x: p[0],\n          y: p[1],\n          fill: labelModel.getTextColor() || angleAxisModel.get(['axisLine', 'lineStyle', 'color']),\n          text: labelItem.formattedLabel,\n          align: labelTextAlign,\n          verticalAlign: labelTextVerticalAlign\n        })\n      });\n      group.add(textEl); // Pack data for mouse event\n\n      if (triggerEvent) {\n        var eventData = AxisBuilder.makeAxisEventDataBase(angleAxisModel);\n        eventData.targetType = 'axisLabel';\n        eventData.value = labelItem.rawLabel;\n        getECData(textEl).eventData = eventData;\n      }\n    }, this);\n  },\n  splitLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n    var splitLineModel = angleAxisModel.getModel('splitLine');\n    var lineStyleModel = splitLineModel.getModel('lineStyle');\n    var lineColors = lineStyleModel.get('color');\n    var lineCount = 0;\n    lineColors = lineColors instanceof Array ? lineColors : [lineColors];\n    var splitLines = [];\n\n    for (var i = 0; i < ticksAngles.length; i++) {\n      var colorIndex = lineCount++ % lineColors.length;\n      splitLines[colorIndex] = splitLines[colorIndex] || [];\n      splitLines[colorIndex].push(new Line({\n        shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord)\n      }));\n    } // Simple optimization\n    // Batching the lines if color are the same\n\n\n    for (var i = 0; i < splitLines.length; i++) {\n      group.add(mergePath$1(splitLines[i], {\n        style: defaults({\n          stroke: lineColors[i % lineColors.length]\n        }, lineStyleModel.getLineStyle()),\n        silent: true,\n        z: angleAxisModel.get('z')\n      }));\n    }\n  },\n  minorSplitLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n    if (!minorTickAngles.length) {\n      return;\n    }\n\n    var minorSplitLineModel = angleAxisModel.getModel('minorSplitLine');\n    var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n    var lines = [];\n\n    for (var i = 0; i < minorTickAngles.length; i++) {\n      for (var k = 0; k < minorTickAngles[i].length; k++) {\n        lines.push(new Line({\n          shape: getAxisLineShape(polar, radiusExtent, minorTickAngles[i][k].coord)\n        }));\n      }\n    }\n\n    group.add(mergePath$1(lines, {\n      style: lineStyleModel.getLineStyle(),\n      silent: true,\n      z: angleAxisModel.get('z')\n    }));\n  },\n  splitArea: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n    if (!ticksAngles.length) {\n      return;\n    }\n\n    var splitAreaModel = angleAxisModel.getModel('splitArea');\n    var areaStyleModel = splitAreaModel.getModel('areaStyle');\n    var areaColors = areaStyleModel.get('color');\n    var lineCount = 0;\n    areaColors = areaColors instanceof Array ? areaColors : [areaColors];\n    var splitAreas = [];\n    var RADIAN = Math.PI / 180;\n    var prevAngle = -ticksAngles[0].coord * RADIAN;\n    var r0 = Math.min(radiusExtent[0], radiusExtent[1]);\n    var r1 = Math.max(radiusExtent[0], radiusExtent[1]);\n    var clockwise = angleAxisModel.get('clockwise');\n\n    for (var i = 1, len = ticksAngles.length; i <= len; i++) {\n      var coord = i === len ? ticksAngles[0].coord : ticksAngles[i].coord;\n      var colorIndex = lineCount++ % areaColors.length;\n      splitAreas[colorIndex] = splitAreas[colorIndex] || [];\n      splitAreas[colorIndex].push(new Sector({\n        shape: {\n          cx: polar.cx,\n          cy: polar.cy,\n          r0: r0,\n          r: r1,\n          startAngle: prevAngle,\n          endAngle: -coord * RADIAN,\n          clockwise: clockwise\n        },\n        silent: true\n      }));\n      prevAngle = -coord * RADIAN;\n    } // Simple optimization\n    // Batching the lines if color are the same\n\n\n    for (var i = 0; i < splitAreas.length; i++) {\n      group.add(mergePath$1(splitAreas[i], {\n        style: defaults({\n          fill: areaColors[i % areaColors.length]\n        }, areaStyleModel.getAreaStyle()),\n        silent: true\n      }));\n    }\n  }\n};\n\nvar axisBuilderAttrs$2 = ['axisLine', 'axisTickLabel', 'axisName'];\nvar selfBuilderAttrs$1 = ['splitLine', 'splitArea', 'minorSplitLine'];\n\nvar RadiusAxisView =\n/** @class */\nfunction (_super) {\n  __extends(RadiusAxisView, _super);\n\n  function RadiusAxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = RadiusAxisView.type;\n    _this.axisPointerClass = 'PolarAxisPointer';\n    return _this;\n  }\n\n  RadiusAxisView.prototype.render = function (radiusAxisModel, ecModel) {\n    this.group.removeAll();\n\n    if (!radiusAxisModel.get('show')) {\n      return;\n    }\n\n    var oldAxisGroup = this._axisGroup;\n    var newAxisGroup = this._axisGroup = new Group();\n    this.group.add(newAxisGroup);\n    var radiusAxis = radiusAxisModel.axis;\n    var polar = radiusAxis.polar;\n    var angleAxis = polar.getAngleAxis();\n    var ticksCoords = radiusAxis.getTicksCoords();\n    var minorTicksCoords = radiusAxis.getMinorTicksCoords();\n    var axisAngle = angleAxis.getExtent()[0];\n    var radiusExtent = radiusAxis.getExtent();\n    var layout = layoutAxis(polar, radiusAxisModel, axisAngle);\n    var axisBuilder = new AxisBuilder(radiusAxisModel, layout);\n    each(axisBuilderAttrs$2, axisBuilder.add, axisBuilder);\n    newAxisGroup.add(axisBuilder.getGroup());\n    groupTransition(oldAxisGroup, newAxisGroup, radiusAxisModel);\n    each(selfBuilderAttrs$1, function (name) {\n      if (radiusAxisModel.get([name, 'show']) && !radiusAxis.scale.isBlank()) {\n        axisElementBuilders$1[name](this.group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords, minorTicksCoords);\n      }\n    }, this);\n  };\n\n  RadiusAxisView.type = 'radiusAxis';\n  return RadiusAxisView;\n}(AxisView);\n\nvar axisElementBuilders$1 = {\n  splitLine: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) {\n    var splitLineModel = radiusAxisModel.getModel('splitLine');\n    var lineStyleModel = splitLineModel.getModel('lineStyle');\n    var lineColors = lineStyleModel.get('color');\n    var lineCount = 0;\n    lineColors = lineColors instanceof Array ? lineColors : [lineColors];\n    var splitLines = [];\n\n    for (var i = 0; i < ticksCoords.length; i++) {\n      var colorIndex = lineCount++ % lineColors.length;\n      splitLines[colorIndex] = splitLines[colorIndex] || [];\n      splitLines[colorIndex].push(new Circle({\n        shape: {\n          cx: polar.cx,\n          cy: polar.cy,\n          // ensure circle radius >= 0\n          r: Math.max(ticksCoords[i].coord, 0)\n        }\n      }));\n    } // Simple optimization\n    // Batching the lines if color are the same\n\n\n    for (var i = 0; i < splitLines.length; i++) {\n      group.add(mergePath$1(splitLines[i], {\n        style: defaults({\n          stroke: lineColors[i % lineColors.length],\n          fill: null\n        }, lineStyleModel.getLineStyle()),\n        silent: true\n      }));\n    }\n  },\n  minorSplitLine: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords, minorTicksCoords) {\n    if (!minorTicksCoords.length) {\n      return;\n    }\n\n    var minorSplitLineModel = radiusAxisModel.getModel('minorSplitLine');\n    var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n    var lines = [];\n\n    for (var i = 0; i < minorTicksCoords.length; i++) {\n      for (var k = 0; k < minorTicksCoords[i].length; k++) {\n        lines.push(new Circle({\n          shape: {\n            cx: polar.cx,\n            cy: polar.cy,\n            r: minorTicksCoords[i][k].coord\n          }\n        }));\n      }\n    }\n\n    group.add(mergePath$1(lines, {\n      style: defaults({\n        fill: null\n      }, lineStyleModel.getLineStyle()),\n      silent: true\n    }));\n  },\n  splitArea: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) {\n    if (!ticksCoords.length) {\n      return;\n    }\n\n    var splitAreaModel = radiusAxisModel.getModel('splitArea');\n    var areaStyleModel = splitAreaModel.getModel('areaStyle');\n    var areaColors = areaStyleModel.get('color');\n    var lineCount = 0;\n    areaColors = areaColors instanceof Array ? areaColors : [areaColors];\n    var splitAreas = [];\n    var prevRadius = ticksCoords[0].coord;\n\n    for (var i = 1; i < ticksCoords.length; i++) {\n      var colorIndex = lineCount++ % areaColors.length;\n      splitAreas[colorIndex] = splitAreas[colorIndex] || [];\n      splitAreas[colorIndex].push(new Sector({\n        shape: {\n          cx: polar.cx,\n          cy: polar.cy,\n          r0: prevRadius,\n          r: ticksCoords[i].coord,\n          startAngle: 0,\n          endAngle: Math.PI * 2\n        },\n        silent: true\n      }));\n      prevRadius = ticksCoords[i].coord;\n    } // Simple optimization\n    // Batching the lines if color are the same\n\n\n    for (var i = 0; i < splitAreas.length; i++) {\n      group.add(mergePath$1(splitAreas[i], {\n        style: defaults({\n          fill: areaColors[i % areaColors.length]\n        }, areaStyleModel.getAreaStyle()),\n        silent: true\n      }));\n    }\n  }\n};\n/**\n * @inner\n */\n\nfunction layoutAxis(polar, radiusAxisModel, axisAngle) {\n  return {\n    position: [polar.cx, polar.cy],\n    rotation: axisAngle / 180 * Math.PI,\n    labelDirection: -1,\n    tickDirection: -1,\n    nameDirection: 1,\n    labelRotate: radiusAxisModel.getModel('axisLabel').get('rotate'),\n    // Over splitLine and splitArea\n    z2: 1\n  };\n}\n\nfunction getSeriesStackId$1(seriesModel) {\n  return seriesModel.get('stack') || '__ec_stack_' + seriesModel.seriesIndex;\n}\n\nfunction getAxisKey$1(polar, axis) {\n  return axis.dim + polar.model.componentIndex;\n}\n\nfunction barLayoutPolar(seriesType, ecModel, api) {\n  var lastStackCoords = {};\n  var barWidthAndOffset = calRadialBar(filter(ecModel.getSeriesByType(seriesType), function (seriesModel) {\n    return !ecModel.isSeriesFiltered(seriesModel) && seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'polar';\n  }));\n  ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n    // Check series coordinate, do layout for polar only\n    if (seriesModel.coordinateSystem.type !== 'polar') {\n      return;\n    }\n\n    var data = seriesModel.getData();\n    var polar = seriesModel.coordinateSystem;\n    var baseAxis = polar.getBaseAxis();\n    var axisKey = getAxisKey$1(polar, baseAxis);\n    var stackId = getSeriesStackId$1(seriesModel);\n    var columnLayoutInfo = barWidthAndOffset[axisKey][stackId];\n    var columnOffset = columnLayoutInfo.offset;\n    var columnWidth = columnLayoutInfo.width;\n    var valueAxis = polar.getOtherAxis(baseAxis);\n    var cx = seriesModel.coordinateSystem.cx;\n    var cy = seriesModel.coordinateSystem.cy;\n    var barMinHeight = seriesModel.get('barMinHeight') || 0;\n    var barMinAngle = seriesModel.get('barMinAngle') || 0;\n    lastStackCoords[stackId] = lastStackCoords[stackId] || [];\n    var valueDim = data.mapDimension(valueAxis.dim);\n    var baseDim = data.mapDimension(baseAxis.dim);\n    var stacked = isDimensionStacked(data, valueDim\n    /* , baseDim */\n    );\n    var clampLayout = baseAxis.dim !== 'radius' || !seriesModel.get('roundCap', true);\n    var valueAxisStart = valueAxis.dataToCoord(0);\n\n    for (var idx = 0, len = data.count(); idx < len; idx++) {\n      var value = data.get(valueDim, idx);\n      var baseValue = data.get(baseDim, idx);\n      var sign = value >= 0 ? 'p' : 'n';\n      var baseCoord = valueAxisStart; // Because of the barMinHeight, we can not use the value in\n      // stackResultDimension directly.\n      // Only ordinal axis can be stacked.\n\n      if (stacked) {\n        if (!lastStackCoords[stackId][baseValue]) {\n          lastStackCoords[stackId][baseValue] = {\n            p: valueAxisStart,\n            n: valueAxisStart // Negative stack\n\n          };\n        } // Should also consider #4243\n\n\n        baseCoord = lastStackCoords[stackId][baseValue][sign];\n      }\n\n      var r0 = void 0;\n      var r = void 0;\n      var startAngle = void 0;\n      var endAngle = void 0; // radial sector\n\n      if (valueAxis.dim === 'radius') {\n        var radiusSpan = valueAxis.dataToCoord(value) - valueAxisStart;\n        var angle = baseAxis.dataToCoord(baseValue);\n\n        if (Math.abs(radiusSpan) < barMinHeight) {\n          radiusSpan = (radiusSpan < 0 ? -1 : 1) * barMinHeight;\n        }\n\n        r0 = baseCoord;\n        r = baseCoord + radiusSpan;\n        startAngle = angle - columnOffset;\n        endAngle = startAngle - columnWidth;\n        stacked && (lastStackCoords[stackId][baseValue][sign] = r);\n      } // tangential sector\n      else {\n          var angleSpan = valueAxis.dataToCoord(value, clampLayout) - valueAxisStart;\n          var radius = baseAxis.dataToCoord(baseValue);\n\n          if (Math.abs(angleSpan) < barMinAngle) {\n            angleSpan = (angleSpan < 0 ? -1 : 1) * barMinAngle;\n          }\n\n          r0 = radius + columnOffset;\n          r = r0 + columnWidth;\n          startAngle = baseCoord;\n          endAngle = baseCoord + angleSpan; // if the previous stack is at the end of the ring,\n          // add a round to differentiate it from origin\n          // let extent = angleAxis.getExtent();\n          // let stackCoord = angle;\n          // if (stackCoord === extent[0] && value > 0) {\n          //     stackCoord = extent[1];\n          // }\n          // else if (stackCoord === extent[1] && value < 0) {\n          //     stackCoord = extent[0];\n          // }\n\n          stacked && (lastStackCoords[stackId][baseValue][sign] = endAngle);\n        }\n\n      data.setItemLayout(idx, {\n        cx: cx,\n        cy: cy,\n        r0: r0,\n        r: r,\n        // Consider that positive angle is anti-clockwise,\n        // while positive radian of sector is clockwise\n        startAngle: -startAngle * Math.PI / 180,\n        endAngle: -endAngle * Math.PI / 180,\n\n        /**\n         * Keep the same logic with bar in catesion: use end value to\n         * control direction. Notice that if clockwise is true (by\n         * default), the sector will always draw clockwisely, no matter\n         * whether endAngle is greater or less than startAngle.\n         */\n        clockwise: startAngle >= endAngle\n      });\n    }\n  });\n}\n/**\n * Calculate bar width and offset for radial bar charts\n */\n\n\nfunction calRadialBar(barSeries) {\n  // Columns info on each category axis. Key is polar name\n  var columnsMap = {};\n  each(barSeries, function (seriesModel, idx) {\n    var data = seriesModel.getData();\n    var polar = seriesModel.coordinateSystem;\n    var baseAxis = polar.getBaseAxis();\n    var axisKey = getAxisKey$1(polar, baseAxis);\n    var axisExtent = baseAxis.getExtent();\n    var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : Math.abs(axisExtent[1] - axisExtent[0]) / data.count();\n    var columnsOnAxis = columnsMap[axisKey] || {\n      bandWidth: bandWidth,\n      remainedWidth: bandWidth,\n      autoWidthCount: 0,\n      categoryGap: '20%',\n      gap: '30%',\n      stacks: {}\n    };\n    var stacks = columnsOnAxis.stacks;\n    columnsMap[axisKey] = columnsOnAxis;\n    var stackId = getSeriesStackId$1(seriesModel);\n\n    if (!stacks[stackId]) {\n      columnsOnAxis.autoWidthCount++;\n    }\n\n    stacks[stackId] = stacks[stackId] || {\n      width: 0,\n      maxWidth: 0\n    };\n    var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth);\n    var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth);\n    var barGap = seriesModel.get('barGap');\n    var barCategoryGap = seriesModel.get('barCategoryGap');\n\n    if (barWidth && !stacks[stackId].width) {\n      barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);\n      stacks[stackId].width = barWidth;\n      columnsOnAxis.remainedWidth -= barWidth;\n    }\n\n    barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);\n    barGap != null && (columnsOnAxis.gap = barGap);\n    barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap);\n  });\n  var result = {};\n  each(columnsMap, function (columnsOnAxis, coordSysName) {\n    result[coordSysName] = {};\n    var stacks = columnsOnAxis.stacks;\n    var bandWidth = columnsOnAxis.bandWidth;\n    var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth);\n    var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);\n    var remainedWidth = columnsOnAxis.remainedWidth;\n    var autoWidthCount = columnsOnAxis.autoWidthCount;\n    var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n    autoWidth = Math.max(autoWidth, 0); // Find if any auto calculated bar exceeded maxBarWidth\n\n    each(stacks, function (column, stack) {\n      var maxWidth = column.maxWidth;\n\n      if (maxWidth && maxWidth < autoWidth) {\n        maxWidth = Math.min(maxWidth, remainedWidth);\n\n        if (column.width) {\n          maxWidth = Math.min(maxWidth, column.width);\n        }\n\n        remainedWidth -= maxWidth;\n        column.width = maxWidth;\n        autoWidthCount--;\n      }\n    }); // Recalculate width again\n\n    autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n    autoWidth = Math.max(autoWidth, 0);\n    var widthSum = 0;\n    var lastColumn;\n    each(stacks, function (column, idx) {\n      if (!column.width) {\n        column.width = autoWidth;\n      }\n\n      lastColumn = column;\n      widthSum += column.width * (1 + barGapPercent);\n    });\n\n    if (lastColumn) {\n      widthSum -= lastColumn.width * barGapPercent;\n    }\n\n    var offset = -widthSum / 2;\n    each(stacks, function (column, stackId) {\n      result[coordSysName][stackId] = result[coordSysName][stackId] || {\n        offset: offset,\n        width: column.width\n      };\n      offset += column.width * (1 + barGapPercent);\n    });\n  });\n  return result;\n}\n\nvar angleAxisExtraOption = {\n  startAngle: 90,\n  clockwise: true,\n  splitNumber: 12,\n  axisLabel: {\n    rotate: 0\n  }\n};\nvar radiusAxisExtraOption = {\n  splitNumber: 5\n};\n\nvar PolarView =\n/** @class */\nfunction (_super) {\n  __extends(PolarView, _super);\n\n  function PolarView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = PolarView.type;\n    return _this;\n  }\n\n  PolarView.type = 'polar';\n  return PolarView;\n}(ComponentView);\n\nfunction install$u(registers) {\n  use(install$s);\n  AxisView.registerAxisPointerClass('PolarAxisPointer', PolarAxisPointer);\n  registers.registerCoordinateSystem('polar', polarCreator);\n  registers.registerComponentModel(PolarModel);\n  registers.registerComponentView(PolarView); // Model and view for angleAxis and radiusAxis\n\n  axisModelCreator(registers, 'angle', AngleAxisModel, angleAxisExtraOption);\n  axisModelCreator(registers, 'radius', RadiusAxisModel, radiusAxisExtraOption);\n  registers.registerComponentView(AngleAxisView);\n  registers.registerComponentView(RadiusAxisView);\n  registers.registerLayout(curry(barLayoutPolar, 'bar'));\n}\n\nfunction layout$2(axisModel, opt) {\n  opt = opt || {};\n  var single = axisModel.coordinateSystem;\n  var axis = axisModel.axis;\n  var layout = {};\n  var axisPosition = axis.position;\n  var orient = axis.orient;\n  var rect = single.getRect();\n  var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];\n  var positionMap = {\n    horizontal: {\n      top: rectBound[2],\n      bottom: rectBound[3]\n    },\n    vertical: {\n      left: rectBound[0],\n      right: rectBound[1]\n    }\n  };\n  layout.position = [orient === 'vertical' ? positionMap.vertical[axisPosition] : rectBound[0], orient === 'horizontal' ? positionMap.horizontal[axisPosition] : rectBound[3]];\n  var r = {\n    horizontal: 0,\n    vertical: 1\n  };\n  layout.rotation = Math.PI / 2 * r[orient];\n  var directionMap = {\n    top: -1,\n    bottom: 1,\n    right: 1,\n    left: -1\n  };\n  layout.labelDirection = layout.tickDirection = layout.nameDirection = directionMap[axisPosition];\n\n  if (axisModel.get(['axisTick', 'inside'])) {\n    layout.tickDirection = -layout.tickDirection;\n  }\n\n  if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) {\n    layout.labelDirection = -layout.labelDirection;\n  }\n\n  var labelRotation = opt.rotate;\n  labelRotation == null && (labelRotation = axisModel.get(['axisLabel', 'rotate']));\n  layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation;\n  layout.z2 = 1;\n  return layout;\n}\n\nvar axisBuilderAttrs$3 = ['axisLine', 'axisTickLabel', 'axisName'];\nvar selfBuilderAttrs$2 = ['splitArea', 'splitLine'];\n\nvar SingleAxisView =\n/** @class */\nfunction (_super) {\n  __extends(SingleAxisView, _super);\n\n  function SingleAxisView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SingleAxisView.type;\n    _this.axisPointerClass = 'SingleAxisPointer';\n    return _this;\n  }\n\n  SingleAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n    var group = this.group;\n    group.removeAll();\n    var oldAxisGroup = this._axisGroup;\n    this._axisGroup = new Group();\n    var layout = layout$2(axisModel);\n    var axisBuilder = new AxisBuilder(axisModel, layout);\n    each(axisBuilderAttrs$3, axisBuilder.add, axisBuilder);\n    group.add(this._axisGroup);\n    group.add(axisBuilder.getGroup());\n    each(selfBuilderAttrs$2, function (name) {\n      if (axisModel.get([name, 'show'])) {\n        axisElementBuilders$2[name](this, this.group, this._axisGroup, axisModel);\n      }\n    }, this);\n    groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n\n    _super.prototype.render.call(this, axisModel, ecModel, api, payload);\n  };\n\n  SingleAxisView.prototype.remove = function () {\n    rectCoordAxisHandleRemove(this);\n  };\n\n  SingleAxisView.type = 'singleAxis';\n  return SingleAxisView;\n}(AxisView);\n\nvar axisElementBuilders$2 = {\n  splitLine: function (axisView, group, axisGroup, axisModel) {\n    var axis = axisModel.axis;\n\n    if (axis.scale.isBlank()) {\n      return;\n    }\n\n    var splitLineModel = axisModel.getModel('splitLine');\n    var lineStyleModel = splitLineModel.getModel('lineStyle');\n    var lineColors = lineStyleModel.get('color');\n    lineColors = lineColors instanceof Array ? lineColors : [lineColors];\n    var lineWidth = lineStyleModel.get('width');\n    var gridRect = axisModel.coordinateSystem.getRect();\n    var isHorizontal = axis.isHorizontal();\n    var splitLines = [];\n    var lineCount = 0;\n    var ticksCoords = axis.getTicksCoords({\n      tickModel: splitLineModel\n    });\n    var p1 = [];\n    var p2 = [];\n\n    for (var i = 0; i < ticksCoords.length; ++i) {\n      var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n\n      if (isHorizontal) {\n        p1[0] = tickCoord;\n        p1[1] = gridRect.y;\n        p2[0] = tickCoord;\n        p2[1] = gridRect.y + gridRect.height;\n      } else {\n        p1[0] = gridRect.x;\n        p1[1] = tickCoord;\n        p2[0] = gridRect.x + gridRect.width;\n        p2[1] = tickCoord;\n      }\n\n      var line = new Line({\n        shape: {\n          x1: p1[0],\n          y1: p1[1],\n          x2: p2[0],\n          y2: p2[1]\n        },\n        silent: true\n      });\n      subPixelOptimizeLine$1(line.shape, lineWidth);\n      var colorIndex = lineCount++ % lineColors.length;\n      splitLines[colorIndex] = splitLines[colorIndex] || [];\n      splitLines[colorIndex].push(line);\n    }\n\n    var lineStyle = lineStyleModel.getLineStyle(['color']);\n\n    for (var i = 0; i < splitLines.length; ++i) {\n      group.add(mergePath$1(splitLines[i], {\n        style: defaults({\n          stroke: lineColors[i % lineColors.length]\n        }, lineStyle),\n        silent: true\n      }));\n    }\n  },\n  splitArea: function (axisView, group, axisGroup, axisModel) {\n    rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, axisModel);\n  }\n};\n\nvar SingleAxisModel =\n/** @class */\nfunction (_super) {\n  __extends(SingleAxisModel, _super);\n\n  function SingleAxisModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SingleAxisModel.type;\n    return _this;\n  }\n\n  SingleAxisModel.prototype.getCoordSysModel = function () {\n    return this;\n  };\n\n  SingleAxisModel.type = 'singleAxis';\n  SingleAxisModel.layoutMode = 'box';\n  SingleAxisModel.defaultOption = {\n    left: '5%',\n    top: '5%',\n    right: '5%',\n    bottom: '5%',\n    type: 'value',\n    position: 'bottom',\n    orient: 'horizontal',\n    axisLine: {\n      show: true,\n      lineStyle: {\n        width: 1,\n        type: 'solid'\n      }\n    },\n    // Single coordinate system and single axis is the,\n    // which is used as the parent tooltip model.\n    // same model, so we set default tooltip show as true.\n    tooltip: {\n      show: true\n    },\n    axisTick: {\n      show: true,\n      length: 6,\n      lineStyle: {\n        width: 1\n      }\n    },\n    axisLabel: {\n      show: true,\n      interval: 'auto'\n    },\n    splitLine: {\n      show: true,\n      lineStyle: {\n        type: 'dashed',\n        opacity: 0.2\n      }\n    }\n  };\n  return SingleAxisModel;\n}(ComponentModel);\n\nmixin(SingleAxisModel, AxisModelCommonMixin.prototype);\n\nvar SingleAxis =\n/** @class */\nfunction (_super) {\n  __extends(SingleAxis, _super);\n\n  function SingleAxis(dim, scale, coordExtent, axisType, position) {\n    var _this = _super.call(this, dim, scale, coordExtent) || this;\n\n    _this.type = axisType || 'value';\n    _this.position = position || 'bottom';\n    return _this;\n  }\n  /**\n   * Judge the orient of the axis.\n   */\n\n\n  SingleAxis.prototype.isHorizontal = function () {\n    var position = this.position;\n    return position === 'top' || position === 'bottom';\n  };\n\n  SingleAxis.prototype.pointToData = function (point, clamp) {\n    return this.coordinateSystem.pointToData(point)[0];\n  };\n\n  return SingleAxis;\n}(Axis);\n\nvar singleDimensions = ['single'];\n/**\n * Create a single coordinates system.\n */\n\nvar Single =\n/** @class */\nfunction () {\n  function Single(axisModel, ecModel, api) {\n    this.type = 'single';\n    this.dimension = 'single';\n    /**\n     * Add it just for draw tooltip.\n     */\n\n    this.dimensions = singleDimensions;\n    this.axisPointerEnabled = true;\n    this.model = axisModel;\n\n    this._init(axisModel, ecModel, api);\n  }\n  /**\n   * Initialize single coordinate system.\n   */\n\n\n  Single.prototype._init = function (axisModel, ecModel, api) {\n    var dim = this.dimension;\n    var axis = new SingleAxis(dim, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisModel.get('position'));\n    var isCategory = axis.type === 'category';\n    axis.onBand = isCategory && axisModel.get('boundaryGap');\n    axis.inverse = axisModel.get('inverse');\n    axis.orient = axisModel.get('orient');\n    axisModel.axis = axis;\n    axis.model = axisModel;\n    axis.coordinateSystem = this;\n    this._axis = axis;\n  };\n  /**\n   * Update axis scale after data processed\n   */\n\n\n  Single.prototype.update = function (ecModel, api) {\n    ecModel.eachSeries(function (seriesModel) {\n      if (seriesModel.coordinateSystem === this) {\n        var data_1 = seriesModel.getData();\n        each(data_1.mapDimensionsAll(this.dimension), function (dim) {\n          this._axis.scale.unionExtentFromData(data_1, dim);\n        }, this);\n        niceScaleExtent(this._axis.scale, this._axis.model);\n      }\n    }, this);\n  };\n  /**\n   * Resize the single coordinate system.\n   */\n\n\n  Single.prototype.resize = function (axisModel, api) {\n    this._rect = getLayoutRect({\n      left: axisModel.get('left'),\n      top: axisModel.get('top'),\n      right: axisModel.get('right'),\n      bottom: axisModel.get('bottom'),\n      width: axisModel.get('width'),\n      height: axisModel.get('height')\n    }, {\n      width: api.getWidth(),\n      height: api.getHeight()\n    });\n\n    this._adjustAxis();\n  };\n\n  Single.prototype.getRect = function () {\n    return this._rect;\n  };\n\n  Single.prototype._adjustAxis = function () {\n    var rect = this._rect;\n    var axis = this._axis;\n    var isHorizontal = axis.isHorizontal();\n    var extent = isHorizontal ? [0, rect.width] : [0, rect.height];\n    var idx = axis.inverse ? 1 : 0;\n    axis.setExtent(extent[idx], extent[1 - idx]);\n\n    this._updateAxisTransform(axis, isHorizontal ? rect.x : rect.y);\n  };\n\n  Single.prototype._updateAxisTransform = function (axis, coordBase) {\n    var axisExtent = axis.getExtent();\n    var extentSum = axisExtent[0] + axisExtent[1];\n    var isHorizontal = axis.isHorizontal();\n    axis.toGlobalCoord = isHorizontal ? function (coord) {\n      return coord + coordBase;\n    } : function (coord) {\n      return extentSum - coord + coordBase;\n    };\n    axis.toLocalCoord = isHorizontal ? function (coord) {\n      return coord - coordBase;\n    } : function (coord) {\n      return extentSum - coord + coordBase;\n    };\n  };\n  /**\n   * Get axis.\n   */\n\n\n  Single.prototype.getAxis = function () {\n    return this._axis;\n  };\n  /**\n   * Get axis, add it just for draw tooltip.\n   */\n\n\n  Single.prototype.getBaseAxis = function () {\n    return this._axis;\n  };\n\n  Single.prototype.getAxes = function () {\n    return [this._axis];\n  };\n\n  Single.prototype.getTooltipAxes = function () {\n    return {\n      baseAxes: [this.getAxis()],\n      // Empty otherAxes\n      otherAxes: []\n    };\n  };\n  /**\n   * If contain point.\n   */\n\n\n  Single.prototype.containPoint = function (point) {\n    var rect = this.getRect();\n    var axis = this.getAxis();\n    var orient = axis.orient;\n\n    if (orient === 'horizontal') {\n      return axis.contain(axis.toLocalCoord(point[0])) && point[1] >= rect.y && point[1] <= rect.y + rect.height;\n    } else {\n      return axis.contain(axis.toLocalCoord(point[1])) && point[0] >= rect.y && point[0] <= rect.y + rect.height;\n    }\n  };\n\n  Single.prototype.pointToData = function (point) {\n    var axis = this.getAxis();\n    return [axis.coordToData(axis.toLocalCoord(point[axis.orient === 'horizontal' ? 0 : 1]))];\n  };\n  /**\n   * Convert the series data to concrete point.\n   * Can be [val] | val\n   */\n\n\n  Single.prototype.dataToPoint = function (val) {\n    var axis = this.getAxis();\n    var rect = this.getRect();\n    var pt = [];\n    var idx = axis.orient === 'horizontal' ? 0 : 1;\n\n    if (val instanceof Array) {\n      val = val[0];\n    }\n\n    pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val));\n    pt[1 - idx] = idx === 0 ? rect.y + rect.height / 2 : rect.x + rect.width / 2;\n    return pt;\n  };\n\n  Single.prototype.convertToPixel = function (ecModel, finder, value) {\n    var coordSys = getCoordSys$3(finder);\n    return coordSys === this ? this.dataToPoint(value) : null;\n  };\n\n  Single.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n    var coordSys = getCoordSys$3(finder);\n    return coordSys === this ? this.pointToData(pixel) : null;\n  };\n\n  return Single;\n}();\n\nfunction getCoordSys$3(finder) {\n  var seriesModel = finder.seriesModel;\n  var singleModel = finder.singleAxisModel;\n  return singleModel && singleModel.coordinateSystem || seriesModel && seriesModel.coordinateSystem;\n}\n\n/**\n * Create single coordinate system and inject it into seriesModel.\n */\n\nfunction create$2(ecModel, api) {\n  var singles = [];\n  ecModel.eachComponent('singleAxis', function (axisModel, idx) {\n    var single = new Single(axisModel, ecModel, api);\n    single.name = 'single_' + idx;\n    single.resize(axisModel, api);\n    axisModel.coordinateSystem = single;\n    singles.push(single);\n  });\n  ecModel.eachSeries(function (seriesModel) {\n    if (seriesModel.get('coordinateSystem') === 'singleAxis') {\n      var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n      seriesModel.coordinateSystem = singleAxisModel && singleAxisModel.coordinateSystem;\n    }\n  });\n  return singles;\n}\n\nvar singleCreator = {\n  create: create$2,\n  dimensions: singleDimensions\n};\n\nvar XY = ['x', 'y'];\nvar WH = ['width', 'height'];\n\nvar SingleAxisPointer =\n/** @class */\nfunction (_super) {\n  __extends(SingleAxisPointer, _super);\n\n  function SingleAxisPointer() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n  /**\n   * @override\n   */\n\n\n  SingleAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {\n    var axis = axisModel.axis;\n    var coordSys = axis.coordinateSystem;\n    var otherExtent = getGlobalExtent(coordSys, 1 - getPointDimIndex(axis));\n    var pixelValue = coordSys.dataToPoint(value)[0];\n    var axisPointerType = axisPointerModel.get('type');\n\n    if (axisPointerType && axisPointerType !== 'none') {\n      var elStyle = buildElStyle(axisPointerModel);\n      var pointerOption = pointerShapeBuilder$2[axisPointerType](axis, pixelValue, otherExtent);\n      pointerOption.style = elStyle;\n      elOption.graphicKey = pointerOption.type;\n      elOption.pointer = pointerOption;\n    }\n\n    var layoutInfo = layout$2(axisModel);\n    buildCartesianSingleLabelElOption( // @ts-ignore\n    value, elOption, layoutInfo, axisModel, axisPointerModel, api);\n  };\n  /**\n   * @override\n   */\n\n\n  SingleAxisPointer.prototype.getHandleTransform = function (value, axisModel, axisPointerModel) {\n    var layoutInfo = layout$2(axisModel, {\n      labelInside: false\n    }); // @ts-ignore\n\n    layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']);\n    var position = getTransformedPosition(axisModel.axis, value, layoutInfo);\n    return {\n      x: position[0],\n      y: position[1],\n      rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)\n    };\n  };\n  /**\n   * @override\n   */\n\n\n  SingleAxisPointer.prototype.updateHandleTransform = function (transform, delta, axisModel, axisPointerModel) {\n    var axis = axisModel.axis;\n    var coordSys = axis.coordinateSystem;\n    var dimIndex = getPointDimIndex(axis);\n    var axisExtent = getGlobalExtent(coordSys, dimIndex);\n    var currPosition = [transform.x, transform.y];\n    currPosition[dimIndex] += delta[dimIndex];\n    currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);\n    currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);\n    var otherExtent = getGlobalExtent(coordSys, 1 - dimIndex);\n    var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;\n    var cursorPoint = [cursorOtherValue, cursorOtherValue];\n    cursorPoint[dimIndex] = currPosition[dimIndex];\n    return {\n      x: currPosition[0],\n      y: currPosition[1],\n      rotation: transform.rotation,\n      cursorPoint: cursorPoint,\n      tooltipOption: {\n        verticalAlign: 'middle'\n      }\n    };\n  };\n\n  return SingleAxisPointer;\n}(BaseAxisPointer);\n\nvar pointerShapeBuilder$2 = {\n  line: function (axis, pixelValue, otherExtent) {\n    var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getPointDimIndex(axis));\n    return {\n      type: 'Line',\n      subPixelOptimize: true,\n      shape: targetShape\n    };\n  },\n  shadow: function (axis, pixelValue, otherExtent) {\n    var bandWidth = axis.getBandWidth();\n    var span = otherExtent[1] - otherExtent[0];\n    return {\n      type: 'Rect',\n      shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getPointDimIndex(axis))\n    };\n  }\n};\n\nfunction getPointDimIndex(axis) {\n  return axis.isHorizontal() ? 0 : 1;\n}\n\nfunction getGlobalExtent(coordSys, dimIndex) {\n  var rect = coordSys.getRect();\n  return [rect[XY[dimIndex]], rect[XY[dimIndex]] + rect[WH[dimIndex]]];\n}\n\nvar SingleView =\n/** @class */\nfunction (_super) {\n  __extends(SingleView, _super);\n\n  function SingleView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SingleView.type;\n    return _this;\n  }\n\n  SingleView.type = 'single';\n  return SingleView;\n}(ComponentView);\n\nfunction install$v(registers) {\n  use(install$s);\n  AxisView.registerAxisPointerClass('SingleAxisPointer', SingleAxisPointer);\n  registers.registerComponentView(SingleView); // Axis\n\n  registers.registerComponentView(SingleAxisView);\n  registers.registerComponentModel(SingleAxisModel);\n  axisModelCreator(registers, 'single', SingleAxisModel, SingleAxisModel.defaultOption);\n  registers.registerCoordinateSystem('single', singleCreator);\n}\n\nvar CalendarModel =\n/** @class */\nfunction (_super) {\n  __extends(CalendarModel, _super);\n\n  function CalendarModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CalendarModel.type;\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  CalendarModel.prototype.init = function (option, parentModel, ecModel) {\n    var inputPositionParams = getLayoutParams(option);\n\n    _super.prototype.init.apply(this, arguments);\n\n    mergeAndNormalizeLayoutParams(option, inputPositionParams);\n  };\n  /**\n   * @override\n   */\n\n\n  CalendarModel.prototype.mergeOption = function (option) {\n    _super.prototype.mergeOption.apply(this, arguments);\n\n    mergeAndNormalizeLayoutParams(this.option, option);\n  };\n\n  CalendarModel.prototype.getCellSize = function () {\n    // Has been normalized\n    return this.option.cellSize;\n  };\n\n  CalendarModel.type = 'calendar';\n  CalendarModel.defaultOption = {\n    // zlevel: 0,\n    z: 2,\n    left: 80,\n    top: 60,\n    cellSize: 20,\n    // horizontal vertical\n    orient: 'horizontal',\n    // month separate line style\n    splitLine: {\n      show: true,\n      lineStyle: {\n        color: '#000',\n        width: 1,\n        type: 'solid'\n      }\n    },\n    // rect style  temporarily unused emphasis\n    itemStyle: {\n      color: '#fff',\n      borderWidth: 1,\n      borderColor: '#ccc'\n    },\n    // week text style\n    dayLabel: {\n      show: true,\n      firstDay: 0,\n      // start end\n      position: 'start',\n      margin: '50%',\n      color: '#000'\n    },\n    // month text style\n    monthLabel: {\n      show: true,\n      // start end\n      position: 'start',\n      margin: 5,\n      // center or left\n      align: 'center',\n      formatter: null,\n      color: '#000'\n    },\n    // year text style\n    yearLabel: {\n      show: true,\n      // top bottom left right\n      position: null,\n      margin: 30,\n      formatter: null,\n      color: '#ccc',\n      fontFamily: 'sans-serif',\n      fontWeight: 'bolder',\n      fontSize: 20\n    }\n  };\n  return CalendarModel;\n}(ComponentModel);\n\nfunction mergeAndNormalizeLayoutParams(target, raw) {\n  // Normalize cellSize\n  var cellSize = target.cellSize;\n  var cellSizeArr;\n\n  if (!isArray(cellSize)) {\n    cellSizeArr = target.cellSize = [cellSize, cellSize];\n  } else {\n    cellSizeArr = cellSize;\n  }\n\n  if (cellSizeArr.length === 1) {\n    cellSizeArr[1] = cellSizeArr[0];\n  }\n\n  var ignoreSize = map([0, 1], function (hvIdx) {\n    // If user have set `width` or both `left` and `right`, cellSizeArr\n    // will be automatically set to 'auto', otherwise the default\n    // setting of cellSizeArr will make `width` setting not work.\n    if (sizeCalculable(raw, hvIdx)) {\n      cellSizeArr[hvIdx] = 'auto';\n    }\n\n    return cellSizeArr[hvIdx] != null && cellSizeArr[hvIdx] !== 'auto';\n  });\n  mergeLayoutParam(target, raw, {\n    type: 'box',\n    ignoreSize: ignoreSize\n  });\n}\n\nvar CalendarView =\n/** @class */\nfunction (_super) {\n  __extends(CalendarView, _super);\n\n  function CalendarView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = CalendarView.type;\n    return _this;\n  }\n\n  CalendarView.prototype.render = function (calendarModel, ecModel, api) {\n    var group = this.group;\n    group.removeAll();\n    var coordSys = calendarModel.coordinateSystem; // range info\n\n    var rangeData = coordSys.getRangeInfo();\n    var orient = coordSys.getOrient(); // locale\n\n    var localeModel = ecModel.getLocaleModel();\n\n    this._renderDayRect(calendarModel, rangeData, group); // _renderLines must be called prior to following function\n\n\n    this._renderLines(calendarModel, rangeData, orient, group);\n\n    this._renderYearText(calendarModel, rangeData, orient, group);\n\n    this._renderMonthText(calendarModel, localeModel, orient, group);\n\n    this._renderWeekText(calendarModel, localeModel, rangeData, orient, group);\n  }; // render day rect\n\n\n  CalendarView.prototype._renderDayRect = function (calendarModel, rangeData, group) {\n    var coordSys = calendarModel.coordinateSystem;\n    var itemRectStyleModel = calendarModel.getModel('itemStyle').getItemStyle();\n    var sw = coordSys.getCellWidth();\n    var sh = coordSys.getCellHeight();\n\n    for (var i = rangeData.start.time; i <= rangeData.end.time; i = coordSys.getNextNDay(i, 1).time) {\n      var point = coordSys.dataToRect([i], false).tl; // every rect\n\n      var rect = new Rect({\n        shape: {\n          x: point[0],\n          y: point[1],\n          width: sw,\n          height: sh\n        },\n        cursor: 'default',\n        style: itemRectStyleModel\n      });\n      group.add(rect);\n    }\n  }; // render separate line\n\n\n  CalendarView.prototype._renderLines = function (calendarModel, rangeData, orient, group) {\n    var self = this;\n    var coordSys = calendarModel.coordinateSystem;\n    var lineStyleModel = calendarModel.getModel(['splitLine', 'lineStyle']).getLineStyle();\n    var show = calendarModel.get(['splitLine', 'show']);\n    var lineWidth = lineStyleModel.lineWidth;\n    this._tlpoints = [];\n    this._blpoints = [];\n    this._firstDayOfMonth = [];\n    this._firstDayPoints = [];\n    var firstDay = rangeData.start;\n\n    for (var i = 0; firstDay.time <= rangeData.end.time; i++) {\n      addPoints(firstDay.formatedDate);\n\n      if (i === 0) {\n        firstDay = coordSys.getDateInfo(rangeData.start.y + '-' + rangeData.start.m);\n      }\n\n      var date = firstDay.date;\n      date.setMonth(date.getMonth() + 1);\n      firstDay = coordSys.getDateInfo(date);\n    }\n\n    addPoints(coordSys.getNextNDay(rangeData.end.time, 1).formatedDate);\n\n    function addPoints(date) {\n      self._firstDayOfMonth.push(coordSys.getDateInfo(date));\n\n      self._firstDayPoints.push(coordSys.dataToRect([date], false).tl);\n\n      var points = self._getLinePointsOfOneWeek(calendarModel, date, orient);\n\n      self._tlpoints.push(points[0]);\n\n      self._blpoints.push(points[points.length - 1]);\n\n      show && self._drawSplitline(points, lineStyleModel, group);\n    } // render top/left line\n\n\n    show && this._drawSplitline(self._getEdgesPoints(self._tlpoints, lineWidth, orient), lineStyleModel, group); // render bottom/right line\n\n    show && this._drawSplitline(self._getEdgesPoints(self._blpoints, lineWidth, orient), lineStyleModel, group);\n  }; // get points at both ends\n\n\n  CalendarView.prototype._getEdgesPoints = function (points, lineWidth, orient) {\n    var rs = [points[0].slice(), points[points.length - 1].slice()];\n    var idx = orient === 'horizontal' ? 0 : 1; // both ends of the line are extend half lineWidth\n\n    rs[0][idx] = rs[0][idx] - lineWidth / 2;\n    rs[1][idx] = rs[1][idx] + lineWidth / 2;\n    return rs;\n  }; // render split line\n\n\n  CalendarView.prototype._drawSplitline = function (points, lineStyle, group) {\n    var poyline = new Polyline({\n      z2: 20,\n      shape: {\n        points: points\n      },\n      style: lineStyle\n    });\n    group.add(poyline);\n  }; // render month line of one week points\n\n\n  CalendarView.prototype._getLinePointsOfOneWeek = function (calendarModel, date, orient) {\n    var coordSys = calendarModel.coordinateSystem;\n    var parsedDate = coordSys.getDateInfo(date);\n    var points = [];\n\n    for (var i = 0; i < 7; i++) {\n      var tmpD = coordSys.getNextNDay(parsedDate.time, i);\n      var point = coordSys.dataToRect([tmpD.time], false);\n      points[2 * tmpD.day] = point.tl;\n      points[2 * tmpD.day + 1] = point[orient === 'horizontal' ? 'bl' : 'tr'];\n    }\n\n    return points;\n  };\n\n  CalendarView.prototype._formatterLabel = function (formatter, params) {\n    if (isString(formatter) && formatter) {\n      return formatTplSimple(formatter, params);\n    }\n\n    if (isFunction(formatter)) {\n      return formatter(params);\n    }\n\n    return params.nameMap;\n  };\n\n  CalendarView.prototype._yearTextPositionControl = function (textEl, point, orient, position, margin) {\n    var x = point[0];\n    var y = point[1];\n    var aligns = ['center', 'bottom'];\n\n    if (position === 'bottom') {\n      y += margin;\n      aligns = ['center', 'top'];\n    } else if (position === 'left') {\n      x -= margin;\n    } else if (position === 'right') {\n      x += margin;\n      aligns = ['center', 'top'];\n    } else {\n      // top\n      y -= margin;\n    }\n\n    var rotate = 0;\n\n    if (position === 'left' || position === 'right') {\n      rotate = Math.PI / 2;\n    }\n\n    return {\n      rotation: rotate,\n      x: x,\n      y: y,\n      style: {\n        align: aligns[0],\n        verticalAlign: aligns[1]\n      }\n    };\n  }; // render year\n\n\n  CalendarView.prototype._renderYearText = function (calendarModel, rangeData, orient, group) {\n    var yearLabel = calendarModel.getModel('yearLabel');\n\n    if (!yearLabel.get('show')) {\n      return;\n    }\n\n    var margin = yearLabel.get('margin');\n    var pos = yearLabel.get('position');\n\n    if (!pos) {\n      pos = orient !== 'horizontal' ? 'top' : 'left';\n    }\n\n    var points = [this._tlpoints[this._tlpoints.length - 1], this._blpoints[0]];\n    var xc = (points[0][0] + points[1][0]) / 2;\n    var yc = (points[0][1] + points[1][1]) / 2;\n    var idx = orient === 'horizontal' ? 0 : 1;\n    var posPoints = {\n      top: [xc, points[idx][1]],\n      bottom: [xc, points[1 - idx][1]],\n      left: [points[1 - idx][0], yc],\n      right: [points[idx][0], yc]\n    };\n    var name = rangeData.start.y;\n\n    if (+rangeData.end.y > +rangeData.start.y) {\n      name = name + '-' + rangeData.end.y;\n    }\n\n    var formatter = yearLabel.get('formatter');\n    var params = {\n      start: rangeData.start.y,\n      end: rangeData.end.y,\n      nameMap: name\n    };\n\n    var content = this._formatterLabel(formatter, params);\n\n    var yearText = new ZRText({\n      z2: 30,\n      style: createTextStyle(yearLabel, {\n        text: content\n      })\n    });\n    yearText.attr(this._yearTextPositionControl(yearText, posPoints[pos], orient, pos, margin));\n    group.add(yearText);\n  };\n\n  CalendarView.prototype._monthTextPositionControl = function (point, isCenter, orient, position, margin) {\n    var align = 'left';\n    var vAlign = 'top';\n    var x = point[0];\n    var y = point[1];\n\n    if (orient === 'horizontal') {\n      y = y + margin;\n\n      if (isCenter) {\n        align = 'center';\n      }\n\n      if (position === 'start') {\n        vAlign = 'bottom';\n      }\n    } else {\n      x = x + margin;\n\n      if (isCenter) {\n        vAlign = 'middle';\n      }\n\n      if (position === 'start') {\n        align = 'right';\n      }\n    }\n\n    return {\n      x: x,\n      y: y,\n      align: align,\n      verticalAlign: vAlign\n    };\n  }; // render month and year text\n\n\n  CalendarView.prototype._renderMonthText = function (calendarModel, localeModel, orient, group) {\n    var monthLabel = calendarModel.getModel('monthLabel');\n\n    if (!monthLabel.get('show')) {\n      return;\n    }\n\n    var nameMap = monthLabel.get('nameMap');\n    var margin = monthLabel.get('margin');\n    var pos = monthLabel.get('position');\n    var align = monthLabel.get('align');\n    var termPoints = [this._tlpoints, this._blpoints];\n\n    if (!nameMap || isString(nameMap)) {\n      if (nameMap) {\n        // case-sensitive\n        localeModel = getLocaleModel(nameMap) || localeModel;\n      } // PENDING\n      // for ZH locale, original form is `一月` but current form is `1月`\n\n\n      nameMap = localeModel.get(['time', 'monthAbbr']) || [];\n    }\n\n    var idx = pos === 'start' ? 0 : 1;\n    var axis = orient === 'horizontal' ? 0 : 1;\n    margin = pos === 'start' ? -margin : margin;\n    var isCenter = align === 'center';\n\n    for (var i = 0; i < termPoints[idx].length - 1; i++) {\n      var tmp = termPoints[idx][i].slice();\n      var firstDay = this._firstDayOfMonth[i];\n\n      if (isCenter) {\n        var firstDayPoints = this._firstDayPoints[i];\n        tmp[axis] = (firstDayPoints[axis] + termPoints[0][i + 1][axis]) / 2;\n      }\n\n      var formatter = monthLabel.get('formatter');\n      var name_1 = nameMap[+firstDay.m - 1];\n      var params = {\n        yyyy: firstDay.y,\n        yy: (firstDay.y + '').slice(2),\n        MM: firstDay.m,\n        M: +firstDay.m,\n        nameMap: name_1\n      };\n\n      var content = this._formatterLabel(formatter, params);\n\n      var monthText = new ZRText({\n        z2: 30,\n        style: extend(createTextStyle(monthLabel, {\n          text: content\n        }), this._monthTextPositionControl(tmp, isCenter, orient, pos, margin))\n      });\n      group.add(monthText);\n    }\n  };\n\n  CalendarView.prototype._weekTextPositionControl = function (point, orient, position, margin, cellSize) {\n    var align = 'center';\n    var vAlign = 'middle';\n    var x = point[0];\n    var y = point[1];\n    var isStart = position === 'start';\n\n    if (orient === 'horizontal') {\n      x = x + margin + (isStart ? 1 : -1) * cellSize[0] / 2;\n      align = isStart ? 'right' : 'left';\n    } else {\n      y = y + margin + (isStart ? 1 : -1) * cellSize[1] / 2;\n      vAlign = isStart ? 'bottom' : 'top';\n    }\n\n    return {\n      x: x,\n      y: y,\n      align: align,\n      verticalAlign: vAlign\n    };\n  }; // render weeks\n\n\n  CalendarView.prototype._renderWeekText = function (calendarModel, localeModel, rangeData, orient, group) {\n    var dayLabel = calendarModel.getModel('dayLabel');\n\n    if (!dayLabel.get('show')) {\n      return;\n    }\n\n    var coordSys = calendarModel.coordinateSystem;\n    var pos = dayLabel.get('position');\n    var nameMap = dayLabel.get('nameMap');\n    var margin = dayLabel.get('margin');\n    var firstDayOfWeek = coordSys.getFirstDayOfWeek();\n\n    if (!nameMap || isString(nameMap)) {\n      if (nameMap) {\n        // case-sensitive\n        localeModel = getLocaleModel(nameMap) || localeModel;\n      } // Use the first letter of `dayOfWeekAbbr` if `dayOfWeekShort` doesn't exist in the locale file\n\n\n      var dayOfWeekShort = localeModel.get(['time', 'dayOfWeekShort']);\n      nameMap = dayOfWeekShort || map(localeModel.get(['time', 'dayOfWeekAbbr']), function (val) {\n        return val[0];\n      });\n    }\n\n    var start = coordSys.getNextNDay(rangeData.end.time, 7 - rangeData.lweek).time;\n    var cellSize = [coordSys.getCellWidth(), coordSys.getCellHeight()];\n    margin = parsePercent$1(margin, Math.min(cellSize[1], cellSize[0]));\n\n    if (pos === 'start') {\n      start = coordSys.getNextNDay(rangeData.start.time, -(7 + rangeData.fweek)).time;\n      margin = -margin;\n    }\n\n    for (var i = 0; i < 7; i++) {\n      var tmpD = coordSys.getNextNDay(start, i);\n      var point = coordSys.dataToRect([tmpD.time], false).center;\n      var day = i;\n      day = Math.abs((i + firstDayOfWeek) % 7);\n      var weekText = new ZRText({\n        z2: 30,\n        style: extend(createTextStyle(dayLabel, {\n          text: nameMap[day]\n        }), this._weekTextPositionControl(point, orient, pos, margin, cellSize))\n      });\n      group.add(weekText);\n    }\n  };\n\n  CalendarView.type = 'calendar';\n  return CalendarView;\n}(ComponentView);\n\nvar PROXIMATE_ONE_DAY = 86400000;\n\nvar Calendar =\n/** @class */\nfunction () {\n  function Calendar(calendarModel, ecModel, api) {\n    this.type = 'calendar';\n    this.dimensions = Calendar.dimensions; // Required in createListFromData\n\n    this.getDimensionsInfo = Calendar.getDimensionsInfo;\n    this._model = calendarModel;\n  }\n\n  Calendar.getDimensionsInfo = function () {\n    return [{\n      name: 'time',\n      type: 'time'\n    }, 'value'];\n  };\n\n  Calendar.prototype.getRangeInfo = function () {\n    return this._rangeInfo;\n  };\n\n  Calendar.prototype.getModel = function () {\n    return this._model;\n  };\n\n  Calendar.prototype.getRect = function () {\n    return this._rect;\n  };\n\n  Calendar.prototype.getCellWidth = function () {\n    return this._sw;\n  };\n\n  Calendar.prototype.getCellHeight = function () {\n    return this._sh;\n  };\n\n  Calendar.prototype.getOrient = function () {\n    return this._orient;\n  };\n  /**\n   * getFirstDayOfWeek\n   *\n   * @example\n   *     0 : start at Sunday\n   *     1 : start at Monday\n   *\n   * @return {number}\n   */\n\n\n  Calendar.prototype.getFirstDayOfWeek = function () {\n    return this._firstDayOfWeek;\n  };\n  /**\n   * get date info\n   * }\n   */\n\n\n  Calendar.prototype.getDateInfo = function (date) {\n    date = parseDate(date);\n    var y = date.getFullYear();\n    var m = date.getMonth() + 1;\n    var mStr = m < 10 ? '0' + m : '' + m;\n    var d = date.getDate();\n    var dStr = d < 10 ? '0' + d : '' + d;\n    var day = date.getDay();\n    day = Math.abs((day + 7 - this.getFirstDayOfWeek()) % 7);\n    return {\n      y: y + '',\n      m: mStr,\n      d: dStr,\n      day: day,\n      time: date.getTime(),\n      formatedDate: y + '-' + mStr + '-' + dStr,\n      date: date\n    };\n  };\n\n  Calendar.prototype.getNextNDay = function (date, n) {\n    n = n || 0;\n\n    if (n === 0) {\n      return this.getDateInfo(date);\n    }\n\n    date = new Date(this.getDateInfo(date).time);\n    date.setDate(date.getDate() + n);\n    return this.getDateInfo(date);\n  };\n\n  Calendar.prototype.update = function (ecModel, api) {\n    this._firstDayOfWeek = +this._model.getModel('dayLabel').get('firstDay');\n    this._orient = this._model.get('orient');\n    this._lineWidth = this._model.getModel('itemStyle').getItemStyle().lineWidth || 0;\n    this._rangeInfo = this._getRangeInfo(this._initRangeOption());\n    var weeks = this._rangeInfo.weeks || 1;\n    var whNames = ['width', 'height'];\n\n    var cellSize = this._model.getCellSize().slice();\n\n    var layoutParams = this._model.getBoxLayoutParams();\n\n    var cellNumbers = this._orient === 'horizontal' ? [weeks, 7] : [7, weeks];\n    each([0, 1], function (idx) {\n      if (cellSizeSpecified(cellSize, idx)) {\n        layoutParams[whNames[idx]] = cellSize[idx] * cellNumbers[idx];\n      }\n    });\n    var whGlobal = {\n      width: api.getWidth(),\n      height: api.getHeight()\n    };\n    var calendarRect = this._rect = getLayoutRect(layoutParams, whGlobal);\n    each([0, 1], function (idx) {\n      if (!cellSizeSpecified(cellSize, idx)) {\n        cellSize[idx] = calendarRect[whNames[idx]] / cellNumbers[idx];\n      }\n    });\n\n    function cellSizeSpecified(cellSize, idx) {\n      return cellSize[idx] != null && cellSize[idx] !== 'auto';\n    } // Has been calculated out number.\n\n\n    this._sw = cellSize[0];\n    this._sh = cellSize[1];\n  };\n  /**\n   * Convert a time data(time, value) item to (x, y) point.\n   */\n  // TODO Clamp of calendar is not same with cartesian coordinate systems.\n  // It will return NaN if data exceeds.\n\n\n  Calendar.prototype.dataToPoint = function (data, clamp) {\n    isArray(data) && (data = data[0]);\n    clamp == null && (clamp = true);\n    var dayInfo = this.getDateInfo(data);\n    var range = this._rangeInfo;\n    var date = dayInfo.formatedDate; // if not in range return [NaN, NaN]\n\n    if (clamp && !(dayInfo.time >= range.start.time && dayInfo.time < range.end.time + PROXIMATE_ONE_DAY)) {\n      return [NaN, NaN];\n    }\n\n    var week = dayInfo.day;\n\n    var nthWeek = this._getRangeInfo([range.start.time, date]).nthWeek;\n\n    if (this._orient === 'vertical') {\n      return [this._rect.x + week * this._sw + this._sw / 2, this._rect.y + nthWeek * this._sh + this._sh / 2];\n    }\n\n    return [this._rect.x + nthWeek * this._sw + this._sw / 2, this._rect.y + week * this._sh + this._sh / 2];\n  };\n  /**\n   * Convert a (x, y) point to time data\n   */\n\n\n  Calendar.prototype.pointToData = function (point) {\n    var date = this.pointToDate(point);\n    return date && date.time;\n  };\n  /**\n   * Convert a time date item to (x, y) four point.\n   */\n\n\n  Calendar.prototype.dataToRect = function (data, clamp) {\n    var point = this.dataToPoint(data, clamp);\n    return {\n      contentShape: {\n        x: point[0] - (this._sw - this._lineWidth) / 2,\n        y: point[1] - (this._sh - this._lineWidth) / 2,\n        width: this._sw - this._lineWidth,\n        height: this._sh - this._lineWidth\n      },\n      center: point,\n      tl: [point[0] - this._sw / 2, point[1] - this._sh / 2],\n      tr: [point[0] + this._sw / 2, point[1] - this._sh / 2],\n      br: [point[0] + this._sw / 2, point[1] + this._sh / 2],\n      bl: [point[0] - this._sw / 2, point[1] + this._sh / 2]\n    };\n  };\n  /**\n   * Convert a (x, y) point to time date\n   *\n   * @param  {Array} point point\n   * @return {Object}       date\n   */\n\n\n  Calendar.prototype.pointToDate = function (point) {\n    var nthX = Math.floor((point[0] - this._rect.x) / this._sw) + 1;\n    var nthY = Math.floor((point[1] - this._rect.y) / this._sh) + 1;\n    var range = this._rangeInfo.range;\n\n    if (this._orient === 'vertical') {\n      return this._getDateByWeeksAndDay(nthY, nthX - 1, range);\n    }\n\n    return this._getDateByWeeksAndDay(nthX, nthY - 1, range);\n  };\n\n  Calendar.prototype.convertToPixel = function (ecModel, finder, value) {\n    var coordSys = getCoordSys$4(finder);\n    return coordSys === this ? coordSys.dataToPoint(value) : null;\n  };\n\n  Calendar.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n    var coordSys = getCoordSys$4(finder);\n    return coordSys === this ? coordSys.pointToData(pixel) : null;\n  };\n\n  Calendar.prototype.containPoint = function (point) {\n    console.warn('Not implemented.');\n    return false;\n  };\n  /**\n   * initRange\n   * Normalize to an [start, end] array\n   */\n\n\n  Calendar.prototype._initRangeOption = function () {\n    var range = this._model.get('range');\n\n    var normalizedRange; // Convert [1990] to 1990\n\n    if (isArray(range) && range.length === 1) {\n      range = range[0];\n    }\n\n    if (!isArray(range)) {\n      var rangeStr = range.toString(); // One year.\n\n      if (/^\\d{4}$/.test(rangeStr)) {\n        normalizedRange = [rangeStr + '-01-01', rangeStr + '-12-31'];\n      } // One month\n\n\n      if (/^\\d{4}[\\/|-]\\d{1,2}$/.test(rangeStr)) {\n        var start = this.getDateInfo(rangeStr);\n        var firstDay = start.date;\n        firstDay.setMonth(firstDay.getMonth() + 1);\n        var end = this.getNextNDay(firstDay, -1);\n        normalizedRange = [start.formatedDate, end.formatedDate];\n      } // One day\n\n\n      if (/^\\d{4}[\\/|-]\\d{1,2}[\\/|-]\\d{1,2}$/.test(rangeStr)) {\n        normalizedRange = [rangeStr, rangeStr];\n      }\n    } else {\n      normalizedRange = range;\n    }\n\n    if (!normalizedRange) {\n      if (\"development\" !== 'production') {\n        logError('Invalid date range.');\n      } // Not handling it.\n\n\n      return range;\n    }\n\n    var tmp = this._getRangeInfo(normalizedRange);\n\n    if (tmp.start.time > tmp.end.time) {\n      normalizedRange.reverse();\n    }\n\n    return normalizedRange;\n  };\n  /**\n   * range info\n   *\n   * @private\n   * @param  {Array} range range ['2017-01-01', '2017-07-08']\n   *  If range[0] > range[1], they will not be reversed.\n   * @return {Object}       obj\n   */\n\n\n  Calendar.prototype._getRangeInfo = function (range) {\n    var parsedRange = [this.getDateInfo(range[0]), this.getDateInfo(range[1])];\n    var reversed;\n\n    if (parsedRange[0].time > parsedRange[1].time) {\n      reversed = true;\n      parsedRange.reverse();\n    }\n\n    var allDay = Math.floor(parsedRange[1].time / PROXIMATE_ONE_DAY) - Math.floor(parsedRange[0].time / PROXIMATE_ONE_DAY) + 1; // Consider case1 (#11677 #10430):\n    // Set the system timezone as \"UK\", set the range to `['2016-07-01', '2016-12-31']`\n    // Consider case2:\n    // Firstly set system timezone as \"Time Zone: America/Toronto\",\n    // ```\n    // let first = new Date(1478412000000 - 3600 * 1000 * 2.5);\n    // let second = new Date(1478412000000);\n    // let allDays = Math.floor(second / ONE_DAY) - Math.floor(first / ONE_DAY) + 1;\n    // ```\n    // will get wrong result because of DST. So we should fix it.\n\n    var date = new Date(parsedRange[0].time);\n    var startDateNum = date.getDate();\n    var endDateNum = parsedRange[1].date.getDate();\n    date.setDate(startDateNum + allDay - 1); // The bias can not over a month, so just compare date.\n\n    var dateNum = date.getDate();\n\n    if (dateNum !== endDateNum) {\n      var sign = date.getTime() - parsedRange[1].time > 0 ? 1 : -1;\n\n      while ((dateNum = date.getDate()) !== endDateNum && (date.getTime() - parsedRange[1].time) * sign > 0) {\n        allDay -= sign;\n        date.setDate(dateNum - sign);\n      }\n    }\n\n    var weeks = Math.floor((allDay + parsedRange[0].day + 6) / 7);\n    var nthWeek = reversed ? -weeks + 1 : weeks - 1;\n    reversed && parsedRange.reverse();\n    return {\n      range: [parsedRange[0].formatedDate, parsedRange[1].formatedDate],\n      start: parsedRange[0],\n      end: parsedRange[1],\n      allDay: allDay,\n      weeks: weeks,\n      // From 0.\n      nthWeek: nthWeek,\n      fweek: parsedRange[0].day,\n      lweek: parsedRange[1].day\n    };\n  };\n  /**\n   * get date by nthWeeks and week day in range\n   *\n   * @private\n   * @param  {number} nthWeek the week\n   * @param  {number} day   the week day\n   * @param  {Array} range [d1, d2]\n   * @return {Object}\n   */\n\n\n  Calendar.prototype._getDateByWeeksAndDay = function (nthWeek, day, range) {\n    var rangeInfo = this._getRangeInfo(range);\n\n    if (nthWeek > rangeInfo.weeks || nthWeek === 0 && day < rangeInfo.fweek || nthWeek === rangeInfo.weeks && day > rangeInfo.lweek) {\n      return null;\n    }\n\n    var nthDay = (nthWeek - 1) * 7 - rangeInfo.fweek + day;\n    var date = new Date(rangeInfo.start.time);\n    date.setDate(+rangeInfo.start.d + nthDay);\n    return this.getDateInfo(date);\n  };\n\n  Calendar.create = function (ecModel, api) {\n    var calendarList = [];\n    ecModel.eachComponent('calendar', function (calendarModel) {\n      var calendar = new Calendar(calendarModel, ecModel, api);\n      calendarList.push(calendar);\n      calendarModel.coordinateSystem = calendar;\n    });\n    ecModel.eachSeries(function (calendarSeries) {\n      if (calendarSeries.get('coordinateSystem') === 'calendar') {\n        // Inject coordinate system\n        calendarSeries.coordinateSystem = calendarList[calendarSeries.get('calendarIndex') || 0];\n      }\n    });\n    return calendarList;\n  };\n\n  Calendar.dimensions = ['time', 'value'];\n  return Calendar;\n}();\n\nfunction getCoordSys$4(finder) {\n  var calendarModel = finder.calendarModel;\n  var seriesModel = finder.seriesModel;\n  var coordSys = calendarModel ? calendarModel.coordinateSystem : seriesModel ? seriesModel.coordinateSystem : null;\n  return coordSys;\n}\n\nfunction install$w(registers) {\n  registers.registerComponentModel(CalendarModel);\n  registers.registerComponentView(CalendarView);\n  registers.registerCoordinateSystem('calendar', Calendar);\n}\n\nfunction setKeyInfoToNewElOption(resultItem, newElOption) {\n  var existElOption = resultItem.existing; // Set id and type after id assigned.\n\n  newElOption.id = resultItem.keyInfo.id;\n  !newElOption.type && existElOption && (newElOption.type = existElOption.type); // Set parent id if not specified\n\n  if (newElOption.parentId == null) {\n    var newElParentOption = newElOption.parentOption;\n\n    if (newElParentOption) {\n      newElOption.parentId = newElParentOption.id;\n    } else if (existElOption) {\n      newElOption.parentId = existElOption.parentId;\n    }\n  } // Clear\n\n\n  newElOption.parentOption = null;\n}\n\nfunction isSetLoc(obj, props) {\n  var isSet;\n  each(props, function (prop) {\n    obj[prop] != null && obj[prop] !== 'auto' && (isSet = true);\n  });\n  return isSet;\n}\n\nfunction mergeNewElOptionToExist(existList, index, newElOption) {\n  // Update existing options, for `getOption` feature.\n  var newElOptCopy = extend({}, newElOption);\n  var existElOption = existList[index];\n  var $action = newElOption.$action || 'merge';\n\n  if ($action === 'merge') {\n    if (existElOption) {\n      if (\"development\" !== 'production') {\n        var newType = newElOption.type;\n        assert(!newType || existElOption.type === newType, 'Please set $action: \"replace\" to change `type`');\n      } // We can ensure that newElOptCopy and existElOption are not\n      // the same object, so `merge` will not change newElOptCopy.\n\n\n      merge(existElOption, newElOptCopy, true); // Rigid body, use ignoreSize.\n\n      mergeLayoutParam(existElOption, newElOptCopy, {\n        ignoreSize: true\n      }); // Will be used in render.\n\n      copyLayoutParams(newElOption, existElOption); // Copy transition info to new option so it can be used in the transition.\n      // DO IT AFTER merge\n\n      copyTransitionInfo(newElOption, existElOption);\n      copyTransitionInfo(newElOption, existElOption, 'shape');\n      copyTransitionInfo(newElOption, existElOption, 'style');\n      copyTransitionInfo(newElOption, existElOption, 'extra'); // Copy clipPath\n\n      newElOption.clipPath = existElOption.clipPath;\n    } else {\n      existList[index] = newElOptCopy;\n    }\n  } else if ($action === 'replace') {\n    existList[index] = newElOptCopy;\n  } else if ($action === 'remove') {\n    // null will be cleaned later.\n    existElOption && (existList[index] = null);\n  }\n}\n\nvar TRANSITION_PROPS_TO_COPY = ['transition', 'enterFrom', 'leaveTo'];\nvar ROOT_TRANSITION_PROPS_TO_COPY = TRANSITION_PROPS_TO_COPY.concat(['enterAnimation', 'updateAnimation', 'leaveAnimation']);\n\nfunction copyTransitionInfo(target, source, targetProp) {\n  if (targetProp) {\n    if (!target[targetProp] && source[targetProp]) {\n      // TODO avoid creating this empty object when there is no transition configuration.\n      target[targetProp] = {};\n    }\n\n    target = target[targetProp];\n    source = source[targetProp];\n  }\n\n  if (!target || !source) {\n    return;\n  }\n\n  var props = targetProp ? TRANSITION_PROPS_TO_COPY : ROOT_TRANSITION_PROPS_TO_COPY;\n\n  for (var i = 0; i < props.length; i++) {\n    var prop = props[i];\n\n    if (target[prop] == null && source[prop] != null) {\n      target[prop] = source[prop];\n    }\n  }\n}\n\nfunction setLayoutInfoToExist(existItem, newElOption) {\n  if (!existItem) {\n    return;\n  }\n\n  existItem.hv = newElOption.hv = [// Rigid body, don't care about `width`.\n  isSetLoc(newElOption, ['left', 'right']), // Rigid body, don't care about `height`.\n  isSetLoc(newElOption, ['top', 'bottom'])]; // Give default group size. Otherwise layout error may occur.\n\n  if (existItem.type === 'group') {\n    var existingGroupOpt = existItem;\n    var newGroupOpt = newElOption;\n    existingGroupOpt.width == null && (existingGroupOpt.width = newGroupOpt.width = 0);\n    existingGroupOpt.height == null && (existingGroupOpt.height = newGroupOpt.height = 0);\n  }\n}\n\nvar GraphicComponentModel =\n/** @class */\nfunction (_super) {\n  __extends(GraphicComponentModel, _super);\n\n  function GraphicComponentModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GraphicComponentModel.type;\n    _this.preventAutoZ = true;\n    return _this;\n  }\n\n  GraphicComponentModel.prototype.mergeOption = function (option, ecModel) {\n    // Prevent default merge to elements\n    var elements = this.option.elements;\n    this.option.elements = null;\n\n    _super.prototype.mergeOption.call(this, option, ecModel);\n\n    this.option.elements = elements;\n  };\n\n  GraphicComponentModel.prototype.optionUpdated = function (newOption, isInit) {\n    var thisOption = this.option;\n    var newList = (isInit ? thisOption : newOption).elements;\n    var existList = thisOption.elements = isInit ? [] : thisOption.elements;\n    var flattenedList = [];\n\n    this._flatten(newList, flattenedList, null);\n\n    var mappingResult = mappingToExists(existList, flattenedList, 'normalMerge'); // Clear elOptionsToUpdate\n\n    var elOptionsToUpdate = this._elOptionsToUpdate = [];\n    each(mappingResult, function (resultItem, index) {\n      var newElOption = resultItem.newOption;\n\n      if (\"development\" !== 'production') {\n        assert(isObject(newElOption) || resultItem.existing, 'Empty graphic option definition');\n      }\n\n      if (!newElOption) {\n        return;\n      }\n\n      elOptionsToUpdate.push(newElOption);\n      setKeyInfoToNewElOption(resultItem, newElOption);\n      mergeNewElOptionToExist(existList, index, newElOption);\n      setLayoutInfoToExist(existList[index], newElOption);\n    }, this); // Clean\n\n    thisOption.elements = filter(existList, function (item) {\n      // $action should be volatile, otherwise option gotten from\n      // `getOption` will contain unexpected $action.\n      item && delete item.$action;\n      return item != null;\n    });\n  };\n  /**\n   * Convert\n   * [{\n   *  type: 'group',\n   *  id: 'xx',\n   *  children: [{type: 'circle'}, {type: 'polygon'}]\n   * }]\n   * to\n   * [\n   *  {type: 'group', id: 'xx'},\n   *  {type: 'circle', parentId: 'xx'},\n   *  {type: 'polygon', parentId: 'xx'}\n   * ]\n   */\n\n\n  GraphicComponentModel.prototype._flatten = function (optionList, result, parentOption) {\n    each(optionList, function (option) {\n      if (!option) {\n        return;\n      }\n\n      if (parentOption) {\n        option.parentOption = parentOption;\n      }\n\n      result.push(option);\n      var children = option.children; // here we don't judge if option.type is `group`\n      // when new option doesn't provide `type`, it will cause that the children can't be updated.\n\n      if (children && children.length) {\n        this._flatten(children, result, option);\n      } // Deleting for JSON output, and for not affecting group creation.\n\n\n      delete option.children;\n    }, this);\n  }; // FIXME\n  // Pass to view using payload? setOption has a payload?\n\n\n  GraphicComponentModel.prototype.useElOptionsToUpdate = function () {\n    var els = this._elOptionsToUpdate; // Clear to avoid render duplicately when zooming.\n\n    this._elOptionsToUpdate = null;\n    return els;\n  };\n\n  GraphicComponentModel.type = 'graphic';\n  GraphicComponentModel.defaultOption = {\n    elements: [] // parentId: null\n\n  };\n  return GraphicComponentModel;\n}(ComponentModel);\n\nvar nonShapeGraphicElements = {\n  // Reserved but not supported in graphic component.\n  path: null,\n  compoundPath: null,\n  // Supported in graphic component.\n  group: Group,\n  image: ZRImage,\n  text: ZRText\n};\nvar inner$e = makeInner(); // ------------------------\n// View\n// ------------------------\n\nvar GraphicComponentView =\n/** @class */\nfunction (_super) {\n  __extends(GraphicComponentView, _super);\n\n  function GraphicComponentView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = GraphicComponentView.type;\n    return _this;\n  }\n\n  GraphicComponentView.prototype.init = function () {\n    this._elMap = createHashMap();\n  };\n\n  GraphicComponentView.prototype.render = function (graphicModel, ecModel, api) {\n    // Having leveraged between use cases and algorithm complexity, a very\n    // simple layout mechanism is used:\n    // The size(width/height) can be determined by itself or its parent (not\n    // implemented yet), but can not by its children. (Top-down travel)\n    // The location(x/y) can be determined by the bounding rect of itself\n    // (can including its descendants or not) and the size of its parent.\n    // (Bottom-up travel)\n    // When `chart.clear()` or `chart.setOption({...}, true)` with the same id,\n    // view will be reused.\n    if (graphicModel !== this._lastGraphicModel) {\n      this._clear();\n    }\n\n    this._lastGraphicModel = graphicModel;\n\n    this._updateElements(graphicModel);\n\n    this._relocate(graphicModel, api);\n  };\n  /**\n   * Update graphic elements.\n   */\n\n\n  GraphicComponentView.prototype._updateElements = function (graphicModel) {\n    var elOptionsToUpdate = graphicModel.useElOptionsToUpdate();\n\n    if (!elOptionsToUpdate) {\n      return;\n    }\n\n    var elMap = this._elMap;\n    var rootGroup = this.group;\n    var globalZ = graphicModel.get('z');\n    var globalZLevel = graphicModel.get('zlevel'); // Top-down tranverse to assign graphic settings to each elements.\n\n    each(elOptionsToUpdate, function (elOption) {\n      var id = convertOptionIdName(elOption.id, null);\n      var elExisting = id != null ? elMap.get(id) : null;\n      var parentId = convertOptionIdName(elOption.parentId, null);\n      var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup;\n      var elType = elOption.type;\n      var elOptionStyle = elOption.style;\n\n      if (elType === 'text' && elOptionStyle) {\n        // In top/bottom mode, textVerticalAlign should not be used, which cause\n        // inaccurately locating.\n        if (elOption.hv && elOption.hv[1]) {\n          elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = elOptionStyle.verticalAlign = elOptionStyle.align = null;\n        }\n      }\n\n      var textContentOption = elOption.textContent;\n      var textConfig = elOption.textConfig;\n\n      if (elOptionStyle && isEC4CompatibleStyle(elOptionStyle, elType, !!textConfig, !!textContentOption)) {\n        var convertResult = convertFromEC4CompatibleStyle(elOptionStyle, elType, true);\n\n        if (!textConfig && convertResult.textConfig) {\n          textConfig = elOption.textConfig = convertResult.textConfig;\n        }\n\n        if (!textContentOption && convertResult.textContent) {\n          textContentOption = convertResult.textContent;\n        }\n      } // Remove unnecessary props to avoid potential problems.\n\n\n      var elOptionCleaned = getCleanedElOption(elOption); // For simple, do not support parent change, otherwise reorder is needed.\n\n      if (\"development\" !== 'production') {\n        elExisting && assert(targetElParent === elExisting.parent, 'Changing parent is not supported.');\n      }\n\n      var $action = elOption.$action || 'merge';\n      var isMerge = $action === 'merge';\n      var isReplace = $action === 'replace';\n\n      if (isMerge) {\n        var isInit = !elExisting;\n        var el_1 = elExisting;\n\n        if (isInit) {\n          el_1 = createEl$1(id, targetElParent, elOption.type, elMap);\n        } else {\n          el_1 && (inner$e(el_1).isNew = false); // Stop and restore before update any other attributes.\n\n          stopPreviousKeyframeAnimationAndRestore(el_1);\n        }\n\n        if (el_1) {\n          applyUpdateTransition(el_1, elOptionCleaned, graphicModel, {\n            isInit: isInit\n          });\n          updateCommonAttrs(el_1, elOption, globalZ, globalZLevel);\n        }\n      } else if (isReplace) {\n        removeEl(elExisting, elOption, elMap, graphicModel);\n        var el_2 = createEl$1(id, targetElParent, elOption.type, elMap);\n\n        if (el_2) {\n          applyUpdateTransition(el_2, elOptionCleaned, graphicModel, {\n            isInit: true\n          });\n          updateCommonAttrs(el_2, elOption, globalZ, globalZLevel);\n        }\n      } else if ($action === 'remove') {\n        updateLeaveTo(elExisting, elOption);\n        removeEl(elExisting, elOption, elMap, graphicModel);\n      }\n\n      var el = elMap.get(id);\n\n      if (el && textContentOption) {\n        if (isMerge) {\n          var textContentExisting = el.getTextContent();\n          textContentExisting ? textContentExisting.attr(textContentOption) : el.setTextContent(new ZRText(textContentOption));\n        } else if (isReplace) {\n          el.setTextContent(new ZRText(textContentOption));\n        }\n      }\n\n      if (el) {\n        var clipPathOption = elOption.clipPath;\n\n        if (clipPathOption) {\n          var clipPathType = clipPathOption.type;\n          var clipPath = void 0;\n          var isInit = false;\n\n          if (isMerge) {\n            var oldClipPath = el.getClipPath();\n            isInit = !oldClipPath || inner$e(oldClipPath).type !== clipPathType;\n            clipPath = isInit ? newEl(clipPathType) : oldClipPath;\n          } else if (isReplace) {\n            isInit = true;\n            clipPath = newEl(clipPathType);\n          }\n\n          el.setClipPath(clipPath);\n          applyUpdateTransition(clipPath, clipPathOption, graphicModel, {\n            isInit: isInit\n          });\n          applyKeyframeAnimation(clipPath, clipPathOption.keyframeAnimation, graphicModel);\n        }\n\n        var elInner = inner$e(el);\n        el.setTextConfig(textConfig);\n        elInner.option = elOption;\n        setEventData(el, graphicModel, elOption);\n        setTooltipConfig({\n          el: el,\n          componentModel: graphicModel,\n          itemName: el.name,\n          itemTooltipOption: elOption.tooltip\n        });\n        applyKeyframeAnimation(el, elOption.keyframeAnimation, graphicModel);\n      }\n    });\n  };\n  /**\n   * Locate graphic elements.\n   */\n\n\n  GraphicComponentView.prototype._relocate = function (graphicModel, api) {\n    var elOptions = graphicModel.option.elements;\n    var rootGroup = this.group;\n    var elMap = this._elMap;\n    var apiWidth = api.getWidth();\n    var apiHeight = api.getHeight();\n    var xy = ['x', 'y']; // Top-down to calculate percentage width/height of group\n\n    for (var i = 0; i < elOptions.length; i++) {\n      var elOption = elOptions[i];\n      var id = convertOptionIdName(elOption.id, null);\n      var el = id != null ? elMap.get(id) : null;\n\n      if (!el || !el.isGroup) {\n        continue;\n      }\n\n      var parentEl = el.parent;\n      var isParentRoot = parentEl === rootGroup; // Like 'position:absolut' in css, default 0.\n\n      var elInner = inner$e(el);\n      var parentElInner = inner$e(parentEl);\n      elInner.width = parsePercent$1(elInner.option.width, isParentRoot ? apiWidth : parentElInner.width) || 0;\n      elInner.height = parsePercent$1(elInner.option.height, isParentRoot ? apiHeight : parentElInner.height) || 0;\n    } // Bottom-up tranvese all elements (consider ec resize) to locate elements.\n\n\n    for (var i = elOptions.length - 1; i >= 0; i--) {\n      var elOption = elOptions[i];\n      var id = convertOptionIdName(elOption.id, null);\n      var el = id != null ? elMap.get(id) : null;\n\n      if (!el) {\n        continue;\n      }\n\n      var parentEl = el.parent;\n      var parentElInner = inner$e(parentEl);\n      var containerInfo = parentEl === rootGroup ? {\n        width: apiWidth,\n        height: apiHeight\n      } : {\n        width: parentElInner.width,\n        height: parentElInner.height\n      }; // PENDING\n      // Currently, when `bounding: 'all'`, the union bounding rect of the group\n      // does not include the rect of [0, 0, group.width, group.height], which\n      // is probably weird for users. Should we make a break change for it?\n\n      var layoutPos = {};\n      var layouted = positionElement(el, elOption, containerInfo, null, {\n        hv: elOption.hv,\n        boundingMode: elOption.bounding\n      }, layoutPos);\n\n      if (!inner$e(el).isNew && layouted) {\n        var transition = elOption.transition;\n        var animatePos = {};\n\n        for (var k = 0; k < xy.length; k++) {\n          var key = xy[k];\n          var val = layoutPos[key];\n\n          if (transition && (isTransitionAll(transition) || indexOf(transition, key) >= 0)) {\n            animatePos[key] = val;\n          } else {\n            el[key] = val;\n          }\n        }\n\n        updateProps(el, animatePos, graphicModel, 0);\n      } else {\n        el.attr(layoutPos);\n      }\n    }\n  };\n  /**\n   * Clear all elements.\n   */\n\n\n  GraphicComponentView.prototype._clear = function () {\n    var _this = this;\n\n    var elMap = this._elMap;\n    elMap.each(function (el) {\n      removeEl(el, inner$e(el).option, elMap, _this._lastGraphicModel);\n    });\n    this._elMap = createHashMap();\n  };\n\n  GraphicComponentView.prototype.dispose = function () {\n    this._clear();\n  };\n\n  GraphicComponentView.type = 'graphic';\n  return GraphicComponentView;\n}(ComponentView);\n\nfunction newEl(graphicType) {\n  if (\"development\" !== 'production') {\n    assert(graphicType, 'graphic type MUST be set');\n  }\n\n  var Clz = hasOwn(nonShapeGraphicElements, graphicType) // Those graphic elements are not shapes. They should not be\n  // overwritten by users, so do them first.\n  ? nonShapeGraphicElements[graphicType] : getShapeClass(graphicType);\n\n  if (\"development\" !== 'production') {\n    assert(Clz, \"graphic type \" + graphicType + \" can not be found\");\n  }\n\n  var el = new Clz({});\n  inner$e(el).type = graphicType;\n  return el;\n}\n\nfunction createEl$1(id, targetElParent, graphicType, elMap) {\n  var el = newEl(graphicType);\n  targetElParent.add(el);\n  elMap.set(id, el);\n  inner$e(el).id = id;\n  inner$e(el).isNew = true;\n  return el;\n}\n\nfunction removeEl(elExisting, elOption, elMap, graphicModel) {\n  var existElParent = elExisting && elExisting.parent;\n\n  if (existElParent) {\n    elExisting.type === 'group' && elExisting.traverse(function (el) {\n      removeEl(el, elOption, elMap, graphicModel);\n    });\n    applyLeaveTransition(elExisting, elOption, graphicModel);\n    elMap.removeKey(inner$e(elExisting).id);\n  }\n}\n\nfunction updateCommonAttrs(el, elOption, defaultZ, defaultZlevel) {\n  if (!el.isGroup) {\n    each([['cursor', Displayable.prototype.cursor], // We should not support configure z and zlevel in the element level.\n    // But seems we didn't limit it previously. So here still use it to avoid breaking.\n    ['zlevel', defaultZlevel || 0], ['z', defaultZ || 0], // z2 must not be null/undefined, otherwise sort error may occur.\n    ['z2', 0]], function (item) {\n      var prop = item[0];\n\n      if (hasOwn(elOption, prop)) {\n        el[prop] = retrieve2(elOption[prop], item[1]);\n      } else if (el[prop] == null) {\n        el[prop] = item[1];\n      }\n    });\n  }\n\n  each(keys(elOption), function (key) {\n    // Assign event handlers.\n    // PENDING: should enumerate all event names or use pattern matching?\n    if (key.indexOf('on') === 0) {\n      var val = elOption[key];\n      el[key] = isFunction(val) ? val : null;\n    }\n  });\n\n  if (hasOwn(elOption, 'draggable')) {\n    el.draggable = elOption.draggable;\n  } // Other attributes\n\n\n  elOption.name != null && (el.name = elOption.name);\n  elOption.id != null && (el.id = elOption.id);\n} // Remove unnecessary props to avoid potential problems.\n\n\nfunction getCleanedElOption(elOption) {\n  elOption = extend({}, elOption);\n  each(['id', 'parentId', '$action', 'hv', 'bounding', 'textContent', 'clipPath'].concat(LOCATION_PARAMS), function (name) {\n    delete elOption[name];\n  });\n  return elOption;\n}\n\nfunction setEventData(el, graphicModel, elOption) {\n  var eventData = getECData(el).eventData; // Simple optimize for large amount of elements that no need event.\n\n  if (!el.silent && !el.ignore && !eventData) {\n    eventData = getECData(el).eventData = {\n      componentType: 'graphic',\n      componentIndex: graphicModel.componentIndex,\n      name: el.name\n    };\n  } // `elOption.info` enables user to mount some info on\n  // elements and use them in event handlers.\n\n\n  if (eventData) {\n    eventData.info = elOption.info;\n  }\n}\n\nfunction install$x(registers) {\n  registers.registerComponentModel(GraphicComponentModel);\n  registers.registerComponentView(GraphicComponentView);\n  registers.registerPreprocessor(function (option) {\n    var graphicOption = option.graphic; // Convert\n    // {graphic: [{left: 10, type: 'circle'}, ...]}\n    // or\n    // {graphic: {left: 10, type: 'circle'}}\n    // to\n    // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]}\n\n    if (isArray(graphicOption)) {\n      if (!graphicOption[0] || !graphicOption[0].elements) {\n        option.graphic = [{\n          elements: graphicOption\n        }];\n      } else {\n        // Only one graphic instance can be instantiated. (We don't\n        // want that too many views are created in echarts._viewMap.)\n        option.graphic = [option.graphic[0]];\n      }\n    } else if (graphicOption && !graphicOption.elements) {\n      option.graphic = [{\n        elements: [graphicOption]\n      }];\n    }\n  });\n}\n\nvar DATA_ZOOM_AXIS_DIMENSIONS = ['x', 'y', 'radius', 'angle', 'single']; // Supported coords.\n// FIXME: polar has been broken (but rarely used).\n\nvar SERIES_COORDS = ['cartesian2d', 'polar', 'singleAxis'];\nfunction isCoordSupported(seriesModel) {\n  var coordType = seriesModel.get('coordinateSystem');\n  return indexOf(SERIES_COORDS, coordType) >= 0;\n}\nfunction getAxisMainType(axisDim) {\n  if (\"development\" !== 'production') {\n    assert(axisDim);\n  }\n\n  return axisDim + 'Axis';\n}\n/**\n * If two dataZoomModels has the same axis controlled, we say that they are 'linked'.\n * This function finds all linked dataZoomModels start from the given payload.\n */\n\nfunction findEffectedDataZooms(ecModel, payload) {\n  // Key: `DataZoomAxisDimension`\n  var axisRecords = createHashMap();\n  var effectedModels = []; // Key: uid of dataZoomModel\n\n  var effectedModelMap = createHashMap(); // Find the dataZooms specified by payload.\n\n  ecModel.eachComponent({\n    mainType: 'dataZoom',\n    query: payload\n  }, function (dataZoomModel) {\n    if (!effectedModelMap.get(dataZoomModel.uid)) {\n      addToEffected(dataZoomModel);\n    }\n  }); // Start from the given dataZoomModels, travel the graph to find\n  // all of the linked dataZoom models.\n\n  var foundNewLink;\n\n  do {\n    foundNewLink = false;\n    ecModel.eachComponent('dataZoom', processSingle);\n  } while (foundNewLink);\n\n  function processSingle(dataZoomModel) {\n    if (!effectedModelMap.get(dataZoomModel.uid) && isLinked(dataZoomModel)) {\n      addToEffected(dataZoomModel);\n      foundNewLink = true;\n    }\n  }\n\n  function addToEffected(dataZoom) {\n    effectedModelMap.set(dataZoom.uid, true);\n    effectedModels.push(dataZoom);\n    markAxisControlled(dataZoom);\n  }\n\n  function isLinked(dataZoomModel) {\n    var isLink = false;\n    dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n      var axisIdxArr = axisRecords.get(axisDim);\n\n      if (axisIdxArr && axisIdxArr[axisIndex]) {\n        isLink = true;\n      }\n    });\n    return isLink;\n  }\n\n  function markAxisControlled(dataZoomModel) {\n    dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n      (axisRecords.get(axisDim) || axisRecords.set(axisDim, []))[axisIndex] = true;\n    });\n  }\n\n  return effectedModels;\n}\n/**\n * Find the first target coordinate system.\n * Available after model built.\n *\n * @return Like {\n *                  grid: [\n *                      {model: coord0, axisModels: [axis1, axis3], coordIndex: 1},\n *                      {model: coord1, axisModels: [axis0, axis2], coordIndex: 0},\n *                      ...\n *                  ],  // cartesians must not be null/undefined.\n *                  polar: [\n *                      {model: coord0, axisModels: [axis4], coordIndex: 0},\n *                      ...\n *                  ],  // polars must not be null/undefined.\n *                  singleAxis: [\n *                      {model: coord0, axisModels: [], coordIndex: 0}\n *                  ]\n *              }\n */\n\nfunction collectReferCoordSysModelInfo(dataZoomModel) {\n  var ecModel = dataZoomModel.ecModel;\n  var coordSysInfoWrap = {\n    infoList: [],\n    infoMap: createHashMap()\n  };\n  dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n    var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n\n    if (!axisModel) {\n      return;\n    }\n\n    var coordSysModel = axisModel.getCoordSysModel();\n\n    if (!coordSysModel) {\n      return;\n    }\n\n    var coordSysUid = coordSysModel.uid;\n    var coordSysInfo = coordSysInfoWrap.infoMap.get(coordSysUid);\n\n    if (!coordSysInfo) {\n      coordSysInfo = {\n        model: coordSysModel,\n        axisModels: []\n      };\n      coordSysInfoWrap.infoList.push(coordSysInfo);\n      coordSysInfoWrap.infoMap.set(coordSysUid, coordSysInfo);\n    }\n\n    coordSysInfo.axisModels.push(axisModel);\n  });\n  return coordSysInfoWrap;\n}\n\nvar DataZoomAxisInfo =\n/** @class */\nfunction () {\n  function DataZoomAxisInfo() {\n    this.indexList = [];\n    this.indexMap = [];\n  }\n\n  DataZoomAxisInfo.prototype.add = function (axisCmptIdx) {\n    // Remove duplication.\n    if (!this.indexMap[axisCmptIdx]) {\n      this.indexList.push(axisCmptIdx);\n      this.indexMap[axisCmptIdx] = true;\n    }\n  };\n\n  return DataZoomAxisInfo;\n}();\n\nvar DataZoomModel =\n/** @class */\nfunction (_super) {\n  __extends(DataZoomModel, _super);\n\n  function DataZoomModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = DataZoomModel.type;\n    _this._autoThrottle = true;\n    _this._noTarget = true;\n    /**\n     * It is `[rangeModeForMin, rangeModeForMax]`.\n     * The optional values for `rangeMode`:\n     * + `'value'` mode: the axis extent will always be determined by\n     *     `dataZoom.startValue` and `dataZoom.endValue`, despite\n     *     how data like and how `axis.min` and `axis.max` are.\n     * + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`,\n     *     where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`,\n     *     and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`.\n     *     Axis extent will be determined by the result of the percent of `[dMin, dMax]`.\n     *\n     * For example, when users are using dynamic data (update data periodically via `setOption`),\n     * if in `'value`' mode, the window will be kept in a fixed value range despite how\n     * data are appended, while if in `'percent'` mode, whe window range will be changed alone with\n     * the appended data (suppose `axis.min` and `axis.max` are not specified).\n     */\n\n    _this._rangePropMode = ['percent', 'percent'];\n    return _this;\n  }\n\n  DataZoomModel.prototype.init = function (option, parentModel, ecModel) {\n    var inputRawOption = retrieveRawOption(option);\n    /**\n     * Suppose a \"main process\" start at the point that model prepared (that is,\n     * model initialized or merged or method called in `action`).\n     * We should keep the `main process` idempotent, that is, given a set of values\n     * on `option`, we get the same result.\n     *\n     * But sometimes, values on `option` will be updated for providing users\n     * a \"final calculated value\" (`dataZoomProcessor` will do that). Those value\n     * should not be the base/input of the `main process`.\n     *\n     * So in that case we should save and keep the input of the `main process`\n     * separately, called `settledOption`.\n     *\n     * For example, consider the case:\n     * (Step_1) brush zoom the grid by `toolbox.dataZoom`,\n     *     where the original input `option.startValue`, `option.endValue` are earsed by\n     *     calculated value.\n     * (Step)2) click the legend to hide and show a series,\n     *     where the new range is calculated by the earsed `startValue` and `endValue`,\n     *     which brings incorrect result.\n     */\n\n    this.settledOption = inputRawOption;\n    this.mergeDefaultAndTheme(option, ecModel);\n\n    this._doInit(inputRawOption);\n  };\n\n  DataZoomModel.prototype.mergeOption = function (newOption) {\n    var inputRawOption = retrieveRawOption(newOption); // FIX #2591\n\n    merge(this.option, newOption, true);\n    merge(this.settledOption, inputRawOption, true);\n\n    this._doInit(inputRawOption);\n  };\n\n  DataZoomModel.prototype._doInit = function (inputRawOption) {\n    var thisOption = this.option;\n\n    this._setDefaultThrottle(inputRawOption);\n\n    this._updateRangeUse(inputRawOption);\n\n    var settledOption = this.settledOption;\n    each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {\n      // start/end has higher priority over startValue/endValue if they\n      // both set, but we should make chart.setOption({endValue: 1000})\n      // effective, rather than chart.setOption({endValue: 1000, end: null}).\n      if (this._rangePropMode[index] === 'value') {\n        thisOption[names[0]] = settledOption[names[0]] = null;\n      } // Otherwise do nothing and use the merge result.\n\n    }, this);\n\n    this._resetTarget();\n  };\n\n  DataZoomModel.prototype._resetTarget = function () {\n    var optionOrient = this.get('orient', true);\n    var targetAxisIndexMap = this._targetAxisInfoMap = createHashMap();\n\n    var hasAxisSpecified = this._fillSpecifiedTargetAxis(targetAxisIndexMap);\n\n    if (hasAxisSpecified) {\n      this._orient = optionOrient || this._makeAutoOrientByTargetAxis();\n    } else {\n      this._orient = optionOrient || 'horizontal';\n\n      this._fillAutoTargetAxisByOrient(targetAxisIndexMap, this._orient);\n    }\n\n    this._noTarget = true;\n    targetAxisIndexMap.each(function (axisInfo) {\n      if (axisInfo.indexList.length) {\n        this._noTarget = false;\n      }\n    }, this);\n  };\n\n  DataZoomModel.prototype._fillSpecifiedTargetAxis = function (targetAxisIndexMap) {\n    var hasAxisSpecified = false;\n    each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) {\n      var refering = this.getReferringComponents(getAxisMainType(axisDim), MULTIPLE_REFERRING); // When user set axisIndex as a empty array, we think that user specify axisIndex\n      // but do not want use auto mode. Because empty array may be encountered when\n      // some error occurred.\n\n      if (!refering.specified) {\n        return;\n      }\n\n      hasAxisSpecified = true;\n      var axisInfo = new DataZoomAxisInfo();\n      each(refering.models, function (axisModel) {\n        axisInfo.add(axisModel.componentIndex);\n      });\n      targetAxisIndexMap.set(axisDim, axisInfo);\n    }, this);\n    return hasAxisSpecified;\n  };\n\n  DataZoomModel.prototype._fillAutoTargetAxisByOrient = function (targetAxisIndexMap, orient) {\n    var ecModel = this.ecModel;\n    var needAuto = true; // Find axis that parallel to dataZoom as default.\n\n    if (needAuto) {\n      var axisDim = orient === 'vertical' ? 'y' : 'x';\n      var axisModels = ecModel.findComponents({\n        mainType: axisDim + 'Axis'\n      });\n      setParallelAxis(axisModels, axisDim);\n    } // Find axis that parallel to dataZoom as default.\n\n\n    if (needAuto) {\n      var axisModels = ecModel.findComponents({\n        mainType: 'singleAxis',\n        filter: function (axisModel) {\n          return axisModel.get('orient', true) === orient;\n        }\n      });\n      setParallelAxis(axisModels, 'single');\n    }\n\n    function setParallelAxis(axisModels, axisDim) {\n      // At least use the first parallel axis as the target axis.\n      var axisModel = axisModels[0];\n\n      if (!axisModel) {\n        return;\n      }\n\n      var axisInfo = new DataZoomAxisInfo();\n      axisInfo.add(axisModel.componentIndex);\n      targetAxisIndexMap.set(axisDim, axisInfo);\n      needAuto = false; // Find parallel axes in the same grid.\n\n      if (axisDim === 'x' || axisDim === 'y') {\n        var gridModel_1 = axisModel.getReferringComponents('grid', SINGLE_REFERRING).models[0];\n        gridModel_1 && each(axisModels, function (axModel) {\n          if (axisModel.componentIndex !== axModel.componentIndex && gridModel_1 === axModel.getReferringComponents('grid', SINGLE_REFERRING).models[0]) {\n            axisInfo.add(axModel.componentIndex);\n          }\n        });\n      }\n    }\n\n    if (needAuto) {\n      // If no parallel axis, find the first category axis as default. (Also consider polar).\n      each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) {\n        if (!needAuto) {\n          return;\n        }\n\n        var axisModels = ecModel.findComponents({\n          mainType: getAxisMainType(axisDim),\n          filter: function (axisModel) {\n            return axisModel.get('type', true) === 'category';\n          }\n        });\n\n        if (axisModels[0]) {\n          var axisInfo = new DataZoomAxisInfo();\n          axisInfo.add(axisModels[0].componentIndex);\n          targetAxisIndexMap.set(axisDim, axisInfo);\n          needAuto = false;\n        }\n      }, this);\n    }\n  };\n\n  DataZoomModel.prototype._makeAutoOrientByTargetAxis = function () {\n    var dim; // Find the first axis\n\n    this.eachTargetAxis(function (axisDim) {\n      !dim && (dim = axisDim);\n    }, this);\n    return dim === 'y' ? 'vertical' : 'horizontal';\n  };\n\n  DataZoomModel.prototype._setDefaultThrottle = function (inputRawOption) {\n    // When first time user set throttle, auto throttle ends.\n    if (inputRawOption.hasOwnProperty('throttle')) {\n      this._autoThrottle = false;\n    }\n\n    if (this._autoThrottle) {\n      var globalOption = this.ecModel.option;\n      this.option.throttle = globalOption.animation && globalOption.animationDurationUpdate > 0 ? 100 : 20;\n    }\n  };\n\n  DataZoomModel.prototype._updateRangeUse = function (inputRawOption) {\n    var rangePropMode = this._rangePropMode;\n    var rangeModeInOption = this.get('rangeMode');\n    each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {\n      var percentSpecified = inputRawOption[names[0]] != null;\n      var valueSpecified = inputRawOption[names[1]] != null;\n\n      if (percentSpecified && !valueSpecified) {\n        rangePropMode[index] = 'percent';\n      } else if (!percentSpecified && valueSpecified) {\n        rangePropMode[index] = 'value';\n      } else if (rangeModeInOption) {\n        rangePropMode[index] = rangeModeInOption[index];\n      } else if (percentSpecified) {\n        // percentSpecified && valueSpecified\n        rangePropMode[index] = 'percent';\n      } // else remain its original setting.\n\n    });\n  };\n\n  DataZoomModel.prototype.noTarget = function () {\n    return this._noTarget;\n  };\n\n  DataZoomModel.prototype.getFirstTargetAxisModel = function () {\n    var firstAxisModel;\n    this.eachTargetAxis(function (axisDim, axisIndex) {\n      if (firstAxisModel == null) {\n        firstAxisModel = this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n      }\n    }, this);\n    return firstAxisModel;\n  };\n  /**\n   * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel\n   */\n\n\n  DataZoomModel.prototype.eachTargetAxis = function (callback, context) {\n    this._targetAxisInfoMap.each(function (axisInfo, axisDim) {\n      each(axisInfo.indexList, function (axisIndex) {\n        callback.call(context, axisDim, axisIndex);\n      });\n    });\n  };\n  /**\n   * @return If not found, return null/undefined.\n   */\n\n\n  DataZoomModel.prototype.getAxisProxy = function (axisDim, axisIndex) {\n    var axisModel = this.getAxisModel(axisDim, axisIndex);\n\n    if (axisModel) {\n      return axisModel.__dzAxisProxy;\n    }\n  };\n  /**\n   * @return If not found, return null/undefined.\n   */\n\n\n  DataZoomModel.prototype.getAxisModel = function (axisDim, axisIndex) {\n    if (\"development\" !== 'production') {\n      assert(axisDim && axisIndex != null);\n    }\n\n    var axisInfo = this._targetAxisInfoMap.get(axisDim);\n\n    if (axisInfo && axisInfo.indexMap[axisIndex]) {\n      return this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n    }\n  };\n  /**\n   * If not specified, set to undefined.\n   */\n\n\n  DataZoomModel.prototype.setRawRange = function (opt) {\n    var thisOption = this.option;\n    var settledOption = this.settledOption;\n    each([['start', 'startValue'], ['end', 'endValue']], function (names) {\n      // Consider the pair <start, startValue>:\n      // If one has value and the other one is `null/undefined`, we both set them\n      // to `settledOption`. This strategy enables the feature to clear the original\n      // value in `settledOption` to `null/undefined`.\n      // But if both of them are `null/undefined`, we do not set them to `settledOption`\n      // and keep `settledOption` with the original value. This strategy enables users to\n      // only set <end or endValue> but not set <start or startValue> when calling\n      // `dispatchAction`.\n      // The pair <end, endValue> is treated in the same way.\n      if (opt[names[0]] != null || opt[names[1]] != null) {\n        thisOption[names[0]] = settledOption[names[0]] = opt[names[0]];\n        thisOption[names[1]] = settledOption[names[1]] = opt[names[1]];\n      }\n    }, this);\n\n    this._updateRangeUse(opt);\n  };\n\n  DataZoomModel.prototype.setCalculatedRange = function (opt) {\n    var option = this.option;\n    each(['start', 'startValue', 'end', 'endValue'], function (name) {\n      option[name] = opt[name];\n    });\n  };\n\n  DataZoomModel.prototype.getPercentRange = function () {\n    var axisProxy = this.findRepresentativeAxisProxy();\n\n    if (axisProxy) {\n      return axisProxy.getDataPercentWindow();\n    }\n  };\n  /**\n   * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);\n   *\n   * @return [startValue, endValue] value can only be '-' or finite number.\n   */\n\n\n  DataZoomModel.prototype.getValueRange = function (axisDim, axisIndex) {\n    if (axisDim == null && axisIndex == null) {\n      var axisProxy = this.findRepresentativeAxisProxy();\n\n      if (axisProxy) {\n        return axisProxy.getDataValueWindow();\n      }\n    } else {\n      return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow();\n    }\n  };\n  /**\n   * @param axisModel If axisModel given, find axisProxy\n   *      corresponding to the axisModel\n   */\n\n\n  DataZoomModel.prototype.findRepresentativeAxisProxy = function (axisModel) {\n    if (axisModel) {\n      return axisModel.__dzAxisProxy;\n    } // Find the first hosted axisProxy\n\n\n    var firstProxy;\n\n    var axisDimList = this._targetAxisInfoMap.keys();\n\n    for (var i = 0; i < axisDimList.length; i++) {\n      var axisDim = axisDimList[i];\n\n      var axisInfo = this._targetAxisInfoMap.get(axisDim);\n\n      for (var j = 0; j < axisInfo.indexList.length; j++) {\n        var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]);\n\n        if (proxy.hostedBy(this)) {\n          return proxy;\n        }\n\n        if (!firstProxy) {\n          firstProxy = proxy;\n        }\n      }\n    } // If no hosted proxy found, still need to return a proxy.\n    // This case always happens in toolbox dataZoom, where axes are all hosted by\n    // other dataZooms.\n\n\n    return firstProxy;\n  };\n\n  DataZoomModel.prototype.getRangePropMode = function () {\n    return this._rangePropMode.slice();\n  };\n\n  DataZoomModel.prototype.getOrient = function () {\n    if (\"development\" !== 'production') {\n      // Should not be called before initialized.\n      assert(this._orient);\n    }\n\n    return this._orient;\n  };\n\n  DataZoomModel.type = 'dataZoom';\n  DataZoomModel.dependencies = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series', 'toolbox'];\n  DataZoomModel.defaultOption = {\n    // zlevel: 0,\n    z: 4,\n    filterMode: 'filter',\n    start: 0,\n    end: 100\n  };\n  return DataZoomModel;\n}(ComponentModel);\n/**\n * Retrieve those raw params from option, which will be cached separately,\n * because they will be overwritten by normalized/calculated values in the main\n * process.\n */\n\n\nfunction retrieveRawOption(option) {\n  var ret = {};\n  each(['start', 'end', 'startValue', 'endValue', 'throttle'], function (name) {\n    option.hasOwnProperty(name) && (ret[name] = option[name]);\n  });\n  return ret;\n}\n\nvar SelectDataZoomModel =\n/** @class */\nfunction (_super) {\n  __extends(SelectDataZoomModel, _super);\n\n  function SelectDataZoomModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SelectDataZoomModel.type;\n    return _this;\n  }\n\n  SelectDataZoomModel.type = 'dataZoom.select';\n  return SelectDataZoomModel;\n}(DataZoomModel);\n\nvar DataZoomView =\n/** @class */\nfunction (_super) {\n  __extends(DataZoomView, _super);\n\n  function DataZoomView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = DataZoomView.type;\n    return _this;\n  }\n\n  DataZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) {\n    this.dataZoomModel = dataZoomModel;\n    this.ecModel = ecModel;\n    this.api = api;\n  };\n\n  DataZoomView.type = 'dataZoom';\n  return DataZoomView;\n}(ComponentView);\n\nvar SelectDataZoomView =\n/** @class */\nfunction (_super) {\n  __extends(SelectDataZoomView, _super);\n\n  function SelectDataZoomView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SelectDataZoomView.type;\n    return _this;\n  }\n\n  SelectDataZoomView.type = 'dataZoom.select';\n  return SelectDataZoomView;\n}(DataZoomView);\n\nvar each$8 = each;\nvar asc$1 = asc;\n/**\n * Operate single axis.\n * One axis can only operated by one axis operator.\n * Different dataZoomModels may be defined to operate the same axis.\n * (i.e. 'inside' data zoom and 'slider' data zoom components)\n * So dataZoomModels share one axisProxy in that case.\n */\n\nvar AxisProxy =\n/** @class */\nfunction () {\n  function AxisProxy(dimName, axisIndex, dataZoomModel, ecModel) {\n    this._dimName = dimName;\n    this._axisIndex = axisIndex;\n    this.ecModel = ecModel;\n    this._dataZoomModel = dataZoomModel; // /**\n    //  * @readOnly\n    //  * @private\n    //  */\n    // this.hasSeriesStacked;\n  }\n  /**\n   * Whether the axisProxy is hosted by dataZoomModel.\n   */\n\n\n  AxisProxy.prototype.hostedBy = function (dataZoomModel) {\n    return this._dataZoomModel === dataZoomModel;\n  };\n  /**\n   * @return Value can only be NaN or finite value.\n   */\n\n\n  AxisProxy.prototype.getDataValueWindow = function () {\n    return this._valueWindow.slice();\n  };\n  /**\n   * @return {Array.<number>}\n   */\n\n\n  AxisProxy.prototype.getDataPercentWindow = function () {\n    return this._percentWindow.slice();\n  };\n\n  AxisProxy.prototype.getTargetSeriesModels = function () {\n    var seriesModels = [];\n    this.ecModel.eachSeries(function (seriesModel) {\n      if (isCoordSupported(seriesModel)) {\n        var axisMainType = getAxisMainType(this._dimName);\n        var axisModel = seriesModel.getReferringComponents(axisMainType, SINGLE_REFERRING).models[0];\n\n        if (axisModel && this._axisIndex === axisModel.componentIndex) {\n          seriesModels.push(seriesModel);\n        }\n      }\n    }, this);\n    return seriesModels;\n  };\n\n  AxisProxy.prototype.getAxisModel = function () {\n    return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex);\n  };\n\n  AxisProxy.prototype.getMinMaxSpan = function () {\n    return clone(this._minMaxSpan);\n  };\n  /**\n   * Only calculate by given range and this._dataExtent, do not change anything.\n   */\n\n\n  AxisProxy.prototype.calculateDataWindow = function (opt) {\n    var dataExtent = this._dataExtent;\n    var axisModel = this.getAxisModel();\n    var scale = axisModel.axis.scale;\n\n    var rangePropMode = this._dataZoomModel.getRangePropMode();\n\n    var percentExtent = [0, 100];\n    var percentWindow = [];\n    var valueWindow = [];\n    var hasPropModeValue;\n    each$8(['start', 'end'], function (prop, idx) {\n      var boundPercent = opt[prop];\n      var boundValue = opt[prop + 'Value']; // Notice: dataZoom is based either on `percentProp` ('start', 'end') or\n      // on `valueProp` ('startValue', 'endValue'). (They are based on the data extent\n      // but not min/max of axis, which will be calculated by data window then).\n      // The former one is suitable for cases that a dataZoom component controls multiple\n      // axes with different unit or extent, and the latter one is suitable for accurate\n      // zoom by pixel (e.g., in dataZoomSelect).\n      // we use `getRangePropMode()` to mark which prop is used. `rangePropMode` is updated\n      // only when setOption or dispatchAction, otherwise it remains its original value.\n      // (Why not only record `percentProp` and always map to `valueProp`? Because\n      // the map `valueProp` -> `percentProp` -> `valueProp` probably not the original\n      // `valueProp`. consider two axes constrolled by one dataZoom. They have different\n      // data extent. All of values that are overflow the `dataExtent` will be calculated\n      // to percent '100%').\n\n      if (rangePropMode[idx] === 'percent') {\n        boundPercent == null && (boundPercent = percentExtent[idx]); // Use scale.parse to math round for category or time axis.\n\n        boundValue = scale.parse(linearMap(boundPercent, percentExtent, dataExtent));\n      } else {\n        hasPropModeValue = true;\n        boundValue = boundValue == null ? dataExtent[idx] : scale.parse(boundValue); // Calculating `percent` from `value` may be not accurate, because\n        // This calculation can not be inversed, because all of values that\n        // are overflow the `dataExtent` will be calculated to percent '100%'\n\n        boundPercent = linearMap(boundValue, dataExtent, percentExtent);\n      } // valueWindow[idx] = round(boundValue);\n      // percentWindow[idx] = round(boundPercent);\n      // fallback to extent start/end when parsed value or percent is invalid\n\n\n      valueWindow[idx] = boundValue == null || isNaN(boundValue) ? dataExtent[idx] : boundValue;\n      percentWindow[idx] = boundPercent == null || isNaN(boundPercent) ? percentExtent[idx] : boundPercent;\n    });\n    asc$1(valueWindow);\n    asc$1(percentWindow); // The windows from user calling of `dispatchAction` might be out of the extent,\n    // or do not obey the `min/maxSpan`, `min/maxValueSpan`. But we don't restrict window\n    // by `zoomLock` here, because we see `zoomLock` just as a interaction constraint,\n    // where API is able to initialize/modify the window size even though `zoomLock`\n    // specified.\n\n    var spans = this._minMaxSpan;\n    hasPropModeValue ? restrictSet(valueWindow, percentWindow, dataExtent, percentExtent, false) : restrictSet(percentWindow, valueWindow, percentExtent, dataExtent, true);\n\n    function restrictSet(fromWindow, toWindow, fromExtent, toExtent, toValue) {\n      var suffix = toValue ? 'Span' : 'ValueSpan';\n      sliderMove(0, fromWindow, fromExtent, 'all', spans['min' + suffix], spans['max' + suffix]);\n\n      for (var i = 0; i < 2; i++) {\n        toWindow[i] = linearMap(fromWindow[i], fromExtent, toExtent, true);\n        toValue && (toWindow[i] = scale.parse(toWindow[i]));\n      }\n    }\n\n    return {\n      valueWindow: valueWindow,\n      percentWindow: percentWindow\n    };\n  };\n  /**\n   * Notice: reset should not be called before series.restoreData() is called,\n   * so it is recommended to be called in \"process stage\" but not \"model init\n   * stage\".\n   */\n\n\n  AxisProxy.prototype.reset = function (dataZoomModel) {\n    if (dataZoomModel !== this._dataZoomModel) {\n      return;\n    }\n\n    var targetSeries = this.getTargetSeriesModels(); // Culculate data window and data extent, and record them.\n\n    this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries); // `calculateDataWindow` uses min/maxSpan.\n\n    this._updateMinMaxSpan();\n\n    var dataWindow = this.calculateDataWindow(dataZoomModel.settledOption);\n    this._valueWindow = dataWindow.valueWindow;\n    this._percentWindow = dataWindow.percentWindow; // Update axis setting then.\n\n    this._setAxisModel();\n  };\n\n  AxisProxy.prototype.filterData = function (dataZoomModel, api) {\n    if (dataZoomModel !== this._dataZoomModel) {\n      return;\n    }\n\n    var axisDim = this._dimName;\n    var seriesModels = this.getTargetSeriesModels();\n    var filterMode = dataZoomModel.get('filterMode');\n    var valueWindow = this._valueWindow;\n\n    if (filterMode === 'none') {\n      return;\n    } // FIXME\n    // Toolbox may has dataZoom injected. And if there are stacked bar chart\n    // with NaN data, NaN will be filtered and stack will be wrong.\n    // So we need to force the mode to be set empty.\n    // In fect, it is not a big deal that do not support filterMode-'filter'\n    // when using toolbox#dataZoom, utill tooltip#dataZoom support \"single axis\n    // selection\" some day, which might need \"adapt to data extent on the\n    // otherAxis\", which is disabled by filterMode-'empty'.\n    // But currently, stack has been fixed to based on value but not index,\n    // so this is not an issue any more.\n    // let otherAxisModel = this.getOtherAxisModel();\n    // if (dataZoomModel.get('$fromToolbox')\n    //     && otherAxisModel\n    //     && otherAxisModel.hasSeriesStacked\n    // ) {\n    //     filterMode = 'empty';\n    // }\n    // TODO\n    // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet.\n\n\n    each$8(seriesModels, function (seriesModel) {\n      var seriesData = seriesModel.getData();\n      var dataDims = seriesData.mapDimensionsAll(axisDim);\n\n      if (!dataDims.length) {\n        return;\n      }\n\n      if (filterMode === 'weakFilter') {\n        var store_1 = seriesData.getStore();\n        var dataDimIndices_1 = map(dataDims, function (dim) {\n          return seriesData.getDimensionIndex(dim);\n        }, seriesData);\n        seriesData.filterSelf(function (dataIndex) {\n          var leftOut;\n          var rightOut;\n          var hasValue;\n\n          for (var i = 0; i < dataDims.length; i++) {\n            var value = store_1.get(dataDimIndices_1[i], dataIndex);\n            var thisHasValue = !isNaN(value);\n            var thisLeftOut = value < valueWindow[0];\n            var thisRightOut = value > valueWindow[1];\n\n            if (thisHasValue && !thisLeftOut && !thisRightOut) {\n              return true;\n            }\n\n            thisHasValue && (hasValue = true);\n            thisLeftOut && (leftOut = true);\n            thisRightOut && (rightOut = true);\n          } // If both left out and right out, do not filter.\n\n\n          return hasValue && leftOut && rightOut;\n        });\n      } else {\n        each$8(dataDims, function (dim) {\n          if (filterMode === 'empty') {\n            seriesModel.setData(seriesData = seriesData.map(dim, function (value) {\n              return !isInWindow(value) ? NaN : value;\n            }));\n          } else {\n            var range = {};\n            range[dim] = valueWindow; // console.time('select');\n\n            seriesData.selectRange(range); // console.timeEnd('select');\n          }\n        });\n      }\n\n      each$8(dataDims, function (dim) {\n        seriesData.setApproximateExtent(valueWindow, dim);\n      });\n    });\n\n    function isInWindow(value) {\n      return value >= valueWindow[0] && value <= valueWindow[1];\n    }\n  };\n\n  AxisProxy.prototype._updateMinMaxSpan = function () {\n    var minMaxSpan = this._minMaxSpan = {};\n    var dataZoomModel = this._dataZoomModel;\n    var dataExtent = this._dataExtent;\n    each$8(['min', 'max'], function (minMax) {\n      var percentSpan = dataZoomModel.get(minMax + 'Span');\n      var valueSpan = dataZoomModel.get(minMax + 'ValueSpan');\n      valueSpan != null && (valueSpan = this.getAxisModel().axis.scale.parse(valueSpan)); // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan\n\n      if (valueSpan != null) {\n        percentSpan = linearMap(dataExtent[0] + valueSpan, dataExtent, [0, 100], true);\n      } else if (percentSpan != null) {\n        valueSpan = linearMap(percentSpan, [0, 100], dataExtent, true) - dataExtent[0];\n      }\n\n      minMaxSpan[minMax + 'Span'] = percentSpan;\n      minMaxSpan[minMax + 'ValueSpan'] = valueSpan;\n    }, this);\n  };\n\n  AxisProxy.prototype._setAxisModel = function () {\n    var axisModel = this.getAxisModel();\n    var percentWindow = this._percentWindow;\n    var valueWindow = this._valueWindow;\n\n    if (!percentWindow) {\n      return;\n    } // [0, 500]: arbitrary value, guess axis extent.\n\n\n    var precision = getPixelPrecision(valueWindow, [0, 500]);\n    precision = Math.min(precision, 20); // For value axis, if min/max/scale are not set, we just use the extent obtained\n    // by series data, which may be a little different from the extent calculated by\n    // `axisHelper.getScaleExtent`. But the different just affects the experience a\n    // little when zooming. So it will not be fixed until some users require it strongly.\n\n    var rawExtentInfo = axisModel.axis.scale.rawExtentInfo;\n\n    if (percentWindow[0] !== 0) {\n      rawExtentInfo.setDeterminedMinMax('min', +valueWindow[0].toFixed(precision));\n    }\n\n    if (percentWindow[1] !== 100) {\n      rawExtentInfo.setDeterminedMinMax('max', +valueWindow[1].toFixed(precision));\n    }\n\n    rawExtentInfo.freeze();\n  };\n\n  return AxisProxy;\n}();\n\nfunction calculateDataExtent(axisProxy, axisDim, seriesModels) {\n  var dataExtent = [Infinity, -Infinity];\n  each$8(seriesModels, function (seriesModel) {\n    unionAxisExtentFromData(dataExtent, seriesModel.getData(), axisDim);\n  }); // It is important to get \"consistent\" extent when more then one axes is\n  // controlled by a `dataZoom`, otherwise those axes will not be synchronized\n  // when zooming. But it is difficult to know what is \"consistent\", considering\n  // axes have different type or even different meanings (For example, two\n  // time axes are used to compare data of the same date in different years).\n  // So basically dataZoom just obtains extent by series.data (in category axis\n  // extent can be obtained from axis.data).\n  // Nevertheless, user can set min/max/scale on axes to make extent of axes\n  // consistent.\n\n  var axisModel = axisProxy.getAxisModel();\n  var rawExtentResult = ensureScaleRawExtentInfo(axisModel.axis.scale, axisModel, dataExtent).calculate();\n  return [rawExtentResult.min, rawExtentResult.max];\n}\n\nvar dataZoomProcessor = {\n  // `dataZoomProcessor` will only be performed in needed series. Consider if\n  // there is a line series and a pie series, it is better not to update the\n  // line series if only pie series is needed to be updated.\n  getTargetSeries: function (ecModel) {\n    function eachAxisModel(cb) {\n      ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n        dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n          var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n          cb(axisDim, axisIndex, axisModel, dataZoomModel);\n        });\n      });\n    } // FIXME: it brings side-effect to `getTargetSeries`.\n    // Prepare axis proxies.\n\n\n    eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {\n      // dispose all last axis proxy, in case that some axis are deleted.\n      axisModel.__dzAxisProxy = null;\n    });\n    var proxyList = [];\n    eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {\n      // Different dataZooms may constrol the same axis. In that case,\n      // an axisProxy serves both of them.\n      if (!axisModel.__dzAxisProxy) {\n        // Use the first dataZoomModel as the main model of axisProxy.\n        axisModel.__dzAxisProxy = new AxisProxy(axisDim, axisIndex, dataZoomModel, ecModel);\n        proxyList.push(axisModel.__dzAxisProxy);\n      }\n    });\n    var seriesModelMap = createHashMap();\n    each(proxyList, function (axisProxy) {\n      each(axisProxy.getTargetSeriesModels(), function (seriesModel) {\n        seriesModelMap.set(seriesModel.uid, seriesModel);\n      });\n    });\n    return seriesModelMap;\n  },\n  // Consider appendData, where filter should be performed. Because data process is\n  // in block mode currently, it is not need to worry about that the overallProgress\n  // execute every frame.\n  overallReset: function (ecModel, api) {\n    ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n      // We calculate window and reset axis here but not in model\n      // init stage and not after action dispatch handler, because\n      // reset should be called after seriesData.restoreData.\n      dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n        dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel);\n      }); // Caution: data zoom filtering is order sensitive when using\n      // percent range and no min/max/scale set on axis.\n      // For example, we have dataZoom definition:\n      // [\n      //      {xAxisIndex: 0, start: 30, end: 70},\n      //      {yAxisIndex: 0, start: 20, end: 80}\n      // ]\n      // In this case, [20, 80] of y-dataZoom should be based on data\n      // that have filtered by x-dataZoom using range of [30, 70],\n      // but should not be based on full raw data. Thus sliding\n      // x-dataZoom will change both ranges of xAxis and yAxis,\n      // while sliding y-dataZoom will only change the range of yAxis.\n      // So we should filter x-axis after reset x-axis immediately,\n      // and then reset y-axis and filter y-axis.\n\n      dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n        dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api);\n      });\n    });\n    ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n      // Fullfill all of the range props so that user\n      // is able to get them from chart.getOption().\n      var axisProxy = dataZoomModel.findRepresentativeAxisProxy();\n\n      if (axisProxy) {\n        var percentRange = axisProxy.getDataPercentWindow();\n        var valueRange = axisProxy.getDataValueWindow();\n        dataZoomModel.setCalculatedRange({\n          start: percentRange[0],\n          end: percentRange[1],\n          startValue: valueRange[0],\n          endValue: valueRange[1]\n        });\n      }\n    });\n  }\n};\n\nfunction installDataZoomAction(registers) {\n  registers.registerAction('dataZoom', function (payload, ecModel) {\n    var effectedModels = findEffectedDataZooms(ecModel, payload);\n    each(effectedModels, function (dataZoomModel) {\n      dataZoomModel.setRawRange({\n        start: payload.start,\n        end: payload.end,\n        startValue: payload.startValue,\n        endValue: payload.endValue\n      });\n    });\n  });\n}\n\nvar installed = false;\nfunction installCommon(registers) {\n  if (installed) {\n    return;\n  }\n\n  installed = true;\n  registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, dataZoomProcessor);\n  installDataZoomAction(registers);\n  registers.registerSubTypeDefaulter('dataZoom', function () {\n    // Default 'slider' when no type specified.\n    return 'slider';\n  });\n}\n\nfunction install$y(registers) {\n  registers.registerComponentModel(SelectDataZoomModel);\n  registers.registerComponentView(SelectDataZoomView);\n  installCommon(registers);\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\nvar ToolboxFeature =\n/** @class */\nfunction () {\n  function ToolboxFeature() {}\n\n  return ToolboxFeature;\n}();\nvar features = {};\nfunction registerFeature(name, ctor) {\n  features[name] = ctor;\n}\nfunction getFeature(name) {\n  return features[name];\n}\n\nvar ToolboxModel =\n/** @class */\nfunction (_super) {\n  __extends(ToolboxModel, _super);\n\n  function ToolboxModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ToolboxModel.type;\n    return _this;\n  }\n\n  ToolboxModel.prototype.optionUpdated = function () {\n    _super.prototype.optionUpdated.apply(this, arguments);\n\n    var ecModel = this.ecModel;\n    each(this.option.feature, function (featureOpt, featureName) {\n      var Feature = getFeature(featureName);\n\n      if (Feature) {\n        if (Feature.getDefaultOption) {\n          Feature.defaultOption = Feature.getDefaultOption(ecModel);\n        }\n\n        merge(featureOpt, Feature.defaultOption);\n      }\n    });\n  };\n\n  ToolboxModel.type = 'toolbox';\n  ToolboxModel.layoutMode = {\n    type: 'box',\n    ignoreSize: true\n  };\n  ToolboxModel.defaultOption = {\n    show: true,\n    z: 6,\n    // zlevel: 0,\n    orient: 'horizontal',\n    left: 'right',\n    top: 'top',\n    // right\n    // bottom\n    backgroundColor: 'transparent',\n    borderColor: '#ccc',\n    borderRadius: 0,\n    borderWidth: 0,\n    padding: 5,\n    itemSize: 15,\n    itemGap: 8,\n    showTitle: true,\n    iconStyle: {\n      borderColor: '#666',\n      color: 'none'\n    },\n    emphasis: {\n      iconStyle: {\n        borderColor: '#3E98C5'\n      }\n    },\n    // textStyle: {},\n    // feature\n    tooltip: {\n      show: false,\n      position: 'bottom'\n    }\n  };\n  return ToolboxModel;\n}(ComponentModel);\n\n/**\n * Layout list like component.\n * It will box layout each items in group of component and then position the whole group in the viewport\n * @param {module:zrender/group/Group} group\n * @param {module:echarts/model/Component} componentModel\n * @param {module:echarts/ExtensionAPI}\n */\n\nfunction layout$3(group, componentModel, api) {\n  var boxLayoutParams = componentModel.getBoxLayoutParams();\n  var padding = componentModel.get('padding');\n  var viewportSize = {\n    width: api.getWidth(),\n    height: api.getHeight()\n  };\n  var rect = getLayoutRect(boxLayoutParams, viewportSize, padding);\n  box(componentModel.get('orient'), group, componentModel.get('itemGap'), rect.width, rect.height);\n  positionElement(group, boxLayoutParams, viewportSize, padding);\n}\nfunction makeBackground(rect, componentModel) {\n  var padding = normalizeCssArray$1(componentModel.get('padding'));\n  var style = componentModel.getItemStyle(['color', 'opacity']);\n  style.fill = componentModel.get('backgroundColor');\n  rect = new Rect({\n    shape: {\n      x: rect.x - padding[3],\n      y: rect.y - padding[0],\n      width: rect.width + padding[1] + padding[3],\n      height: rect.height + padding[0] + padding[2],\n      r: componentModel.get('borderRadius')\n    },\n    style: style,\n    silent: true,\n    z2: -1\n  }); // FIXME\n  // `subPixelOptimizeRect` may bring some gap between edge of viewpart\n  // and background rect when setting like `left: 0`, `top: 0`.\n  // graphic.subPixelOptimizeRect(rect);\n\n  return rect;\n}\n\nvar ToolboxView =\n/** @class */\nfunction (_super) {\n  __extends(ToolboxView, _super);\n\n  function ToolboxView() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  ToolboxView.prototype.render = function (toolboxModel, ecModel, api, payload) {\n    var group = this.group;\n    group.removeAll();\n\n    if (!toolboxModel.get('show')) {\n      return;\n    }\n\n    var itemSize = +toolboxModel.get('itemSize');\n    var isVertical = toolboxModel.get('orient') === 'vertical';\n    var featureOpts = toolboxModel.get('feature') || {};\n    var features = this._features || (this._features = {});\n    var featureNames = [];\n    each(featureOpts, function (opt, name) {\n      featureNames.push(name);\n    });\n    new DataDiffer(this._featureNames || [], featureNames).add(processFeature).update(processFeature).remove(curry(processFeature, null)).execute(); // Keep for diff.\n\n    this._featureNames = featureNames;\n\n    function processFeature(newIndex, oldIndex) {\n      var featureName = featureNames[newIndex];\n      var oldName = featureNames[oldIndex];\n      var featureOpt = featureOpts[featureName];\n      var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel);\n      var feature; // FIX#11236, merge feature title from MagicType newOption. TODO: consider seriesIndex ?\n\n      if (payload && payload.newTitle != null && payload.featureName === featureName) {\n        featureOpt.title = payload.newTitle;\n      }\n\n      if (featureName && !oldName) {\n        // Create\n        if (isUserFeatureName(featureName)) {\n          feature = {\n            onclick: featureModel.option.onclick,\n            featureName: featureName\n          };\n        } else {\n          var Feature = getFeature(featureName);\n\n          if (!Feature) {\n            return;\n          }\n\n          feature = new Feature();\n        }\n\n        features[featureName] = feature;\n      } else {\n        feature = features[oldName]; // If feature does not exsit.\n\n        if (!feature) {\n          return;\n        }\n      }\n\n      feature.uid = getUID('toolbox-feature');\n      feature.model = featureModel;\n      feature.ecModel = ecModel;\n      feature.api = api;\n      var isToolboxFeature = feature instanceof ToolboxFeature;\n\n      if (!featureName && oldName) {\n        isToolboxFeature && feature.dispose && feature.dispose(ecModel, api);\n        return;\n      }\n\n      if (!featureModel.get('show') || isToolboxFeature && feature.unusable) {\n        isToolboxFeature && feature.remove && feature.remove(ecModel, api);\n        return;\n      }\n\n      createIconPaths(featureModel, feature, featureName);\n\n      featureModel.setIconStatus = function (iconName, status) {\n        var option = this.option;\n        var iconPaths = this.iconPaths;\n        option.iconStatus = option.iconStatus || {};\n        option.iconStatus[iconName] = status;\n\n        if (iconPaths[iconName]) {\n          (status === 'emphasis' ? enterEmphasis : leaveEmphasis)(iconPaths[iconName]);\n        }\n      };\n\n      if (feature instanceof ToolboxFeature) {\n        if (feature.render) {\n          feature.render(featureModel, ecModel, api, payload);\n        }\n      }\n    }\n\n    function createIconPaths(featureModel, feature, featureName) {\n      var iconStyleModel = featureModel.getModel('iconStyle');\n      var iconStyleEmphasisModel = featureModel.getModel(['emphasis', 'iconStyle']); // If one feature has mutiple icon. they are orginaized as\n      // {\n      //     icon: {\n      //         foo: '',\n      //         bar: ''\n      //     },\n      //     title: {\n      //         foo: '',\n      //         bar: ''\n      //     }\n      // }\n\n      var icons = feature instanceof ToolboxFeature && feature.getIcons ? feature.getIcons() : featureModel.get('icon');\n      var titles = featureModel.get('title') || {};\n      var iconsMap;\n      var titlesMap;\n\n      if (isString(icons)) {\n        iconsMap = {};\n        iconsMap[featureName] = icons;\n      } else {\n        iconsMap = icons;\n      }\n\n      if (isString(titles)) {\n        titlesMap = {};\n        titlesMap[featureName] = titles;\n      } else {\n        titlesMap = titles;\n      }\n\n      var iconPaths = featureModel.iconPaths = {};\n      each(iconsMap, function (iconStr, iconName) {\n        var path = createIcon(iconStr, {}, {\n          x: -itemSize / 2,\n          y: -itemSize / 2,\n          width: itemSize,\n          height: itemSize\n        }); // TODO handling image\n\n        path.setStyle(iconStyleModel.getItemStyle());\n        var pathEmphasisState = path.ensureState('emphasis');\n        pathEmphasisState.style = iconStyleEmphasisModel.getItemStyle(); // Text position calculation\n\n        var textContent = new ZRText({\n          style: {\n            text: titlesMap[iconName],\n            align: iconStyleEmphasisModel.get('textAlign'),\n            borderRadius: iconStyleEmphasisModel.get('textBorderRadius'),\n            padding: iconStyleEmphasisModel.get('textPadding'),\n            fill: null\n          },\n          ignore: true\n        });\n        path.setTextContent(textContent);\n        setTooltipConfig({\n          el: path,\n          componentModel: toolboxModel,\n          itemName: iconName,\n          formatterParamsExtra: {\n            title: titlesMap[iconName]\n          }\n        });\n        path.__title = titlesMap[iconName];\n        path.on('mouseover', function () {\n          // Should not reuse above hoverStyle, which might be modified.\n          var hoverStyle = iconStyleEmphasisModel.getItemStyle();\n          var defaultTextPosition = isVertical ? toolboxModel.get('right') == null && toolboxModel.get('left') !== 'right' ? 'right' : 'left' : toolboxModel.get('bottom') == null && toolboxModel.get('top') !== 'bottom' ? 'bottom' : 'top';\n          textContent.setStyle({\n            fill: iconStyleEmphasisModel.get('textFill') || hoverStyle.fill || hoverStyle.stroke || '#000',\n            backgroundColor: iconStyleEmphasisModel.get('textBackgroundColor')\n          });\n          path.setTextConfig({\n            position: iconStyleEmphasisModel.get('textPosition') || defaultTextPosition\n          });\n          textContent.ignore = !toolboxModel.get('showTitle'); // Use enterEmphasis and leaveEmphasis provide by ec.\n          // There are flags managed by the echarts.\n\n          api.enterEmphasis(this);\n        }).on('mouseout', function () {\n          if (featureModel.get(['iconStatus', iconName]) !== 'emphasis') {\n            api.leaveEmphasis(this);\n          }\n\n          textContent.hide();\n        });\n        (featureModel.get(['iconStatus', iconName]) === 'emphasis' ? enterEmphasis : leaveEmphasis)(path);\n        group.add(path);\n        path.on('click', bind(feature.onclick, feature, ecModel, api, iconName));\n        iconPaths[iconName] = path;\n      });\n    }\n\n    layout$3(group, toolboxModel, api); // Render background after group is layout\n    // FIXME\n\n    group.add(makeBackground(group.getBoundingRect(), toolboxModel)); // Adjust icon title positions to avoid them out of screen\n\n    isVertical || group.eachChild(function (icon) {\n      var titleText = icon.__title; // const hoverStyle = icon.hoverStyle;\n      // TODO simplify code?\n\n      var emphasisState = icon.ensureState('emphasis');\n      var emphasisTextConfig = emphasisState.textConfig || (emphasisState.textConfig = {});\n      var textContent = icon.getTextContent();\n      var emphasisTextState = textContent && textContent.ensureState('emphasis'); // May be background element\n\n      if (emphasisTextState && !isFunction(emphasisTextState) && titleText) {\n        var emphasisTextStyle = emphasisTextState.style || (emphasisTextState.style = {});\n        var rect = getBoundingRect(titleText, ZRText.makeFont(emphasisTextStyle));\n        var offsetX = icon.x + group.x;\n        var offsetY = icon.y + group.y + itemSize;\n        var needPutOnTop = false;\n\n        if (offsetY + rect.height > api.getHeight()) {\n          emphasisTextConfig.position = 'top';\n          needPutOnTop = true;\n        }\n\n        var topOffset = needPutOnTop ? -5 - rect.height : itemSize + 10;\n\n        if (offsetX + rect.width / 2 > api.getWidth()) {\n          emphasisTextConfig.position = ['100%', topOffset];\n          emphasisTextStyle.align = 'right';\n        } else if (offsetX - rect.width / 2 < 0) {\n          emphasisTextConfig.position = [0, topOffset];\n          emphasisTextStyle.align = 'left';\n        }\n      }\n    });\n  };\n\n  ToolboxView.prototype.updateView = function (toolboxModel, ecModel, api, payload) {\n    each(this._features, function (feature) {\n      feature instanceof ToolboxFeature && feature.updateView && feature.updateView(feature.model, ecModel, api, payload);\n    });\n  }; // updateLayout(toolboxModel, ecModel, api, payload) {\n  //     zrUtil.each(this._features, function (feature) {\n  //         feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload);\n  //     });\n  // },\n\n\n  ToolboxView.prototype.remove = function (ecModel, api) {\n    each(this._features, function (feature) {\n      feature instanceof ToolboxFeature && feature.remove && feature.remove(ecModel, api);\n    });\n    this.group.removeAll();\n  };\n\n  ToolboxView.prototype.dispose = function (ecModel, api) {\n    each(this._features, function (feature) {\n      feature instanceof ToolboxFeature && feature.dispose && feature.dispose(ecModel, api);\n    });\n  };\n\n  ToolboxView.type = 'toolbox';\n  return ToolboxView;\n}(ComponentView);\n\nfunction isUserFeatureName(featureName) {\n  return featureName.indexOf('my') === 0;\n}\n\n/* global window, document */\n\nvar SaveAsImage =\n/** @class */\nfunction (_super) {\n  __extends(SaveAsImage, _super);\n\n  function SaveAsImage() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  SaveAsImage.prototype.onclick = function (ecModel, api) {\n    var model = this.model;\n    var title = model.get('name') || ecModel.get('title.0.text') || 'echarts';\n    var isSvg = api.getZr().painter.getType() === 'svg';\n    var type = isSvg ? 'svg' : model.get('type', true) || 'png';\n    var url = api.getConnectedDataURL({\n      type: type,\n      backgroundColor: model.get('backgroundColor', true) || ecModel.get('backgroundColor') || '#fff',\n      connectedBackgroundColor: model.get('connectedBackgroundColor'),\n      excludeComponents: model.get('excludeComponents'),\n      pixelRatio: model.get('pixelRatio')\n    });\n    var browser = env.browser; // Chrome, Firefox, New Edge\n\n    if (isFunction(MouseEvent) && (browser.newEdge || !browser.ie && !browser.edge)) {\n      var $a = document.createElement('a');\n      $a.download = title + '.' + type;\n      $a.target = '_blank';\n      $a.href = url;\n      var evt = new MouseEvent('click', {\n        // some micro front-end framework， window maybe is a Proxy\n        view: document.defaultView,\n        bubbles: true,\n        cancelable: false\n      });\n      $a.dispatchEvent(evt);\n    } // IE or old Edge\n    else {\n        // @ts-ignore\n        if (window.navigator.msSaveOrOpenBlob || isSvg) {\n          var parts = url.split(','); // data:[<mime type>][;charset=<charset>][;base64],<encoded data>\n\n          var base64Encoded = parts[0].indexOf('base64') > -1;\n          var bstr = isSvg // should decode the svg data uri first\n          ? decodeURIComponent(parts[1]) : parts[1]; // only `atob` when the data uri is encoded with base64\n          // otherwise, like `svg` data uri exported by zrender,\n          // there will be an error, for it's not encoded with base64.\n          // (just a url-encoded string through `encodeURIComponent`)\n\n          base64Encoded && (bstr = window.atob(bstr));\n          var filename = title + '.' + type; // @ts-ignore\n\n          if (window.navigator.msSaveOrOpenBlob) {\n            var n = bstr.length;\n            var u8arr = new Uint8Array(n);\n\n            while (n--) {\n              u8arr[n] = bstr.charCodeAt(n);\n            }\n\n            var blob = new Blob([u8arr]); // @ts-ignore\n\n            window.navigator.msSaveOrOpenBlob(blob, filename);\n          } else {\n            var frame = document.createElement('iframe');\n            document.body.appendChild(frame);\n            var cw = frame.contentWindow;\n            var doc = cw.document;\n            doc.open('image/svg+xml', 'replace');\n            doc.write(bstr);\n            doc.close();\n            cw.focus();\n            doc.execCommand('SaveAs', true, filename);\n            document.body.removeChild(frame);\n          }\n        } else {\n          var lang = model.get('lang');\n          var html = '' + '<body style=\"margin:0;\">' + '<img src=\"' + url + '\" style=\"max-width:100%;\" title=\"' + (lang && lang[0] || '') + '\" />' + '</body>';\n          var tab = window.open();\n          tab.document.write(html);\n          tab.document.title = title;\n        }\n      }\n  };\n\n  SaveAsImage.getDefaultOption = function (ecModel) {\n    var defaultOption = {\n      show: true,\n      icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0',\n      title: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'title']),\n      type: 'png',\n      // Default use option.backgroundColor\n      // backgroundColor: '#fff',\n      connectedBackgroundColor: '#fff',\n      name: '',\n      excludeComponents: ['toolbox'],\n      // use current pixel ratio of device by default\n      // pixelRatio: 1,\n      lang: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'lang'])\n    };\n    return defaultOption;\n  };\n\n  return SaveAsImage;\n}(ToolboxFeature);\n\nvar INNER_STACK_KEYWORD = '__ec_magicType_stack__';\nvar radioTypes = [['line', 'bar'], ['stack']];\n\nvar MagicType =\n/** @class */\nfunction (_super) {\n  __extends(MagicType, _super);\n\n  function MagicType() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  MagicType.prototype.getIcons = function () {\n    var model = this.model;\n    var availableIcons = model.get('icon');\n    var icons = {};\n    each(model.get('type'), function (type) {\n      if (availableIcons[type]) {\n        icons[type] = availableIcons[type];\n      }\n    });\n    return icons;\n  };\n\n  MagicType.getDefaultOption = function (ecModel) {\n    var defaultOption = {\n      show: true,\n      type: [],\n      // Icon group\n      icon: {\n        line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4',\n        bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7',\n        // eslint-disable-next-line\n        stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z' // jshint ignore:line\n\n      },\n      // `line`, `bar`, `stack`, `tiled`\n      title: ecModel.getLocaleModel().get(['toolbox', 'magicType', 'title']),\n      option: {},\n      seriesIndex: {}\n    };\n    return defaultOption;\n  };\n\n  MagicType.prototype.onclick = function (ecModel, api, type) {\n    var model = this.model;\n    var seriesIndex = model.get(['seriesIndex', type]); // Not supported magicType\n\n    if (!seriesOptGenreator[type]) {\n      return;\n    }\n\n    var newOption = {\n      series: []\n    };\n\n    var generateNewSeriesTypes = function (seriesModel) {\n      var seriesType = seriesModel.subType;\n      var seriesId = seriesModel.id;\n      var newSeriesOpt = seriesOptGenreator[type](seriesType, seriesId, seriesModel, model);\n\n      if (newSeriesOpt) {\n        // PENDING If merge original option?\n        defaults(newSeriesOpt, seriesModel.option);\n        newOption.series.push(newSeriesOpt);\n      } // Modify boundaryGap\n\n\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) {\n        var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n\n        if (categoryAxis) {\n          var axisDim = categoryAxis.dim;\n          var axisType = axisDim + 'Axis';\n          var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0];\n          var axisIndex = axisModel.componentIndex;\n          newOption[axisType] = newOption[axisType] || [];\n\n          for (var i = 0; i <= axisIndex; i++) {\n            newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {};\n          }\n\n          newOption[axisType][axisIndex].boundaryGap = type === 'bar';\n        }\n      }\n    };\n\n    each(radioTypes, function (radio) {\n      if (indexOf(radio, type) >= 0) {\n        each(radio, function (item) {\n          model.setIconStatus(item, 'normal');\n        });\n      }\n    });\n    model.setIconStatus(type, 'emphasis');\n    ecModel.eachComponent({\n      mainType: 'series',\n      query: seriesIndex == null ? null : {\n        seriesIndex: seriesIndex\n      }\n    }, generateNewSeriesTypes);\n    var newTitle;\n    var currentType = type; // Change title of stack\n\n    if (type === 'stack') {\n      // use titles in model instead of ecModel\n      // as stack and tiled appears in pair, just flip them\n      // no need of checking stack state\n      newTitle = merge({\n        stack: model.option.title.tiled,\n        tiled: model.option.title.stack\n      }, model.option.title);\n\n      if (model.get(['iconStatus', type]) !== 'emphasis') {\n        currentType = 'tiled';\n      }\n    }\n\n    api.dispatchAction({\n      type: 'changeMagicType',\n      currentType: currentType,\n      newOption: newOption,\n      newTitle: newTitle,\n      featureName: 'magicType'\n    });\n  };\n\n  return MagicType;\n}(ToolboxFeature);\n\nvar seriesOptGenreator = {\n  'line': function (seriesType, seriesId, seriesModel, model) {\n    if (seriesType === 'bar') {\n      return merge({\n        id: seriesId,\n        type: 'line',\n        // Preserve data related option\n        data: seriesModel.get('data'),\n        stack: seriesModel.get('stack'),\n        markPoint: seriesModel.get('markPoint'),\n        markLine: seriesModel.get('markLine')\n      }, model.get(['option', 'line']) || {}, true);\n    }\n  },\n  'bar': function (seriesType, seriesId, seriesModel, model) {\n    if (seriesType === 'line') {\n      return merge({\n        id: seriesId,\n        type: 'bar',\n        // Preserve data related option\n        data: seriesModel.get('data'),\n        stack: seriesModel.get('stack'),\n        markPoint: seriesModel.get('markPoint'),\n        markLine: seriesModel.get('markLine')\n      }, model.get(['option', 'bar']) || {}, true);\n    }\n  },\n  'stack': function (seriesType, seriesId, seriesModel, model) {\n    var isStack = seriesModel.get('stack') === INNER_STACK_KEYWORD;\n\n    if (seriesType === 'line' || seriesType === 'bar') {\n      model.setIconStatus('stack', isStack ? 'normal' : 'emphasis');\n      return merge({\n        id: seriesId,\n        stack: isStack ? '' : INNER_STACK_KEYWORD\n      }, model.get(['option', 'stack']) || {}, true);\n    }\n  }\n}; // TODO: SELF REGISTERED.\n\nregisterAction({\n  type: 'changeMagicType',\n  event: 'magicTypeChanged',\n  update: 'prepareAndUpdate'\n}, function (payload, ecModel) {\n  ecModel.mergeOption(payload.newOption);\n});\n\n/* global document */\n\nvar BLOCK_SPLITER = new Array(60).join('-');\nvar ITEM_SPLITER = '\\t';\n/**\n * Group series into two types\n *  1. on category axis, like line, bar\n *  2. others, like scatter, pie\n */\n\nfunction groupSeries(ecModel) {\n  var seriesGroupByCategoryAxis = {};\n  var otherSeries = [];\n  var meta = [];\n  ecModel.eachRawSeries(function (seriesModel) {\n    var coordSys = seriesModel.coordinateSystem;\n\n    if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) {\n      // TODO: TYPE Consider polar? Include polar may increase unecessary bundle size.\n      var baseAxis = coordSys.getBaseAxis();\n\n      if (baseAxis.type === 'category') {\n        var key = baseAxis.dim + '_' + baseAxis.index;\n\n        if (!seriesGroupByCategoryAxis[key]) {\n          seriesGroupByCategoryAxis[key] = {\n            categoryAxis: baseAxis,\n            valueAxis: coordSys.getOtherAxis(baseAxis),\n            series: []\n          };\n          meta.push({\n            axisDim: baseAxis.dim,\n            axisIndex: baseAxis.index\n          });\n        }\n\n        seriesGroupByCategoryAxis[key].series.push(seriesModel);\n      } else {\n        otherSeries.push(seriesModel);\n      }\n    } else {\n      otherSeries.push(seriesModel);\n    }\n  });\n  return {\n    seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,\n    other: otherSeries,\n    meta: meta\n  };\n}\n/**\n * Assemble content of series on cateogory axis\n * @inner\n */\n\n\nfunction assembleSeriesWithCategoryAxis(groups) {\n  var tables = [];\n  each(groups, function (group, key) {\n    var categoryAxis = group.categoryAxis;\n    var valueAxis = group.valueAxis;\n    var valueAxisDim = valueAxis.dim;\n    var headers = [' '].concat(map(group.series, function (series) {\n      return series.name;\n    })); // @ts-ignore TODO Polar\n\n    var columns = [categoryAxis.model.getCategories()];\n    each(group.series, function (series) {\n      var rawData = series.getRawData();\n      columns.push(series.getRawData().mapArray(rawData.mapDimension(valueAxisDim), function (val) {\n        return val;\n      }));\n    }); // Assemble table content\n\n    var lines = [headers.join(ITEM_SPLITER)];\n\n    for (var i = 0; i < columns[0].length; i++) {\n      var items = [];\n\n      for (var j = 0; j < columns.length; j++) {\n        items.push(columns[j][i]);\n      }\n\n      lines.push(items.join(ITEM_SPLITER));\n    }\n\n    tables.push(lines.join('\\n'));\n  });\n  return tables.join('\\n\\n' + BLOCK_SPLITER + '\\n\\n');\n}\n/**\n * Assemble content of other series\n */\n\n\nfunction assembleOtherSeries(series) {\n  return map(series, function (series) {\n    var data = series.getRawData();\n    var lines = [series.name];\n    var vals = [];\n    data.each(data.dimensions, function () {\n      var argLen = arguments.length;\n      var dataIndex = arguments[argLen - 1];\n      var name = data.getName(dataIndex);\n\n      for (var i = 0; i < argLen - 1; i++) {\n        vals[i] = arguments[i];\n      }\n\n      lines.push((name ? name + ITEM_SPLITER : '') + vals.join(ITEM_SPLITER));\n    });\n    return lines.join('\\n');\n  }).join('\\n\\n' + BLOCK_SPLITER + '\\n\\n');\n}\n\nfunction getContentFromModel(ecModel) {\n  var result = groupSeries(ecModel);\n  return {\n    value: filter([assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), assembleOtherSeries(result.other)], function (str) {\n      return !!str.replace(/[\\n\\t\\s]/g, '');\n    }).join('\\n\\n' + BLOCK_SPLITER + '\\n\\n'),\n    meta: result.meta\n  };\n}\n\nfunction trim$1(str) {\n  return str.replace(/^\\s\\s*/, '').replace(/\\s\\s*$/, '');\n}\n/**\n * If a block is tsv format\n */\n\n\nfunction isTSVFormat(block) {\n  // Simple method to find out if a block is tsv format\n  var firstLine = block.slice(0, block.indexOf('\\n'));\n\n  if (firstLine.indexOf(ITEM_SPLITER) >= 0) {\n    return true;\n  }\n}\n\nvar itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g');\n/**\n * @param {string} tsv\n * @return {Object}\n */\n\nfunction parseTSVContents(tsv) {\n  var tsvLines = tsv.split(/\\n+/g);\n  var headers = trim$1(tsvLines.shift()).split(itemSplitRegex);\n  var categories = [];\n  var series = map(headers, function (header) {\n    return {\n      name: header,\n      data: []\n    };\n  });\n\n  for (var i = 0; i < tsvLines.length; i++) {\n    var items = trim$1(tsvLines[i]).split(itemSplitRegex);\n    categories.push(items.shift());\n\n    for (var j = 0; j < items.length; j++) {\n      series[j] && (series[j].data[i] = items[j]);\n    }\n  }\n\n  return {\n    series: series,\n    categories: categories\n  };\n}\n\nfunction parseListContents(str) {\n  var lines = str.split(/\\n+/g);\n  var seriesName = trim$1(lines.shift());\n  var data = [];\n\n  for (var i = 0; i < lines.length; i++) {\n    // if line is empty, ignore it.\n    // there is a case that a user forgot to delete `\\n`.\n    var line = trim$1(lines[i]);\n\n    if (!line) {\n      continue;\n    }\n\n    var items = line.split(itemSplitRegex);\n    var name_1 = '';\n    var value = void 0;\n    var hasName = false;\n\n    if (isNaN(items[0])) {\n      // First item is name\n      hasName = true;\n      name_1 = items[0];\n      items = items.slice(1);\n      data[i] = {\n        name: name_1,\n        value: []\n      };\n      value = data[i].value;\n    } else {\n      value = data[i] = [];\n    }\n\n    for (var j = 0; j < items.length; j++) {\n      value.push(+items[j]);\n    }\n\n    if (value.length === 1) {\n      hasName ? data[i].value = value[0] : data[i] = value[0];\n    }\n  }\n\n  return {\n    name: seriesName,\n    data: data\n  };\n}\n\nfunction parseContents(str, blockMetaList) {\n  var blocks = str.split(new RegExp('\\n*' + BLOCK_SPLITER + '\\n*', 'g'));\n  var newOption = {\n    series: []\n  };\n  each(blocks, function (block, idx) {\n    if (isTSVFormat(block)) {\n      var result = parseTSVContents(block);\n      var blockMeta = blockMetaList[idx];\n      var axisKey = blockMeta.axisDim + 'Axis';\n\n      if (blockMeta) {\n        newOption[axisKey] = newOption[axisKey] || [];\n        newOption[axisKey][blockMeta.axisIndex] = {\n          data: result.categories\n        };\n        newOption.series = newOption.series.concat(result.series);\n      }\n    } else {\n      var result = parseListContents(block);\n      newOption.series.push(result);\n    }\n  });\n  return newOption;\n}\n\nvar DataView =\n/** @class */\nfunction (_super) {\n  __extends(DataView, _super);\n\n  function DataView() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  DataView.prototype.onclick = function (ecModel, api) {\n    // FIXME: better way?\n    setTimeout(function () {\n      api.dispatchAction({\n        type: 'hideTip'\n      });\n    });\n    var container = api.getDom();\n    var model = this.model;\n\n    if (this._dom) {\n      container.removeChild(this._dom);\n    }\n\n    var root = document.createElement('div'); // use padding to avoid 5px whitespace\n\n    root.style.cssText = 'position:absolute;top:0;bottom:0;left:0;right:0;padding:5px';\n    root.style.backgroundColor = model.get('backgroundColor') || '#fff'; // Create elements\n\n    var header = document.createElement('h4');\n    var lang = model.get('lang') || [];\n    header.innerHTML = lang[0] || model.get('title');\n    header.style.cssText = 'margin:10px 20px';\n    header.style.color = model.get('textColor');\n    var viewMain = document.createElement('div');\n    var textarea = document.createElement('textarea');\n    viewMain.style.cssText = 'overflow:auto';\n    var optionToContent = model.get('optionToContent');\n    var contentToOption = model.get('contentToOption');\n    var result = getContentFromModel(ecModel);\n\n    if (isFunction(optionToContent)) {\n      var htmlOrDom = optionToContent(api.getOption());\n\n      if (isString(htmlOrDom)) {\n        viewMain.innerHTML = htmlOrDom;\n      } else if (isDom(htmlOrDom)) {\n        viewMain.appendChild(htmlOrDom);\n      }\n    } else {\n      // Use default textarea\n      textarea.readOnly = model.get('readOnly');\n      var style = textarea.style; // eslint-disable-next-line max-len\n\n      style.cssText = 'display:block;width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;resize:none;box-sizing:border-box;outline:none';\n      style.color = model.get('textColor');\n      style.borderColor = model.get('textareaBorderColor');\n      style.backgroundColor = model.get('textareaColor');\n      textarea.value = result.value;\n      viewMain.appendChild(textarea);\n    }\n\n    var blockMetaList = result.meta;\n    var buttonContainer = document.createElement('div');\n    buttonContainer.style.cssText = 'position:absolute;bottom:5px;left:0;right:0'; // eslint-disable-next-line max-len\n\n    var buttonStyle = 'float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px';\n    var closeButton = document.createElement('div');\n    var refreshButton = document.createElement('div');\n    buttonStyle += ';background-color:' + model.get('buttonColor');\n    buttonStyle += ';color:' + model.get('buttonTextColor');\n    var self = this;\n\n    function close() {\n      container.removeChild(root);\n      self._dom = null;\n    }\n\n    addEventListener(closeButton, 'click', close);\n    addEventListener(refreshButton, 'click', function () {\n      if (contentToOption == null && optionToContent != null || contentToOption != null && optionToContent == null) {\n        if (\"development\" !== 'production') {\n          // eslint-disable-next-line\n          warn('It seems you have just provided one of `contentToOption` and `optionToContent` functions but missed the other one. Data change is ignored.');\n        }\n\n        close();\n        return;\n      }\n\n      var newOption;\n\n      try {\n        if (isFunction(contentToOption)) {\n          newOption = contentToOption(viewMain, api.getOption());\n        } else {\n          newOption = parseContents(textarea.value, blockMetaList);\n        }\n      } catch (e) {\n        close();\n        throw new Error('Data view format error ' + e);\n      }\n\n      if (newOption) {\n        api.dispatchAction({\n          type: 'changeDataView',\n          newOption: newOption\n        });\n      }\n\n      close();\n    });\n    closeButton.innerHTML = lang[1];\n    refreshButton.innerHTML = lang[2];\n    refreshButton.style.cssText = closeButton.style.cssText = buttonStyle;\n    !model.get('readOnly') && buttonContainer.appendChild(refreshButton);\n    buttonContainer.appendChild(closeButton);\n    root.appendChild(header);\n    root.appendChild(viewMain);\n    root.appendChild(buttonContainer);\n    viewMain.style.height = container.clientHeight - 80 + 'px';\n    container.appendChild(root);\n    this._dom = root;\n  };\n\n  DataView.prototype.remove = function (ecModel, api) {\n    this._dom && api.getDom().removeChild(this._dom);\n  };\n\n  DataView.prototype.dispose = function (ecModel, api) {\n    this.remove(ecModel, api);\n  };\n\n  DataView.getDefaultOption = function (ecModel) {\n    var defaultOption = {\n      show: true,\n      readOnly: false,\n      optionToContent: null,\n      contentToOption: null,\n      // eslint-disable-next-line\n      icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',\n      title: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'title']),\n      lang: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'lang']),\n      backgroundColor: '#fff',\n      textColor: '#000',\n      textareaColor: '#fff',\n      textareaBorderColor: '#333',\n      buttonColor: '#c23531',\n      buttonTextColor: '#fff'\n    };\n    return defaultOption;\n  };\n\n  return DataView;\n}(ToolboxFeature);\n/**\n * @inner\n */\n\n\nfunction tryMergeDataOption(newData, originalData) {\n  return map(newData, function (newVal, idx) {\n    var original = originalData && originalData[idx];\n\n    if (isObject(original) && !isArray(original)) {\n      var newValIsObject = isObject(newVal) && !isArray(newVal);\n\n      if (!newValIsObject) {\n        newVal = {\n          value: newVal\n        };\n      } // original data has name but new data has no name\n\n\n      var shouldDeleteName = original.name != null && newVal.name == null; // Original data has option\n\n      newVal = defaults(newVal, original);\n      shouldDeleteName && delete newVal.name;\n      return newVal;\n    } else {\n      return newVal;\n    }\n  });\n} // TODO: SELF REGISTERED.\n\n\nregisterAction({\n  type: 'changeDataView',\n  event: 'dataViewChanged',\n  update: 'prepareAndUpdate'\n}, function (payload, ecModel) {\n  var newSeriesOptList = [];\n  each(payload.newOption.series, function (seriesOpt) {\n    var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];\n\n    if (!seriesModel) {\n      // New created series\n      // Geuss the series type\n      newSeriesOptList.push(extend({\n        // Default is scatter\n        type: 'scatter'\n      }, seriesOpt));\n    } else {\n      var originalData = seriesModel.get('data');\n      newSeriesOptList.push({\n        name: seriesOpt.name,\n        data: tryMergeDataOption(seriesOpt.data, originalData)\n      });\n    }\n  });\n  ecModel.mergeOption(defaults({\n    series: newSeriesOptList\n  }, payload.newOption));\n});\n\nvar each$9 = each;\nvar inner$f = makeInner();\n/**\n * @param ecModel\n * @param newSnapshot key is dataZoomId\n */\n\nfunction push(ecModel, newSnapshot) {\n  var storedSnapshots = getStoreSnapshots(ecModel); // If previous dataZoom can not be found,\n  // complete an range with current range.\n\n  each$9(newSnapshot, function (batchItem, dataZoomId) {\n    var i = storedSnapshots.length - 1;\n\n    for (; i >= 0; i--) {\n      var snapshot = storedSnapshots[i];\n\n      if (snapshot[dataZoomId]) {\n        break;\n      }\n    }\n\n    if (i < 0) {\n      // No origin range set, create one by current range.\n      var dataZoomModel = ecModel.queryComponents({\n        mainType: 'dataZoom',\n        subType: 'select',\n        id: dataZoomId\n      })[0];\n\n      if (dataZoomModel) {\n        var percentRange = dataZoomModel.getPercentRange();\n        storedSnapshots[0][dataZoomId] = {\n          dataZoomId: dataZoomId,\n          start: percentRange[0],\n          end: percentRange[1]\n        };\n      }\n    }\n  });\n  storedSnapshots.push(newSnapshot);\n}\nfunction pop(ecModel) {\n  var storedSnapshots = getStoreSnapshots(ecModel);\n  var head = storedSnapshots[storedSnapshots.length - 1];\n  storedSnapshots.length > 1 && storedSnapshots.pop(); // Find top for all dataZoom.\n\n  var snapshot = {};\n  each$9(head, function (batchItem, dataZoomId) {\n    for (var i = storedSnapshots.length - 1; i >= 0; i--) {\n      batchItem = storedSnapshots[i][dataZoomId];\n\n      if (batchItem) {\n        snapshot[dataZoomId] = batchItem;\n        break;\n      }\n    }\n  });\n  return snapshot;\n}\nfunction clear$1(ecModel) {\n  inner$f(ecModel).snapshots = null;\n}\nfunction count(ecModel) {\n  return getStoreSnapshots(ecModel).length;\n}\n/**\n * History length of each dataZoom may be different.\n * this._history[0] is used to store origin range.\n */\n\nfunction getStoreSnapshots(ecModel) {\n  var store = inner$f(ecModel);\n\n  if (!store.snapshots) {\n    store.snapshots = [{}];\n  }\n\n  return store.snapshots;\n}\n\nvar RestoreOption =\n/** @class */\nfunction (_super) {\n  __extends(RestoreOption, _super);\n\n  function RestoreOption() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  RestoreOption.prototype.onclick = function (ecModel, api) {\n    clear$1(ecModel);\n    api.dispatchAction({\n      type: 'restore',\n      from: this.uid\n    });\n  };\n\n  RestoreOption.getDefaultOption = function (ecModel) {\n    var defaultOption = {\n      show: true,\n      // eslint-disable-next-line\n      icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5',\n      title: ecModel.getLocaleModel().get(['toolbox', 'restore', 'title'])\n    };\n    return defaultOption;\n  };\n\n  return RestoreOption;\n}(ToolboxFeature); // TODO: SELF REGISTERED.\n\n\nregisterAction({\n  type: 'restore',\n  event: 'restore',\n  update: 'prepareAndUpdate'\n}, function (payload, ecModel) {\n  ecModel.resetOption('recreate');\n});\n\n// how to genarialize to more coordinate systems.\n\nvar INCLUDE_FINDER_MAIN_TYPES = ['grid', 'xAxis', 'yAxis', 'geo', 'graph', 'polar', 'radiusAxis', 'angleAxis', 'bmap'];\n\nvar BrushTargetManager =\n/** @class */\nfunction () {\n  /**\n   * @param finder contains Index/Id/Name of xAxis/yAxis/geo/grid\n   *        Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}\n   * @param opt.include include coordinate system types.\n   */\n  function BrushTargetManager(finder, ecModel, opt) {\n    var _this = this;\n\n    this._targetInfoList = [];\n    var foundCpts = parseFinder$1(ecModel, finder);\n    each(targetInfoBuilders, function (builder, type) {\n      if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {\n        builder(foundCpts, _this._targetInfoList);\n      }\n    });\n  }\n\n  BrushTargetManager.prototype.setOutputRanges = function (areas, ecModel) {\n    this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {\n      (area.coordRanges || (area.coordRanges = [])).push(coordRange); // area.coordRange is the first of area.coordRanges\n\n      if (!area.coordRange) {\n        area.coordRange = coordRange; // In 'category' axis, coord to pixel is not reversible, so we can not\n        // rebuild range by coordRange accrately, which may bring trouble when\n        // brushing only one item. So we use __rangeOffset to rebuilding range\n        // by coordRange. And this it only used in brush component so it is no\n        // need to be adapted to coordRanges.\n\n        var result = coordConvert[area.brushType](0, coordSys, coordRange);\n        area.__rangeOffset = {\n          offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),\n          xyMinMax: result.xyMinMax\n        };\n      }\n    });\n    return areas;\n  };\n\n  BrushTargetManager.prototype.matchOutputRanges = function (areas, ecModel, cb) {\n    each(areas, function (area) {\n      var targetInfo = this.findTargetInfo(area, ecModel);\n\n      if (targetInfo && targetInfo !== true) {\n        each(targetInfo.coordSyses, function (coordSys) {\n          var result = coordConvert[area.brushType](1, coordSys, area.range, true);\n          cb(area, result.values, coordSys, ecModel);\n        });\n      }\n    }, this);\n  };\n  /**\n   * the `areas` is `BrushModel.areas`.\n   * Called in layout stage.\n   * convert `area.coordRange` to global range and set panelId to `area.range`.\n   */\n\n\n  BrushTargetManager.prototype.setInputRanges = function (areas, ecModel) {\n    each(areas, function (area) {\n      var targetInfo = this.findTargetInfo(area, ecModel);\n\n      if (\"development\" !== 'production') {\n        assert(!targetInfo || targetInfo === true || area.coordRange, 'coordRange must be specified when coord index specified.');\n        assert(!targetInfo || targetInfo !== true || area.range, 'range must be specified in global brush.');\n      }\n\n      area.range = area.range || []; // convert coordRange to global range and set panelId.\n\n      if (targetInfo && targetInfo !== true) {\n        area.panelId = targetInfo.panelId; // (1) area.range shoule always be calculate from coordRange but does\n        // not keep its original value, for the sake of the dataZoom scenario,\n        // where area.coordRange remains unchanged but area.range may be changed.\n        // (2) Only support converting one coordRange to pixel range in brush\n        // component. So do not consider `coordRanges`.\n        // (3) About __rangeOffset, see comment above.\n\n        var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);\n        var rangeOffset = area.__rangeOffset;\n        area.range = rangeOffset ? diffProcessor[area.brushType](result.values, rangeOffset.offset, getScales(result.xyMinMax, rangeOffset.xyMinMax)) : result.values;\n      }\n    }, this);\n  };\n\n  BrushTargetManager.prototype.makePanelOpts = function (api, getDefaultBrushType) {\n    return map(this._targetInfoList, function (targetInfo) {\n      var rect = targetInfo.getPanelRect();\n      return {\n        panelId: targetInfo.panelId,\n        defaultBrushType: getDefaultBrushType ? getDefaultBrushType(targetInfo) : null,\n        clipPath: makeRectPanelClipPath(rect),\n        isTargetByCursor: makeRectIsTargetByCursor(rect, api, targetInfo.coordSysModel),\n        getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect)\n      };\n    });\n  };\n\n  BrushTargetManager.prototype.controlSeries = function (area, seriesModel, ecModel) {\n    // Check whether area is bound in coord, and series do not belong to that coord.\n    // If do not do this check, some brush (like lineX) will controll all axes.\n    var targetInfo = this.findTargetInfo(area, ecModel);\n    return targetInfo === true || targetInfo && indexOf(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0;\n  };\n  /**\n   * If return Object, a coord found.\n   * If reutrn true, global found.\n   * Otherwise nothing found.\n   */\n\n\n  BrushTargetManager.prototype.findTargetInfo = function (area, ecModel) {\n    var targetInfoList = this._targetInfoList;\n    var foundCpts = parseFinder$1(ecModel, area);\n\n    for (var i = 0; i < targetInfoList.length; i++) {\n      var targetInfo = targetInfoList[i];\n      var areaPanelId = area.panelId;\n\n      if (areaPanelId) {\n        if (targetInfo.panelId === areaPanelId) {\n          return targetInfo;\n        }\n      } else {\n        for (var j = 0; j < targetInfoMatchers.length; j++) {\n          if (targetInfoMatchers[j](foundCpts, targetInfo)) {\n            return targetInfo;\n          }\n        }\n      }\n    }\n\n    return true;\n  };\n\n  return BrushTargetManager;\n}();\n\nfunction formatMinMax(minMax) {\n  minMax[0] > minMax[1] && minMax.reverse();\n  return minMax;\n}\n\nfunction parseFinder$1(ecModel, finder) {\n  return parseFinder(ecModel, finder, {\n    includeMainTypes: INCLUDE_FINDER_MAIN_TYPES\n  });\n}\n\nvar targetInfoBuilders = {\n  grid: function (foundCpts, targetInfoList) {\n    var xAxisModels = foundCpts.xAxisModels;\n    var yAxisModels = foundCpts.yAxisModels;\n    var gridModels = foundCpts.gridModels; // Remove duplicated.\n\n    var gridModelMap = createHashMap();\n    var xAxesHas = {};\n    var yAxesHas = {};\n\n    if (!xAxisModels && !yAxisModels && !gridModels) {\n      return;\n    }\n\n    each(xAxisModels, function (axisModel) {\n      var gridModel = axisModel.axis.grid.model;\n      gridModelMap.set(gridModel.id, gridModel);\n      xAxesHas[gridModel.id] = true;\n    });\n    each(yAxisModels, function (axisModel) {\n      var gridModel = axisModel.axis.grid.model;\n      gridModelMap.set(gridModel.id, gridModel);\n      yAxesHas[gridModel.id] = true;\n    });\n    each(gridModels, function (gridModel) {\n      gridModelMap.set(gridModel.id, gridModel);\n      xAxesHas[gridModel.id] = true;\n      yAxesHas[gridModel.id] = true;\n    });\n    gridModelMap.each(function (gridModel) {\n      var grid = gridModel.coordinateSystem;\n      var cartesians = [];\n      each(grid.getCartesians(), function (cartesian, index) {\n        if (indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0 || indexOf(yAxisModels, cartesian.getAxis('y').model) >= 0) {\n          cartesians.push(cartesian);\n        }\n      });\n      targetInfoList.push({\n        panelId: 'grid--' + gridModel.id,\n        gridModel: gridModel,\n        coordSysModel: gridModel,\n        // Use the first one as the representitive coordSys.\n        coordSys: cartesians[0],\n        coordSyses: cartesians,\n        getPanelRect: panelRectBuilders.grid,\n        xAxisDeclared: xAxesHas[gridModel.id],\n        yAxisDeclared: yAxesHas[gridModel.id]\n      });\n    });\n  },\n  geo: function (foundCpts, targetInfoList) {\n    each(foundCpts.geoModels, function (geoModel) {\n      var coordSys = geoModel.coordinateSystem;\n      targetInfoList.push({\n        panelId: 'geo--' + geoModel.id,\n        geoModel: geoModel,\n        coordSysModel: geoModel,\n        coordSys: coordSys,\n        coordSyses: [coordSys],\n        getPanelRect: panelRectBuilders.geo\n      });\n    });\n  }\n};\nvar targetInfoMatchers = [// grid\nfunction (foundCpts, targetInfo) {\n  var xAxisModel = foundCpts.xAxisModel;\n  var yAxisModel = foundCpts.yAxisModel;\n  var gridModel = foundCpts.gridModel;\n  !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);\n  !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);\n  return gridModel && gridModel === targetInfo.gridModel;\n}, // geo\nfunction (foundCpts, targetInfo) {\n  var geoModel = foundCpts.geoModel;\n  return geoModel && geoModel === targetInfo.geoModel;\n}];\nvar panelRectBuilders = {\n  grid: function () {\n    // grid is not Transformable.\n    return this.coordSys.master.getRect().clone();\n  },\n  geo: function () {\n    var coordSys = this.coordSys;\n    var rect = coordSys.getBoundingRect().clone(); // geo roam and zoom transform\n\n    rect.applyTransform(getTransform(coordSys));\n    return rect;\n  }\n};\nvar coordConvert = {\n  lineX: curry(axisConvert, 0),\n  lineY: curry(axisConvert, 1),\n  rect: function (to, coordSys, rangeOrCoordRange, clamp) {\n    var xminymin = to ? coordSys.pointToData([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp);\n    var xmaxymax = to ? coordSys.pointToData([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp);\n    var values = [formatMinMax([xminymin[0], xmaxymax[0]]), formatMinMax([xminymin[1], xmaxymax[1]])];\n    return {\n      values: values,\n      xyMinMax: values\n    };\n  },\n  polygon: function (to, coordSys, rangeOrCoordRange, clamp) {\n    var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];\n    var values = map(rangeOrCoordRange, function (item) {\n      var p = to ? coordSys.pointToData(item, clamp) : coordSys.dataToPoint(item, clamp);\n      xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);\n      xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);\n      xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]);\n      xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]);\n      return p;\n    });\n    return {\n      values: values,\n      xyMinMax: xyMinMax\n    };\n  }\n};\n\nfunction axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {\n  if (\"development\" !== 'production') {\n    assert(coordSys.type === 'cartesian2d', 'lineX/lineY brush is available only in cartesian2d.');\n  }\n\n  var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);\n  var values = formatMinMax(map([0, 1], function (i) {\n    return to ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]), true) : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));\n  }));\n  var xyMinMax = [];\n  xyMinMax[axisNameIndex] = values;\n  xyMinMax[1 - axisNameIndex] = [NaN, NaN];\n  return {\n    values: values,\n    xyMinMax: xyMinMax\n  };\n}\n\nvar diffProcessor = {\n  lineX: curry(axisDiffProcessor, 0),\n  lineY: curry(axisDiffProcessor, 1),\n  rect: function (values, refer, scales) {\n    return [[values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]], [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]];\n  },\n  polygon: function (values, refer, scales) {\n    return map(values, function (item, idx) {\n      return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];\n    });\n  }\n};\n\nfunction axisDiffProcessor(axisNameIndex, values, refer, scales) {\n  return [values[0] - scales[axisNameIndex] * refer[0], values[1] - scales[axisNameIndex] * refer[1]];\n} // We have to process scale caused by dataZoom manually,\n// although it might be not accurate.\n// Return [0~1, 0~1]\n\n\nfunction getScales(xyMinMaxCurr, xyMinMaxOrigin) {\n  var sizeCurr = getSize$1(xyMinMaxCurr);\n  var sizeOrigin = getSize$1(xyMinMaxOrigin);\n  var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];\n  isNaN(scales[0]) && (scales[0] = 1);\n  isNaN(scales[1]) && (scales[1] = 1);\n  return scales;\n}\n\nfunction getSize$1(xyMinMax) {\n  return xyMinMax ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] : [NaN, NaN];\n}\n\nvar each$a = each;\nvar DATA_ZOOM_ID_BASE = makeInternalComponentId('toolbox-dataZoom_');\n\nvar DataZoomFeature =\n/** @class */\nfunction (_super) {\n  __extends(DataZoomFeature, _super);\n\n  function DataZoomFeature() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  DataZoomFeature.prototype.render = function (featureModel, ecModel, api, payload) {\n    if (!this._brushController) {\n      this._brushController = new BrushController(api.getZr());\n\n      this._brushController.on('brush', bind(this._onBrush, this)).mount();\n    }\n\n    updateZoomBtnStatus(featureModel, ecModel, this, payload, api);\n    updateBackBtnStatus(featureModel, ecModel);\n  };\n\n  DataZoomFeature.prototype.onclick = function (ecModel, api, type) {\n    handlers$1[type].call(this);\n  };\n\n  DataZoomFeature.prototype.remove = function (ecModel, api) {\n    this._brushController && this._brushController.unmount();\n  };\n\n  DataZoomFeature.prototype.dispose = function (ecModel, api) {\n    this._brushController && this._brushController.dispose();\n  };\n\n  DataZoomFeature.prototype._onBrush = function (eventParam) {\n    var areas = eventParam.areas;\n\n    if (!eventParam.isEnd || !areas.length) {\n      return;\n    }\n\n    var snapshot = {};\n    var ecModel = this.ecModel;\n\n    this._brushController.updateCovers([]); // remove cover\n\n\n    var brushTargetManager = new BrushTargetManager(makeAxisFinder(this.model), ecModel, {\n      include: ['grid']\n    });\n    brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {\n      if (coordSys.type !== 'cartesian2d') {\n        return;\n      }\n\n      var brushType = area.brushType;\n\n      if (brushType === 'rect') {\n        setBatch('x', coordSys, coordRange[0]);\n        setBatch('y', coordSys, coordRange[1]);\n      } else {\n        setBatch({\n          lineX: 'x',\n          lineY: 'y'\n        }[brushType], coordSys, coordRange);\n      }\n    });\n    push(ecModel, snapshot);\n\n    this._dispatchZoomAction(snapshot);\n\n    function setBatch(dimName, coordSys, minMax) {\n      var axis = coordSys.getAxis(dimName);\n      var axisModel = axis.model;\n      var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); // Restrict range.\n\n      var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan();\n\n      if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) {\n        minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan);\n      }\n\n      dataZoomModel && (snapshot[dataZoomModel.id] = {\n        dataZoomId: dataZoomModel.id,\n        startValue: minMax[0],\n        endValue: minMax[1]\n      });\n    }\n\n    function findDataZoom(dimName, axisModel, ecModel) {\n      var found;\n      ecModel.eachComponent({\n        mainType: 'dataZoom',\n        subType: 'select'\n      }, function (dzModel) {\n        var has = dzModel.getAxisModel(dimName, axisModel.componentIndex);\n        has && (found = dzModel);\n      });\n      return found;\n    }\n  };\n\n  DataZoomFeature.prototype._dispatchZoomAction = function (snapshot) {\n    var batch = []; // Convert from hash map to array.\n\n    each$a(snapshot, function (batchItem, dataZoomId) {\n      batch.push(clone(batchItem));\n    });\n    batch.length && this.api.dispatchAction({\n      type: 'dataZoom',\n      from: this.uid,\n      batch: batch\n    });\n  };\n\n  DataZoomFeature.getDefaultOption = function (ecModel) {\n    var defaultOption = {\n      show: true,\n      filterMode: 'filter',\n      // Icon group\n      icon: {\n        zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1',\n        back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26'\n      },\n      // `zoom`, `back`\n      title: ecModel.getLocaleModel().get(['toolbox', 'dataZoom', 'title']),\n      brushStyle: {\n        borderWidth: 0,\n        color: 'rgba(210,219,238,0.2)'\n      }\n    };\n    return defaultOption;\n  };\n\n  return DataZoomFeature;\n}(ToolboxFeature);\n\nvar handlers$1 = {\n  zoom: function () {\n    var nextActive = !this._isZoomActive;\n    this.api.dispatchAction({\n      type: 'takeGlobalCursor',\n      key: 'dataZoomSelect',\n      dataZoomSelectActive: nextActive\n    });\n  },\n  back: function () {\n    this._dispatchZoomAction(pop(this.ecModel));\n  }\n};\n\nfunction makeAxisFinder(dzFeatureModel) {\n  var setting = {\n    xAxisIndex: dzFeatureModel.get('xAxisIndex', true),\n    yAxisIndex: dzFeatureModel.get('yAxisIndex', true),\n    xAxisId: dzFeatureModel.get('xAxisId', true),\n    yAxisId: dzFeatureModel.get('yAxisId', true)\n  }; // If both `xAxisIndex` `xAxisId` not set, it means 'all'.\n  // If both `yAxisIndex` `yAxisId` not set, it means 'all'.\n  // Some old cases set like this below to close yAxis control but leave xAxis control:\n  // `{ feature: { dataZoom: { yAxisIndex: false } }`.\n\n  if (setting.xAxisIndex == null && setting.xAxisId == null) {\n    setting.xAxisIndex = 'all';\n  }\n\n  if (setting.yAxisIndex == null && setting.yAxisId == null) {\n    setting.yAxisIndex = 'all';\n  }\n\n  return setting;\n}\n\nfunction updateBackBtnStatus(featureModel, ecModel) {\n  featureModel.setIconStatus('back', count(ecModel) > 1 ? 'emphasis' : 'normal');\n}\n\nfunction updateZoomBtnStatus(featureModel, ecModel, view, payload, api) {\n  var zoomActive = view._isZoomActive;\n\n  if (payload && payload.type === 'takeGlobalCursor') {\n    zoomActive = payload.key === 'dataZoomSelect' ? payload.dataZoomSelectActive : false;\n  }\n\n  view._isZoomActive = zoomActive;\n  featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal');\n  var brushTargetManager = new BrushTargetManager(makeAxisFinder(featureModel), ecModel, {\n    include: ['grid']\n  });\n  var panels = brushTargetManager.makePanelOpts(api, function (targetInfo) {\n    return targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared ? 'lineX' : !targetInfo.xAxisDeclared && targetInfo.yAxisDeclared ? 'lineY' : 'rect';\n  });\n\n  view._brushController.setPanels(panels).enableBrush(zoomActive && panels.length ? {\n    brushType: 'auto',\n    brushStyle: featureModel.getModel('brushStyle').getItemStyle()\n  } : false);\n}\n\nregisterInternalOptionCreator('dataZoom', function (ecModel) {\n  var toolboxModel = ecModel.getComponent('toolbox', 0);\n  var featureDataZoomPath = ['feature', 'dataZoom'];\n\n  if (!toolboxModel || toolboxModel.get(featureDataZoomPath) == null) {\n    return;\n  }\n\n  var dzFeatureModel = toolboxModel.getModel(featureDataZoomPath);\n  var dzOptions = [];\n  var finder = makeAxisFinder(dzFeatureModel);\n  var finderResult = parseFinder(ecModel, finder);\n  each$a(finderResult.xAxisModels, function (axisModel) {\n    return buildInternalOptions(axisModel, 'xAxis', 'xAxisIndex');\n  });\n  each$a(finderResult.yAxisModels, function (axisModel) {\n    return buildInternalOptions(axisModel, 'yAxis', 'yAxisIndex');\n  });\n\n  function buildInternalOptions(axisModel, axisMainType, axisIndexPropName) {\n    var axisIndex = axisModel.componentIndex;\n    var newOpt = {\n      type: 'select',\n      $fromToolbox: true,\n      // Default to be filter\n      filterMode: dzFeatureModel.get('filterMode', true) || 'filter',\n      // Id for merge mapping.\n      id: DATA_ZOOM_ID_BASE + axisMainType + axisIndex\n    };\n    newOpt[axisIndexPropName] = axisIndex;\n    dzOptions.push(newOpt);\n  }\n\n  return dzOptions;\n});\n\nfunction install$z(registers) {\n  registers.registerComponentModel(ToolboxModel);\n  registers.registerComponentView(ToolboxView);\n  registerFeature('saveAsImage', SaveAsImage);\n  registerFeature('magicType', MagicType);\n  registerFeature('dataView', DataView);\n  registerFeature('dataZoom', DataZoomFeature);\n  registerFeature('restore', RestoreOption);\n  use(install$y);\n}\n\nvar TooltipModel =\n/** @class */\nfunction (_super) {\n  __extends(TooltipModel, _super);\n\n  function TooltipModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TooltipModel.type;\n    return _this;\n  }\n\n  TooltipModel.type = 'tooltip';\n  TooltipModel.dependencies = ['axisPointer'];\n  TooltipModel.defaultOption = {\n    // zlevel: 0,\n    z: 60,\n    show: true,\n    // tooltip main content\n    showContent: true,\n    // 'trigger' only works on coordinate system.\n    // 'item' | 'axis' | 'none'\n    trigger: 'item',\n    // 'click' | 'mousemove' | 'none'\n    triggerOn: 'mousemove|click',\n    alwaysShowContent: false,\n    displayMode: 'single',\n    renderMode: 'auto',\n    // whether restraint content inside viewRect.\n    // If renderMode: 'richText', default true.\n    // If renderMode: 'html', defaut false (for backward compat).\n    confine: null,\n    showDelay: 0,\n    hideDelay: 100,\n    // Animation transition time, unit is second\n    transitionDuration: 0.4,\n    enterable: false,\n    backgroundColor: '#fff',\n    // box shadow\n    shadowBlur: 10,\n    shadowColor: 'rgba(0, 0, 0, .2)',\n    shadowOffsetX: 1,\n    shadowOffsetY: 2,\n    // tooltip border radius, unit is px, default is 4\n    borderRadius: 4,\n    // tooltip border width, unit is px, default is 0 (no border)\n    borderWidth: 1,\n    // Tooltip inside padding, default is 5 for all direction\n    // Array is allowed to set up, right, bottom, left, same with css\n    // The default value: See `tooltip/tooltipMarkup.ts#getPaddingFromTooltipModel`.\n    padding: null,\n    // Extra css text\n    extraCssText: '',\n    // axis indicator, trigger by axis\n    axisPointer: {\n      // default is line\n      // legal values: 'line' | 'shadow' | 'cross'\n      type: 'line',\n      // Valid when type is line, appoint tooltip line locate on which line. Optional\n      // legal values: 'x' | 'y' | 'angle' | 'radius' | 'auto'\n      // default is 'auto', chose the axis which type is category.\n      // for multiply y axis, cartesian coord chose x axis, polar chose angle axis\n      axis: 'auto',\n      animation: 'auto',\n      animationDurationUpdate: 200,\n      animationEasingUpdate: 'exponentialOut',\n      crossStyle: {\n        color: '#999',\n        width: 1,\n        type: 'dashed',\n        // TODO formatter\n        textStyle: {}\n      } // lineStyle and shadowStyle should not be specified here,\n      // otherwise it will always override those styles on option.axisPointer.\n\n    },\n    textStyle: {\n      color: '#666',\n      fontSize: 14\n    }\n  };\n  return TooltipModel;\n}(ComponentModel);\n\n/* global document */\n\nfunction shouldTooltipConfine(tooltipModel) {\n  var confineOption = tooltipModel.get('confine');\n  return confineOption != null ? !!confineOption // In richText mode, the outside part can not be visible.\n  : tooltipModel.get('renderMode') === 'richText';\n}\n\nfunction testStyle(styleProps) {\n  if (!env.domSupported) {\n    return;\n  }\n\n  var style = document.documentElement.style;\n\n  for (var i = 0, len = styleProps.length; i < len; i++) {\n    if (styleProps[i] in style) {\n      return styleProps[i];\n    }\n  }\n}\n\nvar TRANSFORM_VENDOR = testStyle(['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);\nvar TRANSITION_VENDOR = testStyle(['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);\nfunction toCSSVendorPrefix(styleVendor, styleProp) {\n  if (!styleVendor) {\n    return styleProp;\n  }\n\n  styleProp = toCamelCase(styleProp, true);\n  var idx = styleVendor.indexOf(styleProp);\n  styleVendor = idx === -1 ? styleProp : \"-\" + styleVendor.slice(0, idx) + \"-\" + styleProp;\n  return styleVendor.toLowerCase();\n}\nfunction getComputedStyle(el, style) {\n  var stl = el.currentStyle || document.defaultView && document.defaultView.getComputedStyle(el);\n  return stl ? style ? stl[style] : stl : null;\n}\n\n/* global document, window */\n\nvar CSS_TRANSITION_VENDOR = toCSSVendorPrefix(TRANSITION_VENDOR, 'transition');\nvar CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(TRANSFORM_VENDOR, 'transform'); // eslint-disable-next-line\n\nvar gCssText = \"position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;\" + (env.transform3dSupported ? 'will-change:transform;' : '');\n\nfunction mirrorPos(pos) {\n  pos = pos === 'left' ? 'right' : pos === 'right' ? 'left' : pos === 'top' ? 'bottom' : 'top';\n  return pos;\n}\n\nfunction assembleArrow(tooltipModel, borderColor, arrowPosition) {\n  if (!isString(arrowPosition) || arrowPosition === 'inside') {\n    return '';\n  }\n\n  var backgroundColor = tooltipModel.get('backgroundColor');\n  var borderWidth = tooltipModel.get('borderWidth');\n  borderColor = convertToColorString(borderColor);\n  var arrowPos = mirrorPos(arrowPosition);\n  var arrowSize = Math.max(Math.round(borderWidth) * 1.5, 6);\n  var positionStyle = '';\n  var transformStyle = CSS_TRANSFORM_VENDOR + ':';\n  var rotateDeg;\n\n  if (indexOf(['left', 'right'], arrowPos) > -1) {\n    positionStyle += 'top:50%';\n    transformStyle += \"translateY(-50%) rotate(\" + (rotateDeg = arrowPos === 'left' ? -225 : -45) + \"deg)\";\n  } else {\n    positionStyle += 'left:50%';\n    transformStyle += \"translateX(-50%) rotate(\" + (rotateDeg = arrowPos === 'top' ? 225 : 45) + \"deg)\";\n  }\n\n  var rotateRadian = rotateDeg * Math.PI / 180;\n  var arrowWH = arrowSize + borderWidth;\n  var rotatedWH = arrowWH * Math.abs(Math.cos(rotateRadian)) + arrowWH * Math.abs(Math.sin(rotateRadian));\n  var arrowOffset = Math.round(((rotatedWH - Math.SQRT2 * borderWidth) / 2 + Math.SQRT2 * borderWidth - (rotatedWH - arrowWH) / 2) * 100) / 100;\n  positionStyle += \";\" + arrowPos + \":-\" + arrowOffset + \"px\";\n  var borderStyle = borderColor + \" solid \" + borderWidth + \"px;\";\n  var styleCss = [\"position:absolute;width:\" + arrowSize + \"px;height:\" + arrowSize + \"px;z-index:-1;\", positionStyle + \";\" + transformStyle + \";\", \"border-bottom:\" + borderStyle, \"border-right:\" + borderStyle, \"background-color:\" + backgroundColor + \";\"];\n  return \"<div style=\\\"\" + styleCss.join('') + \"\\\"></div>\";\n}\n\nfunction assembleTransition(duration, onlyFade) {\n  var transitionCurve = 'cubic-bezier(0.23,1,0.32,1)';\n  var transitionOption = \" \" + duration / 2 + \"s \" + transitionCurve;\n  var transitionText = \"opacity\" + transitionOption + \",visibility\" + transitionOption;\n\n  if (!onlyFade) {\n    transitionOption = \" \" + duration + \"s \" + transitionCurve;\n    transitionText += env.transformSupported ? \",\" + CSS_TRANSFORM_VENDOR + transitionOption : \",left\" + transitionOption + \",top\" + transitionOption;\n  }\n\n  return CSS_TRANSITION_VENDOR + ':' + transitionText;\n}\n\nfunction assembleTransform(x, y, toString) {\n  // If using float on style, the final width of the dom might\n  // keep changing slightly while mouse move. So `toFixed(0)` them.\n  var x0 = x.toFixed(0) + 'px';\n  var y0 = y.toFixed(0) + 'px'; // not support transform, use `left` and `top` instead.\n\n  if (!env.transformSupported) {\n    return toString ? \"top:\" + y0 + \";left:\" + x0 + \";\" : [['top', y0], ['left', x0]];\n  } // support transform\n\n\n  var is3d = env.transform3dSupported;\n  var translate = \"translate\" + (is3d ? '3d' : '') + \"(\" + x0 + \",\" + y0 + (is3d ? ',0' : '') + \")\";\n  return toString ? 'top:0;left:0;' + CSS_TRANSFORM_VENDOR + ':' + translate + ';' : [['top', 0], ['left', 0], [TRANSFORM_VENDOR, translate]];\n}\n/**\n * @param {Object} textStyle\n * @return {string}\n * @inner\n */\n\n\nfunction assembleFont(textStyleModel) {\n  var cssText = [];\n  var fontSize = textStyleModel.get('fontSize');\n  var color = textStyleModel.getTextColor();\n  color && cssText.push('color:' + color);\n  cssText.push('font:' + textStyleModel.getFont());\n  fontSize // @ts-ignore, leave it to the tooltip refactor.\n  && cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');\n  var shadowColor = textStyleModel.get('textShadowColor');\n  var shadowBlur = textStyleModel.get('textShadowBlur') || 0;\n  var shadowOffsetX = textStyleModel.get('textShadowOffsetX') || 0;\n  var shadowOffsetY = textStyleModel.get('textShadowOffsetY') || 0;\n  shadowColor && shadowBlur && cssText.push('text-shadow:' + shadowOffsetX + 'px ' + shadowOffsetY + 'px ' + shadowBlur + 'px ' + shadowColor);\n  each(['decoration', 'align'], function (name) {\n    var val = textStyleModel.get(name);\n    val && cssText.push('text-' + name + ':' + val);\n  });\n  return cssText.join(';');\n}\n\nfunction assembleCssText(tooltipModel, enableTransition, onlyFade) {\n  var cssText = [];\n  var transitionDuration = tooltipModel.get('transitionDuration');\n  var backgroundColor = tooltipModel.get('backgroundColor');\n  var shadowBlur = tooltipModel.get('shadowBlur');\n  var shadowColor = tooltipModel.get('shadowColor');\n  var shadowOffsetX = tooltipModel.get('shadowOffsetX');\n  var shadowOffsetY = tooltipModel.get('shadowOffsetY');\n  var textStyleModel = tooltipModel.getModel('textStyle');\n  var padding = getPaddingFromTooltipModel(tooltipModel, 'html');\n  var boxShadow = shadowOffsetX + \"px \" + shadowOffsetY + \"px \" + shadowBlur + \"px \" + shadowColor;\n  cssText.push('box-shadow:' + boxShadow); // Animation transition. Do not animate when transitionDuration is 0.\n\n  enableTransition && transitionDuration && cssText.push(assembleTransition(transitionDuration, onlyFade));\n\n  if (backgroundColor) {\n    cssText.push('background-color:' + backgroundColor);\n  } // Border style\n\n\n  each(['width', 'color', 'radius'], function (name) {\n    var borderName = 'border-' + name;\n    var camelCase = toCamelCase(borderName);\n    var val = tooltipModel.get(camelCase);\n    val != null && cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));\n  }); // Text style\n\n  cssText.push(assembleFont(textStyleModel)); // Padding\n\n  if (padding != null) {\n    cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px');\n  }\n\n  return cssText.join(';') + ';';\n} // If not able to make, do not modify the input `out`.\n\n\nfunction makeStyleCoord(out, zr, appendToBody, zrX, zrY) {\n  var zrPainter = zr && zr.painter;\n\n  if (appendToBody) {\n    var zrViewportRoot = zrPainter && zrPainter.getViewportRoot();\n\n    if (zrViewportRoot) {\n      // Some APPs might use scale on body, so we support CSS transform here.\n      transformLocalCoord(out, zrViewportRoot, document.body, zrX, zrY);\n    }\n  } else {\n    out[0] = zrX;\n    out[1] = zrY; // xy should be based on canvas root. But tooltipContent is\n    // the sibling of canvas root. So padding of ec container\n    // should be considered here.\n\n    var viewportRootOffset = zrPainter && zrPainter.getViewportRootOffset();\n\n    if (viewportRootOffset) {\n      out[0] += viewportRootOffset.offsetLeft;\n      out[1] += viewportRootOffset.offsetTop;\n    }\n  }\n\n  out[2] = out[0] / zr.getWidth();\n  out[3] = out[1] / zr.getHeight();\n}\n\nvar TooltipHTMLContent =\n/** @class */\nfunction () {\n  function TooltipHTMLContent(container, api, opt) {\n    this._show = false;\n    this._styleCoord = [0, 0, 0, 0];\n    this._enterable = true;\n    this._firstShow = true;\n    this._longHide = true;\n\n    if (env.wxa) {\n      return null;\n    }\n\n    var el = document.createElement('div'); // TODO: TYPE\n\n    el.domBelongToZr = true;\n    this.el = el;\n    var zr = this._zr = api.getZr();\n    var appendToBody = this._appendToBody = opt && opt.appendToBody;\n    makeStyleCoord(this._styleCoord, zr, appendToBody, api.getWidth() / 2, api.getHeight() / 2);\n\n    if (appendToBody) {\n      document.body.appendChild(el);\n    } else {\n      container.appendChild(el);\n    }\n\n    this._container = container; // FIXME\n    // Is it needed to trigger zr event manually if\n    // the browser do not support `pointer-events: none`.\n\n    var self = this;\n\n    el.onmouseenter = function () {\n      // clear the timeout in hideLater and keep showing tooltip\n      if (self._enterable) {\n        clearTimeout(self._hideTimeout);\n        self._show = true;\n      }\n\n      self._inContent = true;\n    };\n\n    el.onmousemove = function (e) {\n      e = e || window.event;\n\n      if (!self._enterable) {\n        // `pointer-events: none` is set to tooltip content div\n        // if `enterable` is set as `false`, and `el.onmousemove`\n        // can not be triggered. But in browser that do not\n        // support `pointer-events`, we need to do this:\n        // Try trigger zrender event to avoid mouse\n        // in and out shape too frequently\n        var handler = zr.handler;\n        var zrViewportRoot = zr.painter.getViewportRoot();\n        normalizeEvent(zrViewportRoot, e, true);\n        handler.dispatch('mousemove', e);\n      }\n    };\n\n    el.onmouseleave = function () {\n      // set `_inContent` to `false` before `hideLater`\n      self._inContent = false;\n\n      if (self._enterable) {\n        if (self._show) {\n          self.hideLater(self._hideDelay);\n        }\n      }\n    };\n  }\n  /**\n   * Update when tooltip is rendered\n   */\n\n\n  TooltipHTMLContent.prototype.update = function (tooltipModel) {\n    // FIXME\n    // Move this logic to ec main?\n    var container = this._container;\n    var position = getComputedStyle(container, 'position');\n    var domStyle = container.style;\n\n    if (domStyle.position !== 'absolute' && position !== 'absolute') {\n      domStyle.position = 'relative';\n    } // move tooltip if chart resized\n\n\n    var alwaysShowContent = tooltipModel.get('alwaysShowContent');\n    alwaysShowContent && this._moveIfResized(); // update className\n\n    this.el.className = tooltipModel.get('className') || ''; // Hide the tooltip\n    // PENDING\n    // this.hide();\n  };\n\n  TooltipHTMLContent.prototype.show = function (tooltipModel, nearPointColor) {\n    clearTimeout(this._hideTimeout);\n    clearTimeout(this._longHideTimeout);\n    var el = this.el;\n    var style = el.style;\n    var styleCoord = this._styleCoord;\n\n    if (!el.innerHTML) {\n      style.display = 'none';\n    } else {\n      style.cssText = gCssText + assembleCssText(tooltipModel, !this._firstShow, this._longHide) // initial transform\n      + assembleTransform(styleCoord[0], styleCoord[1], true) + (\"border-color:\" + convertToColorString(nearPointColor) + \";\") + (tooltipModel.get('extraCssText') || '') // If mouse occasionally move over the tooltip, a mouseout event will be\n      // triggered by canvas, and cause some unexpectable result like dragging\n      // stop, \"unfocusAdjacency\". Here `pointer-events: none` is used to solve\n      // it. Although it is not supported by IE8~IE10, fortunately it is a rare\n      // scenario.\n      + (\";pointer-events:\" + (this._enterable ? 'auto' : 'none'));\n    }\n\n    this._show = true;\n    this._firstShow = false;\n    this._longHide = false;\n  };\n\n  TooltipHTMLContent.prototype.setContent = function (content, markers, tooltipModel, borderColor, arrowPosition) {\n    var el = this.el;\n\n    if (content == null) {\n      el.innerHTML = '';\n      return;\n    }\n\n    var arrow = '';\n\n    if (isString(arrowPosition) && tooltipModel.get('trigger') === 'item' && !shouldTooltipConfine(tooltipModel)) {\n      arrow = assembleArrow(tooltipModel, borderColor, arrowPosition);\n    }\n\n    if (isString(content)) {\n      el.innerHTML = content + arrow;\n    } else if (content) {\n      // Clear previous\n      el.innerHTML = '';\n\n      if (!isArray(content)) {\n        content = [content];\n      }\n\n      for (var i = 0; i < content.length; i++) {\n        if (isDom(content[i]) && content[i].parentNode !== el) {\n          el.appendChild(content[i]);\n        }\n      } // no arrow if empty\n\n\n      if (arrow && el.childNodes.length) {\n        // no need to create a new parent element, but it's not supported by IE 10 and older.\n        // const arrowEl = document.createRange().createContextualFragment(arrow);\n        var arrowEl = document.createElement('div');\n        arrowEl.innerHTML = arrow;\n        el.appendChild(arrowEl);\n      }\n    }\n  };\n\n  TooltipHTMLContent.prototype.setEnterable = function (enterable) {\n    this._enterable = enterable;\n  };\n\n  TooltipHTMLContent.prototype.getSize = function () {\n    var el = this.el;\n    return [el.offsetWidth, el.offsetHeight];\n  };\n\n  TooltipHTMLContent.prototype.moveTo = function (zrX, zrY) {\n    var styleCoord = this._styleCoord;\n    makeStyleCoord(styleCoord, this._zr, this._appendToBody, zrX, zrY);\n\n    if (styleCoord[0] != null && styleCoord[1] != null) {\n      var style_1 = this.el.style;\n      var transforms = assembleTransform(styleCoord[0], styleCoord[1]);\n      each(transforms, function (transform) {\n        style_1[transform[0]] = transform[1];\n      });\n    }\n  };\n  /**\n   * when `alwaysShowContent` is true,\n   * move the tooltip after chart resized\n   */\n\n\n  TooltipHTMLContent.prototype._moveIfResized = function () {\n    // The ratio of left to width\n    var ratioX = this._styleCoord[2]; // The ratio of top to height\n\n    var ratioY = this._styleCoord[3];\n    this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight());\n  };\n\n  TooltipHTMLContent.prototype.hide = function () {\n    var _this = this;\n\n    var style = this.el.style;\n    style.visibility = 'hidden';\n    style.opacity = '0';\n    env.transform3dSupported && (style.willChange = '');\n    this._show = false;\n    this._longHideTimeout = setTimeout(function () {\n      return _this._longHide = true;\n    }, 500);\n  };\n\n  TooltipHTMLContent.prototype.hideLater = function (time) {\n    if (this._show && !(this._inContent && this._enterable)) {\n      if (time) {\n        this._hideDelay = time; // Set show false to avoid invoke hideLater multiple times\n\n        this._show = false;\n        this._hideTimeout = setTimeout(bind(this.hide, this), time);\n      } else {\n        this.hide();\n      }\n    }\n  };\n\n  TooltipHTMLContent.prototype.isShow = function () {\n    return this._show;\n  };\n\n  TooltipHTMLContent.prototype.dispose = function () {\n    this.el.parentNode.removeChild(this.el);\n  };\n\n  return TooltipHTMLContent;\n}();\n\nvar TooltipRichContent =\n/** @class */\nfunction () {\n  function TooltipRichContent(api) {\n    this._show = false;\n    this._styleCoord = [0, 0, 0, 0];\n    this._enterable = true;\n    this._zr = api.getZr();\n    makeStyleCoord$1(this._styleCoord, this._zr, api.getWidth() / 2, api.getHeight() / 2);\n  }\n  /**\n   * Update when tooltip is rendered\n   */\n\n\n  TooltipRichContent.prototype.update = function (tooltipModel) {\n    var alwaysShowContent = tooltipModel.get('alwaysShowContent');\n    alwaysShowContent && this._moveIfResized();\n  };\n\n  TooltipRichContent.prototype.show = function () {\n    if (this._hideTimeout) {\n      clearTimeout(this._hideTimeout);\n    }\n\n    this.el.show();\n    this._show = true;\n  };\n  /**\n   * Set tooltip content\n   */\n\n\n  TooltipRichContent.prototype.setContent = function (content, markupStyleCreator, tooltipModel, borderColor, arrowPosition) {\n    var _this = this;\n\n    if (isObject(content)) {\n      throwError(\"development\" !== 'production' ? 'Passing DOM nodes as content is not supported in richText tooltip!' : '');\n    }\n\n    if (this.el) {\n      this._zr.remove(this.el);\n    }\n\n    var textStyleModel = tooltipModel.getModel('textStyle');\n    this.el = new ZRText({\n      style: {\n        rich: markupStyleCreator.richTextStyles,\n        text: content,\n        lineHeight: 22,\n        borderWidth: 1,\n        borderColor: borderColor,\n        textShadowColor: textStyleModel.get('textShadowColor'),\n        fill: tooltipModel.get(['textStyle', 'color']),\n        padding: getPaddingFromTooltipModel(tooltipModel, 'richText'),\n        verticalAlign: 'top',\n        align: 'left'\n      },\n      z: tooltipModel.get('z')\n    });\n    each(['backgroundColor', 'borderRadius', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'], function (propName) {\n      _this.el.style[propName] = tooltipModel.get(propName);\n    });\n    each(['textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'], function (propName) {\n      _this.el.style[propName] = textStyleModel.get(propName) || 0;\n    });\n\n    this._zr.add(this.el);\n\n    var self = this;\n    this.el.on('mouseover', function () {\n      // clear the timeout in hideLater and keep showing tooltip\n      if (self._enterable) {\n        clearTimeout(self._hideTimeout);\n        self._show = true;\n      }\n\n      self._inContent = true;\n    });\n    this.el.on('mouseout', function () {\n      if (self._enterable) {\n        if (self._show) {\n          self.hideLater(self._hideDelay);\n        }\n      }\n\n      self._inContent = false;\n    });\n  };\n\n  TooltipRichContent.prototype.setEnterable = function (enterable) {\n    this._enterable = enterable;\n  };\n\n  TooltipRichContent.prototype.getSize = function () {\n    var el = this.el;\n    var bounding = this.el.getBoundingRect(); // bounding rect does not include shadow. For renderMode richText,\n    // if overflow, it will be cut. So calculate them accurately.\n\n    var shadowOuterSize = calcShadowOuterSize(el.style);\n    return [bounding.width + shadowOuterSize.left + shadowOuterSize.right, bounding.height + shadowOuterSize.top + shadowOuterSize.bottom];\n  };\n\n  TooltipRichContent.prototype.moveTo = function (x, y) {\n    var el = this.el;\n\n    if (el) {\n      var styleCoord = this._styleCoord;\n      makeStyleCoord$1(styleCoord, this._zr, x, y);\n      x = styleCoord[0];\n      y = styleCoord[1];\n      var style = el.style;\n      var borderWidth = mathMaxWith0(style.borderWidth || 0);\n      var shadowOuterSize = calcShadowOuterSize(style); // rich text x, y do not include border.\n\n      el.x = x + borderWidth + shadowOuterSize.left;\n      el.y = y + borderWidth + shadowOuterSize.top;\n      el.markRedraw();\n    }\n  };\n  /**\n   * when `alwaysShowContent` is true,\n   * move the tooltip after chart resized\n   */\n\n\n  TooltipRichContent.prototype._moveIfResized = function () {\n    // The ratio of left to width\n    var ratioX = this._styleCoord[2]; // The ratio of top to height\n\n    var ratioY = this._styleCoord[3];\n    this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight());\n  };\n\n  TooltipRichContent.prototype.hide = function () {\n    if (this.el) {\n      this.el.hide();\n    }\n\n    this._show = false;\n  };\n\n  TooltipRichContent.prototype.hideLater = function (time) {\n    if (this._show && !(this._inContent && this._enterable)) {\n      if (time) {\n        this._hideDelay = time; // Set show false to avoid invoke hideLater multiple times\n\n        this._show = false;\n        this._hideTimeout = setTimeout(bind(this.hide, this), time);\n      } else {\n        this.hide();\n      }\n    }\n  };\n\n  TooltipRichContent.prototype.isShow = function () {\n    return this._show;\n  };\n\n  TooltipRichContent.prototype.dispose = function () {\n    this._zr.remove(this.el);\n  };\n\n  return TooltipRichContent;\n}();\n\nfunction mathMaxWith0(val) {\n  return Math.max(0, val);\n}\n\nfunction calcShadowOuterSize(style) {\n  var shadowBlur = mathMaxWith0(style.shadowBlur || 0);\n  var shadowOffsetX = mathMaxWith0(style.shadowOffsetX || 0);\n  var shadowOffsetY = mathMaxWith0(style.shadowOffsetY || 0);\n  return {\n    left: mathMaxWith0(shadowBlur - shadowOffsetX),\n    right: mathMaxWith0(shadowBlur + shadowOffsetX),\n    top: mathMaxWith0(shadowBlur - shadowOffsetY),\n    bottom: mathMaxWith0(shadowBlur + shadowOffsetY)\n  };\n}\n\nfunction makeStyleCoord$1(out, zr, zrX, zrY) {\n  out[0] = zrX;\n  out[1] = zrY;\n  out[2] = out[0] / zr.getWidth();\n  out[3] = out[1] / zr.getHeight();\n}\n\nvar proxyRect = new Rect({\n  shape: {\n    x: -1,\n    y: -1,\n    width: 2,\n    height: 2\n  }\n});\n\nvar TooltipView =\n/** @class */\nfunction (_super) {\n  __extends(TooltipView, _super);\n\n  function TooltipView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TooltipView.type;\n    return _this;\n  }\n\n  TooltipView.prototype.init = function (ecModel, api) {\n    if (env.node || !api.getDom()) {\n      return;\n    }\n\n    var tooltipModel = ecModel.getComponent('tooltip');\n    var renderMode = this._renderMode = getTooltipRenderMode(tooltipModel.get('renderMode'));\n    this._tooltipContent = renderMode === 'richText' ? new TooltipRichContent(api) : new TooltipHTMLContent(api.getDom(), api, {\n      appendToBody: tooltipModel.get('appendToBody', true)\n    });\n  };\n\n  TooltipView.prototype.render = function (tooltipModel, ecModel, api) {\n    if (env.node || !api.getDom()) {\n      return;\n    } // Reset\n\n\n    this.group.removeAll();\n    this._tooltipModel = tooltipModel;\n    this._ecModel = ecModel;\n    this._api = api;\n    /**\n     * @private\n     * @type {boolean}\n     */\n\n    this._alwaysShowContent = tooltipModel.get('alwaysShowContent');\n    var tooltipContent = this._tooltipContent;\n    tooltipContent.update(tooltipModel);\n    tooltipContent.setEnterable(tooltipModel.get('enterable'));\n\n    this._initGlobalListener();\n\n    this._keepShow(); // PENDING\n    // `mousemove` event will be triggered very frequently when the mouse moves fast,\n    // which causes that the `updatePosition` function was also called frequently.\n    // In Chrome with devtools open and Firefox, tooltip looks laggy and shakes. See #14695 #16101\n    // To avoid frequent triggering,\n    // consider throttling it in 50ms when transition is enabled\n\n\n    if (this._renderMode !== 'richText' && tooltipModel.get('transitionDuration')) {\n      createOrUpdate(this, '_updatePosition', 50, 'fixRate');\n    } else {\n      clear(this, '_updatePosition');\n    }\n  };\n\n  TooltipView.prototype._initGlobalListener = function () {\n    var tooltipModel = this._tooltipModel;\n    var triggerOn = tooltipModel.get('triggerOn');\n    register('itemTooltip', this._api, bind(function (currTrigger, e, dispatchAction) {\n      // If 'none', it is not controlled by mouse totally.\n      if (triggerOn !== 'none') {\n        if (triggerOn.indexOf(currTrigger) >= 0) {\n          this._tryShow(e, dispatchAction);\n        } else if (currTrigger === 'leave') {\n          this._hide(dispatchAction);\n        }\n      }\n    }, this));\n  };\n\n  TooltipView.prototype._keepShow = function () {\n    var tooltipModel = this._tooltipModel;\n    var ecModel = this._ecModel;\n    var api = this._api;\n    var triggerOn = tooltipModel.get('triggerOn'); // Try to keep the tooltip show when refreshing\n\n    if (this._lastX != null && this._lastY != null // When user is willing to control tooltip totally using API,\n    // self.manuallyShowTip({x, y}) might cause tooltip hide,\n    // which is not expected.\n    && triggerOn !== 'none' && triggerOn !== 'click') {\n      var self_1 = this;\n      clearTimeout(this._refreshUpdateTimeout);\n      this._refreshUpdateTimeout = setTimeout(function () {\n        // Show tip next tick after other charts are rendered\n        // In case highlight action has wrong result\n        // FIXME\n        !api.isDisposed() && self_1.manuallyShowTip(tooltipModel, ecModel, api, {\n          x: self_1._lastX,\n          y: self_1._lastY,\n          dataByCoordSys: self_1._lastDataByCoordSys\n        });\n      });\n    }\n  };\n  /**\n   * Show tip manually by\n   * dispatchAction({\n   *     type: 'showTip',\n   *     x: 10,\n   *     y: 10\n   * });\n   * Or\n   * dispatchAction({\n   *      type: 'showTip',\n   *      seriesIndex: 0,\n   *      dataIndex or dataIndexInside or name\n   * });\n   *\n   *  TODO Batch\n   */\n\n\n  TooltipView.prototype.manuallyShowTip = function (tooltipModel, ecModel, api, payload) {\n    if (payload.from === this.uid || env.node || !api.getDom()) {\n      return;\n    }\n\n    var dispatchAction = makeDispatchAction$1(payload, api); // Reset ticket\n\n    this._ticket = ''; // When triggered from axisPointer.\n\n    var dataByCoordSys = payload.dataByCoordSys;\n    var cmptRef = findComponentReference(payload, ecModel, api);\n\n    if (cmptRef) {\n      var rect = cmptRef.el.getBoundingRect().clone();\n      rect.applyTransform(cmptRef.el.transform);\n\n      this._tryShow({\n        offsetX: rect.x + rect.width / 2,\n        offsetY: rect.y + rect.height / 2,\n        target: cmptRef.el,\n        position: payload.position,\n        // When manully trigger, the mouse is not on the el, so we'd better to\n        // position tooltip on the bottom of the el and display arrow is possible.\n        positionDefault: 'bottom'\n      }, dispatchAction);\n    } else if (payload.tooltip && payload.x != null && payload.y != null) {\n      var el = proxyRect;\n      el.x = payload.x;\n      el.y = payload.y;\n      el.update();\n      getECData(el).tooltipConfig = {\n        name: null,\n        option: payload.tooltip\n      }; // Manually show tooltip while view is not using zrender elements.\n\n      this._tryShow({\n        offsetX: payload.x,\n        offsetY: payload.y,\n        target: el\n      }, dispatchAction);\n    } else if (dataByCoordSys) {\n      this._tryShow({\n        offsetX: payload.x,\n        offsetY: payload.y,\n        position: payload.position,\n        dataByCoordSys: dataByCoordSys,\n        tooltipOption: payload.tooltipOption\n      }, dispatchAction);\n    } else if (payload.seriesIndex != null) {\n      if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) {\n        return;\n      }\n\n      var pointInfo = findPointFromSeries(payload, ecModel);\n      var cx = pointInfo.point[0];\n      var cy = pointInfo.point[1];\n\n      if (cx != null && cy != null) {\n        this._tryShow({\n          offsetX: cx,\n          offsetY: cy,\n          target: pointInfo.el,\n          position: payload.position,\n          // When manully trigger, the mouse is not on the el, so we'd better to\n          // position tooltip on the bottom of the el and display arrow is possible.\n          positionDefault: 'bottom'\n        }, dispatchAction);\n      }\n    } else if (payload.x != null && payload.y != null) {\n      // FIXME\n      // should wrap dispatchAction like `axisPointer/globalListener` ?\n      api.dispatchAction({\n        type: 'updateAxisPointer',\n        x: payload.x,\n        y: payload.y\n      });\n\n      this._tryShow({\n        offsetX: payload.x,\n        offsetY: payload.y,\n        position: payload.position,\n        target: api.getZr().findHover(payload.x, payload.y).target\n      }, dispatchAction);\n    }\n  };\n\n  TooltipView.prototype.manuallyHideTip = function (tooltipModel, ecModel, api, payload) {\n    var tooltipContent = this._tooltipContent;\n\n    if (!this._alwaysShowContent && this._tooltipModel) {\n      tooltipContent.hideLater(this._tooltipModel.get('hideDelay'));\n    }\n\n    this._lastX = this._lastY = this._lastDataByCoordSys = null;\n\n    if (payload.from !== this.uid) {\n      this._hide(makeDispatchAction$1(payload, api));\n    }\n  }; // Be compatible with previous design, that is, when tooltip.type is 'axis' and\n  // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer\n  // and tooltip.\n\n\n  TooltipView.prototype._manuallyAxisShowTip = function (tooltipModel, ecModel, api, payload) {\n    var seriesIndex = payload.seriesIndex;\n    var dataIndex = payload.dataIndex; // @ts-ignore\n\n    var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;\n\n    if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {\n      return;\n    }\n\n    var seriesModel = ecModel.getSeriesByIndex(seriesIndex);\n\n    if (!seriesModel) {\n      return;\n    }\n\n    var data = seriesModel.getData();\n    var tooltipCascadedModel = buildTooltipModel([data.getItemModel(dataIndex), seriesModel, (seriesModel.coordinateSystem || {}).model], this._tooltipModel);\n\n    if (tooltipCascadedModel.get('trigger') !== 'axis') {\n      return;\n    }\n\n    api.dispatchAction({\n      type: 'updateAxisPointer',\n      seriesIndex: seriesIndex,\n      dataIndex: dataIndex,\n      position: payload.position\n    });\n    return true;\n  };\n\n  TooltipView.prototype._tryShow = function (e, dispatchAction) {\n    var el = e.target;\n    var tooltipModel = this._tooltipModel;\n\n    if (!tooltipModel) {\n      return;\n    } // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed\n\n\n    this._lastX = e.offsetX;\n    this._lastY = e.offsetY;\n    var dataByCoordSys = e.dataByCoordSys;\n\n    if (dataByCoordSys && dataByCoordSys.length) {\n      this._showAxisTooltip(dataByCoordSys, e);\n    } else if (el) {\n      this._lastDataByCoordSys = null;\n      var seriesDispatcher_1;\n      var cmptDispatcher_1;\n      findEventDispatcher(el, function (target) {\n        // Always show item tooltip if mouse is on the element with dataIndex\n        if (getECData(target).dataIndex != null) {\n          seriesDispatcher_1 = target;\n          return true;\n        } // Tooltip provided directly. Like legend.\n\n\n        if (getECData(target).tooltipConfig != null) {\n          cmptDispatcher_1 = target;\n          return true;\n        }\n      }, true);\n\n      if (seriesDispatcher_1) {\n        this._showSeriesItemTooltip(e, seriesDispatcher_1, dispatchAction);\n      } else if (cmptDispatcher_1) {\n        this._showComponentItemTooltip(e, cmptDispatcher_1, dispatchAction);\n      } else {\n        this._hide(dispatchAction);\n      }\n    } else {\n      this._lastDataByCoordSys = null;\n\n      this._hide(dispatchAction);\n    }\n  };\n\n  TooltipView.prototype._showOrMove = function (tooltipModel, cb) {\n    // showDelay is used in this case: tooltip.enterable is set\n    // as true. User intent to move mouse into tooltip and click\n    // something. `showDelay` makes it easier to enter the content\n    // but tooltip do not move immediately.\n    var delay = tooltipModel.get('showDelay');\n    cb = bind(cb, this);\n    clearTimeout(this._showTimout);\n    delay > 0 ? this._showTimout = setTimeout(cb, delay) : cb();\n  };\n\n  TooltipView.prototype._showAxisTooltip = function (dataByCoordSys, e) {\n    var ecModel = this._ecModel;\n    var globalTooltipModel = this._tooltipModel;\n    var point = [e.offsetX, e.offsetY];\n    var singleTooltipModel = buildTooltipModel([e.tooltipOption], globalTooltipModel);\n    var renderMode = this._renderMode;\n    var cbParamsList = [];\n    var articleMarkup = createTooltipMarkup('section', {\n      blocks: [],\n      noHeader: true\n    }); // Only for legacy: `Serise['formatTooltip']` returns a string.\n\n    var markupTextArrLegacy = [];\n    var markupStyleCreator = new TooltipMarkupStyleCreator();\n    each(dataByCoordSys, function (itemCoordSys) {\n      each(itemCoordSys.dataByAxis, function (axisItem) {\n        var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex);\n        var axisValue = axisItem.value;\n\n        if (!axisModel || axisValue == null) {\n          return;\n        }\n\n        var axisValueLabel = getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt);\n        var axisSectionMarkup = createTooltipMarkup('section', {\n          header: axisValueLabel,\n          noHeader: !trim(axisValueLabel),\n          sortBlocks: true,\n          blocks: []\n        });\n        articleMarkup.blocks.push(axisSectionMarkup);\n        each(axisItem.seriesDataIndices, function (idxItem) {\n          var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);\n          var dataIndex = idxItem.dataIndexInside;\n          var cbParams = series.getDataParams(dataIndex); // Can't find data.\n\n          if (cbParams.dataIndex < 0) {\n            return;\n          }\n\n          cbParams.axisDim = axisItem.axisDim;\n          cbParams.axisIndex = axisItem.axisIndex;\n          cbParams.axisType = axisItem.axisType;\n          cbParams.axisId = axisItem.axisId;\n          cbParams.axisValue = getAxisRawValue(axisModel.axis, {\n            value: axisValue\n          });\n          cbParams.axisValueLabel = axisValueLabel; // Pre-create marker style for makers. Users can assemble richText\n          // text in `formatter` callback and use those markers style.\n\n          cbParams.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(cbParams.color), renderMode);\n          var seriesTooltipResult = normalizeTooltipFormatResult(series.formatTooltip(dataIndex, true, null));\n          var frag = seriesTooltipResult.frag;\n\n          if (frag) {\n            var valueFormatter = buildTooltipModel([series], globalTooltipModel).get('valueFormatter');\n            axisSectionMarkup.blocks.push(valueFormatter ? extend({\n              valueFormatter: valueFormatter\n            }, frag) : frag);\n          }\n\n          if (seriesTooltipResult.text) {\n            markupTextArrLegacy.push(seriesTooltipResult.text);\n          }\n\n          cbParamsList.push(cbParams);\n        });\n      });\n    }); // In most cases, the second axis is displays upper on the first one.\n    // So we reverse it to look better.\n\n    articleMarkup.blocks.reverse();\n    markupTextArrLegacy.reverse();\n    var positionExpr = e.position;\n    var orderMode = singleTooltipModel.get('order');\n    var builtMarkupText = buildTooltipMarkup(articleMarkup, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), singleTooltipModel.get('textStyle'));\n    builtMarkupText && markupTextArrLegacy.unshift(builtMarkupText);\n    var blockBreak = renderMode === 'richText' ? '\\n\\n' : '<br/>';\n    var allMarkupText = markupTextArrLegacy.join(blockBreak);\n\n    this._showOrMove(singleTooltipModel, function () {\n      if (this._updateContentNotChangedOnAxis(dataByCoordSys, cbParamsList)) {\n        this._updatePosition(singleTooltipModel, positionExpr, point[0], point[1], this._tooltipContent, cbParamsList);\n      } else {\n        this._showTooltipContent(singleTooltipModel, allMarkupText, cbParamsList, Math.random() + '', point[0], point[1], positionExpr, null, markupStyleCreator);\n      }\n    }); // Do not trigger events here, because this branch only be entered\n    // from dispatchAction.\n\n  };\n\n  TooltipView.prototype._showSeriesItemTooltip = function (e, dispatcher, dispatchAction) {\n    var ecModel = this._ecModel;\n    var ecData = getECData(dispatcher); // Use dataModel in element if possible\n    // Used when mouseover on a element like markPoint or edge\n    // In which case, the data is not main data in series.\n\n    var seriesIndex = ecData.seriesIndex;\n    var seriesModel = ecModel.getSeriesByIndex(seriesIndex); // For example, graph link.\n\n    var dataModel = ecData.dataModel || seriesModel;\n    var dataIndex = ecData.dataIndex;\n    var dataType = ecData.dataType;\n    var data = dataModel.getData(dataType);\n    var renderMode = this._renderMode;\n    var positionDefault = e.positionDefault;\n    var tooltipModel = buildTooltipModel([data.getItemModel(dataIndex), dataModel, seriesModel && (seriesModel.coordinateSystem || {}).model], this._tooltipModel, positionDefault ? {\n      position: positionDefault\n    } : null);\n    var tooltipTrigger = tooltipModel.get('trigger');\n\n    if (tooltipTrigger != null && tooltipTrigger !== 'item') {\n      return;\n    }\n\n    var params = dataModel.getDataParams(dataIndex, dataType);\n    var markupStyleCreator = new TooltipMarkupStyleCreator(); // Pre-create marker style for makers. Users can assemble richText\n    // text in `formatter` callback and use those markers style.\n\n    params.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(params.color), renderMode);\n    var seriesTooltipResult = normalizeTooltipFormatResult(dataModel.formatTooltip(dataIndex, false, dataType));\n    var orderMode = tooltipModel.get('order');\n    var valueFormatter = tooltipModel.get('valueFormatter');\n    var frag = seriesTooltipResult.frag;\n    var markupText = frag ? buildTooltipMarkup(valueFormatter ? extend({\n      valueFormatter: valueFormatter\n    }, frag) : frag, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), tooltipModel.get('textStyle')) : seriesTooltipResult.text;\n    var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;\n\n    this._showOrMove(tooltipModel, function () {\n      this._showTooltipContent(tooltipModel, markupText, params, asyncTicket, e.offsetX, e.offsetY, e.position, e.target, markupStyleCreator);\n    }); // FIXME\n    // duplicated showtip if manuallyShowTip is called from dispatchAction.\n\n\n    dispatchAction({\n      type: 'showTip',\n      dataIndexInside: dataIndex,\n      dataIndex: data.getRawIndex(dataIndex),\n      seriesIndex: seriesIndex,\n      from: this.uid\n    });\n  };\n\n  TooltipView.prototype._showComponentItemTooltip = function (e, el, dispatchAction) {\n    var ecData = getECData(el);\n    var tooltipConfig = ecData.tooltipConfig;\n    var tooltipOpt = tooltipConfig.option || {};\n\n    if (isString(tooltipOpt)) {\n      var content = tooltipOpt;\n      tooltipOpt = {\n        content: content,\n        // Fixed formatter\n        formatter: content\n      };\n    }\n\n    var tooltipModelCascade = [tooltipOpt];\n\n    var cmpt = this._ecModel.getComponent(ecData.componentMainType, ecData.componentIndex);\n\n    if (cmpt) {\n      tooltipModelCascade.push(cmpt);\n    } // In most cases, component tooltip formatter has different params with series tooltip formatter,\n    // so that they can not share the same formatter. Since the global tooltip formatter is used for series\n    // by convension, we do not use it as the default formatter for component.\n\n\n    tooltipModelCascade.push({\n      formatter: tooltipOpt.content\n    });\n    var positionDefault = e.positionDefault;\n    var subTooltipModel = buildTooltipModel(tooltipModelCascade, this._tooltipModel, positionDefault ? {\n      position: positionDefault\n    } : null);\n    var defaultHtml = subTooltipModel.get('content');\n    var asyncTicket = Math.random() + ''; // PENDING: this case do not support richText style yet.\n\n    var markupStyleCreator = new TooltipMarkupStyleCreator(); // Do not check whether `trigger` is 'none' here, because `trigger`\n    // only works on coordinate system. In fact, we have not found case\n    // that requires setting `trigger` nothing on component yet.\n\n    this._showOrMove(subTooltipModel, function () {\n      // Use formatterParams from element defined in component\n      // Avoid users modify it.\n      var formatterParams = clone(subTooltipModel.get('formatterParams') || {});\n\n      this._showTooltipContent(subTooltipModel, defaultHtml, formatterParams, asyncTicket, e.offsetX, e.offsetY, e.position, el, markupStyleCreator);\n    }); // If not dispatch showTip, tip may be hide triggered by axis.\n\n\n    dispatchAction({\n      type: 'showTip',\n      from: this.uid\n    });\n  };\n\n  TooltipView.prototype._showTooltipContent = function ( // Use Model<TooltipOption> insteadof TooltipModel because this model may be from series or other options.\n  // Instead of top level tooltip.\n  tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el, markupStyleCreator) {\n    // Reset ticket\n    this._ticket = '';\n\n    if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) {\n      return;\n    }\n\n    var tooltipContent = this._tooltipContent;\n    tooltipContent.setEnterable(tooltipModel.get('enterable'));\n    var formatter = tooltipModel.get('formatter');\n    positionExpr = positionExpr || tooltipModel.get('position');\n    var html = defaultHtml;\n\n    var nearPoint = this._getNearestPoint([x, y], params, tooltipModel.get('trigger'), tooltipModel.get('borderColor'));\n\n    var nearPointColor = nearPoint.color;\n\n    if (formatter) {\n      if (isString(formatter)) {\n        var useUTC = tooltipModel.ecModel.get('useUTC');\n        var params0 = isArray(params) ? params[0] : params;\n        var isTimeAxis = params0 && params0.axisType && params0.axisType.indexOf('time') >= 0;\n        html = formatter;\n\n        if (isTimeAxis) {\n          html = format(params0.axisValue, html, useUTC);\n        }\n\n        html = formatTpl(html, params, true);\n      } else if (isFunction(formatter)) {\n        var callback = bind(function (cbTicket, html) {\n          if (cbTicket === this._ticket) {\n            tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);\n\n            this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);\n          }\n        }, this);\n        this._ticket = asyncTicket;\n        html = formatter(params, asyncTicket, callback);\n      } else {\n        html = formatter;\n      }\n    }\n\n    tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);\n    tooltipContent.show(tooltipModel, nearPointColor);\n\n    this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);\n  };\n\n  TooltipView.prototype._getNearestPoint = function (point, tooltipDataParams, trigger, borderColor) {\n    if (trigger === 'axis' || isArray(tooltipDataParams)) {\n      return {\n        color: borderColor || (this._renderMode === 'html' ? '#fff' : 'none')\n      };\n    }\n\n    if (!isArray(tooltipDataParams)) {\n      return {\n        color: borderColor || tooltipDataParams.color || tooltipDataParams.borderColor\n      };\n    }\n  };\n\n  TooltipView.prototype._updatePosition = function (tooltipModel, positionExpr, x, // Mouse x\n  y, // Mouse y\n  content, params, el) {\n    var viewWidth = this._api.getWidth();\n\n    var viewHeight = this._api.getHeight();\n\n    positionExpr = positionExpr || tooltipModel.get('position');\n    var contentSize = content.getSize();\n    var align = tooltipModel.get('align');\n    var vAlign = tooltipModel.get('verticalAlign');\n    var rect = el && el.getBoundingRect().clone();\n    el && rect.applyTransform(el.transform);\n\n    if (isFunction(positionExpr)) {\n      // Callback of position can be an array or a string specify the position\n      positionExpr = positionExpr([x, y], params, content.el, rect, {\n        viewSize: [viewWidth, viewHeight],\n        contentSize: contentSize.slice()\n      });\n    }\n\n    if (isArray(positionExpr)) {\n      x = parsePercent$1(positionExpr[0], viewWidth);\n      y = parsePercent$1(positionExpr[1], viewHeight);\n    } else if (isObject(positionExpr)) {\n      var boxLayoutPosition = positionExpr;\n      boxLayoutPosition.width = contentSize[0];\n      boxLayoutPosition.height = contentSize[1];\n      var layoutRect = getLayoutRect(boxLayoutPosition, {\n        width: viewWidth,\n        height: viewHeight\n      });\n      x = layoutRect.x;\n      y = layoutRect.y;\n      align = null; // When positionExpr is left/top/right/bottom,\n      // align and verticalAlign will not work.\n\n      vAlign = null;\n    } // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element\n    else if (isString(positionExpr) && el) {\n        var pos = calcTooltipPosition(positionExpr, rect, contentSize, tooltipModel.get('borderWidth'));\n        x = pos[0];\n        y = pos[1];\n      } else {\n        var pos = refixTooltipPosition(x, y, content, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20);\n        x = pos[0];\n        y = pos[1];\n      }\n\n    align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0);\n    vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);\n\n    if (shouldTooltipConfine(tooltipModel)) {\n      var pos = confineTooltipPosition(x, y, content, viewWidth, viewHeight);\n      x = pos[0];\n      y = pos[1];\n    }\n\n    content.moveTo(x, y);\n  }; // FIXME\n  // Should we remove this but leave this to user?\n\n\n  TooltipView.prototype._updateContentNotChangedOnAxis = function (dataByCoordSys, cbParamsList) {\n    var lastCoordSys = this._lastDataByCoordSys;\n    var lastCbParamsList = this._cbParamsList;\n    var contentNotChanged = !!lastCoordSys && lastCoordSys.length === dataByCoordSys.length;\n    contentNotChanged && each(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {\n      var lastDataByAxis = lastItemCoordSys.dataByAxis || [];\n      var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};\n      var thisDataByAxis = thisItemCoordSys.dataByAxis || [];\n      contentNotChanged = contentNotChanged && lastDataByAxis.length === thisDataByAxis.length;\n      contentNotChanged && each(lastDataByAxis, function (lastItem, indexAxis) {\n        var thisItem = thisDataByAxis[indexAxis] || {};\n        var lastIndices = lastItem.seriesDataIndices || [];\n        var newIndices = thisItem.seriesDataIndices || [];\n        contentNotChanged = contentNotChanged && lastItem.value === thisItem.value && lastItem.axisType === thisItem.axisType && lastItem.axisId === thisItem.axisId && lastIndices.length === newIndices.length;\n        contentNotChanged && each(lastIndices, function (lastIdxItem, j) {\n          var newIdxItem = newIndices[j];\n          contentNotChanged = contentNotChanged && lastIdxItem.seriesIndex === newIdxItem.seriesIndex && lastIdxItem.dataIndex === newIdxItem.dataIndex;\n        }); // check is cbParams data value changed\n\n        lastCbParamsList && each(lastItem.seriesDataIndices, function (idxItem) {\n          var seriesIdx = idxItem.seriesIndex;\n          var cbParams = cbParamsList[seriesIdx];\n          var lastCbParams = lastCbParamsList[seriesIdx];\n\n          if (cbParams && lastCbParams && lastCbParams.data !== cbParams.data) {\n            contentNotChanged = false;\n          }\n        });\n      });\n    });\n    this._lastDataByCoordSys = dataByCoordSys;\n    this._cbParamsList = cbParamsList;\n    return !!contentNotChanged;\n  };\n\n  TooltipView.prototype._hide = function (dispatchAction) {\n    // Do not directly hideLater here, because this behavior may be prevented\n    // in dispatchAction when showTip is dispatched.\n    // FIXME\n    // duplicated hideTip if manuallyHideTip is called from dispatchAction.\n    this._lastDataByCoordSys = null;\n    dispatchAction({\n      type: 'hideTip',\n      from: this.uid\n    });\n  };\n\n  TooltipView.prototype.dispose = function (ecModel, api) {\n    if (env.node || !api.getDom()) {\n      return;\n    }\n\n    clear(this, '_updatePosition');\n\n    this._tooltipContent.dispose();\n\n    unregister('itemTooltip', api);\n  };\n\n  TooltipView.type = 'tooltip';\n  return TooltipView;\n}(ComponentView);\n/**\n * From top to bottom. (the last one should be globalTooltipModel);\n */\n\n\nfunction buildTooltipModel(modelCascade, globalTooltipModel, defaultTooltipOption) {\n  // Last is always tooltip model.\n  var ecModel = globalTooltipModel.ecModel;\n  var resultModel;\n\n  if (defaultTooltipOption) {\n    resultModel = new Model(defaultTooltipOption, ecModel, ecModel);\n    resultModel = new Model(globalTooltipModel.option, resultModel, ecModel);\n  } else {\n    resultModel = globalTooltipModel;\n  }\n\n  for (var i = modelCascade.length - 1; i >= 0; i--) {\n    var tooltipOpt = modelCascade[i];\n\n    if (tooltipOpt) {\n      if (tooltipOpt instanceof Model) {\n        tooltipOpt = tooltipOpt.get('tooltip', true);\n      } // In each data item tooltip can be simply write:\n      // {\n      //  value: 10,\n      //  tooltip: 'Something you need to know'\n      // }\n\n\n      if (isString(tooltipOpt)) {\n        tooltipOpt = {\n          formatter: tooltipOpt\n        };\n      }\n\n      if (tooltipOpt) {\n        resultModel = new Model(tooltipOpt, resultModel, ecModel);\n      }\n    }\n  }\n\n  return resultModel;\n}\n\nfunction makeDispatchAction$1(payload, api) {\n  return payload.dispatchAction || bind(api.dispatchAction, api);\n}\n\nfunction refixTooltipPosition(x, y, content, viewWidth, viewHeight, gapH, gapV) {\n  var size = content.getSize();\n  var width = size[0];\n  var height = size[1];\n\n  if (gapH != null) {\n    // Add extra 2 pixels for this case:\n    // At present the \"values\" in defaut tooltip are using CSS `float: right`.\n    // When the right edge of the tooltip box is on the right side of the\n    // viewport, the `float` layout might push the \"values\" to the second line.\n    if (x + width + gapH + 2 > viewWidth) {\n      x -= width + gapH;\n    } else {\n      x += gapH;\n    }\n  }\n\n  if (gapV != null) {\n    if (y + height + gapV > viewHeight) {\n      y -= height + gapV;\n    } else {\n      y += gapV;\n    }\n  }\n\n  return [x, y];\n}\n\nfunction confineTooltipPosition(x, y, content, viewWidth, viewHeight) {\n  var size = content.getSize();\n  var width = size[0];\n  var height = size[1];\n  x = Math.min(x + width, viewWidth) - width;\n  y = Math.min(y + height, viewHeight) - height;\n  x = Math.max(x, 0);\n  y = Math.max(y, 0);\n  return [x, y];\n}\n\nfunction calcTooltipPosition(position, rect, contentSize, borderWidth) {\n  var domWidth = contentSize[0];\n  var domHeight = contentSize[1];\n  var offset = Math.ceil(Math.SQRT2 * borderWidth) + 8;\n  var x = 0;\n  var y = 0;\n  var rectWidth = rect.width;\n  var rectHeight = rect.height;\n\n  switch (position) {\n    case 'inside':\n      x = rect.x + rectWidth / 2 - domWidth / 2;\n      y = rect.y + rectHeight / 2 - domHeight / 2;\n      break;\n\n    case 'top':\n      x = rect.x + rectWidth / 2 - domWidth / 2;\n      y = rect.y - domHeight - offset;\n      break;\n\n    case 'bottom':\n      x = rect.x + rectWidth / 2 - domWidth / 2;\n      y = rect.y + rectHeight + offset;\n      break;\n\n    case 'left':\n      x = rect.x - domWidth - offset;\n      y = rect.y + rectHeight / 2 - domHeight / 2;\n      break;\n\n    case 'right':\n      x = rect.x + rectWidth + offset;\n      y = rect.y + rectHeight / 2 - domHeight / 2;\n  }\n\n  return [x, y];\n}\n\nfunction isCenterAlign(align) {\n  return align === 'center' || align === 'middle';\n}\n/**\n * Find target component by payload like:\n * ```js\n * { legendId: 'some_id', name: 'xxx' }\n * { toolboxIndex: 1, name: 'xxx' }\n * { geoName: 'some_name', name: 'xxx' }\n * ```\n * PENDING: at present only\n *\n * If not found, return null/undefined.\n */\n\n\nfunction findComponentReference(payload, ecModel, api) {\n  var queryOptionMap = preParseFinder(payload).queryOptionMap;\n  var componentMainType = queryOptionMap.keys()[0];\n\n  if (!componentMainType || componentMainType === 'series') {\n    return;\n  }\n\n  var queryResult = queryReferringComponents(ecModel, componentMainType, queryOptionMap.get(componentMainType), {\n    useDefault: false,\n    enableAll: false,\n    enableNone: false\n  });\n  var model = queryResult.models[0];\n\n  if (!model) {\n    return;\n  }\n\n  var view = api.getViewOfComponentModel(model);\n  var el;\n  view.group.traverse(function (subEl) {\n    var tooltipConfig = getECData(subEl).tooltipConfig;\n\n    if (tooltipConfig && tooltipConfig.name === payload.name) {\n      el = subEl;\n      return true; // stop\n    }\n  });\n\n  if (el) {\n    return {\n      componentMainType: componentMainType,\n      componentIndex: model.componentIndex,\n      el: el\n    };\n  }\n}\n\nfunction install$A(registers) {\n  use(install$s);\n  registers.registerComponentModel(TooltipModel);\n  registers.registerComponentView(TooltipView);\n  /**\n   * @action\n   * @property {string} type\n   * @property {number} seriesIndex\n   * @property {number} dataIndex\n   * @property {number} [x]\n   * @property {number} [y]\n   */\n\n  registers.registerAction({\n    type: 'showTip',\n    event: 'showTip',\n    update: 'tooltip:manuallyShowTip'\n  }, noop);\n  registers.registerAction({\n    type: 'hideTip',\n    event: 'hideTip',\n    update: 'tooltip:manuallyHideTip'\n  }, noop);\n}\n\nvar DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear'];\nfunction brushPreprocessor(option, isNew) {\n  var brushComponents = normalizeToArray(option ? option.brush : []);\n\n  if (!brushComponents.length) {\n    return;\n  }\n\n  var brushComponentSpecifiedBtns = [];\n  each(brushComponents, function (brushOpt) {\n    var tbs = brushOpt.hasOwnProperty('toolbox') ? brushOpt.toolbox : [];\n\n    if (tbs instanceof Array) {\n      brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs);\n    }\n  });\n  var toolbox = option && option.toolbox;\n\n  if (isArray(toolbox)) {\n    toolbox = toolbox[0];\n  }\n\n  if (!toolbox) {\n    toolbox = {\n      feature: {}\n    };\n    option.toolbox = [toolbox];\n  }\n\n  var toolboxFeature = toolbox.feature || (toolbox.feature = {});\n  var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {});\n  var brushTypes = toolboxBrush.type || (toolboxBrush.type = []);\n  brushTypes.push.apply(brushTypes, brushComponentSpecifiedBtns);\n  removeDuplicate(brushTypes);\n\n  if (isNew && !brushTypes.length) {\n    brushTypes.push.apply(brushTypes, DEFAULT_TOOLBOX_BTNS);\n  }\n}\n\nfunction removeDuplicate(arr) {\n  var map = {};\n  each(arr, function (val) {\n    map[val] = 1;\n  });\n  arr.length = 0;\n  each(map, function (flag, val) {\n    arr.push(val);\n  });\n}\n\nvar each$b = each;\n\nfunction hasKeys(obj) {\n  if (obj) {\n    for (var name_1 in obj) {\n      if (obj.hasOwnProperty(name_1)) {\n        return true;\n      }\n    }\n  }\n}\n\nfunction createVisualMappings(option, stateList, supplementVisualOption) {\n  var visualMappings = {};\n  each$b(stateList, function (state) {\n    var mappings = visualMappings[state] = createMappings();\n    each$b(option[state], function (visualData, visualType) {\n      if (!VisualMapping.isValidType(visualType)) {\n        return;\n      }\n\n      var mappingOption = {\n        type: visualType,\n        visual: visualData\n      };\n      supplementVisualOption && supplementVisualOption(mappingOption, state);\n      mappings[visualType] = new VisualMapping(mappingOption); // Prepare a alpha for opacity, for some case that opacity\n      // is not supported, such as rendering using gradient color.\n\n      if (visualType === 'opacity') {\n        mappingOption = clone(mappingOption);\n        mappingOption.type = 'colorAlpha';\n        mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption);\n      }\n    });\n  });\n  return visualMappings;\n\n  function createMappings() {\n    var Creater = function () {}; // Make sure hidden fields will not be visited by\n    // object iteration (with hasOwnProperty checking).\n\n\n    Creater.prototype.__hidden = Creater.prototype;\n    var obj = new Creater();\n    return obj;\n  }\n}\nfunction replaceVisualOption(thisOption, newOption, keys) {\n  // Visual attributes merge is not supported, otherwise it\n  // brings overcomplicated merge logic. See #2853. So if\n  // newOption has anyone of these keys, all of these keys\n  // will be reset. Otherwise, all keys remain.\n  var has;\n  each(keys, function (key) {\n    if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) {\n      has = true;\n    }\n  });\n  has && each(keys, function (key) {\n    if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) {\n      thisOption[key] = clone(newOption[key]);\n    } else {\n      delete thisOption[key];\n    }\n  });\n}\n/**\n * @param stateList\n * @param visualMappings\n * @param list\n * @param getValueState param: valueOrIndex, return: state.\n * @param scope Scope for getValueState\n * @param dimension Concrete dimension, if used.\n */\n// ???! handle brush?\n\nfunction applyVisual(stateList, visualMappings, data, getValueState, scope, dimension) {\n  var visualTypesMap = {};\n  each(stateList, function (state) {\n    var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]);\n    visualTypesMap[state] = visualTypes;\n  });\n  var dataIndex;\n\n  function getVisual(key) {\n    return getItemVisualFromData(data, dataIndex, key);\n  }\n\n  function setVisual(key, value) {\n    setItemVisualFromData(data, dataIndex, key, value);\n  }\n\n  if (dimension == null) {\n    data.each(eachItem);\n  } else {\n    data.each([dimension], eachItem);\n  }\n\n  function eachItem(valueOrIndex, index) {\n    dataIndex = dimension == null ? valueOrIndex // First argument is index\n    : index;\n    var rawDataItem = data.getRawDataItem(dataIndex); // Consider performance\n    // @ts-ignore\n\n    if (rawDataItem && rawDataItem.visualMap === false) {\n      return;\n    }\n\n    var valueState = getValueState.call(scope, valueOrIndex);\n    var mappings = visualMappings[valueState];\n    var visualTypes = visualTypesMap[valueState];\n\n    for (var i = 0, len = visualTypes.length; i < len; i++) {\n      var type = visualTypes[i];\n      mappings[type] && mappings[type].applyVisual(valueOrIndex, getVisual, setVisual);\n    }\n  }\n}\n/**\n * @param data\n * @param stateList\n * @param visualMappings <state, Object.<visualType, module:echarts/visual/VisualMapping>>\n * @param getValueState param: valueOrIndex, return: state.\n * @param dim dimension or dimension index.\n */\n\nfunction incrementalApplyVisual(stateList, visualMappings, getValueState, dim) {\n  var visualTypesMap = {};\n  each(stateList, function (state) {\n    var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]);\n    visualTypesMap[state] = visualTypes;\n  });\n  return {\n    progress: function progress(params, data) {\n      var dimIndex;\n\n      if (dim != null) {\n        dimIndex = data.getDimensionIndex(dim);\n      }\n\n      function getVisual(key) {\n        return getItemVisualFromData(data, dataIndex, key);\n      }\n\n      function setVisual(key, value) {\n        setItemVisualFromData(data, dataIndex, key, value);\n      }\n\n      var dataIndex;\n      var store = data.getStore();\n\n      while ((dataIndex = params.next()) != null) {\n        var rawDataItem = data.getRawDataItem(dataIndex); // Consider performance\n        // @ts-ignore\n\n        if (rawDataItem && rawDataItem.visualMap === false) {\n          continue;\n        }\n\n        var value = dim != null ? store.get(dimIndex, dataIndex) : dataIndex;\n        var valueState = getValueState(value);\n        var mappings = visualMappings[valueState];\n        var visualTypes = visualTypesMap[valueState];\n\n        for (var i = 0, len = visualTypes.length; i < len; i++) {\n          var type = visualTypes[i];\n          mappings[type] && mappings[type].applyVisual(value, getVisual, setVisual);\n        }\n      }\n    }\n  };\n}\n\nfunction makeBrushCommonSelectorForSeries(area) {\n  var brushType = area.brushType; // Do not use function binding or curry for performance.\n\n  var selectors = {\n    point: function (itemLayout) {\n      return selector[brushType].point(itemLayout, selectors, area);\n    },\n    rect: function (itemLayout) {\n      return selector[brushType].rect(itemLayout, selectors, area);\n    }\n  };\n  return selectors;\n}\nvar selector = {\n  lineX: getLineSelectors(0),\n  lineY: getLineSelectors(1),\n  rect: {\n    point: function (itemLayout, selectors, area) {\n      return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]);\n    },\n    rect: function (itemLayout, selectors, area) {\n      return itemLayout && area.boundingRect.intersect(itemLayout);\n    }\n  },\n  polygon: {\n    point: function (itemLayout, selectors, area) {\n      return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]) && contain$2(area.range, itemLayout[0], itemLayout[1]);\n    },\n    rect: function (itemLayout, selectors, area) {\n      var points = area.range;\n\n      if (!itemLayout || points.length <= 1) {\n        return false;\n      }\n\n      var x = itemLayout.x;\n      var y = itemLayout.y;\n      var width = itemLayout.width;\n      var height = itemLayout.height;\n      var p = points[0];\n\n      if (contain$2(points, x, y) || contain$2(points, x + width, y) || contain$2(points, x, y + height) || contain$2(points, x + width, y + height) || BoundingRect.create(itemLayout).contain(p[0], p[1]) || linePolygonIntersect(x, y, x + width, y, points) || linePolygonIntersect(x, y, x, y + height, points) || linePolygonIntersect(x + width, y, x + width, y + height, points) || linePolygonIntersect(x, y + height, x + width, y + height, points)) {\n        return true;\n      }\n    }\n  }\n};\n\nfunction getLineSelectors(xyIndex) {\n  var xy = ['x', 'y'];\n  var wh = ['width', 'height'];\n  return {\n    point: function (itemLayout, selectors, area) {\n      if (itemLayout) {\n        var range = area.range;\n        var p = itemLayout[xyIndex];\n        return inLineRange(p, range);\n      }\n    },\n    rect: function (itemLayout, selectors, area) {\n      if (itemLayout) {\n        var range = area.range;\n        var layoutRange = [itemLayout[xy[xyIndex]], itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]]];\n        layoutRange[1] < layoutRange[0] && layoutRange.reverse();\n        return inLineRange(layoutRange[0], range) || inLineRange(layoutRange[1], range) || inLineRange(range[0], layoutRange) || inLineRange(range[1], layoutRange);\n      }\n    }\n  };\n}\n\nfunction inLineRange(p, range) {\n  return range[0] <= p && p <= range[1];\n}\n\nvar STATE_LIST = ['inBrush', 'outOfBrush'];\nvar DISPATCH_METHOD = '__ecBrushSelect';\nvar DISPATCH_FLAG = '__ecInBrushSelectEvent';\nfunction layoutCovers(ecModel) {\n  ecModel.eachComponent({\n    mainType: 'brush'\n  }, function (brushModel) {\n    var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel);\n    brushTargetManager.setInputRanges(brushModel.areas, ecModel);\n  });\n}\n/**\n * Register the visual encoding if this modules required.\n */\n\nfunction brushVisual(ecModel, api, payload) {\n  var brushSelected = [];\n  var throttleType;\n  var throttleDelay;\n  ecModel.eachComponent({\n    mainType: 'brush'\n  }, function (brushModel) {\n    payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption(payload.key === 'brush' ? payload.brushOption : {\n      brushType: false\n    });\n  });\n  layoutCovers(ecModel);\n  ecModel.eachComponent({\n    mainType: 'brush'\n  }, function (brushModel, brushIndex) {\n    var thisBrushSelected = {\n      brushId: brushModel.id,\n      brushIndex: brushIndex,\n      brushName: brushModel.name,\n      areas: clone(brushModel.areas),\n      selected: []\n    }; // Every brush component exists in event params, convenient\n    // for user to find by index.\n\n    brushSelected.push(thisBrushSelected);\n    var brushOption = brushModel.option;\n    var brushLink = brushOption.brushLink;\n    var linkedSeriesMap = [];\n    var selectedDataIndexForLink = [];\n    var rangeInfoBySeries = [];\n    var hasBrushExists = false;\n\n    if (!brushIndex) {\n      // Only the first throttle setting works.\n      throttleType = brushOption.throttleType;\n      throttleDelay = brushOption.throttleDelay;\n    } // Add boundingRect and selectors to range.\n\n\n    var areas = map(brushModel.areas, function (area) {\n      var builder = boundingRectBuilders[area.brushType];\n      var selectableArea = defaults({\n        boundingRect: builder ? builder(area) : void 0\n      }, area);\n      selectableArea.selectors = makeBrushCommonSelectorForSeries(selectableArea);\n      return selectableArea;\n    });\n    var visualMappings = createVisualMappings(brushModel.option, STATE_LIST, function (mappingOption) {\n      mappingOption.mappingMethod = 'fixed';\n    });\n    isArray(brushLink) && each(brushLink, function (seriesIndex) {\n      linkedSeriesMap[seriesIndex] = 1;\n    });\n\n    function linkOthers(seriesIndex) {\n      return brushLink === 'all' || !!linkedSeriesMap[seriesIndex];\n    } // If no supported brush or no brush on the series,\n    // all visuals should be in original state.\n\n\n    function brushed(rangeInfoList) {\n      return !!rangeInfoList.length;\n    }\n    /**\n     * Logic for each series: (If the logic has to be modified one day, do it carefully!)\n     *\n     * ( brushed ┬ && ┬hasBrushExist ┬ && linkOthers  ) => StepA: ┬record, ┬ StepB: ┬visualByRecord.\n     *   !brushed┘    ├hasBrushExist ┤                            └nothing,┘        ├visualByRecord.\n     *                └!hasBrushExist┘                                              └nothing.\n     * ( !brushed  && ┬hasBrushExist ┬ && linkOthers  ) => StepA:  nothing,  StepB: ┬visualByRecord.\n     *                └!hasBrushExist┘                                              └nothing.\n     * ( brushed ┬ &&                     !linkOthers ) => StepA:  nothing,  StepB: ┬visualByCheck.\n     *   !brushed┘                                                                  └nothing.\n     * ( !brushed  &&                     !linkOthers ) => StepA:  nothing,  StepB:  nothing.\n     */\n    // Step A\n\n\n    ecModel.eachSeries(function (seriesModel, seriesIndex) {\n      var rangeInfoList = rangeInfoBySeries[seriesIndex] = [];\n      seriesModel.subType === 'parallel' ? stepAParallel(seriesModel, seriesIndex) : stepAOthers(seriesModel, seriesIndex, rangeInfoList);\n    });\n\n    function stepAParallel(seriesModel, seriesIndex) {\n      var coordSys = seriesModel.coordinateSystem;\n      hasBrushExists = hasBrushExists || coordSys.hasAxisBrushed();\n      linkOthers(seriesIndex) && coordSys.eachActiveState(seriesModel.getData(), function (activeState, dataIndex) {\n        activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1);\n      });\n    }\n\n    function stepAOthers(seriesModel, seriesIndex, rangeInfoList) {\n      if (!seriesModel.brushSelector || brushModelNotControll(brushModel, seriesIndex)) {\n        return;\n      }\n\n      each(areas, function (area) {\n        if (brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel)) {\n          rangeInfoList.push(area);\n        }\n\n        hasBrushExists = hasBrushExists || brushed(rangeInfoList);\n      });\n\n      if (linkOthers(seriesIndex) && brushed(rangeInfoList)) {\n        var data_1 = seriesModel.getData();\n        data_1.each(function (dataIndex) {\n          if (checkInRange(seriesModel, rangeInfoList, data_1, dataIndex)) {\n            selectedDataIndexForLink[dataIndex] = 1;\n          }\n        });\n      }\n    } // Step B\n\n\n    ecModel.eachSeries(function (seriesModel, seriesIndex) {\n      var seriesBrushSelected = {\n        seriesId: seriesModel.id,\n        seriesIndex: seriesIndex,\n        seriesName: seriesModel.name,\n        dataIndex: []\n      }; // Every series exists in event params, convenient\n      // for user to find series by seriesIndex.\n\n      thisBrushSelected.selected.push(seriesBrushSelected);\n      var rangeInfoList = rangeInfoBySeries[seriesIndex];\n      var data = seriesModel.getData();\n      var getValueState = linkOthers(seriesIndex) ? function (dataIndex) {\n        return selectedDataIndexForLink[dataIndex] ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush';\n      } : function (dataIndex) {\n        return checkInRange(seriesModel, rangeInfoList, data, dataIndex) ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush';\n      }; // If no supported brush or no brush, all visuals are in original state.\n\n      (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList)) && applyVisual(STATE_LIST, visualMappings, data, getValueState);\n    });\n  });\n  dispatchAction(api, throttleType, throttleDelay, brushSelected, payload);\n}\n\nfunction dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) {\n  // This event will not be triggered when `setOpion`, otherwise dead lock may\n  // triggered when do `setOption` in event listener, which we do not find\n  // satisfactory way to solve yet. Some considered resolutions:\n  // (a) Diff with prevoius selected data ant only trigger event when changed.\n  // But store previous data and diff precisely (i.e., not only by dataIndex, but\n  // also detect value changes in selected data) might bring complexity or fragility.\n  // (b) Use spectial param like `silent` to suppress event triggering.\n  // But such kind of volatile param may be weird in `setOption`.\n  if (!payload) {\n    return;\n  }\n\n  var zr = api.getZr();\n\n  if (zr[DISPATCH_FLAG]) {\n    return;\n  }\n\n  if (!zr[DISPATCH_METHOD]) {\n    zr[DISPATCH_METHOD] = doDispatch;\n  }\n\n  var fn = createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType);\n  fn(api, brushSelected);\n}\n\nfunction doDispatch(api, brushSelected) {\n  if (!api.isDisposed()) {\n    var zr = api.getZr();\n    zr[DISPATCH_FLAG] = true;\n    api.dispatchAction({\n      type: 'brushSelect',\n      batch: brushSelected\n    });\n    zr[DISPATCH_FLAG] = false;\n  }\n}\n\nfunction checkInRange(seriesModel, rangeInfoList, data, dataIndex) {\n  for (var i = 0, len = rangeInfoList.length; i < len; i++) {\n    var area = rangeInfoList[i];\n\n    if (seriesModel.brushSelector(dataIndex, data, area.selectors, area)) {\n      return true;\n    }\n  }\n}\n\nfunction brushModelNotControll(brushModel, seriesIndex) {\n  var seriesIndices = brushModel.option.seriesIndex;\n  return seriesIndices != null && seriesIndices !== 'all' && (isArray(seriesIndices) ? indexOf(seriesIndices, seriesIndex) < 0 : seriesIndex !== seriesIndices);\n}\n\nvar boundingRectBuilders = {\n  rect: function (area) {\n    return getBoundingRectFromMinMax(area.range);\n  },\n  polygon: function (area) {\n    var minMax;\n    var range = area.range;\n\n    for (var i = 0, len = range.length; i < len; i++) {\n      minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]];\n      var rg = range[i];\n      rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]);\n      rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]);\n      rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]);\n      rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]);\n    }\n\n    return minMax && getBoundingRectFromMinMax(minMax);\n  }\n};\n\nfunction getBoundingRectFromMinMax(minMax) {\n  return new BoundingRect(minMax[0][0], minMax[1][0], minMax[0][1] - minMax[0][0], minMax[1][1] - minMax[1][0]);\n}\n\nvar BrushView =\n/** @class */\nfunction (_super) {\n  __extends(BrushView, _super);\n\n  function BrushView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = BrushView.type;\n    return _this;\n  }\n\n  BrushView.prototype.init = function (ecModel, api) {\n    this.ecModel = ecModel;\n    this.api = api;\n    this.model;\n    (this._brushController = new BrushController(api.getZr())).on('brush', bind(this._onBrush, this)).mount();\n  };\n\n  BrushView.prototype.render = function (brushModel, ecModel, api, payload) {\n    this.model = brushModel;\n\n    this._updateController(brushModel, ecModel, api, payload);\n  };\n\n  BrushView.prototype.updateTransform = function (brushModel, ecModel, api, payload) {\n    // PENDING: `updateTransform` is a little tricky, whose layout need\n    // to be calculate mandatorily and other stages will not be performed.\n    // Take care the correctness of the logic. See #11754 .\n    layoutCovers(ecModel);\n\n    this._updateController(brushModel, ecModel, api, payload);\n  };\n\n  BrushView.prototype.updateVisual = function (brushModel, ecModel, api, payload) {\n    this.updateTransform(brushModel, ecModel, api, payload);\n  };\n\n  BrushView.prototype.updateView = function (brushModel, ecModel, api, payload) {\n    this._updateController(brushModel, ecModel, api, payload);\n  };\n\n  BrushView.prototype._updateController = function (brushModel, ecModel, api, payload) {\n    // Do not update controller when drawing.\n    (!payload || payload.$from !== brushModel.id) && this._brushController.setPanels(brushModel.brushTargetManager.makePanelOpts(api)).enableBrush(brushModel.brushOption).updateCovers(brushModel.areas.slice());\n  }; // updateLayout: updateController,\n  // updateVisual: updateController,\n\n\n  BrushView.prototype.dispose = function () {\n    this._brushController.dispose();\n  };\n\n  BrushView.prototype._onBrush = function (eventParam) {\n    var modelId = this.model.id;\n    var areas = this.model.brushTargetManager.setOutputRanges(eventParam.areas, this.ecModel); // Action is not dispatched on drag end, because the drag end\n    // emits the same params with the last drag move event, and\n    // may have some delay when using touch pad, which makes\n    // animation not smooth (when using debounce).\n\n    (!eventParam.isEnd || eventParam.removeOnClick) && this.api.dispatchAction({\n      type: 'brush',\n      brushId: modelId,\n      areas: clone(areas),\n      $from: modelId\n    });\n    eventParam.isEnd && this.api.dispatchAction({\n      type: 'brushEnd',\n      brushId: modelId,\n      areas: clone(areas),\n      $from: modelId\n    });\n  };\n\n  BrushView.type = 'brush';\n  return BrushView;\n}(ComponentView);\n\nvar DEFAULT_OUT_OF_BRUSH_COLOR = '#ddd';\n\nvar BrushModel =\n/** @class */\nfunction (_super) {\n  __extends(BrushModel, _super);\n\n  function BrushModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = BrushModel.type;\n    /**\n     * @readOnly\n     */\n\n    _this.areas = [];\n    /**\n     * Current brush painting area settings.\n     * @readOnly\n     */\n\n    _this.brushOption = {};\n    return _this;\n  }\n\n  BrushModel.prototype.optionUpdated = function (newOption, isInit) {\n    var thisOption = this.option;\n    !isInit && replaceVisualOption(thisOption, newOption, ['inBrush', 'outOfBrush']);\n    var inBrush = thisOption.inBrush = thisOption.inBrush || {}; // Always give default visual, consider setOption at the second time.\n\n    thisOption.outOfBrush = thisOption.outOfBrush || {\n      color: DEFAULT_OUT_OF_BRUSH_COLOR\n    };\n\n    if (!inBrush.hasOwnProperty('liftZ')) {\n      // Bigger than the highlight z lift, otherwise it will\n      // be effected by the highlight z when brush.\n      inBrush.liftZ = 5;\n    }\n  };\n  /**\n   * If `areas` is null/undefined, range state remain.\n   */\n\n\n  BrushModel.prototype.setAreas = function (areas) {\n    if (\"development\" !== 'production') {\n      assert(isArray(areas));\n      each(areas, function (area) {\n        assert(area.brushType, 'Illegal areas');\n      });\n    } // If areas is null/undefined, range state remain.\n    // This helps user to dispatchAction({type: 'brush'}) with no areas\n    // set but just want to get the current brush select info from a `brush` event.\n\n\n    if (!areas) {\n      return;\n    }\n\n    this.areas = map(areas, function (area) {\n      return generateBrushOption(this.option, area);\n    }, this);\n  };\n  /**\n   * Set the current painting brush option.\n   */\n\n\n  BrushModel.prototype.setBrushOption = function (brushOption) {\n    this.brushOption = generateBrushOption(this.option, brushOption);\n    this.brushType = this.brushOption.brushType;\n  };\n\n  BrushModel.type = 'brush';\n  BrushModel.dependencies = ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'];\n  BrushModel.defaultOption = {\n    seriesIndex: 'all',\n    brushType: 'rect',\n    brushMode: 'single',\n    transformable: true,\n    brushStyle: {\n      borderWidth: 1,\n      color: 'rgba(210,219,238,0.3)',\n      borderColor: '#D2DBEE'\n    },\n    throttleType: 'fixRate',\n    throttleDelay: 0,\n    removeOnClick: true,\n    z: 10000\n  };\n  return BrushModel;\n}(ComponentModel);\n\nfunction generateBrushOption(option, brushOption) {\n  return merge({\n    brushType: option.brushType,\n    brushMode: option.brushMode,\n    transformable: option.transformable,\n    brushStyle: new Model(option.brushStyle).getItemStyle(),\n    removeOnClick: option.removeOnClick,\n    z: option.z\n  }, brushOption, true);\n}\n\nvar ICON_TYPES = ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'];\n\nvar BrushFeature =\n/** @class */\nfunction (_super) {\n  __extends(BrushFeature, _super);\n\n  function BrushFeature() {\n    return _super !== null && _super.apply(this, arguments) || this;\n  }\n\n  BrushFeature.prototype.render = function (featureModel, ecModel, api) {\n    var brushType;\n    var brushMode;\n    var isBrushed;\n    ecModel.eachComponent({\n      mainType: 'brush'\n    }, function (brushModel) {\n      brushType = brushModel.brushType;\n      brushMode = brushModel.brushOption.brushMode || 'single';\n      isBrushed = isBrushed || !!brushModel.areas.length;\n    });\n    this._brushType = brushType;\n    this._brushMode = brushMode;\n    each(featureModel.get('type', true), function (type) {\n      featureModel.setIconStatus(type, (type === 'keep' ? brushMode === 'multiple' : type === 'clear' ? isBrushed : type === brushType) ? 'emphasis' : 'normal');\n    });\n  };\n\n  BrushFeature.prototype.updateView = function (featureModel, ecModel, api) {\n    this.render(featureModel, ecModel, api);\n  };\n\n  BrushFeature.prototype.getIcons = function () {\n    var model = this.model;\n    var availableIcons = model.get('icon', true);\n    var icons = {};\n    each(model.get('type', true), function (type) {\n      if (availableIcons[type]) {\n        icons[type] = availableIcons[type];\n      }\n    });\n    return icons;\n  };\n\n  BrushFeature.prototype.onclick = function (ecModel, api, type) {\n    var brushType = this._brushType;\n    var brushMode = this._brushMode;\n\n    if (type === 'clear') {\n      // Trigger parallel action firstly\n      api.dispatchAction({\n        type: 'axisAreaSelect',\n        intervals: []\n      });\n      api.dispatchAction({\n        type: 'brush',\n        command: 'clear',\n        // Clear all areas of all brush components.\n        areas: []\n      });\n    } else {\n      api.dispatchAction({\n        type: 'takeGlobalCursor',\n        key: 'brush',\n        brushOption: {\n          brushType: type === 'keep' ? brushType : brushType === type ? false : type,\n          brushMode: type === 'keep' ? brushMode === 'multiple' ? 'single' : 'multiple' : brushMode\n        }\n      });\n    }\n  };\n\n  BrushFeature.getDefaultOption = function (ecModel) {\n    var defaultOption = {\n      show: true,\n      type: ICON_TYPES.slice(),\n      icon: {\n        /* eslint-disable */\n        rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13',\n        polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2',\n        lineX: 'M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4',\n        lineY: 'M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4',\n        keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z',\n        clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2' // jshint ignore:line\n\n        /* eslint-enable */\n\n      },\n      // `rect`, `polygon`, `lineX`, `lineY`, `keep`, `clear`\n      title: ecModel.getLocaleModel().get(['toolbox', 'brush', 'title'])\n    };\n    return defaultOption;\n  };\n\n  return BrushFeature;\n}(ToolboxFeature);\n\nfunction install$B(registers) {\n  registers.registerComponentView(BrushView);\n  registers.registerComponentModel(BrushModel);\n  registers.registerPreprocessor(brushPreprocessor);\n  registers.registerVisual(registers.PRIORITY.VISUAL.BRUSH, brushVisual);\n  registers.registerAction({\n    type: 'brush',\n    event: 'brush',\n    update: 'updateVisual'\n  }, function (payload, ecModel) {\n    ecModel.eachComponent({\n      mainType: 'brush',\n      query: payload\n    }, function (brushModel) {\n      brushModel.setAreas(payload.areas);\n    });\n  });\n  /**\n   * payload: {\n   *      brushComponents: [\n   *          {\n   *              brushId,\n   *              brushIndex,\n   *              brushName,\n   *              series: [\n   *                  {\n   *                      seriesId,\n   *                      seriesIndex,\n   *                      seriesName,\n   *                      rawIndices: [21, 34, ...]\n   *                  },\n   *                  ...\n   *              ]\n   *          },\n   *          ...\n   *      ]\n   * }\n   */\n\n  registers.registerAction({\n    type: 'brushSelect',\n    event: 'brushSelected',\n    update: 'none'\n  }, noop);\n  registers.registerAction({\n    type: 'brushEnd',\n    event: 'brushEnd',\n    update: 'none'\n  }, noop);\n  registerFeature('brush', BrushFeature);\n}\n\nvar TitleModel =\n/** @class */\nfunction (_super) {\n  __extends(TitleModel, _super);\n\n  function TitleModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TitleModel.type;\n    _this.layoutMode = {\n      type: 'box',\n      ignoreSize: true\n    };\n    return _this;\n  }\n\n  TitleModel.type = 'title';\n  TitleModel.defaultOption = {\n    // zlevel: 0,\n    z: 6,\n    show: true,\n    text: '',\n    target: 'blank',\n    subtext: '',\n    subtarget: 'blank',\n    left: 0,\n    top: 0,\n    backgroundColor: 'rgba(0,0,0,0)',\n    borderColor: '#ccc',\n    borderWidth: 0,\n    padding: 5,\n    itemGap: 10,\n    textStyle: {\n      fontSize: 18,\n      fontWeight: 'bold',\n      color: '#464646'\n    },\n    subtextStyle: {\n      fontSize: 12,\n      color: '#6E7079'\n    }\n  };\n  return TitleModel;\n}(ComponentModel); // View\n\n\nvar TitleView =\n/** @class */\nfunction (_super) {\n  __extends(TitleView, _super);\n\n  function TitleView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TitleView.type;\n    return _this;\n  }\n\n  TitleView.prototype.render = function (titleModel, ecModel, api) {\n    this.group.removeAll();\n\n    if (!titleModel.get('show')) {\n      return;\n    }\n\n    var group = this.group;\n    var textStyleModel = titleModel.getModel('textStyle');\n    var subtextStyleModel = titleModel.getModel('subtextStyle');\n    var textAlign = titleModel.get('textAlign');\n    var textVerticalAlign = retrieve2(titleModel.get('textBaseline'), titleModel.get('textVerticalAlign'));\n    var textEl = new ZRText({\n      style: createTextStyle(textStyleModel, {\n        text: titleModel.get('text'),\n        fill: textStyleModel.getTextColor()\n      }, {\n        disableBox: true\n      }),\n      z2: 10\n    });\n    var textRect = textEl.getBoundingRect();\n    var subText = titleModel.get('subtext');\n    var subTextEl = new ZRText({\n      style: createTextStyle(subtextStyleModel, {\n        text: subText,\n        fill: subtextStyleModel.getTextColor(),\n        y: textRect.height + titleModel.get('itemGap'),\n        verticalAlign: 'top'\n      }, {\n        disableBox: true\n      }),\n      z2: 10\n    });\n    var link = titleModel.get('link');\n    var sublink = titleModel.get('sublink');\n    var triggerEvent = titleModel.get('triggerEvent', true);\n    textEl.silent = !link && !triggerEvent;\n    subTextEl.silent = !sublink && !triggerEvent;\n\n    if (link) {\n      textEl.on('click', function () {\n        windowOpen(link, '_' + titleModel.get('target'));\n      });\n    }\n\n    if (sublink) {\n      subTextEl.on('click', function () {\n        windowOpen(sublink, '_' + titleModel.get('subtarget'));\n      });\n    }\n\n    getECData(textEl).eventData = getECData(subTextEl).eventData = triggerEvent ? {\n      componentType: 'title',\n      componentIndex: titleModel.componentIndex\n    } : null;\n    group.add(textEl);\n    subText && group.add(subTextEl); // If no subText, but add subTextEl, there will be an empty line.\n\n    var groupRect = group.getBoundingRect();\n    var layoutOption = titleModel.getBoxLayoutParams();\n    layoutOption.width = groupRect.width;\n    layoutOption.height = groupRect.height;\n    var layoutRect = getLayoutRect(layoutOption, {\n      width: api.getWidth(),\n      height: api.getHeight()\n    }, titleModel.get('padding')); // Adjust text align based on position\n\n    if (!textAlign) {\n      // Align left if title is on the left. center and right is same\n      textAlign = titleModel.get('left') || titleModel.get('right'); // @ts-ignore\n\n      if (textAlign === 'middle') {\n        textAlign = 'center';\n      } // Adjust layout by text align\n\n\n      if (textAlign === 'right') {\n        layoutRect.x += layoutRect.width;\n      } else if (textAlign === 'center') {\n        layoutRect.x += layoutRect.width / 2;\n      }\n    }\n\n    if (!textVerticalAlign) {\n      textVerticalAlign = titleModel.get('top') || titleModel.get('bottom'); // @ts-ignore\n\n      if (textVerticalAlign === 'center') {\n        textVerticalAlign = 'middle';\n      }\n\n      if (textVerticalAlign === 'bottom') {\n        layoutRect.y += layoutRect.height;\n      } else if (textVerticalAlign === 'middle') {\n        layoutRect.y += layoutRect.height / 2;\n      }\n\n      textVerticalAlign = textVerticalAlign || 'top';\n    }\n\n    group.x = layoutRect.x;\n    group.y = layoutRect.y;\n    group.markRedraw();\n    var alignStyle = {\n      align: textAlign,\n      verticalAlign: textVerticalAlign\n    };\n    textEl.setStyle(alignStyle);\n    subTextEl.setStyle(alignStyle); // Render background\n    // Get groupRect again because textAlign has been changed\n\n    groupRect = group.getBoundingRect();\n    var padding = layoutRect.margin;\n    var style = titleModel.getItemStyle(['color', 'opacity']);\n    style.fill = titleModel.get('backgroundColor');\n    var rect = new Rect({\n      shape: {\n        x: groupRect.x - padding[3],\n        y: groupRect.y - padding[0],\n        width: groupRect.width + padding[1] + padding[3],\n        height: groupRect.height + padding[0] + padding[2],\n        r: titleModel.get('borderRadius')\n      },\n      style: style,\n      subPixelOptimize: true,\n      silent: true\n    });\n    group.add(rect);\n  };\n\n  TitleView.type = 'title';\n  return TitleView;\n}(ComponentView);\n\nfunction install$C(registers) {\n  registers.registerComponentModel(TitleModel);\n  registers.registerComponentView(TitleView);\n}\n\nvar TimelineModel =\n/** @class */\nfunction (_super) {\n  __extends(TimelineModel, _super);\n\n  function TimelineModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TimelineModel.type;\n    _this.layoutMode = 'box';\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  TimelineModel.prototype.init = function (option, parentModel, ecModel) {\n    this.mergeDefaultAndTheme(option, ecModel);\n\n    this._initData();\n  };\n  /**\n   * @override\n   */\n\n\n  TimelineModel.prototype.mergeOption = function (option) {\n    _super.prototype.mergeOption.apply(this, arguments);\n\n    this._initData();\n  };\n\n  TimelineModel.prototype.setCurrentIndex = function (currentIndex) {\n    if (currentIndex == null) {\n      currentIndex = this.option.currentIndex;\n    }\n\n    var count = this._data.count();\n\n    if (this.option.loop) {\n      currentIndex = (currentIndex % count + count) % count;\n    } else {\n      currentIndex >= count && (currentIndex = count - 1);\n      currentIndex < 0 && (currentIndex = 0);\n    }\n\n    this.option.currentIndex = currentIndex;\n  };\n  /**\n   * @return {number} currentIndex\n   */\n\n\n  TimelineModel.prototype.getCurrentIndex = function () {\n    return this.option.currentIndex;\n  };\n  /**\n   * @return {boolean}\n   */\n\n\n  TimelineModel.prototype.isIndexMax = function () {\n    return this.getCurrentIndex() >= this._data.count() - 1;\n  };\n  /**\n   * @param {boolean} state true: play, false: stop\n   */\n\n\n  TimelineModel.prototype.setPlayState = function (state) {\n    this.option.autoPlay = !!state;\n  };\n  /**\n   * @return {boolean} true: play, false: stop\n   */\n\n\n  TimelineModel.prototype.getPlayState = function () {\n    return !!this.option.autoPlay;\n  };\n  /**\n   * @private\n   */\n\n\n  TimelineModel.prototype._initData = function () {\n    var thisOption = this.option;\n    var dataArr = thisOption.data || [];\n    var axisType = thisOption.axisType;\n    var names = this._names = [];\n    var processedDataArr;\n\n    if (axisType === 'category') {\n      processedDataArr = [];\n      each(dataArr, function (item, index) {\n        var value = convertOptionIdName(getDataItemValue(item), '');\n        var newItem;\n\n        if (isObject(item)) {\n          newItem = clone(item);\n          newItem.value = index;\n        } else {\n          newItem = index;\n        }\n\n        processedDataArr.push(newItem);\n        names.push(value);\n      });\n    } else {\n      processedDataArr = dataArr;\n    }\n\n    var dimType = {\n      category: 'ordinal',\n      time: 'time',\n      value: 'number'\n    }[axisType] || 'number';\n    var data = this._data = new SeriesData([{\n      name: 'value',\n      type: dimType\n    }], this);\n    data.initData(processedDataArr, names);\n  };\n\n  TimelineModel.prototype.getData = function () {\n    return this._data;\n  };\n  /**\n   * @public\n   * @return {Array.<string>} categoreis\n   */\n\n\n  TimelineModel.prototype.getCategories = function () {\n    if (this.get('axisType') === 'category') {\n      return this._names.slice();\n    }\n  };\n\n  TimelineModel.type = 'timeline';\n  /**\n   * @protected\n   */\n\n  TimelineModel.defaultOption = {\n    // zlevel: 0,                  // 一级层叠\n    z: 4,\n    show: true,\n    axisType: 'time',\n    realtime: true,\n    left: '20%',\n    top: null,\n    right: '20%',\n    bottom: 0,\n    width: null,\n    height: 40,\n    padding: 5,\n    controlPosition: 'left',\n    autoPlay: false,\n    rewind: false,\n    loop: true,\n    playInterval: 2000,\n    currentIndex: 0,\n    itemStyle: {},\n    label: {\n      color: '#000'\n    },\n    data: []\n  };\n  return TimelineModel;\n}(ComponentModel);\n\nvar SliderTimelineModel =\n/** @class */\nfunction (_super) {\n  __extends(SliderTimelineModel, _super);\n\n  function SliderTimelineModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SliderTimelineModel.type;\n    return _this;\n  }\n\n  SliderTimelineModel.type = 'timeline.slider';\n  /**\n   * @protected\n   */\n\n  SliderTimelineModel.defaultOption = inheritDefaultOption(TimelineModel.defaultOption, {\n    backgroundColor: 'rgba(0,0,0,0)',\n    borderColor: '#ccc',\n    borderWidth: 0,\n    orient: 'horizontal',\n    inverse: false,\n    tooltip: {\n      trigger: 'item' // data item may also have tootip attr.\n\n    },\n    symbol: 'circle',\n    symbolSize: 12,\n    lineStyle: {\n      show: true,\n      width: 2,\n      color: '#DAE1F5'\n    },\n    label: {\n      position: 'auto',\n      // When using number, label position is not\n      // restricted by viewRect.\n      // positive: right/bottom, negative: left/top\n      show: true,\n      interval: 'auto',\n      rotate: 0,\n      // formatter: null,\n      // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n      color: '#A4B1D7'\n    },\n    itemStyle: {\n      color: '#A4B1D7',\n      borderWidth: 1\n    },\n    checkpointStyle: {\n      symbol: 'circle',\n      symbolSize: 15,\n      color: '#316bf3',\n      borderColor: '#fff',\n      borderWidth: 2,\n      shadowBlur: 2,\n      shadowOffsetX: 1,\n      shadowOffsetY: 1,\n      shadowColor: 'rgba(0, 0, 0, 0.3)',\n      // borderColor: 'rgba(194,53,49, 0.5)',\n      animation: true,\n      animationDuration: 300,\n      animationEasing: 'quinticInOut'\n    },\n    controlStyle: {\n      show: true,\n      showPlayBtn: true,\n      showPrevBtn: true,\n      showNextBtn: true,\n      itemSize: 24,\n      itemGap: 12,\n      position: 'left',\n      playIcon: 'path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z',\n      stopIcon: 'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z',\n      // eslint-disable-next-line max-len\n      nextIcon: 'M2,18.5A1.52,1.52,0,0,1,.92,18a1.49,1.49,0,0,1,0-2.12L7.81,9.36,1,3.11A1.5,1.5,0,1,1,3,.89l8,7.34a1.48,1.48,0,0,1,.49,1.09,1.51,1.51,0,0,1-.46,1.1L3,18.08A1.5,1.5,0,0,1,2,18.5Z',\n      // eslint-disable-next-line max-len\n      prevIcon: 'M10,.5A1.52,1.52,0,0,1,11.08,1a1.49,1.49,0,0,1,0,2.12L4.19,9.64,11,15.89a1.5,1.5,0,1,1-2,2.22L1,10.77A1.48,1.48,0,0,1,.5,9.68,1.51,1.51,0,0,1,1,8.58L9,.92A1.5,1.5,0,0,1,10,.5Z',\n      prevBtnSize: 18,\n      nextBtnSize: 18,\n      color: '#A4B1D7',\n      borderColor: '#A4B1D7',\n      borderWidth: 1\n    },\n    emphasis: {\n      label: {\n        show: true,\n        // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n        color: '#6f778d'\n      },\n      itemStyle: {\n        color: '#316BF3'\n      },\n      controlStyle: {\n        color: '#316BF3',\n        borderColor: '#316BF3',\n        borderWidth: 2\n      }\n    },\n    progress: {\n      lineStyle: {\n        color: '#316BF3'\n      },\n      itemStyle: {\n        color: '#316BF3'\n      },\n      label: {\n        color: '#6f778d'\n      }\n    },\n    data: []\n  });\n  return SliderTimelineModel;\n}(TimelineModel);\n\nmixin(SliderTimelineModel, DataFormatMixin.prototype);\n\nvar TimelineView =\n/** @class */\nfunction (_super) {\n  __extends(TimelineView, _super);\n\n  function TimelineView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = TimelineView.type;\n    return _this;\n  }\n\n  TimelineView.type = 'timeline';\n  return TimelineView;\n}(ComponentView);\n\n/**\n * Extend axis 2d\n */\n\nvar TimelineAxis =\n/** @class */\nfunction (_super) {\n  __extends(TimelineAxis, _super);\n\n  function TimelineAxis(dim, scale, coordExtent, axisType) {\n    var _this = _super.call(this, dim, scale, coordExtent) || this;\n\n    _this.type = axisType || 'value';\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  TimelineAxis.prototype.getLabelModel = function () {\n    // Force override\n    return this.model.getModel('label');\n  };\n  /**\n   * @override\n   */\n\n\n  TimelineAxis.prototype.isHorizontal = function () {\n    return this.model.get('orient') === 'horizontal';\n  };\n\n  return TimelineAxis;\n}(Axis);\n\nvar PI$8 = Math.PI;\nvar labelDataIndexStore = makeInner();\n\nvar SliderTimelineView =\n/** @class */\nfunction (_super) {\n  __extends(SliderTimelineView, _super);\n\n  function SliderTimelineView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SliderTimelineView.type;\n    return _this;\n  }\n\n  SliderTimelineView.prototype.init = function (ecModel, api) {\n    this.api = api;\n  };\n  /**\n   * @override\n   */\n\n\n  SliderTimelineView.prototype.render = function (timelineModel, ecModel, api) {\n    this.model = timelineModel;\n    this.api = api;\n    this.ecModel = ecModel;\n    this.group.removeAll();\n\n    if (timelineModel.get('show', true)) {\n      var layoutInfo_1 = this._layout(timelineModel, api);\n\n      var mainGroup_1 = this._createGroup('_mainGroup');\n\n      var labelGroup = this._createGroup('_labelGroup');\n\n      var axis_1 = this._axis = this._createAxis(layoutInfo_1, timelineModel);\n\n      timelineModel.formatTooltip = function (dataIndex) {\n        var name = axis_1.scale.getLabel({\n          value: dataIndex\n        });\n        return createTooltipMarkup('nameValue', {\n          noName: true,\n          value: name\n        });\n      };\n\n      each(['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], function (name) {\n        this['_render' + name](layoutInfo_1, mainGroup_1, axis_1, timelineModel);\n      }, this);\n\n      this._renderAxisLabel(layoutInfo_1, labelGroup, axis_1, timelineModel);\n\n      this._position(layoutInfo_1, timelineModel);\n    }\n\n    this._doPlayStop();\n\n    this._updateTicksStatus();\n  };\n  /**\n   * @override\n   */\n\n\n  SliderTimelineView.prototype.remove = function () {\n    this._clearTimer();\n\n    this.group.removeAll();\n  };\n  /**\n   * @override\n   */\n\n\n  SliderTimelineView.prototype.dispose = function () {\n    this._clearTimer();\n  };\n\n  SliderTimelineView.prototype._layout = function (timelineModel, api) {\n    var labelPosOpt = timelineModel.get(['label', 'position']);\n    var orient = timelineModel.get('orient');\n    var viewRect = getViewRect$5(timelineModel, api);\n    var parsedLabelPos; // Auto label offset.\n\n    if (labelPosOpt == null || labelPosOpt === 'auto') {\n      parsedLabelPos = orient === 'horizontal' ? viewRect.y + viewRect.height / 2 < api.getHeight() / 2 ? '-' : '+' : viewRect.x + viewRect.width / 2 < api.getWidth() / 2 ? '+' : '-';\n    } else if (isString(labelPosOpt)) {\n      parsedLabelPos = {\n        horizontal: {\n          top: '-',\n          bottom: '+'\n        },\n        vertical: {\n          left: '-',\n          right: '+'\n        }\n      }[orient][labelPosOpt];\n    } else {\n      // is number\n      parsedLabelPos = labelPosOpt;\n    }\n\n    var labelAlignMap = {\n      horizontal: 'center',\n      vertical: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'left' : 'right'\n    };\n    var labelBaselineMap = {\n      horizontal: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'top' : 'bottom',\n      vertical: 'middle'\n    };\n    var rotationMap = {\n      horizontal: 0,\n      vertical: PI$8 / 2\n    }; // Position\n\n    var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width;\n    var controlModel = timelineModel.getModel('controlStyle');\n    var showControl = controlModel.get('show', true);\n    var controlSize = showControl ? controlModel.get('itemSize') : 0;\n    var controlGap = showControl ? controlModel.get('itemGap') : 0;\n    var sizePlusGap = controlSize + controlGap; // Special label rotate.\n\n    var labelRotation = timelineModel.get(['label', 'rotate']) || 0;\n    labelRotation = labelRotation * PI$8 / 180; // To radian.\n\n    var playPosition;\n    var prevBtnPosition;\n    var nextBtnPosition;\n    var controlPosition = controlModel.get('position', true);\n    var showPlayBtn = showControl && controlModel.get('showPlayBtn', true);\n    var showPrevBtn = showControl && controlModel.get('showPrevBtn', true);\n    var showNextBtn = showControl && controlModel.get('showNextBtn', true);\n    var xLeft = 0;\n    var xRight = mainLength; // position[0] means left, position[1] means middle.\n\n    if (controlPosition === 'left' || controlPosition === 'bottom') {\n      showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap);\n      showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap);\n      showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);\n    } else {\n      // 'top' 'right'\n      showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);\n      showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap);\n      showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);\n    }\n\n    var axisExtent = [xLeft, xRight];\n\n    if (timelineModel.get('inverse')) {\n      axisExtent.reverse();\n    }\n\n    return {\n      viewRect: viewRect,\n      mainLength: mainLength,\n      orient: orient,\n      rotation: rotationMap[orient],\n      labelRotation: labelRotation,\n      labelPosOpt: parsedLabelPos,\n      labelAlign: timelineModel.get(['label', 'align']) || labelAlignMap[orient],\n      labelBaseline: timelineModel.get(['label', 'verticalAlign']) || timelineModel.get(['label', 'baseline']) || labelBaselineMap[orient],\n      // Based on mainGroup.\n      playPosition: playPosition,\n      prevBtnPosition: prevBtnPosition,\n      nextBtnPosition: nextBtnPosition,\n      axisExtent: axisExtent,\n      controlSize: controlSize,\n      controlGap: controlGap\n    };\n  };\n\n  SliderTimelineView.prototype._position = function (layoutInfo, timelineModel) {\n    // Position is be called finally, because bounding rect is needed for\n    // adapt content to fill viewRect (auto adapt offset).\n    // Timeline may be not all in the viewRect when 'offset' is specified\n    // as a number, because it is more appropriate that label aligns at\n    // 'offset' but not the other edge defined by viewRect.\n    var mainGroup = this._mainGroup;\n    var labelGroup = this._labelGroup;\n    var viewRect = layoutInfo.viewRect;\n\n    if (layoutInfo.orient === 'vertical') {\n      // transform to horizontal, inverse rotate by left-top point.\n      var m = create$1();\n      var rotateOriginX = viewRect.x;\n      var rotateOriginY = viewRect.y + viewRect.height;\n      translate(m, m, [-rotateOriginX, -rotateOriginY]);\n      rotate(m, m, -PI$8 / 2);\n      translate(m, m, [rotateOriginX, rotateOriginY]);\n      viewRect = viewRect.clone();\n      viewRect.applyTransform(m);\n    }\n\n    var viewBound = getBound(viewRect);\n    var mainBound = getBound(mainGroup.getBoundingRect());\n    var labelBound = getBound(labelGroup.getBoundingRect());\n    var mainPosition = [mainGroup.x, mainGroup.y];\n    var labelsPosition = [labelGroup.x, labelGroup.y];\n    labelsPosition[0] = mainPosition[0] = viewBound[0][0];\n    var labelPosOpt = layoutInfo.labelPosOpt;\n\n    if (labelPosOpt == null || isString(labelPosOpt)) {\n      // '+' or '-'\n      var mainBoundIdx = labelPosOpt === '+' ? 0 : 1;\n      toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);\n      toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx);\n    } else {\n      var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1;\n      toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);\n      labelsPosition[1] = mainPosition[1] + labelPosOpt;\n    }\n\n    mainGroup.setPosition(mainPosition);\n    labelGroup.setPosition(labelsPosition);\n    mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation;\n    setOrigin(mainGroup);\n    setOrigin(labelGroup);\n\n    function setOrigin(targetGroup) {\n      targetGroup.originX = viewBound[0][0] - targetGroup.x;\n      targetGroup.originY = viewBound[1][0] - targetGroup.y;\n    }\n\n    function getBound(rect) {\n      // [[xmin, xmax], [ymin, ymax]]\n      return [[rect.x, rect.x + rect.width], [rect.y, rect.y + rect.height]];\n    }\n\n    function toBound(fromPos, from, to, dimIdx, boundIdx) {\n      fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx];\n    }\n  };\n\n  SliderTimelineView.prototype._createAxis = function (layoutInfo, timelineModel) {\n    var data = timelineModel.getData();\n    var axisType = timelineModel.get('axisType');\n    var scale = createScaleByModel$1(timelineModel, axisType); // Customize scale. The `tickValue` is `dataIndex`.\n\n    scale.getTicks = function () {\n      return data.mapArray(['value'], function (value) {\n        return {\n          value: value\n        };\n      });\n    };\n\n    var dataExtent = data.getDataExtent('value');\n    scale.setExtent(dataExtent[0], dataExtent[1]);\n    scale.calcNiceTicks();\n    var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType);\n    axis.model = timelineModel;\n    return axis;\n  };\n\n  SliderTimelineView.prototype._createGroup = function (key) {\n    var newGroup = this[key] = new Group();\n    this.group.add(newGroup);\n    return newGroup;\n  };\n\n  SliderTimelineView.prototype._renderAxisLine = function (layoutInfo, group, axis, timelineModel) {\n    var axisExtent = axis.getExtent();\n\n    if (!timelineModel.get(['lineStyle', 'show'])) {\n      return;\n    }\n\n    var line = new Line({\n      shape: {\n        x1: axisExtent[0],\n        y1: 0,\n        x2: axisExtent[1],\n        y2: 0\n      },\n      style: extend({\n        lineCap: 'round'\n      }, timelineModel.getModel('lineStyle').getLineStyle()),\n      silent: true,\n      z2: 1\n    });\n    group.add(line);\n    var progressLine = this._progressLine = new Line({\n      shape: {\n        x1: axisExtent[0],\n        x2: this._currentPointer ? this._currentPointer.x : axisExtent[0],\n        y1: 0,\n        y2: 0\n      },\n      style: defaults({\n        lineCap: 'round',\n        lineWidth: line.style.lineWidth\n      }, timelineModel.getModel(['progress', 'lineStyle']).getLineStyle()),\n      silent: true,\n      z2: 1\n    });\n    group.add(progressLine);\n  };\n\n  SliderTimelineView.prototype._renderAxisTick = function (layoutInfo, group, axis, timelineModel) {\n    var _this = this;\n\n    var data = timelineModel.getData(); // Show all ticks, despite ignoring strategy.\n\n    var ticks = axis.scale.getTicks();\n    this._tickSymbols = []; // The value is dataIndex, see the customized scale.\n\n    each(ticks, function (tick) {\n      var tickCoord = axis.dataToCoord(tick.value);\n      var itemModel = data.getItemModel(tick.value);\n      var itemStyleModel = itemModel.getModel('itemStyle');\n      var hoverStyleModel = itemModel.getModel(['emphasis', 'itemStyle']);\n      var progressStyleModel = itemModel.getModel(['progress', 'itemStyle']);\n      var symbolOpt = {\n        x: tickCoord,\n        y: 0,\n        onclick: bind(_this._changeTimeline, _this, tick.value)\n      };\n      var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt);\n      el.ensureState('emphasis').style = hoverStyleModel.getItemStyle();\n      el.ensureState('progress').style = progressStyleModel.getItemStyle();\n      enableHoverEmphasis(el);\n      var ecData = getECData(el);\n\n      if (itemModel.get('tooltip')) {\n        ecData.dataIndex = tick.value;\n        ecData.dataModel = timelineModel;\n      } else {\n        ecData.dataIndex = ecData.dataModel = null;\n      }\n\n      _this._tickSymbols.push(el);\n    });\n  };\n\n  SliderTimelineView.prototype._renderAxisLabel = function (layoutInfo, group, axis, timelineModel) {\n    var _this = this;\n\n    var labelModel = axis.getLabelModel();\n\n    if (!labelModel.get('show')) {\n      return;\n    }\n\n    var data = timelineModel.getData();\n    var labels = axis.getViewLabels();\n    this._tickLabels = [];\n    each(labels, function (labelItem) {\n      // The tickValue is dataIndex, see the customized scale.\n      var dataIndex = labelItem.tickValue;\n      var itemModel = data.getItemModel(dataIndex);\n      var normalLabelModel = itemModel.getModel('label');\n      var hoverLabelModel = itemModel.getModel(['emphasis', 'label']);\n      var progressLabelModel = itemModel.getModel(['progress', 'label']);\n      var tickCoord = axis.dataToCoord(labelItem.tickValue);\n      var textEl = new ZRText({\n        x: tickCoord,\n        y: 0,\n        rotation: layoutInfo.labelRotation - layoutInfo.rotation,\n        onclick: bind(_this._changeTimeline, _this, dataIndex),\n        silent: false,\n        style: createTextStyle(normalLabelModel, {\n          text: labelItem.formattedLabel,\n          align: layoutInfo.labelAlign,\n          verticalAlign: layoutInfo.labelBaseline\n        })\n      });\n      textEl.ensureState('emphasis').style = createTextStyle(hoverLabelModel);\n      textEl.ensureState('progress').style = createTextStyle(progressLabelModel);\n      group.add(textEl);\n      enableHoverEmphasis(textEl);\n      labelDataIndexStore(textEl).dataIndex = dataIndex;\n\n      _this._tickLabels.push(textEl);\n    });\n  };\n\n  SliderTimelineView.prototype._renderControl = function (layoutInfo, group, axis, timelineModel) {\n    var controlSize = layoutInfo.controlSize;\n    var rotation = layoutInfo.rotation;\n    var itemStyle = timelineModel.getModel('controlStyle').getItemStyle();\n    var hoverStyle = timelineModel.getModel(['emphasis', 'controlStyle']).getItemStyle();\n    var playState = timelineModel.getPlayState();\n    var inverse = timelineModel.get('inverse', true);\n    makeBtn(layoutInfo.nextBtnPosition, 'next', bind(this._changeTimeline, this, inverse ? '-' : '+'));\n    makeBtn(layoutInfo.prevBtnPosition, 'prev', bind(this._changeTimeline, this, inverse ? '+' : '-'));\n    makeBtn(layoutInfo.playPosition, playState ? 'stop' : 'play', bind(this._handlePlayClick, this, !playState), true);\n\n    function makeBtn(position, iconName, onclick, willRotate) {\n      if (!position) {\n        return;\n      }\n\n      var iconSize = parsePercent(retrieve2(timelineModel.get(['controlStyle', iconName + 'BtnSize']), controlSize), controlSize);\n      var rect = [0, -iconSize / 2, iconSize, iconSize];\n      var btn = makeControlIcon(timelineModel, iconName + 'Icon', rect, {\n        x: position[0],\n        y: position[1],\n        originX: controlSize / 2,\n        originY: 0,\n        rotation: willRotate ? -rotation : 0,\n        rectHover: true,\n        style: itemStyle,\n        onclick: onclick\n      });\n      btn.ensureState('emphasis').style = hoverStyle;\n      group.add(btn);\n      enableHoverEmphasis(btn);\n    }\n  };\n\n  SliderTimelineView.prototype._renderCurrentPointer = function (layoutInfo, group, axis, timelineModel) {\n    var data = timelineModel.getData();\n    var currentIndex = timelineModel.getCurrentIndex();\n    var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle');\n    var me = this;\n    var callback = {\n      onCreate: function (pointer) {\n        pointer.draggable = true;\n        pointer.drift = bind(me._handlePointerDrag, me);\n        pointer.ondragend = bind(me._handlePointerDragend, me);\n        pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel, true);\n      },\n      onUpdate: function (pointer) {\n        pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel);\n      }\n    }; // Reuse when exists, for animation and drag.\n\n    this._currentPointer = giveSymbol(pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback);\n  };\n\n  SliderTimelineView.prototype._handlePlayClick = function (nextState) {\n    this._clearTimer();\n\n    this.api.dispatchAction({\n      type: 'timelinePlayChange',\n      playState: nextState,\n      from: this.uid\n    });\n  };\n\n  SliderTimelineView.prototype._handlePointerDrag = function (dx, dy, e) {\n    this._clearTimer();\n\n    this._pointerChangeTimeline([e.offsetX, e.offsetY]);\n  };\n\n  SliderTimelineView.prototype._handlePointerDragend = function (e) {\n    this._pointerChangeTimeline([e.offsetX, e.offsetY], true);\n  };\n\n  SliderTimelineView.prototype._pointerChangeTimeline = function (mousePos, trigger) {\n    var toCoord = this._toAxisCoord(mousePos)[0];\n\n    var axis = this._axis;\n    var axisExtent = asc(axis.getExtent().slice());\n    toCoord > axisExtent[1] && (toCoord = axisExtent[1]);\n    toCoord < axisExtent[0] && (toCoord = axisExtent[0]);\n    this._currentPointer.x = toCoord;\n\n    this._currentPointer.markRedraw();\n\n    var progressLine = this._progressLine;\n\n    if (progressLine) {\n      progressLine.shape.x2 = toCoord;\n      progressLine.dirty();\n    }\n\n    var targetDataIndex = this._findNearestTick(toCoord);\n\n    var timelineModel = this.model;\n\n    if (trigger || targetDataIndex !== timelineModel.getCurrentIndex() && timelineModel.get('realtime')) {\n      this._changeTimeline(targetDataIndex);\n    }\n  };\n\n  SliderTimelineView.prototype._doPlayStop = function () {\n    var _this = this;\n\n    this._clearTimer();\n\n    if (this.model.getPlayState()) {\n      this._timer = setTimeout(function () {\n        // Do not cache\n        var timelineModel = _this.model;\n\n        _this._changeTimeline(timelineModel.getCurrentIndex() + (timelineModel.get('rewind', true) ? -1 : 1));\n      }, this.model.get('playInterval'));\n    }\n  };\n\n  SliderTimelineView.prototype._toAxisCoord = function (vertex) {\n    var trans = this._mainGroup.getLocalTransform();\n\n    return applyTransform$1(vertex, trans, true);\n  };\n\n  SliderTimelineView.prototype._findNearestTick = function (axisCoord) {\n    var data = this.model.getData();\n    var dist = Infinity;\n    var targetDataIndex;\n    var axis = this._axis;\n    data.each(['value'], function (value, dataIndex) {\n      var coord = axis.dataToCoord(value);\n      var d = Math.abs(coord - axisCoord);\n\n      if (d < dist) {\n        dist = d;\n        targetDataIndex = dataIndex;\n      }\n    });\n    return targetDataIndex;\n  };\n\n  SliderTimelineView.prototype._clearTimer = function () {\n    if (this._timer) {\n      clearTimeout(this._timer);\n      this._timer = null;\n    }\n  };\n\n  SliderTimelineView.prototype._changeTimeline = function (nextIndex) {\n    var currentIndex = this.model.getCurrentIndex();\n\n    if (nextIndex === '+') {\n      nextIndex = currentIndex + 1;\n    } else if (nextIndex === '-') {\n      nextIndex = currentIndex - 1;\n    }\n\n    this.api.dispatchAction({\n      type: 'timelineChange',\n      currentIndex: nextIndex,\n      from: this.uid\n    });\n  };\n\n  SliderTimelineView.prototype._updateTicksStatus = function () {\n    var currentIndex = this.model.getCurrentIndex();\n    var tickSymbols = this._tickSymbols;\n    var tickLabels = this._tickLabels;\n\n    if (tickSymbols) {\n      for (var i = 0; i < tickSymbols.length; i++) {\n        tickSymbols && tickSymbols[i] && tickSymbols[i].toggleState('progress', i < currentIndex);\n      }\n    }\n\n    if (tickLabels) {\n      for (var i = 0; i < tickLabels.length; i++) {\n        tickLabels && tickLabels[i] && tickLabels[i].toggleState('progress', labelDataIndexStore(tickLabels[i]).dataIndex <= currentIndex);\n      }\n    }\n  };\n\n  SliderTimelineView.type = 'timeline.slider';\n  return SliderTimelineView;\n}(TimelineView);\n\nfunction createScaleByModel$1(model, axisType) {\n  axisType = axisType || model.get('type');\n\n  if (axisType) {\n    switch (axisType) {\n      // Buildin scale\n      case 'category':\n        return new OrdinalScale({\n          ordinalMeta: model.getCategories(),\n          extent: [Infinity, -Infinity]\n        });\n\n      case 'time':\n        return new TimeScale({\n          locale: model.ecModel.getLocaleModel(),\n          useUTC: model.ecModel.get('useUTC')\n        });\n\n      default:\n        // default to be value\n        return new IntervalScale();\n    }\n  }\n}\n\nfunction getViewRect$5(model, api) {\n  return getLayoutRect(model.getBoxLayoutParams(), {\n    width: api.getWidth(),\n    height: api.getHeight()\n  }, model.get('padding'));\n}\n\nfunction makeControlIcon(timelineModel, objPath, rect, opts) {\n  var style = opts.style;\n  var icon = createIcon(timelineModel.get(['controlStyle', objPath]), opts || {}, new BoundingRect(rect[0], rect[1], rect[2], rect[3])); // TODO createIcon won't use style in opt.\n\n  if (style) {\n    icon.setStyle(style);\n  }\n\n  return icon;\n}\n/**\n * Create symbol or update symbol\n * opt: basic position and event handlers\n */\n\n\nfunction giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) {\n  var color = itemStyleModel.get('color');\n\n  if (!symbol) {\n    var symbolType = hostModel.get('symbol');\n    symbol = createSymbol(symbolType, -1, -1, 2, 2, color);\n    symbol.setStyle('strokeNoScale', true);\n    group.add(symbol);\n    callback && callback.onCreate(symbol);\n  } else {\n    symbol.setColor(color);\n    group.add(symbol); // Group may be new, also need to add.\n\n    callback && callback.onUpdate(symbol);\n  } // Style\n\n\n  var itemStyle = itemStyleModel.getItemStyle(['color']);\n  symbol.setStyle(itemStyle); // Transform and events.\n\n  opt = merge({\n    rectHover: true,\n    z2: 100\n  }, opt, true);\n  var symbolSize = normalizeSymbolSize(hostModel.get('symbolSize'));\n  opt.scaleX = symbolSize[0] / 2;\n  opt.scaleY = symbolSize[1] / 2;\n  var symbolOffset = normalizeSymbolOffset(hostModel.get('symbolOffset'), symbolSize);\n\n  if (symbolOffset) {\n    opt.x = (opt.x || 0) + symbolOffset[0];\n    opt.y = (opt.y || 0) + symbolOffset[1];\n  }\n\n  var symbolRotate = hostModel.get('symbolRotate');\n  opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;\n  symbol.attr(opt); // FIXME\n  // (1) When symbol.style.strokeNoScale is true and updateTransform is not performed,\n  // getBoundingRect will return wrong result.\n  // (This is supposed to be resolved in zrender, but it is a little difficult to\n  // leverage performance and auto updateTransform)\n  // (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol.\n\n  symbol.updateTransform();\n  return symbol;\n}\n\nfunction pointerMoveTo(pointer, progressLine, dataIndex, axis, timelineModel, noAnimation) {\n  if (pointer.dragging) {\n    return;\n  }\n\n  var pointerModel = timelineModel.getModel('checkpointStyle');\n  var toCoord = axis.dataToCoord(timelineModel.getData().get('value', dataIndex));\n\n  if (noAnimation || !pointerModel.get('animation', true)) {\n    pointer.attr({\n      x: toCoord,\n      y: 0\n    });\n    progressLine && progressLine.attr({\n      shape: {\n        x2: toCoord\n      }\n    });\n  } else {\n    var animationCfg = {\n      duration: pointerModel.get('animationDuration', true),\n      easing: pointerModel.get('animationEasing', true)\n    };\n    pointer.stopAnimation(null, true);\n    pointer.animateTo({\n      x: toCoord,\n      y: 0\n    }, animationCfg);\n    progressLine && progressLine.animateTo({\n      shape: {\n        x2: toCoord\n      }\n    }, animationCfg);\n  }\n}\n\nfunction installTimelineAction(registers) {\n  registers.registerAction({\n    type: 'timelineChange',\n    event: 'timelineChanged',\n    update: 'prepareAndUpdate'\n  }, function (payload, ecModel, api) {\n    var timelineModel = ecModel.getComponent('timeline');\n\n    if (timelineModel && payload.currentIndex != null) {\n      timelineModel.setCurrentIndex(payload.currentIndex);\n\n      if (!timelineModel.get('loop', true) && timelineModel.isIndexMax() && timelineModel.getPlayState()) {\n        timelineModel.setPlayState(false); // The timeline has played to the end, trigger event\n\n        api.dispatchAction({\n          type: 'timelinePlayChange',\n          playState: false,\n          from: payload.from\n        });\n      }\n    } // Set normalized currentIndex to payload.\n\n\n    ecModel.resetOption('timeline', {\n      replaceMerge: timelineModel.get('replaceMerge', true)\n    });\n    return defaults({\n      currentIndex: timelineModel.option.currentIndex\n    }, payload);\n  });\n  registers.registerAction({\n    type: 'timelinePlayChange',\n    event: 'timelinePlayChanged',\n    update: 'update'\n  }, function (payload, ecModel) {\n    var timelineModel = ecModel.getComponent('timeline');\n\n    if (timelineModel && payload.playState != null) {\n      timelineModel.setPlayState(payload.playState);\n    }\n  });\n}\n\nfunction timelinePreprocessor(option) {\n  var timelineOpt = option && option.timeline;\n\n  if (!isArray(timelineOpt)) {\n    timelineOpt = timelineOpt ? [timelineOpt] : [];\n  }\n\n  each(timelineOpt, function (opt) {\n    if (!opt) {\n      return;\n    }\n\n    compatibleEC2(opt);\n  });\n}\n\nfunction compatibleEC2(opt) {\n  var type = opt.type;\n  var ec2Types = {\n    'number': 'value',\n    'time': 'time'\n  }; // Compatible with ec2\n\n  if (ec2Types[type]) {\n    opt.axisType = ec2Types[type];\n    delete opt.type;\n  }\n\n  transferItem(opt);\n\n  if (has(opt, 'controlPosition')) {\n    var controlStyle = opt.controlStyle || (opt.controlStyle = {});\n\n    if (!has(controlStyle, 'position')) {\n      controlStyle.position = opt.controlPosition;\n    }\n\n    if (controlStyle.position === 'none' && !has(controlStyle, 'show')) {\n      controlStyle.show = false;\n      delete controlStyle.position;\n    }\n\n    delete opt.controlPosition;\n  }\n\n  each(opt.data || [], function (dataItem) {\n    if (isObject(dataItem) && !isArray(dataItem)) {\n      if (!has(dataItem, 'value') && has(dataItem, 'name')) {\n        // In ec2, using name as value.\n        dataItem.value = dataItem.name;\n      }\n\n      transferItem(dataItem);\n    }\n  });\n}\n\nfunction transferItem(opt) {\n  var itemStyle = opt.itemStyle || (opt.itemStyle = {});\n  var itemStyleEmphasis = itemStyle.emphasis || (itemStyle.emphasis = {}); // Transfer label out\n\n  var label = opt.label || opt.label || {};\n  var labelNormal = label.normal || (label.normal = {});\n  var excludeLabelAttr = {\n    normal: 1,\n    emphasis: 1\n  };\n  each(label, function (value, name) {\n    if (!excludeLabelAttr[name] && !has(labelNormal, name)) {\n      labelNormal[name] = value;\n    }\n  });\n\n  if (itemStyleEmphasis.label && !has(label, 'emphasis')) {\n    label.emphasis = itemStyleEmphasis.label;\n    delete itemStyleEmphasis.label;\n  }\n}\n\nfunction has(obj, attr) {\n  return obj.hasOwnProperty(attr);\n}\n\nfunction install$D(registers) {\n  registers.registerComponentModel(SliderTimelineModel);\n  registers.registerComponentView(SliderTimelineView);\n  registers.registerSubTypeDefaulter('timeline', function () {\n    // Only slider now.\n    return 'slider';\n  });\n  installTimelineAction(registers);\n  registers.registerPreprocessor(timelinePreprocessor);\n}\n\nfunction checkMarkerInSeries(seriesOpts, markerType) {\n  if (!seriesOpts) {\n    return false;\n  }\n\n  var seriesOptArr = isArray(seriesOpts) ? seriesOpts : [seriesOpts];\n\n  for (var idx = 0; idx < seriesOptArr.length; idx++) {\n    if (seriesOptArr[idx] && seriesOptArr[idx][markerType]) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nfunction fillLabel(opt) {\n  defaultEmphasis(opt, 'label', ['show']);\n} // { [componentType]: MarkerModel }\n\n\nvar inner$g = makeInner();\n\nvar MarkerModel =\n/** @class */\nfunction (_super) {\n  __extends(MarkerModel, _super);\n\n  function MarkerModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkerModel.type;\n    /**\n     * If marker model is created by self from series\n     */\n\n    _this.createdBySelf = false;\n    return _this;\n  }\n  /**\n   * @overrite\n   */\n\n\n  MarkerModel.prototype.init = function (option, parentModel, ecModel) {\n    if (\"development\" !== 'production') {\n      if (this.type === 'marker') {\n        throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.');\n      }\n    }\n\n    this.mergeDefaultAndTheme(option, ecModel);\n\n    this._mergeOption(option, ecModel, false, true);\n  };\n\n  MarkerModel.prototype.isAnimationEnabled = function () {\n    if (env.node) {\n      return false;\n    }\n\n    var hostSeries = this.__hostSeries;\n    return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled();\n  };\n  /**\n   * @overrite\n   */\n\n\n  MarkerModel.prototype.mergeOption = function (newOpt, ecModel) {\n    this._mergeOption(newOpt, ecModel, false, false);\n  };\n\n  MarkerModel.prototype._mergeOption = function (newOpt, ecModel, createdBySelf, isInit) {\n    var componentType = this.mainType;\n\n    if (!createdBySelf) {\n      ecModel.eachSeries(function (seriesModel) {\n        // mainType can be markPoint, markLine, markArea\n        var markerOpt = seriesModel.get(this.mainType, true);\n        var markerModel = inner$g(seriesModel)[componentType];\n\n        if (!markerOpt || !markerOpt.data) {\n          inner$g(seriesModel)[componentType] = null;\n          return;\n        }\n\n        if (!markerModel) {\n          if (isInit) {\n            // Default label emphasis `position` and `show`\n            fillLabel(markerOpt);\n          }\n\n          each(markerOpt.data, function (item) {\n            // FIXME Overwrite fillLabel method ?\n            if (item instanceof Array) {\n              fillLabel(item[0]);\n              fillLabel(item[1]);\n            } else {\n              fillLabel(item);\n            }\n          });\n          markerModel = this.createMarkerModelFromSeries(markerOpt, this, ecModel); // markerModel = new ImplementedMarkerModel(\n          //     markerOpt, this, ecModel\n          // );\n\n          extend(markerModel, {\n            mainType: this.mainType,\n            // Use the same series index and name\n            seriesIndex: seriesModel.seriesIndex,\n            name: seriesModel.name,\n            createdBySelf: true\n          });\n          markerModel.__hostSeries = seriesModel;\n        } else {\n          markerModel._mergeOption(markerOpt, ecModel, true);\n        }\n\n        inner$g(seriesModel)[componentType] = markerModel;\n      }, this);\n    }\n  };\n\n  MarkerModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n    var data = this.getData();\n    var value = this.getRawValue(dataIndex);\n    var itemName = data.getName(dataIndex);\n    return createTooltipMarkup('section', {\n      header: this.name,\n      blocks: [createTooltipMarkup('nameValue', {\n        name: itemName,\n        value: value,\n        noName: !itemName,\n        noValue: value == null\n      })]\n    });\n  };\n\n  MarkerModel.prototype.getData = function () {\n    return this._data;\n  };\n\n  MarkerModel.prototype.setData = function (data) {\n    this._data = data;\n  };\n\n  MarkerModel.getMarkerModelFromSeries = function (seriesModel, // Support three types of markers. Strict check.\n  componentType) {\n    return inner$g(seriesModel)[componentType];\n  };\n\n  MarkerModel.type = 'marker';\n  MarkerModel.dependencies = ['series', 'grid', 'polar', 'geo'];\n  return MarkerModel;\n}(ComponentModel);\n\nmixin(MarkerModel, DataFormatMixin.prototype);\n\nvar MarkPointModel =\n/** @class */\nfunction (_super) {\n  __extends(MarkPointModel, _super);\n\n  function MarkPointModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkPointModel.type;\n    return _this;\n  }\n\n  MarkPointModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n    return new MarkPointModel(markerOpt, masterMarkerModel, ecModel);\n  };\n\n  MarkPointModel.type = 'markPoint';\n  MarkPointModel.defaultOption = {\n    // zlevel: 0,\n    z: 5,\n    symbol: 'pin',\n    symbolSize: 50,\n    // symbolRotate: 0,\n    // symbolOffset: [0, 0]\n    tooltip: {\n      trigger: 'item'\n    },\n    label: {\n      show: true,\n      position: 'inside'\n    },\n    itemStyle: {\n      borderWidth: 2\n    },\n    emphasis: {\n      label: {\n        show: true\n      }\n    }\n  };\n  return MarkPointModel;\n}(MarkerModel);\n\nfunction hasXOrY(item) {\n  return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y)));\n}\n\nfunction hasXAndY(item) {\n  return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y));\n}\n\nfunction markerTypeCalculatorWithExtent(markerType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex) {\n  var coordArr = [];\n  var stacked = isDimensionStacked(data, targetDataDim\n  /* , otherDataDim */\n  );\n  var calcDataDim = stacked ? data.getCalculationInfo('stackResultDimension') : targetDataDim;\n  var value = numCalculate(data, calcDataDim, markerType);\n  var dataIndex = data.indicesOfNearest(calcDataDim, value)[0];\n  coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex);\n  coordArr[targetCoordIndex] = data.get(calcDataDim, dataIndex);\n  var coordArrValue = data.get(targetDataDim, dataIndex); // Make it simple, do not visit all stacked value to count precision.\n\n  var precision = getPrecision(data.get(targetDataDim, dataIndex));\n  precision = Math.min(precision, 20);\n\n  if (precision >= 0) {\n    coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision);\n  }\n\n  return [coordArr, coordArrValue];\n} // TODO Specified percent\n\n\nvar markerTypeCalculator = {\n  min: curry(markerTypeCalculatorWithExtent, 'min'),\n  max: curry(markerTypeCalculatorWithExtent, 'max'),\n  average: curry(markerTypeCalculatorWithExtent, 'average'),\n  median: curry(markerTypeCalculatorWithExtent, 'median')\n};\n/**\n * Transform markPoint data item to format used in List by do the following\n * 1. Calculate statistic like `max`, `min`, `average`\n * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array\n */\n\nfunction dataTransform(seriesModel, item) {\n  if (!item) {\n    return;\n  }\n\n  var data = seriesModel.getData();\n  var coordSys = seriesModel.coordinateSystem;\n  var dims = coordSys.dimensions; // 1. If not specify the position with pixel directly\n  // 2. If `coord` is not a data array. Which uses `xAxis`,\n  // `yAxis` to specify the coord on each dimension\n  // parseFloat first because item.x and item.y can be percent string like '20%'\n\n  if (!hasXAndY(item) && !isArray(item.coord) && coordSys) {\n    var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); // Clone the option\n    // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value\n\n    item = clone(item);\n\n    if (item.type && markerTypeCalculator[item.type] && axisInfo.baseAxis && axisInfo.valueAxis) {\n      var otherCoordIndex = indexOf(dims, axisInfo.baseAxis.dim);\n      var targetCoordIndex = indexOf(dims, axisInfo.valueAxis.dim);\n      var coordInfo = markerTypeCalculator[item.type](data, axisInfo.baseDataDim, axisInfo.valueDataDim, otherCoordIndex, targetCoordIndex);\n      item.coord = coordInfo[0]; // Force to use the value of calculated value.\n      // let item use the value without stack.\n\n      item.value = coordInfo[1];\n    } else {\n      // FIXME Only has one of xAxis and yAxis.\n      item.coord = [item.xAxis != null ? item.xAxis : item.radiusAxis, item.yAxis != null ? item.yAxis : item.angleAxis];\n    }\n  } // x y is provided\n\n\n  if (item.coord == null) {\n    item.coord = [];\n  } else {\n    // Each coord support max, min, average\n    var coord = item.coord;\n\n    for (var i = 0; i < 2; i++) {\n      if (markerTypeCalculator[coord[i]]) {\n        coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]);\n      }\n    }\n  }\n\n  return item;\n}\nfunction getAxisInfo$1(item, data, coordSys, seriesModel) {\n  var ret = {};\n\n  if (item.valueIndex != null || item.valueDim != null) {\n    ret.valueDataDim = item.valueIndex != null ? data.getDimension(item.valueIndex) : item.valueDim;\n    ret.valueAxis = coordSys.getAxis(dataDimToCoordDim(seriesModel, ret.valueDataDim));\n    ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis);\n    ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);\n  } else {\n    ret.baseAxis = seriesModel.getBaseAxis();\n    ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis);\n    ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);\n    ret.valueDataDim = data.mapDimension(ret.valueAxis.dim);\n  }\n\n  return ret;\n}\n\nfunction dataDimToCoordDim(seriesModel, dataDim) {\n  var dimItem = seriesModel.getData().getDimensionInfo(dataDim);\n  return dimItem && dimItem.coordDim;\n}\n/**\n * Filter data which is out of coordinateSystem range\n * [dataFilter description]\n */\n\n\nfunction dataFilter$1( // Currently only polar and cartesian has containData.\ncoordSys, item) {\n  // Always return true if there is no coordSys\n  return coordSys && coordSys.containData && item.coord && !hasXOrY(item) ? coordSys.containData(item.coord) : true;\n}\nfunction zoneFilter( // Currently only polar and cartesian has containData.\ncoordSys, item1, item2) {\n  // Always return true if there is no coordSys\n  return coordSys && coordSys.containZone && item1.coord && item2.coord && !hasXOrY(item1) && !hasXOrY(item2) ? coordSys.containZone(item1.coord, item2.coord) : true;\n}\nfunction createMarkerDimValueGetter(inCoordSys, dims) {\n  return inCoordSys ? function (item, dimName, dataIndex, dimIndex) {\n    var rawVal = dimIndex < 2 // x, y, radius, angle\n    ? item.coord && item.coord[dimIndex] : item.value;\n    return parseDataValue(rawVal, dims[dimIndex]);\n  } : function (item, dimName, dataIndex, dimIndex) {\n    return parseDataValue(item.value, dims[dimIndex]);\n  };\n}\nfunction numCalculate(data, valueDataDim, type) {\n  if (type === 'average') {\n    var sum_1 = 0;\n    var count_1 = 0;\n    data.each(valueDataDim, function (val, idx) {\n      if (!isNaN(val)) {\n        sum_1 += val;\n        count_1++;\n      }\n    });\n    return sum_1 / count_1;\n  } else if (type === 'median') {\n    return data.getMedian(valueDataDim);\n  } else {\n    // max & min\n    return data.getDataExtent(valueDataDim)[type === 'max' ? 1 : 0];\n  }\n}\n\nvar inner$h = makeInner();\n\nvar MarkerView =\n/** @class */\nfunction (_super) {\n  __extends(MarkerView, _super);\n\n  function MarkerView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkerView.type;\n    return _this;\n  }\n\n  MarkerView.prototype.init = function () {\n    this.markerGroupMap = createHashMap();\n  };\n\n  MarkerView.prototype.render = function (markerModel, ecModel, api) {\n    var _this = this;\n\n    var markerGroupMap = this.markerGroupMap;\n    markerGroupMap.each(function (item) {\n      inner$h(item).keep = false;\n    });\n    ecModel.eachSeries(function (seriesModel) {\n      var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type);\n      markerModel && _this.renderSeries(seriesModel, markerModel, ecModel, api);\n    });\n    markerGroupMap.each(function (item) {\n      !inner$h(item).keep && _this.group.remove(item.group);\n    });\n  };\n\n  MarkerView.prototype.markKeep = function (drawGroup) {\n    inner$h(drawGroup).keep = true;\n  };\n\n  MarkerView.prototype.toggleBlurSeries = function (seriesModelList, isBlur) {\n    var _this = this;\n\n    each(seriesModelList, function (seriesModel) {\n      var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type);\n\n      if (markerModel) {\n        var data = markerModel.getData();\n        data.eachItemGraphicEl(function (el) {\n          if (el) {\n            isBlur ? enterBlur(el) : leaveBlur(el);\n          }\n        });\n      }\n    });\n  };\n\n  MarkerView.type = 'marker';\n  return MarkerView;\n}(ComponentView);\n\nfunction updateMarkerLayout(mpData, seriesModel, api) {\n  var coordSys = seriesModel.coordinateSystem;\n  mpData.each(function (idx) {\n    var itemModel = mpData.getItemModel(idx);\n    var point;\n    var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());\n    var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());\n\n    if (!isNaN(xPx) && !isNaN(yPx)) {\n      point = [xPx, yPx];\n    } // Chart like bar may have there own marker positioning logic\n    else if (seriesModel.getMarkerPosition) {\n        // Use the getMarkerPosition\n        point = seriesModel.getMarkerPosition(mpData.getValues(mpData.dimensions, idx));\n      } else if (coordSys) {\n        var x = mpData.get(coordSys.dimensions[0], idx);\n        var y = mpData.get(coordSys.dimensions[1], idx);\n        point = coordSys.dataToPoint([x, y]);\n      } // Use x, y if has any\n\n\n    if (!isNaN(xPx)) {\n      point[0] = xPx;\n    }\n\n    if (!isNaN(yPx)) {\n      point[1] = yPx;\n    }\n\n    mpData.setItemLayout(idx, point);\n  });\n}\n\nvar MarkPointView =\n/** @class */\nfunction (_super) {\n  __extends(MarkPointView, _super);\n\n  function MarkPointView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkPointView.type;\n    return _this;\n  }\n\n  MarkPointView.prototype.updateTransform = function (markPointModel, ecModel, api) {\n    ecModel.eachSeries(function (seriesModel) {\n      var mpModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markPoint');\n\n      if (mpModel) {\n        updateMarkerLayout(mpModel.getData(), seriesModel, api);\n        this.markerGroupMap.get(seriesModel.id).updateLayout();\n      }\n    }, this);\n  };\n\n  MarkPointView.prototype.renderSeries = function (seriesModel, mpModel, ecModel, api) {\n    var coordSys = seriesModel.coordinateSystem;\n    var seriesId = seriesModel.id;\n    var seriesData = seriesModel.getData();\n    var symbolDrawMap = this.markerGroupMap;\n    var symbolDraw = symbolDrawMap.get(seriesId) || symbolDrawMap.set(seriesId, new SymbolDraw());\n    var mpData = createData(coordSys, seriesModel, mpModel); // FIXME\n\n    mpModel.setData(mpData);\n    updateMarkerLayout(mpModel.getData(), seriesModel, api);\n    mpData.each(function (idx) {\n      var itemModel = mpData.getItemModel(idx);\n      var symbol = itemModel.getShallow('symbol');\n      var symbolSize = itemModel.getShallow('symbolSize');\n      var symbolRotate = itemModel.getShallow('symbolRotate');\n      var symbolOffset = itemModel.getShallow('symbolOffset');\n      var symbolKeepAspect = itemModel.getShallow('symbolKeepAspect'); // TODO: refactor needed: single data item should not support callback function\n\n      if (isFunction(symbol) || isFunction(symbolSize) || isFunction(symbolRotate) || isFunction(symbolOffset)) {\n        var rawIdx = mpModel.getRawValue(idx);\n        var dataParams = mpModel.getDataParams(idx);\n\n        if (isFunction(symbol)) {\n          symbol = symbol(rawIdx, dataParams);\n        }\n\n        if (isFunction(symbolSize)) {\n          // FIXME 这里不兼容 ECharts 2.x，2.x 貌似参数是整个数据？\n          symbolSize = symbolSize(rawIdx, dataParams);\n        }\n\n        if (isFunction(symbolRotate)) {\n          symbolRotate = symbolRotate(rawIdx, dataParams);\n        }\n\n        if (isFunction(symbolOffset)) {\n          symbolOffset = symbolOffset(rawIdx, dataParams);\n        }\n      }\n\n      var style = itemModel.getModel('itemStyle').getItemStyle();\n      var color = getVisualFromData(seriesData, 'color');\n\n      if (!style.fill) {\n        style.fill = color;\n      }\n\n      mpData.setItemVisual(idx, {\n        symbol: symbol,\n        symbolSize: symbolSize,\n        symbolRotate: symbolRotate,\n        symbolOffset: symbolOffset,\n        symbolKeepAspect: symbolKeepAspect,\n        style: style\n      });\n    }); // TODO Text are wrong\n\n    symbolDraw.updateData(mpData);\n    this.group.add(symbolDraw.group); // Set host model for tooltip\n    // FIXME\n\n    mpData.eachItemGraphicEl(function (el) {\n      el.traverse(function (child) {\n        getECData(child).dataModel = mpModel;\n      });\n    });\n    this.markKeep(symbolDraw);\n    symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent');\n  };\n\n  MarkPointView.type = 'markPoint';\n  return MarkPointView;\n}(MarkerView);\n\nfunction createData(coordSys, seriesModel, mpModel) {\n  var coordDimsInfos;\n\n  if (coordSys) {\n    coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {\n      var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n      return extend(extend({}, info), {\n        name: coordDim,\n        // DON'T use ordinalMeta to parse and collect ordinal.\n        ordinalMeta: null\n      });\n    });\n  } else {\n    coordDimsInfos = [{\n      name: 'value',\n      type: 'float'\n    }];\n  }\n\n  var mpData = new SeriesData(coordDimsInfos, mpModel);\n  var dataOpt = map(mpModel.get('data'), curry(dataTransform, seriesModel));\n\n  if (coordSys) {\n    dataOpt = filter(dataOpt, curry(dataFilter$1, coordSys));\n  }\n\n  var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos);\n  mpData.initData(dataOpt, null, dimValueGetter);\n  return mpData;\n}\n\nfunction install$E(registers) {\n  registers.registerComponentModel(MarkPointModel);\n  registers.registerComponentView(MarkPointView);\n  registers.registerPreprocessor(function (opt) {\n    if (checkMarkerInSeries(opt.series, 'markPoint')) {\n      // Make sure markPoint component is enabled\n      opt.markPoint = opt.markPoint || {};\n    }\n  });\n}\n\nvar MarkLineModel =\n/** @class */\nfunction (_super) {\n  __extends(MarkLineModel, _super);\n\n  function MarkLineModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkLineModel.type;\n    return _this;\n  }\n\n  MarkLineModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n    return new MarkLineModel(markerOpt, masterMarkerModel, ecModel);\n  };\n\n  MarkLineModel.type = 'markLine';\n  MarkLineModel.defaultOption = {\n    // zlevel: 0,\n    z: 5,\n    symbol: ['circle', 'arrow'],\n    symbolSize: [8, 16],\n    // symbolRotate: 0,\n    symbolOffset: 0,\n    precision: 2,\n    tooltip: {\n      trigger: 'item'\n    },\n    label: {\n      show: true,\n      position: 'end',\n      distance: 5\n    },\n    lineStyle: {\n      type: 'dashed'\n    },\n    emphasis: {\n      label: {\n        show: true\n      },\n      lineStyle: {\n        width: 3\n      }\n    },\n    animationEasing: 'linear'\n  };\n  return MarkLineModel;\n}(MarkerModel);\n\nvar inner$i = makeInner();\n\nvar markLineTransform = function (seriesModel, coordSys, mlModel, item) {\n  var data = seriesModel.getData();\n  var itemArray;\n\n  if (!isArray(item)) {\n    // Special type markLine like 'min', 'max', 'average', 'median'\n    var mlType = item.type;\n\n    if (mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median' // In case\n    // data: [{\n    //   yAxis: 10\n    // }]\n    || item.xAxis != null || item.yAxis != null) {\n      var valueAxis = void 0;\n      var value = void 0;\n\n      if (item.yAxis != null || item.xAxis != null) {\n        valueAxis = coordSys.getAxis(item.yAxis != null ? 'y' : 'x');\n        value = retrieve(item.yAxis, item.xAxis);\n      } else {\n        var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel);\n        valueAxis = axisInfo.valueAxis;\n        var valueDataDim = getStackedDimension(data, axisInfo.valueDataDim);\n        value = numCalculate(data, valueDataDim, mlType);\n      }\n\n      var valueIndex = valueAxis.dim === 'x' ? 0 : 1;\n      var baseIndex = 1 - valueIndex; // Normized to 2d data with start and end point\n\n      var mlFrom = clone(item);\n      var mlTo = {\n        coord: []\n      };\n      mlFrom.type = null;\n      mlFrom.coord = [];\n      mlFrom.coord[baseIndex] = -Infinity;\n      mlTo.coord[baseIndex] = Infinity;\n      var precision = mlModel.get('precision');\n\n      if (precision >= 0 && isNumber(value)) {\n        value = +value.toFixed(Math.min(precision, 20));\n      }\n\n      mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;\n      itemArray = [mlFrom, mlTo, {\n        type: mlType,\n        valueIndex: item.valueIndex,\n        // Force to use the value of calculated value.\n        value: value\n      }];\n    } else {\n      // Invalid data\n      if (\"development\" !== 'production') {\n        logError('Invalid markLine data.');\n      }\n\n      itemArray = [];\n    }\n  } else {\n    itemArray = item;\n  }\n\n  var normalizedItem = [dataTransform(seriesModel, itemArray[0]), dataTransform(seriesModel, itemArray[1]), extend({}, itemArray[2])]; // Avoid line data type is extended by from(to) data type\n\n  normalizedItem[2].type = normalizedItem[2].type || null; // Merge from option and to option into line option\n\n  merge(normalizedItem[2], normalizedItem[0]);\n  merge(normalizedItem[2], normalizedItem[1]);\n  return normalizedItem;\n};\n\nfunction isInfinity(val) {\n  return !isNaN(val) && !isFinite(val);\n} // If a markLine has one dim\n\n\nfunction ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {\n  var otherDimIndex = 1 - dimIndex;\n  var dimName = coordSys.dimensions[dimIndex];\n  return isInfinity(fromCoord[otherDimIndex]) && isInfinity(toCoord[otherDimIndex]) && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);\n}\n\nfunction markLineFilter(coordSys, item) {\n  if (coordSys.type === 'cartesian2d') {\n    var fromCoord = item[0].coord;\n    var toCoord = item[1].coord; // In case\n    // {\n    //  markLine: {\n    //    data: [{ yAxis: 2 }]\n    //  }\n    // }\n\n    if (fromCoord && toCoord && (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))) {\n      return true;\n    }\n  }\n\n  return dataFilter$1(coordSys, item[0]) && dataFilter$1(coordSys, item[1]);\n}\n\nfunction updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api) {\n  var coordSys = seriesModel.coordinateSystem;\n  var itemModel = data.getItemModel(idx);\n  var point;\n  var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());\n  var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());\n\n  if (!isNaN(xPx) && !isNaN(yPx)) {\n    point = [xPx, yPx];\n  } else {\n    // Chart like bar may have there own marker positioning logic\n    if (seriesModel.getMarkerPosition) {\n      // Use the getMarkerPosition\n      point = seriesModel.getMarkerPosition(data.getValues(data.dimensions, idx));\n    } else {\n      var dims = coordSys.dimensions;\n      var x = data.get(dims[0], idx);\n      var y = data.get(dims[1], idx);\n      point = coordSys.dataToPoint([x, y]);\n    } // Expand line to the edge of grid if value on one axis is Inifnity\n    // In case\n    //  markLine: {\n    //    data: [{\n    //      yAxis: 2\n    //      // or\n    //      type: 'average'\n    //    }]\n    //  }\n\n\n    if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n      // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug\n      var xAxis = coordSys.getAxis('x');\n      var yAxis = coordSys.getAxis('y');\n      var dims = coordSys.dimensions;\n\n      if (isInfinity(data.get(dims[0], idx))) {\n        point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);\n      } else if (isInfinity(data.get(dims[1], idx))) {\n        point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);\n      }\n    } // Use x, y if has any\n\n\n    if (!isNaN(xPx)) {\n      point[0] = xPx;\n    }\n\n    if (!isNaN(yPx)) {\n      point[1] = yPx;\n    }\n  }\n\n  data.setItemLayout(idx, point);\n}\n\nvar MarkLineView =\n/** @class */\nfunction (_super) {\n  __extends(MarkLineView, _super);\n\n  function MarkLineView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkLineView.type;\n    return _this;\n  }\n\n  MarkLineView.prototype.updateTransform = function (markLineModel, ecModel, api) {\n    ecModel.eachSeries(function (seriesModel) {\n      var mlModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markLine');\n\n      if (mlModel) {\n        var mlData_1 = mlModel.getData();\n        var fromData_1 = inner$i(mlModel).from;\n        var toData_1 = inner$i(mlModel).to; // Update visual and layout of from symbol and to symbol\n\n        fromData_1.each(function (idx) {\n          updateSingleMarkerEndLayout(fromData_1, idx, true, seriesModel, api);\n          updateSingleMarkerEndLayout(toData_1, idx, false, seriesModel, api);\n        }); // Update layout of line\n\n        mlData_1.each(function (idx) {\n          mlData_1.setItemLayout(idx, [fromData_1.getItemLayout(idx), toData_1.getItemLayout(idx)]);\n        });\n        this.markerGroupMap.get(seriesModel.id).updateLayout();\n      }\n    }, this);\n  };\n\n  MarkLineView.prototype.renderSeries = function (seriesModel, mlModel, ecModel, api) {\n    var coordSys = seriesModel.coordinateSystem;\n    var seriesId = seriesModel.id;\n    var seriesData = seriesModel.getData();\n    var lineDrawMap = this.markerGroupMap;\n    var lineDraw = lineDrawMap.get(seriesId) || lineDrawMap.set(seriesId, new LineDraw());\n    this.group.add(lineDraw.group);\n    var mlData = createList$1(coordSys, seriesModel, mlModel);\n    var fromData = mlData.from;\n    var toData = mlData.to;\n    var lineData = mlData.line;\n    inner$i(mlModel).from = fromData;\n    inner$i(mlModel).to = toData; // Line data for tooltip and formatter\n\n    mlModel.setData(lineData); // TODO\n    // Functionally, `symbolSize` & `symbolOffset` can also be 2D array now.\n    // But the related logic and type definition are not finished yet.\n    // Finish it if required\n\n    var symbolType = mlModel.get('symbol');\n    var symbolSize = mlModel.get('symbolSize');\n    var symbolRotate = mlModel.get('symbolRotate');\n    var symbolOffset = mlModel.get('symbolOffset'); // TODO: support callback function like markPoint\n\n    if (!isArray(symbolType)) {\n      symbolType = [symbolType, symbolType];\n    }\n\n    if (!isArray(symbolSize)) {\n      symbolSize = [symbolSize, symbolSize];\n    }\n\n    if (!isArray(symbolRotate)) {\n      symbolRotate = [symbolRotate, symbolRotate];\n    }\n\n    if (!isArray(symbolOffset)) {\n      symbolOffset = [symbolOffset, symbolOffset];\n    } // Update visual and layout of from symbol and to symbol\n\n\n    mlData.from.each(function (idx) {\n      updateDataVisualAndLayout(fromData, idx, true);\n      updateDataVisualAndLayout(toData, idx, false);\n    }); // Update visual and layout of line\n\n    lineData.each(function (idx) {\n      var lineStyle = lineData.getItemModel(idx).getModel('lineStyle').getLineStyle(); // lineData.setItemVisual(idx, {\n      //     color: lineColor || fromData.getItemVisual(idx, 'color')\n      // });\n\n      lineData.setItemLayout(idx, [fromData.getItemLayout(idx), toData.getItemLayout(idx)]);\n\n      if (lineStyle.stroke == null) {\n        lineStyle.stroke = fromData.getItemVisual(idx, 'style').fill;\n      }\n\n      lineData.setItemVisual(idx, {\n        fromSymbolKeepAspect: fromData.getItemVisual(idx, 'symbolKeepAspect'),\n        fromSymbolOffset: fromData.getItemVisual(idx, 'symbolOffset'),\n        fromSymbolRotate: fromData.getItemVisual(idx, 'symbolRotate'),\n        fromSymbolSize: fromData.getItemVisual(idx, 'symbolSize'),\n        fromSymbol: fromData.getItemVisual(idx, 'symbol'),\n        toSymbolKeepAspect: toData.getItemVisual(idx, 'symbolKeepAspect'),\n        toSymbolOffset: toData.getItemVisual(idx, 'symbolOffset'),\n        toSymbolRotate: toData.getItemVisual(idx, 'symbolRotate'),\n        toSymbolSize: toData.getItemVisual(idx, 'symbolSize'),\n        toSymbol: toData.getItemVisual(idx, 'symbol'),\n        style: lineStyle\n      });\n    });\n    lineDraw.updateData(lineData); // Set host model for tooltip\n    // FIXME\n\n    mlData.line.eachItemGraphicEl(function (el) {\n      getECData(el).dataModel = mlModel;\n      el.traverse(function (child) {\n        getECData(child).dataModel = mlModel;\n      });\n    });\n\n    function updateDataVisualAndLayout(data, idx, isFrom) {\n      var itemModel = data.getItemModel(idx);\n      updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api);\n      var style = itemModel.getModel('itemStyle').getItemStyle();\n\n      if (style.fill == null) {\n        style.fill = getVisualFromData(seriesData, 'color');\n      }\n\n      data.setItemVisual(idx, {\n        symbolKeepAspect: itemModel.get('symbolKeepAspect'),\n        // `0` should be considered as a valid value, so use `retrieve2` instead of `||`\n        symbolOffset: retrieve2(itemModel.get('symbolOffset', true), symbolOffset[isFrom ? 0 : 1]),\n        symbolRotate: retrieve2(itemModel.get('symbolRotate', true), symbolRotate[isFrom ? 0 : 1]),\n        // TODO: when 2d array is supported, it should ignore parent\n        symbolSize: retrieve2(itemModel.get('symbolSize'), symbolSize[isFrom ? 0 : 1]),\n        symbol: retrieve2(itemModel.get('symbol', true), symbolType[isFrom ? 0 : 1]),\n        style: style\n      });\n    }\n\n    this.markKeep(lineDraw);\n    lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');\n  };\n\n  MarkLineView.type = 'markLine';\n  return MarkLineView;\n}(MarkerView);\n\nfunction createList$1(coordSys, seriesModel, mlModel) {\n  var coordDimsInfos;\n\n  if (coordSys) {\n    coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {\n      var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n      return extend(extend({}, info), {\n        name: coordDim,\n        // DON'T use ordinalMeta to parse and collect ordinal.\n        ordinalMeta: null\n      });\n    });\n  } else {\n    coordDimsInfos = [{\n      name: 'value',\n      type: 'float'\n    }];\n  }\n\n  var fromData = new SeriesData(coordDimsInfos, mlModel);\n  var toData = new SeriesData(coordDimsInfos, mlModel); // No dimensions\n\n  var lineData = new SeriesData([], mlModel);\n  var optData = map(mlModel.get('data'), curry(markLineTransform, seriesModel, coordSys, mlModel));\n\n  if (coordSys) {\n    optData = filter(optData, curry(markLineFilter, coordSys));\n  }\n\n  var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos);\n  fromData.initData(map(optData, function (item) {\n    return item[0];\n  }), null, dimValueGetter);\n  toData.initData(map(optData, function (item) {\n    return item[1];\n  }), null, dimValueGetter);\n  lineData.initData(map(optData, function (item) {\n    return item[2];\n  }));\n  lineData.hasItemOption = true;\n  return {\n    from: fromData,\n    to: toData,\n    line: lineData\n  };\n}\n\nfunction install$F(registers) {\n  registers.registerComponentModel(MarkLineModel);\n  registers.registerComponentView(MarkLineView);\n  registers.registerPreprocessor(function (opt) {\n    if (checkMarkerInSeries(opt.series, 'markLine')) {\n      // Make sure markLine component is enabled\n      opt.markLine = opt.markLine || {};\n    }\n  });\n}\n\nvar MarkAreaModel =\n/** @class */\nfunction (_super) {\n  __extends(MarkAreaModel, _super);\n\n  function MarkAreaModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkAreaModel.type;\n    return _this;\n  }\n\n  MarkAreaModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n    return new MarkAreaModel(markerOpt, masterMarkerModel, ecModel);\n  };\n\n  MarkAreaModel.type = 'markArea';\n  MarkAreaModel.defaultOption = {\n    // zlevel: 0,\n    // PENDING\n    z: 1,\n    tooltip: {\n      trigger: 'item'\n    },\n    // markArea should fixed on the coordinate system\n    animation: false,\n    label: {\n      show: true,\n      position: 'top'\n    },\n    itemStyle: {\n      // color and borderColor default to use color from series\n      // color: 'auto'\n      // borderColor: 'auto'\n      borderWidth: 0\n    },\n    emphasis: {\n      label: {\n        show: true,\n        position: 'top'\n      }\n    }\n  };\n  return MarkAreaModel;\n}(MarkerModel);\n\nvar inner$j = makeInner();\n\nvar markAreaTransform = function (seriesModel, coordSys, maModel, item) {\n  // item may be null\n  var item0 = item[0];\n  var item1 = item[1];\n\n  if (!item0 || !item1) {\n    return;\n  }\n\n  var lt = dataTransform(seriesModel, item0);\n  var rb = dataTransform(seriesModel, item1); // FIXME make sure lt is less than rb\n\n  var ltCoord = lt.coord;\n  var rbCoord = rb.coord;\n  ltCoord[0] = retrieve(ltCoord[0], -Infinity);\n  ltCoord[1] = retrieve(ltCoord[1], -Infinity);\n  rbCoord[0] = retrieve(rbCoord[0], Infinity);\n  rbCoord[1] = retrieve(rbCoord[1], Infinity); // Merge option into one\n\n  var result = mergeAll([{}, lt, rb]);\n  result.coord = [lt.coord, rb.coord];\n  result.x0 = lt.x;\n  result.y0 = lt.y;\n  result.x1 = rb.x;\n  result.y1 = rb.y;\n  return result;\n};\n\nfunction isInfinity$1(val) {\n  return !isNaN(val) && !isFinite(val);\n} // If a markArea has one dim\n\n\nfunction ifMarkAreaHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {\n  var otherDimIndex = 1 - dimIndex;\n  return isInfinity$1(fromCoord[otherDimIndex]) && isInfinity$1(toCoord[otherDimIndex]);\n}\n\nfunction markAreaFilter(coordSys, item) {\n  var fromCoord = item.coord[0];\n  var toCoord = item.coord[1];\n  var item0 = {\n    coord: fromCoord,\n    x: item.x0,\n    y: item.y0\n  };\n  var item1 = {\n    coord: toCoord,\n    x: item.x1,\n    y: item.y1\n  };\n\n  if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n    // In case\n    // {\n    //  markArea: {\n    //    data: [{ yAxis: 2 }]\n    //  }\n    // }\n    if (fromCoord && toCoord && (ifMarkAreaHasOnlyDim(1, fromCoord, toCoord) || ifMarkAreaHasOnlyDim(0, fromCoord, toCoord))) {\n      return true;\n    } // Directly returning true may also do the work,\n    // because markArea will not be shown automatically\n    // when it's not included in coordinate system.\n    // But filtering ahead can avoid keeping rendering markArea\n    // when there are too many of them.\n\n\n    return zoneFilter(coordSys, item0, item1);\n  }\n\n  return dataFilter$1(coordSys, item0) || dataFilter$1(coordSys, item1);\n} // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0']\n\n\nfunction getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) {\n  var coordSys = seriesModel.coordinateSystem;\n  var itemModel = data.getItemModel(idx);\n  var point;\n  var xPx = parsePercent$1(itemModel.get(dims[0]), api.getWidth());\n  var yPx = parsePercent$1(itemModel.get(dims[1]), api.getHeight());\n\n  if (!isNaN(xPx) && !isNaN(yPx)) {\n    point = [xPx, yPx];\n  } else {\n    // Chart like bar may have there own marker positioning logic\n    if (seriesModel.getMarkerPosition) {\n      // Consider the case that user input the right-bottom point first\n      // Pick the larger x and y as 'x1' and 'y1'\n      var pointValue0 = data.getValues(['x0', 'y0'], idx);\n      var pointValue1 = data.getValues(['x1', 'y1'], idx);\n      var clampPointValue0 = coordSys.clampData(pointValue0);\n      var clampPointValue1 = coordSys.clampData(pointValue1);\n      var pointValue = [];\n\n      if (dims[0] === 'x0') {\n        pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue1[0] : pointValue0[0];\n      } else {\n        pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue0[0] : pointValue1[0];\n      }\n\n      if (dims[1] === 'y0') {\n        pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue1[1] : pointValue0[1];\n      } else {\n        pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue0[1] : pointValue1[1];\n      } // Use the getMarkerPosition\n\n\n      point = seriesModel.getMarkerPosition(pointValue, dims, true);\n    } else {\n      var x = data.get(dims[0], idx);\n      var y = data.get(dims[1], idx);\n      var pt = [x, y];\n      coordSys.clampData && coordSys.clampData(pt, pt);\n      point = coordSys.dataToPoint(pt, true);\n    }\n\n    if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n      // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug\n      var xAxis = coordSys.getAxis('x');\n      var yAxis = coordSys.getAxis('y');\n      var x = data.get(dims[0], idx);\n      var y = data.get(dims[1], idx);\n\n      if (isInfinity$1(x)) {\n        point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]);\n      } else if (isInfinity$1(y)) {\n        point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]);\n      }\n    } // Use x, y if has any\n\n\n    if (!isNaN(xPx)) {\n      point[0] = xPx;\n    }\n\n    if (!isNaN(yPx)) {\n      point[1] = yPx;\n    }\n  }\n\n  return point;\n}\n\nvar dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']];\n\nvar MarkAreaView =\n/** @class */\nfunction (_super) {\n  __extends(MarkAreaView, _super);\n\n  function MarkAreaView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = MarkAreaView.type;\n    return _this;\n  }\n\n  MarkAreaView.prototype.updateTransform = function (markAreaModel, ecModel, api) {\n    ecModel.eachSeries(function (seriesModel) {\n      var maModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markArea');\n\n      if (maModel) {\n        var areaData_1 = maModel.getData();\n        areaData_1.each(function (idx) {\n          var points = map(dimPermutations, function (dim) {\n            return getSingleMarkerEndPoint(areaData_1, idx, dim, seriesModel, api);\n          }); // Layout\n\n          areaData_1.setItemLayout(idx, points);\n          var el = areaData_1.getItemGraphicEl(idx);\n          el.setShape('points', points);\n        });\n      }\n    }, this);\n  };\n\n  MarkAreaView.prototype.renderSeries = function (seriesModel, maModel, ecModel, api) {\n    var coordSys = seriesModel.coordinateSystem;\n    var seriesId = seriesModel.id;\n    var seriesData = seriesModel.getData();\n    var areaGroupMap = this.markerGroupMap;\n    var polygonGroup = areaGroupMap.get(seriesId) || areaGroupMap.set(seriesId, {\n      group: new Group()\n    });\n    this.group.add(polygonGroup.group);\n    this.markKeep(polygonGroup);\n    var areaData = createList$2(coordSys, seriesModel, maModel); // Line data for tooltip and formatter\n\n    maModel.setData(areaData); // Update visual and layout of line\n\n    areaData.each(function (idx) {\n      // Layout\n      var points = map(dimPermutations, function (dim) {\n        return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);\n      });\n      var xAxisScale = coordSys.getAxis('x').scale;\n      var yAxisScale = coordSys.getAxis('y').scale;\n      var xAxisExtent = xAxisScale.getExtent();\n      var yAxisExtent = yAxisScale.getExtent();\n      var xPointExtent = [xAxisScale.parse(areaData.get('x0', idx)), xAxisScale.parse(areaData.get('x1', idx))];\n      var yPointExtent = [yAxisScale.parse(areaData.get('y0', idx)), yAxisScale.parse(areaData.get('y1', idx))];\n      asc(xPointExtent);\n      asc(yPointExtent);\n      var overlapped = !(xAxisExtent[0] > xPointExtent[1] || xAxisExtent[1] < xPointExtent[0] || yAxisExtent[0] > yPointExtent[1] || yAxisExtent[1] < yPointExtent[0]); // If none of the area is inside coordSys, allClipped is set to be true\n      // in layout so that label will not be displayed. See #12591\n\n      var allClipped = !overlapped;\n      areaData.setItemLayout(idx, {\n        points: points,\n        allClipped: allClipped\n      });\n      var style = areaData.getItemModel(idx).getModel('itemStyle').getItemStyle();\n      var color$1 = getVisualFromData(seriesData, 'color');\n\n      if (!style.fill) {\n        style.fill = color$1;\n\n        if (isString(style.fill)) {\n          style.fill = modifyAlpha(style.fill, 0.4);\n        }\n      }\n\n      if (!style.stroke) {\n        style.stroke = color$1;\n      } // Visual\n\n\n      areaData.setItemVisual(idx, 'style', style);\n    });\n    areaData.diff(inner$j(polygonGroup).data).add(function (idx) {\n      var layout = areaData.getItemLayout(idx);\n\n      if (!layout.allClipped) {\n        var polygon = new Polygon({\n          shape: {\n            points: layout.points\n          }\n        });\n        areaData.setItemGraphicEl(idx, polygon);\n        polygonGroup.group.add(polygon);\n      }\n    }).update(function (newIdx, oldIdx) {\n      var polygon = inner$j(polygonGroup).data.getItemGraphicEl(oldIdx);\n      var layout = areaData.getItemLayout(newIdx);\n\n      if (!layout.allClipped) {\n        if (polygon) {\n          updateProps(polygon, {\n            shape: {\n              points: layout.points\n            }\n          }, maModel, newIdx);\n        } else {\n          polygon = new Polygon({\n            shape: {\n              points: layout.points\n            }\n          });\n        }\n\n        areaData.setItemGraphicEl(newIdx, polygon);\n        polygonGroup.group.add(polygon);\n      } else if (polygon) {\n        polygonGroup.group.remove(polygon);\n      }\n    }).remove(function (idx) {\n      var polygon = inner$j(polygonGroup).data.getItemGraphicEl(idx);\n      polygonGroup.group.remove(polygon);\n    }).execute();\n    areaData.eachItemGraphicEl(function (polygon, idx) {\n      var itemModel = areaData.getItemModel(idx);\n      var style = areaData.getItemVisual(idx, 'style');\n      polygon.useStyle(areaData.getItemVisual(idx, 'style'));\n      setLabelStyle(polygon, getLabelStatesModels(itemModel), {\n        labelFetcher: maModel,\n        labelDataIndex: idx,\n        defaultText: areaData.getName(idx) || '',\n        inheritColor: isString(style.fill) ? modifyAlpha(style.fill, 1) : '#000'\n      });\n      setStatesStylesFromModel(polygon, itemModel);\n      toggleHoverEmphasis(polygon, null, null, itemModel.get(['emphasis', 'disabled']));\n      getECData(polygon).dataModel = maModel;\n    });\n    inner$j(polygonGroup).data = areaData;\n    polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent');\n  };\n\n  MarkAreaView.type = 'markArea';\n  return MarkAreaView;\n}(MarkerView);\n\nfunction createList$2(coordSys, seriesModel, maModel) {\n  var areaData;\n  var dataDims;\n  var dims = ['x0', 'y0', 'x1', 'y1'];\n\n  if (coordSys) {\n    var coordDimsInfos_1 = map(coordSys && coordSys.dimensions, function (coordDim) {\n      var data = seriesModel.getData();\n      var info = data.getDimensionInfo(data.mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n      return extend(extend({}, info), {\n        name: coordDim,\n        // DON'T use ordinalMeta to parse and collect ordinal.\n        ordinalMeta: null\n      });\n    });\n    dataDims = map(dims, function (dim, idx) {\n      return {\n        name: dim,\n        type: coordDimsInfos_1[idx % 2].type\n      };\n    });\n    areaData = new SeriesData(dataDims, maModel);\n  } else {\n    dataDims = [{\n      name: 'value',\n      type: 'float'\n    }];\n    areaData = new SeriesData(dataDims, maModel);\n  }\n\n  var optData = map(maModel.get('data'), curry(markAreaTransform, seriesModel, coordSys, maModel));\n\n  if (coordSys) {\n    optData = filter(optData, curry(markAreaFilter, coordSys));\n  }\n\n  var dimValueGetter = coordSys ? function (item, dimName, dataIndex, dimIndex) {\n    // TODO should convert to ParsedValue?\n    var rawVal = item.coord[Math.floor(dimIndex / 2)][dimIndex % 2];\n    return parseDataValue(rawVal, dataDims[dimIndex]);\n  } : function (item, dimName, dataIndex, dimIndex) {\n    return parseDataValue(item.value, dataDims[dimIndex]);\n  };\n  areaData.initData(optData, null, dimValueGetter);\n  areaData.hasItemOption = true;\n  return areaData;\n}\n\nfunction install$G(registers) {\n  registers.registerComponentModel(MarkAreaModel);\n  registers.registerComponentView(MarkAreaView);\n  registers.registerPreprocessor(function (opt) {\n    if (checkMarkerInSeries(opt.series, 'markArea')) {\n      // Make sure markArea component is enabled\n      opt.markArea = opt.markArea || {};\n    }\n  });\n}\n\nvar getDefaultSelectorOptions = function (ecModel, type) {\n  if (type === 'all') {\n    return {\n      type: 'all',\n      title: ecModel.getLocaleModel().get(['legend', 'selector', 'all'])\n    };\n  } else if (type === 'inverse') {\n    return {\n      type: 'inverse',\n      title: ecModel.getLocaleModel().get(['legend', 'selector', 'inverse'])\n    };\n  }\n};\n\nvar LegendModel =\n/** @class */\nfunction (_super) {\n  __extends(LegendModel, _super);\n\n  function LegendModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = LegendModel.type;\n    _this.layoutMode = {\n      type: 'box',\n      // legend.width/height are maxWidth/maxHeight actually,\n      // whereas real width/height is calculated by its content.\n      // (Setting {left: 10, right: 10} does not make sense).\n      // So consider the case:\n      // `setOption({legend: {left: 10});`\n      // then `setOption({legend: {right: 10});`\n      // The previous `left` should be cleared by setting `ignoreSize`.\n      ignoreSize: true\n    };\n    return _this;\n  }\n\n  LegendModel.prototype.init = function (option, parentModel, ecModel) {\n    this.mergeDefaultAndTheme(option, ecModel);\n    option.selected = option.selected || {};\n\n    this._updateSelector(option);\n  };\n\n  LegendModel.prototype.mergeOption = function (option, ecModel) {\n    _super.prototype.mergeOption.call(this, option, ecModel);\n\n    this._updateSelector(option);\n  };\n\n  LegendModel.prototype._updateSelector = function (option) {\n    var selector = option.selector;\n    var ecModel = this.ecModel;\n\n    if (selector === true) {\n      selector = option.selector = ['all', 'inverse'];\n    }\n\n    if (isArray(selector)) {\n      each(selector, function (item, index) {\n        isString(item) && (item = {\n          type: item\n        });\n        selector[index] = merge(item, getDefaultSelectorOptions(ecModel, item.type));\n      });\n    }\n  };\n\n  LegendModel.prototype.optionUpdated = function () {\n    this._updateData(this.ecModel);\n\n    var legendData = this._data; // If selectedMode is single, try to select one\n\n    if (legendData[0] && this.get('selectedMode') === 'single') {\n      var hasSelected = false; // If has any selected in option.selected\n\n      for (var i = 0; i < legendData.length; i++) {\n        var name_1 = legendData[i].get('name');\n\n        if (this.isSelected(name_1)) {\n          // Force to unselect others\n          this.select(name_1);\n          hasSelected = true;\n          break;\n        }\n      } // Try select the first if selectedMode is single\n\n\n      !hasSelected && this.select(legendData[0].get('name'));\n    }\n  };\n\n  LegendModel.prototype._updateData = function (ecModel) {\n    var potentialData = [];\n    var availableNames = [];\n    ecModel.eachRawSeries(function (seriesModel) {\n      var seriesName = seriesModel.name;\n      availableNames.push(seriesName);\n      var isPotential;\n\n      if (seriesModel.legendVisualProvider) {\n        var provider = seriesModel.legendVisualProvider;\n        var names = provider.getAllNames();\n\n        if (!ecModel.isSeriesFiltered(seriesModel)) {\n          availableNames = availableNames.concat(names);\n        }\n\n        if (names.length) {\n          potentialData = potentialData.concat(names);\n        } else {\n          isPotential = true;\n        }\n      } else {\n        isPotential = true;\n      }\n\n      if (isPotential && isNameSpecified(seriesModel)) {\n        potentialData.push(seriesModel.name);\n      }\n    });\n    /**\n     * @type {Array.<string>}\n     * @private\n     */\n\n    this._availableNames = availableNames; // If legend.data is not specified in option, use availableNames as data,\n    // which is convenient for user preparing option.\n\n    var rawData = this.get('data') || potentialData;\n    var legendNameMap = createHashMap();\n    var legendData = map(rawData, function (dataItem) {\n      // Can be string or number\n      if (isString(dataItem) || isNumber(dataItem)) {\n        dataItem = {\n          name: dataItem\n        };\n      }\n\n      if (legendNameMap.get(dataItem.name)) {\n        // remove legend name duplicate\n        return null;\n      }\n\n      legendNameMap.set(dataItem.name, true);\n      return new Model(dataItem, this, this.ecModel);\n    }, this);\n    /**\n     * @type {Array.<module:echarts/model/Model>}\n     * @private\n     */\n\n    this._data = filter(legendData, function (item) {\n      return !!item;\n    });\n  };\n\n  LegendModel.prototype.getData = function () {\n    return this._data;\n  };\n\n  LegendModel.prototype.select = function (name) {\n    var selected = this.option.selected;\n    var selectedMode = this.get('selectedMode');\n\n    if (selectedMode === 'single') {\n      var data = this._data;\n      each(data, function (dataItem) {\n        selected[dataItem.get('name')] = false;\n      });\n    }\n\n    selected[name] = true;\n  };\n\n  LegendModel.prototype.unSelect = function (name) {\n    if (this.get('selectedMode') !== 'single') {\n      this.option.selected[name] = false;\n    }\n  };\n\n  LegendModel.prototype.toggleSelected = function (name) {\n    var selected = this.option.selected; // Default is true\n\n    if (!selected.hasOwnProperty(name)) {\n      selected[name] = true;\n    }\n\n    this[selected[name] ? 'unSelect' : 'select'](name);\n  };\n\n  LegendModel.prototype.allSelect = function () {\n    var data = this._data;\n    var selected = this.option.selected;\n    each(data, function (dataItem) {\n      selected[dataItem.get('name', true)] = true;\n    });\n  };\n\n  LegendModel.prototype.inverseSelect = function () {\n    var data = this._data;\n    var selected = this.option.selected;\n    each(data, function (dataItem) {\n      var name = dataItem.get('name', true); // Initially, default value is true\n\n      if (!selected.hasOwnProperty(name)) {\n        selected[name] = true;\n      }\n\n      selected[name] = !selected[name];\n    });\n  };\n\n  LegendModel.prototype.isSelected = function (name) {\n    var selected = this.option.selected;\n    return !(selected.hasOwnProperty(name) && !selected[name]) && indexOf(this._availableNames, name) >= 0;\n  };\n\n  LegendModel.prototype.getOrient = function () {\n    return this.get('orient') === 'vertical' ? {\n      index: 1,\n      name: 'vertical'\n    } : {\n      index: 0,\n      name: 'horizontal'\n    };\n  };\n\n  LegendModel.type = 'legend.plain';\n  LegendModel.dependencies = ['series'];\n  LegendModel.defaultOption = {\n    // zlevel: 0,\n    z: 4,\n    show: true,\n    orient: 'horizontal',\n    left: 'center',\n    // right: 'center',\n    top: 0,\n    // bottom: null,\n    align: 'auto',\n    backgroundColor: 'rgba(0,0,0,0)',\n    borderColor: '#ccc',\n    borderRadius: 0,\n    borderWidth: 0,\n    padding: 5,\n    itemGap: 10,\n    itemWidth: 25,\n    itemHeight: 14,\n    symbolRotate: 'inherit',\n    symbolKeepAspect: true,\n    inactiveColor: '#ccc',\n    inactiveBorderColor: '#ccc',\n    inactiveBorderWidth: 'auto',\n    itemStyle: {\n      color: 'inherit',\n      opacity: 'inherit',\n      borderColor: 'inherit',\n      borderWidth: 'auto',\n      borderCap: 'inherit',\n      borderJoin: 'inherit',\n      borderDashOffset: 'inherit',\n      borderMiterLimit: 'inherit'\n    },\n    lineStyle: {\n      width: 'auto',\n      color: 'inherit',\n      inactiveColor: '#ccc',\n      inactiveWidth: 2,\n      opacity: 'inherit',\n      type: 'inherit',\n      cap: 'inherit',\n      join: 'inherit',\n      dashOffset: 'inherit',\n      miterLimit: 'inherit'\n    },\n    textStyle: {\n      color: '#333'\n    },\n    selectedMode: true,\n    selector: false,\n    selectorLabel: {\n      show: true,\n      borderRadius: 10,\n      padding: [3, 5, 3, 5],\n      fontSize: 12,\n      fontFamily: 'sans-serif',\n      color: '#666',\n      borderWidth: 1,\n      borderColor: '#666'\n    },\n    emphasis: {\n      selectorLabel: {\n        show: true,\n        color: '#eee',\n        backgroundColor: '#666'\n      }\n    },\n    selectorPosition: 'auto',\n    selectorItemGap: 7,\n    selectorButtonGap: 10,\n    tooltip: {\n      show: false\n    }\n  };\n  return LegendModel;\n}(ComponentModel);\n\nvar curry$1 = curry;\nvar each$c = each;\nvar Group$2 = Group;\n\nvar LegendView =\n/** @class */\nfunction (_super) {\n  __extends(LegendView, _super);\n\n  function LegendView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = LegendView.type;\n    _this.newlineDisabled = false;\n    return _this;\n  }\n\n  LegendView.prototype.init = function () {\n    this.group.add(this._contentGroup = new Group$2());\n    this.group.add(this._selectorGroup = new Group$2());\n    this._isFirstRender = true;\n  };\n  /**\n   * @protected\n   */\n\n\n  LegendView.prototype.getContentGroup = function () {\n    return this._contentGroup;\n  };\n  /**\n   * @protected\n   */\n\n\n  LegendView.prototype.getSelectorGroup = function () {\n    return this._selectorGroup;\n  };\n  /**\n   * @override\n   */\n\n\n  LegendView.prototype.render = function (legendModel, ecModel, api) {\n    var isFirstRender = this._isFirstRender;\n    this._isFirstRender = false;\n    this.resetInner();\n\n    if (!legendModel.get('show', true)) {\n      return;\n    }\n\n    var itemAlign = legendModel.get('align');\n    var orient = legendModel.get('orient');\n\n    if (!itemAlign || itemAlign === 'auto') {\n      itemAlign = legendModel.get('left') === 'right' && orient === 'vertical' ? 'right' : 'left';\n    } // selector has been normalized to an array in model\n\n\n    var selector = legendModel.get('selector', true);\n    var selectorPosition = legendModel.get('selectorPosition', true);\n\n    if (selector && (!selectorPosition || selectorPosition === 'auto')) {\n      selectorPosition = orient === 'horizontal' ? 'end' : 'start';\n    }\n\n    this.renderInner(itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition); // Perform layout.\n\n    var positionInfo = legendModel.getBoxLayoutParams();\n    var viewportSize = {\n      width: api.getWidth(),\n      height: api.getHeight()\n    };\n    var padding = legendModel.get('padding');\n    var maxSize = getLayoutRect(positionInfo, viewportSize, padding);\n    var mainRect = this.layoutInner(legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition); // Place mainGroup, based on the calculated `mainRect`.\n\n    var layoutRect = getLayoutRect(defaults({\n      width: mainRect.width,\n      height: mainRect.height\n    }, positionInfo), viewportSize, padding);\n    this.group.x = layoutRect.x - mainRect.x;\n    this.group.y = layoutRect.y - mainRect.y;\n    this.group.markRedraw(); // Render background after group is layout.\n\n    this.group.add(this._backgroundEl = makeBackground(mainRect, legendModel));\n  };\n\n  LegendView.prototype.resetInner = function () {\n    this.getContentGroup().removeAll();\n    this._backgroundEl && this.group.remove(this._backgroundEl);\n    this.getSelectorGroup().removeAll();\n  };\n\n  LegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {\n    var contentGroup = this.getContentGroup();\n    var legendDrawnMap = createHashMap();\n    var selectMode = legendModel.get('selectedMode');\n    var excludeSeriesId = [];\n    ecModel.eachRawSeries(function (seriesModel) {\n      !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id);\n    });\n    each$c(legendModel.getData(), function (legendItemModel, dataIndex) {\n      var name = legendItemModel.get('name'); // Use empty string or \\n as a newline string\n\n      if (!this.newlineDisabled && (name === '' || name === '\\n')) {\n        var g = new Group$2(); // @ts-ignore\n\n        g.newline = true;\n        contentGroup.add(g);\n        return;\n      } // Representitive series.\n\n\n      var seriesModel = ecModel.getSeriesByName(name)[0];\n\n      if (legendDrawnMap.get(name)) {\n        // Have been drawn\n        return;\n      } // Legend to control series.\n\n\n      if (seriesModel) {\n        var data = seriesModel.getData();\n        var lineVisualStyle = data.getVisual('legendLineStyle') || {};\n        var legendIcon = data.getVisual('legendIcon');\n        /**\n         * `data.getVisual('style')` may be the color from the register\n         * in series. For example, for line series,\n         */\n\n        var style = data.getVisual('style');\n\n        var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, style, legendIcon, selectMode, api);\n\n        itemGroup.on('click', curry$1(dispatchSelectAction, name, null, api, excludeSeriesId)).on('mouseover', curry$1(dispatchHighlightAction, seriesModel.name, null, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, seriesModel.name, null, api, excludeSeriesId));\n        legendDrawnMap.set(name, true);\n      } else {\n        // Legend to control data. In pie and funnel.\n        ecModel.eachRawSeries(function (seriesModel) {\n          // In case multiple series has same data name\n          if (legendDrawnMap.get(name)) {\n            return;\n          }\n\n          if (seriesModel.legendVisualProvider) {\n            var provider = seriesModel.legendVisualProvider;\n\n            if (!provider.containName(name)) {\n              return;\n            }\n\n            var idx = provider.indexOfName(name);\n            var style = provider.getItemVisual(idx, 'style');\n            var legendIcon = provider.getItemVisual(idx, 'legendIcon');\n            var colorArr = parse(style.fill); // Color may be set to transparent in visualMap when data is out of range.\n            // Do not show nothing.\n\n            if (colorArr && colorArr[3] === 0) {\n              colorArr[3] = 0.2; // TODO color is set to 0, 0, 0, 0. Should show correct RGBA\n\n              style = extend(extend({}, style), {\n                fill: stringify(colorArr, 'rgba')\n              });\n            }\n\n            var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, {}, style, legendIcon, selectMode, api); // FIXME: consider different series has items with the same name.\n\n\n            itemGroup.on('click', curry$1(dispatchSelectAction, null, name, api, excludeSeriesId)) // Should not specify the series name, consider legend controls\n            // more than one pie series.\n            .on('mouseover', curry$1(dispatchHighlightAction, null, name, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, null, name, api, excludeSeriesId));\n            legendDrawnMap.set(name, true);\n          }\n        }, this);\n      }\n\n      if (\"development\" !== 'production') {\n        if (!legendDrawnMap.get(name)) {\n          console.warn(name + ' series not exists. Legend data should be same with series name or data name.');\n        }\n      }\n    }, this);\n\n    if (selector) {\n      this._createSelector(selector, legendModel, api, orient, selectorPosition);\n    }\n  };\n\n  LegendView.prototype._createSelector = function (selector, legendModel, api, orient, selectorPosition) {\n    var selectorGroup = this.getSelectorGroup();\n    each$c(selector, function createSelectorButton(selectorItem) {\n      var type = selectorItem.type;\n      var labelText = new ZRText({\n        style: {\n          x: 0,\n          y: 0,\n          align: 'center',\n          verticalAlign: 'middle'\n        },\n        onclick: function () {\n          api.dispatchAction({\n            type: type === 'all' ? 'legendAllSelect' : 'legendInverseSelect'\n          });\n        }\n      });\n      selectorGroup.add(labelText);\n      var labelModel = legendModel.getModel('selectorLabel');\n      var emphasisLabelModel = legendModel.getModel(['emphasis', 'selectorLabel']);\n      setLabelStyle(labelText, {\n        normal: labelModel,\n        emphasis: emphasisLabelModel\n      }, {\n        defaultText: selectorItem.title\n      });\n      enableHoverEmphasis(labelText);\n    });\n  };\n\n  LegendView.prototype._createItem = function (seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, itemVisualStyle, legendIcon, selectMode, api) {\n    var drawType = seriesModel.visualDrawType;\n    var itemWidth = legendModel.get('itemWidth');\n    var itemHeight = legendModel.get('itemHeight');\n    var isSelected = legendModel.isSelected(name);\n    var iconRotate = legendItemModel.get('symbolRotate');\n    var symbolKeepAspect = legendItemModel.get('symbolKeepAspect');\n    var legendIconType = legendItemModel.get('icon');\n    legendIcon = legendIconType || legendIcon || 'roundRect';\n    var style = getLegendStyle(legendIcon, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api);\n    var itemGroup = new Group$2();\n    var textStyleModel = legendItemModel.getModel('textStyle');\n\n    if (isFunction(seriesModel.getLegendIcon) && (!legendIconType || legendIconType === 'inherit')) {\n      // Series has specific way to define legend icon\n      itemGroup.add(seriesModel.getLegendIcon({\n        itemWidth: itemWidth,\n        itemHeight: itemHeight,\n        icon: legendIcon,\n        iconRotate: iconRotate,\n        itemStyle: style.itemStyle,\n        lineStyle: style.lineStyle,\n        symbolKeepAspect: symbolKeepAspect\n      }));\n    } else {\n      // Use default legend icon policy for most series\n      var rotate = legendIconType === 'inherit' && seriesModel.getData().getVisual('symbol') ? iconRotate === 'inherit' ? seriesModel.getData().getVisual('symbolRotate') : iconRotate : 0; // No rotation for no icon\n\n      itemGroup.add(getDefaultLegendIcon({\n        itemWidth: itemWidth,\n        itemHeight: itemHeight,\n        icon: legendIcon,\n        iconRotate: rotate,\n        itemStyle: style.itemStyle,\n        lineStyle: style.lineStyle,\n        symbolKeepAspect: symbolKeepAspect\n      }));\n    }\n\n    var textX = itemAlign === 'left' ? itemWidth + 5 : -5;\n    var textAlign = itemAlign;\n    var formatter = legendModel.get('formatter');\n    var content = name;\n\n    if (isString(formatter) && formatter) {\n      content = formatter.replace('{name}', name != null ? name : '');\n    } else if (isFunction(formatter)) {\n      content = formatter(name);\n    }\n\n    var inactiveColor = legendItemModel.get('inactiveColor');\n    itemGroup.add(new ZRText({\n      style: createTextStyle(textStyleModel, {\n        text: content,\n        x: textX,\n        y: itemHeight / 2,\n        fill: isSelected ? textStyleModel.getTextColor() : inactiveColor,\n        align: textAlign,\n        verticalAlign: 'middle'\n      })\n    })); // Add a invisible rect to increase the area of mouse hover\n\n    var hitRect = new Rect({\n      shape: itemGroup.getBoundingRect(),\n      invisible: true\n    });\n    var tooltipModel = legendItemModel.getModel('tooltip');\n\n    if (tooltipModel.get('show')) {\n      setTooltipConfig({\n        el: hitRect,\n        componentModel: legendModel,\n        itemName: name,\n        itemTooltipOption: tooltipModel.option\n      });\n    }\n\n    itemGroup.add(hitRect);\n    itemGroup.eachChild(function (child) {\n      child.silent = true;\n    });\n    hitRect.silent = !selectMode;\n    this.getContentGroup().add(itemGroup);\n    enableHoverEmphasis(itemGroup); // @ts-ignore\n\n    itemGroup.__legendDataIndex = dataIndex;\n    return itemGroup;\n  };\n\n  LegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {\n    var contentGroup = this.getContentGroup();\n    var selectorGroup = this.getSelectorGroup(); // Place items in contentGroup.\n\n    box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), maxSize.width, maxSize.height);\n    var contentRect = contentGroup.getBoundingRect();\n    var contentPos = [-contentRect.x, -contentRect.y];\n    selectorGroup.markRedraw();\n    contentGroup.markRedraw();\n\n    if (selector) {\n      // Place buttons in selectorGroup\n      box( // Buttons in selectorGroup always layout horizontally\n      'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));\n      var selectorRect = selectorGroup.getBoundingRect();\n      var selectorPos = [-selectorRect.x, -selectorRect.y];\n      var selectorButtonGap = legendModel.get('selectorButtonGap', true);\n      var orientIdx = legendModel.getOrient().index;\n      var wh = orientIdx === 0 ? 'width' : 'height';\n      var hw = orientIdx === 0 ? 'height' : 'width';\n      var yx = orientIdx === 0 ? 'y' : 'x';\n\n      if (selectorPosition === 'end') {\n        selectorPos[orientIdx] += contentRect[wh] + selectorButtonGap;\n      } else {\n        contentPos[orientIdx] += selectorRect[wh] + selectorButtonGap;\n      } // Always align selector to content as 'middle'\n\n\n      selectorPos[1 - orientIdx] += contentRect[hw] / 2 - selectorRect[hw] / 2;\n      selectorGroup.x = selectorPos[0];\n      selectorGroup.y = selectorPos[1];\n      contentGroup.x = contentPos[0];\n      contentGroup.y = contentPos[1];\n      var mainRect = {\n        x: 0,\n        y: 0\n      };\n      mainRect[wh] = contentRect[wh] + selectorButtonGap + selectorRect[wh];\n      mainRect[hw] = Math.max(contentRect[hw], selectorRect[hw]);\n      mainRect[yx] = Math.min(0, selectorRect[yx] + selectorPos[1 - orientIdx]);\n      return mainRect;\n    } else {\n      contentGroup.x = contentPos[0];\n      contentGroup.y = contentPos[1];\n      return this.group.getBoundingRect();\n    }\n  };\n  /**\n   * @protected\n   */\n\n\n  LegendView.prototype.remove = function () {\n    this.getContentGroup().removeAll();\n    this._isFirstRender = true;\n  };\n\n  LegendView.type = 'legend.plain';\n  return LegendView;\n}(ComponentView);\n\nfunction getLegendStyle(iconType, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api) {\n  /**\n   * Use series style if is inherit;\n   * elsewise, use legend style\n   */\n  function handleCommonProps(style, visualStyle) {\n    // If lineStyle.width is 'auto', it is set to be 2 if series has border\n    if (style.lineWidth === 'auto') {\n      style.lineWidth = visualStyle.lineWidth > 0 ? 2 : 0;\n    }\n\n    each$c(style, function (propVal, propName) {\n      style[propName] === 'inherit' && (style[propName] = visualStyle[propName]);\n    });\n  } // itemStyle\n\n\n  var itemStyleModel = legendItemModel.getModel('itemStyle');\n  var itemStyle = itemStyleModel.getItemStyle();\n  var iconBrushType = iconType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke';\n  var decalStyle = itemStyleModel.getShallow('decal');\n  itemStyle.decal = !decalStyle || decalStyle === 'inherit' ? itemVisualStyle.decal : createOrUpdatePatternFromDecal(decalStyle, api);\n\n  if (itemStyle.fill === 'inherit') {\n    /**\n     * Series with visualDrawType as 'stroke' should have\n     * series stroke as legend fill\n     */\n    itemStyle.fill = itemVisualStyle[drawType];\n  }\n\n  if (itemStyle.stroke === 'inherit') {\n    /**\n     * icon type with \"emptyXXX\" should use fill color\n     * in visual style\n     */\n    itemStyle.stroke = itemVisualStyle[iconBrushType];\n  }\n\n  if (itemStyle.opacity === 'inherit') {\n    /**\n     * Use lineStyle.opacity if drawType is stroke\n     */\n    itemStyle.opacity = (drawType === 'fill' ? itemVisualStyle : lineVisualStyle).opacity;\n  }\n\n  handleCommonProps(itemStyle, itemVisualStyle); // lineStyle\n\n  var legendLineModel = legendItemModel.getModel('lineStyle');\n  var lineStyle = legendLineModel.getLineStyle();\n  handleCommonProps(lineStyle, lineVisualStyle); // Fix auto color to real color\n\n  itemStyle.fill === 'auto' && (itemStyle.fill = itemVisualStyle.fill);\n  itemStyle.stroke === 'auto' && (itemStyle.stroke = itemVisualStyle.fill);\n  lineStyle.stroke === 'auto' && (lineStyle.stroke = itemVisualStyle.fill);\n\n  if (!isSelected) {\n    var borderWidth = legendItemModel.get('inactiveBorderWidth');\n    /**\n     * Since stroke is set to be inactiveBorderColor, it may occur that\n     * there is no border in series but border in legend, so we need to\n     * use border only when series has border if is set to be auto\n     */\n\n    var visualHasBorder = itemStyle[iconBrushType];\n    itemStyle.lineWidth = borderWidth === 'auto' ? itemVisualStyle.lineWidth > 0 && visualHasBorder ? 2 : 0 : itemStyle.lineWidth;\n    itemStyle.fill = legendItemModel.get('inactiveColor');\n    itemStyle.stroke = legendItemModel.get('inactiveBorderColor');\n    lineStyle.stroke = legendLineModel.get('inactiveColor');\n    lineStyle.lineWidth = legendLineModel.get('inactiveWidth');\n  }\n\n  return {\n    itemStyle: itemStyle,\n    lineStyle: lineStyle\n  };\n}\n\nfunction getDefaultLegendIcon(opt) {\n  var symboType = opt.icon || 'roundRect';\n  var icon = createSymbol(symboType, 0, 0, opt.itemWidth, opt.itemHeight, opt.itemStyle.fill, opt.symbolKeepAspect);\n  icon.setStyle(opt.itemStyle);\n  icon.rotation = (opt.iconRotate || 0) * Math.PI / 180;\n  icon.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);\n\n  if (symboType.indexOf('empty') > -1) {\n    icon.style.stroke = icon.style.fill;\n    icon.style.fill = '#fff';\n    icon.style.lineWidth = 2;\n  }\n\n  return icon;\n}\n\nfunction dispatchSelectAction(seriesName, dataName, api, excludeSeriesId) {\n  // downplay before unselect\n  dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId);\n  api.dispatchAction({\n    type: 'legendToggleSelect',\n    name: seriesName != null ? seriesName : dataName\n  }); // highlight after select\n  // TODO highlight immediately may cause animation loss.\n\n  dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId);\n}\n\nfunction isUseHoverLayer(api) {\n  var list = api.getZr().storage.getDisplayList();\n  var emphasisState;\n  var i = 0;\n  var len = list.length;\n\n  while (i < len && !(emphasisState = list[i].states.emphasis)) {\n    i++;\n  }\n\n  return emphasisState && emphasisState.hoverLayer;\n}\n\nfunction dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId) {\n  // If element hover will move to a hoverLayer.\n  if (!isUseHoverLayer(api)) {\n    api.dispatchAction({\n      type: 'highlight',\n      seriesName: seriesName,\n      name: dataName,\n      excludeSeriesId: excludeSeriesId\n    });\n  }\n}\n\nfunction dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId) {\n  // If element hover will move to a hoverLayer.\n  if (!isUseHoverLayer(api)) {\n    api.dispatchAction({\n      type: 'downplay',\n      seriesName: seriesName,\n      name: dataName,\n      excludeSeriesId: excludeSeriesId\n    });\n  }\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction legendFilter(ecModel) {\n  var legendModels = ecModel.findComponents({\n    mainType: 'legend'\n  });\n\n  if (legendModels && legendModels.length) {\n    ecModel.filterSeries(function (series) {\n      // If in any legend component the status is not selected.\n      // Because in legend series is assumed selected when it is not in the legend data.\n      for (var i = 0; i < legendModels.length; i++) {\n        if (!legendModels[i].isSelected(series.name)) {\n          return false;\n        }\n      }\n\n      return true;\n    });\n  }\n}\n\nfunction legendSelectActionHandler(methodName, payload, ecModel) {\n  var selectedMap = {};\n  var isToggleSelect = methodName === 'toggleSelected';\n  var isSelected; // Update all legend components\n\n  ecModel.eachComponent('legend', function (legendModel) {\n    if (isToggleSelect && isSelected != null) {\n      // Force other legend has same selected status\n      // Or the first is toggled to true and other are toggled to false\n      // In the case one legend has some item unSelected in option. And if other legend\n      // doesn't has the item, they will assume it is selected.\n      legendModel[isSelected ? 'select' : 'unSelect'](payload.name);\n    } else if (methodName === 'allSelect' || methodName === 'inverseSelect') {\n      legendModel[methodName]();\n    } else {\n      legendModel[methodName](payload.name);\n      isSelected = legendModel.isSelected(payload.name);\n    }\n\n    var legendData = legendModel.getData();\n    each(legendData, function (model) {\n      var name = model.get('name'); // Wrap element\n\n      if (name === '\\n' || name === '') {\n        return;\n      }\n\n      var isItemSelected = legendModel.isSelected(name);\n\n      if (selectedMap.hasOwnProperty(name)) {\n        // Unselected if any legend is unselected\n        selectedMap[name] = selectedMap[name] && isItemSelected;\n      } else {\n        selectedMap[name] = isItemSelected;\n      }\n    });\n  }); // Return the event explicitly\n\n  return methodName === 'allSelect' || methodName === 'inverseSelect' ? {\n    selected: selectedMap\n  } : {\n    name: payload.name,\n    selected: selectedMap\n  };\n}\n\nfunction installLegendAction(registers) {\n  /**\n   * @event legendToggleSelect\n   * @type {Object}\n   * @property {string} type 'legendToggleSelect'\n   * @property {string} [from]\n   * @property {string} name Series name or data item name\n   */\n  registers.registerAction('legendToggleSelect', 'legendselectchanged', curry(legendSelectActionHandler, 'toggleSelected'));\n  registers.registerAction('legendAllSelect', 'legendselectall', curry(legendSelectActionHandler, 'allSelect'));\n  registers.registerAction('legendInverseSelect', 'legendinverseselect', curry(legendSelectActionHandler, 'inverseSelect'));\n  /**\n   * @event legendSelect\n   * @type {Object}\n   * @property {string} type 'legendSelect'\n   * @property {string} name Series name or data item name\n   */\n\n  registers.registerAction('legendSelect', 'legendselected', curry(legendSelectActionHandler, 'select'));\n  /**\n   * @event legendUnSelect\n   * @type {Object}\n   * @property {string} type 'legendUnSelect'\n   * @property {string} name Series name or data item name\n   */\n\n  registers.registerAction('legendUnSelect', 'legendunselected', curry(legendSelectActionHandler, 'unSelect'));\n}\n\nfunction install$H(registers) {\n  registers.registerComponentModel(LegendModel);\n  registers.registerComponentView(LegendView);\n  registers.registerProcessor(registers.PRIORITY.PROCESSOR.SERIES_FILTER, legendFilter);\n  registers.registerSubTypeDefaulter('legend', function () {\n    return 'plain';\n  });\n  installLegendAction(registers);\n}\n\nvar ScrollableLegendModel =\n/** @class */\nfunction (_super) {\n  __extends(ScrollableLegendModel, _super);\n\n  function ScrollableLegendModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ScrollableLegendModel.type;\n    return _this;\n  }\n  /**\n   * @param {number} scrollDataIndex\n   */\n\n\n  ScrollableLegendModel.prototype.setScrollDataIndex = function (scrollDataIndex) {\n    this.option.scrollDataIndex = scrollDataIndex;\n  };\n\n  ScrollableLegendModel.prototype.init = function (option, parentModel, ecModel) {\n    var inputPositionParams = getLayoutParams(option);\n\n    _super.prototype.init.call(this, option, parentModel, ecModel);\n\n    mergeAndNormalizeLayoutParams$1(this, option, inputPositionParams);\n  };\n  /**\n   * @override\n   */\n\n\n  ScrollableLegendModel.prototype.mergeOption = function (option, ecModel) {\n    _super.prototype.mergeOption.call(this, option, ecModel);\n\n    mergeAndNormalizeLayoutParams$1(this, this.option, option);\n  };\n\n  ScrollableLegendModel.type = 'legend.scroll';\n  ScrollableLegendModel.defaultOption = inheritDefaultOption(LegendModel.defaultOption, {\n    scrollDataIndex: 0,\n    pageButtonItemGap: 5,\n    pageButtonGap: null,\n    pageButtonPosition: 'end',\n    pageFormatter: '{current}/{total}',\n    pageIcons: {\n      horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'],\n      vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z']\n    },\n    pageIconColor: '#2f4554',\n    pageIconInactiveColor: '#aaa',\n    pageIconSize: 15,\n    pageTextStyle: {\n      color: '#333'\n    },\n    animationDurationUpdate: 800\n  });\n  return ScrollableLegendModel;\n}(LegendModel);\n\nfunction mergeAndNormalizeLayoutParams$1(legendModel, target, raw) {\n  var orient = legendModel.getOrient();\n  var ignoreSize = [1, 1];\n  ignoreSize[orient.index] = 0;\n  mergeLayoutParam(target, raw, {\n    type: 'box',\n    ignoreSize: !!ignoreSize\n  });\n}\n\nvar Group$3 = Group;\nvar WH$1 = ['width', 'height'];\nvar XY$1 = ['x', 'y'];\n\nvar ScrollableLegendView =\n/** @class */\nfunction (_super) {\n  __extends(ScrollableLegendView, _super);\n\n  function ScrollableLegendView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ScrollableLegendView.type;\n    _this.newlineDisabled = true;\n    _this._currentIndex = 0;\n    return _this;\n  }\n\n  ScrollableLegendView.prototype.init = function () {\n    _super.prototype.init.call(this);\n\n    this.group.add(this._containerGroup = new Group$3());\n\n    this._containerGroup.add(this.getContentGroup());\n\n    this.group.add(this._controllerGroup = new Group$3());\n  };\n  /**\n   * @override\n   */\n\n\n  ScrollableLegendView.prototype.resetInner = function () {\n    _super.prototype.resetInner.call(this);\n\n    this._controllerGroup.removeAll();\n\n    this._containerGroup.removeClipPath();\n\n    this._containerGroup.__rectSize = null;\n  };\n  /**\n   * @override\n   */\n\n\n  ScrollableLegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {\n    var self = this; // Render content items.\n\n    _super.prototype.renderInner.call(this, itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition);\n\n    var controllerGroup = this._controllerGroup; // FIXME: support be 'auto' adapt to size number text length,\n    // e.g., '3/12345' should not overlap with the control arrow button.\n\n    var pageIconSize = legendModel.get('pageIconSize', true);\n    var pageIconSizeArr = isArray(pageIconSize) ? pageIconSize : [pageIconSize, pageIconSize];\n    createPageButton('pagePrev', 0);\n    var pageTextStyleModel = legendModel.getModel('pageTextStyle');\n    controllerGroup.add(new ZRText({\n      name: 'pageText',\n      style: {\n        // Placeholder to calculate a proper layout.\n        text: 'xx/xx',\n        fill: pageTextStyleModel.getTextColor(),\n        font: pageTextStyleModel.getFont(),\n        verticalAlign: 'middle',\n        align: 'center'\n      },\n      silent: true\n    }));\n    createPageButton('pageNext', 1);\n\n    function createPageButton(name, iconIdx) {\n      var pageDataIndexName = name + 'DataIndex';\n      var icon = createIcon(legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], {\n        // Buttons will be created in each render, so we do not need\n        // to worry about avoiding using legendModel kept in scope.\n        onclick: bind(self._pageGo, self, pageDataIndexName, legendModel, api)\n      }, {\n        x: -pageIconSizeArr[0] / 2,\n        y: -pageIconSizeArr[1] / 2,\n        width: pageIconSizeArr[0],\n        height: pageIconSizeArr[1]\n      });\n      icon.name = name;\n      controllerGroup.add(icon);\n    }\n  };\n  /**\n   * @override\n   */\n\n\n  ScrollableLegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {\n    var selectorGroup = this.getSelectorGroup();\n    var orientIdx = legendModel.getOrient().index;\n    var wh = WH$1[orientIdx];\n    var xy = XY$1[orientIdx];\n    var hw = WH$1[1 - orientIdx];\n    var yx = XY$1[1 - orientIdx];\n    selector && box( // Buttons in selectorGroup always layout horizontally\n    'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));\n    var selectorButtonGap = legendModel.get('selectorButtonGap', true);\n    var selectorRect = selectorGroup.getBoundingRect();\n    var selectorPos = [-selectorRect.x, -selectorRect.y];\n    var processMaxSize = clone(maxSize);\n    selector && (processMaxSize[wh] = maxSize[wh] - selectorRect[wh] - selectorButtonGap);\n\n    var mainRect = this._layoutContentAndController(legendModel, isFirstRender, processMaxSize, orientIdx, wh, hw, yx, xy);\n\n    if (selector) {\n      if (selectorPosition === 'end') {\n        selectorPos[orientIdx] += mainRect[wh] + selectorButtonGap;\n      } else {\n        var offset = selectorRect[wh] + selectorButtonGap;\n        selectorPos[orientIdx] -= offset;\n        mainRect[xy] -= offset;\n      }\n\n      mainRect[wh] += selectorRect[wh] + selectorButtonGap;\n      selectorPos[1 - orientIdx] += mainRect[yx] + mainRect[hw] / 2 - selectorRect[hw] / 2;\n      mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw]);\n      mainRect[yx] = Math.min(mainRect[yx], selectorRect[yx] + selectorPos[1 - orientIdx]);\n      selectorGroup.x = selectorPos[0];\n      selectorGroup.y = selectorPos[1];\n      selectorGroup.markRedraw();\n    }\n\n    return mainRect;\n  };\n\n  ScrollableLegendView.prototype._layoutContentAndController = function (legendModel, isFirstRender, maxSize, orientIdx, wh, hw, yx, xy) {\n    var contentGroup = this.getContentGroup();\n    var containerGroup = this._containerGroup;\n    var controllerGroup = this._controllerGroup; // Place items in contentGroup.\n\n    box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), !orientIdx ? null : maxSize.width, orientIdx ? null : maxSize.height);\n    box( // Buttons in controller are layout always horizontally.\n    'horizontal', controllerGroup, legendModel.get('pageButtonItemGap', true));\n    var contentRect = contentGroup.getBoundingRect();\n    var controllerRect = controllerGroup.getBoundingRect();\n    var showController = this._showController = contentRect[wh] > maxSize[wh]; // In case that the inner elements of contentGroup layout do not based on [0, 0]\n\n    var contentPos = [-contentRect.x, -contentRect.y]; // Remain contentPos when scroll animation perfroming.\n    // If first rendering, `contentGroup.position` is [0, 0], which\n    // does not make sense and may cause unexepcted animation if adopted.\n\n    if (!isFirstRender) {\n      contentPos[orientIdx] = contentGroup[xy];\n    } // Layout container group based on 0.\n\n\n    var containerPos = [0, 0];\n    var controllerPos = [-controllerRect.x, -controllerRect.y];\n    var pageButtonGap = retrieve2(legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)); // Place containerGroup and controllerGroup and contentGroup.\n\n    if (showController) {\n      var pageButtonPosition = legendModel.get('pageButtonPosition', true); // controller is on the right / bottom.\n\n      if (pageButtonPosition === 'end') {\n        controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];\n      } // controller is on the left / top.\n      else {\n          containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;\n        }\n    } // Always align controller to content as 'middle'.\n\n\n    controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;\n    contentGroup.setPosition(contentPos);\n    containerGroup.setPosition(containerPos);\n    controllerGroup.setPosition(controllerPos); // Calculate `mainRect` and set `clipPath`.\n    // mainRect should not be calculated by `this.group.getBoundingRect()`\n    // for sake of the overflow.\n\n    var mainRect = {\n      x: 0,\n      y: 0\n    }; // Consider content may be overflow (should be clipped).\n\n    mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];\n    mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.\n\n    mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);\n    containerGroup.__rectSize = maxSize[wh];\n\n    if (showController) {\n      var clipShape = {\n        x: 0,\n        y: 0\n      };\n      clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);\n      clipShape[hw] = mainRect[hw];\n      containerGroup.setClipPath(new Rect({\n        shape: clipShape\n      })); // Consider content may be larger than container, container rect\n      // can not be obtained from `containerGroup.getBoundingRect()`.\n\n      containerGroup.__rectSize = clipShape[wh];\n    } else {\n      // Do not remove or ignore controller. Keep them set as placeholders.\n      controllerGroup.eachChild(function (child) {\n        child.attr({\n          invisible: true,\n          silent: true\n        });\n      });\n    } // Content translate animation.\n\n\n    var pageInfo = this._getPageInfo(legendModel);\n\n    pageInfo.pageIndex != null && updateProps(contentGroup, {\n      x: pageInfo.contentPosition[0],\n      y: pageInfo.contentPosition[1]\n    }, // When switch from \"show controller\" to \"not show controller\", view should be\n    // updated immediately without animation, otherwise causes weird effect.\n    showController ? legendModel : null);\n\n    this._updatePageInfoView(legendModel, pageInfo);\n\n    return mainRect;\n  };\n\n  ScrollableLegendView.prototype._pageGo = function (to, legendModel, api) {\n    var scrollDataIndex = this._getPageInfo(legendModel)[to];\n\n    scrollDataIndex != null && api.dispatchAction({\n      type: 'legendScroll',\n      scrollDataIndex: scrollDataIndex,\n      legendId: legendModel.id\n    });\n  };\n\n  ScrollableLegendView.prototype._updatePageInfoView = function (legendModel, pageInfo) {\n    var controllerGroup = this._controllerGroup;\n    each(['pagePrev', 'pageNext'], function (name) {\n      var key = name + 'DataIndex';\n      var canJump = pageInfo[key] != null;\n      var icon = controllerGroup.childOfName(name);\n\n      if (icon) {\n        icon.setStyle('fill', canJump ? legendModel.get('pageIconColor', true) : legendModel.get('pageIconInactiveColor', true));\n        icon.cursor = canJump ? 'pointer' : 'default';\n      }\n    });\n    var pageText = controllerGroup.childOfName('pageText');\n    var pageFormatter = legendModel.get('pageFormatter');\n    var pageIndex = pageInfo.pageIndex;\n    var current = pageIndex != null ? pageIndex + 1 : 0;\n    var total = pageInfo.pageCount;\n    pageText && pageFormatter && pageText.setStyle('text', isString(pageFormatter) ? pageFormatter.replace('{current}', current == null ? '' : current + '').replace('{total}', total == null ? '' : total + '') : pageFormatter({\n      current: current,\n      total: total\n    }));\n  };\n  /**\n   *  contentPosition: Array.<number>, null when data item not found.\n   *  pageIndex: number, null when data item not found.\n   *  pageCount: number, always be a number, can be 0.\n   *  pagePrevDataIndex: number, null when no previous page.\n   *  pageNextDataIndex: number, null when no next page.\n   * }\n   */\n\n\n  ScrollableLegendView.prototype._getPageInfo = function (legendModel) {\n    var scrollDataIndex = legendModel.get('scrollDataIndex', true);\n    var contentGroup = this.getContentGroup();\n    var containerRectSize = this._containerGroup.__rectSize;\n    var orientIdx = legendModel.getOrient().index;\n    var wh = WH$1[orientIdx];\n    var xy = XY$1[orientIdx];\n\n    var targetItemIndex = this._findTargetItemIndex(scrollDataIndex);\n\n    var children = contentGroup.children();\n    var targetItem = children[targetItemIndex];\n    var itemCount = children.length;\n    var pCount = !itemCount ? 0 : 1;\n    var result = {\n      contentPosition: [contentGroup.x, contentGroup.y],\n      pageCount: pCount,\n      pageIndex: pCount - 1,\n      pagePrevDataIndex: null,\n      pageNextDataIndex: null\n    };\n\n    if (!targetItem) {\n      return result;\n    }\n\n    var targetItemInfo = getItemInfo(targetItem);\n    result.contentPosition[orientIdx] = -targetItemInfo.s; // Strategy:\n    // (1) Always align based on the left/top most item.\n    // (2) It is user-friendly that the last item shown in the\n    // current window is shown at the begining of next window.\n    // Otherwise if half of the last item is cut by the window,\n    // it will have no chance to display entirely.\n    // (3) Consider that item size probably be different, we\n    // have calculate pageIndex by size rather than item index,\n    // and we can not get page index directly by division.\n    // (4) The window is to narrow to contain more than\n    // one item, we should make sure that the page can be fliped.\n\n    for (var i = targetItemIndex + 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i <= itemCount; ++i) {\n      currItemInfo = getItemInfo(children[i]);\n\n      if ( // Half of the last item is out of the window.\n      !currItemInfo && winEndItemInfo.e > winStartItemInfo.s + containerRectSize || // If the current item does not intersect with the window, the new page\n      // can be started at the current item or the last item.\n      currItemInfo && !intersect(currItemInfo, winStartItemInfo.s)) {\n        if (winEndItemInfo.i > winStartItemInfo.i) {\n          winStartItemInfo = winEndItemInfo;\n        } else {\n          // e.g., when page size is smaller than item size.\n          winStartItemInfo = currItemInfo;\n        }\n\n        if (winStartItemInfo) {\n          if (result.pageNextDataIndex == null) {\n            result.pageNextDataIndex = winStartItemInfo.i;\n          }\n\n          ++result.pageCount;\n        }\n      }\n\n      winEndItemInfo = currItemInfo;\n    }\n\n    for (var i = targetItemIndex - 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i >= -1; --i) {\n      currItemInfo = getItemInfo(children[i]);\n\n      if ( // If the the end item does not intersect with the window started\n      // from the current item, a page can be settled.\n      (!currItemInfo || !intersect(winEndItemInfo, currItemInfo.s)) && // e.g., when page size is smaller than item size.\n      winStartItemInfo.i < winEndItemInfo.i) {\n        winEndItemInfo = winStartItemInfo;\n\n        if (result.pagePrevDataIndex == null) {\n          result.pagePrevDataIndex = winStartItemInfo.i;\n        }\n\n        ++result.pageCount;\n        ++result.pageIndex;\n      }\n\n      winStartItemInfo = currItemInfo;\n    }\n\n    return result;\n\n    function getItemInfo(el) {\n      if (el) {\n        var itemRect = el.getBoundingRect();\n        var start = itemRect[xy] + el[xy];\n        return {\n          s: start,\n          e: start + itemRect[wh],\n          i: el.__legendDataIndex\n        };\n      }\n    }\n\n    function intersect(itemInfo, winStart) {\n      return itemInfo.e >= winStart && itemInfo.s <= winStart + containerRectSize;\n    }\n  };\n\n  ScrollableLegendView.prototype._findTargetItemIndex = function (targetDataIndex) {\n    if (!this._showController) {\n      return 0;\n    }\n\n    var index;\n    var contentGroup = this.getContentGroup();\n    var defaultIndex;\n    contentGroup.eachChild(function (child, idx) {\n      var legendDataIdx = child.__legendDataIndex; // FIXME\n      // If the given targetDataIndex (from model) is illegal,\n      // we use defaultIndex. But the index on the legend model and\n      // action payload is still illegal. That case will not be\n      // changed until some scenario requires.\n\n      if (defaultIndex == null && legendDataIdx != null) {\n        defaultIndex = idx;\n      }\n\n      if (legendDataIdx === targetDataIndex) {\n        index = idx;\n      }\n    });\n    return index != null ? index : defaultIndex;\n  };\n\n  ScrollableLegendView.type = 'legend.scroll';\n  return ScrollableLegendView;\n}(LegendView);\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nfunction installScrollableLegendAction(registers) {\n  /**\n   * @event legendScroll\n   * @type {Object}\n   * @property {string} type 'legendScroll'\n   * @property {string} scrollDataIndex\n   */\n  registers.registerAction('legendScroll', 'legendscroll', function (payload, ecModel) {\n    var scrollDataIndex = payload.scrollDataIndex;\n    scrollDataIndex != null && ecModel.eachComponent({\n      mainType: 'legend',\n      subType: 'scroll',\n      query: payload\n    }, function (legendModel) {\n      legendModel.setScrollDataIndex(scrollDataIndex);\n    });\n  });\n}\n\nfunction install$I(registers) {\n  use(install$H);\n  registers.registerComponentModel(ScrollableLegendModel);\n  registers.registerComponentView(ScrollableLegendView);\n  installScrollableLegendAction(registers);\n}\n\nfunction install$J(registers) {\n  use(install$H);\n  use(install$I);\n}\n\nvar InsideZoomModel =\n/** @class */\nfunction (_super) {\n  __extends(InsideZoomModel, _super);\n\n  function InsideZoomModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = InsideZoomModel.type;\n    return _this;\n  }\n\n  InsideZoomModel.type = 'dataZoom.inside';\n  InsideZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, {\n    disabled: false,\n    zoomLock: false,\n    zoomOnMouseWheel: true,\n    moveOnMouseMove: true,\n    moveOnMouseWheel: false,\n    preventDefaultMouseMove: true\n  });\n  return InsideZoomModel;\n}(DataZoomModel);\n\nvar inner$k = makeInner();\nfunction setViewInfoToCoordSysRecord(api, dataZoomModel, getRange) {\n  inner$k(api).coordSysRecordMap.each(function (coordSysRecord) {\n    var dzInfo = coordSysRecord.dataZoomInfoMap.get(dataZoomModel.uid);\n\n    if (dzInfo) {\n      dzInfo.getRange = getRange;\n    }\n  });\n}\nfunction disposeCoordSysRecordIfNeeded(api, dataZoomModel) {\n  var coordSysRecordMap = inner$k(api).coordSysRecordMap;\n  var coordSysKeyArr = coordSysRecordMap.keys();\n\n  for (var i = 0; i < coordSysKeyArr.length; i++) {\n    var coordSysKey = coordSysKeyArr[i];\n    var coordSysRecord = coordSysRecordMap.get(coordSysKey);\n    var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap;\n\n    if (dataZoomInfoMap) {\n      var dzUid = dataZoomModel.uid;\n      var dzInfo = dataZoomInfoMap.get(dzUid);\n\n      if (dzInfo) {\n        dataZoomInfoMap.removeKey(dzUid);\n\n        if (!dataZoomInfoMap.keys().length) {\n          disposeCoordSysRecord(coordSysRecordMap, coordSysRecord);\n        }\n      }\n    }\n  }\n}\n\nfunction disposeCoordSysRecord(coordSysRecordMap, coordSysRecord) {\n  if (coordSysRecord) {\n    coordSysRecordMap.removeKey(coordSysRecord.model.uid);\n    var controller = coordSysRecord.controller;\n    controller && controller.dispose();\n  }\n}\n\nfunction createCoordSysRecord(api, coordSysModel) {\n  // These init props will never change after record created.\n  var coordSysRecord = {\n    model: coordSysModel,\n    containsPoint: curry(containsPoint, coordSysModel),\n    dispatchAction: curry(dispatchAction$1, api),\n    dataZoomInfoMap: null,\n    controller: null\n  }; // Must not do anything depends on coordSysRecord outside the event handler here,\n  // because coordSysRecord not completed yet.\n\n  var controller = coordSysRecord.controller = new RoamController(api.getZr());\n  each(['pan', 'zoom', 'scrollMove'], function (eventName) {\n    controller.on(eventName, function (event) {\n      var batch = [];\n      coordSysRecord.dataZoomInfoMap.each(function (dzInfo) {\n        // Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove,\n        // moveOnMouseWheel, ...) enabled.\n        if (!event.isAvailableBehavior(dzInfo.model.option)) {\n          return;\n        }\n\n        var method = (dzInfo.getRange || {})[eventName];\n        var range = method && method(dzInfo.dzReferCoordSysInfo, coordSysRecord.model.mainType, coordSysRecord.controller, event);\n        !dzInfo.model.get('disabled', true) && range && batch.push({\n          dataZoomId: dzInfo.model.id,\n          start: range[0],\n          end: range[1]\n        });\n      });\n      batch.length && coordSysRecord.dispatchAction(batch);\n    });\n  });\n  return coordSysRecord;\n}\n/**\n * This action will be throttled.\n */\n\n\nfunction dispatchAction$1(api, batch) {\n  if (!api.isDisposed()) {\n    api.dispatchAction({\n      type: 'dataZoom',\n      animation: {\n        easing: 'cubicOut',\n        duration: 100\n      },\n      batch: batch\n    });\n  }\n}\n\nfunction containsPoint(coordSysModel, e, x, y) {\n  return coordSysModel.coordinateSystem.containPoint([x, y]);\n}\n/**\n * Merge roamController settings when multiple dataZooms share one roamController.\n */\n\n\nfunction mergeControllerParams(dataZoomInfoMap) {\n  var controlType; // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated\n  // as string, it is probably revert to reserved word by compress tool. See #7411.\n\n  var prefix = 'type_';\n  var typePriority = {\n    'type_true': 2,\n    'type_move': 1,\n    'type_false': 0,\n    'type_undefined': -1\n  };\n  var preventDefaultMouseMove = true;\n  dataZoomInfoMap.each(function (dataZoomInfo) {\n    var dataZoomModel = dataZoomInfo.model;\n    var oneType = dataZoomModel.get('disabled', true) ? false : dataZoomModel.get('zoomLock', true) ? 'move' : true;\n\n    if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) {\n      controlType = oneType;\n    } // Prevent default move event by default. If one false, do not prevent. Otherwise\n    // users may be confused why it does not work when multiple insideZooms exist.\n\n\n    preventDefaultMouseMove = preventDefaultMouseMove && dataZoomModel.get('preventDefaultMouseMove', true);\n  });\n  return {\n    controlType: controlType,\n    opt: {\n      // RoamController will enable all of these functionalities,\n      // and the final behavior is determined by its event listener\n      // provided by each inside zoom.\n      zoomOnMouseWheel: true,\n      moveOnMouseMove: true,\n      moveOnMouseWheel: true,\n      preventDefaultMouseMove: !!preventDefaultMouseMove\n    }\n  };\n}\n\nfunction installDataZoomRoamProcessor(registers) {\n  registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, function (ecModel, api) {\n    var apiInner = inner$k(api);\n    var coordSysRecordMap = apiInner.coordSysRecordMap || (apiInner.coordSysRecordMap = createHashMap());\n    coordSysRecordMap.each(function (coordSysRecord) {\n      // `coordSysRecordMap` always exists (because it holds the `roam controller`, which should\n      // better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow.\n      coordSysRecord.dataZoomInfoMap = null;\n    });\n    ecModel.eachComponent({\n      mainType: 'dataZoom',\n      subType: 'inside'\n    }, function (dataZoomModel) {\n      var dzReferCoordSysWrap = collectReferCoordSysModelInfo(dataZoomModel);\n      each(dzReferCoordSysWrap.infoList, function (dzCoordSysInfo) {\n        var coordSysUid = dzCoordSysInfo.model.uid;\n        var coordSysRecord = coordSysRecordMap.get(coordSysUid) || coordSysRecordMap.set(coordSysUid, createCoordSysRecord(api, dzCoordSysInfo.model));\n        var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap || (coordSysRecord.dataZoomInfoMap = createHashMap()); // Notice these props might be changed each time for a single dataZoomModel.\n\n        dataZoomInfoMap.set(dataZoomModel.uid, {\n          dzReferCoordSysInfo: dzCoordSysInfo,\n          model: dataZoomModel,\n          getRange: null\n        });\n      });\n    }); // (1) Merge dataZoom settings for each coord sys and set to the roam controller.\n    // (2) Clear coord sys if not refered by any dataZoom.\n\n    coordSysRecordMap.each(function (coordSysRecord) {\n      var controller = coordSysRecord.controller;\n      var firstDzInfo;\n      var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap;\n\n      if (dataZoomInfoMap) {\n        var firstDzKey = dataZoomInfoMap.keys()[0];\n\n        if (firstDzKey != null) {\n          firstDzInfo = dataZoomInfoMap.get(firstDzKey);\n        }\n      }\n\n      if (!firstDzInfo) {\n        disposeCoordSysRecord(coordSysRecordMap, coordSysRecord);\n        return;\n      }\n\n      var controllerParams = mergeControllerParams(dataZoomInfoMap);\n      controller.enable(controllerParams.controlType, controllerParams.opt);\n      controller.setPointerChecker(coordSysRecord.containsPoint);\n      createOrUpdate(coordSysRecord, 'dispatchAction', firstDzInfo.model.get('throttle', true), 'fixRate');\n    });\n  });\n}\n\nvar InsideZoomView =\n/** @class */\nfunction (_super) {\n  __extends(InsideZoomView, _super);\n\n  function InsideZoomView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = 'dataZoom.inside';\n    return _this;\n  }\n\n  InsideZoomView.prototype.render = function (dataZoomModel, ecModel, api) {\n    _super.prototype.render.apply(this, arguments);\n\n    if (dataZoomModel.noTarget()) {\n      this._clear();\n\n      return;\n    } // Hence the `throttle` util ensures to preserve command order,\n    // here simply updating range all the time will not cause missing\n    // any of the the roam change.\n\n\n    this.range = dataZoomModel.getPercentRange(); // Reset controllers.\n\n    setViewInfoToCoordSysRecord(api, dataZoomModel, {\n      pan: bind(getRangeHandlers.pan, this),\n      zoom: bind(getRangeHandlers.zoom, this),\n      scrollMove: bind(getRangeHandlers.scrollMove, this)\n    });\n  };\n\n  InsideZoomView.prototype.dispose = function () {\n    this._clear();\n\n    _super.prototype.dispose.apply(this, arguments);\n  };\n\n  InsideZoomView.prototype._clear = function () {\n    disposeCoordSysRecordIfNeeded(this.api, this.dataZoomModel);\n    this.range = null;\n  };\n\n  InsideZoomView.type = 'dataZoom.inside';\n  return InsideZoomView;\n}(DataZoomView);\n\nvar getRangeHandlers = {\n  zoom: function (coordSysInfo, coordSysMainType, controller, e) {\n    var lastRange = this.range;\n    var range = lastRange.slice(); // Calculate transform by the first axis.\n\n    var axisModel = coordSysInfo.axisModels[0];\n\n    if (!axisModel) {\n      return;\n    }\n\n    var directionInfo = getDirectionInfo[coordSysMainType](null, [e.originX, e.originY], axisModel, controller, coordSysInfo);\n    var percentPoint = (directionInfo.signal > 0 ? directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel : directionInfo.pixel - directionInfo.pixelStart) / directionInfo.pixelLength * (range[1] - range[0]) + range[0];\n    var scale = Math.max(1 / e.scale, 0);\n    range[0] = (range[0] - percentPoint) * scale + percentPoint;\n    range[1] = (range[1] - percentPoint) * scale + percentPoint; // Restrict range.\n\n    var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();\n    sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);\n    this.range = range;\n\n    if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {\n      return range;\n    }\n  },\n  pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) {\n    var directionInfo = getDirectionInfo[coordSysMainType]([e.oldX, e.oldY], [e.newX, e.newY], axisModel, controller, coordSysInfo);\n    return directionInfo.signal * (range[1] - range[0]) * directionInfo.pixel / directionInfo.pixelLength;\n  }),\n  scrollMove: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) {\n    var directionInfo = getDirectionInfo[coordSysMainType]([0, 0], [e.scrollDelta, e.scrollDelta], axisModel, controller, coordSysInfo);\n    return directionInfo.signal * (range[1] - range[0]) * e.scrollDelta;\n  })\n};\n\nfunction makeMover(getPercentDelta) {\n  return function (coordSysInfo, coordSysMainType, controller, e) {\n    var lastRange = this.range;\n    var range = lastRange.slice(); // Calculate transform by the first axis.\n\n    var axisModel = coordSysInfo.axisModels[0];\n\n    if (!axisModel) {\n      return;\n    }\n\n    var percentDelta = getPercentDelta(range, axisModel, coordSysInfo, coordSysMainType, controller, e);\n    sliderMove(percentDelta, range, [0, 100], 'all');\n    this.range = range;\n\n    if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {\n      return range;\n    }\n  };\n}\n\nvar getDirectionInfo = {\n  grid: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n    var axis = axisModel.axis;\n    var ret = {};\n    var rect = coordSysInfo.model.coordinateSystem.getRect();\n    oldPoint = oldPoint || [0, 0];\n\n    if (axis.dim === 'x') {\n      ret.pixel = newPoint[0] - oldPoint[0];\n      ret.pixelLength = rect.width;\n      ret.pixelStart = rect.x;\n      ret.signal = axis.inverse ? 1 : -1;\n    } else {\n      // axis.dim === 'y'\n      ret.pixel = newPoint[1] - oldPoint[1];\n      ret.pixelLength = rect.height;\n      ret.pixelStart = rect.y;\n      ret.signal = axis.inverse ? -1 : 1;\n    }\n\n    return ret;\n  },\n  polar: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n    var axis = axisModel.axis;\n    var ret = {};\n    var polar = coordSysInfo.model.coordinateSystem;\n    var radiusExtent = polar.getRadiusAxis().getExtent();\n    var angleExtent = polar.getAngleAxis().getExtent();\n    oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0];\n    newPoint = polar.pointToCoord(newPoint);\n\n    if (axisModel.mainType === 'radiusAxis') {\n      ret.pixel = newPoint[0] - oldPoint[0]; // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]);\n      // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]);\n\n      ret.pixelLength = radiusExtent[1] - radiusExtent[0];\n      ret.pixelStart = radiusExtent[0];\n      ret.signal = axis.inverse ? 1 : -1;\n    } else {\n      // 'angleAxis'\n      ret.pixel = newPoint[1] - oldPoint[1]; // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]);\n      // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]);\n\n      ret.pixelLength = angleExtent[1] - angleExtent[0];\n      ret.pixelStart = angleExtent[0];\n      ret.signal = axis.inverse ? -1 : 1;\n    }\n\n    return ret;\n  },\n  singleAxis: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n    var axis = axisModel.axis;\n    var rect = coordSysInfo.model.coordinateSystem.getRect();\n    var ret = {};\n    oldPoint = oldPoint || [0, 0];\n\n    if (axis.orient === 'horizontal') {\n      ret.pixel = newPoint[0] - oldPoint[0];\n      ret.pixelLength = rect.width;\n      ret.pixelStart = rect.x;\n      ret.signal = axis.inverse ? 1 : -1;\n    } else {\n      // 'vertical'\n      ret.pixel = newPoint[1] - oldPoint[1];\n      ret.pixelLength = rect.height;\n      ret.pixelStart = rect.y;\n      ret.signal = axis.inverse ? -1 : 1;\n    }\n\n    return ret;\n  }\n};\n\nfunction install$K(registers) {\n  installCommon(registers);\n  registers.registerComponentModel(InsideZoomModel);\n  registers.registerComponentView(InsideZoomView);\n  installDataZoomRoamProcessor(registers);\n}\n\nvar SliderZoomModel =\n/** @class */\nfunction (_super) {\n  __extends(SliderZoomModel, _super);\n\n  function SliderZoomModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SliderZoomModel.type;\n    return _this;\n  }\n\n  SliderZoomModel.type = 'dataZoom.slider';\n  SliderZoomModel.layoutMode = 'box';\n  SliderZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, {\n    show: true,\n    // deault value can only be drived in view stage.\n    right: 'ph',\n    top: 'ph',\n    width: 'ph',\n    height: 'ph',\n    left: null,\n    bottom: null,\n    borderColor: '#d2dbee',\n    borderRadius: 3,\n    backgroundColor: 'rgba(47,69,84,0)',\n    // dataBackgroundColor: '#ddd',\n    dataBackground: {\n      lineStyle: {\n        color: '#d2dbee',\n        width: 0.5\n      },\n      areaStyle: {\n        color: '#d2dbee',\n        opacity: 0.2\n      }\n    },\n    selectedDataBackground: {\n      lineStyle: {\n        color: '#8fb0f7',\n        width: 0.5\n      },\n      areaStyle: {\n        color: '#8fb0f7',\n        opacity: 0.2\n      }\n    },\n    // Color of selected window.\n    fillerColor: 'rgba(135,175,274,0.2)',\n    handleIcon: 'path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z',\n    // Percent of the slider height\n    handleSize: '100%',\n    handleStyle: {\n      color: '#fff',\n      borderColor: '#ACB8D1'\n    },\n    moveHandleSize: 7,\n    moveHandleIcon: 'path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z',\n    moveHandleStyle: {\n      color: '#D2DBEE',\n      opacity: 0.7\n    },\n    showDetail: true,\n    showDataShadow: 'auto',\n    realtime: true,\n    zoomLock: false,\n    textStyle: {\n      color: '#6E7079'\n    },\n    brushSelect: true,\n    brushStyle: {\n      color: 'rgba(135,175,274,0.15)'\n    },\n    emphasis: {\n      handleStyle: {\n        borderColor: '#8FB0F7'\n      },\n      moveHandleStyle: {\n        color: '#8FB0F7'\n      }\n    }\n  });\n  return SliderZoomModel;\n}(DataZoomModel);\n\nvar Rect$2 = Rect; // Constants\n\nvar DEFAULT_LOCATION_EDGE_GAP = 7;\nvar DEFAULT_FRAME_BORDER_WIDTH = 1;\nvar DEFAULT_FILLER_SIZE = 30;\nvar DEFAULT_MOVE_HANDLE_SIZE = 7;\nvar HORIZONTAL = 'horizontal';\nvar VERTICAL = 'vertical';\nvar LABEL_GAP = 5;\nvar SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];\nvar REALTIME_ANIMATION_CONFIG = {\n  easing: 'cubicOut',\n  duration: 100,\n  delay: 0\n};\n\nvar SliderZoomView =\n/** @class */\nfunction (_super) {\n  __extends(SliderZoomView, _super);\n\n  function SliderZoomView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = SliderZoomView.type;\n    _this._displayables = {};\n    return _this;\n  }\n\n  SliderZoomView.prototype.init = function (ecModel, api) {\n    this.api = api; // A unique handler for each dataZoom component\n\n    this._onBrush = bind(this._onBrush, this);\n    this._onBrushEnd = bind(this._onBrushEnd, this);\n  };\n\n  SliderZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) {\n    _super.prototype.render.apply(this, arguments);\n\n    createOrUpdate(this, '_dispatchZoomAction', dataZoomModel.get('throttle'), 'fixRate');\n    this._orient = dataZoomModel.getOrient();\n\n    if (dataZoomModel.get('show') === false) {\n      this.group.removeAll();\n      return;\n    }\n\n    if (dataZoomModel.noTarget()) {\n      this._clear();\n\n      this.group.removeAll();\n      return;\n    } // Notice: this._resetInterval() should not be executed when payload.type\n    // is 'dataZoom', origin this._range should be maintained, otherwise 'pan'\n    // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,\n\n\n    if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) {\n      this._buildView();\n    }\n\n    this._updateView();\n  };\n\n  SliderZoomView.prototype.dispose = function () {\n    this._clear();\n\n    _super.prototype.dispose.apply(this, arguments);\n  };\n\n  SliderZoomView.prototype._clear = function () {\n    clear(this, '_dispatchZoomAction');\n    var zr = this.api.getZr();\n    zr.off('mousemove', this._onBrush);\n    zr.off('mouseup', this._onBrushEnd);\n  };\n\n  SliderZoomView.prototype._buildView = function () {\n    var thisGroup = this.group;\n    thisGroup.removeAll();\n    this._brushing = false;\n    this._displayables.brushRect = null;\n\n    this._resetLocation();\n\n    this._resetInterval();\n\n    var barGroup = this._displayables.sliderGroup = new Group();\n\n    this._renderBackground();\n\n    this._renderHandle();\n\n    this._renderDataShadow();\n\n    thisGroup.add(barGroup);\n\n    this._positionGroup();\n  };\n\n  SliderZoomView.prototype._resetLocation = function () {\n    var dataZoomModel = this.dataZoomModel;\n    var api = this.api;\n    var showMoveHandle = dataZoomModel.get('brushSelect');\n    var moveHandleSize = showMoveHandle ? DEFAULT_MOVE_HANDLE_SIZE : 0; // If some of x/y/width/height are not specified,\n    // auto-adapt according to target grid.\n\n    var coordRect = this._findCoordRect();\n\n    var ecSize = {\n      width: api.getWidth(),\n      height: api.getHeight()\n    }; // Default align by coordinate system rect.\n\n    var positionInfo = this._orient === HORIZONTAL ? {\n      // Why using 'right', because right should be used in vertical,\n      // and it is better to be consistent for dealing with position param merge.\n      right: ecSize.width - coordRect.x - coordRect.width,\n      top: ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP - moveHandleSize,\n      width: coordRect.width,\n      height: DEFAULT_FILLER_SIZE\n    } : {\n      right: DEFAULT_LOCATION_EDGE_GAP,\n      top: coordRect.y,\n      width: DEFAULT_FILLER_SIZE,\n      height: coordRect.height\n    }; // Do not write back to option and replace value 'ph', because\n    // the 'ph' value should be recalculated when resize.\n\n    var layoutParams = getLayoutParams(dataZoomModel.option); // Replace the placeholder value.\n\n    each(['right', 'top', 'width', 'height'], function (name) {\n      if (layoutParams[name] === 'ph') {\n        layoutParams[name] = positionInfo[name];\n      }\n    });\n    var layoutRect = getLayoutRect(layoutParams, ecSize);\n    this._location = {\n      x: layoutRect.x,\n      y: layoutRect.y\n    };\n    this._size = [layoutRect.width, layoutRect.height];\n    this._orient === VERTICAL && this._size.reverse();\n  };\n\n  SliderZoomView.prototype._positionGroup = function () {\n    var thisGroup = this.group;\n    var location = this._location;\n    var orient = this._orient; // Just use the first axis to determine mapping.\n\n    var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel();\n    var inverse = targetAxisModel && targetAxisModel.get('inverse');\n    var sliderGroup = this._displayables.sliderGroup;\n    var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse; // Transform barGroup.\n\n    sliderGroup.attr(orient === HORIZONTAL && !inverse ? {\n      scaleY: otherAxisInverse ? 1 : -1,\n      scaleX: 1\n    } : orient === HORIZONTAL && inverse ? {\n      scaleY: otherAxisInverse ? 1 : -1,\n      scaleX: -1\n    } : orient === VERTICAL && !inverse ? {\n      scaleY: otherAxisInverse ? -1 : 1,\n      scaleX: 1,\n      rotation: Math.PI / 2\n    } // Dont use Math.PI, considering shadow direction.\n    : {\n      scaleY: otherAxisInverse ? -1 : 1,\n      scaleX: -1,\n      rotation: Math.PI / 2\n    }); // Position barGroup\n\n    var rect = thisGroup.getBoundingRect([sliderGroup]);\n    thisGroup.x = location.x - rect.x;\n    thisGroup.y = location.y - rect.y;\n    thisGroup.markRedraw();\n  };\n\n  SliderZoomView.prototype._getViewExtent = function () {\n    return [0, this._size[0]];\n  };\n\n  SliderZoomView.prototype._renderBackground = function () {\n    var dataZoomModel = this.dataZoomModel;\n    var size = this._size;\n    var barGroup = this._displayables.sliderGroup;\n    var brushSelect = dataZoomModel.get('brushSelect');\n    barGroup.add(new Rect$2({\n      silent: true,\n      shape: {\n        x: 0,\n        y: 0,\n        width: size[0],\n        height: size[1]\n      },\n      style: {\n        fill: dataZoomModel.get('backgroundColor')\n      },\n      z2: -40\n    })); // Click panel, over shadow, below handles.\n\n    var clickPanel = new Rect$2({\n      shape: {\n        x: 0,\n        y: 0,\n        width: size[0],\n        height: size[1]\n      },\n      style: {\n        fill: 'transparent'\n      },\n      z2: 0,\n      onclick: bind(this._onClickPanel, this)\n    });\n    var zr = this.api.getZr();\n\n    if (brushSelect) {\n      clickPanel.on('mousedown', this._onBrushStart, this);\n      clickPanel.cursor = 'crosshair';\n      zr.on('mousemove', this._onBrush);\n      zr.on('mouseup', this._onBrushEnd);\n    } else {\n      zr.off('mousemove', this._onBrush);\n      zr.off('mouseup', this._onBrushEnd);\n    }\n\n    barGroup.add(clickPanel);\n  };\n\n  SliderZoomView.prototype._renderDataShadow = function () {\n    var info = this._dataShadowInfo = this._prepareDataShadowInfo();\n\n    this._displayables.dataShadowSegs = [];\n\n    if (!info) {\n      return;\n    }\n\n    var size = this._size;\n    var oldSize = this._shadowSize || [];\n    var seriesModel = info.series;\n    var data = seriesModel.getRawData();\n    var candlestickDim = seriesModel.getShadowDim && seriesModel.getShadowDim();\n    var otherDim = candlestickDim && data.getDimensionInfo(candlestickDim) ? seriesModel.getShadowDim() // @see candlestick\n    : info.otherDim;\n\n    if (otherDim == null) {\n      return;\n    }\n\n    var polygonPts = this._shadowPolygonPts;\n    var polylinePts = this._shadowPolylinePts; // Not re-render if data doesn't change.\n\n    if (data !== this._shadowData || otherDim !== this._shadowDim || size[0] !== oldSize[0] || size[1] !== oldSize[1]) {\n      var otherDataExtent_1 = data.getDataExtent(otherDim); // Nice extent.\n\n      var otherOffset = (otherDataExtent_1[1] - otherDataExtent_1[0]) * 0.3;\n      otherDataExtent_1 = [otherDataExtent_1[0] - otherOffset, otherDataExtent_1[1] + otherOffset];\n      var otherShadowExtent_1 = [0, size[1]];\n      var thisShadowExtent = [0, size[0]];\n      var areaPoints_1 = [[size[0], 0], [0, 0]];\n      var linePoints_1 = [];\n      var step_1 = thisShadowExtent[1] / (data.count() - 1);\n      var thisCoord_1 = 0; // Optimize for large data shadow\n\n      var stride_1 = Math.round(data.count() / size[0]);\n      var lastIsEmpty_1;\n      data.each([otherDim], function (value, index) {\n        if (stride_1 > 0 && index % stride_1) {\n          thisCoord_1 += step_1;\n          return;\n        } // FIXME\n        // Should consider axis.min/axis.max when drawing dataShadow.\n        // FIXME\n        // 应该使用统一的空判断？还是在list里进行空判断？\n\n\n        var isEmpty = value == null || isNaN(value) || value === ''; // See #4235.\n\n        var otherCoord = isEmpty ? 0 : linearMap(value, otherDataExtent_1, otherShadowExtent_1, true); // Attempt to draw data shadow precisely when there are empty value.\n\n        if (isEmpty && !lastIsEmpty_1 && index) {\n          areaPoints_1.push([areaPoints_1[areaPoints_1.length - 1][0], 0]);\n          linePoints_1.push([linePoints_1[linePoints_1.length - 1][0], 0]);\n        } else if (!isEmpty && lastIsEmpty_1) {\n          areaPoints_1.push([thisCoord_1, 0]);\n          linePoints_1.push([thisCoord_1, 0]);\n        }\n\n        areaPoints_1.push([thisCoord_1, otherCoord]);\n        linePoints_1.push([thisCoord_1, otherCoord]);\n        thisCoord_1 += step_1;\n        lastIsEmpty_1 = isEmpty;\n      });\n      polygonPts = this._shadowPolygonPts = areaPoints_1;\n      polylinePts = this._shadowPolylinePts = linePoints_1;\n    }\n\n    this._shadowData = data;\n    this._shadowDim = otherDim;\n    this._shadowSize = [size[0], size[1]];\n    var dataZoomModel = this.dataZoomModel;\n\n    function createDataShadowGroup(isSelectedArea) {\n      var model = dataZoomModel.getModel(isSelectedArea ? 'selectedDataBackground' : 'dataBackground');\n      var group = new Group();\n      var polygon = new Polygon({\n        shape: {\n          points: polygonPts\n        },\n        segmentIgnoreThreshold: 1,\n        style: model.getModel('areaStyle').getAreaStyle(),\n        silent: true,\n        z2: -20\n      });\n      var polyline = new Polyline({\n        shape: {\n          points: polylinePts\n        },\n        segmentIgnoreThreshold: 1,\n        style: model.getModel('lineStyle').getLineStyle(),\n        silent: true,\n        z2: -19\n      });\n      group.add(polygon);\n      group.add(polyline);\n      return group;\n    } // let dataBackgroundModel = dataZoomModel.getModel('dataBackground');\n\n\n    for (var i = 0; i < 3; i++) {\n      var group = createDataShadowGroup(i === 1);\n\n      this._displayables.sliderGroup.add(group);\n\n      this._displayables.dataShadowSegs.push(group);\n    }\n  };\n\n  SliderZoomView.prototype._prepareDataShadowInfo = function () {\n    var dataZoomModel = this.dataZoomModel;\n    var showDataShadow = dataZoomModel.get('showDataShadow');\n\n    if (showDataShadow === false) {\n      return;\n    } // Find a representative series.\n\n\n    var result;\n    var ecModel = this.ecModel;\n    dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n      var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels();\n      each(seriesModels, function (seriesModel) {\n        if (result) {\n          return;\n        }\n\n        if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) {\n          return;\n        }\n\n        var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis;\n        var otherDim = getOtherDim(axisDim);\n        var otherAxisInverse;\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (otherDim != null && coordSys.getOtherAxis) {\n          otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;\n        }\n\n        otherDim = seriesModel.getData().mapDimension(otherDim);\n        result = {\n          thisAxis: thisAxis,\n          series: seriesModel,\n          thisDim: axisDim,\n          otherDim: otherDim,\n          otherAxisInverse: otherAxisInverse\n        };\n      }, this);\n    }, this);\n    return result;\n  };\n\n  SliderZoomView.prototype._renderHandle = function () {\n    var thisGroup = this.group;\n    var displayables = this._displayables;\n    var handles = displayables.handles = [null, null];\n    var handleLabels = displayables.handleLabels = [null, null];\n    var sliderGroup = this._displayables.sliderGroup;\n    var size = this._size;\n    var dataZoomModel = this.dataZoomModel;\n    var api = this.api;\n    var borderRadius = dataZoomModel.get('borderRadius') || 0;\n    var brushSelect = dataZoomModel.get('brushSelect');\n    var filler = displayables.filler = new Rect$2({\n      silent: brushSelect,\n      style: {\n        fill: dataZoomModel.get('fillerColor')\n      },\n      textConfig: {\n        position: 'inside'\n      }\n    });\n    sliderGroup.add(filler); // Frame border.\n\n    sliderGroup.add(new Rect$2({\n      silent: true,\n      subPixelOptimize: true,\n      shape: {\n        x: 0,\n        y: 0,\n        width: size[0],\n        height: size[1],\n        r: borderRadius\n      },\n      style: {\n        // deprecated option\n        stroke: dataZoomModel.get('dataBackgroundColor') || dataZoomModel.get('borderColor'),\n        lineWidth: DEFAULT_FRAME_BORDER_WIDTH,\n        fill: 'rgba(0,0,0,0)'\n      }\n    })); // Left and right handle to resize\n\n    each([0, 1], function (handleIndex) {\n      var iconStr = dataZoomModel.get('handleIcon');\n\n      if (!symbolBuildProxies[iconStr] && iconStr.indexOf('path://') < 0 && iconStr.indexOf('image://') < 0) {\n        // Compatitable with the old icon parsers. Which can use a path string without path://\n        iconStr = 'path://' + iconStr;\n\n        if (\"development\" !== 'production') {\n          deprecateLog('handleIcon now needs \\'path://\\' prefix when using a path string');\n        }\n      }\n\n      var path = createSymbol(iconStr, -1, 0, 2, 2, null, true);\n      path.attr({\n        cursor: getCursor(this._orient),\n        draggable: true,\n        drift: bind(this._onDragMove, this, handleIndex),\n        ondragend: bind(this._onDragEnd, this),\n        onmouseover: bind(this._showDataInfo, this, true),\n        onmouseout: bind(this._showDataInfo, this, false),\n        z2: 5\n      });\n      var bRect = path.getBoundingRect();\n      var handleSize = dataZoomModel.get('handleSize');\n      this._handleHeight = parsePercent$1(handleSize, this._size[1]);\n      this._handleWidth = bRect.width / bRect.height * this._handleHeight;\n      path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle());\n      path.style.strokeNoScale = true;\n      path.rectHover = true;\n      path.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'handleStyle']).getItemStyle();\n      enableHoverEmphasis(path);\n      var handleColor = dataZoomModel.get('handleColor'); // deprecated option\n      // Compatitable with previous version\n\n      if (handleColor != null) {\n        path.style.fill = handleColor;\n      }\n\n      sliderGroup.add(handles[handleIndex] = path);\n      var textStyleModel = dataZoomModel.getModel('textStyle');\n      thisGroup.add(handleLabels[handleIndex] = new ZRText({\n        silent: true,\n        invisible: true,\n        style: createTextStyle(textStyleModel, {\n          x: 0,\n          y: 0,\n          text: '',\n          verticalAlign: 'middle',\n          align: 'center',\n          fill: textStyleModel.getTextColor(),\n          font: textStyleModel.getFont()\n        }),\n        z2: 10\n      }));\n    }, this); // Handle to move. Only visible when brushSelect is set true.\n\n    var actualMoveZone = filler;\n\n    if (brushSelect) {\n      var moveHandleHeight = parsePercent$1(dataZoomModel.get('moveHandleSize'), size[1]);\n      var moveHandle_1 = displayables.moveHandle = new Rect({\n        style: dataZoomModel.getModel('moveHandleStyle').getItemStyle(),\n        silent: true,\n        shape: {\n          r: [0, 0, 2, 2],\n          y: size[1] - 0.5,\n          height: moveHandleHeight\n        }\n      });\n      var iconSize = moveHandleHeight * 0.8;\n      var moveHandleIcon = displayables.moveHandleIcon = createSymbol(dataZoomModel.get('moveHandleIcon'), -iconSize / 2, -iconSize / 2, iconSize, iconSize, '#fff', true);\n      moveHandleIcon.silent = true;\n      moveHandleIcon.y = size[1] + moveHandleHeight / 2 - 0.5;\n      moveHandle_1.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'moveHandleStyle']).getItemStyle();\n      var moveZoneExpandSize = Math.min(size[1] / 2, Math.max(moveHandleHeight, 10));\n      actualMoveZone = displayables.moveZone = new Rect({\n        invisible: true,\n        shape: {\n          y: size[1] - moveZoneExpandSize,\n          height: moveHandleHeight + moveZoneExpandSize\n        }\n      });\n      actualMoveZone.on('mouseover', function () {\n        api.enterEmphasis(moveHandle_1);\n      }).on('mouseout', function () {\n        api.leaveEmphasis(moveHandle_1);\n      });\n      sliderGroup.add(moveHandle_1);\n      sliderGroup.add(moveHandleIcon);\n      sliderGroup.add(actualMoveZone);\n    }\n\n    actualMoveZone.attr({\n      draggable: true,\n      cursor: getCursor(this._orient),\n      drift: bind(this._onDragMove, this, 'all'),\n      ondragstart: bind(this._showDataInfo, this, true),\n      ondragend: bind(this._onDragEnd, this),\n      onmouseover: bind(this._showDataInfo, this, true),\n      onmouseout: bind(this._showDataInfo, this, false)\n    });\n  };\n\n  SliderZoomView.prototype._resetInterval = function () {\n    var range = this._range = this.dataZoomModel.getPercentRange();\n\n    var viewExtent = this._getViewExtent();\n\n    this._handleEnds = [linearMap(range[0], [0, 100], viewExtent, true), linearMap(range[1], [0, 100], viewExtent, true)];\n  };\n\n  SliderZoomView.prototype._updateInterval = function (handleIndex, delta) {\n    var dataZoomModel = this.dataZoomModel;\n    var handleEnds = this._handleEnds;\n\n    var viewExtend = this._getViewExtent();\n\n    var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();\n    var percentExtent = [0, 100];\n    sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null);\n    var lastRange = this._range;\n    var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]);\n    return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];\n  };\n\n  SliderZoomView.prototype._updateView = function (nonRealtime) {\n    var displaybles = this._displayables;\n    var handleEnds = this._handleEnds;\n    var handleInterval = asc(handleEnds.slice());\n    var size = this._size;\n    each([0, 1], function (handleIndex) {\n      // Handles\n      var handle = displaybles.handles[handleIndex];\n      var handleHeight = this._handleHeight;\n      handle.attr({\n        scaleX: handleHeight / 2,\n        scaleY: handleHeight / 2,\n        // This is a trick, by adding an extra tiny offset to let the default handle's end point align to the drag window.\n        // NOTE: It may affect some custom shapes a bit. But we prefer to have better result by default.\n        x: handleEnds[handleIndex] + (handleIndex ? -1 : 1),\n        y: size[1] / 2 - handleHeight / 2\n      });\n    }, this); // Filler\n\n    displaybles.filler.setShape({\n      x: handleInterval[0],\n      y: 0,\n      width: handleInterval[1] - handleInterval[0],\n      height: size[1]\n    });\n    var viewExtent = {\n      x: handleInterval[0],\n      width: handleInterval[1] - handleInterval[0]\n    }; // Move handle\n\n    if (displaybles.moveHandle) {\n      displaybles.moveHandle.setShape(viewExtent);\n      displaybles.moveZone.setShape(viewExtent); // Force update path on the invisible object\n\n      displaybles.moveZone.getBoundingRect();\n      displaybles.moveHandleIcon && displaybles.moveHandleIcon.attr('x', viewExtent.x + viewExtent.width / 2);\n    } // update clip path of shadow.\n\n\n    var dataShadowSegs = displaybles.dataShadowSegs;\n    var segIntervals = [0, handleInterval[0], handleInterval[1], size[0]];\n\n    for (var i = 0; i < dataShadowSegs.length; i++) {\n      var segGroup = dataShadowSegs[i];\n      var clipPath = segGroup.getClipPath();\n\n      if (!clipPath) {\n        clipPath = new Rect();\n        segGroup.setClipPath(clipPath);\n      }\n\n      clipPath.setShape({\n        x: segIntervals[i],\n        y: 0,\n        width: segIntervals[i + 1] - segIntervals[i],\n        height: size[1]\n      });\n    }\n\n    this._updateDataInfo(nonRealtime);\n  };\n\n  SliderZoomView.prototype._updateDataInfo = function (nonRealtime) {\n    var dataZoomModel = this.dataZoomModel;\n    var displaybles = this._displayables;\n    var handleLabels = displaybles.handleLabels;\n    var orient = this._orient;\n    var labelTexts = ['', '']; // FIXME\n    // date型，支持formatter，autoformatter（ec2 date.getAutoFormatter）\n\n    if (dataZoomModel.get('showDetail')) {\n      var axisProxy = dataZoomModel.findRepresentativeAxisProxy();\n\n      if (axisProxy) {\n        var axis = axisProxy.getAxisModel().axis;\n        var range = this._range;\n        var dataInterval = nonRealtime // See #4434, data and axis are not processed and reset yet in non-realtime mode.\n        ? axisProxy.calculateDataWindow({\n          start: range[0],\n          end: range[1]\n        }).valueWindow : axisProxy.getDataValueWindow();\n        labelTexts = [this._formatLabel(dataInterval[0], axis), this._formatLabel(dataInterval[1], axis)];\n      }\n    }\n\n    var orderedHandleEnds = asc(this._handleEnds.slice());\n    setLabel.call(this, 0);\n    setLabel.call(this, 1);\n\n    function setLabel(handleIndex) {\n      // Label\n      // Text should not transform by barGroup.\n      // Ignore handlers transform\n      var barTransform = getTransform(displaybles.handles[handleIndex].parent, this.group);\n      var direction = transformDirection(handleIndex === 0 ? 'right' : 'left', barTransform);\n      var offset = this._handleWidth / 2 + LABEL_GAP;\n      var textPoint = applyTransform$1([orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), this._size[1] / 2], barTransform);\n      handleLabels[handleIndex].setStyle({\n        x: textPoint[0],\n        y: textPoint[1],\n        verticalAlign: orient === HORIZONTAL ? 'middle' : direction,\n        align: orient === HORIZONTAL ? direction : 'center',\n        text: labelTexts[handleIndex]\n      });\n    }\n  };\n\n  SliderZoomView.prototype._formatLabel = function (value, axis) {\n    var dataZoomModel = this.dataZoomModel;\n    var labelFormatter = dataZoomModel.get('labelFormatter');\n    var labelPrecision = dataZoomModel.get('labelPrecision');\n\n    if (labelPrecision == null || labelPrecision === 'auto') {\n      labelPrecision = axis.getPixelPrecision();\n    }\n\n    var valueStr = value == null || isNaN(value) ? '' // FIXME Glue code\n    : axis.type === 'category' || axis.type === 'time' ? axis.scale.getLabel({\n      value: Math.round(value)\n    }) // param of toFixed should less then 20.\n    : value.toFixed(Math.min(labelPrecision, 20));\n    return isFunction(labelFormatter) ? labelFormatter(value, valueStr) : isString(labelFormatter) ? labelFormatter.replace('{value}', valueStr) : valueStr;\n  };\n  /**\n   * @param showOrHide true: show, false: hide\n   */\n\n\n  SliderZoomView.prototype._showDataInfo = function (showOrHide) {\n    // Always show when drgging.\n    showOrHide = this._dragging || showOrHide;\n    var displayables = this._displayables;\n    var handleLabels = displayables.handleLabels;\n    handleLabels[0].attr('invisible', !showOrHide);\n    handleLabels[1].attr('invisible', !showOrHide); // Highlight move handle\n\n    displayables.moveHandle && this.api[showOrHide ? 'enterEmphasis' : 'leaveEmphasis'](displayables.moveHandle, 1);\n  };\n\n  SliderZoomView.prototype._onDragMove = function (handleIndex, dx, dy, event) {\n    this._dragging = true; // For mobile device, prevent screen slider on the button.\n\n    stop(event.event); // Transform dx, dy to bar coordination.\n\n    var barTransform = this._displayables.sliderGroup.getLocalTransform();\n\n    var vertex = applyTransform$1([dx, dy], barTransform, true);\n\n    var changed = this._updateInterval(handleIndex, vertex[0]);\n\n    var realtime = this.dataZoomModel.get('realtime');\n\n    this._updateView(!realtime); // Avoid dispatch dataZoom repeatly but range not changed,\n    // which cause bad visual effect when progressive enabled.\n\n\n    changed && realtime && this._dispatchZoomAction(true);\n  };\n\n  SliderZoomView.prototype._onDragEnd = function () {\n    this._dragging = false;\n\n    this._showDataInfo(false); // While in realtime mode and stream mode, dispatch action when\n    // drag end will cause the whole view rerender, which is unnecessary.\n\n\n    var realtime = this.dataZoomModel.get('realtime');\n    !realtime && this._dispatchZoomAction(false);\n  };\n\n  SliderZoomView.prototype._onClickPanel = function (e) {\n    var size = this._size;\n\n    var localPoint = this._displayables.sliderGroup.transformCoordToLocal(e.offsetX, e.offsetY);\n\n    if (localPoint[0] < 0 || localPoint[0] > size[0] || localPoint[1] < 0 || localPoint[1] > size[1]) {\n      return;\n    }\n\n    var handleEnds = this._handleEnds;\n    var center = (handleEnds[0] + handleEnds[1]) / 2;\n\n    var changed = this._updateInterval('all', localPoint[0] - center);\n\n    this._updateView();\n\n    changed && this._dispatchZoomAction(false);\n  };\n\n  SliderZoomView.prototype._onBrushStart = function (e) {\n    var x = e.offsetX;\n    var y = e.offsetY;\n    this._brushStart = new Point(x, y);\n    this._brushing = true;\n    this._brushStartTime = +new Date(); // this._updateBrushRect(x, y);\n  };\n\n  SliderZoomView.prototype._onBrushEnd = function (e) {\n    if (!this._brushing) {\n      return;\n    }\n\n    var brushRect = this._displayables.brushRect;\n    this._brushing = false;\n\n    if (!brushRect) {\n      return;\n    }\n\n    brushRect.attr('ignore', true);\n    var brushShape = brushRect.shape;\n    var brushEndTime = +new Date(); // console.log(brushEndTime - this._brushStartTime);\n\n    if (brushEndTime - this._brushStartTime < 200 && Math.abs(brushShape.width) < 5) {\n      // Will treat it as a click\n      return;\n    }\n\n    var viewExtend = this._getViewExtent();\n\n    var percentExtent = [0, 100];\n    this._range = asc([linearMap(brushShape.x, viewExtend, percentExtent, true), linearMap(brushShape.x + brushShape.width, viewExtend, percentExtent, true)]);\n    this._handleEnds = [brushShape.x, brushShape.x + brushShape.width];\n\n    this._updateView();\n\n    this._dispatchZoomAction(false);\n  };\n\n  SliderZoomView.prototype._onBrush = function (e) {\n    if (this._brushing) {\n      // For mobile device, prevent screen slider on the button.\n      stop(e.event);\n\n      this._updateBrushRect(e.offsetX, e.offsetY);\n    }\n  };\n\n  SliderZoomView.prototype._updateBrushRect = function (mouseX, mouseY) {\n    var displayables = this._displayables;\n    var dataZoomModel = this.dataZoomModel;\n    var brushRect = displayables.brushRect;\n\n    if (!brushRect) {\n      brushRect = displayables.brushRect = new Rect$2({\n        silent: true,\n        style: dataZoomModel.getModel('brushStyle').getItemStyle()\n      });\n      displayables.sliderGroup.add(brushRect);\n    }\n\n    brushRect.attr('ignore', false);\n    var brushStart = this._brushStart;\n    var sliderGroup = this._displayables.sliderGroup;\n    var endPoint = sliderGroup.transformCoordToLocal(mouseX, mouseY);\n    var startPoint = sliderGroup.transformCoordToLocal(brushStart.x, brushStart.y);\n    var size = this._size;\n    endPoint[0] = Math.max(Math.min(size[0], endPoint[0]), 0);\n    brushRect.setShape({\n      x: startPoint[0],\n      y: 0,\n      width: endPoint[0] - startPoint[0],\n      height: size[1]\n    });\n  };\n  /**\n   * This action will be throttled.\n   */\n\n\n  SliderZoomView.prototype._dispatchZoomAction = function (realtime) {\n    var range = this._range;\n    this.api.dispatchAction({\n      type: 'dataZoom',\n      from: this.uid,\n      dataZoomId: this.dataZoomModel.id,\n      animation: realtime ? REALTIME_ANIMATION_CONFIG : null,\n      start: range[0],\n      end: range[1]\n    });\n  };\n\n  SliderZoomView.prototype._findCoordRect = function () {\n    // Find the grid coresponding to the first axis referred by dataZoom.\n    var rect;\n    var coordSysInfoList = collectReferCoordSysModelInfo(this.dataZoomModel).infoList;\n\n    if (!rect && coordSysInfoList.length) {\n      var coordSys = coordSysInfoList[0].model.coordinateSystem;\n      rect = coordSys.getRect && coordSys.getRect();\n    }\n\n    if (!rect) {\n      var width = this.api.getWidth();\n      var height = this.api.getHeight();\n      rect = {\n        x: width * 0.2,\n        y: height * 0.2,\n        width: width * 0.6,\n        height: height * 0.6\n      };\n    }\n\n    return rect;\n  };\n\n  SliderZoomView.type = 'dataZoom.slider';\n  return SliderZoomView;\n}(DataZoomView);\n\nfunction getOtherDim(thisDim) {\n  // FIXME\n  // 这个逻辑和getOtherAxis里一致，但是写在这里是否不好\n  var map = {\n    x: 'y',\n    y: 'x',\n    radius: 'angle',\n    angle: 'radius'\n  };\n  return map[thisDim];\n}\n\nfunction getCursor(orient) {\n  return orient === 'vertical' ? 'ns-resize' : 'ew-resize';\n}\n\nfunction install$L(registers) {\n  registers.registerComponentModel(SliderZoomModel);\n  registers.registerComponentView(SliderZoomView);\n  installCommon(registers);\n}\n\nfunction install$M(registers) {\n  use(install$K);\n  use(install$L); // Do not install './dataZoomSelect',\n  // since it only work for toolbox dataZoom.\n}\n\nvar visualDefault = {\n  /**\n   * @public\n   */\n  get: function (visualType, key, isCategory) {\n    var value = clone((defaultOption$1[visualType] || {})[key]);\n    return isCategory ? isArray(value) ? value[value.length - 1] : value : value;\n  }\n};\nvar defaultOption$1 = {\n  color: {\n    active: ['#006edd', '#e0ffff'],\n    inactive: ['rgba(0,0,0,0)']\n  },\n  colorHue: {\n    active: [0, 360],\n    inactive: [0, 0]\n  },\n  colorSaturation: {\n    active: [0.3, 1],\n    inactive: [0, 0]\n  },\n  colorLightness: {\n    active: [0.9, 0.5],\n    inactive: [0, 0]\n  },\n  colorAlpha: {\n    active: [0.3, 1],\n    inactive: [0, 0]\n  },\n  opacity: {\n    active: [0.3, 1],\n    inactive: [0, 0]\n  },\n  symbol: {\n    active: ['circle', 'roundRect', 'diamond'],\n    inactive: ['none']\n  },\n  symbolSize: {\n    active: [10, 50],\n    inactive: [0, 0]\n  }\n};\n\nvar mapVisual$1 = VisualMapping.mapVisual;\nvar eachVisual = VisualMapping.eachVisual;\nvar isArray$1 = isArray;\nvar each$d = each;\nvar asc$2 = asc;\nvar linearMap$1 = linearMap;\n\nvar VisualMapModel =\n/** @class */\nfunction (_super) {\n  __extends(VisualMapModel, _super);\n\n  function VisualMapModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = VisualMapModel.type;\n    _this.stateList = ['inRange', 'outOfRange'];\n    _this.replacableOptionKeys = ['inRange', 'outOfRange', 'target', 'controller', 'color'];\n    _this.layoutMode = {\n      type: 'box',\n      ignoreSize: true\n    };\n    /**\n     * [lowerBound, upperBound]\n     */\n\n    _this.dataBound = [-Infinity, Infinity];\n    _this.targetVisuals = {};\n    _this.controllerVisuals = {};\n    return _this;\n  }\n\n  VisualMapModel.prototype.init = function (option, parentModel, ecModel) {\n    this.mergeDefaultAndTheme(option, ecModel);\n  };\n  /**\n   * @protected\n   */\n\n\n  VisualMapModel.prototype.optionUpdated = function (newOption, isInit) {\n    var thisOption = this.option;\n    !isInit && replaceVisualOption(thisOption, newOption, this.replacableOptionKeys);\n    this.textStyleModel = this.getModel('textStyle');\n    this.resetItemSize();\n    this.completeVisualOption();\n  };\n  /**\n   * @protected\n   */\n\n\n  VisualMapModel.prototype.resetVisual = function (supplementVisualOption) {\n    var stateList = this.stateList;\n    supplementVisualOption = bind(supplementVisualOption, this);\n    this.controllerVisuals = createVisualMappings(this.option.controller, stateList, supplementVisualOption);\n    this.targetVisuals = createVisualMappings(this.option.target, stateList, supplementVisualOption);\n  };\n  /**\n   * @public\n   */\n\n\n  VisualMapModel.prototype.getItemSymbol = function () {\n    return null;\n  };\n  /**\n   * @protected\n   * @return {Array.<number>} An array of series indices.\n   */\n\n\n  VisualMapModel.prototype.getTargetSeriesIndices = function () {\n    var optionSeriesIndex = this.option.seriesIndex;\n    var seriesIndices = [];\n\n    if (optionSeriesIndex == null || optionSeriesIndex === 'all') {\n      this.ecModel.eachSeries(function (seriesModel, index) {\n        seriesIndices.push(index);\n      });\n    } else {\n      seriesIndices = normalizeToArray(optionSeriesIndex);\n    }\n\n    return seriesIndices;\n  };\n  /**\n   * @public\n   */\n\n\n  VisualMapModel.prototype.eachTargetSeries = function (callback, context) {\n    each(this.getTargetSeriesIndices(), function (seriesIndex) {\n      var seriesModel = this.ecModel.getSeriesByIndex(seriesIndex);\n\n      if (seriesModel) {\n        callback.call(context, seriesModel);\n      }\n    }, this);\n  };\n  /**\n   * @pubilc\n   */\n\n\n  VisualMapModel.prototype.isTargetSeries = function (seriesModel) {\n    var is = false;\n    this.eachTargetSeries(function (model) {\n      model === seriesModel && (is = true);\n    });\n    return is;\n  };\n  /**\n   * @example\n   * this.formatValueText(someVal); // format single numeric value to text.\n   * this.formatValueText(someVal, true); // format single category value to text.\n   * this.formatValueText([min, max]); // format numeric min-max to text.\n   * this.formatValueText([this.dataBound[0], max]); // using data lower bound.\n   * this.formatValueText([min, this.dataBound[1]]); // using data upper bound.\n   *\n   * @param value Real value, or this.dataBound[0 or 1].\n   * @param isCategory Only available when value is number.\n   * @param edgeSymbols Open-close symbol when value is interval.\n   * @protected\n   */\n\n\n  VisualMapModel.prototype.formatValueText = function (value, isCategory, edgeSymbols) {\n    var option = this.option;\n    var precision = option.precision;\n    var dataBound = this.dataBound;\n    var formatter = option.formatter;\n    var isMinMax;\n    edgeSymbols = edgeSymbols || ['<', '>'];\n\n    if (isArray(value)) {\n      value = value.slice();\n      isMinMax = true;\n    }\n\n    var textValue = isCategory ? value // Value is string when isCategory\n    : isMinMax ? [toFixed(value[0]), toFixed(value[1])] : toFixed(value);\n\n    if (isString(formatter)) {\n      return formatter.replace('{value}', isMinMax ? textValue[0] : textValue).replace('{value2}', isMinMax ? textValue[1] : textValue);\n    } else if (isFunction(formatter)) {\n      return isMinMax ? formatter(value[0], value[1]) : formatter(value);\n    }\n\n    if (isMinMax) {\n      if (value[0] === dataBound[0]) {\n        return edgeSymbols[0] + ' ' + textValue[1];\n      } else if (value[1] === dataBound[1]) {\n        return edgeSymbols[1] + ' ' + textValue[0];\n      } else {\n        return textValue[0] + ' - ' + textValue[1];\n      }\n    } else {\n      // Format single value (includes category case).\n      return textValue;\n    }\n\n    function toFixed(val) {\n      return val === dataBound[0] ? 'min' : val === dataBound[1] ? 'max' : (+val).toFixed(Math.min(precision, 20));\n    }\n  };\n  /**\n   * @protected\n   */\n\n\n  VisualMapModel.prototype.resetExtent = function () {\n    var thisOption = this.option; // Can not calculate data extent by data here.\n    // Because series and data may be modified in processing stage.\n    // So we do not support the feature \"auto min/max\".\n\n    var extent = asc$2([thisOption.min, thisOption.max]);\n    this._dataExtent = extent;\n  };\n  /**\n   * PENDING:\n   * delete this method if no outer usage.\n   *\n   * Return  Concrete dimention. If return null/undefined, no dimension used.\n   */\n  // getDataDimension(data: SeriesData) {\n  //     const optDim = this.option.dimension;\n  //     if (optDim != null) {\n  //         return data.getDimension(optDim);\n  //     }\n  //     const dimNames = data.dimensions;\n  //     for (let i = dimNames.length - 1; i >= 0; i--) {\n  //         const dimName = dimNames[i];\n  //         const dimInfo = data.getDimensionInfo(dimName);\n  //         if (!dimInfo.isCalculationCoord) {\n  //             return dimName;\n  //         }\n  //     }\n  // }\n\n\n  VisualMapModel.prototype.getDataDimensionIndex = function (data) {\n    var optDim = this.option.dimension;\n\n    if (optDim != null) {\n      return data.getDimensionIndex(optDim);\n    }\n\n    var dimNames = data.dimensions;\n\n    for (var i = dimNames.length - 1; i >= 0; i--) {\n      var dimName = dimNames[i];\n      var dimInfo = data.getDimensionInfo(dimName);\n\n      if (!dimInfo.isCalculationCoord) {\n        return dimInfo.storeDimIndex;\n      }\n    }\n  };\n\n  VisualMapModel.prototype.getExtent = function () {\n    return this._dataExtent.slice();\n  };\n\n  VisualMapModel.prototype.completeVisualOption = function () {\n    var ecModel = this.ecModel;\n    var thisOption = this.option;\n    var base = {\n      inRange: thisOption.inRange,\n      outOfRange: thisOption.outOfRange\n    };\n    var target = thisOption.target || (thisOption.target = {});\n    var controller = thisOption.controller || (thisOption.controller = {});\n    merge(target, base); // Do not override\n\n    merge(controller, base); // Do not override\n\n    var isCategory = this.isCategory();\n    completeSingle.call(this, target);\n    completeSingle.call(this, controller);\n    completeInactive.call(this, target, 'inRange', 'outOfRange'); // completeInactive.call(this, target, 'outOfRange', 'inRange');\n\n    completeController.call(this, controller);\n\n    function completeSingle(base) {\n      // Compatible with ec2 dataRange.color.\n      // The mapping order of dataRange.color is: [high value, ..., low value]\n      // whereas inRange.color and outOfRange.color is [low value, ..., high value]\n      // Notice: ec2 has no inverse.\n      if (isArray$1(thisOption.color) // If there has been inRange: {symbol: ...}, adding color is a mistake.\n      // So adding color only when no inRange defined.\n      && !base.inRange) {\n        base.inRange = {\n          color: thisOption.color.slice().reverse()\n        };\n      } // Compatible with previous logic, always give a defautl color, otherwise\n      // simple config with no inRange and outOfRange will not work.\n      // Originally we use visualMap.color as the default color, but setOption at\n      // the second time the default color will be erased. So we change to use\n      // constant DEFAULT_COLOR.\n      // If user do not want the default color, set inRange: {color: null}.\n\n\n      base.inRange = base.inRange || {\n        color: ecModel.get('gradientColor')\n      };\n    }\n\n    function completeInactive(base, stateExist, stateAbsent) {\n      var optExist = base[stateExist];\n      var optAbsent = base[stateAbsent];\n\n      if (optExist && !optAbsent) {\n        optAbsent = base[stateAbsent] = {};\n        each$d(optExist, function (visualData, visualType) {\n          if (!VisualMapping.isValidType(visualType)) {\n            return;\n          }\n\n          var defa = visualDefault.get(visualType, 'inactive', isCategory);\n\n          if (defa != null) {\n            optAbsent[visualType] = defa; // Compatibable with ec2:\n            // Only inactive color to rgba(0,0,0,0) can not\n            // make label transparent, so use opacity also.\n\n            if (visualType === 'color' && !optAbsent.hasOwnProperty('opacity') && !optAbsent.hasOwnProperty('colorAlpha')) {\n              optAbsent.opacity = [0, 0];\n            }\n          }\n        });\n      }\n    }\n\n    function completeController(controller) {\n      var symbolExists = (controller.inRange || {}).symbol || (controller.outOfRange || {}).symbol;\n      var symbolSizeExists = (controller.inRange || {}).symbolSize || (controller.outOfRange || {}).symbolSize;\n      var inactiveColor = this.get('inactiveColor');\n      var itemSymbol = this.getItemSymbol();\n      var defaultSymbol = itemSymbol || 'roundRect';\n      each$d(this.stateList, function (state) {\n        var itemSize = this.itemSize;\n        var visuals = controller[state]; // Set inactive color for controller if no other color\n        // attr (like colorAlpha) specified.\n\n        if (!visuals) {\n          visuals = controller[state] = {\n            color: isCategory ? inactiveColor : [inactiveColor]\n          };\n        } // Consistent symbol and symbolSize if not specified.\n\n\n        if (visuals.symbol == null) {\n          visuals.symbol = symbolExists && clone(symbolExists) || (isCategory ? defaultSymbol : [defaultSymbol]);\n        }\n\n        if (visuals.symbolSize == null) {\n          visuals.symbolSize = symbolSizeExists && clone(symbolSizeExists) || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]);\n        } // Filter none\n\n\n        visuals.symbol = mapVisual$1(visuals.symbol, function (symbol) {\n          return symbol === 'none' ? defaultSymbol : symbol;\n        }); // Normalize symbolSize\n\n        var symbolSize = visuals.symbolSize;\n\n        if (symbolSize != null) {\n          var max_1 = -Infinity; // symbolSize can be object when categories defined.\n\n          eachVisual(symbolSize, function (value) {\n            value > max_1 && (max_1 = value);\n          });\n          visuals.symbolSize = mapVisual$1(symbolSize, function (value) {\n            return linearMap$1(value, [0, max_1], [0, itemSize[0]], true);\n          });\n        }\n      }, this);\n    }\n  };\n\n  VisualMapModel.prototype.resetItemSize = function () {\n    this.itemSize = [parseFloat(this.get('itemWidth')), parseFloat(this.get('itemHeight'))];\n  };\n\n  VisualMapModel.prototype.isCategory = function () {\n    return !!this.option.categories;\n  };\n  /**\n   * @public\n   * @abstract\n   */\n\n\n  VisualMapModel.prototype.setSelected = function (selected) {};\n\n  VisualMapModel.prototype.getSelected = function () {\n    return null;\n  };\n  /**\n   * @public\n   * @abstract\n   */\n\n\n  VisualMapModel.prototype.getValueState = function (value) {\n    return null;\n  };\n  /**\n   * FIXME\n   * Do not publish to thirt-part-dev temporarily\n   * util the interface is stable. (Should it return\n   * a function but not visual meta?)\n   *\n   * @pubilc\n   * @abstract\n   * @param getColorVisual\n   *        params: value, valueState\n   *        return: color\n   * @return {Object} visualMeta\n   *        should includes {stops, outerColors}\n   *        outerColor means [colorBeyondMinValue, colorBeyondMaxValue]\n   */\n\n\n  VisualMapModel.prototype.getVisualMeta = function (getColorVisual) {\n    return null;\n  };\n\n  VisualMapModel.type = 'visualMap';\n  VisualMapModel.dependencies = ['series'];\n  VisualMapModel.defaultOption = {\n    show: true,\n    // zlevel: 0,\n    z: 4,\n    seriesIndex: 'all',\n    min: 0,\n    max: 200,\n    left: 0,\n    right: null,\n    top: null,\n    bottom: 0,\n    itemWidth: null,\n    itemHeight: null,\n    inverse: false,\n    orient: 'vertical',\n    backgroundColor: 'rgba(0,0,0,0)',\n    borderColor: '#ccc',\n    contentColor: '#5793f3',\n    inactiveColor: '#aaa',\n    borderWidth: 0,\n    padding: 5,\n    // 接受数组分别设定上右下左边距，同css\n    textGap: 10,\n    precision: 0,\n    textStyle: {\n      color: '#333' // 值域文字颜色\n\n    }\n  };\n  return VisualMapModel;\n}(ComponentModel);\n\nvar DEFAULT_BAR_BOUND = [20, 140];\n\nvar ContinuousModel =\n/** @class */\nfunction (_super) {\n  __extends(ContinuousModel, _super);\n\n  function ContinuousModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ContinuousModel.type;\n    return _this;\n  }\n  /**\n   * @override\n   */\n\n\n  ContinuousModel.prototype.optionUpdated = function (newOption, isInit) {\n    _super.prototype.optionUpdated.apply(this, arguments);\n\n    this.resetExtent();\n    this.resetVisual(function (mappingOption) {\n      mappingOption.mappingMethod = 'linear';\n      mappingOption.dataExtent = this.getExtent();\n    });\n\n    this._resetRange();\n  };\n  /**\n   * @protected\n   * @override\n   */\n\n\n  ContinuousModel.prototype.resetItemSize = function () {\n    _super.prototype.resetItemSize.apply(this, arguments);\n\n    var itemSize = this.itemSize;\n    (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]);\n    (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]);\n  };\n  /**\n   * @private\n   */\n\n\n  ContinuousModel.prototype._resetRange = function () {\n    var dataExtent = this.getExtent();\n    var range = this.option.range;\n\n    if (!range || range.auto) {\n      // `range` should always be array (so we dont use other\n      // value like 'auto') for user-friend. (consider getOption).\n      dataExtent.auto = 1;\n      this.option.range = dataExtent;\n    } else if (isArray(range)) {\n      if (range[0] > range[1]) {\n        range.reverse();\n      }\n\n      range[0] = Math.max(range[0], dataExtent[0]);\n      range[1] = Math.min(range[1], dataExtent[1]);\n    }\n  };\n  /**\n   * @protected\n   * @override\n   */\n\n\n  ContinuousModel.prototype.completeVisualOption = function () {\n    _super.prototype.completeVisualOption.apply(this, arguments);\n\n    each(this.stateList, function (state) {\n      var symbolSize = this.option.controller[state].symbolSize;\n\n      if (symbolSize && symbolSize[0] !== symbolSize[1]) {\n        symbolSize[0] = symbolSize[1] / 3; // For good looking.\n      }\n    }, this);\n  };\n  /**\n   * @override\n   */\n\n\n  ContinuousModel.prototype.setSelected = function (selected) {\n    this.option.range = selected.slice();\n\n    this._resetRange();\n  };\n  /**\n   * @public\n   */\n\n\n  ContinuousModel.prototype.getSelected = function () {\n    var dataExtent = this.getExtent();\n    var dataInterval = asc((this.get('range') || []).slice()); // Clamp\n\n    dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]);\n    dataInterval[1] > dataExtent[1] && (dataInterval[1] = dataExtent[1]);\n    dataInterval[0] < dataExtent[0] && (dataInterval[0] = dataExtent[0]);\n    dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]);\n    return dataInterval;\n  };\n  /**\n   * @override\n   */\n\n\n  ContinuousModel.prototype.getValueState = function (value) {\n    var range = this.option.range;\n    var dataExtent = this.getExtent(); // When range[0] === dataExtent[0], any value larger than dataExtent[0] maps to 'inRange'.\n    // range[1] is processed likewise.\n\n    return (range[0] <= dataExtent[0] || range[0] <= value) && (range[1] >= dataExtent[1] || value <= range[1]) ? 'inRange' : 'outOfRange';\n  };\n\n  ContinuousModel.prototype.findTargetDataIndices = function (range) {\n    var result = [];\n    this.eachTargetSeries(function (seriesModel) {\n      var dataIndices = [];\n      var data = seriesModel.getData();\n      data.each(this.getDataDimensionIndex(data), function (value, dataIndex) {\n        range[0] <= value && value <= range[1] && dataIndices.push(dataIndex);\n      }, this);\n      result.push({\n        seriesId: seriesModel.id,\n        dataIndex: dataIndices\n      });\n    }, this);\n    return result;\n  };\n  /**\n   * @implement\n   */\n\n\n  ContinuousModel.prototype.getVisualMeta = function (getColorVisual) {\n    var oVals = getColorStopValues(this, 'outOfRange', this.getExtent());\n    var iVals = getColorStopValues(this, 'inRange', this.option.range.slice());\n    var stops = [];\n\n    function setStop(value, valueState) {\n      stops.push({\n        value: value,\n        color: getColorVisual(value, valueState)\n      });\n    } // Format to: outOfRange -- inRange -- outOfRange.\n\n\n    var iIdx = 0;\n    var oIdx = 0;\n    var iLen = iVals.length;\n    var oLen = oVals.length;\n\n    for (; oIdx < oLen && (!iVals.length || oVals[oIdx] <= iVals[0]); oIdx++) {\n      // If oVal[oIdx] === iVals[iIdx], oVal[oIdx] should be ignored.\n      if (oVals[oIdx] < iVals[iIdx]) {\n        setStop(oVals[oIdx], 'outOfRange');\n      }\n    }\n\n    for (var first = 1; iIdx < iLen; iIdx++, first = 0) {\n      // If range is full, value beyond min, max will be clamped.\n      // make a singularity\n      first && stops.length && setStop(iVals[iIdx], 'outOfRange');\n      setStop(iVals[iIdx], 'inRange');\n    }\n\n    for (var first = 1; oIdx < oLen; oIdx++) {\n      if (!iVals.length || iVals[iVals.length - 1] < oVals[oIdx]) {\n        // make a singularity\n        if (first) {\n          stops.length && setStop(stops[stops.length - 1].value, 'outOfRange');\n          first = 0;\n        }\n\n        setStop(oVals[oIdx], 'outOfRange');\n      }\n    }\n\n    var stopsLen = stops.length;\n    return {\n      stops: stops,\n      outerColors: [stopsLen ? stops[0].color : 'transparent', stopsLen ? stops[stopsLen - 1].color : 'transparent']\n    };\n  };\n\n  ContinuousModel.type = 'visualMap.continuous';\n  ContinuousModel.defaultOption = inheritDefaultOption(VisualMapModel.defaultOption, {\n    align: 'auto',\n    calculable: false,\n    hoverLink: true,\n    realtime: true,\n    handleIcon: 'path://M-11.39,9.77h0a3.5,3.5,0,0,1-3.5,3.5h-22a3.5,3.5,0,0,1-3.5-3.5h0a3.5,3.5,0,0,1,3.5-3.5h22A3.5,3.5,0,0,1-11.39,9.77Z',\n    handleSize: '120%',\n    handleStyle: {\n      borderColor: '#fff',\n      borderWidth: 1\n    },\n    indicatorIcon: 'circle',\n    indicatorSize: '50%',\n    indicatorStyle: {\n      borderColor: '#fff',\n      borderWidth: 2,\n      shadowBlur: 2,\n      shadowOffsetX: 1,\n      shadowOffsetY: 1,\n      shadowColor: 'rgba(0,0,0,0.2)'\n    } // emphasis: {\n    //     handleStyle: {\n    //         shadowBlur: 3,\n    //         shadowOffsetX: 1,\n    //         shadowOffsetY: 1,\n    //         shadowColor: 'rgba(0,0,0,0.2)'\n    //     }\n    // }\n\n  });\n  return ContinuousModel;\n}(VisualMapModel);\n\nfunction getColorStopValues(visualMapModel, valueState, dataExtent) {\n  if (dataExtent[0] === dataExtent[1]) {\n    return dataExtent.slice();\n  } // When using colorHue mapping, it is not linear color any more.\n  // Moreover, canvas gradient seems not to be accurate linear.\n  // FIXME\n  // Should be arbitrary value 100? or based on pixel size?\n\n\n  var count = 200;\n  var step = (dataExtent[1] - dataExtent[0]) / count;\n  var value = dataExtent[0];\n  var stopValues = [];\n\n  for (var i = 0; i <= count && value < dataExtent[1]; i++) {\n    stopValues.push(value);\n    value += step;\n  }\n\n  stopValues.push(dataExtent[1]);\n  return stopValues;\n}\n\nvar VisualMapView =\n/** @class */\nfunction (_super) {\n  __extends(VisualMapView, _super);\n\n  function VisualMapView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = VisualMapView.type;\n    _this.autoPositionValues = {\n      left: 1,\n      right: 1,\n      top: 1,\n      bottom: 1\n    };\n    return _this;\n  }\n\n  VisualMapView.prototype.init = function (ecModel, api) {\n    this.ecModel = ecModel;\n    this.api = api;\n  };\n  /**\n   * @protected\n   */\n\n\n  VisualMapView.prototype.render = function (visualMapModel, ecModel, api, payload // TODO: TYPE\n  ) {\n    this.visualMapModel = visualMapModel;\n\n    if (visualMapModel.get('show') === false) {\n      this.group.removeAll();\n      return;\n    }\n\n    this.doRender(visualMapModel, ecModel, api, payload);\n  };\n  /**\n   * @protected\n   */\n\n\n  VisualMapView.prototype.renderBackground = function (group) {\n    var visualMapModel = this.visualMapModel;\n    var padding = normalizeCssArray$1(visualMapModel.get('padding') || 0);\n    var rect = group.getBoundingRect();\n    group.add(new Rect({\n      z2: -1,\n      silent: true,\n      shape: {\n        x: rect.x - padding[3],\n        y: rect.y - padding[0],\n        width: rect.width + padding[3] + padding[1],\n        height: rect.height + padding[0] + padding[2]\n      },\n      style: {\n        fill: visualMapModel.get('backgroundColor'),\n        stroke: visualMapModel.get('borderColor'),\n        lineWidth: visualMapModel.get('borderWidth')\n      }\n    }));\n  };\n  /**\n   * @protected\n   * @param targetValue can be Infinity or -Infinity\n   * @param visualCluster Only can be 'color' 'opacity' 'symbol' 'symbolSize'\n   * @param opts\n   * @param opts.forceState Specify state, instead of using getValueState method.\n   * @param opts.convertOpacityToAlpha For color gradient in controller widget.\n   * @return {*} Visual value.\n   */\n\n\n  VisualMapView.prototype.getControllerVisual = function (targetValue, visualCluster, opts) {\n    opts = opts || {};\n    var forceState = opts.forceState;\n    var visualMapModel = this.visualMapModel;\n    var visualObj = {}; // Default values.\n\n    if (visualCluster === 'color') {\n      var defaultColor = visualMapModel.get('contentColor');\n      visualObj.color = defaultColor;\n    }\n\n    function getter(key) {\n      return visualObj[key];\n    }\n\n    function setter(key, value) {\n      visualObj[key] = value;\n    }\n\n    var mappings = visualMapModel.controllerVisuals[forceState || visualMapModel.getValueState(targetValue)];\n    var visualTypes = VisualMapping.prepareVisualTypes(mappings);\n    each(visualTypes, function (type) {\n      var visualMapping = mappings[type];\n\n      if (opts.convertOpacityToAlpha && type === 'opacity') {\n        type = 'colorAlpha';\n        visualMapping = mappings.__alphaForOpacity;\n      }\n\n      if (VisualMapping.dependsOn(type, visualCluster)) {\n        visualMapping && visualMapping.applyVisual(targetValue, getter, setter);\n      }\n    });\n    return visualObj[visualCluster];\n  };\n\n  VisualMapView.prototype.positionGroup = function (group) {\n    var model = this.visualMapModel;\n    var api = this.api;\n    positionElement(group, model.getBoxLayoutParams(), {\n      width: api.getWidth(),\n      height: api.getHeight()\n    });\n  };\n\n  VisualMapView.prototype.doRender = function (visualMapModel, ecModel, api, payload) {};\n\n  VisualMapView.type = 'visualMap';\n  return VisualMapView;\n}(ComponentView);\n\nvar paramsSet = [['left', 'right', 'width'], ['top', 'bottom', 'height']];\n/**\n * @param visualMapModel\n * @param api\n * @param itemSize always [short, long]\n * @return {string} 'left' or 'right' or 'top' or 'bottom'\n */\n\nfunction getItemAlign(visualMapModel, api, itemSize) {\n  var modelOption = visualMapModel.option;\n  var itemAlign = modelOption.align;\n\n  if (itemAlign != null && itemAlign !== 'auto') {\n    return itemAlign;\n  } // Auto decision align.\n\n\n  var ecSize = {\n    width: api.getWidth(),\n    height: api.getHeight()\n  };\n  var realIndex = modelOption.orient === 'horizontal' ? 1 : 0;\n  var reals = paramsSet[realIndex];\n  var fakeValue = [0, null, 10];\n  var layoutInput = {};\n\n  for (var i = 0; i < 3; i++) {\n    layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i];\n    layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]];\n  }\n\n  var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex];\n  var rect = getLayoutRect(layoutInput, ecSize, modelOption.padding);\n  return reals[(rect.margin[rParam[2]] || 0) + rect[rParam[0]] + rect[rParam[1]] * 0.5 < ecSize[rParam[1]] * 0.5 ? 0 : 1];\n}\n/**\n * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and\n * dataIndexInside means filtered index.\n */\n// TODO: TYPE more specified payload types.\n\nfunction makeHighDownBatch(batch, visualMapModel) {\n  each(batch || [], function (batchItem) {\n    if (batchItem.dataIndex != null) {\n      batchItem.dataIndexInside = batchItem.dataIndex;\n      batchItem.dataIndex = null;\n    }\n\n    batchItem.highlightKey = 'visualMap' + (visualMapModel ? visualMapModel.componentIndex : '');\n  });\n  return batch;\n}\n\nvar linearMap$2 = linearMap;\nvar each$e = each;\nvar mathMin$a = Math.min;\nvar mathMax$a = Math.max; // Arbitrary value\n\nvar HOVER_LINK_SIZE = 12;\nvar HOVER_LINK_OUT = 6; // Notice:\n// Any \"interval\" should be by the order of [low, high].\n// \"handle0\" (handleIndex === 0) maps to\n// low data value: this._dataInterval[0] and has low coord.\n// \"handle1\" (handleIndex === 1) maps to\n// high data value: this._dataInterval[1] and has high coord.\n// The logic of transform is implemented in this._createBarGroup.\n\nvar ContinuousView =\n/** @class */\nfunction (_super) {\n  __extends(ContinuousView, _super);\n\n  function ContinuousView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = ContinuousView.type;\n    _this._shapes = {};\n    _this._dataInterval = [];\n    _this._handleEnds = [];\n    _this._hoverLinkDataIndices = [];\n    return _this;\n  }\n\n  ContinuousView.prototype.doRender = function (visualMapModel, ecModel, api, payload) {\n    this._api = api;\n\n    if (!payload || payload.type !== 'selectDataRange' || payload.from !== this.uid) {\n      this._buildView();\n    }\n  };\n\n  ContinuousView.prototype._buildView = function () {\n    this.group.removeAll();\n    var visualMapModel = this.visualMapModel;\n    var thisGroup = this.group;\n    this._orient = visualMapModel.get('orient');\n    this._useHandle = visualMapModel.get('calculable');\n\n    this._resetInterval();\n\n    this._renderBar(thisGroup);\n\n    var dataRangeText = visualMapModel.get('text');\n\n    this._renderEndsText(thisGroup, dataRangeText, 0);\n\n    this._renderEndsText(thisGroup, dataRangeText, 1); // Do this for background size calculation.\n\n\n    this._updateView(true); // After updating view, inner shapes is built completely,\n    // and then background can be rendered.\n\n\n    this.renderBackground(thisGroup); // Real update view\n\n    this._updateView();\n\n    this._enableHoverLinkToSeries();\n\n    this._enableHoverLinkFromSeries();\n\n    this.positionGroup(thisGroup);\n  };\n\n  ContinuousView.prototype._renderEndsText = function (group, dataRangeText, endsIndex) {\n    if (!dataRangeText) {\n      return;\n    } // Compatible with ec2, text[0] map to high value, text[1] map low value.\n\n\n    var text = dataRangeText[1 - endsIndex];\n    text = text != null ? text + '' : '';\n    var visualMapModel = this.visualMapModel;\n    var textGap = visualMapModel.get('textGap');\n    var itemSize = visualMapModel.itemSize;\n    var barGroup = this._shapes.mainGroup;\n\n    var position = this._applyTransform([itemSize[0] / 2, endsIndex === 0 ? -textGap : itemSize[1] + textGap], barGroup);\n\n    var align = this._applyTransform(endsIndex === 0 ? 'bottom' : 'top', barGroup);\n\n    var orient = this._orient;\n    var textStyleModel = this.visualMapModel.textStyleModel;\n    this.group.add(new ZRText({\n      style: createTextStyle(textStyleModel, {\n        x: position[0],\n        y: position[1],\n        verticalAlign: orient === 'horizontal' ? 'middle' : align,\n        align: orient === 'horizontal' ? align : 'center',\n        text: text\n      })\n    }));\n  };\n\n  ContinuousView.prototype._renderBar = function (targetGroup) {\n    var visualMapModel = this.visualMapModel;\n    var shapes = this._shapes;\n    var itemSize = visualMapModel.itemSize;\n    var orient = this._orient;\n    var useHandle = this._useHandle;\n    var itemAlign = getItemAlign(visualMapModel, this.api, itemSize);\n\n    var mainGroup = shapes.mainGroup = this._createBarGroup(itemAlign);\n\n    var gradientBarGroup = new Group();\n    mainGroup.add(gradientBarGroup); // Bar\n\n    gradientBarGroup.add(shapes.outOfRange = createPolygon());\n    gradientBarGroup.add(shapes.inRange = createPolygon(null, useHandle ? getCursor$1(this._orient) : null, bind(this._dragHandle, this, 'all', false), bind(this._dragHandle, this, 'all', true))); // A border radius clip.\n\n    gradientBarGroup.setClipPath(new Rect({\n      shape: {\n        x: 0,\n        y: 0,\n        width: itemSize[0],\n        height: itemSize[1],\n        r: 3\n      }\n    }));\n    var textRect = visualMapModel.textStyleModel.getTextRect('国');\n    var textSize = mathMax$a(textRect.width, textRect.height); // Handle\n\n    if (useHandle) {\n      shapes.handleThumbs = [];\n      shapes.handleLabels = [];\n      shapes.handleLabelPoints = [];\n\n      this._createHandle(visualMapModel, mainGroup, 0, itemSize, textSize, orient);\n\n      this._createHandle(visualMapModel, mainGroup, 1, itemSize, textSize, orient);\n    }\n\n    this._createIndicator(visualMapModel, mainGroup, itemSize, textSize, orient);\n\n    targetGroup.add(mainGroup);\n  };\n\n  ContinuousView.prototype._createHandle = function (visualMapModel, mainGroup, handleIndex, itemSize, textSize, orient) {\n    var onDrift = bind(this._dragHandle, this, handleIndex, false);\n    var onDragEnd = bind(this._dragHandle, this, handleIndex, true);\n    var handleSize = parsePercent(visualMapModel.get('handleSize'), itemSize[0]);\n    var handleThumb = createSymbol(visualMapModel.get('handleIcon'), -handleSize / 2, -handleSize / 2, handleSize, handleSize, null, true);\n    var cursor = getCursor$1(this._orient);\n    handleThumb.attr({\n      cursor: cursor,\n      draggable: true,\n      drift: onDrift,\n      ondragend: onDragEnd,\n      onmousemove: function (e) {\n        stop(e.event);\n      }\n    });\n    handleThumb.x = itemSize[0] / 2;\n    handleThumb.useStyle(visualMapModel.getModel('handleStyle').getItemStyle());\n    handleThumb.setStyle({\n      strokeNoScale: true,\n      strokeFirst: true\n    });\n    handleThumb.style.lineWidth *= 2;\n    handleThumb.ensureState('emphasis').style = visualMapModel.getModel(['emphasis', 'handleStyle']).getItemStyle();\n    setAsHighDownDispatcher(handleThumb, true);\n    mainGroup.add(handleThumb); // Text is always horizontal layout but should not be effected by\n    // transform (orient/inverse). So label is built separately but not\n    // use zrender/graphic/helper/RectText, and is located based on view\n    // group (according to handleLabelPoint) but not barGroup.\n\n    var textStyleModel = this.visualMapModel.textStyleModel;\n    var handleLabel = new ZRText({\n      cursor: cursor,\n      draggable: true,\n      drift: onDrift,\n      onmousemove: function (e) {\n        // Fot mobile devicem, prevent screen slider on the button.\n        stop(e.event);\n      },\n      ondragend: onDragEnd,\n      style: createTextStyle(textStyleModel, {\n        x: 0,\n        y: 0,\n        text: ''\n      })\n    });\n    handleLabel.ensureState('blur').style = {\n      opacity: 0.1\n    };\n    handleLabel.stateTransition = {\n      duration: 200\n    };\n    this.group.add(handleLabel);\n    var handleLabelPoint = [handleSize, 0];\n    var shapes = this._shapes;\n    shapes.handleThumbs[handleIndex] = handleThumb;\n    shapes.handleLabelPoints[handleIndex] = handleLabelPoint;\n    shapes.handleLabels[handleIndex] = handleLabel;\n  };\n\n  ContinuousView.prototype._createIndicator = function (visualMapModel, mainGroup, itemSize, textSize, orient) {\n    var scale = parsePercent(visualMapModel.get('indicatorSize'), itemSize[0]);\n    var indicator = createSymbol(visualMapModel.get('indicatorIcon'), -scale / 2, -scale / 2, scale, scale, null, true);\n    indicator.attr({\n      cursor: 'move',\n      invisible: true,\n      silent: true,\n      x: itemSize[0] / 2\n    });\n    var indicatorStyle = visualMapModel.getModel('indicatorStyle').getItemStyle();\n\n    if (indicator instanceof ZRImage) {\n      var pathStyle = indicator.style;\n      indicator.useStyle(extend({\n        // TODO other properties like x, y ?\n        image: pathStyle.image,\n        x: pathStyle.x,\n        y: pathStyle.y,\n        width: pathStyle.width,\n        height: pathStyle.height\n      }, indicatorStyle));\n    } else {\n      indicator.useStyle(indicatorStyle);\n    }\n\n    mainGroup.add(indicator);\n    var textStyleModel = this.visualMapModel.textStyleModel;\n    var indicatorLabel = new ZRText({\n      silent: true,\n      invisible: true,\n      style: createTextStyle(textStyleModel, {\n        x: 0,\n        y: 0,\n        text: ''\n      })\n    });\n    this.group.add(indicatorLabel);\n    var indicatorLabelPoint = [(orient === 'horizontal' ? textSize / 2 : HOVER_LINK_OUT) + itemSize[0] / 2, 0];\n    var shapes = this._shapes;\n    shapes.indicator = indicator;\n    shapes.indicatorLabel = indicatorLabel;\n    shapes.indicatorLabelPoint = indicatorLabelPoint;\n    this._firstShowIndicator = true;\n  };\n\n  ContinuousView.prototype._dragHandle = function (handleIndex, isEnd, // dx is event from ondragend if isEnd is true. It's not used\n  dx, dy) {\n    if (!this._useHandle) {\n      return;\n    }\n\n    this._dragging = !isEnd;\n\n    if (!isEnd) {\n      // Transform dx, dy to bar coordination.\n      var vertex = this._applyTransform([dx, dy], this._shapes.mainGroup, true);\n\n      this._updateInterval(handleIndex, vertex[1]);\n\n      this._hideIndicator(); // Considering realtime, update view should be executed\n      // before dispatch action.\n\n\n      this._updateView();\n    } // dragEnd do not dispatch action when realtime.\n\n\n    if (isEnd === !this.visualMapModel.get('realtime')) {\n      // jshint ignore:line\n      this.api.dispatchAction({\n        type: 'selectDataRange',\n        from: this.uid,\n        visualMapId: this.visualMapModel.id,\n        selected: this._dataInterval.slice()\n      });\n    }\n\n    if (isEnd) {\n      !this._hovering && this._clearHoverLinkToSeries();\n    } else if (useHoverLinkOnHandle(this.visualMapModel)) {\n      this._doHoverLinkToSeries(this._handleEnds[handleIndex], false);\n    }\n  };\n\n  ContinuousView.prototype._resetInterval = function () {\n    var visualMapModel = this.visualMapModel;\n    var dataInterval = this._dataInterval = visualMapModel.getSelected();\n    var dataExtent = visualMapModel.getExtent();\n    var sizeExtent = [0, visualMapModel.itemSize[1]];\n    this._handleEnds = [linearMap$2(dataInterval[0], dataExtent, sizeExtent, true), linearMap$2(dataInterval[1], dataExtent, sizeExtent, true)];\n  };\n  /**\n   * @private\n   * @param {(number|string)} handleIndex 0 or 1 or 'all'\n   * @param {number} dx\n   * @param {number} dy\n   */\n\n\n  ContinuousView.prototype._updateInterval = function (handleIndex, delta) {\n    delta = delta || 0;\n    var visualMapModel = this.visualMapModel;\n    var handleEnds = this._handleEnds;\n    var sizeExtent = [0, visualMapModel.itemSize[1]];\n    sliderMove(delta, handleEnds, sizeExtent, handleIndex, // cross is forbiden\n    0);\n    var dataExtent = visualMapModel.getExtent(); // Update data interval.\n\n    this._dataInterval = [linearMap$2(handleEnds[0], sizeExtent, dataExtent, true), linearMap$2(handleEnds[1], sizeExtent, dataExtent, true)];\n  };\n\n  ContinuousView.prototype._updateView = function (forSketch) {\n    var visualMapModel = this.visualMapModel;\n    var dataExtent = visualMapModel.getExtent();\n    var shapes = this._shapes;\n    var outOfRangeHandleEnds = [0, visualMapModel.itemSize[1]];\n    var inRangeHandleEnds = forSketch ? outOfRangeHandleEnds : this._handleEnds;\n\n    var visualInRange = this._createBarVisual(this._dataInterval, dataExtent, inRangeHandleEnds, 'inRange');\n\n    var visualOutOfRange = this._createBarVisual(dataExtent, dataExtent, outOfRangeHandleEnds, 'outOfRange');\n\n    shapes.inRange.setStyle({\n      fill: visualInRange.barColor // opacity: visualInRange.opacity\n\n    }).setShape('points', visualInRange.barPoints);\n    shapes.outOfRange.setStyle({\n      fill: visualOutOfRange.barColor // opacity: visualOutOfRange.opacity\n\n    }).setShape('points', visualOutOfRange.barPoints);\n\n    this._updateHandle(inRangeHandleEnds, visualInRange);\n  };\n\n  ContinuousView.prototype._createBarVisual = function (dataInterval, dataExtent, handleEnds, forceState) {\n    var opts = {\n      forceState: forceState,\n      convertOpacityToAlpha: true\n    };\n\n    var colorStops = this._makeColorGradient(dataInterval, opts);\n\n    var symbolSizes = [this.getControllerVisual(dataInterval[0], 'symbolSize', opts), this.getControllerVisual(dataInterval[1], 'symbolSize', opts)];\n\n    var barPoints = this._createBarPoints(handleEnds, symbolSizes);\n\n    return {\n      barColor: new LinearGradient(0, 0, 0, 1, colorStops),\n      barPoints: barPoints,\n      handlesColor: [colorStops[0].color, colorStops[colorStops.length - 1].color]\n    };\n  };\n\n  ContinuousView.prototype._makeColorGradient = function (dataInterval, opts) {\n    // Considering colorHue, which is not linear, so we have to sample\n    // to calculate gradient color stops, but not only caculate head\n    // and tail.\n    var sampleNumber = 100; // Arbitrary value.\n\n    var colorStops = [];\n    var step = (dataInterval[1] - dataInterval[0]) / sampleNumber;\n    colorStops.push({\n      color: this.getControllerVisual(dataInterval[0], 'color', opts),\n      offset: 0\n    });\n\n    for (var i = 1; i < sampleNumber; i++) {\n      var currValue = dataInterval[0] + step * i;\n\n      if (currValue > dataInterval[1]) {\n        break;\n      }\n\n      colorStops.push({\n        color: this.getControllerVisual(currValue, 'color', opts),\n        offset: i / sampleNumber\n      });\n    }\n\n    colorStops.push({\n      color: this.getControllerVisual(dataInterval[1], 'color', opts),\n      offset: 1\n    });\n    return colorStops;\n  };\n\n  ContinuousView.prototype._createBarPoints = function (handleEnds, symbolSizes) {\n    var itemSize = this.visualMapModel.itemSize;\n    return [[itemSize[0] - symbolSizes[0], handleEnds[0]], [itemSize[0], handleEnds[0]], [itemSize[0], handleEnds[1]], [itemSize[0] - symbolSizes[1], handleEnds[1]]];\n  };\n\n  ContinuousView.prototype._createBarGroup = function (itemAlign) {\n    var orient = this._orient;\n    var inverse = this.visualMapModel.get('inverse');\n    return new Group(orient === 'horizontal' && !inverse ? {\n      scaleX: itemAlign === 'bottom' ? 1 : -1,\n      rotation: Math.PI / 2\n    } : orient === 'horizontal' && inverse ? {\n      scaleX: itemAlign === 'bottom' ? -1 : 1,\n      rotation: -Math.PI / 2\n    } : orient === 'vertical' && !inverse ? {\n      scaleX: itemAlign === 'left' ? 1 : -1,\n      scaleY: -1\n    } : {\n      scaleX: itemAlign === 'left' ? 1 : -1\n    });\n  };\n\n  ContinuousView.prototype._updateHandle = function (handleEnds, visualInRange) {\n    if (!this._useHandle) {\n      return;\n    }\n\n    var shapes = this._shapes;\n    var visualMapModel = this.visualMapModel;\n    var handleThumbs = shapes.handleThumbs;\n    var handleLabels = shapes.handleLabels;\n    var itemSize = visualMapModel.itemSize;\n    var dataExtent = visualMapModel.getExtent();\n    each$e([0, 1], function (handleIndex) {\n      var handleThumb = handleThumbs[handleIndex];\n      handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]);\n      handleThumb.y = handleEnds[handleIndex];\n      var val = linearMap$2(handleEnds[handleIndex], [0, itemSize[1]], dataExtent, true);\n      var symbolSize = this.getControllerVisual(val, 'symbolSize');\n      handleThumb.scaleX = handleThumb.scaleY = symbolSize / itemSize[0];\n      handleThumb.x = itemSize[0] - symbolSize / 2; // Update handle label position.\n\n      var textPoint = applyTransform$1(shapes.handleLabelPoints[handleIndex], getTransform(handleThumb, this.group));\n      handleLabels[handleIndex].setStyle({\n        x: textPoint[0],\n        y: textPoint[1],\n        text: visualMapModel.formatValueText(this._dataInterval[handleIndex]),\n        verticalAlign: 'middle',\n        align: this._orient === 'vertical' ? this._applyTransform('left', shapes.mainGroup) : 'center'\n      });\n    }, this);\n  };\n\n  ContinuousView.prototype._showIndicator = function (cursorValue, textValue, rangeSymbol, halfHoverLinkSize) {\n    var visualMapModel = this.visualMapModel;\n    var dataExtent = visualMapModel.getExtent();\n    var itemSize = visualMapModel.itemSize;\n    var sizeExtent = [0, itemSize[1]];\n    var shapes = this._shapes;\n    var indicator = shapes.indicator;\n\n    if (!indicator) {\n      return;\n    }\n\n    indicator.attr('invisible', false);\n    var opts = {\n      convertOpacityToAlpha: true\n    };\n    var color = this.getControllerVisual(cursorValue, 'color', opts);\n    var symbolSize = this.getControllerVisual(cursorValue, 'symbolSize');\n    var y = linearMap$2(cursorValue, dataExtent, sizeExtent, true);\n    var x = itemSize[0] - symbolSize / 2;\n    var oldIndicatorPos = {\n      x: indicator.x,\n      y: indicator.y\n    }; // Update handle label position.\n\n    indicator.y = y;\n    indicator.x = x;\n    var textPoint = applyTransform$1(shapes.indicatorLabelPoint, getTransform(indicator, this.group));\n    var indicatorLabel = shapes.indicatorLabel;\n    indicatorLabel.attr('invisible', false);\n\n    var align = this._applyTransform('left', shapes.mainGroup);\n\n    var orient = this._orient;\n    var isHorizontal = orient === 'horizontal';\n    indicatorLabel.setStyle({\n      text: (rangeSymbol ? rangeSymbol : '') + visualMapModel.formatValueText(textValue),\n      verticalAlign: isHorizontal ? align : 'middle',\n      align: isHorizontal ? 'center' : align\n    });\n    var indicatorNewProps = {\n      x: x,\n      y: y,\n      style: {\n        fill: color\n      }\n    };\n    var labelNewProps = {\n      style: {\n        x: textPoint[0],\n        y: textPoint[1]\n      }\n    };\n\n    if (visualMapModel.ecModel.isAnimationEnabled() && !this._firstShowIndicator) {\n      var animationCfg = {\n        duration: 100,\n        easing: 'cubicInOut',\n        additive: true\n      };\n      indicator.x = oldIndicatorPos.x;\n      indicator.y = oldIndicatorPos.y;\n      indicator.animateTo(indicatorNewProps, animationCfg);\n      indicatorLabel.animateTo(labelNewProps, animationCfg);\n    } else {\n      indicator.attr(indicatorNewProps);\n      indicatorLabel.attr(labelNewProps);\n    }\n\n    this._firstShowIndicator = false;\n    var handleLabels = this._shapes.handleLabels;\n\n    if (handleLabels) {\n      for (var i = 0; i < handleLabels.length; i++) {\n        // Fade out handle labels.\n        // NOTE: Must use api enter/leave on emphasis/blur/select state. Or the global states manager will change it.\n        this._api.enterBlur(handleLabels[i]);\n      }\n    }\n  };\n\n  ContinuousView.prototype._enableHoverLinkToSeries = function () {\n    var self = this;\n\n    this._shapes.mainGroup.on('mousemove', function (e) {\n      self._hovering = true;\n\n      if (!self._dragging) {\n        var itemSize = self.visualMapModel.itemSize;\n\n        var pos = self._applyTransform([e.offsetX, e.offsetY], self._shapes.mainGroup, true, true); // For hover link show when hover handle, which might be\n        // below or upper than sizeExtent.\n\n\n        pos[1] = mathMin$a(mathMax$a(0, pos[1]), itemSize[1]);\n\n        self._doHoverLinkToSeries(pos[1], 0 <= pos[0] && pos[0] <= itemSize[0]);\n      }\n    }).on('mouseout', function () {\n      // When mouse is out of handle, hoverLink still need\n      // to be displayed when realtime is set as false.\n      self._hovering = false;\n      !self._dragging && self._clearHoverLinkToSeries();\n    });\n  };\n\n  ContinuousView.prototype._enableHoverLinkFromSeries = function () {\n    var zr = this.api.getZr();\n\n    if (this.visualMapModel.option.hoverLink) {\n      zr.on('mouseover', this._hoverLinkFromSeriesMouseOver, this);\n      zr.on('mouseout', this._hideIndicator, this);\n    } else {\n      this._clearHoverLinkFromSeries();\n    }\n  };\n\n  ContinuousView.prototype._doHoverLinkToSeries = function (cursorPos, hoverOnBar) {\n    var visualMapModel = this.visualMapModel;\n    var itemSize = visualMapModel.itemSize;\n\n    if (!visualMapModel.option.hoverLink) {\n      return;\n    }\n\n    var sizeExtent = [0, itemSize[1]];\n    var dataExtent = visualMapModel.getExtent(); // For hover link show when hover handle, which might be below or upper than sizeExtent.\n\n    cursorPos = mathMin$a(mathMax$a(sizeExtent[0], cursorPos), sizeExtent[1]);\n    var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent);\n    var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize];\n    var cursorValue = linearMap$2(cursorPos, sizeExtent, dataExtent, true);\n    var valueRange = [linearMap$2(hoverRange[0], sizeExtent, dataExtent, true), linearMap$2(hoverRange[1], sizeExtent, dataExtent, true)]; // Consider data range is out of visualMap range, see test/visualMap-continuous.html,\n    // where china and india has very large population.\n\n    hoverRange[0] < sizeExtent[0] && (valueRange[0] = -Infinity);\n    hoverRange[1] > sizeExtent[1] && (valueRange[1] = Infinity); // Do not show indicator when mouse is over handle,\n    // otherwise labels overlap, especially when dragging.\n\n    if (hoverOnBar) {\n      if (valueRange[0] === -Infinity) {\n        this._showIndicator(cursorValue, valueRange[1], '< ', halfHoverLinkSize);\n      } else if (valueRange[1] === Infinity) {\n        this._showIndicator(cursorValue, valueRange[0], '> ', halfHoverLinkSize);\n      } else {\n        this._showIndicator(cursorValue, cursorValue, '≈ ', halfHoverLinkSize);\n      }\n    } // When realtime is set as false, handles, which are in barGroup,\n    // also trigger hoverLink, which help user to realize where they\n    // focus on when dragging. (see test/heatmap-large.html)\n    // When realtime is set as true, highlight will not show when hover\n    // handle, because the label on handle, which displays a exact value\n    // but not range, might mislead users.\n\n\n    var oldBatch = this._hoverLinkDataIndices;\n    var newBatch = [];\n\n    if (hoverOnBar || useHoverLinkOnHandle(visualMapModel)) {\n      newBatch = this._hoverLinkDataIndices = visualMapModel.findTargetDataIndices(valueRange);\n    }\n\n    var resultBatches = compressBatches(oldBatch, newBatch);\n\n    this._dispatchHighDown('downplay', makeHighDownBatch(resultBatches[0], visualMapModel));\n\n    this._dispatchHighDown('highlight', makeHighDownBatch(resultBatches[1], visualMapModel));\n  };\n\n  ContinuousView.prototype._hoverLinkFromSeriesMouseOver = function (e) {\n    var ecData;\n    findEventDispatcher(e.target, function (target) {\n      var currECData = getECData(target);\n\n      if (currECData.dataIndex != null) {\n        ecData = currECData;\n        return true;\n      }\n    }, true);\n\n    if (!ecData) {\n      return;\n    }\n\n    var dataModel = this.ecModel.getSeriesByIndex(ecData.seriesIndex);\n    var visualMapModel = this.visualMapModel;\n\n    if (!visualMapModel.isTargetSeries(dataModel)) {\n      return;\n    }\n\n    var data = dataModel.getData(ecData.dataType);\n    var value = data.getStore().get(visualMapModel.getDataDimensionIndex(data), ecData.dataIndex);\n\n    if (!isNaN(value)) {\n      this._showIndicator(value, value);\n    }\n  };\n\n  ContinuousView.prototype._hideIndicator = function () {\n    var shapes = this._shapes;\n    shapes.indicator && shapes.indicator.attr('invisible', true);\n    shapes.indicatorLabel && shapes.indicatorLabel.attr('invisible', true);\n    var handleLabels = this._shapes.handleLabels;\n\n    if (handleLabels) {\n      for (var i = 0; i < handleLabels.length; i++) {\n        // Fade out handle labels.\n        // NOTE: Must use api enter/leave on emphasis/blur/select state. Or the global states manager will change it.\n        this._api.leaveBlur(handleLabels[i]);\n      }\n    }\n  };\n\n  ContinuousView.prototype._clearHoverLinkToSeries = function () {\n    this._hideIndicator();\n\n    var indices = this._hoverLinkDataIndices;\n\n    this._dispatchHighDown('downplay', makeHighDownBatch(indices, this.visualMapModel));\n\n    indices.length = 0;\n  };\n\n  ContinuousView.prototype._clearHoverLinkFromSeries = function () {\n    this._hideIndicator();\n\n    var zr = this.api.getZr();\n    zr.off('mouseover', this._hoverLinkFromSeriesMouseOver);\n    zr.off('mouseout', this._hideIndicator);\n  };\n\n  ContinuousView.prototype._applyTransform = function (vertex, element, inverse, global) {\n    var transform = getTransform(element, global ? null : this.group);\n    return isArray(vertex) ? applyTransform$1(vertex, transform, inverse) : transformDirection(vertex, transform, inverse);\n  }; // TODO: TYPE more specified payload types.\n\n\n  ContinuousView.prototype._dispatchHighDown = function (type, batch) {\n    batch && batch.length && this.api.dispatchAction({\n      type: type,\n      batch: batch\n    });\n  };\n  /**\n   * @override\n   */\n\n\n  ContinuousView.prototype.dispose = function () {\n    this._clearHoverLinkFromSeries();\n\n    this._clearHoverLinkToSeries();\n  };\n  /**\n   * @override\n   */\n\n\n  ContinuousView.prototype.remove = function () {\n    this._clearHoverLinkFromSeries();\n\n    this._clearHoverLinkToSeries();\n  };\n\n  ContinuousView.type = 'visualMap.continuous';\n  return ContinuousView;\n}(VisualMapView);\n\nfunction createPolygon(points, cursor, onDrift, onDragEnd) {\n  return new Polygon({\n    shape: {\n      points: points\n    },\n    draggable: !!onDrift,\n    cursor: cursor,\n    drift: onDrift,\n    onmousemove: function (e) {\n      // Fot mobile devicem, prevent screen slider on the button.\n      stop(e.event);\n    },\n    ondragend: onDragEnd\n  });\n}\n\nfunction getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent) {\n  var halfHoverLinkSize = HOVER_LINK_SIZE / 2;\n  var hoverLinkDataSize = visualMapModel.get('hoverLinkDataSize');\n\n  if (hoverLinkDataSize) {\n    halfHoverLinkSize = linearMap$2(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2;\n  }\n\n  return halfHoverLinkSize;\n}\n\nfunction useHoverLinkOnHandle(visualMapModel) {\n  var hoverLinkOnHandle = visualMapModel.get('hoverLinkOnHandle');\n  return !!(hoverLinkOnHandle == null ? visualMapModel.get('realtime') : hoverLinkOnHandle);\n}\n\nfunction getCursor$1(orient) {\n  return orient === 'vertical' ? 'ns-resize' : 'ew-resize';\n}\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n\n/**\n * AUTO-GENERATED FILE. DO NOT MODIFY.\n */\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\nvar visualMapActionInfo = {\n  type: 'selectDataRange',\n  event: 'dataRangeSelected',\n  // FIXME use updateView appears wrong\n  update: 'update'\n};\nvar visualMapActionHander = function (payload, ecModel) {\n  ecModel.eachComponent({\n    mainType: 'visualMap',\n    query: payload\n  }, function (model) {\n    model.setSelected(payload.selected);\n  });\n};\n\nvar visualMapEncodingHandlers = [{\n  createOnAllSeries: true,\n  reset: function (seriesModel, ecModel) {\n    var resetDefines = [];\n    ecModel.eachComponent('visualMap', function (visualMapModel) {\n      var pipelineContext = seriesModel.pipelineContext;\n\n      if (!visualMapModel.isTargetSeries(seriesModel) || pipelineContext && pipelineContext.large) {\n        return;\n      }\n\n      resetDefines.push(incrementalApplyVisual(visualMapModel.stateList, visualMapModel.targetVisuals, bind(visualMapModel.getValueState, visualMapModel), visualMapModel.getDataDimensionIndex(seriesModel.getData())));\n    });\n    return resetDefines;\n  }\n}, // Only support color.\n{\n  createOnAllSeries: true,\n  reset: function (seriesModel, ecModel) {\n    var data = seriesModel.getData();\n    var visualMetaList = [];\n    ecModel.eachComponent('visualMap', function (visualMapModel) {\n      if (visualMapModel.isTargetSeries(seriesModel)) {\n        var visualMeta = visualMapModel.getVisualMeta(bind(getColorVisual, null, seriesModel, visualMapModel)) || {\n          stops: [],\n          outerColors: []\n        };\n        var dimIdx = visualMapModel.getDataDimensionIndex(data);\n\n        if (dimIdx >= 0) {\n          // visualMeta.dimension should be dimension index, but not concrete dimension.\n          visualMeta.dimension = dimIdx;\n          visualMetaList.push(visualMeta);\n        }\n      }\n    }); // console.log(JSON.stringify(visualMetaList.map(a => a.stops)));\n\n    seriesModel.getData().setVisual('visualMeta', visualMetaList);\n  }\n}]; // FIXME\n// performance and export for heatmap?\n// value can be Infinity or -Infinity\n\nfunction getColorVisual(seriesModel, visualMapModel, value, valueState) {\n  var mappings = visualMapModel.targetVisuals[valueState];\n  var visualTypes = VisualMapping.prepareVisualTypes(mappings);\n  var resultVisual = {\n    color: getVisualFromData(seriesModel.getData(), 'color') // default color.\n\n  };\n\n  for (var i = 0, len = visualTypes.length; i < len; i++) {\n    var type = visualTypes[i];\n    var mapping = mappings[type === 'opacity' ? '__alphaForOpacity' : type];\n    mapping && mapping.applyVisual(value, getVisual, setVisual);\n  }\n\n  return resultVisual.color;\n\n  function getVisual(key) {\n    return resultVisual[key];\n  }\n\n  function setVisual(key, value) {\n    resultVisual[key] = value;\n  }\n}\n\nvar each$f = each;\nfunction visualMapPreprocessor(option) {\n  var visualMap = option && option.visualMap;\n\n  if (!isArray(visualMap)) {\n    visualMap = visualMap ? [visualMap] : [];\n  }\n\n  each$f(visualMap, function (opt) {\n    if (!opt) {\n      return;\n    } // rename splitList to pieces\n\n\n    if (has$1(opt, 'splitList') && !has$1(opt, 'pieces')) {\n      opt.pieces = opt.splitList;\n      delete opt.splitList;\n    }\n\n    var pieces = opt.pieces;\n\n    if (pieces && isArray(pieces)) {\n      each$f(pieces, function (piece) {\n        if (isObject(piece)) {\n          if (has$1(piece, 'start') && !has$1(piece, 'min')) {\n            piece.min = piece.start;\n          }\n\n          if (has$1(piece, 'end') && !has$1(piece, 'max')) {\n            piece.max = piece.end;\n          }\n        }\n      });\n    }\n  });\n}\n\nfunction has$1(obj, name) {\n  return obj && obj.hasOwnProperty && obj.hasOwnProperty(name);\n}\n\nvar installed$1 = false;\nfunction installCommon$1(registers) {\n  if (installed$1) {\n    return;\n  }\n\n  installed$1 = true;\n  registers.registerSubTypeDefaulter('visualMap', function (option) {\n    // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used.\n    return !option.categories && (!(option.pieces ? option.pieces.length > 0 : option.splitNumber > 0) || option.calculable) ? 'continuous' : 'piecewise';\n  });\n  registers.registerAction(visualMapActionInfo, visualMapActionHander);\n  each(visualMapEncodingHandlers, function (handler) {\n    registers.registerVisual(registers.PRIORITY.VISUAL.COMPONENT, handler);\n  });\n  registers.registerPreprocessor(visualMapPreprocessor);\n}\n\nfunction install$N(registers) {\n  registers.registerComponentModel(ContinuousModel);\n  registers.registerComponentView(ContinuousView);\n  installCommon$1(registers);\n}\n\nvar PiecewiseModel =\n/** @class */\nfunction (_super) {\n  __extends(PiecewiseModel, _super);\n\n  function PiecewiseModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = PiecewiseModel.type;\n    /**\n     * The order is always [low, ..., high].\n     * [{text: string, interval: Array.<number>}, ...]\n     */\n\n    _this._pieceList = [];\n    return _this;\n  }\n\n  PiecewiseModel.prototype.optionUpdated = function (newOption, isInit) {\n    _super.prototype.optionUpdated.apply(this, arguments);\n\n    this.resetExtent();\n\n    var mode = this._mode = this._determineMode();\n\n    this._pieceList = [];\n\n    resetMethods[this._mode].call(this, this._pieceList);\n\n    this._resetSelected(newOption, isInit);\n\n    var categories = this.option.categories;\n    this.resetVisual(function (mappingOption, state) {\n      if (mode === 'categories') {\n        mappingOption.mappingMethod = 'category';\n        mappingOption.categories = clone(categories);\n      } else {\n        mappingOption.dataExtent = this.getExtent();\n        mappingOption.mappingMethod = 'piecewise';\n        mappingOption.pieceList = map(this._pieceList, function (piece) {\n          piece = clone(piece);\n\n          if (state !== 'inRange') {\n            // FIXME\n            // outOfRange do not support special visual in pieces.\n            piece.visual = null;\n          }\n\n          return piece;\n        });\n      }\n    });\n  };\n  /**\n   * @protected\n   * @override\n   */\n\n\n  PiecewiseModel.prototype.completeVisualOption = function () {\n    // Consider this case:\n    // visualMap: {\n    //      pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}]\n    // }\n    // where no inRange/outOfRange set but only pieces. So we should make\n    // default inRange/outOfRange for this case, otherwise visuals that only\n    // appear in `pieces` will not be taken into account in visual encoding.\n    var option = this.option;\n    var visualTypesInPieces = {};\n    var visualTypes = VisualMapping.listVisualTypes();\n    var isCategory = this.isCategory();\n    each(option.pieces, function (piece) {\n      each(visualTypes, function (visualType) {\n        if (piece.hasOwnProperty(visualType)) {\n          visualTypesInPieces[visualType] = 1;\n        }\n      });\n    });\n    each(visualTypesInPieces, function (v, visualType) {\n      var exists = false;\n      each(this.stateList, function (state) {\n        exists = exists || has(option, state, visualType) || has(option.target, state, visualType);\n      }, this);\n      !exists && each(this.stateList, function (state) {\n        (option[state] || (option[state] = {}))[visualType] = visualDefault.get(visualType, state === 'inRange' ? 'active' : 'inactive', isCategory);\n      });\n    }, this);\n\n    function has(obj, state, visualType) {\n      return obj && obj[state] && obj[state].hasOwnProperty(visualType);\n    }\n\n    _super.prototype.completeVisualOption.apply(this, arguments);\n  };\n\n  PiecewiseModel.prototype._resetSelected = function (newOption, isInit) {\n    var thisOption = this.option;\n    var pieceList = this._pieceList; // Selected do not merge but all override.\n\n    var selected = (isInit ? thisOption : newOption).selected || {};\n    thisOption.selected = selected; // Consider 'not specified' means true.\n\n    each(pieceList, function (piece, index) {\n      var key = this.getSelectedMapKey(piece);\n\n      if (!selected.hasOwnProperty(key)) {\n        selected[key] = true;\n      }\n    }, this);\n\n    if (thisOption.selectedMode === 'single') {\n      // Ensure there is only one selected.\n      var hasSel_1 = false;\n      each(pieceList, function (piece, index) {\n        var key = this.getSelectedMapKey(piece);\n\n        if (selected[key]) {\n          hasSel_1 ? selected[key] = false : hasSel_1 = true;\n        }\n      }, this);\n    } // thisOption.selectedMode === 'multiple', default: all selected.\n\n  };\n  /**\n   * @public\n   */\n\n\n  PiecewiseModel.prototype.getItemSymbol = function () {\n    return this.get('itemSymbol');\n  };\n  /**\n   * @public\n   */\n\n\n  PiecewiseModel.prototype.getSelectedMapKey = function (piece) {\n    return this._mode === 'categories' ? piece.value + '' : piece.index + '';\n  };\n  /**\n   * @public\n   */\n\n\n  PiecewiseModel.prototype.getPieceList = function () {\n    return this._pieceList;\n  };\n  /**\n   * @return {string}\n   */\n\n\n  PiecewiseModel.prototype._determineMode = function () {\n    var option = this.option;\n    return option.pieces && option.pieces.length > 0 ? 'pieces' : this.option.categories ? 'categories' : 'splitNumber';\n  };\n  /**\n   * @override\n   */\n\n\n  PiecewiseModel.prototype.setSelected = function (selected) {\n    this.option.selected = clone(selected);\n  };\n  /**\n   * @override\n   */\n\n\n  PiecewiseModel.prototype.getValueState = function (value) {\n    var index = VisualMapping.findPieceIndex(value, this._pieceList);\n    return index != null ? this.option.selected[this.getSelectedMapKey(this._pieceList[index])] ? 'inRange' : 'outOfRange' : 'outOfRange';\n  };\n  /**\n   * @public\n   * @param pieceIndex piece index in visualMapModel.getPieceList()\n   */\n\n\n  PiecewiseModel.prototype.findTargetDataIndices = function (pieceIndex) {\n    var result = [];\n    var pieceList = this._pieceList;\n    this.eachTargetSeries(function (seriesModel) {\n      var dataIndices = [];\n      var data = seriesModel.getData();\n      data.each(this.getDataDimensionIndex(data), function (value, dataIndex) {\n        // Should always base on model pieceList, because it is order sensitive.\n        var pIdx = VisualMapping.findPieceIndex(value, pieceList);\n        pIdx === pieceIndex && dataIndices.push(dataIndex);\n      }, this);\n      result.push({\n        seriesId: seriesModel.id,\n        dataIndex: dataIndices\n      });\n    }, this);\n    return result;\n  };\n  /**\n   * @private\n   * @param piece piece.value or piece.interval is required.\n   * @return  Can be Infinity or -Infinity\n   */\n\n\n  PiecewiseModel.prototype.getRepresentValue = function (piece) {\n    var representValue;\n\n    if (this.isCategory()) {\n      representValue = piece.value;\n    } else {\n      if (piece.value != null) {\n        representValue = piece.value;\n      } else {\n        var pieceInterval = piece.interval || [];\n        representValue = pieceInterval[0] === -Infinity && pieceInterval[1] === Infinity ? 0 : (pieceInterval[0] + pieceInterval[1]) / 2;\n      }\n    }\n\n    return representValue;\n  };\n\n  PiecewiseModel.prototype.getVisualMeta = function (getColorVisual) {\n    // Do not support category. (category axis is ordinal, numerical)\n    if (this.isCategory()) {\n      return;\n    }\n\n    var stops = [];\n    var outerColors = ['', ''];\n    var visualMapModel = this;\n\n    function setStop(interval, valueState) {\n      var representValue = visualMapModel.getRepresentValue({\n        interval: interval\n      }); // Not category\n\n      if (!valueState) {\n        valueState = visualMapModel.getValueState(representValue);\n      }\n\n      var color = getColorVisual(representValue, valueState);\n\n      if (interval[0] === -Infinity) {\n        outerColors[0] = color;\n      } else if (interval[1] === Infinity) {\n        outerColors[1] = color;\n      } else {\n        stops.push({\n          value: interval[0],\n          color: color\n        }, {\n          value: interval[1],\n          color: color\n        });\n      }\n    } // Suplement\n\n\n    var pieceList = this._pieceList.slice();\n\n    if (!pieceList.length) {\n      pieceList.push({\n        interval: [-Infinity, Infinity]\n      });\n    } else {\n      var edge = pieceList[0].interval[0];\n      edge !== -Infinity && pieceList.unshift({\n        interval: [-Infinity, edge]\n      });\n      edge = pieceList[pieceList.length - 1].interval[1];\n      edge !== Infinity && pieceList.push({\n        interval: [edge, Infinity]\n      });\n    }\n\n    var curr = -Infinity;\n    each(pieceList, function (piece) {\n      var interval = piece.interval;\n\n      if (interval) {\n        // Fulfill gap.\n        interval[0] > curr && setStop([curr, interval[0]], 'outOfRange');\n        setStop(interval.slice());\n        curr = interval[1];\n      }\n    }, this);\n    return {\n      stops: stops,\n      outerColors: outerColors\n    };\n  };\n\n  PiecewiseModel.type = 'visualMap.piecewise';\n  PiecewiseModel.defaultOption = inheritDefaultOption(VisualMapModel.defaultOption, {\n    selected: null,\n    minOpen: false,\n    maxOpen: false,\n    align: 'auto',\n    itemWidth: 20,\n    itemHeight: 14,\n    itemSymbol: 'roundRect',\n    pieces: null,\n    categories: null,\n    splitNumber: 5,\n    selectedMode: 'multiple',\n    itemGap: 10,\n    hoverLink: true // Enable hover highlight.\n\n  });\n  return PiecewiseModel;\n}(VisualMapModel);\n/**\n * Key is this._mode\n * @type {Object}\n * @this {module:echarts/component/viusalMap/PiecewiseMode}\n */\n\nvar resetMethods = {\n  splitNumber: function (outPieceList) {\n    var thisOption = this.option;\n    var precision = Math.min(thisOption.precision, 20);\n    var dataExtent = this.getExtent();\n    var splitNumber = thisOption.splitNumber;\n    splitNumber = Math.max(parseInt(splitNumber, 10), 1);\n    thisOption.splitNumber = splitNumber;\n    var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; // Precision auto-adaption\n\n    while (+splitStep.toFixed(precision) !== splitStep && precision < 5) {\n      precision++;\n    }\n\n    thisOption.precision = precision;\n    splitStep = +splitStep.toFixed(precision);\n\n    if (thisOption.minOpen) {\n      outPieceList.push({\n        interval: [-Infinity, dataExtent[0]],\n        close: [0, 0]\n      });\n    }\n\n    for (var index = 0, curr = dataExtent[0]; index < splitNumber; curr += splitStep, index++) {\n      var max = index === splitNumber - 1 ? dataExtent[1] : curr + splitStep;\n      outPieceList.push({\n        interval: [curr, max],\n        close: [1, 1]\n      });\n    }\n\n    if (thisOption.maxOpen) {\n      outPieceList.push({\n        interval: [dataExtent[1], Infinity],\n        close: [0, 0]\n      });\n    }\n\n    reformIntervals(outPieceList);\n    each(outPieceList, function (piece, index) {\n      piece.index = index;\n      piece.text = this.formatValueText(piece.interval);\n    }, this);\n  },\n  categories: function (outPieceList) {\n    var thisOption = this.option;\n    each(thisOption.categories, function (cate) {\n      // FIXME category模式也使用pieceList，但在visualMapping中不是使用pieceList。\n      // 是否改一致。\n      outPieceList.push({\n        text: this.formatValueText(cate, true),\n        value: cate\n      });\n    }, this); // See \"Order Rule\".\n\n    normalizeReverse(thisOption, outPieceList);\n  },\n  pieces: function (outPieceList) {\n    var thisOption = this.option;\n    each(thisOption.pieces, function (pieceListItem, index) {\n      if (!isObject(pieceListItem)) {\n        pieceListItem = {\n          value: pieceListItem\n        };\n      }\n\n      var item = {\n        text: '',\n        index: index\n      };\n\n      if (pieceListItem.label != null) {\n        item.text = pieceListItem.label;\n      }\n\n      if (pieceListItem.hasOwnProperty('value')) {\n        var value = item.value = pieceListItem.value;\n        item.interval = [value, value];\n        item.close = [1, 1];\n      } else {\n        // `min` `max` is legacy option.\n        // `lt` `gt` `lte` `gte` is recommanded.\n        var interval = item.interval = [];\n        var close_1 = item.close = [0, 0];\n        var closeList = [1, 0, 1];\n        var infinityList = [-Infinity, Infinity];\n        var useMinMax = [];\n\n        for (var lg = 0; lg < 2; lg++) {\n          var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg];\n\n          for (var i = 0; i < 3 && interval[lg] == null; i++) {\n            interval[lg] = pieceListItem[names[i]];\n            close_1[lg] = closeList[i];\n            useMinMax[lg] = i === 2;\n          }\n\n          interval[lg] == null && (interval[lg] = infinityList[lg]);\n        }\n\n        useMinMax[0] && interval[1] === Infinity && (close_1[0] = 0);\n        useMinMax[1] && interval[0] === -Infinity && (close_1[1] = 0);\n\n        if (\"development\" !== 'production') {\n          if (interval[0] > interval[1]) {\n            console.warn('Piece ' + index + 'is illegal: ' + interval + ' lower bound should not greater then uppper bound.');\n          }\n        }\n\n        if (interval[0] === interval[1] && close_1[0] && close_1[1]) {\n          // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}],\n          // we use value to lift the priority when min === max\n          item.value = interval[0];\n        }\n      }\n\n      item.visual = VisualMapping.retrieveVisuals(pieceListItem);\n      outPieceList.push(item);\n    }, this); // See \"Order Rule\".\n\n    normalizeReverse(thisOption, outPieceList); // Only pieces\n\n    reformIntervals(outPieceList);\n    each(outPieceList, function (piece) {\n      var close = piece.close;\n      var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]];\n      piece.text = piece.text || this.formatValueText(piece.value != null ? piece.value : piece.interval, false, edgeSymbols);\n    }, this);\n  }\n};\n\nfunction normalizeReverse(thisOption, pieceList) {\n  var inverse = thisOption.inverse;\n\n  if (thisOption.orient === 'vertical' ? !inverse : inverse) {\n    pieceList.reverse();\n  }\n}\n\nvar PiecewiseVisualMapView =\n/** @class */\nfunction (_super) {\n  __extends(PiecewiseVisualMapView, _super);\n\n  function PiecewiseVisualMapView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = PiecewiseVisualMapView.type;\n    return _this;\n  }\n\n  PiecewiseVisualMapView.prototype.doRender = function () {\n    var thisGroup = this.group;\n    thisGroup.removeAll();\n    var visualMapModel = this.visualMapModel;\n    var textGap = visualMapModel.get('textGap');\n    var textStyleModel = visualMapModel.textStyleModel;\n    var textFont = textStyleModel.getFont();\n    var textFill = textStyleModel.getTextColor();\n\n    var itemAlign = this._getItemAlign();\n\n    var itemSize = visualMapModel.itemSize;\n\n    var viewData = this._getViewData();\n\n    var endsText = viewData.endsText;\n    var showLabel = retrieve(visualMapModel.get('showLabel', true), !endsText);\n    endsText && this._renderEndsText(thisGroup, endsText[0], itemSize, showLabel, itemAlign);\n    each(viewData.viewPieceList, function (item) {\n      var piece = item.piece;\n      var itemGroup = new Group();\n      itemGroup.onclick = bind(this._onItemClick, this, piece);\n\n      this._enableHoverLink(itemGroup, item.indexInModelPieceList); // TODO Category\n\n\n      var representValue = visualMapModel.getRepresentValue(piece);\n\n      this._createItemSymbol(itemGroup, representValue, [0, 0, itemSize[0], itemSize[1]]);\n\n      if (showLabel) {\n        var visualState = this.visualMapModel.getValueState(representValue);\n        itemGroup.add(new ZRText({\n          style: {\n            x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap,\n            y: itemSize[1] / 2,\n            text: piece.text,\n            verticalAlign: 'middle',\n            align: itemAlign,\n            font: textFont,\n            fill: textFill,\n            opacity: visualState === 'outOfRange' ? 0.5 : 1\n          }\n        }));\n      }\n\n      thisGroup.add(itemGroup);\n    }, this);\n    endsText && this._renderEndsText(thisGroup, endsText[1], itemSize, showLabel, itemAlign);\n    box(visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap'));\n    this.renderBackground(thisGroup);\n    this.positionGroup(thisGroup);\n  };\n\n  PiecewiseVisualMapView.prototype._enableHoverLink = function (itemGroup, pieceIndex) {\n    var _this = this;\n\n    itemGroup.on('mouseover', function () {\n      return onHoverLink('highlight');\n    }).on('mouseout', function () {\n      return onHoverLink('downplay');\n    });\n\n    var onHoverLink = function (method) {\n      var visualMapModel = _this.visualMapModel; // TODO: TYPE More detailed action types\n\n      visualMapModel.option.hoverLink && _this.api.dispatchAction({\n        type: method,\n        batch: makeHighDownBatch(visualMapModel.findTargetDataIndices(pieceIndex), visualMapModel)\n      });\n    };\n  };\n\n  PiecewiseVisualMapView.prototype._getItemAlign = function () {\n    var visualMapModel = this.visualMapModel;\n    var modelOption = visualMapModel.option;\n\n    if (modelOption.orient === 'vertical') {\n      return getItemAlign(visualMapModel, this.api, visualMapModel.itemSize);\n    } else {\n      // horizontal, most case left unless specifying right.\n      var align = modelOption.align;\n\n      if (!align || align === 'auto') {\n        align = 'left';\n      }\n\n      return align;\n    }\n  };\n\n  PiecewiseVisualMapView.prototype._renderEndsText = function (group, text, itemSize, showLabel, itemAlign) {\n    if (!text) {\n      return;\n    }\n\n    var itemGroup = new Group();\n    var textStyleModel = this.visualMapModel.textStyleModel;\n    itemGroup.add(new ZRText({\n      style: createTextStyle(textStyleModel, {\n        x: showLabel ? itemAlign === 'right' ? itemSize[0] : 0 : itemSize[0] / 2,\n        y: itemSize[1] / 2,\n        verticalAlign: 'middle',\n        align: showLabel ? itemAlign : 'center',\n        text: text\n      })\n    }));\n    group.add(itemGroup);\n  };\n  /**\n   * @private\n   * @return {Object} {peiceList, endsText} The order is the same as screen pixel order.\n   */\n\n\n  PiecewiseVisualMapView.prototype._getViewData = function () {\n    var visualMapModel = this.visualMapModel;\n    var viewPieceList = map(visualMapModel.getPieceList(), function (piece, index) {\n      return {\n        piece: piece,\n        indexInModelPieceList: index\n      };\n    });\n    var endsText = visualMapModel.get('text'); // Consider orient and inverse.\n\n    var orient = visualMapModel.get('orient');\n    var inverse = visualMapModel.get('inverse'); // Order of model pieceList is always [low, ..., high]\n\n    if (orient === 'horizontal' ? inverse : !inverse) {\n      viewPieceList.reverse();\n    } // Origin order of endsText is [high, low]\n    else if (endsText) {\n        endsText = endsText.slice().reverse();\n      }\n\n    return {\n      viewPieceList: viewPieceList,\n      endsText: endsText\n    };\n  };\n\n  PiecewiseVisualMapView.prototype._createItemSymbol = function (group, representValue, shapeParam) {\n    group.add(createSymbol( // symbol will be string\n    this.getControllerVisual(representValue, 'symbol'), shapeParam[0], shapeParam[1], shapeParam[2], shapeParam[3], // color will be string\n    this.getControllerVisual(representValue, 'color')));\n  };\n\n  PiecewiseVisualMapView.prototype._onItemClick = function (piece) {\n    var visualMapModel = this.visualMapModel;\n    var option = visualMapModel.option;\n    var selectedMode = option.selectedMode;\n\n    if (!selectedMode) {\n      return;\n    }\n\n    var selected = clone(option.selected);\n    var newKey = visualMapModel.getSelectedMapKey(piece);\n\n    if (selectedMode === 'single' || selectedMode === true) {\n      selected[newKey] = true;\n      each(selected, function (o, key) {\n        selected[key] = key === newKey;\n      });\n    } else {\n      selected[newKey] = !selected[newKey];\n    }\n\n    this.api.dispatchAction({\n      type: 'selectDataRange',\n      from: this.uid,\n      visualMapId: this.visualMapModel.id,\n      selected: selected\n    });\n  };\n\n  PiecewiseVisualMapView.type = 'visualMap.piecewise';\n  return PiecewiseVisualMapView;\n}(VisualMapView);\n\nfunction install$O(registers) {\n  registers.registerComponentModel(PiecewiseModel);\n  registers.registerComponentView(PiecewiseVisualMapView);\n  installCommon$1(registers);\n}\n\nfunction install$P(registers) {\n  use(install$N);\n  use(install$O); // Do not install './dataZoomSelect',\n  // since it only work for toolbox dataZoom.\n}\n\nvar DEFAULT_OPTION = {\n  label: {\n    enabled: true\n  },\n  decal: {\n    show: false\n  }\n};\nvar inner$l = makeInner();\nvar decalPaletteScope = {};\nfunction ariaVisual(ecModel, api) {\n  var ariaModel = ecModel.getModel('aria'); // See \"area enabled\" detection code in `GlobalModel.ts`.\n\n  if (!ariaModel.get('enabled')) {\n    return;\n  }\n\n  var defaultOption = clone(DEFAULT_OPTION);\n  merge(defaultOption.label, ecModel.getLocaleModel().get('aria'), false);\n  merge(ariaModel.option, defaultOption, false);\n  setDecal();\n  setLabel();\n\n  function setDecal() {\n    var decalModel = ariaModel.getModel('decal');\n    var useDecal = decalModel.get('show');\n\n    if (useDecal) {\n      // Each type of series use one scope.\n      // Pie and funnel are using different scopes.\n      var paletteScopeGroupByType_1 = createHashMap();\n      ecModel.eachSeries(function (seriesModel) {\n        if (seriesModel.isColorBySeries()) {\n          return;\n        }\n\n        var decalScope = paletteScopeGroupByType_1.get(seriesModel.type);\n\n        if (!decalScope) {\n          decalScope = {};\n          paletteScopeGroupByType_1.set(seriesModel.type, decalScope);\n        }\n\n        inner$l(seriesModel).scope = decalScope;\n      });\n      ecModel.eachRawSeries(function (seriesModel) {\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        if (isFunction(seriesModel.enableAriaDecal)) {\n          // Let series define how to use decal palette on data\n          seriesModel.enableAriaDecal();\n          return;\n        }\n\n        var data = seriesModel.getData();\n\n        if (!seriesModel.isColorBySeries()) {\n          var dataAll_1 = seriesModel.getRawData();\n          var idxMap_1 = {};\n          var decalScope_1 = inner$l(seriesModel).scope;\n          data.each(function (idx) {\n            var rawIdx = data.getRawIndex(idx);\n            idxMap_1[rawIdx] = idx;\n          });\n          var dataCount_1 = dataAll_1.count();\n          dataAll_1.each(function (rawIdx) {\n            var idx = idxMap_1[rawIdx];\n            var name = dataAll_1.getName(rawIdx) || rawIdx + '';\n            var paletteDecal = getDecalFromPalette(seriesModel.ecModel, name, decalScope_1, dataCount_1);\n            var specifiedDecal = data.getItemVisual(idx, 'decal');\n            data.setItemVisual(idx, 'decal', mergeDecal(specifiedDecal, paletteDecal));\n          });\n        } else {\n          var paletteDecal = getDecalFromPalette(seriesModel.ecModel, seriesModel.name, decalPaletteScope, ecModel.getSeriesCount());\n          var specifiedDecal = data.getVisual('decal');\n          data.setVisual('decal', mergeDecal(specifiedDecal, paletteDecal));\n        }\n\n        function mergeDecal(specifiedDecal, paletteDecal) {\n          // Merge decal from palette to decal from itemStyle.\n          // User do not need to specify all of the decal props.\n          var resultDecal = specifiedDecal ? extend(extend({}, paletteDecal), specifiedDecal) : paletteDecal;\n          resultDecal.dirty = true;\n          return resultDecal;\n        }\n      });\n    }\n  }\n\n  function setLabel() {\n    var labelLocale = ecModel.getLocaleModel().get('aria');\n    var labelModel = ariaModel.getModel('label');\n    labelModel.option = defaults(labelModel.option, labelLocale);\n\n    if (!labelModel.get('enabled')) {\n      return;\n    }\n\n    var dom = api.getZr().dom;\n\n    if (labelModel.get('description')) {\n      dom.setAttribute('aria-label', labelModel.get('description'));\n      return;\n    }\n\n    var seriesCnt = ecModel.getSeriesCount();\n    var maxDataCnt = labelModel.get(['data', 'maxCount']) || 10;\n    var maxSeriesCnt = labelModel.get(['series', 'maxCount']) || 10;\n    var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt);\n    var ariaLabel;\n\n    if (seriesCnt < 1) {\n      // No series, no aria label\n      return;\n    } else {\n      var title = getTitle();\n\n      if (title) {\n        var withTitle = labelModel.get(['general', 'withTitle']);\n        ariaLabel = replace(withTitle, {\n          title: title\n        });\n      } else {\n        ariaLabel = labelModel.get(['general', 'withoutTitle']);\n      }\n\n      var seriesLabels_1 = [];\n      var prefix = seriesCnt > 1 ? labelModel.get(['series', 'multiple', 'prefix']) : labelModel.get(['series', 'single', 'prefix']);\n      ariaLabel += replace(prefix, {\n        seriesCount: seriesCnt\n      });\n      ecModel.eachSeries(function (seriesModel, idx) {\n        if (idx < displaySeriesCnt) {\n          var seriesLabel = void 0;\n          var seriesName = seriesModel.get('name');\n          var withName = seriesName ? 'withName' : 'withoutName';\n          seriesLabel = seriesCnt > 1 ? labelModel.get(['series', 'multiple', withName]) : labelModel.get(['series', 'single', withName]);\n          seriesLabel = replace(seriesLabel, {\n            seriesId: seriesModel.seriesIndex,\n            seriesName: seriesModel.get('name'),\n            seriesType: getSeriesTypeName(seriesModel.subType)\n          });\n          var data = seriesModel.getData();\n\n          if (data.count() > maxDataCnt) {\n            // Show part of data\n            var partialLabel = labelModel.get(['data', 'partialData']);\n            seriesLabel += replace(partialLabel, {\n              displayCnt: maxDataCnt\n            });\n          } else {\n            seriesLabel += labelModel.get(['data', 'allData']);\n          }\n\n          var middleSeparator_1 = labelModel.get(['data', 'separator', 'middle']);\n          var endSeparator_1 = labelModel.get(['data', 'separator', 'end']);\n          var dataLabels = [];\n\n          for (var i = 0; i < data.count(); i++) {\n            if (i < maxDataCnt) {\n              var name_1 = data.getName(i);\n              var value = data.getValues(i);\n              var dataLabel = labelModel.get(['data', name_1 ? 'withName' : 'withoutName']);\n              dataLabels.push(replace(dataLabel, {\n                name: name_1,\n                value: value.join(middleSeparator_1)\n              }));\n            }\n          }\n\n          seriesLabel += dataLabels.join(middleSeparator_1) + endSeparator_1;\n          seriesLabels_1.push(seriesLabel);\n        }\n      });\n      var separatorModel = labelModel.getModel(['series', 'multiple', 'separator']);\n      var middleSeparator = separatorModel.get('middle');\n      var endSeparator = separatorModel.get('end');\n      ariaLabel += seriesLabels_1.join(middleSeparator) + endSeparator;\n      dom.setAttribute('aria-label', ariaLabel);\n    }\n  }\n\n  function replace(str, keyValues) {\n    if (!isString(str)) {\n      return str;\n    }\n\n    var result = str;\n    each(keyValues, function (value, key) {\n      result = result.replace(new RegExp('\\\\{\\\\s*' + key + '\\\\s*\\\\}', 'g'), value);\n    });\n    return result;\n  }\n\n  function getTitle() {\n    var title = ecModel.get('title');\n\n    if (title && title.length) {\n      title = title[0];\n    }\n\n    return title && title.text;\n  }\n\n  function getSeriesTypeName(type) {\n    return ecModel.getLocaleModel().get(['series', 'typeNames'])[type] || '自定义图';\n  }\n}\n\nfunction ariaPreprocessor(option) {\n  if (!option || !option.aria) {\n    return;\n  }\n\n  var aria = option.aria; // aria.show is deprecated and should use aria.enabled instead\n\n  if (aria.show != null) {\n    aria.enabled = aria.show;\n  }\n\n  aria.label = aria.label || {}; // move description, general, series, data to be under aria.label\n\n  each(['description', 'general', 'series', 'data'], function (name) {\n    if (aria[name] != null) {\n      aria.label[name] = aria[name];\n    }\n  });\n}\n\nfunction install$Q(registers) {\n  registers.registerPreprocessor(ariaPreprocessor);\n  registers.registerVisual(registers.PRIORITY.VISUAL.ARIA, ariaVisual);\n}\n\nvar RELATIONAL_EXPRESSION_OP_ALIAS_MAP = {\n  value: 'eq',\n  // PENDING: not good for literal semantic?\n  '<': 'lt',\n  '<=': 'lte',\n  '>': 'gt',\n  '>=': 'gte',\n  '=': 'eq',\n  '!=': 'ne',\n  '<>': 'ne' // Might be misleading for sake of the difference between '==' and '===',\n  // so don't support them.\n  // '==': 'eq',\n  // '===': 'seq',\n  // '!==': 'sne'\n  // PENDING: Whether support some common alias \"ge\", \"le\", \"neq\"?\n  // ge: 'gte',\n  // le: 'lte',\n  // neq: 'ne',\n\n}; // type RelationalExpressionOpEvaluate = (tarVal: unknown, condVal: unknown) => boolean;\n\nvar RegExpEvaluator =\n/** @class */\nfunction () {\n  function RegExpEvaluator(rVal) {\n    // Support condVal: RegExp | string\n    var condValue = this._condVal = isString(rVal) ? new RegExp(rVal) : isRegExp(rVal) ? rVal : null;\n\n    if (condValue == null) {\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        errMsg = makePrintable('Illegal regexp', rVal, 'in');\n      }\n\n      throwError(errMsg);\n    }\n  }\n\n  RegExpEvaluator.prototype.evaluate = function (lVal) {\n    var type = typeof lVal;\n    return isString(type) ? this._condVal.test(lVal) : isNumber(type) ? this._condVal.test(lVal + '') : false;\n  };\n\n  return RegExpEvaluator;\n}();\n\nvar ConstConditionInternal =\n/** @class */\nfunction () {\n  function ConstConditionInternal() {}\n\n  ConstConditionInternal.prototype.evaluate = function () {\n    return this.value;\n  };\n\n  return ConstConditionInternal;\n}();\n\nvar AndConditionInternal =\n/** @class */\nfunction () {\n  function AndConditionInternal() {}\n\n  AndConditionInternal.prototype.evaluate = function () {\n    var children = this.children;\n\n    for (var i = 0; i < children.length; i++) {\n      if (!children[i].evaluate()) {\n        return false;\n      }\n    }\n\n    return true;\n  };\n\n  return AndConditionInternal;\n}();\n\nvar OrConditionInternal =\n/** @class */\nfunction () {\n  function OrConditionInternal() {}\n\n  OrConditionInternal.prototype.evaluate = function () {\n    var children = this.children;\n\n    for (var i = 0; i < children.length; i++) {\n      if (children[i].evaluate()) {\n        return true;\n      }\n    }\n\n    return false;\n  };\n\n  return OrConditionInternal;\n}();\n\nvar NotConditionInternal =\n/** @class */\nfunction () {\n  function NotConditionInternal() {}\n\n  NotConditionInternal.prototype.evaluate = function () {\n    return !this.child.evaluate();\n  };\n\n  return NotConditionInternal;\n}();\n\nvar RelationalConditionInternal =\n/** @class */\nfunction () {\n  function RelationalConditionInternal() {}\n\n  RelationalConditionInternal.prototype.evaluate = function () {\n    var needParse = !!this.valueParser; // Call getValue with no `this`.\n\n    var getValue = this.getValue;\n    var tarValRaw = getValue(this.valueGetterParam);\n    var tarValParsed = needParse ? this.valueParser(tarValRaw) : null; // Relational cond follow \"and\" logic internally.\n\n    for (var i = 0; i < this.subCondList.length; i++) {\n      if (!this.subCondList[i].evaluate(needParse ? tarValParsed : tarValRaw)) {\n        return false;\n      }\n    }\n\n    return true;\n  };\n\n  return RelationalConditionInternal;\n}();\n\nfunction parseOption(exprOption, getters) {\n  if (exprOption === true || exprOption === false) {\n    var cond = new ConstConditionInternal();\n    cond.value = exprOption;\n    return cond;\n  }\n\n  var errMsg = '';\n\n  if (!isObjectNotArray(exprOption)) {\n    if (\"development\" !== 'production') {\n      errMsg = makePrintable('Illegal config. Expect a plain object but actually', exprOption);\n    }\n\n    throwError(errMsg);\n  }\n\n  if (exprOption.and) {\n    return parseAndOrOption('and', exprOption, getters);\n  } else if (exprOption.or) {\n    return parseAndOrOption('or', exprOption, getters);\n  } else if (exprOption.not) {\n    return parseNotOption(exprOption, getters);\n  }\n\n  return parseRelationalOption(exprOption, getters);\n}\n\nfunction parseAndOrOption(op, exprOption, getters) {\n  var subOptionArr = exprOption[op];\n  var errMsg = '';\n\n  if (\"development\" !== 'production') {\n    errMsg = makePrintable('\"and\"/\"or\" condition should only be `' + op + ': [...]` and must not be empty array.', 'Illegal condition:', exprOption);\n  }\n\n  if (!isArray(subOptionArr)) {\n    throwError(errMsg);\n  }\n\n  if (!subOptionArr.length) {\n    throwError(errMsg);\n  }\n\n  var cond = op === 'and' ? new AndConditionInternal() : new OrConditionInternal();\n  cond.children = map(subOptionArr, function (subOption) {\n    return parseOption(subOption, getters);\n  });\n\n  if (!cond.children.length) {\n    throwError(errMsg);\n  }\n\n  return cond;\n}\n\nfunction parseNotOption(exprOption, getters) {\n  var subOption = exprOption.not;\n  var errMsg = '';\n\n  if (\"development\" !== 'production') {\n    errMsg = makePrintable('\"not\" condition should only be `not: {}`.', 'Illegal condition:', exprOption);\n  }\n\n  if (!isObjectNotArray(subOption)) {\n    throwError(errMsg);\n  }\n\n  var cond = new NotConditionInternal();\n  cond.child = parseOption(subOption, getters);\n\n  if (!cond.child) {\n    throwError(errMsg);\n  }\n\n  return cond;\n}\n\nfunction parseRelationalOption(exprOption, getters) {\n  var errMsg = '';\n  var valueGetterParam = getters.prepareGetValue(exprOption);\n  var subCondList = [];\n  var exprKeys = keys(exprOption);\n  var parserName = exprOption.parser;\n  var valueParser = parserName ? getRawValueParser(parserName) : null;\n\n  for (var i = 0; i < exprKeys.length; i++) {\n    var keyRaw = exprKeys[i];\n\n    if (keyRaw === 'parser' || getters.valueGetterAttrMap.get(keyRaw)) {\n      continue;\n    }\n\n    var op = hasOwn(RELATIONAL_EXPRESSION_OP_ALIAS_MAP, keyRaw) ? RELATIONAL_EXPRESSION_OP_ALIAS_MAP[keyRaw] : keyRaw;\n    var condValueRaw = exprOption[keyRaw];\n    var condValueParsed = valueParser ? valueParser(condValueRaw) : condValueRaw;\n    var evaluator = createFilterComparator(op, condValueParsed) || op === 'reg' && new RegExpEvaluator(condValueParsed);\n\n    if (!evaluator) {\n      if (\"development\" !== 'production') {\n        errMsg = makePrintable('Illegal relational operation: \"' + keyRaw + '\" in condition:', exprOption);\n      }\n\n      throwError(errMsg);\n    }\n\n    subCondList.push(evaluator);\n  }\n\n  if (!subCondList.length) {\n    if (\"development\" !== 'production') {\n      errMsg = makePrintable('Relational condition must have at least one operator.', 'Illegal condition:', exprOption);\n    } // No relational operator always disabled in case of dangers result.\n\n\n    throwError(errMsg);\n  }\n\n  var cond = new RelationalConditionInternal();\n  cond.valueGetterParam = valueGetterParam;\n  cond.valueParser = valueParser;\n  cond.getValue = getters.getValue;\n  cond.subCondList = subCondList;\n  return cond;\n}\n\nfunction isObjectNotArray(val) {\n  return isObject(val) && !isArrayLike(val);\n}\n\nvar ConditionalExpressionParsed =\n/** @class */\nfunction () {\n  function ConditionalExpressionParsed(exprOption, getters) {\n    this._cond = parseOption(exprOption, getters);\n  }\n\n  ConditionalExpressionParsed.prototype.evaluate = function () {\n    return this._cond.evaluate();\n  };\n\n  return ConditionalExpressionParsed;\n}();\nfunction parseConditionalExpression(exprOption, getters) {\n  return new ConditionalExpressionParsed(exprOption, getters);\n}\n\nvar filterTransform = {\n  type: 'echarts:filter',\n  // PEDING: enhance to filter by index rather than create new data\n  transform: function (params) {\n    // [Caveat] Fail-Fast:\n    // Do not return the whole dataset unless user config indicate it explicitly.\n    // For example, if no condition specified by mistake, return an empty result\n    // is better than return the entire raw soruce for user to find the mistake.\n    var upstream = params.upstream;\n    var rawItem;\n    var condition = parseConditionalExpression(params.config, {\n      valueGetterAttrMap: createHashMap({\n        dimension: true\n      }),\n      prepareGetValue: function (exprOption) {\n        var errMsg = '';\n        var dimLoose = exprOption.dimension;\n\n        if (!hasOwn(exprOption, 'dimension')) {\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('Relation condition must has prop \"dimension\" specified.', 'Illegal condition:', exprOption);\n          }\n\n          throwError(errMsg);\n        }\n\n        var dimInfo = upstream.getDimensionInfo(dimLoose);\n\n        if (!dimInfo) {\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('Can not find dimension info via: ' + dimLoose + '.\\n', 'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\\n', 'Illegal condition:', exprOption, '.\\n');\n          }\n\n          throwError(errMsg);\n        }\n\n        return {\n          dimIdx: dimInfo.index\n        };\n      },\n      getValue: function (param) {\n        return upstream.retrieveValueFromItem(rawItem, param.dimIdx);\n      }\n    });\n    var resultData = [];\n\n    for (var i = 0, len = upstream.count(); i < len; i++) {\n      rawItem = upstream.getRawDataItem(i);\n\n      if (condition.evaluate()) {\n        resultData.push(rawItem);\n      }\n    }\n\n    return {\n      data: resultData\n    };\n  }\n};\n\nvar sampleLog = '';\n\nif (\"development\" !== 'production') {\n  sampleLog = ['Valid config is like:', '{ dimension: \"age\", order: \"asc\" }', 'or [{ dimension: \"age\", order: \"asc\"], { dimension: \"date\", order: \"desc\" }]'].join(' ');\n}\n\nvar sortTransform = {\n  type: 'echarts:sort',\n  transform: function (params) {\n    var upstream = params.upstream;\n    var config = params.config;\n    var errMsg = ''; // Normalize\n    // const orderExprList: OrderExpression[] = isArray(config[0])\n    //     ? config as OrderExpression[]\n    //     : [config as OrderExpression];\n\n    var orderExprList = normalizeToArray(config);\n\n    if (!orderExprList.length) {\n      if (\"development\" !== 'production') {\n        errMsg = 'Empty `config` in sort transform.';\n      }\n\n      throwError(errMsg);\n    }\n\n    var orderDefList = [];\n    each(orderExprList, function (orderExpr) {\n      var dimLoose = orderExpr.dimension;\n      var order = orderExpr.order;\n      var parserName = orderExpr.parser;\n      var incomparable = orderExpr.incomparable;\n\n      if (dimLoose == null) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Sort transform config must has \"dimension\" specified.' + sampleLog;\n        }\n\n        throwError(errMsg);\n      }\n\n      if (order !== 'asc' && order !== 'desc') {\n        if (\"development\" !== 'production') {\n          errMsg = 'Sort transform config must has \"order\" specified.' + sampleLog;\n        }\n\n        throwError(errMsg);\n      }\n\n      if (incomparable && incomparable !== 'min' && incomparable !== 'max') {\n        var errMsg_1 = '';\n\n        if (\"development\" !== 'production') {\n          errMsg_1 = 'incomparable must be \"min\" or \"max\" rather than \"' + incomparable + '\".';\n        }\n\n        throwError(errMsg_1);\n      }\n\n      if (order !== 'asc' && order !== 'desc') {\n        var errMsg_2 = '';\n\n        if (\"development\" !== 'production') {\n          errMsg_2 = 'order must be \"asc\" or \"desc\" rather than \"' + order + '\".';\n        }\n\n        throwError(errMsg_2);\n      }\n\n      var dimInfo = upstream.getDimensionInfo(dimLoose);\n\n      if (!dimInfo) {\n        if (\"development\" !== 'production') {\n          errMsg = makePrintable('Can not find dimension info via: ' + dimLoose + '.\\n', 'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\\n', 'Illegal config:', orderExpr, '.\\n');\n        }\n\n        throwError(errMsg);\n      }\n\n      var parser = parserName ? getRawValueParser(parserName) : null;\n\n      if (parserName && !parser) {\n        if (\"development\" !== 'production') {\n          errMsg = makePrintable('Invalid parser name ' + parserName + '.\\n', 'Illegal config:', orderExpr, '.\\n');\n        }\n\n        throwError(errMsg);\n      }\n\n      orderDefList.push({\n        dimIdx: dimInfo.index,\n        parser: parser,\n        comparator: new SortOrderComparator(order, incomparable)\n      });\n    }); // TODO: support it?\n\n    var sourceFormat = upstream.sourceFormat;\n\n    if (sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS) {\n      if (\"development\" !== 'production') {\n        errMsg = 'sourceFormat \"' + sourceFormat + '\" is not supported yet';\n      }\n\n      throwError(errMsg);\n    } // Other upstream format are all array.\n\n\n    var resultData = [];\n\n    for (var i = 0, len = upstream.count(); i < len; i++) {\n      resultData.push(upstream.getRawDataItem(i));\n    }\n\n    resultData.sort(function (item0, item1) {\n      for (var i = 0; i < orderDefList.length; i++) {\n        var orderDef = orderDefList[i];\n        var val0 = upstream.retrieveValueFromItem(item0, orderDef.dimIdx);\n        var val1 = upstream.retrieveValueFromItem(item1, orderDef.dimIdx);\n\n        if (orderDef.parser) {\n          val0 = orderDef.parser(val0);\n          val1 = orderDef.parser(val1);\n        }\n\n        var result = orderDef.comparator.evaluate(val0, val1);\n\n        if (result !== 0) {\n          return result;\n        }\n      }\n\n      return 0;\n    });\n    return {\n      data: resultData\n    };\n  }\n};\n\nfunction install$R(registers) {\n  registers.registerTransform(filterTransform);\n  registers.registerTransform(sortTransform);\n}\n\nvar DatasetModel =\n/** @class */\nfunction (_super) {\n  __extends(DatasetModel, _super);\n\n  function DatasetModel() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = 'dataset';\n    return _this;\n  }\n\n  DatasetModel.prototype.init = function (option, parentModel, ecModel) {\n    _super.prototype.init.call(this, option, parentModel, ecModel);\n\n    this._sourceManager = new SourceManager(this);\n    disableTransformOptionMerge(this);\n  };\n\n  DatasetModel.prototype.mergeOption = function (newOption, ecModel) {\n    _super.prototype.mergeOption.call(this, newOption, ecModel);\n\n    disableTransformOptionMerge(this);\n  };\n\n  DatasetModel.prototype.optionUpdated = function () {\n    this._sourceManager.dirty();\n  };\n\n  DatasetModel.prototype.getSourceManager = function () {\n    return this._sourceManager;\n  };\n\n  DatasetModel.type = 'dataset';\n  DatasetModel.defaultOption = {\n    seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN\n  };\n  return DatasetModel;\n}(ComponentModel);\n\nvar DatasetView =\n/** @class */\nfunction (_super) {\n  __extends(DatasetView, _super);\n\n  function DatasetView() {\n    var _this = _super !== null && _super.apply(this, arguments) || this;\n\n    _this.type = 'dataset';\n    return _this;\n  }\n\n  DatasetView.type = 'dataset';\n  return DatasetView;\n}(ComponentView);\n\nfunction install$S(registers) {\n  registers.registerComponentModel(DatasetModel);\n  registers.registerComponentView(DatasetView);\n}\n\nvar CMD$4 = PathProxy.CMD;\nfunction aroundEqual(a, b) {\n    return Math.abs(a - b) < 1e-5;\n}\nfunction pathToBezierCurves(path) {\n    var data = path.data;\n    var len = path.len();\n    var bezierArrayGroups = [];\n    var currentSubpath;\n    var xi = 0;\n    var yi = 0;\n    var x0 = 0;\n    var y0 = 0;\n    function createNewSubpath(x, y) {\n        if (currentSubpath && currentSubpath.length > 2) {\n            bezierArrayGroups.push(currentSubpath);\n        }\n        currentSubpath = [x, y];\n    }\n    function addLine(x0, y0, x1, y1) {\n        if (!(aroundEqual(x0, x1) && aroundEqual(y0, y1))) {\n            currentSubpath.push(x0, y0, x1, y1, x1, y1);\n        }\n    }\n    function addArc(startAngle, endAngle, cx, cy, rx, ry) {\n        var delta = Math.abs(endAngle - startAngle);\n        var len = Math.tan(delta / 4) * 4 / 3;\n        var dir = endAngle < startAngle ? -1 : 1;\n        var c1 = Math.cos(startAngle);\n        var s1 = Math.sin(startAngle);\n        var c2 = Math.cos(endAngle);\n        var s2 = Math.sin(endAngle);\n        var x1 = c1 * rx + cx;\n        var y1 = s1 * ry + cy;\n        var x4 = c2 * rx + cx;\n        var y4 = s2 * ry + cy;\n        var hx = rx * len * dir;\n        var hy = ry * len * dir;\n        currentSubpath.push(x1 - hx * s1, y1 + hy * c1, x4 + hx * s2, y4 - hy * c2, x4, y4);\n    }\n    var x1;\n    var y1;\n    var x2;\n    var y2;\n    for (var i = 0; i < len;) {\n        var cmd = data[i++];\n        var isFirst = i === 1;\n        if (isFirst) {\n            xi = data[i];\n            yi = data[i + 1];\n            x0 = xi;\n            y0 = yi;\n            if (cmd === CMD$4.L || cmd === CMD$4.C || cmd === CMD$4.Q) {\n                currentSubpath = [x0, y0];\n            }\n        }\n        switch (cmd) {\n            case CMD$4.M:\n                xi = x0 = data[i++];\n                yi = y0 = data[i++];\n                createNewSubpath(x0, y0);\n                break;\n            case CMD$4.L:\n                x1 = data[i++];\n                y1 = data[i++];\n                addLine(xi, yi, x1, y1);\n                xi = x1;\n                yi = y1;\n                break;\n            case CMD$4.C:\n                currentSubpath.push(data[i++], data[i++], data[i++], data[i++], xi = data[i++], yi = data[i++]);\n                break;\n            case CMD$4.Q:\n                x1 = data[i++];\n                y1 = data[i++];\n                x2 = data[i++];\n                y2 = data[i++];\n                currentSubpath.push(xi + 2 / 3 * (x1 - xi), yi + 2 / 3 * (y1 - yi), x2 + 2 / 3 * (x1 - x2), y2 + 2 / 3 * (y1 - y2), x2, y2);\n                xi = x2;\n                yi = y2;\n                break;\n            case CMD$4.A:\n                var cx = data[i++];\n                var cy = data[i++];\n                var rx = data[i++];\n                var ry = data[i++];\n                var startAngle = data[i++];\n                var endAngle = data[i++] + startAngle;\n                i += 1;\n                var anticlockwise = !data[i++];\n                x1 = Math.cos(startAngle) * rx + cx;\n                y1 = Math.sin(startAngle) * ry + cy;\n                if (isFirst) {\n                    x0 = x1;\n                    y0 = y1;\n                    createNewSubpath(x0, y0);\n                }\n                else {\n                    addLine(xi, yi, x1, y1);\n                }\n                xi = Math.cos(endAngle) * rx + cx;\n                yi = Math.sin(endAngle) * ry + cy;\n                var step = (anticlockwise ? -1 : 1) * Math.PI / 2;\n                for (var angle = startAngle; anticlockwise ? angle > endAngle : angle < endAngle; angle += step) {\n                    var nextAngle = anticlockwise ? Math.max(angle + step, endAngle)\n                        : Math.min(angle + step, endAngle);\n                    addArc(angle, nextAngle, cx, cy, rx, ry);\n                }\n                break;\n            case CMD$4.R:\n                x0 = xi = data[i++];\n                y0 = yi = data[i++];\n                x1 = x0 + data[i++];\n                y1 = y0 + data[i++];\n                createNewSubpath(x1, y0);\n                addLine(x1, y0, x1, y1);\n                addLine(x1, y1, x0, y1);\n                addLine(x0, y1, x0, y0);\n                addLine(x0, y0, x1, y0);\n                break;\n            case CMD$4.Z:\n                currentSubpath && addLine(xi, yi, x0, y0);\n                xi = x0;\n                yi = y0;\n                break;\n        }\n    }\n    if (currentSubpath && currentSubpath.length > 2) {\n        bezierArrayGroups.push(currentSubpath);\n    }\n    return bezierArrayGroups;\n}\nfunction adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, out, scale) {\n    if (aroundEqual(x0, x1) && aroundEqual(y0, y1) && aroundEqual(x2, x3) && aroundEqual(y2, y3)) {\n        out.push(x3, y3);\n        return;\n    }\n    var PIXEL_DISTANCE = 2 / scale;\n    var PIXEL_DISTANCE_SQR = PIXEL_DISTANCE * PIXEL_DISTANCE;\n    var dx = x3 - x0;\n    var dy = y3 - y0;\n    var d = Math.sqrt(dx * dx + dy * dy);\n    dx /= d;\n    dy /= d;\n    var dx1 = x1 - x0;\n    var dy1 = y1 - y0;\n    var dx2 = x2 - x3;\n    var dy2 = y2 - y3;\n    var cp1LenSqr = dx1 * dx1 + dy1 * dy1;\n    var cp2LenSqr = dx2 * dx2 + dy2 * dy2;\n    if (cp1LenSqr < PIXEL_DISTANCE_SQR && cp2LenSqr < PIXEL_DISTANCE_SQR) {\n        out.push(x3, y3);\n        return;\n    }\n    var projLen1 = dx * dx1 + dy * dy1;\n    var projLen2 = -dx * dx2 - dy * dy2;\n    var d1Sqr = cp1LenSqr - projLen1 * projLen1;\n    var d2Sqr = cp2LenSqr - projLen2 * projLen2;\n    if (d1Sqr < PIXEL_DISTANCE_SQR && projLen1 >= 0\n        && d2Sqr < PIXEL_DISTANCE_SQR && projLen2 >= 0) {\n        out.push(x3, y3);\n        return;\n    }\n    var tmpSegX = [];\n    var tmpSegY = [];\n    cubicSubdivide(x0, x1, x2, x3, 0.5, tmpSegX);\n    cubicSubdivide(y0, y1, y2, y3, 0.5, tmpSegY);\n    adpativeBezier(tmpSegX[0], tmpSegY[0], tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], tmpSegX[3], tmpSegY[3], out, scale);\n    adpativeBezier(tmpSegX[4], tmpSegY[4], tmpSegX[5], tmpSegY[5], tmpSegX[6], tmpSegY[6], tmpSegX[7], tmpSegY[7], out, scale);\n}\nfunction pathToPolygons(path, scale) {\n    var bezierArrayGroups = pathToBezierCurves(path);\n    var polygons = [];\n    scale = scale || 1;\n    for (var i = 0; i < bezierArrayGroups.length; i++) {\n        var beziers = bezierArrayGroups[i];\n        var polygon = [];\n        var x0 = beziers[0];\n        var y0 = beziers[1];\n        polygon.push(x0, y0);\n        for (var k = 2; k < beziers.length;) {\n            var x1 = beziers[k++];\n            var y1 = beziers[k++];\n            var x2 = beziers[k++];\n            var y2 = beziers[k++];\n            var x3 = beziers[k++];\n            var y3 = beziers[k++];\n            adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, polygon, scale);\n            x0 = x3;\n            y0 = y3;\n        }\n        polygons.push(polygon);\n    }\n    return polygons;\n}\n\nfunction getDividingGrids(dimSize, rowDim, count) {\n    var rowSize = dimSize[rowDim];\n    var columnSize = dimSize[1 - rowDim];\n    var ratio = Math.abs(rowSize / columnSize);\n    var rowCount = Math.ceil(Math.sqrt(ratio * count));\n    var columnCount = Math.floor(count / rowCount);\n    if (columnCount === 0) {\n        columnCount = 1;\n        rowCount = count;\n    }\n    var grids = [];\n    for (var i = 0; i < rowCount; i++) {\n        grids.push(columnCount);\n    }\n    var currentCount = rowCount * columnCount;\n    var remained = count - currentCount;\n    if (remained > 0) {\n        for (var i = 0; i < remained; i++) {\n            grids[i % rowCount] += 1;\n        }\n    }\n    return grids;\n}\nfunction divideSector(sectorShape, count, outShapes) {\n    var r0 = sectorShape.r0;\n    var r = sectorShape.r;\n    var startAngle = sectorShape.startAngle;\n    var endAngle = sectorShape.endAngle;\n    var angle = Math.abs(endAngle - startAngle);\n    var arcLen = angle * r;\n    var deltaR = r - r0;\n    var isAngleRow = arcLen > Math.abs(deltaR);\n    var grids = getDividingGrids([arcLen, deltaR], isAngleRow ? 0 : 1, count);\n    var rowSize = (isAngleRow ? angle : deltaR) / grids.length;\n    for (var row = 0; row < grids.length; row++) {\n        var columnSize = (isAngleRow ? deltaR : angle) / grids[row];\n        for (var column = 0; column < grids[row]; column++) {\n            var newShape = {};\n            if (isAngleRow) {\n                newShape.startAngle = startAngle + rowSize * row;\n                newShape.endAngle = startAngle + rowSize * (row + 1);\n                newShape.r0 = r0 + columnSize * column;\n                newShape.r = r0 + columnSize * (column + 1);\n            }\n            else {\n                newShape.startAngle = startAngle + columnSize * column;\n                newShape.endAngle = startAngle + columnSize * (column + 1);\n                newShape.r0 = r0 + rowSize * row;\n                newShape.r = r0 + rowSize * (row + 1);\n            }\n            newShape.clockwise = sectorShape.clockwise;\n            newShape.cx = sectorShape.cx;\n            newShape.cy = sectorShape.cy;\n            outShapes.push(newShape);\n        }\n    }\n}\nfunction divideRect(rectShape, count, outShapes) {\n    var width = rectShape.width;\n    var height = rectShape.height;\n    var isHorizontalRow = width > height;\n    var grids = getDividingGrids([width, height], isHorizontalRow ? 0 : 1, count);\n    var rowSizeDim = isHorizontalRow ? 'width' : 'height';\n    var columnSizeDim = isHorizontalRow ? 'height' : 'width';\n    var rowDim = isHorizontalRow ? 'x' : 'y';\n    var columnDim = isHorizontalRow ? 'y' : 'x';\n    var rowSize = rectShape[rowSizeDim] / grids.length;\n    for (var row = 0; row < grids.length; row++) {\n        var columnSize = rectShape[columnSizeDim] / grids[row];\n        for (var column = 0; column < grids[row]; column++) {\n            var newShape = {};\n            newShape[rowDim] = row * rowSize;\n            newShape[columnDim] = column * columnSize;\n            newShape[rowSizeDim] = rowSize;\n            newShape[columnSizeDim] = columnSize;\n            newShape.x += rectShape.x;\n            newShape.y += rectShape.y;\n            outShapes.push(newShape);\n        }\n    }\n}\nfunction crossProduct2d$1(x1, y1, x2, y2) {\n    return x1 * y2 - x2 * y1;\n}\nfunction lineLineIntersect$1(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {\n    var mx = a2x - a1x;\n    var my = a2y - a1y;\n    var nx = b2x - b1x;\n    var ny = b2y - b1y;\n    var nmCrossProduct = crossProduct2d$1(nx, ny, mx, my);\n    if (Math.abs(nmCrossProduct) < 1e-6) {\n        return null;\n    }\n    var b1a1x = a1x - b1x;\n    var b1a1y = a1y - b1y;\n    var p = crossProduct2d$1(b1a1x, b1a1y, nx, ny) / nmCrossProduct;\n    if (p < 0 || p > 1) {\n        return null;\n    }\n    return new Point(p * mx + a1x, p * my + a1y);\n}\nfunction projPtOnLine(pt, lineA, lineB) {\n    var dir = new Point();\n    Point.sub(dir, lineB, lineA);\n    dir.normalize();\n    var dir2 = new Point();\n    Point.sub(dir2, pt, lineA);\n    var len = dir2.dot(dir);\n    return len;\n}\nfunction addToPoly(poly, pt) {\n    var last = poly[poly.length - 1];\n    if (last && last[0] === pt[0] && last[1] === pt[1]) {\n        return;\n    }\n    poly.push(pt);\n}\nfunction splitPolygonByLine(points, lineA, lineB) {\n    var len = points.length;\n    var intersections = [];\n    for (var i = 0; i < len; i++) {\n        var p0 = points[i];\n        var p1 = points[(i + 1) % len];\n        var intersectionPt = lineLineIntersect$1(p0[0], p0[1], p1[0], p1[1], lineA.x, lineA.y, lineB.x, lineB.y);\n        if (intersectionPt) {\n            intersections.push({\n                projPt: projPtOnLine(intersectionPt, lineA, lineB),\n                pt: intersectionPt,\n                idx: i\n            });\n        }\n    }\n    if (intersections.length < 2) {\n        return [{ points: points }, { points: points }];\n    }\n    intersections.sort(function (a, b) {\n        return a.projPt - b.projPt;\n    });\n    var splitPt0 = intersections[0];\n    var splitPt1 = intersections[intersections.length - 1];\n    if (splitPt1.idx < splitPt0.idx) {\n        var tmp = splitPt0;\n        splitPt0 = splitPt1;\n        splitPt1 = tmp;\n    }\n    var splitPt0Arr = [splitPt0.pt.x, splitPt0.pt.y];\n    var splitPt1Arr = [splitPt1.pt.x, splitPt1.pt.y];\n    var newPolyA = [splitPt0Arr];\n    var newPolyB = [splitPt1Arr];\n    for (var i = splitPt0.idx + 1; i <= splitPt1.idx; i++) {\n        addToPoly(newPolyA, points[i].slice());\n    }\n    addToPoly(newPolyA, splitPt1Arr);\n    addToPoly(newPolyA, splitPt0Arr);\n    for (var i = splitPt1.idx + 1; i <= splitPt0.idx + len; i++) {\n        addToPoly(newPolyB, points[i % len].slice());\n    }\n    addToPoly(newPolyB, splitPt0Arr);\n    addToPoly(newPolyB, splitPt1Arr);\n    return [{\n            points: newPolyA\n        }, {\n            points: newPolyB\n        }];\n}\nfunction binaryDividePolygon(polygonShape) {\n    var points = polygonShape.points;\n    var min = [];\n    var max = [];\n    fromPoints(points, min, max);\n    var boundingRect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n    var width = boundingRect.width;\n    var height = boundingRect.height;\n    var x = boundingRect.x;\n    var y = boundingRect.y;\n    var pt0 = new Point();\n    var pt1 = new Point();\n    if (width > height) {\n        pt0.x = pt1.x = x + width / 2;\n        pt0.y = y;\n        pt1.y = y + height;\n    }\n    else {\n        pt0.y = pt1.y = y + height / 2;\n        pt0.x = x;\n        pt1.x = x + width;\n    }\n    return splitPolygonByLine(points, pt0, pt1);\n}\nfunction binaryDivideRecursive(divider, shape, count, out) {\n    if (count === 1) {\n        out.push(shape);\n    }\n    else {\n        var mid = Math.floor(count / 2);\n        var sub = divider(shape);\n        binaryDivideRecursive(divider, sub[0], mid, out);\n        binaryDivideRecursive(divider, sub[1], count - mid, out);\n    }\n    return out;\n}\nfunction clone$4(path, count) {\n    var paths = [];\n    for (var i = 0; i < count; i++) {\n        paths.push(clonePath(path));\n    }\n    return paths;\n}\nfunction copyPathProps(source, target) {\n    target.setStyle(source.style);\n    target.z = source.z;\n    target.z2 = source.z2;\n    target.zlevel = source.zlevel;\n}\nfunction polygonConvert(points) {\n    var out = [];\n    for (var i = 0; i < points.length;) {\n        out.push([points[i++], points[i++]]);\n    }\n    return out;\n}\nfunction split(path, count) {\n    var outShapes = [];\n    var shape = path.shape;\n    var OutShapeCtor;\n    switch (path.type) {\n        case 'rect':\n            divideRect(shape, count, outShapes);\n            OutShapeCtor = Rect;\n            break;\n        case 'sector':\n            divideSector(shape, count, outShapes);\n            OutShapeCtor = Sector;\n            break;\n        case 'circle':\n            divideSector({\n                r0: 0, r: shape.r, startAngle: 0, endAngle: Math.PI * 2,\n                cx: shape.cx, cy: shape.cy\n            }, count, outShapes);\n            OutShapeCtor = Sector;\n            break;\n        default:\n            var m = path.getComputedTransform();\n            var scale = m ? Math.sqrt(Math.max(m[0] * m[0] + m[1] * m[1], m[2] * m[2] + m[3] * m[3])) : 1;\n            var polygons = map(pathToPolygons(path.getUpdatedPathProxy(), scale), function (poly) { return polygonConvert(poly); });\n            var polygonCount = polygons.length;\n            if (polygonCount === 0) {\n                binaryDivideRecursive(binaryDividePolygon, {\n                    points: polygons[0]\n                }, count, outShapes);\n            }\n            else if (polygonCount === count) {\n                for (var i = 0; i < polygonCount; i++) {\n                    outShapes.push({\n                        points: polygons[i]\n                    });\n                }\n            }\n            else {\n                var totalArea_1 = 0;\n                var items = map(polygons, function (poly) {\n                    var min = [];\n                    var max = [];\n                    fromPoints(poly, min, max);\n                    var area = (max[1] - min[1]) * (max[0] - min[0]);\n                    totalArea_1 += area;\n                    return { poly: poly, area: area };\n                });\n                items.sort(function (a, b) { return b.area - a.area; });\n                var left = count;\n                for (var i = 0; i < polygonCount; i++) {\n                    var item = items[i];\n                    if (left <= 0) {\n                        break;\n                    }\n                    var selfCount = i === polygonCount - 1\n                        ? left\n                        : Math.ceil(item.area / totalArea_1 * count);\n                    if (selfCount < 0) {\n                        continue;\n                    }\n                    binaryDivideRecursive(binaryDividePolygon, {\n                        points: item.poly\n                    }, selfCount, outShapes);\n                    left -= selfCount;\n                }\n            }\n            OutShapeCtor = Polygon;\n            break;\n    }\n    if (!OutShapeCtor) {\n        return clone$4(path, count);\n    }\n    var out = [];\n    for (var i = 0; i < outShapes.length; i++) {\n        var subPath = new OutShapeCtor();\n        subPath.setShape(outShapes[i]);\n        copyPathProps(path, subPath);\n        out.push(subPath);\n    }\n    return out;\n}\n\nfunction alignSubpath(subpath1, subpath2) {\n    var len1 = subpath1.length;\n    var len2 = subpath2.length;\n    if (len1 === len2) {\n        return [subpath1, subpath2];\n    }\n    var tmpSegX = [];\n    var tmpSegY = [];\n    var shorterPath = len1 < len2 ? subpath1 : subpath2;\n    var shorterLen = Math.min(len1, len2);\n    var diff = Math.abs(len2 - len1) / 6;\n    var shorterBezierCount = (shorterLen - 2) / 6;\n    var eachCurveSubDivCount = Math.ceil(diff / shorterBezierCount) + 1;\n    var newSubpath = [shorterPath[0], shorterPath[1]];\n    var remained = diff;\n    for (var i = 2; i < shorterLen;) {\n        var x0 = shorterPath[i - 2];\n        var y0 = shorterPath[i - 1];\n        var x1 = shorterPath[i++];\n        var y1 = shorterPath[i++];\n        var x2 = shorterPath[i++];\n        var y2 = shorterPath[i++];\n        var x3 = shorterPath[i++];\n        var y3 = shorterPath[i++];\n        if (remained <= 0) {\n            newSubpath.push(x1, y1, x2, y2, x3, y3);\n            continue;\n        }\n        var actualSubDivCount = Math.min(remained, eachCurveSubDivCount - 1) + 1;\n        for (var k = 1; k <= actualSubDivCount; k++) {\n            var p = k / actualSubDivCount;\n            cubicSubdivide(x0, x1, x2, x3, p, tmpSegX);\n            cubicSubdivide(y0, y1, y2, y3, p, tmpSegY);\n            x0 = tmpSegX[3];\n            y0 = tmpSegY[3];\n            newSubpath.push(tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], x0, y0);\n            x1 = tmpSegX[5];\n            y1 = tmpSegY[5];\n            x2 = tmpSegX[6];\n            y2 = tmpSegY[6];\n        }\n        remained -= actualSubDivCount - 1;\n    }\n    return shorterPath === subpath1 ? [newSubpath, subpath2] : [subpath1, newSubpath];\n}\nfunction createSubpath(lastSubpathSubpath, otherSubpath) {\n    var len = lastSubpathSubpath.length;\n    var lastX = lastSubpathSubpath[len - 2];\n    var lastY = lastSubpathSubpath[len - 1];\n    var newSubpath = [];\n    for (var i = 0; i < otherSubpath.length;) {\n        newSubpath[i++] = lastX;\n        newSubpath[i++] = lastY;\n    }\n    return newSubpath;\n}\nfunction alignBezierCurves(array1, array2) {\n    var _a;\n    var lastSubpath1;\n    var lastSubpath2;\n    var newArray1 = [];\n    var newArray2 = [];\n    for (var i = 0; i < Math.max(array1.length, array2.length); i++) {\n        var subpath1 = array1[i];\n        var subpath2 = array2[i];\n        var newSubpath1 = void 0;\n        var newSubpath2 = void 0;\n        if (!subpath1) {\n            newSubpath1 = createSubpath(lastSubpath1 || subpath2, subpath2);\n            newSubpath2 = subpath2;\n        }\n        else if (!subpath2) {\n            newSubpath2 = createSubpath(lastSubpath2 || subpath1, subpath1);\n            newSubpath1 = subpath1;\n        }\n        else {\n            _a = alignSubpath(subpath1, subpath2), newSubpath1 = _a[0], newSubpath2 = _a[1];\n            lastSubpath1 = newSubpath1;\n            lastSubpath2 = newSubpath2;\n        }\n        newArray1.push(newSubpath1);\n        newArray2.push(newSubpath2);\n    }\n    return [newArray1, newArray2];\n}\nfunction centroid$1(array) {\n    var signedArea = 0;\n    var cx = 0;\n    var cy = 0;\n    var len = array.length;\n    for (var i = 0, j = len - 2; i < len; j = i, i += 2) {\n        var x0 = array[j];\n        var y0 = array[j + 1];\n        var x1 = array[i];\n        var y1 = array[i + 1];\n        var a = x0 * y1 - x1 * y0;\n        signedArea += a;\n        cx += (x0 + x1) * a;\n        cy += (y0 + y1) * a;\n    }\n    if (signedArea === 0) {\n        return [array[0] || 0, array[1] || 0];\n    }\n    return [cx / signedArea / 3, cy / signedArea / 3, signedArea];\n}\nfunction findBestRingOffset(fromSubBeziers, toSubBeziers, fromCp, toCp) {\n    var bezierCount = (fromSubBeziers.length - 2) / 6;\n    var bestScore = Infinity;\n    var bestOffset = 0;\n    var len = fromSubBeziers.length;\n    var len2 = len - 2;\n    for (var offset = 0; offset < bezierCount; offset++) {\n        var cursorOffset = offset * 6;\n        var score = 0;\n        for (var k = 0; k < len; k += 2) {\n            var idx = k === 0 ? cursorOffset : ((cursorOffset + k - 2) % len2 + 2);\n            var x0 = fromSubBeziers[idx] - fromCp[0];\n            var y0 = fromSubBeziers[idx + 1] - fromCp[1];\n            var x1 = toSubBeziers[k] - toCp[0];\n            var y1 = toSubBeziers[k + 1] - toCp[1];\n            var dx = x1 - x0;\n            var dy = y1 - y0;\n            score += dx * dx + dy * dy;\n        }\n        if (score < bestScore) {\n            bestScore = score;\n            bestOffset = offset;\n        }\n    }\n    return bestOffset;\n}\nfunction reverse(array) {\n    var newArr = [];\n    var len = array.length;\n    for (var i = 0; i < len; i += 2) {\n        newArr[i] = array[len - i - 2];\n        newArr[i + 1] = array[len - i - 1];\n    }\n    return newArr;\n}\nfunction findBestMorphingRotation(fromArr, toArr, searchAngleIteration, searchAngleRange) {\n    var result = [];\n    var fromNeedsReverse;\n    for (var i = 0; i < fromArr.length; i++) {\n        var fromSubpathBezier = fromArr[i];\n        var toSubpathBezier = toArr[i];\n        var fromCp = centroid$1(fromSubpathBezier);\n        var toCp = centroid$1(toSubpathBezier);\n        if (fromNeedsReverse == null) {\n            fromNeedsReverse = fromCp[2] < 0 !== toCp[2] < 0;\n        }\n        var newFromSubpathBezier = [];\n        var newToSubpathBezier = [];\n        var bestAngle = 0;\n        var bestScore = Infinity;\n        var tmpArr = [];\n        var len = fromSubpathBezier.length;\n        if (fromNeedsReverse) {\n            fromSubpathBezier = reverse(fromSubpathBezier);\n        }\n        var offset = findBestRingOffset(fromSubpathBezier, toSubpathBezier, fromCp, toCp) * 6;\n        var len2 = len - 2;\n        for (var k = 0; k < len2; k += 2) {\n            var idx = (offset + k) % len2 + 2;\n            newFromSubpathBezier[k + 2] = fromSubpathBezier[idx] - fromCp[0];\n            newFromSubpathBezier[k + 3] = fromSubpathBezier[idx + 1] - fromCp[1];\n        }\n        newFromSubpathBezier[0] = fromSubpathBezier[offset] - fromCp[0];\n        newFromSubpathBezier[1] = fromSubpathBezier[offset + 1] - fromCp[1];\n        if (searchAngleIteration > 0) {\n            var step = searchAngleRange / searchAngleIteration;\n            for (var angle = -searchAngleRange / 2; angle <= searchAngleRange / 2; angle += step) {\n                var sa = Math.sin(angle);\n                var ca = Math.cos(angle);\n                var score = 0;\n                for (var k = 0; k < fromSubpathBezier.length; k += 2) {\n                    var x0 = newFromSubpathBezier[k];\n                    var y0 = newFromSubpathBezier[k + 1];\n                    var x1 = toSubpathBezier[k] - toCp[0];\n                    var y1 = toSubpathBezier[k + 1] - toCp[1];\n                    var newX1 = x1 * ca - y1 * sa;\n                    var newY1 = x1 * sa + y1 * ca;\n                    tmpArr[k] = newX1;\n                    tmpArr[k + 1] = newY1;\n                    var dx = newX1 - x0;\n                    var dy = newY1 - y0;\n                    score += dx * dx + dy * dy;\n                }\n                if (score < bestScore) {\n                    bestScore = score;\n                    bestAngle = angle;\n                    for (var m = 0; m < tmpArr.length; m++) {\n                        newToSubpathBezier[m] = tmpArr[m];\n                    }\n                }\n            }\n        }\n        else {\n            for (var i_1 = 0; i_1 < len; i_1 += 2) {\n                newToSubpathBezier[i_1] = toSubpathBezier[i_1] - toCp[0];\n                newToSubpathBezier[i_1 + 1] = toSubpathBezier[i_1 + 1] - toCp[1];\n            }\n        }\n        result.push({\n            from: newFromSubpathBezier,\n            to: newToSubpathBezier,\n            fromCp: fromCp,\n            toCp: toCp,\n            rotation: -bestAngle\n        });\n    }\n    return result;\n}\nfunction isCombineMorphing(path) {\n    return path.__isCombineMorphing;\n}\nvar SAVED_METHOD_PREFIX = '__mOriginal_';\nfunction saveAndModifyMethod(obj, methodName, modifiers) {\n    var savedMethodName = SAVED_METHOD_PREFIX + methodName;\n    var originalMethod = obj[savedMethodName] || obj[methodName];\n    if (!obj[savedMethodName]) {\n        obj[savedMethodName] = obj[methodName];\n    }\n    var replace = modifiers.replace;\n    var after = modifiers.after;\n    var before = modifiers.before;\n    obj[methodName] = function () {\n        var args = arguments;\n        var res;\n        before && before.apply(this, args);\n        if (replace) {\n            res = replace.apply(this, args);\n        }\n        else {\n            res = originalMethod.apply(this, args);\n        }\n        after && after.apply(this, args);\n        return res;\n    };\n}\nfunction restoreMethod(obj, methodName) {\n    var savedMethodName = SAVED_METHOD_PREFIX + methodName;\n    if (obj[savedMethodName]) {\n        obj[methodName] = obj[savedMethodName];\n        obj[savedMethodName] = null;\n    }\n}\nfunction applyTransformOnBeziers(bezierCurves, mm) {\n    for (var i = 0; i < bezierCurves.length; i++) {\n        var subBeziers = bezierCurves[i];\n        for (var k = 0; k < subBeziers.length;) {\n            var x = subBeziers[k];\n            var y = subBeziers[k + 1];\n            subBeziers[k++] = mm[0] * x + mm[2] * y + mm[4];\n            subBeziers[k++] = mm[1] * x + mm[3] * y + mm[5];\n        }\n    }\n}\nfunction prepareMorphPath(fromPath, toPath) {\n    var fromPathProxy = fromPath.getUpdatedPathProxy();\n    var toPathProxy = toPath.getUpdatedPathProxy();\n    var _a = alignBezierCurves(pathToBezierCurves(fromPathProxy), pathToBezierCurves(toPathProxy)), fromBezierCurves = _a[0], toBezierCurves = _a[1];\n    var fromPathTransform = fromPath.getComputedTransform();\n    var toPathTransform = toPath.getComputedTransform();\n    function updateIdentityTransform() {\n        this.transform = null;\n    }\n    fromPathTransform && applyTransformOnBeziers(fromBezierCurves, fromPathTransform);\n    toPathTransform && applyTransformOnBeziers(toBezierCurves, toPathTransform);\n    saveAndModifyMethod(toPath, 'updateTransform', { replace: updateIdentityTransform });\n    toPath.transform = null;\n    var morphingData = findBestMorphingRotation(fromBezierCurves, toBezierCurves, 10, Math.PI);\n    var tmpArr = [];\n    saveAndModifyMethod(toPath, 'buildPath', { replace: function (path) {\n            var t = toPath.__morphT;\n            var onet = 1 - t;\n            var newCp = [];\n            for (var i = 0; i < morphingData.length; i++) {\n                var item = morphingData[i];\n                var from = item.from;\n                var to = item.to;\n                var angle = item.rotation * t;\n                var fromCp = item.fromCp;\n                var toCp = item.toCp;\n                var sa = Math.sin(angle);\n                var ca = Math.cos(angle);\n                lerp(newCp, fromCp, toCp, t);\n                for (var m = 0; m < from.length; m += 2) {\n                    var x0_1 = from[m];\n                    var y0_1 = from[m + 1];\n                    var x1 = to[m];\n                    var y1 = to[m + 1];\n                    var x = x0_1 * onet + x1 * t;\n                    var y = y0_1 * onet + y1 * t;\n                    tmpArr[m] = (x * ca - y * sa) + newCp[0];\n                    tmpArr[m + 1] = (x * sa + y * ca) + newCp[1];\n                }\n                var x0 = tmpArr[0];\n                var y0 = tmpArr[1];\n                path.moveTo(x0, y0);\n                for (var m = 2; m < from.length;) {\n                    var x1 = tmpArr[m++];\n                    var y1 = tmpArr[m++];\n                    var x2 = tmpArr[m++];\n                    var y2 = tmpArr[m++];\n                    var x3 = tmpArr[m++];\n                    var y3 = tmpArr[m++];\n                    if (x0 === x1 && y0 === y1 && x2 === x3 && y2 === y3) {\n                        path.lineTo(x3, y3);\n                    }\n                    else {\n                        path.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n                    }\n                    x0 = x3;\n                    y0 = y3;\n                }\n            }\n        } });\n}\nfunction morphPath(fromPath, toPath, animationOpts) {\n    if (!fromPath || !toPath) {\n        return toPath;\n    }\n    var oldDone = animationOpts.done;\n    var oldDuring = animationOpts.during;\n    prepareMorphPath(fromPath, toPath);\n    toPath.__morphT = 0;\n    function restoreToPath() {\n        restoreMethod(toPath, 'buildPath');\n        restoreMethod(toPath, 'updateTransform');\n        toPath.__morphT = -1;\n        toPath.createPathProxy();\n        toPath.dirtyShape();\n    }\n    toPath.animateTo({\n        __morphT: 1\n    }, defaults({\n        during: function (p) {\n            toPath.dirtyShape();\n            oldDuring && oldDuring(p);\n        },\n        done: function () {\n            restoreToPath();\n            oldDone && oldDone();\n        }\n    }, animationOpts));\n    return toPath;\n}\nfunction hilbert(x, y, minX, minY, maxX, maxY) {\n    var bits = 16;\n    x = (maxX === minX) ? 0 : Math.round(32767 * (x - minX) / (maxX - minX));\n    y = (maxY === minY) ? 0 : Math.round(32767 * (y - minY) / (maxY - minY));\n    var d = 0;\n    var tmp;\n    for (var s = (1 << bits) / 2; s > 0; s /= 2) {\n        var rx = 0;\n        var ry = 0;\n        if ((x & s) > 0) {\n            rx = 1;\n        }\n        if ((y & s) > 0) {\n            ry = 1;\n        }\n        d += s * s * ((3 * rx) ^ ry);\n        if (ry === 0) {\n            if (rx === 1) {\n                x = s - 1 - x;\n                y = s - 1 - y;\n            }\n            tmp = x;\n            x = y;\n            y = tmp;\n        }\n    }\n    return d;\n}\nfunction sortPaths(pathList) {\n    var xMin = Infinity;\n    var yMin = Infinity;\n    var xMax = -Infinity;\n    var yMax = -Infinity;\n    var cps = map(pathList, function (path) {\n        var rect = path.getBoundingRect();\n        var m = path.getComputedTransform();\n        var x = rect.x + rect.width / 2 + (m ? m[4] : 0);\n        var y = rect.y + rect.height / 2 + (m ? m[5] : 0);\n        xMin = Math.min(x, xMin);\n        yMin = Math.min(y, yMin);\n        xMax = Math.max(x, xMax);\n        yMax = Math.max(y, yMax);\n        return [x, y];\n    });\n    var items = map(cps, function (cp, idx) {\n        return {\n            cp: cp,\n            z: hilbert(cp[0], cp[1], xMin, yMin, xMax, yMax),\n            path: pathList[idx]\n        };\n    });\n    return items.sort(function (a, b) { return a.z - b.z; }).map(function (item) { return item.path; });\n}\nfunction defaultDividePath(param) {\n    return split(param.path, param.count);\n}\nfunction createEmptyReturn() {\n    return {\n        fromIndividuals: [],\n        toIndividuals: [],\n        count: 0\n    };\n}\nfunction combineMorph(fromList, toPath, animationOpts) {\n    var fromPathList = [];\n    function addFromPath(fromList) {\n        for (var i = 0; i < fromList.length; i++) {\n            var from = fromList[i];\n            if (isCombineMorphing(from)) {\n                addFromPath(from.childrenRef());\n            }\n            else if (from instanceof Path) {\n                fromPathList.push(from);\n            }\n        }\n    }\n    addFromPath(fromList);\n    var separateCount = fromPathList.length;\n    if (!separateCount) {\n        return createEmptyReturn();\n    }\n    var dividePath = animationOpts.dividePath || defaultDividePath;\n    var toSubPathList = dividePath({\n        path: toPath, count: separateCount\n    });\n    if (toSubPathList.length !== separateCount) {\n        console.error('Invalid morphing: unmatched splitted path');\n        return createEmptyReturn();\n    }\n    fromPathList = sortPaths(fromPathList);\n    toSubPathList = sortPaths(toSubPathList);\n    var oldDone = animationOpts.done;\n    var oldDuring = animationOpts.during;\n    var individualDelay = animationOpts.individualDelay;\n    var identityTransform = new Transformable();\n    for (var i = 0; i < separateCount; i++) {\n        var from = fromPathList[i];\n        var to = toSubPathList[i];\n        to.parent = toPath;\n        to.copyTransform(identityTransform);\n        if (!individualDelay) {\n            prepareMorphPath(from, to);\n        }\n    }\n    toPath.__isCombineMorphing = true;\n    toPath.childrenRef = function () {\n        return toSubPathList;\n    };\n    function addToSubPathListToZr(zr) {\n        for (var i = 0; i < toSubPathList.length; i++) {\n            toSubPathList[i].addSelfToZr(zr);\n        }\n    }\n    saveAndModifyMethod(toPath, 'addSelfToZr', {\n        after: function (zr) {\n            addToSubPathListToZr(zr);\n        }\n    });\n    saveAndModifyMethod(toPath, 'removeSelfFromZr', {\n        after: function (zr) {\n            for (var i = 0; i < toSubPathList.length; i++) {\n                toSubPathList[i].removeSelfFromZr(zr);\n            }\n        }\n    });\n    function restoreToPath() {\n        toPath.__isCombineMorphing = false;\n        toPath.__morphT = -1;\n        toPath.childrenRef = null;\n        restoreMethod(toPath, 'addSelfToZr');\n        restoreMethod(toPath, 'removeSelfFromZr');\n    }\n    var toLen = toSubPathList.length;\n    if (individualDelay) {\n        var animating_1 = toLen;\n        var eachDone = function () {\n            animating_1--;\n            if (animating_1 === 0) {\n                restoreToPath();\n                oldDone && oldDone();\n            }\n        };\n        for (var i = 0; i < toLen; i++) {\n            var indivdualAnimationOpts = individualDelay ? defaults({\n                delay: (animationOpts.delay || 0) + individualDelay(i, toLen, fromPathList[i], toSubPathList[i]),\n                done: eachDone\n            }, animationOpts) : animationOpts;\n            morphPath(fromPathList[i], toSubPathList[i], indivdualAnimationOpts);\n        }\n    }\n    else {\n        toPath.__morphT = 0;\n        toPath.animateTo({\n            __morphT: 1\n        }, defaults({\n            during: function (p) {\n                for (var i = 0; i < toLen; i++) {\n                    var child = toSubPathList[i];\n                    child.__morphT = toPath.__morphT;\n                    child.dirtyShape();\n                }\n                oldDuring && oldDuring(p);\n            },\n            done: function () {\n                restoreToPath();\n                for (var i = 0; i < fromList.length; i++) {\n                    restoreMethod(fromList[i], 'updateTransform');\n                }\n                oldDone && oldDone();\n            }\n        }, animationOpts));\n    }\n    if (toPath.__zr) {\n        addToSubPathListToZr(toPath.__zr);\n    }\n    return {\n        fromIndividuals: fromPathList,\n        toIndividuals: toSubPathList,\n        count: toLen\n    };\n}\nfunction separateMorph(fromPath, toPathList, animationOpts) {\n    var toLen = toPathList.length;\n    var fromPathList = [];\n    var dividePath = animationOpts.dividePath || defaultDividePath;\n    function addFromPath(fromList) {\n        for (var i = 0; i < fromList.length; i++) {\n            var from = fromList[i];\n            if (isCombineMorphing(from)) {\n                addFromPath(from.childrenRef());\n            }\n            else if (from instanceof Path) {\n                fromPathList.push(from);\n            }\n        }\n    }\n    if (isCombineMorphing(fromPath)) {\n        addFromPath(fromPath.childrenRef());\n        var fromLen = fromPathList.length;\n        if (fromLen < toLen) {\n            var k = 0;\n            for (var i = fromLen; i < toLen; i++) {\n                fromPathList.push(clonePath(fromPathList[k++ % fromLen]));\n            }\n        }\n        fromPathList.length = toLen;\n    }\n    else {\n        fromPathList = dividePath({ path: fromPath, count: toLen });\n        var fromPathTransform = fromPath.getComputedTransform();\n        for (var i = 0; i < fromPathList.length; i++) {\n            fromPathList[i].setLocalTransform(fromPathTransform);\n        }\n        if (fromPathList.length !== toLen) {\n            console.error('Invalid morphing: unmatched splitted path');\n            return createEmptyReturn();\n        }\n    }\n    fromPathList = sortPaths(fromPathList);\n    toPathList = sortPaths(toPathList);\n    var individualDelay = animationOpts.individualDelay;\n    for (var i = 0; i < toLen; i++) {\n        var indivdualAnimationOpts = individualDelay ? defaults({\n            delay: (animationOpts.delay || 0) + individualDelay(i, toLen, fromPathList[i], toPathList[i])\n        }, animationOpts) : animationOpts;\n        morphPath(fromPathList[i], toPathList[i], indivdualAnimationOpts);\n    }\n    return {\n        fromIndividuals: fromPathList,\n        toIndividuals: toPathList,\n        count: toPathList.length\n    };\n}\n\nfunction isMultiple(elements) {\n  return isArray(elements[0]);\n}\n\nfunction prepareMorphBatches(one, many) {\n  var batches = [];\n  var batchCount = one.length;\n\n  for (var i = 0; i < batchCount; i++) {\n    batches.push({\n      one: one[i],\n      many: []\n    });\n  }\n\n  for (var i = 0; i < many.length; i++) {\n    var len = many[i].length;\n    var k = void 0;\n\n    for (k = 0; k < len; k++) {\n      batches[k % batchCount].many.push(many[i][k]);\n    }\n  }\n\n  var off = 0; // If one has more paths than each one of many. average them.\n\n  for (var i = batchCount - 1; i >= 0; i--) {\n    if (!batches[i].many.length) {\n      var moveFrom = batches[off].many;\n\n      if (moveFrom.length <= 1) {\n        // Not enough\n        // Start from the first one.\n        if (off) {\n          off = 0;\n        } else {\n          return batches;\n        }\n      }\n\n      var len = moveFrom.length;\n      var mid = Math.ceil(len / 2);\n      batches[i].many = moveFrom.slice(mid, len);\n      batches[off].many = moveFrom.slice(0, mid);\n      off++;\n    }\n  }\n\n  return batches;\n}\n\nvar pathDividers = {\n  clone: function (params) {\n    var ret = []; // Fitting the alpha\n\n    var approxOpacity = 1 - Math.pow(1 - params.path.style.opacity, 1 / params.count);\n\n    for (var i = 0; i < params.count; i++) {\n      var cloned = clonePath(params.path);\n      cloned.setStyle('opacity', approxOpacity);\n      ret.push(cloned);\n    }\n\n    return ret;\n  },\n  // Use the default divider\n  split: null\n};\nfunction applyMorphAnimation(from, to, divideShape, seriesModel, dataIndex, animateOtherProps) {\n  if (!from.length || !to.length) {\n    return;\n  }\n\n  var updateAnimationCfg = getAnimationConfig('update', seriesModel, dataIndex);\n\n  if (!(updateAnimationCfg && updateAnimationCfg.duration > 0)) {\n    return;\n  }\n\n  var animationDelay = seriesModel.getModel('universalTransition').get('delay');\n  var animationCfg = Object.assign({\n    // Need to setToFinal so the further calculation based on the style can be correct.\n    // Like emphasis color.\n    setToFinal: true\n  }, updateAnimationCfg);\n  var many;\n  var one;\n\n  if (isMultiple(from)) {\n    // manyToOne\n    many = from;\n    one = to;\n  }\n\n  if (isMultiple(to)) {\n    // oneToMany\n    many = to;\n    one = from;\n  }\n\n  function morphOneBatch(batch, fromIsMany, animateIndex, animateCount, forceManyOne) {\n    var batchMany = batch.many;\n    var batchOne = batch.one;\n\n    if (batchMany.length === 1 && !forceManyOne) {\n      // Is one to one\n      var batchFrom = fromIsMany ? batchMany[0] : batchOne;\n      var batchTo = fromIsMany ? batchOne : batchMany[0];\n\n      if (isCombineMorphing(batchFrom)) {\n        // Keep doing combine animation.\n        morphOneBatch({\n          many: [batchFrom],\n          one: batchTo\n        }, true, animateIndex, animateCount, true);\n      } else {\n        var individualAnimationCfg = animationDelay ? defaults({\n          delay: animationDelay(animateIndex, animateCount)\n        }, animationCfg) : animationCfg;\n        morphPath(batchFrom, batchTo, individualAnimationCfg);\n        animateOtherProps(batchFrom, batchTo, batchFrom, batchTo, individualAnimationCfg);\n      }\n    } else {\n      var separateAnimationCfg = defaults({\n        dividePath: pathDividers[divideShape],\n        individualDelay: animationDelay && function (idx, count, fromPath, toPath) {\n          return animationDelay(idx + animateIndex, animateCount);\n        }\n      }, animationCfg);\n\n      var _a = fromIsMany ? combineMorph(batchMany, batchOne, separateAnimationCfg) : separateMorph(batchOne, batchMany, separateAnimationCfg),\n          fromIndividuals = _a.fromIndividuals,\n          toIndividuals = _a.toIndividuals;\n\n      var count = fromIndividuals.length;\n\n      for (var k = 0; k < count; k++) {\n        var individualAnimationCfg = animationDelay ? defaults({\n          delay: animationDelay(k, count)\n        }, animationCfg) : animationCfg;\n        animateOtherProps(fromIndividuals[k], toIndividuals[k], fromIsMany ? batchMany[k] : batch.one, fromIsMany ? batch.one : batchMany[k], individualAnimationCfg);\n      }\n    }\n  }\n\n  var fromIsMany = many ? many === from // Is one to one. If the path number not match. also needs do merge and separate morphing.\n  : from.length > to.length;\n  var morphBatches = many ? prepareMorphBatches(one, many) : prepareMorphBatches(fromIsMany ? to : from, [fromIsMany ? from : to]);\n  var animateCount = 0;\n\n  for (var i = 0; i < morphBatches.length; i++) {\n    animateCount += morphBatches[i].many.length;\n  }\n\n  var animateIndex = 0;\n\n  for (var i = 0; i < morphBatches.length; i++) {\n    morphOneBatch(morphBatches[i], fromIsMany, animateIndex, animateCount);\n    animateIndex += morphBatches[i].many.length;\n  }\n}\nfunction getPathList(elements) {\n  if (!elements) {\n    return [];\n  }\n\n  if (isArray(elements)) {\n    var pathList_1 = [];\n\n    for (var i = 0; i < elements.length; i++) {\n      pathList_1.push(getPathList(elements[i]));\n    }\n\n    return pathList_1;\n  }\n\n  var pathList = [];\n  elements.traverse(function (el) {\n    if (el instanceof Path && !el.disableMorphing && !el.invisible && !el.ignore) {\n      pathList.push(el);\n    }\n  });\n  return pathList;\n}\n\nvar DATA_COUNT_THRESHOLD = 1e4;\nvar getUniversalTransitionGlobalStore = makeInner();\n\nfunction getGroupIdDimension(data) {\n  var dimensions = data.dimensions;\n\n  for (var i = 0; i < dimensions.length; i++) {\n    var dimInfo = data.getDimensionInfo(dimensions[i]);\n\n    if (dimInfo && dimInfo.otherDims.itemGroupId === 0) {\n      return dimensions[i];\n    }\n  }\n}\n\nfunction flattenDataDiffItems(list) {\n  var items = [];\n  each(list, function (seriesInfo) {\n    var data = seriesInfo.data;\n\n    if (data.count() > DATA_COUNT_THRESHOLD) {\n      if (\"development\" !== 'production') {\n        warn('Universal transition is disabled on large data > 10k.');\n      }\n\n      return;\n    }\n\n    var indices = data.getIndices();\n    var groupDim = getGroupIdDimension(data);\n\n    for (var dataIndex = 0; dataIndex < indices.length; dataIndex++) {\n      items.push({\n        dataGroupId: seriesInfo.dataGroupId,\n        data: data,\n        dim: seriesInfo.dim || groupDim,\n        divide: seriesInfo.divide,\n        dataIndex: dataIndex\n      });\n    }\n  });\n  return items;\n}\n\nfunction fadeInElement(newEl, newSeries, newIndex) {\n  newEl.traverse(function (el) {\n    if (el instanceof Path) {\n      // TODO use fade in animation for target element.\n      initProps(el, {\n        style: {\n          opacity: 0\n        }\n      }, newSeries, {\n        dataIndex: newIndex,\n        isFrom: true\n      });\n    }\n  });\n}\n\nfunction removeEl$1(el) {\n  if (el.parent) {\n    // Bake parent transform to element.\n    // So it can still have proper transform to transition after it's removed.\n    var computedTransform = el.getComputedTransform();\n    el.setLocalTransform(computedTransform);\n    el.parent.remove(el);\n  }\n}\n\nfunction stopAnimation(el) {\n  el.stopAnimation();\n\n  if (el.isGroup) {\n    el.traverse(function (child) {\n      child.stopAnimation();\n    });\n  }\n}\n\nfunction animateElementStyles(el, dataIndex, seriesModel) {\n  var animationConfig = getAnimationConfig('update', seriesModel, dataIndex);\n  animationConfig && el.traverse(function (child) {\n    if (child instanceof Displayable) {\n      var oldStyle = getOldStyle(child);\n\n      if (oldStyle) {\n        child.animateFrom({\n          style: oldStyle\n        }, animationConfig);\n      }\n    }\n  });\n}\n\nfunction isAllIdSame(oldDiffItems, newDiffItems) {\n  var len = oldDiffItems.length;\n\n  if (len !== newDiffItems.length) {\n    return false;\n  }\n\n  for (var i = 0; i < len; i++) {\n    var oldItem = oldDiffItems[i];\n    var newItem = newDiffItems[i];\n\n    if (oldItem.data.getId(oldItem.dataIndex) !== newItem.data.getId(newItem.dataIndex)) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nfunction transitionBetween(oldList, newList, api) {\n  var oldDiffItems = flattenDataDiffItems(oldList);\n  var newDiffItems = flattenDataDiffItems(newList);\n\n  function updateMorphingPathProps(from, to, rawFrom, rawTo, animationCfg) {\n    if (rawFrom || from) {\n      to.animateFrom({\n        style: rawFrom && rawFrom !== from ? // dividingMethod like clone may override the style(opacity)\n        // So extend it to raw style.\n        extend(extend({}, rawFrom.style), from.style) : from.style\n      }, animationCfg);\n    }\n  }\n\n  function findKeyDim(items) {\n    for (var i = 0; i < items.length; i++) {\n      if (items[i].dim) {\n        return items[i].dim;\n      }\n    }\n  }\n\n  var oldKeyDim = findKeyDim(oldDiffItems);\n  var newKeyDim = findKeyDim(newDiffItems);\n  var hasMorphAnimation = false;\n\n  function createKeyGetter(isOld, onlyGetId) {\n    return function (diffItem) {\n      var data = diffItem.data;\n      var dataIndex = diffItem.dataIndex; // TODO if specified dim\n\n      if (onlyGetId) {\n        return data.getId(dataIndex);\n      } // Use group id as transition key by default.\n      // So we can achieve multiple to multiple animation like drilldown / up naturally.\n      // If group id not exits. Use id instead. If so, only one to one transition will be applied.\n\n\n      var dataGroupId = diffItem.dataGroupId; // If specified key dimension(itemGroupId by default). Use this same dimension from other data.\n      // PENDING: If only use key dimension of newData.\n\n      var keyDim = isOld ? oldKeyDim || newKeyDim : newKeyDim || oldKeyDim;\n      var dimInfo = keyDim && data.getDimensionInfo(keyDim);\n      var dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta;\n\n      if (dimInfo) {\n        // Get from encode.itemGroupId.\n        var key = data.get(dimInfo.name, dataIndex);\n\n        if (dimOrdinalMeta) {\n          return dimOrdinalMeta.categories[key] || key + '';\n        }\n\n        return key + '';\n      } // Get groupId from raw item. { groupId: '' }\n\n\n      var itemVal = data.getRawDataItem(dataIndex);\n\n      if (itemVal && itemVal.groupId) {\n        return itemVal.groupId + '';\n      }\n\n      return dataGroupId || data.getId(dataIndex);\n    };\n  } // Use id if it's very likely to be an one to one animation\n  // It's more robust than groupId\n  // TODO Check if key dimension is specified.\n\n\n  var useId = isAllIdSame(oldDiffItems, newDiffItems);\n  var isElementStillInChart = {};\n\n  if (!useId) {\n    // We may have different diff strategy with basicTransition if we use other dimension as key.\n    // If so, we can't simply check if oldEl is same with newEl. We need a map to check if oldEl is still being used in the new chart.\n    // We can't use the elements that already being morphed. Let it keep it's original basic transition.\n    for (var i = 0; i < newDiffItems.length; i++) {\n      var newItem = newDiffItems[i];\n      var el = newItem.data.getItemGraphicEl(newItem.dataIndex);\n\n      if (el) {\n        isElementStillInChart[el.id] = true;\n      }\n    }\n  }\n\n  function updateOneToOne(newIndex, oldIndex) {\n    var oldItem = oldDiffItems[oldIndex];\n    var newItem = newDiffItems[newIndex];\n    var newSeries = newItem.data.hostModel; // TODO Mark this elements is morphed and don't morph them anymore\n\n    var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex);\n    var newEl = newItem.data.getItemGraphicEl(newItem.dataIndex); // Can't handle same elements.\n\n    if (oldEl === newEl) {\n      newEl && animateElementStyles(newEl, newItem.dataIndex, newSeries);\n      return;\n    }\n\n    if ( // We can't use the elements that already being morphed\n    oldEl && isElementStillInChart[oldEl.id]) {\n      return;\n    }\n\n    if (newEl) {\n      // TODO: If keep animating the group in case\n      // some of the elements don't want to be morphed.\n      // TODO Label?\n      stopAnimation(newEl);\n\n      if (oldEl) {\n        stopAnimation(oldEl); // If old element is doing leaving animation. stop it and remove it immediately.\n\n        removeEl$1(oldEl);\n        hasMorphAnimation = true;\n        applyMorphAnimation(getPathList(oldEl), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps);\n      } else {\n        fadeInElement(newEl, newSeries, newIndex);\n      }\n    } // else keep oldEl leaving animation.\n\n  }\n\n  new DataDiffer(oldDiffItems, newDiffItems, createKeyGetter(true, useId), createKeyGetter(false, useId), null, 'multiple').update(updateOneToOne).updateManyToOne(function (newIndex, oldIndices) {\n    var newItem = newDiffItems[newIndex];\n    var newData = newItem.data;\n    var newSeries = newData.hostModel;\n    var newEl = newData.getItemGraphicEl(newItem.dataIndex);\n    var oldElsList = filter(map(oldIndices, function (idx) {\n      return oldDiffItems[idx].data.getItemGraphicEl(oldDiffItems[idx].dataIndex);\n    }), function (oldEl) {\n      return oldEl && oldEl !== newEl && !isElementStillInChart[oldEl.id];\n    });\n\n    if (newEl) {\n      stopAnimation(newEl);\n\n      if (oldElsList.length) {\n        // If old element is doing leaving animation. stop it and remove it immediately.\n        each(oldElsList, function (oldEl) {\n          stopAnimation(oldEl);\n          removeEl$1(oldEl);\n        });\n        hasMorphAnimation = true;\n        applyMorphAnimation(getPathList(oldElsList), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps);\n      } else {\n        fadeInElement(newEl, newSeries, newItem.dataIndex);\n      }\n    } // else keep oldEl leaving animation.\n\n  }).updateOneToMany(function (newIndices, oldIndex) {\n    var oldItem = oldDiffItems[oldIndex];\n    var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex); // We can't use the elements that already being morphed\n\n    if (oldEl && isElementStillInChart[oldEl.id]) {\n      return;\n    }\n\n    var newElsList = filter(map(newIndices, function (idx) {\n      return newDiffItems[idx].data.getItemGraphicEl(newDiffItems[idx].dataIndex);\n    }), function (el) {\n      return el && el !== oldEl;\n    });\n    var newSeris = newDiffItems[newIndices[0]].data.hostModel;\n\n    if (newElsList.length) {\n      each(newElsList, function (newEl) {\n        return stopAnimation(newEl);\n      });\n\n      if (oldEl) {\n        stopAnimation(oldEl); // If old element is doing leaving animation. stop it and remove it immediately.\n\n        removeEl$1(oldEl);\n        hasMorphAnimation = true;\n        applyMorphAnimation(getPathList(oldEl), getPathList(newElsList), oldItem.divide, // Use divide on old.\n        newSeris, newIndices[0], updateMorphingPathProps);\n      } else {\n        each(newElsList, function (newEl) {\n          return fadeInElement(newEl, newSeris, newIndices[0]);\n        });\n      }\n    } // else keep oldEl leaving animation.\n\n  }).updateManyToMany(function (newIndices, oldIndices) {\n    // If two data are same and both have groupId.\n    // Normally they should be diff by id.\n    new DataDiffer(oldIndices, newIndices, function (rawIdx) {\n      return oldDiffItems[rawIdx].data.getId(oldDiffItems[rawIdx].dataIndex);\n    }, function (rawIdx) {\n      return newDiffItems[rawIdx].data.getId(newDiffItems[rawIdx].dataIndex);\n    }).update(function (newIndex, oldIndex) {\n      // Use the original index\n      updateOneToOne(newIndices[newIndex], oldIndices[oldIndex]);\n    }).execute();\n  }).execute();\n\n  if (hasMorphAnimation) {\n    each(newList, function (_a) {\n      var data = _a.data;\n      var seriesModel = data.hostModel;\n      var view = seriesModel && api.getViewOfSeriesModel(seriesModel);\n      var animationCfg = getAnimationConfig('update', seriesModel, 0); // use 0 index.\n\n      if (view && seriesModel.isAnimationEnabled() && animationCfg && animationCfg.duration > 0) {\n        view.group.traverse(function (el) {\n          if (el instanceof Path && !el.animators.length) {\n            // We can't accept there still exists element that has no animation\n            // if universalTransition is enabled\n            el.animateFrom({\n              style: {\n                opacity: 0\n              }\n            }, animationCfg);\n          }\n        });\n      }\n    });\n  }\n}\n\nfunction getSeriesTransitionKey(series) {\n  var seriesKey = series.getModel('universalTransition').get('seriesKey');\n\n  if (!seriesKey) {\n    // Use series id by default.\n    return series.id;\n  }\n\n  return seriesKey;\n}\n\nfunction convertArraySeriesKeyToString(seriesKey) {\n  if (isArray(seriesKey)) {\n    // Order independent.\n    return seriesKey.sort().join(',');\n  }\n\n  return seriesKey;\n}\n\nfunction getDivideShapeFromData(data) {\n  if (data.hostModel) {\n    return data.hostModel.getModel('universalTransition').get('divideShape');\n  }\n}\n\nfunction findTransitionSeriesBatches(globalStore, params) {\n  var updateBatches = createHashMap();\n  var oldDataMap = createHashMap(); // Map that only store key in array seriesKey.\n  // Which is used to query the old data when transition from one to multiple series.\n\n  var oldDataMapForSplit = createHashMap();\n  each(globalStore.oldSeries, function (series, idx) {\n    var oldDataGroupId = globalStore.oldDataGroupIds[idx];\n    var oldData = globalStore.oldData[idx];\n    var transitionKey = getSeriesTransitionKey(series);\n    var transitionKeyStr = convertArraySeriesKeyToString(transitionKey);\n    oldDataMap.set(transitionKeyStr, {\n      dataGroupId: oldDataGroupId,\n      data: oldData\n    });\n\n    if (isArray(transitionKey)) {\n      // Same key can't in different array seriesKey.\n      each(transitionKey, function (key) {\n        oldDataMapForSplit.set(key, {\n          key: transitionKeyStr,\n          dataGroupId: oldDataGroupId,\n          data: oldData\n        });\n      });\n    }\n  });\n\n  function checkTransitionSeriesKeyDuplicated(transitionKeyStr) {\n    if (updateBatches.get(transitionKeyStr)) {\n      warn(\"Duplicated seriesKey in universalTransition \" + transitionKeyStr);\n    }\n  }\n\n  each(params.updatedSeries, function (series) {\n    if (series.isUniversalTransitionEnabled() && series.isAnimationEnabled()) {\n      var newDataGroupId = series.get('dataGroupId');\n      var newData = series.getData();\n      var transitionKey = getSeriesTransitionKey(series);\n      var transitionKeyStr = convertArraySeriesKeyToString(transitionKey); // Only transition between series with same id.\n\n      var oldData = oldDataMap.get(transitionKeyStr); // string transition key is the best match.\n\n      if (oldData) {\n        if (\"development\" !== 'production') {\n          checkTransitionSeriesKeyDuplicated(transitionKeyStr);\n        } // TODO check if data is same?\n\n\n        updateBatches.set(transitionKeyStr, {\n          oldSeries: [{\n            dataGroupId: oldData.dataGroupId,\n            divide: getDivideShapeFromData(oldData.data),\n            data: oldData.data\n          }],\n          newSeries: [{\n            dataGroupId: newDataGroupId,\n            divide: getDivideShapeFromData(newData),\n            data: newData\n          }]\n        });\n      } else {\n        // Transition from multiple series.\n        if (isArray(transitionKey)) {\n          if (\"development\" !== 'production') {\n            checkTransitionSeriesKeyDuplicated(transitionKeyStr);\n          }\n\n          var oldSeries_1 = [];\n          each(transitionKey, function (key) {\n            var oldData = oldDataMap.get(key);\n\n            if (oldData.data) {\n              oldSeries_1.push({\n                dataGroupId: oldData.dataGroupId,\n                divide: getDivideShapeFromData(oldData.data),\n                data: oldData.data\n              });\n            }\n          });\n\n          if (oldSeries_1.length) {\n            updateBatches.set(transitionKeyStr, {\n              oldSeries: oldSeries_1,\n              newSeries: [{\n                dataGroupId: newDataGroupId,\n                data: newData,\n                divide: getDivideShapeFromData(newData)\n              }]\n            });\n          }\n        } else {\n          // Try transition to multiple series.\n          var oldData_1 = oldDataMapForSplit.get(transitionKey);\n\n          if (oldData_1) {\n            var batch = updateBatches.get(oldData_1.key);\n\n            if (!batch) {\n              batch = {\n                oldSeries: [{\n                  dataGroupId: oldData_1.dataGroupId,\n                  data: oldData_1.data,\n                  divide: getDivideShapeFromData(oldData_1.data)\n                }],\n                newSeries: []\n              };\n              updateBatches.set(oldData_1.key, batch);\n            }\n\n            batch.newSeries.push({\n              dataGroupId: newDataGroupId,\n              data: newData,\n              divide: getDivideShapeFromData(newData)\n            });\n          }\n        }\n      }\n    }\n  });\n  return updateBatches;\n}\n\nfunction querySeries(series, finder) {\n  for (var i = 0; i < series.length; i++) {\n    var found = finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id;\n\n    if (found) {\n      return i;\n    }\n  }\n}\n\nfunction transitionSeriesFromOpt(transitionOpt, globalStore, params, api) {\n  var from = [];\n  var to = [];\n  each(normalizeToArray(transitionOpt.from), function (finder) {\n    var idx = querySeries(globalStore.oldSeries, finder);\n\n    if (idx >= 0) {\n      from.push({\n        dataGroupId: globalStore.oldDataGroupIds[idx],\n        data: globalStore.oldData[idx],\n        // TODO can specify divideShape in transition.\n        divide: getDivideShapeFromData(globalStore.oldData[idx]),\n        dim: finder.dimension\n      });\n    }\n  });\n  each(normalizeToArray(transitionOpt.to), function (finder) {\n    var idx = querySeries(params.updatedSeries, finder);\n\n    if (idx >= 0) {\n      var data = params.updatedSeries[idx].getData();\n      to.push({\n        dataGroupId: globalStore.oldDataGroupIds[idx],\n        data: data,\n        divide: getDivideShapeFromData(data),\n        dim: finder.dimension\n      });\n    }\n  });\n\n  if (from.length > 0 && to.length > 0) {\n    transitionBetween(from, to, api);\n  }\n}\n\nfunction installUniversalTransition(registers) {\n  registers.registerUpdateLifecycle('series:beforeupdate', function (ecMOdel, api, params) {\n    each(normalizeToArray(params.seriesTransition), function (transOpt) {\n      each(normalizeToArray(transOpt.to), function (finder) {\n        var series = params.updatedSeries;\n\n        for (var i = 0; i < series.length; i++) {\n          if (finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id) {\n            series[i][SERIES_UNIVERSAL_TRANSITION_PROP] = true;\n          }\n        }\n      });\n    });\n  });\n  registers.registerUpdateLifecycle('series:transition', function (ecModel, api, params) {\n    // TODO api provide an namespace that can save stuff per instance\n    var globalStore = getUniversalTransitionGlobalStore(api); // TODO multiple to multiple series.\n\n    if (globalStore.oldSeries && params.updatedSeries && params.optionChanged) {\n      // Use give transition config if its' give;\n      var transitionOpt = params.seriesTransition;\n\n      if (transitionOpt) {\n        each(normalizeToArray(transitionOpt), function (opt) {\n          transitionSeriesFromOpt(opt, globalStore, params, api);\n        });\n      } else {\n        // Else guess from series based on transition series key.\n        var updateBatches_1 = findTransitionSeriesBatches(globalStore, params);\n        each(updateBatches_1.keys(), function (key) {\n          var batch = updateBatches_1.get(key);\n          transitionBetween(batch.oldSeries, batch.newSeries, api);\n        });\n      } // Reset\n\n\n      each(params.updatedSeries, function (series) {\n        // Reset;\n        if (series[SERIES_UNIVERSAL_TRANSITION_PROP]) {\n          series[SERIES_UNIVERSAL_TRANSITION_PROP] = false;\n        }\n      });\n    } // Save all series of current update. Not only the updated one.\n\n\n    var allSeries = ecModel.getSeries();\n    var savedSeries = globalStore.oldSeries = [];\n    var savedDataGroupIds = globalStore.oldDataGroupIds = [];\n    var savedData = globalStore.oldData = [];\n\n    for (var i = 0; i < allSeries.length; i++) {\n      var data = allSeries[i].getData(); // Only save the data that can have transition.\n      // Avoid large data costing too much extra memory\n\n      if (data.count() < DATA_COUNT_THRESHOLD) {\n        savedSeries.push(allSeries[i]);\n        savedDataGroupIds.push(allSeries[i].get('dataGroupId'));\n        savedData.push(data);\n      }\n    }\n  });\n}\n\n// Render engines\n// -----------------\n// Render via Canvas.\n// echarts.init(dom, null, { renderer: 'canvas' })\n\nuse([install$1]); // Render via SVG.\n// echarts.init(dom, null, { renderer: 'svg' })\n\nuse([install]); // ----------------\n// Charts (series)\n// ----------------\n// All of the series types, for example:\n// chart.setOption({\n//     series: [{\n//         type: 'line' // or 'bar', 'pie', ...\n//     }]\n// });\n\nuse([install$2, install$3, install$4, install$6, install$8, install$a, install$b, install$c, install$d, install$e, install$f, install$h, install$i, install$j, install$k, install$l, install$m, install$n, install$o, install$p, install$q, install$r]); // -------------------\n// Coordinate systems\n// -------------------\n// All of the axis modules have been included in the\n// coordinate system module below, do not need to\n// make extra import.\n// `cartesian` coordinate system. For some historical\n// reasons, it is named as grid, for example:\n// chart.setOption({\n//     grid: {...},\n//     xAxis: {...},\n//     yAxis: {...},\n//     series: [{...}]\n// });\n\nuse(install$t); // `polar` coordinate system, for example:\n// chart.setOption({\n//     polar: {...},\n//     radiusAxis: {...},\n//     angleAxis: {...},\n//     series: [{\n//         coordinateSystem: 'polar'\n//     }]\n// });\n\nuse(install$u); // `geo` coordinate system, for example:\n// chart.setOption({\n//     geo: {...},\n//     series: [{\n//         coordinateSystem: 'geo'\n//     }]\n// });\n\nuse(install$9); // `singleAxis` coordinate system (notice, it is a coordinate system\n// with only one axis, work for chart like theme river), for example:\n// chart.setOption({\n//     singleAxis: {...}\n//     series: [{type: 'themeRiver', ...}]\n// });\n\nuse(install$v); // `parallel` coordinate system, only work for parallel series, for example:\n// chart.setOption({\n//     parallel: {...},\n//     parallelAxis: [{...}, ...],\n//     series: [{\n//         type: 'parallel'\n//     }]\n// });\n\nuse(install$g); // `calendar` coordinate system. for example,\n// chart.setOptionp({\n//     calendar: {...},\n//     series: [{\n//         coordinateSystem: 'calendar'\n//     }]\n// );\n\nuse(install$w); // ------------------\n// Other components\n// ------------------\n// `graphic` component, for example:\n// chart.setOption({\n//     graphic: {...}\n// });\n\nuse(install$x); // `toolbox` component, for example:\n// chart.setOption({\n//     toolbox: {...}\n// });\n\nuse(install$z); // `tooltip` component, for example:\n// chart.setOption({\n//     tooltip: {...}\n// });\n\nuse(install$A); // `axisPointer` component, for example:\n// chart.setOption({\n//     tooltip: {axisPointer: {...}, ...}\n// });\n// Or\n// chart.setOption({\n//     axisPointer: {...}\n// });\n\nuse(install$s); // `brush` component, for example:\n// chart.setOption({\n//     brush: {...}\n// });\n// Or\n// chart.setOption({\n//     tooltip: {feature: {brush: {...}}\n// })\n\nuse(install$B); // `title` component, for example:\n// chart.setOption({\n//     title: {...}\n// });\n\nuse(install$C); // `timeline` component, for example:\n// chart.setOption({\n//     timeline: {...}\n// });\n\nuse(install$D); // `markPoint` component, for example:\n// chart.setOption({\n//     series: [{markPoint: {...}}]\n// });\n\nuse(install$E); // `markLine` component, for example:\n// chart.setOption({\n//     series: [{markLine: {...}}]\n// });\n\nuse(install$F); // `markArea` component, for example:\n// chart.setOption({\n//     series: [{markArea: {...}}]\n// });\n\nuse(install$G); // `legend` component not scrollable. for example:\n// chart.setOption({\n//     legend: {...}\n// });\n\nuse(install$J); // `dataZoom` component including both `dataZoomInside` and `dataZoomSlider`.\n\nuse(install$M); // `dataZoom` component providing drag, pinch, wheel behaviors\n// inside coodinate system, for example:\n// chart.setOption({\n//     dataZoom: {type: 'inside'}\n// });\n\nuse(install$K); // `dataZoom` component providing a slider bar, for example:\n// chart.setOption({\n//     dataZoom: {type: 'slider'}\n// });\n\nuse(install$L); // `visualMap` component including both `visualMapContinuous` and `visualMapPiecewise`.\n\nuse(install$P); // `visualMap` component providing continuous bar, for example:\n// chart.setOption({\n//     visualMap: {type: 'continuous'}\n// });\n\nuse(install$N); // `visualMap` component providing pieces bar, for example:\n// chart.setOption({\n//     visualMap: {type: 'piecewise'}\n// });\n\nuse(install$O); // `aria` component providing aria, for example:\n// chart.setOption({\n//     aria: {...}\n// });\n\nuse(install$Q); // dataset transform\n// chart.setOption({\n//     dataset: {\n//          transform: []\n//     }\n// });\n\nuse(install$R);\nuse(install$S); // universal transition\n// chart.setOption({\n//     series: {\n//         universalTransition: { enabled: true }\n//     }\n// })\n\nuse(installUniversalTransition); // label layout\n// chart.setOption({\n//     series: {\n//         labelLayout: { hideOverlap: true }\n//     }\n// })\n\nuse(installLabelLayout);\n\nexport { Axis, ChartView, ComponentModel, ComponentView, SeriesData as List, Model, PRIORITY, SeriesModel, color, connect, dataTool, dependencies, disConnect, disconnect, dispose$1 as dispose, env, extendChartView, extendComponentModel, extendComponentView, extendSeriesModel, format$1 as format, getCoordinateSystemDimensions, getInstanceByDom, getInstanceById, getMap, graphic$1 as graphic, helper, init$1 as init, brushSingle as innerDrawElementOnCanvas, matrix, number, parseGeoJSON, parseGeoJSON as parseGeoJson, registerAction, registerCoordinateSystem, registerLayout, registerLoading, registerLocale, registerMap, registerPostInit, registerPostUpdate, registerPreprocessor, registerProcessor, registerTheme, registerTransform, registerUpdateLifecycle, registerVisual, setCanvasCreator, setPlatformAPI, throttle, time, use, util$1 as util, vector, version$1 as version, util as zrUtil, zrender };\n//# sourceMappingURL=echarts.esm.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/echarts/echarts.js",
    "content": "\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n    typeof define === 'function' && define.amd ? define(['exports'], factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.echarts = {}));\n}(this, (function (exports) { 'use strict';\n\n    /*! *****************************************************************************\n    Copyright (c) Microsoft Corporation.\n\n    Permission to use, copy, modify, and/or distribute this software for any\n    purpose with or without fee is hereby granted.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\n    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n    PERFORMANCE OF THIS SOFTWARE.\n    ***************************************************************************** */\n    /* global Reflect, Promise */\n\n    var extendStatics = function(d, b) {\n        extendStatics = Object.setPrototypeOf ||\n            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n        return extendStatics(d, b);\n    };\n\n    function __extends(d, b) {\n        if (typeof b !== \"function\" && b !== null)\n            throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    }\n\n    var Browser = (function () {\n        function Browser() {\n            this.firefox = false;\n            this.ie = false;\n            this.edge = false;\n            this.newEdge = false;\n            this.weChat = false;\n        }\n        return Browser;\n    }());\n    var Env = (function () {\n        function Env() {\n            this.browser = new Browser();\n            this.node = false;\n            this.wxa = false;\n            this.worker = false;\n            this.svgSupported = false;\n            this.touchEventsSupported = false;\n            this.pointerEventsSupported = false;\n            this.domSupported = false;\n            this.transformSupported = false;\n            this.transform3dSupported = false;\n            this.hasGlobalWindow = typeof window !== 'undefined';\n        }\n        return Env;\n    }());\n    var env = new Env();\n    if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {\n        env.wxa = true;\n        env.touchEventsSupported = true;\n    }\n    else if (typeof document === 'undefined' && typeof self !== 'undefined') {\n        env.worker = true;\n    }\n    else if (typeof navigator === 'undefined') {\n        env.node = true;\n        env.svgSupported = true;\n    }\n    else {\n        detect(navigator.userAgent, env);\n    }\n    function detect(ua, env) {\n        var browser = env.browser;\n        var firefox = ua.match(/Firefox\\/([\\d.]+)/);\n        var ie = ua.match(/MSIE\\s([\\d.]+)/)\n            || ua.match(/Trident\\/.+?rv:(([\\d.]+))/);\n        var edge = ua.match(/Edge?\\/([\\d.]+)/);\n        var weChat = (/micromessenger/i).test(ua);\n        if (firefox) {\n            browser.firefox = true;\n            browser.version = firefox[1];\n        }\n        if (ie) {\n            browser.ie = true;\n            browser.version = ie[1];\n        }\n        if (edge) {\n            browser.edge = true;\n            browser.version = edge[1];\n            browser.newEdge = +edge[1].split('.')[0] > 18;\n        }\n        if (weChat) {\n            browser.weChat = true;\n        }\n        env.svgSupported = typeof SVGRect !== 'undefined';\n        env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge;\n        env.pointerEventsSupported = 'onpointerdown' in window\n            && (browser.edge || (browser.ie && +browser.version >= 11));\n        env.domSupported = typeof document !== 'undefined';\n        var style = document.documentElement.style;\n        env.transform3dSupported = ((browser.ie && 'transition' in style)\n            || browser.edge\n            || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix()))\n            || 'MozPerspective' in style)\n            && !('OTransition' in style);\n        env.transformSupported = env.transform3dSupported\n            || (browser.ie && +browser.version >= 9);\n    }\n\n    var DEFAULT_FONT_SIZE = 12;\n    var DEFAULT_FONT_FAMILY = 'sans-serif';\n    var DEFAULT_FONT = DEFAULT_FONT_SIZE + \"px \" + DEFAULT_FONT_FAMILY;\n    var OFFSET = 20;\n    var SCALE = 100;\n    var defaultWidthMapStr = \"007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\\\\\WQb\\\\0FWLg\\\\bWb\\\\WQ\\\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\\\FFF5.5N\";\n    function getTextWidthMap(mapStr) {\n        var map = {};\n        if (typeof JSON === 'undefined') {\n            return map;\n        }\n        for (var i = 0; i < mapStr.length; i++) {\n            var char = String.fromCharCode(i + 32);\n            var size = (mapStr.charCodeAt(i) - OFFSET) / SCALE;\n            map[char] = size;\n        }\n        return map;\n    }\n    var DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr);\n    var platformApi = {\n        createCanvas: function () {\n            return typeof document !== 'undefined'\n                && document.createElement('canvas');\n        },\n        measureText: (function () {\n            var _ctx;\n            var _cachedFont;\n            return function (text, font) {\n                if (!_ctx) {\n                    var canvas = platformApi.createCanvas();\n                    _ctx = canvas && canvas.getContext('2d');\n                }\n                if (_ctx) {\n                    if (_cachedFont !== font) {\n                        _cachedFont = _ctx.font = font || DEFAULT_FONT;\n                    }\n                    return _ctx.measureText(text);\n                }\n                else {\n                    text = text || '';\n                    font = font || DEFAULT_FONT;\n                    var res = /(\\d+)px/.exec(font);\n                    var fontSize = res && +res[1] || DEFAULT_FONT_SIZE;\n                    var width = 0;\n                    if (font.indexOf('mono') >= 0) {\n                        width = fontSize * text.length;\n                    }\n                    else {\n                        for (var i = 0; i < text.length; i++) {\n                            var preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]];\n                            width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize);\n                        }\n                    }\n                    return { width: width };\n                }\n            };\n        })(),\n        loadImage: function (src, onload, onerror) {\n            var image = new Image();\n            image.onload = onload;\n            image.onerror = onerror;\n            image.src = src;\n            return image;\n        }\n    };\n    function setPlatformAPI(newPlatformApis) {\n        for (var key in platformApi) {\n            if (newPlatformApis[key]) {\n                platformApi[key] = newPlatformApis[key];\n            }\n        }\n    }\n\n    var BUILTIN_OBJECT = reduce([\n        'Function',\n        'RegExp',\n        'Date',\n        'Error',\n        'CanvasGradient',\n        'CanvasPattern',\n        'Image',\n        'Canvas'\n    ], function (obj, val) {\n        obj['[object ' + val + ']'] = true;\n        return obj;\n    }, {});\n    var TYPED_ARRAY = reduce([\n        'Int8',\n        'Uint8',\n        'Uint8Clamped',\n        'Int16',\n        'Uint16',\n        'Int32',\n        'Uint32',\n        'Float32',\n        'Float64'\n    ], function (obj, val) {\n        obj['[object ' + val + 'Array]'] = true;\n        return obj;\n    }, {});\n    var objToString = Object.prototype.toString;\n    var arrayProto = Array.prototype;\n    var nativeForEach = arrayProto.forEach;\n    var nativeFilter = arrayProto.filter;\n    var nativeSlice = arrayProto.slice;\n    var nativeMap = arrayProto.map;\n    var ctorFunction = function () { }.constructor;\n    var protoFunction = ctorFunction ? ctorFunction.prototype : null;\n    var protoKey = '__proto__';\n    var idStart = 0x0907;\n    function guid() {\n        return idStart++;\n    }\n    function logError() {\n        var args = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            args[_i] = arguments[_i];\n        }\n        if (typeof console !== 'undefined') {\n            console.error.apply(console, args);\n        }\n    }\n    function clone(source) {\n        if (source == null || typeof source !== 'object') {\n            return source;\n        }\n        var result = source;\n        var typeStr = objToString.call(source);\n        if (typeStr === '[object Array]') {\n            if (!isPrimitive(source)) {\n                result = [];\n                for (var i = 0, len = source.length; i < len; i++) {\n                    result[i] = clone(source[i]);\n                }\n            }\n        }\n        else if (TYPED_ARRAY[typeStr]) {\n            if (!isPrimitive(source)) {\n                var Ctor = source.constructor;\n                if (Ctor.from) {\n                    result = Ctor.from(source);\n                }\n                else {\n                    result = new Ctor(source.length);\n                    for (var i = 0, len = source.length; i < len; i++) {\n                        result[i] = source[i];\n                    }\n                }\n            }\n        }\n        else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {\n            result = {};\n            for (var key in source) {\n                if (source.hasOwnProperty(key) && key !== protoKey) {\n                    result[key] = clone(source[key]);\n                }\n            }\n        }\n        return result;\n    }\n    function merge(target, source, overwrite) {\n        if (!isObject(source) || !isObject(target)) {\n            return overwrite ? clone(source) : target;\n        }\n        for (var key in source) {\n            if (source.hasOwnProperty(key) && key !== protoKey) {\n                var targetProp = target[key];\n                var sourceProp = source[key];\n                if (isObject(sourceProp)\n                    && isObject(targetProp)\n                    && !isArray(sourceProp)\n                    && !isArray(targetProp)\n                    && !isDom(sourceProp)\n                    && !isDom(targetProp)\n                    && !isBuiltInObject(sourceProp)\n                    && !isBuiltInObject(targetProp)\n                    && !isPrimitive(sourceProp)\n                    && !isPrimitive(targetProp)) {\n                    merge(targetProp, sourceProp, overwrite);\n                }\n                else if (overwrite || !(key in target)) {\n                    target[key] = clone(source[key]);\n                }\n            }\n        }\n        return target;\n    }\n    function mergeAll(targetAndSources, overwrite) {\n        var result = targetAndSources[0];\n        for (var i = 1, len = targetAndSources.length; i < len; i++) {\n            result = merge(result, targetAndSources[i], overwrite);\n        }\n        return result;\n    }\n    function extend(target, source) {\n        if (Object.assign) {\n            Object.assign(target, source);\n        }\n        else {\n            for (var key in source) {\n                if (source.hasOwnProperty(key) && key !== protoKey) {\n                    target[key] = source[key];\n                }\n            }\n        }\n        return target;\n    }\n    function defaults(target, source, overlay) {\n        var keysArr = keys(source);\n        for (var i = 0; i < keysArr.length; i++) {\n            var key = keysArr[i];\n            if ((overlay ? source[key] != null : target[key] == null)) {\n                target[key] = source[key];\n            }\n        }\n        return target;\n    }\n    var createCanvas = platformApi.createCanvas;\n    function indexOf(array, value) {\n        if (array) {\n            if (array.indexOf) {\n                return array.indexOf(value);\n            }\n            for (var i = 0, len = array.length; i < len; i++) {\n                if (array[i] === value) {\n                    return i;\n                }\n            }\n        }\n        return -1;\n    }\n    function inherits(clazz, baseClazz) {\n        var clazzPrototype = clazz.prototype;\n        function F() { }\n        F.prototype = baseClazz.prototype;\n        clazz.prototype = new F();\n        for (var prop in clazzPrototype) {\n            if (clazzPrototype.hasOwnProperty(prop)) {\n                clazz.prototype[prop] = clazzPrototype[prop];\n            }\n        }\n        clazz.prototype.constructor = clazz;\n        clazz.superClass = baseClazz;\n    }\n    function mixin(target, source, override) {\n        target = 'prototype' in target ? target.prototype : target;\n        source = 'prototype' in source ? source.prototype : source;\n        if (Object.getOwnPropertyNames) {\n            var keyList = Object.getOwnPropertyNames(source);\n            for (var i = 0; i < keyList.length; i++) {\n                var key = keyList[i];\n                if (key !== 'constructor') {\n                    if ((override ? source[key] != null : target[key] == null)) {\n                        target[key] = source[key];\n                    }\n                }\n            }\n        }\n        else {\n            defaults(target, source, override);\n        }\n    }\n    function isArrayLike(data) {\n        if (!data) {\n            return false;\n        }\n        if (typeof data === 'string') {\n            return false;\n        }\n        return typeof data.length === 'number';\n    }\n    function each(arr, cb, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        if (arr.forEach && arr.forEach === nativeForEach) {\n            arr.forEach(cb, context);\n        }\n        else if (arr.length === +arr.length) {\n            for (var i = 0, len = arr.length; i < len; i++) {\n                cb.call(context, arr[i], i, arr);\n            }\n        }\n        else {\n            for (var key in arr) {\n                if (arr.hasOwnProperty(key)) {\n                    cb.call(context, arr[key], key, arr);\n                }\n            }\n        }\n    }\n    function map(arr, cb, context) {\n        if (!arr) {\n            return [];\n        }\n        if (!cb) {\n            return slice(arr);\n        }\n        if (arr.map && arr.map === nativeMap) {\n            return arr.map(cb, context);\n        }\n        else {\n            var result = [];\n            for (var i = 0, len = arr.length; i < len; i++) {\n                result.push(cb.call(context, arr[i], i, arr));\n            }\n            return result;\n        }\n    }\n    function reduce(arr, cb, memo, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        for (var i = 0, len = arr.length; i < len; i++) {\n            memo = cb.call(context, memo, arr[i], i, arr);\n        }\n        return memo;\n    }\n    function filter(arr, cb, context) {\n        if (!arr) {\n            return [];\n        }\n        if (!cb) {\n            return slice(arr);\n        }\n        if (arr.filter && arr.filter === nativeFilter) {\n            return arr.filter(cb, context);\n        }\n        else {\n            var result = [];\n            for (var i = 0, len = arr.length; i < len; i++) {\n                if (cb.call(context, arr[i], i, arr)) {\n                    result.push(arr[i]);\n                }\n            }\n            return result;\n        }\n    }\n    function find(arr, cb, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        for (var i = 0, len = arr.length; i < len; i++) {\n            if (cb.call(context, arr[i], i, arr)) {\n                return arr[i];\n            }\n        }\n    }\n    function keys(obj) {\n        if (!obj) {\n            return [];\n        }\n        if (Object.keys) {\n            return Object.keys(obj);\n        }\n        var keyList = [];\n        for (var key in obj) {\n            if (obj.hasOwnProperty(key)) {\n                keyList.push(key);\n            }\n        }\n        return keyList;\n    }\n    function bindPolyfill(func, context) {\n        var args = [];\n        for (var _i = 2; _i < arguments.length; _i++) {\n            args[_i - 2] = arguments[_i];\n        }\n        return function () {\n            return func.apply(context, args.concat(nativeSlice.call(arguments)));\n        };\n    }\n    var bind = (protoFunction && isFunction(protoFunction.bind))\n        ? protoFunction.call.bind(protoFunction.bind)\n        : bindPolyfill;\n    function curry(func) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        return function () {\n            return func.apply(this, args.concat(nativeSlice.call(arguments)));\n        };\n    }\n    function isArray(value) {\n        if (Array.isArray) {\n            return Array.isArray(value);\n        }\n        return objToString.call(value) === '[object Array]';\n    }\n    function isFunction(value) {\n        return typeof value === 'function';\n    }\n    function isString(value) {\n        return typeof value === 'string';\n    }\n    function isStringSafe(value) {\n        return objToString.call(value) === '[object String]';\n    }\n    function isNumber(value) {\n        return typeof value === 'number';\n    }\n    function isObject(value) {\n        var type = typeof value;\n        return type === 'function' || (!!value && type === 'object');\n    }\n    function isBuiltInObject(value) {\n        return !!BUILTIN_OBJECT[objToString.call(value)];\n    }\n    function isTypedArray(value) {\n        return !!TYPED_ARRAY[objToString.call(value)];\n    }\n    function isDom(value) {\n        return typeof value === 'object'\n            && typeof value.nodeType === 'number'\n            && typeof value.ownerDocument === 'object';\n    }\n    function isGradientObject(value) {\n        return value.colorStops != null;\n    }\n    function isImagePatternObject(value) {\n        return value.image != null;\n    }\n    function isRegExp(value) {\n        return objToString.call(value) === '[object RegExp]';\n    }\n    function eqNaN(value) {\n        return value !== value;\n    }\n    function retrieve() {\n        var args = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            args[_i] = arguments[_i];\n        }\n        for (var i = 0, len = args.length; i < len; i++) {\n            if (args[i] != null) {\n                return args[i];\n            }\n        }\n    }\n    function retrieve2(value0, value1) {\n        return value0 != null\n            ? value0\n            : value1;\n    }\n    function retrieve3(value0, value1, value2) {\n        return value0 != null\n            ? value0\n            : value1 != null\n                ? value1\n                : value2;\n    }\n    function slice(arr) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        return nativeSlice.apply(arr, args);\n    }\n    function normalizeCssArray(val) {\n        if (typeof (val) === 'number') {\n            return [val, val, val, val];\n        }\n        var len = val.length;\n        if (len === 2) {\n            return [val[0], val[1], val[0], val[1]];\n        }\n        else if (len === 3) {\n            return [val[0], val[1], val[2], val[1]];\n        }\n        return val;\n    }\n    function assert(condition, message) {\n        if (!condition) {\n            throw new Error(message);\n        }\n    }\n    function trim(str) {\n        if (str == null) {\n            return null;\n        }\n        else if (typeof str.trim === 'function') {\n            return str.trim();\n        }\n        else {\n            return str.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n        }\n    }\n    var primitiveKey = '__ec_primitive__';\n    function setAsPrimitive(obj) {\n        obj[primitiveKey] = true;\n    }\n    function isPrimitive(obj) {\n        return obj[primitiveKey];\n    }\n    var MapPolyfill = (function () {\n        function MapPolyfill() {\n            this.data = {};\n        }\n        MapPolyfill.prototype[\"delete\"] = function (key) {\n            var existed = this.has(key);\n            if (existed) {\n                delete this.data[key];\n            }\n            return existed;\n        };\n        MapPolyfill.prototype.has = function (key) {\n            return this.data.hasOwnProperty(key);\n        };\n        MapPolyfill.prototype.get = function (key) {\n            return this.data[key];\n        };\n        MapPolyfill.prototype.set = function (key, value) {\n            this.data[key] = value;\n            return this;\n        };\n        MapPolyfill.prototype.keys = function () {\n            return keys(this.data);\n        };\n        MapPolyfill.prototype.forEach = function (callback) {\n            var data = this.data;\n            for (var key in data) {\n                if (data.hasOwnProperty(key)) {\n                    callback(data[key], key);\n                }\n            }\n        };\n        return MapPolyfill;\n    }());\n    var isNativeMapSupported = typeof Map === 'function';\n    function maybeNativeMap() {\n        return (isNativeMapSupported ? new Map() : new MapPolyfill());\n    }\n    var HashMap = (function () {\n        function HashMap(obj) {\n            var isArr = isArray(obj);\n            this.data = maybeNativeMap();\n            var thisMap = this;\n            (obj instanceof HashMap)\n                ? obj.each(visit)\n                : (obj && each(obj, visit));\n            function visit(value, key) {\n                isArr ? thisMap.set(value, key) : thisMap.set(key, value);\n            }\n        }\n        HashMap.prototype.hasKey = function (key) {\n            return this.data.has(key);\n        };\n        HashMap.prototype.get = function (key) {\n            return this.data.get(key);\n        };\n        HashMap.prototype.set = function (key, value) {\n            this.data.set(key, value);\n            return value;\n        };\n        HashMap.prototype.each = function (cb, context) {\n            this.data.forEach(function (value, key) {\n                cb.call(context, value, key);\n            });\n        };\n        HashMap.prototype.keys = function () {\n            var keys = this.data.keys();\n            return isNativeMapSupported\n                ? Array.from(keys)\n                : keys;\n        };\n        HashMap.prototype.removeKey = function (key) {\n            this.data[\"delete\"](key);\n        };\n        return HashMap;\n    }());\n    function createHashMap(obj) {\n        return new HashMap(obj);\n    }\n    function concatArray(a, b) {\n        var newArray = new a.constructor(a.length + b.length);\n        for (var i = 0; i < a.length; i++) {\n            newArray[i] = a[i];\n        }\n        var offset = a.length;\n        for (var i = 0; i < b.length; i++) {\n            newArray[i + offset] = b[i];\n        }\n        return newArray;\n    }\n    function createObject(proto, properties) {\n        var obj;\n        if (Object.create) {\n            obj = Object.create(proto);\n        }\n        else {\n            var StyleCtor = function () { };\n            StyleCtor.prototype = proto;\n            obj = new StyleCtor();\n        }\n        if (properties) {\n            extend(obj, properties);\n        }\n        return obj;\n    }\n    function disableUserSelect(dom) {\n        var domStyle = dom.style;\n        domStyle.webkitUserSelect = 'none';\n        domStyle.userSelect = 'none';\n        domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)';\n        domStyle['-webkit-touch-callout'] = 'none';\n    }\n    function hasOwn(own, prop) {\n        return own.hasOwnProperty(prop);\n    }\n    function noop() { }\n    var RADIAN_TO_DEGREE = 180 / Math.PI;\n\n    var util = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        guid: guid,\n        logError: logError,\n        clone: clone,\n        merge: merge,\n        mergeAll: mergeAll,\n        extend: extend,\n        defaults: defaults,\n        createCanvas: createCanvas,\n        indexOf: indexOf,\n        inherits: inherits,\n        mixin: mixin,\n        isArrayLike: isArrayLike,\n        each: each,\n        map: map,\n        reduce: reduce,\n        filter: filter,\n        find: find,\n        keys: keys,\n        bind: bind,\n        curry: curry,\n        isArray: isArray,\n        isFunction: isFunction,\n        isString: isString,\n        isStringSafe: isStringSafe,\n        isNumber: isNumber,\n        isObject: isObject,\n        isBuiltInObject: isBuiltInObject,\n        isTypedArray: isTypedArray,\n        isDom: isDom,\n        isGradientObject: isGradientObject,\n        isImagePatternObject: isImagePatternObject,\n        isRegExp: isRegExp,\n        eqNaN: eqNaN,\n        retrieve: retrieve,\n        retrieve2: retrieve2,\n        retrieve3: retrieve3,\n        slice: slice,\n        normalizeCssArray: normalizeCssArray,\n        assert: assert,\n        trim: trim,\n        setAsPrimitive: setAsPrimitive,\n        isPrimitive: isPrimitive,\n        HashMap: HashMap,\n        createHashMap: createHashMap,\n        concatArray: concatArray,\n        createObject: createObject,\n        disableUserSelect: disableUserSelect,\n        hasOwn: hasOwn,\n        noop: noop,\n        RADIAN_TO_DEGREE: RADIAN_TO_DEGREE\n    });\n\n    function create(x, y) {\n        if (x == null) {\n            x = 0;\n        }\n        if (y == null) {\n            y = 0;\n        }\n        return [x, y];\n    }\n    function copy(out, v) {\n        out[0] = v[0];\n        out[1] = v[1];\n        return out;\n    }\n    function clone$1(v) {\n        return [v[0], v[1]];\n    }\n    function set(out, a, b) {\n        out[0] = a;\n        out[1] = b;\n        return out;\n    }\n    function add(out, v1, v2) {\n        out[0] = v1[0] + v2[0];\n        out[1] = v1[1] + v2[1];\n        return out;\n    }\n    function scaleAndAdd(out, v1, v2, a) {\n        out[0] = v1[0] + v2[0] * a;\n        out[1] = v1[1] + v2[1] * a;\n        return out;\n    }\n    function sub(out, v1, v2) {\n        out[0] = v1[0] - v2[0];\n        out[1] = v1[1] - v2[1];\n        return out;\n    }\n    function len(v) {\n        return Math.sqrt(lenSquare(v));\n    }\n    var length = len;\n    function lenSquare(v) {\n        return v[0] * v[0] + v[1] * v[1];\n    }\n    var lengthSquare = lenSquare;\n    function mul(out, v1, v2) {\n        out[0] = v1[0] * v2[0];\n        out[1] = v1[1] * v2[1];\n        return out;\n    }\n    function div(out, v1, v2) {\n        out[0] = v1[0] / v2[0];\n        out[1] = v1[1] / v2[1];\n        return out;\n    }\n    function dot(v1, v2) {\n        return v1[0] * v2[0] + v1[1] * v2[1];\n    }\n    function scale(out, v, s) {\n        out[0] = v[0] * s;\n        out[1] = v[1] * s;\n        return out;\n    }\n    function normalize(out, v) {\n        var d = len(v);\n        if (d === 0) {\n            out[0] = 0;\n            out[1] = 0;\n        }\n        else {\n            out[0] = v[0] / d;\n            out[1] = v[1] / d;\n        }\n        return out;\n    }\n    function distance(v1, v2) {\n        return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0])\n            + (v1[1] - v2[1]) * (v1[1] - v2[1]));\n    }\n    var dist = distance;\n    function distanceSquare(v1, v2) {\n        return (v1[0] - v2[0]) * (v1[0] - v2[0])\n            + (v1[1] - v2[1]) * (v1[1] - v2[1]);\n    }\n    var distSquare = distanceSquare;\n    function negate(out, v) {\n        out[0] = -v[0];\n        out[1] = -v[1];\n        return out;\n    }\n    function lerp(out, v1, v2, t) {\n        out[0] = v1[0] + t * (v2[0] - v1[0]);\n        out[1] = v1[1] + t * (v2[1] - v1[1]);\n        return out;\n    }\n    function applyTransform(out, v, m) {\n        var x = v[0];\n        var y = v[1];\n        out[0] = m[0] * x + m[2] * y + m[4];\n        out[1] = m[1] * x + m[3] * y + m[5];\n        return out;\n    }\n    function min(out, v1, v2) {\n        out[0] = Math.min(v1[0], v2[0]);\n        out[1] = Math.min(v1[1], v2[1]);\n        return out;\n    }\n    function max(out, v1, v2) {\n        out[0] = Math.max(v1[0], v2[0]);\n        out[1] = Math.max(v1[1], v2[1]);\n        return out;\n    }\n\n    var vector = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        create: create,\n        copy: copy,\n        clone: clone$1,\n        set: set,\n        add: add,\n        scaleAndAdd: scaleAndAdd,\n        sub: sub,\n        len: len,\n        length: length,\n        lenSquare: lenSquare,\n        lengthSquare: lengthSquare,\n        mul: mul,\n        div: div,\n        dot: dot,\n        scale: scale,\n        normalize: normalize,\n        distance: distance,\n        dist: dist,\n        distanceSquare: distanceSquare,\n        distSquare: distSquare,\n        negate: negate,\n        lerp: lerp,\n        applyTransform: applyTransform,\n        min: min,\n        max: max\n    });\n\n    var Param = (function () {\n        function Param(target, e) {\n            this.target = target;\n            this.topTarget = e && e.topTarget;\n        }\n        return Param;\n    }());\n    var Draggable = (function () {\n        function Draggable(handler) {\n            this.handler = handler;\n            handler.on('mousedown', this._dragStart, this);\n            handler.on('mousemove', this._drag, this);\n            handler.on('mouseup', this._dragEnd, this);\n        }\n        Draggable.prototype._dragStart = function (e) {\n            var draggingTarget = e.target;\n            while (draggingTarget && !draggingTarget.draggable) {\n                draggingTarget = draggingTarget.parent || draggingTarget.__hostTarget;\n            }\n            if (draggingTarget) {\n                this._draggingTarget = draggingTarget;\n                draggingTarget.dragging = true;\n                this._x = e.offsetX;\n                this._y = e.offsetY;\n                this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragstart', e.event);\n            }\n        };\n        Draggable.prototype._drag = function (e) {\n            var draggingTarget = this._draggingTarget;\n            if (draggingTarget) {\n                var x = e.offsetX;\n                var y = e.offsetY;\n                var dx = x - this._x;\n                var dy = y - this._y;\n                this._x = x;\n                this._y = y;\n                draggingTarget.drift(dx, dy, e);\n                this.handler.dispatchToElement(new Param(draggingTarget, e), 'drag', e.event);\n                var dropTarget = this.handler.findHover(x, y, draggingTarget).target;\n                var lastDropTarget = this._dropTarget;\n                this._dropTarget = dropTarget;\n                if (draggingTarget !== dropTarget) {\n                    if (lastDropTarget && dropTarget !== lastDropTarget) {\n                        this.handler.dispatchToElement(new Param(lastDropTarget, e), 'dragleave', e.event);\n                    }\n                    if (dropTarget && dropTarget !== lastDropTarget) {\n                        this.handler.dispatchToElement(new Param(dropTarget, e), 'dragenter', e.event);\n                    }\n                }\n            }\n        };\n        Draggable.prototype._dragEnd = function (e) {\n            var draggingTarget = this._draggingTarget;\n            if (draggingTarget) {\n                draggingTarget.dragging = false;\n            }\n            this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragend', e.event);\n            if (this._dropTarget) {\n                this.handler.dispatchToElement(new Param(this._dropTarget, e), 'drop', e.event);\n            }\n            this._draggingTarget = null;\n            this._dropTarget = null;\n        };\n        return Draggable;\n    }());\n\n    var Eventful = (function () {\n        function Eventful(eventProcessors) {\n            if (eventProcessors) {\n                this._$eventProcessor = eventProcessors;\n            }\n        }\n        Eventful.prototype.on = function (event, query, handler, context) {\n            if (!this._$handlers) {\n                this._$handlers = {};\n            }\n            var _h = this._$handlers;\n            if (typeof query === 'function') {\n                context = handler;\n                handler = query;\n                query = null;\n            }\n            if (!handler || !event) {\n                return this;\n            }\n            var eventProcessor = this._$eventProcessor;\n            if (query != null && eventProcessor && eventProcessor.normalizeQuery) {\n                query = eventProcessor.normalizeQuery(query);\n            }\n            if (!_h[event]) {\n                _h[event] = [];\n            }\n            for (var i = 0; i < _h[event].length; i++) {\n                if (_h[event][i].h === handler) {\n                    return this;\n                }\n            }\n            var wrap = {\n                h: handler,\n                query: query,\n                ctx: (context || this),\n                callAtLast: handler.zrEventfulCallAtLast\n            };\n            var lastIndex = _h[event].length - 1;\n            var lastWrap = _h[event][lastIndex];\n            (lastWrap && lastWrap.callAtLast)\n                ? _h[event].splice(lastIndex, 0, wrap)\n                : _h[event].push(wrap);\n            return this;\n        };\n        Eventful.prototype.isSilent = function (eventName) {\n            var _h = this._$handlers;\n            return !_h || !_h[eventName] || !_h[eventName].length;\n        };\n        Eventful.prototype.off = function (eventType, handler) {\n            var _h = this._$handlers;\n            if (!_h) {\n                return this;\n            }\n            if (!eventType) {\n                this._$handlers = {};\n                return this;\n            }\n            if (handler) {\n                if (_h[eventType]) {\n                    var newList = [];\n                    for (var i = 0, l = _h[eventType].length; i < l; i++) {\n                        if (_h[eventType][i].h !== handler) {\n                            newList.push(_h[eventType][i]);\n                        }\n                    }\n                    _h[eventType] = newList;\n                }\n                if (_h[eventType] && _h[eventType].length === 0) {\n                    delete _h[eventType];\n                }\n            }\n            else {\n                delete _h[eventType];\n            }\n            return this;\n        };\n        Eventful.prototype.trigger = function (eventType) {\n            var args = [];\n            for (var _i = 1; _i < arguments.length; _i++) {\n                args[_i - 1] = arguments[_i];\n            }\n            if (!this._$handlers) {\n                return this;\n            }\n            var _h = this._$handlers[eventType];\n            var eventProcessor = this._$eventProcessor;\n            if (_h) {\n                var argLen = args.length;\n                var len = _h.length;\n                for (var i = 0; i < len; i++) {\n                    var hItem = _h[i];\n                    if (eventProcessor\n                        && eventProcessor.filter\n                        && hItem.query != null\n                        && !eventProcessor.filter(eventType, hItem.query)) {\n                        continue;\n                    }\n                    switch (argLen) {\n                        case 0:\n                            hItem.h.call(hItem.ctx);\n                            break;\n                        case 1:\n                            hItem.h.call(hItem.ctx, args[0]);\n                            break;\n                        case 2:\n                            hItem.h.call(hItem.ctx, args[0], args[1]);\n                            break;\n                        default:\n                            hItem.h.apply(hItem.ctx, args);\n                            break;\n                    }\n                }\n            }\n            eventProcessor && eventProcessor.afterTrigger\n                && eventProcessor.afterTrigger(eventType);\n            return this;\n        };\n        Eventful.prototype.triggerWithContext = function (type) {\n            var args = [];\n            for (var _i = 1; _i < arguments.length; _i++) {\n                args[_i - 1] = arguments[_i];\n            }\n            if (!this._$handlers) {\n                return this;\n            }\n            var _h = this._$handlers[type];\n            var eventProcessor = this._$eventProcessor;\n            if (_h) {\n                var argLen = args.length;\n                var ctx = args[argLen - 1];\n                var len = _h.length;\n                for (var i = 0; i < len; i++) {\n                    var hItem = _h[i];\n                    if (eventProcessor\n                        && eventProcessor.filter\n                        && hItem.query != null\n                        && !eventProcessor.filter(type, hItem.query)) {\n                        continue;\n                    }\n                    switch (argLen) {\n                        case 0:\n                            hItem.h.call(ctx);\n                            break;\n                        case 1:\n                            hItem.h.call(ctx, args[0]);\n                            break;\n                        case 2:\n                            hItem.h.call(ctx, args[0], args[1]);\n                            break;\n                        default:\n                            hItem.h.apply(ctx, args.slice(1, argLen - 1));\n                            break;\n                    }\n                }\n            }\n            eventProcessor && eventProcessor.afterTrigger\n                && eventProcessor.afterTrigger(type);\n            return this;\n        };\n        return Eventful;\n    }());\n\n    var LN2 = Math.log(2);\n    function determinant(rows, rank, rowStart, rowMask, colMask, detCache) {\n        var cacheKey = rowMask + '-' + colMask;\n        var fullRank = rows.length;\n        if (detCache.hasOwnProperty(cacheKey)) {\n            return detCache[cacheKey];\n        }\n        if (rank === 1) {\n            var colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2);\n            return rows[rowStart][colStart];\n        }\n        var subRowMask = rowMask | (1 << rowStart);\n        var subRowStart = rowStart + 1;\n        while (rowMask & (1 << subRowStart)) {\n            subRowStart++;\n        }\n        var sum = 0;\n        for (var j = 0, colLocalIdx = 0; j < fullRank; j++) {\n            var colTag = 1 << j;\n            if (!(colTag & colMask)) {\n                sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j]\n                    * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache);\n                colLocalIdx++;\n            }\n        }\n        detCache[cacheKey] = sum;\n        return sum;\n    }\n    function buildTransformer(src, dest) {\n        var mA = [\n            [src[0], src[1], 1, 0, 0, 0, -dest[0] * src[0], -dest[0] * src[1]],\n            [0, 0, 0, src[0], src[1], 1, -dest[1] * src[0], -dest[1] * src[1]],\n            [src[2], src[3], 1, 0, 0, 0, -dest[2] * src[2], -dest[2] * src[3]],\n            [0, 0, 0, src[2], src[3], 1, -dest[3] * src[2], -dest[3] * src[3]],\n            [src[4], src[5], 1, 0, 0, 0, -dest[4] * src[4], -dest[4] * src[5]],\n            [0, 0, 0, src[4], src[5], 1, -dest[5] * src[4], -dest[5] * src[5]],\n            [src[6], src[7], 1, 0, 0, 0, -dest[6] * src[6], -dest[6] * src[7]],\n            [0, 0, 0, src[6], src[7], 1, -dest[7] * src[6], -dest[7] * src[7]]\n        ];\n        var detCache = {};\n        var det = determinant(mA, 8, 0, 0, 0, detCache);\n        if (det === 0) {\n            return;\n        }\n        var vh = [];\n        for (var i = 0; i < 8; i++) {\n            for (var j = 0; j < 8; j++) {\n                vh[j] == null && (vh[j] = 0);\n                vh[j] += ((i + j) % 2 ? -1 : 1)\n                    * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache)\n                    / det * dest[i];\n            }\n        }\n        return function (out, srcPointX, srcPointY) {\n            var pk = srcPointX * vh[6] + srcPointY * vh[7] + 1;\n            out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk;\n            out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk;\n        };\n    }\n\n    var EVENT_SAVED_PROP = '___zrEVENTSAVED';\n    var _calcOut = [];\n    function transformLocalCoord(out, elFrom, elTarget, inX, inY) {\n        return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true)\n            && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]);\n    }\n    function transformCoordWithViewport(out, el, inX, inY, inverse) {\n        if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) {\n            var saved = el[EVENT_SAVED_PROP] || (el[EVENT_SAVED_PROP] = {});\n            var markers = prepareCoordMarkers(el, saved);\n            var transformer = preparePointerTransformer(markers, saved, inverse);\n            if (transformer) {\n                transformer(out, inX, inY);\n                return true;\n            }\n        }\n        return false;\n    }\n    function prepareCoordMarkers(el, saved) {\n        var markers = saved.markers;\n        if (markers) {\n            return markers;\n        }\n        markers = saved.markers = [];\n        var propLR = ['left', 'right'];\n        var propTB = ['top', 'bottom'];\n        for (var i = 0; i < 4; i++) {\n            var marker = document.createElement('div');\n            var stl = marker.style;\n            var idxLR = i % 2;\n            var idxTB = (i >> 1) % 2;\n            stl.cssText = [\n                'position: absolute',\n                'visibility: hidden',\n                'padding: 0',\n                'margin: 0',\n                'border-width: 0',\n                'user-select: none',\n                'width:0',\n                'height:0',\n                propLR[idxLR] + ':0',\n                propTB[idxTB] + ':0',\n                propLR[1 - idxLR] + ':auto',\n                propTB[1 - idxTB] + ':auto',\n                ''\n            ].join('!important;');\n            el.appendChild(marker);\n            markers.push(marker);\n        }\n        return markers;\n    }\n    function preparePointerTransformer(markers, saved, inverse) {\n        var transformerName = inverse ? 'invTrans' : 'trans';\n        var transformer = saved[transformerName];\n        var oldSrcCoords = saved.srcCoords;\n        var srcCoords = [];\n        var destCoords = [];\n        var oldCoordTheSame = true;\n        for (var i = 0; i < 4; i++) {\n            var rect = markers[i].getBoundingClientRect();\n            var ii = 2 * i;\n            var x = rect.left;\n            var y = rect.top;\n            srcCoords.push(x, y);\n            oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1];\n            destCoords.push(markers[i].offsetLeft, markers[i].offsetTop);\n        }\n        return (oldCoordTheSame && transformer)\n            ? transformer\n            : (saved.srcCoords = srcCoords,\n                saved[transformerName] = inverse\n                    ? buildTransformer(destCoords, srcCoords)\n                    : buildTransformer(srcCoords, destCoords));\n    }\n    function isCanvasEl(el) {\n        return el.nodeName.toUpperCase() === 'CANVAS';\n    }\n    var replaceReg = /([&<>\"'])/g;\n    var replaceMap = {\n        '&': '&amp;',\n        '<': '&lt;',\n        '>': '&gt;',\n        '\"': '&quot;',\n        '\\'': '&#39;'\n    };\n    function encodeHTML(source) {\n        return source == null\n            ? ''\n            : (source + '').replace(replaceReg, function (str, c) {\n                return replaceMap[c];\n            });\n    }\n\n    var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;\n    var _calcOut$1 = [];\n    var firefoxNotSupportOffsetXY = env.browser.firefox\n        && +env.browser.version.split('.')[0] < 39;\n    function clientToLocal(el, e, out, calculate) {\n        out = out || {};\n        if (calculate) {\n            calculateZrXY(el, e, out);\n        }\n        else if (firefoxNotSupportOffsetXY\n            && e.layerX != null\n            && e.layerX !== e.offsetX) {\n            out.zrX = e.layerX;\n            out.zrY = e.layerY;\n        }\n        else if (e.offsetX != null) {\n            out.zrX = e.offsetX;\n            out.zrY = e.offsetY;\n        }\n        else {\n            calculateZrXY(el, e, out);\n        }\n        return out;\n    }\n    function calculateZrXY(el, e, out) {\n        if (env.domSupported && el.getBoundingClientRect) {\n            var ex = e.clientX;\n            var ey = e.clientY;\n            if (isCanvasEl(el)) {\n                var box = el.getBoundingClientRect();\n                out.zrX = ex - box.left;\n                out.zrY = ey - box.top;\n                return;\n            }\n            else {\n                if (transformCoordWithViewport(_calcOut$1, el, ex, ey)) {\n                    out.zrX = _calcOut$1[0];\n                    out.zrY = _calcOut$1[1];\n                    return;\n                }\n            }\n        }\n        out.zrX = out.zrY = 0;\n    }\n    function getNativeEvent(e) {\n        return e\n            || window.event;\n    }\n    function normalizeEvent(el, e, calculate) {\n        e = getNativeEvent(e);\n        if (e.zrX != null) {\n            return e;\n        }\n        var eventType = e.type;\n        var isTouch = eventType && eventType.indexOf('touch') >= 0;\n        if (!isTouch) {\n            clientToLocal(el, e, e, calculate);\n            var wheelDelta = getWheelDeltaMayPolyfill(e);\n            e.zrDelta = wheelDelta ? wheelDelta / 120 : -(e.detail || 0) / 3;\n        }\n        else {\n            var touch = eventType !== 'touchend'\n                ? e.targetTouches[0]\n                : e.changedTouches[0];\n            touch && clientToLocal(el, touch, e, calculate);\n        }\n        var button = e.button;\n        if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {\n            e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n        }\n        return e;\n    }\n    function getWheelDeltaMayPolyfill(e) {\n        var rawWheelDelta = e.wheelDelta;\n        if (rawWheelDelta) {\n            return rawWheelDelta;\n        }\n        var deltaX = e.deltaX;\n        var deltaY = e.deltaY;\n        if (deltaX == null || deltaY == null) {\n            return rawWheelDelta;\n        }\n        var delta = deltaY !== 0 ? Math.abs(deltaY) : Math.abs(deltaX);\n        var sign = deltaY > 0 ? -1\n            : deltaY < 0 ? 1\n                : deltaX > 0 ? -1\n                    : 1;\n        return 3 * delta * sign;\n    }\n    function addEventListener(el, name, handler, opt) {\n        el.addEventListener(name, handler, opt);\n    }\n    function removeEventListener(el, name, handler, opt) {\n        el.removeEventListener(name, handler, opt);\n    }\n    var stop = function (e) {\n        e.preventDefault();\n        e.stopPropagation();\n        e.cancelBubble = true;\n    };\n    function isMiddleOrRightButtonOnMouseUpDown(e) {\n        return e.which === 2 || e.which === 3;\n    }\n\n    var GestureMgr = (function () {\n        function GestureMgr() {\n            this._track = [];\n        }\n        GestureMgr.prototype.recognize = function (event, target, root) {\n            this._doTrack(event, target, root);\n            return this._recognize(event);\n        };\n        GestureMgr.prototype.clear = function () {\n            this._track.length = 0;\n            return this;\n        };\n        GestureMgr.prototype._doTrack = function (event, target, root) {\n            var touches = event.touches;\n            if (!touches) {\n                return;\n            }\n            var trackItem = {\n                points: [],\n                touches: [],\n                target: target,\n                event: event\n            };\n            for (var i = 0, len = touches.length; i < len; i++) {\n                var touch = touches[i];\n                var pos = clientToLocal(root, touch, {});\n                trackItem.points.push([pos.zrX, pos.zrY]);\n                trackItem.touches.push(touch);\n            }\n            this._track.push(trackItem);\n        };\n        GestureMgr.prototype._recognize = function (event) {\n            for (var eventName in recognizers) {\n                if (recognizers.hasOwnProperty(eventName)) {\n                    var gestureInfo = recognizers[eventName](this._track, event);\n                    if (gestureInfo) {\n                        return gestureInfo;\n                    }\n                }\n            }\n        };\n        return GestureMgr;\n    }());\n    function dist$1(pointPair) {\n        var dx = pointPair[1][0] - pointPair[0][0];\n        var dy = pointPair[1][1] - pointPair[0][1];\n        return Math.sqrt(dx * dx + dy * dy);\n    }\n    function center(pointPair) {\n        return [\n            (pointPair[0][0] + pointPair[1][0]) / 2,\n            (pointPair[0][1] + pointPair[1][1]) / 2\n        ];\n    }\n    var recognizers = {\n        pinch: function (tracks, event) {\n            var trackLen = tracks.length;\n            if (!trackLen) {\n                return;\n            }\n            var pinchEnd = (tracks[trackLen - 1] || {}).points;\n            var pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd;\n            if (pinchPre\n                && pinchPre.length > 1\n                && pinchEnd\n                && pinchEnd.length > 1) {\n                var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);\n                !isFinite(pinchScale) && (pinchScale = 1);\n                event.pinchScale = pinchScale;\n                var pinchCenter = center(pinchEnd);\n                event.pinchX = pinchCenter[0];\n                event.pinchY = pinchCenter[1];\n                return {\n                    type: 'pinch',\n                    target: tracks[0].target,\n                    event: event\n                };\n            }\n        }\n    };\n\n    function create$1() {\n        return [1, 0, 0, 1, 0, 0];\n    }\n    function identity(out) {\n        out[0] = 1;\n        out[1] = 0;\n        out[2] = 0;\n        out[3] = 1;\n        out[4] = 0;\n        out[5] = 0;\n        return out;\n    }\n    function copy$1(out, m) {\n        out[0] = m[0];\n        out[1] = m[1];\n        out[2] = m[2];\n        out[3] = m[3];\n        out[4] = m[4];\n        out[5] = m[5];\n        return out;\n    }\n    function mul$1(out, m1, m2) {\n        var out0 = m1[0] * m2[0] + m1[2] * m2[1];\n        var out1 = m1[1] * m2[0] + m1[3] * m2[1];\n        var out2 = m1[0] * m2[2] + m1[2] * m2[3];\n        var out3 = m1[1] * m2[2] + m1[3] * m2[3];\n        var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];\n        var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];\n        out[0] = out0;\n        out[1] = out1;\n        out[2] = out2;\n        out[3] = out3;\n        out[4] = out4;\n        out[5] = out5;\n        return out;\n    }\n    function translate(out, a, v) {\n        out[0] = a[0];\n        out[1] = a[1];\n        out[2] = a[2];\n        out[3] = a[3];\n        out[4] = a[4] + v[0];\n        out[5] = a[5] + v[1];\n        return out;\n    }\n    function rotate(out, a, rad) {\n        var aa = a[0];\n        var ac = a[2];\n        var atx = a[4];\n        var ab = a[1];\n        var ad = a[3];\n        var aty = a[5];\n        var st = Math.sin(rad);\n        var ct = Math.cos(rad);\n        out[0] = aa * ct + ab * st;\n        out[1] = -aa * st + ab * ct;\n        out[2] = ac * ct + ad * st;\n        out[3] = -ac * st + ct * ad;\n        out[4] = ct * atx + st * aty;\n        out[5] = ct * aty - st * atx;\n        return out;\n    }\n    function scale$1(out, a, v) {\n        var vx = v[0];\n        var vy = v[1];\n        out[0] = a[0] * vx;\n        out[1] = a[1] * vy;\n        out[2] = a[2] * vx;\n        out[3] = a[3] * vy;\n        out[4] = a[4] * vx;\n        out[5] = a[5] * vy;\n        return out;\n    }\n    function invert(out, a) {\n        var aa = a[0];\n        var ac = a[2];\n        var atx = a[4];\n        var ab = a[1];\n        var ad = a[3];\n        var aty = a[5];\n        var det = aa * ad - ab * ac;\n        if (!det) {\n            return null;\n        }\n        det = 1.0 / det;\n        out[0] = ad * det;\n        out[1] = -ab * det;\n        out[2] = -ac * det;\n        out[3] = aa * det;\n        out[4] = (ac * aty - ad * atx) * det;\n        out[5] = (ab * atx - aa * aty) * det;\n        return out;\n    }\n    function clone$2(a) {\n        var b = create$1();\n        copy$1(b, a);\n        return b;\n    }\n\n    var matrix = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        create: create$1,\n        identity: identity,\n        copy: copy$1,\n        mul: mul$1,\n        translate: translate,\n        rotate: rotate,\n        scale: scale$1,\n        invert: invert,\n        clone: clone$2\n    });\n\n    var Point = (function () {\n        function Point(x, y) {\n            this.x = x || 0;\n            this.y = y || 0;\n        }\n        Point.prototype.copy = function (other) {\n            this.x = other.x;\n            this.y = other.y;\n            return this;\n        };\n        Point.prototype.clone = function () {\n            return new Point(this.x, this.y);\n        };\n        Point.prototype.set = function (x, y) {\n            this.x = x;\n            this.y = y;\n            return this;\n        };\n        Point.prototype.equal = function (other) {\n            return other.x === this.x && other.y === this.y;\n        };\n        Point.prototype.add = function (other) {\n            this.x += other.x;\n            this.y += other.y;\n            return this;\n        };\n        Point.prototype.scale = function (scalar) {\n            this.x *= scalar;\n            this.y *= scalar;\n        };\n        Point.prototype.scaleAndAdd = function (other, scalar) {\n            this.x += other.x * scalar;\n            this.y += other.y * scalar;\n        };\n        Point.prototype.sub = function (other) {\n            this.x -= other.x;\n            this.y -= other.y;\n            return this;\n        };\n        Point.prototype.dot = function (other) {\n            return this.x * other.x + this.y * other.y;\n        };\n        Point.prototype.len = function () {\n            return Math.sqrt(this.x * this.x + this.y * this.y);\n        };\n        Point.prototype.lenSquare = function () {\n            return this.x * this.x + this.y * this.y;\n        };\n        Point.prototype.normalize = function () {\n            var len = this.len();\n            this.x /= len;\n            this.y /= len;\n            return this;\n        };\n        Point.prototype.distance = function (other) {\n            var dx = this.x - other.x;\n            var dy = this.y - other.y;\n            return Math.sqrt(dx * dx + dy * dy);\n        };\n        Point.prototype.distanceSquare = function (other) {\n            var dx = this.x - other.x;\n            var dy = this.y - other.y;\n            return dx * dx + dy * dy;\n        };\n        Point.prototype.negate = function () {\n            this.x = -this.x;\n            this.y = -this.y;\n            return this;\n        };\n        Point.prototype.transform = function (m) {\n            if (!m) {\n                return;\n            }\n            var x = this.x;\n            var y = this.y;\n            this.x = m[0] * x + m[2] * y + m[4];\n            this.y = m[1] * x + m[3] * y + m[5];\n            return this;\n        };\n        Point.prototype.toArray = function (out) {\n            out[0] = this.x;\n            out[1] = this.y;\n            return out;\n        };\n        Point.prototype.fromArray = function (input) {\n            this.x = input[0];\n            this.y = input[1];\n        };\n        Point.set = function (p, x, y) {\n            p.x = x;\n            p.y = y;\n        };\n        Point.copy = function (p, p2) {\n            p.x = p2.x;\n            p.y = p2.y;\n        };\n        Point.len = function (p) {\n            return Math.sqrt(p.x * p.x + p.y * p.y);\n        };\n        Point.lenSquare = function (p) {\n            return p.x * p.x + p.y * p.y;\n        };\n        Point.dot = function (p0, p1) {\n            return p0.x * p1.x + p0.y * p1.y;\n        };\n        Point.add = function (out, p0, p1) {\n            out.x = p0.x + p1.x;\n            out.y = p0.y + p1.y;\n        };\n        Point.sub = function (out, p0, p1) {\n            out.x = p0.x - p1.x;\n            out.y = p0.y - p1.y;\n        };\n        Point.scale = function (out, p0, scalar) {\n            out.x = p0.x * scalar;\n            out.y = p0.y * scalar;\n        };\n        Point.scaleAndAdd = function (out, p0, p1, scalar) {\n            out.x = p0.x + p1.x * scalar;\n            out.y = p0.y + p1.y * scalar;\n        };\n        Point.lerp = function (out, p0, p1, t) {\n            var onet = 1 - t;\n            out.x = onet * p0.x + t * p1.x;\n            out.y = onet * p0.y + t * p1.y;\n        };\n        return Point;\n    }());\n\n    var mathMin = Math.min;\n    var mathMax = Math.max;\n    var lt = new Point();\n    var rb = new Point();\n    var lb = new Point();\n    var rt = new Point();\n    var minTv = new Point();\n    var maxTv = new Point();\n    var BoundingRect = (function () {\n        function BoundingRect(x, y, width, height) {\n            if (width < 0) {\n                x = x + width;\n                width = -width;\n            }\n            if (height < 0) {\n                y = y + height;\n                height = -height;\n            }\n            this.x = x;\n            this.y = y;\n            this.width = width;\n            this.height = height;\n        }\n        BoundingRect.prototype.union = function (other) {\n            var x = mathMin(other.x, this.x);\n            var y = mathMin(other.y, this.y);\n            if (isFinite(this.x) && isFinite(this.width)) {\n                this.width = mathMax(other.x + other.width, this.x + this.width) - x;\n            }\n            else {\n                this.width = other.width;\n            }\n            if (isFinite(this.y) && isFinite(this.height)) {\n                this.height = mathMax(other.y + other.height, this.y + this.height) - y;\n            }\n            else {\n                this.height = other.height;\n            }\n            this.x = x;\n            this.y = y;\n        };\n        BoundingRect.prototype.applyTransform = function (m) {\n            BoundingRect.applyTransform(this, this, m);\n        };\n        BoundingRect.prototype.calculateTransform = function (b) {\n            var a = this;\n            var sx = b.width / a.width;\n            var sy = b.height / a.height;\n            var m = create$1();\n            translate(m, m, [-a.x, -a.y]);\n            scale$1(m, m, [sx, sy]);\n            translate(m, m, [b.x, b.y]);\n            return m;\n        };\n        BoundingRect.prototype.intersect = function (b, mtv) {\n            if (!b) {\n                return false;\n            }\n            if (!(b instanceof BoundingRect)) {\n                b = BoundingRect.create(b);\n            }\n            var a = this;\n            var ax0 = a.x;\n            var ax1 = a.x + a.width;\n            var ay0 = a.y;\n            var ay1 = a.y + a.height;\n            var bx0 = b.x;\n            var bx1 = b.x + b.width;\n            var by0 = b.y;\n            var by1 = b.y + b.height;\n            var overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);\n            if (mtv) {\n                var dMin = Infinity;\n                var dMax = 0;\n                var d0 = Math.abs(ax1 - bx0);\n                var d1 = Math.abs(bx1 - ax0);\n                var d2 = Math.abs(ay1 - by0);\n                var d3 = Math.abs(by1 - ay0);\n                var dx = Math.min(d0, d1);\n                var dy = Math.min(d2, d3);\n                if (ax1 < bx0 || bx1 < ax0) {\n                    if (dx > dMax) {\n                        dMax = dx;\n                        if (d0 < d1) {\n                            Point.set(maxTv, -d0, 0);\n                        }\n                        else {\n                            Point.set(maxTv, d1, 0);\n                        }\n                    }\n                }\n                else {\n                    if (dx < dMin) {\n                        dMin = dx;\n                        if (d0 < d1) {\n                            Point.set(minTv, d0, 0);\n                        }\n                        else {\n                            Point.set(minTv, -d1, 0);\n                        }\n                    }\n                }\n                if (ay1 < by0 || by1 < ay0) {\n                    if (dy > dMax) {\n                        dMax = dy;\n                        if (d2 < d3) {\n                            Point.set(maxTv, 0, -d2);\n                        }\n                        else {\n                            Point.set(maxTv, 0, d3);\n                        }\n                    }\n                }\n                else {\n                    if (dx < dMin) {\n                        dMin = dx;\n                        if (d2 < d3) {\n                            Point.set(minTv, 0, d2);\n                        }\n                        else {\n                            Point.set(minTv, 0, -d3);\n                        }\n                    }\n                }\n            }\n            if (mtv) {\n                Point.copy(mtv, overlap ? minTv : maxTv);\n            }\n            return overlap;\n        };\n        BoundingRect.prototype.contain = function (x, y) {\n            var rect = this;\n            return x >= rect.x\n                && x <= (rect.x + rect.width)\n                && y >= rect.y\n                && y <= (rect.y + rect.height);\n        };\n        BoundingRect.prototype.clone = function () {\n            return new BoundingRect(this.x, this.y, this.width, this.height);\n        };\n        BoundingRect.prototype.copy = function (other) {\n            BoundingRect.copy(this, other);\n        };\n        BoundingRect.prototype.plain = function () {\n            return {\n                x: this.x,\n                y: this.y,\n                width: this.width,\n                height: this.height\n            };\n        };\n        BoundingRect.prototype.isFinite = function () {\n            return isFinite(this.x)\n                && isFinite(this.y)\n                && isFinite(this.width)\n                && isFinite(this.height);\n        };\n        BoundingRect.prototype.isZero = function () {\n            return this.width === 0 || this.height === 0;\n        };\n        BoundingRect.create = function (rect) {\n            return new BoundingRect(rect.x, rect.y, rect.width, rect.height);\n        };\n        BoundingRect.copy = function (target, source) {\n            target.x = source.x;\n            target.y = source.y;\n            target.width = source.width;\n            target.height = source.height;\n        };\n        BoundingRect.applyTransform = function (target, source, m) {\n            if (!m) {\n                if (target !== source) {\n                    BoundingRect.copy(target, source);\n                }\n                return;\n            }\n            if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) {\n                var sx = m[0];\n                var sy = m[3];\n                var tx = m[4];\n                var ty = m[5];\n                target.x = source.x * sx + tx;\n                target.y = source.y * sy + ty;\n                target.width = source.width * sx;\n                target.height = source.height * sy;\n                if (target.width < 0) {\n                    target.x += target.width;\n                    target.width = -target.width;\n                }\n                if (target.height < 0) {\n                    target.y += target.height;\n                    target.height = -target.height;\n                }\n                return;\n            }\n            lt.x = lb.x = source.x;\n            lt.y = rt.y = source.y;\n            rb.x = rt.x = source.x + source.width;\n            rb.y = lb.y = source.y + source.height;\n            lt.transform(m);\n            rt.transform(m);\n            rb.transform(m);\n            lb.transform(m);\n            target.x = mathMin(lt.x, rb.x, lb.x, rt.x);\n            target.y = mathMin(lt.y, rb.y, lb.y, rt.y);\n            var maxX = mathMax(lt.x, rb.x, lb.x, rt.x);\n            var maxY = mathMax(lt.y, rb.y, lb.y, rt.y);\n            target.width = maxX - target.x;\n            target.height = maxY - target.y;\n        };\n        return BoundingRect;\n    }());\n\n    var SILENT = 'silent';\n    function makeEventPacket(eveType, targetInfo, event) {\n        return {\n            type: eveType,\n            event: event,\n            target: targetInfo.target,\n            topTarget: targetInfo.topTarget,\n            cancelBubble: false,\n            offsetX: event.zrX,\n            offsetY: event.zrY,\n            gestureEvent: event.gestureEvent,\n            pinchX: event.pinchX,\n            pinchY: event.pinchY,\n            pinchScale: event.pinchScale,\n            wheelDelta: event.zrDelta,\n            zrByTouch: event.zrByTouch,\n            which: event.which,\n            stop: stopEvent\n        };\n    }\n    function stopEvent() {\n        stop(this.event);\n    }\n    var EmptyProxy = (function (_super) {\n        __extends(EmptyProxy, _super);\n        function EmptyProxy() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.handler = null;\n            return _this;\n        }\n        EmptyProxy.prototype.dispose = function () { };\n        EmptyProxy.prototype.setCursor = function () { };\n        return EmptyProxy;\n    }(Eventful));\n    var HoveredResult = (function () {\n        function HoveredResult(x, y) {\n            this.x = x;\n            this.y = y;\n        }\n        return HoveredResult;\n    }());\n    var handlerNames = [\n        'click', 'dblclick', 'mousewheel', 'mouseout',\n        'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n    ];\n    var tmpRect = new BoundingRect(0, 0, 0, 0);\n    var Handler = (function (_super) {\n        __extends(Handler, _super);\n        function Handler(storage, painter, proxy, painterRoot, pointerSize) {\n            var _this = _super.call(this) || this;\n            _this._hovered = new HoveredResult(0, 0);\n            _this.storage = storage;\n            _this.painter = painter;\n            _this.painterRoot = painterRoot;\n            _this._pointerSize = pointerSize;\n            proxy = proxy || new EmptyProxy();\n            _this.proxy = null;\n            _this.setHandlerProxy(proxy);\n            _this._draggingMgr = new Draggable(_this);\n            return _this;\n        }\n        Handler.prototype.setHandlerProxy = function (proxy) {\n            if (this.proxy) {\n                this.proxy.dispose();\n            }\n            if (proxy) {\n                each(handlerNames, function (name) {\n                    proxy.on && proxy.on(name, this[name], this);\n                }, this);\n                proxy.handler = this;\n            }\n            this.proxy = proxy;\n        };\n        Handler.prototype.mousemove = function (event) {\n            var x = event.zrX;\n            var y = event.zrY;\n            var isOutside = isOutsideBoundary(this, x, y);\n            var lastHovered = this._hovered;\n            var lastHoveredTarget = lastHovered.target;\n            if (lastHoveredTarget && !lastHoveredTarget.__zr) {\n                lastHovered = this.findHover(lastHovered.x, lastHovered.y);\n                lastHoveredTarget = lastHovered.target;\n            }\n            var hovered = this._hovered = isOutside ? new HoveredResult(x, y) : this.findHover(x, y);\n            var hoveredTarget = hovered.target;\n            var proxy = this.proxy;\n            proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');\n            if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {\n                this.dispatchToElement(lastHovered, 'mouseout', event);\n            }\n            this.dispatchToElement(hovered, 'mousemove', event);\n            if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {\n                this.dispatchToElement(hovered, 'mouseover', event);\n            }\n        };\n        Handler.prototype.mouseout = function (event) {\n            var eventControl = event.zrEventControl;\n            if (eventControl !== 'only_globalout') {\n                this.dispatchToElement(this._hovered, 'mouseout', event);\n            }\n            if (eventControl !== 'no_globalout') {\n                this.trigger('globalout', { type: 'globalout', event: event });\n            }\n        };\n        Handler.prototype.resize = function () {\n            this._hovered = new HoveredResult(0, 0);\n        };\n        Handler.prototype.dispatch = function (eventName, eventArgs) {\n            var handler = this[eventName];\n            handler && handler.call(this, eventArgs);\n        };\n        Handler.prototype.dispose = function () {\n            this.proxy.dispose();\n            this.storage = null;\n            this.proxy = null;\n            this.painter = null;\n        };\n        Handler.prototype.setCursorStyle = function (cursorStyle) {\n            var proxy = this.proxy;\n            proxy.setCursor && proxy.setCursor(cursorStyle);\n        };\n        Handler.prototype.dispatchToElement = function (targetInfo, eventName, event) {\n            targetInfo = targetInfo || {};\n            var el = targetInfo.target;\n            if (el && el.silent) {\n                return;\n            }\n            var eventKey = ('on' + eventName);\n            var eventPacket = makeEventPacket(eventName, targetInfo, event);\n            while (el) {\n                el[eventKey]\n                    && (eventPacket.cancelBubble = !!el[eventKey].call(el, eventPacket));\n                el.trigger(eventName, eventPacket);\n                el = el.__hostTarget ? el.__hostTarget : el.parent;\n                if (eventPacket.cancelBubble) {\n                    break;\n                }\n            }\n            if (!eventPacket.cancelBubble) {\n                this.trigger(eventName, eventPacket);\n                if (this.painter && this.painter.eachOtherLayer) {\n                    this.painter.eachOtherLayer(function (layer) {\n                        if (typeof (layer[eventKey]) === 'function') {\n                            layer[eventKey].call(layer, eventPacket);\n                        }\n                        if (layer.trigger) {\n                            layer.trigger(eventName, eventPacket);\n                        }\n                    });\n                }\n            }\n        };\n        Handler.prototype.findHover = function (x, y, exclude) {\n            var list = this.storage.getDisplayList();\n            var out = new HoveredResult(x, y);\n            setHoverTarget(list, out, x, y, exclude);\n            if (this._pointerSize && !out.target) {\n                var candidates = [];\n                var pointerSize = this._pointerSize;\n                var targetSizeHalf = pointerSize / 2;\n                var pointerRect = new BoundingRect(x - targetSizeHalf, y - targetSizeHalf, pointerSize, pointerSize);\n                for (var i = list.length - 1; i >= 0; i--) {\n                    var el = list[i];\n                    if (el !== exclude\n                        && !el.ignore\n                        && !el.ignoreCoarsePointer\n                        && (!el.parent || !el.parent.ignoreCoarsePointer)) {\n                        tmpRect.copy(el.getBoundingRect());\n                        if (el.transform) {\n                            tmpRect.applyTransform(el.transform);\n                        }\n                        if (tmpRect.intersect(pointerRect)) {\n                            candidates.push(el);\n                        }\n                    }\n                }\n                if (candidates.length) {\n                    var rStep = 4;\n                    var thetaStep = Math.PI / 12;\n                    var PI2 = Math.PI * 2;\n                    for (var r = 0; r < targetSizeHalf; r += rStep) {\n                        for (var theta = 0; theta < PI2; theta += thetaStep) {\n                            var x1 = x + r * Math.cos(theta);\n                            var y1 = y + r * Math.sin(theta);\n                            setHoverTarget(candidates, out, x1, y1, exclude);\n                            if (out.target) {\n                                return out;\n                            }\n                        }\n                    }\n                }\n            }\n            return out;\n        };\n        Handler.prototype.processGesture = function (event, stage) {\n            if (!this._gestureMgr) {\n                this._gestureMgr = new GestureMgr();\n            }\n            var gestureMgr = this._gestureMgr;\n            stage === 'start' && gestureMgr.clear();\n            var gestureInfo = gestureMgr.recognize(event, this.findHover(event.zrX, event.zrY, null).target, this.proxy.dom);\n            stage === 'end' && gestureMgr.clear();\n            if (gestureInfo) {\n                var type = gestureInfo.type;\n                event.gestureEvent = type;\n                var res = new HoveredResult();\n                res.target = gestureInfo.target;\n                this.dispatchToElement(res, type, gestureInfo.event);\n            }\n        };\n        return Handler;\n    }(Eventful));\n    each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {\n        Handler.prototype[name] = function (event) {\n            var x = event.zrX;\n            var y = event.zrY;\n            var isOutside = isOutsideBoundary(this, x, y);\n            var hovered;\n            var hoveredTarget;\n            if (name !== 'mouseup' || !isOutside) {\n                hovered = this.findHover(x, y);\n                hoveredTarget = hovered.target;\n            }\n            if (name === 'mousedown') {\n                this._downEl = hoveredTarget;\n                this._downPoint = [event.zrX, event.zrY];\n                this._upEl = hoveredTarget;\n            }\n            else if (name === 'mouseup') {\n                this._upEl = hoveredTarget;\n            }\n            else if (name === 'click') {\n                if (this._downEl !== this._upEl\n                    || !this._downPoint\n                    || dist(this._downPoint, [event.zrX, event.zrY]) > 4) {\n                    return;\n                }\n                this._downPoint = null;\n            }\n            this.dispatchToElement(hovered, name, event);\n        };\n    });\n    function isHover(displayable, x, y) {\n        if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {\n            var el = displayable;\n            var isSilent = void 0;\n            var ignoreClip = false;\n            while (el) {\n                if (el.ignoreClip) {\n                    ignoreClip = true;\n                }\n                if (!ignoreClip) {\n                    var clipPath = el.getClipPath();\n                    if (clipPath && !clipPath.contain(x, y)) {\n                        return false;\n                    }\n                    if (el.silent) {\n                        isSilent = true;\n                    }\n                }\n                var hostEl = el.__hostTarget;\n                el = hostEl ? hostEl : el.parent;\n            }\n            return isSilent ? SILENT : true;\n        }\n        return false;\n    }\n    function setHoverTarget(list, out, x, y, exclude) {\n        for (var i = list.length - 1; i >= 0; i--) {\n            var el = list[i];\n            var hoverCheckResult = void 0;\n            if (el !== exclude\n                && !el.ignore\n                && (hoverCheckResult = isHover(el, x, y))) {\n                !out.topTarget && (out.topTarget = el);\n                if (hoverCheckResult !== SILENT) {\n                    out.target = el;\n                    break;\n                }\n            }\n        }\n    }\n    function isOutsideBoundary(handlerInstance, x, y) {\n        var painter = handlerInstance.painter;\n        return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight();\n    }\n\n    var DEFAULT_MIN_MERGE = 32;\n    var DEFAULT_MIN_GALLOPING = 7;\n    function minRunLength(n) {\n        var r = 0;\n        while (n >= DEFAULT_MIN_MERGE) {\n            r |= n & 1;\n            n >>= 1;\n        }\n        return n + r;\n    }\n    function makeAscendingRun(array, lo, hi, compare) {\n        var runHi = lo + 1;\n        if (runHi === hi) {\n            return 1;\n        }\n        if (compare(array[runHi++], array[lo]) < 0) {\n            while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {\n                runHi++;\n            }\n            reverseRun(array, lo, runHi);\n        }\n        else {\n            while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {\n                runHi++;\n            }\n        }\n        return runHi - lo;\n    }\n    function reverseRun(array, lo, hi) {\n        hi--;\n        while (lo < hi) {\n            var t = array[lo];\n            array[lo++] = array[hi];\n            array[hi--] = t;\n        }\n    }\n    function binaryInsertionSort(array, lo, hi, start, compare) {\n        if (start === lo) {\n            start++;\n        }\n        for (; start < hi; start++) {\n            var pivot = array[start];\n            var left = lo;\n            var right = start;\n            var mid;\n            while (left < right) {\n                mid = left + right >>> 1;\n                if (compare(pivot, array[mid]) < 0) {\n                    right = mid;\n                }\n                else {\n                    left = mid + 1;\n                }\n            }\n            var n = start - left;\n            switch (n) {\n                case 3:\n                    array[left + 3] = array[left + 2];\n                case 2:\n                    array[left + 2] = array[left + 1];\n                case 1:\n                    array[left + 1] = array[left];\n                    break;\n                default:\n                    while (n > 0) {\n                        array[left + n] = array[left + n - 1];\n                        n--;\n                    }\n            }\n            array[left] = pivot;\n        }\n    }\n    function gallopLeft(value, array, start, length, hint, compare) {\n        var lastOffset = 0;\n        var maxOffset = 0;\n        var offset = 1;\n        if (compare(value, array[start + hint]) > 0) {\n            maxOffset = length - hint;\n            while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            lastOffset += hint;\n            offset += hint;\n        }\n        else {\n            maxOffset = hint + 1;\n            while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            var tmp = lastOffset;\n            lastOffset = hint - offset;\n            offset = hint - tmp;\n        }\n        lastOffset++;\n        while (lastOffset < offset) {\n            var m = lastOffset + (offset - lastOffset >>> 1);\n            if (compare(value, array[start + m]) > 0) {\n                lastOffset = m + 1;\n            }\n            else {\n                offset = m;\n            }\n        }\n        return offset;\n    }\n    function gallopRight(value, array, start, length, hint, compare) {\n        var lastOffset = 0;\n        var maxOffset = 0;\n        var offset = 1;\n        if (compare(value, array[start + hint]) < 0) {\n            maxOffset = hint + 1;\n            while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            var tmp = lastOffset;\n            lastOffset = hint - offset;\n            offset = hint - tmp;\n        }\n        else {\n            maxOffset = length - hint;\n            while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            lastOffset += hint;\n            offset += hint;\n        }\n        lastOffset++;\n        while (lastOffset < offset) {\n            var m = lastOffset + (offset - lastOffset >>> 1);\n            if (compare(value, array[start + m]) < 0) {\n                offset = m;\n            }\n            else {\n                lastOffset = m + 1;\n            }\n        }\n        return offset;\n    }\n    function TimSort(array, compare) {\n        var minGallop = DEFAULT_MIN_GALLOPING;\n        var length = 0;\n        var runStart;\n        var runLength;\n        var stackSize = 0;\n        length = array.length;\n        var tmp = [];\n        runStart = [];\n        runLength = [];\n        function pushRun(_runStart, _runLength) {\n            runStart[stackSize] = _runStart;\n            runLength[stackSize] = _runLength;\n            stackSize += 1;\n        }\n        function mergeRuns() {\n            while (stackSize > 1) {\n                var n = stackSize - 2;\n                if ((n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1])\n                    || (n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1])) {\n                    if (runLength[n - 1] < runLength[n + 1]) {\n                        n--;\n                    }\n                }\n                else if (runLength[n] > runLength[n + 1]) {\n                    break;\n                }\n                mergeAt(n);\n            }\n        }\n        function forceMergeRuns() {\n            while (stackSize > 1) {\n                var n = stackSize - 2;\n                if (n > 0 && runLength[n - 1] < runLength[n + 1]) {\n                    n--;\n                }\n                mergeAt(n);\n            }\n        }\n        function mergeAt(i) {\n            var start1 = runStart[i];\n            var length1 = runLength[i];\n            var start2 = runStart[i + 1];\n            var length2 = runLength[i + 1];\n            runLength[i] = length1 + length2;\n            if (i === stackSize - 3) {\n                runStart[i + 1] = runStart[i + 2];\n                runLength[i + 1] = runLength[i + 2];\n            }\n            stackSize--;\n            var k = gallopRight(array[start2], array, start1, length1, 0, compare);\n            start1 += k;\n            length1 -= k;\n            if (length1 === 0) {\n                return;\n            }\n            length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);\n            if (length2 === 0) {\n                return;\n            }\n            if (length1 <= length2) {\n                mergeLow(start1, length1, start2, length2);\n            }\n            else {\n                mergeHigh(start1, length1, start2, length2);\n            }\n        }\n        function mergeLow(start1, length1, start2, length2) {\n            var i = 0;\n            for (i = 0; i < length1; i++) {\n                tmp[i] = array[start1 + i];\n            }\n            var cursor1 = 0;\n            var cursor2 = start2;\n            var dest = start1;\n            array[dest++] = array[cursor2++];\n            if (--length2 === 0) {\n                for (i = 0; i < length1; i++) {\n                    array[dest + i] = tmp[cursor1 + i];\n                }\n                return;\n            }\n            if (length1 === 1) {\n                for (i = 0; i < length2; i++) {\n                    array[dest + i] = array[cursor2 + i];\n                }\n                array[dest + length2] = tmp[cursor1];\n                return;\n            }\n            var _minGallop = minGallop;\n            var count1;\n            var count2;\n            var exit;\n            while (1) {\n                count1 = 0;\n                count2 = 0;\n                exit = false;\n                do {\n                    if (compare(array[cursor2], tmp[cursor1]) < 0) {\n                        array[dest++] = array[cursor2++];\n                        count2++;\n                        count1 = 0;\n                        if (--length2 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    else {\n                        array[dest++] = tmp[cursor1++];\n                        count1++;\n                        count2 = 0;\n                        if (--length1 === 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                } while ((count1 | count2) < _minGallop);\n                if (exit) {\n                    break;\n                }\n                do {\n                    count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);\n                    if (count1 !== 0) {\n                        for (i = 0; i < count1; i++) {\n                            array[dest + i] = tmp[cursor1 + i];\n                        }\n                        dest += count1;\n                        cursor1 += count1;\n                        length1 -= count1;\n                        if (length1 <= 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest++] = array[cursor2++];\n                    if (--length2 === 0) {\n                        exit = true;\n                        break;\n                    }\n                    count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);\n                    if (count2 !== 0) {\n                        for (i = 0; i < count2; i++) {\n                            array[dest + i] = array[cursor2 + i];\n                        }\n                        dest += count2;\n                        cursor2 += count2;\n                        length2 -= count2;\n                        if (length2 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest++] = tmp[cursor1++];\n                    if (--length1 === 1) {\n                        exit = true;\n                        break;\n                    }\n                    _minGallop--;\n                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n                if (exit) {\n                    break;\n                }\n                if (_minGallop < 0) {\n                    _minGallop = 0;\n                }\n                _minGallop += 2;\n            }\n            minGallop = _minGallop;\n            minGallop < 1 && (minGallop = 1);\n            if (length1 === 1) {\n                for (i = 0; i < length2; i++) {\n                    array[dest + i] = array[cursor2 + i];\n                }\n                array[dest + length2] = tmp[cursor1];\n            }\n            else if (length1 === 0) {\n                throw new Error();\n            }\n            else {\n                for (i = 0; i < length1; i++) {\n                    array[dest + i] = tmp[cursor1 + i];\n                }\n            }\n        }\n        function mergeHigh(start1, length1, start2, length2) {\n            var i = 0;\n            for (i = 0; i < length2; i++) {\n                tmp[i] = array[start2 + i];\n            }\n            var cursor1 = start1 + length1 - 1;\n            var cursor2 = length2 - 1;\n            var dest = start2 + length2 - 1;\n            var customCursor = 0;\n            var customDest = 0;\n            array[dest--] = array[cursor1--];\n            if (--length1 === 0) {\n                customCursor = dest - (length2 - 1);\n                for (i = 0; i < length2; i++) {\n                    array[customCursor + i] = tmp[i];\n                }\n                return;\n            }\n            if (length2 === 1) {\n                dest -= length1;\n                cursor1 -= length1;\n                customDest = dest + 1;\n                customCursor = cursor1 + 1;\n                for (i = length1 - 1; i >= 0; i--) {\n                    array[customDest + i] = array[customCursor + i];\n                }\n                array[dest] = tmp[cursor2];\n                return;\n            }\n            var _minGallop = minGallop;\n            while (true) {\n                var count1 = 0;\n                var count2 = 0;\n                var exit = false;\n                do {\n                    if (compare(tmp[cursor2], array[cursor1]) < 0) {\n                        array[dest--] = array[cursor1--];\n                        count1++;\n                        count2 = 0;\n                        if (--length1 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    else {\n                        array[dest--] = tmp[cursor2--];\n                        count2++;\n                        count1 = 0;\n                        if (--length2 === 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                } while ((count1 | count2) < _minGallop);\n                if (exit) {\n                    break;\n                }\n                do {\n                    count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);\n                    if (count1 !== 0) {\n                        dest -= count1;\n                        cursor1 -= count1;\n                        length1 -= count1;\n                        customDest = dest + 1;\n                        customCursor = cursor1 + 1;\n                        for (i = count1 - 1; i >= 0; i--) {\n                            array[customDest + i] = array[customCursor + i];\n                        }\n                        if (length1 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest--] = tmp[cursor2--];\n                    if (--length2 === 1) {\n                        exit = true;\n                        break;\n                    }\n                    count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);\n                    if (count2 !== 0) {\n                        dest -= count2;\n                        cursor2 -= count2;\n                        length2 -= count2;\n                        customDest = dest + 1;\n                        customCursor = cursor2 + 1;\n                        for (i = 0; i < count2; i++) {\n                            array[customDest + i] = tmp[customCursor + i];\n                        }\n                        if (length2 <= 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest--] = array[cursor1--];\n                    if (--length1 === 0) {\n                        exit = true;\n                        break;\n                    }\n                    _minGallop--;\n                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n                if (exit) {\n                    break;\n                }\n                if (_minGallop < 0) {\n                    _minGallop = 0;\n                }\n                _minGallop += 2;\n            }\n            minGallop = _minGallop;\n            if (minGallop < 1) {\n                minGallop = 1;\n            }\n            if (length2 === 1) {\n                dest -= length1;\n                cursor1 -= length1;\n                customDest = dest + 1;\n                customCursor = cursor1 + 1;\n                for (i = length1 - 1; i >= 0; i--) {\n                    array[customDest + i] = array[customCursor + i];\n                }\n                array[dest] = tmp[cursor2];\n            }\n            else if (length2 === 0) {\n                throw new Error();\n            }\n            else {\n                customCursor = dest - (length2 - 1);\n                for (i = 0; i < length2; i++) {\n                    array[customCursor + i] = tmp[i];\n                }\n            }\n        }\n        return {\n            mergeRuns: mergeRuns,\n            forceMergeRuns: forceMergeRuns,\n            pushRun: pushRun\n        };\n    }\n    function sort(array, compare, lo, hi) {\n        if (!lo) {\n            lo = 0;\n        }\n        if (!hi) {\n            hi = array.length;\n        }\n        var remaining = hi - lo;\n        if (remaining < 2) {\n            return;\n        }\n        var runLength = 0;\n        if (remaining < DEFAULT_MIN_MERGE) {\n            runLength = makeAscendingRun(array, lo, hi, compare);\n            binaryInsertionSort(array, lo, hi, lo + runLength, compare);\n            return;\n        }\n        var ts = TimSort(array, compare);\n        var minRun = minRunLength(remaining);\n        do {\n            runLength = makeAscendingRun(array, lo, hi, compare);\n            if (runLength < minRun) {\n                var force = remaining;\n                if (force > minRun) {\n                    force = minRun;\n                }\n                binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);\n                runLength = force;\n            }\n            ts.pushRun(lo, runLength);\n            ts.mergeRuns();\n            remaining -= runLength;\n            lo += runLength;\n        } while (remaining !== 0);\n        ts.forceMergeRuns();\n    }\n\n    var REDRAW_BIT = 1;\n    var STYLE_CHANGED_BIT = 2;\n    var SHAPE_CHANGED_BIT = 4;\n\n    var invalidZErrorLogged = false;\n    function logInvalidZError() {\n        if (invalidZErrorLogged) {\n            return;\n        }\n        invalidZErrorLogged = true;\n        console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors');\n    }\n    function shapeCompareFunc(a, b) {\n        if (a.zlevel === b.zlevel) {\n            if (a.z === b.z) {\n                return a.z2 - b.z2;\n            }\n            return a.z - b.z;\n        }\n        return a.zlevel - b.zlevel;\n    }\n    var Storage = (function () {\n        function Storage() {\n            this._roots = [];\n            this._displayList = [];\n            this._displayListLen = 0;\n            this.displayableSortFunc = shapeCompareFunc;\n        }\n        Storage.prototype.traverse = function (cb, context) {\n            for (var i = 0; i < this._roots.length; i++) {\n                this._roots[i].traverse(cb, context);\n            }\n        };\n        Storage.prototype.getDisplayList = function (update, includeIgnore) {\n            includeIgnore = includeIgnore || false;\n            var displayList = this._displayList;\n            if (update || !displayList.length) {\n                this.updateDisplayList(includeIgnore);\n            }\n            return displayList;\n        };\n        Storage.prototype.updateDisplayList = function (includeIgnore) {\n            this._displayListLen = 0;\n            var roots = this._roots;\n            var displayList = this._displayList;\n            for (var i = 0, len = roots.length; i < len; i++) {\n                this._updateAndAddDisplayable(roots[i], null, includeIgnore);\n            }\n            displayList.length = this._displayListLen;\n            sort(displayList, shapeCompareFunc);\n        };\n        Storage.prototype._updateAndAddDisplayable = function (el, clipPaths, includeIgnore) {\n            if (el.ignore && !includeIgnore) {\n                return;\n            }\n            el.beforeUpdate();\n            el.update();\n            el.afterUpdate();\n            var userSetClipPath = el.getClipPath();\n            if (el.ignoreClip) {\n                clipPaths = null;\n            }\n            else if (userSetClipPath) {\n                if (clipPaths) {\n                    clipPaths = clipPaths.slice();\n                }\n                else {\n                    clipPaths = [];\n                }\n                var currentClipPath = userSetClipPath;\n                var parentClipPath = el;\n                while (currentClipPath) {\n                    currentClipPath.parent = parentClipPath;\n                    currentClipPath.updateTransform();\n                    clipPaths.push(currentClipPath);\n                    parentClipPath = currentClipPath;\n                    currentClipPath = currentClipPath.getClipPath();\n                }\n            }\n            if (el.childrenRef) {\n                var children = el.childrenRef();\n                for (var i = 0; i < children.length; i++) {\n                    var child = children[i];\n                    if (el.__dirty) {\n                        child.__dirty |= REDRAW_BIT;\n                    }\n                    this._updateAndAddDisplayable(child, clipPaths, includeIgnore);\n                }\n                el.__dirty = 0;\n            }\n            else {\n                var disp = el;\n                if (clipPaths && clipPaths.length) {\n                    disp.__clipPaths = clipPaths;\n                }\n                else if (disp.__clipPaths && disp.__clipPaths.length > 0) {\n                    disp.__clipPaths = [];\n                }\n                if (isNaN(disp.z)) {\n                    logInvalidZError();\n                    disp.z = 0;\n                }\n                if (isNaN(disp.z2)) {\n                    logInvalidZError();\n                    disp.z2 = 0;\n                }\n                if (isNaN(disp.zlevel)) {\n                    logInvalidZError();\n                    disp.zlevel = 0;\n                }\n                this._displayList[this._displayListLen++] = disp;\n            }\n            var decalEl = el.getDecalElement && el.getDecalElement();\n            if (decalEl) {\n                this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore);\n            }\n            var textGuide = el.getTextGuideLine();\n            if (textGuide) {\n                this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore);\n            }\n            var textEl = el.getTextContent();\n            if (textEl) {\n                this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore);\n            }\n        };\n        Storage.prototype.addRoot = function (el) {\n            if (el.__zr && el.__zr.storage === this) {\n                return;\n            }\n            this._roots.push(el);\n        };\n        Storage.prototype.delRoot = function (el) {\n            if (el instanceof Array) {\n                for (var i = 0, l = el.length; i < l; i++) {\n                    this.delRoot(el[i]);\n                }\n                return;\n            }\n            var idx = indexOf(this._roots, el);\n            if (idx >= 0) {\n                this._roots.splice(idx, 1);\n            }\n        };\n        Storage.prototype.delAllRoots = function () {\n            this._roots = [];\n            this._displayList = [];\n            this._displayListLen = 0;\n            return;\n        };\n        Storage.prototype.getRoots = function () {\n            return this._roots;\n        };\n        Storage.prototype.dispose = function () {\n            this._displayList = null;\n            this._roots = null;\n        };\n        return Storage;\n    }());\n\n    var requestAnimationFrame;\n    requestAnimationFrame = (env.hasGlobalWindow\n        && ((window.requestAnimationFrame && window.requestAnimationFrame.bind(window))\n            || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))\n            || window.mozRequestAnimationFrame\n            || window.webkitRequestAnimationFrame)) || function (func) {\n        return setTimeout(func, 16);\n    };\n    var requestAnimationFrame$1 = requestAnimationFrame;\n\n    var easingFuncs = {\n        linear: function (k) {\n            return k;\n        },\n        quadraticIn: function (k) {\n            return k * k;\n        },\n        quadraticOut: function (k) {\n            return k * (2 - k);\n        },\n        quadraticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k;\n            }\n            return -0.5 * (--k * (k - 2) - 1);\n        },\n        cubicIn: function (k) {\n            return k * k * k;\n        },\n        cubicOut: function (k) {\n            return --k * k * k + 1;\n        },\n        cubicInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k;\n            }\n            return 0.5 * ((k -= 2) * k * k + 2);\n        },\n        quarticIn: function (k) {\n            return k * k * k * k;\n        },\n        quarticOut: function (k) {\n            return 1 - (--k * k * k * k);\n        },\n        quarticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k * k;\n            }\n            return -0.5 * ((k -= 2) * k * k * k - 2);\n        },\n        quinticIn: function (k) {\n            return k * k * k * k * k;\n        },\n        quinticOut: function (k) {\n            return --k * k * k * k * k + 1;\n        },\n        quinticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k * k * k;\n            }\n            return 0.5 * ((k -= 2) * k * k * k * k + 2);\n        },\n        sinusoidalIn: function (k) {\n            return 1 - Math.cos(k * Math.PI / 2);\n        },\n        sinusoidalOut: function (k) {\n            return Math.sin(k * Math.PI / 2);\n        },\n        sinusoidalInOut: function (k) {\n            return 0.5 * (1 - Math.cos(Math.PI * k));\n        },\n        exponentialIn: function (k) {\n            return k === 0 ? 0 : Math.pow(1024, k - 1);\n        },\n        exponentialOut: function (k) {\n            return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);\n        },\n        exponentialInOut: function (k) {\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if ((k *= 2) < 1) {\n                return 0.5 * Math.pow(1024, k - 1);\n            }\n            return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);\n        },\n        circularIn: function (k) {\n            return 1 - Math.sqrt(1 - k * k);\n        },\n        circularOut: function (k) {\n            return Math.sqrt(1 - (--k * k));\n        },\n        circularInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return -0.5 * (Math.sqrt(1 - k * k) - 1);\n            }\n            return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);\n        },\n        elasticIn: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            return -(a * Math.pow(2, 10 * (k -= 1))\n                * Math.sin((k - s) * (2 * Math.PI) / p));\n        },\n        elasticOut: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            return (a * Math.pow(2, -10 * k)\n                * Math.sin((k - s) * (2 * Math.PI) / p) + 1);\n        },\n        elasticInOut: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            if ((k *= 2) < 1) {\n                return -0.5 * (a * Math.pow(2, 10 * (k -= 1))\n                    * Math.sin((k - s) * (2 * Math.PI) / p));\n            }\n            return a * Math.pow(2, -10 * (k -= 1))\n                * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;\n        },\n        backIn: function (k) {\n            var s = 1.70158;\n            return k * k * ((s + 1) * k - s);\n        },\n        backOut: function (k) {\n            var s = 1.70158;\n            return --k * k * ((s + 1) * k + s) + 1;\n        },\n        backInOut: function (k) {\n            var s = 1.70158 * 1.525;\n            if ((k *= 2) < 1) {\n                return 0.5 * (k * k * ((s + 1) * k - s));\n            }\n            return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);\n        },\n        bounceIn: function (k) {\n            return 1 - easingFuncs.bounceOut(1 - k);\n        },\n        bounceOut: function (k) {\n            if (k < (1 / 2.75)) {\n                return 7.5625 * k * k;\n            }\n            else if (k < (2 / 2.75)) {\n                return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;\n            }\n            else if (k < (2.5 / 2.75)) {\n                return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;\n            }\n            else {\n                return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;\n            }\n        },\n        bounceInOut: function (k) {\n            if (k < 0.5) {\n                return easingFuncs.bounceIn(k * 2) * 0.5;\n            }\n            return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5;\n        }\n    };\n\n    var mathPow = Math.pow;\n    var mathSqrt = Math.sqrt;\n    var EPSILON = 1e-8;\n    var EPSILON_NUMERIC = 1e-4;\n    var THREE_SQRT = mathSqrt(3);\n    var ONE_THIRD = 1 / 3;\n    var _v0 = create();\n    var _v1 = create();\n    var _v2 = create();\n    function isAroundZero(val) {\n        return val > -EPSILON && val < EPSILON;\n    }\n    function isNotAroundZero(val) {\n        return val > EPSILON || val < -EPSILON;\n    }\n    function cubicAt(p0, p1, p2, p3, t) {\n        var onet = 1 - t;\n        return onet * onet * (onet * p0 + 3 * t * p1)\n            + t * t * (t * p3 + 3 * onet * p2);\n    }\n    function cubicDerivativeAt(p0, p1, p2, p3, t) {\n        var onet = 1 - t;\n        return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet\n            + (p3 - p2) * t * t);\n    }\n    function cubicRootAt(p0, p1, p2, p3, val, roots) {\n        var a = p3 + 3 * (p1 - p2) - p0;\n        var b = 3 * (p2 - p1 * 2 + p0);\n        var c = 3 * (p1 - p0);\n        var d = p0 - val;\n        var A = b * b - 3 * a * c;\n        var B = b * c - 9 * a * d;\n        var C = c * c - 3 * b * d;\n        var n = 0;\n        if (isAroundZero(A) && isAroundZero(B)) {\n            if (isAroundZero(b)) {\n                roots[0] = 0;\n            }\n            else {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = B * B - 4 * A * C;\n            if (isAroundZero(disc)) {\n                var K = B / A;\n                var t1 = -b / a + K;\n                var t2 = -K / 2;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var Y1 = A * b + 1.5 * a * (-B + discSqrt);\n                var Y2 = A * b + 1.5 * a * (-B - discSqrt);\n                if (Y1 < 0) {\n                    Y1 = -mathPow(-Y1, ONE_THIRD);\n                }\n                else {\n                    Y1 = mathPow(Y1, ONE_THIRD);\n                }\n                if (Y2 < 0) {\n                    Y2 = -mathPow(-Y2, ONE_THIRD);\n                }\n                else {\n                    Y2 = mathPow(Y2, ONE_THIRD);\n                }\n                var t1 = (-b - (Y1 + Y2)) / (3 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n            else {\n                var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));\n                var theta = Math.acos(T) / 3;\n                var ASqrt = mathSqrt(A);\n                var tmp = Math.cos(theta);\n                var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);\n                var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);\n                var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n                if (t3 >= 0 && t3 <= 1) {\n                    roots[n++] = t3;\n                }\n            }\n        }\n        return n;\n    }\n    function cubicExtrema(p0, p1, p2, p3, extrema) {\n        var b = 6 * p2 - 12 * p1 + 6 * p0;\n        var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;\n        var c = 3 * p1 - 3 * p0;\n        var n = 0;\n        if (isAroundZero(a)) {\n            if (isNotAroundZero(b)) {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    extrema[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = b * b - 4 * a * c;\n            if (isAroundZero(disc)) {\n                extrema[0] = -b / (2 * a);\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var t1 = (-b + discSqrt) / (2 * a);\n                var t2 = (-b - discSqrt) / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    extrema[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    extrema[n++] = t2;\n                }\n            }\n        }\n        return n;\n    }\n    function cubicSubdivide(p0, p1, p2, p3, t, out) {\n        var p01 = (p1 - p0) * t + p0;\n        var p12 = (p2 - p1) * t + p1;\n        var p23 = (p3 - p2) * t + p2;\n        var p012 = (p12 - p01) * t + p01;\n        var p123 = (p23 - p12) * t + p12;\n        var p0123 = (p123 - p012) * t + p012;\n        out[0] = p0;\n        out[1] = p01;\n        out[2] = p012;\n        out[3] = p0123;\n        out[4] = p0123;\n        out[5] = p123;\n        out[6] = p23;\n        out[7] = p3;\n    }\n    function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) {\n        var t;\n        var interval = 0.005;\n        var d = Infinity;\n        var prev;\n        var next;\n        var d1;\n        var d2;\n        _v0[0] = x;\n        _v0[1] = y;\n        for (var _t = 0; _t < 1; _t += 0.05) {\n            _v1[0] = cubicAt(x0, x1, x2, x3, _t);\n            _v1[1] = cubicAt(y0, y1, y2, y3, _t);\n            d1 = distSquare(_v0, _v1);\n            if (d1 < d) {\n                t = _t;\n                d = d1;\n            }\n        }\n        d = Infinity;\n        for (var i = 0; i < 32; i++) {\n            if (interval < EPSILON_NUMERIC) {\n                break;\n            }\n            prev = t - interval;\n            next = t + interval;\n            _v1[0] = cubicAt(x0, x1, x2, x3, prev);\n            _v1[1] = cubicAt(y0, y1, y2, y3, prev);\n            d1 = distSquare(_v1, _v0);\n            if (prev >= 0 && d1 < d) {\n                t = prev;\n                d = d1;\n            }\n            else {\n                _v2[0] = cubicAt(x0, x1, x2, x3, next);\n                _v2[1] = cubicAt(y0, y1, y2, y3, next);\n                d2 = distSquare(_v2, _v0);\n                if (next <= 1 && d2 < d) {\n                    t = next;\n                    d = d2;\n                }\n                else {\n                    interval *= 0.5;\n                }\n            }\n        }\n        if (out) {\n            out[0] = cubicAt(x0, x1, x2, x3, t);\n            out[1] = cubicAt(y0, y1, y2, y3, t);\n        }\n        return mathSqrt(d);\n    }\n    function cubicLength(x0, y0, x1, y1, x2, y2, x3, y3, iteration) {\n        var px = x0;\n        var py = y0;\n        var d = 0;\n        var step = 1 / iteration;\n        for (var i = 1; i <= iteration; i++) {\n            var t = i * step;\n            var x = cubicAt(x0, x1, x2, x3, t);\n            var y = cubicAt(y0, y1, y2, y3, t);\n            var dx = x - px;\n            var dy = y - py;\n            d += Math.sqrt(dx * dx + dy * dy);\n            px = x;\n            py = y;\n        }\n        return d;\n    }\n    function quadraticAt(p0, p1, p2, t) {\n        var onet = 1 - t;\n        return onet * (onet * p0 + 2 * t * p1) + t * t * p2;\n    }\n    function quadraticDerivativeAt(p0, p1, p2, t) {\n        return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));\n    }\n    function quadraticRootAt(p0, p1, p2, val, roots) {\n        var a = p0 - 2 * p1 + p2;\n        var b = 2 * (p1 - p0);\n        var c = p0 - val;\n        var n = 0;\n        if (isAroundZero(a)) {\n            if (isNotAroundZero(b)) {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = b * b - 4 * a * c;\n            if (isAroundZero(disc)) {\n                var t1 = -b / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var t1 = (-b + discSqrt) / (2 * a);\n                var t2 = (-b - discSqrt) / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n            }\n        }\n        return n;\n    }\n    function quadraticExtremum(p0, p1, p2) {\n        var divider = p0 + p2 - 2 * p1;\n        if (divider === 0) {\n            return 0.5;\n        }\n        else {\n            return (p0 - p1) / divider;\n        }\n    }\n    function quadraticSubdivide(p0, p1, p2, t, out) {\n        var p01 = (p1 - p0) * t + p0;\n        var p12 = (p2 - p1) * t + p1;\n        var p012 = (p12 - p01) * t + p01;\n        out[0] = p0;\n        out[1] = p01;\n        out[2] = p012;\n        out[3] = p012;\n        out[4] = p12;\n        out[5] = p2;\n    }\n    function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) {\n        var t;\n        var interval = 0.005;\n        var d = Infinity;\n        _v0[0] = x;\n        _v0[1] = y;\n        for (var _t = 0; _t < 1; _t += 0.05) {\n            _v1[0] = quadraticAt(x0, x1, x2, _t);\n            _v1[1] = quadraticAt(y0, y1, y2, _t);\n            var d1 = distSquare(_v0, _v1);\n            if (d1 < d) {\n                t = _t;\n                d = d1;\n            }\n        }\n        d = Infinity;\n        for (var i = 0; i < 32; i++) {\n            if (interval < EPSILON_NUMERIC) {\n                break;\n            }\n            var prev = t - interval;\n            var next = t + interval;\n            _v1[0] = quadraticAt(x0, x1, x2, prev);\n            _v1[1] = quadraticAt(y0, y1, y2, prev);\n            var d1 = distSquare(_v1, _v0);\n            if (prev >= 0 && d1 < d) {\n                t = prev;\n                d = d1;\n            }\n            else {\n                _v2[0] = quadraticAt(x0, x1, x2, next);\n                _v2[1] = quadraticAt(y0, y1, y2, next);\n                var d2 = distSquare(_v2, _v0);\n                if (next <= 1 && d2 < d) {\n                    t = next;\n                    d = d2;\n                }\n                else {\n                    interval *= 0.5;\n                }\n            }\n        }\n        if (out) {\n            out[0] = quadraticAt(x0, x1, x2, t);\n            out[1] = quadraticAt(y0, y1, y2, t);\n        }\n        return mathSqrt(d);\n    }\n    function quadraticLength(x0, y0, x1, y1, x2, y2, iteration) {\n        var px = x0;\n        var py = y0;\n        var d = 0;\n        var step = 1 / iteration;\n        for (var i = 1; i <= iteration; i++) {\n            var t = i * step;\n            var x = quadraticAt(x0, x1, x2, t);\n            var y = quadraticAt(y0, y1, y2, t);\n            var dx = x - px;\n            var dy = y - py;\n            d += Math.sqrt(dx * dx + dy * dy);\n            px = x;\n            py = y;\n        }\n        return d;\n    }\n\n    var regexp = /cubic-bezier\\(([0-9,\\.e ]+)\\)/;\n    function createCubicEasingFunc(cubicEasingStr) {\n        var cubic = cubicEasingStr && regexp.exec(cubicEasingStr);\n        if (cubic) {\n            var points = cubic[1].split(',');\n            var a_1 = +trim(points[0]);\n            var b_1 = +trim(points[1]);\n            var c_1 = +trim(points[2]);\n            var d_1 = +trim(points[3]);\n            if (isNaN(a_1 + b_1 + c_1 + d_1)) {\n                return;\n            }\n            var roots_1 = [];\n            return function (p) {\n                return p <= 0\n                    ? 0 : p >= 1\n                    ? 1\n                    : cubicRootAt(0, a_1, c_1, 1, p, roots_1) && cubicAt(0, b_1, d_1, 1, roots_1[0]);\n            };\n        }\n    }\n\n    var Clip = (function () {\n        function Clip(opts) {\n            this._inited = false;\n            this._startTime = 0;\n            this._pausedTime = 0;\n            this._paused = false;\n            this._life = opts.life || 1000;\n            this._delay = opts.delay || 0;\n            this.loop = opts.loop || false;\n            this.onframe = opts.onframe || noop;\n            this.ondestroy = opts.ondestroy || noop;\n            this.onrestart = opts.onrestart || noop;\n            opts.easing && this.setEasing(opts.easing);\n        }\n        Clip.prototype.step = function (globalTime, deltaTime) {\n            if (!this._inited) {\n                this._startTime = globalTime + this._delay;\n                this._inited = true;\n            }\n            if (this._paused) {\n                this._pausedTime += deltaTime;\n                return;\n            }\n            var life = this._life;\n            var elapsedTime = globalTime - this._startTime - this._pausedTime;\n            var percent = elapsedTime / life;\n            if (percent < 0) {\n                percent = 0;\n            }\n            percent = Math.min(percent, 1);\n            var easingFunc = this.easingFunc;\n            var schedule = easingFunc ? easingFunc(percent) : percent;\n            this.onframe(schedule);\n            if (percent === 1) {\n                if (this.loop) {\n                    var remainder = elapsedTime % life;\n                    this._startTime = globalTime - remainder;\n                    this._pausedTime = 0;\n                    this.onrestart();\n                }\n                else {\n                    return true;\n                }\n            }\n            return false;\n        };\n        Clip.prototype.pause = function () {\n            this._paused = true;\n        };\n        Clip.prototype.resume = function () {\n            this._paused = false;\n        };\n        Clip.prototype.setEasing = function (easing) {\n            this.easing = easing;\n            this.easingFunc = isFunction(easing)\n                ? easing\n                : easingFuncs[easing] || createCubicEasingFunc(easing);\n        };\n        return Clip;\n    }());\n\n    var Entry = (function () {\n        function Entry(val) {\n            this.value = val;\n        }\n        return Entry;\n    }());\n    var LinkedList = (function () {\n        function LinkedList() {\n            this._len = 0;\n        }\n        LinkedList.prototype.insert = function (val) {\n            var entry = new Entry(val);\n            this.insertEntry(entry);\n            return entry;\n        };\n        LinkedList.prototype.insertEntry = function (entry) {\n            if (!this.head) {\n                this.head = this.tail = entry;\n            }\n            else {\n                this.tail.next = entry;\n                entry.prev = this.tail;\n                entry.next = null;\n                this.tail = entry;\n            }\n            this._len++;\n        };\n        LinkedList.prototype.remove = function (entry) {\n            var prev = entry.prev;\n            var next = entry.next;\n            if (prev) {\n                prev.next = next;\n            }\n            else {\n                this.head = next;\n            }\n            if (next) {\n                next.prev = prev;\n            }\n            else {\n                this.tail = prev;\n            }\n            entry.next = entry.prev = null;\n            this._len--;\n        };\n        LinkedList.prototype.len = function () {\n            return this._len;\n        };\n        LinkedList.prototype.clear = function () {\n            this.head = this.tail = null;\n            this._len = 0;\n        };\n        return LinkedList;\n    }());\n    var LRU = (function () {\n        function LRU(maxSize) {\n            this._list = new LinkedList();\n            this._maxSize = 10;\n            this._map = {};\n            this._maxSize = maxSize;\n        }\n        LRU.prototype.put = function (key, value) {\n            var list = this._list;\n            var map = this._map;\n            var removed = null;\n            if (map[key] == null) {\n                var len = list.len();\n                var entry = this._lastRemovedEntry;\n                if (len >= this._maxSize && len > 0) {\n                    var leastUsedEntry = list.head;\n                    list.remove(leastUsedEntry);\n                    delete map[leastUsedEntry.key];\n                    removed = leastUsedEntry.value;\n                    this._lastRemovedEntry = leastUsedEntry;\n                }\n                if (entry) {\n                    entry.value = value;\n                }\n                else {\n                    entry = new Entry(value);\n                }\n                entry.key = key;\n                list.insertEntry(entry);\n                map[key] = entry;\n            }\n            return removed;\n        };\n        LRU.prototype.get = function (key) {\n            var entry = this._map[key];\n            var list = this._list;\n            if (entry != null) {\n                if (entry !== list.tail) {\n                    list.remove(entry);\n                    list.insertEntry(entry);\n                }\n                return entry.value;\n            }\n        };\n        LRU.prototype.clear = function () {\n            this._list.clear();\n            this._map = {};\n        };\n        LRU.prototype.len = function () {\n            return this._list.len();\n        };\n        return LRU;\n    }());\n\n    var kCSSColorTable = {\n        'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1],\n        'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1],\n        'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1],\n        'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1],\n        'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1],\n        'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1],\n        'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1],\n        'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1],\n        'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1],\n        'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1],\n        'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1],\n        'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1],\n        'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1],\n        'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1],\n        'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1],\n        'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1],\n        'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1],\n        'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1],\n        'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1],\n        'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1],\n        'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1],\n        'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1],\n        'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1],\n        'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1],\n        'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1],\n        'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1],\n        'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1],\n        'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1],\n        'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1],\n        'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1],\n        'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1],\n        'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1],\n        'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1],\n        'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1],\n        'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1],\n        'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1],\n        'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1],\n        'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1],\n        'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1],\n        'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1],\n        'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1],\n        'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1],\n        'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1],\n        'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1],\n        'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1],\n        'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1],\n        'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1],\n        'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1],\n        'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1],\n        'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1],\n        'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1],\n        'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1],\n        'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1],\n        'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1],\n        'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1],\n        'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1],\n        'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1],\n        'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1],\n        'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1],\n        'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1],\n        'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1],\n        'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1],\n        'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1],\n        'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1],\n        'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1],\n        'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1],\n        'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1],\n        'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1],\n        'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1],\n        'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1],\n        'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1],\n        'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1],\n        'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1],\n        'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1]\n    };\n    function clampCssByte(i) {\n        i = Math.round(i);\n        return i < 0 ? 0 : i > 255 ? 255 : i;\n    }\n    function clampCssAngle(i) {\n        i = Math.round(i);\n        return i < 0 ? 0 : i > 360 ? 360 : i;\n    }\n    function clampCssFloat(f) {\n        return f < 0 ? 0 : f > 1 ? 1 : f;\n    }\n    function parseCssInt(val) {\n        var str = val;\n        if (str.length && str.charAt(str.length - 1) === '%') {\n            return clampCssByte(parseFloat(str) / 100 * 255);\n        }\n        return clampCssByte(parseInt(str, 10));\n    }\n    function parseCssFloat(val) {\n        var str = val;\n        if (str.length && str.charAt(str.length - 1) === '%') {\n            return clampCssFloat(parseFloat(str) / 100);\n        }\n        return clampCssFloat(parseFloat(str));\n    }\n    function cssHueToRgb(m1, m2, h) {\n        if (h < 0) {\n            h += 1;\n        }\n        else if (h > 1) {\n            h -= 1;\n        }\n        if (h * 6 < 1) {\n            return m1 + (m2 - m1) * h * 6;\n        }\n        if (h * 2 < 1) {\n            return m2;\n        }\n        if (h * 3 < 2) {\n            return m1 + (m2 - m1) * (2 / 3 - h) * 6;\n        }\n        return m1;\n    }\n    function lerpNumber(a, b, p) {\n        return a + (b - a) * p;\n    }\n    function setRgba(out, r, g, b, a) {\n        out[0] = r;\n        out[1] = g;\n        out[2] = b;\n        out[3] = a;\n        return out;\n    }\n    function copyRgba(out, a) {\n        out[0] = a[0];\n        out[1] = a[1];\n        out[2] = a[2];\n        out[3] = a[3];\n        return out;\n    }\n    var colorCache = new LRU(20);\n    var lastRemovedArr = null;\n    function putToCache(colorStr, rgbaArr) {\n        if (lastRemovedArr) {\n            copyRgba(lastRemovedArr, rgbaArr);\n        }\n        lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));\n    }\n    function parse(colorStr, rgbaArr) {\n        if (!colorStr) {\n            return;\n        }\n        rgbaArr = rgbaArr || [];\n        var cached = colorCache.get(colorStr);\n        if (cached) {\n            return copyRgba(rgbaArr, cached);\n        }\n        colorStr = colorStr + '';\n        var str = colorStr.replace(/ /g, '').toLowerCase();\n        if (str in kCSSColorTable) {\n            copyRgba(rgbaArr, kCSSColorTable[str]);\n            putToCache(colorStr, rgbaArr);\n            return rgbaArr;\n        }\n        var strLen = str.length;\n        if (str.charAt(0) === '#') {\n            if (strLen === 4 || strLen === 5) {\n                var iv = parseInt(str.slice(1, 4), 16);\n                if (!(iv >= 0 && iv <= 0xfff)) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                setRgba(rgbaArr, ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), (iv & 0xf0) | ((iv & 0xf0) >> 4), (iv & 0xf) | ((iv & 0xf) << 4), strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            }\n            else if (strLen === 7 || strLen === 9) {\n                var iv = parseInt(str.slice(1, 7), 16);\n                if (!(iv >= 0 && iv <= 0xffffff)) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            }\n            return;\n        }\n        var op = str.indexOf('(');\n        var ep = str.indexOf(')');\n        if (op !== -1 && ep + 1 === strLen) {\n            var fname = str.substr(0, op);\n            var params = str.substr(op + 1, ep - (op + 1)).split(',');\n            var alpha = 1;\n            switch (fname) {\n                case 'rgba':\n                    if (params.length !== 4) {\n                        return params.length === 3\n                            ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1)\n                            : setRgba(rgbaArr, 0, 0, 0, 1);\n                    }\n                    alpha = parseCssFloat(params.pop());\n                case 'rgb':\n                    if (params.length >= 3) {\n                        setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), params.length === 3 ? alpha : parseCssFloat(params[3]));\n                        putToCache(colorStr, rgbaArr);\n                        return rgbaArr;\n                    }\n                    else {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                case 'hsla':\n                    if (params.length !== 4) {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                    params[3] = parseCssFloat(params[3]);\n                    hsla2rgba(params, rgbaArr);\n                    putToCache(colorStr, rgbaArr);\n                    return rgbaArr;\n                case 'hsl':\n                    if (params.length !== 3) {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                    hsla2rgba(params, rgbaArr);\n                    putToCache(colorStr, rgbaArr);\n                    return rgbaArr;\n                default:\n                    return;\n            }\n        }\n        setRgba(rgbaArr, 0, 0, 0, 1);\n        return;\n    }\n    function hsla2rgba(hsla, rgba) {\n        var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360;\n        var s = parseCssFloat(hsla[1]);\n        var l = parseCssFloat(hsla[2]);\n        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n        var m1 = l * 2 - m2;\n        rgba = rgba || [];\n        setRgba(rgba, clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), clampCssByte(cssHueToRgb(m1, m2, h) * 255), clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 1);\n        if (hsla.length === 4) {\n            rgba[3] = hsla[3];\n        }\n        return rgba;\n    }\n    function rgba2hsla(rgba) {\n        if (!rgba) {\n            return;\n        }\n        var R = rgba[0] / 255;\n        var G = rgba[1] / 255;\n        var B = rgba[2] / 255;\n        var vMin = Math.min(R, G, B);\n        var vMax = Math.max(R, G, B);\n        var delta = vMax - vMin;\n        var L = (vMax + vMin) / 2;\n        var H;\n        var S;\n        if (delta === 0) {\n            H = 0;\n            S = 0;\n        }\n        else {\n            if (L < 0.5) {\n                S = delta / (vMax + vMin);\n            }\n            else {\n                S = delta / (2 - vMax - vMin);\n            }\n            var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;\n            var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;\n            var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;\n            if (R === vMax) {\n                H = deltaB - deltaG;\n            }\n            else if (G === vMax) {\n                H = (1 / 3) + deltaR - deltaB;\n            }\n            else if (B === vMax) {\n                H = (2 / 3) + deltaG - deltaR;\n            }\n            if (H < 0) {\n                H += 1;\n            }\n            if (H > 1) {\n                H -= 1;\n            }\n        }\n        var hsla = [H * 360, S, L];\n        if (rgba[3] != null) {\n            hsla.push(rgba[3]);\n        }\n        return hsla;\n    }\n    function lift(color, level) {\n        var colorArr = parse(color);\n        if (colorArr) {\n            for (var i = 0; i < 3; i++) {\n                if (level < 0) {\n                    colorArr[i] = colorArr[i] * (1 - level) | 0;\n                }\n                else {\n                    colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;\n                }\n                if (colorArr[i] > 255) {\n                    colorArr[i] = 255;\n                }\n                else if (colorArr[i] < 0) {\n                    colorArr[i] = 0;\n                }\n            }\n            return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');\n        }\n    }\n    function toHex(color) {\n        var colorArr = parse(color);\n        if (colorArr) {\n            return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);\n        }\n    }\n    function fastLerp(normalizedValue, colors, out) {\n        if (!(colors && colors.length)\n            || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n            return;\n        }\n        out = out || [];\n        var value = normalizedValue * (colors.length - 1);\n        var leftIndex = Math.floor(value);\n        var rightIndex = Math.ceil(value);\n        var leftColor = colors[leftIndex];\n        var rightColor = colors[rightIndex];\n        var dv = value - leftIndex;\n        out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));\n        out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));\n        out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));\n        out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));\n        return out;\n    }\n    var fastMapToColor = fastLerp;\n    function lerp$1(normalizedValue, colors, fullOutput) {\n        if (!(colors && colors.length)\n            || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n            return;\n        }\n        var value = normalizedValue * (colors.length - 1);\n        var leftIndex = Math.floor(value);\n        var rightIndex = Math.ceil(value);\n        var leftColor = parse(colors[leftIndex]);\n        var rightColor = parse(colors[rightIndex]);\n        var dv = value - leftIndex;\n        var color = stringify([\n            clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),\n            clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),\n            clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),\n            clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))\n        ], 'rgba');\n        return fullOutput\n            ? {\n                color: color,\n                leftIndex: leftIndex,\n                rightIndex: rightIndex,\n                value: value\n            }\n            : color;\n    }\n    var mapToColor = lerp$1;\n    function modifyHSL(color, h, s, l) {\n        var colorArr = parse(color);\n        if (color) {\n            colorArr = rgba2hsla(colorArr);\n            h != null && (colorArr[0] = clampCssAngle(h));\n            s != null && (colorArr[1] = parseCssFloat(s));\n            l != null && (colorArr[2] = parseCssFloat(l));\n            return stringify(hsla2rgba(colorArr), 'rgba');\n        }\n    }\n    function modifyAlpha(color, alpha) {\n        var colorArr = parse(color);\n        if (colorArr && alpha != null) {\n            colorArr[3] = clampCssFloat(alpha);\n            return stringify(colorArr, 'rgba');\n        }\n    }\n    function stringify(arrColor, type) {\n        if (!arrColor || !arrColor.length) {\n            return;\n        }\n        var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];\n        if (type === 'rgba' || type === 'hsva' || type === 'hsla') {\n            colorStr += ',' + arrColor[3];\n        }\n        return type + '(' + colorStr + ')';\n    }\n    function lum(color, backgroundLum) {\n        var arr = parse(color);\n        return arr\n            ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255\n                + (1 - arr[3]) * backgroundLum\n            : 0;\n    }\n    function random() {\n        return stringify([\n            Math.round(Math.random() * 255),\n            Math.round(Math.random() * 255),\n            Math.round(Math.random() * 255)\n        ], 'rgb');\n    }\n\n    var color = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        parse: parse,\n        lift: lift,\n        toHex: toHex,\n        fastLerp: fastLerp,\n        fastMapToColor: fastMapToColor,\n        lerp: lerp$1,\n        mapToColor: mapToColor,\n        modifyHSL: modifyHSL,\n        modifyAlpha: modifyAlpha,\n        stringify: stringify,\n        lum: lum,\n        random: random\n    });\n\n    var mathRound = Math.round;\n    function normalizeColor(color) {\n        var opacity;\n        if (!color || color === 'transparent') {\n            color = 'none';\n        }\n        else if (typeof color === 'string' && color.indexOf('rgba') > -1) {\n            var arr = parse(color);\n            if (arr) {\n                color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')';\n                opacity = arr[3];\n            }\n        }\n        return {\n            color: color,\n            opacity: opacity == null ? 1 : opacity\n        };\n    }\n    var EPSILON$1 = 1e-4;\n    function isAroundZero$1(transform) {\n        return transform < EPSILON$1 && transform > -EPSILON$1;\n    }\n    function round3(transform) {\n        return mathRound(transform * 1e3) / 1e3;\n    }\n    function round4(transform) {\n        return mathRound(transform * 1e4) / 1e4;\n    }\n    function getMatrixStr(m) {\n        return 'matrix('\n            + round3(m[0]) + ','\n            + round3(m[1]) + ','\n            + round3(m[2]) + ','\n            + round3(m[3]) + ','\n            + round4(m[4]) + ','\n            + round4(m[5])\n            + ')';\n    }\n    var TEXT_ALIGN_TO_ANCHOR = {\n        left: 'start',\n        right: 'end',\n        center: 'middle',\n        middle: 'middle'\n    };\n    function adjustTextY(y, lineHeight, textBaseline) {\n        if (textBaseline === 'top') {\n            y += lineHeight / 2;\n        }\n        else if (textBaseline === 'bottom') {\n            y -= lineHeight / 2;\n        }\n        return y;\n    }\n    function hasShadow(style) {\n        return style\n            && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY);\n    }\n    function getShadowKey(displayable) {\n        var style = displayable.style;\n        var globalScale = displayable.getGlobalScale();\n        return [\n            style.shadowColor,\n            (style.shadowBlur || 0).toFixed(2),\n            (style.shadowOffsetX || 0).toFixed(2),\n            (style.shadowOffsetY || 0).toFixed(2),\n            globalScale[0],\n            globalScale[1]\n        ].join(',');\n    }\n    function isImagePattern(val) {\n        return val && (!!val.image);\n    }\n    function isSVGPattern(val) {\n        return val && (!!val.svgElement);\n    }\n    function isPattern(val) {\n        return isImagePattern(val) || isSVGPattern(val);\n    }\n    function isLinearGradient(val) {\n        return val.type === 'linear';\n    }\n    function isRadialGradient(val) {\n        return val.type === 'radial';\n    }\n    function isGradient(val) {\n        return val && (val.type === 'linear'\n            || val.type === 'radial');\n    }\n    function getIdURL(id) {\n        return \"url(#\" + id + \")\";\n    }\n    function getPathPrecision(el) {\n        var scale = el.getGlobalScale();\n        var size = Math.max(scale[0], scale[1]);\n        return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1);\n    }\n    function getSRTTransformString(transform) {\n        var x = transform.x || 0;\n        var y = transform.y || 0;\n        var rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE;\n        var scaleX = retrieve2(transform.scaleX, 1);\n        var scaleY = retrieve2(transform.scaleY, 1);\n        var skewX = transform.skewX || 0;\n        var skewY = transform.skewY || 0;\n        var res = [];\n        if (x || y) {\n            res.push(\"translate(\" + x + \"px,\" + y + \"px)\");\n        }\n        if (rotation) {\n            res.push(\"rotate(\" + rotation + \")\");\n        }\n        if (scaleX !== 1 || scaleY !== 1) {\n            res.push(\"scale(\" + scaleX + \",\" + scaleY + \")\");\n        }\n        if (skewX || skewY) {\n            res.push(\"skew(\" + mathRound(skewX * RADIAN_TO_DEGREE) + \"deg, \" + mathRound(skewY * RADIAN_TO_DEGREE) + \"deg)\");\n        }\n        return res.join(' ');\n    }\n    var encodeBase64 = (function () {\n        if (env.hasGlobalWindow && isFunction(window.btoa)) {\n            return function (str) {\n                return window.btoa(unescape(encodeURIComponent(str)));\n            };\n        }\n        if (typeof Buffer !== 'undefined') {\n            return function (str) {\n                return Buffer.from(str).toString('base64');\n            };\n        }\n        return function (str) {\n            if (\"development\" !== 'production') {\n                logError('Base64 isn\\'t natively supported in the current environment.');\n            }\n            return null;\n        };\n    })();\n\n    var arraySlice = Array.prototype.slice;\n    function interpolateNumber(p0, p1, percent) {\n        return (p1 - p0) * percent + p0;\n    }\n    function interpolate1DArray(out, p0, p1, percent) {\n        var len = p0.length;\n        for (var i = 0; i < len; i++) {\n            out[i] = interpolateNumber(p0[i], p1[i], percent);\n        }\n        return out;\n    }\n    function interpolate2DArray(out, p0, p1, percent) {\n        var len = p0.length;\n        var len2 = len && p0[0].length;\n        for (var i = 0; i < len; i++) {\n            if (!out[i]) {\n                out[i] = [];\n            }\n            for (var j = 0; j < len2; j++) {\n                out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent);\n            }\n        }\n        return out;\n    }\n    function add1DArray(out, p0, p1, sign) {\n        var len = p0.length;\n        for (var i = 0; i < len; i++) {\n            out[i] = p0[i] + p1[i] * sign;\n        }\n        return out;\n    }\n    function add2DArray(out, p0, p1, sign) {\n        var len = p0.length;\n        var len2 = len && p0[0].length;\n        for (var i = 0; i < len; i++) {\n            if (!out[i]) {\n                out[i] = [];\n            }\n            for (var j = 0; j < len2; j++) {\n                out[i][j] = p0[i][j] + p1[i][j] * sign;\n            }\n        }\n        return out;\n    }\n    function fillColorStops(val0, val1) {\n        var len0 = val0.length;\n        var len1 = val1.length;\n        var shorterArr = len0 > len1 ? val1 : val0;\n        var shorterLen = Math.min(len0, len1);\n        var last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 };\n        for (var i = shorterLen; i < Math.max(len0, len1); i++) {\n            shorterArr.push({\n                offset: last.offset,\n                color: last.color.slice()\n            });\n        }\n    }\n    function fillArray(val0, val1, arrDim) {\n        var arr0 = val0;\n        var arr1 = val1;\n        if (!arr0.push || !arr1.push) {\n            return;\n        }\n        var arr0Len = arr0.length;\n        var arr1Len = arr1.length;\n        if (arr0Len !== arr1Len) {\n            var isPreviousLarger = arr0Len > arr1Len;\n            if (isPreviousLarger) {\n                arr0.length = arr1Len;\n            }\n            else {\n                for (var i = arr0Len; i < arr1Len; i++) {\n                    arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]));\n                }\n            }\n        }\n        var len2 = arr0[0] && arr0[0].length;\n        for (var i = 0; i < arr0.length; i++) {\n            if (arrDim === 1) {\n                if (isNaN(arr0[i])) {\n                    arr0[i] = arr1[i];\n                }\n            }\n            else {\n                for (var j = 0; j < len2; j++) {\n                    if (isNaN(arr0[i][j])) {\n                        arr0[i][j] = arr1[i][j];\n                    }\n                }\n            }\n        }\n    }\n    function cloneValue(value) {\n        if (isArrayLike(value)) {\n            var len = value.length;\n            if (isArrayLike(value[0])) {\n                var ret = [];\n                for (var i = 0; i < len; i++) {\n                    ret.push(arraySlice.call(value[i]));\n                }\n                return ret;\n            }\n            return arraySlice.call(value);\n        }\n        return value;\n    }\n    function rgba2String(rgba) {\n        rgba[0] = Math.floor(rgba[0]) || 0;\n        rgba[1] = Math.floor(rgba[1]) || 0;\n        rgba[2] = Math.floor(rgba[2]) || 0;\n        rgba[3] = rgba[3] == null ? 1 : rgba[3];\n        return 'rgba(' + rgba.join(',') + ')';\n    }\n    function guessArrayDim(value) {\n        return isArrayLike(value && value[0]) ? 2 : 1;\n    }\n    var VALUE_TYPE_NUMBER = 0;\n    var VALUE_TYPE_1D_ARRAY = 1;\n    var VALUE_TYPE_2D_ARRAY = 2;\n    var VALUE_TYPE_COLOR = 3;\n    var VALUE_TYPE_LINEAR_GRADIENT = 4;\n    var VALUE_TYPE_RADIAL_GRADIENT = 5;\n    var VALUE_TYPE_UNKOWN = 6;\n    function isGradientValueType(valType) {\n        return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT;\n    }\n    function isArrayValueType(valType) {\n        return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY;\n    }\n    var tmpRgba = [0, 0, 0, 0];\n    var Track = (function () {\n        function Track(propName) {\n            this.keyframes = [];\n            this.discrete = false;\n            this._invalid = false;\n            this._needsSort = false;\n            this._lastFr = 0;\n            this._lastFrP = 0;\n            this.propName = propName;\n        }\n        Track.prototype.isFinished = function () {\n            return this._finished;\n        };\n        Track.prototype.setFinished = function () {\n            this._finished = true;\n            if (this._additiveTrack) {\n                this._additiveTrack.setFinished();\n            }\n        };\n        Track.prototype.needsAnimate = function () {\n            return this.keyframes.length >= 1;\n        };\n        Track.prototype.getAdditiveTrack = function () {\n            return this._additiveTrack;\n        };\n        Track.prototype.addKeyframe = function (time, rawValue, easing) {\n            this._needsSort = true;\n            var keyframes = this.keyframes;\n            var len = keyframes.length;\n            var discrete = false;\n            var valType = VALUE_TYPE_UNKOWN;\n            var value = rawValue;\n            if (isArrayLike(rawValue)) {\n                var arrayDim = guessArrayDim(rawValue);\n                valType = arrayDim;\n                if (arrayDim === 1 && !isNumber(rawValue[0])\n                    || arrayDim === 2 && !isNumber(rawValue[0][0])) {\n                    discrete = true;\n                }\n            }\n            else {\n                if (isNumber(rawValue) && !eqNaN(rawValue)) {\n                    valType = VALUE_TYPE_NUMBER;\n                }\n                else if (isString(rawValue)) {\n                    if (!isNaN(+rawValue)) {\n                        valType = VALUE_TYPE_NUMBER;\n                    }\n                    else {\n                        var colorArray = parse(rawValue);\n                        if (colorArray) {\n                            value = colorArray;\n                            valType = VALUE_TYPE_COLOR;\n                        }\n                    }\n                }\n                else if (isGradientObject(rawValue)) {\n                    var parsedGradient = extend({}, value);\n                    parsedGradient.colorStops = map(rawValue.colorStops, function (colorStop) { return ({\n                        offset: colorStop.offset,\n                        color: parse(colorStop.color)\n                    }); });\n                    if (isLinearGradient(rawValue)) {\n                        valType = VALUE_TYPE_LINEAR_GRADIENT;\n                    }\n                    else if (isRadialGradient(rawValue)) {\n                        valType = VALUE_TYPE_RADIAL_GRADIENT;\n                    }\n                    value = parsedGradient;\n                }\n            }\n            if (len === 0) {\n                this.valType = valType;\n            }\n            else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) {\n                discrete = true;\n            }\n            this.discrete = this.discrete || discrete;\n            var kf = {\n                time: time,\n                value: value,\n                rawValue: rawValue,\n                percent: 0\n            };\n            if (easing) {\n                kf.easing = easing;\n                kf.easingFunc = isFunction(easing)\n                    ? easing\n                    : easingFuncs[easing] || createCubicEasingFunc(easing);\n            }\n            keyframes.push(kf);\n            return kf;\n        };\n        Track.prototype.prepare = function (maxTime, additiveTrack) {\n            var kfs = this.keyframes;\n            if (this._needsSort) {\n                kfs.sort(function (a, b) {\n                    return a.time - b.time;\n                });\n            }\n            var valType = this.valType;\n            var kfsLen = kfs.length;\n            var lastKf = kfs[kfsLen - 1];\n            var isDiscrete = this.discrete;\n            var isArr = isArrayValueType(valType);\n            var isGradient = isGradientValueType(valType);\n            for (var i = 0; i < kfsLen; i++) {\n                var kf = kfs[i];\n                var value = kf.value;\n                var lastValue = lastKf.value;\n                kf.percent = kf.time / maxTime;\n                if (!isDiscrete) {\n                    if (isArr && i !== kfsLen - 1) {\n                        fillArray(value, lastValue, valType);\n                    }\n                    else if (isGradient) {\n                        fillColorStops(value.colorStops, lastValue.colorStops);\n                    }\n                }\n            }\n            if (!isDiscrete\n                && valType !== VALUE_TYPE_RADIAL_GRADIENT\n                && additiveTrack\n                && this.needsAnimate()\n                && additiveTrack.needsAnimate()\n                && valType === additiveTrack.valType\n                && !additiveTrack._finished) {\n                this._additiveTrack = additiveTrack;\n                var startValue = kfs[0].value;\n                for (var i = 0; i < kfsLen; i++) {\n                    if (valType === VALUE_TYPE_NUMBER) {\n                        kfs[i].additiveValue = kfs[i].value - startValue;\n                    }\n                    else if (valType === VALUE_TYPE_COLOR) {\n                        kfs[i].additiveValue =\n                            add1DArray([], kfs[i].value, startValue, -1);\n                    }\n                    else if (isArrayValueType(valType)) {\n                        kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY\n                            ? add1DArray([], kfs[i].value, startValue, -1)\n                            : add2DArray([], kfs[i].value, startValue, -1);\n                    }\n                }\n            }\n        };\n        Track.prototype.step = function (target, percent) {\n            if (this._finished) {\n                return;\n            }\n            if (this._additiveTrack && this._additiveTrack._finished) {\n                this._additiveTrack = null;\n            }\n            var isAdditive = this._additiveTrack != null;\n            var valueKey = isAdditive ? 'additiveValue' : 'value';\n            var valType = this.valType;\n            var keyframes = this.keyframes;\n            var kfsNum = keyframes.length;\n            var propName = this.propName;\n            var isValueColor = valType === VALUE_TYPE_COLOR;\n            var frameIdx;\n            var lastFrame = this._lastFr;\n            var mathMin = Math.min;\n            var frame;\n            var nextFrame;\n            if (kfsNum === 1) {\n                frame = nextFrame = keyframes[0];\n            }\n            else {\n                if (percent < 0) {\n                    frameIdx = 0;\n                }\n                else if (percent < this._lastFrP) {\n                    var start = mathMin(lastFrame + 1, kfsNum - 1);\n                    for (frameIdx = start; frameIdx >= 0; frameIdx--) {\n                        if (keyframes[frameIdx].percent <= percent) {\n                            break;\n                        }\n                    }\n                    frameIdx = mathMin(frameIdx, kfsNum - 2);\n                }\n                else {\n                    for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) {\n                        if (keyframes[frameIdx].percent > percent) {\n                            break;\n                        }\n                    }\n                    frameIdx = mathMin(frameIdx - 1, kfsNum - 2);\n                }\n                nextFrame = keyframes[frameIdx + 1];\n                frame = keyframes[frameIdx];\n            }\n            if (!(frame && nextFrame)) {\n                return;\n            }\n            this._lastFr = frameIdx;\n            this._lastFrP = percent;\n            var interval = (nextFrame.percent - frame.percent);\n            var w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1);\n            if (nextFrame.easingFunc) {\n                w = nextFrame.easingFunc(w);\n            }\n            var targetArr = isAdditive ? this._additiveValue\n                : (isValueColor ? tmpRgba : target[propName]);\n            if ((isArrayValueType(valType) || isValueColor) && !targetArr) {\n                targetArr = this._additiveValue = [];\n            }\n            if (this.discrete) {\n                target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue;\n            }\n            else if (isArrayValueType(valType)) {\n                valType === VALUE_TYPE_1D_ARRAY\n                    ? interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w)\n                    : interpolate2DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n            }\n            else if (isGradientValueType(valType)) {\n                var val = frame[valueKey];\n                var nextVal_1 = nextFrame[valueKey];\n                var isLinearGradient_1 = valType === VALUE_TYPE_LINEAR_GRADIENT;\n                target[propName] = {\n                    type: isLinearGradient_1 ? 'linear' : 'radial',\n                    x: interpolateNumber(val.x, nextVal_1.x, w),\n                    y: interpolateNumber(val.y, nextVal_1.y, w),\n                    colorStops: map(val.colorStops, function (colorStop, idx) {\n                        var nextColorStop = nextVal_1.colorStops[idx];\n                        return {\n                            offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w),\n                            color: rgba2String(interpolate1DArray([], colorStop.color, nextColorStop.color, w))\n                        };\n                    }),\n                    global: nextVal_1.global\n                };\n                if (isLinearGradient_1) {\n                    target[propName].x2 = interpolateNumber(val.x2, nextVal_1.x2, w);\n                    target[propName].y2 = interpolateNumber(val.y2, nextVal_1.y2, w);\n                }\n                else {\n                    target[propName].r = interpolateNumber(val.r, nextVal_1.r, w);\n                }\n            }\n            else if (isValueColor) {\n                interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n                if (!isAdditive) {\n                    target[propName] = rgba2String(targetArr);\n                }\n            }\n            else {\n                var value = interpolateNumber(frame[valueKey], nextFrame[valueKey], w);\n                if (isAdditive) {\n                    this._additiveValue = value;\n                }\n                else {\n                    target[propName] = value;\n                }\n            }\n            if (isAdditive) {\n                this._addToTarget(target);\n            }\n        };\n        Track.prototype._addToTarget = function (target) {\n            var valType = this.valType;\n            var propName = this.propName;\n            var additiveValue = this._additiveValue;\n            if (valType === VALUE_TYPE_NUMBER) {\n                target[propName] = target[propName] + additiveValue;\n            }\n            else if (valType === VALUE_TYPE_COLOR) {\n                parse(target[propName], tmpRgba);\n                add1DArray(tmpRgba, tmpRgba, additiveValue, 1);\n                target[propName] = rgba2String(tmpRgba);\n            }\n            else if (valType === VALUE_TYPE_1D_ARRAY) {\n                add1DArray(target[propName], target[propName], additiveValue, 1);\n            }\n            else if (valType === VALUE_TYPE_2D_ARRAY) {\n                add2DArray(target[propName], target[propName], additiveValue, 1);\n            }\n        };\n        return Track;\n    }());\n    var Animator = (function () {\n        function Animator(target, loop, allowDiscreteAnimation, additiveTo) {\n            this._tracks = {};\n            this._trackKeys = [];\n            this._maxTime = 0;\n            this._started = 0;\n            this._clip = null;\n            this._target = target;\n            this._loop = loop;\n            if (loop && additiveTo) {\n                logError('Can\\' use additive animation on looped animation.');\n                return;\n            }\n            this._additiveAnimators = additiveTo;\n            this._allowDiscrete = allowDiscreteAnimation;\n        }\n        Animator.prototype.getMaxTime = function () {\n            return this._maxTime;\n        };\n        Animator.prototype.getDelay = function () {\n            return this._delay;\n        };\n        Animator.prototype.getLoop = function () {\n            return this._loop;\n        };\n        Animator.prototype.getTarget = function () {\n            return this._target;\n        };\n        Animator.prototype.changeTarget = function (target) {\n            this._target = target;\n        };\n        Animator.prototype.when = function (time, props, easing) {\n            return this.whenWithKeys(time, props, keys(props), easing);\n        };\n        Animator.prototype.whenWithKeys = function (time, props, propNames, easing) {\n            var tracks = this._tracks;\n            for (var i = 0; i < propNames.length; i++) {\n                var propName = propNames[i];\n                var track = tracks[propName];\n                if (!track) {\n                    track = tracks[propName] = new Track(propName);\n                    var initialValue = void 0;\n                    var additiveTrack = this._getAdditiveTrack(propName);\n                    if (additiveTrack) {\n                        var addtiveTrackKfs = additiveTrack.keyframes;\n                        var lastFinalKf = addtiveTrackKfs[addtiveTrackKfs.length - 1];\n                        initialValue = lastFinalKf && lastFinalKf.value;\n                        if (additiveTrack.valType === VALUE_TYPE_COLOR && initialValue) {\n                            initialValue = rgba2String(initialValue);\n                        }\n                    }\n                    else {\n                        initialValue = this._target[propName];\n                    }\n                    if (initialValue == null) {\n                        continue;\n                    }\n                    if (time > 0) {\n                        track.addKeyframe(0, cloneValue(initialValue), easing);\n                    }\n                    this._trackKeys.push(propName);\n                }\n                track.addKeyframe(time, cloneValue(props[propName]), easing);\n            }\n            this._maxTime = Math.max(this._maxTime, time);\n            return this;\n        };\n        Animator.prototype.pause = function () {\n            this._clip.pause();\n            this._paused = true;\n        };\n        Animator.prototype.resume = function () {\n            this._clip.resume();\n            this._paused = false;\n        };\n        Animator.prototype.isPaused = function () {\n            return !!this._paused;\n        };\n        Animator.prototype.duration = function (duration) {\n            this._maxTime = duration;\n            this._force = true;\n            return this;\n        };\n        Animator.prototype._doneCallback = function () {\n            this._setTracksFinished();\n            this._clip = null;\n            var doneList = this._doneCbs;\n            if (doneList) {\n                var len = doneList.length;\n                for (var i = 0; i < len; i++) {\n                    doneList[i].call(this);\n                }\n            }\n        };\n        Animator.prototype._abortedCallback = function () {\n            this._setTracksFinished();\n            var animation = this.animation;\n            var abortedList = this._abortedCbs;\n            if (animation) {\n                animation.removeClip(this._clip);\n            }\n            this._clip = null;\n            if (abortedList) {\n                for (var i = 0; i < abortedList.length; i++) {\n                    abortedList[i].call(this);\n                }\n            }\n        };\n        Animator.prototype._setTracksFinished = function () {\n            var tracks = this._tracks;\n            var tracksKeys = this._trackKeys;\n            for (var i = 0; i < tracksKeys.length; i++) {\n                tracks[tracksKeys[i]].setFinished();\n            }\n        };\n        Animator.prototype._getAdditiveTrack = function (trackName) {\n            var additiveTrack;\n            var additiveAnimators = this._additiveAnimators;\n            if (additiveAnimators) {\n                for (var i = 0; i < additiveAnimators.length; i++) {\n                    var track = additiveAnimators[i].getTrack(trackName);\n                    if (track) {\n                        additiveTrack = track;\n                    }\n                }\n            }\n            return additiveTrack;\n        };\n        Animator.prototype.start = function (easing) {\n            if (this._started > 0) {\n                return;\n            }\n            this._started = 1;\n            var self = this;\n            var tracks = [];\n            var maxTime = this._maxTime || 0;\n            for (var i = 0; i < this._trackKeys.length; i++) {\n                var propName = this._trackKeys[i];\n                var track = this._tracks[propName];\n                var additiveTrack = this._getAdditiveTrack(propName);\n                var kfs = track.keyframes;\n                var kfsNum = kfs.length;\n                track.prepare(maxTime, additiveTrack);\n                if (track.needsAnimate()) {\n                    if (!this._allowDiscrete && track.discrete) {\n                        var lastKf = kfs[kfsNum - 1];\n                        if (lastKf) {\n                            self._target[track.propName] = lastKf.rawValue;\n                        }\n                        track.setFinished();\n                    }\n                    else {\n                        tracks.push(track);\n                    }\n                }\n            }\n            if (tracks.length || this._force) {\n                var clip = new Clip({\n                    life: maxTime,\n                    loop: this._loop,\n                    delay: this._delay || 0,\n                    onframe: function (percent) {\n                        self._started = 2;\n                        var additiveAnimators = self._additiveAnimators;\n                        if (additiveAnimators) {\n                            var stillHasAdditiveAnimator = false;\n                            for (var i = 0; i < additiveAnimators.length; i++) {\n                                if (additiveAnimators[i]._clip) {\n                                    stillHasAdditiveAnimator = true;\n                                    break;\n                                }\n                            }\n                            if (!stillHasAdditiveAnimator) {\n                                self._additiveAnimators = null;\n                            }\n                        }\n                        for (var i = 0; i < tracks.length; i++) {\n                            tracks[i].step(self._target, percent);\n                        }\n                        var onframeList = self._onframeCbs;\n                        if (onframeList) {\n                            for (var i = 0; i < onframeList.length; i++) {\n                                onframeList[i](self._target, percent);\n                            }\n                        }\n                    },\n                    ondestroy: function () {\n                        self._doneCallback();\n                    }\n                });\n                this._clip = clip;\n                if (this.animation) {\n                    this.animation.addClip(clip);\n                }\n                if (easing) {\n                    clip.setEasing(easing);\n                }\n            }\n            else {\n                this._doneCallback();\n            }\n            return this;\n        };\n        Animator.prototype.stop = function (forwardToLast) {\n            if (!this._clip) {\n                return;\n            }\n            var clip = this._clip;\n            if (forwardToLast) {\n                clip.onframe(1);\n            }\n            this._abortedCallback();\n        };\n        Animator.prototype.delay = function (time) {\n            this._delay = time;\n            return this;\n        };\n        Animator.prototype.during = function (cb) {\n            if (cb) {\n                if (!this._onframeCbs) {\n                    this._onframeCbs = [];\n                }\n                this._onframeCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.done = function (cb) {\n            if (cb) {\n                if (!this._doneCbs) {\n                    this._doneCbs = [];\n                }\n                this._doneCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.aborted = function (cb) {\n            if (cb) {\n                if (!this._abortedCbs) {\n                    this._abortedCbs = [];\n                }\n                this._abortedCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.getClip = function () {\n            return this._clip;\n        };\n        Animator.prototype.getTrack = function (propName) {\n            return this._tracks[propName];\n        };\n        Animator.prototype.getTracks = function () {\n            var _this = this;\n            return map(this._trackKeys, function (key) { return _this._tracks[key]; });\n        };\n        Animator.prototype.stopTracks = function (propNames, forwardToLast) {\n            if (!propNames.length || !this._clip) {\n                return true;\n            }\n            var tracks = this._tracks;\n            var tracksKeys = this._trackKeys;\n            for (var i = 0; i < propNames.length; i++) {\n                var track = tracks[propNames[i]];\n                if (track && !track.isFinished()) {\n                    if (forwardToLast) {\n                        track.step(this._target, 1);\n                    }\n                    else if (this._started === 1) {\n                        track.step(this._target, 0);\n                    }\n                    track.setFinished();\n                }\n            }\n            var allAborted = true;\n            for (var i = 0; i < tracksKeys.length; i++) {\n                if (!tracks[tracksKeys[i]].isFinished()) {\n                    allAborted = false;\n                    break;\n                }\n            }\n            if (allAborted) {\n                this._abortedCallback();\n            }\n            return allAborted;\n        };\n        Animator.prototype.saveTo = function (target, trackKeys, firstOrLast) {\n            if (!target) {\n                return;\n            }\n            trackKeys = trackKeys || this._trackKeys;\n            for (var i = 0; i < trackKeys.length; i++) {\n                var propName = trackKeys[i];\n                var track = this._tracks[propName];\n                if (!track || track.isFinished()) {\n                    continue;\n                }\n                var kfs = track.keyframes;\n                var kf = kfs[firstOrLast ? 0 : kfs.length - 1];\n                if (kf) {\n                    target[propName] = cloneValue(kf.rawValue);\n                }\n            }\n        };\n        Animator.prototype.__changeFinalValue = function (finalProps, trackKeys) {\n            trackKeys = trackKeys || keys(finalProps);\n            for (var i = 0; i < trackKeys.length; i++) {\n                var propName = trackKeys[i];\n                var track = this._tracks[propName];\n                if (!track) {\n                    continue;\n                }\n                var kfs = track.keyframes;\n                if (kfs.length > 1) {\n                    var lastKf = kfs.pop();\n                    track.addKeyframe(lastKf.time, finalProps[propName]);\n                    track.prepare(this._maxTime, track.getAdditiveTrack());\n                }\n            }\n        };\n        return Animator;\n    }());\n\n    function getTime() {\n        return new Date().getTime();\n    }\n    var Animation = (function (_super) {\n        __extends(Animation, _super);\n        function Animation(opts) {\n            var _this = _super.call(this) || this;\n            _this._running = false;\n            _this._time = 0;\n            _this._pausedTime = 0;\n            _this._pauseStart = 0;\n            _this._paused = false;\n            opts = opts || {};\n            _this.stage = opts.stage || {};\n            return _this;\n        }\n        Animation.prototype.addClip = function (clip) {\n            if (clip.animation) {\n                this.removeClip(clip);\n            }\n            if (!this._head) {\n                this._head = this._tail = clip;\n            }\n            else {\n                this._tail.next = clip;\n                clip.prev = this._tail;\n                clip.next = null;\n                this._tail = clip;\n            }\n            clip.animation = this;\n        };\n        Animation.prototype.addAnimator = function (animator) {\n            animator.animation = this;\n            var clip = animator.getClip();\n            if (clip) {\n                this.addClip(clip);\n            }\n        };\n        Animation.prototype.removeClip = function (clip) {\n            if (!clip.animation) {\n                return;\n            }\n            var prev = clip.prev;\n            var next = clip.next;\n            if (prev) {\n                prev.next = next;\n            }\n            else {\n                this._head = next;\n            }\n            if (next) {\n                next.prev = prev;\n            }\n            else {\n                this._tail = prev;\n            }\n            clip.next = clip.prev = clip.animation = null;\n        };\n        Animation.prototype.removeAnimator = function (animator) {\n            var clip = animator.getClip();\n            if (clip) {\n                this.removeClip(clip);\n            }\n            animator.animation = null;\n        };\n        Animation.prototype.update = function (notTriggerFrameAndStageUpdate) {\n            var time = getTime() - this._pausedTime;\n            var delta = time - this._time;\n            var clip = this._head;\n            while (clip) {\n                var nextClip = clip.next;\n                var finished = clip.step(time, delta);\n                if (finished) {\n                    clip.ondestroy();\n                    this.removeClip(clip);\n                    clip = nextClip;\n                }\n                else {\n                    clip = nextClip;\n                }\n            }\n            this._time = time;\n            if (!notTriggerFrameAndStageUpdate) {\n                this.trigger('frame', delta);\n                this.stage.update && this.stage.update();\n            }\n        };\n        Animation.prototype._startLoop = function () {\n            var self = this;\n            this._running = true;\n            function step() {\n                if (self._running) {\n                    requestAnimationFrame$1(step);\n                    !self._paused && self.update();\n                }\n            }\n            requestAnimationFrame$1(step);\n        };\n        Animation.prototype.start = function () {\n            if (this._running) {\n                return;\n            }\n            this._time = getTime();\n            this._pausedTime = 0;\n            this._startLoop();\n        };\n        Animation.prototype.stop = function () {\n            this._running = false;\n        };\n        Animation.prototype.pause = function () {\n            if (!this._paused) {\n                this._pauseStart = getTime();\n                this._paused = true;\n            }\n        };\n        Animation.prototype.resume = function () {\n            if (this._paused) {\n                this._pausedTime += getTime() - this._pauseStart;\n                this._paused = false;\n            }\n        };\n        Animation.prototype.clear = function () {\n            var clip = this._head;\n            while (clip) {\n                var nextClip = clip.next;\n                clip.prev = clip.next = clip.animation = null;\n                clip = nextClip;\n            }\n            this._head = this._tail = null;\n        };\n        Animation.prototype.isFinished = function () {\n            return this._head == null;\n        };\n        Animation.prototype.animate = function (target, options) {\n            options = options || {};\n            this.start();\n            var animator = new Animator(target, options.loop);\n            this.addAnimator(animator);\n            return animator;\n        };\n        return Animation;\n    }(Eventful));\n\n    var TOUCH_CLICK_DELAY = 300;\n    var globalEventSupported = env.domSupported;\n    var localNativeListenerNames = (function () {\n        var mouseHandlerNames = [\n            'click', 'dblclick', 'mousewheel', 'wheel', 'mouseout',\n            'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n        ];\n        var touchHandlerNames = [\n            'touchstart', 'touchend', 'touchmove'\n        ];\n        var pointerEventNameMap = {\n            pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1\n        };\n        var pointerHandlerNames = map(mouseHandlerNames, function (name) {\n            var nm = name.replace('mouse', 'pointer');\n            return pointerEventNameMap.hasOwnProperty(nm) ? nm : name;\n        });\n        return {\n            mouse: mouseHandlerNames,\n            touch: touchHandlerNames,\n            pointer: pointerHandlerNames\n        };\n    })();\n    var globalNativeListenerNames = {\n        mouse: ['mousemove', 'mouseup'],\n        pointer: ['pointermove', 'pointerup']\n    };\n    var wheelEventSupported = false;\n    function isPointerFromTouch(event) {\n        var pointerType = event.pointerType;\n        return pointerType === 'pen' || pointerType === 'touch';\n    }\n    function setTouchTimer(scope) {\n        scope.touching = true;\n        if (scope.touchTimer != null) {\n            clearTimeout(scope.touchTimer);\n            scope.touchTimer = null;\n        }\n        scope.touchTimer = setTimeout(function () {\n            scope.touching = false;\n            scope.touchTimer = null;\n        }, 700);\n    }\n    function markTouch(event) {\n        event && (event.zrByTouch = true);\n    }\n    function normalizeGlobalEvent(instance, event) {\n        return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true);\n    }\n    function isLocalEl(instance, el) {\n        var elTmp = el;\n        var isLocal = false;\n        while (elTmp && elTmp.nodeType !== 9\n            && !(isLocal = elTmp.domBelongToZr\n                || (elTmp !== el && elTmp === instance.painterRoot))) {\n            elTmp = elTmp.parentNode;\n        }\n        return isLocal;\n    }\n    var FakeGlobalEvent = (function () {\n        function FakeGlobalEvent(instance, event) {\n            this.stopPropagation = noop;\n            this.stopImmediatePropagation = noop;\n            this.preventDefault = noop;\n            this.type = event.type;\n            this.target = this.currentTarget = instance.dom;\n            this.pointerType = event.pointerType;\n            this.clientX = event.clientX;\n            this.clientY = event.clientY;\n        }\n        return FakeGlobalEvent;\n    }());\n    var localDOMHandlers = {\n        mousedown: function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.__mayPointerCapture = [event.zrX, event.zrY];\n            this.trigger('mousedown', event);\n        },\n        mousemove: function (event) {\n            event = normalizeEvent(this.dom, event);\n            var downPoint = this.__mayPointerCapture;\n            if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) {\n                this.__togglePointerCapture(true);\n            }\n            this.trigger('mousemove', event);\n        },\n        mouseup: function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.__togglePointerCapture(false);\n            this.trigger('mouseup', event);\n        },\n        mouseout: function (event) {\n            event = normalizeEvent(this.dom, event);\n            var element = event.toElement || event.relatedTarget;\n            if (!isLocalEl(this, element)) {\n                if (this.__pointerCapturing) {\n                    event.zrEventControl = 'no_globalout';\n                }\n                this.trigger('mouseout', event);\n            }\n        },\n        wheel: function (event) {\n            wheelEventSupported = true;\n            event = normalizeEvent(this.dom, event);\n            this.trigger('mousewheel', event);\n        },\n        mousewheel: function (event) {\n            if (wheelEventSupported) {\n                return;\n            }\n            event = normalizeEvent(this.dom, event);\n            this.trigger('mousewheel', event);\n        },\n        touchstart: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.__lastTouchMoment = new Date();\n            this.handler.processGesture(event, 'start');\n            localDOMHandlers.mousemove.call(this, event);\n            localDOMHandlers.mousedown.call(this, event);\n        },\n        touchmove: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.handler.processGesture(event, 'change');\n            localDOMHandlers.mousemove.call(this, event);\n        },\n        touchend: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.handler.processGesture(event, 'end');\n            localDOMHandlers.mouseup.call(this, event);\n            if (+new Date() - (+this.__lastTouchMoment) < TOUCH_CLICK_DELAY) {\n                localDOMHandlers.click.call(this, event);\n            }\n        },\n        pointerdown: function (event) {\n            localDOMHandlers.mousedown.call(this, event);\n        },\n        pointermove: function (event) {\n            if (!isPointerFromTouch(event)) {\n                localDOMHandlers.mousemove.call(this, event);\n            }\n        },\n        pointerup: function (event) {\n            localDOMHandlers.mouseup.call(this, event);\n        },\n        pointerout: function (event) {\n            if (!isPointerFromTouch(event)) {\n                localDOMHandlers.mouseout.call(this, event);\n            }\n        }\n    };\n    each(['click', 'dblclick', 'contextmenu'], function (name) {\n        localDOMHandlers[name] = function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.trigger(name, event);\n        };\n    });\n    var globalDOMHandlers = {\n        pointermove: function (event) {\n            if (!isPointerFromTouch(event)) {\n                globalDOMHandlers.mousemove.call(this, event);\n            }\n        },\n        pointerup: function (event) {\n            globalDOMHandlers.mouseup.call(this, event);\n        },\n        mousemove: function (event) {\n            this.trigger('mousemove', event);\n        },\n        mouseup: function (event) {\n            var pointerCaptureReleasing = this.__pointerCapturing;\n            this.__togglePointerCapture(false);\n            this.trigger('mouseup', event);\n            if (pointerCaptureReleasing) {\n                event.zrEventControl = 'only_globalout';\n                this.trigger('mouseout', event);\n            }\n        }\n    };\n    function mountLocalDOMEventListeners(instance, scope) {\n        var domHandlers = scope.domHandlers;\n        if (env.pointerEventsSupported) {\n            each(localNativeListenerNames.pointer, function (nativeEventName) {\n                mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                    domHandlers[nativeEventName].call(instance, event);\n                });\n            });\n        }\n        else {\n            if (env.touchEventsSupported) {\n                each(localNativeListenerNames.touch, function (nativeEventName) {\n                    mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                        domHandlers[nativeEventName].call(instance, event);\n                        setTouchTimer(scope);\n                    });\n                });\n            }\n            each(localNativeListenerNames.mouse, function (nativeEventName) {\n                mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                    event = getNativeEvent(event);\n                    if (!scope.touching) {\n                        domHandlers[nativeEventName].call(instance, event);\n                    }\n                });\n            });\n        }\n    }\n    function mountGlobalDOMEventListeners(instance, scope) {\n        if (env.pointerEventsSupported) {\n            each(globalNativeListenerNames.pointer, mount);\n        }\n        else if (!env.touchEventsSupported) {\n            each(globalNativeListenerNames.mouse, mount);\n        }\n        function mount(nativeEventName) {\n            function nativeEventListener(event) {\n                event = getNativeEvent(event);\n                if (!isLocalEl(instance, event.target)) {\n                    event = normalizeGlobalEvent(instance, event);\n                    scope.domHandlers[nativeEventName].call(instance, event);\n                }\n            }\n            mountSingleDOMEventListener(scope, nativeEventName, nativeEventListener, { capture: true });\n        }\n    }\n    function mountSingleDOMEventListener(scope, nativeEventName, listener, opt) {\n        scope.mounted[nativeEventName] = listener;\n        scope.listenerOpts[nativeEventName] = opt;\n        addEventListener(scope.domTarget, nativeEventName, listener, opt);\n    }\n    function unmountDOMEventListeners(scope) {\n        var mounted = scope.mounted;\n        for (var nativeEventName in mounted) {\n            if (mounted.hasOwnProperty(nativeEventName)) {\n                removeEventListener(scope.domTarget, nativeEventName, mounted[nativeEventName], scope.listenerOpts[nativeEventName]);\n            }\n        }\n        scope.mounted = {};\n    }\n    var DOMHandlerScope = (function () {\n        function DOMHandlerScope(domTarget, domHandlers) {\n            this.mounted = {};\n            this.listenerOpts = {};\n            this.touching = false;\n            this.domTarget = domTarget;\n            this.domHandlers = domHandlers;\n        }\n        return DOMHandlerScope;\n    }());\n    var HandlerDomProxy = (function (_super) {\n        __extends(HandlerDomProxy, _super);\n        function HandlerDomProxy(dom, painterRoot) {\n            var _this = _super.call(this) || this;\n            _this.__pointerCapturing = false;\n            _this.dom = dom;\n            _this.painterRoot = painterRoot;\n            _this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers);\n            if (globalEventSupported) {\n                _this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers);\n            }\n            mountLocalDOMEventListeners(_this, _this._localHandlerScope);\n            return _this;\n        }\n        HandlerDomProxy.prototype.dispose = function () {\n            unmountDOMEventListeners(this._localHandlerScope);\n            if (globalEventSupported) {\n                unmountDOMEventListeners(this._globalHandlerScope);\n            }\n        };\n        HandlerDomProxy.prototype.setCursor = function (cursorStyle) {\n            this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');\n        };\n        HandlerDomProxy.prototype.__togglePointerCapture = function (isPointerCapturing) {\n            this.__mayPointerCapture = null;\n            if (globalEventSupported\n                && ((+this.__pointerCapturing) ^ (+isPointerCapturing))) {\n                this.__pointerCapturing = isPointerCapturing;\n                var globalHandlerScope = this._globalHandlerScope;\n                isPointerCapturing\n                    ? mountGlobalDOMEventListeners(this, globalHandlerScope)\n                    : unmountDOMEventListeners(globalHandlerScope);\n            }\n        };\n        return HandlerDomProxy;\n    }(Eventful));\n\n    var dpr = 1;\n    if (env.hasGlobalWindow) {\n        dpr = Math.max(window.devicePixelRatio\n            || (window.screen && window.screen.deviceXDPI / window.screen.logicalXDPI)\n            || 1, 1);\n    }\n    var devicePixelRatio = dpr;\n    var DARK_MODE_THRESHOLD = 0.4;\n    var DARK_LABEL_COLOR = '#333';\n    var LIGHT_LABEL_COLOR = '#ccc';\n    var LIGHTER_LABEL_COLOR = '#eee';\n\n    var mIdentity = identity;\n    var EPSILON$2 = 5e-5;\n    function isNotAroundZero$1(val) {\n        return val > EPSILON$2 || val < -EPSILON$2;\n    }\n    var scaleTmp = [];\n    var tmpTransform = [];\n    var originTransform = create$1();\n    var abs = Math.abs;\n    var Transformable = (function () {\n        function Transformable() {\n        }\n        Transformable.prototype.getLocalTransform = function (m) {\n            return Transformable.getLocalTransform(this, m);\n        };\n        Transformable.prototype.setPosition = function (arr) {\n            this.x = arr[0];\n            this.y = arr[1];\n        };\n        Transformable.prototype.setScale = function (arr) {\n            this.scaleX = arr[0];\n            this.scaleY = arr[1];\n        };\n        Transformable.prototype.setSkew = function (arr) {\n            this.skewX = arr[0];\n            this.skewY = arr[1];\n        };\n        Transformable.prototype.setOrigin = function (arr) {\n            this.originX = arr[0];\n            this.originY = arr[1];\n        };\n        Transformable.prototype.needLocalTransform = function () {\n            return isNotAroundZero$1(this.rotation)\n                || isNotAroundZero$1(this.x)\n                || isNotAroundZero$1(this.y)\n                || isNotAroundZero$1(this.scaleX - 1)\n                || isNotAroundZero$1(this.scaleY - 1)\n                || isNotAroundZero$1(this.skewX)\n                || isNotAroundZero$1(this.skewY);\n        };\n        Transformable.prototype.updateTransform = function () {\n            var parentTransform = this.parent && this.parent.transform;\n            var needLocalTransform = this.needLocalTransform();\n            var m = this.transform;\n            if (!(needLocalTransform || parentTransform)) {\n                m && mIdentity(m);\n                return;\n            }\n            m = m || create$1();\n            if (needLocalTransform) {\n                this.getLocalTransform(m);\n            }\n            else {\n                mIdentity(m);\n            }\n            if (parentTransform) {\n                if (needLocalTransform) {\n                    mul$1(m, parentTransform, m);\n                }\n                else {\n                    copy$1(m, parentTransform);\n                }\n            }\n            this.transform = m;\n            this._resolveGlobalScaleRatio(m);\n        };\n        Transformable.prototype._resolveGlobalScaleRatio = function (m) {\n            var globalScaleRatio = this.globalScaleRatio;\n            if (globalScaleRatio != null && globalScaleRatio !== 1) {\n                this.getGlobalScale(scaleTmp);\n                var relX = scaleTmp[0] < 0 ? -1 : 1;\n                var relY = scaleTmp[1] < 0 ? -1 : 1;\n                var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;\n                var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;\n                m[0] *= sx;\n                m[1] *= sx;\n                m[2] *= sy;\n                m[3] *= sy;\n            }\n            this.invTransform = this.invTransform || create$1();\n            invert(this.invTransform, m);\n        };\n        Transformable.prototype.getComputedTransform = function () {\n            var transformNode = this;\n            var ancestors = [];\n            while (transformNode) {\n                ancestors.push(transformNode);\n                transformNode = transformNode.parent;\n            }\n            while (transformNode = ancestors.pop()) {\n                transformNode.updateTransform();\n            }\n            return this.transform;\n        };\n        Transformable.prototype.setLocalTransform = function (m) {\n            if (!m) {\n                return;\n            }\n            var sx = m[0] * m[0] + m[1] * m[1];\n            var sy = m[2] * m[2] + m[3] * m[3];\n            var rotation = Math.atan2(m[1], m[0]);\n            var shearX = Math.PI / 2 + rotation - Math.atan2(m[3], m[2]);\n            sy = Math.sqrt(sy) * Math.cos(shearX);\n            sx = Math.sqrt(sx);\n            this.skewX = shearX;\n            this.skewY = 0;\n            this.rotation = -rotation;\n            this.x = +m[4];\n            this.y = +m[5];\n            this.scaleX = sx;\n            this.scaleY = sy;\n            this.originX = 0;\n            this.originY = 0;\n        };\n        Transformable.prototype.decomposeTransform = function () {\n            if (!this.transform) {\n                return;\n            }\n            var parent = this.parent;\n            var m = this.transform;\n            if (parent && parent.transform) {\n                mul$1(tmpTransform, parent.invTransform, m);\n                m = tmpTransform;\n            }\n            var ox = this.originX;\n            var oy = this.originY;\n            if (ox || oy) {\n                originTransform[4] = ox;\n                originTransform[5] = oy;\n                mul$1(tmpTransform, m, originTransform);\n                tmpTransform[4] -= ox;\n                tmpTransform[5] -= oy;\n                m = tmpTransform;\n            }\n            this.setLocalTransform(m);\n        };\n        Transformable.prototype.getGlobalScale = function (out) {\n            var m = this.transform;\n            out = out || [];\n            if (!m) {\n                out[0] = 1;\n                out[1] = 1;\n                return out;\n            }\n            out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);\n            out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);\n            if (m[0] < 0) {\n                out[0] = -out[0];\n            }\n            if (m[3] < 0) {\n                out[1] = -out[1];\n            }\n            return out;\n        };\n        Transformable.prototype.transformCoordToLocal = function (x, y) {\n            var v2 = [x, y];\n            var invTransform = this.invTransform;\n            if (invTransform) {\n                applyTransform(v2, v2, invTransform);\n            }\n            return v2;\n        };\n        Transformable.prototype.transformCoordToGlobal = function (x, y) {\n            var v2 = [x, y];\n            var transform = this.transform;\n            if (transform) {\n                applyTransform(v2, v2, transform);\n            }\n            return v2;\n        };\n        Transformable.prototype.getLineScale = function () {\n            var m = this.transform;\n            return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10\n                ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))\n                : 1;\n        };\n        Transformable.prototype.copyTransform = function (source) {\n            copyTransform(this, source);\n        };\n        Transformable.getLocalTransform = function (target, m) {\n            m = m || [];\n            var ox = target.originX || 0;\n            var oy = target.originY || 0;\n            var sx = target.scaleX;\n            var sy = target.scaleY;\n            var ax = target.anchorX;\n            var ay = target.anchorY;\n            var rotation = target.rotation || 0;\n            var x = target.x;\n            var y = target.y;\n            var skewX = target.skewX ? Math.tan(target.skewX) : 0;\n            var skewY = target.skewY ? Math.tan(-target.skewY) : 0;\n            if (ox || oy || ax || ay) {\n                var dx = ox + ax;\n                var dy = oy + ay;\n                m[4] = -dx * sx - skewX * dy * sy;\n                m[5] = -dy * sy - skewY * dx * sx;\n            }\n            else {\n                m[4] = m[5] = 0;\n            }\n            m[0] = sx;\n            m[3] = sy;\n            m[1] = skewY * sx;\n            m[2] = skewX * sy;\n            rotation && rotate(m, m, rotation);\n            m[4] += ox + x;\n            m[5] += oy + y;\n            return m;\n        };\n        Transformable.initDefaultProps = (function () {\n            var proto = Transformable.prototype;\n            proto.scaleX =\n                proto.scaleY =\n                    proto.globalScaleRatio = 1;\n            proto.x =\n                proto.y =\n                    proto.originX =\n                        proto.originY =\n                            proto.skewX =\n                                proto.skewY =\n                                    proto.rotation =\n                                        proto.anchorX =\n                                            proto.anchorY = 0;\n        })();\n        return Transformable;\n    }());\n    var TRANSFORMABLE_PROPS = [\n        'x', 'y', 'originX', 'originY', 'anchorX', 'anchorY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY'\n    ];\n    function copyTransform(target, source) {\n        for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {\n            var propName = TRANSFORMABLE_PROPS[i];\n            target[propName] = source[propName];\n        }\n    }\n\n    var textWidthCache = {};\n    function getWidth(text, font) {\n        font = font || DEFAULT_FONT;\n        var cacheOfFont = textWidthCache[font];\n        if (!cacheOfFont) {\n            cacheOfFont = textWidthCache[font] = new LRU(500);\n        }\n        var width = cacheOfFont.get(text);\n        if (width == null) {\n            width = platformApi.measureText(text, font).width;\n            cacheOfFont.put(text, width);\n        }\n        return width;\n    }\n    function innerGetBoundingRect(text, font, textAlign, textBaseline) {\n        var width = getWidth(text, font);\n        var height = getLineHeight(font);\n        var x = adjustTextX(0, width, textAlign);\n        var y = adjustTextY$1(0, height, textBaseline);\n        var rect = new BoundingRect(x, y, width, height);\n        return rect;\n    }\n    function getBoundingRect(text, font, textAlign, textBaseline) {\n        var textLines = ((text || '') + '').split('\\n');\n        var len = textLines.length;\n        if (len === 1) {\n            return innerGetBoundingRect(textLines[0], font, textAlign, textBaseline);\n        }\n        else {\n            var uniondRect = new BoundingRect(0, 0, 0, 0);\n            for (var i = 0; i < textLines.length; i++) {\n                var rect = innerGetBoundingRect(textLines[i], font, textAlign, textBaseline);\n                i === 0 ? uniondRect.copy(rect) : uniondRect.union(rect);\n            }\n            return uniondRect;\n        }\n    }\n    function adjustTextX(x, width, textAlign) {\n        if (textAlign === 'right') {\n            x -= width;\n        }\n        else if (textAlign === 'center') {\n            x -= width / 2;\n        }\n        return x;\n    }\n    function adjustTextY$1(y, height, verticalAlign) {\n        if (verticalAlign === 'middle') {\n            y -= height / 2;\n        }\n        else if (verticalAlign === 'bottom') {\n            y -= height;\n        }\n        return y;\n    }\n    function getLineHeight(font) {\n        return getWidth('国', font);\n    }\n    function parsePercent(value, maxValue) {\n        if (typeof value === 'string') {\n            if (value.lastIndexOf('%') >= 0) {\n                return parseFloat(value) / 100 * maxValue;\n            }\n            return parseFloat(value);\n        }\n        return value;\n    }\n    function calculateTextPosition(out, opts, rect) {\n        var textPosition = opts.position || 'inside';\n        var distance = opts.distance != null ? opts.distance : 5;\n        var height = rect.height;\n        var width = rect.width;\n        var halfHeight = height / 2;\n        var x = rect.x;\n        var y = rect.y;\n        var textAlign = 'left';\n        var textVerticalAlign = 'top';\n        if (textPosition instanceof Array) {\n            x += parsePercent(textPosition[0], rect.width);\n            y += parsePercent(textPosition[1], rect.height);\n            textAlign = null;\n            textVerticalAlign = null;\n        }\n        else {\n            switch (textPosition) {\n                case 'left':\n                    x -= distance;\n                    y += halfHeight;\n                    textAlign = 'right';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'right':\n                    x += distance + width;\n                    y += halfHeight;\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'top':\n                    x += width / 2;\n                    y -= distance;\n                    textAlign = 'center';\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'bottom':\n                    x += width / 2;\n                    y += height + distance;\n                    textAlign = 'center';\n                    break;\n                case 'inside':\n                    x += width / 2;\n                    y += halfHeight;\n                    textAlign = 'center';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideLeft':\n                    x += distance;\n                    y += halfHeight;\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideRight':\n                    x += width - distance;\n                    y += halfHeight;\n                    textAlign = 'right';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideTop':\n                    x += width / 2;\n                    y += distance;\n                    textAlign = 'center';\n                    break;\n                case 'insideBottom':\n                    x += width / 2;\n                    y += height - distance;\n                    textAlign = 'center';\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'insideTopLeft':\n                    x += distance;\n                    y += distance;\n                    break;\n                case 'insideTopRight':\n                    x += width - distance;\n                    y += distance;\n                    textAlign = 'right';\n                    break;\n                case 'insideBottomLeft':\n                    x += distance;\n                    y += height - distance;\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'insideBottomRight':\n                    x += width - distance;\n                    y += height - distance;\n                    textAlign = 'right';\n                    textVerticalAlign = 'bottom';\n                    break;\n            }\n        }\n        out = out || {};\n        out.x = x;\n        out.y = y;\n        out.align = textAlign;\n        out.verticalAlign = textVerticalAlign;\n        return out;\n    }\n\n    var PRESERVED_NORMAL_STATE = '__zr_normal__';\n    var PRIMARY_STATES_KEYS = TRANSFORMABLE_PROPS.concat(['ignore']);\n    var DEFAULT_ANIMATABLE_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {\n        obj[key] = true;\n        return obj;\n    }, { ignore: false });\n    var tmpTextPosCalcRes = {};\n    var tmpBoundingRect = new BoundingRect(0, 0, 0, 0);\n    var Element = (function () {\n        function Element(props) {\n            this.id = guid();\n            this.animators = [];\n            this.currentStates = [];\n            this.states = {};\n            this._init(props);\n        }\n        Element.prototype._init = function (props) {\n            this.attr(props);\n        };\n        Element.prototype.drift = function (dx, dy, e) {\n            switch (this.draggable) {\n                case 'horizontal':\n                    dy = 0;\n                    break;\n                case 'vertical':\n                    dx = 0;\n                    break;\n            }\n            var m = this.transform;\n            if (!m) {\n                m = this.transform = [1, 0, 0, 1, 0, 0];\n            }\n            m[4] += dx;\n            m[5] += dy;\n            this.decomposeTransform();\n            this.markRedraw();\n        };\n        Element.prototype.beforeUpdate = function () { };\n        Element.prototype.afterUpdate = function () { };\n        Element.prototype.update = function () {\n            this.updateTransform();\n            if (this.__dirty) {\n                this.updateInnerText();\n            }\n        };\n        Element.prototype.updateInnerText = function (forceUpdate) {\n            var textEl = this._textContent;\n            if (textEl && (!textEl.ignore || forceUpdate)) {\n                if (!this.textConfig) {\n                    this.textConfig = {};\n                }\n                var textConfig = this.textConfig;\n                var isLocal = textConfig.local;\n                var innerTransformable = textEl.innerTransformable;\n                var textAlign = void 0;\n                var textVerticalAlign = void 0;\n                var textStyleChanged = false;\n                innerTransformable.parent = isLocal ? this : null;\n                var innerOrigin = false;\n                innerTransformable.copyTransform(textEl);\n                if (textConfig.position != null) {\n                    var layoutRect = tmpBoundingRect;\n                    if (textConfig.layoutRect) {\n                        layoutRect.copy(textConfig.layoutRect);\n                    }\n                    else {\n                        layoutRect.copy(this.getBoundingRect());\n                    }\n                    if (!isLocal) {\n                        layoutRect.applyTransform(this.transform);\n                    }\n                    if (this.calculateTextPosition) {\n                        this.calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                    }\n                    else {\n                        calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                    }\n                    innerTransformable.x = tmpTextPosCalcRes.x;\n                    innerTransformable.y = tmpTextPosCalcRes.y;\n                    textAlign = tmpTextPosCalcRes.align;\n                    textVerticalAlign = tmpTextPosCalcRes.verticalAlign;\n                    var textOrigin = textConfig.origin;\n                    if (textOrigin && textConfig.rotation != null) {\n                        var relOriginX = void 0;\n                        var relOriginY = void 0;\n                        if (textOrigin === 'center') {\n                            relOriginX = layoutRect.width * 0.5;\n                            relOriginY = layoutRect.height * 0.5;\n                        }\n                        else {\n                            relOriginX = parsePercent(textOrigin[0], layoutRect.width);\n                            relOriginY = parsePercent(textOrigin[1], layoutRect.height);\n                        }\n                        innerOrigin = true;\n                        innerTransformable.originX = -innerTransformable.x + relOriginX + (isLocal ? 0 : layoutRect.x);\n                        innerTransformable.originY = -innerTransformable.y + relOriginY + (isLocal ? 0 : layoutRect.y);\n                    }\n                }\n                if (textConfig.rotation != null) {\n                    innerTransformable.rotation = textConfig.rotation;\n                }\n                var textOffset = textConfig.offset;\n                if (textOffset) {\n                    innerTransformable.x += textOffset[0];\n                    innerTransformable.y += textOffset[1];\n                    if (!innerOrigin) {\n                        innerTransformable.originX = -textOffset[0];\n                        innerTransformable.originY = -textOffset[1];\n                    }\n                }\n                var isInside = textConfig.inside == null\n                    ? (typeof textConfig.position === 'string' && textConfig.position.indexOf('inside') >= 0)\n                    : textConfig.inside;\n                var innerTextDefaultStyle = this._innerTextDefaultStyle || (this._innerTextDefaultStyle = {});\n                var textFill = void 0;\n                var textStroke = void 0;\n                var autoStroke = void 0;\n                if (isInside && this.canBeInsideText()) {\n                    textFill = textConfig.insideFill;\n                    textStroke = textConfig.insideStroke;\n                    if (textFill == null || textFill === 'auto') {\n                        textFill = this.getInsideTextFill();\n                    }\n                    if (textStroke == null || textStroke === 'auto') {\n                        textStroke = this.getInsideTextStroke(textFill);\n                        autoStroke = true;\n                    }\n                }\n                else {\n                    textFill = textConfig.outsideFill;\n                    textStroke = textConfig.outsideStroke;\n                    if (textFill == null || textFill === 'auto') {\n                        textFill = this.getOutsideFill();\n                    }\n                    if (textStroke == null || textStroke === 'auto') {\n                        textStroke = this.getOutsideStroke(textFill);\n                        autoStroke = true;\n                    }\n                }\n                textFill = textFill || '#000';\n                if (textFill !== innerTextDefaultStyle.fill\n                    || textStroke !== innerTextDefaultStyle.stroke\n                    || autoStroke !== innerTextDefaultStyle.autoStroke\n                    || textAlign !== innerTextDefaultStyle.align\n                    || textVerticalAlign !== innerTextDefaultStyle.verticalAlign) {\n                    textStyleChanged = true;\n                    innerTextDefaultStyle.fill = textFill;\n                    innerTextDefaultStyle.stroke = textStroke;\n                    innerTextDefaultStyle.autoStroke = autoStroke;\n                    innerTextDefaultStyle.align = textAlign;\n                    innerTextDefaultStyle.verticalAlign = textVerticalAlign;\n                    textEl.setDefaultTextStyle(innerTextDefaultStyle);\n                }\n                textEl.__dirty |= REDRAW_BIT;\n                if (textStyleChanged) {\n                    textEl.dirtyStyle(true);\n                }\n            }\n        };\n        Element.prototype.canBeInsideText = function () {\n            return true;\n        };\n        Element.prototype.getInsideTextFill = function () {\n            return '#fff';\n        };\n        Element.prototype.getInsideTextStroke = function (textFill) {\n            return '#000';\n        };\n        Element.prototype.getOutsideFill = function () {\n            return this.__zr && this.__zr.isDarkMode() ? LIGHT_LABEL_COLOR : DARK_LABEL_COLOR;\n        };\n        Element.prototype.getOutsideStroke = function (textFill) {\n            var backgroundColor = this.__zr && this.__zr.getBackgroundColor();\n            var colorArr = typeof backgroundColor === 'string' && parse(backgroundColor);\n            if (!colorArr) {\n                colorArr = [255, 255, 255, 1];\n            }\n            var alpha = colorArr[3];\n            var isDark = this.__zr.isDarkMode();\n            for (var i = 0; i < 3; i++) {\n                colorArr[i] = colorArr[i] * alpha + (isDark ? 0 : 255) * (1 - alpha);\n            }\n            colorArr[3] = 1;\n            return stringify(colorArr, 'rgba');\n        };\n        Element.prototype.traverse = function (cb, context) { };\n        Element.prototype.attrKV = function (key, value) {\n            if (key === 'textConfig') {\n                this.setTextConfig(value);\n            }\n            else if (key === 'textContent') {\n                this.setTextContent(value);\n            }\n            else if (key === 'clipPath') {\n                this.setClipPath(value);\n            }\n            else if (key === 'extra') {\n                this.extra = this.extra || {};\n                extend(this.extra, value);\n            }\n            else {\n                this[key] = value;\n            }\n        };\n        Element.prototype.hide = function () {\n            this.ignore = true;\n            this.markRedraw();\n        };\n        Element.prototype.show = function () {\n            this.ignore = false;\n            this.markRedraw();\n        };\n        Element.prototype.attr = function (keyOrObj, value) {\n            if (typeof keyOrObj === 'string') {\n                this.attrKV(keyOrObj, value);\n            }\n            else if (isObject(keyOrObj)) {\n                var obj = keyOrObj;\n                var keysArr = keys(obj);\n                for (var i = 0; i < keysArr.length; i++) {\n                    var key = keysArr[i];\n                    this.attrKV(key, keyOrObj[key]);\n                }\n            }\n            this.markRedraw();\n            return this;\n        };\n        Element.prototype.saveCurrentToNormalState = function (toState) {\n            this._innerSaveToNormal(toState);\n            var normalState = this._normalState;\n            for (var i = 0; i < this.animators.length; i++) {\n                var animator = this.animators[i];\n                var fromStateTransition = animator.__fromStateTransition;\n                if (animator.getLoop() || fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) {\n                    continue;\n                }\n                var targetName = animator.targetName;\n                var target = targetName\n                    ? normalState[targetName] : normalState;\n                animator.saveTo(target);\n            }\n        };\n        Element.prototype._innerSaveToNormal = function (toState) {\n            var normalState = this._normalState;\n            if (!normalState) {\n                normalState = this._normalState = {};\n            }\n            if (toState.textConfig && !normalState.textConfig) {\n                normalState.textConfig = this.textConfig;\n            }\n            this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS);\n        };\n        Element.prototype._savePrimaryToNormal = function (toState, normalState, primaryKeys) {\n            for (var i = 0; i < primaryKeys.length; i++) {\n                var key = primaryKeys[i];\n                if (toState[key] != null && !(key in normalState)) {\n                    normalState[key] = this[key];\n                }\n            }\n        };\n        Element.prototype.hasState = function () {\n            return this.currentStates.length > 0;\n        };\n        Element.prototype.getState = function (name) {\n            return this.states[name];\n        };\n        Element.prototype.ensureState = function (name) {\n            var states = this.states;\n            if (!states[name]) {\n                states[name] = {};\n            }\n            return states[name];\n        };\n        Element.prototype.clearStates = function (noAnimation) {\n            this.useState(PRESERVED_NORMAL_STATE, false, noAnimation);\n        };\n        Element.prototype.useState = function (stateName, keepCurrentStates, noAnimation, forceUseHoverLayer) {\n            var toNormalState = stateName === PRESERVED_NORMAL_STATE;\n            var hasStates = this.hasState();\n            if (!hasStates && toNormalState) {\n                return;\n            }\n            var currentStates = this.currentStates;\n            var animationCfg = this.stateTransition;\n            if (indexOf(currentStates, stateName) >= 0 && (keepCurrentStates || currentStates.length === 1)) {\n                return;\n            }\n            var state;\n            if (this.stateProxy && !toNormalState) {\n                state = this.stateProxy(stateName);\n            }\n            if (!state) {\n                state = (this.states && this.states[stateName]);\n            }\n            if (!state && !toNormalState) {\n                logError(\"State \" + stateName + \" not exists.\");\n                return;\n            }\n            if (!toNormalState) {\n                this.saveCurrentToNormalState(state);\n            }\n            var useHoverLayer = !!((state && state.hoverLayer) || forceUseHoverLayer);\n            if (useHoverLayer) {\n                this._toggleHoverLayerFlag(true);\n            }\n            this._applyStateObj(stateName, state, this._normalState, keepCurrentStates, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n            var textContent = this._textContent;\n            var textGuide = this._textGuide;\n            if (textContent) {\n                textContent.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n            }\n            if (textGuide) {\n                textGuide.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n            }\n            if (toNormalState) {\n                this.currentStates = [];\n                this._normalState = {};\n            }\n            else {\n                if (!keepCurrentStates) {\n                    this.currentStates = [stateName];\n                }\n                else {\n                    this.currentStates.push(stateName);\n                }\n            }\n            this._updateAnimationTargets();\n            this.markRedraw();\n            if (!useHoverLayer && this.__inHover) {\n                this._toggleHoverLayerFlag(false);\n                this.__dirty &= ~REDRAW_BIT;\n            }\n            return state;\n        };\n        Element.prototype.useStates = function (states, noAnimation, forceUseHoverLayer) {\n            if (!states.length) {\n                this.clearStates();\n            }\n            else {\n                var stateObjects = [];\n                var currentStates = this.currentStates;\n                var len = states.length;\n                var notChange = len === currentStates.length;\n                if (notChange) {\n                    for (var i = 0; i < len; i++) {\n                        if (states[i] !== currentStates[i]) {\n                            notChange = false;\n                            break;\n                        }\n                    }\n                }\n                if (notChange) {\n                    return;\n                }\n                for (var i = 0; i < len; i++) {\n                    var stateName = states[i];\n                    var stateObj = void 0;\n                    if (this.stateProxy) {\n                        stateObj = this.stateProxy(stateName, states);\n                    }\n                    if (!stateObj) {\n                        stateObj = this.states[stateName];\n                    }\n                    if (stateObj) {\n                        stateObjects.push(stateObj);\n                    }\n                }\n                var lastStateObj = stateObjects[len - 1];\n                var useHoverLayer = !!((lastStateObj && lastStateObj.hoverLayer) || forceUseHoverLayer);\n                if (useHoverLayer) {\n                    this._toggleHoverLayerFlag(true);\n                }\n                var mergedState = this._mergeStates(stateObjects);\n                var animationCfg = this.stateTransition;\n                this.saveCurrentToNormalState(mergedState);\n                this._applyStateObj(states.join(','), mergedState, this._normalState, false, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n                var textContent = this._textContent;\n                var textGuide = this._textGuide;\n                if (textContent) {\n                    textContent.useStates(states, noAnimation, useHoverLayer);\n                }\n                if (textGuide) {\n                    textGuide.useStates(states, noAnimation, useHoverLayer);\n                }\n                this._updateAnimationTargets();\n                this.currentStates = states.slice();\n                this.markRedraw();\n                if (!useHoverLayer && this.__inHover) {\n                    this._toggleHoverLayerFlag(false);\n                    this.__dirty &= ~REDRAW_BIT;\n                }\n            }\n        };\n        Element.prototype._updateAnimationTargets = function () {\n            for (var i = 0; i < this.animators.length; i++) {\n                var animator = this.animators[i];\n                if (animator.targetName) {\n                    animator.changeTarget(this[animator.targetName]);\n                }\n            }\n        };\n        Element.prototype.removeState = function (state) {\n            var idx = indexOf(this.currentStates, state);\n            if (idx >= 0) {\n                var currentStates = this.currentStates.slice();\n                currentStates.splice(idx, 1);\n                this.useStates(currentStates);\n            }\n        };\n        Element.prototype.replaceState = function (oldState, newState, forceAdd) {\n            var currentStates = this.currentStates.slice();\n            var idx = indexOf(currentStates, oldState);\n            var newStateExists = indexOf(currentStates, newState) >= 0;\n            if (idx >= 0) {\n                if (!newStateExists) {\n                    currentStates[idx] = newState;\n                }\n                else {\n                    currentStates.splice(idx, 1);\n                }\n            }\n            else if (forceAdd && !newStateExists) {\n                currentStates.push(newState);\n            }\n            this.useStates(currentStates);\n        };\n        Element.prototype.toggleState = function (state, enable) {\n            if (enable) {\n                this.useState(state, true);\n            }\n            else {\n                this.removeState(state);\n            }\n        };\n        Element.prototype._mergeStates = function (states) {\n            var mergedState = {};\n            var mergedTextConfig;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                extend(mergedState, state);\n                if (state.textConfig) {\n                    mergedTextConfig = mergedTextConfig || {};\n                    extend(mergedTextConfig, state.textConfig);\n                }\n            }\n            if (mergedTextConfig) {\n                mergedState.textConfig = mergedTextConfig;\n            }\n            return mergedState;\n        };\n        Element.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            if (state && state.textConfig) {\n                this.textConfig = extend({}, keepCurrentStates ? this.textConfig : normalState.textConfig);\n                extend(this.textConfig, state.textConfig);\n            }\n            else if (needsRestoreToNormal) {\n                if (normalState.textConfig) {\n                    this.textConfig = normalState.textConfig;\n                }\n            }\n            var transitionTarget = {};\n            var hasTransition = false;\n            for (var i = 0; i < PRIMARY_STATES_KEYS.length; i++) {\n                var key = PRIMARY_STATES_KEYS[i];\n                var propNeedsTransition = transition && DEFAULT_ANIMATABLE_MAP[key];\n                if (state && state[key] != null) {\n                    if (propNeedsTransition) {\n                        hasTransition = true;\n                        transitionTarget[key] = state[key];\n                    }\n                    else {\n                        this[key] = state[key];\n                    }\n                }\n                else if (needsRestoreToNormal) {\n                    if (normalState[key] != null) {\n                        if (propNeedsTransition) {\n                            hasTransition = true;\n                            transitionTarget[key] = normalState[key];\n                        }\n                        else {\n                            this[key] = normalState[key];\n                        }\n                    }\n                }\n            }\n            if (!transition) {\n                for (var i = 0; i < this.animators.length; i++) {\n                    var animator = this.animators[i];\n                    var targetName = animator.targetName;\n                    if (!animator.getLoop()) {\n                        animator.__changeFinalValue(targetName\n                            ? (state || normalState)[targetName]\n                            : (state || normalState));\n                    }\n                }\n            }\n            if (hasTransition) {\n                this._transitionState(stateName, transitionTarget, animationCfg);\n            }\n        };\n        Element.prototype._attachComponent = function (componentEl) {\n            if (componentEl.__zr && !componentEl.__hostTarget) {\n                if (\"development\" !== 'production') {\n                    throw new Error('Text element has been added to zrender.');\n                }\n                return;\n            }\n            if (componentEl === this) {\n                if (\"development\" !== 'production') {\n                    throw new Error('Recursive component attachment.');\n                }\n                return;\n            }\n            var zr = this.__zr;\n            if (zr) {\n                componentEl.addSelfToZr(zr);\n            }\n            componentEl.__zr = zr;\n            componentEl.__hostTarget = this;\n        };\n        Element.prototype._detachComponent = function (componentEl) {\n            if (componentEl.__zr) {\n                componentEl.removeSelfFromZr(componentEl.__zr);\n            }\n            componentEl.__zr = null;\n            componentEl.__hostTarget = null;\n        };\n        Element.prototype.getClipPath = function () {\n            return this._clipPath;\n        };\n        Element.prototype.setClipPath = function (clipPath) {\n            if (this._clipPath && this._clipPath !== clipPath) {\n                this.removeClipPath();\n            }\n            this._attachComponent(clipPath);\n            this._clipPath = clipPath;\n            this.markRedraw();\n        };\n        Element.prototype.removeClipPath = function () {\n            var clipPath = this._clipPath;\n            if (clipPath) {\n                this._detachComponent(clipPath);\n                this._clipPath = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.getTextContent = function () {\n            return this._textContent;\n        };\n        Element.prototype.setTextContent = function (textEl) {\n            var previousTextContent = this._textContent;\n            if (previousTextContent === textEl) {\n                return;\n            }\n            if (previousTextContent && previousTextContent !== textEl) {\n                this.removeTextContent();\n            }\n            if (\"development\" !== 'production') {\n                if (textEl.__zr && !textEl.__hostTarget) {\n                    throw new Error('Text element has been added to zrender.');\n                }\n            }\n            textEl.innerTransformable = new Transformable();\n            this._attachComponent(textEl);\n            this._textContent = textEl;\n            this.markRedraw();\n        };\n        Element.prototype.setTextConfig = function (cfg) {\n            if (!this.textConfig) {\n                this.textConfig = {};\n            }\n            extend(this.textConfig, cfg);\n            this.markRedraw();\n        };\n        Element.prototype.removeTextConfig = function () {\n            this.textConfig = null;\n            this.markRedraw();\n        };\n        Element.prototype.removeTextContent = function () {\n            var textEl = this._textContent;\n            if (textEl) {\n                textEl.innerTransformable = null;\n                this._detachComponent(textEl);\n                this._textContent = null;\n                this._innerTextDefaultStyle = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.getTextGuideLine = function () {\n            return this._textGuide;\n        };\n        Element.prototype.setTextGuideLine = function (guideLine) {\n            if (this._textGuide && this._textGuide !== guideLine) {\n                this.removeTextGuideLine();\n            }\n            this._attachComponent(guideLine);\n            this._textGuide = guideLine;\n            this.markRedraw();\n        };\n        Element.prototype.removeTextGuideLine = function () {\n            var textGuide = this._textGuide;\n            if (textGuide) {\n                this._detachComponent(textGuide);\n                this._textGuide = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.markRedraw = function () {\n            this.__dirty |= REDRAW_BIT;\n            var zr = this.__zr;\n            if (zr) {\n                if (this.__inHover) {\n                    zr.refreshHover();\n                }\n                else {\n                    zr.refresh();\n                }\n            }\n            if (this.__hostTarget) {\n                this.__hostTarget.markRedraw();\n            }\n        };\n        Element.prototype.dirty = function () {\n            this.markRedraw();\n        };\n        Element.prototype._toggleHoverLayerFlag = function (inHover) {\n            this.__inHover = inHover;\n            var textContent = this._textContent;\n            var textGuide = this._textGuide;\n            if (textContent) {\n                textContent.__inHover = inHover;\n            }\n            if (textGuide) {\n                textGuide.__inHover = inHover;\n            }\n        };\n        Element.prototype.addSelfToZr = function (zr) {\n            if (this.__zr === zr) {\n                return;\n            }\n            this.__zr = zr;\n            var animators = this.animators;\n            if (animators) {\n                for (var i = 0; i < animators.length; i++) {\n                    zr.animation.addAnimator(animators[i]);\n                }\n            }\n            if (this._clipPath) {\n                this._clipPath.addSelfToZr(zr);\n            }\n            if (this._textContent) {\n                this._textContent.addSelfToZr(zr);\n            }\n            if (this._textGuide) {\n                this._textGuide.addSelfToZr(zr);\n            }\n        };\n        Element.prototype.removeSelfFromZr = function (zr) {\n            if (!this.__zr) {\n                return;\n            }\n            this.__zr = null;\n            var animators = this.animators;\n            if (animators) {\n                for (var i = 0; i < animators.length; i++) {\n                    zr.animation.removeAnimator(animators[i]);\n                }\n            }\n            if (this._clipPath) {\n                this._clipPath.removeSelfFromZr(zr);\n            }\n            if (this._textContent) {\n                this._textContent.removeSelfFromZr(zr);\n            }\n            if (this._textGuide) {\n                this._textGuide.removeSelfFromZr(zr);\n            }\n        };\n        Element.prototype.animate = function (key, loop, allowDiscreteAnimation) {\n            var target = key ? this[key] : this;\n            if (\"development\" !== 'production') {\n                if (!target) {\n                    logError('Property \"'\n                        + key\n                        + '\" is not existed in element '\n                        + this.id);\n                    return;\n                }\n            }\n            var animator = new Animator(target, loop, allowDiscreteAnimation);\n            key && (animator.targetName = key);\n            this.addAnimator(animator, key);\n            return animator;\n        };\n        Element.prototype.addAnimator = function (animator, key) {\n            var zr = this.__zr;\n            var el = this;\n            animator.during(function () {\n                el.updateDuringAnimation(key);\n            }).done(function () {\n                var animators = el.animators;\n                var idx = indexOf(animators, animator);\n                if (idx >= 0) {\n                    animators.splice(idx, 1);\n                }\n            });\n            this.animators.push(animator);\n            if (zr) {\n                zr.animation.addAnimator(animator);\n            }\n            zr && zr.wakeUp();\n        };\n        Element.prototype.updateDuringAnimation = function (key) {\n            this.markRedraw();\n        };\n        Element.prototype.stopAnimation = function (scope, forwardToLast) {\n            var animators = this.animators;\n            var len = animators.length;\n            var leftAnimators = [];\n            for (var i = 0; i < len; i++) {\n                var animator = animators[i];\n                if (!scope || scope === animator.scope) {\n                    animator.stop(forwardToLast);\n                }\n                else {\n                    leftAnimators.push(animator);\n                }\n            }\n            this.animators = leftAnimators;\n            return this;\n        };\n        Element.prototype.animateTo = function (target, cfg, animationProps) {\n            animateTo(this, target, cfg, animationProps);\n        };\n        Element.prototype.animateFrom = function (target, cfg, animationProps) {\n            animateTo(this, target, cfg, animationProps, true);\n        };\n        Element.prototype._transitionState = function (stateName, target, cfg, animationProps) {\n            var animators = animateTo(this, target, cfg, animationProps);\n            for (var i = 0; i < animators.length; i++) {\n                animators[i].__fromStateTransition = stateName;\n            }\n        };\n        Element.prototype.getBoundingRect = function () {\n            return null;\n        };\n        Element.prototype.getPaintRect = function () {\n            return null;\n        };\n        Element.initDefaultProps = (function () {\n            var elProto = Element.prototype;\n            elProto.type = 'element';\n            elProto.name = '';\n            elProto.ignore =\n                elProto.silent =\n                    elProto.isGroup =\n                        elProto.draggable =\n                            elProto.dragging =\n                                elProto.ignoreClip =\n                                    elProto.__inHover = false;\n            elProto.__dirty = REDRAW_BIT;\n            var logs = {};\n            function logDeprecatedError(key, xKey, yKey) {\n                if (!logs[key + xKey + yKey]) {\n                    console.warn(\"DEPRECATED: '\" + key + \"' has been deprecated. use '\" + xKey + \"', '\" + yKey + \"' instead\");\n                    logs[key + xKey + yKey] = true;\n                }\n            }\n            function createLegacyProperty(key, privateKey, xKey, yKey) {\n                Object.defineProperty(elProto, key, {\n                    get: function () {\n                        if (\"development\" !== 'production') {\n                            logDeprecatedError(key, xKey, yKey);\n                        }\n                        if (!this[privateKey]) {\n                            var pos = this[privateKey] = [];\n                            enhanceArray(this, pos);\n                        }\n                        return this[privateKey];\n                    },\n                    set: function (pos) {\n                        if (\"development\" !== 'production') {\n                            logDeprecatedError(key, xKey, yKey);\n                        }\n                        this[xKey] = pos[0];\n                        this[yKey] = pos[1];\n                        this[privateKey] = pos;\n                        enhanceArray(this, pos);\n                    }\n                });\n                function enhanceArray(self, pos) {\n                    Object.defineProperty(pos, 0, {\n                        get: function () {\n                            return self[xKey];\n                        },\n                        set: function (val) {\n                            self[xKey] = val;\n                        }\n                    });\n                    Object.defineProperty(pos, 1, {\n                        get: function () {\n                            return self[yKey];\n                        },\n                        set: function (val) {\n                            self[yKey] = val;\n                        }\n                    });\n                }\n            }\n            if (Object.defineProperty) {\n                createLegacyProperty('position', '_legacyPos', 'x', 'y');\n                createLegacyProperty('scale', '_legacyScale', 'scaleX', 'scaleY');\n                createLegacyProperty('origin', '_legacyOrigin', 'originX', 'originY');\n            }\n        })();\n        return Element;\n    }());\n    mixin(Element, Eventful);\n    mixin(Element, Transformable);\n    function animateTo(animatable, target, cfg, animationProps, reverse) {\n        cfg = cfg || {};\n        var animators = [];\n        animateToShallow(animatable, '', animatable, target, cfg, animationProps, animators, reverse);\n        var finishCount = animators.length;\n        var doneHappened = false;\n        var cfgDone = cfg.done;\n        var cfgAborted = cfg.aborted;\n        var doneCb = function () {\n            doneHappened = true;\n            finishCount--;\n            if (finishCount <= 0) {\n                doneHappened\n                    ? (cfgDone && cfgDone())\n                    : (cfgAborted && cfgAborted());\n            }\n        };\n        var abortedCb = function () {\n            finishCount--;\n            if (finishCount <= 0) {\n                doneHappened\n                    ? (cfgDone && cfgDone())\n                    : (cfgAborted && cfgAborted());\n            }\n        };\n        if (!finishCount) {\n            cfgDone && cfgDone();\n        }\n        if (animators.length > 0 && cfg.during) {\n            animators[0].during(function (target, percent) {\n                cfg.during(percent);\n            });\n        }\n        for (var i = 0; i < animators.length; i++) {\n            var animator = animators[i];\n            if (doneCb) {\n                animator.done(doneCb);\n            }\n            if (abortedCb) {\n                animator.aborted(abortedCb);\n            }\n            if (cfg.force) {\n                animator.duration(cfg.duration);\n            }\n            animator.start(cfg.easing);\n        }\n        return animators;\n    }\n    function copyArrShallow(source, target, len) {\n        for (var i = 0; i < len; i++) {\n            source[i] = target[i];\n        }\n    }\n    function is2DArray(value) {\n        return isArrayLike(value[0]);\n    }\n    function copyValue(target, source, key) {\n        if (isArrayLike(source[key])) {\n            if (!isArrayLike(target[key])) {\n                target[key] = [];\n            }\n            if (isTypedArray(source[key])) {\n                var len = source[key].length;\n                if (target[key].length !== len) {\n                    target[key] = new (source[key].constructor)(len);\n                    copyArrShallow(target[key], source[key], len);\n                }\n            }\n            else {\n                var sourceArr = source[key];\n                var targetArr = target[key];\n                var len0 = sourceArr.length;\n                if (is2DArray(sourceArr)) {\n                    var len1 = sourceArr[0].length;\n                    for (var i = 0; i < len0; i++) {\n                        if (!targetArr[i]) {\n                            targetArr[i] = Array.prototype.slice.call(sourceArr[i]);\n                        }\n                        else {\n                            copyArrShallow(targetArr[i], sourceArr[i], len1);\n                        }\n                    }\n                }\n                else {\n                    copyArrShallow(targetArr, sourceArr, len0);\n                }\n                targetArr.length = sourceArr.length;\n            }\n        }\n        else {\n            target[key] = source[key];\n        }\n    }\n    function isValueSame(val1, val2) {\n        return val1 === val2\n            || isArrayLike(val1) && isArrayLike(val2) && is1DArraySame(val1, val2);\n    }\n    function is1DArraySame(arr0, arr1) {\n        var len = arr0.length;\n        if (len !== arr1.length) {\n            return false;\n        }\n        for (var i = 0; i < len; i++) {\n            if (arr0[i] !== arr1[i]) {\n                return false;\n            }\n        }\n        return true;\n    }\n    function animateToShallow(animatable, topKey, animateObj, target, cfg, animationProps, animators, reverse) {\n        var targetKeys = keys(target);\n        var duration = cfg.duration;\n        var delay = cfg.delay;\n        var additive = cfg.additive;\n        var setToFinal = cfg.setToFinal;\n        var animateAll = !isObject(animationProps);\n        var existsAnimators = animatable.animators;\n        var animationKeys = [];\n        for (var k = 0; k < targetKeys.length; k++) {\n            var innerKey = targetKeys[k];\n            var targetVal = target[innerKey];\n            if (targetVal != null && animateObj[innerKey] != null\n                && (animateAll || animationProps[innerKey])) {\n                if (isObject(targetVal)\n                    && !isArrayLike(targetVal)\n                    && !isGradientObject(targetVal)) {\n                    if (topKey) {\n                        if (!reverse) {\n                            animateObj[innerKey] = targetVal;\n                            animatable.updateDuringAnimation(topKey);\n                        }\n                        continue;\n                    }\n                    animateToShallow(animatable, innerKey, animateObj[innerKey], targetVal, cfg, animationProps && animationProps[innerKey], animators, reverse);\n                }\n                else {\n                    animationKeys.push(innerKey);\n                }\n            }\n            else if (!reverse) {\n                animateObj[innerKey] = targetVal;\n                animatable.updateDuringAnimation(topKey);\n                animationKeys.push(innerKey);\n            }\n        }\n        var keyLen = animationKeys.length;\n        if (!additive && keyLen) {\n            for (var i = 0; i < existsAnimators.length; i++) {\n                var animator = existsAnimators[i];\n                if (animator.targetName === topKey) {\n                    var allAborted = animator.stopTracks(animationKeys);\n                    if (allAborted) {\n                        var idx = indexOf(existsAnimators, animator);\n                        existsAnimators.splice(idx, 1);\n                    }\n                }\n            }\n        }\n        if (!cfg.force) {\n            animationKeys = filter(animationKeys, function (key) { return !isValueSame(target[key], animateObj[key]); });\n            keyLen = animationKeys.length;\n        }\n        if (keyLen > 0\n            || (cfg.force && !animators.length)) {\n            var revertedSource = void 0;\n            var reversedTarget = void 0;\n            var sourceClone = void 0;\n            if (reverse) {\n                reversedTarget = {};\n                if (setToFinal) {\n                    revertedSource = {};\n                }\n                for (var i = 0; i < keyLen; i++) {\n                    var innerKey = animationKeys[i];\n                    reversedTarget[innerKey] = animateObj[innerKey];\n                    if (setToFinal) {\n                        revertedSource[innerKey] = target[innerKey];\n                    }\n                    else {\n                        animateObj[innerKey] = target[innerKey];\n                    }\n                }\n            }\n            else if (setToFinal) {\n                sourceClone = {};\n                for (var i = 0; i < keyLen; i++) {\n                    var innerKey = animationKeys[i];\n                    sourceClone[innerKey] = cloneValue(animateObj[innerKey]);\n                    copyValue(animateObj, target, innerKey);\n                }\n            }\n            var animator = new Animator(animateObj, false, false, additive ? filter(existsAnimators, function (animator) { return animator.targetName === topKey; }) : null);\n            animator.targetName = topKey;\n            if (cfg.scope) {\n                animator.scope = cfg.scope;\n            }\n            if (setToFinal && revertedSource) {\n                animator.whenWithKeys(0, revertedSource, animationKeys);\n            }\n            if (sourceClone) {\n                animator.whenWithKeys(0, sourceClone, animationKeys);\n            }\n            animator.whenWithKeys(duration == null ? 500 : duration, reverse ? reversedTarget : target, animationKeys).delay(delay || 0);\n            animatable.addAnimator(animator, topKey);\n            animators.push(animator);\n        }\n    }\n\n    var Group = (function (_super) {\n        __extends(Group, _super);\n        function Group(opts) {\n            var _this = _super.call(this) || this;\n            _this.isGroup = true;\n            _this._children = [];\n            _this.attr(opts);\n            return _this;\n        }\n        Group.prototype.childrenRef = function () {\n            return this._children;\n        };\n        Group.prototype.children = function () {\n            return this._children.slice();\n        };\n        Group.prototype.childAt = function (idx) {\n            return this._children[idx];\n        };\n        Group.prototype.childOfName = function (name) {\n            var children = this._children;\n            for (var i = 0; i < children.length; i++) {\n                if (children[i].name === name) {\n                    return children[i];\n                }\n            }\n        };\n        Group.prototype.childCount = function () {\n            return this._children.length;\n        };\n        Group.prototype.add = function (child) {\n            if (child) {\n                if (child !== this && child.parent !== this) {\n                    this._children.push(child);\n                    this._doAdd(child);\n                }\n                if (\"development\" !== 'production') {\n                    if (child.__hostTarget) {\n                        throw 'This elemenet has been used as an attachment';\n                    }\n                }\n            }\n            return this;\n        };\n        Group.prototype.addBefore = function (child, nextSibling) {\n            if (child && child !== this && child.parent !== this\n                && nextSibling && nextSibling.parent === this) {\n                var children = this._children;\n                var idx = children.indexOf(nextSibling);\n                if (idx >= 0) {\n                    children.splice(idx, 0, child);\n                    this._doAdd(child);\n                }\n            }\n            return this;\n        };\n        Group.prototype.replace = function (oldChild, newChild) {\n            var idx = indexOf(this._children, oldChild);\n            if (idx >= 0) {\n                this.replaceAt(newChild, idx);\n            }\n            return this;\n        };\n        Group.prototype.replaceAt = function (child, index) {\n            var children = this._children;\n            var old = children[index];\n            if (child && child !== this && child.parent !== this && child !== old) {\n                children[index] = child;\n                old.parent = null;\n                var zr = this.__zr;\n                if (zr) {\n                    old.removeSelfFromZr(zr);\n                }\n                this._doAdd(child);\n            }\n            return this;\n        };\n        Group.prototype._doAdd = function (child) {\n            if (child.parent) {\n                child.parent.remove(child);\n            }\n            child.parent = this;\n            var zr = this.__zr;\n            if (zr && zr !== child.__zr) {\n                child.addSelfToZr(zr);\n            }\n            zr && zr.refresh();\n        };\n        Group.prototype.remove = function (child) {\n            var zr = this.__zr;\n            var children = this._children;\n            var idx = indexOf(children, child);\n            if (idx < 0) {\n                return this;\n            }\n            children.splice(idx, 1);\n            child.parent = null;\n            if (zr) {\n                child.removeSelfFromZr(zr);\n            }\n            zr && zr.refresh();\n            return this;\n        };\n        Group.prototype.removeAll = function () {\n            var children = this._children;\n            var zr = this.__zr;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                if (zr) {\n                    child.removeSelfFromZr(zr);\n                }\n                child.parent = null;\n            }\n            children.length = 0;\n            return this;\n        };\n        Group.prototype.eachChild = function (cb, context) {\n            var children = this._children;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                cb.call(context, child, i);\n            }\n            return this;\n        };\n        Group.prototype.traverse = function (cb, context) {\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                var stopped = cb.call(context, child);\n                if (child.isGroup && !stopped) {\n                    child.traverse(cb, context);\n                }\n            }\n            return this;\n        };\n        Group.prototype.addSelfToZr = function (zr) {\n            _super.prototype.addSelfToZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.addSelfToZr(zr);\n            }\n        };\n        Group.prototype.removeSelfFromZr = function (zr) {\n            _super.prototype.removeSelfFromZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.removeSelfFromZr(zr);\n            }\n        };\n        Group.prototype.getBoundingRect = function (includeChildren) {\n            var tmpRect = new BoundingRect(0, 0, 0, 0);\n            var children = includeChildren || this._children;\n            var tmpMat = [];\n            var rect = null;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                if (child.ignore || child.invisible) {\n                    continue;\n                }\n                var childRect = child.getBoundingRect();\n                var transform = child.getLocalTransform(tmpMat);\n                if (transform) {\n                    BoundingRect.applyTransform(tmpRect, childRect, transform);\n                    rect = rect || tmpRect.clone();\n                    rect.union(tmpRect);\n                }\n                else {\n                    rect = rect || childRect.clone();\n                    rect.union(childRect);\n                }\n            }\n            return rect || tmpRect;\n        };\n        return Group;\n    }(Element));\n    Group.prototype.type = 'group';\n\n    /*!\n    * ZRender, a high performance 2d drawing library.\n    *\n    * Copyright (c) 2013, Baidu Inc.\n    * All rights reserved.\n    *\n    * LICENSE\n    * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt\n    */\n    var painterCtors = {};\n    var instances = {};\n    function delInstance(id) {\n        delete instances[id];\n    }\n    function isDarkMode(backgroundColor) {\n        if (!backgroundColor) {\n            return false;\n        }\n        if (typeof backgroundColor === 'string') {\n            return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD;\n        }\n        else if (backgroundColor.colorStops) {\n            var colorStops = backgroundColor.colorStops;\n            var totalLum = 0;\n            var len = colorStops.length;\n            for (var i = 0; i < len; i++) {\n                totalLum += lum(colorStops[i].color, 1);\n            }\n            totalLum /= len;\n            return totalLum < DARK_MODE_THRESHOLD;\n        }\n        return false;\n    }\n    var ZRender = (function () {\n        function ZRender(id, dom, opts) {\n            var _this = this;\n            this._sleepAfterStill = 10;\n            this._stillFrameAccum = 0;\n            this._needsRefresh = true;\n            this._needsRefreshHover = true;\n            this._darkMode = false;\n            opts = opts || {};\n            this.dom = dom;\n            this.id = id;\n            var storage = new Storage();\n            var rendererType = opts.renderer || 'canvas';\n            if (!painterCtors[rendererType]) {\n                rendererType = keys(painterCtors)[0];\n            }\n            if (\"development\" !== 'production') {\n                if (!painterCtors[rendererType]) {\n                    throw new Error(\"Renderer '\" + rendererType + \"' is not imported. Please import it first.\");\n                }\n            }\n            opts.useDirtyRect = opts.useDirtyRect == null\n                ? false\n                : opts.useDirtyRect;\n            var painter = new painterCtors[rendererType](dom, storage, opts, id);\n            var ssrMode = opts.ssr || painter.ssrOnly;\n            this.storage = storage;\n            this.painter = painter;\n            var handerProxy = (!env.node && !env.worker && !ssrMode)\n                ? new HandlerDomProxy(painter.getViewportRoot(), painter.root)\n                : null;\n            var useCoarsePointer = opts.useCoarsePointer;\n            var usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto')\n                ? env.touchEventsSupported\n                : !!useCoarsePointer;\n            var defaultPointerSize = 44;\n            var pointerSize;\n            if (usePointerSize) {\n                pointerSize = retrieve2(opts.pointerSize, defaultPointerSize);\n            }\n            this.handler = new Handler(storage, painter, handerProxy, painter.root, pointerSize);\n            this.animation = new Animation({\n                stage: {\n                    update: ssrMode ? null : function () { return _this._flush(true); }\n                }\n            });\n            if (!ssrMode) {\n                this.animation.start();\n            }\n        }\n        ZRender.prototype.add = function (el) {\n            if (!el) {\n                return;\n            }\n            this.storage.addRoot(el);\n            el.addSelfToZr(this);\n            this.refresh();\n        };\n        ZRender.prototype.remove = function (el) {\n            if (!el) {\n                return;\n            }\n            this.storage.delRoot(el);\n            el.removeSelfFromZr(this);\n            this.refresh();\n        };\n        ZRender.prototype.configLayer = function (zLevel, config) {\n            if (this.painter.configLayer) {\n                this.painter.configLayer(zLevel, config);\n            }\n            this.refresh();\n        };\n        ZRender.prototype.setBackgroundColor = function (backgroundColor) {\n            if (this.painter.setBackgroundColor) {\n                this.painter.setBackgroundColor(backgroundColor);\n            }\n            this.refresh();\n            this._backgroundColor = backgroundColor;\n            this._darkMode = isDarkMode(backgroundColor);\n        };\n        ZRender.prototype.getBackgroundColor = function () {\n            return this._backgroundColor;\n        };\n        ZRender.prototype.setDarkMode = function (darkMode) {\n            this._darkMode = darkMode;\n        };\n        ZRender.prototype.isDarkMode = function () {\n            return this._darkMode;\n        };\n        ZRender.prototype.refreshImmediately = function (fromInside) {\n            if (!fromInside) {\n                this.animation.update(true);\n            }\n            this._needsRefresh = false;\n            this.painter.refresh();\n            this._needsRefresh = false;\n        };\n        ZRender.prototype.refresh = function () {\n            this._needsRefresh = true;\n            this.animation.start();\n        };\n        ZRender.prototype.flush = function () {\n            this._flush(false);\n        };\n        ZRender.prototype._flush = function (fromInside) {\n            var triggerRendered;\n            var start = getTime();\n            if (this._needsRefresh) {\n                triggerRendered = true;\n                this.refreshImmediately(fromInside);\n            }\n            if (this._needsRefreshHover) {\n                triggerRendered = true;\n                this.refreshHoverImmediately();\n            }\n            var end = getTime();\n            if (triggerRendered) {\n                this._stillFrameAccum = 0;\n                this.trigger('rendered', {\n                    elapsedTime: end - start\n                });\n            }\n            else if (this._sleepAfterStill > 0) {\n                this._stillFrameAccum++;\n                if (this._stillFrameAccum > this._sleepAfterStill) {\n                    this.animation.stop();\n                }\n            }\n        };\n        ZRender.prototype.setSleepAfterStill = function (stillFramesCount) {\n            this._sleepAfterStill = stillFramesCount;\n        };\n        ZRender.prototype.wakeUp = function () {\n            this.animation.start();\n            this._stillFrameAccum = 0;\n        };\n        ZRender.prototype.refreshHover = function () {\n            this._needsRefreshHover = true;\n        };\n        ZRender.prototype.refreshHoverImmediately = function () {\n            this._needsRefreshHover = false;\n            if (this.painter.refreshHover && this.painter.getType() === 'canvas') {\n                this.painter.refreshHover();\n            }\n        };\n        ZRender.prototype.resize = function (opts) {\n            opts = opts || {};\n            this.painter.resize(opts.width, opts.height);\n            this.handler.resize();\n        };\n        ZRender.prototype.clearAnimation = function () {\n            this.animation.clear();\n        };\n        ZRender.prototype.getWidth = function () {\n            return this.painter.getWidth();\n        };\n        ZRender.prototype.getHeight = function () {\n            return this.painter.getHeight();\n        };\n        ZRender.prototype.setCursorStyle = function (cursorStyle) {\n            this.handler.setCursorStyle(cursorStyle);\n        };\n        ZRender.prototype.findHover = function (x, y) {\n            return this.handler.findHover(x, y);\n        };\n        ZRender.prototype.on = function (eventName, eventHandler, context) {\n            this.handler.on(eventName, eventHandler, context);\n            return this;\n        };\n        ZRender.prototype.off = function (eventName, eventHandler) {\n            this.handler.off(eventName, eventHandler);\n        };\n        ZRender.prototype.trigger = function (eventName, event) {\n            this.handler.trigger(eventName, event);\n        };\n        ZRender.prototype.clear = function () {\n            var roots = this.storage.getRoots();\n            for (var i = 0; i < roots.length; i++) {\n                if (roots[i] instanceof Group) {\n                    roots[i].removeSelfFromZr(this);\n                }\n            }\n            this.storage.delAllRoots();\n            this.painter.clear();\n        };\n        ZRender.prototype.dispose = function () {\n            this.animation.stop();\n            this.clear();\n            this.storage.dispose();\n            this.painter.dispose();\n            this.handler.dispose();\n            this.animation =\n                this.storage =\n                    this.painter =\n                        this.handler = null;\n            delInstance(this.id);\n        };\n        return ZRender;\n    }());\n    function init(dom, opts) {\n        var zr = new ZRender(guid(), dom, opts);\n        instances[zr.id] = zr;\n        return zr;\n    }\n    function dispose(zr) {\n        zr.dispose();\n    }\n    function disposeAll() {\n        for (var key in instances) {\n            if (instances.hasOwnProperty(key)) {\n                instances[key].dispose();\n            }\n        }\n        instances = {};\n    }\n    function getInstance(id) {\n        return instances[id];\n    }\n    function registerPainter(name, Ctor) {\n        painterCtors[name] = Ctor;\n    }\n    var version = '5.4.1';\n\n    var zrender = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init,\n        dispose: dispose,\n        disposeAll: disposeAll,\n        getInstance: getInstance,\n        registerPainter: registerPainter,\n        version: version\n    });\n\n    var RADIAN_EPSILON = 1e-4; // Although chrome already enlarge this number to 100 for `toFixed`, but\n    // we sill follow the spec for compatibility.\n\n    var ROUND_SUPPORTED_PRECISION_MAX = 20;\n\n    function _trim(str) {\n      return str.replace(/^\\s+|\\s+$/g, '');\n    }\n    /**\n     * Linear mapping a value from domain to range\n     * @param  val\n     * @param  domain Domain extent domain[0] can be bigger than domain[1]\n     * @param  range  Range extent range[0] can be bigger than range[1]\n     * @param  clamp Default to be false\n     */\n\n\n    function linearMap(val, domain, range, clamp) {\n      var d0 = domain[0];\n      var d1 = domain[1];\n      var r0 = range[0];\n      var r1 = range[1];\n      var subDomain = d1 - d0;\n      var subRange = r1 - r0;\n\n      if (subDomain === 0) {\n        return subRange === 0 ? r0 : (r0 + r1) / 2;\n      } // Avoid accuracy problem in edge, such as\n      // 146.39 - 62.83 === 83.55999999999999.\n      // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError\n      // It is a little verbose for efficiency considering this method\n      // is a hotspot.\n\n\n      if (clamp) {\n        if (subDomain > 0) {\n          if (val <= d0) {\n            return r0;\n          } else if (val >= d1) {\n            return r1;\n          }\n        } else {\n          if (val >= d0) {\n            return r0;\n          } else if (val <= d1) {\n            return r1;\n          }\n        }\n      } else {\n        if (val === d0) {\n          return r0;\n        }\n\n        if (val === d1) {\n          return r1;\n        }\n      }\n\n      return (val - d0) / subDomain * subRange + r0;\n    }\n    /**\n     * Convert a percent string to absolute number.\n     * Returns NaN if percent is not a valid string or number\n     */\n\n    function parsePercent$1(percent, all) {\n      switch (percent) {\n        case 'center':\n        case 'middle':\n          percent = '50%';\n          break;\n\n        case 'left':\n        case 'top':\n          percent = '0%';\n          break;\n\n        case 'right':\n        case 'bottom':\n          percent = '100%';\n          break;\n      }\n\n      if (isString(percent)) {\n        if (_trim(percent).match(/%$/)) {\n          return parseFloat(percent) / 100 * all;\n        }\n\n        return parseFloat(percent);\n      }\n\n      return percent == null ? NaN : +percent;\n    }\n    function round(x, precision, returnStr) {\n      if (precision == null) {\n        precision = 10;\n      } // Avoid range error\n\n\n      precision = Math.min(Math.max(0, precision), ROUND_SUPPORTED_PRECISION_MAX); // PENDING: 1.005.toFixed(2) is '1.00' rather than '1.01'\n\n      x = (+x).toFixed(precision);\n      return returnStr ? x : +x;\n    }\n    /**\n     * Inplacd asc sort arr.\n     * The input arr will be modified.\n     */\n\n    function asc(arr) {\n      arr.sort(function (a, b) {\n        return a - b;\n      });\n      return arr;\n    }\n    /**\n     * Get precision.\n     */\n\n    function getPrecision(val) {\n      val = +val;\n\n      if (isNaN(val)) {\n        return 0;\n      } // It is much faster than methods converting number to string as follows\n      //      let tmp = val.toString();\n      //      return tmp.length - 1 - tmp.indexOf('.');\n      // especially when precision is low\n      // Notice:\n      // (1) If the loop count is over about 20, it is slower than `getPrecisionSafe`.\n      //     (see https://jsbench.me/2vkpcekkvw/1)\n      // (2) If the val is less than for example 1e-15, the result may be incorrect.\n      //     (see test/ut/spec/util/number.test.ts `getPrecision_equal_random`)\n\n\n      if (val > 1e-14) {\n        var e = 1;\n\n        for (var i = 0; i < 15; i++, e *= 10) {\n          if (Math.round(val * e) / e === val) {\n            return i;\n          }\n        }\n      }\n\n      return getPrecisionSafe(val);\n    }\n    /**\n     * Get precision with slow but safe method\n     */\n\n    function getPrecisionSafe(val) {\n      // toLowerCase for: '3.4E-12'\n      var str = val.toString().toLowerCase(); // Consider scientific notation: '3.4e-12' '3.4e+12'\n\n      var eIndex = str.indexOf('e');\n      var exp = eIndex > 0 ? +str.slice(eIndex + 1) : 0;\n      var significandPartLen = eIndex > 0 ? eIndex : str.length;\n      var dotIndex = str.indexOf('.');\n      var decimalPartLen = dotIndex < 0 ? 0 : significandPartLen - 1 - dotIndex;\n      return Math.max(0, decimalPartLen - exp);\n    }\n    /**\n     * Minimal dicernible data precisioin according to a single pixel.\n     */\n\n    function getPixelPrecision(dataExtent, pixelExtent) {\n      var log = Math.log;\n      var LN10 = Math.LN10;\n      var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);\n      var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); // toFixed() digits argument must be between 0 and 20.\n\n      var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);\n      return !isFinite(precision) ? 20 : precision;\n    }\n    /**\n     * Get a data of given precision, assuring the sum of percentages\n     * in valueList is 1.\n     * The largest remainder method is used.\n     * https://en.wikipedia.org/wiki/Largest_remainder_method\n     *\n     * @param valueList a list of all data\n     * @param idx index of the data to be processed in valueList\n     * @param precision integer number showing digits of precision\n     * @return percent ranging from 0 to 100\n     */\n\n    function getPercentWithPrecision(valueList, idx, precision) {\n      if (!valueList[idx]) {\n        return 0;\n      }\n\n      var seats = getPercentSeats(valueList, precision);\n      return seats[idx] || 0;\n    }\n    /**\n     * Get a data of given precision, assuring the sum of percentages\n     * in valueList is 1.\n     * The largest remainder method is used.\n     * https://en.wikipedia.org/wiki/Largest_remainder_method\n     *\n     * @param valueList a list of all data\n     * @param precision integer number showing digits of precision\n     * @return {Array<number>}\n     */\n\n    function getPercentSeats(valueList, precision) {\n      var sum = reduce(valueList, function (acc, val) {\n        return acc + (isNaN(val) ? 0 : val);\n      }, 0);\n\n      if (sum === 0) {\n        return [];\n      }\n\n      var digits = Math.pow(10, precision);\n      var votesPerQuota = map(valueList, function (val) {\n        return (isNaN(val) ? 0 : val) / sum * digits * 100;\n      });\n      var targetSeats = digits * 100;\n      var seats = map(votesPerQuota, function (votes) {\n        // Assign automatic seats.\n        return Math.floor(votes);\n      });\n      var currentSum = reduce(seats, function (acc, val) {\n        return acc + val;\n      }, 0);\n      var remainder = map(votesPerQuota, function (votes, idx) {\n        return votes - seats[idx];\n      }); // Has remainding votes.\n\n      while (currentSum < targetSeats) {\n        // Find next largest remainder.\n        var max = Number.NEGATIVE_INFINITY;\n        var maxId = null;\n\n        for (var i = 0, len = remainder.length; i < len; ++i) {\n          if (remainder[i] > max) {\n            max = remainder[i];\n            maxId = i;\n          }\n        } // Add a vote to max remainder.\n\n\n        ++seats[maxId];\n        remainder[maxId] = 0;\n        ++currentSum;\n      }\n\n      return map(seats, function (seat) {\n        return seat / digits;\n      });\n    }\n    /**\n     * Solve the floating point adding problem like 0.1 + 0.2 === 0.30000000000000004\n     * See <http://0.30000000000000004.com/>\n     */\n\n    function addSafe(val0, val1) {\n      var maxPrecision = Math.max(getPrecision(val0), getPrecision(val1)); // const multiplier = Math.pow(10, maxPrecision);\n      // return (Math.round(val0 * multiplier) + Math.round(val1 * multiplier)) / multiplier;\n\n      var sum = val0 + val1; // // PENDING: support more?\n\n      return maxPrecision > ROUND_SUPPORTED_PRECISION_MAX ? sum : round(sum, maxPrecision);\n    } // Number.MAX_SAFE_INTEGER, ie do not support.\n\n    var MAX_SAFE_INTEGER = 9007199254740991;\n    /**\n     * To 0 - 2 * PI, considering negative radian.\n     */\n\n    function remRadian(radian) {\n      var pi2 = Math.PI * 2;\n      return (radian % pi2 + pi2) % pi2;\n    }\n    /**\n     * @param {type} radian\n     * @return {boolean}\n     */\n\n    function isRadianAroundZero(val) {\n      return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;\n    } // eslint-disable-next-line\n\n    var TIME_REG = /^(?:(\\d{4})(?:[-\\/](\\d{1,2})(?:[-\\/](\\d{1,2})(?:[T ](\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2})(?:[.,](\\d+))?)?)?(Z|[\\+\\-]\\d\\d:?\\d\\d)?)?)?)?)?$/; // jshint ignore:line\n\n    /**\n     * @param value valid type: number | string | Date, otherwise return `new Date(NaN)`\n     *   These values can be accepted:\n     *   + An instance of Date, represent a time in its own time zone.\n     *   + Or string in a subset of ISO 8601, only including:\n     *     + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',\n     *     + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',\n     *     + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',\n     *     all of which will be treated as local time if time zone is not specified\n     *     (see <https://momentjs.com/>).\n     *   + Or other string format, including (all of which will be treated as local time):\n     *     '2012', '2012-3-1', '2012/3/1', '2012/03/01',\n     *     '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'\n     *   + a timestamp, which represent a time in UTC.\n     * @return date Never be null/undefined. If invalid, return `new Date(NaN)`.\n     */\n\n    function parseDate(value) {\n      if (value instanceof Date) {\n        return value;\n      } else if (isString(value)) {\n        // Different browsers parse date in different way, so we parse it manually.\n        // Some other issues:\n        // new Date('1970-01-01') is UTC,\n        // new Date('1970/01/01') and new Date('1970-1-01') is local.\n        // See issue #3623\n        var match = TIME_REG.exec(value);\n\n        if (!match) {\n          // return Invalid Date.\n          return new Date(NaN);\n        } // Use local time when no timezone offset is specified.\n\n\n        if (!match[8]) {\n          // match[n] can only be string or undefined.\n          // But take care of '12' + 1 => '121'.\n          return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0);\n        } // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,\n        // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).\n        // For example, system timezone is set as \"Time Zone: America/Toronto\",\n        // then these code will get different result:\n        // `new Date(1478411999999).getTimezoneOffset();  // get 240`\n        // `new Date(1478412000000).getTimezoneOffset();  // get 300`\n        // So we should not use `new Date`, but use `Date.UTC`.\n        else {\n            var hour = +match[4] || 0;\n\n            if (match[8].toUpperCase() !== 'Z') {\n              hour -= +match[8].slice(0, 3);\n            }\n\n            return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0));\n          }\n      } else if (value == null) {\n        return new Date(NaN);\n      }\n\n      return new Date(Math.round(value));\n    }\n    /**\n     * Quantity of a number. e.g. 0.1, 1, 10, 100\n     *\n     * @param val\n     * @return\n     */\n\n    function quantity(val) {\n      return Math.pow(10, quantityExponent(val));\n    }\n    /**\n     * Exponent of the quantity of a number\n     * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3\n     *\n     * @param val non-negative value\n     * @return\n     */\n\n    function quantityExponent(val) {\n      if (val === 0) {\n        return 0;\n      }\n\n      var exp = Math.floor(Math.log(val) / Math.LN10);\n      /**\n       * exp is expected to be the rounded-down result of the base-10 log of val.\n       * But due to the precision loss with Math.log(val), we need to restore it\n       * using 10^exp to make sure we can get val back from exp. #11249\n       */\n\n      if (val / Math.pow(10, exp) >= 10) {\n        exp++;\n      }\n\n      return exp;\n    }\n    /**\n     * find a “nice” number approximately equal to x. Round the number if round = true,\n     * take ceiling if round = false. The primary observation is that the “nicest”\n     * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.\n     *\n     * See \"Nice Numbers for Graph Labels\" of Graphic Gems.\n     *\n     * @param  val Non-negative value.\n     * @param  round\n     * @return Niced number\n     */\n\n    function nice(val, round) {\n      var exponent = quantityExponent(val);\n      var exp10 = Math.pow(10, exponent);\n      var f = val / exp10; // 1 <= f < 10\n\n      var nf;\n\n      if (round) {\n        if (f < 1.5) {\n          nf = 1;\n        } else if (f < 2.5) {\n          nf = 2;\n        } else if (f < 4) {\n          nf = 3;\n        } else if (f < 7) {\n          nf = 5;\n        } else {\n          nf = 10;\n        }\n      } else {\n        if (f < 1) {\n          nf = 1;\n        } else if (f < 2) {\n          nf = 2;\n        } else if (f < 3) {\n          nf = 3;\n        } else if (f < 5) {\n          nf = 5;\n        } else {\n          nf = 10;\n        }\n      }\n\n      val = nf * exp10; // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).\n      // 20 is the uppper bound of toFixed.\n\n      return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;\n    }\n    /**\n     * This code was copied from \"d3.js\"\n     * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.\n     * See the license statement at the head of this file.\n     * @param ascArr\n     */\n\n    function quantile(ascArr, p) {\n      var H = (ascArr.length - 1) * p + 1;\n      var h = Math.floor(H);\n      var v = +ascArr[h - 1];\n      var e = H - h;\n      return e ? v + e * (ascArr[h] - v) : v;\n    }\n    /**\n     * Order intervals asc, and split them when overlap.\n     * expect(numberUtil.reformIntervals([\n     *     {interval: [18, 62], close: [1, 1]},\n     *     {interval: [-Infinity, -70], close: [0, 0]},\n     *     {interval: [-70, -26], close: [1, 1]},\n     *     {interval: [-26, 18], close: [1, 1]},\n     *     {interval: [62, 150], close: [1, 1]},\n     *     {interval: [106, 150], close: [1, 1]},\n     *     {interval: [150, Infinity], close: [0, 0]}\n     * ])).toEqual([\n     *     {interval: [-Infinity, -70], close: [0, 0]},\n     *     {interval: [-70, -26], close: [1, 1]},\n     *     {interval: [-26, 18], close: [0, 1]},\n     *     {interval: [18, 62], close: [0, 1]},\n     *     {interval: [62, 150], close: [0, 1]},\n     *     {interval: [150, Infinity], close: [0, 0]}\n     * ]);\n     * @param list, where `close` mean open or close\n     *        of the interval, and Infinity can be used.\n     * @return The origin list, which has been reformed.\n     */\n\n    function reformIntervals(list) {\n      list.sort(function (a, b) {\n        return littleThan(a, b, 0) ? -1 : 1;\n      });\n      var curr = -Infinity;\n      var currClose = 1;\n\n      for (var i = 0; i < list.length;) {\n        var interval = list[i].interval;\n        var close_1 = list[i].close;\n\n        for (var lg = 0; lg < 2; lg++) {\n          if (interval[lg] <= curr) {\n            interval[lg] = curr;\n            close_1[lg] = !lg ? 1 - currClose : 1;\n          }\n\n          curr = interval[lg];\n          currClose = close_1[lg];\n        }\n\n        if (interval[0] === interval[1] && close_1[0] * close_1[1] !== 1) {\n          list.splice(i, 1);\n        } else {\n          i++;\n        }\n      }\n\n      return list;\n\n      function littleThan(a, b, lg) {\n        return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1));\n      }\n    }\n    /**\n     * [Numeric is defined as]:\n     *     `parseFloat(val) == val`\n     * For example:\n     * numeric:\n     *     typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity,\n     *     and they rounded by white-spaces or line-terminal like ' -123 \\n ' (see es spec)\n     * not-numeric:\n     *     null, undefined, [], {}, true, false, 'NaN', NaN, '123ab',\n     *     empty string, string with only white-spaces or line-terminal (see es spec),\n     *     0x12, '0x12', '-0x12', 012, '012', '-012',\n     *     non-string, ...\n     *\n     * @test See full test cases in `test/ut/spec/util/number.js`.\n     * @return Must be a typeof number. If not numeric, return NaN.\n     */\n\n    function numericToNumber(val) {\n      var valFloat = parseFloat(val);\n      return valFloat == val // eslint-disable-line eqeqeq\n      && (valFloat !== 0 || !isString(val) || val.indexOf('x') <= 0) // For case ' 0x0 '.\n      ? valFloat : NaN;\n    }\n    /**\n     * Definition of \"numeric\": see `numericToNumber`.\n     */\n\n    function isNumeric(val) {\n      return !isNaN(numericToNumber(val));\n    }\n    /**\n     * Use random base to prevent users hard code depending on\n     * this auto generated marker id.\n     * @return An positive integer.\n     */\n\n    function getRandomIdBase() {\n      return Math.round(Math.random() * 9);\n    }\n    /**\n     * Get the greatest common divisor.\n     *\n     * @param {number} a one number\n     * @param {number} b the other number\n     */\n\n    function getGreatestCommonDividor(a, b) {\n      if (b === 0) {\n        return a;\n      }\n\n      return getGreatestCommonDividor(b, a % b);\n    }\n    /**\n     * Get the least common multiple.\n     *\n     * @param {number} a one number\n     * @param {number} b the other number\n     */\n\n    function getLeastCommonMultiple(a, b) {\n      if (a == null) {\n        return b;\n      }\n\n      if (b == null) {\n        return a;\n      }\n\n      return a * b / getGreatestCommonDividor(a, b);\n    }\n\n    var ECHARTS_PREFIX = '[ECharts] ';\n    var storedLogs = {};\n    var hasConsole = typeof console !== 'undefined' // eslint-disable-next-line\n    && console.warn && console.log;\n\n    function outputLog(type, str, onlyOnce) {\n      if (hasConsole) {\n        if (onlyOnce) {\n          if (storedLogs[str]) {\n            return;\n          }\n\n          storedLogs[str] = true;\n        } // eslint-disable-next-line\n\n\n        console[type](ECHARTS_PREFIX + str);\n      }\n    }\n\n    function log(str, onlyOnce) {\n      outputLog('log', str, onlyOnce);\n    }\n    function warn(str, onlyOnce) {\n      outputLog('warn', str, onlyOnce);\n    }\n    function error(str, onlyOnce) {\n      outputLog('error', str, onlyOnce);\n    }\n    function deprecateLog(str) {\n      if (\"development\" !== 'production') {\n        // Not display duplicate message.\n        outputLog('warn', 'DEPRECATED: ' + str, true);\n      }\n    }\n    function deprecateReplaceLog(oldOpt, newOpt, scope) {\n      if (\"development\" !== 'production') {\n        deprecateLog((scope ? \"[\" + scope + \"]\" : '') + (oldOpt + \" is deprecated, use \" + newOpt + \" instead.\"));\n      }\n    }\n    /**\n     * If in __DEV__ environment, get console printable message for users hint.\n     * Parameters are separated by ' '.\n     * @usage\n     * makePrintable('This is an error on', someVar, someObj);\n     *\n     * @param hintInfo anything about the current execution context to hint users.\n     * @throws Error\n     */\n\n    function makePrintable() {\n      var hintInfo = [];\n\n      for (var _i = 0; _i < arguments.length; _i++) {\n        hintInfo[_i] = arguments[_i];\n      }\n\n      var msg = '';\n\n      if (\"development\" !== 'production') {\n        // Fuzzy stringify for print.\n        // This code only exist in dev environment.\n        var makePrintableStringIfPossible_1 = function (val) {\n          return val === void 0 ? 'undefined' : val === Infinity ? 'Infinity' : val === -Infinity ? '-Infinity' : eqNaN(val) ? 'NaN' : val instanceof Date ? 'Date(' + val.toISOString() + ')' : isFunction(val) ? 'function () { ... }' : isRegExp(val) ? val + '' : null;\n        };\n\n        msg = map(hintInfo, function (arg) {\n          if (isString(arg)) {\n            // Print without quotation mark for some statement.\n            return arg;\n          } else {\n            var printableStr = makePrintableStringIfPossible_1(arg);\n\n            if (printableStr != null) {\n              return printableStr;\n            } else if (typeof JSON !== 'undefined' && JSON.stringify) {\n              try {\n                return JSON.stringify(arg, function (n, val) {\n                  var printableStr = makePrintableStringIfPossible_1(val);\n                  return printableStr == null ? val : printableStr;\n                }); // In most cases the info object is small, so do not line break.\n              } catch (err) {\n                return '?';\n              }\n            } else {\n              return '?';\n            }\n          }\n        }).join(' ');\n      }\n\n      return msg;\n    }\n    /**\n     * @throws Error\n     */\n\n    function throwError(msg) {\n      throw new Error(msg);\n    }\n\n    function interpolateNumber$1(p0, p1, percent) {\n      return (p1 - p0) * percent + p0;\n    }\n    /**\n     * Make the name displayable. But we should\n     * make sure it is not duplicated with user\n     * specified name, so use '\\0';\n     */\n\n\n    var DUMMY_COMPONENT_NAME_PREFIX = 'series\\0';\n    var INTERNAL_COMPONENT_ID_PREFIX = '\\0_ec_\\0';\n    /**\n     * If value is not array, then translate it to array.\n     * @param  {*} value\n     * @return {Array} [value] or value\n     */\n\n    function normalizeToArray(value) {\n      return value instanceof Array ? value : value == null ? [] : [value];\n    }\n    /**\n     * Sync default option between normal and emphasis like `position` and `show`\n     * In case some one will write code like\n     *     label: {\n     *          show: false,\n     *          position: 'outside',\n     *          fontSize: 18\n     *     },\n     *     emphasis: {\n     *          label: { show: true }\n     *     }\n     */\n\n    function defaultEmphasis(opt, key, subOpts) {\n      // Caution: performance sensitive.\n      if (opt) {\n        opt[key] = opt[key] || {};\n        opt.emphasis = opt.emphasis || {};\n        opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal\n\n        for (var i = 0, len = subOpts.length; i < len; i++) {\n          var subOptName = subOpts[i];\n\n          if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) {\n            opt.emphasis[key][subOptName] = opt[key][subOptName];\n          }\n        }\n      }\n    }\n    var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([\n    //     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',\n    //     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',\n    //     // FIXME: deprecated, check and remove it.\n    //     'textStyle'\n    // ]);\n\n    /**\n     * The method does not ensure performance.\n     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n     * This helper method retrieves value from data.\n     */\n\n    function getDataItemValue(dataItem) {\n      return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;\n    }\n    /**\n     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n     * This helper method determine if dataItem has extra option besides value\n     */\n\n    function isDataItemOption(dataItem) {\n      return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array\n      // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));\n    }\n    /**\n     * Mapping to existings for merge.\n     *\n     * Mode \"normalMege\":\n     *     The mapping result (merge result) will keep the order of the existing\n     *     component, rather than the order of new option. Because we should ensure\n     *     some specified index reference (like xAxisIndex) keep work.\n     *     And in most cases, \"merge option\" is used to update partial option but not\n     *     be expected to change the order.\n     *\n     * Mode \"replaceMege\":\n     *     (1) Only the id mapped components will be merged.\n     *     (2) Other existing components (except internal components) will be removed.\n     *     (3) Other new options will be used to create new component.\n     *     (4) The index of the existing components will not be modified.\n     *     That means their might be \"hole\" after the removal.\n     *     The new components are created first at those available index.\n     *\n     * Mode \"replaceAll\":\n     *     This mode try to support that reproduce an echarts instance from another\n     *     echarts instance (via `getOption`) in some simple cases.\n     *     In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`,\n     *     which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is,\n     *     the \"hole\" in `newCmptOptions` will also be kept.\n     *     On the contrary, other modes try best to eliminate holes.\n     *     PENDING: This is an experimental mode yet.\n     *\n     * @return See the comment of <MappingResult>.\n     */\n\n    function mappingToExists(existings, newCmptOptions, mode) {\n      var isNormalMergeMode = mode === 'normalMerge';\n      var isReplaceMergeMode = mode === 'replaceMerge';\n      var isReplaceAllMode = mode === 'replaceAll';\n      existings = existings || [];\n      newCmptOptions = (newCmptOptions || []).slice();\n      var existingIdIdxMap = createHashMap(); // Validate id and name on user input option.\n\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!isObject(cmptOption)) {\n          newCmptOptions[index] = null;\n          return;\n        }\n\n        if (\"development\" !== 'production') {\n          // There is some legacy case that name is set as `false`.\n          // But should work normally rather than throw error.\n          if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) {\n            warnInvalidateIdOrName(cmptOption.id);\n          }\n\n          if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) {\n            warnInvalidateIdOrName(cmptOption.name);\n          }\n        }\n      });\n      var result = prepareResult(existings, existingIdIdxMap, mode);\n\n      if (isNormalMergeMode || isReplaceMergeMode) {\n        mappingById(result, existings, existingIdIdxMap, newCmptOptions);\n      }\n\n      if (isNormalMergeMode) {\n        mappingByName(result, newCmptOptions);\n      }\n\n      if (isNormalMergeMode || isReplaceMergeMode) {\n        mappingByIndex(result, newCmptOptions, isReplaceMergeMode);\n      } else if (isReplaceAllMode) {\n        mappingInReplaceAllMode(result, newCmptOptions);\n      }\n\n      makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the\n      // forEach will omit those items and result in incorrect result.\n\n      return result;\n    }\n\n    function prepareResult(existings, existingIdIdxMap, mode) {\n      var result = [];\n\n      if (mode === 'replaceAll') {\n        return result;\n      } // Do not use native `map` to in case that the array `existings`\n      // contains elided items, which will be omitted.\n\n\n      for (var index = 0; index < existings.length; index++) {\n        var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined.\n\n        if (existing && existing.id != null) {\n          existingIdIdxMap.set(existing.id, index);\n        } // For non-internal-componnets:\n        //     Mode \"normalMerge\": all existings kept.\n        //     Mode \"replaceMerge\": all existing removed unless mapped by id.\n        // For internal-components:\n        //     go with \"replaceMerge\" approach in both mode.\n\n\n        result.push({\n          existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing,\n          newOption: null,\n          keyInfo: null,\n          brandNew: null\n        });\n      }\n\n      return result;\n    }\n\n    function mappingById(result, existings, existingIdIdxMap, newCmptOptions) {\n      // Mapping by id if specified.\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!cmptOption || cmptOption.id == null) {\n          return;\n        }\n\n        var optionId = makeComparableKey(cmptOption.id);\n        var existingIdx = existingIdIdxMap.get(optionId);\n\n        if (existingIdx != null) {\n          var resultItem = result[existingIdx];\n          assert(!resultItem.newOption, 'Duplicated option on id \"' + optionId + '\".');\n          resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to\n          // the existings rather than creating new component model.\n\n          resultItem.existing = existings[existingIdx];\n          newCmptOptions[index] = null;\n        }\n      });\n    }\n\n    function mappingByName(result, newCmptOptions) {\n      // Mapping by name if specified.\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!cmptOption || cmptOption.name == null) {\n          return;\n        }\n\n        for (var i = 0; i < result.length; i++) {\n          var existing = result[i].existing;\n\n          if (!result[i].newOption // Consider name: two map to one.\n          // Can not match when both ids existing but different.\n          && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {\n            result[i].newOption = cmptOption;\n            newCmptOptions[index] = null;\n            return;\n          }\n        }\n      });\n    }\n\n    function mappingByIndex(result, newCmptOptions, brandNew) {\n      each(newCmptOptions, function (cmptOption) {\n        if (!cmptOption) {\n          return;\n        } // Find the first place that not mapped by id and not internal component (consider the \"hole\").\n\n\n        var resultItem;\n        var nextIdx = 0;\n\n        while ( // Be `!resultItem` only when `nextIdx >= result.length`.\n        (resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because\n        // after mapping performed, model will always be assigned with an id if user not given.\n        // After that all models have id.\n        // (2) If new option has id, it can only set to a hole or append to the last. It should\n        // not be merged to the existings with different id. Because id should not be overwritten.\n        // (3) Name can be overwritten, because axis use name as 'show label text'.\n        resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode \"replaceMerge\", here no not-mapped-non-internal-existing.\n        resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {\n          nextIdx++;\n        }\n\n        if (resultItem) {\n          resultItem.newOption = cmptOption;\n          resultItem.brandNew = brandNew;\n        } else {\n          result.push({\n            newOption: cmptOption,\n            brandNew: brandNew,\n            existing: null,\n            keyInfo: null\n          });\n        }\n\n        nextIdx++;\n      });\n    }\n\n    function mappingInReplaceAllMode(result, newCmptOptions) {\n      each(newCmptOptions, function (cmptOption) {\n        // The feature \"reproduce\" requires \"hole\" will also reproduced\n        // in case that component index referring are broken.\n        result.push({\n          newOption: cmptOption,\n          brandNew: true,\n          existing: null,\n          keyInfo: null\n        });\n      });\n    }\n    /**\n     * Make id and name for mapping result (result of mappingToExists)\n     * into `keyInfo` field.\n     */\n\n\n    function makeIdAndName(mapResult) {\n      // We use this id to hash component models and view instances\n      // in echarts. id can be specified by user, or auto generated.\n      // The id generation rule ensures new view instance are able\n      // to mapped to old instance when setOption are called in\n      // no-merge mode. So we generate model id by name and plus\n      // type in view id.\n      // name can be duplicated among components, which is convenient\n      // to specify multi components (like series) by one name.\n      // Ensure that each id is distinct.\n      var idMap = createHashMap();\n      each(mapResult, function (item) {\n        var existing = item.existing;\n        existing && idMap.set(existing.id, item);\n      });\n      each(mapResult, function (item) {\n        var opt = item.newOption; // Force ensure id not duplicated.\n\n        assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));\n        opt && opt.id != null && idMap.set(opt.id, item);\n        !item.keyInfo && (item.keyInfo = {});\n      }); // Make name and id.\n\n      each(mapResult, function (item, index) {\n        var existing = item.existing;\n        var opt = item.newOption;\n        var keyInfo = item.keyInfo;\n\n        if (!isObject(opt)) {\n          return;\n        } // Name can be overwritten. Consider case: axis.name = '20km'.\n        // But id generated by name will not be changed, which affect\n        // only in that case: setOption with 'not merge mode' and view\n        // instance will be recreated, which can be accepted.\n\n\n        keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid that different series has the same name,\n        // because name may be used like in color pallet.\n        : DUMMY_COMPONENT_NAME_PREFIX + index;\n\n        if (existing) {\n          keyInfo.id = makeComparableKey(existing.id);\n        } else if (opt.id != null) {\n          keyInfo.id = makeComparableKey(opt.id);\n        } else {\n          // Consider this situatoin:\n          //  optionA: [{name: 'a'}, {name: 'a'}, {..}]\n          //  optionB [{..}, {name: 'a'}, {name: 'a'}]\n          // Series with the same name between optionA and optionB\n          // should be mapped.\n          var idNum = 0;\n\n          do {\n            keyInfo.id = '\\0' + keyInfo.name + '\\0' + idNum++;\n          } while (idMap.get(keyInfo.id));\n        }\n\n        idMap.set(keyInfo.id, item);\n      });\n    }\n\n    function keyExistAndEqual(attr, obj1, obj2) {\n      var key1 = convertOptionIdName(obj1[attr], null);\n      var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number.\n\n      return key1 != null && key2 != null && key1 === key2;\n    }\n    /**\n     * @return return null if not exist.\n     */\n\n\n    function makeComparableKey(val) {\n      if (\"development\" !== 'production') {\n        if (val == null) {\n          throw new Error();\n        }\n      }\n\n      return convertOptionIdName(val, '');\n    }\n\n    function convertOptionIdName(idOrName, defaultValue) {\n      if (idOrName == null) {\n        return defaultValue;\n      }\n\n      return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue;\n    }\n\n    function warnInvalidateIdOrName(idOrName) {\n      if (\"development\" !== 'production') {\n        warn('`' + idOrName + '` is invalid id or name. Must be a string or number.');\n      }\n    }\n\n    function isValidIdOrName(idOrName) {\n      return isStringSafe(idOrName) || isNumeric(idOrName);\n    }\n\n    function isNameSpecified(componentModel) {\n      var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.\n\n      return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));\n    }\n    /**\n     * @public\n     * @param {Object} cmptOption\n     * @return {boolean}\n     */\n\n    function isComponentIdInternal(cmptOption) {\n      return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;\n    }\n    function makeInternalComponentId(idSuffix) {\n      return INTERNAL_COMPONENT_ID_PREFIX + idSuffix;\n    }\n    function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) {\n      // Set mainType and complete subType.\n      each(mappingResult, function (item) {\n        var newOption = item.newOption;\n\n        if (isObject(newOption)) {\n          item.keyInfo.mainType = mainType;\n          item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor);\n        }\n      });\n    }\n\n    function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) {\n      var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.\n      : componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType\n\n      return subType;\n    }\n    /**\n     * A helper for removing duplicate items between batchA and batchB,\n     * and in themselves, and categorize by series.\n     *\n     * @param batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n     * @param batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n     * @return result: [resultBatchA, resultBatchB]\n     */\n\n\n    function compressBatches(batchA, batchB) {\n      var mapA = {};\n      var mapB = {};\n      makeMap(batchA || [], mapA);\n      makeMap(batchB || [], mapB, mapA);\n      return [mapToArray(mapA), mapToArray(mapB)];\n\n      function makeMap(sourceBatch, map, otherMap) {\n        for (var i = 0, len = sourceBatch.length; i < len; i++) {\n          var seriesId = convertOptionIdName(sourceBatch[i].seriesId, null);\n\n          if (seriesId == null) {\n            return;\n          }\n\n          var dataIndices = normalizeToArray(sourceBatch[i].dataIndex);\n          var otherDataIndices = otherMap && otherMap[seriesId];\n\n          for (var j = 0, lenj = dataIndices.length; j < lenj; j++) {\n            var dataIndex = dataIndices[j];\n\n            if (otherDataIndices && otherDataIndices[dataIndex]) {\n              otherDataIndices[dataIndex] = null;\n            } else {\n              (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1;\n            }\n          }\n        }\n      }\n\n      function mapToArray(map, isData) {\n        var result = [];\n\n        for (var i in map) {\n          if (map.hasOwnProperty(i) && map[i] != null) {\n            if (isData) {\n              result.push(+i);\n            } else {\n              var dataIndices = mapToArray(map[i], true);\n              dataIndices.length && result.push({\n                seriesId: i,\n                dataIndex: dataIndices\n              });\n            }\n          }\n        }\n\n        return result;\n      }\n    }\n    /**\n     * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name\n     *                         each of which can be Array or primary type.\n     * @return dataIndex If not found, return undefined/null.\n     */\n\n    function queryDataIndex(data, payload) {\n      if (payload.dataIndexInside != null) {\n        return payload.dataIndexInside;\n      } else if (payload.dataIndex != null) {\n        return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) {\n          return data.indexOfRawIndex(value);\n        }) : data.indexOfRawIndex(payload.dataIndex);\n      } else if (payload.name != null) {\n        return isArray(payload.name) ? map(payload.name, function (value) {\n          return data.indexOfName(value);\n        }) : data.indexOfName(payload.name);\n      }\n    }\n    /**\n     * Enable property storage to any host object.\n     * Notice: Serialization is not supported.\n     *\n     * For example:\n     * let inner = zrUitl.makeInner();\n     *\n     * function some1(hostObj) {\n     *      inner(hostObj).someProperty = 1212;\n     *      ...\n     * }\n     * function some2() {\n     *      let fields = inner(this);\n     *      fields.someProperty1 = 1212;\n     *      fields.someProperty2 = 'xx';\n     *      ...\n     * }\n     *\n     * @return {Function}\n     */\n\n    function makeInner() {\n      var key = '__ec_inner_' + innerUniqueIndex++;\n      return function (hostObj) {\n        return hostObj[key] || (hostObj[key] = {});\n      };\n    }\n    var innerUniqueIndex = getRandomIdBase();\n    /**\n     * The same behavior as `component.getReferringComponents`.\n     */\n\n    function parseFinder(ecModel, finderInput, opt) {\n      var _a = preParseFinder(finderInput, opt),\n          mainTypeSpecified = _a.mainTypeSpecified,\n          queryOptionMap = _a.queryOptionMap,\n          others = _a.others;\n\n      var result = others;\n      var defaultMainType = opt ? opt.defaultMainType : null;\n\n      if (!mainTypeSpecified && defaultMainType) {\n        queryOptionMap.set(defaultMainType, {});\n      }\n\n      queryOptionMap.each(function (queryOption, mainType) {\n        var queryResult = queryReferringComponents(ecModel, mainType, queryOption, {\n          useDefault: defaultMainType === mainType,\n          enableAll: opt && opt.enableAll != null ? opt.enableAll : true,\n          enableNone: opt && opt.enableNone != null ? opt.enableNone : true\n        });\n        result[mainType + 'Models'] = queryResult.models;\n        result[mainType + 'Model'] = queryResult.models[0];\n      });\n      return result;\n    }\n    function preParseFinder(finderInput, opt) {\n      var finder;\n\n      if (isString(finderInput)) {\n        var obj = {};\n        obj[finderInput + 'Index'] = 0;\n        finder = obj;\n      } else {\n        finder = finderInput;\n      }\n\n      var queryOptionMap = createHashMap();\n      var others = {};\n      var mainTypeSpecified = false;\n      each(finder, function (value, key) {\n        // Exclude 'dataIndex' and other illgal keys.\n        if (key === 'dataIndex' || key === 'dataIndexInside') {\n          others[key] = value;\n          return;\n        }\n\n        var parsedKey = key.match(/^(\\w+)(Index|Id|Name)$/) || [];\n        var mainType = parsedKey[1];\n        var queryType = (parsedKey[2] || '').toLowerCase();\n\n        if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) {\n          return;\n        }\n\n        mainTypeSpecified = mainTypeSpecified || !!mainType;\n        var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {});\n        queryOption[queryType] = value;\n      });\n      return {\n        mainTypeSpecified: mainTypeSpecified,\n        queryOptionMap: queryOptionMap,\n        others: others\n      };\n    }\n    var SINGLE_REFERRING = {\n      useDefault: true,\n      enableAll: false,\n      enableNone: false\n    };\n    var MULTIPLE_REFERRING = {\n      useDefault: false,\n      enableAll: true,\n      enableNone: true\n    };\n    function queryReferringComponents(ecModel, mainType, userOption, opt) {\n      opt = opt || SINGLE_REFERRING;\n      var indexOption = userOption.index;\n      var idOption = userOption.id;\n      var nameOption = userOption.name;\n      var result = {\n        models: null,\n        specified: indexOption != null || idOption != null || nameOption != null\n      };\n\n      if (!result.specified) {\n        // Use the first as default if `useDefault`.\n        var firstCmpt = void 0;\n        result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : [];\n        return result;\n      }\n\n      if (indexOption === 'none' || indexOption === false) {\n        assert(opt.enableNone, '`\"none\"` or `false` is not a valid value on index option.');\n        result.models = [];\n        return result;\n      } // `queryComponents` will return all components if\n      // both all of index/id/name are null/undefined.\n\n\n      if (indexOption === 'all') {\n        assert(opt.enableAll, '`\"all\"` is not a valid value on index option.');\n        indexOption = idOption = nameOption = null;\n      }\n\n      result.models = ecModel.queryComponents({\n        mainType: mainType,\n        index: indexOption,\n        id: idOption,\n        name: nameOption\n      });\n      return result;\n    }\n    function setAttribute(dom, key, value) {\n      dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value;\n    }\n    function getAttribute(dom, key) {\n      return dom.getAttribute ? dom.getAttribute(key) : dom[key];\n    }\n    function getTooltipRenderMode(renderModeOption) {\n      if (renderModeOption === 'auto') {\n        // Using html when `document` exists, use richText otherwise\n        return env.domSupported ? 'html' : 'richText';\n      } else {\n        return renderModeOption || 'html';\n      }\n    }\n    /**\n     * Group a list by key.\n     */\n\n    function groupData(array, getKey // return key\n    ) {\n      var buckets = createHashMap();\n      var keys = [];\n      each(array, function (item) {\n        var key = getKey(item);\n        (buckets.get(key) || (keys.push(key), buckets.set(key, []))).push(item);\n      });\n      return {\n        keys: keys,\n        buckets: buckets\n      };\n    }\n    /**\n     * Interpolate raw values of a series with percent\n     *\n     * @param data         data\n     * @param labelModel   label model of the text element\n     * @param sourceValue  start value. May be null/undefined when init.\n     * @param targetValue  end value\n     * @param percent      0~1 percentage; 0 uses start value while 1 uses end value\n     * @return             interpolated values\n     *                     If `sourceValue` and `targetValue` are `number`, return `number`.\n     *                     If `sourceValue` and `targetValue` are `string`, return `string`.\n     *                     If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`.\n     *                     Other cases do not supported.\n     */\n\n    function interpolateRawValues(data, precision, sourceValue, targetValue, percent) {\n      var isAutoPrecision = precision == null || precision === 'auto';\n\n      if (targetValue == null) {\n        return targetValue;\n      }\n\n      if (isNumber(targetValue)) {\n        var value = interpolateNumber$1(sourceValue || 0, targetValue, percent);\n        return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision);\n      } else if (isString(targetValue)) {\n        return percent < 1 ? sourceValue : targetValue;\n      } else {\n        var interpolated = [];\n        var leftArr = sourceValue;\n        var rightArr = targetValue;\n        var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length);\n\n        for (var i = 0; i < length_1; ++i) {\n          var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims\n\n          if (info && info.type === 'ordinal') {\n            // In init, there is no `sourceValue`, but should better not to get undefined result.\n            interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i];\n          } else {\n            var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0;\n            var rightVal = rightArr[i];\n            var value = interpolateNumber$1(leftVal, rightVal, percent);\n            interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision);\n          }\n        }\n\n        return interpolated;\n      }\n    }\n\n    var TYPE_DELIMITER = '.';\n    var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';\n    var IS_EXTENDED_CLASS = '___EC__EXTENDED_CLASS___';\n    /**\n     * Notice, parseClassType('') should returns {main: '', sub: ''}\n     * @public\n     */\n\n    function parseClassType(componentType) {\n      var ret = {\n        main: '',\n        sub: ''\n      };\n\n      if (componentType) {\n        var typeArr = componentType.split(TYPE_DELIMITER);\n        ret.main = typeArr[0] || '';\n        ret.sub = typeArr[1] || '';\n      }\n\n      return ret;\n    }\n    /**\n     * @public\n     */\n\n    function checkClassType(componentType) {\n      assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType \"' + componentType + '\" illegal');\n    }\n\n    function isExtendedClass(clz) {\n      return !!(clz && clz[IS_EXTENDED_CLASS]);\n    }\n    /**\n     * Implements `ExtendableConstructor` for `rootClz`.\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & ExtendableConstructor\n     * enableClassExtend(Xxx as XxxConstructor);\n     * ```\n     */\n\n    function enableClassExtend(rootClz, mandatoryMethods) {\n      rootClz.$constructor = rootClz; // FIXME: not necessary?\n\n      rootClz.extend = function (proto) {\n        if (\"development\" !== 'production') {\n          each(mandatoryMethods, function (method) {\n            if (!proto[method]) {\n              console.warn('Method `' + method + '` should be implemented' + (proto.type ? ' in ' + proto.type : '') + '.');\n            }\n          });\n        }\n\n        var superClass = this;\n        var ExtendedClass;\n\n        if (isESClass(superClass)) {\n          ExtendedClass =\n          /** @class */\n          function (_super) {\n            __extends(class_1, _super);\n\n            function class_1() {\n              return _super.apply(this, arguments) || this;\n            }\n\n            return class_1;\n          }(superClass);\n        } else {\n          // For backward compat, we both support ts class inheritance and this\n          // \"extend\" approach.\n          // The constructor should keep the same behavior as ts class inheritance:\n          // If this constructor/$constructor is not declared, auto invoke the super\n          // constructor.\n          // If this constructor/$constructor is declared, it is responsible for\n          // calling the super constructor.\n          ExtendedClass = function () {\n            (proto.$constructor || superClass).apply(this, arguments);\n          };\n\n          inherits(ExtendedClass, this);\n        }\n\n        extend(ExtendedClass.prototype, proto);\n        ExtendedClass[IS_EXTENDED_CLASS] = true;\n        ExtendedClass.extend = this.extend;\n        ExtendedClass.superCall = superCall;\n        ExtendedClass.superApply = superApply;\n        ExtendedClass.superClass = superClass;\n        return ExtendedClass;\n      };\n    }\n\n    function isESClass(fn) {\n      return isFunction(fn) && /^class\\s/.test(Function.prototype.toString.call(fn));\n    }\n    /**\n     * A work around to both support ts extend and this extend mechanism.\n     * on sub-class.\n     * @usage\n     * ```ts\n     * class Component { ... }\n     * classUtil.enableClassExtend(Component);\n     * classUtil.enableClassManagement(Component, {registerWhenExtend: true});\n     *\n     * class Series extends Component { ... }\n     * // Without calling `markExtend`, `registerWhenExtend` will not work.\n     * Component.markExtend(Series);\n     * ```\n     */\n\n\n    function mountExtend(SubClz, SupperClz) {\n      SubClz.extend = SupperClz.extend;\n    } // A random offset.\n\n    var classBase = Math.round(Math.random() * 10);\n    /**\n     * Implements `CheckableConstructor` for `target`.\n     * Can not use instanceof, consider different scope by\n     * cross domain or es module import in ec extensions.\n     * Mount a method \"isInstance()\" to Clz.\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & CheckableConstructor;\n     * enableClassCheck(Xxx as XxxConstructor)\n     * ```\n     */\n\n    function enableClassCheck(target) {\n      var classAttr = ['__\\0is_clz', classBase++].join('_');\n      target.prototype[classAttr] = true;\n\n      if (\"development\" !== 'production') {\n        assert(!target.isInstance, 'The method \"is\" can not be defined.');\n      }\n\n      target.isInstance = function (obj) {\n        return !!(obj && obj[classAttr]);\n      };\n    } // superCall should have class info, which can not be fetched from 'this'.\n    // Consider this case:\n    // class A has method f,\n    // class B inherits class A, overrides method f, f call superApply('f'),\n    // class C inherits class B, does not override method f,\n    // then when method of class C is called, dead loop occurred.\n\n    function superCall(context, methodName) {\n      var args = [];\n\n      for (var _i = 2; _i < arguments.length; _i++) {\n        args[_i - 2] = arguments[_i];\n      }\n\n      return this.superClass.prototype[methodName].apply(context, args);\n    }\n\n    function superApply(context, methodName, args) {\n      return this.superClass.prototype[methodName].apply(context, args);\n    }\n    /**\n     * Implements `ClassManager` for `target`\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & ClassManager\n     * enableClassManagement(Xxx as XxxConstructor);\n     * ```\n     */\n\n\n    function enableClassManagement(target) {\n      /**\n       * Component model classes\n       * key: componentType,\n       * value:\n       *     componentClass, when componentType is 'a'\n       *     or Object.<subKey, componentClass>, when componentType is 'a.b'\n       */\n      var storage = {};\n\n      target.registerClass = function (clz) {\n        // `type` should not be a \"instance member\".\n        // If using TS class, should better declared as `static type = 'series.pie'`.\n        // otherwise users have to mount `type` on prototype manually.\n        // For backward compat and enable instance visit type via `this.type`,\n        // we still support fetch `type` from prototype.\n        var componentFullType = clz.type || clz.prototype.type;\n\n        if (componentFullType) {\n          checkClassType(componentFullType); // If only static type declared, we assign it to prototype mandatorily.\n\n          clz.prototype.type = componentFullType;\n          var componentTypeInfo = parseClassType(componentFullType);\n\n          if (!componentTypeInfo.sub) {\n            if (\"development\" !== 'production') {\n              if (storage[componentTypeInfo.main]) {\n                console.warn(componentTypeInfo.main + ' exists.');\n              }\n            }\n\n            storage[componentTypeInfo.main] = clz;\n          } else if (componentTypeInfo.sub !== IS_CONTAINER) {\n            var container = makeContainer(componentTypeInfo);\n            container[componentTypeInfo.sub] = clz;\n          }\n        }\n\n        return clz;\n      };\n\n      target.getClass = function (mainType, subType, throwWhenNotFound) {\n        var clz = storage[mainType];\n\n        if (clz && clz[IS_CONTAINER]) {\n          clz = subType ? clz[subType] : null;\n        }\n\n        if (throwWhenNotFound && !clz) {\n          throw new Error(!subType ? mainType + '.' + 'type should be specified.' : 'Component ' + mainType + '.' + (subType || '') + ' is used but not imported.');\n        }\n\n        return clz;\n      };\n\n      target.getClassesByMainType = function (componentType) {\n        var componentTypeInfo = parseClassType(componentType);\n        var result = [];\n        var obj = storage[componentTypeInfo.main];\n\n        if (obj && obj[IS_CONTAINER]) {\n          each(obj, function (o, type) {\n            type !== IS_CONTAINER && result.push(o);\n          });\n        } else {\n          result.push(obj);\n        }\n\n        return result;\n      };\n\n      target.hasClass = function (componentType) {\n        // Just consider componentType.main.\n        var componentTypeInfo = parseClassType(componentType);\n        return !!storage[componentTypeInfo.main];\n      };\n      /**\n       * @return Like ['aa', 'bb'], but can not be ['aa.xx']\n       */\n\n\n      target.getAllClassMainTypes = function () {\n        var types = [];\n        each(storage, function (obj, type) {\n          types.push(type);\n        });\n        return types;\n      };\n      /**\n       * If a main type is container and has sub types\n       */\n\n\n      target.hasSubTypes = function (componentType) {\n        var componentTypeInfo = parseClassType(componentType);\n        var obj = storage[componentTypeInfo.main];\n        return obj && obj[IS_CONTAINER];\n      };\n\n      function makeContainer(componentTypeInfo) {\n        var container = storage[componentTypeInfo.main];\n\n        if (!container || !container[IS_CONTAINER]) {\n          container = storage[componentTypeInfo.main] = {};\n          container[IS_CONTAINER] = true;\n        }\n\n        return container;\n      }\n    } // /**\n    //  * @param {string|Array.<string>} properties\n    //  */\n    // export function setReadOnly(obj, properties) {\n    // FIXME It seems broken in IE8 simulation of IE11\n    // if (!zrUtil.isArray(properties)) {\n    //     properties = properties != null ? [properties] : [];\n    // }\n    // zrUtil.each(properties, function (prop) {\n    //     let value = obj[prop];\n    //     Object.defineProperty\n    //         && Object.defineProperty(obj, prop, {\n    //             value: value, writable: false\n    //         });\n    //     zrUtil.isArray(obj[prop])\n    //         && Object.freeze\n    //         && Object.freeze(obj[prop]);\n    // });\n    // }\n\n    function makeStyleMapper(properties, ignoreParent) {\n      // Normalize\n      for (var i = 0; i < properties.length; i++) {\n        if (!properties[i][1]) {\n          properties[i][1] = properties[i][0];\n        }\n      }\n\n      ignoreParent = ignoreParent || false;\n      return function (model, excludes, includes) {\n        var style = {};\n\n        for (var i = 0; i < properties.length; i++) {\n          var propName = properties[i][1];\n\n          if (excludes && indexOf(excludes, propName) >= 0 || includes && indexOf(includes, propName) < 0) {\n            continue;\n          }\n\n          var val = model.getShallow(propName, ignoreParent);\n\n          if (val != null) {\n            style[properties[i][0]] = val;\n          }\n        } // TODO Text or image?\n\n\n        return style;\n      };\n    }\n\n    var AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getAreaStyle = makeStyleMapper(AREA_STYLE_KEY_MAP);\n\n    var AreaStyleMixin =\n    /** @class */\n    function () {\n      function AreaStyleMixin() {}\n\n      AreaStyleMixin.prototype.getAreaStyle = function (excludes, includes) {\n        return getAreaStyle(this, excludes, includes);\n      };\n\n      return AreaStyleMixin;\n    }();\n\n    var globalImageCache = new LRU(50);\n    function findExistImage(newImageOrSrc) {\n        if (typeof newImageOrSrc === 'string') {\n            var cachedImgObj = globalImageCache.get(newImageOrSrc);\n            return cachedImgObj && cachedImgObj.image;\n        }\n        else {\n            return newImageOrSrc;\n        }\n    }\n    function createOrUpdateImage(newImageOrSrc, image, hostEl, onload, cbPayload) {\n        if (!newImageOrSrc) {\n            return image;\n        }\n        else if (typeof newImageOrSrc === 'string') {\n            if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {\n                return image;\n            }\n            var cachedImgObj = globalImageCache.get(newImageOrSrc);\n            var pendingWrap = { hostEl: hostEl, cb: onload, cbPayload: cbPayload };\n            if (cachedImgObj) {\n                image = cachedImgObj.image;\n                !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);\n            }\n            else {\n                image = platformApi.loadImage(newImageOrSrc, imageOnLoad, imageOnLoad);\n                image.__zrImageSrc = newImageOrSrc;\n                globalImageCache.put(newImageOrSrc, image.__cachedImgObj = {\n                    image: image,\n                    pending: [pendingWrap]\n                });\n            }\n            return image;\n        }\n        else {\n            return newImageOrSrc;\n        }\n    }\n    function imageOnLoad() {\n        var cachedImgObj = this.__cachedImgObj;\n        this.onload = this.onerror = this.__cachedImgObj = null;\n        for (var i = 0; i < cachedImgObj.pending.length; i++) {\n            var pendingWrap = cachedImgObj.pending[i];\n            var cb = pendingWrap.cb;\n            cb && cb(this, pendingWrap.cbPayload);\n            pendingWrap.hostEl.dirty();\n        }\n        cachedImgObj.pending.length = 0;\n    }\n    function isImageReady(image) {\n        return image && image.width && image.height;\n    }\n\n    var STYLE_REG = /\\{([a-zA-Z0-9_]+)\\|([^}]*)\\}/g;\n    function truncateText(text, containerWidth, font, ellipsis, options) {\n        if (!containerWidth) {\n            return '';\n        }\n        var textLines = (text + '').split('\\n');\n        options = prepareTruncateOptions(containerWidth, font, ellipsis, options);\n        for (var i = 0, len = textLines.length; i < len; i++) {\n            textLines[i] = truncateSingleLine(textLines[i], options);\n        }\n        return textLines.join('\\n');\n    }\n    function prepareTruncateOptions(containerWidth, font, ellipsis, options) {\n        options = options || {};\n        var preparedOpts = extend({}, options);\n        preparedOpts.font = font;\n        ellipsis = retrieve2(ellipsis, '...');\n        preparedOpts.maxIterations = retrieve2(options.maxIterations, 2);\n        var minChar = preparedOpts.minChar = retrieve2(options.minChar, 0);\n        preparedOpts.cnCharWidth = getWidth('国', font);\n        var ascCharWidth = preparedOpts.ascCharWidth = getWidth('a', font);\n        preparedOpts.placeholder = retrieve2(options.placeholder, '');\n        var contentWidth = containerWidth = Math.max(0, containerWidth - 1);\n        for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {\n            contentWidth -= ascCharWidth;\n        }\n        var ellipsisWidth = getWidth(ellipsis, font);\n        if (ellipsisWidth > contentWidth) {\n            ellipsis = '';\n            ellipsisWidth = 0;\n        }\n        contentWidth = containerWidth - ellipsisWidth;\n        preparedOpts.ellipsis = ellipsis;\n        preparedOpts.ellipsisWidth = ellipsisWidth;\n        preparedOpts.contentWidth = contentWidth;\n        preparedOpts.containerWidth = containerWidth;\n        return preparedOpts;\n    }\n    function truncateSingleLine(textLine, options) {\n        var containerWidth = options.containerWidth;\n        var font = options.font;\n        var contentWidth = options.contentWidth;\n        if (!containerWidth) {\n            return '';\n        }\n        var lineWidth = getWidth(textLine, font);\n        if (lineWidth <= containerWidth) {\n            return textLine;\n        }\n        for (var j = 0;; j++) {\n            if (lineWidth <= contentWidth || j >= options.maxIterations) {\n                textLine += options.ellipsis;\n                break;\n            }\n            var subLength = j === 0\n                ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)\n                : lineWidth > 0\n                    ? Math.floor(textLine.length * contentWidth / lineWidth)\n                    : 0;\n            textLine = textLine.substr(0, subLength);\n            lineWidth = getWidth(textLine, font);\n        }\n        if (textLine === '') {\n            textLine = options.placeholder;\n        }\n        return textLine;\n    }\n    function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {\n        var width = 0;\n        var i = 0;\n        for (var len = text.length; i < len && width < contentWidth; i++) {\n            var charCode = text.charCodeAt(i);\n            width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;\n        }\n        return i;\n    }\n    function parsePlainText(text, style) {\n        text != null && (text += '');\n        var overflow = style.overflow;\n        var padding = style.padding;\n        var font = style.font;\n        var truncate = overflow === 'truncate';\n        var calculatedLineHeight = getLineHeight(font);\n        var lineHeight = retrieve2(style.lineHeight, calculatedLineHeight);\n        var bgColorDrawn = !!(style.backgroundColor);\n        var truncateLineOverflow = style.lineOverflow === 'truncate';\n        var width = style.width;\n        var lines;\n        if (width != null && (overflow === 'break' || overflow === 'breakAll')) {\n            lines = text ? wrapText(text, style.font, width, overflow === 'breakAll', 0).lines : [];\n        }\n        else {\n            lines = text ? text.split('\\n') : [];\n        }\n        var contentHeight = lines.length * lineHeight;\n        var height = retrieve2(style.height, contentHeight);\n        if (contentHeight > height && truncateLineOverflow) {\n            var lineCount = Math.floor(height / lineHeight);\n            lines = lines.slice(0, lineCount);\n        }\n        if (text && truncate && width != null) {\n            var options = prepareTruncateOptions(width, font, style.ellipsis, {\n                minChar: style.truncateMinChar,\n                placeholder: style.placeholder\n            });\n            for (var i = 0; i < lines.length; i++) {\n                lines[i] = truncateSingleLine(lines[i], options);\n            }\n        }\n        var outerHeight = height;\n        var contentWidth = 0;\n        for (var i = 0; i < lines.length; i++) {\n            contentWidth = Math.max(getWidth(lines[i], font), contentWidth);\n        }\n        if (width == null) {\n            width = contentWidth;\n        }\n        var outerWidth = contentWidth;\n        if (padding) {\n            outerHeight += padding[0] + padding[2];\n            outerWidth += padding[1] + padding[3];\n            width += padding[1] + padding[3];\n        }\n        if (bgColorDrawn) {\n            outerWidth = width;\n        }\n        return {\n            lines: lines,\n            height: height,\n            outerWidth: outerWidth,\n            outerHeight: outerHeight,\n            lineHeight: lineHeight,\n            calculatedLineHeight: calculatedLineHeight,\n            contentWidth: contentWidth,\n            contentHeight: contentHeight,\n            width: width\n        };\n    }\n    var RichTextToken = (function () {\n        function RichTextToken() {\n        }\n        return RichTextToken;\n    }());\n    var RichTextLine = (function () {\n        function RichTextLine(tokens) {\n            this.tokens = [];\n            if (tokens) {\n                this.tokens = tokens;\n            }\n        }\n        return RichTextLine;\n    }());\n    var RichTextContentBlock = (function () {\n        function RichTextContentBlock() {\n            this.width = 0;\n            this.height = 0;\n            this.contentWidth = 0;\n            this.contentHeight = 0;\n            this.outerWidth = 0;\n            this.outerHeight = 0;\n            this.lines = [];\n        }\n        return RichTextContentBlock;\n    }());\n    function parseRichText(text, style) {\n        var contentBlock = new RichTextContentBlock();\n        text != null && (text += '');\n        if (!text) {\n            return contentBlock;\n        }\n        var topWidth = style.width;\n        var topHeight = style.height;\n        var overflow = style.overflow;\n        var wrapInfo = (overflow === 'break' || overflow === 'breakAll') && topWidth != null\n            ? { width: topWidth, accumWidth: 0, breakAll: overflow === 'breakAll' }\n            : null;\n        var lastIndex = STYLE_REG.lastIndex = 0;\n        var result;\n        while ((result = STYLE_REG.exec(text)) != null) {\n            var matchedIndex = result.index;\n            if (matchedIndex > lastIndex) {\n                pushTokens(contentBlock, text.substring(lastIndex, matchedIndex), style, wrapInfo);\n            }\n            pushTokens(contentBlock, result[2], style, wrapInfo, result[1]);\n            lastIndex = STYLE_REG.lastIndex;\n        }\n        if (lastIndex < text.length) {\n            pushTokens(contentBlock, text.substring(lastIndex, text.length), style, wrapInfo);\n        }\n        var pendingList = [];\n        var calculatedHeight = 0;\n        var calculatedWidth = 0;\n        var stlPadding = style.padding;\n        var truncate = overflow === 'truncate';\n        var truncateLine = style.lineOverflow === 'truncate';\n        function finishLine(line, lineWidth, lineHeight) {\n            line.width = lineWidth;\n            line.lineHeight = lineHeight;\n            calculatedHeight += lineHeight;\n            calculatedWidth = Math.max(calculatedWidth, lineWidth);\n        }\n        outer: for (var i = 0; i < contentBlock.lines.length; i++) {\n            var line = contentBlock.lines[i];\n            var lineHeight = 0;\n            var lineWidth = 0;\n            for (var j = 0; j < line.tokens.length; j++) {\n                var token = line.tokens[j];\n                var tokenStyle = token.styleName && style.rich[token.styleName] || {};\n                var textPadding = token.textPadding = tokenStyle.padding;\n                var paddingH = textPadding ? textPadding[1] + textPadding[3] : 0;\n                var font = token.font = tokenStyle.font || style.font;\n                token.contentHeight = getLineHeight(font);\n                var tokenHeight = retrieve2(tokenStyle.height, token.contentHeight);\n                token.innerHeight = tokenHeight;\n                textPadding && (tokenHeight += textPadding[0] + textPadding[2]);\n                token.height = tokenHeight;\n                token.lineHeight = retrieve3(tokenStyle.lineHeight, style.lineHeight, tokenHeight);\n                token.align = tokenStyle && tokenStyle.align || style.align;\n                token.verticalAlign = tokenStyle && tokenStyle.verticalAlign || 'middle';\n                if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) {\n                    if (j > 0) {\n                        line.tokens = line.tokens.slice(0, j);\n                        finishLine(line, lineWidth, lineHeight);\n                        contentBlock.lines = contentBlock.lines.slice(0, i + 1);\n                    }\n                    else {\n                        contentBlock.lines = contentBlock.lines.slice(0, i);\n                    }\n                    break outer;\n                }\n                var styleTokenWidth = tokenStyle.width;\n                var tokenWidthNotSpecified = styleTokenWidth == null || styleTokenWidth === 'auto';\n                if (typeof styleTokenWidth === 'string' && styleTokenWidth.charAt(styleTokenWidth.length - 1) === '%') {\n                    token.percentWidth = styleTokenWidth;\n                    pendingList.push(token);\n                    token.contentWidth = getWidth(token.text, font);\n                }\n                else {\n                    if (tokenWidthNotSpecified) {\n                        var textBackgroundColor = tokenStyle.backgroundColor;\n                        var bgImg = textBackgroundColor && textBackgroundColor.image;\n                        if (bgImg) {\n                            bgImg = findExistImage(bgImg);\n                            if (isImageReady(bgImg)) {\n                                token.width = Math.max(token.width, bgImg.width * tokenHeight / bgImg.height);\n                            }\n                        }\n                    }\n                    var remainTruncWidth = truncate && topWidth != null\n                        ? topWidth - lineWidth : null;\n                    if (remainTruncWidth != null && remainTruncWidth < token.width) {\n                        if (!tokenWidthNotSpecified || remainTruncWidth < paddingH) {\n                            token.text = '';\n                            token.width = token.contentWidth = 0;\n                        }\n                        else {\n                            token.text = truncateText(token.text, remainTruncWidth - paddingH, font, style.ellipsis, { minChar: style.truncateMinChar });\n                            token.width = token.contentWidth = getWidth(token.text, font);\n                        }\n                    }\n                    else {\n                        token.contentWidth = getWidth(token.text, font);\n                    }\n                }\n                token.width += paddingH;\n                lineWidth += token.width;\n                tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));\n            }\n            finishLine(line, lineWidth, lineHeight);\n        }\n        contentBlock.outerWidth = contentBlock.width = retrieve2(topWidth, calculatedWidth);\n        contentBlock.outerHeight = contentBlock.height = retrieve2(topHeight, calculatedHeight);\n        contentBlock.contentHeight = calculatedHeight;\n        contentBlock.contentWidth = calculatedWidth;\n        if (stlPadding) {\n            contentBlock.outerWidth += stlPadding[1] + stlPadding[3];\n            contentBlock.outerHeight += stlPadding[0] + stlPadding[2];\n        }\n        for (var i = 0; i < pendingList.length; i++) {\n            var token = pendingList[i];\n            var percentWidth = token.percentWidth;\n            token.width = parseInt(percentWidth, 10) / 100 * contentBlock.width;\n        }\n        return contentBlock;\n    }\n    function pushTokens(block, str, style, wrapInfo, styleName) {\n        var isEmptyStr = str === '';\n        var tokenStyle = styleName && style.rich[styleName] || {};\n        var lines = block.lines;\n        var font = tokenStyle.font || style.font;\n        var newLine = false;\n        var strLines;\n        var linesWidths;\n        if (wrapInfo) {\n            var tokenPadding = tokenStyle.padding;\n            var tokenPaddingH = tokenPadding ? tokenPadding[1] + tokenPadding[3] : 0;\n            if (tokenStyle.width != null && tokenStyle.width !== 'auto') {\n                var outerWidth_1 = parsePercent(tokenStyle.width, wrapInfo.width) + tokenPaddingH;\n                if (lines.length > 0) {\n                    if (outerWidth_1 + wrapInfo.accumWidth > wrapInfo.width) {\n                        strLines = str.split('\\n');\n                        newLine = true;\n                    }\n                }\n                wrapInfo.accumWidth = outerWidth_1;\n            }\n            else {\n                var res = wrapText(str, font, wrapInfo.width, wrapInfo.breakAll, wrapInfo.accumWidth);\n                wrapInfo.accumWidth = res.accumWidth + tokenPaddingH;\n                linesWidths = res.linesWidths;\n                strLines = res.lines;\n            }\n        }\n        else {\n            strLines = str.split('\\n');\n        }\n        for (var i = 0; i < strLines.length; i++) {\n            var text = strLines[i];\n            var token = new RichTextToken();\n            token.styleName = styleName;\n            token.text = text;\n            token.isLineHolder = !text && !isEmptyStr;\n            if (typeof tokenStyle.width === 'number') {\n                token.width = tokenStyle.width;\n            }\n            else {\n                token.width = linesWidths\n                    ? linesWidths[i]\n                    : getWidth(text, font);\n            }\n            if (!i && !newLine) {\n                var tokens = (lines[lines.length - 1] || (lines[0] = new RichTextLine())).tokens;\n                var tokensLen = tokens.length;\n                (tokensLen === 1 && tokens[0].isLineHolder)\n                    ? (tokens[0] = token)\n                    : ((text || !tokensLen || isEmptyStr) && tokens.push(token));\n            }\n            else {\n                lines.push(new RichTextLine([token]));\n            }\n        }\n    }\n    function isLatin(ch) {\n        var code = ch.charCodeAt(0);\n        return code >= 0x21 && code <= 0x17F;\n    }\n    var breakCharMap = reduce(',&?/;] '.split(''), function (obj, ch) {\n        obj[ch] = true;\n        return obj;\n    }, {});\n    function isWordBreakChar(ch) {\n        if (isLatin(ch)) {\n            if (breakCharMap[ch]) {\n                return true;\n            }\n            return false;\n        }\n        return true;\n    }\n    function wrapText(text, font, lineWidth, isBreakAll, lastAccumWidth) {\n        var lines = [];\n        var linesWidths = [];\n        var line = '';\n        var currentWord = '';\n        var currentWordWidth = 0;\n        var accumWidth = 0;\n        for (var i = 0; i < text.length; i++) {\n            var ch = text.charAt(i);\n            if (ch === '\\n') {\n                if (currentWord) {\n                    line += currentWord;\n                    accumWidth += currentWordWidth;\n                }\n                lines.push(line);\n                linesWidths.push(accumWidth);\n                line = '';\n                currentWord = '';\n                currentWordWidth = 0;\n                accumWidth = 0;\n                continue;\n            }\n            var chWidth = getWidth(ch, font);\n            var inWord = isBreakAll ? false : !isWordBreakChar(ch);\n            if (!lines.length\n                ? lastAccumWidth + accumWidth + chWidth > lineWidth\n                : accumWidth + chWidth > lineWidth) {\n                if (!accumWidth) {\n                    if (inWord) {\n                        lines.push(currentWord);\n                        linesWidths.push(currentWordWidth);\n                        currentWord = ch;\n                        currentWordWidth = chWidth;\n                    }\n                    else {\n                        lines.push(ch);\n                        linesWidths.push(chWidth);\n                    }\n                }\n                else if (line || currentWord) {\n                    if (inWord) {\n                        if (!line) {\n                            line = currentWord;\n                            currentWord = '';\n                            currentWordWidth = 0;\n                            accumWidth = currentWordWidth;\n                        }\n                        lines.push(line);\n                        linesWidths.push(accumWidth - currentWordWidth);\n                        currentWord += ch;\n                        currentWordWidth += chWidth;\n                        line = '';\n                        accumWidth = currentWordWidth;\n                    }\n                    else {\n                        if (currentWord) {\n                            line += currentWord;\n                            currentWord = '';\n                            currentWordWidth = 0;\n                        }\n                        lines.push(line);\n                        linesWidths.push(accumWidth);\n                        line = ch;\n                        accumWidth = chWidth;\n                    }\n                }\n                continue;\n            }\n            accumWidth += chWidth;\n            if (inWord) {\n                currentWord += ch;\n                currentWordWidth += chWidth;\n            }\n            else {\n                if (currentWord) {\n                    line += currentWord;\n                    currentWord = '';\n                    currentWordWidth = 0;\n                }\n                line += ch;\n            }\n        }\n        if (!lines.length && !line) {\n            line = text;\n            currentWord = '';\n            currentWordWidth = 0;\n        }\n        if (currentWord) {\n            line += currentWord;\n        }\n        if (line) {\n            lines.push(line);\n            linesWidths.push(accumWidth);\n        }\n        if (lines.length === 1) {\n            accumWidth += lastAccumWidth;\n        }\n        return {\n            accumWidth: accumWidth,\n            lines: lines,\n            linesWidths: linesWidths\n        };\n    }\n\n    var STYLE_MAGIC_KEY = '__zr_style_' + Math.round((Math.random() * 10));\n    var DEFAULT_COMMON_STYLE = {\n        shadowBlur: 0,\n        shadowOffsetX: 0,\n        shadowOffsetY: 0,\n        shadowColor: '#000',\n        opacity: 1,\n        blend: 'source-over'\n    };\n    var DEFAULT_COMMON_ANIMATION_PROPS = {\n        style: {\n            shadowBlur: true,\n            shadowOffsetX: true,\n            shadowOffsetY: true,\n            shadowColor: true,\n            opacity: true\n        }\n    };\n    DEFAULT_COMMON_STYLE[STYLE_MAGIC_KEY] = true;\n    var PRIMARY_STATES_KEYS$1 = ['z', 'z2', 'invisible'];\n    var PRIMARY_STATES_KEYS_IN_HOVER_LAYER = ['invisible'];\n    var Displayable = (function (_super) {\n        __extends(Displayable, _super);\n        function Displayable(props) {\n            return _super.call(this, props) || this;\n        }\n        Displayable.prototype._init = function (props) {\n            var keysArr = keys(props);\n            for (var i = 0; i < keysArr.length; i++) {\n                var key = keysArr[i];\n                if (key === 'style') {\n                    this.useStyle(props[key]);\n                }\n                else {\n                    _super.prototype.attrKV.call(this, key, props[key]);\n                }\n            }\n            if (!this.style) {\n                this.useStyle({});\n            }\n        };\n        Displayable.prototype.beforeBrush = function () { };\n        Displayable.prototype.afterBrush = function () { };\n        Displayable.prototype.innerBeforeBrush = function () { };\n        Displayable.prototype.innerAfterBrush = function () { };\n        Displayable.prototype.shouldBePainted = function (viewWidth, viewHeight, considerClipPath, considerAncestors) {\n            var m = this.transform;\n            if (this.ignore\n                || this.invisible\n                || this.style.opacity === 0\n                || (this.culling\n                    && isDisplayableCulled(this, viewWidth, viewHeight))\n                || (m && !m[0] && !m[3])) {\n                return false;\n            }\n            if (considerClipPath && this.__clipPaths) {\n                for (var i = 0; i < this.__clipPaths.length; ++i) {\n                    if (this.__clipPaths[i].isZeroArea()) {\n                        return false;\n                    }\n                }\n            }\n            if (considerAncestors && this.parent) {\n                var parent_1 = this.parent;\n                while (parent_1) {\n                    if (parent_1.ignore) {\n                        return false;\n                    }\n                    parent_1 = parent_1.parent;\n                }\n            }\n            return true;\n        };\n        Displayable.prototype.contain = function (x, y) {\n            return this.rectContain(x, y);\n        };\n        Displayable.prototype.traverse = function (cb, context) {\n            cb.call(context, this);\n        };\n        Displayable.prototype.rectContain = function (x, y) {\n            var coord = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            return rect.contain(coord[0], coord[1]);\n        };\n        Displayable.prototype.getPaintRect = function () {\n            var rect = this._paintRect;\n            if (!this._paintRect || this.__dirty) {\n                var transform = this.transform;\n                var elRect = this.getBoundingRect();\n                var style = this.style;\n                var shadowSize = style.shadowBlur || 0;\n                var shadowOffsetX = style.shadowOffsetX || 0;\n                var shadowOffsetY = style.shadowOffsetY || 0;\n                rect = this._paintRect || (this._paintRect = new BoundingRect(0, 0, 0, 0));\n                if (transform) {\n                    BoundingRect.applyTransform(rect, elRect, transform);\n                }\n                else {\n                    rect.copy(elRect);\n                }\n                if (shadowSize || shadowOffsetX || shadowOffsetY) {\n                    rect.width += shadowSize * 2 + Math.abs(shadowOffsetX);\n                    rect.height += shadowSize * 2 + Math.abs(shadowOffsetY);\n                    rect.x = Math.min(rect.x, rect.x + shadowOffsetX - shadowSize);\n                    rect.y = Math.min(rect.y, rect.y + shadowOffsetY - shadowSize);\n                }\n                var tolerance = this.dirtyRectTolerance;\n                if (!rect.isZero()) {\n                    rect.x = Math.floor(rect.x - tolerance);\n                    rect.y = Math.floor(rect.y - tolerance);\n                    rect.width = Math.ceil(rect.width + 1 + tolerance * 2);\n                    rect.height = Math.ceil(rect.height + 1 + tolerance * 2);\n                }\n            }\n            return rect;\n        };\n        Displayable.prototype.setPrevPaintRect = function (paintRect) {\n            if (paintRect) {\n                this._prevPaintRect = this._prevPaintRect || new BoundingRect(0, 0, 0, 0);\n                this._prevPaintRect.copy(paintRect);\n            }\n            else {\n                this._prevPaintRect = null;\n            }\n        };\n        Displayable.prototype.getPrevPaintRect = function () {\n            return this._prevPaintRect;\n        };\n        Displayable.prototype.animateStyle = function (loop) {\n            return this.animate('style', loop);\n        };\n        Displayable.prototype.updateDuringAnimation = function (targetKey) {\n            if (targetKey === 'style') {\n                this.dirtyStyle();\n            }\n            else {\n                this.markRedraw();\n            }\n        };\n        Displayable.prototype.attrKV = function (key, value) {\n            if (key !== 'style') {\n                _super.prototype.attrKV.call(this, key, value);\n            }\n            else {\n                if (!this.style) {\n                    this.useStyle(value);\n                }\n                else {\n                    this.setStyle(value);\n                }\n            }\n        };\n        Displayable.prototype.setStyle = function (keyOrObj, value) {\n            if (typeof keyOrObj === 'string') {\n                this.style[keyOrObj] = value;\n            }\n            else {\n                extend(this.style, keyOrObj);\n            }\n            this.dirtyStyle();\n            return this;\n        };\n        Displayable.prototype.dirtyStyle = function (notRedraw) {\n            if (!notRedraw) {\n                this.markRedraw();\n            }\n            this.__dirty |= STYLE_CHANGED_BIT;\n            if (this._rect) {\n                this._rect = null;\n            }\n        };\n        Displayable.prototype.dirty = function () {\n            this.dirtyStyle();\n        };\n        Displayable.prototype.styleChanged = function () {\n            return !!(this.__dirty & STYLE_CHANGED_BIT);\n        };\n        Displayable.prototype.styleUpdated = function () {\n            this.__dirty &= ~STYLE_CHANGED_BIT;\n        };\n        Displayable.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_COMMON_STYLE, obj);\n        };\n        Displayable.prototype.useStyle = function (obj) {\n            if (!obj[STYLE_MAGIC_KEY]) {\n                obj = this.createStyle(obj);\n            }\n            if (this.__inHover) {\n                this.__hoverStyle = obj;\n            }\n            else {\n                this.style = obj;\n            }\n            this.dirtyStyle();\n        };\n        Displayable.prototype.isStyleObject = function (obj) {\n            return obj[STYLE_MAGIC_KEY];\n        };\n        Displayable.prototype._innerSaveToNormal = function (toState) {\n            _super.prototype._innerSaveToNormal.call(this, toState);\n            var normalState = this._normalState;\n            if (toState.style && !normalState.style) {\n                normalState.style = this._mergeStyle(this.createStyle(), this.style);\n            }\n            this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS$1);\n        };\n        Displayable.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            var targetStyle;\n            if (state && state.style) {\n                if (transition) {\n                    if (keepCurrentStates) {\n                        targetStyle = state.style;\n                    }\n                    else {\n                        targetStyle = this._mergeStyle(this.createStyle(), normalState.style);\n                        this._mergeStyle(targetStyle, state.style);\n                    }\n                }\n                else {\n                    targetStyle = this._mergeStyle(this.createStyle(), keepCurrentStates ? this.style : normalState.style);\n                    this._mergeStyle(targetStyle, state.style);\n                }\n            }\n            else if (needsRestoreToNormal) {\n                targetStyle = normalState.style;\n            }\n            if (targetStyle) {\n                if (transition) {\n                    var sourceStyle = this.style;\n                    this.style = this.createStyle(needsRestoreToNormal ? {} : sourceStyle);\n                    if (needsRestoreToNormal) {\n                        var changedKeys = keys(sourceStyle);\n                        for (var i = 0; i < changedKeys.length; i++) {\n                            var key = changedKeys[i];\n                            if (key in targetStyle) {\n                                targetStyle[key] = targetStyle[key];\n                                this.style[key] = sourceStyle[key];\n                            }\n                        }\n                    }\n                    var targetKeys = keys(targetStyle);\n                    for (var i = 0; i < targetKeys.length; i++) {\n                        var key = targetKeys[i];\n                        this.style[key] = this.style[key];\n                    }\n                    this._transitionState(stateName, {\n                        style: targetStyle\n                    }, animationCfg, this.getAnimationStyleProps());\n                }\n                else {\n                    this.useStyle(targetStyle);\n                }\n            }\n            var statesKeys = this.__inHover ? PRIMARY_STATES_KEYS_IN_HOVER_LAYER : PRIMARY_STATES_KEYS$1;\n            for (var i = 0; i < statesKeys.length; i++) {\n                var key = statesKeys[i];\n                if (state && state[key] != null) {\n                    this[key] = state[key];\n                }\n                else if (needsRestoreToNormal) {\n                    if (normalState[key] != null) {\n                        this[key] = normalState[key];\n                    }\n                }\n            }\n        };\n        Displayable.prototype._mergeStates = function (states) {\n            var mergedState = _super.prototype._mergeStates.call(this, states);\n            var mergedStyle;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                if (state.style) {\n                    mergedStyle = mergedStyle || {};\n                    this._mergeStyle(mergedStyle, state.style);\n                }\n            }\n            if (mergedStyle) {\n                mergedState.style = mergedStyle;\n            }\n            return mergedState;\n        };\n        Displayable.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n            extend(targetStyle, sourceStyle);\n            return targetStyle;\n        };\n        Displayable.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_COMMON_ANIMATION_PROPS;\n        };\n        Displayable.initDefaultProps = (function () {\n            var dispProto = Displayable.prototype;\n            dispProto.type = 'displayable';\n            dispProto.invisible = false;\n            dispProto.z = 0;\n            dispProto.z2 = 0;\n            dispProto.zlevel = 0;\n            dispProto.culling = false;\n            dispProto.cursor = 'pointer';\n            dispProto.rectHover = false;\n            dispProto.incremental = false;\n            dispProto._rect = null;\n            dispProto.dirtyRectTolerance = 0;\n            dispProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT;\n        })();\n        return Displayable;\n    }(Element));\n    var tmpRect$1 = new BoundingRect(0, 0, 0, 0);\n    var viewRect = new BoundingRect(0, 0, 0, 0);\n    function isDisplayableCulled(el, width, height) {\n        tmpRect$1.copy(el.getBoundingRect());\n        if (el.transform) {\n            tmpRect$1.applyTransform(el.transform);\n        }\n        viewRect.width = width;\n        viewRect.height = height;\n        return !tmpRect$1.intersect(viewRect);\n    }\n\n    var mathMin$1 = Math.min;\n    var mathMax$1 = Math.max;\n    var mathSin = Math.sin;\n    var mathCos = Math.cos;\n    var PI2 = Math.PI * 2;\n    var start = create();\n    var end = create();\n    var extremity = create();\n    function fromPoints(points, min, max) {\n        if (points.length === 0) {\n            return;\n        }\n        var p = points[0];\n        var left = p[0];\n        var right = p[0];\n        var top = p[1];\n        var bottom = p[1];\n        for (var i = 1; i < points.length; i++) {\n            p = points[i];\n            left = mathMin$1(left, p[0]);\n            right = mathMax$1(right, p[0]);\n            top = mathMin$1(top, p[1]);\n            bottom = mathMax$1(bottom, p[1]);\n        }\n        min[0] = left;\n        min[1] = top;\n        max[0] = right;\n        max[1] = bottom;\n    }\n    function fromLine(x0, y0, x1, y1, min, max) {\n        min[0] = mathMin$1(x0, x1);\n        min[1] = mathMin$1(y0, y1);\n        max[0] = mathMax$1(x0, x1);\n        max[1] = mathMax$1(y0, y1);\n    }\n    var xDim = [];\n    var yDim = [];\n    function fromCubic(x0, y0, x1, y1, x2, y2, x3, y3, min, max) {\n        var cubicExtrema$1 = cubicExtrema;\n        var cubicAt$1 = cubicAt;\n        var n = cubicExtrema$1(x0, x1, x2, x3, xDim);\n        min[0] = Infinity;\n        min[1] = Infinity;\n        max[0] = -Infinity;\n        max[1] = -Infinity;\n        for (var i = 0; i < n; i++) {\n            var x = cubicAt$1(x0, x1, x2, x3, xDim[i]);\n            min[0] = mathMin$1(x, min[0]);\n            max[0] = mathMax$1(x, max[0]);\n        }\n        n = cubicExtrema$1(y0, y1, y2, y3, yDim);\n        for (var i = 0; i < n; i++) {\n            var y = cubicAt$1(y0, y1, y2, y3, yDim[i]);\n            min[1] = mathMin$1(y, min[1]);\n            max[1] = mathMax$1(y, max[1]);\n        }\n        min[0] = mathMin$1(x0, min[0]);\n        max[0] = mathMax$1(x0, max[0]);\n        min[0] = mathMin$1(x3, min[0]);\n        max[0] = mathMax$1(x3, max[0]);\n        min[1] = mathMin$1(y0, min[1]);\n        max[1] = mathMax$1(y0, max[1]);\n        min[1] = mathMin$1(y3, min[1]);\n        max[1] = mathMax$1(y3, max[1]);\n    }\n    function fromQuadratic(x0, y0, x1, y1, x2, y2, min, max) {\n        var quadraticExtremum$1 = quadraticExtremum;\n        var quadraticAt$1 = quadraticAt;\n        var tx = mathMax$1(mathMin$1(quadraticExtremum$1(x0, x1, x2), 1), 0);\n        var ty = mathMax$1(mathMin$1(quadraticExtremum$1(y0, y1, y2), 1), 0);\n        var x = quadraticAt$1(x0, x1, x2, tx);\n        var y = quadraticAt$1(y0, y1, y2, ty);\n        min[0] = mathMin$1(x0, x2, x);\n        min[1] = mathMin$1(y0, y2, y);\n        max[0] = mathMax$1(x0, x2, x);\n        max[1] = mathMax$1(y0, y2, y);\n    }\n    function fromArc(x, y, rx, ry, startAngle, endAngle, anticlockwise, min$1, max$1) {\n        var vec2Min = min;\n        var vec2Max = max;\n        var diff = Math.abs(startAngle - endAngle);\n        if (diff % PI2 < 1e-4 && diff > 1e-4) {\n            min$1[0] = x - rx;\n            min$1[1] = y - ry;\n            max$1[0] = x + rx;\n            max$1[1] = y + ry;\n            return;\n        }\n        start[0] = mathCos(startAngle) * rx + x;\n        start[1] = mathSin(startAngle) * ry + y;\n        end[0] = mathCos(endAngle) * rx + x;\n        end[1] = mathSin(endAngle) * ry + y;\n        vec2Min(min$1, start, end);\n        vec2Max(max$1, start, end);\n        startAngle = startAngle % (PI2);\n        if (startAngle < 0) {\n            startAngle = startAngle + PI2;\n        }\n        endAngle = endAngle % (PI2);\n        if (endAngle < 0) {\n            endAngle = endAngle + PI2;\n        }\n        if (startAngle > endAngle && !anticlockwise) {\n            endAngle += PI2;\n        }\n        else if (startAngle < endAngle && anticlockwise) {\n            startAngle += PI2;\n        }\n        if (anticlockwise) {\n            var tmp = endAngle;\n            endAngle = startAngle;\n            startAngle = tmp;\n        }\n        for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {\n            if (angle > startAngle) {\n                extremity[0] = mathCos(angle) * rx + x;\n                extremity[1] = mathSin(angle) * ry + y;\n                vec2Min(min$1, extremity, min$1);\n                vec2Max(max$1, extremity, max$1);\n            }\n        }\n    }\n\n    var CMD = {\n        M: 1,\n        L: 2,\n        C: 3,\n        Q: 4,\n        A: 5,\n        Z: 6,\n        R: 7\n    };\n    var tmpOutX = [];\n    var tmpOutY = [];\n    var min$1 = [];\n    var max$1 = [];\n    var min2 = [];\n    var max2 = [];\n    var mathMin$2 = Math.min;\n    var mathMax$2 = Math.max;\n    var mathCos$1 = Math.cos;\n    var mathSin$1 = Math.sin;\n    var mathAbs = Math.abs;\n    var PI = Math.PI;\n    var PI2$1 = PI * 2;\n    var hasTypedArray = typeof Float32Array !== 'undefined';\n    var tmpAngles = [];\n    function modPI2(radian) {\n        var n = Math.round(radian / PI * 1e8) / 1e8;\n        return (n % 2) * PI;\n    }\n    function normalizeArcAngles(angles, anticlockwise) {\n        var newStartAngle = modPI2(angles[0]);\n        if (newStartAngle < 0) {\n            newStartAngle += PI2$1;\n        }\n        var delta = newStartAngle - angles[0];\n        var newEndAngle = angles[1];\n        newEndAngle += delta;\n        if (!anticlockwise && newEndAngle - newStartAngle >= PI2$1) {\n            newEndAngle = newStartAngle + PI2$1;\n        }\n        else if (anticlockwise && newStartAngle - newEndAngle >= PI2$1) {\n            newEndAngle = newStartAngle - PI2$1;\n        }\n        else if (!anticlockwise && newStartAngle > newEndAngle) {\n            newEndAngle = newStartAngle + (PI2$1 - modPI2(newStartAngle - newEndAngle));\n        }\n        else if (anticlockwise && newStartAngle < newEndAngle) {\n            newEndAngle = newStartAngle - (PI2$1 - modPI2(newEndAngle - newStartAngle));\n        }\n        angles[0] = newStartAngle;\n        angles[1] = newEndAngle;\n    }\n    var PathProxy = (function () {\n        function PathProxy(notSaveData) {\n            this.dpr = 1;\n            this._xi = 0;\n            this._yi = 0;\n            this._x0 = 0;\n            this._y0 = 0;\n            this._len = 0;\n            if (notSaveData) {\n                this._saveData = false;\n            }\n            if (this._saveData) {\n                this.data = [];\n            }\n        }\n        PathProxy.prototype.increaseVersion = function () {\n            this._version++;\n        };\n        PathProxy.prototype.getVersion = function () {\n            return this._version;\n        };\n        PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) {\n            segmentIgnoreThreshold = segmentIgnoreThreshold || 0;\n            if (segmentIgnoreThreshold > 0) {\n                this._ux = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sx) || 0;\n                this._uy = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sy) || 0;\n            }\n        };\n        PathProxy.prototype.setDPR = function (dpr) {\n            this.dpr = dpr;\n        };\n        PathProxy.prototype.setContext = function (ctx) {\n            this._ctx = ctx;\n        };\n        PathProxy.prototype.getContext = function () {\n            return this._ctx;\n        };\n        PathProxy.prototype.beginPath = function () {\n            this._ctx && this._ctx.beginPath();\n            this.reset();\n            return this;\n        };\n        PathProxy.prototype.reset = function () {\n            if (this._saveData) {\n                this._len = 0;\n            }\n            if (this._pathSegLen) {\n                this._pathSegLen = null;\n                this._pathLen = 0;\n            }\n            this._version++;\n        };\n        PathProxy.prototype.moveTo = function (x, y) {\n            this._drawPendingPt();\n            this.addData(CMD.M, x, y);\n            this._ctx && this._ctx.moveTo(x, y);\n            this._x0 = x;\n            this._y0 = y;\n            this._xi = x;\n            this._yi = y;\n            return this;\n        };\n        PathProxy.prototype.lineTo = function (x, y) {\n            var dx = mathAbs(x - this._xi);\n            var dy = mathAbs(y - this._yi);\n            var exceedUnit = dx > this._ux || dy > this._uy;\n            this.addData(CMD.L, x, y);\n            if (this._ctx && exceedUnit) {\n                this._ctx.lineTo(x, y);\n            }\n            if (exceedUnit) {\n                this._xi = x;\n                this._yi = y;\n                this._pendingPtDist = 0;\n            }\n            else {\n                var d2 = dx * dx + dy * dy;\n                if (d2 > this._pendingPtDist) {\n                    this._pendingPtX = x;\n                    this._pendingPtY = y;\n                    this._pendingPtDist = d2;\n                }\n            }\n            return this;\n        };\n        PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) {\n            this._drawPendingPt();\n            this.addData(CMD.C, x1, y1, x2, y2, x3, y3);\n            if (this._ctx) {\n                this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n            }\n            this._xi = x3;\n            this._yi = y3;\n            return this;\n        };\n        PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) {\n            this._drawPendingPt();\n            this.addData(CMD.Q, x1, y1, x2, y2);\n            if (this._ctx) {\n                this._ctx.quadraticCurveTo(x1, y1, x2, y2);\n            }\n            this._xi = x2;\n            this._yi = y2;\n            return this;\n        };\n        PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n            this._drawPendingPt();\n            tmpAngles[0] = startAngle;\n            tmpAngles[1] = endAngle;\n            normalizeArcAngles(tmpAngles, anticlockwise);\n            startAngle = tmpAngles[0];\n            endAngle = tmpAngles[1];\n            var delta = endAngle - startAngle;\n            this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 0, anticlockwise ? 0 : 1);\n            this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n            this._xi = mathCos$1(endAngle) * r + cx;\n            this._yi = mathSin$1(endAngle) * r + cy;\n            return this;\n        };\n        PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) {\n            this._drawPendingPt();\n            if (this._ctx) {\n                this._ctx.arcTo(x1, y1, x2, y2, radius);\n            }\n            return this;\n        };\n        PathProxy.prototype.rect = function (x, y, w, h) {\n            this._drawPendingPt();\n            this._ctx && this._ctx.rect(x, y, w, h);\n            this.addData(CMD.R, x, y, w, h);\n            return this;\n        };\n        PathProxy.prototype.closePath = function () {\n            this._drawPendingPt();\n            this.addData(CMD.Z);\n            var ctx = this._ctx;\n            var x0 = this._x0;\n            var y0 = this._y0;\n            if (ctx) {\n                ctx.closePath();\n            }\n            this._xi = x0;\n            this._yi = y0;\n            return this;\n        };\n        PathProxy.prototype.fill = function (ctx) {\n            ctx && ctx.fill();\n            this.toStatic();\n        };\n        PathProxy.prototype.stroke = function (ctx) {\n            ctx && ctx.stroke();\n            this.toStatic();\n        };\n        PathProxy.prototype.len = function () {\n            return this._len;\n        };\n        PathProxy.prototype.setData = function (data) {\n            var len = data.length;\n            if (!(this.data && this.data.length === len) && hasTypedArray) {\n                this.data = new Float32Array(len);\n            }\n            for (var i = 0; i < len; i++) {\n                this.data[i] = data[i];\n            }\n            this._len = len;\n        };\n        PathProxy.prototype.appendPath = function (path) {\n            if (!(path instanceof Array)) {\n                path = [path];\n            }\n            var len = path.length;\n            var appendSize = 0;\n            var offset = this._len;\n            for (var i = 0; i < len; i++) {\n                appendSize += path[i].len();\n            }\n            if (hasTypedArray && (this.data instanceof Float32Array)) {\n                this.data = new Float32Array(offset + appendSize);\n            }\n            for (var i = 0; i < len; i++) {\n                var appendPathData = path[i].data;\n                for (var k = 0; k < appendPathData.length; k++) {\n                    this.data[offset++] = appendPathData[k];\n                }\n            }\n            this._len = offset;\n        };\n        PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) {\n            if (!this._saveData) {\n                return;\n            }\n            var data = this.data;\n            if (this._len + arguments.length > data.length) {\n                this._expandData();\n                data = this.data;\n            }\n            for (var i = 0; i < arguments.length; i++) {\n                data[this._len++] = arguments[i];\n            }\n        };\n        PathProxy.prototype._drawPendingPt = function () {\n            if (this._pendingPtDist > 0) {\n                this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY);\n                this._pendingPtDist = 0;\n            }\n        };\n        PathProxy.prototype._expandData = function () {\n            if (!(this.data instanceof Array)) {\n                var newData = [];\n                for (var i = 0; i < this._len; i++) {\n                    newData[i] = this.data[i];\n                }\n                this.data = newData;\n            }\n        };\n        PathProxy.prototype.toStatic = function () {\n            if (!this._saveData) {\n                return;\n            }\n            this._drawPendingPt();\n            var data = this.data;\n            if (data instanceof Array) {\n                data.length = this._len;\n                if (hasTypedArray && this._len > 11) {\n                    this.data = new Float32Array(data);\n                }\n            }\n        };\n        PathProxy.prototype.getBoundingRect = function () {\n            min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;\n            max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;\n            var data = this.data;\n            var xi = 0;\n            var yi = 0;\n            var x0 = 0;\n            var y0 = 0;\n            var i;\n            for (i = 0; i < this._len;) {\n                var cmd = data[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = data[i];\n                    yi = data[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                switch (cmd) {\n                    case CMD.M:\n                        xi = x0 = data[i++];\n                        yi = y0 = data[i++];\n                        min2[0] = x0;\n                        min2[1] = y0;\n                        max2[0] = x0;\n                        max2[1] = y0;\n                        break;\n                    case CMD.L:\n                        fromLine(xi, yi, data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.C:\n                        fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.Q:\n                        fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.A:\n                        var cx = data[i++];\n                        var cy = data[i++];\n                        var rx = data[i++];\n                        var ry = data[i++];\n                        var startAngle = data[i++];\n                        var endAngle = data[i++] + startAngle;\n                        i += 1;\n                        var anticlockwise = !data[i++];\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R:\n                        x0 = xi = data[i++];\n                        y0 = yi = data[i++];\n                        var width = data[i++];\n                        var height = data[i++];\n                        fromLine(x0, y0, x0 + width, y0 + height, min2, max2);\n                        break;\n                    case CMD.Z:\n                        xi = x0;\n                        yi = y0;\n                        break;\n                }\n                min(min$1, min$1, min2);\n                max(max$1, max$1, max2);\n            }\n            if (i === 0) {\n                min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;\n            }\n            return new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]);\n        };\n        PathProxy.prototype._calculateLength = function () {\n            var data = this.data;\n            var len = this._len;\n            var ux = this._ux;\n            var uy = this._uy;\n            var xi = 0;\n            var yi = 0;\n            var x0 = 0;\n            var y0 = 0;\n            if (!this._pathSegLen) {\n                this._pathSegLen = [];\n            }\n            var pathSegLen = this._pathSegLen;\n            var pathTotalLen = 0;\n            var segCount = 0;\n            for (var i = 0; i < len;) {\n                var cmd = data[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = data[i];\n                    yi = data[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                var l = -1;\n                switch (cmd) {\n                    case CMD.M:\n                        xi = x0 = data[i++];\n                        yi = y0 = data[i++];\n                        break;\n                    case CMD.L: {\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        var dx = x2 - xi;\n                        var dy = y2 - yi;\n                        if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) {\n                            l = Math.sqrt(dx * dx + dy * dy);\n                            xi = x2;\n                            yi = y2;\n                        }\n                        break;\n                    }\n                    case CMD.C: {\n                        var x1 = data[i++];\n                        var y1 = data[i++];\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        var x3 = data[i++];\n                        var y3 = data[i++];\n                        l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10);\n                        xi = x3;\n                        yi = y3;\n                        break;\n                    }\n                    case CMD.Q: {\n                        var x1 = data[i++];\n                        var y1 = data[i++];\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        l = quadraticLength(xi, yi, x1, y1, x2, y2, 10);\n                        xi = x2;\n                        yi = y2;\n                        break;\n                    }\n                    case CMD.A:\n                        var cx = data[i++];\n                        var cy = data[i++];\n                        var rx = data[i++];\n                        var ry = data[i++];\n                        var startAngle = data[i++];\n                        var delta = data[i++];\n                        var endAngle = delta + startAngle;\n                        i += 1;\n                        var anticlockwise = !data[i++];\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        l = mathMax$2(rx, ry) * mathMin$2(PI2$1, Math.abs(delta));\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R: {\n                        x0 = xi = data[i++];\n                        y0 = yi = data[i++];\n                        var width = data[i++];\n                        var height = data[i++];\n                        l = width * 2 + height * 2;\n                        break;\n                    }\n                    case CMD.Z: {\n                        var dx = x0 - xi;\n                        var dy = y0 - yi;\n                        l = Math.sqrt(dx * dx + dy * dy);\n                        xi = x0;\n                        yi = y0;\n                        break;\n                    }\n                }\n                if (l >= 0) {\n                    pathSegLen[segCount++] = l;\n                    pathTotalLen += l;\n                }\n            }\n            this._pathLen = pathTotalLen;\n            return pathTotalLen;\n        };\n        PathProxy.prototype.rebuildPath = function (ctx, percent) {\n            var d = this.data;\n            var ux = this._ux;\n            var uy = this._uy;\n            var len = this._len;\n            var x0;\n            var y0;\n            var xi;\n            var yi;\n            var x;\n            var y;\n            var drawPart = percent < 1;\n            var pathSegLen;\n            var pathTotalLen;\n            var accumLength = 0;\n            var segCount = 0;\n            var displayedLength;\n            var pendingPtDist = 0;\n            var pendingPtX;\n            var pendingPtY;\n            if (drawPart) {\n                if (!this._pathSegLen) {\n                    this._calculateLength();\n                }\n                pathSegLen = this._pathSegLen;\n                pathTotalLen = this._pathLen;\n                displayedLength = percent * pathTotalLen;\n                if (!displayedLength) {\n                    return;\n                }\n            }\n            lo: for (var i = 0; i < len;) {\n                var cmd = d[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = d[i];\n                    yi = d[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                if (cmd !== CMD.L && pendingPtDist > 0) {\n                    ctx.lineTo(pendingPtX, pendingPtY);\n                    pendingPtDist = 0;\n                }\n                switch (cmd) {\n                    case CMD.M:\n                        x0 = xi = d[i++];\n                        y0 = yi = d[i++];\n                        ctx.moveTo(xi, yi);\n                        break;\n                    case CMD.L: {\n                        x = d[i++];\n                        y = d[i++];\n                        var dx = mathAbs(x - xi);\n                        var dy = mathAbs(y - yi);\n                        if (dx > ux || dy > uy) {\n                            if (drawPart) {\n                                var l = pathSegLen[segCount++];\n                                if (accumLength + l > displayedLength) {\n                                    var t = (displayedLength - accumLength) / l;\n                                    ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t);\n                                    break lo;\n                                }\n                                accumLength += l;\n                            }\n                            ctx.lineTo(x, y);\n                            xi = x;\n                            yi = y;\n                            pendingPtDist = 0;\n                        }\n                        else {\n                            var d2 = dx * dx + dy * dy;\n                            if (d2 > pendingPtDist) {\n                                pendingPtX = x;\n                                pendingPtY = y;\n                                pendingPtDist = d2;\n                            }\n                        }\n                        break;\n                    }\n                    case CMD.C: {\n                        var x1 = d[i++];\n                        var y1 = d[i++];\n                        var x2 = d[i++];\n                        var y2 = d[i++];\n                        var x3 = d[i++];\n                        var y3 = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                cubicSubdivide(xi, x1, x2, x3, t, tmpOutX);\n                                cubicSubdivide(yi, y1, y2, y3, t, tmpOutY);\n                                ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n                        xi = x3;\n                        yi = y3;\n                        break;\n                    }\n                    case CMD.Q: {\n                        var x1 = d[i++];\n                        var y1 = d[i++];\n                        var x2 = d[i++];\n                        var y2 = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                quadraticSubdivide(xi, x1, x2, t, tmpOutX);\n                                quadraticSubdivide(yi, y1, y2, t, tmpOutY);\n                                ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.quadraticCurveTo(x1, y1, x2, y2);\n                        xi = x2;\n                        yi = y2;\n                        break;\n                    }\n                    case CMD.A:\n                        var cx = d[i++];\n                        var cy = d[i++];\n                        var rx = d[i++];\n                        var ry = d[i++];\n                        var startAngle = d[i++];\n                        var delta = d[i++];\n                        var psi = d[i++];\n                        var anticlockwise = !d[i++];\n                        var r = (rx > ry) ? rx : ry;\n                        var isEllipse = mathAbs(rx - ry) > 1e-3;\n                        var endAngle = startAngle + delta;\n                        var breakBuild = false;\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                endAngle = startAngle + delta * (displayedLength - accumLength) / l;\n                                breakBuild = true;\n                            }\n                            accumLength += l;\n                        }\n                        if (isEllipse && ctx.ellipse) {\n                            ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise);\n                        }\n                        else {\n                            ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n                        }\n                        if (breakBuild) {\n                            break lo;\n                        }\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R:\n                        x0 = xi = d[i];\n                        y0 = yi = d[i + 1];\n                        x = d[i++];\n                        y = d[i++];\n                        var width = d[i++];\n                        var height = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var d_1 = displayedLength - accumLength;\n                                ctx.moveTo(x, y);\n                                ctx.lineTo(x + mathMin$2(d_1, width), y);\n                                d_1 -= width;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x + width, y + mathMin$2(d_1, height));\n                                }\n                                d_1 -= height;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x + mathMax$2(width - d_1, 0), y + height);\n                                }\n                                d_1 -= width;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x, y + mathMax$2(height - d_1, 0));\n                                }\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.rect(x, y, width, height);\n                        break;\n                    case CMD.Z:\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.closePath();\n                        xi = x0;\n                        yi = y0;\n                }\n            }\n        };\n        PathProxy.prototype.clone = function () {\n            var newProxy = new PathProxy();\n            var data = this.data;\n            newProxy.data = data.slice ? data.slice()\n                : Array.prototype.slice.call(data);\n            newProxy._len = this._len;\n            return newProxy;\n        };\n        PathProxy.CMD = CMD;\n        PathProxy.initDefaultProps = (function () {\n            var proto = PathProxy.prototype;\n            proto._saveData = true;\n            proto._ux = 0;\n            proto._uy = 0;\n            proto._pendingPtDist = 0;\n            proto._version = 0;\n        })();\n        return PathProxy;\n    }());\n\n    function containStroke(x0, y0, x1, y1, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        var _a = 0;\n        var _b = x0;\n        if ((y > y0 + _l && y > y1 + _l)\n            || (y < y0 - _l && y < y1 - _l)\n            || (x > x0 + _l && x > x1 + _l)\n            || (x < x0 - _l && x < x1 - _l)) {\n            return false;\n        }\n        if (x0 !== x1) {\n            _a = (y0 - y1) / (x0 - x1);\n            _b = (x0 * y1 - x1 * y0) / (x0 - x1);\n        }\n        else {\n            return Math.abs(x - x0) <= _l / 2;\n        }\n        var tmp = _a * x - y + _b;\n        var _s = tmp * tmp / (_a * _a + 1);\n        return _s <= _l / 2 * _l / 2;\n    }\n\n    function containStroke$1(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        if ((y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)\n            || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)\n            || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)\n            || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)) {\n            return false;\n        }\n        var d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null);\n        return d <= _l / 2;\n    }\n\n    function containStroke$2(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        if ((y > y0 + _l && y > y1 + _l && y > y2 + _l)\n            || (y < y0 - _l && y < y1 - _l && y < y2 - _l)\n            || (x > x0 + _l && x > x1 + _l && x > x2 + _l)\n            || (x < x0 - _l && x < x1 - _l && x < x2 - _l)) {\n            return false;\n        }\n        var d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null);\n        return d <= _l / 2;\n    }\n\n    var PI2$2 = Math.PI * 2;\n    function normalizeRadian(angle) {\n        angle %= PI2$2;\n        if (angle < 0) {\n            angle += PI2$2;\n        }\n        return angle;\n    }\n\n    var PI2$3 = Math.PI * 2;\n    function containStroke$3(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        x -= cx;\n        y -= cy;\n        var d = Math.sqrt(x * x + y * y);\n        if ((d - _l > r) || (d + _l < r)) {\n            return false;\n        }\n        if (Math.abs(startAngle - endAngle) % PI2$3 < 1e-4) {\n            return true;\n        }\n        if (anticlockwise) {\n            var tmp = startAngle;\n            startAngle = normalizeRadian(endAngle);\n            endAngle = normalizeRadian(tmp);\n        }\n        else {\n            startAngle = normalizeRadian(startAngle);\n            endAngle = normalizeRadian(endAngle);\n        }\n        if (startAngle > endAngle) {\n            endAngle += PI2$3;\n        }\n        var angle = Math.atan2(y, x);\n        if (angle < 0) {\n            angle += PI2$3;\n        }\n        return (angle >= startAngle && angle <= endAngle)\n            || (angle + PI2$3 >= startAngle && angle + PI2$3 <= endAngle);\n    }\n\n    function windingLine(x0, y0, x1, y1, x, y) {\n        if ((y > y0 && y > y1) || (y < y0 && y < y1)) {\n            return 0;\n        }\n        if (y1 === y0) {\n            return 0;\n        }\n        var t = (y - y0) / (y1 - y0);\n        var dir = y1 < y0 ? 1 : -1;\n        if (t === 1 || t === 0) {\n            dir = y1 < y0 ? 0.5 : -0.5;\n        }\n        var x_ = t * (x1 - x0) + x0;\n        return x_ === x ? Infinity : x_ > x ? dir : 0;\n    }\n\n    var CMD$1 = PathProxy.CMD;\n    var PI2$4 = Math.PI * 2;\n    var EPSILON$3 = 1e-4;\n    function isAroundEqual(a, b) {\n        return Math.abs(a - b) < EPSILON$3;\n    }\n    var roots = [-1, -1, -1];\n    var extrema = [-1, -1];\n    function swapExtrema() {\n        var tmp = extrema[0];\n        extrema[0] = extrema[1];\n        extrema[1] = tmp;\n    }\n    function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {\n        if ((y > y0 && y > y1 && y > y2 && y > y3)\n            || (y < y0 && y < y1 && y < y2 && y < y3)) {\n            return 0;\n        }\n        var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);\n        if (nRoots === 0) {\n            return 0;\n        }\n        else {\n            var w = 0;\n            var nExtrema = -1;\n            var y0_ = void 0;\n            var y1_ = void 0;\n            for (var i = 0; i < nRoots; i++) {\n                var t = roots[i];\n                var unit = (t === 0 || t === 1) ? 0.5 : 1;\n                var x_ = cubicAt(x0, x1, x2, x3, t);\n                if (x_ < x) {\n                    continue;\n                }\n                if (nExtrema < 0) {\n                    nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);\n                    if (extrema[1] < extrema[0] && nExtrema > 1) {\n                        swapExtrema();\n                    }\n                    y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);\n                    if (nExtrema > 1) {\n                        y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);\n                    }\n                }\n                if (nExtrema === 2) {\n                    if (t < extrema[0]) {\n                        w += y0_ < y0 ? unit : -unit;\n                    }\n                    else if (t < extrema[1]) {\n                        w += y1_ < y0_ ? unit : -unit;\n                    }\n                    else {\n                        w += y3 < y1_ ? unit : -unit;\n                    }\n                }\n                else {\n                    if (t < extrema[0]) {\n                        w += y0_ < y0 ? unit : -unit;\n                    }\n                    else {\n                        w += y3 < y0_ ? unit : -unit;\n                    }\n                }\n            }\n            return w;\n        }\n    }\n    function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {\n        if ((y > y0 && y > y1 && y > y2)\n            || (y < y0 && y < y1 && y < y2)) {\n            return 0;\n        }\n        var nRoots = quadraticRootAt(y0, y1, y2, y, roots);\n        if (nRoots === 0) {\n            return 0;\n        }\n        else {\n            var t = quadraticExtremum(y0, y1, y2);\n            if (t >= 0 && t <= 1) {\n                var w = 0;\n                var y_ = quadraticAt(y0, y1, y2, t);\n                for (var i = 0; i < nRoots; i++) {\n                    var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;\n                    var x_ = quadraticAt(x0, x1, x2, roots[i]);\n                    if (x_ < x) {\n                        continue;\n                    }\n                    if (roots[i] < t) {\n                        w += y_ < y0 ? unit : -unit;\n                    }\n                    else {\n                        w += y2 < y_ ? unit : -unit;\n                    }\n                }\n                return w;\n            }\n            else {\n                var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;\n                var x_ = quadraticAt(x0, x1, x2, roots[0]);\n                if (x_ < x) {\n                    return 0;\n                }\n                return y2 < y0 ? unit : -unit;\n            }\n        }\n    }\n    function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {\n        y -= cy;\n        if (y > r || y < -r) {\n            return 0;\n        }\n        var tmp = Math.sqrt(r * r - y * y);\n        roots[0] = -tmp;\n        roots[1] = tmp;\n        var dTheta = Math.abs(startAngle - endAngle);\n        if (dTheta < 1e-4) {\n            return 0;\n        }\n        if (dTheta >= PI2$4 - 1e-4) {\n            startAngle = 0;\n            endAngle = PI2$4;\n            var dir = anticlockwise ? 1 : -1;\n            if (x >= roots[0] + cx && x <= roots[1] + cx) {\n                return dir;\n            }\n            else {\n                return 0;\n            }\n        }\n        if (startAngle > endAngle) {\n            var tmp_1 = startAngle;\n            startAngle = endAngle;\n            endAngle = tmp_1;\n        }\n        if (startAngle < 0) {\n            startAngle += PI2$4;\n            endAngle += PI2$4;\n        }\n        var w = 0;\n        for (var i = 0; i < 2; i++) {\n            var x_ = roots[i];\n            if (x_ + cx > x) {\n                var angle = Math.atan2(y, x_);\n                var dir = anticlockwise ? 1 : -1;\n                if (angle < 0) {\n                    angle = PI2$4 + angle;\n                }\n                if ((angle >= startAngle && angle <= endAngle)\n                    || (angle + PI2$4 >= startAngle && angle + PI2$4 <= endAngle)) {\n                    if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {\n                        dir = -dir;\n                    }\n                    w += dir;\n                }\n            }\n        }\n        return w;\n    }\n    function containPath(path, lineWidth, isStroke, x, y) {\n        var data = path.data;\n        var len = path.len();\n        var w = 0;\n        var xi = 0;\n        var yi = 0;\n        var x0 = 0;\n        var y0 = 0;\n        var x1;\n        var y1;\n        for (var i = 0; i < len;) {\n            var cmd = data[i++];\n            var isFirst = i === 1;\n            if (cmd === CMD$1.M && i > 1) {\n                if (!isStroke) {\n                    w += windingLine(xi, yi, x0, y0, x, y);\n                }\n            }\n            if (isFirst) {\n                xi = data[i];\n                yi = data[i + 1];\n                x0 = xi;\n                y0 = yi;\n            }\n            switch (cmd) {\n                case CMD$1.M:\n                    x0 = data[i++];\n                    y0 = data[i++];\n                    xi = x0;\n                    yi = y0;\n                    break;\n                case CMD$1.L:\n                    if (isStroke) {\n                        if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.C:\n                    if (isStroke) {\n                        if (containStroke$1(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.Q:\n                    if (isStroke) {\n                        if (containStroke$2(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.A:\n                    var cx = data[i++];\n                    var cy = data[i++];\n                    var rx = data[i++];\n                    var ry = data[i++];\n                    var theta = data[i++];\n                    var dTheta = data[i++];\n                    i += 1;\n                    var anticlockwise = !!(1 - data[i++]);\n                    x1 = Math.cos(theta) * rx + cx;\n                    y1 = Math.sin(theta) * ry + cy;\n                    if (!isFirst) {\n                        w += windingLine(xi, yi, x1, y1, x, y);\n                    }\n                    else {\n                        x0 = x1;\n                        y0 = y1;\n                    }\n                    var _x = (x - cx) * ry / rx + cx;\n                    if (isStroke) {\n                        if (containStroke$3(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);\n                    }\n                    xi = Math.cos(theta + dTheta) * rx + cx;\n                    yi = Math.sin(theta + dTheta) * ry + cy;\n                    break;\n                case CMD$1.R:\n                    x0 = xi = data[i++];\n                    y0 = yi = data[i++];\n                    var width = data[i++];\n                    var height = data[i++];\n                    x1 = x0 + width;\n                    y1 = y0 + height;\n                    if (isStroke) {\n                        if (containStroke(x0, y0, x1, y0, lineWidth, x, y)\n                            || containStroke(x1, y0, x1, y1, lineWidth, x, y)\n                            || containStroke(x1, y1, x0, y1, lineWidth, x, y)\n                            || containStroke(x0, y1, x0, y0, lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(x1, y0, x1, y1, x, y);\n                        w += windingLine(x0, y1, x0, y0, x, y);\n                    }\n                    break;\n                case CMD$1.Z:\n                    if (isStroke) {\n                        if (containStroke(xi, yi, x0, y0, lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(xi, yi, x0, y0, x, y);\n                    }\n                    xi = x0;\n                    yi = y0;\n                    break;\n            }\n        }\n        if (!isStroke && !isAroundEqual(yi, y0)) {\n            w += windingLine(xi, yi, x0, y0, x, y) || 0;\n        }\n        return w !== 0;\n    }\n    function contain(pathProxy, x, y) {\n        return containPath(pathProxy, 0, false, x, y);\n    }\n    function containStroke$4(pathProxy, lineWidth, x, y) {\n        return containPath(pathProxy, lineWidth, true, x, y);\n    }\n\n    var DEFAULT_PATH_STYLE = defaults({\n        fill: '#000',\n        stroke: null,\n        strokePercent: 1,\n        fillOpacity: 1,\n        strokeOpacity: 1,\n        lineDashOffset: 0,\n        lineWidth: 1,\n        lineCap: 'butt',\n        miterLimit: 10,\n        strokeNoScale: false,\n        strokeFirst: false\n    }, DEFAULT_COMMON_STYLE);\n    var DEFAULT_PATH_ANIMATION_PROPS = {\n        style: defaults({\n            fill: true,\n            stroke: true,\n            strokePercent: true,\n            fillOpacity: true,\n            strokeOpacity: true,\n            lineDashOffset: true,\n            lineWidth: true,\n            miterLimit: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    var pathCopyParams = TRANSFORMABLE_PROPS.concat(['invisible',\n        'culling', 'z', 'z2', 'zlevel', 'parent'\n    ]);\n    var Path = (function (_super) {\n        __extends(Path, _super);\n        function Path(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Path.prototype.update = function () {\n            var _this = this;\n            _super.prototype.update.call(this);\n            var style = this.style;\n            if (style.decal) {\n                var decalEl = this._decalEl = this._decalEl || new Path();\n                if (decalEl.buildPath === Path.prototype.buildPath) {\n                    decalEl.buildPath = function (ctx) {\n                        _this.buildPath(ctx, _this.shape);\n                    };\n                }\n                decalEl.silent = true;\n                var decalElStyle = decalEl.style;\n                for (var key in style) {\n                    if (decalElStyle[key] !== style[key]) {\n                        decalElStyle[key] = style[key];\n                    }\n                }\n                decalElStyle.fill = style.fill ? style.decal : null;\n                decalElStyle.decal = null;\n                decalElStyle.shadowColor = null;\n                style.strokeFirst && (decalElStyle.stroke = null);\n                for (var i = 0; i < pathCopyParams.length; ++i) {\n                    decalEl[pathCopyParams[i]] = this[pathCopyParams[i]];\n                }\n                decalEl.__dirty |= REDRAW_BIT;\n            }\n            else if (this._decalEl) {\n                this._decalEl = null;\n            }\n        };\n        Path.prototype.getDecalElement = function () {\n            return this._decalEl;\n        };\n        Path.prototype._init = function (props) {\n            var keysArr = keys(props);\n            this.shape = this.getDefaultShape();\n            var defaultStyle = this.getDefaultStyle();\n            if (defaultStyle) {\n                this.useStyle(defaultStyle);\n            }\n            for (var i = 0; i < keysArr.length; i++) {\n                var key = keysArr[i];\n                var value = props[key];\n                if (key === 'style') {\n                    if (!this.style) {\n                        this.useStyle(value);\n                    }\n                    else {\n                        extend(this.style, value);\n                    }\n                }\n                else if (key === 'shape') {\n                    extend(this.shape, value);\n                }\n                else {\n                    _super.prototype.attrKV.call(this, key, value);\n                }\n            }\n            if (!this.style) {\n                this.useStyle({});\n            }\n        };\n        Path.prototype.getDefaultStyle = function () {\n            return null;\n        };\n        Path.prototype.getDefaultShape = function () {\n            return {};\n        };\n        Path.prototype.canBeInsideText = function () {\n            return this.hasFill();\n        };\n        Path.prototype.getInsideTextFill = function () {\n            var pathFill = this.style.fill;\n            if (pathFill !== 'none') {\n                if (isString(pathFill)) {\n                    var fillLum = lum(pathFill, 0);\n                    if (fillLum > 0.5) {\n                        return DARK_LABEL_COLOR;\n                    }\n                    else if (fillLum > 0.2) {\n                        return LIGHTER_LABEL_COLOR;\n                    }\n                    return LIGHT_LABEL_COLOR;\n                }\n                else if (pathFill) {\n                    return LIGHT_LABEL_COLOR;\n                }\n            }\n            return DARK_LABEL_COLOR;\n        };\n        Path.prototype.getInsideTextStroke = function (textFill) {\n            var pathFill = this.style.fill;\n            if (isString(pathFill)) {\n                var zr = this.__zr;\n                var isDarkMode = !!(zr && zr.isDarkMode());\n                var isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD;\n                if (isDarkMode === isDarkLabel) {\n                    return pathFill;\n                }\n            }\n        };\n        Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { };\n        Path.prototype.pathUpdated = function () {\n            this.__dirty &= ~SHAPE_CHANGED_BIT;\n        };\n        Path.prototype.getUpdatedPathProxy = function (inBatch) {\n            !this.path && this.createPathProxy();\n            this.path.beginPath();\n            this.buildPath(this.path, this.shape, inBatch);\n            return this.path;\n        };\n        Path.prototype.createPathProxy = function () {\n            this.path = new PathProxy(false);\n        };\n        Path.prototype.hasStroke = function () {\n            var style = this.style;\n            var stroke = style.stroke;\n            return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n        };\n        Path.prototype.hasFill = function () {\n            var style = this.style;\n            var fill = style.fill;\n            return fill != null && fill !== 'none';\n        };\n        Path.prototype.getBoundingRect = function () {\n            var rect = this._rect;\n            var style = this.style;\n            var needsUpdateRect = !rect;\n            if (needsUpdateRect) {\n                var firstInvoke = false;\n                if (!this.path) {\n                    firstInvoke = true;\n                    this.createPathProxy();\n                }\n                var path = this.path;\n                if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) {\n                    path.beginPath();\n                    this.buildPath(path, this.shape, false);\n                    this.pathUpdated();\n                }\n                rect = path.getBoundingRect();\n            }\n            this._rect = rect;\n            if (this.hasStroke() && this.path && this.path.len() > 0) {\n                var rectStroke = this._rectStroke || (this._rectStroke = rect.clone());\n                if (this.__dirty || needsUpdateRect) {\n                    rectStroke.copy(rect);\n                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                    var w = style.lineWidth;\n                    if (!this.hasFill()) {\n                        var strokeContainThreshold = this.strokeContainThreshold;\n                        w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold);\n                    }\n                    if (lineScale > 1e-10) {\n                        rectStroke.width += w / lineScale;\n                        rectStroke.height += w / lineScale;\n                        rectStroke.x -= w / lineScale / 2;\n                        rectStroke.y -= w / lineScale / 2;\n                    }\n                }\n                return rectStroke;\n            }\n            return rect;\n        };\n        Path.prototype.contain = function (x, y) {\n            var localPos = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            var style = this.style;\n            x = localPos[0];\n            y = localPos[1];\n            if (rect.contain(x, y)) {\n                var pathProxy = this.path;\n                if (this.hasStroke()) {\n                    var lineWidth = style.lineWidth;\n                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                    if (lineScale > 1e-10) {\n                        if (!this.hasFill()) {\n                            lineWidth = Math.max(lineWidth, this.strokeContainThreshold);\n                        }\n                        if (containStroke$4(pathProxy, lineWidth / lineScale, x, y)) {\n                            return true;\n                        }\n                    }\n                }\n                if (this.hasFill()) {\n                    return contain(pathProxy, x, y);\n                }\n            }\n            return false;\n        };\n        Path.prototype.dirtyShape = function () {\n            this.__dirty |= SHAPE_CHANGED_BIT;\n            if (this._rect) {\n                this._rect = null;\n            }\n            if (this._decalEl) {\n                this._decalEl.dirtyShape();\n            }\n            this.markRedraw();\n        };\n        Path.prototype.dirty = function () {\n            this.dirtyStyle();\n            this.dirtyShape();\n        };\n        Path.prototype.animateShape = function (loop) {\n            return this.animate('shape', loop);\n        };\n        Path.prototype.updateDuringAnimation = function (targetKey) {\n            if (targetKey === 'style') {\n                this.dirtyStyle();\n            }\n            else if (targetKey === 'shape') {\n                this.dirtyShape();\n            }\n            else {\n                this.markRedraw();\n            }\n        };\n        Path.prototype.attrKV = function (key, value) {\n            if (key === 'shape') {\n                this.setShape(value);\n            }\n            else {\n                _super.prototype.attrKV.call(this, key, value);\n            }\n        };\n        Path.prototype.setShape = function (keyOrObj, value) {\n            var shape = this.shape;\n            if (!shape) {\n                shape = this.shape = {};\n            }\n            if (typeof keyOrObj === 'string') {\n                shape[keyOrObj] = value;\n            }\n            else {\n                extend(shape, keyOrObj);\n            }\n            this.dirtyShape();\n            return this;\n        };\n        Path.prototype.shapeChanged = function () {\n            return !!(this.__dirty & SHAPE_CHANGED_BIT);\n        };\n        Path.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_PATH_STYLE, obj);\n        };\n        Path.prototype._innerSaveToNormal = function (toState) {\n            _super.prototype._innerSaveToNormal.call(this, toState);\n            var normalState = this._normalState;\n            if (toState.shape && !normalState.shape) {\n                normalState.shape = extend({}, this.shape);\n            }\n        };\n        Path.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            var targetShape;\n            if (state && state.shape) {\n                if (transition) {\n                    if (keepCurrentStates) {\n                        targetShape = state.shape;\n                    }\n                    else {\n                        targetShape = extend({}, normalState.shape);\n                        extend(targetShape, state.shape);\n                    }\n                }\n                else {\n                    targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape);\n                    extend(targetShape, state.shape);\n                }\n            }\n            else if (needsRestoreToNormal) {\n                targetShape = normalState.shape;\n            }\n            if (targetShape) {\n                if (transition) {\n                    this.shape = extend({}, this.shape);\n                    var targetShapePrimaryProps = {};\n                    var shapeKeys = keys(targetShape);\n                    for (var i = 0; i < shapeKeys.length; i++) {\n                        var key = shapeKeys[i];\n                        if (typeof targetShape[key] === 'object') {\n                            this.shape[key] = targetShape[key];\n                        }\n                        else {\n                            targetShapePrimaryProps[key] = targetShape[key];\n                        }\n                    }\n                    this._transitionState(stateName, {\n                        shape: targetShapePrimaryProps\n                    }, animationCfg);\n                }\n                else {\n                    this.shape = targetShape;\n                    this.dirtyShape();\n                }\n            }\n        };\n        Path.prototype._mergeStates = function (states) {\n            var mergedState = _super.prototype._mergeStates.call(this, states);\n            var mergedShape;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                if (state.shape) {\n                    mergedShape = mergedShape || {};\n                    this._mergeStyle(mergedShape, state.shape);\n                }\n            }\n            if (mergedShape) {\n                mergedState.shape = mergedShape;\n            }\n            return mergedState;\n        };\n        Path.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_PATH_ANIMATION_PROPS;\n        };\n        Path.prototype.isZeroArea = function () {\n            return false;\n        };\n        Path.extend = function (defaultProps) {\n            var Sub = (function (_super) {\n                __extends(Sub, _super);\n                function Sub(opts) {\n                    var _this = _super.call(this, opts) || this;\n                    defaultProps.init && defaultProps.init.call(_this, opts);\n                    return _this;\n                }\n                Sub.prototype.getDefaultStyle = function () {\n                    return clone(defaultProps.style);\n                };\n                Sub.prototype.getDefaultShape = function () {\n                    return clone(defaultProps.shape);\n                };\n                return Sub;\n            }(Path));\n            for (var key in defaultProps) {\n                if (typeof defaultProps[key] === 'function') {\n                    Sub.prototype[key] = defaultProps[key];\n                }\n            }\n            return Sub;\n        };\n        Path.initDefaultProps = (function () {\n            var pathProto = Path.prototype;\n            pathProto.type = 'path';\n            pathProto.strokeContainThreshold = 5;\n            pathProto.segmentIgnoreThreshold = 0;\n            pathProto.subPixelOptimize = false;\n            pathProto.autoBatch = false;\n            pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT;\n        })();\n        return Path;\n    }(Displayable));\n\n    var DEFAULT_TSPAN_STYLE = defaults({\n        strokeFirst: true,\n        font: DEFAULT_FONT,\n        x: 0,\n        y: 0,\n        textAlign: 'left',\n        textBaseline: 'top',\n        miterLimit: 2\n    }, DEFAULT_PATH_STYLE);\n    var TSpan = (function (_super) {\n        __extends(TSpan, _super);\n        function TSpan() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        TSpan.prototype.hasStroke = function () {\n            var style = this.style;\n            var stroke = style.stroke;\n            return stroke != null && stroke !== 'none' && style.lineWidth > 0;\n        };\n        TSpan.prototype.hasFill = function () {\n            var style = this.style;\n            var fill = style.fill;\n            return fill != null && fill !== 'none';\n        };\n        TSpan.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_TSPAN_STYLE, obj);\n        };\n        TSpan.prototype.setBoundingRect = function (rect) {\n            this._rect = rect;\n        };\n        TSpan.prototype.getBoundingRect = function () {\n            var style = this.style;\n            if (!this._rect) {\n                var text = style.text;\n                text != null ? (text += '') : (text = '');\n                var rect = getBoundingRect(text, style.font, style.textAlign, style.textBaseline);\n                rect.x += style.x || 0;\n                rect.y += style.y || 0;\n                if (this.hasStroke()) {\n                    var w = style.lineWidth;\n                    rect.x -= w / 2;\n                    rect.y -= w / 2;\n                    rect.width += w;\n                    rect.height += w;\n                }\n                this._rect = rect;\n            }\n            return this._rect;\n        };\n        TSpan.initDefaultProps = (function () {\n            var tspanProto = TSpan.prototype;\n            tspanProto.dirtyRectTolerance = 10;\n        })();\n        return TSpan;\n    }(Displayable));\n    TSpan.prototype.type = 'tspan';\n\n    var DEFAULT_IMAGE_STYLE = defaults({\n        x: 0,\n        y: 0\n    }, DEFAULT_COMMON_STYLE);\n    var DEFAULT_IMAGE_ANIMATION_PROPS = {\n        style: defaults({\n            x: true,\n            y: true,\n            width: true,\n            height: true,\n            sx: true,\n            sy: true,\n            sWidth: true,\n            sHeight: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    function isImageLike(source) {\n        return !!(source\n            && typeof source !== 'string'\n            && source.width && source.height);\n    }\n    var ZRImage = (function (_super) {\n        __extends(ZRImage, _super);\n        function ZRImage() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        ZRImage.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_IMAGE_STYLE, obj);\n        };\n        ZRImage.prototype._getSize = function (dim) {\n            var style = this.style;\n            var size = style[dim];\n            if (size != null) {\n                return size;\n            }\n            var imageSource = isImageLike(style.image)\n                ? style.image : this.__image;\n            if (!imageSource) {\n                return 0;\n            }\n            var otherDim = dim === 'width' ? 'height' : 'width';\n            var otherDimSize = style[otherDim];\n            if (otherDimSize == null) {\n                return imageSource[dim];\n            }\n            else {\n                return imageSource[dim] / imageSource[otherDim] * otherDimSize;\n            }\n        };\n        ZRImage.prototype.getWidth = function () {\n            return this._getSize('width');\n        };\n        ZRImage.prototype.getHeight = function () {\n            return this._getSize('height');\n        };\n        ZRImage.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_IMAGE_ANIMATION_PROPS;\n        };\n        ZRImage.prototype.getBoundingRect = function () {\n            var style = this.style;\n            if (!this._rect) {\n                this._rect = new BoundingRect(style.x || 0, style.y || 0, this.getWidth(), this.getHeight());\n            }\n            return this._rect;\n        };\n        return ZRImage;\n    }(Displayable));\n    ZRImage.prototype.type = 'image';\n\n    function buildPath(ctx, shape) {\n        var x = shape.x;\n        var y = shape.y;\n        var width = shape.width;\n        var height = shape.height;\n        var r = shape.r;\n        var r1;\n        var r2;\n        var r3;\n        var r4;\n        if (width < 0) {\n            x = x + width;\n            width = -width;\n        }\n        if (height < 0) {\n            y = y + height;\n            height = -height;\n        }\n        if (typeof r === 'number') {\n            r1 = r2 = r3 = r4 = r;\n        }\n        else if (r instanceof Array) {\n            if (r.length === 1) {\n                r1 = r2 = r3 = r4 = r[0];\n            }\n            else if (r.length === 2) {\n                r1 = r3 = r[0];\n                r2 = r4 = r[1];\n            }\n            else if (r.length === 3) {\n                r1 = r[0];\n                r2 = r4 = r[1];\n                r3 = r[2];\n            }\n            else {\n                r1 = r[0];\n                r2 = r[1];\n                r3 = r[2];\n                r4 = r[3];\n            }\n        }\n        else {\n            r1 = r2 = r3 = r4 = 0;\n        }\n        var total;\n        if (r1 + r2 > width) {\n            total = r1 + r2;\n            r1 *= width / total;\n            r2 *= width / total;\n        }\n        if (r3 + r4 > width) {\n            total = r3 + r4;\n            r3 *= width / total;\n            r4 *= width / total;\n        }\n        if (r2 + r3 > height) {\n            total = r2 + r3;\n            r2 *= height / total;\n            r3 *= height / total;\n        }\n        if (r1 + r4 > height) {\n            total = r1 + r4;\n            r1 *= height / total;\n            r4 *= height / total;\n        }\n        ctx.moveTo(x + r1, y);\n        ctx.lineTo(x + width - r2, y);\n        r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0);\n        ctx.lineTo(x + width, y + height - r3);\n        r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2);\n        ctx.lineTo(x + r4, y + height);\n        r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI);\n        ctx.lineTo(x, y + r1);\n        r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5);\n    }\n\n    var round$1 = Math.round;\n    function subPixelOptimizeLine(outputShape, inputShape, style) {\n        if (!inputShape) {\n            return;\n        }\n        var x1 = inputShape.x1;\n        var x2 = inputShape.x2;\n        var y1 = inputShape.y1;\n        var y2 = inputShape.y2;\n        outputShape.x1 = x1;\n        outputShape.x2 = x2;\n        outputShape.y1 = y1;\n        outputShape.y2 = y2;\n        var lineWidth = style && style.lineWidth;\n        if (!lineWidth) {\n            return outputShape;\n        }\n        if (round$1(x1 * 2) === round$1(x2 * 2)) {\n            outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true);\n        }\n        if (round$1(y1 * 2) === round$1(y2 * 2)) {\n            outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true);\n        }\n        return outputShape;\n    }\n    function subPixelOptimizeRect(outputShape, inputShape, style) {\n        if (!inputShape) {\n            return;\n        }\n        var originX = inputShape.x;\n        var originY = inputShape.y;\n        var originWidth = inputShape.width;\n        var originHeight = inputShape.height;\n        outputShape.x = originX;\n        outputShape.y = originY;\n        outputShape.width = originWidth;\n        outputShape.height = originHeight;\n        var lineWidth = style && style.lineWidth;\n        if (!lineWidth) {\n            return outputShape;\n        }\n        outputShape.x = subPixelOptimize(originX, lineWidth, true);\n        outputShape.y = subPixelOptimize(originY, lineWidth, true);\n        outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1);\n        outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1);\n        return outputShape;\n    }\n    function subPixelOptimize(position, lineWidth, positiveOrNegative) {\n        if (!lineWidth) {\n            return position;\n        }\n        var doubledPosition = round$1(position * 2);\n        return (doubledPosition + round$1(lineWidth)) % 2 === 0\n            ? doubledPosition / 2\n            : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;\n    }\n\n    var RectShape = (function () {\n        function RectShape() {\n            this.x = 0;\n            this.y = 0;\n            this.width = 0;\n            this.height = 0;\n        }\n        return RectShape;\n    }());\n    var subPixelOptimizeOutputShape = {};\n    var Rect = (function (_super) {\n        __extends(Rect, _super);\n        function Rect(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Rect.prototype.getDefaultShape = function () {\n            return new RectShape();\n        };\n        Rect.prototype.buildPath = function (ctx, shape) {\n            var x;\n            var y;\n            var width;\n            var height;\n            if (this.subPixelOptimize) {\n                var optimizedShape = subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style);\n                x = optimizedShape.x;\n                y = optimizedShape.y;\n                width = optimizedShape.width;\n                height = optimizedShape.height;\n                optimizedShape.r = shape.r;\n                shape = optimizedShape;\n            }\n            else {\n                x = shape.x;\n                y = shape.y;\n                width = shape.width;\n                height = shape.height;\n            }\n            if (!shape.r) {\n                ctx.rect(x, y, width, height);\n            }\n            else {\n                buildPath(ctx, shape);\n            }\n        };\n        Rect.prototype.isZeroArea = function () {\n            return !this.shape.width || !this.shape.height;\n        };\n        return Rect;\n    }(Path));\n    Rect.prototype.type = 'rect';\n\n    var DEFAULT_RICH_TEXT_COLOR = {\n        fill: '#000'\n    };\n    var DEFAULT_STROKE_LINE_WIDTH = 2;\n    var DEFAULT_TEXT_ANIMATION_PROPS = {\n        style: defaults({\n            fill: true,\n            stroke: true,\n            fillOpacity: true,\n            strokeOpacity: true,\n            lineWidth: true,\n            fontSize: true,\n            lineHeight: true,\n            width: true,\n            height: true,\n            textShadowColor: true,\n            textShadowBlur: true,\n            textShadowOffsetX: true,\n            textShadowOffsetY: true,\n            backgroundColor: true,\n            padding: true,\n            borderColor: true,\n            borderWidth: true,\n            borderRadius: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    var ZRText = (function (_super) {\n        __extends(ZRText, _super);\n        function ZRText(opts) {\n            var _this = _super.call(this) || this;\n            _this.type = 'text';\n            _this._children = [];\n            _this._defaultStyle = DEFAULT_RICH_TEXT_COLOR;\n            _this.attr(opts);\n            return _this;\n        }\n        ZRText.prototype.childrenRef = function () {\n            return this._children;\n        };\n        ZRText.prototype.update = function () {\n            _super.prototype.update.call(this);\n            if (this.styleChanged()) {\n                this._updateSubTexts();\n            }\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.zlevel = this.zlevel;\n                child.z = this.z;\n                child.z2 = this.z2;\n                child.culling = this.culling;\n                child.cursor = this.cursor;\n                child.invisible = this.invisible;\n            }\n        };\n        ZRText.prototype.updateTransform = function () {\n            var innerTransformable = this.innerTransformable;\n            if (innerTransformable) {\n                innerTransformable.updateTransform();\n                if (innerTransformable.transform) {\n                    this.transform = innerTransformable.transform;\n                }\n            }\n            else {\n                _super.prototype.updateTransform.call(this);\n            }\n        };\n        ZRText.prototype.getLocalTransform = function (m) {\n            var innerTransformable = this.innerTransformable;\n            return innerTransformable\n                ? innerTransformable.getLocalTransform(m)\n                : _super.prototype.getLocalTransform.call(this, m);\n        };\n        ZRText.prototype.getComputedTransform = function () {\n            if (this.__hostTarget) {\n                this.__hostTarget.getComputedTransform();\n                this.__hostTarget.updateInnerText(true);\n            }\n            return _super.prototype.getComputedTransform.call(this);\n        };\n        ZRText.prototype._updateSubTexts = function () {\n            this._childCursor = 0;\n            normalizeTextStyle(this.style);\n            this.style.rich\n                ? this._updateRichTexts()\n                : this._updatePlainTexts();\n            this._children.length = this._childCursor;\n            this.styleUpdated();\n        };\n        ZRText.prototype.addSelfToZr = function (zr) {\n            _super.prototype.addSelfToZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                this._children[i].__zr = zr;\n            }\n        };\n        ZRText.prototype.removeSelfFromZr = function (zr) {\n            _super.prototype.removeSelfFromZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                this._children[i].__zr = null;\n            }\n        };\n        ZRText.prototype.getBoundingRect = function () {\n            if (this.styleChanged()) {\n                this._updateSubTexts();\n            }\n            if (!this._rect) {\n                var tmpRect = new BoundingRect(0, 0, 0, 0);\n                var children = this._children;\n                var tmpMat = [];\n                var rect = null;\n                for (var i = 0; i < children.length; i++) {\n                    var child = children[i];\n                    var childRect = child.getBoundingRect();\n                    var transform = child.getLocalTransform(tmpMat);\n                    if (transform) {\n                        tmpRect.copy(childRect);\n                        tmpRect.applyTransform(transform);\n                        rect = rect || tmpRect.clone();\n                        rect.union(tmpRect);\n                    }\n                    else {\n                        rect = rect || childRect.clone();\n                        rect.union(childRect);\n                    }\n                }\n                this._rect = rect || tmpRect;\n            }\n            return this._rect;\n        };\n        ZRText.prototype.setDefaultTextStyle = function (defaultTextStyle) {\n            this._defaultStyle = defaultTextStyle || DEFAULT_RICH_TEXT_COLOR;\n        };\n        ZRText.prototype.setTextContent = function (textContent) {\n            if (\"development\" !== 'production') {\n                throw new Error('Can\\'t attach text on another text');\n            }\n        };\n        ZRText.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n            if (!sourceStyle) {\n                return targetStyle;\n            }\n            var sourceRich = sourceStyle.rich;\n            var targetRich = targetStyle.rich || (sourceRich && {});\n            extend(targetStyle, sourceStyle);\n            if (sourceRich && targetRich) {\n                this._mergeRich(targetRich, sourceRich);\n                targetStyle.rich = targetRich;\n            }\n            else if (targetRich) {\n                targetStyle.rich = targetRich;\n            }\n            return targetStyle;\n        };\n        ZRText.prototype._mergeRich = function (targetRich, sourceRich) {\n            var richNames = keys(sourceRich);\n            for (var i = 0; i < richNames.length; i++) {\n                var richName = richNames[i];\n                targetRich[richName] = targetRich[richName] || {};\n                extend(targetRich[richName], sourceRich[richName]);\n            }\n        };\n        ZRText.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_TEXT_ANIMATION_PROPS;\n        };\n        ZRText.prototype._getOrCreateChild = function (Ctor) {\n            var child = this._children[this._childCursor];\n            if (!child || !(child instanceof Ctor)) {\n                child = new Ctor();\n            }\n            this._children[this._childCursor++] = child;\n            child.__zr = this.__zr;\n            child.parent = this;\n            return child;\n        };\n        ZRText.prototype._updatePlainTexts = function () {\n            var style = this.style;\n            var textFont = style.font || DEFAULT_FONT;\n            var textPadding = style.padding;\n            var text = getStyleText(style);\n            var contentBlock = parsePlainText(text, style);\n            var needDrawBg = needDrawBackground(style);\n            var bgColorDrawn = !!(style.backgroundColor);\n            var outerHeight = contentBlock.outerHeight;\n            var outerWidth = contentBlock.outerWidth;\n            var contentWidth = contentBlock.contentWidth;\n            var textLines = contentBlock.lines;\n            var lineHeight = contentBlock.lineHeight;\n            var defaultStyle = this._defaultStyle;\n            var baseX = style.x || 0;\n            var baseY = style.y || 0;\n            var textAlign = style.align || defaultStyle.align || 'left';\n            var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign || 'top';\n            var textX = baseX;\n            var textY = adjustTextY$1(baseY, contentBlock.contentHeight, verticalAlign);\n            if (needDrawBg || textPadding) {\n                var boxX = adjustTextX(baseX, outerWidth, textAlign);\n                var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign);\n                needDrawBg && this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n            }\n            textY += lineHeight / 2;\n            if (textPadding) {\n                textX = getTextXForPadding(baseX, textAlign, textPadding);\n                if (verticalAlign === 'top') {\n                    textY += textPadding[0];\n                }\n                else if (verticalAlign === 'bottom') {\n                    textY -= textPadding[2];\n                }\n            }\n            var defaultLineWidth = 0;\n            var useDefaultFill = false;\n            var textFill = getFill('fill' in style\n                ? style.fill\n                : (useDefaultFill = true, defaultStyle.fill));\n            var textStroke = getStroke('stroke' in style\n                ? style.stroke\n                : (!bgColorDrawn\n                    && (!defaultStyle.autoStroke || useDefaultFill))\n                    ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                    : null);\n            var hasShadow = style.textShadowBlur > 0;\n            var fixedBoundingRect = style.width != null\n                && (style.overflow === 'truncate' || style.overflow === 'break' || style.overflow === 'breakAll');\n            var calculatedLineHeight = contentBlock.calculatedLineHeight;\n            for (var i = 0; i < textLines.length; i++) {\n                var el = this._getOrCreateChild(TSpan);\n                var subElStyle = el.createStyle();\n                el.useStyle(subElStyle);\n                subElStyle.text = textLines[i];\n                subElStyle.x = textX;\n                subElStyle.y = textY;\n                if (textAlign) {\n                    subElStyle.textAlign = textAlign;\n                }\n                subElStyle.textBaseline = 'middle';\n                subElStyle.opacity = style.opacity;\n                subElStyle.strokeFirst = true;\n                if (hasShadow) {\n                    subElStyle.shadowBlur = style.textShadowBlur || 0;\n                    subElStyle.shadowColor = style.textShadowColor || 'transparent';\n                    subElStyle.shadowOffsetX = style.textShadowOffsetX || 0;\n                    subElStyle.shadowOffsetY = style.textShadowOffsetY || 0;\n                }\n                subElStyle.stroke = textStroke;\n                subElStyle.fill = textFill;\n                if (textStroke) {\n                    subElStyle.lineWidth = style.lineWidth || defaultLineWidth;\n                    subElStyle.lineDash = style.lineDash;\n                    subElStyle.lineDashOffset = style.lineDashOffset || 0;\n                }\n                subElStyle.font = textFont;\n                setSeparateFont(subElStyle, style);\n                textY += lineHeight;\n                if (fixedBoundingRect) {\n                    el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, style.width, subElStyle.textAlign), adjustTextY$1(subElStyle.y, calculatedLineHeight, subElStyle.textBaseline), contentWidth, calculatedLineHeight));\n                }\n            }\n        };\n        ZRText.prototype._updateRichTexts = function () {\n            var style = this.style;\n            var text = getStyleText(style);\n            var contentBlock = parseRichText(text, style);\n            var contentWidth = contentBlock.width;\n            var outerWidth = contentBlock.outerWidth;\n            var outerHeight = contentBlock.outerHeight;\n            var textPadding = style.padding;\n            var baseX = style.x || 0;\n            var baseY = style.y || 0;\n            var defaultStyle = this._defaultStyle;\n            var textAlign = style.align || defaultStyle.align;\n            var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign;\n            var boxX = adjustTextX(baseX, outerWidth, textAlign);\n            var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign);\n            var xLeft = boxX;\n            var lineTop = boxY;\n            if (textPadding) {\n                xLeft += textPadding[3];\n                lineTop += textPadding[0];\n            }\n            var xRight = xLeft + contentWidth;\n            if (needDrawBackground(style)) {\n                this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n            }\n            var bgColorDrawn = !!(style.backgroundColor);\n            for (var i = 0; i < contentBlock.lines.length; i++) {\n                var line = contentBlock.lines[i];\n                var tokens = line.tokens;\n                var tokenCount = tokens.length;\n                var lineHeight = line.lineHeight;\n                var remainedWidth = line.width;\n                var leftIndex = 0;\n                var lineXLeft = xLeft;\n                var lineXRight = xRight;\n                var rightIndex = tokenCount - 1;\n                var token = void 0;\n                while (leftIndex < tokenCount\n                    && (token = tokens[leftIndex], !token.align || token.align === 'left')) {\n                    this._placeToken(token, style, lineHeight, lineTop, lineXLeft, 'left', bgColorDrawn);\n                    remainedWidth -= token.width;\n                    lineXLeft += token.width;\n                    leftIndex++;\n                }\n                while (rightIndex >= 0\n                    && (token = tokens[rightIndex], token.align === 'right')) {\n                    this._placeToken(token, style, lineHeight, lineTop, lineXRight, 'right', bgColorDrawn);\n                    remainedWidth -= token.width;\n                    lineXRight -= token.width;\n                    rightIndex--;\n                }\n                lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - remainedWidth) / 2;\n                while (leftIndex <= rightIndex) {\n                    token = tokens[leftIndex];\n                    this._placeToken(token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center', bgColorDrawn);\n                    lineXLeft += token.width;\n                    leftIndex++;\n                }\n                lineTop += lineHeight;\n            }\n        };\n        ZRText.prototype._placeToken = function (token, style, lineHeight, lineTop, x, textAlign, parentBgColorDrawn) {\n            var tokenStyle = style.rich[token.styleName] || {};\n            tokenStyle.text = token.text;\n            var verticalAlign = token.verticalAlign;\n            var y = lineTop + lineHeight / 2;\n            if (verticalAlign === 'top') {\n                y = lineTop + token.height / 2;\n            }\n            else if (verticalAlign === 'bottom') {\n                y = lineTop + lineHeight - token.height / 2;\n            }\n            var needDrawBg = !token.isLineHolder && needDrawBackground(tokenStyle);\n            needDrawBg && this._renderBackground(tokenStyle, style, textAlign === 'right'\n                ? x - token.width\n                : textAlign === 'center'\n                    ? x - token.width / 2\n                    : x, y - token.height / 2, token.width, token.height);\n            var bgColorDrawn = !!tokenStyle.backgroundColor;\n            var textPadding = token.textPadding;\n            if (textPadding) {\n                x = getTextXForPadding(x, textAlign, textPadding);\n                y -= token.height / 2 - textPadding[0] - token.innerHeight / 2;\n            }\n            var el = this._getOrCreateChild(TSpan);\n            var subElStyle = el.createStyle();\n            el.useStyle(subElStyle);\n            var defaultStyle = this._defaultStyle;\n            var useDefaultFill = false;\n            var defaultLineWidth = 0;\n            var textFill = getFill('fill' in tokenStyle ? tokenStyle.fill\n                : 'fill' in style ? style.fill\n                    : (useDefaultFill = true, defaultStyle.fill));\n            var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke\n                : 'stroke' in style ? style.stroke\n                    : (!bgColorDrawn\n                        && !parentBgColorDrawn\n                        && (!defaultStyle.autoStroke || useDefaultFill)) ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                        : null);\n            var hasShadow = tokenStyle.textShadowBlur > 0\n                || style.textShadowBlur > 0;\n            subElStyle.text = token.text;\n            subElStyle.x = x;\n            subElStyle.y = y;\n            if (hasShadow) {\n                subElStyle.shadowBlur = tokenStyle.textShadowBlur || style.textShadowBlur || 0;\n                subElStyle.shadowColor = tokenStyle.textShadowColor || style.textShadowColor || 'transparent';\n                subElStyle.shadowOffsetX = tokenStyle.textShadowOffsetX || style.textShadowOffsetX || 0;\n                subElStyle.shadowOffsetY = tokenStyle.textShadowOffsetY || style.textShadowOffsetY || 0;\n            }\n            subElStyle.textAlign = textAlign;\n            subElStyle.textBaseline = 'middle';\n            subElStyle.font = token.font || DEFAULT_FONT;\n            subElStyle.opacity = retrieve3(tokenStyle.opacity, style.opacity, 1);\n            setSeparateFont(subElStyle, tokenStyle);\n            if (textStroke) {\n                subElStyle.lineWidth = retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultLineWidth);\n                subElStyle.lineDash = retrieve2(tokenStyle.lineDash, style.lineDash);\n                subElStyle.lineDashOffset = style.lineDashOffset || 0;\n                subElStyle.stroke = textStroke;\n            }\n            if (textFill) {\n                subElStyle.fill = textFill;\n            }\n            var textWidth = token.contentWidth;\n            var textHeight = token.contentHeight;\n            el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, textWidth, subElStyle.textAlign), adjustTextY$1(subElStyle.y, textHeight, subElStyle.textBaseline), textWidth, textHeight));\n        };\n        ZRText.prototype._renderBackground = function (style, topStyle, x, y, width, height) {\n            var textBackgroundColor = style.backgroundColor;\n            var textBorderWidth = style.borderWidth;\n            var textBorderColor = style.borderColor;\n            var isImageBg = textBackgroundColor && textBackgroundColor.image;\n            var isPlainOrGradientBg = textBackgroundColor && !isImageBg;\n            var textBorderRadius = style.borderRadius;\n            var self = this;\n            var rectEl;\n            var imgEl;\n            if (isPlainOrGradientBg || style.lineHeight || (textBorderWidth && textBorderColor)) {\n                rectEl = this._getOrCreateChild(Rect);\n                rectEl.useStyle(rectEl.createStyle());\n                rectEl.style.fill = null;\n                var rectShape = rectEl.shape;\n                rectShape.x = x;\n                rectShape.y = y;\n                rectShape.width = width;\n                rectShape.height = height;\n                rectShape.r = textBorderRadius;\n                rectEl.dirtyShape();\n            }\n            if (isPlainOrGradientBg) {\n                var rectStyle = rectEl.style;\n                rectStyle.fill = textBackgroundColor || null;\n                rectStyle.fillOpacity = retrieve2(style.fillOpacity, 1);\n            }\n            else if (isImageBg) {\n                imgEl = this._getOrCreateChild(ZRImage);\n                imgEl.onload = function () {\n                    self.dirtyStyle();\n                };\n                var imgStyle = imgEl.style;\n                imgStyle.image = textBackgroundColor.image;\n                imgStyle.x = x;\n                imgStyle.y = y;\n                imgStyle.width = width;\n                imgStyle.height = height;\n            }\n            if (textBorderWidth && textBorderColor) {\n                var rectStyle = rectEl.style;\n                rectStyle.lineWidth = textBorderWidth;\n                rectStyle.stroke = textBorderColor;\n                rectStyle.strokeOpacity = retrieve2(style.strokeOpacity, 1);\n                rectStyle.lineDash = style.borderDash;\n                rectStyle.lineDashOffset = style.borderDashOffset || 0;\n                rectEl.strokeContainThreshold = 0;\n                if (rectEl.hasFill() && rectEl.hasStroke()) {\n                    rectStyle.strokeFirst = true;\n                    rectStyle.lineWidth *= 2;\n                }\n            }\n            var commonStyle = (rectEl || imgEl).style;\n            commonStyle.shadowBlur = style.shadowBlur || 0;\n            commonStyle.shadowColor = style.shadowColor || 'transparent';\n            commonStyle.shadowOffsetX = style.shadowOffsetX || 0;\n            commonStyle.shadowOffsetY = style.shadowOffsetY || 0;\n            commonStyle.opacity = retrieve3(style.opacity, topStyle.opacity, 1);\n        };\n        ZRText.makeFont = function (style) {\n            var font = '';\n            if (hasSeparateFont(style)) {\n                font = [\n                    style.fontStyle,\n                    style.fontWeight,\n                    parseFontSize(style.fontSize),\n                    style.fontFamily || 'sans-serif'\n                ].join(' ');\n            }\n            return font && trim(font) || style.textFont || style.font;\n        };\n        return ZRText;\n    }(Displayable));\n    var VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 };\n    var VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 };\n    var FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];\n    function parseFontSize(fontSize) {\n        if (typeof fontSize === 'string'\n            && (fontSize.indexOf('px') !== -1\n                || fontSize.indexOf('rem') !== -1\n                || fontSize.indexOf('em') !== -1)) {\n            return fontSize;\n        }\n        else if (!isNaN(+fontSize)) {\n            return fontSize + 'px';\n        }\n        else {\n            return DEFAULT_FONT_SIZE + 'px';\n        }\n    }\n    function setSeparateFont(targetStyle, sourceStyle) {\n        for (var i = 0; i < FONT_PARTS.length; i++) {\n            var fontProp = FONT_PARTS[i];\n            var val = sourceStyle[fontProp];\n            if (val != null) {\n                targetStyle[fontProp] = val;\n            }\n        }\n    }\n    function hasSeparateFont(style) {\n        return style.fontSize != null || style.fontFamily || style.fontWeight;\n    }\n    function normalizeTextStyle(style) {\n        normalizeStyle(style);\n        each(style.rich, normalizeStyle);\n        return style;\n    }\n    function normalizeStyle(style) {\n        if (style) {\n            style.font = ZRText.makeFont(style);\n            var textAlign = style.align;\n            textAlign === 'middle' && (textAlign = 'center');\n            style.align = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left';\n            var verticalAlign = style.verticalAlign;\n            verticalAlign === 'center' && (verticalAlign = 'middle');\n            style.verticalAlign = (verticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[verticalAlign]) ? verticalAlign : 'top';\n            var textPadding = style.padding;\n            if (textPadding) {\n                style.padding = normalizeCssArray(style.padding);\n            }\n        }\n    }\n    function getStroke(stroke, lineWidth) {\n        return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')\n            ? null\n            : (stroke.image || stroke.colorStops)\n                ? '#000'\n                : stroke;\n    }\n    function getFill(fill) {\n        return (fill == null || fill === 'none')\n            ? null\n            : (fill.image || fill.colorStops)\n                ? '#000'\n                : fill;\n    }\n    function getTextXForPadding(x, textAlign, textPadding) {\n        return textAlign === 'right'\n            ? (x - textPadding[1])\n            : textAlign === 'center'\n                ? (x + textPadding[3] / 2 - textPadding[1] / 2)\n                : (x + textPadding[3]);\n    }\n    function getStyleText(style) {\n        var text = style.text;\n        text != null && (text += '');\n        return text;\n    }\n    function needDrawBackground(style) {\n        return !!(style.backgroundColor\n            || style.lineHeight\n            || (style.borderWidth && style.borderColor));\n    }\n\n    var getECData = makeInner();\n    var setCommonECData = function (seriesIndex, dataType, dataIdx, el) {\n      if (el) {\n        var ecData = getECData(el); // Add data index and series index for indexing the data by element\n        // Useful in tooltip\n\n        ecData.dataIndex = dataIdx;\n        ecData.dataType = dataType;\n        ecData.seriesIndex = seriesIndex; // TODO: not store dataIndex on children.\n\n        if (el.type === 'group') {\n          el.traverse(function (child) {\n            var childECData = getECData(child);\n            childECData.seriesIndex = seriesIndex;\n            childECData.dataIndex = dataIdx;\n            childECData.dataType = dataType;\n          });\n        }\n      }\n    };\n\n    var _highlightNextDigit = 1;\n    var _highlightKeyMap = {};\n    var getSavedStates = makeInner();\n    var getComponentStates = makeInner();\n    var HOVER_STATE_NORMAL = 0;\n    var HOVER_STATE_BLUR = 1;\n    var HOVER_STATE_EMPHASIS = 2;\n    var SPECIAL_STATES = ['emphasis', 'blur', 'select'];\n    var DISPLAY_STATES = ['normal', 'emphasis', 'blur', 'select'];\n    var Z2_EMPHASIS_LIFT = 10;\n    var Z2_SELECT_LIFT = 9;\n    var HIGHLIGHT_ACTION_TYPE = 'highlight';\n    var DOWNPLAY_ACTION_TYPE = 'downplay';\n    var SELECT_ACTION_TYPE = 'select';\n    var UNSELECT_ACTION_TYPE = 'unselect';\n    var TOGGLE_SELECT_ACTION_TYPE = 'toggleSelect';\n\n    function hasFillOrStroke(fillOrStroke) {\n      return fillOrStroke != null && fillOrStroke !== 'none';\n    } // Most lifted color are duplicated.\n\n\n    var liftedColorCache = new LRU(100);\n\n    function liftColor(color$1) {\n      if (isString(color$1)) {\n        var liftedColor = liftedColorCache.get(color$1);\n\n        if (!liftedColor) {\n          liftedColor = lift(color$1, -0.1);\n          liftedColorCache.put(color$1, liftedColor);\n        }\n\n        return liftedColor;\n      } else if (isGradientObject(color$1)) {\n        var ret = extend({}, color$1);\n        ret.colorStops = map(color$1.colorStops, function (stop) {\n          return {\n            offset: stop.offset,\n            color: lift(stop.color, -0.1)\n          };\n        });\n        return ret;\n      } // Change nothing.\n\n\n      return color$1;\n    }\n\n    function doChangeHoverState(el, stateName, hoverStateEnum) {\n      if (el.onHoverStateChange && (el.hoverState || 0) !== hoverStateEnum) {\n        el.onHoverStateChange(stateName);\n      }\n\n      el.hoverState = hoverStateEnum;\n    }\n\n    function singleEnterEmphasis(el) {\n      // Only mark the flag.\n      // States will be applied in the echarts.ts in next frame.\n      doChangeHoverState(el, 'emphasis', HOVER_STATE_EMPHASIS);\n    }\n\n    function singleLeaveEmphasis(el) {\n      // Only mark the flag.\n      // States will be applied in the echarts.ts in next frame.\n      if (el.hoverState === HOVER_STATE_EMPHASIS) {\n        doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n      }\n    }\n\n    function singleEnterBlur(el) {\n      doChangeHoverState(el, 'blur', HOVER_STATE_BLUR);\n    }\n\n    function singleLeaveBlur(el) {\n      if (el.hoverState === HOVER_STATE_BLUR) {\n        doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n      }\n    }\n\n    function singleEnterSelect(el) {\n      el.selected = true;\n    }\n\n    function singleLeaveSelect(el) {\n      el.selected = false;\n    }\n\n    function updateElementState(el, updater, commonParam) {\n      updater(el, commonParam);\n    }\n\n    function traverseUpdateState(el, updater, commonParam) {\n      updateElementState(el, updater, commonParam);\n      el.isGroup && el.traverse(function (child) {\n        updateElementState(child, updater, commonParam);\n      });\n    }\n\n    function setStatesFlag(el, stateName) {\n      switch (stateName) {\n        case 'emphasis':\n          el.hoverState = HOVER_STATE_EMPHASIS;\n          break;\n\n        case 'normal':\n          el.hoverState = HOVER_STATE_NORMAL;\n          break;\n\n        case 'blur':\n          el.hoverState = HOVER_STATE_BLUR;\n          break;\n\n        case 'select':\n          el.selected = true;\n      }\n    }\n\n    function getFromStateStyle(el, props, toStateName, defaultValue) {\n      var style = el.style;\n      var fromState = {};\n\n      for (var i = 0; i < props.length; i++) {\n        var propName = props[i];\n        var val = style[propName];\n        fromState[propName] = val == null ? defaultValue && defaultValue[propName] : val;\n      }\n\n      for (var i = 0; i < el.animators.length; i++) {\n        var animator = el.animators[i];\n\n        if (animator.__fromStateTransition // Don't consider the animation to emphasis state.\n        && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') {\n          animator.saveTo(fromState, props);\n        }\n      }\n\n      return fromState;\n    }\n\n    function createEmphasisDefaultState(el, stateName, targetStates, state) {\n      var hasSelect = targetStates && indexOf(targetStates, 'select') >= 0;\n      var cloned = false;\n\n      if (el instanceof Path) {\n        var store = getSavedStates(el);\n        var fromFill = hasSelect ? store.selectFill || store.normalFill : store.normalFill;\n        var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke;\n\n        if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) {\n          state = state || {};\n          var emphasisStyle = state.style || {}; // inherit case\n\n          if (emphasisStyle.fill === 'inherit') {\n            cloned = true;\n            state = extend({}, state);\n            emphasisStyle = extend({}, emphasisStyle);\n            emphasisStyle.fill = fromFill;\n          } // Apply default color lift\n          else if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) {\n              cloned = true; // Not modify the original value.\n\n              state = extend({}, state);\n              emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times.\n\n              emphasisStyle.fill = liftColor(fromFill);\n            } // Not highlight stroke if fill has been highlighted.\n            else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) {\n                if (!cloned) {\n                  state = extend({}, state);\n                  emphasisStyle = extend({}, emphasisStyle);\n                }\n\n                emphasisStyle.stroke = liftColor(fromStroke);\n              }\n\n          state.style = emphasisStyle;\n        }\n      }\n\n      if (state) {\n        // TODO Share with textContent?\n        if (state.z2 == null) {\n          if (!cloned) {\n            state = extend({}, state);\n          }\n\n          var z2EmphasisLift = el.z2EmphasisLift;\n          state.z2 = el.z2 + (z2EmphasisLift != null ? z2EmphasisLift : Z2_EMPHASIS_LIFT);\n        }\n      }\n\n      return state;\n    }\n\n    function createSelectDefaultState(el, stateName, state) {\n      // const hasSelect = indexOf(el.currentStates, stateName) >= 0;\n      if (state) {\n        // TODO Share with textContent?\n        if (state.z2 == null) {\n          state = extend({}, state);\n          var z2SelectLift = el.z2SelectLift;\n          state.z2 = el.z2 + (z2SelectLift != null ? z2SelectLift : Z2_SELECT_LIFT);\n        }\n      }\n\n      return state;\n    }\n\n    function createBlurDefaultState(el, stateName, state) {\n      var hasBlur = indexOf(el.currentStates, stateName) >= 0;\n      var currentOpacity = el.style.opacity;\n      var fromState = !hasBlur ? getFromStateStyle(el, ['opacity'], stateName, {\n        opacity: 1\n      }) : null;\n      state = state || {};\n      var blurStyle = state.style || {};\n\n      if (blurStyle.opacity == null) {\n        // clone state\n        state = extend({}, state);\n        blurStyle = extend({\n          // Already being applied 'emphasis'. DON'T mul opacity multiple times.\n          opacity: hasBlur ? currentOpacity : fromState.opacity * 0.1\n        }, blurStyle);\n        state.style = blurStyle;\n      }\n\n      return state;\n    }\n\n    function elementStateProxy(stateName, targetStates) {\n      var state = this.states[stateName];\n\n      if (this.style) {\n        if (stateName === 'emphasis') {\n          return createEmphasisDefaultState(this, stateName, targetStates, state);\n        } else if (stateName === 'blur') {\n          return createBlurDefaultState(this, stateName, state);\n        } else if (stateName === 'select') {\n          return createSelectDefaultState(this, stateName, state);\n        }\n      }\n\n      return state;\n    }\n    /**\n     * Set hover style (namely \"emphasis style\") of element.\n     * @param el Should not be `zrender/graphic/Group`.\n     * @param focus 'self' | 'selfInSeries' | 'series'\n     */\n\n\n    function setDefaultStateProxy(el) {\n      el.stateProxy = elementStateProxy;\n      var textContent = el.getTextContent();\n      var textGuide = el.getTextGuideLine();\n\n      if (textContent) {\n        textContent.stateProxy = elementStateProxy;\n      }\n\n      if (textGuide) {\n        textGuide.stateProxy = elementStateProxy;\n      }\n    }\n    function enterEmphasisWhenMouseOver(el, e) {\n      !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n      && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis);\n    }\n    function leaveEmphasisWhenMouseOut(el, e) {\n      !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n      && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis);\n    }\n    function enterEmphasis(el, highlightDigit) {\n      el.__highByOuter |= 1 << (highlightDigit || 0);\n      traverseUpdateState(el, singleEnterEmphasis);\n    }\n    function leaveEmphasis(el, highlightDigit) {\n      !(el.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdateState(el, singleLeaveEmphasis);\n    }\n    function enterBlur(el) {\n      traverseUpdateState(el, singleEnterBlur);\n    }\n    function leaveBlur(el) {\n      traverseUpdateState(el, singleLeaveBlur);\n    }\n    function enterSelect(el) {\n      traverseUpdateState(el, singleEnterSelect);\n    }\n    function leaveSelect(el) {\n      traverseUpdateState(el, singleLeaveSelect);\n    }\n\n    function shouldSilent(el, e) {\n      return el.__highDownSilentOnTouch && e.zrByTouch;\n    }\n\n    function allLeaveBlur(api) {\n      var model = api.getModel();\n      var leaveBlurredSeries = [];\n      var allComponentViews = [];\n      model.eachComponent(function (componentType, componentModel) {\n        var componentStates = getComponentStates(componentModel);\n        var isSeries = componentType === 'series';\n        var view = isSeries ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel);\n        !isSeries && allComponentViews.push(view);\n\n        if (componentStates.isBlured) {\n          // Leave blur anyway\n          view.group.traverse(function (child) {\n            singleLeaveBlur(child);\n          });\n          isSeries && leaveBlurredSeries.push(componentModel);\n        }\n\n        componentStates.isBlured = false;\n      });\n      each(allComponentViews, function (view) {\n        if (view && view.toggleBlurSeries) {\n          view.toggleBlurSeries(leaveBlurredSeries, false, model);\n        }\n      });\n    }\n    function blurSeries(targetSeriesIndex, focus, blurScope, api) {\n      var ecModel = api.getModel();\n      blurScope = blurScope || 'coordinateSystem';\n\n      function leaveBlurOfIndices(data, dataIndices) {\n        for (var i = 0; i < dataIndices.length; i++) {\n          var itemEl = data.getItemGraphicEl(dataIndices[i]);\n          itemEl && leaveBlur(itemEl);\n        }\n      }\n\n      if (targetSeriesIndex == null) {\n        return;\n      }\n\n      if (!focus || focus === 'none') {\n        return;\n      }\n\n      var targetSeriesModel = ecModel.getSeriesByIndex(targetSeriesIndex);\n      var targetCoordSys = targetSeriesModel.coordinateSystem;\n\n      if (targetCoordSys && targetCoordSys.master) {\n        targetCoordSys = targetCoordSys.master;\n      }\n\n      var blurredSeries = [];\n      ecModel.eachSeries(function (seriesModel) {\n        var sameSeries = targetSeriesModel === seriesModel;\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys && coordSys.master) {\n          coordSys = coordSys.master;\n        }\n\n        var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries; // If there is no coordinate system. use sameSeries instead.\n\n        if (!( // Not blur other series if blurScope series\n        blurScope === 'series' && !sameSeries // Not blur other coordinate system if blurScope is coordinateSystem\n        || blurScope === 'coordinateSystem' && !sameCoordSys // Not blur self series if focus is series.\n        || focus === 'series' && sameSeries // TODO blurScope: coordinate system\n        )) {\n          var view = api.getViewOfSeriesModel(seriesModel);\n          view.group.traverse(function (child) {\n            singleEnterBlur(child);\n          });\n\n          if (isArrayLike(focus)) {\n            leaveBlurOfIndices(seriesModel.getData(), focus);\n          } else if (isObject(focus)) {\n            var dataTypes = keys(focus);\n\n            for (var d = 0; d < dataTypes.length; d++) {\n              leaveBlurOfIndices(seriesModel.getData(dataTypes[d]), focus[dataTypes[d]]);\n            }\n          }\n\n          blurredSeries.push(seriesModel);\n          getComponentStates(seriesModel).isBlured = true;\n        }\n      });\n      ecModel.eachComponent(function (componentType, componentModel) {\n        if (componentType === 'series') {\n          return;\n        }\n\n        var view = api.getViewOfComponentModel(componentModel);\n\n        if (view && view.toggleBlurSeries) {\n          view.toggleBlurSeries(blurredSeries, true, ecModel);\n        }\n      });\n    }\n    function blurComponent(componentMainType, componentIndex, api) {\n      if (componentMainType == null || componentIndex == null) {\n        return;\n      }\n\n      var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n      if (!componentModel) {\n        return;\n      }\n\n      getComponentStates(componentModel).isBlured = true;\n      var view = api.getViewOfComponentModel(componentModel);\n\n      if (!view || !view.focusBlurEnabled) {\n        return;\n      }\n\n      view.group.traverse(function (child) {\n        singleEnterBlur(child);\n      });\n    }\n    function blurSeriesFromHighlightPayload(seriesModel, payload, api) {\n      var seriesIndex = seriesModel.seriesIndex;\n      var data = seriesModel.getData(payload.dataType);\n\n      if (!data) {\n        if (\"development\" !== 'production') {\n          error(\"Unknown dataType \" + payload.dataType);\n        }\n\n        return;\n      }\n\n      var dataIndex = queryDataIndex(data, payload); // Pick the first one if there is multiple/none exists.\n\n      dataIndex = (isArray(dataIndex) ? dataIndex[0] : dataIndex) || 0;\n      var el = data.getItemGraphicEl(dataIndex);\n\n      if (!el) {\n        var count = data.count();\n        var current = 0; // If data on dataIndex is NaN.\n\n        while (!el && current < count) {\n          el = data.getItemGraphicEl(current++);\n        }\n      }\n\n      if (el) {\n        var ecData = getECData(el);\n        blurSeries(seriesIndex, ecData.focus, ecData.blurScope, api);\n      } else {\n        // If there is no element put on the data. Try getting it from raw option\n        // TODO Should put it on seriesModel?\n        var focus_1 = seriesModel.get(['emphasis', 'focus']);\n        var blurScope = seriesModel.get(['emphasis', 'blurScope']);\n\n        if (focus_1 != null) {\n          blurSeries(seriesIndex, focus_1, blurScope, api);\n        }\n      }\n    }\n    function findComponentHighDownDispatchers(componentMainType, componentIndex, name, api) {\n      var ret = {\n        focusSelf: false,\n        dispatchers: null\n      };\n\n      if (componentMainType == null || componentMainType === 'series' || componentIndex == null || name == null) {\n        return ret;\n      }\n\n      var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n      if (!componentModel) {\n        return ret;\n      }\n\n      var view = api.getViewOfComponentModel(componentModel);\n\n      if (!view || !view.findHighDownDispatchers) {\n        return ret;\n      }\n\n      var dispatchers = view.findHighDownDispatchers(name); // At presnet, the component (like Geo) only blur inside itself.\n      // So we do not use `blurScope` in component.\n\n      var focusSelf;\n\n      for (var i = 0; i < dispatchers.length; i++) {\n        if (\"development\" !== 'production' && !isHighDownDispatcher(dispatchers[i])) {\n          error('param should be highDownDispatcher');\n        }\n\n        if (getECData(dispatchers[i]).focus === 'self') {\n          focusSelf = true;\n          break;\n        }\n      }\n\n      return {\n        focusSelf: focusSelf,\n        dispatchers: dispatchers\n      };\n    }\n    function handleGlobalMouseOverForHighDown(dispatcher, e, api) {\n      if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n        error('param should be highDownDispatcher');\n      }\n\n      var ecData = getECData(dispatcher);\n\n      var _a = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api),\n          dispatchers = _a.dispatchers,\n          focusSelf = _a.focusSelf; // If `findHighDownDispatchers` is supported on the component,\n      // highlight/downplay elements with the same name.\n\n\n      if (dispatchers) {\n        if (focusSelf) {\n          blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n        }\n\n        each(dispatchers, function (dispatcher) {\n          return enterEmphasisWhenMouseOver(dispatcher, e);\n        });\n      } else {\n        // Try blur all in the related series. Then emphasis the hoverred.\n        // TODO. progressive mode.\n        blurSeries(ecData.seriesIndex, ecData.focus, ecData.blurScope, api);\n\n        if (ecData.focus === 'self') {\n          blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n        } // Other than series, component that not support `findHighDownDispatcher` will\n        // also use it. But in this case, highlight/downplay are only supported in\n        // mouse hover but not in dispatchAction.\n\n\n        enterEmphasisWhenMouseOver(dispatcher, e);\n      }\n    }\n    function handleGlobalMouseOutForHighDown(dispatcher, e, api) {\n      if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n        error('param should be highDownDispatcher');\n      }\n\n      allLeaveBlur(api);\n      var ecData = getECData(dispatcher);\n      var dispatchers = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api).dispatchers;\n\n      if (dispatchers) {\n        each(dispatchers, function (dispatcher) {\n          return leaveEmphasisWhenMouseOut(dispatcher, e);\n        });\n      } else {\n        leaveEmphasisWhenMouseOut(dispatcher, e);\n      }\n    }\n    function toggleSelectionFromPayload(seriesModel, payload, api) {\n      if (!isSelectChangePayload(payload)) {\n        return;\n      }\n\n      var dataType = payload.dataType;\n      var data = seriesModel.getData(dataType);\n      var dataIndex = queryDataIndex(data, payload);\n\n      if (!isArray(dataIndex)) {\n        dataIndex = [dataIndex];\n      }\n\n      seriesModel[payload.type === TOGGLE_SELECT_ACTION_TYPE ? 'toggleSelect' : payload.type === SELECT_ACTION_TYPE ? 'select' : 'unselect'](dataIndex, dataType);\n    }\n    function updateSeriesElementSelection(seriesModel) {\n      var allData = seriesModel.getAllData();\n      each(allData, function (_a) {\n        var data = _a.data,\n            type = _a.type;\n        data.eachItemGraphicEl(function (el, idx) {\n          seriesModel.isSelected(idx, type) ? enterSelect(el) : leaveSelect(el);\n        });\n      });\n    }\n    function getAllSelectedIndices(ecModel) {\n      var ret = [];\n      ecModel.eachSeries(function (seriesModel) {\n        var allData = seriesModel.getAllData();\n        each(allData, function (_a) {\n          var data = _a.data,\n              type = _a.type;\n          var dataIndices = seriesModel.getSelectedDataIndices();\n\n          if (dataIndices.length > 0) {\n            var item = {\n              dataIndex: dataIndices,\n              seriesIndex: seriesModel.seriesIndex\n            };\n\n            if (type != null) {\n              item.dataType = type;\n            }\n\n            ret.push(item);\n          }\n        });\n      });\n      return ret;\n    }\n    /**\n     * Enable the function that mouseover will trigger the emphasis state.\n     *\n     * NOTE:\n     * This function should be used on the element with dataIndex, seriesIndex.\n     *\n     */\n\n    function enableHoverEmphasis(el, focus, blurScope) {\n      setAsHighDownDispatcher(el, true);\n      traverseUpdateState(el, setDefaultStateProxy);\n      enableHoverFocus(el, focus, blurScope);\n    }\n    function disableHoverEmphasis(el) {\n      setAsHighDownDispatcher(el, false);\n    }\n    function toggleHoverEmphasis(el, focus, blurScope, isDisabled) {\n      isDisabled ? disableHoverEmphasis(el) : enableHoverEmphasis(el, focus, blurScope);\n    }\n    function enableHoverFocus(el, focus, blurScope) {\n      var ecData = getECData(el);\n\n      if (focus != null) {\n        // TODO dataIndex may be set after this function. This check is not useful.\n        // if (ecData.dataIndex == null) {\n        //     if (__DEV__) {\n        //         console.warn('focus can only been set on element with dataIndex');\n        //     }\n        // }\n        // else {\n        ecData.focus = focus;\n        ecData.blurScope = blurScope; // }\n      } else if (ecData.focus) {\n        ecData.focus = null;\n      }\n    }\n    var OTHER_STATES = ['emphasis', 'blur', 'select'];\n    var defaultStyleGetterMap = {\n      itemStyle: 'getItemStyle',\n      lineStyle: 'getLineStyle',\n      areaStyle: 'getAreaStyle'\n    };\n    /**\n     * Set emphasis/blur/selected states of element.\n     */\n\n    function setStatesStylesFromModel(el, itemModel, styleType, // default itemStyle\n    getter) {\n      styleType = styleType || 'itemStyle';\n\n      for (var i = 0; i < OTHER_STATES.length; i++) {\n        var stateName = OTHER_STATES[i];\n        var model = itemModel.getModel([stateName, styleType]);\n        var state = el.ensureState(stateName); // Let it throw error if getterType is not found.\n\n        state.style = getter ? getter(model) : model[defaultStyleGetterMap[styleType]]();\n      }\n    }\n    /**\n     *\n     * Set element as highlight / downplay dispatcher.\n     * It will be checked when element received mouseover event or from highlight action.\n     * It's in change of all highlight/downplay behavior of it's children.\n     *\n     * @param el\n     * @param el.highDownSilentOnTouch\n     *        In touch device, mouseover event will be trigger on touchstart event\n     *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can\n     *        conveniently use hoverStyle when tap on touch screen without additional\n     *        code for compatibility.\n     *        But if the chart/component has select feature, which usually also use\n     *        hoverStyle, there might be conflict between 'select-highlight' and\n     *        'hover-highlight' especially when roam is enabled (see geo for example).\n     *        In this case, `highDownSilentOnTouch` should be used to disable\n     *        hover-highlight on touch device.\n     * @param asDispatcher If `false`, do not set as \"highDownDispatcher\".\n     */\n\n    function setAsHighDownDispatcher(el, asDispatcher) {\n      var disable = asDispatcher === false;\n      var extendedEl = el; // Make `highDownSilentOnTouch` and `onStateChange` only work after\n      // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.\n\n      if (el.highDownSilentOnTouch) {\n        extendedEl.__highDownSilentOnTouch = el.highDownSilentOnTouch;\n      } // Simple optimize, since this method might be\n      // called for each elements of a group in some cases.\n\n\n      if (!disable || extendedEl.__highDownDispatcher) {\n        // Emphasis, normal can be triggered manually by API or other components like hover link.\n        // el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent);\n        // Also keep previous record.\n        extendedEl.__highByOuter = extendedEl.__highByOuter || 0;\n        extendedEl.__highDownDispatcher = !disable;\n      }\n    }\n    function isHighDownDispatcher(el) {\n      return !!(el && el.__highDownDispatcher);\n    }\n    /**\n     * Enable component highlight/downplay features:\n     * + hover link (within the same name)\n     * + focus blur in component\n     */\n\n    function enableComponentHighDownFeatures(el, componentModel, componentHighDownName) {\n      var ecData = getECData(el);\n      ecData.componentMainType = componentModel.mainType;\n      ecData.componentIndex = componentModel.componentIndex;\n      ecData.componentHighDownName = componentHighDownName;\n    }\n    /**\n     * Support highlight/downplay record on each elements.\n     * For the case: hover highlight/downplay (legend, visualMap, ...) and\n     * user triggered highlight/downplay should not conflict.\n     * Only all of the highlightDigit cleared, return to normal.\n     * @param {string} highlightKey\n     * @return {number} highlightDigit\n     */\n\n    function getHighlightDigit(highlightKey) {\n      var highlightDigit = _highlightKeyMap[highlightKey];\n\n      if (highlightDigit == null && _highlightNextDigit <= 32) {\n        highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++;\n      }\n\n      return highlightDigit;\n    }\n    function isSelectChangePayload(payload) {\n      var payloadType = payload.type;\n      return payloadType === SELECT_ACTION_TYPE || payloadType === UNSELECT_ACTION_TYPE || payloadType === TOGGLE_SELECT_ACTION_TYPE;\n    }\n    function isHighDownPayload(payload) {\n      var payloadType = payload.type;\n      return payloadType === HIGHLIGHT_ACTION_TYPE || payloadType === DOWNPLAY_ACTION_TYPE;\n    }\n    function savePathStates(el) {\n      var store = getSavedStates(el);\n      store.normalFill = el.style.fill;\n      store.normalStroke = el.style.stroke;\n      var selectState = el.states.select || {};\n      store.selectFill = selectState.style && selectState.style.fill || null;\n      store.selectStroke = selectState.style && selectState.style.stroke || null;\n    }\n\n    var CMD$2 = PathProxy.CMD;\n    var points = [[], [], []];\n    var mathSqrt$1 = Math.sqrt;\n    var mathAtan2 = Math.atan2;\n    function transformPath(path, m) {\n        if (!m) {\n            return;\n        }\n        var data = path.data;\n        var len = path.len();\n        var cmd;\n        var nPoint;\n        var i;\n        var j;\n        var k;\n        var p;\n        var M = CMD$2.M;\n        var C = CMD$2.C;\n        var L = CMD$2.L;\n        var R = CMD$2.R;\n        var A = CMD$2.A;\n        var Q = CMD$2.Q;\n        for (i = 0, j = 0; i < len;) {\n            cmd = data[i++];\n            j = i;\n            nPoint = 0;\n            switch (cmd) {\n                case M:\n                    nPoint = 1;\n                    break;\n                case L:\n                    nPoint = 1;\n                    break;\n                case C:\n                    nPoint = 3;\n                    break;\n                case Q:\n                    nPoint = 2;\n                    break;\n                case A:\n                    var x = m[4];\n                    var y = m[5];\n                    var sx = mathSqrt$1(m[0] * m[0] + m[1] * m[1]);\n                    var sy = mathSqrt$1(m[2] * m[2] + m[3] * m[3]);\n                    var angle = mathAtan2(-m[1] / sy, m[0] / sx);\n                    data[i] *= sx;\n                    data[i++] += x;\n                    data[i] *= sy;\n                    data[i++] += y;\n                    data[i++] *= sx;\n                    data[i++] *= sy;\n                    data[i++] += angle;\n                    data[i++] += angle;\n                    i += 2;\n                    j = i;\n                    break;\n                case R:\n                    p[0] = data[i++];\n                    p[1] = data[i++];\n                    applyTransform(p, p, m);\n                    data[j++] = p[0];\n                    data[j++] = p[1];\n                    p[0] += data[i++];\n                    p[1] += data[i++];\n                    applyTransform(p, p, m);\n                    data[j++] = p[0];\n                    data[j++] = p[1];\n            }\n            for (k = 0; k < nPoint; k++) {\n                var p_1 = points[k];\n                p_1[0] = data[i++];\n                p_1[1] = data[i++];\n                applyTransform(p_1, p_1, m);\n                data[j++] = p_1[0];\n                data[j++] = p_1[1];\n            }\n        }\n        path.increaseVersion();\n    }\n\n    var mathSqrt$2 = Math.sqrt;\n    var mathSin$2 = Math.sin;\n    var mathCos$2 = Math.cos;\n    var PI$1 = Math.PI;\n    function vMag(v) {\n        return Math.sqrt(v[0] * v[0] + v[1] * v[1]);\n    }\n    function vRatio(u, v) {\n        return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));\n    }\n    function vAngle(u, v) {\n        return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)\n            * Math.acos(vRatio(u, v));\n    }\n    function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {\n        var psi = psiDeg * (PI$1 / 180.0);\n        var xp = mathCos$2(psi) * (x1 - x2) / 2.0\n            + mathSin$2(psi) * (y1 - y2) / 2.0;\n        var yp = -1 * mathSin$2(psi) * (x1 - x2) / 2.0\n            + mathCos$2(psi) * (y1 - y2) / 2.0;\n        var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);\n        if (lambda > 1) {\n            rx *= mathSqrt$2(lambda);\n            ry *= mathSqrt$2(lambda);\n        }\n        var f = (fa === fs ? -1 : 1)\n            * mathSqrt$2((((rx * rx) * (ry * ry))\n                - ((rx * rx) * (yp * yp))\n                - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)\n                + (ry * ry) * (xp * xp))) || 0;\n        var cxp = f * rx * yp / ry;\n        var cyp = f * -ry * xp / rx;\n        var cx = (x1 + x2) / 2.0\n            + mathCos$2(psi) * cxp\n            - mathSin$2(psi) * cyp;\n        var cy = (y1 + y2) / 2.0\n            + mathSin$2(psi) * cxp\n            + mathCos$2(psi) * cyp;\n        var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);\n        var u = [(xp - cxp) / rx, (yp - cyp) / ry];\n        var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];\n        var dTheta = vAngle(u, v);\n        if (vRatio(u, v) <= -1) {\n            dTheta = PI$1;\n        }\n        if (vRatio(u, v) >= 1) {\n            dTheta = 0;\n        }\n        if (dTheta < 0) {\n            var n = Math.round(dTheta / PI$1 * 1e6) / 1e6;\n            dTheta = PI$1 * 2 + (n % 2) * PI$1;\n        }\n        path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);\n    }\n    var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig;\n    var numberReg = /-?([0-9]*\\.)?[0-9]+([eE]-?[0-9]+)?/g;\n    function createPathProxyFromString(data) {\n        var path = new PathProxy();\n        if (!data) {\n            return path;\n        }\n        var cpx = 0;\n        var cpy = 0;\n        var subpathX = cpx;\n        var subpathY = cpy;\n        var prevCmd;\n        var CMD = PathProxy.CMD;\n        var cmdList = data.match(commandReg);\n        if (!cmdList) {\n            return path;\n        }\n        for (var l = 0; l < cmdList.length; l++) {\n            var cmdText = cmdList[l];\n            var cmdStr = cmdText.charAt(0);\n            var cmd = void 0;\n            var p = cmdText.match(numberReg) || [];\n            var pLen = p.length;\n            for (var i = 0; i < pLen; i++) {\n                p[i] = parseFloat(p[i]);\n            }\n            var off = 0;\n            while (off < pLen) {\n                var ctlPtx = void 0;\n                var ctlPty = void 0;\n                var rx = void 0;\n                var ry = void 0;\n                var psi = void 0;\n                var fa = void 0;\n                var fs = void 0;\n                var x1 = cpx;\n                var y1 = cpy;\n                var len = void 0;\n                var pathData = void 0;\n                switch (cmdStr) {\n                    case 'l':\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'L':\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'm':\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.M;\n                        path.addData(cmd, cpx, cpy);\n                        subpathX = cpx;\n                        subpathY = cpy;\n                        cmdStr = 'l';\n                        break;\n                    case 'M':\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.M;\n                        path.addData(cmd, cpx, cpy);\n                        subpathX = cpx;\n                        subpathY = cpy;\n                        cmdStr = 'L';\n                        break;\n                    case 'h':\n                        cpx += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'H':\n                        cpx = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'v':\n                        cpy += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'V':\n                        cpy = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'C':\n                        cmd = CMD.C;\n                        path.addData(cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]);\n                        cpx = p[off - 2];\n                        cpy = p[off - 1];\n                        break;\n                    case 'c':\n                        cmd = CMD.C;\n                        path.addData(cmd, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy);\n                        cpx += p[off - 2];\n                        cpy += p[off - 1];\n                        break;\n                    case 'S':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.C) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cmd = CMD.C;\n                        x1 = p[off++];\n                        y1 = p[off++];\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                        break;\n                    case 's':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.C) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cmd = CMD.C;\n                        x1 = cpx + p[off++];\n                        y1 = cpy + p[off++];\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                        break;\n                    case 'Q':\n                        x1 = p[off++];\n                        y1 = p[off++];\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, x1, y1, cpx, cpy);\n                        break;\n                    case 'q':\n                        x1 = p[off++] + cpx;\n                        y1 = p[off++] + cpy;\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, x1, y1, cpx, cpy);\n                        break;\n                    case 'T':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.Q) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                        break;\n                    case 't':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.Q) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                        break;\n                    case 'A':\n                        rx = p[off++];\n                        ry = p[off++];\n                        psi = p[off++];\n                        fa = p[off++];\n                        fs = p[off++];\n                        x1 = cpx, y1 = cpy;\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.A;\n                        processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                        break;\n                    case 'a':\n                        rx = p[off++];\n                        ry = p[off++];\n                        psi = p[off++];\n                        fa = p[off++];\n                        fs = p[off++];\n                        x1 = cpx, y1 = cpy;\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.A;\n                        processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                        break;\n                }\n            }\n            if (cmdStr === 'z' || cmdStr === 'Z') {\n                cmd = CMD.Z;\n                path.addData(cmd);\n                cpx = subpathX;\n                cpy = subpathY;\n            }\n            prevCmd = cmd;\n        }\n        path.toStatic();\n        return path;\n    }\n    var SVGPath = (function (_super) {\n        __extends(SVGPath, _super);\n        function SVGPath() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        SVGPath.prototype.applyTransform = function (m) { };\n        return SVGPath;\n    }(Path));\n    function isPathProxy(path) {\n        return path.setData != null;\n    }\n    function createPathOptions(str, opts) {\n        var pathProxy = createPathProxyFromString(str);\n        var innerOpts = extend({}, opts);\n        innerOpts.buildPath = function (path) {\n            if (isPathProxy(path)) {\n                path.setData(pathProxy.data);\n                var ctx = path.getContext();\n                if (ctx) {\n                    path.rebuildPath(ctx, 1);\n                }\n            }\n            else {\n                var ctx = path;\n                pathProxy.rebuildPath(ctx, 1);\n            }\n        };\n        innerOpts.applyTransform = function (m) {\n            transformPath(pathProxy, m);\n            this.dirtyShape();\n        };\n        return innerOpts;\n    }\n    function createFromString(str, opts) {\n        return new SVGPath(createPathOptions(str, opts));\n    }\n    function extendFromString(str, defaultOpts) {\n        var innerOpts = createPathOptions(str, defaultOpts);\n        var Sub = (function (_super) {\n            __extends(Sub, _super);\n            function Sub(opts) {\n                var _this = _super.call(this, opts) || this;\n                _this.applyTransform = innerOpts.applyTransform;\n                _this.buildPath = innerOpts.buildPath;\n                return _this;\n            }\n            return Sub;\n        }(SVGPath));\n        return Sub;\n    }\n    function mergePath(pathEls, opts) {\n        var pathList = [];\n        var len = pathEls.length;\n        for (var i = 0; i < len; i++) {\n            var pathEl = pathEls[i];\n            pathList.push(pathEl.getUpdatedPathProxy(true));\n        }\n        var pathBundle = new Path(opts);\n        pathBundle.createPathProxy();\n        pathBundle.buildPath = function (path) {\n            if (isPathProxy(path)) {\n                path.appendPath(pathList);\n                var ctx = path.getContext();\n                if (ctx) {\n                    path.rebuildPath(ctx, 1);\n                }\n            }\n        };\n        return pathBundle;\n    }\n    function clonePath(sourcePath, opts) {\n        opts = opts || {};\n        var path = new Path();\n        if (sourcePath.shape) {\n            path.setShape(sourcePath.shape);\n        }\n        path.setStyle(sourcePath.style);\n        if (opts.bakeTransform) {\n            transformPath(path.path, sourcePath.getComputedTransform());\n        }\n        else {\n            if (opts.toLocal) {\n                path.setLocalTransform(sourcePath.getComputedTransform());\n            }\n            else {\n                path.copyTransform(sourcePath);\n            }\n        }\n        path.buildPath = sourcePath.buildPath;\n        path.applyTransform = path.applyTransform;\n        path.z = sourcePath.z;\n        path.z2 = sourcePath.z2;\n        path.zlevel = sourcePath.zlevel;\n        return path;\n    }\n\n    var CircleShape = (function () {\n        function CircleShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n        }\n        return CircleShape;\n    }());\n    var Circle = (function (_super) {\n        __extends(Circle, _super);\n        function Circle(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Circle.prototype.getDefaultShape = function () {\n            return new CircleShape();\n        };\n        Circle.prototype.buildPath = function (ctx, shape) {\n            ctx.moveTo(shape.cx + shape.r, shape.cy);\n            ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);\n        };\n        return Circle;\n    }(Path));\n    Circle.prototype.type = 'circle';\n\n    var EllipseShape = (function () {\n        function EllipseShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.rx = 0;\n            this.ry = 0;\n        }\n        return EllipseShape;\n    }());\n    var Ellipse = (function (_super) {\n        __extends(Ellipse, _super);\n        function Ellipse(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Ellipse.prototype.getDefaultShape = function () {\n            return new EllipseShape();\n        };\n        Ellipse.prototype.buildPath = function (ctx, shape) {\n            var k = 0.5522848;\n            var x = shape.cx;\n            var y = shape.cy;\n            var a = shape.rx;\n            var b = shape.ry;\n            var ox = a * k;\n            var oy = b * k;\n            ctx.moveTo(x - a, y);\n            ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);\n            ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);\n            ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);\n            ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);\n            ctx.closePath();\n        };\n        return Ellipse;\n    }(Path));\n    Ellipse.prototype.type = 'ellipse';\n\n    var PI$2 = Math.PI;\n    var PI2$5 = PI$2 * 2;\n    var mathSin$3 = Math.sin;\n    var mathCos$3 = Math.cos;\n    var mathACos = Math.acos;\n    var mathATan2 = Math.atan2;\n    var mathAbs$1 = Math.abs;\n    var mathSqrt$3 = Math.sqrt;\n    var mathMax$3 = Math.max;\n    var mathMin$3 = Math.min;\n    var e = 1e-4;\n    function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n        var dx10 = x1 - x0;\n        var dy10 = y1 - y0;\n        var dx32 = x3 - x2;\n        var dy32 = y3 - y2;\n        var t = dy32 * dx10 - dx32 * dy10;\n        if (t * t < e) {\n            return;\n        }\n        t = (dx32 * (y0 - y2) - dy32 * (x0 - x2)) / t;\n        return [x0 + t * dx10, y0 + t * dy10];\n    }\n    function computeCornerTangents(x0, y0, x1, y1, radius, cr, clockwise) {\n        var x01 = x0 - x1;\n        var y01 = y0 - y1;\n        var lo = (clockwise ? cr : -cr) / mathSqrt$3(x01 * x01 + y01 * y01);\n        var ox = lo * y01;\n        var oy = -lo * x01;\n        var x11 = x0 + ox;\n        var y11 = y0 + oy;\n        var x10 = x1 + ox;\n        var y10 = y1 + oy;\n        var x00 = (x11 + x10) / 2;\n        var y00 = (y11 + y10) / 2;\n        var dx = x10 - x11;\n        var dy = y10 - y11;\n        var d2 = dx * dx + dy * dy;\n        var r = radius - cr;\n        var s = x11 * y10 - x10 * y11;\n        var d = (dy < 0 ? -1 : 1) * mathSqrt$3(mathMax$3(0, r * r * d2 - s * s));\n        var cx0 = (s * dy - dx * d) / d2;\n        var cy0 = (-s * dx - dy * d) / d2;\n        var cx1 = (s * dy + dx * d) / d2;\n        var cy1 = (-s * dx + dy * d) / d2;\n        var dx0 = cx0 - x00;\n        var dy0 = cy0 - y00;\n        var dx1 = cx1 - x00;\n        var dy1 = cy1 - y00;\n        if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) {\n            cx0 = cx1;\n            cy0 = cy1;\n        }\n        return {\n            cx: cx0,\n            cy: cy0,\n            x0: -ox,\n            y0: -oy,\n            x1: cx0 * (radius / r - 1),\n            y1: cy0 * (radius / r - 1)\n        };\n    }\n    function normalizeCornerRadius(cr) {\n        var arr;\n        if (isArray(cr)) {\n            var len = cr.length;\n            if (!len) {\n                return cr;\n            }\n            if (len === 1) {\n                arr = [cr[0], cr[0], 0, 0];\n            }\n            else if (len === 2) {\n                arr = [cr[0], cr[0], cr[1], cr[1]];\n            }\n            else if (len === 3) {\n                arr = cr.concat(cr[2]);\n            }\n            else {\n                arr = cr;\n            }\n        }\n        else {\n            arr = [cr, cr, cr, cr];\n        }\n        return arr;\n    }\n    function buildPath$1(ctx, shape) {\n        var _a;\n        var radius = mathMax$3(shape.r, 0);\n        var innerRadius = mathMax$3(shape.r0 || 0, 0);\n        var hasRadius = radius > 0;\n        var hasInnerRadius = innerRadius > 0;\n        if (!hasRadius && !hasInnerRadius) {\n            return;\n        }\n        if (!hasRadius) {\n            radius = innerRadius;\n            innerRadius = 0;\n        }\n        if (innerRadius > radius) {\n            var tmp = radius;\n            radius = innerRadius;\n            innerRadius = tmp;\n        }\n        var startAngle = shape.startAngle, endAngle = shape.endAngle;\n        if (isNaN(startAngle) || isNaN(endAngle)) {\n            return;\n        }\n        var cx = shape.cx, cy = shape.cy;\n        var clockwise = !!shape.clockwise;\n        var arc = mathAbs$1(endAngle - startAngle);\n        var mod = arc > PI2$5 && arc % PI2$5;\n        mod > e && (arc = mod);\n        if (!(radius > e)) {\n            ctx.moveTo(cx, cy);\n        }\n        else if (arc > PI2$5 - e) {\n            ctx.moveTo(cx + radius * mathCos$3(startAngle), cy + radius * mathSin$3(startAngle));\n            ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n            if (innerRadius > e) {\n                ctx.moveTo(cx + innerRadius * mathCos$3(endAngle), cy + innerRadius * mathSin$3(endAngle));\n                ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n            }\n        }\n        else {\n            var icrStart = void 0;\n            var icrEnd = void 0;\n            var ocrStart = void 0;\n            var ocrEnd = void 0;\n            var ocrs = void 0;\n            var ocre = void 0;\n            var icrs = void 0;\n            var icre = void 0;\n            var ocrMax = void 0;\n            var icrMax = void 0;\n            var limitedOcrMax = void 0;\n            var limitedIcrMax = void 0;\n            var xre = void 0;\n            var yre = void 0;\n            var xirs = void 0;\n            var yirs = void 0;\n            var xrs = radius * mathCos$3(startAngle);\n            var yrs = radius * mathSin$3(startAngle);\n            var xire = innerRadius * mathCos$3(endAngle);\n            var yire = innerRadius * mathSin$3(endAngle);\n            var hasArc = arc > e;\n            if (hasArc) {\n                var cornerRadius = shape.cornerRadius;\n                if (cornerRadius) {\n                    _a = normalizeCornerRadius(cornerRadius), icrStart = _a[0], icrEnd = _a[1], ocrStart = _a[2], ocrEnd = _a[3];\n                }\n                var halfRd = mathAbs$1(radius - innerRadius) / 2;\n                ocrs = mathMin$3(halfRd, ocrStart);\n                ocre = mathMin$3(halfRd, ocrEnd);\n                icrs = mathMin$3(halfRd, icrStart);\n                icre = mathMin$3(halfRd, icrEnd);\n                limitedOcrMax = ocrMax = mathMax$3(ocrs, ocre);\n                limitedIcrMax = icrMax = mathMax$3(icrs, icre);\n                if (ocrMax > e || icrMax > e) {\n                    xre = radius * mathCos$3(endAngle);\n                    yre = radius * mathSin$3(endAngle);\n                    xirs = innerRadius * mathCos$3(startAngle);\n                    yirs = innerRadius * mathSin$3(startAngle);\n                    if (arc < PI$2) {\n                        var it_1 = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire);\n                        if (it_1) {\n                            var x0 = xrs - it_1[0];\n                            var y0 = yrs - it_1[1];\n                            var x1 = xre - it_1[0];\n                            var y1 = yre - it_1[1];\n                            var a = 1 / mathSin$3(mathACos((x0 * x1 + y0 * y1) / (mathSqrt$3(x0 * x0 + y0 * y0) * mathSqrt$3(x1 * x1 + y1 * y1))) / 2);\n                            var b = mathSqrt$3(it_1[0] * it_1[0] + it_1[1] * it_1[1]);\n                            limitedOcrMax = mathMin$3(ocrMax, (radius - b) / (a + 1));\n                            limitedIcrMax = mathMin$3(icrMax, (innerRadius - b) / (a - 1));\n                        }\n                    }\n                }\n            }\n            if (!hasArc) {\n                ctx.moveTo(cx + xrs, cy + yrs);\n            }\n            else if (limitedOcrMax > e) {\n                var crStart = mathMin$3(ocrStart, limitedOcrMax);\n                var crEnd = mathMin$3(ocrEnd, limitedOcrMax);\n                var ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, crStart, clockwise);\n                var ct1 = computeCornerTangents(xre, yre, xire, yire, radius, crEnd, clockwise);\n                ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n                if (limitedOcrMax < ocrMax && crStart === crEnd) {\n                    ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n                else {\n                    crStart > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                    ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise);\n                    crEnd > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n            }\n            else {\n                ctx.moveTo(cx + xrs, cy + yrs);\n                ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n            }\n            if (!(innerRadius > e) || !hasArc) {\n                ctx.lineTo(cx + xire, cy + yire);\n            }\n            else if (limitedIcrMax > e) {\n                var crStart = mathMin$3(icrStart, limitedIcrMax);\n                var crEnd = mathMin$3(icrEnd, limitedIcrMax);\n                var ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -crEnd, clockwise);\n                var ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -crStart, clockwise);\n                ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n                if (limitedIcrMax < icrMax && crStart === crEnd) {\n                    ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n                else {\n                    crEnd > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                    ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise);\n                    crStart > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n            }\n            else {\n                ctx.lineTo(cx + xire, cy + yire);\n                ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n            }\n        }\n        ctx.closePath();\n    }\n\n    var SectorShape = (function () {\n        function SectorShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r0 = 0;\n            this.r = 0;\n            this.startAngle = 0;\n            this.endAngle = Math.PI * 2;\n            this.clockwise = true;\n            this.cornerRadius = 0;\n        }\n        return SectorShape;\n    }());\n    var Sector = (function (_super) {\n        __extends(Sector, _super);\n        function Sector(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Sector.prototype.getDefaultShape = function () {\n            return new SectorShape();\n        };\n        Sector.prototype.buildPath = function (ctx, shape) {\n            buildPath$1(ctx, shape);\n        };\n        Sector.prototype.isZeroArea = function () {\n            return this.shape.startAngle === this.shape.endAngle\n                || this.shape.r === this.shape.r0;\n        };\n        return Sector;\n    }(Path));\n    Sector.prototype.type = 'sector';\n\n    var RingShape = (function () {\n        function RingShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n            this.r0 = 0;\n        }\n        return RingShape;\n    }());\n    var Ring = (function (_super) {\n        __extends(Ring, _super);\n        function Ring(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Ring.prototype.getDefaultShape = function () {\n            return new RingShape();\n        };\n        Ring.prototype.buildPath = function (ctx, shape) {\n            var x = shape.cx;\n            var y = shape.cy;\n            var PI2 = Math.PI * 2;\n            ctx.moveTo(x + shape.r, y);\n            ctx.arc(x, y, shape.r, 0, PI2, false);\n            ctx.moveTo(x + shape.r0, y);\n            ctx.arc(x, y, shape.r0, 0, PI2, true);\n        };\n        return Ring;\n    }(Path));\n    Ring.prototype.type = 'ring';\n\n    function smoothBezier(points, smooth, isLoop, constraint) {\n        var cps = [];\n        var v = [];\n        var v1 = [];\n        var v2 = [];\n        var prevPoint;\n        var nextPoint;\n        var min$1;\n        var max$1;\n        if (constraint) {\n            min$1 = [Infinity, Infinity];\n            max$1 = [-Infinity, -Infinity];\n            for (var i = 0, len = points.length; i < len; i++) {\n                min(min$1, min$1, points[i]);\n                max(max$1, max$1, points[i]);\n            }\n            min(min$1, min$1, constraint[0]);\n            max(max$1, max$1, constraint[1]);\n        }\n        for (var i = 0, len = points.length; i < len; i++) {\n            var point = points[i];\n            if (isLoop) {\n                prevPoint = points[i ? i - 1 : len - 1];\n                nextPoint = points[(i + 1) % len];\n            }\n            else {\n                if (i === 0 || i === len - 1) {\n                    cps.push(clone$1(points[i]));\n                    continue;\n                }\n                else {\n                    prevPoint = points[i - 1];\n                    nextPoint = points[i + 1];\n                }\n            }\n            sub(v, nextPoint, prevPoint);\n            scale(v, v, smooth);\n            var d0 = distance(point, prevPoint);\n            var d1 = distance(point, nextPoint);\n            var sum = d0 + d1;\n            if (sum !== 0) {\n                d0 /= sum;\n                d1 /= sum;\n            }\n            scale(v1, v, -d0);\n            scale(v2, v, d1);\n            var cp0 = add([], point, v1);\n            var cp1 = add([], point, v2);\n            if (constraint) {\n                max(cp0, cp0, min$1);\n                min(cp0, cp0, max$1);\n                max(cp1, cp1, min$1);\n                min(cp1, cp1, max$1);\n            }\n            cps.push(cp0);\n            cps.push(cp1);\n        }\n        if (isLoop) {\n            cps.push(cps.shift());\n        }\n        return cps;\n    }\n\n    function buildPath$2(ctx, shape, closePath) {\n        var smooth = shape.smooth;\n        var points = shape.points;\n        if (points && points.length >= 2) {\n            if (smooth) {\n                var controlPoints = smoothBezier(points, smooth, closePath, shape.smoothConstraint);\n                ctx.moveTo(points[0][0], points[0][1]);\n                var len = points.length;\n                for (var i = 0; i < (closePath ? len : len - 1); i++) {\n                    var cp1 = controlPoints[i * 2];\n                    var cp2 = controlPoints[i * 2 + 1];\n                    var p = points[(i + 1) % len];\n                    ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]);\n                }\n            }\n            else {\n                ctx.moveTo(points[0][0], points[0][1]);\n                for (var i = 1, l = points.length; i < l; i++) {\n                    ctx.lineTo(points[i][0], points[i][1]);\n                }\n            }\n            closePath && ctx.closePath();\n        }\n    }\n\n    var PolygonShape = (function () {\n        function PolygonShape() {\n            this.points = null;\n            this.smooth = 0;\n            this.smoothConstraint = null;\n        }\n        return PolygonShape;\n    }());\n    var Polygon = (function (_super) {\n        __extends(Polygon, _super);\n        function Polygon(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Polygon.prototype.getDefaultShape = function () {\n            return new PolygonShape();\n        };\n        Polygon.prototype.buildPath = function (ctx, shape) {\n            buildPath$2(ctx, shape, true);\n        };\n        return Polygon;\n    }(Path));\n    Polygon.prototype.type = 'polygon';\n\n    var PolylineShape = (function () {\n        function PolylineShape() {\n            this.points = null;\n            this.percent = 1;\n            this.smooth = 0;\n            this.smoothConstraint = null;\n        }\n        return PolylineShape;\n    }());\n    var Polyline = (function (_super) {\n        __extends(Polyline, _super);\n        function Polyline(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Polyline.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Polyline.prototype.getDefaultShape = function () {\n            return new PolylineShape();\n        };\n        Polyline.prototype.buildPath = function (ctx, shape) {\n            buildPath$2(ctx, shape, false);\n        };\n        return Polyline;\n    }(Path));\n    Polyline.prototype.type = 'polyline';\n\n    var subPixelOptimizeOutputShape$1 = {};\n    var LineShape = (function () {\n        function LineShape() {\n            this.x1 = 0;\n            this.y1 = 0;\n            this.x2 = 0;\n            this.y2 = 0;\n            this.percent = 1;\n        }\n        return LineShape;\n    }());\n    var Line = (function (_super) {\n        __extends(Line, _super);\n        function Line(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Line.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Line.prototype.getDefaultShape = function () {\n            return new LineShape();\n        };\n        Line.prototype.buildPath = function (ctx, shape) {\n            var x1;\n            var y1;\n            var x2;\n            var y2;\n            if (this.subPixelOptimize) {\n                var optimizedShape = subPixelOptimizeLine(subPixelOptimizeOutputShape$1, shape, this.style);\n                x1 = optimizedShape.x1;\n                y1 = optimizedShape.y1;\n                x2 = optimizedShape.x2;\n                y2 = optimizedShape.y2;\n            }\n            else {\n                x1 = shape.x1;\n                y1 = shape.y1;\n                x2 = shape.x2;\n                y2 = shape.y2;\n            }\n            var percent = shape.percent;\n            if (percent === 0) {\n                return;\n            }\n            ctx.moveTo(x1, y1);\n            if (percent < 1) {\n                x2 = x1 * (1 - percent) + x2 * percent;\n                y2 = y1 * (1 - percent) + y2 * percent;\n            }\n            ctx.lineTo(x2, y2);\n        };\n        Line.prototype.pointAt = function (p) {\n            var shape = this.shape;\n            return [\n                shape.x1 * (1 - p) + shape.x2 * p,\n                shape.y1 * (1 - p) + shape.y2 * p\n            ];\n        };\n        return Line;\n    }(Path));\n    Line.prototype.type = 'line';\n\n    var out = [];\n    var BezierCurveShape = (function () {\n        function BezierCurveShape() {\n            this.x1 = 0;\n            this.y1 = 0;\n            this.x2 = 0;\n            this.y2 = 0;\n            this.cpx1 = 0;\n            this.cpy1 = 0;\n            this.percent = 1;\n        }\n        return BezierCurveShape;\n    }());\n    function someVectorAt(shape, t, isTangent) {\n        var cpx2 = shape.cpx2;\n        var cpy2 = shape.cpy2;\n        if (cpx2 != null || cpy2 != null) {\n            return [\n                (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),\n                (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)\n            ];\n        }\n        else {\n            return [\n                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),\n                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)\n            ];\n        }\n    }\n    var BezierCurve = (function (_super) {\n        __extends(BezierCurve, _super);\n        function BezierCurve(opts) {\n            return _super.call(this, opts) || this;\n        }\n        BezierCurve.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        BezierCurve.prototype.getDefaultShape = function () {\n            return new BezierCurveShape();\n        };\n        BezierCurve.prototype.buildPath = function (ctx, shape) {\n            var x1 = shape.x1;\n            var y1 = shape.y1;\n            var x2 = shape.x2;\n            var y2 = shape.y2;\n            var cpx1 = shape.cpx1;\n            var cpy1 = shape.cpy1;\n            var cpx2 = shape.cpx2;\n            var cpy2 = shape.cpy2;\n            var percent = shape.percent;\n            if (percent === 0) {\n                return;\n            }\n            ctx.moveTo(x1, y1);\n            if (cpx2 == null || cpy2 == null) {\n                if (percent < 1) {\n                    quadraticSubdivide(x1, cpx1, x2, percent, out);\n                    cpx1 = out[1];\n                    x2 = out[2];\n                    quadraticSubdivide(y1, cpy1, y2, percent, out);\n                    cpy1 = out[1];\n                    y2 = out[2];\n                }\n                ctx.quadraticCurveTo(cpx1, cpy1, x2, y2);\n            }\n            else {\n                if (percent < 1) {\n                    cubicSubdivide(x1, cpx1, cpx2, x2, percent, out);\n                    cpx1 = out[1];\n                    cpx2 = out[2];\n                    x2 = out[3];\n                    cubicSubdivide(y1, cpy1, cpy2, y2, percent, out);\n                    cpy1 = out[1];\n                    cpy2 = out[2];\n                    y2 = out[3];\n                }\n                ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2);\n            }\n        };\n        BezierCurve.prototype.pointAt = function (t) {\n            return someVectorAt(this.shape, t, false);\n        };\n        BezierCurve.prototype.tangentAt = function (t) {\n            var p = someVectorAt(this.shape, t, true);\n            return normalize(p, p);\n        };\n        return BezierCurve;\n    }(Path));\n    BezierCurve.prototype.type = 'bezier-curve';\n\n    var ArcShape = (function () {\n        function ArcShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n            this.startAngle = 0;\n            this.endAngle = Math.PI * 2;\n            this.clockwise = true;\n        }\n        return ArcShape;\n    }());\n    var Arc = (function (_super) {\n        __extends(Arc, _super);\n        function Arc(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Arc.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Arc.prototype.getDefaultShape = function () {\n            return new ArcShape();\n        };\n        Arc.prototype.buildPath = function (ctx, shape) {\n            var x = shape.cx;\n            var y = shape.cy;\n            var r = Math.max(shape.r, 0);\n            var startAngle = shape.startAngle;\n            var endAngle = shape.endAngle;\n            var clockwise = shape.clockwise;\n            var unitX = Math.cos(startAngle);\n            var unitY = Math.sin(startAngle);\n            ctx.moveTo(unitX * r + x, unitY * r + y);\n            ctx.arc(x, y, r, startAngle, endAngle, !clockwise);\n        };\n        return Arc;\n    }(Path));\n    Arc.prototype.type = 'arc';\n\n    var CompoundPath = (function (_super) {\n        __extends(CompoundPath, _super);\n        function CompoundPath() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.type = 'compound';\n            return _this;\n        }\n        CompoundPath.prototype._updatePathDirty = function () {\n            var paths = this.shape.paths;\n            var dirtyPath = this.shapeChanged();\n            for (var i = 0; i < paths.length; i++) {\n                dirtyPath = dirtyPath || paths[i].shapeChanged();\n            }\n            if (dirtyPath) {\n                this.dirtyShape();\n            }\n        };\n        CompoundPath.prototype.beforeBrush = function () {\n            this._updatePathDirty();\n            var paths = this.shape.paths || [];\n            var scale = this.getGlobalScale();\n            for (var i = 0; i < paths.length; i++) {\n                if (!paths[i].path) {\n                    paths[i].createPathProxy();\n                }\n                paths[i].path.setScale(scale[0], scale[1], paths[i].segmentIgnoreThreshold);\n            }\n        };\n        CompoundPath.prototype.buildPath = function (ctx, shape) {\n            var paths = shape.paths || [];\n            for (var i = 0; i < paths.length; i++) {\n                paths[i].buildPath(ctx, paths[i].shape, true);\n            }\n        };\n        CompoundPath.prototype.afterBrush = function () {\n            var paths = this.shape.paths || [];\n            for (var i = 0; i < paths.length; i++) {\n                paths[i].pathUpdated();\n            }\n        };\n        CompoundPath.prototype.getBoundingRect = function () {\n            this._updatePathDirty.call(this);\n            return Path.prototype.getBoundingRect.call(this);\n        };\n        return CompoundPath;\n    }(Path));\n\n    var Gradient = (function () {\n        function Gradient(colorStops) {\n            this.colorStops = colorStops || [];\n        }\n        Gradient.prototype.addColorStop = function (offset, color) {\n            this.colorStops.push({\n                offset: offset,\n                color: color\n            });\n        };\n        return Gradient;\n    }());\n\n    var LinearGradient = (function (_super) {\n        __extends(LinearGradient, _super);\n        function LinearGradient(x, y, x2, y2, colorStops, globalCoord) {\n            var _this = _super.call(this, colorStops) || this;\n            _this.x = x == null ? 0 : x;\n            _this.y = y == null ? 0 : y;\n            _this.x2 = x2 == null ? 1 : x2;\n            _this.y2 = y2 == null ? 0 : y2;\n            _this.type = 'linear';\n            _this.global = globalCoord || false;\n            return _this;\n        }\n        return LinearGradient;\n    }(Gradient));\n\n    var RadialGradient = (function (_super) {\n        __extends(RadialGradient, _super);\n        function RadialGradient(x, y, r, colorStops, globalCoord) {\n            var _this = _super.call(this, colorStops) || this;\n            _this.x = x == null ? 0.5 : x;\n            _this.y = y == null ? 0.5 : y;\n            _this.r = r == null ? 0.5 : r;\n            _this.type = 'radial';\n            _this.global = globalCoord || false;\n            return _this;\n        }\n        return RadialGradient;\n    }(Gradient));\n\n    var extent = [0, 0];\n    var extent2 = [0, 0];\n    var minTv$1 = new Point();\n    var maxTv$1 = new Point();\n    var OrientedBoundingRect = (function () {\n        function OrientedBoundingRect(rect, transform) {\n            this._corners = [];\n            this._axes = [];\n            this._origin = [0, 0];\n            for (var i = 0; i < 4; i++) {\n                this._corners[i] = new Point();\n            }\n            for (var i = 0; i < 2; i++) {\n                this._axes[i] = new Point();\n            }\n            if (rect) {\n                this.fromBoundingRect(rect, transform);\n            }\n        }\n        OrientedBoundingRect.prototype.fromBoundingRect = function (rect, transform) {\n            var corners = this._corners;\n            var axes = this._axes;\n            var x = rect.x;\n            var y = rect.y;\n            var x2 = x + rect.width;\n            var y2 = y + rect.height;\n            corners[0].set(x, y);\n            corners[1].set(x2, y);\n            corners[2].set(x2, y2);\n            corners[3].set(x, y2);\n            if (transform) {\n                for (var i = 0; i < 4; i++) {\n                    corners[i].transform(transform);\n                }\n            }\n            Point.sub(axes[0], corners[1], corners[0]);\n            Point.sub(axes[1], corners[3], corners[0]);\n            axes[0].normalize();\n            axes[1].normalize();\n            for (var i = 0; i < 2; i++) {\n                this._origin[i] = axes[i].dot(corners[0]);\n            }\n        };\n        OrientedBoundingRect.prototype.intersect = function (other, mtv) {\n            var overlapped = true;\n            var noMtv = !mtv;\n            minTv$1.set(Infinity, Infinity);\n            maxTv$1.set(0, 0);\n            if (!this._intersectCheckOneSide(this, other, minTv$1, maxTv$1, noMtv, 1)) {\n                overlapped = false;\n                if (noMtv) {\n                    return overlapped;\n                }\n            }\n            if (!this._intersectCheckOneSide(other, this, minTv$1, maxTv$1, noMtv, -1)) {\n                overlapped = false;\n                if (noMtv) {\n                    return overlapped;\n                }\n            }\n            if (!noMtv) {\n                Point.copy(mtv, overlapped ? minTv$1 : maxTv$1);\n            }\n            return overlapped;\n        };\n        OrientedBoundingRect.prototype._intersectCheckOneSide = function (self, other, minTv, maxTv, noMtv, inverse) {\n            var overlapped = true;\n            for (var i = 0; i < 2; i++) {\n                var axis = this._axes[i];\n                this._getProjMinMaxOnAxis(i, self._corners, extent);\n                this._getProjMinMaxOnAxis(i, other._corners, extent2);\n                if (extent[1] < extent2[0] || extent[0] > extent2[1]) {\n                    overlapped = false;\n                    if (noMtv) {\n                        return overlapped;\n                    }\n                    var dist0 = Math.abs(extent2[0] - extent[1]);\n                    var dist1 = Math.abs(extent[0] - extent2[1]);\n                    if (Math.min(dist0, dist1) > maxTv.len()) {\n                        if (dist0 < dist1) {\n                            Point.scale(maxTv, axis, -dist0 * inverse);\n                        }\n                        else {\n                            Point.scale(maxTv, axis, dist1 * inverse);\n                        }\n                    }\n                }\n                else if (minTv) {\n                    var dist0 = Math.abs(extent2[0] - extent[1]);\n                    var dist1 = Math.abs(extent[0] - extent2[1]);\n                    if (Math.min(dist0, dist1) < minTv.len()) {\n                        if (dist0 < dist1) {\n                            Point.scale(minTv, axis, dist0 * inverse);\n                        }\n                        else {\n                            Point.scale(minTv, axis, -dist1 * inverse);\n                        }\n                    }\n                }\n            }\n            return overlapped;\n        };\n        OrientedBoundingRect.prototype._getProjMinMaxOnAxis = function (dim, corners, out) {\n            var axis = this._axes[dim];\n            var origin = this._origin;\n            var proj = corners[0].dot(axis) + origin[dim];\n            var min = proj;\n            var max = proj;\n            for (var i = 1; i < corners.length; i++) {\n                var proj_1 = corners[i].dot(axis) + origin[dim];\n                min = Math.min(proj_1, min);\n                max = Math.max(proj_1, max);\n            }\n            out[0] = min;\n            out[1] = max;\n        };\n        return OrientedBoundingRect;\n    }());\n\n    var m = [];\n    var IncrementalDisplayable = (function (_super) {\n        __extends(IncrementalDisplayable, _super);\n        function IncrementalDisplayable() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.notClear = true;\n            _this.incremental = true;\n            _this._displayables = [];\n            _this._temporaryDisplayables = [];\n            _this._cursor = 0;\n            return _this;\n        }\n        IncrementalDisplayable.prototype.traverse = function (cb, context) {\n            cb.call(context, this);\n        };\n        IncrementalDisplayable.prototype.useStyle = function () {\n            this.style = {};\n        };\n        IncrementalDisplayable.prototype.getCursor = function () {\n            return this._cursor;\n        };\n        IncrementalDisplayable.prototype.innerAfterBrush = function () {\n            this._cursor = this._displayables.length;\n        };\n        IncrementalDisplayable.prototype.clearDisplaybles = function () {\n            this._displayables = [];\n            this._temporaryDisplayables = [];\n            this._cursor = 0;\n            this.markRedraw();\n            this.notClear = false;\n        };\n        IncrementalDisplayable.prototype.clearTemporalDisplayables = function () {\n            this._temporaryDisplayables = [];\n        };\n        IncrementalDisplayable.prototype.addDisplayable = function (displayable, notPersistent) {\n            if (notPersistent) {\n                this._temporaryDisplayables.push(displayable);\n            }\n            else {\n                this._displayables.push(displayable);\n            }\n            this.markRedraw();\n        };\n        IncrementalDisplayable.prototype.addDisplayables = function (displayables, notPersistent) {\n            notPersistent = notPersistent || false;\n            for (var i = 0; i < displayables.length; i++) {\n                this.addDisplayable(displayables[i], notPersistent);\n            }\n        };\n        IncrementalDisplayable.prototype.getDisplayables = function () {\n            return this._displayables;\n        };\n        IncrementalDisplayable.prototype.getTemporalDisplayables = function () {\n            return this._temporaryDisplayables;\n        };\n        IncrementalDisplayable.prototype.eachPendingDisplayable = function (cb) {\n            for (var i = this._cursor; i < this._displayables.length; i++) {\n                cb && cb(this._displayables[i]);\n            }\n            for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n                cb && cb(this._temporaryDisplayables[i]);\n            }\n        };\n        IncrementalDisplayable.prototype.update = function () {\n            this.updateTransform();\n            for (var i = this._cursor; i < this._displayables.length; i++) {\n                var displayable = this._displayables[i];\n                displayable.parent = this;\n                displayable.update();\n                displayable.parent = null;\n            }\n            for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n                var displayable = this._temporaryDisplayables[i];\n                displayable.parent = this;\n                displayable.update();\n                displayable.parent = null;\n            }\n        };\n        IncrementalDisplayable.prototype.getBoundingRect = function () {\n            if (!this._rect) {\n                var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);\n                for (var i = 0; i < this._displayables.length; i++) {\n                    var displayable = this._displayables[i];\n                    var childRect = displayable.getBoundingRect().clone();\n                    if (displayable.needLocalTransform()) {\n                        childRect.applyTransform(displayable.getLocalTransform(m));\n                    }\n                    rect.union(childRect);\n                }\n                this._rect = rect;\n            }\n            return this._rect;\n        };\n        IncrementalDisplayable.prototype.contain = function (x, y) {\n            var localPos = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            if (rect.contain(localPos[0], localPos[1])) {\n                for (var i = 0; i < this._displayables.length; i++) {\n                    var displayable = this._displayables[i];\n                    if (displayable.contain(x, y)) {\n                        return true;\n                    }\n                }\n            }\n            return false;\n        };\n        return IncrementalDisplayable;\n    }(Displayable));\n\n    var transitionStore = makeInner();\n    /**\n     * Return null if animation is disabled.\n     */\n\n    function getAnimationConfig(animationType, animatableModel, dataIndex, // Extra opts can override the option in animatable model.\n    extraOpts, // TODO It's only for pictorial bar now.\n    extraDelayParams) {\n      var animationPayload; // Check if there is global animation configuration from dataZoom/resize can override the config in option.\n      // If animation is enabled. Will use this animation config in payload.\n      // If animation is disabled. Just ignore it.\n\n      if (animatableModel && animatableModel.ecModel) {\n        var updatePayload = animatableModel.ecModel.getUpdatePayload();\n        animationPayload = updatePayload && updatePayload.animation;\n      }\n\n      var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();\n      var isUpdate = animationType === 'update';\n\n      if (animationEnabled) {\n        var duration = void 0;\n        var easing = void 0;\n        var delay = void 0;\n\n        if (extraOpts) {\n          duration = retrieve2(extraOpts.duration, 200);\n          easing = retrieve2(extraOpts.easing, 'cubicOut');\n          delay = 0;\n        } else {\n          duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration');\n          easing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing');\n          delay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay');\n        } // animation from payload has highest priority.\n\n\n        if (animationPayload) {\n          animationPayload.duration != null && (duration = animationPayload.duration);\n          animationPayload.easing != null && (easing = animationPayload.easing);\n          animationPayload.delay != null && (delay = animationPayload.delay);\n        }\n\n        if (isFunction(delay)) {\n          delay = delay(dataIndex, extraDelayParams);\n        }\n\n        if (isFunction(duration)) {\n          duration = duration(dataIndex);\n        }\n\n        var config = {\n          duration: duration || 0,\n          delay: delay,\n          easing: easing\n        };\n        return config;\n      } else {\n        return null;\n      }\n    }\n\n    function animateOrSetProps(animationType, el, props, animatableModel, dataIndex, cb, during) {\n      var isFrom = false;\n      var removeOpt;\n\n      if (isFunction(dataIndex)) {\n        during = cb;\n        cb = dataIndex;\n        dataIndex = null;\n      } else if (isObject(dataIndex)) {\n        cb = dataIndex.cb;\n        during = dataIndex.during;\n        isFrom = dataIndex.isFrom;\n        removeOpt = dataIndex.removeOpt;\n        dataIndex = dataIndex.dataIndex;\n      }\n\n      var isRemove = animationType === 'leave';\n\n      if (!isRemove) {\n        // Must stop the remove animation.\n        el.stopAnimation('leave');\n      }\n\n      var animationConfig = getAnimationConfig(animationType, animatableModel, dataIndex, isRemove ? removeOpt || {} : null, animatableModel && animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);\n\n      if (animationConfig && animationConfig.duration > 0) {\n        var duration = animationConfig.duration;\n        var animationDelay = animationConfig.delay;\n        var animationEasing = animationConfig.easing;\n        var animateConfig = {\n          duration: duration,\n          delay: animationDelay || 0,\n          easing: animationEasing,\n          done: cb,\n          force: !!cb || !!during,\n          // Set to final state in update/init animation.\n          // So the post processing based on the path shape can be done correctly.\n          setToFinal: !isRemove,\n          scope: animationType,\n          during: during\n        };\n        isFrom ? el.animateFrom(props, animateConfig) : el.animateTo(props, animateConfig);\n      } else {\n        el.stopAnimation(); // If `isFrom`, the props is the \"from\" props.\n\n        !isFrom && el.attr(props); // Call during at least once.\n\n        during && during(1);\n        cb && cb();\n      }\n    }\n    /**\n     * Update graphic element properties with or without animation according to the\n     * configuration in series.\n     *\n     * Caution: this method will stop previous animation.\n     * So do not use this method to one element twice before\n     * animation starts, unless you know what you are doing.\n     * @example\n     *     graphic.updateProps(el, {\n     *         position: [100, 100]\n     *     }, seriesModel, dataIndex, function () { console.log('Animation done!'); });\n     *     // Or\n     *     graphic.updateProps(el, {\n     *         position: [100, 100]\n     *     }, seriesModel, function () { console.log('Animation done!'); });\n     */\n\n\n    function updateProps(el, props, // TODO: TYPE AnimatableModel\n    animatableModel, dataIndex, cb, during) {\n      animateOrSetProps('update', el, props, animatableModel, dataIndex, cb, during);\n    }\n    /**\n     * Init graphic element properties with or without animation according to the\n     * configuration in series.\n     *\n     * Caution: this method will stop previous animation.\n     * So do not use this method to one element twice before\n     * animation starts, unless you know what you are doing.\n     */\n\n    function initProps(el, props, animatableModel, dataIndex, cb, during) {\n      animateOrSetProps('enter', el, props, animatableModel, dataIndex, cb, during);\n    }\n    /**\n     * If element is removed.\n     * It can determine if element is having remove animation.\n     */\n\n    function isElementRemoved(el) {\n      if (!el.__zr) {\n        return true;\n      }\n\n      for (var i = 0; i < el.animators.length; i++) {\n        var animator = el.animators[i];\n\n        if (animator.scope === 'leave') {\n          return true;\n        }\n      }\n\n      return false;\n    }\n    /**\n     * Remove graphic element\n     */\n\n    function removeElement(el, props, animatableModel, dataIndex, cb, during) {\n      // Don't do remove animation twice.\n      if (isElementRemoved(el)) {\n        return;\n      }\n\n      animateOrSetProps('leave', el, props, animatableModel, dataIndex, cb, during);\n    }\n\n    function fadeOutDisplayable(el, animatableModel, dataIndex, done) {\n      el.removeTextContent();\n      el.removeTextGuideLine();\n      removeElement(el, {\n        style: {\n          opacity: 0\n        }\n      }, animatableModel, dataIndex, done);\n    }\n\n    function removeElementWithFadeOut(el, animatableModel, dataIndex) {\n      function doRemove() {\n        el.parent && el.parent.remove(el);\n      } // Hide label and labelLine first\n      // TODO Also use fade out animation?\n\n\n      if (!el.isGroup) {\n        fadeOutDisplayable(el, animatableModel, dataIndex, doRemove);\n      } else {\n        el.traverse(function (disp) {\n          if (!disp.isGroup) {\n            // Can invoke doRemove multiple times.\n            fadeOutDisplayable(disp, animatableModel, dataIndex, doRemove);\n          }\n        });\n      }\n    }\n    /**\n     * Save old style for style transition in universalTransition module.\n     * It's used when element will be reused in each render.\n     * For chart like map, heatmap, which will always create new element.\n     * We don't need to save this because universalTransition can get old style from the old element\n     */\n\n    function saveOldStyle(el) {\n      transitionStore(el).oldStyle = el.style;\n    }\n    function getOldStyle(el) {\n      return transitionStore(el).oldStyle;\n    }\n\n    var mathMax$4 = Math.max;\n    var mathMin$4 = Math.min;\n    var _customShapeMap = {};\n    /**\n     * Extend shape with parameters\n     */\n\n    function extendShape(opts) {\n      return Path.extend(opts);\n    }\n    var extendPathFromString = extendFromString;\n    /**\n     * Extend path\n     */\n\n    function extendPath(pathData, opts) {\n      return extendPathFromString(pathData, opts);\n    }\n    /**\n     * Register a user defined shape.\n     * The shape class can be fetched by `getShapeClass`\n     * This method will overwrite the registered shapes, including\n     * the registered built-in shapes, if using the same `name`.\n     * The shape can be used in `custom series` and\n     * `graphic component` by declaring `{type: name}`.\n     *\n     * @param name\n     * @param ShapeClass Can be generated by `extendShape`.\n     */\n\n    function registerShape(name, ShapeClass) {\n      _customShapeMap[name] = ShapeClass;\n    }\n    /**\n     * Find shape class registered by `registerShape`. Usually used in\n     * fetching user defined shape.\n     *\n     * [Caution]:\n     * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared\n     * to use user registered shapes.\n     * Because the built-in shape (see `getBuiltInShape`) will be registered by\n     * `registerShape` by default. That enables users to get both built-in\n     * shapes as well as the shapes belonging to themsleves. But users can overwrite\n     * the built-in shapes by using names like 'circle', 'rect' via calling\n     * `registerShape`. So the echarts inner featrues should not fetch shapes from here\n     * in case that it is overwritten by users, except that some features, like\n     * `custom series`, `graphic component`, do it deliberately.\n     *\n     * (2) In the features like `custom series`, `graphic component`, the user input\n     * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic\n     * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names\n     * are reserved names, that is, if some user registers a shape named `'image'`,\n     * the shape will not be used. If we intending to add some more reserved names\n     * in feature, that might bring break changes (disable some existing user shape\n     * names). But that case probably rarely happens. So we don't make more mechanism\n     * to resolve this issue here.\n     *\n     * @param name\n     * @return The shape class. If not found, return nothing.\n     */\n\n    function getShapeClass(name) {\n      if (_customShapeMap.hasOwnProperty(name)) {\n        return _customShapeMap[name];\n      }\n    }\n    /**\n     * Create a path element from path data string\n     * @param pathData\n     * @param opts\n     * @param rect\n     * @param layout 'center' or 'cover' default to be cover\n     */\n\n    function makePath(pathData, opts, rect, layout) {\n      var path = createFromString(pathData, opts);\n\n      if (rect) {\n        if (layout === 'center') {\n          rect = centerGraphic(rect, path.getBoundingRect());\n        }\n\n        resizePath(path, rect);\n      }\n\n      return path;\n    }\n    /**\n     * Create a image element from image url\n     * @param imageUrl image url\n     * @param opts options\n     * @param rect constrain rect\n     * @param layout 'center' or 'cover'. Default to be 'cover'\n     */\n\n    function makeImage(imageUrl, rect, layout) {\n      var zrImg = new ZRImage({\n        style: {\n          image: imageUrl,\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height\n        },\n        onload: function (img) {\n          if (layout === 'center') {\n            var boundingRect = {\n              width: img.width,\n              height: img.height\n            };\n            zrImg.setStyle(centerGraphic(rect, boundingRect));\n          }\n        }\n      });\n      return zrImg;\n    }\n    /**\n     * Get position of centered element in bounding box.\n     *\n     * @param  rect         element local bounding box\n     * @param  boundingRect constraint bounding box\n     * @return element position containing x, y, width, and height\n     */\n\n    function centerGraphic(rect, boundingRect) {\n      // Set rect to center, keep width / height ratio.\n      var aspect = boundingRect.width / boundingRect.height;\n      var width = rect.height * aspect;\n      var height;\n\n      if (width <= rect.width) {\n        height = rect.height;\n      } else {\n        width = rect.width;\n        height = width / aspect;\n      }\n\n      var cx = rect.x + rect.width / 2;\n      var cy = rect.y + rect.height / 2;\n      return {\n        x: cx - width / 2,\n        y: cy - height / 2,\n        width: width,\n        height: height\n      };\n    }\n\n    var mergePath$1 = mergePath;\n    /**\n     * Resize a path to fit the rect\n     * @param path\n     * @param rect\n     */\n\n    function resizePath(path, rect) {\n      if (!path.applyTransform) {\n        return;\n      }\n\n      var pathRect = path.getBoundingRect();\n      var m = pathRect.calculateTransform(rect);\n      path.applyTransform(m);\n    }\n    /**\n     * Sub pixel optimize line for canvas\n     */\n\n    function subPixelOptimizeLine$1(shape, lineWidth) {\n      subPixelOptimizeLine(shape, shape, {\n        lineWidth: lineWidth\n      });\n      return shape;\n    }\n    /**\n     * Sub pixel optimize rect for canvas\n     */\n\n    function subPixelOptimizeRect$1(param) {\n      subPixelOptimizeRect(param.shape, param.shape, param.style);\n      return param;\n    }\n    /**\n     * Sub pixel optimize for canvas\n     *\n     * @param position Coordinate, such as x, y\n     * @param lineWidth Should be nonnegative integer.\n     * @param positiveOrNegative Default false (negative).\n     * @return Optimized position.\n     */\n\n    var subPixelOptimize$1 = subPixelOptimize;\n    /**\n     * Get transform matrix of target (param target),\n     * in coordinate of its ancestor (param ancestor)\n     *\n     * @param target\n     * @param [ancestor]\n     */\n\n    function getTransform(target, ancestor) {\n      var mat = identity([]);\n\n      while (target && target !== ancestor) {\n        mul$1(mat, target.getLocalTransform(), mat);\n        target = target.parent;\n      }\n\n      return mat;\n    }\n    /**\n     * Apply transform to an vertex.\n     * @param target [x, y]\n     * @param transform Can be:\n     *      + Transform matrix: like [1, 0, 0, 1, 0, 0]\n     *      + {position, rotation, scale}, the same as `zrender/Transformable`.\n     * @param invert Whether use invert matrix.\n     * @return [x, y]\n     */\n\n    function applyTransform$1(target, transform, invert$1) {\n      if (transform && !isArrayLike(transform)) {\n        transform = Transformable.getLocalTransform(transform);\n      }\n\n      if (invert$1) {\n        transform = invert([], transform);\n      }\n\n      return applyTransform([], target, transform);\n    }\n    /**\n     * @param direction 'left' 'right' 'top' 'bottom'\n     * @param transform Transform matrix: like [1, 0, 0, 1, 0, 0]\n     * @param invert Whether use invert matrix.\n     * @return Transformed direction. 'left' 'right' 'top' 'bottom'\n     */\n\n    function transformDirection(direction, transform, invert) {\n      // Pick a base, ensure that transform result will not be (0, 0).\n      var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]);\n      var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]);\n      var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0];\n      vertex = applyTransform$1(vertex, transform, invert);\n      return Math.abs(vertex[0]) > Math.abs(vertex[1]) ? vertex[0] > 0 ? 'right' : 'left' : vertex[1] > 0 ? 'bottom' : 'top';\n    }\n\n    function isNotGroup(el) {\n      return !el.isGroup;\n    }\n\n    function isPath(el) {\n      return el.shape != null;\n    }\n    /**\n     * Apply group transition animation from g1 to g2.\n     * If no animatableModel, no animation.\n     */\n\n\n    function groupTransition(g1, g2, animatableModel) {\n      if (!g1 || !g2) {\n        return;\n      }\n\n      function getElMap(g) {\n        var elMap = {};\n        g.traverse(function (el) {\n          if (isNotGroup(el) && el.anid) {\n            elMap[el.anid] = el;\n          }\n        });\n        return elMap;\n      }\n\n      function getAnimatableProps(el) {\n        var obj = {\n          x: el.x,\n          y: el.y,\n          rotation: el.rotation\n        };\n\n        if (isPath(el)) {\n          obj.shape = extend({}, el.shape);\n        }\n\n        return obj;\n      }\n\n      var elMap1 = getElMap(g1);\n      g2.traverse(function (el) {\n        if (isNotGroup(el) && el.anid) {\n          var oldEl = elMap1[el.anid];\n\n          if (oldEl) {\n            var newProp = getAnimatableProps(el);\n            el.attr(getAnimatableProps(oldEl));\n            updateProps(el, newProp, animatableModel, getECData(el).dataIndex);\n          }\n        }\n      });\n    }\n    function clipPointsByRect(points, rect) {\n      // FIXME: This way might be incorrect when graphic clipped by a corner\n      // and when element has a border.\n      return map(points, function (point) {\n        var x = point[0];\n        x = mathMax$4(x, rect.x);\n        x = mathMin$4(x, rect.x + rect.width);\n        var y = point[1];\n        y = mathMax$4(y, rect.y);\n        y = mathMin$4(y, rect.y + rect.height);\n        return [x, y];\n      });\n    }\n    /**\n     * Return a new clipped rect. If rect size are negative, return undefined.\n     */\n\n    function clipRectByRect(targetRect, rect) {\n      var x = mathMax$4(targetRect.x, rect.x);\n      var x2 = mathMin$4(targetRect.x + targetRect.width, rect.x + rect.width);\n      var y = mathMax$4(targetRect.y, rect.y);\n      var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border,\n      // should be painted. So return undefined.\n\n      if (x2 >= x && y2 >= y) {\n        return {\n          x: x,\n          y: y,\n          width: x2 - x,\n          height: y2 - y\n        };\n      }\n    }\n    function createIcon(iconStr, // Support 'image://' or 'path://' or direct svg path.\n    opt, rect) {\n      var innerOpts = extend({\n        rectHover: true\n      }, opt);\n      var style = innerOpts.style = {\n        strokeNoScale: true\n      };\n      rect = rect || {\n        x: -1,\n        y: -1,\n        width: 2,\n        height: 2\n      };\n\n      if (iconStr) {\n        return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), defaults(style, rect), new ZRImage(innerOpts)) : makePath(iconStr.replace('path://', ''), innerOpts, rect, 'center');\n      }\n    }\n    /**\n     * Return `true` if the given line (line `a`) and the given polygon\n     * are intersect.\n     * Note that we do not count colinear as intersect here because no\n     * requirement for that. We could do that if required in future.\n     */\n\n    function linePolygonIntersect(a1x, a1y, a2x, a2y, points) {\n      for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) {\n        var p = points[i];\n\n        if (lineLineIntersect(a1x, a1y, a2x, a2y, p[0], p[1], p2[0], p2[1])) {\n          return true;\n        }\n\n        p2 = p;\n      }\n    }\n    /**\n     * Return `true` if the given two lines (line `a` and line `b`)\n     * are intersect.\n     * Note that we do not count colinear as intersect here because no\n     * requirement for that. We could do that if required in future.\n     */\n\n    function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {\n      // let `vec_m` to be `vec_a2 - vec_a1` and `vec_n` to be `vec_b2 - vec_b1`.\n      var mx = a2x - a1x;\n      var my = a2y - a1y;\n      var nx = b2x - b1x;\n      var ny = b2y - b1y; // `vec_m` and `vec_n` are parallel iff\n      //     existing `k` such that `vec_m = k · vec_n`, equivalent to `vec_m X vec_n = 0`.\n\n      var nmCrossProduct = crossProduct2d(nx, ny, mx, my);\n\n      if (nearZero(nmCrossProduct)) {\n        return false;\n      } // `vec_m` and `vec_n` are intersect iff\n      //     existing `p` and `q` in [0, 1] such that `vec_a1 + p * vec_m = vec_b1 + q * vec_n`,\n      //     such that `q = ((vec_a1 - vec_b1) X vec_m) / (vec_n X vec_m)`\n      //           and `p = ((vec_a1 - vec_b1) X vec_n) / (vec_n X vec_m)`.\n\n\n      var b1a1x = a1x - b1x;\n      var b1a1y = a1y - b1y;\n      var q = crossProduct2d(b1a1x, b1a1y, mx, my) / nmCrossProduct;\n\n      if (q < 0 || q > 1) {\n        return false;\n      }\n\n      var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct;\n\n      if (p < 0 || p > 1) {\n        return false;\n      }\n\n      return true;\n    }\n    /**\n     * Cross product of 2-dimension vector.\n     */\n\n    function crossProduct2d(x1, y1, x2, y2) {\n      return x1 * y2 - x2 * y1;\n    }\n\n    function nearZero(val) {\n      return val <= 1e-6 && val >= -1e-6;\n    }\n\n    function setTooltipConfig(opt) {\n      var itemTooltipOption = opt.itemTooltipOption;\n      var componentModel = opt.componentModel;\n      var itemName = opt.itemName;\n      var itemTooltipOptionObj = isString(itemTooltipOption) ? {\n        formatter: itemTooltipOption\n      } : itemTooltipOption;\n      var mainType = componentModel.mainType;\n      var componentIndex = componentModel.componentIndex;\n      var formatterParams = {\n        componentType: mainType,\n        name: itemName,\n        $vars: ['name']\n      };\n      formatterParams[mainType + 'Index'] = componentIndex;\n      var formatterParamsExtra = opt.formatterParamsExtra;\n\n      if (formatterParamsExtra) {\n        each(keys(formatterParamsExtra), function (key) {\n          if (!hasOwn(formatterParams, key)) {\n            formatterParams[key] = formatterParamsExtra[key];\n            formatterParams.$vars.push(key);\n          }\n        });\n      }\n\n      var ecData = getECData(opt.el);\n      ecData.componentMainType = mainType;\n      ecData.componentIndex = componentIndex;\n      ecData.tooltipConfig = {\n        name: itemName,\n        option: defaults({\n          content: itemName,\n          formatterParams: formatterParams\n        }, itemTooltipOptionObj)\n      };\n    }\n\n    function traverseElement(el, cb) {\n      var stopped; // TODO\n      // Polyfill for fixing zrender group traverse don't visit it's root issue.\n\n      if (el.isGroup) {\n        stopped = cb(el);\n      }\n\n      if (!stopped) {\n        el.traverse(cb);\n      }\n    }\n\n    function traverseElements(els, cb) {\n      if (els) {\n        if (isArray(els)) {\n          for (var i = 0; i < els.length; i++) {\n            traverseElement(els[i], cb);\n          }\n        } else {\n          traverseElement(els, cb);\n        }\n      }\n    } // Register built-in shapes. These shapes might be overwritten\n    // by users, although we do not recommend that.\n\n    registerShape('circle', Circle);\n    registerShape('ellipse', Ellipse);\n    registerShape('sector', Sector);\n    registerShape('ring', Ring);\n    registerShape('polygon', Polygon);\n    registerShape('polyline', Polyline);\n    registerShape('rect', Rect);\n    registerShape('line', Line);\n    registerShape('bezierCurve', BezierCurve);\n    registerShape('arc', Arc);\n\n    var graphic = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        updateProps: updateProps,\n        initProps: initProps,\n        removeElement: removeElement,\n        removeElementWithFadeOut: removeElementWithFadeOut,\n        isElementRemoved: isElementRemoved,\n        extendShape: extendShape,\n        extendPath: extendPath,\n        registerShape: registerShape,\n        getShapeClass: getShapeClass,\n        makePath: makePath,\n        makeImage: makeImage,\n        mergePath: mergePath$1,\n        resizePath: resizePath,\n        subPixelOptimizeLine: subPixelOptimizeLine$1,\n        subPixelOptimizeRect: subPixelOptimizeRect$1,\n        subPixelOptimize: subPixelOptimize$1,\n        getTransform: getTransform,\n        applyTransform: applyTransform$1,\n        transformDirection: transformDirection,\n        groupTransition: groupTransition,\n        clipPointsByRect: clipPointsByRect,\n        clipRectByRect: clipRectByRect,\n        createIcon: createIcon,\n        linePolygonIntersect: linePolygonIntersect,\n        lineLineIntersect: lineLineIntersect,\n        setTooltipConfig: setTooltipConfig,\n        traverseElements: traverseElements,\n        Group: Group,\n        Image: ZRImage,\n        Text: ZRText,\n        Circle: Circle,\n        Ellipse: Ellipse,\n        Sector: Sector,\n        Ring: Ring,\n        Polygon: Polygon,\n        Polyline: Polyline,\n        Rect: Rect,\n        Line: Line,\n        BezierCurve: BezierCurve,\n        Arc: Arc,\n        IncrementalDisplayable: IncrementalDisplayable,\n        CompoundPath: CompoundPath,\n        LinearGradient: LinearGradient,\n        RadialGradient: RadialGradient,\n        BoundingRect: BoundingRect,\n        OrientedBoundingRect: OrientedBoundingRect,\n        Point: Point,\n        Path: Path\n    });\n\n    var EMPTY_OBJ = {};\n    function setLabelText(label, labelTexts) {\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        var text = labelTexts[stateName];\n        var state = label.ensureState(stateName);\n        state.style = state.style || {};\n        state.style.text = text;\n      }\n\n      var oldStates = label.currentStates.slice();\n      label.clearStates(true);\n      label.setStyle({\n        text: labelTexts.normal\n      });\n      label.useStates(oldStates, true);\n    }\n\n    function getLabelText(opt, stateModels, interpolatedValue) {\n      var labelFetcher = opt.labelFetcher;\n      var labelDataIndex = opt.labelDataIndex;\n      var labelDimIndex = opt.labelDimIndex;\n      var normalModel = stateModels.normal;\n      var baseText;\n\n      if (labelFetcher) {\n        baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, normalModel && normalModel.get('formatter'), interpolatedValue != null ? {\n          interpolatedValue: interpolatedValue\n        } : null);\n      }\n\n      if (baseText == null) {\n        baseText = isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt, interpolatedValue) : opt.defaultText;\n      }\n\n      var statesText = {\n        normal: baseText\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        var stateModel = stateModels[stateName];\n        statesText[stateName] = retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, stateName, null, labelDimIndex, stateModel && stateModel.get('formatter')) : null, baseText);\n      }\n\n      return statesText;\n    }\n\n    function setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified // TODO specified position?\n    ) {\n      opt = opt || EMPTY_OBJ;\n      var isSetOnText = targetEl instanceof ZRText;\n      var needsCreateText = false;\n\n      for (var i = 0; i < DISPLAY_STATES.length; i++) {\n        var stateModel = labelStatesModels[DISPLAY_STATES[i]];\n\n        if (stateModel && stateModel.getShallow('show')) {\n          needsCreateText = true;\n          break;\n        }\n      }\n\n      var textContent = isSetOnText ? targetEl : targetEl.getTextContent();\n\n      if (needsCreateText) {\n        if (!isSetOnText) {\n          // Reuse the previous\n          if (!textContent) {\n            textContent = new ZRText();\n            targetEl.setTextContent(textContent);\n          } // Use same state proxy\n\n\n          if (targetEl.stateProxy) {\n            textContent.stateProxy = targetEl.stateProxy;\n          }\n        }\n\n        var labelStatesTexts = getLabelText(opt, labelStatesModels);\n        var normalModel = labelStatesModels.normal;\n        var showNormal = !!normalModel.getShallow('show');\n        var normalStyle = createTextStyle(normalModel, stateSpecified && stateSpecified.normal, opt, false, !isSetOnText);\n        normalStyle.text = labelStatesTexts.normal;\n\n        if (!isSetOnText) {\n          // Always create new\n          targetEl.setTextConfig(createTextConfig(normalModel, opt, false));\n        }\n\n        for (var i = 0; i < SPECIAL_STATES.length; i++) {\n          var stateName = SPECIAL_STATES[i];\n          var stateModel = labelStatesModels[stateName];\n\n          if (stateModel) {\n            var stateObj = textContent.ensureState(stateName);\n            var stateShow = !!retrieve2(stateModel.getShallow('show'), showNormal);\n\n            if (stateShow !== showNormal) {\n              stateObj.ignore = !stateShow;\n            }\n\n            stateObj.style = createTextStyle(stateModel, stateSpecified && stateSpecified[stateName], opt, true, !isSetOnText);\n            stateObj.style.text = labelStatesTexts[stateName];\n\n            if (!isSetOnText) {\n              var targetElEmphasisState = targetEl.ensureState(stateName);\n              targetElEmphasisState.textConfig = createTextConfig(stateModel, opt, true);\n            }\n          }\n        } // PENDING: if there is many requirements that emphasis position\n        // need to be different from normal position, we might consider\n        // auto silent is those cases.\n\n\n        textContent.silent = !!normalModel.getShallow('silent'); // Keep x and y\n\n        if (textContent.style.x != null) {\n          normalStyle.x = textContent.style.x;\n        }\n\n        if (textContent.style.y != null) {\n          normalStyle.y = textContent.style.y;\n        }\n\n        textContent.ignore = !showNormal; // Always create new style.\n\n        textContent.useStyle(normalStyle);\n        textContent.dirty();\n\n        if (opt.enableTextSetter) {\n          labelInner(textContent).setLabelText = function (interpolatedValue) {\n            var labelStatesTexts = getLabelText(opt, labelStatesModels, interpolatedValue);\n            setLabelText(textContent, labelStatesTexts);\n          };\n        }\n      } else if (textContent) {\n        // Not display rich text.\n        textContent.ignore = true;\n      }\n\n      targetEl.dirty();\n    }\n    function getLabelStatesModels(itemModel, labelName) {\n      labelName = labelName || 'label';\n      var statesModels = {\n        normal: itemModel.getModel(labelName)\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        statesModels[stateName] = itemModel.getModel([stateName, labelName]);\n      }\n\n      return statesModels;\n    }\n    /**\n     * Set basic textStyle properties.\n     */\n\n    function createTextStyle(textStyleModel, specifiedTextStyle, // Fixed style in the code. Can't be set by model.\n    opt, isNotNormal, isAttached // If text is attached on an element. If so, auto color will handling in zrender.\n    ) {\n      var textStyle = {};\n      setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached);\n      specifiedTextStyle && extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\n\n      return textStyle;\n    }\n    function createTextConfig(textStyleModel, opt, isNotNormal) {\n      opt = opt || {};\n      var textConfig = {};\n      var labelPosition;\n      var labelRotate = textStyleModel.getShallow('rotate');\n      var labelDistance = retrieve2(textStyleModel.getShallow('distance'), isNotNormal ? null : 5);\n      var labelOffset = textStyleModel.getShallow('offset');\n      labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used\n      // in bar series, and magric type should be considered.\n\n      labelPosition === 'outside' && (labelPosition = opt.defaultOutsidePosition || 'top');\n\n      if (labelPosition != null) {\n        textConfig.position = labelPosition;\n      }\n\n      if (labelOffset != null) {\n        textConfig.offset = labelOffset;\n      }\n\n      if (labelRotate != null) {\n        labelRotate *= Math.PI / 180;\n        textConfig.rotation = labelRotate;\n      }\n\n      if (labelDistance != null) {\n        textConfig.distance = labelDistance;\n      } // fill and auto is determined by the color of path fill if it's not specified by developers.\n\n\n      textConfig.outsideFill = textStyleModel.get('color') === 'inherit' ? opt.inheritColor || null : 'auto';\n      return textConfig;\n    }\n    /**\n     * The uniform entry of set text style, that is, retrieve style definitions\n     * from `model` and set to `textStyle` object.\n     *\n     * Never in merge mode, but in overwrite mode, that is, all of the text style\n     * properties will be set. (Consider the states of normal and emphasis and\n     * default value can be adopted, merge would make the logic too complicated\n     * to manage.)\n     */\n\n    function setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached) {\n      // Consider there will be abnormal when merge hover style to normal style if given default value.\n      opt = opt || EMPTY_OBJ;\n      var ecModel = textStyleModel.ecModel;\n      var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:\n      // {\n      //     data: [{\n      //         value: 12,\n      //         label: {\n      //             rich: {\n      //                 // no 'a' here but using parent 'a'.\n      //             }\n      //         }\n      //     }],\n      //     rich: {\n      //         a: { ... }\n      //     }\n      // }\n\n      var richItemNames = getRichItemNames(textStyleModel);\n      var richResult;\n\n      if (richItemNames) {\n        richResult = {};\n\n        for (var name_1 in richItemNames) {\n          if (richItemNames.hasOwnProperty(name_1)) {\n            // Cascade is supported in rich.\n            var richTextStyle = textStyleModel.getModel(['rich', name_1]); // In rich, never `disableBox`.\n            // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,\n            // the default color `'blue'` will not be adopted if no color declared in `rich`.\n            // That might confuses users. So probably we should put `textStyleModel` as the\n            // root ancestor of the `richTextStyle`. But that would be a break change.\n\n            setTokenTextStyle(richResult[name_1] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true);\n          }\n        }\n      }\n\n      if (richResult) {\n        textStyle.rich = richResult;\n      }\n\n      var overflow = textStyleModel.get('overflow');\n\n      if (overflow) {\n        textStyle.overflow = overflow;\n      }\n\n      var margin = textStyleModel.get('minMargin');\n\n      if (margin != null) {\n        textStyle.margin = margin;\n      }\n\n      setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false);\n    } // Consider case:\n    // {\n    //     data: [{\n    //         value: 12,\n    //         label: {\n    //             rich: {\n    //                 // no 'a' here but using parent 'a'.\n    //             }\n    //         }\n    //     }],\n    //     rich: {\n    //         a: { ... }\n    //     }\n    // }\n    // TODO TextStyleModel\n\n\n    function getRichItemNames(textStyleModel) {\n      // Use object to remove duplicated names.\n      var richItemNameMap;\n\n      while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {\n        var rich = (textStyleModel.option || EMPTY_OBJ).rich;\n\n        if (rich) {\n          richItemNameMap = richItemNameMap || {};\n          var richKeys = keys(rich);\n\n          for (var i = 0; i < richKeys.length; i++) {\n            var richKey = richKeys[i];\n            richItemNameMap[richKey] = 1;\n          }\n        }\n\n        textStyleModel = textStyleModel.parentModel;\n      }\n\n      return richItemNameMap;\n    }\n\n    var TEXT_PROPS_WITH_GLOBAL = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'];\n    var TEXT_PROPS_SELF = ['align', 'lineHeight', 'width', 'height', 'tag', 'verticalAlign'];\n    var TEXT_PROPS_BOX = ['padding', 'borderWidth', 'borderRadius', 'borderDashOffset', 'backgroundColor', 'borderColor', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\n\n    function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, isBlock, inRich) {\n      // In merge mode, default value should not be given.\n      globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ;\n      var inheritColor = opt && opt.inheritColor;\n      var fillColor = textStyleModel.getShallow('color');\n      var strokeColor = textStyleModel.getShallow('textBorderColor');\n      var opacity = retrieve2(textStyleModel.getShallow('opacity'), globalTextStyle.opacity);\n\n      if (fillColor === 'inherit' || fillColor === 'auto') {\n        if (\"development\" !== 'production') {\n          if (fillColor === 'auto') {\n            deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n          }\n        }\n\n        if (inheritColor) {\n          fillColor = inheritColor;\n        } else {\n          fillColor = null;\n        }\n      }\n\n      if (strokeColor === 'inherit' || strokeColor === 'auto') {\n        if (\"development\" !== 'production') {\n          if (strokeColor === 'auto') {\n            deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n          }\n        }\n\n        if (inheritColor) {\n          strokeColor = inheritColor;\n        } else {\n          strokeColor = null;\n        }\n      }\n\n      if (!isAttached) {\n        // Only use default global textStyle.color if text is individual.\n        // Otherwise it will use the strategy of attached text color because text may be on a path.\n        fillColor = fillColor || globalTextStyle.color;\n        strokeColor = strokeColor || globalTextStyle.textBorderColor;\n      }\n\n      if (fillColor != null) {\n        textStyle.fill = fillColor;\n      }\n\n      if (strokeColor != null) {\n        textStyle.stroke = strokeColor;\n      }\n\n      var textBorderWidth = retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth);\n\n      if (textBorderWidth != null) {\n        textStyle.lineWidth = textBorderWidth;\n      }\n\n      var textBorderType = retrieve2(textStyleModel.getShallow('textBorderType'), globalTextStyle.textBorderType);\n\n      if (textBorderType != null) {\n        textStyle.lineDash = textBorderType;\n      }\n\n      var textBorderDashOffset = retrieve2(textStyleModel.getShallow('textBorderDashOffset'), globalTextStyle.textBorderDashOffset);\n\n      if (textBorderDashOffset != null) {\n        textStyle.lineDashOffset = textBorderDashOffset;\n      }\n\n      if (!isNotNormal && opacity == null && !inRich) {\n        opacity = opt && opt.defaultOpacity;\n      }\n\n      if (opacity != null) {\n        textStyle.opacity = opacity;\n      } // TODO\n\n\n      if (!isNotNormal && !isAttached) {\n        // Set default finally.\n        if (textStyle.fill == null && opt.inheritColor) {\n          textStyle.fill = opt.inheritColor;\n        }\n      } // Do not use `getFont` here, because merge should be supported, where\n      // part of these properties may be changed in emphasis style, and the\n      // others should remain their original value got from normal style.\n\n\n      for (var i = 0; i < TEXT_PROPS_WITH_GLOBAL.length; i++) {\n        var key = TEXT_PROPS_WITH_GLOBAL[i];\n        var val = retrieve2(textStyleModel.getShallow(key), globalTextStyle[key]);\n\n        if (val != null) {\n          textStyle[key] = val;\n        }\n      }\n\n      for (var i = 0; i < TEXT_PROPS_SELF.length; i++) {\n        var key = TEXT_PROPS_SELF[i];\n        var val = textStyleModel.getShallow(key);\n\n        if (val != null) {\n          textStyle[key] = val;\n        }\n      }\n\n      if (textStyle.verticalAlign == null) {\n        var baseline = textStyleModel.getShallow('baseline');\n\n        if (baseline != null) {\n          textStyle.verticalAlign = baseline;\n        }\n      }\n\n      if (!isBlock || !opt.disableBox) {\n        for (var i = 0; i < TEXT_PROPS_BOX.length; i++) {\n          var key = TEXT_PROPS_BOX[i];\n          var val = textStyleModel.getShallow(key);\n\n          if (val != null) {\n            textStyle[key] = val;\n          }\n        }\n\n        var borderType = textStyleModel.getShallow('borderType');\n\n        if (borderType != null) {\n          textStyle.borderDash = borderType;\n        }\n\n        if ((textStyle.backgroundColor === 'auto' || textStyle.backgroundColor === 'inherit') && inheritColor) {\n          if (\"development\" !== 'production') {\n            if (textStyle.backgroundColor === 'auto') {\n              deprecateReplaceLog('backgroundColor: \\'auto\\'', 'backgroundColor: \\'inherit\\'');\n            }\n          }\n\n          textStyle.backgroundColor = inheritColor;\n        }\n\n        if ((textStyle.borderColor === 'auto' || textStyle.borderColor === 'inherit') && inheritColor) {\n          if (\"development\" !== 'production') {\n            if (textStyle.borderColor === 'auto') {\n              deprecateReplaceLog('borderColor: \\'auto\\'', 'borderColor: \\'inherit\\'');\n            }\n          }\n\n          textStyle.borderColor = inheritColor;\n        }\n      }\n    }\n\n    function getFont(opt, ecModel) {\n      var gTextStyleModel = ecModel && ecModel.getModel('textStyle');\n      return trim([// FIXME in node-canvas fontWeight is before fontStyle\n      opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));\n    }\n    var labelInner = makeInner();\n    function setLabelValueAnimation(label, labelStatesModels, value, getDefaultText) {\n      if (!label) {\n        return;\n      }\n\n      var obj = labelInner(label);\n      obj.prevValue = obj.value;\n      obj.value = value;\n      var normalLabelModel = labelStatesModels.normal;\n      obj.valueAnimation = normalLabelModel.get('valueAnimation');\n\n      if (obj.valueAnimation) {\n        obj.precision = normalLabelModel.get('precision');\n        obj.defaultInterpolatedText = getDefaultText;\n        obj.statesModels = labelStatesModels;\n      }\n    }\n    function animateLabelValue(textEl, dataIndex, data, animatableModel, labelFetcher) {\n      var labelInnerStore = labelInner(textEl);\n\n      if (!labelInnerStore.valueAnimation || labelInnerStore.prevValue === labelInnerStore.value) {\n        // Value not changed, no new label animation\n        return;\n      }\n\n      var defaultInterpolatedText = labelInnerStore.defaultInterpolatedText; // Consider the case that being animating, do not use the `obj.value`,\n      // Otherwise it will jump to the `obj.value` when this new animation started.\n\n      var currValue = retrieve2(labelInnerStore.interpolatedValue, labelInnerStore.prevValue);\n      var targetValue = labelInnerStore.value;\n\n      function during(percent) {\n        var interpolated = interpolateRawValues(data, labelInnerStore.precision, currValue, targetValue, percent);\n        labelInnerStore.interpolatedValue = percent === 1 ? null : interpolated;\n        var labelText = getLabelText({\n          labelDataIndex: dataIndex,\n          labelFetcher: labelFetcher,\n          defaultText: defaultInterpolatedText ? defaultInterpolatedText(interpolated) : interpolated + ''\n        }, labelInnerStore.statesModels, interpolated);\n        setLabelText(textEl, labelText);\n      }\n\n      textEl.percent = 0;\n      (labelInnerStore.prevValue == null ? initProps : updateProps)(textEl, {\n        // percent is used to prevent animation from being aborted #15916\n        percent: 1\n      }, animatableModel, dataIndex, null, during);\n    }\n\n    var PATH_COLOR = ['textStyle', 'color'];\n    var textStyleParams = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'padding', 'lineHeight', 'rich', 'width', 'height', 'overflow']; // TODO Performance improvement?\n\n    var tmpText = new ZRText();\n\n    var TextStyleMixin =\n    /** @class */\n    function () {\n      function TextStyleMixin() {}\n      /**\n       * Get color property or get color from option.textStyle.color\n       */\n      // TODO Callback\n\n\n      TextStyleMixin.prototype.getTextColor = function (isEmphasis) {\n        var ecModel = this.ecModel;\n        return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null);\n      };\n      /**\n       * Create font string from fontStyle, fontWeight, fontSize, fontFamily\n       * @return {string}\n       */\n\n\n      TextStyleMixin.prototype.getFont = function () {\n        return getFont({\n          fontStyle: this.getShallow('fontStyle'),\n          fontWeight: this.getShallow('fontWeight'),\n          fontSize: this.getShallow('fontSize'),\n          fontFamily: this.getShallow('fontFamily')\n        }, this.ecModel);\n      };\n\n      TextStyleMixin.prototype.getTextRect = function (text) {\n        var style = {\n          text: text,\n          verticalAlign: this.getShallow('verticalAlign') || this.getShallow('baseline')\n        };\n\n        for (var i = 0; i < textStyleParams.length; i++) {\n          style[textStyleParams[i]] = this.getShallow(textStyleParams[i]);\n        }\n\n        tmpText.useStyle(style);\n        tmpText.update();\n        return tmpText.getBoundingRect();\n      };\n\n      return TextStyleMixin;\n    }();\n\n    var LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getLineStyle = makeStyleMapper(LINE_STYLE_KEY_MAP);\n\n    var LineStyleMixin =\n    /** @class */\n    function () {\n      function LineStyleMixin() {}\n\n      LineStyleMixin.prototype.getLineStyle = function (excludes) {\n        return getLineStyle(this, excludes);\n      };\n\n      return LineStyleMixin;\n    }();\n\n    var ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getItemStyle = makeStyleMapper(ITEM_STYLE_KEY_MAP);\n\n    var ItemStyleMixin =\n    /** @class */\n    function () {\n      function ItemStyleMixin() {}\n\n      ItemStyleMixin.prototype.getItemStyle = function (excludes, includes) {\n        return getItemStyle(this, excludes, includes);\n      };\n\n      return ItemStyleMixin;\n    }();\n\n    var Model =\n    /** @class */\n    function () {\n      function Model(option, parentModel, ecModel) {\n        this.parentModel = parentModel;\n        this.ecModel = ecModel;\n        this.option = option; // Simple optimization\n        // if (this.init) {\n        //     if (arguments.length <= 4) {\n        //         this.init(option, parentModel, ecModel, extraOpt);\n        //     }\n        //     else {\n        //         this.init.apply(this, arguments);\n        //     }\n        // }\n      }\n\n      Model.prototype.init = function (option, parentModel, ecModel) {\n        var rest = [];\n\n        for (var _i = 3; _i < arguments.length; _i++) {\n          rest[_i - 3] = arguments[_i];\n        }\n      };\n      /**\n       * Merge the input option to me.\n       */\n\n\n      Model.prototype.mergeOption = function (option, ecModel) {\n        merge(this.option, option, true);\n      }; // `path` can be 'a.b.c', so the return value type have to be `ModelOption`\n      // TODO: TYPE strict key check?\n      // get(path: string | string[], ignoreParent?: boolean): ModelOption;\n\n\n      Model.prototype.get = function (path, ignoreParent) {\n        if (path == null) {\n          return this.option;\n        }\n\n        return this._doGet(this.parsePath(path), !ignoreParent && this.parentModel);\n      };\n\n      Model.prototype.getShallow = function (key, ignoreParent) {\n        var option = this.option;\n        var val = option == null ? option : option[key];\n\n        if (val == null && !ignoreParent) {\n          var parentModel = this.parentModel;\n\n          if (parentModel) {\n            // FIXME:TS do not know how to make it works\n            val = parentModel.getShallow(key);\n          }\n        }\n\n        return val;\n      }; // `path` can be 'a.b.c', so the return value type have to be `Model<ModelOption>`\n      // getModel(path: string | string[], parentModel?: Model): Model;\n      // TODO 'a.b.c' is deprecated\n\n\n      Model.prototype.getModel = function (path, parentModel) {\n        var hasPath = path != null;\n        var pathFinal = hasPath ? this.parsePath(path) : null;\n        var obj = hasPath ? this._doGet(pathFinal) : this.option;\n        parentModel = parentModel || this.parentModel && this.parentModel.getModel(this.resolveParentPath(pathFinal));\n        return new Model(obj, parentModel, this.ecModel);\n      };\n      /**\n       * If model has option\n       */\n\n\n      Model.prototype.isEmpty = function () {\n        return this.option == null;\n      };\n\n      Model.prototype.restoreData = function () {}; // Pending\n\n\n      Model.prototype.clone = function () {\n        var Ctor = this.constructor;\n        return new Ctor(clone(this.option));\n      }; // setReadOnly(properties): void {\n      // clazzUtil.setReadOnly(this, properties);\n      // }\n      // If path is null/undefined, return null/undefined.\n\n\n      Model.prototype.parsePath = function (path) {\n        if (typeof path === 'string') {\n          return path.split('.');\n        }\n\n        return path;\n      }; // Resolve path for parent. Perhaps useful when parent use a different property.\n      // Default to be a identity resolver.\n      // Can be modified to a different resolver.\n\n\n      Model.prototype.resolveParentPath = function (path) {\n        return path;\n      }; // FIXME:TS check whether put this method here\n\n\n      Model.prototype.isAnimationEnabled = function () {\n        if (!env.node && this.option) {\n          if (this.option.animation != null) {\n            return !!this.option.animation;\n          } else if (this.parentModel) {\n            return this.parentModel.isAnimationEnabled();\n          }\n        }\n      };\n\n      Model.prototype._doGet = function (pathArr, parentModel) {\n        var obj = this.option;\n\n        if (!pathArr) {\n          return obj;\n        }\n\n        for (var i = 0; i < pathArr.length; i++) {\n          // Ignore empty\n          if (!pathArr[i]) {\n            continue;\n          } // obj could be number/string/... (like 0)\n\n\n          obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null;\n\n          if (obj == null) {\n            break;\n          }\n        }\n\n        if (obj == null && parentModel) {\n          obj = parentModel._doGet(this.resolveParentPath(pathArr), parentModel.parentModel);\n        }\n\n        return obj;\n      };\n\n      return Model;\n    }();\n\n    enableClassExtend(Model);\n    enableClassCheck(Model);\n    mixin(Model, LineStyleMixin);\n    mixin(Model, ItemStyleMixin);\n    mixin(Model, AreaStyleMixin);\n    mixin(Model, TextStyleMixin);\n\n    var base = Math.round(Math.random() * 10);\n    /**\n     * @public\n     * @param {string} type\n     * @return {string}\n     */\n\n    function getUID(type) {\n      // Considering the case of crossing js context,\n      // use Math.random to make id as unique as possible.\n      return [type || '', base++].join('_');\n    }\n    /**\n     * Implements `SubTypeDefaulterManager` for `target`.\n     */\n\n    function enableSubTypeDefaulter(target) {\n      var subTypeDefaulters = {};\n\n      target.registerSubTypeDefaulter = function (componentType, defaulter) {\n        var componentTypeInfo = parseClassType(componentType);\n        subTypeDefaulters[componentTypeInfo.main] = defaulter;\n      };\n\n      target.determineSubType = function (componentType, option) {\n        var type = option.type;\n\n        if (!type) {\n          var componentTypeMain = parseClassType(componentType).main;\n\n          if (target.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {\n            type = subTypeDefaulters[componentTypeMain](option);\n          }\n        }\n\n        return type;\n      };\n    }\n    /**\n     * Implements `TopologicalTravelable<any>` for `entity`.\n     *\n     * Topological travel on Activity Network (Activity On Vertices).\n     * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].\n     * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.\n     * If there is circular dependencey, Error will be thrown.\n     */\n\n    function enableTopologicalTravel(entity, dependencyGetter) {\n      /**\n       * @param targetNameList Target Component type list.\n       *                       Can be ['aa', 'bb', 'aa.xx']\n       * @param fullNameList By which we can build dependency graph.\n       * @param callback Params: componentType, dependencies.\n       * @param context Scope of callback.\n       */\n      entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {\n        if (!targetNameList.length) {\n          return;\n        }\n\n        var result = makeDepndencyGraph(fullNameList);\n        var graph = result.graph;\n        var noEntryList = result.noEntryList;\n        var targetNameSet = {};\n        each(targetNameList, function (name) {\n          targetNameSet[name] = true;\n        });\n\n        while (noEntryList.length) {\n          var currComponentType = noEntryList.pop();\n          var currVertex = graph[currComponentType];\n          var isInTargetNameSet = !!targetNameSet[currComponentType];\n\n          if (isInTargetNameSet) {\n            callback.call(context, currComponentType, currVertex.originalDeps.slice());\n            delete targetNameSet[currComponentType];\n          }\n\n          each(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge);\n        }\n\n        each(targetNameSet, function () {\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('Circular dependency may exists: ', targetNameSet, targetNameList, fullNameList);\n          }\n\n          throw new Error(errMsg);\n        });\n\n        function removeEdge(succComponentType) {\n          graph[succComponentType].entryCount--;\n\n          if (graph[succComponentType].entryCount === 0) {\n            noEntryList.push(succComponentType);\n          }\n        } // Consider this case: legend depends on series, and we call\n        // chart.setOption({series: [...]}), where only series is in option.\n        // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will\n        // not be called, but only sereis.mergeOption is called. Thus legend\n        // have no chance to update its local record about series (like which\n        // name of series is available in legend).\n\n\n        function removeEdgeAndAdd(succComponentType) {\n          targetNameSet[succComponentType] = true;\n          removeEdge(succComponentType);\n        }\n      };\n\n      function makeDepndencyGraph(fullNameList) {\n        var graph = {};\n        var noEntryList = [];\n        each(fullNameList, function (name) {\n          var thisItem = createDependencyGraphItem(graph, name);\n          var originalDeps = thisItem.originalDeps = dependencyGetter(name);\n          var availableDeps = getAvailableDependencies(originalDeps, fullNameList);\n          thisItem.entryCount = availableDeps.length;\n\n          if (thisItem.entryCount === 0) {\n            noEntryList.push(name);\n          }\n\n          each(availableDeps, function (dependentName) {\n            if (indexOf(thisItem.predecessor, dependentName) < 0) {\n              thisItem.predecessor.push(dependentName);\n            }\n\n            var thatItem = createDependencyGraphItem(graph, dependentName);\n\n            if (indexOf(thatItem.successor, dependentName) < 0) {\n              thatItem.successor.push(name);\n            }\n          });\n        });\n        return {\n          graph: graph,\n          noEntryList: noEntryList\n        };\n      }\n\n      function createDependencyGraphItem(graph, name) {\n        if (!graph[name]) {\n          graph[name] = {\n            predecessor: [],\n            successor: []\n          };\n        }\n\n        return graph[name];\n      }\n\n      function getAvailableDependencies(originalDeps, fullNameList) {\n        var availableDeps = [];\n        each(originalDeps, function (dep) {\n          indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);\n        });\n        return availableDeps;\n      }\n    }\n    function inheritDefaultOption(superOption, subOption) {\n      // See also `model/Component.ts#getDefaultOption`\n      return merge(merge({}, superOption, true), subOption, true);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * Language: English.\n     */\n    var langEN = {\n      time: {\n        month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n        monthAbbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n        dayOfWeek: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],\n        dayOfWeekAbbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']\n      },\n      legend: {\n        selector: {\n          all: 'All',\n          inverse: 'Inv'\n        }\n      },\n      toolbox: {\n        brush: {\n          title: {\n            rect: 'Box Select',\n            polygon: 'Lasso Select',\n            lineX: 'Horizontally Select',\n            lineY: 'Vertically Select',\n            keep: 'Keep Selections',\n            clear: 'Clear Selections'\n          }\n        },\n        dataView: {\n          title: 'Data View',\n          lang: ['Data View', 'Close', 'Refresh']\n        },\n        dataZoom: {\n          title: {\n            zoom: 'Zoom',\n            back: 'Zoom Reset'\n          }\n        },\n        magicType: {\n          title: {\n            line: 'Switch to Line Chart',\n            bar: 'Switch to Bar Chart',\n            stack: 'Stack',\n            tiled: 'Tile'\n          }\n        },\n        restore: {\n          title: 'Restore'\n        },\n        saveAsImage: {\n          title: 'Save as Image',\n          lang: ['Right Click to Save Image']\n        }\n      },\n      series: {\n        typeNames: {\n          pie: 'Pie chart',\n          bar: 'Bar chart',\n          line: 'Line chart',\n          scatter: 'Scatter plot',\n          effectScatter: 'Ripple scatter plot',\n          radar: 'Radar chart',\n          tree: 'Tree',\n          treemap: 'Treemap',\n          boxplot: 'Boxplot',\n          candlestick: 'Candlestick',\n          k: 'K line chart',\n          heatmap: 'Heat map',\n          map: 'Map',\n          parallel: 'Parallel coordinate map',\n          lines: 'Line graph',\n          graph: 'Relationship graph',\n          sankey: 'Sankey diagram',\n          funnel: 'Funnel chart',\n          gauge: 'Gauge',\n          pictorialBar: 'Pictorial bar',\n          themeRiver: 'Theme River Map',\n          sunburst: 'Sunburst'\n        }\n      },\n      aria: {\n        general: {\n          withTitle: 'This is a chart about \"{title}\"',\n          withoutTitle: 'This is a chart'\n        },\n        series: {\n          single: {\n            prefix: '',\n            withName: ' with type {seriesType} named {seriesName}.',\n            withoutName: ' with type {seriesType}.'\n          },\n          multiple: {\n            prefix: '. It consists of {seriesCount} series count.',\n            withName: ' The {seriesId} series is a {seriesType} representing {seriesName}.',\n            withoutName: ' The {seriesId} series is a {seriesType}.',\n            separator: {\n              middle: '',\n              end: ''\n            }\n          }\n        },\n        data: {\n          allData: 'The data is as follows: ',\n          partialData: 'The first {displayCnt} items are: ',\n          withName: 'the data for {name} is {value}',\n          withoutName: '{value}',\n          separator: {\n            middle: ', ',\n            end: '. '\n          }\n        }\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n     * Licensed to the Apache Software Foundation (ASF) under one\n     * or more contributor license agreements.  See the NOTICE file\n     * distributed with this work for additional information\n     * regarding copyright ownership.  The ASF licenses this file\n     * to you under the Apache License, Version 2.0 (the\n     * \"License\"); you may not use this file except in compliance\n     * with the License.  You may obtain a copy of the License at\n     *\n     *   http://www.apache.org/licenses/LICENSE-2.0\n     *\n     * Unless required by applicable law or agreed to in writing,\n     * software distributed under the License is distributed on an\n     * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n     * KIND, either express or implied.  See the License for the\n     * specific language governing permissions and limitations\n     * under the License.\n     */\n    var langZH = {\n      time: {\n        month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],\n        monthAbbr: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],\n        dayOfWeek: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],\n        dayOfWeekAbbr: ['日', '一', '二', '三', '四', '五', '六']\n      },\n      legend: {\n        selector: {\n          all: '全选',\n          inverse: '反选'\n        }\n      },\n      toolbox: {\n        brush: {\n          title: {\n            rect: '矩形选择',\n            polygon: '圈选',\n            lineX: '横向选择',\n            lineY: '纵向选择',\n            keep: '保持选择',\n            clear: '清除选择'\n          }\n        },\n        dataView: {\n          title: '数据视图',\n          lang: ['数据视图', '关闭', '刷新']\n        },\n        dataZoom: {\n          title: {\n            zoom: '区域缩放',\n            back: '区域缩放还原'\n          }\n        },\n        magicType: {\n          title: {\n            line: '切换为折线图',\n            bar: '切换为柱状图',\n            stack: '切换为堆叠',\n            tiled: '切换为平铺'\n          }\n        },\n        restore: {\n          title: '还原'\n        },\n        saveAsImage: {\n          title: '保存为图片',\n          lang: ['右键另存为图片']\n        }\n      },\n      series: {\n        typeNames: {\n          pie: '饼图',\n          bar: '柱状图',\n          line: '折线图',\n          scatter: '散点图',\n          effectScatter: '涟漪散点图',\n          radar: '雷达图',\n          tree: '树图',\n          treemap: '矩形树图',\n          boxplot: '箱型图',\n          candlestick: 'K线图',\n          k: 'K线图',\n          heatmap: '热力图',\n          map: '地图',\n          parallel: '平行坐标图',\n          lines: '线图',\n          graph: '关系图',\n          sankey: '桑基图',\n          funnel: '漏斗图',\n          gauge: '仪表盘图',\n          pictorialBar: '象形柱图',\n          themeRiver: '主题河流图',\n          sunburst: '旭日图'\n        }\n      },\n      aria: {\n        general: {\n          withTitle: '这是一个关于“{title}”的图表。',\n          withoutTitle: '这是一个图表，'\n        },\n        series: {\n          single: {\n            prefix: '',\n            withName: '图表类型是{seriesType}，表示{seriesName}。',\n            withoutName: '图表类型是{seriesType}。'\n          },\n          multiple: {\n            prefix: '它由{seriesCount}个图表系列组成。',\n            withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType}，',\n            withoutName: '第{seriesId}个系列是一个{seriesType}，',\n            separator: {\n              middle: '；',\n              end: '。'\n            }\n          }\n        },\n        data: {\n          allData: '其数据是——',\n          partialData: '其中，前{displayCnt}项是——',\n          withName: '{name}的数据是{value}',\n          withoutName: '{value}',\n          separator: {\n            middle: '，',\n            end: ''\n          }\n        }\n      }\n    };\n\n    var LOCALE_ZH = 'ZH';\n    var LOCALE_EN = 'EN';\n    var DEFAULT_LOCALE = LOCALE_EN;\n    var localeStorage = {};\n    var localeModels = {};\n    var SYSTEM_LANG = !env.domSupported ? DEFAULT_LOCALE : function () {\n      var langStr = (\n      /* eslint-disable-next-line */\n      document.documentElement.lang || navigator.language || navigator.browserLanguage).toUpperCase();\n      return langStr.indexOf(LOCALE_ZH) > -1 ? LOCALE_ZH : DEFAULT_LOCALE;\n    }();\n    function registerLocale(locale, localeObj) {\n      locale = locale.toUpperCase();\n      localeModels[locale] = new Model(localeObj);\n      localeStorage[locale] = localeObj;\n    } // export function getLocale(locale: string) {\n    //     return localeStorage[locale];\n    // }\n\n    function createLocaleObject(locale) {\n      if (isString(locale)) {\n        var localeObj = localeStorage[locale.toUpperCase()] || {};\n\n        if (locale === LOCALE_ZH || locale === LOCALE_EN) {\n          return clone(localeObj);\n        } else {\n          return merge(clone(localeObj), clone(localeStorage[DEFAULT_LOCALE]), false);\n        }\n      } else {\n        return merge(clone(locale), clone(localeStorage[DEFAULT_LOCALE]), false);\n      }\n    }\n    function getLocaleModel(lang) {\n      return localeModels[lang];\n    }\n    function getDefaultLocaleModel() {\n      return localeModels[DEFAULT_LOCALE];\n    } // Default locale\n\n    registerLocale(LOCALE_EN, langEN);\n    registerLocale(LOCALE_ZH, langZH);\n\n    var ONE_SECOND = 1000;\n    var ONE_MINUTE = ONE_SECOND * 60;\n    var ONE_HOUR = ONE_MINUTE * 60;\n    var ONE_DAY = ONE_HOUR * 24;\n    var ONE_YEAR = ONE_DAY * 365;\n    var defaultLeveledFormatter = {\n      year: '{yyyy}',\n      month: '{MMM}',\n      day: '{d}',\n      hour: '{HH}:{mm}',\n      minute: '{HH}:{mm}',\n      second: '{HH}:{mm}:{ss}',\n      millisecond: '{HH}:{mm}:{ss} {SSS}',\n      none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}'\n    };\n    var fullDayFormatter = '{yyyy}-{MM}-{dd}';\n    var fullLeveledFormatter = {\n      year: '{yyyy}',\n      month: '{yyyy}-{MM}',\n      day: fullDayFormatter,\n      hour: fullDayFormatter + ' ' + defaultLeveledFormatter.hour,\n      minute: fullDayFormatter + ' ' + defaultLeveledFormatter.minute,\n      second: fullDayFormatter + ' ' + defaultLeveledFormatter.second,\n      millisecond: defaultLeveledFormatter.none\n    };\n    var primaryTimeUnits = ['year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond'];\n    var timeUnits = ['year', 'half-year', 'quarter', 'month', 'week', 'half-week', 'day', 'half-day', 'quarter-day', 'hour', 'minute', 'second', 'millisecond'];\n    function pad(str, len) {\n      str += '';\n      return '0000'.substr(0, len - str.length) + str;\n    }\n    function getPrimaryTimeUnit(timeUnit) {\n      switch (timeUnit) {\n        case 'half-year':\n        case 'quarter':\n          return 'month';\n\n        case 'week':\n        case 'half-week':\n          return 'day';\n\n        case 'half-day':\n        case 'quarter-day':\n          return 'hour';\n\n        default:\n          // year, minutes, second, milliseconds\n          return timeUnit;\n      }\n    }\n    function isPrimaryTimeUnit(timeUnit) {\n      return timeUnit === getPrimaryTimeUnit(timeUnit);\n    }\n    function getDefaultFormatPrecisionOfInterval(timeUnit) {\n      switch (timeUnit) {\n        case 'year':\n        case 'month':\n          return 'day';\n\n        case 'millisecond':\n          return 'millisecond';\n\n        default:\n          // Also for day, hour, minute, second\n          return 'second';\n      }\n    }\n    function format( // Note: The result based on `isUTC` are totally different, which can not be just simply\n    // substituted by the result without `isUTC`. So we make the param `isUTC` mandatory.\n    time, template, isUTC, lang) {\n      var date = parseDate(time);\n      var y = date[fullYearGetterName(isUTC)]();\n      var M = date[monthGetterName(isUTC)]() + 1;\n      var q = Math.floor((M - 1) / 3) + 1;\n      var d = date[dateGetterName(isUTC)]();\n      var e = date['get' + (isUTC ? 'UTC' : '') + 'Day']();\n      var H = date[hoursGetterName(isUTC)]();\n      var h = (H - 1) % 12 + 1;\n      var m = date[minutesGetterName(isUTC)]();\n      var s = date[secondsGetterName(isUTC)]();\n      var S = date[millisecondsGetterName(isUTC)]();\n      var localeModel = lang instanceof Model ? lang : getLocaleModel(lang || SYSTEM_LANG) || getDefaultLocaleModel();\n      var timeModel = localeModel.getModel('time');\n      var month = timeModel.get('month');\n      var monthAbbr = timeModel.get('monthAbbr');\n      var dayOfWeek = timeModel.get('dayOfWeek');\n      var dayOfWeekAbbr = timeModel.get('dayOfWeekAbbr');\n      return (template || '').replace(/{yyyy}/g, y + '').replace(/{yy}/g, y % 100 + '').replace(/{Q}/g, q + '').replace(/{MMMM}/g, month[M - 1]).replace(/{MMM}/g, monthAbbr[M - 1]).replace(/{MM}/g, pad(M, 2)).replace(/{M}/g, M + '').replace(/{dd}/g, pad(d, 2)).replace(/{d}/g, d + '').replace(/{eeee}/g, dayOfWeek[e]).replace(/{ee}/g, dayOfWeekAbbr[e]).replace(/{e}/g, e + '').replace(/{HH}/g, pad(H, 2)).replace(/{H}/g, H + '').replace(/{hh}/g, pad(h + '', 2)).replace(/{h}/g, h + '').replace(/{mm}/g, pad(m, 2)).replace(/{m}/g, m + '').replace(/{ss}/g, pad(s, 2)).replace(/{s}/g, s + '').replace(/{SSS}/g, pad(S, 3)).replace(/{S}/g, S + '');\n    }\n    function leveledFormat(tick, idx, formatter, lang, isUTC) {\n      var template = null;\n\n      if (isString(formatter)) {\n        // Single formatter for all units at all levels\n        template = formatter;\n      } else if (isFunction(formatter)) {\n        // Callback formatter\n        template = formatter(tick.value, idx, {\n          level: tick.level\n        });\n      } else {\n        var defaults$1 = extend({}, defaultLeveledFormatter);\n\n        if (tick.level > 0) {\n          for (var i = 0; i < primaryTimeUnits.length; ++i) {\n            defaults$1[primaryTimeUnits[i]] = \"{primary|\" + defaults$1[primaryTimeUnits[i]] + \"}\";\n          }\n        }\n\n        var mergedFormatter = formatter ? formatter.inherit === false ? formatter // Use formatter with bigger units\n        : defaults(formatter, defaults$1) : defaults$1;\n        var unit = getUnitFromValue(tick.value, isUTC);\n\n        if (mergedFormatter[unit]) {\n          template = mergedFormatter[unit];\n        } else if (mergedFormatter.inherit) {\n          // Unit formatter is not defined and should inherit from bigger units\n          var targetId = timeUnits.indexOf(unit);\n\n          for (var i = targetId - 1; i >= 0; --i) {\n            if (mergedFormatter[unit]) {\n              template = mergedFormatter[unit];\n              break;\n            }\n          }\n\n          template = template || defaults$1.none;\n        }\n\n        if (isArray(template)) {\n          var levelId = tick.level == null ? 0 : tick.level >= 0 ? tick.level : template.length + tick.level;\n          levelId = Math.min(levelId, template.length - 1);\n          template = template[levelId];\n        }\n      }\n\n      return format(new Date(tick.value), template, isUTC, lang);\n    }\n    function getUnitFromValue(value, isUTC) {\n      var date = parseDate(value);\n      var M = date[monthGetterName(isUTC)]() + 1;\n      var d = date[dateGetterName(isUTC)]();\n      var h = date[hoursGetterName(isUTC)]();\n      var m = date[minutesGetterName(isUTC)]();\n      var s = date[secondsGetterName(isUTC)]();\n      var S = date[millisecondsGetterName(isUTC)]();\n      var isSecond = S === 0;\n      var isMinute = isSecond && s === 0;\n      var isHour = isMinute && m === 0;\n      var isDay = isHour && h === 0;\n      var isMonth = isDay && d === 1;\n      var isYear = isMonth && M === 1;\n\n      if (isYear) {\n        return 'year';\n      } else if (isMonth) {\n        return 'month';\n      } else if (isDay) {\n        return 'day';\n      } else if (isHour) {\n        return 'hour';\n      } else if (isMinute) {\n        return 'minute';\n      } else if (isSecond) {\n        return 'second';\n      } else {\n        return 'millisecond';\n      }\n    }\n    function getUnitValue(value, unit, isUTC) {\n      var date = isNumber(value) ? parseDate(value) : value;\n      unit = unit || getUnitFromValue(value, isUTC);\n\n      switch (unit) {\n        case 'year':\n          return date[fullYearGetterName(isUTC)]();\n\n        case 'half-year':\n          return date[monthGetterName(isUTC)]() >= 6 ? 1 : 0;\n\n        case 'quarter':\n          return Math.floor((date[monthGetterName(isUTC)]() + 1) / 4);\n\n        case 'month':\n          return date[monthGetterName(isUTC)]();\n\n        case 'day':\n          return date[dateGetterName(isUTC)]();\n\n        case 'half-day':\n          return date[hoursGetterName(isUTC)]() / 24;\n\n        case 'hour':\n          return date[hoursGetterName(isUTC)]();\n\n        case 'minute':\n          return date[minutesGetterName(isUTC)]();\n\n        case 'second':\n          return date[secondsGetterName(isUTC)]();\n\n        case 'millisecond':\n          return date[millisecondsGetterName(isUTC)]();\n      }\n    }\n    function fullYearGetterName(isUTC) {\n      return isUTC ? 'getUTCFullYear' : 'getFullYear';\n    }\n    function monthGetterName(isUTC) {\n      return isUTC ? 'getUTCMonth' : 'getMonth';\n    }\n    function dateGetterName(isUTC) {\n      return isUTC ? 'getUTCDate' : 'getDate';\n    }\n    function hoursGetterName(isUTC) {\n      return isUTC ? 'getUTCHours' : 'getHours';\n    }\n    function minutesGetterName(isUTC) {\n      return isUTC ? 'getUTCMinutes' : 'getMinutes';\n    }\n    function secondsGetterName(isUTC) {\n      return isUTC ? 'getUTCSeconds' : 'getSeconds';\n    }\n    function millisecondsGetterName(isUTC) {\n      return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds';\n    }\n    function fullYearSetterName(isUTC) {\n      return isUTC ? 'setUTCFullYear' : 'setFullYear';\n    }\n    function monthSetterName(isUTC) {\n      return isUTC ? 'setUTCMonth' : 'setMonth';\n    }\n    function dateSetterName(isUTC) {\n      return isUTC ? 'setUTCDate' : 'setDate';\n    }\n    function hoursSetterName(isUTC) {\n      return isUTC ? 'setUTCHours' : 'setHours';\n    }\n    function minutesSetterName(isUTC) {\n      return isUTC ? 'setUTCMinutes' : 'setMinutes';\n    }\n    function secondsSetterName(isUTC) {\n      return isUTC ? 'setUTCSeconds' : 'setSeconds';\n    }\n    function millisecondsSetterName(isUTC) {\n      return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds';\n    }\n\n    function getTextRect(text, font, align, verticalAlign, padding, rich, truncate, lineHeight) {\n      var textEl = new ZRText({\n        style: {\n          text: text,\n          font: font,\n          align: align,\n          verticalAlign: verticalAlign,\n          padding: padding,\n          rich: rich,\n          overflow: truncate ? 'truncate' : null,\n          lineHeight: lineHeight\n        }\n      });\n      return textEl.getBoundingRect();\n    }\n\n    /**\n     * Add a comma each three digit.\n     */\n\n    function addCommas(x) {\n      if (!isNumeric(x)) {\n        return isString(x) ? x : '-';\n      }\n\n      var parts = (x + '').split('.');\n      return parts[0].replace(/(\\d{1,3})(?=(?:\\d{3})+(?!\\d))/g, '$1,') + (parts.length > 1 ? '.' + parts[1] : '');\n    }\n    function toCamelCase(str, upperCaseFirst) {\n      str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {\n        return group1.toUpperCase();\n      });\n\n      if (upperCaseFirst && str) {\n        str = str.charAt(0).toUpperCase() + str.slice(1);\n      }\n\n      return str;\n    }\n    var normalizeCssArray$1 = normalizeCssArray;\n    /**\n     * Make value user readable for tooltip and label.\n     * \"User readable\":\n     *     Try to not print programmer-specific text like NaN, Infinity, null, undefined.\n     *     Avoid to display an empty string, which users can not recognize there is\n     *     a value and it might look like a bug.\n     */\n\n    function makeValueReadable(value, valueType, useUTC) {\n      var USER_READABLE_DEFUALT_TIME_PATTERN = '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}';\n\n      function stringToUserReadable(str) {\n        return str && trim(str) ? str : '-';\n      }\n\n      function isNumberUserReadable(num) {\n        return !!(num != null && !isNaN(num) && isFinite(num));\n      }\n\n      var isTypeTime = valueType === 'time';\n      var isValueDate = value instanceof Date;\n\n      if (isTypeTime || isValueDate) {\n        var date = isTypeTime ? parseDate(value) : value;\n\n        if (!isNaN(+date)) {\n          return format(date, USER_READABLE_DEFUALT_TIME_PATTERN, useUTC);\n        } else if (isValueDate) {\n          return '-';\n        } // In other cases, continue to try to display the value in the following code.\n\n      }\n\n      if (valueType === 'ordinal') {\n        return isStringSafe(value) ? stringToUserReadable(value) : isNumber(value) ? isNumberUserReadable(value) ? value + '' : '-' : '-';\n      } // By default.\n\n\n      var numericResult = numericToNumber(value);\n      return isNumberUserReadable(numericResult) ? addCommas(numericResult) : isStringSafe(value) ? stringToUserReadable(value) : typeof value === 'boolean' ? value + '' : '-';\n    }\n    var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];\n\n    var wrapVar = function (varName, seriesIdx) {\n      return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';\n    };\n    /**\n     * Template formatter\n     * @param {Array.<Object>|Object} paramsList\n     */\n\n\n    function formatTpl(tpl, paramsList, encode) {\n      if (!isArray(paramsList)) {\n        paramsList = [paramsList];\n      }\n\n      var seriesLen = paramsList.length;\n\n      if (!seriesLen) {\n        return '';\n      }\n\n      var $vars = paramsList[0].$vars || [];\n\n      for (var i = 0; i < $vars.length; i++) {\n        var alias = TPL_VAR_ALIAS[i];\n        tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0));\n      }\n\n      for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {\n        for (var k = 0; k < $vars.length; k++) {\n          var val = paramsList[seriesIdx][$vars[k]];\n          tpl = tpl.replace(wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val);\n        }\n      }\n\n      return tpl;\n    }\n    /**\n     * simple Template formatter\n     */\n\n    function formatTplSimple(tpl, param, encode) {\n      each(param, function (value, key) {\n        tpl = tpl.replace('{' + key + '}', encode ? encodeHTML(value) : value);\n      });\n      return tpl;\n    }\n    function getTooltipMarker(inOpt, extraCssText) {\n      var opt = isString(inOpt) ? {\n        color: inOpt,\n        extraCssText: extraCssText\n      } : inOpt || {};\n      var color = opt.color;\n      var type = opt.type;\n      extraCssText = opt.extraCssText;\n      var renderMode = opt.renderMode || 'html';\n\n      if (!color) {\n        return '';\n      }\n\n      if (renderMode === 'html') {\n        return type === 'subItem' ? '<span style=\"display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' + 'border-radius:4px;width:4px;height:4px;background-color:' // Only support string\n        + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>' : '<span style=\"display:inline-block;margin-right:4px;' + 'border-radius:10px;width:10px;height:10px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>';\n      } else {\n        // Should better not to auto generate style name by auto-increment number here.\n        // Because this util is usually called in tooltip formatter, which is probably\n        // called repeatedly when mouse move and the auto-increment number increases fast.\n        // Users can make their own style name by theirselves, make it unique and readable.\n        var markerId = opt.markerId || 'markerX';\n        return {\n          renderMode: renderMode,\n          content: '{' + markerId + '|}  ',\n          style: type === 'subItem' ? {\n            width: 4,\n            height: 4,\n            borderRadius: 2,\n            backgroundColor: color\n          } : {\n            width: 10,\n            height: 10,\n            borderRadius: 5,\n            backgroundColor: color\n          }\n        };\n      }\n    }\n    /**\n     * @deprecated Use `time/format` instead.\n     * ISO Date format\n     * @param {string} tpl\n     * @param {number} value\n     * @param {boolean} [isUTC=false] Default in local time.\n     *           see `module:echarts/scale/Time`\n     *           and `module:echarts/util/number#parseDate`.\n     * @inner\n     */\n\n    function formatTime(tpl, value, isUTC) {\n      if (\"development\" !== 'production') {\n        deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format');\n      }\n\n      if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year') {\n        tpl = 'MM-dd\\nyyyy';\n      }\n\n      var date = parseDate(value);\n      var getUTC = isUTC ? 'getUTC' : 'get';\n      var y = date[getUTC + 'FullYear']();\n      var M = date[getUTC + 'Month']() + 1;\n      var d = date[getUTC + 'Date']();\n      var h = date[getUTC + 'Hours']();\n      var m = date[getUTC + 'Minutes']();\n      var s = date[getUTC + 'Seconds']();\n      var S = date[getUTC + 'Milliseconds']();\n      tpl = tpl.replace('MM', pad(M, 2)).replace('M', M).replace('yyyy', y).replace('yy', pad(y % 100 + '', 2)).replace('dd', pad(d, 2)).replace('d', d).replace('hh', pad(h, 2)).replace('h', h).replace('mm', pad(m, 2)).replace('m', m).replace('ss', pad(s, 2)).replace('s', s).replace('SSS', pad(S, 3));\n      return tpl;\n    }\n    /**\n     * Capital first\n     * @param {string} str\n     * @return {string}\n     */\n\n    function capitalFirst(str) {\n      return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;\n    }\n    /**\n     * @return Never be null/undefined.\n     */\n\n    function convertToColorString(color, defaultColor) {\n      defaultColor = defaultColor || 'transparent';\n      return isString(color) ? color : isObject(color) ? color.colorStops && (color.colorStops[0] || {}).color || defaultColor : defaultColor;\n    }\n    /**\n     * open new tab\n     * @param link url\n     * @param target blank or self\n     */\n\n    function windowOpen(link, target) {\n      /* global window */\n      if (target === '_blank' || target === 'blank') {\n        var blank = window.open();\n        blank.opener = null;\n        blank.location.href = link;\n      } else {\n        window.open(link, target);\n      }\n    }\n\n    var each$1 = each;\n    /**\n     * @public\n     */\n\n    var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];\n    /**\n     * @public\n     */\n\n    var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];\n\n    function boxLayout(orient, group, gap, maxWidth, maxHeight) {\n      var x = 0;\n      var y = 0;\n\n      if (maxWidth == null) {\n        maxWidth = Infinity;\n      }\n\n      if (maxHeight == null) {\n        maxHeight = Infinity;\n      }\n\n      var currentLineMaxSize = 0;\n      group.eachChild(function (child, idx) {\n        var rect = child.getBoundingRect();\n        var nextChild = group.childAt(idx + 1);\n        var nextChildRect = nextChild && nextChild.getBoundingRect();\n        var nextX;\n        var nextY;\n\n        if (orient === 'horizontal') {\n          var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);\n          nextX = x + moveX; // Wrap when width exceeds maxWidth or meet a `newline` group\n          // FIXME compare before adding gap?\n\n          if (nextX > maxWidth || child.newline) {\n            x = 0;\n            nextX = moveX;\n            y += currentLineMaxSize + gap;\n            currentLineMaxSize = rect.height;\n          } else {\n            // FIXME: consider rect.y is not `0`?\n            currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);\n          }\n        } else {\n          var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);\n          nextY = y + moveY; // Wrap when width exceeds maxHeight or meet a `newline` group\n\n          if (nextY > maxHeight || child.newline) {\n            x += currentLineMaxSize + gap;\n            y = 0;\n            nextY = moveY;\n            currentLineMaxSize = rect.width;\n          } else {\n            currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);\n          }\n        }\n\n        if (child.newline) {\n          return;\n        }\n\n        child.x = x;\n        child.y = y;\n        child.markRedraw();\n        orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;\n      });\n    }\n    /**\n     * VBox or HBox layouting\n     * @param {string} orient\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n\n    var box = boxLayout;\n    /**\n     * VBox layouting\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n    var vbox = curry(boxLayout, 'vertical');\n    /**\n     * HBox layouting\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n    var hbox = curry(boxLayout, 'horizontal');\n    /**\n     * If x or x2 is not specified or 'center' 'left' 'right',\n     * the width would be as long as possible.\n     * If y or y2 is not specified or 'middle' 'top' 'bottom',\n     * the height would be as long as possible.\n     */\n\n    function getAvailableSize(positionInfo, containerRect, margin) {\n      var containerWidth = containerRect.width;\n      var containerHeight = containerRect.height;\n      var x = parsePercent$1(positionInfo.left, containerWidth);\n      var y = parsePercent$1(positionInfo.top, containerHeight);\n      var x2 = parsePercent$1(positionInfo.right, containerWidth);\n      var y2 = parsePercent$1(positionInfo.bottom, containerHeight);\n      (isNaN(x) || isNaN(parseFloat(positionInfo.left))) && (x = 0);\n      (isNaN(x2) || isNaN(parseFloat(positionInfo.right))) && (x2 = containerWidth);\n      (isNaN(y) || isNaN(parseFloat(positionInfo.top))) && (y = 0);\n      (isNaN(y2) || isNaN(parseFloat(positionInfo.bottom))) && (y2 = containerHeight);\n      margin = normalizeCssArray$1(margin || 0);\n      return {\n        width: Math.max(x2 - x - margin[1] - margin[3], 0),\n        height: Math.max(y2 - y - margin[0] - margin[2], 0)\n      };\n    }\n    /**\n     * Parse position info.\n     */\n\n    function getLayoutRect(positionInfo, containerRect, margin) {\n      margin = normalizeCssArray$1(margin || 0);\n      var containerWidth = containerRect.width;\n      var containerHeight = containerRect.height;\n      var left = parsePercent$1(positionInfo.left, containerWidth);\n      var top = parsePercent$1(positionInfo.top, containerHeight);\n      var right = parsePercent$1(positionInfo.right, containerWidth);\n      var bottom = parsePercent$1(positionInfo.bottom, containerHeight);\n      var width = parsePercent$1(positionInfo.width, containerWidth);\n      var height = parsePercent$1(positionInfo.height, containerHeight);\n      var verticalMargin = margin[2] + margin[0];\n      var horizontalMargin = margin[1] + margin[3];\n      var aspect = positionInfo.aspect; // If width is not specified, calculate width from left and right\n\n      if (isNaN(width)) {\n        width = containerWidth - right - horizontalMargin - left;\n      }\n\n      if (isNaN(height)) {\n        height = containerHeight - bottom - verticalMargin - top;\n      }\n\n      if (aspect != null) {\n        // If width and height are not given\n        // 1. Graph should not exceeds the container\n        // 2. Aspect must be keeped\n        // 3. Graph should take the space as more as possible\n        // FIXME\n        // Margin is not considered, because there is no case that both\n        // using margin and aspect so far.\n        if (isNaN(width) && isNaN(height)) {\n          if (aspect > containerWidth / containerHeight) {\n            width = containerWidth * 0.8;\n          } else {\n            height = containerHeight * 0.8;\n          }\n        } // Calculate width or height with given aspect\n\n\n        if (isNaN(width)) {\n          width = aspect * height;\n        }\n\n        if (isNaN(height)) {\n          height = width / aspect;\n        }\n      } // If left is not specified, calculate left from right and width\n\n\n      if (isNaN(left)) {\n        left = containerWidth - right - width - horizontalMargin;\n      }\n\n      if (isNaN(top)) {\n        top = containerHeight - bottom - height - verticalMargin;\n      } // Align left and top\n\n\n      switch (positionInfo.left || positionInfo.right) {\n        case 'center':\n          left = containerWidth / 2 - width / 2 - margin[3];\n          break;\n\n        case 'right':\n          left = containerWidth - width - horizontalMargin;\n          break;\n      }\n\n      switch (positionInfo.top || positionInfo.bottom) {\n        case 'middle':\n        case 'center':\n          top = containerHeight / 2 - height / 2 - margin[0];\n          break;\n\n        case 'bottom':\n          top = containerHeight - height - verticalMargin;\n          break;\n      } // If something is wrong and left, top, width, height are calculated as NaN\n\n\n      left = left || 0;\n      top = top || 0;\n\n      if (isNaN(width)) {\n        // Width may be NaN if only one value is given except width\n        width = containerWidth - horizontalMargin - left - (right || 0);\n      }\n\n      if (isNaN(height)) {\n        // Height may be NaN if only one value is given except height\n        height = containerHeight - verticalMargin - top - (bottom || 0);\n      }\n\n      var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);\n      rect.margin = margin;\n      return rect;\n    }\n    /**\n     * Position a zr element in viewport\n     *  Group position is specified by either\n     *  {left, top}, {right, bottom}\n     *  If all properties exists, right and bottom will be igonred.\n     *\n     * Logic:\n     *     1. Scale (against origin point in parent coord)\n     *     2. Rotate (against origin point in parent coord)\n     *     3. Translate (with el.position by this method)\n     * So this method only fixes the last step 'Translate', which does not affect\n     * scaling and rotating.\n     *\n     * If be called repeatedly with the same input el, the same result will be gotten.\n     *\n     * Return true if the layout happened.\n     *\n     * @param el Should have `getBoundingRect` method.\n     * @param positionInfo\n     * @param positionInfo.left\n     * @param positionInfo.top\n     * @param positionInfo.right\n     * @param positionInfo.bottom\n     * @param positionInfo.width Only for opt.boundingModel: 'raw'\n     * @param positionInfo.height Only for opt.boundingModel: 'raw'\n     * @param containerRect\n     * @param margin\n     * @param opt\n     * @param opt.hv Only horizontal or only vertical. Default to be [1, 1]\n     * @param opt.boundingMode\n     *        Specify how to calculate boundingRect when locating.\n     *        'all': Position the boundingRect that is transformed and uioned\n     *               both itself and its descendants.\n     *               This mode simplies confine the elements in the bounding\n     *               of their container (e.g., using 'right: 0').\n     *        'raw': Position the boundingRect that is not transformed and only itself.\n     *               This mode is useful when you want a element can overflow its\n     *               container. (Consider a rotated circle needs to be located in a corner.)\n     *               In this mode positionInfo.width/height can only be number.\n     */\n\n    function positionElement(el, positionInfo, containerRect, margin, opt, out) {\n      var h = !opt || !opt.hv || opt.hv[0];\n      var v = !opt || !opt.hv || opt.hv[1];\n      var boundingMode = opt && opt.boundingMode || 'all';\n      out = out || el;\n      out.x = el.x;\n      out.y = el.y;\n\n      if (!h && !v) {\n        return false;\n      }\n\n      var rect;\n\n      if (boundingMode === 'raw') {\n        rect = el.type === 'group' ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) : el.getBoundingRect();\n      } else {\n        rect = el.getBoundingRect();\n\n        if (el.needLocalTransform()) {\n          var transform = el.getLocalTransform(); // Notice: raw rect may be inner object of el,\n          // which should not be modified.\n\n          rect = rect.clone();\n          rect.applyTransform(transform);\n        }\n      } // The real width and height can not be specified but calculated by the given el.\n\n\n      var layoutRect = getLayoutRect(defaults({\n        width: rect.width,\n        height: rect.height\n      }, positionInfo), containerRect, margin); // Because 'tranlate' is the last step in transform\n      // (see zrender/core/Transformable#getLocalTransform),\n      // we can just only modify el.position to get final result.\n\n      var dx = h ? layoutRect.x - rect.x : 0;\n      var dy = v ? layoutRect.y - rect.y : 0;\n\n      if (boundingMode === 'raw') {\n        out.x = dx;\n        out.y = dy;\n      } else {\n        out.x += dx;\n        out.y += dy;\n      }\n\n      if (out === el) {\n        el.markRedraw();\n      }\n\n      return true;\n    }\n    /**\n     * @param option Contains some of the properties in HV_NAMES.\n     * @param hvIdx 0: horizontal; 1: vertical.\n     */\n\n    function sizeCalculable(option, hvIdx) {\n      return option[HV_NAMES[hvIdx][0]] != null || option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null;\n    }\n    function fetchLayoutMode(ins) {\n      var layoutMode = ins.layoutMode || ins.constructor.layoutMode;\n      return isObject(layoutMode) ? layoutMode : layoutMode ? {\n        type: layoutMode\n      } : null;\n    }\n    /**\n     * Consider Case:\n     * When default option has {left: 0, width: 100}, and we set {right: 0}\n     * through setOption or media query, using normal zrUtil.merge will cause\n     * {right: 0} does not take effect.\n     *\n     * @example\n     * ComponentModel.extend({\n     *     init: function () {\n     *         ...\n     *         let inputPositionParams = layout.getLayoutParams(option);\n     *         this.mergeOption(inputPositionParams);\n     *     },\n     *     mergeOption: function (newOption) {\n     *         newOption && zrUtil.merge(thisOption, newOption, true);\n     *         layout.mergeLayoutParam(thisOption, newOption);\n     *     }\n     * });\n     *\n     * @param targetOption\n     * @param newOption\n     * @param opt\n     */\n\n    function mergeLayoutParam(targetOption, newOption, opt) {\n      var ignoreSize = opt && opt.ignoreSize;\n      !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);\n      var hResult = merge(HV_NAMES[0], 0);\n      var vResult = merge(HV_NAMES[1], 1);\n      copy(HV_NAMES[0], targetOption, hResult);\n      copy(HV_NAMES[1], targetOption, vResult);\n\n      function merge(names, hvIdx) {\n        var newParams = {};\n        var newValueCount = 0;\n        var merged = {};\n        var mergedValueCount = 0;\n        var enoughParamNumber = 2;\n        each$1(names, function (name) {\n          merged[name] = targetOption[name];\n        });\n        each$1(names, function (name) {\n          // Consider case: newOption.width is null, which is\n          // set by user for removing width setting.\n          hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);\n          hasValue(newParams, name) && newValueCount++;\n          hasValue(merged, name) && mergedValueCount++;\n        });\n\n        if (ignoreSize[hvIdx]) {\n          // Only one of left/right is premitted to exist.\n          if (hasValue(newOption, names[1])) {\n            merged[names[2]] = null;\n          } else if (hasValue(newOption, names[2])) {\n            merged[names[1]] = null;\n          }\n\n          return merged;\n        } // Case: newOption: {width: ..., right: ...},\n        // or targetOption: {right: ...} and newOption: {width: ...},\n        // There is no conflict when merged only has params count\n        // little than enoughParamNumber.\n\n\n        if (mergedValueCount === enoughParamNumber || !newValueCount) {\n          return merged;\n        } // Case: newOption: {width: ..., right: ...},\n        // Than we can make sure user only want those two, and ignore\n        // all origin params in targetOption.\n        else if (newValueCount >= enoughParamNumber) {\n            return newParams;\n          } else {\n            // Chose another param from targetOption by priority.\n            for (var i = 0; i < names.length; i++) {\n              var name_1 = names[i];\n\n              if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) {\n                newParams[name_1] = targetOption[name_1];\n                break;\n              }\n            }\n\n            return newParams;\n          }\n      }\n\n      function hasProp(obj, name) {\n        return obj.hasOwnProperty(name);\n      }\n\n      function hasValue(obj, name) {\n        return obj[name] != null && obj[name] !== 'auto';\n      }\n\n      function copy(names, target, source) {\n        each$1(names, function (name) {\n          target[name] = source[name];\n        });\n      }\n    }\n    /**\n     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n     */\n\n    function getLayoutParams(source) {\n      return copyLayoutParams({}, source);\n    }\n    /**\n     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n     * @param {Object} source\n     * @return {Object} Result contains those props.\n     */\n\n    function copyLayoutParams(target, source) {\n      source && target && each$1(LOCATION_PARAMS, function (name) {\n        source.hasOwnProperty(name) && (target[name] = source[name]);\n      });\n      return target;\n    }\n\n    var inner = makeInner();\n\n    var ComponentModel =\n    /** @class */\n    function (_super) {\n      __extends(ComponentModel, _super);\n\n      function ComponentModel(option, parentModel, ecModel) {\n        var _this = _super.call(this, option, parentModel, ecModel) || this;\n\n        _this.uid = getUID('ec_cpt_model');\n        return _this;\n      }\n\n      ComponentModel.prototype.init = function (option, parentModel, ecModel) {\n        this.mergeDefaultAndTheme(option, ecModel);\n      };\n\n      ComponentModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n        var layoutMode = fetchLayoutMode(this);\n        var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n        var themeModel = ecModel.getTheme();\n        merge(option, themeModel.get(this.mainType));\n        merge(option, this.getDefaultOption());\n\n        if (layoutMode) {\n          mergeLayoutParam(option, inputPositionParams, layoutMode);\n        }\n      };\n\n      ComponentModel.prototype.mergeOption = function (option, ecModel) {\n        merge(this.option, option, true);\n        var layoutMode = fetchLayoutMode(this);\n\n        if (layoutMode) {\n          mergeLayoutParam(this.option, option, layoutMode);\n        }\n      };\n      /**\n       * Called immediately after `init` or `mergeOption` of this instance called.\n       */\n\n\n      ComponentModel.prototype.optionUpdated = function (newCptOption, isInit) {};\n      /**\n       * [How to declare defaultOption]:\n       *\n       * (A) If using class declaration in typescript (since echarts 5):\n       * ```ts\n       * import {ComponentOption} from '../model/option.js';\n       * export interface XxxOption extends ComponentOption {\n       *     aaa: number\n       * }\n       * export class XxxModel extends Component {\n       *     static type = 'xxx';\n       *     static defaultOption: XxxOption = {\n       *         aaa: 123\n       *     }\n       * }\n       * Component.registerClass(XxxModel);\n       * ```\n       * ```ts\n       * import {inheritDefaultOption} from '../util/component.js';\n       * import {XxxModel, XxxOption} from './XxxModel.js';\n       * export interface XxxSubOption extends XxxOption {\n       *     bbb: number\n       * }\n       * class XxxSubModel extends XxxModel {\n       *     static defaultOption: XxxSubOption = inheritDefaultOption(XxxModel.defaultOption, {\n       *         bbb: 456\n       *     })\n       *     fn() {\n       *         let opt = this.getDefaultOption();\n       *         // opt is {aaa: 123, bbb: 456}\n       *     }\n       * }\n       * ```\n       *\n       * (B) If using class extend (previous approach in echarts 3 & 4):\n       * ```js\n       * let XxxComponent = Component.extend({\n       *     defaultOption: {\n       *         xx: 123\n       *     }\n       * })\n       * ```\n       * ```js\n       * let XxxSubComponent = XxxComponent.extend({\n       *     defaultOption: {\n       *         yy: 456\n       *     },\n       *     fn: function () {\n       *         let opt = this.getDefaultOption();\n       *         // opt is {xx: 123, yy: 456}\n       *     }\n       * })\n       * ```\n       */\n\n\n      ComponentModel.prototype.getDefaultOption = function () {\n        var ctor = this.constructor; // If using class declaration, it is different to travel super class\n        // in legacy env and auto merge defaultOption. So if using class\n        // declaration, defaultOption should be merged manually.\n\n        if (!isExtendedClass(ctor)) {\n          // When using ts class, defaultOption must be declared as static.\n          return ctor.defaultOption;\n        } // FIXME: remove this approach?\n\n\n        var fields = inner(this);\n\n        if (!fields.defaultOption) {\n          var optList = [];\n          var clz = ctor;\n\n          while (clz) {\n            var opt = clz.prototype.defaultOption;\n            opt && optList.push(opt);\n            clz = clz.superClass;\n          }\n\n          var defaultOption = {};\n\n          for (var i = optList.length - 1; i >= 0; i--) {\n            defaultOption = merge(defaultOption, optList[i], true);\n          }\n\n          fields.defaultOption = defaultOption;\n        }\n\n        return fields.defaultOption;\n      };\n      /**\n       * Notice: always force to input param `useDefault` in case that forget to consider it.\n       * The same behavior as `modelUtil.parseFinder`.\n       *\n       * @param useDefault In many cases like series refer axis and axis refer grid,\n       *        If axis index / axis id not specified, use the first target as default.\n       *        In other cases like dataZoom refer axis, if not specified, measn no refer.\n       */\n\n\n      ComponentModel.prototype.getReferringComponents = function (mainType, opt) {\n        var indexKey = mainType + 'Index';\n        var idKey = mainType + 'Id';\n        return queryReferringComponents(this.ecModel, mainType, {\n          index: this.get(indexKey, true),\n          id: this.get(idKey, true)\n        }, opt);\n      };\n\n      ComponentModel.prototype.getBoxLayoutParams = function () {\n        // Consider itself having box layout configs.\n        var boxLayoutModel = this;\n        return {\n          left: boxLayoutModel.get('left'),\n          top: boxLayoutModel.get('top'),\n          right: boxLayoutModel.get('right'),\n          bottom: boxLayoutModel.get('bottom'),\n          width: boxLayoutModel.get('width'),\n          height: boxLayoutModel.get('height')\n        };\n      };\n      /**\n       * Get key for zlevel.\n       * If developers don't configure zlevel. We will assign zlevel to series based on the key.\n       * For example, lines with trail effect and progressive series will in an individual zlevel.\n       */\n\n\n      ComponentModel.prototype.getZLevelKey = function () {\n        return '';\n      };\n\n      ComponentModel.prototype.setZLevel = function (zlevel) {\n        this.option.zlevel = zlevel;\n      };\n\n      ComponentModel.protoInitialize = function () {\n        var proto = ComponentModel.prototype;\n        proto.type = 'component';\n        proto.id = '';\n        proto.name = '';\n        proto.mainType = '';\n        proto.subType = '';\n        proto.componentIndex = 0;\n      }();\n\n      return ComponentModel;\n    }(Model);\n\n    mountExtend(ComponentModel, Model);\n    enableClassManagement(ComponentModel);\n    enableSubTypeDefaulter(ComponentModel);\n    enableTopologicalTravel(ComponentModel, getDependencies);\n\n    function getDependencies(componentType) {\n      var deps = [];\n      each(ComponentModel.getClassesByMainType(componentType), function (clz) {\n        deps = deps.concat(clz.dependencies || clz.prototype.dependencies || []);\n      }); // Ensure main type.\n\n      deps = map(deps, function (type) {\n        return parseClassType(type).main;\n      }); // Hack dataset for convenience.\n\n      if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {\n        deps.unshift('dataset');\n      }\n\n      return deps;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var platform = ''; // Navigator not exists in node\n\n    if (typeof navigator !== 'undefined') {\n      /* global navigator */\n      platform = navigator.platform || '';\n    }\n\n    var decalColor = 'rgba(0, 0, 0, 0.2)';\n    var globalDefault = {\n      darkMode: 'auto',\n      // backgroundColor: 'rgba(0,0,0,0)',\n      colorBy: 'series',\n      color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],\n      gradientColor: ['#f6efa6', '#d88273', '#bf444c'],\n      aria: {\n        decal: {\n          decals: [{\n            color: decalColor,\n            dashArrayX: [1, 0],\n            dashArrayY: [2, 5],\n            symbolSize: 1,\n            rotation: Math.PI / 6\n          }, {\n            color: decalColor,\n            symbol: 'circle',\n            dashArrayX: [[8, 8], [0, 8, 8, 0]],\n            dashArrayY: [6, 0],\n            symbolSize: 0.8\n          }, {\n            color: decalColor,\n            dashArrayX: [1, 0],\n            dashArrayY: [4, 3],\n            rotation: -Math.PI / 4\n          }, {\n            color: decalColor,\n            dashArrayX: [[6, 6], [0, 6, 6, 0]],\n            dashArrayY: [6, 0]\n          }, {\n            color: decalColor,\n            dashArrayX: [[1, 0], [1, 6]],\n            dashArrayY: [1, 0, 6, 0],\n            rotation: Math.PI / 4\n          }, {\n            color: decalColor,\n            symbol: 'triangle',\n            dashArrayX: [[9, 9], [0, 9, 9, 0]],\n            dashArrayY: [7, 2],\n            symbolSize: 0.75\n          }]\n        }\n      },\n      // If xAxis and yAxis declared, grid is created by default.\n      // grid: {},\n      textStyle: {\n        // color: '#000',\n        // decoration: 'none',\n        // PENDING\n        fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',\n        // fontFamily: 'Arial, Verdana, sans-serif',\n        fontSize: 12,\n        fontStyle: 'normal',\n        fontWeight: 'normal'\n      },\n      // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/\n      // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n      // Default is source-over\n      blendMode: null,\n      stateAnimation: {\n        duration: 300,\n        easing: 'cubicOut'\n      },\n      animation: 'auto',\n      animationDuration: 1000,\n      animationDurationUpdate: 500,\n      animationEasing: 'cubicInOut',\n      animationEasingUpdate: 'cubicInOut',\n      animationThreshold: 2000,\n      // Configuration for progressive/incremental rendering\n      progressiveThreshold: 3000,\n      progressive: 400,\n      // Threshold of if use single hover layer to optimize.\n      // It is recommended that `hoverLayerThreshold` is equivalent to or less than\n      // `progressiveThreshold`, otherwise hover will cause restart of progressive,\n      // which is unexpected.\n      // see example <echarts/test/heatmap-large.html>.\n      hoverLayerThreshold: 3000,\n      // See: module:echarts/scale/Time\n      useUTC: false\n    };\n\n    var VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName']);\n    var SOURCE_FORMAT_ORIGINAL = 'original';\n    var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';\n    var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';\n    var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';\n    var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';\n    var SOURCE_FORMAT_UNKNOWN = 'unknown';\n    var SERIES_LAYOUT_BY_COLUMN = 'column';\n    var SERIES_LAYOUT_BY_ROW = 'row';\n\n    var BE_ORDINAL = {\n      Must: 1,\n      Might: 2,\n      Not: 3 // Other cases\n\n    };\n    var innerGlobalModel = makeInner();\n    /**\n     * MUST be called before mergeOption of all series.\n     */\n\n    function resetSourceDefaulter(ecModel) {\n      // `datasetMap` is used to make default encode.\n      innerGlobalModel(ecModel).datasetMap = createHashMap();\n    }\n    /**\n     * [The strategy of the arrengment of data dimensions for dataset]:\n     * \"value way\": all axes are non-category axes. So series one by one take\n     *     several (the number is coordSysDims.length) dimensions from dataset.\n     *     The result of data arrengment of data dimensions like:\n     *     | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y |\n     * \"category way\": at least one axis is category axis. So the the first data\n     *     dimension is always mapped to the first category axis and shared by\n     *     all of the series. The other data dimensions are taken by series like\n     *     \"value way\" does.\n     *     The result of data arrengment of data dimensions like:\n     *     | ser_shared_x | ser0_y | ser1_y | ser2_y |\n     *\n     * @return encode Never be `null/undefined`.\n     */\n\n    function makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) {\n      var encode = {};\n      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n      if (!datasetModel || !coordDimensions) {\n        return encode;\n      }\n\n      var encodeItemName = [];\n      var encodeSeriesName = [];\n      var ecModel = seriesModel.ecModel;\n      var datasetMap = innerGlobalModel(ecModel).datasetMap;\n      var key = datasetModel.uid + '_' + source.seriesLayoutBy;\n      var baseCategoryDimIndex;\n      var categoryWayValueDimStart;\n      coordDimensions = coordDimensions.slice();\n      each(coordDimensions, function (coordDimInfoLoose, coordDimIdx) {\n        var coordDimInfo = isObject(coordDimInfoLoose) ? coordDimInfoLoose : coordDimensions[coordDimIdx] = {\n          name: coordDimInfoLoose\n        };\n\n        if (coordDimInfo.type === 'ordinal' && baseCategoryDimIndex == null) {\n          baseCategoryDimIndex = coordDimIdx;\n          categoryWayValueDimStart = getDataDimCountOnCoordDim(coordDimInfo);\n        }\n\n        encode[coordDimInfo.name] = [];\n      });\n      var datasetRecord = datasetMap.get(key) || datasetMap.set(key, {\n        categoryWayDim: categoryWayValueDimStart,\n        valueWayDim: 0\n      }); // TODO\n      // Auto detect first time axis and do arrangement.\n\n      each(coordDimensions, function (coordDimInfo, coordDimIdx) {\n        var coordDimName = coordDimInfo.name;\n        var count = getDataDimCountOnCoordDim(coordDimInfo); // In value way.\n\n        if (baseCategoryDimIndex == null) {\n          var start = datasetRecord.valueWayDim;\n          pushDim(encode[coordDimName], start, count);\n          pushDim(encodeSeriesName, start, count);\n          datasetRecord.valueWayDim += count; // ??? TODO give a better default series name rule?\n          // especially when encode x y specified.\n          // consider: when multiple series share one dimension\n          // category axis, series name should better use\n          // the other dimension name. On the other hand, use\n          // both dimensions name.\n        } // In category way, the first category axis.\n        else if (baseCategoryDimIndex === coordDimIdx) {\n            pushDim(encode[coordDimName], 0, count);\n            pushDim(encodeItemName, 0, count);\n          } // In category way, the other axis.\n          else {\n              var start = datasetRecord.categoryWayDim;\n              pushDim(encode[coordDimName], start, count);\n              pushDim(encodeSeriesName, start, count);\n              datasetRecord.categoryWayDim += count;\n            }\n      });\n\n      function pushDim(dimIdxArr, idxFrom, idxCount) {\n        for (var i = 0; i < idxCount; i++) {\n          dimIdxArr.push(idxFrom + i);\n        }\n      }\n\n      function getDataDimCountOnCoordDim(coordDimInfo) {\n        var dimsDef = coordDimInfo.dimsDef;\n        return dimsDef ? dimsDef.length : 1;\n      }\n\n      encodeItemName.length && (encode.itemName = encodeItemName);\n      encodeSeriesName.length && (encode.seriesName = encodeSeriesName);\n      return encode;\n    }\n    /**\n     * Work for data like [{name: ..., value: ...}, ...].\n     *\n     * @return encode Never be `null/undefined`.\n     */\n\n    function makeSeriesEncodeForNameBased(seriesModel, source, dimCount) {\n      var encode = {};\n      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n      if (!datasetModel) {\n        return encode;\n      }\n\n      var sourceFormat = source.sourceFormat;\n      var dimensionsDefine = source.dimensionsDefine;\n      var potentialNameDimIndex;\n\n      if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        each(dimensionsDefine, function (dim, idx) {\n          if ((isObject(dim) ? dim.name : dim) === 'name') {\n            potentialNameDimIndex = idx;\n          }\n        });\n      }\n\n      var idxResult = function () {\n        var idxRes0 = {};\n        var idxRes1 = {};\n        var guessRecords = []; // 5 is an experience value.\n\n        for (var i = 0, len = Math.min(5, dimCount); i < len; i++) {\n          var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i);\n          guessRecords.push(guessResult);\n          var isPureNumber = guessResult === BE_ORDINAL.Not; // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim,\n          // and then find a name dim with the priority:\n          // \"BE_ORDINAL.Might|BE_ORDINAL.Must\" > \"other dim\" > \"the value dim itself\".\n\n          if (isPureNumber && idxRes0.v == null && i !== potentialNameDimIndex) {\n            idxRes0.v = i;\n          }\n\n          if (idxRes0.n == null || idxRes0.n === idxRes0.v || !isPureNumber && guessRecords[idxRes0.n] === BE_ORDINAL.Not) {\n            idxRes0.n = i;\n          }\n\n          if (fulfilled(idxRes0) && guessRecords[idxRes0.n] !== BE_ORDINAL.Not) {\n            return idxRes0;\n          } // [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not),\n          // find the first BE_ORDINAL.Might as the value dim,\n          // and then find a name dim with the priority:\n          // \"other dim\" > \"the value dim itself\".\n          // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be\n          // treated as number.\n\n\n          if (!isPureNumber) {\n            if (guessResult === BE_ORDINAL.Might && idxRes1.v == null && i !== potentialNameDimIndex) {\n              idxRes1.v = i;\n            }\n\n            if (idxRes1.n == null || idxRes1.n === idxRes1.v) {\n              idxRes1.n = i;\n            }\n          }\n        }\n\n        function fulfilled(idxResult) {\n          return idxResult.v != null && idxResult.n != null;\n        }\n\n        return fulfilled(idxRes0) ? idxRes0 : fulfilled(idxRes1) ? idxRes1 : null;\n      }();\n\n      if (idxResult) {\n        encode.value = [idxResult.v]; // `potentialNameDimIndex` has highest priority.\n\n        var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; // By default, label uses itemName in charts.\n        // So we don't set encodeLabel here.\n\n        encode.itemName = [nameDimIndex];\n        encode.seriesName = [nameDimIndex];\n      }\n\n      return encode;\n    }\n    /**\n     * @return If return null/undefined, indicate that should not use datasetModel.\n     */\n\n    function querySeriesUpstreamDatasetModel(seriesModel) {\n      // Caution: consider the scenario:\n      // A dataset is declared and a series is not expected to use the dataset,\n      // and at the beginning `setOption({series: { noData })` (just prepare other\n      // option but no data), then `setOption({series: {data: [...]}); In this case,\n      // the user should set an empty array to avoid that dataset is used by default.\n      var thisData = seriesModel.get('data', true);\n\n      if (!thisData) {\n        return queryReferringComponents(seriesModel.ecModel, 'dataset', {\n          index: seriesModel.get('datasetIndex', true),\n          id: seriesModel.get('datasetId', true)\n        }, SINGLE_REFERRING).models[0];\n      }\n    }\n    /**\n     * @return Always return an array event empty.\n     */\n\n    function queryDatasetUpstreamDatasetModels(datasetModel) {\n      // Only these attributes declared, we by defualt reference to `datasetIndex: 0`.\n      // Otherwise, no reference.\n      if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true)) {\n        return [];\n      }\n\n      return queryReferringComponents(datasetModel.ecModel, 'dataset', {\n        index: datasetModel.get('fromDatasetIndex', true),\n        id: datasetModel.get('fromDatasetId', true)\n      }, SINGLE_REFERRING).models;\n    }\n    /**\n     * The rule should not be complex, otherwise user might not\n     * be able to known where the data is wrong.\n     * The code is ugly, but how to make it neat?\n     */\n\n    function guessOrdinal(source, dimIndex) {\n      return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex);\n    } // dimIndex may be overflow source data.\n    // return {BE_ORDINAL}\n\n    function doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) {\n      var result; // Experience value.\n\n      var maxLoop = 5;\n\n      if (isTypedArray(data)) {\n        return BE_ORDINAL.Not;\n      } // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine\n      // always exists in source.\n\n\n      var dimName;\n      var dimType;\n\n      if (dimensionsDefine) {\n        var dimDefItem = dimensionsDefine[dimIndex];\n\n        if (isObject(dimDefItem)) {\n          dimName = dimDefItem.name;\n          dimType = dimDefItem.type;\n        } else if (isString(dimDefItem)) {\n          dimName = dimDefItem;\n        }\n      }\n\n      if (dimType != null) {\n        return dimType === 'ordinal' ? BE_ORDINAL.Must : BE_ORDINAL.Not;\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var dataArrayRows = data;\n\n        if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n          var sample = dataArrayRows[dimIndex];\n\n          for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {\n            if ((result = detectValue(sample[startIndex + i])) != null) {\n              return result;\n            }\n          }\n        } else {\n          for (var i = 0; i < dataArrayRows.length && i < maxLoop; i++) {\n            var row = dataArrayRows[startIndex + i];\n\n            if (row && (result = detectValue(row[dimIndex])) != null) {\n              return result;\n            }\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        var dataObjectRows = data;\n\n        if (!dimName) {\n          return BE_ORDINAL.Not;\n        }\n\n        for (var i = 0; i < dataObjectRows.length && i < maxLoop; i++) {\n          var item = dataObjectRows[i];\n\n          if (item && (result = detectValue(item[dimName])) != null) {\n            return result;\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        var dataKeyedColumns = data;\n\n        if (!dimName) {\n          return BE_ORDINAL.Not;\n        }\n\n        var sample = dataKeyedColumns[dimName];\n\n        if (!sample || isTypedArray(sample)) {\n          return BE_ORDINAL.Not;\n        }\n\n        for (var i = 0; i < sample.length && i < maxLoop; i++) {\n          if ((result = detectValue(sample[i])) != null) {\n            return result;\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var dataOriginal = data;\n\n        for (var i = 0; i < dataOriginal.length && i < maxLoop; i++) {\n          var item = dataOriginal[i];\n          var val = getDataItemValue(item);\n\n          if (!isArray(val)) {\n            return BE_ORDINAL.Not;\n          }\n\n          if ((result = detectValue(val[dimIndex])) != null) {\n            return result;\n          }\n        }\n      }\n\n      function detectValue(val) {\n        var beStr = isString(val); // Consider usage convenience, '1', '2' will be treated as \"number\".\n        // `isFinit('')` get `true`.\n\n        if (val != null && isFinite(val) && val !== '') {\n          return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not;\n        } else if (beStr && val !== '-') {\n          return BE_ORDINAL.Must;\n        }\n      }\n\n      return BE_ORDINAL.Not;\n    }\n\n    var internalOptionCreatorMap = createHashMap();\n    function registerInternalOptionCreator(mainType, creator) {\n      assert(internalOptionCreatorMap.get(mainType) == null && creator);\n      internalOptionCreatorMap.set(mainType, creator);\n    }\n    function concatInternalOptions(ecModel, mainType, newCmptOptionList) {\n      var internalOptionCreator = internalOptionCreatorMap.get(mainType);\n\n      if (!internalOptionCreator) {\n        return newCmptOptionList;\n      }\n\n      var internalOptions = internalOptionCreator(ecModel);\n\n      if (!internalOptions) {\n        return newCmptOptionList;\n      }\n\n      if (\"development\" !== 'production') {\n        for (var i = 0; i < internalOptions.length; i++) {\n          assert(isComponentIdInternal(internalOptions[i]));\n        }\n      }\n\n      return newCmptOptionList.concat(internalOptions);\n    }\n\n    var innerColor = makeInner();\n    var innerDecal = makeInner();\n\n    var PaletteMixin =\n    /** @class */\n    function () {\n      function PaletteMixin() {}\n\n      PaletteMixin.prototype.getColorFromPalette = function (name, scope, requestNum) {\n        var defaultPalette = normalizeToArray(this.get('color', true));\n        var layeredPalette = this.get('colorLayer', true);\n        return getFromPalette(this, innerColor, defaultPalette, layeredPalette, name, scope, requestNum);\n      };\n\n      PaletteMixin.prototype.clearColorPalette = function () {\n        clearPalette(this, innerColor);\n      };\n\n      return PaletteMixin;\n    }();\n\n    function getDecalFromPalette(ecModel, name, scope, requestNum) {\n      var defaultDecals = normalizeToArray(ecModel.get(['aria', 'decal', 'decals']));\n      return getFromPalette(ecModel, innerDecal, defaultDecals, null, name, scope, requestNum);\n    }\n\n    function getNearestPalette(palettes, requestColorNum) {\n      var paletteNum = palettes.length; // TODO palettes must be in order\n\n      for (var i = 0; i < paletteNum; i++) {\n        if (palettes[i].length > requestColorNum) {\n          return palettes[i];\n        }\n      }\n\n      return palettes[paletteNum - 1];\n    }\n    /**\n     * @param name MUST NOT be null/undefined. Otherwise call this function\n     *             twise with the same parameters will get different result.\n     * @param scope default this.\n     * @return Can be null/undefined\n     */\n\n\n    function getFromPalette(that, inner, defaultPalette, layeredPalette, name, scope, requestNum) {\n      scope = scope || that;\n      var scopeFields = inner(scope);\n      var paletteIdx = scopeFields.paletteIdx || 0;\n      var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; // Use `hasOwnProperty` to avoid conflict with Object.prototype.\n\n      if (paletteNameMap.hasOwnProperty(name)) {\n        return paletteNameMap[name];\n      }\n\n      var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum); // In case can't find in layered color palette.\n\n      palette = palette || defaultPalette;\n\n      if (!palette || !palette.length) {\n        return;\n      }\n\n      var pickedPaletteItem = palette[paletteIdx];\n\n      if (name) {\n        paletteNameMap[name] = pickedPaletteItem;\n      }\n\n      scopeFields.paletteIdx = (paletteIdx + 1) % palette.length;\n      return pickedPaletteItem;\n    }\n\n    function clearPalette(that, inner) {\n      inner(that).paletteIdx = 0;\n      inner(that).paletteNameMap = {};\n    }\n\n    // Internal method names:\n    // -----------------------\n\n    var reCreateSeriesIndices;\n    var assertSeriesInitialized;\n    var initBase;\n    var OPTION_INNER_KEY = '\\0_ec_inner';\n    var OPTION_INNER_VALUE = 1;\n    var BUITIN_COMPONENTS_MAP = {\n      grid: 'GridComponent',\n      polar: 'PolarComponent',\n      geo: 'GeoComponent',\n      singleAxis: 'SingleAxisComponent',\n      parallel: 'ParallelComponent',\n      calendar: 'CalendarComponent',\n      graphic: 'GraphicComponent',\n      toolbox: 'ToolboxComponent',\n      tooltip: 'TooltipComponent',\n      axisPointer: 'AxisPointerComponent',\n      brush: 'BrushComponent',\n      title: 'TitleComponent',\n      timeline: 'TimelineComponent',\n      markPoint: 'MarkPointComponent',\n      markLine: 'MarkLineComponent',\n      markArea: 'MarkAreaComponent',\n      legend: 'LegendComponent',\n      dataZoom: 'DataZoomComponent',\n      visualMap: 'VisualMapComponent',\n      // aria: 'AriaComponent',\n      // dataset: 'DatasetComponent',\n      // Dependencies\n      xAxis: 'GridComponent',\n      yAxis: 'GridComponent',\n      angleAxis: 'PolarComponent',\n      radiusAxis: 'PolarComponent'\n    };\n    var BUILTIN_CHARTS_MAP = {\n      line: 'LineChart',\n      bar: 'BarChart',\n      pie: 'PieChart',\n      scatter: 'ScatterChart',\n      radar: 'RadarChart',\n      map: 'MapChart',\n      tree: 'TreeChart',\n      treemap: 'TreemapChart',\n      graph: 'GraphChart',\n      gauge: 'GaugeChart',\n      funnel: 'FunnelChart',\n      parallel: 'ParallelChart',\n      sankey: 'SankeyChart',\n      boxplot: 'BoxplotChart',\n      candlestick: 'CandlestickChart',\n      effectScatter: 'EffectScatterChart',\n      lines: 'LinesChart',\n      heatmap: 'HeatmapChart',\n      pictorialBar: 'PictorialBarChart',\n      themeRiver: 'ThemeRiverChart',\n      sunburst: 'SunburstChart',\n      custom: 'CustomChart'\n    };\n    var componetsMissingLogPrinted = {};\n\n    function checkMissingComponents(option) {\n      each(option, function (componentOption, mainType) {\n        if (!ComponentModel.hasClass(mainType)) {\n          var componentImportName = BUITIN_COMPONENTS_MAP[mainType];\n\n          if (componentImportName && !componetsMissingLogPrinted[componentImportName]) {\n            error(\"Component \" + mainType + \" is used but not imported.\\nimport { \" + componentImportName + \" } from 'echarts/components';\\necharts.use([\" + componentImportName + \"]);\");\n            componetsMissingLogPrinted[componentImportName] = true;\n          }\n        }\n      });\n    }\n\n    var GlobalModel =\n    /** @class */\n    function (_super) {\n      __extends(GlobalModel, _super);\n\n      function GlobalModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) {\n        theme = theme || {};\n        this.option = null; // Mark as not initialized.\n\n        this._theme = new Model(theme);\n        this._locale = new Model(locale);\n        this._optionManager = optionManager;\n      };\n\n      GlobalModel.prototype.setOption = function (option, opts, optionPreprocessorFuncs) {\n        if (\"development\" !== 'production') {\n          assert(option != null, 'option is null/undefined');\n          assert(option[OPTION_INNER_KEY] !== OPTION_INNER_VALUE, 'please use chart.getOption()');\n        }\n\n        var innerOpt = normalizeSetOptionInput(opts);\n\n        this._optionManager.setOption(option, optionPreprocessorFuncs, innerOpt);\n\n        this._resetOption(null, innerOpt);\n      };\n      /**\n       * @param type null/undefined: reset all.\n       *        'recreate': force recreate all.\n       *        'timeline': only reset timeline option\n       *        'media': only reset media query option\n       * @return Whether option changed.\n       */\n\n\n      GlobalModel.prototype.resetOption = function (type, opt) {\n        return this._resetOption(type, normalizeSetOptionInput(opt));\n      };\n\n      GlobalModel.prototype._resetOption = function (type, opt) {\n        var optionChanged = false;\n        var optionManager = this._optionManager;\n\n        if (!type || type === 'recreate') {\n          var baseOption = optionManager.mountOption(type === 'recreate');\n\n          if (\"development\" !== 'production') {\n            checkMissingComponents(baseOption);\n          }\n\n          if (!this.option || type === 'recreate') {\n            initBase(this, baseOption);\n          } else {\n            this.restoreData();\n\n            this._mergeOption(baseOption, opt);\n          }\n\n          optionChanged = true;\n        }\n\n        if (type === 'timeline' || type === 'media') {\n          this.restoreData();\n        } // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`,\n        // it should better not have the same props with `MediaUnit['option']`.\n        // Because either `option2` or `MediaUnit['option']` will be always merged to \"current option\"\n        // rather than original \"baseOption\". If they both override a prop, the result might be\n        // unexpected when media state changed after `setOption` called.\n        // If we really need to modify a props in each `MediaUnit['option']`, use the full version\n        // (`{baseOption, media}`) in `setOption`.\n        // For `timeline`, the case is the same.\n\n\n        if (!type || type === 'recreate' || type === 'timeline') {\n          var timelineOption = optionManager.getTimelineOption(this);\n\n          if (timelineOption) {\n            optionChanged = true;\n\n            this._mergeOption(timelineOption, opt);\n          }\n        }\n\n        if (!type || type === 'recreate' || type === 'media') {\n          var mediaOptions = optionManager.getMediaOption(this);\n\n          if (mediaOptions.length) {\n            each(mediaOptions, function (mediaOption) {\n              optionChanged = true;\n\n              this._mergeOption(mediaOption, opt);\n            }, this);\n          }\n        }\n\n        return optionChanged;\n      };\n\n      GlobalModel.prototype.mergeOption = function (option) {\n        this._mergeOption(option, null);\n      };\n\n      GlobalModel.prototype._mergeOption = function (newOption, opt) {\n        var option = this.option;\n        var componentsMap = this._componentsMap;\n        var componentsCount = this._componentsCount;\n        var newCmptTypes = [];\n        var newCmptTypeMap = createHashMap();\n        var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap;\n        resetSourceDefaulter(this); // If no component class, merge directly.\n        // For example: color, animaiton options, etc.\n\n        each(newOption, function (componentOption, mainType) {\n          if (componentOption == null) {\n            return;\n          }\n\n          if (!ComponentModel.hasClass(mainType)) {\n            // globalSettingTask.dirty();\n            option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true);\n          } else if (mainType) {\n            newCmptTypes.push(mainType);\n            newCmptTypeMap.set(mainType, true);\n          }\n        });\n\n        if (replaceMergeMainTypeMap) {\n          // If there is a mainType `xxx` in `replaceMerge` but not declared in option,\n          // we trade it as it is declared in option as `{xxx: []}`. Because:\n          // (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`.\n          // (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`.\n          replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) {\n            if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) {\n              newCmptTypes.push(mainTypeInReplaceMerge);\n              newCmptTypeMap.set(mainTypeInReplaceMerge, true);\n            }\n          });\n        }\n\n        ComponentModel.topologicalTravel(newCmptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this);\n\n        function visitComponent(mainType) {\n          var newCmptOptionList = concatInternalOptions(this, mainType, normalizeToArray(newOption[mainType]));\n          var oldCmptList = componentsMap.get(mainType);\n          var mergeMode = // `!oldCmptList` means init. See the comment in `mappingToExists`\n          !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge';\n          var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode); // Set mainType and complete subType.\n\n          setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); // Empty it before the travel, in order to prevent `this._componentsMap`\n          // from being used in the `init`/`mergeOption`/`optionUpdated` of some\n          // components, which is probably incorrect logic.\n\n          option[mainType] = null;\n          componentsMap.set(mainType, null);\n          componentsCount.set(mainType, 0);\n          var optionsByMainType = [];\n          var cmptsByMainType = [];\n          var cmptsCountByMainType = 0;\n          var tooltipExists;\n          var tooltipWarningLogged;\n          each(mappingResult, function (resultItem, index) {\n            var componentModel = resultItem.existing;\n            var newCmptOption = resultItem.newOption;\n\n            if (!newCmptOption) {\n              if (componentModel) {\n                // Consider where is no new option and should be merged using {},\n                // see removeEdgeAndAdd in topologicalTravel and\n                // ComponentModel.getAllClassMainTypes.\n                componentModel.mergeOption({}, this);\n                componentModel.optionUpdated({}, false);\n              } // If no both `resultItem.exist` and `resultItem.option`,\n              // either it is in `replaceMerge` and not matched by any id,\n              // or it has been removed in previous `replaceMerge` and left a \"hole\" in this component index.\n\n            } else {\n              var isSeriesType = mainType === 'series';\n              var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, !isSeriesType // Give a more detailed warn later if series don't exists\n              );\n\n              if (!ComponentModelClass) {\n                if (\"development\" !== 'production') {\n                  var subType = resultItem.keyInfo.subType;\n                  var seriesImportName = BUILTIN_CHARTS_MAP[subType];\n\n                  if (!componetsMissingLogPrinted[subType]) {\n                    componetsMissingLogPrinted[subType] = true;\n\n                    if (seriesImportName) {\n                      error(\"Series \" + subType + \" is used but not imported.\\nimport { \" + seriesImportName + \" } from 'echarts/charts';\\necharts.use([\" + seriesImportName + \"]);\");\n                    } else {\n                      error(\"Unknown series \" + subType);\n                    }\n                  }\n                }\n\n                return;\n              } // TODO Before multiple tooltips get supported, we do this check to avoid unexpected exception.\n\n\n              if (mainType === 'tooltip') {\n                if (tooltipExists) {\n                  if (\"development\" !== 'production') {\n                    if (!tooltipWarningLogged) {\n                      warn('Currently only one tooltip component is allowed.');\n                      tooltipWarningLogged = true;\n                    }\n                  }\n\n                  return;\n                }\n\n                tooltipExists = true;\n              }\n\n              if (componentModel && componentModel.constructor === ComponentModelClass) {\n                componentModel.name = resultItem.keyInfo.name; // componentModel.settingTask && componentModel.settingTask.dirty();\n\n                componentModel.mergeOption(newCmptOption, this);\n                componentModel.optionUpdated(newCmptOption, false);\n              } else {\n                // PENDING Global as parent ?\n                var extraOpt = extend({\n                  componentIndex: index\n                }, resultItem.keyInfo);\n                componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); // Assign `keyInfo`\n\n                extend(componentModel, extraOpt);\n\n                if (resultItem.brandNew) {\n                  componentModel.__requireNewView = true;\n                }\n\n                componentModel.init(newCmptOption, this, this); // Call optionUpdated after init.\n                // newCmptOption has been used as componentModel.option\n                // and may be merged with theme and default, so pass null\n                // to avoid confusion.\n\n                componentModel.optionUpdated(null, true);\n              }\n            }\n\n            if (componentModel) {\n              optionsByMainType.push(componentModel.option);\n              cmptsByMainType.push(componentModel);\n              cmptsCountByMainType++;\n            } else {\n              // Always do assign to avoid elided item in array.\n              optionsByMainType.push(void 0);\n              cmptsByMainType.push(void 0);\n            }\n          }, this);\n          option[mainType] = optionsByMainType;\n          componentsMap.set(mainType, cmptsByMainType);\n          componentsCount.set(mainType, cmptsCountByMainType); // Backup series for filtering.\n\n          if (mainType === 'series') {\n            reCreateSeriesIndices(this);\n          }\n        } // If no series declared, ensure `_seriesIndices` initialized.\n\n\n        if (!this._seriesIndices) {\n          reCreateSeriesIndices(this);\n        }\n      };\n      /**\n       * Get option for output (cloned option and inner info removed)\n       */\n\n\n      GlobalModel.prototype.getOption = function () {\n        var option = clone(this.option);\n        each(option, function (optInMainType, mainType) {\n          if (ComponentModel.hasClass(mainType)) {\n            var opts = normalizeToArray(optInMainType); // Inner cmpts need to be removed.\n            // Inner cmpts might not be at last since ec5.0, but still\n            // compatible for users: if inner cmpt at last, splice the returned array.\n\n            var realLen = opts.length;\n            var metNonInner = false;\n\n            for (var i = realLen - 1; i >= 0; i--) {\n              // Remove options with inner id.\n              if (opts[i] && !isComponentIdInternal(opts[i])) {\n                metNonInner = true;\n              } else {\n                opts[i] = null;\n                !metNonInner && realLen--;\n              }\n            }\n\n            opts.length = realLen;\n            option[mainType] = opts;\n          }\n        });\n        delete option[OPTION_INNER_KEY];\n        return option;\n      };\n\n      GlobalModel.prototype.getTheme = function () {\n        return this._theme;\n      };\n\n      GlobalModel.prototype.getLocaleModel = function () {\n        return this._locale;\n      };\n\n      GlobalModel.prototype.setUpdatePayload = function (payload) {\n        this._payload = payload;\n      };\n\n      GlobalModel.prototype.getUpdatePayload = function () {\n        return this._payload;\n      };\n      /**\n       * @param idx If not specified, return the first one.\n       */\n\n\n      GlobalModel.prototype.getComponent = function (mainType, idx) {\n        var list = this._componentsMap.get(mainType);\n\n        if (list) {\n          var cmpt = list[idx || 0];\n\n          if (cmpt) {\n            return cmpt;\n          } else if (idx == null) {\n            for (var i = 0; i < list.length; i++) {\n              if (list[i]) {\n                return list[i];\n              }\n            }\n          }\n        }\n      };\n      /**\n       * @return Never be null/undefined.\n       */\n\n\n      GlobalModel.prototype.queryComponents = function (condition) {\n        var mainType = condition.mainType;\n\n        if (!mainType) {\n          return [];\n        }\n\n        var index = condition.index;\n        var id = condition.id;\n        var name = condition.name;\n\n        var cmpts = this._componentsMap.get(mainType);\n\n        if (!cmpts || !cmpts.length) {\n          return [];\n        }\n\n        var result;\n\n        if (index != null) {\n          result = [];\n          each(normalizeToArray(index), function (idx) {\n            cmpts[idx] && result.push(cmpts[idx]);\n          });\n        } else if (id != null) {\n          result = queryByIdOrName('id', id, cmpts);\n        } else if (name != null) {\n          result = queryByIdOrName('name', name, cmpts);\n        } else {\n          // Return all non-empty components in that mainType\n          result = filter(cmpts, function (cmpt) {\n            return !!cmpt;\n          });\n        }\n\n        return filterBySubType(result, condition);\n      };\n      /**\n       * The interface is different from queryComponents,\n       * which is convenient for inner usage.\n       *\n       * @usage\n       * let result = findComponents(\n       *     {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}\n       * );\n       * let result = findComponents(\n       *     {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}\n       * );\n       * let result = findComponents(\n       *     {mainType: 'series',\n       *     filter: function (model, index) {...}}\n       * );\n       * // result like [component0, componnet1, ...]\n       */\n\n\n      GlobalModel.prototype.findComponents = function (condition) {\n        var query = condition.query;\n        var mainType = condition.mainType;\n        var queryCond = getQueryCond(query);\n        var result = queryCond ? this.queryComponents(queryCond) // Retrieve all non-empty components.\n        : filter(this._componentsMap.get(mainType), function (cmpt) {\n          return !!cmpt;\n        });\n        return doFilter(filterBySubType(result, condition));\n\n        function getQueryCond(q) {\n          var indexAttr = mainType + 'Index';\n          var idAttr = mainType + 'Id';\n          var nameAttr = mainType + 'Name';\n          return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? {\n            mainType: mainType,\n            // subType will be filtered finally.\n            index: q[indexAttr],\n            id: q[idAttr],\n            name: q[nameAttr]\n          } : null;\n        }\n\n        function doFilter(res) {\n          return condition.filter ? filter(res, condition.filter) : res;\n        }\n      };\n\n      GlobalModel.prototype.eachComponent = function (mainType, cb, context) {\n        var componentsMap = this._componentsMap;\n\n        if (isFunction(mainType)) {\n          var ctxForAll_1 = cb;\n          var cbForAll_1 = mainType;\n          componentsMap.each(function (cmpts, componentType) {\n            for (var i = 0; cmpts && i < cmpts.length; i++) {\n              var cmpt = cmpts[i];\n              cmpt && cbForAll_1.call(ctxForAll_1, componentType, cmpt, cmpt.componentIndex);\n            }\n          });\n        } else {\n          var cmpts = isString(mainType) ? componentsMap.get(mainType) : isObject(mainType) ? this.findComponents(mainType) : null;\n\n          for (var i = 0; cmpts && i < cmpts.length; i++) {\n            var cmpt = cmpts[i];\n            cmpt && cb.call(context, cmpt, cmpt.componentIndex);\n          }\n        }\n      };\n      /**\n       * Get series list before filtered by name.\n       */\n\n\n      GlobalModel.prototype.getSeriesByName = function (name) {\n        var nameStr = convertOptionIdName(name, null);\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries && nameStr != null && oneSeries.name === nameStr;\n        });\n      };\n      /**\n       * Get series list before filtered by index.\n       */\n\n\n      GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) {\n        return this._componentsMap.get('series')[seriesIndex];\n      };\n      /**\n       * Get series list before filtered by type.\n       * FIXME: rename to getRawSeriesByType?\n       */\n\n\n      GlobalModel.prototype.getSeriesByType = function (subType) {\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries && oneSeries.subType === subType;\n        });\n      };\n      /**\n       * Get all series before filtered.\n       */\n\n\n      GlobalModel.prototype.getSeries = function () {\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries;\n        });\n      };\n      /**\n       * Count series before filtered.\n       */\n\n\n      GlobalModel.prototype.getSeriesCount = function () {\n        return this._componentsCount.get('series');\n      };\n      /**\n       * After filtering, series may be different\n       * from raw series.\n       */\n\n\n      GlobalModel.prototype.eachSeries = function (cb, context) {\n        assertSeriesInitialized(this);\n        each(this._seriesIndices, function (rawSeriesIndex) {\n          var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n          cb.call(context, series, rawSeriesIndex);\n        }, this);\n      };\n      /**\n       * Iterate raw series before filtered.\n       *\n       * @param {Function} cb\n       * @param {*} context\n       */\n\n\n      GlobalModel.prototype.eachRawSeries = function (cb, context) {\n        each(this._componentsMap.get('series'), function (series) {\n          series && cb.call(context, series, series.componentIndex);\n        });\n      };\n      /**\n       * After filtering, series may be different.\n       * from raw series.\n       */\n\n\n      GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) {\n        assertSeriesInitialized(this);\n        each(this._seriesIndices, function (rawSeriesIndex) {\n          var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n          if (series.subType === subType) {\n            cb.call(context, series, rawSeriesIndex);\n          }\n        }, this);\n      };\n      /**\n       * Iterate raw series before filtered of given type.\n       */\n\n\n      GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) {\n        return each(this.getSeriesByType(subType), cb, context);\n      };\n\n      GlobalModel.prototype.isSeriesFiltered = function (seriesModel) {\n        assertSeriesInitialized(this);\n        return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;\n      };\n\n      GlobalModel.prototype.getCurrentSeriesIndices = function () {\n        return (this._seriesIndices || []).slice();\n      };\n\n      GlobalModel.prototype.filterSeries = function (cb, context) {\n        assertSeriesInitialized(this);\n        var newSeriesIndices = [];\n        each(this._seriesIndices, function (seriesRawIdx) {\n          var series = this._componentsMap.get('series')[seriesRawIdx];\n\n          cb.call(context, series, seriesRawIdx) && newSeriesIndices.push(seriesRawIdx);\n        }, this);\n        this._seriesIndices = newSeriesIndices;\n        this._seriesIndicesMap = createHashMap(newSeriesIndices);\n      };\n\n      GlobalModel.prototype.restoreData = function (payload) {\n        reCreateSeriesIndices(this);\n        var componentsMap = this._componentsMap;\n        var componentTypes = [];\n        componentsMap.each(function (components, componentType) {\n          if (ComponentModel.hasClass(componentType)) {\n            componentTypes.push(componentType);\n          }\n        });\n        ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType) {\n          each(componentsMap.get(componentType), function (component) {\n            if (component && (componentType !== 'series' || !isNotTargetSeries(component, payload))) {\n              component.restoreData();\n            }\n          });\n        });\n      };\n\n      GlobalModel.internalField = function () {\n        reCreateSeriesIndices = function (ecModel) {\n          var seriesIndices = ecModel._seriesIndices = [];\n          each(ecModel._componentsMap.get('series'), function (series) {\n            // series may have been removed by `replaceMerge`.\n            series && seriesIndices.push(series.componentIndex);\n          });\n          ecModel._seriesIndicesMap = createHashMap(seriesIndices);\n        };\n\n        assertSeriesInitialized = function (ecModel) {\n          // Components that use _seriesIndices should depends on series component,\n          // which make sure that their initialization is after series.\n          if (\"development\" !== 'production') {\n            if (!ecModel._seriesIndices) {\n              throw new Error('Option should contains series.');\n            }\n          }\n        };\n\n        initBase = function (ecModel, baseOption) {\n          // Using OPTION_INNER_KEY to mark that this option cannot be used outside,\n          // i.e. `chart.setOption(chart.getModel().option);` is forbidden.\n          ecModel.option = {};\n          ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; // Init with series: [], in case of calling findSeries method\n          // before series initialized.\n\n          ecModel._componentsMap = createHashMap({\n            series: []\n          });\n          ecModel._componentsCount = createHashMap(); // If user spefied `option.aria`, aria will be enable. This detection should be\n          // performed before theme and globalDefault merge.\n\n          var airaOption = baseOption.aria;\n\n          if (isObject(airaOption) && airaOption.enabled == null) {\n            airaOption.enabled = true;\n          }\n\n          mergeTheme(baseOption, ecModel._theme.option); // TODO Needs clone when merging to the unexisted property\n\n          merge(baseOption, globalDefault, false);\n\n          ecModel._mergeOption(baseOption, null);\n        };\n      }();\n\n      return GlobalModel;\n    }(Model);\n\n    function isNotTargetSeries(seriesModel, payload) {\n      if (payload) {\n        var index = payload.seriesIndex;\n        var id = payload.seriesId;\n        var name_1 = payload.seriesName;\n        return index != null && seriesModel.componentIndex !== index || id != null && seriesModel.id !== id || name_1 != null && seriesModel.name !== name_1;\n      }\n    }\n\n    function mergeTheme(option, theme) {\n      // PENDING\n      // NOT use `colorLayer` in theme if option has `color`\n      var notMergeColorLayer = option.color && !option.colorLayer;\n      each(theme, function (themeItem, name) {\n        if (name === 'colorLayer' && notMergeColorLayer) {\n          return;\n        } // If it is component model mainType, the model handles that merge later.\n        // otherwise, merge them here.\n\n\n        if (!ComponentModel.hasClass(name)) {\n          if (typeof themeItem === 'object') {\n            option[name] = !option[name] ? clone(themeItem) : merge(option[name], themeItem, false);\n          } else {\n            if (option[name] == null) {\n              option[name] = themeItem;\n            }\n          }\n        }\n      });\n    }\n\n    function queryByIdOrName(attr, idOrName, cmpts) {\n      // Here is a break from echarts4: string and number are\n      // treated as equal.\n      if (isArray(idOrName)) {\n        var keyMap_1 = createHashMap();\n        each(idOrName, function (idOrNameItem) {\n          if (idOrNameItem != null) {\n            var idName = convertOptionIdName(idOrNameItem, null);\n            idName != null && keyMap_1.set(idOrNameItem, true);\n          }\n        });\n        return filter(cmpts, function (cmpt) {\n          return cmpt && keyMap_1.get(cmpt[attr]);\n        });\n      } else {\n        var idName_1 = convertOptionIdName(idOrName, null);\n        return filter(cmpts, function (cmpt) {\n          return cmpt && idName_1 != null && cmpt[attr] === idName_1;\n        });\n      }\n    }\n\n    function filterBySubType(components, condition) {\n      // Using hasOwnProperty for restrict. Consider\n      // subType is undefined in user payload.\n      return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) {\n        return cmpt && cmpt.subType === condition.subType;\n      }) : components;\n    }\n\n    function normalizeSetOptionInput(opts) {\n      var replaceMergeMainTypeMap = createHashMap();\n      opts && each(normalizeToArray(opts.replaceMerge), function (mainType) {\n        if (\"development\" !== 'production') {\n          assert(ComponentModel.hasClass(mainType), '\"' + mainType + '\" is not valid component main type in \"replaceMerge\"');\n        }\n\n        replaceMergeMainTypeMap.set(mainType, true);\n      });\n      return {\n        replaceMergeMainTypeMap: replaceMergeMainTypeMap\n      };\n    }\n\n    mixin(GlobalModel, PaletteMixin);\n\n    var availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isSSR', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', // 'getModel',\n    'getOption', // 'getViewOfComponentModel',\n    // 'getViewOfSeriesModel',\n    'getId', 'updateLabelLayout'];\n\n    var ExtensionAPI =\n    /** @class */\n    function () {\n      function ExtensionAPI(ecInstance) {\n        each(availableMethods, function (methodName) {\n          this[methodName] = bind(ecInstance[methodName], ecInstance);\n        }, this);\n      }\n\n      return ExtensionAPI;\n    }();\n\n    var coordinateSystemCreators = {};\n\n    var CoordinateSystemManager =\n    /** @class */\n    function () {\n      function CoordinateSystemManager() {\n        this._coordinateSystems = [];\n      }\n\n      CoordinateSystemManager.prototype.create = function (ecModel, api) {\n        var coordinateSystems = [];\n        each(coordinateSystemCreators, function (creator, type) {\n          var list = creator.create(ecModel, api);\n          coordinateSystems = coordinateSystems.concat(list || []);\n        });\n        this._coordinateSystems = coordinateSystems;\n      };\n\n      CoordinateSystemManager.prototype.update = function (ecModel, api) {\n        each(this._coordinateSystems, function (coordSys) {\n          coordSys.update && coordSys.update(ecModel, api);\n        });\n      };\n\n      CoordinateSystemManager.prototype.getCoordinateSystems = function () {\n        return this._coordinateSystems.slice();\n      };\n\n      CoordinateSystemManager.register = function (type, creator) {\n        coordinateSystemCreators[type] = creator;\n      };\n\n      CoordinateSystemManager.get = function (type) {\n        return coordinateSystemCreators[type];\n      };\n\n      return CoordinateSystemManager;\n    }();\n\n    var QUERY_REG = /^(min|max)?(.+)$/; // Key: mainType\n    // type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>;\n\n    /**\n     * TERM EXPLANATIONS:\n     * See `ECOption` and `ECUnitOption` in `src/util/types.ts`.\n     */\n\n    var OptionManager =\n    /** @class */\n    function () {\n      // timeline.notMerge is not supported in ec3. Firstly there is rearly\n      // case that notMerge is needed. Secondly supporting 'notMerge' requires\n      // rawOption cloned and backuped when timeline changed, which does no\n      // good to performance. What's more, that both timeline and setOption\n      // method supply 'notMerge' brings complex and some problems.\n      // Consider this case:\n      // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);\n      // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);\n      function OptionManager(api) {\n        this._timelineOptions = [];\n        this._mediaList = [];\n        /**\n         * -1, means default.\n         * empty means no media.\n         */\n\n        this._currentMediaIndices = [];\n        this._api = api;\n      }\n\n      OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) {\n        if (rawOption) {\n          // That set dat primitive is dangerous if user reuse the data when setOption again.\n          each(normalizeToArray(rawOption.series), function (series) {\n            series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);\n          });\n          each(normalizeToArray(rawOption.dataset), function (dataset) {\n            dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source);\n          });\n        } // Caution: some series modify option data, if do not clone,\n        // it should ensure that the repeat modify correctly\n        // (create a new object when modify itself).\n\n\n        rawOption = clone(rawOption); // FIXME\n        // If some property is set in timeline options or media option but\n        // not set in baseOption, a warning should be given.\n\n        var optionBackup = this._optionBackup;\n        var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup);\n        this._newBaseOption = newParsedOption.baseOption; // For setOption at second time (using merge mode);\n\n        if (optionBackup) {\n          // FIXME\n          // the restore merge solution is essentially incorrect.\n          // the mapping can not be 100% consistent with ecModel, which probably brings\n          // potential bug!\n          // The first merge is delayed, because in most cases, users do not call `setOption` twice.\n          // let fakeCmptsMap = this._fakeCmptsMap;\n          // if (!fakeCmptsMap) {\n          //     fakeCmptsMap = this._fakeCmptsMap = createHashMap();\n          //     mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null);\n          // }\n          // mergeToBackupOption(\n          //     fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt\n          // );\n          // For simplicity, timeline options and media options do not support merge,\n          // that is, if you `setOption` twice and both has timeline options, the latter\n          // timeline options will not be merged to the former, but just substitute them.\n          if (newParsedOption.timelineOptions.length) {\n            optionBackup.timelineOptions = newParsedOption.timelineOptions;\n          }\n\n          if (newParsedOption.mediaList.length) {\n            optionBackup.mediaList = newParsedOption.mediaList;\n          }\n\n          if (newParsedOption.mediaDefault) {\n            optionBackup.mediaDefault = newParsedOption.mediaDefault;\n          }\n        } else {\n          this._optionBackup = newParsedOption;\n        }\n      };\n\n      OptionManager.prototype.mountOption = function (isRecreate) {\n        var optionBackup = this._optionBackup;\n        this._timelineOptions = optionBackup.timelineOptions;\n        this._mediaList = optionBackup.mediaList;\n        this._mediaDefault = optionBackup.mediaDefault;\n        this._currentMediaIndices = [];\n        return clone(isRecreate // this._optionBackup.baseOption, which is created at the first `setOption`\n        // called, and is merged into every new option by inner method `mergeToBackupOption`\n        // each time `setOption` called, can be only used in `isRecreate`, because\n        // its reliability is under suspicion. In other cases option merge is\n        // performed by `model.mergeOption`.\n        ? optionBackup.baseOption : this._newBaseOption);\n      };\n\n      OptionManager.prototype.getTimelineOption = function (ecModel) {\n        var option;\n        var timelineOptions = this._timelineOptions;\n\n        if (timelineOptions.length) {\n          // getTimelineOption can only be called after ecModel inited,\n          // so we can get currentIndex from timelineModel.\n          var timelineModel = ecModel.getComponent('timeline');\n\n          if (timelineModel) {\n            option = clone( // FIXME:TS as TimelineModel or quivlant interface\n            timelineOptions[timelineModel.getCurrentIndex()]);\n          }\n        }\n\n        return option;\n      };\n\n      OptionManager.prototype.getMediaOption = function (ecModel) {\n        var ecWidth = this._api.getWidth();\n\n        var ecHeight = this._api.getHeight();\n\n        var mediaList = this._mediaList;\n        var mediaDefault = this._mediaDefault;\n        var indices = [];\n        var result = []; // No media defined.\n\n        if (!mediaList.length && !mediaDefault) {\n          return result;\n        } // Multi media may be applied, the latter defined media has higher priority.\n\n\n        for (var i = 0, len = mediaList.length; i < len; i++) {\n          if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {\n            indices.push(i);\n          }\n        } // FIXME\n        // Whether mediaDefault should force users to provide? Otherwise\n        // the change by media query can not be recorvered.\n\n\n        if (!indices.length && mediaDefault) {\n          indices = [-1];\n        }\n\n        if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {\n          result = map(indices, function (index) {\n            return clone(index === -1 ? mediaDefault.option : mediaList[index].option);\n          });\n        } // Otherwise return nothing.\n\n\n        this._currentMediaIndices = indices;\n        return result;\n      };\n\n      return OptionManager;\n    }();\n    /**\n     * [RAW_OPTION_PATTERNS]\n     * (Note: \"series: []\" represents all other props in `ECUnitOption`)\n     *\n     * (1) No prop \"baseOption\" declared:\n     * Root option is used as \"baseOption\" (except prop \"options\" and \"media\").\n     * ```js\n     * option = {\n     *     series: [],\n     *     timeline: {},\n     *     options: [],\n     * };\n     * option = {\n     *     series: [],\n     *     media: {},\n     * };\n     * option = {\n     *     series: [],\n     *     timeline: {},\n     *     options: [],\n     *     media: {},\n     * }\n     * ```\n     *\n     * (2) Prop \"baseOption\" declared:\n     * If \"baseOption\" declared, `ECUnitOption` props can only be declared\n     * inside \"baseOption\" except prop \"timeline\" (compat ec2).\n     * ```js\n     * option = {\n     *     baseOption: {\n     *         timeline: {},\n     *         series: [],\n     *     },\n     *     options: []\n     * };\n     * option = {\n     *     baseOption: {\n     *         series: [],\n     *     },\n     *     media: []\n     * };\n     * option = {\n     *     baseOption: {\n     *         timeline: {},\n     *         series: [],\n     *     },\n     *     options: []\n     *     media: []\n     * };\n     * option = {\n     *     // ec3 compat ec2: allow (only) `timeline` declared\n     *     // outside baseOption. Keep this setting for compat.\n     *     timeline: {},\n     *     baseOption: {\n     *         series: [],\n     *     },\n     *     options: [],\n     *     media: []\n     * };\n     * ```\n     */\n\n\n    function parseRawOption( // `rawOption` May be modified\n    rawOption, optionPreprocessorFuncs, isNew) {\n      var mediaList = [];\n      var mediaDefault;\n      var baseOption;\n      var declaredBaseOption = rawOption.baseOption; // Compatible with ec2, [RAW_OPTION_PATTERNS] above.\n\n      var timelineOnRoot = rawOption.timeline;\n      var timelineOptionsOnRoot = rawOption.options;\n      var mediaOnRoot = rawOption.media;\n      var hasMedia = !!rawOption.media;\n      var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline);\n\n      if (declaredBaseOption) {\n        baseOption = declaredBaseOption; // For merge option.\n\n        if (!baseOption.timeline) {\n          baseOption.timeline = timelineOnRoot;\n        }\n      } // For convenience, enable to use the root option as the `baseOption`:\n      // `{ ...normalOptionProps, media: [{ ... }, { ... }] }`\n      else {\n          if (hasTimeline || hasMedia) {\n            rawOption.options = rawOption.media = null;\n          }\n\n          baseOption = rawOption;\n        }\n\n      if (hasMedia) {\n        if (isArray(mediaOnRoot)) {\n          each(mediaOnRoot, function (singleMedia) {\n            if (\"development\" !== 'production') {\n              // Real case of wrong config.\n              if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) {\n                error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }');\n              }\n            }\n\n            if (singleMedia && singleMedia.option) {\n              if (singleMedia.query) {\n                mediaList.push(singleMedia);\n              } else if (!mediaDefault) {\n                // Use the first media default.\n                mediaDefault = singleMedia;\n              }\n            }\n          });\n        } else {\n          if (\"development\" !== 'production') {\n            // Real case of wrong config.\n            error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }');\n          }\n        }\n      }\n\n      doPreprocess(baseOption);\n      each(timelineOptionsOnRoot, function (option) {\n        return doPreprocess(option);\n      });\n      each(mediaList, function (media) {\n        return doPreprocess(media.option);\n      });\n\n      function doPreprocess(option) {\n        each(optionPreprocessorFuncs, function (preProcess) {\n          preProcess(option, isNew);\n        });\n      }\n\n      return {\n        baseOption: baseOption,\n        timelineOptions: timelineOptionsOnRoot || [],\n        mediaDefault: mediaDefault,\n        mediaList: mediaList\n      };\n    }\n    /**\n     * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>\n     * Support: width, height, aspectRatio\n     * Can use max or min as prefix.\n     */\n\n\n    function applyMediaQuery(query, ecWidth, ecHeight) {\n      var realMap = {\n        width: ecWidth,\n        height: ecHeight,\n        aspectratio: ecWidth / ecHeight // lower case for convenience.\n\n      };\n      var applicable = true;\n      each(query, function (value, attr) {\n        var matched = attr.match(QUERY_REG);\n\n        if (!matched || !matched[1] || !matched[2]) {\n          return;\n        }\n\n        var operator = matched[1];\n        var realAttr = matched[2].toLowerCase();\n\n        if (!compare(realMap[realAttr], value, operator)) {\n          applicable = false;\n        }\n      });\n      return applicable;\n    }\n\n    function compare(real, expect, operator) {\n      if (operator === 'min') {\n        return real >= expect;\n      } else if (operator === 'max') {\n        return real <= expect;\n      } else {\n        // Equals\n        return real === expect;\n      }\n    }\n\n    function indicesEquals(indices1, indices2) {\n      // indices is always order by asc and has only finite number.\n      return indices1.join(',') === indices2.join(',');\n    }\n\n    var each$2 = each;\n    var isObject$1 = isObject;\n    var POSSIBLE_STYLES = ['areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', 'chordStyle', 'label', 'labelLine'];\n\n    function compatEC2ItemStyle(opt) {\n      var itemStyleOpt = opt && opt.itemStyle;\n\n      if (!itemStyleOpt) {\n        return;\n      }\n\n      for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {\n        var styleName = POSSIBLE_STYLES[i];\n        var normalItemStyleOpt = itemStyleOpt.normal;\n        var emphasisItemStyleOpt = itemStyleOpt.emphasis;\n\n        if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(\"itemStyle.normal.\" + styleName, styleName);\n          }\n\n          opt[styleName] = opt[styleName] || {};\n\n          if (!opt[styleName].normal) {\n            opt[styleName].normal = normalItemStyleOpt[styleName];\n          } else {\n            merge(opt[styleName].normal, normalItemStyleOpt[styleName]);\n          }\n\n          normalItemStyleOpt[styleName] = null;\n        }\n\n        if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(\"itemStyle.emphasis.\" + styleName, \"emphasis.\" + styleName);\n          }\n\n          opt[styleName] = opt[styleName] || {};\n\n          if (!opt[styleName].emphasis) {\n            opt[styleName].emphasis = emphasisItemStyleOpt[styleName];\n          } else {\n            merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);\n          }\n\n          emphasisItemStyleOpt[styleName] = null;\n        }\n      }\n    }\n\n    function convertNormalEmphasis(opt, optType, useExtend) {\n      if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) {\n        var normalOpt = opt[optType].normal;\n        var emphasisOpt = opt[optType].emphasis;\n\n        if (normalOpt) {\n          if (\"development\" !== 'production') {\n            // eslint-disable-next-line max-len\n            deprecateLog(\"'normal' hierarchy in \" + optType + \" has been removed since 4.0. All style properties are configured in \" + optType + \" directly now.\");\n          } // Timeline controlStyle has other properties besides normal and emphasis\n\n\n          if (useExtend) {\n            opt[optType].normal = opt[optType].emphasis = null;\n            defaults(opt[optType], normalOpt);\n          } else {\n            opt[optType] = normalOpt;\n          }\n        }\n\n        if (emphasisOpt) {\n          if (\"development\" !== 'production') {\n            deprecateLog(optType + \".emphasis has been changed to emphasis.\" + optType + \" since 4.0\");\n          }\n\n          opt.emphasis = opt.emphasis || {};\n          opt.emphasis[optType] = emphasisOpt; // Also compat the case user mix the style and focus together in ec3 style\n          // for example: { itemStyle: { normal: {}, emphasis: {focus, shadowBlur} } }\n\n          if (emphasisOpt.focus) {\n            opt.emphasis.focus = emphasisOpt.focus;\n          }\n\n          if (emphasisOpt.blurScope) {\n            opt.emphasis.blurScope = emphasisOpt.blurScope;\n          }\n        }\n      }\n    }\n\n    function removeEC3NormalStatus(opt) {\n      convertNormalEmphasis(opt, 'itemStyle');\n      convertNormalEmphasis(opt, 'lineStyle');\n      convertNormalEmphasis(opt, 'areaStyle');\n      convertNormalEmphasis(opt, 'label');\n      convertNormalEmphasis(opt, 'labelLine'); // treemap\n\n      convertNormalEmphasis(opt, 'upperLabel'); // graph\n\n      convertNormalEmphasis(opt, 'edgeLabel');\n    }\n\n    function compatTextStyle(opt, propName) {\n      // Check whether is not object (string\\null\\undefined ...)\n      var labelOptSingle = isObject$1(opt) && opt[propName];\n      var textStyle = isObject$1(labelOptSingle) && labelOptSingle.textStyle;\n\n      if (textStyle) {\n        if (\"development\" !== 'production') {\n          // eslint-disable-next-line max-len\n          deprecateLog(\"textStyle hierarchy in \" + propName + \" has been removed since 4.0. All textStyle properties are configured in \" + propName + \" directly now.\");\n        }\n\n        for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {\n          var textPropName = TEXT_STYLE_OPTIONS[i];\n\n          if (textStyle.hasOwnProperty(textPropName)) {\n            labelOptSingle[textPropName] = textStyle[textPropName];\n          }\n        }\n      }\n    }\n\n    function compatEC3CommonStyles(opt) {\n      if (opt) {\n        removeEC3NormalStatus(opt);\n        compatTextStyle(opt, 'label');\n        opt.emphasis && compatTextStyle(opt.emphasis, 'label');\n      }\n    }\n\n    function processSeries(seriesOpt) {\n      if (!isObject$1(seriesOpt)) {\n        return;\n      }\n\n      compatEC2ItemStyle(seriesOpt);\n      removeEC3NormalStatus(seriesOpt);\n      compatTextStyle(seriesOpt, 'label'); // treemap\n\n      compatTextStyle(seriesOpt, 'upperLabel'); // graph\n\n      compatTextStyle(seriesOpt, 'edgeLabel');\n\n      if (seriesOpt.emphasis) {\n        compatTextStyle(seriesOpt.emphasis, 'label'); // treemap\n\n        compatTextStyle(seriesOpt.emphasis, 'upperLabel'); // graph\n\n        compatTextStyle(seriesOpt.emphasis, 'edgeLabel');\n      }\n\n      var markPoint = seriesOpt.markPoint;\n\n      if (markPoint) {\n        compatEC2ItemStyle(markPoint);\n        compatEC3CommonStyles(markPoint);\n      }\n\n      var markLine = seriesOpt.markLine;\n\n      if (markLine) {\n        compatEC2ItemStyle(markLine);\n        compatEC3CommonStyles(markLine);\n      }\n\n      var markArea = seriesOpt.markArea;\n\n      if (markArea) {\n        compatEC3CommonStyles(markArea);\n      }\n\n      var data = seriesOpt.data; // Break with ec3: if `setOption` again, there may be no `type` in option,\n      // then the backward compat based on option type will not be performed.\n\n      if (seriesOpt.type === 'graph') {\n        data = data || seriesOpt.nodes;\n        var edgeData = seriesOpt.links || seriesOpt.edges;\n\n        if (edgeData && !isTypedArray(edgeData)) {\n          for (var i = 0; i < edgeData.length; i++) {\n            compatEC3CommonStyles(edgeData[i]);\n          }\n        }\n\n        each(seriesOpt.categories, function (opt) {\n          removeEC3NormalStatus(opt);\n        });\n      }\n\n      if (data && !isTypedArray(data)) {\n        for (var i = 0; i < data.length; i++) {\n          compatEC3CommonStyles(data[i]);\n        }\n      } // mark point data\n\n\n      markPoint = seriesOpt.markPoint;\n\n      if (markPoint && markPoint.data) {\n        var mpData = markPoint.data;\n\n        for (var i = 0; i < mpData.length; i++) {\n          compatEC3CommonStyles(mpData[i]);\n        }\n      } // mark line data\n\n\n      markLine = seriesOpt.markLine;\n\n      if (markLine && markLine.data) {\n        var mlData = markLine.data;\n\n        for (var i = 0; i < mlData.length; i++) {\n          if (isArray(mlData[i])) {\n            compatEC3CommonStyles(mlData[i][0]);\n            compatEC3CommonStyles(mlData[i][1]);\n          } else {\n            compatEC3CommonStyles(mlData[i]);\n          }\n        }\n      } // Series\n\n\n      if (seriesOpt.type === 'gauge') {\n        compatTextStyle(seriesOpt, 'axisLabel');\n        compatTextStyle(seriesOpt, 'title');\n        compatTextStyle(seriesOpt, 'detail');\n      } else if (seriesOpt.type === 'treemap') {\n        convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle');\n        each(seriesOpt.levels, function (opt) {\n          removeEC3NormalStatus(opt);\n        });\n      } else if (seriesOpt.type === 'tree') {\n        removeEC3NormalStatus(seriesOpt.leaves);\n      } // sunburst starts from ec4, so it does not need to compat levels.\n\n    }\n\n    function toArr(o) {\n      return isArray(o) ? o : o ? [o] : [];\n    }\n\n    function toObj(o) {\n      return (isArray(o) ? o[0] : o) || {};\n    }\n\n    function globalCompatStyle(option, isTheme) {\n      each$2(toArr(option.series), function (seriesOpt) {\n        isObject$1(seriesOpt) && processSeries(seriesOpt);\n      });\n      var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];\n      isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');\n      each$2(axes, function (axisName) {\n        each$2(toArr(option[axisName]), function (axisOpt) {\n          if (axisOpt) {\n            compatTextStyle(axisOpt, 'axisLabel');\n            compatTextStyle(axisOpt.axisPointer, 'label');\n          }\n        });\n      });\n      each$2(toArr(option.parallel), function (parallelOpt) {\n        var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;\n        compatTextStyle(parallelAxisDefault, 'axisLabel');\n        compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');\n      });\n      each$2(toArr(option.calendar), function (calendarOpt) {\n        convertNormalEmphasis(calendarOpt, 'itemStyle');\n        compatTextStyle(calendarOpt, 'dayLabel');\n        compatTextStyle(calendarOpt, 'monthLabel');\n        compatTextStyle(calendarOpt, 'yearLabel');\n      }); // radar.name.textStyle\n\n      each$2(toArr(option.radar), function (radarOpt) {\n        compatTextStyle(radarOpt, 'name'); // Use axisName instead of name because component has name property\n\n        if (radarOpt.name && radarOpt.axisName == null) {\n          radarOpt.axisName = radarOpt.name;\n          delete radarOpt.name;\n\n          if (\"development\" !== 'production') {\n            deprecateLog('name property in radar component has been changed to axisName');\n          }\n        }\n\n        if (radarOpt.nameGap != null && radarOpt.axisNameGap == null) {\n          radarOpt.axisNameGap = radarOpt.nameGap;\n          delete radarOpt.nameGap;\n\n          if (\"development\" !== 'production') {\n            deprecateLog('nameGap property in radar component has been changed to axisNameGap');\n          }\n        }\n\n        if (\"development\" !== 'production') {\n          each$2(radarOpt.indicator, function (indicatorOpt) {\n            if (indicatorOpt.text) {\n              deprecateReplaceLog('text', 'name', 'radar.indicator');\n            }\n          });\n        }\n      });\n      each$2(toArr(option.geo), function (geoOpt) {\n        if (isObject$1(geoOpt)) {\n          compatEC3CommonStyles(geoOpt);\n          each$2(toArr(geoOpt.regions), function (regionObj) {\n            compatEC3CommonStyles(regionObj);\n          });\n        }\n      });\n      each$2(toArr(option.timeline), function (timelineOpt) {\n        compatEC3CommonStyles(timelineOpt);\n        convertNormalEmphasis(timelineOpt, 'label');\n        convertNormalEmphasis(timelineOpt, 'itemStyle');\n        convertNormalEmphasis(timelineOpt, 'controlStyle', true);\n        var data = timelineOpt.data;\n        isArray(data) && each(data, function (item) {\n          if (isObject(item)) {\n            convertNormalEmphasis(item, 'label');\n            convertNormalEmphasis(item, 'itemStyle');\n          }\n        });\n      });\n      each$2(toArr(option.toolbox), function (toolboxOpt) {\n        convertNormalEmphasis(toolboxOpt, 'iconStyle');\n        each$2(toolboxOpt.feature, function (featureOpt) {\n          convertNormalEmphasis(featureOpt, 'iconStyle');\n        });\n      });\n      compatTextStyle(toObj(option.axisPointer), 'label');\n      compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); // Clean logs\n      // storedLogs = {};\n    }\n\n    function get(opt, path) {\n      var pathArr = path.split(',');\n      var obj = opt;\n\n      for (var i = 0; i < pathArr.length; i++) {\n        obj = obj && obj[pathArr[i]];\n\n        if (obj == null) {\n          break;\n        }\n      }\n\n      return obj;\n    }\n\n    function set$1(opt, path, val, overwrite) {\n      var pathArr = path.split(',');\n      var obj = opt;\n      var key;\n      var i = 0;\n\n      for (; i < pathArr.length - 1; i++) {\n        key = pathArr[i];\n\n        if (obj[key] == null) {\n          obj[key] = {};\n        }\n\n        obj = obj[key];\n      }\n\n      if (overwrite || obj[pathArr[i]] == null) {\n        obj[pathArr[i]] = val;\n      }\n    }\n\n    function compatLayoutProperties(option) {\n      option && each(LAYOUT_PROPERTIES, function (prop) {\n        if (prop[0] in option && !(prop[1] in option)) {\n          option[prop[1]] = option[prop[0]];\n        }\n      });\n    }\n\n    var LAYOUT_PROPERTIES = [['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']];\n    var COMPATITABLE_COMPONENTS = ['grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'];\n    var BAR_ITEM_STYLE_MAP = [['borderRadius', 'barBorderRadius'], ['borderColor', 'barBorderColor'], ['borderWidth', 'barBorderWidth']];\n\n    function compatBarItemStyle(option) {\n      var itemStyle = option && option.itemStyle;\n\n      if (itemStyle) {\n        for (var i = 0; i < BAR_ITEM_STYLE_MAP.length; i++) {\n          var oldName = BAR_ITEM_STYLE_MAP[i][1];\n          var newName = BAR_ITEM_STYLE_MAP[i][0];\n\n          if (itemStyle[oldName] != null) {\n            itemStyle[newName] = itemStyle[oldName];\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog(oldName, newName);\n            }\n          }\n        }\n      }\n    }\n\n    function compatPieLabel(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.alignTo === 'edge' && option.margin != null && option.edgeDistance == null) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('label.margin', 'label.edgeDistance', 'pie');\n        }\n\n        option.edgeDistance = option.margin;\n      }\n    }\n\n    function compatSunburstState(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.downplay && !option.blur) {\n        option.blur = option.downplay;\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('downplay', 'blur', 'sunburst');\n        }\n      }\n    }\n\n    function compatGraphFocus(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.focusNodeAdjacency != null) {\n        option.emphasis = option.emphasis || {};\n\n        if (option.emphasis.focus == null) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog('focusNodeAdjacency', 'emphasis: { focus: \\'adjacency\\'}', 'graph/sankey');\n          }\n\n          option.emphasis.focus = 'adjacency';\n        }\n      }\n    }\n\n    function traverseTree(data, cb) {\n      if (data) {\n        for (var i = 0; i < data.length; i++) {\n          cb(data[i]);\n          data[i] && traverseTree(data[i].children, cb);\n        }\n      }\n    }\n\n    function globalBackwardCompat(option, isTheme) {\n      globalCompatStyle(option, isTheme); // Make sure series array for model initialization.\n\n      option.series = normalizeToArray(option.series);\n      each(option.series, function (seriesOpt) {\n        if (!isObject(seriesOpt)) {\n          return;\n        }\n\n        var seriesType = seriesOpt.type;\n\n        if (seriesType === 'line') {\n          if (seriesOpt.clipOverflow != null) {\n            seriesOpt.clip = seriesOpt.clipOverflow;\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('clipOverflow', 'clip', 'line');\n            }\n          }\n        } else if (seriesType === 'pie' || seriesType === 'gauge') {\n          if (seriesOpt.clockWise != null) {\n            seriesOpt.clockwise = seriesOpt.clockWise;\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('clockWise', 'clockwise');\n            }\n          }\n\n          compatPieLabel(seriesOpt.label);\n          var data = seriesOpt.data;\n\n          if (data && !isTypedArray(data)) {\n            for (var i = 0; i < data.length; i++) {\n              compatPieLabel(data[i]);\n            }\n          }\n\n          if (seriesOpt.hoverOffset != null) {\n            seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n            if (seriesOpt.emphasis.scaleSize = null) {\n              if (\"development\" !== 'production') {\n                deprecateReplaceLog('hoverOffset', 'emphasis.scaleSize');\n              }\n\n              seriesOpt.emphasis.scaleSize = seriesOpt.hoverOffset;\n            }\n          }\n        } else if (seriesType === 'gauge') {\n          var pointerColor = get(seriesOpt, 'pointer.color');\n          pointerColor != null && set$1(seriesOpt, 'itemStyle.color', pointerColor);\n        } else if (seriesType === 'bar') {\n          compatBarItemStyle(seriesOpt);\n          compatBarItemStyle(seriesOpt.backgroundStyle);\n          compatBarItemStyle(seriesOpt.emphasis);\n          var data = seriesOpt.data;\n\n          if (data && !isTypedArray(data)) {\n            for (var i = 0; i < data.length; i++) {\n              if (typeof data[i] === 'object') {\n                compatBarItemStyle(data[i]);\n                compatBarItemStyle(data[i] && data[i].emphasis);\n              }\n            }\n          }\n        } else if (seriesType === 'sunburst') {\n          var highlightPolicy = seriesOpt.highlightPolicy;\n\n          if (highlightPolicy) {\n            seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n            if (!seriesOpt.emphasis.focus) {\n              seriesOpt.emphasis.focus = highlightPolicy;\n\n              if (\"development\" !== 'production') {\n                deprecateReplaceLog('highlightPolicy', 'emphasis.focus', 'sunburst');\n              }\n            }\n          }\n\n          compatSunburstState(seriesOpt);\n          traverseTree(seriesOpt.data, compatSunburstState);\n        } else if (seriesType === 'graph' || seriesType === 'sankey') {\n          compatGraphFocus(seriesOpt); // TODO nodes, edges?\n        } else if (seriesType === 'map') {\n          if (seriesOpt.mapType && !seriesOpt.map) {\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('mapType', 'map', 'map');\n            }\n\n            seriesOpt.map = seriesOpt.mapType;\n          }\n\n          if (seriesOpt.mapLocation) {\n            if (\"development\" !== 'production') {\n              deprecateLog('`mapLocation` is not used anymore.');\n            }\n\n            defaults(seriesOpt, seriesOpt.mapLocation);\n          }\n        }\n\n        if (seriesOpt.hoverAnimation != null) {\n          seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n          if (seriesOpt.emphasis && seriesOpt.emphasis.scale == null) {\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('hoverAnimation', 'emphasis.scale');\n            }\n\n            seriesOpt.emphasis.scale = seriesOpt.hoverAnimation;\n          }\n        }\n\n        compatLayoutProperties(seriesOpt);\n      }); // dataRange has changed to visualMap\n\n      if (option.dataRange) {\n        option.visualMap = option.dataRange;\n      }\n\n      each(COMPATITABLE_COMPONENTS, function (componentName) {\n        var options = option[componentName];\n\n        if (options) {\n          if (!isArray(options)) {\n            options = [options];\n          }\n\n          each(options, function (option) {\n            compatLayoutProperties(option);\n          });\n        }\n      });\n    }\n\n    //     data processing stage is blocked in stream.\n    //     See <module:echarts/stream/Scheduler#performDataProcessorTasks>\n    // (2) Only register once when import repeatedly.\n    //     Should be executed after series is filtered and before stack calculation.\n\n    function dataStack(ecModel) {\n      var stackInfoMap = createHashMap();\n      ecModel.eachSeries(function (seriesModel) {\n        var stack = seriesModel.get('stack'); // Compatible: when `stack` is set as '', do not stack.\n\n        if (stack) {\n          var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []);\n          var data = seriesModel.getData();\n          var stackInfo = {\n            // Used for calculate axis extent automatically.\n            // TODO: Type getCalculationInfo return more specific type?\n            stackResultDimension: data.getCalculationInfo('stackResultDimension'),\n            stackedOverDimension: data.getCalculationInfo('stackedOverDimension'),\n            stackedDimension: data.getCalculationInfo('stackedDimension'),\n            stackedByDimension: data.getCalculationInfo('stackedByDimension'),\n            isStackedByIndex: data.getCalculationInfo('isStackedByIndex'),\n            data: data,\n            seriesModel: seriesModel\n          }; // If stacked on axis that do not support data stack.\n\n          if (!stackInfo.stackedDimension || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)) {\n            return;\n          }\n\n          stackInfoList.length && data.setCalculationInfo('stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel);\n          stackInfoList.push(stackInfo);\n        }\n      });\n      stackInfoMap.each(calculateStack);\n    }\n\n    function calculateStack(stackInfoList) {\n      each(stackInfoList, function (targetStackInfo, idxInStack) {\n        var resultVal = [];\n        var resultNaN = [NaN, NaN];\n        var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension];\n        var targetData = targetStackInfo.data;\n        var isStackedByIndex = targetStackInfo.isStackedByIndex;\n        var stackStrategy = targetStackInfo.seriesModel.get('stackStrategy') || 'samesign'; // Should not write on raw data, because stack series model list changes\n        // depending on legend selection.\n\n        targetData.modify(dims, function (v0, v1, dataIndex) {\n          var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); // Consider `connectNulls` of line area, if value is NaN, stackedOver\n          // should also be NaN, to draw a appropriate belt area.\n\n          if (isNaN(sum)) {\n            return resultNaN;\n          }\n\n          var byValue;\n          var stackedDataRawIndex;\n\n          if (isStackedByIndex) {\n            stackedDataRawIndex = targetData.getRawIndex(dataIndex);\n          } else {\n            byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex);\n          } // If stackOver is NaN, chart view will render point on value start.\n\n\n          var stackedOver = NaN;\n\n          for (var j = idxInStack - 1; j >= 0; j--) {\n            var stackInfo = stackInfoList[j]; // Has been optimized by inverted indices on `stackedByDimension`.\n\n            if (!isStackedByIndex) {\n              stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue);\n            }\n\n            if (stackedDataRawIndex >= 0) {\n              var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); // Considering positive stack, negative stack and empty data\n\n              if (stackStrategy === 'all' // single stack group\n              || stackStrategy === 'positive' && val > 0 || stackStrategy === 'negative' && val < 0 || stackStrategy === 'samesign' && sum >= 0 && val > 0 // All positive stack\n              || stackStrategy === 'samesign' && sum <= 0 && val < 0 // All negative stack\n              ) {\n                  // The sum has to be very small to be affected by the\n                  // floating arithmetic problem. An incorrect result will probably\n                  // cause axis min/max to be filtered incorrectly.\n                  sum = addSafe(sum, val);\n                  stackedOver = val;\n                  break;\n                }\n            }\n          }\n\n          resultVal[0] = sum;\n          resultVal[1] = stackedOver;\n          return resultVal;\n        });\n      });\n    }\n\n    var SourceImpl =\n    /** @class */\n    function () {\n      function SourceImpl(fields) {\n        this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []);\n        this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; // Visit config\n\n        this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;\n        this.startIndex = fields.startIndex || 0;\n        this.dimensionsDetectedCount = fields.dimensionsDetectedCount;\n        this.metaRawOption = fields.metaRawOption;\n        var dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine;\n\n        if (dimensionsDefine) {\n          for (var i = 0; i < dimensionsDefine.length; i++) {\n            var dim = dimensionsDefine[i];\n\n            if (dim.type == null) {\n              if (guessOrdinal(this, i) === BE_ORDINAL.Must) {\n                dim.type = 'ordinal';\n              }\n            }\n          }\n        }\n      }\n\n      return SourceImpl;\n    }();\n\n    function isSourceInstance(val) {\n      return val instanceof SourceImpl;\n    }\n    /**\n     * Create a source from option.\n     * NOTE: Created source is immutable. Don't change any properties in it.\n     */\n\n    function createSource(sourceData, thisMetaRawOption, // can be null. If not provided, auto detect it from `sourceData`.\n    sourceFormat) {\n      sourceFormat = sourceFormat || detectSourceFormat(sourceData);\n      var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy;\n      var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions);\n      var source = new SourceImpl({\n        data: sourceData,\n        sourceFormat: sourceFormat,\n        seriesLayoutBy: seriesLayoutBy,\n        dimensionsDefine: determined.dimensionsDefine,\n        startIndex: determined.startIndex,\n        dimensionsDetectedCount: determined.dimensionsDetectedCount,\n        metaRawOption: clone(thisMetaRawOption)\n      });\n      return source;\n    }\n    /**\n     * Wrap original series data for some compatibility cases.\n     */\n\n    function createSourceFromSeriesDataOption(data) {\n      return new SourceImpl({\n        data: data,\n        sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL\n      });\n    }\n    /**\n     * Clone source but excludes source data.\n     */\n\n    function cloneSourceShallow(source) {\n      return new SourceImpl({\n        data: source.data,\n        sourceFormat: source.sourceFormat,\n        seriesLayoutBy: source.seriesLayoutBy,\n        dimensionsDefine: clone(source.dimensionsDefine),\n        startIndex: source.startIndex,\n        dimensionsDetectedCount: source.dimensionsDetectedCount\n      });\n    }\n    /**\n     * Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`.\n     */\n\n    function detectSourceFormat(data) {\n      var sourceFormat = SOURCE_FORMAT_UNKNOWN;\n\n      if (isTypedArray(data)) {\n        sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;\n      } else if (isArray(data)) {\n        // FIXME Whether tolerate null in top level array?\n        if (data.length === 0) {\n          sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n        }\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          var item = data[i];\n\n          if (item == null) {\n            continue;\n          } else if (isArray(item)) {\n            sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n            break;\n          } else if (isObject(item)) {\n            sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;\n            break;\n          }\n        }\n      } else if (isObject(data)) {\n        for (var key in data) {\n          if (hasOwn(data, key) && isArrayLike(data[key])) {\n            sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;\n            break;\n          }\n        }\n      }\n\n      return sourceFormat;\n    }\n    /**\n     * Determine the source definitions from data standalone dimensions definitions\n     * are not specified.\n     */\n\n    function determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, // standalone raw dimensions definition, like:\n    // {\n    //     dimensions: ['aa', 'bb', { name: 'cc', type: 'time' }]\n    // }\n    // in `dataset` or `series`\n    dimensionsDefine) {\n      var dimensionsDetectedCount;\n      var startIndex; // PENDING: Could data be null/undefined here?\n      // currently, if `dataset.source` not specified, error thrown.\n      // if `series.data` not specified, nothing rendered without error thrown.\n      // Should test these cases.\n\n      if (!data) {\n        return {\n          dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n          startIndex: startIndex,\n          dimensionsDetectedCount: dimensionsDetectedCount\n        };\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var dataArrayRows = data; // Rule: Most of the first line are string: it is header.\n        // Caution: consider a line with 5 string and 1 number,\n        // it still can not be sure it is a head, because the\n        // 5 string may be 5 values of category columns.\n\n        if (sourceHeader === 'auto' || sourceHeader == null) {\n          arrayRowsTravelFirst(function (val) {\n            // '-' is regarded as null/undefined.\n            if (val != null && val !== '-') {\n              if (isString(val)) {\n                startIndex == null && (startIndex = 1);\n              } else {\n                startIndex = 0;\n              }\n            } // 10 is an experience number, avoid long loop.\n\n          }, seriesLayoutBy, dataArrayRows, 10);\n        } else {\n          startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0;\n        }\n\n        if (!dimensionsDefine && startIndex === 1) {\n          dimensionsDefine = [];\n          arrayRowsTravelFirst(function (val, index) {\n            dimensionsDefine[index] = val != null ? val + '' : '';\n          }, seriesLayoutBy, dataArrayRows, Infinity);\n        }\n\n        dimensionsDetectedCount = dimensionsDefine ? dimensionsDefine.length : seriesLayoutBy === SERIES_LAYOUT_BY_ROW ? dataArrayRows.length : dataArrayRows[0] ? dataArrayRows[0].length : null;\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        if (!dimensionsDefine) {\n          dimensionsDefine = objectRowsCollectDimensions(data);\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        if (!dimensionsDefine) {\n          dimensionsDefine = [];\n          each(data, function (colArr, key) {\n            dimensionsDefine.push(key);\n          });\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var value0 = getDataItemValue(data[0]);\n        dimensionsDetectedCount = isArray(value0) && value0.length || 1;\n      } else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n        if (\"development\" !== 'production') {\n          assert(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.');\n        }\n      }\n\n      return {\n        startIndex: startIndex,\n        dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n        dimensionsDetectedCount: dimensionsDetectedCount\n      };\n    }\n\n    function objectRowsCollectDimensions(data) {\n      var firstIndex = 0;\n      var obj;\n\n      while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line\n\n\n      if (obj) {\n        var dimensions_1 = [];\n        each(obj, function (value, key) {\n          dimensions_1.push(key);\n        });\n        return dimensions_1;\n      }\n    } // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],\n    // which is reasonable. But dimension name is duplicated.\n    // Returns undefined or an array contains only object without null/undefined or string.\n\n\n    function normalizeDimensionsOption(dimensionsDefine) {\n      if (!dimensionsDefine) {\n        // The meaning of null/undefined is different from empty array.\n        return;\n      }\n\n      var nameMap = createHashMap();\n      return map(dimensionsDefine, function (rawItem, index) {\n        rawItem = isObject(rawItem) ? rawItem : {\n          name: rawItem\n        }; // Other fields will be discarded.\n\n        var item = {\n          name: rawItem.name,\n          displayName: rawItem.displayName,\n          type: rawItem.type\n        }; // User can set null in dimensions.\n        // We don't auto specify name, otherwise a given name may\n        // cause it to be referred unexpectedly.\n\n        if (item.name == null) {\n          return item;\n        } // Also consider number form like 2012.\n\n\n        item.name += ''; // User may also specify displayName.\n        // displayName will always exists except user not\n        // specified or dim name is not specified or detected.\n        // (A auto generated dim name will not be used as\n        // displayName).\n\n        if (item.displayName == null) {\n          item.displayName = item.name;\n        }\n\n        var exist = nameMap.get(item.name);\n\n        if (!exist) {\n          nameMap.set(item.name, {\n            count: 1\n          });\n        } else {\n          item.name += '-' + exist.count++;\n        }\n\n        return item;\n      });\n    }\n\n    function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {\n      if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n        for (var i = 0; i < data.length && i < maxLoop; i++) {\n          cb(data[i] ? data[i][0] : null, i);\n        }\n      } else {\n        var value0 = data[0] || [];\n\n        for (var i = 0; i < value0.length && i < maxLoop; i++) {\n          cb(value0[i], i);\n        }\n      }\n    }\n\n    function shouldRetrieveDataByName(source) {\n      var sourceFormat = source.sourceFormat;\n      return sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var _a, _b, _c; // TODO\n    var providerMethods;\n    var mountMethods;\n    /**\n     * If normal array used, mutable chunk size is supported.\n     * If typed array used, chunk size must be fixed.\n     */\n\n    var DefaultDataProvider =\n    /** @class */\n    function () {\n      function DefaultDataProvider(sourceParam, dimSize) {\n        // let source: Source;\n        var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam; // declare source is Source;\n\n        this._source = source;\n        var data = this._data = source.data; // Typed array. TODO IE10+?\n\n        if (source.sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n          if (\"development\" !== 'production') {\n            if (dimSize == null) {\n              throw new Error('Typed array data must specify dimension size');\n            }\n          }\n\n          this._offset = 0;\n          this._dimSize = dimSize;\n          this._data = data;\n        }\n\n        mountMethods(this, data, source);\n      }\n\n      DefaultDataProvider.prototype.getSource = function () {\n        return this._source;\n      };\n\n      DefaultDataProvider.prototype.count = function () {\n        return 0;\n      };\n\n      DefaultDataProvider.prototype.getItem = function (idx, out) {\n        return;\n      };\n\n      DefaultDataProvider.prototype.appendData = function (newData) {};\n\n      DefaultDataProvider.prototype.clean = function () {};\n\n      DefaultDataProvider.protoInitialize = function () {\n        // PENDING: To avoid potential incompat (e.g., prototype\n        // is visited somewhere), still init them on prototype.\n        var proto = DefaultDataProvider.prototype;\n        proto.pure = false;\n        proto.persistent = true;\n      }();\n\n      DefaultDataProvider.internalField = function () {\n        var _a;\n\n        mountMethods = function (provider, data, source) {\n          var sourceFormat = source.sourceFormat;\n          var seriesLayoutBy = source.seriesLayoutBy;\n          var startIndex = source.startIndex;\n          var dimsDef = source.dimensionsDefine;\n          var methods = providerMethods[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n          if (\"development\" !== 'production') {\n            assert(methods, 'Invalide sourceFormat: ' + sourceFormat);\n          }\n\n          extend(provider, methods);\n\n          if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n            provider.getItem = getItemForTypedArray;\n            provider.count = countForTypedArray;\n            provider.fillStorage = fillStorageForTypedArray;\n          } else {\n            var rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy);\n            provider.getItem = bind(rawItemGetter, null, data, startIndex, dimsDef);\n            var rawCounter = getRawSourceDataCounter(sourceFormat, seriesLayoutBy);\n            provider.count = bind(rawCounter, null, data, startIndex, dimsDef);\n          }\n        };\n\n        var getItemForTypedArray = function (idx, out) {\n          idx = idx - this._offset;\n          out = out || [];\n          var data = this._data;\n          var dimSize = this._dimSize;\n          var offset = dimSize * idx;\n\n          for (var i = 0; i < dimSize; i++) {\n            out[i] = data[offset + i];\n          }\n\n          return out;\n        };\n\n        var fillStorageForTypedArray = function (start, end, storage, extent) {\n          var data = this._data;\n          var dimSize = this._dimSize;\n\n          for (var dim = 0; dim < dimSize; dim++) {\n            var dimExtent = extent[dim];\n            var min = dimExtent[0] == null ? Infinity : dimExtent[0];\n            var max = dimExtent[1] == null ? -Infinity : dimExtent[1];\n            var count = end - start;\n            var arr = storage[dim];\n\n            for (var i = 0; i < count; i++) {\n              // appendData with TypedArray will always do replace in provider.\n              var val = data[i * dimSize + dim];\n              arr[start + i] = val;\n              val < min && (min = val);\n              val > max && (max = val);\n            }\n\n            dimExtent[0] = min;\n            dimExtent[1] = max;\n          }\n        };\n\n        var countForTypedArray = function () {\n          return this._data ? this._data.length / this._dimSize : 0;\n        };\n\n        providerMethods = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = {\n          pure: true,\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = {\n          pure: true,\n          appendData: function () {\n            throw new Error('Do not support appendData when set seriesLayoutBy: \"row\".');\n          }\n        }, _a[SOURCE_FORMAT_OBJECT_ROWS] = {\n          pure: true,\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_KEYED_COLUMNS] = {\n          pure: true,\n          appendData: function (newData) {\n            var data = this._data;\n            each(newData, function (newCol, key) {\n              var oldCol = data[key] || (data[key] = []);\n\n              for (var i = 0; i < (newCol || []).length; i++) {\n                oldCol.push(newCol[i]);\n              }\n            });\n          }\n        }, _a[SOURCE_FORMAT_ORIGINAL] = {\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_TYPED_ARRAY] = {\n          persistent: false,\n          pure: true,\n          appendData: function (newData) {\n            if (\"development\" !== 'production') {\n              assert(isTypedArray(newData), 'Added data must be TypedArray if data in initialization is TypedArray');\n            }\n\n            this._data = newData;\n          },\n          // Clean self if data is already used.\n          clean: function () {\n            // PENDING\n            this._offset += this.count();\n            this._data = null;\n          }\n        }, _a);\n\n        function appendDataSimply(newData) {\n          for (var i = 0; i < newData.length; i++) {\n            this._data.push(newData[i]);\n          }\n        }\n      }();\n\n      return DefaultDataProvider;\n    }();\n\n    var getItemSimply = function (rawData, startIndex, dimsDef, idx) {\n      return rawData[idx];\n    };\n\n    var rawSourceItemGetterMap = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef, idx) {\n      return rawData[idx + startIndex];\n    }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx, out) {\n      idx += startIndex;\n      var item = out || [];\n      var data = rawData;\n\n      for (var i = 0; i < data.length; i++) {\n        var row = data[i];\n        item[i] = row ? row[idx] : null;\n      }\n\n      return item;\n    }, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx, out) {\n      var item = out || [];\n\n      for (var i = 0; i < dimsDef.length; i++) {\n        var dimName = dimsDef[i].name;\n\n        if (\"development\" !== 'production') {\n          if (dimName == null) {\n            throw new Error();\n          }\n        }\n\n        var col = rawData[dimName];\n        item[i] = col ? col[idx] : null;\n      }\n\n      return item;\n    }, _a[SOURCE_FORMAT_ORIGINAL] = getItemSimply, _a);\n    function getRawSourceItemGetter(sourceFormat, seriesLayoutBy) {\n      var method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support get item on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n      }\n\n      return method;\n    }\n\n    var countSimply = function (rawData, startIndex, dimsDef) {\n      return rawData.length;\n    };\n\n    var rawSourceDataCounterMap = (_b = {}, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef) {\n      return Math.max(0, rawData.length - startIndex);\n    }, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef) {\n      var row = rawData[0];\n      return row ? Math.max(0, row.length - startIndex) : 0;\n    }, _b[SOURCE_FORMAT_OBJECT_ROWS] = countSimply, _b[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef) {\n      var dimName = dimsDef[0].name;\n\n      if (\"development\" !== 'production') {\n        if (dimName == null) {\n          throw new Error();\n        }\n      }\n\n      var col = rawData[dimName];\n      return col ? col.length : 0;\n    }, _b[SOURCE_FORMAT_ORIGINAL] = countSimply, _b);\n    function getRawSourceDataCounter(sourceFormat, seriesLayoutBy) {\n      var method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support count on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n      }\n\n      return method;\n    }\n\n    var getRawValueSimply = function (dataItem, dimIndex, property) {\n      return dataItem[dimIndex];\n    };\n\n    var rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, property) {\n      return dataItem[property];\n    }, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, property) {\n      // FIXME: In some case (markpoint in geo (geo-map.html)),\n      // dataItem is {coord: [...]}\n      var value = getDataItemValue(dataItem);\n      return !(value instanceof Array) ? value : value[dimIndex];\n    }, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c);\n    function getRawSourceValueGetter(sourceFormat) {\n      var method = rawSourceValueGetterMap[sourceFormat];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support get value on \"' + sourceFormat + '\".');\n      }\n\n      return method;\n    }\n\n    function getMethodMapKey(sourceFormat, seriesLayoutBy) {\n      return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + seriesLayoutBy : sourceFormat;\n    } // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,\n    // Consider persistent.\n    // Caution: why use raw value to display on label or tooltip?\n    // A reason is to avoid format. For example time value we do not know\n    // how to format is expected. More over, if stack is used, calculated\n    // value may be 0.91000000001, which have brings trouble to display.\n    // TODO: consider how to treat null/undefined/NaN when display?\n\n\n    function retrieveRawValue(data, dataIndex, // If dimIndex is null/undefined, return OptionDataItem.\n    // Otherwise, return OptionDataValue.\n    dim) {\n      if (!data) {\n        return;\n      } // Consider data may be not persistent.\n\n\n      var dataItem = data.getRawDataItem(dataIndex);\n\n      if (dataItem == null) {\n        return;\n      }\n\n      var store = data.getStore();\n      var sourceFormat = store.getSource().sourceFormat;\n\n      if (dim != null) {\n        var dimIndex = data.getDimensionIndex(dim);\n        var property = store.getDimensionProperty(dimIndex);\n        return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, property);\n      } else {\n        var result = dataItem;\n\n        if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n          result = getDataItemValue(dataItem);\n        }\n\n        return result;\n      }\n    }\n\n    var DIMENSION_LABEL_REG = /\\{@(.+?)\\}/g;\n\n    var DataFormatMixin =\n    /** @class */\n    function () {\n      function DataFormatMixin() {}\n      /**\n       * Get params for formatter\n       */\n\n\n      DataFormatMixin.prototype.getDataParams = function (dataIndex, dataType) {\n        var data = this.getData(dataType);\n        var rawValue = this.getRawValue(dataIndex, dataType);\n        var rawDataIndex = data.getRawIndex(dataIndex);\n        var name = data.getName(dataIndex);\n        var itemOpt = data.getRawDataItem(dataIndex);\n        var style = data.getItemVisual(dataIndex, 'style');\n        var color = style && style[data.getItemVisual(dataIndex, 'drawType') || 'fill'];\n        var borderColor = style && style.stroke;\n        var mainType = this.mainType;\n        var isSeries = mainType === 'series';\n        var userOutput = data.userOutput && data.userOutput.get();\n        return {\n          componentType: mainType,\n          componentSubType: this.subType,\n          componentIndex: this.componentIndex,\n          seriesType: isSeries ? this.subType : null,\n          seriesIndex: this.seriesIndex,\n          seriesId: isSeries ? this.id : null,\n          seriesName: isSeries ? this.name : null,\n          name: name,\n          dataIndex: rawDataIndex,\n          data: itemOpt,\n          dataType: dataType,\n          value: rawValue,\n          color: color,\n          borderColor: borderColor,\n          dimensionNames: userOutput ? userOutput.fullDimensions : null,\n          encode: userOutput ? userOutput.encode : null,\n          // Param name list for mapping `a`, `b`, `c`, `d`, `e`\n          $vars: ['seriesName', 'name', 'value']\n        };\n      };\n      /**\n       * Format label\n       * @param dataIndex\n       * @param status 'normal' by default\n       * @param dataType\n       * @param labelDimIndex Only used in some chart that\n       *        use formatter in different dimensions, like radar.\n       * @param formatter Formatter given outside.\n       * @return return null/undefined if no formatter\n       */\n\n\n      DataFormatMixin.prototype.getFormattedLabel = function (dataIndex, status, dataType, labelDimIndex, formatter, extendParams) {\n        status = status || 'normal';\n        var data = this.getData(dataType);\n        var params = this.getDataParams(dataIndex, dataType);\n\n        if (extendParams) {\n          params.value = extendParams.interpolatedValue;\n        }\n\n        if (labelDimIndex != null && isArray(params.value)) {\n          params.value = params.value[labelDimIndex];\n        }\n\n        if (!formatter) {\n          var itemModel = data.getItemModel(dataIndex); // @ts-ignore\n\n          formatter = itemModel.get(status === 'normal' ? ['label', 'formatter'] : [status, 'label', 'formatter']);\n        }\n\n        if (isFunction(formatter)) {\n          params.status = status;\n          params.dimensionIndex = labelDimIndex;\n          return formatter(params);\n        } else if (isString(formatter)) {\n          var str = formatTpl(formatter, params); // Support 'aaa{@[3]}bbb{@product}ccc'.\n          // Do not support '}' in dim name util have to.\n\n          return str.replace(DIMENSION_LABEL_REG, function (origin, dimStr) {\n            var len = dimStr.length;\n            var dimLoose = dimStr;\n\n            if (dimLoose.charAt(0) === '[' && dimLoose.charAt(len - 1) === ']') {\n              dimLoose = +dimLoose.slice(1, len - 1); // Also support: '[]' => 0\n\n              if (\"development\" !== 'production') {\n                if (isNaN(dimLoose)) {\n                  error(\"Invalide label formatter: @\" + dimStr + \", only support @[0], @[1], @[2], ...\");\n                }\n              }\n            }\n\n            var val = retrieveRawValue(data, dataIndex, dimLoose);\n\n            if (extendParams && isArray(extendParams.interpolatedValue)) {\n              var dimIndex = data.getDimensionIndex(dimLoose);\n\n              if (dimIndex >= 0) {\n                val = extendParams.interpolatedValue[dimIndex];\n              }\n            }\n\n            return val != null ? val + '' : '';\n          });\n        }\n      };\n      /**\n       * Get raw value in option\n       */\n\n\n      DataFormatMixin.prototype.getRawValue = function (idx, dataType) {\n        return retrieveRawValue(this.getData(dataType), idx);\n      };\n      /**\n       * Should be implemented.\n       * @param {number} dataIndex\n       * @param {boolean} [multipleSeries=false]\n       * @param {string} [dataType]\n       */\n\n\n      DataFormatMixin.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        // Empty function\n        return;\n      };\n\n      return DataFormatMixin;\n    }();\n    // but guess little chance has been used outside. Do we need to backward\n    // compat it?\n    // type TooltipFormatResultLegacyObject = {\n    //     // `html` means the markup language text, either in 'html' or 'richText'.\n    //     // The name `html` is not appropriate because in 'richText' it is not a HTML\n    //     // string. But still support it for backward compatibility.\n    //     html: string;\n    //     markers: Dictionary<ColorString>;\n    // };\n\n    /**\n     * For backward compat, normalize the return from `formatTooltip`.\n     */\n\n    function normalizeTooltipFormatResult(result) {\n      var markupText; // let markers: Dictionary<ColorString>;\n\n      var markupFragment;\n\n      if (isObject(result)) {\n        if (result.type) {\n          markupFragment = result;\n        } else {\n          if (\"development\" !== 'production') {\n            console.warn('The return type of `formatTooltip` is not supported: ' + makePrintable(result));\n          }\n        } // else {\n        //     markupText = (result as TooltipFormatResultLegacyObject).html;\n        //     markers = (result as TooltipFormatResultLegacyObject).markers;\n        //     if (markersExisting) {\n        //         markers = zrUtil.merge(markersExisting, markers);\n        //     }\n        // }\n\n      } else {\n        markupText = result;\n      }\n\n      return {\n        text: markupText,\n        // markers: markers || markersExisting,\n        frag: markupFragment\n      };\n    }\n\n    /**\n     * @param {Object} define\n     * @return See the return of `createTask`.\n     */\n\n    function createTask(define) {\n      return new Task(define);\n    }\n\n    var Task =\n    /** @class */\n    function () {\n      function Task(define) {\n        define = define || {};\n        this._reset = define.reset;\n        this._plan = define.plan;\n        this._count = define.count;\n        this._onDirty = define.onDirty;\n        this._dirty = true;\n      }\n      /**\n       * @param step Specified step.\n       * @param skip Skip customer perform call.\n       * @param modBy Sampling window size.\n       * @param modDataCount Sampling count.\n       * @return whether unfinished.\n       */\n\n\n      Task.prototype.perform = function (performArgs) {\n        var upTask = this._upstream;\n        var skip = performArgs && performArgs.skip; // TODO some refactor.\n        // Pull data. Must pull data each time, because context.data\n        // may be updated by Series.setData.\n\n        if (this._dirty && upTask) {\n          var context = this.context;\n          context.data = context.outputData = upTask.context.outputData;\n        }\n\n        if (this.__pipeline) {\n          this.__pipeline.currentTask = this;\n        }\n\n        var planResult;\n\n        if (this._plan && !skip) {\n          planResult = this._plan(this.context);\n        } // Support sharding by mod, which changes the render sequence and makes the rendered graphic\n        // elements uniformed distributed when progress, especially when moving or zooming.\n\n\n        var lastModBy = normalizeModBy(this._modBy);\n        var lastModDataCount = this._modDataCount || 0;\n        var modBy = normalizeModBy(performArgs && performArgs.modBy);\n        var modDataCount = performArgs && performArgs.modDataCount || 0;\n\n        if (lastModBy !== modBy || lastModDataCount !== modDataCount) {\n          planResult = 'reset';\n        }\n\n        function normalizeModBy(val) {\n          !(val >= 1) && (val = 1); // jshint ignore:line\n\n          return val;\n        }\n\n        var forceFirstProgress;\n\n        if (this._dirty || planResult === 'reset') {\n          this._dirty = false;\n          forceFirstProgress = this._doReset(skip);\n        }\n\n        this._modBy = modBy;\n        this._modDataCount = modDataCount;\n        var step = performArgs && performArgs.step;\n\n        if (upTask) {\n          if (\"development\" !== 'production') {\n            assert(upTask._outputDueEnd != null);\n          }\n\n          this._dueEnd = upTask._outputDueEnd;\n        } // DataTask or overallTask\n        else {\n            if (\"development\" !== 'production') {\n              assert(!this._progress || this._count);\n            }\n\n            this._dueEnd = this._count ? this._count(this.context) : Infinity;\n          } // Note: Stubs, that its host overall task let it has progress, has progress.\n        // If no progress, pass index from upstream to downstream each time plan called.\n\n\n        if (this._progress) {\n          var start = this._dueIndex;\n          var end = Math.min(step != null ? this._dueIndex + step : Infinity, this._dueEnd);\n\n          if (!skip && (forceFirstProgress || start < end)) {\n            var progress = this._progress;\n\n            if (isArray(progress)) {\n              for (var i = 0; i < progress.length; i++) {\n                this._doProgress(progress[i], start, end, modBy, modDataCount);\n              }\n            } else {\n              this._doProgress(progress, start, end, modBy, modDataCount);\n            }\n          }\n\n          this._dueIndex = end; // If no `outputDueEnd`, assume that output data and\n          // input data is the same, so use `dueIndex` as `outputDueEnd`.\n\n          var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end;\n\n          if (\"development\" !== 'production') {\n            // ??? Can not rollback.\n            assert(outputDueEnd >= this._outputDueEnd);\n          }\n\n          this._outputDueEnd = outputDueEnd;\n        } else {\n          // (1) Some overall task has no progress.\n          // (2) Stubs, that its host overall task do not let it has progress, has no progress.\n          // This should always be performed so it can be passed to downstream.\n          this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd;\n        }\n\n        return this.unfinished();\n      };\n\n      Task.prototype.dirty = function () {\n        this._dirty = true;\n        this._onDirty && this._onDirty(this.context);\n      };\n\n      Task.prototype._doProgress = function (progress, start, end, modBy, modDataCount) {\n        iterator.reset(start, end, modBy, modDataCount);\n        this._callingProgress = progress;\n\n        this._callingProgress({\n          start: start,\n          end: end,\n          count: end - start,\n          next: iterator.next\n        }, this.context);\n      };\n\n      Task.prototype._doReset = function (skip) {\n        this._dueIndex = this._outputDueEnd = this._dueEnd = 0;\n        this._settedOutputEnd = null;\n        var progress;\n        var forceFirstProgress;\n\n        if (!skip && this._reset) {\n          progress = this._reset(this.context);\n\n          if (progress && progress.progress) {\n            forceFirstProgress = progress.forceFirstProgress;\n            progress = progress.progress;\n          } // To simplify no progress checking, array must has item.\n\n\n          if (isArray(progress) && !progress.length) {\n            progress = null;\n          }\n        }\n\n        this._progress = progress;\n        this._modBy = this._modDataCount = null;\n        var downstream = this._downstream;\n        downstream && downstream.dirty();\n        return forceFirstProgress;\n      };\n\n      Task.prototype.unfinished = function () {\n        return this._progress && this._dueIndex < this._dueEnd;\n      };\n      /**\n       * @param downTask The downstream task.\n       * @return The downstream task.\n       */\n\n\n      Task.prototype.pipe = function (downTask) {\n        if (\"development\" !== 'production') {\n          assert(downTask && !downTask._disposed && downTask !== this);\n        } // If already downstream, do not dirty downTask.\n\n\n        if (this._downstream !== downTask || this._dirty) {\n          this._downstream = downTask;\n          downTask._upstream = this;\n          downTask.dirty();\n        }\n      };\n\n      Task.prototype.dispose = function () {\n        if (this._disposed) {\n          return;\n        }\n\n        this._upstream && (this._upstream._downstream = null);\n        this._downstream && (this._downstream._upstream = null);\n        this._dirty = false;\n        this._disposed = true;\n      };\n\n      Task.prototype.getUpstream = function () {\n        return this._upstream;\n      };\n\n      Task.prototype.getDownstream = function () {\n        return this._downstream;\n      };\n\n      Task.prototype.setOutputEnd = function (end) {\n        // This only happens in dataTask, dataZoom, map, currently.\n        // where dataZoom do not set end each time, but only set\n        // when reset. So we should record the set end, in case\n        // that the stub of dataZoom perform again and earse the\n        // set end by upstream.\n        this._outputDueEnd = this._settedOutputEnd = end;\n      };\n\n      return Task;\n    }();\n\n    var iterator = function () {\n      var end;\n      var current;\n      var modBy;\n      var modDataCount;\n      var winCount;\n      var it = {\n        reset: function (s, e, sStep, sCount) {\n          current = s;\n          end = e;\n          modBy = sStep;\n          modDataCount = sCount;\n          winCount = Math.ceil(modDataCount / modBy);\n          it.next = modBy > 1 && modDataCount > 0 ? modNext : sequentialNext;\n        }\n      };\n      return it;\n\n      function sequentialNext() {\n        return current < end ? current++ : null;\n      }\n\n      function modNext() {\n        var dataIndex = current % winCount * modBy + Math.ceil(current / winCount);\n        var result = current >= end ? null : dataIndex < modDataCount ? dataIndex // If modDataCount is smaller than data.count() (consider `appendData` case),\n        // Use normal linear rendering mode.\n        : current;\n        current++;\n        return result;\n      }\n    }(); // -----------------------------------------------------------------------------\n    // For stream debug (Should be commented out after used!)\n    // @usage: printTask(this, 'begin');\n    // @usage: printTask(this, null, {someExtraProp});\n    // @usage: Use `__idxInPipeline` as conditional breakpiont.\n    //\n    // window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void {\n    //     window.ecTaskUID == null && (window.ecTaskUID = 0);\n    //     task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);\n    //     task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);\n    //     let props = [];\n    //     if (task.__pipeline) {\n    //         let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;\n    //         props.push({text: '__idxInPipeline/total', value: val});\n    //     } else {\n    //         let stubCount = 0;\n    //         task.agentStubMap.each(() => stubCount++);\n    //         props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});\n    //     }\n    //     props.push({text: 'uid', value: task.uidDebug});\n    //     if (task.__pipeline) {\n    //         props.push({text: 'pipelineId', value: task.__pipeline.id});\n    //         task.agent && props.push(\n    //             {text: 'stubFor', value: task.agent.uidDebug}\n    //         );\n    //     }\n    //     props.push(\n    //         {text: 'dirty', value: task._dirty},\n    //         {text: 'dueIndex', value: task._dueIndex},\n    //         {text: 'dueEnd', value: task._dueEnd},\n    //         {text: 'outputDueEnd', value: task._outputDueEnd}\n    //     );\n    //     if (extra) {\n    //         Object.keys(extra).forEach(key => {\n    //             props.push({text: key, value: extra[key]});\n    //         });\n    //     }\n    //     let args = ['color: blue'];\n    //     let msg = `%c[${prefix || 'T'}] %c` + props.map(item => (\n    //         args.push('color: green', 'color: red'),\n    //         `${item.text}: %c${item.value}`\n    //     )).join('%c, ');\n    //     console.log.apply(console, [msg].concat(args));\n    //     // console.log(this);\n    // };\n    // window.printPipeline = function (task: any, prefix: string) {\n    //     const pipeline = task.__pipeline;\n    //     let currTask = pipeline.head;\n    //     while (currTask) {\n    //         window.printTask(currTask, prefix);\n    //         currTask = currTask._downstream;\n    //     }\n    // };\n    // window.showChain = function (chainHeadTask) {\n    //     var chain = [];\n    //     var task = chainHeadTask;\n    //     while (task) {\n    //         chain.push({\n    //             task: task,\n    //             up: task._upstream,\n    //             down: task._downstream,\n    //             idxInPipeline: task.__idxInPipeline\n    //         });\n    //         task = task._downstream;\n    //     }\n    //     return chain;\n    // };\n    // window.findTaskInChain = function (task, chainHeadTask) {\n    //     let chain = window.showChain(chainHeadTask);\n    //     let result = [];\n    //     for (let i = 0; i < chain.length; i++) {\n    //         let chainItem = chain[i];\n    //         if (chainItem.task === task) {\n    //             result.push(i);\n    //         }\n    //     }\n    //     return result;\n    // };\n    // window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) {\n    //     let chainA = window.showChain(chainHeadTaskA);\n    //     for (let i = 0; i < chainA.length; i++) {\n    //         console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB));\n    //     }\n    // };\n\n    /**\n     * Convert raw the value in to inner value in List.\n     *\n     * [Performance sensitive]\n     *\n     * [Caution]: this is the key logic of user value parser.\n     * For backward compatibility, do not modify it until you have to!\n     */\n\n    function parseDataValue(value, // For high performance, do not omit the second param.\n    opt) {\n      // Performance sensitive.\n      var dimType = opt && opt.type;\n\n      if (dimType === 'ordinal') {\n        // If given value is a category string\n        return value;\n      }\n\n      if (dimType === 'time' // spead up when using timestamp\n      && !isNumber(value) && value != null && value !== '-') {\n        value = +parseDate(value);\n      } // dimType defaults 'number'.\n      // If dimType is not ordinal and value is null or undefined or NaN or '-',\n      // parse to NaN.\n      // number-like string (like ' 123 ') can be converted to a number.\n      // where null/undefined or other string will be converted to NaN.\n\n\n      return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN\n      // If object, also parse to NaN\n      : +value;\n    }\n    var valueParserMap = createHashMap({\n      'number': function (val) {\n        // Do not use `numericToNumber` here. We have `numericToNumber` by default.\n        // Here the number parser can have loose rule:\n        // enable to cut suffix: \"120px\" => 120, \"14%\" => 14.\n        return parseFloat(val);\n      },\n      'time': function (val) {\n        // return timestamp.\n        return +parseDate(val);\n      },\n      'trim': function (val) {\n        return isString(val) ? trim(val) : val;\n      }\n    });\n    function getRawValueParser(type) {\n      return valueParserMap.get(type);\n    }\n    var ORDER_COMPARISON_OP_MAP = {\n      lt: function (lval, rval) {\n        return lval < rval;\n      },\n      lte: function (lval, rval) {\n        return lval <= rval;\n      },\n      gt: function (lval, rval) {\n        return lval > rval;\n      },\n      gte: function (lval, rval) {\n        return lval >= rval;\n      }\n    };\n\n    var FilterOrderComparator =\n    /** @class */\n    function () {\n      function FilterOrderComparator(op, rval) {\n        if (!isNumber(rval)) {\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            errMsg = 'rvalue of \"<\", \">\", \"<=\", \">=\" can only be number in filter.';\n          }\n\n          throwError(errMsg);\n        }\n\n        this._opFn = ORDER_COMPARISON_OP_MAP[op];\n        this._rvalFloat = numericToNumber(rval);\n      } // Performance sensitive.\n\n\n      FilterOrderComparator.prototype.evaluate = function (lval) {\n        // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.\n        return isNumber(lval) ? this._opFn(lval, this._rvalFloat) : this._opFn(numericToNumber(lval), this._rvalFloat);\n      };\n\n      return FilterOrderComparator;\n    }();\n\n    var SortOrderComparator =\n    /** @class */\n    function () {\n      /**\n       * @param order by default: 'asc'\n       * @param incomparable by default: Always on the tail.\n       *        That is, if 'asc' => 'max', if 'desc' => 'min'\n       *        See the definition of \"incomparable\" in [SORT_COMPARISON_RULE].\n       */\n      function SortOrderComparator(order, incomparable) {\n        var isDesc = order === 'desc';\n        this._resultLT = isDesc ? 1 : -1;\n\n        if (incomparable == null) {\n          incomparable = isDesc ? 'min' : 'max';\n        }\n\n        this._incomparable = incomparable === 'min' ? -Infinity : Infinity;\n      } // See [SORT_COMPARISON_RULE].\n      // Performance sensitive.\n\n\n      SortOrderComparator.prototype.evaluate = function (lval, rval) {\n        // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.\n        var lvalFloat = isNumber(lval) ? lval : numericToNumber(lval);\n        var rvalFloat = isNumber(rval) ? rval : numericToNumber(rval);\n        var lvalNotNumeric = isNaN(lvalFloat);\n        var rvalNotNumeric = isNaN(rvalFloat);\n\n        if (lvalNotNumeric) {\n          lvalFloat = this._incomparable;\n        }\n\n        if (rvalNotNumeric) {\n          rvalFloat = this._incomparable;\n        }\n\n        if (lvalNotNumeric && rvalNotNumeric) {\n          var lvalIsStr = isString(lval);\n          var rvalIsStr = isString(rval);\n\n          if (lvalIsStr) {\n            lvalFloat = rvalIsStr ? lval : 0;\n          }\n\n          if (rvalIsStr) {\n            rvalFloat = lvalIsStr ? rval : 0;\n          }\n        }\n\n        return lvalFloat < rvalFloat ? this._resultLT : lvalFloat > rvalFloat ? -this._resultLT : 0;\n      };\n\n      return SortOrderComparator;\n    }();\n\n    var FilterEqualityComparator =\n    /** @class */\n    function () {\n      function FilterEqualityComparator(isEq, rval) {\n        this._rval = rval;\n        this._isEQ = isEq;\n        this._rvalTypeof = typeof rval;\n        this._rvalFloat = numericToNumber(rval);\n      } // Performance sensitive.\n\n\n      FilterEqualityComparator.prototype.evaluate = function (lval) {\n        var eqResult = lval === this._rval;\n\n        if (!eqResult) {\n          var lvalTypeof = typeof lval;\n\n          if (lvalTypeof !== this._rvalTypeof && (lvalTypeof === 'number' || this._rvalTypeof === 'number')) {\n            eqResult = numericToNumber(lval) === this._rvalFloat;\n          }\n        }\n\n        return this._isEQ ? eqResult : !eqResult;\n      };\n\n      return FilterEqualityComparator;\n    }();\n    /**\n     * [FILTER_COMPARISON_RULE]\n     * `lt`|`lte`|`gt`|`gte`:\n     * + rval must be a number. And lval will be converted to number (`numericToNumber`) to compare.\n     * `eq`:\n     * + If same type, compare with `===`.\n     * + If there is one number, convert to number (`numericToNumber`) to compare.\n     * + Else return `false`.\n     * `ne`:\n     * + Not `eq`.\n     *\n     *\n     * [SORT_COMPARISON_RULE]\n     * All the values are grouped into three categories:\n     * + \"numeric\" (number and numeric string)\n     * + \"non-numeric-string\" (string that excluding numeric string)\n     * + \"others\"\n     * \"numeric\" vs \"numeric\": values are ordered by number order.\n     * \"non-numeric-string\" vs \"non-numeric-string\": values are ordered by ES spec (#sec-abstract-relational-comparison).\n     * \"others\" vs \"others\": do not change order (always return 0).\n     * \"numeric\" vs \"non-numeric-string\": \"non-numeric-string\" is treated as \"incomparable\".\n     * \"number\" vs \"others\": \"others\" is treated as \"incomparable\".\n     * \"non-numeric-string\" vs \"others\": \"others\" is treated as \"incomparable\".\n     * \"incomparable\" will be seen as -Infinity or Infinity (depends on the settings).\n     * MEMO:\n     *   Non-numeric string sort makes sense when we need to put the items with the same tag together.\n     *   But if we support string sort, we still need to avoid the misleading like `'2' > '12'`,\n     *   So we treat \"numeric-string\" sorted by number order rather than string comparison.\n     *\n     *\n     * [CHECK_LIST_OF_THE_RULE_DESIGN]\n     * + Do not support string comparison until required. And also need to\n     *   avoid the misleading of \"2\" > \"12\".\n     * + Should avoid the misleading case:\n     *   `\" 22 \" gte \"22\"` is `true` but `\" 22 \" eq \"22\"` is `false`.\n     * + JS bad case should be avoided: null <= 0, [] <= 0, ' ' <= 0, ...\n     * + Only \"numeric\" can be converted to comparable number, otherwise converted to NaN.\n     *   See `util/number.ts#numericToNumber`.\n     *\n     * @return If `op` is not `RelationalOperator`, return null;\n     */\n\n\n    function createFilterComparator(op, rval) {\n      return op === 'eq' || op === 'ne' ? new FilterEqualityComparator(op === 'eq', rval) : hasOwn(ORDER_COMPARISON_OP_MAP, op) ? new FilterOrderComparator(op, rval) : null;\n    }\n\n    /**\n     * TODO: disable writable.\n     * This structure will be exposed to users.\n     */\n\n    var ExternalSource =\n    /** @class */\n    function () {\n      function ExternalSource() {}\n\n      ExternalSource.prototype.getRawData = function () {\n        // Only built-in transform available.\n        throw new Error('not supported');\n      };\n\n      ExternalSource.prototype.getRawDataItem = function (dataIndex) {\n        // Only built-in transform available.\n        throw new Error('not supported');\n      };\n\n      ExternalSource.prototype.cloneRawData = function () {\n        return;\n      };\n      /**\n       * @return If dimension not found, return null/undefined.\n       */\n\n\n      ExternalSource.prototype.getDimensionInfo = function (dim) {\n        return;\n      };\n      /**\n       * dimensions defined if and only if either:\n       * (a) dataset.dimensions are declared.\n       * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).\n       * If dimensions are defined, `dimensionInfoAll` is corresponding to\n       * the defined dimensions.\n       * Otherwise, `dimensionInfoAll` is determined by data columns.\n       * @return Always return an array (even empty array).\n       */\n\n\n      ExternalSource.prototype.cloneAllDimensionInfo = function () {\n        return;\n      };\n\n      ExternalSource.prototype.count = function () {\n        return;\n      };\n      /**\n       * Only support by dimension index.\n       * No need to support by dimension name in transform function,\n       * because transform function is not case-specific, no need to use name literally.\n       */\n\n\n      ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {\n        return;\n      };\n\n      ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {\n        return;\n      };\n\n      ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {\n        return parseDataValue(rawVal, dimInfo);\n      };\n\n      return ExternalSource;\n    }();\n\n    function createExternalSource(internalSource, externalTransform) {\n      var extSource = new ExternalSource();\n      var data = internalSource.data;\n      var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;\n      var sourceHeaderCount = internalSource.startIndex;\n      var errMsg = '';\n\n      if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {\n        // For the logic simplicity in transformer, only 'culumn' is\n        // supported in data transform. Otherwise, the `dimensionsDefine`\n        // might be detected by 'row', which probably confuses users.\n        if (\"development\" !== 'production') {\n          errMsg = '`seriesLayoutBy` of upstream dataset can only be \"column\" in data transform.';\n        }\n\n        throwError(errMsg);\n      } // [MEMO]\n      // Create a new dimensions structure for exposing.\n      // Do not expose all dimension info to users directly.\n      // Because the dimension is probably auto detected from data and not might reliable.\n      // Should not lead the transformers to think that is reliable and return it.\n      // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n\n\n      var dimensions = [];\n      var dimsByName = {};\n      var dimsDef = internalSource.dimensionsDefine;\n\n      if (dimsDef) {\n        each(dimsDef, function (dimDef, idx) {\n          var name = dimDef.name;\n          var dimDefExt = {\n            index: idx,\n            name: name,\n            displayName: dimDef.displayName\n          };\n          dimensions.push(dimDefExt); // Users probably do not specify dimension name. For simplicity, data transform\n          // does not generate dimension name.\n\n          if (name != null) {\n            // Dimension name should not be duplicated.\n            // For simplicity, data transform forbids name duplication, do not generate\n            // new name like module `completeDimensions.ts` did, but just tell users.\n            var errMsg_1 = '';\n\n            if (hasOwn(dimsByName, name)) {\n              if (\"development\" !== 'production') {\n                errMsg_1 = 'dimension name \"' + name + '\" duplicated.';\n              }\n\n              throwError(errMsg_1);\n            }\n\n            dimsByName[name] = dimDefExt;\n          }\n        });\n      } // If dimension definitions are not defined and can not be detected.\n      // e.g., pure data `[[11, 22], ...]`.\n      else {\n          for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {\n            // Do not generete name or anything others. The consequence process in\n            // `transform` or `series` probably have there own name generation strategry.\n            dimensions.push({\n              index: i\n            });\n          }\n        } // Implement public methods:\n\n\n      var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n\n      if (externalTransform.__isBuiltIn) {\n        extSource.getRawDataItem = function (dataIndex) {\n          return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n        };\n\n        extSource.getRawData = bind(getRawData, null, internalSource);\n      }\n\n      extSource.cloneRawData = bind(cloneRawData, null, internalSource);\n      var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n      extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);\n      var rawValueGetter = getRawSourceValueGetter(sourceFormat);\n\n      extSource.retrieveValue = function (dataIndex, dimIndex) {\n        var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n        return retrieveValueFromItem(rawItem, dimIndex);\n      };\n\n      var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {\n        if (dataItem == null) {\n          return;\n        }\n\n        var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item.\n\n        if (dimDef) {\n          return rawValueGetter(dataItem, dimIndex, dimDef.name);\n        }\n      };\n\n      extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);\n      extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);\n      return extSource;\n    }\n\n    function getRawData(upstream) {\n      var sourceFormat = upstream.sourceFormat;\n\n      if (!isSupportedSourceFormat(sourceFormat)) {\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '`getRawData` is not supported in source format ' + sourceFormat;\n        }\n\n        throwError(errMsg);\n      }\n\n      return upstream.data;\n    }\n\n    function cloneRawData(upstream) {\n      var sourceFormat = upstream.sourceFormat;\n      var data = upstream.data;\n\n      if (!isSupportedSourceFormat(sourceFormat)) {\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;\n        }\n\n        throwError(errMsg);\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var result = [];\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          // Not strictly clone for performance\n          result.push(data[i].slice());\n        }\n\n        return result;\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        var result = [];\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          // Not strictly clone for performance\n          result.push(extend({}, data[i]));\n        }\n\n        return result;\n      }\n    }\n\n    function getDimensionInfo(dimensions, dimsByName, dim) {\n      if (dim == null) {\n        return;\n      } // Keep the same logic as `List::getDimension` did.\n\n\n      if (isNumber(dim) // If being a number-like string but not being defined a dimension name.\n      || !isNaN(dim) && !hasOwn(dimsByName, dim)) {\n        return dimensions[dim];\n      } else if (hasOwn(dimsByName, dim)) {\n        return dimsByName[dim];\n      }\n    }\n\n    function cloneAllDimensionInfo(dimensions) {\n      return clone(dimensions);\n    }\n\n    var externalTransformMap = createHashMap();\n    function registerExternalTransform(externalTransform) {\n      externalTransform = clone(externalTransform);\n      var type = externalTransform.type;\n      var errMsg = '';\n\n      if (!type) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Must have a `type` when `registerTransform`.';\n        }\n\n        throwError(errMsg);\n      }\n\n      var typeParsed = type.split(':');\n\n      if (typeParsed.length !== 2) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Name must include namespace like \"ns:regression\".';\n        }\n\n        throwError(errMsg);\n      } // Namespace 'echarts:xxx' is official namespace, where the transforms should\n      // be called directly via 'xxx' rather than 'echarts:xxx'.\n\n\n      var isBuiltIn = false;\n\n      if (typeParsed[0] === 'echarts') {\n        type = typeParsed[1];\n        isBuiltIn = true;\n      }\n\n      externalTransform.__isBuiltIn = isBuiltIn;\n      externalTransformMap.set(type, externalTransform);\n    }\n    function applyDataTransform(rawTransOption, sourceList, infoForPrint) {\n      var pipedTransOption = normalizeToArray(rawTransOption);\n      var pipeLen = pipedTransOption.length;\n      var errMsg = '';\n\n      if (!pipeLen) {\n        if (\"development\" !== 'production') {\n          errMsg = 'If `transform` declared, it should at least contain one transform.';\n        }\n\n        throwError(errMsg);\n      }\n\n      for (var i = 0, len = pipeLen; i < len; i++) {\n        var transOption = pipedTransOption[i];\n        sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one.\n        // piped transform only support single output, except the last one.\n\n        if (i !== len - 1) {\n          sourceList.length = Math.max(sourceList.length, 1);\n        }\n      }\n\n      return sourceList;\n    }\n\n    function applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform.\n    pipeIndex) {\n      var errMsg = '';\n\n      if (!upSourceList.length) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Must have at least one upstream dataset.';\n        }\n\n        throwError(errMsg);\n      }\n\n      if (!isObject(transOption)) {\n        if (\"development\" !== 'production') {\n          errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';\n        }\n\n        throwError(errMsg);\n      }\n\n      var transType = transOption.type;\n      var externalTransform = externalTransformMap.get(transType);\n\n      if (!externalTransform) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Can not find transform on type \"' + transType + '\".';\n        }\n\n        throwError(errMsg);\n      } // Prepare source\n\n\n      var extUpSourceList = map(upSourceList, function (upSource) {\n        return createExternalSource(upSource, externalTransform);\n      });\n      var resultList = normalizeToArray(externalTransform.transform({\n        upstream: extUpSourceList[0],\n        upstreamList: extUpSourceList,\n        config: clone(transOption.config)\n      }));\n\n      if (\"development\" !== 'production') {\n        if (transOption.print) {\n          var printStrArr = map(resultList, function (extSource) {\n            var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';\n            return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\\n');\n          }).join('\\n');\n          log(printStrArr);\n        }\n      }\n\n      return map(resultList, function (result, resultIndex) {\n        var errMsg = '';\n\n        if (!isObject(result)) {\n          if (\"development\" !== 'production') {\n            errMsg = 'A transform should not return some empty results.';\n          }\n\n          throwError(errMsg);\n        }\n\n        if (!result.data) {\n          if (\"development\" !== 'production') {\n            errMsg = 'Transform result data should be not be null or undefined';\n          }\n\n          throwError(errMsg);\n        }\n\n        var sourceFormat = detectSourceFormat(result.data);\n\n        if (!isSupportedSourceFormat(sourceFormat)) {\n          if (\"development\" !== 'production') {\n            errMsg = 'Transform result data should be array rows or object rows.';\n          }\n\n          throwError(errMsg);\n        }\n\n        var resultMetaRawOption;\n        var firstUpSource = upSourceList[0];\n        /**\n         * Intuitively, the end users known the content of the original `dataset.source`,\n         * calucating the transform result in mind.\n         * Suppose the original `dataset.source` is:\n         * ```js\n         * [\n         *     ['product', '2012', '2013', '2014', '2015'],\n         *     ['AAA', 41.1, 30.4, 65.1, 53.3],\n         *     ['BBB', 86.5, 92.1, 85.7, 83.1],\n         *     ['CCC', 24.1, 67.2, 79.5, 86.4]\n         * ]\n         * ```\n         * The dimension info have to be detected from the source data.\n         * Some of the transformers (like filter, sort) will follow the dimension info\n         * of upstream, while others use new dimensions (like aggregate).\n         * Transformer can output a field `dimensions` to define the its own output dimensions.\n         * We also allow transformers to ignore the output `dimensions` field, and\n         * inherit the upstream dimensions definition. It can reduce the burden of handling\n         * dimensions in transformers.\n         *\n         * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n         */\n\n        if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different\n        // dimensions definitions. We do not inherit anything from upstream.\n        && !result.dimensions) {\n          var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result, because:\n          // (1) The returned data always does not contain header line and can not be used\n          // as dimension-detection. In this case we can not use \"detected dimensions\" of\n          // upstream directly, because it might be detected based on different `seriesLayoutBy`.\n          // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.\n          // So the original detected header should be add to the result, otherwise they can not be read.\n\n          if (startIndex) {\n            result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);\n          }\n\n          resultMetaRawOption = {\n            seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n            sourceHeader: startIndex,\n            dimensions: firstUpSource.metaRawOption.dimensions\n          };\n        } else {\n          resultMetaRawOption = {\n            seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n            sourceHeader: 0,\n            dimensions: result.dimensions\n          };\n        }\n\n        return createSource(result.data, resultMetaRawOption, null);\n      });\n    }\n\n    function isSupportedSourceFormat(sourceFormat) {\n      return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;\n    }\n\n    var UNDEFINED = 'undefined';\n    /* global Float64Array, Int32Array, Uint32Array, Uint16Array */\n    // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is\n    // different from the Ctor of typed array.\n\n    var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;\n    var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;\n    var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;\n    var CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array;\n    /**\n     * Multi dimensional data store\n     */\n\n    var dataCtors = {\n      'float': CtorFloat64Array,\n      'int': CtorInt32Array,\n      // Ordinal data type can be string or int\n      'ordinal': Array,\n      'number': Array,\n      'time': CtorFloat64Array\n    };\n    var defaultDimValueGetters;\n\n    function getIndicesCtor(rawCount) {\n      // The possible max value in this._indicies is always this._rawCount despite of filtering.\n      return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;\n    }\n\n    function getInitialExtent() {\n      return [Infinity, -Infinity];\n    }\n\n    function cloneChunk(originalChunk) {\n      var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array.\n\n      return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);\n    }\n\n    function prepareStore(store, dimIdx, dimType, end, append) {\n      var DataCtor = dataCtors[dimType || 'float'];\n\n      if (append) {\n        var oldStore = store[dimIdx];\n        var oldLen = oldStore && oldStore.length;\n\n        if (!(oldLen === end)) {\n          var newStore = new DataCtor(end); // The cost of the copy is probably inconsiderable\n          // within the initial chunkSize.\n\n          for (var j = 0; j < oldLen; j++) {\n            newStore[j] = oldStore[j];\n          }\n\n          store[dimIdx] = newStore;\n        }\n      } else {\n        store[dimIdx] = new DataCtor(end);\n      }\n    }\n    /**\n     * Basically, DataStore API keep immutable.\n     */\n\n    var DataStore =\n    /** @class */\n    function () {\n      function DataStore() {\n        this._chunks = []; // It will not be calculated util needed.\n\n        this._rawExtent = [];\n        this._extent = [];\n        this._count = 0;\n        this._rawCount = 0;\n        this._calcDimNameToIdx = createHashMap();\n      }\n      /**\n       * Initialize from data\n       */\n\n\n      DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {\n        if (\"development\" !== 'production') {\n          assert(isFunction(provider.getItem) && isFunction(provider.count), 'Invalid data provider.');\n        }\n\n        this._provider = provider; // Clear\n\n        this._chunks = [];\n        this._indices = null;\n        this.getRawIndex = this._getRawIdxIdentity;\n        var source = provider.getSource();\n        var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; // Default dim value getter\n\n        this._dimValueGetter = dimValueGetter || defaultGetter; // Reset raw extent.\n\n        this._rawExtent = [];\n        var willRetrieveDataByName = shouldRetrieveDataByName(source);\n        this._dimensions = map(inputDimensions, function (dim) {\n          if (\"development\" !== 'production') {\n            if (willRetrieveDataByName) {\n              assert(dim.property != null);\n            }\n          }\n\n          return {\n            // Only pick these two props. Not leak other properties like orderMeta.\n            type: dim.type,\n            property: dim.property\n          };\n        });\n\n        this._initDataFromProvider(0, provider.count());\n      };\n\n      DataStore.prototype.getProvider = function () {\n        return this._provider;\n      };\n      /**\n       * Caution: even when a `source` instance owned by a series, the created data store\n       * may still be shared by different sereis (the source hash does not use all `source`\n       * props, see `sourceManager`). In this case, the `source` props that are not used in\n       * hash (like `source.dimensionDefine`) probably only belongs to a certain series and\n       * thus should not be fetch here.\n       */\n\n\n      DataStore.prototype.getSource = function () {\n        return this._provider.getSource();\n      };\n      /**\n       * @caution Only used in dataStack.\n       */\n\n\n      DataStore.prototype.ensureCalculationDimension = function (dimName, type) {\n        var calcDimNameToIdx = this._calcDimNameToIdx;\n        var dimensions = this._dimensions;\n        var calcDimIdx = calcDimNameToIdx.get(dimName);\n\n        if (calcDimIdx != null) {\n          if (dimensions[calcDimIdx].type === type) {\n            return calcDimIdx;\n          }\n        } else {\n          calcDimIdx = dimensions.length;\n        }\n\n        dimensions[calcDimIdx] = {\n          type: type\n        };\n        calcDimNameToIdx.set(dimName, calcDimIdx);\n        this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);\n        this._rawExtent[calcDimIdx] = getInitialExtent();\n        return calcDimIdx;\n      };\n\n      DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) {\n        var chunk = this._chunks[dimIdx];\n        var dim = this._dimensions[dimIdx];\n        var rawExtents = this._rawExtent;\n        var offset = dim.ordinalOffset || 0;\n        var len = chunk.length;\n\n        if (offset === 0) {\n          // We need to reset the rawExtent if collect is from start.\n          // Because this dimension may be guessed as number and calcuating a wrong extent.\n          rawExtents[dimIdx] = getInitialExtent();\n        }\n\n        var dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData\n\n        for (var i = offset; i < len; i++) {\n          var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]);\n\n          if (!isNaN(val)) {\n            dimRawExtent[0] = Math.min(val, dimRawExtent[0]);\n            dimRawExtent[1] = Math.max(val, dimRawExtent[1]);\n          }\n        }\n\n        dim.ordinalMeta = ordinalMeta;\n        dim.ordinalOffset = len;\n        dim.type = 'ordinal'; // Force to be ordinal\n      };\n\n      DataStore.prototype.getOrdinalMeta = function (dimIdx) {\n        var dimInfo = this._dimensions[dimIdx];\n        var ordinalMeta = dimInfo.ordinalMeta;\n        return ordinalMeta;\n      };\n\n      DataStore.prototype.getDimensionProperty = function (dimIndex) {\n        var item = this._dimensions[dimIndex];\n        return item && item.property;\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       */\n\n\n      DataStore.prototype.appendData = function (data) {\n        if (\"development\" !== 'production') {\n          assert(!this._indices, 'appendData can only be called on raw data.');\n        }\n\n        var provider = this._provider;\n        var start = this.count();\n        provider.appendData(data);\n        var end = provider.count();\n\n        if (!provider.persistent) {\n          end += start;\n        }\n\n        if (start < end) {\n          this._initDataFromProvider(start, end, true);\n        }\n\n        return [start, end];\n      };\n\n      DataStore.prototype.appendValues = function (values, minFillLen) {\n        var chunks = this._chunks;\n        var dimensions = this._dimensions;\n        var dimLen = dimensions.length;\n        var rawExtent = this._rawExtent;\n        var start = this.count();\n        var end = start + Math.max(values.length, minFillLen || 0);\n\n        for (var i = 0; i < dimLen; i++) {\n          var dim = dimensions[i];\n          prepareStore(chunks, i, dim.type, end, true);\n        }\n\n        var emptyDataItem = [];\n\n        for (var idx = start; idx < end; idx++) {\n          var sourceIdx = idx - start; // Store the data by dimensions\n\n          for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n            var dim = dimensions[dimIdx];\n            var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx);\n            chunks[dimIdx][idx] = val;\n            var dimRawExtent = rawExtent[dimIdx];\n            val < dimRawExtent[0] && (dimRawExtent[0] = val);\n            val > dimRawExtent[1] && (dimRawExtent[1] = val);\n          }\n        }\n\n        this._rawCount = this._count = end;\n        return {\n          start: start,\n          end: end\n        };\n      };\n\n      DataStore.prototype._initDataFromProvider = function (start, end, append) {\n        var provider = this._provider;\n        var chunks = this._chunks;\n        var dimensions = this._dimensions;\n        var dimLen = dimensions.length;\n        var rawExtent = this._rawExtent;\n        var dimNames = map(dimensions, function (dim) {\n          return dim.property;\n        });\n\n        for (var i = 0; i < dimLen; i++) {\n          var dim = dimensions[i];\n\n          if (!rawExtent[i]) {\n            rawExtent[i] = getInitialExtent();\n          }\n\n          prepareStore(chunks, i, dim.type, end, append);\n        }\n\n        if (provider.fillStorage) {\n          provider.fillStorage(start, end, chunks, rawExtent);\n        } else {\n          var dataItem = [];\n\n          for (var idx = start; idx < end; idx++) {\n            // NOTICE: Try not to write things into dataItem\n            dataItem = provider.getItem(idx, dataItem); // Each data item is value\n            // [1, 2]\n            // 2\n            // Bar chart, line chart which uses category axis\n            // only gives the 'y' value. 'x' value is the indices of category\n            // Use a tempValue to normalize the value to be a (x, y) value\n            // Store the data by dimensions\n\n            for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n              var dimStorage = chunks[dimIdx]; // PENDING NULL is empty or zero\n\n              var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx);\n\n              dimStorage[idx] = val;\n              var dimRawExtent = rawExtent[dimIdx];\n              val < dimRawExtent[0] && (dimRawExtent[0] = val);\n              val > dimRawExtent[1] && (dimRawExtent[1] = val);\n            }\n          }\n        }\n\n        if (!provider.persistent && provider.clean) {\n          // Clean unused data if data source is typed array.\n          provider.clean();\n        }\n\n        this._rawCount = this._count = end; // Reset data extent\n\n        this._extent = [];\n      };\n\n      DataStore.prototype.count = function () {\n        return this._count;\n      };\n      /**\n       * Get value. Return NaN if idx is out of range.\n       */\n\n\n      DataStore.prototype.get = function (dim, idx) {\n        if (!(idx >= 0 && idx < this._count)) {\n          return NaN;\n        }\n\n        var dimStore = this._chunks[dim];\n        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;\n      };\n\n      DataStore.prototype.getValues = function (dimensions, idx) {\n        var values = [];\n        var dimArr = [];\n\n        if (idx == null) {\n          idx = dimensions; // TODO get all from store?\n\n          dimensions = []; // All dimensions\n\n          for (var i = 0; i < this._dimensions.length; i++) {\n            dimArr.push(i);\n          }\n        } else {\n          dimArr = dimensions;\n        }\n\n        for (var i = 0, len = dimArr.length; i < len; i++) {\n          values.push(this.get(dimArr[i], idx));\n        }\n\n        return values;\n      };\n      /**\n       * @param dim concrete dim\n       */\n\n\n      DataStore.prototype.getByRawIndex = function (dim, rawIdx) {\n        if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {\n          return NaN;\n        }\n\n        var dimStore = this._chunks[dim];\n        return dimStore ? dimStore[rawIdx] : NaN;\n      };\n      /**\n       * Get sum of data in one dimension\n       */\n\n\n      DataStore.prototype.getSum = function (dim) {\n        var dimData = this._chunks[dim];\n        var sum = 0;\n\n        if (dimData) {\n          for (var i = 0, len = this.count(); i < len; i++) {\n            var value = this.get(dim, i);\n\n            if (!isNaN(value)) {\n              sum += value;\n            }\n          }\n        }\n\n        return sum;\n      };\n      /**\n       * Get median of data in one dimension\n       */\n\n\n      DataStore.prototype.getMedian = function (dim) {\n        var dimDataArray = []; // map all data of one dimension\n\n        this.each([dim], function (val) {\n          if (!isNaN(val)) {\n            dimDataArray.push(val);\n          }\n        }); // TODO\n        // Use quick select?\n\n        var sortedDimDataArray = dimDataArray.sort(function (a, b) {\n          return a - b;\n        });\n        var len = this.count(); // calculate median\n\n        return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;\n      };\n      /**\n       * Retrieve the index with given raw data index.\n       */\n\n\n      DataStore.prototype.indexOfRawIndex = function (rawIndex) {\n        if (rawIndex >= this._rawCount || rawIndex < 0) {\n          return -1;\n        }\n\n        if (!this._indices) {\n          return rawIndex;\n        } // Indices are ascending\n\n\n        var indices = this._indices; // If rawIndex === dataIndex\n\n        var rawDataIndex = indices[rawIndex];\n\n        if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {\n          return rawIndex;\n        }\n\n        var left = 0;\n        var right = this._count - 1;\n\n        while (left <= right) {\n          var mid = (left + right) / 2 | 0;\n\n          if (indices[mid] < rawIndex) {\n            left = mid + 1;\n          } else if (indices[mid] > rawIndex) {\n            right = mid - 1;\n          } else {\n            return mid;\n          }\n        }\n\n        return -1;\n      };\n      /**\n       * Retrieve the index of nearest value.\n       * @param dim\n       * @param value\n       * @param [maxDistance=Infinity]\n       * @return If and only if multiple indices have\n       *         the same value, they are put to the result.\n       */\n\n\n      DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n        var chunks = this._chunks;\n        var dimData = chunks[dim];\n        var nearestIndices = [];\n\n        if (!dimData) {\n          return nearestIndices;\n        }\n\n        if (maxDistance == null) {\n          maxDistance = Infinity;\n        }\n\n        var minDist = Infinity;\n        var minDiff = -1;\n        var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/SeriesData.js`.\n\n        for (var i = 0, len = this.count(); i < len; i++) {\n          var dataIndex = this.getRawIndex(i);\n          var diff = value - dimData[dataIndex];\n          var dist = Math.abs(diff);\n\n          if (dist <= maxDistance) {\n            // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,\n            // we'd better not push both of them to `nearestIndices`, otherwise it is easy to\n            // get more than one item in `nearestIndices` (more specifically, in `tooltip`).\n            // So we chose the one that `diff >= 0` in this csae.\n            // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them\n            // should be push to `nearestIndices`.\n            if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) {\n              minDist = dist;\n              minDiff = diff;\n              nearestIndicesLen = 0;\n            }\n\n            if (diff === minDiff) {\n              nearestIndices[nearestIndicesLen++] = i;\n            }\n          }\n        }\n\n        nearestIndices.length = nearestIndicesLen;\n        return nearestIndices;\n      };\n\n      DataStore.prototype.getIndices = function () {\n        var newIndices;\n        var indices = this._indices;\n\n        if (indices) {\n          var Ctor = indices.constructor;\n          var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.\n\n          if (Ctor === Array) {\n            newIndices = new Ctor(thisCount);\n\n            for (var i = 0; i < thisCount; i++) {\n              newIndices[i] = indices[i];\n            }\n          } else {\n            newIndices = new Ctor(indices.buffer, 0, thisCount);\n          }\n        } else {\n          var Ctor = getIndicesCtor(this._rawCount);\n          newIndices = new Ctor(this.count());\n\n          for (var i = 0; i < newIndices.length; i++) {\n            newIndices[i] = i;\n          }\n        }\n\n        return newIndices;\n      };\n      /**\n       * Data filter.\n       */\n\n\n      DataStore.prototype.filter = function (dims, cb) {\n        if (!this._count) {\n          return this;\n        }\n\n        var newStore = this.clone();\n        var count = newStore.count();\n        var Ctor = getIndicesCtor(newStore._rawCount);\n        var newIndices = new Ctor(count);\n        var value = [];\n        var dimSize = dims.length;\n        var offset = 0;\n        var dim0 = dims[0];\n        var chunks = newStore._chunks;\n\n        for (var i = 0; i < count; i++) {\n          var keep = void 0;\n          var rawIdx = newStore.getRawIndex(i); // Simple optimization\n\n          if (dimSize === 0) {\n            keep = cb(i);\n          } else if (dimSize === 1) {\n            var val = chunks[dim0][rawIdx];\n            keep = cb(val, i);\n          } else {\n            var k = 0;\n\n            for (; k < dimSize; k++) {\n              value[k] = chunks[dims[k]][rawIdx];\n            }\n\n            value[k] = i;\n            keep = cb.apply(null, value);\n          }\n\n          if (keep) {\n            newIndices[offset++] = rawIdx;\n          }\n        } // Set indices after filtered.\n\n\n        if (offset < count) {\n          newStore._indices = newIndices;\n        }\n\n        newStore._count = offset; // Reset data extent\n\n        newStore._extent = [];\n\n        newStore._updateGetRawIdx();\n\n        return newStore;\n      };\n      /**\n       * Select data in range. (For optimization of filter)\n       * (Manually inline code, support 5 million data filtering in data zoom.)\n       */\n\n\n      DataStore.prototype.selectRange = function (range) {\n        var newStore = this.clone();\n        var len = newStore._count;\n\n        if (!len) {\n          return this;\n        }\n\n        var dims = keys(range);\n        var dimSize = dims.length;\n\n        if (!dimSize) {\n          return this;\n        }\n\n        var originalCount = newStore.count();\n        var Ctor = getIndicesCtor(newStore._rawCount);\n        var newIndices = new Ctor(originalCount);\n        var offset = 0;\n        var dim0 = dims[0];\n        var min = range[dim0][0];\n        var max = range[dim0][1];\n        var storeArr = newStore._chunks;\n        var quickFinished = false;\n\n        if (!newStore._indices) {\n          // Extreme optimization for common case. About 2x faster in chrome.\n          var idx = 0;\n\n          if (dimSize === 1) {\n            var dimStorage = storeArr[dims[0]];\n\n            for (var i = 0; i < len; i++) {\n              var val = dimStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty\n              // value indicates the line should be broken. But for the case like\n              // scatter plot, a data item with empty value will not be rendered,\n              // but the axis extent may be effected if some other dim of the data\n              // item has value. Fortunately it is not a significant negative effect.\n\n              if (val >= min && val <= max || isNaN(val)) {\n                newIndices[offset++] = idx;\n              }\n\n              idx++;\n            }\n\n            quickFinished = true;\n          } else if (dimSize === 2) {\n            var dimStorage = storeArr[dims[0]];\n            var dimStorage2 = storeArr[dims[1]];\n            var min2 = range[dims[1]][0];\n            var max2 = range[dims[1]][1];\n\n            for (var i = 0; i < len; i++) {\n              var val = dimStorage[i];\n              var val2 = dimStorage2[i]; // Do not filter NaN, see comment above.\n\n              if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {\n                newIndices[offset++] = idx;\n              }\n\n              idx++;\n            }\n\n            quickFinished = true;\n          }\n        }\n\n        if (!quickFinished) {\n          if (dimSize === 1) {\n            for (var i = 0; i < originalCount; i++) {\n              var rawIndex = newStore.getRawIndex(i);\n              var val = storeArr[dims[0]][rawIndex]; // Do not filter NaN, see comment above.\n\n              if (val >= min && val <= max || isNaN(val)) {\n                newIndices[offset++] = rawIndex;\n              }\n            }\n          } else {\n            for (var i = 0; i < originalCount; i++) {\n              var keep = true;\n              var rawIndex = newStore.getRawIndex(i);\n\n              for (var k = 0; k < dimSize; k++) {\n                var dimk = dims[k];\n                var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above.\n\n                if (val < range[dimk][0] || val > range[dimk][1]) {\n                  keep = false;\n                }\n              }\n\n              if (keep) {\n                newIndices[offset++] = newStore.getRawIndex(i);\n              }\n            }\n          }\n        } // Set indices after filtered.\n\n\n        if (offset < originalCount) {\n          newStore._indices = newIndices;\n        }\n\n        newStore._count = offset; // Reset data extent\n\n        newStore._extent = [];\n\n        newStore._updateGetRawIdx();\n\n        return newStore;\n      }; // /**\n      //  * Data mapping to a plain array\n      //  */\n      // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {\n      //     const result: any[] = [];\n      //     this.each(dims, function () {\n      //         result.push(cb && (cb as MapArrayCb).apply(null, arguments));\n      //     });\n      //     return result;\n      // }\n\n      /**\n       * Data mapping to a new List with given dimensions\n       */\n\n\n      DataStore.prototype.map = function (dims, cb) {\n        // TODO only clone picked chunks.\n        var target = this.clone(dims);\n\n        this._updateDims(target, dims, cb);\n\n        return target;\n      };\n      /**\n       * @caution Danger!! Only used in dataStack.\n       */\n\n\n      DataStore.prototype.modify = function (dims, cb) {\n        this._updateDims(this, dims, cb);\n      };\n\n      DataStore.prototype._updateDims = function (target, dims, cb) {\n        var targetChunks = target._chunks;\n        var tmpRetValue = [];\n        var dimSize = dims.length;\n        var dataCount = target.count();\n        var values = [];\n        var rawExtent = target._rawExtent;\n\n        for (var i = 0; i < dims.length; i++) {\n          rawExtent[dims[i]] = getInitialExtent();\n        }\n\n        for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {\n          var rawIndex = target.getRawIndex(dataIndex);\n\n          for (var k = 0; k < dimSize; k++) {\n            values[k] = targetChunks[dims[k]][rawIndex];\n          }\n\n          values[dimSize] = dataIndex;\n          var retValue = cb && cb.apply(null, values);\n\n          if (retValue != null) {\n            // a number or string (in oridinal dimension)?\n            if (typeof retValue !== 'object') {\n              tmpRetValue[0] = retValue;\n              retValue = tmpRetValue;\n            }\n\n            for (var i = 0; i < retValue.length; i++) {\n              var dim = dims[i];\n              var val = retValue[i];\n              var rawExtentOnDim = rawExtent[dim];\n              var dimStore = targetChunks[dim];\n\n              if (dimStore) {\n                dimStore[rawIndex] = val;\n              }\n\n              if (val < rawExtentOnDim[0]) {\n                rawExtentOnDim[0] = val;\n              }\n\n              if (val > rawExtentOnDim[1]) {\n                rawExtentOnDim[1] = val;\n              }\n            }\n          }\n        }\n      };\n      /**\n       * Large data down sampling using largest-triangle-three-buckets\n       * @param {string} valueDimension\n       * @param {number} targetCount\n       */\n\n\n      DataStore.prototype.lttbDownSample = function (valueDimension, rate) {\n        var target = this.clone([valueDimension], true);\n        var targetStorage = target._chunks;\n        var dimStore = targetStorage[valueDimension];\n        var len = this.count();\n        var sampledIndex = 0;\n        var frameSize = Math.floor(1 / rate);\n        var currentRawIndex = this.getRawIndex(0);\n        var maxArea;\n        var area;\n        var nextRawIndex;\n        var newIndices = new (getIndicesCtor(this._rawCount))(Math.min((Math.ceil(len / frameSize) + 2) * 2, len)); // First frame use the first data.\n\n        newIndices[sampledIndex++] = currentRawIndex;\n\n        for (var i = 1; i < len - 1; i += frameSize) {\n          var nextFrameStart = Math.min(i + frameSize, len - 1);\n          var nextFrameEnd = Math.min(i + frameSize * 2, len);\n          var avgX = (nextFrameEnd + nextFrameStart) / 2;\n          var avgY = 0;\n\n          for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {\n            var rawIndex = this.getRawIndex(idx);\n            var y = dimStore[rawIndex];\n\n            if (isNaN(y)) {\n              continue;\n            }\n\n            avgY += y;\n          }\n\n          avgY /= nextFrameEnd - nextFrameStart;\n          var frameStart = i;\n          var frameEnd = Math.min(i + frameSize, len);\n          var pointAX = i - 1;\n          var pointAY = dimStore[currentRawIndex];\n          maxArea = -1;\n          nextRawIndex = frameStart;\n          var firstNaNIndex = -1;\n          var countNaN = 0; // Find a point from current frame that construct a triangel with largest area with previous selected point\n          // And the average of next frame.\n\n          for (var idx = frameStart; idx < frameEnd; idx++) {\n            var rawIndex = this.getRawIndex(idx);\n            var y = dimStore[rawIndex];\n\n            if (isNaN(y)) {\n              countNaN++;\n\n              if (firstNaNIndex < 0) {\n                firstNaNIndex = rawIndex;\n              }\n\n              continue;\n            } // Calculate triangle area over three buckets\n\n\n            area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));\n\n            if (area > maxArea) {\n              maxArea = area;\n              nextRawIndex = rawIndex; // Next a is this b\n            }\n          }\n\n          if (countNaN > 0 && countNaN < frameEnd - frameStart) {\n            // Append first NaN point in every bucket.\n            // It is necessary to ensure the correct order of indices.\n            newIndices[sampledIndex++] = Math.min(firstNaNIndex, nextRawIndex);\n            nextRawIndex = Math.max(firstNaNIndex, nextRawIndex);\n          }\n\n          newIndices[sampledIndex++] = nextRawIndex;\n          currentRawIndex = nextRawIndex; // This a is the next a (chosen b)\n        } // First frame use the last data.\n\n\n        newIndices[sampledIndex++] = this.getRawIndex(len - 1);\n        target._count = sampledIndex;\n        target._indices = newIndices;\n        target.getRawIndex = this._getRawIdx;\n        return target;\n      };\n      /**\n       * Large data down sampling on given dimension\n       * @param sampleIndex Sample index for name and id\n       */\n\n\n      DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n        var target = this.clone([dimension], true);\n        var targetStorage = target._chunks;\n        var frameValues = [];\n        var frameSize = Math.floor(1 / rate);\n        var dimStore = targetStorage[dimension];\n        var len = this.count();\n        var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent();\n        var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize));\n        var offset = 0;\n\n        for (var i = 0; i < len; i += frameSize) {\n          // Last frame\n          if (frameSize > len - i) {\n            frameSize = len - i;\n            frameValues.length = frameSize;\n          }\n\n          for (var k = 0; k < frameSize; k++) {\n            var dataIdx = this.getRawIndex(i + k);\n            frameValues[k] = dimStore[dataIdx];\n          }\n\n          var value = sampleValue(frameValues);\n          var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data\n\n          dimStore[sampleFrameIdx] = value;\n\n          if (value < rawExtentOnDim[0]) {\n            rawExtentOnDim[0] = value;\n          }\n\n          if (value > rawExtentOnDim[1]) {\n            rawExtentOnDim[1] = value;\n          }\n\n          newIndices[offset++] = sampleFrameIdx;\n        }\n\n        target._count = offset;\n        target._indices = newIndices;\n\n        target._updateGetRawIdx();\n\n        return target;\n      };\n      /**\n       * Data iteration\n       * @param ctx default this\n       * @example\n       *  list.each('x', function (x, idx) {});\n       *  list.each(['x', 'y'], function (x, y, idx) {});\n       *  list.each(function (idx) {})\n       */\n\n\n      DataStore.prototype.each = function (dims, cb) {\n        if (!this._count) {\n          return;\n        }\n\n        var dimSize = dims.length;\n        var chunks = this._chunks;\n\n        for (var i = 0, len = this.count(); i < len; i++) {\n          var rawIdx = this.getRawIndex(i); // Simple optimization\n\n          switch (dimSize) {\n            case 0:\n              cb(i);\n              break;\n\n            case 1:\n              cb(chunks[dims[0]][rawIdx], i);\n              break;\n\n            case 2:\n              cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);\n              break;\n\n            default:\n              var k = 0;\n              var value = [];\n\n              for (; k < dimSize; k++) {\n                value[k] = chunks[dims[k]][rawIdx];\n              } // Index\n\n\n              value[k] = i;\n              cb.apply(null, value);\n          }\n        }\n      };\n      /**\n       * Get extent of data in one dimension\n       */\n\n\n      DataStore.prototype.getDataExtent = function (dim) {\n        // Make sure use concrete dim as cache name.\n        var dimData = this._chunks[dim];\n        var initialExtent = getInitialExtent();\n\n        if (!dimData) {\n          return initialExtent;\n        } // Make more strict checkings to ensure hitting cache.\n\n\n        var currEnd = this.count(); // Consider the most cases when using data zoom, `getDataExtent`\n        // happened before filtering. We cache raw extent, which is not\n        // necessary to be cleared and recalculated when restore data.\n\n        var useRaw = !this._indices;\n        var dimExtent;\n\n        if (useRaw) {\n          return this._rawExtent[dim].slice();\n        }\n\n        dimExtent = this._extent[dim];\n\n        if (dimExtent) {\n          return dimExtent.slice();\n        }\n\n        dimExtent = initialExtent;\n        var min = dimExtent[0];\n        var max = dimExtent[1];\n\n        for (var i = 0; i < currEnd; i++) {\n          var rawIdx = this.getRawIndex(i);\n          var value = dimData[rawIdx];\n          value < min && (min = value);\n          value > max && (max = value);\n        }\n\n        dimExtent = [min, max];\n        this._extent[dim] = dimExtent;\n        return dimExtent;\n      };\n      /**\n       * Get raw data item\n       */\n\n\n      DataStore.prototype.getRawDataItem = function (idx) {\n        var rawIdx = this.getRawIndex(idx);\n\n        if (!this._provider.persistent) {\n          var val = [];\n          var chunks = this._chunks;\n\n          for (var i = 0; i < chunks.length; i++) {\n            val.push(chunks[i][rawIdx]);\n          }\n\n          return val;\n        } else {\n          return this._provider.getItem(rawIdx);\n        }\n      };\n      /**\n       * Clone shallow.\n       *\n       * @param clonedDims Determine which dims to clone. Will share the data if not specified.\n       */\n\n\n      DataStore.prototype.clone = function (clonedDims, ignoreIndices) {\n        var target = new DataStore();\n        var chunks = this._chunks;\n        var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {\n          obj[dimIdx] = true;\n          return obj;\n        }, {});\n\n        if (clonedDimsMap) {\n          for (var i = 0; i < chunks.length; i++) {\n            // Not clone if dim is not picked.\n            target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);\n          }\n        } else {\n          target._chunks = chunks;\n        }\n\n        this._copyCommonProps(target);\n\n        if (!ignoreIndices) {\n          target._indices = this._cloneIndices();\n        }\n\n        target._updateGetRawIdx();\n\n        return target;\n      };\n\n      DataStore.prototype._copyCommonProps = function (target) {\n        target._count = this._count;\n        target._rawCount = this._rawCount;\n        target._provider = this._provider;\n        target._dimensions = this._dimensions;\n        target._extent = clone(this._extent);\n        target._rawExtent = clone(this._rawExtent);\n      };\n\n      DataStore.prototype._cloneIndices = function () {\n        if (this._indices) {\n          var Ctor = this._indices.constructor;\n          var indices = void 0;\n\n          if (Ctor === Array) {\n            var thisCount = this._indices.length;\n            indices = new Ctor(thisCount);\n\n            for (var i = 0; i < thisCount; i++) {\n              indices[i] = this._indices[i];\n            }\n          } else {\n            indices = new Ctor(this._indices);\n          }\n\n          return indices;\n        }\n\n        return null;\n      };\n\n      DataStore.prototype._getRawIdxIdentity = function (idx) {\n        return idx;\n      };\n\n      DataStore.prototype._getRawIdx = function (idx) {\n        if (idx < this._count && idx >= 0) {\n          return this._indices[idx];\n        }\n\n        return -1;\n      };\n\n      DataStore.prototype._updateGetRawIdx = function () {\n        this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;\n      };\n\n      DataStore.internalField = function () {\n        function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {\n          return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);\n        }\n\n        defaultDimValueGetters = {\n          arrayRows: getDimValueSimply,\n          objectRows: function (dataItem, property, dataIndex, dimIndex) {\n            return parseDataValue(dataItem[property], this._dimensions[dimIndex]);\n          },\n          keyedColumns: getDimValueSimply,\n          original: function (dataItem, property, dataIndex, dimIndex) {\n            // Performance sensitive, do not use modelUtil.getDataItemValue.\n            // If dataItem is an plain object with no value field, the let `value`\n            // will be assigned with the object, but it will be tread correctly\n            // in the `convertValue`.\n            var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);\n            return parseDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.\n            : value, this._dimensions[dimIndex]);\n          },\n          typedArray: function (dataItem, property, dataIndex, dimIndex) {\n            return dataItem[dimIndex];\n          }\n        };\n      }();\n\n      return DataStore;\n    }();\n\n    /**\n     * [REQUIREMENT_MEMO]:\n     * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option.\n     * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and\n     * `root-dataset`. Them on `series` has higher priority.\n     * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might\n     * confuse users: whether those props indicate how to visit the upstream source or visit\n     * the transform result source, and some transforms has nothing to do with these props,\n     * and some transforms might have multiple upstream.\n     * (3) Transforms should specify `metaRawOption` in each output, just like they can be\n     * declared in `root-dataset`.\n     * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms.\n     * That is for reducing complexity in transforms.\n     * PENDING: Whether to provide transposition transform?\n     *\n     * [IMPLEMENTAION_MEMO]:\n     * \"sourceVisitConfig\" are calculated from `metaRawOption` and `data`.\n     * They will not be calculated until `source` is about to be visited (to prevent from\n     * duplicate calcuation). `source` is visited only in series and input to transforms.\n     *\n     * [DIMENSION_INHERIT_RULE]:\n     * By default the dimensions are inherited from ancestors, unless a transform return\n     * a new dimensions definition.\n     * Consider the case:\n     * ```js\n     * dataset: [{\n     *     source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...]\n     * }, {\n     *     transform: { type: 'filter', ... }\n     * }]\n     * dataset: [{\n     *     dimension: ['Product', 'Sales', 'Prise'],\n     *     source: [ ['Cookies', 321, 44.21], ...]\n     * }, {\n     *     transform: { type: 'filter', ... }\n     * }]\n     * ```\n     * The two types of option should have the same behavior after transform.\n     *\n     *\n     * [SCENARIO]:\n     * (1) Provide source data directly:\n     * ```js\n     * series: {\n     *     encode: {...},\n     *     dimensions: [...]\n     *     seriesLayoutBy: 'row',\n     *     data: [[...]]\n     * }\n     * ```\n     * (2) Series refer to dataset.\n     * ```js\n     * series: [{\n     *     encode: {...}\n     *     // Ignore datasetIndex means `datasetIndex: 0`\n     *     // and the dimensions defination in dataset is used\n     * }, {\n     *     encode: {...},\n     *     seriesLayoutBy: 'column',\n     *     datasetIndex: 1\n     * }]\n     * ```\n     * (3) dataset transform\n     * ```js\n     * dataset: [{\n     *     source: [...]\n     * }, {\n     *     source: [...]\n     * }, {\n     *     // By default from 0.\n     *     transform: { type: 'filter', config: {...} }\n     * }, {\n     *     // Piped.\n     *     transform: [\n     *         { type: 'filter', config: {...} },\n     *         { type: 'sort', config: {...} }\n     *     ]\n     * }, {\n     *     id: 'regressionData',\n     *     fromDatasetIndex: 1,\n     *     // Third-party transform\n     *     transform: { type: 'ecStat:regression', config: {...} }\n     * }, {\n     *     // retrieve the extra result.\n     *     id: 'regressionFormula',\n     *     fromDatasetId: 'regressionData',\n     *     fromTransformResult: 1\n     * }]\n     * ```\n     */\n\n    var SourceManager =\n    /** @class */\n    function () {\n      function SourceManager(sourceHost) {\n        // Cached source. Do not repeat calculating if not dirty.\n        this._sourceList = [];\n        this._storeList = []; // version sign of each upstream source manager.\n\n        this._upstreamSignList = [];\n        this._versionSignBase = 0;\n        this._dirty = true;\n        this._sourceHost = sourceHost;\n      }\n      /**\n       * Mark dirty.\n       */\n\n\n      SourceManager.prototype.dirty = function () {\n        this._setLocalSource([], []);\n\n        this._storeList = [];\n        this._dirty = true;\n      };\n\n      SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {\n        this._sourceList = sourceList;\n        this._upstreamSignList = upstreamSignList;\n        this._versionSignBase++;\n\n        if (this._versionSignBase > 9e10) {\n          this._versionSignBase = 0;\n        }\n      };\n      /**\n       * For detecting whether the upstream source is dirty, so that\n       * the local cached source (in `_sourceList`) should be discarded.\n       */\n\n\n      SourceManager.prototype._getVersionSign = function () {\n        return this._sourceHost.uid + '_' + this._versionSignBase;\n      };\n      /**\n       * Always return a source instance. Otherwise throw error.\n       */\n\n\n      SourceManager.prototype.prepareSource = function () {\n        // For the case that call `setOption` multiple time but no data changed,\n        // cache the result source to prevent from repeating transform.\n        if (this._isDirty()) {\n          this._createSource();\n\n          this._dirty = false;\n        }\n      };\n\n      SourceManager.prototype._createSource = function () {\n        this._setLocalSource([], []);\n\n        var sourceHost = this._sourceHost;\n\n        var upSourceMgrList = this._getUpstreamSourceManagers();\n\n        var hasUpstream = !!upSourceMgrList.length;\n        var resultSourceList;\n        var upstreamSignList;\n\n        if (isSeries(sourceHost)) {\n          var seriesModel = sourceHost;\n          var data = void 0;\n          var sourceFormat = void 0;\n          var upSource = void 0; // Has upstream dataset\n\n          if (hasUpstream) {\n            var upSourceMgr = upSourceMgrList[0];\n            upSourceMgr.prepareSource();\n            upSource = upSourceMgr.getSource();\n            data = upSource.data;\n            sourceFormat = upSource.sourceFormat;\n            upstreamSignList = [upSourceMgr._getVersionSign()];\n          } // Series data is from own.\n          else {\n              data = seriesModel.get('data', true);\n              sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;\n              upstreamSignList = [];\n            } // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root.\n\n\n          var newMetaRawOption = this._getSourceMetaRawOption() || {};\n          var upMetaRawOption = upSource && upSource.metaRawOption || {};\n          var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null;\n          var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); // Note here we should not use `upSource.dimensionsDefine`. Consider the case:\n          // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`,\n          // but series need `seriesLayoutBy: 'row'`.\n\n          var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); // We share source with dataset as much as possible\n          // to avoid extra memory cost of high dimensional data.\n\n          var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions;\n          resultSourceList = needsCreateSource ? [createSource(data, {\n            seriesLayoutBy: seriesLayoutBy,\n            sourceHeader: sourceHeader,\n            dimensions: dimensions\n          }, sourceFormat)] : [];\n        } else {\n          var datasetModel = sourceHost; // Has upstream dataset.\n\n          if (hasUpstream) {\n            var result = this._applyTransform(upSourceMgrList);\n\n            resultSourceList = result.sourceList;\n            upstreamSignList = result.upstreamSignList;\n          } // Is root dataset.\n          else {\n              var sourceData = datasetModel.get('source', true);\n              resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)];\n              upstreamSignList = [];\n            }\n        }\n\n        if (\"development\" !== 'production') {\n          assert(resultSourceList && upstreamSignList);\n        }\n\n        this._setLocalSource(resultSourceList, upstreamSignList);\n      };\n\n      SourceManager.prototype._applyTransform = function (upMgrList) {\n        var datasetModel = this._sourceHost;\n        var transformOption = datasetModel.get('transform', true);\n        var fromTransformResult = datasetModel.get('fromTransformResult', true);\n\n        if (\"development\" !== 'production') {\n          assert(fromTransformResult != null || transformOption != null);\n        }\n\n        if (fromTransformResult != null) {\n          var errMsg = '';\n\n          if (upMgrList.length !== 1) {\n            if (\"development\" !== 'production') {\n              errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset';\n            }\n\n            doThrow(errMsg);\n          }\n        }\n\n        var sourceList;\n        var upSourceList = [];\n        var upstreamSignList = [];\n        each(upMgrList, function (upMgr) {\n          upMgr.prepareSource();\n          var upSource = upMgr.getSource(fromTransformResult || 0);\n          var errMsg = '';\n\n          if (fromTransformResult != null && !upSource) {\n            if (\"development\" !== 'production') {\n              errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult;\n            }\n\n            doThrow(errMsg);\n          }\n\n          upSourceList.push(upSource);\n          upstreamSignList.push(upMgr._getVersionSign());\n        });\n\n        if (transformOption) {\n          sourceList = applyDataTransform(transformOption, upSourceList, {\n            datasetIndex: datasetModel.componentIndex\n          });\n        } else if (fromTransformResult != null) {\n          sourceList = [cloneSourceShallow(upSourceList[0])];\n        }\n\n        return {\n          sourceList: sourceList,\n          upstreamSignList: upstreamSignList\n        };\n      };\n\n      SourceManager.prototype._isDirty = function () {\n        if (this._dirty) {\n          return true;\n        } // All sourceList is from the some upstream.\n\n\n        var upSourceMgrList = this._getUpstreamSourceManagers();\n\n        for (var i = 0; i < upSourceMgrList.length; i++) {\n          var upSrcMgr = upSourceMgrList[i];\n\n          if ( // Consider the case that there is ancestor diry, call it recursively.\n          // The performance is probably not an issue because usually the chain is not long.\n          upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {\n            return true;\n          }\n        }\n      };\n      /**\n       * @param sourceIndex By default 0, means \"main source\".\n       *                    In most cases there is only one source.\n       */\n\n\n      SourceManager.prototype.getSource = function (sourceIndex) {\n        sourceIndex = sourceIndex || 0;\n        var source = this._sourceList[sourceIndex];\n\n        if (!source) {\n          // Series may share source instance with dataset.\n          var upSourceMgrList = this._getUpstreamSourceManagers();\n\n          return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex);\n        }\n\n        return source;\n      };\n      /**\n       *\n       * Get a data store which can be shared across series.\n       * Only available for series.\n       *\n       * @param seriesDimRequest Dimensions that are generated in series.\n       *        Should have been sorted by `storeDimIndex` asc.\n       */\n\n\n      SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) {\n        if (\"development\" !== 'production') {\n          assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.');\n        }\n\n        var schema = seriesDimRequest.makeStoreSchema();\n        return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash);\n      };\n\n      SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) {\n        // TODO Can use other sourceIndex?\n        var sourceIndex = 0;\n        var storeList = this._storeList;\n        var cachedStoreMap = storeList[sourceIndex];\n\n        if (!cachedStoreMap) {\n          cachedStoreMap = storeList[sourceIndex] = {};\n        }\n\n        var cachedStore = cachedStoreMap[sourceReadKey];\n\n        if (!cachedStore) {\n          var upSourceMgr = this._getUpstreamSourceManagers()[0];\n\n          if (isSeries(this._sourceHost) && upSourceMgr) {\n            cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey);\n          } else {\n            cachedStore = new DataStore(); // Always create store from source of series.\n\n            cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims);\n          }\n\n          cachedStoreMap[sourceReadKey] = cachedStore;\n        }\n\n        return cachedStore;\n      };\n      /**\n       * PENDING: Is it fast enough?\n       * If no upstream, return empty array.\n       */\n\n\n      SourceManager.prototype._getUpstreamSourceManagers = function () {\n        // Always get the relationship from the raw option.\n        // Do not cache the link of the dependency graph, so that\n        // there is no need to update them when change happens.\n        var sourceHost = this._sourceHost;\n\n        if (isSeries(sourceHost)) {\n          var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);\n          return !datasetModel ? [] : [datasetModel.getSourceManager()];\n        } else {\n          return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {\n            return datasetModel.getSourceManager();\n          });\n        }\n      };\n\n      SourceManager.prototype._getSourceMetaRawOption = function () {\n        var sourceHost = this._sourceHost;\n        var seriesLayoutBy;\n        var sourceHeader;\n        var dimensions;\n\n        if (isSeries(sourceHost)) {\n          seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);\n          sourceHeader = sourceHost.get('sourceHeader', true);\n          dimensions = sourceHost.get('dimensions', true);\n        } // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them.\n        else if (!this._getUpstreamSourceManagers().length) {\n            var model = sourceHost;\n            seriesLayoutBy = model.get('seriesLayoutBy', true);\n            sourceHeader = model.get('sourceHeader', true);\n            dimensions = model.get('dimensions', true);\n          }\n\n        return {\n          seriesLayoutBy: seriesLayoutBy,\n          sourceHeader: sourceHeader,\n          dimensions: dimensions\n        };\n      };\n\n      return SourceManager;\n    }();\n    // disable the transform merge, but do not disable transform clone from rawOption.\n\n    function disableTransformOptionMerge(datasetModel) {\n      var transformOption = datasetModel.option.transform;\n      transformOption && setAsPrimitive(datasetModel.option.transform);\n    }\n\n    function isSeries(sourceHost) {\n      // Avoid circular dependency with Series.ts\n      return sourceHost.mainType === 'series';\n    }\n\n    function doThrow(errMsg) {\n      throw new Error(errMsg);\n    }\n\n    var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; // TODO: more textStyle option\n\n    function getTooltipTextStyle(textStyle, renderMode) {\n      var nameFontColor = textStyle.color || '#6e7079';\n      var nameFontSize = textStyle.fontSize || 12;\n      var nameFontWeight = textStyle.fontWeight || '400';\n      var valueFontColor = textStyle.color || '#464646';\n      var valueFontSize = textStyle.fontSize || 14;\n      var valueFontWeight = textStyle.fontWeight || '900';\n\n      if (renderMode === 'html') {\n        // `textStyle` is probably from user input, should be encoded to reduce security risk.\n        return {\n          // eslint-disable-next-line max-len\n          nameStyle: \"font-size:\" + encodeHTML(nameFontSize + '') + \"px;color:\" + encodeHTML(nameFontColor) + \";font-weight:\" + encodeHTML(nameFontWeight + ''),\n          // eslint-disable-next-line max-len\n          valueStyle: \"font-size:\" + encodeHTML(valueFontSize + '') + \"px;color:\" + encodeHTML(valueFontColor) + \";font-weight:\" + encodeHTML(valueFontWeight + '')\n        };\n      } else {\n        return {\n          nameStyle: {\n            fontSize: nameFontSize,\n            fill: nameFontColor,\n            fontWeight: nameFontWeight\n          },\n          valueStyle: {\n            fontSize: valueFontSize,\n            fill: valueFontColor,\n            fontWeight: valueFontWeight\n          }\n        };\n      }\n    } // See `TooltipMarkupLayoutIntent['innerGapLevel']`.\n    // (value from UI design)\n\n\n    var HTML_GAPS = [0, 10, 20, 30];\n    var RICH_TEXT_GAPS = ['', '\\n', '\\n\\n', '\\n\\n\\n']; // eslint-disable-next-line max-len\n\n    function createTooltipMarkup(type, option) {\n      option.type = type;\n      return option;\n    }\n\n    function isSectionFragment(frag) {\n      return frag.type === 'section';\n    }\n\n    function getBuilder(frag) {\n      return isSectionFragment(frag) ? buildSection : buildNameValue;\n    }\n\n    function getBlockGapLevel(frag) {\n      if (isSectionFragment(frag)) {\n        var gapLevel_1 = 0;\n        var subBlockLen = frag.blocks.length;\n        var hasInnerGap_1 = subBlockLen > 1 || subBlockLen > 0 && !frag.noHeader;\n        each(frag.blocks, function (subBlock) {\n          var subGapLevel = getBlockGapLevel(subBlock); // If the some of the sub-blocks have some gaps (like 10px) inside, this block\n          // should use a larger gap (like 20px) to distinguish those sub-blocks.\n\n          if (subGapLevel >= gapLevel_1) {\n            gapLevel_1 = subGapLevel + +(hasInnerGap_1 && ( // 0 always can not be readable gap level.\n            !subGapLevel // If no header, always keep the sub gap level. Otherwise\n            // look weird in case `multipleSeries`.\n            || isSectionFragment(subBlock) && !subBlock.noHeader));\n          }\n        });\n        return gapLevel_1;\n      }\n\n      return 0;\n    }\n\n    function buildSection(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {\n      var noHeader = fragment.noHeader;\n      var gaps = getGap(getBlockGapLevel(fragment));\n      var subMarkupTextList = [];\n      var subBlocks = fragment.blocks || [];\n      assert(!subBlocks || isArray(subBlocks));\n      subBlocks = subBlocks || [];\n      var orderMode = ctx.orderMode;\n\n      if (fragment.sortBlocks && orderMode) {\n        subBlocks = subBlocks.slice();\n        var orderMap = {\n          valueAsc: 'asc',\n          valueDesc: 'desc'\n        };\n\n        if (hasOwn(orderMap, orderMode)) {\n          var comparator_1 = new SortOrderComparator(orderMap[orderMode], null);\n          subBlocks.sort(function (a, b) {\n            return comparator_1.evaluate(a.sortParam, b.sortParam);\n          });\n        } // FIXME 'seriesDesc' necessary?\n        else if (orderMode === 'seriesDesc') {\n            subBlocks.reverse();\n          }\n      }\n\n      each(subBlocks, function (subBlock, idx) {\n        var valueFormatter = fragment.valueFormatter;\n        var subMarkupText = getBuilder(subBlock)( // Inherit valueFormatter\n        valueFormatter ? extend(extend({}, ctx), {\n          valueFormatter: valueFormatter\n        }) : ctx, subBlock, idx > 0 ? gaps.html : 0, toolTipTextStyle);\n        subMarkupText != null && subMarkupTextList.push(subMarkupText);\n      });\n      var subMarkupText = ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), noHeader ? topMarginForOuterGap : gaps.html);\n\n      if (noHeader) {\n        return subMarkupText;\n      }\n\n      var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC);\n      var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle;\n\n      if (ctx.renderMode === 'richText') {\n        return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText;\n      } else {\n        return wrapBlockHTML(\"<div style=\\\"\" + nameStyle + \";\" + TOOLTIP_LINE_HEIGHT_CSS + \";\\\">\" + encodeHTML(displayableHeader) + '</div>' + subMarkupText, topMarginForOuterGap);\n      }\n    }\n\n    function buildNameValue(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {\n      var renderMode = ctx.renderMode;\n      var noName = fragment.noName;\n      var noValue = fragment.noValue;\n      var noMarker = !fragment.markerType;\n      var name = fragment.name;\n      var useUTC = ctx.useUTC;\n\n      var valueFormatter = fragment.valueFormatter || ctx.valueFormatter || function (value) {\n        value = isArray(value) ? value : [value];\n        return map(value, function (val, idx) {\n          return makeValueReadable(val, isArray(valueTypeOption) ? valueTypeOption[idx] : valueTypeOption, useUTC);\n        });\n      };\n\n      if (noName && noValue) {\n        return;\n      }\n\n      var markerStr = noMarker ? '' : ctx.markupStyleCreator.makeTooltipMarker(fragment.markerType, fragment.markerColor || '#333', renderMode);\n      var readableName = noName ? '' : makeValueReadable(name, 'ordinal', useUTC);\n      var valueTypeOption = fragment.valueType;\n      var readableValueList = noValue ? [] : valueFormatter(fragment.value);\n      var valueAlignRight = !noMarker || !noName; // It little weird if only value next to marker but far from marker.\n\n      var valueCloseToMarker = !noMarker && noName;\n\n      var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),\n          nameStyle = _a.nameStyle,\n          valueStyle = _a.valueStyle;\n\n      return renderMode === 'richText' ? (noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameRichText(ctx, readableName, nameStyle)) // Value has commas inside, so use ' ' as delimiter for multiple values.\n      + (noValue ? '' : wrapInlineValueRichText(ctx, readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)) : wrapBlockHTML((noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameHTML(readableName, !noMarker, nameStyle)) + (noValue ? '' : wrapInlineValueHTML(readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)), topMarginForOuterGap);\n    }\n    /**\n     * @return markupText. null/undefined means no content.\n     */\n\n\n    function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {\n      if (!fragment) {\n        return;\n      }\n\n      var builder = getBuilder(fragment);\n      var ctx = {\n        useUTC: useUTC,\n        renderMode: renderMode,\n        orderMode: orderMode,\n        markupStyleCreator: markupStyleCreator,\n        valueFormatter: fragment.valueFormatter\n      };\n      return builder(ctx, fragment, 0, toolTipTextStyle);\n    }\n\n    function getGap(gapLevel) {\n      return {\n        html: HTML_GAPS[gapLevel],\n        richText: RICH_TEXT_GAPS[gapLevel]\n      };\n    }\n\n    function wrapBlockHTML(encodedContent, topGap) {\n      var clearfix = '<div style=\"clear:both\"></div>';\n      var marginCSS = \"margin: \" + topGap + \"px 0 0\";\n      return \"<div style=\\\"\" + marginCSS + \";\" + TOOLTIP_LINE_HEIGHT_CSS + \";\\\">\" + encodedContent + clearfix + '</div>';\n    }\n\n    function wrapInlineNameHTML(name, leftHasMarker, style) {\n      var marginCss = leftHasMarker ? 'margin-left:2px' : '';\n      return \"<span style=\\\"\" + style + \";\" + marginCss + \"\\\">\" + encodeHTML(name) + '</span>';\n    }\n\n    function wrapInlineValueHTML(valueList, alignRight, valueCloseToMarker, style) {\n      // Do not too close to marker, considering there are multiple values separated by spaces.\n      var paddingStr = valueCloseToMarker ? '10px' : '20px';\n      var alignCSS = alignRight ? \"float:right;margin-left:\" + paddingStr : '';\n      valueList = isArray(valueList) ? valueList : [valueList];\n      return \"<span style=\\\"\" + alignCSS + \";\" + style + \"\\\">\" // Value has commas inside, so use '  ' as delimiter for multiple values.\n      + map(valueList, function (value) {\n        return encodeHTML(value);\n      }).join('&nbsp;&nbsp;') + '</span>';\n    }\n\n    function wrapInlineNameRichText(ctx, name, style) {\n      return ctx.markupStyleCreator.wrapRichTextStyle(name, style);\n    }\n\n    function wrapInlineValueRichText(ctx, values, alignRight, valueCloseToMarker, style) {\n      var styles = [style];\n      var paddingLeft = valueCloseToMarker ? 10 : 20;\n      alignRight && styles.push({\n        padding: [0, 0, 0, paddingLeft],\n        align: 'right'\n      }); // Value has commas inside, so use '  ' as delimiter for multiple values.\n\n      return ctx.markupStyleCreator.wrapRichTextStyle(isArray(values) ? values.join('  ') : values, styles);\n    }\n\n    function retrieveVisualColorForTooltipMarker(series, dataIndex) {\n      var style = series.getData().getItemVisual(dataIndex, 'style');\n      var color = style[series.visualDrawType];\n      return convertToColorString(color);\n    }\n    function getPaddingFromTooltipModel(model, renderMode) {\n      var padding = model.get('padding');\n      return padding != null ? padding // We give slightly different to look pretty.\n      : renderMode === 'richText' ? [8, 10] : 10;\n    }\n    /**\n     * The major feature is generate styles for `renderMode: 'richText'`.\n     * But it also serves `renderMode: 'html'` to provide\n     * \"renderMode-independent\" API.\n     */\n\n    var TooltipMarkupStyleCreator =\n    /** @class */\n    function () {\n      function TooltipMarkupStyleCreator() {\n        this.richTextStyles = {}; // Notice that \"generate a style name\" usuall happens repeatly when mouse moving and\n        // displaying a tooltip. So we put the `_nextStyleNameId` as a member of each creator\n        // rather than static shared by all creators (which will cause it increase to fast).\n\n        this._nextStyleNameId = getRandomIdBase();\n      }\n\n      TooltipMarkupStyleCreator.prototype._generateStyleName = function () {\n        return '__EC_aUTo_' + this._nextStyleNameId++;\n      };\n\n      TooltipMarkupStyleCreator.prototype.makeTooltipMarker = function (markerType, colorStr, renderMode) {\n        var markerId = renderMode === 'richText' ? this._generateStyleName() : null;\n        var marker = getTooltipMarker({\n          color: colorStr,\n          type: markerType,\n          renderMode: renderMode,\n          markerId: markerId\n        });\n\n        if (isString(marker)) {\n          return marker;\n        } else {\n          if (\"development\" !== 'production') {\n            assert(markerId);\n          }\n\n          this.richTextStyles[markerId] = marker.style;\n          return marker.content;\n        }\n      };\n      /**\n       * @usage\n       * ```ts\n       * const styledText = markupStyleCreator.wrapRichTextStyle([\n       *     // The styles will be auto merged.\n       *     {\n       *         fontSize: 12,\n       *         color: 'blue'\n       *     },\n       *     {\n       *         padding: 20\n       *     }\n       * ]);\n       * ```\n       */\n\n\n      TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {\n        var finalStl = {};\n\n        if (isArray(styles)) {\n          each(styles, function (stl) {\n            return extend(finalStl, stl);\n          });\n        } else {\n          extend(finalStl, styles);\n        }\n\n        var styleName = this._generateStyleName();\n\n        this.richTextStyles[styleName] = finalStl;\n        return \"{\" + styleName + \"|\" + text + \"}\";\n      };\n\n      return TooltipMarkupStyleCreator;\n    }();\n\n    function defaultSeriesFormatTooltip(opt) {\n      var series = opt.series;\n      var dataIndex = opt.dataIndex;\n      var multipleSeries = opt.multipleSeries;\n      var data = series.getData();\n      var tooltipDims = data.mapDimensionsAll('defaultedTooltip');\n      var tooltipDimLen = tooltipDims.length;\n      var value = series.getRawValue(dataIndex);\n      var isValueArr = isArray(value);\n      var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip.\n\n      var inlineValue;\n      var inlineValueType;\n      var subBlocks;\n      var sortParam;\n\n      if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) {\n        var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);\n        inlineValue = formatArrResult.inlineValues;\n        inlineValueType = formatArrResult.inlineValueTypes;\n        subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases.\n\n        sortParam = formatArrResult.inlineValues[0];\n      } else if (tooltipDimLen) {\n        var dimInfo = data.getDimensionInfo(tooltipDims[0]);\n        sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]);\n        inlineValueType = dimInfo.type;\n      } else {\n        sortParam = inlineValue = isValueArr ? value[0] : value;\n      } // Do not show generated series name. It might not be readable.\n\n\n      var seriesNameSpecified = isNameSpecified(series);\n      var seriesName = seriesNameSpecified && series.name || '';\n      var itemName = data.getName(dataIndex);\n      var inlineName = multipleSeries ? seriesName : itemName;\n      return createTooltipMarkup('section', {\n        header: seriesName,\n        // When series name not specified, do not show a header line with only '-'.\n        // This case alway happen in tooltip.trigger: 'item'.\n        noHeader: multipleSeries || !seriesNameSpecified,\n        sortParam: sortParam,\n        blocks: [createTooltipMarkup('nameValue', {\n          markerType: 'item',\n          markerColor: markerColor,\n          // Do not mix display seriesName and itemName in one tooltip,\n          // which might confuses users.\n          name: inlineName,\n          // name dimension might be auto assigned, where the name might\n          // be not readable. So we check trim here.\n          noName: !trim(inlineName),\n          value: inlineValue,\n          valueType: inlineValueType\n        })].concat(subBlocks || [])\n      });\n    }\n\n    function formatTooltipArrayValue(value, series, dataIndex, tooltipDims, colorStr) {\n      // check: category-no-encode-has-axis-data in dataset.html\n      var data = series.getData();\n      var isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) {\n        var dimItem = data.getDimensionInfo(idx);\n        return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null;\n      }, false);\n      var inlineValues = [];\n      var inlineValueTypes = [];\n      var blocks = [];\n      tooltipDims.length ? each(tooltipDims, function (dim) {\n        setEachItem(retrieveRawValue(data, dataIndex, dim), dim);\n      }) // By default, all dims is used on tooltip.\n      : each(value, setEachItem);\n\n      function setEachItem(val, dim) {\n        var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip.\n\n        if (!dimInfo || dimInfo.otherDims.tooltip === false) {\n          return;\n        }\n\n        if (isValueMultipleLine) {\n          blocks.push(createTooltipMarkup('nameValue', {\n            markerType: 'subItem',\n            markerColor: colorStr,\n            name: dimInfo.displayName,\n            value: val,\n            valueType: dimInfo.type\n          }));\n        } else {\n          inlineValues.push(val);\n          inlineValueTypes.push(dimInfo.type);\n        }\n      }\n\n      return {\n        inlineValues: inlineValues,\n        inlineValueTypes: inlineValueTypes,\n        blocks: blocks\n      };\n    }\n\n    var inner$1 = makeInner();\n\n    function getSelectionKey(data, dataIndex) {\n      return data.getName(dataIndex) || data.getId(dataIndex);\n    }\n\n    var SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled';\n\n    var SeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(SeriesModel, _super);\n\n      function SeriesModel() {\n        // [Caution]: Because this class or desecendants can be used as `XXX.extend(subProto)`,\n        // the class members must not be initialized in constructor or declaration place.\n        // Otherwise there is bad case:\n        //   class A {xxx = 1;}\n        //   enableClassExtend(A);\n        //   class B extends A {}\n        //   var C = B.extend({xxx: 5});\n        //   var c = new C();\n        //   console.log(c.xxx); // expect 5 but always 1.\n        var _this = _super !== null && _super.apply(this, arguments) || this; // ---------------------------------------\n        // Props about data selection\n        // ---------------------------------------\n\n\n        _this._selectedDataIndicesMap = {};\n        return _this;\n      }\n\n      SeriesModel.prototype.init = function (option, parentModel, ecModel) {\n        this.seriesIndex = this.componentIndex;\n        this.dataTask = createTask({\n          count: dataTaskCount,\n          reset: dataTaskReset\n        });\n        this.dataTask.context = {\n          model: this\n        };\n        this.mergeDefaultAndTheme(option, ecModel);\n        var sourceManager = inner$1(this).sourceManager = new SourceManager(this);\n        sourceManager.prepareSource();\n        var data = this.getInitialData(option, ecModel);\n        wrapData(data, this);\n        this.dataTask.context.data = data;\n\n        if (\"development\" !== 'production') {\n          assert(data, 'getInitialData returned invalid data.');\n        }\n\n        inner$1(this).dataBeforeProcessed = data; // If we reverse the order (make data firstly, and then make\n        // dataBeforeProcessed by cloneShallow), cloneShallow will\n        // cause data.graph.data !== data when using\n        // module:echarts/data/Graph or module:echarts/data/Tree.\n        // See module:echarts/data/helper/linkSeriesData\n        // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model\n        // init or merge stage, because the data can be restored. So we do not `restoreData`\n        // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.\n        // Call `seriesModel.getRawData()` instead.\n        // this.restoreData();\n\n        autoSeriesName(this);\n\n        this._initSelectedMapFromData(data);\n      };\n      /**\n       * Util for merge default and theme to option\n       */\n\n\n      SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n        var layoutMode = fetchLayoutMode(this);\n        var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme.\n        // But if name duplicate between series subType\n        // (for example: parallel) add component mainType,\n        // add suffix 'Series'.\n\n        var themeSubType = this.subType;\n\n        if (ComponentModel.hasClass(themeSubType)) {\n          themeSubType += 'Series';\n        }\n\n        merge(option, ecModel.getTheme().get(this.subType));\n        merge(option, this.getDefaultOption()); // Default label emphasis `show`\n\n        defaultEmphasis(option, 'label', ['show']);\n        this.fillDataTextStyle(option.data);\n\n        if (layoutMode) {\n          mergeLayoutParam(option, inputPositionParams, layoutMode);\n        }\n      };\n\n      SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {\n        // this.settingTask.dirty();\n        newSeriesOption = merge(this.option, newSeriesOption, true);\n        this.fillDataTextStyle(newSeriesOption.data);\n        var layoutMode = fetchLayoutMode(this);\n\n        if (layoutMode) {\n          mergeLayoutParam(this.option, newSeriesOption, layoutMode);\n        }\n\n        var sourceManager = inner$1(this).sourceManager;\n        sourceManager.dirty();\n        sourceManager.prepareSource();\n        var data = this.getInitialData(newSeriesOption, ecModel);\n        wrapData(data, this);\n        this.dataTask.dirty();\n        this.dataTask.context.data = data;\n        inner$1(this).dataBeforeProcessed = data;\n        autoSeriesName(this);\n\n        this._initSelectedMapFromData(data);\n      };\n\n      SeriesModel.prototype.fillDataTextStyle = function (data) {\n        // Default data label emphasis `show`\n        // FIXME Tree structure data ?\n        // FIXME Performance ?\n        if (data && !isTypedArray(data)) {\n          var props = ['show'];\n\n          for (var i = 0; i < data.length; i++) {\n            if (data[i] && data[i].label) {\n              defaultEmphasis(data[i], 'label', props);\n            }\n          }\n        }\n      };\n      /**\n       * Init a data structure from data related option in series\n       * Must be overridden.\n       */\n\n\n      SeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return;\n      };\n      /**\n       * Append data to list\n       */\n\n\n      SeriesModel.prototype.appendData = function (params) {\n        // FIXME ???\n        // (1) If data from dataset, forbidden append.\n        // (2) support append data of dataset.\n        var data = this.getRawData();\n        data.appendData(params.data);\n      };\n      /**\n       * Consider some method like `filter`, `map` need make new data,\n       * We should make sure that `seriesModel.getData()` get correct\n       * data in the stream procedure. So we fetch data from upstream\n       * each time `task.perform` called.\n       */\n\n\n      SeriesModel.prototype.getData = function (dataType) {\n        var task = getCurrentTask(this);\n\n        if (task) {\n          var data = task.context.data;\n          return dataType == null ? data : data.getLinkedData(dataType);\n        } else {\n          // When series is not alive (that may happen when click toolbox\n          // restore or setOption with not merge mode), series data may\n          // be still need to judge animation or something when graphic\n          // elements want to know whether fade out.\n          return inner$1(this).data;\n        }\n      };\n\n      SeriesModel.prototype.getAllData = function () {\n        var mainData = this.getData();\n        return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{\n          data: mainData\n        }];\n      };\n\n      SeriesModel.prototype.setData = function (data) {\n        var task = getCurrentTask(this);\n\n        if (task) {\n          var context = task.context; // Consider case: filter, data sample.\n          // FIXME:TS never used, so comment it\n          // if (context.data !== data && task.modifyOutputEnd) {\n          //     task.setOutputEnd(data.count());\n          // }\n\n          context.outputData = data; // Caution: setData should update context.data,\n          // Because getData may be called multiply in a\n          // single stage and expect to get the data just\n          // set. (For example, AxisProxy, x y both call\n          // getData and setDate sequentially).\n          // So the context.data should be fetched from\n          // upstream each time when a stage starts to be\n          // performed.\n\n          if (task !== this.dataTask) {\n            context.data = data;\n          }\n        }\n\n        inner$1(this).data = data;\n      };\n\n      SeriesModel.prototype.getEncode = function () {\n        var encode = this.get('encode', true);\n\n        if (encode) {\n          return createHashMap(encode);\n        }\n      };\n\n      SeriesModel.prototype.getSourceManager = function () {\n        return inner$1(this).sourceManager;\n      };\n\n      SeriesModel.prototype.getSource = function () {\n        return this.getSourceManager().getSource();\n      };\n      /**\n       * Get data before processed\n       */\n\n\n      SeriesModel.prototype.getRawData = function () {\n        return inner$1(this).dataBeforeProcessed;\n      };\n\n      SeriesModel.prototype.getColorBy = function () {\n        var colorBy = this.get('colorBy');\n        return colorBy || 'series';\n      };\n\n      SeriesModel.prototype.isColorBySeries = function () {\n        return this.getColorBy() === 'series';\n      };\n      /**\n       * Get base axis if has coordinate system and has axis.\n       * By default use coordSys.getBaseAxis();\n       * Can be overridden for some chart.\n       * @return {type} description\n       */\n\n\n      SeriesModel.prototype.getBaseAxis = function () {\n        var coordSys = this.coordinateSystem; // @ts-ignore\n\n        return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();\n      };\n      /**\n       * Default tooltip formatter\n       *\n       * @param dataIndex\n       * @param multipleSeries\n       * @param dataType\n       * @param renderMode valid values: 'html'(by default) and 'richText'.\n       *        'html' is used for rendering tooltip in extra DOM form, and the result\n       *        string is used as DOM HTML content.\n       *        'richText' is used for rendering tooltip in rich text form, for those where\n       *        DOM operation is not supported.\n       * @return formatted tooltip with `html` and `markers`\n       *        Notice: The override method can also return string\n       */\n\n\n      SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        return defaultSeriesFormatTooltip({\n          series: this,\n          dataIndex: dataIndex,\n          multipleSeries: multipleSeries\n        });\n      };\n\n      SeriesModel.prototype.isAnimationEnabled = function () {\n        var ecModel = this.ecModel; // Disable animation if using echarts in node but not give ssr flag.\n        // In ssr mode, renderToString will generate svg with css animation.\n\n        if (env.node && !(ecModel && ecModel.ssr)) {\n          return false;\n        }\n\n        var animationEnabled = this.getShallow('animation');\n\n        if (animationEnabled) {\n          if (this.getData().count() > this.getShallow('animationThreshold')) {\n            animationEnabled = false;\n          }\n        }\n\n        return !!animationEnabled;\n      };\n\n      SeriesModel.prototype.restoreData = function () {\n        this.dataTask.dirty();\n      };\n\n      SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {\n        var ecModel = this.ecModel; // PENDING\n\n        var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);\n\n        if (!color) {\n          color = ecModel.getColorFromPalette(name, scope, requestColorNum);\n        }\n\n        return color;\n      };\n      /**\n       * Use `data.mapDimensionsAll(coordDim)` instead.\n       * @deprecated\n       */\n\n\n      SeriesModel.prototype.coordDimToDataDim = function (coordDim) {\n        return this.getRawData().mapDimensionsAll(coordDim);\n      };\n      /**\n       * Get progressive rendering count each step\n       */\n\n\n      SeriesModel.prototype.getProgressive = function () {\n        return this.get('progressive');\n      };\n      /**\n       * Get progressive rendering count each step\n       */\n\n\n      SeriesModel.prototype.getProgressiveThreshold = function () {\n        return this.get('progressiveThreshold');\n      }; // PENGING If selectedMode is null ?\n\n\n      SeriesModel.prototype.select = function (innerDataIndices, dataType) {\n        this._innerSelect(this.getData(dataType), innerDataIndices);\n      };\n\n      SeriesModel.prototype.unselect = function (innerDataIndices, dataType) {\n        var selectedMap = this.option.selectedMap;\n\n        if (!selectedMap) {\n          return;\n        }\n\n        var selectedMode = this.option.selectedMode;\n        var data = this.getData(dataType);\n\n        if (selectedMode === 'series' || selectedMap === 'all') {\n          this.option.selectedMap = {};\n          this._selectedDataIndicesMap = {};\n          return;\n        }\n\n        for (var i = 0; i < innerDataIndices.length; i++) {\n          var dataIndex = innerDataIndices[i];\n          var nameOrId = getSelectionKey(data, dataIndex);\n          selectedMap[nameOrId] = false;\n          this._selectedDataIndicesMap[nameOrId] = -1;\n        }\n      };\n\n      SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) {\n        var tmpArr = [];\n\n        for (var i = 0; i < innerDataIndices.length; i++) {\n          tmpArr[0] = innerDataIndices[i];\n          this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType);\n        }\n      };\n\n      SeriesModel.prototype.getSelectedDataIndices = function () {\n        if (this.option.selectedMap === 'all') {\n          return [].slice.call(this.getData().getIndices());\n        }\n\n        var selectedDataIndicesMap = this._selectedDataIndicesMap;\n        var nameOrIds = keys(selectedDataIndicesMap);\n        var dataIndices = [];\n\n        for (var i = 0; i < nameOrIds.length; i++) {\n          var dataIndex = selectedDataIndicesMap[nameOrIds[i]];\n\n          if (dataIndex >= 0) {\n            dataIndices.push(dataIndex);\n          }\n        }\n\n        return dataIndices;\n      };\n\n      SeriesModel.prototype.isSelected = function (dataIndex, dataType) {\n        var selectedMap = this.option.selectedMap;\n\n        if (!selectedMap) {\n          return false;\n        }\n\n        var data = this.getData(dataType);\n        return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) && !data.getItemModel(dataIndex).get(['select', 'disabled']);\n      };\n\n      SeriesModel.prototype.isUniversalTransitionEnabled = function () {\n        if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) {\n          return true;\n        }\n\n        var universalTransitionOpt = this.option.universalTransition; // Quick reject\n\n        if (!universalTransitionOpt) {\n          return false;\n        }\n\n        if (universalTransitionOpt === true) {\n          return true;\n        } // Can be simply 'universalTransition: true'\n\n\n        return universalTransitionOpt && universalTransitionOpt.enabled;\n      };\n\n      SeriesModel.prototype._innerSelect = function (data, innerDataIndices) {\n        var _a, _b;\n\n        var option = this.option;\n        var selectedMode = option.selectedMode;\n        var len = innerDataIndices.length;\n\n        if (!selectedMode || !len) {\n          return;\n        }\n\n        if (selectedMode === 'series') {\n          option.selectedMap = 'all';\n        } else if (selectedMode === 'multiple') {\n          if (!isObject(option.selectedMap)) {\n            option.selectedMap = {};\n          }\n\n          var selectedMap = option.selectedMap;\n\n          for (var i = 0; i < len; i++) {\n            var dataIndex = innerDataIndices[i]; // TODO different types of data share same object.\n\n            var nameOrId = getSelectionKey(data, dataIndex);\n            selectedMap[nameOrId] = true;\n            this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex);\n          }\n        } else if (selectedMode === 'single' || selectedMode === true) {\n          var lastDataIndex = innerDataIndices[len - 1];\n          var nameOrId = getSelectionKey(data, lastDataIndex);\n          option.selectedMap = (_a = {}, _a[nameOrId] = true, _a);\n          this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b);\n        }\n      };\n\n      SeriesModel.prototype._initSelectedMapFromData = function (data) {\n        // Ignore select info in data if selectedMap exists.\n        // NOTE It's only for legacy usage. edge data is not supported.\n        if (this.option.selectedMap) {\n          return;\n        }\n\n        var dataIndices = [];\n\n        if (data.hasItemOption) {\n          data.each(function (idx) {\n            var rawItem = data.getRawDataItem(idx);\n\n            if (rawItem && rawItem.selected) {\n              dataIndices.push(idx);\n            }\n          });\n        }\n\n        if (dataIndices.length > 0) {\n          this._innerSelect(data, dataIndices);\n        }\n      }; // /**\n      //  * @see {module:echarts/stream/Scheduler}\n      //  */\n      // abstract pipeTask: null\n\n\n      SeriesModel.registerClass = function (clz) {\n        return ComponentModel.registerClass(clz);\n      };\n\n      SeriesModel.protoInitialize = function () {\n        var proto = SeriesModel.prototype;\n        proto.type = 'series.__base__';\n        proto.seriesIndex = 0;\n        proto.ignoreStyleOnData = false;\n        proto.hasSymbolVisual = false;\n        proto.defaultSymbol = 'circle'; // Make sure the values can be accessed!\n\n        proto.visualStyleAccessPath = 'itemStyle';\n        proto.visualDrawType = 'fill';\n      }();\n\n      return SeriesModel;\n    }(ComponentModel);\n\n    mixin(SeriesModel, DataFormatMixin);\n    mixin(SeriesModel, PaletteMixin);\n    mountExtend(SeriesModel, ComponentModel);\n    /**\n     * MUST be called after `prepareSource` called\n     * Here we need to make auto series, especially for auto legend. But we\n     * do not modify series.name in option to avoid side effects.\n     */\n\n    function autoSeriesName(seriesModel) {\n      // User specified name has higher priority, otherwise it may cause\n      // series can not be queried unexpectedly.\n      var name = seriesModel.name;\n\n      if (!isNameSpecified(seriesModel)) {\n        seriesModel.name = getSeriesAutoName(seriesModel) || name;\n      }\n    }\n\n    function getSeriesAutoName(seriesModel) {\n      var data = seriesModel.getRawData();\n      var dataDims = data.mapDimensionsAll('seriesName');\n      var nameArr = [];\n      each(dataDims, function (dataDim) {\n        var dimInfo = data.getDimensionInfo(dataDim);\n        dimInfo.displayName && nameArr.push(dimInfo.displayName);\n      });\n      return nameArr.join(' ');\n    }\n\n    function dataTaskCount(context) {\n      return context.model.getRawData().count();\n    }\n\n    function dataTaskReset(context) {\n      var seriesModel = context.model;\n      seriesModel.setData(seriesModel.getRawData().cloneShallow());\n      return dataTaskProgress;\n    }\n\n    function dataTaskProgress(param, context) {\n      // Avoid repead cloneShallow when data just created in reset.\n      if (context.outputData && param.end > context.outputData.count()) {\n        context.model.getRawData().cloneShallow(context.outputData);\n      }\n    } // TODO refactor\n\n\n    function wrapData(data, seriesModel) {\n      each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {\n        data.wrapMethod(methodName, curry(onDataChange, seriesModel));\n      });\n    }\n\n    function onDataChange(seriesModel, newList) {\n      var task = getCurrentTask(seriesModel);\n\n      if (task) {\n        // Consider case: filter, selectRange\n        task.setOutputEnd((newList || this).count());\n      }\n\n      return newList;\n    }\n\n    function getCurrentTask(seriesModel) {\n      var scheduler = (seriesModel.ecModel || {}).scheduler;\n      var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);\n\n      if (pipeline) {\n        // When pipline finished, the currrentTask keep the last\n        // task (renderTask).\n        var task = pipeline.currentTask;\n\n        if (task) {\n          var agentStubMap = task.agentStubMap;\n\n          if (agentStubMap) {\n            task = agentStubMap.get(seriesModel.uid);\n          }\n        }\n\n        return task;\n      }\n    }\n\n    var ComponentView =\n    /** @class */\n    function () {\n      function ComponentView() {\n        this.group = new Group();\n        this.uid = getUID('viewComponent');\n      }\n\n      ComponentView.prototype.init = function (ecModel, api) {};\n\n      ComponentView.prototype.render = function (model, ecModel, api, payload) {};\n\n      ComponentView.prototype.dispose = function (ecModel, api) {};\n\n      ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;\n      };\n\n      ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing;\n      };\n\n      ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing;\n      };\n      /**\n       * Hook for toggle blur target series.\n       * Can be used in marker for blur or leave blur the markers\n       */\n\n\n      ComponentView.prototype.toggleBlurSeries = function (seriesModels, isBlur, ecModel) {// Do nothing;\n      };\n      /**\n       * Traverse the new rendered elements.\n       *\n       * It will traverse the new added element in progressive rendering.\n       * And traverse all in normal rendering.\n       */\n\n\n      ComponentView.prototype.eachRendered = function (cb) {\n        var group = this.group;\n\n        if (group) {\n          group.traverse(cb);\n        }\n      };\n\n      return ComponentView;\n    }();\n    enableClassExtend(ComponentView);\n    enableClassManagement(ComponentView);\n\n    /**\n     * @return {string} If large mode changed, return string 'reset';\n     */\n\n    function createRenderPlanner() {\n      var inner = makeInner();\n      return function (seriesModel) {\n        var fields = inner(seriesModel);\n        var pipelineContext = seriesModel.pipelineContext;\n        var originalLarge = !!fields.large;\n        var originalProgressive = !!fields.progressiveRender; // FIXME: if the planner works on a filtered series, `pipelineContext` does not\n        // exists. See #11611 . Probably we need to modify this structure, see the comment\n        // on `performRawSeries` in `Schedular.js`.\n\n        var large = fields.large = !!(pipelineContext && pipelineContext.large);\n        var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender);\n        return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset';\n      };\n    }\n\n    var inner$2 = makeInner();\n    var renderPlanner = createRenderPlanner();\n\n    var ChartView =\n    /** @class */\n    function () {\n      function ChartView() {\n        this.group = new Group();\n        this.uid = getUID('viewChart');\n        this.renderTask = createTask({\n          plan: renderTaskPlan,\n          reset: renderTaskReset\n        });\n        this.renderTask.context = {\n          view: this\n        };\n      }\n\n      ChartView.prototype.init = function (ecModel, api) {};\n\n      ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        if (\"development\" !== 'production') {\n          throw new Error('render method must been implemented');\n        }\n      };\n      /**\n       * Highlight series or specified data item.\n       */\n\n\n      ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData(payload && payload.dataType);\n\n        if (!data) {\n          if (\"development\" !== 'production') {\n            error(\"Unknown dataType \" + payload.dataType);\n          }\n\n          return;\n        }\n\n        toggleHighlight(data, payload, 'emphasis');\n      };\n      /**\n       * Downplay series or specified data item.\n       */\n\n\n      ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData(payload && payload.dataType);\n\n        if (!data) {\n          if (\"development\" !== 'production') {\n            error(\"Unknown dataType \" + payload.dataType);\n          }\n\n          return;\n        }\n\n        toggleHighlight(data, payload, 'normal');\n      };\n      /**\n       * Remove self.\n       */\n\n\n      ChartView.prototype.remove = function (ecModel, api) {\n        this.group.removeAll();\n      };\n      /**\n       * Dispose self.\n       */\n\n\n      ChartView.prototype.dispose = function (ecModel, api) {};\n\n      ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      }; // FIXME never used?\n\n\n      ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      }; // FIXME never used?\n\n\n      ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      };\n      /**\n       * Traverse the new rendered elements.\n       *\n       * It will traverse the new added element in progressive rendering.\n       * And traverse all in normal rendering.\n       */\n\n\n      ChartView.prototype.eachRendered = function (cb) {\n        traverseElements(this.group, cb);\n      };\n\n      ChartView.markUpdateMethod = function (payload, methodName) {\n        inner$2(payload).updateMethod = methodName;\n      };\n\n      ChartView.protoInitialize = function () {\n        var proto = ChartView.prototype;\n        proto.type = 'chart';\n      }();\n\n      return ChartView;\n    }();\n    /**\n     * Set state of single element\n     */\n\n    function elSetState(el, state, highlightDigit) {\n      if (el && isHighDownDispatcher(el)) {\n        (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit);\n      }\n    }\n\n    function toggleHighlight(data, payload, state) {\n      var dataIndex = queryDataIndex(data, payload);\n      var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null;\n\n      if (dataIndex != null) {\n        each(normalizeToArray(dataIndex), function (dataIdx) {\n          elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);\n        });\n      } else {\n        data.eachItemGraphicEl(function (el) {\n          elSetState(el, state, highlightDigit);\n        });\n      }\n    }\n\n    enableClassExtend(ChartView, ['dispose']);\n    enableClassManagement(ChartView);\n\n    function renderTaskPlan(context) {\n      return renderPlanner(context.model);\n    }\n\n    function renderTaskReset(context) {\n      var seriesModel = context.model;\n      var ecModel = context.ecModel;\n      var api = context.api;\n      var payload = context.payload; // FIXME: remove updateView updateVisual\n\n      var progressiveRender = seriesModel.pipelineContext.progressiveRender;\n      var view = context.view;\n      var updateMethod = payload && inner$2(payload).updateMethod;\n      var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount\n      // is less than progressive threshold.\n      : 'render';\n\n      if (methodName !== 'render') {\n        view[methodName](seriesModel, ecModel, api, payload);\n      }\n\n      return progressMethodMap[methodName];\n    }\n\n    var progressMethodMap = {\n      incrementalPrepareRender: {\n        progress: function (params, context) {\n          context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload);\n        }\n      },\n      render: {\n        // Put view.render in `progress` to support appendData. But in this case\n        // view.render should not be called in reset, otherwise it will be called\n        // twise. Use `forceFirstProgress` to make sure that view.render is called\n        // in any cases.\n        forceFirstProgress: true,\n        progress: function (params, context) {\n          context.view.render(context.model, context.ecModel, context.api, context.payload);\n        }\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var ORIGIN_METHOD = '\\0__throttleOriginMethod';\n    var RATE = '\\0__throttleRate';\n    var THROTTLE_TYPE = '\\0__throttleType';\n    /**\n     * @public\n     * @param {(Function)} fn\n     * @param {number} [delay=0] Unit: ms.\n     * @param {boolean} [debounce=false]\n     *        true: If call interval less than `delay`, only the last call works.\n     *        false: If call interval less than `delay, call works on fixed rate.\n     * @return {(Function)} throttled fn.\n     */\n\n    function throttle(fn, delay, debounce) {\n      var currCall;\n      var lastCall = 0;\n      var lastExec = 0;\n      var timer = null;\n      var diff;\n      var scope;\n      var args;\n      var debounceNextCall;\n      delay = delay || 0;\n\n      function exec() {\n        lastExec = new Date().getTime();\n        timer = null;\n        fn.apply(scope, args || []);\n      }\n\n      var cb = function () {\n        var cbArgs = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          cbArgs[_i] = arguments[_i];\n        }\n\n        currCall = new Date().getTime();\n        scope = this;\n        args = cbArgs;\n        var thisDelay = debounceNextCall || delay;\n        var thisDebounce = debounceNextCall || debounce;\n        debounceNextCall = null;\n        diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;\n        clearTimeout(timer); // Here we should make sure that: the `exec` SHOULD NOT be called later\n        // than a new call of `cb`, that is, preserving the command order. Consider\n        // calculating \"scale rate\" when roaming as an example. When a call of `cb`\n        // happens, either the `exec` is called dierectly, or the call is delayed.\n        // But the delayed call should never be later than next call of `cb`. Under\n        // this assurance, we can simply update view state each time `dispatchAction`\n        // triggered by user roaming, but not need to add extra code to avoid the\n        // state being \"rolled-back\".\n\n        if (thisDebounce) {\n          timer = setTimeout(exec, thisDelay);\n        } else {\n          if (diff >= 0) {\n            exec();\n          } else {\n            timer = setTimeout(exec, -diff);\n          }\n        }\n\n        lastCall = currCall;\n      };\n      /**\n       * Clear throttle.\n       * @public\n       */\n\n\n      cb.clear = function () {\n        if (timer) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      /**\n       * Enable debounce once.\n       */\n\n\n      cb.debounceNextCall = function (debounceDelay) {\n        debounceNextCall = debounceDelay;\n      };\n\n      return cb;\n    }\n    /**\n     * Create throttle method or update throttle rate.\n     *\n     * @example\n     * ComponentView.prototype.render = function () {\n     *     ...\n     *     throttle.createOrUpdate(\n     *         this,\n     *         '_dispatchAction',\n     *         this.model.get('throttle'),\n     *         'fixRate'\n     *     );\n     * };\n     * ComponentView.prototype.remove = function () {\n     *     throttle.clear(this, '_dispatchAction');\n     * };\n     * ComponentView.prototype.dispose = function () {\n     *     throttle.clear(this, '_dispatchAction');\n     * };\n     *\n     */\n\n    function createOrUpdate(obj, fnAttr, rate, throttleType) {\n      var fn = obj[fnAttr];\n\n      if (!fn) {\n        return;\n      }\n\n      var originFn = fn[ORIGIN_METHOD] || fn;\n      var lastThrottleType = fn[THROTTLE_TYPE];\n      var lastRate = fn[RATE];\n\n      if (lastRate !== rate || lastThrottleType !== throttleType) {\n        if (rate == null || !throttleType) {\n          return obj[fnAttr] = originFn;\n        }\n\n        fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce');\n        fn[ORIGIN_METHOD] = originFn;\n        fn[THROTTLE_TYPE] = throttleType;\n        fn[RATE] = rate;\n      }\n\n      return fn;\n    }\n    /**\n     * Clear throttle. Example see throttle.createOrUpdate.\n     */\n\n    function clear(obj, fnAttr) {\n      var fn = obj[fnAttr];\n\n      if (fn && fn[ORIGIN_METHOD]) {\n        // Clear throttle\n        fn.clear && fn.clear();\n        obj[fnAttr] = fn[ORIGIN_METHOD];\n      }\n    }\n\n    var inner$3 = makeInner();\n    var defaultStyleMappers = {\n      itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true),\n      lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true)\n    };\n    var defaultColorKey = {\n      lineStyle: 'stroke',\n      itemStyle: 'fill'\n    };\n\n    function getStyleMapper(seriesModel, stylePath) {\n      var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath];\n\n      if (!styleMapper) {\n        console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n        return defaultStyleMappers.itemStyle;\n      }\n\n      return styleMapper;\n    }\n\n    function getDefaultColorKey(seriesModel, stylePath) {\n      // return defaultColorKey[stylePath] ||\n      var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath];\n\n      if (!colorKey) {\n        console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n        return 'fill';\n      }\n\n      return colorKey;\n    }\n\n    var seriesStyleTask = {\n      createOnAllSeries: true,\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var data = seriesModel.getData();\n        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n        var styleModel = seriesModel.getModel(stylePath);\n        var getStyle = getStyleMapper(seriesModel, stylePath);\n        var globalStyle = getStyle(styleModel);\n        var decalOption = styleModel.getShallow('decal');\n\n        if (decalOption) {\n          data.setVisual('decal', decalOption);\n          decalOption.dirty = true;\n        } // TODO\n\n\n        var colorKey = getDefaultColorKey(seriesModel, stylePath);\n        var color = globalStyle[colorKey]; // TODO style callback\n\n        var colorCallback = isFunction(color) ? color : null;\n        var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; // Get from color palette by default.\n\n        if (!globalStyle[colorKey] || colorCallback || hasAutoColor) {\n          // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT\n          // make it effect palette. Because some scenarios users need to make some series\n          // transparent or as background, which should better not effect the palette.\n          var colorPalette = seriesModel.getColorFromPalette( // TODO series count changed.\n          seriesModel.name, null, ecModel.getSeriesCount());\n\n          if (!globalStyle[colorKey]) {\n            globalStyle[colorKey] = colorPalette;\n            data.setVisual('colorFromPalette', true);\n          }\n\n          globalStyle.fill = globalStyle.fill === 'auto' || isFunction(globalStyle.fill) ? colorPalette : globalStyle.fill;\n          globalStyle.stroke = globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke) ? colorPalette : globalStyle.stroke;\n        }\n\n        data.setVisual('style', globalStyle);\n        data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded\n\n        if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) {\n          data.setVisual('colorFromPalette', false);\n          return {\n            dataEach: function (data, idx) {\n              var dataParams = seriesModel.getDataParams(idx);\n              var itemStyle = extend({}, globalStyle);\n              itemStyle[colorKey] = colorCallback(dataParams);\n              data.setItemVisual(idx, 'style', itemStyle);\n            }\n          };\n        }\n      }\n    };\n    var sharedModel = new Model();\n    var dataStyleTask = {\n      createOnAllSeries: true,\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n        var getStyle = getStyleMapper(seriesModel, stylePath);\n        var colorKey = data.getVisual('drawType');\n        return {\n          dataEach: data.hasItemOption ? function (data, idx) {\n            // Not use getItemModel for performance considuration\n            var rawItem = data.getRawDataItem(idx);\n\n            if (rawItem && rawItem[stylePath]) {\n              sharedModel.option = rawItem[stylePath];\n              var style = getStyle(sharedModel);\n              var existsStyle = data.ensureUniqueItemVisual(idx, 'style');\n              extend(existsStyle, style);\n\n              if (sharedModel.option.decal) {\n                data.setItemVisual(idx, 'decal', sharedModel.option.decal);\n                sharedModel.option.decal.dirty = true;\n              }\n\n              if (colorKey in style) {\n                data.setItemVisual(idx, 'colorFromPalette', false);\n              }\n            }\n          } : null\n        };\n      }\n    }; // Pick color from palette for the data which has not been set with color yet.\n    // Note: do not support stream rendering. No such cases yet.\n\n    var dataColorPaletteTask = {\n      performRawSeries: true,\n      overallReset: function (ecModel) {\n        // Each type of series uses one scope.\n        // Pie and funnel are using different scopes.\n        var paletteScopeGroupByType = createHashMap();\n        ecModel.eachSeries(function (seriesModel) {\n          var colorBy = seriesModel.getColorBy();\n\n          if (seriesModel.isColorBySeries()) {\n            return;\n          }\n\n          var key = seriesModel.type + '-' + colorBy;\n          var colorScope = paletteScopeGroupByType.get(key);\n\n          if (!colorScope) {\n            colorScope = {};\n            paletteScopeGroupByType.set(key, colorScope);\n          }\n\n          inner$3(seriesModel).scope = colorScope;\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) {\n            return;\n          }\n\n          var dataAll = seriesModel.getRawData();\n          var idxMap = {};\n          var data = seriesModel.getData();\n          var colorScope = inner$3(seriesModel).scope;\n          var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';\n          var colorKey = getDefaultColorKey(seriesModel, stylePath);\n          data.each(function (idx) {\n            var rawIdx = data.getRawIndex(idx);\n            idxMap[rawIdx] = idx;\n          }); // Iterate on data before filtered. To make sure color from palette can be\n          // Consistent when toggling legend.\n\n          dataAll.each(function (rawIdx) {\n            var idx = idxMap[rawIdx];\n            var fromPalette = data.getItemVisual(idx, 'colorFromPalette'); // Get color from palette for each data only when the color is inherited from series color, which is\n            // also picked from color palette. So following situation is not in the case:\n            // 1. series.itemStyle.color is set\n            // 2. color is encoded by visualMap\n\n            if (fromPalette) {\n              var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n              var name_1 = dataAll.getName(rawIdx) || rawIdx + '';\n              var dataCount = dataAll.count();\n              itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount);\n            }\n          });\n        });\n      }\n    };\n\n    var PI$3 = Math.PI;\n    /**\n     * @param {module:echarts/ExtensionAPI} api\n     * @param {Object} [opts]\n     * @param {string} [opts.text]\n     * @param {string} [opts.color]\n     * @param {string} [opts.textColor]\n     * @return {module:zrender/Element}\n     */\n\n    function defaultLoading(api, opts) {\n      opts = opts || {};\n      defaults(opts, {\n        text: 'loading',\n        textColor: '#000',\n        fontSize: 12,\n        fontWeight: 'normal',\n        fontStyle: 'normal',\n        fontFamily: 'sans-serif',\n        maskColor: 'rgba(255, 255, 255, 0.8)',\n        showSpinner: true,\n        color: '#5470c6',\n        spinnerRadius: 10,\n        lineWidth: 5,\n        zlevel: 0\n      });\n      var group = new Group();\n      var mask = new Rect({\n        style: {\n          fill: opts.maskColor\n        },\n        zlevel: opts.zlevel,\n        z: 10000\n      });\n      group.add(mask);\n      var textContent = new ZRText({\n        style: {\n          text: opts.text,\n          fill: opts.textColor,\n          fontSize: opts.fontSize,\n          fontWeight: opts.fontWeight,\n          fontStyle: opts.fontStyle,\n          fontFamily: opts.fontFamily\n        },\n        zlevel: opts.zlevel,\n        z: 10001\n      });\n      var labelRect = new Rect({\n        style: {\n          fill: 'none'\n        },\n        textContent: textContent,\n        textConfig: {\n          position: 'right',\n          distance: 10\n        },\n        zlevel: opts.zlevel,\n        z: 10001\n      });\n      group.add(labelRect);\n      var arc;\n\n      if (opts.showSpinner) {\n        arc = new Arc({\n          shape: {\n            startAngle: -PI$3 / 2,\n            endAngle: -PI$3 / 2 + 0.1,\n            r: opts.spinnerRadius\n          },\n          style: {\n            stroke: opts.color,\n            lineCap: 'round',\n            lineWidth: opts.lineWidth\n          },\n          zlevel: opts.zlevel,\n          z: 10001\n        });\n        arc.animateShape(true).when(1000, {\n          endAngle: PI$3 * 3 / 2\n        }).start('circularInOut');\n        arc.animateShape(true).when(1000, {\n          startAngle: PI$3 * 3 / 2\n        }).delay(300).start('circularInOut');\n        group.add(arc);\n      } // Inject resize\n\n\n      group.resize = function () {\n        var textWidth = textContent.getBoundingRect().width;\n        var r = opts.showSpinner ? opts.spinnerRadius : 0; // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2\n        // textDistance needs to be calculated when both animation and text exist\n\n        var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) // only show the text\n        + (opts.showSpinner ? 0 : textWidth / 2) // only show the spinner\n        + (textWidth ? 0 : r);\n        var cy = api.getHeight() / 2;\n        opts.showSpinner && arc.setShape({\n          cx: cx,\n          cy: cy\n        });\n        labelRect.setShape({\n          x: cx - r,\n          y: cy - r,\n          width: r * 2,\n          height: r * 2\n        });\n        mask.setShape({\n          x: 0,\n          y: 0,\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n      };\n\n      group.resize();\n      return group;\n    }\n\n    var Scheduler =\n    /** @class */\n    function () {\n      function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {\n        // key: handlerUID\n        this._stageTaskMap = createHashMap();\n        this.ecInstance = ecInstance;\n        this.api = api; // Fix current processors in case that in some rear cases that\n        // processors might be registered after echarts instance created.\n        // Register processors incrementally for a echarts instance is\n        // not supported by this stream architecture.\n\n        dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();\n        visualHandlers = this._visualHandlers = visualHandlers.slice();\n        this._allHandlers = dataProcessorHandlers.concat(visualHandlers);\n      }\n\n      Scheduler.prototype.restoreData = function (ecModel, payload) {\n        // TODO: Only restore needed series and components, but not all components.\n        // Currently `restoreData` of all of the series and component will be called.\n        // But some independent components like `title`, `legend`, `graphic`, `toolbox`,\n        // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`,\n        // and some components like coordinate system, axes, dataZoom, visualMap only\n        // need their target series refresh.\n        // (1) If we are implementing this feature some day, we should consider these cases:\n        // if a data processor depends on a component (e.g., dataZoomProcessor depends\n        // on the settings of `dataZoom`), it should be re-performed if the component\n        // is modified by `setOption`.\n        // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`,\n        // it should be re-performed when the result array of `getTargetSeries` changed.\n        // We use `dependencies` to cover these issues.\n        // (3) How to update target series when coordinate system related components modified.\n        // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty,\n        // and this case all of the tasks will be set as dirty.\n        ecModel.restoreData(payload); // Theoretically an overall task not only depends on each of its target series, but also\n        // depends on all of the series.\n        // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks\n        // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure\n        // that the overall task is set as dirty and to be performed, otherwise it probably cause\n        // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it\n        // probably cause state chaos (consider `dataZoomProcessor`).\n\n        this._stageTaskMap.each(function (taskRecord) {\n          var overallTask = taskRecord.overallTask;\n          overallTask && overallTask.dirty();\n        });\n      }; // If seriesModel provided, incremental threshold is check by series data.\n\n\n      Scheduler.prototype.getPerformArgs = function (task, isBlock) {\n        // For overall task\n        if (!task.__pipeline) {\n          return;\n        }\n\n        var pipeline = this._pipelineMap.get(task.__pipeline.id);\n\n        var pCtx = pipeline.context;\n        var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex;\n        var step = incremental ? pipeline.step : null;\n        var modDataCount = pCtx && pCtx.modDataCount;\n        var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null;\n        return {\n          step: step,\n          modBy: modBy,\n          modDataCount: modDataCount\n        };\n      };\n\n      Scheduler.prototype.getPipeline = function (pipelineId) {\n        return this._pipelineMap.get(pipelineId);\n      };\n      /**\n       * Current, progressive rendering starts from visual and layout.\n       * Always detect render mode in the same stage, avoiding that incorrect\n       * detection caused by data filtering.\n       * Caution:\n       * `updateStreamModes` use `seriesModel.getData()`.\n       */\n\n\n      Scheduler.prototype.updateStreamModes = function (seriesModel, view) {\n        var pipeline = this._pipelineMap.get(seriesModel.uid);\n\n        var data = seriesModel.getData();\n        var dataLen = data.count(); // `progressiveRender` means that can render progressively in each\n        // animation frame. Note that some types of series do not provide\n        // `view.incrementalPrepareRender` but support `chart.appendData`. We\n        // use the term `incremental` but not `progressive` to describe the\n        // case that `chart.appendData`.\n\n        var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;\n        var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.\n        // see `test/candlestick-large3.html`\n\n        var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;\n        seriesModel.pipelineContext = pipeline.context = {\n          progressiveRender: progressiveRender,\n          modDataCount: modDataCount,\n          large: large\n        };\n      };\n\n      Scheduler.prototype.restorePipelines = function (ecModel) {\n        var scheduler = this;\n        var pipelineMap = scheduler._pipelineMap = createHashMap();\n        ecModel.eachSeries(function (seriesModel) {\n          var progressive = seriesModel.getProgressive();\n          var pipelineId = seriesModel.uid;\n          pipelineMap.set(pipelineId, {\n            id: pipelineId,\n            head: null,\n            tail: null,\n            threshold: seriesModel.getProgressiveThreshold(),\n            progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),\n            blockIndex: -1,\n            step: Math.round(progressive || 700),\n            count: 0\n          });\n\n          scheduler._pipe(seriesModel, seriesModel.dataTask);\n        });\n      };\n\n      Scheduler.prototype.prepareStageTasks = function () {\n        var stageTaskMap = this._stageTaskMap;\n        var ecModel = this.api.getModel();\n        var api = this.api;\n        each(this._allHandlers, function (handler) {\n          var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {});\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            // Currently do not need to support to sepecify them both.\n            errMsg = '\"reset\" and \"overallReset\" must not be both specified.';\n          }\n\n          assert(!(handler.reset && handler.overallReset), errMsg);\n          handler.reset && this._createSeriesStageTask(handler, record, ecModel, api);\n          handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api);\n        }, this);\n      };\n\n      Scheduler.prototype.prepareView = function (view, model, ecModel, api) {\n        var renderTask = view.renderTask;\n        var context = renderTask.context;\n        context.model = model;\n        context.ecModel = ecModel;\n        context.api = api;\n        renderTask.__block = !view.incrementalPrepareRender;\n\n        this._pipe(model, renderTask);\n      };\n\n      Scheduler.prototype.performDataProcessorTasks = function (ecModel, payload) {\n        // If we do not use `block` here, it should be considered when to update modes.\n        this._performStageTasks(this._dataProcessorHandlers, ecModel, payload, {\n          block: true\n        });\n      };\n\n      Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) {\n        this._performStageTasks(this._visualHandlers, ecModel, payload, opt);\n      };\n\n      Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) {\n        opt = opt || {};\n        var unfinished = false;\n        var scheduler = this;\n        each(stageHandlers, function (stageHandler, idx) {\n          if (opt.visualType && opt.visualType !== stageHandler.visualType) {\n            return;\n          }\n\n          var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);\n\n          var seriesTaskMap = stageHandlerRecord.seriesTaskMap;\n          var overallTask = stageHandlerRecord.overallTask;\n\n          if (overallTask) {\n            var overallNeedDirty_1;\n            var agentStubMap = overallTask.agentStubMap;\n            agentStubMap.each(function (stub) {\n              if (needSetDirty(opt, stub)) {\n                stub.dirty();\n                overallNeedDirty_1 = true;\n              }\n            });\n            overallNeedDirty_1 && overallTask.dirty();\n            scheduler.updatePayload(overallTask, payload);\n            var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block); // Execute stubs firstly, which may set the overall task dirty,\n            // then execute the overall task. And stub will call seriesModel.setData,\n            // which ensures that in the overallTask seriesModel.getData() will not\n            // return incorrect data.\n\n            agentStubMap.each(function (stub) {\n              stub.perform(performArgs_1);\n            });\n\n            if (overallTask.perform(performArgs_1)) {\n              unfinished = true;\n            }\n          } else if (seriesTaskMap) {\n            seriesTaskMap.each(function (task, pipelineId) {\n              if (needSetDirty(opt, task)) {\n                task.dirty();\n              }\n\n              var performArgs = scheduler.getPerformArgs(task, opt.block); // FIXME\n              // if intending to declare `performRawSeries` in handlers, only\n              // stream-independent (specifically, data item independent) operations can be\n              // performed. Because if a series is filtered, most of the tasks will not\n              // be performed. A stream-dependent operation probably cause wrong biz logic.\n              // Perhaps we should not provide a separate callback for this case instead\n              // of providing the config `performRawSeries`. The stream-dependent operations\n              // and stream-independent operations should better not be mixed.\n\n              performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);\n              scheduler.updatePayload(task, payload);\n\n              if (task.perform(performArgs)) {\n                unfinished = true;\n              }\n            });\n          }\n        });\n\n        function needSetDirty(opt, task) {\n          return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));\n        }\n\n        this.unfinished = unfinished || this.unfinished;\n      };\n\n      Scheduler.prototype.performSeriesTasks = function (ecModel) {\n        var unfinished;\n        ecModel.eachSeries(function (seriesModel) {\n          // Progress to the end for dataInit and dataRestore.\n          unfinished = seriesModel.dataTask.perform() || unfinished;\n        });\n        this.unfinished = unfinished || this.unfinished;\n      };\n\n      Scheduler.prototype.plan = function () {\n        // Travel pipelines, check block.\n        this._pipelineMap.each(function (pipeline) {\n          var task = pipeline.tail;\n\n          do {\n            if (task.__block) {\n              pipeline.blockIndex = task.__idxInPipeline;\n              break;\n            }\n\n            task = task.getUpstream();\n          } while (task);\n        });\n      };\n\n      Scheduler.prototype.updatePayload = function (task, payload) {\n        payload !== 'remain' && (task.context.payload = payload);\n      };\n\n      Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n        var scheduler = this;\n        var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; // The count of stages are totally about only several dozen, so\n        // do not need to reuse the map.\n\n        var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap();\n        var seriesType = stageHandler.seriesType;\n        var getTargetSeries = stageHandler.getTargetSeries; // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,\n        // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,\n        // it works but it may cause other irrelevant charts blocked.\n\n        if (stageHandler.createOnAllSeries) {\n          ecModel.eachRawSeries(create);\n        } else if (seriesType) {\n          ecModel.eachRawSeriesByType(seriesType, create);\n        } else if (getTargetSeries) {\n          getTargetSeries(ecModel, api).each(create);\n        }\n\n        function create(seriesModel) {\n          var pipelineId = seriesModel.uid; // Init tasks for each seriesModel only once.\n          // Reuse original task instance.\n\n          var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({\n            plan: seriesTaskPlan,\n            reset: seriesTaskReset,\n            count: seriesTaskCount\n          }));\n          task.context = {\n            model: seriesModel,\n            ecModel: ecModel,\n            api: api,\n            // PENDING: `useClearVisual` not used?\n            useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,\n            plan: stageHandler.plan,\n            reset: stageHandler.reset,\n            scheduler: scheduler\n          };\n\n          scheduler._pipe(seriesModel, task);\n        }\n      };\n\n      Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n        var scheduler = this;\n        var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage.\n        || createTask({\n          reset: overallTaskReset\n        });\n        overallTask.context = {\n          ecModel: ecModel,\n          api: api,\n          overallReset: stageHandler.overallReset,\n          scheduler: scheduler\n        };\n        var oldAgentStubMap = overallTask.agentStubMap; // The count of stages are totally about only several dozen, so\n        // do not need to reuse the map.\n\n        var newAgentStubMap = overallTask.agentStubMap = createHashMap();\n        var seriesType = stageHandler.seriesType;\n        var getTargetSeries = stageHandler.getTargetSeries;\n        var overallProgress = true;\n        var shouldOverallTaskDirty = false; // FIXME:TS never used, so comment it\n        // let modifyOutputEnd = stageHandler.modifyOutputEnd;\n        // An overall task with seriesType detected or has `getTargetSeries`, we add\n        // stub in each pipelines, it will set the overall task dirty when the pipeline\n        // progress. Moreover, to avoid call the overall task each frame (too frequent),\n        // we set the pipeline block.\n\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '\"createOnAllSeries\" is not supported for \"overallReset\", ' + 'because it will block all streams.';\n        }\n\n        assert(!stageHandler.createOnAllSeries, errMsg);\n\n        if (seriesType) {\n          ecModel.eachRawSeriesByType(seriesType, createStub);\n        } else if (getTargetSeries) {\n          getTargetSeries(ecModel, api).each(createStub);\n        } // Otherwise, (usually it is legacy case), the overall task will only be\n        // executed when upstream is dirty. Otherwise the progressive rendering of all\n        // pipelines will be disabled unexpectedly. But it still needs stubs to receive\n        // dirty info from upstream.\n        else {\n            overallProgress = false;\n            each(ecModel.getSeries(), createStub);\n          }\n\n        function createStub(seriesModel) {\n          var pipelineId = seriesModel.uid;\n          var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || ( // When the result of `getTargetSeries` changed, the overallTask\n          // should be set as dirty and re-performed.\n          shouldOverallTaskDirty = true, createTask({\n            reset: stubReset,\n            onDirty: stubOnDirty\n          })));\n          stub.context = {\n            model: seriesModel,\n            overallProgress: overallProgress // FIXME:TS never used, so comment it\n            // modifyOutputEnd: modifyOutputEnd\n\n          };\n          stub.agent = overallTask;\n          stub.__block = overallProgress;\n\n          scheduler._pipe(seriesModel, stub);\n        }\n\n        if (shouldOverallTaskDirty) {\n          overallTask.dirty();\n        }\n      };\n\n      Scheduler.prototype._pipe = function (seriesModel, task) {\n        var pipelineId = seriesModel.uid;\n\n        var pipeline = this._pipelineMap.get(pipelineId);\n\n        !pipeline.head && (pipeline.head = task);\n        pipeline.tail && pipeline.tail.pipe(task);\n        pipeline.tail = task;\n        task.__idxInPipeline = pipeline.count++;\n        task.__pipeline = pipeline;\n      };\n\n      Scheduler.wrapStageHandler = function (stageHandler, visualType) {\n        if (isFunction(stageHandler)) {\n          stageHandler = {\n            overallReset: stageHandler,\n            seriesType: detectSeriseType(stageHandler)\n          };\n        }\n\n        stageHandler.uid = getUID('stageHandler');\n        visualType && (stageHandler.visualType = visualType);\n        return stageHandler;\n      };\n      return Scheduler;\n    }();\n\n    function overallTaskReset(context) {\n      context.overallReset(context.ecModel, context.api, context.payload);\n    }\n\n    function stubReset(context) {\n      return context.overallProgress && stubProgress;\n    }\n\n    function stubProgress() {\n      this.agent.dirty();\n      this.getDownstream().dirty();\n    }\n\n    function stubOnDirty() {\n      this.agent && this.agent.dirty();\n    }\n\n    function seriesTaskPlan(context) {\n      return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null;\n    }\n\n    function seriesTaskReset(context) {\n      if (context.useClearVisual) {\n        context.data.clearAllVisual();\n      }\n\n      var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload));\n      return resetDefines.length > 1 ? map(resetDefines, function (v, idx) {\n        return makeSeriesTaskProgress(idx);\n      }) : singleSeriesTaskProgress;\n    }\n\n    var singleSeriesTaskProgress = makeSeriesTaskProgress(0);\n\n    function makeSeriesTaskProgress(resetDefineIdx) {\n      return function (params, context) {\n        var data = context.data;\n        var resetDefine = context.resetDefines[resetDefineIdx];\n\n        if (resetDefine && resetDefine.dataEach) {\n          for (var i = params.start; i < params.end; i++) {\n            resetDefine.dataEach(data, i);\n          }\n        } else if (resetDefine && resetDefine.progress) {\n          resetDefine.progress(params, data);\n        }\n      };\n    }\n\n    function seriesTaskCount(context) {\n      return context.data.count();\n    }\n    /**\n     * Only some legacy stage handlers (usually in echarts extensions) are pure function.\n     * To ensure that they can work normally, they should work in block mode, that is,\n     * they should not be started util the previous tasks finished. So they cause the\n     * progressive rendering disabled. We try to detect the series type, to narrow down\n     * the block range to only the series type they concern, but not all series.\n     */\n\n\n    function detectSeriseType(legacyFunc) {\n      seriesType = null;\n\n      try {\n        // Assume there is no async when calling `eachSeriesByType`.\n        legacyFunc(ecModelMock, apiMock);\n      } catch (e) {}\n\n      return seriesType;\n    }\n\n    var ecModelMock = {};\n    var apiMock = {};\n    var seriesType;\n    mockMethods(ecModelMock, GlobalModel);\n    mockMethods(apiMock, ExtensionAPI);\n\n    ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {\n      seriesType = type;\n    };\n\n    ecModelMock.eachComponent = function (cond) {\n      if (cond.mainType === 'series' && cond.subType) {\n        seriesType = cond.subType;\n      }\n    };\n\n    function mockMethods(target, Clz) {\n      /* eslint-disable */\n      for (var name_1 in Clz.prototype) {\n        // Do not use hasOwnProperty\n        target[name_1] = noop;\n      }\n      /* eslint-enable */\n\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];\n    var lightTheme = {\n      color: colorAll,\n      colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll]\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var contrastColor = '#B9B8CE';\n    var backgroundColor = '#100C2A';\n\n    var axisCommon = function () {\n      return {\n        axisLine: {\n          lineStyle: {\n            color: contrastColor\n          }\n        },\n        splitLine: {\n          lineStyle: {\n            color: '#484753'\n          }\n        },\n        splitArea: {\n          areaStyle: {\n            color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)']\n          }\n        },\n        minorSplitLine: {\n          lineStyle: {\n            color: '#20203B'\n          }\n        }\n      };\n    };\n\n    var colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff'];\n    var theme = {\n      darkMode: true,\n      color: colorPalette,\n      backgroundColor: backgroundColor,\n      axisPointer: {\n        lineStyle: {\n          color: '#817f91'\n        },\n        crossStyle: {\n          color: '#817f91'\n        },\n        label: {\n          // TODO Contrast of label backgorundColor\n          color: '#fff'\n        }\n      },\n      legend: {\n        textStyle: {\n          color: contrastColor\n        }\n      },\n      textStyle: {\n        color: contrastColor\n      },\n      title: {\n        textStyle: {\n          color: '#EEF1FA'\n        },\n        subtextStyle: {\n          color: '#B9B8CE'\n        }\n      },\n      toolbox: {\n        iconStyle: {\n          borderColor: contrastColor\n        }\n      },\n      dataZoom: {\n        borderColor: '#71708A',\n        textStyle: {\n          color: contrastColor\n        },\n        brushStyle: {\n          color: 'rgba(135,163,206,0.3)'\n        },\n        handleStyle: {\n          color: '#353450',\n          borderColor: '#C5CBE3'\n        },\n        moveHandleStyle: {\n          color: '#B0B6C3',\n          opacity: 0.3\n        },\n        fillerColor: 'rgba(135,163,206,0.2)',\n        emphasis: {\n          handleStyle: {\n            borderColor: '#91B7F2',\n            color: '#4D587D'\n          },\n          moveHandleStyle: {\n            color: '#636D9A',\n            opacity: 0.7\n          }\n        },\n        dataBackground: {\n          lineStyle: {\n            color: '#71708A',\n            width: 1\n          },\n          areaStyle: {\n            color: '#71708A'\n          }\n        },\n        selectedDataBackground: {\n          lineStyle: {\n            color: '#87A3CE'\n          },\n          areaStyle: {\n            color: '#87A3CE'\n          }\n        }\n      },\n      visualMap: {\n        textStyle: {\n          color: contrastColor\n        }\n      },\n      timeline: {\n        lineStyle: {\n          color: contrastColor\n        },\n        label: {\n          color: contrastColor\n        },\n        controlStyle: {\n          color: contrastColor,\n          borderColor: contrastColor\n        }\n      },\n      calendar: {\n        itemStyle: {\n          color: backgroundColor\n        },\n        dayLabel: {\n          color: contrastColor\n        },\n        monthLabel: {\n          color: contrastColor\n        },\n        yearLabel: {\n          color: contrastColor\n        }\n      },\n      timeAxis: axisCommon(),\n      logAxis: axisCommon(),\n      valueAxis: axisCommon(),\n      categoryAxis: axisCommon(),\n      line: {\n        symbol: 'circle'\n      },\n      graph: {\n        color: colorPalette\n      },\n      gauge: {\n        title: {\n          color: contrastColor\n        },\n        axisLine: {\n          lineStyle: {\n            color: [[1, 'rgba(207,212,219,0.2)']]\n          }\n        },\n        axisLabel: {\n          color: contrastColor\n        },\n        detail: {\n          color: '#EEF1FA'\n        }\n      },\n      candlestick: {\n        itemStyle: {\n          color: '#f64e56',\n          color0: '#54ea92',\n          borderColor: '#f64e56',\n          borderColor0: '#54ea92' // borderColor: '#ca2824',\n          // borderColor0: '#09a443'\n\n        }\n      }\n    };\n    theme.categoryAxis.splitLine.show = false;\n\n    /**\n     * Usage of query:\n     * `chart.on('click', query, handler);`\n     * The `query` can be:\n     * + The component type query string, only `mainType` or `mainType.subType`,\n     *   like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.\n     * + The component query object, like:\n     *   `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,\n     *   `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.\n     * + The data query object, like:\n     *   `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.\n     * + The other query object (cmponent customized query), like:\n     *   `{element: 'some'}` (only available in custom series).\n     *\n     * Caveat: If a prop in the `query` object is `null/undefined`, it is the\n     * same as there is no such prop in the `query` object.\n     */\n\n    var ECEventProcessor =\n    /** @class */\n    function () {\n      function ECEventProcessor() {}\n\n      ECEventProcessor.prototype.normalizeQuery = function (query) {\n        var cptQuery = {};\n        var dataQuery = {};\n        var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.\n\n        if (isString(query)) {\n          var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.\n\n          cptQuery.mainType = condCptType.main || null;\n          cptQuery.subType = condCptType.sub || null;\n        } // `query` is an object, convert to {mainType, index, name, id}.\n        else {\n            // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,\n            // can not be used in `compomentModel.filterForExposedEvent`.\n            var suffixes_1 = ['Index', 'Name', 'Id'];\n            var dataKeys_1 = {\n              name: 1,\n              dataIndex: 1,\n              dataType: 1\n            };\n            each(query, function (val, key) {\n              var reserved = false;\n\n              for (var i = 0; i < suffixes_1.length; i++) {\n                var propSuffix = suffixes_1[i];\n                var suffixPos = key.lastIndexOf(propSuffix);\n\n                if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {\n                  var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.\n\n                  if (mainType !== 'data') {\n                    cptQuery.mainType = mainType;\n                    cptQuery[propSuffix.toLowerCase()] = val;\n                    reserved = true;\n                  }\n                }\n              }\n\n              if (dataKeys_1.hasOwnProperty(key)) {\n                dataQuery[key] = val;\n                reserved = true;\n              }\n\n              if (!reserved) {\n                otherQuery[key] = val;\n              }\n            });\n          }\n\n        return {\n          cptQuery: cptQuery,\n          dataQuery: dataQuery,\n          otherQuery: otherQuery\n        };\n      };\n\n      ECEventProcessor.prototype.filter = function (eventType, query) {\n        // They should be assigned before each trigger call.\n        var eventInfo = this.eventInfo;\n\n        if (!eventInfo) {\n          return true;\n        }\n\n        var targetEl = eventInfo.targetEl;\n        var packedEvent = eventInfo.packedEvent;\n        var model = eventInfo.model;\n        var view = eventInfo.view; // For event like 'globalout'.\n\n        if (!model || !view) {\n          return true;\n        }\n\n        var cptQuery = query.cptQuery;\n        var dataQuery = query.dataQuery;\n        return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent));\n\n        function check(query, host, prop, propOnHost) {\n          return query[prop] == null || host[propOnHost || prop] === query[prop];\n        }\n      };\n\n      ECEventProcessor.prototype.afterTrigger = function () {\n        // Make sure the eventInfo won't be used in next trigger.\n        this.eventInfo = null;\n      };\n\n      return ECEventProcessor;\n    }();\n\n    var SYMBOL_PROPS_WITH_CB = ['symbol', 'symbolSize', 'symbolRotate', 'symbolOffset'];\n    var SYMBOL_PROPS = SYMBOL_PROPS_WITH_CB.concat(['symbolKeepAspect']); // Encoding visual for all series include which is filtered for legend drawing\n\n    var seriesSymbolTask = {\n      createOnAllSeries: true,\n      // For legend.\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var data = seriesModel.getData();\n\n        if (seriesModel.legendIcon) {\n          data.setVisual('legendIcon', seriesModel.legendIcon);\n        }\n\n        if (!seriesModel.hasSymbolVisual) {\n          return;\n        }\n\n        var symbolOptions = {};\n        var symbolOptionsCb = {};\n        var hasCallback = false;\n\n        for (var i = 0; i < SYMBOL_PROPS_WITH_CB.length; i++) {\n          var symbolPropName = SYMBOL_PROPS_WITH_CB[i];\n          var val = seriesModel.get(symbolPropName);\n\n          if (isFunction(val)) {\n            hasCallback = true;\n            symbolOptionsCb[symbolPropName] = val;\n          } else {\n            symbolOptions[symbolPropName] = val;\n          }\n        }\n\n        symbolOptions.symbol = symbolOptions.symbol || seriesModel.defaultSymbol;\n        data.setVisual(extend({\n          legendIcon: seriesModel.legendIcon || symbolOptions.symbol,\n          symbolKeepAspect: seriesModel.get('symbolKeepAspect')\n        }, symbolOptions)); // Only visible series has each data be visual encoded\n\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var symbolPropsCb = keys(symbolOptionsCb);\n\n        function dataEach(data, idx) {\n          var rawValue = seriesModel.getRawValue(idx);\n          var params = seriesModel.getDataParams(idx);\n\n          for (var i = 0; i < symbolPropsCb.length; i++) {\n            var symbolPropName = symbolPropsCb[i];\n            data.setItemVisual(idx, symbolPropName, symbolOptionsCb[symbolPropName](rawValue, params));\n          }\n        }\n\n        return {\n          dataEach: hasCallback ? dataEach : null\n        };\n      }\n    };\n    var dataSymbolTask = {\n      createOnAllSeries: true,\n      // For legend.\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        if (!seriesModel.hasSymbolVisual) {\n          return;\n        } // Only visible series has each data be visual encoded\n\n\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n\n        function dataEach(data, idx) {\n          var itemModel = data.getItemModel(idx);\n\n          for (var i = 0; i < SYMBOL_PROPS.length; i++) {\n            var symbolPropName = SYMBOL_PROPS[i];\n            var val = itemModel.getShallow(symbolPropName, true);\n\n            if (val != null) {\n              data.setItemVisual(idx, symbolPropName, val);\n            }\n          }\n        }\n\n        return {\n          dataEach: data.hasItemOption ? dataEach : null\n        };\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function getItemVisualFromData(data, dataIndex, key) {\n      switch (key) {\n        case 'color':\n          var style = data.getItemVisual(dataIndex, 'style');\n          return style[data.getVisual('drawType')];\n\n        case 'opacity':\n          return data.getItemVisual(dataIndex, 'style').opacity;\n\n        case 'symbol':\n        case 'symbolSize':\n        case 'liftZ':\n          return data.getItemVisual(dataIndex, key);\n\n        default:\n          if (\"development\" !== 'production') {\n            console.warn(\"Unknown visual type \" + key);\n          }\n\n      }\n    }\n    function getVisualFromData(data, key) {\n      switch (key) {\n        case 'color':\n          var style = data.getVisual('style');\n          return style[data.getVisual('drawType')];\n\n        case 'opacity':\n          return data.getVisual('style').opacity;\n\n        case 'symbol':\n        case 'symbolSize':\n        case 'liftZ':\n          return data.getVisual(key);\n\n        default:\n          if (\"development\" !== 'production') {\n            console.warn(\"Unknown visual type \" + key);\n          }\n\n      }\n    }\n    function setItemVisualFromData(data, dataIndex, key, value) {\n      switch (key) {\n        case 'color':\n          // Make sure not sharing style object.\n          var style = data.ensureUniqueItemVisual(dataIndex, 'style');\n          style[data.getVisual('drawType')] = value; // Mark the color has been changed, not from palette anymore\n\n          data.setItemVisual(dataIndex, 'colorFromPalette', false);\n          break;\n\n        case 'opacity':\n          data.ensureUniqueItemVisual(dataIndex, 'style').opacity = value;\n          break;\n\n        case 'symbol':\n        case 'symbolSize':\n        case 'liftZ':\n          data.setItemVisual(dataIndex, key, value);\n          break;\n\n        default:\n          if (\"development\" !== 'production') {\n            console.warn(\"Unknown visual type \" + key);\n          }\n\n      }\n    }\n\n    // Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect\n\n    function createLegacyDataSelectAction(seriesType, ecRegisterAction) {\n      function getSeriesIndices(ecModel, payload) {\n        var seriesIndices = [];\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: seriesType,\n          query: payload\n        }, function (seriesModel) {\n          seriesIndices.push(seriesModel.seriesIndex);\n        });\n        return seriesIndices;\n      }\n\n      each([[seriesType + 'ToggleSelect', 'toggleSelect'], [seriesType + 'Select', 'select'], [seriesType + 'UnSelect', 'unselect']], function (eventsMap) {\n        ecRegisterAction(eventsMap[0], function (payload, ecModel, api) {\n          payload = extend({}, payload);\n\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(payload.type, eventsMap[1]);\n          }\n\n          api.dispatchAction(extend(payload, {\n            type: eventsMap[1],\n            seriesIndex: getSeriesIndices(ecModel, payload)\n          }));\n        });\n      });\n    }\n\n    function handleSeriesLegacySelectEvents(type, eventPostfix, ecIns, ecModel, payload) {\n      var legacyEventName = type + eventPostfix;\n\n      if (!ecIns.isSilent(legacyEventName)) {\n        if (\"development\" !== 'production') {\n          deprecateLog(\"event \" + legacyEventName + \" is deprecated.\");\n        }\n\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'pie'\n        }, function (seriesModel) {\n          var seriesIndex = seriesModel.seriesIndex;\n          var selectedMap = seriesModel.option.selectedMap;\n          var selected = payload.selected;\n\n          for (var i = 0; i < selected.length; i++) {\n            if (selected[i].seriesIndex === seriesIndex) {\n              var data = seriesModel.getData();\n              var dataIndex = queryDataIndex(data, payload.fromActionPayload);\n              ecIns.trigger(legacyEventName, {\n                type: legacyEventName,\n                seriesId: seriesModel.id,\n                name: isArray(dataIndex) ? data.getName(dataIndex[0]) : data.getName(dataIndex),\n                selected: isString(selectedMap) ? selectedMap : extend({}, selectedMap)\n              });\n            }\n          }\n        });\n      }\n    }\n\n    function handleLegacySelectEvents(messageCenter, ecIns, api) {\n      messageCenter.on('selectchanged', function (params) {\n        var ecModel = api.getModel();\n\n        if (params.isFromClick) {\n          handleSeriesLegacySelectEvents('map', 'selectchanged', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'selectchanged', ecIns, ecModel, params);\n        } else if (params.fromAction === 'select') {\n          handleSeriesLegacySelectEvents('map', 'selected', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'selected', ecIns, ecModel, params);\n        } else if (params.fromAction === 'unselect') {\n          handleSeriesLegacySelectEvents('map', 'unselected', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'unselected', ecIns, ecModel, params);\n        }\n      });\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function findEventDispatcher(target, det, returnFirstMatch) {\n      var found;\n\n      while (target) {\n        if (det(target)) {\n          found = target;\n\n          if (returnFirstMatch) {\n            break;\n          }\n        }\n\n        target = target.__hostTarget || target.parent;\n      }\n\n      return found;\n    }\n\n    var wmUniqueIndex = Math.round(Math.random() * 9);\n    var supportDefineProperty = typeof Object.defineProperty === 'function';\n    var WeakMap = (function () {\n        function WeakMap() {\n            this._id = '__ec_inner_' + wmUniqueIndex++;\n        }\n        WeakMap.prototype.get = function (key) {\n            return this._guard(key)[this._id];\n        };\n        WeakMap.prototype.set = function (key, value) {\n            var target = this._guard(key);\n            if (supportDefineProperty) {\n                Object.defineProperty(target, this._id, {\n                    value: value,\n                    enumerable: false,\n                    configurable: true\n                });\n            }\n            else {\n                target[this._id] = value;\n            }\n            return this;\n        };\n        WeakMap.prototype[\"delete\"] = function (key) {\n            if (this.has(key)) {\n                delete this._guard(key)[this._id];\n                return true;\n            }\n            return false;\n        };\n        WeakMap.prototype.has = function (key) {\n            return !!this._guard(key)[this._id];\n        };\n        WeakMap.prototype._guard = function (key) {\n            if (key !== Object(key)) {\n                throw TypeError('Value of WeakMap is not a non-null object.');\n            }\n            return key;\n        };\n        return WeakMap;\n    }());\n\n    /**\n     * Triangle shape\n     * @inner\n     */\n\n    var Triangle = Path.extend({\n      type: 'triangle',\n      shape: {\n        cx: 0,\n        cy: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var width = shape.width / 2;\n        var height = shape.height / 2;\n        path.moveTo(cx, cy - height);\n        path.lineTo(cx + width, cy + height);\n        path.lineTo(cx - width, cy + height);\n        path.closePath();\n      }\n    });\n    /**\n     * Diamond shape\n     * @inner\n     */\n\n    var Diamond = Path.extend({\n      type: 'diamond',\n      shape: {\n        cx: 0,\n        cy: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var width = shape.width / 2;\n        var height = shape.height / 2;\n        path.moveTo(cx, cy - height);\n        path.lineTo(cx + width, cy);\n        path.lineTo(cx, cy + height);\n        path.lineTo(cx - width, cy);\n        path.closePath();\n      }\n    });\n    /**\n     * Pin shape\n     * @inner\n     */\n\n    var Pin = Path.extend({\n      type: 'pin',\n      shape: {\n        // x, y on the cusp\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var x = shape.x;\n        var y = shape.y;\n        var w = shape.width / 5 * 3; // Height must be larger than width\n\n        var h = Math.max(w, shape.height);\n        var r = w / 2; // Dist on y with tangent point and circle center\n\n        var dy = r * r / (h - r);\n        var cy = y - h + r + dy;\n        var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center\n\n        var dx = Math.cos(angle) * r;\n        var tanX = Math.sin(angle);\n        var tanY = Math.cos(angle);\n        var cpLen = r * 0.6;\n        var cpLen2 = r * 0.7;\n        path.moveTo(x - dx, cy + dy);\n        path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);\n        path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y);\n        path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy);\n        path.closePath();\n      }\n    });\n    /**\n     * Arrow shape\n     * @inner\n     */\n\n    var Arrow = Path.extend({\n      type: 'arrow',\n      shape: {\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (ctx, shape) {\n        var height = shape.height;\n        var width = shape.width;\n        var x = shape.x;\n        var y = shape.y;\n        var dx = width / 3 * 2;\n        ctx.moveTo(x, y);\n        ctx.lineTo(x + dx, y + height);\n        ctx.lineTo(x, y + height / 4 * 3);\n        ctx.lineTo(x - dx, y + height);\n        ctx.lineTo(x, y);\n        ctx.closePath();\n      }\n    });\n    /**\n     * Map of path constructors\n     */\n    // TODO Use function to build symbol path.\n\n    var symbolCtors = {\n      line: Line,\n      rect: Rect,\n      roundRect: Rect,\n      square: Rect,\n      circle: Circle,\n      diamond: Diamond,\n      pin: Pin,\n      arrow: Arrow,\n      triangle: Triangle\n    };\n    var symbolShapeMakers = {\n      line: function (x, y, w, h, shape) {\n        shape.x1 = x;\n        shape.y1 = y + h / 2;\n        shape.x2 = x + w;\n        shape.y2 = y + h / 2;\n      },\n      rect: function (x, y, w, h, shape) {\n        shape.x = x;\n        shape.y = y;\n        shape.width = w;\n        shape.height = h;\n      },\n      roundRect: function (x, y, w, h, shape) {\n        shape.x = x;\n        shape.y = y;\n        shape.width = w;\n        shape.height = h;\n        shape.r = Math.min(w, h) / 4;\n      },\n      square: function (x, y, w, h, shape) {\n        var size = Math.min(w, h);\n        shape.x = x;\n        shape.y = y;\n        shape.width = size;\n        shape.height = size;\n      },\n      circle: function (x, y, w, h, shape) {\n        // Put circle in the center of square\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.r = Math.min(w, h) / 2;\n      },\n      diamond: function (x, y, w, h, shape) {\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      pin: function (x, y, w, h, shape) {\n        shape.x = x + w / 2;\n        shape.y = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      arrow: function (x, y, w, h, shape) {\n        shape.x = x + w / 2;\n        shape.y = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      triangle: function (x, y, w, h, shape) {\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      }\n    };\n    var symbolBuildProxies = {};\n    each(symbolCtors, function (Ctor, name) {\n      symbolBuildProxies[name] = new Ctor();\n    });\n    var SymbolClz = Path.extend({\n      type: 'symbol',\n      shape: {\n        symbolType: '',\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      calculateTextPosition: function (out, config, rect) {\n        var res = calculateTextPosition(out, config, rect);\n        var shape = this.shape;\n\n        if (shape && shape.symbolType === 'pin' && config.position === 'inside') {\n          res.y = rect.y + rect.height * 0.4;\n        }\n\n        return res;\n      },\n      buildPath: function (ctx, shape, inBundle) {\n        var symbolType = shape.symbolType;\n\n        if (symbolType !== 'none') {\n          var proxySymbol = symbolBuildProxies[symbolType];\n\n          if (!proxySymbol) {\n            // Default rect\n            symbolType = 'rect';\n            proxySymbol = symbolBuildProxies[symbolType];\n          }\n\n          symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape);\n          proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);\n        }\n      }\n    }); // Provide setColor helper method to avoid determine if set the fill or stroke outside\n\n    function symbolPathSetColor(color, innerColor) {\n      if (this.type !== 'image') {\n        var symbolStyle = this.style;\n\n        if (this.__isEmptyBrush) {\n          symbolStyle.stroke = color;\n          symbolStyle.fill = innerColor || '#fff'; // TODO Same width with lineStyle in LineView\n\n          symbolStyle.lineWidth = 2;\n        } else if (this.shape.symbolType === 'line') {\n          symbolStyle.stroke = color;\n        } else {\n          symbolStyle.fill = color;\n        }\n\n        this.markRedraw();\n      }\n    }\n    /**\n     * Create a symbol element with given symbol configuration: shape, x, y, width, height, color\n     */\n\n\n    function createSymbol(symbolType, x, y, w, h, color, // whether to keep the ratio of w/h,\n    keepAspect) {\n      // TODO Support image object, DynamicImage.\n      var isEmpty = symbolType.indexOf('empty') === 0;\n\n      if (isEmpty) {\n        symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);\n      }\n\n      var symbolPath;\n\n      if (symbolType.indexOf('image://') === 0) {\n        symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n      } else if (symbolType.indexOf('path://') === 0) {\n        symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n      } else {\n        symbolPath = new SymbolClz({\n          shape: {\n            symbolType: symbolType,\n            x: x,\n            y: y,\n            width: w,\n            height: h\n          }\n        });\n      }\n\n      symbolPath.__isEmptyBrush = isEmpty; // TODO Should deprecate setColor\n\n      symbolPath.setColor = symbolPathSetColor;\n\n      if (color) {\n        symbolPath.setColor(color);\n      }\n\n      return symbolPath;\n    }\n    function normalizeSymbolSize(symbolSize) {\n      if (!isArray(symbolSize)) {\n        symbolSize = [+symbolSize, +symbolSize];\n      }\n\n      return [symbolSize[0] || 0, symbolSize[1] || 0];\n    }\n    function normalizeSymbolOffset(symbolOffset, symbolSize) {\n      if (symbolOffset == null) {\n        return;\n      }\n\n      if (!isArray(symbolOffset)) {\n        symbolOffset = [symbolOffset, symbolOffset];\n      }\n\n      return [parsePercent$1(symbolOffset[0], symbolSize[0]) || 0, parsePercent$1(retrieve2(symbolOffset[1], symbolOffset[0]), symbolSize[1]) || 0];\n    }\n\n    function isSafeNum(num) {\n        return isFinite(num);\n    }\n    function createLinearGradient(ctx, obj, rect) {\n        var x = obj.x == null ? 0 : obj.x;\n        var x2 = obj.x2 == null ? 1 : obj.x2;\n        var y = obj.y == null ? 0 : obj.y;\n        var y2 = obj.y2 == null ? 0 : obj.y2;\n        if (!obj.global) {\n            x = x * rect.width + rect.x;\n            x2 = x2 * rect.width + rect.x;\n            y = y * rect.height + rect.y;\n            y2 = y2 * rect.height + rect.y;\n        }\n        x = isSafeNum(x) ? x : 0;\n        x2 = isSafeNum(x2) ? x2 : 1;\n        y = isSafeNum(y) ? y : 0;\n        y2 = isSafeNum(y2) ? y2 : 0;\n        var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);\n        return canvasGradient;\n    }\n    function createRadialGradient(ctx, obj, rect) {\n        var width = rect.width;\n        var height = rect.height;\n        var min = Math.min(width, height);\n        var x = obj.x == null ? 0.5 : obj.x;\n        var y = obj.y == null ? 0.5 : obj.y;\n        var r = obj.r == null ? 0.5 : obj.r;\n        if (!obj.global) {\n            x = x * width + rect.x;\n            y = y * height + rect.y;\n            r = r * min;\n        }\n        x = isSafeNum(x) ? x : 0.5;\n        y = isSafeNum(y) ? y : 0.5;\n        r = r >= 0 && isSafeNum(r) ? r : 0.5;\n        var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);\n        return canvasGradient;\n    }\n    function getCanvasGradient(ctx, obj, rect) {\n        var canvasGradient = obj.type === 'radial'\n            ? createRadialGradient(ctx, obj, rect)\n            : createLinearGradient(ctx, obj, rect);\n        var colorStops = obj.colorStops;\n        for (var i = 0; i < colorStops.length; i++) {\n            canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color);\n        }\n        return canvasGradient;\n    }\n    function isClipPathChanged(clipPaths, prevClipPaths) {\n        if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) {\n            return false;\n        }\n        if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {\n            return true;\n        }\n        for (var i = 0; i < clipPaths.length; i++) {\n            if (clipPaths[i] !== prevClipPaths[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n    function parseInt10(val) {\n        return parseInt(val, 10);\n    }\n    function getSize(root, whIdx, opts) {\n        var wh = ['width', 'height'][whIdx];\n        var cwh = ['clientWidth', 'clientHeight'][whIdx];\n        var plt = ['paddingLeft', 'paddingTop'][whIdx];\n        var prb = ['paddingRight', 'paddingBottom'][whIdx];\n        if (opts[wh] != null && opts[wh] !== 'auto') {\n            return parseFloat(opts[wh]);\n        }\n        var stl = document.defaultView.getComputedStyle(root);\n        return ((root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))\n            - (parseInt10(stl[plt]) || 0)\n            - (parseInt10(stl[prb]) || 0)) | 0;\n    }\n\n    function normalizeLineDash(lineType, lineWidth) {\n        if (!lineType || lineType === 'solid' || !(lineWidth > 0)) {\n            return null;\n        }\n        return lineType === 'dashed'\n            ? [4 * lineWidth, 2 * lineWidth]\n            : lineType === 'dotted'\n                ? [lineWidth]\n                : isNumber(lineType)\n                    ? [lineType] : isArray(lineType) ? lineType : null;\n    }\n    function getLineDash(el) {\n        var style = el.style;\n        var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);\n        var lineDashOffset = style.lineDashOffset;\n        if (lineDash) {\n            var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;\n            if (lineScale_1 && lineScale_1 !== 1) {\n                lineDash = map(lineDash, function (rawVal) {\n                    return rawVal / lineScale_1;\n                });\n                lineDashOffset /= lineScale_1;\n            }\n        }\n        return [lineDash, lineDashOffset];\n    }\n\n    var pathProxyForDraw = new PathProxy(true);\n    function styleHasStroke(style) {\n        var stroke = style.stroke;\n        return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n    }\n    function isValidStrokeFillStyle(strokeOrFill) {\n        return typeof strokeOrFill === 'string' && strokeOrFill !== 'none';\n    }\n    function styleHasFill(style) {\n        var fill = style.fill;\n        return fill != null && fill !== 'none';\n    }\n    function doFillPath(ctx, style) {\n        if (style.fillOpacity != null && style.fillOpacity !== 1) {\n            var originalGlobalAlpha = ctx.globalAlpha;\n            ctx.globalAlpha = style.fillOpacity * style.opacity;\n            ctx.fill();\n            ctx.globalAlpha = originalGlobalAlpha;\n        }\n        else {\n            ctx.fill();\n        }\n    }\n    function doStrokePath(ctx, style) {\n        if (style.strokeOpacity != null && style.strokeOpacity !== 1) {\n            var originalGlobalAlpha = ctx.globalAlpha;\n            ctx.globalAlpha = style.strokeOpacity * style.opacity;\n            ctx.stroke();\n            ctx.globalAlpha = originalGlobalAlpha;\n        }\n        else {\n            ctx.stroke();\n        }\n    }\n    function createCanvasPattern(ctx, pattern, el) {\n        var image = createOrUpdateImage(pattern.image, pattern.__image, el);\n        if (isImageReady(image)) {\n            var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat');\n            if (typeof DOMMatrix === 'function'\n                && canvasPattern\n                && canvasPattern.setTransform) {\n                var matrix = new DOMMatrix();\n                matrix.translateSelf((pattern.x || 0), (pattern.y || 0));\n                matrix.rotateSelf(0, 0, (pattern.rotation || 0) * RADIAN_TO_DEGREE);\n                matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1));\n                canvasPattern.setTransform(matrix);\n            }\n            return canvasPattern;\n        }\n    }\n    function brushPath(ctx, el, style, inBatch) {\n        var _a;\n        var hasStroke = styleHasStroke(style);\n        var hasFill = styleHasFill(style);\n        var strokePercent = style.strokePercent;\n        var strokePart = strokePercent < 1;\n        var firstDraw = !el.path;\n        if ((!el.silent || strokePart) && firstDraw) {\n            el.createPathProxy();\n        }\n        var path = el.path || pathProxyForDraw;\n        var dirtyFlag = el.__dirty;\n        if (!inBatch) {\n            var fill = style.fill;\n            var stroke = style.stroke;\n            var hasFillGradient = hasFill && !!fill.colorStops;\n            var hasStrokeGradient = hasStroke && !!stroke.colorStops;\n            var hasFillPattern = hasFill && !!fill.image;\n            var hasStrokePattern = hasStroke && !!stroke.image;\n            var fillGradient = void 0;\n            var strokeGradient = void 0;\n            var fillPattern = void 0;\n            var strokePattern = void 0;\n            var rect = void 0;\n            if (hasFillGradient || hasStrokeGradient) {\n                rect = el.getBoundingRect();\n            }\n            if (hasFillGradient) {\n                fillGradient = dirtyFlag\n                    ? getCanvasGradient(ctx, fill, rect)\n                    : el.__canvasFillGradient;\n                el.__canvasFillGradient = fillGradient;\n            }\n            if (hasStrokeGradient) {\n                strokeGradient = dirtyFlag\n                    ? getCanvasGradient(ctx, stroke, rect)\n                    : el.__canvasStrokeGradient;\n                el.__canvasStrokeGradient = strokeGradient;\n            }\n            if (hasFillPattern) {\n                fillPattern = (dirtyFlag || !el.__canvasFillPattern)\n                    ? createCanvasPattern(ctx, fill, el)\n                    : el.__canvasFillPattern;\n                el.__canvasFillPattern = fillPattern;\n            }\n            if (hasStrokePattern) {\n                strokePattern = (dirtyFlag || !el.__canvasStrokePattern)\n                    ? createCanvasPattern(ctx, stroke, el)\n                    : el.__canvasStrokePattern;\n                el.__canvasStrokePattern = fillPattern;\n            }\n            if (hasFillGradient) {\n                ctx.fillStyle = fillGradient;\n            }\n            else if (hasFillPattern) {\n                if (fillPattern) {\n                    ctx.fillStyle = fillPattern;\n                }\n                else {\n                    hasFill = false;\n                }\n            }\n            if (hasStrokeGradient) {\n                ctx.strokeStyle = strokeGradient;\n            }\n            else if (hasStrokePattern) {\n                if (strokePattern) {\n                    ctx.strokeStyle = strokePattern;\n                }\n                else {\n                    hasStroke = false;\n                }\n            }\n        }\n        var scale = el.getGlobalScale();\n        path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold);\n        var lineDash;\n        var lineDashOffset;\n        if (ctx.setLineDash && style.lineDash) {\n            _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n        }\n        var needsRebuild = true;\n        if (firstDraw || (dirtyFlag & SHAPE_CHANGED_BIT)) {\n            path.setDPR(ctx.dpr);\n            if (strokePart) {\n                path.setContext(null);\n            }\n            else {\n                path.setContext(ctx);\n                needsRebuild = false;\n            }\n            path.reset();\n            el.buildPath(path, el.shape, inBatch);\n            path.toStatic();\n            el.pathUpdated();\n        }\n        if (needsRebuild) {\n            path.rebuildPath(ctx, strokePart ? strokePercent : 1);\n        }\n        if (lineDash) {\n            ctx.setLineDash(lineDash);\n            ctx.lineDashOffset = lineDashOffset;\n        }\n        if (!inBatch) {\n            if (style.strokeFirst) {\n                if (hasStroke) {\n                    doStrokePath(ctx, style);\n                }\n                if (hasFill) {\n                    doFillPath(ctx, style);\n                }\n            }\n            else {\n                if (hasFill) {\n                    doFillPath(ctx, style);\n                }\n                if (hasStroke) {\n                    doStrokePath(ctx, style);\n                }\n            }\n        }\n        if (lineDash) {\n            ctx.setLineDash([]);\n        }\n    }\n    function brushImage(ctx, el, style) {\n        var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload);\n        if (!image || !isImageReady(image)) {\n            return;\n        }\n        var x = style.x || 0;\n        var y = style.y || 0;\n        var width = el.getWidth();\n        var height = el.getHeight();\n        var aspect = image.width / image.height;\n        if (width == null && height != null) {\n            width = height * aspect;\n        }\n        else if (height == null && width != null) {\n            height = width / aspect;\n        }\n        else if (width == null && height == null) {\n            width = image.width;\n            height = image.height;\n        }\n        if (style.sWidth && style.sHeight) {\n            var sx = style.sx || 0;\n            var sy = style.sy || 0;\n            ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height);\n        }\n        else if (style.sx && style.sy) {\n            var sx = style.sx;\n            var sy = style.sy;\n            var sWidth = width - sx;\n            var sHeight = height - sy;\n            ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height);\n        }\n        else {\n            ctx.drawImage(image, x, y, width, height);\n        }\n    }\n    function brushText(ctx, el, style) {\n        var _a;\n        var text = style.text;\n        text != null && (text += '');\n        if (text) {\n            ctx.font = style.font || DEFAULT_FONT;\n            ctx.textAlign = style.textAlign;\n            ctx.textBaseline = style.textBaseline;\n            var lineDash = void 0;\n            var lineDashOffset = void 0;\n            if (ctx.setLineDash && style.lineDash) {\n                _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n            }\n            if (lineDash) {\n                ctx.setLineDash(lineDash);\n                ctx.lineDashOffset = lineDashOffset;\n            }\n            if (style.strokeFirst) {\n                if (styleHasStroke(style)) {\n                    ctx.strokeText(text, style.x, style.y);\n                }\n                if (styleHasFill(style)) {\n                    ctx.fillText(text, style.x, style.y);\n                }\n            }\n            else {\n                if (styleHasFill(style)) {\n                    ctx.fillText(text, style.x, style.y);\n                }\n                if (styleHasStroke(style)) {\n                    ctx.strokeText(text, style.x, style.y);\n                }\n            }\n            if (lineDash) {\n                ctx.setLineDash([]);\n            }\n        }\n    }\n    var SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\n    var STROKE_PROPS = [\n        ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]\n    ];\n    function bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) {\n        var styleChanged = false;\n        if (!forceSetAll) {\n            prevStyle = prevStyle || {};\n            if (style === prevStyle) {\n                return false;\n            }\n        }\n        if (forceSetAll || style.opacity !== prevStyle.opacity) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n            var opacity = Math.max(Math.min(style.opacity, 1), 0);\n            ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity;\n        }\n        if (forceSetAll || style.blend !== prevStyle.blend) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend;\n        }\n        for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) {\n            var propName = SHADOW_NUMBER_PROPS[i];\n            if (forceSetAll || style[propName] !== prevStyle[propName]) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx[propName] = ctx.dpr * (style[propName] || 0);\n            }\n        }\n        if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor;\n        }\n        return styleChanged;\n    }\n    function bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) {\n        var style = getStyle(el, scope.inHover);\n        var prevStyle = forceSetAll\n            ? null\n            : (prevEl && getStyle(prevEl, scope.inHover) || {});\n        if (style === prevStyle) {\n            return false;\n        }\n        var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope);\n        if (forceSetAll || style.fill !== prevStyle.fill) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill);\n        }\n        if (forceSetAll || style.stroke !== prevStyle.stroke) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke);\n        }\n        if (forceSetAll || style.opacity !== prevStyle.opacity) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;\n        }\n        if (el.hasStroke()) {\n            var lineWidth = style.lineWidth;\n            var newLineWidth = lineWidth / ((style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1);\n            if (ctx.lineWidth !== newLineWidth) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx.lineWidth = newLineWidth;\n            }\n        }\n        for (var i = 0; i < STROKE_PROPS.length; i++) {\n            var prop = STROKE_PROPS[i];\n            var propName = prop[0];\n            if (forceSetAll || style[propName] !== prevStyle[propName]) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx[propName] = style[propName] || prop[1];\n            }\n        }\n        return styleChanged;\n    }\n    function bindImageStyle(ctx, el, prevEl, forceSetAll, scope) {\n        return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope);\n    }\n    function setContextTransform(ctx, el) {\n        var m = el.transform;\n        var dpr = ctx.dpr || 1;\n        if (m) {\n            ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);\n        }\n        else {\n            ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n        }\n    }\n    function updateClipStatus(clipPaths, ctx, scope) {\n        var allClipped = false;\n        for (var i = 0; i < clipPaths.length; i++) {\n            var clipPath = clipPaths[i];\n            allClipped = allClipped || clipPath.isZeroArea();\n            setContextTransform(ctx, clipPath);\n            ctx.beginPath();\n            clipPath.buildPath(ctx, clipPath.shape);\n            ctx.clip();\n        }\n        scope.allClipped = allClipped;\n    }\n    function isTransformChanged(m0, m1) {\n        if (m0 && m1) {\n            return m0[0] !== m1[0]\n                || m0[1] !== m1[1]\n                || m0[2] !== m1[2]\n                || m0[3] !== m1[3]\n                || m0[4] !== m1[4]\n                || m0[5] !== m1[5];\n        }\n        else if (!m0 && !m1) {\n            return false;\n        }\n        return true;\n    }\n    var DRAW_TYPE_PATH = 1;\n    var DRAW_TYPE_IMAGE = 2;\n    var DRAW_TYPE_TEXT = 3;\n    var DRAW_TYPE_INCREMENTAL = 4;\n    function canPathBatch(style) {\n        var hasFill = styleHasFill(style);\n        var hasStroke = styleHasStroke(style);\n        return !(style.lineDash\n            || !(+hasFill ^ +hasStroke)\n            || (hasFill && typeof style.fill !== 'string')\n            || (hasStroke && typeof style.stroke !== 'string')\n            || style.strokePercent < 1\n            || style.strokeOpacity < 1\n            || style.fillOpacity < 1);\n    }\n    function flushPathDrawn(ctx, scope) {\n        scope.batchFill && ctx.fill();\n        scope.batchStroke && ctx.stroke();\n        scope.batchFill = '';\n        scope.batchStroke = '';\n    }\n    function getStyle(el, inHover) {\n        return inHover ? (el.__hoverStyle || el.style) : el.style;\n    }\n    function brushSingle(ctx, el) {\n        brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true);\n    }\n    function brush(ctx, el, scope, isLast) {\n        var m = el.transform;\n        if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) {\n            el.__dirty &= ~REDRAW_BIT;\n            el.__isRendered = false;\n            return;\n        }\n        var clipPaths = el.__clipPaths;\n        var prevElClipPaths = scope.prevElClipPaths;\n        var forceSetTransform = false;\n        var forceSetStyle = false;\n        if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) {\n            if (prevElClipPaths && prevElClipPaths.length) {\n                flushPathDrawn(ctx, scope);\n                ctx.restore();\n                forceSetStyle = forceSetTransform = true;\n                scope.prevElClipPaths = null;\n                scope.allClipped = false;\n                scope.prevEl = null;\n            }\n            if (clipPaths && clipPaths.length) {\n                flushPathDrawn(ctx, scope);\n                ctx.save();\n                updateClipStatus(clipPaths, ctx, scope);\n                forceSetTransform = true;\n            }\n            scope.prevElClipPaths = clipPaths;\n        }\n        if (scope.allClipped) {\n            el.__isRendered = false;\n            return;\n        }\n        el.beforeBrush && el.beforeBrush();\n        el.innerBeforeBrush();\n        var prevEl = scope.prevEl;\n        if (!prevEl) {\n            forceSetStyle = forceSetTransform = true;\n        }\n        var canBatchPath = el instanceof Path\n            && el.autoBatch\n            && canPathBatch(el.style);\n        if (forceSetTransform || isTransformChanged(m, prevEl.transform)) {\n            flushPathDrawn(ctx, scope);\n            setContextTransform(ctx, el);\n        }\n        else if (!canBatchPath) {\n            flushPathDrawn(ctx, scope);\n        }\n        var style = getStyle(el, scope.inHover);\n        if (el instanceof Path) {\n            if (scope.lastDrawType !== DRAW_TYPE_PATH) {\n                forceSetStyle = true;\n                scope.lastDrawType = DRAW_TYPE_PATH;\n            }\n            bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n            if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) {\n                ctx.beginPath();\n            }\n            brushPath(ctx, el, style, canBatchPath);\n            if (canBatchPath) {\n                scope.batchFill = style.fill || '';\n                scope.batchStroke = style.stroke || '';\n            }\n        }\n        else {\n            if (el instanceof TSpan) {\n                if (scope.lastDrawType !== DRAW_TYPE_TEXT) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_TEXT;\n                }\n                bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n                brushText(ctx, el, style);\n            }\n            else if (el instanceof ZRImage) {\n                if (scope.lastDrawType !== DRAW_TYPE_IMAGE) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_IMAGE;\n                }\n                bindImageStyle(ctx, el, prevEl, forceSetStyle, scope);\n                brushImage(ctx, el, style);\n            }\n            else if (el.getTemporalDisplayables) {\n                if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_INCREMENTAL;\n                }\n                brushIncremental(ctx, el, scope);\n            }\n        }\n        if (canBatchPath && isLast) {\n            flushPathDrawn(ctx, scope);\n        }\n        el.innerAfterBrush();\n        el.afterBrush && el.afterBrush();\n        scope.prevEl = el;\n        el.__dirty = 0;\n        el.__isRendered = true;\n    }\n    function brushIncremental(ctx, el, scope) {\n        var displayables = el.getDisplayables();\n        var temporalDisplayables = el.getTemporalDisplayables();\n        ctx.save();\n        var innerScope = {\n            prevElClipPaths: null,\n            prevEl: null,\n            allClipped: false,\n            viewWidth: scope.viewWidth,\n            viewHeight: scope.viewHeight,\n            inHover: scope.inHover\n        };\n        var i;\n        var len;\n        for (i = el.getCursor(), len = displayables.length; i < len; i++) {\n            var displayable = displayables[i];\n            displayable.beforeBrush && displayable.beforeBrush();\n            displayable.innerBeforeBrush();\n            brush(ctx, displayable, innerScope, i === len - 1);\n            displayable.innerAfterBrush();\n            displayable.afterBrush && displayable.afterBrush();\n            innerScope.prevEl = displayable;\n        }\n        for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) {\n            var displayable = temporalDisplayables[i_1];\n            displayable.beforeBrush && displayable.beforeBrush();\n            displayable.innerBeforeBrush();\n            brush(ctx, displayable, innerScope, i_1 === len_1 - 1);\n            displayable.innerAfterBrush();\n            displayable.afterBrush && displayable.afterBrush();\n            innerScope.prevEl = displayable;\n        }\n        el.clearTemporalDisplayables();\n        el.notClear = true;\n        ctx.restore();\n    }\n\n    var decalMap = new WeakMap();\n    var decalCache = new LRU(100);\n    var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight'];\n    /**\n     * Create or update pattern image from decal options\n     *\n     * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal\n     * @return {Pattern} pattern with generated image, null if no decal\n     */\n\n    function createOrUpdatePatternFromDecal(decalObject, api) {\n      if (decalObject === 'none') {\n        return null;\n      }\n\n      var dpr = api.getDevicePixelRatio();\n      var zr = api.getZr();\n      var isSVG = zr.painter.type === 'svg';\n\n      if (decalObject.dirty) {\n        decalMap[\"delete\"](decalObject);\n      }\n\n      var oldPattern = decalMap.get(decalObject);\n\n      if (oldPattern) {\n        return oldPattern;\n      }\n\n      var decalOpt = defaults(decalObject, {\n        symbol: 'rect',\n        symbolSize: 1,\n        symbolKeepAspect: true,\n        color: 'rgba(0, 0, 0, 0.2)',\n        backgroundColor: null,\n        dashArrayX: 5,\n        dashArrayY: 5,\n        rotation: 0,\n        maxTileWidth: 512,\n        maxTileHeight: 512\n      });\n\n      if (decalOpt.backgroundColor === 'none') {\n        decalOpt.backgroundColor = null;\n      }\n\n      var pattern = {\n        repeat: 'repeat'\n      };\n      setPatternnSource(pattern);\n      pattern.rotation = decalOpt.rotation;\n      pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr;\n      decalMap.set(decalObject, pattern);\n      decalObject.dirty = false;\n      return pattern;\n\n      function setPatternnSource(pattern) {\n        var keys = [dpr];\n        var isValidKey = true;\n\n        for (var i = 0; i < decalKeys.length; ++i) {\n          var value = decalOpt[decalKeys[i]];\n\n          if (value != null && !isArray(value) && !isString(value) && !isNumber(value) && typeof value !== 'boolean') {\n            isValidKey = false;\n            break;\n          }\n\n          keys.push(value);\n        }\n\n        var cacheKey;\n\n        if (isValidKey) {\n          cacheKey = keys.join(',') + (isSVG ? '-svg' : '');\n          var cache = decalCache.get(cacheKey);\n\n          if (cache) {\n            isSVG ? pattern.svgElement = cache : pattern.image = cache;\n          }\n        }\n\n        var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX);\n        var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY);\n        var symbolArray = normalizeSymbolArray(decalOpt.symbol);\n        var lineBlockLengthsX = getLineBlockLengthX(dashArrayX);\n        var lineBlockLengthY = getLineBlockLengthY(dashArrayY);\n        var canvas = !isSVG && platformApi.createCanvas();\n        var svgRoot = isSVG && {\n          tag: 'g',\n          attrs: {},\n          key: 'dcl',\n          children: []\n        };\n        var pSize = getPatternSize();\n        var ctx;\n\n        if (canvas) {\n          canvas.width = pSize.width * dpr;\n          canvas.height = pSize.height * dpr;\n          ctx = canvas.getContext('2d');\n        }\n\n        brushDecal();\n\n        if (isValidKey) {\n          decalCache.put(cacheKey, canvas || svgRoot);\n        }\n\n        pattern.image = canvas;\n        pattern.svgElement = svgRoot;\n        pattern.svgWidth = pSize.width;\n        pattern.svgHeight = pSize.height;\n        /**\n         * Get minimum length that can make a repeatable pattern.\n         *\n         * @return {Object} pattern width and height\n         */\n\n        function getPatternSize() {\n          /**\n           * For example, if dash is [[3, 2], [2, 1]] for X, it looks like\n           * |---  ---  ---  ---  --- ...\n           * |-- -- -- -- -- -- -- -- ...\n           * |---  ---  ---  ---  --- ...\n           * |-- -- -- -- -- -- -- -- ...\n           * So the minimum length of X is 15,\n           * which is the least common multiple of `3 + 2` and `2 + 1`\n           * |---  ---  ---  |---  --- ...\n           * |-- -- -- -- -- |-- -- -- ...\n           */\n          var width = 1;\n\n          for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) {\n            width = getLeastCommonMultiple(width, lineBlockLengthsX[i]);\n          }\n\n          var symbolRepeats = 1;\n\n          for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) {\n            symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length);\n          }\n\n          width *= symbolRepeats;\n          var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length;\n\n          if (\"development\" !== 'production') {\n            var warn = function (attrName) {\n              /* eslint-disable-next-line */\n              console.warn(\"Calculated decal size is greater than \" + attrName + \" due to decal option settings so \" + attrName + \" is used for the decal size. Please consider changing the decal option to make a smaller decal or set \" + attrName + \" to be larger to avoid incontinuity.\");\n            };\n\n            if (width > decalOpt.maxTileWidth) {\n              warn('maxTileWidth');\n            }\n\n            if (height > decalOpt.maxTileHeight) {\n              warn('maxTileHeight');\n            }\n          }\n\n          return {\n            width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)),\n            height: Math.max(1, Math.min(height, decalOpt.maxTileHeight))\n          };\n        }\n\n        function brushDecal() {\n          if (ctx) {\n            ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n            if (decalOpt.backgroundColor) {\n              ctx.fillStyle = decalOpt.backgroundColor;\n              ctx.fillRect(0, 0, canvas.width, canvas.height);\n            }\n          }\n\n          var ySum = 0;\n\n          for (var i = 0; i < dashArrayY.length; ++i) {\n            ySum += dashArrayY[i];\n          }\n\n          if (ySum <= 0) {\n            // dashArrayY is 0, draw nothing\n            return;\n          }\n\n          var y = -lineBlockLengthY;\n          var yId = 0;\n          var yIdTotal = 0;\n          var xId0 = 0;\n\n          while (y < pSize.height) {\n            if (yId % 2 === 0) {\n              var symbolYId = yIdTotal / 2 % symbolArray.length;\n              var x = 0;\n              var xId1 = 0;\n              var xId1Total = 0;\n\n              while (x < pSize.width * 2) {\n                var xSum = 0;\n\n                for (var i = 0; i < dashArrayX[xId0].length; ++i) {\n                  xSum += dashArrayX[xId0][i];\n                }\n\n                if (xSum <= 0) {\n                  // Skip empty line\n                  break;\n                } // E.g., [15, 5, 20, 5] draws only for 15 and 20\n\n\n                if (xId1 % 2 === 0) {\n                  var size = (1 - decalOpt.symbolSize) * 0.5;\n                  var left = x + dashArrayX[xId0][xId1] * size;\n                  var top_1 = y + dashArrayY[yId] * size;\n                  var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize;\n                  var height = dashArrayY[yId] * decalOpt.symbolSize;\n                  var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length;\n                  brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]);\n                }\n\n                x += dashArrayX[xId0][xId1];\n                ++xId1Total;\n                ++xId1;\n\n                if (xId1 === dashArrayX[xId0].length) {\n                  xId1 = 0;\n                }\n              }\n\n              ++xId0;\n\n              if (xId0 === dashArrayX.length) {\n                xId0 = 0;\n              }\n            }\n\n            y += dashArrayY[yId];\n            ++yIdTotal;\n            ++yId;\n\n            if (yId === dashArrayY.length) {\n              yId = 0;\n            }\n          }\n\n          function brushSymbol(x, y, width, height, symbolType) {\n            var scale = isSVG ? 1 : dpr;\n            var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect);\n\n            if (isSVG) {\n              var symbolVNode = zr.painter.renderOneToVNode(symbol);\n\n              if (symbolVNode) {\n                svgRoot.children.push(symbolVNode);\n              }\n            } else {\n              // Paint to canvas for all other renderers.\n              brushSingle(ctx, symbol);\n            }\n          }\n        }\n      }\n    }\n    /**\n     * Convert symbol array into normalized array\n     *\n     * @param {string | (string | string[])[]} symbol symbol input\n     * @return {string[][]} normolized symbol array\n     */\n\n    function normalizeSymbolArray(symbol) {\n      if (!symbol || symbol.length === 0) {\n        return [['rect']];\n      }\n\n      if (isString(symbol)) {\n        return [[symbol]];\n      }\n\n      var isAllString = true;\n\n      for (var i = 0; i < symbol.length; ++i) {\n        if (!isString(symbol[i])) {\n          isAllString = false;\n          break;\n        }\n      }\n\n      if (isAllString) {\n        return normalizeSymbolArray([symbol]);\n      }\n\n      var result = [];\n\n      for (var i = 0; i < symbol.length; ++i) {\n        if (isString(symbol[i])) {\n          result.push([symbol[i]]);\n        } else {\n          result.push(symbol[i]);\n        }\n      }\n\n      return result;\n    }\n    /**\n     * Convert dash input into dashArray\n     *\n     * @param {DecalDashArrayX} dash dash input\n     * @return {number[][]} normolized dash array\n     */\n\n\n    function normalizeDashArrayX(dash) {\n      if (!dash || dash.length === 0) {\n        return [[0, 0]];\n      }\n\n      if (isNumber(dash)) {\n        var dashValue = Math.ceil(dash);\n        return [[dashValue, dashValue]];\n      }\n      /**\n       * [20, 5] should be normalized into [[20, 5]],\n       * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]]\n       */\n\n\n      var isAllNumber = true;\n\n      for (var i = 0; i < dash.length; ++i) {\n        if (!isNumber(dash[i])) {\n          isAllNumber = false;\n          break;\n        }\n      }\n\n      if (isAllNumber) {\n        return normalizeDashArrayX([dash]);\n      }\n\n      var result = [];\n\n      for (var i = 0; i < dash.length; ++i) {\n        if (isNumber(dash[i])) {\n          var dashValue = Math.ceil(dash[i]);\n          result.push([dashValue, dashValue]);\n        } else {\n          var dashValue = map(dash[i], function (n) {\n            return Math.ceil(n);\n          });\n\n          if (dashValue.length % 2 === 1) {\n            // [4, 2, 1] means |----  -    -- |----  -    -- |\n            // so normalize it to be [4, 2, 1, 4, 2, 1]\n            result.push(dashValue.concat(dashValue));\n          } else {\n            result.push(dashValue);\n          }\n        }\n      }\n\n      return result;\n    }\n    /**\n     * Convert dash input into dashArray\n     *\n     * @param {DecalDashArrayY} dash dash input\n     * @return {number[]} normolized dash array\n     */\n\n\n    function normalizeDashArrayY(dash) {\n      if (!dash || typeof dash === 'object' && dash.length === 0) {\n        return [0, 0];\n      }\n\n      if (isNumber(dash)) {\n        var dashValue_1 = Math.ceil(dash);\n        return [dashValue_1, dashValue_1];\n      }\n\n      var dashValue = map(dash, function (n) {\n        return Math.ceil(n);\n      });\n      return dash.length % 2 ? dashValue.concat(dashValue) : dashValue;\n    }\n    /**\n     * Get block length of each line. A block is the length of dash line and space.\n     * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after\n     * that, so the block length of this line is 5.\n     *\n     * @param {number[][]} dash dash array of X or Y\n     * @return {number[]} block length of each line\n     */\n\n\n    function getLineBlockLengthX(dash) {\n      return map(dash, function (line) {\n        return getLineBlockLengthY(line);\n      });\n    }\n\n    function getLineBlockLengthY(dash) {\n      var blockLength = 0;\n\n      for (var i = 0; i < dash.length; ++i) {\n        blockLength += dash[i];\n      }\n\n      if (dash.length % 2 === 1) {\n        // [4, 2, 1] means |----  -    -- |----  -    -- |\n        // So total length is (4 + 2 + 1) * 2\n        return blockLength * 2;\n      }\n\n      return blockLength;\n    }\n\n    function decalVisual(ecModel, api) {\n      ecModel.eachRawSeries(function (seriesModel) {\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n\n        if (data.hasItemVisual()) {\n          data.each(function (idx) {\n            var decal = data.getItemVisual(idx, 'decal');\n\n            if (decal) {\n              var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n              itemStyle.decal = createOrUpdatePatternFromDecal(decal, api);\n            }\n          });\n        }\n\n        var decal = data.getVisual('decal');\n\n        if (decal) {\n          var style = data.getVisual('style');\n          style.decal = createOrUpdatePatternFromDecal(decal, api);\n        }\n      });\n    }\n\n    var lifecycle = new Eventful();\n\n    // The implementations will be registered when installing the component.\n    // Avoid these code being bundled to the core module.\n\n    var implsStore = {}; // TODO Type\n\n    function registerImpl(name, impl) {\n      if (\"development\" !== 'production') {\n        if (implsStore[name]) {\n          error(\"Already has an implementation of \" + name + \".\");\n        }\n      }\n\n      implsStore[name] = impl;\n    }\n    function getImpl(name) {\n      if (\"development\" !== 'production') {\n        if (!implsStore[name]) {\n          error(\"Implementation of \" + name + \" doesn't exists.\");\n        }\n      }\n\n      return implsStore[name];\n    }\n\n    var version$1 = '5.4.1';\n    var dependencies = {\n      zrender: '5.4.1'\n    };\n    var TEST_FRAME_REMAIN_TIME = 1;\n    var PRIORITY_PROCESSOR_SERIES_FILTER = 800; // Some data processors depends on the stack result dimension (to calculate data extent).\n    // So data stack stage should be in front of data processing stage.\n\n    var PRIORITY_PROCESSOR_DATASTACK = 900; // \"Data filter\" will block the stream, so it should be\n    // put at the beginning of data processing.\n\n    var PRIORITY_PROCESSOR_FILTER = 1000;\n    var PRIORITY_PROCESSOR_DEFAULT = 2000;\n    var PRIORITY_PROCESSOR_STATISTIC = 5000;\n    var PRIORITY_VISUAL_LAYOUT = 1000;\n    var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100;\n    var PRIORITY_VISUAL_GLOBAL = 2000;\n    var PRIORITY_VISUAL_CHART = 3000;\n    var PRIORITY_VISUAL_COMPONENT = 4000; // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to\n    // overwrite the viusal result of component (like `visualMap`)\n    // using data item specific setting (like itemStyle.xxx on data item)\n\n    var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on\n    // visual result like `symbolSize`.\n\n    var PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600;\n    var PRIORITY_VISUAL_BRUSH = 5000;\n    var PRIORITY_VISUAL_ARIA = 6000;\n    var PRIORITY_VISUAL_DECAL = 7000;\n    var PRIORITY = {\n      PROCESSOR: {\n        FILTER: PRIORITY_PROCESSOR_FILTER,\n        SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER,\n        STATISTIC: PRIORITY_PROCESSOR_STATISTIC\n      },\n      VISUAL: {\n        LAYOUT: PRIORITY_VISUAL_LAYOUT,\n        PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT,\n        GLOBAL: PRIORITY_VISUAL_GLOBAL,\n        CHART: PRIORITY_VISUAL_CHART,\n        POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT,\n        COMPONENT: PRIORITY_VISUAL_COMPONENT,\n        BRUSH: PRIORITY_VISUAL_BRUSH,\n        CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM,\n        ARIA: PRIORITY_VISUAL_ARIA,\n        DECAL: PRIORITY_VISUAL_DECAL\n      }\n    }; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,\n    // where they must not be invoked nestedly, except the only case: invoke\n    // dispatchAction with updateMethod \"none\" in main process.\n    // This flag is used to carry out this rule.\n    // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).\n\n    var IN_MAIN_PROCESS_KEY = '__flagInMainProcess';\n    var PENDING_UPDATE = '__pendingUpdate';\n    var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus';\n    var ACTION_REG = /^[a-zA-Z0-9_]+$/;\n    var CONNECT_STATUS_KEY = '__connectUpdateStatus';\n    var CONNECT_STATUS_PENDING = 0;\n    var CONNECT_STATUS_UPDATING = 1;\n    var CONNECT_STATUS_UPDATED = 2;\n\n    function createRegisterEventWithLowercaseECharts(method) {\n      return function () {\n        var args = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args[_i] = arguments[_i];\n        }\n\n        if (this.isDisposed()) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        return toLowercaseNameAndCallEventful(this, method, args);\n      };\n    }\n\n    function createRegisterEventWithLowercaseMessageCenter(method) {\n      return function () {\n        var args = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args[_i] = arguments[_i];\n        }\n\n        return toLowercaseNameAndCallEventful(this, method, args);\n      };\n    }\n\n    function toLowercaseNameAndCallEventful(host, method, args) {\n      // `args[0]` is event name. Event name is all lowercase.\n      args[0] = args[0] && args[0].toLowerCase();\n      return Eventful.prototype[method].apply(host, args);\n    }\n\n    var MessageCenter =\n    /** @class */\n    function (_super) {\n      __extends(MessageCenter, _super);\n\n      function MessageCenter() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return MessageCenter;\n    }(Eventful);\n\n    var messageCenterProto = MessageCenter.prototype;\n    messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on');\n    messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); // ---------------------------------------\n    // Internal method names for class ECharts\n    // ---------------------------------------\n\n    var prepare;\n    var prepareView;\n    var updateDirectly;\n    var updateMethods;\n    var doConvertPixel;\n    var updateStreamModes;\n    var doDispatchAction;\n    var flushPendingActions;\n    var triggerUpdatedEvent;\n    var bindRenderedEvent;\n    var bindMouseEvent;\n    var render;\n    var renderComponents;\n    var renderSeries;\n    var createExtensionAPI;\n    var enableConnect;\n    var markStatusToUpdate;\n    var applyChangedStates;\n\n    var ECharts =\n    /** @class */\n    function (_super) {\n      __extends(ECharts, _super);\n\n      function ECharts(dom, // Theme name or themeOption.\n      theme, opts) {\n        var _this = _super.call(this, new ECEventProcessor()) || this;\n\n        _this._chartsViews = [];\n        _this._chartsMap = {};\n        _this._componentsViews = [];\n        _this._componentsMap = {}; // Can't dispatch action during rendering procedure\n\n        _this._pendingActions = [];\n        opts = opts || {}; // Get theme by name\n\n        if (isString(theme)) {\n          theme = themeStorage[theme];\n        }\n\n        _this._dom = dom;\n        var defaultRenderer = 'canvas';\n        var defaultCoarsePointer = 'auto';\n        var defaultUseDirtyRect = false;\n\n        if (\"development\" !== 'production') {\n          var root =\n          /* eslint-disable-next-line */\n          env.hasGlobalWindow ? window : global;\n          defaultRenderer = root.__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;\n          defaultCoarsePointer = retrieve2(root.__ECHARTS__DEFAULT__COARSE_POINTER, defaultCoarsePointer);\n          var devUseDirtyRect = root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__;\n          defaultUseDirtyRect = devUseDirtyRect == null ? defaultUseDirtyRect : devUseDirtyRect;\n        }\n\n        var zr = _this._zr = init(dom, {\n          renderer: opts.renderer || defaultRenderer,\n          devicePixelRatio: opts.devicePixelRatio,\n          width: opts.width,\n          height: opts.height,\n          ssr: opts.ssr,\n          useDirtyRect: retrieve2(opts.useDirtyRect, defaultUseDirtyRect),\n          useCoarsePointer: retrieve2(opts.useCoarsePointer, defaultCoarsePointer),\n          pointerSize: opts.pointerSize\n        });\n        _this._ssr = opts.ssr; // Expect 60 fps.\n\n        _this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);\n        theme = clone(theme);\n        theme && globalBackwardCompat(theme, true);\n        _this._theme = theme;\n        _this._locale = createLocaleObject(opts.locale || SYSTEM_LANG);\n        _this._coordSysMgr = new CoordinateSystemManager();\n        var api = _this._api = createExtensionAPI(_this); // Sort on demand\n\n        function prioritySortFunc(a, b) {\n          return a.__prio - b.__prio;\n        }\n\n        sort(visualFuncs, prioritySortFunc);\n        sort(dataProcessorFuncs, prioritySortFunc);\n        _this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs);\n        _this._messageCenter = new MessageCenter(); // Init mouse events\n\n        _this._initEvents(); // In case some people write `window.onresize = chart.resize`\n\n\n        _this.resize = bind(_this.resize, _this);\n        zr.animation.on('frame', _this._onframe, _this);\n        bindRenderedEvent(zr, _this);\n        bindMouseEvent(zr, _this); // ECharts instance can be used as value.\n\n        setAsPrimitive(_this);\n        return _this;\n      }\n\n      ECharts.prototype._onframe = function () {\n        if (this._disposed) {\n          return;\n        }\n\n        applyChangedStates(this);\n        var scheduler = this._scheduler; // Lazy update\n\n        if (this[PENDING_UPDATE]) {\n          var silent = this[PENDING_UPDATE].silent;\n          this[IN_MAIN_PROCESS_KEY] = true;\n\n          try {\n            prepare(this);\n            updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams);\n          } catch (e) {\n            this[IN_MAIN_PROCESS_KEY] = false;\n            this[PENDING_UPDATE] = null;\n            throw e;\n          } // At present, in each frame, zrender performs:\n          //   (1) animation step forward.\n          //   (2) trigger('frame') (where this `_onframe` is called)\n          //   (3) zrender flush (render).\n          // If we do nothing here, since we use `setToFinal: true`, the step (3) above\n          // will render the final state of the elements before the real animation started.\n\n\n          this._zr.flush();\n\n          this[IN_MAIN_PROCESS_KEY] = false;\n          this[PENDING_UPDATE] = null;\n          flushPendingActions.call(this, silent);\n          triggerUpdatedEvent.call(this, silent);\n        } // Avoid do both lazy update and progress in one frame.\n        else if (scheduler.unfinished) {\n            // Stream progress.\n            var remainTime = TEST_FRAME_REMAIN_TIME;\n            var ecModel = this._model;\n            var api = this._api;\n            scheduler.unfinished = false;\n\n            do {\n              var startTime = +new Date();\n              scheduler.performSeriesTasks(ecModel); // Currently dataProcessorFuncs do not check threshold.\n\n              scheduler.performDataProcessorTasks(ecModel);\n              updateStreamModes(this, ecModel); // Do not update coordinate system here. Because that coord system update in\n              // each frame is not a good user experience. So we follow the rule that\n              // the extent of the coordinate system is determined in the first frame (the\n              // frame is executed immediately after task reset.\n              // this._coordSysMgr.update(ecModel, api);\n              // console.log('--- ec frame visual ---', remainTime);\n\n              scheduler.performVisualTasks(ecModel);\n              renderSeries(this, this._model, api, 'remain', {});\n              remainTime -= +new Date() - startTime;\n            } while (remainTime > 0 && scheduler.unfinished); // Call flush explicitly for trigger finished event.\n\n\n            if (!scheduler.unfinished) {\n              this._zr.flush();\n            } // Else, zr flushing be ensue within the same frame,\n            // because zr flushing is after onframe event.\n\n          }\n      };\n\n      ECharts.prototype.getDom = function () {\n        return this._dom;\n      };\n\n      ECharts.prototype.getId = function () {\n        return this.id;\n      };\n\n      ECharts.prototype.getZr = function () {\n        return this._zr;\n      };\n\n      ECharts.prototype.isSSR = function () {\n        return this._ssr;\n      };\n      /* eslint-disable-next-line */\n\n\n      ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) {\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          if (\"development\" !== 'production') {\n            error('`setOption` should not be called during main process.');\n          }\n\n          return;\n        }\n\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var silent;\n        var replaceMerge;\n        var transitionOpt;\n\n        if (isObject(notMerge)) {\n          lazyUpdate = notMerge.lazyUpdate;\n          silent = notMerge.silent;\n          replaceMerge = notMerge.replaceMerge;\n          transitionOpt = notMerge.transition;\n          notMerge = notMerge.notMerge;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = true;\n\n        if (!this._model || notMerge) {\n          var optionManager = new OptionManager(this._api);\n          var theme = this._theme;\n          var ecModel = this._model = new GlobalModel();\n          ecModel.scheduler = this._scheduler;\n          ecModel.ssr = this._ssr;\n          ecModel.init(null, null, null, theme, this._locale, optionManager);\n        }\n\n        this._model.setOption(option, {\n          replaceMerge: replaceMerge\n        }, optionPreprocessorFuncs);\n\n        var updateParams = {\n          seriesTransition: transitionOpt,\n          optionChanged: true\n        };\n\n        if (lazyUpdate) {\n          this[PENDING_UPDATE] = {\n            silent: silent,\n            updateParams: updateParams\n          };\n          this[IN_MAIN_PROCESS_KEY] = false; // `setOption(option, {lazyMode: true})` may be called when zrender has been slept.\n          // It should wake it up to make sure zrender start to render at the next frame.\n\n          this.getZr().wakeUp();\n        } else {\n          try {\n            prepare(this);\n            updateMethods.update.call(this, null, updateParams);\n          } catch (e) {\n            this[PENDING_UPDATE] = null;\n            this[IN_MAIN_PROCESS_KEY] = false;\n            throw e;\n          } // Ensure zr refresh sychronously, and then pixel in canvas can be\n          // fetched after `setOption`.\n\n\n          if (!this._ssr) {\n            // not use flush when using ssr mode.\n            this._zr.flush();\n          }\n\n          this[PENDING_UPDATE] = null;\n          this[IN_MAIN_PROCESS_KEY] = false;\n          flushPendingActions.call(this, silent);\n          triggerUpdatedEvent.call(this, silent);\n        }\n      };\n      /**\n       * @deprecated\n       */\n\n\n      ECharts.prototype.setTheme = function () {\n        deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0');\n      }; // We don't want developers to use getModel directly.\n\n\n      ECharts.prototype.getModel = function () {\n        return this._model;\n      };\n\n      ECharts.prototype.getOption = function () {\n        return this._model && this._model.getOption();\n      };\n\n      ECharts.prototype.getWidth = function () {\n        return this._zr.getWidth();\n      };\n\n      ECharts.prototype.getHeight = function () {\n        return this._zr.getHeight();\n      };\n\n      ECharts.prototype.getDevicePixelRatio = function () {\n        return this._zr.painter.dpr\n        /* eslint-disable-next-line */\n        || env.hasGlobalWindow && window.devicePixelRatio || 1;\n      };\n      /**\n       * Get canvas which has all thing rendered\n       * @deprecated Use renderToCanvas instead.\n       */\n\n\n      ECharts.prototype.getRenderedCanvas = function (opts) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas');\n        }\n\n        return this.renderToCanvas(opts);\n      };\n\n      ECharts.prototype.renderToCanvas = function (opts) {\n        opts = opts || {};\n        var painter = this._zr.painter;\n\n        if (\"development\" !== 'production') {\n          if (painter.type !== 'canvas') {\n            throw new Error('renderToCanvas can only be used in the canvas renderer.');\n          }\n        }\n\n        return painter.getRenderedCanvas({\n          backgroundColor: opts.backgroundColor || this._model.get('backgroundColor'),\n          pixelRatio: opts.pixelRatio || this.getDevicePixelRatio()\n        });\n      };\n\n      ECharts.prototype.renderToSVGString = function (opts) {\n        opts = opts || {};\n        var painter = this._zr.painter;\n\n        if (\"development\" !== 'production') {\n          if (painter.type !== 'svg') {\n            throw new Error('renderToSVGString can only be used in the svg renderer.');\n          }\n        }\n\n        return painter.renderToString({\n          useViewBox: opts.useViewBox\n        });\n      };\n      /**\n       * Get svg data url\n       */\n\n\n      ECharts.prototype.getSvgDataURL = function () {\n        if (!env.svgSupported) {\n          return;\n        }\n\n        var zr = this._zr;\n        var list = zr.storage.getDisplayList(); // Stop animations\n\n        each(list, function (el) {\n          el.stopAnimation(null, true);\n        });\n        return zr.painter.toDataURL();\n      };\n\n      ECharts.prototype.getDataURL = function (opts) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        opts = opts || {};\n        var excludeComponents = opts.excludeComponents;\n        var ecModel = this._model;\n        var excludesComponentViews = [];\n        var self = this;\n        each(excludeComponents, function (componentType) {\n          ecModel.eachComponent({\n            mainType: componentType\n          }, function (component) {\n            var view = self._componentsMap[component.__viewId];\n\n            if (!view.group.ignore) {\n              excludesComponentViews.push(view);\n              view.group.ignore = true;\n            }\n          });\n        });\n        var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() : this.renderToCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));\n        each(excludesComponentViews, function (view) {\n          view.group.ignore = false;\n        });\n        return url;\n      };\n\n      ECharts.prototype.getConnectedDataURL = function (opts) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var isSvg = opts.type === 'svg';\n        var groupId = this.group;\n        var mathMin = Math.min;\n        var mathMax = Math.max;\n        var MAX_NUMBER = Infinity;\n\n        if (connectedGroups[groupId]) {\n          var left_1 = MAX_NUMBER;\n          var top_1 = MAX_NUMBER;\n          var right_1 = -MAX_NUMBER;\n          var bottom_1 = -MAX_NUMBER;\n          var canvasList_1 = [];\n          var dpr_1 = opts && opts.pixelRatio || this.getDevicePixelRatio();\n          each(instances$1, function (chart, id) {\n            if (chart.group === groupId) {\n              var canvas = isSvg ? chart.getZr().painter.getSvgDom().innerHTML : chart.renderToCanvas(clone(opts));\n              var boundingRect = chart.getDom().getBoundingClientRect();\n              left_1 = mathMin(boundingRect.left, left_1);\n              top_1 = mathMin(boundingRect.top, top_1);\n              right_1 = mathMax(boundingRect.right, right_1);\n              bottom_1 = mathMax(boundingRect.bottom, bottom_1);\n              canvasList_1.push({\n                dom: canvas,\n                left: boundingRect.left,\n                top: boundingRect.top\n              });\n            }\n          });\n          left_1 *= dpr_1;\n          top_1 *= dpr_1;\n          right_1 *= dpr_1;\n          bottom_1 *= dpr_1;\n          var width = right_1 - left_1;\n          var height = bottom_1 - top_1;\n          var targetCanvas = platformApi.createCanvas();\n          var zr_1 = init(targetCanvas, {\n            renderer: isSvg ? 'svg' : 'canvas'\n          });\n          zr_1.resize({\n            width: width,\n            height: height\n          });\n\n          if (isSvg) {\n            var content_1 = '';\n            each(canvasList_1, function (item) {\n              var x = item.left - left_1;\n              var y = item.top - top_1;\n              content_1 += '<g transform=\"translate(' + x + ',' + y + ')\">' + item.dom + '</g>';\n            });\n            zr_1.painter.getSvgRoot().innerHTML = content_1;\n\n            if (opts.connectedBackgroundColor) {\n              zr_1.painter.setBackgroundColor(opts.connectedBackgroundColor);\n            }\n\n            zr_1.refreshImmediately();\n            return zr_1.painter.toDataURL();\n          } else {\n            // Background between the charts\n            if (opts.connectedBackgroundColor) {\n              zr_1.add(new Rect({\n                shape: {\n                  x: 0,\n                  y: 0,\n                  width: width,\n                  height: height\n                },\n                style: {\n                  fill: opts.connectedBackgroundColor\n                }\n              }));\n            }\n\n            each(canvasList_1, function (item) {\n              var img = new ZRImage({\n                style: {\n                  x: item.left * dpr_1 - left_1,\n                  y: item.top * dpr_1 - top_1,\n                  image: item.dom\n                }\n              });\n              zr_1.add(img);\n            });\n            zr_1.refreshImmediately();\n            return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));\n          }\n        } else {\n          return this.getDataURL(opts);\n        }\n      };\n\n      ECharts.prototype.convertToPixel = function (finder, value) {\n        return doConvertPixel(this, 'convertToPixel', finder, value);\n      };\n\n      ECharts.prototype.convertFromPixel = function (finder, value) {\n        return doConvertPixel(this, 'convertFromPixel', finder, value);\n      };\n      /**\n       * Is the specified coordinate systems or components contain the given pixel point.\n       * @param {Array|number} value\n       * @return {boolean} result\n       */\n\n\n      ECharts.prototype.containPixel = function (finder, value) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var ecModel = this._model;\n        var result;\n        var findResult = parseFinder(ecModel, finder);\n        each(findResult, function (models, key) {\n          key.indexOf('Models') >= 0 && each(models, function (model) {\n            var coordSys = model.coordinateSystem;\n\n            if (coordSys && coordSys.containPoint) {\n              result = result || !!coordSys.containPoint(value);\n            } else if (key === 'seriesModels') {\n              var view = this._chartsMap[model.__viewId];\n\n              if (view && view.containPoint) {\n                result = result || view.containPoint(value, model);\n              } else {\n                if (\"development\" !== 'production') {\n                  warn(key + ': ' + (view ? 'The found component do not support containPoint.' : 'No view mapping to the found component.'));\n                }\n              }\n            } else {\n              if (\"development\" !== 'production') {\n                warn(key + ': containPoint is not supported');\n              }\n            }\n          }, this);\n        }, this);\n        return !!result;\n      };\n      /**\n       * Get visual from series or data.\n       * @param finder\n       *        If string, e.g., 'series', means {seriesIndex: 0}.\n       *        If Object, could contain some of these properties below:\n       *        {\n       *            seriesIndex / seriesId / seriesName,\n       *            dataIndex / dataIndexInside\n       *        }\n       *        If dataIndex is not specified, series visual will be fetched,\n       *        but not data item visual.\n       *        If all of seriesIndex, seriesId, seriesName are not specified,\n       *        visual will be fetched from first series.\n       * @param visualType 'color', 'symbol', 'symbolSize'\n       */\n\n\n      ECharts.prototype.getVisual = function (finder, visualType) {\n        var ecModel = this._model;\n        var parsedFinder = parseFinder(ecModel, finder, {\n          defaultMainType: 'series'\n        });\n        var seriesModel = parsedFinder.seriesModel;\n\n        if (\"development\" !== 'production') {\n          if (!seriesModel) {\n            warn('There is no specified series model');\n          }\n        }\n\n        var data = seriesModel.getData();\n        var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') ? parsedFinder.dataIndexInside : parsedFinder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(parsedFinder.dataIndex) : null;\n        return dataIndexInside != null ? getItemVisualFromData(data, dataIndexInside, visualType) : getVisualFromData(data, visualType);\n      };\n      /**\n       * Get view of corresponding component model\n       */\n\n\n      ECharts.prototype.getViewOfComponentModel = function (componentModel) {\n        return this._componentsMap[componentModel.__viewId];\n      };\n      /**\n       * Get view of corresponding series model\n       */\n\n\n      ECharts.prototype.getViewOfSeriesModel = function (seriesModel) {\n        return this._chartsMap[seriesModel.__viewId];\n      };\n\n      ECharts.prototype._initEvents = function () {\n        var _this = this;\n\n        each(MOUSE_EVENT_NAMES, function (eveName) {\n          var handler = function (e) {\n            var ecModel = _this.getModel();\n\n            var el = e.target;\n            var params;\n            var isGlobalOut = eveName === 'globalout'; // no e.target when 'globalout'.\n\n            if (isGlobalOut) {\n              params = {};\n            } else {\n              el && findEventDispatcher(el, function (parent) {\n                var ecData = getECData(parent);\n\n                if (ecData && ecData.dataIndex != null) {\n                  var dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex);\n                  params = dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType) || {};\n                  return true;\n                } // If element has custom eventData of components\n                else if (ecData.eventData) {\n                    params = extend({}, ecData.eventData);\n                    return true;\n                  }\n              }, true);\n            } // Contract: if params prepared in mouse event,\n            // these properties must be specified:\n            // {\n            //    componentType: string (component main type)\n            //    componentIndex: number\n            // }\n            // Otherwise event query can not work.\n\n\n            if (params) {\n              var componentType = params.componentType;\n              var componentIndex = params.componentIndex; // Special handling for historic reason: when trigger by\n              // markLine/markPoint/markArea, the componentType is\n              // 'markLine'/'markPoint'/'markArea', but we should better\n              // enable them to be queried by seriesIndex, since their\n              // option is set in each series.\n\n              if (componentType === 'markLine' || componentType === 'markPoint' || componentType === 'markArea') {\n                componentType = 'series';\n                componentIndex = params.seriesIndex;\n              }\n\n              var model = componentType && componentIndex != null && ecModel.getComponent(componentType, componentIndex);\n              var view = model && _this[model.mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId];\n\n              if (\"development\" !== 'production') {\n                // `event.componentType` and `event[componentTpype + 'Index']` must not\n                // be missed, otherwise there is no way to distinguish source component.\n                // See `dataFormat.getDataParams`.\n                if (!isGlobalOut && !(model && view)) {\n                  warn('model or view can not be found by params');\n                }\n              }\n\n              params.event = e;\n              params.type = eveName;\n              _this._$eventProcessor.eventInfo = {\n                targetEl: el,\n                packedEvent: params,\n                model: model,\n                view: view\n              };\n\n              _this.trigger(eveName, params);\n            }\n          }; // Consider that some component (like tooltip, brush, ...)\n          // register zr event handler, but user event handler might\n          // do anything, such as call `setOption` or `dispatchAction`,\n          // which probably update any of the content and probably\n          // cause problem if it is called previous other inner handlers.\n\n\n          handler.zrEventfulCallAtLast = true;\n\n          _this._zr.on(eveName, handler, _this);\n        });\n        each(eventActionMap, function (actionType, eventType) {\n          _this._messageCenter.on(eventType, function (event) {\n            this.trigger(eventType, event);\n          }, _this);\n        }); // Extra events\n        // TODO register?\n\n        each(['selectchanged'], function (eventType) {\n          _this._messageCenter.on(eventType, function (event) {\n            this.trigger(eventType, event);\n          }, _this);\n        });\n        handleLegacySelectEvents(this._messageCenter, this, this._api);\n      };\n\n      ECharts.prototype.isDisposed = function () {\n        return this._disposed;\n      };\n\n      ECharts.prototype.clear = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this.setOption({\n          series: []\n        }, true);\n      };\n\n      ECharts.prototype.dispose = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._disposed = true;\n        var dom = this.getDom();\n\n        if (dom) {\n          setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');\n        }\n\n        var chart = this;\n        var api = chart._api;\n        var ecModel = chart._model;\n        each(chart._componentsViews, function (component) {\n          component.dispose(ecModel, api);\n        });\n        each(chart._chartsViews, function (chart) {\n          chart.dispose(ecModel, api);\n        }); // Dispose after all views disposed\n\n        chart._zr.dispose(); // Set properties to null.\n        // To reduce the memory cost in case the top code still holds this instance unexpectedly.\n\n\n        chart._dom = chart._model = chart._chartsMap = chart._componentsMap = chart._chartsViews = chart._componentsViews = chart._scheduler = chart._api = chart._zr = chart._throttledZrFlush = chart._theme = chart._coordSysMgr = chart._messageCenter = null;\n        delete instances$1[chart.id];\n      };\n      /**\n       * Resize the chart\n       */\n\n\n      ECharts.prototype.resize = function (opts) {\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          if (\"development\" !== 'production') {\n            error('`resize` should not be called during main process.');\n          }\n\n          return;\n        }\n\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._zr.resize(opts);\n\n        var ecModel = this._model; // Resize loading effect\n\n        this._loadingFX && this._loadingFX.resize();\n\n        if (!ecModel) {\n          return;\n        }\n\n        var needPrepare = ecModel.resetOption('media');\n        var silent = opts && opts.silent; // There is some real cases that:\n        // chart.setOption(option, { lazyUpdate: true });\n        // chart.resize();\n\n        if (this[PENDING_UPDATE]) {\n          if (silent == null) {\n            silent = this[PENDING_UPDATE].silent;\n          }\n\n          needPrepare = true;\n          this[PENDING_UPDATE] = null;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = true;\n\n        try {\n          needPrepare && prepare(this);\n          updateMethods.update.call(this, {\n            type: 'resize',\n            animation: extend({\n              // Disable animation\n              duration: 0\n            }, opts && opts.animation)\n          });\n        } catch (e) {\n          this[IN_MAIN_PROCESS_KEY] = false;\n          throw e;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = false;\n        flushPendingActions.call(this, silent);\n        triggerUpdatedEvent.call(this, silent);\n      };\n\n      ECharts.prototype.showLoading = function (name, cfg) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        if (isObject(name)) {\n          cfg = name;\n          name = '';\n        }\n\n        name = name || 'default';\n        this.hideLoading();\n\n        if (!loadingEffects[name]) {\n          if (\"development\" !== 'production') {\n            warn('Loading effects ' + name + ' not exists.');\n          }\n\n          return;\n        }\n\n        var el = loadingEffects[name](this._api, cfg);\n        var zr = this._zr;\n        this._loadingFX = el;\n        zr.add(el);\n      };\n      /**\n       * Hide loading effect\n       */\n\n\n      ECharts.prototype.hideLoading = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._loadingFX && this._zr.remove(this._loadingFX);\n        this._loadingFX = null;\n      };\n\n      ECharts.prototype.makeActionFromEvent = function (eventObj) {\n        var payload = extend({}, eventObj);\n        payload.type = eventActionMap[eventObj.type];\n        return payload;\n      };\n      /**\n       * @param opt If pass boolean, means opt.silent\n       * @param opt.silent Default `false`. Whether trigger events.\n       * @param opt.flush Default `undefined`.\n       *        true: Flush immediately, and then pixel in canvas can be fetched\n       *            immediately. Caution: it might affect performance.\n       *        false: Not flush.\n       *        undefined: Auto decide whether perform flush.\n       */\n\n\n      ECharts.prototype.dispatchAction = function (payload, opt) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        if (!isObject(opt)) {\n          opt = {\n            silent: !!opt\n          };\n        }\n\n        if (!actions[payload.type]) {\n          return;\n        } // Avoid dispatch action before setOption. Especially in `connect`.\n\n\n        if (!this._model) {\n          return;\n        } // May dispatchAction in rendering procedure\n\n\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          this._pendingActions.push(payload);\n\n          return;\n        }\n\n        var silent = opt.silent;\n        doDispatchAction.call(this, payload, silent);\n        var flush = opt.flush;\n\n        if (flush) {\n          this._zr.flush();\n        } else if (flush !== false && env.browser.weChat) {\n          // In WeChat embedded browser, `requestAnimationFrame` and `setInterval`\n          // hang when sliding page (on touch event), which cause that zr does not\n          // refresh until user interaction finished, which is not expected.\n          // But `dispatchAction` may be called too frequently when pan on touch\n          // screen, which impacts performance if do not throttle them.\n          this._throttledZrFlush();\n        }\n\n        flushPendingActions.call(this, silent);\n        triggerUpdatedEvent.call(this, silent);\n      };\n\n      ECharts.prototype.updateLabelLayout = function () {\n        lifecycle.trigger('series:layoutlabels', this._model, this._api, {\n          // Not adding series labels.\n          // TODO\n          updatedSeries: []\n        });\n      };\n\n      ECharts.prototype.appendData = function (params) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var seriesIndex = params.seriesIndex;\n        var ecModel = this.getModel();\n        var seriesModel = ecModel.getSeriesByIndex(seriesIndex);\n\n        if (\"development\" !== 'production') {\n          assert(params.data && seriesModel);\n        }\n\n        seriesModel.appendData(params); // Note: `appendData` does not support that update extent of coordinate\n        // system, util some scenario require that. In the expected usage of\n        // `appendData`, the initial extent of coordinate system should better\n        // be fixed by axis `min`/`max` setting or initial data, otherwise if\n        // the extent changed while `appendData`, the location of the painted\n        // graphic elements have to be changed, which make the usage of\n        // `appendData` meaningless.\n\n        this._scheduler.unfinished = true;\n        this.getZr().wakeUp();\n      }; // A work around for no `internal` modifier in ts yet but\n      // need to strictly hide private methods to JS users.\n\n\n      ECharts.internalField = function () {\n        prepare = function (ecIns) {\n          var scheduler = ecIns._scheduler;\n          scheduler.restorePipelines(ecIns._model);\n          scheduler.prepareStageTasks();\n          prepareView(ecIns, true);\n          prepareView(ecIns, false);\n          scheduler.plan();\n        };\n        /**\n         * Prepare view instances of charts and components\n         */\n\n\n        prepareView = function (ecIns, isComponent) {\n          var ecModel = ecIns._model;\n          var scheduler = ecIns._scheduler;\n          var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;\n          var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;\n          var zr = ecIns._zr;\n          var api = ecIns._api;\n\n          for (var i = 0; i < viewList.length; i++) {\n            viewList[i].__alive = false;\n          }\n\n          isComponent ? ecModel.eachComponent(function (componentType, model) {\n            componentType !== 'series' && doPrepare(model);\n          }) : ecModel.eachSeries(doPrepare);\n\n          function doPrepare(model) {\n            // By default view will be reused if possible for the case that `setOption` with \"notMerge\"\n            // mode and need to enable transition animation. (Usually, when they have the same id, or\n            // especially no id but have the same type & name & index. See the `model.id` generation\n            // rule in `makeIdAndName` and `viewId` generation rule here).\n            // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that\n            // the new model has nothing to do with the old model.\n            var requireNewView = model.__requireNewView; // This command should not work twice.\n\n            model.__requireNewView = false; // Consider: id same and type changed.\n\n            var viewId = '_ec_' + model.id + '_' + model.type;\n            var view = !requireNewView && viewMap[viewId];\n\n            if (!view) {\n              var classType = parseClassType(model.type);\n              var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : // FIXME:TS\n              // (ChartView as ChartViewConstructor).getClass('series', classType.sub)\n              // For backward compat, still support a chart type declared as only subType\n              // like \"liquidfill\", but recommend \"series.liquidfill\"\n              // But need a base class to make a type series.\n              ChartView.getClass(classType.sub);\n\n              if (\"development\" !== 'production') {\n                assert(Clazz, classType.sub + ' does not exist.');\n              }\n\n              view = new Clazz();\n              view.init(ecModel, api);\n              viewMap[viewId] = view;\n              viewList.push(view);\n              zr.add(view.group);\n            }\n\n            model.__viewId = view.__id = viewId;\n            view.__alive = true;\n            view.__model = model;\n            view.group.__ecComponentInfo = {\n              mainType: model.mainType,\n              index: model.componentIndex\n            };\n            !isComponent && scheduler.prepareView(view, model, ecModel, api);\n          }\n\n          for (var i = 0; i < viewList.length;) {\n            var view = viewList[i];\n\n            if (!view.__alive) {\n              !isComponent && view.renderTask.dispose();\n              zr.remove(view.group);\n              view.dispose(ecModel, api);\n              viewList.splice(i, 1);\n\n              if (viewMap[view.__id] === view) {\n                delete viewMap[view.__id];\n              }\n\n              view.__id = view.group.__ecComponentInfo = null;\n            } else {\n              i++;\n            }\n          }\n        };\n\n        updateDirectly = function (ecIns, method, payload, mainType, subType) {\n          var ecModel = ecIns._model;\n          ecModel.setUpdatePayload(payload); // broadcast\n\n          if (!mainType) {\n            // FIXME\n            // Chart will not be update directly here, except set dirty.\n            // But there is no such scenario now.\n            each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView);\n            return;\n          }\n\n          var query = {};\n          query[mainType + 'Id'] = payload[mainType + 'Id'];\n          query[mainType + 'Index'] = payload[mainType + 'Index'];\n          query[mainType + 'Name'] = payload[mainType + 'Name'];\n          var condition = {\n            mainType: mainType,\n            query: query\n          };\n          subType && (condition.subType = subType); // subType may be '' by parseClassType;\n\n          var excludeSeriesId = payload.excludeSeriesId;\n          var excludeSeriesIdMap;\n\n          if (excludeSeriesId != null) {\n            excludeSeriesIdMap = createHashMap();\n            each(normalizeToArray(excludeSeriesId), function (id) {\n              var modelId = convertOptionIdName(id, null);\n\n              if (modelId != null) {\n                excludeSeriesIdMap.set(modelId, true);\n              }\n            });\n          } // If dispatchAction before setOption, do nothing.\n\n\n          ecModel && ecModel.eachComponent(condition, function (model) {\n            var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n            if (isExcluded) {\n              return;\n            }\n\n            if (isHighDownPayload(payload)) {\n              if (model instanceof SeriesModel) {\n                if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur && !model.get(['emphasis', 'disabled'])) {\n                  blurSeriesFromHighlightPayload(model, payload, ecIns._api);\n                }\n              } else {\n                var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api),\n                    focusSelf = _a.focusSelf,\n                    dispatchers = _a.dispatchers;\n\n                if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) {\n                  blurComponent(model.mainType, model.componentIndex, ecIns._api);\n                } // PENDING:\n                // Whether to put this \"enter emphasis\" code in `ComponentView`,\n                // which will be the same as `ChartView` but might be not necessary\n                // and will be far from this logic.\n\n\n                if (dispatchers) {\n                  each(dispatchers, function (dispatcher) {\n                    payload.type === HIGHLIGHT_ACTION_TYPE ? enterEmphasis(dispatcher) : leaveEmphasis(dispatcher);\n                  });\n                }\n              }\n            } else if (isSelectChangePayload(payload)) {\n              // TODO geo\n              if (model instanceof SeriesModel) {\n                toggleSelectionFromPayload(model, payload, ecIns._api);\n                updateSeriesElementSelection(model);\n                markStatusToUpdate(ecIns);\n              }\n            }\n          }, ecIns);\n          ecModel && ecModel.eachComponent(condition, function (model) {\n            var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n            if (isExcluded) {\n              return;\n            }\n            callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]);\n          }, ecIns);\n\n          function callView(view) {\n            view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload);\n          }\n        };\n\n        updateMethods = {\n          prepareAndUpdate: function (payload) {\n            prepare(this);\n            updateMethods.update.call(this, payload, {\n              // Needs to mark option changed if newOption is given.\n              // It's from MagicType.\n              // TODO If use a separate flag optionChanged in payload?\n              optionChanged: payload.newOption != null\n            });\n          },\n          update: function (payload, updateParams) {\n            var ecModel = this._model;\n            var api = this._api;\n            var zr = this._zr;\n            var coordSysMgr = this._coordSysMgr;\n            var scheduler = this._scheduler; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload);\n            scheduler.restoreData(ecModel, payload);\n            scheduler.performSeriesTasks(ecModel); // TODO\n            // Save total ecModel here for undo/redo (after restoring data and before processing data).\n            // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.\n            // Create new coordinate system each update\n            // In LineView may save the old coordinate system and use it to get the original point.\n\n            coordSysMgr.create(ecModel, api);\n            scheduler.performDataProcessorTasks(ecModel, payload); // Current stream render is not supported in data process. So we can update\n            // stream modes after data processing, where the filtered data is used to\n            // determine whether to use progressive rendering.\n\n            updateStreamModes(this, ecModel); // We update stream modes before coordinate system updated, then the modes info\n            // can be fetched when coord sys updating (consider the barGrid extent fix). But\n            // the drawback is the full coord info can not be fetched. Fortunately this full\n            // coord is not required in stream mode updater currently.\n\n            coordSysMgr.update(ecModel, api);\n            clearColorPalette(ecModel);\n            scheduler.performVisualTasks(ecModel, payload);\n            render(this, ecModel, api, payload, updateParams); // Set background\n\n            var backgroundColor = ecModel.get('backgroundColor') || 'transparent';\n            var darkMode = ecModel.get('darkMode');\n            zr.setBackgroundColor(backgroundColor); // Force set dark mode.\n\n            if (darkMode != null && darkMode !== 'auto') {\n              zr.setDarkMode(darkMode);\n            }\n\n            lifecycle.trigger('afterupdate', ecModel, api);\n          },\n          updateTransform: function (payload) {\n            var _this = this;\n\n            var ecModel = this._model;\n            var api = this._api; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload); // ChartView.markUpdateMethod(payload, 'updateTransform');\n\n            var componentDirtyList = [];\n            ecModel.eachComponent(function (componentType, componentModel) {\n              if (componentType === 'series') {\n                return;\n              }\n\n              var componentView = _this.getViewOfComponentModel(componentModel);\n\n              if (componentView && componentView.__alive) {\n                if (componentView.updateTransform) {\n                  var result = componentView.updateTransform(componentModel, ecModel, api, payload);\n                  result && result.update && componentDirtyList.push(componentView);\n                } else {\n                  componentDirtyList.push(componentView);\n                }\n              }\n            });\n            var seriesDirtyMap = createHashMap();\n            ecModel.eachSeries(function (seriesModel) {\n              var chartView = _this._chartsMap[seriesModel.__viewId];\n\n              if (chartView.updateTransform) {\n                var result = chartView.updateTransform(seriesModel, ecModel, api, payload);\n                result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);\n              } else {\n                seriesDirtyMap.set(seriesModel.uid, 1);\n              }\n            });\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n            // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              setDirty: true,\n              dirtyMap: seriesDirtyMap\n            }); // Currently, not call render of components. Geo render cost a lot.\n            // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);\n\n\n            renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap);\n            lifecycle.trigger('afterupdate', ecModel, api);\n          },\n          updateView: function (payload) {\n            var ecModel = this._model; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload);\n            ChartView.markUpdateMethod(payload, 'updateView');\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              setDirty: true\n            });\n\n            render(this, ecModel, this._api, payload, {});\n            lifecycle.trigger('afterupdate', ecModel, this._api);\n          },\n          updateVisual: function (payload) {\n            // updateMethods.update.call(this, payload);\n            var _this = this;\n\n            var ecModel = this._model; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload); // clear all visual\n\n            ecModel.eachSeries(function (seriesModel) {\n              seriesModel.getData().clearAllVisual();\n            }); // Perform visual\n\n            ChartView.markUpdateMethod(payload, 'updateVisual');\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              visualType: 'visual',\n              setDirty: true\n            });\n\n            ecModel.eachComponent(function (componentType, componentModel) {\n              if (componentType !== 'series') {\n                var componentView = _this.getViewOfComponentModel(componentModel);\n\n                componentView && componentView.__alive && componentView.updateVisual(componentModel, ecModel, _this._api, payload);\n              }\n            });\n            ecModel.eachSeries(function (seriesModel) {\n              var chartView = _this._chartsMap[seriesModel.__viewId];\n              chartView.updateVisual(seriesModel, ecModel, _this._api, payload);\n            });\n            lifecycle.trigger('afterupdate', ecModel, this._api);\n          },\n          updateLayout: function (payload) {\n            updateMethods.update.call(this, payload);\n          }\n        };\n\n        doConvertPixel = function (ecIns, methodName, finder, value) {\n          if (ecIns._disposed) {\n            disposedWarning(ecIns.id);\n            return;\n          }\n\n          var ecModel = ecIns._model;\n\n          var coordSysList = ecIns._coordSysMgr.getCoordinateSystems();\n\n          var result;\n          var parsedFinder = parseFinder(ecModel, finder);\n\n          for (var i = 0; i < coordSysList.length; i++) {\n            var coordSys = coordSysList[i];\n\n            if (coordSys[methodName] && (result = coordSys[methodName](ecModel, parsedFinder, value)) != null) {\n              return result;\n            }\n          }\n\n          if (\"development\" !== 'production') {\n            warn('No coordinate system that supports ' + methodName + ' found by the given finder.');\n          }\n        };\n\n        updateStreamModes = function (ecIns, ecModel) {\n          var chartsMap = ecIns._chartsMap;\n          var scheduler = ecIns._scheduler;\n          ecModel.eachSeries(function (seriesModel) {\n            scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);\n          });\n        };\n\n        doDispatchAction = function (payload, silent) {\n          var _this = this;\n\n          var ecModel = this.getModel();\n          var payloadType = payload.type;\n          var escapeConnect = payload.escapeConnect;\n          var actionWrap = actions[payloadType];\n          var actionInfo = actionWrap.actionInfo;\n          var cptTypeTmp = (actionInfo.update || 'update').split(':');\n          var updateMethod = cptTypeTmp.pop();\n          var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]);\n          this[IN_MAIN_PROCESS_KEY] = true;\n          var payloads = [payload];\n          var batched = false; // Batch action\n\n          if (payload.batch) {\n            batched = true;\n            payloads = map(payload.batch, function (item) {\n              item = defaults(extend({}, item), payload);\n              item.batch = null;\n              return item;\n            });\n          }\n\n          var eventObjBatch = [];\n          var eventObj;\n          var isSelectChange = isSelectChangePayload(payload);\n          var isHighDown = isHighDownPayload(payload); // Only leave blur once if there are multiple batches.\n\n          if (isHighDown) {\n            allLeaveBlur(this._api);\n          }\n\n          each(payloads, function (batchItem) {\n            // Action can specify the event by return it.\n            eventObj = actionWrap.action(batchItem, _this._model, _this._api); // Emit event outside\n\n            eventObj = eventObj || extend({}, batchItem); // Convert type to eventType\n\n            eventObj.type = actionInfo.event || eventObj.type;\n            eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.\n\n            if (isHighDown) {\n              var _a = preParseFinder(payload),\n                  queryOptionMap = _a.queryOptionMap,\n                  mainTypeSpecified = _a.mainTypeSpecified;\n\n              var componentMainType = mainTypeSpecified ? queryOptionMap.keys()[0] : 'series';\n              updateDirectly(_this, updateMethod, batchItem, componentMainType);\n              markStatusToUpdate(_this);\n            } else if (isSelectChange) {\n              // At present `dispatchAction({ type: 'select', ... })` is not supported on components.\n              // geo still use 'geoselect'.\n              updateDirectly(_this, updateMethod, batchItem, 'series');\n              markStatusToUpdate(_this);\n            } else if (cptType) {\n              updateDirectly(_this, updateMethod, batchItem, cptType.main, cptType.sub);\n            }\n          });\n\n          if (updateMethod !== 'none' && !isHighDown && !isSelectChange && !cptType) {\n            try {\n              // Still dirty\n              if (this[PENDING_UPDATE]) {\n                prepare(this);\n                updateMethods.update.call(this, payload);\n                this[PENDING_UPDATE] = null;\n              } else {\n                updateMethods[updateMethod].call(this, payload);\n              }\n            } catch (e) {\n              this[IN_MAIN_PROCESS_KEY] = false;\n              throw e;\n            }\n          } // Follow the rule of action batch\n\n\n          if (batched) {\n            eventObj = {\n              type: actionInfo.event || payloadType,\n              escapeConnect: escapeConnect,\n              batch: eventObjBatch\n            };\n          } else {\n            eventObj = eventObjBatch[0];\n          }\n\n          this[IN_MAIN_PROCESS_KEY] = false;\n\n          if (!silent) {\n            var messageCenter = this._messageCenter;\n            messageCenter.trigger(eventObj.type, eventObj); // Extra triggered 'selectchanged' event\n\n            if (isSelectChange) {\n              var newObj = {\n                type: 'selectchanged',\n                escapeConnect: escapeConnect,\n                selected: getAllSelectedIndices(ecModel),\n                isFromClick: payload.isFromClick || false,\n                fromAction: payload.type,\n                fromActionPayload: payload\n              };\n              messageCenter.trigger(newObj.type, newObj);\n            }\n          }\n        };\n\n        flushPendingActions = function (silent) {\n          var pendingActions = this._pendingActions;\n\n          while (pendingActions.length) {\n            var payload = pendingActions.shift();\n            doDispatchAction.call(this, payload, silent);\n          }\n        };\n\n        triggerUpdatedEvent = function (silent) {\n          !silent && this.trigger('updated');\n        };\n        /**\n         * Event `rendered` is triggered when zr\n         * rendered. It is useful for realtime\n         * snapshot (reflect animation).\n         *\n         * Event `finished` is triggered when:\n         * (1) zrender rendering finished.\n         * (2) initial animation finished.\n         * (3) progressive rendering finished.\n         * (4) no pending action.\n         * (5) no delayed setOption needs to be processed.\n         */\n\n\n        bindRenderedEvent = function (zr, ecIns) {\n          zr.on('rendered', function (params) {\n            ecIns.trigger('rendered', params); // The `finished` event should not be triggered repeatedly,\n            // so it should only be triggered when rendering indeed happens\n            // in zrender. (Consider the case that dipatchAction is keep\n            // triggering when mouse move).\n\n            if ( // Although zr is dirty if initial animation is not finished\n            // and this checking is called on frame, we also check\n            // animation finished for robustness.\n            zr.animation.isFinished() && !ecIns[PENDING_UPDATE] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) {\n              ecIns.trigger('finished');\n            }\n          });\n        };\n\n        bindMouseEvent = function (zr, ecIns) {\n          zr.on('mouseover', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n            if (dispatcher) {\n              handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api);\n              markStatusToUpdate(ecIns);\n            }\n          }).on('mouseout', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n            if (dispatcher) {\n              handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api);\n              markStatusToUpdate(ecIns);\n            }\n          }).on('click', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, function (target) {\n              return getECData(target).dataIndex != null;\n            }, true);\n\n            if (dispatcher) {\n              var actionType = dispatcher.selected ? 'unselect' : 'select';\n              var ecData = getECData(dispatcher);\n\n              ecIns._api.dispatchAction({\n                type: actionType,\n                dataType: ecData.dataType,\n                dataIndexInside: ecData.dataIndex,\n                seriesIndex: ecData.seriesIndex,\n                isFromClick: true\n              });\n            }\n          });\n        };\n\n        function clearColorPalette(ecModel) {\n          ecModel.clearColorPalette();\n          ecModel.eachSeries(function (seriesModel) {\n            seriesModel.clearColorPalette();\n          });\n        }\n\n        function allocateZlevels(ecModel) {\n          var componentZLevels = [];\n          var seriesZLevels = [];\n          var hasSeperateZLevel = false;\n          ecModel.eachComponent(function (componentType, componentModel) {\n            var zlevel = componentModel.get('zlevel') || 0;\n            var z = componentModel.get('z') || 0;\n            var zlevelKey = componentModel.getZLevelKey();\n            hasSeperateZLevel = hasSeperateZLevel || !!zlevelKey;\n            (componentType === 'series' ? seriesZLevels : componentZLevels).push({\n              zlevel: zlevel,\n              z: z,\n              idx: componentModel.componentIndex,\n              type: componentType,\n              key: zlevelKey\n            });\n          });\n\n          if (hasSeperateZLevel) {\n            // Series after component\n            var zLevels = componentZLevels.concat(seriesZLevels);\n            var lastSeriesZLevel_1;\n            var lastSeriesKey_1;\n            sort(zLevels, function (a, b) {\n              if (a.zlevel === b.zlevel) {\n                return a.z - b.z;\n              }\n\n              return a.zlevel - b.zlevel;\n            });\n            each(zLevels, function (item) {\n              var componentModel = ecModel.getComponent(item.type, item.idx);\n              var zlevel = item.zlevel;\n              var key = item.key;\n\n              if (lastSeriesZLevel_1 != null) {\n                zlevel = Math.max(lastSeriesZLevel_1, zlevel);\n              }\n\n              if (key) {\n                if (zlevel === lastSeriesZLevel_1 && key !== lastSeriesKey_1) {\n                  zlevel++;\n                }\n\n                lastSeriesKey_1 = key;\n              } else if (lastSeriesKey_1) {\n                if (zlevel === lastSeriesZLevel_1) {\n                  zlevel++;\n                }\n\n                lastSeriesKey_1 = '';\n              }\n\n              lastSeriesZLevel_1 = zlevel;\n              componentModel.setZLevel(zlevel);\n            });\n          }\n        }\n\n        render = function (ecIns, ecModel, api, payload, updateParams) {\n          allocateZlevels(ecModel);\n          renderComponents(ecIns, ecModel, api, payload, updateParams);\n          each(ecIns._chartsViews, function (chart) {\n            chart.__alive = false;\n          });\n          renderSeries(ecIns, ecModel, api, payload, updateParams); // Remove groups of unrendered charts\n\n          each(ecIns._chartsViews, function (chart) {\n            if (!chart.__alive) {\n              chart.remove(ecModel, api);\n            }\n          });\n        };\n\n        renderComponents = function (ecIns, ecModel, api, payload, updateParams, dirtyList) {\n          each(dirtyList || ecIns._componentsViews, function (componentView) {\n            var componentModel = componentView.__model;\n            clearStates(componentModel, componentView);\n            componentView.render(componentModel, ecModel, api, payload);\n            updateZ(componentModel, componentView);\n            updateStates(componentModel, componentView);\n          });\n        };\n        /**\n         * Render each chart and component\n         */\n\n\n        renderSeries = function (ecIns, ecModel, api, payload, updateParams, dirtyMap) {\n          // Render all charts\n          var scheduler = ecIns._scheduler;\n          updateParams = extend(updateParams || {}, {\n            updatedSeries: ecModel.getSeries()\n          }); // TODO progressive?\n\n          lifecycle.trigger('series:beforeupdate', ecModel, api, updateParams);\n          var unfinished = false;\n          ecModel.eachSeries(function (seriesModel) {\n            var chartView = ecIns._chartsMap[seriesModel.__viewId];\n            chartView.__alive = true;\n            var renderTask = chartView.renderTask;\n            scheduler.updatePayload(renderTask, payload); // TODO states on marker.\n\n            clearStates(seriesModel, chartView);\n\n            if (dirtyMap && dirtyMap.get(seriesModel.uid)) {\n              renderTask.dirty();\n            }\n\n            if (renderTask.perform(scheduler.getPerformArgs(renderTask))) {\n              unfinished = true;\n            }\n\n            chartView.group.silent = !!seriesModel.get('silent'); // Should not call markRedraw on group, because it will disable zrender\n            // incremental render (always render from the __startIndex each frame)\n            // chartView.group.markRedraw();\n\n            updateBlend(seriesModel, chartView);\n            updateSeriesElementSelection(seriesModel);\n          });\n          scheduler.unfinished = unfinished || scheduler.unfinished;\n          lifecycle.trigger('series:layoutlabels', ecModel, api, updateParams); // transition after label is layouted.\n\n          lifecycle.trigger('series:transition', ecModel, api, updateParams);\n          ecModel.eachSeries(function (seriesModel) {\n            var chartView = ecIns._chartsMap[seriesModel.__viewId]; // Update Z after labels updated. Before applying states.\n\n            updateZ(seriesModel, chartView); // NOTE: Update states after label is updated.\n            // label should be in normal status when layouting.\n\n            updateStates(seriesModel, chartView);\n          }); // If use hover layer\n\n          updateHoverLayerStatus(ecIns, ecModel);\n          lifecycle.trigger('series:afterupdate', ecModel, api, updateParams);\n        };\n\n        markStatusToUpdate = function (ecIns) {\n          ecIns[STATUS_NEEDS_UPDATE_KEY] = true; // Wake up zrender if it's sleep. Let it update states in the next frame.\n\n          ecIns.getZr().wakeUp();\n        };\n\n        applyChangedStates = function (ecIns) {\n          if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) {\n            return;\n          }\n\n          ecIns.getZr().storage.traverse(function (el) {\n            // Not applied on removed elements, it may still in fading.\n            if (isElementRemoved(el)) {\n              return;\n            }\n\n            applyElementStates(el);\n          });\n          ecIns[STATUS_NEEDS_UPDATE_KEY] = false;\n        };\n\n        function applyElementStates(el) {\n          var newStates = [];\n          var oldStates = el.currentStates; // Keep other states.\n\n          for (var i = 0; i < oldStates.length; i++) {\n            var stateName = oldStates[i];\n\n            if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) {\n              newStates.push(stateName);\n            }\n          } // Only use states when it's exists.\n\n\n          if (el.selected && el.states.select) {\n            newStates.push('select');\n          }\n\n          if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) {\n            newStates.push('emphasis');\n          } else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) {\n            newStates.push('blur');\n          }\n\n          el.useStates(newStates);\n        }\n\n        function updateHoverLayerStatus(ecIns, ecModel) {\n          var zr = ecIns._zr;\n          var storage = zr.storage;\n          var elCount = 0;\n          storage.traverse(function (el) {\n            if (!el.isGroup) {\n              elCount++;\n            }\n          });\n\n          if (elCount > ecModel.get('hoverLayerThreshold') && !env.node && !env.worker) {\n            ecModel.eachSeries(function (seriesModel) {\n              if (seriesModel.preventUsingHoverLayer) {\n                return;\n              }\n\n              var chartView = ecIns._chartsMap[seriesModel.__viewId];\n\n              if (chartView.__alive) {\n                chartView.eachRendered(function (el) {\n                  if (el.states.emphasis) {\n                    el.states.emphasis.hoverLayer = true;\n                  }\n                });\n              }\n            });\n          }\n        }\n        /**\n         * Update chart and blend.\n         */\n\n        function updateBlend(seriesModel, chartView) {\n          var blendMode = seriesModel.get('blendMode') || null;\n          chartView.eachRendered(function (el) {\n            // FIXME marker and other components\n            if (!el.isGroup) {\n              // DON'T mark the element dirty. In case element is incremental and don't want to rerender.\n              el.style.blend = blendMode;\n            }\n          });\n        }\n\n        function updateZ(model, view) {\n          if (model.preventAutoZ) {\n            return;\n          }\n\n          var z = model.get('z') || 0;\n          var zlevel = model.get('zlevel') || 0; // Set z and zlevel\n\n          view.eachRendered(function (el) {\n            doUpdateZ(el, z, zlevel, -Infinity); // Don't traverse the children because it has been traversed in _updateZ.\n\n            return true;\n          });\n        }\n\n        function doUpdateZ(el, z, zlevel, maxZ2) {\n          // Group may also have textContent\n          var label = el.getTextContent();\n          var labelLine = el.getTextGuideLine();\n          var isGroup = el.isGroup;\n\n          if (isGroup) {\n            // set z & zlevel of children elements of Group\n            var children = el.childrenRef();\n\n            for (var i = 0; i < children.length; i++) {\n              maxZ2 = Math.max(doUpdateZ(children[i], z, zlevel, maxZ2), maxZ2);\n            }\n          } else {\n            // not Group\n            el.z = z;\n            el.zlevel = zlevel;\n            maxZ2 = Math.max(el.z2, maxZ2);\n          } // always set z and zlevel if label/labelLine exists\n\n\n          if (label) {\n            label.z = z;\n            label.zlevel = zlevel; // lift z2 of text content\n            // TODO if el.emphasis.z2 is spcefied, what about textContent.\n\n            isFinite(maxZ2) && (label.z2 = maxZ2 + 2);\n          }\n\n          if (labelLine) {\n            var textGuideLineConfig = el.textGuideLineConfig;\n            labelLine.z = z;\n            labelLine.zlevel = zlevel;\n            isFinite(maxZ2) && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1));\n          }\n\n          return maxZ2;\n        } // Clear states without animation.\n        // TODO States on component.\n\n\n        function clearStates(model, view) {\n          view.eachRendered(function (el) {\n            // Not applied on removed elements, it may still in fading.\n            if (isElementRemoved(el)) {\n              return;\n            }\n\n            var textContent = el.getTextContent();\n            var textGuide = el.getTextGuideLine();\n\n            if (el.stateTransition) {\n              el.stateTransition = null;\n            }\n\n            if (textContent && textContent.stateTransition) {\n              textContent.stateTransition = null;\n            }\n\n            if (textGuide && textGuide.stateTransition) {\n              textGuide.stateTransition = null;\n            } // TODO If el is incremental.\n\n\n            if (el.hasState()) {\n              el.prevStates = el.currentStates;\n              el.clearStates();\n            } else if (el.prevStates) {\n              el.prevStates = null;\n            }\n          });\n        }\n\n        function updateStates(model, view) {\n          var stateAnimationModel = model.getModel('stateAnimation');\n          var enableAnimation = model.isAnimationEnabled();\n          var duration = stateAnimationModel.get('duration');\n          var stateTransition = duration > 0 ? {\n            duration: duration,\n            delay: stateAnimationModel.get('delay'),\n            easing: stateAnimationModel.get('easing') // additive: stateAnimationModel.get('additive')\n\n          } : null;\n          view.eachRendered(function (el) {\n            if (el.states && el.states.emphasis) {\n              // Not applied on removed elements, it may still in fading.\n              if (isElementRemoved(el)) {\n                return;\n              }\n\n              if (el instanceof Path) {\n                savePathStates(el);\n              } // Only updated on changed element. In case element is incremental and don't want to rerender.\n              // TODO, a more proper way?\n\n\n              if (el.__dirty) {\n                var prevStates = el.prevStates; // Restore states without animation\n\n                if (prevStates) {\n                  el.useStates(prevStates);\n                }\n              } // Update state transition and enable animation again.\n\n\n              if (enableAnimation) {\n                el.stateTransition = stateTransition;\n                var textContent = el.getTextContent();\n                var textGuide = el.getTextGuideLine(); // TODO Is it necessary to animate label?\n\n                if (textContent) {\n                  textContent.stateTransition = stateTransition;\n                }\n\n                if (textGuide) {\n                  textGuide.stateTransition = stateTransition;\n                }\n              } // Use highlighted and selected flag to toggle states.\n\n\n              if (el.__dirty) {\n                applyElementStates(el);\n              }\n            }\n          });\n        }\n\n        createExtensionAPI = function (ecIns) {\n          return new (\n          /** @class */\n          function (_super) {\n            __extends(class_1, _super);\n\n            function class_1() {\n              return _super !== null && _super.apply(this, arguments) || this;\n            }\n\n            class_1.prototype.getCoordinateSystems = function () {\n              return ecIns._coordSysMgr.getCoordinateSystems();\n            };\n\n            class_1.prototype.getComponentByElement = function (el) {\n              while (el) {\n                var modelInfo = el.__ecComponentInfo;\n\n                if (modelInfo != null) {\n                  return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index);\n                }\n\n                el = el.parent;\n              }\n            };\n\n            class_1.prototype.enterEmphasis = function (el, highlightDigit) {\n              enterEmphasis(el, highlightDigit);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveEmphasis = function (el, highlightDigit) {\n              leaveEmphasis(el, highlightDigit);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.enterBlur = function (el) {\n              enterBlur(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveBlur = function (el) {\n              leaveBlur(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.enterSelect = function (el) {\n              enterSelect(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveSelect = function (el) {\n              leaveSelect(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.getModel = function () {\n              return ecIns.getModel();\n            };\n\n            class_1.prototype.getViewOfComponentModel = function (componentModel) {\n              return ecIns.getViewOfComponentModel(componentModel);\n            };\n\n            class_1.prototype.getViewOfSeriesModel = function (seriesModel) {\n              return ecIns.getViewOfSeriesModel(seriesModel);\n            };\n\n            return class_1;\n          }(ExtensionAPI))(ecIns);\n        };\n\n        enableConnect = function (chart) {\n          function updateConnectedChartsStatus(charts, status) {\n            for (var i = 0; i < charts.length; i++) {\n              var otherChart = charts[i];\n              otherChart[CONNECT_STATUS_KEY] = status;\n            }\n          }\n\n          each(eventActionMap, function (actionType, eventType) {\n            chart._messageCenter.on(eventType, function (event) {\n              if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) {\n                if (event && event.escapeConnect) {\n                  return;\n                }\n\n                var action_1 = chart.makeActionFromEvent(event);\n                var otherCharts_1 = [];\n                each(instances$1, function (otherChart) {\n                  if (otherChart !== chart && otherChart.group === chart.group) {\n                    otherCharts_1.push(otherChart);\n                  }\n                });\n                updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_PENDING);\n                each(otherCharts_1, function (otherChart) {\n                  if (otherChart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_UPDATING) {\n                    otherChart.dispatchAction(action_1);\n                  }\n                });\n                updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_UPDATED);\n              }\n            });\n          });\n        };\n      }();\n\n      return ECharts;\n    }(Eventful);\n\n    var echartsProto = ECharts.prototype;\n    echartsProto.on = createRegisterEventWithLowercaseECharts('on');\n    echartsProto.off = createRegisterEventWithLowercaseECharts('off');\n    /**\n     * @deprecated\n     */\n    // @ts-ignore\n\n    echartsProto.one = function (eventName, cb, ctx) {\n      var self = this;\n      deprecateLog('ECharts#one is deprecated.');\n\n      function wrapped() {\n        var args2 = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args2[_i] = arguments[_i];\n        }\n\n        cb && cb.apply && cb.apply(this, args2); // @ts-ignore\n\n        self.off(eventName, wrapped);\n      }\n\n      this.on.call(this, eventName, wrapped, ctx);\n    };\n\n    var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];\n\n    function disposedWarning(id) {\n      if (\"development\" !== 'production') {\n        warn('Instance ' + id + ' has been disposed');\n      }\n    }\n\n    var actions = {};\n    /**\n     * Map eventType to actionType\n     */\n\n    var eventActionMap = {};\n    var dataProcessorFuncs = [];\n    var optionPreprocessorFuncs = [];\n    var visualFuncs = [];\n    var themeStorage = {};\n    var loadingEffects = {};\n    var instances$1 = {};\n    var connectedGroups = {};\n    var idBase = +new Date() - 0;\n    var groupIdBase = +new Date() - 0;\n    var DOM_ATTRIBUTE_KEY = '_echarts_instance_';\n    /**\n     * @param opts.devicePixelRatio Use window.devicePixelRatio by default\n     * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart.\n     * @param opts.width Use clientWidth of the input `dom` by default.\n     *        Can be 'auto' (the same as null/undefined)\n     * @param opts.height Use clientHeight of the input `dom` by default.\n     *        Can be 'auto' (the same as null/undefined)\n     * @param opts.locale Specify the locale.\n     * @param opts.useDirtyRect Enable dirty rectangle rendering or not.\n     */\n\n    function init$1(dom, theme, opts) {\n      var isClient = !(opts && opts.ssr);\n\n      if (isClient) {\n        if (\"development\" !== 'production') {\n          if (!dom) {\n            throw new Error('Initialize failed: invalid dom.');\n          }\n        }\n\n        var existInstance = getInstanceByDom(dom);\n\n        if (existInstance) {\n          if (\"development\" !== 'production') {\n            warn('There is a chart instance already initialized on the dom.');\n          }\n\n          return existInstance;\n        }\n\n        if (\"development\" !== 'production') {\n          if (isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth && (!opts || opts.width == null) || !dom.clientHeight && (!opts || opts.height == null))) {\n            warn('Can\\'t get DOM width or height. Please check ' + 'dom.clientWidth and dom.clientHeight. They should not be 0.' + 'For example, you may need to call this in the callback ' + 'of window.onload.');\n          }\n        }\n      }\n\n      var chart = new ECharts(dom, theme, opts);\n      chart.id = 'ec_' + idBase++;\n      instances$1[chart.id] = chart;\n      isClient && setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);\n      enableConnect(chart);\n      lifecycle.trigger('afterinit', chart);\n      return chart;\n    }\n    /**\n     * @usage\n     * (A)\n     * ```js\n     * let chart1 = echarts.init(dom1);\n     * let chart2 = echarts.init(dom2);\n     * chart1.group = 'xxx';\n     * chart2.group = 'xxx';\n     * echarts.connect('xxx');\n     * ```\n     * (B)\n     * ```js\n     * let chart1 = echarts.init(dom1);\n     * let chart2 = echarts.init(dom2);\n     * echarts.connect('xxx', [chart1, chart2]);\n     * ```\n     */\n\n    function connect(groupId) {\n      // Is array of charts\n      if (isArray(groupId)) {\n        var charts = groupId;\n        groupId = null; // If any chart has group\n\n        each(charts, function (chart) {\n          if (chart.group != null) {\n            groupId = chart.group;\n          }\n        });\n        groupId = groupId || 'g_' + groupIdBase++;\n        each(charts, function (chart) {\n          chart.group = groupId;\n        });\n      }\n\n      connectedGroups[groupId] = true;\n      return groupId;\n    }\n    /**\n     * @deprecated\n     */\n\n    function disConnect(groupId) {\n      connectedGroups[groupId] = false;\n    }\n    /**\n     * Alias and backward compatibility\n     */\n\n    var disconnect = disConnect;\n    /**\n     * Dispose a chart instance\n     */\n\n    function dispose$1(chart) {\n      if (isString(chart)) {\n        chart = instances$1[chart];\n      } else if (!(chart instanceof ECharts)) {\n        // Try to treat as dom\n        chart = getInstanceByDom(chart);\n      }\n\n      if (chart instanceof ECharts && !chart.isDisposed()) {\n        chart.dispose();\n      }\n    }\n    function getInstanceByDom(dom) {\n      return instances$1[getAttribute(dom, DOM_ATTRIBUTE_KEY)];\n    }\n    function getInstanceById(key) {\n      return instances$1[key];\n    }\n    /**\n     * Register theme\n     */\n\n    function registerTheme(name, theme) {\n      themeStorage[name] = theme;\n    }\n    /**\n     * Register option preprocessor\n     */\n\n    function registerPreprocessor(preprocessorFunc) {\n      if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) {\n        optionPreprocessorFuncs.push(preprocessorFunc);\n      }\n    }\n    function registerProcessor(priority, processor) {\n      normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT);\n    }\n    /**\n     * Register postIniter\n     * @param {Function} postInitFunc\n     */\n\n    function registerPostInit(postInitFunc) {\n      registerUpdateLifecycle('afterinit', postInitFunc);\n    }\n    /**\n     * Register postUpdater\n     * @param {Function} postUpdateFunc\n     */\n\n    function registerPostUpdate(postUpdateFunc) {\n      registerUpdateLifecycle('afterupdate', postUpdateFunc);\n    }\n    function registerUpdateLifecycle(name, cb) {\n      lifecycle.on(name, cb);\n    }\n    function registerAction(actionInfo, eventName, action) {\n      if (isFunction(eventName)) {\n        action = eventName;\n        eventName = '';\n      }\n\n      var actionType = isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = {\n        event: eventName\n      }][0]; // Event name is all lowercase\n\n      actionInfo.event = (actionInfo.event || actionType).toLowerCase();\n      eventName = actionInfo.event;\n\n      if (eventActionMap[eventName]) {\n        // Already registered.\n        return;\n      } // Validate action type and event name.\n\n\n      assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));\n\n      if (!actions[actionType]) {\n        actions[actionType] = {\n          action: action,\n          actionInfo: actionInfo\n        };\n      }\n\n      eventActionMap[eventName] = actionType;\n    }\n    function registerCoordinateSystem(type, coordSysCreator) {\n      CoordinateSystemManager.register(type, coordSysCreator);\n    }\n    /**\n     * Get dimensions of specified coordinate system.\n     * @param {string} type\n     * @return {Array.<string|Object>}\n     */\n\n    function getCoordinateSystemDimensions(type) {\n      var coordSysCreator = CoordinateSystemManager.get(type);\n\n      if (coordSysCreator) {\n        return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice();\n      }\n    }\n\n    function registerLayout(priority, layoutTask) {\n      normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');\n    }\n\n    function registerVisual(priority, visualTask) {\n      normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');\n    }\n    var registeredTasks = [];\n\n    function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {\n      if (isFunction(priority) || isObject(priority)) {\n        fn = priority;\n        priority = defaultPriority;\n      }\n\n      if (\"development\" !== 'production') {\n        if (isNaN(priority) || priority == null) {\n          throw new Error('Illegal priority');\n        } // Check duplicate\n\n\n        each(targetList, function (wrap) {\n          assert(wrap.__raw !== fn);\n        });\n      } // Already registered\n\n\n      if (indexOf(registeredTasks, fn) >= 0) {\n        return;\n      }\n\n      registeredTasks.push(fn);\n      var stageHandler = Scheduler.wrapStageHandler(fn, visualType);\n      stageHandler.__prio = priority;\n      stageHandler.__raw = fn;\n      targetList.push(stageHandler);\n    }\n\n    function registerLoading(name, loadingFx) {\n      loadingEffects[name] = loadingFx;\n    }\n    /**\n     * ZRender need a canvas context to do measureText.\n     * But in node environment canvas may be created by node-canvas.\n     * So we need to specify how to create a canvas instead of using document.createElement('canvas')\n     *\n     *\n     * @deprecated use setPlatformAPI({ createCanvas }) instead.\n     *\n     * @example\n     *     let Canvas = require('canvas');\n     *     let echarts = require('echarts');\n     *     echarts.setCanvasCreator(function () {\n     *         // Small size is enough.\n     *         return new Canvas(32, 32);\n     *     });\n     */\n\n    function setCanvasCreator(creator) {\n      if (\"development\" !== 'production') {\n        deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.');\n      }\n\n      setPlatformAPI({\n        createCanvas: creator\n      });\n    }\n    /**\n     * The parameters and usage: see `geoSourceManager.registerMap`.\n     * Compatible with previous `echarts.registerMap`.\n     */\n\n    function registerMap(mapName, geoJson, specialAreas) {\n      var registerMap = getImpl('registerMap');\n      registerMap && registerMap(mapName, geoJson, specialAreas);\n    }\n    function getMap(mapName) {\n      var getMap = getImpl('getMap');\n      return getMap && getMap(mapName);\n    }\n    var registerTransform = registerExternalTransform;\n    /**\n     * Globa dispatchAction to a specified chart instance.\n     */\n    // export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters<ECharts['dispatchAction']>[1]) {\n    //     if (!payload || !payload.chartId) {\n    //         // Must have chartId to find chart\n    //         return;\n    //     }\n    //     const chart = instances[payload.chartId];\n    //     if (chart) {\n    //         chart.dispatchAction(payload, opt);\n    //     }\n    // }\n    // Builtin global visual\n\n    registerVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataColorPaletteTask);\n    registerVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask);\n    registerVisual(PRIORITY_VISUAL_DECAL, decalVisual);\n    registerPreprocessor(globalBackwardCompat);\n    registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack);\n    registerLoading('default', defaultLoading); // Default actions\n\n    registerAction({\n      type: HIGHLIGHT_ACTION_TYPE,\n      event: HIGHLIGHT_ACTION_TYPE,\n      update: HIGHLIGHT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: DOWNPLAY_ACTION_TYPE,\n      event: DOWNPLAY_ACTION_TYPE,\n      update: DOWNPLAY_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: SELECT_ACTION_TYPE,\n      event: SELECT_ACTION_TYPE,\n      update: SELECT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: UNSELECT_ACTION_TYPE,\n      event: UNSELECT_ACTION_TYPE,\n      update: UNSELECT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: TOGGLE_SELECT_ACTION_TYPE,\n      event: TOGGLE_SELECT_ACTION_TYPE,\n      update: TOGGLE_SELECT_ACTION_TYPE\n    }, noop); // Default theme\n\n    registerTheme('light', lightTheme);\n    registerTheme('dark', theme); // For backward compatibility, where the namespace `dataTool` will\n    // be mounted on `echarts` is the extension `dataTool` is imported.\n\n    var dataTool = {};\n\n    var extensions = [];\n    var extensionRegisters = {\n      registerPreprocessor: registerPreprocessor,\n      registerProcessor: registerProcessor,\n      registerPostInit: registerPostInit,\n      registerPostUpdate: registerPostUpdate,\n      registerUpdateLifecycle: registerUpdateLifecycle,\n      registerAction: registerAction,\n      registerCoordinateSystem: registerCoordinateSystem,\n      registerLayout: registerLayout,\n      registerVisual: registerVisual,\n      registerTransform: registerTransform,\n      registerLoading: registerLoading,\n      registerMap: registerMap,\n      registerImpl: registerImpl,\n      PRIORITY: PRIORITY,\n      ComponentModel: ComponentModel,\n      ComponentView: ComponentView,\n      SeriesModel: SeriesModel,\n      ChartView: ChartView,\n      // TODO Use ComponentModel and SeriesModel instead of Constructor\n      registerComponentModel: function (ComponentModelClass) {\n        ComponentModel.registerClass(ComponentModelClass);\n      },\n      registerComponentView: function (ComponentViewClass) {\n        ComponentView.registerClass(ComponentViewClass);\n      },\n      registerSeriesModel: function (SeriesModelClass) {\n        SeriesModel.registerClass(SeriesModelClass);\n      },\n      registerChartView: function (ChartViewClass) {\n        ChartView.registerClass(ChartViewClass);\n      },\n      registerSubTypeDefaulter: function (componentType, defaulter) {\n        ComponentModel.registerSubTypeDefaulter(componentType, defaulter);\n      },\n      registerPainter: function (painterType, PainterCtor) {\n        registerPainter(painterType, PainterCtor);\n      }\n    };\n    function use(ext) {\n      if (isArray(ext)) {\n        // use([ChartLine, ChartBar]);\n        each(ext, function (singleExt) {\n          use(singleExt);\n        });\n        return;\n      }\n\n      if (indexOf(extensions, ext) >= 0) {\n        return;\n      }\n\n      extensions.push(ext);\n\n      if (isFunction(ext)) {\n        ext = {\n          install: ext\n        };\n      }\n\n      ext.install(extensionRegisters);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) {\n      return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1;\n    }\n\n    function defaultKeyGetter(item) {\n      return item;\n    }\n\n    var DataDiffer =\n    /** @class */\n    function () {\n      /**\n       * @param context Can be visited by this.context in callback.\n       */\n      function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'.\n      diffMode) {\n        this._old = oldArr;\n        this._new = newArr;\n        this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;\n        this._newKeyGetter = newKeyGetter || defaultKeyGetter; // Visible in callback via `this.context`;\n\n        this.context = context;\n        this._diffModeMultiple = diffMode === 'multiple';\n      }\n      /**\n       * Callback function when add a data\n       */\n\n\n      DataDiffer.prototype.add = function (func) {\n        this._add = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data\n       */\n\n\n      DataDiffer.prototype.update = function (func) {\n        this._update = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateManyToOne = function (func) {\n        this._updateManyToOne = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateOneToMany = function (func) {\n        this._updateOneToMany = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateManyToMany = function (func) {\n        this._updateManyToMany = func;\n        return this;\n      };\n      /**\n       * Callback function when remove a data\n       */\n\n\n      DataDiffer.prototype.remove = function (func) {\n        this._remove = func;\n        return this;\n      };\n\n      DataDiffer.prototype.execute = function () {\n        this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne']();\n      };\n\n      DataDiffer.prototype._executeOneToOne = function () {\n        var oldArr = this._old;\n        var newArr = this._new;\n        var newDataIndexMap = {};\n        var oldDataKeyArr = new Array(oldArr.length);\n        var newDataKeyArr = new Array(newArr.length);\n\n        this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter');\n\n        this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n        for (var i = 0; i < oldArr.length; i++) {\n          var oldKey = oldDataKeyArr[i];\n          var newIdxMapVal = newDataIndexMap[oldKey];\n          var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); // idx can never be empty array here. see 'set null' logic below.\n\n          if (newIdxMapValLen > 1) {\n            // Consider there is duplicate key (for example, use dataItem.name as key).\n            // We should make sure every item in newArr and oldArr can be visited.\n            var newIdx = newIdxMapVal.shift();\n\n            if (newIdxMapVal.length === 1) {\n              newDataIndexMap[oldKey] = newIdxMapVal[0];\n            }\n\n            this._update && this._update(newIdx, i);\n          } else if (newIdxMapValLen === 1) {\n            newDataIndexMap[oldKey] = null;\n            this._update && this._update(newIdxMapVal, i);\n          } else {\n            this._remove && this._remove(i);\n          }\n        }\n\n        this._performRestAdd(newDataKeyArr, newDataIndexMap);\n      };\n      /**\n       * For example, consider the case:\n       * oldData: [o0, o1, o2, o3, o4, o5, o6, o7],\n       * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],\n       * Where:\n       *     o0, o1, n0 has key 'a' (many to one)\n       *     o5, n4, n5, n6 has key 'b' (one to many)\n       *     o2, n1 has key 'c' (one to one)\n       *     n2, n3 has key 'd' (add)\n       *     o3, o4 has key 'e' (remove)\n       *     o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)\n       * Then:\n       *     (The order of the following directives are not ensured.)\n       *     this._updateManyToOne(n0, [o0, o1]);\n       *     this._updateOneToMany([n4, n5, n6], o5);\n       *     this._update(n1, o2);\n       *     this._remove(o3);\n       *     this._remove(o4);\n       *     this._remove(o6);\n       *     this._remove(o7);\n       *     this._add(n2);\n       *     this._add(n3);\n       *     this._add(n7);\n       *     this._add(n8);\n       */\n\n\n      DataDiffer.prototype._executeMultiple = function () {\n        var oldArr = this._old;\n        var newArr = this._new;\n        var oldDataIndexMap = {};\n        var newDataIndexMap = {};\n        var oldDataKeyArr = [];\n        var newDataKeyArr = [];\n\n        this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');\n\n        this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n        for (var i = 0; i < oldDataKeyArr.length; i++) {\n          var oldKey = oldDataKeyArr[i];\n          var oldIdxMapVal = oldDataIndexMap[oldKey];\n          var newIdxMapVal = newDataIndexMap[oldKey];\n          var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal);\n          var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n          if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {\n            this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {\n            this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {\n            this._update && this._update(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {\n            this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen > 1) {\n            for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {\n              this._remove && this._remove(oldIdxMapVal[i_1]);\n            }\n          } else {\n            this._remove && this._remove(oldIdxMapVal);\n          }\n        }\n\n        this._performRestAdd(newDataKeyArr, newDataIndexMap);\n      };\n\n      DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) {\n        for (var i = 0; i < newDataKeyArr.length; i++) {\n          var newKey = newDataKeyArr[i];\n          var newIdxMapVal = newDataIndexMap[newKey];\n          var idxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n          if (idxMapValLen > 1) {\n            for (var j = 0; j < idxMapValLen; j++) {\n              this._add && this._add(newIdxMapVal[j]);\n            }\n          } else if (idxMapValLen === 1) {\n            this._add && this._add(newIdxMapVal);\n          } // Support both `newDataKeyArr` are duplication removed or not removed.\n\n\n          newDataIndexMap[newKey] = null;\n        }\n      };\n\n      DataDiffer.prototype._initIndexMap = function (arr, // Can be null.\n      map, // In 'byKey', the output `keyArr` is duplication removed.\n      // In 'byIndex', the output `keyArr` is not duplication removed and\n      //     its indices are accurately corresponding to `arr`.\n      keyArr, keyGetterName) {\n        var cbModeMultiple = this._diffModeMultiple;\n\n        for (var i = 0; i < arr.length; i++) {\n          // Add prefix to avoid conflict with Object.prototype.\n          var key = '_ec_' + this[keyGetterName](arr[i], i);\n\n          if (!cbModeMultiple) {\n            keyArr[i] = key;\n          }\n\n          if (!map) {\n            continue;\n          }\n\n          var idxMapVal = map[key];\n          var idxMapValLen = dataIndexMapValueLength(idxMapVal);\n\n          if (idxMapValLen === 0) {\n            // Simple optimize: in most cases, one index has one key,\n            // do not need array.\n            map[key] = i;\n\n            if (cbModeMultiple) {\n              keyArr.push(key);\n            }\n          } else if (idxMapValLen === 1) {\n            map[key] = [idxMapVal, i];\n          } else {\n            idxMapVal.push(i);\n          }\n        }\n      };\n\n      return DataDiffer;\n    }();\n\n    var DimensionUserOuput =\n    /** @class */\n    function () {\n      function DimensionUserOuput(encode, dimRequest) {\n        this._encode = encode;\n        this._schema = dimRequest;\n      }\n\n      DimensionUserOuput.prototype.get = function () {\n        return {\n          // Do not generate full dimension name until fist used.\n          fullDimensions: this._getFullDimensionNames(),\n          encode: this._encode\n        };\n      };\n      /**\n       * Get all data store dimension names.\n       * Theoretically a series data store is defined both by series and used dataset (if any).\n       * If some dimensions are omitted for performance reason in `this.dimensions`,\n       * the dimension name may not be auto-generated if user does not specify a dimension name.\n       * In this case, the dimension name is `null`/`undefined`.\n       */\n\n\n      DimensionUserOuput.prototype._getFullDimensionNames = function () {\n        if (!this._cachedDimNames) {\n          this._cachedDimNames = this._schema ? this._schema.makeOutputDimensionNames() : [];\n        }\n\n        return this._cachedDimNames;\n      };\n\n      return DimensionUserOuput;\n    }();\n    function summarizeDimensions(data, schema) {\n      var summary = {};\n      var encode = summary.encode = {};\n      var notExtraCoordDimMap = createHashMap();\n      var defaultedLabel = [];\n      var defaultedTooltip = [];\n      var userOutputEncode = {};\n      each(data.dimensions, function (dimName) {\n        var dimItem = data.getDimensionInfo(dimName);\n        var coordDim = dimItem.coordDim;\n\n        if (coordDim) {\n          if (\"development\" !== 'production') {\n            assert(VISUAL_DIMENSIONS.get(coordDim) == null);\n          }\n\n          var coordDimIndex = dimItem.coordDimIndex;\n          getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName;\n\n          if (!dimItem.isExtraCoord) {\n            notExtraCoordDimMap.set(coordDim, 1); // Use the last coord dim (and label friendly) as default label,\n            // because when dataset is used, it is hard to guess which dimension\n            // can be value dimension. If both show x, y on label is not look good,\n            // and conventionally y axis is focused more.\n\n            if (mayLabelDimType(dimItem.type)) {\n              defaultedLabel[0] = dimName;\n            } // User output encode do not contain generated coords.\n            // And it only has index. User can use index to retrieve value from the raw item array.\n\n\n            getOrCreateEncodeArr(userOutputEncode, coordDim)[coordDimIndex] = data.getDimensionIndex(dimItem.name);\n          }\n\n          if (dimItem.defaultTooltip) {\n            defaultedTooltip.push(dimName);\n          }\n        }\n\n        VISUAL_DIMENSIONS.each(function (v, otherDim) {\n          var encodeArr = getOrCreateEncodeArr(encode, otherDim);\n          var dimIndex = dimItem.otherDims[otherDim];\n\n          if (dimIndex != null && dimIndex !== false) {\n            encodeArr[dimIndex] = dimItem.name;\n          }\n        });\n      });\n      var dataDimsOnCoord = [];\n      var encodeFirstDimNotExtra = {};\n      notExtraCoordDimMap.each(function (v, coordDim) {\n        var dimArr = encode[coordDim];\n        encodeFirstDimNotExtra[coordDim] = dimArr[0]; // Not necessary to remove duplicate, because a data\n        // dim canot on more than one coordDim.\n\n        dataDimsOnCoord = dataDimsOnCoord.concat(dimArr);\n      });\n      summary.dataDimsOnCoord = dataDimsOnCoord;\n      summary.dataDimIndicesOnCoord = map(dataDimsOnCoord, function (dimName) {\n        return data.getDimensionInfo(dimName).storeDimIndex;\n      });\n      summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra;\n      var encodeLabel = encode.label; // FIXME `encode.label` is not recommended, because formatter cannot be set\n      // in this way. Use label.formatter instead. Maybe remove this approach someday.\n\n      if (encodeLabel && encodeLabel.length) {\n        defaultedLabel = encodeLabel.slice();\n      }\n\n      var encodeTooltip = encode.tooltip;\n\n      if (encodeTooltip && encodeTooltip.length) {\n        defaultedTooltip = encodeTooltip.slice();\n      } else if (!defaultedTooltip.length) {\n        defaultedTooltip = defaultedLabel.slice();\n      }\n\n      encode.defaultedLabel = defaultedLabel;\n      encode.defaultedTooltip = defaultedTooltip;\n      summary.userOutput = new DimensionUserOuput(userOutputEncode, schema);\n      return summary;\n    }\n\n    function getOrCreateEncodeArr(encode, dim) {\n      if (!encode.hasOwnProperty(dim)) {\n        encode[dim] = [];\n      }\n\n      return encode[dim];\n    } // FIXME:TS should be type `AxisType`\n\n\n    function getDimensionTypeByAxis(axisType) {\n      return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float';\n    }\n\n    function mayLabelDimType(dimType) {\n      // In most cases, ordinal and time do not suitable for label.\n      // Ordinal info can be displayed on axis. Time is too long.\n      return !(dimType === 'ordinal' || dimType === 'time');\n    } // function findTheLastDimMayLabel(data) {\n    //     // Get last value dim\n    //     let dimensions = data.dimensions.slice();\n    //     let valueType;\n    //     let valueDim;\n    //     while (dimensions.length && (\n    //         valueDim = dimensions.pop(),\n    //         valueType = data.getDimensionInfo(valueDim).type,\n    //         valueType === 'ordinal' || valueType === 'time'\n    //     )) {} // jshint ignore:line\n    //     return valueDim;\n    // }\n\n    var SeriesDimensionDefine =\n    /** @class */\n    function () {\n      /**\n       * @param opt All of the fields will be shallow copied.\n       */\n      function SeriesDimensionDefine(opt) {\n        /**\n         * The format of `otherDims` is:\n         * ```js\n         * {\n         *     tooltip?: number\n         *     label?: number\n         *     itemName?: number\n         *     seriesName?: number\n         * }\n         * ```\n         *\n         * A `series.encode` can specified these fields:\n         * ```js\n         * encode: {\n         *     // \"3, 1, 5\" is the index of data dimension.\n         *     tooltip: [3, 1, 5],\n         *     label: [0, 3],\n         *     ...\n         * }\n         * ```\n         * `otherDims` is the parse result of the `series.encode` above, like:\n         * ```js\n         * // Suppose the index of this data dimension is `3`.\n         * this.otherDims = {\n         *     // `3` is at the index `0` of the `encode.tooltip`\n         *     tooltip: 0,\n         *     // `3` is at the index `1` of the `encode.label`\n         *     label: 1\n         * };\n         * ```\n         *\n         * This prop should never be `null`/`undefined` after initialized.\n         */\n        this.otherDims = {};\n\n        if (opt != null) {\n          extend(this, opt);\n        }\n      }\n\n      return SeriesDimensionDefine;\n    }();\n\n    var inner$4 = makeInner();\n    var dimTypeShort = {\n      float: 'f',\n      int: 'i',\n      ordinal: 'o',\n      number: 'n',\n      time: 't'\n    };\n    /**\n     * Represents the dimension requirement of a series.\n     *\n     * NOTICE:\n     * When there are too many dimensions in dataset and many series, only the used dimensions\n     * (i.e., used by coord sys and declared in `series.encode`) are add to `dimensionDefineList`.\n     * But users may query data by other unused dimension names.\n     * In this case, users can only query data if and only if they have defined dimension names\n     * via ec option, so we provide `getDimensionIndexFromSource`, which only query them from\n     * `source` dimensions.\n     */\n\n    var SeriesDataSchema =\n    /** @class */\n    function () {\n      function SeriesDataSchema(opt) {\n        this.dimensions = opt.dimensions;\n        this._dimOmitted = opt.dimensionOmitted;\n        this.source = opt.source;\n        this._fullDimCount = opt.fullDimensionCount;\n\n        this._updateDimOmitted(opt.dimensionOmitted);\n      }\n\n      SeriesDataSchema.prototype.isDimensionOmitted = function () {\n        return this._dimOmitted;\n      };\n\n      SeriesDataSchema.prototype._updateDimOmitted = function (dimensionOmitted) {\n        this._dimOmitted = dimensionOmitted;\n\n        if (!dimensionOmitted) {\n          return;\n        }\n\n        if (!this._dimNameMap) {\n          this._dimNameMap = ensureSourceDimNameMap(this.source);\n        }\n      };\n      /**\n       * @caution Can only be used when `dimensionOmitted: true`.\n       *\n       * Get index by user defined dimension name (i.e., not internal generate name).\n       * That is, get index from `dimensionsDefine`.\n       * If no `dimensionsDefine`, or no name get, return -1.\n       */\n\n\n      SeriesDataSchema.prototype.getSourceDimensionIndex = function (dimName) {\n        return retrieve2(this._dimNameMap.get(dimName), -1);\n      };\n      /**\n       * @caution Can only be used when `dimensionOmitted: true`.\n       *\n       * Notice: may return `null`/`undefined` if user not specify dimension names.\n       */\n\n\n      SeriesDataSchema.prototype.getSourceDimension = function (dimIndex) {\n        var dimensionsDefine = this.source.dimensionsDefine;\n\n        if (dimensionsDefine) {\n          return dimensionsDefine[dimIndex];\n        }\n      };\n\n      SeriesDataSchema.prototype.makeStoreSchema = function () {\n        var dimCount = this._fullDimCount;\n        var willRetrieveDataByName = shouldRetrieveDataByName(this.source);\n        var makeHashStrict = !shouldOmitUnusedDimensions(dimCount); // If source don't have dimensions or series don't omit unsed dimensions.\n        // Generate from seriesDimList directly\n\n        var dimHash = '';\n        var dims = [];\n\n        for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < dimCount; fullDimIdx++) {\n          var property = void 0;\n          var type = void 0;\n          var ordinalMeta = void 0;\n          var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n          if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n            property = willRetrieveDataByName ? seriesDimDef.name : null;\n            type = seriesDimDef.type;\n            ordinalMeta = seriesDimDef.ordinalMeta;\n            seriesDimIdx++;\n          } else {\n            var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n            if (sourceDimDef) {\n              property = willRetrieveDataByName ? sourceDimDef.name : null;\n              type = sourceDimDef.type;\n            }\n          }\n\n          dims.push({\n            property: property,\n            type: type,\n            ordinalMeta: ordinalMeta\n          }); // If retrieving data by index,\n          //   use <index, type, ordinalMeta> to determine whether data can be shared.\n          //   (Because in this case there might be no dimension name defined in dataset, but indices always exists).\n          //   (Indices are always 0, 1, 2, ..., so we can ignore them to shorten the hash).\n          // Otherwise if retrieving data by property name (like `data: [{aa: 123, bb: 765}, ...]`),\n          //   use <property, type, ordinalMeta> in hash.\n\n          if (willRetrieveDataByName && property != null // For data stack, we have make sure each series has its own dim on this store.\n          // So we do not add property to hash to make sure they can share this store.\n          && (!seriesDimDef || !seriesDimDef.isCalculationCoord)) {\n            dimHash += makeHashStrict // Use escape character '`' in case that property name contains '$'.\n            ? property.replace(/\\`/g, '`1').replace(/\\$/g, '`2') // For better performance, when there are large dimensions, tolerant this defects that hardly meet.\n            : property;\n          }\n\n          dimHash += '$';\n          dimHash += dimTypeShort[type] || 'f';\n\n          if (ordinalMeta) {\n            dimHash += ordinalMeta.uid;\n          }\n\n          dimHash += '$';\n        } // Source from endpoint(usually series) will be read differently\n        // when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different.\n        // So we use this three props as key.\n\n\n        var source = this.source;\n        var hash = [source.seriesLayoutBy, source.startIndex, dimHash].join('$$');\n        return {\n          dimensions: dims,\n          hash: hash\n        };\n      };\n\n      SeriesDataSchema.prototype.makeOutputDimensionNames = function () {\n        var result = [];\n\n        for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < this._fullDimCount; fullDimIdx++) {\n          var name_1 = void 0;\n          var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n          if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n            if (!seriesDimDef.isCalculationCoord) {\n              name_1 = seriesDimDef.name;\n            }\n\n            seriesDimIdx++;\n          } else {\n            var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n            if (sourceDimDef) {\n              name_1 = sourceDimDef.name;\n            }\n          }\n\n          result.push(name_1);\n        }\n\n        return result;\n      };\n\n      SeriesDataSchema.prototype.appendCalculationDimension = function (dimDef) {\n        this.dimensions.push(dimDef);\n        dimDef.isCalculationCoord = true;\n        this._fullDimCount++; // If append dimension on a data store, consider the store\n        // might be shared by different series, series dimensions not\n        // really map to store dimensions.\n\n        this._updateDimOmitted(true);\n      };\n\n      return SeriesDataSchema;\n    }();\n    function isSeriesDataSchema(schema) {\n      return schema instanceof SeriesDataSchema;\n    }\n    function createDimNameMap(dimsDef) {\n      var dataDimNameMap = createHashMap();\n\n      for (var i = 0; i < (dimsDef || []).length; i++) {\n        var dimDefItemRaw = dimsDef[i];\n        var userDimName = isObject(dimDefItemRaw) ? dimDefItemRaw.name : dimDefItemRaw;\n\n        if (userDimName != null && dataDimNameMap.get(userDimName) == null) {\n          dataDimNameMap.set(userDimName, i);\n        }\n      }\n\n      return dataDimNameMap;\n    }\n    function ensureSourceDimNameMap(source) {\n      var innerSource = inner$4(source);\n      return innerSource.dimNameMap || (innerSource.dimNameMap = createDimNameMap(source.dimensionsDefine));\n    }\n    function shouldOmitUnusedDimensions(dimCount) {\n      return dimCount > 30;\n    }\n\n    var isObject$2 = isObject;\n    var map$1 = map;\n    var CtorInt32Array$1 = typeof Int32Array === 'undefined' ? Array : Int32Array; // Use prefix to avoid index to be the same as otherIdList[idx],\n    // which will cause weird update animation.\n\n    var ID_PREFIX = 'e\\0\\0';\n    var INDEX_NOT_FOUND = -1; // type SeriesDimensionIndex = DimensionIndex;\n\n    var TRANSFERABLE_PROPERTIES = ['hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', '_dimSummary', 'userOutput', '_rawData', '_dimValueGetter', '_nameDimIdx', '_idDimIdx', '_nameRepeatCount'];\n    var CLONE_PROPERTIES = ['_approximateExtent']; // -----------------------------\n    // Internal method declarations:\n    // -----------------------------\n\n    var prepareInvertedIndex;\n    var getId;\n    var getIdNameFromStore;\n    var normalizeDimensions;\n    var transferProperties;\n    var cloneListForMapAndSample;\n    var makeIdFromName;\n\n    var SeriesData =\n    /** @class */\n    function () {\n      /**\n       * @param dimensionsInput.dimensions\n       *        For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].\n       *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius\n       */\n      function SeriesData(dimensionsInput, hostModel) {\n        this.type = 'list';\n        this._dimOmitted = false;\n        this._nameList = [];\n        this._idList = []; // Models of data option is stored sparse for optimizing memory cost\n        // Never used yet (not used yet).\n        // private _optionModels: Model[] = [];\n        // Global visual properties after visual coding\n\n        this._visual = {}; // Global layout properties.\n\n        this._layout = {}; // Item visual properties after visual coding\n\n        this._itemVisuals = []; // Item layout properties after layout\n\n        this._itemLayouts = []; // Graphic elements\n\n        this._graphicEls = []; // key: dim, value: extent\n\n        this._approximateExtent = {};\n        this._calculationInfo = {}; // Having detected that there is data item is non primitive type\n        // (in type `OptionDataItemObject`).\n        // Like `data: [ { value: xx, itemStyle: {...} }, ...]`\n        // At present it only happen in `SOURCE_FORMAT_ORIGINAL`.\n\n        this.hasItemOption = false; // Methods that create a new list based on this list should be listed here.\n        // Notice that those method should `RETURN` the new list.\n\n        this.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map']; // Methods that change indices of this list should be listed here.\n\n        this.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];\n        this.DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'];\n        var dimensions;\n        var assignStoreDimIdx = false;\n\n        if (isSeriesDataSchema(dimensionsInput)) {\n          dimensions = dimensionsInput.dimensions;\n          this._dimOmitted = dimensionsInput.isDimensionOmitted();\n          this._schema = dimensionsInput;\n        } else {\n          assignStoreDimIdx = true;\n          dimensions = dimensionsInput;\n        }\n\n        dimensions = dimensions || ['x', 'y'];\n        var dimensionInfos = {};\n        var dimensionNames = [];\n        var invertedIndicesMap = {};\n        var needsHasOwn = false;\n        var emptyObj = {};\n\n        for (var i = 0; i < dimensions.length; i++) {\n          // Use the original dimensions[i], where other flag props may exists.\n          var dimInfoInput = dimensions[i];\n          var dimensionInfo = isString(dimInfoInput) ? new SeriesDimensionDefine({\n            name: dimInfoInput\n          }) : !(dimInfoInput instanceof SeriesDimensionDefine) ? new SeriesDimensionDefine(dimInfoInput) : dimInfoInput;\n          var dimensionName = dimensionInfo.name;\n          dimensionInfo.type = dimensionInfo.type || 'float';\n\n          if (!dimensionInfo.coordDim) {\n            dimensionInfo.coordDim = dimensionName;\n            dimensionInfo.coordDimIndex = 0;\n          }\n\n          var otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {};\n          dimensionNames.push(dimensionName);\n          dimensionInfos[dimensionName] = dimensionInfo;\n\n          if (emptyObj[dimensionName] != null) {\n            needsHasOwn = true;\n          }\n\n          if (dimensionInfo.createInvertedIndices) {\n            invertedIndicesMap[dimensionName] = [];\n          }\n\n          if (otherDims.itemName === 0) {\n            this._nameDimIdx = i;\n          }\n\n          if (otherDims.itemId === 0) {\n            this._idDimIdx = i;\n          }\n\n          if (\"development\" !== 'production') {\n            assert(assignStoreDimIdx || dimensionInfo.storeDimIndex >= 0);\n          }\n\n          if (assignStoreDimIdx) {\n            dimensionInfo.storeDimIndex = i;\n          }\n        }\n\n        this.dimensions = dimensionNames;\n        this._dimInfos = dimensionInfos;\n\n        this._initGetDimensionInfo(needsHasOwn);\n\n        this.hostModel = hostModel;\n        this._invertedIndicesMap = invertedIndicesMap;\n\n        if (this._dimOmitted) {\n          var dimIdxToName_1 = this._dimIdxToName = createHashMap();\n          each(dimensionNames, function (dimName) {\n            dimIdxToName_1.set(dimensionInfos[dimName].storeDimIndex, dimName);\n          });\n        }\n      }\n      /**\n       *\n       * Get concrete dimension name by dimension name or dimension index.\n       * If input a dimension name, do not validate whether the dimension name exits.\n       *\n       * @caution\n       * @param dim Must make sure the dimension is `SeriesDimensionLoose`.\n       * Because only those dimensions will have auto-generated dimension names if not\n       * have a user-specified name, and other dimensions will get a return of null/undefined.\n       *\n       * @notice Because of this reason, should better use `getDimensionIndex` instead, for examples:\n       * ```js\n       * const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx);\n       * ```\n       *\n       * @return Concrete dim name.\n       */\n\n\n      SeriesData.prototype.getDimension = function (dim) {\n        var dimIdx = this._recognizeDimIndex(dim);\n\n        if (dimIdx == null) {\n          return dim;\n        }\n\n        dimIdx = dim;\n\n        if (!this._dimOmitted) {\n          return this.dimensions[dimIdx];\n        } // Retrieve from series dimension definition because it probably contains\n        // generated dimension name (like 'x', 'y').\n\n\n        var dimName = this._dimIdxToName.get(dimIdx);\n\n        if (dimName != null) {\n          return dimName;\n        }\n\n        var sourceDimDef = this._schema.getSourceDimension(dimIdx);\n\n        if (sourceDimDef) {\n          return sourceDimDef.name;\n        }\n      };\n      /**\n       * Get dimension index in data store. Return -1 if not found.\n       * Can be used to index value from getRawValue.\n       */\n\n\n      SeriesData.prototype.getDimensionIndex = function (dim) {\n        var dimIdx = this._recognizeDimIndex(dim);\n\n        if (dimIdx != null) {\n          return dimIdx;\n        }\n\n        if (dim == null) {\n          return -1;\n        }\n\n        var dimInfo = this._getDimInfo(dim);\n\n        return dimInfo ? dimInfo.storeDimIndex : this._dimOmitted ? this._schema.getSourceDimensionIndex(dim) : -1;\n      };\n      /**\n       * The meanings of the input parameter `dim`:\n       *\n       * + If dim is a number (e.g., `1`), it means the index of the dimension.\n       *   For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.\n       * + If dim is a number-like string (e.g., `\"1\"`):\n       *     + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`,\n       *        it means that concrete name.\n       *     + If not, it will be converted to a number, which means the index of the dimension.\n       *        (why? because of the backward compatibility. We have been tolerating number-like string in\n       *        dimension setting, although now it seems that it is not a good idea.)\n       *     For example, `visualMap[i].dimension: \"1\"` is the same meaning as `visualMap[i].dimension: 1`,\n       *     if no dimension name is defined as `\"1\"`.\n       * + If dim is a not-number-like string, it means the concrete dim name.\n       *   For example, it can be be default name `\"x\"`, `\"y\"`, `\"z\"`, `\"lng\"`, `\"lat\"`, `\"angle\"`, `\"radius\"`,\n       *   or customized in `dimensions` property of option like `\"age\"`.\n       *\n       * @return recognized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`).\n       */\n\n\n      SeriesData.prototype._recognizeDimIndex = function (dim) {\n        if (isNumber(dim) // If being a number-like string but not being defined as a dimension name.\n        || dim != null && !isNaN(dim) && !this._getDimInfo(dim) && (!this._dimOmitted || this._schema.getSourceDimensionIndex(dim) < 0)) {\n          return +dim;\n        }\n      };\n\n      SeriesData.prototype._getStoreDimIndex = function (dim) {\n        var dimIdx = this.getDimensionIndex(dim);\n\n        if (\"development\" !== 'production') {\n          if (dimIdx == null) {\n            throw new Error('Unknown dimension ' + dim);\n          }\n        }\n\n        return dimIdx;\n      };\n      /**\n       * Get type and calculation info of particular dimension\n       * @param dim\n       *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius\n       *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'\n       */\n\n\n      SeriesData.prototype.getDimensionInfo = function (dim) {\n        // Do not clone, because there may be categories in dimInfo.\n        return this._getDimInfo(this.getDimension(dim));\n      };\n\n      SeriesData.prototype._initGetDimensionInfo = function (needsHasOwn) {\n        var dimensionInfos = this._dimInfos;\n        this._getDimInfo = needsHasOwn ? function (dimName) {\n          return dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined;\n        } : function (dimName) {\n          return dimensionInfos[dimName];\n        };\n      };\n      /**\n       * concrete dimension name list on coord.\n       */\n\n\n      SeriesData.prototype.getDimensionsOnCoord = function () {\n        return this._dimSummary.dataDimsOnCoord.slice();\n      };\n\n      SeriesData.prototype.mapDimension = function (coordDim, idx) {\n        var dimensionsSummary = this._dimSummary;\n\n        if (idx == null) {\n          return dimensionsSummary.encodeFirstDimNotExtra[coordDim];\n        }\n\n        var dims = dimensionsSummary.encode[coordDim];\n        return dims ? dims[idx] : null;\n      };\n\n      SeriesData.prototype.mapDimensionsAll = function (coordDim) {\n        var dimensionsSummary = this._dimSummary;\n        var dims = dimensionsSummary.encode[coordDim];\n        return (dims || []).slice();\n      };\n\n      SeriesData.prototype.getStore = function () {\n        return this._store;\n      };\n      /**\n       * Initialize from data\n       * @param data source or data or data store.\n       * @param nameList The name of a datum is used on data diff and\n       *        default label/tooltip.\n       *        A name can be specified in encode.itemName,\n       *        or dataItem.name (only for series option data),\n       *        or provided in nameList from outside.\n       */\n\n\n      SeriesData.prototype.initData = function (data, nameList, dimValueGetter) {\n        var _this = this;\n\n        var store;\n\n        if (data instanceof DataStore) {\n          store = data;\n        }\n\n        if (!store) {\n          var dimensions = this.dimensions;\n          var provider = isSourceInstance(data) || isArrayLike(data) ? new DefaultDataProvider(data, dimensions.length) : data;\n          store = new DataStore();\n          var dimensionInfos = map$1(dimensions, function (dimName) {\n            return {\n              type: _this._dimInfos[dimName].type,\n              property: dimName\n            };\n          });\n          store.initData(provider, dimensionInfos, dimValueGetter);\n        }\n\n        this._store = store; // Reset\n\n        this._nameList = (nameList || []).slice();\n        this._idList = [];\n        this._nameRepeatCount = {};\n\n        this._doInit(0, store.count()); // Cache summary info for fast visit. See \"dimensionHelper\".\n        // Needs to be initialized after store is prepared.\n\n\n        this._dimSummary = summarizeDimensions(this, this._schema);\n        this.userOutput = this._dimSummary.userOutput;\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       */\n\n\n      SeriesData.prototype.appendData = function (data) {\n        var range = this._store.appendData(data);\n\n        this._doInit(range[0], range[1]);\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       * This method does not modify `rawData` (`dataProvider`), but only\n       * add values to store.\n       *\n       * The final count will be increased by `Math.max(values.length, names.length)`.\n       *\n       * @param values That is the SourceType: 'arrayRows', like\n       *        [\n       *            [12, 33, 44],\n       *            [NaN, 43, 1],\n       *            ['-', 'asdf', 0]\n       *        ]\n       *        Each item is exactly corresponding to a dimension.\n       */\n\n\n      SeriesData.prototype.appendValues = function (values, names) {\n        var _a = this._store.appendValues(values, names.length),\n            start = _a.start,\n            end = _a.end;\n\n        var shouldMakeIdFromName = this._shouldMakeIdFromName();\n\n        this._updateOrdinalMeta();\n\n        if (names) {\n          for (var idx = start; idx < end; idx++) {\n            var sourceIdx = idx - start;\n            this._nameList[idx] = names[sourceIdx];\n\n            if (shouldMakeIdFromName) {\n              makeIdFromName(this, idx);\n            }\n          }\n        }\n      };\n\n      SeriesData.prototype._updateOrdinalMeta = function () {\n        var store = this._store;\n        var dimensions = this.dimensions;\n\n        for (var i = 0; i < dimensions.length; i++) {\n          var dimInfo = this._dimInfos[dimensions[i]];\n\n          if (dimInfo.ordinalMeta) {\n            store.collectOrdinalMeta(dimInfo.storeDimIndex, dimInfo.ordinalMeta);\n          }\n        }\n      };\n\n      SeriesData.prototype._shouldMakeIdFromName = function () {\n        var provider = this._store.getProvider();\n\n        return this._idDimIdx == null && provider.getSource().sourceFormat !== SOURCE_FORMAT_TYPED_ARRAY && !provider.fillStorage;\n      };\n\n      SeriesData.prototype._doInit = function (start, end) {\n        if (start >= end) {\n          return;\n        }\n\n        var store = this._store;\n        var provider = store.getProvider();\n\n        this._updateOrdinalMeta();\n\n        var nameList = this._nameList;\n        var idList = this._idList;\n        var sourceFormat = provider.getSource().sourceFormat;\n        var isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL; // Each data item is value\n        // [1, 2]\n        // 2\n        // Bar chart, line chart which uses category axis\n        // only gives the 'y' value. 'x' value is the indices of category\n        // Use a tempValue to normalize the value to be a (x, y) value\n        // If dataItem is {name: ...} or {id: ...}, it has highest priority.\n        // This kind of ids and names are always stored `_nameList` and `_idList`.\n\n        if (isFormatOriginal && !provider.pure) {\n          var sharedDataItem = [];\n\n          for (var idx = start; idx < end; idx++) {\n            // NOTICE: Try not to write things into dataItem\n            var dataItem = provider.getItem(idx, sharedDataItem);\n\n            if (!this.hasItemOption && isDataItemOption(dataItem)) {\n              this.hasItemOption = true;\n            }\n\n            if (dataItem) {\n              var itemName = dataItem.name;\n\n              if (nameList[idx] == null && itemName != null) {\n                nameList[idx] = convertOptionIdName(itemName, null);\n              }\n\n              var itemId = dataItem.id;\n\n              if (idList[idx] == null && itemId != null) {\n                idList[idx] = convertOptionIdName(itemId, null);\n              }\n            }\n          }\n        }\n\n        if (this._shouldMakeIdFromName()) {\n          for (var idx = start; idx < end; idx++) {\n            makeIdFromName(this, idx);\n          }\n        }\n\n        prepareInvertedIndex(this);\n      };\n      /**\n       * PENDING: In fact currently this function is only used to short-circuit\n       * the calling of `scale.unionExtentFromData` when data have been filtered by modules\n       * like \"dataZoom\". `scale.unionExtentFromData` is used to calculate data extent for series on\n       * an axis, but if a \"axis related data filter module\" is used, the extent of the axis have\n       * been fixed and no need to calling `scale.unionExtentFromData` actually.\n       * But if we add \"custom data filter\" in future, which is not \"axis related\", this method may\n       * be still needed.\n       *\n       * Optimize for the scenario that data is filtered by a given extent.\n       * Consider that if data amount is more than hundreds of thousand,\n       * extent calculation will cost more than 10ms and the cache will\n       * be erased because of the filtering.\n       */\n\n\n      SeriesData.prototype.getApproximateExtent = function (dim) {\n        return this._approximateExtent[dim] || this._store.getDataExtent(this._getStoreDimIndex(dim));\n      };\n      /**\n       * Calculate extent on a filtered data might be time consuming.\n       * Approximate extent is only used for: calculate extent of filtered data outside.\n       */\n\n\n      SeriesData.prototype.setApproximateExtent = function (extent, dim) {\n        dim = this.getDimension(dim);\n        this._approximateExtent[dim] = extent.slice();\n      };\n\n      SeriesData.prototype.getCalculationInfo = function (key) {\n        return this._calculationInfo[key];\n      };\n\n      SeriesData.prototype.setCalculationInfo = function (key, value) {\n        isObject$2(key) ? extend(this._calculationInfo, key) : this._calculationInfo[key] = value;\n      };\n      /**\n       * @return Never be null/undefined. `number` will be converted to string. Because:\n       * In most cases, name is used in display, where returning a string is more convenient.\n       * In other cases, name is used in query (see `indexOfName`), where we can keep the\n       * rule that name `2` equals to name `'2'`.\n       */\n\n\n      SeriesData.prototype.getName = function (idx) {\n        var rawIndex = this.getRawIndex(idx);\n        var name = this._nameList[rawIndex];\n\n        if (name == null && this._nameDimIdx != null) {\n          name = getIdNameFromStore(this, this._nameDimIdx, rawIndex);\n        }\n\n        if (name == null) {\n          name = '';\n        }\n\n        return name;\n      };\n\n      SeriesData.prototype._getCategory = function (dimIdx, idx) {\n        var ordinal = this._store.get(dimIdx, idx);\n\n        var ordinalMeta = this._store.getOrdinalMeta(dimIdx);\n\n        if (ordinalMeta) {\n          return ordinalMeta.categories[ordinal];\n        }\n\n        return ordinal;\n      };\n      /**\n       * @return Never null/undefined. `number` will be converted to string. Because:\n       * In all cases having encountered at present, id is used in making diff comparison, which\n       * are usually based on hash map. We can keep the rule that the internal id are always string\n       * (treat `2` is the same as `'2'`) to make the related logic simple.\n       */\n\n\n      SeriesData.prototype.getId = function (idx) {\n        return getId(this, this.getRawIndex(idx));\n      };\n\n      SeriesData.prototype.count = function () {\n        return this._store.count();\n      };\n      /**\n       * Get value. Return NaN if idx is out of range.\n       *\n       * @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead.\n       */\n\n\n      SeriesData.prototype.get = function (dim, idx) {\n        var store = this._store;\n        var dimInfo = this._dimInfos[dim];\n\n        if (dimInfo) {\n          return store.get(dimInfo.storeDimIndex, idx);\n        }\n      };\n      /**\n       * @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead.\n       */\n\n\n      SeriesData.prototype.getByRawIndex = function (dim, rawIdx) {\n        var store = this._store;\n        var dimInfo = this._dimInfos[dim];\n\n        if (dimInfo) {\n          return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx);\n        }\n      };\n\n      SeriesData.prototype.getIndices = function () {\n        return this._store.getIndices();\n      };\n\n      SeriesData.prototype.getDataExtent = function (dim) {\n        return this._store.getDataExtent(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getSum = function (dim) {\n        return this._store.getSum(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getMedian = function (dim) {\n        return this._store.getMedian(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getValues = function (dimensions, idx) {\n        var _this = this;\n\n        var store = this._store;\n        return isArray(dimensions) ? store.getValues(map$1(dimensions, function (dim) {\n          return _this._getStoreDimIndex(dim);\n        }), idx) : store.getValues(dimensions);\n      };\n      /**\n       * If value is NaN. Including '-'\n       * Only check the coord dimensions.\n       */\n\n\n      SeriesData.prototype.hasValue = function (idx) {\n        var dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord;\n\n        for (var i = 0, len = dataDimIndicesOnCoord.length; i < len; i++) {\n          // Ordinal type originally can be string or number.\n          // But when an ordinal type is used on coord, it can\n          // not be string but only number. So we can also use isNaN.\n          if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx))) {\n            return false;\n          }\n        }\n\n        return true;\n      };\n      /**\n       * Retrieve the index with given name\n       */\n\n\n      SeriesData.prototype.indexOfName = function (name) {\n        for (var i = 0, len = this._store.count(); i < len; i++) {\n          if (this.getName(i) === name) {\n            return i;\n          }\n        }\n\n        return -1;\n      };\n\n      SeriesData.prototype.getRawIndex = function (idx) {\n        return this._store.getRawIndex(idx);\n      };\n\n      SeriesData.prototype.indexOfRawIndex = function (rawIndex) {\n        return this._store.indexOfRawIndex(rawIndex);\n      };\n      /**\n       * Only support the dimension which inverted index created.\n       * Do not support other cases until required.\n       * @param dim concrete dim\n       * @param value ordinal index\n       * @return rawIndex\n       */\n\n\n      SeriesData.prototype.rawIndexOf = function (dim, value) {\n        var invertedIndices = dim && this._invertedIndicesMap[dim];\n\n        if (\"development\" !== 'production') {\n          if (!invertedIndices) {\n            throw new Error('Do not supported yet');\n          }\n        }\n\n        var rawIndex = invertedIndices[value];\n\n        if (rawIndex == null || isNaN(rawIndex)) {\n          return INDEX_NOT_FOUND;\n        }\n\n        return rawIndex;\n      };\n      /**\n       * Retrieve the index of nearest value\n       * @param dim\n       * @param value\n       * @param [maxDistance=Infinity]\n       * @return If and only if multiple indices has\n       *         the same value, they are put to the result.\n       */\n\n\n      SeriesData.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n        return this._store.indicesOfNearest(this._getStoreDimIndex(dim), value, maxDistance);\n      };\n\n      SeriesData.prototype.each = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n\n        this._store.each(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n      };\n\n      SeriesData.prototype.filterSelf = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n        this._store = this._store.filter(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n        return this;\n      };\n      /**\n       * Select data in range. (For optimization of filter)\n       * (Manually inline code, support 5 million data filtering in data zoom.)\n       */\n\n\n      SeriesData.prototype.selectRange = function (range) {\n\n        var _this = this;\n\n        var innerRange = {};\n        var dims = keys(range);\n        each(dims, function (dim) {\n          var dimIdx = _this._getStoreDimIndex(dim);\n\n          innerRange[dimIdx] = range[dim];\n        });\n        this._store = this._store.selectRange(innerRange);\n        return this;\n      };\n      /* eslint-enable max-len */\n\n\n      SeriesData.prototype.mapArray = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        ctx = ctx || this;\n        var result = [];\n        this.each(dims, function () {\n          result.push(cb && cb.apply(this, arguments));\n        }, ctx);\n        return result;\n      };\n\n      SeriesData.prototype.map = function (dims, cb, ctx, ctxCompat) {\n\n        var fCtx = ctx || ctxCompat || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.map(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n        return list;\n      };\n\n      SeriesData.prototype.modify = function (dims, cb, ctx, ctxCompat) {\n        var _this = this; // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || ctxCompat || this;\n\n        if (\"development\" !== 'production') {\n          each(normalizeDimensions(dims), function (dim) {\n            var dimInfo = _this.getDimensionInfo(dim);\n\n            if (!dimInfo.isCalculationCoord) {\n              console.error('Danger: only stack dimension can be modified');\n            }\n          });\n        }\n\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); // If do shallow clone here, if there are too many stacked series,\n        // it still cost lots of memory, because `_store.dimensions` are not shared.\n        // We should consider there probably be shallow clone happen in each series\n        // in consequent filter/map.\n\n        this._store.modify(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n      };\n      /**\n       * Large data down sampling on given dimension\n       * @param sampleIndex Sample index for name and id\n       */\n\n\n      SeriesData.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.downSample(this._getStoreDimIndex(dimension), rate, sampleValue, sampleIndex);\n        return list;\n      };\n      /**\n       * Large data down sampling using largest-triangle-three-buckets\n       * @param {string} valueDimension\n       * @param {number} targetCount\n       */\n\n\n      SeriesData.prototype.lttbDownSample = function (valueDimension, rate) {\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.lttbDownSample(this._getStoreDimIndex(valueDimension), rate);\n        return list;\n      };\n\n      SeriesData.prototype.getRawDataItem = function (idx) {\n        return this._store.getRawDataItem(idx);\n      };\n      /**\n       * Get model of one data item.\n       */\n      // TODO: Type of data item\n\n\n      SeriesData.prototype.getItemModel = function (idx) {\n        var hostModel = this.hostModel;\n        var dataItem = this.getRawDataItem(idx);\n        return new Model(dataItem, hostModel, hostModel && hostModel.ecModel);\n      };\n      /**\n       * Create a data differ\n       */\n\n\n      SeriesData.prototype.diff = function (otherList) {\n        var thisList = this;\n        return new DataDiffer(otherList ? otherList.getStore().getIndices() : [], this.getStore().getIndices(), function (idx) {\n          return getId(otherList, idx);\n        }, function (idx) {\n          return getId(thisList, idx);\n        });\n      };\n      /**\n       * Get visual property.\n       */\n\n\n      SeriesData.prototype.getVisual = function (key) {\n        var visual = this._visual;\n        return visual && visual[key];\n      };\n\n      SeriesData.prototype.setVisual = function (kvObj, val) {\n        this._visual = this._visual || {};\n\n        if (isObject$2(kvObj)) {\n          extend(this._visual, kvObj);\n        } else {\n          this._visual[kvObj] = val;\n        }\n      };\n      /**\n       * Get visual property of single data item\n       */\n      // eslint-disable-next-line\n\n\n      SeriesData.prototype.getItemVisual = function (idx, key) {\n        var itemVisual = this._itemVisuals[idx];\n        var val = itemVisual && itemVisual[key];\n\n        if (val == null) {\n          // Use global visual property\n          return this.getVisual(key);\n        }\n\n        return val;\n      };\n      /**\n       * If exists visual property of single data item\n       */\n\n\n      SeriesData.prototype.hasItemVisual = function () {\n        return this._itemVisuals.length > 0;\n      };\n      /**\n       * Make sure itemVisual property is unique\n       */\n      // TODO: use key to save visual to reduce memory.\n\n\n      SeriesData.prototype.ensureUniqueItemVisual = function (idx, key) {\n        var itemVisuals = this._itemVisuals;\n        var itemVisual = itemVisuals[idx];\n\n        if (!itemVisual) {\n          itemVisual = itemVisuals[idx] = {};\n        }\n\n        var val = itemVisual[key];\n\n        if (val == null) {\n          val = this.getVisual(key); // TODO Performance?\n\n          if (isArray(val)) {\n            val = val.slice();\n          } else if (isObject$2(val)) {\n            val = extend({}, val);\n          }\n\n          itemVisual[key] = val;\n        }\n\n        return val;\n      }; // eslint-disable-next-line\n\n\n      SeriesData.prototype.setItemVisual = function (idx, key, value) {\n        var itemVisual = this._itemVisuals[idx] || {};\n        this._itemVisuals[idx] = itemVisual;\n\n        if (isObject$2(key)) {\n          extend(itemVisual, key);\n        } else {\n          itemVisual[key] = value;\n        }\n      };\n      /**\n       * Clear itemVisuals and list visual.\n       */\n\n\n      SeriesData.prototype.clearAllVisual = function () {\n        this._visual = {};\n        this._itemVisuals = [];\n      };\n\n      SeriesData.prototype.setLayout = function (key, val) {\n        isObject$2(key) ? extend(this._layout, key) : this._layout[key] = val;\n      };\n      /**\n       * Get layout property.\n       */\n\n\n      SeriesData.prototype.getLayout = function (key) {\n        return this._layout[key];\n      };\n      /**\n       * Get layout of single data item\n       */\n\n\n      SeriesData.prototype.getItemLayout = function (idx) {\n        return this._itemLayouts[idx];\n      };\n      /**\n       * Set layout of single data item\n       */\n\n\n      SeriesData.prototype.setItemLayout = function (idx, layout, merge) {\n        this._itemLayouts[idx] = merge ? extend(this._itemLayouts[idx] || {}, layout) : layout;\n      };\n      /**\n       * Clear all layout of single data item\n       */\n\n\n      SeriesData.prototype.clearItemLayouts = function () {\n        this._itemLayouts.length = 0;\n      };\n      /**\n       * Set graphic element relative to data. It can be set as null\n       */\n\n\n      SeriesData.prototype.setItemGraphicEl = function (idx, el) {\n        var seriesIndex = this.hostModel && this.hostModel.seriesIndex;\n        setCommonECData(seriesIndex, this.dataType, idx, el);\n        this._graphicEls[idx] = el;\n      };\n\n      SeriesData.prototype.getItemGraphicEl = function (idx) {\n        return this._graphicEls[idx];\n      };\n\n      SeriesData.prototype.eachItemGraphicEl = function (cb, context) {\n        each(this._graphicEls, function (el, idx) {\n          if (el) {\n            cb && cb.call(context, el, idx);\n          }\n        });\n      };\n      /**\n       * Shallow clone a new list except visual and layout properties, and graph elements.\n       * New list only change the indices.\n       */\n\n\n      SeriesData.prototype.cloneShallow = function (list) {\n        if (!list) {\n          list = new SeriesData(this._schema ? this._schema : map$1(this.dimensions, this._getDimInfo, this), this.hostModel);\n        }\n\n        transferProperties(list, this);\n        list._store = this._store;\n        return list;\n      };\n      /**\n       * Wrap some method to add more feature\n       */\n\n\n      SeriesData.prototype.wrapMethod = function (methodName, injectFunction) {\n        var originalMethod = this[methodName];\n\n        if (!isFunction(originalMethod)) {\n          return;\n        }\n\n        this.__wrappedMethods = this.__wrappedMethods || [];\n\n        this.__wrappedMethods.push(methodName);\n\n        this[methodName] = function () {\n          var res = originalMethod.apply(this, arguments);\n          return injectFunction.apply(this, [res].concat(slice(arguments)));\n        };\n      }; // ----------------------------------------------------------\n      // A work around for internal method visiting private member.\n      // ----------------------------------------------------------\n\n\n      SeriesData.internalField = function () {\n        prepareInvertedIndex = function (data) {\n          var invertedIndicesMap = data._invertedIndicesMap;\n          each(invertedIndicesMap, function (invertedIndices, dim) {\n            var dimInfo = data._dimInfos[dim]; // Currently, only dimensions that has ordinalMeta can create inverted indices.\n\n            var ordinalMeta = dimInfo.ordinalMeta;\n            var store = data._store;\n\n            if (ordinalMeta) {\n              invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array$1(ordinalMeta.categories.length); // The default value of TypedArray is 0. To avoid miss\n              // mapping to 0, we should set it as INDEX_NOT_FOUND.\n\n              for (var i = 0; i < invertedIndices.length; i++) {\n                invertedIndices[i] = INDEX_NOT_FOUND;\n              }\n\n              for (var i = 0; i < store.count(); i++) {\n                // Only support the case that all values are distinct.\n                invertedIndices[store.get(dimInfo.storeDimIndex, i)] = i;\n              }\n            }\n          });\n        };\n\n        getIdNameFromStore = function (data, dimIdx, idx) {\n          return convertOptionIdName(data._getCategory(dimIdx, idx), null);\n        };\n        /**\n         * @see the comment of `List['getId']`.\n         */\n\n\n        getId = function (data, rawIndex) {\n          var id = data._idList[rawIndex];\n\n          if (id == null && data._idDimIdx != null) {\n            id = getIdNameFromStore(data, data._idDimIdx, rawIndex);\n          }\n\n          if (id == null) {\n            id = ID_PREFIX + rawIndex;\n          }\n\n          return id;\n        };\n\n        normalizeDimensions = function (dimensions) {\n          if (!isArray(dimensions)) {\n            dimensions = dimensions != null ? [dimensions] : [];\n          }\n\n          return dimensions;\n        };\n        /**\n         * Data in excludeDimensions is copied, otherwise transferred.\n         */\n\n\n        cloneListForMapAndSample = function (original) {\n          var list = new SeriesData(original._schema ? original._schema : map$1(original.dimensions, original._getDimInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked\n\n          transferProperties(list, original);\n          return list;\n        };\n\n        transferProperties = function (target, source) {\n          each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {\n            if (source.hasOwnProperty(propName)) {\n              target[propName] = source[propName];\n            }\n          });\n          target.__wrappedMethods = source.__wrappedMethods;\n          each(CLONE_PROPERTIES, function (propName) {\n            target[propName] = clone(source[propName]);\n          });\n          target._calculationInfo = extend({}, source._calculationInfo);\n        };\n\n        makeIdFromName = function (data, idx) {\n          var nameList = data._nameList;\n          var idList = data._idList;\n          var nameDimIdx = data._nameDimIdx;\n          var idDimIdx = data._idDimIdx;\n          var name = nameList[idx];\n          var id = idList[idx];\n\n          if (name == null && nameDimIdx != null) {\n            nameList[idx] = name = getIdNameFromStore(data, nameDimIdx, idx);\n          }\n\n          if (id == null && idDimIdx != null) {\n            idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx);\n          }\n\n          if (id == null && name != null) {\n            var nameRepeatCount = data._nameRepeatCount;\n            var nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1;\n            id = name;\n\n            if (nmCnt > 1) {\n              id += '__ec__' + nmCnt;\n            }\n\n            idList[idx] = id;\n          }\n        };\n      }();\n\n      return SeriesData;\n    }();\n\n    /**\n     * For outside usage compat (like echarts-gl are using it).\n     */\n\n    function createDimensions(source, opt) {\n      return prepareSeriesDataSchema(source, opt).dimensions;\n    }\n    /**\n     * This method builds the relationship between:\n     * + \"what the coord sys or series requires (see `coordDimensions`)\",\n     * + \"what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)\"\n     * + \"what the data source provids (see `source`)\".\n     *\n     * Some guess strategy will be adapted if user does not define something.\n     * If no 'value' dimension specified, the first no-named dimension will be\n     * named as 'value'.\n     *\n     * @return The results are always sorted by `storeDimIndex` asc.\n     */\n\n    function prepareSeriesDataSchema( // TODO: TYPE completeDimensions type\n    source, opt) {\n      if (!isSourceInstance(source)) {\n        source = createSourceFromSeriesDataOption(source);\n      }\n\n      opt = opt || {};\n      var sysDims = opt.coordDimensions || [];\n      var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || [];\n      var coordDimNameMap = createHashMap();\n      var resultList = [];\n      var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount); // Try to ignore unused dimensions if sharing a high dimension datastore\n      // 30 is an experience value.\n\n      var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount);\n      var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine;\n      var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef);\n      var encodeDef = opt.encodeDefine;\n\n      if (!encodeDef && opt.encodeDefaulter) {\n        encodeDef = opt.encodeDefaulter(source, dimCount);\n      }\n\n      var encodeDefMap = createHashMap(encodeDef);\n      var indicesMap = new CtorInt32Array(dimCount);\n\n      for (var i = 0; i < indicesMap.length; i++) {\n        indicesMap[i] = -1;\n      }\n\n      function getResultItem(dimIdx) {\n        var idx = indicesMap[dimIdx];\n\n        if (idx < 0) {\n          var dimDefItemRaw = dimsDef[dimIdx];\n          var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : {\n            name: dimDefItemRaw\n          };\n          var resultItem = new SeriesDimensionDefine();\n          var userDimName = dimDefItem.name;\n\n          if (userDimName != null && dataDimNameMap.get(userDimName) != null) {\n            // Only if `series.dimensions` is defined in option\n            // displayName, will be set, and dimension will be displayed vertically in\n            // tooltip by default.\n            resultItem.name = resultItem.displayName = userDimName;\n          }\n\n          dimDefItem.type != null && (resultItem.type = dimDefItem.type);\n          dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);\n          var newIdx = resultList.length;\n          indicesMap[dimIdx] = newIdx;\n          resultItem.storeDimIndex = dimIdx;\n          resultList.push(resultItem);\n          return resultItem;\n        }\n\n        return resultList[idx];\n      }\n\n      if (!omitUnusedDimensions) {\n        for (var i = 0; i < dimCount; i++) {\n          getResultItem(i);\n        }\n      } // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.\n\n\n      encodeDefMap.each(function (dataDimsRaw, coordDim) {\n        var dataDims = normalizeToArray(dataDimsRaw).slice(); // Note: It is allowed that `dataDims.length` is `0`, e.g., options is\n        // `{encode: {x: -1, y: 1}}`. Should not filter anything in\n        // this case.\n\n        if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) {\n          encodeDefMap.set(coordDim, false);\n          return;\n        }\n\n        var validDataDims = encodeDefMap.set(coordDim, []);\n        each(dataDims, function (resultDimIdxOrName, idx) {\n          // The input resultDimIdx can be dim name or index.\n          var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName;\n\n          if (resultDimIdx != null && resultDimIdx < dimCount) {\n            validDataDims[idx] = resultDimIdx;\n            applyDim(getResultItem(resultDimIdx), coordDim, idx);\n          }\n        });\n      }); // Apply templates and default order from `sysDims`.\n\n      var availDimIdx = 0;\n      each(sysDims, function (sysDimItemRaw) {\n        var coordDim;\n        var sysDimItemDimsDef;\n        var sysDimItemOtherDims;\n        var sysDimItem;\n\n        if (isString(sysDimItemRaw)) {\n          coordDim = sysDimItemRaw;\n          sysDimItem = {};\n        } else {\n          sysDimItem = sysDimItemRaw;\n          coordDim = sysDimItem.name;\n          var ordinalMeta = sysDimItem.ordinalMeta;\n          sysDimItem.ordinalMeta = null;\n          sysDimItem = extend({}, sysDimItem);\n          sysDimItem.ordinalMeta = ordinalMeta; // `coordDimIndex` should not be set directly.\n\n          sysDimItemDimsDef = sysDimItem.dimsDef;\n          sysDimItemOtherDims = sysDimItem.otherDims;\n          sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null;\n        }\n\n        var dataDims = encodeDefMap.get(coordDim); // negative resultDimIdx means no need to mapping.\n\n        if (dataDims === false) {\n          return;\n        }\n\n        dataDims = normalizeToArray(dataDims); // dimensions provides default dim sequences.\n\n        if (!dataDims.length) {\n          for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {\n            while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) {\n              availDimIdx++;\n            }\n\n            availDimIdx < dimCount && dataDims.push(availDimIdx++);\n          }\n        } // Apply templates.\n\n\n        each(dataDims, function (resultDimIdx, coordDimIndex) {\n          var resultItem = getResultItem(resultDimIdx); // Coordinate system has a higher priority on dim type than source.\n\n          if (isUsingSourceDimensionsDef && sysDimItem.type != null) {\n            resultItem.type = sysDimItem.type;\n          }\n\n          applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);\n\n          if (resultItem.name == null && sysDimItemDimsDef) {\n            var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];\n            !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {\n              name: sysDimItemDimsDefItem\n            });\n            resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;\n            resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;\n          } // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}\n\n\n          sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);\n        });\n      });\n\n      function applyDim(resultItem, coordDim, coordDimIndex) {\n        if (VISUAL_DIMENSIONS.get(coordDim) != null) {\n          resultItem.otherDims[coordDim] = coordDimIndex;\n        } else {\n          resultItem.coordDim = coordDim;\n          resultItem.coordDimIndex = coordDimIndex;\n          coordDimNameMap.set(coordDim, true);\n        }\n      } // Make sure the first extra dim is 'value'.\n\n\n      var generateCoord = opt.generateCoord;\n      var generateCoordCount = opt.generateCoordCount;\n      var fromZero = generateCoordCount != null;\n      generateCoordCount = generateCoord ? generateCoordCount || 1 : 0;\n      var extra = generateCoord || 'value';\n\n      function ifNoNameFillWithCoordName(resultItem) {\n        if (resultItem.name == null) {\n          // Duplication will be removed in the next step.\n          resultItem.name = resultItem.coordDim;\n        }\n      } // Set dim `name` and other `coordDim` and other props.\n\n\n      if (!omitUnusedDimensions) {\n        for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {\n          var resultItem = getResultItem(resultDimIdx);\n          var coordDim = resultItem.coordDim;\n\n          if (coordDim == null) {\n            // TODO no need to generate coordDim for isExtraCoord?\n            resultItem.coordDim = genCoordDimName(extra, coordDimNameMap, fromZero);\n            resultItem.coordDimIndex = 0; // Series specified generateCoord is using out.\n\n            if (!generateCoord || generateCoordCount <= 0) {\n              resultItem.isExtraCoord = true;\n            }\n\n            generateCoordCount--;\n          }\n\n          ifNoNameFillWithCoordName(resultItem);\n\n          if (resultItem.type == null && (guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must // Consider the case:\n          // {\n          //    dataset: {source: [\n          //        ['2001', 123],\n          //        ['2002', 456],\n          //        ...\n          //        ['The others', 987],\n          //    ]},\n          //    series: {type: 'pie'}\n          // }\n          // The first column should better be treated as a \"ordinal\" although it\n          // might not be detected as an \"ordinal\" by `guessOrdinal`.\n          || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) {\n            resultItem.type = 'ordinal';\n          }\n        }\n      } else {\n        each(resultList, function (resultItem) {\n          // PENDING: guessOrdinal or let user specify type: 'ordinal' manually?\n          ifNoNameFillWithCoordName(resultItem);\n        }); // Sort dimensions: there are some rule that use the last dim as label,\n        // and for some latter travel process easier.\n\n        resultList.sort(function (item0, item1) {\n          return item0.storeDimIndex - item1.storeDimIndex;\n        });\n      }\n\n      removeDuplication(resultList);\n      return new SeriesDataSchema({\n        source: source,\n        dimensions: resultList,\n        fullDimensionCount: dimCount,\n        dimensionOmitted: omitUnusedDimensions\n      });\n    }\n\n    function removeDuplication(result) {\n      var duplicationMap = createHashMap();\n\n      for (var i = 0; i < result.length; i++) {\n        var dim = result[i];\n        var dimOriginalName = dim.name;\n        var count = duplicationMap.get(dimOriginalName) || 0;\n\n        if (count > 0) {\n          // Starts from 0.\n          dim.name = dimOriginalName + (count - 1);\n        }\n\n        count++;\n        duplicationMap.set(dimOriginalName, count);\n      }\n    } // ??? TODO\n    // Originally detect dimCount by data[0]. Should we\n    // optimize it to only by sysDims and dimensions and encode.\n    // So only necessary dims will be initialized.\n    // But\n    // (1) custom series should be considered. where other dims\n    // may be visited.\n    // (2) sometimes user need to calculate bubble size or use visualMap\n    // on other dimensions besides coordSys needed.\n    // So, dims that is not used by system, should be shared in data store?\n\n\n    function getDimCount(source, sysDims, dimsDef, optDimCount) {\n      // Note that the result dimCount should not small than columns count\n      // of data, otherwise `dataDimNameMap` checking will be incorrect.\n      var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0);\n      each(sysDims, function (sysDimItem) {\n        var sysDimItemDimsDef;\n\n        if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) {\n          dimCount = Math.max(dimCount, sysDimItemDimsDef.length);\n        }\n      });\n      return dimCount;\n    }\n\n    function genCoordDimName(name, map, fromZero) {\n      if (fromZero || map.hasKey(name)) {\n        var i = 0;\n\n        while (map.hasKey(name + i)) {\n          i++;\n        }\n\n        name += i;\n      }\n\n      map.set(name, true);\n      return name;\n    }\n\n    /**\n     * @class\n     * For example:\n     * {\n     *     coordSysName: 'cartesian2d',\n     *     coordSysDims: ['x', 'y', ...],\n     *     axisMap: HashMap({\n     *         x: xAxisModel,\n     *         y: yAxisModel\n     *     }),\n     *     categoryAxisMap: HashMap({\n     *         x: xAxisModel,\n     *         y: undefined\n     *     }),\n     *     // The index of the first category axis in `coordSysDims`.\n     *     // `null/undefined` means no category axis exists.\n     *     firstCategoryDimIndex: 1,\n     *     // To replace user specified encode.\n     * }\n     */\n\n    var CoordSysInfo =\n    /** @class */\n    function () {\n      function CoordSysInfo(coordSysName) {\n        this.coordSysDims = [];\n        this.axisMap = createHashMap();\n        this.categoryAxisMap = createHashMap();\n        this.coordSysName = coordSysName;\n      }\n\n      return CoordSysInfo;\n    }();\n\n    function getCoordSysInfoBySeries(seriesModel) {\n      var coordSysName = seriesModel.get('coordinateSystem');\n      var result = new CoordSysInfo(coordSysName);\n      var fetch = fetchers[coordSysName];\n\n      if (fetch) {\n        fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);\n        return result;\n      }\n    }\n    var fetchers = {\n      cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var xAxisModel = seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n        var yAxisModel = seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!xAxisModel) {\n            throw new Error('xAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('xAxisId'), 0) + '\" not found');\n          }\n\n          if (!yAxisModel) {\n            throw new Error('yAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('yAxisId'), 0) + '\" not found');\n          }\n        }\n\n        result.coordSysDims = ['x', 'y'];\n        axisMap.set('x', xAxisModel);\n        axisMap.set('y', yAxisModel);\n\n        if (isCategory(xAxisModel)) {\n          categoryAxisMap.set('x', xAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n\n        if (isCategory(yAxisModel)) {\n          categoryAxisMap.set('y', yAxisModel);\n          result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n        }\n      },\n      singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!singleAxisModel) {\n            throw new Error('singleAxis should be specified.');\n          }\n        }\n\n        result.coordSysDims = ['single'];\n        axisMap.set('single', singleAxisModel);\n\n        if (isCategory(singleAxisModel)) {\n          categoryAxisMap.set('single', singleAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n      },\n      polar: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n        var radiusAxisModel = polarModel.findAxisModel('radiusAxis');\n        var angleAxisModel = polarModel.findAxisModel('angleAxis');\n\n        if (\"development\" !== 'production') {\n          if (!angleAxisModel) {\n            throw new Error('angleAxis option not found');\n          }\n\n          if (!radiusAxisModel) {\n            throw new Error('radiusAxis option not found');\n          }\n        }\n\n        result.coordSysDims = ['radius', 'angle'];\n        axisMap.set('radius', radiusAxisModel);\n        axisMap.set('angle', angleAxisModel);\n\n        if (isCategory(radiusAxisModel)) {\n          categoryAxisMap.set('radius', radiusAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n\n        if (isCategory(angleAxisModel)) {\n          categoryAxisMap.set('angle', angleAxisModel);\n          result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n        }\n      },\n      geo: function (seriesModel, result, axisMap, categoryAxisMap) {\n        result.coordSysDims = ['lng', 'lat'];\n      },\n      parallel: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var ecModel = seriesModel.ecModel;\n        var parallelModel = ecModel.getComponent('parallel', seriesModel.get('parallelIndex'));\n        var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice();\n        each(parallelModel.parallelAxisIndex, function (axisIndex, index) {\n          var axisModel = ecModel.getComponent('parallelAxis', axisIndex);\n          var axisDim = coordSysDims[index];\n          axisMap.set(axisDim, axisModel);\n\n          if (isCategory(axisModel)) {\n            categoryAxisMap.set(axisDim, axisModel);\n\n            if (result.firstCategoryDimIndex == null) {\n              result.firstCategoryDimIndex = index;\n            }\n          }\n        });\n      }\n    };\n\n    function isCategory(axisModel) {\n      return axisModel.get('type') === 'category';\n    }\n\n    /**\n     * Note that it is too complicated to support 3d stack by value\n     * (have to create two-dimension inverted index), so in 3d case\n     * we just support that stacked by index.\n     *\n     * @param seriesModel\n     * @param dimensionsInput The same as the input of <module:echarts/data/SeriesData>.\n     *        The input will be modified.\n     * @param opt\n     * @param opt.stackedCoordDimension Specify a coord dimension if needed.\n     * @param opt.byIndex=false\n     * @return calculationInfo\n     * {\n     *     stackedDimension: string\n     *     stackedByDimension: string\n     *     isStackedByIndex: boolean\n     *     stackedOverDimension: string\n     *     stackResultDimension: string\n     * }\n     */\n\n    function enableDataStack(seriesModel, dimensionsInput, opt) {\n      opt = opt || {};\n      var byIndex = opt.byIndex;\n      var stackedCoordDimension = opt.stackedCoordDimension;\n      var dimensionDefineList;\n      var schema;\n      var store;\n\n      if (isLegacyDimensionsInput(dimensionsInput)) {\n        dimensionDefineList = dimensionsInput;\n      } else {\n        schema = dimensionsInput.schema;\n        dimensionDefineList = schema.dimensions;\n        store = dimensionsInput.store;\n      } // Compatibal: when `stack` is set as '', do not stack.\n\n\n      var mayStack = !!(seriesModel && seriesModel.get('stack'));\n      var stackedByDimInfo;\n      var stackedDimInfo;\n      var stackResultDimension;\n      var stackedOverDimension;\n      each(dimensionDefineList, function (dimensionInfo, index) {\n        if (isString(dimensionInfo)) {\n          dimensionDefineList[index] = dimensionInfo = {\n            name: dimensionInfo\n          };\n        }\n\n        if (mayStack && !dimensionInfo.isExtraCoord) {\n          // Find the first ordinal dimension as the stackedByDimInfo.\n          if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) {\n            stackedByDimInfo = dimensionInfo;\n          } // Find the first stackable dimension as the stackedDimInfo.\n\n\n          if (!stackedDimInfo && dimensionInfo.type !== 'ordinal' && dimensionInfo.type !== 'time' && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)) {\n            stackedDimInfo = dimensionInfo;\n          }\n        }\n      });\n\n      if (stackedDimInfo && !byIndex && !stackedByDimInfo) {\n        // Compatible with previous design, value axis (time axis) only stack by index.\n        // It may make sense if the user provides elaborately constructed data.\n        byIndex = true;\n      } // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`.\n      // That put stack logic in List is for using conveniently in echarts extensions, but it\n      // might not be a good way.\n\n\n      if (stackedDimInfo) {\n        // Use a weird name that not duplicated with other names.\n        // Also need to use seriesModel.id as postfix because different\n        // series may share same data store. The stack dimension needs to be distinguished.\n        stackResultDimension = '__\\0ecstackresult_' + seriesModel.id;\n        stackedOverDimension = '__\\0ecstackedover_' + seriesModel.id; // Create inverted index to fast query index by value.\n\n        if (stackedByDimInfo) {\n          stackedByDimInfo.createInvertedIndices = true;\n        }\n\n        var stackedDimCoordDim_1 = stackedDimInfo.coordDim;\n        var stackedDimType = stackedDimInfo.type;\n        var stackedDimCoordIndex_1 = 0;\n        each(dimensionDefineList, function (dimensionInfo) {\n          if (dimensionInfo.coordDim === stackedDimCoordDim_1) {\n            stackedDimCoordIndex_1++;\n          }\n        });\n        var stackedOverDimensionDefine = {\n          name: stackResultDimension,\n          coordDim: stackedDimCoordDim_1,\n          coordDimIndex: stackedDimCoordIndex_1,\n          type: stackedDimType,\n          isExtraCoord: true,\n          isCalculationCoord: true,\n          storeDimIndex: dimensionDefineList.length\n        };\n        var stackResultDimensionDefine = {\n          name: stackedOverDimension,\n          // This dimension contains stack base (generally, 0), so do not set it as\n          // `stackedDimCoordDim` to avoid extent calculation, consider log scale.\n          coordDim: stackedOverDimension,\n          coordDimIndex: stackedDimCoordIndex_1 + 1,\n          type: stackedDimType,\n          isExtraCoord: true,\n          isCalculationCoord: true,\n          storeDimIndex: dimensionDefineList.length + 1\n        };\n\n        if (schema) {\n          if (store) {\n            stackedOverDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackedOverDimension, stackedDimType);\n            stackResultDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackResultDimension, stackedDimType);\n          }\n\n          schema.appendCalculationDimension(stackedOverDimensionDefine);\n          schema.appendCalculationDimension(stackResultDimensionDefine);\n        } else {\n          dimensionDefineList.push(stackedOverDimensionDefine);\n          dimensionDefineList.push(stackResultDimensionDefine);\n        }\n      }\n\n      return {\n        stackedDimension: stackedDimInfo && stackedDimInfo.name,\n        stackedByDimension: stackedByDimInfo && stackedByDimInfo.name,\n        isStackedByIndex: byIndex,\n        stackedOverDimension: stackedOverDimension,\n        stackResultDimension: stackResultDimension\n      };\n    }\n\n    function isLegacyDimensionsInput(dimensionsInput) {\n      return !isSeriesDataSchema(dimensionsInput.schema);\n    }\n\n    function isDimensionStacked(data, stackedDim) {\n      // Each single series only maps to one pair of axis. So we do not need to\n      // check stackByDim, whatever stacked by a dimension or stacked by index.\n      return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension');\n    }\n    function getStackedDimension(data, targetDim) {\n      return isDimensionStacked(data, targetDim) ? data.getCalculationInfo('stackResultDimension') : targetDim;\n    }\n\n    function getCoordSysDimDefs(seriesModel, coordSysInfo) {\n      var coordSysName = seriesModel.get('coordinateSystem');\n      var registeredCoordSys = CoordinateSystemManager.get(coordSysName);\n      var coordSysDimDefs;\n\n      if (coordSysInfo && coordSysInfo.coordSysDims) {\n        coordSysDimDefs = map(coordSysInfo.coordSysDims, function (dim) {\n          var dimInfo = {\n            name: dim\n          };\n          var axisModel = coordSysInfo.axisMap.get(dim);\n\n          if (axisModel) {\n            var axisType = axisModel.get('type');\n            dimInfo.type = getDimensionTypeByAxis(axisType);\n          }\n\n          return dimInfo;\n        });\n      }\n\n      if (!coordSysDimDefs) {\n        // Get dimensions from registered coordinate system\n        coordSysDimDefs = registeredCoordSys && (registeredCoordSys.getDimensionsInfo ? registeredCoordSys.getDimensionsInfo() : registeredCoordSys.dimensions.slice()) || ['x', 'y'];\n      }\n\n      return coordSysDimDefs;\n    }\n\n    function injectOrdinalMeta(dimInfoList, createInvertedIndices, coordSysInfo) {\n      var firstCategoryDimIndex;\n      var hasNameEncode;\n      coordSysInfo && each(dimInfoList, function (dimInfo, dimIndex) {\n        var coordDim = dimInfo.coordDim;\n        var categoryAxisModel = coordSysInfo.categoryAxisMap.get(coordDim);\n\n        if (categoryAxisModel) {\n          if (firstCategoryDimIndex == null) {\n            firstCategoryDimIndex = dimIndex;\n          }\n\n          dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta();\n\n          if (createInvertedIndices) {\n            dimInfo.createInvertedIndices = true;\n          }\n        }\n\n        if (dimInfo.otherDims.itemName != null) {\n          hasNameEncode = true;\n        }\n      });\n\n      if (!hasNameEncode && firstCategoryDimIndex != null) {\n        dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0;\n      }\n\n      return firstCategoryDimIndex;\n    }\n    /**\n     * Caution: there are side effects to `sourceManager` in this method.\n     * Should better only be called in `Series['getInitialData']`.\n     */\n\n\n    function createSeriesData(sourceRaw, seriesModel, opt) {\n      opt = opt || {};\n      var sourceManager = seriesModel.getSourceManager();\n      var source;\n      var isOriginalSource = false;\n\n      if (sourceRaw) {\n        isOriginalSource = true;\n        source = createSourceFromSeriesDataOption(sourceRaw);\n      } else {\n        source = sourceManager.getSource(); // Is series.data. not dataset.\n\n        isOriginalSource = source.sourceFormat === SOURCE_FORMAT_ORIGINAL;\n      }\n\n      var coordSysInfo = getCoordSysInfoBySeries(seriesModel);\n      var coordSysDimDefs = getCoordSysDimDefs(seriesModel, coordSysInfo);\n      var useEncodeDefaulter = opt.useEncodeDefaulter;\n      var encodeDefaulter = isFunction(useEncodeDefaulter) ? useEncodeDefaulter : useEncodeDefaulter ? curry(makeSeriesEncodeForAxisCoordSys, coordSysDimDefs, seriesModel) : null;\n      var createDimensionOptions = {\n        coordDimensions: coordSysDimDefs,\n        generateCoord: opt.generateCoord,\n        encodeDefine: seriesModel.getEncode(),\n        encodeDefaulter: encodeDefaulter,\n        canOmitUnusedDimensions: !isOriginalSource\n      };\n      var schema = prepareSeriesDataSchema(source, createDimensionOptions);\n      var firstCategoryDimIndex = injectOrdinalMeta(schema.dimensions, opt.createInvertedIndices, coordSysInfo);\n      var store = !isOriginalSource ? sourceManager.getSharedDataStore(schema) : null;\n      var stackCalculationInfo = enableDataStack(seriesModel, {\n        schema: schema,\n        store: store\n      });\n      var data = new SeriesData(schema, seriesModel);\n      data.setCalculationInfo(stackCalculationInfo);\n      var dimValueGetter = firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source) ? function (itemOpt, dimName, dataIndex, dimIndex) {\n        // Use dataIndex as ordinal value in categoryAxis\n        return dimIndex === firstCategoryDimIndex ? dataIndex : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);\n      } : null;\n      data.hasItemOption = false;\n      data.initData( // Try to reuse the data store in sourceManager if using dataset.\n      isOriginalSource ? source : store, null, dimValueGetter);\n      return data;\n    }\n\n    function isNeedCompleteOrdinalData(source) {\n      if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var sampleItem = firstDataNotNull(source.data || []);\n        return !isArray(getDataItemValue(sampleItem));\n      }\n    }\n\n    function firstDataNotNull(arr) {\n      var i = 0;\n\n      while (i < arr.length && arr[i] == null) {\n        i++;\n      }\n\n      return arr[i];\n    }\n\n    var Scale =\n    /** @class */\n    function () {\n      function Scale(setting) {\n        this._setting = setting || {};\n        this._extent = [Infinity, -Infinity];\n      }\n\n      Scale.prototype.getSetting = function (name) {\n        return this._setting[name];\n      };\n      /**\n       * Set extent from data\n       */\n\n\n      Scale.prototype.unionExtent = function (other) {\n        var extent = this._extent;\n        other[0] < extent[0] && (extent[0] = other[0]);\n        other[1] > extent[1] && (extent[1] = other[1]); // not setExtent because in log axis it may transformed to power\n        // this.setExtent(extent[0], extent[1]);\n      };\n      /**\n       * Set extent from data\n       */\n\n\n      Scale.prototype.unionExtentFromData = function (data, dim) {\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * Get extent\n       *\n       * Extent is always in increase order.\n       */\n\n\n      Scale.prototype.getExtent = function () {\n        return this._extent.slice();\n      };\n      /**\n       * Set extent\n       */\n\n\n      Scale.prototype.setExtent = function (start, end) {\n        var thisExtent = this._extent;\n\n        if (!isNaN(start)) {\n          thisExtent[0] = start;\n        }\n\n        if (!isNaN(end)) {\n          thisExtent[1] = end;\n        }\n      };\n      /**\n       * If value is in extent range\n       */\n\n\n      Scale.prototype.isInExtentRange = function (value) {\n        return this._extent[0] <= value && this._extent[1] >= value;\n      };\n      /**\n       * When axis extent depends on data and no data exists,\n       * axis ticks should not be drawn, which is named 'blank'.\n       */\n\n\n      Scale.prototype.isBlank = function () {\n        return this._isBlank;\n      };\n      /**\n       * When axis extent depends on data and no data exists,\n       * axis ticks should not be drawn, which is named 'blank'.\n       */\n\n\n      Scale.prototype.setBlank = function (isBlank) {\n        this._isBlank = isBlank;\n      };\n\n      return Scale;\n    }();\n\n    enableClassManagement(Scale);\n\n    var uidBase = 0;\n\n    var OrdinalMeta =\n    /** @class */\n    function () {\n      function OrdinalMeta(opt) {\n        this.categories = opt.categories || [];\n        this._needCollect = opt.needCollect;\n        this._deduplication = opt.deduplication;\n        this.uid = ++uidBase;\n      }\n\n      OrdinalMeta.createByAxisModel = function (axisModel) {\n        var option = axisModel.option;\n        var data = option.data;\n        var categories = data && map(data, getName);\n        return new OrdinalMeta({\n          categories: categories,\n          needCollect: !categories,\n          // deduplication is default in axis.\n          deduplication: option.dedplication !== false\n        });\n      };\n\n      OrdinalMeta.prototype.getOrdinal = function (category) {\n        // @ts-ignore\n        return this._getOrCreateMap().get(category);\n      };\n      /**\n       * @return The ordinal. If not found, return NaN.\n       */\n\n\n      OrdinalMeta.prototype.parseAndCollect = function (category) {\n        var index;\n        var needCollect = this._needCollect; // The value of category dim can be the index of the given category set.\n        // This feature is only supported when !needCollect, because we should\n        // consider a common case: a value is 2017, which is a number but is\n        // expected to be tread as a category. This case usually happen in dataset,\n        // where it happent to be no need of the index feature.\n\n        if (!isString(category) && !needCollect) {\n          return category;\n        } // Optimize for the scenario:\n        // category is ['2012-01-01', '2012-01-02', ...], where the input\n        // data has been ensured not duplicate and is large data.\n        // Notice, if a dataset dimension provide categroies, usually echarts\n        // should remove duplication except user tell echarts dont do that\n        // (set axis.deduplication = false), because echarts do not know whether\n        // the values in the category dimension has duplication (consider the\n        // parallel-aqi example)\n\n\n        if (needCollect && !this._deduplication) {\n          index = this.categories.length;\n          this.categories[index] = category;\n          return index;\n        }\n\n        var map = this._getOrCreateMap(); // @ts-ignore\n\n\n        index = map.get(category);\n\n        if (index == null) {\n          if (needCollect) {\n            index = this.categories.length;\n            this.categories[index] = category; // @ts-ignore\n\n            map.set(category, index);\n          } else {\n            index = NaN;\n          }\n        }\n\n        return index;\n      }; // Consider big data, do not create map until needed.\n\n\n      OrdinalMeta.prototype._getOrCreateMap = function () {\n        return this._map || (this._map = createHashMap(this.categories));\n      };\n\n      return OrdinalMeta;\n    }();\n\n    function getName(obj) {\n      if (isObject(obj) && obj.value != null) {\n        return obj.value;\n      } else {\n        return obj + '';\n      }\n    }\n\n    function isValueNice(val) {\n      var exp10 = Math.pow(10, quantityExponent(Math.abs(val)));\n      var f = Math.abs(val / exp10);\n      return f === 0 || f === 1 || f === 2 || f === 3 || f === 5;\n    }\n    function isIntervalOrLogScale(scale) {\n      return scale.type === 'interval' || scale.type === 'log';\n    }\n    /**\n     * @param extent Both extent[0] and extent[1] should be valid number.\n     *               Should be extent[0] < extent[1].\n     * @param splitNumber splitNumber should be >= 1.\n     */\n\n    function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {\n      var result = {};\n      var span = extent[1] - extent[0];\n      var interval = result.interval = nice(span / splitNumber, true);\n\n      if (minInterval != null && interval < minInterval) {\n        interval = result.interval = minInterval;\n      }\n\n      if (maxInterval != null && interval > maxInterval) {\n        interval = result.interval = maxInterval;\n      } // Tow more digital for tick.\n\n\n      var precision = result.intervalPrecision = getIntervalPrecision(interval); // Niced extent inside original extent\n\n      var niceTickExtent = result.niceTickExtent = [round(Math.ceil(extent[0] / interval) * interval, precision), round(Math.floor(extent[1] / interval) * interval, precision)];\n      fixExtent(niceTickExtent, extent);\n      return result;\n    }\n    function increaseInterval(interval) {\n      var exp10 = Math.pow(10, quantityExponent(interval)); // Increase interval\n\n      var f = interval / exp10;\n\n      if (!f) {\n        f = 1;\n      } else if (f === 2) {\n        f = 3;\n      } else if (f === 3) {\n        f = 5;\n      } else {\n        // f is 1 or 5\n        f *= 2;\n      }\n\n      return round(f * exp10);\n    }\n    /**\n     * @return interval precision\n     */\n\n    function getIntervalPrecision(interval) {\n      // Tow more digital for tick.\n      return getPrecision(interval) + 2;\n    }\n\n    function clamp(niceTickExtent, idx, extent) {\n      niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);\n    } // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.\n\n\n    function fixExtent(niceTickExtent, extent) {\n      !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);\n      !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);\n      clamp(niceTickExtent, 0, extent);\n      clamp(niceTickExtent, 1, extent);\n\n      if (niceTickExtent[0] > niceTickExtent[1]) {\n        niceTickExtent[0] = niceTickExtent[1];\n      }\n    }\n    function contain$1(val, extent) {\n      return val >= extent[0] && val <= extent[1];\n    }\n    function normalize$1(val, extent) {\n      if (extent[1] === extent[0]) {\n        return 0.5;\n      }\n\n      return (val - extent[0]) / (extent[1] - extent[0]);\n    }\n    function scale$2(val, extent) {\n      return val * (extent[1] - extent[0]) + extent[0];\n    }\n\n    var OrdinalScale =\n    /** @class */\n    function (_super) {\n      __extends(OrdinalScale, _super);\n\n      function OrdinalScale(setting) {\n        var _this = _super.call(this, setting) || this;\n\n        _this.type = 'ordinal';\n\n        var ordinalMeta = _this.getSetting('ordinalMeta'); // Caution: Should not use instanceof, consider ec-extensions using\n        // import approach to get OrdinalMeta class.\n\n\n        if (!ordinalMeta) {\n          ordinalMeta = new OrdinalMeta({});\n        }\n\n        if (isArray(ordinalMeta)) {\n          ordinalMeta = new OrdinalMeta({\n            categories: map(ordinalMeta, function (item) {\n              return isObject(item) ? item.value : item;\n            })\n          });\n        }\n\n        _this._ordinalMeta = ordinalMeta;\n        _this._extent = _this.getSetting('extent') || [0, ordinalMeta.categories.length - 1];\n        return _this;\n      }\n\n      OrdinalScale.prototype.parse = function (val) {\n        // Caution: Math.round(null) will return `0` rather than `NaN`\n        if (val == null) {\n          return NaN;\n        }\n\n        return isString(val) ? this._ordinalMeta.getOrdinal(val) // val might be float.\n        : Math.round(val);\n      };\n\n      OrdinalScale.prototype.contain = function (rank) {\n        rank = this.parse(rank);\n        return contain$1(rank, this._extent) && this._ordinalMeta.categories[rank] != null;\n      };\n      /**\n       * Normalize given rank or name to linear [0, 1]\n       * @param val raw ordinal number.\n       * @return normalized value in [0, 1].\n       */\n\n\n      OrdinalScale.prototype.normalize = function (val) {\n        val = this._getTickNumber(this.parse(val));\n        return normalize$1(val, this._extent);\n      };\n      /**\n       * @param val normalized value in [0, 1].\n       * @return raw ordinal number.\n       */\n\n\n      OrdinalScale.prototype.scale = function (val) {\n        val = Math.round(scale$2(val, this._extent));\n        return this.getRawOrdinalNumber(val);\n      };\n\n      OrdinalScale.prototype.getTicks = function () {\n        var ticks = [];\n        var extent = this._extent;\n        var rank = extent[0];\n\n        while (rank <= extent[1]) {\n          ticks.push({\n            value: rank\n          });\n          rank++;\n        }\n\n        return ticks;\n      };\n\n      OrdinalScale.prototype.getMinorTicks = function (splitNumber) {\n        // Not support.\n        return;\n      };\n      /**\n       * @see `Ordinal['_ordinalNumbersByTick']`\n       */\n\n\n      OrdinalScale.prototype.setSortInfo = function (info) {\n        if (info == null) {\n          this._ordinalNumbersByTick = this._ticksByOrdinalNumber = null;\n          return;\n        }\n\n        var infoOrdinalNumbers = info.ordinalNumbers;\n        var ordinalsByTick = this._ordinalNumbersByTick = [];\n        var ticksByOrdinal = this._ticksByOrdinalNumber = []; // Unnecessary support negative tick in `realtimeSort`.\n\n        var tickNum = 0;\n        var allCategoryLen = this._ordinalMeta.categories.length;\n\n        for (var len = Math.min(allCategoryLen, infoOrdinalNumbers.length); tickNum < len; ++tickNum) {\n          var ordinalNumber = infoOrdinalNumbers[tickNum];\n          ordinalsByTick[tickNum] = ordinalNumber;\n          ticksByOrdinal[ordinalNumber] = tickNum;\n        } // Handle that `series.data` only covers part of the `axis.category.data`.\n\n\n        var unusedOrdinal = 0;\n\n        for (; tickNum < allCategoryLen; ++tickNum) {\n          while (ticksByOrdinal[unusedOrdinal] != null) {\n            unusedOrdinal++;\n          }\n          ordinalsByTick.push(unusedOrdinal);\n          ticksByOrdinal[unusedOrdinal] = tickNum;\n        }\n      };\n\n      OrdinalScale.prototype._getTickNumber = function (ordinal) {\n        var ticksByOrdinalNumber = this._ticksByOrdinalNumber; // also support ordinal out of range of `ordinalMeta.categories.length`,\n        // where ordinal numbers are used as tick value directly.\n\n        return ticksByOrdinalNumber && ordinal >= 0 && ordinal < ticksByOrdinalNumber.length ? ticksByOrdinalNumber[ordinal] : ordinal;\n      };\n      /**\n       * @usage\n       * ```js\n       * const ordinalNumber = ordinalScale.getRawOrdinalNumber(tickVal);\n       *\n       * // case0\n       * const rawOrdinalValue = axisModel.getCategories()[ordinalNumber];\n       * // case1\n       * const rawOrdinalValue = this._ordinalMeta.categories[ordinalNumber];\n       * // case2\n       * const coord = axis.dataToCoord(ordinalNumber);\n       * ```\n       *\n       * @param {OrdinalNumber} tickNumber index of display\n       */\n\n\n      OrdinalScale.prototype.getRawOrdinalNumber = function (tickNumber) {\n        var ordinalNumbersByTick = this._ordinalNumbersByTick; // tickNumber may be out of range, e.g., when axis max is larger than `ordinalMeta.categories.length`.,\n        // where ordinal numbers are used as tick value directly.\n\n        return ordinalNumbersByTick && tickNumber >= 0 && tickNumber < ordinalNumbersByTick.length ? ordinalNumbersByTick[tickNumber] : tickNumber;\n      };\n      /**\n       * Get item on tick\n       */\n\n\n      OrdinalScale.prototype.getLabel = function (tick) {\n        if (!this.isBlank()) {\n          var ordinalNumber = this.getRawOrdinalNumber(tick.value);\n          var cateogry = this._ordinalMeta.categories[ordinalNumber]; // Note that if no data, ordinalMeta.categories is an empty array.\n          // Return empty if it's not exist.\n\n          return cateogry == null ? '' : cateogry + '';\n        }\n      };\n\n      OrdinalScale.prototype.count = function () {\n        return this._extent[1] - this._extent[0] + 1;\n      };\n\n      OrdinalScale.prototype.unionExtentFromData = function (data, dim) {\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * @override\n       * If value is in extent range\n       */\n\n\n      OrdinalScale.prototype.isInExtentRange = function (value) {\n        value = this._getTickNumber(value);\n        return this._extent[0] <= value && this._extent[1] >= value;\n      };\n\n      OrdinalScale.prototype.getOrdinalMeta = function () {\n        return this._ordinalMeta;\n      };\n\n      OrdinalScale.prototype.calcNiceTicks = function () {};\n\n      OrdinalScale.prototype.calcNiceExtent = function () {};\n\n      OrdinalScale.type = 'ordinal';\n      return OrdinalScale;\n    }(Scale);\n\n    Scale.registerClass(OrdinalScale);\n\n    var roundNumber = round;\n\n    var IntervalScale =\n    /** @class */\n    function (_super) {\n      __extends(IntervalScale, _super);\n\n      function IntervalScale() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'interval'; // Step is calculated in adjustExtent.\n\n        _this._interval = 0;\n        _this._intervalPrecision = 2;\n        return _this;\n      }\n\n      IntervalScale.prototype.parse = function (val) {\n        return val;\n      };\n\n      IntervalScale.prototype.contain = function (val) {\n        return contain$1(val, this._extent);\n      };\n\n      IntervalScale.prototype.normalize = function (val) {\n        return normalize$1(val, this._extent);\n      };\n\n      IntervalScale.prototype.scale = function (val) {\n        return scale$2(val, this._extent);\n      };\n\n      IntervalScale.prototype.setExtent = function (start, end) {\n        var thisExtent = this._extent; // start,end may be a Number like '25',so...\n\n        if (!isNaN(start)) {\n          thisExtent[0] = parseFloat(start);\n        }\n\n        if (!isNaN(end)) {\n          thisExtent[1] = parseFloat(end);\n        }\n      };\n\n      IntervalScale.prototype.unionExtent = function (other) {\n        var extent = this._extent;\n        other[0] < extent[0] && (extent[0] = other[0]);\n        other[1] > extent[1] && (extent[1] = other[1]); // unionExtent may called by it's sub classes\n\n        this.setExtent(extent[0], extent[1]);\n      };\n\n      IntervalScale.prototype.getInterval = function () {\n        return this._interval;\n      };\n\n      IntervalScale.prototype.setInterval = function (interval) {\n        this._interval = interval; // Dropped auto calculated niceExtent and use user-set extent.\n        // We assume user wants to set both interval, min, max to get a better result.\n\n        this._niceExtent = this._extent.slice();\n        this._intervalPrecision = getIntervalPrecision(interval);\n      };\n      /**\n       * @param expandToNicedExtent Whether expand the ticks to niced extent.\n       */\n\n\n      IntervalScale.prototype.getTicks = function (expandToNicedExtent) {\n        var interval = this._interval;\n        var extent = this._extent;\n        var niceTickExtent = this._niceExtent;\n        var intervalPrecision = this._intervalPrecision;\n        var ticks = []; // If interval is 0, return [];\n\n        if (!interval) {\n          return ticks;\n        } // Consider this case: using dataZoom toolbox, zoom and zoom.\n\n\n        var safeLimit = 10000;\n\n        if (extent[0] < niceTickExtent[0]) {\n          if (expandToNicedExtent) {\n            ticks.push({\n              value: roundNumber(niceTickExtent[0] - interval, intervalPrecision)\n            });\n          } else {\n            ticks.push({\n              value: extent[0]\n            });\n          }\n        }\n\n        var tick = niceTickExtent[0];\n\n        while (tick <= niceTickExtent[1]) {\n          ticks.push({\n            value: tick\n          }); // Avoid rounding error\n\n          tick = roundNumber(tick + interval, intervalPrecision);\n\n          if (tick === ticks[ticks.length - 1].value) {\n            // Consider out of safe float point, e.g.,\n            // -3711126.9907707 + 2e-10 === -3711126.9907707\n            break;\n          }\n\n          if (ticks.length > safeLimit) {\n            return [];\n          }\n        } // Consider this case: the last item of ticks is smaller\n        // than niceTickExtent[1] and niceTickExtent[1] === extent[1].\n\n\n        var lastNiceTick = ticks.length ? ticks[ticks.length - 1].value : niceTickExtent[1];\n\n        if (extent[1] > lastNiceTick) {\n          if (expandToNicedExtent) {\n            ticks.push({\n              value: roundNumber(lastNiceTick + interval, intervalPrecision)\n            });\n          } else {\n            ticks.push({\n              value: extent[1]\n            });\n          }\n        }\n\n        return ticks;\n      };\n\n      IntervalScale.prototype.getMinorTicks = function (splitNumber) {\n        var ticks = this.getTicks(true);\n        var minorTicks = [];\n        var extent = this.getExtent();\n\n        for (var i = 1; i < ticks.length; i++) {\n          var nextTick = ticks[i];\n          var prevTick = ticks[i - 1];\n          var count = 0;\n          var minorTicksGroup = [];\n          var interval = nextTick.value - prevTick.value;\n          var minorInterval = interval / splitNumber;\n\n          while (count < splitNumber - 1) {\n            var minorTick = roundNumber(prevTick.value + (count + 1) * minorInterval); // For the first and last interval. The count may be less than splitNumber.\n\n            if (minorTick > extent[0] && minorTick < extent[1]) {\n              minorTicksGroup.push(minorTick);\n            }\n\n            count++;\n          }\n\n          minorTicks.push(minorTicksGroup);\n        }\n\n        return minorTicks;\n      };\n      /**\n       * @param opt.precision If 'auto', use nice presision.\n       * @param opt.pad returns 1.50 but not 1.5 if precision is 2.\n       */\n\n\n      IntervalScale.prototype.getLabel = function (data, opt) {\n        if (data == null) {\n          return '';\n        }\n\n        var precision = opt && opt.precision;\n\n        if (precision == null) {\n          precision = getPrecision(data.value) || 0;\n        } else if (precision === 'auto') {\n          // Should be more precise then tick.\n          precision = this._intervalPrecision;\n        } // (1) If `precision` is set, 12.005 should be display as '12.00500'.\n        // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.\n\n\n        var dataNum = roundNumber(data.value, precision, true);\n        return addCommas(dataNum);\n      };\n      /**\n       * @param splitNumber By default `5`.\n       */\n\n\n      IntervalScale.prototype.calcNiceTicks = function (splitNumber, minInterval, maxInterval) {\n        splitNumber = splitNumber || 5;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n\n        if (!isFinite(span)) {\n          return;\n        } // User may set axis min 0 and data are all negative\n        // FIXME If it needs to reverse ?\n\n\n        if (span < 0) {\n          span = -span;\n          extent.reverse();\n        }\n\n        var result = intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval);\n        this._intervalPrecision = result.intervalPrecision;\n        this._interval = result.interval;\n        this._niceExtent = result.niceTickExtent;\n      };\n\n      IntervalScale.prototype.calcNiceExtent = function (opt) {\n        var extent = this._extent; // If extent start and end are same, expand them\n\n        if (extent[0] === extent[1]) {\n          if (extent[0] !== 0) {\n            // Expand extent\n            // Note that extents can be both negative. See #13154\n            var expandSize = Math.abs(extent[0]); // In the fowllowing case\n            //      Axis has been fixed max 100\n            //      Plus data are all 100 and axis extent are [100, 100].\n            // Extend to the both side will cause expanded max is larger than fixed max.\n            // So only expand to the smaller side.\n\n            if (!opt.fixMax) {\n              extent[1] += expandSize / 2;\n              extent[0] -= expandSize / 2;\n            } else {\n              extent[0] -= expandSize / 2;\n            }\n          } else {\n            extent[1] = 1;\n          }\n        }\n\n        var span = extent[1] - extent[0]; // If there are no data and extent are [Infinity, -Infinity]\n\n        if (!isFinite(span)) {\n          extent[0] = 0;\n          extent[1] = 1;\n        }\n\n        this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // let extent = this._extent;\n\n        var interval = this._interval;\n\n        if (!opt.fixMin) {\n          extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);\n        }\n\n        if (!opt.fixMax) {\n          extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);\n        }\n      };\n\n      IntervalScale.prototype.setNiceExtent = function (min, max) {\n        this._niceExtent = [min, max];\n      };\n\n      IntervalScale.type = 'interval';\n      return IntervalScale;\n    }(Scale);\n\n    Scale.registerClass(IntervalScale);\n\n    /* global Float32Array */\n\n    var supportFloat32Array = typeof Float32Array !== 'undefined';\n    var Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array;\n    function createFloat32Array(arg) {\n      if (isArray(arg)) {\n        // Return self directly if don't support TypedArray.\n        return supportFloat32Array ? new Float32Array(arg) : arg;\n      } // Else is number\n\n\n      return new Float32ArrayCtor(arg);\n    }\n\n    var STACK_PREFIX = '__ec_stack_';\n\n    function getSeriesStackId(seriesModel) {\n      return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;\n    }\n\n    function getAxisKey(axis) {\n      return axis.dim + axis.index;\n    }\n    /**\n     * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined.\n     */\n\n\n    function getLayoutOnAxis(opt) {\n      var params = [];\n      var baseAxis = opt.axis;\n      var axisKey = 'axis0';\n\n      if (baseAxis.type !== 'category') {\n        return;\n      }\n\n      var bandWidth = baseAxis.getBandWidth();\n\n      for (var i = 0; i < opt.count || 0; i++) {\n        params.push(defaults({\n          bandWidth: bandWidth,\n          axisKey: axisKey,\n          stackId: STACK_PREFIX + i\n        }, opt));\n      }\n\n      var widthAndOffsets = doCalBarWidthAndOffset(params);\n      var result = [];\n\n      for (var i = 0; i < opt.count; i++) {\n        var item = widthAndOffsets[axisKey][STACK_PREFIX + i];\n        item.offsetCenter = item.offset + item.width / 2;\n        result.push(item);\n      }\n\n      return result;\n    }\n    function prepareLayoutBarSeries(seriesType, ecModel) {\n      var seriesModels = [];\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        // Check series coordinate, do layout for cartesian2d only\n        if (isOnCartesian(seriesModel)) {\n          seriesModels.push(seriesModel);\n        }\n      });\n      return seriesModels;\n    }\n    /**\n     * Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent\n     * values.\n     * This works for time axes, value axes, and log axes.\n     * For a single time axis, return value is in the form like\n     * {'x_0': [1000000]}.\n     * The value of 1000000 is in milliseconds.\n     */\n\n    function getValueAxesMinGaps(barSeries) {\n      /**\n       * Map from axis.index to values.\n       * For a single time axis, axisValues is in the form like\n       * {'x_0': [1495555200000, 1495641600000, 1495728000000]}.\n       * Items in axisValues[x], e.g. 1495555200000, are time values of all\n       * series.\n       */\n      var axisValues = {};\n      each(barSeries, function (seriesModel) {\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n\n        if (baseAxis.type !== 'time' && baseAxis.type !== 'value') {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var key = baseAxis.dim + '_' + baseAxis.index;\n        var dimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n        var store = data.getStore();\n\n        for (var i = 0, cnt = store.count(); i < cnt; ++i) {\n          var value = store.get(dimIdx, i);\n\n          if (!axisValues[key]) {\n            // No previous data for the axis\n            axisValues[key] = [value];\n          } else {\n            // No value in previous series\n            axisValues[key].push(value);\n          } // Ignore duplicated time values in the same axis\n\n        }\n      });\n      var axisMinGaps = {};\n\n      for (var key in axisValues) {\n        if (axisValues.hasOwnProperty(key)) {\n          var valuesInAxis = axisValues[key];\n\n          if (valuesInAxis) {\n            // Sort axis values into ascending order to calculate gaps\n            valuesInAxis.sort(function (a, b) {\n              return a - b;\n            });\n            var min = null;\n\n            for (var j = 1; j < valuesInAxis.length; ++j) {\n              var delta = valuesInAxis[j] - valuesInAxis[j - 1];\n\n              if (delta > 0) {\n                // Ignore 0 delta because they are of the same axis value\n                min = min === null ? delta : Math.min(min, delta);\n              }\n            } // Set to null if only have one data\n\n\n            axisMinGaps[key] = min;\n          }\n        }\n      }\n\n      return axisMinGaps;\n    }\n\n    function makeColumnLayout(barSeries) {\n      var axisMinGaps = getValueAxesMinGaps(barSeries);\n      var seriesInfoList = [];\n      each(barSeries, function (seriesModel) {\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n        var axisExtent = baseAxis.getExtent();\n        var bandWidth;\n\n        if (baseAxis.type === 'category') {\n          bandWidth = baseAxis.getBandWidth();\n        } else if (baseAxis.type === 'value' || baseAxis.type === 'time') {\n          var key = baseAxis.dim + '_' + baseAxis.index;\n          var minGap = axisMinGaps[key];\n          var extentSpan = Math.abs(axisExtent[1] - axisExtent[0]);\n          var scale = baseAxis.scale.getExtent();\n          var scaleSpan = Math.abs(scale[1] - scale[0]);\n          bandWidth = minGap ? extentSpan / scaleSpan * minGap : extentSpan; // When there is only one data value\n        } else {\n          var data = seriesModel.getData();\n          bandWidth = Math.abs(axisExtent[1] - axisExtent[0]) / data.count();\n        }\n\n        var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth);\n        var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth);\n        var barMinWidth = parsePercent$1( // barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis,\n        // the auto-calculated bar width might be less than 0.5 / 1.\n        seriesModel.get('barMinWidth') || (isInLargeMode(seriesModel) ? 0.5 : 1), bandWidth);\n        var barGap = seriesModel.get('barGap');\n        var barCategoryGap = seriesModel.get('barCategoryGap');\n        seriesInfoList.push({\n          bandWidth: bandWidth,\n          barWidth: barWidth,\n          barMaxWidth: barMaxWidth,\n          barMinWidth: barMinWidth,\n          barGap: barGap,\n          barCategoryGap: barCategoryGap,\n          axisKey: getAxisKey(baseAxis),\n          stackId: getSeriesStackId(seriesModel)\n        });\n      });\n      return doCalBarWidthAndOffset(seriesInfoList);\n    }\n\n    function doCalBarWidthAndOffset(seriesInfoList) {\n      // Columns info on each category axis. Key is cartesian name\n      var columnsMap = {};\n      each(seriesInfoList, function (seriesInfo, idx) {\n        var axisKey = seriesInfo.axisKey;\n        var bandWidth = seriesInfo.bandWidth;\n        var columnsOnAxis = columnsMap[axisKey] || {\n          bandWidth: bandWidth,\n          remainedWidth: bandWidth,\n          autoWidthCount: 0,\n          categoryGap: null,\n          gap: '20%',\n          stacks: {}\n        };\n        var stacks = columnsOnAxis.stacks;\n        columnsMap[axisKey] = columnsOnAxis;\n        var stackId = seriesInfo.stackId;\n\n        if (!stacks[stackId]) {\n          columnsOnAxis.autoWidthCount++;\n        }\n\n        stacks[stackId] = stacks[stackId] || {\n          width: 0,\n          maxWidth: 0\n        }; // Caution: In a single coordinate system, these barGrid attributes\n        // will be shared by series. Consider that they have default values,\n        // only the attributes set on the last series will work.\n        // Do not change this fact unless there will be a break change.\n\n        var barWidth = seriesInfo.barWidth;\n\n        if (barWidth && !stacks[stackId].width) {\n          // See #6312, do not restrict width.\n          stacks[stackId].width = barWidth;\n          barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);\n          columnsOnAxis.remainedWidth -= barWidth;\n        }\n\n        var barMaxWidth = seriesInfo.barMaxWidth;\n        barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);\n        var barMinWidth = seriesInfo.barMinWidth;\n        barMinWidth && (stacks[stackId].minWidth = barMinWidth);\n        var barGap = seriesInfo.barGap;\n        barGap != null && (columnsOnAxis.gap = barGap);\n        var barCategoryGap = seriesInfo.barCategoryGap;\n        barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap);\n      });\n      var result = {};\n      each(columnsMap, function (columnsOnAxis, coordSysName) {\n        result[coordSysName] = {};\n        var stacks = columnsOnAxis.stacks;\n        var bandWidth = columnsOnAxis.bandWidth;\n        var categoryGapPercent = columnsOnAxis.categoryGap;\n\n        if (categoryGapPercent == null) {\n          var columnCount = keys(stacks).length; // More columns in one group\n          // the spaces between group is smaller. Or the column will be too thin.\n\n          categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%';\n        }\n\n        var categoryGap = parsePercent$1(categoryGapPercent, bandWidth);\n        var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);\n        var remainedWidth = columnsOnAxis.remainedWidth;\n        var autoWidthCount = columnsOnAxis.autoWidthCount;\n        var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0); // Find if any auto calculated bar exceeded maxBarWidth\n\n        each(stacks, function (column) {\n          var maxWidth = column.maxWidth;\n          var minWidth = column.minWidth;\n\n          if (!column.width) {\n            var finalWidth = autoWidth;\n\n            if (maxWidth && maxWidth < finalWidth) {\n              finalWidth = Math.min(maxWidth, remainedWidth);\n            } // `minWidth` has higher priority. `minWidth` decide that whether the\n            // bar is able to be visible. So `minWidth` should not be restricted\n            // by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In\n            // the extreme cases for `value` axis, bars are allowed to overlap\n            // with each other if `minWidth` specified.\n\n\n            if (minWidth && minWidth > finalWidth) {\n              finalWidth = minWidth;\n            }\n\n            if (finalWidth !== autoWidth) {\n              column.width = finalWidth;\n              remainedWidth -= finalWidth + barGapPercent * finalWidth;\n              autoWidthCount--;\n            }\n          } else {\n            // `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as\n            // CSS does. Because barWidth can be a percent value, where\n            // `barMaxWidth` can be used to restrict the final width.\n            var finalWidth = column.width;\n\n            if (maxWidth) {\n              finalWidth = Math.min(finalWidth, maxWidth);\n            } // `minWidth` has higher priority, as described above\n\n\n            if (minWidth) {\n              finalWidth = Math.max(finalWidth, minWidth);\n            }\n\n            column.width = finalWidth;\n            remainedWidth -= finalWidth + barGapPercent * finalWidth;\n            autoWidthCount--;\n          }\n        }); // Recalculate width again\n\n        autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0);\n        var widthSum = 0;\n        var lastColumn;\n        each(stacks, function (column, idx) {\n          if (!column.width) {\n            column.width = autoWidth;\n          }\n\n          lastColumn = column;\n          widthSum += column.width * (1 + barGapPercent);\n        });\n\n        if (lastColumn) {\n          widthSum -= lastColumn.width * barGapPercent;\n        }\n\n        var offset = -widthSum / 2;\n        each(stacks, function (column, stackId) {\n          result[coordSysName][stackId] = result[coordSysName][stackId] || {\n            bandWidth: bandWidth,\n            offset: offset,\n            width: column.width\n          };\n          offset += column.width * (1 + barGapPercent);\n        });\n      });\n      return result;\n    }\n\n    function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) {\n      if (barWidthAndOffset && axis) {\n        var result = barWidthAndOffset[getAxisKey(axis)];\n\n        if (result != null && seriesModel != null) {\n          return result[getSeriesStackId(seriesModel)];\n        }\n\n        return result;\n      }\n    }\n    function layout(seriesType, ecModel) {\n      var seriesModels = prepareLayoutBarSeries(seriesType, ecModel);\n      var barWidthAndOffset = makeColumnLayout(seriesModels);\n      each(seriesModels, function (seriesModel) {\n        var data = seriesModel.getData();\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n        var stackId = getSeriesStackId(seriesModel);\n        var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];\n        var columnOffset = columnLayoutInfo.offset;\n        var columnWidth = columnLayoutInfo.width;\n        data.setLayout({\n          bandWidth: columnLayoutInfo.bandWidth,\n          offset: columnOffset,\n          size: columnWidth\n        });\n      });\n    } // TODO: Do not support stack in large mode yet.\n\n    function createProgressiveLayout(seriesType) {\n      return {\n        seriesType: seriesType,\n        plan: createRenderPlanner(),\n        reset: function (seriesModel) {\n          if (!isOnCartesian(seriesModel)) {\n            return;\n          }\n\n          var data = seriesModel.getData();\n          var cartesian = seriesModel.coordinateSystem;\n          var baseAxis = cartesian.getBaseAxis();\n          var valueAxis = cartesian.getOtherAxis(baseAxis);\n          var valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim));\n          var baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n          var drawBackground = seriesModel.get('showBackground', true);\n          var valueDim = data.mapDimension(valueAxis.dim);\n          var stackResultDim = data.getCalculationInfo('stackResultDimension');\n          var stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries');\n          var isValueAxisH = valueAxis.isHorizontal();\n          var valueAxisStart = getValueAxisStart(baseAxis, valueAxis);\n          var isLarge = isInLargeMode(seriesModel);\n          var barMinHeight = seriesModel.get('barMinHeight') || 0;\n          var stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim); // Layout info.\n\n          var columnWidth = data.getLayout('size');\n          var columnOffset = data.getLayout('offset');\n          return {\n            progress: function (params, data) {\n              var count = params.count;\n              var largePoints = isLarge && createFloat32Array(count * 3);\n              var largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3);\n              var largeDataIndices = isLarge && createFloat32Array(count);\n              var coordLayout = cartesian.master.getRect();\n              var bgSize = isValueAxisH ? coordLayout.width : coordLayout.height;\n              var dataIndex;\n              var store = data.getStore();\n              var idxOffset = 0;\n\n              while ((dataIndex = params.next()) != null) {\n                var value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex);\n                var baseValue = store.get(baseDimIdx, dataIndex);\n                var baseCoord = valueAxisStart;\n                var startValue = void 0; // Because of the barMinHeight, we can not use the value in\n                // stackResultDimension directly.\n\n                if (stacked) {\n                  startValue = +value - store.get(valueDimIdx, dataIndex);\n                }\n\n                var x = void 0;\n                var y = void 0;\n                var width = void 0;\n                var height = void 0;\n\n                if (isValueAxisH) {\n                  var coord = cartesian.dataToPoint([value, baseValue]);\n\n                  if (stacked) {\n                    var startCoord = cartesian.dataToPoint([startValue, baseValue]);\n                    baseCoord = startCoord[0];\n                  }\n\n                  x = baseCoord;\n                  y = coord[1] + columnOffset;\n                  width = coord[0] - baseCoord;\n                  height = columnWidth;\n\n                  if (Math.abs(width) < barMinHeight) {\n                    width = (width < 0 ? -1 : 1) * barMinHeight;\n                  }\n                } else {\n                  var coord = cartesian.dataToPoint([baseValue, value]);\n\n                  if (stacked) {\n                    var startCoord = cartesian.dataToPoint([baseValue, startValue]);\n                    baseCoord = startCoord[1];\n                  }\n\n                  x = coord[0] + columnOffset;\n                  y = baseCoord;\n                  width = columnWidth;\n                  height = coord[1] - baseCoord;\n\n                  if (Math.abs(height) < barMinHeight) {\n                    // Include zero to has a positive bar\n                    height = (height <= 0 ? -1 : 1) * barMinHeight;\n                  }\n                }\n\n                if (!isLarge) {\n                  data.setItemLayout(dataIndex, {\n                    x: x,\n                    y: y,\n                    width: width,\n                    height: height\n                  });\n                } else {\n                  largePoints[idxOffset] = x;\n                  largePoints[idxOffset + 1] = y;\n                  largePoints[idxOffset + 2] = isValueAxisH ? width : height;\n\n                  if (largeBackgroundPoints) {\n                    largeBackgroundPoints[idxOffset] = isValueAxisH ? coordLayout.x : x;\n                    largeBackgroundPoints[idxOffset + 1] = isValueAxisH ? y : coordLayout.y;\n                    largeBackgroundPoints[idxOffset + 2] = bgSize;\n                  }\n\n                  largeDataIndices[dataIndex] = dataIndex;\n                }\n\n                idxOffset += 3;\n              }\n\n              if (isLarge) {\n                data.setLayout({\n                  largePoints: largePoints,\n                  largeDataIndices: largeDataIndices,\n                  largeBackgroundPoints: largeBackgroundPoints,\n                  valueAxisHorizontal: isValueAxisH\n                });\n              }\n            }\n          };\n        }\n      };\n    }\n\n    function isOnCartesian(seriesModel) {\n      return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d';\n    }\n\n    function isInLargeMode(seriesModel) {\n      return seriesModel.pipelineContext && seriesModel.pipelineContext.large;\n    } // See cases in `test/bar-start.html` and `#7412`, `#8747`.\n\n\n    function getValueAxisStart(baseAxis, valueAxis) {\n      return valueAxis.toGlobalCoord(valueAxis.dataToCoord(valueAxis.type === 'log' ? 1 : 0));\n    }\n\n    var bisect = function (a, x, lo, hi) {\n      while (lo < hi) {\n        var mid = lo + hi >>> 1;\n\n        if (a[mid][1] < x) {\n          lo = mid + 1;\n        } else {\n          hi = mid;\n        }\n      }\n\n      return lo;\n    };\n\n    var TimeScale =\n    /** @class */\n    function (_super) {\n      __extends(TimeScale, _super);\n\n      function TimeScale(settings) {\n        var _this = _super.call(this, settings) || this;\n\n        _this.type = 'time';\n        return _this;\n      }\n      /**\n       * Get label is mainly for other components like dataZoom, tooltip.\n       */\n\n\n      TimeScale.prototype.getLabel = function (tick) {\n        var useUTC = this.getSetting('useUTC');\n        return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale'));\n      };\n\n      TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) {\n        var isUTC = this.getSetting('useUTC');\n        var lang = this.getSetting('locale');\n        return leveledFormat(tick, idx, labelFormatter, lang, isUTC);\n      };\n      /**\n       * @override\n       */\n\n\n      TimeScale.prototype.getTicks = function () {\n        var interval = this._interval;\n        var extent = this._extent;\n        var ticks = []; // If interval is 0, return [];\n\n        if (!interval) {\n          return ticks;\n        }\n\n        ticks.push({\n          value: extent[0],\n          level: 0\n        });\n        var useUTC = this.getSetting('useUTC');\n        var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent);\n        ticks = ticks.concat(innerTicks);\n        ticks.push({\n          value: extent[1],\n          level: 0\n        });\n        return ticks;\n      };\n\n      TimeScale.prototype.calcNiceExtent = function (opt) {\n        var extent = this._extent; // If extent start and end are same, expand them\n\n        if (extent[0] === extent[1]) {\n          // Expand extent\n          extent[0] -= ONE_DAY;\n          extent[1] += ONE_DAY;\n        } // If there are no data and extent are [Infinity, -Infinity]\n\n\n        if (extent[1] === -Infinity && extent[0] === Infinity) {\n          var d = new Date();\n          extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());\n          extent[0] = extent[1] - ONE_DAY;\n        }\n\n        this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);\n      };\n\n      TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) {\n        approxTickNum = approxTickNum || 10;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n        this._approxInterval = span / approxTickNum;\n\n        if (minInterval != null && this._approxInterval < minInterval) {\n          this._approxInterval = minInterval;\n        }\n\n        if (maxInterval != null && this._approxInterval > maxInterval) {\n          this._approxInterval = maxInterval;\n        }\n\n        var scaleIntervalsLen = scaleIntervals.length;\n        var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1); // Interval that can be used to calculate ticks\n\n        this._interval = scaleIntervals[idx][1]; // Min level used when picking ticks from top down.\n        // We check one more level to avoid the ticks are to sparse in some case.\n\n        this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0];\n      };\n\n      TimeScale.prototype.parse = function (val) {\n        // val might be float.\n        return isNumber(val) ? val : +parseDate(val);\n      };\n\n      TimeScale.prototype.contain = function (val) {\n        return contain$1(this.parse(val), this._extent);\n      };\n\n      TimeScale.prototype.normalize = function (val) {\n        return normalize$1(this.parse(val), this._extent);\n      };\n\n      TimeScale.prototype.scale = function (val) {\n        return scale$2(val, this._extent);\n      };\n\n      TimeScale.type = 'time';\n      return TimeScale;\n    }(IntervalScale);\n    /**\n     * This implementation was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     */\n\n\n    var scaleIntervals = [// Format                           interval\n    ['second', ONE_SECOND], ['minute', ONE_MINUTE], ['hour', ONE_HOUR], ['quarter-day', ONE_HOUR * 6], ['half-day', ONE_HOUR * 12], ['day', ONE_DAY * 1.2], ['half-week', ONE_DAY * 3.5], ['week', ONE_DAY * 7], ['month', ONE_DAY * 31], ['quarter', ONE_DAY * 95], ['half-year', ONE_YEAR / 2], ['year', ONE_YEAR] // 1Y\n    ];\n\n    function isUnitValueSame(unit, valueA, valueB, isUTC) {\n      var dateA = parseDate(valueA);\n      var dateB = parseDate(valueB);\n\n      var isSame = function (unit) {\n        return getUnitValue(dateA, unit, isUTC) === getUnitValue(dateB, unit, isUTC);\n      };\n\n      var isSameYear = function () {\n        return isSame('year');\n      }; // const isSameHalfYear = () => isSameYear() && isSame('half-year');\n      // const isSameQuater = () => isSameYear() && isSame('quarter');\n\n\n      var isSameMonth = function () {\n        return isSameYear() && isSame('month');\n      };\n\n      var isSameDay = function () {\n        return isSameMonth() && isSame('day');\n      }; // const isSameHalfDay = () => isSameDay() && isSame('half-day');\n\n\n      var isSameHour = function () {\n        return isSameDay() && isSame('hour');\n      };\n\n      var isSameMinute = function () {\n        return isSameHour() && isSame('minute');\n      };\n\n      var isSameSecond = function () {\n        return isSameMinute() && isSame('second');\n      };\n\n      var isSameMilliSecond = function () {\n        return isSameSecond() && isSame('millisecond');\n      };\n\n      switch (unit) {\n        case 'year':\n          return isSameYear();\n\n        case 'month':\n          return isSameMonth();\n\n        case 'day':\n          return isSameDay();\n\n        case 'hour':\n          return isSameHour();\n\n        case 'minute':\n          return isSameMinute();\n\n        case 'second':\n          return isSameSecond();\n\n        case 'millisecond':\n          return isSameMilliSecond();\n      }\n    } // const primaryUnitGetters = {\n    //     year: fullYearGetterName(),\n    //     month: monthGetterName(),\n    //     day: dateGetterName(),\n    //     hour: hoursGetterName(),\n    //     minute: minutesGetterName(),\n    //     second: secondsGetterName(),\n    //     millisecond: millisecondsGetterName()\n    // };\n    // const primaryUnitUTCGetters = {\n    //     year: fullYearGetterName(true),\n    //     month: monthGetterName(true),\n    //     day: dateGetterName(true),\n    //     hour: hoursGetterName(true),\n    //     minute: minutesGetterName(true),\n    //     second: secondsGetterName(true),\n    //     millisecond: millisecondsGetterName(true)\n    // };\n    // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) {\n    //     step = step || 1;\n    //     switch (getPrimaryTimeUnit(unitName)) {\n    //         case 'year':\n    //             date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'month':\n    //             date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'day':\n    //             date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'hour':\n    //             date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'minute':\n    //             date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'second':\n    //             date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'millisecond':\n    //             date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step);\n    //             break;\n    //     }\n    //     return date.getTime();\n    // }\n    // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]];\n    // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]];\n    // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]];\n\n\n    function getDateInterval(approxInterval, daysInMonth) {\n      approxInterval /= ONE_DAY;\n      return approxInterval > 16 ? 16 // Math.floor(daysInMonth / 2) + 1  // In this case we only want one tick between two months.\n      : approxInterval > 7.5 ? 7 // TODO week 7 or day 8?\n      : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1;\n    }\n\n    function getMonthInterval(approxInterval) {\n      var APPROX_ONE_MONTH = 30 * ONE_DAY;\n      approxInterval /= APPROX_ONE_MONTH;\n      return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getHourInterval(approxInterval) {\n      approxInterval /= ONE_HOUR;\n      return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getMinutesAndSecondsInterval(approxInterval, isMinutes) {\n      approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND;\n      return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getMillisecondsInterval(approxInterval) {\n      return nice(approxInterval, true);\n    }\n\n    function getFirstTimestampOfUnit(date, unitName, isUTC) {\n      var outDate = new Date(date);\n\n      switch (getPrimaryTimeUnit(unitName)) {\n        case 'year':\n        case 'month':\n          outDate[monthSetterName(isUTC)](0);\n\n        case 'day':\n          outDate[dateSetterName(isUTC)](1);\n\n        case 'hour':\n          outDate[hoursSetterName(isUTC)](0);\n\n        case 'minute':\n          outDate[minutesSetterName(isUTC)](0);\n\n        case 'second':\n          outDate[secondsSetterName(isUTC)](0);\n          outDate[millisecondsSetterName(isUTC)](0);\n      }\n\n      return outDate.getTime();\n    }\n\n    function getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent) {\n      var safeLimit = 10000;\n      var unitNames = timeUnits;\n      var iter = 0;\n\n      function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) {\n        var date = new Date(minTimestamp);\n        var dateTime = minTimestamp;\n        var d = date[getMethodName](); // if (isDate) {\n        //     d -= 1; // Starts with 0;   PENDING\n        // }\n\n        while (dateTime < maxTimestamp && dateTime <= extent[1]) {\n          out.push({\n            value: dateTime\n          });\n          d += interval;\n          date[setMethodName](d);\n          dateTime = date.getTime();\n        } // This extra tick is for calcuating ticks of next level. Will not been added to the final result\n\n\n        out.push({\n          value: dateTime,\n          notAdd: true\n        });\n      }\n\n      function addLevelTicks(unitName, lastLevelTicks, levelTicks) {\n        var newAddedTicks = [];\n        var isFirstLevel = !lastLevelTicks.length;\n\n        if (isUnitValueSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) {\n          return;\n        }\n\n        if (isFirstLevel) {\n          lastLevelTicks = [{\n            // TODO Optimize. Not include so may ticks.\n            value: getFirstTimestampOfUnit(new Date(extent[0]), unitName, isUTC)\n          }, {\n            value: extent[1]\n          }];\n        }\n\n        for (var i = 0; i < lastLevelTicks.length - 1; i++) {\n          var startTick = lastLevelTicks[i].value;\n          var endTick = lastLevelTicks[i + 1].value;\n\n          if (startTick === endTick) {\n            continue;\n          }\n\n          var interval = void 0;\n          var getterName = void 0;\n          var setterName = void 0;\n          var isDate = false;\n\n          switch (unitName) {\n            case 'year':\n              interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365));\n              getterName = fullYearGetterName(isUTC);\n              setterName = fullYearSetterName(isUTC);\n              break;\n\n            case 'half-year':\n            case 'quarter':\n            case 'month':\n              interval = getMonthInterval(approxInterval);\n              getterName = monthGetterName(isUTC);\n              setterName = monthSetterName(isUTC);\n              break;\n\n            case 'week': // PENDING If week is added. Ignore day.\n\n            case 'half-week':\n            case 'day':\n              interval = getDateInterval(approxInterval); // Use 32 days and let interval been 16\n\n              getterName = dateGetterName(isUTC);\n              setterName = dateSetterName(isUTC);\n              isDate = true;\n              break;\n\n            case 'half-day':\n            case 'quarter-day':\n            case 'hour':\n              interval = getHourInterval(approxInterval);\n              getterName = hoursGetterName(isUTC);\n              setterName = hoursSetterName(isUTC);\n              break;\n\n            case 'minute':\n              interval = getMinutesAndSecondsInterval(approxInterval, true);\n              getterName = minutesGetterName(isUTC);\n              setterName = minutesSetterName(isUTC);\n              break;\n\n            case 'second':\n              interval = getMinutesAndSecondsInterval(approxInterval, false);\n              getterName = secondsGetterName(isUTC);\n              setterName = secondsSetterName(isUTC);\n              break;\n\n            case 'millisecond':\n              interval = getMillisecondsInterval(approxInterval);\n              getterName = millisecondsGetterName(isUTC);\n              setterName = millisecondsSetterName(isUTC);\n              break;\n          }\n\n          addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks);\n\n          if (unitName === 'year' && levelTicks.length > 1 && i === 0) {\n            // Add nearest years to the left extent.\n            levelTicks.unshift({\n              value: levelTicks[0].value - interval\n            });\n          }\n        }\n\n        for (var i = 0; i < newAddedTicks.length; i++) {\n          levelTicks.push(newAddedTicks[i]);\n        } // newAddedTicks.length && console.log(unitName, newAddedTicks);\n\n\n        return newAddedTicks;\n      }\n\n      var levelsTicks = [];\n      var currentLevelTicks = [];\n      var tickCount = 0;\n      var lastLevelTickCount = 0;\n\n      for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) {\n        var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]);\n\n        if (!isPrimaryTimeUnit(unitNames[i])) {\n          // TODO\n          continue;\n        }\n\n        addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks);\n        var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null;\n\n        if (primaryTimeUnit !== nextPrimaryTimeUnit) {\n          if (currentLevelTicks.length) {\n            lastLevelTickCount = tickCount; // Remove the duplicate so the tick count can be precisely.\n\n            currentLevelTicks.sort(function (a, b) {\n              return a.value - b.value;\n            });\n            var levelTicksRemoveDuplicated = [];\n\n            for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) {\n              var tickValue = currentLevelTicks[i_1].value;\n\n              if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) {\n                levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]);\n\n                if (tickValue >= extent[0] && tickValue <= extent[1]) {\n                  tickCount++;\n                }\n              }\n            }\n\n            var targetTickNum = (extent[1] - extent[0]) / approxInterval; // Added too much in this level and not too less in last level\n\n            if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) {\n              break;\n            } // Only treat primary time unit as one level.\n\n\n            levelsTicks.push(levelTicksRemoveDuplicated);\n\n            if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) {\n              break;\n            }\n          } // Reset if next unitName is primary\n\n\n          currentLevelTicks = [];\n        }\n      }\n\n      if (\"development\" !== 'production') {\n        if (iter >= safeLimit) {\n          warn('Exceed safe limit.');\n        }\n      }\n\n      var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) {\n        return filter(levelTicks, function (tick) {\n          return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd;\n        });\n      }), function (levelTicks) {\n        return levelTicks.length > 0;\n      });\n      var ticks = [];\n      var maxLevel = levelsTicksInExtent.length - 1;\n\n      for (var i = 0; i < levelsTicksInExtent.length; ++i) {\n        var levelTicks = levelsTicksInExtent[i];\n\n        for (var k = 0; k < levelTicks.length; ++k) {\n          ticks.push({\n            value: levelTicks[k].value,\n            level: maxLevel - i\n          });\n        }\n      }\n\n      ticks.sort(function (a, b) {\n        return a.value - b.value;\n      }); // Remove duplicates\n\n      var result = [];\n\n      for (var i = 0; i < ticks.length; ++i) {\n        if (i === 0 || ticks[i].value !== ticks[i - 1].value) {\n          result.push(ticks[i]);\n        }\n      }\n\n      return result;\n    }\n\n    Scale.registerClass(TimeScale);\n\n    var scaleProto = Scale.prototype; // FIXME:TS refactor: not good to call it directly with `this`?\n\n    var intervalScaleProto = IntervalScale.prototype;\n    var roundingErrorFix = round;\n    var mathFloor = Math.floor;\n    var mathCeil = Math.ceil;\n    var mathPow$1 = Math.pow;\n    var mathLog = Math.log;\n\n    var LogScale =\n    /** @class */\n    function (_super) {\n      __extends(LogScale, _super);\n\n      function LogScale() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'log';\n        _this.base = 10;\n        _this._originalScale = new IntervalScale(); // FIXME:TS actually used by `IntervalScale`\n\n        _this._interval = 0;\n        return _this;\n      }\n      /**\n       * @param Whether expand the ticks to niced extent.\n       */\n\n\n      LogScale.prototype.getTicks = function (expandToNicedExtent) {\n        var originalScale = this._originalScale;\n        var extent = this._extent;\n        var originalExtent = originalScale.getExtent();\n        var ticks = intervalScaleProto.getTicks.call(this, expandToNicedExtent);\n        return map(ticks, function (tick) {\n          var val = tick.value;\n          var powVal = round(mathPow$1(this.base, val)); // Fix #4158\n\n          powVal = val === extent[0] && this._fixMin ? fixRoundingError(powVal, originalExtent[0]) : powVal;\n          powVal = val === extent[1] && this._fixMax ? fixRoundingError(powVal, originalExtent[1]) : powVal;\n          return {\n            value: powVal\n          };\n        }, this);\n      };\n\n      LogScale.prototype.setExtent = function (start, end) {\n        var base = mathLog(this.base); // log(-Infinity) is NaN, so safe guard here\n\n        start = mathLog(Math.max(0, start)) / base;\n        end = mathLog(Math.max(0, end)) / base;\n        intervalScaleProto.setExtent.call(this, start, end);\n      };\n      /**\n       * @return {number} end\n       */\n\n\n      LogScale.prototype.getExtent = function () {\n        var base = this.base;\n        var extent = scaleProto.getExtent.call(this);\n        extent[0] = mathPow$1(base, extent[0]);\n        extent[1] = mathPow$1(base, extent[1]); // Fix #4158\n\n        var originalScale = this._originalScale;\n        var originalExtent = originalScale.getExtent();\n        this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));\n        this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));\n        return extent;\n      };\n\n      LogScale.prototype.unionExtent = function (extent) {\n        this._originalScale.unionExtent(extent);\n\n        var base = this.base;\n        extent[0] = mathLog(extent[0]) / mathLog(base);\n        extent[1] = mathLog(extent[1]) / mathLog(base);\n        scaleProto.unionExtent.call(this, extent);\n      };\n\n      LogScale.prototype.unionExtentFromData = function (data, dim) {\n        // TODO\n        // filter value that <= 0\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * Update interval and extent of intervals for nice ticks\n       * @param approxTickNum default 10 Given approx tick number\n       */\n\n\n      LogScale.prototype.calcNiceTicks = function (approxTickNum) {\n        approxTickNum = approxTickNum || 10;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n\n        if (span === Infinity || span <= 0) {\n          return;\n        }\n\n        var interval = quantity(span);\n        var err = approxTickNum / span * interval; // Filter ticks to get closer to the desired count.\n\n        if (err <= 0.5) {\n          interval *= 10;\n        } // Interval should be integer\n\n\n        while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {\n          interval *= 10;\n        }\n\n        var niceExtent = [round(mathCeil(extent[0] / interval) * interval), round(mathFloor(extent[1] / interval) * interval)];\n        this._interval = interval;\n        this._niceExtent = niceExtent;\n      };\n\n      LogScale.prototype.calcNiceExtent = function (opt) {\n        intervalScaleProto.calcNiceExtent.call(this, opt);\n        this._fixMin = opt.fixMin;\n        this._fixMax = opt.fixMax;\n      };\n\n      LogScale.prototype.parse = function (val) {\n        return val;\n      };\n\n      LogScale.prototype.contain = function (val) {\n        val = mathLog(val) / mathLog(this.base);\n        return contain$1(val, this._extent);\n      };\n\n      LogScale.prototype.normalize = function (val) {\n        val = mathLog(val) / mathLog(this.base);\n        return normalize$1(val, this._extent);\n      };\n\n      LogScale.prototype.scale = function (val) {\n        val = scale$2(val, this._extent);\n        return mathPow$1(this.base, val);\n      };\n\n      LogScale.type = 'log';\n      return LogScale;\n    }(Scale);\n\n    var proto = LogScale.prototype;\n    proto.getMinorTicks = intervalScaleProto.getMinorTicks;\n    proto.getLabel = intervalScaleProto.getLabel;\n\n    function fixRoundingError(val, originalVal) {\n      return roundingErrorFix(val, getPrecision(originalVal));\n    }\n\n    Scale.registerClass(LogScale);\n\n    var ScaleRawExtentInfo =\n    /** @class */\n    function () {\n      function ScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\n      originalExtent) {\n        this._prepareParams(scale, model, originalExtent);\n      }\n      /**\n       * Parameters depending on outside (like model, user callback)\n       * are prepared and fixed here.\n       */\n\n\n      ScaleRawExtentInfo.prototype._prepareParams = function (scale, model, // Usually: data extent from all series on this axis.\n      dataExtent) {\n        if (dataExtent[1] < dataExtent[0]) {\n          dataExtent = [NaN, NaN];\n        }\n\n        this._dataMin = dataExtent[0];\n        this._dataMax = dataExtent[1];\n        var isOrdinal = this._isOrdinal = scale.type === 'ordinal';\n        this._needCrossZero = scale.type === 'interval' && model.getNeedCrossZero && model.getNeedCrossZero();\n        var modelMinRaw = this._modelMinRaw = model.get('min', true);\n\n        if (isFunction(modelMinRaw)) {\n          // This callback always provides users the full data extent (before data is filtered).\n          this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw({\n            min: dataExtent[0],\n            max: dataExtent[1]\n          }));\n        } else if (modelMinRaw !== 'dataMin') {\n          this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw);\n        }\n\n        var modelMaxRaw = this._modelMaxRaw = model.get('max', true);\n\n        if (isFunction(modelMaxRaw)) {\n          // This callback always provides users the full data extent (before data is filtered).\n          this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw({\n            min: dataExtent[0],\n            max: dataExtent[1]\n          }));\n        } else if (modelMaxRaw !== 'dataMax') {\n          this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw);\n        }\n\n        if (isOrdinal) {\n          // FIXME: there is a flaw here: if there is no \"block\" data processor like `dataZoom`,\n          // and progressive rendering is using, here the category result might just only contain\n          // the processed chunk rather than the entire result.\n          this._axisDataLen = model.getCategories().length;\n        } else {\n          var boundaryGap = model.get('boundaryGap');\n          var boundaryGapArr = isArray(boundaryGap) ? boundaryGap : [boundaryGap || 0, boundaryGap || 0];\n\n          if (typeof boundaryGapArr[0] === 'boolean' || typeof boundaryGapArr[1] === 'boolean') {\n            if (\"development\" !== 'production') {\n              console.warn('Boolean type for boundaryGap is only ' + 'allowed for ordinal axis. Please use string in ' + 'percentage instead, e.g., \"20%\". Currently, ' + 'boundaryGap is set to be 0.');\n            }\n\n            this._boundaryGapInner = [0, 0];\n          } else {\n            this._boundaryGapInner = [parsePercent(boundaryGapArr[0], 1), parsePercent(boundaryGapArr[1], 1)];\n          }\n        }\n      };\n      /**\n       * Calculate extent by prepared parameters.\n       * This method has no external dependency and can be called duplicatedly,\n       * getting the same result.\n       * If parameters changed, should call this method to recalcuate.\n       */\n\n\n      ScaleRawExtentInfo.prototype.calculate = function () {\n        // Notice: When min/max is not set (that is, when there are null/undefined,\n        // which is the most common case), these cases should be ensured:\n        // (1) For 'ordinal', show all axis.data.\n        // (2) For others:\n        //      + `boundaryGap` is applied (if min/max set, boundaryGap is\n        //      disabled).\n        //      + If `needCrossZero`, min/max should be zero, otherwise, min/max should\n        //      be the result that originalExtent enlarged by boundaryGap.\n        // (3) If no data, it should be ensured that `scale.setBlank` is set.\n        var isOrdinal = this._isOrdinal;\n        var dataMin = this._dataMin;\n        var dataMax = this._dataMax;\n        var axisDataLen = this._axisDataLen;\n        var boundaryGapInner = this._boundaryGapInner;\n        var span = !isOrdinal ? dataMax - dataMin || Math.abs(dataMin) : null; // Currently if a `'value'` axis model min is specified as 'dataMin'/'dataMax',\n        // `boundaryGap` will not be used. It's the different from specifying as `null`/`undefined`.\n\n        var min = this._modelMinRaw === 'dataMin' ? dataMin : this._modelMinNum;\n        var max = this._modelMaxRaw === 'dataMax' ? dataMax : this._modelMaxNum; // If `_modelMinNum`/`_modelMaxNum` is `null`/`undefined`, should not be fixed.\n\n        var minFixed = min != null;\n        var maxFixed = max != null;\n\n        if (min == null) {\n          min = isOrdinal ? axisDataLen ? 0 : NaN : dataMin - boundaryGapInner[0] * span;\n        }\n\n        if (max == null) {\n          max = isOrdinal ? axisDataLen ? axisDataLen - 1 : NaN : dataMax + boundaryGapInner[1] * span;\n        }\n\n        (min == null || !isFinite(min)) && (min = NaN);\n        (max == null || !isFinite(max)) && (max = NaN);\n        var isBlank = eqNaN(min) || eqNaN(max) || isOrdinal && !axisDataLen; // If data extent modified, need to recalculated to ensure cross zero.\n\n        if (this._needCrossZero) {\n          // Axis is over zero and min is not set\n          if (min > 0 && max > 0 && !minFixed) {\n            min = 0; // minFixed = true;\n          } // Axis is under zero and max is not set\n\n\n          if (min < 0 && max < 0 && !maxFixed) {\n            max = 0; // maxFixed = true;\n          } // PENDING:\n          // When `needCrossZero` and all data is positive/negative, should it be ensured\n          // that the results processed by boundaryGap are positive/negative?\n          // If so, here `minFixed`/`maxFixed` need to be set.\n\n        }\n\n        var determinedMin = this._determinedMin;\n        var determinedMax = this._determinedMax;\n\n        if (determinedMin != null) {\n          min = determinedMin;\n          minFixed = true;\n        }\n\n        if (determinedMax != null) {\n          max = determinedMax;\n          maxFixed = true;\n        } // Ensure min/max be finite number or NaN here. (not to be null/undefined)\n        // `NaN` means min/max axis is blank.\n\n\n        return {\n          min: min,\n          max: max,\n          minFixed: minFixed,\n          maxFixed: maxFixed,\n          isBlank: isBlank\n        };\n      };\n\n      ScaleRawExtentInfo.prototype.modifyDataMinMax = function (minMaxName, val) {\n        if (\"development\" !== 'production') {\n          assert(!this.frozen);\n        }\n\n        this[DATA_MIN_MAX_ATTR[minMaxName]] = val;\n      };\n\n      ScaleRawExtentInfo.prototype.setDeterminedMinMax = function (minMaxName, val) {\n        var attr = DETERMINED_MIN_MAX_ATTR[minMaxName];\n\n        if (\"development\" !== 'production') {\n          assert(!this.frozen // Earse them usually means logic flaw.\n          && this[attr] == null);\n        }\n\n        this[attr] = val;\n      };\n\n      ScaleRawExtentInfo.prototype.freeze = function () {\n        // @ts-ignore\n        this.frozen = true;\n      };\n\n      return ScaleRawExtentInfo;\n    }();\n    var DETERMINED_MIN_MAX_ATTR = {\n      min: '_determinedMin',\n      max: '_determinedMax'\n    };\n    var DATA_MIN_MAX_ATTR = {\n      min: '_dataMin',\n      max: '_dataMax'\n    };\n    /**\n     * Get scale min max and related info only depends on model settings.\n     * This method can be called after coordinate system created.\n     * For example, in data processing stage.\n     *\n     * Scale extent info probably be required multiple times during a workflow.\n     * For example:\n     * (1) `dataZoom` depends it to get the axis extent in \"100%\" state.\n     * (2) `processor/extentCalculator` depends it to make sure whether axis extent is specified.\n     * (3) `coordSys.update` use it to finally decide the scale extent.\n     * But the callback of `min`/`max` should not be called multiple times.\n     * The code below should not be implemented repeatedly either.\n     * So we cache the result in the scale instance, which will be recreated at the beginning\n     * of the workflow (because `scale` instance will be recreated each round of the workflow).\n     */\n\n    function ensureScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\n    originalExtent) {\n      // Do not permit to recreate.\n      var rawExtentInfo = scale.rawExtentInfo;\n\n      if (rawExtentInfo) {\n        return rawExtentInfo;\n      }\n\n      rawExtentInfo = new ScaleRawExtentInfo(scale, model, originalExtent); // @ts-ignore\n\n      scale.rawExtentInfo = rawExtentInfo;\n      return rawExtentInfo;\n    }\n    function parseAxisModelMinMax(scale, minMax) {\n      return minMax == null ? null : eqNaN(minMax) ? NaN : scale.parse(minMax);\n    }\n\n    /**\n     * Get axis scale extent before niced.\n     * Item of returned array can only be number (including Infinity and NaN).\n     *\n     * Caution:\n     * Precondition of calling this method:\n     * The scale extent has been initialized using series data extent via\n     * `scale.setExtent` or `scale.unionExtentFromData`;\n     */\n\n    function getScaleExtent(scale, model) {\n      var scaleType = scale.type;\n      var rawExtentResult = ensureScaleRawExtentInfo(scale, model, scale.getExtent()).calculate();\n      scale.setBlank(rawExtentResult.isBlank);\n      var min = rawExtentResult.min;\n      var max = rawExtentResult.max; // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis\n      // is base axis\n      // FIXME\n      // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly.\n      // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent?\n      //     Should not depend on series type `bar`?\n      // (3) Fix that might overlap when using dataZoom.\n      // (4) Consider other chart types using `barGrid`?\n      // See #6728, #4862, `test/bar-overflow-time-plot.html`\n\n      var ecModel = model.ecModel;\n\n      if (ecModel && scaleType === 'time'\n      /* || scaleType === 'interval' */\n      ) {\n        var barSeriesModels = prepareLayoutBarSeries('bar', ecModel);\n        var isBaseAxisAndHasBarSeries_1 = false;\n        each(barSeriesModels, function (seriesModel) {\n          isBaseAxisAndHasBarSeries_1 = isBaseAxisAndHasBarSeries_1 || seriesModel.getBaseAxis() === model.axis;\n        });\n\n        if (isBaseAxisAndHasBarSeries_1) {\n          // Calculate placement of bars on axis. TODO should be decoupled\n          // with barLayout\n          var barWidthAndOffset = makeColumnLayout(barSeriesModels); // Adjust axis min and max to account for overflow\n\n          var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset);\n          min = adjustedScale.min;\n          max = adjustedScale.max;\n        }\n      }\n\n      return {\n        extent: [min, max],\n        // \"fix\" means \"fixed\", the value should not be\n        // changed in the subsequent steps.\n        fixMin: rawExtentResult.minFixed,\n        fixMax: rawExtentResult.maxFixed\n      };\n    }\n\n    function adjustScaleForOverflow(min, max, model, // Only support cartesian coord yet.\n    barWidthAndOffset) {\n      // Get Axis Length\n      var axisExtent = model.axis.getExtent();\n      var axisLength = axisExtent[1] - axisExtent[0]; // Get bars on current base axis and calculate min and max overflow\n\n      var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis);\n\n      if (barsOnCurrentAxis === undefined) {\n        return {\n          min: min,\n          max: max\n        };\n      }\n\n      var minOverflow = Infinity;\n      each(barsOnCurrentAxis, function (item) {\n        minOverflow = Math.min(item.offset, minOverflow);\n      });\n      var maxOverflow = -Infinity;\n      each(barsOnCurrentAxis, function (item) {\n        maxOverflow = Math.max(item.offset + item.width, maxOverflow);\n      });\n      minOverflow = Math.abs(minOverflow);\n      maxOverflow = Math.abs(maxOverflow);\n      var totalOverFlow = minOverflow + maxOverflow; // Calculate required buffer based on old range and overflow\n\n      var oldRange = max - min;\n      var oldRangePercentOfNew = 1 - (minOverflow + maxOverflow) / axisLength;\n      var overflowBuffer = oldRange / oldRangePercentOfNew - oldRange;\n      max += overflowBuffer * (maxOverflow / totalOverFlow);\n      min -= overflowBuffer * (minOverflow / totalOverFlow);\n      return {\n        min: min,\n        max: max\n      };\n    } // Precondition of calling this method:\n    // The scale extent has been initialized using series data extent via\n    // `scale.setExtent` or `scale.unionExtentFromData`;\n\n\n    function niceScaleExtent(scale, inModel) {\n      var model = inModel;\n      var extentInfo = getScaleExtent(scale, model);\n      var extent = extentInfo.extent;\n      var splitNumber = model.get('splitNumber');\n\n      if (scale instanceof LogScale) {\n        scale.base = model.get('logBase');\n      }\n\n      var scaleType = scale.type;\n      var interval = model.get('interval');\n      var isIntervalOrTime = scaleType === 'interval' || scaleType === 'time';\n      scale.setExtent(extent[0], extent[1]);\n      scale.calcNiceExtent({\n        splitNumber: splitNumber,\n        fixMin: extentInfo.fixMin,\n        fixMax: extentInfo.fixMax,\n        minInterval: isIntervalOrTime ? model.get('minInterval') : null,\n        maxInterval: isIntervalOrTime ? model.get('maxInterval') : null\n      }); // If some one specified the min, max. And the default calculated interval\n      // is not good enough. He can specify the interval. It is often appeared\n      // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard\n      // to be 60.\n      // FIXME\n\n      if (interval != null) {\n        scale.setInterval && scale.setInterval(interval);\n      }\n    }\n    /**\n     * @param axisType Default retrieve from model.type\n     */\n\n    function createScaleByModel(model, axisType) {\n      axisType = axisType || model.get('type');\n\n      if (axisType) {\n        switch (axisType) {\n          // Buildin scale\n          case 'category':\n            return new OrdinalScale({\n              ordinalMeta: model.getOrdinalMeta ? model.getOrdinalMeta() : model.getCategories(),\n              extent: [Infinity, -Infinity]\n            });\n\n          case 'time':\n            return new TimeScale({\n              locale: model.ecModel.getLocaleModel(),\n              useUTC: model.ecModel.get('useUTC')\n            });\n\n          default:\n            // case 'value'/'interval', 'log', or others.\n            return new (Scale.getClass(axisType) || IntervalScale)();\n        }\n      }\n    }\n    /**\n     * Check if the axis cross 0\n     */\n\n    function ifAxisCrossZero(axis) {\n      var dataExtent = axis.scale.getExtent();\n      var min = dataExtent[0];\n      var max = dataExtent[1];\n      return !(min > 0 && max > 0 || min < 0 && max < 0);\n    }\n    /**\n     * @param axis\n     * @return Label formatter function.\n     *         param: {number} tickValue,\n     *         param: {number} idx, the index in all ticks.\n     *                         If category axis, this param is not required.\n     *         return: {string} label string.\n     */\n\n    function makeLabelFormatter(axis) {\n      var labelFormatter = axis.getLabelModel().get('formatter');\n      var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null;\n\n      if (axis.scale.type === 'time') {\n        return function (tpl) {\n          return function (tick, idx) {\n            return axis.scale.getFormattedLabel(tick, idx, tpl);\n          };\n        }(labelFormatter);\n      } else if (isString(labelFormatter)) {\n        return function (tpl) {\n          return function (tick) {\n            // For category axis, get raw value; for numeric axis,\n            // get formatted label like '1,333,444'.\n            var label = axis.scale.getLabel(tick);\n            var text = tpl.replace('{value}', label != null ? label : '');\n            return text;\n          };\n        }(labelFormatter);\n      } else if (isFunction(labelFormatter)) {\n        return function (cb) {\n          return function (tick, idx) {\n            // The original intention of `idx` is \"the index of the tick in all ticks\".\n            // But the previous implementation of category axis do not consider the\n            // `axisLabel.interval`, which cause that, for example, the `interval` is\n            // `1`, then the ticks \"name5\", \"name7\", \"name9\" are displayed, where the\n            // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep\n            // the definition here for back compatibility.\n            if (categoryTickStart != null) {\n              idx = tick.value - categoryTickStart;\n            }\n\n            return cb(getAxisRawValue(axis, tick), idx, tick.level != null ? {\n              level: tick.level\n            } : null);\n          };\n        }(labelFormatter);\n      } else {\n        return function (tick) {\n          return axis.scale.getLabel(tick);\n        };\n      }\n    }\n    function getAxisRawValue(axis, tick) {\n      // In category axis with data zoom, tick is not the original\n      // index of axis.data. So tick should not be exposed to user\n      // in category axis.\n      return axis.type === 'category' ? axis.scale.getLabel(tick) : tick.value;\n    }\n    /**\n     * @param axis\n     * @return Be null/undefined if no labels.\n     */\n\n    function estimateLabelUnionRect(axis) {\n      var axisModel = axis.model;\n      var scale = axis.scale;\n\n      if (!axisModel.get(['axisLabel', 'show']) || scale.isBlank()) {\n        return;\n      }\n\n      var realNumberScaleTicks;\n      var tickCount;\n      var categoryScaleExtent = scale.getExtent(); // Optimize for large category data, avoid call `getTicks()`.\n\n      if (scale instanceof OrdinalScale) {\n        tickCount = scale.count();\n      } else {\n        realNumberScaleTicks = scale.getTicks();\n        tickCount = realNumberScaleTicks.length;\n      }\n\n      var axisLabelModel = axis.getLabelModel();\n      var labelFormatter = makeLabelFormatter(axis);\n      var rect;\n      var step = 1; // Simple optimization for large amount of labels\n\n      if (tickCount > 40) {\n        step = Math.ceil(tickCount / 40);\n      }\n\n      for (var i = 0; i < tickCount; i += step) {\n        var tick = realNumberScaleTicks ? realNumberScaleTicks[i] : {\n          value: categoryScaleExtent[0] + i\n        };\n        var label = labelFormatter(tick, i);\n        var unrotatedSingleRect = axisLabelModel.getTextRect(label);\n        var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);\n        rect ? rect.union(singleRect) : rect = singleRect;\n      }\n\n      return rect;\n    }\n\n    function rotateTextRect(textRect, rotate) {\n      var rotateRadians = rotate * Math.PI / 180;\n      var beforeWidth = textRect.width;\n      var beforeHeight = textRect.height;\n      var afterWidth = beforeWidth * Math.abs(Math.cos(rotateRadians)) + Math.abs(beforeHeight * Math.sin(rotateRadians));\n      var afterHeight = beforeWidth * Math.abs(Math.sin(rotateRadians)) + Math.abs(beforeHeight * Math.cos(rotateRadians));\n      var rotatedRect = new BoundingRect(textRect.x, textRect.y, afterWidth, afterHeight);\n      return rotatedRect;\n    }\n    /**\n     * @param model axisLabelModel or axisTickModel\n     * @return {number|String} Can be null|'auto'|number|function\n     */\n\n\n    function getOptionCategoryInterval(model) {\n      var interval = model.get('interval');\n      return interval == null ? 'auto' : interval;\n    }\n    /**\n     * Set `categoryInterval` as 0 implicitly indicates that\n     * show all labels regardless of overlap.\n     * @param {Object} axis axisModel.axis\n     */\n\n    function shouldShowAllLabels(axis) {\n      return axis.type === 'category' && getOptionCategoryInterval(axis.getLabelModel()) === 0;\n    }\n    function getDataDimensionsOnAxis(data, axisDim) {\n      // Remove duplicated dat dimensions caused by `getStackedDimension`.\n      var dataDimMap = {}; // Currently `mapDimensionsAll` will contain stack result dimension ('__\\0ecstackresult').\n      // PENDING: is it reasonable? Do we need to remove the original dim from \"coord dim\" since\n      // there has been stacked result dim?\n\n      each(data.mapDimensionsAll(axisDim), function (dataDim) {\n        // For example, the extent of the original dimension\n        // is [0.1, 0.5], the extent of the `stackResultDimension`\n        // is [7, 9], the final extent should NOT include [0.1, 0.5],\n        // because there is no graphic corresponding to [0.1, 0.5].\n        // See the case in `test/area-stack.html` `main1`, where area line\n        // stack needs `yAxis` not start from 0.\n        dataDimMap[getStackedDimension(data, dataDim)] = true;\n      });\n      return keys(dataDimMap);\n    }\n    function unionAxisExtentFromData(dataExtent, data, axisDim) {\n      if (data) {\n        each(getDataDimensionsOnAxis(data, axisDim), function (dim) {\n          var seriesExtent = data.getApproximateExtent(dim);\n          seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]);\n          seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]);\n        });\n      }\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    var AxisModelCommonMixin =\n    /** @class */\n    function () {\n      function AxisModelCommonMixin() {}\n\n      AxisModelCommonMixin.prototype.getNeedCrossZero = function () {\n        var option = this.option;\n        return !option.scale;\n      };\n      /**\n       * Should be implemented by each axis model if necessary.\n       * @return coordinate system model\n       */\n\n\n      AxisModelCommonMixin.prototype.getCoordSysModel = function () {\n        return;\n      };\n\n      return AxisModelCommonMixin;\n    }();\n\n    /**\n     * Create a multi dimension List structure from seriesModel.\n     */\n\n    function createList(seriesModel) {\n      return createSeriesData(null, seriesModel);\n    } // export function createGraph(seriesModel) {\n    var dataStack$1 = {\n      isDimensionStacked: isDimensionStacked,\n      enableDataStack: enableDataStack,\n      getStackedDimension: getStackedDimension\n    };\n    /**\n     * Create scale\n     * @param {Array.<number>} dataExtent\n     * @param {Object|module:echarts/Model} option If `optoin.type`\n     *        is secified, it can only be `'value'` currently.\n     */\n\n    function createScale(dataExtent, option) {\n      var axisModel = option;\n\n      if (!(option instanceof Model)) {\n        axisModel = new Model(option); // FIXME\n        // Currently AxisModelCommonMixin has nothing to do with the\n        // the requirements of `axisHelper.createScaleByModel`. For\n        // example the methods `getCategories` and `getOrdinalMeta`\n        // are required for `'category'` axis, and ecModel is required\n        // for `'time'` axis. But occasionally echarts-gl happened\n        // to only use `'value'` axis.\n        // zrUtil.mixin(axisModel, AxisModelCommonMixin);\n      }\n\n      var scale = createScaleByModel(axisModel);\n      scale.setExtent(dataExtent[0], dataExtent[1]);\n      niceScaleExtent(scale, axisModel);\n      return scale;\n    }\n    /**\n     * Mixin common methods to axis model,\n     *\n     * Include methods\n     * `getFormattedLabels() => Array.<string>`\n     * `getCategories() => Array.<string>`\n     * `getMin(origin: boolean) => number`\n     * `getMax(origin: boolean) => number`\n     * `getNeedCrossZero() => boolean`\n     */\n\n    function mixinAxisModelCommonMethods(Model) {\n      mixin(Model, AxisModelCommonMixin);\n    }\n    function createTextStyle$1(textStyleModel, opts) {\n      opts = opts || {};\n      return createTextStyle(textStyleModel, null, null, opts.state !== 'normal');\n    }\n\n    var helper = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        createList: createList,\n        getLayoutRect: getLayoutRect,\n        dataStack: dataStack$1,\n        createScale: createScale,\n        mixinAxisModelCommonMethods: mixinAxisModelCommonMethods,\n        getECData: getECData,\n        createTextStyle: createTextStyle$1,\n        createDimensions: createDimensions,\n        createSymbol: createSymbol,\n        enableHoverEmphasis: enableHoverEmphasis\n    });\n\n    var EPSILON$4 = 1e-8;\n    function isAroundEqual$1(a, b) {\n        return Math.abs(a - b) < EPSILON$4;\n    }\n    function contain$2(points, x, y) {\n        var w = 0;\n        var p = points[0];\n        if (!p) {\n            return false;\n        }\n        for (var i = 1; i < points.length; i++) {\n            var p2 = points[i];\n            w += windingLine(p[0], p[1], p2[0], p2[1], x, y);\n            p = p2;\n        }\n        var p0 = points[0];\n        if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) {\n            w += windingLine(p[0], p[1], p0[0], p0[1], x, y);\n        }\n        return w !== 0;\n    }\n\n    var TMP_TRANSFORM = [];\n\n    function transformPoints(points, transform) {\n      for (var p = 0; p < points.length; p++) {\n        applyTransform(points[p], points[p], transform);\n      }\n    }\n\n    function updateBBoxFromPoints(points, min$1, max$1, projection) {\n      for (var i = 0; i < points.length; i++) {\n        var p = points[i];\n\n        if (projection) {\n          // projection may return null point.\n          p = projection.project(p);\n        }\n\n        if (p && isFinite(p[0]) && isFinite(p[1])) {\n          min(min$1, min$1, p);\n          max(max$1, max$1, p);\n        }\n      }\n    }\n\n    function centroid(points) {\n      var signedArea = 0;\n      var cx = 0;\n      var cy = 0;\n      var len = points.length;\n      var x0 = points[len - 1][0];\n      var y0 = points[len - 1][1]; // Polygon should been closed.\n\n      for (var i = 0; i < len; i++) {\n        var x1 = points[i][0];\n        var y1 = points[i][1];\n        var a = x0 * y1 - x1 * y0;\n        signedArea += a;\n        cx += (x0 + x1) * a;\n        cy += (y0 + y1) * a;\n        x0 = x1;\n        y0 = y1;\n      }\n\n      return signedArea ? [cx / signedArea / 3, cy / signedArea / 3, signedArea] : [points[0][0] || 0, points[0][1] || 0];\n    }\n\n    var Region =\n    /** @class */\n    function () {\n      function Region(name) {\n        this.name = name;\n      }\n\n      Region.prototype.setCenter = function (center) {\n        this._center = center;\n      };\n      /**\n       * Get center point in data unit. That is,\n       * for GeoJSONRegion, the unit is lat/lng,\n       * for GeoSVGRegion, the unit is SVG local coord.\n       */\n\n\n      Region.prototype.getCenter = function () {\n        var center = this._center;\n\n        if (!center) {\n          // In most cases there are no need to calculate this center.\n          // So calculate only when called.\n          center = this._center = this.calcCenter();\n        }\n\n        return center;\n      };\n\n      return Region;\n    }();\n\n    var GeoJSONPolygonGeometry =\n    /** @class */\n    function () {\n      function GeoJSONPolygonGeometry(exterior, interiors) {\n        this.type = 'polygon';\n        this.exterior = exterior;\n        this.interiors = interiors;\n      }\n\n      return GeoJSONPolygonGeometry;\n    }();\n\n    var GeoJSONLineStringGeometry =\n    /** @class */\n    function () {\n      function GeoJSONLineStringGeometry(points) {\n        this.type = 'linestring';\n        this.points = points;\n      }\n\n      return GeoJSONLineStringGeometry;\n    }();\n\n    var GeoJSONRegion =\n    /** @class */\n    function (_super) {\n      __extends(GeoJSONRegion, _super);\n\n      function GeoJSONRegion(name, geometries, cp) {\n        var _this = _super.call(this, name) || this;\n\n        _this.type = 'geoJSON';\n        _this.geometries = geometries;\n        _this._center = cp && [cp[0], cp[1]];\n        return _this;\n      }\n\n      GeoJSONRegion.prototype.calcCenter = function () {\n        var geometries = this.geometries;\n        var largestGeo;\n        var largestGeoSize = 0;\n\n        for (var i = 0; i < geometries.length; i++) {\n          var geo = geometries[i];\n          var exterior = geo.exterior; // Simple trick to use points count instead of polygon area as region size.\n          // Ignore linestring\n\n          var size = exterior && exterior.length;\n\n          if (size > largestGeoSize) {\n            largestGeo = geo;\n            largestGeoSize = size;\n          }\n        }\n\n        if (largestGeo) {\n          return centroid(largestGeo.exterior);\n        } // from bounding rect by default.\n\n\n        var rect = this.getBoundingRect();\n        return [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      };\n\n      GeoJSONRegion.prototype.getBoundingRect = function (projection) {\n        var rect = this._rect; // Always recalculate if using projection.\n\n        if (rect && !projection) {\n          return rect;\n        }\n\n        var min = [Infinity, Infinity];\n        var max = [-Infinity, -Infinity];\n        var geometries = this.geometries;\n        each(geometries, function (geo) {\n          if (geo.type === 'polygon') {\n            // Doesn't consider hole\n            updateBBoxFromPoints(geo.exterior, min, max, projection);\n          } else {\n            each(geo.points, function (points) {\n              updateBBoxFromPoints(points, min, max, projection);\n            });\n          }\n        }); // Normalie invalid bounding.\n\n        if (!(isFinite(min[0]) && isFinite(min[1]) && isFinite(max[0]) && isFinite(max[1]))) {\n          min[0] = min[1] = max[0] = max[1] = 0;\n        }\n\n        rect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n\n        if (!projection) {\n          this._rect = rect;\n        }\n\n        return rect;\n      };\n\n      GeoJSONRegion.prototype.contain = function (coord) {\n        var rect = this.getBoundingRect();\n        var geometries = this.geometries;\n\n        if (!rect.contain(coord[0], coord[1])) {\n          return false;\n        }\n\n        loopGeo: for (var i = 0, len = geometries.length; i < len; i++) {\n          var geo = geometries[i]; // Only support polygon.\n\n          if (geo.type !== 'polygon') {\n            continue;\n          }\n\n          var exterior = geo.exterior;\n          var interiors = geo.interiors;\n\n          if (contain$2(exterior, coord[0], coord[1])) {\n            // Not in the region if point is in the hole.\n            for (var k = 0; k < (interiors ? interiors.length : 0); k++) {\n              if (contain$2(interiors[k], coord[0], coord[1])) {\n                continue loopGeo;\n              }\n            }\n\n            return true;\n          }\n        }\n\n        return false;\n      };\n      /**\n       * Transform the raw coords to target bounding.\n       * @param x\n       * @param y\n       * @param width\n       * @param height\n       */\n\n\n      GeoJSONRegion.prototype.transformTo = function (x, y, width, height) {\n        var rect = this.getBoundingRect();\n        var aspect = rect.width / rect.height;\n\n        if (!width) {\n          width = aspect * height;\n        } else if (!height) {\n          height = width / aspect;\n        }\n\n        var target = new BoundingRect(x, y, width, height);\n        var transform = rect.calculateTransform(target);\n        var geometries = this.geometries;\n\n        for (var i = 0; i < geometries.length; i++) {\n          var geo = geometries[i];\n\n          if (geo.type === 'polygon') {\n            transformPoints(geo.exterior, transform);\n            each(geo.interiors, function (interior) {\n              transformPoints(interior, transform);\n            });\n          } else {\n            each(geo.points, function (points) {\n              transformPoints(points, transform);\n            });\n          }\n        }\n\n        rect = this._rect;\n        rect.copy(target); // Update center\n\n        this._center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      };\n\n      GeoJSONRegion.prototype.cloneShallow = function (name) {\n        name == null && (name = this.name);\n        var newRegion = new GeoJSONRegion(name, this.geometries, this._center);\n        newRegion._rect = this._rect;\n        newRegion.transformTo = null; // Simply avoid to be called.\n\n        return newRegion;\n      };\n\n      return GeoJSONRegion;\n    }(Region);\n\n    var GeoSVGRegion =\n    /** @class */\n    function (_super) {\n      __extends(GeoSVGRegion, _super);\n\n      function GeoSVGRegion(name, elOnlyForCalculate) {\n        var _this = _super.call(this, name) || this;\n\n        _this.type = 'geoSVG';\n        _this._elOnlyForCalculate = elOnlyForCalculate;\n        return _this;\n      }\n\n      GeoSVGRegion.prototype.calcCenter = function () {\n        var el = this._elOnlyForCalculate;\n        var rect = el.getBoundingRect();\n        var center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n        var mat = identity(TMP_TRANSFORM);\n        var target = el;\n\n        while (target && !target.isGeoSVGGraphicRoot) {\n          mul$1(mat, target.getLocalTransform(), mat);\n          target = target.parent;\n        }\n\n        invert(mat, mat);\n        applyTransform(center, center, mat);\n        return center;\n      };\n\n      return GeoSVGRegion;\n    }(Region);\n\n    function decode(json) {\n      if (!json.UTF8Encoding) {\n        return json;\n      }\n\n      var jsonCompressed = json;\n      var encodeScale = jsonCompressed.UTF8Scale;\n\n      if (encodeScale == null) {\n        encodeScale = 1024;\n      }\n\n      var features = jsonCompressed.features;\n      each(features, function (feature) {\n        var geometry = feature.geometry;\n        var encodeOffsets = geometry.encodeOffsets;\n        var coordinates = geometry.coordinates; // Geometry may be appeded manually in the script after json loaded.\n        // In this case this geometry is usually not encoded.\n\n        if (!encodeOffsets) {\n          return;\n        }\n\n        switch (geometry.type) {\n          case 'LineString':\n            geometry.coordinates = decodeRing(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'Polygon':\n            decodeRings(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'MultiLineString':\n            decodeRings(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'MultiPolygon':\n            each(coordinates, function (rings, idx) {\n              return decodeRings(rings, encodeOffsets[idx], encodeScale);\n            });\n        }\n      }); // Has been decoded\n\n      jsonCompressed.UTF8Encoding = false;\n      return jsonCompressed;\n    }\n\n    function decodeRings(rings, encodeOffsets, encodeScale) {\n      for (var c = 0; c < rings.length; c++) {\n        rings[c] = decodeRing(rings[c], encodeOffsets[c], encodeScale);\n      }\n    }\n\n    function decodeRing(coordinate, encodeOffsets, encodeScale) {\n      var result = [];\n      var prevX = encodeOffsets[0];\n      var prevY = encodeOffsets[1];\n\n      for (var i = 0; i < coordinate.length; i += 2) {\n        var x = coordinate.charCodeAt(i) - 64;\n        var y = coordinate.charCodeAt(i + 1) - 64; // ZigZag decoding\n\n        x = x >> 1 ^ -(x & 1);\n        y = y >> 1 ^ -(y & 1); // Delta deocding\n\n        x += prevX;\n        y += prevY;\n        prevX = x;\n        prevY = y; // Dequantize\n\n        result.push([x / encodeScale, y / encodeScale]);\n      }\n\n      return result;\n    }\n\n    function parseGeoJSON(geoJson, nameProperty) {\n      geoJson = decode(geoJson);\n      return map(filter(geoJson.features, function (featureObj) {\n        // Output of mapshaper may have geometry null\n        return featureObj.geometry && featureObj.properties && featureObj.geometry.coordinates.length > 0;\n      }), function (featureObj) {\n        var properties = featureObj.properties;\n        var geo = featureObj.geometry;\n        var geometries = [];\n\n        switch (geo.type) {\n          case 'Polygon':\n            var coordinates = geo.coordinates; // According to the GeoJSON specification.\n            // First must be exterior, and the rest are all interior(holes).\n\n            geometries.push(new GeoJSONPolygonGeometry(coordinates[0], coordinates.slice(1)));\n            break;\n\n          case 'MultiPolygon':\n            each(geo.coordinates, function (item) {\n              if (item[0]) {\n                geometries.push(new GeoJSONPolygonGeometry(item[0], item.slice(1)));\n              }\n            });\n            break;\n\n          case 'LineString':\n            geometries.push(new GeoJSONLineStringGeometry([geo.coordinates]));\n            break;\n\n          case 'MultiLineString':\n            geometries.push(new GeoJSONLineStringGeometry(geo.coordinates));\n        }\n\n        var region = new GeoJSONRegion(properties[nameProperty || 'name'], geometries, properties.cp);\n        region.properties = properties;\n        return region;\n      });\n    }\n\n    var number = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        linearMap: linearMap,\n        round: round,\n        asc: asc,\n        getPrecision: getPrecision,\n        getPrecisionSafe: getPrecisionSafe,\n        getPixelPrecision: getPixelPrecision,\n        getPercentWithPrecision: getPercentWithPrecision,\n        MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,\n        remRadian: remRadian,\n        isRadianAroundZero: isRadianAroundZero,\n        parseDate: parseDate,\n        quantity: quantity,\n        quantityExponent: quantityExponent,\n        nice: nice,\n        quantile: quantile,\n        reformIntervals: reformIntervals,\n        isNumeric: isNumeric,\n        numericToNumber: numericToNumber\n    });\n\n    var time = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        parse: parseDate,\n        format: format\n    });\n\n    var graphic$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        extendShape: extendShape,\n        extendPath: extendPath,\n        makePath: makePath,\n        makeImage: makeImage,\n        mergePath: mergePath$1,\n        resizePath: resizePath,\n        createIcon: createIcon,\n        updateProps: updateProps,\n        initProps: initProps,\n        getTransform: getTransform,\n        clipPointsByRect: clipPointsByRect,\n        clipRectByRect: clipRectByRect,\n        registerShape: registerShape,\n        getShapeClass: getShapeClass,\n        Group: Group,\n        Image: ZRImage,\n        Text: ZRText,\n        Circle: Circle,\n        Ellipse: Ellipse,\n        Sector: Sector,\n        Ring: Ring,\n        Polygon: Polygon,\n        Polyline: Polyline,\n        Rect: Rect,\n        Line: Line,\n        BezierCurve: BezierCurve,\n        Arc: Arc,\n        IncrementalDisplayable: IncrementalDisplayable,\n        CompoundPath: CompoundPath,\n        LinearGradient: LinearGradient,\n        RadialGradient: RadialGradient,\n        BoundingRect: BoundingRect\n    });\n\n    var format$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        addCommas: addCommas,\n        toCamelCase: toCamelCase,\n        normalizeCssArray: normalizeCssArray$1,\n        encodeHTML: encodeHTML,\n        formatTpl: formatTpl,\n        getTooltipMarker: getTooltipMarker,\n        formatTime: formatTime,\n        capitalFirst: capitalFirst,\n        truncateText: truncateText,\n        getTextRect: getTextRect\n    });\n\n    var util$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        map: map,\n        each: each,\n        indexOf: indexOf,\n        inherits: inherits,\n        reduce: reduce,\n        filter: filter,\n        bind: bind,\n        curry: curry,\n        isArray: isArray,\n        isString: isString,\n        isObject: isObject,\n        isFunction: isFunction,\n        extend: extend,\n        defaults: defaults,\n        clone: clone,\n        merge: merge\n    });\n\n    var inner$5 = makeInner();\n    function createAxisLabels(axis) {\n      // Only ordinal scale support tick interval\n      return axis.type === 'category' ? makeCategoryLabels(axis) : makeRealNumberLabels(axis);\n    }\n    /**\n     * @param {module:echats/coord/Axis} axis\n     * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea.\n     * @return {Object} {\n     *     ticks: Array.<number>\n     *     tickCategoryInterval: number\n     * }\n     */\n\n    function createAxisTicks(axis, tickModel) {\n      // Only ordinal scale support tick interval\n      return axis.type === 'category' ? makeCategoryTicks(axis, tickModel) : {\n        ticks: map(axis.scale.getTicks(), function (tick) {\n          return tick.value;\n        })\n      };\n    }\n\n    function makeCategoryLabels(axis) {\n      var labelModel = axis.getLabelModel();\n      var result = makeCategoryLabelsActually(axis, labelModel);\n      return !labelModel.get('show') || axis.scale.isBlank() ? {\n        labels: [],\n        labelCategoryInterval: result.labelCategoryInterval\n      } : result;\n    }\n\n    function makeCategoryLabelsActually(axis, labelModel) {\n      var labelsCache = getListCache(axis, 'labels');\n      var optionLabelInterval = getOptionCategoryInterval(labelModel);\n      var result = listCacheGet(labelsCache, optionLabelInterval);\n\n      if (result) {\n        return result;\n      }\n\n      var labels;\n      var numericLabelInterval;\n\n      if (isFunction(optionLabelInterval)) {\n        labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval);\n      } else {\n        numericLabelInterval = optionLabelInterval === 'auto' ? makeAutoCategoryInterval(axis) : optionLabelInterval;\n        labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval);\n      } // Cache to avoid calling interval function repeatedly.\n\n\n      return listCacheSet(labelsCache, optionLabelInterval, {\n        labels: labels,\n        labelCategoryInterval: numericLabelInterval\n      });\n    }\n\n    function makeCategoryTicks(axis, tickModel) {\n      var ticksCache = getListCache(axis, 'ticks');\n      var optionTickInterval = getOptionCategoryInterval(tickModel);\n      var result = listCacheGet(ticksCache, optionTickInterval);\n\n      if (result) {\n        return result;\n      }\n\n      var ticks;\n      var tickCategoryInterval; // Optimize for the case that large category data and no label displayed,\n      // we should not return all ticks.\n\n      if (!tickModel.get('show') || axis.scale.isBlank()) {\n        ticks = [];\n      }\n\n      if (isFunction(optionTickInterval)) {\n        ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true);\n      } // Always use label interval by default despite label show. Consider this\n      // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows\n      // labels. `splitLine` and `axisTick` should be consistent in this case.\n      else if (optionTickInterval === 'auto') {\n          var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel());\n          tickCategoryInterval = labelsResult.labelCategoryInterval;\n          ticks = map(labelsResult.labels, function (labelItem) {\n            return labelItem.tickValue;\n          });\n        } else {\n          tickCategoryInterval = optionTickInterval;\n          ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true);\n        } // Cache to avoid calling interval function repeatedly.\n\n\n      return listCacheSet(ticksCache, optionTickInterval, {\n        ticks: ticks,\n        tickCategoryInterval: tickCategoryInterval\n      });\n    }\n\n    function makeRealNumberLabels(axis) {\n      var ticks = axis.scale.getTicks();\n      var labelFormatter = makeLabelFormatter(axis);\n      return {\n        labels: map(ticks, function (tick, idx) {\n          return {\n            level: tick.level,\n            formattedLabel: labelFormatter(tick, idx),\n            rawLabel: axis.scale.getLabel(tick),\n            tickValue: tick.value\n          };\n        })\n      };\n    }\n\n    function getListCache(axis, prop) {\n      // Because key can be a function, and cache size always is small, we use array cache.\n      return inner$5(axis)[prop] || (inner$5(axis)[prop] = []);\n    }\n\n    function listCacheGet(cache, key) {\n      for (var i = 0; i < cache.length; i++) {\n        if (cache[i].key === key) {\n          return cache[i].value;\n        }\n      }\n    }\n\n    function listCacheSet(cache, key, value) {\n      cache.push({\n        key: key,\n        value: value\n      });\n      return value;\n    }\n\n    function makeAutoCategoryInterval(axis) {\n      var result = inner$5(axis).autoInterval;\n      return result != null ? result : inner$5(axis).autoInterval = axis.calculateCategoryInterval();\n    }\n    /**\n     * Calculate interval for category axis ticks and labels.\n     * To get precise result, at least one of `getRotate` and `isHorizontal`\n     * should be implemented in axis.\n     */\n\n\n    function calculateCategoryInterval(axis) {\n      var params = fetchAutoCategoryIntervalCalculationParams(axis);\n      var labelFormatter = makeLabelFormatter(axis);\n      var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI;\n      var ordinalScale = axis.scale;\n      var ordinalExtent = ordinalScale.getExtent(); // Providing this method is for optimization:\n      // avoid generating a long array by `getTicks`\n      // in large category data case.\n\n      var tickCount = ordinalScale.count();\n\n      if (ordinalExtent[1] - ordinalExtent[0] < 1) {\n        return 0;\n      }\n\n      var step = 1; // Simple optimization. Empirical value: tick count should less than 40.\n\n      if (tickCount > 40) {\n        step = Math.max(1, Math.floor(tickCount / 40));\n      }\n\n      var tickValue = ordinalExtent[0];\n      var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);\n      var unitW = Math.abs(unitSpan * Math.cos(rotation));\n      var unitH = Math.abs(unitSpan * Math.sin(rotation));\n      var maxW = 0;\n      var maxH = 0; // Caution: Performance sensitive for large category data.\n      // Consider dataZoom, we should make appropriate step to avoid O(n) loop.\n\n      for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n        var width = 0;\n        var height = 0; // Not precise, do not consider align and vertical align\n        // and each distance from axis line yet.\n\n        var rect = getBoundingRect(labelFormatter({\n          value: tickValue\n        }), params.font, 'center', 'top'); // Magic number\n\n        width = rect.width * 1.3;\n        height = rect.height * 1.3; // Min size, void long loop.\n\n        maxW = Math.max(maxW, width, 7);\n        maxH = Math.max(maxH, height, 7);\n      }\n\n      var dw = maxW / unitW;\n      var dh = maxH / unitH; // 0/0 is NaN, 1/0 is Infinity.\n\n      isNaN(dw) && (dw = Infinity);\n      isNaN(dh) && (dh = Infinity);\n      var interval = Math.max(0, Math.floor(Math.min(dw, dh)));\n      var cache = inner$5(axis.model);\n      var axisExtent = axis.getExtent();\n      var lastAutoInterval = cache.lastAutoInterval;\n      var lastTickCount = cache.lastTickCount; // Use cache to keep interval stable while moving zoom window,\n      // otherwise the calculated interval might jitter when the zoom\n      // window size is close to the interval-changing size.\n      // For example, if all of the axis labels are `a, b, c, d, e, f, g`.\n      // The jitter will cause that sometimes the displayed labels are\n      // `a, d, g` (interval: 2) sometimes `a, c, e`(interval: 1).\n\n      if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 // Always choose the bigger one, otherwise the critical\n      // point is not the same when zooming in or zooming out.\n      && lastAutoInterval > interval // If the axis change is caused by chart resize, the cache should not\n      // be used. Otherwise some hidden labels might not be shown again.\n      && cache.axisExtent0 === axisExtent[0] && cache.axisExtent1 === axisExtent[1]) {\n        interval = lastAutoInterval;\n      } // Only update cache if cache not used, otherwise the\n      // changing of interval is too insensitive.\n      else {\n          cache.lastTickCount = tickCount;\n          cache.lastAutoInterval = interval;\n          cache.axisExtent0 = axisExtent[0];\n          cache.axisExtent1 = axisExtent[1];\n        }\n\n      return interval;\n    }\n\n    function fetchAutoCategoryIntervalCalculationParams(axis) {\n      var labelModel = axis.getLabelModel();\n      return {\n        axisRotate: axis.getRotate ? axis.getRotate() : axis.isHorizontal && !axis.isHorizontal() ? 90 : 0,\n        labelRotate: labelModel.get('rotate') || 0,\n        font: labelModel.getFont()\n      };\n    }\n\n    function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) {\n      var labelFormatter = makeLabelFormatter(axis);\n      var ordinalScale = axis.scale;\n      var ordinalExtent = ordinalScale.getExtent();\n      var labelModel = axis.getLabelModel();\n      var result = []; // TODO: axisType: ordinalTime, pick the tick from each month/day/year/...\n\n      var step = Math.max((categoryInterval || 0) + 1, 1);\n      var startTick = ordinalExtent[0];\n      var tickCount = ordinalScale.count(); // Calculate start tick based on zero if possible to keep label consistent\n      // while zooming and moving while interval > 0. Otherwise the selection\n      // of displayable ticks and symbols probably keep changing.\n      // 3 is empirical value.\n\n      if (startTick !== 0 && step > 1 && tickCount / step > 2) {\n        startTick = Math.round(Math.ceil(startTick / step) * step);\n      } // (1) Only add min max label here but leave overlap checking\n      // to render stage, which also ensure the returned list\n      // suitable for splitLine and splitArea rendering.\n      // (2) Scales except category always contain min max label so\n      // do not need to perform this process.\n\n\n      var showAllLabel = shouldShowAllLabels(axis);\n      var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel;\n      var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel;\n\n      if (includeMinLabel && startTick !== ordinalExtent[0]) {\n        addItem(ordinalExtent[0]);\n      } // Optimize: avoid generating large array by `ordinalScale.getTicks()`.\n\n\n      var tickValue = startTick;\n\n      for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n        addItem(tickValue);\n      }\n\n      if (includeMaxLabel && tickValue - step !== ordinalExtent[1]) {\n        addItem(ordinalExtent[1]);\n      }\n\n      function addItem(tickValue) {\n        var tickObj = {\n          value: tickValue\n        };\n        result.push(onlyTick ? tickValue : {\n          formattedLabel: labelFormatter(tickObj),\n          rawLabel: ordinalScale.getLabel(tickObj),\n          tickValue: tickValue\n        });\n      }\n\n      return result;\n    }\n\n    function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) {\n      var ordinalScale = axis.scale;\n      var labelFormatter = makeLabelFormatter(axis);\n      var result = [];\n      each(ordinalScale.getTicks(), function (tick) {\n        var rawLabel = ordinalScale.getLabel(tick);\n        var tickValue = tick.value;\n\n        if (categoryInterval(tick.value, rawLabel)) {\n          result.push(onlyTick ? tickValue : {\n            formattedLabel: labelFormatter(tick),\n            rawLabel: rawLabel,\n            tickValue: tickValue\n          });\n        }\n      });\n      return result;\n    }\n\n    var NORMALIZED_EXTENT = [0, 1];\n    /**\n     * Base class of Axis.\n     */\n\n    var Axis =\n    /** @class */\n    function () {\n      function Axis(dim, scale, extent) {\n        this.onBand = false;\n        this.inverse = false;\n        this.dim = dim;\n        this.scale = scale;\n        this._extent = extent || [0, 0];\n      }\n      /**\n       * If axis extent contain given coord\n       */\n\n\n      Axis.prototype.contain = function (coord) {\n        var extent = this._extent;\n        var min = Math.min(extent[0], extent[1]);\n        var max = Math.max(extent[0], extent[1]);\n        return coord >= min && coord <= max;\n      };\n      /**\n       * If axis extent contain given data\n       */\n\n\n      Axis.prototype.containData = function (data) {\n        return this.scale.contain(data);\n      };\n      /**\n       * Get coord extent.\n       */\n\n\n      Axis.prototype.getExtent = function () {\n        return this._extent.slice();\n      };\n      /**\n       * Get precision used for formatting\n       */\n\n\n      Axis.prototype.getPixelPrecision = function (dataExtent) {\n        return getPixelPrecision(dataExtent || this.scale.getExtent(), this._extent);\n      };\n      /**\n       * Set coord extent\n       */\n\n\n      Axis.prototype.setExtent = function (start, end) {\n        var extent = this._extent;\n        extent[0] = start;\n        extent[1] = end;\n      };\n      /**\n       * Convert data to coord. Data is the rank if it has an ordinal scale\n       */\n\n\n      Axis.prototype.dataToCoord = function (data, clamp) {\n        var extent = this._extent;\n        var scale = this.scale;\n        data = scale.normalize(data);\n\n        if (this.onBand && scale.type === 'ordinal') {\n          extent = extent.slice();\n          fixExtentWithBands(extent, scale.count());\n        }\n\n        return linearMap(data, NORMALIZED_EXTENT, extent, clamp);\n      };\n      /**\n       * Convert coord to data. Data is the rank if it has an ordinal scale\n       */\n\n\n      Axis.prototype.coordToData = function (coord, clamp) {\n        var extent = this._extent;\n        var scale = this.scale;\n\n        if (this.onBand && scale.type === 'ordinal') {\n          extent = extent.slice();\n          fixExtentWithBands(extent, scale.count());\n        }\n\n        var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp);\n        return this.scale.scale(t);\n      };\n      /**\n       * Convert pixel point to data in axis\n       */\n\n\n      Axis.prototype.pointToData = function (point, clamp) {\n        // Should be implemented in derived class if necessary.\n        return;\n      };\n      /**\n       * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`,\n       * `axis.getTicksCoords` considers `onBand`, which is used by\n       * `boundaryGap:true` of category axis and splitLine and splitArea.\n       * @param opt.tickModel default: axis.model.getModel('axisTick')\n       * @param opt.clamp If `true`, the first and the last\n       *        tick must be at the axis end points. Otherwise, clip ticks\n       *        that outside the axis extent.\n       */\n\n\n      Axis.prototype.getTicksCoords = function (opt) {\n        opt = opt || {};\n        var tickModel = opt.tickModel || this.getTickModel();\n        var result = createAxisTicks(this, tickModel);\n        var ticks = result.ticks;\n        var ticksCoords = map(ticks, function (tickVal) {\n          return {\n            coord: this.dataToCoord(this.scale.type === 'ordinal' ? this.scale.getRawOrdinalNumber(tickVal) : tickVal),\n            tickValue: tickVal\n          };\n        }, this);\n        var alignWithLabel = tickModel.get('alignWithLabel');\n        fixOnBandTicksCoords(this, ticksCoords, alignWithLabel, opt.clamp);\n        return ticksCoords;\n      };\n\n      Axis.prototype.getMinorTicksCoords = function () {\n        if (this.scale.type === 'ordinal') {\n          // Category axis doesn't support minor ticks\n          return [];\n        }\n\n        var minorTickModel = this.model.getModel('minorTick');\n        var splitNumber = minorTickModel.get('splitNumber'); // Protection.\n\n        if (!(splitNumber > 0 && splitNumber < 100)) {\n          splitNumber = 5;\n        }\n\n        var minorTicks = this.scale.getMinorTicks(splitNumber);\n        var minorTicksCoords = map(minorTicks, function (minorTicksGroup) {\n          return map(minorTicksGroup, function (minorTick) {\n            return {\n              coord: this.dataToCoord(minorTick),\n              tickValue: minorTick\n            };\n          }, this);\n        }, this);\n        return minorTicksCoords;\n      };\n\n      Axis.prototype.getViewLabels = function () {\n        return createAxisLabels(this).labels;\n      };\n\n      Axis.prototype.getLabelModel = function () {\n        return this.model.getModel('axisLabel');\n      };\n      /**\n       * Notice here we only get the default tick model. For splitLine\n       * or splitArea, we should pass the splitLineModel or splitAreaModel\n       * manually when calling `getTicksCoords`.\n       * In GL, this method may be overridden to:\n       * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`\n       */\n\n\n      Axis.prototype.getTickModel = function () {\n        return this.model.getModel('axisTick');\n      };\n      /**\n       * Get width of band\n       */\n\n\n      Axis.prototype.getBandWidth = function () {\n        var axisExtent = this._extent;\n        var dataExtent = this.scale.getExtent();\n        var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); // Fix #2728, avoid NaN when only one data.\n\n        len === 0 && (len = 1);\n        var size = Math.abs(axisExtent[1] - axisExtent[0]);\n        return Math.abs(size) / len;\n      };\n      /**\n       * Only be called in category axis.\n       * Can be overridden, consider other axes like in 3D.\n       * @return Auto interval for cateogry axis tick and label\n       */\n\n\n      Axis.prototype.calculateCategoryInterval = function () {\n        return calculateCategoryInterval(this);\n      };\n\n      return Axis;\n    }();\n\n    function fixExtentWithBands(extent, nTick) {\n      var size = extent[1] - extent[0];\n      var len = nTick;\n      var margin = size / len / 2;\n      extent[0] += margin;\n      extent[1] -= margin;\n    } // If axis has labels [1, 2, 3, 4]. Bands on the axis are\n    // |---1---|---2---|---3---|---4---|.\n    // So the displayed ticks and splitLine/splitArea should between\n    // each data item, otherwise cause misleading (e.g., split tow bars\n    // of a single data item when there are two bar series).\n    // Also consider if tickCategoryInterval > 0 and onBand, ticks and\n    // splitLine/spliteArea should layout appropriately corresponding\n    // to displayed labels. (So we should not use `getBandWidth` in this\n    // case).\n\n\n    function fixOnBandTicksCoords(axis, ticksCoords, alignWithLabel, clamp) {\n      var ticksLen = ticksCoords.length;\n\n      if (!axis.onBand || alignWithLabel || !ticksLen) {\n        return;\n      }\n\n      var axisExtent = axis.getExtent();\n      var last;\n      var diffSize;\n\n      if (ticksLen === 1) {\n        ticksCoords[0].coord = axisExtent[0];\n        last = ticksCoords[1] = {\n          coord: axisExtent[0]\n        };\n      } else {\n        var crossLen = ticksCoords[ticksLen - 1].tickValue - ticksCoords[0].tickValue;\n        var shift_1 = (ticksCoords[ticksLen - 1].coord - ticksCoords[0].coord) / crossLen;\n        each(ticksCoords, function (ticksItem) {\n          ticksItem.coord -= shift_1 / 2;\n        });\n        var dataExtent = axis.scale.getExtent();\n        diffSize = 1 + dataExtent[1] - ticksCoords[ticksLen - 1].tickValue;\n        last = {\n          coord: ticksCoords[ticksLen - 1].coord + shift_1 * diffSize\n        };\n        ticksCoords.push(last);\n      }\n\n      var inverse = axisExtent[0] > axisExtent[1]; // Handling clamp.\n\n      if (littleThan(ticksCoords[0].coord, axisExtent[0])) {\n        clamp ? ticksCoords[0].coord = axisExtent[0] : ticksCoords.shift();\n      }\n\n      if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) {\n        ticksCoords.unshift({\n          coord: axisExtent[0]\n        });\n      }\n\n      if (littleThan(axisExtent[1], last.coord)) {\n        clamp ? last.coord = axisExtent[1] : ticksCoords.pop();\n      }\n\n      if (clamp && littleThan(last.coord, axisExtent[1])) {\n        ticksCoords.push({\n          coord: axisExtent[1]\n        });\n      }\n\n      function littleThan(a, b) {\n        // Avoid rounding error cause calculated tick coord different with extent.\n        // It may cause an extra unnecessary tick added.\n        a = round(a);\n        b = round(b);\n        return inverse ? a > b : a < b;\n      }\n    }\n\n    // Should use `ComponentModel.extend` or `class XXXX extend ComponentModel` to create class.\n    // Then use `registerComponentModel` in `install` parameter when `use` this extension. For example:\n    // class Bar3DModel extends ComponentModel {}\n    // export function install(registers) { registers.registerComponentModel(Bar3DModel); }\n    // echarts.use(install);\n\n    function extendComponentModel(proto) {\n      var Model = ComponentModel.extend(proto);\n      ComponentModel.registerClass(Model);\n      return Model;\n    }\n    function extendComponentView(proto) {\n      var View = ComponentView.extend(proto);\n      ComponentView.registerClass(View);\n      return View;\n    }\n    function extendSeriesModel(proto) {\n      var Model = SeriesModel.extend(proto);\n      SeriesModel.registerClass(Model);\n      return Model;\n    }\n    function extendChartView(proto) {\n      var View = ChartView.extend(proto);\n      ChartView.registerClass(View);\n      return View;\n    }\n\n    var PI2$6 = Math.PI * 2;\n    var CMD$3 = PathProxy.CMD;\n    var DEFAULT_SEARCH_SPACE = ['top', 'right', 'bottom', 'left'];\n\n    function getCandidateAnchor(pos, distance, rect, outPt, outDir) {\n      var width = rect.width;\n      var height = rect.height;\n\n      switch (pos) {\n        case 'top':\n          outPt.set(rect.x + width / 2, rect.y - distance);\n          outDir.set(0, -1);\n          break;\n\n        case 'bottom':\n          outPt.set(rect.x + width / 2, rect.y + height + distance);\n          outDir.set(0, 1);\n          break;\n\n        case 'left':\n          outPt.set(rect.x - distance, rect.y + height / 2);\n          outDir.set(-1, 0);\n          break;\n\n        case 'right':\n          outPt.set(rect.x + width + distance, rect.y + height / 2);\n          outDir.set(1, 0);\n          break;\n      }\n    }\n\n    function projectPointToArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y, out) {\n      x -= cx;\n      y -= cy;\n      var d = Math.sqrt(x * x + y * y);\n      x /= d;\n      y /= d; // Intersect point.\n\n      var ox = x * r + cx;\n      var oy = y * r + cy;\n\n      if (Math.abs(startAngle - endAngle) % PI2$6 < 1e-4) {\n        // Is a circle\n        out[0] = ox;\n        out[1] = oy;\n        return d - r;\n      }\n\n      if (anticlockwise) {\n        var tmp = startAngle;\n        startAngle = normalizeRadian(endAngle);\n        endAngle = normalizeRadian(tmp);\n      } else {\n        startAngle = normalizeRadian(startAngle);\n        endAngle = normalizeRadian(endAngle);\n      }\n\n      if (startAngle > endAngle) {\n        endAngle += PI2$6;\n      }\n\n      var angle = Math.atan2(y, x);\n\n      if (angle < 0) {\n        angle += PI2$6;\n      }\n\n      if (angle >= startAngle && angle <= endAngle || angle + PI2$6 >= startAngle && angle + PI2$6 <= endAngle) {\n        // Project point is on the arc.\n        out[0] = ox;\n        out[1] = oy;\n        return d - r;\n      }\n\n      var x1 = r * Math.cos(startAngle) + cx;\n      var y1 = r * Math.sin(startAngle) + cy;\n      var x2 = r * Math.cos(endAngle) + cx;\n      var y2 = r * Math.sin(endAngle) + cy;\n      var d1 = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);\n      var d2 = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y);\n\n      if (d1 < d2) {\n        out[0] = x1;\n        out[1] = y1;\n        return Math.sqrt(d1);\n      } else {\n        out[0] = x2;\n        out[1] = y2;\n        return Math.sqrt(d2);\n      }\n    }\n\n    function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) {\n      var dx = x - x1;\n      var dy = y - y1;\n      var dx1 = x2 - x1;\n      var dy1 = y2 - y1;\n      var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n      dx1 /= lineLen;\n      dy1 /= lineLen; // dot product\n\n      var projectedLen = dx * dx1 + dy * dy1;\n      var t = projectedLen / lineLen;\n\n      if (limitToEnds) {\n        t = Math.min(Math.max(t, 0), 1);\n      }\n\n      t *= lineLen;\n      var ox = out[0] = x1 + t * dx1;\n      var oy = out[1] = y1 + t * dy1;\n      return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));\n    }\n\n    function projectPointToRect(x1, y1, width, height, x, y, out) {\n      if (width < 0) {\n        x1 = x1 + width;\n        width = -width;\n      }\n\n      if (height < 0) {\n        y1 = y1 + height;\n        height = -height;\n      }\n\n      var x2 = x1 + width;\n      var y2 = y1 + height;\n      var ox = out[0] = Math.min(Math.max(x, x1), x2);\n      var oy = out[1] = Math.min(Math.max(y, y1), y2);\n      return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));\n    }\n\n    var tmpPt = [];\n\n    function nearestPointOnRect(pt, rect, out) {\n      var dist = projectPointToRect(rect.x, rect.y, rect.width, rect.height, pt.x, pt.y, tmpPt);\n      out.set(tmpPt[0], tmpPt[1]);\n      return dist;\n    }\n    /**\n     * Calculate min distance corresponding point.\n     * This method won't evaluate if point is in the path.\n     */\n\n\n    function nearestPointOnPath(pt, path, out) {\n      var xi = 0;\n      var yi = 0;\n      var x0 = 0;\n      var y0 = 0;\n      var x1;\n      var y1;\n      var minDist = Infinity;\n      var data = path.data;\n      var x = pt.x;\n      var y = pt.y;\n\n      for (var i = 0; i < data.length;) {\n        var cmd = data[i++];\n\n        if (i === 1) {\n          xi = data[i];\n          yi = data[i + 1];\n          x0 = xi;\n          y0 = yi;\n        }\n\n        var d = minDist;\n\n        switch (cmd) {\n          case CMD$3.M:\n            // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点\n            // 在 closePath 的时候使用\n            x0 = data[i++];\n            y0 = data[i++];\n            xi = x0;\n            yi = y0;\n            break;\n\n          case CMD$3.L:\n            d = projectPointToLine(xi, yi, data[i], data[i + 1], x, y, tmpPt, true);\n            xi = data[i++];\n            yi = data[i++];\n            break;\n\n          case CMD$3.C:\n            d = cubicProjectPoint(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt);\n            xi = data[i++];\n            yi = data[i++];\n            break;\n\n          case CMD$3.Q:\n            d = quadraticProjectPoint(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt);\n            xi = data[i++];\n            yi = data[i++];\n            break;\n\n          case CMD$3.A:\n            // TODO Arc 判断的开销比较大\n            var cx = data[i++];\n            var cy = data[i++];\n            var rx = data[i++];\n            var ry = data[i++];\n            var theta = data[i++];\n            var dTheta = data[i++]; // TODO Arc 旋转\n\n            i += 1;\n            var anticlockwise = !!(1 - data[i++]);\n            x1 = Math.cos(theta) * rx + cx;\n            y1 = Math.sin(theta) * ry + cy; // 不是直接使用 arc 命令\n\n            if (i <= 1) {\n              // 第一个命令起点还未定义\n              x0 = x1;\n              y0 = y1;\n            } // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放\n\n\n            var _x = (x - cx) * ry / rx + cx;\n\n            d = projectPointToArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y, tmpPt);\n            xi = Math.cos(theta + dTheta) * rx + cx;\n            yi = Math.sin(theta + dTheta) * ry + cy;\n            break;\n\n          case CMD$3.R:\n            x0 = xi = data[i++];\n            y0 = yi = data[i++];\n            var width = data[i++];\n            var height = data[i++];\n            d = projectPointToRect(x0, y0, width, height, x, y, tmpPt);\n            break;\n\n          case CMD$3.Z:\n            d = projectPointToLine(xi, yi, x0, y0, x, y, tmpPt, true);\n            xi = x0;\n            yi = y0;\n            break;\n        }\n\n        if (d < minDist) {\n          minDist = d;\n          out.set(tmpPt[0], tmpPt[1]);\n        }\n      }\n\n      return minDist;\n    } // Temporal variable for intermediate usage.\n\n\n    var pt0 = new Point();\n    var pt1 = new Point();\n    var pt2 = new Point();\n    var dir = new Point();\n    var dir2 = new Point();\n    /**\n     * Calculate a proper guide line based on the label position and graphic element definition\n     * @param label\n     * @param labelRect\n     * @param target\n     * @param targetRect\n     */\n\n    function updateLabelLinePoints(target, labelLineModel) {\n      if (!target) {\n        return;\n      }\n\n      var labelLine = target.getTextGuideLine();\n      var label = target.getTextContent(); // Needs to create text guide in each charts.\n\n      if (!(label && labelLine)) {\n        return;\n      }\n\n      var labelGuideConfig = target.textGuideLineConfig || {};\n      var points = [[0, 0], [0, 0], [0, 0]];\n      var searchSpace = labelGuideConfig.candidates || DEFAULT_SEARCH_SPACE;\n      var labelRect = label.getBoundingRect().clone();\n      labelRect.applyTransform(label.getComputedTransform());\n      var minDist = Infinity;\n      var anchorPoint = labelGuideConfig.anchor;\n      var targetTransform = target.getComputedTransform();\n      var targetInversedTransform = targetTransform && invert([], targetTransform);\n      var len = labelLineModel.get('length2') || 0;\n\n      if (anchorPoint) {\n        pt2.copy(anchorPoint);\n      }\n\n      for (var i = 0; i < searchSpace.length; i++) {\n        var candidate = searchSpace[i];\n        getCandidateAnchor(candidate, 0, labelRect, pt0, dir);\n        Point.scaleAndAdd(pt1, pt0, dir, len); // Transform to target coord space.\n\n        pt1.transform(targetInversedTransform); // Note: getBoundingRect will ensure the `path` being created.\n\n        var boundingRect = target.getBoundingRect();\n        var dist = anchorPoint ? anchorPoint.distance(pt1) : target instanceof Path ? nearestPointOnPath(pt1, target.path, pt2) : nearestPointOnRect(pt1, boundingRect, pt2); // TODO pt2 is in the path\n\n        if (dist < minDist) {\n          minDist = dist; // Transform back to global space.\n\n          pt1.transform(targetTransform);\n          pt2.transform(targetTransform);\n          pt2.toArray(points[0]);\n          pt1.toArray(points[1]);\n          pt0.toArray(points[2]);\n        }\n      }\n\n      limitTurnAngle(points, labelLineModel.get('minTurnAngle'));\n      labelLine.setShape({\n        points: points\n      });\n    } // Temporal variable for the limitTurnAngle function\n\n    var tmpArr = [];\n    var tmpProjPoint = new Point();\n    /**\n     * Reduce the line segment attached to the label to limit the turn angle between two segments.\n     * @param linePoints\n     * @param minTurnAngle Radian of minimum turn angle. 0 - 180\n     */\n\n    function limitTurnAngle(linePoints, minTurnAngle) {\n      if (!(minTurnAngle <= 180 && minTurnAngle > 0)) {\n        return;\n      }\n\n      minTurnAngle = minTurnAngle / 180 * Math.PI; // The line points can be\n      //      /pt1----pt2 (label)\n      //     /\n      // pt0/\n\n      pt0.fromArray(linePoints[0]);\n      pt1.fromArray(linePoints[1]);\n      pt2.fromArray(linePoints[2]);\n      Point.sub(dir, pt0, pt1);\n      Point.sub(dir2, pt2, pt1);\n      var len1 = dir.len();\n      var len2 = dir2.len();\n\n      if (len1 < 1e-3 || len2 < 1e-3) {\n        return;\n      }\n\n      dir.scale(1 / len1);\n      dir2.scale(1 / len2);\n      var angleCos = dir.dot(dir2);\n      var minTurnAngleCos = Math.cos(minTurnAngle);\n\n      if (minTurnAngleCos < angleCos) {\n        // Smaller than minTurnAngle\n        // Calculate project point of pt0 on pt1-pt2\n        var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n        tmpProjPoint.fromArray(tmpArr); // Calculate new projected length with limited minTurnAngle and get the new connect point\n\n        tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n        var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n        if (isNaN(t)) {\n          return;\n        }\n\n        if (t < 0) {\n          Point.copy(tmpProjPoint, pt1);\n        } else if (t > 1) {\n          Point.copy(tmpProjPoint, pt2);\n        }\n\n        tmpProjPoint.toArray(linePoints[1]);\n      }\n    }\n    /**\n     * Limit the angle of line and the surface\n     * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite\n     */\n\n    function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) {\n      if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {\n        return;\n      }\n\n      maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI;\n      pt0.fromArray(linePoints[0]);\n      pt1.fromArray(linePoints[1]);\n      pt2.fromArray(linePoints[2]);\n      Point.sub(dir, pt1, pt0);\n      Point.sub(dir2, pt2, pt1);\n      var len1 = dir.len();\n      var len2 = dir2.len();\n\n      if (len1 < 1e-3 || len2 < 1e-3) {\n        return;\n      }\n\n      dir.scale(1 / len1);\n      dir2.scale(1 / len2);\n      var angleCos = dir.dot(surfaceNormal);\n      var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle);\n\n      if (angleCos < maxSurfaceAngleCos) {\n        // Calculate project point of pt0 on pt1-pt2\n        var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n        tmpProjPoint.fromArray(tmpArr);\n        var HALF_PI = Math.PI / 2;\n        var angle2 = Math.acos(dir2.dot(surfaceNormal));\n        var newAngle = HALF_PI + angle2 - maxSurfaceAngle;\n\n        if (newAngle >= HALF_PI) {\n          // parallel\n          Point.copy(tmpProjPoint, pt2);\n        } else {\n          // Calculate new projected length with limited minTurnAngle and get the new connect point\n          tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n          var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n          if (isNaN(t)) {\n            return;\n          }\n\n          if (t < 0) {\n            Point.copy(tmpProjPoint, pt1);\n          } else if (t > 1) {\n            Point.copy(tmpProjPoint, pt2);\n          }\n        }\n\n        tmpProjPoint.toArray(linePoints[1]);\n      }\n    }\n\n    function setLabelLineState(labelLine, ignore, stateName, stateModel) {\n      var isNormal = stateName === 'normal';\n      var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); // Make sure display.\n\n      stateObj.ignore = ignore; // Set smooth\n\n      var smooth = stateModel.get('smooth');\n\n      if (smooth && smooth === true) {\n        smooth = 0.3;\n      }\n\n      stateObj.shape = stateObj.shape || {};\n\n      if (smooth > 0) {\n        stateObj.shape.smooth = smooth;\n      }\n\n      var styleObj = stateModel.getModel('lineStyle').getLineStyle();\n      isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj;\n    }\n\n    function buildLabelLinePath(path, shape) {\n      var smooth = shape.smooth;\n      var points = shape.points;\n\n      if (!points) {\n        return;\n      }\n\n      path.moveTo(points[0][0], points[0][1]);\n\n      if (smooth > 0 && points.length >= 3) {\n        var len1 = dist(points[0], points[1]);\n        var len2 = dist(points[1], points[2]);\n\n        if (!len1 || !len2) {\n          path.lineTo(points[1][0], points[1][1]);\n          path.lineTo(points[2][0], points[2][1]);\n          return;\n        }\n\n        var moveLen = Math.min(len1, len2) * smooth;\n        var midPoint0 = lerp([], points[1], points[0], moveLen / len1);\n        var midPoint2 = lerp([], points[1], points[2], moveLen / len2);\n        var midPoint1 = lerp([], midPoint0, midPoint2, 0.5);\n        path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]);\n        path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]);\n      } else {\n        for (var i = 1; i < points.length; i++) {\n          path.lineTo(points[i][0], points[i][1]);\n        }\n      }\n    }\n    /**\n     * Create a label line if necessary and set it's style.\n     */\n\n\n    function setLabelLineStyle(targetEl, statesModels, defaultStyle) {\n      var labelLine = targetEl.getTextGuideLine();\n      var label = targetEl.getTextContent();\n\n      if (!label) {\n        // Not show label line if there is no label.\n        if (labelLine) {\n          targetEl.removeTextGuideLine();\n        }\n\n        return;\n      }\n\n      var normalModel = statesModels.normal;\n      var showNormal = normalModel.get('show');\n      var labelIgnoreNormal = label.ignore;\n\n      for (var i = 0; i < DISPLAY_STATES.length; i++) {\n        var stateName = DISPLAY_STATES[i];\n        var stateModel = statesModels[stateName];\n        var isNormal = stateName === 'normal';\n\n        if (stateModel) {\n          var stateShow = stateModel.get('show');\n          var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal);\n\n          if (isLabelIgnored // Not show when label is not shown in this state.\n          || !retrieve2(stateShow, showNormal) // Use normal state by default if not set.\n          ) {\n              var stateObj = isNormal ? labelLine : labelLine && labelLine.states[stateName];\n\n              if (stateObj) {\n                stateObj.ignore = true;\n              }\n\n              continue;\n            } // Create labelLine if not exists\n\n\n          if (!labelLine) {\n            labelLine = new Polyline();\n            targetEl.setTextGuideLine(labelLine); // Reset state of normal because it's new created.\n            // NOTE: NORMAL should always been the first!\n\n            if (!isNormal && (labelIgnoreNormal || !showNormal)) {\n              setLabelLineState(labelLine, true, 'normal', statesModels.normal);\n            } // Use same state proxy.\n\n\n            if (targetEl.stateProxy) {\n              labelLine.stateProxy = targetEl.stateProxy;\n            }\n          }\n\n          setLabelLineState(labelLine, false, stateName, stateModel);\n        }\n      }\n\n      if (labelLine) {\n        defaults(labelLine.style, defaultStyle); // Not fill.\n\n        labelLine.style.fill = null;\n        var showAbove = normalModel.get('showAbove');\n        var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {};\n        labelLineConfig.showAbove = showAbove || false; // Custom the buildPath.\n\n        labelLine.buildPath = buildLabelLinePath;\n      }\n    }\n    function getLabelLineStatesModels(itemModel, labelLineName) {\n      labelLineName = labelLineName || 'labelLine';\n      var statesModels = {\n        normal: itemModel.getModel(labelLineName)\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        statesModels[stateName] = itemModel.getModel([stateName, labelLineName]);\n      }\n\n      return statesModels;\n    }\n\n    function prepareLayoutList(input) {\n      var list = [];\n\n      for (var i = 0; i < input.length; i++) {\n        var rawItem = input[i];\n\n        if (rawItem.defaultAttr.ignore) {\n          continue;\n        }\n\n        var label = rawItem.label;\n        var transform = label.getComputedTransform(); // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el.\n\n        var localRect = label.getBoundingRect();\n        var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5;\n        var minMargin = label.style.margin || 0;\n        var globalRect = localRect.clone();\n        globalRect.applyTransform(transform);\n        globalRect.x -= minMargin / 2;\n        globalRect.y -= minMargin / 2;\n        globalRect.width += minMargin;\n        globalRect.height += minMargin;\n        var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null;\n        list.push({\n          label: label,\n          labelLine: rawItem.labelLine,\n          rect: globalRect,\n          localRect: localRect,\n          obb: obb,\n          priority: rawItem.priority,\n          defaultAttr: rawItem.defaultAttr,\n          layoutOption: rawItem.computedLayoutOption,\n          axisAligned: isAxisAligned,\n          transform: transform\n        });\n      }\n\n      return list;\n    }\n\n    function shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) {\n      var len = list.length;\n\n      if (len < 2) {\n        return;\n      }\n\n      list.sort(function (a, b) {\n        return a.rect[xyDim] - b.rect[xyDim];\n      });\n      var lastPos = 0;\n      var delta;\n      var adjusted = false;\n      var totalShifts = 0;\n\n      for (var i = 0; i < len; i++) {\n        var item = list[i];\n        var rect = item.rect;\n        delta = rect[xyDim] - lastPos;\n\n        if (delta < 0) {\n          // shiftForward(i, len, -delta);\n          rect[xyDim] -= delta;\n          item.label[xyDim] -= delta;\n          adjusted = true;\n        }\n\n        var shift = Math.max(-delta, 0);\n        totalShifts += shift;\n        lastPos = rect[xyDim] + rect[sizeDim];\n      }\n\n      if (totalShifts > 0 && balanceShift) {\n        // Shift back to make the distribution more equally.\n        shiftList(-totalShifts / len, 0, len);\n      } // TODO bleedMargin?\n\n\n      var first = list[0];\n      var last = list[len - 1];\n      var minGap;\n      var maxGap;\n      updateMinMaxGap(); // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds.\n\n      minGap < 0 && squeezeGaps(-minGap, 0.8);\n      maxGap < 0 && squeezeGaps(maxGap, 0.8);\n      updateMinMaxGap();\n      takeBoundsGap(minGap, maxGap, 1);\n      takeBoundsGap(maxGap, minGap, -1); // Handle bailout when there is not enough space.\n\n      updateMinMaxGap();\n\n      if (minGap < 0) {\n        squeezeWhenBailout(-minGap);\n      }\n\n      if (maxGap < 0) {\n        squeezeWhenBailout(maxGap);\n      }\n\n      function updateMinMaxGap() {\n        minGap = first.rect[xyDim] - minBound;\n        maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim];\n      }\n\n      function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) {\n        if (gapThisBound < 0) {\n          // Move from other gap if can.\n          var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound);\n\n          if (moveFromMaxGap > 0) {\n            shiftList(moveFromMaxGap * moveDir, 0, len);\n            var remained = moveFromMaxGap + gapThisBound;\n\n            if (remained < 0) {\n              squeezeGaps(-remained * moveDir, 1);\n            }\n          } else {\n            squeezeGaps(-gapThisBound * moveDir, 1);\n          }\n        }\n      }\n\n      function shiftList(delta, start, end) {\n        if (delta !== 0) {\n          adjusted = true;\n        }\n\n        for (var i = start; i < end; i++) {\n          var item = list[i];\n          var rect = item.rect;\n          rect[xyDim] += delta;\n          item.label[xyDim] += delta;\n        }\n      } // Squeeze gaps if the labels exceed margin.\n\n\n      function squeezeGaps(delta, maxSqeezePercent) {\n        var gaps = [];\n        var totalGaps = 0;\n\n        for (var i = 1; i < len; i++) {\n          var prevItemRect = list[i - 1].rect;\n          var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0);\n          gaps.push(gap);\n          totalGaps += gap;\n        }\n\n        if (!totalGaps) {\n          return;\n        }\n\n        var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent);\n\n        if (delta > 0) {\n          for (var i = 0; i < len - 1; i++) {\n            // Distribute the shift delta to all gaps.\n            var movement = gaps[i] * squeezePercent; // Forward\n\n            shiftList(movement, 0, i + 1);\n          }\n        } else {\n          // Backward\n          for (var i = len - 1; i > 0; i--) {\n            // Distribute the shift delta to all gaps.\n            var movement = gaps[i - 1] * squeezePercent;\n            shiftList(-movement, i, len);\n          }\n        }\n      }\n      /**\n       * Squeeze to allow overlap if there is no more space available.\n       * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds.\n       */\n\n\n      function squeezeWhenBailout(delta) {\n        var dir = delta < 0 ? -1 : 1;\n        delta = Math.abs(delta);\n        var moveForEachLabel = Math.ceil(delta / (len - 1));\n\n        for (var i = 0; i < len - 1; i++) {\n          if (dir > 0) {\n            // Forward\n            shiftList(moveForEachLabel, 0, i + 1);\n          } else {\n            // Backward\n            shiftList(-moveForEachLabel, len - i - 1, len);\n          }\n\n          delta -= moveForEachLabel;\n\n          if (delta <= 0) {\n            return;\n          }\n        }\n      }\n\n      return adjusted;\n    }\n    /**\n     * Adjust labels on x direction to avoid overlap.\n     */\n\n\n    function shiftLayoutOnX(list, leftBound, rightBound, // If average the shifts on all labels and add them to 0\n    // TODO: Not sure if should enable it.\n    // Pros: The angle of lines will distribute more equally\n    // Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly.\n    balanceShift) {\n      return shiftLayout(list, 'x', 'width', leftBound, rightBound, balanceShift);\n    }\n    /**\n     * Adjust labels on y direction to avoid overlap.\n     */\n\n    function shiftLayoutOnY(list, topBound, bottomBound, // If average the shifts on all labels and add them to 0\n    balanceShift) {\n      return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift);\n    }\n    function hideOverlap(labelList) {\n      var displayedLabels = []; // TODO, render overflow visible first, put in the displayedLabels.\n\n      labelList.sort(function (a, b) {\n        return b.priority - a.priority;\n      });\n      var globalRect = new BoundingRect(0, 0, 0, 0);\n\n      function hideEl(el) {\n        if (!el.ignore) {\n          // Show on emphasis.\n          var emphasisState = el.ensureState('emphasis');\n\n          if (emphasisState.ignore == null) {\n            emphasisState.ignore = false;\n          }\n        }\n\n        el.ignore = true;\n      }\n\n      for (var i = 0; i < labelList.length; i++) {\n        var labelItem = labelList[i];\n        var isAxisAligned = labelItem.axisAligned;\n        var localRect = labelItem.localRect;\n        var transform = labelItem.transform;\n        var label = labelItem.label;\n        var labelLine = labelItem.labelLine;\n        globalRect.copy(labelItem.rect); // Add a threshold because layout may be aligned precisely.\n\n        globalRect.width -= 0.1;\n        globalRect.height -= 0.1;\n        globalRect.x += 0.05;\n        globalRect.y += 0.05;\n        var obb = labelItem.obb;\n        var overlapped = false;\n\n        for (var j = 0; j < displayedLabels.length; j++) {\n          var existsTextCfg = displayedLabels[j]; // Fast rejection.\n\n          if (!globalRect.intersect(existsTextCfg.rect)) {\n            continue;\n          }\n\n          if (isAxisAligned && existsTextCfg.axisAligned) {\n            // Is overlapped\n            overlapped = true;\n            break;\n          }\n\n          if (!existsTextCfg.obb) {\n            // If self is not axis aligned. But other is.\n            existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform);\n          }\n\n          if (!obb) {\n            // If self is axis aligned. But other is not.\n            obb = new OrientedBoundingRect(localRect, transform);\n          }\n\n          if (obb.intersect(existsTextCfg.obb)) {\n            overlapped = true;\n            break;\n          }\n        } // TODO Callback to determine if this overlap should be handled?\n\n\n        if (overlapped) {\n          hideEl(label);\n          labelLine && hideEl(labelLine);\n        } else {\n          label.attr('ignore', labelItem.defaultAttr.ignore);\n          labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore);\n          displayedLabels.push(labelItem);\n        }\n      }\n    }\n\n    function cloneArr(points) {\n      if (points) {\n        var newPoints = [];\n\n        for (var i = 0; i < points.length; i++) {\n          newPoints.push(points[i].slice());\n        }\n\n        return newPoints;\n      }\n    }\n\n    function prepareLayoutCallbackParams(labelItem, hostEl) {\n      var label = labelItem.label;\n      var labelLine = hostEl && hostEl.getTextGuideLine();\n      return {\n        dataIndex: labelItem.dataIndex,\n        dataType: labelItem.dataType,\n        seriesIndex: labelItem.seriesModel.seriesIndex,\n        text: labelItem.label.style.text,\n        rect: labelItem.hostRect,\n        labelRect: labelItem.rect,\n        // x: labelAttr.x,\n        // y: labelAttr.y,\n        align: label.style.align,\n        verticalAlign: label.style.verticalAlign,\n        labelLinePoints: cloneArr(labelLine && labelLine.shape.points)\n      };\n    }\n\n    var LABEL_OPTION_TO_STYLE_KEYS = ['align', 'verticalAlign', 'width', 'height', 'fontSize'];\n    var dummyTransformable = new Transformable();\n    var labelLayoutInnerStore = makeInner();\n    var labelLineAnimationStore = makeInner();\n\n    function extendWithKeys(target, source, keys) {\n      for (var i = 0; i < keys.length; i++) {\n        var key = keys[i];\n\n        if (source[key] != null) {\n          target[key] = source[key];\n        }\n      }\n    }\n\n    var LABEL_LAYOUT_PROPS = ['x', 'y', 'rotation'];\n\n    var LabelManager =\n    /** @class */\n    function () {\n      function LabelManager() {\n        this._labelList = [];\n        this._chartViewList = [];\n      }\n\n      LabelManager.prototype.clearLabels = function () {\n        this._labelList = [];\n        this._chartViewList = [];\n      };\n      /**\n       * Add label to manager\n       */\n\n\n      LabelManager.prototype._addLabel = function (dataIndex, dataType, seriesModel, label, layoutOption) {\n        var labelStyle = label.style;\n        var hostEl = label.__hostTarget;\n        var textConfig = hostEl.textConfig || {}; // TODO: If label is in other state.\n\n        var labelTransform = label.getComputedTransform();\n        var labelRect = label.getBoundingRect().plain();\n        BoundingRect.applyTransform(labelRect, labelRect, labelTransform);\n\n        if (labelTransform) {\n          dummyTransformable.setLocalTransform(labelTransform);\n        } else {\n          // Identity transform.\n          dummyTransformable.x = dummyTransformable.y = dummyTransformable.rotation = dummyTransformable.originX = dummyTransformable.originY = 0;\n          dummyTransformable.scaleX = dummyTransformable.scaleY = 1;\n        }\n\n        var host = label.__hostTarget;\n        var hostRect;\n\n        if (host) {\n          hostRect = host.getBoundingRect().plain();\n          var transform = host.getComputedTransform();\n          BoundingRect.applyTransform(hostRect, hostRect, transform);\n        }\n\n        var labelGuide = hostRect && host.getTextGuideLine();\n\n        this._labelList.push({\n          label: label,\n          labelLine: labelGuide,\n          seriesModel: seriesModel,\n          dataIndex: dataIndex,\n          dataType: dataType,\n          layoutOption: layoutOption,\n          computedLayoutOption: null,\n          rect: labelRect,\n          hostRect: hostRect,\n          // Label with lower priority will be hidden when overlapped\n          // Use rect size as default priority\n          priority: hostRect ? hostRect.width * hostRect.height : 0,\n          // Save default label attributes.\n          // For restore if developers want get back to default value in callback.\n          defaultAttr: {\n            ignore: label.ignore,\n            labelGuideIgnore: labelGuide && labelGuide.ignore,\n            x: dummyTransformable.x,\n            y: dummyTransformable.y,\n            scaleX: dummyTransformable.scaleX,\n            scaleY: dummyTransformable.scaleY,\n            rotation: dummyTransformable.rotation,\n            style: {\n              x: labelStyle.x,\n              y: labelStyle.y,\n              align: labelStyle.align,\n              verticalAlign: labelStyle.verticalAlign,\n              width: labelStyle.width,\n              height: labelStyle.height,\n              fontSize: labelStyle.fontSize\n            },\n            cursor: label.cursor,\n            attachedPos: textConfig.position,\n            attachedRot: textConfig.rotation\n          }\n        });\n      };\n\n      LabelManager.prototype.addLabelsOfSeries = function (chartView) {\n        var _this = this;\n\n        this._chartViewList.push(chartView);\n\n        var seriesModel = chartView.__model;\n        var layoutOption = seriesModel.get('labelLayout');\n        /**\n         * Ignore layouting if it's not specified anything.\n         */\n\n        if (!(isFunction(layoutOption) || keys(layoutOption).length)) {\n          return;\n        }\n\n        chartView.group.traverse(function (child) {\n          if (child.ignore) {\n            return true; // Stop traverse descendants.\n          } // Only support label being hosted on graphic elements.\n\n\n          var textEl = child.getTextContent();\n          var ecData = getECData(child); // Can only attach the text on the element with dataIndex\n\n          if (textEl && !textEl.disableLabelLayout) {\n            _this._addLabel(ecData.dataIndex, ecData.dataType, seriesModel, textEl, layoutOption);\n          }\n        });\n      };\n\n      LabelManager.prototype.updateLayoutConfig = function (api) {\n        var width = api.getWidth();\n        var height = api.getHeight();\n\n        function createDragHandler(el, labelLineModel) {\n          return function () {\n            updateLabelLinePoints(el, labelLineModel);\n          };\n        }\n\n        for (var i = 0; i < this._labelList.length; i++) {\n          var labelItem = this._labelList[i];\n          var label = labelItem.label;\n          var hostEl = label.__hostTarget;\n          var defaultLabelAttr = labelItem.defaultAttr;\n          var layoutOption = void 0; // TODO A global layout option?\n\n          if (isFunction(labelItem.layoutOption)) {\n            layoutOption = labelItem.layoutOption(prepareLayoutCallbackParams(labelItem, hostEl));\n          } else {\n            layoutOption = labelItem.layoutOption;\n          }\n\n          layoutOption = layoutOption || {};\n          labelItem.computedLayoutOption = layoutOption;\n          var degreeToRadian = Math.PI / 180; // TODO hostEl should always exists.\n          // Or label should not have parent because the x, y is all in global space.\n\n          if (hostEl) {\n            hostEl.setTextConfig({\n              // Force to set local false.\n              local: false,\n              // Ignore position and rotation config on the host el if x or y is changed.\n              position: layoutOption.x != null || layoutOption.y != null ? null : defaultLabelAttr.attachedPos,\n              // Ignore rotation config on the host el if rotation is changed.\n              rotation: layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.attachedRot,\n              offset: [layoutOption.dx || 0, layoutOption.dy || 0]\n            });\n          }\n\n          var needsUpdateLabelLine = false;\n\n          if (layoutOption.x != null) {\n            // TODO width of chart view.\n            label.x = parsePercent$1(layoutOption.x, width);\n            label.setStyle('x', 0); // Ignore movement in style. TODO: origin.\n\n            needsUpdateLabelLine = true;\n          } else {\n            label.x = defaultLabelAttr.x;\n            label.setStyle('x', defaultLabelAttr.style.x);\n          }\n\n          if (layoutOption.y != null) {\n            // TODO height of chart view.\n            label.y = parsePercent$1(layoutOption.y, height);\n            label.setStyle('y', 0); // Ignore movement in style.\n\n            needsUpdateLabelLine = true;\n          } else {\n            label.y = defaultLabelAttr.y;\n            label.setStyle('y', defaultLabelAttr.style.y);\n          }\n\n          if (layoutOption.labelLinePoints) {\n            var guideLine = hostEl.getTextGuideLine();\n\n            if (guideLine) {\n              guideLine.setShape({\n                points: layoutOption.labelLinePoints\n              }); // Not update\n\n              needsUpdateLabelLine = false;\n            }\n          }\n\n          var labelLayoutStore = labelLayoutInnerStore(label);\n          labelLayoutStore.needsUpdateLabelLine = needsUpdateLabelLine;\n          label.rotation = layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.rotation;\n          label.scaleX = defaultLabelAttr.scaleX;\n          label.scaleY = defaultLabelAttr.scaleY;\n\n          for (var k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) {\n            var key = LABEL_OPTION_TO_STYLE_KEYS[k];\n            label.setStyle(key, layoutOption[key] != null ? layoutOption[key] : defaultLabelAttr.style[key]);\n          }\n\n          if (layoutOption.draggable) {\n            label.draggable = true;\n            label.cursor = 'move';\n\n            if (hostEl) {\n              var hostModel = labelItem.seriesModel;\n\n              if (labelItem.dataIndex != null) {\n                var data = labelItem.seriesModel.getData(labelItem.dataType);\n                hostModel = data.getItemModel(labelItem.dataIndex);\n              }\n\n              label.on('drag', createDragHandler(hostEl, hostModel.getModel('labelLine')));\n            }\n          } else {\n            // TODO Other drag functions?\n            label.off('drag');\n            label.cursor = defaultLabelAttr.cursor;\n          }\n        }\n      };\n\n      LabelManager.prototype.layout = function (api) {\n        var width = api.getWidth();\n        var height = api.getHeight();\n        var labelList = prepareLayoutList(this._labelList);\n        var labelsNeedsAdjustOnX = filter(labelList, function (item) {\n          return item.layoutOption.moveOverlap === 'shiftX';\n        });\n        var labelsNeedsAdjustOnY = filter(labelList, function (item) {\n          return item.layoutOption.moveOverlap === 'shiftY';\n        });\n        shiftLayoutOnX(labelsNeedsAdjustOnX, 0, width);\n        shiftLayoutOnY(labelsNeedsAdjustOnY, 0, height);\n        var labelsNeedsHideOverlap = filter(labelList, function (item) {\n          return item.layoutOption.hideOverlap;\n        });\n        hideOverlap(labelsNeedsHideOverlap);\n      };\n      /**\n       * Process all labels. Not only labels with layoutOption.\n       */\n\n\n      LabelManager.prototype.processLabelsOverall = function () {\n        var _this = this;\n\n        each(this._chartViewList, function (chartView) {\n          var seriesModel = chartView.__model;\n          var ignoreLabelLineUpdate = chartView.ignoreLabelLineUpdate;\n          var animationEnabled = seriesModel.isAnimationEnabled();\n          chartView.group.traverse(function (child) {\n            if (child.ignore && !child.forceLabelAnimation) {\n              return true; // Stop traverse descendants.\n            }\n\n            var needsUpdateLabelLine = !ignoreLabelLineUpdate;\n            var label = child.getTextContent();\n\n            if (!needsUpdateLabelLine && label) {\n              needsUpdateLabelLine = labelLayoutInnerStore(label).needsUpdateLabelLine;\n            }\n\n            if (needsUpdateLabelLine) {\n              _this._updateLabelLine(child, seriesModel);\n            }\n\n            if (animationEnabled) {\n              _this._animateLabels(child, seriesModel);\n            }\n          });\n        });\n      };\n\n      LabelManager.prototype._updateLabelLine = function (el, seriesModel) {\n        // Only support label being hosted on graphic elements.\n        var textEl = el.getTextContent(); // Update label line style.\n\n        var ecData = getECData(el);\n        var dataIndex = ecData.dataIndex; // Only support labelLine on the labels represent data.\n\n        if (textEl && dataIndex != null) {\n          var data = seriesModel.getData(ecData.dataType);\n          var itemModel = data.getItemModel(dataIndex);\n          var defaultStyle = {};\n          var visualStyle = data.getItemVisual(dataIndex, 'style');\n          var visualType = data.getVisual('drawType'); // Default to be same with main color\n\n          defaultStyle.stroke = visualStyle[visualType];\n          var labelLineModel = itemModel.getModel('labelLine');\n          setLabelLineStyle(el, getLabelLineStatesModels(itemModel), defaultStyle);\n          updateLabelLinePoints(el, labelLineModel);\n        }\n      };\n\n      LabelManager.prototype._animateLabels = function (el, seriesModel) {\n        var textEl = el.getTextContent();\n        var guideLine = el.getTextGuideLine(); // Animate\n\n        if (textEl // `forceLabelAnimation` has the highest priority\n        && (el.forceLabelAnimation || !textEl.ignore && !textEl.invisible && !el.disableLabelAnimation && !isElementRemoved(el))) {\n          var layoutStore = labelLayoutInnerStore(textEl);\n          var oldLayout = layoutStore.oldLayout;\n          var ecData = getECData(el);\n          var dataIndex = ecData.dataIndex;\n          var newProps = {\n            x: textEl.x,\n            y: textEl.y,\n            rotation: textEl.rotation\n          };\n          var data = seriesModel.getData(ecData.dataType);\n\n          if (!oldLayout) {\n            textEl.attr(newProps); // Disable fade in animation if value animation is enabled.\n\n            if (!labelInner(textEl).valueAnimation) {\n              var oldOpacity = retrieve2(textEl.style.opacity, 1); // Fade in animation\n\n              textEl.style.opacity = 0;\n              initProps(textEl, {\n                style: {\n                  opacity: oldOpacity\n                }\n              }, seriesModel, dataIndex);\n            }\n          } else {\n            textEl.attr(oldLayout); // Make sure the animation from is in the right status.\n\n            var prevStates = el.prevStates;\n\n            if (prevStates) {\n              if (indexOf(prevStates, 'select') >= 0) {\n                textEl.attr(layoutStore.oldLayoutSelect);\n              }\n\n              if (indexOf(prevStates, 'emphasis') >= 0) {\n                textEl.attr(layoutStore.oldLayoutEmphasis);\n              }\n            }\n\n            updateProps(textEl, newProps, seriesModel, dataIndex);\n          }\n\n          layoutStore.oldLayout = newProps;\n\n          if (textEl.states.select) {\n            var layoutSelect = layoutStore.oldLayoutSelect = {};\n            extendWithKeys(layoutSelect, newProps, LABEL_LAYOUT_PROPS);\n            extendWithKeys(layoutSelect, textEl.states.select, LABEL_LAYOUT_PROPS);\n          }\n\n          if (textEl.states.emphasis) {\n            var layoutEmphasis = layoutStore.oldLayoutEmphasis = {};\n            extendWithKeys(layoutEmphasis, newProps, LABEL_LAYOUT_PROPS);\n            extendWithKeys(layoutEmphasis, textEl.states.emphasis, LABEL_LAYOUT_PROPS);\n          }\n\n          animateLabelValue(textEl, dataIndex, data, seriesModel, seriesModel);\n        }\n\n        if (guideLine && !guideLine.ignore && !guideLine.invisible) {\n          var layoutStore = labelLineAnimationStore(guideLine);\n          var oldLayout = layoutStore.oldLayout;\n          var newLayout = {\n            points: guideLine.shape.points\n          };\n\n          if (!oldLayout) {\n            guideLine.setShape(newLayout);\n            guideLine.style.strokePercent = 0;\n            initProps(guideLine, {\n              style: {\n                strokePercent: 1\n              }\n            }, seriesModel);\n          } else {\n            guideLine.attr({\n              shape: oldLayout\n            });\n            updateProps(guideLine, {\n              shape: newLayout\n            }, seriesModel);\n          }\n\n          layoutStore.oldLayout = newLayout;\n        }\n      };\n\n      return LabelManager;\n    }();\n\n    var getLabelManager = makeInner();\n    function installLabelLayout(registers) {\n      registers.registerUpdateLifecycle('series:beforeupdate', function (ecModel, api, params) {\n        // TODO api provide an namespace that can save stuff per instance\n        var labelManager = getLabelManager(api).labelManager;\n\n        if (!labelManager) {\n          labelManager = getLabelManager(api).labelManager = new LabelManager();\n        }\n\n        labelManager.clearLabels();\n      });\n      registers.registerUpdateLifecycle('series:layoutlabels', function (ecModel, api, params) {\n        var labelManager = getLabelManager(api).labelManager;\n        params.updatedSeries.forEach(function (series) {\n          labelManager.addLabelsOfSeries(api.getViewOfSeriesModel(series));\n        });\n        labelManager.updateLayoutConfig(api);\n        labelManager.layout(api);\n        labelManager.processLabelsOverall();\n      });\n    }\n\n    var mathSin$4 = Math.sin;\n    var mathCos$4 = Math.cos;\n    var PI$4 = Math.PI;\n    var PI2$7 = Math.PI * 2;\n    var degree = 180 / PI$4;\n    var SVGPathRebuilder = (function () {\n        function SVGPathRebuilder() {\n        }\n        SVGPathRebuilder.prototype.reset = function (precision) {\n            this._start = true;\n            this._d = [];\n            this._str = '';\n            this._p = Math.pow(10, precision || 4);\n        };\n        SVGPathRebuilder.prototype.moveTo = function (x, y) {\n            this._add('M', x, y);\n        };\n        SVGPathRebuilder.prototype.lineTo = function (x, y) {\n            this._add('L', x, y);\n        };\n        SVGPathRebuilder.prototype.bezierCurveTo = function (x, y, x2, y2, x3, y3) {\n            this._add('C', x, y, x2, y2, x3, y3);\n        };\n        SVGPathRebuilder.prototype.quadraticCurveTo = function (x, y, x2, y2) {\n            this._add('Q', x, y, x2, y2);\n        };\n        SVGPathRebuilder.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n            this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise);\n        };\n        SVGPathRebuilder.prototype.ellipse = function (cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise) {\n            var dTheta = endAngle - startAngle;\n            var clockwise = !anticlockwise;\n            var dThetaPositive = Math.abs(dTheta);\n            var isCircle = isAroundZero$1(dThetaPositive - PI2$7)\n                || (clockwise ? dTheta >= PI2$7 : -dTheta >= PI2$7);\n            var unifiedTheta = dTheta > 0 ? dTheta % PI2$7 : (dTheta % PI2$7 + PI2$7);\n            var large = false;\n            if (isCircle) {\n                large = true;\n            }\n            else if (isAroundZero$1(dThetaPositive)) {\n                large = false;\n            }\n            else {\n                large = (unifiedTheta >= PI$4) === !!clockwise;\n            }\n            var x0 = cx + rx * mathCos$4(startAngle);\n            var y0 = cy + ry * mathSin$4(startAngle);\n            if (this._start) {\n                this._add('M', x0, y0);\n            }\n            var xRot = Math.round(psi * degree);\n            if (isCircle) {\n                var p = 1 / this._p;\n                var dTheta_1 = (clockwise ? 1 : -1) * (PI2$7 - p);\n                this._add('A', rx, ry, xRot, 1, +clockwise, cx + rx * mathCos$4(startAngle + dTheta_1), cy + ry * mathSin$4(startAngle + dTheta_1));\n                if (p > 1e-2) {\n                    this._add('A', rx, ry, xRot, 0, +clockwise, x0, y0);\n                }\n            }\n            else {\n                var x = cx + rx * mathCos$4(endAngle);\n                var y = cy + ry * mathSin$4(endAngle);\n                this._add('A', rx, ry, xRot, +large, +clockwise, x, y);\n            }\n        };\n        SVGPathRebuilder.prototype.rect = function (x, y, w, h) {\n            this._add('M', x, y);\n            this._add('l', w, 0);\n            this._add('l', 0, h);\n            this._add('l', -w, 0);\n            this._add('Z');\n        };\n        SVGPathRebuilder.prototype.closePath = function () {\n            if (this._d.length > 0) {\n                this._add('Z');\n            }\n        };\n        SVGPathRebuilder.prototype._add = function (cmd, a, b, c, d, e, f, g, h) {\n            var vals = [];\n            var p = this._p;\n            for (var i = 1; i < arguments.length; i++) {\n                var val = arguments[i];\n                if (isNaN(val)) {\n                    this._invalid = true;\n                    return;\n                }\n                vals.push(Math.round(val * p) / p);\n            }\n            this._d.push(cmd + vals.join(' '));\n            this._start = cmd === 'Z';\n        };\n        SVGPathRebuilder.prototype.generateStr = function () {\n            this._str = this._invalid ? '' : this._d.join('');\n            this._d = [];\n        };\n        SVGPathRebuilder.prototype.getStr = function () {\n            return this._str;\n        };\n        return SVGPathRebuilder;\n    }());\n\n    var NONE = 'none';\n    var mathRound$1 = Math.round;\n    function pathHasFill(style) {\n        var fill = style.fill;\n        return fill != null && fill !== NONE;\n    }\n    function pathHasStroke(style) {\n        var stroke = style.stroke;\n        return stroke != null && stroke !== NONE;\n    }\n    var strokeProps = ['lineCap', 'miterLimit', 'lineJoin'];\n    var svgStrokeProps = map(strokeProps, function (prop) { return \"stroke-\" + prop.toLowerCase(); });\n    function mapStyleToAttrs(updateAttr, style, el, forceUpdate) {\n        var opacity = style.opacity == null ? 1 : style.opacity;\n        if (el instanceof ZRImage) {\n            updateAttr('opacity', opacity);\n            return;\n        }\n        if (pathHasFill(style)) {\n            var fill = normalizeColor(style.fill);\n            updateAttr('fill', fill.color);\n            var fillOpacity = style.fillOpacity != null\n                ? style.fillOpacity * fill.opacity * opacity\n                : fill.opacity * opacity;\n            if (forceUpdate || fillOpacity < 1) {\n                updateAttr('fill-opacity', fillOpacity);\n            }\n        }\n        else {\n            updateAttr('fill', NONE);\n        }\n        if (pathHasStroke(style)) {\n            var stroke = normalizeColor(style.stroke);\n            updateAttr('stroke', stroke.color);\n            var strokeScale = style.strokeNoScale\n                ? el.getLineScale()\n                : 1;\n            var strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0);\n            var strokeOpacity = style.strokeOpacity != null\n                ? style.strokeOpacity * stroke.opacity * opacity\n                : stroke.opacity * opacity;\n            var strokeFirst = style.strokeFirst;\n            if (forceUpdate || strokeWidth !== 1) {\n                updateAttr('stroke-width', strokeWidth);\n            }\n            if (forceUpdate || strokeFirst) {\n                updateAttr('paint-order', strokeFirst ? 'stroke' : 'fill');\n            }\n            if (forceUpdate || strokeOpacity < 1) {\n                updateAttr('stroke-opacity', strokeOpacity);\n            }\n            if (style.lineDash) {\n                var _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n                if (lineDash) {\n                    lineDashOffset = mathRound$1(lineDashOffset || 0);\n                    updateAttr('stroke-dasharray', lineDash.join(','));\n                    if (lineDashOffset || forceUpdate) {\n                        updateAttr('stroke-dashoffset', lineDashOffset);\n                    }\n                }\n            }\n            else if (forceUpdate) {\n                updateAttr('stroke-dasharray', NONE);\n            }\n            for (var i = 0; i < strokeProps.length; i++) {\n                var propName = strokeProps[i];\n                if (forceUpdate || style[propName] !== DEFAULT_PATH_STYLE[propName]) {\n                    var val = style[propName] || DEFAULT_PATH_STYLE[propName];\n                    val && updateAttr(svgStrokeProps[i], val);\n                }\n            }\n        }\n        else if (forceUpdate) {\n            updateAttr('stroke', NONE);\n        }\n    }\n\n    var SVGNS = 'http://www.w3.org/2000/svg';\n    var XLINKNS = 'http://www.w3.org/1999/xlink';\n    var XMLNS = 'http://www.w3.org/2000/xmlns/';\n    var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';\n    function createElement(name) {\n        return document.createElementNS(SVGNS, name);\n    }\n    function createVNode(tag, key, attrs, children, text) {\n        return {\n            tag: tag,\n            attrs: attrs || {},\n            children: children,\n            text: text,\n            key: key\n        };\n    }\n    function createElementOpen(name, attrs) {\n        var attrsStr = [];\n        if (attrs) {\n            for (var key in attrs) {\n                var val = attrs[key];\n                var part = key;\n                if (val === false) {\n                    continue;\n                }\n                else if (val !== true && val != null) {\n                    part += \"=\\\"\" + val + \"\\\"\";\n                }\n                attrsStr.push(part);\n            }\n        }\n        return \"<\" + name + \" \" + attrsStr.join(' ') + \">\";\n    }\n    function createElementClose(name) {\n        return \"</\" + name + \">\";\n    }\n    function vNodeToString(el, opts) {\n        opts = opts || {};\n        var S = opts.newline ? '\\n' : '';\n        function convertElToString(el) {\n            var children = el.children, tag = el.tag, attrs = el.attrs;\n            return createElementOpen(tag, attrs)\n                + encodeHTML(el.text)\n                + (children ? \"\" + S + map(children, function (child) { return convertElToString(child); }).join(S) + S : '')\n                + createElementClose(tag);\n        }\n        return convertElToString(el);\n    }\n    function getCssString(selectorNodes, animationNodes, opts) {\n        opts = opts || {};\n        var S = opts.newline ? '\\n' : '';\n        var bracketBegin = \" {\" + S;\n        var bracketEnd = S + \"}\";\n        var selectors = map(keys(selectorNodes), function (className) {\n            return className + bracketBegin + map(keys(selectorNodes[className]), function (attrName) {\n                return attrName + \":\" + selectorNodes[className][attrName] + \";\";\n            }).join(S) + bracketEnd;\n        }).join(S);\n        var animations = map(keys(animationNodes), function (animationName) {\n            return \"@keyframes \" + animationName + bracketBegin + map(keys(animationNodes[animationName]), function (percent) {\n                return percent + bracketBegin + map(keys(animationNodes[animationName][percent]), function (attrName) {\n                    var val = animationNodes[animationName][percent][attrName];\n                    if (attrName === 'd') {\n                        val = \"path(\\\"\" + val + \"\\\")\";\n                    }\n                    return attrName + \":\" + val + \";\";\n                }).join(S) + bracketEnd;\n            }).join(S) + bracketEnd;\n        }).join(S);\n        if (!selectors && !animations) {\n            return '';\n        }\n        return ['<![CDATA[', selectors, animations, ']]>'].join(S);\n    }\n    function createBrushScope(zrId) {\n        return {\n            zrId: zrId,\n            shadowCache: {},\n            patternCache: {},\n            gradientCache: {},\n            clipPathCache: {},\n            defs: {},\n            cssNodes: {},\n            cssAnims: {},\n            cssClassIdx: 0,\n            cssAnimIdx: 0,\n            shadowIdx: 0,\n            gradientIdx: 0,\n            patternIdx: 0,\n            clipPathIdx: 0\n        };\n    }\n    function createSVGVNode(width, height, children, useViewBox) {\n        return createVNode('svg', 'root', {\n            'width': width,\n            'height': height,\n            'xmlns': SVGNS,\n            'xmlns:xlink': XLINKNS,\n            'version': '1.1',\n            'baseProfile': 'full',\n            'viewBox': useViewBox ? \"0 0 \" + width + \" \" + height : false\n        }, children);\n    }\n\n    var EASING_MAP = {\n        cubicIn: '0.32,0,0.67,0',\n        cubicOut: '0.33,1,0.68,1',\n        cubicInOut: '0.65,0,0.35,1',\n        quadraticIn: '0.11,0,0.5,0',\n        quadraticOut: '0.5,1,0.89,1',\n        quadraticInOut: '0.45,0,0.55,1',\n        quarticIn: '0.5,0,0.75,0',\n        quarticOut: '0.25,1,0.5,1',\n        quarticInOut: '0.76,0,0.24,1',\n        quinticIn: '0.64,0,0.78,0',\n        quinticOut: '0.22,1,0.36,1',\n        quinticInOut: '0.83,0,0.17,1',\n        sinusoidalIn: '0.12,0,0.39,0',\n        sinusoidalOut: '0.61,1,0.88,1',\n        sinusoidalInOut: '0.37,0,0.63,1',\n        exponentialIn: '0.7,0,0.84,0',\n        exponentialOut: '0.16,1,0.3,1',\n        exponentialInOut: '0.87,0,0.13,1',\n        circularIn: '0.55,0,1,0.45',\n        circularOut: '0,0.55,0.45,1',\n        circularInOut: '0.85,0,0.15,1'\n    };\n    var transformOriginKey = 'transform-origin';\n    function buildPathString(el, kfShape, path) {\n        var shape = extend({}, el.shape);\n        extend(shape, kfShape);\n        el.buildPath(path, shape);\n        var svgPathBuilder = new SVGPathRebuilder();\n        svgPathBuilder.reset(getPathPrecision(el));\n        path.rebuildPath(svgPathBuilder, 1);\n        svgPathBuilder.generateStr();\n        return svgPathBuilder.getStr();\n    }\n    function setTransformOrigin(target, transform) {\n        var originX = transform.originX, originY = transform.originY;\n        if (originX || originY) {\n            target[transformOriginKey] = originX + \"px \" + originY + \"px\";\n        }\n    }\n    var ANIMATE_STYLE_MAP = {\n        fill: 'fill',\n        opacity: 'opacity',\n        lineWidth: 'stroke-width',\n        lineDashOffset: 'stroke-dashoffset'\n    };\n    function addAnimation(cssAnim, scope) {\n        var animationName = scope.zrId + '-ani-' + scope.cssAnimIdx++;\n        scope.cssAnims[animationName] = cssAnim;\n        return animationName;\n    }\n    function createCompoundPathCSSAnimation(el, attrs, scope) {\n        var paths = el.shape.paths;\n        var composedAnim = {};\n        var cssAnimationCfg;\n        var cssAnimationName;\n        each(paths, function (path) {\n            var subScope = createBrushScope(scope.zrId);\n            subScope.animation = true;\n            createCSSAnimation(path, {}, subScope, true);\n            var cssAnims = subScope.cssAnims;\n            var cssNodes = subScope.cssNodes;\n            var animNames = keys(cssAnims);\n            var len = animNames.length;\n            if (!len) {\n                return;\n            }\n            cssAnimationName = animNames[len - 1];\n            var lastAnim = cssAnims[cssAnimationName];\n            for (var percent in lastAnim) {\n                var kf = lastAnim[percent];\n                composedAnim[percent] = composedAnim[percent] || { d: '' };\n                composedAnim[percent].d += kf.d || '';\n            }\n            for (var className in cssNodes) {\n                var val = cssNodes[className].animation;\n                if (val.indexOf(cssAnimationName) >= 0) {\n                    cssAnimationCfg = val;\n                }\n            }\n        });\n        if (!cssAnimationCfg) {\n            return;\n        }\n        attrs.d = false;\n        var animationName = addAnimation(composedAnim, scope);\n        return cssAnimationCfg.replace(cssAnimationName, animationName);\n    }\n    function getEasingFunc(easing) {\n        return isString(easing)\n            ? EASING_MAP[easing]\n                ? \"cubic-bezier(\" + EASING_MAP[easing] + \")\"\n                : createCubicEasingFunc(easing) ? easing : ''\n            : '';\n    }\n    function createCSSAnimation(el, attrs, scope, onlyShape) {\n        var animators = el.animators;\n        var len = animators.length;\n        var cssAnimations = [];\n        if (el instanceof CompoundPath) {\n            var animationCfg = createCompoundPathCSSAnimation(el, attrs, scope);\n            if (animationCfg) {\n                cssAnimations.push(animationCfg);\n            }\n            else if (!len) {\n                return;\n            }\n        }\n        else if (!len) {\n            return;\n        }\n        var groupAnimators = {};\n        for (var i = 0; i < len; i++) {\n            var animator = animators[i];\n            var cfgArr = [animator.getMaxTime() / 1000 + 's'];\n            var easing = getEasingFunc(animator.getClip().easing);\n            var delay = animator.getDelay();\n            if (easing) {\n                cfgArr.push(easing);\n            }\n            else {\n                cfgArr.push('linear');\n            }\n            if (delay) {\n                cfgArr.push(delay / 1000 + 's');\n            }\n            if (animator.getLoop()) {\n                cfgArr.push('infinite');\n            }\n            var cfg = cfgArr.join(' ');\n            groupAnimators[cfg] = groupAnimators[cfg] || [cfg, []];\n            groupAnimators[cfg][1].push(animator);\n        }\n        function createSingleCSSAnimation(groupAnimator) {\n            var animators = groupAnimator[1];\n            var len = animators.length;\n            var transformKfs = {};\n            var shapeKfs = {};\n            var finalKfs = {};\n            var animationTimingFunctionAttrName = 'animation-timing-function';\n            function saveAnimatorTrackToCssKfs(animator, cssKfs, toCssAttrName) {\n                var tracks = animator.getTracks();\n                var maxTime = animator.getMaxTime();\n                for (var k = 0; k < tracks.length; k++) {\n                    var track = tracks[k];\n                    if (track.needsAnimate()) {\n                        var kfs = track.keyframes;\n                        var attrName = track.propName;\n                        toCssAttrName && (attrName = toCssAttrName(attrName));\n                        if (attrName) {\n                            for (var i = 0; i < kfs.length; i++) {\n                                var kf = kfs[i];\n                                var percent = Math.round(kf.time / maxTime * 100) + '%';\n                                var kfEasing = getEasingFunc(kf.easing);\n                                var rawValue = kf.rawValue;\n                                if (isString(rawValue) || isNumber(rawValue)) {\n                                    cssKfs[percent] = cssKfs[percent] || {};\n                                    cssKfs[percent][attrName] = kf.rawValue;\n                                    if (kfEasing) {\n                                        cssKfs[percent][animationTimingFunctionAttrName] = kfEasing;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            for (var i = 0; i < len; i++) {\n                var animator = animators[i];\n                var targetProp = animator.targetName;\n                if (!targetProp) {\n                    !onlyShape && saveAnimatorTrackToCssKfs(animator, transformKfs);\n                }\n                else if (targetProp === 'shape') {\n                    saveAnimatorTrackToCssKfs(animator, shapeKfs);\n                }\n            }\n            for (var percent in transformKfs) {\n                var transform = {};\n                copyTransform(transform, el);\n                extend(transform, transformKfs[percent]);\n                var str = getSRTTransformString(transform);\n                var timingFunction = transformKfs[percent][animationTimingFunctionAttrName];\n                finalKfs[percent] = str ? {\n                    transform: str\n                } : {};\n                setTransformOrigin(finalKfs[percent], transform);\n                if (timingFunction) {\n                    finalKfs[percent][animationTimingFunctionAttrName] = timingFunction;\n                }\n            }\n            var path;\n            var canAnimateShape = true;\n            for (var percent in shapeKfs) {\n                finalKfs[percent] = finalKfs[percent] || {};\n                var isFirst = !path;\n                var timingFunction = shapeKfs[percent][animationTimingFunctionAttrName];\n                if (isFirst) {\n                    path = new PathProxy();\n                }\n                var len_1 = path.len();\n                path.reset();\n                finalKfs[percent].d = buildPathString(el, shapeKfs[percent], path);\n                var newLen = path.len();\n                if (!isFirst && len_1 !== newLen) {\n                    canAnimateShape = false;\n                    break;\n                }\n                if (timingFunction) {\n                    finalKfs[percent][animationTimingFunctionAttrName] = timingFunction;\n                }\n            }\n            if (!canAnimateShape) {\n                for (var percent in finalKfs) {\n                    delete finalKfs[percent].d;\n                }\n            }\n            if (!onlyShape) {\n                for (var i = 0; i < len; i++) {\n                    var animator = animators[i];\n                    var targetProp = animator.targetName;\n                    if (targetProp === 'style') {\n                        saveAnimatorTrackToCssKfs(animator, finalKfs, function (propName) { return ANIMATE_STYLE_MAP[propName]; });\n                    }\n                }\n            }\n            var percents = keys(finalKfs);\n            var allTransformOriginSame = true;\n            var transformOrigin;\n            for (var i = 1; i < percents.length; i++) {\n                var p0 = percents[i - 1];\n                var p1 = percents[i];\n                if (finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) {\n                    allTransformOriginSame = false;\n                    break;\n                }\n                transformOrigin = finalKfs[p0][transformOriginKey];\n            }\n            if (allTransformOriginSame && transformOrigin) {\n                for (var percent in finalKfs) {\n                    if (finalKfs[percent][transformOriginKey]) {\n                        delete finalKfs[percent][transformOriginKey];\n                    }\n                }\n                attrs[transformOriginKey] = transformOrigin;\n            }\n            if (filter(percents, function (percent) { return keys(finalKfs[percent]).length > 0; }).length) {\n                var animationName = addAnimation(finalKfs, scope);\n                return animationName + \" \" + groupAnimator[0] + \" both\";\n            }\n        }\n        for (var key in groupAnimators) {\n            var animationCfg = createSingleCSSAnimation(groupAnimators[key]);\n            if (animationCfg) {\n                cssAnimations.push(animationCfg);\n            }\n        }\n        if (cssAnimations.length) {\n            var className = scope.zrId + '-cls-' + scope.cssClassIdx++;\n            scope.cssNodes['.' + className] = {\n                animation: cssAnimations.join(',')\n            };\n            attrs[\"class\"] = className;\n        }\n    }\n\n    var round$2 = Math.round;\n    function isImageLike$1(val) {\n        return val && isString(val.src);\n    }\n    function isCanvasLike(val) {\n        return val && isFunction(val.toDataURL);\n    }\n    function setStyleAttrs(attrs, style, el, scope) {\n        mapStyleToAttrs(function (key, val) {\n            var isFillStroke = key === 'fill' || key === 'stroke';\n            if (isFillStroke && isGradient(val)) {\n                setGradient(style, attrs, key, scope);\n            }\n            else if (isFillStroke && isPattern(val)) {\n                setPattern(el, attrs, key, scope);\n            }\n            else {\n                attrs[key] = val;\n            }\n        }, style, el, false);\n        setShadow(el, attrs, scope);\n    }\n    function noRotateScale(m) {\n        return isAroundZero$1(m[0] - 1)\n            && isAroundZero$1(m[1])\n            && isAroundZero$1(m[2])\n            && isAroundZero$1(m[3] - 1);\n    }\n    function noTranslate(m) {\n        return isAroundZero$1(m[4]) && isAroundZero$1(m[5]);\n    }\n    function setTransform(attrs, m, compress) {\n        if (m && !(noTranslate(m) && noRotateScale(m))) {\n            var mul = compress ? 10 : 1e4;\n            attrs.transform = noRotateScale(m)\n                ? \"translate(\" + round$2(m[4] * mul) / mul + \" \" + round$2(m[5] * mul) / mul + \")\" : getMatrixStr(m);\n        }\n    }\n    function convertPolyShape(shape, attrs, mul) {\n        var points = shape.points;\n        var strArr = [];\n        for (var i = 0; i < points.length; i++) {\n            strArr.push(round$2(points[i][0] * mul) / mul);\n            strArr.push(round$2(points[i][1] * mul) / mul);\n        }\n        attrs.points = strArr.join(' ');\n    }\n    function validatePolyShape(shape) {\n        return !shape.smooth;\n    }\n    function createAttrsConvert(desc) {\n        var normalizedDesc = map(desc, function (item) {\n            return (typeof item === 'string' ? [item, item] : item);\n        });\n        return function (shape, attrs, mul) {\n            for (var i = 0; i < normalizedDesc.length; i++) {\n                var item = normalizedDesc[i];\n                var val = shape[item[0]];\n                if (val != null) {\n                    attrs[item[1]] = round$2(val * mul) / mul;\n                }\n            }\n        };\n    }\n    var builtinShapesDef = {\n        circle: [createAttrsConvert(['cx', 'cy', 'r'])],\n        polyline: [convertPolyShape, validatePolyShape],\n        polygon: [convertPolyShape, validatePolyShape]\n    };\n    function hasShapeAnimation(el) {\n        var animators = el.animators;\n        for (var i = 0; i < animators.length; i++) {\n            if (animators[i].targetName === 'shape') {\n                return true;\n            }\n        }\n        return false;\n    }\n    function brushSVGPath(el, scope) {\n        var style = el.style;\n        var shape = el.shape;\n        var builtinShpDef = builtinShapesDef[el.type];\n        var attrs = {};\n        var needsAnimate = scope.animation;\n        var svgElType = 'path';\n        var strokePercent = el.style.strokePercent;\n        var precision = (scope.compress && getPathPrecision(el)) || 4;\n        if (builtinShpDef\n            && !scope.willUpdate\n            && !(builtinShpDef[1] && !builtinShpDef[1](shape))\n            && !(needsAnimate && hasShapeAnimation(el))\n            && !(strokePercent < 1)) {\n            svgElType = el.type;\n            var mul = Math.pow(10, precision);\n            builtinShpDef[0](shape, attrs, mul);\n        }\n        else {\n            var needBuildPath = !el.path || el.shapeChanged();\n            if (!el.path) {\n                el.createPathProxy();\n            }\n            var path = el.path;\n            if (needBuildPath) {\n                path.beginPath();\n                el.buildPath(path, el.shape);\n                el.pathUpdated();\n            }\n            var pathVersion = path.getVersion();\n            var elExt = el;\n            var svgPathBuilder = elExt.__svgPathBuilder;\n            if (elExt.__svgPathVersion !== pathVersion\n                || !svgPathBuilder\n                || strokePercent !== elExt.__svgPathStrokePercent) {\n                if (!svgPathBuilder) {\n                    svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder();\n                }\n                svgPathBuilder.reset(precision);\n                path.rebuildPath(svgPathBuilder, strokePercent);\n                svgPathBuilder.generateStr();\n                elExt.__svgPathVersion = pathVersion;\n                elExt.__svgPathStrokePercent = strokePercent;\n            }\n            attrs.d = svgPathBuilder.getStr();\n        }\n        setTransform(attrs, el.transform);\n        setStyleAttrs(attrs, style, el, scope);\n        scope.animation && createCSSAnimation(el, attrs, scope);\n        return createVNode(svgElType, el.id + '', attrs);\n    }\n    function brushSVGImage(el, scope) {\n        var style = el.style;\n        var image = style.image;\n        if (image && !isString(image)) {\n            if (isImageLike$1(image)) {\n                image = image.src;\n            }\n            else if (isCanvasLike(image)) {\n                image = image.toDataURL();\n            }\n        }\n        if (!image) {\n            return;\n        }\n        var x = style.x || 0;\n        var y = style.y || 0;\n        var dw = style.width;\n        var dh = style.height;\n        var attrs = {\n            href: image,\n            width: dw,\n            height: dh\n        };\n        if (x) {\n            attrs.x = x;\n        }\n        if (y) {\n            attrs.y = y;\n        }\n        setTransform(attrs, el.transform);\n        setStyleAttrs(attrs, style, el, scope);\n        scope.animation && createCSSAnimation(el, attrs, scope);\n        return createVNode('image', el.id + '', attrs);\n    }\n    function brushSVGTSpan(el, scope) {\n        var style = el.style;\n        var text = style.text;\n        text != null && (text += '');\n        if (!text || isNaN(style.x) || isNaN(style.y)) {\n            return;\n        }\n        var font = style.font || DEFAULT_FONT;\n        var x = style.x || 0;\n        var y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline);\n        var textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign]\n            || style.textAlign;\n        var attrs = {\n            'dominant-baseline': 'central',\n            'text-anchor': textAlign\n        };\n        if (hasSeparateFont(style)) {\n            var separatedFontStr = '';\n            var fontStyle = style.fontStyle;\n            var fontSize = parseFontSize(style.fontSize);\n            if (!parseFloat(fontSize)) {\n                return;\n            }\n            var fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY;\n            var fontWeight = style.fontWeight;\n            separatedFontStr += \"font-size:\" + fontSize + \";font-family:\" + fontFamily + \";\";\n            if (fontStyle && fontStyle !== 'normal') {\n                separatedFontStr += \"font-style:\" + fontStyle + \";\";\n            }\n            if (fontWeight && fontWeight !== 'normal') {\n                separatedFontStr += \"font-weight:\" + fontWeight + \";\";\n            }\n            attrs.style = separatedFontStr;\n        }\n        else {\n            attrs.style = \"font: \" + font;\n        }\n        if (text.match(/\\s/)) {\n            attrs['xml:space'] = 'preserve';\n        }\n        if (x) {\n            attrs.x = x;\n        }\n        if (y) {\n            attrs.y = y;\n        }\n        setTransform(attrs, el.transform);\n        setStyleAttrs(attrs, style, el, scope);\n        scope.animation && createCSSAnimation(el, attrs, scope);\n        return createVNode('text', el.id + '', attrs, undefined, text);\n    }\n    function brush$1(el, scope) {\n        if (el instanceof Path) {\n            return brushSVGPath(el, scope);\n        }\n        else if (el instanceof ZRImage) {\n            return brushSVGImage(el, scope);\n        }\n        else if (el instanceof TSpan) {\n            return brushSVGTSpan(el, scope);\n        }\n    }\n    function setShadow(el, attrs, scope) {\n        var style = el.style;\n        if (hasShadow(style)) {\n            var shadowKey = getShadowKey(el);\n            var shadowCache = scope.shadowCache;\n            var shadowId = shadowCache[shadowKey];\n            if (!shadowId) {\n                var globalScale = el.getGlobalScale();\n                var scaleX = globalScale[0];\n                var scaleY = globalScale[1];\n                if (!scaleX || !scaleY) {\n                    return;\n                }\n                var offsetX = style.shadowOffsetX || 0;\n                var offsetY = style.shadowOffsetY || 0;\n                var blur_1 = style.shadowBlur;\n                var _a = normalizeColor(style.shadowColor), opacity = _a.opacity, color = _a.color;\n                var stdDx = blur_1 / 2 / scaleX;\n                var stdDy = blur_1 / 2 / scaleY;\n                var stdDeviation = stdDx + ' ' + stdDy;\n                shadowId = scope.zrId + '-s' + scope.shadowIdx++;\n                scope.defs[shadowId] = createVNode('filter', shadowId, {\n                    'id': shadowId,\n                    'x': '-100%',\n                    'y': '-100%',\n                    'width': '300%',\n                    'height': '300%'\n                }, [\n                    createVNode('feDropShadow', '', {\n                        'dx': offsetX / scaleX,\n                        'dy': offsetY / scaleY,\n                        'stdDeviation': stdDeviation,\n                        'flood-color': color,\n                        'flood-opacity': opacity\n                    })\n                ]);\n                shadowCache[shadowKey] = shadowId;\n            }\n            attrs.filter = getIdURL(shadowId);\n        }\n    }\n    function setGradient(style, attrs, target, scope) {\n        var val = style[target];\n        var gradientTag;\n        var gradientAttrs = {\n            'gradientUnits': val.global\n                ? 'userSpaceOnUse'\n                : 'objectBoundingBox'\n        };\n        if (isLinearGradient(val)) {\n            gradientTag = 'linearGradient';\n            gradientAttrs.x1 = val.x;\n            gradientAttrs.y1 = val.y;\n            gradientAttrs.x2 = val.x2;\n            gradientAttrs.y2 = val.y2;\n        }\n        else if (isRadialGradient(val)) {\n            gradientTag = 'radialGradient';\n            gradientAttrs.cx = retrieve2(val.x, 0.5);\n            gradientAttrs.cy = retrieve2(val.y, 0.5);\n            gradientAttrs.r = retrieve2(val.r, 0.5);\n        }\n        else {\n            if (\"development\" !== 'production') {\n                logError('Illegal gradient type.');\n            }\n            return;\n        }\n        var colors = val.colorStops;\n        var colorStops = [];\n        for (var i = 0, len = colors.length; i < len; ++i) {\n            var offset = round4(colors[i].offset) * 100 + '%';\n            var stopColor = colors[i].color;\n            var _a = normalizeColor(stopColor), color = _a.color, opacity = _a.opacity;\n            var stopsAttrs = {\n                'offset': offset\n            };\n            stopsAttrs['stop-color'] = color;\n            if (opacity < 1) {\n                stopsAttrs['stop-opacity'] = opacity;\n            }\n            colorStops.push(createVNode('stop', i + '', stopsAttrs));\n        }\n        var gradientVNode = createVNode(gradientTag, '', gradientAttrs, colorStops);\n        var gradientKey = vNodeToString(gradientVNode);\n        var gradientCache = scope.gradientCache;\n        var gradientId = gradientCache[gradientKey];\n        if (!gradientId) {\n            gradientId = scope.zrId + '-g' + scope.gradientIdx++;\n            gradientCache[gradientKey] = gradientId;\n            gradientAttrs.id = gradientId;\n            scope.defs[gradientId] = createVNode(gradientTag, gradientId, gradientAttrs, colorStops);\n        }\n        attrs[target] = getIdURL(gradientId);\n    }\n    function setPattern(el, attrs, target, scope) {\n        var val = el.style[target];\n        var boundingRect = el.getBoundingRect();\n        var patternAttrs = {};\n        var repeat = val.repeat;\n        var noRepeat = repeat === 'no-repeat';\n        var repeatX = repeat === 'repeat-x';\n        var repeatY = repeat === 'repeat-y';\n        var child;\n        if (isImagePattern(val)) {\n            var imageWidth_1 = val.imageWidth;\n            var imageHeight_1 = val.imageHeight;\n            var imageSrc = void 0;\n            var patternImage = val.image;\n            if (isString(patternImage)) {\n                imageSrc = patternImage;\n            }\n            else if (isImageLike$1(patternImage)) {\n                imageSrc = patternImage.src;\n            }\n            else if (isCanvasLike(patternImage)) {\n                imageSrc = patternImage.toDataURL();\n            }\n            if (typeof Image === 'undefined') {\n                var errMsg = 'Image width/height must been given explictly in svg-ssr renderer.';\n                assert(imageWidth_1, errMsg);\n                assert(imageHeight_1, errMsg);\n            }\n            else if (imageWidth_1 == null || imageHeight_1 == null) {\n                var setSizeToVNode_1 = function (vNode, img) {\n                    if (vNode) {\n                        var svgEl = vNode.elm;\n                        var width = imageWidth_1 || img.width;\n                        var height = imageHeight_1 || img.height;\n                        if (vNode.tag === 'pattern') {\n                            if (repeatX) {\n                                height = 1;\n                                width /= boundingRect.width;\n                            }\n                            else if (repeatY) {\n                                width = 1;\n                                height /= boundingRect.height;\n                            }\n                        }\n                        vNode.attrs.width = width;\n                        vNode.attrs.height = height;\n                        if (svgEl) {\n                            svgEl.setAttribute('width', width);\n                            svgEl.setAttribute('height', height);\n                        }\n                    }\n                };\n                var createdImage = createOrUpdateImage(imageSrc, null, el, function (img) {\n                    noRepeat || setSizeToVNode_1(patternVNode, img);\n                    setSizeToVNode_1(child, img);\n                });\n                if (createdImage && createdImage.width && createdImage.height) {\n                    imageWidth_1 = imageWidth_1 || createdImage.width;\n                    imageHeight_1 = imageHeight_1 || createdImage.height;\n                }\n            }\n            child = createVNode('image', 'img', {\n                href: imageSrc,\n                width: imageWidth_1,\n                height: imageHeight_1\n            });\n            patternAttrs.width = imageWidth_1;\n            patternAttrs.height = imageHeight_1;\n        }\n        else if (val.svgElement) {\n            child = clone(val.svgElement);\n            patternAttrs.width = val.svgWidth;\n            patternAttrs.height = val.svgHeight;\n        }\n        if (!child) {\n            return;\n        }\n        var patternWidth;\n        var patternHeight;\n        if (noRepeat) {\n            patternWidth = patternHeight = 1;\n        }\n        else if (repeatX) {\n            patternHeight = 1;\n            patternWidth = patternAttrs.width / boundingRect.width;\n        }\n        else if (repeatY) {\n            patternWidth = 1;\n            patternHeight = patternAttrs.height / boundingRect.height;\n        }\n        else {\n            patternAttrs.patternUnits = 'userSpaceOnUse';\n        }\n        if (patternWidth != null && !isNaN(patternWidth)) {\n            patternAttrs.width = patternWidth;\n        }\n        if (patternHeight != null && !isNaN(patternHeight)) {\n            patternAttrs.height = patternHeight;\n        }\n        var patternTransform = getSRTTransformString(val);\n        patternTransform && (patternAttrs.patternTransform = patternTransform);\n        var patternVNode = createVNode('pattern', '', patternAttrs, [child]);\n        var patternKey = vNodeToString(patternVNode);\n        var patternCache = scope.patternCache;\n        var patternId = patternCache[patternKey];\n        if (!patternId) {\n            patternId = scope.zrId + '-p' + scope.patternIdx++;\n            patternCache[patternKey] = patternId;\n            patternAttrs.id = patternId;\n            patternVNode = scope.defs[patternId] = createVNode('pattern', patternId, patternAttrs, [child]);\n        }\n        attrs[target] = getIdURL(patternId);\n    }\n    function setClipPath(clipPath, attrs, scope) {\n        var clipPathCache = scope.clipPathCache, defs = scope.defs;\n        var clipPathId = clipPathCache[clipPath.id];\n        if (!clipPathId) {\n            clipPathId = scope.zrId + '-c' + scope.clipPathIdx++;\n            var clipPathAttrs = {\n                id: clipPathId\n            };\n            clipPathCache[clipPath.id] = clipPathId;\n            defs[clipPathId] = createVNode('clipPath', clipPathId, clipPathAttrs, [brushSVGPath(clipPath, scope)]);\n        }\n        attrs['clip-path'] = getIdURL(clipPathId);\n    }\n\n    function createTextNode(text) {\n        return document.createTextNode(text);\n    }\n    function insertBefore(parentNode, newNode, referenceNode) {\n        parentNode.insertBefore(newNode, referenceNode);\n    }\n    function removeChild(node, child) {\n        node.removeChild(child);\n    }\n    function appendChild(node, child) {\n        node.appendChild(child);\n    }\n    function parentNode(node) {\n        return node.parentNode;\n    }\n    function nextSibling(node) {\n        return node.nextSibling;\n    }\n    function setTextContent(node, text) {\n        node.textContent = text;\n    }\n\n    var colonChar = 58;\n    var xChar = 120;\n    var emptyNode = createVNode('', '');\n    function isUndef(s) {\n        return s === undefined;\n    }\n    function isDef(s) {\n        return s !== undefined;\n    }\n    function createKeyToOldIdx(children, beginIdx, endIdx) {\n        var map = {};\n        for (var i = beginIdx; i <= endIdx; ++i) {\n            var key = children[i].key;\n            if (key !== undefined) {\n                if (\"development\" !== 'production') {\n                    if (map[key] != null) {\n                        console.error(\"Duplicate key \" + key);\n                    }\n                }\n                map[key] = i;\n            }\n        }\n        return map;\n    }\n    function sameVnode(vnode1, vnode2) {\n        var isSameKey = vnode1.key === vnode2.key;\n        var isSameTag = vnode1.tag === vnode2.tag;\n        return isSameTag && isSameKey;\n    }\n    function createElm(vnode) {\n        var i;\n        var children = vnode.children;\n        var tag = vnode.tag;\n        if (isDef(tag)) {\n            var elm = (vnode.elm = createElement(tag));\n            updateAttrs(emptyNode, vnode);\n            if (isArray(children)) {\n                for (i = 0; i < children.length; ++i) {\n                    var ch = children[i];\n                    if (ch != null) {\n                        appendChild(elm, createElm(ch));\n                    }\n                }\n            }\n            else if (isDef(vnode.text) && !isObject(vnode.text)) {\n                appendChild(elm, createTextNode(vnode.text));\n            }\n        }\n        else {\n            vnode.elm = createTextNode(vnode.text);\n        }\n        return vnode.elm;\n    }\n    function addVnodes(parentElm, before, vnodes, startIdx, endIdx) {\n        for (; startIdx <= endIdx; ++startIdx) {\n            var ch = vnodes[startIdx];\n            if (ch != null) {\n                insertBefore(parentElm, createElm(ch), before);\n            }\n        }\n    }\n    function removeVnodes(parentElm, vnodes, startIdx, endIdx) {\n        for (; startIdx <= endIdx; ++startIdx) {\n            var ch = vnodes[startIdx];\n            if (ch != null) {\n                if (isDef(ch.tag)) {\n                    var parent_1 = parentNode(ch.elm);\n                    removeChild(parent_1, ch.elm);\n                }\n                else {\n                    removeChild(parentElm, ch.elm);\n                }\n            }\n        }\n    }\n    function updateAttrs(oldVnode, vnode) {\n        var key;\n        var elm = vnode.elm;\n        var oldAttrs = oldVnode && oldVnode.attrs || {};\n        var attrs = vnode.attrs || {};\n        if (oldAttrs === attrs) {\n            return;\n        }\n        for (key in attrs) {\n            var cur = attrs[key];\n            var old = oldAttrs[key];\n            if (old !== cur) {\n                if (cur === true) {\n                    elm.setAttribute(key, '');\n                }\n                else if (cur === false) {\n                    elm.removeAttribute(key);\n                }\n                else {\n                    if (key.charCodeAt(0) !== xChar) {\n                        elm.setAttribute(key, cur);\n                    }\n                    else if (key === 'xmlns:xlink' || key === 'xmlns') {\n                        elm.setAttributeNS(XMLNS, key, cur);\n                    }\n                    else if (key.charCodeAt(3) === colonChar) {\n                        elm.setAttributeNS(XML_NAMESPACE, key, cur);\n                    }\n                    else if (key.charCodeAt(5) === colonChar) {\n                        elm.setAttributeNS(XLINKNS, key, cur);\n                    }\n                    else {\n                        elm.setAttribute(key, cur);\n                    }\n                }\n            }\n        }\n        for (key in oldAttrs) {\n            if (!(key in attrs)) {\n                elm.removeAttribute(key);\n            }\n        }\n    }\n    function updateChildren(parentElm, oldCh, newCh) {\n        var oldStartIdx = 0;\n        var newStartIdx = 0;\n        var oldEndIdx = oldCh.length - 1;\n        var oldStartVnode = oldCh[0];\n        var oldEndVnode = oldCh[oldEndIdx];\n        var newEndIdx = newCh.length - 1;\n        var newStartVnode = newCh[0];\n        var newEndVnode = newCh[newEndIdx];\n        var oldKeyToIdx;\n        var idxInOld;\n        var elmToMove;\n        var before;\n        while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {\n            if (oldStartVnode == null) {\n                oldStartVnode = oldCh[++oldStartIdx];\n            }\n            else if (oldEndVnode == null) {\n                oldEndVnode = oldCh[--oldEndIdx];\n            }\n            else if (newStartVnode == null) {\n                newStartVnode = newCh[++newStartIdx];\n            }\n            else if (newEndVnode == null) {\n                newEndVnode = newCh[--newEndIdx];\n            }\n            else if (sameVnode(oldStartVnode, newStartVnode)) {\n                patchVnode(oldStartVnode, newStartVnode);\n                oldStartVnode = oldCh[++oldStartIdx];\n                newStartVnode = newCh[++newStartIdx];\n            }\n            else if (sameVnode(oldEndVnode, newEndVnode)) {\n                patchVnode(oldEndVnode, newEndVnode);\n                oldEndVnode = oldCh[--oldEndIdx];\n                newEndVnode = newCh[--newEndIdx];\n            }\n            else if (sameVnode(oldStartVnode, newEndVnode)) {\n                patchVnode(oldStartVnode, newEndVnode);\n                insertBefore(parentElm, oldStartVnode.elm, nextSibling(oldEndVnode.elm));\n                oldStartVnode = oldCh[++oldStartIdx];\n                newEndVnode = newCh[--newEndIdx];\n            }\n            else if (sameVnode(oldEndVnode, newStartVnode)) {\n                patchVnode(oldEndVnode, newStartVnode);\n                insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);\n                oldEndVnode = oldCh[--oldEndIdx];\n                newStartVnode = newCh[++newStartIdx];\n            }\n            else {\n                if (isUndef(oldKeyToIdx)) {\n                    oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);\n                }\n                idxInOld = oldKeyToIdx[newStartVnode.key];\n                if (isUndef(idxInOld)) {\n                    insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm);\n                }\n                else {\n                    elmToMove = oldCh[idxInOld];\n                    if (elmToMove.tag !== newStartVnode.tag) {\n                        insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm);\n                    }\n                    else {\n                        patchVnode(elmToMove, newStartVnode);\n                        oldCh[idxInOld] = undefined;\n                        insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);\n                    }\n                }\n                newStartVnode = newCh[++newStartIdx];\n            }\n        }\n        if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) {\n            if (oldStartIdx > oldEndIdx) {\n                before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;\n                addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx);\n            }\n            else {\n                removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);\n            }\n        }\n    }\n    function patchVnode(oldVnode, vnode) {\n        var elm = (vnode.elm = oldVnode.elm);\n        var oldCh = oldVnode.children;\n        var ch = vnode.children;\n        if (oldVnode === vnode) {\n            return;\n        }\n        updateAttrs(oldVnode, vnode);\n        if (isUndef(vnode.text)) {\n            if (isDef(oldCh) && isDef(ch)) {\n                if (oldCh !== ch) {\n                    updateChildren(elm, oldCh, ch);\n                }\n            }\n            else if (isDef(ch)) {\n                if (isDef(oldVnode.text)) {\n                    setTextContent(elm, '');\n                }\n                addVnodes(elm, null, ch, 0, ch.length - 1);\n            }\n            else if (isDef(oldCh)) {\n                removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n            }\n            else if (isDef(oldVnode.text)) {\n                setTextContent(elm, '');\n            }\n        }\n        else if (oldVnode.text !== vnode.text) {\n            if (isDef(oldCh)) {\n                removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n            }\n            setTextContent(elm, vnode.text);\n        }\n    }\n    function patch(oldVnode, vnode) {\n        if (sameVnode(oldVnode, vnode)) {\n            patchVnode(oldVnode, vnode);\n        }\n        else {\n            var elm = oldVnode.elm;\n            var parent_2 = parentNode(elm);\n            createElm(vnode);\n            if (parent_2 !== null) {\n                insertBefore(parent_2, vnode.elm, nextSibling(elm));\n                removeVnodes(parent_2, [oldVnode], 0, 0);\n            }\n        }\n        return vnode;\n    }\n\n    var svgId = 0;\n    var SVGPainter = (function () {\n        function SVGPainter(root, storage, opts) {\n            this.type = 'svg';\n            this.refreshHover = createMethodNotSupport('refreshHover');\n            this.configLayer = createMethodNotSupport('configLayer');\n            this.storage = storage;\n            this._opts = opts = extend({}, opts);\n            this.root = root;\n            this._id = 'zr' + svgId++;\n            this._oldVNode = createSVGVNode(opts.width, opts.height);\n            if (root && !opts.ssr) {\n                var viewport = this._viewport = document.createElement('div');\n                viewport.style.cssText = 'position:relative;overflow:hidden';\n                var svgDom = this._svgDom = this._oldVNode.elm = createElement('svg');\n                updateAttrs(null, this._oldVNode);\n                viewport.appendChild(svgDom);\n                root.appendChild(viewport);\n            }\n            this.resize(opts.width, opts.height);\n        }\n        SVGPainter.prototype.getType = function () {\n            return this.type;\n        };\n        SVGPainter.prototype.getViewportRoot = function () {\n            return this._viewport;\n        };\n        SVGPainter.prototype.getViewportRootOffset = function () {\n            var viewportRoot = this.getViewportRoot();\n            if (viewportRoot) {\n                return {\n                    offsetLeft: viewportRoot.offsetLeft || 0,\n                    offsetTop: viewportRoot.offsetTop || 0\n                };\n            }\n        };\n        SVGPainter.prototype.getSvgDom = function () {\n            return this._svgDom;\n        };\n        SVGPainter.prototype.refresh = function () {\n            if (this.root) {\n                var vnode = this.renderToVNode({\n                    willUpdate: true\n                });\n                vnode.attrs.style = 'position:absolute;left:0;top:0;user-select:none';\n                patch(this._oldVNode, vnode);\n                this._oldVNode = vnode;\n            }\n        };\n        SVGPainter.prototype.renderOneToVNode = function (el) {\n            return brush$1(el, createBrushScope(this._id));\n        };\n        SVGPainter.prototype.renderToVNode = function (opts) {\n            opts = opts || {};\n            var list = this.storage.getDisplayList(true);\n            var width = this._width;\n            var height = this._height;\n            var scope = createBrushScope(this._id);\n            scope.animation = opts.animation;\n            scope.willUpdate = opts.willUpdate;\n            scope.compress = opts.compress;\n            var children = [];\n            var bgVNode = this._bgVNode = createBackgroundVNode(width, height, this._backgroundColor, scope);\n            bgVNode && children.push(bgVNode);\n            var mainVNode = !opts.compress\n                ? (this._mainVNode = createVNode('g', 'main', {}, [])) : null;\n            this._paintList(list, scope, mainVNode ? mainVNode.children : children);\n            mainVNode && children.push(mainVNode);\n            var defs = map(keys(scope.defs), function (id) { return scope.defs[id]; });\n            if (defs.length) {\n                children.push(createVNode('defs', 'defs', {}, defs));\n            }\n            if (opts.animation) {\n                var animationCssStr = getCssString(scope.cssNodes, scope.cssAnims, { newline: true });\n                if (animationCssStr) {\n                    var styleNode = createVNode('style', 'stl', {}, [], animationCssStr);\n                    children.push(styleNode);\n                }\n            }\n            return createSVGVNode(width, height, children, opts.useViewBox);\n        };\n        SVGPainter.prototype.renderToString = function (opts) {\n            opts = opts || {};\n            return vNodeToString(this.renderToVNode({\n                animation: retrieve2(opts.cssAnimation, true),\n                willUpdate: false,\n                compress: true,\n                useViewBox: retrieve2(opts.useViewBox, true)\n            }), { newline: true });\n        };\n        SVGPainter.prototype.setBackgroundColor = function (backgroundColor) {\n            this._backgroundColor = backgroundColor;\n        };\n        SVGPainter.prototype.getSvgRoot = function () {\n            return this._mainVNode && this._mainVNode.elm;\n        };\n        SVGPainter.prototype._paintList = function (list, scope, out) {\n            var listLen = list.length;\n            var clipPathsGroupsStack = [];\n            var clipPathsGroupsStackDepth = 0;\n            var currentClipPathGroup;\n            var prevClipPaths;\n            var clipGroupNodeIdx = 0;\n            for (var i = 0; i < listLen; i++) {\n                var displayable = list[i];\n                if (!displayable.invisible) {\n                    var clipPaths = displayable.__clipPaths;\n                    var len = clipPaths && clipPaths.length || 0;\n                    var prevLen = prevClipPaths && prevClipPaths.length || 0;\n                    var lca = void 0;\n                    for (lca = Math.max(len - 1, prevLen - 1); lca >= 0; lca--) {\n                        if (clipPaths && prevClipPaths\n                            && clipPaths[lca] === prevClipPaths[lca]) {\n                            break;\n                        }\n                    }\n                    for (var i_1 = prevLen - 1; i_1 > lca; i_1--) {\n                        clipPathsGroupsStackDepth--;\n                        currentClipPathGroup = clipPathsGroupsStack[clipPathsGroupsStackDepth - 1];\n                    }\n                    for (var i_2 = lca + 1; i_2 < len; i_2++) {\n                        var groupAttrs = {};\n                        setClipPath(clipPaths[i_2], groupAttrs, scope);\n                        var g = createVNode('g', 'clip-g-' + clipGroupNodeIdx++, groupAttrs, []);\n                        (currentClipPathGroup ? currentClipPathGroup.children : out).push(g);\n                        clipPathsGroupsStack[clipPathsGroupsStackDepth++] = g;\n                        currentClipPathGroup = g;\n                    }\n                    prevClipPaths = clipPaths;\n                    var ret = brush$1(displayable, scope);\n                    if (ret) {\n                        (currentClipPathGroup ? currentClipPathGroup.children : out).push(ret);\n                    }\n                }\n            }\n        };\n        SVGPainter.prototype.resize = function (width, height) {\n            var opts = this._opts;\n            var root = this.root;\n            var viewport = this._viewport;\n            width != null && (opts.width = width);\n            height != null && (opts.height = height);\n            if (root && viewport) {\n                viewport.style.display = 'none';\n                width = getSize(root, 0, opts);\n                height = getSize(root, 1, opts);\n                viewport.style.display = '';\n            }\n            if (this._width !== width || this._height !== height) {\n                this._width = width;\n                this._height = height;\n                if (viewport) {\n                    var viewportStyle = viewport.style;\n                    viewportStyle.width = width + 'px';\n                    viewportStyle.height = height + 'px';\n                }\n                if (!isPattern(this._backgroundColor)) {\n                    var svgDom = this._svgDom;\n                    if (svgDom) {\n                        svgDom.setAttribute('width', width);\n                        svgDom.setAttribute('height', height);\n                    }\n                    var bgEl = this._bgVNode && this._bgVNode.elm;\n                    if (bgEl) {\n                        bgEl.setAttribute('width', width);\n                        bgEl.setAttribute('height', height);\n                    }\n                }\n                else {\n                    this.refresh();\n                }\n            }\n        };\n        SVGPainter.prototype.getWidth = function () {\n            return this._width;\n        };\n        SVGPainter.prototype.getHeight = function () {\n            return this._height;\n        };\n        SVGPainter.prototype.dispose = function () {\n            if (this.root) {\n                this.root.innerHTML = '';\n            }\n            this._svgDom =\n                this._viewport =\n                    this.storage =\n                        this._oldVNode =\n                            this._bgVNode =\n                                this._mainVNode = null;\n        };\n        SVGPainter.prototype.clear = function () {\n            if (this._svgDom) {\n                this._svgDom.innerHTML = null;\n            }\n            this._oldVNode = null;\n        };\n        SVGPainter.prototype.toDataURL = function (base64) {\n            var str = this.renderToString();\n            var prefix = 'data:image/svg+xml;';\n            if (base64) {\n                str = encodeBase64(str);\n                return str && prefix + 'base64,' + str;\n            }\n            return prefix + 'charset=UTF-8,' + encodeURIComponent(str);\n        };\n        return SVGPainter;\n    }());\n    function createMethodNotSupport(method) {\n        return function () {\n            if (\"development\" !== 'production') {\n                logError('In SVG mode painter not support method \"' + method + '\"');\n            }\n        };\n    }\n    function createBackgroundVNode(width, height, backgroundColor, scope) {\n        var bgVNode;\n        if (backgroundColor && backgroundColor !== 'none') {\n            bgVNode = createVNode('rect', 'bg', {\n                width: width,\n                height: height,\n                x: '0',\n                y: '0',\n                id: '0'\n            });\n            if (isGradient(backgroundColor)) {\n                setGradient({ fill: backgroundColor }, bgVNode.attrs, 'fill', scope);\n            }\n            else if (isPattern(backgroundColor)) {\n                setPattern({\n                    style: {\n                        fill: backgroundColor\n                    },\n                    dirty: noop,\n                    getBoundingRect: function () { return ({ width: width, height: height }); }\n                }, bgVNode.attrs, 'fill', scope);\n            }\n            else {\n                var _a = normalizeColor(backgroundColor), color = _a.color, opacity = _a.opacity;\n                bgVNode.attrs.fill = color;\n                opacity < 1 && (bgVNode.attrs['fill-opacity'] = opacity);\n            }\n        }\n        return bgVNode;\n    }\n\n    function install(registers) {\n      registers.registerPainter('svg', SVGPainter);\n    }\n\n    function createDom(id, painter, dpr) {\n        var newDom = platformApi.createCanvas();\n        var width = painter.getWidth();\n        var height = painter.getHeight();\n        var newDomStyle = newDom.style;\n        if (newDomStyle) {\n            newDomStyle.position = 'absolute';\n            newDomStyle.left = '0';\n            newDomStyle.top = '0';\n            newDomStyle.width = width + 'px';\n            newDomStyle.height = height + 'px';\n            newDom.setAttribute('data-zr-dom-id', id);\n        }\n        newDom.width = width * dpr;\n        newDom.height = height * dpr;\n        return newDom;\n    }\n    var Layer = (function (_super) {\n        __extends(Layer, _super);\n        function Layer(id, painter, dpr) {\n            var _this = _super.call(this) || this;\n            _this.motionBlur = false;\n            _this.lastFrameAlpha = 0.7;\n            _this.dpr = 1;\n            _this.virtual = false;\n            _this.config = {};\n            _this.incremental = false;\n            _this.zlevel = 0;\n            _this.maxRepaintRectCount = 5;\n            _this.__dirty = true;\n            _this.__firstTimePaint = true;\n            _this.__used = false;\n            _this.__drawIndex = 0;\n            _this.__startIndex = 0;\n            _this.__endIndex = 0;\n            _this.__prevStartIndex = null;\n            _this.__prevEndIndex = null;\n            var dom;\n            dpr = dpr || devicePixelRatio;\n            if (typeof id === 'string') {\n                dom = createDom(id, painter, dpr);\n            }\n            else if (isObject(id)) {\n                dom = id;\n                id = dom.id;\n            }\n            _this.id = id;\n            _this.dom = dom;\n            var domStyle = dom.style;\n            if (domStyle) {\n                disableUserSelect(dom);\n                dom.onselectstart = function () { return false; };\n                domStyle.padding = '0';\n                domStyle.margin = '0';\n                domStyle.borderWidth = '0';\n            }\n            _this.painter = painter;\n            _this.dpr = dpr;\n            return _this;\n        }\n        Layer.prototype.getElementCount = function () {\n            return this.__endIndex - this.__startIndex;\n        };\n        Layer.prototype.afterBrush = function () {\n            this.__prevStartIndex = this.__startIndex;\n            this.__prevEndIndex = this.__endIndex;\n        };\n        Layer.prototype.initContext = function () {\n            this.ctx = this.dom.getContext('2d');\n            this.ctx.dpr = this.dpr;\n        };\n        Layer.prototype.setUnpainted = function () {\n            this.__firstTimePaint = true;\n        };\n        Layer.prototype.createBackBuffer = function () {\n            var dpr = this.dpr;\n            this.domBack = createDom('back-' + this.id, this.painter, dpr);\n            this.ctxBack = this.domBack.getContext('2d');\n            if (dpr !== 1) {\n                this.ctxBack.scale(dpr, dpr);\n            }\n        };\n        Layer.prototype.createRepaintRects = function (displayList, prevList, viewWidth, viewHeight) {\n            if (this.__firstTimePaint) {\n                this.__firstTimePaint = false;\n                return null;\n            }\n            var mergedRepaintRects = [];\n            var maxRepaintRectCount = this.maxRepaintRectCount;\n            var full = false;\n            var pendingRect = new BoundingRect(0, 0, 0, 0);\n            function addRectToMergePool(rect) {\n                if (!rect.isFinite() || rect.isZero()) {\n                    return;\n                }\n                if (mergedRepaintRects.length === 0) {\n                    var boundingRect = new BoundingRect(0, 0, 0, 0);\n                    boundingRect.copy(rect);\n                    mergedRepaintRects.push(boundingRect);\n                }\n                else {\n                    var isMerged = false;\n                    var minDeltaArea = Infinity;\n                    var bestRectToMergeIdx = 0;\n                    for (var i = 0; i < mergedRepaintRects.length; ++i) {\n                        var mergedRect = mergedRepaintRects[i];\n                        if (mergedRect.intersect(rect)) {\n                            var pendingRect_1 = new BoundingRect(0, 0, 0, 0);\n                            pendingRect_1.copy(mergedRect);\n                            pendingRect_1.union(rect);\n                            mergedRepaintRects[i] = pendingRect_1;\n                            isMerged = true;\n                            break;\n                        }\n                        else if (full) {\n                            pendingRect.copy(rect);\n                            pendingRect.union(mergedRect);\n                            var aArea = rect.width * rect.height;\n                            var bArea = mergedRect.width * mergedRect.height;\n                            var pendingArea = pendingRect.width * pendingRect.height;\n                            var deltaArea = pendingArea - aArea - bArea;\n                            if (deltaArea < minDeltaArea) {\n                                minDeltaArea = deltaArea;\n                                bestRectToMergeIdx = i;\n                            }\n                        }\n                    }\n                    if (full) {\n                        mergedRepaintRects[bestRectToMergeIdx].union(rect);\n                        isMerged = true;\n                    }\n                    if (!isMerged) {\n                        var boundingRect = new BoundingRect(0, 0, 0, 0);\n                        boundingRect.copy(rect);\n                        mergedRepaintRects.push(boundingRect);\n                    }\n                    if (!full) {\n                        full = mergedRepaintRects.length >= maxRepaintRectCount;\n                    }\n                }\n            }\n            for (var i = this.__startIndex; i < this.__endIndex; ++i) {\n                var el = displayList[i];\n                if (el) {\n                    var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n                    var prevRect = el.__isRendered && ((el.__dirty & REDRAW_BIT) || !shouldPaint)\n                        ? el.getPrevPaintRect()\n                        : null;\n                    if (prevRect) {\n                        addRectToMergePool(prevRect);\n                    }\n                    var curRect = shouldPaint && ((el.__dirty & REDRAW_BIT) || !el.__isRendered)\n                        ? el.getPaintRect()\n                        : null;\n                    if (curRect) {\n                        addRectToMergePool(curRect);\n                    }\n                }\n            }\n            for (var i = this.__prevStartIndex; i < this.__prevEndIndex; ++i) {\n                var el = prevList[i];\n                var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n                if (el && (!shouldPaint || !el.__zr) && el.__isRendered) {\n                    var prevRect = el.getPrevPaintRect();\n                    if (prevRect) {\n                        addRectToMergePool(prevRect);\n                    }\n                }\n            }\n            var hasIntersections;\n            do {\n                hasIntersections = false;\n                for (var i = 0; i < mergedRepaintRects.length;) {\n                    if (mergedRepaintRects[i].isZero()) {\n                        mergedRepaintRects.splice(i, 1);\n                        continue;\n                    }\n                    for (var j = i + 1; j < mergedRepaintRects.length;) {\n                        if (mergedRepaintRects[i].intersect(mergedRepaintRects[j])) {\n                            hasIntersections = true;\n                            mergedRepaintRects[i].union(mergedRepaintRects[j]);\n                            mergedRepaintRects.splice(j, 1);\n                        }\n                        else {\n                            j++;\n                        }\n                    }\n                    i++;\n                }\n            } while (hasIntersections);\n            this._paintRects = mergedRepaintRects;\n            return mergedRepaintRects;\n        };\n        Layer.prototype.debugGetPaintRects = function () {\n            return (this._paintRects || []).slice();\n        };\n        Layer.prototype.resize = function (width, height) {\n            var dpr = this.dpr;\n            var dom = this.dom;\n            var domStyle = dom.style;\n            var domBack = this.domBack;\n            if (domStyle) {\n                domStyle.width = width + 'px';\n                domStyle.height = height + 'px';\n            }\n            dom.width = width * dpr;\n            dom.height = height * dpr;\n            if (domBack) {\n                domBack.width = width * dpr;\n                domBack.height = height * dpr;\n                if (dpr !== 1) {\n                    this.ctxBack.scale(dpr, dpr);\n                }\n            }\n        };\n        Layer.prototype.clear = function (clearAll, clearColor, repaintRects) {\n            var dom = this.dom;\n            var ctx = this.ctx;\n            var width = dom.width;\n            var height = dom.height;\n            clearColor = clearColor || this.clearColor;\n            var haveMotionBLur = this.motionBlur && !clearAll;\n            var lastFrameAlpha = this.lastFrameAlpha;\n            var dpr = this.dpr;\n            var self = this;\n            if (haveMotionBLur) {\n                if (!this.domBack) {\n                    this.createBackBuffer();\n                }\n                this.ctxBack.globalCompositeOperation = 'copy';\n                this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr);\n            }\n            var domBack = this.domBack;\n            function doClear(x, y, width, height) {\n                ctx.clearRect(x, y, width, height);\n                if (clearColor && clearColor !== 'transparent') {\n                    var clearColorGradientOrPattern = void 0;\n                    if (isGradientObject(clearColor)) {\n                        var shouldCache = clearColor.global || (clearColor.__width === width\n                            && clearColor.__height === height);\n                        clearColorGradientOrPattern = shouldCache\n                            && clearColor.__canvasGradient\n                            || getCanvasGradient(ctx, clearColor, {\n                                x: 0,\n                                y: 0,\n                                width: width,\n                                height: height\n                            });\n                        clearColor.__canvasGradient = clearColorGradientOrPattern;\n                        clearColor.__width = width;\n                        clearColor.__height = height;\n                    }\n                    else if (isImagePatternObject(clearColor)) {\n                        clearColor.scaleX = clearColor.scaleX || dpr;\n                        clearColor.scaleY = clearColor.scaleY || dpr;\n                        clearColorGradientOrPattern = createCanvasPattern(ctx, clearColor, {\n                            dirty: function () {\n                                self.setUnpainted();\n                                self.__painter.refresh();\n                            }\n                        });\n                    }\n                    ctx.save();\n                    ctx.fillStyle = clearColorGradientOrPattern || clearColor;\n                    ctx.fillRect(x, y, width, height);\n                    ctx.restore();\n                }\n                if (haveMotionBLur) {\n                    ctx.save();\n                    ctx.globalAlpha = lastFrameAlpha;\n                    ctx.drawImage(domBack, x, y, width, height);\n                    ctx.restore();\n                }\n            }\n            if (!repaintRects || haveMotionBLur) {\n                doClear(0, 0, width, height);\n            }\n            else if (repaintRects.length) {\n                each(repaintRects, function (rect) {\n                    doClear(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n                });\n            }\n        };\n        return Layer;\n    }(Eventful));\n\n    var HOVER_LAYER_ZLEVEL = 1e5;\n    var CANVAS_ZLEVEL = 314159;\n    var EL_AFTER_INCREMENTAL_INC = 0.01;\n    var INCREMENTAL_INC = 0.001;\n    function isLayerValid(layer) {\n        if (!layer) {\n            return false;\n        }\n        if (layer.__builtin__) {\n            return true;\n        }\n        if (typeof (layer.resize) !== 'function'\n            || typeof (layer.refresh) !== 'function') {\n            return false;\n        }\n        return true;\n    }\n    function createRoot(width, height) {\n        var domRoot = document.createElement('div');\n        domRoot.style.cssText = [\n            'position:relative',\n            'width:' + width + 'px',\n            'height:' + height + 'px',\n            'padding:0',\n            'margin:0',\n            'border-width:0'\n        ].join(';') + ';';\n        return domRoot;\n    }\n    var CanvasPainter = (function () {\n        function CanvasPainter(root, storage, opts, id) {\n            this.type = 'canvas';\n            this._zlevelList = [];\n            this._prevDisplayList = [];\n            this._layers = {};\n            this._layerConfig = {};\n            this._needsManuallyCompositing = false;\n            this.type = 'canvas';\n            var singleCanvas = !root.nodeName\n                || root.nodeName.toUpperCase() === 'CANVAS';\n            this._opts = opts = extend({}, opts || {});\n            this.dpr = opts.devicePixelRatio || devicePixelRatio;\n            this._singleCanvas = singleCanvas;\n            this.root = root;\n            var rootStyle = root.style;\n            if (rootStyle) {\n                disableUserSelect(root);\n                root.innerHTML = '';\n            }\n            this.storage = storage;\n            var zlevelList = this._zlevelList;\n            this._prevDisplayList = [];\n            var layers = this._layers;\n            if (!singleCanvas) {\n                this._width = getSize(root, 0, opts);\n                this._height = getSize(root, 1, opts);\n                var domRoot = this._domRoot = createRoot(this._width, this._height);\n                root.appendChild(domRoot);\n            }\n            else {\n                var rootCanvas = root;\n                var width = rootCanvas.width;\n                var height = rootCanvas.height;\n                if (opts.width != null) {\n                    width = opts.width;\n                }\n                if (opts.height != null) {\n                    height = opts.height;\n                }\n                this.dpr = opts.devicePixelRatio || 1;\n                rootCanvas.width = width * this.dpr;\n                rootCanvas.height = height * this.dpr;\n                this._width = width;\n                this._height = height;\n                var mainLayer = new Layer(rootCanvas, this, this.dpr);\n                mainLayer.__builtin__ = true;\n                mainLayer.initContext();\n                layers[CANVAS_ZLEVEL] = mainLayer;\n                mainLayer.zlevel = CANVAS_ZLEVEL;\n                zlevelList.push(CANVAS_ZLEVEL);\n                this._domRoot = root;\n            }\n        }\n        CanvasPainter.prototype.getType = function () {\n            return 'canvas';\n        };\n        CanvasPainter.prototype.isSingleCanvas = function () {\n            return this._singleCanvas;\n        };\n        CanvasPainter.prototype.getViewportRoot = function () {\n            return this._domRoot;\n        };\n        CanvasPainter.prototype.getViewportRootOffset = function () {\n            var viewportRoot = this.getViewportRoot();\n            if (viewportRoot) {\n                return {\n                    offsetLeft: viewportRoot.offsetLeft || 0,\n                    offsetTop: viewportRoot.offsetTop || 0\n                };\n            }\n        };\n        CanvasPainter.prototype.refresh = function (paintAll) {\n            var list = this.storage.getDisplayList(true);\n            var prevList = this._prevDisplayList;\n            var zlevelList = this._zlevelList;\n            this._redrawId = Math.random();\n            this._paintList(list, prevList, paintAll, this._redrawId);\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (!layer.__builtin__ && layer.refresh) {\n                    var clearColor = i === 0 ? this._backgroundColor : null;\n                    layer.refresh(clearColor);\n                }\n            }\n            if (this._opts.useDirtyRect) {\n                this._prevDisplayList = list.slice();\n            }\n            return this;\n        };\n        CanvasPainter.prototype.refreshHover = function () {\n            this._paintHoverList(this.storage.getDisplayList(false));\n        };\n        CanvasPainter.prototype._paintHoverList = function (list) {\n            var len = list.length;\n            var hoverLayer = this._hoverlayer;\n            hoverLayer && hoverLayer.clear();\n            if (!len) {\n                return;\n            }\n            var scope = {\n                inHover: true,\n                viewWidth: this._width,\n                viewHeight: this._height\n            };\n            var ctx;\n            for (var i = 0; i < len; i++) {\n                var el = list[i];\n                if (el.__inHover) {\n                    if (!hoverLayer) {\n                        hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);\n                    }\n                    if (!ctx) {\n                        ctx = hoverLayer.ctx;\n                        ctx.save();\n                    }\n                    brush(ctx, el, scope, i === len - 1);\n                }\n            }\n            if (ctx) {\n                ctx.restore();\n            }\n        };\n        CanvasPainter.prototype.getHoverLayer = function () {\n            return this.getLayer(HOVER_LAYER_ZLEVEL);\n        };\n        CanvasPainter.prototype.paintOne = function (ctx, el) {\n            brushSingle(ctx, el);\n        };\n        CanvasPainter.prototype._paintList = function (list, prevList, paintAll, redrawId) {\n            if (this._redrawId !== redrawId) {\n                return;\n            }\n            paintAll = paintAll || false;\n            this._updateLayerStatus(list);\n            var _a = this._doPaintList(list, prevList, paintAll), finished = _a.finished, needsRefreshHover = _a.needsRefreshHover;\n            if (this._needsManuallyCompositing) {\n                this._compositeManually();\n            }\n            if (needsRefreshHover) {\n                this._paintHoverList(list);\n            }\n            if (!finished) {\n                var self_1 = this;\n                requestAnimationFrame$1(function () {\n                    self_1._paintList(list, prevList, paintAll, redrawId);\n                });\n            }\n            else {\n                this.eachLayer(function (layer) {\n                    layer.afterBrush && layer.afterBrush();\n                });\n            }\n        };\n        CanvasPainter.prototype._compositeManually = function () {\n            var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;\n            var width = this._domRoot.width;\n            var height = this._domRoot.height;\n            ctx.clearRect(0, 0, width, height);\n            this.eachBuiltinLayer(function (layer) {\n                if (layer.virtual) {\n                    ctx.drawImage(layer.dom, 0, 0, width, height);\n                }\n            });\n        };\n        CanvasPainter.prototype._doPaintList = function (list, prevList, paintAll) {\n            var _this = this;\n            var layerList = [];\n            var useDirtyRect = this._opts.useDirtyRect;\n            for (var zi = 0; zi < this._zlevelList.length; zi++) {\n                var zlevel = this._zlevelList[zi];\n                var layer = this._layers[zlevel];\n                if (layer.__builtin__\n                    && layer !== this._hoverlayer\n                    && (layer.__dirty || paintAll)) {\n                    layerList.push(layer);\n                }\n            }\n            var finished = true;\n            var needsRefreshHover = false;\n            var _loop_1 = function (k) {\n                var layer = layerList[k];\n                var ctx = layer.ctx;\n                var repaintRects = useDirtyRect\n                    && layer.createRepaintRects(list, prevList, this_1._width, this_1._height);\n                var start = paintAll ? layer.__startIndex : layer.__drawIndex;\n                var useTimer = !paintAll && layer.incremental && Date.now;\n                var startTime = useTimer && Date.now();\n                var clearColor = layer.zlevel === this_1._zlevelList[0]\n                    ? this_1._backgroundColor : null;\n                if (layer.__startIndex === layer.__endIndex) {\n                    layer.clear(false, clearColor, repaintRects);\n                }\n                else if (start === layer.__startIndex) {\n                    var firstEl = list[start];\n                    if (!firstEl.incremental || !firstEl.notClear || paintAll) {\n                        layer.clear(false, clearColor, repaintRects);\n                    }\n                }\n                if (start === -1) {\n                    console.error('For some unknown reason. drawIndex is -1');\n                    start = layer.__startIndex;\n                }\n                var i;\n                var repaint = function (repaintRect) {\n                    var scope = {\n                        inHover: false,\n                        allClipped: false,\n                        prevEl: null,\n                        viewWidth: _this._width,\n                        viewHeight: _this._height\n                    };\n                    for (i = start; i < layer.__endIndex; i++) {\n                        var el = list[i];\n                        if (el.__inHover) {\n                            needsRefreshHover = true;\n                        }\n                        _this._doPaintEl(el, layer, useDirtyRect, repaintRect, scope, i === layer.__endIndex - 1);\n                        if (useTimer) {\n                            var dTime = Date.now() - startTime;\n                            if (dTime > 15) {\n                                break;\n                            }\n                        }\n                    }\n                    if (scope.prevElClipPaths) {\n                        ctx.restore();\n                    }\n                };\n                if (repaintRects) {\n                    if (repaintRects.length === 0) {\n                        i = layer.__endIndex;\n                    }\n                    else {\n                        var dpr = this_1.dpr;\n                        for (var r = 0; r < repaintRects.length; ++r) {\n                            var rect = repaintRects[r];\n                            ctx.save();\n                            ctx.beginPath();\n                            ctx.rect(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n                            ctx.clip();\n                            repaint(rect);\n                            ctx.restore();\n                        }\n                    }\n                }\n                else {\n                    ctx.save();\n                    repaint();\n                    ctx.restore();\n                }\n                layer.__drawIndex = i;\n                if (layer.__drawIndex < layer.__endIndex) {\n                    finished = false;\n                }\n            };\n            var this_1 = this;\n            for (var k = 0; k < layerList.length; k++) {\n                _loop_1(k);\n            }\n            if (env.wxa) {\n                each(this._layers, function (layer) {\n                    if (layer && layer.ctx && layer.ctx.draw) {\n                        layer.ctx.draw();\n                    }\n                });\n            }\n            return {\n                finished: finished,\n                needsRefreshHover: needsRefreshHover\n            };\n        };\n        CanvasPainter.prototype._doPaintEl = function (el, currentLayer, useDirtyRect, repaintRect, scope, isLast) {\n            var ctx = currentLayer.ctx;\n            if (useDirtyRect) {\n                var paintRect = el.getPaintRect();\n                if (!repaintRect || paintRect && paintRect.intersect(repaintRect)) {\n                    brush(ctx, el, scope, isLast);\n                    el.setPrevPaintRect(paintRect);\n                }\n            }\n            else {\n                brush(ctx, el, scope, isLast);\n            }\n        };\n        CanvasPainter.prototype.getLayer = function (zlevel, virtual) {\n            if (this._singleCanvas && !this._needsManuallyCompositing) {\n                zlevel = CANVAS_ZLEVEL;\n            }\n            var layer = this._layers[zlevel];\n            if (!layer) {\n                layer = new Layer('zr_' + zlevel, this, this.dpr);\n                layer.zlevel = zlevel;\n                layer.__builtin__ = true;\n                if (this._layerConfig[zlevel]) {\n                    merge(layer, this._layerConfig[zlevel], true);\n                }\n                else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) {\n                    merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true);\n                }\n                if (virtual) {\n                    layer.virtual = virtual;\n                }\n                this.insertLayer(zlevel, layer);\n                layer.initContext();\n            }\n            return layer;\n        };\n        CanvasPainter.prototype.insertLayer = function (zlevel, layer) {\n            var layersMap = this._layers;\n            var zlevelList = this._zlevelList;\n            var len = zlevelList.length;\n            var domRoot = this._domRoot;\n            var prevLayer = null;\n            var i = -1;\n            if (layersMap[zlevel]) {\n                if (\"development\" !== 'production') {\n                    logError('ZLevel ' + zlevel + ' has been used already');\n                }\n                return;\n            }\n            if (!isLayerValid(layer)) {\n                if (\"development\" !== 'production') {\n                    logError('Layer of zlevel ' + zlevel + ' is not valid');\n                }\n                return;\n            }\n            if (len > 0 && zlevel > zlevelList[0]) {\n                for (i = 0; i < len - 1; i++) {\n                    if (zlevelList[i] < zlevel\n                        && zlevelList[i + 1] > zlevel) {\n                        break;\n                    }\n                }\n                prevLayer = layersMap[zlevelList[i]];\n            }\n            zlevelList.splice(i + 1, 0, zlevel);\n            layersMap[zlevel] = layer;\n            if (!layer.virtual) {\n                if (prevLayer) {\n                    var prevDom = prevLayer.dom;\n                    if (prevDom.nextSibling) {\n                        domRoot.insertBefore(layer.dom, prevDom.nextSibling);\n                    }\n                    else {\n                        domRoot.appendChild(layer.dom);\n                    }\n                }\n                else {\n                    if (domRoot.firstChild) {\n                        domRoot.insertBefore(layer.dom, domRoot.firstChild);\n                    }\n                    else {\n                        domRoot.appendChild(layer.dom);\n                    }\n                }\n            }\n            layer.__painter = this;\n        };\n        CanvasPainter.prototype.eachLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                cb.call(context, this._layers[z], z);\n            }\n        };\n        CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (layer.__builtin__) {\n                    cb.call(context, layer, z);\n                }\n            }\n        };\n        CanvasPainter.prototype.eachOtherLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (!layer.__builtin__) {\n                    cb.call(context, layer, z);\n                }\n            }\n        };\n        CanvasPainter.prototype.getLayers = function () {\n            return this._layers;\n        };\n        CanvasPainter.prototype._updateLayerStatus = function (list) {\n            this.eachBuiltinLayer(function (layer, z) {\n                layer.__dirty = layer.__used = false;\n            });\n            function updatePrevLayer(idx) {\n                if (prevLayer) {\n                    if (prevLayer.__endIndex !== idx) {\n                        prevLayer.__dirty = true;\n                    }\n                    prevLayer.__endIndex = idx;\n                }\n            }\n            if (this._singleCanvas) {\n                for (var i_1 = 1; i_1 < list.length; i_1++) {\n                    var el = list[i_1];\n                    if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) {\n                        this._needsManuallyCompositing = true;\n                        break;\n                    }\n                }\n            }\n            var prevLayer = null;\n            var incrementalLayerCount = 0;\n            var prevZlevel;\n            var i;\n            for (i = 0; i < list.length; i++) {\n                var el = list[i];\n                var zlevel = el.zlevel;\n                var layer = void 0;\n                if (prevZlevel !== zlevel) {\n                    prevZlevel = zlevel;\n                    incrementalLayerCount = 0;\n                }\n                if (el.incremental) {\n                    layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);\n                    layer.incremental = true;\n                    incrementalLayerCount = 1;\n                }\n                else {\n                    layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);\n                }\n                if (!layer.__builtin__) {\n                    logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);\n                }\n                if (layer !== prevLayer) {\n                    layer.__used = true;\n                    if (layer.__startIndex !== i) {\n                        layer.__dirty = true;\n                    }\n                    layer.__startIndex = i;\n                    if (!layer.incremental) {\n                        layer.__drawIndex = i;\n                    }\n                    else {\n                        layer.__drawIndex = -1;\n                    }\n                    updatePrevLayer(i);\n                    prevLayer = layer;\n                }\n                if ((el.__dirty & REDRAW_BIT) && !el.__inHover) {\n                    layer.__dirty = true;\n                    if (layer.incremental && layer.__drawIndex < 0) {\n                        layer.__drawIndex = i;\n                    }\n                }\n            }\n            updatePrevLayer(i);\n            this.eachBuiltinLayer(function (layer, z) {\n                if (!layer.__used && layer.getElementCount() > 0) {\n                    layer.__dirty = true;\n                    layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;\n                }\n                if (layer.__dirty && layer.__drawIndex < 0) {\n                    layer.__drawIndex = layer.__startIndex;\n                }\n            });\n        };\n        CanvasPainter.prototype.clear = function () {\n            this.eachBuiltinLayer(this._clearLayer);\n            return this;\n        };\n        CanvasPainter.prototype._clearLayer = function (layer) {\n            layer.clear();\n        };\n        CanvasPainter.prototype.setBackgroundColor = function (backgroundColor) {\n            this._backgroundColor = backgroundColor;\n            each(this._layers, function (layer) {\n                layer.setUnpainted();\n            });\n        };\n        CanvasPainter.prototype.configLayer = function (zlevel, config) {\n            if (config) {\n                var layerConfig = this._layerConfig;\n                if (!layerConfig[zlevel]) {\n                    layerConfig[zlevel] = config;\n                }\n                else {\n                    merge(layerConfig[zlevel], config, true);\n                }\n                for (var i = 0; i < this._zlevelList.length; i++) {\n                    var _zlevel = this._zlevelList[i];\n                    if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {\n                        var layer = this._layers[_zlevel];\n                        merge(layer, layerConfig[zlevel], true);\n                    }\n                }\n            }\n        };\n        CanvasPainter.prototype.delLayer = function (zlevel) {\n            var layers = this._layers;\n            var zlevelList = this._zlevelList;\n            var layer = layers[zlevel];\n            if (!layer) {\n                return;\n            }\n            layer.dom.parentNode.removeChild(layer.dom);\n            delete layers[zlevel];\n            zlevelList.splice(indexOf(zlevelList, zlevel), 1);\n        };\n        CanvasPainter.prototype.resize = function (width, height) {\n            if (!this._domRoot.style) {\n                if (width == null || height == null) {\n                    return;\n                }\n                this._width = width;\n                this._height = height;\n                this.getLayer(CANVAS_ZLEVEL).resize(width, height);\n            }\n            else {\n                var domRoot = this._domRoot;\n                domRoot.style.display = 'none';\n                var opts = this._opts;\n                var root = this.root;\n                width != null && (opts.width = width);\n                height != null && (opts.height = height);\n                width = getSize(root, 0, opts);\n                height = getSize(root, 1, opts);\n                domRoot.style.display = '';\n                if (this._width !== width || height !== this._height) {\n                    domRoot.style.width = width + 'px';\n                    domRoot.style.height = height + 'px';\n                    for (var id in this._layers) {\n                        if (this._layers.hasOwnProperty(id)) {\n                            this._layers[id].resize(width, height);\n                        }\n                    }\n                    this.refresh(true);\n                }\n                this._width = width;\n                this._height = height;\n            }\n            return this;\n        };\n        CanvasPainter.prototype.clearLayer = function (zlevel) {\n            var layer = this._layers[zlevel];\n            if (layer) {\n                layer.clear();\n            }\n        };\n        CanvasPainter.prototype.dispose = function () {\n            this.root.innerHTML = '';\n            this.root =\n                this.storage =\n                    this._domRoot =\n                        this._layers = null;\n        };\n        CanvasPainter.prototype.getRenderedCanvas = function (opts) {\n            opts = opts || {};\n            if (this._singleCanvas && !this._compositeManually) {\n                return this._layers[CANVAS_ZLEVEL].dom;\n            }\n            var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);\n            imageLayer.initContext();\n            imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);\n            var ctx = imageLayer.ctx;\n            if (opts.pixelRatio <= this.dpr) {\n                this.refresh();\n                var width_1 = imageLayer.dom.width;\n                var height_1 = imageLayer.dom.height;\n                this.eachLayer(function (layer) {\n                    if (layer.__builtin__) {\n                        ctx.drawImage(layer.dom, 0, 0, width_1, height_1);\n                    }\n                    else if (layer.renderToCanvas) {\n                        ctx.save();\n                        layer.renderToCanvas(ctx);\n                        ctx.restore();\n                    }\n                });\n            }\n            else {\n                var scope = {\n                    inHover: false,\n                    viewWidth: this._width,\n                    viewHeight: this._height\n                };\n                var displayList = this.storage.getDisplayList(true);\n                for (var i = 0, len = displayList.length; i < len; i++) {\n                    var el = displayList[i];\n                    brush(ctx, el, scope, i === len - 1);\n                }\n            }\n            return imageLayer.dom;\n        };\n        CanvasPainter.prototype.getWidth = function () {\n            return this._width;\n        };\n        CanvasPainter.prototype.getHeight = function () {\n            return this._height;\n        };\n        return CanvasPainter;\n    }());\n\n    function install$1(registers) {\n      registers.registerPainter('canvas', CanvasPainter);\n    }\n\n    var LineSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(LineSeriesModel, _super);\n\n      function LineSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LineSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      }\n\n      LineSeriesModel.prototype.getInitialData = function (option) {\n        if (\"development\" !== 'production') {\n          var coordSys = option.coordinateSystem;\n\n          if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {\n            throw new Error('Line not support coordinateSystem besides cartesian and polar');\n          }\n        }\n\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      LineSeriesModel.prototype.getLegendIcon = function (opt) {\n        var group = new Group();\n        var line = createSymbol('line', 0, opt.itemHeight / 2, opt.itemWidth, 0, opt.lineStyle.stroke, false);\n        group.add(line);\n        line.setStyle(opt.lineStyle);\n        var visualType = this.getData().getVisual('symbol');\n        var visualRotate = this.getData().getVisual('symbolRotate');\n        var symbolType = visualType === 'none' ? 'circle' : visualType; // Symbol size is 80% when there is a line\n\n        var size = opt.itemHeight * 0.8;\n        var symbol = createSymbol(symbolType, (opt.itemWidth - size) / 2, (opt.itemHeight - size) / 2, size, size, opt.itemStyle.fill);\n        group.add(symbol);\n        symbol.setStyle(opt.itemStyle);\n        var symbolRotate = opt.iconRotate === 'inherit' ? visualRotate : opt.iconRotate || 0;\n        symbol.rotation = symbolRotate * Math.PI / 180;\n        symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);\n\n        if (symbolType.indexOf('empty') > -1) {\n          symbol.style.stroke = symbol.style.fill;\n          symbol.style.fill = '#fff';\n          symbol.style.lineWidth = 2;\n        }\n\n        return group;\n      };\n\n      LineSeriesModel.type = 'series.line';\n      LineSeriesModel.dependencies = ['grid', 'polar'];\n      LineSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 3,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        clip: true,\n        label: {\n          position: 'top'\n        },\n        // itemStyle: {\n        // },\n        endLabel: {\n          show: false,\n          valueAnimation: true,\n          distance: 8\n        },\n        lineStyle: {\n          width: 2,\n          type: 'solid'\n        },\n        emphasis: {\n          scale: true\n        },\n        // areaStyle: {\n        // origin of areaStyle. Valid values:\n        // `'auto'/null/undefined`: from axisLine to data\n        // `'start'`: from min to data\n        // `'end'`: from data to max\n        // origin: 'auto'\n        // },\n        // false, 'start', 'end', 'middle'\n        step: false,\n        // Disabled if step is true\n        smooth: false,\n        smoothMonotone: null,\n        symbol: 'emptyCircle',\n        symbolSize: 4,\n        symbolRotate: null,\n        showSymbol: true,\n        // `false`: follow the label interval strategy.\n        // `true`: show all symbols.\n        // `'auto'`: If possible, show all symbols, otherwise\n        //           follow the label interval strategy.\n        showAllSymbol: 'auto',\n        // Whether to connect break point.\n        connectNulls: false,\n        // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'.\n        sampling: 'none',\n        animationEasing: 'linear',\n        // Disable progressive\n        progressive: 0,\n        hoverLayerThreshold: Infinity,\n        universalTransition: {\n          divideShape: 'clone'\n        },\n        triggerLineEvent: false\n      };\n      return LineSeriesModel;\n    }(SeriesModel);\n\n    /**\n     * @return label string. Not null/undefined\n     */\n\n    function getDefaultLabel(data, dataIndex) {\n      var labelDims = data.mapDimensionsAll('defaultedLabel');\n      var len = labelDims.length; // Simple optimization (in lots of cases, label dims length is 1)\n\n      if (len === 1) {\n        var rawVal = retrieveRawValue(data, dataIndex, labelDims[0]);\n        return rawVal != null ? rawVal + '' : null;\n      } else if (len) {\n        var vals = [];\n\n        for (var i = 0; i < labelDims.length; i++) {\n          vals.push(retrieveRawValue(data, dataIndex, labelDims[i]));\n        }\n\n        return vals.join(' ');\n      }\n    }\n    function getDefaultInterpolatedLabel(data, interpolatedValue) {\n      var labelDims = data.mapDimensionsAll('defaultedLabel');\n\n      if (!isArray(interpolatedValue)) {\n        return interpolatedValue + '';\n      }\n\n      var vals = [];\n\n      for (var i = 0; i < labelDims.length; i++) {\n        var dimIndex = data.getDimensionIndex(labelDims[i]);\n\n        if (dimIndex >= 0) {\n          vals.push(interpolatedValue[dimIndex]);\n        }\n      }\n\n      return vals.join(' ');\n    }\n\n    var Symbol =\n    /** @class */\n    function (_super) {\n      __extends(Symbol, _super);\n\n      function Symbol(data, idx, seriesScope, opts) {\n        var _this = _super.call(this) || this;\n\n        _this.updateData(data, idx, seriesScope, opts);\n\n        return _this;\n      }\n\n      Symbol.prototype._createSymbol = function (symbolType, data, idx, symbolSize, keepAspect) {\n        // Remove paths created before\n        this.removeAll(); // let symbolPath = createSymbol(\n        //     symbolType, -0.5, -0.5, 1, 1, color\n        // );\n        // If width/height are set too small (e.g., set to 1) on ios10\n        // and macOS Sierra, a circle stroke become a rect, no matter what\n        // the scale is set. So we set width/height as 2. See #4150.\n\n        var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, null, keepAspect);\n        symbolPath.attr({\n          z2: 100,\n          culling: true,\n          scaleX: symbolSize[0] / 2,\n          scaleY: symbolSize[1] / 2\n        }); // Rewrite drift method\n\n        symbolPath.drift = driftSymbol;\n        this._symbolType = symbolType;\n        this.add(symbolPath);\n      };\n      /**\n       * Stop animation\n       * @param {boolean} toLastFrame\n       */\n\n\n      Symbol.prototype.stopSymbolAnimation = function (toLastFrame) {\n        this.childAt(0).stopAnimation(null, toLastFrame);\n      };\n\n      Symbol.prototype.getSymbolType = function () {\n        return this._symbolType;\n      };\n      /**\n       * FIXME:\n       * Caution: This method breaks the encapsulation of this module,\n       * but it indeed brings convenience. So do not use the method\n       * unless you detailedly know all the implements of `Symbol`,\n       * especially animation.\n       *\n       * Get symbol path element.\n       */\n\n\n      Symbol.prototype.getSymbolPath = function () {\n        return this.childAt(0);\n      };\n      /**\n       * Highlight symbol\n       */\n\n\n      Symbol.prototype.highlight = function () {\n        enterEmphasis(this.childAt(0));\n      };\n      /**\n       * Downplay symbol\n       */\n\n\n      Symbol.prototype.downplay = function () {\n        leaveEmphasis(this.childAt(0));\n      };\n      /**\n       * @param {number} zlevel\n       * @param {number} z\n       */\n\n\n      Symbol.prototype.setZ = function (zlevel, z) {\n        var symbolPath = this.childAt(0);\n        symbolPath.zlevel = zlevel;\n        symbolPath.z = z;\n      };\n\n      Symbol.prototype.setDraggable = function (draggable, hasCursorOption) {\n        var symbolPath = this.childAt(0);\n        symbolPath.draggable = draggable;\n        symbolPath.cursor = !hasCursorOption && draggable ? 'move' : symbolPath.cursor;\n      };\n      /**\n       * Update symbol properties\n       */\n\n\n      Symbol.prototype.updateData = function (data, idx, seriesScope, opts) {\n        this.silent = false;\n        var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';\n        var seriesModel = data.hostModel;\n        var symbolSize = Symbol.getSymbolSize(data, idx);\n        var isInit = symbolType !== this._symbolType;\n        var disableAnimation = opts && opts.disableAnimation;\n\n        if (isInit) {\n          var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect');\n\n          this._createSymbol(symbolType, data, idx, symbolSize, keepAspect);\n        } else {\n          var symbolPath = this.childAt(0);\n          symbolPath.silent = false;\n          var target = {\n            scaleX: symbolSize[0] / 2,\n            scaleY: symbolSize[1] / 2\n          };\n          disableAnimation ? symbolPath.attr(target) : updateProps(symbolPath, target, seriesModel, idx);\n          saveOldStyle(symbolPath);\n        }\n\n        this._updateCommon(data, idx, symbolSize, seriesScope, opts);\n\n        if (isInit) {\n          var symbolPath = this.childAt(0);\n\n          if (!disableAnimation) {\n            var target = {\n              scaleX: this._sizeX,\n              scaleY: this._sizeY,\n              style: {\n                // Always fadeIn. Because it has fadeOut animation when symbol is removed..\n                opacity: symbolPath.style.opacity\n              }\n            };\n            symbolPath.scaleX = symbolPath.scaleY = 0;\n            symbolPath.style.opacity = 0;\n            initProps(symbolPath, target, seriesModel, idx);\n          }\n        }\n\n        if (disableAnimation) {\n          // Must stop leave transition manually if don't call initProps or updateProps.\n          this.childAt(0).stopAnimation('leave');\n        }\n      };\n\n      Symbol.prototype._updateCommon = function (data, idx, symbolSize, seriesScope, opts) {\n        var symbolPath = this.childAt(0);\n        var seriesModel = data.hostModel;\n        var emphasisItemStyle;\n        var blurItemStyle;\n        var selectItemStyle;\n        var focus;\n        var blurScope;\n        var emphasisDisabled;\n        var labelStatesModels;\n        var hoverScale;\n        var cursorStyle;\n\n        if (seriesScope) {\n          emphasisItemStyle = seriesScope.emphasisItemStyle;\n          blurItemStyle = seriesScope.blurItemStyle;\n          selectItemStyle = seriesScope.selectItemStyle;\n          focus = seriesScope.focus;\n          blurScope = seriesScope.blurScope;\n          labelStatesModels = seriesScope.labelStatesModels;\n          hoverScale = seriesScope.hoverScale;\n          cursorStyle = seriesScope.cursorStyle;\n          emphasisDisabled = seriesScope.emphasisDisabled;\n        }\n\n        if (!seriesScope || data.hasItemOption) {\n          var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx);\n          var emphasisModel = itemModel.getModel('emphasis');\n          emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n          selectItemStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();\n          blurItemStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n          focus = emphasisModel.get('focus');\n          blurScope = emphasisModel.get('blurScope');\n          emphasisDisabled = emphasisModel.get('disabled');\n          labelStatesModels = getLabelStatesModels(itemModel);\n          hoverScale = emphasisModel.getShallow('scale');\n          cursorStyle = itemModel.getShallow('cursor');\n        }\n\n        var symbolRotate = data.getItemVisual(idx, 'symbolRotate');\n        symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);\n        var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize);\n\n        if (symbolOffset) {\n          symbolPath.x = symbolOffset[0];\n          symbolPath.y = symbolOffset[1];\n        }\n\n        cursorStyle && symbolPath.attr('cursor', cursorStyle);\n        var symbolStyle = data.getItemVisual(idx, 'style');\n        var visualColor = symbolStyle.fill;\n\n        if (symbolPath instanceof ZRImage) {\n          var pathStyle = symbolPath.style;\n          symbolPath.useStyle(extend({\n            // TODO other properties like x, y ?\n            image: pathStyle.image,\n            x: pathStyle.x,\n            y: pathStyle.y,\n            width: pathStyle.width,\n            height: pathStyle.height\n          }, symbolStyle));\n        } else {\n          if (symbolPath.__isEmptyBrush) {\n            // fill and stroke will be swapped if it's empty.\n            // So we cloned a new style to avoid it affecting the original style in visual storage.\n            // TODO Better implementation. No empty logic!\n            symbolPath.useStyle(extend({}, symbolStyle));\n          } else {\n            symbolPath.useStyle(symbolStyle);\n          } // Disable decal because symbol scale will been applied on the decal.\n\n\n          symbolPath.style.decal = null;\n          symbolPath.setColor(visualColor, opts && opts.symbolInnerColor);\n          symbolPath.style.strokeNoScale = true;\n        }\n\n        var liftZ = data.getItemVisual(idx, 'liftZ');\n        var z2Origin = this._z2;\n\n        if (liftZ != null) {\n          if (z2Origin == null) {\n            this._z2 = symbolPath.z2;\n            symbolPath.z2 += liftZ;\n          }\n        } else if (z2Origin != null) {\n          symbolPath.z2 = z2Origin;\n          this._z2 = null;\n        }\n\n        var useNameLabel = opts && opts.useNameLabel;\n        setLabelStyle(symbolPath, labelStatesModels, {\n          labelFetcher: seriesModel,\n          labelDataIndex: idx,\n          defaultText: getLabelDefaultText,\n          inheritColor: visualColor,\n          defaultOpacity: symbolStyle.opacity\n        }); // Do not execute util needed.\n\n        function getLabelDefaultText(idx) {\n          return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);\n        }\n\n        this._sizeX = symbolSize[0] / 2;\n        this._sizeY = symbolSize[1] / 2;\n        var emphasisState = symbolPath.ensureState('emphasis');\n        emphasisState.style = emphasisItemStyle;\n        symbolPath.ensureState('select').style = selectItemStyle;\n        symbolPath.ensureState('blur').style = blurItemStyle; // null / undefined / true means to use default strategy.\n        // 0 / false / negative number / NaN / Infinity means no scale.\n\n        var scaleRatio = hoverScale == null || hoverScale === true ? Math.max(1.1, 3 / this._sizeY) // PENDING: restrict hoverScale > 1? It seems unreasonable to scale down\n        : isFinite(hoverScale) && hoverScale > 0 ? +hoverScale : 1; // always set scale to allow resetting\n\n        emphasisState.scaleX = this._sizeX * scaleRatio;\n        emphasisState.scaleY = this._sizeY * scaleRatio;\n        this.setSymbolScale(1);\n        toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n      };\n\n      Symbol.prototype.setSymbolScale = function (scale) {\n        this.scaleX = this.scaleY = scale;\n      };\n\n      Symbol.prototype.fadeOut = function (cb, seriesModel, opt) {\n        var symbolPath = this.childAt(0);\n        var dataIndex = getECData(this).dataIndex;\n        var animationOpt = opt && opt.animation; // Avoid mistaken hover when fading out\n\n        this.silent = symbolPath.silent = true; // Not show text when animating\n\n        if (opt && opt.fadeLabel) {\n          var textContent = symbolPath.getTextContent();\n\n          if (textContent) {\n            removeElement(textContent, {\n              style: {\n                opacity: 0\n              }\n            }, seriesModel, {\n              dataIndex: dataIndex,\n              removeOpt: animationOpt,\n              cb: function () {\n                symbolPath.removeTextContent();\n              }\n            });\n          }\n        } else {\n          symbolPath.removeTextContent();\n        }\n\n        removeElement(symbolPath, {\n          style: {\n            opacity: 0\n          },\n          scaleX: 0,\n          scaleY: 0\n        }, seriesModel, {\n          dataIndex: dataIndex,\n          cb: cb,\n          removeOpt: animationOpt\n        });\n      };\n\n      Symbol.getSymbolSize = function (data, idx) {\n        return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n      };\n\n      return Symbol;\n    }(Group);\n\n    function driftSymbol(dx, dy) {\n      this.parent.drift(dx, dy);\n    }\n\n    function symbolNeedsDraw(data, point, idx, opt) {\n      return point && !isNaN(point[0]) && !isNaN(point[1]) && !(opt.isIgnore && opt.isIgnore(idx)) // We do not set clipShape on group, because it will cut part of\n      // the symbol element shape. We use the same clip shape here as\n      // the line clip.\n      && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) && data.getItemVisual(idx, 'symbol') !== 'none';\n    }\n\n    function normalizeUpdateOpt(opt) {\n      if (opt != null && !isObject(opt)) {\n        opt = {\n          isIgnore: opt\n        };\n      }\n\n      return opt || {};\n    }\n\n    function makeSeriesScope(data) {\n      var seriesModel = data.hostModel;\n      var emphasisModel = seriesModel.getModel('emphasis');\n      return {\n        emphasisItemStyle: emphasisModel.getModel('itemStyle').getItemStyle(),\n        blurItemStyle: seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(),\n        selectItemStyle: seriesModel.getModel(['select', 'itemStyle']).getItemStyle(),\n        focus: emphasisModel.get('focus'),\n        blurScope: emphasisModel.get('blurScope'),\n        emphasisDisabled: emphasisModel.get('disabled'),\n        hoverScale: emphasisModel.get('scale'),\n        labelStatesModels: getLabelStatesModels(seriesModel),\n        cursorStyle: seriesModel.get('cursor')\n      };\n    }\n\n    var SymbolDraw =\n    /** @class */\n    function () {\n      function SymbolDraw(SymbolCtor) {\n        this.group = new Group();\n        this._SymbolCtor = SymbolCtor || Symbol;\n      }\n      /**\n       * Update symbols draw by new data\n       */\n\n\n      SymbolDraw.prototype.updateData = function (data, opt) {\n        // Remove progressive els.\n        this._progressiveEls = null;\n        opt = normalizeUpdateOpt(opt);\n        var group = this.group;\n        var seriesModel = data.hostModel;\n        var oldData = this._data;\n        var SymbolCtor = this._SymbolCtor;\n        var disableAnimation = opt.disableAnimation;\n        var seriesScope = makeSeriesScope(data);\n        var symbolUpdateOpt = {\n          disableAnimation: disableAnimation\n        };\n\n        var getSymbolPoint = opt.getSymbolPoint || function (idx) {\n          return data.getItemLayout(idx);\n        }; // There is no oldLineData only when first rendering or switching from\n        // stream mode to normal mode, where previous elements should be removed.\n\n\n        if (!oldData) {\n          group.removeAll();\n        }\n\n        data.diff(oldData).add(function (newIdx) {\n          var point = getSymbolPoint(newIdx);\n\n          if (symbolNeedsDraw(data, point, newIdx, opt)) {\n            var symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n            symbolEl.setPosition(point);\n            data.setItemGraphicEl(newIdx, symbolEl);\n            group.add(symbolEl);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var symbolEl = oldData.getItemGraphicEl(oldIdx);\n          var point = getSymbolPoint(newIdx);\n\n          if (!symbolNeedsDraw(data, point, newIdx, opt)) {\n            group.remove(symbolEl);\n            return;\n          }\n\n          var newSymbolType = data.getItemVisual(newIdx, 'symbol') || 'circle';\n          var oldSymbolType = symbolEl && symbolEl.getSymbolType && symbolEl.getSymbolType();\n\n          if (!symbolEl // Create a new if symbol type changed.\n          || oldSymbolType && oldSymbolType !== newSymbolType) {\n            group.remove(symbolEl);\n            symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n            symbolEl.setPosition(point);\n          } else {\n            symbolEl.updateData(data, newIdx, seriesScope, symbolUpdateOpt);\n            var target = {\n              x: point[0],\n              y: point[1]\n            };\n            disableAnimation ? symbolEl.attr(target) : updateProps(symbolEl, target, seriesModel);\n          } // Add back\n\n\n          group.add(symbolEl);\n          data.setItemGraphicEl(newIdx, symbolEl);\n        }).remove(function (oldIdx) {\n          var el = oldData.getItemGraphicEl(oldIdx);\n          el && el.fadeOut(function () {\n            group.remove(el);\n          }, seriesModel);\n        }).execute();\n        this._getSymbolPoint = getSymbolPoint;\n        this._data = data;\n      };\n\n      SymbolDraw.prototype.updateLayout = function () {\n        var _this = this;\n\n        var data = this._data;\n\n        if (data) {\n          // Not use animation\n          data.eachItemGraphicEl(function (el, idx) {\n            var point = _this._getSymbolPoint(idx);\n\n            el.setPosition(point);\n            el.markRedraw();\n          });\n        }\n      };\n\n      SymbolDraw.prototype.incrementalPrepareUpdate = function (data) {\n        this._seriesScope = makeSeriesScope(data);\n        this._data = null;\n        this.group.removeAll();\n      };\n      /**\n       * Update symbols draw by new data\n       */\n\n      SymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {\n        // Clear\n        this._progressiveEls = [];\n        opt = normalizeUpdateOpt(opt);\n\n        function updateIncrementalAndHover(el) {\n          if (!el.isGroup) {\n            el.incremental = true;\n            el.ensureState('emphasis').hoverLayer = true;\n          }\n        }\n\n        for (var idx = taskParams.start; idx < taskParams.end; idx++) {\n          var point = data.getItemLayout(idx);\n\n          if (symbolNeedsDraw(data, point, idx, opt)) {\n            var el = new this._SymbolCtor(data, idx, this._seriesScope);\n            el.traverse(updateIncrementalAndHover);\n            el.setPosition(point);\n            this.group.add(el);\n            data.setItemGraphicEl(idx, el);\n\n            this._progressiveEls.push(el);\n          }\n        }\n      };\n\n      SymbolDraw.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      SymbolDraw.prototype.remove = function (enableAnimation) {\n        var group = this.group;\n        var data = this._data; // Incremental model do not have this._data.\n\n        if (data && enableAnimation) {\n          data.eachItemGraphicEl(function (el) {\n            el.fadeOut(function () {\n              group.remove(el);\n            }, data.hostModel);\n          });\n        } else {\n          group.removeAll();\n        }\n      };\n      return SymbolDraw;\n    }();\n\n    function prepareDataCoordInfo(coordSys, data, valueOrigin) {\n      var baseAxis = coordSys.getBaseAxis();\n      var valueAxis = coordSys.getOtherAxis(baseAxis);\n      var valueStart = getValueStart(valueAxis, valueOrigin);\n      var baseAxisDim = baseAxis.dim;\n      var valueAxisDim = valueAxis.dim;\n      var valueDim = data.mapDimension(valueAxisDim);\n      var baseDim = data.mapDimension(baseAxisDim);\n      var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;\n      var dims = map(coordSys.dimensions, function (coordDim) {\n        return data.mapDimension(coordDim);\n      });\n      var stacked = false;\n      var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n      if (isDimensionStacked(data, dims[0]\n      /* , dims[1] */\n      )) {\n        // jshint ignore:line\n        stacked = true;\n        dims[0] = stackResultDim;\n      }\n\n      if (isDimensionStacked(data, dims[1]\n      /* , dims[0] */\n      )) {\n        // jshint ignore:line\n        stacked = true;\n        dims[1] = stackResultDim;\n      }\n\n      return {\n        dataDimsForPoint: dims,\n        valueStart: valueStart,\n        valueAxisDim: valueAxisDim,\n        baseAxisDim: baseAxisDim,\n        stacked: !!stacked,\n        valueDim: valueDim,\n        baseDim: baseDim,\n        baseDataOffset: baseDataOffset,\n        stackedOverDimension: data.getCalculationInfo('stackedOverDimension')\n      };\n    }\n\n    function getValueStart(valueAxis, valueOrigin) {\n      var valueStart = 0;\n      var extent = valueAxis.scale.getExtent();\n\n      if (valueOrigin === 'start') {\n        valueStart = extent[0];\n      } else if (valueOrigin === 'end') {\n        valueStart = extent[1];\n      } // If origin is specified as a number, use it as\n      // valueStart directly\n      else if (isNumber(valueOrigin) && !isNaN(valueOrigin)) {\n          valueStart = valueOrigin;\n        } // auto\n        else {\n            // Both positive\n            if (extent[0] > 0) {\n              valueStart = extent[0];\n            } // Both negative\n            else if (extent[1] < 0) {\n                valueStart = extent[1];\n              } // If is one positive, and one negative, onZero shall be true\n\n          }\n\n      return valueStart;\n    }\n\n    function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) {\n      var value = NaN;\n\n      if (dataCoordInfo.stacked) {\n        value = data.get(data.getCalculationInfo('stackedOverDimension'), idx);\n      }\n\n      if (isNaN(value)) {\n        value = dataCoordInfo.valueStart;\n      }\n\n      var baseDataOffset = dataCoordInfo.baseDataOffset;\n      var stackedData = [];\n      stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx);\n      stackedData[1 - baseDataOffset] = value;\n      return coordSys.dataToPoint(stackedData);\n    }\n\n    function diffData(oldData, newData) {\n      var diffResult = [];\n      newData.diff(oldData).add(function (idx) {\n        diffResult.push({\n          cmd: '+',\n          idx: idx\n        });\n      }).update(function (newIdx, oldIdx) {\n        diffResult.push({\n          cmd: '=',\n          idx: oldIdx,\n          idx1: newIdx\n        });\n      }).remove(function (idx) {\n        diffResult.push({\n          cmd: '-',\n          idx: idx\n        });\n      }).execute();\n      return diffResult;\n    }\n\n    function lineAnimationDiff(oldData, newData, oldStackedOnPoints, newStackedOnPoints, oldCoordSys, newCoordSys, oldValueOrigin, newValueOrigin) {\n      var diff = diffData(oldData, newData); // let newIdList = newData.mapArray(newData.getId);\n      // let oldIdList = oldData.mapArray(oldData.getId);\n      // convertToIntId(newIdList, oldIdList);\n      // // FIXME One data ?\n      // diff = arrayDiff(oldIdList, newIdList);\n\n      var currPoints = [];\n      var nextPoints = []; // Points for stacking base line\n\n      var currStackedPoints = [];\n      var nextStackedPoints = [];\n      var status = [];\n      var sortedIndices = [];\n      var rawIndices = [];\n      var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); // const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin);\n\n      var oldPoints = oldData.getLayout('points') || [];\n      var newPoints = newData.getLayout('points') || [];\n\n      for (var i = 0; i < diff.length; i++) {\n        var diffItem = diff[i];\n        var pointAdded = true;\n        var oldIdx2 = void 0;\n        var newIdx2 = void 0; // FIXME, animation is not so perfect when dataZoom window moves fast\n        // Which is in case remvoing or add more than one data in the tail or head\n\n        switch (diffItem.cmd) {\n          case '=':\n            oldIdx2 = diffItem.idx * 2;\n            newIdx2 = diffItem.idx1 * 2;\n            var currentX = oldPoints[oldIdx2];\n            var currentY = oldPoints[oldIdx2 + 1];\n            var nextX = newPoints[newIdx2];\n            var nextY = newPoints[newIdx2 + 1]; // If previous data is NaN, use next point directly\n\n            if (isNaN(currentX) || isNaN(currentY)) {\n              currentX = nextX;\n              currentY = nextY;\n            }\n\n            currPoints.push(currentX, currentY);\n            nextPoints.push(nextX, nextY);\n            currStackedPoints.push(oldStackedOnPoints[oldIdx2], oldStackedOnPoints[oldIdx2 + 1]);\n            nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n            rawIndices.push(newData.getRawIndex(diffItem.idx1));\n            break;\n\n          case '+':\n            var newIdx = diffItem.idx;\n            var newDataDimsForPoint = newDataOldCoordInfo.dataDimsForPoint;\n            var oldPt = oldCoordSys.dataToPoint([newData.get(newDataDimsForPoint[0], newIdx), newData.get(newDataDimsForPoint[1], newIdx)]);\n            newIdx2 = newIdx * 2;\n            currPoints.push(oldPt[0], oldPt[1]);\n            nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1]);\n            var stackedOnPoint = getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, newIdx);\n            currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1]);\n            nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n            rawIndices.push(newData.getRawIndex(newIdx));\n            break;\n\n          case '-':\n            pointAdded = false;\n        } // Original indices\n\n\n        if (pointAdded) {\n          status.push(diffItem);\n          sortedIndices.push(sortedIndices.length);\n        }\n      } // Diff result may be crossed if all items are changed\n      // Sort by data index\n\n\n      sortedIndices.sort(function (a, b) {\n        return rawIndices[a] - rawIndices[b];\n      });\n      var len = currPoints.length;\n      var sortedCurrPoints = createFloat32Array(len);\n      var sortedNextPoints = createFloat32Array(len);\n      var sortedCurrStackedPoints = createFloat32Array(len);\n      var sortedNextStackedPoints = createFloat32Array(len);\n      var sortedStatus = [];\n\n      for (var i = 0; i < sortedIndices.length; i++) {\n        var idx = sortedIndices[i];\n        var i2 = i * 2;\n        var idx2 = idx * 2;\n        sortedCurrPoints[i2] = currPoints[idx2];\n        sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1];\n        sortedNextPoints[i2] = nextPoints[idx2];\n        sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1];\n        sortedCurrStackedPoints[i2] = currStackedPoints[idx2];\n        sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1];\n        sortedNextStackedPoints[i2] = nextStackedPoints[idx2];\n        sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1];\n        sortedStatus[i] = status[idx];\n      }\n\n      return {\n        current: sortedCurrPoints,\n        next: sortedNextPoints,\n        stackedOnCurrent: sortedCurrStackedPoints,\n        stackedOnNext: sortedNextStackedPoints,\n        status: sortedStatus\n      };\n    }\n\n    var mathMin$5 = Math.min;\n    var mathMax$5 = Math.max;\n\n    function isPointNull(x, y) {\n      return isNaN(x) || isNaN(y);\n    }\n    /**\n     * Draw smoothed line in non-monotone, in may cause undesired curve in extreme\n     * situations. This should be used when points are non-monotone neither in x or\n     * y dimension.\n     */\n\n\n    function drawSegment(ctx, points, start, segLen, allLen, dir, smooth, smoothMonotone, connectNulls) {\n      var prevX;\n      var prevY;\n      var cpx0;\n      var cpy0;\n      var cpx1;\n      var cpy1;\n      var idx = start;\n      var k = 0;\n\n      for (; k < segLen; k++) {\n        var x = points[idx * 2];\n        var y = points[idx * 2 + 1];\n\n        if (idx >= allLen || idx < 0) {\n          break;\n        }\n\n        if (isPointNull(x, y)) {\n          if (connectNulls) {\n            idx += dir;\n            continue;\n          }\n\n          break;\n        }\n\n        if (idx === start) {\n          ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y);\n          cpx0 = x;\n          cpy0 = y;\n        } else {\n          var dx = x - prevX;\n          var dy = y - prevY; // Ignore tiny segment.\n\n          if (dx * dx + dy * dy < 0.5) {\n            idx += dir;\n            continue;\n          }\n\n          if (smooth > 0) {\n            var nextIdx = idx + dir;\n            var nextX = points[nextIdx * 2];\n            var nextY = points[nextIdx * 2 + 1]; // Ignore duplicate point\n\n            while (nextX === x && nextY === y && k < segLen) {\n              k++;\n              nextIdx += dir;\n              idx += dir;\n              nextX = points[nextIdx * 2];\n              nextY = points[nextIdx * 2 + 1];\n              x = points[idx * 2];\n              y = points[idx * 2 + 1];\n              dx = x - prevX;\n              dy = y - prevY;\n            }\n\n            var tmpK = k + 1;\n\n            if (connectNulls) {\n              // Find next point not null\n              while (isPointNull(nextX, nextY) && tmpK < segLen) {\n                tmpK++;\n                nextIdx += dir;\n                nextX = points[nextIdx * 2];\n                nextY = points[nextIdx * 2 + 1];\n              }\n            }\n\n            var ratioNextSeg = 0.5;\n            var vx = 0;\n            var vy = 0;\n            var nextCpx0 = void 0;\n            var nextCpy0 = void 0; // Is last point\n\n            if (tmpK >= segLen || isPointNull(nextX, nextY)) {\n              cpx1 = x;\n              cpy1 = y;\n            } else {\n              vx = nextX - prevX;\n              vy = nextY - prevY;\n              var dx0 = x - prevX;\n              var dx1 = nextX - x;\n              var dy0 = y - prevY;\n              var dy1 = nextY - y;\n              var lenPrevSeg = void 0;\n              var lenNextSeg = void 0;\n\n              if (smoothMonotone === 'x') {\n                lenPrevSeg = Math.abs(dx0);\n                lenNextSeg = Math.abs(dx1);\n                var dir_1 = vx > 0 ? 1 : -1;\n                cpx1 = x - dir_1 * lenPrevSeg * smooth;\n                cpy1 = y;\n                nextCpx0 = x + dir_1 * lenNextSeg * smooth;\n                nextCpy0 = y;\n              } else if (smoothMonotone === 'y') {\n                lenPrevSeg = Math.abs(dy0);\n                lenNextSeg = Math.abs(dy1);\n                var dir_2 = vy > 0 ? 1 : -1;\n                cpx1 = x;\n                cpy1 = y - dir_2 * lenPrevSeg * smooth;\n                nextCpx0 = x;\n                nextCpy0 = y + dir_2 * lenNextSeg * smooth;\n              } else {\n                lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0);\n                lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1); // Use ratio of seg length\n\n                ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);\n                cpx1 = x - vx * smooth * (1 - ratioNextSeg);\n                cpy1 = y - vy * smooth * (1 - ratioNextSeg); // cp0 of next segment\n\n                nextCpx0 = x + vx * smooth * ratioNextSeg;\n                nextCpy0 = y + vy * smooth * ratioNextSeg; // Smooth constraint between point and next point.\n                // Avoid exceeding extreme after smoothing.\n\n                nextCpx0 = mathMin$5(nextCpx0, mathMax$5(nextX, x));\n                nextCpy0 = mathMin$5(nextCpy0, mathMax$5(nextY, y));\n                nextCpx0 = mathMax$5(nextCpx0, mathMin$5(nextX, x));\n                nextCpy0 = mathMax$5(nextCpy0, mathMin$5(nextY, y)); // Reclaculate cp1 based on the adjusted cp0 of next seg.\n\n                vx = nextCpx0 - x;\n                vy = nextCpy0 - y;\n                cpx1 = x - vx * lenPrevSeg / lenNextSeg;\n                cpy1 = y - vy * lenPrevSeg / lenNextSeg; // Smooth constraint between point and prev point.\n                // Avoid exceeding extreme after smoothing.\n\n                cpx1 = mathMin$5(cpx1, mathMax$5(prevX, x));\n                cpy1 = mathMin$5(cpy1, mathMax$5(prevY, y));\n                cpx1 = mathMax$5(cpx1, mathMin$5(prevX, x));\n                cpy1 = mathMax$5(cpy1, mathMin$5(prevY, y)); // Adjust next cp0 again.\n\n                vx = x - cpx1;\n                vy = y - cpy1;\n                nextCpx0 = x + vx * lenNextSeg / lenPrevSeg;\n                nextCpy0 = y + vy * lenNextSeg / lenPrevSeg;\n              }\n            }\n\n            ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y);\n            cpx0 = nextCpx0;\n            cpy0 = nextCpy0;\n          } else {\n            ctx.lineTo(x, y);\n          }\n        }\n\n        prevX = x;\n        prevY = y;\n        idx += dir;\n      }\n\n      return k;\n    }\n\n    var ECPolylineShape =\n    /** @class */\n    function () {\n      function ECPolylineShape() {\n        this.smooth = 0;\n        this.smoothConstraint = true;\n      }\n\n      return ECPolylineShape;\n    }();\n\n    var ECPolyline =\n    /** @class */\n    function (_super) {\n      __extends(ECPolyline, _super);\n\n      function ECPolyline(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-polyline';\n        return _this;\n      }\n\n      ECPolyline.prototype.getDefaultStyle = function () {\n        return {\n          stroke: '#000',\n          fill: null\n        };\n      };\n\n      ECPolyline.prototype.getDefaultShape = function () {\n        return new ECPolylineShape();\n      };\n\n      ECPolyline.prototype.buildPath = function (ctx, shape) {\n        var points = shape.points;\n        var i = 0;\n        var len = points.length / 2; // const result = getBoundingBox(points, shape.smoothConstraint);\n\n        if (shape.connectNulls) {\n          // Must remove first and last null values avoid draw error in polygon\n          for (; len > 0; len--) {\n            if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n              break;\n            }\n          }\n\n          for (; i < len; i++) {\n            if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n              break;\n            }\n          }\n        }\n\n        while (i < len) {\n          i += drawSegment(ctx, points, i, len, len, 1, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1;\n        }\n      };\n\n      ECPolyline.prototype.getPointOn = function (xOrY, dim) {\n        if (!this.path) {\n          this.createPathProxy();\n          this.buildPath(this.path, this.shape);\n        }\n\n        var path = this.path;\n        var data = path.data;\n        var CMD = PathProxy.CMD;\n        var x0;\n        var y0;\n        var isDimX = dim === 'x';\n        var roots = [];\n\n        for (var i = 0; i < data.length;) {\n          var cmd = data[i++];\n          var x = void 0;\n          var y = void 0;\n          var x2 = void 0;\n          var y2 = void 0;\n          var x3 = void 0;\n          var y3 = void 0;\n          var t = void 0;\n\n          switch (cmd) {\n            case CMD.M:\n              x0 = data[i++];\n              y0 = data[i++];\n              break;\n\n            case CMD.L:\n              x = data[i++];\n              y = data[i++];\n              t = isDimX ? (xOrY - x0) / (x - x0) : (xOrY - y0) / (y - y0);\n\n              if (t <= 1 && t >= 0) {\n                var val = isDimX ? (y - y0) * t + y0 : (x - x0) * t + x0;\n                return isDimX ? [xOrY, val] : [val, xOrY];\n              }\n\n              x0 = x;\n              y0 = y;\n              break;\n\n            case CMD.C:\n              x = data[i++];\n              y = data[i++];\n              x2 = data[i++];\n              y2 = data[i++];\n              x3 = data[i++];\n              y3 = data[i++];\n              var nRoot = isDimX ? cubicRootAt(x0, x, x2, x3, xOrY, roots) : cubicRootAt(y0, y, y2, y3, xOrY, roots);\n\n              if (nRoot > 0) {\n                for (var i_1 = 0; i_1 < nRoot; i_1++) {\n                  var t_1 = roots[i_1];\n\n                  if (t_1 <= 1 && t_1 >= 0) {\n                    var val = isDimX ? cubicAt(y0, y, y2, y3, t_1) : cubicAt(x0, x, x2, x3, t_1);\n                    return isDimX ? [xOrY, val] : [val, xOrY];\n                  }\n                }\n              }\n\n              x0 = x3;\n              y0 = y3;\n              break;\n          }\n        }\n      };\n\n      return ECPolyline;\n    }(Path);\n\n    var ECPolygonShape =\n    /** @class */\n    function (_super) {\n      __extends(ECPolygonShape, _super);\n\n      function ECPolygonShape() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return ECPolygonShape;\n    }(ECPolylineShape);\n\n    var ECPolygon =\n    /** @class */\n    function (_super) {\n      __extends(ECPolygon, _super);\n\n      function ECPolygon(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-polygon';\n        return _this;\n      }\n\n      ECPolygon.prototype.getDefaultShape = function () {\n        return new ECPolygonShape();\n      };\n\n      ECPolygon.prototype.buildPath = function (ctx, shape) {\n        var points = shape.points;\n        var stackedOnPoints = shape.stackedOnPoints;\n        var i = 0;\n        var len = points.length / 2;\n        var smoothMonotone = shape.smoothMonotone;\n\n        if (shape.connectNulls) {\n          // Must remove first and last null values avoid draw error in polygon\n          for (; len > 0; len--) {\n            if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n              break;\n            }\n          }\n\n          for (; i < len; i++) {\n            if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n              break;\n            }\n          }\n        }\n\n        while (i < len) {\n          var k = drawSegment(ctx, points, i, len, len, 1, shape.smooth, smoothMonotone, shape.connectNulls);\n          drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls);\n          i += k + 1;\n          ctx.closePath();\n        }\n      };\n\n      return ECPolygon;\n    }(Path);\n\n    function createGridClipPath(cartesian, hasAnimation, seriesModel, done, during) {\n      var rect = cartesian.getArea();\n      var x = rect.x;\n      var y = rect.y;\n      var width = rect.width;\n      var height = rect.height;\n      var lineWidth = seriesModel.get(['lineStyle', 'width']) || 2; // Expand the clip path a bit to avoid the border is clipped and looks thinner\n\n      x -= lineWidth / 2;\n      y -= lineWidth / 2;\n      width += lineWidth;\n      height += lineWidth; // fix: https://github.com/apache/incubator-echarts/issues/11369\n\n      x = Math.floor(x);\n      width = Math.round(width);\n      var clipPath = new Rect({\n        shape: {\n          x: x,\n          y: y,\n          width: width,\n          height: height\n        }\n      });\n\n      if (hasAnimation) {\n        var baseAxis = cartesian.getBaseAxis();\n        var isHorizontal = baseAxis.isHorizontal();\n        var isAxisInversed = baseAxis.inverse;\n\n        if (isHorizontal) {\n          if (isAxisInversed) {\n            clipPath.shape.x += width;\n          }\n\n          clipPath.shape.width = 0;\n        } else {\n          if (!isAxisInversed) {\n            clipPath.shape.y += height;\n          }\n\n          clipPath.shape.height = 0;\n        }\n\n        var duringCb = isFunction(during) ? function (percent) {\n          during(percent, clipPath);\n        } : null;\n        initProps(clipPath, {\n          shape: {\n            width: width,\n            height: height,\n            x: x,\n            y: y\n          }\n        }, seriesModel, null, done, duringCb);\n      }\n\n      return clipPath;\n    }\n\n    function createPolarClipPath(polar, hasAnimation, seriesModel) {\n      var sectorArea = polar.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n\n      var r0 = round(sectorArea.r0, 1);\n      var r = round(sectorArea.r, 1);\n      var clipPath = new Sector({\n        shape: {\n          cx: round(polar.cx, 1),\n          cy: round(polar.cy, 1),\n          r0: r0,\n          r: r,\n          startAngle: sectorArea.startAngle,\n          endAngle: sectorArea.endAngle,\n          clockwise: sectorArea.clockwise\n        }\n      });\n\n      if (hasAnimation) {\n        var isRadial = polar.getBaseAxis().dim === 'angle';\n\n        if (isRadial) {\n          clipPath.shape.endAngle = sectorArea.startAngle;\n        } else {\n          clipPath.shape.r = r0;\n        }\n\n        initProps(clipPath, {\n          shape: {\n            endAngle: sectorArea.endAngle,\n            r: r\n          }\n        }, seriesModel);\n      }\n\n      return clipPath;\n    }\n\n    function createClipPath(coordSys, hasAnimation, seriesModel, done, during) {\n      if (!coordSys) {\n        return null;\n      } else if (coordSys.type === 'polar') {\n        return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n      } else if (coordSys.type === 'cartesian2d') {\n        return createGridClipPath(coordSys, hasAnimation, seriesModel, done, during);\n      }\n\n      return null;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function isCoordinateSystemType(coordSys, type) {\n      return coordSys.type === type;\n    }\n\n    function isPointsSame(points1, points2) {\n      if (points1.length !== points2.length) {\n        return;\n      }\n\n      for (var i = 0; i < points1.length; i++) {\n        if (points1[i] !== points2[i]) {\n          return;\n        }\n      }\n\n      return true;\n    }\n\n    function bboxFromPoints(points) {\n      var minX = Infinity;\n      var minY = Infinity;\n      var maxX = -Infinity;\n      var maxY = -Infinity;\n\n      for (var i = 0; i < points.length;) {\n        var x = points[i++];\n        var y = points[i++];\n\n        if (!isNaN(x)) {\n          minX = Math.min(x, minX);\n          maxX = Math.max(x, maxX);\n        }\n\n        if (!isNaN(y)) {\n          minY = Math.min(y, minY);\n          maxY = Math.max(y, maxY);\n        }\n      }\n\n      return [[minX, minY], [maxX, maxY]];\n    }\n\n    function getBoundingDiff(points1, points2) {\n      var _a = bboxFromPoints(points1),\n          min1 = _a[0],\n          max1 = _a[1];\n\n      var _b = bboxFromPoints(points2),\n          min2 = _b[0],\n          max2 = _b[1]; // Get a max value from each corner of two boundings.\n\n\n      return Math.max(Math.abs(min1[0] - min2[0]), Math.abs(min1[1] - min2[1]), Math.abs(max1[0] - max2[0]), Math.abs(max1[1] - max2[1]));\n    }\n\n    function getSmooth(smooth) {\n      return isNumber(smooth) ? smooth : smooth ? 0.5 : 0;\n    }\n\n    function getStackedOnPoints(coordSys, data, dataCoordInfo) {\n      if (!dataCoordInfo.valueDim) {\n        return [];\n      }\n\n      var len = data.count();\n      var points = createFloat32Array(len * 2);\n\n      for (var idx = 0; idx < len; idx++) {\n        var pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx);\n        points[idx * 2] = pt[0];\n        points[idx * 2 + 1] = pt[1];\n      }\n\n      return points;\n    }\n\n    function turnPointsIntoStep(points, coordSys, stepTurnAt, connectNulls) {\n      var baseAxis = coordSys.getBaseAxis();\n      var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;\n      var stepPoints = [];\n      var i = 0;\n      var stepPt = [];\n      var pt = [];\n      var nextPt = [];\n      var filteredPoints = [];\n\n      if (connectNulls) {\n        for (i = 0; i < points.length; i += 2) {\n          if (!isNaN(points[i]) && !isNaN(points[i + 1])) {\n            filteredPoints.push(points[i], points[i + 1]);\n          }\n        }\n\n        points = filteredPoints;\n      }\n\n      for (i = 0; i < points.length - 2; i += 2) {\n        nextPt[0] = points[i + 2];\n        nextPt[1] = points[i + 3];\n        pt[0] = points[i];\n        pt[1] = points[i + 1];\n        stepPoints.push(pt[0], pt[1]);\n\n        switch (stepTurnAt) {\n          case 'end':\n            stepPt[baseIndex] = nextPt[baseIndex];\n            stepPt[1 - baseIndex] = pt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n            break;\n\n          case 'middle':\n            var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;\n            var stepPt2 = [];\n            stepPt[baseIndex] = stepPt2[baseIndex] = middle;\n            stepPt[1 - baseIndex] = pt[1 - baseIndex];\n            stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n            stepPoints.push(stepPt2[0], stepPt2[1]);\n            break;\n\n          default:\n            // default is start\n            stepPt[baseIndex] = pt[baseIndex];\n            stepPt[1 - baseIndex] = nextPt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n        }\n      } // Last points\n\n\n      stepPoints.push(points[i++], points[i++]);\n      return stepPoints;\n    }\n    /**\n     * Clip color stops to edge. Avoid creating too large gradients.\n     * Which may lead to blurry when GPU acceleration is enabled. See #15680\n     *\n     * The stops has been sorted from small to large.\n     */\n\n\n    function clipColorStops(colorStops, maxSize) {\n      var newColorStops = [];\n      var len = colorStops.length; // coord will always < 0 in prevOutOfRangeColorStop.\n\n      var prevOutOfRangeColorStop;\n      var prevInRangeColorStop;\n\n      function lerpStop(stop0, stop1, clippedCoord) {\n        var coord0 = stop0.coord;\n        var p = (clippedCoord - coord0) / (stop1.coord - coord0);\n        var color = lerp$1(p, [stop0.color, stop1.color]);\n        return {\n          coord: clippedCoord,\n          color: color\n        };\n      }\n\n      for (var i = 0; i < len; i++) {\n        var stop_1 = colorStops[i];\n        var coord = stop_1.coord;\n\n        if (coord < 0) {\n          prevOutOfRangeColorStop = stop_1;\n        } else if (coord > maxSize) {\n          if (prevInRangeColorStop) {\n            newColorStops.push(lerpStop(prevInRangeColorStop, stop_1, maxSize));\n          } else if (prevOutOfRangeColorStop) {\n            // If there are two stops and coord range is between these two stops\n            newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0), lerpStop(prevOutOfRangeColorStop, stop_1, maxSize));\n          } // All following stop will be out of range. So just ignore them.\n\n\n          break;\n        } else {\n          if (prevOutOfRangeColorStop) {\n            newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0)); // Reset\n\n            prevOutOfRangeColorStop = null;\n          }\n\n          newColorStops.push(stop_1);\n          prevInRangeColorStop = stop_1;\n        }\n      }\n\n      return newColorStops;\n    }\n\n    function getVisualGradient(data, coordSys, api) {\n      var visualMetaList = data.getVisual('visualMeta');\n\n      if (!visualMetaList || !visualMetaList.length || !data.count()) {\n        // When data.count() is 0, gradient range can not be calculated.\n        return;\n      }\n\n      if (coordSys.type !== 'cartesian2d') {\n        if (\"development\" !== 'production') {\n          console.warn('Visual map on line style is only supported on cartesian2d.');\n        }\n\n        return;\n      }\n\n      var coordDim;\n      var visualMeta;\n\n      for (var i = visualMetaList.length - 1; i >= 0; i--) {\n        var dimInfo = data.getDimensionInfo(visualMetaList[i].dimension);\n        coordDim = dimInfo && dimInfo.coordDim; // Can only be x or y\n\n        if (coordDim === 'x' || coordDim === 'y') {\n          visualMeta = visualMetaList[i];\n          break;\n        }\n      }\n\n      if (!visualMeta) {\n        if (\"development\" !== 'production') {\n          console.warn('Visual map on line style only support x or y dimension.');\n        }\n\n        return;\n      } // If the area to be rendered is bigger than area defined by LinearGradient,\n      // the canvas spec prescribes that the color of the first stop and the last\n      // stop should be used. But if two stops are added at offset 0, in effect\n      // browsers use the color of the second stop to render area outside\n      // LinearGradient. So we can only infinitesimally extend area defined in\n      // LinearGradient to render `outerColors`.\n\n\n      var axis = coordSys.getAxis(coordDim); // dataToCoord mapping may not be linear, but must be monotonic.\n\n      var colorStops = map(visualMeta.stops, function (stop) {\n        // offset will be calculated later.\n        return {\n          coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),\n          color: stop.color\n        };\n      });\n      var stopLen = colorStops.length;\n      var outerColors = visualMeta.outerColors.slice();\n\n      if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {\n        colorStops.reverse();\n        outerColors.reverse();\n      }\n\n      var colorStopsInRange = clipColorStops(colorStops, coordDim === 'x' ? api.getWidth() : api.getHeight());\n      var inRangeStopLen = colorStopsInRange.length;\n\n      if (!inRangeStopLen && stopLen) {\n        // All stops are out of range. All will be the same color.\n        return colorStops[0].coord < 0 ? outerColors[1] ? outerColors[1] : colorStops[stopLen - 1].color : outerColors[0] ? outerColors[0] : colorStops[0].color;\n      }\n\n      var tinyExtent = 10; // Arbitrary value: 10px\n\n      var minCoord = colorStopsInRange[0].coord - tinyExtent;\n      var maxCoord = colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent;\n      var coordSpan = maxCoord - minCoord;\n\n      if (coordSpan < 1e-3) {\n        return 'transparent';\n      }\n\n      each(colorStopsInRange, function (stop) {\n        stop.offset = (stop.coord - minCoord) / coordSpan;\n      });\n      colorStopsInRange.push({\n        // NOTE: inRangeStopLen may still be 0 if stoplen is zero.\n        offset: inRangeStopLen ? colorStopsInRange[inRangeStopLen - 1].offset : 0.5,\n        color: outerColors[1] || 'transparent'\n      });\n      colorStopsInRange.unshift({\n        offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5,\n        color: outerColors[0] || 'transparent'\n      });\n      var gradient = new LinearGradient(0, 0, 0, 0, colorStopsInRange, true);\n      gradient[coordDim] = minCoord;\n      gradient[coordDim + '2'] = maxCoord;\n      return gradient;\n    }\n\n    function getIsIgnoreFunc(seriesModel, data, coordSys) {\n      var showAllSymbol = seriesModel.get('showAllSymbol');\n      var isAuto = showAllSymbol === 'auto';\n\n      if (showAllSymbol && !isAuto) {\n        return;\n      }\n\n      var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n\n      if (!categoryAxis) {\n        return;\n      } // Note that category label interval strategy might bring some weird effect\n      // in some scenario: users may wonder why some of the symbols are not\n      // displayed. So we show all symbols as possible as we can.\n\n\n      if (isAuto // Simplify the logic, do not determine label overlap here.\n      && canShowAllSymbolForCategory(categoryAxis, data)) {\n        return;\n      } // Otherwise follow the label interval strategy on category axis.\n\n\n      var categoryDataDim = data.mapDimension(categoryAxis.dim);\n      var labelMap = {};\n      each(categoryAxis.getViewLabels(), function (labelItem) {\n        var ordinalNumber = categoryAxis.scale.getRawOrdinalNumber(labelItem.tickValue);\n        labelMap[ordinalNumber] = 1;\n      });\n      return function (dataIndex) {\n        return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex));\n      };\n    }\n\n    function canShowAllSymbolForCategory(categoryAxis, data) {\n      // In most cases, line is monotonous on category axis, and the label size\n      // is close with each other. So we check the symbol size and some of the\n      // label size alone with the category axis to estimate whether all symbol\n      // can be shown without overlap.\n      var axisExtent = categoryAxis.getExtent();\n      var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count();\n      isNaN(availSize) && (availSize = 0); // 0/0 is NaN.\n      // Sampling some points, max 5.\n\n      var dataLen = data.count();\n      var step = Math.max(1, Math.round(dataLen / 5));\n\n      for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) {\n        if (Symbol.getSymbolSize(data, dataIndex // Only for cartesian, where `isHorizontal` exists.\n        )[categoryAxis.isHorizontal() ? 1 : 0] // Empirical number\n        * 1.5 > availSize) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    function isPointNull$1(x, y) {\n      return isNaN(x) || isNaN(y);\n    }\n\n    function getLastIndexNotNull(points) {\n      var len = points.length / 2;\n\n      for (; len > 0; len--) {\n        if (!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])) {\n          break;\n        }\n      }\n\n      return len - 1;\n    }\n\n    function getPointAtIndex(points, idx) {\n      return [points[idx * 2], points[idx * 2 + 1]];\n    }\n\n    function getIndexRange(points, xOrY, dim) {\n      var len = points.length / 2;\n      var dimIdx = dim === 'x' ? 0 : 1;\n      var a;\n      var b;\n      var prevIndex = 0;\n      var nextIndex = -1;\n\n      for (var i = 0; i < len; i++) {\n        b = points[i * 2 + dimIdx];\n\n        if (isNaN(b) || isNaN(points[i * 2 + 1 - dimIdx])) {\n          continue;\n        }\n\n        if (i === 0) {\n          a = b;\n          continue;\n        }\n\n        if (a <= xOrY && b >= xOrY || a >= xOrY && b <= xOrY) {\n          nextIndex = i;\n          break;\n        }\n\n        prevIndex = i;\n        a = b;\n      }\n\n      return {\n        range: [prevIndex, nextIndex],\n        t: (xOrY - a) / (b - a)\n      };\n    }\n\n    function anyStateShowEndLabel(seriesModel) {\n      if (seriesModel.get(['endLabel', 'show'])) {\n        return true;\n      }\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        if (seriesModel.get([SPECIAL_STATES[i], 'endLabel', 'show'])) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    function createLineClipPath(lineView, coordSys, hasAnimation, seriesModel) {\n      if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n        var endLabelModel_1 = seriesModel.getModel('endLabel');\n        var valueAnimation_1 = endLabelModel_1.get('valueAnimation');\n        var data_1 = seriesModel.getData();\n        var labelAnimationRecord_1 = {\n          lastFrameIndex: 0\n        };\n        var during = anyStateShowEndLabel(seriesModel) ? function (percent, clipRect) {\n          lineView._endLabelOnDuring(percent, clipRect, data_1, labelAnimationRecord_1, valueAnimation_1, endLabelModel_1, coordSys);\n        } : null;\n        var isHorizontal = coordSys.getBaseAxis().isHorizontal();\n        var clipPath = createGridClipPath(coordSys, hasAnimation, seriesModel, function () {\n          var endLabel = lineView._endLabel;\n\n          if (endLabel && hasAnimation) {\n            if (labelAnimationRecord_1.originalX != null) {\n              endLabel.attr({\n                x: labelAnimationRecord_1.originalX,\n                y: labelAnimationRecord_1.originalY\n              });\n            }\n          }\n        }, during); // Expand clip shape to avoid clipping when line value exceeds axis\n\n        if (!seriesModel.get('clip', true)) {\n          var rectShape = clipPath.shape;\n          var expandSize = Math.max(rectShape.width, rectShape.height);\n\n          if (isHorizontal) {\n            rectShape.y -= expandSize;\n            rectShape.height += expandSize * 2;\n          } else {\n            rectShape.x -= expandSize;\n            rectShape.width += expandSize * 2;\n          }\n        } // Set to the final frame. To make sure label layout is right.\n\n\n        if (during) {\n          during(1, clipPath);\n        }\n\n        return clipPath;\n      } else {\n        if (\"development\" !== 'production') {\n          if (seriesModel.get(['endLabel', 'show'])) {\n            console.warn('endLabel is not supported for lines in polar systems.');\n          }\n        }\n\n        return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n      }\n    }\n\n    function getEndLabelStateSpecified(endLabelModel, coordSys) {\n      var baseAxis = coordSys.getBaseAxis();\n      var isHorizontal = baseAxis.isHorizontal();\n      var isBaseInversed = baseAxis.inverse;\n      var align = isHorizontal ? isBaseInversed ? 'right' : 'left' : 'center';\n      var verticalAlign = isHorizontal ? 'middle' : isBaseInversed ? 'top' : 'bottom';\n      return {\n        normal: {\n          align: endLabelModel.get('align') || align,\n          verticalAlign: endLabelModel.get('verticalAlign') || verticalAlign\n        }\n      };\n    }\n\n    var LineView =\n    /** @class */\n    function (_super) {\n      __extends(LineView, _super);\n\n      function LineView() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      LineView.prototype.init = function () {\n        var lineGroup = new Group();\n        var symbolDraw = new SymbolDraw();\n        this.group.add(symbolDraw.group);\n        this._symbolDraw = symbolDraw;\n        this._lineGroup = lineGroup;\n      };\n\n      LineView.prototype.render = function (seriesModel, ecModel, api) {\n        var _this = this;\n\n        var coordSys = seriesModel.coordinateSystem;\n        var group = this.group;\n        var data = seriesModel.getData();\n        var lineStyleModel = seriesModel.getModel('lineStyle');\n        var areaStyleModel = seriesModel.getModel('areaStyle');\n        var points = data.getLayout('points') || [];\n        var isCoordSysPolar = coordSys.type === 'polar';\n        var prevCoordSys = this._coordSys;\n        var symbolDraw = this._symbolDraw;\n        var polyline = this._polyline;\n        var polygon = this._polygon;\n        var lineGroup = this._lineGroup;\n        var hasAnimation = seriesModel.get('animation');\n        var isAreaChart = !areaStyleModel.isEmpty();\n        var valueOrigin = areaStyleModel.get('origin');\n        var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin);\n        var stackedOnPoints = isAreaChart && getStackedOnPoints(coordSys, data, dataCoordInfo);\n        var showSymbol = seriesModel.get('showSymbol');\n        var connectNulls = seriesModel.get('connectNulls');\n        var isIgnoreFunc = showSymbol && !isCoordSysPolar && getIsIgnoreFunc(seriesModel, data, coordSys); // Remove temporary symbols\n\n        var oldData = this._data;\n        oldData && oldData.eachItemGraphicEl(function (el, idx) {\n          if (el.__temp) {\n            group.remove(el);\n            oldData.setItemGraphicEl(idx, null);\n          }\n        }); // Remove previous created symbols if showSymbol changed to false\n\n        if (!showSymbol) {\n          symbolDraw.remove();\n        }\n\n        group.add(lineGroup); // FIXME step not support polar\n\n        var step = !isCoordSysPolar ? seriesModel.get('step') : false;\n        var clipShapeForSymbol;\n\n        if (coordSys && coordSys.getArea && seriesModel.get('clip', true)) {\n          clipShapeForSymbol = coordSys.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n          // See #7913 and `test/dataZoom-clip.html`.\n\n          if (clipShapeForSymbol.width != null) {\n            clipShapeForSymbol.x -= 0.1;\n            clipShapeForSymbol.y -= 0.1;\n            clipShapeForSymbol.width += 0.2;\n            clipShapeForSymbol.height += 0.2;\n          } else if (clipShapeForSymbol.r0) {\n            clipShapeForSymbol.r0 -= 0.5;\n            clipShapeForSymbol.r += 0.5;\n          }\n        }\n\n        this._clipShapeForSymbol = clipShapeForSymbol;\n        var visualColor = getVisualGradient(data, coordSys, api) || data.getVisual('style')[data.getVisual('drawType')]; // Initialization animation or coordinate system changed\n\n        if (!(polyline && prevCoordSys.type === coordSys.type && step === this._step)) {\n          showSymbol && symbolDraw.updateData(data, {\n            isIgnore: isIgnoreFunc,\n            clipShape: clipShapeForSymbol,\n            disableAnimation: true,\n            getSymbolPoint: function (idx) {\n              return [points[idx * 2], points[idx * 2 + 1]];\n            }\n          });\n          hasAnimation && this._initSymbolLabelAnimation(data, coordSys, clipShapeForSymbol);\n\n          if (step) {\n            // TODO If stacked series is not step\n            points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n            if (stackedOnPoints) {\n              stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n            }\n          }\n\n          polyline = this._newPolyline(points);\n\n          if (isAreaChart) {\n            polygon = this._newPolygon(points, stackedOnPoints);\n          } // If areaStyle is removed\n          else if (polygon) {\n              lineGroup.remove(polygon);\n              polygon = this._polygon = null;\n            } // NOTE: Must update _endLabel before setClipPath.\n\n\n          if (!isCoordSysPolar) {\n            this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n          }\n\n          lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n        } else {\n          if (isAreaChart && !polygon) {\n            // If areaStyle is added\n            polygon = this._newPolygon(points, stackedOnPoints);\n          } else if (polygon && !isAreaChart) {\n            // If areaStyle is removed\n            lineGroup.remove(polygon);\n            polygon = this._polygon = null;\n          } // NOTE: Must update _endLabel before setClipPath.\n\n\n          if (!isCoordSysPolar) {\n            this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n          } // Update clipPath\n\n\n          var oldClipPath = lineGroup.getClipPath();\n\n          if (oldClipPath) {\n            var newClipPath = createLineClipPath(this, coordSys, false, seriesModel);\n            initProps(oldClipPath, {\n              shape: newClipPath.shape\n            }, seriesModel);\n          } else {\n            lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n          } // Always update, or it is wrong in the case turning on legend\n          // because points are not changed.\n\n\n          showSymbol && symbolDraw.updateData(data, {\n            isIgnore: isIgnoreFunc,\n            clipShape: clipShapeForSymbol,\n            disableAnimation: true,\n            getSymbolPoint: function (idx) {\n              return [points[idx * 2], points[idx * 2 + 1]];\n            }\n          }); // In the case data zoom triggered refreshing frequently\n          // Data may not change if line has a category axis. So it should animate nothing.\n\n          if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) || !isPointsSame(this._points, points)) {\n            if (hasAnimation) {\n              this._doUpdateAnimation(data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls);\n            } else {\n              // Not do it in update with animation\n              if (step) {\n                // TODO If stacked series is not step\n                points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n                if (stackedOnPoints) {\n                  stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n                }\n              }\n\n              polyline.setShape({\n                points: points\n              });\n              polygon && polygon.setShape({\n                points: points,\n                stackedOnPoints: stackedOnPoints\n              });\n            }\n          }\n        }\n\n        var emphasisModel = seriesModel.getModel('emphasis');\n        var focus = emphasisModel.get('focus');\n        var blurScope = emphasisModel.get('blurScope');\n        var emphasisDisabled = emphasisModel.get('disabled');\n        polyline.useStyle(defaults( // Use color in lineStyle first\n        lineStyleModel.getLineStyle(), {\n          fill: 'none',\n          stroke: visualColor,\n          lineJoin: 'bevel'\n        }));\n        setStatesStylesFromModel(polyline, seriesModel, 'lineStyle');\n\n        if (polyline.style.lineWidth > 0 && seriesModel.get(['emphasis', 'lineStyle', 'width']) === 'bolder') {\n          var emphasisLineStyle = polyline.getState('emphasis').style;\n          emphasisLineStyle.lineWidth = +polyline.style.lineWidth + 1;\n        } // Needs seriesIndex for focus\n\n\n        getECData(polyline).seriesIndex = seriesModel.seriesIndex;\n        toggleHoverEmphasis(polyline, focus, blurScope, emphasisDisabled);\n        var smooth = getSmooth(seriesModel.get('smooth'));\n        var smoothMonotone = seriesModel.get('smoothMonotone');\n        polyline.setShape({\n          smooth: smooth,\n          smoothMonotone: smoothMonotone,\n          connectNulls: connectNulls\n        });\n\n        if (polygon) {\n          var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');\n          var stackedOnSmooth = 0;\n          polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), {\n            fill: visualColor,\n            opacity: 0.7,\n            lineJoin: 'bevel',\n            decal: data.getVisual('style').decal\n          }));\n\n          if (stackedOnSeries) {\n            stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));\n          }\n\n          polygon.setShape({\n            smooth: smooth,\n            stackedOnSmooth: stackedOnSmooth,\n            smoothMonotone: smoothMonotone,\n            connectNulls: connectNulls\n          });\n          setStatesStylesFromModel(polygon, seriesModel, 'areaStyle'); // Needs seriesIndex for focus\n\n          getECData(polygon).seriesIndex = seriesModel.seriesIndex;\n          toggleHoverEmphasis(polygon, focus, blurScope, emphasisDisabled);\n        }\n\n        var changePolyState = function (toState) {\n          _this._changePolyState(toState);\n        };\n\n        data.eachItemGraphicEl(function (el) {\n          // Switch polyline / polygon state if element changed its state.\n          el && (el.onHoverStateChange = changePolyState);\n        });\n        this._polyline.onHoverStateChange = changePolyState;\n        this._data = data; // Save the coordinate system for transition animation when data changed\n\n        this._coordSys = coordSys;\n        this._stackedOnPoints = stackedOnPoints;\n        this._points = points;\n        this._step = step;\n        this._valueOrigin = valueOrigin;\n\n        if (seriesModel.get('triggerLineEvent')) {\n          this.packEventData(seriesModel, polyline);\n          polygon && this.packEventData(seriesModel, polygon);\n        }\n      };\n\n      LineView.prototype.packEventData = function (seriesModel, el) {\n        getECData(el).eventData = {\n          componentType: 'series',\n          componentSubType: 'line',\n          componentIndex: seriesModel.componentIndex,\n          seriesIndex: seriesModel.seriesIndex,\n          seriesName: seriesModel.name,\n          seriesType: 'line'\n        };\n      };\n\n      LineView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var dataIndex = queryDataIndex(data, payload);\n\n        this._changePolyState('emphasis');\n\n        if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {\n          var points = data.getLayout('points');\n          var symbol = data.getItemGraphicEl(dataIndex);\n\n          if (!symbol) {\n            // Create a temporary symbol if it is not exists\n            var x = points[dataIndex * 2];\n            var y = points[dataIndex * 2 + 1];\n\n            if (isNaN(x) || isNaN(y)) {\n              // Null data\n              return;\n            } // fix #11360: shouldn't draw symbol outside clipShapeForSymbol\n\n\n            if (this._clipShapeForSymbol && !this._clipShapeForSymbol.contain(x, y)) {\n              return;\n            }\n\n            var zlevel = seriesModel.get('zlevel') || 0;\n            var z = seriesModel.get('z') || 0;\n            symbol = new Symbol(data, dataIndex);\n            symbol.x = x;\n            symbol.y = y;\n            symbol.setZ(zlevel, z); // ensure label text of the temporary symbol is in front of line and area polygon\n\n            var symbolLabel = symbol.getSymbolPath().getTextContent();\n\n            if (symbolLabel) {\n              symbolLabel.zlevel = zlevel;\n              symbolLabel.z = z;\n              symbolLabel.z2 = this._polyline.z2 + 1;\n            }\n\n            symbol.__temp = true;\n            data.setItemGraphicEl(dataIndex, symbol); // Stop scale animation\n\n            symbol.stopSymbolAnimation(true);\n            this.group.add(symbol);\n          }\n\n          symbol.highlight();\n        } else {\n          // Highlight whole series\n          ChartView.prototype.highlight.call(this, seriesModel, ecModel, api, payload);\n        }\n      };\n\n      LineView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var dataIndex = queryDataIndex(data, payload);\n\n        this._changePolyState('normal');\n\n        if (dataIndex != null && dataIndex >= 0) {\n          var symbol = data.getItemGraphicEl(dataIndex);\n\n          if (symbol) {\n            if (symbol.__temp) {\n              data.setItemGraphicEl(dataIndex, null);\n              this.group.remove(symbol);\n            } else {\n              symbol.downplay();\n            }\n          }\n        } else {\n          // FIXME\n          // can not downplay completely.\n          // Downplay whole series\n          ChartView.prototype.downplay.call(this, seriesModel, ecModel, api, payload);\n        }\n      };\n\n      LineView.prototype._changePolyState = function (toState) {\n        var polygon = this._polygon;\n        setStatesFlag(this._polyline, toState);\n        polygon && setStatesFlag(polygon, toState);\n      };\n\n      LineView.prototype._newPolyline = function (points) {\n        var polyline = this._polyline; // Remove previous created polyline\n\n        if (polyline) {\n          this._lineGroup.remove(polyline);\n        }\n\n        polyline = new ECPolyline({\n          shape: {\n            points: points\n          },\n          segmentIgnoreThreshold: 2,\n          z2: 10\n        });\n\n        this._lineGroup.add(polyline);\n\n        this._polyline = polyline;\n        return polyline;\n      };\n\n      LineView.prototype._newPolygon = function (points, stackedOnPoints) {\n        var polygon = this._polygon; // Remove previous created polygon\n\n        if (polygon) {\n          this._lineGroup.remove(polygon);\n        }\n\n        polygon = new ECPolygon({\n          shape: {\n            points: points,\n            stackedOnPoints: stackedOnPoints\n          },\n          segmentIgnoreThreshold: 2\n        });\n\n        this._lineGroup.add(polygon);\n\n        this._polygon = polygon;\n        return polygon;\n      };\n\n      LineView.prototype._initSymbolLabelAnimation = function (data, coordSys, clipShape) {\n        var isHorizontalOrRadial;\n        var isCoordSysPolar;\n        var baseAxis = coordSys.getBaseAxis();\n        var isAxisInverse = baseAxis.inverse;\n\n        if (coordSys.type === 'cartesian2d') {\n          isHorizontalOrRadial = baseAxis.isHorizontal();\n          isCoordSysPolar = false;\n        } else if (coordSys.type === 'polar') {\n          isHorizontalOrRadial = baseAxis.dim === 'angle';\n          isCoordSysPolar = true;\n        }\n\n        var seriesModel = data.hostModel;\n        var seriesDuration = seriesModel.get('animationDuration');\n\n        if (isFunction(seriesDuration)) {\n          seriesDuration = seriesDuration(null);\n        }\n\n        var seriesDalay = seriesModel.get('animationDelay') || 0;\n        var seriesDalayValue = isFunction(seriesDalay) ? seriesDalay(null) : seriesDalay;\n        data.eachItemGraphicEl(function (symbol, idx) {\n          var el = symbol;\n\n          if (el) {\n            var point = [symbol.x, symbol.y];\n            var start = void 0;\n            var end = void 0;\n            var current = void 0;\n\n            if (clipShape) {\n              if (isCoordSysPolar) {\n                var polarClip = clipShape;\n                var coord = coordSys.pointToCoord(point);\n\n                if (isHorizontalOrRadial) {\n                  start = polarClip.startAngle;\n                  end = polarClip.endAngle;\n                  current = -coord[1] / 180 * Math.PI;\n                } else {\n                  start = polarClip.r0;\n                  end = polarClip.r;\n                  current = coord[0];\n                }\n              } else {\n                var gridClip = clipShape;\n\n                if (isHorizontalOrRadial) {\n                  start = gridClip.x;\n                  end = gridClip.x + gridClip.width;\n                  current = symbol.x;\n                } else {\n                  start = gridClip.y + gridClip.height;\n                  end = gridClip.y;\n                  current = symbol.y;\n                }\n              }\n            }\n\n            var ratio = end === start ? 0 : (current - start) / (end - start);\n\n            if (isAxisInverse) {\n              ratio = 1 - ratio;\n            }\n\n            var delay = isFunction(seriesDalay) ? seriesDalay(idx) : seriesDuration * ratio + seriesDalayValue;\n            var symbolPath = el.getSymbolPath();\n            var text = symbolPath.getTextContent();\n            el.attr({\n              scaleX: 0,\n              scaleY: 0\n            });\n            el.animateTo({\n              scaleX: 1,\n              scaleY: 1\n            }, {\n              duration: 200,\n              setToFinal: true,\n              delay: delay\n            });\n\n            if (text) {\n              text.animateFrom({\n                style: {\n                  opacity: 0\n                }\n              }, {\n                duration: 300,\n                delay: delay\n              });\n            }\n\n            symbolPath.disableLabelAnimation = true;\n          }\n        });\n      };\n\n      LineView.prototype._initOrUpdateEndLabel = function (seriesModel, coordSys, inheritColor) {\n        var endLabelModel = seriesModel.getModel('endLabel');\n\n        if (anyStateShowEndLabel(seriesModel)) {\n          var data_2 = seriesModel.getData();\n          var polyline = this._polyline; // series may be filtered.\n\n          var points = data_2.getLayout('points');\n\n          if (!points) {\n            polyline.removeTextContent();\n            this._endLabel = null;\n            return;\n          }\n\n          var endLabel = this._endLabel;\n\n          if (!endLabel) {\n            endLabel = this._endLabel = new ZRText({\n              z2: 200 // should be higher than item symbol\n\n            });\n            endLabel.ignoreClip = true;\n            polyline.setTextContent(this._endLabel);\n            polyline.disableLabelAnimation = true;\n          } // Find last non-NaN data to display data\n\n\n          var dataIndex = getLastIndexNotNull(points);\n\n          if (dataIndex >= 0) {\n            setLabelStyle(polyline, getLabelStatesModels(seriesModel, 'endLabel'), {\n              inheritColor: inheritColor,\n              labelFetcher: seriesModel,\n              labelDataIndex: dataIndex,\n              defaultText: function (dataIndex, opt, interpolatedValue) {\n                return interpolatedValue != null ? getDefaultInterpolatedLabel(data_2, interpolatedValue) : getDefaultLabel(data_2, dataIndex);\n              },\n              enableTextSetter: true\n            }, getEndLabelStateSpecified(endLabelModel, coordSys));\n            polyline.textConfig.position = null;\n          }\n        } else if (this._endLabel) {\n          this._polyline.removeTextContent();\n\n          this._endLabel = null;\n        }\n      };\n\n      LineView.prototype._endLabelOnDuring = function (percent, clipRect, data, animationRecord, valueAnimation, endLabelModel, coordSys) {\n        var endLabel = this._endLabel;\n        var polyline = this._polyline;\n\n        if (endLabel) {\n          // NOTE: Don't remove percent < 1. percent === 1 means the first frame during render.\n          // The label is not prepared at this time.\n          if (percent < 1 && animationRecord.originalX == null) {\n            animationRecord.originalX = endLabel.x;\n            animationRecord.originalY = endLabel.y;\n          }\n\n          var points = data.getLayout('points');\n          var seriesModel = data.hostModel;\n          var connectNulls = seriesModel.get('connectNulls');\n          var precision = endLabelModel.get('precision');\n          var distance = endLabelModel.get('distance') || 0;\n          var baseAxis = coordSys.getBaseAxis();\n          var isHorizontal = baseAxis.isHorizontal();\n          var isBaseInversed = baseAxis.inverse;\n          var clipShape = clipRect.shape;\n          var xOrY = isBaseInversed ? isHorizontal ? clipShape.x : clipShape.y + clipShape.height : isHorizontal ? clipShape.x + clipShape.width : clipShape.y;\n          var distanceX = (isHorizontal ? distance : 0) * (isBaseInversed ? -1 : 1);\n          var distanceY = (isHorizontal ? 0 : -distance) * (isBaseInversed ? -1 : 1);\n          var dim = isHorizontal ? 'x' : 'y';\n          var dataIndexRange = getIndexRange(points, xOrY, dim);\n          var indices = dataIndexRange.range;\n          var diff = indices[1] - indices[0];\n          var value = void 0;\n\n          if (diff >= 1) {\n            // diff > 1 && connectNulls, which is on the null data.\n            if (diff > 1 && !connectNulls) {\n              var pt = getPointAtIndex(points, indices[0]);\n              endLabel.attr({\n                x: pt[0] + distanceX,\n                y: pt[1] + distanceY\n              });\n              valueAnimation && (value = seriesModel.getRawValue(indices[0]));\n            } else {\n              var pt = polyline.getPointOn(xOrY, dim);\n              pt && endLabel.attr({\n                x: pt[0] + distanceX,\n                y: pt[1] + distanceY\n              });\n              var startValue = seriesModel.getRawValue(indices[0]);\n              var endValue = seriesModel.getRawValue(indices[1]);\n              valueAnimation && (value = interpolateRawValues(data, precision, startValue, endValue, dataIndexRange.t));\n            }\n\n            animationRecord.lastFrameIndex = indices[0];\n          } else {\n            // If diff <= 0, which is the range is not found(Include NaN)\n            // Choose the first point or last point.\n            var idx = percent === 1 || animationRecord.lastFrameIndex > 0 ? indices[0] : 0;\n            var pt = getPointAtIndex(points, idx);\n            valueAnimation && (value = seriesModel.getRawValue(idx));\n            endLabel.attr({\n              x: pt[0] + distanceX,\n              y: pt[1] + distanceY\n            });\n          }\n\n          if (valueAnimation) {\n            labelInner(endLabel).setLabelText(value);\n          }\n        }\n      };\n      /**\n       * @private\n       */\n      // FIXME Two value axis\n\n\n      LineView.prototype._doUpdateAnimation = function (data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls) {\n        var polyline = this._polyline;\n        var polygon = this._polygon;\n        var seriesModel = data.hostModel;\n        var diff = lineAnimationDiff(this._data, data, this._stackedOnPoints, stackedOnPoints, this._coordSys, coordSys, this._valueOrigin);\n        var current = diff.current;\n        var stackedOnCurrent = diff.stackedOnCurrent;\n        var next = diff.next;\n        var stackedOnNext = diff.stackedOnNext;\n\n        if (step) {\n          // TODO If stacked series is not step\n          current = turnPointsIntoStep(diff.current, coordSys, step, connectNulls);\n          stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step, connectNulls);\n          next = turnPointsIntoStep(diff.next, coordSys, step, connectNulls);\n          stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step, connectNulls);\n        } // Don't apply animation if diff is large.\n        // For better result and avoid memory explosion problems like\n        // https://github.com/apache/incubator-echarts/issues/12229\n\n\n        if (getBoundingDiff(current, next) > 3000 || polygon && getBoundingDiff(stackedOnCurrent, stackedOnNext) > 3000) {\n          polyline.stopAnimation();\n          polyline.setShape({\n            points: next\n          });\n\n          if (polygon) {\n            polygon.stopAnimation();\n            polygon.setShape({\n              points: next,\n              stackedOnPoints: stackedOnNext\n            });\n          }\n\n          return;\n        }\n\n        polyline.shape.__points = diff.current;\n        polyline.shape.points = current;\n        var target = {\n          shape: {\n            points: next\n          }\n        }; // Also animate the original points.\n        // If points reference is changed when turning into step line.\n\n        if (diff.current !== current) {\n          target.shape.__points = diff.next;\n        } // Stop previous animation.\n\n\n        polyline.stopAnimation();\n        updateProps(polyline, target, seriesModel);\n\n        if (polygon) {\n          polygon.setShape({\n            // Reuse the points with polyline.\n            points: current,\n            stackedOnPoints: stackedOnCurrent\n          });\n          polygon.stopAnimation();\n          updateProps(polygon, {\n            shape: {\n              stackedOnPoints: stackedOnNext\n            }\n          }, seriesModel); // If use attr directly in updateProps.\n\n          if (polyline.shape.points !== polygon.shape.points) {\n            polygon.shape.points = polyline.shape.points;\n          }\n        }\n\n        var updatedDataInfo = [];\n        var diffStatus = diff.status;\n\n        for (var i = 0; i < diffStatus.length; i++) {\n          var cmd = diffStatus[i].cmd;\n\n          if (cmd === '=') {\n            var el = data.getItemGraphicEl(diffStatus[i].idx1);\n\n            if (el) {\n              updatedDataInfo.push({\n                el: el,\n                ptIdx: i // Index of points\n\n              });\n            }\n          }\n        }\n\n        if (polyline.animators && polyline.animators.length) {\n          polyline.animators[0].during(function () {\n            polygon && polygon.dirtyShape();\n            var points = polyline.shape.__points;\n\n            for (var i = 0; i < updatedDataInfo.length; i++) {\n              var el = updatedDataInfo[i].el;\n              var offset = updatedDataInfo[i].ptIdx * 2;\n              el.x = points[offset];\n              el.y = points[offset + 1];\n              el.markRedraw();\n            }\n          });\n        }\n      };\n\n      LineView.prototype.remove = function (ecModel) {\n        var group = this.group;\n        var oldData = this._data;\n\n        this._lineGroup.removeAll();\n\n        this._symbolDraw.remove(true); // Remove temporary created elements when highlighting\n\n\n        oldData && oldData.eachItemGraphicEl(function (el, idx) {\n          if (el.__temp) {\n            group.remove(el);\n            oldData.setItemGraphicEl(idx, null);\n          }\n        });\n        this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._endLabel = this._data = null;\n      };\n\n      LineView.type = 'line';\n      return LineView;\n    }(ChartView);\n\n    function pointsLayout(seriesType, forceStoreInTypedArray) {\n      return {\n        seriesType: seriesType,\n        plan: createRenderPlanner(),\n        reset: function (seriesModel) {\n          var data = seriesModel.getData();\n          var coordSys = seriesModel.coordinateSystem;\n          var pipelineContext = seriesModel.pipelineContext;\n          var useTypedArray = forceStoreInTypedArray || pipelineContext.large;\n\n          if (!coordSys) {\n            return;\n          }\n\n          var dims = map(coordSys.dimensions, function (dim) {\n            return data.mapDimension(dim);\n          }).slice(0, 2);\n          var dimLen = dims.length;\n          var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n          if (isDimensionStacked(data, dims[0])) {\n            dims[0] = stackResultDim;\n          }\n\n          if (isDimensionStacked(data, dims[1])) {\n            dims[1] = stackResultDim;\n          }\n\n          var store = data.getStore();\n          var dimIdx0 = data.getDimensionIndex(dims[0]);\n          var dimIdx1 = data.getDimensionIndex(dims[1]);\n          return dimLen && {\n            progress: function (params, data) {\n              var segCount = params.end - params.start;\n              var points = useTypedArray && createFloat32Array(segCount * dimLen);\n              var tmpIn = [];\n              var tmpOut = [];\n\n              for (var i = params.start, offset = 0; i < params.end; i++) {\n                var point = void 0;\n\n                if (dimLen === 1) {\n                  var x = store.get(dimIdx0, i); // NOTE: Make sure the second parameter is null to use default strategy.\n\n                  point = coordSys.dataToPoint(x, null, tmpOut);\n                } else {\n                  tmpIn[0] = store.get(dimIdx0, i);\n                  tmpIn[1] = store.get(dimIdx1, i); // Let coordinate system to handle the NaN data.\n\n                  point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n                }\n\n                if (useTypedArray) {\n                  points[offset++] = point[0];\n                  points[offset++] = point[1];\n                } else {\n                  data.setItemLayout(i, point.slice());\n                }\n              }\n\n              useTypedArray && data.setLayout('points', points);\n            }\n          };\n        }\n      };\n    }\n\n    var samplers = {\n      average: function (frame) {\n        var sum = 0;\n        var count = 0;\n\n        for (var i = 0; i < frame.length; i++) {\n          if (!isNaN(frame[i])) {\n            sum += frame[i];\n            count++;\n          }\n        } // Return NaN if count is 0\n\n\n        return count === 0 ? NaN : sum / count;\n      },\n      sum: function (frame) {\n        var sum = 0;\n\n        for (var i = 0; i < frame.length; i++) {\n          // Ignore NaN\n          sum += frame[i] || 0;\n        }\n\n        return sum;\n      },\n      max: function (frame) {\n        var max = -Infinity;\n\n        for (var i = 0; i < frame.length; i++) {\n          frame[i] > max && (max = frame[i]);\n        } // NaN will cause illegal axis extent.\n\n\n        return isFinite(max) ? max : NaN;\n      },\n      min: function (frame) {\n        var min = Infinity;\n\n        for (var i = 0; i < frame.length; i++) {\n          frame[i] < min && (min = frame[i]);\n        } // NaN will cause illegal axis extent.\n\n\n        return isFinite(min) ? min : NaN;\n      },\n      // TODO\n      // Median\n      nearest: function (frame) {\n        return frame[0];\n      }\n    };\n\n    var indexSampler = function (frame) {\n      return Math.round(frame.length / 2);\n    };\n\n    function dataSample(seriesType) {\n      return {\n        seriesType: seriesType,\n        // FIXME:TS never used, so comment it\n        // modifyOutputEnd: true,\n        reset: function (seriesModel, ecModel, api) {\n          var data = seriesModel.getData();\n          var sampling = seriesModel.get('sampling');\n          var coordSys = seriesModel.coordinateSystem;\n          var count = data.count(); // Only cartesian2d support down sampling. Disable it when there is few data.\n\n          if (count > 10 && coordSys.type === 'cartesian2d' && sampling) {\n            var baseAxis = coordSys.getBaseAxis();\n            var valueAxis = coordSys.getOtherAxis(baseAxis);\n            var extent = baseAxis.getExtent();\n            var dpr = api.getDevicePixelRatio(); // Coordinste system has been resized\n\n            var size = Math.abs(extent[1] - extent[0]) * (dpr || 1);\n            var rate = Math.round(count / size);\n\n            if (isFinite(rate) && rate > 1) {\n              if (sampling === 'lttb') {\n                seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate));\n              }\n\n              var sampler = void 0;\n\n              if (isString(sampling)) {\n                sampler = samplers[sampling];\n              } else if (isFunction(sampling)) {\n                sampler = sampling;\n              }\n\n              if (sampler) {\n                // Only support sample the first dim mapped from value axis.\n                seriesModel.setData(data.downSample(data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler));\n              }\n            }\n          }\n        }\n      };\n    }\n\n    function install$2(registers) {\n      registers.registerChartView(LineView);\n      registers.registerSeriesModel(LineSeriesModel);\n      registers.registerLayout(pointsLayout('line', true));\n      registers.registerVisual({\n        seriesType: 'line',\n        reset: function (seriesModel) {\n          var data = seriesModel.getData(); // Visual coding for legend\n\n          var lineStyle = seriesModel.getModel('lineStyle').getLineStyle();\n\n          if (lineStyle && !lineStyle.stroke) {\n            // Fill in visual should be palette color if\n            // has color callback\n            lineStyle.stroke = data.getVisual('style').fill;\n          }\n\n          data.setVisual('legendLineStyle', lineStyle);\n        }\n      }); // Down sample after filter\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('line'));\n    }\n\n    var BaseBarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(BaseBarSeriesModel, _super);\n\n      function BaseBarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BaseBarSeriesModel.type;\n        return _this;\n      }\n\n      BaseBarSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      BaseBarSeriesModel.prototype.getMarkerPosition = function (value, dims, startingAtTick) {\n        var coordSys = this.coordinateSystem;\n\n        if (coordSys && coordSys.clampData) {\n          // PENDING if clamp ?\n          var pt_1 = coordSys.dataToPoint(coordSys.clampData(value));\n\n          if (startingAtTick) {\n            each(coordSys.getAxes(), function (axis, idx) {\n              // If axis type is category, use tick coords instead\n              if (axis.type === 'category') {\n                var tickCoords = axis.getTicksCoords();\n                var tickIdx = coordSys.clampData(value)[idx]; // The index of rightmost tick of markArea is 1 larger than x1/y1 index\n\n                if (dims && (dims[idx] === 'x1' || dims[idx] === 'y1')) {\n                  tickIdx += 1;\n                }\n\n                tickIdx > tickCoords.length - 1 && (tickIdx = tickCoords.length - 1);\n                tickIdx < 0 && (tickIdx = 0);\n                tickCoords[tickIdx] && (pt_1[idx] = axis.toGlobalCoord(tickCoords[tickIdx].coord));\n              }\n            });\n          } else {\n            var data = this.getData();\n            var offset = data.getLayout('offset');\n            var size = data.getLayout('size');\n            var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;\n            pt_1[offsetIndex] += offset + size / 2;\n          }\n\n          return pt_1;\n        }\n\n        return [NaN, NaN];\n      };\n\n      BaseBarSeriesModel.type = 'series.__base_bar__';\n      BaseBarSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        // stack: null\n        // Cartesian coordinate system\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        barMinHeight: 0,\n        barMinAngle: 0,\n        // cursor: null,\n        large: false,\n        largeThreshold: 400,\n        progressive: 3e3,\n        progressiveChunkMode: 'mod'\n      };\n      return BaseBarSeriesModel;\n    }(SeriesModel);\n\n    SeriesModel.registerClass(BaseBarSeriesModel);\n\n    var BarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(BarSeriesModel, _super);\n\n      function BarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BarSeriesModel.type;\n        return _this;\n      }\n\n      BarSeriesModel.prototype.getInitialData = function () {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true,\n          createInvertedIndices: !!this.get('realtimeSort', true) || null\n        });\n      };\n      /**\n       * @override\n       */\n\n\n      BarSeriesModel.prototype.getProgressive = function () {\n        // Do not support progressive in normal mode.\n        return this.get('large') ? this.get('progressive') : false;\n      };\n      /**\n       * @override\n       */\n\n\n      BarSeriesModel.prototype.getProgressiveThreshold = function () {\n        // Do not support progressive in normal mode.\n        var progressiveThreshold = this.get('progressiveThreshold');\n        var largeThreshold = this.get('largeThreshold');\n\n        if (largeThreshold > progressiveThreshold) {\n          progressiveThreshold = largeThreshold;\n        }\n\n        return progressiveThreshold;\n      };\n\n      BarSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n        return selectors.rect(data.getItemLayout(dataIndex));\n      };\n\n      BarSeriesModel.type = 'series.bar';\n      BarSeriesModel.dependencies = ['grid', 'polar'];\n      BarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, {\n        // If clipped\n        // Only available on cartesian2d\n        clip: true,\n        roundCap: false,\n        showBackground: false,\n        backgroundStyle: {\n          color: 'rgba(180, 180, 180, 0.2)',\n          borderColor: null,\n          borderWidth: 0,\n          borderType: 'solid',\n          borderRadius: 0,\n          shadowBlur: 0,\n          shadowColor: null,\n          shadowOffsetX: 0,\n          shadowOffsetY: 0,\n          opacity: 1\n        },\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        },\n        realtimeSort: false\n      });\n      return BarSeriesModel;\n    }(BaseBarSeriesModel);\n\n    /**\n     * Sausage: similar to sector, but have half circle on both sides\n     */\n\n    var SausageShape =\n    /** @class */\n    function () {\n      function SausageShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.r0 = 0;\n        this.r = 0;\n        this.startAngle = 0;\n        this.endAngle = Math.PI * 2;\n        this.clockwise = true;\n      }\n\n      return SausageShape;\n    }();\n\n    var SausagePath =\n    /** @class */\n    function (_super) {\n      __extends(SausagePath, _super);\n\n      function SausagePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'sausage';\n        return _this;\n      }\n\n      SausagePath.prototype.getDefaultShape = function () {\n        return new SausageShape();\n      };\n\n      SausagePath.prototype.buildPath = function (ctx, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var r0 = Math.max(shape.r0 || 0, 0);\n        var r = Math.max(shape.r, 0);\n        var dr = (r - r0) * 0.5;\n        var rCenter = r0 + dr;\n        var startAngle = shape.startAngle;\n        var endAngle = shape.endAngle;\n        var clockwise = shape.clockwise;\n        var PI2 = Math.PI * 2;\n        var lessThanCircle = clockwise ? endAngle - startAngle < PI2 : startAngle - endAngle < PI2;\n\n        if (!lessThanCircle) {\n          // Normalize angles\n          startAngle = endAngle - (clockwise ? PI2 : -PI2);\n        }\n\n        var unitStartX = Math.cos(startAngle);\n        var unitStartY = Math.sin(startAngle);\n        var unitEndX = Math.cos(endAngle);\n        var unitEndY = Math.sin(endAngle);\n\n        if (lessThanCircle) {\n          ctx.moveTo(unitStartX * r0 + cx, unitStartY * r0 + cy);\n          ctx.arc(unitStartX * rCenter + cx, unitStartY * rCenter + cy, dr, -Math.PI + startAngle, startAngle, !clockwise);\n        } else {\n          ctx.moveTo(unitStartX * r + cx, unitStartY * r + cy);\n        }\n\n        ctx.arc(cx, cy, r, startAngle, endAngle, !clockwise);\n        ctx.arc(unitEndX * rCenter + cx, unitEndY * rCenter + cy, dr, endAngle - Math.PI * 2, endAngle - Math.PI, !clockwise);\n\n        if (r0 !== 0) {\n          ctx.arc(cx, cy, r0, endAngle, startAngle, clockwise);\n        } // ctx.closePath();\n\n      };\n\n      return SausagePath;\n    }(Path);\n\n    function createSectorCalculateTextPosition(positionMapping, opts) {\n      opts = opts || {};\n      var isRoundCap = opts.isRoundCap;\n      return function (out, opts, boundingRect) {\n        var textPosition = opts.position;\n\n        if (!textPosition || textPosition instanceof Array) {\n          return calculateTextPosition(out, opts, boundingRect);\n        }\n\n        var mappedSectorPosition = positionMapping(textPosition);\n        var distance = opts.distance != null ? opts.distance : 5;\n        var sector = this.shape;\n        var cx = sector.cx;\n        var cy = sector.cy;\n        var r = sector.r;\n        var r0 = sector.r0;\n        var middleR = (r + r0) / 2;\n        var startAngle = sector.startAngle;\n        var endAngle = sector.endAngle;\n        var middleAngle = (startAngle + endAngle) / 2;\n        var extraDist = isRoundCap ? Math.abs(r - r0) / 2 : 0;\n        var mathCos = Math.cos;\n        var mathSin = Math.sin; // base position: top-left\n\n        var x = cx + r * mathCos(startAngle);\n        var y = cy + r * mathSin(startAngle);\n        var textAlign = 'left';\n        var textVerticalAlign = 'top';\n\n        switch (mappedSectorPosition) {\n          case 'startArc':\n            x = cx + (r0 - distance) * mathCos(middleAngle);\n            y = cy + (r0 - distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'top';\n            break;\n\n          case 'insideStartArc':\n            x = cx + (r0 + distance) * mathCos(middleAngle);\n            y = cy + (r0 + distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'bottom';\n            break;\n\n          case 'startAngle':\n            x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, distance + extraDist, false);\n            y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, distance + extraDist, false);\n            textAlign = 'right';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'insideStartAngle':\n            x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, -distance + extraDist, false);\n            y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, -distance + extraDist, false);\n            textAlign = 'left';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'middle':\n            x = cx + middleR * mathCos(middleAngle);\n            y = cy + middleR * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'endArc':\n            x = cx + (r + distance) * mathCos(middleAngle);\n            y = cy + (r + distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'bottom';\n            break;\n\n          case 'insideEndArc':\n            x = cx + (r - distance) * mathCos(middleAngle);\n            y = cy + (r - distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'top';\n            break;\n\n          case 'endAngle':\n            x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, distance + extraDist, true);\n            y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, distance + extraDist, true);\n            textAlign = 'left';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'insideEndAngle':\n            x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, -distance + extraDist, true);\n            y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, -distance + extraDist, true);\n            textAlign = 'right';\n            textVerticalAlign = 'middle';\n            break;\n\n          default:\n            return calculateTextPosition(out, opts, boundingRect);\n        }\n\n        out = out || {};\n        out.x = x;\n        out.y = y;\n        out.align = textAlign;\n        out.verticalAlign = textVerticalAlign;\n        return out;\n      };\n    }\n    function setSectorTextRotation(sector, textPosition, positionMapping, rotateType) {\n      if (isNumber(rotateType)) {\n        // user-set rotation\n        sector.setTextConfig({\n          rotation: rotateType\n        });\n        return;\n      } else if (isArray(textPosition)) {\n        // user-set position, use 0 as auto rotation\n        sector.setTextConfig({\n          rotation: 0\n        });\n        return;\n      }\n\n      var shape = sector.shape;\n      var startAngle = shape.clockwise ? shape.startAngle : shape.endAngle;\n      var endAngle = shape.clockwise ? shape.endAngle : shape.startAngle;\n      var middleAngle = (startAngle + endAngle) / 2;\n      var anchorAngle;\n      var mappedSectorPosition = positionMapping(textPosition);\n\n      switch (mappedSectorPosition) {\n        case 'startArc':\n        case 'insideStartArc':\n        case 'middle':\n        case 'insideEndArc':\n        case 'endArc':\n          anchorAngle = middleAngle;\n          break;\n\n        case 'startAngle':\n        case 'insideStartAngle':\n          anchorAngle = startAngle;\n          break;\n\n        case 'endAngle':\n        case 'insideEndAngle':\n          anchorAngle = endAngle;\n          break;\n\n        default:\n          sector.setTextConfig({\n            rotation: 0\n          });\n          return;\n      }\n\n      var rotate = Math.PI * 1.5 - anchorAngle;\n      /**\n       * TODO: labels with rotate > Math.PI / 2 should be rotate another\n       * half round flipped to increase readability. However, only middle\n       * position supports this for now, because in other positions, the\n       * anchor point is not at the center of the text, so the positions\n       * after rotating is not as expected.\n       */\n\n      if (mappedSectorPosition === 'middle' && rotate > Math.PI / 2 && rotate < Math.PI * 1.5) {\n        rotate -= Math.PI;\n      }\n\n      sector.setTextConfig({\n        rotation: rotate\n      });\n    }\n\n    function adjustAngleDistanceX(angle, distance, isEnd) {\n      return distance * Math.sin(angle) * (isEnd ? -1 : 1);\n    }\n\n    function adjustAngleDistanceY(angle, distance, isEnd) {\n      return distance * Math.cos(angle) * (isEnd ? 1 : -1);\n    }\n\n    var mathMax$6 = Math.max;\n    var mathMin$6 = Math.min;\n\n    function getClipArea(coord, data) {\n      var coordSysClipArea = coord.getArea && coord.getArea();\n\n      if (isCoordinateSystemType(coord, 'cartesian2d')) {\n        var baseAxis = coord.getBaseAxis(); // When boundaryGap is false or using time axis. bar may exceed the grid.\n        // We should not clip this part.\n        // See test/bar2.html\n\n        if (baseAxis.type !== 'category' || !baseAxis.onBand) {\n          var expandWidth = data.getLayout('bandWidth');\n\n          if (baseAxis.isHorizontal()) {\n            coordSysClipArea.x -= expandWidth;\n            coordSysClipArea.width += expandWidth * 2;\n          } else {\n            coordSysClipArea.y -= expandWidth;\n            coordSysClipArea.height += expandWidth * 2;\n          }\n        }\n      }\n\n      return coordSysClipArea;\n    }\n\n    var BarView =\n    /** @class */\n    function (_super) {\n      __extends(BarView, _super);\n\n      function BarView() {\n        var _this = _super.call(this) || this;\n\n        _this.type = BarView.type;\n        _this._isFirstFrame = true;\n        return _this;\n      }\n\n      BarView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        this._model = seriesModel;\n\n        this._removeOnRenderedListener(api);\n\n        this._updateDrawMode(seriesModel);\n\n        var coordinateSystemType = seriesModel.get('coordinateSystem');\n\n        if (coordinateSystemType === 'cartesian2d' || coordinateSystemType === 'polar') {\n          // Clear previously rendered progressive elements.\n          this._progressiveEls = null;\n          this._isLargeDraw ? this._renderLarge(seriesModel, ecModel, api) : this._renderNormal(seriesModel, ecModel, api, payload);\n        } else if (\"development\" !== 'production') {\n          warn('Only cartesian2d and polar supported for bar.');\n        }\n      };\n\n      BarView.prototype.incrementalPrepareRender = function (seriesModel) {\n        this._clear();\n\n        this._updateDrawMode(seriesModel); // incremental also need to clip, otherwise might be overlow.\n        // But must not set clip in each frame, otherwise all of the children will be marked redraw.\n\n\n        this._updateLargeClip(seriesModel);\n      };\n\n      BarView.prototype.incrementalRender = function (params, seriesModel) {\n        // Reset\n        this._progressiveEls = []; // Do not support progressive in normal mode.\n\n        this._incrementalRenderLarge(params, seriesModel);\n      };\n\n      BarView.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      BarView.prototype._updateDrawMode = function (seriesModel) {\n        var isLargeDraw = seriesModel.pipelineContext.large;\n\n        if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) {\n          this._isLargeDraw = isLargeDraw;\n\n          this._clear();\n        }\n      };\n\n      BarView.prototype._renderNormal = function (seriesModel, ecModel, api, payload) {\n        var group = this.group;\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var coord = seriesModel.coordinateSystem;\n        var baseAxis = coord.getBaseAxis();\n        var isHorizontalOrRadial;\n\n        if (coord.type === 'cartesian2d') {\n          isHorizontalOrRadial = baseAxis.isHorizontal();\n        } else if (coord.type === 'polar') {\n          isHorizontalOrRadial = baseAxis.dim === 'angle';\n        }\n\n        var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;\n        var realtimeSortCfg = shouldRealtimeSort(seriesModel, coord);\n\n        if (realtimeSortCfg) {\n          this._enableRealtimeSort(realtimeSortCfg, data, api);\n        }\n\n        var needsClip = seriesModel.get('clip', true) || realtimeSortCfg;\n        var coordSysClipArea = getClipArea(coord, data); // If there is clipPath created in large mode. Remove it.\n\n        group.removeClipPath(); // We don't use clipPath in normal mode because we needs a perfect animation\n        // And don't want the label are clipped.\n\n        var roundCap = seriesModel.get('roundCap', true);\n        var drawBackground = seriesModel.get('showBackground', true);\n        var backgroundModel = seriesModel.getModel('backgroundStyle');\n        var barBorderRadius = backgroundModel.get('borderRadius') || 0;\n        var bgEls = [];\n        var oldBgEls = this._backgroundEls;\n        var isInitSort = payload && payload.isInitSort;\n        var isChangeOrder = payload && payload.type === 'changeAxisOrder';\n\n        function createBackground(dataIndex) {\n          var bgLayout = getLayout[coord.type](data, dataIndex);\n          var bgEl = createBackgroundEl(coord, isHorizontalOrRadial, bgLayout);\n          bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n          if (coord.type === 'cartesian2d') {\n            bgEl.setShape('r', barBorderRadius);\n          }\n\n          bgEls[dataIndex] = bgEl;\n          return bgEl;\n        }\n        data.diff(oldData).add(function (dataIndex) {\n          var itemModel = data.getItemModel(dataIndex);\n          var layout = getLayout[coord.type](data, dataIndex, itemModel);\n\n          if (drawBackground) {\n            createBackground(dataIndex);\n          } // If dataZoom in filteMode: 'empty', the baseValue can be set as NaN in \"axisProxy\".\n\n\n          if (!data.hasValue(dataIndex) || !isValidLayout[coord.type](layout)) {\n            return;\n          }\n\n          var isClipped = false;\n\n          if (needsClip) {\n            // Clip will modify the layout params.\n            // And return a boolean to determine if the shape are fully clipped.\n            isClipped = clip[coord.type](coordSysClipArea, layout);\n          }\n\n          var el = elementCreator[coord.type](seriesModel, data, dataIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, false, roundCap);\n\n          if (realtimeSortCfg) {\n            /**\n             * Force label animation because even if the element is\n             * ignored because it's clipped, it may not be clipped after\n             * changing order. Then, if not using forceLabelAnimation,\n             * the label animation was never started, in which case,\n             * the label will be the final value and doesn't have label\n             * animation.\n             */\n            el.forceLabelAnimation = true;\n          }\n\n          updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n\n          if (isInitSort) {\n            el.attr({\n              shape: layout\n            });\n          } else if (realtimeSortCfg) {\n            updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, dataIndex, isHorizontalOrRadial, false, false);\n          } else {\n            initProps(el, {\n              shape: layout\n            }, seriesModel, dataIndex);\n          }\n\n          data.setItemGraphicEl(dataIndex, el);\n          group.add(el);\n          el.ignore = isClipped;\n        }).update(function (newIndex, oldIndex) {\n          var itemModel = data.getItemModel(newIndex);\n          var layout = getLayout[coord.type](data, newIndex, itemModel);\n\n          if (drawBackground) {\n            var bgEl = void 0;\n\n            if (oldBgEls.length === 0) {\n              bgEl = createBackground(oldIndex);\n            } else {\n              bgEl = oldBgEls[oldIndex];\n              bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n              if (coord.type === 'cartesian2d') {\n                bgEl.setShape('r', barBorderRadius);\n              }\n\n              bgEls[newIndex] = bgEl;\n            }\n\n            var bgLayout = getLayout[coord.type](data, newIndex);\n            var shape = createBackgroundShape(isHorizontalOrRadial, bgLayout, coord);\n            updateProps(bgEl, {\n              shape: shape\n            }, animationModel, newIndex);\n          }\n\n          var el = oldData.getItemGraphicEl(oldIndex);\n\n          if (!data.hasValue(newIndex) || !isValidLayout[coord.type](layout)) {\n            group.remove(el);\n            return;\n          }\n\n          var isClipped = false;\n\n          if (needsClip) {\n            isClipped = clip[coord.type](coordSysClipArea, layout);\n\n            if (isClipped) {\n              group.remove(el);\n            }\n          }\n\n          if (!el) {\n            el = elementCreator[coord.type](seriesModel, data, newIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, !!el, roundCap);\n          } else {\n            saveOldStyle(el);\n          }\n\n          if (realtimeSortCfg) {\n            el.forceLabelAnimation = true;\n          }\n\n          if (isChangeOrder) {\n            var textEl = el.getTextContent();\n\n            if (textEl) {\n              var labelInnerStore = labelInner(textEl);\n\n              if (labelInnerStore.prevValue != null) {\n                /**\n                 * Set preValue to be value so that no new label\n                 * should be started, otherwise, it will take a full\n                 * `animationDurationUpdate` time to finish the\n                 * animation, which is not expected.\n                 */\n                labelInnerStore.prevValue = labelInnerStore.value;\n              }\n            }\n          } // Not change anything if only order changed.\n          // Especially not change label.\n          else {\n              updateStyle(el, data, newIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n            }\n\n          if (isInitSort) {\n            el.attr({\n              shape: layout\n            });\n          } else if (realtimeSortCfg) {\n            updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, newIndex, isHorizontalOrRadial, true, isChangeOrder);\n          } else {\n            updateProps(el, {\n              shape: layout\n            }, seriesModel, newIndex, null);\n          }\n\n          data.setItemGraphicEl(newIndex, el);\n          el.ignore = isClipped;\n          group.add(el);\n        }).remove(function (dataIndex) {\n          var el = oldData.getItemGraphicEl(dataIndex);\n          el && removeElementWithFadeOut(el, seriesModel, dataIndex);\n        }).execute();\n        var bgGroup = this._backgroundGroup || (this._backgroundGroup = new Group());\n        bgGroup.removeAll();\n\n        for (var i = 0; i < bgEls.length; ++i) {\n          bgGroup.add(bgEls[i]);\n        }\n\n        group.add(bgGroup);\n        this._backgroundEls = bgEls;\n        this._data = data;\n      };\n\n      BarView.prototype._renderLarge = function (seriesModel, ecModel, api) {\n        this._clear();\n\n        createLarge(seriesModel, this.group);\n\n        this._updateLargeClip(seriesModel);\n      };\n\n      BarView.prototype._incrementalRenderLarge = function (params, seriesModel) {\n        this._removeBackground();\n\n        createLarge(seriesModel, this.group, this._progressiveEls, true);\n      };\n\n      BarView.prototype._updateLargeClip = function (seriesModel) {\n        // Use clipPath in large mode.\n        var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel);\n        var group = this.group;\n\n        if (clipPath) {\n          group.setClipPath(clipPath);\n        } else {\n          group.removeClipPath();\n        }\n      };\n\n      BarView.prototype._enableRealtimeSort = function (realtimeSortCfg, data, api) {\n        var _this = this; // If no data in the first frame, wait for data to initSort\n\n\n        if (!data.count()) {\n          return;\n        }\n\n        var baseAxis = realtimeSortCfg.baseAxis;\n\n        if (this._isFirstFrame) {\n          this._dispatchInitSort(data, realtimeSortCfg, api);\n\n          this._isFirstFrame = false;\n        } else {\n          var orderMapping_1 = function (idx) {\n            var el = data.getItemGraphicEl(idx);\n            var shape = el && el.shape;\n            return shape && // The result should be consistent with the initial sort by data value.\n            // Do not support the case that both positive and negative exist.\n            Math.abs(baseAxis.isHorizontal() ? shape.height : shape.width) // If data is NaN, shape.xxx may be NaN, so use || 0 here in case\n            || 0;\n          };\n\n          this._onRendered = function () {\n            _this._updateSortWithinSameData(data, orderMapping_1, baseAxis, api);\n          };\n\n          api.getZr().on('rendered', this._onRendered);\n        }\n      };\n\n      BarView.prototype._dataSort = function (data, baseAxis, orderMapping) {\n        var info = [];\n        data.each(data.mapDimension(baseAxis.dim), function (ordinalNumber, dataIdx) {\n          var mappedValue = orderMapping(dataIdx);\n          mappedValue = mappedValue == null ? NaN : mappedValue;\n          info.push({\n            dataIndex: dataIdx,\n            mappedValue: mappedValue,\n            ordinalNumber: ordinalNumber\n          });\n        });\n        info.sort(function (a, b) {\n          // If NaN, it will be treated as min val.\n          return b.mappedValue - a.mappedValue;\n        });\n        return {\n          ordinalNumbers: map(info, function (item) {\n            return item.ordinalNumber;\n          })\n        };\n      };\n\n      BarView.prototype._isOrderChangedWithinSameData = function (data, orderMapping, baseAxis) {\n        var scale = baseAxis.scale;\n        var ordinalDataDim = data.mapDimension(baseAxis.dim);\n        var lastValue = Number.MAX_VALUE;\n\n        for (var tickNum = 0, len = scale.getOrdinalMeta().categories.length; tickNum < len; ++tickNum) {\n          var rawIdx = data.rawIndexOf(ordinalDataDim, scale.getRawOrdinalNumber(tickNum));\n          var value = rawIdx < 0 // If some tick have no bar, the tick will be treated as min.\n          ? Number.MIN_VALUE // PENDING: if dataZoom on baseAxis exits, is it a performance issue?\n          : orderMapping(data.indexOfRawIndex(rawIdx));\n\n          if (value > lastValue) {\n            return true;\n          }\n\n          lastValue = value;\n        }\n\n        return false;\n      };\n      /*\n       * Consider the case when A and B changed order, whose representing\n       * bars are both out of sight, we don't wish to trigger reorder action\n       * as long as the order in the view doesn't change.\n       */\n\n\n      BarView.prototype._isOrderDifferentInView = function (orderInfo, baseAxis) {\n        var scale = baseAxis.scale;\n        var extent = scale.getExtent();\n        var tickNum = Math.max(0, extent[0]);\n        var tickMax = Math.min(extent[1], scale.getOrdinalMeta().categories.length - 1);\n\n        for (; tickNum <= tickMax; ++tickNum) {\n          if (orderInfo.ordinalNumbers[tickNum] !== scale.getRawOrdinalNumber(tickNum)) {\n            return true;\n          }\n        }\n      };\n\n      BarView.prototype._updateSortWithinSameData = function (data, orderMapping, baseAxis, api) {\n        if (!this._isOrderChangedWithinSameData(data, orderMapping, baseAxis)) {\n          return;\n        }\n\n        var sortInfo = this._dataSort(data, baseAxis, orderMapping);\n\n        if (this._isOrderDifferentInView(sortInfo, baseAxis)) {\n          this._removeOnRenderedListener(api);\n\n          api.dispatchAction({\n            type: 'changeAxisOrder',\n            componentType: baseAxis.dim + 'Axis',\n            axisId: baseAxis.index,\n            sortInfo: sortInfo\n          });\n        }\n      };\n\n      BarView.prototype._dispatchInitSort = function (data, realtimeSortCfg, api) {\n        var baseAxis = realtimeSortCfg.baseAxis;\n\n        var sortResult = this._dataSort(data, baseAxis, function (dataIdx) {\n          return data.get(data.mapDimension(realtimeSortCfg.otherAxis.dim), dataIdx);\n        });\n\n        api.dispatchAction({\n          type: 'changeAxisOrder',\n          componentType: baseAxis.dim + 'Axis',\n          isInitSort: true,\n          axisId: baseAxis.index,\n          sortInfo: sortResult\n        });\n      };\n\n      BarView.prototype.remove = function (ecModel, api) {\n        this._clear(this._model);\n\n        this._removeOnRenderedListener(api);\n      };\n\n      BarView.prototype.dispose = function (ecModel, api) {\n        this._removeOnRenderedListener(api);\n      };\n\n      BarView.prototype._removeOnRenderedListener = function (api) {\n        if (this._onRendered) {\n          api.getZr().off('rendered', this._onRendered);\n          this._onRendered = null;\n        }\n      };\n\n      BarView.prototype._clear = function (model) {\n        var group = this.group;\n        var data = this._data;\n\n        if (model && model.isAnimationEnabled() && data && !this._isLargeDraw) {\n          this._removeBackground();\n\n          this._backgroundEls = [];\n          data.eachItemGraphicEl(function (el) {\n            removeElementWithFadeOut(el, model, getECData(el).dataIndex);\n          });\n        } else {\n          group.removeAll();\n        }\n\n        this._data = null;\n        this._isFirstFrame = true;\n      };\n\n      BarView.prototype._removeBackground = function () {\n        this.group.remove(this._backgroundGroup);\n        this._backgroundGroup = null;\n      };\n\n      BarView.type = 'bar';\n      return BarView;\n    }(ChartView);\n\n    var clip = {\n      cartesian2d: function (coordSysBoundingRect, layout) {\n        var signWidth = layout.width < 0 ? -1 : 1;\n        var signHeight = layout.height < 0 ? -1 : 1; // Needs positive width and height\n\n        if (signWidth < 0) {\n          layout.x += layout.width;\n          layout.width = -layout.width;\n        }\n\n        if (signHeight < 0) {\n          layout.y += layout.height;\n          layout.height = -layout.height;\n        }\n\n        var coordSysX2 = coordSysBoundingRect.x + coordSysBoundingRect.width;\n        var coordSysY2 = coordSysBoundingRect.y + coordSysBoundingRect.height;\n        var x = mathMax$6(layout.x, coordSysBoundingRect.x);\n        var x2 = mathMin$6(layout.x + layout.width, coordSysX2);\n        var y = mathMax$6(layout.y, coordSysBoundingRect.y);\n        var y2 = mathMin$6(layout.y + layout.height, coordSysY2);\n        var xClipped = x2 < x;\n        var yClipped = y2 < y; // When xClipped or yClipped, the element will be marked as `ignore`.\n        // But we should also place the element at the edge of the coord sys bounding rect.\n        // Because if data changed and the bar shows again, its transition animation\n        // will begin at this place.\n\n        layout.x = xClipped && x > coordSysX2 ? x2 : x;\n        layout.y = yClipped && y > coordSysY2 ? y2 : y;\n        layout.width = xClipped ? 0 : x2 - x;\n        layout.height = yClipped ? 0 : y2 - y; // Reverse back\n\n        if (signWidth < 0) {\n          layout.x += layout.width;\n          layout.width = -layout.width;\n        }\n\n        if (signHeight < 0) {\n          layout.y += layout.height;\n          layout.height = -layout.height;\n        }\n\n        return xClipped || yClipped;\n      },\n      polar: function (coordSysClipArea, layout) {\n        var signR = layout.r0 <= layout.r ? 1 : -1; // Make sure r is larger than r0\n\n        if (signR < 0) {\n          var tmp = layout.r;\n          layout.r = layout.r0;\n          layout.r0 = tmp;\n        }\n\n        var r = mathMin$6(layout.r, coordSysClipArea.r);\n        var r0 = mathMax$6(layout.r0, coordSysClipArea.r0);\n        layout.r = r;\n        layout.r0 = r0;\n        var clipped = r - r0 < 0; // Reverse back\n\n        if (signR < 0) {\n          var tmp = layout.r;\n          layout.r = layout.r0;\n          layout.r0 = tmp;\n        }\n\n        return clipped;\n      }\n    };\n    var elementCreator = {\n      cartesian2d: function (seriesModel, data, newIndex, layout, isHorizontal, animationModel, axisModel, isUpdate, roundCap) {\n        var rect = new Rect({\n          shape: extend({}, layout),\n          z2: 1\n        });\n        rect.__dataIndex = newIndex;\n        rect.name = 'item';\n\n        if (animationModel) {\n          var rectShape = rect.shape;\n          var animateProperty = isHorizontal ? 'height' : 'width';\n          rectShape[animateProperty] = 0;\n        }\n\n        return rect;\n      },\n      polar: function (seriesModel, data, newIndex, layout, isRadial, animationModel, axisModel, isUpdate, roundCap) {\n        var ShapeClass = !isRadial && roundCap ? SausagePath : Sector;\n        var sector = new ShapeClass({\n          shape: layout,\n          z2: 1\n        });\n        sector.name = 'item';\n        var positionMap = createPolarPositionMapping(isRadial);\n        sector.calculateTextPosition = createSectorCalculateTextPosition(positionMap, {\n          isRoundCap: ShapeClass === SausagePath\n        }); // Animation\n\n        if (animationModel) {\n          var sectorShape = sector.shape;\n          var animateProperty = isRadial ? 'r' : 'endAngle';\n          var animateTarget = {};\n          sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;\n          animateTarget[animateProperty] = layout[animateProperty];\n          (isUpdate ? updateProps : initProps)(sector, {\n            shape: animateTarget // __value: typeof dataValue === 'string' ? parseInt(dataValue, 10) : dataValue\n\n          }, animationModel);\n        }\n\n        return sector;\n      }\n    };\n\n    function shouldRealtimeSort(seriesModel, coordSys) {\n      var realtimeSortOption = seriesModel.get('realtimeSort', true);\n      var baseAxis = coordSys.getBaseAxis();\n\n      if (\"development\" !== 'production') {\n        if (realtimeSortOption) {\n          if (baseAxis.type !== 'category') {\n            warn('`realtimeSort` will not work because this bar series is not based on a category axis.');\n          }\n\n          if (coordSys.type !== 'cartesian2d') {\n            warn('`realtimeSort` will not work because this bar series is not on cartesian2d.');\n          }\n        }\n      }\n\n      if (realtimeSortOption && baseAxis.type === 'category' && coordSys.type === 'cartesian2d') {\n        return {\n          baseAxis: baseAxis,\n          otherAxis: coordSys.getOtherAxis(baseAxis)\n        };\n      }\n    }\n\n    function updateRealtimeAnimation(realtimeSortCfg, seriesAnimationModel, el, layout, newIndex, isHorizontal, isUpdate, isChangeOrder) {\n      var seriesTarget;\n      var axisTarget;\n\n      if (isHorizontal) {\n        axisTarget = {\n          x: layout.x,\n          width: layout.width\n        };\n        seriesTarget = {\n          y: layout.y,\n          height: layout.height\n        };\n      } else {\n        axisTarget = {\n          y: layout.y,\n          height: layout.height\n        };\n        seriesTarget = {\n          x: layout.x,\n          width: layout.width\n        };\n      }\n\n      if (!isChangeOrder) {\n        // Keep the original growth animation if only axis order changed.\n        // Not start a new animation.\n        (isUpdate ? updateProps : initProps)(el, {\n          shape: seriesTarget\n        }, seriesAnimationModel, newIndex, null);\n      }\n\n      var axisAnimationModel = seriesAnimationModel ? realtimeSortCfg.baseAxis.model : null;\n      (isUpdate ? updateProps : initProps)(el, {\n        shape: axisTarget\n      }, axisAnimationModel, newIndex);\n    }\n\n    function checkPropertiesNotValid(obj, props) {\n      for (var i = 0; i < props.length; i++) {\n        if (!isFinite(obj[props[i]])) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    var rectPropties = ['x', 'y', 'width', 'height'];\n    var polarPropties = ['cx', 'cy', 'r', 'startAngle', 'endAngle'];\n    var isValidLayout = {\n      cartesian2d: function (layout) {\n        return !checkPropertiesNotValid(layout, rectPropties);\n      },\n      polar: function (layout) {\n        return !checkPropertiesNotValid(layout, polarPropties);\n      }\n    };\n    var getLayout = {\n      // itemModel is only used to get borderWidth, which is not needed\n      // when calculating bar background layout.\n      cartesian2d: function (data, dataIndex, itemModel) {\n        var layout = data.getItemLayout(dataIndex);\n        var fixedLineWidth = itemModel ? getLineWidth(itemModel, layout) : 0; // fix layout with lineWidth\n\n        var signX = layout.width > 0 ? 1 : -1;\n        var signY = layout.height > 0 ? 1 : -1;\n        return {\n          x: layout.x + signX * fixedLineWidth / 2,\n          y: layout.y + signY * fixedLineWidth / 2,\n          width: layout.width - signX * fixedLineWidth,\n          height: layout.height - signY * fixedLineWidth\n        };\n      },\n      polar: function (data, dataIndex, itemModel) {\n        var layout = data.getItemLayout(dataIndex);\n        return {\n          cx: layout.cx,\n          cy: layout.cy,\n          r0: layout.r0,\n          r: layout.r,\n          startAngle: layout.startAngle,\n          endAngle: layout.endAngle,\n          clockwise: layout.clockwise\n        };\n      }\n    };\n\n    function isZeroOnPolar(layout) {\n      return layout.startAngle != null && layout.endAngle != null && layout.startAngle === layout.endAngle;\n    }\n\n    function createPolarPositionMapping(isRadial) {\n      return function (isRadial) {\n        var arcOrAngle = isRadial ? 'Arc' : 'Angle';\n        return function (position) {\n          switch (position) {\n            case 'start':\n            case 'insideStart':\n            case 'end':\n            case 'insideEnd':\n              return position + arcOrAngle;\n\n            default:\n              return position;\n          }\n        };\n      }(isRadial);\n    }\n\n    function updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, isPolar) {\n      var style = data.getItemVisual(dataIndex, 'style');\n\n      if (!isPolar) {\n        el.setShape('r', itemModel.get(['itemStyle', 'borderRadius']) || 0);\n      }\n\n      el.useStyle(style);\n      var cursorStyle = itemModel.getShallow('cursor');\n      cursorStyle && el.attr('cursor', cursorStyle);\n      var labelPositionOutside = isPolar ? isHorizontalOrRadial ? layout.r >= layout.r0 ? 'endArc' : 'startArc' : layout.endAngle >= layout.startAngle ? 'endAngle' : 'startAngle' : isHorizontalOrRadial ? layout.height >= 0 ? 'bottom' : 'top' : layout.width >= 0 ? 'right' : 'left';\n      var labelStatesModels = getLabelStatesModels(itemModel);\n      setLabelStyle(el, labelStatesModels, {\n        labelFetcher: seriesModel,\n        labelDataIndex: dataIndex,\n        defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),\n        inheritColor: style.fill,\n        defaultOpacity: style.opacity,\n        defaultOutsidePosition: labelPositionOutside\n      });\n      var label = el.getTextContent();\n\n      if (isPolar && label) {\n        var position = itemModel.get(['label', 'position']);\n        el.textConfig.inside = position === 'middle' ? true : null;\n        setSectorTextRotation(el, position === 'outside' ? labelPositionOutside : position, createPolarPositionMapping(isHorizontalOrRadial), itemModel.get(['label', 'rotate']));\n      }\n\n      setLabelValueAnimation(label, labelStatesModels, seriesModel.getRawValue(dataIndex), function (value) {\n        return getDefaultInterpolatedLabel(data, value);\n      });\n      var emphasisModel = itemModel.getModel(['emphasis']);\n      toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      setStatesStylesFromModel(el, itemModel);\n\n      if (isZeroOnPolar(layout)) {\n        el.style.fill = 'none';\n        el.style.stroke = 'none';\n        each(el.states, function (state) {\n          if (state.style) {\n            state.style.fill = state.style.stroke = 'none';\n          }\n        });\n      }\n    } // In case width or height are too small.\n\n\n    function getLineWidth(itemModel, rawLayout) {\n      // Has no border.\n      var borderColor = itemModel.get(['itemStyle', 'borderColor']);\n\n      if (!borderColor || borderColor === 'none') {\n        return 0;\n      }\n\n      var lineWidth = itemModel.get(['itemStyle', 'borderWidth']) || 0; // width or height may be NaN for empty data\n\n      var width = isNaN(rawLayout.width) ? Number.MAX_VALUE : Math.abs(rawLayout.width);\n      var height = isNaN(rawLayout.height) ? Number.MAX_VALUE : Math.abs(rawLayout.height);\n      return Math.min(lineWidth, width, height);\n    }\n\n    var LagePathShape =\n    /** @class */\n    function () {\n      function LagePathShape() {}\n\n      return LagePathShape;\n    }();\n\n    var LargePath =\n    /** @class */\n    function (_super) {\n      __extends(LargePath, _super);\n\n      function LargePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'largeBar';\n        return _this;\n      }\n\n      LargePath.prototype.getDefaultShape = function () {\n        return new LagePathShape();\n      };\n\n      LargePath.prototype.buildPath = function (ctx, shape) {\n        // Drawing lines is more efficient than drawing\n        // a whole line or drawing rects.\n        var points = shape.points;\n        var baseDimIdx = this.baseDimIdx;\n        var valueDimIdx = 1 - this.baseDimIdx;\n        var startPoint = [];\n        var size = [];\n        var barWidth = this.barWidth;\n\n        for (var i = 0; i < points.length; i += 3) {\n          size[baseDimIdx] = barWidth;\n          size[valueDimIdx] = points[i + 2];\n          startPoint[baseDimIdx] = points[i + baseDimIdx];\n          startPoint[valueDimIdx] = points[i + valueDimIdx];\n          ctx.rect(startPoint[0], startPoint[1], size[0], size[1]);\n        }\n      };\n\n      return LargePath;\n    }(Path);\n\n    function createLarge(seriesModel, group, progressiveEls, incremental) {\n      // TODO support polar\n      var data = seriesModel.getData();\n      var baseDimIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0;\n      var largeDataIndices = data.getLayout('largeDataIndices');\n      var barWidth = data.getLayout('size');\n      var backgroundModel = seriesModel.getModel('backgroundStyle');\n      var bgPoints = data.getLayout('largeBackgroundPoints');\n\n      if (bgPoints) {\n        var bgEl = new LargePath({\n          shape: {\n            points: bgPoints\n          },\n          incremental: !!incremental,\n          silent: true,\n          z2: 0\n        });\n        bgEl.baseDimIdx = baseDimIdx;\n        bgEl.largeDataIndices = largeDataIndices;\n        bgEl.barWidth = barWidth;\n        bgEl.useStyle(backgroundModel.getItemStyle());\n        group.add(bgEl);\n        progressiveEls && progressiveEls.push(bgEl);\n      }\n\n      var el = new LargePath({\n        shape: {\n          points: data.getLayout('largePoints')\n        },\n        incremental: !!incremental,\n        ignoreCoarsePointer: true,\n        z2: 1\n      });\n      el.baseDimIdx = baseDimIdx;\n      el.largeDataIndices = largeDataIndices;\n      el.barWidth = barWidth;\n      group.add(el);\n      el.useStyle(data.getVisual('style')); // Enable tooltip and user mouse/touch event handlers.\n\n      getECData(el).seriesIndex = seriesModel.seriesIndex;\n\n      if (!seriesModel.get('silent')) {\n        el.on('mousedown', largePathUpdateDataIndex);\n        el.on('mousemove', largePathUpdateDataIndex);\n      }\n\n      progressiveEls && progressiveEls.push(el);\n    } // Use throttle to avoid frequently traverse to find dataIndex.\n\n\n    var largePathUpdateDataIndex = throttle(function (event) {\n      var largePath = this;\n      var dataIndex = largePathFindDataIndex(largePath, event.offsetX, event.offsetY);\n      getECData(largePath).dataIndex = dataIndex >= 0 ? dataIndex : null;\n    }, 30, false);\n\n    function largePathFindDataIndex(largePath, x, y) {\n      var baseDimIdx = largePath.baseDimIdx;\n      var valueDimIdx = 1 - baseDimIdx;\n      var points = largePath.shape.points;\n      var largeDataIndices = largePath.largeDataIndices;\n      var startPoint = [];\n      var size = [];\n      var barWidth = largePath.barWidth;\n\n      for (var i = 0, len = points.length / 3; i < len; i++) {\n        var ii = i * 3;\n        size[baseDimIdx] = barWidth;\n        size[valueDimIdx] = points[ii + 2];\n        startPoint[baseDimIdx] = points[ii + baseDimIdx];\n        startPoint[valueDimIdx] = points[ii + valueDimIdx];\n\n        if (size[valueDimIdx] < 0) {\n          startPoint[valueDimIdx] += size[valueDimIdx];\n          size[valueDimIdx] = -size[valueDimIdx];\n        }\n\n        if (x >= startPoint[0] && x <= startPoint[0] + size[0] && y >= startPoint[1] && y <= startPoint[1] + size[1]) {\n          return largeDataIndices[i];\n        }\n      }\n\n      return -1;\n    }\n\n    function createBackgroundShape(isHorizontalOrRadial, layout, coord) {\n      if (isCoordinateSystemType(coord, 'cartesian2d')) {\n        var rectShape = layout;\n        var coordLayout = coord.getArea();\n        return {\n          x: isHorizontalOrRadial ? rectShape.x : coordLayout.x,\n          y: isHorizontalOrRadial ? coordLayout.y : rectShape.y,\n          width: isHorizontalOrRadial ? rectShape.width : coordLayout.width,\n          height: isHorizontalOrRadial ? coordLayout.height : rectShape.height\n        };\n      } else {\n        var coordLayout = coord.getArea();\n        var sectorShape = layout;\n        return {\n          cx: coordLayout.cx,\n          cy: coordLayout.cy,\n          r0: isHorizontalOrRadial ? coordLayout.r0 : sectorShape.r0,\n          r: isHorizontalOrRadial ? coordLayout.r : sectorShape.r,\n          startAngle: isHorizontalOrRadial ? sectorShape.startAngle : 0,\n          endAngle: isHorizontalOrRadial ? sectorShape.endAngle : Math.PI * 2\n        };\n      }\n    }\n\n    function createBackgroundEl(coord, isHorizontalOrRadial, layout) {\n      var ElementClz = coord.type === 'polar' ? Sector : Rect;\n      return new ElementClz({\n        shape: createBackgroundShape(isHorizontalOrRadial, layout, coord),\n        silent: true,\n        z2: 0\n      });\n    }\n\n    function install$3(registers) {\n      registers.registerChartView(BarView);\n      registers.registerSeriesModel(BarSeriesModel);\n      registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'bar')); // Do layout after other overall layout, which can prepare some information.\n\n      registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('bar')); // Down sample after filter\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('bar'));\n      /**\n       * @payload\n       * @property {string} [componentType=series]\n       * @property {number} [dx]\n       * @property {number} [dy]\n       * @property {number} [zoom]\n       * @property {number} [originX]\n       * @property {number} [originY]\n       */\n\n      registers.registerAction({\n        type: 'changeAxisOrder',\n        event: 'changeAxisOrder',\n        update: 'update'\n      }, function (payload, ecModel) {\n        var componentType = payload.componentType || 'series';\n        ecModel.eachComponent({\n          mainType: componentType,\n          query: payload\n        }, function (componentModel) {\n          if (payload.sortInfo) {\n            componentModel.axis.setCategorySortInfo(payload.sortInfo);\n          }\n        });\n      });\n    }\n\n    var PI2$8 = Math.PI * 2;\n    var RADIAN = Math.PI / 180;\n\n    function getViewRect(seriesModel, api) {\n      return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n        width: api.getWidth(),\n        height: api.getHeight()\n      });\n    }\n\n    function getBasicPieLayout(seriesModel, api) {\n      var viewRect = getViewRect(seriesModel, api); // center can be string or number when coordinateSystem is specified\n\n      var center = seriesModel.get('center');\n      var radius = seriesModel.get('radius');\n\n      if (!isArray(radius)) {\n        radius = [0, radius];\n      }\n\n      var width = parsePercent$1(viewRect.width, api.getWidth());\n      var height = parsePercent$1(viewRect.height, api.getHeight());\n      var size = Math.min(width, height);\n      var r0 = parsePercent$1(radius[0], size / 2);\n      var r = parsePercent$1(radius[1], size / 2);\n      var cx;\n      var cy;\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (coordSys) {\n        // percentage is not allowed when coordinate system is specified\n        var point = coordSys.dataToPoint(center);\n        cx = point[0] || 0;\n        cy = point[1] || 0;\n      } else {\n        if (!isArray(center)) {\n          center = [center, center];\n        }\n\n        cx = parsePercent$1(center[0], width) + viewRect.x;\n        cy = parsePercent$1(center[1], height) + viewRect.y;\n      }\n\n      return {\n        cx: cx,\n        cy: cy,\n        r0: r0,\n        r: r\n      };\n    }\n    function pieLayout(seriesType, ecModel, api) {\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        var data = seriesModel.getData();\n        var valueDim = data.mapDimension('value');\n        var viewRect = getViewRect(seriesModel, api);\n\n        var _a = getBasicPieLayout(seriesModel, api),\n            cx = _a.cx,\n            cy = _a.cy,\n            r = _a.r,\n            r0 = _a.r0;\n\n        var startAngle = -seriesModel.get('startAngle') * RADIAN;\n        var minAngle = seriesModel.get('minAngle') * RADIAN;\n        var validDataCount = 0;\n        data.each(valueDim, function (value) {\n          !isNaN(value) && validDataCount++;\n        });\n        var sum = data.getSum(valueDim); // Sum may be 0\n\n        var unitRadian = Math.PI / (sum || validDataCount) * 2;\n        var clockwise = seriesModel.get('clockwise');\n        var roseType = seriesModel.get('roseType');\n        var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); // [0...max]\n\n        var extent = data.getDataExtent(valueDim);\n        extent[0] = 0; // In the case some sector angle is smaller than minAngle\n\n        var restAngle = PI2$8;\n        var valueSumLargerThanMinAngle = 0;\n        var currentAngle = startAngle;\n        var dir = clockwise ? 1 : -1;\n        data.setLayout({\n          viewRect: viewRect,\n          r: r\n        });\n        data.each(valueDim, function (value, idx) {\n          var angle;\n\n          if (isNaN(value)) {\n            data.setItemLayout(idx, {\n              angle: NaN,\n              startAngle: NaN,\n              endAngle: NaN,\n              clockwise: clockwise,\n              cx: cx,\n              cy: cy,\n              r0: r0,\n              r: roseType ? NaN : r\n            });\n            return;\n          } // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样？\n\n\n          if (roseType !== 'area') {\n            angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian;\n          } else {\n            angle = PI2$8 / validDataCount;\n          }\n\n          if (angle < minAngle) {\n            angle = minAngle;\n            restAngle -= minAngle;\n          } else {\n            valueSumLargerThanMinAngle += value;\n          }\n\n          var endAngle = currentAngle + dir * angle;\n          data.setItemLayout(idx, {\n            angle: angle,\n            startAngle: currentAngle,\n            endAngle: endAngle,\n            clockwise: clockwise,\n            cx: cx,\n            cy: cy,\n            r0: r0,\n            r: roseType ? linearMap(value, extent, [r0, r]) : r\n          });\n          currentAngle = endAngle;\n        }); // Some sector is constrained by minAngle\n        // Rest sectors needs recalculate angle\n\n        if (restAngle < PI2$8 && validDataCount) {\n          // Average the angle if rest angle is not enough after all angles is\n          // Constrained by minAngle\n          if (restAngle <= 1e-3) {\n            var angle_1 = PI2$8 / validDataCount;\n            data.each(valueDim, function (value, idx) {\n              if (!isNaN(value)) {\n                var layout_1 = data.getItemLayout(idx);\n                layout_1.angle = angle_1;\n                layout_1.startAngle = startAngle + dir * idx * angle_1;\n                layout_1.endAngle = startAngle + dir * (idx + 1) * angle_1;\n              }\n            });\n          } else {\n            unitRadian = restAngle / valueSumLargerThanMinAngle;\n            currentAngle = startAngle;\n            data.each(valueDim, function (value, idx) {\n              if (!isNaN(value)) {\n                var layout_2 = data.getItemLayout(idx);\n                var angle = layout_2.angle === minAngle ? minAngle : value * unitRadian;\n                layout_2.startAngle = currentAngle;\n                layout_2.endAngle = currentAngle + dir * angle;\n                currentAngle += dir * angle;\n              }\n            });\n          }\n        }\n      });\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function dataFilter(seriesType) {\n      return {\n        seriesType: seriesType,\n        reset: function (seriesModel, ecModel) {\n          var legendModels = ecModel.findComponents({\n            mainType: 'legend'\n          });\n\n          if (!legendModels || !legendModels.length) {\n            return;\n          }\n\n          var data = seriesModel.getData();\n          data.filterSelf(function (idx) {\n            var name = data.getName(idx); // If in any legend component the status is not selected.\n\n            for (var i = 0; i < legendModels.length; i++) {\n              // @ts-ignore FIXME: LegendModel\n              if (!legendModels[i].isSelected(name)) {\n                return false;\n              }\n            }\n\n            return true;\n          });\n        }\n      };\n    }\n\n    var RADIAN$1 = Math.PI / 180;\n\n    function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight, viewLeft, viewTop, farthestX) {\n      if (list.length < 2) {\n        return;\n      }\n\n      function recalculateXOnSemiToAlignOnEllipseCurve(semi) {\n        var rB = semi.rB;\n        var rB2 = rB * rB;\n\n        for (var i = 0; i < semi.list.length; i++) {\n          var item = semi.list[i];\n          var dy = Math.abs(item.label.y - cy); // horizontal r is always same with original r because x is not changed.\n\n          var rA = r + item.len;\n          var rA2 = rA * rA; // Use ellipse implicit function to calculate x\n\n          var dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2);\n          var newX = cx + (dx + item.len2) * dir;\n          var deltaX = newX - item.label.x;\n          var newTargetWidth = item.targetTextWidth - deltaX * dir; // text x is changed, so need to recalculate width.\n\n          constrainTextWidth(item, newTargetWidth, true);\n          item.label.x = newX;\n        }\n      } // Adjust X based on the shifted y. Make tight labels aligned on an ellipse curve.\n\n\n      function recalculateX(items) {\n        // Extremes of\n        var topSemi = {\n          list: [],\n          maxY: 0\n        };\n        var bottomSemi = {\n          list: [],\n          maxY: 0\n        };\n\n        for (var i = 0; i < items.length; i++) {\n          if (items[i].labelAlignTo !== 'none') {\n            continue;\n          }\n\n          var item = items[i];\n          var semi = item.label.y > cy ? bottomSemi : topSemi;\n          var dy = Math.abs(item.label.y - cy);\n\n          if (dy >= semi.maxY) {\n            var dx = item.label.x - cx - item.len2 * dir; // horizontal r is always same with original r because x is not changed.\n\n            var rA = r + item.len; // Canculate rB based on the topest / bottemest label.\n\n            var rB = Math.abs(dx) < rA ? Math.sqrt(dy * dy / (1 - dx * dx / rA / rA)) : rA;\n            semi.rB = rB;\n            semi.maxY = dy;\n          }\n\n          semi.list.push(item);\n        }\n\n        recalculateXOnSemiToAlignOnEllipseCurve(topSemi);\n        recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi);\n      }\n\n      var len = list.length;\n\n      for (var i = 0; i < len; i++) {\n        if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') {\n          var dx = list[i].label.x - farthestX;\n          list[i].linePoints[1][0] += dx;\n          list[i].label.x = farthestX;\n        }\n      }\n\n      if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) {\n        recalculateX(list);\n      }\n    }\n\n    function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop) {\n      var leftList = [];\n      var rightList = [];\n      var leftmostX = Number.MAX_VALUE;\n      var rightmostX = -Number.MAX_VALUE;\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var label = labelLayoutList[i].label;\n\n        if (isPositionCenter(labelLayoutList[i])) {\n          continue;\n        }\n\n        if (label.x < cx) {\n          leftmostX = Math.min(leftmostX, label.x);\n          leftList.push(labelLayoutList[i]);\n        } else {\n          rightmostX = Math.max(rightmostX, label.x);\n          rightList.push(labelLayoutList[i]);\n        }\n      }\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n\n        if (!isPositionCenter(layout) && layout.linePoints) {\n          if (layout.labelStyleWidth != null) {\n            continue;\n          }\n\n          var label = layout.label;\n          var linePoints = layout.linePoints;\n          var targetTextWidth = void 0;\n\n          if (layout.labelAlignTo === 'edge') {\n            if (label.x < cx) {\n              targetTextWidth = linePoints[2][0] - layout.labelDistance - viewLeft - layout.edgeDistance;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - layout.edgeDistance - linePoints[2][0] - layout.labelDistance;\n            }\n          } else if (layout.labelAlignTo === 'labelLine') {\n            if (label.x < cx) {\n              targetTextWidth = leftmostX - viewLeft - layout.bleedMargin;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - rightmostX - layout.bleedMargin;\n            }\n          } else {\n            if (label.x < cx) {\n              targetTextWidth = label.x - viewLeft - layout.bleedMargin;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin;\n            }\n          }\n\n          layout.targetTextWidth = targetTextWidth;\n          constrainTextWidth(layout, targetTextWidth);\n        }\n      }\n\n      adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX);\n      adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n\n        if (!isPositionCenter(layout) && layout.linePoints) {\n          var label = layout.label;\n          var linePoints = layout.linePoints;\n          var isAlignToEdge = layout.labelAlignTo === 'edge';\n          var padding = label.style.padding;\n          var paddingH = padding ? padding[1] + padding[3] : 0; // textRect.width already contains paddingH if bgColor is set\n\n          var extraPaddingH = label.style.backgroundColor ? 0 : paddingH;\n          var realTextWidth = layout.rect.width + extraPaddingH;\n          var dist = linePoints[1][0] - linePoints[2][0];\n\n          if (isAlignToEdge) {\n            if (label.x < cx) {\n              linePoints[2][0] = viewLeft + layout.edgeDistance + realTextWidth + layout.labelDistance;\n            } else {\n              linePoints[2][0] = viewLeft + viewWidth - layout.edgeDistance - realTextWidth - layout.labelDistance;\n            }\n          } else {\n            if (label.x < cx) {\n              linePoints[2][0] = label.x + layout.labelDistance;\n            } else {\n              linePoints[2][0] = label.x - layout.labelDistance;\n            }\n\n            linePoints[1][0] = linePoints[2][0] + dist;\n          }\n\n          linePoints[1][1] = linePoints[2][1] = label.y;\n        }\n      }\n    }\n    /**\n     * Set max width of each label, and then wrap each label to the max width.\n     *\n     * @param layout label layout\n     * @param availableWidth max width for the label to display\n     * @param forceRecalculate recaculate the text layout even if the current width\n     * is smaller than `availableWidth`. This is useful when the text was previously\n     * wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in\n     * which case, previous wrapping should be redo.\n     */\n\n\n    function constrainTextWidth(layout, availableWidth, forceRecalculate) {\n      if (forceRecalculate === void 0) {\n        forceRecalculate = false;\n      }\n\n      if (layout.labelStyleWidth != null) {\n        // User-defined style.width has the highest priority.\n        return;\n      }\n\n      var label = layout.label;\n      var style = label.style;\n      var textRect = layout.rect;\n      var bgColor = style.backgroundColor;\n      var padding = style.padding;\n      var paddingH = padding ? padding[1] + padding[3] : 0;\n      var overflow = style.overflow; // textRect.width already contains paddingH if bgColor is set\n\n      var oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH);\n\n      if (availableWidth < oldOuterWidth || forceRecalculate) {\n        var oldHeight = textRect.height;\n\n        if (overflow && overflow.match('break')) {\n          // Temporarily set background to be null to calculate\n          // the bounding box without background.\n          label.setStyle('backgroundColor', null); // Set constraining width\n\n          label.setStyle('width', availableWidth - paddingH); // This is the real bounding box of the text without padding.\n\n          var innerRect = label.getBoundingRect();\n          label.setStyle('width', Math.ceil(innerRect.width));\n          label.setStyle('backgroundColor', bgColor);\n        } else {\n          var availableInnerWidth = availableWidth - paddingH;\n          var newWidth = availableWidth < oldOuterWidth // Current text is too wide, use `availableWidth` as max width.\n          ? availableInnerWidth : // Current available width is enough, but the text may have\n          // already been wrapped with a smaller available width.\n          forceRecalculate ? availableInnerWidth > layout.unconstrainedWidth // Current available is larger than text width,\n          // so don't constrain width (otherwise it may have\n          // empty space in the background).\n          ? null // Current available is smaller than text width, so\n          // use the current available width as constraining\n          // width.\n          : availableInnerWidth : // Current available width is enough, so no need to\n          // constrain.\n          null;\n          label.setStyle('width', newWidth);\n        }\n\n        var newRect = label.getBoundingRect();\n        textRect.width = newRect.width;\n        var margin = (label.style.margin || 0) + 2.1;\n        textRect.height = newRect.height + margin;\n        textRect.y -= (textRect.height - oldHeight) / 2;\n      }\n    }\n\n    function isPositionCenter(sectorShape) {\n      // Not change x for center label\n      return sectorShape.position === 'center';\n    }\n\n    function pieLabelLayout(seriesModel) {\n      var data = seriesModel.getData();\n      var labelLayoutList = [];\n      var cx;\n      var cy;\n      var hasLabelRotate = false;\n      var minShowLabelRadian = (seriesModel.get('minShowLabelAngle') || 0) * RADIAN$1;\n      var viewRect = data.getLayout('viewRect');\n      var r = data.getLayout('r');\n      var viewWidth = viewRect.width;\n      var viewLeft = viewRect.x;\n      var viewTop = viewRect.y;\n      var viewHeight = viewRect.height;\n\n      function setNotShow(el) {\n        el.ignore = true;\n      }\n\n      function isLabelShown(label) {\n        if (!label.ignore) {\n          return true;\n        }\n\n        for (var key in label.states) {\n          if (label.states[key].ignore === false) {\n            return true;\n          }\n        }\n\n        return false;\n      }\n\n      data.each(function (idx) {\n        var sector = data.getItemGraphicEl(idx);\n        var sectorShape = sector.shape;\n        var label = sector.getTextContent();\n        var labelLine = sector.getTextGuideLine();\n        var itemModel = data.getItemModel(idx);\n        var labelModel = itemModel.getModel('label'); // Use position in normal or emphasis\n\n        var labelPosition = labelModel.get('position') || itemModel.get(['emphasis', 'label', 'position']);\n        var labelDistance = labelModel.get('distanceToLabelLine');\n        var labelAlignTo = labelModel.get('alignTo');\n        var edgeDistance = parsePercent$1(labelModel.get('edgeDistance'), viewWidth);\n        var bleedMargin = labelModel.get('bleedMargin');\n        var labelLineModel = itemModel.getModel('labelLine');\n        var labelLineLen = labelLineModel.get('length');\n        labelLineLen = parsePercent$1(labelLineLen, viewWidth);\n        var labelLineLen2 = labelLineModel.get('length2');\n        labelLineLen2 = parsePercent$1(labelLineLen2, viewWidth);\n\n        if (Math.abs(sectorShape.endAngle - sectorShape.startAngle) < minShowLabelRadian) {\n          each(label.states, setNotShow);\n          label.ignore = true;\n\n          if (labelLine) {\n            each(labelLine.states, setNotShow);\n            labelLine.ignore = true;\n          }\n\n          return;\n        }\n\n        if (!isLabelShown(label)) {\n          return;\n        }\n\n        var midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2;\n        var nx = Math.cos(midAngle);\n        var ny = Math.sin(midAngle);\n        var textX;\n        var textY;\n        var linePoints;\n        var textAlign;\n        cx = sectorShape.cx;\n        cy = sectorShape.cy;\n        var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';\n\n        if (labelPosition === 'center') {\n          textX = sectorShape.cx;\n          textY = sectorShape.cy;\n          textAlign = 'center';\n        } else {\n          var x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * nx : sectorShape.r * nx) + cx;\n          var y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * ny : sectorShape.r * ny) + cy;\n          textX = x1 + nx * 3;\n          textY = y1 + ny * 3;\n\n          if (!isLabelInside) {\n            // For roseType\n            var x2 = x1 + nx * (labelLineLen + r - sectorShape.r);\n            var y2 = y1 + ny * (labelLineLen + r - sectorShape.r);\n            var x3 = x2 + (nx < 0 ? -1 : 1) * labelLineLen2;\n            var y3 = y2;\n\n            if (labelAlignTo === 'edge') {\n              // Adjust textX because text align of edge is opposite\n              textX = nx < 0 ? viewLeft + edgeDistance : viewLeft + viewWidth - edgeDistance;\n            } else {\n              textX = x3 + (nx < 0 ? -labelDistance : labelDistance);\n            }\n\n            textY = y3;\n            linePoints = [[x1, y1], [x2, y2], [x3, y3]];\n          }\n\n          textAlign = isLabelInside ? 'center' : labelAlignTo === 'edge' ? nx > 0 ? 'right' : 'left' : nx > 0 ? 'left' : 'right';\n        }\n\n        var PI = Math.PI;\n        var labelRotate = 0;\n        var rotate = labelModel.get('rotate');\n\n        if (isNumber(rotate)) {\n          labelRotate = rotate * (PI / 180);\n        } else if (labelPosition === 'center') {\n          labelRotate = 0;\n        } else if (rotate === 'radial' || rotate === true) {\n          var radialAngle = nx < 0 ? -midAngle + PI : -midAngle;\n          labelRotate = radialAngle;\n        } else if (rotate === 'tangential' && labelPosition !== 'outside' && labelPosition !== 'outer') {\n          var rad = Math.atan2(nx, ny);\n\n          if (rad < 0) {\n            rad = PI * 2 + rad;\n          }\n\n          var isDown = ny > 0;\n\n          if (isDown) {\n            rad = PI + rad;\n          }\n\n          labelRotate = rad - PI;\n        }\n\n        hasLabelRotate = !!labelRotate;\n        label.x = textX;\n        label.y = textY;\n        label.rotation = labelRotate;\n        label.setStyle({\n          verticalAlign: 'middle'\n        }); // Not sectorShape the inside label\n\n        if (!isLabelInside) {\n          var textRect = label.getBoundingRect().clone();\n          textRect.applyTransform(label.getComputedTransform()); // Text has a default 1px stroke. Exclude this.\n\n          var margin = (label.style.margin || 0) + 2.1;\n          textRect.y -= margin / 2;\n          textRect.height += margin;\n          labelLayoutList.push({\n            label: label,\n            labelLine: labelLine,\n            position: labelPosition,\n            len: labelLineLen,\n            len2: labelLineLen2,\n            minTurnAngle: labelLineModel.get('minTurnAngle'),\n            maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'),\n            surfaceNormal: new Point(nx, ny),\n            linePoints: linePoints,\n            textAlign: textAlign,\n            labelDistance: labelDistance,\n            labelAlignTo: labelAlignTo,\n            edgeDistance: edgeDistance,\n            bleedMargin: bleedMargin,\n            rect: textRect,\n            unconstrainedWidth: textRect.width,\n            labelStyleWidth: label.style.width\n          });\n        } else {\n          label.setStyle({\n            align: textAlign\n          });\n          var selectState = label.states.select;\n\n          if (selectState) {\n            selectState.x += label.x;\n            selectState.y += label.y;\n          }\n        }\n\n        sector.setTextConfig({\n          inside: isLabelInside\n        });\n      });\n\n      if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {\n        avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop);\n      }\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n        var label = layout.label;\n        var labelLine = layout.labelLine;\n        var notShowLabel = isNaN(label.x) || isNaN(label.y);\n\n        if (label) {\n          label.setStyle({\n            align: layout.textAlign\n          });\n\n          if (notShowLabel) {\n            each(label.states, setNotShow);\n            label.ignore = true;\n          }\n\n          var selectState = label.states.select;\n\n          if (selectState) {\n            selectState.x += label.x;\n            selectState.y += label.y;\n          }\n        }\n\n        if (labelLine) {\n          var linePoints = layout.linePoints;\n\n          if (notShowLabel || !linePoints) {\n            each(labelLine.states, setNotShow);\n            labelLine.ignore = true;\n          } else {\n            limitTurnAngle(linePoints, layout.minTurnAngle);\n            limitSurfaceAngle(linePoints, layout.surfaceNormal, layout.maxSurfaceAngle);\n            labelLine.setShape({\n              points: linePoints\n            }); // Set the anchor to the midpoint of sector\n\n            label.__hostTarget.textGuideLineConfig = {\n              anchor: new Point(linePoints[0][0], linePoints[0][1])\n            };\n          }\n        }\n      }\n    }\n\n    function getSectorCornerRadius(model, shape, zeroIfNull) {\n      var cornerRadius = model.get('borderRadius');\n\n      if (cornerRadius == null) {\n        return zeroIfNull ? {\n          cornerRadius: 0\n        } : null;\n      }\n\n      if (!isArray(cornerRadius)) {\n        cornerRadius = [cornerRadius, cornerRadius, cornerRadius, cornerRadius];\n      }\n\n      var dr = Math.abs(shape.r || 0 - shape.r0 || 0);\n      return {\n        cornerRadius: map(cornerRadius, function (cr) {\n          return parsePercent(cr, dr);\n        })\n      };\n    }\n\n    /**\n     * Piece of pie including Sector, Label, LabelLine\n     */\n\n    var PiePiece =\n    /** @class */\n    function (_super) {\n      __extends(PiePiece, _super);\n\n      function PiePiece(data, idx, startAngle) {\n        var _this = _super.call(this) || this;\n\n        _this.z2 = 2;\n        var text = new ZRText();\n\n        _this.setTextContent(text);\n\n        _this.updateData(data, idx, startAngle, true);\n\n        return _this;\n      }\n\n      PiePiece.prototype.updateData = function (data, idx, startAngle, firstCreate) {\n        var sector = this;\n        var seriesModel = data.hostModel;\n        var itemModel = data.getItemModel(idx);\n        var emphasisModel = itemModel.getModel('emphasis');\n        var layout = data.getItemLayout(idx); // cornerRadius & innerCornerRadius doesn't exist in the item layout. Use `0` if null value is specified.\n        // see `setItemLayout` in `pieLayout.ts`.\n\n        var sectorShape = extend(getSectorCornerRadius(itemModel.getModel('itemStyle'), layout, true), layout); // Ignore NaN data.\n\n        if (isNaN(sectorShape.startAngle)) {\n          // Use NaN shape to avoid drawing shape.\n          sector.setShape(sectorShape);\n          return;\n        }\n\n        if (firstCreate) {\n          sector.setShape(sectorShape);\n          var animationType = seriesModel.getShallow('animationType');\n\n          if (seriesModel.ecModel.ssr) {\n            // Use scale animation in SSR mode(opacity?)\n            // Because CSS SVG animation doesn't support very customized shape animation.\n            initProps(sector, {\n              scaleX: 0,\n              scaleY: 0\n            }, seriesModel, {\n              dataIndex: idx,\n              isFrom: true\n            });\n            sector.originX = sectorShape.cx;\n            sector.originY = sectorShape.cy;\n          } else if (animationType === 'scale') {\n            sector.shape.r = layout.r0;\n            initProps(sector, {\n              shape: {\n                r: layout.r\n              }\n            }, seriesModel, idx);\n          } // Expansion\n          else {\n              if (startAngle != null) {\n                sector.setShape({\n                  startAngle: startAngle,\n                  endAngle: startAngle\n                });\n                initProps(sector, {\n                  shape: {\n                    startAngle: layout.startAngle,\n                    endAngle: layout.endAngle\n                  }\n                }, seriesModel, idx);\n              } else {\n                sector.shape.endAngle = layout.startAngle;\n                updateProps(sector, {\n                  shape: {\n                    endAngle: layout.endAngle\n                  }\n                }, seriesModel, idx);\n              }\n            }\n        } else {\n          saveOldStyle(sector); // Transition animation from the old shape\n\n          updateProps(sector, {\n            shape: sectorShape\n          }, seriesModel, idx);\n        }\n\n        sector.useStyle(data.getItemVisual(idx, 'style'));\n        setStatesStylesFromModel(sector, itemModel);\n        var midAngle = (layout.startAngle + layout.endAngle) / 2;\n        var offset = seriesModel.get('selectedOffset');\n        var dx = Math.cos(midAngle) * offset;\n        var dy = Math.sin(midAngle) * offset;\n        var cursorStyle = itemModel.getShallow('cursor');\n        cursorStyle && sector.attr('cursor', cursorStyle);\n\n        this._updateLabel(seriesModel, data, idx);\n\n        sector.ensureState('emphasis').shape = extend({\n          r: layout.r + (emphasisModel.get('scale') ? emphasisModel.get('scaleSize') || 0 : 0)\n        }, getSectorCornerRadius(emphasisModel.getModel('itemStyle'), layout));\n        extend(sector.ensureState('select'), {\n          x: dx,\n          y: dy,\n          shape: getSectorCornerRadius(itemModel.getModel(['select', 'itemStyle']), layout)\n        });\n        extend(sector.ensureState('blur'), {\n          shape: getSectorCornerRadius(itemModel.getModel(['blur', 'itemStyle']), layout)\n        });\n        var labelLine = sector.getTextGuideLine();\n        var labelText = sector.getTextContent();\n        labelLine && extend(labelLine.ensureState('select'), {\n          x: dx,\n          y: dy\n        }); // TODO: needs dx, dy in zrender?\n\n        extend(labelText.ensureState('select'), {\n          x: dx,\n          y: dy\n        });\n        toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      };\n\n      PiePiece.prototype._updateLabel = function (seriesModel, data, idx) {\n        var sector = this;\n        var itemModel = data.getItemModel(idx);\n        var labelLineModel = itemModel.getModel('labelLine');\n        var style = data.getItemVisual(idx, 'style');\n        var visualColor = style && style.fill;\n        var visualOpacity = style && style.opacity;\n        setLabelStyle(sector, getLabelStatesModels(itemModel), {\n          labelFetcher: data.hostModel,\n          labelDataIndex: idx,\n          inheritColor: visualColor,\n          defaultOpacity: visualOpacity,\n          defaultText: seriesModel.getFormattedLabel(idx, 'normal') || data.getName(idx)\n        });\n        var labelText = sector.getTextContent(); // Set textConfig on sector.\n\n        sector.setTextConfig({\n          // reset position, rotation\n          position: null,\n          rotation: null\n        }); // Make sure update style on labelText after setLabelStyle.\n        // Because setLabelStyle will replace a new style on it.\n\n        labelText.attr({\n          z2: 10\n        });\n        var labelPosition = seriesModel.get(['label', 'position']);\n\n        if (labelPosition !== 'outside' && labelPosition !== 'outer') {\n          sector.removeTextGuideLine();\n        } else {\n          var polyline = this.getTextGuideLine();\n\n          if (!polyline) {\n            polyline = new Polyline();\n            this.setTextGuideLine(polyline);\n          } // Default use item visual color\n\n\n          setLabelLineStyle(this, getLabelLineStatesModels(itemModel), {\n            stroke: visualColor,\n            opacity: retrieve3(labelLineModel.get(['lineStyle', 'opacity']), visualOpacity, 1)\n          });\n        }\n      };\n\n      return PiePiece;\n    }(Sector); // Pie view\n\n\n    var PieView =\n    /** @class */\n    function (_super) {\n      __extends(PieView, _super);\n\n      function PieView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.ignoreLabelLineUpdate = true;\n        return _this;\n      }\n\n      PieView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var group = this.group;\n        var startAngle; // First render\n\n        if (!oldData && data.count() > 0) {\n          var shape = data.getItemLayout(0);\n\n          for (var s = 1; isNaN(shape && shape.startAngle) && s < data.count(); ++s) {\n            shape = data.getItemLayout(s);\n          }\n\n          if (shape) {\n            startAngle = shape.startAngle;\n          }\n        } // remove empty-circle if it exists\n\n\n        if (this._emptyCircleSector) {\n          group.remove(this._emptyCircleSector);\n        } // when all data are filtered, show lightgray empty circle\n\n\n        if (data.count() === 0 && seriesModel.get('showEmptyCircle')) {\n          var sector = new Sector({\n            shape: getBasicPieLayout(seriesModel, api)\n          });\n          sector.useStyle(seriesModel.getModel('emptyCircleStyle').getItemStyle());\n          this._emptyCircleSector = sector;\n          group.add(sector);\n        }\n\n        data.diff(oldData).add(function (idx) {\n          var piePiece = new PiePiece(data, idx, startAngle);\n          data.setItemGraphicEl(idx, piePiece);\n          group.add(piePiece);\n        }).update(function (newIdx, oldIdx) {\n          var piePiece = oldData.getItemGraphicEl(oldIdx);\n          piePiece.updateData(data, newIdx, startAngle);\n          piePiece.off('click');\n          group.add(piePiece);\n          data.setItemGraphicEl(newIdx, piePiece);\n        }).remove(function (idx) {\n          var piePiece = oldData.getItemGraphicEl(idx);\n          removeElementWithFadeOut(piePiece, seriesModel, idx);\n        }).execute();\n        pieLabelLayout(seriesModel); // Always use initial animation.\n\n        if (seriesModel.get('animationTypeUpdate') !== 'expansion') {\n          this._data = data;\n        }\n      };\n\n      PieView.prototype.dispose = function () {};\n\n      PieView.prototype.containPoint = function (point, seriesModel) {\n        var data = seriesModel.getData();\n        var itemLayout = data.getItemLayout(0);\n\n        if (itemLayout) {\n          var dx = point[0] - itemLayout.cx;\n          var dy = point[1] - itemLayout.cy;\n          var radius = Math.sqrt(dx * dx + dy * dy);\n          return radius <= itemLayout.r && radius >= itemLayout.r0;\n        }\n      };\n\n      PieView.type = 'pie';\n      return PieView;\n    }(ChartView);\n\n    /**\n     * [Usage]:\n     * (1)\n     * createListSimply(seriesModel, ['value']);\n     * (2)\n     * createListSimply(seriesModel, {\n     *     coordDimensions: ['value'],\n     *     dimensionsCount: 5\n     * });\n     */\n\n    function createSeriesDataSimply(seriesModel, opt, nameList) {\n      opt = isArray(opt) && {\n        coordDimensions: opt\n      } || extend({\n        encodeDefine: seriesModel.getEncode()\n      }, opt);\n      var source = seriesModel.getSource();\n      var dimensions = prepareSeriesDataSchema(source, opt).dimensions;\n      var list = new SeriesData(dimensions, seriesModel);\n      list.initData(source, nameList);\n      return list;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * LegendVisualProvider is an bridge that pick encoded color from data and\n     * provide to the legend component.\n     */\n    var LegendVisualProvider =\n    /** @class */\n    function () {\n      function LegendVisualProvider( // Function to get data after filtered. It stores all the encoding info\n      getDataWithEncodedVisual, // Function to get raw data before filtered.\n      getRawData) {\n        this._getDataWithEncodedVisual = getDataWithEncodedVisual;\n        this._getRawData = getRawData;\n      }\n\n      LegendVisualProvider.prototype.getAllNames = function () {\n        var rawData = this._getRawData(); // We find the name from the raw data. In case it's filtered by the legend component.\n        // Normally, the name can be found in rawData, but can't be found in filtered data will display as gray.\n\n\n        return rawData.mapArray(rawData.getName);\n      };\n\n      LegendVisualProvider.prototype.containName = function (name) {\n        var rawData = this._getRawData();\n\n        return rawData.indexOfName(name) >= 0;\n      };\n\n      LegendVisualProvider.prototype.indexOfName = function (name) {\n        // Only get data when necessary.\n        // Because LegendVisualProvider constructor may be new in the stage that data is not prepared yet.\n        // Invoking Series#getData immediately will throw an error.\n        var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n        return dataWithEncodedVisual.indexOfName(name);\n      };\n\n      LegendVisualProvider.prototype.getItemVisual = function (dataIndex, key) {\n        // Get encoded visual properties from final filtered data.\n        var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n        return dataWithEncodedVisual.getItemVisual(dataIndex, key);\n      };\n\n      return LegendVisualProvider;\n    }();\n\n    var innerData = makeInner();\n\n    var PieSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(PieSeriesModel, _super);\n\n      function PieSeriesModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.init = function (option) {\n        _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n        // Use a function instead of direct access because data reference may changed\n\n\n        this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n\n        this._defaultLabelLine(option);\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.mergeOption = function () {\n        _super.prototype.mergeOption.apply(this, arguments);\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.getInitialData = function () {\n        return createSeriesDataSimply(this, {\n          coordDimensions: ['value'],\n          encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n        });\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.getDataParams = function (dataIndex) {\n        var data = this.getData(); // update seats when data is changed\n\n        var dataInner = innerData(data);\n        var seats = dataInner.seats;\n\n        if (!seats) {\n          var valueList_1 = [];\n          data.each(data.mapDimension('value'), function (value) {\n            valueList_1.push(value);\n          });\n          seats = dataInner.seats = getPercentSeats(valueList_1, data.hostModel.get('percentPrecision'));\n        }\n\n        var params = _super.prototype.getDataParams.call(this, dataIndex); // seats may be empty when sum is 0\n\n\n        params.percent = seats[dataIndex] || 0;\n        params.$vars.push('percent');\n        return params;\n      };\n\n      PieSeriesModel.prototype._defaultLabelLine = function (option) {\n        // Extend labelLine emphasis\n        defaultEmphasis(option, 'labelLine', ['show']);\n        var labelLineNormalOpt = option.labelLine;\n        var labelLineEmphasisOpt = option.emphasis.labelLine; // Not show label line if `label.normal.show = false`\n\n        labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show;\n        labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show;\n      };\n\n      PieSeriesModel.type = 'series.pie';\n      PieSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        colorBy: 'data',\n        // 默认全局居中\n        center: ['50%', '50%'],\n        radius: [0, '75%'],\n        // 默认顺时针\n        clockwise: true,\n        startAngle: 90,\n        // 最小角度改为0\n        minAngle: 0,\n        // If the angle of a sector less than `minShowLabelAngle`,\n        // the label will not be displayed.\n        minShowLabelAngle: 0,\n        // 选中时扇区偏移量\n        selectedOffset: 10,\n        // 选择模式，默认关闭，可选single，multiple\n        // selectedMode: false,\n        // 南丁格尔玫瑰图模式，'radius'（半径） | 'area'（面积）\n        // roseType: null,\n        percentPrecision: 2,\n        // If still show when all data zero.\n        stillShowZeroSum: true,\n        // cursor: null,\n        left: 0,\n        top: 0,\n        right: 0,\n        bottom: 0,\n        width: null,\n        height: null,\n        label: {\n          // color: 'inherit',\n          // If rotate around circle\n          rotate: 0,\n          show: true,\n          overflow: 'truncate',\n          // 'outer', 'inside', 'center'\n          position: 'outer',\n          // 'none', 'labelLine', 'edge'. Works only when position is 'outer'\n          alignTo: 'none',\n          // Closest distance between label and chart edge.\n          // Works only position is 'outer' and alignTo is 'edge'.\n          edgeDistance: '25%',\n          // Works only position is 'outer' and alignTo is not 'edge'.\n          bleedMargin: 10,\n          // Distance between text and label line.\n          distanceToLabelLine: 5 // formatter: 标签文本格式器，同 tooltip.formatter，不支持异步回调\n          // 默认使用全局文本样式，详见 textStyle\n          // distance: 当position为inner时有效，为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数\n\n        },\n        // Enabled when label.normal.position is 'outer'\n        labelLine: {\n          show: true,\n          // 引导线两段中的第一段长度\n          length: 15,\n          // 引导线两段中的第二段长度\n          length2: 15,\n          smooth: false,\n          minTurnAngle: 90,\n          maxSurfaceAngle: 90,\n          lineStyle: {\n            // color: 各异,\n            width: 1,\n            type: 'solid'\n          }\n        },\n        itemStyle: {\n          borderWidth: 1,\n          borderJoin: 'round'\n        },\n        showEmptyCircle: true,\n        emptyCircleStyle: {\n          color: 'lightgray',\n          opacity: 1\n        },\n        labelLayout: {\n          // Hide the overlapped label.\n          hideOverlap: true\n        },\n        emphasis: {\n          scale: true,\n          scaleSize: 5\n        },\n        // If use strategy to avoid label overlapping\n        avoidLabelOverlap: true,\n        // Animation type. Valid values: expansion, scale\n        animationType: 'expansion',\n        animationDuration: 1000,\n        // Animation type when update. Valid values: transition, expansion\n        animationTypeUpdate: 'transition',\n        animationEasingUpdate: 'cubicInOut',\n        animationDurationUpdate: 500,\n        animationEasing: 'cubicInOut'\n      };\n      return PieSeriesModel;\n    }(SeriesModel);\n\n    function negativeDataFilter(seriesType) {\n      return {\n        seriesType: seriesType,\n        reset: function (seriesModel, ecModel) {\n          var data = seriesModel.getData();\n          data.filterSelf(function (idx) {\n            // handle negative value condition\n            var valueDim = data.mapDimension('value');\n            var curValue = data.get(valueDim, idx);\n\n            if (isNumber(curValue) && !isNaN(curValue) && curValue < 0) {\n              return false;\n            }\n\n            return true;\n          });\n        }\n      };\n    }\n\n    function install$4(registers) {\n      registers.registerChartView(PieView);\n      registers.registerSeriesModel(PieSeriesModel);\n      createLegacyDataSelectAction('pie', registers.registerAction);\n      registers.registerLayout(curry(pieLayout, 'pie'));\n      registers.registerProcessor(dataFilter('pie'));\n      registers.registerProcessor(negativeDataFilter('pie'));\n    }\n\n    var ScatterSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(ScatterSeriesModel, _super);\n\n      function ScatterSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScatterSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      }\n\n      ScatterSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      ScatterSeriesModel.prototype.getProgressive = function () {\n        var progressive = this.option.progressive;\n\n        if (progressive == null) {\n          // PENDING\n          return this.option.large ? 5e3 : this.get('progressive');\n        }\n\n        return progressive;\n      };\n\n      ScatterSeriesModel.prototype.getProgressiveThreshold = function () {\n        var progressiveThreshold = this.option.progressiveThreshold;\n\n        if (progressiveThreshold == null) {\n          // PENDING\n          return this.option.large ? 1e4 : this.get('progressiveThreshold');\n        }\n\n        return progressiveThreshold;\n      };\n\n      ScatterSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n        return selectors.point(data.getItemLayout(dataIndex));\n      };\n\n      ScatterSeriesModel.prototype.getZLevelKey = function () {\n        // Each progressive series has individual key.\n        return this.getData().count() > this.getProgressiveThreshold() ? this.id : '';\n      };\n\n      ScatterSeriesModel.type = 'series.scatter';\n      ScatterSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];\n      ScatterSeriesModel.defaultOption = {\n        coordinateSystem: 'cartesian2d',\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        symbolSize: 10,\n        // symbolRotate: null,  // 图形旋转控制\n        large: false,\n        // Available when large is true\n        largeThreshold: 2000,\n        // cursor: null,\n        itemStyle: {\n          opacity: 0.8 // color: 各异\n\n        },\n        emphasis: {\n          scale: true\n        },\n        // If clip the overflow graphics\n        // Works on cartesian / polar series\n        clip: true,\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        },\n        universalTransition: {\n          divideShape: 'clone'\n        } // progressive: null\n\n      };\n      return ScatterSeriesModel;\n    }(SeriesModel);\n\n    var BOOST_SIZE_THRESHOLD = 4;\n\n    var LargeSymbolPathShape =\n    /** @class */\n    function () {\n      function LargeSymbolPathShape() {}\n\n      return LargeSymbolPathShape;\n    }();\n\n    var LargeSymbolPath =\n    /** @class */\n    function (_super) {\n      __extends(LargeSymbolPath, _super);\n\n      function LargeSymbolPath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this._off = 0;\n        _this.hoverDataIdx = -1;\n        return _this;\n      }\n\n      LargeSymbolPath.prototype.getDefaultShape = function () {\n        return new LargeSymbolPathShape();\n      };\n\n      LargeSymbolPath.prototype.reset = function () {\n        this.notClear = false;\n        this._off = 0;\n      };\n\n      LargeSymbolPath.prototype.buildPath = function (path, shape) {\n        var points = shape.points;\n        var size = shape.size;\n        var symbolProxy = this.symbolProxy;\n        var symbolProxyShape = symbolProxy.shape;\n        var ctx = path.getContext ? path.getContext() : path;\n        var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD;\n        var softClipShape = this.softClipShape;\n        var i; // Do draw in afterBrush.\n\n        if (canBoost) {\n          this._ctx = ctx;\n          return;\n        }\n\n        this._ctx = null;\n\n        for (i = this._off; i < points.length;) {\n          var x = points[i++];\n          var y = points[i++];\n\n          if (isNaN(x) || isNaN(y)) {\n            continue;\n          }\n\n          if (softClipShape && !softClipShape.contain(x, y)) {\n            continue;\n          }\n\n          symbolProxyShape.x = x - size[0] / 2;\n          symbolProxyShape.y = y - size[1] / 2;\n          symbolProxyShape.width = size[0];\n          symbolProxyShape.height = size[1];\n          symbolProxy.buildPath(path, symbolProxyShape, true);\n        }\n\n        if (this.incremental) {\n          this._off = i;\n          this.notClear = true;\n        }\n      };\n\n      LargeSymbolPath.prototype.afterBrush = function () {\n        var shape = this.shape;\n        var points = shape.points;\n        var size = shape.size;\n        var ctx = this._ctx;\n        var softClipShape = this.softClipShape;\n        var i;\n\n        if (!ctx) {\n          return;\n        } // PENDING If style or other canvas status changed?\n\n\n        for (i = this._off; i < points.length;) {\n          var x = points[i++];\n          var y = points[i++];\n\n          if (isNaN(x) || isNaN(y)) {\n            continue;\n          }\n\n          if (softClipShape && !softClipShape.contain(x, y)) {\n            continue;\n          } // fillRect is faster than building a rect path and draw.\n          // And it support light globalCompositeOperation.\n\n\n          ctx.fillRect(x - size[0] / 2, y - size[1] / 2, size[0], size[1]);\n        }\n\n        if (this.incremental) {\n          this._off = i;\n          this.notClear = true;\n        }\n      };\n\n      LargeSymbolPath.prototype.findDataIndex = function (x, y) {\n        // TODO ???\n        // Consider transform\n        var shape = this.shape;\n        var points = shape.points;\n        var size = shape.size;\n        var w = Math.max(size[0], 4);\n        var h = Math.max(size[1], 4); // Not consider transform\n        // Treat each element as a rect\n        // top down traverse\n\n        for (var idx = points.length / 2 - 1; idx >= 0; idx--) {\n          var i = idx * 2;\n          var x0 = points[i] - w / 2;\n          var y0 = points[i + 1] - h / 2;\n\n          if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) {\n            return idx;\n          }\n        }\n\n        return -1;\n      };\n\n      LargeSymbolPath.prototype.contain = function (x, y) {\n        var localPos = this.transformCoordToLocal(x, y);\n        var rect = this.getBoundingRect();\n        x = localPos[0];\n        y = localPos[1];\n\n        if (rect.contain(x, y)) {\n          // Cache found data index.\n          var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y);\n          return dataIdx >= 0;\n        }\n\n        this.hoverDataIdx = -1;\n        return false;\n      };\n\n      LargeSymbolPath.prototype.getBoundingRect = function () {\n        // Ignore stroke for large symbol draw.\n        var rect = this._rect;\n\n        if (!rect) {\n          var shape = this.shape;\n          var points = shape.points;\n          var size = shape.size;\n          var w = size[0];\n          var h = size[1];\n          var minX = Infinity;\n          var minY = Infinity;\n          var maxX = -Infinity;\n          var maxY = -Infinity;\n\n          for (var i = 0; i < points.length;) {\n            var x = points[i++];\n            var y = points[i++];\n            minX = Math.min(x, minX);\n            maxX = Math.max(x, maxX);\n            minY = Math.min(y, minY);\n            maxY = Math.max(y, maxY);\n          }\n\n          rect = this._rect = new BoundingRect(minX - w / 2, minY - h / 2, maxX - minX + w, maxY - minY + h);\n        }\n\n        return rect;\n      };\n\n      return LargeSymbolPath;\n    }(Path);\n\n    var LargeSymbolDraw =\n    /** @class */\n    function () {\n      function LargeSymbolDraw() {\n        this.group = new Group();\n      }\n      /**\n       * Update symbols draw by new data\n       */\n\n\n      LargeSymbolDraw.prototype.updateData = function (data, opt) {\n        this._clear();\n\n        var symbolEl = this._create();\n\n        symbolEl.setShape({\n          points: data.getLayout('points')\n        });\n\n        this._setCommon(symbolEl, data, opt);\n      };\n\n      LargeSymbolDraw.prototype.updateLayout = function (data) {\n        var points = data.getLayout('points');\n        this.group.eachChild(function (child) {\n          if (child.startIndex != null) {\n            var len = (child.endIndex - child.startIndex) * 2;\n            var byteOffset = child.startIndex * 4 * 2;\n            points = new Float32Array(points.buffer, byteOffset, len);\n          }\n\n          child.setShape('points', points); // Reset draw cursor.\n\n          child.reset();\n        });\n      };\n\n      LargeSymbolDraw.prototype.incrementalPrepareUpdate = function (data) {\n        this._clear();\n      };\n\n      LargeSymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {\n        var lastAdded = this._newAdded[0];\n        var points = data.getLayout('points');\n        var oldPoints = lastAdded && lastAdded.shape.points; // Merging the exists. Each element has 1e4 points.\n        // Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization)\n\n        if (oldPoints && oldPoints.length < 2e4) {\n          var oldLen = oldPoints.length;\n          var newPoints = new Float32Array(oldLen + points.length); // Concat two array\n\n          newPoints.set(oldPoints);\n          newPoints.set(points, oldLen); // Update endIndex\n\n          lastAdded.endIndex = taskParams.end;\n          lastAdded.setShape({\n            points: newPoints\n          });\n        } else {\n          // Clear\n          this._newAdded = [];\n\n          var symbolEl = this._create();\n\n          symbolEl.startIndex = taskParams.start;\n          symbolEl.endIndex = taskParams.end;\n          symbolEl.incremental = true;\n          symbolEl.setShape({\n            points: points\n          });\n\n          this._setCommon(symbolEl, data, opt);\n        }\n      };\n\n      LargeSymbolDraw.prototype.eachRendered = function (cb) {\n        this._newAdded[0] && cb(this._newAdded[0]);\n      };\n\n      LargeSymbolDraw.prototype._create = function () {\n        var symbolEl = new LargeSymbolPath({\n          cursor: 'default'\n        });\n        symbolEl.ignoreCoarsePointer = true;\n        this.group.add(symbolEl);\n\n        this._newAdded.push(symbolEl);\n\n        return symbolEl;\n      };\n\n      LargeSymbolDraw.prototype._setCommon = function (symbolEl, data, opt) {\n        var hostModel = data.hostModel;\n        opt = opt || {};\n        var size = data.getVisual('symbolSize');\n        symbolEl.setShape('size', size instanceof Array ? size : [size, size]);\n        symbolEl.softClipShape = opt.clipShape || null; // Create symbolProxy to build path for each data\n\n        symbolEl.symbolProxy = createSymbol(data.getVisual('symbol'), 0, 0, 0, 0); // Use symbolProxy setColor method\n\n        symbolEl.setColor = symbolEl.symbolProxy.setColor;\n        var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD;\n        symbolEl.useStyle( // Draw shadow when doing fillRect is extremely slow.\n        hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color']));\n        var globalStyle = data.getVisual('style');\n        var visualColor = globalStyle && globalStyle.fill;\n\n        if (visualColor) {\n          symbolEl.setColor(visualColor);\n        }\n\n        var ecData = getECData(symbolEl); // Enable tooltip\n        // PENDING May have performance issue when path is extremely large\n\n        ecData.seriesIndex = hostModel.seriesIndex;\n        symbolEl.on('mousemove', function (e) {\n          ecData.dataIndex = null;\n          var dataIndex = symbolEl.hoverDataIdx;\n\n          if (dataIndex >= 0) {\n            // Provide dataIndex for tooltip\n            ecData.dataIndex = dataIndex + (symbolEl.startIndex || 0);\n          }\n        });\n      };\n\n      LargeSymbolDraw.prototype.remove = function () {\n        this._clear();\n      };\n\n      LargeSymbolDraw.prototype._clear = function () {\n        this._newAdded = [];\n        this.group.removeAll();\n      };\n\n      return LargeSymbolDraw;\n    }();\n\n    var ScatterView =\n    /** @class */\n    function (_super) {\n      __extends(ScatterView, _super);\n\n      function ScatterView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScatterView.type;\n        return _this;\n      }\n\n      ScatterView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n\n        var symbolDraw = this._updateSymbolDraw(data, seriesModel);\n\n        symbolDraw.updateData(data, {\n          // TODO\n          // If this parameter should be a shape or a bounding volume\n          // shape will be more general.\n          // But bounding volume like bounding rect will be much faster in the contain calculation\n          clipShape: this._getClipShape(seriesModel)\n        });\n        this._finished = true;\n      };\n\n      ScatterView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n\n        var symbolDraw = this._updateSymbolDraw(data, seriesModel);\n\n        symbolDraw.incrementalPrepareUpdate(data);\n        this._finished = false;\n      };\n\n      ScatterView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) {\n        this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData(), {\n          clipShape: this._getClipShape(seriesModel)\n        });\n\n        this._finished = taskParams.end === seriesModel.getData().count();\n      };\n\n      ScatterView.prototype.updateTransform = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData(); // Must mark group dirty and make sure the incremental layer will be cleared\n        // PENDING\n\n        this.group.dirty();\n\n        if (!this._finished || data.count() > 1e4) {\n          return {\n            update: true\n          };\n        } else {\n          var res = pointsLayout('').reset(seriesModel, ecModel, api);\n\n          if (res.progress) {\n            res.progress({\n              start: 0,\n              end: data.count(),\n              count: data.count()\n            }, data);\n          }\n\n          this._symbolDraw.updateLayout(data);\n        }\n      };\n\n      ScatterView.prototype.eachRendered = function (cb) {\n        this._symbolDraw && this._symbolDraw.eachRendered(cb);\n      };\n\n      ScatterView.prototype._getClipShape = function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n        var clipArea = coordSys && coordSys.getArea && coordSys.getArea();\n        return seriesModel.get('clip', true) ? clipArea : null;\n      };\n\n      ScatterView.prototype._updateSymbolDraw = function (data, seriesModel) {\n        var symbolDraw = this._symbolDraw;\n        var pipelineContext = seriesModel.pipelineContext;\n        var isLargeDraw = pipelineContext.large;\n\n        if (!symbolDraw || isLargeDraw !== this._isLargeDraw) {\n          symbolDraw && symbolDraw.remove();\n          symbolDraw = this._symbolDraw = isLargeDraw ? new LargeSymbolDraw() : new SymbolDraw();\n          this._isLargeDraw = isLargeDraw;\n          this.group.removeAll();\n        }\n\n        this.group.add(symbolDraw.group);\n        return symbolDraw;\n      };\n\n      ScatterView.prototype.remove = function (ecModel, api) {\n        this._symbolDraw && this._symbolDraw.remove(true);\n        this._symbolDraw = null;\n      };\n\n      ScatterView.prototype.dispose = function () {};\n\n      ScatterView.type = 'scatter';\n      return ScatterView;\n    }(ChartView);\n\n    var GridModel =\n    /** @class */\n    function (_super) {\n      __extends(GridModel, _super);\n\n      function GridModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      GridModel.type = 'grid';\n      GridModel.dependencies = ['xAxis', 'yAxis'];\n      GridModel.layoutMode = 'box';\n      GridModel.defaultOption = {\n        show: false,\n        // zlevel: 0,\n        z: 0,\n        left: '10%',\n        top: 60,\n        right: '10%',\n        bottom: 70,\n        // If grid size contain label\n        containLabel: false,\n        // width: {totalWidth} - left - right,\n        // height: {totalHeight} - top - bottom,\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderWidth: 1,\n        borderColor: '#ccc'\n      };\n      return GridModel;\n    }(ComponentModel);\n\n    var CartesianAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisModel, _super);\n\n      function CartesianAxisModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      CartesianAxisModel.prototype.getCoordSysModel = function () {\n        return this.getReferringComponents('grid', SINGLE_REFERRING).models[0];\n      };\n\n      CartesianAxisModel.type = 'cartesian2dAxis';\n      return CartesianAxisModel;\n    }(ComponentModel);\n    mixin(CartesianAxisModel, AxisModelCommonMixin);\n\n    var defaultOption = {\n      show: true,\n      // zlevel: 0,\n      z: 0,\n      // Inverse the axis.\n      inverse: false,\n      // Axis name displayed.\n      name: '',\n      // 'start' | 'middle' | 'end'\n      nameLocation: 'end',\n      // By degree. By default auto rotate by nameLocation.\n      nameRotate: null,\n      nameTruncate: {\n        maxWidth: null,\n        ellipsis: '...',\n        placeholder: '.'\n      },\n      // Use global text style by default.\n      nameTextStyle: {},\n      // The gap between axisName and axisLine.\n      nameGap: 15,\n      // Default `false` to support tooltip.\n      silent: false,\n      // Default `false` to avoid legacy user event listener fail.\n      triggerEvent: false,\n      tooltip: {\n        show: false\n      },\n      axisPointer: {},\n      axisLine: {\n        show: true,\n        onZero: true,\n        onZeroAxisIndex: null,\n        lineStyle: {\n          color: '#6E7079',\n          width: 1,\n          type: 'solid'\n        },\n        // The arrow at both ends the the axis.\n        symbol: ['none', 'none'],\n        symbolSize: [10, 15]\n      },\n      axisTick: {\n        show: true,\n        // Whether axisTick is inside the grid or outside the grid.\n        inside: false,\n        // The length of axisTick.\n        length: 5,\n        lineStyle: {\n          width: 1\n        }\n      },\n      axisLabel: {\n        show: true,\n        // Whether axisLabel is inside the grid or outside the grid.\n        inside: false,\n        rotate: 0,\n        // true | false | null/undefined (auto)\n        showMinLabel: null,\n        // true | false | null/undefined (auto)\n        showMaxLabel: null,\n        margin: 8,\n        // formatter: null,\n        fontSize: 12\n      },\n      splitLine: {\n        show: true,\n        lineStyle: {\n          color: ['#E0E6F1'],\n          width: 1,\n          type: 'solid'\n        }\n      },\n      splitArea: {\n        show: false,\n        areaStyle: {\n          color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)']\n        }\n      }\n    };\n    var categoryAxis = merge({\n      // The gap at both ends of the axis. For categoryAxis, boolean.\n      boundaryGap: true,\n      // Set false to faster category collection.\n      deduplication: null,\n      // splitArea: {\n      // show: false\n      // },\n      splitLine: {\n        show: false\n      },\n      axisTick: {\n        // If tick is align with label when boundaryGap is true\n        alignWithLabel: false,\n        interval: 'auto'\n      },\n      axisLabel: {\n        interval: 'auto'\n      }\n    }, defaultOption);\n    var valueAxis = merge({\n      boundaryGap: [0, 0],\n      axisLine: {\n        // Not shown when other axis is categoryAxis in cartesian\n        show: 'auto'\n      },\n      axisTick: {\n        // Not shown when other axis is categoryAxis in cartesian\n        show: 'auto'\n      },\n      // TODO\n      // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]\n      splitNumber: 5,\n      minorTick: {\n        // Minor tick, not available for cateogry axis.\n        show: false,\n        // Split number of minor ticks. The value should be in range of (0, 100)\n        splitNumber: 5,\n        // Length of minor tick\n        length: 3,\n        // Line style\n        lineStyle: {// Default to be same with axisTick\n        }\n      },\n      minorSplitLine: {\n        show: false,\n        lineStyle: {\n          color: '#F4F7FD',\n          width: 1\n        }\n      }\n    }, defaultOption);\n    var timeAxis = merge({\n      splitNumber: 6,\n      axisLabel: {\n        // To eliminate labels that are not nice\n        showMinLabel: false,\n        showMaxLabel: false,\n        rich: {\n          primary: {\n            fontWeight: 'bold'\n          }\n        }\n      },\n      splitLine: {\n        show: false\n      }\n    }, valueAxis);\n    var logAxis = defaults({\n      logBase: 10\n    }, valueAxis);\n    var axisDefault = {\n      category: categoryAxis,\n      value: valueAxis,\n      time: timeAxis,\n      log: logAxis\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var AXIS_TYPES = {\n      value: 1,\n      category: 1,\n      time: 1,\n      log: 1\n    };\n\n    /**\n     * Generate sub axis model class\n     * @param axisName 'x' 'y' 'radius' 'angle' 'parallel' ...\n     */\n\n    function axisModelCreator(registers, axisName, BaseAxisModelClass, extraDefaultOption) {\n      each(AXIS_TYPES, function (v, axisType) {\n        var defaultOption = merge(merge({}, axisDefault[axisType], true), extraDefaultOption, true);\n\n        var AxisModel =\n        /** @class */\n        function (_super) {\n          __extends(AxisModel, _super);\n\n          function AxisModel() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n\n            _this.type = axisName + 'Axis.' + axisType;\n            return _this;\n          }\n\n          AxisModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n            var layoutMode = fetchLayoutMode(this);\n            var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n            var themeModel = ecModel.getTheme();\n            merge(option, themeModel.get(axisType + 'Axis'));\n            merge(option, this.getDefaultOption());\n            option.type = getAxisType(option);\n\n            if (layoutMode) {\n              mergeLayoutParam(option, inputPositionParams, layoutMode);\n            }\n          };\n\n          AxisModel.prototype.optionUpdated = function () {\n            var thisOption = this.option;\n\n            if (thisOption.type === 'category') {\n              this.__ordinalMeta = OrdinalMeta.createByAxisModel(this);\n            }\n          };\n          /**\n           * Should not be called before all of 'getInitailData' finished.\n           * Because categories are collected during initializing data.\n           */\n\n\n          AxisModel.prototype.getCategories = function (rawData) {\n            var option = this.option; // FIXME\n            // warning if called before all of 'getInitailData' finished.\n\n            if (option.type === 'category') {\n              if (rawData) {\n                return option.data;\n              }\n\n              return this.__ordinalMeta.categories;\n            }\n          };\n\n          AxisModel.prototype.getOrdinalMeta = function () {\n            return this.__ordinalMeta;\n          };\n\n          AxisModel.type = axisName + 'Axis.' + axisType;\n          AxisModel.defaultOption = defaultOption;\n          return AxisModel;\n        }(BaseAxisModelClass);\n\n        registers.registerComponentModel(AxisModel);\n      });\n      registers.registerSubTypeDefaulter(axisName + 'Axis', getAxisType);\n    }\n\n    function getAxisType(option) {\n      // Default axis with data is category axis\n      return option.type || (option.data ? 'category' : 'value');\n    }\n\n    var Cartesian =\n    /** @class */\n    function () {\n      function Cartesian(name) {\n        this.type = 'cartesian';\n        this._dimList = [];\n        this._axes = {};\n        this.name = name || '';\n      }\n\n      Cartesian.prototype.getAxis = function (dim) {\n        return this._axes[dim];\n      };\n\n      Cartesian.prototype.getAxes = function () {\n        return map(this._dimList, function (dim) {\n          return this._axes[dim];\n        }, this);\n      };\n\n      Cartesian.prototype.getAxesByScale = function (scaleType) {\n        scaleType = scaleType.toLowerCase();\n        return filter(this.getAxes(), function (axis) {\n          return axis.scale.type === scaleType;\n        });\n      };\n\n      Cartesian.prototype.addAxis = function (axis) {\n        var dim = axis.dim;\n        this._axes[dim] = axis;\n\n        this._dimList.push(dim);\n      };\n\n      return Cartesian;\n    }();\n\n    var cartesian2DDimensions = ['x', 'y'];\n\n    function canCalculateAffineTransform(scale) {\n      return scale.type === 'interval' || scale.type === 'time';\n    }\n\n    var Cartesian2D =\n    /** @class */\n    function (_super) {\n      __extends(Cartesian2D, _super);\n\n      function Cartesian2D() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'cartesian2d';\n        _this.dimensions = cartesian2DDimensions;\n        return _this;\n      }\n      /**\n       * Calculate an affine transform matrix if two axes are time or value.\n       * It's mainly for accelartion on the large time series data.\n       */\n\n\n      Cartesian2D.prototype.calcAffineTransform = function () {\n        this._transform = this._invTransform = null;\n        var xAxisScale = this.getAxis('x').scale;\n        var yAxisScale = this.getAxis('y').scale;\n\n        if (!canCalculateAffineTransform(xAxisScale) || !canCalculateAffineTransform(yAxisScale)) {\n          return;\n        }\n\n        var xScaleExtent = xAxisScale.getExtent();\n        var yScaleExtent = yAxisScale.getExtent();\n        var start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]]);\n        var end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]]);\n        var xScaleSpan = xScaleExtent[1] - xScaleExtent[0];\n        var yScaleSpan = yScaleExtent[1] - yScaleExtent[0];\n\n        if (!xScaleSpan || !yScaleSpan) {\n          return;\n        } // Accelerate data to point calculation on the special large time series data.\n\n\n        var scaleX = (end[0] - start[0]) / xScaleSpan;\n        var scaleY = (end[1] - start[1]) / yScaleSpan;\n        var translateX = start[0] - xScaleExtent[0] * scaleX;\n        var translateY = start[1] - yScaleExtent[0] * scaleY;\n        var m = this._transform = [scaleX, 0, 0, scaleY, translateX, translateY];\n        this._invTransform = invert([], m);\n      };\n      /**\n       * Base axis will be used on stacking.\n       */\n\n\n      Cartesian2D.prototype.getBaseAxis = function () {\n        return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAxis('x');\n      };\n\n      Cartesian2D.prototype.containPoint = function (point) {\n        var axisX = this.getAxis('x');\n        var axisY = this.getAxis('y');\n        return axisX.contain(axisX.toLocalCoord(point[0])) && axisY.contain(axisY.toLocalCoord(point[1]));\n      };\n\n      Cartesian2D.prototype.containData = function (data) {\n        return this.getAxis('x').containData(data[0]) && this.getAxis('y').containData(data[1]);\n      };\n\n      Cartesian2D.prototype.containZone = function (data1, data2) {\n        var zoneDiag1 = this.dataToPoint(data1);\n        var zoneDiag2 = this.dataToPoint(data2);\n        var area = this.getArea();\n        var zone = new BoundingRect(zoneDiag1[0], zoneDiag1[1], zoneDiag2[0] - zoneDiag1[0], zoneDiag2[1] - zoneDiag1[1]);\n        return area.intersect(zone);\n      };\n\n      Cartesian2D.prototype.dataToPoint = function (data, clamp, out) {\n        out = out || [];\n        var xVal = data[0];\n        var yVal = data[1]; // Fast path\n\n        if (this._transform // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated.\n        && xVal != null && isFinite(xVal) && yVal != null && isFinite(yVal)) {\n          return applyTransform(out, data, this._transform);\n        }\n\n        var xAxis = this.getAxis('x');\n        var yAxis = this.getAxis('y');\n        out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal, clamp));\n        out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal, clamp));\n        return out;\n      };\n\n      Cartesian2D.prototype.clampData = function (data, out) {\n        var xScale = this.getAxis('x').scale;\n        var yScale = this.getAxis('y').scale;\n        var xAxisExtent = xScale.getExtent();\n        var yAxisExtent = yScale.getExtent();\n        var x = xScale.parse(data[0]);\n        var y = yScale.parse(data[1]);\n        out = out || [];\n        out[0] = Math.min(Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), Math.max(xAxisExtent[0], xAxisExtent[1]));\n        out[1] = Math.min(Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), Math.max(yAxisExtent[0], yAxisExtent[1]));\n        return out;\n      };\n\n      Cartesian2D.prototype.pointToData = function (point, clamp) {\n        var out = [];\n\n        if (this._invTransform) {\n          return applyTransform(out, point, this._invTransform);\n        }\n\n        var xAxis = this.getAxis('x');\n        var yAxis = this.getAxis('y');\n        out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp);\n        out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp);\n        return out;\n      };\n\n      Cartesian2D.prototype.getOtherAxis = function (axis) {\n        return this.getAxis(axis.dim === 'x' ? 'y' : 'x');\n      };\n      /**\n       * Get rect area of cartesian.\n       * Area will have a contain function to determine if a point is in the coordinate system.\n       */\n\n\n      Cartesian2D.prototype.getArea = function () {\n        var xExtent = this.getAxis('x').getGlobalExtent();\n        var yExtent = this.getAxis('y').getGlobalExtent();\n        var x = Math.min(xExtent[0], xExtent[1]);\n        var y = Math.min(yExtent[0], yExtent[1]);\n        var width = Math.max(xExtent[0], xExtent[1]) - x;\n        var height = Math.max(yExtent[0], yExtent[1]) - y;\n        return new BoundingRect(x, y, width, height);\n      };\n\n      return Cartesian2D;\n    }(Cartesian);\n\n    var Axis2D =\n    /** @class */\n    function (_super) {\n      __extends(Axis2D, _super);\n\n      function Axis2D(dim, scale, coordExtent, axisType, position) {\n        var _this = _super.call(this, dim, scale, coordExtent) || this;\n        /**\n         * Index of axis, can be used as key\n         * Injected outside.\n         */\n\n\n        _this.index = 0;\n        _this.type = axisType || 'value';\n        _this.position = position || 'bottom';\n        return _this;\n      }\n\n      Axis2D.prototype.isHorizontal = function () {\n        var position = this.position;\n        return position === 'top' || position === 'bottom';\n      };\n      /**\n       * Each item cooresponds to this.getExtent(), which\n       * means globalExtent[0] may greater than globalExtent[1],\n       * unless `asc` is input.\n       *\n       * @param {boolean} [asc]\n       * @return {Array.<number>}\n       */\n\n\n      Axis2D.prototype.getGlobalExtent = function (asc) {\n        var ret = this.getExtent();\n        ret[0] = this.toGlobalCoord(ret[0]);\n        ret[1] = this.toGlobalCoord(ret[1]);\n        asc && ret[0] > ret[1] && ret.reverse();\n        return ret;\n      };\n\n      Axis2D.prototype.pointToData = function (point, clamp) {\n        return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);\n      };\n      /**\n       * Set ordinalSortInfo\n       * @param info new OrdinalSortInfo\n       */\n\n\n      Axis2D.prototype.setCategorySortInfo = function (info) {\n        if (this.type !== 'category') {\n          return false;\n        }\n\n        this.model.option.categorySortInfo = info;\n        this.scale.setSortInfo(info);\n      };\n\n      return Axis2D;\n    }(Axis);\n\n    /**\n     * Can only be called after coordinate system creation stage.\n     * (Can be called before coordinate system update stage).\n     */\n\n    function layout$1(gridModel, axisModel, opt) {\n      opt = opt || {};\n      var grid = gridModel.coordinateSystem;\n      var axis = axisModel.axis;\n      var layout = {};\n      var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0];\n      var rawAxisPosition = axis.position;\n      var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition;\n      var axisDim = axis.dim;\n      var rect = grid.getRect();\n      var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];\n      var idx = {\n        left: 0,\n        right: 1,\n        top: 0,\n        bottom: 1,\n        onZero: 2\n      };\n      var axisOffset = axisModel.get('offset') || 0;\n      var posBound = axisDim === 'x' ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];\n\n      if (otherAxisOnZeroOf) {\n        var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0));\n        posBound[idx.onZero] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);\n      } // Axis position\n\n\n      layout.position = [axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]]; // Axis rotation\n\n      layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); // Tick and label direction, x y is axisDim\n\n      var dirMap = {\n        top: -1,\n        bottom: 1,\n        left: -1,\n        right: 1\n      };\n      layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];\n      layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0;\n\n      if (axisModel.get(['axisTick', 'inside'])) {\n        layout.tickDirection = -layout.tickDirection;\n      }\n\n      if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) {\n        layout.labelDirection = -layout.labelDirection;\n      } // Special label rotation\n\n\n      var labelRotate = axisModel.get(['axisLabel', 'rotate']);\n      layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; // Over splitLine and splitArea\n\n      layout.z2 = 1;\n      return layout;\n    }\n    function isCartesian2DSeries(seriesModel) {\n      return seriesModel.get('coordinateSystem') === 'cartesian2d';\n    }\n    function findAxisModels(seriesModel) {\n      var axisModelMap = {\n        xAxisModel: null,\n        yAxisModel: null\n      };\n      each(axisModelMap, function (v, key) {\n        var axisType = key.replace(/Model$/, '');\n        var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!axisModel) {\n            throw new Error(axisType + ' \"' + retrieve3(seriesModel.get(axisType + 'Index'), seriesModel.get(axisType + 'Id'), 0) + '\" not found');\n          }\n        }\n\n        axisModelMap[key] = axisModel;\n      });\n      return axisModelMap;\n    }\n\n    var mathLog$1 = Math.log;\n    function alignScaleTicks(scale, axisModel, alignToScale) {\n      var intervalScaleProto = IntervalScale.prototype; // NOTE: There is a precondition for log scale  here:\n      // In log scale we store _interval and _extent of exponent value.\n      // So if we use the method of InternalScale to set/get these data.\n      // It process the exponent value, which is linear and what we want here.\n\n      var alignToTicks = intervalScaleProto.getTicks.call(alignToScale);\n      var alignToNicedTicks = intervalScaleProto.getTicks.call(alignToScale, true);\n      var alignToSplitNumber = alignToTicks.length - 1;\n      var alignToInterval = intervalScaleProto.getInterval.call(alignToScale);\n      var scaleExtent = getScaleExtent(scale, axisModel);\n      var rawExtent = scaleExtent.extent;\n      var isMinFixed = scaleExtent.fixMin;\n      var isMaxFixed = scaleExtent.fixMax;\n\n      if (scale.type === 'log') {\n        var logBase = mathLog$1(scale.base);\n        rawExtent = [mathLog$1(rawExtent[0]) / logBase, mathLog$1(rawExtent[1]) / logBase];\n      }\n\n      scale.setExtent(rawExtent[0], rawExtent[1]);\n      scale.calcNiceExtent({\n        splitNumber: alignToSplitNumber,\n        fixMin: isMinFixed,\n        fixMax: isMaxFixed\n      });\n      var extent = intervalScaleProto.getExtent.call(scale); // Need to update the rawExtent.\n      // Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax'\n\n      if (isMinFixed) {\n        rawExtent[0] = extent[0];\n      }\n\n      if (isMaxFixed) {\n        rawExtent[1] = extent[1];\n      }\n\n      var interval = intervalScaleProto.getInterval.call(scale);\n      var min = rawExtent[0];\n      var max = rawExtent[1];\n\n      if (isMinFixed && isMaxFixed) {\n        // User set min, max, divide to get new interval\n        interval = (max - min) / alignToSplitNumber;\n      } else if (isMinFixed) {\n        max = rawExtent[0] + interval * alignToSplitNumber; // User set min, expand extent on the other side\n\n        while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])) {\n          interval = increaseInterval(interval);\n          max = rawExtent[0] + interval * alignToSplitNumber;\n        }\n      } else if (isMaxFixed) {\n        // User set max, expand extent on the other side\n        min = rawExtent[1] - interval * alignToSplitNumber;\n\n        while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])) {\n          interval = increaseInterval(interval);\n          min = rawExtent[1] - interval * alignToSplitNumber;\n        }\n      } else {\n        var nicedSplitNumber = scale.getTicks().length - 1;\n\n        if (nicedSplitNumber > alignToSplitNumber) {\n          interval = increaseInterval(interval);\n        }\n\n        var range = interval * alignToSplitNumber;\n        max = Math.ceil(rawExtent[1] / interval) * interval;\n        min = round(max - range); // Not change the result that crossing zero.\n\n        if (min < 0 && rawExtent[0] >= 0) {\n          min = 0;\n          max = round(range);\n        } else if (max > 0 && rawExtent[1] <= 0) {\n          max = 0;\n          min = -round(range);\n        }\n      } // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale\n\n\n      var t0 = (alignToTicks[0].value - alignToNicedTicks[0].value) / alignToInterval;\n      var t1 = (alignToTicks[alignToSplitNumber].value - alignToNicedTicks[alignToSplitNumber].value) / alignToInterval; // NOTE: Must in setExtent -> setInterval -> setNiceExtent order.\n\n      intervalScaleProto.setExtent.call(scale, min + interval * t0, max + interval * t1);\n      intervalScaleProto.setInterval.call(scale, interval);\n\n      if (t0 || t1) {\n        intervalScaleProto.setNiceExtent.call(scale, min + interval, max - interval);\n      }\n\n      if (\"development\" !== 'production') {\n        var ticks = intervalScaleProto.getTicks.call(scale);\n\n        if (ticks[1] && (!isValueNice(interval) || getPrecisionSafe(ticks[1].value) > getPrecisionSafe(interval))) {\n          warn( // eslint-disable-next-line\n          \"The ticks may be not readable when set min: \" + axisModel.get('min') + \", max: \" + axisModel.get('max') + \" and alignTicks: true\");\n        }\n      }\n    }\n\n    var Grid =\n    /** @class */\n    function () {\n      function Grid(gridModel, ecModel, api) {\n        // FIXME:TS where used (different from registered type 'cartesian2d')?\n        this.type = 'grid';\n        this._coordsMap = {};\n        this._coordsList = [];\n        this._axesMap = {};\n        this._axesList = [];\n        this.axisPointerEnabled = true;\n        this.dimensions = cartesian2DDimensions;\n\n        this._initCartesian(gridModel, ecModel, api);\n\n        this.model = gridModel;\n      }\n\n      Grid.prototype.getRect = function () {\n        return this._rect;\n      };\n\n      Grid.prototype.update = function (ecModel, api) {\n        var axesMap = this._axesMap;\n\n        this._updateScale(ecModel, this.model);\n\n        function updateAxisTicks(axes) {\n          var alignTo; // Axis is added in order of axisIndex.\n\n          var axesIndices = keys(axes);\n          var len = axesIndices.length;\n\n          if (!len) {\n            return;\n          }\n\n          var axisNeedsAlign = []; // Process once and calculate the ticks for those don't use alignTicks.\n\n          for (var i = len - 1; i >= 0; i--) {\n            var idx = +axesIndices[i]; // Convert to number.\n\n            var axis = axes[idx];\n            var model = axis.model;\n            var scale = axis.scale;\n\n            if ( // Only value and log axis without interval support alignTicks.\n            isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) {\n              axisNeedsAlign.push(axis);\n            } else {\n              niceScaleExtent(scale, model);\n\n              if (isIntervalOrLogScale(scale)) {\n                // Can only align to interval or log axis.\n                alignTo = axis;\n              }\n            }\n          }\n          // PENDING. Should we find the axis that both set interval, min, max and align to this one?\n\n          if (axisNeedsAlign.length) {\n            if (!alignTo) {\n              alignTo = axisNeedsAlign.pop();\n              niceScaleExtent(alignTo.scale, alignTo.model);\n            }\n\n            each(axisNeedsAlign, function (axis) {\n              alignScaleTicks(axis.scale, axis.model, alignTo.scale);\n            });\n          }\n        }\n\n        updateAxisTicks(axesMap.x);\n        updateAxisTicks(axesMap.y); // Key: axisDim_axisIndex, value: boolean, whether onZero target.\n\n        var onZeroRecords = {};\n        each(axesMap.x, function (xAxis) {\n          fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords);\n        });\n        each(axesMap.y, function (yAxis) {\n          fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords);\n        }); // Resize again if containLabel is enabled\n        // FIXME It may cause getting wrong grid size in data processing stage\n\n        this.resize(this.model, api);\n      };\n      /**\n       * Resize the grid\n       */\n\n\n      Grid.prototype.resize = function (gridModel, api, ignoreContainLabel) {\n        var boxLayoutParams = gridModel.getBoxLayoutParams();\n        var isContainLabel = !ignoreContainLabel && gridModel.get('containLabel');\n        var gridRect = getLayoutRect(boxLayoutParams, {\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n        this._rect = gridRect;\n        var axesList = this._axesList;\n        adjustAxes(); // Minus label size\n\n        if (isContainLabel) {\n          each(axesList, function (axis) {\n            if (!axis.model.get(['axisLabel', 'inside'])) {\n              var labelUnionRect = estimateLabelUnionRect(axis);\n\n              if (labelUnionRect) {\n                var dim = axis.isHorizontal() ? 'height' : 'width';\n                var margin = axis.model.get(['axisLabel', 'margin']);\n                gridRect[dim] -= labelUnionRect[dim] + margin;\n\n                if (axis.position === 'top') {\n                  gridRect.y += labelUnionRect.height + margin;\n                } else if (axis.position === 'left') {\n                  gridRect.x += labelUnionRect.width + margin;\n                }\n              }\n            }\n          });\n          adjustAxes();\n        }\n\n        each(this._coordsList, function (coord) {\n          // Calculate affine matrix to accelerate the data to point transform.\n          // If all the axes scales are time or value.\n          coord.calcAffineTransform();\n        });\n\n        function adjustAxes() {\n          each(axesList, function (axis) {\n            var isHorizontal = axis.isHorizontal();\n            var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];\n            var idx = axis.inverse ? 1 : 0;\n            axis.setExtent(extent[idx], extent[1 - idx]);\n            updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);\n          });\n        }\n      };\n\n      Grid.prototype.getAxis = function (dim, axisIndex) {\n        var axesMapOnDim = this._axesMap[dim];\n\n        if (axesMapOnDim != null) {\n          return axesMapOnDim[axisIndex || 0];\n        }\n      };\n\n      Grid.prototype.getAxes = function () {\n        return this._axesList.slice();\n      };\n\n      Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) {\n        if (xAxisIndex != null && yAxisIndex != null) {\n          var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n          return this._coordsMap[key];\n        }\n\n        if (isObject(xAxisIndex)) {\n          yAxisIndex = xAxisIndex.yAxisIndex;\n          xAxisIndex = xAxisIndex.xAxisIndex;\n        }\n\n        for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {\n          if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {\n            return coordList[i];\n          }\n        }\n      };\n\n      Grid.prototype.getCartesians = function () {\n        return this._coordsList.slice();\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.convertToPixel = function (ecModel, finder, value) {\n        var target = this._findConvertTarget(finder);\n\n        return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.convertFromPixel = function (ecModel, finder, value) {\n        var target = this._findConvertTarget(finder);\n\n        return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;\n      };\n\n      Grid.prototype._findConvertTarget = function (finder) {\n        var seriesModel = finder.seriesModel;\n        var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n        var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n        var gridModel = finder.gridModel;\n        var coordsList = this._coordsList;\n        var cartesian;\n        var axis;\n\n        if (seriesModel) {\n          cartesian = seriesModel.coordinateSystem;\n          indexOf(coordsList, cartesian) < 0 && (cartesian = null);\n        } else if (xAxisModel && yAxisModel) {\n          cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n        } else if (xAxisModel) {\n          axis = this.getAxis('x', xAxisModel.componentIndex);\n        } else if (yAxisModel) {\n          axis = this.getAxis('y', yAxisModel.componentIndex);\n        } // Lowest priority.\n        else if (gridModel) {\n            var grid = gridModel.coordinateSystem;\n\n            if (grid === this) {\n              cartesian = this._coordsList[0];\n            }\n          }\n\n        return {\n          cartesian: cartesian,\n          axis: axis\n        };\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.containPoint = function (point) {\n        var coord = this._coordsList[0];\n\n        if (coord) {\n          return coord.containPoint(point);\n        }\n      };\n      /**\n       * Initialize cartesian coordinate systems\n       */\n\n\n      Grid.prototype._initCartesian = function (gridModel, ecModel, api) {\n        var _this = this;\n\n        var grid = this;\n        var axisPositionUsed = {\n          left: false,\n          right: false,\n          top: false,\n          bottom: false\n        };\n        var axesMap = {\n          x: {},\n          y: {}\n        };\n        var axesCount = {\n          x: 0,\n          y: 0\n        }; // Create axis\n\n        ecModel.eachComponent('xAxis', createAxisCreator('x'), this);\n        ecModel.eachComponent('yAxis', createAxisCreator('y'), this);\n\n        if (!axesCount.x || !axesCount.y) {\n          // Roll back when there no either x or y axis\n          this._axesMap = {};\n          this._axesList = [];\n          return;\n        }\n\n        this._axesMap = axesMap; // Create cartesian2d\n\n        each(axesMap.x, function (xAxis, xAxisIndex) {\n          each(axesMap.y, function (yAxis, yAxisIndex) {\n            var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n            var cartesian = new Cartesian2D(key);\n            cartesian.master = _this;\n            cartesian.model = gridModel;\n            _this._coordsMap[key] = cartesian;\n\n            _this._coordsList.push(cartesian);\n\n            cartesian.addAxis(xAxis);\n            cartesian.addAxis(yAxis);\n          });\n        });\n\n        function createAxisCreator(dimName) {\n          return function (axisModel, idx) {\n            if (!isAxisUsedInTheGrid(axisModel, gridModel)) {\n              return;\n            }\n\n            var axisPosition = axisModel.get('position');\n\n            if (dimName === 'x') {\n              // Fix position\n              if (axisPosition !== 'top' && axisPosition !== 'bottom') {\n                // Default bottom of X\n                axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom';\n              }\n            } else {\n              // Fix position\n              if (axisPosition !== 'left' && axisPosition !== 'right') {\n                // Default left of Y\n                axisPosition = axisPositionUsed.left ? 'right' : 'left';\n              }\n            }\n\n            axisPositionUsed[axisPosition] = true;\n            var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);\n            var isCategory = axis.type === 'category';\n            axis.onBand = isCategory && axisModel.get('boundaryGap');\n            axis.inverse = axisModel.get('inverse'); // Inject axis into axisModel\n\n            axisModel.axis = axis; // Inject axisModel into axis\n\n            axis.model = axisModel; // Inject grid info axis\n\n            axis.grid = grid; // Index of axis, can be used as key\n\n            axis.index = idx;\n\n            grid._axesList.push(axis);\n\n            axesMap[dimName][idx] = axis;\n            axesCount[dimName]++;\n          };\n        }\n      };\n      /**\n       * Update cartesian properties from series.\n       */\n\n\n      Grid.prototype._updateScale = function (ecModel, gridModel) {\n        // Reset scale\n        each(this._axesList, function (axis) {\n          axis.scale.setExtent(Infinity, -Infinity);\n\n          if (axis.type === 'category') {\n            var categorySortInfo = axis.model.get('categorySortInfo');\n            axis.scale.setSortInfo(categorySortInfo);\n          }\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          if (isCartesian2DSeries(seriesModel)) {\n            var axesModelMap = findAxisModels(seriesModel);\n            var xAxisModel = axesModelMap.xAxisModel;\n            var yAxisModel = axesModelMap.yAxisModel;\n\n            if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) {\n              return;\n            }\n\n            var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n            var data = seriesModel.getData();\n            var xAxis = cartesian.getAxis('x');\n            var yAxis = cartesian.getAxis('y');\n            unionExtent(data, xAxis);\n            unionExtent(data, yAxis);\n          }\n        }, this);\n\n        function unionExtent(data, axis) {\n          each(getDataDimensionsOnAxis(data, axis.dim), function (dim) {\n            axis.scale.unionExtentFromData(data, dim);\n          });\n        }\n      };\n      /**\n       * @param dim 'x' or 'y' or 'auto' or null/undefined\n       */\n\n\n      Grid.prototype.getTooltipAxes = function (dim) {\n        var baseAxes = [];\n        var otherAxes = [];\n        each(this.getCartesians(), function (cartesian) {\n          var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();\n          var otherAxis = cartesian.getOtherAxis(baseAxis);\n          indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);\n          indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);\n        });\n        return {\n          baseAxes: baseAxes,\n          otherAxes: otherAxes\n        };\n      };\n\n      Grid.create = function (ecModel, api) {\n        var grids = [];\n        ecModel.eachComponent('grid', function (gridModel, idx) {\n          var grid = new Grid(gridModel, ecModel, api);\n          grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize\n          // should be performed in create stage.\n\n          grid.resize(gridModel, api, true);\n          gridModel.coordinateSystem = grid;\n          grids.push(grid);\n        }); // Inject the coordinateSystems into seriesModel\n\n        ecModel.eachSeries(function (seriesModel) {\n          if (!isCartesian2DSeries(seriesModel)) {\n            return;\n          }\n\n          var axesModelMap = findAxisModels(seriesModel);\n          var xAxisModel = axesModelMap.xAxisModel;\n          var yAxisModel = axesModelMap.yAxisModel;\n          var gridModel = xAxisModel.getCoordSysModel();\n\n          if (\"development\" !== 'production') {\n            if (!gridModel) {\n              throw new Error('Grid \"' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '\" not found');\n            }\n\n            if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {\n              throw new Error('xAxis and yAxis must use the same grid');\n            }\n          }\n\n          var grid = gridModel.coordinateSystem;\n          seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n        });\n        return grids;\n      }; // For deciding which dimensions to use when creating list data\n\n\n      Grid.dimensions = cartesian2DDimensions;\n      return Grid;\n    }();\n    /**\n     * Check if the axis is used in the specified grid.\n     */\n\n\n    function isAxisUsedInTheGrid(axisModel, gridModel) {\n      return axisModel.getCoordSysModel() === gridModel;\n    }\n\n    function fixAxisOnZero(axesMap, otherAxisDim, axis, // Key: see `getOnZeroRecordKey`\n    onZeroRecords) {\n      axis.getAxesOnZeroOf = function () {\n        // TODO: onZero of multiple axes.\n        return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : [];\n      }; // onZero can not be enabled in these two situations:\n      // 1. When any other axis is a category axis.\n      // 2. When no axis is cross 0 point.\n\n\n      var otherAxes = axesMap[otherAxisDim];\n      var otherAxisOnZeroOf;\n      var axisModel = axis.model;\n      var onZero = axisModel.get(['axisLine', 'onZero']);\n      var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']);\n\n      if (!onZero) {\n        return;\n      } // If target axis is specified.\n\n\n      if (onZeroAxisIndex != null) {\n        if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {\n          otherAxisOnZeroOf = otherAxes[onZeroAxisIndex];\n        }\n      } else {\n        // Find the first available other axis.\n        for (var idx in otherAxes) {\n          if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx]) // Consider that two Y axes on one value axis,\n          // if both onZero, the two Y axes overlap.\n          && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) {\n            otherAxisOnZeroOf = otherAxes[idx];\n            break;\n          }\n        }\n      }\n\n      if (otherAxisOnZeroOf) {\n        onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true;\n      }\n\n      function getOnZeroRecordKey(axis) {\n        return axis.dim + '_' + axis.index;\n      }\n    }\n\n    function canOnZeroToAxis(axis) {\n      return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);\n    }\n\n    function updateAxisTransform(axis, coordBase) {\n      var axisExtent = axis.getExtent();\n      var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform\n\n      axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {\n        return coord + coordBase;\n      } : function (coord) {\n        return axisExtentSum - coord + coordBase;\n      };\n      axis.toLocalCoord = axis.dim === 'x' ? function (coord) {\n        return coord - coordBase;\n      } : function (coord) {\n        return axisExtentSum - coord + coordBase;\n      };\n    }\n\n    var PI$5 = Math.PI;\n    /**\n     * A final axis is translated and rotated from a \"standard axis\".\n     * So opt.position and opt.rotation is required.\n     *\n     * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],\n     * for example: (0, 0) ------------> (0, 50)\n     *\n     * nameDirection or tickDirection or labelDirection is 1 means tick\n     * or label is below the standard axis, whereas is -1 means above\n     * the standard axis. labelOffset means offset between label and axis,\n     * which is useful when 'onZero', where axisLabel is in the grid and\n     * label in outside grid.\n     *\n     * Tips: like always,\n     * positive rotation represents anticlockwise, and negative rotation\n     * represents clockwise.\n     * The direction of position coordinate is the same as the direction\n     * of screen coordinate.\n     *\n     * Do not need to consider axis 'inverse', which is auto processed by\n     * axis extent.\n     */\n\n    var AxisBuilder =\n    /** @class */\n    function () {\n      function AxisBuilder(axisModel, opt) {\n        this.group = new Group();\n        this.opt = opt;\n        this.axisModel = axisModel; // Default value\n\n        defaults(opt, {\n          labelOffset: 0,\n          nameDirection: 1,\n          tickDirection: 1,\n          labelDirection: 1,\n          silent: true,\n          handleAutoShown: function () {\n            return true;\n          }\n        }); // FIXME Not use a seperate text group?\n\n        var transformGroup = new Group({\n          x: opt.position[0],\n          y: opt.position[1],\n          rotation: opt.rotation\n        }); // this.group.add(transformGroup);\n        // this._transformGroup = transformGroup;\n\n        transformGroup.updateTransform();\n        this._transformGroup = transformGroup;\n      }\n\n      AxisBuilder.prototype.hasBuilder = function (name) {\n        return !!builders[name];\n      };\n\n      AxisBuilder.prototype.add = function (name) {\n        builders[name](this.opt, this.axisModel, this.group, this._transformGroup);\n      };\n\n      AxisBuilder.prototype.getGroup = function () {\n        return this.group;\n      };\n\n      AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {\n        var rotationDiff = remRadian(textRotation - axisRotation);\n        var textAlign;\n        var textVerticalAlign;\n\n        if (isRadianAroundZero(rotationDiff)) {\n          // Label is parallel with axis line.\n          textVerticalAlign = direction > 0 ? 'top' : 'bottom';\n          textAlign = 'center';\n        } else if (isRadianAroundZero(rotationDiff - PI$5)) {\n          // Label is inverse parallel with axis line.\n          textVerticalAlign = direction > 0 ? 'bottom' : 'top';\n          textAlign = 'center';\n        } else {\n          textVerticalAlign = 'middle';\n\n          if (rotationDiff > 0 && rotationDiff < PI$5) {\n            textAlign = direction > 0 ? 'right' : 'left';\n          } else {\n            textAlign = direction > 0 ? 'left' : 'right';\n          }\n        }\n\n        return {\n          rotation: rotationDiff,\n          textAlign: textAlign,\n          textVerticalAlign: textVerticalAlign\n        };\n      };\n\n      AxisBuilder.makeAxisEventDataBase = function (axisModel) {\n        var eventData = {\n          componentType: axisModel.mainType,\n          componentIndex: axisModel.componentIndex\n        };\n        eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;\n        return eventData;\n      };\n\n      AxisBuilder.isLabelSilent = function (axisModel) {\n        var tooltipOpt = axisModel.get('tooltip');\n        return axisModel.get('silent') // Consider mouse cursor, add these restrictions.\n        || !(axisModel.get('triggerEvent') || tooltipOpt && tooltipOpt.show);\n      };\n\n      return AxisBuilder;\n    }();\n    var builders = {\n      axisLine: function (opt, axisModel, group, transformGroup) {\n        var shown = axisModel.get(['axisLine', 'show']);\n\n        if (shown === 'auto' && opt.handleAutoShown) {\n          shown = opt.handleAutoShown('axisLine');\n        }\n\n        if (!shown) {\n          return;\n        }\n\n        var extent = axisModel.axis.getExtent();\n        var matrix = transformGroup.transform;\n        var pt1 = [extent[0], 0];\n        var pt2 = [extent[1], 0];\n        var inverse = pt1[0] > pt2[0];\n\n        if (matrix) {\n          applyTransform(pt1, pt1, matrix);\n          applyTransform(pt2, pt2, matrix);\n        }\n\n        var lineStyle = extend({\n          lineCap: 'round'\n        }, axisModel.getModel(['axisLine', 'lineStyle']).getLineStyle());\n        var line = new Line({\n          shape: {\n            x1: pt1[0],\n            y1: pt1[1],\n            x2: pt2[0],\n            y2: pt2[1]\n          },\n          style: lineStyle,\n          strokeContainThreshold: opt.strokeContainThreshold || 5,\n          silent: true,\n          z2: 1\n        });\n        subPixelOptimizeLine$1(line.shape, line.style.lineWidth);\n        line.anid = 'line';\n        group.add(line);\n        var arrows = axisModel.get(['axisLine', 'symbol']);\n\n        if (arrows != null) {\n          var arrowSize = axisModel.get(['axisLine', 'symbolSize']);\n\n          if (isString(arrows)) {\n            // Use the same arrow for start and end point\n            arrows = [arrows, arrows];\n          }\n\n          if (isString(arrowSize) || isNumber(arrowSize)) {\n            // Use the same size for width and height\n            arrowSize = [arrowSize, arrowSize];\n          }\n\n          var arrowOffset = normalizeSymbolOffset(axisModel.get(['axisLine', 'symbolOffset']) || 0, arrowSize);\n          var symbolWidth_1 = arrowSize[0];\n          var symbolHeight_1 = arrowSize[1];\n          each([{\n            rotate: opt.rotation + Math.PI / 2,\n            offset: arrowOffset[0],\n            r: 0\n          }, {\n            rotate: opt.rotation - Math.PI / 2,\n            offset: arrowOffset[1],\n            r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1]))\n          }], function (point, index) {\n            if (arrows[index] !== 'none' && arrows[index] != null) {\n              var symbol = createSymbol(arrows[index], -symbolWidth_1 / 2, -symbolHeight_1 / 2, symbolWidth_1, symbolHeight_1, lineStyle.stroke, true); // Calculate arrow position with offset\n\n              var r = point.r + point.offset;\n              var pt = inverse ? pt2 : pt1;\n              symbol.attr({\n                rotation: point.rotate,\n                x: pt[0] + r * Math.cos(opt.rotation),\n                y: pt[1] - r * Math.sin(opt.rotation),\n                silent: true,\n                z2: 11\n              });\n              group.add(symbol);\n            }\n          });\n        }\n      },\n      axisTickLabel: function (opt, axisModel, group, transformGroup) {\n        var ticksEls = buildAxisMajorTicks(group, transformGroup, axisModel, opt);\n        var labelEls = buildAxisLabel(group, transformGroup, axisModel, opt);\n        fixMinMaxLabelShow(axisModel, labelEls, ticksEls);\n        buildAxisMinorTicks(group, transformGroup, axisModel, opt.tickDirection); // This bit fixes the label overlap issue for the time chart.\n        // See https://github.com/apache/echarts/issues/14266 for more.\n\n        if (axisModel.get(['axisLabel', 'hideOverlap'])) {\n          var labelList = prepareLayoutList(map(labelEls, function (label) {\n            return {\n              label: label,\n              priority: label.z2,\n              defaultAttr: {\n                ignore: label.ignore\n              }\n            };\n          }));\n          hideOverlap(labelList);\n        }\n      },\n      axisName: function (opt, axisModel, group, transformGroup) {\n        var name = retrieve(opt.axisName, axisModel.get('name'));\n\n        if (!name) {\n          return;\n        }\n\n        var nameLocation = axisModel.get('nameLocation');\n        var nameDirection = opt.nameDirection;\n        var textStyleModel = axisModel.getModel('nameTextStyle');\n        var gap = axisModel.get('nameGap') || 0;\n        var extent = axisModel.axis.getExtent();\n        var gapSignal = extent[0] > extent[1] ? -1 : 1;\n        var pos = [nameLocation === 'start' ? extent[0] - gapSignal * gap : nameLocation === 'end' ? extent[1] + gapSignal * gap : (extent[0] + extent[1]) / 2, // Reuse labelOffset.\n        isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0];\n        var labelLayout;\n        var nameRotation = axisModel.get('nameRotate');\n\n        if (nameRotation != null) {\n          nameRotation = nameRotation * PI$5 / 180; // To radian.\n        }\n\n        var axisNameAvailableWidth;\n\n        if (isNameLocationCenter(nameLocation)) {\n          labelLayout = AxisBuilder.innerTextLayout(opt.rotation, nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.\n          nameDirection);\n        } else {\n          labelLayout = endTextLayout(opt.rotation, nameLocation, nameRotation || 0, extent);\n          axisNameAvailableWidth = opt.axisNameAvailableWidth;\n\n          if (axisNameAvailableWidth != null) {\n            axisNameAvailableWidth = Math.abs(axisNameAvailableWidth / Math.sin(labelLayout.rotation));\n            !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);\n          }\n        }\n\n        var textFont = textStyleModel.getFont();\n        var truncateOpt = axisModel.get('nameTruncate', true) || {};\n        var ellipsis = truncateOpt.ellipsis;\n        var maxWidth = retrieve(opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth);\n        var textEl = new ZRText({\n          x: pos[0],\n          y: pos[1],\n          rotation: labelLayout.rotation,\n          silent: AxisBuilder.isLabelSilent(axisModel),\n          style: createTextStyle(textStyleModel, {\n            text: name,\n            font: textFont,\n            overflow: 'truncate',\n            width: maxWidth,\n            ellipsis: ellipsis,\n            fill: textStyleModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']),\n            align: textStyleModel.get('align') || labelLayout.textAlign,\n            verticalAlign: textStyleModel.get('verticalAlign') || labelLayout.textVerticalAlign\n          }),\n          z2: 1\n        });\n        setTooltipConfig({\n          el: textEl,\n          componentModel: axisModel,\n          itemName: name\n        });\n        textEl.__fullText = name; // Id for animation\n\n        textEl.anid = 'name';\n\n        if (axisModel.get('triggerEvent')) {\n          var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n          eventData.targetType = 'axisName';\n          eventData.name = name;\n          getECData(textEl).eventData = eventData;\n        } // FIXME\n\n\n        transformGroup.add(textEl);\n        textEl.updateTransform();\n        group.add(textEl);\n        textEl.decomposeTransform();\n      }\n    };\n\n    function endTextLayout(rotation, textPosition, textRotate, extent) {\n      var rotationDiff = remRadian(textRotate - rotation);\n      var textAlign;\n      var textVerticalAlign;\n      var inverse = extent[0] > extent[1];\n      var onLeft = textPosition === 'start' && !inverse || textPosition !== 'start' && inverse;\n\n      if (isRadianAroundZero(rotationDiff - PI$5 / 2)) {\n        textVerticalAlign = onLeft ? 'bottom' : 'top';\n        textAlign = 'center';\n      } else if (isRadianAroundZero(rotationDiff - PI$5 * 1.5)) {\n        textVerticalAlign = onLeft ? 'top' : 'bottom';\n        textAlign = 'center';\n      } else {\n        textVerticalAlign = 'middle';\n\n        if (rotationDiff < PI$5 * 1.5 && rotationDiff > PI$5 / 2) {\n          textAlign = onLeft ? 'left' : 'right';\n        } else {\n          textAlign = onLeft ? 'right' : 'left';\n        }\n      }\n\n      return {\n        rotation: rotationDiff,\n        textAlign: textAlign,\n        textVerticalAlign: textVerticalAlign\n      };\n    }\n\n    function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {\n      if (shouldShowAllLabels(axisModel.axis)) {\n        return;\n      } // If min or max are user set, we need to check\n      // If the tick on min(max) are overlap on their neighbour tick\n      // If they are overlapped, we need to hide the min(max) tick label\n\n\n      var showMinLabel = axisModel.get(['axisLabel', 'showMinLabel']);\n      var showMaxLabel = axisModel.get(['axisLabel', 'showMaxLabel']); // FIXME\n      // Have not consider onBand yet, where tick els is more than label els.\n\n      labelEls = labelEls || [];\n      tickEls = tickEls || [];\n      var firstLabel = labelEls[0];\n      var nextLabel = labelEls[1];\n      var lastLabel = labelEls[labelEls.length - 1];\n      var prevLabel = labelEls[labelEls.length - 2];\n      var firstTick = tickEls[0];\n      var nextTick = tickEls[1];\n      var lastTick = tickEls[tickEls.length - 1];\n      var prevTick = tickEls[tickEls.length - 2];\n\n      if (showMinLabel === false) {\n        ignoreEl(firstLabel);\n        ignoreEl(firstTick);\n      } else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {\n        if (showMinLabel) {\n          ignoreEl(nextLabel);\n          ignoreEl(nextTick);\n        } else {\n          ignoreEl(firstLabel);\n          ignoreEl(firstTick);\n        }\n      }\n\n      if (showMaxLabel === false) {\n        ignoreEl(lastLabel);\n        ignoreEl(lastTick);\n      } else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {\n        if (showMaxLabel) {\n          ignoreEl(prevLabel);\n          ignoreEl(prevTick);\n        } else {\n          ignoreEl(lastLabel);\n          ignoreEl(lastTick);\n        }\n      }\n    }\n\n    function ignoreEl(el) {\n      el && (el.ignore = true);\n    }\n\n    function isTwoLabelOverlapped(current, next) {\n      // current and next has the same rotation.\n      var firstRect = current && current.getBoundingRect().clone();\n      var nextRect = next && next.getBoundingRect().clone();\n\n      if (!firstRect || !nextRect) {\n        return;\n      } // When checking intersect of two rotated labels, we use mRotationBack\n      // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.\n\n\n      var mRotationBack = identity([]);\n      rotate(mRotationBack, mRotationBack, -current.rotation);\n      firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));\n      nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));\n      return firstRect.intersect(nextRect);\n    }\n\n    function isNameLocationCenter(nameLocation) {\n      return nameLocation === 'middle' || nameLocation === 'center';\n    }\n\n    function createTicks(ticksCoords, tickTransform, tickEndCoord, tickLineStyle, anidPrefix) {\n      var tickEls = [];\n      var pt1 = [];\n      var pt2 = [];\n\n      for (var i = 0; i < ticksCoords.length; i++) {\n        var tickCoord = ticksCoords[i].coord;\n        pt1[0] = tickCoord;\n        pt1[1] = 0;\n        pt2[0] = tickCoord;\n        pt2[1] = tickEndCoord;\n\n        if (tickTransform) {\n          applyTransform(pt1, pt1, tickTransform);\n          applyTransform(pt2, pt2, tickTransform);\n        } // Tick line, Not use group transform to have better line draw\n\n\n        var tickEl = new Line({\n          shape: {\n            x1: pt1[0],\n            y1: pt1[1],\n            x2: pt2[0],\n            y2: pt2[1]\n          },\n          style: tickLineStyle,\n          z2: 2,\n          autoBatch: true,\n          silent: true\n        });\n        subPixelOptimizeLine$1(tickEl.shape, tickEl.style.lineWidth);\n        tickEl.anid = anidPrefix + '_' + ticksCoords[i].tickValue;\n        tickEls.push(tickEl);\n      }\n\n      return tickEls;\n    }\n\n    function buildAxisMajorTicks(group, transformGroup, axisModel, opt) {\n      var axis = axisModel.axis;\n      var tickModel = axisModel.getModel('axisTick');\n      var shown = tickModel.get('show');\n\n      if (shown === 'auto' && opt.handleAutoShown) {\n        shown = opt.handleAutoShown('axisTick');\n      }\n\n      if (!shown || axis.scale.isBlank()) {\n        return;\n      }\n\n      var lineStyleModel = tickModel.getModel('lineStyle');\n      var tickEndCoord = opt.tickDirection * tickModel.get('length');\n      var ticksCoords = axis.getTicksCoords();\n      var ticksEls = createTicks(ticksCoords, transformGroup.transform, tickEndCoord, defaults(lineStyleModel.getLineStyle(), {\n        stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n      }), 'ticks');\n\n      for (var i = 0; i < ticksEls.length; i++) {\n        group.add(ticksEls[i]);\n      }\n\n      return ticksEls;\n    }\n\n    function buildAxisMinorTicks(group, transformGroup, axisModel, tickDirection) {\n      var axis = axisModel.axis;\n      var minorTickModel = axisModel.getModel('minorTick');\n\n      if (!minorTickModel.get('show') || axis.scale.isBlank()) {\n        return;\n      }\n\n      var minorTicksCoords = axis.getMinorTicksCoords();\n\n      if (!minorTicksCoords.length) {\n        return;\n      }\n\n      var lineStyleModel = minorTickModel.getModel('lineStyle');\n      var tickEndCoord = tickDirection * minorTickModel.get('length');\n      var minorTickLineStyle = defaults(lineStyleModel.getLineStyle(), defaults(axisModel.getModel('axisTick').getLineStyle(), {\n        stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n      }));\n\n      for (var i = 0; i < minorTicksCoords.length; i++) {\n        var minorTicksEls = createTicks(minorTicksCoords[i], transformGroup.transform, tickEndCoord, minorTickLineStyle, 'minorticks_' + i);\n\n        for (var k = 0; k < minorTicksEls.length; k++) {\n          group.add(minorTicksEls[k]);\n        }\n      }\n    }\n\n    function buildAxisLabel(group, transformGroup, axisModel, opt) {\n      var axis = axisModel.axis;\n      var show = retrieve(opt.axisLabelShow, axisModel.get(['axisLabel', 'show']));\n\n      if (!show || axis.scale.isBlank()) {\n        return;\n      }\n\n      var labelModel = axisModel.getModel('axisLabel');\n      var labelMargin = labelModel.get('margin');\n      var labels = axis.getViewLabels(); // Special label rotate.\n\n      var labelRotation = (retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) * PI$5 / 180;\n      var labelLayout = AxisBuilder.innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);\n      var rawCategoryData = axisModel.getCategories && axisModel.getCategories(true);\n      var labelEls = [];\n      var silent = AxisBuilder.isLabelSilent(axisModel);\n      var triggerEvent = axisModel.get('triggerEvent');\n      each(labels, function (labelItem, index) {\n        var tickValue = axis.scale.type === 'ordinal' ? axis.scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue;\n        var formattedLabel = labelItem.formattedLabel;\n        var rawLabel = labelItem.rawLabel;\n        var itemLabelModel = labelModel;\n\n        if (rawCategoryData && rawCategoryData[tickValue]) {\n          var rawCategoryItem = rawCategoryData[tickValue];\n\n          if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) {\n            itemLabelModel = new Model(rawCategoryItem.textStyle, labelModel, axisModel.ecModel);\n          }\n        }\n\n        var textColor = itemLabelModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']);\n        var tickCoord = axis.dataToCoord(tickValue);\n        var textEl = new ZRText({\n          x: tickCoord,\n          y: opt.labelOffset + opt.labelDirection * labelMargin,\n          rotation: labelLayout.rotation,\n          silent: silent,\n          z2: 10 + (labelItem.level || 0),\n          style: createTextStyle(itemLabelModel, {\n            text: formattedLabel,\n            align: itemLabelModel.getShallow('align', true) || labelLayout.textAlign,\n            verticalAlign: itemLabelModel.getShallow('verticalAlign', true) || itemLabelModel.getShallow('baseline', true) || labelLayout.textVerticalAlign,\n            fill: isFunction(textColor) ? textColor( // (1) In category axis with data zoom, tick is not the original\n            // index of axis.data. So tick should not be exposed to user\n            // in category axis.\n            // (2) Compatible with previous version, which always use formatted label as\n            // input. But in interval scale the formatted label is like '223,445', which\n            // maked user repalce ','. So we modify it to return original val but remain\n            // it as 'string' to avoid error in replacing.\n            axis.type === 'category' ? rawLabel : axis.type === 'value' ? tickValue + '' : tickValue, index) : textColor\n          })\n        });\n        textEl.anid = 'label_' + tickValue; // Pack data for mouse event\n\n        if (triggerEvent) {\n          var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n          eventData.targetType = 'axisLabel';\n          eventData.value = rawLabel;\n          eventData.tickIndex = index;\n\n          if (axis.type === 'category') {\n            eventData.dataIndex = tickValue;\n          }\n\n          getECData(textEl).eventData = eventData;\n        } // FIXME\n\n\n        transformGroup.add(textEl);\n        textEl.updateTransform();\n        labelEls.push(textEl);\n        group.add(textEl);\n        textEl.decomposeTransform();\n      });\n      return labelEls;\n    }\n\n    // allAxesInfo should be updated when setOption performed.\n\n    function collect(ecModel, api) {\n      var result = {\n        /**\n         * key: makeKey(axis.model)\n         * value: {\n         *      axis,\n         *      coordSys,\n         *      axisPointerModel,\n         *      triggerTooltip,\n         *      involveSeries,\n         *      snap,\n         *      seriesModels,\n         *      seriesDataCount\n         * }\n         */\n        axesInfo: {},\n        seriesInvolved: false,\n\n        /**\n         * key: makeKey(coordSys.model)\n         * value: Object: key makeKey(axis.model), value: axisInfo\n         */\n        coordSysAxesInfo: {},\n        coordSysMap: {}\n      };\n      collectAxesInfo(result, ecModel, api); // Check seriesInvolved for performance, in case too many series in some chart.\n\n      result.seriesInvolved && collectSeriesInfo(result, ecModel);\n      return result;\n    }\n\n    function collectAxesInfo(result, ecModel, api) {\n      var globalTooltipModel = ecModel.getComponent('tooltip');\n      var globalAxisPointerModel = ecModel.getComponent('axisPointer'); // links can only be set on global.\n\n      var linksOption = globalAxisPointerModel.get('link', true) || [];\n      var linkGroups = []; // Collect axes info.\n\n      each(api.getCoordinateSystems(), function (coordSys) {\n        // Some coordinate system do not support axes, like geo.\n        if (!coordSys.axisPointerEnabled) {\n          return;\n        }\n\n        var coordSysKey = makeKey(coordSys.model);\n        var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};\n        result.coordSysMap[coordSysKey] = coordSys; // Set tooltip (like 'cross') is a convienent way to show axisPointer\n        // for user. So we enable seting tooltip on coordSys model.\n\n        var coordSysModel = coordSys.model;\n        var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);\n        each(coordSys.getAxes(), curry(saveTooltipAxisInfo, false, null)); // If axis tooltip used, choose tooltip axis for each coordSys.\n        // Notice this case: coordSys is `grid` but not `cartesian2D` here.\n\n        if (coordSys.getTooltipAxes && globalTooltipModel // If tooltip.showContent is set as false, tooltip will not\n        // show but axisPointer will show as normal.\n        && baseTooltipModel.get('show')) {\n          // Compatible with previous logic. But series.tooltip.trigger: 'axis'\n          // or series.data[n].tooltip.trigger: 'axis' are not support any more.\n          var triggerAxis = baseTooltipModel.get('trigger') === 'axis';\n          var cross = baseTooltipModel.get(['axisPointer', 'type']) === 'cross';\n          var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get(['axisPointer', 'axis']));\n\n          if (triggerAxis || cross) {\n            each(tooltipAxes.baseAxes, curry(saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis));\n          }\n\n          if (cross) {\n            each(tooltipAxes.otherAxes, curry(saveTooltipAxisInfo, 'cross', false));\n          }\n        } // fromTooltip: true | false | 'cross'\n        // triggerTooltip: true | false | null\n\n\n        function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {\n          var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);\n          var axisPointerShow = axisPointerModel.get('show');\n\n          if (!axisPointerShow || axisPointerShow === 'auto' && !fromTooltip && !isHandleTrigger(axisPointerModel)) {\n            return;\n          }\n\n          if (triggerTooltip == null) {\n            triggerTooltip = axisPointerModel.get('triggerTooltip');\n          }\n\n          axisPointerModel = fromTooltip ? makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) : axisPointerModel;\n          var snap = axisPointerModel.get('snap');\n          var axisKey = makeKey(axis.model);\n          var involveSeries = triggerTooltip || snap || axis.type === 'category'; // If result.axesInfo[key] exist, override it (tooltip has higher priority).\n\n          var axisInfo = result.axesInfo[axisKey] = {\n            key: axisKey,\n            axis: axis,\n            coordSys: coordSys,\n            axisPointerModel: axisPointerModel,\n            triggerTooltip: triggerTooltip,\n            involveSeries: involveSeries,\n            snap: snap,\n            useHandle: isHandleTrigger(axisPointerModel),\n            seriesModels: [],\n            linkGroup: null\n          };\n          axesInfoInCoordSys[axisKey] = axisInfo;\n          result.seriesInvolved = result.seriesInvolved || involveSeries;\n          var groupIndex = getLinkGroupIndex(linksOption, axis);\n\n          if (groupIndex != null) {\n            var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {\n              axesInfo: {}\n            });\n            linkGroup.axesInfo[axisKey] = axisInfo;\n            linkGroup.mapper = linksOption[groupIndex].mapper;\n            axisInfo.linkGroup = linkGroup;\n          }\n        }\n      });\n    }\n\n    function makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) {\n      var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');\n      var fields = ['type', 'snap', 'lineStyle', 'shadowStyle', 'label', 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'];\n      var volatileOption = {};\n      each(fields, function (field) {\n        volatileOption[field] = clone(tooltipAxisPointerModel.get(field));\n      }); // category axis do not auto snap, otherwise some tick that do not\n      // has value can not be hovered. value/time/log axis default snap if\n      // triggered from tooltip and trigger tooltip.\n\n      volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; // Compatibel with previous behavior, tooltip axis do not show label by default.\n      // Only these properties can be overrided from tooltip to axisPointer.\n\n      if (tooltipAxisPointerModel.get('type') === 'cross') {\n        volatileOption.type = 'line';\n      }\n\n      var labelOption = volatileOption.label || (volatileOption.label = {}); // Follow the convention, do not show label when triggered by tooltip by default.\n\n      labelOption.show == null && (labelOption.show = false);\n\n      if (fromTooltip === 'cross') {\n        // When 'cross', both axes show labels.\n        var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get(['label', 'show']);\n        labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true; // If triggerTooltip, this is a base axis, which should better not use cross style\n        // (cross style is dashed by default)\n\n        if (!triggerTooltip) {\n          var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');\n          crossStyle && defaults(labelOption, crossStyle.textStyle);\n        }\n      }\n\n      return axis.model.getModel('axisPointer', new Model(volatileOption, globalAxisPointerModel, ecModel));\n    }\n\n    function collectSeriesInfo(result, ecModel) {\n      // Prepare data for axis trigger\n      ecModel.eachSeries(function (seriesModel) {\n        // Notice this case: this coordSys is `cartesian2D` but not `grid`.\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesTooltipTrigger = seriesModel.get(['tooltip', 'trigger'], true);\n        var seriesTooltipShow = seriesModel.get(['tooltip', 'show'], true);\n\n        if (!coordSys || seriesTooltipTrigger === 'none' || seriesTooltipTrigger === false || seriesTooltipTrigger === 'item' || seriesTooltipShow === false || seriesModel.get(['axisPointer', 'show'], true) === false) {\n          return;\n        }\n\n        each(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {\n          var axis = axisInfo.axis;\n\n          if (coordSys.getAxis(axis.dim) === axis) {\n            axisInfo.seriesModels.push(seriesModel);\n            axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);\n            axisInfo.seriesDataCount += seriesModel.getData().count();\n          }\n        });\n      });\n    }\n    /**\n     * For example:\n     * {\n     *     axisPointer: {\n     *         links: [{\n     *             xAxisIndex: [2, 4],\n     *             yAxisIndex: 'all'\n     *         }, {\n     *             xAxisId: ['a5', 'a7'],\n     *             xAxisName: 'xxx'\n     *         }]\n     *     }\n     * }\n     */\n\n\n    function getLinkGroupIndex(linksOption, axis) {\n      var axisModel = axis.model;\n      var dim = axis.dim;\n\n      for (var i = 0; i < linksOption.length; i++) {\n        var linkOption = linksOption[i] || {};\n\n        if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)) {\n          return i;\n        }\n      }\n    }\n\n    function checkPropInLink(linkPropValue, axisPropValue) {\n      return linkPropValue === 'all' || isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0 || linkPropValue === axisPropValue;\n    }\n\n    function fixValue(axisModel) {\n      var axisInfo = getAxisInfo(axisModel);\n\n      if (!axisInfo) {\n        return;\n      }\n\n      var axisPointerModel = axisInfo.axisPointerModel;\n      var scale = axisInfo.axis.scale;\n      var option = axisPointerModel.option;\n      var status = axisPointerModel.get('status');\n      var value = axisPointerModel.get('value'); // Parse init value for category and time axis.\n\n      if (value != null) {\n        value = scale.parse(value);\n      }\n\n      var useHandle = isHandleTrigger(axisPointerModel); // If `handle` used, `axisPointer` will always be displayed, so value\n      // and status should be initialized.\n\n      if (status == null) {\n        option.status = useHandle ? 'show' : 'hide';\n      }\n\n      var extent = scale.getExtent().slice();\n      extent[0] > extent[1] && extent.reverse();\n\n      if ( // Pick a value on axis when initializing.\n      value == null // If both `handle` and `dataZoom` are used, value may be out of axis extent,\n      // where we should re-pick a value to keep `handle` displaying normally.\n      || value > extent[1]) {\n        // Make handle displayed on the end of the axis when init, which looks better.\n        value = extent[1];\n      }\n\n      if (value < extent[0]) {\n        value = extent[0];\n      }\n\n      option.value = value;\n\n      if (useHandle) {\n        option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';\n      }\n    }\n    function getAxisInfo(axisModel) {\n      var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;\n      return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];\n    }\n    function getAxisPointerModel(axisModel) {\n      var axisInfo = getAxisInfo(axisModel);\n      return axisInfo && axisInfo.axisPointerModel;\n    }\n\n    function isHandleTrigger(axisPointerModel) {\n      return !!axisPointerModel.get(['handle', 'show']);\n    }\n    /**\n     * @param {module:echarts/model/Model} model\n     * @return {string} unique key\n     */\n\n\n    function makeKey(model) {\n      return model.type + '||' + model.id;\n    }\n\n    var axisPointerClazz = {};\n    /**\n     * Base class of AxisView.\n     */\n\n    var AxisView =\n    /** @class */\n    function (_super) {\n      __extends(AxisView, _super);\n\n      function AxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AxisView.type;\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        // FIXME\n        // This process should proformed after coordinate systems updated\n        // (axis scale updated), and should be performed each time update.\n        // So put it here temporarily, although it is not appropriate to\n        // put a model-writing procedure in `view`.\n        this.axisPointerClass && fixValue(axisModel);\n\n        _super.prototype.render.apply(this, arguments);\n\n        this._doUpdateAxisPointerClass(axisModel, api, true);\n      };\n      /**\n       * Action handler.\n       */\n\n\n      AxisView.prototype.updateAxisPointer = function (axisModel, ecModel, api, payload) {\n        this._doUpdateAxisPointerClass(axisModel, api, false);\n      };\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.remove = function (ecModel, api) {\n        var axisPointer = this._axisPointer;\n        axisPointer && axisPointer.remove(api);\n      };\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.dispose = function (ecModel, api) {\n        this._disposeAxisPointer(api);\n\n        _super.prototype.dispose.apply(this, arguments);\n      };\n\n      AxisView.prototype._doUpdateAxisPointerClass = function (axisModel, api, forceRender) {\n        var Clazz = AxisView.getAxisPointerClass(this.axisPointerClass);\n\n        if (!Clazz) {\n          return;\n        }\n\n        var axisPointerModel = getAxisPointerModel(axisModel);\n        axisPointerModel ? (this._axisPointer || (this._axisPointer = new Clazz())).render(axisModel, axisPointerModel, api, forceRender) : this._disposeAxisPointer(api);\n      };\n\n      AxisView.prototype._disposeAxisPointer = function (api) {\n        this._axisPointer && this._axisPointer.dispose(api);\n        this._axisPointer = null;\n      };\n\n      AxisView.registerAxisPointerClass = function (type, clazz) {\n        if (\"development\" !== 'production') {\n          if (axisPointerClazz[type]) {\n            throw new Error('axisPointer ' + type + ' exists');\n          }\n        }\n\n        axisPointerClazz[type] = clazz;\n      };\n\n      AxisView.getAxisPointerClass = function (type) {\n        return type && axisPointerClazz[type];\n      };\n      AxisView.type = 'axis';\n      return AxisView;\n    }(ComponentView);\n\n    var inner$6 = makeInner();\n    function rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel) {\n      var axis = axisModel.axis;\n\n      if (axis.scale.isBlank()) {\n        return;\n      } // TODO: TYPE\n\n\n      var splitAreaModel = axisModel.getModel('splitArea');\n      var areaStyleModel = splitAreaModel.getModel('areaStyle');\n      var areaColors = areaStyleModel.get('color');\n      var gridRect = gridModel.coordinateSystem.getRect();\n      var ticksCoords = axis.getTicksCoords({\n        tickModel: splitAreaModel,\n        clamp: true\n      });\n\n      if (!ticksCoords.length) {\n        return;\n      } // For Making appropriate splitArea animation, the color and anid\n      // should be corresponding to previous one if possible.\n\n\n      var areaColorsLen = areaColors.length;\n      var lastSplitAreaColors = inner$6(axisView).splitAreaColors;\n      var newSplitAreaColors = createHashMap();\n      var colorIndex = 0;\n\n      if (lastSplitAreaColors) {\n        for (var i = 0; i < ticksCoords.length; i++) {\n          var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue);\n\n          if (cIndex != null) {\n            colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen;\n            break;\n          }\n        }\n      }\n\n      var prev = axis.toGlobalCoord(ticksCoords[0].coord);\n      var areaStyle = areaStyleModel.getAreaStyle();\n      areaColors = isArray(areaColors) ? areaColors : [areaColors];\n\n      for (var i = 1; i < ticksCoords.length; i++) {\n        var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n        var x = void 0;\n        var y = void 0;\n        var width = void 0;\n        var height = void 0;\n\n        if (axis.isHorizontal()) {\n          x = prev;\n          y = gridRect.y;\n          width = tickCoord - x;\n          height = gridRect.height;\n          prev = x + width;\n        } else {\n          x = gridRect.x;\n          y = prev;\n          width = gridRect.width;\n          height = tickCoord - y;\n          prev = y + height;\n        }\n\n        var tickValue = ticksCoords[i - 1].tickValue;\n        tickValue != null && newSplitAreaColors.set(tickValue, colorIndex);\n        axisGroup.add(new Rect({\n          anid: tickValue != null ? 'area_' + tickValue : null,\n          shape: {\n            x: x,\n            y: y,\n            width: width,\n            height: height\n          },\n          style: defaults({\n            fill: areaColors[colorIndex]\n          }, areaStyle),\n          autoBatch: true,\n          silent: true\n        }));\n        colorIndex = (colorIndex + 1) % areaColorsLen;\n      }\n\n      inner$6(axisView).splitAreaColors = newSplitAreaColors;\n    }\n    function rectCoordAxisHandleRemove(axisView) {\n      inner$6(axisView).splitAreaColors = null;\n    }\n\n    var axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName'];\n    var selfBuilderAttrs = ['splitArea', 'splitLine', 'minorSplitLine'];\n\n    var CartesianAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisView, _super);\n\n      function CartesianAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianAxisView.type;\n        _this.axisPointerClass = 'CartesianAxisPointer';\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      CartesianAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        this.group.removeAll();\n        var oldAxisGroup = this._axisGroup;\n        this._axisGroup = new Group();\n        this.group.add(this._axisGroup);\n\n        if (!axisModel.get('show')) {\n          return;\n        }\n\n        var gridModel = axisModel.getCoordSysModel();\n        var layout = layout$1(gridModel, axisModel);\n        var axisBuilder = new AxisBuilder(axisModel, extend({\n          handleAutoShown: function (elementType) {\n            var cartesians = gridModel.coordinateSystem.getCartesians();\n\n            for (var i = 0; i < cartesians.length; i++) {\n              if (isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) {\n                // Still show axis tick or axisLine if other axis is value / log\n                return true;\n              }\n            } // Not show axisTick or axisLine if other axis is category / time\n\n\n            return false;\n          }\n        }, layout));\n        each(axisBuilderAttrs, axisBuilder.add, axisBuilder);\n\n        this._axisGroup.add(axisBuilder.getGroup());\n\n        each(selfBuilderAttrs, function (name) {\n          if (axisModel.get([name, 'show'])) {\n            axisElementBuilders[name](this, this._axisGroup, axisModel, gridModel);\n          }\n        }, this); // THIS is a special case for bar racing chart.\n        // Update the axis label from the natural initial layout to\n        // sorted layout should has no animation.\n\n        var isInitialSortFromBarRacing = payload && payload.type === 'changeAxisOrder' && payload.isInitSort;\n\n        if (!isInitialSortFromBarRacing) {\n          groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n        }\n\n        _super.prototype.render.call(this, axisModel, ecModel, api, payload);\n      };\n\n      CartesianAxisView.prototype.remove = function () {\n        rectCoordAxisHandleRemove(this);\n      };\n\n      CartesianAxisView.type = 'cartesianAxis';\n      return CartesianAxisView;\n    }(AxisView);\n\n    var axisElementBuilders = {\n      splitLine: function (axisView, axisGroup, axisModel, gridModel) {\n        var axis = axisModel.axis;\n\n        if (axis.scale.isBlank()) {\n          return;\n        }\n\n        var splitLineModel = axisModel.getModel('splitLine');\n        var lineStyleModel = splitLineModel.getModel('lineStyle');\n        var lineColors = lineStyleModel.get('color');\n        lineColors = isArray(lineColors) ? lineColors : [lineColors];\n        var gridRect = gridModel.coordinateSystem.getRect();\n        var isHorizontal = axis.isHorizontal();\n        var lineCount = 0;\n        var ticksCoords = axis.getTicksCoords({\n          tickModel: splitLineModel\n        });\n        var p1 = [];\n        var p2 = [];\n        var lineStyle = lineStyleModel.getLineStyle();\n\n        for (var i = 0; i < ticksCoords.length; i++) {\n          var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n\n          if (isHorizontal) {\n            p1[0] = tickCoord;\n            p1[1] = gridRect.y;\n            p2[0] = tickCoord;\n            p2[1] = gridRect.y + gridRect.height;\n          } else {\n            p1[0] = gridRect.x;\n            p1[1] = tickCoord;\n            p2[0] = gridRect.x + gridRect.width;\n            p2[1] = tickCoord;\n          }\n\n          var colorIndex = lineCount++ % lineColors.length;\n          var tickValue = ticksCoords[i].tickValue;\n          var line = new Line({\n            anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null,\n            autoBatch: true,\n            shape: {\n              x1: p1[0],\n              y1: p1[1],\n              x2: p2[0],\n              y2: p2[1]\n            },\n            style: defaults({\n              stroke: lineColors[colorIndex]\n            }, lineStyle),\n            silent: true\n          });\n          subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n          axisGroup.add(line);\n        }\n      },\n      minorSplitLine: function (axisView, axisGroup, axisModel, gridModel) {\n        var axis = axisModel.axis;\n        var minorSplitLineModel = axisModel.getModel('minorSplitLine');\n        var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n        var gridRect = gridModel.coordinateSystem.getRect();\n        var isHorizontal = axis.isHorizontal();\n        var minorTicksCoords = axis.getMinorTicksCoords();\n\n        if (!minorTicksCoords.length) {\n          return;\n        }\n\n        var p1 = [];\n        var p2 = [];\n        var lineStyle = lineStyleModel.getLineStyle();\n\n        for (var i = 0; i < minorTicksCoords.length; i++) {\n          for (var k = 0; k < minorTicksCoords[i].length; k++) {\n            var tickCoord = axis.toGlobalCoord(minorTicksCoords[i][k].coord);\n\n            if (isHorizontal) {\n              p1[0] = tickCoord;\n              p1[1] = gridRect.y;\n              p2[0] = tickCoord;\n              p2[1] = gridRect.y + gridRect.height;\n            } else {\n              p1[0] = gridRect.x;\n              p1[1] = tickCoord;\n              p2[0] = gridRect.x + gridRect.width;\n              p2[1] = tickCoord;\n            }\n\n            var line = new Line({\n              anid: 'minor_line_' + minorTicksCoords[i][k].tickValue,\n              autoBatch: true,\n              shape: {\n                x1: p1[0],\n                y1: p1[1],\n                x2: p2[0],\n                y2: p2[1]\n              },\n              style: lineStyle,\n              silent: true\n            });\n            subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n            axisGroup.add(line);\n          }\n        }\n      },\n      splitArea: function (axisView, axisGroup, axisModel, gridModel) {\n        rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel);\n      }\n    };\n\n    var CartesianXAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianXAxisView, _super);\n\n      function CartesianXAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianXAxisView.type;\n        return _this;\n      }\n\n      CartesianXAxisView.type = 'xAxis';\n      return CartesianXAxisView;\n    }(CartesianAxisView);\n\n    var CartesianYAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianYAxisView, _super);\n\n      function CartesianYAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianXAxisView.type;\n        return _this;\n      }\n\n      CartesianYAxisView.type = 'yAxis';\n      return CartesianYAxisView;\n    }(CartesianAxisView);\n\n    var GridView =\n    /** @class */\n    function (_super) {\n      __extends(GridView, _super);\n\n      function GridView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'grid';\n        return _this;\n      }\n\n      GridView.prototype.render = function (gridModel, ecModel) {\n        this.group.removeAll();\n\n        if (gridModel.get('show')) {\n          this.group.add(new Rect({\n            shape: gridModel.coordinateSystem.getRect(),\n            style: defaults({\n              fill: gridModel.get('backgroundColor')\n            }, gridModel.getItemStyle()),\n            silent: true,\n            z2: -1\n          }));\n        }\n      };\n\n      GridView.type = 'grid';\n      return GridView;\n    }(ComponentView);\n\n    var extraOption = {\n      // gridIndex: 0,\n      // gridId: '',\n      offset: 0\n    };\n    function install$5(registers) {\n      registers.registerComponentView(GridView);\n      registers.registerComponentModel(GridModel);\n      registers.registerCoordinateSystem('cartesian2d', Grid);\n      axisModelCreator(registers, 'x', CartesianAxisModel, extraOption);\n      axisModelCreator(registers, 'y', CartesianAxisModel, extraOption);\n      registers.registerComponentView(CartesianXAxisView);\n      registers.registerComponentView(CartesianYAxisView);\n      registers.registerPreprocessor(function (option) {\n        // Only create grid when need\n        if (option.xAxis && option.yAxis && !option.grid) {\n          option.grid = {};\n        }\n      });\n    }\n\n    function install$6(registers) {\n      // In case developer forget to include grid component\n      use(install$5);\n      registers.registerSeriesModel(ScatterSeriesModel);\n      registers.registerChartView(ScatterView);\n      registers.registerLayout(pointsLayout('scatter'));\n    }\n\n    function radarLayout(ecModel) {\n      ecModel.eachSeriesByType('radar', function (seriesModel) {\n        var data = seriesModel.getData();\n        var points = [];\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (!coordSys) {\n          return;\n        }\n\n        var axes = coordSys.getIndicatorAxes();\n        each(axes, function (axis, axisIndex) {\n          data.each(data.mapDimension(axes[axisIndex].dim), function (val, dataIndex) {\n            points[dataIndex] = points[dataIndex] || [];\n            var point = coordSys.dataToPoint(val, axisIndex);\n            points[dataIndex][axisIndex] = isValidPoint(point) ? point : getValueMissingPoint(coordSys);\n          });\n        }); // Close polygon\n\n        data.each(function (idx) {\n          // TODO\n          // Is it appropriate to connect to the next data when some data is missing?\n          // Or, should trade it like `connectNull` in line chart?\n          var firstPoint = find(points[idx], function (point) {\n            return isValidPoint(point);\n          }) || getValueMissingPoint(coordSys); // Copy the first actual point to the end of the array\n\n          points[idx].push(firstPoint.slice());\n          data.setItemLayout(idx, points[idx]);\n        });\n      });\n    }\n\n    function isValidPoint(point) {\n      return !isNaN(point[0]) && !isNaN(point[1]);\n    }\n\n    function getValueMissingPoint(coordSys) {\n      // It is error-prone to input [NaN, NaN] into polygon, polygon.\n      // (probably cause problem when refreshing or animating)\n      return [coordSys.cx, coordSys.cy];\n    }\n\n    function radarBackwardCompat(option) {\n      var polarOptArr = option.polar;\n\n      if (polarOptArr) {\n        if (!isArray(polarOptArr)) {\n          polarOptArr = [polarOptArr];\n        }\n\n        var polarNotRadar_1 = [];\n        each(polarOptArr, function (polarOpt, idx) {\n          if (polarOpt.indicator) {\n            if (polarOpt.type && !polarOpt.shape) {\n              polarOpt.shape = polarOpt.type;\n            }\n\n            option.radar = option.radar || [];\n\n            if (!isArray(option.radar)) {\n              option.radar = [option.radar];\n            }\n\n            option.radar.push(polarOpt);\n          } else {\n            polarNotRadar_1.push(polarOpt);\n          }\n        });\n        option.polar = polarNotRadar_1;\n      }\n\n      each(option.series, function (seriesOpt) {\n        if (seriesOpt && seriesOpt.type === 'radar' && seriesOpt.polarIndex) {\n          seriesOpt.radarIndex = seriesOpt.polarIndex;\n        }\n      });\n    }\n\n    var RadarView =\n    /** @class */\n    function (_super) {\n      __extends(RadarView, _super);\n\n      function RadarView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = RadarView.type;\n        return _this;\n      }\n\n      RadarView.prototype.render = function (seriesModel, ecModel, api) {\n        var polar = seriesModel.coordinateSystem;\n        var group = this.group;\n        var data = seriesModel.getData();\n        var oldData = this._data;\n\n        function createSymbol$1(data, idx) {\n          var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';\n\n          if (symbolType === 'none') {\n            return;\n          }\n\n          var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n          var symbolPath = createSymbol(symbolType, -1, -1, 2, 2);\n          var symbolRotate = data.getItemVisual(idx, 'symbolRotate') || 0;\n          symbolPath.attr({\n            style: {\n              strokeNoScale: true\n            },\n            z2: 100,\n            scaleX: symbolSize[0] / 2,\n            scaleY: symbolSize[1] / 2,\n            rotation: symbolRotate * Math.PI / 180 || 0\n          });\n          return symbolPath;\n        }\n\n        function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) {\n          // Simply rerender all\n          symbolGroup.removeAll();\n\n          for (var i = 0; i < newPoints.length - 1; i++) {\n            var symbolPath = createSymbol$1(data, idx);\n\n            if (symbolPath) {\n              symbolPath.__dimIdx = i;\n\n              if (oldPoints[i]) {\n                symbolPath.setPosition(oldPoints[i]);\n                graphic[isInit ? 'initProps' : 'updateProps'](symbolPath, {\n                  x: newPoints[i][0],\n                  y: newPoints[i][1]\n                }, seriesModel, idx);\n              } else {\n                symbolPath.setPosition(newPoints[i]);\n              }\n\n              symbolGroup.add(symbolPath);\n            }\n          }\n        }\n\n        function getInitialPoints(points) {\n          return map(points, function (pt) {\n            return [polar.cx, polar.cy];\n          });\n        }\n\n        data.diff(oldData).add(function (idx) {\n          var points = data.getItemLayout(idx);\n\n          if (!points) {\n            return;\n          }\n\n          var polygon = new Polygon();\n          var polyline = new Polyline();\n          var target = {\n            shape: {\n              points: points\n            }\n          };\n          polygon.shape.points = getInitialPoints(points);\n          polyline.shape.points = getInitialPoints(points);\n          initProps(polygon, target, seriesModel, idx);\n          initProps(polyline, target, seriesModel, idx);\n          var itemGroup = new Group();\n          var symbolGroup = new Group();\n          itemGroup.add(polyline);\n          itemGroup.add(polygon);\n          itemGroup.add(symbolGroup);\n          updateSymbols(polyline.shape.points, points, symbolGroup, data, idx, true);\n          data.setItemGraphicEl(idx, itemGroup);\n        }).update(function (newIdx, oldIdx) {\n          var itemGroup = oldData.getItemGraphicEl(oldIdx);\n          var polyline = itemGroup.childAt(0);\n          var polygon = itemGroup.childAt(1);\n          var symbolGroup = itemGroup.childAt(2);\n          var target = {\n            shape: {\n              points: data.getItemLayout(newIdx)\n            }\n          };\n\n          if (!target.shape.points) {\n            return;\n          }\n\n          updateSymbols(polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false);\n          saveOldStyle(polygon);\n          saveOldStyle(polyline);\n          updateProps(polyline, target, seriesModel);\n          updateProps(polygon, target, seriesModel);\n          data.setItemGraphicEl(newIdx, itemGroup);\n        }).remove(function (idx) {\n          group.remove(oldData.getItemGraphicEl(idx));\n        }).execute();\n        data.eachItemGraphicEl(function (itemGroup, idx) {\n          var itemModel = data.getItemModel(idx);\n          var polyline = itemGroup.childAt(0);\n          var polygon = itemGroup.childAt(1);\n          var symbolGroup = itemGroup.childAt(2); // Radar uses the visual encoded from itemStyle.\n\n          var itemStyle = data.getItemVisual(idx, 'style');\n          var color = itemStyle.fill;\n          group.add(itemGroup);\n          polyline.useStyle(defaults(itemModel.getModel('lineStyle').getLineStyle(), {\n            fill: 'none',\n            stroke: color\n          }));\n          setStatesStylesFromModel(polyline, itemModel, 'lineStyle');\n          setStatesStylesFromModel(polygon, itemModel, 'areaStyle');\n          var areaStyleModel = itemModel.getModel('areaStyle');\n          var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty();\n          polygon.ignore = polygonIgnore;\n          each(['emphasis', 'select', 'blur'], function (stateName) {\n            var stateModel = itemModel.getModel([stateName, 'areaStyle']);\n            var stateIgnore = stateModel.isEmpty() && stateModel.parentModel.isEmpty(); // Won't be ignore if normal state is not ignore.\n\n            polygon.ensureState(stateName).ignore = stateIgnore && polygonIgnore;\n          });\n          polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), {\n            fill: color,\n            opacity: 0.7,\n            decal: itemStyle.decal\n          }));\n          var emphasisModel = itemModel.getModel('emphasis');\n          var itemHoverStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n          symbolGroup.eachChild(function (symbolPath) {\n            if (symbolPath instanceof ZRImage) {\n              var pathStyle = symbolPath.style;\n              symbolPath.useStyle(extend({\n                // TODO other properties like x, y ?\n                image: pathStyle.image,\n                x: pathStyle.x,\n                y: pathStyle.y,\n                width: pathStyle.width,\n                height: pathStyle.height\n              }, itemStyle));\n            } else {\n              symbolPath.useStyle(itemStyle);\n              symbolPath.setColor(color);\n              symbolPath.style.strokeNoScale = true;\n            }\n\n            var pathEmphasisState = symbolPath.ensureState('emphasis');\n            pathEmphasisState.style = clone(itemHoverStyle);\n            var defaultText = data.getStore().get(data.getDimensionIndex(symbolPath.__dimIdx), idx);\n            (defaultText == null || isNaN(defaultText)) && (defaultText = '');\n            setLabelStyle(symbolPath, getLabelStatesModels(itemModel), {\n              labelFetcher: data.hostModel,\n              labelDataIndex: idx,\n              labelDimIndex: symbolPath.__dimIdx,\n              defaultText: defaultText,\n              inheritColor: color,\n              defaultOpacity: itemStyle.opacity\n            });\n          });\n          toggleHoverEmphasis(itemGroup, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n        });\n        this._data = data;\n      };\n\n      RadarView.prototype.remove = function () {\n        this.group.removeAll();\n        this._data = null;\n      };\n\n      RadarView.type = 'radar';\n      return RadarView;\n    }(ChartView);\n\n    var RadarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(RadarSeriesModel, _super);\n\n      function RadarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = RadarSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      } // Overwrite\n\n\n      RadarSeriesModel.prototype.init = function (option) {\n        _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n        // Use a function instead of direct access because data reference may changed\n\n\n        this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n      };\n\n      RadarSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesDataSimply(this, {\n          generateCoord: 'indicator_',\n          generateCoordCount: Infinity\n        });\n      };\n\n      RadarSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        var data = this.getData();\n        var coordSys = this.coordinateSystem;\n        var indicatorAxes = coordSys.getIndicatorAxes();\n        var name = this.getData().getName(dataIndex);\n        var nameToDisplay = name === '' ? this.name : name;\n        var markerColor = retrieveVisualColorForTooltipMarker(this, dataIndex);\n        return createTooltipMarkup('section', {\n          header: nameToDisplay,\n          sortBlocks: true,\n          blocks: map(indicatorAxes, function (axis) {\n            var val = data.get(data.mapDimension(axis.dim), dataIndex);\n            return createTooltipMarkup('nameValue', {\n              markerType: 'subItem',\n              markerColor: markerColor,\n              name: axis.name,\n              value: val,\n              sortParam: val\n            });\n          })\n        });\n      };\n\n      RadarSeriesModel.prototype.getTooltipPosition = function (dataIndex) {\n        if (dataIndex != null) {\n          var data_1 = this.getData();\n          var coordSys = this.coordinateSystem;\n          var values = data_1.getValues(map(coordSys.dimensions, function (dim) {\n            return data_1.mapDimension(dim);\n          }), dataIndex);\n\n          for (var i = 0, len = values.length; i < len; i++) {\n            if (!isNaN(values[i])) {\n              var indicatorAxes = coordSys.getIndicatorAxes();\n              return coordSys.coordToPoint(indicatorAxes[i].dataToCoord(values[i]), i);\n            }\n          }\n        }\n      };\n\n      RadarSeriesModel.type = 'series.radar';\n      RadarSeriesModel.dependencies = ['radar'];\n      RadarSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        colorBy: 'data',\n        coordinateSystem: 'radar',\n        legendHoverLink: true,\n        radarIndex: 0,\n        lineStyle: {\n          width: 2,\n          type: 'solid',\n          join: 'round'\n        },\n        label: {\n          position: 'top'\n        },\n        // areaStyle: {\n        // },\n        // itemStyle: {}\n        symbolSize: 8 // symbolRotate: null\n\n      };\n      return RadarSeriesModel;\n    }(SeriesModel);\n\n    var valueAxisDefault = axisDefault.value;\n\n    function defaultsShow(opt, show) {\n      return defaults({\n        show: show\n      }, opt);\n    }\n\n    var RadarModel =\n    /** @class */\n    function (_super) {\n      __extends(RadarModel, _super);\n\n      function RadarModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = RadarModel.type;\n        return _this;\n      }\n\n      RadarModel.prototype.optionUpdated = function () {\n        var boundaryGap = this.get('boundaryGap');\n        var splitNumber = this.get('splitNumber');\n        var scale = this.get('scale');\n        var axisLine = this.get('axisLine');\n        var axisTick = this.get('axisTick'); // let axisType = this.get('axisType');\n\n        var axisLabel = this.get('axisLabel');\n        var nameTextStyle = this.get('axisName');\n        var showName = this.get(['axisName', 'show']);\n        var nameFormatter = this.get(['axisName', 'formatter']);\n        var nameGap = this.get('axisNameGap');\n        var triggerEvent = this.get('triggerEvent');\n        var indicatorModels = map(this.get('indicator') || [], function (indicatorOpt) {\n          // PENDING\n          if (indicatorOpt.max != null && indicatorOpt.max > 0 && !indicatorOpt.min) {\n            indicatorOpt.min = 0;\n          } else if (indicatorOpt.min != null && indicatorOpt.min < 0 && !indicatorOpt.max) {\n            indicatorOpt.max = 0;\n          }\n\n          var iNameTextStyle = nameTextStyle;\n\n          if (indicatorOpt.color != null) {\n            iNameTextStyle = defaults({\n              color: indicatorOpt.color\n            }, nameTextStyle);\n          } // Use same configuration\n\n\n          var innerIndicatorOpt = merge(clone(indicatorOpt), {\n            boundaryGap: boundaryGap,\n            splitNumber: splitNumber,\n            scale: scale,\n            axisLine: axisLine,\n            axisTick: axisTick,\n            // axisType: axisType,\n            axisLabel: axisLabel,\n            // Compatible with 2 and use text\n            name: indicatorOpt.text,\n            showName: showName,\n            nameLocation: 'end',\n            nameGap: nameGap,\n            // min: 0,\n            nameTextStyle: iNameTextStyle,\n            triggerEvent: triggerEvent\n          }, false);\n\n          if (isString(nameFormatter)) {\n            var indName = innerIndicatorOpt.name;\n            innerIndicatorOpt.name = nameFormatter.replace('{value}', indName != null ? indName : '');\n          } else if (isFunction(nameFormatter)) {\n            innerIndicatorOpt.name = nameFormatter(innerIndicatorOpt.name, innerIndicatorOpt);\n          }\n\n          var model = new Model(innerIndicatorOpt, null, this.ecModel);\n          mixin(model, AxisModelCommonMixin.prototype); // For triggerEvent.\n\n          model.mainType = 'radar';\n          model.componentIndex = this.componentIndex;\n          return model;\n        }, this);\n        this._indicatorModels = indicatorModels;\n      };\n\n      RadarModel.prototype.getIndicatorModels = function () {\n        return this._indicatorModels;\n      };\n\n      RadarModel.type = 'radar';\n      RadarModel.defaultOption = {\n        // zlevel: 0,\n        z: 0,\n        center: ['50%', '50%'],\n        radius: '75%',\n        startAngle: 90,\n        axisName: {\n          show: true // formatter: null\n          // textStyle: {}\n\n        },\n        boundaryGap: [0, 0],\n        splitNumber: 5,\n        axisNameGap: 15,\n        scale: false,\n        // Polygon or circle\n        shape: 'polygon',\n        axisLine: merge({\n          lineStyle: {\n            color: '#bbb'\n          }\n        }, valueAxisDefault.axisLine),\n        axisLabel: defaultsShow(valueAxisDefault.axisLabel, false),\n        axisTick: defaultsShow(valueAxisDefault.axisTick, false),\n        // axisType: 'value',\n        splitLine: defaultsShow(valueAxisDefault.splitLine, true),\n        splitArea: defaultsShow(valueAxisDefault.splitArea, true),\n        // {text, min, max}\n        indicator: []\n      };\n      return RadarModel;\n    }(ComponentModel);\n\n    var axisBuilderAttrs$1 = ['axisLine', 'axisTickLabel', 'axisName'];\n\n    var RadarView$1 =\n    /** @class */\n    function (_super) {\n      __extends(RadarView, _super);\n\n      function RadarView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = RadarView.type;\n        return _this;\n      }\n\n      RadarView.prototype.render = function (radarModel, ecModel, api) {\n        var group = this.group;\n        group.removeAll();\n\n        this._buildAxes(radarModel);\n\n        this._buildSplitLineAndArea(radarModel);\n      };\n\n      RadarView.prototype._buildAxes = function (radarModel) {\n        var radar = radarModel.coordinateSystem;\n        var indicatorAxes = radar.getIndicatorAxes();\n        var axisBuilders = map(indicatorAxes, function (indicatorAxis) {\n          var axisName = indicatorAxis.model.get('showName') ? indicatorAxis.name : ''; // hide name\n\n          var axisBuilder = new AxisBuilder(indicatorAxis.model, {\n            axisName: axisName,\n            position: [radar.cx, radar.cy],\n            rotation: indicatorAxis.angle,\n            labelDirection: -1,\n            tickDirection: -1,\n            nameDirection: 1\n          });\n          return axisBuilder;\n        });\n        each(axisBuilders, function (axisBuilder) {\n          each(axisBuilderAttrs$1, axisBuilder.add, axisBuilder);\n          this.group.add(axisBuilder.getGroup());\n        }, this);\n      };\n\n      RadarView.prototype._buildSplitLineAndArea = function (radarModel) {\n        var radar = radarModel.coordinateSystem;\n        var indicatorAxes = radar.getIndicatorAxes();\n\n        if (!indicatorAxes.length) {\n          return;\n        }\n\n        var shape = radarModel.get('shape');\n        var splitLineModel = radarModel.getModel('splitLine');\n        var splitAreaModel = radarModel.getModel('splitArea');\n        var lineStyleModel = splitLineModel.getModel('lineStyle');\n        var areaStyleModel = splitAreaModel.getModel('areaStyle');\n        var showSplitLine = splitLineModel.get('show');\n        var showSplitArea = splitAreaModel.get('show');\n        var splitLineColors = lineStyleModel.get('color');\n        var splitAreaColors = areaStyleModel.get('color');\n        var splitLineColorsArr = isArray(splitLineColors) ? splitLineColors : [splitLineColors];\n        var splitAreaColorsArr = isArray(splitAreaColors) ? splitAreaColors : [splitAreaColors];\n        var splitLines = [];\n        var splitAreas = [];\n\n        function getColorIndex(areaOrLine, areaOrLineColorList, idx) {\n          var colorIndex = idx % areaOrLineColorList.length;\n          areaOrLine[colorIndex] = areaOrLine[colorIndex] || [];\n          return colorIndex;\n        }\n\n        if (shape === 'circle') {\n          var ticksRadius = indicatorAxes[0].getTicksCoords();\n          var cx = radar.cx;\n          var cy = radar.cy;\n\n          for (var i = 0; i < ticksRadius.length; i++) {\n            if (showSplitLine) {\n              var colorIndex = getColorIndex(splitLines, splitLineColorsArr, i);\n              splitLines[colorIndex].push(new Circle({\n                shape: {\n                  cx: cx,\n                  cy: cy,\n                  r: ticksRadius[i].coord\n                }\n              }));\n            }\n\n            if (showSplitArea && i < ticksRadius.length - 1) {\n              var colorIndex = getColorIndex(splitAreas, splitAreaColorsArr, i);\n              splitAreas[colorIndex].push(new Ring({\n                shape: {\n                  cx: cx,\n                  cy: cy,\n                  r0: ticksRadius[i].coord,\n                  r: ticksRadius[i + 1].coord\n                }\n              }));\n            }\n          }\n        } // Polyyon\n        else {\n            var realSplitNumber_1;\n            var axesTicksPoints = map(indicatorAxes, function (indicatorAxis, idx) {\n              var ticksCoords = indicatorAxis.getTicksCoords();\n              realSplitNumber_1 = realSplitNumber_1 == null ? ticksCoords.length - 1 : Math.min(ticksCoords.length - 1, realSplitNumber_1);\n              return map(ticksCoords, function (tickCoord) {\n                return radar.coordToPoint(tickCoord.coord, idx);\n              });\n            });\n            var prevPoints = [];\n\n            for (var i = 0; i <= realSplitNumber_1; i++) {\n              var points = [];\n\n              for (var j = 0; j < indicatorAxes.length; j++) {\n                points.push(axesTicksPoints[j][i]);\n              } // Close\n\n\n              if (points[0]) {\n                points.push(points[0].slice());\n              } else {\n                if (\"development\" !== 'production') {\n                  console.error('Can\\'t draw value axis ' + i);\n                }\n              }\n\n              if (showSplitLine) {\n                var colorIndex = getColorIndex(splitLines, splitLineColorsArr, i);\n                splitLines[colorIndex].push(new Polyline({\n                  shape: {\n                    points: points\n                  }\n                }));\n              }\n\n              if (showSplitArea && prevPoints) {\n                var colorIndex = getColorIndex(splitAreas, splitAreaColorsArr, i - 1);\n                splitAreas[colorIndex].push(new Polygon({\n                  shape: {\n                    points: points.concat(prevPoints)\n                  }\n                }));\n              }\n\n              prevPoints = points.slice().reverse();\n            }\n          }\n\n        var lineStyle = lineStyleModel.getLineStyle();\n        var areaStyle = areaStyleModel.getAreaStyle(); // Add splitArea before splitLine\n\n        each(splitAreas, function (splitAreas, idx) {\n          this.group.add(mergePath$1(splitAreas, {\n            style: defaults({\n              stroke: 'none',\n              fill: splitAreaColorsArr[idx % splitAreaColorsArr.length]\n            }, areaStyle),\n            silent: true\n          }));\n        }, this);\n        each(splitLines, function (splitLines, idx) {\n          this.group.add(mergePath$1(splitLines, {\n            style: defaults({\n              fill: 'none',\n              stroke: splitLineColorsArr[idx % splitLineColorsArr.length]\n            }, lineStyle),\n            silent: true\n          }));\n        }, this);\n      };\n\n      RadarView.type = 'radar';\n      return RadarView;\n    }(ComponentView);\n\n    var IndicatorAxis =\n    /** @class */\n    function (_super) {\n      __extends(IndicatorAxis, _super);\n\n      function IndicatorAxis(dim, scale, radiusExtent) {\n        var _this = _super.call(this, dim, scale, radiusExtent) || this;\n\n        _this.type = 'value';\n        _this.angle = 0;\n        _this.name = '';\n        return _this;\n      }\n\n      return IndicatorAxis;\n    }(Axis);\n\n    var Radar =\n    /** @class */\n    function () {\n      function Radar(radarModel, ecModel, api) {\n        /**\n         *\n         * Radar dimensions\n         */\n        this.dimensions = [];\n        this._model = radarModel;\n        this._indicatorAxes = map(radarModel.getIndicatorModels(), function (indicatorModel, idx) {\n          var dim = 'indicator_' + idx;\n          var indicatorAxis = new IndicatorAxis(dim, new IntervalScale() // (indicatorModel.get('axisType') === 'log') ? new LogScale() : new IntervalScale()\n          );\n          indicatorAxis.name = indicatorModel.get('name'); // Inject model and axis\n\n          indicatorAxis.model = indicatorModel;\n          indicatorModel.axis = indicatorAxis;\n          this.dimensions.push(dim);\n          return indicatorAxis;\n        }, this);\n        this.resize(radarModel, api);\n      }\n\n      Radar.prototype.getIndicatorAxes = function () {\n        return this._indicatorAxes;\n      };\n\n      Radar.prototype.dataToPoint = function (value, indicatorIndex) {\n        var indicatorAxis = this._indicatorAxes[indicatorIndex];\n        return this.coordToPoint(indicatorAxis.dataToCoord(value), indicatorIndex);\n      }; // TODO: API should be coordToPoint([coord, indicatorIndex])\n\n\n      Radar.prototype.coordToPoint = function (coord, indicatorIndex) {\n        var indicatorAxis = this._indicatorAxes[indicatorIndex];\n        var angle = indicatorAxis.angle;\n        var x = this.cx + coord * Math.cos(angle);\n        var y = this.cy - coord * Math.sin(angle);\n        return [x, y];\n      };\n\n      Radar.prototype.pointToData = function (pt) {\n        var dx = pt[0] - this.cx;\n        var dy = pt[1] - this.cy;\n        var radius = Math.sqrt(dx * dx + dy * dy);\n        dx /= radius;\n        dy /= radius;\n        var radian = Math.atan2(-dy, dx); // Find the closest angle\n        // FIXME index can calculated directly\n\n        var minRadianDiff = Infinity;\n        var closestAxis;\n        var closestAxisIdx = -1;\n\n        for (var i = 0; i < this._indicatorAxes.length; i++) {\n          var indicatorAxis = this._indicatorAxes[i];\n          var diff = Math.abs(radian - indicatorAxis.angle);\n\n          if (diff < minRadianDiff) {\n            closestAxis = indicatorAxis;\n            closestAxisIdx = i;\n            minRadianDiff = diff;\n          }\n        }\n\n        return [closestAxisIdx, +(closestAxis && closestAxis.coordToData(radius))];\n      };\n\n      Radar.prototype.resize = function (radarModel, api) {\n        var center = radarModel.get('center');\n        var viewWidth = api.getWidth();\n        var viewHeight = api.getHeight();\n        var viewSize = Math.min(viewWidth, viewHeight) / 2;\n        this.cx = parsePercent$1(center[0], viewWidth);\n        this.cy = parsePercent$1(center[1], viewHeight);\n        this.startAngle = radarModel.get('startAngle') * Math.PI / 180; // radius may be single value like `20`, `'80%'`, or array like `[10, '80%']`\n\n        var radius = radarModel.get('radius');\n\n        if (isString(radius) || isNumber(radius)) {\n          radius = [0, radius];\n        }\n\n        this.r0 = parsePercent$1(radius[0], viewSize);\n        this.r = parsePercent$1(radius[1], viewSize);\n        each(this._indicatorAxes, function (indicatorAxis, idx) {\n          indicatorAxis.setExtent(this.r0, this.r);\n          var angle = this.startAngle + idx * Math.PI * 2 / this._indicatorAxes.length; // Normalize to [-PI, PI]\n\n          angle = Math.atan2(Math.sin(angle), Math.cos(angle));\n          indicatorAxis.angle = angle;\n        }, this);\n      };\n\n      Radar.prototype.update = function (ecModel, api) {\n        var indicatorAxes = this._indicatorAxes;\n        var radarModel = this._model;\n        each(indicatorAxes, function (indicatorAxis) {\n          indicatorAxis.scale.setExtent(Infinity, -Infinity);\n        });\n        ecModel.eachSeriesByType('radar', function (radarSeries, idx) {\n          if (radarSeries.get('coordinateSystem') !== 'radar' // @ts-ignore\n          || ecModel.getComponent('radar', radarSeries.get('radarIndex')) !== radarModel) {\n            return;\n          }\n\n          var data = radarSeries.getData();\n          each(indicatorAxes, function (indicatorAxis) {\n            indicatorAxis.scale.unionExtentFromData(data, data.mapDimension(indicatorAxis.dim));\n          });\n        }, this);\n        var splitNumber = radarModel.get('splitNumber');\n        var dummyScale = new IntervalScale();\n        dummyScale.setExtent(0, splitNumber);\n        dummyScale.setInterval(1); // Force all the axis fixing the maxSplitNumber.\n\n        each(indicatorAxes, function (indicatorAxis, idx) {\n          alignScaleTicks(indicatorAxis.scale, indicatorAxis.model, dummyScale);\n        });\n      };\n\n      Radar.prototype.convertToPixel = function (ecModel, finder, value) {\n        console.warn('Not implemented.');\n        return null;\n      };\n\n      Radar.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n        console.warn('Not implemented.');\n        return null;\n      };\n\n      Radar.prototype.containPoint = function (point) {\n        console.warn('Not implemented.');\n        return false;\n      };\n\n      Radar.create = function (ecModel, api) {\n        var radarList = [];\n        ecModel.eachComponent('radar', function (radarModel) {\n          var radar = new Radar(radarModel, ecModel, api);\n          radarList.push(radar);\n          radarModel.coordinateSystem = radar;\n        });\n        ecModel.eachSeriesByType('radar', function (radarSeries) {\n          if (radarSeries.get('coordinateSystem') === 'radar') {\n            // Inject coordinate system\n            // @ts-ignore\n            radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0];\n          }\n        });\n        return radarList;\n      };\n      /**\n       * Radar dimensions is based on the data\n       */\n\n\n      Radar.dimensions = [];\n      return Radar;\n    }();\n\n    function install$7(registers) {\n      registers.registerCoordinateSystem('radar', Radar);\n      registers.registerComponentModel(RadarModel);\n      registers.registerComponentView(RadarView$1);\n      registers.registerVisual({\n        seriesType: 'radar',\n        reset: function (seriesModel) {\n          var data = seriesModel.getData(); // itemVisual symbol is for selected data\n\n          data.each(function (idx) {\n            data.setItemVisual(idx, 'legendIcon', 'roundRect');\n          }); // visual is for unselected data\n\n          data.setVisual('legendIcon', 'roundRect');\n        }\n      });\n    }\n\n    function install$8(registers) {\n      use(install$7);\n      registers.registerChartView(RadarView);\n      registers.registerSeriesModel(RadarSeriesModel);\n      registers.registerLayout(radarLayout);\n      registers.registerProcessor(dataFilter('radar'));\n      registers.registerPreprocessor(radarBackwardCompat);\n    }\n\n    var ATTR = '\\0_ec_interaction_mutex';\n    function take(zr, resourceKey, userKey) {\n      var store = getStore(zr);\n      store[resourceKey] = userKey;\n    }\n    function release(zr, resourceKey, userKey) {\n      var store = getStore(zr);\n      var uKey = store[resourceKey];\n\n      if (uKey === userKey) {\n        store[resourceKey] = null;\n      }\n    }\n    function isTaken(zr, resourceKey) {\n      return !!getStore(zr)[resourceKey];\n    }\n\n    function getStore(zr) {\n      return zr[ATTR] || (zr[ATTR] = {});\n    }\n    /**\n     * payload: {\n     *     type: 'takeGlobalCursor',\n     *     key: 'dataZoomSelect', or 'brush', or ...,\n     *         If no userKey, release global cursor.\n     * }\n     */\n    // TODO: SELF REGISTERED.\n\n\n    registerAction({\n      type: 'takeGlobalCursor',\n      event: 'globalCursorTaken',\n      update: 'update'\n    }, noop);\n\n    var RoamController =\n    /** @class */\n    function (_super) {\n      __extends(RoamController, _super);\n\n      function RoamController(zr) {\n        var _this = _super.call(this) || this;\n\n        _this._zr = zr; // Avoid two roamController bind the same handler\n\n        var mousedownHandler = bind(_this._mousedownHandler, _this);\n        var mousemoveHandler = bind(_this._mousemoveHandler, _this);\n        var mouseupHandler = bind(_this._mouseupHandler, _this);\n        var mousewheelHandler = bind(_this._mousewheelHandler, _this);\n        var pinchHandler = bind(_this._pinchHandler, _this);\n        /**\n         * Notice: only enable needed types. For example, if 'zoom'\n         * is not needed, 'zoom' should not be enabled, otherwise\n         * default mousewheel behaviour (scroll page) will be disabled.\n         */\n\n        _this.enable = function (controlType, opt) {\n          // Disable previous first\n          this.disable();\n          this._opt = defaults(clone(opt) || {}, {\n            zoomOnMouseWheel: true,\n            moveOnMouseMove: true,\n            // By default, wheel do not trigger move.\n            moveOnMouseWheel: false,\n            preventDefaultMouseMove: true\n          });\n\n          if (controlType == null) {\n            controlType = true;\n          }\n\n          if (controlType === true || controlType === 'move' || controlType === 'pan') {\n            zr.on('mousedown', mousedownHandler);\n            zr.on('mousemove', mousemoveHandler);\n            zr.on('mouseup', mouseupHandler);\n          }\n\n          if (controlType === true || controlType === 'scale' || controlType === 'zoom') {\n            zr.on('mousewheel', mousewheelHandler);\n            zr.on('pinch', pinchHandler);\n          }\n        };\n\n        _this.disable = function () {\n          zr.off('mousedown', mousedownHandler);\n          zr.off('mousemove', mousemoveHandler);\n          zr.off('mouseup', mouseupHandler);\n          zr.off('mousewheel', mousewheelHandler);\n          zr.off('pinch', pinchHandler);\n        };\n\n        return _this;\n      }\n\n      RoamController.prototype.isDragging = function () {\n        return this._dragging;\n      };\n\n      RoamController.prototype.isPinching = function () {\n        return this._pinching;\n      };\n\n      RoamController.prototype.setPointerChecker = function (pointerChecker) {\n        this.pointerChecker = pointerChecker;\n      };\n\n      RoamController.prototype.dispose = function () {\n        this.disable();\n      };\n\n      RoamController.prototype._mousedownHandler = function (e) {\n        if (isMiddleOrRightButtonOnMouseUpDown(e)) {\n          return;\n        }\n\n        var el = e.target;\n\n        while (el) {\n          if (el.draggable) {\n            return;\n          } // check if host is draggable\n\n\n          el = el.__hostTarget || el.parent;\n        }\n\n        var x = e.offsetX;\n        var y = e.offsetY; // Only check on mosedown, but not mousemove.\n        // Mouse can be out of target when mouse moving.\n\n        if (this.pointerChecker && this.pointerChecker(e, x, y)) {\n          this._x = x;\n          this._y = y;\n          this._dragging = true;\n        }\n      };\n\n      RoamController.prototype._mousemoveHandler = function (e) {\n        if (!this._dragging || !isAvailableBehavior('moveOnMouseMove', e, this._opt) || e.gestureEvent === 'pinch' || isTaken(this._zr, 'globalPan')) {\n          return;\n        }\n\n        var x = e.offsetX;\n        var y = e.offsetY;\n        var oldX = this._x;\n        var oldY = this._y;\n        var dx = x - oldX;\n        var dy = y - oldY;\n        this._x = x;\n        this._y = y;\n        this._opt.preventDefaultMouseMove && stop(e.event);\n        trigger(this, 'pan', 'moveOnMouseMove', e, {\n          dx: dx,\n          dy: dy,\n          oldX: oldX,\n          oldY: oldY,\n          newX: x,\n          newY: y,\n          isAvailableBehavior: null\n        });\n      };\n\n      RoamController.prototype._mouseupHandler = function (e) {\n        if (!isMiddleOrRightButtonOnMouseUpDown(e)) {\n          this._dragging = false;\n        }\n      };\n\n      RoamController.prototype._mousewheelHandler = function (e) {\n        var shouldZoom = isAvailableBehavior('zoomOnMouseWheel', e, this._opt);\n        var shouldMove = isAvailableBehavior('moveOnMouseWheel', e, this._opt);\n        var wheelDelta = e.wheelDelta;\n        var absWheelDeltaDelta = Math.abs(wheelDelta);\n        var originX = e.offsetX;\n        var originY = e.offsetY; // wheelDelta maybe -0 in chrome mac.\n\n        if (wheelDelta === 0 || !shouldZoom && !shouldMove) {\n          return;\n        } // If both `shouldZoom` and `shouldMove` is true, trigger\n        // their event both, and the final behavior is determined\n        // by event listener themselves.\n\n\n        if (shouldZoom) {\n          // Convenience:\n          // Mac and VM Windows on Mac: scroll up: zoom out.\n          // Windows: scroll up: zoom in.\n          // FIXME: Should do more test in different environment.\n          // wheelDelta is too complicated in difference nvironment\n          // (https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel),\n          // although it has been normallized by zrender.\n          // wheelDelta of mouse wheel is bigger than touch pad.\n          var factor = absWheelDeltaDelta > 3 ? 1.4 : absWheelDeltaDelta > 1 ? 1.2 : 1.1;\n          var scale = wheelDelta > 0 ? factor : 1 / factor;\n          checkPointerAndTrigger(this, 'zoom', 'zoomOnMouseWheel', e, {\n            scale: scale,\n            originX: originX,\n            originY: originY,\n            isAvailableBehavior: null\n          });\n        }\n\n        if (shouldMove) {\n          // FIXME: Should do more test in different environment.\n          var absDelta = Math.abs(wheelDelta); // wheelDelta of mouse wheel is bigger than touch pad.\n\n          var scrollDelta = (wheelDelta > 0 ? 1 : -1) * (absDelta > 3 ? 0.4 : absDelta > 1 ? 0.15 : 0.05);\n          checkPointerAndTrigger(this, 'scrollMove', 'moveOnMouseWheel', e, {\n            scrollDelta: scrollDelta,\n            originX: originX,\n            originY: originY,\n            isAvailableBehavior: null\n          });\n        }\n      };\n\n      RoamController.prototype._pinchHandler = function (e) {\n        if (isTaken(this._zr, 'globalPan')) {\n          return;\n        }\n\n        var scale = e.pinchScale > 1 ? 1.1 : 1 / 1.1;\n        checkPointerAndTrigger(this, 'zoom', null, e, {\n          scale: scale,\n          originX: e.pinchX,\n          originY: e.pinchY,\n          isAvailableBehavior: null\n        });\n      };\n\n      return RoamController;\n    }(Eventful);\n\n    function checkPointerAndTrigger(controller, eventName, behaviorToCheck, e, contollerEvent) {\n      if (controller.pointerChecker && controller.pointerChecker(e, contollerEvent.originX, contollerEvent.originY)) {\n        // When mouse is out of roamController rect,\n        // default befavoius should not be be disabled, otherwise\n        // page sliding is disabled, contrary to expectation.\n        stop(e.event);\n        trigger(controller, eventName, behaviorToCheck, e, contollerEvent);\n      }\n    }\n\n    function trigger(controller, eventName, behaviorToCheck, e, contollerEvent) {\n      // Also provide behavior checker for event listener, for some case that\n      // multiple components share one listener.\n      contollerEvent.isAvailableBehavior = bind(isAvailableBehavior, null, behaviorToCheck, e); // TODO should not have type issue.\n\n      controller.trigger(eventName, contollerEvent);\n    } // settings: {\n    //     zoomOnMouseWheel\n    //     moveOnMouseMove\n    //     moveOnMouseWheel\n    // }\n    // The value can be: true / false / 'shift' / 'ctrl' / 'alt'.\n\n\n    function isAvailableBehavior(behaviorToCheck, e, settings) {\n      var setting = settings[behaviorToCheck];\n      return !behaviorToCheck || setting && (!isString(setting) || e.event[setting + 'Key']);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * For geo and graph.\n     */\n    function updateViewOnPan(controllerHost, dx, dy) {\n      var target = controllerHost.target;\n      target.x += dx;\n      target.y += dy;\n      target.dirty();\n    }\n    /**\n     * For geo and graph.\n     */\n\n    function updateViewOnZoom(controllerHost, zoomDelta, zoomX, zoomY) {\n      var target = controllerHost.target;\n      var zoomLimit = controllerHost.zoomLimit;\n      var newZoom = controllerHost.zoom = controllerHost.zoom || 1;\n      newZoom *= zoomDelta;\n\n      if (zoomLimit) {\n        var zoomMin = zoomLimit.min || 0;\n        var zoomMax = zoomLimit.max || Infinity;\n        newZoom = Math.max(Math.min(zoomMax, newZoom), zoomMin);\n      }\n\n      var zoomScale = newZoom / controllerHost.zoom;\n      controllerHost.zoom = newZoom; // Keep the mouse center when scaling\n\n      target.x -= (zoomX - target.x) * (zoomScale - 1);\n      target.y -= (zoomY - target.y) * (zoomScale - 1);\n      target.scaleX *= zoomScale;\n      target.scaleY *= zoomScale;\n      target.dirty();\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var IRRELEVANT_EXCLUDES = {\n      'axisPointer': 1,\n      'tooltip': 1,\n      'brush': 1\n    };\n    /**\n     * Avoid that: mouse click on a elements that is over geo or graph,\n     * but roam is triggered.\n     */\n\n    function onIrrelevantElement(e, api, targetCoordSysModel) {\n      var model = api.getComponentByElement(e.topTarget); // If model is axisModel, it works only if it is injected with coordinateSystem.\n\n      var coordSys = model && model.coordinateSystem;\n      return model && model !== targetCoordSysModel && !IRRELEVANT_EXCLUDES.hasOwnProperty(model.mainType) && coordSys && coordSys.model !== targetCoordSysModel;\n    }\n\n    function parseXML(svg) {\n        if (isString(svg)) {\n            var parser = new DOMParser();\n            svg = parser.parseFromString(svg, 'text/xml');\n        }\n        var svgNode = svg;\n        if (svgNode.nodeType === 9) {\n            svgNode = svgNode.firstChild;\n        }\n        while (svgNode.nodeName.toLowerCase() !== 'svg' || svgNode.nodeType !== 1) {\n            svgNode = svgNode.nextSibling;\n        }\n        return svgNode;\n    }\n\n    var nodeParsers;\n    var INHERITABLE_STYLE_ATTRIBUTES_MAP = {\n        'fill': 'fill',\n        'stroke': 'stroke',\n        'stroke-width': 'lineWidth',\n        'opacity': 'opacity',\n        'fill-opacity': 'fillOpacity',\n        'stroke-opacity': 'strokeOpacity',\n        'stroke-dasharray': 'lineDash',\n        'stroke-dashoffset': 'lineDashOffset',\n        'stroke-linecap': 'lineCap',\n        'stroke-linejoin': 'lineJoin',\n        'stroke-miterlimit': 'miterLimit',\n        'font-family': 'fontFamily',\n        'font-size': 'fontSize',\n        'font-style': 'fontStyle',\n        'font-weight': 'fontWeight',\n        'text-anchor': 'textAlign',\n        'visibility': 'visibility',\n        'display': 'display'\n    };\n    var INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS = keys(INHERITABLE_STYLE_ATTRIBUTES_MAP);\n    var SELF_STYLE_ATTRIBUTES_MAP = {\n        'alignment-baseline': 'textBaseline',\n        'stop-color': 'stopColor'\n    };\n    var SELF_STYLE_ATTRIBUTES_MAP_KEYS = keys(SELF_STYLE_ATTRIBUTES_MAP);\n    var SVGParser = (function () {\n        function SVGParser() {\n            this._defs = {};\n            this._root = null;\n        }\n        SVGParser.prototype.parse = function (xml, opt) {\n            opt = opt || {};\n            var svg = parseXML(xml);\n            if (\"development\" !== 'production') {\n                if (!svg) {\n                    throw new Error('Illegal svg');\n                }\n            }\n            this._defsUsePending = [];\n            var root = new Group();\n            this._root = root;\n            var named = [];\n            var viewBox = svg.getAttribute('viewBox') || '';\n            var width = parseFloat((svg.getAttribute('width') || opt.width));\n            var height = parseFloat((svg.getAttribute('height') || opt.height));\n            isNaN(width) && (width = null);\n            isNaN(height) && (height = null);\n            parseAttributes(svg, root, null, true, false);\n            var child = svg.firstChild;\n            while (child) {\n                this._parseNode(child, root, named, null, false, false);\n                child = child.nextSibling;\n            }\n            applyDefs(this._defs, this._defsUsePending);\n            this._defsUsePending = [];\n            var viewBoxRect;\n            var viewBoxTransform;\n            if (viewBox) {\n                var viewBoxArr = splitNumberSequence(viewBox);\n                if (viewBoxArr.length >= 4) {\n                    viewBoxRect = {\n                        x: parseFloat((viewBoxArr[0] || 0)),\n                        y: parseFloat((viewBoxArr[1] || 0)),\n                        width: parseFloat(viewBoxArr[2]),\n                        height: parseFloat(viewBoxArr[3])\n                    };\n                }\n            }\n            if (viewBoxRect && width != null && height != null) {\n                viewBoxTransform = makeViewBoxTransform(viewBoxRect, { x: 0, y: 0, width: width, height: height });\n                if (!opt.ignoreViewBox) {\n                    var elRoot = root;\n                    root = new Group();\n                    root.add(elRoot);\n                    elRoot.scaleX = elRoot.scaleY = viewBoxTransform.scale;\n                    elRoot.x = viewBoxTransform.x;\n                    elRoot.y = viewBoxTransform.y;\n                }\n            }\n            if (!opt.ignoreRootClip && width != null && height != null) {\n                root.setClipPath(new Rect({\n                    shape: { x: 0, y: 0, width: width, height: height }\n                }));\n            }\n            return {\n                root: root,\n                width: width,\n                height: height,\n                viewBoxRect: viewBoxRect,\n                viewBoxTransform: viewBoxTransform,\n                named: named\n            };\n        };\n        SVGParser.prototype._parseNode = function (xmlNode, parentGroup, named, namedFrom, isInDefs, isInText) {\n            var nodeName = xmlNode.nodeName.toLowerCase();\n            var el;\n            var namedFromForSub = namedFrom;\n            if (nodeName === 'defs') {\n                isInDefs = true;\n            }\n            if (nodeName === 'text') {\n                isInText = true;\n            }\n            if (nodeName === 'defs' || nodeName === 'switch') {\n                el = parentGroup;\n            }\n            else {\n                if (!isInDefs) {\n                    var parser_1 = nodeParsers[nodeName];\n                    if (parser_1 && hasOwn(nodeParsers, nodeName)) {\n                        el = parser_1.call(this, xmlNode, parentGroup);\n                        var nameAttr = xmlNode.getAttribute('name');\n                        if (nameAttr) {\n                            var newNamed = {\n                                name: nameAttr,\n                                namedFrom: null,\n                                svgNodeTagLower: nodeName,\n                                el: el\n                            };\n                            named.push(newNamed);\n                            if (nodeName === 'g') {\n                                namedFromForSub = newNamed;\n                            }\n                        }\n                        else if (namedFrom) {\n                            named.push({\n                                name: namedFrom.name,\n                                namedFrom: namedFrom,\n                                svgNodeTagLower: nodeName,\n                                el: el\n                            });\n                        }\n                        parentGroup.add(el);\n                    }\n                }\n                var parser = paintServerParsers[nodeName];\n                if (parser && hasOwn(paintServerParsers, nodeName)) {\n                    var def = parser.call(this, xmlNode);\n                    var id = xmlNode.getAttribute('id');\n                    if (id) {\n                        this._defs[id] = def;\n                    }\n                }\n            }\n            if (el && el.isGroup) {\n                var child = xmlNode.firstChild;\n                while (child) {\n                    if (child.nodeType === 1) {\n                        this._parseNode(child, el, named, namedFromForSub, isInDefs, isInText);\n                    }\n                    else if (child.nodeType === 3 && isInText) {\n                        this._parseText(child, el);\n                    }\n                    child = child.nextSibling;\n                }\n            }\n        };\n        SVGParser.prototype._parseText = function (xmlNode, parentGroup) {\n            var text = new TSpan({\n                style: {\n                    text: xmlNode.textContent\n                },\n                silent: true,\n                x: this._textX || 0,\n                y: this._textY || 0\n            });\n            inheritStyle(parentGroup, text);\n            parseAttributes(xmlNode, text, this._defsUsePending, false, false);\n            applyTextAlignment(text, parentGroup);\n            var textStyle = text.style;\n            var fontSize = textStyle.fontSize;\n            if (fontSize && fontSize < 9) {\n                textStyle.fontSize = 9;\n                text.scaleX *= fontSize / 9;\n                text.scaleY *= fontSize / 9;\n            }\n            var font = (textStyle.fontSize || textStyle.fontFamily) && [\n                textStyle.fontStyle,\n                textStyle.fontWeight,\n                (textStyle.fontSize || 12) + 'px',\n                textStyle.fontFamily || 'sans-serif'\n            ].join(' ');\n            textStyle.font = font;\n            var rect = text.getBoundingRect();\n            this._textX += rect.width;\n            parentGroup.add(text);\n            return text;\n        };\n        SVGParser.internalField = (function () {\n            nodeParsers = {\n                'g': function (xmlNode, parentGroup) {\n                    var g = new Group();\n                    inheritStyle(parentGroup, g);\n                    parseAttributes(xmlNode, g, this._defsUsePending, false, false);\n                    return g;\n                },\n                'rect': function (xmlNode, parentGroup) {\n                    var rect = new Rect();\n                    inheritStyle(parentGroup, rect);\n                    parseAttributes(xmlNode, rect, this._defsUsePending, false, false);\n                    rect.setShape({\n                        x: parseFloat(xmlNode.getAttribute('x') || '0'),\n                        y: parseFloat(xmlNode.getAttribute('y') || '0'),\n                        width: parseFloat(xmlNode.getAttribute('width') || '0'),\n                        height: parseFloat(xmlNode.getAttribute('height') || '0')\n                    });\n                    rect.silent = true;\n                    return rect;\n                },\n                'circle': function (xmlNode, parentGroup) {\n                    var circle = new Circle();\n                    inheritStyle(parentGroup, circle);\n                    parseAttributes(xmlNode, circle, this._defsUsePending, false, false);\n                    circle.setShape({\n                        cx: parseFloat(xmlNode.getAttribute('cx') || '0'),\n                        cy: parseFloat(xmlNode.getAttribute('cy') || '0'),\n                        r: parseFloat(xmlNode.getAttribute('r') || '0')\n                    });\n                    circle.silent = true;\n                    return circle;\n                },\n                'line': function (xmlNode, parentGroup) {\n                    var line = new Line();\n                    inheritStyle(parentGroup, line);\n                    parseAttributes(xmlNode, line, this._defsUsePending, false, false);\n                    line.setShape({\n                        x1: parseFloat(xmlNode.getAttribute('x1') || '0'),\n                        y1: parseFloat(xmlNode.getAttribute('y1') || '0'),\n                        x2: parseFloat(xmlNode.getAttribute('x2') || '0'),\n                        y2: parseFloat(xmlNode.getAttribute('y2') || '0')\n                    });\n                    line.silent = true;\n                    return line;\n                },\n                'ellipse': function (xmlNode, parentGroup) {\n                    var ellipse = new Ellipse();\n                    inheritStyle(parentGroup, ellipse);\n                    parseAttributes(xmlNode, ellipse, this._defsUsePending, false, false);\n                    ellipse.setShape({\n                        cx: parseFloat(xmlNode.getAttribute('cx') || '0'),\n                        cy: parseFloat(xmlNode.getAttribute('cy') || '0'),\n                        rx: parseFloat(xmlNode.getAttribute('rx') || '0'),\n                        ry: parseFloat(xmlNode.getAttribute('ry') || '0')\n                    });\n                    ellipse.silent = true;\n                    return ellipse;\n                },\n                'polygon': function (xmlNode, parentGroup) {\n                    var pointsStr = xmlNode.getAttribute('points');\n                    var pointsArr;\n                    if (pointsStr) {\n                        pointsArr = parsePoints(pointsStr);\n                    }\n                    var polygon = new Polygon({\n                        shape: {\n                            points: pointsArr || []\n                        },\n                        silent: true\n                    });\n                    inheritStyle(parentGroup, polygon);\n                    parseAttributes(xmlNode, polygon, this._defsUsePending, false, false);\n                    return polygon;\n                },\n                'polyline': function (xmlNode, parentGroup) {\n                    var pointsStr = xmlNode.getAttribute('points');\n                    var pointsArr;\n                    if (pointsStr) {\n                        pointsArr = parsePoints(pointsStr);\n                    }\n                    var polyline = new Polyline({\n                        shape: {\n                            points: pointsArr || []\n                        },\n                        silent: true\n                    });\n                    inheritStyle(parentGroup, polyline);\n                    parseAttributes(xmlNode, polyline, this._defsUsePending, false, false);\n                    return polyline;\n                },\n                'image': function (xmlNode, parentGroup) {\n                    var img = new ZRImage();\n                    inheritStyle(parentGroup, img);\n                    parseAttributes(xmlNode, img, this._defsUsePending, false, false);\n                    img.setStyle({\n                        image: xmlNode.getAttribute('xlink:href') || xmlNode.getAttribute('href'),\n                        x: +xmlNode.getAttribute('x'),\n                        y: +xmlNode.getAttribute('y'),\n                        width: +xmlNode.getAttribute('width'),\n                        height: +xmlNode.getAttribute('height')\n                    });\n                    img.silent = true;\n                    return img;\n                },\n                'text': function (xmlNode, parentGroup) {\n                    var x = xmlNode.getAttribute('x') || '0';\n                    var y = xmlNode.getAttribute('y') || '0';\n                    var dx = xmlNode.getAttribute('dx') || '0';\n                    var dy = xmlNode.getAttribute('dy') || '0';\n                    this._textX = parseFloat(x) + parseFloat(dx);\n                    this._textY = parseFloat(y) + parseFloat(dy);\n                    var g = new Group();\n                    inheritStyle(parentGroup, g);\n                    parseAttributes(xmlNode, g, this._defsUsePending, false, true);\n                    return g;\n                },\n                'tspan': function (xmlNode, parentGroup) {\n                    var x = xmlNode.getAttribute('x');\n                    var y = xmlNode.getAttribute('y');\n                    if (x != null) {\n                        this._textX = parseFloat(x);\n                    }\n                    if (y != null) {\n                        this._textY = parseFloat(y);\n                    }\n                    var dx = xmlNode.getAttribute('dx') || '0';\n                    var dy = xmlNode.getAttribute('dy') || '0';\n                    var g = new Group();\n                    inheritStyle(parentGroup, g);\n                    parseAttributes(xmlNode, g, this._defsUsePending, false, true);\n                    this._textX += parseFloat(dx);\n                    this._textY += parseFloat(dy);\n                    return g;\n                },\n                'path': function (xmlNode, parentGroup) {\n                    var d = xmlNode.getAttribute('d') || '';\n                    var path = createFromString(d);\n                    inheritStyle(parentGroup, path);\n                    parseAttributes(xmlNode, path, this._defsUsePending, false, false);\n                    path.silent = true;\n                    return path;\n                }\n            };\n        })();\n        return SVGParser;\n    }());\n    var paintServerParsers = {\n        'lineargradient': function (xmlNode) {\n            var x1 = parseInt(xmlNode.getAttribute('x1') || '0', 10);\n            var y1 = parseInt(xmlNode.getAttribute('y1') || '0', 10);\n            var x2 = parseInt(xmlNode.getAttribute('x2') || '10', 10);\n            var y2 = parseInt(xmlNode.getAttribute('y2') || '0', 10);\n            var gradient = new LinearGradient(x1, y1, x2, y2);\n            parsePaintServerUnit(xmlNode, gradient);\n            parseGradientColorStops(xmlNode, gradient);\n            return gradient;\n        },\n        'radialgradient': function (xmlNode) {\n            var cx = parseInt(xmlNode.getAttribute('cx') || '0', 10);\n            var cy = parseInt(xmlNode.getAttribute('cy') || '0', 10);\n            var r = parseInt(xmlNode.getAttribute('r') || '0', 10);\n            var gradient = new RadialGradient(cx, cy, r);\n            parsePaintServerUnit(xmlNode, gradient);\n            parseGradientColorStops(xmlNode, gradient);\n            return gradient;\n        }\n    };\n    function parsePaintServerUnit(xmlNode, gradient) {\n        var gradientUnits = xmlNode.getAttribute('gradientUnits');\n        if (gradientUnits === 'userSpaceOnUse') {\n            gradient.global = true;\n        }\n    }\n    function parseGradientColorStops(xmlNode, gradient) {\n        var stop = xmlNode.firstChild;\n        while (stop) {\n            if (stop.nodeType === 1\n                && stop.nodeName.toLocaleLowerCase() === 'stop') {\n                var offsetStr = stop.getAttribute('offset');\n                var offset = void 0;\n                if (offsetStr && offsetStr.indexOf('%') > 0) {\n                    offset = parseInt(offsetStr, 10) / 100;\n                }\n                else if (offsetStr) {\n                    offset = parseFloat(offsetStr);\n                }\n                else {\n                    offset = 0;\n                }\n                var styleVals = {};\n                parseInlineStyle(stop, styleVals, styleVals);\n                var stopColor = styleVals.stopColor\n                    || stop.getAttribute('stop-color')\n                    || '#000000';\n                gradient.colorStops.push({\n                    offset: offset,\n                    color: stopColor\n                });\n            }\n            stop = stop.nextSibling;\n        }\n    }\n    function inheritStyle(parent, child) {\n        if (parent && parent.__inheritedStyle) {\n            if (!child.__inheritedStyle) {\n                child.__inheritedStyle = {};\n            }\n            defaults(child.__inheritedStyle, parent.__inheritedStyle);\n        }\n    }\n    function parsePoints(pointsString) {\n        var list = splitNumberSequence(pointsString);\n        var points = [];\n        for (var i = 0; i < list.length; i += 2) {\n            var x = parseFloat(list[i]);\n            var y = parseFloat(list[i + 1]);\n            points.push([x, y]);\n        }\n        return points;\n    }\n    function parseAttributes(xmlNode, el, defsUsePending, onlyInlineStyle, isTextGroup) {\n        var disp = el;\n        var inheritedStyle = disp.__inheritedStyle = disp.__inheritedStyle || {};\n        var selfStyle = {};\n        if (xmlNode.nodeType === 1) {\n            parseTransformAttribute(xmlNode, el);\n            parseInlineStyle(xmlNode, inheritedStyle, selfStyle);\n            if (!onlyInlineStyle) {\n                parseAttributeStyle(xmlNode, inheritedStyle, selfStyle);\n            }\n        }\n        disp.style = disp.style || {};\n        if (inheritedStyle.fill != null) {\n            disp.style.fill = getFillStrokeStyle(disp, 'fill', inheritedStyle.fill, defsUsePending);\n        }\n        if (inheritedStyle.stroke != null) {\n            disp.style.stroke = getFillStrokeStyle(disp, 'stroke', inheritedStyle.stroke, defsUsePending);\n        }\n        each([\n            'lineWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'miterLimit', 'fontSize'\n        ], function (propName) {\n            if (inheritedStyle[propName] != null) {\n                disp.style[propName] = parseFloat(inheritedStyle[propName]);\n            }\n        });\n        each([\n            'lineDashOffset', 'lineCap', 'lineJoin', 'fontWeight', 'fontFamily', 'fontStyle', 'textAlign'\n        ], function (propName) {\n            if (inheritedStyle[propName] != null) {\n                disp.style[propName] = inheritedStyle[propName];\n            }\n        });\n        if (isTextGroup) {\n            disp.__selfStyle = selfStyle;\n        }\n        if (inheritedStyle.lineDash) {\n            disp.style.lineDash = map(splitNumberSequence(inheritedStyle.lineDash), function (str) {\n                return parseFloat(str);\n            });\n        }\n        if (inheritedStyle.visibility === 'hidden' || inheritedStyle.visibility === 'collapse') {\n            disp.invisible = true;\n        }\n        if (inheritedStyle.display === 'none') {\n            disp.ignore = true;\n        }\n    }\n    function applyTextAlignment(text, parentGroup) {\n        var parentSelfStyle = parentGroup.__selfStyle;\n        if (parentSelfStyle) {\n            var textBaseline = parentSelfStyle.textBaseline;\n            var zrTextBaseline = textBaseline;\n            if (!textBaseline || textBaseline === 'auto') {\n                zrTextBaseline = 'alphabetic';\n            }\n            else if (textBaseline === 'baseline') {\n                zrTextBaseline = 'alphabetic';\n            }\n            else if (textBaseline === 'before-edge' || textBaseline === 'text-before-edge') {\n                zrTextBaseline = 'top';\n            }\n            else if (textBaseline === 'after-edge' || textBaseline === 'text-after-edge') {\n                zrTextBaseline = 'bottom';\n            }\n            else if (textBaseline === 'central' || textBaseline === 'mathematical') {\n                zrTextBaseline = 'middle';\n            }\n            text.style.textBaseline = zrTextBaseline;\n        }\n        var parentInheritedStyle = parentGroup.__inheritedStyle;\n        if (parentInheritedStyle) {\n            var textAlign = parentInheritedStyle.textAlign;\n            var zrTextAlign = textAlign;\n            if (textAlign) {\n                if (textAlign === 'middle') {\n                    zrTextAlign = 'center';\n                }\n                text.style.textAlign = zrTextAlign;\n            }\n        }\n    }\n    var urlRegex = /^url\\(\\s*#(.*?)\\)/;\n    function getFillStrokeStyle(el, method, str, defsUsePending) {\n        var urlMatch = str && str.match(urlRegex);\n        if (urlMatch) {\n            var url = trim(urlMatch[1]);\n            defsUsePending.push([el, method, url]);\n            return;\n        }\n        if (str === 'none') {\n            str = null;\n        }\n        return str;\n    }\n    function applyDefs(defs, defsUsePending) {\n        for (var i = 0; i < defsUsePending.length; i++) {\n            var item = defsUsePending[i];\n            item[0].style[item[1]] = defs[item[2]];\n        }\n    }\n    var numberReg$1 = /-?([0-9]*\\.)?[0-9]+([eE]-?[0-9]+)?/g;\n    function splitNumberSequence(rawStr) {\n        return rawStr.match(numberReg$1) || [];\n    }\n    var transformRegex = /(translate|scale|rotate|skewX|skewY|matrix)\\(([\\-\\s0-9\\.eE,]*)\\)/g;\n    var DEGREE_TO_ANGLE = Math.PI / 180;\n    function parseTransformAttribute(xmlNode, node) {\n        var transform = xmlNode.getAttribute('transform');\n        if (transform) {\n            transform = transform.replace(/,/g, ' ');\n            var transformOps_1 = [];\n            var mt = null;\n            transform.replace(transformRegex, function (str, type, value) {\n                transformOps_1.push(type, value);\n                return '';\n            });\n            for (var i = transformOps_1.length - 1; i > 0; i -= 2) {\n                var value = transformOps_1[i];\n                var type = transformOps_1[i - 1];\n                var valueArr = splitNumberSequence(value);\n                mt = mt || create$1();\n                switch (type) {\n                    case 'translate':\n                        translate(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || '0')]);\n                        break;\n                    case 'scale':\n                        scale$1(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || valueArr[0])]);\n                        break;\n                    case 'rotate':\n                        rotate(mt, mt, -parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);\n                        break;\n                    case 'skewX':\n                        var sx = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);\n                        mul$1(mt, [1, 0, sx, 1, 0, 0], mt);\n                        break;\n                    case 'skewY':\n                        var sy = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);\n                        mul$1(mt, [1, sy, 0, 1, 0, 0], mt);\n                        break;\n                    case 'matrix':\n                        mt[0] = parseFloat(valueArr[0]);\n                        mt[1] = parseFloat(valueArr[1]);\n                        mt[2] = parseFloat(valueArr[2]);\n                        mt[3] = parseFloat(valueArr[3]);\n                        mt[4] = parseFloat(valueArr[4]);\n                        mt[5] = parseFloat(valueArr[5]);\n                        break;\n                }\n            }\n            node.setLocalTransform(mt);\n        }\n    }\n    var styleRegex = /([^\\s:;]+)\\s*:\\s*([^:;]+)/g;\n    function parseInlineStyle(xmlNode, inheritableStyleResult, selfStyleResult) {\n        var style = xmlNode.getAttribute('style');\n        if (!style) {\n            return;\n        }\n        styleRegex.lastIndex = 0;\n        var styleRegResult;\n        while ((styleRegResult = styleRegex.exec(style)) != null) {\n            var svgStlAttr = styleRegResult[1];\n            var zrInheritableStlAttr = hasOwn(INHERITABLE_STYLE_ATTRIBUTES_MAP, svgStlAttr)\n                ? INHERITABLE_STYLE_ATTRIBUTES_MAP[svgStlAttr]\n                : null;\n            if (zrInheritableStlAttr) {\n                inheritableStyleResult[zrInheritableStlAttr] = styleRegResult[2];\n            }\n            var zrSelfStlAttr = hasOwn(SELF_STYLE_ATTRIBUTES_MAP, svgStlAttr)\n                ? SELF_STYLE_ATTRIBUTES_MAP[svgStlAttr]\n                : null;\n            if (zrSelfStlAttr) {\n                selfStyleResult[zrSelfStlAttr] = styleRegResult[2];\n            }\n        }\n    }\n    function parseAttributeStyle(xmlNode, inheritableStyleResult, selfStyleResult) {\n        for (var i = 0; i < INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) {\n            var svgAttrName = INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS[i];\n            var attrValue = xmlNode.getAttribute(svgAttrName);\n            if (attrValue != null) {\n                inheritableStyleResult[INHERITABLE_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue;\n            }\n        }\n        for (var i = 0; i < SELF_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) {\n            var svgAttrName = SELF_STYLE_ATTRIBUTES_MAP_KEYS[i];\n            var attrValue = xmlNode.getAttribute(svgAttrName);\n            if (attrValue != null) {\n                selfStyleResult[SELF_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue;\n            }\n        }\n    }\n    function makeViewBoxTransform(viewBoxRect, boundingRect) {\n        var scaleX = boundingRect.width / viewBoxRect.width;\n        var scaleY = boundingRect.height / viewBoxRect.height;\n        var scale = Math.min(scaleX, scaleY);\n        return {\n            scale: scale,\n            x: -(viewBoxRect.x + viewBoxRect.width / 2) * scale + (boundingRect.x + boundingRect.width / 2),\n            y: -(viewBoxRect.y + viewBoxRect.height / 2) * scale + (boundingRect.y + boundingRect.height / 2)\n        };\n    }\n    function parseSVG(xml, opt) {\n        var parser = new SVGParser();\n        return parser.parse(xml, opt);\n    }\n\n    /**\n     * \"region available\" means that: enable users to set attribute `name=\"xxx\"` on those tags\n     * to make it be a region.\n     * 1. region styles and its label styles can be defined in echarts opton:\n     * ```js\n     * geo: {\n     *     regions: [{\n     *         name: 'xxx',\n     *         itemStyle: { ... },\n     *         label: { ... }\n     *     }, {\n     *         ...\n     *     },\n     *     ...]\n     * };\n     * ```\n     * 2. name can be duplicated in different SVG tag. All of the tags with the same name share\n     * a region option. For exampel if there are two <path> representing two lung lobes. They have\n     * no common parents but both of them need to display label \"lung\" inside.\n     */\n\n    var REGION_AVAILABLE_SVG_TAG_MAP = createHashMap(['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path', // <text> <tspan> are also enabled because some SVG might paint text itself,\n    // but still need to trigger events or tooltip.\n    'text', 'tspan', // <g> is also enabled because this case: if multiple tags share one name\n    // and need label displayed, every tags will display the name, which is not\n    // expected. So we can put them into a <g name=\"xxx\">. Thereby only one label\n    // displayed and located based on the bounding rect of the <g>.\n    'g']);\n\n    var GeoSVGResource =\n    /** @class */\n    function () {\n      function GeoSVGResource(mapName, svg) {\n        this.type = 'geoSVG'; // All used graphics. key: hostKey, value: root\n\n        this._usedGraphicMap = createHashMap(); // All unused graphics.\n\n        this._freedGraphics = [];\n        this._mapName = mapName; // Only perform parse to XML object here, which might be time\n        // consiming for large SVG.\n        // Although convert XML to zrender element is also time consiming,\n        // if we do it here, the clone of zrender elements has to be\n        // required. So we do it once for each geo instance, util real\n        // performance issues call for optimizing it.\n\n        this._parsedXML = parseXML(svg);\n      }\n\n      GeoSVGResource.prototype.load = function ()\n      /* nameMap: NameMap */\n      {\n        // In the \"load\" stage, graphic need to be built to\n        // get boundingRect for geo coordinate system.\n        var firstGraphic = this._firstGraphic; // Create the return data structure only when first graphic created.\n        // Because they will be used in geo coordinate system update stage,\n        // and `regions` will be mounted at `geo` coordinate system,\n        // in which there is no \"view\" info, so that it should better not to\n        // make references to graphic elements.\n\n        if (!firstGraphic) {\n          firstGraphic = this._firstGraphic = this._buildGraphic(this._parsedXML);\n\n          this._freedGraphics.push(firstGraphic);\n\n          this._boundingRect = this._firstGraphic.boundingRect.clone(); // PENDING: `nameMap` will not be supported until some real requirement come.\n          // if (nameMap) {\n          //     named = applyNameMap(named, nameMap);\n          // }\n\n          var _a = createRegions(firstGraphic.named),\n              regions = _a.regions,\n              regionsMap = _a.regionsMap;\n\n          this._regions = regions;\n          this._regionsMap = regionsMap;\n        }\n\n        return {\n          boundingRect: this._boundingRect,\n          regions: this._regions,\n          regionsMap: this._regionsMap\n        };\n      };\n\n      GeoSVGResource.prototype._buildGraphic = function (svgXML) {\n        var result;\n        var rootFromParse;\n\n        try {\n          result = svgXML && parseSVG(svgXML, {\n            ignoreViewBox: true,\n            ignoreRootClip: true\n          }) || {};\n          rootFromParse = result.root;\n          assert(rootFromParse != null);\n        } catch (e) {\n          throw new Error('Invalid svg format\\n' + e.message);\n        } // Note: we keep the covenant that the root has no transform. So always add an extra root.\n\n\n        var root = new Group();\n        root.add(rootFromParse);\n        root.isGeoSVGGraphicRoot = true; // [THE_RULE_OF_VIEWPORT_AND_VIEWBOX]\n        //\n        // Consider: `<svg width=\"...\" height=\"...\" viewBox=\"...\">`\n        // - the `width/height` we call it `svgWidth/svgHeight` for short.\n        // - `(0, 0, svgWidth, svgHeight)` defines the viewport of the SVG, or say,\n        //   \"viewport boundingRect\", or `boundingRect` for short.\n        // - `viewBox` defines the transform from the real content ot the viewport.\n        //   `viewBox` has the same unit as the content of SVG.\n        //   If `viewBox` exists, a transform is defined, so the unit of `svgWidth/svgHeight` become\n        //   different from the content of SVG. Otherwise, they are the same.\n        //\n        // If both `svgWidth/svgHeight/viewBox` are specified in a SVG file, the transform rule will be:\n        // 0. `boundingRect` is `(0, 0, svgWidth, svgHeight)`. Set it to Geo['_rect'] (View['_rect']).\n        // 1. Make a transform from `viewBox` to `boundingRect`.\n        //    Note: only support `preserveAspectRatio 'xMidYMid'` here. That is, this transform will preserve\n        //    the aspect ratio.\n        // 2. Make a transform from boundingRect to Geo['_viewRect'] (View['_viewRect'])\n        //    (`Geo`/`View` will do this job).\n        //    Note: this transform might not preserve aspect radio, which depending on how users specify\n        //    viewRect in echarts option (e.g., `geo.left/top/width/height` will not preserve aspect ratio,\n        //    but `geo.layoutCenter/layoutSize` will preserve aspect ratio).\n        //\n        // If `svgWidth/svgHeight` not specified, we use `viewBox` as the `boundingRect` to make the SVG\n        // layout look good.\n        //\n        // If neither `svgWidth/svgHeight` nor `viewBox` are not specified, we calculate the boundingRect\n        // of the SVG content and use them to make SVG layout look good.\n\n        var svgWidth = result.width;\n        var svgHeight = result.height;\n        var viewBoxRect = result.viewBoxRect;\n        var boundingRect = this._boundingRect;\n\n        if (!boundingRect) {\n          var bRectX = void 0;\n          var bRectY = void 0;\n          var bRectWidth = void 0;\n          var bRectHeight = void 0;\n\n          if (svgWidth != null) {\n            bRectX = 0;\n            bRectWidth = svgWidth;\n          } else if (viewBoxRect) {\n            bRectX = viewBoxRect.x;\n            bRectWidth = viewBoxRect.width;\n          }\n\n          if (svgHeight != null) {\n            bRectY = 0;\n            bRectHeight = svgHeight;\n          } else if (viewBoxRect) {\n            bRectY = viewBoxRect.y;\n            bRectHeight = viewBoxRect.height;\n          } // If both viewBox and svgWidth/svgHeight not specified,\n          // we have to determine how to layout those element to make them look good.\n\n\n          if (bRectX == null || bRectY == null) {\n            var calculatedBoundingRect = rootFromParse.getBoundingRect();\n\n            if (bRectX == null) {\n              bRectX = calculatedBoundingRect.x;\n              bRectWidth = calculatedBoundingRect.width;\n            }\n\n            if (bRectY == null) {\n              bRectY = calculatedBoundingRect.y;\n              bRectHeight = calculatedBoundingRect.height;\n            }\n          }\n\n          boundingRect = this._boundingRect = new BoundingRect(bRectX, bRectY, bRectWidth, bRectHeight);\n        }\n\n        if (viewBoxRect) {\n          var viewBoxTransform = makeViewBoxTransform(viewBoxRect, boundingRect); // Only support `preserveAspectRatio 'xMidYMid'`\n\n          rootFromParse.scaleX = rootFromParse.scaleY = viewBoxTransform.scale;\n          rootFromParse.x = viewBoxTransform.x;\n          rootFromParse.y = viewBoxTransform.y;\n        } // SVG needs to clip based on `viewBox`. And some SVG files really rely on this feature.\n        // They do not strictly confine all of the content inside a display rect, but deliberately\n        // use a `viewBox` to define a displayable rect.\n        // PENDING:\n        // The drawback of the `setClipPath` here is: the region label (genereted by echarts) near the\n        // edge might also be clipped, because region labels are put as `textContent` of the SVG path.\n\n\n        root.setClipPath(new Rect({\n          shape: boundingRect.plain()\n        }));\n        var named = [];\n        each(result.named, function (namedItem) {\n          if (REGION_AVAILABLE_SVG_TAG_MAP.get(namedItem.svgNodeTagLower) != null) {\n            named.push(namedItem);\n            setSilent(namedItem.el);\n          }\n        });\n        return {\n          root: root,\n          boundingRect: boundingRect,\n          named: named\n        };\n      };\n      /**\n       * Consider:\n       * (1) One graphic element can not be shared by different `geoView` running simultaneously.\n       *     Notice, also need to consider multiple echarts instances share a `mapRecord`.\n       * (2) Converting SVG to graphic elements is time consuming.\n       * (3) In the current architecture, `load` should be called frequently to get boundingRect,\n       *     and it is called without view info.\n       * So we maintain graphic elements in this module, and enables `view` to use/return these\n       * graphics from/to the pool with it's uid.\n       */\n\n\n      GeoSVGResource.prototype.useGraphic = function (hostKey\n      /* , nameMap: NameMap */\n      ) {\n        var usedRootMap = this._usedGraphicMap;\n        var svgGraphic = usedRootMap.get(hostKey);\n\n        if (svgGraphic) {\n          return svgGraphic;\n        }\n\n        svgGraphic = this._freedGraphics.pop() // use the first boundingRect to avoid duplicated boundingRect calculation.\n        || this._buildGraphic(this._parsedXML);\n        usedRootMap.set(hostKey, svgGraphic); // PENDING: `nameMap` will not be supported until some real requirement come.\n        // `nameMap` can only be obtained from echarts option.\n        // The original `named` must not be modified.\n        // if (nameMap) {\n        //     svgGraphic = extend({}, svgGraphic);\n        //     svgGraphic.named = applyNameMap(svgGraphic.named, nameMap);\n        // }\n\n        return svgGraphic;\n      };\n\n      GeoSVGResource.prototype.freeGraphic = function (hostKey) {\n        var usedRootMap = this._usedGraphicMap;\n        var svgGraphic = usedRootMap.get(hostKey);\n\n        if (svgGraphic) {\n          usedRootMap.removeKey(hostKey);\n\n          this._freedGraphics.push(svgGraphic);\n        }\n      };\n\n      return GeoSVGResource;\n    }();\n\n    function setSilent(el) {\n      // Only named element has silent: false, other elements should\n      // act as background and has no user interaction.\n      el.silent = false; // text|tspan will be converted to group.\n\n      if (el.isGroup) {\n        el.traverse(function (child) {\n          child.silent = false;\n        });\n      }\n    }\n\n    function createRegions(named) {\n      var regions = [];\n      var regionsMap = createHashMap(); // Create resions only for the first graphic.\n\n      each(named, function (namedItem) {\n        // Region has feature to calculate center for tooltip or other features.\n        // If there is a <g name=\"xxx\">, the center should be the center of the\n        // bounding rect of the g.\n        if (namedItem.namedFrom != null) {\n          return;\n        }\n\n        var region = new GeoSVGRegion(namedItem.name, namedItem.el); // PENDING: if `nameMap` supported, this region can not be mounted on\n        // `this`, but can only be created each time `load()` called.\n\n        regions.push(region); // PENDING: if multiple tag named with the same name, only one will be\n        // found by `_regionsMap`. `_regionsMap` is used to find a coordinate\n        // by name. We use `region.getCenter()` as the coordinate.\n\n        regionsMap.set(namedItem.name, region);\n      });\n      return {\n        regions: regions,\n        regionsMap: regionsMap\n      };\n    } // PENDING: `nameMap` will not be supported until some real requirement come.\n    // /**\n    //  * Use the alias in geoNameMap.\n    //  * The input `named` must not be modified.\n    //  */\n    // function applyNameMap(\n    //     named: GeoSVGGraphicRecord['named'],\n    //     nameMap: NameMap\n    // ): GeoSVGGraphicRecord['named'] {\n    //     const result = [] as GeoSVGGraphicRecord['named'];\n    //     for (let i = 0; i < named.length; i++) {\n    //         let regionGraphic = named[i];\n    //         const name = regionGraphic.name;\n    //         if (nameMap && nameMap.hasOwnProperty(name)) {\n    //             regionGraphic = extend({}, regionGraphic);\n    //             regionGraphic.name = name;\n    //         }\n    //         result.push(regionGraphic);\n    //     }\n    //     return result;\n    // }\n\n    var geoCoord = [126, 25];\n    var nanhaiName = '南海诸岛';\n    var points$1 = [[[0, 3.5], [7, 11.2], [15, 11.9], [30, 7], [42, 0.7], [52, 0.7], [56, 7.7], [59, 0.7], [64, 0.7], [64, 0], [5, 0], [0, 3.5]], [[13, 16.1], [19, 14.7], [16, 21.7], [11, 23.1], [13, 16.1]], [[12, 32.2], [14, 38.5], [15, 38.5], [13, 32.2], [12, 32.2]], [[16, 47.6], [12, 53.2], [13, 53.2], [18, 47.6], [16, 47.6]], [[6, 64.4], [8, 70], [9, 70], [8, 64.4], [6, 64.4]], [[23, 82.6], [29, 79.8], [30, 79.8], [25, 82.6], [23, 82.6]], [[37, 70.7], [43, 62.3], [44, 62.3], [39, 70.7], [37, 70.7]], [[48, 51.1], [51, 45.5], [53, 45.5], [50, 51.1], [48, 51.1]], [[51, 35], [51, 28.7], [53, 28.7], [53, 35], [51, 35]], [[52, 22.4], [55, 17.5], [56, 17.5], [53, 22.4], [52, 22.4]], [[58, 12.6], [62, 7], [63, 7], [60, 12.6], [58, 12.6]], [[0, 3.5], [0, 93.1], [64, 93.1], [64, 0], [63, 0], [63, 92.4], [1, 92.4], [1, 3.5], [0, 3.5]]];\n\n    for (var i = 0; i < points$1.length; i++) {\n      for (var k = 0; k < points$1[i].length; k++) {\n        points$1[i][k][0] /= 10.5;\n        points$1[i][k][1] /= -10.5 / 0.75;\n        points$1[i][k][0] += geoCoord[0];\n        points$1[i][k][1] += geoCoord[1];\n      }\n    }\n\n    function fixNanhai(mapType, regions) {\n      if (mapType === 'china') {\n        for (var i = 0; i < regions.length; i++) {\n          // Already exists.\n          if (regions[i].name === nanhaiName) {\n            return;\n          }\n        }\n\n        regions.push(new GeoJSONRegion(nanhaiName, map(points$1, function (exterior) {\n          return {\n            type: 'polygon',\n            exterior: exterior\n          };\n        }), geoCoord));\n      }\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var coordsOffsetMap = {\n      '南海诸岛': [32, 80],\n      // 全国\n      '广东': [0, -10],\n      '香港': [10, 5],\n      '澳门': [-10, 10],\n      // '北京': [-10, 0],\n      '天津': [5, 5]\n    };\n    function fixTextCoords(mapType, region) {\n      if (mapType === 'china') {\n        var coordFix = coordsOffsetMap[region.name];\n\n        if (coordFix) {\n          var cp = region.getCenter();\n          cp[0] += coordFix[0] / 10.5;\n          cp[1] += -coordFix[1] / (10.5 / 0.75);\n          region.setCenter(cp);\n        }\n      }\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    // Fix for 钓鱼岛\n    // let Region = require('../Region');\n    // let zrUtil = require('zrender/lib/core/util');\n    // let geoCoord = [126, 25];\n    var points$2 = [[[123.45165252685547, 25.73527164402261], [123.49731445312499, 25.73527164402261], [123.49731445312499, 25.750734064600884], [123.45165252685547, 25.750734064600884], [123.45165252685547, 25.73527164402261]]];\n    function fixDiaoyuIsland(mapType, region) {\n      if (mapType === 'china' && region.name === '台湾') {\n        region.geometries.push({\n          type: 'polygon',\n          exterior: points$2[0]\n        });\n      }\n    }\n\n    var DEFAULT_NAME_PROPERTY = 'name';\n\n    var GeoJSONResource =\n    /** @class */\n    function () {\n      function GeoJSONResource(mapName, geoJSON, specialAreas) {\n        this.type = 'geoJSON';\n        this._parsedMap = createHashMap();\n        this._mapName = mapName;\n        this._specialAreas = specialAreas; // PENDING: delay the parse to the first usage to rapid up the FMP?\n\n        this._geoJSON = parseInput(geoJSON);\n      }\n      /**\n       * @param nameMap can be null/undefined\n       * @param nameProperty can be null/undefined\n       */\n\n\n      GeoJSONResource.prototype.load = function (nameMap, nameProperty) {\n        nameProperty = nameProperty || DEFAULT_NAME_PROPERTY;\n\n        var parsed = this._parsedMap.get(nameProperty);\n\n        if (!parsed) {\n          var rawRegions = this._parseToRegions(nameProperty);\n\n          parsed = this._parsedMap.set(nameProperty, {\n            regions: rawRegions,\n            boundingRect: calculateBoundingRect(rawRegions)\n          });\n        }\n\n        var regionsMap = createHashMap();\n        var finalRegions = [];\n        each(parsed.regions, function (region) {\n          var regionName = region.name; // Try use the alias in geoNameMap\n\n          if (nameMap && hasOwn(nameMap, regionName)) {\n            region = region.cloneShallow(regionName = nameMap[regionName]);\n          }\n\n          finalRegions.push(region);\n          regionsMap.set(regionName, region);\n        });\n        return {\n          regions: finalRegions,\n          boundingRect: parsed.boundingRect || new BoundingRect(0, 0, 0, 0),\n          regionsMap: regionsMap\n        };\n      };\n\n      GeoJSONResource.prototype._parseToRegions = function (nameProperty) {\n        var mapName = this._mapName;\n        var geoJSON = this._geoJSON;\n        var rawRegions; // https://jsperf.com/try-catch-performance-overhead\n\n        try {\n          rawRegions = geoJSON ? parseGeoJSON(geoJSON, nameProperty) : [];\n        } catch (e) {\n          throw new Error('Invalid geoJson format\\n' + e.message);\n        }\n\n        fixNanhai(mapName, rawRegions);\n        each(rawRegions, function (region) {\n          var regionName = region.name;\n          fixTextCoords(mapName, region);\n          fixDiaoyuIsland(mapName, region); // Some area like Alaska in USA map needs to be tansformed\n          // to look better\n\n          var specialArea = this._specialAreas && this._specialAreas[regionName];\n\n          if (specialArea) {\n            region.transformTo(specialArea.left, specialArea.top, specialArea.width, specialArea.height);\n          }\n        }, this);\n        return rawRegions;\n      };\n      /**\n       * Only for exporting to users.\n       * **MUST NOT** used internally.\n       */\n\n\n      GeoJSONResource.prototype.getMapForUser = function () {\n        return {\n          // For backward compatibility, use geoJson\n          // PENDING: it has been returning them without clone.\n          // do we need to avoid outsite modification?\n          geoJson: this._geoJSON,\n          geoJSON: this._geoJSON,\n          specialAreas: this._specialAreas\n        };\n      };\n\n      return GeoJSONResource;\n    }();\n\n    function calculateBoundingRect(regions) {\n      var rect;\n\n      for (var i = 0; i < regions.length; i++) {\n        var regionRect = regions[i].getBoundingRect();\n        rect = rect || regionRect.clone();\n        rect.union(regionRect);\n      }\n\n      return rect;\n    }\n\n    function parseInput(source) {\n      return !isString(source) ? source : typeof JSON !== 'undefined' && JSON.parse ? JSON.parse(source) : new Function('return (' + source + ');')();\n    }\n\n    var storage = createHashMap();\n    var geoSourceManager = {\n      /**\n       * Compatible with previous `echarts.registerMap`.\n       *\n       * @usage\n       * ```js\n       *\n       * echarts.registerMap('USA', geoJson, specialAreas);\n       *\n       * echarts.registerMap('USA', {\n       *     geoJson: geoJson,\n       *     specialAreas: {...}\n       * });\n       * echarts.registerMap('USA', {\n       *     geoJSON: geoJson,\n       *     specialAreas: {...}\n       * });\n       *\n       * echarts.registerMap('airport', {\n       *     svg: svg\n       * }\n       * ```\n       *\n       * Note:\n       * Do not support that register multiple geoJSON or SVG\n       * one map name. Because different geoJSON and SVG have\n       * different unit. It's not easy to make sure how those\n       * units are mapping/normalize.\n       * If intending to use multiple geoJSON or SVG, we can\n       * use multiple geo coordinate system.\n       */\n      registerMap: function (mapName, rawDef, rawSpecialAreas) {\n        if (rawDef.svg) {\n          var resource = new GeoSVGResource(mapName, rawDef.svg);\n          storage.set(mapName, resource);\n        } else {\n          // Recommend:\n          //     echarts.registerMap('eu', { geoJSON: xxx, specialAreas: xxx });\n          // Backward compatibility:\n          //     echarts.registerMap('eu', geoJSON, specialAreas);\n          //     echarts.registerMap('eu', { geoJson: xxx, specialAreas: xxx });\n          var geoJSON = rawDef.geoJson || rawDef.geoJSON;\n\n          if (geoJSON && !rawDef.features) {\n            rawSpecialAreas = rawDef.specialAreas;\n          } else {\n            geoJSON = rawDef;\n          }\n\n          var resource = new GeoJSONResource(mapName, geoJSON, rawSpecialAreas);\n          storage.set(mapName, resource);\n        }\n      },\n      getGeoResource: function (mapName) {\n        return storage.get(mapName);\n      },\n\n      /**\n       * Only for exporting to users.\n       * **MUST NOT** used internally.\n       */\n      getMapForUser: function (mapName) {\n        var resource = storage.get(mapName); // Do not support return SVG until some real requirement come.\n\n        return resource && resource.type === 'geoJSON' && resource.getMapForUser();\n      },\n      load: function (mapName, nameMap, nameProperty) {\n        var resource = storage.get(mapName);\n\n        if (!resource) {\n          if (\"development\" !== 'production') {\n            console.error('Map ' + mapName + ' not exists. The GeoJSON of the map must be provided.');\n          }\n\n          return;\n        }\n\n        return resource.load(nameMap, nameProperty);\n      }\n    };\n\n    /**\n     * Only these tags enable use `itemStyle` if they are named in SVG.\n     * Other tags like <text> <tspan> <image> might not suitable for `itemStyle`.\n     * They will not be considered to be styled until some requirements come.\n     */\n\n    var OPTION_STYLE_ENABLED_TAGS = ['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path'];\n    var OPTION_STYLE_ENABLED_TAG_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS);\n    var STATE_TRIGGER_TAG_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g']));\n    var LABEL_HOST_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g']));\n    var mapLabelRaw = makeInner();\n\n    function getFixedItemStyle(model) {\n      var itemStyle = model.getItemStyle();\n      var areaColor = model.get('areaColor'); // If user want the color not to be changed when hover,\n      // they should both set areaColor and color to be null.\n\n      if (areaColor != null) {\n        itemStyle.fill = areaColor;\n      }\n\n      return itemStyle;\n    } // Only stroke can be used for line.\n    // Using fill in style if stroke not exits.\n    // TODO Not sure yet. Perhaps a separate `lineStyle`?\n\n\n    function fixLineStyle(styleHost) {\n      var style = styleHost.style;\n\n      if (style) {\n        style.stroke = style.stroke || style.fill;\n        style.fill = null;\n      }\n    }\n\n    var MapDraw =\n    /** @class */\n    function () {\n      function MapDraw(api) {\n        var group = new Group();\n        this.uid = getUID('ec_map_draw');\n        this._controller = new RoamController(api.getZr());\n        this._controllerHost = {\n          target: group\n        };\n        this.group = group;\n        group.add(this._regionsGroup = new Group());\n        group.add(this._svgGroup = new Group());\n      }\n\n      MapDraw.prototype.draw = function (mapOrGeoModel, ecModel, api, fromView, payload) {\n        var isGeo = mapOrGeoModel.mainType === 'geo'; // Map series has data. GEO model that controlled by map series\n        // will be assigned with map data. Other GEO model has no data.\n\n        var data = mapOrGeoModel.getData && mapOrGeoModel.getData();\n        isGeo && ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'map'\n        }, function (mapSeries) {\n          if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) {\n            data = mapSeries.getData();\n          }\n        });\n        var geo = mapOrGeoModel.coordinateSystem;\n        var regionsGroup = this._regionsGroup;\n        var group = this.group;\n        var transformInfo = geo.getTransformInfo();\n        var transformInfoRaw = transformInfo.raw;\n        var transformInfoRoam = transformInfo.roam; // No animation when first draw or in action\n\n        var isFirstDraw = !regionsGroup.childAt(0) || payload;\n\n        if (isFirstDraw) {\n          group.x = transformInfoRoam.x;\n          group.y = transformInfoRoam.y;\n          group.scaleX = transformInfoRoam.scaleX;\n          group.scaleY = transformInfoRoam.scaleY;\n          group.dirty();\n        } else {\n          updateProps(group, transformInfoRoam, mapOrGeoModel);\n        }\n\n        var isVisualEncodedByVisualMap = data && data.getVisual('visualMeta') && data.getVisual('visualMeta').length > 0;\n        var viewBuildCtx = {\n          api: api,\n          geo: geo,\n          mapOrGeoModel: mapOrGeoModel,\n          data: data,\n          isVisualEncodedByVisualMap: isVisualEncodedByVisualMap,\n          isGeo: isGeo,\n          transformInfoRaw: transformInfoRaw\n        };\n\n        if (geo.resourceType === 'geoJSON') {\n          this._buildGeoJSON(viewBuildCtx);\n        } else if (geo.resourceType === 'geoSVG') {\n          this._buildSVG(viewBuildCtx);\n        }\n\n        this._updateController(mapOrGeoModel, ecModel, api);\n\n        this._updateMapSelectHandler(mapOrGeoModel, regionsGroup, api, fromView);\n      };\n\n      MapDraw.prototype._buildGeoJSON = function (viewBuildCtx) {\n        var regionsGroupByName = this._regionsGroupByName = createHashMap();\n        var regionsInfoByName = createHashMap();\n        var regionsGroup = this._regionsGroup;\n        var transformInfoRaw = viewBuildCtx.transformInfoRaw;\n        var mapOrGeoModel = viewBuildCtx.mapOrGeoModel;\n        var data = viewBuildCtx.data;\n        var projection = viewBuildCtx.geo.projection;\n        var projectionStream = projection && projection.stream;\n\n        function transformPoint(point, project) {\n          if (project) {\n            // projection may return null point.\n            point = project(point);\n          }\n\n          return point && [point[0] * transformInfoRaw.scaleX + transformInfoRaw.x, point[1] * transformInfoRaw.scaleY + transformInfoRaw.y];\n        }\n\n        function transformPolygonPoints(inPoints) {\n          var outPoints = []; // If projectionStream is provided. Use it instead of single point project.\n\n          var project = !projectionStream && projection && projection.project;\n\n          for (var i = 0; i < inPoints.length; ++i) {\n            var newPt = transformPoint(inPoints[i], project);\n            newPt && outPoints.push(newPt);\n          }\n\n          return outPoints;\n        }\n\n        function getPolyShape(points) {\n          return {\n            shape: {\n              points: transformPolygonPoints(points)\n            }\n          };\n        }\n\n        regionsGroup.removeAll(); // Only when the resource is GeoJSON, there is `geo.regions`.\n\n        each(viewBuildCtx.geo.regions, function (region) {\n          var regionName = region.name; // Consider in GeoJson properties.name may be duplicated, for example,\n          // there is multiple region named \"United Kindom\" or \"France\" (so many\n          // colonies). And it is not appropriate to merge them in geo, which\n          // will make them share the same label and bring trouble in label\n          // location calculation.\n\n          var regionGroup = regionsGroupByName.get(regionName);\n\n          var _a = regionsInfoByName.get(regionName) || {},\n              dataIdx = _a.dataIdx,\n              regionModel = _a.regionModel;\n\n          if (!regionGroup) {\n            regionGroup = regionsGroupByName.set(regionName, new Group());\n            regionsGroup.add(regionGroup);\n            dataIdx = data ? data.indexOfName(regionName) : null;\n            regionModel = viewBuildCtx.isGeo ? mapOrGeoModel.getRegionModel(regionName) : data ? data.getItemModel(dataIdx) : null;\n            regionsInfoByName.set(regionName, {\n              dataIdx: dataIdx,\n              regionModel: regionModel\n            });\n          }\n\n          var polygonSubpaths = [];\n          var polylineSubpaths = [];\n          each(region.geometries, function (geometry) {\n            // Polygon and MultiPolygon\n            if (geometry.type === 'polygon') {\n              var polys = [geometry.exterior].concat(geometry.interiors || []);\n\n              if (projectionStream) {\n                polys = projectPolys(polys, projectionStream);\n              }\n\n              each(polys, function (poly) {\n                polygonSubpaths.push(new Polygon(getPolyShape(poly)));\n              });\n            } // LineString and MultiLineString\n            else {\n                var points = geometry.points;\n\n                if (projectionStream) {\n                  points = projectPolys(points, projectionStream, true);\n                }\n\n                each(points, function (points) {\n                  polylineSubpaths.push(new Polyline(getPolyShape(points)));\n                });\n              }\n          });\n          var centerPt = transformPoint(region.getCenter(), projection && projection.project);\n\n          function createCompoundPath(subpaths, isLine) {\n            if (!subpaths.length) {\n              return;\n            }\n\n            var compoundPath = new CompoundPath({\n              culling: true,\n              segmentIgnoreThreshold: 1,\n              shape: {\n                paths: subpaths\n              }\n            });\n            regionGroup.add(compoundPath);\n            applyOptionStyleForRegion(viewBuildCtx, compoundPath, dataIdx, regionModel);\n            resetLabelForRegion(viewBuildCtx, compoundPath, regionName, regionModel, mapOrGeoModel, dataIdx, centerPt);\n\n            if (isLine) {\n              fixLineStyle(compoundPath);\n              each(compoundPath.states, fixLineStyle);\n            }\n          }\n\n          createCompoundPath(polygonSubpaths);\n          createCompoundPath(polylineSubpaths, true);\n        }); // Ensure children have been added to `regionGroup` before calling them.\n\n        regionsGroupByName.each(function (regionGroup, regionName) {\n          var _a = regionsInfoByName.get(regionName),\n              dataIdx = _a.dataIdx,\n              regionModel = _a.regionModel;\n\n          resetEventTriggerForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel, dataIdx);\n          resetTooltipForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel);\n          resetStateTriggerForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel);\n        }, this);\n      };\n\n      MapDraw.prototype._buildSVG = function (viewBuildCtx) {\n        var mapName = viewBuildCtx.geo.map;\n        var transformInfoRaw = viewBuildCtx.transformInfoRaw;\n        this._svgGroup.x = transformInfoRaw.x;\n        this._svgGroup.y = transformInfoRaw.y;\n        this._svgGroup.scaleX = transformInfoRaw.scaleX;\n        this._svgGroup.scaleY = transformInfoRaw.scaleY;\n\n        if (this._svgResourceChanged(mapName)) {\n          this._freeSVG();\n\n          this._useSVG(mapName);\n        }\n\n        var svgDispatcherMap = this._svgDispatcherMap = createHashMap();\n        var focusSelf = false;\n        each(this._svgGraphicRecord.named, function (namedItem) {\n          // Note that we also allow different elements have the same name.\n          // For example, a glyph of a city and the label of the city have\n          // the same name and their tooltip info can be defined in a single\n          // region option.\n          var regionName = namedItem.name;\n          var mapOrGeoModel = viewBuildCtx.mapOrGeoModel;\n          var data = viewBuildCtx.data;\n          var svgNodeTagLower = namedItem.svgNodeTagLower;\n          var el = namedItem.el;\n          var dataIdx = data ? data.indexOfName(regionName) : null;\n          var regionModel = mapOrGeoModel.getRegionModel(regionName);\n\n          if (OPTION_STYLE_ENABLED_TAG_MAP.get(svgNodeTagLower) != null && el instanceof Displayable) {\n            applyOptionStyleForRegion(viewBuildCtx, el, dataIdx, regionModel);\n          }\n\n          if (el instanceof Displayable) {\n            el.culling = true;\n          } // We do not know how the SVG like so we'd better not to change z2.\n          // Otherwise it might bring some unexpected result. For example,\n          // an area hovered that make some inner city can not be clicked.\n\n\n          el.z2EmphasisLift = 0; // If self named:\n\n          if (!namedItem.namedFrom) {\n            // label should batter to be displayed based on the center of <g>\n            // if it is named rather than displayed on each child.\n            if (LABEL_HOST_MAP.get(svgNodeTagLower) != null) {\n              resetLabelForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx, null);\n            }\n\n            resetEventTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx);\n            resetTooltipForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel);\n\n            if (STATE_TRIGGER_TAG_MAP.get(svgNodeTagLower) != null) {\n              var focus_1 = resetStateTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel);\n\n              if (focus_1 === 'self') {\n                focusSelf = true;\n              }\n\n              var els = svgDispatcherMap.get(regionName) || svgDispatcherMap.set(regionName, []);\n              els.push(el);\n            }\n          }\n        }, this);\n\n        this._enableBlurEntireSVG(focusSelf, viewBuildCtx);\n      };\n\n      MapDraw.prototype._enableBlurEntireSVG = function (focusSelf, viewBuildCtx) {\n        // It's a little complicated to support blurring the entire geoSVG in series-map.\n        // So do not suport it until some requirements come.\n        // At present, in series-map, only regions can be blurred.\n        if (focusSelf && viewBuildCtx.isGeo) {\n          var blurStyle = viewBuildCtx.mapOrGeoModel.getModel(['blur', 'itemStyle']).getItemStyle(); // Only suport `opacity` here. Because not sure that other props are suitable for\n          // all of the elements generated by SVG (especially for Text/TSpan/Image/... ).\n\n          var opacity_1 = blurStyle.opacity;\n\n          this._svgGraphicRecord.root.traverse(function (el) {\n            if (!el.isGroup) {\n              // PENDING: clear those settings to SVG elements when `_freeSVG`.\n              // (Currently it happen not to be needed.)\n              setDefaultStateProxy(el);\n              var style = el.ensureState('blur').style || {}; // Do not overwrite the region style that already set from region option.\n\n              if (style.opacity == null && opacity_1 != null) {\n                style.opacity = opacity_1;\n              } // If `ensureState('blur').style = {}`, there will be default opacity.\n              // Enable `stateTransition` (animation).\n\n\n              el.ensureState('emphasis');\n            }\n          });\n        }\n      };\n\n      MapDraw.prototype.remove = function () {\n        this._regionsGroup.removeAll();\n\n        this._regionsGroupByName = null;\n\n        this._svgGroup.removeAll();\n\n        this._freeSVG();\n\n        this._controller.dispose();\n\n        this._controllerHost = null;\n      };\n\n      MapDraw.prototype.findHighDownDispatchers = function (name, geoModel) {\n        if (name == null) {\n          return [];\n        }\n\n        var geo = geoModel.coordinateSystem;\n\n        if (geo.resourceType === 'geoJSON') {\n          var regionsGroupByName = this._regionsGroupByName;\n\n          if (regionsGroupByName) {\n            var regionGroup = regionsGroupByName.get(name);\n            return regionGroup ? [regionGroup] : [];\n          }\n        } else if (geo.resourceType === 'geoSVG') {\n          return this._svgDispatcherMap && this._svgDispatcherMap.get(name) || [];\n        }\n      };\n\n      MapDraw.prototype._svgResourceChanged = function (mapName) {\n        return this._svgMapName !== mapName;\n      };\n\n      MapDraw.prototype._useSVG = function (mapName) {\n        var resource = geoSourceManager.getGeoResource(mapName);\n\n        if (resource && resource.type === 'geoSVG') {\n          var svgGraphic = resource.useGraphic(this.uid);\n\n          this._svgGroup.add(svgGraphic.root);\n\n          this._svgGraphicRecord = svgGraphic;\n          this._svgMapName = mapName;\n        }\n      };\n\n      MapDraw.prototype._freeSVG = function () {\n        var mapName = this._svgMapName;\n\n        if (mapName == null) {\n          return;\n        }\n\n        var resource = geoSourceManager.getGeoResource(mapName);\n\n        if (resource && resource.type === 'geoSVG') {\n          resource.freeGraphic(this.uid);\n        }\n\n        this._svgGraphicRecord = null;\n        this._svgDispatcherMap = null;\n\n        this._svgGroup.removeAll();\n\n        this._svgMapName = null;\n      };\n\n      MapDraw.prototype._updateController = function (mapOrGeoModel, ecModel, api) {\n        var geo = mapOrGeoModel.coordinateSystem;\n        var controller = this._controller;\n        var controllerHost = this._controllerHost; // @ts-ignore FIXME:TS\n\n        controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit');\n        controllerHost.zoom = geo.getZoom(); // roamType is will be set default true if it is null\n        // @ts-ignore FIXME:TS\n\n        controller.enable(mapOrGeoModel.get('roam') || false);\n        var mainType = mapOrGeoModel.mainType;\n\n        function makeActionBase() {\n          var action = {\n            type: 'geoRoam',\n            componentType: mainType\n          };\n          action[mainType + 'Id'] = mapOrGeoModel.id;\n          return action;\n        }\n\n        controller.off('pan').on('pan', function (e) {\n          this._mouseDownFlag = false;\n          updateViewOnPan(controllerHost, e.dx, e.dy);\n          api.dispatchAction(extend(makeActionBase(), {\n            dx: e.dx,\n            dy: e.dy,\n            animation: {\n              duration: 0\n            }\n          }));\n        }, this);\n        controller.off('zoom').on('zoom', function (e) {\n          this._mouseDownFlag = false;\n          updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);\n          api.dispatchAction(extend(makeActionBase(), {\n            zoom: e.scale,\n            originX: e.originX,\n            originY: e.originY,\n            animation: {\n              duration: 0\n            }\n          }));\n        }, this);\n        controller.setPointerChecker(function (e, x, y) {\n          return geo.containPoint([x, y]) && !onIrrelevantElement(e, api, mapOrGeoModel);\n        });\n      };\n      /**\n       * FIXME: this is a temporarily workaround.\n       * When `geoRoam` the elements need to be reset in `MapView['render']`, because the props like\n       * `ignore` might have been modified by `LabelManager`, and `LabelManager#addLabelsOfSeries`\n       * will subsequently cache `defaultAttr` like `ignore`. If do not do this reset, the modified\n       * props will have no chance to be restored.\n       * Note: This reset should be after `clearStates` in `renderSeries` because `useStates` in\n       * `renderSeries` will cache the modified `ignore` to `el._normalState`.\n       * TODO:\n       * Use clone/immutable in `LabelManager`?\n       */\n\n\n      MapDraw.prototype.resetForLabelLayout = function () {\n        this.group.traverse(function (el) {\n          var label = el.getTextContent();\n\n          if (label) {\n            label.ignore = mapLabelRaw(label).ignore;\n          }\n        });\n      };\n\n      MapDraw.prototype._updateMapSelectHandler = function (mapOrGeoModel, regionsGroup, api, fromView) {\n        var mapDraw = this;\n        regionsGroup.off('mousedown');\n        regionsGroup.off('click'); // @ts-ignore FIXME:TS resolve type conflict\n\n        if (mapOrGeoModel.get('selectedMode')) {\n          regionsGroup.on('mousedown', function () {\n            mapDraw._mouseDownFlag = true;\n          });\n          regionsGroup.on('click', function (e) {\n            if (!mapDraw._mouseDownFlag) {\n              return;\n            }\n\n            mapDraw._mouseDownFlag = false;\n          });\n        }\n      };\n\n      return MapDraw;\n    }();\n\n    function applyOptionStyleForRegion(viewBuildCtx, el, dataIndex, regionModel) {\n      // All of the path are using `itemStyle`, because\n      // (1) Some SVG also use fill on polyline (The different between\n      // polyline and polygon is \"open\" or \"close\" but not fill or not).\n      // (2) For the common props like opacity, if some use itemStyle\n      // and some use `lineStyle`, it might confuse users.\n      // (3) Most SVG use <path>, where can not detect wether draw a \"line\"\n      // or a filled shape, so use `itemStyle` for <path>.\n      var normalStyleModel = regionModel.getModel('itemStyle');\n      var emphasisStyleModel = regionModel.getModel(['emphasis', 'itemStyle']);\n      var blurStyleModel = regionModel.getModel(['blur', 'itemStyle']);\n      var selectStyleModel = regionModel.getModel(['select', 'itemStyle']); // NOTE: DONT use 'style' in visual when drawing map.\n      // This component is used for drawing underlying map for both geo component and map series.\n\n      var normalStyle = getFixedItemStyle(normalStyleModel);\n      var emphasisStyle = getFixedItemStyle(emphasisStyleModel);\n      var selectStyle = getFixedItemStyle(selectStyleModel);\n      var blurStyle = getFixedItemStyle(blurStyleModel); // Update the itemStyle if has data visual\n\n      var data = viewBuildCtx.data;\n\n      if (data) {\n        // Only visual color of each item will be used. It can be encoded by visualMap\n        // But visual color of series is used in symbol drawing\n        // Visual color for each series is for the symbol draw\n        var style = data.getItemVisual(dataIndex, 'style');\n        var decal = data.getItemVisual(dataIndex, 'decal');\n\n        if (viewBuildCtx.isVisualEncodedByVisualMap && style.fill) {\n          normalStyle.fill = style.fill;\n        }\n\n        if (decal) {\n          normalStyle.decal = createOrUpdatePatternFromDecal(decal, viewBuildCtx.api);\n        }\n      } // SVG text, tspan and image can be named but not supporeted\n      // to be styled by region option yet.\n\n\n      el.setStyle(normalStyle);\n      el.style.strokeNoScale = true;\n      el.ensureState('emphasis').style = emphasisStyle;\n      el.ensureState('select').style = selectStyle;\n      el.ensureState('blur').style = blurStyle; // Enable blur\n\n      setDefaultStateProxy(el);\n    }\n\n    function resetLabelForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, // Exist only if `viewBuildCtx.data` exists.\n    dataIdx, // If labelXY not provided, use `textConfig.position: 'inside'`\n    labelXY) {\n      var data = viewBuildCtx.data;\n      var isGeo = viewBuildCtx.isGeo;\n      var isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx));\n      var itemLayout = data && data.getItemLayout(dataIdx); // In the following cases label will be drawn\n      // 1. In map series and data value is NaN\n      // 2. In geo component\n      // 3. Region has no series legendIcon, which will be add a showLabel flag in mapSymbolLayout\n\n      if (isGeo || isDataNaN || itemLayout && itemLayout.showLabel) {\n        var query = !isGeo ? dataIdx : regionName;\n        var labelFetcher = void 0; // Consider dataIdx not found.\n\n        if (!data || dataIdx >= 0) {\n          labelFetcher = mapOrGeoModel;\n        }\n\n        var specifiedTextOpt = labelXY ? {\n          normal: {\n            align: 'center',\n            verticalAlign: 'middle'\n          }\n        } : null; // Caveat: must be called after `setDefaultStateProxy(el);` called.\n        // because textContent will be assign with `el.stateProxy` inside.\n\n        setLabelStyle(el, getLabelStatesModels(regionModel), {\n          labelFetcher: labelFetcher,\n          labelDataIndex: query,\n          defaultText: regionName\n        }, specifiedTextOpt);\n        var textEl = el.getTextContent();\n\n        if (textEl) {\n          mapLabelRaw(textEl).ignore = textEl.ignore;\n\n          if (el.textConfig && labelXY) {\n            // Compute a relative offset based on the el bounding rect.\n            var rect = el.getBoundingRect().clone(); // Need to make sure the percent position base on the same rect in normal and\n            // emphasis state. Otherwise if using boundingRect of el, but the emphasis state\n            // has borderWidth (even 0.5px), the text position will be changed obviously\n            // if the position is very big like ['1234%', '1345%'].\n\n            el.textConfig.layoutRect = rect;\n            el.textConfig.position = [(labelXY[0] - rect.x) / rect.width * 100 + '%', (labelXY[1] - rect.y) / rect.height * 100 + '%'];\n          }\n        } // PENDING:\n        // If labelLayout is enabled (test/label-layout.html), el.dataIndex should be specified.\n        // But el.dataIndex is also used to determine whether user event should be triggered,\n        // where el.seriesIndex or el.dataModel must be specified. At present for a single el\n        // there is not case that \"only label layout enabled but user event disabled\", so here\n        // we depends `resetEventTriggerForRegion` to do the job of setting `el.dataIndex`.\n\n\n        el.disableLabelAnimation = true;\n      } else {\n        el.removeTextContent();\n        el.removeTextConfig();\n        el.disableLabelAnimation = null;\n      }\n    }\n\n    function resetEventTriggerForRegion(viewBuildCtx, eventTrigger, regionName, regionModel, mapOrGeoModel, // Exist only if `viewBuildCtx.data` exists.\n    dataIdx) {\n      // setItemGraphicEl, setHoverStyle after all polygons and labels\n      // are added to the regionGroup\n      if (viewBuildCtx.data) {\n        // FIXME: when series-map use a SVG map, and there are duplicated name specified\n        // on different SVG elements, after `data.setItemGraphicEl(...)`:\n        // (1) all of them will be mounted with `dataIndex`, `seriesIndex`, so that tooltip\n        // can be triggered only mouse hover. That's correct.\n        // (2) only the last element will be kept in `data`, so that if trigger tooltip\n        // by `dispatchAction`, only the last one can be found and triggered. That might be\n        // not correct. We will fix it in future if anyone demanding that.\n        viewBuildCtx.data.setItemGraphicEl(dataIdx, eventTrigger);\n      } // series-map will not trigger \"geoselectchange\" no matter it is\n      // based on a declared geo component. Because series-map will\n      // trigger \"selectchange\". If it trigger both the two events,\n      // If users call `chart.dispatchAction({type: 'toggleSelect'})`,\n      // it not easy to also fire event \"geoselectchanged\".\n      else {\n          // Package custom mouse event for geo component\n          getECData(eventTrigger).eventData = {\n            componentType: 'geo',\n            componentIndex: mapOrGeoModel.componentIndex,\n            geoIndex: mapOrGeoModel.componentIndex,\n            name: regionName,\n            region: regionModel && regionModel.option || {}\n          };\n        }\n    }\n\n    function resetTooltipForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel) {\n      if (!viewBuildCtx.data) {\n        setTooltipConfig({\n          el: el,\n          componentModel: mapOrGeoModel,\n          itemName: regionName,\n          // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n          itemTooltipOption: regionModel.get('tooltip')\n        });\n      }\n    }\n\n    function resetStateTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel) {\n      // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n      el.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode'); // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n\n      var emphasisModel = regionModel.getModel('emphasis');\n      var focus = emphasisModel.get('focus');\n      toggleHoverEmphasis(el, focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n\n      if (viewBuildCtx.isGeo) {\n        enableComponentHighDownFeatures(el, mapOrGeoModel, regionName);\n      }\n\n      return focus;\n    }\n\n    function projectPolys(rings, // Polygons include exterior and interiors. Or polylines.\n    createStream, isLine) {\n      var polygons = [];\n      var curPoly;\n\n      function startPolygon() {\n        curPoly = [];\n      }\n\n      function endPolygon() {\n        if (curPoly.length) {\n          polygons.push(curPoly);\n          curPoly = [];\n        }\n      }\n\n      var stream = createStream({\n        polygonStart: startPolygon,\n        polygonEnd: endPolygon,\n        lineStart: startPolygon,\n        lineEnd: endPolygon,\n        point: function (x, y) {\n          // May have NaN values from stream.\n          if (isFinite(x) && isFinite(y)) {\n            curPoly.push([x, y]);\n          }\n        },\n        sphere: function () {}\n      });\n      !isLine && stream.polygonStart();\n      each(rings, function (ring) {\n        stream.lineStart();\n\n        for (var i = 0; i < ring.length; i++) {\n          stream.point(ring[i][0], ring[i][1]);\n        }\n\n        stream.lineEnd();\n      });\n      !isLine && stream.polygonEnd();\n      return polygons;\n    }\n     // @ts-ignore FIXME:TS fix the \"compatible with each other\"?\n\n    var MapView =\n    /** @class */\n    function (_super) {\n      __extends(MapView, _super);\n\n      function MapView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MapView.type;\n        return _this;\n      }\n\n      MapView.prototype.render = function (mapModel, ecModel, api, payload) {\n        // Not render if it is an toggleSelect action from self\n        if (payload && payload.type === 'mapToggleSelect' && payload.from === this.uid) {\n          return;\n        }\n\n        var group = this.group;\n        group.removeAll();\n\n        if (mapModel.getHostGeoModel()) {\n          return;\n        }\n\n        if (this._mapDraw && payload && payload.type === 'geoRoam') {\n          this._mapDraw.resetForLabelLayout();\n        } // Not update map if it is an roam action from self\n\n\n        if (!(payload && payload.type === 'geoRoam' && payload.componentType === 'series' && payload.seriesId === mapModel.id)) {\n          if (mapModel.needsDrawMap) {\n            var mapDraw = this._mapDraw || new MapDraw(api);\n            group.add(mapDraw.group);\n            mapDraw.draw(mapModel, ecModel, api, this, payload);\n            this._mapDraw = mapDraw;\n          } else {\n            // Remove drawn map\n            this._mapDraw && this._mapDraw.remove();\n            this._mapDraw = null;\n          }\n        } else {\n          var mapDraw = this._mapDraw;\n          mapDraw && group.add(mapDraw.group);\n        }\n\n        mapModel.get('showLegendSymbol') && ecModel.getComponent('legend') && this._renderSymbols(mapModel, ecModel, api);\n      };\n\n      MapView.prototype.remove = function () {\n        this._mapDraw && this._mapDraw.remove();\n        this._mapDraw = null;\n        this.group.removeAll();\n      };\n\n      MapView.prototype.dispose = function () {\n        this._mapDraw && this._mapDraw.remove();\n        this._mapDraw = null;\n      };\n\n      MapView.prototype._renderSymbols = function (mapModel, ecModel, api) {\n        var originalData = mapModel.originalData;\n        var group = this.group;\n        originalData.each(originalData.mapDimension('value'), function (value, originalDataIndex) {\n          if (isNaN(value)) {\n            return;\n          }\n\n          var layout = originalData.getItemLayout(originalDataIndex);\n\n          if (!layout || !layout.point) {\n            // Not exists in map\n            return;\n          }\n\n          var point = layout.point;\n          var offset = layout.offset;\n          var circle = new Circle({\n            style: {\n              // Because the special of map draw.\n              // Which needs statistic of multiple series and draw on one map.\n              // And each series also need a symbol with legend color\n              //\n              // Layout and visual are put one the different data\n              // TODO\n              fill: mapModel.getData().getVisual('style').fill\n            },\n            shape: {\n              cx: point[0] + offset * 9,\n              cy: point[1],\n              r: 3\n            },\n            silent: true,\n            // Do not overlap the first series, on which labels are displayed.\n            z2: 8 + (!offset ? Z2_EMPHASIS_LIFT + 1 : 0)\n          }); // Only the series that has the first value on the same region is in charge of rendering the label.\n          // But consider the case:\n          // series: [\n          //     {id: 'X', type: 'map', map: 'm', {data: [{name: 'A', value: 11}, {name: 'B', {value: 22}]},\n          //     {id: 'Y', type: 'map', map: 'm', {data: [{name: 'A', value: 21}, {name: 'C', {value: 33}]}\n          // ]\n          // The offset `0` of item `A` is at series `X`, but of item `C` is at series `Y`.\n          // For backward compatibility, we follow the rule that render label `A` by the\n          // settings on series `X` but render label `C` by the settings on series `Y`.\n\n          if (!offset) {\n            var fullData = mapModel.mainSeries.getData();\n            var name_1 = originalData.getName(originalDataIndex);\n            var fullIndex_1 = fullData.indexOfName(name_1);\n            var itemModel = originalData.getItemModel(originalDataIndex);\n            var labelModel = itemModel.getModel('label');\n            var regionGroup = fullData.getItemGraphicEl(fullIndex_1); // `getFormattedLabel` needs to use `getData` inside. Here\n            // `mapModel.getData()` is shallow cloned from `mainSeries.getData()`.\n            // FIXME\n            // If this is not the `mainSeries`, the item model (like label formatter)\n            // set on original data item will never get. But it has been working\n            // like that from the beginning, and this scenario is rarely encountered.\n            // So it won't be fixed until we have to.\n\n            setLabelStyle(circle, getLabelStatesModels(itemModel), {\n              labelFetcher: {\n                getFormattedLabel: function (idx, state) {\n                  return mapModel.getFormattedLabel(fullIndex_1, state);\n                }\n              },\n              defaultText: name_1\n            });\n            circle.disableLabelAnimation = true;\n\n            if (!labelModel.get('position')) {\n              circle.setTextConfig({\n                position: 'bottom'\n              });\n            }\n\n            regionGroup.onHoverStateChange = function (toState) {\n              setStatesFlag(circle, toState);\n            };\n          }\n\n          group.add(circle);\n        });\n      };\n\n      MapView.type = 'map';\n      return MapView;\n    }(ChartView);\n\n    var MapSeries =\n    /** @class */\n    function (_super) {\n      __extends(MapSeries, _super);\n\n      function MapSeries() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MapSeries.type; // Only first map series of same mapType will drawMap.\n\n        _this.needsDrawMap = false; // Group of all map series with same mapType\n\n        _this.seriesGroup = [];\n\n        _this.getTooltipPosition = function (dataIndex) {\n          if (dataIndex != null) {\n            var name_1 = this.getData().getName(dataIndex);\n            var geo = this.coordinateSystem;\n            var region = geo.getRegion(name_1);\n            return region && geo.dataToPoint(region.getCenter());\n          }\n        };\n\n        return _this;\n      }\n\n      MapSeries.prototype.getInitialData = function (option) {\n        var data = createSeriesDataSimply(this, {\n          coordDimensions: ['value'],\n          encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n        });\n        var dataNameMap = createHashMap();\n        var toAppendNames = [];\n\n        for (var i = 0, len = data.count(); i < len; i++) {\n          var name_2 = data.getName(i);\n          dataNameMap.set(name_2, true);\n        }\n\n        var geoSource = geoSourceManager.load(this.getMapType(), this.option.nameMap, this.option.nameProperty);\n        each(geoSource.regions, function (region) {\n          var name = region.name;\n\n          if (!dataNameMap.get(name)) {\n            toAppendNames.push(name);\n          }\n        }); // Complete data with missing regions. The consequent processes (like visual\n        // map and render) can not be performed without a \"full data\". For example,\n        // find `dataIndex` by name.\n\n        data.appendValues([], toAppendNames);\n        return data;\n      };\n      /**\n       * If no host geo model, return null, which means using a\n       * inner exclusive geo model.\n       */\n\n\n      MapSeries.prototype.getHostGeoModel = function () {\n        var geoIndex = this.option.geoIndex;\n        return geoIndex != null ? this.ecModel.getComponent('geo', geoIndex) : null;\n      };\n\n      MapSeries.prototype.getMapType = function () {\n        return (this.getHostGeoModel() || this).option.map;\n      }; // _fillOption(option, mapName) {\n      // Shallow clone\n      // option = zrUtil.extend({}, option);\n      // option.data = geoCreator.getFilledRegions(option.data, mapName, option.nameMap);\n      // return option;\n      // }\n\n\n      MapSeries.prototype.getRawValue = function (dataIndex) {\n        // Use value stored in data instead because it is calculated from multiple series\n        // FIXME Provide all value of multiple series ?\n        var data = this.getData();\n        return data.get(data.mapDimension('value'), dataIndex);\n      };\n      /**\n       * Get model of region\n       */\n\n\n      MapSeries.prototype.getRegionModel = function (regionName) {\n        var data = this.getData();\n        return data.getItemModel(data.indexOfName(regionName));\n      };\n      /**\n       * Map tooltip formatter\n       */\n\n\n      MapSeries.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        // FIXME orignalData and data is a bit confusing\n        var data = this.getData();\n        var value = this.getRawValue(dataIndex);\n        var name = data.getName(dataIndex);\n        var seriesGroup = this.seriesGroup;\n        var seriesNames = [];\n\n        for (var i = 0; i < seriesGroup.length; i++) {\n          var otherIndex = seriesGroup[i].originalData.indexOfName(name);\n          var valueDim = data.mapDimension('value');\n\n          if (!isNaN(seriesGroup[i].originalData.get(valueDim, otherIndex))) {\n            seriesNames.push(seriesGroup[i].name);\n          }\n        }\n\n        return createTooltipMarkup('section', {\n          header: seriesNames.join(', '),\n          noHeader: !seriesNames.length,\n          blocks: [createTooltipMarkup('nameValue', {\n            name: name,\n            value: value\n          })]\n        });\n      };\n\n      MapSeries.prototype.setZoom = function (zoom) {\n        this.option.zoom = zoom;\n      };\n\n      MapSeries.prototype.setCenter = function (center) {\n        this.option.center = center;\n      };\n\n      MapSeries.prototype.getLegendIcon = function (opt) {\n        var iconType = opt.icon || 'roundRect';\n        var icon = createSymbol(iconType, 0, 0, opt.itemWidth, opt.itemHeight, opt.itemStyle.fill);\n        icon.setStyle(opt.itemStyle); // Map do not use itemStyle.borderWidth as border width\n\n        icon.style.stroke = 'none'; // No rotation because no series visual symbol for map\n\n        if (iconType.indexOf('empty') > -1) {\n          icon.style.stroke = icon.style.fill;\n          icon.style.fill = '#fff';\n          icon.style.lineWidth = 2;\n        }\n\n        return icon;\n      };\n\n      MapSeries.type = 'series.map';\n      MapSeries.dependencies = ['geo'];\n      MapSeries.layoutMode = 'box';\n      MapSeries.defaultOption = {\n        // 一级层叠\n        // zlevel: 0,\n        // 二级层叠\n        z: 2,\n        coordinateSystem: 'geo',\n        // map should be explicitly specified since ec3.\n        map: '',\n        // If `geoIndex` is not specified, a exclusive geo will be\n        // created. Otherwise use the specified geo component, and\n        // `map` and `mapType` are ignored.\n        // geoIndex: 0,\n        // 'center' | 'left' | 'right' | 'x%' | {number}\n        left: 'center',\n        // 'center' | 'top' | 'bottom' | 'x%' | {number}\n        top: 'center',\n        // right\n        // bottom\n        // width:\n        // height\n        // Aspect is width / height. Inited to be geoJson bbox aspect\n        // This parameter is used for scale this aspect\n        // Default value:\n        // for geoSVG source: 1,\n        // for geoJSON source: 0.75.\n        aspectScale: null,\n        // Layout with center and size\n        // If you want to put map in a fixed size box with right aspect ratio\n        // This two properties may be more convenient.\n        // layoutCenter: [50%, 50%]\n        // layoutSize: 100\n        showLegendSymbol: true,\n        // Define left-top, right-bottom coords to control view\n        // For example, [ [180, 90], [-180, -90] ],\n        // higher priority than center and zoom\n        boundingCoords: null,\n        // Default on center of map\n        center: null,\n        zoom: 1,\n        scaleLimit: null,\n        selectedMode: true,\n        label: {\n          show: false,\n          color: '#000'\n        },\n        // scaleLimit: null,\n        itemStyle: {\n          borderWidth: 0.5,\n          borderColor: '#444',\n          areaColor: '#eee'\n        },\n        emphasis: {\n          label: {\n            show: true,\n            color: 'rgb(100,0,0)'\n          },\n          itemStyle: {\n            areaColor: 'rgba(255,215,0,0.8)'\n          }\n        },\n        select: {\n          label: {\n            show: true,\n            color: 'rgb(100,0,0)'\n          },\n          itemStyle: {\n            color: 'rgba(255,215,0,0.8)'\n          }\n        },\n        nameProperty: 'name'\n      };\n      return MapSeries;\n    }(SeriesModel);\n\n    function dataStatistics(datas, statisticType) {\n      var dataNameMap = {};\n      each(datas, function (data) {\n        data.each(data.mapDimension('value'), function (value, idx) {\n          // Add prefix to avoid conflict with Object.prototype.\n          var mapKey = 'ec-' + data.getName(idx);\n          dataNameMap[mapKey] = dataNameMap[mapKey] || [];\n\n          if (!isNaN(value)) {\n            dataNameMap[mapKey].push(value);\n          }\n        });\n      });\n      return datas[0].map(datas[0].mapDimension('value'), function (value, idx) {\n        var mapKey = 'ec-' + datas[0].getName(idx);\n        var sum = 0;\n        var min = Infinity;\n        var max = -Infinity;\n        var len = dataNameMap[mapKey].length;\n\n        for (var i = 0; i < len; i++) {\n          min = Math.min(min, dataNameMap[mapKey][i]);\n          max = Math.max(max, dataNameMap[mapKey][i]);\n          sum += dataNameMap[mapKey][i];\n        }\n\n        var result;\n\n        if (statisticType === 'min') {\n          result = min;\n        } else if (statisticType === 'max') {\n          result = max;\n        } else if (statisticType === 'average') {\n          result = sum / len;\n        } else {\n          result = sum;\n        }\n\n        return len === 0 ? NaN : result;\n      });\n    }\n\n    function mapDataStatistic(ecModel) {\n      var seriesGroups = {};\n      ecModel.eachSeriesByType('map', function (seriesModel) {\n        var hostGeoModel = seriesModel.getHostGeoModel();\n        var key = hostGeoModel ? 'o' + hostGeoModel.id : 'i' + seriesModel.getMapType();\n        (seriesGroups[key] = seriesGroups[key] || []).push(seriesModel);\n      });\n      each(seriesGroups, function (seriesList, key) {\n        var data = dataStatistics(map(seriesList, function (seriesModel) {\n          return seriesModel.getData();\n        }), seriesList[0].get('mapValueCalculation'));\n\n        for (var i = 0; i < seriesList.length; i++) {\n          seriesList[i].originalData = seriesList[i].getData();\n        } // FIXME Put where?\n\n\n        for (var i = 0; i < seriesList.length; i++) {\n          seriesList[i].seriesGroup = seriesList;\n          seriesList[i].needsDrawMap = i === 0 && !seriesList[i].getHostGeoModel();\n          seriesList[i].setData(data.cloneShallow());\n          seriesList[i].mainSeries = seriesList[0];\n        }\n      });\n    }\n\n    function mapSymbolLayout(ecModel) {\n      var processedMapType = {};\n      ecModel.eachSeriesByType('map', function (mapSeries) {\n        var mapType = mapSeries.getMapType();\n\n        if (mapSeries.getHostGeoModel() || processedMapType[mapType]) {\n          return;\n        }\n\n        var mapSymbolOffsets = {};\n        each(mapSeries.seriesGroup, function (subMapSeries) {\n          var geo = subMapSeries.coordinateSystem;\n          var data = subMapSeries.originalData;\n\n          if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) {\n            data.each(data.mapDimension('value'), function (value, idx) {\n              var name = data.getName(idx);\n              var region = geo.getRegion(name); // If input series.data is [11, 22, '-'/null/undefined, 44],\n              // it will be filled with NaN: [11, 22, NaN, 44] and NaN will\n              // not be drawn. So here must validate if value is NaN.\n\n              if (!region || isNaN(value)) {\n                return;\n              }\n\n              var offset = mapSymbolOffsets[name] || 0;\n              var point = geo.dataToPoint(region.getCenter());\n              mapSymbolOffsets[name] = offset + 1;\n              data.setItemLayout(idx, {\n                point: point,\n                offset: offset\n              });\n            });\n          }\n        }); // Show label of those region not has legendIcon (which is offset 0)\n\n        var data = mapSeries.getData();\n        data.each(function (idx) {\n          var name = data.getName(idx);\n          var layout = data.getItemLayout(idx) || {};\n          layout.showLabel = !mapSymbolOffsets[name];\n          data.setItemLayout(idx, layout);\n        });\n        processedMapType[mapType] = true;\n      });\n    }\n\n    var v2ApplyTransform = applyTransform;\n\n    var View =\n    /** @class */\n    function (_super) {\n      __extends(View, _super);\n\n      function View(name) {\n        var _this = _super.call(this) || this;\n\n        _this.type = 'view';\n        _this.dimensions = ['x', 'y'];\n        /**\n         * Represents the transform brought by roam/zoom.\n         * If `View['_viewRect']` applies roam transform,\n         * we can get the final displayed rect.\n         */\n\n        _this._roamTransformable = new Transformable();\n        /**\n         * Represents the transform from `View['_rect']` to `View['_viewRect']`.\n         */\n\n        _this._rawTransformable = new Transformable();\n        _this.name = name;\n        return _this;\n      }\n\n      View.prototype.setBoundingRect = function (x, y, width, height) {\n        this._rect = new BoundingRect(x, y, width, height);\n        return this._rect;\n      };\n      /**\n       * @return {module:zrender/core/BoundingRect}\n       */\n\n\n      View.prototype.getBoundingRect = function () {\n        return this._rect;\n      };\n\n      View.prototype.setViewRect = function (x, y, width, height) {\n        this._transformTo(x, y, width, height);\n\n        this._viewRect = new BoundingRect(x, y, width, height);\n      };\n      /**\n       * Transformed to particular position and size\n       */\n\n\n      View.prototype._transformTo = function (x, y, width, height) {\n        var rect = this.getBoundingRect();\n        var rawTransform = this._rawTransformable;\n        rawTransform.transform = rect.calculateTransform(new BoundingRect(x, y, width, height));\n        var rawParent = rawTransform.parent;\n        rawTransform.parent = null;\n        rawTransform.decomposeTransform();\n        rawTransform.parent = rawParent;\n\n        this._updateTransform();\n      };\n      /**\n       * Set center of view\n       */\n\n\n      View.prototype.setCenter = function (centerCoord, api) {\n        if (!centerCoord) {\n          return;\n        }\n\n        this._center = [parsePercent$1(centerCoord[0], api.getWidth()), parsePercent$1(centerCoord[1], api.getHeight())];\n\n        this._updateCenterAndZoom();\n      };\n\n      View.prototype.setZoom = function (zoom) {\n        zoom = zoom || 1;\n        var zoomLimit = this.zoomLimit;\n\n        if (zoomLimit) {\n          if (zoomLimit.max != null) {\n            zoom = Math.min(zoomLimit.max, zoom);\n          }\n\n          if (zoomLimit.min != null) {\n            zoom = Math.max(zoomLimit.min, zoom);\n          }\n        }\n\n        this._zoom = zoom;\n\n        this._updateCenterAndZoom();\n      };\n      /**\n       * Get default center without roam\n       */\n\n\n      View.prototype.getDefaultCenter = function () {\n        // Rect before any transform\n        var rawRect = this.getBoundingRect();\n        var cx = rawRect.x + rawRect.width / 2;\n        var cy = rawRect.y + rawRect.height / 2;\n        return [cx, cy];\n      };\n\n      View.prototype.getCenter = function () {\n        return this._center || this.getDefaultCenter();\n      };\n\n      View.prototype.getZoom = function () {\n        return this._zoom || 1;\n      };\n\n      View.prototype.getRoamTransform = function () {\n        return this._roamTransformable.getLocalTransform();\n      };\n      /**\n       * Remove roam\n       */\n\n\n      View.prototype._updateCenterAndZoom = function () {\n        // Must update after view transform updated\n        var rawTransformMatrix = this._rawTransformable.getLocalTransform();\n\n        var roamTransform = this._roamTransformable;\n        var defaultCenter = this.getDefaultCenter();\n        var center = this.getCenter();\n        var zoom = this.getZoom();\n        center = applyTransform([], center, rawTransformMatrix);\n        defaultCenter = applyTransform([], defaultCenter, rawTransformMatrix);\n        roamTransform.originX = center[0];\n        roamTransform.originY = center[1];\n        roamTransform.x = defaultCenter[0] - center[0];\n        roamTransform.y = defaultCenter[1] - center[1];\n        roamTransform.scaleX = roamTransform.scaleY = zoom;\n\n        this._updateTransform();\n      };\n      /**\n       * Update transform props on `this` based on the current\n       * `this._roamTransformable` and `this._rawTransformable`.\n       */\n\n\n      View.prototype._updateTransform = function () {\n        var roamTransformable = this._roamTransformable;\n        var rawTransformable = this._rawTransformable;\n        rawTransformable.parent = roamTransformable;\n        roamTransformable.updateTransform();\n        rawTransformable.updateTransform();\n        copy$1(this.transform || (this.transform = []), rawTransformable.transform || create$1());\n        this._rawTransform = rawTransformable.getLocalTransform();\n        this.invTransform = this.invTransform || [];\n        invert(this.invTransform, this.transform);\n        this.decomposeTransform();\n      };\n\n      View.prototype.getTransformInfo = function () {\n        var rawTransformable = this._rawTransformable;\n        var roamTransformable = this._roamTransformable; // Because roamTransformabel has `originX/originY` modified,\n        // but the caller of `getTransformInfo` can not handle `originX/originY`,\n        // so need to recalculate them.\n\n        var dummyTransformable = new Transformable();\n        dummyTransformable.transform = roamTransformable.transform;\n        dummyTransformable.decomposeTransform();\n        return {\n          roam: {\n            x: dummyTransformable.x,\n            y: dummyTransformable.y,\n            scaleX: dummyTransformable.scaleX,\n            scaleY: dummyTransformable.scaleY\n          },\n          raw: {\n            x: rawTransformable.x,\n            y: rawTransformable.y,\n            scaleX: rawTransformable.scaleX,\n            scaleY: rawTransformable.scaleY\n          }\n        };\n      };\n\n      View.prototype.getViewRect = function () {\n        return this._viewRect;\n      };\n      /**\n       * Get view rect after roam transform\n       */\n\n\n      View.prototype.getViewRectAfterRoam = function () {\n        var rect = this.getBoundingRect().clone();\n        rect.applyTransform(this.transform);\n        return rect;\n      };\n      /**\n       * Convert a single (lon, lat) data item to (x, y) point.\n       */\n\n\n      View.prototype.dataToPoint = function (data, noRoam, out) {\n        var transform = noRoam ? this._rawTransform : this.transform;\n        out = out || [];\n        return transform ? v2ApplyTransform(out, data, transform) : copy(out, data);\n      };\n      /**\n       * Convert a (x, y) point to (lon, lat) data\n       */\n\n\n      View.prototype.pointToData = function (point) {\n        var invTransform = this.invTransform;\n        return invTransform ? v2ApplyTransform([], point, invTransform) : [point[0], point[1]];\n      };\n\n      View.prototype.convertToPixel = function (ecModel, finder, value) {\n        var coordSys = getCoordSys(finder);\n        return coordSys === this ? coordSys.dataToPoint(value) : null;\n      };\n\n      View.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n        var coordSys = getCoordSys(finder);\n        return coordSys === this ? coordSys.pointToData(pixel) : null;\n      };\n      /**\n       * @implements\n       */\n\n\n      View.prototype.containPoint = function (point) {\n        return this.getViewRectAfterRoam().contain(point[0], point[1]);\n      };\n\n      View.dimensions = ['x', 'y'];\n      return View;\n    }(Transformable);\n\n    function getCoordSys(finder) {\n      var seriesModel = finder.seriesModel;\n      return seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph.\n    }\n\n    var GEO_DEFAULT_PARAMS = {\n      'geoJSON': {\n        aspectScale: 0.75,\n        invertLongitute: true\n      },\n      'geoSVG': {\n        aspectScale: 1,\n        invertLongitute: false\n      }\n    };\n    var geo2DDimensions = ['lng', 'lat'];\n\n    var Geo =\n    /** @class */\n    function (_super) {\n      __extends(Geo, _super);\n\n      function Geo(name, map, opt) {\n        var _this = _super.call(this, name) || this;\n\n        _this.dimensions = geo2DDimensions;\n        _this.type = 'geo'; // Only store specified name coord via `addGeoCoord`.\n\n        _this._nameCoordMap = createHashMap();\n        _this.map = map;\n        var projection = opt.projection;\n        var source = geoSourceManager.load(map, opt.nameMap, opt.nameProperty);\n        var resource = geoSourceManager.getGeoResource(map);\n        var resourceType = _this.resourceType = resource ? resource.type : null;\n        var regions = _this.regions = source.regions;\n        var defaultParams = GEO_DEFAULT_PARAMS[resource.type];\n        _this._regionsMap = source.regionsMap;\n        _this.regions = source.regions;\n\n        if (\"development\" !== 'production' && projection) {\n          // Do some check\n          if (resourceType === 'geoSVG') {\n            if (\"development\" !== 'production') {\n              warn(\"Map \" + map + \" with SVG source can't use projection. Only GeoJSON source supports projection.\");\n            }\n\n            projection = null;\n          }\n\n          if (!(projection.project && projection.unproject)) {\n            if (\"development\" !== 'production') {\n              warn('project and unproject must be both provided in the projeciton.');\n            }\n\n            projection = null;\n          }\n        }\n\n        _this.projection = projection;\n        var boundingRect;\n\n        if (projection) {\n          // Can't reuse the raw bounding rect\n          for (var i = 0; i < regions.length; i++) {\n            var regionRect = regions[i].getBoundingRect(projection);\n            boundingRect = boundingRect || regionRect.clone();\n            boundingRect.union(regionRect);\n          }\n        } else {\n          boundingRect = source.boundingRect;\n        }\n\n        _this.setBoundingRect(boundingRect.x, boundingRect.y, boundingRect.width, boundingRect.height); // aspectScale and invertLongitute actually is the parameters default raw projection.\n        // So we ignore them if projection is given.\n        // Ignore default aspect scale if projection exits.\n\n\n        _this.aspectScale = projection ? 1 : retrieve2(opt.aspectScale, defaultParams.aspectScale); // Not invert longitude if projection exits.\n\n        _this._invertLongitute = projection ? false : defaultParams.invertLongitute;\n        return _this;\n      }\n\n      Geo.prototype._transformTo = function (x, y, width, height) {\n        var rect = this.getBoundingRect();\n        var invertLongitute = this._invertLongitute;\n        rect = rect.clone();\n\n        if (invertLongitute) {\n          // Longitude is inverted.\n          rect.y = -rect.y - rect.height;\n        }\n\n        var rawTransformable = this._rawTransformable;\n        rawTransformable.transform = rect.calculateTransform(new BoundingRect(x, y, width, height));\n        var rawParent = rawTransformable.parent;\n        rawTransformable.parent = null;\n        rawTransformable.decomposeTransform();\n        rawTransformable.parent = rawParent;\n\n        if (invertLongitute) {\n          rawTransformable.scaleY = -rawTransformable.scaleY;\n        }\n\n        this._updateTransform();\n      };\n\n      Geo.prototype.getRegion = function (name) {\n        return this._regionsMap.get(name);\n      };\n\n      Geo.prototype.getRegionByCoord = function (coord) {\n        var regions = this.regions;\n\n        for (var i = 0; i < regions.length; i++) {\n          var region = regions[i];\n\n          if (region.type === 'geoJSON' && region.contain(coord)) {\n            return regions[i];\n          }\n        }\n      };\n      /**\n       * Add geoCoord for indexing by name\n       */\n\n\n      Geo.prototype.addGeoCoord = function (name, geoCoord) {\n        this._nameCoordMap.set(name, geoCoord);\n      };\n      /**\n       * Get geoCoord by name\n       */\n\n\n      Geo.prototype.getGeoCoord = function (name) {\n        var region = this._regionsMap.get(name); // Calculate center only on demand.\n\n\n        return this._nameCoordMap.get(name) || region && region.getCenter();\n      };\n\n      Geo.prototype.dataToPoint = function (data, noRoam, out) {\n        if (isString(data)) {\n          // Map area name to geoCoord\n          data = this.getGeoCoord(data);\n        }\n\n        if (data) {\n          var projection = this.projection;\n\n          if (projection) {\n            // projection may return null point.\n            data = projection.project(data);\n          }\n\n          return data && this.projectedToPoint(data, noRoam, out);\n        }\n      };\n\n      Geo.prototype.pointToData = function (point) {\n        var projection = this.projection;\n\n        if (projection) {\n          // projection may return null point.\n          point = projection.unproject(point);\n        }\n\n        return point && this.pointToProjected(point);\n      };\n      /**\n       * Point to projected data. Same with pointToData when projection is used.\n       */\n\n\n      Geo.prototype.pointToProjected = function (point) {\n        return _super.prototype.pointToData.call(this, point);\n      };\n\n      Geo.prototype.projectedToPoint = function (projected, noRoam, out) {\n        return _super.prototype.dataToPoint.call(this, projected, noRoam, out);\n      };\n\n      Geo.prototype.convertToPixel = function (ecModel, finder, value) {\n        var coordSys = getCoordSys$1(finder);\n        return coordSys === this ? coordSys.dataToPoint(value) : null;\n      };\n\n      Geo.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n        var coordSys = getCoordSys$1(finder);\n        return coordSys === this ? coordSys.pointToData(pixel) : null;\n      };\n\n      return Geo;\n    }(View);\n    mixin(Geo, View);\n\n    function getCoordSys$1(finder) {\n      var geoModel = finder.geoModel;\n      var seriesModel = finder.seriesModel;\n      return geoModel ? geoModel.coordinateSystem : seriesModel ? seriesModel.coordinateSystem // For map series.\n      || (seriesModel.getReferringComponents('geo', SINGLE_REFERRING).models[0] || {}).coordinateSystem : null;\n    }\n\n    /**\n     * Resize method bound to the geo\n     */\n\n    function resizeGeo(geoModel, api) {\n      var boundingCoords = geoModel.get('boundingCoords');\n\n      if (boundingCoords != null) {\n        var leftTop_1 = boundingCoords[0];\n        var rightBottom_1 = boundingCoords[1];\n\n        if (!(isFinite(leftTop_1[0]) && isFinite(leftTop_1[1]) && isFinite(rightBottom_1[0]) && isFinite(rightBottom_1[1]))) {\n          if (\"development\" !== 'production') {\n            console.error('Invalid boundingCoords');\n          }\n        } else {\n          // Sample around the lng/lat rect and use projection to calculate actual bounding rect.\n          var projection_1 = this.projection;\n\n          if (projection_1) {\n            var xMin = leftTop_1[0];\n            var yMin = leftTop_1[1];\n            var xMax = rightBottom_1[0];\n            var yMax = rightBottom_1[1];\n            leftTop_1 = [Infinity, Infinity];\n            rightBottom_1 = [-Infinity, -Infinity]; // TODO better way?\n\n            var sampleLine = function (x0, y0, x1, y1) {\n              var dx = x1 - x0;\n              var dy = y1 - y0;\n\n              for (var i = 0; i <= 100; i++) {\n                var p = i / 100;\n                var pt = projection_1.project([x0 + dx * p, y0 + dy * p]);\n                min(leftTop_1, leftTop_1, pt);\n                max(rightBottom_1, rightBottom_1, pt);\n              }\n            }; // Top\n\n\n            sampleLine(xMin, yMin, xMax, yMin); // Right\n\n            sampleLine(xMax, yMin, xMax, yMax); // Bottom\n\n            sampleLine(xMax, yMax, xMin, yMax); // Left\n\n            sampleLine(xMin, yMax, xMax, yMin);\n          }\n\n          this.setBoundingRect(leftTop_1[0], leftTop_1[1], rightBottom_1[0] - leftTop_1[0], rightBottom_1[1] - leftTop_1[1]);\n        }\n      }\n\n      var rect = this.getBoundingRect();\n      var centerOption = geoModel.get('layoutCenter');\n      var sizeOption = geoModel.get('layoutSize');\n      var viewWidth = api.getWidth();\n      var viewHeight = api.getHeight();\n      var aspect = rect.width / rect.height * this.aspectScale;\n      var useCenterAndSize = false;\n      var center;\n      var size;\n\n      if (centerOption && sizeOption) {\n        center = [parsePercent$1(centerOption[0], viewWidth), parsePercent$1(centerOption[1], viewHeight)];\n        size = parsePercent$1(sizeOption, Math.min(viewWidth, viewHeight));\n\n        if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) {\n          useCenterAndSize = true;\n        } else {\n          if (\"development\" !== 'production') {\n            console.warn('Given layoutCenter or layoutSize data are invalid. Use left/top/width/height instead.');\n          }\n        }\n      }\n\n      var viewRect;\n\n      if (useCenterAndSize) {\n        viewRect = {};\n\n        if (aspect > 1) {\n          // Width is same with size\n          viewRect.width = size;\n          viewRect.height = size / aspect;\n        } else {\n          viewRect.height = size;\n          viewRect.width = size * aspect;\n        }\n\n        viewRect.y = center[1] - viewRect.height / 2;\n        viewRect.x = center[0] - viewRect.width / 2;\n      } else {\n        // Use left/top/width/height\n        var boxLayoutOption = geoModel.getBoxLayoutParams();\n        boxLayoutOption.aspect = aspect;\n        viewRect = getLayoutRect(boxLayoutOption, {\n          width: viewWidth,\n          height: viewHeight\n        });\n      }\n\n      this.setViewRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);\n      this.setCenter(geoModel.get('center'), api);\n      this.setZoom(geoModel.get('zoom'));\n    } // Back compat for ECharts2, where the coord map is set on map series:\n    // {type: 'map', geoCoord: {'cityA': [116.46,39.92], 'cityA': [119.12,24.61]}},\n\n\n    function setGeoCoords(geo, model) {\n      each(model.get('geoCoord'), function (geoCoord, name) {\n        geo.addGeoCoord(name, geoCoord);\n      });\n    }\n\n    var GeoCreator =\n    /** @class */\n    function () {\n      function GeoCreator() {\n        // For deciding which dimensions to use when creating list data\n        this.dimensions = geo2DDimensions;\n      }\n\n      GeoCreator.prototype.create = function (ecModel, api) {\n        var geoList = [];\n\n        function getCommonGeoProperties(model) {\n          return {\n            nameProperty: model.get('nameProperty'),\n            aspectScale: model.get('aspectScale'),\n            projection: model.get('projection')\n          };\n        } // FIXME Create each time may be slow\n\n\n        ecModel.eachComponent('geo', function (geoModel, idx) {\n          var mapName = geoModel.get('map');\n          var geo = new Geo(mapName + idx, mapName, extend({\n            nameMap: geoModel.get('nameMap')\n          }, getCommonGeoProperties(geoModel)));\n          geo.zoomLimit = geoModel.get('scaleLimit');\n          geoList.push(geo); // setGeoCoords(geo, geoModel);\n\n          geoModel.coordinateSystem = geo;\n          geo.model = geoModel; // Inject resize method\n\n          geo.resize = resizeGeo;\n          geo.resize(geoModel, api);\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          var coordSys = seriesModel.get('coordinateSystem');\n\n          if (coordSys === 'geo') {\n            var geoIndex = seriesModel.get('geoIndex') || 0;\n            seriesModel.coordinateSystem = geoList[geoIndex];\n          }\n        }); // If has map series\n\n        var mapModelGroupBySeries = {};\n        ecModel.eachSeriesByType('map', function (seriesModel) {\n          if (!seriesModel.getHostGeoModel()) {\n            var mapType = seriesModel.getMapType();\n            mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || [];\n            mapModelGroupBySeries[mapType].push(seriesModel);\n          }\n        });\n        each(mapModelGroupBySeries, function (mapSeries, mapType) {\n          var nameMapList = map(mapSeries, function (singleMapSeries) {\n            return singleMapSeries.get('nameMap');\n          });\n          var geo = new Geo(mapType, mapType, extend({\n            nameMap: mergeAll(nameMapList)\n          }, getCommonGeoProperties(mapSeries[0])));\n          geo.zoomLimit = retrieve.apply(null, map(mapSeries, function (singleMapSeries) {\n            return singleMapSeries.get('scaleLimit');\n          }));\n          geoList.push(geo); // Inject resize method\n\n          geo.resize = resizeGeo;\n          geo.resize(mapSeries[0], api);\n          each(mapSeries, function (singleMapSeries) {\n            singleMapSeries.coordinateSystem = geo;\n            setGeoCoords(geo, singleMapSeries);\n          });\n        });\n        return geoList;\n      };\n      /**\n       * Fill given regions array\n       */\n\n\n      GeoCreator.prototype.getFilledRegions = function (originRegionArr, mapName, nameMap, nameProperty) {\n        // Not use the original\n        var regionsArr = (originRegionArr || []).slice();\n        var dataNameMap = createHashMap();\n\n        for (var i = 0; i < regionsArr.length; i++) {\n          dataNameMap.set(regionsArr[i].name, regionsArr[i]);\n        }\n\n        var source = geoSourceManager.load(mapName, nameMap, nameProperty);\n        each(source.regions, function (region) {\n          var name = region.name;\n          !dataNameMap.get(name) && regionsArr.push({\n            name: name\n          });\n        });\n        return regionsArr;\n      };\n\n      return GeoCreator;\n    }();\n\n    var geoCreator = new GeoCreator();\n\n    var GeoModel =\n    /** @class */\n    function (_super) {\n      __extends(GeoModel, _super);\n\n      function GeoModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GeoModel.type;\n        return _this;\n      }\n\n      GeoModel.prototype.init = function (option, parentModel, ecModel) {\n        var source = geoSourceManager.getGeoResource(option.map);\n\n        if (source && source.type === 'geoJSON') {\n          var itemStyle = option.itemStyle = option.itemStyle || {};\n\n          if (!('color' in itemStyle)) {\n            itemStyle.color = '#eee';\n          }\n        }\n\n        this.mergeDefaultAndTheme(option, ecModel); // Default label emphasis `show`\n\n        defaultEmphasis(option, 'label', ['show']);\n      };\n\n      GeoModel.prototype.optionUpdated = function () {\n        var _this = this;\n\n        var option = this.option;\n        option.regions = geoCreator.getFilledRegions(option.regions, option.map, option.nameMap, option.nameProperty);\n        var selectedMap = {};\n        this._optionModelMap = reduce(option.regions || [], function (optionModelMap, regionOpt) {\n          var regionName = regionOpt.name;\n\n          if (regionName) {\n            optionModelMap.set(regionName, new Model(regionOpt, _this, _this.ecModel));\n\n            if (regionOpt.selected) {\n              selectedMap[regionName] = true;\n            }\n          }\n\n          return optionModelMap;\n        }, createHashMap());\n\n        if (!option.selectedMap) {\n          option.selectedMap = selectedMap;\n        }\n      };\n      /**\n       * Get model of region.\n       */\n\n\n      GeoModel.prototype.getRegionModel = function (name) {\n        return this._optionModelMap.get(name) || new Model(null, this, this.ecModel);\n      };\n      /**\n       * Format label\n       * @param name Region name\n       */\n\n\n      GeoModel.prototype.getFormattedLabel = function (name, status) {\n        var regionModel = this.getRegionModel(name);\n        var formatter = status === 'normal' ? regionModel.get(['label', 'formatter']) : regionModel.get(['emphasis', 'label', 'formatter']);\n        var params = {\n          name: name\n        };\n\n        if (isFunction(formatter)) {\n          params.status = status;\n          return formatter(params);\n        } else if (isString(formatter)) {\n          return formatter.replace('{a}', name != null ? name : '');\n        }\n      };\n\n      GeoModel.prototype.setZoom = function (zoom) {\n        this.option.zoom = zoom;\n      };\n\n      GeoModel.prototype.setCenter = function (center) {\n        this.option.center = center;\n      }; // PENGING If selectedMode is null ?\n\n\n      GeoModel.prototype.select = function (name) {\n        var option = this.option;\n        var selectedMode = option.selectedMode;\n\n        if (!selectedMode) {\n          return;\n        }\n\n        if (selectedMode !== 'multiple') {\n          option.selectedMap = null;\n        }\n\n        var selectedMap = option.selectedMap || (option.selectedMap = {});\n        selectedMap[name] = true;\n      };\n\n      GeoModel.prototype.unSelect = function (name) {\n        var selectedMap = this.option.selectedMap;\n\n        if (selectedMap) {\n          selectedMap[name] = false;\n        }\n      };\n\n      GeoModel.prototype.toggleSelected = function (name) {\n        this[this.isSelected(name) ? 'unSelect' : 'select'](name);\n      };\n\n      GeoModel.prototype.isSelected = function (name) {\n        var selectedMap = this.option.selectedMap;\n        return !!(selectedMap && selectedMap[name]);\n      };\n\n      GeoModel.type = 'geo';\n      GeoModel.layoutMode = 'box';\n      GeoModel.defaultOption = {\n        // zlevel: 0,\n        z: 0,\n        show: true,\n        left: 'center',\n        top: 'center',\n        // Default value:\n        // for geoSVG source: 1,\n        // for geoJSON source: 0.75.\n        aspectScale: null,\n        // /// Layout with center and size\n        // If you want to put map in a fixed size box with right aspect ratio\n        // This two properties may be more convenient\n        // layoutCenter: [50%, 50%]\n        // layoutSize: 100\n        silent: false,\n        // Map type\n        map: '',\n        // Define left-top, right-bottom coords to control view\n        // For example, [ [180, 90], [-180, -90] ]\n        boundingCoords: null,\n        // Default on center of map\n        center: null,\n        zoom: 1,\n        scaleLimit: null,\n        // selectedMode: false\n        label: {\n          show: false,\n          color: '#000'\n        },\n        itemStyle: {\n          borderWidth: 0.5,\n          borderColor: '#444' // Default color:\n          // + geoJSON: #eee\n          // + geoSVG: null (use SVG original `fill`)\n          // color: '#eee'\n\n        },\n        emphasis: {\n          label: {\n            show: true,\n            color: 'rgb(100,0,0)'\n          },\n          itemStyle: {\n            color: 'rgba(255,215,0,0.8)'\n          }\n        },\n        select: {\n          label: {\n            show: true,\n            color: 'rgb(100,0,0)'\n          },\n          itemStyle: {\n            color: 'rgba(255,215,0,0.8)'\n          }\n        },\n        regions: [] // tooltip: {\n        //     show: false\n        // }\n\n      };\n      return GeoModel;\n    }(ComponentModel);\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function getCenterCoord(view, point) {\n      // Use projected coord as center because it's linear.\n      return view.pointToProjected ? view.pointToProjected(point) : view.pointToData(point);\n    }\n\n    function updateCenterAndZoom(view, payload, zoomLimit, api) {\n      var previousZoom = view.getZoom();\n      var center = view.getCenter();\n      var zoom = payload.zoom;\n      var point = view.projectedToPoint ? view.projectedToPoint(center) : view.dataToPoint(center);\n\n      if (payload.dx != null && payload.dy != null) {\n        point[0] -= payload.dx;\n        point[1] -= payload.dy;\n        view.setCenter(getCenterCoord(view, point), api);\n      }\n\n      if (zoom != null) {\n        if (zoomLimit) {\n          var zoomMin = zoomLimit.min || 0;\n          var zoomMax = zoomLimit.max || Infinity;\n          zoom = Math.max(Math.min(previousZoom * zoom, zoomMax), zoomMin) / previousZoom;\n        } // Zoom on given point(originX, originY)\n\n\n        view.scaleX *= zoom;\n        view.scaleY *= zoom;\n        var fixX = (payload.originX - view.x) * (zoom - 1);\n        var fixY = (payload.originY - view.y) * (zoom - 1);\n        view.x -= fixX;\n        view.y -= fixY;\n        view.updateTransform(); // Get the new center\n\n        view.setCenter(getCenterCoord(view, point), api);\n        view.setZoom(zoom * previousZoom);\n      }\n\n      return {\n        center: view.getCenter(),\n        zoom: view.getZoom()\n      };\n    }\n\n    var GeoView =\n    /** @class */\n    function (_super) {\n      __extends(GeoView, _super);\n\n      function GeoView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GeoView.type;\n        _this.focusBlurEnabled = true;\n        return _this;\n      }\n\n      GeoView.prototype.init = function (ecModel, api) {\n        this._api = api;\n      };\n\n      GeoView.prototype.render = function (geoModel, ecModel, api, payload) {\n        this._model = geoModel;\n\n        if (!geoModel.get('show')) {\n          this._mapDraw && this._mapDraw.remove();\n          this._mapDraw = null;\n          return;\n        }\n\n        if (!this._mapDraw) {\n          this._mapDraw = new MapDraw(api);\n        }\n\n        var mapDraw = this._mapDraw;\n        mapDraw.draw(geoModel, ecModel, api, this, payload);\n        mapDraw.group.on('click', this._handleRegionClick, this);\n        mapDraw.group.silent = geoModel.get('silent');\n        this.group.add(mapDraw.group);\n        this.updateSelectStatus(geoModel, ecModel, api);\n      };\n\n      GeoView.prototype._handleRegionClick = function (e) {\n        var eventData;\n        findEventDispatcher(e.target, function (current) {\n          return (eventData = getECData(current).eventData) != null;\n        }, true);\n\n        if (eventData) {\n          this._api.dispatchAction({\n            type: 'geoToggleSelect',\n            geoId: this._model.id,\n            name: eventData.name\n          });\n        }\n      };\n\n      GeoView.prototype.updateSelectStatus = function (model, ecModel, api) {\n        var _this = this;\n\n        this._mapDraw.group.traverse(function (node) {\n          var eventData = getECData(node).eventData;\n\n          if (eventData) {\n            _this._model.isSelected(eventData.name) ? api.enterSelect(node) : api.leaveSelect(node); // No need to traverse children.\n\n            return true;\n          }\n        });\n      };\n\n      GeoView.prototype.findHighDownDispatchers = function (name) {\n        return this._mapDraw && this._mapDraw.findHighDownDispatchers(name, this._model);\n      };\n\n      GeoView.prototype.dispose = function () {\n        this._mapDraw && this._mapDraw.remove();\n      };\n\n      GeoView.type = 'geo';\n      return GeoView;\n    }(ComponentView);\n\n    function registerMap$1(mapName, geoJson, specialAreas) {\n      geoSourceManager.registerMap(mapName, geoJson, specialAreas);\n    }\n\n    function install$9(registers) {\n      registers.registerCoordinateSystem('geo', geoCreator);\n      registers.registerComponentModel(GeoModel);\n      registers.registerComponentView(GeoView);\n      registers.registerImpl('registerMap', registerMap$1);\n      registers.registerImpl('getMap', function (mapName) {\n        return geoSourceManager.getMapForUser(mapName);\n      });\n\n      function makeAction(method, actionInfo) {\n        actionInfo.update = 'geo:updateSelectStatus';\n        registers.registerAction(actionInfo, function (payload, ecModel) {\n          var selected = {};\n          var allSelected = [];\n          ecModel.eachComponent({\n            mainType: 'geo',\n            query: payload\n          }, function (geoModel) {\n            geoModel[method](payload.name);\n            var geo = geoModel.coordinateSystem;\n            each(geo.regions, function (region) {\n              selected[region.name] = geoModel.isSelected(region.name) || false;\n            }); // Notice: there might be duplicated name in different regions.\n\n            var names = [];\n            each(selected, function (v, name) {\n              selected[name] && names.push(name);\n            });\n            allSelected.push({\n              geoIndex: geoModel.componentIndex,\n              // Use singular, the same naming convention as the event `selectchanged`.\n              name: names\n            });\n          });\n          return {\n            selected: selected,\n            allSelected: allSelected,\n            name: payload.name\n          };\n        });\n      }\n\n      makeAction('toggleSelected', {\n        type: 'geoToggleSelect',\n        event: 'geoselectchanged'\n      });\n      makeAction('select', {\n        type: 'geoSelect',\n        event: 'geoselected'\n      });\n      makeAction('unSelect', {\n        type: 'geoUnSelect',\n        event: 'geounselected'\n      });\n      /**\n       * @payload\n       * @property {string} [componentType=series]\n       * @property {number} [dx]\n       * @property {number} [dy]\n       * @property {number} [zoom]\n       * @property {number} [originX]\n       * @property {number} [originY]\n       */\n\n      registers.registerAction({\n        type: 'geoRoam',\n        event: 'geoRoam',\n        update: 'updateTransform'\n      }, function (payload, ecModel, api) {\n        var componentType = payload.componentType || 'series';\n        ecModel.eachComponent({\n          mainType: componentType,\n          query: payload\n        }, function (componentModel) {\n          var geo = componentModel.coordinateSystem;\n\n          if (geo.type !== 'geo') {\n            return;\n          }\n\n          var res = updateCenterAndZoom(geo, payload, componentModel.get('scaleLimit'), api);\n          componentModel.setCenter && componentModel.setCenter(res.center);\n          componentModel.setZoom && componentModel.setZoom(res.zoom); // All map series with same `map` use the same geo coordinate system\n          // So the center and zoom must be in sync. Include the series not selected by legend\n\n          if (componentType === 'series') {\n            each(componentModel.seriesGroup, function (seriesModel) {\n              seriesModel.setCenter(res.center);\n              seriesModel.setZoom(res.zoom);\n            });\n          }\n        });\n      });\n    }\n\n    function install$a(registers) {\n      use(install$9);\n      registers.registerChartView(MapView);\n      registers.registerSeriesModel(MapSeries);\n      registers.registerLayout(mapSymbolLayout);\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, mapDataStatistic);\n      createLegacyDataSelectAction('map', registers.registerAction);\n    }\n\n    /**\n     * Initialize all computational message for following algorithm.\n     */\n\n    function init$2(inRoot) {\n      var root = inRoot;\n      root.hierNode = {\n        defaultAncestor: null,\n        ancestor: root,\n        prelim: 0,\n        modifier: 0,\n        change: 0,\n        shift: 0,\n        i: 0,\n        thread: null\n      };\n      var nodes = [root];\n      var node;\n      var children;\n\n      while (node = nodes.pop()) {\n        // jshint ignore:line\n        children = node.children;\n\n        if (node.isExpand && children.length) {\n          var n = children.length;\n\n          for (var i = n - 1; i >= 0; i--) {\n            var child = children[i];\n            child.hierNode = {\n              defaultAncestor: null,\n              ancestor: child,\n              prelim: 0,\n              modifier: 0,\n              change: 0,\n              shift: 0,\n              i: i,\n              thread: null\n            };\n            nodes.push(child);\n          }\n        }\n      }\n    }\n    /**\n     * The implementation of this function was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     *\n     * Computes a preliminary x coordinate for node. Before that, this function is\n     * applied recursively to the children of node, as well as the function\n     * apportion(). After spacing out the children by calling executeShifts(), the\n     * node is placed to the midpoint of its outermost children.\n     */\n\n    function firstWalk(node, separation) {\n      var children = node.isExpand ? node.children : [];\n      var siblings = node.parentNode.children;\n      var subtreeW = node.hierNode.i ? siblings[node.hierNode.i - 1] : null;\n\n      if (children.length) {\n        executeShifts(node);\n        var midPoint = (children[0].hierNode.prelim + children[children.length - 1].hierNode.prelim) / 2;\n\n        if (subtreeW) {\n          node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW);\n          node.hierNode.modifier = node.hierNode.prelim - midPoint;\n        } else {\n          node.hierNode.prelim = midPoint;\n        }\n      } else if (subtreeW) {\n        node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW);\n      }\n\n      node.parentNode.hierNode.defaultAncestor = apportion(node, subtreeW, node.parentNode.hierNode.defaultAncestor || siblings[0], separation);\n    }\n    /**\n     * The implementation of this function was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     *\n     * Computes all real x-coordinates by summing up the modifiers recursively.\n     */\n\n    function secondWalk(node) {\n      var nodeX = node.hierNode.prelim + node.parentNode.hierNode.modifier;\n      node.setLayout({\n        x: nodeX\n      }, true);\n      node.hierNode.modifier += node.parentNode.hierNode.modifier;\n    }\n    function separation(cb) {\n      return arguments.length ? cb : defaultSeparation;\n    }\n    /**\n     * Transform the common coordinate to radial coordinate.\n     */\n\n    function radialCoordinate(rad, r) {\n      rad -= Math.PI / 2;\n      return {\n        x: r * Math.cos(rad),\n        y: r * Math.sin(rad)\n      };\n    }\n    /**\n     * Get the layout position of the whole view.\n     */\n\n    function getViewRect$1(seriesModel, api) {\n      return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n        width: api.getWidth(),\n        height: api.getHeight()\n      });\n    }\n    /**\n     * All other shifts, applied to the smaller subtrees between w- and w+, are\n     * performed by this function.\n     *\n     * The implementation of this function was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     */\n\n    function executeShifts(node) {\n      var children = node.children;\n      var n = children.length;\n      var shift = 0;\n      var change = 0;\n\n      while (--n >= 0) {\n        var child = children[n];\n        child.hierNode.prelim += shift;\n        child.hierNode.modifier += shift;\n        change += child.hierNode.change;\n        shift += child.hierNode.shift + change;\n      }\n    }\n    /**\n     * The implementation of this function was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     *\n     * The core of the algorithm. Here, a new subtree is combined with the\n     * previous subtrees. Threads are used to traverse the inside and outside\n     * contours of the left and right subtree up to the highest common level.\n     * Whenever two nodes of the inside contours conflict, we compute the left\n     * one of the greatest uncommon ancestors using the function nextAncestor()\n     * and call moveSubtree() to shift the subtree and prepare the shifts of\n     * smaller subtrees. Finally, we add a new thread (if necessary).\n     */\n\n\n    function apportion(subtreeV, subtreeW, ancestor, separation) {\n      if (subtreeW) {\n        var nodeOutRight = subtreeV;\n        var nodeInRight = subtreeV;\n        var nodeOutLeft = nodeInRight.parentNode.children[0];\n        var nodeInLeft = subtreeW;\n        var sumOutRight = nodeOutRight.hierNode.modifier;\n        var sumInRight = nodeInRight.hierNode.modifier;\n        var sumOutLeft = nodeOutLeft.hierNode.modifier;\n        var sumInLeft = nodeInLeft.hierNode.modifier;\n\n        while (nodeInLeft = nextRight(nodeInLeft), nodeInRight = nextLeft(nodeInRight), nodeInLeft && nodeInRight) {\n          nodeOutRight = nextRight(nodeOutRight);\n          nodeOutLeft = nextLeft(nodeOutLeft);\n          nodeOutRight.hierNode.ancestor = subtreeV;\n          var shift = nodeInLeft.hierNode.prelim + sumInLeft - nodeInRight.hierNode.prelim - sumInRight + separation(nodeInLeft, nodeInRight);\n\n          if (shift > 0) {\n            moveSubtree(nextAncestor(nodeInLeft, subtreeV, ancestor), subtreeV, shift);\n            sumInRight += shift;\n            sumOutRight += shift;\n          }\n\n          sumInLeft += nodeInLeft.hierNode.modifier;\n          sumInRight += nodeInRight.hierNode.modifier;\n          sumOutRight += nodeOutRight.hierNode.modifier;\n          sumOutLeft += nodeOutLeft.hierNode.modifier;\n        }\n\n        if (nodeInLeft && !nextRight(nodeOutRight)) {\n          nodeOutRight.hierNode.thread = nodeInLeft;\n          nodeOutRight.hierNode.modifier += sumInLeft - sumOutRight;\n        }\n\n        if (nodeInRight && !nextLeft(nodeOutLeft)) {\n          nodeOutLeft.hierNode.thread = nodeInRight;\n          nodeOutLeft.hierNode.modifier += sumInRight - sumOutLeft;\n          ancestor = subtreeV;\n        }\n      }\n\n      return ancestor;\n    }\n    /**\n     * This function is used to traverse the right contour of a subtree.\n     * It returns the rightmost child of node or the thread of node. The function\n     * returns null if and only if node is on the highest depth of its subtree.\n     */\n\n\n    function nextRight(node) {\n      var children = node.children;\n      return children.length && node.isExpand ? children[children.length - 1] : node.hierNode.thread;\n    }\n    /**\n     * This function is used to traverse the left contour of a subtree (or a subforest).\n     * It returns the leftmost child of node or the thread of node. The function\n     * returns null if and only if node is on the highest depth of its subtree.\n     */\n\n\n    function nextLeft(node) {\n      var children = node.children;\n      return children.length && node.isExpand ? children[0] : node.hierNode.thread;\n    }\n    /**\n     * If nodeInLeft’s ancestor is a sibling of node, returns nodeInLeft’s ancestor.\n     * Otherwise, returns the specified ancestor.\n     */\n\n\n    function nextAncestor(nodeInLeft, node, ancestor) {\n      return nodeInLeft.hierNode.ancestor.parentNode === node.parentNode ? nodeInLeft.hierNode.ancestor : ancestor;\n    }\n    /**\n     * The implementation of this function was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     *\n     * Shifts the current subtree rooted at wr.\n     * This is done by increasing prelim(w+) and modifier(w+) by shift.\n     */\n\n\n    function moveSubtree(wl, wr, shift) {\n      var change = shift / (wr.hierNode.i - wl.hierNode.i);\n      wr.hierNode.change -= change;\n      wr.hierNode.shift += shift;\n      wr.hierNode.modifier += shift;\n      wr.hierNode.prelim += shift;\n      wl.hierNode.change += change;\n    }\n    /**\n     * The implementation of this function was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3-hierarchy/blob/4c1f038f2725d6eae2e49b61d01456400694bac4/src/tree.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     */\n\n\n    function defaultSeparation(node1, node2) {\n      return node1.parentNode === node2.parentNode ? 1 : 2;\n    }\n\n    var TreeEdgeShape =\n    /** @class */\n    function () {\n      function TreeEdgeShape() {\n        this.parentPoint = [];\n        this.childPoints = [];\n      }\n\n      return TreeEdgeShape;\n    }();\n\n    var TreePath =\n    /** @class */\n    function (_super) {\n      __extends(TreePath, _super);\n\n      function TreePath(opts) {\n        return _super.call(this, opts) || this;\n      }\n\n      TreePath.prototype.getDefaultStyle = function () {\n        return {\n          stroke: '#000',\n          fill: null\n        };\n      };\n\n      TreePath.prototype.getDefaultShape = function () {\n        return new TreeEdgeShape();\n      };\n\n      TreePath.prototype.buildPath = function (ctx, shape) {\n        var childPoints = shape.childPoints;\n        var childLen = childPoints.length;\n        var parentPoint = shape.parentPoint;\n        var firstChildPos = childPoints[0];\n        var lastChildPos = childPoints[childLen - 1];\n\n        if (childLen === 1) {\n          ctx.moveTo(parentPoint[0], parentPoint[1]);\n          ctx.lineTo(firstChildPos[0], firstChildPos[1]);\n          return;\n        }\n\n        var orient = shape.orient;\n        var forkDim = orient === 'TB' || orient === 'BT' ? 0 : 1;\n        var otherDim = 1 - forkDim;\n        var forkPosition = parsePercent$1(shape.forkPosition, 1);\n        var tmpPoint = [];\n        tmpPoint[forkDim] = parentPoint[forkDim];\n        tmpPoint[otherDim] = parentPoint[otherDim] + (lastChildPos[otherDim] - parentPoint[otherDim]) * forkPosition;\n        ctx.moveTo(parentPoint[0], parentPoint[1]);\n        ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n        ctx.moveTo(firstChildPos[0], firstChildPos[1]);\n        tmpPoint[forkDim] = firstChildPos[forkDim];\n        ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n        tmpPoint[forkDim] = lastChildPos[forkDim];\n        ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n        ctx.lineTo(lastChildPos[0], lastChildPos[1]);\n\n        for (var i = 1; i < childLen - 1; i++) {\n          var point = childPoints[i];\n          ctx.moveTo(point[0], point[1]);\n          tmpPoint[forkDim] = point[forkDim];\n          ctx.lineTo(tmpPoint[0], tmpPoint[1]);\n        }\n      };\n\n      return TreePath;\n    }(Path);\n\n    var TreeView =\n    /** @class */\n    function (_super) {\n      __extends(TreeView, _super);\n\n      function TreeView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TreeView.type;\n        _this._mainGroup = new Group();\n        return _this;\n      }\n\n      TreeView.prototype.init = function (ecModel, api) {\n        this._controller = new RoamController(api.getZr());\n        this._controllerHost = {\n          target: this.group\n        };\n        this.group.add(this._mainGroup);\n      };\n\n      TreeView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n        var layoutInfo = seriesModel.layoutInfo;\n        var group = this._mainGroup;\n        var layout = seriesModel.get('layout');\n\n        if (layout === 'radial') {\n          group.x = layoutInfo.x + layoutInfo.width / 2;\n          group.y = layoutInfo.y + layoutInfo.height / 2;\n        } else {\n          group.x = layoutInfo.x;\n          group.y = layoutInfo.y;\n        }\n\n        this._updateViewCoordSys(seriesModel, api);\n\n        this._updateController(seriesModel, ecModel, api);\n\n        var oldData = this._data;\n        data.diff(oldData).add(function (newIdx) {\n          if (symbolNeedsDraw$1(data, newIdx)) {\n            // Create node and edge\n            updateNode(data, newIdx, null, group, seriesModel);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var symbolEl = oldData.getItemGraphicEl(oldIdx);\n\n          if (!symbolNeedsDraw$1(data, newIdx)) {\n            symbolEl && removeNode(oldData, oldIdx, symbolEl, group, seriesModel);\n            return;\n          } // Update node and edge\n\n\n          updateNode(data, newIdx, symbolEl, group, seriesModel);\n        }).remove(function (oldIdx) {\n          var symbolEl = oldData.getItemGraphicEl(oldIdx); // When remove a collapsed node of subtree, since the collapsed\n          // node haven't been initialized with a symbol element,\n          // you can't found it's symbol element through index.\n          // so if we want to remove the symbol element we should insure\n          // that the symbol element is not null.\n\n          if (symbolEl) {\n            removeNode(oldData, oldIdx, symbolEl, group, seriesModel);\n          }\n        }).execute();\n        this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');\n\n        this._updateNodeAndLinkScale(seriesModel);\n\n        if (seriesModel.get('expandAndCollapse') === true) {\n          data.eachItemGraphicEl(function (el, dataIndex) {\n            el.off('click').on('click', function () {\n              api.dispatchAction({\n                type: 'treeExpandAndCollapse',\n                seriesId: seriesModel.id,\n                dataIndex: dataIndex\n              });\n            });\n          });\n        }\n\n        this._data = data;\n      };\n\n      TreeView.prototype._updateViewCoordSys = function (seriesModel, api) {\n        var data = seriesModel.getData();\n        var points = [];\n        data.each(function (idx) {\n          var layout = data.getItemLayout(idx);\n\n          if (layout && !isNaN(layout.x) && !isNaN(layout.y)) {\n            points.push([+layout.x, +layout.y]);\n          }\n        });\n        var min = [];\n        var max = [];\n        fromPoints(points, min, max); // If don't Store min max when collapse the root node after roam,\n        // the root node will disappear.\n\n        var oldMin = this._min;\n        var oldMax = this._max; // If width or height is 0\n\n        if (max[0] - min[0] === 0) {\n          min[0] = oldMin ? oldMin[0] : min[0] - 1;\n          max[0] = oldMax ? oldMax[0] : max[0] + 1;\n        }\n\n        if (max[1] - min[1] === 0) {\n          min[1] = oldMin ? oldMin[1] : min[1] - 1;\n          max[1] = oldMax ? oldMax[1] : max[1] + 1;\n        }\n\n        var viewCoordSys = seriesModel.coordinateSystem = new View();\n        viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');\n        viewCoordSys.setBoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n        viewCoordSys.setCenter(seriesModel.get('center'), api);\n        viewCoordSys.setZoom(seriesModel.get('zoom')); // Here we use viewCoordSys just for computing the 'position' and 'scale' of the group\n\n        this.group.attr({\n          x: viewCoordSys.x,\n          y: viewCoordSys.y,\n          scaleX: viewCoordSys.scaleX,\n          scaleY: viewCoordSys.scaleY\n        });\n        this._min = min;\n        this._max = max;\n      };\n\n      TreeView.prototype._updateController = function (seriesModel, ecModel, api) {\n        var _this = this;\n\n        var controller = this._controller;\n        var controllerHost = this._controllerHost;\n        var group = this.group;\n        controller.setPointerChecker(function (e, x, y) {\n          var rect = group.getBoundingRect();\n          rect.applyTransform(group.transform);\n          return rect.contain(x, y) && !onIrrelevantElement(e, api, seriesModel);\n        });\n        controller.enable(seriesModel.get('roam'));\n        controllerHost.zoomLimit = seriesModel.get('scaleLimit');\n        controllerHost.zoom = seriesModel.coordinateSystem.getZoom();\n        controller.off('pan').off('zoom').on('pan', function (e) {\n          updateViewOnPan(controllerHost, e.dx, e.dy);\n          api.dispatchAction({\n            seriesId: seriesModel.id,\n            type: 'treeRoam',\n            dx: e.dx,\n            dy: e.dy\n          });\n        }).on('zoom', function (e) {\n          updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);\n          api.dispatchAction({\n            seriesId: seriesModel.id,\n            type: 'treeRoam',\n            zoom: e.scale,\n            originX: e.originX,\n            originY: e.originY\n          });\n\n          _this._updateNodeAndLinkScale(seriesModel); // Only update label layout on zoom\n\n\n          api.updateLabelLayout();\n        });\n      };\n\n      TreeView.prototype._updateNodeAndLinkScale = function (seriesModel) {\n        var data = seriesModel.getData();\n\n        var nodeScale = this._getNodeGlobalScale(seriesModel);\n\n        data.eachItemGraphicEl(function (el, idx) {\n          el.setSymbolScale(nodeScale);\n        });\n      };\n\n      TreeView.prototype._getNodeGlobalScale = function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys.type !== 'view') {\n          return 1;\n        }\n\n        var nodeScaleRatio = this._nodeScaleRatio;\n        var groupZoom = coordSys.scaleX || 1; // Scale node when zoom changes\n\n        var roamZoom = coordSys.getZoom();\n        var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;\n        return nodeScale / groupZoom;\n      };\n\n      TreeView.prototype.dispose = function () {\n        this._controller && this._controller.dispose();\n        this._controllerHost = null;\n      };\n\n      TreeView.prototype.remove = function () {\n        this._mainGroup.removeAll();\n\n        this._data = null;\n      };\n\n      TreeView.type = 'tree';\n      return TreeView;\n    }(ChartView);\n\n    function symbolNeedsDraw$1(data, dataIndex) {\n      var layout = data.getItemLayout(dataIndex);\n      return layout && !isNaN(layout.x) && !isNaN(layout.y);\n    }\n\n    function updateNode(data, dataIndex, symbolEl, group, seriesModel) {\n      var isInit = !symbolEl;\n      var node = data.tree.getNodeByDataIndex(dataIndex);\n      var itemModel = node.getModel();\n      var visualColor = node.getVisual('style').fill;\n      var symbolInnerColor = node.isExpand === false && node.children.length !== 0 ? visualColor : '#fff';\n      var virtualRoot = data.tree.root;\n      var source = node.parentNode === virtualRoot ? node : node.parentNode || node;\n      var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex);\n      var sourceLayout = source.getLayout();\n      var sourceOldLayout = sourceSymbolEl ? {\n        x: sourceSymbolEl.__oldX,\n        y: sourceSymbolEl.__oldY,\n        rawX: sourceSymbolEl.__radialOldRawX,\n        rawY: sourceSymbolEl.__radialOldRawY\n      } : sourceLayout;\n      var targetLayout = node.getLayout();\n\n      if (isInit) {\n        symbolEl = new Symbol(data, dataIndex, null, {\n          symbolInnerColor: symbolInnerColor,\n          useNameLabel: true\n        });\n        symbolEl.x = sourceOldLayout.x;\n        symbolEl.y = sourceOldLayout.y;\n      } else {\n        symbolEl.updateData(data, dataIndex, null, {\n          symbolInnerColor: symbolInnerColor,\n          useNameLabel: true\n        });\n      }\n\n      symbolEl.__radialOldRawX = symbolEl.__radialRawX;\n      symbolEl.__radialOldRawY = symbolEl.__radialRawY;\n      symbolEl.__radialRawX = targetLayout.rawX;\n      symbolEl.__radialRawY = targetLayout.rawY;\n      group.add(symbolEl);\n      data.setItemGraphicEl(dataIndex, symbolEl);\n      symbolEl.__oldX = symbolEl.x;\n      symbolEl.__oldY = symbolEl.y;\n      updateProps(symbolEl, {\n        x: targetLayout.x,\n        y: targetLayout.y\n      }, seriesModel);\n      var symbolPath = symbolEl.getSymbolPath();\n\n      if (seriesModel.get('layout') === 'radial') {\n        var realRoot = virtualRoot.children[0];\n        var rootLayout = realRoot.getLayout();\n        var length_1 = realRoot.children.length;\n        var rad = void 0;\n        var isLeft = void 0;\n\n        if (targetLayout.x === rootLayout.x && node.isExpand === true && realRoot.children.length) {\n          var center = {\n            x: (realRoot.children[0].getLayout().x + realRoot.children[length_1 - 1].getLayout().x) / 2,\n            y: (realRoot.children[0].getLayout().y + realRoot.children[length_1 - 1].getLayout().y) / 2\n          };\n          rad = Math.atan2(center.y - rootLayout.y, center.x - rootLayout.x);\n\n          if (rad < 0) {\n            rad = Math.PI * 2 + rad;\n          }\n\n          isLeft = center.x < rootLayout.x;\n\n          if (isLeft) {\n            rad = rad - Math.PI;\n          }\n        } else {\n          rad = Math.atan2(targetLayout.y - rootLayout.y, targetLayout.x - rootLayout.x);\n\n          if (rad < 0) {\n            rad = Math.PI * 2 + rad;\n          }\n\n          if (node.children.length === 0 || node.children.length !== 0 && node.isExpand === false) {\n            isLeft = targetLayout.x < rootLayout.x;\n\n            if (isLeft) {\n              rad = rad - Math.PI;\n            }\n          } else {\n            isLeft = targetLayout.x > rootLayout.x;\n\n            if (!isLeft) {\n              rad = rad - Math.PI;\n            }\n          }\n        }\n\n        var textPosition = isLeft ? 'left' : 'right';\n        var normalLabelModel = itemModel.getModel('label');\n        var rotate = normalLabelModel.get('rotate');\n        var labelRotateRadian = rotate * (Math.PI / 180);\n        var textContent = symbolPath.getTextContent();\n\n        if (textContent) {\n          symbolPath.setTextConfig({\n            position: normalLabelModel.get('position') || textPosition,\n            rotation: rotate == null ? -rad : labelRotateRadian,\n            origin: 'center'\n          });\n          textContent.setStyle('verticalAlign', 'middle');\n        }\n      } // Handle status\n\n\n      var focus = itemModel.get(['emphasis', 'focus']);\n      var focusDataIndices = focus === 'relative' ? concatArray(node.getAncestorsIndices(), node.getDescendantIndices()) : focus === 'ancestor' ? node.getAncestorsIndices() : focus === 'descendant' ? node.getDescendantIndices() : null;\n\n      if (focusDataIndices) {\n        // Modify the focus to data indices.\n        getECData(symbolEl).focus = focusDataIndices;\n      }\n\n      drawEdge(seriesModel, node, virtualRoot, symbolEl, sourceOldLayout, sourceLayout, targetLayout, group);\n\n      if (symbolEl.__edge) {\n        symbolEl.onHoverStateChange = function (toState) {\n          if (toState !== 'blur') {\n            // NOTE: Ensure the parent elements will been blurred firstly.\n            // According to the return of getAncestorsIndices and getDescendantIndices\n            // TODO: A bit tricky.\n            var parentEl = node.parentNode && data.getItemGraphicEl(node.parentNode.dataIndex);\n\n            if (!(parentEl && parentEl.hoverState === HOVER_STATE_BLUR)) {\n              setStatesFlag(symbolEl.__edge, toState);\n            }\n          }\n        };\n      }\n    }\n\n    function drawEdge(seriesModel, node, virtualRoot, symbolEl, sourceOldLayout, sourceLayout, targetLayout, group) {\n      var itemModel = node.getModel();\n      var edgeShape = seriesModel.get('edgeShape');\n      var layout = seriesModel.get('layout');\n      var orient = seriesModel.getOrient();\n      var curvature = seriesModel.get(['lineStyle', 'curveness']);\n      var edgeForkPosition = seriesModel.get('edgeForkPosition');\n      var lineStyle = itemModel.getModel('lineStyle').getLineStyle();\n      var edge = symbolEl.__edge; // curve edge from node -> parent\n      // polyline edge from node -> children\n\n      if (edgeShape === 'curve') {\n        if (node.parentNode && node.parentNode !== virtualRoot) {\n          if (!edge) {\n            edge = symbolEl.__edge = new BezierCurve({\n              shape: getEdgeShape(layout, orient, curvature, sourceOldLayout, sourceOldLayout)\n            });\n          }\n\n          updateProps(edge, {\n            shape: getEdgeShape(layout, orient, curvature, sourceLayout, targetLayout)\n          }, seriesModel);\n        }\n      } else if (edgeShape === 'polyline') {\n        if (layout === 'orthogonal') {\n          if (node !== virtualRoot && node.children && node.children.length !== 0 && node.isExpand === true) {\n            var children = node.children;\n            var childPoints = [];\n\n            for (var i = 0; i < children.length; i++) {\n              var childLayout = children[i].getLayout();\n              childPoints.push([childLayout.x, childLayout.y]);\n            }\n\n            if (!edge) {\n              edge = symbolEl.__edge = new TreePath({\n                shape: {\n                  parentPoint: [targetLayout.x, targetLayout.y],\n                  childPoints: [[targetLayout.x, targetLayout.y]],\n                  orient: orient,\n                  forkPosition: edgeForkPosition\n                }\n              });\n            }\n\n            updateProps(edge, {\n              shape: {\n                parentPoint: [targetLayout.x, targetLayout.y],\n                childPoints: childPoints\n              }\n            }, seriesModel);\n          }\n        } else {\n          if (\"development\" !== 'production') {\n            throw new Error('The polyline edgeShape can only be used in orthogonal layout');\n          }\n        }\n      } // show all edge when edgeShape is 'curve', filter node `isExpand` is false when edgeShape is 'polyline'\n\n\n      if (edge && !(edgeShape === 'polyline' && !node.isExpand)) {\n        edge.useStyle(defaults({\n          strokeNoScale: true,\n          fill: null\n        }, lineStyle));\n        setStatesStylesFromModel(edge, itemModel, 'lineStyle');\n        setDefaultStateProxy(edge);\n        group.add(edge);\n      }\n    }\n\n    function removeNodeEdge(node, data, group, seriesModel, removeAnimationOpt) {\n      var virtualRoot = data.tree.root;\n\n      var _a = getSourceNode(virtualRoot, node),\n          source = _a.source,\n          sourceLayout = _a.sourceLayout;\n\n      var symbolEl = data.getItemGraphicEl(node.dataIndex);\n\n      if (!symbolEl) {\n        return;\n      }\n\n      var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex);\n      var sourceEdge = sourceSymbolEl.__edge; // 1. when expand the sub tree, delete the children node should delete the edge of\n      // the source at the same time. because the polyline edge shape is only owned by the source.\n      // 2.when the node is the only children of the source, delete the node should delete the edge of\n      // the source at the same time. the same reason as above.\n\n      var edge = symbolEl.__edge || (source.isExpand === false || source.children.length === 1 ? sourceEdge : undefined);\n      var edgeShape = seriesModel.get('edgeShape');\n      var layoutOpt = seriesModel.get('layout');\n      var orient = seriesModel.get('orient');\n      var curvature = seriesModel.get(['lineStyle', 'curveness']);\n\n      if (edge) {\n        if (edgeShape === 'curve') {\n          removeElement(edge, {\n            shape: getEdgeShape(layoutOpt, orient, curvature, sourceLayout, sourceLayout),\n            style: {\n              opacity: 0\n            }\n          }, seriesModel, {\n            cb: function () {\n              group.remove(edge);\n            },\n            removeOpt: removeAnimationOpt\n          });\n        } else if (edgeShape === 'polyline' && seriesModel.get('layout') === 'orthogonal') {\n          removeElement(edge, {\n            shape: {\n              parentPoint: [sourceLayout.x, sourceLayout.y],\n              childPoints: [[sourceLayout.x, sourceLayout.y]]\n            },\n            style: {\n              opacity: 0\n            }\n          }, seriesModel, {\n            cb: function () {\n              group.remove(edge);\n            },\n            removeOpt: removeAnimationOpt\n          });\n        }\n      }\n    }\n\n    function getSourceNode(virtualRoot, node) {\n      var source = node.parentNode === virtualRoot ? node : node.parentNode || node;\n      var sourceLayout;\n\n      while (sourceLayout = source.getLayout(), sourceLayout == null) {\n        source = source.parentNode === virtualRoot ? source : source.parentNode || source;\n      }\n\n      return {\n        source: source,\n        sourceLayout: sourceLayout\n      };\n    }\n\n    function removeNode(data, dataIndex, symbolEl, group, seriesModel) {\n      var node = data.tree.getNodeByDataIndex(dataIndex);\n      var virtualRoot = data.tree.root;\n      var sourceLayout = getSourceNode(virtualRoot, node).sourceLayout; // Use same duration and easing with update to have more consistent animation.\n\n      var removeAnimationOpt = {\n        duration: seriesModel.get('animationDurationUpdate'),\n        easing: seriesModel.get('animationEasingUpdate')\n      };\n      removeElement(symbolEl, {\n        x: sourceLayout.x + 1,\n        y: sourceLayout.y + 1\n      }, seriesModel, {\n        cb: function () {\n          group.remove(symbolEl);\n          data.setItemGraphicEl(dataIndex, null);\n        },\n        removeOpt: removeAnimationOpt\n      });\n      symbolEl.fadeOut(null, data.hostModel, {\n        fadeLabel: true,\n        animation: removeAnimationOpt\n      }); // remove edge as parent node\n\n      node.children.forEach(function (childNode) {\n        removeNodeEdge(childNode, data, group, seriesModel, removeAnimationOpt);\n      }); // remove edge as child node\n\n      removeNodeEdge(node, data, group, seriesModel, removeAnimationOpt);\n    }\n\n    function getEdgeShape(layoutOpt, orient, curvature, sourceLayout, targetLayout) {\n      var cpx1;\n      var cpy1;\n      var cpx2;\n      var cpy2;\n      var x1;\n      var x2;\n      var y1;\n      var y2;\n\n      if (layoutOpt === 'radial') {\n        x1 = sourceLayout.rawX;\n        y1 = sourceLayout.rawY;\n        x2 = targetLayout.rawX;\n        y2 = targetLayout.rawY;\n        var radialCoor1 = radialCoordinate(x1, y1);\n        var radialCoor2 = radialCoordinate(x1, y1 + (y2 - y1) * curvature);\n        var radialCoor3 = radialCoordinate(x2, y2 + (y1 - y2) * curvature);\n        var radialCoor4 = radialCoordinate(x2, y2);\n        return {\n          x1: radialCoor1.x || 0,\n          y1: radialCoor1.y || 0,\n          x2: radialCoor4.x || 0,\n          y2: radialCoor4.y || 0,\n          cpx1: radialCoor2.x || 0,\n          cpy1: radialCoor2.y || 0,\n          cpx2: radialCoor3.x || 0,\n          cpy2: radialCoor3.y || 0\n        };\n      } else {\n        x1 = sourceLayout.x;\n        y1 = sourceLayout.y;\n        x2 = targetLayout.x;\n        y2 = targetLayout.y;\n\n        if (orient === 'LR' || orient === 'RL') {\n          cpx1 = x1 + (x2 - x1) * curvature;\n          cpy1 = y1;\n          cpx2 = x2 + (x1 - x2) * curvature;\n          cpy2 = y2;\n        }\n\n        if (orient === 'TB' || orient === 'BT') {\n          cpx1 = x1;\n          cpy1 = y1 + (y2 - y1) * curvature;\n          cpx2 = x2;\n          cpy2 = y2 + (y1 - y2) * curvature;\n        }\n      }\n\n      return {\n        x1: x1,\n        y1: y1,\n        x2: x2,\n        y2: y2,\n        cpx1: cpx1,\n        cpy1: cpy1,\n        cpx2: cpx2,\n        cpy2: cpy2\n      };\n    }\n\n    var inner$7 = makeInner();\n\n    function linkSeriesData(opt) {\n      var mainData = opt.mainData;\n      var datas = opt.datas;\n\n      if (!datas) {\n        datas = {\n          main: mainData\n        };\n        opt.datasAttr = {\n          main: 'data'\n        };\n      }\n\n      opt.datas = opt.mainData = null;\n      linkAll(mainData, datas, opt); // Porxy data original methods.\n\n      each(datas, function (data) {\n        each(mainData.TRANSFERABLE_METHODS, function (methodName) {\n          data.wrapMethod(methodName, curry(transferInjection, opt));\n        });\n      }); // Beyond transfer, additional features should be added to `cloneShallow`.\n\n      mainData.wrapMethod('cloneShallow', curry(cloneShallowInjection, opt)); // Only mainData trigger change, because struct.update may trigger\n      // another changable methods, which may bring about dead lock.\n\n      each(mainData.CHANGABLE_METHODS, function (methodName) {\n        mainData.wrapMethod(methodName, curry(changeInjection, opt));\n      }); // Make sure datas contains mainData.\n\n      assert(datas[mainData.dataType] === mainData);\n    }\n\n    function transferInjection(opt, res) {\n      if (isMainData(this)) {\n        // Transfer datas to new main data.\n        var datas = extend({}, inner$7(this).datas);\n        datas[this.dataType] = res;\n        linkAll(res, datas, opt);\n      } else {\n        // Modify the reference in main data to point newData.\n        linkSingle(res, this.dataType, inner$7(this).mainData, opt);\n      }\n\n      return res;\n    }\n\n    function changeInjection(opt, res) {\n      opt.struct && opt.struct.update();\n      return res;\n    }\n\n    function cloneShallowInjection(opt, res) {\n      // cloneShallow, which brings about some fragilities, may be inappropriate\n      // to be exposed as an API. So for implementation simplicity we can make\n      // the restriction that cloneShallow of not-mainData should not be invoked\n      // outside, but only be invoked here.\n      each(inner$7(res).datas, function (data, dataType) {\n        data !== res && linkSingle(data.cloneShallow(), dataType, res, opt);\n      });\n      return res;\n    }\n    /**\n     * Supplement method to List.\n     *\n     * @public\n     * @param [dataType] If not specified, return mainData.\n     */\n\n\n    function getLinkedData(dataType) {\n      var mainData = inner$7(this).mainData;\n      return dataType == null || mainData == null ? mainData : inner$7(mainData).datas[dataType];\n    }\n    /**\n     * Get list of all linked data\n     */\n\n\n    function getLinkedDataAll() {\n      var mainData = inner$7(this).mainData;\n      return mainData == null ? [{\n        data: mainData\n      }] : map(keys(inner$7(mainData).datas), function (type) {\n        return {\n          type: type,\n          data: inner$7(mainData).datas[type]\n        };\n      });\n    }\n\n    function isMainData(data) {\n      return inner$7(data).mainData === data;\n    }\n\n    function linkAll(mainData, datas, opt) {\n      inner$7(mainData).datas = {};\n      each(datas, function (data, dataType) {\n        linkSingle(data, dataType, mainData, opt);\n      });\n    }\n\n    function linkSingle(data, dataType, mainData, opt) {\n      inner$7(mainData).datas[dataType] = data;\n      inner$7(data).mainData = mainData;\n      data.dataType = dataType;\n\n      if (opt.struct) {\n        data[opt.structAttr] = opt.struct;\n        opt.struct[opt.datasAttr[dataType]] = data;\n      } // Supplement method.\n\n\n      data.getLinkedData = getLinkedData;\n      data.getLinkedDataAll = getLinkedDataAll;\n    }\n\n    var TreeNode =\n    /** @class */\n    function () {\n      function TreeNode(name, hostTree) {\n        this.depth = 0;\n        this.height = 0;\n        /**\n         * Reference to list item.\n         * Do not persistent dataIndex outside,\n         * besause it may be changed by list.\n         * If dataIndex -1,\n         * this node is logical deleted (filtered) in list.\n         */\n\n        this.dataIndex = -1;\n        this.children = [];\n        this.viewChildren = [];\n        this.isExpand = false;\n        this.name = name || '';\n        this.hostTree = hostTree;\n      }\n      /**\n       * The node is removed.\n       */\n\n\n      TreeNode.prototype.isRemoved = function () {\n        return this.dataIndex < 0;\n      };\n\n      TreeNode.prototype.eachNode = function (options, cb, context) {\n        if (isFunction(options)) {\n          context = cb;\n          cb = options;\n          options = null;\n        }\n\n        options = options || {};\n\n        if (isString(options)) {\n          options = {\n            order: options\n          };\n        }\n\n        var order = options.order || 'preorder';\n        var children = this[options.attr || 'children'];\n        var suppressVisitSub;\n        order === 'preorder' && (suppressVisitSub = cb.call(context, this));\n\n        for (var i = 0; !suppressVisitSub && i < children.length; i++) {\n          children[i].eachNode(options, cb, context);\n        }\n\n        order === 'postorder' && cb.call(context, this);\n      };\n      /**\n       * Update depth and height of this subtree.\n       */\n\n\n      TreeNode.prototype.updateDepthAndHeight = function (depth) {\n        var height = 0;\n        this.depth = depth;\n\n        for (var i = 0; i < this.children.length; i++) {\n          var child = this.children[i];\n          child.updateDepthAndHeight(depth + 1);\n\n          if (child.height > height) {\n            height = child.height;\n          }\n        }\n\n        this.height = height + 1;\n      };\n\n      TreeNode.prototype.getNodeById = function (id) {\n        if (this.getId() === id) {\n          return this;\n        }\n\n        for (var i = 0, children = this.children, len = children.length; i < len; i++) {\n          var res = children[i].getNodeById(id);\n\n          if (res) {\n            return res;\n          }\n        }\n      };\n\n      TreeNode.prototype.contains = function (node) {\n        if (node === this) {\n          return true;\n        }\n\n        for (var i = 0, children = this.children, len = children.length; i < len; i++) {\n          var res = children[i].contains(node);\n\n          if (res) {\n            return res;\n          }\n        }\n      };\n      /**\n       * @param includeSelf Default false.\n       * @return order: [root, child, grandchild, ...]\n       */\n\n\n      TreeNode.prototype.getAncestors = function (includeSelf) {\n        var ancestors = [];\n        var node = includeSelf ? this : this.parentNode;\n\n        while (node) {\n          ancestors.push(node);\n          node = node.parentNode;\n        }\n\n        ancestors.reverse();\n        return ancestors;\n      };\n\n      TreeNode.prototype.getAncestorsIndices = function () {\n        var indices = [];\n        var currNode = this;\n\n        while (currNode) {\n          indices.push(currNode.dataIndex);\n          currNode = currNode.parentNode;\n        }\n\n        indices.reverse();\n        return indices;\n      };\n\n      TreeNode.prototype.getDescendantIndices = function () {\n        var indices = [];\n        this.eachNode(function (childNode) {\n          indices.push(childNode.dataIndex);\n        });\n        return indices;\n      };\n\n      TreeNode.prototype.getValue = function (dimension) {\n        var data = this.hostTree.data;\n        return data.getStore().get(data.getDimensionIndex(dimension || 'value'), this.dataIndex);\n      };\n\n      TreeNode.prototype.setLayout = function (layout, merge) {\n        this.dataIndex >= 0 && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge);\n      };\n      /**\n       * @return {Object} layout\n       */\n\n\n      TreeNode.prototype.getLayout = function () {\n        return this.hostTree.data.getItemLayout(this.dataIndex);\n      }; // @depcrecated\n      // getModel<T = unknown, S extends keyof T = keyof T>(path: S): Model<T[S]>\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n\n\n      TreeNode.prototype.getModel = function (path) {\n        if (this.dataIndex < 0) {\n          return;\n        }\n\n        var hostTree = this.hostTree;\n        var itemModel = hostTree.data.getItemModel(this.dataIndex);\n        return itemModel.getModel(path);\n      }; // TODO: TYPE More specific model\n\n\n      TreeNode.prototype.getLevelModel = function () {\n        return (this.hostTree.levelModels || [])[this.depth];\n      };\n\n      TreeNode.prototype.setVisual = function (key, value) {\n        this.dataIndex >= 0 && this.hostTree.data.setItemVisual(this.dataIndex, key, value);\n      };\n      /**\n       * Get item visual\n       * FIXME: make return type better\n       */\n\n\n      TreeNode.prototype.getVisual = function (key) {\n        return this.hostTree.data.getItemVisual(this.dataIndex, key);\n      };\n\n      TreeNode.prototype.getRawIndex = function () {\n        return this.hostTree.data.getRawIndex(this.dataIndex);\n      };\n\n      TreeNode.prototype.getId = function () {\n        return this.hostTree.data.getId(this.dataIndex);\n      };\n      /**\n       * index in parent's children\n       */\n\n\n      TreeNode.prototype.getChildIndex = function () {\n        if (this.parentNode) {\n          var children = this.parentNode.children;\n\n          for (var i = 0; i < children.length; ++i) {\n            if (children[i] === this) {\n              return i;\n            }\n          }\n\n          return -1;\n        }\n\n        return -1;\n      };\n      /**\n       * if this is an ancestor of another node\n       *\n       * @param node another node\n       * @return if is ancestor\n       */\n\n\n      TreeNode.prototype.isAncestorOf = function (node) {\n        var parent = node.parentNode;\n\n        while (parent) {\n          if (parent === this) {\n            return true;\n          }\n\n          parent = parent.parentNode;\n        }\n\n        return false;\n      };\n      /**\n       * if this is an descendant of another node\n       *\n       * @param node another node\n       * @return if is descendant\n       */\n\n\n      TreeNode.prototype.isDescendantOf = function (node) {\n        return node !== this && node.isAncestorOf(this);\n      };\n\n      return TreeNode;\n    }();\n\n    var Tree =\n    /** @class */\n    function () {\n      function Tree(hostModel) {\n        this.type = 'tree';\n        this._nodes = [];\n        this.hostModel = hostModel;\n      }\n\n      Tree.prototype.eachNode = function (options, cb, context) {\n        this.root.eachNode(options, cb, context);\n      };\n\n      Tree.prototype.getNodeByDataIndex = function (dataIndex) {\n        var rawIndex = this.data.getRawIndex(dataIndex);\n        return this._nodes[rawIndex];\n      };\n\n      Tree.prototype.getNodeById = function (name) {\n        return this.root.getNodeById(name);\n      };\n      /**\n       * Update item available by list,\n       * when list has been performed options like 'filterSelf' or 'map'.\n       */\n\n\n      Tree.prototype.update = function () {\n        var data = this.data;\n        var nodes = this._nodes;\n\n        for (var i = 0, len = nodes.length; i < len; i++) {\n          nodes[i].dataIndex = -1;\n        }\n\n        for (var i = 0, len = data.count(); i < len; i++) {\n          nodes[data.getRawIndex(i)].dataIndex = i;\n        }\n      };\n      /**\n       * Clear all layouts\n       */\n\n\n      Tree.prototype.clearLayouts = function () {\n        this.data.clearItemLayouts();\n      };\n      /**\n       * data node format:\n       * {\n       *     name: ...\n       *     value: ...\n       *     children: [\n       *         {\n       *             name: ...\n       *             value: ...\n       *             children: ...\n       *         },\n       *         ...\n       *     ]\n       * }\n       */\n\n\n      Tree.createTree = function (dataRoot, hostModel, beforeLink) {\n        var tree = new Tree(hostModel);\n        var listData = [];\n        var dimMax = 1;\n        buildHierarchy(dataRoot);\n\n        function buildHierarchy(dataNode, parentNode) {\n          var value = dataNode.value;\n          dimMax = Math.max(dimMax, isArray(value) ? value.length : 1);\n          listData.push(dataNode);\n          var node = new TreeNode(convertOptionIdName(dataNode.name, ''), tree);\n          parentNode ? addChild(node, parentNode) : tree.root = node;\n\n          tree._nodes.push(node);\n\n          var children = dataNode.children;\n\n          if (children) {\n            for (var i = 0; i < children.length; i++) {\n              buildHierarchy(children[i], node);\n            }\n          }\n        }\n\n        tree.root.updateDepthAndHeight(0);\n        var dimensions = prepareSeriesDataSchema(listData, {\n          coordDimensions: ['value'],\n          dimensionsCount: dimMax\n        }).dimensions;\n        var list = new SeriesData(dimensions, hostModel);\n        list.initData(listData);\n        beforeLink && beforeLink(list);\n        linkSeriesData({\n          mainData: list,\n          struct: tree,\n          structAttr: 'tree'\n        });\n        tree.update();\n        return tree;\n      };\n\n      return Tree;\n    }();\n    /**\n     * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote,\n     * so this function is not ready and not necessary to be public.\n     */\n\n\n    function addChild(child, node) {\n      var children = node.children;\n\n      if (child.parentNode === node) {\n        return;\n      }\n\n      children.push(child);\n      child.parentNode = node;\n    }\n\n    function retrieveTargetInfo(payload, validPayloadTypes, seriesModel) {\n      if (payload && indexOf(validPayloadTypes, payload.type) >= 0) {\n        var root = seriesModel.getData().tree.root;\n        var targetNode = payload.targetNode;\n\n        if (isString(targetNode)) {\n          targetNode = root.getNodeById(targetNode);\n        }\n\n        if (targetNode && root.contains(targetNode)) {\n          return {\n            node: targetNode\n          };\n        }\n\n        var targetNodeId = payload.targetNodeId;\n\n        if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) {\n          return {\n            node: targetNode\n          };\n        }\n      }\n    } // Not includes the given node at the last item.\n\n    function getPathToRoot(node) {\n      var path = [];\n\n      while (node) {\n        node = node.parentNode;\n        node && path.push(node);\n      }\n\n      return path.reverse();\n    }\n    function aboveViewRoot(viewRoot, node) {\n      var viewPath = getPathToRoot(viewRoot);\n      return indexOf(viewPath, node) >= 0;\n    } // From root to the input node (the input node will be included).\n\n    function wrapTreePathInfo(node, seriesModel) {\n      var treePathInfo = [];\n\n      while (node) {\n        var nodeDataIndex = node.dataIndex;\n        treePathInfo.push({\n          name: node.name,\n          dataIndex: nodeDataIndex,\n          value: seriesModel.getRawValue(nodeDataIndex)\n        });\n        node = node.parentNode;\n      }\n\n      treePathInfo.reverse();\n      return treePathInfo;\n    }\n\n    var TreeSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(TreeSeriesModel, _super);\n\n      function TreeSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.hasSymbolVisual = true; // Do it self.\n\n        _this.ignoreStyleOnData = true;\n        return _this;\n      }\n      /**\n       * Init a tree data structure from data in option series\n       */\n\n\n      TreeSeriesModel.prototype.getInitialData = function (option) {\n        // create a virtual root\n        var root = {\n          name: option.name,\n          children: option.data\n        };\n        var leaves = option.leaves || {};\n        var leavesModel = new Model(leaves, this, this.ecModel);\n        var tree = Tree.createTree(root, this, beforeLink);\n\n        function beforeLink(nodeData) {\n          nodeData.wrapMethod('getItemModel', function (model, idx) {\n            var node = tree.getNodeByDataIndex(idx);\n\n            if (!(node && node.children.length && node.isExpand)) {\n              model.parentModel = leavesModel;\n            }\n\n            return model;\n          });\n        }\n\n        var treeDepth = 0;\n        tree.eachNode('preorder', function (node) {\n          if (node.depth > treeDepth) {\n            treeDepth = node.depth;\n          }\n        });\n        var expandAndCollapse = option.expandAndCollapse;\n        var expandTreeDepth = expandAndCollapse && option.initialTreeDepth >= 0 ? option.initialTreeDepth : treeDepth;\n        tree.root.eachNode('preorder', function (node) {\n          var item = node.hostTree.data.getRawDataItem(node.dataIndex); // Add item.collapsed != null, because users can collapse node original in the series.data.\n\n          node.isExpand = item && item.collapsed != null ? !item.collapsed : node.depth <= expandTreeDepth;\n        });\n        return tree.data;\n      };\n      /**\n       * Make the configuration 'orient' backward compatibly, with 'horizontal = LR', 'vertical = TB'.\n       * @returns {string} orient\n       */\n\n\n      TreeSeriesModel.prototype.getOrient = function () {\n        var orient = this.get('orient');\n\n        if (orient === 'horizontal') {\n          orient = 'LR';\n        } else if (orient === 'vertical') {\n          orient = 'TB';\n        }\n\n        return orient;\n      };\n\n      TreeSeriesModel.prototype.setZoom = function (zoom) {\n        this.option.zoom = zoom;\n      };\n\n      TreeSeriesModel.prototype.setCenter = function (center) {\n        this.option.center = center;\n      };\n\n      TreeSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        var tree = this.getData().tree;\n        var realRoot = tree.root.children[0];\n        var node = tree.getNodeByDataIndex(dataIndex);\n        var value = node.getValue();\n        var name = node.name;\n\n        while (node && node !== realRoot) {\n          name = node.parentNode.name + '.' + name;\n          node = node.parentNode;\n        }\n\n        return createTooltipMarkup('nameValue', {\n          name: name,\n          value: value,\n          noValue: isNaN(value) || value == null\n        });\n      }; // Add tree path to tooltip param\n\n\n      TreeSeriesModel.prototype.getDataParams = function (dataIndex) {\n        var params = _super.prototype.getDataParams.apply(this, arguments);\n\n        var node = this.getData().tree.getNodeByDataIndex(dataIndex);\n        params.treeAncestors = wrapTreePathInfo(node, this);\n        params.collapsed = !node.isExpand;\n        return params;\n      };\n\n      TreeSeriesModel.type = 'series.tree'; // can support the position parameters 'left', 'top','right','bottom', 'width',\n      // 'height' in the setOption() with 'merge' mode normal.\n\n      TreeSeriesModel.layoutMode = 'box';\n      TreeSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'view',\n        // the position of the whole view\n        left: '12%',\n        top: '12%',\n        right: '12%',\n        bottom: '12%',\n        // the layout of the tree, two value can be selected, 'orthogonal' or 'radial'\n        layout: 'orthogonal',\n        // value can be 'polyline'\n        edgeShape: 'curve',\n        edgeForkPosition: '50%',\n        // true | false | 'move' | 'scale', see module:component/helper/RoamController.\n        roam: false,\n        // Symbol size scale ratio in roam\n        nodeScaleRatio: 0.4,\n        // Default on center of graph\n        center: null,\n        zoom: 1,\n        orient: 'LR',\n        symbol: 'emptyCircle',\n        symbolSize: 7,\n        expandAndCollapse: true,\n        initialTreeDepth: 2,\n        lineStyle: {\n          color: '#ccc',\n          width: 1.5,\n          curveness: 0.5\n        },\n        itemStyle: {\n          color: 'lightsteelblue',\n          // borderColor: '#c23531',\n          borderWidth: 1.5\n        },\n        label: {\n          show: true\n        },\n        animationEasing: 'linear',\n        animationDuration: 700,\n        animationDurationUpdate: 500\n      };\n      return TreeSeriesModel;\n    }(SeriesModel);\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * Traverse the tree from bottom to top and do something\n     */\n    function eachAfter(root, callback, separation) {\n      var nodes = [root];\n      var next = [];\n      var node;\n\n      while (node = nodes.pop()) {\n        // jshint ignore:line\n        next.push(node);\n\n        if (node.isExpand) {\n          var children = node.children;\n\n          if (children.length) {\n            for (var i = 0; i < children.length; i++) {\n              nodes.push(children[i]);\n            }\n          }\n        }\n      }\n\n      while (node = next.pop()) {\n        // jshint ignore:line\n        callback(node, separation);\n      }\n    }\n    /**\n     * Traverse the tree from top to bottom and do something\n     */\n\n\n    function eachBefore(root, callback) {\n      var nodes = [root];\n      var node;\n\n      while (node = nodes.pop()) {\n        // jshint ignore:line\n        callback(node);\n\n        if (node.isExpand) {\n          var children = node.children;\n\n          if (children.length) {\n            for (var i = children.length - 1; i >= 0; i--) {\n              nodes.push(children[i]);\n            }\n          }\n        }\n      }\n    }\n\n    function treeLayout(ecModel, api) {\n      ecModel.eachSeriesByType('tree', function (seriesModel) {\n        commonLayout(seriesModel, api);\n      });\n    }\n\n    function commonLayout(seriesModel, api) {\n      var layoutInfo = getViewRect$1(seriesModel, api);\n      seriesModel.layoutInfo = layoutInfo;\n      var layout = seriesModel.get('layout');\n      var width = 0;\n      var height = 0;\n      var separation$1 = null;\n\n      if (layout === 'radial') {\n        width = 2 * Math.PI;\n        height = Math.min(layoutInfo.height, layoutInfo.width) / 2;\n        separation$1 = separation(function (node1, node2) {\n          return (node1.parentNode === node2.parentNode ? 1 : 2) / node1.depth;\n        });\n      } else {\n        width = layoutInfo.width;\n        height = layoutInfo.height;\n        separation$1 = separation();\n      }\n\n      var virtualRoot = seriesModel.getData().tree.root;\n      var realRoot = virtualRoot.children[0];\n\n      if (realRoot) {\n        init$2(virtualRoot);\n        eachAfter(realRoot, firstWalk, separation$1);\n        virtualRoot.hierNode.modifier = -realRoot.hierNode.prelim;\n        eachBefore(realRoot, secondWalk);\n        var left_1 = realRoot;\n        var right_1 = realRoot;\n        var bottom_1 = realRoot;\n        eachBefore(realRoot, function (node) {\n          var x = node.getLayout().x;\n\n          if (x < left_1.getLayout().x) {\n            left_1 = node;\n          }\n\n          if (x > right_1.getLayout().x) {\n            right_1 = node;\n          }\n\n          if (node.depth > bottom_1.depth) {\n            bottom_1 = node;\n          }\n        });\n        var delta = left_1 === right_1 ? 1 : separation$1(left_1, right_1) / 2;\n        var tx_1 = delta - left_1.getLayout().x;\n        var kx_1 = 0;\n        var ky_1 = 0;\n        var coorX_1 = 0;\n        var coorY_1 = 0;\n\n        if (layout === 'radial') {\n          kx_1 = width / (right_1.getLayout().x + delta + tx_1); // here we use (node.depth - 1), bucause the real root's depth is 1\n\n          ky_1 = height / (bottom_1.depth - 1 || 1);\n          eachBefore(realRoot, function (node) {\n            coorX_1 = (node.getLayout().x + tx_1) * kx_1;\n            coorY_1 = (node.depth - 1) * ky_1;\n            var finalCoor = radialCoordinate(coorX_1, coorY_1);\n            node.setLayout({\n              x: finalCoor.x,\n              y: finalCoor.y,\n              rawX: coorX_1,\n              rawY: coorY_1\n            }, true);\n          });\n        } else {\n          var orient_1 = seriesModel.getOrient();\n\n          if (orient_1 === 'RL' || orient_1 === 'LR') {\n            ky_1 = height / (right_1.getLayout().x + delta + tx_1);\n            kx_1 = width / (bottom_1.depth - 1 || 1);\n            eachBefore(realRoot, function (node) {\n              coorY_1 = (node.getLayout().x + tx_1) * ky_1;\n              coorX_1 = orient_1 === 'LR' ? (node.depth - 1) * kx_1 : width - (node.depth - 1) * kx_1;\n              node.setLayout({\n                x: coorX_1,\n                y: coorY_1\n              }, true);\n            });\n          } else if (orient_1 === 'TB' || orient_1 === 'BT') {\n            kx_1 = width / (right_1.getLayout().x + delta + tx_1);\n            ky_1 = height / (bottom_1.depth - 1 || 1);\n            eachBefore(realRoot, function (node) {\n              coorX_1 = (node.getLayout().x + tx_1) * kx_1;\n              coorY_1 = orient_1 === 'TB' ? (node.depth - 1) * ky_1 : height - (node.depth - 1) * ky_1;\n              node.setLayout({\n                x: coorX_1,\n                y: coorY_1\n              }, true);\n            });\n          }\n        }\n      }\n    }\n\n    function treeVisual(ecModel) {\n      ecModel.eachSeriesByType('tree', function (seriesModel) {\n        var data = seriesModel.getData();\n        var tree = data.tree;\n        tree.eachNode(function (node) {\n          var model = node.getModel(); // TODO Optimize\n\n          var style = model.getModel('itemStyle').getItemStyle();\n          var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style');\n          extend(existsStyle, style);\n        });\n      });\n    }\n\n    function installTreeAction(registers) {\n      registers.registerAction({\n        type: 'treeExpandAndCollapse',\n        event: 'treeExpandAndCollapse',\n        update: 'update'\n      }, function (payload, ecModel) {\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'tree',\n          query: payload\n        }, function (seriesModel) {\n          var dataIndex = payload.dataIndex;\n          var tree = seriesModel.getData().tree;\n          var node = tree.getNodeByDataIndex(dataIndex);\n          node.isExpand = !node.isExpand;\n        });\n      });\n      registers.registerAction({\n        type: 'treeRoam',\n        event: 'treeRoam',\n        // Here we set 'none' instead of 'update', because roam action\n        // just need to update the transform matrix without having to recalculate\n        // the layout. So don't need to go through the whole update process, such\n        // as 'dataPrcocess', 'coordSystemUpdate', 'layout' and so on.\n        update: 'none'\n      }, function (payload, ecModel, api) {\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'tree',\n          query: payload\n        }, function (seriesModel) {\n          var coordSys = seriesModel.coordinateSystem;\n          var res = updateCenterAndZoom(coordSys, payload, undefined, api);\n          seriesModel.setCenter && seriesModel.setCenter(res.center);\n          seriesModel.setZoom && seriesModel.setZoom(res.zoom);\n        });\n      });\n    }\n\n    function install$b(registers) {\n      registers.registerChartView(TreeView);\n      registers.registerSeriesModel(TreeSeriesModel);\n      registers.registerLayout(treeLayout);\n      registers.registerVisual(treeVisual);\n      installTreeAction(registers);\n    }\n\n    var actionTypes = ['treemapZoomToNode', 'treemapRender', 'treemapMove'];\n    function installTreemapAction(registers) {\n      for (var i = 0; i < actionTypes.length; i++) {\n        registers.registerAction({\n          type: actionTypes[i],\n          update: 'updateView'\n        }, noop);\n      }\n\n      registers.registerAction({\n        type: 'treemapRootToNode',\n        update: 'updateView'\n      }, function (payload, ecModel) {\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'treemap',\n          query: payload\n        }, handleRootToNode);\n\n        function handleRootToNode(model, index) {\n          var types = ['treemapZoomToNode', 'treemapRootToNode'];\n          var targetInfo = retrieveTargetInfo(payload, types, model);\n\n          if (targetInfo) {\n            var originViewRoot = model.getViewRoot();\n\n            if (originViewRoot) {\n              payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) ? 'rollUp' : 'drillDown';\n            }\n\n            model.resetViewRoot(targetInfo.node);\n          }\n        }\n      });\n    }\n\n    function enableAriaDecalForTree(seriesModel) {\n      var data = seriesModel.getData();\n      var tree = data.tree;\n      var decalPaletteScope = {};\n      tree.eachNode(function (node) {\n        // Use decal of level 1 node\n        var current = node;\n\n        while (current && current.depth > 1) {\n          current = current.parentNode;\n        }\n\n        var decal = getDecalFromPalette(seriesModel.ecModel, current.name || current.dataIndex + '', decalPaletteScope);\n        node.setVisual('decal', decal);\n      });\n    }\n\n    var TreemapSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(TreemapSeriesModel, _super);\n\n      function TreemapSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TreemapSeriesModel.type;\n        _this.preventUsingHoverLayer = true;\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      TreemapSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        // Create a virtual root.\n        var root = {\n          name: option.name,\n          children: option.data\n        };\n        completeTreeValue(root);\n        var levels = option.levels || []; // Used in \"visual priority\" in `treemapVisual.js`.\n        // This way is a little tricky, must satisfy the precondition:\n        //   1. There is no `treeNode.getModel('itemStyle.xxx')` used.\n        //   2. The `Model.prototype.getModel()` will not use any clone-like way.\n\n        var designatedVisualItemStyle = this.designatedVisualItemStyle = {};\n        var designatedVisualModel = new Model({\n          itemStyle: designatedVisualItemStyle\n        }, this, ecModel);\n        levels = option.levels = setDefault(levels, ecModel);\n        var levelModels = map(levels || [], function (levelDefine) {\n          return new Model(levelDefine, designatedVisualModel, ecModel);\n        }, this); // Make sure always a new tree is created when setOption,\n        // in TreemapView, we check whether oldTree === newTree\n        // to choose mappings approach among old shapes and new shapes.\n\n        var tree = Tree.createTree(root, this, beforeLink);\n\n        function beforeLink(nodeData) {\n          nodeData.wrapMethod('getItemModel', function (model, idx) {\n            var node = tree.getNodeByDataIndex(idx);\n            var levelModel = node ? levelModels[node.depth] : null; // If no levelModel, we also need `designatedVisualModel`.\n\n            model.parentModel = levelModel || designatedVisualModel;\n            return model;\n          });\n        }\n\n        return tree.data;\n      };\n\n      TreemapSeriesModel.prototype.optionUpdated = function () {\n        this.resetViewRoot();\n      };\n      /**\n       * @override\n       * @param {number} dataIndex\n       * @param {boolean} [mutipleSeries=false]\n       */\n\n\n      TreemapSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        var data = this.getData();\n        var value = this.getRawValue(dataIndex);\n        var name = data.getName(dataIndex);\n        return createTooltipMarkup('nameValue', {\n          name: name,\n          value: value\n        });\n      };\n      /**\n       * Add tree path to tooltip param\n       *\n       * @override\n       * @param {number} dataIndex\n       * @return {Object}\n       */\n\n\n      TreemapSeriesModel.prototype.getDataParams = function (dataIndex) {\n        var params = _super.prototype.getDataParams.apply(this, arguments);\n\n        var node = this.getData().tree.getNodeByDataIndex(dataIndex);\n        params.treeAncestors = wrapTreePathInfo(node, this); // compatitable the previous code.\n\n        params.treePathInfo = params.treeAncestors;\n        return params;\n      };\n      /**\n       * @public\n       * @param {Object} layoutInfo {\n       *                                x: containerGroup x\n       *                                y: containerGroup y\n       *                                width: containerGroup width\n       *                                height: containerGroup height\n       *                            }\n       */\n\n\n      TreemapSeriesModel.prototype.setLayoutInfo = function (layoutInfo) {\n        /**\n         * @readOnly\n         * @type {Object}\n         */\n        this.layoutInfo = this.layoutInfo || {};\n        extend(this.layoutInfo, layoutInfo);\n      };\n      /**\n       * @param  {string} id\n       * @return {number} index\n       */\n\n\n      TreemapSeriesModel.prototype.mapIdToIndex = function (id) {\n        // A feature is implemented:\n        // index is monotone increasing with the sequence of\n        // input id at the first time.\n        // This feature can make sure that each data item and its\n        // mapped color have the same index between data list and\n        // color list at the beginning, which is useful for user\n        // to adjust data-color mapping.\n\n        /**\n         * @private\n         * @type {Object}\n         */\n        var idIndexMap = this._idIndexMap;\n\n        if (!idIndexMap) {\n          idIndexMap = this._idIndexMap = createHashMap();\n          /**\n           * @private\n           * @type {number}\n           */\n\n          this._idIndexMapCount = 0;\n        }\n\n        var index = idIndexMap.get(id);\n\n        if (index == null) {\n          idIndexMap.set(id, index = this._idIndexMapCount++);\n        }\n\n        return index;\n      };\n\n      TreemapSeriesModel.prototype.getViewRoot = function () {\n        return this._viewRoot;\n      };\n\n      TreemapSeriesModel.prototype.resetViewRoot = function (viewRoot) {\n        viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot;\n        var root = this.getRawData().tree.root;\n\n        if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) {\n          this._viewRoot = root;\n        }\n      };\n\n      TreemapSeriesModel.prototype.enableAriaDecal = function () {\n        enableAriaDecalForTree(this);\n      };\n\n      TreemapSeriesModel.type = 'series.treemap';\n      TreemapSeriesModel.layoutMode = 'box';\n      TreemapSeriesModel.defaultOption = {\n        // Disable progressive rendering\n        progressive: 0,\n        // size: ['80%', '80%'],            // deprecated, compatible with ec2.\n        left: 'center',\n        top: 'middle',\n        width: '80%',\n        height: '80%',\n        sort: true,\n        clipWindow: 'origin',\n        squareRatio: 0.5 * (1 + Math.sqrt(5)),\n        leafDepth: null,\n        drillDownIcon: '▶',\n        // to align specialized icon. ▷▶❒❐▼✚\n        zoomToNodeRatio: 0.32 * 0.32,\n        roam: true,\n        nodeClick: 'zoomToNode',\n        animation: true,\n        animationDurationUpdate: 900,\n        animationEasing: 'quinticInOut',\n        breadcrumb: {\n          show: true,\n          height: 22,\n          left: 'center',\n          top: 'bottom',\n          // right\n          // bottom\n          emptyItemWidth: 25,\n          itemStyle: {\n            color: 'rgba(0,0,0,0.7)',\n            textStyle: {\n              color: '#fff'\n            }\n          },\n          emphasis: {\n            itemStyle: {\n              color: 'rgba(0,0,0,0.9)' // '#5793f3',\n\n            }\n          }\n        },\n        label: {\n          show: true,\n          // Do not use textDistance, for ellipsis rect just the same as treemap node rect.\n          distance: 0,\n          padding: 5,\n          position: 'inside',\n          // formatter: null,\n          color: '#fff',\n          overflow: 'truncate' // align\n          // verticalAlign\n\n        },\n        upperLabel: {\n          show: false,\n          position: [0, '50%'],\n          height: 20,\n          // formatter: null,\n          // color: '#fff',\n          overflow: 'truncate',\n          // align: null,\n          verticalAlign: 'middle'\n        },\n        itemStyle: {\n          color: null,\n          colorAlpha: null,\n          colorSaturation: null,\n          borderWidth: 0,\n          gapWidth: 0,\n          borderColor: '#fff',\n          borderColorSaturation: null // If specified, borderColor will be ineffective, and the\n          // border color is evaluated by color of current node and\n          // borderColorSaturation.\n\n        },\n        emphasis: {\n          upperLabel: {\n            show: true,\n            position: [0, '50%'],\n            overflow: 'truncate',\n            verticalAlign: 'middle'\n          }\n        },\n        visualDimension: 0,\n        visualMin: null,\n        visualMax: null,\n        color: [],\n        // level[n].color (if necessary).\n        // + Specify color list of each level. level[0].color would be global\n        // color list if not specified. (see method `setDefault`).\n        // + But set as a empty array to forbid fetch color from global palette\n        // when using nodeModel.get('color'), otherwise nodes on deep level\n        // will always has color palette set and are not able to inherit color\n        // from parent node.\n        // + TreemapSeries.color can not be set as 'none', otherwise effect\n        // legend color fetching (see seriesColor.js).\n        colorAlpha: null,\n        colorSaturation: null,\n        colorMappingBy: 'index',\n        visibleMin: 10,\n        // be rendered. Only works when sort is 'asc' or 'desc'.\n        childrenVisibleMin: null,\n        // grandchildren will not show.\n        // Why grandchildren? If not grandchildren but children,\n        // some siblings show children and some not,\n        // the appearance may be mess and not consistent,\n        levels: [] // Each item: {\n        //     visibleMin, itemStyle, visualDimension, label\n        // }\n\n      };\n      return TreemapSeriesModel;\n    }(SeriesModel);\n    /**\n     * @param {Object} dataNode\n     */\n\n\n    function completeTreeValue(dataNode) {\n      // Postorder travel tree.\n      // If value of none-leaf node is not set,\n      // calculate it by suming up the value of all children.\n      var sum = 0;\n      each(dataNode.children, function (child) {\n        completeTreeValue(child);\n        var childValue = child.value;\n        isArray(childValue) && (childValue = childValue[0]);\n        sum += childValue;\n      });\n      var thisValue = dataNode.value;\n\n      if (isArray(thisValue)) {\n        thisValue = thisValue[0];\n      }\n\n      if (thisValue == null || isNaN(thisValue)) {\n        thisValue = sum;\n      } // Value should not less than 0.\n\n\n      if (thisValue < 0) {\n        thisValue = 0;\n      }\n\n      isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue;\n    }\n    /**\n     * set default to level configuration\n     */\n\n\n    function setDefault(levels, ecModel) {\n      var globalColorList = normalizeToArray(ecModel.get('color'));\n      var globalDecalList = normalizeToArray(ecModel.get(['aria', 'decal', 'decals']));\n\n      if (!globalColorList) {\n        return;\n      }\n\n      levels = levels || [];\n      var hasColorDefine;\n      var hasDecalDefine;\n      each(levels, function (levelDefine) {\n        var model = new Model(levelDefine);\n        var modelColor = model.get('color');\n        var modelDecal = model.get('decal');\n\n        if (model.get(['itemStyle', 'color']) || modelColor && modelColor !== 'none') {\n          hasColorDefine = true;\n        }\n\n        if (model.get(['itemStyle', 'decal']) || modelDecal && modelDecal !== 'none') {\n          hasDecalDefine = true;\n        }\n      });\n      var level0 = levels[0] || (levels[0] = {});\n\n      if (!hasColorDefine) {\n        level0.color = globalColorList.slice();\n      }\n\n      if (!hasDecalDefine && globalDecalList) {\n        level0.decal = globalDecalList.slice();\n      }\n\n      return levels;\n    }\n\n    var TEXT_PADDING = 8;\n    var ITEM_GAP = 8;\n    var ARRAY_LENGTH = 5;\n\n    var Breadcrumb =\n    /** @class */\n    function () {\n      function Breadcrumb(containerGroup) {\n        this.group = new Group();\n        containerGroup.add(this.group);\n      }\n\n      Breadcrumb.prototype.render = function (seriesModel, api, targetNode, onSelect) {\n        var model = seriesModel.getModel('breadcrumb');\n        var thisGroup = this.group;\n        thisGroup.removeAll();\n\n        if (!model.get('show') || !targetNode) {\n          return;\n        }\n\n        var normalStyleModel = model.getModel('itemStyle');\n        var emphasisModel = model.getModel('emphasis');\n        var textStyleModel = normalStyleModel.getModel('textStyle');\n        var emphasisTextStyleModel = emphasisModel.getModel(['itemStyle', 'textStyle']);\n        var layoutParam = {\n          pos: {\n            left: model.get('left'),\n            right: model.get('right'),\n            top: model.get('top'),\n            bottom: model.get('bottom')\n          },\n          box: {\n            width: api.getWidth(),\n            height: api.getHeight()\n          },\n          emptyItemWidth: model.get('emptyItemWidth'),\n          totalWidth: 0,\n          renderList: []\n        };\n\n        this._prepare(targetNode, layoutParam, textStyleModel);\n\n        this._renderContent(seriesModel, layoutParam, normalStyleModel, emphasisModel, textStyleModel, emphasisTextStyleModel, onSelect);\n\n        positionElement(thisGroup, layoutParam.pos, layoutParam.box);\n      };\n      /**\n       * Prepare render list and total width\n       * @private\n       */\n\n\n      Breadcrumb.prototype._prepare = function (targetNode, layoutParam, textStyleModel) {\n        for (var node = targetNode; node; node = node.parentNode) {\n          var text = convertOptionIdName(node.getModel().get('name'), '');\n          var textRect = textStyleModel.getTextRect(text);\n          var itemWidth = Math.max(textRect.width + TEXT_PADDING * 2, layoutParam.emptyItemWidth);\n          layoutParam.totalWidth += itemWidth + ITEM_GAP;\n          layoutParam.renderList.push({\n            node: node,\n            text: text,\n            width: itemWidth\n          });\n        }\n      };\n      /**\n       * @private\n       */\n\n\n      Breadcrumb.prototype._renderContent = function (seriesModel, layoutParam, normalStyleModel, emphasisModel, textStyleModel, emphasisTextStyleModel, onSelect) {\n        // Start rendering.\n        var lastX = 0;\n        var emptyItemWidth = layoutParam.emptyItemWidth;\n        var height = seriesModel.get(['breadcrumb', 'height']);\n        var availableSize = getAvailableSize(layoutParam.pos, layoutParam.box);\n        var totalWidth = layoutParam.totalWidth;\n        var renderList = layoutParam.renderList;\n        var emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n\n        for (var i = renderList.length - 1; i >= 0; i--) {\n          var item = renderList[i];\n          var itemNode = item.node;\n          var itemWidth = item.width;\n          var text = item.text; // Hdie text and shorten width if necessary.\n\n          if (totalWidth > availableSize.width) {\n            totalWidth -= itemWidth - emptyItemWidth;\n            itemWidth = emptyItemWidth;\n            text = null;\n          }\n\n          var el = new Polygon({\n            shape: {\n              points: makeItemPoints(lastX, 0, itemWidth, height, i === renderList.length - 1, i === 0)\n            },\n            style: defaults(normalStyleModel.getItemStyle(), {\n              lineJoin: 'bevel'\n            }),\n            textContent: new ZRText({\n              style: createTextStyle(textStyleModel, {\n                text: text\n              })\n            }),\n            textConfig: {\n              position: 'inside'\n            },\n            z2: Z2_EMPHASIS_LIFT * 1e4,\n            onclick: curry(onSelect, itemNode)\n          });\n          el.disableLabelAnimation = true;\n          el.getTextContent().ensureState('emphasis').style = createTextStyle(emphasisTextStyleModel, {\n            text: text\n          });\n          el.ensureState('emphasis').style = emphasisItemStyle;\n          toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n          this.group.add(el);\n          packEventData(el, seriesModel, itemNode);\n          lastX += itemWidth + ITEM_GAP;\n        }\n      };\n\n      Breadcrumb.prototype.remove = function () {\n        this.group.removeAll();\n      };\n\n      return Breadcrumb;\n    }();\n\n    function makeItemPoints(x, y, itemWidth, itemHeight, head, tail) {\n      var points = [[head ? x : x - ARRAY_LENGTH, y], [x + itemWidth, y], [x + itemWidth, y + itemHeight], [head ? x : x - ARRAY_LENGTH, y + itemHeight]];\n      !tail && points.splice(2, 0, [x + itemWidth + ARRAY_LENGTH, y + itemHeight / 2]);\n      !head && points.push([x, y + itemHeight / 2]);\n      return points;\n    } // Package custom mouse event.\n\n\n    function packEventData(el, seriesModel, itemNode) {\n      getECData(el).eventData = {\n        componentType: 'series',\n        componentSubType: 'treemap',\n        componentIndex: seriesModel.componentIndex,\n        seriesIndex: seriesModel.seriesIndex,\n        seriesName: seriesModel.name,\n        seriesType: 'treemap',\n        selfType: 'breadcrumb',\n        nodeData: {\n          dataIndex: itemNode && itemNode.dataIndex,\n          name: itemNode && itemNode.name\n        },\n        treePathInfo: itemNode && wrapTreePathInfo(itemNode, seriesModel)\n      };\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * Animate multiple elements with a single done-callback.\n     *\n     * @example\n     *  animation\n     *      .createWrap()\n     *      .add(el1, {x: 10, y: 10})\n     *      .add(el2, {shape: {width: 500}, style: {fill: 'red'}}, 400)\n     *      .done(function () { // done })\n     *      .start('cubicOut');\n     */\n    var AnimationWrap =\n    /** @class */\n    function () {\n      function AnimationWrap() {\n        this._storage = [];\n        this._elExistsMap = {};\n      }\n      /**\n       * Caution: a el can only be added once, otherwise 'done'\n       * might not be called. This method checks this (by el.id),\n       * suppresses adding and returns false when existing el found.\n       *\n       * @return Whether adding succeeded.\n       */\n\n\n      AnimationWrap.prototype.add = function (el, target, duration, delay, easing) {\n        if (this._elExistsMap[el.id]) {\n          return false;\n        }\n\n        this._elExistsMap[el.id] = true;\n\n        this._storage.push({\n          el: el,\n          target: target,\n          duration: duration,\n          delay: delay,\n          easing: easing\n        });\n\n        return true;\n      };\n      /**\n       * Only execute when animation done/aborted.\n       */\n\n\n      AnimationWrap.prototype.finished = function (callback) {\n        this._finishedCallback = callback;\n        return this;\n      };\n      /**\n       * Will stop exist animation firstly.\n       */\n\n\n      AnimationWrap.prototype.start = function () {\n        var _this = this;\n\n        var count = this._storage.length;\n\n        var checkTerminate = function () {\n          count--;\n\n          if (count <= 0) {\n            // Guard.\n            _this._storage.length = 0;\n            _this._elExistsMap = {};\n            _this._finishedCallback && _this._finishedCallback();\n          }\n        };\n\n        for (var i = 0, len = this._storage.length; i < len; i++) {\n          var item = this._storage[i];\n          item.el.animateTo(item.target, {\n            duration: item.duration,\n            delay: item.delay,\n            easing: item.easing,\n            setToFinal: true,\n            done: checkTerminate,\n            aborted: checkTerminate\n          });\n        }\n\n        return this;\n      };\n\n      return AnimationWrap;\n    }();\n\n    function createWrap() {\n      return new AnimationWrap();\n    }\n\n    var Group$1 = Group;\n    var Rect$1 = Rect;\n    var DRAG_THRESHOLD = 3;\n    var PATH_LABEL_NOAMAL = 'label';\n    var PATH_UPPERLABEL_NORMAL = 'upperLabel'; // Should larger than emphasis states lift z\n\n    var Z2_BASE = Z2_EMPHASIS_LIFT * 10; // Should bigger than every z2.\n\n    var Z2_BG = Z2_EMPHASIS_LIFT * 2;\n    var Z2_CONTENT = Z2_EMPHASIS_LIFT * 3;\n    var getStateItemStyle = makeStyleMapper([['fill', 'color'], // `borderColor` and `borderWidth` has been occupied,\n    // so use `stroke` to indicate the stroke of the rect.\n    ['stroke', 'strokeColor'], ['lineWidth', 'strokeWidth'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ]);\n\n    var getItemStyleNormal = function (model) {\n      // Normal style props should include emphasis style props.\n      var itemStyle = getStateItemStyle(model); // Clear styles set by emphasis.\n\n      itemStyle.stroke = itemStyle.fill = itemStyle.lineWidth = null;\n      return itemStyle;\n    };\n\n    var inner$8 = makeInner();\n\n    var TreemapView =\n    /** @class */\n    function (_super) {\n      __extends(TreemapView, _super);\n\n      function TreemapView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TreemapView.type;\n        _this._state = 'ready';\n        _this._storage = createStorage();\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      TreemapView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        var models = ecModel.findComponents({\n          mainType: 'series',\n          subType: 'treemap',\n          query: payload\n        });\n\n        if (indexOf(models, seriesModel) < 0) {\n          return;\n        }\n\n        this.seriesModel = seriesModel;\n        this.api = api;\n        this.ecModel = ecModel;\n        var types = ['treemapZoomToNode', 'treemapRootToNode'];\n        var targetInfo = retrieveTargetInfo(payload, types, seriesModel);\n        var payloadType = payload && payload.type;\n        var layoutInfo = seriesModel.layoutInfo;\n        var isInit = !this._oldTree;\n        var thisStorage = this._storage; // Mark new root when action is treemapRootToNode.\n\n        var reRoot = payloadType === 'treemapRootToNode' && targetInfo && thisStorage ? {\n          rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()],\n          direction: payload.direction\n        } : null;\n\n        var containerGroup = this._giveContainerGroup(layoutInfo);\n\n        var hasAnimation = seriesModel.get('animation');\n\n        var renderResult = this._doRender(containerGroup, seriesModel, reRoot);\n\n        hasAnimation && !isInit && (!payloadType || payloadType === 'treemapZoomToNode' || payloadType === 'treemapRootToNode') ? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot) : renderResult.renderFinally();\n\n        this._resetController(api);\n\n        this._renderBreadcrumb(seriesModel, api, targetInfo);\n      };\n\n      TreemapView.prototype._giveContainerGroup = function (layoutInfo) {\n        var containerGroup = this._containerGroup;\n\n        if (!containerGroup) {\n          // FIXME\n          // 加一层containerGroup是为了clip，但是现在clip功能并没有实现。\n          containerGroup = this._containerGroup = new Group$1();\n\n          this._initEvents(containerGroup);\n\n          this.group.add(containerGroup);\n        }\n\n        containerGroup.x = layoutInfo.x;\n        containerGroup.y = layoutInfo.y;\n        return containerGroup;\n      };\n\n      TreemapView.prototype._doRender = function (containerGroup, seriesModel, reRoot) {\n        var thisTree = seriesModel.getData().tree;\n        var oldTree = this._oldTree; // Clear last shape records.\n\n        var lastsForAnimation = createStorage();\n        var thisStorage = createStorage();\n        var oldStorage = this._storage;\n        var willInvisibleEls = [];\n\n        function doRenderNode(thisNode, oldNode, parentGroup, depth) {\n          return renderNode(seriesModel, thisStorage, oldStorage, reRoot, lastsForAnimation, willInvisibleEls, thisNode, oldNode, parentGroup, depth);\n        } // Notice: When thisTree and oldTree are the same tree (see list.cloneShallow),\n        // the oldTree is actually losted, so we cannot find all of the old graphic\n        // elements from tree. So we use this strategy: make element storage, move\n        // from old storage to new storage, clear old storage.\n\n\n        dualTravel(thisTree.root ? [thisTree.root] : [], oldTree && oldTree.root ? [oldTree.root] : [], containerGroup, thisTree === oldTree || !oldTree, 0); // Process all removing.\n\n        var willDeleteEls = clearStorage(oldStorage);\n        this._oldTree = thisTree;\n        this._storage = thisStorage;\n        return {\n          lastsForAnimation: lastsForAnimation,\n          willDeleteEls: willDeleteEls,\n          renderFinally: renderFinally\n        };\n\n        function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) {\n          // When 'render' is triggered by action,\n          // 'this' and 'old' may be the same tree,\n          // we use rawIndex in that case.\n          if (sameTree) {\n            oldViewChildren = thisViewChildren;\n            each(thisViewChildren, function (child, index) {\n              !child.isRemoved() && processNode(index, index);\n            });\n          } // Diff hierarchically (diff only in each subtree, but not whole).\n          // because, consistency of view is important.\n          else {\n              new DataDiffer(oldViewChildren, thisViewChildren, getKey, getKey).add(processNode).update(processNode).remove(curry(processNode, null)).execute();\n            }\n\n          function getKey(node) {\n            // Identify by name or raw index.\n            return node.getId();\n          }\n\n          function processNode(newIndex, oldIndex) {\n            var thisNode = newIndex != null ? thisViewChildren[newIndex] : null;\n            var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null;\n            var group = doRenderNode(thisNode, oldNode, parentGroup, depth);\n            group && dualTravel(thisNode && thisNode.viewChildren || [], oldNode && oldNode.viewChildren || [], group, sameTree, depth + 1);\n          }\n        }\n\n        function clearStorage(storage) {\n          var willDeleteEls = createStorage();\n          storage && each(storage, function (store, storageName) {\n            var delEls = willDeleteEls[storageName];\n            each(store, function (el) {\n              el && (delEls.push(el), inner$8(el).willDelete = true);\n            });\n          });\n          return willDeleteEls;\n        }\n\n        function renderFinally() {\n          each(willDeleteEls, function (els) {\n            each(els, function (el) {\n              el.parent && el.parent.remove(el);\n            });\n          });\n          each(willInvisibleEls, function (el) {\n            el.invisible = true; // Setting invisible is for optimizing, so no need to set dirty,\n            // just mark as invisible.\n\n            el.dirty();\n          });\n        }\n      };\n\n      TreemapView.prototype._doAnimation = function (containerGroup, renderResult, seriesModel, reRoot) {\n        var durationOption = seriesModel.get('animationDurationUpdate');\n        var easingOption = seriesModel.get('animationEasing'); // TODO: do not support function until necessary.\n\n        var duration = (isFunction(durationOption) ? 0 : durationOption) || 0;\n        var easing = (isFunction(easingOption) ? null : easingOption) || 'cubicOut';\n        var animationWrap = createWrap(); // Make delete animations.\n\n        each(renderResult.willDeleteEls, function (store, storageName) {\n          each(store, function (el, rawIndex) {\n            if (el.invisible) {\n              return;\n            }\n\n            var parent = el.parent; // Always has parent, and parent is nodeGroup.\n\n            var target;\n            var innerStore = inner$8(parent);\n\n            if (reRoot && reRoot.direction === 'drillDown') {\n              target = parent === reRoot.rootNodeGroup // This is the content element of view root.\n              // Only `content` will enter this branch, because\n              // `background` and `nodeGroup` will not be deleted.\n              ? {\n                shape: {\n                  x: 0,\n                  y: 0,\n                  width: innerStore.nodeWidth,\n                  height: innerStore.nodeHeight\n                },\n                style: {\n                  opacity: 0\n                }\n              } // Others.\n              : {\n                style: {\n                  opacity: 0\n                }\n              };\n            } else {\n              var targetX = 0;\n              var targetY = 0;\n\n              if (!innerStore.willDelete) {\n                // Let node animate to right-bottom corner, cooperating with fadeout,\n                // which is appropriate for user understanding.\n                // Divided by 2 for reRoot rolling up effect.\n                targetX = innerStore.nodeWidth / 2;\n                targetY = innerStore.nodeHeight / 2;\n              }\n\n              target = storageName === 'nodeGroup' ? {\n                x: targetX,\n                y: targetY,\n                style: {\n                  opacity: 0\n                }\n              } : {\n                shape: {\n                  x: targetX,\n                  y: targetY,\n                  width: 0,\n                  height: 0\n                },\n                style: {\n                  opacity: 0\n                }\n              };\n            } // TODO: do not support delay until necessary.\n\n\n            target && animationWrap.add(el, target, duration, 0, easing);\n          });\n        }); // Make other animations\n\n        each(this._storage, function (store, storageName) {\n          each(store, function (el, rawIndex) {\n            var last = renderResult.lastsForAnimation[storageName][rawIndex];\n            var target = {};\n\n            if (!last) {\n              return;\n            }\n\n            if (el instanceof Group) {\n              if (last.oldX != null) {\n                target.x = el.x;\n                target.y = el.y;\n                el.x = last.oldX;\n                el.y = last.oldY;\n              }\n            } else {\n              if (last.oldShape) {\n                target.shape = extend({}, el.shape);\n                el.setShape(last.oldShape);\n              }\n\n              if (last.fadein) {\n                el.setStyle('opacity', 0);\n                target.style = {\n                  opacity: 1\n                };\n              } // When animation is stopped for succedent animation starting,\n              // el.style.opacity might not be 1\n              else if (el.style.opacity !== 1) {\n                  target.style = {\n                    opacity: 1\n                  };\n                }\n            }\n\n            animationWrap.add(el, target, duration, 0, easing);\n          });\n        }, this);\n        this._state = 'animating';\n        animationWrap.finished(bind(function () {\n          this._state = 'ready';\n          renderResult.renderFinally();\n        }, this)).start();\n      };\n\n      TreemapView.prototype._resetController = function (api) {\n        var controller = this._controller; // Init controller.\n\n        if (!controller) {\n          controller = this._controller = new RoamController(api.getZr());\n          controller.enable(this.seriesModel.get('roam'));\n          controller.on('pan', bind(this._onPan, this));\n          controller.on('zoom', bind(this._onZoom, this));\n        }\n\n        var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight());\n        controller.setPointerChecker(function (e, x, y) {\n          return rect.contain(x, y);\n        });\n      };\n\n      TreemapView.prototype._clearController = function () {\n        var controller = this._controller;\n\n        if (controller) {\n          controller.dispose();\n          controller = null;\n        }\n      };\n\n      TreemapView.prototype._onPan = function (e) {\n        if (this._state !== 'animating' && (Math.abs(e.dx) > DRAG_THRESHOLD || Math.abs(e.dy) > DRAG_THRESHOLD)) {\n          // These param must not be cached.\n          var root = this.seriesModel.getData().tree.root;\n\n          if (!root) {\n            return;\n          }\n\n          var rootLayout = root.getLayout();\n\n          if (!rootLayout) {\n            return;\n          }\n\n          this.api.dispatchAction({\n            type: 'treemapMove',\n            from: this.uid,\n            seriesId: this.seriesModel.id,\n            rootRect: {\n              x: rootLayout.x + e.dx,\n              y: rootLayout.y + e.dy,\n              width: rootLayout.width,\n              height: rootLayout.height\n            }\n          });\n        }\n      };\n\n      TreemapView.prototype._onZoom = function (e) {\n        var mouseX = e.originX;\n        var mouseY = e.originY;\n\n        if (this._state !== 'animating') {\n          // These param must not be cached.\n          var root = this.seriesModel.getData().tree.root;\n\n          if (!root) {\n            return;\n          }\n\n          var rootLayout = root.getLayout();\n\n          if (!rootLayout) {\n            return;\n          }\n\n          var rect = new BoundingRect(rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height);\n          var layoutInfo = this.seriesModel.layoutInfo; // Transform mouse coord from global to containerGroup.\n\n          mouseX -= layoutInfo.x;\n          mouseY -= layoutInfo.y; // Scale root bounding rect.\n\n          var m = create$1();\n          translate(m, m, [-mouseX, -mouseY]);\n          scale$1(m, m, [e.scale, e.scale]);\n          translate(m, m, [mouseX, mouseY]);\n          rect.applyTransform(m);\n          this.api.dispatchAction({\n            type: 'treemapRender',\n            from: this.uid,\n            seriesId: this.seriesModel.id,\n            rootRect: {\n              x: rect.x,\n              y: rect.y,\n              width: rect.width,\n              height: rect.height\n            }\n          });\n        }\n      };\n\n      TreemapView.prototype._initEvents = function (containerGroup) {\n        var _this = this;\n\n        containerGroup.on('click', function (e) {\n          if (_this._state !== 'ready') {\n            return;\n          }\n\n          var nodeClick = _this.seriesModel.get('nodeClick', true);\n\n          if (!nodeClick) {\n            return;\n          }\n\n          var targetInfo = _this.findTarget(e.offsetX, e.offsetY);\n\n          if (!targetInfo) {\n            return;\n          }\n\n          var node = targetInfo.node;\n\n          if (node.getLayout().isLeafRoot) {\n            _this._rootToNode(targetInfo);\n          } else {\n            if (nodeClick === 'zoomToNode') {\n              _this._zoomToNode(targetInfo);\n            } else if (nodeClick === 'link') {\n              var itemModel = node.hostTree.data.getItemModel(node.dataIndex);\n              var link = itemModel.get('link', true);\n              var linkTarget = itemModel.get('target', true) || 'blank';\n              link && windowOpen(link, linkTarget);\n            }\n          }\n        }, this);\n      };\n\n      TreemapView.prototype._renderBreadcrumb = function (seriesModel, api, targetInfo) {\n        var _this = this;\n\n        if (!targetInfo) {\n          targetInfo = seriesModel.get('leafDepth', true) != null ? {\n            node: seriesModel.getViewRoot()\n          } // FIXME\n          // better way?\n          // Find breadcrumb tail on center of containerGroup.\n          : this.findTarget(api.getWidth() / 2, api.getHeight() / 2);\n\n          if (!targetInfo) {\n            targetInfo = {\n              node: seriesModel.getData().tree.root\n            };\n          }\n        }\n\n        (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group))).render(seriesModel, api, targetInfo.node, function (node) {\n          if (_this._state !== 'animating') {\n            aboveViewRoot(seriesModel.getViewRoot(), node) ? _this._rootToNode({\n              node: node\n            }) : _this._zoomToNode({\n              node: node\n            });\n          }\n        });\n      };\n      /**\n       * @override\n       */\n\n\n      TreemapView.prototype.remove = function () {\n        this._clearController();\n\n        this._containerGroup && this._containerGroup.removeAll();\n        this._storage = createStorage();\n        this._state = 'ready';\n        this._breadcrumb && this._breadcrumb.remove();\n      };\n\n      TreemapView.prototype.dispose = function () {\n        this._clearController();\n      };\n\n      TreemapView.prototype._zoomToNode = function (targetInfo) {\n        this.api.dispatchAction({\n          type: 'treemapZoomToNode',\n          from: this.uid,\n          seriesId: this.seriesModel.id,\n          targetNode: targetInfo.node\n        });\n      };\n\n      TreemapView.prototype._rootToNode = function (targetInfo) {\n        this.api.dispatchAction({\n          type: 'treemapRootToNode',\n          from: this.uid,\n          seriesId: this.seriesModel.id,\n          targetNode: targetInfo.node\n        });\n      };\n      /**\n       * @public\n       * @param {number} x Global coord x.\n       * @param {number} y Global coord y.\n       * @return {Object} info If not found, return undefined;\n       * @return {number} info.node Target node.\n       * @return {number} info.offsetX x refer to target node.\n       * @return {number} info.offsetY y refer to target node.\n       */\n\n\n      TreemapView.prototype.findTarget = function (x, y) {\n        var targetInfo;\n        var viewRoot = this.seriesModel.getViewRoot();\n        viewRoot.eachNode({\n          attr: 'viewChildren',\n          order: 'preorder'\n        }, function (node) {\n          var bgEl = this._storage.background[node.getRawIndex()]; // If invisible, there might be no element.\n\n\n          if (bgEl) {\n            var point = bgEl.transformCoordToLocal(x, y);\n            var shape = bgEl.shape; // For performance consideration, don't use 'getBoundingRect'.\n\n            if (shape.x <= point[0] && point[0] <= shape.x + shape.width && shape.y <= point[1] && point[1] <= shape.y + shape.height) {\n              targetInfo = {\n                node: node,\n                offsetX: point[0],\n                offsetY: point[1]\n              };\n            } else {\n              return false; // Suppress visit subtree.\n            }\n          }\n        }, this);\n        return targetInfo;\n      };\n\n      TreemapView.type = 'treemap';\n      return TreemapView;\n    }(ChartView);\n    /**\n     * @inner\n     */\n\n\n    function createStorage() {\n      return {\n        nodeGroup: [],\n        background: [],\n        content: []\n      };\n    }\n    /**\n     * @inner\n     * @return Return undefined means do not travel further.\n     */\n\n\n    function renderNode(seriesModel, thisStorage, oldStorage, reRoot, lastsForAnimation, willInvisibleEls, thisNode, oldNode, parentGroup, depth) {\n      // Whether under viewRoot.\n      if (!thisNode) {\n        // Deleting nodes will be performed finally. This method just find\n        // element from old storage, or create new element, set them to new\n        // storage, and set styles.\n        return;\n      } // -------------------------------------------------------------------\n      // Start of closure variables available in \"Procedures in renderNode\".\n\n\n      var thisLayout = thisNode.getLayout();\n      var data = seriesModel.getData();\n      var nodeModel = thisNode.getModel(); // Only for enabling highlight/downplay. Clear firstly.\n      // Because some node will not be rendered.\n\n      data.setItemGraphicEl(thisNode.dataIndex, null);\n\n      if (!thisLayout || !thisLayout.isInView) {\n        return;\n      }\n\n      var thisWidth = thisLayout.width;\n      var thisHeight = thisLayout.height;\n      var borderWidth = thisLayout.borderWidth;\n      var thisInvisible = thisLayout.invisible;\n      var thisRawIndex = thisNode.getRawIndex();\n      var oldRawIndex = oldNode && oldNode.getRawIndex();\n      var thisViewChildren = thisNode.viewChildren;\n      var upperHeight = thisLayout.upperHeight;\n      var isParent = thisViewChildren && thisViewChildren.length;\n      var itemStyleNormalModel = nodeModel.getModel('itemStyle');\n      var itemStyleEmphasisModel = nodeModel.getModel(['emphasis', 'itemStyle']);\n      var itemStyleBlurModel = nodeModel.getModel(['blur', 'itemStyle']);\n      var itemStyleSelectModel = nodeModel.getModel(['select', 'itemStyle']);\n      var borderRadius = itemStyleNormalModel.get('borderRadius') || 0; // End of closure ariables available in \"Procedures in renderNode\".\n      // -----------------------------------------------------------------\n      // Node group\n\n      var group = giveGraphic('nodeGroup', Group$1);\n\n      if (!group) {\n        return;\n      }\n\n      parentGroup.add(group); // x,y are not set when el is above view root.\n\n      group.x = thisLayout.x || 0;\n      group.y = thisLayout.y || 0;\n      group.markRedraw();\n      inner$8(group).nodeWidth = thisWidth;\n      inner$8(group).nodeHeight = thisHeight;\n\n      if (thisLayout.isAboveViewRoot) {\n        return group;\n      } // Background\n\n\n      var bg = giveGraphic('background', Rect$1, depth, Z2_BG);\n      bg && renderBackground(group, bg, isParent && thisLayout.upperLabelHeight);\n      var emphasisModel = nodeModel.getModel('emphasis');\n      var focus = emphasisModel.get('focus');\n      var blurScope = emphasisModel.get('blurScope');\n      var isDisabled = emphasisModel.get('disabled');\n      var focusOrIndices = focus === 'ancestor' ? thisNode.getAncestorsIndices() : focus === 'descendant' ? thisNode.getDescendantIndices() : focus; // No children, render content.\n\n      if (isParent) {\n        // Because of the implementation about \"traverse\" in graphic hover style, we\n        // can not set hover listener on the \"group\" of non-leaf node. Otherwise the\n        // hover event from the descendents will be listenered.\n        if (isHighDownDispatcher(group)) {\n          setAsHighDownDispatcher(group, false);\n        }\n\n        if (bg) {\n          setAsHighDownDispatcher(bg, !isDisabled); // Only for enabling highlight/downplay.\n\n          data.setItemGraphicEl(thisNode.dataIndex, bg);\n          enableHoverFocus(bg, focusOrIndices, blurScope);\n        }\n      } else {\n        var content = giveGraphic('content', Rect$1, depth, Z2_CONTENT);\n        content && renderContent(group, content);\n        bg.disableMorphing = true;\n\n        if (bg && isHighDownDispatcher(bg)) {\n          setAsHighDownDispatcher(bg, false);\n        }\n\n        setAsHighDownDispatcher(group, !isDisabled); // Only for enabling highlight/downplay.\n\n        data.setItemGraphicEl(thisNode.dataIndex, group);\n        enableHoverFocus(group, focusOrIndices, blurScope);\n      }\n\n      return group; // ----------------------------\n      // | Procedures in renderNode |\n      // ----------------------------\n\n      function renderBackground(group, bg, useUpperLabel) {\n        var ecData = getECData(bg); // For tooltip.\n\n        ecData.dataIndex = thisNode.dataIndex;\n        ecData.seriesIndex = seriesModel.seriesIndex;\n        bg.setShape({\n          x: 0,\n          y: 0,\n          width: thisWidth,\n          height: thisHeight,\n          r: borderRadius\n        });\n\n        if (thisInvisible) {\n          // If invisible, do not set visual, otherwise the element will\n          // change immediately before animation. We think it is OK to\n          // remain its origin color when moving out of the view window.\n          processInvisible(bg);\n        } else {\n          bg.invisible = false;\n          var style = thisNode.getVisual('style');\n          var visualBorderColor = style.stroke;\n          var normalStyle = getItemStyleNormal(itemStyleNormalModel);\n          normalStyle.fill = visualBorderColor;\n          var emphasisStyle = getStateItemStyle(itemStyleEmphasisModel);\n          emphasisStyle.fill = itemStyleEmphasisModel.get('borderColor');\n          var blurStyle = getStateItemStyle(itemStyleBlurModel);\n          blurStyle.fill = itemStyleBlurModel.get('borderColor');\n          var selectStyle = getStateItemStyle(itemStyleSelectModel);\n          selectStyle.fill = itemStyleSelectModel.get('borderColor');\n\n          if (useUpperLabel) {\n            var upperLabelWidth = thisWidth - 2 * borderWidth;\n            prepareText( // PENDING: convert ZRColor to ColorString for text.\n            bg, visualBorderColor, style.opacity, {\n              x: borderWidth,\n              y: 0,\n              width: upperLabelWidth,\n              height: upperHeight\n            });\n          } // For old bg.\n          else {\n              bg.removeTextContent();\n            }\n\n          bg.setStyle(normalStyle);\n          bg.ensureState('emphasis').style = emphasisStyle;\n          bg.ensureState('blur').style = blurStyle;\n          bg.ensureState('select').style = selectStyle;\n          setDefaultStateProxy(bg);\n        }\n\n        group.add(bg);\n      }\n\n      function renderContent(group, content) {\n        var ecData = getECData(content); // For tooltip.\n\n        ecData.dataIndex = thisNode.dataIndex;\n        ecData.seriesIndex = seriesModel.seriesIndex;\n        var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0);\n        var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0);\n        content.culling = true;\n        content.setShape({\n          x: borderWidth,\n          y: borderWidth,\n          width: contentWidth,\n          height: contentHeight,\n          r: borderRadius\n        });\n\n        if (thisInvisible) {\n          // If invisible, do not set visual, otherwise the element will\n          // change immediately before animation. We think it is OK to\n          // remain its origin color when moving out of the view window.\n          processInvisible(content);\n        } else {\n          content.invisible = false;\n          var nodeStyle = thisNode.getVisual('style');\n          var visualColor = nodeStyle.fill;\n          var normalStyle = getItemStyleNormal(itemStyleNormalModel);\n          normalStyle.fill = visualColor;\n          normalStyle.decal = nodeStyle.decal;\n          var emphasisStyle = getStateItemStyle(itemStyleEmphasisModel);\n          var blurStyle = getStateItemStyle(itemStyleBlurModel);\n          var selectStyle = getStateItemStyle(itemStyleSelectModel); // PENDING: convert ZRColor to ColorString for text.\n\n          prepareText(content, visualColor, nodeStyle.opacity, null);\n          content.setStyle(normalStyle);\n          content.ensureState('emphasis').style = emphasisStyle;\n          content.ensureState('blur').style = blurStyle;\n          content.ensureState('select').style = selectStyle;\n          setDefaultStateProxy(content);\n        }\n\n        group.add(content);\n      }\n\n      function processInvisible(element) {\n        // Delay invisible setting utill animation finished,\n        // avoid element vanish suddenly before animation.\n        !element.invisible && willInvisibleEls.push(element);\n      }\n\n      function prepareText(rectEl, visualColor, visualOpacity, // Can be null/undefined\n      upperLabelRect) {\n        var normalLabelModel = nodeModel.getModel(upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL);\n        var defaultText = convertOptionIdName(nodeModel.get('name'), null);\n        var isShow = normalLabelModel.getShallow('show');\n        setLabelStyle(rectEl, getLabelStatesModels(nodeModel, upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL), {\n          defaultText: isShow ? defaultText : null,\n          inheritColor: visualColor,\n          defaultOpacity: visualOpacity,\n          labelFetcher: seriesModel,\n          labelDataIndex: thisNode.dataIndex\n        });\n        var textEl = rectEl.getTextContent();\n\n        if (!textEl) {\n          return;\n        }\n\n        var textStyle = textEl.style;\n        var textPadding = normalizeCssArray(textStyle.padding || 0);\n\n        if (upperLabelRect) {\n          rectEl.setTextConfig({\n            layoutRect: upperLabelRect\n          });\n          textEl.disableLabelLayout = true;\n        }\n\n        textEl.beforeUpdate = function () {\n          var width = Math.max((upperLabelRect ? upperLabelRect.width : rectEl.shape.width) - textPadding[1] - textPadding[3], 0);\n          var height = Math.max((upperLabelRect ? upperLabelRect.height : rectEl.shape.height) - textPadding[0] - textPadding[2], 0);\n\n          if (textStyle.width !== width || textStyle.height !== height) {\n            textEl.setStyle({\n              width: width,\n              height: height\n            });\n          }\n        };\n\n        textStyle.truncateMinChar = 2;\n        textStyle.lineOverflow = 'truncate';\n        addDrillDownIcon(textStyle, upperLabelRect, thisLayout);\n        var textEmphasisState = textEl.getState('emphasis');\n        addDrillDownIcon(textEmphasisState ? textEmphasisState.style : null, upperLabelRect, thisLayout);\n      }\n\n      function addDrillDownIcon(style, upperLabelRect, thisLayout) {\n        var text = style ? style.text : null;\n\n        if (!upperLabelRect && thisLayout.isLeafRoot && text != null) {\n          var iconChar = seriesModel.get('drillDownIcon', true);\n          style.text = iconChar ? iconChar + ' ' + text : text;\n        }\n      }\n\n      function giveGraphic(storageName, Ctor, depth, z) {\n        var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex];\n        var lasts = lastsForAnimation[storageName];\n\n        if (element) {\n          // Remove from oldStorage\n          oldStorage[storageName][oldRawIndex] = null;\n          prepareAnimationWhenHasOld(lasts, element);\n        } // If invisible and no old element, do not create new element (for optimizing).\n        else if (!thisInvisible) {\n            element = new Ctor();\n\n            if (element instanceof Displayable) {\n              element.z2 = calculateZ2(depth, z);\n            }\n\n            prepareAnimationWhenNoOld(lasts, element);\n          } // Set to thisStorage\n\n\n        return thisStorage[storageName][thisRawIndex] = element;\n      }\n\n      function prepareAnimationWhenHasOld(lasts, element) {\n        var lastCfg = lasts[thisRawIndex] = {};\n\n        if (element instanceof Group$1) {\n          lastCfg.oldX = element.x;\n          lastCfg.oldY = element.y;\n        } else {\n          lastCfg.oldShape = extend({}, element.shape);\n        }\n      } // If a element is new, we need to find the animation start point carefully,\n      // otherwise it will looks strange when 'zoomToNode'.\n\n\n      function prepareAnimationWhenNoOld(lasts, element) {\n        var lastCfg = lasts[thisRawIndex] = {};\n        var parentNode = thisNode.parentNode;\n        var isGroup = element instanceof Group;\n\n        if (parentNode && (!reRoot || reRoot.direction === 'drillDown')) {\n          var parentOldX = 0;\n          var parentOldY = 0; // New nodes appear from right-bottom corner in 'zoomToNode' animation.\n          // For convenience, get old bounding rect from background.\n\n          var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()];\n\n          if (!reRoot && parentOldBg && parentOldBg.oldShape) {\n            parentOldX = parentOldBg.oldShape.width;\n            parentOldY = parentOldBg.oldShape.height;\n          } // When no parent old shape found, its parent is new too,\n          // so we can just use {x:0, y:0}.\n\n\n          if (isGroup) {\n            lastCfg.oldX = 0;\n            lastCfg.oldY = parentOldY;\n          } else {\n            lastCfg.oldShape = {\n              x: parentOldX,\n              y: parentOldY,\n              width: 0,\n              height: 0\n            };\n          }\n        } // Fade in, user can be aware that these nodes are new.\n\n\n        lastCfg.fadein = !isGroup;\n      }\n    } // We cannot set all background with the same z, because the behaviour of\n    // drill down and roll up differ background creation sequence from tree\n    // hierarchy sequence, which cause lower background elements to overlap\n    // upper ones. So we calculate z based on depth.\n    // Moreover, we try to shrink down z interval to [0, 1] to avoid that\n    // treemap with large z overlaps other components.\n\n\n    function calculateZ2(depth, z2InLevel) {\n      return depth * Z2_BASE + z2InLevel;\n    }\n\n    var each$3 = each;\n    var isObject$3 = isObject;\n    var CATEGORY_DEFAULT_VISUAL_INDEX = -1;\n\n    var VisualMapping =\n    /** @class */\n    function () {\n      function VisualMapping(option) {\n        var mappingMethod = option.mappingMethod;\n        var visualType = option.type;\n        var thisOption = this.option = clone(option);\n        this.type = visualType;\n        this.mappingMethod = mappingMethod;\n        this._normalizeData = normalizers[mappingMethod];\n        var visualHandler = VisualMapping.visualHandlers[visualType];\n        this.applyVisual = visualHandler.applyVisual;\n        this.getColorMapper = visualHandler.getColorMapper;\n        this._normalizedToVisual = visualHandler._normalizedToVisual[mappingMethod];\n\n        if (mappingMethod === 'piecewise') {\n          normalizeVisualRange(thisOption);\n          preprocessForPiecewise(thisOption);\n        } else if (mappingMethod === 'category') {\n          thisOption.categories ? preprocessForSpecifiedCategory(thisOption) // categories is ordinal when thisOption.categories not specified,\n          // which need no more preprocess except normalize visual.\n          : normalizeVisualRange(thisOption, true);\n        } else {\n          // mappingMethod === 'linear' or 'fixed'\n          assert(mappingMethod !== 'linear' || thisOption.dataExtent);\n          normalizeVisualRange(thisOption);\n        }\n      }\n\n      VisualMapping.prototype.mapValueToVisual = function (value) {\n        var normalized = this._normalizeData(value);\n\n        return this._normalizedToVisual(normalized, value);\n      };\n\n      VisualMapping.prototype.getNormalizer = function () {\n        return bind(this._normalizeData, this);\n      };\n      /**\n       * List available visual types.\n       *\n       * @public\n       * @return {Array.<string>}\n       */\n\n\n      VisualMapping.listVisualTypes = function () {\n        return keys(VisualMapping.visualHandlers);\n      }; // /**\n      //  * @public\n      //  */\n      // static addVisualHandler(name, handler) {\n      //     visualHandlers[name] = handler;\n      // }\n\n      /**\n       * @public\n       */\n\n\n      VisualMapping.isValidType = function (visualType) {\n        return VisualMapping.visualHandlers.hasOwnProperty(visualType);\n      };\n      /**\n       * Convenient method.\n       * Visual can be Object or Array or primary type.\n       */\n\n\n      VisualMapping.eachVisual = function (visual, callback, context) {\n        if (isObject(visual)) {\n          each(visual, callback, context);\n        } else {\n          callback.call(context, visual);\n        }\n      };\n\n      VisualMapping.mapVisual = function (visual, callback, context) {\n        var isPrimary;\n        var newVisual = isArray(visual) ? [] : isObject(visual) ? {} : (isPrimary = true, null);\n        VisualMapping.eachVisual(visual, function (v, key) {\n          var newVal = callback.call(context, v, key);\n          isPrimary ? newVisual = newVal : newVisual[key] = newVal;\n        });\n        return newVisual;\n      };\n      /**\n       * Retrieve visual properties from given object.\n       */\n\n\n      VisualMapping.retrieveVisuals = function (obj) {\n        var ret = {};\n        var hasVisual;\n        obj && each$3(VisualMapping.visualHandlers, function (h, visualType) {\n          if (obj.hasOwnProperty(visualType)) {\n            ret[visualType] = obj[visualType];\n            hasVisual = true;\n          }\n        });\n        return hasVisual ? ret : null;\n      };\n      /**\n       * Give order to visual types, considering colorSaturation, colorAlpha depends on color.\n       *\n       * @public\n       * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...}\n       *                                     IF Array, like: ['color', 'symbol', 'colorSaturation']\n       * @return {Array.<string>} Sorted visual types.\n       */\n\n\n      VisualMapping.prepareVisualTypes = function (visualTypes) {\n        if (isArray(visualTypes)) {\n          visualTypes = visualTypes.slice();\n        } else if (isObject$3(visualTypes)) {\n          var types_1 = [];\n          each$3(visualTypes, function (item, type) {\n            types_1.push(type);\n          });\n          visualTypes = types_1;\n        } else {\n          return [];\n        }\n\n        visualTypes.sort(function (type1, type2) {\n          // color should be front of colorSaturation, colorAlpha, ...\n          // symbol and symbolSize do not matter.\n          return type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0 ? 1 : -1;\n        });\n        return visualTypes;\n      };\n      /**\n       * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'.\n       * Other visuals are only depends on themself.\n       */\n\n\n      VisualMapping.dependsOn = function (visualType1, visualType2) {\n        return visualType2 === 'color' ? !!(visualType1 && visualType1.indexOf(visualType2) === 0) : visualType1 === visualType2;\n      };\n      /**\n       * @param value\n       * @param pieceList [{value: ..., interval: [min, max]}, ...]\n       *                         Always from small to big.\n       * @param findClosestWhenOutside Default to be false\n       * @return index\n       */\n\n\n      VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) {\n        var possibleI;\n        var abs = Infinity; // value has the higher priority.\n\n        for (var i = 0, len = pieceList.length; i < len; i++) {\n          var pieceValue = pieceList[i].value;\n\n          if (pieceValue != null) {\n            if (pieceValue === value // FIXME\n            // It is supposed to compare value according to value type of dimension,\n            // but currently value type can exactly be string or number.\n            // Compromise for numeric-like string (like '12'), especially\n            // in the case that visualMap.categories is ['22', '33'].\n            || isString(pieceValue) && pieceValue === value + '') {\n              return i;\n            }\n\n            findClosestWhenOutside && updatePossible(pieceValue, i);\n          }\n        }\n\n        for (var i = 0, len = pieceList.length; i < len; i++) {\n          var piece = pieceList[i];\n          var interval = piece.interval;\n          var close_1 = piece.close;\n\n          if (interval) {\n            if (interval[0] === -Infinity) {\n              if (littleThan(close_1[1], value, interval[1])) {\n                return i;\n              }\n            } else if (interval[1] === Infinity) {\n              if (littleThan(close_1[0], interval[0], value)) {\n                return i;\n              }\n            } else if (littleThan(close_1[0], interval[0], value) && littleThan(close_1[1], value, interval[1])) {\n              return i;\n            }\n\n            findClosestWhenOutside && updatePossible(interval[0], i);\n            findClosestWhenOutside && updatePossible(interval[1], i);\n          }\n        }\n\n        if (findClosestWhenOutside) {\n          return value === Infinity ? pieceList.length - 1 : value === -Infinity ? 0 : possibleI;\n        }\n\n        function updatePossible(val, index) {\n          var newAbs = Math.abs(val - value);\n\n          if (newAbs < abs) {\n            abs = newAbs;\n            possibleI = index;\n          }\n        }\n      };\n\n      VisualMapping.visualHandlers = {\n        color: {\n          applyVisual: makeApplyVisual('color'),\n          getColorMapper: function () {\n            var thisOption = this.option;\n            return bind(thisOption.mappingMethod === 'category' ? function (value, isNormalized) {\n              !isNormalized && (value = this._normalizeData(value));\n              return doMapCategory.call(this, value);\n            } : function (value, isNormalized, out) {\n              // If output rgb array\n              // which will be much faster and useful in pixel manipulation\n              var returnRGBArray = !!out;\n              !isNormalized && (value = this._normalizeData(value));\n              out = fastLerp(value, thisOption.parsedVisual, out);\n              return returnRGBArray ? out : stringify(out, 'rgba');\n            }, this);\n          },\n          _normalizedToVisual: {\n            linear: function (normalized) {\n              return stringify(fastLerp(normalized, this.option.parsedVisual), 'rgba');\n            },\n            category: doMapCategory,\n            piecewise: function (normalized, value) {\n              var result = getSpecifiedVisual.call(this, value);\n\n              if (result == null) {\n                result = stringify(fastLerp(normalized, this.option.parsedVisual), 'rgba');\n              }\n\n              return result;\n            },\n            fixed: doMapFixed\n          }\n        },\n        colorHue: makePartialColorVisualHandler(function (color$1, value) {\n          return modifyHSL(color$1, value);\n        }),\n        colorSaturation: makePartialColorVisualHandler(function (color$1, value) {\n          return modifyHSL(color$1, null, value);\n        }),\n        colorLightness: makePartialColorVisualHandler(function (color$1, value) {\n          return modifyHSL(color$1, null, null, value);\n        }),\n        colorAlpha: makePartialColorVisualHandler(function (color$1, value) {\n          return modifyAlpha(color$1, value);\n        }),\n        decal: {\n          applyVisual: makeApplyVisual('decal'),\n          _normalizedToVisual: {\n            linear: null,\n            category: doMapCategory,\n            piecewise: null,\n            fixed: null\n          }\n        },\n        opacity: {\n          applyVisual: makeApplyVisual('opacity'),\n          _normalizedToVisual: createNormalizedToNumericVisual([0, 1])\n        },\n        liftZ: {\n          applyVisual: makeApplyVisual('liftZ'),\n          _normalizedToVisual: {\n            linear: doMapFixed,\n            category: doMapFixed,\n            piecewise: doMapFixed,\n            fixed: doMapFixed\n          }\n        },\n        symbol: {\n          applyVisual: function (value, getter, setter) {\n            var symbolCfg = this.mapValueToVisual(value);\n            setter('symbol', symbolCfg);\n          },\n          _normalizedToVisual: {\n            linear: doMapToArray,\n            category: doMapCategory,\n            piecewise: function (normalized, value) {\n              var result = getSpecifiedVisual.call(this, value);\n\n              if (result == null) {\n                result = doMapToArray.call(this, normalized);\n              }\n\n              return result;\n            },\n            fixed: doMapFixed\n          }\n        },\n        symbolSize: {\n          applyVisual: makeApplyVisual('symbolSize'),\n          _normalizedToVisual: createNormalizedToNumericVisual([0, 1])\n        }\n      };\n      return VisualMapping;\n    }();\n\n    function preprocessForPiecewise(thisOption) {\n      var pieceList = thisOption.pieceList;\n      thisOption.hasSpecialVisual = false;\n      each(pieceList, function (piece, index) {\n        piece.originIndex = index; // piece.visual is \"result visual value\" but not\n        // a visual range, so it does not need to be normalized.\n\n        if (piece.visual != null) {\n          thisOption.hasSpecialVisual = true;\n        }\n      });\n    }\n\n    function preprocessForSpecifiedCategory(thisOption) {\n      // Hash categories.\n      var categories = thisOption.categories;\n      var categoryMap = thisOption.categoryMap = {};\n      var visual = thisOption.visual;\n      each$3(categories, function (cate, index) {\n        categoryMap[cate] = index;\n      }); // Process visual map input.\n\n      if (!isArray(visual)) {\n        var visualArr_1 = [];\n\n        if (isObject(visual)) {\n          each$3(visual, function (v, cate) {\n            var index = categoryMap[cate];\n            visualArr_1[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v;\n          });\n        } else {\n          // Is primary type, represents default visual.\n          visualArr_1[CATEGORY_DEFAULT_VISUAL_INDEX] = visual;\n        }\n\n        visual = setVisualToOption(thisOption, visualArr_1);\n      } // Remove categories that has no visual,\n      // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX.\n\n\n      for (var i = categories.length - 1; i >= 0; i--) {\n        if (visual[i] == null) {\n          delete categoryMap[categories[i]];\n          categories.pop();\n        }\n      }\n    }\n\n    function normalizeVisualRange(thisOption, isCategory) {\n      var visual = thisOption.visual;\n      var visualArr = [];\n\n      if (isObject(visual)) {\n        each$3(visual, function (v) {\n          visualArr.push(v);\n        });\n      } else if (visual != null) {\n        visualArr.push(visual);\n      }\n\n      var doNotNeedPair = {\n        color: 1,\n        symbol: 1\n      };\n\n      if (!isCategory && visualArr.length === 1 && !doNotNeedPair.hasOwnProperty(thisOption.type)) {\n        // Do not care visualArr.length === 0, which is illegal.\n        visualArr[1] = visualArr[0];\n      }\n\n      setVisualToOption(thisOption, visualArr);\n    }\n\n    function makePartialColorVisualHandler(applyValue) {\n      return {\n        applyVisual: function (value, getter, setter) {\n          // Only used in HSL\n          var colorChannel = this.mapValueToVisual(value); // Must not be array value\n\n          setter('color', applyValue(getter('color'), colorChannel));\n        },\n        _normalizedToVisual: createNormalizedToNumericVisual([0, 1])\n      };\n    }\n\n    function doMapToArray(normalized) {\n      var visual = this.option.visual;\n      return visual[Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true))] || {}; // TODO {}?\n    }\n\n    function makeApplyVisual(visualType) {\n      return function (value, getter, setter) {\n        setter(visualType, this.mapValueToVisual(value));\n      };\n    }\n\n    function doMapCategory(normalized) {\n      var visual = this.option.visual;\n      return visual[this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX ? normalized % visual.length : normalized];\n    }\n\n    function doMapFixed() {\n      // visual will be convert to array.\n      return this.option.visual[0];\n    }\n    /**\n     * Create mapped to numeric visual\n     */\n\n\n    function createNormalizedToNumericVisual(sourceExtent) {\n      return {\n        linear: function (normalized) {\n          return linearMap(normalized, sourceExtent, this.option.visual, true);\n        },\n        category: doMapCategory,\n        piecewise: function (normalized, value) {\n          var result = getSpecifiedVisual.call(this, value);\n\n          if (result == null) {\n            result = linearMap(normalized, sourceExtent, this.option.visual, true);\n          }\n\n          return result;\n        },\n        fixed: doMapFixed\n      };\n    }\n\n    function getSpecifiedVisual(value) {\n      var thisOption = this.option;\n      var pieceList = thisOption.pieceList;\n\n      if (thisOption.hasSpecialVisual) {\n        var pieceIndex = VisualMapping.findPieceIndex(value, pieceList);\n        var piece = pieceList[pieceIndex];\n\n        if (piece && piece.visual) {\n          return piece.visual[this.type];\n        }\n      }\n    }\n\n    function setVisualToOption(thisOption, visualArr) {\n      thisOption.visual = visualArr;\n\n      if (thisOption.type === 'color') {\n        thisOption.parsedVisual = map(visualArr, function (item) {\n          var color$1 = parse(item);\n\n          if (!color$1 && \"development\" !== 'production') {\n            warn(\"'\" + item + \"' is an illegal color, fallback to '#000000'\", true);\n          }\n\n          return color$1 || [0, 0, 0, 1];\n        });\n      }\n\n      return visualArr;\n    }\n    /**\n     * Normalizers by mapping methods.\n     */\n\n\n    var normalizers = {\n      linear: function (value) {\n        return linearMap(value, this.option.dataExtent, [0, 1], true);\n      },\n      piecewise: function (value) {\n        var pieceList = this.option.pieceList;\n        var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true);\n\n        if (pieceIndex != null) {\n          return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true);\n        }\n      },\n      category: function (value) {\n        var index = this.option.categories ? this.option.categoryMap[value] : value; // ordinal value\n\n        return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index;\n      },\n      fixed: noop\n    };\n\n    function littleThan(close, a, b) {\n      return close ? a <= b : a < b;\n    }\n\n    var ITEM_STYLE_NORMAL = 'itemStyle';\n    var inner$9 = makeInner();\n    var treemapVisual = {\n      seriesType: 'treemap',\n      reset: function (seriesModel) {\n        var tree = seriesModel.getData().tree;\n        var root = tree.root;\n\n        if (root.isRemoved()) {\n          return;\n        }\n\n        travelTree(root, // Visual should calculate from tree root but not view root.\n        {}, seriesModel.getViewRoot().getAncestors(), seriesModel);\n      }\n    };\n\n    function travelTree(node, designatedVisual, viewRootAncestors, seriesModel) {\n      var nodeModel = node.getModel();\n      var nodeLayout = node.getLayout();\n      var data = node.hostTree.data; // Optimize\n\n      if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) {\n        return;\n      }\n\n      var nodeItemStyleModel = nodeModel.getModel(ITEM_STYLE_NORMAL);\n      var visuals = buildVisuals(nodeItemStyleModel, designatedVisual, seriesModel);\n      var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style'); // calculate border color\n\n      var borderColor = nodeItemStyleModel.get('borderColor');\n      var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation');\n      var thisNodeColor;\n\n      if (borderColorSaturation != null) {\n        // For performance, do not always execute 'calculateColor'.\n        thisNodeColor = calculateColor(visuals);\n        borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor);\n      }\n\n      existsStyle.stroke = borderColor;\n      var viewChildren = node.viewChildren;\n\n      if (!viewChildren || !viewChildren.length) {\n        thisNodeColor = calculateColor(visuals); // Apply visual to this node.\n\n        existsStyle.fill = thisNodeColor;\n      } else {\n        var mapping_1 = buildVisualMapping(node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren); // Designate visual to children.\n\n        each(viewChildren, function (child, index) {\n          // If higher than viewRoot, only ancestors of viewRoot is needed to visit.\n          if (child.depth >= viewRootAncestors.length || child === viewRootAncestors[child.depth]) {\n            var childVisual = mapVisual(nodeModel, visuals, child, index, mapping_1, seriesModel);\n            travelTree(child, childVisual, viewRootAncestors, seriesModel);\n          }\n        });\n      }\n    }\n\n    function buildVisuals(nodeItemStyleModel, designatedVisual, seriesModel) {\n      var visuals = extend({}, designatedVisual);\n      var designatedVisualItemStyle = seriesModel.designatedVisualItemStyle;\n      each(['color', 'colorAlpha', 'colorSaturation'], function (visualName) {\n        // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel\n        designatedVisualItemStyle[visualName] = designatedVisual[visualName];\n        var val = nodeItemStyleModel.get(visualName);\n        designatedVisualItemStyle[visualName] = null;\n        val != null && (visuals[visualName] = val);\n      });\n      return visuals;\n    }\n\n    function calculateColor(visuals) {\n      var color = getValueVisualDefine(visuals, 'color');\n\n      if (color) {\n        var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha');\n        var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation');\n\n        if (colorSaturation) {\n          color = modifyHSL(color, null, null, colorSaturation);\n        }\n\n        if (colorAlpha) {\n          color = modifyAlpha(color, colorAlpha);\n        }\n\n        return color;\n      }\n    }\n\n    function calculateBorderColor(borderColorSaturation, thisNodeColor) {\n      return thisNodeColor != null // Can only be string\n      ? modifyHSL(thisNodeColor, null, null, borderColorSaturation) : null;\n    }\n\n    function getValueVisualDefine(visuals, name) {\n      var value = visuals[name];\n\n      if (value != null && value !== 'none') {\n        return value;\n      }\n    }\n\n    function buildVisualMapping(node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren) {\n      if (!viewChildren || !viewChildren.length) {\n        return;\n      }\n\n      var rangeVisual = getRangeVisual(nodeModel, 'color') || visuals.color != null && visuals.color !== 'none' && (getRangeVisual(nodeModel, 'colorAlpha') || getRangeVisual(nodeModel, 'colorSaturation'));\n\n      if (!rangeVisual) {\n        return;\n      }\n\n      var visualMin = nodeModel.get('visualMin');\n      var visualMax = nodeModel.get('visualMax');\n      var dataExtent = nodeLayout.dataExtent.slice();\n      visualMin != null && visualMin < dataExtent[0] && (dataExtent[0] = visualMin);\n      visualMax != null && visualMax > dataExtent[1] && (dataExtent[1] = visualMax);\n      var colorMappingBy = nodeModel.get('colorMappingBy');\n      var opt = {\n        type: rangeVisual.name,\n        dataExtent: dataExtent,\n        visual: rangeVisual.range\n      };\n\n      if (opt.type === 'color' && (colorMappingBy === 'index' || colorMappingBy === 'id')) {\n        opt.mappingMethod = 'category';\n        opt.loop = true; // categories is ordinal, so do not set opt.categories.\n      } else {\n        opt.mappingMethod = 'linear';\n      }\n\n      var mapping = new VisualMapping(opt);\n      inner$9(mapping).drColorMappingBy = colorMappingBy;\n      return mapping;\n    } // Notice: If we don't have the attribute 'colorRange', but only use\n    // attribute 'color' to represent both concepts of 'colorRange' and 'color',\n    // (It means 'colorRange' when 'color' is Array, means 'color' when not array),\n    // this problem will be encountered:\n    // If a level-1 node doesn't have children, and its siblings have children,\n    // and colorRange is set on level-1, then the node cannot be colored.\n    // So we separate 'colorRange' and 'color' to different attributes.\n\n\n    function getRangeVisual(nodeModel, name) {\n      // 'colorRange', 'colorARange', 'colorSRange'.\n      // If not exists on this node, fetch from levels and series.\n      var range = nodeModel.get(name);\n      return isArray(range) && range.length ? {\n        name: name,\n        range: range\n      } : null;\n    }\n\n    function mapVisual(nodeModel, visuals, child, index, mapping, seriesModel) {\n      var childVisuals = extend({}, visuals);\n\n      if (mapping) {\n        // Only support color, colorAlpha, colorSaturation.\n        var mappingType = mapping.type;\n        var colorMappingBy = mappingType === 'color' && inner$9(mapping).drColorMappingBy;\n        var value = colorMappingBy === 'index' ? index : colorMappingBy === 'id' ? seriesModel.mapIdToIndex(child.getId()) : child.getValue(nodeModel.get('visualDimension'));\n        childVisuals[mappingType] = mapping.mapValueToVisual(value);\n      }\n\n      return childVisuals;\n    }\n\n    var mathMax$7 = Math.max;\n    var mathMin$7 = Math.min;\n    var retrieveValue = retrieve;\n    var each$4 = each;\n    var PATH_BORDER_WIDTH = ['itemStyle', 'borderWidth'];\n    var PATH_GAP_WIDTH = ['itemStyle', 'gapWidth'];\n    var PATH_UPPER_LABEL_SHOW = ['upperLabel', 'show'];\n    var PATH_UPPER_LABEL_HEIGHT = ['upperLabel', 'height'];\n    /**\n     * @public\n     */\n\n    var treemapLayout = {\n      seriesType: 'treemap',\n      reset: function (seriesModel, ecModel, api, payload) {\n        // Layout result in each node:\n        // {x, y, width, height, area, borderWidth}\n        var ecWidth = api.getWidth();\n        var ecHeight = api.getHeight();\n        var seriesOption = seriesModel.option;\n        var layoutInfo = getLayoutRect(seriesModel.getBoxLayoutParams(), {\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n        var size = seriesOption.size || []; // Compatible with ec2.\n\n        var containerWidth = parsePercent$1(retrieveValue(layoutInfo.width, size[0]), ecWidth);\n        var containerHeight = parsePercent$1(retrieveValue(layoutInfo.height, size[1]), ecHeight); // Fetch payload info.\n\n        var payloadType = payload && payload.type;\n        var types = ['treemapZoomToNode', 'treemapRootToNode'];\n        var targetInfo = retrieveTargetInfo(payload, types, seriesModel);\n        var rootRect = payloadType === 'treemapRender' || payloadType === 'treemapMove' ? payload.rootRect : null;\n        var viewRoot = seriesModel.getViewRoot();\n        var viewAbovePath = getPathToRoot(viewRoot);\n\n        if (payloadType !== 'treemapMove') {\n          var rootSize = payloadType === 'treemapZoomToNode' ? estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) : rootRect ? [rootRect.width, rootRect.height] : [containerWidth, containerHeight];\n          var sort_1 = seriesOption.sort;\n\n          if (sort_1 && sort_1 !== 'asc' && sort_1 !== 'desc') {\n            // Default to be desc order.\n            sort_1 = 'desc';\n          }\n\n          var options = {\n            squareRatio: seriesOption.squareRatio,\n            sort: sort_1,\n            leafDepth: seriesOption.leafDepth\n          }; // layout should be cleared because using updateView but not update.\n\n          viewRoot.hostTree.clearLayouts(); // TODO\n          // optimize: if out of view clip, do not layout.\n          // But take care that if do not render node out of view clip,\n          // how to calculate start po\n\n          var viewRootLayout_1 = {\n            x: 0,\n            y: 0,\n            width: rootSize[0],\n            height: rootSize[1],\n            area: rootSize[0] * rootSize[1]\n          };\n          viewRoot.setLayout(viewRootLayout_1);\n          squarify(viewRoot, options, false, 0); // Supplement layout.\n\n          viewRootLayout_1 = viewRoot.getLayout();\n          each$4(viewAbovePath, function (node, index) {\n            var childValue = (viewAbovePath[index + 1] || viewRoot).getValue();\n            node.setLayout(extend({\n              dataExtent: [childValue, childValue],\n              borderWidth: 0,\n              upperHeight: 0\n            }, viewRootLayout_1));\n          });\n        }\n\n        var treeRoot = seriesModel.getData().tree.root;\n        treeRoot.setLayout(calculateRootPosition(layoutInfo, rootRect, targetInfo), true);\n        seriesModel.setLayoutInfo(layoutInfo); // FIXME\n        // 现在没有clip功能，暂时取ec高宽。\n\n        prunning(treeRoot, // Transform to base element coordinate system.\n        new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight), viewAbovePath, viewRoot, 0);\n      }\n    };\n    /**\n     * Layout treemap with squarify algorithm.\n     * The original presentation of this algorithm\n     * was made by Mark Bruls, Kees Huizing, and Jarke J. van Wijk\n     * <https://graphics.ethz.ch/teaching/scivis_common/Literature/squarifiedTreeMaps.pdf>.\n     * The implementation of this algorithm was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/layout/treemap.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     *\n     * @protected\n     * @param {module:echarts/data/Tree~TreeNode} node\n     * @param {Object} options\n     * @param {string} options.sort 'asc' or 'desc'\n     * @param {number} options.squareRatio\n     * @param {boolean} hideChildren\n     * @param {number} depth\n     */\n\n    function squarify(node, options, hideChildren, depth) {\n      var width;\n      var height;\n\n      if (node.isRemoved()) {\n        return;\n      }\n\n      var thisLayout = node.getLayout();\n      width = thisLayout.width;\n      height = thisLayout.height; // Considering border and gap\n\n      var nodeModel = node.getModel();\n      var borderWidth = nodeModel.get(PATH_BORDER_WIDTH);\n      var halfGapWidth = nodeModel.get(PATH_GAP_WIDTH) / 2;\n      var upperLabelHeight = getUpperLabelHeight(nodeModel);\n      var upperHeight = Math.max(borderWidth, upperLabelHeight);\n      var layoutOffset = borderWidth - halfGapWidth;\n      var layoutOffsetUpper = upperHeight - halfGapWidth;\n      node.setLayout({\n        borderWidth: borderWidth,\n        upperHeight: upperHeight,\n        upperLabelHeight: upperLabelHeight\n      }, true);\n      width = mathMax$7(width - 2 * layoutOffset, 0);\n      height = mathMax$7(height - layoutOffset - layoutOffsetUpper, 0);\n      var totalArea = width * height;\n      var viewChildren = initChildren(node, nodeModel, totalArea, options, hideChildren, depth);\n\n      if (!viewChildren.length) {\n        return;\n      }\n\n      var rect = {\n        x: layoutOffset,\n        y: layoutOffsetUpper,\n        width: width,\n        height: height\n      };\n      var rowFixedLength = mathMin$7(width, height);\n      var best = Infinity; // the best row score so far\n\n      var row = [];\n      row.area = 0;\n\n      for (var i = 0, len = viewChildren.length; i < len;) {\n        var child = viewChildren[i];\n        row.push(child);\n        row.area += child.getLayout().area;\n        var score = worst(row, rowFixedLength, options.squareRatio); // continue with this orientation\n\n        if (score <= best) {\n          i++;\n          best = score;\n        } // abort, and try a different orientation\n        else {\n            row.area -= row.pop().getLayout().area;\n            position(row, rowFixedLength, rect, halfGapWidth, false);\n            rowFixedLength = mathMin$7(rect.width, rect.height);\n            row.length = row.area = 0;\n            best = Infinity;\n          }\n      }\n\n      if (row.length) {\n        position(row, rowFixedLength, rect, halfGapWidth, true);\n      }\n\n      if (!hideChildren) {\n        var childrenVisibleMin = nodeModel.get('childrenVisibleMin');\n\n        if (childrenVisibleMin != null && totalArea < childrenVisibleMin) {\n          hideChildren = true;\n        }\n      }\n\n      for (var i = 0, len = viewChildren.length; i < len; i++) {\n        squarify(viewChildren[i], options, hideChildren, depth + 1);\n      }\n    }\n    /**\n     * Set area to each child, and calculate data extent for visual coding.\n     */\n\n\n    function initChildren(node, nodeModel, totalArea, options, hideChildren, depth) {\n      var viewChildren = node.children || [];\n      var orderBy = options.sort;\n      orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null);\n      var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth; // leafDepth has higher priority.\n\n      if (hideChildren && !overLeafDepth) {\n        return node.viewChildren = [];\n      } // Sort children, order by desc.\n\n\n      viewChildren = filter(viewChildren, function (child) {\n        return !child.isRemoved();\n      });\n      sort$1(viewChildren, orderBy);\n      var info = statistic(nodeModel, viewChildren, orderBy);\n\n      if (info.sum === 0) {\n        return node.viewChildren = [];\n      }\n\n      info.sum = filterByThreshold(nodeModel, totalArea, info.sum, orderBy, viewChildren);\n\n      if (info.sum === 0) {\n        return node.viewChildren = [];\n      } // Set area to each child.\n\n\n      for (var i = 0, len = viewChildren.length; i < len; i++) {\n        var area = viewChildren[i].getValue() / info.sum * totalArea; // Do not use setLayout({...}, true), because it is needed to clear last layout.\n\n        viewChildren[i].setLayout({\n          area: area\n        });\n      }\n\n      if (overLeafDepth) {\n        viewChildren.length && node.setLayout({\n          isLeafRoot: true\n        }, true);\n        viewChildren.length = 0;\n      }\n\n      node.viewChildren = viewChildren;\n      node.setLayout({\n        dataExtent: info.dataExtent\n      }, true);\n      return viewChildren;\n    }\n    /**\n     * Consider 'visibleMin'. Modify viewChildren and get new sum.\n     */\n\n\n    function filterByThreshold(nodeModel, totalArea, sum, orderBy, orderedChildren) {\n      // visibleMin is not supported yet when no option.sort.\n      if (!orderBy) {\n        return sum;\n      }\n\n      var visibleMin = nodeModel.get('visibleMin');\n      var len = orderedChildren.length;\n      var deletePoint = len; // Always travel from little value to big value.\n\n      for (var i = len - 1; i >= 0; i--) {\n        var value = orderedChildren[orderBy === 'asc' ? len - i - 1 : i].getValue();\n\n        if (value / sum * totalArea < visibleMin) {\n          deletePoint = i;\n          sum -= value;\n        }\n      }\n\n      orderBy === 'asc' ? orderedChildren.splice(0, len - deletePoint) : orderedChildren.splice(deletePoint, len - deletePoint);\n      return sum;\n    }\n    /**\n     * Sort\n     */\n\n\n    function sort$1(viewChildren, orderBy) {\n      if (orderBy) {\n        viewChildren.sort(function (a, b) {\n          var diff = orderBy === 'asc' ? a.getValue() - b.getValue() : b.getValue() - a.getValue();\n          return diff === 0 ? orderBy === 'asc' ? a.dataIndex - b.dataIndex : b.dataIndex - a.dataIndex : diff;\n        });\n      }\n\n      return viewChildren;\n    }\n    /**\n     * Statistic\n     */\n\n\n    function statistic(nodeModel, children, orderBy) {\n      // Calculate sum.\n      var sum = 0;\n\n      for (var i = 0, len = children.length; i < len; i++) {\n        sum += children[i].getValue();\n      } // Statistic data extent for latter visual coding.\n      // Notice: data extent should be calculate based on raw children\n      // but not filtered view children, otherwise visual mapping will not\n      // be stable when zoom (where children is filtered by visibleMin).\n\n\n      var dimension = nodeModel.get('visualDimension');\n      var dataExtent; // The same as area dimension.\n\n      if (!children || !children.length) {\n        dataExtent = [NaN, NaN];\n      } else if (dimension === 'value' && orderBy) {\n        dataExtent = [children[children.length - 1].getValue(), children[0].getValue()];\n        orderBy === 'asc' && dataExtent.reverse();\n      } // Other dimension.\n      else {\n          dataExtent = [Infinity, -Infinity];\n          each$4(children, function (child) {\n            var value = child.getValue(dimension);\n            value < dataExtent[0] && (dataExtent[0] = value);\n            value > dataExtent[1] && (dataExtent[1] = value);\n          });\n        }\n\n      return {\n        sum: sum,\n        dataExtent: dataExtent\n      };\n    }\n    /**\n     * Computes the score for the specified row,\n     * as the worst aspect ratio.\n     */\n\n\n    function worst(row, rowFixedLength, ratio) {\n      var areaMax = 0;\n      var areaMin = Infinity;\n\n      for (var i = 0, area = void 0, len = row.length; i < len; i++) {\n        area = row[i].getLayout().area;\n\n        if (area) {\n          area < areaMin && (areaMin = area);\n          area > areaMax && (areaMax = area);\n        }\n      }\n\n      var squareArea = row.area * row.area;\n      var f = rowFixedLength * rowFixedLength * ratio;\n      return squareArea ? mathMax$7(f * areaMax / squareArea, squareArea / (f * areaMin)) : Infinity;\n    }\n    /**\n     * Positions the specified row of nodes. Modifies `rect`.\n     */\n\n\n    function position(row, rowFixedLength, rect, halfGapWidth, flush) {\n      // When rowFixedLength === rect.width,\n      // it is horizontal subdivision,\n      // rowFixedLength is the width of the subdivision,\n      // rowOtherLength is the height of the subdivision,\n      // and nodes will be positioned from left to right.\n      // wh[idx0WhenH] means: when horizontal,\n      //      wh[idx0WhenH] => wh[0] => 'width'.\n      //      xy[idx1WhenH] => xy[1] => 'y'.\n      var idx0WhenH = rowFixedLength === rect.width ? 0 : 1;\n      var idx1WhenH = 1 - idx0WhenH;\n      var xy = ['x', 'y'];\n      var wh = ['width', 'height'];\n      var last = rect[xy[idx0WhenH]];\n      var rowOtherLength = rowFixedLength ? row.area / rowFixedLength : 0;\n\n      if (flush || rowOtherLength > rect[wh[idx1WhenH]]) {\n        rowOtherLength = rect[wh[idx1WhenH]]; // over+underflow\n      }\n\n      for (var i = 0, rowLen = row.length; i < rowLen; i++) {\n        var node = row[i];\n        var nodeLayout = {};\n        var step = rowOtherLength ? node.getLayout().area / rowOtherLength : 0;\n        var wh1 = nodeLayout[wh[idx1WhenH]] = mathMax$7(rowOtherLength - 2 * halfGapWidth, 0); // We use Math.max/min to avoid negative width/height when considering gap width.\n\n        var remain = rect[xy[idx0WhenH]] + rect[wh[idx0WhenH]] - last;\n        var modWH = i === rowLen - 1 || remain < step ? remain : step;\n        var wh0 = nodeLayout[wh[idx0WhenH]] = mathMax$7(modWH - 2 * halfGapWidth, 0);\n        nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin$7(halfGapWidth, wh1 / 2);\n        nodeLayout[xy[idx0WhenH]] = last + mathMin$7(halfGapWidth, wh0 / 2);\n        last += modWH;\n        node.setLayout(nodeLayout, true);\n      }\n\n      rect[xy[idx1WhenH]] += rowOtherLength;\n      rect[wh[idx1WhenH]] -= rowOtherLength;\n    } // Return [containerWidth, containerHeight] as default.\n\n\n    function estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) {\n      // If targetInfo.node exists, we zoom to the node,\n      // so estimate whole width and height by target node.\n      var currNode = (targetInfo || {}).node;\n      var defaultSize = [containerWidth, containerHeight];\n\n      if (!currNode || currNode === viewRoot) {\n        return defaultSize;\n      }\n\n      var parent;\n      var viewArea = containerWidth * containerHeight;\n      var area = viewArea * seriesModel.option.zoomToNodeRatio;\n\n      while (parent = currNode.parentNode) {\n        // jshint ignore:line\n        var sum = 0;\n        var siblings = parent.children;\n\n        for (var i = 0, len = siblings.length; i < len; i++) {\n          sum += siblings[i].getValue();\n        }\n\n        var currNodeValue = currNode.getValue();\n\n        if (currNodeValue === 0) {\n          return defaultSize;\n        }\n\n        area *= sum / currNodeValue; // Considering border, suppose aspect ratio is 1.\n\n        var parentModel = parent.getModel();\n        var borderWidth = parentModel.get(PATH_BORDER_WIDTH);\n        var upperHeight = Math.max(borderWidth, getUpperLabelHeight(parentModel));\n        area += 4 * borderWidth * borderWidth + (3 * borderWidth + upperHeight) * Math.pow(area, 0.5);\n        area > MAX_SAFE_INTEGER && (area = MAX_SAFE_INTEGER);\n        currNode = parent;\n      }\n\n      area < viewArea && (area = viewArea);\n      var scale = Math.pow(area / viewArea, 0.5);\n      return [containerWidth * scale, containerHeight * scale];\n    } // Root position based on coord of containerGroup\n\n\n    function calculateRootPosition(layoutInfo, rootRect, targetInfo) {\n      if (rootRect) {\n        return {\n          x: rootRect.x,\n          y: rootRect.y\n        };\n      }\n\n      var defaultPosition = {\n        x: 0,\n        y: 0\n      };\n\n      if (!targetInfo) {\n        return defaultPosition;\n      } // If targetInfo is fetched by 'retrieveTargetInfo',\n      // old tree and new tree are the same tree,\n      // so the node still exists and we can visit it.\n\n\n      var targetNode = targetInfo.node;\n      var layout = targetNode.getLayout();\n\n      if (!layout) {\n        return defaultPosition;\n      } // Transform coord from local to container.\n\n\n      var targetCenter = [layout.width / 2, layout.height / 2];\n      var node = targetNode;\n\n      while (node) {\n        var nodeLayout = node.getLayout();\n        targetCenter[0] += nodeLayout.x;\n        targetCenter[1] += nodeLayout.y;\n        node = node.parentNode;\n      }\n\n      return {\n        x: layoutInfo.width / 2 - targetCenter[0],\n        y: layoutInfo.height / 2 - targetCenter[1]\n      };\n    } // Mark nodes visible for prunning when visual coding and rendering.\n    // Prunning depends on layout and root position, so we have to do it after layout.\n\n\n    function prunning(node, clipRect, viewAbovePath, viewRoot, depth) {\n      var nodeLayout = node.getLayout();\n      var nodeInViewAbovePath = viewAbovePath[depth];\n      var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node;\n\n      if (nodeInViewAbovePath && !isAboveViewRoot || depth === viewAbovePath.length && node !== viewRoot) {\n        return;\n      }\n\n      node.setLayout({\n        // isInView means: viewRoot sub tree + viewAbovePath\n        isInView: true,\n        // invisible only means: outside view clip so that the node can not\n        // see but still layout for animation preparation but not render.\n        invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout),\n        isAboveViewRoot: isAboveViewRoot\n      }, true); // Transform to child coordinate.\n\n      var childClipRect = new BoundingRect(clipRect.x - nodeLayout.x, clipRect.y - nodeLayout.y, clipRect.width, clipRect.height);\n      each$4(node.viewChildren || [], function (child) {\n        prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1);\n      });\n    }\n\n    function getUpperLabelHeight(model) {\n      return model.get(PATH_UPPER_LABEL_SHOW) ? model.get(PATH_UPPER_LABEL_HEIGHT) : 0;\n    }\n\n    function install$c(registers) {\n      registers.registerSeriesModel(TreemapSeriesModel);\n      registers.registerChartView(TreemapView);\n      registers.registerVisual(treemapVisual);\n      registers.registerLayout(treemapLayout);\n      installTreemapAction(registers);\n    }\n\n    function categoryFilter(ecModel) {\n      var legendModels = ecModel.findComponents({\n        mainType: 'legend'\n      });\n\n      if (!legendModels || !legendModels.length) {\n        return;\n      }\n\n      ecModel.eachSeriesByType('graph', function (graphSeries) {\n        var categoriesData = graphSeries.getCategoriesData();\n        var graph = graphSeries.getGraph();\n        var data = graph.data;\n        var categoryNames = categoriesData.mapArray(categoriesData.getName);\n        data.filterSelf(function (idx) {\n          var model = data.getItemModel(idx);\n          var category = model.getShallow('category');\n\n          if (category != null) {\n            if (isNumber(category)) {\n              category = categoryNames[category];\n            } // If in any legend component the status is not selected.\n\n\n            for (var i = 0; i < legendModels.length; i++) {\n              if (!legendModels[i].isSelected(category)) {\n                return false;\n              }\n            }\n          }\n\n          return true;\n        });\n      });\n    }\n\n    function categoryVisual(ecModel) {\n      var paletteScope = {};\n      ecModel.eachSeriesByType('graph', function (seriesModel) {\n        var categoriesData = seriesModel.getCategoriesData();\n        var data = seriesModel.getData();\n        var categoryNameIdxMap = {};\n        categoriesData.each(function (idx) {\n          var name = categoriesData.getName(idx); // Add prefix to avoid conflict with Object.prototype.\n\n          categoryNameIdxMap['ec-' + name] = idx;\n          var itemModel = categoriesData.getItemModel(idx);\n          var style = itemModel.getModel('itemStyle').getItemStyle();\n\n          if (!style.fill) {\n            // Get color from palette.\n            style.fill = seriesModel.getColorFromPalette(name, paletteScope);\n          }\n\n          categoriesData.setItemVisual(idx, 'style', style);\n          var symbolVisualList = ['symbol', 'symbolSize', 'symbolKeepAspect'];\n\n          for (var i = 0; i < symbolVisualList.length; i++) {\n            var symbolVisual = itemModel.getShallow(symbolVisualList[i], true);\n\n            if (symbolVisual != null) {\n              categoriesData.setItemVisual(idx, symbolVisualList[i], symbolVisual);\n            }\n          }\n        }); // Assign category color to visual\n\n        if (categoriesData.count()) {\n          data.each(function (idx) {\n            var model = data.getItemModel(idx);\n            var categoryIdx = model.getShallow('category');\n\n            if (categoryIdx != null) {\n              if (isString(categoryIdx)) {\n                categoryIdx = categoryNameIdxMap['ec-' + categoryIdx];\n              }\n\n              var categoryStyle = categoriesData.getItemVisual(categoryIdx, 'style');\n              var style = data.ensureUniqueItemVisual(idx, 'style');\n              extend(style, categoryStyle);\n              var visualList = ['symbol', 'symbolSize', 'symbolKeepAspect'];\n\n              for (var i = 0; i < visualList.length; i++) {\n                data.setItemVisual(idx, visualList[i], categoriesData.getItemVisual(categoryIdx, visualList[i]));\n              }\n            }\n          });\n        }\n      });\n    }\n\n    function normalize$2(a) {\n      if (!(a instanceof Array)) {\n        a = [a, a];\n      }\n\n      return a;\n    }\n\n    function graphEdgeVisual(ecModel) {\n      ecModel.eachSeriesByType('graph', function (seriesModel) {\n        var graph = seriesModel.getGraph();\n        var edgeData = seriesModel.getEdgeData();\n        var symbolType = normalize$2(seriesModel.get('edgeSymbol'));\n        var symbolSize = normalize$2(seriesModel.get('edgeSymbolSize')); // const colorQuery = ['lineStyle', 'color'] as const;\n        // const opacityQuery = ['lineStyle', 'opacity'] as const;\n\n        edgeData.setVisual('fromSymbol', symbolType && symbolType[0]);\n        edgeData.setVisual('toSymbol', symbolType && symbolType[1]);\n        edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);\n        edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]);\n        edgeData.setVisual('style', seriesModel.getModel('lineStyle').getLineStyle());\n        edgeData.each(function (idx) {\n          var itemModel = edgeData.getItemModel(idx);\n          var edge = graph.getEdgeByIndex(idx);\n          var symbolType = normalize$2(itemModel.getShallow('symbol', true));\n          var symbolSize = normalize$2(itemModel.getShallow('symbolSize', true)); // Edge visual must after node visual\n\n          var style = itemModel.getModel('lineStyle').getLineStyle();\n          var existsStyle = edgeData.ensureUniqueItemVisual(idx, 'style');\n          extend(existsStyle, style);\n\n          switch (existsStyle.stroke) {\n            case 'source':\n              {\n                var nodeStyle = edge.node1.getVisual('style');\n                existsStyle.stroke = nodeStyle && nodeStyle.fill;\n                break;\n              }\n\n            case 'target':\n              {\n                var nodeStyle = edge.node2.getVisual('style');\n                existsStyle.stroke = nodeStyle && nodeStyle.fill;\n                break;\n              }\n          }\n\n          symbolType[0] && edge.setVisual('fromSymbol', symbolType[0]);\n          symbolType[1] && edge.setVisual('toSymbol', symbolType[1]);\n          symbolSize[0] && edge.setVisual('fromSymbolSize', symbolSize[0]);\n          symbolSize[1] && edge.setVisual('toSymbolSize', symbolSize[1]);\n        });\n      });\n    }\n\n    var KEY_DELIMITER = '-->';\n    /**\n     * params handler\n     * @param {module:echarts/model/SeriesModel} seriesModel\n     * @returns {*}\n     */\n\n    var getAutoCurvenessParams = function (seriesModel) {\n      return seriesModel.get('autoCurveness') || null;\n    };\n    /**\n     * Generate a list of edge curvatures, 20 is the default\n     * @param {module:echarts/model/SeriesModel} seriesModel\n     * @param {number} appendLength\n     * @return  20 => [0, -0.2, 0.2, -0.4, 0.4, -0.6, 0.6, -0.8, 0.8, -1, 1, -1.2, 1.2, -1.4, 1.4, -1.6, 1.6, -1.8, 1.8, -2]\n     */\n\n\n    var createCurveness = function (seriesModel, appendLength) {\n      var autoCurvenessParmas = getAutoCurvenessParams(seriesModel);\n      var length = 20;\n      var curvenessList = []; // handler the function set\n\n      if (isNumber(autoCurvenessParmas)) {\n        length = autoCurvenessParmas;\n      } else if (isArray(autoCurvenessParmas)) {\n        seriesModel.__curvenessList = autoCurvenessParmas;\n        return;\n      } // append length\n\n\n      if (appendLength > length) {\n        length = appendLength;\n      } // make sure the length is even\n\n\n      var len = length % 2 ? length + 2 : length + 3;\n      curvenessList = [];\n\n      for (var i = 0; i < len; i++) {\n        curvenessList.push((i % 2 ? i + 1 : i) / 10 * (i % 2 ? -1 : 1));\n      }\n\n      seriesModel.__curvenessList = curvenessList;\n    };\n    /**\n     * Create different cache key data in the positive and negative directions, in order to set the curvature later\n     * @param {number|string|module:echarts/data/Graph.Node} n1\n     * @param {number|string|module:echarts/data/Graph.Node} n2\n     * @param {module:echarts/model/SeriesModel} seriesModel\n     * @returns {string} key\n     */\n\n\n    var getKeyOfEdges = function (n1, n2, seriesModel) {\n      var source = [n1.id, n1.dataIndex].join('.');\n      var target = [n2.id, n2.dataIndex].join('.');\n      return [seriesModel.uid, source, target].join(KEY_DELIMITER);\n    };\n    /**\n     * get opposite key\n     * @param {string} key\n     * @returns {string}\n     */\n\n\n    var getOppositeKey = function (key) {\n      var keys = key.split(KEY_DELIMITER);\n      return [keys[0], keys[2], keys[1]].join(KEY_DELIMITER);\n    };\n    /**\n     * get edgeMap with key\n     * @param edge\n     * @param {module:echarts/model/SeriesModel} seriesModel\n     */\n\n\n    var getEdgeFromMap = function (edge, seriesModel) {\n      var key = getKeyOfEdges(edge.node1, edge.node2, seriesModel);\n      return seriesModel.__edgeMap[key];\n    };\n    /**\n     * calculate all cases total length\n     * @param edge\n     * @param seriesModel\n     * @returns {number}\n     */\n\n\n    var getTotalLengthBetweenNodes = function (edge, seriesModel) {\n      var len = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node1, edge.node2, seriesModel), seriesModel);\n      var lenV = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node2, edge.node1, seriesModel), seriesModel);\n      return len + lenV;\n    };\n    /**\n     *\n     * @param key\n     */\n\n\n    var getEdgeMapLengthWithKey = function (key, seriesModel) {\n      var edgeMap = seriesModel.__edgeMap;\n      return edgeMap[key] ? edgeMap[key].length : 0;\n    };\n    /**\n     * Count the number of edges between the same two points, used to obtain the curvature table and the parity of the edge\n     * @see /graph/GraphSeries.js@getInitialData\n     * @param {module:echarts/model/SeriesModel} seriesModel\n     */\n\n\n    function initCurvenessList(seriesModel) {\n      if (!getAutoCurvenessParams(seriesModel)) {\n        return;\n      }\n\n      seriesModel.__curvenessList = [];\n      seriesModel.__edgeMap = {}; // calc the array of curveness List\n\n      createCurveness(seriesModel);\n    }\n    /**\n     * set edgeMap with key\n     * @param {number|string|module:echarts/data/Graph.Node} n1\n     * @param {number|string|module:echarts/data/Graph.Node} n2\n     * @param {module:echarts/model/SeriesModel} seriesModel\n     * @param {number} index\n     */\n\n    function createEdgeMapForCurveness(n1, n2, seriesModel, index) {\n      if (!getAutoCurvenessParams(seriesModel)) {\n        return;\n      }\n\n      var key = getKeyOfEdges(n1, n2, seriesModel);\n      var edgeMap = seriesModel.__edgeMap;\n      var oppositeEdges = edgeMap[getOppositeKey(key)]; // set direction\n\n      if (edgeMap[key] && !oppositeEdges) {\n        edgeMap[key].isForward = true;\n      } else if (oppositeEdges && edgeMap[key]) {\n        oppositeEdges.isForward = true;\n        edgeMap[key].isForward = false;\n      }\n\n      edgeMap[key] = edgeMap[key] || [];\n      edgeMap[key].push(index);\n    }\n    /**\n     * get curvature for edge\n     * @param edge\n     * @param {module:echarts/model/SeriesModel} seriesModel\n     * @param index\n     */\n\n    function getCurvenessForEdge(edge, seriesModel, index, needReverse) {\n      var autoCurvenessParams = getAutoCurvenessParams(seriesModel);\n      var isArrayParam = isArray(autoCurvenessParams);\n\n      if (!autoCurvenessParams) {\n        return null;\n      }\n\n      var edgeArray = getEdgeFromMap(edge, seriesModel);\n\n      if (!edgeArray) {\n        return null;\n      }\n\n      var edgeIndex = -1;\n\n      for (var i = 0; i < edgeArray.length; i++) {\n        if (edgeArray[i] === index) {\n          edgeIndex = i;\n          break;\n        }\n      } // if totalLen is Longer createCurveness\n\n\n      var totalLen = getTotalLengthBetweenNodes(edge, seriesModel);\n      createCurveness(seriesModel, totalLen);\n      edge.lineStyle = edge.lineStyle || {}; // if is opposite edge, must set curvenss to opposite number\n\n      var curKey = getKeyOfEdges(edge.node1, edge.node2, seriesModel);\n      var curvenessList = seriesModel.__curvenessList; // if pass array no need parity\n\n      var parityCorrection = isArrayParam ? 0 : totalLen % 2 ? 0 : 1;\n\n      if (!edgeArray.isForward) {\n        // the opposite edge show outside\n        var oppositeKey = getOppositeKey(curKey);\n        var len = getEdgeMapLengthWithKey(oppositeKey, seriesModel);\n        var resValue = curvenessList[edgeIndex + len + parityCorrection]; // isNeedReverse, simple, force type need reverse the curveness in the junction of the forword and the opposite\n\n        if (needReverse) {\n          // set as array may make the parity handle with the len of opposite\n          if (isArrayParam) {\n            if (autoCurvenessParams && autoCurvenessParams[0] === 0) {\n              return (len + parityCorrection) % 2 ? resValue : -resValue;\n            } else {\n              return ((len % 2 ? 0 : 1) + parityCorrection) % 2 ? resValue : -resValue;\n            }\n          } else {\n            return (len + parityCorrection) % 2 ? resValue : -resValue;\n          }\n        } else {\n          return curvenessList[edgeIndex + len + parityCorrection];\n        }\n      } else {\n        return curvenessList[parityCorrection + edgeIndex];\n      }\n    }\n\n    function simpleLayout(seriesModel) {\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (coordSys && coordSys.type !== 'view') {\n        return;\n      }\n\n      var graph = seriesModel.getGraph();\n      graph.eachNode(function (node) {\n        var model = node.getModel();\n        node.setLayout([+model.get('x'), +model.get('y')]);\n      });\n      simpleLayoutEdge(graph, seriesModel);\n    }\n    function simpleLayoutEdge(graph, seriesModel) {\n      graph.eachEdge(function (edge, index) {\n        var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), -getCurvenessForEdge(edge, seriesModel, index, true), 0);\n        var p1 = clone$1(edge.node1.getLayout());\n        var p2 = clone$1(edge.node2.getLayout());\n        var points = [p1, p2];\n\n        if (+curveness) {\n          points.push([(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness, (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness]);\n        }\n\n        edge.setLayout(points);\n      });\n    }\n\n    function graphSimpleLayout(ecModel, api) {\n      ecModel.eachSeriesByType('graph', function (seriesModel) {\n        var layout = seriesModel.get('layout');\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys && coordSys.type !== 'view') {\n          var data_1 = seriesModel.getData();\n          var dimensions_1 = [];\n          each(coordSys.dimensions, function (coordDim) {\n            dimensions_1 = dimensions_1.concat(data_1.mapDimensionsAll(coordDim));\n          });\n\n          for (var dataIndex = 0; dataIndex < data_1.count(); dataIndex++) {\n            var value = [];\n            var hasValue = false;\n\n            for (var i = 0; i < dimensions_1.length; i++) {\n              var val = data_1.get(dimensions_1[i], dataIndex);\n\n              if (!isNaN(val)) {\n                hasValue = true;\n              }\n\n              value.push(val);\n            }\n\n            if (hasValue) {\n              data_1.setItemLayout(dataIndex, coordSys.dataToPoint(value));\n            } else {\n              // Also {Array.<number>}, not undefined to avoid if...else... statement\n              data_1.setItemLayout(dataIndex, [NaN, NaN]);\n            }\n          }\n\n          simpleLayoutEdge(data_1.graph, seriesModel);\n        } else if (!layout || layout === 'none') {\n          simpleLayout(seriesModel);\n        }\n      });\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function getNodeGlobalScale(seriesModel) {\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (coordSys.type !== 'view') {\n        return 1;\n      }\n\n      var nodeScaleRatio = seriesModel.option.nodeScaleRatio;\n      var groupZoom = coordSys.scaleX; // Scale node when zoom changes\n\n      var roamZoom = coordSys.getZoom();\n      var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;\n      return nodeScale / groupZoom;\n    }\n    function getSymbolSize(node) {\n      var symbolSize = node.getVisual('symbolSize');\n\n      if (symbolSize instanceof Array) {\n        symbolSize = (symbolSize[0] + symbolSize[1]) / 2;\n      }\n\n      return +symbolSize;\n    }\n\n    var PI$6 = Math.PI;\n    var _symbolRadiansHalf = [];\n    /**\n     * `basedOn` can be:\n     * 'value':\n     *     This layout is not accurate and have same bad case. For example,\n     *     if the min value is very smaller than the max value, the nodes\n     *     with the min value probably overlap even though there is enough\n     *     space to layout them. So we only use this approach in the as the\n     *     init layout of the force layout.\n     *     FIXME\n     *     Probably we do not need this method any more but use\n     *     `basedOn: 'symbolSize'` in force layout if\n     *     delay its init operations to GraphView.\n     * 'symbolSize':\n     *     This approach work only if all of the symbol size calculated.\n     *     That is, the progressive rendering is not applied to graph.\n     *     FIXME\n     *     If progressive rendering is applied to graph some day,\n     *     probably we have to use `basedOn: 'value'`.\n     */\n\n    function circularLayout(seriesModel, basedOn, draggingNode, pointer) {\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (coordSys && coordSys.type !== 'view') {\n        return;\n      }\n\n      var rect = coordSys.getBoundingRect();\n      var nodeData = seriesModel.getData();\n      var graph = nodeData.graph;\n      var cx = rect.width / 2 + rect.x;\n      var cy = rect.height / 2 + rect.y;\n      var r = Math.min(rect.width, rect.height) / 2;\n      var count = nodeData.count();\n      nodeData.setLayout({\n        cx: cx,\n        cy: cy\n      });\n\n      if (!count) {\n        return;\n      }\n\n      if (draggingNode) {\n        var _a = coordSys.pointToData(pointer),\n            tempX = _a[0],\n            tempY = _a[1];\n\n        var v = [tempX - cx, tempY - cy];\n        normalize(v, v);\n        scale(v, v, r);\n        draggingNode.setLayout([cx + v[0], cy + v[1]], true);\n        var circularRotateLabel = seriesModel.get(['circular', 'rotateLabel']);\n        rotateNodeLabel(draggingNode, circularRotateLabel, cx, cy);\n      }\n\n      _layoutNodesBasedOn[basedOn](seriesModel, graph, nodeData, r, cx, cy, count);\n\n      graph.eachEdge(function (edge, index) {\n        var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), getCurvenessForEdge(edge, seriesModel, index), 0);\n        var p1 = clone$1(edge.node1.getLayout());\n        var p2 = clone$1(edge.node2.getLayout());\n        var cp1;\n        var x12 = (p1[0] + p2[0]) / 2;\n        var y12 = (p1[1] + p2[1]) / 2;\n\n        if (+curveness) {\n          curveness *= 3;\n          cp1 = [cx * curveness + x12 * (1 - curveness), cy * curveness + y12 * (1 - curveness)];\n        }\n\n        edge.setLayout([p1, p2, cp1]);\n      });\n    }\n    var _layoutNodesBasedOn = {\n      value: function (seriesModel, graph, nodeData, r, cx, cy, count) {\n        var angle = 0;\n        var sum = nodeData.getSum('value');\n        var unitAngle = Math.PI * 2 / (sum || count);\n        graph.eachNode(function (node) {\n          var value = node.getValue('value');\n          var radianHalf = unitAngle * (sum ? value : 1) / 2;\n          angle += radianHalf;\n          node.setLayout([r * Math.cos(angle) + cx, r * Math.sin(angle) + cy]);\n          angle += radianHalf;\n        });\n      },\n      symbolSize: function (seriesModel, graph, nodeData, r, cx, cy, count) {\n        var sumRadian = 0;\n        _symbolRadiansHalf.length = count;\n        var nodeScale = getNodeGlobalScale(seriesModel);\n        graph.eachNode(function (node) {\n          var symbolSize = getSymbolSize(node); // Normally this case will not happen, but we still add\n          // some the defensive code (2px is an arbitrary value).\n\n          isNaN(symbolSize) && (symbolSize = 2);\n          symbolSize < 0 && (symbolSize = 0);\n          symbolSize *= nodeScale;\n          var symbolRadianHalf = Math.asin(symbolSize / 2 / r); // when `symbolSize / 2` is bigger than `r`.\n\n          isNaN(symbolRadianHalf) && (symbolRadianHalf = PI$6 / 2);\n          _symbolRadiansHalf[node.dataIndex] = symbolRadianHalf;\n          sumRadian += symbolRadianHalf * 2;\n        });\n        var halfRemainRadian = (2 * PI$6 - sumRadian) / count / 2;\n        var angle = 0;\n        graph.eachNode(function (node) {\n          var radianHalf = halfRemainRadian + _symbolRadiansHalf[node.dataIndex];\n          angle += radianHalf; // init circular layout for\n          // 1. layout undefined node\n          // 2. not fixed node\n\n          (!node.getLayout() || !node.getLayout().fixed) && node.setLayout([r * Math.cos(angle) + cx, r * Math.sin(angle) + cy]);\n          angle += radianHalf;\n        });\n      }\n    };\n    function rotateNodeLabel(node, circularRotateLabel, cx, cy) {\n      var el = node.getGraphicEl(); // need to check if el exists. '-' value may not create node element.\n\n      if (!el) {\n        return;\n      }\n\n      var nodeModel = node.getModel();\n      var labelRotate = nodeModel.get(['label', 'rotate']) || 0;\n      var symbolPath = el.getSymbolPath();\n\n      if (circularRotateLabel) {\n        var pos = node.getLayout();\n        var rad = Math.atan2(pos[1] - cy, pos[0] - cx);\n\n        if (rad < 0) {\n          rad = Math.PI * 2 + rad;\n        }\n\n        var isLeft = pos[0] < cx;\n\n        if (isLeft) {\n          rad = rad - Math.PI;\n        }\n\n        var textPosition = isLeft ? 'left' : 'right';\n        symbolPath.setTextConfig({\n          rotation: -rad,\n          position: textPosition,\n          origin: 'center'\n        });\n        var emphasisState = symbolPath.ensureState('emphasis');\n        extend(emphasisState.textConfig || (emphasisState.textConfig = {}), {\n          position: textPosition\n        });\n      } else {\n        symbolPath.setTextConfig({\n          rotation: labelRotate *= Math.PI / 180\n        });\n      }\n    }\n\n    function graphCircularLayout(ecModel) {\n      ecModel.eachSeriesByType('graph', function (seriesModel) {\n        if (seriesModel.get('layout') === 'circular') {\n          circularLayout(seriesModel, 'symbolSize');\n        }\n      });\n    }\n\n    var scaleAndAdd$1 = scaleAndAdd; // function adjacentNode(n, e) {\n    //     return e.n1 === n ? e.n2 : e.n1;\n    // }\n\n    function forceLayout(inNodes, inEdges, opts) {\n      var nodes = inNodes;\n      var edges = inEdges;\n      var rect = opts.rect;\n      var width = rect.width;\n      var height = rect.height;\n      var center = [rect.x + width / 2, rect.y + height / 2]; // let scale = opts.scale || 1;\n\n      var gravity = opts.gravity == null ? 0.1 : opts.gravity; // for (let i = 0; i < edges.length; i++) {\n      //     let e = edges[i];\n      //     let n1 = e.n1;\n      //     let n2 = e.n2;\n      //     n1.edges = n1.edges || [];\n      //     n2.edges = n2.edges || [];\n      //     n1.edges.push(e);\n      //     n2.edges.push(e);\n      // }\n      // Init position\n\n      for (var i = 0; i < nodes.length; i++) {\n        var n = nodes[i];\n\n        if (!n.p) {\n          n.p = create(width * (Math.random() - 0.5) + center[0], height * (Math.random() - 0.5) + center[1]);\n        }\n\n        n.pp = clone$1(n.p);\n        n.edges = null;\n      } // Formula in 'Graph Drawing by Force-directed Placement'\n      // let k = scale * Math.sqrt(width * height / nodes.length);\n      // let k2 = k * k;\n\n\n      var initialFriction = opts.friction == null ? 0.6 : opts.friction;\n      var friction = initialFriction;\n      var beforeStepCallback;\n      var afterStepCallback;\n      return {\n        warmUp: function () {\n          friction = initialFriction * 0.8;\n        },\n        setFixed: function (idx) {\n          nodes[idx].fixed = true;\n        },\n        setUnfixed: function (idx) {\n          nodes[idx].fixed = false;\n        },\n\n        /**\n         * Before step hook\n         */\n        beforeStep: function (cb) {\n          beforeStepCallback = cb;\n        },\n\n        /**\n         * After step hook\n         */\n        afterStep: function (cb) {\n          afterStepCallback = cb;\n        },\n\n        /**\n         * Some formulas were originally copied from \"d3.js\"\n         * https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/layout/force.js\n         * with some modifications made for this project.\n         * See the license statement at the head of this file.\n         */\n        step: function (cb) {\n          beforeStepCallback && beforeStepCallback(nodes, edges);\n          var v12 = [];\n          var nLen = nodes.length;\n\n          for (var i = 0; i < edges.length; i++) {\n            var e = edges[i];\n\n            if (e.ignoreForceLayout) {\n              continue;\n            }\n\n            var n1 = e.n1;\n            var n2 = e.n2;\n            sub(v12, n2.p, n1.p);\n            var d = len(v12) - e.d;\n            var w = n2.w / (n1.w + n2.w);\n\n            if (isNaN(w)) {\n              w = 0;\n            }\n\n            normalize(v12, v12);\n            !n1.fixed && scaleAndAdd$1(n1.p, n1.p, v12, w * d * friction);\n            !n2.fixed && scaleAndAdd$1(n2.p, n2.p, v12, -(1 - w) * d * friction);\n          } // Gravity\n\n\n          for (var i = 0; i < nLen; i++) {\n            var n = nodes[i];\n\n            if (!n.fixed) {\n              sub(v12, center, n.p); // let d = vec2.len(v12);\n              // vec2.scale(v12, v12, 1 / d);\n              // let gravityFactor = gravity;\n\n              scaleAndAdd$1(n.p, n.p, v12, gravity * friction);\n            }\n          } // Repulsive\n          // PENDING\n\n\n          for (var i = 0; i < nLen; i++) {\n            var n1 = nodes[i];\n\n            for (var j = i + 1; j < nLen; j++) {\n              var n2 = nodes[j];\n              sub(v12, n2.p, n1.p);\n              var d = len(v12);\n\n              if (d === 0) {\n                // Random repulse\n                set(v12, Math.random() - 0.5, Math.random() - 0.5);\n                d = 1;\n              }\n\n              var repFact = (n1.rep + n2.rep) / d / d;\n              !n1.fixed && scaleAndAdd$1(n1.pp, n1.pp, v12, repFact);\n              !n2.fixed && scaleAndAdd$1(n2.pp, n2.pp, v12, -repFact);\n            }\n          }\n\n          var v = [];\n\n          for (var i = 0; i < nLen; i++) {\n            var n = nodes[i];\n\n            if (!n.fixed) {\n              sub(v, n.p, n.pp);\n              scaleAndAdd$1(n.p, n.p, v, friction);\n              copy(n.pp, n.p);\n            }\n          }\n\n          friction = friction * 0.992;\n          var finished = friction < 0.01;\n          afterStepCallback && afterStepCallback(nodes, edges, finished);\n          cb && cb(finished);\n        }\n      };\n    }\n\n    function graphForceLayout(ecModel) {\n      ecModel.eachSeriesByType('graph', function (graphSeries) {\n        var coordSys = graphSeries.coordinateSystem;\n\n        if (coordSys && coordSys.type !== 'view') {\n          return;\n        }\n\n        if (graphSeries.get('layout') === 'force') {\n          var preservedPoints_1 = graphSeries.preservedPoints || {};\n          var graph_1 = graphSeries.getGraph();\n          var nodeData_1 = graph_1.data;\n          var edgeData = graph_1.edgeData;\n          var forceModel = graphSeries.getModel('force');\n          var initLayout = forceModel.get('initLayout');\n\n          if (graphSeries.preservedPoints) {\n            nodeData_1.each(function (idx) {\n              var id = nodeData_1.getId(idx);\n              nodeData_1.setItemLayout(idx, preservedPoints_1[id] || [NaN, NaN]);\n            });\n          } else if (!initLayout || initLayout === 'none') {\n            simpleLayout(graphSeries);\n          } else if (initLayout === 'circular') {\n            circularLayout(graphSeries, 'value');\n          }\n\n          var nodeDataExtent_1 = nodeData_1.getDataExtent('value');\n          var edgeDataExtent_1 = edgeData.getDataExtent('value'); // let edgeDataExtent = edgeData.getDataExtent('value');\n\n          var repulsion = forceModel.get('repulsion');\n          var edgeLength = forceModel.get('edgeLength');\n          var repulsionArr_1 = isArray(repulsion) ? repulsion : [repulsion, repulsion];\n          var edgeLengthArr_1 = isArray(edgeLength) ? edgeLength : [edgeLength, edgeLength]; // Larger value has smaller length\n\n          edgeLengthArr_1 = [edgeLengthArr_1[1], edgeLengthArr_1[0]];\n          var nodes_1 = nodeData_1.mapArray('value', function (value, idx) {\n            var point = nodeData_1.getItemLayout(idx);\n            var rep = linearMap(value, nodeDataExtent_1, repulsionArr_1);\n\n            if (isNaN(rep)) {\n              rep = (repulsionArr_1[0] + repulsionArr_1[1]) / 2;\n            }\n\n            return {\n              w: rep,\n              rep: rep,\n              fixed: nodeData_1.getItemModel(idx).get('fixed'),\n              p: !point || isNaN(point[0]) || isNaN(point[1]) ? null : point\n            };\n          });\n          var edges = edgeData.mapArray('value', function (value, idx) {\n            var edge = graph_1.getEdgeByIndex(idx);\n            var d = linearMap(value, edgeDataExtent_1, edgeLengthArr_1);\n\n            if (isNaN(d)) {\n              d = (edgeLengthArr_1[0] + edgeLengthArr_1[1]) / 2;\n            }\n\n            var edgeModel = edge.getModel();\n            var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), -getCurvenessForEdge(edge, graphSeries, idx, true), 0);\n            return {\n              n1: nodes_1[edge.node1.dataIndex],\n              n2: nodes_1[edge.node2.dataIndex],\n              d: d,\n              curveness: curveness,\n              ignoreForceLayout: edgeModel.get('ignoreForceLayout')\n            };\n          }); // let coordSys = graphSeries.coordinateSystem;\n\n          var rect = coordSys.getBoundingRect();\n          var forceInstance = forceLayout(nodes_1, edges, {\n            rect: rect,\n            gravity: forceModel.get('gravity'),\n            friction: forceModel.get('friction')\n          });\n          forceInstance.beforeStep(function (nodes, edges) {\n            for (var i = 0, l = nodes.length; i < l; i++) {\n              if (nodes[i].fixed) {\n                // Write back to layout instance\n                copy(nodes[i].p, graph_1.getNodeByIndex(i).getLayout());\n              }\n            }\n          });\n          forceInstance.afterStep(function (nodes, edges, stopped) {\n            for (var i = 0, l = nodes.length; i < l; i++) {\n              if (!nodes[i].fixed) {\n                graph_1.getNodeByIndex(i).setLayout(nodes[i].p);\n              }\n\n              preservedPoints_1[nodeData_1.getId(i)] = nodes[i].p;\n            }\n\n            for (var i = 0, l = edges.length; i < l; i++) {\n              var e = edges[i];\n              var edge = graph_1.getEdgeByIndex(i);\n              var p1 = e.n1.p;\n              var p2 = e.n2.p;\n              var points = edge.getLayout();\n              points = points ? points.slice() : [];\n              points[0] = points[0] || [];\n              points[1] = points[1] || [];\n              copy(points[0], p1);\n              copy(points[1], p2);\n\n              if (+e.curveness) {\n                points[2] = [(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness, (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness];\n              }\n\n              edge.setLayout(points);\n            }\n          });\n          graphSeries.forceLayout = forceInstance;\n          graphSeries.preservedPoints = preservedPoints_1; // Step to get the layout\n\n          forceInstance.step();\n        } else {\n          // Remove prev injected forceLayout instance\n          graphSeries.forceLayout = null;\n        }\n      });\n    }\n\n    function getViewRect$2(seriesModel, api, aspect) {\n      var option = extend(seriesModel.getBoxLayoutParams(), {\n        aspect: aspect\n      });\n      return getLayoutRect(option, {\n        width: api.getWidth(),\n        height: api.getHeight()\n      });\n    }\n\n    function createViewCoordSys(ecModel, api) {\n      var viewList = [];\n      ecModel.eachSeriesByType('graph', function (seriesModel) {\n        var coordSysType = seriesModel.get('coordinateSystem');\n\n        if (!coordSysType || coordSysType === 'view') {\n          var data_1 = seriesModel.getData();\n          var positions = data_1.mapArray(function (idx) {\n            var itemModel = data_1.getItemModel(idx);\n            return [+itemModel.get('x'), +itemModel.get('y')];\n          });\n          var min = [];\n          var max = [];\n          fromPoints(positions, min, max); // If width or height is 0\n\n          if (max[0] - min[0] === 0) {\n            max[0] += 1;\n            min[0] -= 1;\n          }\n\n          if (max[1] - min[1] === 0) {\n            max[1] += 1;\n            min[1] -= 1;\n          }\n\n          var aspect = (max[0] - min[0]) / (max[1] - min[1]); // FIXME If get view rect after data processed?\n\n          var viewRect = getViewRect$2(seriesModel, api, aspect); // Position may be NaN, use view rect instead\n\n          if (isNaN(aspect)) {\n            min = [viewRect.x, viewRect.y];\n            max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height];\n          }\n\n          var bbWidth = max[0] - min[0];\n          var bbHeight = max[1] - min[1];\n          var viewWidth = viewRect.width;\n          var viewHeight = viewRect.height;\n          var viewCoordSys = seriesModel.coordinateSystem = new View();\n          viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');\n          viewCoordSys.setBoundingRect(min[0], min[1], bbWidth, bbHeight);\n          viewCoordSys.setViewRect(viewRect.x, viewRect.y, viewWidth, viewHeight); // Update roam info\n\n          viewCoordSys.setCenter(seriesModel.get('center'), api);\n          viewCoordSys.setZoom(seriesModel.get('zoom'));\n          viewList.push(viewCoordSys);\n        }\n      });\n      return viewList;\n    }\n\n    var straightLineProto = Line.prototype;\n    var bezierCurveProto = BezierCurve.prototype;\n\n    var StraightLineShape =\n    /** @class */\n    function () {\n      function StraightLineShape() {\n        // Start point\n        this.x1 = 0;\n        this.y1 = 0; // End point\n\n        this.x2 = 0;\n        this.y2 = 0;\n        this.percent = 1;\n      }\n\n      return StraightLineShape;\n    }();\n\n    var CurveShape =\n    /** @class */\n    function (_super) {\n      __extends(CurveShape, _super);\n\n      function CurveShape() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return CurveShape;\n    }(StraightLineShape);\n\n    function isStraightLine(shape) {\n      return isNaN(+shape.cpx1) || isNaN(+shape.cpy1);\n    }\n\n    var ECLinePath =\n    /** @class */\n    function (_super) {\n      __extends(ECLinePath, _super);\n\n      function ECLinePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-line';\n        return _this;\n      }\n\n      ECLinePath.prototype.getDefaultStyle = function () {\n        return {\n          stroke: '#000',\n          fill: null\n        };\n      };\n\n      ECLinePath.prototype.getDefaultShape = function () {\n        return new StraightLineShape();\n      };\n\n      ECLinePath.prototype.buildPath = function (ctx, shape) {\n        if (isStraightLine(shape)) {\n          straightLineProto.buildPath.call(this, ctx, shape);\n        } else {\n          bezierCurveProto.buildPath.call(this, ctx, shape);\n        }\n      };\n\n      ECLinePath.prototype.pointAt = function (t) {\n        if (isStraightLine(this.shape)) {\n          return straightLineProto.pointAt.call(this, t);\n        } else {\n          return bezierCurveProto.pointAt.call(this, t);\n        }\n      };\n\n      ECLinePath.prototype.tangentAt = function (t) {\n        var shape = this.shape;\n        var p = isStraightLine(shape) ? [shape.x2 - shape.x1, shape.y2 - shape.y1] : bezierCurveProto.tangentAt.call(this, t);\n        return normalize(p, p);\n      };\n\n      return ECLinePath;\n    }(Path);\n\n    var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];\n\n    function makeSymbolTypeKey(symbolCategory) {\n      return '_' + symbolCategory + 'Type';\n    }\n    /**\n     * @inner\n     */\n\n\n    function createSymbol$1(name, lineData, idx) {\n      var symbolType = lineData.getItemVisual(idx, name);\n\n      if (!symbolType || symbolType === 'none') {\n        return;\n      }\n\n      var symbolSize = lineData.getItemVisual(idx, name + 'Size');\n      var symbolRotate = lineData.getItemVisual(idx, name + 'Rotate');\n      var symbolOffset = lineData.getItemVisual(idx, name + 'Offset');\n      var symbolKeepAspect = lineData.getItemVisual(idx, name + 'KeepAspect');\n      var symbolSizeArr = normalizeSymbolSize(symbolSize);\n      var symbolOffsetArr = normalizeSymbolOffset(symbolOffset || 0, symbolSizeArr);\n      var symbolPath = createSymbol(symbolType, -symbolSizeArr[0] / 2 + symbolOffsetArr[0], -symbolSizeArr[1] / 2 + symbolOffsetArr[1], symbolSizeArr[0], symbolSizeArr[1], null, symbolKeepAspect);\n      symbolPath.__specifiedRotation = symbolRotate == null || isNaN(symbolRotate) ? void 0 : +symbolRotate * Math.PI / 180 || 0;\n      symbolPath.name = name;\n      return symbolPath;\n    }\n\n    function createLine(points) {\n      var line = new ECLinePath({\n        name: 'line',\n        subPixelOptimize: true\n      });\n      setLinePoints(line.shape, points);\n      return line;\n    }\n\n    function setLinePoints(targetShape, points) {\n      targetShape.x1 = points[0][0];\n      targetShape.y1 = points[0][1];\n      targetShape.x2 = points[1][0];\n      targetShape.y2 = points[1][1];\n      targetShape.percent = 1;\n      var cp1 = points[2];\n\n      if (cp1) {\n        targetShape.cpx1 = cp1[0];\n        targetShape.cpy1 = cp1[1];\n      } else {\n        targetShape.cpx1 = NaN;\n        targetShape.cpy1 = NaN;\n      }\n    }\n\n    var Line$1 =\n    /** @class */\n    function (_super) {\n      __extends(Line, _super);\n\n      function Line(lineData, idx, seriesScope) {\n        var _this = _super.call(this) || this;\n\n        _this._createLine(lineData, idx, seriesScope);\n\n        return _this;\n      }\n\n      Line.prototype._createLine = function (lineData, idx, seriesScope) {\n        var seriesModel = lineData.hostModel;\n        var linePoints = lineData.getItemLayout(idx);\n        var line = createLine(linePoints);\n        line.shape.percent = 0;\n        initProps(line, {\n          shape: {\n            percent: 1\n          }\n        }, seriesModel, idx);\n        this.add(line);\n        each(SYMBOL_CATEGORIES, function (symbolCategory) {\n          var symbol = createSymbol$1(symbolCategory, lineData, idx); // symbols must added after line to make sure\n          // it will be updated after line#update.\n          // Or symbol position and rotation update in line#beforeUpdate will be one frame slow\n\n          this.add(symbol);\n          this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);\n        }, this);\n\n        this._updateCommonStl(lineData, idx, seriesScope);\n      }; // TODO More strict on the List type in parameters?\n\n\n      Line.prototype.updateData = function (lineData, idx, seriesScope) {\n        var seriesModel = lineData.hostModel;\n        var line = this.childOfName('line');\n        var linePoints = lineData.getItemLayout(idx);\n        var target = {\n          shape: {}\n        };\n        setLinePoints(target.shape, linePoints);\n        updateProps(line, target, seriesModel, idx);\n        each(SYMBOL_CATEGORIES, function (symbolCategory) {\n          var symbolType = lineData.getItemVisual(idx, symbolCategory);\n          var key = makeSymbolTypeKey(symbolCategory); // Symbol changed\n\n          if (this[key] !== symbolType) {\n            this.remove(this.childOfName(symbolCategory));\n            var symbol = createSymbol$1(symbolCategory, lineData, idx);\n            this.add(symbol);\n          }\n\n          this[key] = symbolType;\n        }, this);\n\n        this._updateCommonStl(lineData, idx, seriesScope);\n      };\n\n      Line.prototype.getLinePath = function () {\n        return this.childAt(0);\n      };\n\n      Line.prototype._updateCommonStl = function (lineData, idx, seriesScope) {\n        var seriesModel = lineData.hostModel;\n        var line = this.childOfName('line');\n        var emphasisLineStyle = seriesScope && seriesScope.emphasisLineStyle;\n        var blurLineStyle = seriesScope && seriesScope.blurLineStyle;\n        var selectLineStyle = seriesScope && seriesScope.selectLineStyle;\n        var labelStatesModels = seriesScope && seriesScope.labelStatesModels;\n        var emphasisDisabled = seriesScope && seriesScope.emphasisDisabled;\n        var focus = seriesScope && seriesScope.focus;\n        var blurScope = seriesScope && seriesScope.blurScope; // Optimization for large dataset\n\n        if (!seriesScope || lineData.hasItemOption) {\n          var itemModel = lineData.getItemModel(idx);\n          var emphasisModel = itemModel.getModel('emphasis');\n          emphasisLineStyle = emphasisModel.getModel('lineStyle').getLineStyle();\n          blurLineStyle = itemModel.getModel(['blur', 'lineStyle']).getLineStyle();\n          selectLineStyle = itemModel.getModel(['select', 'lineStyle']).getLineStyle();\n          emphasisDisabled = emphasisModel.get('disabled');\n          focus = emphasisModel.get('focus');\n          blurScope = emphasisModel.get('blurScope');\n          labelStatesModels = getLabelStatesModels(itemModel);\n        }\n\n        var lineStyle = lineData.getItemVisual(idx, 'style');\n        var visualColor = lineStyle.stroke;\n        line.useStyle(lineStyle);\n        line.style.fill = null;\n        line.style.strokeNoScale = true;\n        line.ensureState('emphasis').style = emphasisLineStyle;\n        line.ensureState('blur').style = blurLineStyle;\n        line.ensureState('select').style = selectLineStyle; // Update symbol\n\n        each(SYMBOL_CATEGORIES, function (symbolCategory) {\n          var symbol = this.childOfName(symbolCategory);\n\n          if (symbol) {\n            // Share opacity and color with line.\n            symbol.setColor(visualColor);\n            symbol.style.opacity = lineStyle.opacity;\n\n            for (var i = 0; i < SPECIAL_STATES.length; i++) {\n              var stateName = SPECIAL_STATES[i];\n              var lineState = line.getState(stateName);\n\n              if (lineState) {\n                var lineStateStyle = lineState.style || {};\n                var state = symbol.ensureState(stateName);\n                var stateStyle = state.style || (state.style = {});\n\n                if (lineStateStyle.stroke != null) {\n                  stateStyle[symbol.__isEmptyBrush ? 'stroke' : 'fill'] = lineStateStyle.stroke;\n                }\n\n                if (lineStateStyle.opacity != null) {\n                  stateStyle.opacity = lineStateStyle.opacity;\n                }\n              }\n            }\n\n            symbol.markRedraw();\n          }\n        }, this);\n        var rawVal = seriesModel.getRawValue(idx);\n        setLabelStyle(this, labelStatesModels, {\n          labelDataIndex: idx,\n          labelFetcher: {\n            getFormattedLabel: function (dataIndex, stateName) {\n              return seriesModel.getFormattedLabel(dataIndex, stateName, lineData.dataType);\n            }\n          },\n          inheritColor: visualColor || '#000',\n          defaultOpacity: lineStyle.opacity,\n          defaultText: (rawVal == null ? lineData.getName(idx) : isFinite(rawVal) ? round(rawVal) : rawVal) + ''\n        });\n        var label = this.getTextContent(); // Always set `textStyle` even if `normalStyle.text` is null, because default\n        // values have to be set on `normalStyle`.\n\n        if (label) {\n          var labelNormalModel = labelStatesModels.normal;\n          label.__align = label.style.align;\n          label.__verticalAlign = label.style.verticalAlign; // 'start', 'middle', 'end'\n\n          label.__position = labelNormalModel.get('position') || 'middle';\n          var distance = labelNormalModel.get('distance');\n\n          if (!isArray(distance)) {\n            distance = [distance, distance];\n          }\n\n          label.__labelDistance = distance;\n        }\n\n        this.setTextConfig({\n          position: null,\n          local: true,\n          inside: false // Can't be inside for stroke element.\n\n        });\n        toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n      };\n\n      Line.prototype.highlight = function () {\n        enterEmphasis(this);\n      };\n\n      Line.prototype.downplay = function () {\n        leaveEmphasis(this);\n      };\n\n      Line.prototype.updateLayout = function (lineData, idx) {\n        this.setLinePoints(lineData.getItemLayout(idx));\n      };\n\n      Line.prototype.setLinePoints = function (points) {\n        var linePath = this.childOfName('line');\n        setLinePoints(linePath.shape, points);\n        linePath.dirty();\n      };\n\n      Line.prototype.beforeUpdate = function () {\n        var lineGroup = this;\n        var symbolFrom = lineGroup.childOfName('fromSymbol');\n        var symbolTo = lineGroup.childOfName('toSymbol');\n        var label = lineGroup.getTextContent(); // Quick reject\n\n        if (!symbolFrom && !symbolTo && (!label || label.ignore)) {\n          return;\n        }\n\n        var invScale = 1;\n        var parentNode = this.parent;\n\n        while (parentNode) {\n          if (parentNode.scaleX) {\n            invScale /= parentNode.scaleX;\n          }\n\n          parentNode = parentNode.parent;\n        }\n\n        var line = lineGroup.childOfName('line'); // If line not changed\n        // FIXME Parent scale changed\n\n        if (!this.__dirty && !line.__dirty) {\n          return;\n        }\n\n        var percent = line.shape.percent;\n        var fromPos = line.pointAt(0);\n        var toPos = line.pointAt(percent);\n        var d = sub([], toPos, fromPos);\n        normalize(d, d);\n\n        function setSymbolRotation(symbol, percent) {\n          // Fix #12388\n          // when symbol is set to be 'arrow' in markLine,\n          // symbolRotate value will be ignored, and compulsively use tangent angle.\n          // rotate by default if symbol rotation is not specified\n          var specifiedRotation = symbol.__specifiedRotation;\n\n          if (specifiedRotation == null) {\n            var tangent = line.tangentAt(percent);\n            symbol.attr('rotation', (percent === 1 ? -1 : 1) * Math.PI / 2 - Math.atan2(tangent[1], tangent[0]));\n          } else {\n            symbol.attr('rotation', specifiedRotation);\n          }\n        }\n\n        if (symbolFrom) {\n          symbolFrom.setPosition(fromPos);\n          setSymbolRotation(symbolFrom, 0);\n          symbolFrom.scaleX = symbolFrom.scaleY = invScale * percent;\n          symbolFrom.markRedraw();\n        }\n\n        if (symbolTo) {\n          symbolTo.setPosition(toPos);\n          setSymbolRotation(symbolTo, 1);\n          symbolTo.scaleX = symbolTo.scaleY = invScale * percent;\n          symbolTo.markRedraw();\n        }\n\n        if (label && !label.ignore) {\n          label.x = label.y = 0;\n          label.originX = label.originY = 0;\n          var textAlign = void 0;\n          var textVerticalAlign = void 0;\n          var distance = label.__labelDistance;\n          var distanceX = distance[0] * invScale;\n          var distanceY = distance[1] * invScale;\n          var halfPercent = percent / 2;\n          var tangent = line.tangentAt(halfPercent);\n          var n = [tangent[1], -tangent[0]];\n          var cp = line.pointAt(halfPercent);\n\n          if (n[1] > 0) {\n            n[0] = -n[0];\n            n[1] = -n[1];\n          }\n\n          var dir = tangent[0] < 0 ? -1 : 1;\n\n          if (label.__position !== 'start' && label.__position !== 'end') {\n            var rotation = -Math.atan2(tangent[1], tangent[0]);\n\n            if (toPos[0] < fromPos[0]) {\n              rotation = Math.PI + rotation;\n            }\n\n            label.rotation = rotation;\n          }\n\n          var dy = void 0;\n\n          switch (label.__position) {\n            case 'insideStartTop':\n            case 'insideMiddleTop':\n            case 'insideEndTop':\n            case 'middle':\n              dy = -distanceY;\n              textVerticalAlign = 'bottom';\n              break;\n\n            case 'insideStartBottom':\n            case 'insideMiddleBottom':\n            case 'insideEndBottom':\n              dy = distanceY;\n              textVerticalAlign = 'top';\n              break;\n\n            default:\n              dy = 0;\n              textVerticalAlign = 'middle';\n          }\n\n          switch (label.__position) {\n            case 'end':\n              label.x = d[0] * distanceX + toPos[0];\n              label.y = d[1] * distanceY + toPos[1];\n              textAlign = d[0] > 0.8 ? 'left' : d[0] < -0.8 ? 'right' : 'center';\n              textVerticalAlign = d[1] > 0.8 ? 'top' : d[1] < -0.8 ? 'bottom' : 'middle';\n              break;\n\n            case 'start':\n              label.x = -d[0] * distanceX + fromPos[0];\n              label.y = -d[1] * distanceY + fromPos[1];\n              textAlign = d[0] > 0.8 ? 'right' : d[0] < -0.8 ? 'left' : 'center';\n              textVerticalAlign = d[1] > 0.8 ? 'bottom' : d[1] < -0.8 ? 'top' : 'middle';\n              break;\n\n            case 'insideStartTop':\n            case 'insideStart':\n            case 'insideStartBottom':\n              label.x = distanceX * dir + fromPos[0];\n              label.y = fromPos[1] + dy;\n              textAlign = tangent[0] < 0 ? 'right' : 'left';\n              label.originX = -distanceX * dir;\n              label.originY = -dy;\n              break;\n\n            case 'insideMiddleTop':\n            case 'insideMiddle':\n            case 'insideMiddleBottom':\n            case 'middle':\n              label.x = cp[0];\n              label.y = cp[1] + dy;\n              textAlign = 'center';\n              label.originY = -dy;\n              break;\n\n            case 'insideEndTop':\n            case 'insideEnd':\n            case 'insideEndBottom':\n              label.x = -distanceX * dir + toPos[0];\n              label.y = toPos[1] + dy;\n              textAlign = tangent[0] >= 0 ? 'right' : 'left';\n              label.originX = distanceX * dir;\n              label.originY = -dy;\n              break;\n          }\n\n          label.scaleX = label.scaleY = invScale;\n          label.setStyle({\n            // Use the user specified text align and baseline first\n            verticalAlign: label.__verticalAlign || textVerticalAlign,\n            align: label.__align || textAlign\n          });\n        }\n      };\n\n      return Line;\n    }(Group);\n\n    var LineDraw =\n    /** @class */\n    function () {\n      function LineDraw(LineCtor) {\n        this.group = new Group();\n        this._LineCtor = LineCtor || Line$1;\n      }\n\n      LineDraw.prototype.updateData = function (lineData) {\n        var _this = this; // Remove progressive els.\n\n\n        this._progressiveEls = null;\n        var lineDraw = this;\n        var group = lineDraw.group;\n        var oldLineData = lineDraw._lineData;\n        lineDraw._lineData = lineData; // There is no oldLineData only when first rendering or switching from\n        // stream mode to normal mode, where previous elements should be removed.\n\n        if (!oldLineData) {\n          group.removeAll();\n        }\n\n        var seriesScope = makeSeriesScope$1(lineData);\n        lineData.diff(oldLineData).add(function (idx) {\n          _this._doAdd(lineData, idx, seriesScope);\n        }).update(function (newIdx, oldIdx) {\n          _this._doUpdate(oldLineData, lineData, oldIdx, newIdx, seriesScope);\n        }).remove(function (idx) {\n          group.remove(oldLineData.getItemGraphicEl(idx));\n        }).execute();\n      };\n\n      LineDraw.prototype.updateLayout = function () {\n        var lineData = this._lineData; // Do not support update layout in incremental mode.\n\n        if (!lineData) {\n          return;\n        }\n\n        lineData.eachItemGraphicEl(function (el, idx) {\n          el.updateLayout(lineData, idx);\n        }, this);\n      };\n\n      LineDraw.prototype.incrementalPrepareUpdate = function (lineData) {\n        this._seriesScope = makeSeriesScope$1(lineData);\n        this._lineData = null;\n        this.group.removeAll();\n      };\n\n      LineDraw.prototype.incrementalUpdate = function (taskParams, lineData) {\n        this._progressiveEls = [];\n\n        function updateIncrementalAndHover(el) {\n          if (!el.isGroup && !isEffectObject(el)) {\n            el.incremental = true;\n            el.ensureState('emphasis').hoverLayer = true;\n          }\n        }\n\n        for (var idx = taskParams.start; idx < taskParams.end; idx++) {\n          var itemLayout = lineData.getItemLayout(idx);\n\n          if (lineNeedsDraw(itemLayout)) {\n            var el = new this._LineCtor(lineData, idx, this._seriesScope);\n            el.traverse(updateIncrementalAndHover);\n            this.group.add(el);\n            lineData.setItemGraphicEl(idx, el);\n\n            this._progressiveEls.push(el);\n          }\n        }\n      };\n\n      LineDraw.prototype.remove = function () {\n        this.group.removeAll();\n      };\n\n      LineDraw.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      LineDraw.prototype._doAdd = function (lineData, idx, seriesScope) {\n        var itemLayout = lineData.getItemLayout(idx);\n\n        if (!lineNeedsDraw(itemLayout)) {\n          return;\n        }\n\n        var el = new this._LineCtor(lineData, idx, seriesScope);\n        lineData.setItemGraphicEl(idx, el);\n        this.group.add(el);\n      };\n\n      LineDraw.prototype._doUpdate = function (oldLineData, newLineData, oldIdx, newIdx, seriesScope) {\n        var itemEl = oldLineData.getItemGraphicEl(oldIdx);\n\n        if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {\n          this.group.remove(itemEl);\n          return;\n        }\n\n        if (!itemEl) {\n          itemEl = new this._LineCtor(newLineData, newIdx, seriesScope);\n        } else {\n          itemEl.updateData(newLineData, newIdx, seriesScope);\n        }\n\n        newLineData.setItemGraphicEl(newIdx, itemEl);\n        this.group.add(itemEl);\n      };\n\n      return LineDraw;\n    }();\n\n    function isEffectObject(el) {\n      return el.animators && el.animators.length > 0;\n    }\n\n    function makeSeriesScope$1(lineData) {\n      var hostModel = lineData.hostModel;\n      var emphasisModel = hostModel.getModel('emphasis');\n      return {\n        lineStyle: hostModel.getModel('lineStyle').getLineStyle(),\n        emphasisLineStyle: emphasisModel.getModel(['lineStyle']).getLineStyle(),\n        blurLineStyle: hostModel.getModel(['blur', 'lineStyle']).getLineStyle(),\n        selectLineStyle: hostModel.getModel(['select', 'lineStyle']).getLineStyle(),\n        emphasisDisabled: emphasisModel.get('disabled'),\n        blurScope: emphasisModel.get('blurScope'),\n        focus: emphasisModel.get('focus'),\n        labelStatesModels: getLabelStatesModels(hostModel)\n      };\n    }\n\n    function isPointNaN(pt) {\n      return isNaN(pt[0]) || isNaN(pt[1]);\n    }\n\n    function lineNeedsDraw(pts) {\n      return pts && !isPointNaN(pts[0]) && !isPointNaN(pts[1]);\n    }\n\n    var v1 = [];\n    var v2 = [];\n    var v3 = [];\n    var quadraticAt$1 = quadraticAt;\n    var v2DistSquare = distSquare;\n    var mathAbs$2 = Math.abs;\n\n    function intersectCurveCircle(curvePoints, center, radius) {\n      var p0 = curvePoints[0];\n      var p1 = curvePoints[1];\n      var p2 = curvePoints[2];\n      var d = Infinity;\n      var t;\n      var radiusSquare = radius * radius;\n      var interval = 0.1;\n\n      for (var _t = 0.1; _t <= 0.9; _t += 0.1) {\n        v1[0] = quadraticAt$1(p0[0], p1[0], p2[0], _t);\n        v1[1] = quadraticAt$1(p0[1], p1[1], p2[1], _t);\n        var diff = mathAbs$2(v2DistSquare(v1, center) - radiusSquare);\n\n        if (diff < d) {\n          d = diff;\n          t = _t;\n        }\n      } // Assume the segment is monotone，Find root through Bisection method\n      // At most 32 iteration\n\n\n      for (var i = 0; i < 32; i++) {\n        // let prev = t - interval;\n        var next = t + interval; // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev);\n        // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev);\n\n        v2[0] = quadraticAt$1(p0[0], p1[0], p2[0], t);\n        v2[1] = quadraticAt$1(p0[1], p1[1], p2[1], t);\n        v3[0] = quadraticAt$1(p0[0], p1[0], p2[0], next);\n        v3[1] = quadraticAt$1(p0[1], p1[1], p2[1], next);\n        var diff = v2DistSquare(v2, center) - radiusSquare;\n\n        if (mathAbs$2(diff) < 1e-2) {\n          break;\n        } // let prevDiff = v2DistSquare(v1, center) - radiusSquare;\n\n\n        var nextDiff = v2DistSquare(v3, center) - radiusSquare;\n        interval /= 2;\n\n        if (diff < 0) {\n          if (nextDiff >= 0) {\n            t = t + interval;\n          } else {\n            t = t - interval;\n          }\n        } else {\n          if (nextDiff >= 0) {\n            t = t - interval;\n          } else {\n            t = t + interval;\n          }\n        }\n      }\n\n      return t;\n    } // Adjust edge to avoid\n\n\n    function adjustEdge(graph, scale) {\n      var tmp0 = [];\n      var quadraticSubdivide$1 = quadraticSubdivide;\n      var pts = [[], [], []];\n      var pts2 = [[], []];\n      var v = [];\n      scale /= 2;\n      graph.eachEdge(function (edge, idx) {\n        var linePoints = edge.getLayout();\n        var fromSymbol = edge.getVisual('fromSymbol');\n        var toSymbol = edge.getVisual('toSymbol');\n\n        if (!linePoints.__original) {\n          linePoints.__original = [clone$1(linePoints[0]), clone$1(linePoints[1])];\n\n          if (linePoints[2]) {\n            linePoints.__original.push(clone$1(linePoints[2]));\n          }\n        }\n\n        var originalPoints = linePoints.__original; // Quadratic curve\n\n        if (linePoints[2] != null) {\n          copy(pts[0], originalPoints[0]);\n          copy(pts[1], originalPoints[2]);\n          copy(pts[2], originalPoints[1]);\n\n          if (fromSymbol && fromSymbol !== 'none') {\n            var symbolSize = getSymbolSize(edge.node1);\n            var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale); // Subdivide and get the second\n\n            quadraticSubdivide$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0);\n            pts[0][0] = tmp0[3];\n            pts[1][0] = tmp0[4];\n            quadraticSubdivide$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0);\n            pts[0][1] = tmp0[3];\n            pts[1][1] = tmp0[4];\n          }\n\n          if (toSymbol && toSymbol !== 'none') {\n            var symbolSize = getSymbolSize(edge.node2);\n            var t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale); // Subdivide and get the first\n\n            quadraticSubdivide$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0);\n            pts[1][0] = tmp0[1];\n            pts[2][0] = tmp0[2];\n            quadraticSubdivide$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0);\n            pts[1][1] = tmp0[1];\n            pts[2][1] = tmp0[2];\n          } // Copy back to layout\n\n\n          copy(linePoints[0], pts[0]);\n          copy(linePoints[1], pts[2]);\n          copy(linePoints[2], pts[1]);\n        } // Line\n        else {\n            copy(pts2[0], originalPoints[0]);\n            copy(pts2[1], originalPoints[1]);\n            sub(v, pts2[1], pts2[0]);\n            normalize(v, v);\n\n            if (fromSymbol && fromSymbol !== 'none') {\n              var symbolSize = getSymbolSize(edge.node1);\n              scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale);\n            }\n\n            if (toSymbol && toSymbol !== 'none') {\n              var symbolSize = getSymbolSize(edge.node2);\n              scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale);\n            }\n\n            copy(linePoints[0], pts2[0]);\n            copy(linePoints[1], pts2[1]);\n          }\n      });\n    }\n\n    function isViewCoordSys(coordSys) {\n      return coordSys.type === 'view';\n    }\n\n    var GraphView =\n    /** @class */\n    function (_super) {\n      __extends(GraphView, _super);\n\n      function GraphView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GraphView.type;\n        return _this;\n      }\n\n      GraphView.prototype.init = function (ecModel, api) {\n        var symbolDraw = new SymbolDraw();\n        var lineDraw = new LineDraw();\n        var group = this.group;\n        this._controller = new RoamController(api.getZr());\n        this._controllerHost = {\n          target: group\n        };\n        group.add(symbolDraw.group);\n        group.add(lineDraw.group);\n        this._symbolDraw = symbolDraw;\n        this._lineDraw = lineDraw;\n        this._firstRender = true;\n      };\n\n      GraphView.prototype.render = function (seriesModel, ecModel, api) {\n        var _this = this;\n\n        var coordSys = seriesModel.coordinateSystem;\n        this._model = seriesModel;\n        var symbolDraw = this._symbolDraw;\n        var lineDraw = this._lineDraw;\n        var group = this.group;\n\n        if (isViewCoordSys(coordSys)) {\n          var groupNewProp = {\n            x: coordSys.x,\n            y: coordSys.y,\n            scaleX: coordSys.scaleX,\n            scaleY: coordSys.scaleY\n          };\n\n          if (this._firstRender) {\n            group.attr(groupNewProp);\n          } else {\n            updateProps(group, groupNewProp, seriesModel);\n          }\n        } // Fix edge contact point with node\n\n\n        adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));\n        var data = seriesModel.getData();\n        symbolDraw.updateData(data);\n        var edgeData = seriesModel.getEdgeData(); // TODO: TYPE\n\n        lineDraw.updateData(edgeData);\n\n        this._updateNodeAndLinkScale();\n\n        this._updateController(seriesModel, ecModel, api);\n\n        clearTimeout(this._layoutTimeout);\n        var forceLayout = seriesModel.forceLayout;\n        var layoutAnimation = seriesModel.get(['force', 'layoutAnimation']);\n\n        if (forceLayout) {\n          this._startForceLayoutIteration(forceLayout, layoutAnimation);\n        }\n\n        var layout = seriesModel.get('layout');\n        data.graph.eachNode(function (node) {\n          var idx = node.dataIndex;\n          var el = node.getGraphicEl();\n          var itemModel = node.getModel();\n\n          if (!el) {\n            return;\n          } // Update draggable\n\n\n          el.off('drag').off('dragend');\n          var draggable = itemModel.get('draggable');\n\n          if (draggable) {\n            el.on('drag', function (e) {\n              switch (layout) {\n                case 'force':\n                  forceLayout.warmUp();\n                  !_this._layouting && _this._startForceLayoutIteration(forceLayout, layoutAnimation);\n                  forceLayout.setFixed(idx); // Write position back to layout\n\n                  data.setItemLayout(idx, [el.x, el.y]);\n                  break;\n\n                case 'circular':\n                  data.setItemLayout(idx, [el.x, el.y]); // mark node fixed\n\n                  node.setLayout({\n                    fixed: true\n                  }, true); // recalculate circular layout\n\n                  circularLayout(seriesModel, 'symbolSize', node, [e.offsetX, e.offsetY]);\n\n                  _this.updateLayout(seriesModel);\n\n                  break;\n\n                case 'none':\n                default:\n                  data.setItemLayout(idx, [el.x, el.y]); // update edge\n\n                  simpleLayoutEdge(seriesModel.getGraph(), seriesModel);\n\n                  _this.updateLayout(seriesModel);\n\n                  break;\n              }\n            }).on('dragend', function () {\n              if (forceLayout) {\n                forceLayout.setUnfixed(idx);\n              }\n            });\n          }\n\n          el.setDraggable(draggable, !!itemModel.get('cursor'));\n          var focus = itemModel.get(['emphasis', 'focus']);\n\n          if (focus === 'adjacency') {\n            getECData(el).focus = node.getAdjacentDataIndices();\n          }\n        });\n        data.graph.eachEdge(function (edge) {\n          var el = edge.getGraphicEl();\n          var focus = edge.getModel().get(['emphasis', 'focus']);\n\n          if (!el) {\n            return;\n          }\n\n          if (focus === 'adjacency') {\n            getECData(el).focus = {\n              edge: [edge.dataIndex],\n              node: [edge.node1.dataIndex, edge.node2.dataIndex]\n            };\n          }\n        });\n        var circularRotateLabel = seriesModel.get('layout') === 'circular' && seriesModel.get(['circular', 'rotateLabel']);\n        var cx = data.getLayout('cx');\n        var cy = data.getLayout('cy');\n        data.graph.eachNode(function (node) {\n          rotateNodeLabel(node, circularRotateLabel, cx, cy);\n        });\n        this._firstRender = false;\n      };\n\n      GraphView.prototype.dispose = function () {\n        this._controller && this._controller.dispose();\n        this._controllerHost = null;\n      };\n\n      GraphView.prototype._startForceLayoutIteration = function (forceLayout, layoutAnimation) {\n        var self = this;\n\n        (function step() {\n          forceLayout.step(function (stopped) {\n            self.updateLayout(self._model);\n            (self._layouting = !stopped) && (layoutAnimation ? self._layoutTimeout = setTimeout(step, 16) : step());\n          });\n        })();\n      };\n\n      GraphView.prototype._updateController = function (seriesModel, ecModel, api) {\n        var _this = this;\n\n        var controller = this._controller;\n        var controllerHost = this._controllerHost;\n        var group = this.group;\n        controller.setPointerChecker(function (e, x, y) {\n          var rect = group.getBoundingRect();\n          rect.applyTransform(group.transform);\n          return rect.contain(x, y) && !onIrrelevantElement(e, api, seriesModel);\n        });\n\n        if (!isViewCoordSys(seriesModel.coordinateSystem)) {\n          controller.disable();\n          return;\n        }\n\n        controller.enable(seriesModel.get('roam'));\n        controllerHost.zoomLimit = seriesModel.get('scaleLimit');\n        controllerHost.zoom = seriesModel.coordinateSystem.getZoom();\n        controller.off('pan').off('zoom').on('pan', function (e) {\n          updateViewOnPan(controllerHost, e.dx, e.dy);\n          api.dispatchAction({\n            seriesId: seriesModel.id,\n            type: 'graphRoam',\n            dx: e.dx,\n            dy: e.dy\n          });\n        }).on('zoom', function (e) {\n          updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);\n          api.dispatchAction({\n            seriesId: seriesModel.id,\n            type: 'graphRoam',\n            zoom: e.scale,\n            originX: e.originX,\n            originY: e.originY\n          });\n\n          _this._updateNodeAndLinkScale();\n\n          adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));\n\n          _this._lineDraw.updateLayout(); // Only update label layout on zoom\n\n\n          api.updateLabelLayout();\n        });\n      };\n\n      GraphView.prototype._updateNodeAndLinkScale = function () {\n        var seriesModel = this._model;\n        var data = seriesModel.getData();\n        var nodeScale = getNodeGlobalScale(seriesModel);\n        data.eachItemGraphicEl(function (el, idx) {\n          el && el.setSymbolScale(nodeScale);\n        });\n      };\n\n      GraphView.prototype.updateLayout = function (seriesModel) {\n        adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));\n\n        this._symbolDraw.updateLayout();\n\n        this._lineDraw.updateLayout();\n      };\n\n      GraphView.prototype.remove = function (ecModel, api) {\n        this._symbolDraw && this._symbolDraw.remove();\n        this._lineDraw && this._lineDraw.remove();\n      };\n\n      GraphView.type = 'graph';\n      return GraphView;\n    }(ChartView);\n\n    function generateNodeKey(id) {\n      return '_EC_' + id;\n    }\n\n    var Graph =\n    /** @class */\n    function () {\n      function Graph(directed) {\n        this.type = 'graph';\n        this.nodes = [];\n        this.edges = [];\n        this._nodesMap = {};\n        /**\n         * @type {Object.<string, module:echarts/data/Graph.Edge>}\n         * @private\n         */\n\n        this._edgesMap = {};\n        this._directed = directed || false;\n      }\n      /**\n       * If is directed graph\n       */\n\n\n      Graph.prototype.isDirected = function () {\n        return this._directed;\n      };\n      /**\n       * Add a new node\n       */\n\n      Graph.prototype.addNode = function (id, dataIndex) {\n        id = id == null ? '' + dataIndex : '' + id;\n        var nodesMap = this._nodesMap;\n\n        if (nodesMap[generateNodeKey(id)]) {\n          if (\"development\" !== 'production') {\n            console.error('Graph nodes have duplicate name or id');\n          }\n\n          return;\n        }\n\n        var node = new GraphNode(id, dataIndex);\n        node.hostGraph = this;\n        this.nodes.push(node);\n        nodesMap[generateNodeKey(id)] = node;\n        return node;\n      };\n      /**\n       * Get node by data index\n       */\n\n      Graph.prototype.getNodeByIndex = function (dataIndex) {\n        var rawIdx = this.data.getRawIndex(dataIndex);\n        return this.nodes[rawIdx];\n      };\n      /**\n       * Get node by id\n       */\n\n      Graph.prototype.getNodeById = function (id) {\n        return this._nodesMap[generateNodeKey(id)];\n      };\n      /**\n       * Add a new edge\n       */\n\n      Graph.prototype.addEdge = function (n1, n2, dataIndex) {\n        var nodesMap = this._nodesMap;\n        var edgesMap = this._edgesMap; // PNEDING\n\n        if (isNumber(n1)) {\n          n1 = this.nodes[n1];\n        }\n\n        if (isNumber(n2)) {\n          n2 = this.nodes[n2];\n        }\n\n        if (!(n1 instanceof GraphNode)) {\n          n1 = nodesMap[generateNodeKey(n1)];\n        }\n\n        if (!(n2 instanceof GraphNode)) {\n          n2 = nodesMap[generateNodeKey(n2)];\n        }\n\n        if (!n1 || !n2) {\n          return;\n        }\n\n        var key = n1.id + '-' + n2.id;\n        var edge = new GraphEdge(n1, n2, dataIndex);\n        edge.hostGraph = this;\n\n        if (this._directed) {\n          n1.outEdges.push(edge);\n          n2.inEdges.push(edge);\n        }\n\n        n1.edges.push(edge);\n\n        if (n1 !== n2) {\n          n2.edges.push(edge);\n        }\n\n        this.edges.push(edge);\n        edgesMap[key] = edge;\n        return edge;\n      };\n      /**\n       * Get edge by data index\n       */\n\n      Graph.prototype.getEdgeByIndex = function (dataIndex) {\n        var rawIdx = this.edgeData.getRawIndex(dataIndex);\n        return this.edges[rawIdx];\n      };\n      /**\n       * Get edge by two linked nodes\n       */\n\n      Graph.prototype.getEdge = function (n1, n2) {\n        if (n1 instanceof GraphNode) {\n          n1 = n1.id;\n        }\n\n        if (n2 instanceof GraphNode) {\n          n2 = n2.id;\n        }\n\n        var edgesMap = this._edgesMap;\n\n        if (this._directed) {\n          return edgesMap[n1 + '-' + n2];\n        } else {\n          return edgesMap[n1 + '-' + n2] || edgesMap[n2 + '-' + n1];\n        }\n      };\n      /**\n       * Iterate all nodes\n       */\n\n      Graph.prototype.eachNode = function (cb, context) {\n        var nodes = this.nodes;\n        var len = nodes.length;\n\n        for (var i = 0; i < len; i++) {\n          if (nodes[i].dataIndex >= 0) {\n            cb.call(context, nodes[i], i);\n          }\n        }\n      };\n      /**\n       * Iterate all edges\n       */\n\n      Graph.prototype.eachEdge = function (cb, context) {\n        var edges = this.edges;\n        var len = edges.length;\n\n        for (var i = 0; i < len; i++) {\n          if (edges[i].dataIndex >= 0 && edges[i].node1.dataIndex >= 0 && edges[i].node2.dataIndex >= 0) {\n            cb.call(context, edges[i], i);\n          }\n        }\n      };\n      /**\n       * Breadth first traverse\n       * Return true to stop traversing\n       */\n\n      Graph.prototype.breadthFirstTraverse = function (cb, startNode, direction, context) {\n        if (!(startNode instanceof GraphNode)) {\n          startNode = this._nodesMap[generateNodeKey(startNode)];\n        }\n\n        if (!startNode) {\n          return;\n        }\n\n        var edgeType = direction === 'out' ? 'outEdges' : direction === 'in' ? 'inEdges' : 'edges';\n\n        for (var i = 0; i < this.nodes.length; i++) {\n          this.nodes[i].__visited = false;\n        }\n\n        if (cb.call(context, startNode, null)) {\n          return;\n        }\n\n        var queue = [startNode];\n\n        while (queue.length) {\n          var currentNode = queue.shift();\n          var edges = currentNode[edgeType];\n\n          for (var i = 0; i < edges.length; i++) {\n            var e = edges[i];\n            var otherNode = e.node1 === currentNode ? e.node2 : e.node1;\n\n            if (!otherNode.__visited) {\n              if (cb.call(context, otherNode, currentNode)) {\n                // Stop traversing\n                return;\n              }\n\n              queue.push(otherNode);\n              otherNode.__visited = true;\n            }\n          }\n        }\n      };\n      // depthFirstTraverse(\n      //     cb, startNode, direction, context\n      // ) {\n      // };\n      // Filter update\n\n      Graph.prototype.update = function () {\n        var data = this.data;\n        var edgeData = this.edgeData;\n        var nodes = this.nodes;\n        var edges = this.edges;\n\n        for (var i = 0, len = nodes.length; i < len; i++) {\n          nodes[i].dataIndex = -1;\n        }\n\n        for (var i = 0, len = data.count(); i < len; i++) {\n          nodes[data.getRawIndex(i)].dataIndex = i;\n        }\n\n        edgeData.filterSelf(function (idx) {\n          var edge = edges[edgeData.getRawIndex(idx)];\n          return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0;\n        }); // Update edge\n\n        for (var i = 0, len = edges.length; i < len; i++) {\n          edges[i].dataIndex = -1;\n        }\n\n        for (var i = 0, len = edgeData.count(); i < len; i++) {\n          edges[edgeData.getRawIndex(i)].dataIndex = i;\n        }\n      };\n      /**\n       * @return {module:echarts/data/Graph}\n       */\n\n      Graph.prototype.clone = function () {\n        var graph = new Graph(this._directed);\n        var nodes = this.nodes;\n        var edges = this.edges;\n\n        for (var i = 0; i < nodes.length; i++) {\n          graph.addNode(nodes[i].id, nodes[i].dataIndex);\n        }\n\n        for (var i = 0; i < edges.length; i++) {\n          var e = edges[i];\n          graph.addEdge(e.node1.id, e.node2.id, e.dataIndex);\n        }\n\n        return graph;\n      };\n      return Graph;\n    }();\n\n    var GraphNode =\n    /** @class */\n    function () {\n      function GraphNode(id, dataIndex) {\n        this.inEdges = [];\n        this.outEdges = [];\n        this.edges = [];\n        this.dataIndex = -1;\n        this.id = id == null ? '' : id;\n        this.dataIndex = dataIndex == null ? -1 : dataIndex;\n      }\n      /**\n       * @return {number}\n       */\n\n\n      GraphNode.prototype.degree = function () {\n        return this.edges.length;\n      };\n      /**\n       * @return {number}\n       */\n\n\n      GraphNode.prototype.inDegree = function () {\n        return this.inEdges.length;\n      };\n      /**\n      * @return {number}\n      */\n\n\n      GraphNode.prototype.outDegree = function () {\n        return this.outEdges.length;\n      };\n\n      GraphNode.prototype.getModel = function (path) {\n        if (this.dataIndex < 0) {\n          return;\n        }\n\n        var graph = this.hostGraph;\n        var itemModel = graph.data.getItemModel(this.dataIndex);\n        return itemModel.getModel(path);\n      };\n\n      GraphNode.prototype.getAdjacentDataIndices = function () {\n        var dataIndices = {\n          edge: [],\n          node: []\n        };\n\n        for (var i = 0; i < this.edges.length; i++) {\n          var adjacentEdge = this.edges[i];\n\n          if (adjacentEdge.dataIndex < 0) {\n            continue;\n          }\n\n          dataIndices.edge.push(adjacentEdge.dataIndex);\n          dataIndices.node.push(adjacentEdge.node1.dataIndex, adjacentEdge.node2.dataIndex);\n        }\n\n        return dataIndices;\n      };\n\n      return GraphNode;\n    }();\n\n    var GraphEdge =\n    /** @class */\n    function () {\n      function GraphEdge(n1, n2, dataIndex) {\n        this.dataIndex = -1;\n        this.node1 = n1;\n        this.node2 = n2;\n        this.dataIndex = dataIndex == null ? -1 : dataIndex;\n      } // eslint-disable-next-line @typescript-eslint/no-unused-vars\n\n\n      GraphEdge.prototype.getModel = function (path) {\n        if (this.dataIndex < 0) {\n          return;\n        }\n\n        var graph = this.hostGraph;\n        var itemModel = graph.edgeData.getItemModel(this.dataIndex);\n        return itemModel.getModel(path);\n      };\n\n      GraphEdge.prototype.getAdjacentDataIndices = function () {\n        return {\n          edge: [this.dataIndex],\n          node: [this.node1.dataIndex, this.node2.dataIndex]\n        };\n      };\n\n      return GraphEdge;\n    }();\n\n    function createGraphDataProxyMixin(hostName, dataName) {\n      return {\n        /**\n         * @param Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.\n         */\n        getValue: function (dimension) {\n          var data = this[hostName][dataName];\n          return data.getStore().get(data.getDimensionIndex(dimension || 'value'), this.dataIndex);\n        },\n        // TODO: TYPE stricter type.\n        setVisual: function (key, value) {\n          this.dataIndex >= 0 && this[hostName][dataName].setItemVisual(this.dataIndex, key, value);\n        },\n        getVisual: function (key) {\n          return this[hostName][dataName].getItemVisual(this.dataIndex, key);\n        },\n        setLayout: function (layout, merge) {\n          this.dataIndex >= 0 && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge);\n        },\n        getLayout: function () {\n          return this[hostName][dataName].getItemLayout(this.dataIndex);\n        },\n        getGraphicEl: function () {\n          return this[hostName][dataName].getItemGraphicEl(this.dataIndex);\n        },\n        getRawIndex: function () {\n          return this[hostName][dataName].getRawIndex(this.dataIndex);\n        }\n      };\n    }\n    mixin(GraphNode, createGraphDataProxyMixin('hostGraph', 'data'));\n    mixin(GraphEdge, createGraphDataProxyMixin('hostGraph', 'edgeData'));\n\n    function createGraphFromNodeEdge(nodes, edges, seriesModel, directed, beforeLink) {\n      // ??? TODO\n      // support dataset?\n      var graph = new Graph(directed);\n\n      for (var i = 0; i < nodes.length; i++) {\n        graph.addNode(retrieve( // Id, name, dataIndex\n        nodes[i].id, nodes[i].name, i), i);\n      }\n\n      var linkNameList = [];\n      var validEdges = [];\n      var linkCount = 0;\n\n      for (var i = 0; i < edges.length; i++) {\n        var link = edges[i];\n        var source = link.source;\n        var target = link.target; // addEdge may fail when source or target not exists\n\n        if (graph.addEdge(source, target, linkCount)) {\n          validEdges.push(link);\n          linkNameList.push(retrieve(convertOptionIdName(link.id, null), source + ' > ' + target));\n          linkCount++;\n        }\n      }\n\n      var coordSys = seriesModel.get('coordinateSystem');\n      var nodeData;\n\n      if (coordSys === 'cartesian2d' || coordSys === 'polar') {\n        nodeData = createSeriesData(nodes, seriesModel);\n      } else {\n        var coordSysCtor = CoordinateSystemManager.get(coordSys);\n        var coordDimensions = coordSysCtor ? coordSysCtor.dimensions || [] : []; // FIXME: Some geo do not need `value` dimenson, whereas `calendar` needs\n        // `value` dimension, but graph need `value` dimension. It's better to\n        // uniform this behavior.\n\n        if (indexOf(coordDimensions, 'value') < 0) {\n          coordDimensions.concat(['value']);\n        }\n\n        var dimensions = prepareSeriesDataSchema(nodes, {\n          coordDimensions: coordDimensions,\n          encodeDefine: seriesModel.getEncode()\n        }).dimensions;\n        nodeData = new SeriesData(dimensions, seriesModel);\n        nodeData.initData(nodes);\n      }\n\n      var edgeData = new SeriesData(['value'], seriesModel);\n      edgeData.initData(validEdges, linkNameList);\n      beforeLink && beforeLink(nodeData, edgeData);\n      linkSeriesData({\n        mainData: nodeData,\n        struct: graph,\n        structAttr: 'graph',\n        datas: {\n          node: nodeData,\n          edge: edgeData\n        },\n        datasAttr: {\n          node: 'data',\n          edge: 'edgeData'\n        }\n      }); // Update dataIndex of nodes and edges because invalid edge may be removed\n\n      graph.update();\n      return graph;\n    }\n\n    var GraphSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(GraphSeriesModel, _super);\n\n      function GraphSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GraphSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      }\n\n      GraphSeriesModel.prototype.init = function (option) {\n        _super.prototype.init.apply(this, arguments);\n\n        var self = this;\n\n        function getCategoriesData() {\n          return self._categoriesData;\n        } // Provide data for legend select\n\n\n        this.legendVisualProvider = new LegendVisualProvider(getCategoriesData, getCategoriesData);\n        this.fillDataTextStyle(option.edges || option.links);\n\n        this._updateCategoriesData();\n      };\n\n      GraphSeriesModel.prototype.mergeOption = function (option) {\n        _super.prototype.mergeOption.apply(this, arguments);\n\n        this.fillDataTextStyle(option.edges || option.links);\n\n        this._updateCategoriesData();\n      };\n\n      GraphSeriesModel.prototype.mergeDefaultAndTheme = function (option) {\n        _super.prototype.mergeDefaultAndTheme.apply(this, arguments);\n\n        defaultEmphasis(option, 'edgeLabel', ['show']);\n      };\n\n      GraphSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        var edges = option.edges || option.links || [];\n        var nodes = option.data || option.nodes || [];\n        var self = this;\n\n        if (nodes && edges) {\n          // auto curveness\n          initCurvenessList(this);\n          var graph = createGraphFromNodeEdge(nodes, edges, this, true, beforeLink);\n          each(graph.edges, function (edge) {\n            createEdgeMapForCurveness(edge.node1, edge.node2, this, edge.dataIndex);\n          }, this);\n          return graph.data;\n        }\n\n        function beforeLink(nodeData, edgeData) {\n          // Overwrite nodeData.getItemModel to\n          nodeData.wrapMethod('getItemModel', function (model) {\n            var categoriesModels = self._categoriesModels;\n            var categoryIdx = model.getShallow('category');\n            var categoryModel = categoriesModels[categoryIdx];\n\n            if (categoryModel) {\n              categoryModel.parentModel = model.parentModel;\n              model.parentModel = categoryModel;\n            }\n\n            return model;\n          }); // TODO Inherit resolveParentPath by default in Model#getModel?\n\n          var oldGetModel = Model.prototype.getModel;\n\n          function newGetModel(path, parentModel) {\n            var model = oldGetModel.call(this, path, parentModel);\n            model.resolveParentPath = resolveParentPath;\n            return model;\n          }\n\n          edgeData.wrapMethod('getItemModel', function (model) {\n            model.resolveParentPath = resolveParentPath;\n            model.getModel = newGetModel;\n            return model;\n          });\n\n          function resolveParentPath(pathArr) {\n            if (pathArr && (pathArr[0] === 'label' || pathArr[1] === 'label')) {\n              var newPathArr = pathArr.slice();\n\n              if (pathArr[0] === 'label') {\n                newPathArr[0] = 'edgeLabel';\n              } else if (pathArr[1] === 'label') {\n                newPathArr[1] = 'edgeLabel';\n              }\n\n              return newPathArr;\n            }\n\n            return pathArr;\n          }\n        }\n      };\n\n      GraphSeriesModel.prototype.getGraph = function () {\n        return this.getData().graph;\n      };\n\n      GraphSeriesModel.prototype.getEdgeData = function () {\n        return this.getGraph().edgeData;\n      };\n\n      GraphSeriesModel.prototype.getCategoriesData = function () {\n        return this._categoriesData;\n      };\n\n      GraphSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        if (dataType === 'edge') {\n          var nodeData = this.getData();\n          var params = this.getDataParams(dataIndex, dataType);\n          var edge = nodeData.graph.getEdgeByIndex(dataIndex);\n          var sourceName = nodeData.getName(edge.node1.dataIndex);\n          var targetName = nodeData.getName(edge.node2.dataIndex);\n          var nameArr = [];\n          sourceName != null && nameArr.push(sourceName);\n          targetName != null && nameArr.push(targetName);\n          return createTooltipMarkup('nameValue', {\n            name: nameArr.join(' > '),\n            value: params.value,\n            noValue: params.value == null\n          });\n        } // dataType === 'node' or empty\n\n\n        var nodeMarkup = defaultSeriesFormatTooltip({\n          series: this,\n          dataIndex: dataIndex,\n          multipleSeries: multipleSeries\n        });\n        return nodeMarkup;\n      };\n\n      GraphSeriesModel.prototype._updateCategoriesData = function () {\n        var categories = map(this.option.categories || [], function (category) {\n          // Data must has value\n          return category.value != null ? category : extend({\n            value: 0\n          }, category);\n        });\n        var categoriesData = new SeriesData(['value'], this);\n        categoriesData.initData(categories);\n        this._categoriesData = categoriesData;\n        this._categoriesModels = categoriesData.mapArray(function (idx) {\n          return categoriesData.getItemModel(idx);\n        });\n      };\n\n      GraphSeriesModel.prototype.setZoom = function (zoom) {\n        this.option.zoom = zoom;\n      };\n\n      GraphSeriesModel.prototype.setCenter = function (center) {\n        this.option.center = center;\n      };\n\n      GraphSeriesModel.prototype.isAnimationEnabled = function () {\n        return _super.prototype.isAnimationEnabled.call(this) // Not enable animation when do force layout\n        && !(this.get('layout') === 'force' && this.get(['force', 'layoutAnimation']));\n      };\n\n      GraphSeriesModel.type = 'series.graph';\n      GraphSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];\n      GraphSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'view',\n        // Default option for all coordinate systems\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        // polarIndex: 0,\n        // geoIndex: 0,\n        legendHoverLink: true,\n        layout: null,\n        // Configuration of circular layout\n        circular: {\n          rotateLabel: false\n        },\n        // Configuration of force directed layout\n        force: {\n          initLayout: null,\n          // Node repulsion. Can be an array to represent range.\n          repulsion: [0, 50],\n          gravity: 0.1,\n          // Initial friction\n          friction: 0.6,\n          // Edge length. Can be an array to represent range.\n          edgeLength: 30,\n          layoutAnimation: true\n        },\n        left: 'center',\n        top: 'center',\n        // right: null,\n        // bottom: null,\n        // width: '80%',\n        // height: '80%',\n        symbol: 'circle',\n        symbolSize: 10,\n        edgeSymbol: ['none', 'none'],\n        edgeSymbolSize: 10,\n        edgeLabel: {\n          position: 'middle',\n          distance: 5\n        },\n        draggable: false,\n        roam: false,\n        // Default on center of graph\n        center: null,\n        zoom: 1,\n        // Symbol size scale ratio in roam\n        nodeScaleRatio: 0.6,\n        // cursor: null,\n        // categories: [],\n        // data: []\n        // Or\n        // nodes: []\n        //\n        // links: []\n        // Or\n        // edges: []\n        label: {\n          show: false,\n          formatter: '{b}'\n        },\n        itemStyle: {},\n        lineStyle: {\n          color: '#aaa',\n          width: 1,\n          opacity: 0.5\n        },\n        emphasis: {\n          scale: true,\n          label: {\n            show: true\n          }\n        },\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        }\n      };\n      return GraphSeriesModel;\n    }(SeriesModel);\n\n    var actionInfo = {\n      type: 'graphRoam',\n      event: 'graphRoam',\n      update: 'none'\n    };\n    function install$d(registers) {\n      registers.registerChartView(GraphView);\n      registers.registerSeriesModel(GraphSeriesModel);\n      registers.registerProcessor(categoryFilter);\n      registers.registerVisual(categoryVisual);\n      registers.registerVisual(graphEdgeVisual);\n      registers.registerLayout(graphSimpleLayout);\n      registers.registerLayout(registers.PRIORITY.VISUAL.POST_CHART_LAYOUT, graphCircularLayout);\n      registers.registerLayout(graphForceLayout);\n      registers.registerCoordinateSystem('graphView', {\n        dimensions: View.dimensions,\n        create: createViewCoordSys\n      }); // Register legacy focus actions\n\n      registers.registerAction({\n        type: 'focusNodeAdjacency',\n        event: 'focusNodeAdjacency',\n        update: 'series:focusNodeAdjacency'\n      }, noop);\n      registers.registerAction({\n        type: 'unfocusNodeAdjacency',\n        event: 'unfocusNodeAdjacency',\n        update: 'series:unfocusNodeAdjacency'\n      }, noop); // Register roam action.\n\n      registers.registerAction(actionInfo, function (payload, ecModel, api) {\n        ecModel.eachComponent({\n          mainType: 'series',\n          query: payload\n        }, function (seriesModel) {\n          var coordSys = seriesModel.coordinateSystem;\n          var res = updateCenterAndZoom(coordSys, payload, undefined, api);\n          seriesModel.setCenter && seriesModel.setCenter(res.center);\n          seriesModel.setZoom && seriesModel.setZoom(res.zoom);\n        });\n      });\n    }\n\n    var PointerShape =\n    /** @class */\n    function () {\n      function PointerShape() {\n        this.angle = 0;\n        this.width = 10;\n        this.r = 10;\n        this.x = 0;\n        this.y = 0;\n      }\n\n      return PointerShape;\n    }();\n\n    var PointerPath =\n    /** @class */\n    function (_super) {\n      __extends(PointerPath, _super);\n\n      function PointerPath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'pointer';\n        return _this;\n      }\n\n      PointerPath.prototype.getDefaultShape = function () {\n        return new PointerShape();\n      };\n\n      PointerPath.prototype.buildPath = function (ctx, shape) {\n        var mathCos = Math.cos;\n        var mathSin = Math.sin;\n        var r = shape.r;\n        var width = shape.width;\n        var angle = shape.angle;\n        var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2);\n        var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2);\n        angle = shape.angle - Math.PI / 2;\n        ctx.moveTo(x, y);\n        ctx.lineTo(shape.x + mathCos(angle) * width, shape.y + mathSin(angle) * width);\n        ctx.lineTo(shape.x + mathCos(shape.angle) * r, shape.y + mathSin(shape.angle) * r);\n        ctx.lineTo(shape.x - mathCos(angle) * width, shape.y - mathSin(angle) * width);\n        ctx.lineTo(x, y);\n      };\n\n      return PointerPath;\n    }(Path);\n\n    function parsePosition(seriesModel, api) {\n      var center = seriesModel.get('center');\n      var width = api.getWidth();\n      var height = api.getHeight();\n      var size = Math.min(width, height);\n      var cx = parsePercent$1(center[0], api.getWidth());\n      var cy = parsePercent$1(center[1], api.getHeight());\n      var r = parsePercent$1(seriesModel.get('radius'), size / 2);\n      return {\n        cx: cx,\n        cy: cy,\n        r: r\n      };\n    }\n\n    function formatLabel(value, labelFormatter) {\n      var label = value == null ? '' : value + '';\n\n      if (labelFormatter) {\n        if (isString(labelFormatter)) {\n          label = labelFormatter.replace('{value}', label);\n        } else if (isFunction(labelFormatter)) {\n          label = labelFormatter(value);\n        }\n      }\n\n      return label;\n    }\n\n    var GaugeView =\n    /** @class */\n    function (_super) {\n      __extends(GaugeView, _super);\n\n      function GaugeView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GaugeView.type;\n        return _this;\n      }\n\n      GaugeView.prototype.render = function (seriesModel, ecModel, api) {\n        this.group.removeAll();\n        var colorList = seriesModel.get(['axisLine', 'lineStyle', 'color']);\n        var posInfo = parsePosition(seriesModel, api);\n\n        this._renderMain(seriesModel, ecModel, api, colorList, posInfo);\n\n        this._data = seriesModel.getData();\n      };\n\n      GaugeView.prototype.dispose = function () {};\n\n      GaugeView.prototype._renderMain = function (seriesModel, ecModel, api, colorList, posInfo) {\n        var group = this.group;\n        var clockwise = seriesModel.get('clockwise');\n        var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI;\n        var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI;\n        var axisLineModel = seriesModel.getModel('axisLine');\n        var roundCap = axisLineModel.get('roundCap');\n        var MainPath = roundCap ? SausagePath : Sector;\n        var showAxis = axisLineModel.get('show');\n        var lineStyleModel = axisLineModel.getModel('lineStyle');\n        var axisLineWidth = lineStyleModel.get('width');\n        var angles = [startAngle, endAngle];\n        normalizeArcAngles(angles, !clockwise);\n        startAngle = angles[0];\n        endAngle = angles[1];\n        var angleRangeSpan = endAngle - startAngle;\n        var prevEndAngle = startAngle;\n        var sectors = [];\n\n        for (var i = 0; showAxis && i < colorList.length; i++) {\n          // Clamp\n          var percent = Math.min(Math.max(colorList[i][0], 0), 1);\n          endAngle = startAngle + angleRangeSpan * percent;\n          var sector = new MainPath({\n            shape: {\n              startAngle: prevEndAngle,\n              endAngle: endAngle,\n              cx: posInfo.cx,\n              cy: posInfo.cy,\n              clockwise: clockwise,\n              r0: posInfo.r - axisLineWidth,\n              r: posInfo.r\n            },\n            silent: true\n          });\n          sector.setStyle({\n            fill: colorList[i][1]\n          });\n          sector.setStyle(lineStyleModel.getLineStyle( // Because we use sector to simulate arc\n          // so the properties for stroking are useless\n          ['color', 'width']));\n          sectors.push(sector);\n          prevEndAngle = endAngle;\n        }\n\n        sectors.reverse();\n        each(sectors, function (sector) {\n          return group.add(sector);\n        });\n\n        var getColor = function (percent) {\n          // Less than 0\n          if (percent <= 0) {\n            return colorList[0][1];\n          }\n\n          var i;\n\n          for (i = 0; i < colorList.length; i++) {\n            if (colorList[i][0] >= percent && (i === 0 ? 0 : colorList[i - 1][0]) < percent) {\n              return colorList[i][1];\n            }\n          } // More than 1\n\n\n          return colorList[i - 1][1];\n        };\n\n        this._renderTicks(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth);\n\n        this._renderTitleAndDetail(seriesModel, ecModel, api, getColor, posInfo);\n\n        this._renderAnchor(seriesModel, posInfo);\n\n        this._renderPointer(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth);\n      };\n\n      GaugeView.prototype._renderTicks = function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth) {\n        var group = this.group;\n        var cx = posInfo.cx;\n        var cy = posInfo.cy;\n        var r = posInfo.r;\n        var minVal = +seriesModel.get('min');\n        var maxVal = +seriesModel.get('max');\n        var splitLineModel = seriesModel.getModel('splitLine');\n        var tickModel = seriesModel.getModel('axisTick');\n        var labelModel = seriesModel.getModel('axisLabel');\n        var splitNumber = seriesModel.get('splitNumber');\n        var subSplitNumber = tickModel.get('splitNumber');\n        var splitLineLen = parsePercent$1(splitLineModel.get('length'), r);\n        var tickLen = parsePercent$1(tickModel.get('length'), r);\n        var angle = startAngle;\n        var step = (endAngle - startAngle) / splitNumber;\n        var subStep = step / subSplitNumber;\n        var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle();\n        var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle();\n        var splitLineDistance = splitLineModel.get('distance');\n        var unitX;\n        var unitY;\n\n        for (var i = 0; i <= splitNumber; i++) {\n          unitX = Math.cos(angle);\n          unitY = Math.sin(angle); // Split line\n\n          if (splitLineModel.get('show')) {\n            var distance = splitLineDistance ? splitLineDistance + axisLineWidth : axisLineWidth;\n            var splitLine = new Line({\n              shape: {\n                x1: unitX * (r - distance) + cx,\n                y1: unitY * (r - distance) + cy,\n                x2: unitX * (r - splitLineLen - distance) + cx,\n                y2: unitY * (r - splitLineLen - distance) + cy\n              },\n              style: splitLineStyle,\n              silent: true\n            });\n\n            if (splitLineStyle.stroke === 'auto') {\n              splitLine.setStyle({\n                stroke: getColor(i / splitNumber)\n              });\n            }\n\n            group.add(splitLine);\n          } // Label\n\n\n          if (labelModel.get('show')) {\n            var distance = labelModel.get('distance') + splitLineDistance;\n            var label = formatLabel(round(i / splitNumber * (maxVal - minVal) + minVal), labelModel.get('formatter'));\n            var autoColor = getColor(i / splitNumber);\n            var textStyleX = unitX * (r - splitLineLen - distance) + cx;\n            var textStyleY = unitY * (r - splitLineLen - distance) + cy;\n            var rotateType = labelModel.get('rotate');\n            var rotate = 0;\n\n            if (rotateType === 'radial') {\n              rotate = -angle + 2 * Math.PI;\n\n              if (rotate > Math.PI / 2) {\n                rotate += Math.PI;\n              }\n            } else if (rotateType === 'tangential') {\n              rotate = -angle - Math.PI / 2;\n            } else if (isNumber(rotateType)) {\n              rotate = rotateType * Math.PI / 180;\n            }\n\n            if (rotate === 0) {\n              group.add(new ZRText({\n                style: createTextStyle(labelModel, {\n                  text: label,\n                  x: textStyleX,\n                  y: textStyleY,\n                  verticalAlign: unitY < -0.8 ? 'top' : unitY > 0.8 ? 'bottom' : 'middle',\n                  align: unitX < -0.4 ? 'left' : unitX > 0.4 ? 'right' : 'center'\n                }, {\n                  inheritColor: autoColor\n                }),\n                silent: true\n              }));\n            } else {\n              group.add(new ZRText({\n                style: createTextStyle(labelModel, {\n                  text: label,\n                  x: textStyleX,\n                  y: textStyleY,\n                  verticalAlign: 'middle',\n                  align: 'center'\n                }, {\n                  inheritColor: autoColor\n                }),\n                silent: true,\n                originX: textStyleX,\n                originY: textStyleY,\n                rotation: rotate\n              }));\n            }\n          } // Axis tick\n\n\n          if (tickModel.get('show') && i !== splitNumber) {\n            var distance = tickModel.get('distance');\n            distance = distance ? distance + axisLineWidth : axisLineWidth;\n\n            for (var j = 0; j <= subSplitNumber; j++) {\n              unitX = Math.cos(angle);\n              unitY = Math.sin(angle);\n              var tickLine = new Line({\n                shape: {\n                  x1: unitX * (r - distance) + cx,\n                  y1: unitY * (r - distance) + cy,\n                  x2: unitX * (r - tickLen - distance) + cx,\n                  y2: unitY * (r - tickLen - distance) + cy\n                },\n                silent: true,\n                style: tickLineStyle\n              });\n\n              if (tickLineStyle.stroke === 'auto') {\n                tickLine.setStyle({\n                  stroke: getColor((i + j / subSplitNumber) / splitNumber)\n                });\n              }\n\n              group.add(tickLine);\n              angle += subStep;\n            }\n\n            angle -= subStep;\n          } else {\n            angle += step;\n          }\n        }\n      };\n\n      GaugeView.prototype._renderPointer = function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth) {\n        var group = this.group;\n        var oldData = this._data;\n        var oldProgressData = this._progressEls;\n        var progressList = [];\n        var showPointer = seriesModel.get(['pointer', 'show']);\n        var progressModel = seriesModel.getModel('progress');\n        var showProgress = progressModel.get('show');\n        var data = seriesModel.getData();\n        var valueDim = data.mapDimension('value');\n        var minVal = +seriesModel.get('min');\n        var maxVal = +seriesModel.get('max');\n        var valueExtent = [minVal, maxVal];\n        var angleExtent = [startAngle, endAngle];\n\n        function createPointer(idx, angle) {\n          var itemModel = data.getItemModel(idx);\n          var pointerModel = itemModel.getModel('pointer');\n          var pointerWidth = parsePercent$1(pointerModel.get('width'), posInfo.r);\n          var pointerLength = parsePercent$1(pointerModel.get('length'), posInfo.r);\n          var pointerStr = seriesModel.get(['pointer', 'icon']);\n          var pointerOffset = pointerModel.get('offsetCenter');\n          var pointerOffsetX = parsePercent$1(pointerOffset[0], posInfo.r);\n          var pointerOffsetY = parsePercent$1(pointerOffset[1], posInfo.r);\n          var pointerKeepAspect = pointerModel.get('keepAspect');\n          var pointer; // not exist icon type will be set 'rect'\n\n          if (pointerStr) {\n            pointer = createSymbol(pointerStr, pointerOffsetX - pointerWidth / 2, pointerOffsetY - pointerLength, pointerWidth, pointerLength, null, pointerKeepAspect);\n          } else {\n            pointer = new PointerPath({\n              shape: {\n                angle: -Math.PI / 2,\n                width: pointerWidth,\n                r: pointerLength,\n                x: pointerOffsetX,\n                y: pointerOffsetY\n              }\n            });\n          }\n\n          pointer.rotation = -(angle + Math.PI / 2);\n          pointer.x = posInfo.cx;\n          pointer.y = posInfo.cy;\n          return pointer;\n        }\n\n        function createProgress(idx, endAngle) {\n          var roundCap = progressModel.get('roundCap');\n          var ProgressPath = roundCap ? SausagePath : Sector;\n          var isOverlap = progressModel.get('overlap');\n          var progressWidth = isOverlap ? progressModel.get('width') : axisLineWidth / data.count();\n          var r0 = isOverlap ? posInfo.r - progressWidth : posInfo.r - (idx + 1) * progressWidth;\n          var r = isOverlap ? posInfo.r : posInfo.r - idx * progressWidth;\n          var progress = new ProgressPath({\n            shape: {\n              startAngle: startAngle,\n              endAngle: endAngle,\n              cx: posInfo.cx,\n              cy: posInfo.cy,\n              clockwise: clockwise,\n              r0: r0,\n              r: r\n            }\n          });\n          isOverlap && (progress.z2 = maxVal - data.get(valueDim, idx) % maxVal);\n          return progress;\n        }\n\n        if (showProgress || showPointer) {\n          data.diff(oldData).add(function (idx) {\n            var val = data.get(valueDim, idx);\n\n            if (showPointer) {\n              var pointer = createPointer(idx, startAngle); // TODO hide pointer on NaN value?\n\n              initProps(pointer, {\n                rotation: -((isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) + Math.PI / 2)\n              }, seriesModel);\n              group.add(pointer);\n              data.setItemGraphicEl(idx, pointer);\n            }\n\n            if (showProgress) {\n              var progress = createProgress(idx, startAngle);\n              var isClip = progressModel.get('clip');\n              initProps(progress, {\n                shape: {\n                  endAngle: linearMap(val, valueExtent, angleExtent, isClip)\n                }\n              }, seriesModel);\n              group.add(progress); // Add data index and series index for indexing the data by element\n              // Useful in tooltip\n\n              setCommonECData(seriesModel.seriesIndex, data.dataType, idx, progress);\n              progressList[idx] = progress;\n            }\n          }).update(function (newIdx, oldIdx) {\n            var val = data.get(valueDim, newIdx);\n\n            if (showPointer) {\n              var previousPointer = oldData.getItemGraphicEl(oldIdx);\n              var previousRotate = previousPointer ? previousPointer.rotation : startAngle;\n              var pointer = createPointer(newIdx, previousRotate);\n              pointer.rotation = previousRotate;\n              updateProps(pointer, {\n                rotation: -((isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) + Math.PI / 2)\n              }, seriesModel);\n              group.add(pointer);\n              data.setItemGraphicEl(newIdx, pointer);\n            }\n\n            if (showProgress) {\n              var previousProgress = oldProgressData[oldIdx];\n              var previousEndAngle = previousProgress ? previousProgress.shape.endAngle : startAngle;\n              var progress = createProgress(newIdx, previousEndAngle);\n              var isClip = progressModel.get('clip');\n              updateProps(progress, {\n                shape: {\n                  endAngle: linearMap(val, valueExtent, angleExtent, isClip)\n                }\n              }, seriesModel);\n              group.add(progress); // Add data index and series index for indexing the data by element\n              // Useful in tooltip\n\n              setCommonECData(seriesModel.seriesIndex, data.dataType, newIdx, progress);\n              progressList[newIdx] = progress;\n            }\n          }).execute();\n          data.each(function (idx) {\n            var itemModel = data.getItemModel(idx);\n            var emphasisModel = itemModel.getModel('emphasis');\n            var focus = emphasisModel.get('focus');\n            var blurScope = emphasisModel.get('blurScope');\n            var emphasisDisabled = emphasisModel.get('disabled');\n\n            if (showPointer) {\n              var pointer = data.getItemGraphicEl(idx);\n              var symbolStyle = data.getItemVisual(idx, 'style');\n              var visualColor = symbolStyle.fill;\n\n              if (pointer instanceof ZRImage) {\n                var pathStyle = pointer.style;\n                pointer.useStyle(extend({\n                  image: pathStyle.image,\n                  x: pathStyle.x,\n                  y: pathStyle.y,\n                  width: pathStyle.width,\n                  height: pathStyle.height\n                }, symbolStyle));\n              } else {\n                pointer.useStyle(symbolStyle);\n                pointer.type !== 'pointer' && pointer.setColor(visualColor);\n              }\n\n              pointer.setStyle(itemModel.getModel(['pointer', 'itemStyle']).getItemStyle());\n\n              if (pointer.style.fill === 'auto') {\n                pointer.setStyle('fill', getColor(linearMap(data.get(valueDim, idx), valueExtent, [0, 1], true)));\n              }\n\n              pointer.z2EmphasisLift = 0;\n              setStatesStylesFromModel(pointer, itemModel);\n              toggleHoverEmphasis(pointer, focus, blurScope, emphasisDisabled);\n            }\n\n            if (showProgress) {\n              var progress = progressList[idx];\n              progress.useStyle(data.getItemVisual(idx, 'style'));\n              progress.setStyle(itemModel.getModel(['progress', 'itemStyle']).getItemStyle());\n              progress.z2EmphasisLift = 0;\n              setStatesStylesFromModel(progress, itemModel);\n              toggleHoverEmphasis(progress, focus, blurScope, emphasisDisabled);\n            }\n          });\n          this._progressEls = progressList;\n        }\n      };\n\n      GaugeView.prototype._renderAnchor = function (seriesModel, posInfo) {\n        var anchorModel = seriesModel.getModel('anchor');\n        var showAnchor = anchorModel.get('show');\n\n        if (showAnchor) {\n          var anchorSize = anchorModel.get('size');\n          var anchorType = anchorModel.get('icon');\n          var offsetCenter = anchorModel.get('offsetCenter');\n          var anchorKeepAspect = anchorModel.get('keepAspect');\n          var anchor = createSymbol(anchorType, posInfo.cx - anchorSize / 2 + parsePercent$1(offsetCenter[0], posInfo.r), posInfo.cy - anchorSize / 2 + parsePercent$1(offsetCenter[1], posInfo.r), anchorSize, anchorSize, null, anchorKeepAspect);\n          anchor.z2 = anchorModel.get('showAbove') ? 1 : 0;\n          anchor.setStyle(anchorModel.getModel('itemStyle').getItemStyle());\n          this.group.add(anchor);\n        }\n      };\n\n      GaugeView.prototype._renderTitleAndDetail = function (seriesModel, ecModel, api, getColor, posInfo) {\n        var _this = this;\n\n        var data = seriesModel.getData();\n        var valueDim = data.mapDimension('value');\n        var minVal = +seriesModel.get('min');\n        var maxVal = +seriesModel.get('max');\n        var contentGroup = new Group();\n        var newTitleEls = [];\n        var newDetailEls = [];\n        var hasAnimation = seriesModel.isAnimationEnabled();\n        var showPointerAbove = seriesModel.get(['pointer', 'showAbove']);\n        data.diff(this._data).add(function (idx) {\n          newTitleEls[idx] = new ZRText({\n            silent: true\n          });\n          newDetailEls[idx] = new ZRText({\n            silent: true\n          });\n        }).update(function (idx, oldIdx) {\n          newTitleEls[idx] = _this._titleEls[oldIdx];\n          newDetailEls[idx] = _this._detailEls[oldIdx];\n        }).execute();\n        data.each(function (idx) {\n          var itemModel = data.getItemModel(idx);\n          var value = data.get(valueDim, idx);\n          var itemGroup = new Group();\n          var autoColor = getColor(linearMap(value, [minVal, maxVal], [0, 1], true));\n          var itemTitleModel = itemModel.getModel('title');\n\n          if (itemTitleModel.get('show')) {\n            var titleOffsetCenter = itemTitleModel.get('offsetCenter');\n            var titleX = posInfo.cx + parsePercent$1(titleOffsetCenter[0], posInfo.r);\n            var titleY = posInfo.cy + parsePercent$1(titleOffsetCenter[1], posInfo.r);\n            var labelEl = newTitleEls[idx];\n            labelEl.attr({\n              z2: showPointerAbove ? 0 : 2,\n              style: createTextStyle(itemTitleModel, {\n                x: titleX,\n                y: titleY,\n                text: data.getName(idx),\n                align: 'center',\n                verticalAlign: 'middle'\n              }, {\n                inheritColor: autoColor\n              })\n            });\n            itemGroup.add(labelEl);\n          }\n\n          var itemDetailModel = itemModel.getModel('detail');\n\n          if (itemDetailModel.get('show')) {\n            var detailOffsetCenter = itemDetailModel.get('offsetCenter');\n            var detailX = posInfo.cx + parsePercent$1(detailOffsetCenter[0], posInfo.r);\n            var detailY = posInfo.cy + parsePercent$1(detailOffsetCenter[1], posInfo.r);\n            var width = parsePercent$1(itemDetailModel.get('width'), posInfo.r);\n            var height = parsePercent$1(itemDetailModel.get('height'), posInfo.r);\n            var detailColor = seriesModel.get(['progress', 'show']) ? data.getItemVisual(idx, 'style').fill : autoColor;\n            var labelEl = newDetailEls[idx];\n            var formatter_1 = itemDetailModel.get('formatter');\n            labelEl.attr({\n              z2: showPointerAbove ? 0 : 2,\n              style: createTextStyle(itemDetailModel, {\n                x: detailX,\n                y: detailY,\n                text: formatLabel(value, formatter_1),\n                width: isNaN(width) ? null : width,\n                height: isNaN(height) ? null : height,\n                align: 'center',\n                verticalAlign: 'middle'\n              }, {\n                inheritColor: detailColor\n              })\n            });\n            setLabelValueAnimation(labelEl, {\n              normal: itemDetailModel\n            }, value, function (value) {\n              return formatLabel(value, formatter_1);\n            });\n            hasAnimation && animateLabelValue(labelEl, idx, data, seriesModel, {\n              getFormattedLabel: function (labelDataIndex, status, dataType, labelDimIndex, fmt, extendParams) {\n                return formatLabel(extendParams ? extendParams.interpolatedValue : value, formatter_1);\n              }\n            });\n            itemGroup.add(labelEl);\n          }\n\n          contentGroup.add(itemGroup);\n        });\n        this.group.add(contentGroup);\n        this._titleEls = newTitleEls;\n        this._detailEls = newDetailEls;\n      };\n\n      GaugeView.type = 'gauge';\n      return GaugeView;\n    }(ChartView);\n\n    var GaugeSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(GaugeSeriesModel, _super);\n\n      function GaugeSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GaugeSeriesModel.type;\n        _this.visualStyleAccessPath = 'itemStyle';\n        return _this;\n      }\n\n      GaugeSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesDataSimply(this, ['value']);\n      };\n\n      GaugeSeriesModel.type = 'series.gauge';\n      GaugeSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        colorBy: 'data',\n        // 默认全局居中\n        center: ['50%', '50%'],\n        legendHoverLink: true,\n        radius: '75%',\n        startAngle: 225,\n        endAngle: -45,\n        clockwise: true,\n        // 最小值\n        min: 0,\n        // 最大值\n        max: 100,\n        // 分割段数，默认为10\n        splitNumber: 10,\n        // 坐标轴线\n        axisLine: {\n          // 默认显示，属性show控制显示与否\n          show: true,\n          roundCap: false,\n          lineStyle: {\n            color: [[1, '#E6EBF8']],\n            width: 10\n          }\n        },\n        // 坐标轴线\n        progress: {\n          // 默认显示，属性show控制显示与否\n          show: false,\n          overlap: true,\n          width: 10,\n          roundCap: false,\n          clip: true\n        },\n        // 分隔线\n        splitLine: {\n          // 默认显示，属性show控制显示与否\n          show: true,\n          // 属性length控制线长\n          length: 10,\n          distance: 10,\n          // 属性lineStyle（详见lineStyle）控制线条样式\n          lineStyle: {\n            color: '#63677A',\n            width: 3,\n            type: 'solid'\n          }\n        },\n        // 坐标轴小标记\n        axisTick: {\n          // 属性show控制显示与否，默认不显示\n          show: true,\n          // 每份split细分多少段\n          splitNumber: 5,\n          // 属性length控制线长\n          length: 6,\n          distance: 10,\n          // 属性lineStyle控制线条样式\n          lineStyle: {\n            color: '#63677A',\n            width: 1,\n            type: 'solid'\n          }\n        },\n        axisLabel: {\n          show: true,\n          distance: 15,\n          // formatter: null,\n          color: '#464646',\n          fontSize: 12,\n          rotate: 0\n        },\n        pointer: {\n          icon: null,\n          offsetCenter: [0, 0],\n          show: true,\n          showAbove: true,\n          length: '60%',\n          width: 6,\n          keepAspect: false\n        },\n        anchor: {\n          show: false,\n          showAbove: false,\n          size: 6,\n          icon: 'circle',\n          offsetCenter: [0, 0],\n          keepAspect: false,\n          itemStyle: {\n            color: '#fff',\n            borderWidth: 0,\n            borderColor: '#5470c6'\n          }\n        },\n        title: {\n          show: true,\n          // x, y，单位px\n          offsetCenter: [0, '20%'],\n          // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n          color: '#464646',\n          fontSize: 16,\n          valueAnimation: false\n        },\n        detail: {\n          show: true,\n          backgroundColor: 'rgba(0,0,0,0)',\n          borderWidth: 0,\n          borderColor: '#ccc',\n          width: 100,\n          height: null,\n          padding: [5, 10],\n          // x, y，单位px\n          offsetCenter: [0, '40%'],\n          // formatter: null,\n          // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n          color: '#464646',\n          fontSize: 30,\n          fontWeight: 'bold',\n          lineHeight: 30,\n          valueAnimation: false\n        }\n      };\n      return GaugeSeriesModel;\n    }(SeriesModel);\n\n    function install$e(registers) {\n      registers.registerChartView(GaugeView);\n      registers.registerSeriesModel(GaugeSeriesModel);\n    }\n\n    var opacityAccessPath = ['itemStyle', 'opacity'];\n    /**\n     * Piece of pie including Sector, Label, LabelLine\n     */\n\n    var FunnelPiece =\n    /** @class */\n    function (_super) {\n      __extends(FunnelPiece, _super);\n\n      function FunnelPiece(data, idx) {\n        var _this = _super.call(this) || this;\n\n        var polygon = _this;\n        var labelLine = new Polyline();\n        var text = new ZRText();\n        polygon.setTextContent(text);\n\n        _this.setTextGuideLine(labelLine);\n\n        _this.updateData(data, idx, true);\n\n        return _this;\n      }\n\n      FunnelPiece.prototype.updateData = function (data, idx, firstCreate) {\n        var polygon = this;\n        var seriesModel = data.hostModel;\n        var itemModel = data.getItemModel(idx);\n        var layout = data.getItemLayout(idx);\n        var emphasisModel = itemModel.getModel('emphasis');\n        var opacity = itemModel.get(opacityAccessPath);\n        opacity = opacity == null ? 1 : opacity;\n\n        if (!firstCreate) {\n          saveOldStyle(polygon);\n        } // Update common style\n\n\n        polygon.useStyle(data.getItemVisual(idx, 'style'));\n        polygon.style.lineJoin = 'round';\n\n        if (firstCreate) {\n          polygon.setShape({\n            points: layout.points\n          });\n          polygon.style.opacity = 0;\n          initProps(polygon, {\n            style: {\n              opacity: opacity\n            }\n          }, seriesModel, idx);\n        } else {\n          updateProps(polygon, {\n            style: {\n              opacity: opacity\n            },\n            shape: {\n              points: layout.points\n            }\n          }, seriesModel, idx);\n        }\n\n        setStatesStylesFromModel(polygon, itemModel);\n\n        this._updateLabel(data, idx);\n\n        toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      };\n\n      FunnelPiece.prototype._updateLabel = function (data, idx) {\n        var polygon = this;\n        var labelLine = this.getTextGuideLine();\n        var labelText = polygon.getTextContent();\n        var seriesModel = data.hostModel;\n        var itemModel = data.getItemModel(idx);\n        var layout = data.getItemLayout(idx);\n        var labelLayout = layout.label;\n        var style = data.getItemVisual(idx, 'style');\n        var visualColor = style.fill;\n        setLabelStyle( // position will not be used in setLabelStyle\n        labelText, getLabelStatesModels(itemModel), {\n          labelFetcher: data.hostModel,\n          labelDataIndex: idx,\n          defaultOpacity: style.opacity,\n          defaultText: data.getName(idx)\n        }, {\n          normal: {\n            align: labelLayout.textAlign,\n            verticalAlign: labelLayout.verticalAlign\n          }\n        });\n        polygon.setTextConfig({\n          local: true,\n          inside: !!labelLayout.inside,\n          insideStroke: visualColor,\n          // insideFill: 'auto',\n          outsideFill: visualColor\n        });\n        var linePoints = labelLayout.linePoints;\n        labelLine.setShape({\n          points: linePoints\n        });\n        polygon.textGuideLineConfig = {\n          anchor: linePoints ? new Point(linePoints[0][0], linePoints[0][1]) : null\n        }; // Make sure update style on labelText after setLabelStyle.\n        // Because setLabelStyle will replace a new style on it.\n\n        updateProps(labelText, {\n          style: {\n            x: labelLayout.x,\n            y: labelLayout.y\n          }\n        }, seriesModel, idx);\n        labelText.attr({\n          rotation: labelLayout.rotation,\n          originX: labelLayout.x,\n          originY: labelLayout.y,\n          z2: 10\n        });\n        setLabelLineStyle(polygon, getLabelLineStatesModels(itemModel), {\n          // Default use item visual color\n          stroke: visualColor\n        });\n      };\n\n      return FunnelPiece;\n    }(Polygon);\n\n    var FunnelView =\n    /** @class */\n    function (_super) {\n      __extends(FunnelView, _super);\n\n      function FunnelView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = FunnelView.type;\n        _this.ignoreLabelLineUpdate = true;\n        return _this;\n      }\n\n      FunnelView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var group = this.group;\n        data.diff(oldData).add(function (idx) {\n          var funnelPiece = new FunnelPiece(data, idx);\n          data.setItemGraphicEl(idx, funnelPiece);\n          group.add(funnelPiece);\n        }).update(function (newIdx, oldIdx) {\n          var piece = oldData.getItemGraphicEl(oldIdx);\n          piece.updateData(data, newIdx);\n          group.add(piece);\n          data.setItemGraphicEl(newIdx, piece);\n        }).remove(function (idx) {\n          var piece = oldData.getItemGraphicEl(idx);\n          removeElementWithFadeOut(piece, seriesModel, idx);\n        }).execute();\n        this._data = data;\n      };\n\n      FunnelView.prototype.remove = function () {\n        this.group.removeAll();\n        this._data = null;\n      };\n\n      FunnelView.prototype.dispose = function () {};\n\n      FunnelView.type = 'funnel';\n      return FunnelView;\n    }(ChartView);\n\n    var FunnelSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(FunnelSeriesModel, _super);\n\n      function FunnelSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = FunnelSeriesModel.type;\n        return _this;\n      }\n\n      FunnelSeriesModel.prototype.init = function (option) {\n        _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n        // Use a function instead of direct access because data reference may changed\n\n\n        this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this)); // Extend labelLine emphasis\n\n        this._defaultLabelLine(option);\n      };\n\n      FunnelSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesDataSimply(this, {\n          coordDimensions: ['value'],\n          encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n        });\n      };\n\n      FunnelSeriesModel.prototype._defaultLabelLine = function (option) {\n        // Extend labelLine emphasis\n        defaultEmphasis(option, 'labelLine', ['show']);\n        var labelLineNormalOpt = option.labelLine;\n        var labelLineEmphasisOpt = option.emphasis.labelLine; // Not show label line if `label.normal.show = false`\n\n        labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show;\n        labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show;\n      }; // Overwrite\n\n\n      FunnelSeriesModel.prototype.getDataParams = function (dataIndex) {\n        var data = this.getData();\n\n        var params = _super.prototype.getDataParams.call(this, dataIndex);\n\n        var valueDim = data.mapDimension('value');\n        var sum = data.getSum(valueDim); // Percent is 0 if sum is 0\n\n        params.percent = !sum ? 0 : +(data.get(valueDim, dataIndex) / sum * 100).toFixed(2);\n        params.$vars.push('percent');\n        return params;\n      };\n\n      FunnelSeriesModel.type = 'series.funnel';\n      FunnelSeriesModel.defaultOption = {\n        // zlevel: 0,                  // 一级层叠\n        z: 2,\n        legendHoverLink: true,\n        colorBy: 'data',\n        left: 80,\n        top: 60,\n        right: 80,\n        bottom: 60,\n        // width: {totalWidth} - left - right,\n        // height: {totalHeight} - top - bottom,\n        // 默认取数据最小最大值\n        // min: 0,\n        // max: 100,\n        minSize: '0%',\n        maxSize: '100%',\n        sort: 'descending',\n        orient: 'vertical',\n        gap: 0,\n        funnelAlign: 'center',\n        label: {\n          show: true,\n          position: 'outer' // formatter: 标签文本格式器，同Tooltip.formatter，不支持异步回调\n\n        },\n        labelLine: {\n          show: true,\n          length: 20,\n          lineStyle: {\n            // color: 各异,\n            width: 1\n          }\n        },\n        itemStyle: {\n          // color: 各异,\n          borderColor: '#fff',\n          borderWidth: 1\n        },\n        emphasis: {\n          label: {\n            show: true\n          }\n        },\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        }\n      };\n      return FunnelSeriesModel;\n    }(SeriesModel);\n\n    function getViewRect$3(seriesModel, api) {\n      return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n        width: api.getWidth(),\n        height: api.getHeight()\n      });\n    }\n\n    function getSortedIndices(data, sort) {\n      var valueDim = data.mapDimension('value');\n      var valueArr = data.mapArray(valueDim, function (val) {\n        return val;\n      });\n      var indices = [];\n      var isAscending = sort === 'ascending';\n\n      for (var i = 0, len = data.count(); i < len; i++) {\n        indices[i] = i;\n      } // Add custom sortable function & none sortable opetion by \"options.sort\"\n\n\n      if (isFunction(sort)) {\n        indices.sort(sort);\n      } else if (sort !== 'none') {\n        indices.sort(function (a, b) {\n          return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];\n        });\n      }\n\n      return indices;\n    }\n\n    function labelLayout(data) {\n      var seriesModel = data.hostModel;\n      var orient = seriesModel.get('orient');\n      data.each(function (idx) {\n        var itemModel = data.getItemModel(idx);\n        var labelModel = itemModel.getModel('label');\n        var labelPosition = labelModel.get('position');\n        var labelLineModel = itemModel.getModel('labelLine');\n        var layout = data.getItemLayout(idx);\n        var points = layout.points;\n        var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight';\n        var textAlign;\n        var textX;\n        var textY;\n        var linePoints;\n\n        if (isLabelInside) {\n          if (labelPosition === 'insideLeft') {\n            textX = (points[0][0] + points[3][0]) / 2 + 5;\n            textY = (points[0][1] + points[3][1]) / 2;\n            textAlign = 'left';\n          } else if (labelPosition === 'insideRight') {\n            textX = (points[1][0] + points[2][0]) / 2 - 5;\n            textY = (points[1][1] + points[2][1]) / 2;\n            textAlign = 'right';\n          } else {\n            textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;\n            textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;\n            textAlign = 'center';\n          }\n\n          linePoints = [[textX, textY], [textX, textY]];\n        } else {\n          var x1 = void 0;\n          var y1 = void 0;\n          var x2 = void 0;\n          var y2 = void 0;\n          var labelLineLen = labelLineModel.get('length');\n\n          if (\"development\" !== 'production') {\n            if (orient === 'vertical' && ['top', 'bottom'].indexOf(labelPosition) > -1) {\n              labelPosition = 'left';\n              console.warn('Position error: Funnel chart on vertical orient dose not support top and bottom.');\n            }\n\n            if (orient === 'horizontal' && ['left', 'right'].indexOf(labelPosition) > -1) {\n              labelPosition = 'bottom';\n              console.warn('Position error: Funnel chart on horizontal orient dose not support left and right.');\n            }\n          }\n\n          if (labelPosition === 'left') {\n            // Left side\n            x1 = (points[3][0] + points[0][0]) / 2;\n            y1 = (points[3][1] + points[0][1]) / 2;\n            x2 = x1 - labelLineLen;\n            textX = x2 - 5;\n            textAlign = 'right';\n          } else if (labelPosition === 'right') {\n            // Right side\n            x1 = (points[1][0] + points[2][0]) / 2;\n            y1 = (points[1][1] + points[2][1]) / 2;\n            x2 = x1 + labelLineLen;\n            textX = x2 + 5;\n            textAlign = 'left';\n          } else if (labelPosition === 'top') {\n            // Top side\n            x1 = (points[3][0] + points[0][0]) / 2;\n            y1 = (points[3][1] + points[0][1]) / 2;\n            y2 = y1 - labelLineLen;\n            textY = y2 - 5;\n            textAlign = 'center';\n          } else if (labelPosition === 'bottom') {\n            // Bottom side\n            x1 = (points[1][0] + points[2][0]) / 2;\n            y1 = (points[1][1] + points[2][1]) / 2;\n            y2 = y1 + labelLineLen;\n            textY = y2 + 5;\n            textAlign = 'center';\n          } else if (labelPosition === 'rightTop') {\n            // RightTop side\n            x1 = orient === 'horizontal' ? points[3][0] : points[1][0];\n            y1 = orient === 'horizontal' ? points[3][1] : points[1][1];\n\n            if (orient === 'horizontal') {\n              y2 = y1 - labelLineLen;\n              textY = y2 - 5;\n              textAlign = 'center';\n            } else {\n              x2 = x1 + labelLineLen;\n              textX = x2 + 5;\n              textAlign = 'top';\n            }\n          } else if (labelPosition === 'rightBottom') {\n            // RightBottom side\n            x1 = points[2][0];\n            y1 = points[2][1];\n\n            if (orient === 'horizontal') {\n              y2 = y1 + labelLineLen;\n              textY = y2 + 5;\n              textAlign = 'center';\n            } else {\n              x2 = x1 + labelLineLen;\n              textX = x2 + 5;\n              textAlign = 'bottom';\n            }\n          } else if (labelPosition === 'leftTop') {\n            // LeftTop side\n            x1 = points[0][0];\n            y1 = orient === 'horizontal' ? points[0][1] : points[1][1];\n\n            if (orient === 'horizontal') {\n              y2 = y1 - labelLineLen;\n              textY = y2 - 5;\n              textAlign = 'center';\n            } else {\n              x2 = x1 - labelLineLen;\n              textX = x2 - 5;\n              textAlign = 'right';\n            }\n          } else if (labelPosition === 'leftBottom') {\n            // LeftBottom side\n            x1 = orient === 'horizontal' ? points[1][0] : points[3][0];\n            y1 = orient === 'horizontal' ? points[1][1] : points[2][1];\n\n            if (orient === 'horizontal') {\n              y2 = y1 + labelLineLen;\n              textY = y2 + 5;\n              textAlign = 'center';\n            } else {\n              x2 = x1 - labelLineLen;\n              textX = x2 - 5;\n              textAlign = 'right';\n            }\n          } else {\n            // Right side or Bottom side\n            x1 = (points[1][0] + points[2][0]) / 2;\n            y1 = (points[1][1] + points[2][1]) / 2;\n\n            if (orient === 'horizontal') {\n              y2 = y1 + labelLineLen;\n              textY = y2 + 5;\n              textAlign = 'center';\n            } else {\n              x2 = x1 + labelLineLen;\n              textX = x2 + 5;\n              textAlign = 'left';\n            }\n          }\n\n          if (orient === 'horizontal') {\n            x2 = x1;\n            textX = x2;\n          } else {\n            y2 = y1;\n            textY = y2;\n          }\n\n          linePoints = [[x1, y1], [x2, y2]];\n        }\n\n        layout.label = {\n          linePoints: linePoints,\n          x: textX,\n          y: textY,\n          verticalAlign: 'middle',\n          textAlign: textAlign,\n          inside: isLabelInside\n        };\n      });\n    }\n\n    function funnelLayout(ecModel, api) {\n      ecModel.eachSeriesByType('funnel', function (seriesModel) {\n        var data = seriesModel.getData();\n        var valueDim = data.mapDimension('value');\n        var sort = seriesModel.get('sort');\n        var viewRect = getViewRect$3(seriesModel, api);\n        var orient = seriesModel.get('orient');\n        var viewWidth = viewRect.width;\n        var viewHeight = viewRect.height;\n        var indices = getSortedIndices(data, sort);\n        var x = viewRect.x;\n        var y = viewRect.y;\n        var sizeExtent = orient === 'horizontal' ? [parsePercent$1(seriesModel.get('minSize'), viewHeight), parsePercent$1(seriesModel.get('maxSize'), viewHeight)] : [parsePercent$1(seriesModel.get('minSize'), viewWidth), parsePercent$1(seriesModel.get('maxSize'), viewWidth)];\n        var dataExtent = data.getDataExtent(valueDim);\n        var min = seriesModel.get('min');\n        var max = seriesModel.get('max');\n\n        if (min == null) {\n          min = Math.min(dataExtent[0], 0);\n        }\n\n        if (max == null) {\n          max = dataExtent[1];\n        }\n\n        var funnelAlign = seriesModel.get('funnelAlign');\n        var gap = seriesModel.get('gap');\n        var viewSize = orient === 'horizontal' ? viewWidth : viewHeight;\n        var itemSize = (viewSize - gap * (data.count() - 1)) / data.count();\n\n        var getLinePoints = function (idx, offset) {\n          // End point index is data.count() and we assign it 0\n          if (orient === 'horizontal') {\n            var val_1 = data.get(valueDim, idx) || 0;\n            var itemHeight = linearMap(val_1, [min, max], sizeExtent, true);\n            var y0 = void 0;\n\n            switch (funnelAlign) {\n              case 'top':\n                y0 = y;\n                break;\n\n              case 'center':\n                y0 = y + (viewHeight - itemHeight) / 2;\n                break;\n\n              case 'bottom':\n                y0 = y + (viewHeight - itemHeight);\n                break;\n            }\n\n            return [[offset, y0], [offset, y0 + itemHeight]];\n          }\n\n          var val = data.get(valueDim, idx) || 0;\n          var itemWidth = linearMap(val, [min, max], sizeExtent, true);\n          var x0;\n\n          switch (funnelAlign) {\n            case 'left':\n              x0 = x;\n              break;\n\n            case 'center':\n              x0 = x + (viewWidth - itemWidth) / 2;\n              break;\n\n            case 'right':\n              x0 = x + viewWidth - itemWidth;\n              break;\n          }\n\n          return [[x0, offset], [x0 + itemWidth, offset]];\n        };\n\n        if (sort === 'ascending') {\n          // From bottom to top\n          itemSize = -itemSize;\n          gap = -gap;\n\n          if (orient === 'horizontal') {\n            x += viewWidth;\n          } else {\n            y += viewHeight;\n          }\n\n          indices = indices.reverse();\n        }\n\n        for (var i = 0; i < indices.length; i++) {\n          var idx = indices[i];\n          var nextIdx = indices[i + 1];\n          var itemModel = data.getItemModel(idx);\n\n          if (orient === 'horizontal') {\n            var width = itemModel.get(['itemStyle', 'width']);\n\n            if (width == null) {\n              width = itemSize;\n            } else {\n              width = parsePercent$1(width, viewWidth);\n\n              if (sort === 'ascending') {\n                width = -width;\n              }\n            }\n\n            var start = getLinePoints(idx, x);\n            var end = getLinePoints(nextIdx, x + width);\n            x += width + gap;\n            data.setItemLayout(idx, {\n              points: start.concat(end.slice().reverse())\n            });\n          } else {\n            var height = itemModel.get(['itemStyle', 'height']);\n\n            if (height == null) {\n              height = itemSize;\n            } else {\n              height = parsePercent$1(height, viewHeight);\n\n              if (sort === 'ascending') {\n                height = -height;\n              }\n            }\n\n            var start = getLinePoints(idx, y);\n            var end = getLinePoints(nextIdx, y + height);\n            y += height + gap;\n            data.setItemLayout(idx, {\n              points: start.concat(end.slice().reverse())\n            });\n          }\n        }\n\n        labelLayout(data);\n      });\n    }\n\n    function install$f(registers) {\n      registers.registerChartView(FunnelView);\n      registers.registerSeriesModel(FunnelSeriesModel);\n      registers.registerLayout(funnelLayout);\n      registers.registerProcessor(dataFilter('funnel'));\n    }\n\n    var DEFAULT_SMOOTH = 0.3;\n\n    var ParallelView =\n    /** @class */\n    function (_super) {\n      __extends(ParallelView, _super);\n\n      function ParallelView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ParallelView.type;\n        _this._dataGroup = new Group();\n        _this._initialized = false;\n        return _this;\n      }\n\n      ParallelView.prototype.init = function () {\n        this.group.add(this._dataGroup);\n      };\n      /**\n       * @override\n       */\n\n\n      ParallelView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        // Clear previously rendered progressive elements.\n        this._progressiveEls = null;\n        var dataGroup = this._dataGroup;\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var coordSys = seriesModel.coordinateSystem;\n        var dimensions = coordSys.dimensions;\n        var seriesScope = makeSeriesScope$2(seriesModel);\n        data.diff(oldData).add(add).update(update).remove(remove).execute();\n\n        function add(newDataIndex) {\n          var line = addEl(data, dataGroup, newDataIndex, dimensions, coordSys);\n          updateElCommon(line, data, newDataIndex, seriesScope);\n        }\n\n        function update(newDataIndex, oldDataIndex) {\n          var line = oldData.getItemGraphicEl(oldDataIndex);\n          var points = createLinePoints(data, newDataIndex, dimensions, coordSys);\n          data.setItemGraphicEl(newDataIndex, line);\n          updateProps(line, {\n            shape: {\n              points: points\n            }\n          }, seriesModel, newDataIndex);\n          saveOldStyle(line);\n          updateElCommon(line, data, newDataIndex, seriesScope);\n        }\n\n        function remove(oldDataIndex) {\n          var line = oldData.getItemGraphicEl(oldDataIndex);\n          dataGroup.remove(line);\n        } // First create\n\n\n        if (!this._initialized) {\n          this._initialized = true;\n          var clipPath = createGridClipShape(coordSys, seriesModel, function () {\n            // Callback will be invoked immediately if there is no animation\n            setTimeout(function () {\n              dataGroup.removeClipPath();\n            });\n          });\n          dataGroup.setClipPath(clipPath);\n        }\n\n        this._data = data;\n      };\n\n      ParallelView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n        this._initialized = true;\n        this._data = null;\n\n        this._dataGroup.removeAll();\n      };\n\n      ParallelView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) {\n        var data = seriesModel.getData();\n        var coordSys = seriesModel.coordinateSystem;\n        var dimensions = coordSys.dimensions;\n        var seriesScope = makeSeriesScope$2(seriesModel);\n        var progressiveEls = this._progressiveEls = [];\n\n        for (var dataIndex = taskParams.start; dataIndex < taskParams.end; dataIndex++) {\n          var line = addEl(data, this._dataGroup, dataIndex, dimensions, coordSys);\n          line.incremental = true;\n          updateElCommon(line, data, dataIndex, seriesScope);\n          progressiveEls.push(line);\n        }\n      };\n\n      ParallelView.prototype.remove = function () {\n        this._dataGroup && this._dataGroup.removeAll();\n        this._data = null;\n      };\n\n      ParallelView.type = 'parallel';\n      return ParallelView;\n    }(ChartView);\n\n    function createGridClipShape(coordSys, seriesModel, cb) {\n      var parallelModel = coordSys.model;\n      var rect = coordSys.getRect();\n      var rectEl = new Rect({\n        shape: {\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height\n        }\n      });\n      var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height';\n      rectEl.setShape(dim, 0);\n      initProps(rectEl, {\n        shape: {\n          width: rect.width,\n          height: rect.height\n        }\n      }, seriesModel, cb);\n      return rectEl;\n    }\n\n    function createLinePoints(data, dataIndex, dimensions, coordSys) {\n      var points = [];\n\n      for (var i = 0; i < dimensions.length; i++) {\n        var dimName = dimensions[i];\n        var value = data.get(data.mapDimension(dimName), dataIndex);\n\n        if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) {\n          points.push(coordSys.dataToPoint(value, dimName));\n        }\n      }\n\n      return points;\n    }\n\n    function addEl(data, dataGroup, dataIndex, dimensions, coordSys) {\n      var points = createLinePoints(data, dataIndex, dimensions, coordSys);\n      var line = new Polyline({\n        shape: {\n          points: points\n        },\n        // silent: true,\n        z2: 10\n      });\n      dataGroup.add(line);\n      data.setItemGraphicEl(dataIndex, line);\n      return line;\n    }\n\n    function makeSeriesScope$2(seriesModel) {\n      var smooth = seriesModel.get('smooth', true);\n      smooth === true && (smooth = DEFAULT_SMOOTH);\n      smooth = numericToNumber(smooth);\n      eqNaN(smooth) && (smooth = 0);\n      return {\n        smooth: smooth\n      };\n    }\n\n    function updateElCommon(el, data, dataIndex, seriesScope) {\n      el.useStyle(data.getItemVisual(dataIndex, 'style'));\n      el.style.fill = null;\n      el.setShape('smooth', seriesScope.smooth);\n      var itemModel = data.getItemModel(dataIndex);\n      var emphasisModel = itemModel.getModel('emphasis');\n      setStatesStylesFromModel(el, itemModel, 'lineStyle');\n      toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n    } // function simpleDiff(oldData, newData, dimensions) {\n    //     let oldLen;\n    //     if (!oldData\n    //         || !oldData.__plProgressive\n    //         || (oldLen = oldData.count()) !== newData.count()\n    //     ) {\n    //         return true;\n    //     }\n    //     let dimLen = dimensions.length;\n    //     for (let i = 0; i < oldLen; i++) {\n    //         for (let j = 0; j < dimLen; j++) {\n    //             if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) {\n    //                 return true;\n    //             }\n    //         }\n    //     }\n    //     return false;\n    // }\n    // FIXME put in common util?\n\n\n    function isEmptyValue(val, axisType) {\n      return axisType === 'category' ? val == null : val == null || isNaN(val); // axisType === 'value'\n    }\n\n    var ParallelSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(ParallelSeriesModel, _super);\n\n      function ParallelSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ParallelSeriesModel.type;\n        _this.visualStyleAccessPath = 'lineStyle';\n        _this.visualDrawType = 'stroke';\n        return _this;\n      }\n\n      ParallelSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: bind(makeDefaultEncode, null, this)\n        });\n      };\n      /**\n       * User can get data raw indices on 'axisAreaSelected' event received.\n       *\n       * @return Raw indices\n       */\n\n\n      ParallelSeriesModel.prototype.getRawIndicesByActiveState = function (activeState) {\n        var coordSys = this.coordinateSystem;\n        var data = this.getData();\n        var indices = [];\n        coordSys.eachActiveState(data, function (theActiveState, dataIndex) {\n          if (activeState === theActiveState) {\n            indices.push(data.getRawIndex(dataIndex));\n          }\n        });\n        return indices;\n      };\n\n      ParallelSeriesModel.type = 'series.parallel';\n      ParallelSeriesModel.dependencies = ['parallel'];\n      ParallelSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'parallel',\n        parallelIndex: 0,\n        label: {\n          show: false\n        },\n        inactiveOpacity: 0.05,\n        activeOpacity: 1,\n        lineStyle: {\n          width: 1,\n          opacity: 0.45,\n          type: 'solid'\n        },\n        emphasis: {\n          label: {\n            show: false\n          }\n        },\n        progressive: 500,\n        smooth: false,\n        animationEasing: 'linear'\n      };\n      return ParallelSeriesModel;\n    }(SeriesModel);\n\n    function makeDefaultEncode(seriesModel) {\n      // The mapping of parallelAxis dimension to data dimension can\n      // be specified in parallelAxis.option.dim. For example, if\n      // parallelAxis.option.dim is 'dim3', it mapping to the third\n      // dimension of data. But `data.encode` has higher priority.\n      // Moreover, parallelModel.dimension should not be regarded as data\n      // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6'];\n      var parallelModel = seriesModel.ecModel.getComponent('parallel', seriesModel.get('parallelIndex'));\n\n      if (!parallelModel) {\n        return;\n      }\n\n      var encodeDefine = {};\n      each(parallelModel.dimensions, function (axisDim) {\n        var dataDimIndex = convertDimNameToNumber(axisDim);\n        encodeDefine[axisDim] = dataDimIndex;\n      });\n      return encodeDefine;\n    }\n\n    function convertDimNameToNumber(dimName) {\n      return +dimName.replace('dim', '');\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var opacityAccessPath$1 = ['lineStyle', 'opacity'];\n    var parallelVisual = {\n      seriesType: 'parallel',\n      reset: function (seriesModel, ecModel) {\n        var coordSys = seriesModel.coordinateSystem;\n        var opacityMap = {\n          normal: seriesModel.get(['lineStyle', 'opacity']),\n          active: seriesModel.get('activeOpacity'),\n          inactive: seriesModel.get('inactiveOpacity')\n        };\n        return {\n          progress: function (params, data) {\n            coordSys.eachActiveState(data, function (activeState, dataIndex) {\n              var opacity = opacityMap[activeState];\n\n              if (activeState === 'normal' && data.hasItemOption) {\n                var itemOpacity = data.getItemModel(dataIndex).get(opacityAccessPath$1, true);\n                itemOpacity != null && (opacity = itemOpacity);\n              }\n\n              var existsStyle = data.ensureUniqueItemVisual(dataIndex, 'style');\n              existsStyle.opacity = opacity;\n            }, params.start, params.end);\n          }\n        };\n      }\n    };\n\n    function parallelPreprocessor(option) {\n      createParallelIfNeeded(option);\n      mergeAxisOptionFromParallel(option);\n    }\n    /**\n     * Create a parallel coordinate if not exists.\n     * @inner\n     */\n\n    function createParallelIfNeeded(option) {\n      if (option.parallel) {\n        return;\n      }\n\n      var hasParallelSeries = false;\n      each(option.series, function (seriesOpt) {\n        if (seriesOpt && seriesOpt.type === 'parallel') {\n          hasParallelSeries = true;\n        }\n      });\n\n      if (hasParallelSeries) {\n        option.parallel = [{}];\n      }\n    }\n    /**\n     * Merge aixs definition from parallel option (if exists) to axis option.\n     * @inner\n     */\n\n\n    function mergeAxisOptionFromParallel(option) {\n      var axes = normalizeToArray(option.parallelAxis);\n      each(axes, function (axisOption) {\n        if (!isObject(axisOption)) {\n          return;\n        }\n\n        var parallelIndex = axisOption.parallelIndex || 0;\n        var parallelOption = normalizeToArray(option.parallel)[parallelIndex];\n\n        if (parallelOption && parallelOption.parallelAxisDefault) {\n          merge(axisOption, parallelOption.parallelAxisDefault, false);\n        }\n      });\n    }\n\n    var CLICK_THRESHOLD = 5; // > 4\n\n    var ParallelView$1 =\n    /** @class */\n    function (_super) {\n      __extends(ParallelView, _super);\n\n      function ParallelView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ParallelView.type;\n        return _this;\n      }\n\n      ParallelView.prototype.render = function (parallelModel, ecModel, api) {\n        this._model = parallelModel;\n        this._api = api;\n\n        if (!this._handlers) {\n          this._handlers = {};\n          each(handlers, function (handler, eventName) {\n            api.getZr().on(eventName, this._handlers[eventName] = bind(handler, this));\n          }, this);\n        }\n\n        createOrUpdate(this, '_throttledDispatchExpand', parallelModel.get('axisExpandRate'), 'fixRate');\n      };\n\n      ParallelView.prototype.dispose = function (ecModel, api) {\n        clear(this, '_throttledDispatchExpand');\n        each(this._handlers, function (handler, eventName) {\n          api.getZr().off(eventName, handler);\n        });\n        this._handlers = null;\n      };\n      /**\n       * @internal\n       * @param {Object} [opt] If null, cancel the last action triggering for debounce.\n       */\n\n\n      ParallelView.prototype._throttledDispatchExpand = function (opt) {\n        this._dispatchExpand(opt);\n      };\n      /**\n       * @internal\n       */\n\n\n      ParallelView.prototype._dispatchExpand = function (opt) {\n        opt && this._api.dispatchAction(extend({\n          type: 'parallelAxisExpand'\n        }, opt));\n      };\n\n      ParallelView.type = 'parallel';\n      return ParallelView;\n    }(ComponentView);\n\n    var handlers = {\n      mousedown: function (e) {\n        if (checkTrigger(this, 'click')) {\n          this._mouseDownPoint = [e.offsetX, e.offsetY];\n        }\n      },\n      mouseup: function (e) {\n        var mouseDownPoint = this._mouseDownPoint;\n\n        if (checkTrigger(this, 'click') && mouseDownPoint) {\n          var point = [e.offsetX, e.offsetY];\n          var dist = Math.pow(mouseDownPoint[0] - point[0], 2) + Math.pow(mouseDownPoint[1] - point[1], 2);\n\n          if (dist > CLICK_THRESHOLD) {\n            return;\n          }\n\n          var result = this._model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX, e.offsetY]);\n\n          result.behavior !== 'none' && this._dispatchExpand({\n            axisExpandWindow: result.axisExpandWindow\n          });\n        }\n\n        this._mouseDownPoint = null;\n      },\n      mousemove: function (e) {\n        // Should do nothing when brushing.\n        if (this._mouseDownPoint || !checkTrigger(this, 'mousemove')) {\n          return;\n        }\n\n        var model = this._model;\n        var result = model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX, e.offsetY]);\n        var behavior = result.behavior;\n        behavior === 'jump' && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce'));\n\n        this._throttledDispatchExpand(behavior === 'none' ? null // Cancel the last trigger, in case that mouse slide out of the area quickly.\n        : {\n          axisExpandWindow: result.axisExpandWindow,\n          // Jumping uses animation, and sliding suppresses animation.\n          animation: behavior === 'jump' ? null : {\n            duration: 0 // Disable animation.\n\n          }\n        });\n      }\n    };\n\n    function checkTrigger(view, triggerOn) {\n      var model = view._model;\n      return model.get('axisExpandable') && model.get('axisExpandTriggerOn') === triggerOn;\n    }\n\n    var ParallelModel =\n    /** @class */\n    function (_super) {\n      __extends(ParallelModel, _super);\n\n      function ParallelModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ParallelModel.type;\n        return _this;\n      }\n\n      ParallelModel.prototype.init = function () {\n        _super.prototype.init.apply(this, arguments);\n\n        this.mergeOption({});\n      };\n\n      ParallelModel.prototype.mergeOption = function (newOption) {\n        var thisOption = this.option;\n        newOption && merge(thisOption, newOption, true);\n\n        this._initDimensions();\n      };\n      /**\n       * Whether series or axis is in this coordinate system.\n       */\n\n\n      ParallelModel.prototype.contains = function (model, ecModel) {\n        var parallelIndex = model.get('parallelIndex');\n        return parallelIndex != null && ecModel.getComponent('parallel', parallelIndex) === this;\n      };\n\n      ParallelModel.prototype.setAxisExpand = function (opt) {\n        each(['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth', 'axisExpandWindow'], function (name) {\n          if (opt.hasOwnProperty(name)) {\n            // @ts-ignore FIXME: why \"never\" inferred in this.option[name]?\n            this.option[name] = opt[name];\n          }\n        }, this);\n      };\n\n      ParallelModel.prototype._initDimensions = function () {\n        var dimensions = this.dimensions = [];\n        var parallelAxisIndex = this.parallelAxisIndex = [];\n        var axisModels = filter(this.ecModel.queryComponents({\n          mainType: 'parallelAxis'\n        }), function (axisModel) {\n          // Can not use this.contains here, because\n          // initialization has not been completed yet.\n          return (axisModel.get('parallelIndex') || 0) === this.componentIndex;\n        }, this);\n        each(axisModels, function (axisModel) {\n          dimensions.push('dim' + axisModel.get('dim'));\n          parallelAxisIndex.push(axisModel.componentIndex);\n        });\n      };\n\n      ParallelModel.type = 'parallel';\n      ParallelModel.dependencies = ['parallelAxis'];\n      ParallelModel.layoutMode = 'box';\n      ParallelModel.defaultOption = {\n        // zlevel: 0,\n        z: 0,\n        left: 80,\n        top: 60,\n        right: 80,\n        bottom: 60,\n        // width: {totalWidth} - left - right,\n        // height: {totalHeight} - top - bottom,\n        layout: 'horizontal',\n        // FIXME\n        // naming?\n        axisExpandable: false,\n        axisExpandCenter: null,\n        axisExpandCount: 0,\n        axisExpandWidth: 50,\n        axisExpandRate: 17,\n        axisExpandDebounce: 50,\n        // [out, in, jumpTarget]. In percentage. If use [null, 0.05], null means full.\n        // Do not doc to user until necessary.\n        axisExpandSlideTriggerArea: [-0.15, 0.05, 0.4],\n        axisExpandTriggerOn: 'click',\n        parallelAxisDefault: null\n      };\n      return ParallelModel;\n    }(ComponentModel);\n\n    var ParallelAxis =\n    /** @class */\n    function (_super) {\n      __extends(ParallelAxis, _super);\n\n      function ParallelAxis(dim, scale, coordExtent, axisType, axisIndex) {\n        var _this = _super.call(this, dim, scale, coordExtent) || this;\n\n        _this.type = axisType || 'value';\n        _this.axisIndex = axisIndex;\n        return _this;\n      }\n\n      ParallelAxis.prototype.isHorizontal = function () {\n        return this.coordinateSystem.getModel().get('layout') !== 'horizontal';\n      };\n\n      return ParallelAxis;\n    }(Axis);\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * Calculate slider move result.\n     * Usage:\n     * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as\n     * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`.\n     * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`.\n     *\n     * @param delta Move length.\n     * @param handleEnds handleEnds[0] can be bigger then handleEnds[1].\n     *              handleEnds will be modified in this method.\n     * @param extent handleEnds is restricted by extent.\n     *              extent[0] should less or equals than extent[1].\n     * @param handleIndex Can be 'all', means that both move the two handleEnds.\n     * @param minSpan The range of dataZoom can not be smaller than that.\n     *              If not set, handle0 and cross handle1. If set as a non-negative\n     *              number (including `0`), handles will push each other when reaching\n     *              the minSpan.\n     * @param maxSpan The range of dataZoom can not be larger than that.\n     * @return The input handleEnds.\n     */\n    function sliderMove(delta, handleEnds, extent, handleIndex, minSpan, maxSpan) {\n      delta = delta || 0;\n      var extentSpan = extent[1] - extent[0]; // Notice maxSpan and minSpan can be null/undefined.\n\n      if (minSpan != null) {\n        minSpan = restrict(minSpan, [0, extentSpan]);\n      }\n\n      if (maxSpan != null) {\n        maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0);\n      }\n\n      if (handleIndex === 'all') {\n        var handleSpan = Math.abs(handleEnds[1] - handleEnds[0]);\n        handleSpan = restrict(handleSpan, [0, extentSpan]);\n        minSpan = maxSpan = restrict(handleSpan, [minSpan, maxSpan]);\n        handleIndex = 0;\n      }\n\n      handleEnds[0] = restrict(handleEnds[0], extent);\n      handleEnds[1] = restrict(handleEnds[1], extent);\n      var originalDistSign = getSpanSign(handleEnds, handleIndex);\n      handleEnds[handleIndex] += delta; // Restrict in extent.\n\n      var extentMinSpan = minSpan || 0;\n      var realExtent = extent.slice();\n      originalDistSign.sign < 0 ? realExtent[0] += extentMinSpan : realExtent[1] -= extentMinSpan;\n      handleEnds[handleIndex] = restrict(handleEnds[handleIndex], realExtent); // Expand span.\n\n      var currDistSign;\n      currDistSign = getSpanSign(handleEnds, handleIndex);\n\n      if (minSpan != null && (currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan)) {\n        // If minSpan exists, 'cross' is forbidden.\n        handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan;\n      } // Shrink span.\n\n\n      currDistSign = getSpanSign(handleEnds, handleIndex);\n\n      if (maxSpan != null && currDistSign.span > maxSpan) {\n        handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan;\n      }\n\n      return handleEnds;\n    }\n\n    function getSpanSign(handleEnds, handleIndex) {\n      var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex]; // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0]\n      // is at left of handleEnds[1] for non-cross case.\n\n      return {\n        span: Math.abs(dist),\n        sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1\n      };\n    }\n\n    function restrict(value, extend) {\n      return Math.min(extend[1] != null ? extend[1] : Infinity, Math.max(extend[0] != null ? extend[0] : -Infinity, value));\n    }\n\n    var each$5 = each;\n    var mathMin$8 = Math.min;\n    var mathMax$8 = Math.max;\n    var mathFloor$1 = Math.floor;\n    var mathCeil$1 = Math.ceil;\n    var round$3 = round;\n    var PI$7 = Math.PI;\n\n    var Parallel =\n    /** @class */\n    function () {\n      function Parallel(parallelModel, ecModel, api) {\n        this.type = 'parallel';\n        /**\n         * key: dimension\n         */\n\n        this._axesMap = createHashMap();\n        /**\n         * key: dimension\n         * value: {position: [], rotation, }\n         */\n\n        this._axesLayout = {};\n        this.dimensions = parallelModel.dimensions;\n        this._model = parallelModel;\n\n        this._init(parallelModel, ecModel, api);\n      }\n\n      Parallel.prototype._init = function (parallelModel, ecModel, api) {\n        var dimensions = parallelModel.dimensions;\n        var parallelAxisIndex = parallelModel.parallelAxisIndex;\n        each$5(dimensions, function (dim, idx) {\n          var axisIndex = parallelAxisIndex[idx];\n          var axisModel = ecModel.getComponent('parallelAxis', axisIndex);\n\n          var axis = this._axesMap.set(dim, new ParallelAxis(dim, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisIndex));\n\n          var isCategory = axis.type === 'category';\n          axis.onBand = isCategory && axisModel.get('boundaryGap');\n          axis.inverse = axisModel.get('inverse'); // Injection\n\n          axisModel.axis = axis;\n          axis.model = axisModel;\n          axis.coordinateSystem = axisModel.coordinateSystem = this;\n        }, this);\n      };\n      /**\n       * Update axis scale after data processed\n       */\n\n\n      Parallel.prototype.update = function (ecModel, api) {\n        this._updateAxesFromSeries(this._model, ecModel);\n      };\n\n      Parallel.prototype.containPoint = function (point) {\n        var layoutInfo = this._makeLayoutInfo();\n\n        var axisBase = layoutInfo.axisBase;\n        var layoutBase = layoutInfo.layoutBase;\n        var pixelDimIndex = layoutInfo.pixelDimIndex;\n        var pAxis = point[1 - pixelDimIndex];\n        var pLayout = point[pixelDimIndex];\n        return pAxis >= axisBase && pAxis <= axisBase + layoutInfo.axisLength && pLayout >= layoutBase && pLayout <= layoutBase + layoutInfo.layoutLength;\n      };\n\n      Parallel.prototype.getModel = function () {\n        return this._model;\n      };\n      /**\n       * Update properties from series\n       */\n\n\n      Parallel.prototype._updateAxesFromSeries = function (parallelModel, ecModel) {\n        ecModel.eachSeries(function (seriesModel) {\n          if (!parallelModel.contains(seriesModel, ecModel)) {\n            return;\n          }\n\n          var data = seriesModel.getData();\n          each$5(this.dimensions, function (dim) {\n            var axis = this._axesMap.get(dim);\n\n            axis.scale.unionExtentFromData(data, data.mapDimension(dim));\n            niceScaleExtent(axis.scale, axis.model);\n          }, this);\n        }, this);\n      };\n      /**\n       * Resize the parallel coordinate system.\n       */\n\n\n      Parallel.prototype.resize = function (parallelModel, api) {\n        this._rect = getLayoutRect(parallelModel.getBoxLayoutParams(), {\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n\n        this._layoutAxes();\n      };\n\n      Parallel.prototype.getRect = function () {\n        return this._rect;\n      };\n\n      Parallel.prototype._makeLayoutInfo = function () {\n        var parallelModel = this._model;\n        var rect = this._rect;\n        var xy = ['x', 'y'];\n        var wh = ['width', 'height'];\n        var layout = parallelModel.get('layout');\n        var pixelDimIndex = layout === 'horizontal' ? 0 : 1;\n        var layoutLength = rect[wh[pixelDimIndex]];\n        var layoutExtent = [0, layoutLength];\n        var axisCount = this.dimensions.length;\n        var axisExpandWidth = restrict$1(parallelModel.get('axisExpandWidth'), layoutExtent);\n        var axisExpandCount = restrict$1(parallelModel.get('axisExpandCount') || 0, [0, axisCount]);\n        var axisExpandable = parallelModel.get('axisExpandable') && axisCount > 3 && axisCount > axisExpandCount && axisExpandCount > 1 && axisExpandWidth > 0 && layoutLength > 0; // `axisExpandWindow` is According to the coordinates of [0, axisExpandLength],\n        // for sake of consider the case that axisCollapseWidth is 0 (when screen is narrow),\n        // where collapsed axes should be overlapped.\n\n        var axisExpandWindow = parallelModel.get('axisExpandWindow');\n        var winSize;\n\n        if (!axisExpandWindow) {\n          winSize = restrict$1(axisExpandWidth * (axisExpandCount - 1), layoutExtent);\n          var axisExpandCenter = parallelModel.get('axisExpandCenter') || mathFloor$1(axisCount / 2);\n          axisExpandWindow = [axisExpandWidth * axisExpandCenter - winSize / 2];\n          axisExpandWindow[1] = axisExpandWindow[0] + winSize;\n        } else {\n          winSize = restrict$1(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent);\n          axisExpandWindow[1] = axisExpandWindow[0] + winSize;\n        }\n\n        var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount); // Avoid axisCollapseWidth is too small.\n\n        axisCollapseWidth < 3 && (axisCollapseWidth = 0); // Find the first and last indices > ewin[0] and < ewin[1].\n\n        var winInnerIndices = [mathFloor$1(round$3(axisExpandWindow[0] / axisExpandWidth, 1)) + 1, mathCeil$1(round$3(axisExpandWindow[1] / axisExpandWidth, 1)) - 1]; // Pos in ec coordinates.\n\n        var axisExpandWindow0Pos = axisCollapseWidth / axisExpandWidth * axisExpandWindow[0];\n        return {\n          layout: layout,\n          pixelDimIndex: pixelDimIndex,\n          layoutBase: rect[xy[pixelDimIndex]],\n          layoutLength: layoutLength,\n          axisBase: rect[xy[1 - pixelDimIndex]],\n          axisLength: rect[wh[1 - pixelDimIndex]],\n          axisExpandable: axisExpandable,\n          axisExpandWidth: axisExpandWidth,\n          axisCollapseWidth: axisCollapseWidth,\n          axisExpandWindow: axisExpandWindow,\n          axisCount: axisCount,\n          winInnerIndices: winInnerIndices,\n          axisExpandWindow0Pos: axisExpandWindow0Pos\n        };\n      };\n\n      Parallel.prototype._layoutAxes = function () {\n        var rect = this._rect;\n        var axes = this._axesMap;\n        var dimensions = this.dimensions;\n\n        var layoutInfo = this._makeLayoutInfo();\n\n        var layout = layoutInfo.layout;\n        axes.each(function (axis) {\n          var axisExtent = [0, layoutInfo.axisLength];\n          var idx = axis.inverse ? 1 : 0;\n          axis.setExtent(axisExtent[idx], axisExtent[1 - idx]);\n        });\n        each$5(dimensions, function (dim, idx) {\n          var posInfo = (layoutInfo.axisExpandable ? layoutAxisWithExpand : layoutAxisWithoutExpand)(idx, layoutInfo);\n          var positionTable = {\n            horizontal: {\n              x: posInfo.position,\n              y: layoutInfo.axisLength\n            },\n            vertical: {\n              x: 0,\n              y: posInfo.position\n            }\n          };\n          var rotationTable = {\n            horizontal: PI$7 / 2,\n            vertical: 0\n          };\n          var position = [positionTable[layout].x + rect.x, positionTable[layout].y + rect.y];\n          var rotation = rotationTable[layout];\n          var transform = create$1();\n          rotate(transform, transform, rotation);\n          translate(transform, transform, position); // TODO\n          // tick layout info\n          // TODO\n          // update dimensions info based on axis order.\n\n          this._axesLayout[dim] = {\n            position: position,\n            rotation: rotation,\n            transform: transform,\n            axisNameAvailableWidth: posInfo.axisNameAvailableWidth,\n            axisLabelShow: posInfo.axisLabelShow,\n            nameTruncateMaxWidth: posInfo.nameTruncateMaxWidth,\n            tickDirection: 1,\n            labelDirection: 1\n          };\n        }, this);\n      };\n      /**\n       * Get axis by dim.\n       */\n\n\n      Parallel.prototype.getAxis = function (dim) {\n        return this._axesMap.get(dim);\n      };\n      /**\n       * Convert a dim value of a single item of series data to Point.\n       */\n\n\n      Parallel.prototype.dataToPoint = function (value, dim) {\n        return this.axisCoordToPoint(this._axesMap.get(dim).dataToCoord(value), dim);\n      };\n      /**\n       * Travel data for one time, get activeState of each data item.\n       * @param start the start dataIndex that travel from.\n       * @param end the next dataIndex of the last dataIndex will be travel.\n       */\n\n\n      Parallel.prototype.eachActiveState = function (data, callback, start, end) {\n        start == null && (start = 0);\n        end == null && (end = data.count());\n        var axesMap = this._axesMap;\n        var dimensions = this.dimensions;\n        var dataDimensions = [];\n        var axisModels = [];\n        each(dimensions, function (axisDim) {\n          dataDimensions.push(data.mapDimension(axisDim));\n          axisModels.push(axesMap.get(axisDim).model);\n        });\n        var hasActiveSet = this.hasAxisBrushed();\n\n        for (var dataIndex = start; dataIndex < end; dataIndex++) {\n          var activeState = void 0;\n\n          if (!hasActiveSet) {\n            activeState = 'normal';\n          } else {\n            activeState = 'active';\n            var values = data.getValues(dataDimensions, dataIndex);\n\n            for (var j = 0, lenj = dimensions.length; j < lenj; j++) {\n              var state = axisModels[j].getActiveState(values[j]);\n\n              if (state === 'inactive') {\n                activeState = 'inactive';\n                break;\n              }\n            }\n          }\n\n          callback(activeState, dataIndex);\n        }\n      };\n      /**\n       * Whether has any activeSet.\n       */\n\n\n      Parallel.prototype.hasAxisBrushed = function () {\n        var dimensions = this.dimensions;\n        var axesMap = this._axesMap;\n        var hasActiveSet = false;\n\n        for (var j = 0, lenj = dimensions.length; j < lenj; j++) {\n          if (axesMap.get(dimensions[j]).model.getActiveState() !== 'normal') {\n            hasActiveSet = true;\n          }\n        }\n\n        return hasActiveSet;\n      };\n      /**\n       * Convert coords of each axis to Point.\n       *  Return point. For example: [10, 20]\n       */\n\n\n      Parallel.prototype.axisCoordToPoint = function (coord, dim) {\n        var axisLayout = this._axesLayout[dim];\n        return applyTransform$1([coord, 0], axisLayout.transform);\n      };\n      /**\n       * Get axis layout.\n       */\n\n\n      Parallel.prototype.getAxisLayout = function (dim) {\n        return clone(this._axesLayout[dim]);\n      };\n      /**\n       * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}.\n       */\n\n\n      Parallel.prototype.getSlidedAxisExpandWindow = function (point) {\n        var layoutInfo = this._makeLayoutInfo();\n\n        var pixelDimIndex = layoutInfo.pixelDimIndex;\n        var axisExpandWindow = layoutInfo.axisExpandWindow.slice();\n        var winSize = axisExpandWindow[1] - axisExpandWindow[0];\n        var extent = [0, layoutInfo.axisExpandWidth * (layoutInfo.axisCount - 1)]; // Out of the area of coordinate system.\n\n        if (!this.containPoint(point)) {\n          return {\n            behavior: 'none',\n            axisExpandWindow: axisExpandWindow\n          };\n        } // Convert the point from global to expand coordinates.\n\n\n        var pointCoord = point[pixelDimIndex] - layoutInfo.layoutBase - layoutInfo.axisExpandWindow0Pos; // For dragging operation convenience, the window should not be\n        // slided when mouse is the center area of the window.\n\n        var delta;\n        var behavior = 'slide';\n        var axisCollapseWidth = layoutInfo.axisCollapseWidth;\n\n        var triggerArea = this._model.get('axisExpandSlideTriggerArea'); // But consider touch device, jump is necessary.\n\n\n        var useJump = triggerArea[0] != null;\n\n        if (axisCollapseWidth) {\n          if (useJump && axisCollapseWidth && pointCoord < winSize * triggerArea[0]) {\n            behavior = 'jump';\n            delta = pointCoord - winSize * triggerArea[2];\n          } else if (useJump && axisCollapseWidth && pointCoord > winSize * (1 - triggerArea[0])) {\n            behavior = 'jump';\n            delta = pointCoord - winSize * (1 - triggerArea[2]);\n          } else {\n            (delta = pointCoord - winSize * triggerArea[1]) >= 0 && (delta = pointCoord - winSize * (1 - triggerArea[1])) <= 0 && (delta = 0);\n          }\n\n          delta *= layoutInfo.axisExpandWidth / axisCollapseWidth;\n          delta ? sliderMove(delta, axisExpandWindow, extent, 'all') // Avoid nonsense triger on mousemove.\n          : behavior = 'none';\n        } // When screen is too narrow, make it visible and slidable, although it is hard to interact.\n        else {\n            var winSize2 = axisExpandWindow[1] - axisExpandWindow[0];\n            var pos = extent[1] * pointCoord / winSize2;\n            axisExpandWindow = [mathMax$8(0, pos - winSize2 / 2)];\n            axisExpandWindow[1] = mathMin$8(extent[1], axisExpandWindow[0] + winSize2);\n            axisExpandWindow[0] = axisExpandWindow[1] - winSize2;\n          }\n\n        return {\n          axisExpandWindow: axisExpandWindow,\n          behavior: behavior\n        };\n      };\n\n      return Parallel;\n    }();\n\n    function restrict$1(len, extent) {\n      return mathMin$8(mathMax$8(len, extent[0]), extent[1]);\n    }\n\n    function layoutAxisWithoutExpand(axisIndex, layoutInfo) {\n      var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1);\n      return {\n        position: step * axisIndex,\n        axisNameAvailableWidth: step,\n        axisLabelShow: true\n      };\n    }\n\n    function layoutAxisWithExpand(axisIndex, layoutInfo) {\n      var layoutLength = layoutInfo.layoutLength;\n      var axisExpandWidth = layoutInfo.axisExpandWidth;\n      var axisCount = layoutInfo.axisCount;\n      var axisCollapseWidth = layoutInfo.axisCollapseWidth;\n      var winInnerIndices = layoutInfo.winInnerIndices;\n      var position;\n      var axisNameAvailableWidth = axisCollapseWidth;\n      var axisLabelShow = false;\n      var nameTruncateMaxWidth;\n\n      if (axisIndex < winInnerIndices[0]) {\n        position = axisIndex * axisCollapseWidth;\n        nameTruncateMaxWidth = axisCollapseWidth;\n      } else if (axisIndex <= winInnerIndices[1]) {\n        position = layoutInfo.axisExpandWindow0Pos + axisIndex * axisExpandWidth - layoutInfo.axisExpandWindow[0];\n        axisNameAvailableWidth = axisExpandWidth;\n        axisLabelShow = true;\n      } else {\n        position = layoutLength - (axisCount - 1 - axisIndex) * axisCollapseWidth;\n        nameTruncateMaxWidth = axisCollapseWidth;\n      }\n\n      return {\n        position: position,\n        axisNameAvailableWidth: axisNameAvailableWidth,\n        axisLabelShow: axisLabelShow,\n        nameTruncateMaxWidth: nameTruncateMaxWidth\n      };\n    }\n\n    function createParallelCoordSys(ecModel, api) {\n      var coordSysList = [];\n      ecModel.eachComponent('parallel', function (parallelModel, idx) {\n        var coordSys = new Parallel(parallelModel, ecModel, api);\n        coordSys.name = 'parallel_' + idx;\n        coordSys.resize(parallelModel, api);\n        parallelModel.coordinateSystem = coordSys;\n        coordSys.model = parallelModel;\n        coordSysList.push(coordSys);\n      }); // Inject the coordinateSystems into seriesModel\n\n      ecModel.eachSeries(function (seriesModel) {\n        if (seriesModel.get('coordinateSystem') === 'parallel') {\n          var parallelModel = seriesModel.getReferringComponents('parallel', SINGLE_REFERRING).models[0];\n          seriesModel.coordinateSystem = parallelModel.coordinateSystem;\n        }\n      });\n      return coordSysList;\n    }\n\n    var parallelCoordSysCreator = {\n      create: createParallelCoordSys\n    };\n\n    var ParallelAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(ParallelAxisModel, _super);\n\n      function ParallelAxisModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ParallelAxisModel.type;\n        /**\n         * @readOnly\n         */\n\n        _this.activeIntervals = [];\n        return _this;\n      }\n\n      ParallelAxisModel.prototype.getAreaSelectStyle = function () {\n        return makeStyleMapper([['fill', 'color'], ['lineWidth', 'borderWidth'], ['stroke', 'borderColor'], ['width', 'width'], ['opacity', 'opacity'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n        // So do not transfer decal directly.\n        ])(this.getModel('areaSelectStyle'));\n      };\n      /**\n       * The code of this feature is put on AxisModel but not ParallelAxis,\n       * because axisModel can be alive after echarts updating but instance of\n       * ParallelAxis having been disposed. this._activeInterval should be kept\n       * when action dispatched (i.e. legend click).\n       *\n       * @param intervals `interval.length === 0` means set all active.\n       */\n\n\n      ParallelAxisModel.prototype.setActiveIntervals = function (intervals) {\n        var activeIntervals = this.activeIntervals = clone(intervals); // Normalize\n\n        if (activeIntervals) {\n          for (var i = activeIntervals.length - 1; i >= 0; i--) {\n            asc(activeIntervals[i]);\n          }\n        }\n      };\n      /**\n       * @param value When only attempting detect whether 'no activeIntervals set',\n       *        `value` is not needed to be input.\n       */\n\n\n      ParallelAxisModel.prototype.getActiveState = function (value) {\n        var activeIntervals = this.activeIntervals;\n\n        if (!activeIntervals.length) {\n          return 'normal';\n        }\n\n        if (value == null || isNaN(+value)) {\n          return 'inactive';\n        } // Simple optimization\n\n\n        if (activeIntervals.length === 1) {\n          var interval = activeIntervals[0];\n\n          if (interval[0] <= value && value <= interval[1]) {\n            return 'active';\n          }\n        } else {\n          for (var i = 0, len = activeIntervals.length; i < len; i++) {\n            if (activeIntervals[i][0] <= value && value <= activeIntervals[i][1]) {\n              return 'active';\n            }\n          }\n        }\n\n        return 'inactive';\n      };\n\n      return ParallelAxisModel;\n    }(ComponentModel);\n\n    mixin(ParallelAxisModel, AxisModelCommonMixin);\n\n    var BRUSH_PANEL_GLOBAL = true;\n    var mathMin$9 = Math.min;\n    var mathMax$9 = Math.max;\n    var mathPow$2 = Math.pow;\n    var COVER_Z = 10000;\n    var UNSELECT_THRESHOLD = 6;\n    var MIN_RESIZE_LINE_WIDTH = 6;\n    var MUTEX_RESOURCE_KEY = 'globalPan';\n    var DIRECTION_MAP = {\n      w: [0, 0],\n      e: [0, 1],\n      n: [1, 0],\n      s: [1, 1]\n    };\n    var CURSOR_MAP = {\n      w: 'ew',\n      e: 'ew',\n      n: 'ns',\n      s: 'ns',\n      ne: 'nesw',\n      sw: 'nesw',\n      nw: 'nwse',\n      se: 'nwse'\n    };\n    var DEFAULT_BRUSH_OPT = {\n      brushStyle: {\n        lineWidth: 2,\n        stroke: 'rgba(210,219,238,0.3)',\n        fill: '#D2DBEE'\n      },\n      transformable: true,\n      brushMode: 'single',\n      removeOnClick: false\n    };\n    var baseUID = 0;\n    /**\n     * params:\n     *     areas: Array.<Array>, coord relates to container group,\n     *                             If no container specified, to global.\n     *     opt {\n     *         isEnd: boolean,\n     *         removeOnClick: boolean\n     *     }\n     */\n\n    var BrushController =\n    /** @class */\n    function (_super) {\n      __extends(BrushController, _super);\n\n      function BrushController(zr) {\n        var _this = _super.call(this) || this;\n        /**\n         * @internal\n         */\n\n\n        _this._track = [];\n        /**\n         * @internal\n         */\n\n        _this._covers = [];\n        _this._handlers = {};\n\n        if (\"development\" !== 'production') {\n          assert(zr);\n        }\n\n        _this._zr = zr;\n        _this.group = new Group();\n        _this._uid = 'brushController_' + baseUID++;\n        each(pointerHandlers, function (handler, eventName) {\n          this._handlers[eventName] = bind(handler, this);\n        }, _this);\n        return _this;\n      }\n      /**\n       * If set to `false`, select disabled.\n       */\n\n\n      BrushController.prototype.enableBrush = function (brushOption) {\n        if (\"development\" !== 'production') {\n          assert(this._mounted);\n        }\n\n        this._brushType && this._doDisableBrush();\n        brushOption.brushType && this._doEnableBrush(brushOption);\n        return this;\n      };\n\n      BrushController.prototype._doEnableBrush = function (brushOption) {\n        var zr = this._zr; // Consider roam, which takes globalPan too.\n\n        if (!this._enableGlobalPan) {\n          take(zr, MUTEX_RESOURCE_KEY, this._uid);\n        }\n\n        each(this._handlers, function (handler, eventName) {\n          zr.on(eventName, handler);\n        });\n        this._brushType = brushOption.brushType;\n        this._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true);\n      };\n\n      BrushController.prototype._doDisableBrush = function () {\n        var zr = this._zr;\n        release(zr, MUTEX_RESOURCE_KEY, this._uid);\n        each(this._handlers, function (handler, eventName) {\n          zr.off(eventName, handler);\n        });\n        this._brushType = this._brushOption = null;\n      };\n      /**\n       * @param panelOpts If not pass, it is global brush.\n       */\n\n\n      BrushController.prototype.setPanels = function (panelOpts) {\n        if (panelOpts && panelOpts.length) {\n          var panels_1 = this._panels = {};\n          each(panelOpts, function (panelOpts) {\n            panels_1[panelOpts.panelId] = clone(panelOpts);\n          });\n        } else {\n          this._panels = null;\n        }\n\n        return this;\n      };\n\n      BrushController.prototype.mount = function (opt) {\n        opt = opt || {};\n\n        if (\"development\" !== 'production') {\n          this._mounted = true; // should be at first.\n        }\n\n        this._enableGlobalPan = opt.enableGlobalPan;\n        var thisGroup = this.group;\n\n        this._zr.add(thisGroup);\n\n        thisGroup.attr({\n          x: opt.x || 0,\n          y: opt.y || 0,\n          rotation: opt.rotation || 0,\n          scaleX: opt.scaleX || 1,\n          scaleY: opt.scaleY || 1\n        });\n        this._transform = thisGroup.getLocalTransform();\n        return this;\n      }; // eachCover(cb, context): void {\n      //     each(this._covers, cb, context);\n      // }\n\n      /**\n       * Update covers.\n       * @param coverConfigList\n       *        If coverConfigList is null/undefined, all covers removed.\n       */\n\n\n      BrushController.prototype.updateCovers = function (coverConfigList) {\n        if (\"development\" !== 'production') {\n          assert(this._mounted);\n        }\n\n        coverConfigList = map(coverConfigList, function (coverConfig) {\n          return merge(clone(DEFAULT_BRUSH_OPT), coverConfig, true);\n        });\n        var tmpIdPrefix = '\\0-brush-index-';\n        var oldCovers = this._covers;\n        var newCovers = this._covers = [];\n        var controller = this;\n        var creatingCover = this._creatingCover;\n        new DataDiffer(oldCovers, coverConfigList, oldGetKey, getKey).add(addOrUpdate).update(addOrUpdate).remove(remove).execute();\n        return this;\n\n        function getKey(brushOption, index) {\n          return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + '-' + brushOption.brushType;\n        }\n\n        function oldGetKey(cover, index) {\n          return getKey(cover.__brushOption, index);\n        }\n\n        function addOrUpdate(newIndex, oldIndex) {\n          var newBrushInternal = coverConfigList[newIndex]; // Consider setOption in event listener of brushSelect,\n          // where updating cover when creating should be forbiden.\n\n          if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {\n            newCovers[newIndex] = oldCovers[oldIndex];\n          } else {\n            var cover = newCovers[newIndex] = oldIndex != null ? (oldCovers[oldIndex].__brushOption = newBrushInternal, oldCovers[oldIndex]) : endCreating(controller, createCover(controller, newBrushInternal));\n            updateCoverAfterCreation(controller, cover);\n          }\n        }\n\n        function remove(oldIndex) {\n          if (oldCovers[oldIndex] !== creatingCover) {\n            controller.group.remove(oldCovers[oldIndex]);\n          }\n        }\n      };\n\n      BrushController.prototype.unmount = function () {\n        if (\"development\" !== 'production') {\n          if (!this._mounted) {\n            return;\n          }\n        }\n\n        this.enableBrush(false); // container may 'removeAll' outside.\n\n        clearCovers(this);\n\n        this._zr.remove(this.group);\n\n        if (\"development\" !== 'production') {\n          this._mounted = false; // should be at last.\n        }\n\n        return this;\n      };\n\n      BrushController.prototype.dispose = function () {\n        this.unmount();\n        this.off();\n      };\n\n      return BrushController;\n    }(Eventful);\n\n    function createCover(controller, brushOption) {\n      var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);\n      cover.__brushOption = brushOption;\n      updateZ(cover, brushOption);\n      controller.group.add(cover);\n      return cover;\n    }\n\n    function endCreating(controller, creatingCover) {\n      var coverRenderer = getCoverRenderer(creatingCover);\n\n      if (coverRenderer.endCreating) {\n        coverRenderer.endCreating(controller, creatingCover);\n        updateZ(creatingCover, creatingCover.__brushOption);\n      }\n\n      return creatingCover;\n    }\n\n    function updateCoverShape(controller, cover) {\n      var brushOption = cover.__brushOption;\n      getCoverRenderer(cover).updateCoverShape(controller, cover, brushOption.range, brushOption);\n    }\n\n    function updateZ(cover, brushOption) {\n      var z = brushOption.z;\n      z == null && (z = COVER_Z);\n      cover.traverse(function (el) {\n        el.z = z;\n        el.z2 = z; // Consider in given container.\n      });\n    }\n\n    function updateCoverAfterCreation(controller, cover) {\n      getCoverRenderer(cover).updateCommon(controller, cover);\n      updateCoverShape(controller, cover);\n    }\n\n    function getCoverRenderer(cover) {\n      return coverRenderers[cover.__brushOption.brushType];\n    } // return target panel or `true` (means global panel)\n\n\n    function getPanelByPoint(controller, e, localCursorPoint) {\n      var panels = controller._panels;\n\n      if (!panels) {\n        return BRUSH_PANEL_GLOBAL; // Global panel\n      }\n\n      var panel;\n      var transform = controller._transform;\n      each(panels, function (pn) {\n        pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn);\n      });\n      return panel;\n    } // Return a panel or true\n\n\n    function getPanelByCover(controller, cover) {\n      var panels = controller._panels;\n\n      if (!panels) {\n        return BRUSH_PANEL_GLOBAL; // Global panel\n      }\n\n      var panelId = cover.__brushOption.panelId; // User may give cover without coord sys info,\n      // which is then treated as global panel.\n\n      return panelId != null ? panels[panelId] : BRUSH_PANEL_GLOBAL;\n    }\n\n    function clearCovers(controller) {\n      var covers = controller._covers;\n      var originalLength = covers.length;\n      each(covers, function (cover) {\n        controller.group.remove(cover);\n      }, controller);\n      covers.length = 0;\n      return !!originalLength;\n    }\n\n    function trigger$1(controller, opt) {\n      var areas = map(controller._covers, function (cover) {\n        var brushOption = cover.__brushOption;\n        var range = clone(brushOption.range);\n        return {\n          brushType: brushOption.brushType,\n          panelId: brushOption.panelId,\n          range: range\n        };\n      });\n      controller.trigger('brush', {\n        areas: areas,\n        isEnd: !!opt.isEnd,\n        removeOnClick: !!opt.removeOnClick\n      });\n    }\n\n    function shouldShowCover(controller) {\n      var track = controller._track;\n\n      if (!track.length) {\n        return false;\n      }\n\n      var p2 = track[track.length - 1];\n      var p1 = track[0];\n      var dx = p2[0] - p1[0];\n      var dy = p2[1] - p1[1];\n      var dist = mathPow$2(dx * dx + dy * dy, 0.5);\n      return dist > UNSELECT_THRESHOLD;\n    }\n\n    function getTrackEnds(track) {\n      var tail = track.length - 1;\n      tail < 0 && (tail = 0);\n      return [track[0], track[tail]];\n    }\n\n    function createBaseRectCover(rectRangeConverter, controller, brushOption, edgeNameSequences) {\n      var cover = new Group();\n      cover.add(new Rect({\n        name: 'main',\n        style: makeStyle(brushOption),\n        silent: true,\n        draggable: true,\n        cursor: 'move',\n        drift: curry(driftRect, rectRangeConverter, controller, cover, ['n', 's', 'w', 'e']),\n        ondragend: curry(trigger$1, controller, {\n          isEnd: true\n        })\n      }));\n      each(edgeNameSequences, function (nameSequence) {\n        cover.add(new Rect({\n          name: nameSequence.join(''),\n          style: {\n            opacity: 0\n          },\n          draggable: true,\n          silent: true,\n          invisible: true,\n          drift: curry(driftRect, rectRangeConverter, controller, cover, nameSequence),\n          ondragend: curry(trigger$1, controller, {\n            isEnd: true\n          })\n        }));\n      });\n      return cover;\n    }\n\n    function updateBaseRect(controller, cover, localRange, brushOption) {\n      var lineWidth = brushOption.brushStyle.lineWidth || 0;\n      var handleSize = mathMax$9(lineWidth, MIN_RESIZE_LINE_WIDTH);\n      var x = localRange[0][0];\n      var y = localRange[1][0];\n      var xa = x - lineWidth / 2;\n      var ya = y - lineWidth / 2;\n      var x2 = localRange[0][1];\n      var y2 = localRange[1][1];\n      var x2a = x2 - handleSize + lineWidth / 2;\n      var y2a = y2 - handleSize + lineWidth / 2;\n      var width = x2 - x;\n      var height = y2 - y;\n      var widtha = width + lineWidth;\n      var heighta = height + lineWidth;\n      updateRectShape(controller, cover, 'main', x, y, width, height);\n\n      if (brushOption.transformable) {\n        updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);\n        updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);\n        updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);\n        updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);\n        updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);\n        updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);\n        updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);\n        updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);\n      }\n    }\n\n    function updateCommon(controller, cover) {\n      var brushOption = cover.__brushOption;\n      var transformable = brushOption.transformable;\n      var mainEl = cover.childAt(0);\n      mainEl.useStyle(makeStyle(brushOption));\n      mainEl.attr({\n        silent: !transformable,\n        cursor: transformable ? 'move' : 'default'\n      });\n      each([['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']], function (nameSequence) {\n        var el = cover.childOfName(nameSequence.join(''));\n        var globalDir = nameSequence.length === 1 ? getGlobalDirection1(controller, nameSequence[0]) : getGlobalDirection2(controller, nameSequence);\n        el && el.attr({\n          silent: !transformable,\n          invisible: !transformable,\n          cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null\n        });\n      });\n    }\n\n    function updateRectShape(controller, cover, name, x, y, w, h) {\n      var el = cover.childOfName(name);\n      el && el.setShape(pointsToRect(clipByPanel(controller, cover, [[x, y], [x + w, y + h]])));\n    }\n\n    function makeStyle(brushOption) {\n      return defaults({\n        strokeNoScale: true\n      }, brushOption.brushStyle);\n    }\n\n    function formatRectRange(x, y, x2, y2) {\n      var min = [mathMin$9(x, x2), mathMin$9(y, y2)];\n      var max = [mathMax$9(x, x2), mathMax$9(y, y2)];\n      return [[min[0], max[0]], [min[1], max[1]] // y range\n      ];\n    }\n\n    function getTransform$1(controller) {\n      return getTransform(controller.group);\n    }\n\n    function getGlobalDirection1(controller, localDirName) {\n      var map = {\n        w: 'left',\n        e: 'right',\n        n: 'top',\n        s: 'bottom'\n      };\n      var inverseMap = {\n        left: 'w',\n        right: 'e',\n        top: 'n',\n        bottom: 's'\n      };\n      var dir = transformDirection(map[localDirName], getTransform$1(controller));\n      return inverseMap[dir];\n    }\n\n    function getGlobalDirection2(controller, localDirNameSeq) {\n      var globalDir = [getGlobalDirection1(controller, localDirNameSeq[0]), getGlobalDirection1(controller, localDirNameSeq[1])];\n      (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();\n      return globalDir.join('');\n    }\n\n    function driftRect(rectRangeConverter, controller, cover, dirNameSequence, dx, dy) {\n      var brushOption = cover.__brushOption;\n      var rectRange = rectRangeConverter.toRectRange(brushOption.range);\n      var localDelta = toLocalDelta(controller, dx, dy);\n      each(dirNameSequence, function (dirName) {\n        var ind = DIRECTION_MAP[dirName];\n        rectRange[ind[0]][ind[1]] += localDelta[ind[0]];\n      });\n      brushOption.range = rectRangeConverter.fromRectRange(formatRectRange(rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]));\n      updateCoverAfterCreation(controller, cover);\n      trigger$1(controller, {\n        isEnd: false\n      });\n    }\n\n    function driftPolygon(controller, cover, dx, dy) {\n      var range = cover.__brushOption.range;\n      var localDelta = toLocalDelta(controller, dx, dy);\n      each(range, function (point) {\n        point[0] += localDelta[0];\n        point[1] += localDelta[1];\n      });\n      updateCoverAfterCreation(controller, cover);\n      trigger$1(controller, {\n        isEnd: false\n      });\n    }\n\n    function toLocalDelta(controller, dx, dy) {\n      var thisGroup = controller.group;\n      var localD = thisGroup.transformCoordToLocal(dx, dy);\n      var localZero = thisGroup.transformCoordToLocal(0, 0);\n      return [localD[0] - localZero[0], localD[1] - localZero[1]];\n    }\n\n    function clipByPanel(controller, cover, data) {\n      var panel = getPanelByCover(controller, cover);\n      return panel && panel !== BRUSH_PANEL_GLOBAL ? panel.clipPath(data, controller._transform) : clone(data);\n    }\n\n    function pointsToRect(points) {\n      var xmin = mathMin$9(points[0][0], points[1][0]);\n      var ymin = mathMin$9(points[0][1], points[1][1]);\n      var xmax = mathMax$9(points[0][0], points[1][0]);\n      var ymax = mathMax$9(points[0][1], points[1][1]);\n      return {\n        x: xmin,\n        y: ymin,\n        width: xmax - xmin,\n        height: ymax - ymin\n      };\n    }\n\n    function resetCursor(controller, e, localCursorPoint) {\n      if ( // Check active\n      !controller._brushType // resetCursor should be always called when mouse is in zr area,\n      // but not called when mouse is out of zr area to avoid bad influence\n      // if `mousemove`, `mouseup` are triggered from `document` event.\n      || isOutsideZrArea(controller, e.offsetX, e.offsetY)) {\n        return;\n      }\n\n      var zr = controller._zr;\n      var covers = controller._covers;\n      var currPanel = getPanelByPoint(controller, e, localCursorPoint); // Check whether in covers.\n\n      if (!controller._dragging) {\n        for (var i = 0; i < covers.length; i++) {\n          var brushOption = covers[i].__brushOption;\n\n          if (currPanel && (currPanel === BRUSH_PANEL_GLOBAL || brushOption.panelId === currPanel.panelId) && coverRenderers[brushOption.brushType].contain(covers[i], localCursorPoint[0], localCursorPoint[1])) {\n            // Use cursor style set on cover.\n            return;\n          }\n        }\n      }\n\n      currPanel && zr.setCursorStyle('crosshair');\n    }\n\n    function preventDefault(e) {\n      var rawE = e.event;\n      rawE.preventDefault && rawE.preventDefault();\n    }\n\n    function mainShapeContain(cover, x, y) {\n      return cover.childOfName('main').contain(x, y);\n    }\n\n    function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {\n      var creatingCover = controller._creatingCover;\n      var panel = controller._creatingPanel;\n      var thisBrushOption = controller._brushOption;\n      var eventParams;\n\n      controller._track.push(localCursorPoint.slice());\n\n      if (shouldShowCover(controller) || creatingCover) {\n        if (panel && !creatingCover) {\n          thisBrushOption.brushMode === 'single' && clearCovers(controller);\n          var brushOption = clone(thisBrushOption);\n          brushOption.brushType = determineBrushType(brushOption.brushType, panel);\n          brushOption.panelId = panel === BRUSH_PANEL_GLOBAL ? null : panel.panelId;\n          creatingCover = controller._creatingCover = createCover(controller, brushOption);\n\n          controller._covers.push(creatingCover);\n        }\n\n        if (creatingCover) {\n          var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];\n          var coverBrushOption = creatingCover.__brushOption;\n          coverBrushOption.range = coverRenderer.getCreatingRange(clipByPanel(controller, creatingCover, controller._track));\n\n          if (isEnd) {\n            endCreating(controller, creatingCover);\n            coverRenderer.updateCommon(controller, creatingCover);\n          }\n\n          updateCoverShape(controller, creatingCover);\n          eventParams = {\n            isEnd: isEnd\n          };\n        }\n      } else if (isEnd && thisBrushOption.brushMode === 'single' && thisBrushOption.removeOnClick) {\n        // Help user to remove covers easily, only by a tiny drag, in 'single' mode.\n        // But a single click do not clear covers, because user may have casual\n        // clicks (for example, click on other component and do not expect covers\n        // disappear).\n        // Only some cover removed, trigger action, but not every click trigger action.\n        if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) {\n          eventParams = {\n            isEnd: isEnd,\n            removeOnClick: true\n          };\n        }\n      }\n\n      return eventParams;\n    }\n\n    function determineBrushType(brushType, panel) {\n      if (brushType === 'auto') {\n        if (\"development\" !== 'production') {\n          assert(panel && panel.defaultBrushType, 'MUST have defaultBrushType when brushType is \"atuo\"');\n        }\n\n        return panel.defaultBrushType;\n      }\n\n      return brushType;\n    }\n\n    var pointerHandlers = {\n      mousedown: function (e) {\n        if (this._dragging) {\n          // In case some browser do not support globalOut,\n          // and release mouse out side the browser.\n          handleDragEnd(this, e);\n        } else if (!e.target || !e.target.draggable) {\n          preventDefault(e);\n          var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);\n          this._creatingCover = null;\n          var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint);\n\n          if (panel) {\n            this._dragging = true;\n            this._track = [localCursorPoint.slice()];\n          }\n        }\n      },\n      mousemove: function (e) {\n        var x = e.offsetX;\n        var y = e.offsetY;\n        var localCursorPoint = this.group.transformCoordToLocal(x, y);\n        resetCursor(this, e, localCursorPoint);\n\n        if (this._dragging) {\n          preventDefault(e);\n          var eventParams = updateCoverByMouse(this, e, localCursorPoint, false);\n          eventParams && trigger$1(this, eventParams);\n        }\n      },\n      mouseup: function (e) {\n        handleDragEnd(this, e);\n      }\n    };\n\n    function handleDragEnd(controller, e) {\n      if (controller._dragging) {\n        preventDefault(e);\n        var x = e.offsetX;\n        var y = e.offsetY;\n        var localCursorPoint = controller.group.transformCoordToLocal(x, y);\n        var eventParams = updateCoverByMouse(controller, e, localCursorPoint, true);\n        controller._dragging = false;\n        controller._track = [];\n        controller._creatingCover = null; // trigger event shoule be at final, after procedure will be nested.\n\n        eventParams && trigger$1(controller, eventParams);\n      }\n    }\n\n    function isOutsideZrArea(controller, x, y) {\n      var zr = controller._zr;\n      return x < 0 || x > zr.getWidth() || y < 0 || y > zr.getHeight();\n    }\n    /**\n     * key: brushType\n     */\n\n\n    var coverRenderers = {\n      lineX: getLineRenderer(0),\n      lineY: getLineRenderer(1),\n      rect: {\n        createCover: function (controller, brushOption) {\n          function returnInput(range) {\n            return range;\n          }\n\n          return createBaseRectCover({\n            toRectRange: returnInput,\n            fromRectRange: returnInput\n          }, controller, brushOption, [['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']]);\n        },\n        getCreatingRange: function (localTrack) {\n          var ends = getTrackEnds(localTrack);\n          return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);\n        },\n        updateCoverShape: function (controller, cover, localRange, brushOption) {\n          updateBaseRect(controller, cover, localRange, brushOption);\n        },\n        updateCommon: updateCommon,\n        contain: mainShapeContain\n      },\n      polygon: {\n        createCover: function (controller, brushOption) {\n          var cover = new Group(); // Do not use graphic.Polygon because graphic.Polyline do not close the\n          // border of the shape when drawing, which is a better experience for user.\n\n          cover.add(new Polyline({\n            name: 'main',\n            style: makeStyle(brushOption),\n            silent: true\n          }));\n          return cover;\n        },\n        getCreatingRange: function (localTrack) {\n          return localTrack;\n        },\n        endCreating: function (controller, cover) {\n          cover.remove(cover.childAt(0)); // Use graphic.Polygon close the shape.\n\n          cover.add(new Polygon({\n            name: 'main',\n            draggable: true,\n            drift: curry(driftPolygon, controller, cover),\n            ondragend: curry(trigger$1, controller, {\n              isEnd: true\n            })\n          }));\n        },\n        updateCoverShape: function (controller, cover, localRange, brushOption) {\n          cover.childAt(0).setShape({\n            points: clipByPanel(controller, cover, localRange)\n          });\n        },\n        updateCommon: updateCommon,\n        contain: mainShapeContain\n      }\n    };\n\n    function getLineRenderer(xyIndex) {\n      return {\n        createCover: function (controller, brushOption) {\n          return createBaseRectCover({\n            toRectRange: function (range) {\n              var rectRange = [range, [0, 100]];\n              xyIndex && rectRange.reverse();\n              return rectRange;\n            },\n            fromRectRange: function (rectRange) {\n              return rectRange[xyIndex];\n            }\n          }, controller, brushOption, [[['w'], ['e']], [['n'], ['s']]][xyIndex]);\n        },\n        getCreatingRange: function (localTrack) {\n          var ends = getTrackEnds(localTrack);\n          var min = mathMin$9(ends[0][xyIndex], ends[1][xyIndex]);\n          var max = mathMax$9(ends[0][xyIndex], ends[1][xyIndex]);\n          return [min, max];\n        },\n        updateCoverShape: function (controller, cover, localRange, brushOption) {\n          var otherExtent; // If brushWidth not specified, fit the panel.\n\n          var panel = getPanelByCover(controller, cover);\n\n          if (panel !== BRUSH_PANEL_GLOBAL && panel.getLinearBrushOtherExtent) {\n            otherExtent = panel.getLinearBrushOtherExtent(xyIndex);\n          } else {\n            var zr = controller._zr;\n            otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]];\n          }\n\n          var rectRange = [localRange, otherExtent];\n          xyIndex && rectRange.reverse();\n          updateBaseRect(controller, cover, rectRange, brushOption);\n        },\n        updateCommon: updateCommon,\n        contain: mainShapeContain\n      };\n    }\n\n    function makeRectPanelClipPath(rect) {\n      rect = normalizeRect(rect);\n      return function (localPoints) {\n        return clipPointsByRect(localPoints, rect);\n      };\n    }\n    function makeLinearBrushOtherExtent(rect, specifiedXYIndex) {\n      rect = normalizeRect(rect);\n      return function (xyIndex) {\n        var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex;\n        var brushWidth = idx ? rect.width : rect.height;\n        var base = idx ? rect.x : rect.y;\n        return [base, base + (brushWidth || 0)];\n      };\n    }\n    function makeRectIsTargetByCursor(rect, api, targetModel) {\n      var boundingRect = normalizeRect(rect);\n      return function (e, localCursorPoint) {\n        return boundingRect.contain(localCursorPoint[0], localCursorPoint[1]) && !onIrrelevantElement(e, api, targetModel);\n      };\n    } // Consider width/height is negative.\n\n    function normalizeRect(rect) {\n      return BoundingRect.create(rect);\n    }\n\n    var elementList = ['axisLine', 'axisTickLabel', 'axisName'];\n\n    var ParallelAxisView =\n    /** @class */\n    function (_super) {\n      __extends(ParallelAxisView, _super);\n\n      function ParallelAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ParallelAxisView.type;\n        return _this;\n      }\n\n      ParallelAxisView.prototype.init = function (ecModel, api) {\n        _super.prototype.init.apply(this, arguments);\n\n        (this._brushController = new BrushController(api.getZr())).on('brush', bind(this._onBrush, this));\n      };\n\n      ParallelAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        if (fromAxisAreaSelect(axisModel, ecModel, payload)) {\n          return;\n        }\n\n        this.axisModel = axisModel;\n        this.api = api;\n        this.group.removeAll();\n        var oldAxisGroup = this._axisGroup;\n        this._axisGroup = new Group();\n        this.group.add(this._axisGroup);\n\n        if (!axisModel.get('show')) {\n          return;\n        }\n\n        var coordSysModel = getCoordSysModel(axisModel, ecModel);\n        var coordSys = coordSysModel.coordinateSystem;\n        var areaSelectStyle = axisModel.getAreaSelectStyle();\n        var areaWidth = areaSelectStyle.width;\n        var dim = axisModel.axis.dim;\n        var axisLayout = coordSys.getAxisLayout(dim);\n        var builderOpt = extend({\n          strokeContainThreshold: areaWidth\n        }, axisLayout);\n        var axisBuilder = new AxisBuilder(axisModel, builderOpt);\n        each(elementList, axisBuilder.add, axisBuilder);\n\n        this._axisGroup.add(axisBuilder.getGroup());\n\n        this._refreshBrushController(builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api);\n\n        groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n      }; // /**\n      //  * @override\n      //  */\n      // updateVisual(axisModel, ecModel, api, payload) {\n      //     this._brushController && this._brushController\n      //         .updateCovers(getCoverInfoList(axisModel));\n      // }\n\n\n      ParallelAxisView.prototype._refreshBrushController = function (builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api) {\n        // After filtering, axis may change, select area needs to be update.\n        var extent = axisModel.axis.getExtent();\n        var extentLen = extent[1] - extent[0];\n        var extra = Math.min(30, Math.abs(extentLen) * 0.1); // Arbitrary value.\n        // width/height might be negative, which will be\n        // normalized in BoundingRect.\n\n        var rect = BoundingRect.create({\n          x: extent[0],\n          y: -areaWidth / 2,\n          width: extentLen,\n          height: areaWidth\n        });\n        rect.x -= extra;\n        rect.width += 2 * extra;\n\n        this._brushController.mount({\n          enableGlobalPan: true,\n          rotation: builderOpt.rotation,\n          x: builderOpt.position[0],\n          y: builderOpt.position[1]\n        }).setPanels([{\n          panelId: 'pl',\n          clipPath: makeRectPanelClipPath(rect),\n          isTargetByCursor: makeRectIsTargetByCursor(rect, api, coordSysModel),\n          getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect, 0)\n        }]).enableBrush({\n          brushType: 'lineX',\n          brushStyle: areaSelectStyle,\n          removeOnClick: true\n        }).updateCovers(getCoverInfoList(axisModel));\n      };\n\n      ParallelAxisView.prototype._onBrush = function (eventParam) {\n        var coverInfoList = eventParam.areas; // Do not cache these object, because the mey be changed.\n\n        var axisModel = this.axisModel;\n        var axis = axisModel.axis;\n        var intervals = map(coverInfoList, function (coverInfo) {\n          return [axis.coordToData(coverInfo.range[0], true), axis.coordToData(coverInfo.range[1], true)];\n        }); // If realtime is true, action is not dispatched on drag end, because\n        // the drag end emits the same params with the last drag move event,\n        // and may have some delay when using touch pad.\n\n        if (!axisModel.option.realtime === eventParam.isEnd || eventParam.removeOnClick) {\n          // jshint ignore:line\n          this.api.dispatchAction({\n            type: 'axisAreaSelect',\n            parallelAxisId: axisModel.id,\n            intervals: intervals\n          });\n        }\n      };\n\n      ParallelAxisView.prototype.dispose = function () {\n        this._brushController.dispose();\n      };\n\n      ParallelAxisView.type = 'parallelAxis';\n      return ParallelAxisView;\n    }(ComponentView);\n\n    function fromAxisAreaSelect(axisModel, ecModel, payload) {\n      return payload && payload.type === 'axisAreaSelect' && ecModel.findComponents({\n        mainType: 'parallelAxis',\n        query: payload\n      })[0] === axisModel;\n    }\n\n    function getCoverInfoList(axisModel) {\n      var axis = axisModel.axis;\n      return map(axisModel.activeIntervals, function (interval) {\n        return {\n          brushType: 'lineX',\n          panelId: 'pl',\n          range: [axis.dataToCoord(interval[0], true), axis.dataToCoord(interval[1], true)]\n        };\n      });\n    }\n\n    function getCoordSysModel(axisModel, ecModel) {\n      return ecModel.getComponent('parallel', axisModel.get('parallelIndex'));\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var actionInfo$1 = {\n      type: 'axisAreaSelect',\n      event: 'axisAreaSelected' // update: 'updateVisual'\n\n    };\n    function installParallelActions(registers) {\n      registers.registerAction(actionInfo$1, function (payload, ecModel) {\n        ecModel.eachComponent({\n          mainType: 'parallelAxis',\n          query: payload\n        }, function (parallelAxisModel) {\n          parallelAxisModel.axis.model.setActiveIntervals(payload.intervals);\n        });\n      });\n      /**\n       * @payload\n       */\n\n      registers.registerAction('parallelAxisExpand', function (payload, ecModel) {\n        ecModel.eachComponent({\n          mainType: 'parallel',\n          query: payload\n        }, function (parallelModel) {\n          parallelModel.setAxisExpand(payload);\n        });\n      });\n    }\n\n    var defaultAxisOption = {\n      type: 'value',\n      areaSelectStyle: {\n        width: 20,\n        borderWidth: 1,\n        borderColor: 'rgba(160,197,232)',\n        color: 'rgba(160,197,232)',\n        opacity: 0.3\n      },\n      realtime: true,\n      z: 10\n    };\n    function install$g(registers) {\n      registers.registerComponentView(ParallelView$1);\n      registers.registerComponentModel(ParallelModel);\n      registers.registerCoordinateSystem('parallel', parallelCoordSysCreator);\n      registers.registerPreprocessor(parallelPreprocessor);\n      registers.registerComponentModel(ParallelAxisModel);\n      registers.registerComponentView(ParallelAxisView);\n      axisModelCreator(registers, 'parallel', ParallelAxisModel, defaultAxisOption);\n      installParallelActions(registers);\n    }\n\n    function install$h(registers) {\n      use(install$g);\n      registers.registerChartView(ParallelView);\n      registers.registerSeriesModel(ParallelSeriesModel);\n      registers.registerVisual(registers.PRIORITY.VISUAL.BRUSH, parallelVisual);\n    }\n\n    var SankeyPathShape =\n    /** @class */\n    function () {\n      function SankeyPathShape() {\n        this.x1 = 0;\n        this.y1 = 0;\n        this.x2 = 0;\n        this.y2 = 0;\n        this.cpx1 = 0;\n        this.cpy1 = 0;\n        this.cpx2 = 0;\n        this.cpy2 = 0;\n        this.extent = 0;\n      }\n\n      return SankeyPathShape;\n    }();\n\n    var SankeyPath =\n    /** @class */\n    function (_super) {\n      __extends(SankeyPath, _super);\n\n      function SankeyPath(opts) {\n        return _super.call(this, opts) || this;\n      }\n\n      SankeyPath.prototype.getDefaultShape = function () {\n        return new SankeyPathShape();\n      };\n\n      SankeyPath.prototype.buildPath = function (ctx, shape) {\n        var extent = shape.extent;\n        ctx.moveTo(shape.x1, shape.y1);\n        ctx.bezierCurveTo(shape.cpx1, shape.cpy1, shape.cpx2, shape.cpy2, shape.x2, shape.y2);\n\n        if (shape.orient === 'vertical') {\n          ctx.lineTo(shape.x2 + extent, shape.y2);\n          ctx.bezierCurveTo(shape.cpx2 + extent, shape.cpy2, shape.cpx1 + extent, shape.cpy1, shape.x1 + extent, shape.y1);\n        } else {\n          ctx.lineTo(shape.x2, shape.y2 + extent);\n          ctx.bezierCurveTo(shape.cpx2, shape.cpy2 + extent, shape.cpx1, shape.cpy1 + extent, shape.x1, shape.y1 + extent);\n        }\n\n        ctx.closePath();\n      };\n\n      SankeyPath.prototype.highlight = function () {\n        enterEmphasis(this);\n      };\n\n      SankeyPath.prototype.downplay = function () {\n        leaveEmphasis(this);\n      };\n\n      return SankeyPath;\n    }(Path);\n\n    var SankeyView =\n    /** @class */\n    function (_super) {\n      __extends(SankeyView, _super);\n\n      function SankeyView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SankeyView.type;\n        _this._focusAdjacencyDisabled = false;\n        return _this;\n      }\n\n      SankeyView.prototype.render = function (seriesModel, ecModel, api) {\n        var sankeyView = this;\n        var graph = seriesModel.getGraph();\n        var group = this.group;\n        var layoutInfo = seriesModel.layoutInfo; // view width\n\n        var width = layoutInfo.width; // view height\n\n        var height = layoutInfo.height;\n        var nodeData = seriesModel.getData();\n        var edgeData = seriesModel.getData('edge');\n        var orient = seriesModel.get('orient');\n        this._model = seriesModel;\n        group.removeAll();\n        group.x = layoutInfo.x;\n        group.y = layoutInfo.y; // generate a bezire Curve for each edge\n\n        graph.eachEdge(function (edge) {\n          var curve = new SankeyPath();\n          var ecData = getECData(curve);\n          ecData.dataIndex = edge.dataIndex;\n          ecData.seriesIndex = seriesModel.seriesIndex;\n          ecData.dataType = 'edge';\n          var edgeModel = edge.getModel();\n          var lineStyleModel = edgeModel.getModel('lineStyle');\n          var curvature = lineStyleModel.get('curveness');\n          var n1Layout = edge.node1.getLayout();\n          var node1Model = edge.node1.getModel();\n          var dragX1 = node1Model.get('localX');\n          var dragY1 = node1Model.get('localY');\n          var n2Layout = edge.node2.getLayout();\n          var node2Model = edge.node2.getModel();\n          var dragX2 = node2Model.get('localX');\n          var dragY2 = node2Model.get('localY');\n          var edgeLayout = edge.getLayout();\n          var x1;\n          var y1;\n          var x2;\n          var y2;\n          var cpx1;\n          var cpy1;\n          var cpx2;\n          var cpy2;\n          curve.shape.extent = Math.max(1, edgeLayout.dy);\n          curve.shape.orient = orient;\n\n          if (orient === 'vertical') {\n            x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + edgeLayout.sy;\n            y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + n1Layout.dy;\n            x2 = (dragX2 != null ? dragX2 * width : n2Layout.x) + edgeLayout.ty;\n            y2 = dragY2 != null ? dragY2 * height : n2Layout.y;\n            cpx1 = x1;\n            cpy1 = y1 * (1 - curvature) + y2 * curvature;\n            cpx2 = x2;\n            cpy2 = y1 * curvature + y2 * (1 - curvature);\n          } else {\n            x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + n1Layout.dx;\n            y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + edgeLayout.sy;\n            x2 = dragX2 != null ? dragX2 * width : n2Layout.x;\n            y2 = (dragY2 != null ? dragY2 * height : n2Layout.y) + edgeLayout.ty;\n            cpx1 = x1 * (1 - curvature) + x2 * curvature;\n            cpy1 = y1;\n            cpx2 = x1 * curvature + x2 * (1 - curvature);\n            cpy2 = y2;\n          }\n\n          curve.setShape({\n            x1: x1,\n            y1: y1,\n            x2: x2,\n            y2: y2,\n            cpx1: cpx1,\n            cpy1: cpy1,\n            cpx2: cpx2,\n            cpy2: cpy2\n          });\n          curve.useStyle(lineStyleModel.getItemStyle()); // Special color, use source node color or target node color\n\n          switch (curve.style.fill) {\n            case 'source':\n              curve.style.fill = edge.node1.getVisual('color');\n              curve.style.decal = edge.node1.getVisual('style').decal;\n              break;\n\n            case 'target':\n              curve.style.fill = edge.node2.getVisual('color');\n              curve.style.decal = edge.node2.getVisual('style').decal;\n              break;\n\n            case 'gradient':\n              var sourceColor = edge.node1.getVisual('color');\n              var targetColor = edge.node2.getVisual('color');\n\n              if (isString(sourceColor) && isString(targetColor)) {\n                curve.style.fill = new LinearGradient(0, 0, +(orient === 'horizontal'), +(orient === 'vertical'), [{\n                  color: sourceColor,\n                  offset: 0\n                }, {\n                  color: targetColor,\n                  offset: 1\n                }]);\n              }\n\n          }\n\n          setLabelStyle(curve, getLabelStatesModels(edgeModel, 'edgeLabel'), {\n            labelFetcher: seriesModel,\n            labelDataIndex: edge.dataIndex,\n            defaultText: \"\" + edgeModel.get('value')\n          });\n          curve.setTextConfig({\n            position: 'inside'\n          });\n          var emphasisModel = edgeModel.getModel('emphasis');\n          setStatesStylesFromModel(curve, edgeModel, 'lineStyle', function (model) {\n            return model.getItemStyle();\n          });\n          group.add(curve);\n          edgeData.setItemGraphicEl(edge.dataIndex, curve);\n          var focus = emphasisModel.get('focus');\n          toggleHoverEmphasis(curve, focus === 'adjacency' ? edge.getAdjacentDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n          getECData(curve).dataType = 'edge';\n        }); // Generate a rect for each node\n\n        graph.eachNode(function (node) {\n          var layout = node.getLayout();\n          var itemModel = node.getModel();\n          var dragX = itemModel.get('localX');\n          var dragY = itemModel.get('localY');\n          var emphasisModel = itemModel.getModel('emphasis');\n          var rect = new Rect({\n            shape: {\n              x: dragX != null ? dragX * width : layout.x,\n              y: dragY != null ? dragY * height : layout.y,\n              width: layout.dx,\n              height: layout.dy\n            },\n            style: itemModel.getModel('itemStyle').getItemStyle(),\n            z2: 10\n          });\n          setLabelStyle(rect, getLabelStatesModels(itemModel), {\n            labelFetcher: seriesModel,\n            labelDataIndex: node.dataIndex,\n            defaultText: node.id\n          });\n          rect.disableLabelAnimation = true;\n          rect.setStyle('fill', node.getVisual('color'));\n          rect.setStyle('decal', node.getVisual('style').decal);\n          setStatesStylesFromModel(rect, itemModel);\n          group.add(rect);\n          nodeData.setItemGraphicEl(node.dataIndex, rect);\n          getECData(rect).dataType = 'node';\n          var focus = emphasisModel.get('focus');\n          toggleHoverEmphasis(rect, focus === 'adjacency' ? node.getAdjacentDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n        });\n        nodeData.eachItemGraphicEl(function (el, dataIndex) {\n          var itemModel = nodeData.getItemModel(dataIndex);\n\n          if (itemModel.get('draggable')) {\n            el.drift = function (dx, dy) {\n              sankeyView._focusAdjacencyDisabled = true;\n              this.shape.x += dx;\n              this.shape.y += dy;\n              this.dirty();\n              api.dispatchAction({\n                type: 'dragNode',\n                seriesId: seriesModel.id,\n                dataIndex: nodeData.getRawIndex(dataIndex),\n                localX: this.shape.x / width,\n                localY: this.shape.y / height\n              });\n            };\n\n            el.ondragend = function () {\n              sankeyView._focusAdjacencyDisabled = false;\n            };\n\n            el.draggable = true;\n            el.cursor = 'move';\n          }\n        });\n\n        if (!this._data && seriesModel.isAnimationEnabled()) {\n          group.setClipPath(createGridClipShape$1(group.getBoundingRect(), seriesModel, function () {\n            group.removeClipPath();\n          }));\n        }\n\n        this._data = seriesModel.getData();\n      };\n\n      SankeyView.prototype.dispose = function () {};\n\n      SankeyView.type = 'sankey';\n      return SankeyView;\n    }(ChartView); // Add animation to the view\n\n\n    function createGridClipShape$1(rect, seriesModel, cb) {\n      var rectEl = new Rect({\n        shape: {\n          x: rect.x - 10,\n          y: rect.y - 10,\n          width: 0,\n          height: rect.height + 20\n        }\n      });\n      initProps(rectEl, {\n        shape: {\n          width: rect.width + 20\n        }\n      }, seriesModel, cb);\n      return rectEl;\n    }\n\n    var SankeySeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(SankeySeriesModel, _super);\n\n      function SankeySeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SankeySeriesModel.type;\n        return _this;\n      }\n      /**\n       * Init a graph data structure from data in option series\n       */\n\n\n      SankeySeriesModel.prototype.getInitialData = function (option, ecModel) {\n        var links = option.edges || option.links;\n        var nodes = option.data || option.nodes;\n        var levels = option.levels;\n        this.levelModels = [];\n        var levelModels = this.levelModels;\n\n        for (var i = 0; i < levels.length; i++) {\n          if (levels[i].depth != null && levels[i].depth >= 0) {\n            levelModels[levels[i].depth] = new Model(levels[i], this, ecModel);\n          } else {\n            if (\"development\" !== 'production') {\n              throw new Error('levels[i].depth is mandatory and should be natural number');\n            }\n          }\n        }\n\n        if (nodes && links) {\n          var graph = createGraphFromNodeEdge(nodes, links, this, true, beforeLink);\n          return graph.data;\n        }\n\n        function beforeLink(nodeData, edgeData) {\n          nodeData.wrapMethod('getItemModel', function (model, idx) {\n            var seriesModel = model.parentModel;\n            var layout = seriesModel.getData().getItemLayout(idx);\n\n            if (layout) {\n              var nodeDepth = layout.depth;\n              var levelModel = seriesModel.levelModels[nodeDepth];\n\n              if (levelModel) {\n                model.parentModel = levelModel;\n              }\n            }\n\n            return model;\n          });\n          edgeData.wrapMethod('getItemModel', function (model, idx) {\n            var seriesModel = model.parentModel;\n            var edge = seriesModel.getGraph().getEdgeByIndex(idx);\n            var layout = edge.node1.getLayout();\n\n            if (layout) {\n              var depth = layout.depth;\n              var levelModel = seriesModel.levelModels[depth];\n\n              if (levelModel) {\n                model.parentModel = levelModel;\n              }\n            }\n\n            return model;\n          });\n        }\n      };\n\n      SankeySeriesModel.prototype.setNodePosition = function (dataIndex, localPosition) {\n        var nodes = this.option.data || this.option.nodes;\n        var dataItem = nodes[dataIndex];\n        dataItem.localX = localPosition[0];\n        dataItem.localY = localPosition[1];\n      };\n      /**\n       * Return the graphic data structure\n       *\n       * @return graphic data structure\n       */\n\n\n      SankeySeriesModel.prototype.getGraph = function () {\n        return this.getData().graph;\n      };\n      /**\n       * Get edge data of graphic data structure\n       *\n       * @return data structure of list\n       */\n\n\n      SankeySeriesModel.prototype.getEdgeData = function () {\n        return this.getGraph().edgeData;\n      };\n\n      SankeySeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        function noValue(val) {\n          return isNaN(val) || val == null;\n        } // dataType === 'node' or empty do not show tooltip by default\n\n\n        if (dataType === 'edge') {\n          var params = this.getDataParams(dataIndex, dataType);\n          var rawDataOpt = params.data;\n          var edgeValue = params.value;\n          var edgeName = rawDataOpt.source + ' -- ' + rawDataOpt.target;\n          return createTooltipMarkup('nameValue', {\n            name: edgeName,\n            value: edgeValue,\n            noValue: noValue(edgeValue)\n          });\n        } // dataType === 'node'\n        else {\n            var node = this.getGraph().getNodeByIndex(dataIndex);\n            var value = node.getLayout().value;\n            var name_1 = this.getDataParams(dataIndex, dataType).data.name;\n            return createTooltipMarkup('nameValue', {\n              name: name_1 != null ? name_1 + '' : null,\n              value: value,\n              noValue: noValue(value)\n            });\n          }\n      };\n\n      SankeySeriesModel.prototype.optionUpdated = function () {}; // Override Series.getDataParams()\n\n\n      SankeySeriesModel.prototype.getDataParams = function (dataIndex, dataType) {\n        var params = _super.prototype.getDataParams.call(this, dataIndex, dataType);\n\n        if (params.value == null && dataType === 'node') {\n          var node = this.getGraph().getNodeByIndex(dataIndex);\n          var nodeValue = node.getLayout().value;\n          params.value = nodeValue;\n        }\n\n        return params;\n      };\n\n      SankeySeriesModel.type = 'series.sankey';\n      SankeySeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'view',\n        left: '5%',\n        top: '5%',\n        right: '20%',\n        bottom: '5%',\n        orient: 'horizontal',\n        nodeWidth: 20,\n        nodeGap: 8,\n        draggable: true,\n        layoutIterations: 32,\n        label: {\n          show: true,\n          position: 'right',\n          fontSize: 12\n        },\n        edgeLabel: {\n          show: false,\n          fontSize: 12\n        },\n        levels: [],\n        nodeAlign: 'justify',\n        lineStyle: {\n          color: '#314656',\n          opacity: 0.2,\n          curveness: 0.5\n        },\n        emphasis: {\n          label: {\n            show: true\n          },\n          lineStyle: {\n            opacity: 0.5\n          }\n        },\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        },\n        animationEasing: 'linear',\n        animationDuration: 1000\n      };\n      return SankeySeriesModel;\n    }(SeriesModel);\n\n    function sankeyLayout(ecModel, api) {\n      ecModel.eachSeriesByType('sankey', function (seriesModel) {\n        var nodeWidth = seriesModel.get('nodeWidth');\n        var nodeGap = seriesModel.get('nodeGap');\n        var layoutInfo = getViewRect$4(seriesModel, api);\n        seriesModel.layoutInfo = layoutInfo;\n        var width = layoutInfo.width;\n        var height = layoutInfo.height;\n        var graph = seriesModel.getGraph();\n        var nodes = graph.nodes;\n        var edges = graph.edges;\n        computeNodeValues(nodes);\n        var filteredNodes = filter(nodes, function (node) {\n          return node.getLayout().value === 0;\n        });\n        var iterations = filteredNodes.length !== 0 ? 0 : seriesModel.get('layoutIterations');\n        var orient = seriesModel.get('orient');\n        var nodeAlign = seriesModel.get('nodeAlign');\n        layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign);\n      });\n    }\n    /**\n     * Get the layout position of the whole view\n     */\n\n    function getViewRect$4(seriesModel, api) {\n      return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n        width: api.getWidth(),\n        height: api.getHeight()\n      });\n    }\n\n    function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign) {\n      computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign);\n      computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient);\n      computeEdgeDepths(nodes, orient);\n    }\n    /**\n     * Compute the value of each node by summing the associated edge's value\n     */\n\n\n    function computeNodeValues(nodes) {\n      each(nodes, function (node) {\n        var value1 = sum(node.outEdges, getEdgeValue);\n        var value2 = sum(node.inEdges, getEdgeValue);\n        var nodeRawValue = node.getValue() || 0;\n        var value = Math.max(value1, value2, nodeRawValue);\n        node.setLayout({\n          value: value\n        }, true);\n      });\n    }\n    /**\n     * Compute the x-position for each node.\n     *\n     * Here we use Kahn algorithm to detect cycle when we traverse\n     * the node to computer the initial x position.\n     */\n\n\n    function computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign) {\n      // Used to mark whether the edge is deleted. if it is deleted,\n      // the value is 0, otherwise it is 1.\n      var remainEdges = []; // Storage each node's indegree.\n\n      var indegreeArr = []; // Used to storage the node with indegree is equal to 0.\n\n      var zeroIndegrees = [];\n      var nextTargetNode = [];\n      var x = 0; // let kx = 0;\n\n      for (var i = 0; i < edges.length; i++) {\n        remainEdges[i] = 1;\n      }\n\n      for (var i = 0; i < nodes.length; i++) {\n        indegreeArr[i] = nodes[i].inEdges.length;\n\n        if (indegreeArr[i] === 0) {\n          zeroIndegrees.push(nodes[i]);\n        }\n      }\n\n      var maxNodeDepth = -1; // Traversing nodes using topological sorting to calculate the\n      // horizontal(if orient === 'horizontal') or vertical(if orient === 'vertical')\n      // position of the nodes.\n\n      while (zeroIndegrees.length) {\n        for (var idx = 0; idx < zeroIndegrees.length; idx++) {\n          var node = zeroIndegrees[idx];\n          var item = node.hostGraph.data.getRawDataItem(node.dataIndex);\n          var isItemDepth = item.depth != null && item.depth >= 0;\n\n          if (isItemDepth && item.depth > maxNodeDepth) {\n            maxNodeDepth = item.depth;\n          }\n\n          node.setLayout({\n            depth: isItemDepth ? item.depth : x\n          }, true);\n          orient === 'vertical' ? node.setLayout({\n            dy: nodeWidth\n          }, true) : node.setLayout({\n            dx: nodeWidth\n          }, true);\n\n          for (var edgeIdx = 0; edgeIdx < node.outEdges.length; edgeIdx++) {\n            var edge = node.outEdges[edgeIdx];\n            var indexEdge = edges.indexOf(edge);\n            remainEdges[indexEdge] = 0;\n            var targetNode = edge.node2;\n            var nodeIndex = nodes.indexOf(targetNode);\n\n            if (--indegreeArr[nodeIndex] === 0 && nextTargetNode.indexOf(targetNode) < 0) {\n              nextTargetNode.push(targetNode);\n            }\n          }\n        }\n\n        ++x;\n        zeroIndegrees = nextTargetNode;\n        nextTargetNode = [];\n      }\n\n      for (var i = 0; i < remainEdges.length; i++) {\n        if (remainEdges[i] === 1) {\n          throw new Error('Sankey is a DAG, the original data has cycle!');\n        }\n      }\n\n      var maxDepth = maxNodeDepth > x - 1 ? maxNodeDepth : x - 1;\n\n      if (nodeAlign && nodeAlign !== 'left') {\n        adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth);\n      }\n\n      var kx = orient === 'vertical' ? (height - nodeWidth) / maxDepth : (width - nodeWidth) / maxDepth;\n      scaleNodeBreadths(nodes, kx, orient);\n    }\n\n    function isNodeDepth(node) {\n      var item = node.hostGraph.data.getRawDataItem(node.dataIndex);\n      return item.depth != null && item.depth >= 0;\n    }\n\n    function adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth) {\n      if (nodeAlign === 'right') {\n        var nextSourceNode = [];\n        var remainNodes = nodes;\n        var nodeHeight = 0;\n\n        while (remainNodes.length) {\n          for (var i = 0; i < remainNodes.length; i++) {\n            var node = remainNodes[i];\n            node.setLayout({\n              skNodeHeight: nodeHeight\n            }, true);\n\n            for (var j = 0; j < node.inEdges.length; j++) {\n              var edge = node.inEdges[j];\n\n              if (nextSourceNode.indexOf(edge.node1) < 0) {\n                nextSourceNode.push(edge.node1);\n              }\n            }\n          }\n\n          remainNodes = nextSourceNode;\n          nextSourceNode = [];\n          ++nodeHeight;\n        }\n\n        each(nodes, function (node) {\n          if (!isNodeDepth(node)) {\n            node.setLayout({\n              depth: Math.max(0, maxDepth - node.getLayout().skNodeHeight)\n            }, true);\n          }\n        });\n      } else if (nodeAlign === 'justify') {\n        moveSinksRight(nodes, maxDepth);\n      }\n    }\n    /**\n     * All the node without outEgdes are assigned maximum x-position and\n     *     be aligned in the last column.\n     *\n     * @param nodes.  node of sankey view.\n     * @param maxDepth.  use to assign to node without outEdges as x-position.\n     */\n\n\n    function moveSinksRight(nodes, maxDepth) {\n      each(nodes, function (node) {\n        if (!isNodeDepth(node) && !node.outEdges.length) {\n          node.setLayout({\n            depth: maxDepth\n          }, true);\n        }\n      });\n    }\n    /**\n     * Scale node x-position to the width\n     *\n     * @param nodes  node of sankey view\n     * @param kx   multiple used to scale nodes\n     */\n\n\n    function scaleNodeBreadths(nodes, kx, orient) {\n      each(nodes, function (node) {\n        var nodeDepth = node.getLayout().depth * kx;\n        orient === 'vertical' ? node.setLayout({\n          y: nodeDepth\n        }, true) : node.setLayout({\n          x: nodeDepth\n        }, true);\n      });\n    }\n    /**\n     * Using Gauss-Seidel iterations method to compute the node depth(y-position)\n     *\n     * @param nodes  node of sankey view\n     * @param edges  edge of sankey view\n     * @param height  the whole height of the area to draw the view\n     * @param nodeGap  the vertical distance between two nodes\n     *     in the same column.\n     * @param iterations  the number of iterations for the algorithm\n     */\n\n\n    function computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient) {\n      var nodesByBreadth = prepareNodesByBreadth(nodes, orient);\n      initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient);\n      resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);\n\n      for (var alpha = 1; iterations > 0; iterations--) {\n        // 0.99 is a experience parameter, ensure that each iterations of\n        // changes as small as possible.\n        alpha *= 0.99;\n        relaxRightToLeft(nodesByBreadth, alpha, orient);\n        resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);\n        relaxLeftToRight(nodesByBreadth, alpha, orient);\n        resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);\n      }\n    }\n\n    function prepareNodesByBreadth(nodes, orient) {\n      var nodesByBreadth = [];\n      var keyAttr = orient === 'vertical' ? 'y' : 'x';\n      var groupResult = groupData(nodes, function (node) {\n        return node.getLayout()[keyAttr];\n      });\n      groupResult.keys.sort(function (a, b) {\n        return a - b;\n      });\n      each(groupResult.keys, function (key) {\n        nodesByBreadth.push(groupResult.buckets.get(key));\n      });\n      return nodesByBreadth;\n    }\n    /**\n     * Compute the original y-position for each node\n     */\n\n\n    function initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient) {\n      var minKy = Infinity;\n      each(nodesByBreadth, function (nodes) {\n        var n = nodes.length;\n        var sum = 0;\n        each(nodes, function (node) {\n          sum += node.getLayout().value;\n        });\n        var ky = orient === 'vertical' ? (width - (n - 1) * nodeGap) / sum : (height - (n - 1) * nodeGap) / sum;\n\n        if (ky < minKy) {\n          minKy = ky;\n        }\n      });\n      each(nodesByBreadth, function (nodes) {\n        each(nodes, function (node, i) {\n          var nodeDy = node.getLayout().value * minKy;\n\n          if (orient === 'vertical') {\n            node.setLayout({\n              x: i\n            }, true);\n            node.setLayout({\n              dx: nodeDy\n            }, true);\n          } else {\n            node.setLayout({\n              y: i\n            }, true);\n            node.setLayout({\n              dy: nodeDy\n            }, true);\n          }\n        });\n      });\n      each(edges, function (edge) {\n        var edgeDy = +edge.getValue() * minKy;\n        edge.setLayout({\n          dy: edgeDy\n        }, true);\n      });\n    }\n    /**\n     * Resolve the collision of initialized depth (y-position)\n     */\n\n\n    function resolveCollisions(nodesByBreadth, nodeGap, height, width, orient) {\n      var keyAttr = orient === 'vertical' ? 'x' : 'y';\n      each(nodesByBreadth, function (nodes) {\n        nodes.sort(function (a, b) {\n          return a.getLayout()[keyAttr] - b.getLayout()[keyAttr];\n        });\n        var nodeX;\n        var node;\n        var dy;\n        var y0 = 0;\n        var n = nodes.length;\n        var nodeDyAttr = orient === 'vertical' ? 'dx' : 'dy';\n\n        for (var i = 0; i < n; i++) {\n          node = nodes[i];\n          dy = y0 - node.getLayout()[keyAttr];\n\n          if (dy > 0) {\n            nodeX = node.getLayout()[keyAttr] + dy;\n            orient === 'vertical' ? node.setLayout({\n              x: nodeX\n            }, true) : node.setLayout({\n              y: nodeX\n            }, true);\n          }\n\n          y0 = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap;\n        }\n\n        var viewWidth = orient === 'vertical' ? width : height; // If the bottommost node goes outside the bounds, push it back up\n\n        dy = y0 - nodeGap - viewWidth;\n\n        if (dy > 0) {\n          nodeX = node.getLayout()[keyAttr] - dy;\n          orient === 'vertical' ? node.setLayout({\n            x: nodeX\n          }, true) : node.setLayout({\n            y: nodeX\n          }, true);\n          y0 = nodeX;\n\n          for (var i = n - 2; i >= 0; --i) {\n            node = nodes[i];\n            dy = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap - y0;\n\n            if (dy > 0) {\n              nodeX = node.getLayout()[keyAttr] - dy;\n              orient === 'vertical' ? node.setLayout({\n                x: nodeX\n              }, true) : node.setLayout({\n                y: nodeX\n              }, true);\n            }\n\n            y0 = node.getLayout()[keyAttr];\n          }\n        }\n      });\n    }\n    /**\n     * Change the y-position of the nodes, except most the right side nodes\n     * @param nodesByBreadth\n     * @param alpha  parameter used to adjust the nodes y-position\n     */\n\n\n    function relaxRightToLeft(nodesByBreadth, alpha, orient) {\n      each(nodesByBreadth.slice().reverse(), function (nodes) {\n        each(nodes, function (node) {\n          if (node.outEdges.length) {\n            var y = sum(node.outEdges, weightedTarget, orient) / sum(node.outEdges, getEdgeValue);\n\n            if (isNaN(y)) {\n              var len = node.outEdges.length;\n              y = len ? sum(node.outEdges, centerTarget, orient) / len : 0;\n            }\n\n            if (orient === 'vertical') {\n              var nodeX = node.getLayout().x + (y - center$1(node, orient)) * alpha;\n              node.setLayout({\n                x: nodeX\n              }, true);\n            } else {\n              var nodeY = node.getLayout().y + (y - center$1(node, orient)) * alpha;\n              node.setLayout({\n                y: nodeY\n              }, true);\n            }\n          }\n        });\n      });\n    }\n\n    function weightedTarget(edge, orient) {\n      return center$1(edge.node2, orient) * edge.getValue();\n    }\n\n    function centerTarget(edge, orient) {\n      return center$1(edge.node2, orient);\n    }\n\n    function weightedSource(edge, orient) {\n      return center$1(edge.node1, orient) * edge.getValue();\n    }\n\n    function centerSource(edge, orient) {\n      return center$1(edge.node1, orient);\n    }\n\n    function center$1(node, orient) {\n      return orient === 'vertical' ? node.getLayout().x + node.getLayout().dx / 2 : node.getLayout().y + node.getLayout().dy / 2;\n    }\n\n    function getEdgeValue(edge) {\n      return edge.getValue();\n    }\n\n    function sum(array, cb, orient) {\n      var sum = 0;\n      var len = array.length;\n      var i = -1;\n\n      while (++i < len) {\n        var value = +cb(array[i], orient);\n\n        if (!isNaN(value)) {\n          sum += value;\n        }\n      }\n\n      return sum;\n    }\n    /**\n     * Change the y-position of the nodes, except most the left side nodes\n     */\n\n\n    function relaxLeftToRight(nodesByBreadth, alpha, orient) {\n      each(nodesByBreadth, function (nodes) {\n        each(nodes, function (node) {\n          if (node.inEdges.length) {\n            var y = sum(node.inEdges, weightedSource, orient) / sum(node.inEdges, getEdgeValue);\n\n            if (isNaN(y)) {\n              var len = node.inEdges.length;\n              y = len ? sum(node.inEdges, centerSource, orient) / len : 0;\n            }\n\n            if (orient === 'vertical') {\n              var nodeX = node.getLayout().x + (y - center$1(node, orient)) * alpha;\n              node.setLayout({\n                x: nodeX\n              }, true);\n            } else {\n              var nodeY = node.getLayout().y + (y - center$1(node, orient)) * alpha;\n              node.setLayout({\n                y: nodeY\n              }, true);\n            }\n          }\n        });\n      });\n    }\n    /**\n     * Compute the depth(y-position) of each edge\n     */\n\n\n    function computeEdgeDepths(nodes, orient) {\n      var keyAttr = orient === 'vertical' ? 'x' : 'y';\n      each(nodes, function (node) {\n        node.outEdges.sort(function (a, b) {\n          return a.node2.getLayout()[keyAttr] - b.node2.getLayout()[keyAttr];\n        });\n        node.inEdges.sort(function (a, b) {\n          return a.node1.getLayout()[keyAttr] - b.node1.getLayout()[keyAttr];\n        });\n      });\n      each(nodes, function (node) {\n        var sy = 0;\n        var ty = 0;\n        each(node.outEdges, function (edge) {\n          edge.setLayout({\n            sy: sy\n          }, true);\n          sy += edge.getLayout().dy;\n        });\n        each(node.inEdges, function (edge) {\n          edge.setLayout({\n            ty: ty\n          }, true);\n          ty += edge.getLayout().dy;\n        });\n      });\n    }\n\n    function sankeyVisual(ecModel) {\n      ecModel.eachSeriesByType('sankey', function (seriesModel) {\n        var graph = seriesModel.getGraph();\n        var nodes = graph.nodes;\n        var edges = graph.edges;\n\n        if (nodes.length) {\n          var minValue_1 = Infinity;\n          var maxValue_1 = -Infinity;\n          each(nodes, function (node) {\n            var nodeValue = node.getLayout().value;\n\n            if (nodeValue < minValue_1) {\n              minValue_1 = nodeValue;\n            }\n\n            if (nodeValue > maxValue_1) {\n              maxValue_1 = nodeValue;\n            }\n          });\n          each(nodes, function (node) {\n            var mapping = new VisualMapping({\n              type: 'color',\n              mappingMethod: 'linear',\n              dataExtent: [minValue_1, maxValue_1],\n              visual: seriesModel.get('color')\n            });\n            var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value);\n            var customColor = node.getModel().get(['itemStyle', 'color']);\n\n            if (customColor != null) {\n              node.setVisual('color', customColor);\n              node.setVisual('style', {\n                fill: customColor\n              });\n            } else {\n              node.setVisual('color', mapValueToColor);\n              node.setVisual('style', {\n                fill: mapValueToColor\n              });\n            }\n          });\n        }\n\n        if (edges.length) {\n          each(edges, function (edge) {\n            var edgeStyle = edge.getModel().get('lineStyle');\n            edge.setVisual('style', edgeStyle);\n          });\n        }\n      });\n    }\n\n    function install$i(registers) {\n      registers.registerChartView(SankeyView);\n      registers.registerSeriesModel(SankeySeriesModel);\n      registers.registerLayout(sankeyLayout);\n      registers.registerVisual(sankeyVisual);\n      registers.registerAction({\n        type: 'dragNode',\n        event: 'dragnode',\n        // here can only use 'update' now, other value is not support in echarts.\n        update: 'update'\n      }, function (payload, ecModel) {\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'sankey',\n          query: payload\n        }, function (seriesModel) {\n          seriesModel.setNodePosition(payload.dataIndex, [payload.localX, payload.localY]);\n        });\n      });\n    }\n\n    var WhiskerBoxCommonMixin =\n    /** @class */\n    function () {\n      function WhiskerBoxCommonMixin() {}\n      /**\n       * @override\n       */\n\n\n      WhiskerBoxCommonMixin.prototype.getInitialData = function (option, ecModel) {\n        // When both types of xAxis and yAxis are 'value', layout is\n        // needed to be specified by user. Otherwise, layout can be\n        // judged by which axis is category.\n        var ordinalMeta;\n        var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex'));\n        var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex'));\n        var xAxisType = xAxisModel.get('type');\n        var yAxisType = yAxisModel.get('type');\n        var addOrdinal; // FIXME\n        // Consider time axis.\n\n        if (xAxisType === 'category') {\n          option.layout = 'horizontal';\n          ordinalMeta = xAxisModel.getOrdinalMeta();\n          addOrdinal = true;\n        } else if (yAxisType === 'category') {\n          option.layout = 'vertical';\n          ordinalMeta = yAxisModel.getOrdinalMeta();\n          addOrdinal = true;\n        } else {\n          option.layout = option.layout || 'horizontal';\n        }\n\n        var coordDims = ['x', 'y'];\n        var baseAxisDimIndex = option.layout === 'horizontal' ? 0 : 1;\n        var baseAxisDim = this._baseAxisDim = coordDims[baseAxisDimIndex];\n        var otherAxisDim = coordDims[1 - baseAxisDimIndex];\n        var axisModels = [xAxisModel, yAxisModel];\n        var baseAxisType = axisModels[baseAxisDimIndex].get('type');\n        var otherAxisType = axisModels[1 - baseAxisDimIndex].get('type');\n        var data = option.data; // Clone a new data for next setOption({}) usage.\n        // Avoid modifying current data will affect further update.\n\n        if (data && addOrdinal) {\n          var newOptionData_1 = [];\n          each(data, function (item, index) {\n            var newItem;\n\n            if (isArray(item)) {\n              newItem = item.slice(); // Modify current using data.\n\n              item.unshift(index);\n            } else if (isArray(item.value)) {\n              newItem = extend({}, item);\n              newItem.value = newItem.value.slice(); // Modify current using data.\n\n              item.value.unshift(index);\n            } else {\n              newItem = item;\n            }\n\n            newOptionData_1.push(newItem);\n          });\n          option.data = newOptionData_1;\n        }\n\n        var defaultValueDimensions = this.defaultValueDimensions;\n        var coordDimensions = [{\n          name: baseAxisDim,\n          type: getDimensionTypeByAxis(baseAxisType),\n          ordinalMeta: ordinalMeta,\n          otherDims: {\n            tooltip: false,\n            itemName: 0\n          },\n          dimsDef: ['base']\n        }, {\n          name: otherAxisDim,\n          type: getDimensionTypeByAxis(otherAxisType),\n          dimsDef: defaultValueDimensions.slice()\n        }];\n        return createSeriesDataSimply(this, {\n          coordDimensions: coordDimensions,\n          dimensionsCount: defaultValueDimensions.length + 1,\n          encodeDefaulter: curry(makeSeriesEncodeForAxisCoordSys, coordDimensions, this)\n        });\n      };\n      /**\n       * If horizontal, base axis is x, otherwise y.\n       * @override\n       */\n\n\n      WhiskerBoxCommonMixin.prototype.getBaseAxis = function () {\n        var dim = this._baseAxisDim;\n        return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis;\n      };\n\n      return WhiskerBoxCommonMixin;\n    }();\n\n    var BoxplotSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(BoxplotSeriesModel, _super);\n\n      function BoxplotSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BoxplotSeriesModel.type; // TODO\n        // box width represents group size, so dimension should have 'size'.\n\n        /**\n         * @see <https://en.wikipedia.org/wiki/Box_plot>\n         * The meanings of 'min' and 'max' depend on user,\n         * and echarts do not need to know it.\n         * @readOnly\n         */\n\n        _this.defaultValueDimensions = [{\n          name: 'min',\n          defaultTooltip: true\n        }, {\n          name: 'Q1',\n          defaultTooltip: true\n        }, {\n          name: 'median',\n          defaultTooltip: true\n        }, {\n          name: 'Q3',\n          defaultTooltip: true\n        }, {\n          name: 'max',\n          defaultTooltip: true\n        }];\n        _this.visualDrawType = 'stroke';\n        return _this;\n      }\n\n      BoxplotSeriesModel.type = 'series.boxplot';\n      BoxplotSeriesModel.dependencies = ['xAxis', 'yAxis', 'grid'];\n      BoxplotSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        layout: null,\n        boxWidth: [7, 50],\n        itemStyle: {\n          color: '#fff',\n          borderWidth: 1\n        },\n        emphasis: {\n          scale: true,\n          itemStyle: {\n            borderWidth: 2,\n            shadowBlur: 5,\n            shadowOffsetX: 1,\n            shadowOffsetY: 1,\n            shadowColor: 'rgba(0,0,0,0.2)'\n          }\n        },\n        animationDuration: 800\n      };\n      return BoxplotSeriesModel;\n    }(SeriesModel);\n\n    mixin(BoxplotSeriesModel, WhiskerBoxCommonMixin, true);\n\n    var BoxplotView =\n    /** @class */\n    function (_super) {\n      __extends(BoxplotView, _super);\n\n      function BoxplotView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BoxplotView.type;\n        return _this;\n      }\n\n      BoxplotView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n        var group = this.group;\n        var oldData = this._data; // There is no old data only when first rendering or switching from\n        // stream mode to normal mode, where previous elements should be removed.\n\n        if (!this._data) {\n          group.removeAll();\n        }\n\n        var constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0;\n        data.diff(oldData).add(function (newIdx) {\n          if (data.hasValue(newIdx)) {\n            var itemLayout = data.getItemLayout(newIdx);\n            var symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true);\n            data.setItemGraphicEl(newIdx, symbolEl);\n            group.add(symbolEl);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var symbolEl = oldData.getItemGraphicEl(oldIdx); // Empty data\n\n          if (!data.hasValue(newIdx)) {\n            group.remove(symbolEl);\n            return;\n          }\n\n          var itemLayout = data.getItemLayout(newIdx);\n\n          if (!symbolEl) {\n            symbolEl = createNormalBox(itemLayout, data, newIdx, constDim);\n          } else {\n            saveOldStyle(symbolEl);\n            updateNormalBoxData(itemLayout, symbolEl, data, newIdx);\n          }\n\n          group.add(symbolEl);\n          data.setItemGraphicEl(newIdx, symbolEl);\n        }).remove(function (oldIdx) {\n          var el = oldData.getItemGraphicEl(oldIdx);\n          el && group.remove(el);\n        }).execute();\n        this._data = data;\n      };\n\n      BoxplotView.prototype.remove = function (ecModel) {\n        var group = this.group;\n        var data = this._data;\n        this._data = null;\n        data && data.eachItemGraphicEl(function (el) {\n          el && group.remove(el);\n        });\n      };\n\n      BoxplotView.type = 'boxplot';\n      return BoxplotView;\n    }(ChartView);\n\n    var BoxPathShape =\n    /** @class */\n    function () {\n      function BoxPathShape() {}\n\n      return BoxPathShape;\n    }();\n\n    var BoxPath =\n    /** @class */\n    function (_super) {\n      __extends(BoxPath, _super);\n\n      function BoxPath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'boxplotBoxPath';\n        return _this;\n      }\n\n      BoxPath.prototype.getDefaultShape = function () {\n        return new BoxPathShape();\n      };\n\n      BoxPath.prototype.buildPath = function (ctx, shape) {\n        var ends = shape.points;\n        var i = 0;\n        ctx.moveTo(ends[i][0], ends[i][1]);\n        i++;\n\n        for (; i < 4; i++) {\n          ctx.lineTo(ends[i][0], ends[i][1]);\n        }\n\n        ctx.closePath();\n\n        for (; i < ends.length; i++) {\n          ctx.moveTo(ends[i][0], ends[i][1]);\n          i++;\n          ctx.lineTo(ends[i][0], ends[i][1]);\n        }\n      };\n\n      return BoxPath;\n    }(Path);\n\n    function createNormalBox(itemLayout, data, dataIndex, constDim, isInit) {\n      var ends = itemLayout.ends;\n      var el = new BoxPath({\n        shape: {\n          points: isInit ? transInit(ends, constDim, itemLayout) : ends\n        }\n      });\n      updateNormalBoxData(itemLayout, el, data, dataIndex, isInit);\n      return el;\n    }\n\n    function updateNormalBoxData(itemLayout, el, data, dataIndex, isInit) {\n      var seriesModel = data.hostModel;\n      var updateMethod = graphic[isInit ? 'initProps' : 'updateProps'];\n      updateMethod(el, {\n        shape: {\n          points: itemLayout.ends\n        }\n      }, seriesModel, dataIndex);\n      el.useStyle(data.getItemVisual(dataIndex, 'style'));\n      el.style.strokeNoScale = true;\n      el.z2 = 100;\n      var itemModel = data.getItemModel(dataIndex);\n      var emphasisModel = itemModel.getModel('emphasis');\n      setStatesStylesFromModel(el, itemModel);\n      toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n    }\n\n    function transInit(points, dim, itemLayout) {\n      return map(points, function (point) {\n        point = point.slice();\n        point[dim] = itemLayout.initBaseline;\n        return point;\n      });\n    }\n\n    var each$6 = each;\n    function boxplotLayout(ecModel) {\n      var groupResult = groupSeriesByAxis(ecModel);\n      each$6(groupResult, function (groupItem) {\n        var seriesModels = groupItem.seriesModels;\n\n        if (!seriesModels.length) {\n          return;\n        }\n\n        calculateBase(groupItem);\n        each$6(seriesModels, function (seriesModel, idx) {\n          layoutSingleSeries(seriesModel, groupItem.boxOffsetList[idx], groupItem.boxWidthList[idx]);\n        });\n      });\n    }\n    /**\n     * Group series by axis.\n     */\n\n    function groupSeriesByAxis(ecModel) {\n      var result = [];\n      var axisList = [];\n      ecModel.eachSeriesByType('boxplot', function (seriesModel) {\n        var baseAxis = seriesModel.getBaseAxis();\n        var idx = indexOf(axisList, baseAxis);\n\n        if (idx < 0) {\n          idx = axisList.length;\n          axisList[idx] = baseAxis;\n          result[idx] = {\n            axis: baseAxis,\n            seriesModels: []\n          };\n        }\n\n        result[idx].seriesModels.push(seriesModel);\n      });\n      return result;\n    }\n    /**\n     * Calculate offset and box width for each series.\n     */\n\n\n    function calculateBase(groupItem) {\n      var baseAxis = groupItem.axis;\n      var seriesModels = groupItem.seriesModels;\n      var seriesCount = seriesModels.length;\n      var boxWidthList = groupItem.boxWidthList = [];\n      var boxOffsetList = groupItem.boxOffsetList = [];\n      var boundList = [];\n      var bandWidth;\n\n      if (baseAxis.type === 'category') {\n        bandWidth = baseAxis.getBandWidth();\n      } else {\n        var maxDataCount_1 = 0;\n        each$6(seriesModels, function (seriesModel) {\n          maxDataCount_1 = Math.max(maxDataCount_1, seriesModel.getData().count());\n        });\n        var extent = baseAxis.getExtent();\n        bandWidth = Math.abs(extent[1] - extent[0]) / maxDataCount_1;\n      }\n\n      each$6(seriesModels, function (seriesModel) {\n        var boxWidthBound = seriesModel.get('boxWidth');\n\n        if (!isArray(boxWidthBound)) {\n          boxWidthBound = [boxWidthBound, boxWidthBound];\n        }\n\n        boundList.push([parsePercent$1(boxWidthBound[0], bandWidth) || 0, parsePercent$1(boxWidthBound[1], bandWidth) || 0]);\n      });\n      var availableWidth = bandWidth * 0.8 - 2;\n      var boxGap = availableWidth / seriesCount * 0.3;\n      var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount;\n      var base = boxWidth / 2 - availableWidth / 2;\n      each$6(seriesModels, function (seriesModel, idx) {\n        boxOffsetList.push(base);\n        base += boxGap + boxWidth;\n        boxWidthList.push(Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1]));\n      });\n    }\n    /**\n     * Calculate points location for each series.\n     */\n\n\n    function layoutSingleSeries(seriesModel, offset, boxWidth) {\n      var coordSys = seriesModel.coordinateSystem;\n      var data = seriesModel.getData();\n      var halfWidth = boxWidth / 2;\n      var cDimIdx = seriesModel.get('layout') === 'horizontal' ? 0 : 1;\n      var vDimIdx = 1 - cDimIdx;\n      var coordDims = ['x', 'y'];\n      var cDim = data.mapDimension(coordDims[cDimIdx]);\n      var vDims = data.mapDimensionsAll(coordDims[vDimIdx]);\n\n      if (cDim == null || vDims.length < 5) {\n        return;\n      }\n\n      for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) {\n        var axisDimVal = data.get(cDim, dataIndex);\n        var median = getPoint(axisDimVal, vDims[2], dataIndex);\n        var end1 = getPoint(axisDimVal, vDims[0], dataIndex);\n        var end2 = getPoint(axisDimVal, vDims[1], dataIndex);\n        var end4 = getPoint(axisDimVal, vDims[3], dataIndex);\n        var end5 = getPoint(axisDimVal, vDims[4], dataIndex);\n        var ends = [];\n        addBodyEnd(ends, end2, false);\n        addBodyEnd(ends, end4, true);\n        ends.push(end1, end2, end5, end4);\n        layEndLine(ends, end1);\n        layEndLine(ends, end5);\n        layEndLine(ends, median);\n        data.setItemLayout(dataIndex, {\n          initBaseline: median[vDimIdx],\n          ends: ends\n        });\n      }\n\n      function getPoint(axisDimVal, dim, dataIndex) {\n        var val = data.get(dim, dataIndex);\n        var p = [];\n        p[cDimIdx] = axisDimVal;\n        p[vDimIdx] = val;\n        var point;\n\n        if (isNaN(axisDimVal) || isNaN(val)) {\n          point = [NaN, NaN];\n        } else {\n          point = coordSys.dataToPoint(p);\n          point[cDimIdx] += offset;\n        }\n\n        return point;\n      }\n\n      function addBodyEnd(ends, point, start) {\n        var point1 = point.slice();\n        var point2 = point.slice();\n        point1[cDimIdx] += halfWidth;\n        point2[cDimIdx] -= halfWidth;\n        start ? ends.push(point1, point2) : ends.push(point2, point1);\n      }\n\n      function layEndLine(ends, endCenter) {\n        var from = endCenter.slice();\n        var to = endCenter.slice();\n        from[cDimIdx] -= halfWidth;\n        to[cDimIdx] += halfWidth;\n        ends.push(from, to);\n      }\n    }\n\n    /**\n     * See:\n     *  <https://en.wikipedia.org/wiki/Box_plot#cite_note-frigge_hoaglin_iglewicz-2>\n     *  <http://stat.ethz.ch/R-manual/R-devel/library/grDevices/html/boxplot.stats.html>\n     *\n     * Helper method for preparing data.\n     *\n     * @param rawData like\n     *        [\n     *            [12,232,443], (raw data set for the first box)\n     *            [3843,5545,1232], (raw data set for the second box)\n     *            ...\n     *        ]\n     * @param opt.boundIQR=1.5 Data less than min bound is outlier.\n     *      default 1.5, means Q1 - 1.5 * (Q3 - Q1).\n     *      If 'none'/0 passed, min bound will not be used.\n     */\n\n    function prepareBoxplotData(rawData, opt) {\n      opt = opt || {};\n      var boxData = [];\n      var outliers = [];\n      var boundIQR = opt.boundIQR;\n      var useExtreme = boundIQR === 'none' || boundIQR === 0;\n\n      for (var i = 0; i < rawData.length; i++) {\n        var ascList = asc(rawData[i].slice());\n        var Q1 = quantile(ascList, 0.25);\n        var Q2 = quantile(ascList, 0.5);\n        var Q3 = quantile(ascList, 0.75);\n        var min = ascList[0];\n        var max = ascList[ascList.length - 1];\n        var bound = (boundIQR == null ? 1.5 : boundIQR) * (Q3 - Q1);\n        var low = useExtreme ? min : Math.max(min, Q1 - bound);\n        var high = useExtreme ? max : Math.min(max, Q3 + bound);\n        var itemNameFormatter = opt.itemNameFormatter;\n        var itemName = isFunction(itemNameFormatter) ? itemNameFormatter({\n          value: i\n        }) : isString(itemNameFormatter) ? itemNameFormatter.replace('{value}', i + '') : i + '';\n        boxData.push([itemName, low, Q1, Q2, Q3, high]);\n\n        for (var j = 0; j < ascList.length; j++) {\n          var dataItem = ascList[j];\n\n          if (dataItem < low || dataItem > high) {\n            var outlier = [itemName, dataItem];\n            outliers.push(outlier);\n          }\n        }\n      }\n\n      return {\n        boxData: boxData,\n        outliers: outliers\n      };\n    }\n\n    var boxplotTransform = {\n      type: 'echarts:boxplot',\n      transform: function transform(params) {\n        var upstream = params.upstream;\n\n        if (upstream.sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS) {\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('source data is not applicable for this boxplot transform. Expect number[][].');\n          }\n\n          throwError(errMsg);\n        }\n\n        var result = prepareBoxplotData(upstream.getRawData(), params.config);\n        return [{\n          dimensions: ['ItemName', 'Low', 'Q1', 'Q2', 'Q3', 'High'],\n          data: result.boxData\n        }, {\n          data: result.outliers\n        }];\n      }\n    };\n\n    function install$j(registers) {\n      registers.registerSeriesModel(BoxplotSeriesModel);\n      registers.registerChartView(BoxplotView);\n      registers.registerLayout(boxplotLayout);\n      registers.registerTransform(boxplotTransform);\n    }\n\n    var SKIP_PROPS = ['color', 'borderColor'];\n\n    var CandlestickView =\n    /** @class */\n    function (_super) {\n      __extends(CandlestickView, _super);\n\n      function CandlestickView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CandlestickView.type;\n        return _this;\n      }\n\n      CandlestickView.prototype.render = function (seriesModel, ecModel, api) {\n        // If there is clipPath created in large mode. Remove it.\n        this.group.removeClipPath(); // Clear previously rendered progressive elements.\n\n        this._progressiveEls = null;\n\n        this._updateDrawMode(seriesModel);\n\n        this._isLargeDraw ? this._renderLarge(seriesModel) : this._renderNormal(seriesModel);\n      };\n\n      CandlestickView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n        this._clear();\n\n        this._updateDrawMode(seriesModel);\n      };\n\n      CandlestickView.prototype.incrementalRender = function (params, seriesModel, ecModel, api) {\n        this._progressiveEls = [];\n        this._isLargeDraw ? this._incrementalRenderLarge(params, seriesModel) : this._incrementalRenderNormal(params, seriesModel);\n      };\n\n      CandlestickView.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      CandlestickView.prototype._updateDrawMode = function (seriesModel) {\n        var isLargeDraw = seriesModel.pipelineContext.large;\n\n        if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) {\n          this._isLargeDraw = isLargeDraw;\n\n          this._clear();\n        }\n      };\n\n      CandlestickView.prototype._renderNormal = function (seriesModel) {\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var group = this.group;\n        var isSimpleBox = data.getLayout('isSimpleBox');\n        var needsClip = seriesModel.get('clip', true);\n        var coord = seriesModel.coordinateSystem;\n        var clipArea = coord.getArea && coord.getArea(); // There is no old data only when first rendering or switching from\n        // stream mode to normal mode, where previous elements should be removed.\n\n        if (!this._data) {\n          group.removeAll();\n        }\n\n        data.diff(oldData).add(function (newIdx) {\n          if (data.hasValue(newIdx)) {\n            var itemLayout = data.getItemLayout(newIdx);\n\n            if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) {\n              return;\n            }\n\n            var el = createNormalBox$1(itemLayout, newIdx, true);\n            initProps(el, {\n              shape: {\n                points: itemLayout.ends\n              }\n            }, seriesModel, newIdx);\n            setBoxCommon(el, data, newIdx, isSimpleBox);\n            group.add(el);\n            data.setItemGraphicEl(newIdx, el);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var el = oldData.getItemGraphicEl(oldIdx); // Empty data\n\n          if (!data.hasValue(newIdx)) {\n            group.remove(el);\n            return;\n          }\n\n          var itemLayout = data.getItemLayout(newIdx);\n\n          if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) {\n            group.remove(el);\n            return;\n          }\n\n          if (!el) {\n            el = createNormalBox$1(itemLayout);\n          } else {\n            updateProps(el, {\n              shape: {\n                points: itemLayout.ends\n              }\n            }, seriesModel, newIdx);\n            saveOldStyle(el);\n          }\n\n          setBoxCommon(el, data, newIdx, isSimpleBox);\n          group.add(el);\n          data.setItemGraphicEl(newIdx, el);\n        }).remove(function (oldIdx) {\n          var el = oldData.getItemGraphicEl(oldIdx);\n          el && group.remove(el);\n        }).execute();\n        this._data = data;\n      };\n\n      CandlestickView.prototype._renderLarge = function (seriesModel) {\n        this._clear();\n\n        createLarge$1(seriesModel, this.group);\n        var clipPath = seriesModel.get('clip', true) ? createClipPath(seriesModel.coordinateSystem, false, seriesModel) : null;\n\n        if (clipPath) {\n          this.group.setClipPath(clipPath);\n        } else {\n          this.group.removeClipPath();\n        }\n      };\n\n      CandlestickView.prototype._incrementalRenderNormal = function (params, seriesModel) {\n        var data = seriesModel.getData();\n        var isSimpleBox = data.getLayout('isSimpleBox');\n        var dataIndex;\n\n        while ((dataIndex = params.next()) != null) {\n          var itemLayout = data.getItemLayout(dataIndex);\n          var el = createNormalBox$1(itemLayout);\n          setBoxCommon(el, data, dataIndex, isSimpleBox);\n          el.incremental = true;\n          this.group.add(el);\n\n          this._progressiveEls.push(el);\n        }\n      };\n\n      CandlestickView.prototype._incrementalRenderLarge = function (params, seriesModel) {\n        createLarge$1(seriesModel, this.group, this._progressiveEls, true);\n      };\n\n      CandlestickView.prototype.remove = function (ecModel) {\n        this._clear();\n      };\n\n      CandlestickView.prototype._clear = function () {\n        this.group.removeAll();\n        this._data = null;\n      };\n\n      CandlestickView.type = 'candlestick';\n      return CandlestickView;\n    }(ChartView);\n\n    var NormalBoxPathShape =\n    /** @class */\n    function () {\n      function NormalBoxPathShape() {}\n\n      return NormalBoxPathShape;\n    }();\n\n    var NormalBoxPath =\n    /** @class */\n    function (_super) {\n      __extends(NormalBoxPath, _super);\n\n      function NormalBoxPath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'normalCandlestickBox';\n        return _this;\n      }\n\n      NormalBoxPath.prototype.getDefaultShape = function () {\n        return new NormalBoxPathShape();\n      };\n\n      NormalBoxPath.prototype.buildPath = function (ctx, shape) {\n        var ends = shape.points;\n\n        if (this.__simpleBox) {\n          ctx.moveTo(ends[4][0], ends[4][1]);\n          ctx.lineTo(ends[6][0], ends[6][1]);\n        } else {\n          ctx.moveTo(ends[0][0], ends[0][1]);\n          ctx.lineTo(ends[1][0], ends[1][1]);\n          ctx.lineTo(ends[2][0], ends[2][1]);\n          ctx.lineTo(ends[3][0], ends[3][1]);\n          ctx.closePath();\n          ctx.moveTo(ends[4][0], ends[4][1]);\n          ctx.lineTo(ends[5][0], ends[5][1]);\n          ctx.moveTo(ends[6][0], ends[6][1]);\n          ctx.lineTo(ends[7][0], ends[7][1]);\n        }\n      };\n\n      return NormalBoxPath;\n    }(Path);\n\n    function createNormalBox$1(itemLayout, dataIndex, isInit) {\n      var ends = itemLayout.ends;\n      return new NormalBoxPath({\n        shape: {\n          points: isInit ? transInit$1(ends, itemLayout) : ends\n        },\n        z2: 100\n      });\n    }\n\n    function isNormalBoxClipped(clipArea, itemLayout) {\n      var clipped = true;\n\n      for (var i = 0; i < itemLayout.ends.length; i++) {\n        // If any point are in the region.\n        if (clipArea.contain(itemLayout.ends[i][0], itemLayout.ends[i][1])) {\n          clipped = false;\n          break;\n        }\n      }\n\n      return clipped;\n    }\n\n    function setBoxCommon(el, data, dataIndex, isSimpleBox) {\n      var itemModel = data.getItemModel(dataIndex);\n      el.useStyle(data.getItemVisual(dataIndex, 'style'));\n      el.style.strokeNoScale = true;\n      el.__simpleBox = isSimpleBox;\n      setStatesStylesFromModel(el, itemModel);\n    }\n\n    function transInit$1(points, itemLayout) {\n      return map(points, function (point) {\n        point = point.slice();\n        point[1] = itemLayout.initBaseline;\n        return point;\n      });\n    }\n\n    var LargeBoxPathShape =\n    /** @class */\n    function () {\n      function LargeBoxPathShape() {}\n\n      return LargeBoxPathShape;\n    }();\n\n    var LargeBoxPath =\n    /** @class */\n    function (_super) {\n      __extends(LargeBoxPath, _super);\n\n      function LargeBoxPath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'largeCandlestickBox';\n        return _this;\n      }\n\n      LargeBoxPath.prototype.getDefaultShape = function () {\n        return new LargeBoxPathShape();\n      };\n\n      LargeBoxPath.prototype.buildPath = function (ctx, shape) {\n        // Drawing lines is more efficient than drawing\n        // a whole line or drawing rects.\n        var points = shape.points;\n\n        for (var i = 0; i < points.length;) {\n          if (this.__sign === points[i++]) {\n            var x = points[i++];\n            ctx.moveTo(x, points[i++]);\n            ctx.lineTo(x, points[i++]);\n          } else {\n            i += 3;\n          }\n        }\n      };\n\n      return LargeBoxPath;\n    }(Path);\n\n    function createLarge$1(seriesModel, group, progressiveEls, incremental) {\n      var data = seriesModel.getData();\n      var largePoints = data.getLayout('largePoints');\n      var elP = new LargeBoxPath({\n        shape: {\n          points: largePoints\n        },\n        __sign: 1,\n        ignoreCoarsePointer: true\n      });\n      group.add(elP);\n      var elN = new LargeBoxPath({\n        shape: {\n          points: largePoints\n        },\n        __sign: -1,\n        ignoreCoarsePointer: true\n      });\n      group.add(elN);\n      var elDoji = new LargeBoxPath({\n        shape: {\n          points: largePoints\n        },\n        __sign: 0,\n        ignoreCoarsePointer: true\n      });\n      group.add(elDoji);\n      setLargeStyle(1, elP, seriesModel);\n      setLargeStyle(-1, elN, seriesModel);\n      setLargeStyle(0, elDoji, seriesModel);\n\n      if (incremental) {\n        elP.incremental = true;\n        elN.incremental = true;\n      }\n\n      if (progressiveEls) {\n        progressiveEls.push(elP, elN);\n      }\n    }\n\n    function setLargeStyle(sign, el, seriesModel, data) {\n      // TODO put in visual?\n      var borderColor = seriesModel.get(['itemStyle', sign > 0 ? 'borderColor' : 'borderColor0']) // Use color for border color by default.\n      || seriesModel.get(['itemStyle', sign > 0 ? 'color' : 'color0']);\n\n      if (sign === 0) {\n        borderColor = seriesModel.get(['itemStyle', 'borderColorDoji']);\n      } // Color must be excluded.\n      // Because symbol provide setColor individually to set fill and stroke\n\n\n      var itemStyle = seriesModel.getModel('itemStyle').getItemStyle(SKIP_PROPS);\n      el.useStyle(itemStyle);\n      el.style.fill = null;\n      el.style.stroke = borderColor;\n    }\n\n    var CandlestickSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(CandlestickSeriesModel, _super);\n\n      function CandlestickSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CandlestickSeriesModel.type;\n        _this.defaultValueDimensions = [{\n          name: 'open',\n          defaultTooltip: true\n        }, {\n          name: 'close',\n          defaultTooltip: true\n        }, {\n          name: 'lowest',\n          defaultTooltip: true\n        }, {\n          name: 'highest',\n          defaultTooltip: true\n        }];\n        return _this;\n      }\n      /**\n       * Get dimension for shadow in dataZoom\n       * @return dimension name\n       */\n\n\n      CandlestickSeriesModel.prototype.getShadowDim = function () {\n        return 'open';\n      };\n\n      CandlestickSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n        var itemLayout = data.getItemLayout(dataIndex);\n        return itemLayout && selectors.rect(itemLayout.brushRect);\n      };\n\n      CandlestickSeriesModel.type = 'series.candlestick';\n      CandlestickSeriesModel.dependencies = ['xAxis', 'yAxis', 'grid'];\n      CandlestickSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        layout: null,\n        clip: true,\n        itemStyle: {\n          color: '#eb5454',\n          color0: '#47b262',\n          borderColor: '#eb5454',\n          borderColor0: '#47b262',\n          borderColorDoji: null,\n          // borderColor: '#d24040',\n          // borderColor0: '#398f4f',\n          borderWidth: 1\n        },\n        emphasis: {\n          scale: true,\n          itemStyle: {\n            borderWidth: 2\n          }\n        },\n        barMaxWidth: null,\n        barMinWidth: null,\n        barWidth: null,\n        large: true,\n        largeThreshold: 600,\n        progressive: 3e3,\n        progressiveThreshold: 1e4,\n        progressiveChunkMode: 'mod',\n        animationEasing: 'linear',\n        animationDuration: 300\n      };\n      return CandlestickSeriesModel;\n    }(SeriesModel);\n\n    mixin(CandlestickSeriesModel, WhiskerBoxCommonMixin, true);\n\n    function candlestickPreprocessor(option) {\n      if (!option || !isArray(option.series)) {\n        return;\n      } // Translate 'k' to 'candlestick'.\n\n\n      each(option.series, function (seriesItem) {\n        if (isObject(seriesItem) && seriesItem.type === 'k') {\n          seriesItem.type = 'candlestick';\n        }\n      });\n    }\n\n    var positiveBorderColorQuery = ['itemStyle', 'borderColor'];\n    var negativeBorderColorQuery = ['itemStyle', 'borderColor0'];\n    var dojiBorderColorQuery = ['itemStyle', 'borderColorDoji'];\n    var positiveColorQuery = ['itemStyle', 'color'];\n    var negativeColorQuery = ['itemStyle', 'color0'];\n    var candlestickVisual = {\n      seriesType: 'candlestick',\n      plan: createRenderPlanner(),\n      // For legend.\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        function getColor(sign, model) {\n          return model.get(sign > 0 ? positiveColorQuery : negativeColorQuery);\n        }\n\n        function getBorderColor(sign, model) {\n          return model.get(sign === 0 ? dojiBorderColorQuery : sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery);\n        } // Only visible series has each data be visual encoded\n\n\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var isLargeRender = seriesModel.pipelineContext.large;\n        return !isLargeRender && {\n          progress: function (params, data) {\n            var dataIndex;\n\n            while ((dataIndex = params.next()) != null) {\n              var itemModel = data.getItemModel(dataIndex);\n              var sign = data.getItemLayout(dataIndex).sign;\n              var style = itemModel.getItemStyle();\n              style.fill = getColor(sign, itemModel);\n              style.stroke = getBorderColor(sign, itemModel) || style.fill;\n              var existsStyle = data.ensureUniqueItemVisual(dataIndex, 'style');\n              extend(existsStyle, style);\n            }\n          }\n        };\n      }\n    };\n\n    var candlestickLayout = {\n      seriesType: 'candlestick',\n      plan: createRenderPlanner(),\n      reset: function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n        var data = seriesModel.getData();\n        var candleWidth = calculateCandleWidth(seriesModel, data);\n        var cDimIdx = 0;\n        var vDimIdx = 1;\n        var coordDims = ['x', 'y'];\n        var cDimI = data.getDimensionIndex(data.mapDimension(coordDims[cDimIdx]));\n        var vDimsI = map(data.mapDimensionsAll(coordDims[vDimIdx]), data.getDimensionIndex, data);\n        var openDimI = vDimsI[0];\n        var closeDimI = vDimsI[1];\n        var lowestDimI = vDimsI[2];\n        var highestDimI = vDimsI[3];\n        data.setLayout({\n          candleWidth: candleWidth,\n          // The value is experimented visually.\n          isSimpleBox: candleWidth <= 1.3\n        });\n\n        if (cDimI < 0 || vDimsI.length < 4) {\n          return;\n        }\n\n        return {\n          progress: seriesModel.pipelineContext.large ? largeProgress : normalProgress\n        };\n\n        function normalProgress(params, data) {\n          var dataIndex;\n          var store = data.getStore();\n\n          while ((dataIndex = params.next()) != null) {\n            var axisDimVal = store.get(cDimI, dataIndex);\n            var openVal = store.get(openDimI, dataIndex);\n            var closeVal = store.get(closeDimI, dataIndex);\n            var lowestVal = store.get(lowestDimI, dataIndex);\n            var highestVal = store.get(highestDimI, dataIndex);\n            var ocLow = Math.min(openVal, closeVal);\n            var ocHigh = Math.max(openVal, closeVal);\n            var ocLowPoint = getPoint(ocLow, axisDimVal);\n            var ocHighPoint = getPoint(ocHigh, axisDimVal);\n            var lowestPoint = getPoint(lowestVal, axisDimVal);\n            var highestPoint = getPoint(highestVal, axisDimVal);\n            var ends = [];\n            addBodyEnd(ends, ocHighPoint, 0);\n            addBodyEnd(ends, ocLowPoint, 1);\n            ends.push(subPixelOptimizePoint(highestPoint), subPixelOptimizePoint(ocHighPoint), subPixelOptimizePoint(lowestPoint), subPixelOptimizePoint(ocLowPoint));\n            var itemModel = data.getItemModel(dataIndex);\n            var hasDojiColor = !!itemModel.get(['itemStyle', 'borderColorDoji']);\n            data.setItemLayout(dataIndex, {\n              sign: getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor),\n              initBaseline: openVal > closeVal ? ocHighPoint[vDimIdx] : ocLowPoint[vDimIdx],\n              ends: ends,\n              brushRect: makeBrushRect(lowestVal, highestVal, axisDimVal)\n            });\n          }\n\n          function getPoint(val, axisDimVal) {\n            var p = [];\n            p[cDimIdx] = axisDimVal;\n            p[vDimIdx] = val;\n            return isNaN(axisDimVal) || isNaN(val) ? [NaN, NaN] : coordSys.dataToPoint(p);\n          }\n\n          function addBodyEnd(ends, point, start) {\n            var point1 = point.slice();\n            var point2 = point.slice();\n            point1[cDimIdx] = subPixelOptimize$1(point1[cDimIdx] + candleWidth / 2, 1, false);\n            point2[cDimIdx] = subPixelOptimize$1(point2[cDimIdx] - candleWidth / 2, 1, true);\n            start ? ends.push(point1, point2) : ends.push(point2, point1);\n          }\n\n          function makeBrushRect(lowestVal, highestVal, axisDimVal) {\n            var pmin = getPoint(lowestVal, axisDimVal);\n            var pmax = getPoint(highestVal, axisDimVal);\n            pmin[cDimIdx] -= candleWidth / 2;\n            pmax[cDimIdx] -= candleWidth / 2;\n            return {\n              x: pmin[0],\n              y: pmin[1],\n              width:  candleWidth ,\n              height:  pmax[1] - pmin[1] \n            };\n          }\n\n          function subPixelOptimizePoint(point) {\n            point[cDimIdx] = subPixelOptimize$1(point[cDimIdx], 1);\n            return point;\n          }\n        }\n\n        function largeProgress(params, data) {\n          // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...]\n          var points = createFloat32Array(params.count * 4);\n          var offset = 0;\n          var point;\n          var tmpIn = [];\n          var tmpOut = [];\n          var dataIndex;\n          var store = data.getStore();\n          var hasDojiColor = !!seriesModel.get(['itemStyle', 'borderColorDoji']);\n\n          while ((dataIndex = params.next()) != null) {\n            var axisDimVal = store.get(cDimI, dataIndex);\n            var openVal = store.get(openDimI, dataIndex);\n            var closeVal = store.get(closeDimI, dataIndex);\n            var lowestVal = store.get(lowestDimI, dataIndex);\n            var highestVal = store.get(highestDimI, dataIndex);\n\n            if (isNaN(axisDimVal) || isNaN(lowestVal) || isNaN(highestVal)) {\n              points[offset++] = NaN;\n              offset += 3;\n              continue;\n            }\n\n            points[offset++] = getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor);\n            tmpIn[cDimIdx] = axisDimVal;\n            tmpIn[vDimIdx] = lowestVal;\n            point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n            points[offset++] = point ? point[0] : NaN;\n            points[offset++] = point ? point[1] : NaN;\n            tmpIn[vDimIdx] = highestVal;\n            point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n            points[offset++] = point ? point[1] : NaN;\n          }\n\n          data.setLayout('largePoints', points);\n        }\n      }\n    };\n    /**\n     * Get the sign of a single data.\n     *\n     * @returns 0 for doji with hasDojiColor: true,\n     *          1 for positive,\n     *          -1 for negative.\n     */\n\n    function getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor) {\n      var sign;\n\n      if (openVal > closeVal) {\n        sign = -1;\n      } else if (openVal < closeVal) {\n        sign = 1;\n      } else {\n        sign = hasDojiColor // When doji color is set, use it instead of color/color0.\n        ? 0 : dataIndex > 0 // If close === open, compare with close of last record\n        ? store.get(closeDimI, dataIndex - 1) <= closeVal ? 1 : -1 : // No record of previous, set to be positive\n        1;\n      }\n\n      return sign;\n    }\n\n    function calculateCandleWidth(seriesModel, data) {\n      var baseAxis = seriesModel.getBaseAxis();\n      var extent;\n      var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : (extent = baseAxis.getExtent(), Math.abs(extent[1] - extent[0]) / data.count());\n      var barMaxWidth = parsePercent$1(retrieve2(seriesModel.get('barMaxWidth'), bandWidth), bandWidth);\n      var barMinWidth = parsePercent$1(retrieve2(seriesModel.get('barMinWidth'), 1), bandWidth);\n      var barWidth = seriesModel.get('barWidth');\n      return barWidth != null ? parsePercent$1(barWidth, bandWidth) // Put max outer to ensure bar visible in spite of overlap.\n      : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth);\n    }\n\n    function install$k(registers) {\n      registers.registerChartView(CandlestickView);\n      registers.registerSeriesModel(CandlestickSeriesModel);\n      registers.registerPreprocessor(candlestickPreprocessor);\n      registers.registerVisual(candlestickVisual);\n      registers.registerLayout(candlestickLayout);\n    }\n\n    function updateRipplePath(rippleGroup, effectCfg) {\n      var color = effectCfg.rippleEffectColor || effectCfg.color;\n      rippleGroup.eachChild(function (ripplePath) {\n        ripplePath.attr({\n          z: effectCfg.z,\n          zlevel: effectCfg.zlevel,\n          style: {\n            stroke: effectCfg.brushType === 'stroke' ? color : null,\n            fill: effectCfg.brushType === 'fill' ? color : null\n          }\n        });\n      });\n    }\n\n    var EffectSymbol =\n    /** @class */\n    function (_super) {\n      __extends(EffectSymbol, _super);\n\n      function EffectSymbol(data, idx) {\n        var _this = _super.call(this) || this;\n\n        var symbol = new Symbol(data, idx);\n        var rippleGroup = new Group();\n\n        _this.add(symbol);\n\n        _this.add(rippleGroup);\n\n        _this.updateData(data, idx);\n\n        return _this;\n      }\n\n      EffectSymbol.prototype.stopEffectAnimation = function () {\n        this.childAt(1).removeAll();\n      };\n\n      EffectSymbol.prototype.startEffectAnimation = function (effectCfg) {\n        var symbolType = effectCfg.symbolType;\n        var color = effectCfg.color;\n        var rippleNumber = effectCfg.rippleNumber;\n        var rippleGroup = this.childAt(1);\n\n        for (var i = 0; i < rippleNumber; i++) {\n          // If width/height are set too small (e.g., set to 1) on ios10\n          // and macOS Sierra, a circle stroke become a rect, no matter what\n          // the scale is set. So we set width/height as 2. See #4136.\n          var ripplePath = createSymbol(symbolType, -1, -1, 2, 2, color);\n          ripplePath.attr({\n            style: {\n              strokeNoScale: true\n            },\n            z2: 99,\n            silent: true,\n            scaleX: 0.5,\n            scaleY: 0.5\n          });\n          var delay = -i / rippleNumber * effectCfg.period + effectCfg.effectOffset;\n          ripplePath.animate('', true).when(effectCfg.period, {\n            scaleX: effectCfg.rippleScale / 2,\n            scaleY: effectCfg.rippleScale / 2\n          }).delay(delay).start();\n          ripplePath.animateStyle(true).when(effectCfg.period, {\n            opacity: 0\n          }).delay(delay).start();\n          rippleGroup.add(ripplePath);\n        }\n\n        updateRipplePath(rippleGroup, effectCfg);\n      };\n      /**\n       * Update effect symbol\n       */\n\n\n      EffectSymbol.prototype.updateEffectAnimation = function (effectCfg) {\n        var oldEffectCfg = this._effectCfg;\n        var rippleGroup = this.childAt(1); // Must reinitialize effect if following configuration changed\n\n        var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale', 'rippleNumber'];\n\n        for (var i = 0; i < DIFFICULT_PROPS.length; i++) {\n          var propName = DIFFICULT_PROPS[i];\n\n          if (oldEffectCfg[propName] !== effectCfg[propName]) {\n            this.stopEffectAnimation();\n            this.startEffectAnimation(effectCfg);\n            return;\n          }\n        }\n\n        updateRipplePath(rippleGroup, effectCfg);\n      };\n      /**\n       * Highlight symbol\n       */\n\n\n      EffectSymbol.prototype.highlight = function () {\n        enterEmphasis(this);\n      };\n      /**\n       * Downplay symbol\n       */\n\n\n      EffectSymbol.prototype.downplay = function () {\n        leaveEmphasis(this);\n      };\n\n      EffectSymbol.prototype.getSymbolType = function () {\n        var symbol = this.childAt(0);\n        return symbol && symbol.getSymbolType();\n      };\n      /**\n       * Update symbol properties\n       */\n\n\n      EffectSymbol.prototype.updateData = function (data, idx) {\n        var _this = this;\n\n        var seriesModel = data.hostModel;\n        this.childAt(0).updateData(data, idx);\n        var rippleGroup = this.childAt(1);\n        var itemModel = data.getItemModel(idx);\n        var symbolType = data.getItemVisual(idx, 'symbol');\n        var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n        var symbolStyle = data.getItemVisual(idx, 'style');\n        var color = symbolStyle && symbolStyle.fill;\n        var emphasisModel = itemModel.getModel('emphasis');\n        rippleGroup.setScale(symbolSize);\n        rippleGroup.traverse(function (ripplePath) {\n          ripplePath.setStyle('fill', color);\n        });\n        var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize);\n\n        if (symbolOffset) {\n          rippleGroup.x = symbolOffset[0];\n          rippleGroup.y = symbolOffset[1];\n        }\n\n        var symbolRotate = data.getItemVisual(idx, 'symbolRotate');\n        rippleGroup.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;\n        var effectCfg = {};\n        effectCfg.showEffectOn = seriesModel.get('showEffectOn');\n        effectCfg.rippleScale = itemModel.get(['rippleEffect', 'scale']);\n        effectCfg.brushType = itemModel.get(['rippleEffect', 'brushType']);\n        effectCfg.period = itemModel.get(['rippleEffect', 'period']) * 1000;\n        effectCfg.effectOffset = idx / data.count();\n        effectCfg.z = seriesModel.getShallow('z') || 0;\n        effectCfg.zlevel = seriesModel.getShallow('zlevel') || 0;\n        effectCfg.symbolType = symbolType;\n        effectCfg.color = color;\n        effectCfg.rippleEffectColor = itemModel.get(['rippleEffect', 'color']);\n        effectCfg.rippleNumber = itemModel.get(['rippleEffect', 'number']);\n\n        if (effectCfg.showEffectOn === 'render') {\n          this._effectCfg ? this.updateEffectAnimation(effectCfg) : this.startEffectAnimation(effectCfg);\n          this._effectCfg = effectCfg;\n        } else {\n          // Not keep old effect config\n          this._effectCfg = null;\n          this.stopEffectAnimation();\n\n          this.onHoverStateChange = function (toState) {\n            if (toState === 'emphasis') {\n              if (effectCfg.showEffectOn !== 'render') {\n                _this.startEffectAnimation(effectCfg);\n              }\n            } else if (toState === 'normal') {\n              if (effectCfg.showEffectOn !== 'render') {\n                _this.stopEffectAnimation();\n              }\n            }\n          };\n        }\n\n        this._effectCfg = effectCfg;\n        toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      };\n\n      EffectSymbol.prototype.fadeOut = function (cb) {\n        cb && cb();\n      };\n      return EffectSymbol;\n    }(Group);\n\n    var EffectScatterView =\n    /** @class */\n    function (_super) {\n      __extends(EffectScatterView, _super);\n\n      function EffectScatterView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = EffectScatterView.type;\n        return _this;\n      }\n\n      EffectScatterView.prototype.init = function () {\n        this._symbolDraw = new SymbolDraw(EffectSymbol);\n      };\n\n      EffectScatterView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n        var effectSymbolDraw = this._symbolDraw;\n        effectSymbolDraw.updateData(data, {\n          clipShape: this._getClipShape(seriesModel)\n        });\n        this.group.add(effectSymbolDraw.group);\n      };\n\n      EffectScatterView.prototype._getClipShape = function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n        var clipArea = coordSys && coordSys.getArea && coordSys.getArea();\n        return seriesModel.get('clip', true) ? clipArea : null;\n      };\n\n      EffectScatterView.prototype.updateTransform = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n        this.group.dirty();\n        var res = pointsLayout('').reset(seriesModel, ecModel, api);\n\n        if (res.progress) {\n          res.progress({\n            start: 0,\n            end: data.count(),\n            count: data.count()\n          }, data);\n        }\n\n        this._symbolDraw.updateLayout();\n      };\n\n      EffectScatterView.prototype._updateGroupTransform = function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys && coordSys.getRoamTransform) {\n          this.group.transform = clone$2(coordSys.getRoamTransform());\n          this.group.decomposeTransform();\n        }\n      };\n\n      EffectScatterView.prototype.remove = function (ecModel, api) {\n        this._symbolDraw && this._symbolDraw.remove(true);\n      };\n\n      EffectScatterView.type = 'effectScatter';\n      return EffectScatterView;\n    }(ChartView);\n\n    var EffectScatterSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(EffectScatterSeriesModel, _super);\n\n      function EffectScatterSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = EffectScatterSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      }\n\n      EffectScatterSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      EffectScatterSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n        return selectors.point(data.getItemLayout(dataIndex));\n      };\n\n      EffectScatterSeriesModel.type = 'series.effectScatter';\n      EffectScatterSeriesModel.dependencies = ['grid', 'polar'];\n      EffectScatterSeriesModel.defaultOption = {\n        coordinateSystem: 'cartesian2d',\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        effectType: 'ripple',\n        progressive: 0,\n        // When to show the effect, option: 'render'|'emphasis'\n        showEffectOn: 'render',\n        clip: true,\n        // Ripple effect config\n        rippleEffect: {\n          period: 4,\n          // Scale of ripple\n          scale: 2.5,\n          // Brush type can be fill or stroke\n          brushType: 'fill',\n          // Ripple number\n          number: 3\n        },\n        universalTransition: {\n          divideShape: 'clone'\n        },\n        // Cartesian coordinate system\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        // Polar coordinate system\n        // polarIndex: 0,\n        // Geo coordinate system\n        // geoIndex: 0,\n        // symbol: null,        // 图形类型\n        symbolSize: 10 // 图形大小，半宽（半径）参数，当图形为方向或菱形则总宽度为symbolSize * 2\n        // symbolRotate: null,  // 图形旋转控制\n        // itemStyle: {\n        //     opacity: 1\n        // }\n\n      };\n      return EffectScatterSeriesModel;\n    }(SeriesModel);\n\n    function install$l(registers) {\n      registers.registerChartView(EffectScatterView);\n      registers.registerSeriesModel(EffectScatterSeriesModel);\n      registers.registerLayout(pointsLayout('effectScatter'));\n    }\n\n    var EffectLine =\n    /** @class */\n    function (_super) {\n      __extends(EffectLine, _super);\n\n      function EffectLine(lineData, idx, seriesScope) {\n        var _this = _super.call(this) || this;\n\n        _this.add(_this.createLine(lineData, idx, seriesScope));\n\n        _this._updateEffectSymbol(lineData, idx);\n\n        return _this;\n      }\n\n      EffectLine.prototype.createLine = function (lineData, idx, seriesScope) {\n        return new Line$1(lineData, idx, seriesScope);\n      };\n\n      EffectLine.prototype._updateEffectSymbol = function (lineData, idx) {\n        var itemModel = lineData.getItemModel(idx);\n        var effectModel = itemModel.getModel('effect');\n        var size = effectModel.get('symbolSize');\n        var symbolType = effectModel.get('symbol');\n\n        if (!isArray(size)) {\n          size = [size, size];\n        }\n\n        var lineStyle = lineData.getItemVisual(idx, 'style');\n        var color = effectModel.get('color') || lineStyle && lineStyle.stroke;\n        var symbol = this.childAt(1);\n\n        if (this._symbolType !== symbolType) {\n          // Remove previous\n          this.remove(symbol);\n          symbol = createSymbol(symbolType, -0.5, -0.5, 1, 1, color);\n          symbol.z2 = 100;\n          symbol.culling = true;\n          this.add(symbol);\n        } // Symbol may be removed if loop is false\n\n\n        if (!symbol) {\n          return;\n        } // Shadow color is same with color in default\n\n\n        symbol.setStyle('shadowColor', color);\n        symbol.setStyle(effectModel.getItemStyle(['color']));\n        symbol.scaleX = size[0];\n        symbol.scaleY = size[1];\n        symbol.setColor(color);\n        this._symbolType = symbolType;\n        this._symbolScale = size;\n\n        this._updateEffectAnimation(lineData, effectModel, idx);\n      };\n\n      EffectLine.prototype._updateEffectAnimation = function (lineData, effectModel, idx) {\n        var symbol = this.childAt(1);\n\n        if (!symbol) {\n          return;\n        }\n\n        var points = lineData.getItemLayout(idx);\n        var period = effectModel.get('period') * 1000;\n        var loop = effectModel.get('loop');\n        var roundTrip = effectModel.get('roundTrip');\n        var constantSpeed = effectModel.get('constantSpeed');\n        var delayExpr = retrieve(effectModel.get('delay'), function (idx) {\n          return idx / lineData.count() * period / 3;\n        }); // Ignore when updating\n\n        symbol.ignore = true;\n\n        this._updateAnimationPoints(symbol, points);\n\n        if (constantSpeed > 0) {\n          period = this._getLineLength(symbol) / constantSpeed * 1000;\n        }\n\n        if (period !== this._period || loop !== this._loop || roundTrip !== this._roundTrip) {\n          symbol.stopAnimation();\n          var delayNum = void 0;\n\n          if (isFunction(delayExpr)) {\n            delayNum = delayExpr(idx);\n          } else {\n            delayNum = delayExpr;\n          }\n\n          if (symbol.__t > 0) {\n            delayNum = -period * symbol.__t;\n          }\n\n          this._animateSymbol(symbol, period, delayNum, loop, roundTrip);\n        }\n\n        this._period = period;\n        this._loop = loop;\n        this._roundTrip = roundTrip;\n      };\n\n      EffectLine.prototype._animateSymbol = function (symbol, period, delayNum, loop, roundTrip) {\n        if (period > 0) {\n          symbol.__t = 0;\n          var self_1 = this;\n          var animator = symbol.animate('', loop).when(roundTrip ? period * 2 : period, {\n            __t: roundTrip ? 2 : 1\n          }).delay(delayNum).during(function () {\n            self_1._updateSymbolPosition(symbol);\n          });\n\n          if (!loop) {\n            animator.done(function () {\n              self_1.remove(symbol);\n            });\n          }\n\n          animator.start();\n        }\n      };\n\n      EffectLine.prototype._getLineLength = function (symbol) {\n        // Not so accurate\n        return dist(symbol.__p1, symbol.__cp1) + dist(symbol.__cp1, symbol.__p2);\n      };\n\n      EffectLine.prototype._updateAnimationPoints = function (symbol, points) {\n        symbol.__p1 = points[0];\n        symbol.__p2 = points[1];\n        symbol.__cp1 = points[2] || [(points[0][0] + points[1][0]) / 2, (points[0][1] + points[1][1]) / 2];\n      };\n\n      EffectLine.prototype.updateData = function (lineData, idx, seriesScope) {\n        this.childAt(0).updateData(lineData, idx, seriesScope);\n\n        this._updateEffectSymbol(lineData, idx);\n      };\n\n      EffectLine.prototype._updateSymbolPosition = function (symbol) {\n        var p1 = symbol.__p1;\n        var p2 = symbol.__p2;\n        var cp1 = symbol.__cp1;\n        var t = symbol.__t < 1 ? symbol.__t : 2 - symbol.__t;\n        var pos = [symbol.x, symbol.y];\n        var lastPos = pos.slice();\n        var quadraticAt$1 = quadraticAt;\n        var quadraticDerivativeAt$1 = quadraticDerivativeAt;\n        pos[0] = quadraticAt$1(p1[0], cp1[0], p2[0], t);\n        pos[1] = quadraticAt$1(p1[1], cp1[1], p2[1], t); // Tangent\n\n        var tx = symbol.__t < 1 ? quadraticDerivativeAt$1(p1[0], cp1[0], p2[0], t) : quadraticDerivativeAt$1(p2[0], cp1[0], p1[0], 1 - t);\n        var ty = symbol.__t < 1 ? quadraticDerivativeAt$1(p1[1], cp1[1], p2[1], t) : quadraticDerivativeAt$1(p2[1], cp1[1], p1[1], 1 - t);\n        symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; // enable continuity trail for 'line', 'rect', 'roundRect' symbolType\n\n        if (this._symbolType === 'line' || this._symbolType === 'rect' || this._symbolType === 'roundRect') {\n          if (symbol.__lastT !== undefined && symbol.__lastT < symbol.__t) {\n            symbol.scaleY = dist(lastPos, pos) * 1.05; // make sure the last segment render within endPoint\n\n            if (t === 1) {\n              pos[0] = lastPos[0] + (pos[0] - lastPos[0]) / 2;\n              pos[1] = lastPos[1] + (pos[1] - lastPos[1]) / 2;\n            }\n          } else if (symbol.__lastT === 1) {\n            // After first loop, symbol.__t does NOT start with 0, so connect p1 to pos directly.\n            symbol.scaleY = 2 * dist(p1, pos);\n          } else {\n            symbol.scaleY = this._symbolScale[1];\n          }\n        }\n\n        symbol.__lastT = symbol.__t;\n        symbol.ignore = false;\n        symbol.x = pos[0];\n        symbol.y = pos[1];\n      };\n\n      EffectLine.prototype.updateLayout = function (lineData, idx) {\n        this.childAt(0).updateLayout(lineData, idx);\n        var effectModel = lineData.getItemModel(idx).getModel('effect');\n\n        this._updateEffectAnimation(lineData, effectModel, idx);\n      };\n\n      return EffectLine;\n    }(Group);\n\n    var Polyline$1 =\n    /** @class */\n    function (_super) {\n      __extends(Polyline$1, _super);\n\n      function Polyline$1(lineData, idx, seriesScope) {\n        var _this = _super.call(this) || this;\n\n        _this._createPolyline(lineData, idx, seriesScope);\n\n        return _this;\n      }\n\n      Polyline$1.prototype._createPolyline = function (lineData, idx, seriesScope) {\n        // let seriesModel = lineData.hostModel;\n        var points = lineData.getItemLayout(idx);\n        var line = new Polyline({\n          shape: {\n            points: points\n          }\n        });\n        this.add(line);\n\n        this._updateCommonStl(lineData, idx, seriesScope);\n      };\n\n      Polyline$1.prototype.updateData = function (lineData, idx, seriesScope) {\n        var seriesModel = lineData.hostModel;\n        var line = this.childAt(0);\n        var target = {\n          shape: {\n            points: lineData.getItemLayout(idx)\n          }\n        };\n        updateProps(line, target, seriesModel, idx);\n\n        this._updateCommonStl(lineData, idx, seriesScope);\n      };\n\n      Polyline$1.prototype._updateCommonStl = function (lineData, idx, seriesScope) {\n        var line = this.childAt(0);\n        var itemModel = lineData.getItemModel(idx);\n        var emphasisLineStyle = seriesScope && seriesScope.emphasisLineStyle;\n        var focus = seriesScope && seriesScope.focus;\n        var blurScope = seriesScope && seriesScope.blurScope;\n        var emphasisDisabled = seriesScope && seriesScope.emphasisDisabled;\n\n        if (!seriesScope || lineData.hasItemOption) {\n          var emphasisModel = itemModel.getModel('emphasis');\n          emphasisLineStyle = emphasisModel.getModel('lineStyle').getLineStyle();\n          emphasisDisabled = emphasisModel.get('disabled');\n          focus = emphasisModel.get('focus');\n          blurScope = emphasisModel.get('blurScope');\n        }\n\n        line.useStyle(lineData.getItemVisual(idx, 'style'));\n        line.style.fill = null;\n        line.style.strokeNoScale = true;\n        var lineEmphasisState = line.ensureState('emphasis');\n        lineEmphasisState.style = emphasisLineStyle;\n        toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n      };\n\n      Polyline$1.prototype.updateLayout = function (lineData, idx) {\n        var polyline = this.childAt(0);\n        polyline.setShape('points', lineData.getItemLayout(idx));\n      };\n      return Polyline$1;\n    }(Group);\n\n    var EffectPolyline =\n    /** @class */\n    function (_super) {\n      __extends(EffectPolyline, _super);\n\n      function EffectPolyline() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this._lastFrame = 0;\n        _this._lastFramePercent = 0;\n        return _this;\n      } // Override\n\n\n      EffectPolyline.prototype.createLine = function (lineData, idx, seriesScope) {\n        return new Polyline$1(lineData, idx, seriesScope);\n      };\n\n      EffectPolyline.prototype._updateAnimationPoints = function (symbol, points) {\n        this._points = points;\n        var accLenArr = [0];\n        var len = 0;\n\n        for (var i = 1; i < points.length; i++) {\n          var p1 = points[i - 1];\n          var p2 = points[i];\n          len += dist(p1, p2);\n          accLenArr.push(len);\n        }\n\n        if (len === 0) {\n          this._length = 0;\n          return;\n        }\n\n        for (var i = 0; i < accLenArr.length; i++) {\n          accLenArr[i] /= len;\n        }\n\n        this._offsets = accLenArr;\n        this._length = len;\n      };\n\n      EffectPolyline.prototype._getLineLength = function () {\n        return this._length;\n      };\n\n      EffectPolyline.prototype._updateSymbolPosition = function (symbol) {\n        var t = symbol.__t < 1 ? symbol.__t : 2 - symbol.__t;\n        var points = this._points;\n        var offsets = this._offsets;\n        var len = points.length;\n\n        if (!offsets) {\n          // Has length 0\n          return;\n        }\n\n        var lastFrame = this._lastFrame;\n        var frame;\n\n        if (t < this._lastFramePercent) {\n          // Start from the next frame\n          // PENDING start from lastFrame ?\n          var start = Math.min(lastFrame + 1, len - 1);\n\n          for (frame = start; frame >= 0; frame--) {\n            if (offsets[frame] <= t) {\n              break;\n            }\n          } // PENDING really need to do this ?\n\n\n          frame = Math.min(frame, len - 2);\n        } else {\n          for (frame = lastFrame; frame < len; frame++) {\n            if (offsets[frame] > t) {\n              break;\n            }\n          }\n\n          frame = Math.min(frame - 1, len - 2);\n        }\n\n        var p = (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]);\n        var p0 = points[frame];\n        var p1 = points[frame + 1];\n        symbol.x = p0[0] * (1 - p) + p * p1[0];\n        symbol.y = p0[1] * (1 - p) + p * p1[1];\n        var tx = symbol.__t < 1 ? p1[0] - p0[0] : p0[0] - p1[0];\n        var ty = symbol.__t < 1 ? p1[1] - p0[1] : p0[1] - p1[1];\n        symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2;\n        this._lastFrame = frame;\n        this._lastFramePercent = t;\n        symbol.ignore = false;\n      };\n      return EffectPolyline;\n    }(EffectLine);\n\n    var LargeLinesPathShape =\n    /** @class */\n    function () {\n      function LargeLinesPathShape() {\n        this.polyline = false;\n        this.curveness = 0;\n        this.segs = [];\n      }\n\n      return LargeLinesPathShape;\n    }();\n\n    var LargeLinesPath =\n    /** @class */\n    function (_super) {\n      __extends(LargeLinesPath, _super);\n\n      function LargeLinesPath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this._off = 0;\n        _this.hoverDataIdx = -1;\n        return _this;\n      }\n\n      LargeLinesPath.prototype.reset = function () {\n        this.notClear = false;\n        this._off = 0;\n      };\n\n      LargeLinesPath.prototype.getDefaultStyle = function () {\n        return {\n          stroke: '#000',\n          fill: null\n        };\n      };\n\n      LargeLinesPath.prototype.getDefaultShape = function () {\n        return new LargeLinesPathShape();\n      };\n\n      LargeLinesPath.prototype.buildPath = function (ctx, shape) {\n        var segs = shape.segs;\n        var curveness = shape.curveness;\n        var i;\n\n        if (shape.polyline) {\n          for (i = this._off; i < segs.length;) {\n            var count = segs[i++];\n\n            if (count > 0) {\n              ctx.moveTo(segs[i++], segs[i++]);\n\n              for (var k = 1; k < count; k++) {\n                ctx.lineTo(segs[i++], segs[i++]);\n              }\n            }\n          }\n        } else {\n          for (i = this._off; i < segs.length;) {\n            var x0 = segs[i++];\n            var y0 = segs[i++];\n            var x1 = segs[i++];\n            var y1 = segs[i++];\n            ctx.moveTo(x0, y0);\n\n            if (curveness > 0) {\n              var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;\n              var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;\n              ctx.quadraticCurveTo(x2, y2, x1, y1);\n            } else {\n              ctx.lineTo(x1, y1);\n            }\n          }\n        }\n\n        if (this.incremental) {\n          this._off = i;\n          this.notClear = true;\n        }\n      };\n\n      LargeLinesPath.prototype.findDataIndex = function (x, y) {\n        var shape = this.shape;\n        var segs = shape.segs;\n        var curveness = shape.curveness;\n        var lineWidth = this.style.lineWidth;\n\n        if (shape.polyline) {\n          var dataIndex = 0;\n\n          for (var i = 0; i < segs.length;) {\n            var count = segs[i++];\n\n            if (count > 0) {\n              var x0 = segs[i++];\n              var y0 = segs[i++];\n\n              for (var k = 1; k < count; k++) {\n                var x1 = segs[i++];\n                var y1 = segs[i++];\n\n                if (containStroke(x0, y0, x1, y1, lineWidth, x, y)) {\n                  return dataIndex;\n                }\n              }\n            }\n\n            dataIndex++;\n          }\n        } else {\n          var dataIndex = 0;\n\n          for (var i = 0; i < segs.length;) {\n            var x0 = segs[i++];\n            var y0 = segs[i++];\n            var x1 = segs[i++];\n            var y1 = segs[i++];\n\n            if (curveness > 0) {\n              var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;\n              var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;\n\n              if (containStroke$2(x0, y0, x2, y2, x1, y1, lineWidth, x, y)) {\n                return dataIndex;\n              }\n            } else {\n              if (containStroke(x0, y0, x1, y1, lineWidth, x, y)) {\n                return dataIndex;\n              }\n            }\n\n            dataIndex++;\n          }\n        }\n\n        return -1;\n      };\n\n      LargeLinesPath.prototype.contain = function (x, y) {\n        var localPos = this.transformCoordToLocal(x, y);\n        var rect = this.getBoundingRect();\n        x = localPos[0];\n        y = localPos[1];\n\n        if (rect.contain(x, y)) {\n          // Cache found data index.\n          var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y);\n          return dataIdx >= 0;\n        }\n\n        this.hoverDataIdx = -1;\n        return false;\n      };\n\n      LargeLinesPath.prototype.getBoundingRect = function () {\n        // Ignore stroke for large symbol draw.\n        var rect = this._rect;\n\n        if (!rect) {\n          var shape = this.shape;\n          var points = shape.segs;\n          var minX = Infinity;\n          var minY = Infinity;\n          var maxX = -Infinity;\n          var maxY = -Infinity;\n\n          for (var i = 0; i < points.length;) {\n            var x = points[i++];\n            var y = points[i++];\n            minX = Math.min(x, minX);\n            maxX = Math.max(x, maxX);\n            minY = Math.min(y, minY);\n            maxY = Math.max(y, maxY);\n          }\n\n          rect = this._rect = new BoundingRect(minX, minY, maxX, maxY);\n        }\n\n        return rect;\n      };\n\n      return LargeLinesPath;\n    }(Path);\n\n    var LargeLineDraw =\n    /** @class */\n    function () {\n      function LargeLineDraw() {\n        this.group = new Group();\n      }\n      /**\n       * Update symbols draw by new data\n       */\n\n\n      LargeLineDraw.prototype.updateData = function (data) {\n        this._clear();\n\n        var lineEl = this._create();\n\n        lineEl.setShape({\n          segs: data.getLayout('linesPoints')\n        });\n\n        this._setCommon(lineEl, data);\n      };\n      /**\n       * @override\n       */\n\n      LargeLineDraw.prototype.incrementalPrepareUpdate = function (data) {\n        this.group.removeAll();\n\n        this._clear();\n      };\n      /**\n       * @override\n       */\n\n      LargeLineDraw.prototype.incrementalUpdate = function (taskParams, data) {\n        var lastAdded = this._newAdded[0];\n        var linePoints = data.getLayout('linesPoints');\n        var oldSegs = lastAdded && lastAdded.shape.segs; // Merging the exists. Each element has 1e4 points.\n        // Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization)\n\n        if (oldSegs && oldSegs.length < 2e4) {\n          var oldLen = oldSegs.length;\n          var newSegs = new Float32Array(oldLen + linePoints.length); // Concat two array\n\n          newSegs.set(oldSegs);\n          newSegs.set(linePoints, oldLen);\n          lastAdded.setShape({\n            segs: newSegs\n          });\n        } else {\n          // Clear\n          this._newAdded = [];\n\n          var lineEl = this._create();\n\n          lineEl.incremental = true;\n          lineEl.setShape({\n            segs: linePoints\n          });\n\n          this._setCommon(lineEl, data);\n\n          lineEl.__startIndex = taskParams.start;\n        }\n      };\n      /**\n       * @override\n       */\n\n\n      LargeLineDraw.prototype.remove = function () {\n        this._clear();\n      };\n\n      LargeLineDraw.prototype.eachRendered = function (cb) {\n        this._newAdded[0] && cb(this._newAdded[0]);\n      };\n\n      LargeLineDraw.prototype._create = function () {\n        var lineEl = new LargeLinesPath({\n          cursor: 'default',\n          ignoreCoarsePointer: true\n        });\n\n        this._newAdded.push(lineEl);\n\n        this.group.add(lineEl);\n        return lineEl;\n      };\n\n      LargeLineDraw.prototype._setCommon = function (lineEl, data, isIncremental) {\n        var hostModel = data.hostModel;\n        lineEl.setShape({\n          polyline: hostModel.get('polyline'),\n          curveness: hostModel.get(['lineStyle', 'curveness'])\n        });\n        lineEl.useStyle(hostModel.getModel('lineStyle').getLineStyle());\n        lineEl.style.strokeNoScale = true;\n        var style = data.getVisual('style');\n\n        if (style && style.stroke) {\n          lineEl.setStyle('stroke', style.stroke);\n        }\n\n        lineEl.setStyle('fill', null);\n        var ecData = getECData(lineEl); // Enable tooltip\n        // PENDING May have performance issue when path is extremely large\n\n        ecData.seriesIndex = hostModel.seriesIndex;\n        lineEl.on('mousemove', function (e) {\n          ecData.dataIndex = null;\n          var dataIndex = lineEl.hoverDataIdx;\n\n          if (dataIndex > 0) {\n            // Provide dataIndex for tooltip\n            ecData.dataIndex = dataIndex + lineEl.__startIndex;\n          }\n        });\n      };\n\n      LargeLineDraw.prototype._clear = function () {\n        this._newAdded = [];\n        this.group.removeAll();\n      };\n      return LargeLineDraw;\n    }();\n\n    var linesLayout = {\n      seriesType: 'lines',\n      plan: createRenderPlanner(),\n      reset: function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (!coordSys) {\n          if (\"development\" !== 'production') {\n            error('The lines series must have a coordinate system.');\n          }\n\n          return;\n        }\n\n        var isPolyline = seriesModel.get('polyline');\n        var isLarge = seriesModel.pipelineContext.large;\n        return {\n          progress: function (params, lineData) {\n            var lineCoords = [];\n\n            if (isLarge) {\n              var points = void 0;\n              var segCount = params.end - params.start;\n\n              if (isPolyline) {\n                var totalCoordsCount = 0;\n\n                for (var i = params.start; i < params.end; i++) {\n                  totalCoordsCount += seriesModel.getLineCoordsCount(i);\n                }\n\n                points = new Float32Array(segCount + totalCoordsCount * 2);\n              } else {\n                points = new Float32Array(segCount * 4);\n              }\n\n              var offset = 0;\n              var pt = [];\n\n              for (var i = params.start; i < params.end; i++) {\n                var len = seriesModel.getLineCoords(i, lineCoords);\n\n                if (isPolyline) {\n                  points[offset++] = len;\n                }\n\n                for (var k = 0; k < len; k++) {\n                  pt = coordSys.dataToPoint(lineCoords[k], false, pt);\n                  points[offset++] = pt[0];\n                  points[offset++] = pt[1];\n                }\n              }\n\n              lineData.setLayout('linesPoints', points);\n            } else {\n              for (var i = params.start; i < params.end; i++) {\n                var itemModel = lineData.getItemModel(i);\n                var len = seriesModel.getLineCoords(i, lineCoords);\n                var pts = [];\n\n                if (isPolyline) {\n                  for (var j = 0; j < len; j++) {\n                    pts.push(coordSys.dataToPoint(lineCoords[j]));\n                  }\n                } else {\n                  pts[0] = coordSys.dataToPoint(lineCoords[0]);\n                  pts[1] = coordSys.dataToPoint(lineCoords[1]);\n                  var curveness = itemModel.get(['lineStyle', 'curveness']);\n\n                  if (+curveness) {\n                    pts[2] = [(pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness, (pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness];\n                  }\n                }\n\n                lineData.setItemLayout(i, pts);\n              }\n            }\n          }\n        };\n      }\n    };\n\n    var LinesView =\n    /** @class */\n    function (_super) {\n      __extends(LinesView, _super);\n\n      function LinesView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LinesView.type;\n        return _this;\n      }\n\n      LinesView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n\n        var lineDraw = this._updateLineDraw(data, seriesModel);\n\n        var zlevel = seriesModel.get('zlevel');\n        var trailLength = seriesModel.get(['effect', 'trailLength']);\n        var zr = api.getZr(); // Avoid the drag cause ghost shadow\n        // FIXME Better way ?\n        // SVG doesn't support\n\n        var isSvg = zr.painter.getType() === 'svg';\n\n        if (!isSvg) {\n          zr.painter.getLayer(zlevel).clear(true);\n        } // Config layer with motion blur\n\n\n        if (this._lastZlevel != null && !isSvg) {\n          zr.configLayer(this._lastZlevel, {\n            motionBlur: false\n          });\n        }\n\n        if (this._showEffect(seriesModel) && trailLength > 0) {\n          if (!isSvg) {\n            zr.configLayer(zlevel, {\n              motionBlur: true,\n              lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0)\n            });\n          } else if (\"development\" !== 'production') {\n            console.warn('SVG render mode doesn\\'t support lines with trail effect');\n          }\n        }\n\n        lineDraw.updateData(data);\n        var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel);\n\n        if (clipPath) {\n          this.group.setClipPath(clipPath);\n        } else {\n          this.group.removeClipPath();\n        }\n\n        this._lastZlevel = zlevel;\n        this._finished = true;\n      };\n\n      LinesView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n\n        var lineDraw = this._updateLineDraw(data, seriesModel);\n\n        lineDraw.incrementalPrepareUpdate(data);\n\n        this._clearLayer(api);\n\n        this._finished = false;\n      };\n\n      LinesView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) {\n        this._lineDraw.incrementalUpdate(taskParams, seriesModel.getData());\n\n        this._finished = taskParams.end === seriesModel.getData().count();\n      };\n\n      LinesView.prototype.eachRendered = function (cb) {\n        this._lineDraw && this._lineDraw.eachRendered(cb);\n      };\n\n      LinesView.prototype.updateTransform = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n        var pipelineContext = seriesModel.pipelineContext;\n\n        if (!this._finished || pipelineContext.large || pipelineContext.progressiveRender) {\n          // TODO Don't have to do update in large mode. Only do it when there are millions of data.\n          return {\n            update: true\n          };\n        } else {\n          // TODO Use same logic with ScatterView.\n          // Manually update layout\n          var res = linesLayout.reset(seriesModel, ecModel, api);\n\n          if (res.progress) {\n            res.progress({\n              start: 0,\n              end: data.count(),\n              count: data.count()\n            }, data);\n          } // Not in large mode\n\n\n          this._lineDraw.updateLayout();\n\n          this._clearLayer(api);\n        }\n      };\n\n      LinesView.prototype._updateLineDraw = function (data, seriesModel) {\n        var lineDraw = this._lineDraw;\n\n        var hasEffect = this._showEffect(seriesModel);\n\n        var isPolyline = !!seriesModel.get('polyline');\n        var pipelineContext = seriesModel.pipelineContext;\n        var isLargeDraw = pipelineContext.large;\n\n        if (\"development\" !== 'production') {\n          if (hasEffect && isLargeDraw) {\n            console.warn('Large lines not support effect');\n          }\n        }\n\n        if (!lineDraw || hasEffect !== this._hasEffet || isPolyline !== this._isPolyline || isLargeDraw !== this._isLargeDraw) {\n          if (lineDraw) {\n            lineDraw.remove();\n          }\n\n          lineDraw = this._lineDraw = isLargeDraw ? new LargeLineDraw() : new LineDraw(isPolyline ? hasEffect ? EffectPolyline : Polyline$1 : hasEffect ? EffectLine : Line$1);\n          this._hasEffet = hasEffect;\n          this._isPolyline = isPolyline;\n          this._isLargeDraw = isLargeDraw;\n        }\n\n        this.group.add(lineDraw.group);\n        return lineDraw;\n      };\n\n      LinesView.prototype._showEffect = function (seriesModel) {\n        return !!seriesModel.get(['effect', 'show']);\n      };\n\n      LinesView.prototype._clearLayer = function (api) {\n        // Not use motion when dragging or zooming\n        var zr = api.getZr();\n        var isSvg = zr.painter.getType() === 'svg';\n\n        if (!isSvg && this._lastZlevel != null) {\n          zr.painter.getLayer(this._lastZlevel).clear(true);\n        }\n      };\n\n      LinesView.prototype.remove = function (ecModel, api) {\n        this._lineDraw && this._lineDraw.remove();\n        this._lineDraw = null; // Clear motion when lineDraw is removed\n\n        this._clearLayer(api);\n      };\n\n      LinesView.prototype.dispose = function (ecModel, api) {\n        this.remove(ecModel, api);\n      };\n\n      LinesView.type = 'lines';\n      return LinesView;\n    }(ChartView);\n\n    var Uint32Arr = typeof Uint32Array === 'undefined' ? Array : Uint32Array;\n    var Float64Arr = typeof Float64Array === 'undefined' ? Array : Float64Array;\n\n    function compatEc2(seriesOpt) {\n      var data = seriesOpt.data;\n\n      if (data && data[0] && data[0][0] && data[0][0].coord) {\n        if (\"development\" !== 'production') {\n          console.warn('Lines data configuration has been changed to' + ' { coords:[[1,2],[2,3]] }');\n        }\n\n        seriesOpt.data = map(data, function (itemOpt) {\n          var coords = [itemOpt[0].coord, itemOpt[1].coord];\n          var target = {\n            coords: coords\n          };\n\n          if (itemOpt[0].name) {\n            target.fromName = itemOpt[0].name;\n          }\n\n          if (itemOpt[1].name) {\n            target.toName = itemOpt[1].name;\n          }\n\n          return mergeAll([target, itemOpt[0], itemOpt[1]]);\n        });\n      }\n    }\n\n    var LinesSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(LinesSeriesModel, _super);\n\n      function LinesSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LinesSeriesModel.type;\n        _this.visualStyleAccessPath = 'lineStyle';\n        _this.visualDrawType = 'stroke';\n        return _this;\n      }\n\n      LinesSeriesModel.prototype.init = function (option) {\n        // The input data may be null/undefined.\n        option.data = option.data || []; // Not using preprocessor because mergeOption may not have series.type\n\n        compatEc2(option);\n\n        var result = this._processFlatCoordsArray(option.data);\n\n        this._flatCoords = result.flatCoords;\n        this._flatCoordsOffset = result.flatCoordsOffset;\n\n        if (result.flatCoords) {\n          option.data = new Float32Array(result.count);\n        }\n\n        _super.prototype.init.apply(this, arguments);\n      };\n\n      LinesSeriesModel.prototype.mergeOption = function (option) {\n        compatEc2(option);\n\n        if (option.data) {\n          // Only update when have option data to merge.\n          var result = this._processFlatCoordsArray(option.data);\n\n          this._flatCoords = result.flatCoords;\n          this._flatCoordsOffset = result.flatCoordsOffset;\n\n          if (result.flatCoords) {\n            option.data = new Float32Array(result.count);\n          }\n        }\n\n        _super.prototype.mergeOption.apply(this, arguments);\n      };\n\n      LinesSeriesModel.prototype.appendData = function (params) {\n        var result = this._processFlatCoordsArray(params.data);\n\n        if (result.flatCoords) {\n          if (!this._flatCoords) {\n            this._flatCoords = result.flatCoords;\n            this._flatCoordsOffset = result.flatCoordsOffset;\n          } else {\n            this._flatCoords = concatArray(this._flatCoords, result.flatCoords);\n            this._flatCoordsOffset = concatArray(this._flatCoordsOffset, result.flatCoordsOffset);\n          }\n\n          params.data = new Float32Array(result.count);\n        }\n\n        this.getRawData().appendData(params.data);\n      };\n\n      LinesSeriesModel.prototype._getCoordsFromItemModel = function (idx) {\n        var itemModel = this.getData().getItemModel(idx);\n        var coords = itemModel.option instanceof Array ? itemModel.option : itemModel.getShallow('coords');\n\n        if (\"development\" !== 'production') {\n          if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) {\n            throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.');\n          }\n        }\n\n        return coords;\n      };\n\n      LinesSeriesModel.prototype.getLineCoordsCount = function (idx) {\n        if (this._flatCoordsOffset) {\n          return this._flatCoordsOffset[idx * 2 + 1];\n        } else {\n          return this._getCoordsFromItemModel(idx).length;\n        }\n      };\n\n      LinesSeriesModel.prototype.getLineCoords = function (idx, out) {\n        if (this._flatCoordsOffset) {\n          var offset = this._flatCoordsOffset[idx * 2];\n          var len = this._flatCoordsOffset[idx * 2 + 1];\n\n          for (var i = 0; i < len; i++) {\n            out[i] = out[i] || [];\n            out[i][0] = this._flatCoords[offset + i * 2];\n            out[i][1] = this._flatCoords[offset + i * 2 + 1];\n          }\n\n          return len;\n        } else {\n          var coords = this._getCoordsFromItemModel(idx);\n\n          for (var i = 0; i < coords.length; i++) {\n            out[i] = out[i] || [];\n            out[i][0] = coords[i][0];\n            out[i][1] = coords[i][1];\n          }\n\n          return coords.length;\n        }\n      };\n\n      LinesSeriesModel.prototype._processFlatCoordsArray = function (data) {\n        var startOffset = 0;\n\n        if (this._flatCoords) {\n          startOffset = this._flatCoords.length;\n        } // Stored as a typed array. In format\n        // Points Count(2) | x | y | x | y | Points Count(3) | x |  y | x | y | x | y |\n\n\n        if (isNumber(data[0])) {\n          var len = data.length; // Store offset and len of each segment\n\n          var coordsOffsetAndLenStorage = new Uint32Arr(len);\n          var coordsStorage = new Float64Arr(len);\n          var coordsCursor = 0;\n          var offsetCursor = 0;\n          var dataCount = 0;\n\n          for (var i = 0; i < len;) {\n            dataCount++;\n            var count = data[i++]; // Offset\n\n            coordsOffsetAndLenStorage[offsetCursor++] = coordsCursor + startOffset; // Len\n\n            coordsOffsetAndLenStorage[offsetCursor++] = count;\n\n            for (var k = 0; k < count; k++) {\n              var x = data[i++];\n              var y = data[i++];\n              coordsStorage[coordsCursor++] = x;\n              coordsStorage[coordsCursor++] = y;\n\n              if (i > len) {\n                if (\"development\" !== 'production') {\n                  throw new Error('Invalid data format.');\n                }\n              }\n            }\n          }\n\n          return {\n            flatCoordsOffset: new Uint32Array(coordsOffsetAndLenStorage.buffer, 0, offsetCursor),\n            flatCoords: coordsStorage,\n            count: dataCount\n          };\n        }\n\n        return {\n          flatCoordsOffset: null,\n          flatCoords: null,\n          count: data.length\n        };\n      };\n\n      LinesSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        if (\"development\" !== 'production') {\n          var CoordSys = CoordinateSystemManager.get(option.coordinateSystem);\n\n          if (!CoordSys) {\n            throw new Error('Unknown coordinate system ' + option.coordinateSystem);\n          }\n        }\n\n        var lineData = new SeriesData(['value'], this);\n        lineData.hasItemOption = false;\n        lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) {\n          // dataItem is simply coords\n          if (dataItem instanceof Array) {\n            return NaN;\n          } else {\n            lineData.hasItemOption = true;\n            var value = dataItem.value;\n\n            if (value != null) {\n              return value instanceof Array ? value[dimIndex] : value;\n            }\n          }\n        });\n        return lineData;\n      };\n\n      LinesSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        var data = this.getData();\n        var itemModel = data.getItemModel(dataIndex);\n        var name = itemModel.get('name');\n\n        if (name) {\n          return name;\n        }\n\n        var fromName = itemModel.get('fromName');\n        var toName = itemModel.get('toName');\n        var nameArr = [];\n        fromName != null && nameArr.push(fromName);\n        toName != null && nameArr.push(toName);\n        return createTooltipMarkup('nameValue', {\n          name: nameArr.join(' > ')\n        });\n      };\n\n      LinesSeriesModel.prototype.preventIncremental = function () {\n        return !!this.get(['effect', 'show']);\n      };\n\n      LinesSeriesModel.prototype.getProgressive = function () {\n        var progressive = this.option.progressive;\n\n        if (progressive == null) {\n          return this.option.large ? 1e4 : this.get('progressive');\n        }\n\n        return progressive;\n      };\n\n      LinesSeriesModel.prototype.getProgressiveThreshold = function () {\n        var progressiveThreshold = this.option.progressiveThreshold;\n\n        if (progressiveThreshold == null) {\n          return this.option.large ? 2e4 : this.get('progressiveThreshold');\n        }\n\n        return progressiveThreshold;\n      };\n\n      LinesSeriesModel.prototype.getZLevelKey = function () {\n        var effectModel = this.getModel('effect');\n        var trailLength = effectModel.get('trailLength');\n        return this.getData().count() > this.getProgressiveThreshold() // Each progressive series has individual key.\n        ? this.id : effectModel.get('show') && trailLength > 0 ? trailLength + '' : '';\n      };\n\n      LinesSeriesModel.type = 'series.lines';\n      LinesSeriesModel.dependencies = ['grid', 'polar', 'geo', 'calendar'];\n      LinesSeriesModel.defaultOption = {\n        coordinateSystem: 'geo',\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        // Cartesian coordinate system\n        xAxisIndex: 0,\n        yAxisIndex: 0,\n        symbol: ['none', 'none'],\n        symbolSize: [10, 10],\n        // Geo coordinate system\n        geoIndex: 0,\n        effect: {\n          show: false,\n          period: 4,\n          constantSpeed: 0,\n          symbol: 'circle',\n          symbolSize: 3,\n          loop: true,\n          trailLength: 0.2\n        },\n        large: false,\n        // Available when large is true\n        largeThreshold: 2000,\n        polyline: false,\n        clip: true,\n        label: {\n          show: false,\n          position: 'end' // distance: 5,\n          // formatter: 标签文本格式器，同Tooltip.formatter，不支持异步回调\n\n        },\n        lineStyle: {\n          opacity: 0.5\n        }\n      };\n      return LinesSeriesModel;\n    }(SeriesModel);\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function normalize$3(a) {\n      if (!(a instanceof Array)) {\n        a = [a, a];\n      }\n\n      return a;\n    }\n\n    var linesVisual = {\n      seriesType: 'lines',\n      reset: function (seriesModel) {\n        var symbolType = normalize$3(seriesModel.get('symbol'));\n        var symbolSize = normalize$3(seriesModel.get('symbolSize'));\n        var data = seriesModel.getData();\n        data.setVisual('fromSymbol', symbolType && symbolType[0]);\n        data.setVisual('toSymbol', symbolType && symbolType[1]);\n        data.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);\n        data.setVisual('toSymbolSize', symbolSize && symbolSize[1]);\n\n        function dataEach(data, idx) {\n          var itemModel = data.getItemModel(idx);\n          var symbolType = normalize$3(itemModel.getShallow('symbol', true));\n          var symbolSize = normalize$3(itemModel.getShallow('symbolSize', true));\n          symbolType[0] && data.setItemVisual(idx, 'fromSymbol', symbolType[0]);\n          symbolType[1] && data.setItemVisual(idx, 'toSymbol', symbolType[1]);\n          symbolSize[0] && data.setItemVisual(idx, 'fromSymbolSize', symbolSize[0]);\n          symbolSize[1] && data.setItemVisual(idx, 'toSymbolSize', symbolSize[1]);\n        }\n\n        return {\n          dataEach: data.hasItemOption ? dataEach : null\n        };\n      }\n    };\n\n    function install$m(registers) {\n      registers.registerChartView(LinesView);\n      registers.registerSeriesModel(LinesSeriesModel);\n      registers.registerLayout(linesLayout);\n      registers.registerVisual(linesVisual);\n    }\n\n    var GRADIENT_LEVELS = 256;\n\n    var HeatmapLayer =\n    /** @class */\n    function () {\n      function HeatmapLayer() {\n        this.blurSize = 30;\n        this.pointSize = 20;\n        this.maxOpacity = 1;\n        this.minOpacity = 0;\n        this._gradientPixels = {\n          inRange: null,\n          outOfRange: null\n        };\n        var canvas = platformApi.createCanvas();\n        this.canvas = canvas;\n      }\n      /**\n       * Renders Heatmap and returns the rendered canvas\n       * @param data array of data, each has x, y, value\n       * @param width canvas width\n       * @param height canvas height\n       */\n\n\n      HeatmapLayer.prototype.update = function (data, width, height, normalize, colorFunc, isInRange) {\n        var brush = this._getBrush();\n\n        var gradientInRange = this._getGradient(colorFunc, 'inRange');\n\n        var gradientOutOfRange = this._getGradient(colorFunc, 'outOfRange');\n\n        var r = this.pointSize + this.blurSize;\n        var canvas = this.canvas;\n        var ctx = canvas.getContext('2d');\n        var len = data.length;\n        canvas.width = width;\n        canvas.height = height;\n\n        for (var i = 0; i < len; ++i) {\n          var p = data[i];\n          var x = p[0];\n          var y = p[1];\n          var value = p[2]; // calculate alpha using value\n\n          var alpha = normalize(value); // draw with the circle brush with alpha\n\n          ctx.globalAlpha = alpha;\n          ctx.drawImage(brush, x - r, y - r);\n        }\n\n        if (!canvas.width || !canvas.height) {\n          // Avoid \"Uncaught DOMException: Failed to execute 'getImageData' on\n          // 'CanvasRenderingContext2D': The source height is 0.\"\n          return canvas;\n        } // colorize the canvas using alpha value and set with gradient\n\n\n        var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n        var pixels = imageData.data;\n        var offset = 0;\n        var pixelLen = pixels.length;\n        var minOpacity = this.minOpacity;\n        var maxOpacity = this.maxOpacity;\n        var diffOpacity = maxOpacity - minOpacity;\n\n        while (offset < pixelLen) {\n          var alpha = pixels[offset + 3] / 256;\n          var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4; // Simple optimize to ignore the empty data\n\n          if (alpha > 0) {\n            var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange; // Any alpha > 0 will be mapped to [minOpacity, maxOpacity]\n\n            alpha > 0 && (alpha = alpha * diffOpacity + minOpacity);\n            pixels[offset++] = gradient[gradientOffset];\n            pixels[offset++] = gradient[gradientOffset + 1];\n            pixels[offset++] = gradient[gradientOffset + 2];\n            pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256;\n          } else {\n            offset += 4;\n          }\n        }\n\n        ctx.putImageData(imageData, 0, 0);\n        return canvas;\n      };\n      /**\n       * get canvas of a black circle brush used for canvas to draw later\n       */\n\n\n      HeatmapLayer.prototype._getBrush = function () {\n        var brushCanvas = this._brushCanvas || (this._brushCanvas = platformApi.createCanvas()); // set brush size\n\n        var r = this.pointSize + this.blurSize;\n        var d = r * 2;\n        brushCanvas.width = d;\n        brushCanvas.height = d;\n        var ctx = brushCanvas.getContext('2d');\n        ctx.clearRect(0, 0, d, d); // in order to render shadow without the distinct circle,\n        // draw the distinct circle in an invisible place,\n        // and use shadowOffset to draw shadow in the center of the canvas\n\n        ctx.shadowOffsetX = d;\n        ctx.shadowBlur = this.blurSize; // draw the shadow in black, and use alpha and shadow blur to generate\n        // color in color map\n\n        ctx.shadowColor = '#000'; // draw circle in the left to the canvas\n\n        ctx.beginPath();\n        ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true);\n        ctx.closePath();\n        ctx.fill();\n        return brushCanvas;\n      };\n      /**\n       * get gradient color map\n       * @private\n       */\n\n\n      HeatmapLayer.prototype._getGradient = function (colorFunc, state) {\n        var gradientPixels = this._gradientPixels;\n        var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4));\n        var color = [0, 0, 0, 0];\n        var off = 0;\n\n        for (var i = 0; i < 256; i++) {\n          colorFunc[state](i / 255, true, color);\n          pixelsSingleState[off++] = color[0];\n          pixelsSingleState[off++] = color[1];\n          pixelsSingleState[off++] = color[2];\n          pixelsSingleState[off++] = color[3];\n        }\n\n        return pixelsSingleState;\n      };\n\n      return HeatmapLayer;\n    }();\n\n    function getIsInPiecewiseRange(dataExtent, pieceList, selected) {\n      var dataSpan = dataExtent[1] - dataExtent[0];\n      pieceList = map(pieceList, function (piece) {\n        return {\n          interval: [(piece.interval[0] - dataExtent[0]) / dataSpan, (piece.interval[1] - dataExtent[0]) / dataSpan]\n        };\n      });\n      var len = pieceList.length;\n      var lastIndex = 0;\n      return function (val) {\n        var i; // Try to find in the location of the last found\n\n        for (i = lastIndex; i < len; i++) {\n          var interval = pieceList[i].interval;\n\n          if (interval[0] <= val && val <= interval[1]) {\n            lastIndex = i;\n            break;\n          }\n        }\n\n        if (i === len) {\n          // Not found, back interation\n          for (i = lastIndex - 1; i >= 0; i--) {\n            var interval = pieceList[i].interval;\n\n            if (interval[0] <= val && val <= interval[1]) {\n              lastIndex = i;\n              break;\n            }\n          }\n        }\n\n        return i >= 0 && i < len && selected[i];\n      };\n    }\n\n    function getIsInContinuousRange(dataExtent, range) {\n      var dataSpan = dataExtent[1] - dataExtent[0];\n      range = [(range[0] - dataExtent[0]) / dataSpan, (range[1] - dataExtent[0]) / dataSpan];\n      return function (val) {\n        return val >= range[0] && val <= range[1];\n      };\n    }\n\n    function isGeoCoordSys(coordSys) {\n      var dimensions = coordSys.dimensions; // Not use coordSys.type === 'geo' because coordSys maybe extended\n\n      return dimensions[0] === 'lng' && dimensions[1] === 'lat';\n    }\n\n    var HeatmapView =\n    /** @class */\n    function (_super) {\n      __extends(HeatmapView, _super);\n\n      function HeatmapView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = HeatmapView.type;\n        return _this;\n      }\n\n      HeatmapView.prototype.render = function (seriesModel, ecModel, api) {\n        var visualMapOfThisSeries;\n        ecModel.eachComponent('visualMap', function (visualMap) {\n          visualMap.eachTargetSeries(function (targetSeries) {\n            if (targetSeries === seriesModel) {\n              visualMapOfThisSeries = visualMap;\n            }\n          });\n        });\n\n        if (\"development\" !== 'production') {\n          if (!visualMapOfThisSeries) {\n            throw new Error('Heatmap must use with visualMap');\n          }\n        } // Clear previously rendered progressive elements.\n\n\n        this._progressiveEls = null;\n        this.group.removeAll();\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar') {\n          this._renderOnCartesianAndCalendar(seriesModel, api, 0, seriesModel.getData().count());\n        } else if (isGeoCoordSys(coordSys)) {\n          this._renderOnGeo(coordSys, seriesModel, visualMapOfThisSeries, api);\n        }\n      };\n\n      HeatmapView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {\n        this.group.removeAll();\n      };\n\n      HeatmapView.prototype.incrementalRender = function (params, seriesModel, ecModel, api) {\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys) {\n          // geo does not support incremental rendering?\n          if (isGeoCoordSys(coordSys)) {\n            this.render(seriesModel, ecModel, api);\n          } else {\n            this._progressiveEls = [];\n\n            this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true);\n          }\n        }\n      };\n\n      HeatmapView.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      HeatmapView.prototype._renderOnCartesianAndCalendar = function (seriesModel, api, start, end, incremental) {\n        var coordSys = seriesModel.coordinateSystem;\n        var isCartesian2d = isCoordinateSystemType(coordSys, 'cartesian2d');\n        var width;\n        var height;\n        var xAxisExtent;\n        var yAxisExtent;\n\n        if (isCartesian2d) {\n          var xAxis = coordSys.getAxis('x');\n          var yAxis = coordSys.getAxis('y');\n\n          if (\"development\" !== 'production') {\n            if (!(xAxis.type === 'category' && yAxis.type === 'category')) {\n              throw new Error('Heatmap on cartesian must have two category axes');\n            }\n\n            if (!(xAxis.onBand && yAxis.onBand)) {\n              throw new Error('Heatmap on cartesian must have two axes with boundaryGap true');\n            }\n          } // add 0.5px to avoid the gaps\n\n\n          width = xAxis.getBandWidth() + .5;\n          height = yAxis.getBandWidth() + .5;\n          xAxisExtent = xAxis.scale.getExtent();\n          yAxisExtent = yAxis.scale.getExtent();\n        }\n\n        var group = this.group;\n        var data = seriesModel.getData();\n        var emphasisStyle = seriesModel.getModel(['emphasis', 'itemStyle']).getItemStyle();\n        var blurStyle = seriesModel.getModel(['blur', 'itemStyle']).getItemStyle();\n        var selectStyle = seriesModel.getModel(['select', 'itemStyle']).getItemStyle();\n        var borderRadius = seriesModel.get(['itemStyle', 'borderRadius']);\n        var labelStatesModels = getLabelStatesModels(seriesModel);\n        var emphasisModel = seriesModel.getModel('emphasis');\n        var focus = emphasisModel.get('focus');\n        var blurScope = emphasisModel.get('blurScope');\n        var emphasisDisabled = emphasisModel.get('disabled');\n        var dataDims = isCartesian2d ? [data.mapDimension('x'), data.mapDimension('y'), data.mapDimension('value')] : [data.mapDimension('time'), data.mapDimension('value')];\n\n        for (var idx = start; idx < end; idx++) {\n          var rect = void 0;\n          var style = data.getItemVisual(idx, 'style');\n\n          if (isCartesian2d) {\n            var dataDimX = data.get(dataDims[0], idx);\n            var dataDimY = data.get(dataDims[1], idx); // Ignore empty data and out of extent data\n\n            if (isNaN(data.get(dataDims[2], idx)) || isNaN(dataDimX) || isNaN(dataDimY) || dataDimX < xAxisExtent[0] || dataDimX > xAxisExtent[1] || dataDimY < yAxisExtent[0] || dataDimY > yAxisExtent[1]) {\n              continue;\n            }\n\n            var point = coordSys.dataToPoint([dataDimX, dataDimY]);\n            rect = new Rect({\n              shape: {\n                x: point[0] - width / 2,\n                y: point[1] - height / 2,\n                width: width,\n                height: height\n              },\n              style: style\n            });\n          } else {\n            // Ignore empty data\n            if (isNaN(data.get(dataDims[1], idx))) {\n              continue;\n            }\n\n            rect = new Rect({\n              z2: 1,\n              shape: coordSys.dataToRect([data.get(dataDims[0], idx)]).contentShape,\n              style: style\n            });\n          } // Optimization for large dataset\n\n\n          if (data.hasItemOption) {\n            var itemModel = data.getItemModel(idx);\n            var emphasisModel_1 = itemModel.getModel('emphasis');\n            emphasisStyle = emphasisModel_1.getModel('itemStyle').getItemStyle();\n            blurStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n            selectStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle(); // Each item value struct in the data would be firstly\n            // {\n            //     itemStyle: { borderRadius: [30, 30] },\n            //     value: [2022, 02, 22]\n            // }\n\n            borderRadius = itemModel.get(['itemStyle', 'borderRadius']);\n            focus = emphasisModel_1.get('focus');\n            blurScope = emphasisModel_1.get('blurScope');\n            emphasisDisabled = emphasisModel_1.get('disabled');\n            labelStatesModels = getLabelStatesModels(itemModel);\n          }\n\n          rect.shape.r = borderRadius;\n          var rawValue = seriesModel.getRawValue(idx);\n          var defaultText = '-';\n\n          if (rawValue && rawValue[2] != null) {\n            defaultText = rawValue[2] + '';\n          }\n\n          setLabelStyle(rect, labelStatesModels, {\n            labelFetcher: seriesModel,\n            labelDataIndex: idx,\n            defaultOpacity: style.opacity,\n            defaultText: defaultText\n          });\n          rect.ensureState('emphasis').style = emphasisStyle;\n          rect.ensureState('blur').style = blurStyle;\n          rect.ensureState('select').style = selectStyle;\n          toggleHoverEmphasis(rect, focus, blurScope, emphasisDisabled);\n          rect.incremental = incremental; // PENDING\n\n          if (incremental) {\n            // Rect must use hover layer if it's incremental.\n            rect.states.emphasis.hoverLayer = true;\n          }\n\n          group.add(rect);\n          data.setItemGraphicEl(idx, rect);\n\n          if (this._progressiveEls) {\n            this._progressiveEls.push(rect);\n          }\n        }\n      };\n\n      HeatmapView.prototype._renderOnGeo = function (geo, seriesModel, visualMapModel, api) {\n        var inRangeVisuals = visualMapModel.targetVisuals.inRange;\n        var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange; // if (!visualMapping) {\n        //     throw new Error('Data range must have color visuals');\n        // }\n\n        var data = seriesModel.getData();\n        var hmLayer = this._hmLayer || this._hmLayer || new HeatmapLayer();\n        hmLayer.blurSize = seriesModel.get('blurSize');\n        hmLayer.pointSize = seriesModel.get('pointSize');\n        hmLayer.minOpacity = seriesModel.get('minOpacity');\n        hmLayer.maxOpacity = seriesModel.get('maxOpacity');\n        var rect = geo.getViewRect().clone();\n        var roamTransform = geo.getRoamTransform();\n        rect.applyTransform(roamTransform); // Clamp on viewport\n\n        var x = Math.max(rect.x, 0);\n        var y = Math.max(rect.y, 0);\n        var x2 = Math.min(rect.width + rect.x, api.getWidth());\n        var y2 = Math.min(rect.height + rect.y, api.getHeight());\n        var width = x2 - x;\n        var height = y2 - y;\n        var dims = [data.mapDimension('lng'), data.mapDimension('lat'), data.mapDimension('value')];\n        var points = data.mapArray(dims, function (lng, lat, value) {\n          var pt = geo.dataToPoint([lng, lat]);\n          pt[0] -= x;\n          pt[1] -= y;\n          pt.push(value);\n          return pt;\n        });\n        var dataExtent = visualMapModel.getExtent();\n        var isInRange = visualMapModel.type === 'visualMap.continuous' ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) : getIsInPiecewiseRange(dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected);\n        hmLayer.update(points, width, height, inRangeVisuals.color.getNormalizer(), {\n          inRange: inRangeVisuals.color.getColorMapper(),\n          outOfRange: outOfRangeVisuals.color.getColorMapper()\n        }, isInRange);\n        var img = new ZRImage({\n          style: {\n            width: width,\n            height: height,\n            x: x,\n            y: y,\n            image: hmLayer.canvas\n          },\n          silent: true\n        });\n        this.group.add(img);\n      };\n\n      HeatmapView.type = 'heatmap';\n      return HeatmapView;\n    }(ChartView);\n\n    var HeatmapSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(HeatmapSeriesModel, _super);\n\n      function HeatmapSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = HeatmapSeriesModel.type;\n        return _this;\n      }\n\n      HeatmapSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          generateCoord: 'value'\n        });\n      };\n\n      HeatmapSeriesModel.prototype.preventIncremental = function () {\n        var coordSysCreator = CoordinateSystemManager.get(this.get('coordinateSystem'));\n\n        if (coordSysCreator && coordSysCreator.dimensions) {\n          return coordSysCreator.dimensions[0] === 'lng' && coordSysCreator.dimensions[1] === 'lat';\n        }\n      };\n\n      HeatmapSeriesModel.type = 'series.heatmap';\n      HeatmapSeriesModel.dependencies = ['grid', 'geo', 'calendar'];\n      HeatmapSeriesModel.defaultOption = {\n        coordinateSystem: 'cartesian2d',\n        // zlevel: 0,\n        z: 2,\n        // Cartesian coordinate system\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        // Geo coordinate system\n        geoIndex: 0,\n        blurSize: 30,\n        pointSize: 20,\n        maxOpacity: 1,\n        minOpacity: 0,\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        }\n      };\n      return HeatmapSeriesModel;\n    }(SeriesModel);\n\n    function install$n(registers) {\n      registers.registerChartView(HeatmapView);\n      registers.registerSeriesModel(HeatmapSeriesModel);\n    }\n\n    var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'borderWidth']; // index: +isHorizontal\n\n    var LAYOUT_ATTRS = [{\n      xy: 'x',\n      wh: 'width',\n      index: 0,\n      posDesc: ['left', 'right']\n    }, {\n      xy: 'y',\n      wh: 'height',\n      index: 1,\n      posDesc: ['top', 'bottom']\n    }];\n    var pathForLineWidth = new Circle();\n\n    var PictorialBarView =\n    /** @class */\n    function (_super) {\n      __extends(PictorialBarView, _super);\n\n      function PictorialBarView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = PictorialBarView.type;\n        return _this;\n      }\n\n      PictorialBarView.prototype.render = function (seriesModel, ecModel, api) {\n        var group = this.group;\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n        var isHorizontal = baseAxis.isHorizontal();\n        var coordSysRect = cartesian.master.getRect();\n        var opt = {\n          ecSize: {\n            width: api.getWidth(),\n            height: api.getHeight()\n          },\n          seriesModel: seriesModel,\n          coordSys: cartesian,\n          coordSysExtent: [[coordSysRect.x, coordSysRect.x + coordSysRect.width], [coordSysRect.y, coordSysRect.y + coordSysRect.height]],\n          isHorizontal: isHorizontal,\n          valueDim: LAYOUT_ATTRS[+isHorizontal],\n          categoryDim: LAYOUT_ATTRS[1 - +isHorizontal]\n        };\n        data.diff(oldData).add(function (dataIndex) {\n          if (!data.hasValue(dataIndex)) {\n            return;\n          }\n\n          var itemModel = getItemModel(data, dataIndex);\n          var symbolMeta = getSymbolMeta(data, dataIndex, itemModel, opt);\n          var bar = createBar(data, opt, symbolMeta);\n          data.setItemGraphicEl(dataIndex, bar);\n          group.add(bar);\n          updateCommon$1(bar, opt, symbolMeta);\n        }).update(function (newIndex, oldIndex) {\n          var bar = oldData.getItemGraphicEl(oldIndex);\n\n          if (!data.hasValue(newIndex)) {\n            group.remove(bar);\n            return;\n          }\n\n          var itemModel = getItemModel(data, newIndex);\n          var symbolMeta = getSymbolMeta(data, newIndex, itemModel, opt);\n          var pictorialShapeStr = getShapeStr(data, symbolMeta);\n\n          if (bar && pictorialShapeStr !== bar.__pictorialShapeStr) {\n            group.remove(bar);\n            data.setItemGraphicEl(newIndex, null);\n            bar = null;\n          }\n\n          if (bar) {\n            updateBar(bar, opt, symbolMeta);\n          } else {\n            bar = createBar(data, opt, symbolMeta, true);\n          }\n\n          data.setItemGraphicEl(newIndex, bar);\n          bar.__pictorialSymbolMeta = symbolMeta; // Add back\n\n          group.add(bar);\n          updateCommon$1(bar, opt, symbolMeta);\n        }).remove(function (dataIndex) {\n          var bar = oldData.getItemGraphicEl(dataIndex);\n          bar && removeBar(oldData, dataIndex, bar.__pictorialSymbolMeta.animationModel, bar);\n        }).execute();\n        this._data = data;\n        return this.group;\n      };\n\n      PictorialBarView.prototype.remove = function (ecModel, api) {\n        var group = this.group;\n        var data = this._data;\n\n        if (ecModel.get('animation')) {\n          if (data) {\n            data.eachItemGraphicEl(function (bar) {\n              removeBar(data, getECData(bar).dataIndex, ecModel, bar);\n            });\n          }\n        } else {\n          group.removeAll();\n        }\n      };\n\n      PictorialBarView.type = 'pictorialBar';\n      return PictorialBarView;\n    }(ChartView); // Set or calculate default value about symbol, and calculate layout info.\n\n\n    function getSymbolMeta(data, dataIndex, itemModel, opt) {\n      var layout = data.getItemLayout(dataIndex);\n      var symbolRepeat = itemModel.get('symbolRepeat');\n      var symbolClip = itemModel.get('symbolClip');\n      var symbolPosition = itemModel.get('symbolPosition') || 'start';\n      var symbolRotate = itemModel.get('symbolRotate');\n      var rotation = (symbolRotate || 0) * Math.PI / 180 || 0;\n      var symbolPatternSize = itemModel.get('symbolPatternSize') || 2;\n      var isAnimationEnabled = itemModel.isAnimationEnabled();\n      var symbolMeta = {\n        dataIndex: dataIndex,\n        layout: layout,\n        itemModel: itemModel,\n        symbolType: data.getItemVisual(dataIndex, 'symbol') || 'circle',\n        style: data.getItemVisual(dataIndex, 'style'),\n        symbolClip: symbolClip,\n        symbolRepeat: symbolRepeat,\n        symbolRepeatDirection: itemModel.get('symbolRepeatDirection'),\n        symbolPatternSize: symbolPatternSize,\n        rotation: rotation,\n        animationModel: isAnimationEnabled ? itemModel : null,\n        hoverScale: isAnimationEnabled && itemModel.get(['emphasis', 'scale']),\n        z2: itemModel.getShallow('z', true) || 0\n      };\n      prepareBarLength(itemModel, symbolRepeat, layout, opt, symbolMeta);\n      prepareSymbolSize(data, dataIndex, layout, symbolRepeat, symbolClip, symbolMeta.boundingLength, symbolMeta.pxSign, symbolPatternSize, opt, symbolMeta);\n      prepareLineWidth(itemModel, symbolMeta.symbolScale, rotation, opt, symbolMeta);\n      var symbolSize = symbolMeta.symbolSize;\n      var symbolOffset = normalizeSymbolOffset(itemModel.get('symbolOffset'), symbolSize);\n      prepareLayoutInfo(itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, symbolPosition, symbolMeta.valueLineWidth, symbolMeta.boundingLength, symbolMeta.repeatCutLength, opt, symbolMeta);\n      return symbolMeta;\n    } // bar length can be negative.\n\n\n    function prepareBarLength(itemModel, symbolRepeat, layout, opt, outputSymbolMeta) {\n      var valueDim = opt.valueDim;\n      var symbolBoundingData = itemModel.get('symbolBoundingData');\n      var valueAxis = opt.coordSys.getOtherAxis(opt.coordSys.getBaseAxis());\n      var zeroPx = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0));\n      var pxSignIdx = 1 - +(layout[valueDim.wh] <= 0);\n      var boundingLength;\n\n      if (isArray(symbolBoundingData)) {\n        var symbolBoundingExtent = [convertToCoordOnAxis(valueAxis, symbolBoundingData[0]) - zeroPx, convertToCoordOnAxis(valueAxis, symbolBoundingData[1]) - zeroPx];\n        symbolBoundingExtent[1] < symbolBoundingExtent[0] && symbolBoundingExtent.reverse();\n        boundingLength = symbolBoundingExtent[pxSignIdx];\n      } else if (symbolBoundingData != null) {\n        boundingLength = convertToCoordOnAxis(valueAxis, symbolBoundingData) - zeroPx;\n      } else if (symbolRepeat) {\n        boundingLength = opt.coordSysExtent[valueDim.index][pxSignIdx] - zeroPx;\n      } else {\n        boundingLength = layout[valueDim.wh];\n      }\n\n      outputSymbolMeta.boundingLength = boundingLength;\n\n      if (symbolRepeat) {\n        outputSymbolMeta.repeatCutLength = layout[valueDim.wh];\n      } // if 'pxSign' means sign of pixel,  it can't be zero, or symbolScale will be zero\n      // and when borderWidth be settled, the actual linewidth will be NaN\n\n\n      outputSymbolMeta.pxSign = boundingLength > 0 ? 1 : -1;\n    }\n\n    function convertToCoordOnAxis(axis, value) {\n      return axis.toGlobalCoord(axis.dataToCoord(axis.scale.parse(value)));\n    } // Support ['100%', '100%']\n\n\n    function prepareSymbolSize(data, dataIndex, layout, symbolRepeat, symbolClip, boundingLength, pxSign, symbolPatternSize, opt, outputSymbolMeta) {\n      var valueDim = opt.valueDim;\n      var categoryDim = opt.categoryDim;\n      var categorySize = Math.abs(layout[categoryDim.wh]);\n      var symbolSize = data.getItemVisual(dataIndex, 'symbolSize');\n      var parsedSymbolSize;\n\n      if (isArray(symbolSize)) {\n        parsedSymbolSize = symbolSize.slice();\n      } else {\n        if (symbolSize == null) {\n          // will parse to number below\n          parsedSymbolSize = ['100%', '100%'];\n        } else {\n          parsedSymbolSize = [symbolSize, symbolSize];\n        }\n      } // Note: percentage symbolSize (like '100%') do not consider lineWidth, because it is\n      // to complicated to calculate real percent value if considering scaled lineWidth.\n      // So the actual size will bigger than layout size if lineWidth is bigger than zero,\n      // which can be tolerated in pictorial chart.\n\n\n      parsedSymbolSize[categoryDim.index] = parsePercent$1(parsedSymbolSize[categoryDim.index], categorySize);\n      parsedSymbolSize[valueDim.index] = parsePercent$1(parsedSymbolSize[valueDim.index], symbolRepeat ? categorySize : Math.abs(boundingLength));\n      outputSymbolMeta.symbolSize = parsedSymbolSize; // If x or y is less than zero, show reversed shape.\n\n      var symbolScale = outputSymbolMeta.symbolScale = [parsedSymbolSize[0] / symbolPatternSize, parsedSymbolSize[1] / symbolPatternSize]; // Follow convention, 'right' and 'top' is the normal scale.\n\n      symbolScale[valueDim.index] *= (opt.isHorizontal ? -1 : 1) * pxSign;\n    }\n\n    function prepareLineWidth(itemModel, symbolScale, rotation, opt, outputSymbolMeta) {\n      // In symbols are drawn with scale, so do not need to care about the case that width\n      // or height are too small. But symbol use strokeNoScale, where acture lineWidth should\n      // be calculated.\n      var valueLineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;\n\n      if (valueLineWidth) {\n        pathForLineWidth.attr({\n          scaleX: symbolScale[0],\n          scaleY: symbolScale[1],\n          rotation: rotation\n        });\n        pathForLineWidth.updateTransform();\n        valueLineWidth /= pathForLineWidth.getLineScale();\n        valueLineWidth *= symbolScale[opt.valueDim.index];\n      }\n\n      outputSymbolMeta.valueLineWidth = valueLineWidth || 0;\n    }\n\n    function prepareLayoutInfo(itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, symbolPosition, valueLineWidth, boundingLength, repeatCutLength, opt, outputSymbolMeta) {\n      var categoryDim = opt.categoryDim;\n      var valueDim = opt.valueDim;\n      var pxSign = outputSymbolMeta.pxSign;\n      var unitLength = Math.max(symbolSize[valueDim.index] + valueLineWidth, 0);\n      var pathLen = unitLength; // Note: rotation will not effect the layout of symbols, because user may\n      // want symbols to rotate on its center, which should not be translated\n      // when rotating.\n\n      if (symbolRepeat) {\n        var absBoundingLength = Math.abs(boundingLength);\n        var symbolMargin = retrieve(itemModel.get('symbolMargin'), '15%') + '';\n        var hasEndGap = false;\n\n        if (symbolMargin.lastIndexOf('!') === symbolMargin.length - 1) {\n          hasEndGap = true;\n          symbolMargin = symbolMargin.slice(0, symbolMargin.length - 1);\n        }\n\n        var symbolMarginNumeric = parsePercent$1(symbolMargin, symbolSize[valueDim.index]);\n        var uLenWithMargin = Math.max(unitLength + symbolMarginNumeric * 2, 0); // When symbol margin is less than 0, margin at both ends will be subtracted\n        // to ensure that all of the symbols will not be overflow the given area.\n\n        var endFix = hasEndGap ? 0 : symbolMarginNumeric * 2; // Both final repeatTimes and final symbolMarginNumeric area calculated based on\n        // boundingLength.\n\n        var repeatSpecified = isNumeric(symbolRepeat);\n        var repeatTimes = repeatSpecified ? symbolRepeat : toIntTimes((absBoundingLength + endFix) / uLenWithMargin); // Adjust calculate margin, to ensure each symbol is displayed\n        // entirely in the given layout area.\n\n        var mDiff = absBoundingLength - repeatTimes * unitLength;\n        symbolMarginNumeric = mDiff / 2 / (hasEndGap ? repeatTimes : Math.max(repeatTimes - 1, 1));\n        uLenWithMargin = unitLength + symbolMarginNumeric * 2;\n        endFix = hasEndGap ? 0 : symbolMarginNumeric * 2; // Update repeatTimes when not all symbol will be shown.\n\n        if (!repeatSpecified && symbolRepeat !== 'fixed') {\n          repeatTimes = repeatCutLength ? toIntTimes((Math.abs(repeatCutLength) + endFix) / uLenWithMargin) : 0;\n        }\n\n        pathLen = repeatTimes * uLenWithMargin - endFix;\n        outputSymbolMeta.repeatTimes = repeatTimes;\n        outputSymbolMeta.symbolMargin = symbolMarginNumeric;\n      }\n\n      var sizeFix = pxSign * (pathLen / 2);\n      var pathPosition = outputSymbolMeta.pathPosition = [];\n      pathPosition[categoryDim.index] = layout[categoryDim.wh] / 2;\n      pathPosition[valueDim.index] = symbolPosition === 'start' ? sizeFix : symbolPosition === 'end' ? boundingLength - sizeFix : boundingLength / 2; // 'center'\n\n      if (symbolOffset) {\n        pathPosition[0] += symbolOffset[0];\n        pathPosition[1] += symbolOffset[1];\n      }\n\n      var bundlePosition = outputSymbolMeta.bundlePosition = [];\n      bundlePosition[categoryDim.index] = layout[categoryDim.xy];\n      bundlePosition[valueDim.index] = layout[valueDim.xy];\n      var barRectShape = outputSymbolMeta.barRectShape = extend({}, layout);\n      barRectShape[valueDim.wh] = pxSign * Math.max(Math.abs(layout[valueDim.wh]), Math.abs(pathPosition[valueDim.index] + sizeFix));\n      barRectShape[categoryDim.wh] = layout[categoryDim.wh];\n      var clipShape = outputSymbolMeta.clipShape = {}; // Consider that symbol may be overflow layout rect.\n\n      clipShape[categoryDim.xy] = -layout[categoryDim.xy];\n      clipShape[categoryDim.wh] = opt.ecSize[categoryDim.wh];\n      clipShape[valueDim.xy] = 0;\n      clipShape[valueDim.wh] = layout[valueDim.wh];\n    }\n\n    function createPath(symbolMeta) {\n      var symbolPatternSize = symbolMeta.symbolPatternSize;\n      var path = createSymbol( // Consider texture img, make a big size.\n      symbolMeta.symbolType, -symbolPatternSize / 2, -symbolPatternSize / 2, symbolPatternSize, symbolPatternSize);\n      path.attr({\n        culling: true\n      });\n      path.type !== 'image' && path.setStyle({\n        strokeNoScale: true\n      });\n      return path;\n    }\n\n    function createOrUpdateRepeatSymbols(bar, opt, symbolMeta, isUpdate) {\n      var bundle = bar.__pictorialBundle;\n      var symbolSize = symbolMeta.symbolSize;\n      var valueLineWidth = symbolMeta.valueLineWidth;\n      var pathPosition = symbolMeta.pathPosition;\n      var valueDim = opt.valueDim;\n      var repeatTimes = symbolMeta.repeatTimes || 0;\n      var index = 0;\n      var unit = symbolSize[opt.valueDim.index] + valueLineWidth + symbolMeta.symbolMargin * 2;\n      eachPath(bar, function (path) {\n        path.__pictorialAnimationIndex = index;\n        path.__pictorialRepeatTimes = repeatTimes;\n\n        if (index < repeatTimes) {\n          updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate);\n        } else {\n          updateAttr(path, null, {\n            scaleX: 0,\n            scaleY: 0\n          }, symbolMeta, isUpdate, function () {\n            bundle.remove(path);\n          });\n        } // updateHoverAnimation(path, symbolMeta);\n\n\n        index++;\n      });\n\n      for (; index < repeatTimes; index++) {\n        var path = createPath(symbolMeta);\n        path.__pictorialAnimationIndex = index;\n        path.__pictorialRepeatTimes = repeatTimes;\n        bundle.add(path);\n        var target = makeTarget(index);\n        updateAttr(path, {\n          x: target.x,\n          y: target.y,\n          scaleX: 0,\n          scaleY: 0\n        }, {\n          scaleX: target.scaleX,\n          scaleY: target.scaleY,\n          rotation: target.rotation\n        }, symbolMeta, isUpdate);\n      }\n\n      function makeTarget(index) {\n        var position = pathPosition.slice(); // (start && pxSign > 0) || (end && pxSign < 0): i = repeatTimes - index\n        // Otherwise: i = index;\n\n        var pxSign = symbolMeta.pxSign;\n        var i = index;\n\n        if (symbolMeta.symbolRepeatDirection === 'start' ? pxSign > 0 : pxSign < 0) {\n          i = repeatTimes - 1 - index;\n        }\n\n        position[valueDim.index] = unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index];\n        return {\n          x: position[0],\n          y: position[1],\n          scaleX: symbolMeta.symbolScale[0],\n          scaleY: symbolMeta.symbolScale[1],\n          rotation: symbolMeta.rotation\n        };\n      }\n    }\n\n    function createOrUpdateSingleSymbol(bar, opt, symbolMeta, isUpdate) {\n      var bundle = bar.__pictorialBundle;\n      var mainPath = bar.__pictorialMainPath;\n\n      if (!mainPath) {\n        mainPath = bar.__pictorialMainPath = createPath(symbolMeta);\n        bundle.add(mainPath);\n        updateAttr(mainPath, {\n          x: symbolMeta.pathPosition[0],\n          y: symbolMeta.pathPosition[1],\n          scaleX: 0,\n          scaleY: 0,\n          rotation: symbolMeta.rotation\n        }, {\n          scaleX: symbolMeta.symbolScale[0],\n          scaleY: symbolMeta.symbolScale[1]\n        }, symbolMeta, isUpdate);\n      } else {\n        updateAttr(mainPath, null, {\n          x: symbolMeta.pathPosition[0],\n          y: symbolMeta.pathPosition[1],\n          scaleX: symbolMeta.symbolScale[0],\n          scaleY: symbolMeta.symbolScale[1],\n          rotation: symbolMeta.rotation\n        }, symbolMeta, isUpdate);\n      }\n    } // bar rect is used for label.\n\n\n    function createOrUpdateBarRect(bar, symbolMeta, isUpdate) {\n      var rectShape = extend({}, symbolMeta.barRectShape);\n      var barRect = bar.__pictorialBarRect;\n\n      if (!barRect) {\n        barRect = bar.__pictorialBarRect = new Rect({\n          z2: 2,\n          shape: rectShape,\n          silent: true,\n          style: {\n            stroke: 'transparent',\n            fill: 'transparent',\n            lineWidth: 0\n          }\n        });\n        barRect.disableMorphing = true;\n        bar.add(barRect);\n      } else {\n        updateAttr(barRect, null, {\n          shape: rectShape\n        }, symbolMeta, isUpdate);\n      }\n    }\n\n    function createOrUpdateClip(bar, opt, symbolMeta, isUpdate) {\n      // If not clip, symbol will be remove and rebuilt.\n      if (symbolMeta.symbolClip) {\n        var clipPath = bar.__pictorialClipPath;\n        var clipShape = extend({}, symbolMeta.clipShape);\n        var valueDim = opt.valueDim;\n        var animationModel = symbolMeta.animationModel;\n        var dataIndex = symbolMeta.dataIndex;\n\n        if (clipPath) {\n          updateProps(clipPath, {\n            shape: clipShape\n          }, animationModel, dataIndex);\n        } else {\n          clipShape[valueDim.wh] = 0;\n          clipPath = new Rect({\n            shape: clipShape\n          });\n\n          bar.__pictorialBundle.setClipPath(clipPath);\n\n          bar.__pictorialClipPath = clipPath;\n          var target = {};\n          target[valueDim.wh] = symbolMeta.clipShape[valueDim.wh];\n          graphic[isUpdate ? 'updateProps' : 'initProps'](clipPath, {\n            shape: target\n          }, animationModel, dataIndex);\n        }\n      }\n    }\n\n    function getItemModel(data, dataIndex) {\n      var itemModel = data.getItemModel(dataIndex);\n      itemModel.getAnimationDelayParams = getAnimationDelayParams;\n      itemModel.isAnimationEnabled = isAnimationEnabled;\n      return itemModel;\n    }\n\n    function getAnimationDelayParams(path) {\n      // The order is the same as the z-order, see `symbolRepeatDiretion`.\n      return {\n        index: path.__pictorialAnimationIndex,\n        count: path.__pictorialRepeatTimes\n      };\n    }\n\n    function isAnimationEnabled() {\n      // `animation` prop can be set on itemModel in pictorial bar chart.\n      return this.parentModel.isAnimationEnabled() && !!this.getShallow('animation');\n    }\n\n    function createBar(data, opt, symbolMeta, isUpdate) {\n      // bar is the main element for each data.\n      var bar = new Group(); // bundle is used for location and clip.\n\n      var bundle = new Group();\n      bar.add(bundle);\n      bar.__pictorialBundle = bundle;\n      bundle.x = symbolMeta.bundlePosition[0];\n      bundle.y = symbolMeta.bundlePosition[1];\n\n      if (symbolMeta.symbolRepeat) {\n        createOrUpdateRepeatSymbols(bar, opt, symbolMeta);\n      } else {\n        createOrUpdateSingleSymbol(bar, opt, symbolMeta);\n      }\n\n      createOrUpdateBarRect(bar, symbolMeta, isUpdate);\n      createOrUpdateClip(bar, opt, symbolMeta, isUpdate);\n      bar.__pictorialShapeStr = getShapeStr(data, symbolMeta);\n      bar.__pictorialSymbolMeta = symbolMeta;\n      return bar;\n    }\n\n    function updateBar(bar, opt, symbolMeta) {\n      var animationModel = symbolMeta.animationModel;\n      var dataIndex = symbolMeta.dataIndex;\n      var bundle = bar.__pictorialBundle;\n      updateProps(bundle, {\n        x: symbolMeta.bundlePosition[0],\n        y: symbolMeta.bundlePosition[1]\n      }, animationModel, dataIndex);\n\n      if (symbolMeta.symbolRepeat) {\n        createOrUpdateRepeatSymbols(bar, opt, symbolMeta, true);\n      } else {\n        createOrUpdateSingleSymbol(bar, opt, symbolMeta, true);\n      }\n\n      createOrUpdateBarRect(bar, symbolMeta, true);\n      createOrUpdateClip(bar, opt, symbolMeta, true);\n    }\n\n    function removeBar(data, dataIndex, animationModel, bar) {\n      // Not show text when animating\n      var labelRect = bar.__pictorialBarRect;\n      labelRect && labelRect.removeTextContent();\n      var paths = [];\n      eachPath(bar, function (path) {\n        paths.push(path);\n      });\n      bar.__pictorialMainPath && paths.push(bar.__pictorialMainPath); // I do not find proper remove animation for clip yet.\n\n      bar.__pictorialClipPath && (animationModel = null);\n      each(paths, function (path) {\n        removeElement(path, {\n          scaleX: 0,\n          scaleY: 0\n        }, animationModel, dataIndex, function () {\n          bar.parent && bar.parent.remove(bar);\n        });\n      });\n      data.setItemGraphicEl(dataIndex, null);\n    }\n\n    function getShapeStr(data, symbolMeta) {\n      return [data.getItemVisual(symbolMeta.dataIndex, 'symbol') || 'none', !!symbolMeta.symbolRepeat, !!symbolMeta.symbolClip].join(':');\n    }\n\n    function eachPath(bar, cb, context) {\n      // Do not use Group#eachChild, because it do not support remove.\n      each(bar.__pictorialBundle.children(), function (el) {\n        el !== bar.__pictorialBarRect && cb.call(context, el);\n      });\n    }\n\n    function updateAttr(el, immediateAttrs, animationAttrs, symbolMeta, isUpdate, cb) {\n      immediateAttrs && el.attr(immediateAttrs); // when symbolCip used, only clip path has init animation, otherwise it would be weird effect.\n\n      if (symbolMeta.symbolClip && !isUpdate) {\n        animationAttrs && el.attr(animationAttrs);\n      } else {\n        animationAttrs && graphic[isUpdate ? 'updateProps' : 'initProps'](el, animationAttrs, symbolMeta.animationModel, symbolMeta.dataIndex, cb);\n      }\n    }\n\n    function updateCommon$1(bar, opt, symbolMeta) {\n      var dataIndex = symbolMeta.dataIndex;\n      var itemModel = symbolMeta.itemModel; // Color must be excluded.\n      // Because symbol provide setColor individually to set fill and stroke\n\n      var emphasisModel = itemModel.getModel('emphasis');\n      var emphasisStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n      var blurStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n      var selectStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();\n      var cursorStyle = itemModel.getShallow('cursor');\n      var focus = emphasisModel.get('focus');\n      var blurScope = emphasisModel.get('blurScope');\n      var hoverScale = emphasisModel.get('scale');\n      eachPath(bar, function (path) {\n        if (path instanceof ZRImage) {\n          var pathStyle = path.style;\n          path.useStyle(extend({\n            // TODO other properties like dx, dy ?\n            image: pathStyle.image,\n            x: pathStyle.x,\n            y: pathStyle.y,\n            width: pathStyle.width,\n            height: pathStyle.height\n          }, symbolMeta.style));\n        } else {\n          path.useStyle(symbolMeta.style);\n        }\n\n        var emphasisState = path.ensureState('emphasis');\n        emphasisState.style = emphasisStyle;\n\n        if (hoverScale) {\n          // NOTE: Must after scale is set after updateAttr\n          emphasisState.scaleX = path.scaleX * 1.1;\n          emphasisState.scaleY = path.scaleY * 1.1;\n        }\n\n        path.ensureState('blur').style = blurStyle;\n        path.ensureState('select').style = selectStyle;\n        cursorStyle && (path.cursor = cursorStyle);\n        path.z2 = symbolMeta.z2;\n      });\n      var barPositionOutside = opt.valueDim.posDesc[+(symbolMeta.boundingLength > 0)];\n      var barRect = bar.__pictorialBarRect;\n      setLabelStyle(barRect, getLabelStatesModels(itemModel), {\n        labelFetcher: opt.seriesModel,\n        labelDataIndex: dataIndex,\n        defaultText: getDefaultLabel(opt.seriesModel.getData(), dataIndex),\n        inheritColor: symbolMeta.style.fill,\n        defaultOpacity: symbolMeta.style.opacity,\n        defaultOutsidePosition: barPositionOutside\n      });\n      toggleHoverEmphasis(bar, focus, blurScope, emphasisModel.get('disabled'));\n    }\n\n    function toIntTimes(times) {\n      var roundedTimes = Math.round(times); // Escapse accurate error\n\n      return Math.abs(times - roundedTimes) < 1e-4 ? roundedTimes : Math.ceil(times);\n    }\n\n    var PictorialBarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(PictorialBarSeriesModel, _super);\n\n      function PictorialBarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = PictorialBarSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        _this.defaultSymbol = 'roundRect';\n        return _this;\n      }\n\n      PictorialBarSeriesModel.prototype.getInitialData = function (option) {\n        // Disable stack.\n        option.stack = null;\n        return _super.prototype.getInitialData.apply(this, arguments);\n      };\n\n      PictorialBarSeriesModel.type = 'series.pictorialBar';\n      PictorialBarSeriesModel.dependencies = ['grid'];\n      PictorialBarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, {\n        symbol: 'circle',\n        symbolSize: null,\n        symbolRotate: null,\n        symbolPosition: null,\n        symbolOffset: null,\n        symbolMargin: null,\n        symbolRepeat: false,\n        symbolRepeatDirection: 'end',\n        symbolClip: false,\n        symbolBoundingData: null,\n        symbolPatternSize: 400,\n        barGap: '-100%',\n        // z can be set in data item, which is z2 actually.\n        // Disable progressive\n        progressive: 0,\n        emphasis: {\n          // By default pictorialBar do not hover scale. Hover scale is not suitable\n          // for the case that both has foreground and background.\n          scale: false\n        },\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        }\n      });\n      return PictorialBarSeriesModel;\n    }(BaseBarSeriesModel);\n\n    function install$o(registers) {\n      registers.registerChartView(PictorialBarView);\n      registers.registerSeriesModel(PictorialBarSeriesModel);\n      registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'pictorialBar')); // Do layout after other overall layout, which can prepare some information.\n\n      registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('pictorialBar'));\n    }\n\n    var ThemeRiverView =\n    /** @class */\n    function (_super) {\n      __extends(ThemeRiverView, _super);\n\n      function ThemeRiverView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ThemeRiverView.type;\n        _this._layers = [];\n        return _this;\n      }\n\n      ThemeRiverView.prototype.render = function (seriesModel, ecModel, api) {\n        var data = seriesModel.getData();\n        var self = this;\n        var group = this.group;\n        var layersSeries = seriesModel.getLayerSeries();\n        var layoutInfo = data.getLayout('layoutInfo');\n        var rect = layoutInfo.rect;\n        var boundaryGap = layoutInfo.boundaryGap;\n        group.x = 0;\n        group.y = rect.y + boundaryGap[0];\n\n        function keyGetter(item) {\n          return item.name;\n        }\n\n        var dataDiffer = new DataDiffer(this._layersSeries || [], layersSeries, keyGetter, keyGetter);\n        var newLayersGroups = [];\n        dataDiffer.add(bind(process, this, 'add')).update(bind(process, this, 'update')).remove(bind(process, this, 'remove')).execute();\n\n        function process(status, idx, oldIdx) {\n          var oldLayersGroups = self._layers;\n\n          if (status === 'remove') {\n            group.remove(oldLayersGroups[idx]);\n            return;\n          }\n\n          var points0 = [];\n          var points1 = [];\n          var style;\n          var indices = layersSeries[idx].indices;\n          var j = 0;\n\n          for (; j < indices.length; j++) {\n            var layout = data.getItemLayout(indices[j]);\n            var x = layout.x;\n            var y0 = layout.y0;\n            var y = layout.y;\n            points0.push(x, y0);\n            points1.push(x, y0 + y);\n            style = data.getItemVisual(indices[j], 'style');\n          }\n\n          var polygon;\n          var textLayout = data.getItemLayout(indices[0]);\n          var labelModel = seriesModel.getModel('label');\n          var margin = labelModel.get('margin');\n          var emphasisModel = seriesModel.getModel('emphasis');\n\n          if (status === 'add') {\n            var layerGroup = newLayersGroups[idx] = new Group();\n            polygon = new ECPolygon({\n              shape: {\n                points: points0,\n                stackedOnPoints: points1,\n                smooth: 0.4,\n                stackedOnSmooth: 0.4,\n                smoothConstraint: false\n              },\n              z2: 0\n            });\n            layerGroup.add(polygon);\n            group.add(layerGroup);\n\n            if (seriesModel.isAnimationEnabled()) {\n              polygon.setClipPath(createGridClipShape$2(polygon.getBoundingRect(), seriesModel, function () {\n                polygon.removeClipPath();\n              }));\n            }\n          } else {\n            var layerGroup = oldLayersGroups[oldIdx];\n            polygon = layerGroup.childAt(0);\n            group.add(layerGroup);\n            newLayersGroups[idx] = layerGroup;\n            updateProps(polygon, {\n              shape: {\n                points: points0,\n                stackedOnPoints: points1\n              }\n            }, seriesModel);\n            saveOldStyle(polygon);\n          }\n\n          setLabelStyle(polygon, getLabelStatesModels(seriesModel), {\n            labelDataIndex: indices[j - 1],\n            defaultText: data.getName(indices[j - 1]),\n            inheritColor: style.fill\n          }, {\n            normal: {\n              verticalAlign: 'middle' // align: 'right'\n\n            }\n          });\n          polygon.setTextConfig({\n            position: null,\n            local: true\n          });\n          var labelEl = polygon.getTextContent(); // TODO More label position options.\n\n          if (labelEl) {\n            labelEl.x = textLayout.x - margin;\n            labelEl.y = textLayout.y0 + textLayout.y / 2;\n          }\n\n          polygon.useStyle(style);\n          data.setItemGraphicEl(idx, polygon);\n          setStatesStylesFromModel(polygon, seriesModel);\n          toggleHoverEmphasis(polygon, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n        }\n\n        this._layersSeries = layersSeries;\n        this._layers = newLayersGroups;\n      };\n\n      ThemeRiverView.type = 'themeRiver';\n      return ThemeRiverView;\n    }(ChartView);\n\n    function createGridClipShape$2(rect, seriesModel, cb) {\n      var rectEl = new Rect({\n        shape: {\n          x: rect.x - 10,\n          y: rect.y - 10,\n          width: 0,\n          height: rect.height + 20\n        }\n      });\n      initProps(rectEl, {\n        shape: {\n          x: rect.x - 50,\n          width: rect.width + 100,\n          height: rect.height + 20\n        }\n      }, seriesModel, cb);\n      return rectEl;\n    }\n\n    var DATA_NAME_INDEX = 2;\n\n    var ThemeRiverSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(ThemeRiverSeriesModel, _super);\n\n      function ThemeRiverSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ThemeRiverSeriesModel.type;\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      ThemeRiverSeriesModel.prototype.init = function (option) {\n        // eslint-disable-next-line\n        _super.prototype.init.apply(this, arguments); // Put this function here is for the sake of consistency of code style.\n        // Enable legend selection for each data item\n        // Use a function instead of direct access because data reference may changed\n\n\n        this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n      };\n      /**\n       * If there is no value of a certain point in the time for some event,set it value to 0.\n       *\n       * @param {Array} data  initial data in the option\n       * @return {Array}\n       */\n\n\n      ThemeRiverSeriesModel.prototype.fixData = function (data) {\n        var rawDataLength = data.length;\n        /**\n         * Make sure every layer data get the same keys.\n         * The value index tells which layer has visited.\n         * {\n         *  2014/01/01: -1\n         * }\n         */\n\n        var timeValueKeys = {}; // grouped data by name\n\n        var groupResult = groupData(data, function (item) {\n          if (!timeValueKeys.hasOwnProperty(item[0] + '')) {\n            timeValueKeys[item[0] + ''] = -1;\n          }\n\n          return item[2];\n        });\n        var layerData = [];\n        groupResult.buckets.each(function (items, key) {\n          layerData.push({\n            name: key,\n            dataList: items\n          });\n        });\n        var layerNum = layerData.length;\n\n        for (var k = 0; k < layerNum; ++k) {\n          var name_1 = layerData[k].name;\n\n          for (var j = 0; j < layerData[k].dataList.length; ++j) {\n            var timeValue = layerData[k].dataList[j][0] + '';\n            timeValueKeys[timeValue] = k;\n          }\n\n          for (var timeValue in timeValueKeys) {\n            if (timeValueKeys.hasOwnProperty(timeValue) && timeValueKeys[timeValue] !== k) {\n              timeValueKeys[timeValue] = k;\n              data[rawDataLength] = [timeValue, 0, name_1];\n              rawDataLength++;\n            }\n          }\n        }\n\n        return data;\n      };\n      /**\n       * @override\n       * @param  option  the initial option that user gave\n       * @param  ecModel  the model object for themeRiver option\n       */\n\n\n      ThemeRiverSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        var singleAxisModel = this.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n        var axisType = singleAxisModel.get('type'); // filter the data item with the value of label is undefined\n\n        var filterData = filter(option.data, function (dataItem) {\n          return dataItem[2] !== undefined;\n        }); // ??? TODO design a stage to transfer data for themeRiver and lines?\n\n        var data = this.fixData(filterData || []);\n        var nameList = [];\n        var nameMap = this.nameMap = createHashMap();\n        var count = 0;\n\n        for (var i = 0; i < data.length; ++i) {\n          nameList.push(data[i][DATA_NAME_INDEX]);\n\n          if (!nameMap.get(data[i][DATA_NAME_INDEX])) {\n            nameMap.set(data[i][DATA_NAME_INDEX], count);\n            count++;\n          }\n        }\n\n        var dimensions = prepareSeriesDataSchema(data, {\n          coordDimensions: ['single'],\n          dimensionsDefine: [{\n            name: 'time',\n            type: getDimensionTypeByAxis(axisType)\n          }, {\n            name: 'value',\n            type: 'float'\n          }, {\n            name: 'name',\n            type: 'ordinal'\n          }],\n          encodeDefine: {\n            single: 0,\n            value: 1,\n            itemName: 2\n          }\n        }).dimensions;\n        var list = new SeriesData(dimensions, this);\n        list.initData(data);\n        return list;\n      };\n      /**\n       * The raw data is divided into multiple layers and each layer\n       *     has same name.\n       */\n\n\n      ThemeRiverSeriesModel.prototype.getLayerSeries = function () {\n        var data = this.getData();\n        var lenCount = data.count();\n        var indexArr = [];\n\n        for (var i = 0; i < lenCount; ++i) {\n          indexArr[i] = i;\n        }\n\n        var timeDim = data.mapDimension('single'); // data group by name\n\n        var groupResult = groupData(indexArr, function (index) {\n          return data.get('name', index);\n        });\n        var layerSeries = [];\n        groupResult.buckets.each(function (items, key) {\n          items.sort(function (index1, index2) {\n            return data.get(timeDim, index1) - data.get(timeDim, index2);\n          });\n          layerSeries.push({\n            name: key,\n            indices: items\n          });\n        });\n        return layerSeries;\n      };\n      /**\n       * Get data indices for show tooltip content\n       */\n\n\n      ThemeRiverSeriesModel.prototype.getAxisTooltipData = function (dim, value, baseAxis) {\n        if (!isArray(dim)) {\n          dim = dim ? [dim] : [];\n        }\n\n        var data = this.getData();\n        var layerSeries = this.getLayerSeries();\n        var indices = [];\n        var layerNum = layerSeries.length;\n        var nestestValue;\n\n        for (var i = 0; i < layerNum; ++i) {\n          var minDist = Number.MAX_VALUE;\n          var nearestIdx = -1;\n          var pointNum = layerSeries[i].indices.length;\n\n          for (var j = 0; j < pointNum; ++j) {\n            var theValue = data.get(dim[0], layerSeries[i].indices[j]);\n            var dist = Math.abs(theValue - value);\n\n            if (dist <= minDist) {\n              nestestValue = theValue;\n              minDist = dist;\n              nearestIdx = layerSeries[i].indices[j];\n            }\n          }\n\n          indices.push(nearestIdx);\n        }\n\n        return {\n          dataIndices: indices,\n          nestestValue: nestestValue\n        };\n      };\n\n      ThemeRiverSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        var data = this.getData();\n        var name = data.getName(dataIndex);\n        var value = data.get(data.mapDimension('value'), dataIndex);\n        return createTooltipMarkup('nameValue', {\n          name: name,\n          value: value\n        });\n      };\n\n      ThemeRiverSeriesModel.type = 'series.themeRiver';\n      ThemeRiverSeriesModel.dependencies = ['singleAxis'];\n      ThemeRiverSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        colorBy: 'data',\n        coordinateSystem: 'singleAxis',\n        // gap in axis's orthogonal orientation\n        boundaryGap: ['10%', '10%'],\n        // legendHoverLink: true,\n        singleAxisIndex: 0,\n        animationEasing: 'linear',\n        label: {\n          margin: 4,\n          show: true,\n          position: 'left',\n          fontSize: 11\n        },\n        emphasis: {\n          label: {\n            show: true\n          }\n        }\n      };\n      return ThemeRiverSeriesModel;\n    }(SeriesModel);\n\n    function themeRiverLayout(ecModel, api) {\n      ecModel.eachSeriesByType('themeRiver', function (seriesModel) {\n        var data = seriesModel.getData();\n        var single = seriesModel.coordinateSystem;\n        var layoutInfo = {}; // use the axis boundingRect for view\n\n        var rect = single.getRect();\n        layoutInfo.rect = rect;\n        var boundaryGap = seriesModel.get('boundaryGap');\n        var axis = single.getAxis();\n        layoutInfo.boundaryGap = boundaryGap;\n\n        if (axis.orient === 'horizontal') {\n          boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.height);\n          boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.height);\n          var height = rect.height - boundaryGap[0] - boundaryGap[1];\n          doThemeRiverLayout(data, seriesModel, height);\n        } else {\n          boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.width);\n          boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.width);\n          var width = rect.width - boundaryGap[0] - boundaryGap[1];\n          doThemeRiverLayout(data, seriesModel, width);\n        }\n\n        data.setLayout('layoutInfo', layoutInfo);\n      });\n    }\n    /**\n     * The layout information about themeriver\n     *\n     * @param data  data in the series\n     * @param seriesModel  the model object of themeRiver series\n     * @param height  value used to compute every series height\n     */\n\n    function doThemeRiverLayout(data, seriesModel, height) {\n      if (!data.count()) {\n        return;\n      }\n\n      var coordSys = seriesModel.coordinateSystem; // the data in each layer are organized into a series.\n\n      var layerSeries = seriesModel.getLayerSeries(); // the points in each layer.\n\n      var timeDim = data.mapDimension('single');\n      var valueDim = data.mapDimension('value');\n      var layerPoints = map(layerSeries, function (singleLayer) {\n        return map(singleLayer.indices, function (idx) {\n          var pt = coordSys.dataToPoint(data.get(timeDim, idx));\n          pt[1] = data.get(valueDim, idx);\n          return pt;\n        });\n      });\n      var base = computeBaseline(layerPoints);\n      var baseLine = base.y0;\n      var ky = height / base.max; // set layout information for each item.\n\n      var n = layerSeries.length;\n      var m = layerSeries[0].indices.length;\n      var baseY0;\n\n      for (var j = 0; j < m; ++j) {\n        baseY0 = baseLine[j] * ky;\n        data.setItemLayout(layerSeries[0].indices[j], {\n          layerIndex: 0,\n          x: layerPoints[0][j][0],\n          y0: baseY0,\n          y: layerPoints[0][j][1] * ky\n        });\n\n        for (var i = 1; i < n; ++i) {\n          baseY0 += layerPoints[i - 1][j][1] * ky;\n          data.setItemLayout(layerSeries[i].indices[j], {\n            layerIndex: i,\n            x: layerPoints[i][j][0],\n            y0: baseY0,\n            y: layerPoints[i][j][1] * ky\n          });\n        }\n      }\n    }\n    /**\n     * Compute the baseLine of the rawdata\n     * Inspired by Lee Byron's paper Stacked Graphs - Geometry & Aesthetics\n     *\n     * @param  data  the points in each layer\n     */\n\n\n    function computeBaseline(data) {\n      var layerNum = data.length;\n      var pointNum = data[0].length;\n      var sums = [];\n      var y0 = [];\n      var max = 0;\n\n      for (var i = 0; i < pointNum; ++i) {\n        var temp = 0;\n\n        for (var j = 0; j < layerNum; ++j) {\n          temp += data[j][i][1];\n        }\n\n        if (temp > max) {\n          max = temp;\n        }\n\n        sums.push(temp);\n      }\n\n      for (var k = 0; k < pointNum; ++k) {\n        y0[k] = (max - sums[k]) / 2;\n      }\n\n      max = 0;\n\n      for (var l = 0; l < pointNum; ++l) {\n        var sum = sums[l] + y0[l];\n\n        if (sum > max) {\n          max = sum;\n        }\n      }\n\n      return {\n        y0: y0,\n        max: max\n      };\n    }\n\n    function install$p(registers) {\n      registers.registerChartView(ThemeRiverView);\n      registers.registerSeriesModel(ThemeRiverSeriesModel);\n      registers.registerLayout(themeRiverLayout);\n      registers.registerProcessor(dataFilter('themeRiver'));\n    }\n\n    var DEFAULT_SECTOR_Z = 2;\n    var DEFAULT_TEXT_Z = 4;\n    /**\n     * Sunburstce of Sunburst including Sector, Label, LabelLine\n     */\n\n    var SunburstPiece =\n    /** @class */\n    function (_super) {\n      __extends(SunburstPiece, _super);\n\n      function SunburstPiece(node, seriesModel, ecModel, api) {\n        var _this = _super.call(this) || this;\n\n        _this.z2 = DEFAULT_SECTOR_Z;\n        _this.textConfig = {\n          inside: true\n        };\n        getECData(_this).seriesIndex = seriesModel.seriesIndex;\n        var text = new ZRText({\n          z2: DEFAULT_TEXT_Z,\n          silent: node.getModel().get(['label', 'silent'])\n        });\n\n        _this.setTextContent(text);\n\n        _this.updateData(true, node, seriesModel, ecModel, api);\n\n        return _this;\n      }\n\n      SunburstPiece.prototype.updateData = function (firstCreate, node, // state: 'emphasis' | 'normal' | 'highlight' | 'downplay',\n      seriesModel, ecModel, api) {\n        this.node = node;\n        node.piece = this;\n        seriesModel = seriesModel || this._seriesModel;\n        ecModel = ecModel || this._ecModel;\n        var sector = this;\n        getECData(sector).dataIndex = node.dataIndex;\n        var itemModel = node.getModel();\n        var emphasisModel = itemModel.getModel('emphasis');\n        var layout = node.getLayout();\n        var sectorShape = extend({}, layout);\n        sectorShape.label = null;\n        var normalStyle = node.getVisual('style');\n        normalStyle.lineJoin = 'bevel';\n        var decal = node.getVisual('decal');\n\n        if (decal) {\n          normalStyle.decal = createOrUpdatePatternFromDecal(decal, api);\n        }\n\n        var cornerRadius = getSectorCornerRadius(itemModel.getModel('itemStyle'), sectorShape, true);\n        extend(sectorShape, cornerRadius);\n        each(SPECIAL_STATES, function (stateName) {\n          var state = sector.ensureState(stateName);\n          var itemStyleModel = itemModel.getModel([stateName, 'itemStyle']);\n          state.style = itemStyleModel.getItemStyle(); // border radius\n\n          var cornerRadius = getSectorCornerRadius(itemStyleModel, sectorShape);\n\n          if (cornerRadius) {\n            state.shape = cornerRadius;\n          }\n        });\n\n        if (firstCreate) {\n          sector.setShape(sectorShape);\n          sector.shape.r = layout.r0;\n          updateProps(sector, {\n            shape: {\n              r: layout.r\n            }\n          }, seriesModel, node.dataIndex);\n        } else {\n          // Disable animation for gradient since no interpolation method\n          // is supported for gradient\n          updateProps(sector, {\n            shape: sectorShape\n          }, seriesModel);\n          saveOldStyle(sector);\n        }\n\n        sector.useStyle(normalStyle);\n\n        this._updateLabel(seriesModel);\n\n        var cursorStyle = itemModel.getShallow('cursor');\n        cursorStyle && sector.attr('cursor', cursorStyle);\n        this._seriesModel = seriesModel || this._seriesModel;\n        this._ecModel = ecModel || this._ecModel;\n        var focus = emphasisModel.get('focus');\n        var focusOrIndices = focus === 'ancestor' ? node.getAncestorsIndices() : focus === 'descendant' ? node.getDescendantIndices() : focus;\n        toggleHoverEmphasis(this, focusOrIndices, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      };\n\n      SunburstPiece.prototype._updateLabel = function (seriesModel) {\n        var _this = this;\n\n        var itemModel = this.node.getModel();\n        var normalLabelModel = itemModel.getModel('label');\n        var layout = this.node.getLayout();\n        var angle = layout.endAngle - layout.startAngle;\n        var midAngle = (layout.startAngle + layout.endAngle) / 2;\n        var dx = Math.cos(midAngle);\n        var dy = Math.sin(midAngle);\n        var sector = this;\n        var label = sector.getTextContent();\n        var dataIndex = this.node.dataIndex;\n        var labelMinAngle = normalLabelModel.get('minAngle') / 180 * Math.PI;\n        var isNormalShown = normalLabelModel.get('show') && !(labelMinAngle != null && Math.abs(angle) < labelMinAngle);\n        label.ignore = !isNormalShown; // TODO use setLabelStyle\n\n        each(DISPLAY_STATES, function (stateName) {\n          var labelStateModel = stateName === 'normal' ? itemModel.getModel('label') : itemModel.getModel([stateName, 'label']);\n          var isNormal = stateName === 'normal';\n          var state = isNormal ? label : label.ensureState(stateName);\n          var text = seriesModel.getFormattedLabel(dataIndex, stateName);\n\n          if (isNormal) {\n            text = text || _this.node.name;\n          }\n\n          state.style = createTextStyle(labelStateModel, {}, null, stateName !== 'normal', true);\n\n          if (text) {\n            state.style.text = text;\n          } // Not displaying text when angle is too small\n\n\n          var isShown = labelStateModel.get('show');\n\n          if (isShown != null && !isNormal) {\n            state.ignore = !isShown;\n          }\n\n          var labelPosition = getLabelAttr(labelStateModel, 'position');\n          var sectorState = isNormal ? sector : sector.states[stateName];\n          var labelColor = sectorState.style.fill;\n          sectorState.textConfig = {\n            outsideFill: labelStateModel.get('color') === 'inherit' ? labelColor : null,\n            inside: labelPosition !== 'outside'\n          };\n          var r;\n          var labelPadding = getLabelAttr(labelStateModel, 'distance') || 0;\n          var textAlign = getLabelAttr(labelStateModel, 'align');\n\n          if (labelPosition === 'outside') {\n            r = layout.r + labelPadding;\n            textAlign = midAngle > Math.PI / 2 ? 'right' : 'left';\n          } else {\n            if (!textAlign || textAlign === 'center') {\n              // Put label in the center if it's a circle\n              if (angle === 2 * Math.PI && layout.r0 === 0) {\n                r = 0;\n              } else {\n                r = (layout.r + layout.r0) / 2;\n              }\n\n              textAlign = 'center';\n            } else if (textAlign === 'left') {\n              r = layout.r0 + labelPadding;\n\n              if (midAngle > Math.PI / 2) {\n                textAlign = 'right';\n              }\n            } else if (textAlign === 'right') {\n              r = layout.r - labelPadding;\n\n              if (midAngle > Math.PI / 2) {\n                textAlign = 'left';\n              }\n            }\n          }\n\n          state.style.align = textAlign;\n          state.style.verticalAlign = getLabelAttr(labelStateModel, 'verticalAlign') || 'middle';\n          state.x = r * dx + layout.cx;\n          state.y = r * dy + layout.cy;\n          var rotateType = getLabelAttr(labelStateModel, 'rotate');\n          var rotate = 0;\n\n          if (rotateType === 'radial') {\n            rotate = -midAngle;\n\n            if (rotate < -Math.PI / 2) {\n              rotate += Math.PI;\n            }\n          } else if (rotateType === 'tangential') {\n            rotate = Math.PI / 2 - midAngle;\n\n            if (rotate > Math.PI / 2) {\n              rotate -= Math.PI;\n            } else if (rotate < -Math.PI / 2) {\n              rotate += Math.PI;\n            }\n          } else if (isNumber(rotateType)) {\n            rotate = rotateType * Math.PI / 180;\n          }\n\n          state.rotation = rotate;\n        });\n\n        function getLabelAttr(model, name) {\n          var stateAttr = model.get(name);\n\n          if (stateAttr == null) {\n            return normalLabelModel.get(name);\n          }\n\n          return stateAttr;\n        }\n\n        label.dirtyStyle();\n      };\n\n      return SunburstPiece;\n    }(Sector);\n\n    var ROOT_TO_NODE_ACTION = 'sunburstRootToNode';\n    var HIGHLIGHT_ACTION = 'sunburstHighlight';\n    var UNHIGHLIGHT_ACTION = 'sunburstUnhighlight';\n    function installSunburstAction(registers) {\n      registers.registerAction({\n        type: ROOT_TO_NODE_ACTION,\n        update: 'updateView'\n      }, function (payload, ecModel) {\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'sunburst',\n          query: payload\n        }, handleRootToNode);\n\n        function handleRootToNode(model, index) {\n          var targetInfo = retrieveTargetInfo(payload, [ROOT_TO_NODE_ACTION], model);\n\n          if (targetInfo) {\n            var originViewRoot = model.getViewRoot();\n\n            if (originViewRoot) {\n              payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) ? 'rollUp' : 'drillDown';\n            }\n\n            model.resetViewRoot(targetInfo.node);\n          }\n        }\n      });\n      registers.registerAction({\n        type: HIGHLIGHT_ACTION,\n        update: 'none'\n      }, function (payload, ecModel, api) {\n        // Clone\n        payload = extend({}, payload);\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'sunburst',\n          query: payload\n        }, handleHighlight);\n\n        function handleHighlight(model) {\n          var targetInfo = retrieveTargetInfo(payload, [HIGHLIGHT_ACTION], model);\n\n          if (targetInfo) {\n            payload.dataIndex = targetInfo.node.dataIndex;\n          }\n        }\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('sunburstHighlight', 'highlight');\n        } // Fast forward action\n\n\n        api.dispatchAction(extend(payload, {\n          type: 'highlight'\n        }));\n      });\n      registers.registerAction({\n        type: UNHIGHLIGHT_ACTION,\n        update: 'updateView'\n      }, function (payload, ecModel, api) {\n        payload = extend({}, payload);\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('sunburstUnhighlight', 'downplay');\n        }\n\n        api.dispatchAction(extend(payload, {\n          type: 'downplay'\n        }));\n      });\n    }\n\n    var SunburstView =\n    /** @class */\n    function (_super) {\n      __extends(SunburstView, _super);\n\n      function SunburstView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SunburstView.type;\n        return _this;\n      }\n\n      SunburstView.prototype.render = function (seriesModel, ecModel, api, // @ts-ignore\n      payload) {\n        var self = this;\n        this.seriesModel = seriesModel;\n        this.api = api;\n        this.ecModel = ecModel;\n        var data = seriesModel.getData();\n        var virtualRoot = data.tree.root;\n        var newRoot = seriesModel.getViewRoot();\n        var group = this.group;\n        var renderLabelForZeroData = seriesModel.get('renderLabelForZeroData');\n        var newChildren = [];\n        newRoot.eachNode(function (node) {\n          newChildren.push(node);\n        });\n        var oldChildren = this._oldChildren || [];\n        dualTravel(newChildren, oldChildren);\n        renderRollUp(virtualRoot, newRoot);\n\n        this._initEvents();\n\n        this._oldChildren = newChildren;\n\n        function dualTravel(newChildren, oldChildren) {\n          if (newChildren.length === 0 && oldChildren.length === 0) {\n            return;\n          }\n\n          new DataDiffer(oldChildren, newChildren, getKey, getKey).add(processNode).update(processNode).remove(curry(processNode, null)).execute();\n\n          function getKey(node) {\n            return node.getId();\n          }\n\n          function processNode(newIdx, oldIdx) {\n            var newNode = newIdx == null ? null : newChildren[newIdx];\n            var oldNode = oldIdx == null ? null : oldChildren[oldIdx];\n            doRenderNode(newNode, oldNode);\n          }\n        }\n\n        function doRenderNode(newNode, oldNode) {\n          if (!renderLabelForZeroData && newNode && !newNode.getValue()) {\n            // Not render data with value 0\n            newNode = null;\n          }\n\n          if (newNode !== virtualRoot && oldNode !== virtualRoot) {\n            if (oldNode && oldNode.piece) {\n              if (newNode) {\n                // Update\n                oldNode.piece.updateData(false, newNode, seriesModel, ecModel, api); // For tooltip\n\n                data.setItemGraphicEl(newNode.dataIndex, oldNode.piece);\n              } else {\n                // Remove\n                removeNode(oldNode);\n              }\n            } else if (newNode) {\n              // Add\n              var piece = new SunburstPiece(newNode, seriesModel, ecModel, api);\n              group.add(piece); // For tooltip\n\n              data.setItemGraphicEl(newNode.dataIndex, piece);\n            }\n          }\n        }\n\n        function removeNode(node) {\n          if (!node) {\n            return;\n          }\n\n          if (node.piece) {\n            group.remove(node.piece);\n            node.piece = null;\n          }\n        }\n\n        function renderRollUp(virtualRoot, viewRoot) {\n          if (viewRoot.depth > 0) {\n            // Render\n            if (self.virtualPiece) {\n              // Update\n              self.virtualPiece.updateData(false, virtualRoot, seriesModel, ecModel, api);\n            } else {\n              // Add\n              self.virtualPiece = new SunburstPiece(virtualRoot, seriesModel, ecModel, api);\n              group.add(self.virtualPiece);\n            } // TODO event scope\n\n\n            viewRoot.piece.off('click');\n            self.virtualPiece.on('click', function (e) {\n              self._rootToNode(viewRoot.parentNode);\n            });\n          } else if (self.virtualPiece) {\n            // Remove\n            group.remove(self.virtualPiece);\n            self.virtualPiece = null;\n          }\n        }\n      };\n      /**\n       * @private\n       */\n\n\n      SunburstView.prototype._initEvents = function () {\n        var _this = this;\n\n        this.group.off('click');\n        this.group.on('click', function (e) {\n          var targetFound = false;\n\n          var viewRoot = _this.seriesModel.getViewRoot();\n\n          viewRoot.eachNode(function (node) {\n            if (!targetFound && node.piece && node.piece === e.target) {\n              var nodeClick = node.getModel().get('nodeClick');\n\n              if (nodeClick === 'rootToNode') {\n                _this._rootToNode(node);\n              } else if (nodeClick === 'link') {\n                var itemModel = node.getModel();\n                var link = itemModel.get('link');\n\n                if (link) {\n                  var linkTarget = itemModel.get('target', true) || '_blank';\n                  windowOpen(link, linkTarget);\n                }\n              }\n\n              targetFound = true;\n            }\n          });\n        });\n      };\n      /**\n       * @private\n       */\n\n\n      SunburstView.prototype._rootToNode = function (node) {\n        if (node !== this.seriesModel.getViewRoot()) {\n          this.api.dispatchAction({\n            type: ROOT_TO_NODE_ACTION,\n            from: this.uid,\n            seriesId: this.seriesModel.id,\n            targetNode: node\n          });\n        }\n      };\n      /**\n       * @implement\n       */\n\n\n      SunburstView.prototype.containPoint = function (point, seriesModel) {\n        var treeRoot = seriesModel.getData();\n        var itemLayout = treeRoot.getItemLayout(0);\n\n        if (itemLayout) {\n          var dx = point[0] - itemLayout.cx;\n          var dy = point[1] - itemLayout.cy;\n          var radius = Math.sqrt(dx * dx + dy * dy);\n          return radius <= itemLayout.r && radius >= itemLayout.r0;\n        }\n      };\n\n      SunburstView.type = 'sunburst';\n      return SunburstView;\n    }(ChartView);\n\n    var SunburstSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(SunburstSeriesModel, _super);\n\n      function SunburstSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SunburstSeriesModel.type;\n        _this.ignoreStyleOnData = true;\n        return _this;\n      }\n\n      SunburstSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        // Create a virtual root.\n        var root = {\n          name: option.name,\n          children: option.data\n        };\n        completeTreeValue$1(root);\n        var levelModels = this._levelModels = map(option.levels || [], function (levelDefine) {\n          return new Model(levelDefine, this, ecModel);\n        }, this); // Make sure always a new tree is created when setOption,\n        // in TreemapView, we check whether oldTree === newTree\n        // to choose mappings approach among old shapes and new shapes.\n\n        var tree = Tree.createTree(root, this, beforeLink);\n\n        function beforeLink(nodeData) {\n          nodeData.wrapMethod('getItemModel', function (model, idx) {\n            var node = tree.getNodeByDataIndex(idx);\n            var levelModel = levelModels[node.depth];\n            levelModel && (model.parentModel = levelModel);\n            return model;\n          });\n        }\n\n        return tree.data;\n      };\n\n      SunburstSeriesModel.prototype.optionUpdated = function () {\n        this.resetViewRoot();\n      };\n      /*\n       * @override\n       */\n\n\n      SunburstSeriesModel.prototype.getDataParams = function (dataIndex) {\n        var params = _super.prototype.getDataParams.apply(this, arguments);\n\n        var node = this.getData().tree.getNodeByDataIndex(dataIndex);\n        params.treePathInfo = wrapTreePathInfo(node, this);\n        return params;\n      };\n\n      SunburstSeriesModel.prototype.getLevelModel = function (node) {\n        return this._levelModels && this._levelModels[node.depth];\n      };\n\n      SunburstSeriesModel.prototype.getViewRoot = function () {\n        return this._viewRoot;\n      };\n\n      SunburstSeriesModel.prototype.resetViewRoot = function (viewRoot) {\n        viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot;\n        var root = this.getRawData().tree.root;\n\n        if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) {\n          this._viewRoot = root;\n        }\n      };\n\n      SunburstSeriesModel.prototype.enableAriaDecal = function () {\n        enableAriaDecalForTree(this);\n      };\n\n      SunburstSeriesModel.type = 'series.sunburst';\n      SunburstSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        // 默认全局居中\n        center: ['50%', '50%'],\n        radius: [0, '75%'],\n        // 默认顺时针\n        clockwise: true,\n        startAngle: 90,\n        // 最小角度改为0\n        minAngle: 0,\n        // If still show when all data zero.\n        stillShowZeroSum: true,\n        // 'rootToNode', 'link', or false\n        nodeClick: 'rootToNode',\n        renderLabelForZeroData: false,\n        label: {\n          // could be: 'radial', 'tangential', or 'none'\n          rotate: 'radial',\n          show: true,\n          opacity: 1,\n          // 'left' is for inner side of inside, and 'right' is for outer\n          // side for inside\n          align: 'center',\n          position: 'inside',\n          distance: 5,\n          silent: true\n        },\n        itemStyle: {\n          borderWidth: 1,\n          borderColor: 'white',\n          borderType: 'solid',\n          shadowBlur: 0,\n          shadowColor: 'rgba(0, 0, 0, 0.2)',\n          shadowOffsetX: 0,\n          shadowOffsetY: 0,\n          opacity: 1\n        },\n        emphasis: {\n          focus: 'descendant'\n        },\n        blur: {\n          itemStyle: {\n            opacity: 0.2\n          },\n          label: {\n            opacity: 0.1\n          }\n        },\n        // Animation type can be expansion, scale.\n        animationType: 'expansion',\n        animationDuration: 1000,\n        animationDurationUpdate: 500,\n        data: [],\n\n        /**\n         * Sort order.\n         *\n         * Valid values: 'desc', 'asc', null, or callback function.\n         * 'desc' and 'asc' for descend and ascendant order;\n         * null for not sorting;\n         * example of callback function:\n         * function(nodeA, nodeB) {\n         *     return nodeA.getValue() - nodeB.getValue();\n         * }\n         */\n        sort: 'desc'\n      };\n      return SunburstSeriesModel;\n    }(SeriesModel);\n\n    function completeTreeValue$1(dataNode) {\n      // Postorder travel tree.\n      // If value of none-leaf node is not set,\n      // calculate it by suming up the value of all children.\n      var sum = 0;\n      each(dataNode.children, function (child) {\n        completeTreeValue$1(child);\n        var childValue = child.value; // TODO First value of array must be a number\n\n        isArray(childValue) && (childValue = childValue[0]);\n        sum += childValue;\n      });\n      var thisValue = dataNode.value;\n\n      if (isArray(thisValue)) {\n        thisValue = thisValue[0];\n      }\n\n      if (thisValue == null || isNaN(thisValue)) {\n        thisValue = sum;\n      } // Value should not less than 0.\n\n\n      if (thisValue < 0) {\n        thisValue = 0;\n      }\n\n      isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue;\n    }\n\n    var RADIAN$2 = Math.PI / 180;\n    function sunburstLayout(seriesType, ecModel, api) {\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        var center = seriesModel.get('center');\n        var radius = seriesModel.get('radius');\n\n        if (!isArray(radius)) {\n          radius = [0, radius];\n        }\n\n        if (!isArray(center)) {\n          center = [center, center];\n        }\n\n        var width = api.getWidth();\n        var height = api.getHeight();\n        var size = Math.min(width, height);\n        var cx = parsePercent$1(center[0], width);\n        var cy = parsePercent$1(center[1], height);\n        var r0 = parsePercent$1(radius[0], size / 2);\n        var r = parsePercent$1(radius[1], size / 2);\n        var startAngle = -seriesModel.get('startAngle') * RADIAN$2;\n        var minAngle = seriesModel.get('minAngle') * RADIAN$2;\n        var virtualRoot = seriesModel.getData().tree.root;\n        var treeRoot = seriesModel.getViewRoot();\n        var rootDepth = treeRoot.depth;\n        var sort = seriesModel.get('sort');\n\n        if (sort != null) {\n          initChildren$1(treeRoot, sort);\n        }\n\n        var validDataCount = 0;\n        each(treeRoot.children, function (child) {\n          !isNaN(child.getValue()) && validDataCount++;\n        });\n        var sum = treeRoot.getValue(); // Sum may be 0\n\n        var unitRadian = Math.PI / (sum || validDataCount) * 2;\n        var renderRollupNode = treeRoot.depth > 0;\n        var levels = treeRoot.height - (renderRollupNode ? -1 : 1);\n        var rPerLevel = (r - r0) / (levels || 1);\n        var clockwise = seriesModel.get('clockwise');\n        var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); // In the case some sector angle is smaller than minAngle\n        // let restAngle = PI2;\n        // let valueSumLargerThanMinAngle = 0;\n\n        var dir = clockwise ? 1 : -1;\n        /**\n         * Render a tree\n         * @return increased angle\n         */\n\n        var renderNode = function (node, startAngle) {\n          if (!node) {\n            return;\n          }\n\n          var endAngle = startAngle; // Render self\n\n          if (node !== virtualRoot) {\n            // Tree node is virtual, so it doesn't need to be drawn\n            var value = node.getValue();\n            var angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian;\n\n            if (angle < minAngle) {\n              angle = minAngle; // restAngle -= minAngle;\n            } // else {\n            //     valueSumLargerThanMinAngle += value;\n            // }\n\n\n            endAngle = startAngle + dir * angle;\n            var depth = node.depth - rootDepth - (renderRollupNode ? -1 : 1);\n            var rStart = r0 + rPerLevel * depth;\n            var rEnd = r0 + rPerLevel * (depth + 1);\n            var levelModel = seriesModel.getLevelModel(node);\n\n            if (levelModel) {\n              var r0_1 = levelModel.get('r0', true);\n              var r_1 = levelModel.get('r', true);\n              var radius_1 = levelModel.get('radius', true);\n\n              if (radius_1 != null) {\n                r0_1 = radius_1[0];\n                r_1 = radius_1[1];\n              }\n\n              r0_1 != null && (rStart = parsePercent$1(r0_1, size / 2));\n              r_1 != null && (rEnd = parsePercent$1(r_1, size / 2));\n            }\n\n            node.setLayout({\n              angle: angle,\n              startAngle: startAngle,\n              endAngle: endAngle,\n              clockwise: clockwise,\n              cx: cx,\n              cy: cy,\n              r0: rStart,\n              r: rEnd\n            });\n          } // Render children\n\n\n          if (node.children && node.children.length) {\n            // currentAngle = startAngle;\n            var siblingAngle_1 = 0;\n            each(node.children, function (node) {\n              siblingAngle_1 += renderNode(node, startAngle + siblingAngle_1);\n            });\n          }\n\n          return endAngle - startAngle;\n        }; // Virtual root node for roll up\n\n\n        if (renderRollupNode) {\n          var rStart = r0;\n          var rEnd = r0 + rPerLevel;\n          var angle = Math.PI * 2;\n          virtualRoot.setLayout({\n            angle: angle,\n            startAngle: startAngle,\n            endAngle: startAngle + angle,\n            clockwise: clockwise,\n            cx: cx,\n            cy: cy,\n            r0: rStart,\n            r: rEnd\n          });\n        }\n\n        renderNode(treeRoot, startAngle);\n      });\n    }\n    /**\n     * Init node children by order and update visual\n     */\n\n    function initChildren$1(node, sortOrder) {\n      var children = node.children || [];\n      node.children = sort$2(children, sortOrder); // Init children recursively\n\n      if (children.length) {\n        each(node.children, function (child) {\n          initChildren$1(child, sortOrder);\n        });\n      }\n    }\n    /**\n     * Sort children nodes\n     *\n     * @param {TreeNode[]}               children children of node to be sorted\n     * @param {string | function | null} sort sort method\n     *                                   See SunburstSeries.js for details.\n     */\n\n\n    function sort$2(children, sortOrder) {\n      if (isFunction(sortOrder)) {\n        var sortTargets = map(children, function (child, idx) {\n          var value = child.getValue();\n          return {\n            params: {\n              depth: child.depth,\n              height: child.height,\n              dataIndex: child.dataIndex,\n              getValue: function () {\n                return value;\n              }\n            },\n            index: idx\n          };\n        });\n        sortTargets.sort(function (a, b) {\n          return sortOrder(a.params, b.params);\n        });\n        return map(sortTargets, function (target) {\n          return children[target.index];\n        });\n      } else {\n        var isAsc_1 = sortOrder === 'asc';\n        return children.sort(function (a, b) {\n          var diff = (a.getValue() - b.getValue()) * (isAsc_1 ? 1 : -1);\n          return diff === 0 ? (a.dataIndex - b.dataIndex) * (isAsc_1 ? -1 : 1) : diff;\n        });\n      }\n    }\n\n    function sunburstVisual(ecModel) {\n      var paletteScope = {}; // Default color strategy\n\n      function pickColor(node, seriesModel, treeHeight) {\n        // Choose color from palette based on the first level.\n        var current = node;\n\n        while (current && current.depth > 1) {\n          current = current.parentNode;\n        }\n\n        var color = seriesModel.getColorFromPalette(current.name || current.dataIndex + '', paletteScope);\n\n        if (node.depth > 1 && isString(color)) {\n          // Lighter on the deeper level.\n          color = lift(color, (node.depth - 1) / (treeHeight - 1) * 0.5);\n        }\n\n        return color;\n      }\n\n      ecModel.eachSeriesByType('sunburst', function (seriesModel) {\n        var data = seriesModel.getData();\n        var tree = data.tree;\n        tree.eachNode(function (node) {\n          var model = node.getModel();\n          var style = model.getModel('itemStyle').getItemStyle();\n\n          if (!style.fill) {\n            style.fill = pickColor(node, seriesModel, tree.root.height);\n          }\n\n          var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style');\n          extend(existsStyle, style);\n        });\n      });\n    }\n\n    function install$q(registers) {\n      registers.registerChartView(SunburstView);\n      registers.registerSeriesModel(SunburstSeriesModel);\n      registers.registerLayout(curry(sunburstLayout, 'sunburst'));\n      registers.registerProcessor(curry(dataFilter, 'sunburst'));\n      registers.registerVisual(sunburstVisual);\n      installSunburstAction(registers);\n    }\n\n    // `visual('color') visual('borderColor')` is supported.\n\n    var STYLE_VISUAL_TYPE = {\n      color: 'fill',\n      borderColor: 'stroke'\n    };\n    var NON_STYLE_VISUAL_PROPS = {\n      symbol: 1,\n      symbolSize: 1,\n      symbolKeepAspect: 1,\n      legendIcon: 1,\n      visualMeta: 1,\n      liftZ: 1,\n      decal: 1\n    };\n    var customInnerStore = makeInner();\n\n    var CustomSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(CustomSeriesModel, _super);\n\n      function CustomSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CustomSeriesModel.type;\n        return _this;\n      }\n\n      CustomSeriesModel.prototype.optionUpdated = function () {\n        this.currentZLevel = this.get('zlevel', true);\n        this.currentZ = this.get('z', true);\n      };\n\n      CustomSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this);\n      };\n\n      CustomSeriesModel.prototype.getDataParams = function (dataIndex, dataType, el) {\n        var params = _super.prototype.getDataParams.call(this, dataIndex, dataType);\n\n        el && (params.info = customInnerStore(el).info);\n        return params;\n      };\n\n      CustomSeriesModel.type = 'series.custom';\n      CustomSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];\n      CustomSeriesModel.defaultOption = {\n        coordinateSystem: 'cartesian2d',\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        // Custom series will not clip by default.\n        // Some case will use custom series to draw label\n        // For example https://echarts.apache.org/examples/en/editor.html?c=custom-gantt-flight\n        clip: false // Cartesian coordinate system\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        // Polar coordinate system\n        // polarIndex: 0,\n        // Geo coordinate system\n        // geoIndex: 0,\n\n      };\n      return CustomSeriesModel;\n    }(SeriesModel);\n\n    function dataToCoordSize(dataSize, dataItem) {\n      // dataItem is necessary in log axis.\n      dataItem = dataItem || [0, 0];\n      return map(['x', 'y'], function (dim, dimIdx) {\n        var axis = this.getAxis(dim);\n        var val = dataItem[dimIdx];\n        var halfSize = dataSize[dimIdx] / 2;\n        return axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));\n      }, this);\n    }\n\n    function cartesianPrepareCustom(coordSys) {\n      var rect = coordSys.master.getRect();\n      return {\n        coordSys: {\n          // The name exposed to user is always 'cartesian2d' but not 'grid'.\n          type: 'cartesian2d',\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height\n        },\n        api: {\n          coord: function (data) {\n            // do not provide \"out\" param\n            return coordSys.dataToPoint(data);\n          },\n          size: bind(dataToCoordSize, coordSys)\n        }\n      };\n    }\n\n    function dataToCoordSize$1(dataSize, dataItem) {\n      dataItem = dataItem || [0, 0];\n      return map([0, 1], function (dimIdx) {\n        var val = dataItem[dimIdx];\n        var halfSize = dataSize[dimIdx] / 2;\n        var p1 = [];\n        var p2 = [];\n        p1[dimIdx] = val - halfSize;\n        p2[dimIdx] = val + halfSize;\n        p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx];\n        return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]);\n      }, this);\n    }\n\n    function geoPrepareCustom(coordSys) {\n      var rect = coordSys.getBoundingRect();\n      return {\n        coordSys: {\n          type: 'geo',\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height,\n          zoom: coordSys.getZoom()\n        },\n        api: {\n          coord: function (data) {\n            // do not provide \"out\" and noRoam param,\n            // Compatible with this usage:\n            // echarts.util.map(item.points, api.coord)\n            return coordSys.dataToPoint(data);\n          },\n          size: bind(dataToCoordSize$1, coordSys)\n        }\n      };\n    }\n\n    function dataToCoordSize$2(dataSize, dataItem) {\n      // dataItem is necessary in log axis.\n      var axis = this.getAxis();\n      var val = dataItem instanceof Array ? dataItem[0] : dataItem;\n      var halfSize = (dataSize instanceof Array ? dataSize[0] : dataSize) / 2;\n      return axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));\n    }\n\n    function singlePrepareCustom(coordSys) {\n      var rect = coordSys.getRect();\n      return {\n        coordSys: {\n          type: 'singleAxis',\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height\n        },\n        api: {\n          coord: function (val) {\n            // do not provide \"out\" param\n            return coordSys.dataToPoint(val);\n          },\n          size: bind(dataToCoordSize$2, coordSys)\n        }\n      };\n    }\n\n    function dataToCoordSize$3(dataSize, dataItem) {\n      // dataItem is necessary in log axis.\n      dataItem = dataItem || [0, 0];\n      return map(['Radius', 'Angle'], function (dim, dimIdx) {\n        var getterName = 'get' + dim + 'Axis'; // TODO: TYPE Check Angle Axis\n\n        var axis = this[getterName]();\n        var val = dataItem[dimIdx];\n        var halfSize = dataSize[dimIdx] / 2;\n        var result = axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));\n\n        if (dim === 'Angle') {\n          result = result * Math.PI / 180;\n        }\n\n        return result;\n      }, this);\n    }\n\n    function polarPrepareCustom(coordSys) {\n      var radiusAxis = coordSys.getRadiusAxis();\n      var angleAxis = coordSys.getAngleAxis();\n      var radius = radiusAxis.getExtent();\n      radius[0] > radius[1] && radius.reverse();\n      return {\n        coordSys: {\n          type: 'polar',\n          cx: coordSys.cx,\n          cy: coordSys.cy,\n          r: radius[1],\n          r0: radius[0]\n        },\n        api: {\n          coord: function (data) {\n            var radius = radiusAxis.dataToRadius(data[0]);\n            var angle = angleAxis.dataToAngle(data[1]);\n            var coord = coordSys.coordToPoint([radius, angle]);\n            coord.push(radius, angle * Math.PI / 180);\n            return coord;\n          },\n          size: bind(dataToCoordSize$3, coordSys)\n        }\n      };\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function calendarPrepareCustom(coordSys) {\n      var rect = coordSys.getRect();\n      var rangeInfo = coordSys.getRangeInfo();\n      return {\n        coordSys: {\n          type: 'calendar',\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height,\n          cellWidth: coordSys.getCellWidth(),\n          cellHeight: coordSys.getCellHeight(),\n          rangeInfo: {\n            start: rangeInfo.start,\n            end: rangeInfo.end,\n            weeks: rangeInfo.weeks,\n            dayCount: rangeInfo.allDay\n          }\n        },\n        api: {\n          coord: function (data, clamp) {\n            return coordSys.dataToPoint(data, clamp);\n          }\n        }\n      };\n    }\n\n    var deprecatedLogs = {};\n    /**\n     * Whether need to call `convertEC4CompatibleStyle`.\n     */\n\n    function isEC4CompatibleStyle(style, elType, hasOwnTextContentOption, hasOwnTextConfig) {\n      // Since echarts5, `RectText` is separated from its host element and style.text\n      // does not exist any more. The compat work brings some extra burden on performance.\n      // So we provide:\n      // `legacy: true` force make compat.\n      // `legacy: false`, force do not compat.\n      // `legacy` not set: auto detect whether legacy.\n      //     But in this case we do not compat (difficult to detect and rare case):\n      //     Becuse custom series and graphic component support \"merge\", users may firstly\n      //     only set `textStrokeWidth` style or secondly only set `text`.\n      return style && (style.legacy || style.legacy !== false && !hasOwnTextContentOption && !hasOwnTextConfig && elType !== 'tspan' // Difficult to detect whether legacy for a \"text\" el.\n      && (elType === 'text' || hasOwn(style, 'text')));\n    }\n    /**\n     * `EC4CompatibleStyle` is style that might be in echarts4 format or echarts5 format.\n     * @param hostStyle The properties might be modified.\n     * @return If be text el, `textContentStyle` and `textConfig` will not be returned.\n     *         Otherwise a `textContentStyle` and `textConfig` will be created, whose props area\n     *         retried from the `hostStyle`.\n     */\n\n    function convertFromEC4CompatibleStyle(hostStyle, elType, isNormal) {\n      var srcStyle = hostStyle;\n      var textConfig;\n      var textContent;\n      var textContentStyle;\n\n      if (elType === 'text') {\n        textContentStyle = srcStyle;\n      } else {\n        textContentStyle = {};\n        hasOwn(srcStyle, 'text') && (textContentStyle.text = srcStyle.text);\n        hasOwn(srcStyle, 'rich') && (textContentStyle.rich = srcStyle.rich);\n        hasOwn(srcStyle, 'textFill') && (textContentStyle.fill = srcStyle.textFill);\n        hasOwn(srcStyle, 'textStroke') && (textContentStyle.stroke = srcStyle.textStroke);\n        hasOwn(srcStyle, 'fontFamily') && (textContentStyle.fontFamily = srcStyle.fontFamily);\n        hasOwn(srcStyle, 'fontSize') && (textContentStyle.fontSize = srcStyle.fontSize);\n        hasOwn(srcStyle, 'fontStyle') && (textContentStyle.fontStyle = srcStyle.fontStyle);\n        hasOwn(srcStyle, 'fontWeight') && (textContentStyle.fontWeight = srcStyle.fontWeight);\n        textContent = {\n          type: 'text',\n          style: textContentStyle,\n          // ec4 does not support rectText trigger.\n          // And when text position is different in normal and emphasis\n          // => hover text trigger emphasis;\n          // => text position changed, leave mouse pointer immediately;\n          // That might cause incorrect state.\n          silent: true\n        };\n        textConfig = {};\n        var hasOwnPos = hasOwn(srcStyle, 'textPosition');\n\n        if (isNormal) {\n          textConfig.position = hasOwnPos ? srcStyle.textPosition : 'inside';\n        } else {\n          hasOwnPos && (textConfig.position = srcStyle.textPosition);\n        }\n\n        hasOwn(srcStyle, 'textPosition') && (textConfig.position = srcStyle.textPosition);\n        hasOwn(srcStyle, 'textOffset') && (textConfig.offset = srcStyle.textOffset);\n        hasOwn(srcStyle, 'textRotation') && (textConfig.rotation = srcStyle.textRotation);\n        hasOwn(srcStyle, 'textDistance') && (textConfig.distance = srcStyle.textDistance);\n      }\n\n      convertEC4CompatibleRichItem(textContentStyle, hostStyle);\n      each(textContentStyle.rich, function (richItem) {\n        convertEC4CompatibleRichItem(richItem, richItem);\n      });\n      return {\n        textConfig: textConfig,\n        textContent: textContent\n      };\n    }\n    /**\n     * The result will be set to `out`.\n     */\n\n    function convertEC4CompatibleRichItem(out, richItem) {\n      if (!richItem) {\n        return;\n      } // (1) For simplicity, make textXXX properties (deprecated since ec5) has\n      // higher priority. For example, consider in ec4 `borderColor: 5, textBorderColor: 10`\n      // on a rect means `borderColor: 4` on the rect and `borderColor: 10` on an attached\n      // richText in ec5.\n      // (2) `out === richItem` if and only if `out` is text el or rich item.\n      // So we can overwrite existing props in `out` since textXXX has higher priority.\n\n\n      richItem.font = richItem.textFont || richItem.font;\n      hasOwn(richItem, 'textStrokeWidth') && (out.lineWidth = richItem.textStrokeWidth);\n      hasOwn(richItem, 'textAlign') && (out.align = richItem.textAlign);\n      hasOwn(richItem, 'textVerticalAlign') && (out.verticalAlign = richItem.textVerticalAlign);\n      hasOwn(richItem, 'textLineHeight') && (out.lineHeight = richItem.textLineHeight);\n      hasOwn(richItem, 'textWidth') && (out.width = richItem.textWidth);\n      hasOwn(richItem, 'textHeight') && (out.height = richItem.textHeight);\n      hasOwn(richItem, 'textBackgroundColor') && (out.backgroundColor = richItem.textBackgroundColor);\n      hasOwn(richItem, 'textPadding') && (out.padding = richItem.textPadding);\n      hasOwn(richItem, 'textBorderColor') && (out.borderColor = richItem.textBorderColor);\n      hasOwn(richItem, 'textBorderWidth') && (out.borderWidth = richItem.textBorderWidth);\n      hasOwn(richItem, 'textBorderRadius') && (out.borderRadius = richItem.textBorderRadius);\n      hasOwn(richItem, 'textBoxShadowColor') && (out.shadowColor = richItem.textBoxShadowColor);\n      hasOwn(richItem, 'textBoxShadowBlur') && (out.shadowBlur = richItem.textBoxShadowBlur);\n      hasOwn(richItem, 'textBoxShadowOffsetX') && (out.shadowOffsetX = richItem.textBoxShadowOffsetX);\n      hasOwn(richItem, 'textBoxShadowOffsetY') && (out.shadowOffsetY = richItem.textBoxShadowOffsetY);\n    }\n    /**\n     * Convert to pure echarts4 format style.\n     * `itemStyle` will be modified, added with ec4 style properties from\n     * `textStyle` and `textConfig`.\n     *\n     * [Caveat]: For simplicity, `insideRollback` in ec4 does not compat, where\n     * `styleEmphasis: {textFill: 'red'}` will remove the normal auto added stroke.\n     */\n\n\n    function convertToEC4StyleForCustomSerise(itemStl, txStl, txCfg) {\n      var out = itemStl; // See `custom.ts`, a trick to set extra `textPosition` firstly.\n\n      out.textPosition = out.textPosition || txCfg.position || 'inside';\n      txCfg.offset != null && (out.textOffset = txCfg.offset);\n      txCfg.rotation != null && (out.textRotation = txCfg.rotation);\n      txCfg.distance != null && (out.textDistance = txCfg.distance);\n      var isInside = out.textPosition.indexOf('inside') >= 0;\n      var hostFill = itemStl.fill || '#000';\n      convertToEC4RichItem(out, txStl);\n      var textFillNotSet = out.textFill == null;\n\n      if (isInside) {\n        if (textFillNotSet) {\n          out.textFill = txCfg.insideFill || '#fff';\n          !out.textStroke && txCfg.insideStroke && (out.textStroke = txCfg.insideStroke);\n          !out.textStroke && (out.textStroke = hostFill);\n          out.textStrokeWidth == null && (out.textStrokeWidth = 2);\n        }\n      } else {\n        if (textFillNotSet) {\n          out.textFill = itemStl.fill || txCfg.outsideFill || '#000';\n        }\n\n        !out.textStroke && txCfg.outsideStroke && (out.textStroke = txCfg.outsideStroke);\n      }\n\n      out.text = txStl.text;\n      out.rich = txStl.rich;\n      each(txStl.rich, function (richItem) {\n        convertToEC4RichItem(richItem, richItem);\n      });\n      return out;\n    }\n\n    function convertToEC4RichItem(out, richItem) {\n      if (!richItem) {\n        return;\n      }\n\n      hasOwn(richItem, 'fill') && (out.textFill = richItem.fill);\n      hasOwn(richItem, 'stroke') && (out.textStroke = richItem.fill);\n      hasOwn(richItem, 'lineWidth') && (out.textStrokeWidth = richItem.lineWidth);\n      hasOwn(richItem, 'font') && (out.font = richItem.font);\n      hasOwn(richItem, 'fontStyle') && (out.fontStyle = richItem.fontStyle);\n      hasOwn(richItem, 'fontWeight') && (out.fontWeight = richItem.fontWeight);\n      hasOwn(richItem, 'fontSize') && (out.fontSize = richItem.fontSize);\n      hasOwn(richItem, 'fontFamily') && (out.fontFamily = richItem.fontFamily);\n      hasOwn(richItem, 'align') && (out.textAlign = richItem.align);\n      hasOwn(richItem, 'verticalAlign') && (out.textVerticalAlign = richItem.verticalAlign);\n      hasOwn(richItem, 'lineHeight') && (out.textLineHeight = richItem.lineHeight);\n      hasOwn(richItem, 'width') && (out.textWidth = richItem.width);\n      hasOwn(richItem, 'height') && (out.textHeight = richItem.height);\n      hasOwn(richItem, 'backgroundColor') && (out.textBackgroundColor = richItem.backgroundColor);\n      hasOwn(richItem, 'padding') && (out.textPadding = richItem.padding);\n      hasOwn(richItem, 'borderColor') && (out.textBorderColor = richItem.borderColor);\n      hasOwn(richItem, 'borderWidth') && (out.textBorderWidth = richItem.borderWidth);\n      hasOwn(richItem, 'borderRadius') && (out.textBorderRadius = richItem.borderRadius);\n      hasOwn(richItem, 'shadowColor') && (out.textBoxShadowColor = richItem.shadowColor);\n      hasOwn(richItem, 'shadowBlur') && (out.textBoxShadowBlur = richItem.shadowBlur);\n      hasOwn(richItem, 'shadowOffsetX') && (out.textBoxShadowOffsetX = richItem.shadowOffsetX);\n      hasOwn(richItem, 'shadowOffsetY') && (out.textBoxShadowOffsetY = richItem.shadowOffsetY);\n      hasOwn(richItem, 'textShadowColor') && (out.textShadowColor = richItem.textShadowColor);\n      hasOwn(richItem, 'textShadowBlur') && (out.textShadowBlur = richItem.textShadowBlur);\n      hasOwn(richItem, 'textShadowOffsetX') && (out.textShadowOffsetX = richItem.textShadowOffsetX);\n      hasOwn(richItem, 'textShadowOffsetY') && (out.textShadowOffsetY = richItem.textShadowOffsetY);\n    }\n\n    function warnDeprecated(deprecated, insteadApproach) {\n      if (\"development\" !== 'production') {\n        var key = deprecated + '^_^' + insteadApproach;\n\n        if (!deprecatedLogs[key]) {\n          console.warn(\"[ECharts] DEPRECATED: \\\"\" + deprecated + \"\\\" has been deprecated. \" + insteadApproach);\n          deprecatedLogs[key] = true;\n        }\n      }\n    }\n\n    var LEGACY_TRANSFORM_PROPS_MAP = {\n      position: ['x', 'y'],\n      scale: ['scaleX', 'scaleY'],\n      origin: ['originX', 'originY']\n    };\n    var LEGACY_TRANSFORM_PROPS = keys(LEGACY_TRANSFORM_PROPS_MAP);\n    var TRANSFORM_PROPS_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {\n      obj[key] = 1;\n      return obj;\n    }, {});\n    var transformPropNamesStr = TRANSFORMABLE_PROPS.join(', '); // '' means root\n\n    var ELEMENT_ANIMATABLE_PROPS = ['', 'style', 'shape', 'extra'];\n    var transitionInnerStore = makeInner();\n\n    function getElementAnimationConfig(animationType, el, elOption, parentModel, dataIndex) {\n      var animationProp = animationType + \"Animation\";\n      var config = getAnimationConfig(animationType, parentModel, dataIndex) || {};\n      var userDuring = transitionInnerStore(el).userDuring; // Only set when duration is > 0 and it's need to be animated.\n\n      if (config.duration > 0) {\n        // For simplicity, if during not specified, the previous during will not work any more.\n        config.during = userDuring ? bind(duringCall, {\n          el: el,\n          userDuring: userDuring\n        }) : null;\n        config.setToFinal = true;\n        config.scope = animationType;\n      }\n\n      extend(config, elOption[animationProp]);\n      return config;\n    }\n\n    function applyUpdateTransition(el, elOption, animatableModel, opts) {\n      opts = opts || {};\n      var dataIndex = opts.dataIndex,\n          isInit = opts.isInit,\n          clearStyle = opts.clearStyle;\n      var hasAnimation = animatableModel.isAnimationEnabled(); // Save the meta info for further morphing. Like apply on the sub morphing elements.\n\n      var store = transitionInnerStore(el);\n      var styleOpt = elOption.style;\n      store.userDuring = elOption.during;\n      var transFromProps = {};\n      var propsToSet = {};\n      prepareTransformAllPropsFinal(el, elOption, propsToSet);\n      prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet);\n      prepareShapeOrExtraAllPropsFinal('extra', elOption, propsToSet);\n\n      if (!isInit && hasAnimation) {\n        prepareTransformTransitionFrom(el, elOption, transFromProps);\n        prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps);\n        prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps);\n        prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps);\n      }\n\n      propsToSet.style = styleOpt;\n      applyPropsDirectly(el, propsToSet, clearStyle);\n      applyMiscProps(el, elOption);\n\n      if (hasAnimation) {\n        if (isInit) {\n          var enterFromProps_1 = {};\n          each(ELEMENT_ANIMATABLE_PROPS, function (propName) {\n            var prop = propName ? elOption[propName] : elOption;\n\n            if (prop && prop.enterFrom) {\n              if (propName) {\n                enterFromProps_1[propName] = enterFromProps_1[propName] || {};\n              }\n\n              extend(propName ? enterFromProps_1[propName] : enterFromProps_1, prop.enterFrom);\n            }\n          });\n          var config = getElementAnimationConfig('enter', el, elOption, animatableModel, dataIndex);\n\n          if (config.duration > 0) {\n            el.animateFrom(enterFromProps_1, config);\n          }\n        } else {\n          applyPropsTransition(el, elOption, dataIndex || 0, animatableModel, transFromProps);\n        }\n      } // Store leave to be used in leave transition.\n\n\n      updateLeaveTo(el, elOption);\n      styleOpt ? el.dirty() : el.markRedraw();\n    }\n    function updateLeaveTo(el, elOption) {\n      // Try merge to previous set leaveTo\n      var leaveToProps = transitionInnerStore(el).leaveToProps;\n\n      for (var i = 0; i < ELEMENT_ANIMATABLE_PROPS.length; i++) {\n        var propName = ELEMENT_ANIMATABLE_PROPS[i];\n        var prop = propName ? elOption[propName] : elOption;\n\n        if (prop && prop.leaveTo) {\n          if (!leaveToProps) {\n            leaveToProps = transitionInnerStore(el).leaveToProps = {};\n          }\n\n          if (propName) {\n            leaveToProps[propName] = leaveToProps[propName] || {};\n          }\n\n          extend(propName ? leaveToProps[propName] : leaveToProps, prop.leaveTo);\n        }\n      }\n    }\n    function applyLeaveTransition(el, elOption, animatableModel, onRemove) {\n      if (el) {\n        var parent_1 = el.parent;\n        var leaveToProps = transitionInnerStore(el).leaveToProps;\n\n        if (leaveToProps) {\n          // TODO TODO use leave after leaveAnimation in series is introduced\n          // TODO Data index?\n          var config = getElementAnimationConfig('update', el, elOption, animatableModel, 0);\n\n          config.done = function () {\n            parent_1.remove(el);\n            onRemove && onRemove();\n          };\n\n          el.animateTo(leaveToProps, config);\n        } else {\n          parent_1.remove(el);\n          onRemove && onRemove();\n        }\n      }\n    }\n    function isTransitionAll(transition) {\n      return transition === 'all';\n    }\n\n    function applyPropsDirectly(el, // Can be null/undefined\n    allPropsFinal, clearStyle) {\n      var styleOpt = allPropsFinal.style;\n\n      if (!el.isGroup && styleOpt) {\n        if (clearStyle) {\n          el.useStyle({}); // When style object changed, how to trade the existing animation?\n          // It is probably complicated and not needed to cover all the cases.\n          // But still need consider the case:\n          // (1) When using init animation on `style.opacity`, and before the animation\n          //     ended users triggers an update by mousewhel. At that time the init\n          //     animation should better be continued rather than terminated.\n          //     So after `useStyle` called, we should change the animation target manually\n          //     to continue the effect of the init animation.\n          // (2) PENDING: If the previous animation targeted at a `val1`, and currently we need\n          //     to update the value to `val2` and no animation declared, should be terminate\n          //     the previous animation or just modify the target of the animation?\n          //     Therotically That will happen not only on `style` but also on `shape` and\n          //     `transfrom` props. But we haven't handle this case at present yet.\n          // (3) PENDING: Is it proper to visit `animators` and `targetName`?\n\n          var animators = el.animators;\n\n          for (var i = 0; i < animators.length; i++) {\n            var animator = animators[i]; // targetName is the \"topKey\".\n\n            if (animator.targetName === 'style') {\n              animator.changeTarget(el.style);\n            }\n          }\n        }\n\n        el.setStyle(styleOpt);\n      }\n\n      if (allPropsFinal) {\n        // Not set style here.\n        allPropsFinal.style = null; // Set el to the final state firstly.\n\n        allPropsFinal && el.attr(allPropsFinal);\n        allPropsFinal.style = styleOpt;\n      }\n    }\n\n    function applyPropsTransition(el, elOption, dataIndex, model, // Can be null/undefined\n    transFromProps) {\n      if (transFromProps) {\n        var config = getElementAnimationConfig('update', el, elOption, model, dataIndex);\n\n        if (config.duration > 0) {\n          el.animateFrom(transFromProps, config);\n        }\n      }\n    }\n\n    function applyMiscProps(el, elOption) {\n      // Merge by default.\n      hasOwn(elOption, 'silent') && (el.silent = elOption.silent);\n      hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore);\n\n      if (el instanceof Displayable) {\n        hasOwn(elOption, 'invisible') && (el.invisible = elOption.invisible);\n      }\n\n      if (el instanceof Path) {\n        hasOwn(elOption, 'autoBatch') && (el.autoBatch = elOption.autoBatch);\n      }\n    } // Use it to avoid it be exposed to user.\n\n\n    var tmpDuringScope = {};\n    var transitionDuringAPI = {\n      // Usually other props do not need to be changed in animation during.\n      setTransform: function (key, val) {\n        if (\"development\" !== 'production') {\n          assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `setTransform`.');\n        }\n\n        tmpDuringScope.el[key] = val;\n        return this;\n      },\n      getTransform: function (key) {\n        if (\"development\" !== 'production') {\n          assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `getTransform`.');\n        }\n\n        return tmpDuringScope.el[key];\n      },\n      setShape: function (key, val) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var el = tmpDuringScope.el;\n        var shape = el.shape || (el.shape = {});\n        shape[key] = val;\n        el.dirtyShape && el.dirtyShape();\n        return this;\n      },\n      getShape: function (key) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var shape = tmpDuringScope.el.shape;\n\n        if (shape) {\n          return shape[key];\n        }\n      },\n      setStyle: function (key, val) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var el = tmpDuringScope.el;\n        var style = el.style;\n\n        if (style) {\n          if (\"development\" !== 'production') {\n            if (eqNaN(val)) {\n              warn('style.' + key + ' must not be assigned with NaN.');\n            }\n          }\n\n          style[key] = val;\n          el.dirtyStyle && el.dirtyStyle();\n        }\n\n        return this;\n      },\n      getStyle: function (key) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var style = tmpDuringScope.el.style;\n\n        if (style) {\n          return style[key];\n        }\n      },\n      setExtra: function (key, val) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var extra = tmpDuringScope.el.extra || (tmpDuringScope.el.extra = {});\n        extra[key] = val;\n        return this;\n      },\n      getExtra: function (key) {\n        if (\"development\" !== 'production') {\n          assertNotReserved(key);\n        }\n\n        var extra = tmpDuringScope.el.extra;\n\n        if (extra) {\n          return extra[key];\n        }\n      }\n    };\n\n    function assertNotReserved(key) {\n      if (\"development\" !== 'production') {\n        if (key === 'transition' || key === 'enterFrom' || key === 'leaveTo') {\n          throw new Error('key must not be \"' + key + '\"');\n        }\n      }\n    }\n\n    function duringCall() {\n      // Do not provide \"percent\" until some requirements come.\n      // Because consider thies case:\n      // enterFrom: {x: 100, y: 30}, transition: 'x'.\n      // And enter duration is different from update duration.\n      // Thus it might be confused about the meaning of \"percent\" in during callback.\n      var scope = this;\n      var el = scope.el;\n\n      if (!el) {\n        return;\n      } // If el is remove from zr by reason like legend, during still need to called,\n      // because el will be added back to zr and the prop value should not be incorrect.\n\n\n      var latestUserDuring = transitionInnerStore(el).userDuring;\n      var scopeUserDuring = scope.userDuring; // Ensured a during is only called once in each animation frame.\n      // If a during is called multiple times in one frame, maybe some users' calculation logic\n      // might be wrong (not sure whether this usage exists).\n      // The case of a during might be called twice can be: by default there is a animator for\n      // 'x', 'y' when init. Before the init animation finished, call `setOption` to start\n      // another animators for 'style'/'shape'/'extra'.\n\n      if (latestUserDuring !== scopeUserDuring) {\n        // release\n        scope.el = scope.userDuring = null;\n        return;\n      }\n\n      tmpDuringScope.el = el; // Give no `this` to user in \"during\" calling.\n\n      scopeUserDuring(transitionDuringAPI); // FIXME: if in future meet the case that some prop will be both modified in `during` and `state`,\n      // consider the issue that the prop might be incorrect when return to \"normal\" state.\n    }\n\n    function prepareShapeOrExtraTransitionFrom(mainAttr, fromEl, elOption, transFromProps) {\n      var attrOpt = elOption[mainAttr];\n\n      if (!attrOpt) {\n        return;\n      }\n\n      var elPropsInAttr = fromEl[mainAttr];\n      var transFromPropsInAttr;\n\n      if (elPropsInAttr) {\n        var transition = elOption.transition;\n        var attrTransition = attrOpt.transition;\n\n        if (attrTransition) {\n          !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});\n\n          if (isTransitionAll(attrTransition)) {\n            extend(transFromPropsInAttr, elPropsInAttr);\n          } else {\n            var transitionKeys = normalizeToArray(attrTransition);\n\n            for (var i = 0; i < transitionKeys.length; i++) {\n              var key = transitionKeys[i];\n              var elVal = elPropsInAttr[key];\n              transFromPropsInAttr[key] = elVal;\n            }\n          }\n        } else if (isTransitionAll(transition) || indexOf(transition, mainAttr) >= 0) {\n          !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {});\n          var elPropsInAttrKeys = keys(elPropsInAttr);\n\n          for (var i = 0; i < elPropsInAttrKeys.length; i++) {\n            var key = elPropsInAttrKeys[i];\n            var elVal = elPropsInAttr[key];\n\n            if (isNonStyleTransitionEnabled(attrOpt[key], elVal)) {\n              transFromPropsInAttr[key] = elVal;\n            }\n          }\n        }\n      }\n    }\n\n    function prepareShapeOrExtraAllPropsFinal(mainAttr, elOption, allProps) {\n      var attrOpt = elOption[mainAttr];\n\n      if (!attrOpt) {\n        return;\n      }\n\n      var allPropsInAttr = allProps[mainAttr] = {};\n      var keysInAttr = keys(attrOpt);\n\n      for (var i = 0; i < keysInAttr.length; i++) {\n        var key = keysInAttr[i]; // To avoid share one object with different element, and\n        // to avoid user modify the object inexpectedly, have to clone.\n\n        allPropsInAttr[key] = cloneValue(attrOpt[key]);\n      }\n    }\n\n    function prepareTransformTransitionFrom(el, elOption, transFromProps) {\n      var transition = elOption.transition;\n      var transitionKeys = isTransitionAll(transition) ? TRANSFORMABLE_PROPS : normalizeToArray(transition || []);\n\n      for (var i = 0; i < transitionKeys.length; i++) {\n        var key = transitionKeys[i];\n\n        if (key === 'style' || key === 'shape' || key === 'extra') {\n          continue;\n        }\n\n        var elVal = el[key];\n\n        if (\"development\" !== 'production') {\n          checkTransformPropRefer(key, 'el.transition');\n        } // Do not clone, animator will perform that clone.\n\n\n        transFromProps[key] = elVal;\n      }\n    }\n\n    function prepareTransformAllPropsFinal(el, elOption, allProps) {\n      for (var i = 0; i < LEGACY_TRANSFORM_PROPS.length; i++) {\n        var legacyName = LEGACY_TRANSFORM_PROPS[i];\n        var xyName = LEGACY_TRANSFORM_PROPS_MAP[legacyName];\n        var legacyArr = elOption[legacyName];\n\n        if (legacyArr) {\n          allProps[xyName[0]] = legacyArr[0];\n          allProps[xyName[1]] = legacyArr[1];\n        }\n      }\n\n      for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {\n        var key = TRANSFORMABLE_PROPS[i];\n\n        if (elOption[key] != null) {\n          allProps[key] = elOption[key];\n        }\n      }\n    }\n\n    function prepareStyleTransitionFrom(fromEl, elOption, styleOpt, transFromProps) {\n      if (!styleOpt) {\n        return;\n      }\n\n      var fromElStyle = fromEl.style;\n      var transFromStyleProps;\n\n      if (fromElStyle) {\n        var styleTransition = styleOpt.transition;\n        var elTransition = elOption.transition;\n\n        if (styleTransition && !isTransitionAll(styleTransition)) {\n          var transitionKeys = normalizeToArray(styleTransition);\n          !transFromStyleProps && (transFromStyleProps = transFromProps.style = {});\n\n          for (var i = 0; i < transitionKeys.length; i++) {\n            var key = transitionKeys[i];\n            var elVal = fromElStyle[key]; // Do not clone, see `checkNonStyleTansitionRefer`.\n\n            transFromStyleProps[key] = elVal;\n          }\n        } else if (fromEl.getAnimationStyleProps && (isTransitionAll(elTransition) || isTransitionAll(styleTransition) || indexOf(elTransition, 'style') >= 0)) {\n          var animationProps = fromEl.getAnimationStyleProps();\n          var animationStyleProps = animationProps ? animationProps.style : null;\n\n          if (animationStyleProps) {\n            !transFromStyleProps && (transFromStyleProps = transFromProps.style = {});\n            var styleKeys = keys(styleOpt);\n\n            for (var i = 0; i < styleKeys.length; i++) {\n              var key = styleKeys[i];\n\n              if (animationStyleProps[key]) {\n                var elVal = fromElStyle[key];\n                transFromStyleProps[key] = elVal;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    function isNonStyleTransitionEnabled(optVal, elVal) {\n      // The same as `checkNonStyleTansitionRefer`.\n      return !isArrayLike(optVal) ? optVal != null && isFinite(optVal) : optVal !== elVal;\n    }\n\n    var checkTransformPropRefer;\n\n    if (\"development\" !== 'production') {\n      checkTransformPropRefer = function (key, usedIn) {\n        if (!hasOwn(TRANSFORM_PROPS_MAP, key)) {\n          warn('Prop `' + key + '` is not a permitted in `' + usedIn + '`. ' + 'Only `' + keys(TRANSFORM_PROPS_MAP).join('`, `') + '` are permitted.');\n        }\n      };\n    }\n\n    var getStateToRestore = makeInner();\n    var KEYFRAME_EXCLUDE_KEYS = ['percent', 'easing', 'shape', 'style', 'extra'];\n    /**\n     * Stop previous keyframe animation and restore the attributes.\n     * Avoid new keyframe animation starts with wrong internal state when the percent: 0 is not set.\n     */\n\n    function stopPreviousKeyframeAnimationAndRestore(el) {\n      // Stop previous keyframe animation.\n      el.stopAnimation('keyframe'); // Restore\n\n      el.attr(getStateToRestore(el));\n    }\n    function applyKeyframeAnimation(el, animationOpts, animatableModel) {\n      if (!animatableModel.isAnimationEnabled() || !animationOpts) {\n        return;\n      }\n\n      if (isArray(animationOpts)) {\n        each(animationOpts, function (singleAnimationOpts) {\n          applyKeyframeAnimation(el, singleAnimationOpts, animatableModel);\n        });\n        return;\n      }\n\n      var keyframes = animationOpts.keyframes;\n      var duration = animationOpts.duration;\n\n      if (animatableModel && duration == null) {\n        // Default to use duration of config.\n        // NOTE: animation config from payload will be ignored because they are mainly for transitions.\n        var config = getAnimationConfig('enter', animatableModel, 0);\n        duration = config && config.duration;\n      }\n\n      if (!keyframes || !duration) {\n        return;\n      }\n\n      var stateToRestore = getStateToRestore(el);\n      each(ELEMENT_ANIMATABLE_PROPS, function (targetPropName) {\n        if (targetPropName && !el[targetPropName]) {\n          return;\n        }\n\n        var animator;\n        var endFrameIsSet = false; // Sort keyframes by percent.\n\n        keyframes.sort(function (a, b) {\n          return a.percent - b.percent;\n        });\n        each(keyframes, function (kf) {\n          // Stop current animation.\n          var animators = el.animators;\n          var kfValues = targetPropName ? kf[targetPropName] : kf;\n\n          if (\"development\" !== 'production') {\n            if (kf.percent >= 1) {\n              endFrameIsSet = true;\n            }\n          }\n\n          if (!kfValues) {\n            return;\n          }\n\n          var propKeys = keys(kfValues);\n\n          if (!targetPropName) {\n            // PENDING performance?\n            propKeys = filter(propKeys, function (key) {\n              return indexOf(KEYFRAME_EXCLUDE_KEYS, key) < 0;\n            });\n          }\n\n          if (!propKeys.length) {\n            return;\n          }\n\n          if (!animator) {\n            animator = el.animate(targetPropName, animationOpts.loop, true);\n            animator.scope = 'keyframe';\n          }\n\n          for (var i = 0; i < animators.length; i++) {\n            // Stop all other animation that is not keyframe.\n            if (animators[i] !== animator && animators[i].targetName === animator.targetName) {\n              animators[i].stopTracks(propKeys);\n            }\n          }\n\n          targetPropName && (stateToRestore[targetPropName] = stateToRestore[targetPropName] || {});\n          var savedTarget = targetPropName ? stateToRestore[targetPropName] : stateToRestore;\n          each(propKeys, function (key) {\n            // Save original value.\n            savedTarget[key] = ((targetPropName ? el[targetPropName] : el) || {})[key];\n          });\n          animator.whenWithKeys(duration * kf.percent, kfValues, propKeys, kf.easing);\n        });\n\n        if (!animator) {\n          return;\n        }\n\n        if (\"development\" !== 'production') {\n          if (!endFrameIsSet) {\n            warn('End frame with percent: 1 is missing in the keyframeAnimation.', true);\n          }\n        }\n\n        animator.delay(animationOpts.delay || 0).duration(duration).start(animationOpts.easing);\n      });\n    }\n\n    var EMPHASIS = 'emphasis';\n    var NORMAL = 'normal';\n    var BLUR = 'blur';\n    var SELECT = 'select';\n    var STATES = [NORMAL, EMPHASIS, BLUR, SELECT];\n    var PATH_ITEM_STYLE = {\n      normal: ['itemStyle'],\n      emphasis: [EMPHASIS, 'itemStyle'],\n      blur: [BLUR, 'itemStyle'],\n      select: [SELECT, 'itemStyle']\n    };\n    var PATH_LABEL = {\n      normal: ['label'],\n      emphasis: [EMPHASIS, 'label'],\n      blur: [BLUR, 'label'],\n      select: [SELECT, 'label']\n    };\n    var DEFAULT_TRANSITION = ['x', 'y']; // Use prefix to avoid index to be the same as el.name,\n    // which will cause weird update animation.\n\n    var GROUP_DIFF_PREFIX = 'e\\0\\0';\n    var attachedTxInfoTmp = {\n      normal: {},\n      emphasis: {},\n      blur: {},\n      select: {}\n    };\n    /**\n     * To reduce total package size of each coordinate systems, the modules `prepareCustom`\n     * of each coordinate systems are not required by each coordinate systems directly, but\n     * required by the module `custom`.\n     *\n     * prepareInfoForCustomSeries {Function}: optional\n     *     @return {Object} {coordSys: {...}, api: {\n     *         coord: function (data, clamp) {}, // return point in global.\n     *         size: function (dataSize, dataItem) {} // return size of each axis in coordSys.\n     *     }}\n     */\n\n    var prepareCustoms = {\n      cartesian2d: cartesianPrepareCustom,\n      geo: geoPrepareCustom,\n      single: singlePrepareCustom,\n      polar: polarPrepareCustom,\n      calendar: calendarPrepareCustom\n    };\n\n    function isPath$1(el) {\n      return el instanceof Path;\n    }\n\n    function isDisplayable(el) {\n      return el instanceof Displayable;\n    }\n\n    function copyElement(sourceEl, targetEl) {\n      targetEl.copyTransform(sourceEl);\n\n      if (isDisplayable(targetEl) && isDisplayable(sourceEl)) {\n        targetEl.setStyle(sourceEl.style);\n        targetEl.z = sourceEl.z;\n        targetEl.z2 = sourceEl.z2;\n        targetEl.zlevel = sourceEl.zlevel;\n        targetEl.invisible = sourceEl.invisible;\n        targetEl.ignore = sourceEl.ignore;\n\n        if (isPath$1(targetEl) && isPath$1(sourceEl)) {\n          targetEl.setShape(sourceEl.shape);\n        }\n      }\n    }\n\n    var CustomChartView =\n    /** @class */\n    function (_super) {\n      __extends(CustomChartView, _super);\n\n      function CustomChartView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CustomChartView.type;\n        return _this;\n      }\n\n      CustomChartView.prototype.render = function (customSeries, ecModel, api, payload) {\n        // Clear previously rendered progressive elements.\n        this._progressiveEls = null;\n        var oldData = this._data;\n        var data = customSeries.getData();\n        var group = this.group;\n        var renderItem = makeRenderItem(customSeries, data, ecModel, api);\n\n        if (!oldData) {\n          // Previous render is incremental render or first render.\n          // Needs remove the incremental rendered elements.\n          group.removeAll();\n        }\n\n        data.diff(oldData).add(function (newIdx) {\n          createOrUpdateItem(api, null, newIdx, renderItem(newIdx, payload), customSeries, group, data);\n        }).remove(function (oldIdx) {\n          var el = oldData.getItemGraphicEl(oldIdx);\n          el && applyLeaveTransition(el, customInnerStore(el).option, customSeries);\n        }).update(function (newIdx, oldIdx) {\n          var oldEl = oldData.getItemGraphicEl(oldIdx);\n          createOrUpdateItem(api, oldEl, newIdx, renderItem(newIdx, payload), customSeries, group, data);\n        }).execute(); // Do clipping\n\n        var clipPath = customSeries.get('clip', true) ? createClipPath(customSeries.coordinateSystem, false, customSeries) : null;\n\n        if (clipPath) {\n          group.setClipPath(clipPath);\n        } else {\n          group.removeClipPath();\n        }\n\n        this._data = data;\n      };\n\n      CustomChartView.prototype.incrementalPrepareRender = function (customSeries, ecModel, api) {\n        this.group.removeAll();\n        this._data = null;\n      };\n\n      CustomChartView.prototype.incrementalRender = function (params, customSeries, ecModel, api, payload) {\n        var data = customSeries.getData();\n        var renderItem = makeRenderItem(customSeries, data, ecModel, api);\n        var progressiveEls = this._progressiveEls = [];\n\n        function setIncrementalAndHoverLayer(el) {\n          if (!el.isGroup) {\n            el.incremental = true;\n            el.ensureState('emphasis').hoverLayer = true;\n          }\n        }\n\n        for (var idx = params.start; idx < params.end; idx++) {\n          var el = createOrUpdateItem(null, null, idx, renderItem(idx, payload), customSeries, this.group, data);\n\n          if (el) {\n            el.traverse(setIncrementalAndHoverLayer);\n            progressiveEls.push(el);\n          }\n        }\n      };\n\n      CustomChartView.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      CustomChartView.prototype.filterForExposedEvent = function (eventType, query, targetEl, packedEvent) {\n        var elementName = query.element;\n\n        if (elementName == null || targetEl.name === elementName) {\n          return true;\n        } // Enable to give a name on a group made by `renderItem`, and listen\n        // events that are triggered by its descendents.\n\n\n        while ((targetEl = targetEl.__hostTarget || targetEl.parent) && targetEl !== this.group) {\n          if (targetEl.name === elementName) {\n            return true;\n          }\n        }\n\n        return false;\n      };\n\n      CustomChartView.type = 'custom';\n      return CustomChartView;\n    }(ChartView);\n\n    function createEl(elOption) {\n      var graphicType = elOption.type;\n      var el; // Those graphic elements are not shapes. They should not be\n      // overwritten by users, so do them first.\n\n      if (graphicType === 'path') {\n        var shape = elOption.shape; // Using pathRect brings convenience to users sacle svg path.\n\n        var pathRect = shape.width != null && shape.height != null ? {\n          x: shape.x || 0,\n          y: shape.y || 0,\n          width: shape.width,\n          height: shape.height\n        } : null;\n        var pathData = getPathData(shape); // Path is also used for icon, so layout 'center' by default.\n\n        el = makePath(pathData, null, pathRect, shape.layout || 'center');\n        customInnerStore(el).customPathData = pathData;\n      } else if (graphicType === 'image') {\n        el = new ZRImage({});\n        customInnerStore(el).customImagePath = elOption.style.image;\n      } else if (graphicType === 'text') {\n        el = new ZRText({}); // customInnerStore(el).customText = (elOption.style as TextStyleProps).text;\n      } else if (graphicType === 'group') {\n        el = new Group();\n      } else if (graphicType === 'compoundPath') {\n        throw new Error('\"compoundPath\" is not supported yet.');\n      } else {\n        var Clz = getShapeClass(graphicType);\n\n        if (!Clz) {\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            errMsg = 'graphic type \"' + graphicType + '\" can not be found.';\n          }\n\n          throwError(errMsg);\n        }\n\n        el = new Clz();\n      }\n\n      customInnerStore(el).customGraphicType = graphicType;\n      el.name = elOption.name; // Compat ec4: the default z2 lift is 1. If changing the number,\n      // some cases probably be broken: hierarchy layout along z, like circle packing,\n      // where emphasis only intending to modify color/border rather than lift z2.\n\n      el.z2EmphasisLift = 1;\n      el.z2SelectLift = 1;\n      return el;\n    }\n\n    function updateElNormal( // Can be null/undefined\n    api, el, dataIndex, elOption, attachedTxInfo, seriesModel, isInit) {\n      // Stop and restore before update any other attributes.\n      stopPreviousKeyframeAnimationAndRestore(el);\n      var txCfgOpt = attachedTxInfo && attachedTxInfo.normal.cfg;\n\n      if (txCfgOpt) {\n        // PENDING: whether use user object directly rather than clone?\n        // TODO:5.0 textConfig transition animation?\n        el.setTextConfig(txCfgOpt);\n      } // Default transition ['x', 'y']\n\n\n      if (elOption && elOption.transition == null) {\n        elOption.transition = DEFAULT_TRANSITION;\n      } // Do some normalization on style.\n\n\n      var styleOpt = elOption && elOption.style;\n\n      if (styleOpt) {\n        if (el.type === 'text') {\n          var textOptionStyle = styleOpt; // Compatible with ec4: if `textFill` or `textStroke` exists use them.\n\n          hasOwn(textOptionStyle, 'textFill') && (textOptionStyle.fill = textOptionStyle.textFill);\n          hasOwn(textOptionStyle, 'textStroke') && (textOptionStyle.stroke = textOptionStyle.textStroke);\n        }\n\n        var decalPattern = void 0;\n        var decalObj = isPath$1(el) ? styleOpt.decal : null;\n\n        if (api && decalObj) {\n          decalObj.dirty = true;\n          decalPattern = createOrUpdatePatternFromDecal(decalObj, api);\n        } // Always overwrite in case user specify this prop.\n\n\n        styleOpt.__decalPattern = decalPattern;\n      }\n\n      if (isDisplayable(el)) {\n        if (styleOpt) {\n          var decalPattern = styleOpt.__decalPattern;\n\n          if (decalPattern) {\n            styleOpt.decal = decalPattern;\n          }\n        }\n      }\n\n      applyUpdateTransition(el, elOption, seriesModel, {\n        dataIndex: dataIndex,\n        isInit: isInit,\n        clearStyle: true\n      });\n      applyKeyframeAnimation(el, elOption.keyframeAnimation, seriesModel);\n    }\n\n    function updateElOnState(state, el, elStateOpt, styleOpt, attachedTxInfo) {\n      var elDisplayable = el.isGroup ? null : el;\n      var txCfgOpt = attachedTxInfo && attachedTxInfo[state].cfg; // PENDING:5.0 support customize scale change and transition animation?\n\n      if (elDisplayable) {\n        // By default support auto lift color when hover whether `emphasis` specified.\n        var stateObj = elDisplayable.ensureState(state);\n\n        if (styleOpt === false) {\n          var existingEmphasisState = elDisplayable.getState(state);\n\n          if (existingEmphasisState) {\n            existingEmphasisState.style = null;\n          }\n        } else {\n          // style is needed to enable default emphasis.\n          stateObj.style = styleOpt || null;\n        } // If `elOption.styleEmphasis` or `elOption.emphasis.style` is `false`,\n        // remove hover style.\n        // If `elOption.textConfig` or `elOption.emphasis.textConfig` is null/undefined, it does not\n        // make sense. So for simplicity, we do not ditinguish `hasOwnProperty` and null/undefined.\n\n\n        if (txCfgOpt) {\n          stateObj.textConfig = txCfgOpt;\n        }\n\n        setDefaultStateProxy(elDisplayable);\n      }\n    }\n\n    function updateZ$1(el, elOption, seriesModel) {\n      // Group not support textContent and not support z yet.\n      if (el.isGroup) {\n        return;\n      }\n\n      var elDisplayable = el;\n      var currentZ = seriesModel.currentZ;\n      var currentZLevel = seriesModel.currentZLevel; // Always erase.\n\n      elDisplayable.z = currentZ;\n      elDisplayable.zlevel = currentZLevel; // z2 must not be null/undefined, otherwise sort error may occur.\n\n      var optZ2 = elOption.z2;\n      optZ2 != null && (elDisplayable.z2 = optZ2 || 0);\n\n      for (var i = 0; i < STATES.length; i++) {\n        updateZForEachState(elDisplayable, elOption, STATES[i]);\n      }\n    }\n\n    function updateZForEachState(elDisplayable, elOption, state) {\n      var isNormal = state === NORMAL;\n      var elStateOpt = isNormal ? elOption : retrieveStateOption(elOption, state);\n      var optZ2 = elStateOpt ? elStateOpt.z2 : null;\n      var stateObj;\n\n      if (optZ2 != null) {\n        // Do not `ensureState` until required.\n        stateObj = isNormal ? elDisplayable : elDisplayable.ensureState(state);\n        stateObj.z2 = optZ2 || 0;\n      }\n    }\n\n    function makeRenderItem(customSeries, data, ecModel, api) {\n      var renderItem = customSeries.get('renderItem');\n      var coordSys = customSeries.coordinateSystem;\n      var prepareResult = {};\n\n      if (coordSys) {\n        if (\"development\" !== 'production') {\n          assert(renderItem, 'series.render is required.');\n          assert(coordSys.prepareCustoms || prepareCustoms[coordSys.type], 'This coordSys does not support custom series.');\n        } // `coordSys.prepareCustoms` is used for external coord sys like bmap.\n\n\n        prepareResult = coordSys.prepareCustoms ? coordSys.prepareCustoms(coordSys) : prepareCustoms[coordSys.type](coordSys);\n      }\n\n      var userAPI = defaults({\n        getWidth: api.getWidth,\n        getHeight: api.getHeight,\n        getZr: api.getZr,\n        getDevicePixelRatio: api.getDevicePixelRatio,\n        value: value,\n        style: style,\n        ordinalRawValue: ordinalRawValue,\n        styleEmphasis: styleEmphasis,\n        visual: visual,\n        barLayout: barLayout,\n        currentSeriesIndices: currentSeriesIndices,\n        font: font\n      }, prepareResult.api || {});\n      var userParams = {\n        // The life cycle of context: current round of rendering.\n        // The global life cycle is probably not necessary, because\n        // user can store global status by themselves.\n        context: {},\n        seriesId: customSeries.id,\n        seriesName: customSeries.name,\n        seriesIndex: customSeries.seriesIndex,\n        coordSys: prepareResult.coordSys,\n        dataInsideLength: data.count(),\n        encode: wrapEncodeDef(customSeries.getData())\n      }; // If someday intending to refactor them to a class, should consider do not\n      // break change: currently these attribute member are encapsulated in a closure\n      // so that do not need to force user to call these method with a scope.\n      // Do not support call `api` asynchronously without dataIndexInside input.\n\n      var currDataIndexInside;\n      var currItemModel;\n      var currItemStyleModels = {};\n      var currLabelModels = {};\n      var seriesItemStyleModels = {};\n      var seriesLabelModels = {};\n\n      for (var i = 0; i < STATES.length; i++) {\n        var stateName = STATES[i];\n        seriesItemStyleModels[stateName] = customSeries.getModel(PATH_ITEM_STYLE[stateName]);\n        seriesLabelModels[stateName] = customSeries.getModel(PATH_LABEL[stateName]);\n      }\n\n      function getItemModel(dataIndexInside) {\n        return dataIndexInside === currDataIndexInside ? currItemModel || (currItemModel = data.getItemModel(dataIndexInside)) : data.getItemModel(dataIndexInside);\n      }\n\n      function getItemStyleModel(dataIndexInside, state) {\n        return !data.hasItemOption ? seriesItemStyleModels[state] : dataIndexInside === currDataIndexInside ? currItemStyleModels[state] || (currItemStyleModels[state] = getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state])) : getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state]);\n      }\n\n      function getLabelModel(dataIndexInside, state) {\n        return !data.hasItemOption ? seriesLabelModels[state] : dataIndexInside === currDataIndexInside ? currLabelModels[state] || (currLabelModels[state] = getItemModel(dataIndexInside).getModel(PATH_LABEL[state])) : getItemModel(dataIndexInside).getModel(PATH_LABEL[state]);\n      }\n\n      return function (dataIndexInside, payload) {\n        currDataIndexInside = dataIndexInside;\n        currItemModel = null;\n        currItemStyleModels = {};\n        currLabelModels = {};\n        return renderItem && renderItem(defaults({\n          dataIndexInside: dataIndexInside,\n          dataIndex: data.getRawIndex(dataIndexInside),\n          // Can be used for optimization when zoom or roam.\n          actionType: payload ? payload.type : null\n        }, userParams), userAPI);\n      };\n      /**\n       * @public\n       * @param dim by default 0.\n       * @param dataIndexInside by default `currDataIndexInside`.\n       */\n\n      function value(dim, dataIndexInside) {\n        dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n        return data.getStore().get(data.getDimensionIndex(dim || 0), dataIndexInside);\n      }\n      /**\n       * @public\n       * @param dim by default 0.\n       * @param dataIndexInside by default `currDataIndexInside`.\n       */\n\n\n      function ordinalRawValue(dim, dataIndexInside) {\n        dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n        dim = dim || 0;\n        var dimInfo = data.getDimensionInfo(dim);\n\n        if (!dimInfo) {\n          var dimIndex = data.getDimensionIndex(dim);\n          return dimIndex >= 0 ? data.getStore().get(dimIndex, dataIndexInside) : undefined;\n        }\n\n        var val = data.get(dimInfo.name, dataIndexInside);\n        var ordinalMeta = dimInfo && dimInfo.ordinalMeta;\n        return ordinalMeta ? ordinalMeta.categories[val] : val;\n      }\n      /**\n       * @deprecated The original intention of `api.style` is enable to set itemStyle\n       * like other series. But it is not necessary and not easy to give a strict definition\n       * of what it returns. And since echarts5 it needs to be make compat work. So\n       * deprecates it since echarts5.\n       *\n       * By default, `visual` is applied to style (to support visualMap).\n       * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`,\n       * it can be implemented as:\n       * `api.style({stroke: api.visual('color'), fill: null})`;\n       *\n       * [Compat]: since ec5, RectText has been separated from its hosts el.\n       * so `api.style()` will only return the style from `itemStyle` but not handle `label`\n       * any more. But `series.label` config is never published in doc.\n       * We still compat it in `api.style()`. But not encourage to use it and will still not\n       * to pulish it to doc.\n       * @public\n       * @param dataIndexInside by default `currDataIndexInside`.\n       */\n\n\n      function style(userProps, dataIndexInside) {\n        if (\"development\" !== 'production') {\n          warnDeprecated('api.style', 'Please write literal style directly instead.');\n        }\n\n        dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n        var style = data.getItemVisual(dataIndexInside, 'style');\n        var visualColor = style && style.fill;\n        var opacity = style && style.opacity;\n        var itemStyle = getItemStyleModel(dataIndexInside, NORMAL).getItemStyle();\n        visualColor != null && (itemStyle.fill = visualColor);\n        opacity != null && (itemStyle.opacity = opacity);\n        var opt = {\n          inheritColor: isString(visualColor) ? visualColor : '#000'\n        };\n        var labelModel = getLabelModel(dataIndexInside, NORMAL); // Now that the feature of \"auto adjust text fill/stroke\" has been migrated to zrender\n        // since ec5, we should set `isAttached` as `false` here and make compat in\n        // `convertToEC4StyleForCustomSerise`.\n\n        var textStyle = createTextStyle(labelModel, null, opt, false, true);\n        textStyle.text = labelModel.getShallow('show') ? retrieve2(customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null;\n        var textConfig = createTextConfig(labelModel, opt, false);\n        preFetchFromExtra(userProps, itemStyle);\n        itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig);\n        userProps && applyUserPropsAfter(itemStyle, userProps);\n        itemStyle.legacy = true;\n        return itemStyle;\n      }\n      /**\n       * @deprecated The reason see `api.style()`\n       * @public\n       * @param dataIndexInside by default `currDataIndexInside`.\n       */\n\n\n      function styleEmphasis(userProps, dataIndexInside) {\n        if (\"development\" !== 'production') {\n          warnDeprecated('api.styleEmphasis', 'Please write literal style directly instead.');\n        }\n\n        dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n        var itemStyle = getItemStyleModel(dataIndexInside, EMPHASIS).getItemStyle();\n        var labelModel = getLabelModel(dataIndexInside, EMPHASIS);\n        var textStyle = createTextStyle(labelModel, null, null, true, true);\n        textStyle.text = labelModel.getShallow('show') ? retrieve3(customSeries.getFormattedLabel(dataIndexInside, EMPHASIS), customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null;\n        var textConfig = createTextConfig(labelModel, null, true);\n        preFetchFromExtra(userProps, itemStyle);\n        itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig);\n        userProps && applyUserPropsAfter(itemStyle, userProps);\n        itemStyle.legacy = true;\n        return itemStyle;\n      }\n\n      function applyUserPropsAfter(itemStyle, extra) {\n        for (var key in extra) {\n          if (hasOwn(extra, key)) {\n            itemStyle[key] = extra[key];\n          }\n        }\n      }\n\n      function preFetchFromExtra(extra, itemStyle) {\n        // A trick to retrieve those props firstly, which are used to\n        // apply auto inside fill/stroke in `convertToEC4StyleForCustomSerise`.\n        // (It's not reasonable but only for a degree of compat)\n        if (extra) {\n          extra.textFill && (itemStyle.textFill = extra.textFill);\n          extra.textPosition && (itemStyle.textPosition = extra.textPosition);\n        }\n      }\n      /**\n       * @public\n       * @param dataIndexInside by default `currDataIndexInside`.\n       */\n\n\n      function visual(visualType, dataIndexInside) {\n        dataIndexInside == null && (dataIndexInside = currDataIndexInside);\n\n        if (hasOwn(STYLE_VISUAL_TYPE, visualType)) {\n          var style_1 = data.getItemVisual(dataIndexInside, 'style');\n          return style_1 ? style_1[STYLE_VISUAL_TYPE[visualType]] : null;\n        } // Only support these visuals. Other visual might be inner tricky\n        // for performance (like `style`), do not expose to users.\n\n\n        if (hasOwn(NON_STYLE_VISUAL_PROPS, visualType)) {\n          return data.getItemVisual(dataIndexInside, visualType);\n        }\n      }\n      /**\n       * @public\n       * @return If not support, return undefined.\n       */\n\n\n      function barLayout(opt) {\n        if (coordSys.type === 'cartesian2d') {\n          var baseAxis = coordSys.getBaseAxis();\n          return getLayoutOnAxis(defaults({\n            axis: baseAxis\n          }, opt));\n        }\n      }\n      /**\n       * @public\n       */\n\n\n      function currentSeriesIndices() {\n        return ecModel.getCurrentSeriesIndices();\n      }\n      /**\n       * @public\n       * @return font string\n       */\n\n\n      function font(opt) {\n        return getFont(opt, ecModel);\n      }\n    }\n\n    function wrapEncodeDef(data) {\n      var encodeDef = {};\n      each(data.dimensions, function (dimName) {\n        var dimInfo = data.getDimensionInfo(dimName);\n\n        if (!dimInfo.isExtraCoord) {\n          var coordDim = dimInfo.coordDim;\n          var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || [];\n          dataDims[dimInfo.coordDimIndex] = data.getDimensionIndex(dimName);\n        }\n      });\n      return encodeDef;\n    }\n\n    function createOrUpdateItem(api, existsEl, dataIndex, elOption, seriesModel, group, data) {\n      // [Rule]\n      // If `renderItem` returns `null`/`undefined`/`false`, remove the previous el if existing.\n      //     (It seems that violate the \"merge\" principle, but most of users probably intuitively\n      //     regard \"return;\" as \"show nothing element whatever\", so make a exception to meet the\n      //     most cases.)\n      // The rule or \"merge\" see [STRATEGY_MERGE].\n      // If `elOption` is `null`/`undefined`/`false` (when `renderItem` returns nothing).\n      if (!elOption) {\n        group.remove(existsEl);\n        return;\n      }\n\n      var el = doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group);\n      el && data.setItemGraphicEl(dataIndex, el);\n      el && toggleHoverEmphasis(el, elOption.focus, elOption.blurScope, elOption.emphasisDisabled);\n      return el;\n    }\n\n    function doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group) {\n      if (\"development\" !== 'production') {\n        assert(elOption, 'should not have an null/undefined element setting');\n      }\n\n      var toBeReplacedIdx = -1;\n      var oldEl = existsEl;\n\n      if (existsEl && doesElNeedRecreate(existsEl, elOption, seriesModel) // || (\n      //     // PENDING: even in one-to-one mapping case, if el is marked as morph,\n      //     // do not sure whether the el will be mapped to another el with different\n      //     // hierarchy in Group tree. So always recreate el rather than reuse the el.\n      //     morphHelper && morphHelper.isOneToOneFrom(el)\n      // )\n      ) {\n        // Should keep at the original index, otherwise \"merge by index\" will be incorrect.\n        toBeReplacedIdx = indexOf(group.childrenRef(), existsEl);\n        existsEl = null;\n      }\n\n      var isInit = !existsEl;\n      var el = existsEl;\n\n      if (!el) {\n        el = createEl(elOption);\n\n        if (oldEl) {\n          copyElement(oldEl, el);\n        }\n      } else {\n        // FIMXE:NEXT unified clearState?\n        // If in some case the performance issue arised, consider\n        // do not clearState but update cached normal state directly.\n        el.clearStates();\n      } // Need to set morph: false explictly to disable automatically morphing.\n\n\n      if (elOption.morph === false) {\n        el.disableMorphing = true;\n      } else if (el.disableMorphing) {\n        el.disableMorphing = false;\n      }\n\n      attachedTxInfoTmp.normal.cfg = attachedTxInfoTmp.normal.conOpt = attachedTxInfoTmp.emphasis.cfg = attachedTxInfoTmp.emphasis.conOpt = attachedTxInfoTmp.blur.cfg = attachedTxInfoTmp.blur.conOpt = attachedTxInfoTmp.select.cfg = attachedTxInfoTmp.select.conOpt = null;\n      attachedTxInfoTmp.isLegacy = false;\n      doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfoTmp);\n      doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit);\n      updateElNormal(api, el, dataIndex, elOption, attachedTxInfoTmp, seriesModel, isInit); // `elOption.info` enables user to mount some info on\n      // elements and use them in event handlers.\n      // Update them only when user specified, otherwise, remain.\n\n      hasOwn(elOption, 'info') && (customInnerStore(el).info = elOption.info);\n\n      for (var i = 0; i < STATES.length; i++) {\n        var stateName = STATES[i];\n\n        if (stateName !== NORMAL) {\n          var otherStateOpt = retrieveStateOption(elOption, stateName);\n          var otherStyleOpt = retrieveStyleOptionOnState(elOption, otherStateOpt, stateName);\n          updateElOnState(stateName, el, otherStateOpt, otherStyleOpt, attachedTxInfoTmp);\n        }\n      }\n\n      updateZ$1(el, elOption, seriesModel);\n\n      if (elOption.type === 'group') {\n        mergeChildren(api, el, dataIndex, elOption, seriesModel);\n      }\n\n      if (toBeReplacedIdx >= 0) {\n        group.replaceAt(el, toBeReplacedIdx);\n      } else {\n        group.add(el);\n      }\n\n      return el;\n    } // `el` must not be null/undefined.\n\n\n    function doesElNeedRecreate(el, elOption, seriesModel) {\n      var elInner = customInnerStore(el);\n      var elOptionType = elOption.type;\n      var elOptionShape = elOption.shape;\n      var elOptionStyle = elOption.style;\n      return (// Always create new if universal transition is enabled.\n        // Because we do transition after render. It needs to know what old element is. Replacement will loose it.\n        seriesModel.isUniversalTransitionEnabled() // If `elOptionType` is `null`, follow the merge principle.\n        || elOptionType != null && elOptionType !== elInner.customGraphicType || elOptionType === 'path' && hasOwnPathData(elOptionShape) && getPathData(elOptionShape) !== elInner.customPathData || elOptionType === 'image' && hasOwn(elOptionStyle, 'image') && elOptionStyle.image !== elInner.customImagePath // // FIXME test and remove this restriction?\n        // || (elOptionType === 'text'\n        //     && hasOwn(elOptionStyle, 'text')\n        //     && (elOptionStyle as TextStyleProps).text !== elInner.customText\n        // )\n\n      );\n    }\n\n    function doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit) {\n      // Based on the \"merge\" principle, if no clipPath provided,\n      // do nothing. The exists clip will be totally removed only if\n      // `el.clipPath` is `false`. Otherwise it will be merged/replaced.\n      var clipPathOpt = elOption.clipPath;\n\n      if (clipPathOpt === false) {\n        if (el && el.getClipPath()) {\n          el.removeClipPath();\n        }\n      } else if (clipPathOpt) {\n        var clipPath = el.getClipPath();\n\n        if (clipPath && doesElNeedRecreate(clipPath, clipPathOpt, seriesModel)) {\n          clipPath = null;\n        }\n\n        if (!clipPath) {\n          clipPath = createEl(clipPathOpt);\n\n          if (\"development\" !== 'production') {\n            assert(isPath$1(clipPath), 'Only any type of `path` can be used in `clipPath`, rather than ' + clipPath.type + '.');\n          }\n\n          el.setClipPath(clipPath);\n        }\n\n        updateElNormal(null, clipPath, dataIndex, clipPathOpt, null, seriesModel, isInit);\n      } // If not define `clipPath` in option, do nothing unnecessary.\n\n    }\n\n    function doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfo) {\n      // Group does not support textContent temporarily until necessary.\n      if (el.isGroup) {\n        return;\n      } // Normal must be called before emphasis, for `isLegacy` detection.\n\n\n      processTxInfo(elOption, null, attachedTxInfo);\n      processTxInfo(elOption, EMPHASIS, attachedTxInfo); // If `elOption.textConfig` or `elOption.textContent` is null/undefined, it does not make sense.\n      // So for simplicity, if \"elOption hasOwnProperty of them but be null/undefined\", we do not\n      // trade them as set to null to el.\n      // Especially:\n      // `elOption.textContent: false` means remove textContent.\n      // `elOption.textContent.emphasis.style: false` means remove the style from emphasis state.\n\n      var txConOptNormal = attachedTxInfo.normal.conOpt;\n      var txConOptEmphasis = attachedTxInfo.emphasis.conOpt;\n      var txConOptBlur = attachedTxInfo.blur.conOpt;\n      var txConOptSelect = attachedTxInfo.select.conOpt;\n\n      if (txConOptNormal != null || txConOptEmphasis != null || txConOptSelect != null || txConOptBlur != null) {\n        var textContent = el.getTextContent();\n\n        if (txConOptNormal === false) {\n          textContent && el.removeTextContent();\n        } else {\n          txConOptNormal = attachedTxInfo.normal.conOpt = txConOptNormal || {\n            type: 'text'\n          };\n\n          if (!textContent) {\n            textContent = createEl(txConOptNormal);\n            el.setTextContent(textContent);\n          } else {\n            // If in some case the performance issue arised, consider\n            // do not clearState but update cached normal state directly.\n            textContent.clearStates();\n          }\n\n          updateElNormal(null, textContent, dataIndex, txConOptNormal, null, seriesModel, isInit);\n          var txConStlOptNormal = txConOptNormal && txConOptNormal.style;\n\n          for (var i = 0; i < STATES.length; i++) {\n            var stateName = STATES[i];\n\n            if (stateName !== NORMAL) {\n              var txConOptOtherState = attachedTxInfo[stateName].conOpt;\n              updateElOnState(stateName, textContent, txConOptOtherState, retrieveStyleOptionOnState(txConOptNormal, txConOptOtherState, stateName), null);\n            }\n          }\n\n          txConStlOptNormal ? textContent.dirty() : textContent.markRedraw();\n        }\n      }\n    }\n\n    function processTxInfo(elOption, state, attachedTxInfo) {\n      var stateOpt = !state ? elOption : retrieveStateOption(elOption, state);\n      var styleOpt = !state ? elOption.style : retrieveStyleOptionOnState(elOption, stateOpt, EMPHASIS);\n      var elType = elOption.type;\n      var txCfg = stateOpt ? stateOpt.textConfig : null;\n      var txConOptNormal = elOption.textContent;\n      var txConOpt = !txConOptNormal ? null : !state ? txConOptNormal : retrieveStateOption(txConOptNormal, state);\n\n      if (styleOpt && ( // Because emphasis style has little info to detect legacy,\n      // if normal is legacy, emphasis is trade as legacy.\n      attachedTxInfo.isLegacy || isEC4CompatibleStyle(styleOpt, elType, !!txCfg, !!txConOpt))) {\n        attachedTxInfo.isLegacy = true;\n        var convertResult = convertFromEC4CompatibleStyle(styleOpt, elType, !state); // Explicitly specified `textConfig` and `textContent` has higher priority than\n        // the ones generated by legacy style. Otherwise if users use them and `api.style`\n        // at the same time, they not both work and hardly to known why.\n\n        if (!txCfg && convertResult.textConfig) {\n          txCfg = convertResult.textConfig;\n        }\n\n        if (!txConOpt && convertResult.textContent) {\n          txConOpt = convertResult.textContent;\n        }\n      }\n\n      if (!state && txConOpt) {\n        var txConOptNormal_1 = txConOpt; // `textContent: {type: 'text'}`, the \"type\" is easy to be missing. So we tolerate it.\n\n        !txConOptNormal_1.type && (txConOptNormal_1.type = 'text');\n\n        if (\"development\" !== 'production') {\n          // Do not tolerate incorrcet type for forward compat.\n          assert(txConOptNormal_1.type === 'text', 'textContent.type must be \"text\"');\n        }\n      }\n\n      var info = !state ? attachedTxInfo.normal : attachedTxInfo[state];\n      info.cfg = txCfg;\n      info.conOpt = txConOpt;\n    }\n\n    function retrieveStateOption(elOption, state) {\n      return !state ? elOption : elOption ? elOption[state] : null;\n    }\n\n    function retrieveStyleOptionOnState(stateOptionNormal, stateOption, state) {\n      var style = stateOption && stateOption.style;\n\n      if (style == null && state === EMPHASIS && stateOptionNormal) {\n        style = stateOptionNormal.styleEmphasis;\n      }\n\n      return style;\n    } // Usage:\n    // (1) By default, `elOption.$mergeChildren` is `'byIndex'`, which indicates\n    //     that the existing children will not be removed, and enables the feature\n    //     that update some of the props of some of the children simply by construct\n    //     the returned children of `renderItem` like:\n    //     `var children = group.children = []; children[3] = {opacity: 0.5};`\n    // (2) If `elOption.$mergeChildren` is `'byName'`, add/update/remove children\n    //     by child.name. But that might be lower performance.\n    // (3) If `elOption.$mergeChildren` is `false`, the existing children will be\n    //     replaced totally.\n    // (4) If `!elOption.children`, following the \"merge\" principle, nothing will\n    //     happen.\n    // (5) If `elOption.$mergeChildren` is not `false` neither `'byName'` and the\n    //     `el` is a group, and if any of the new child is null, it means to remove\n    //     the element at the same index, if exists. On the other hand, if the new\n    //     child is and empty object `{}`, it means to keep the element not changed.\n    //\n    // For implementation simpleness, do not provide a direct way to remove single\n    // child (otherwise the total indices of the children array have to be modified).\n    // User can remove a single child by setting its `ignore` to `true`.\n\n\n    function mergeChildren(api, el, dataIndex, elOption, seriesModel) {\n      var newChildren = elOption.children;\n      var newLen = newChildren ? newChildren.length : 0;\n      var mergeChildren = elOption.$mergeChildren; // `diffChildrenByName` has been deprecated.\n\n      var byName = mergeChildren === 'byName' || elOption.diffChildrenByName;\n      var notMerge = mergeChildren === false; // For better performance on roam update, only enter if necessary.\n\n      if (!newLen && !byName && !notMerge) {\n        return;\n      }\n\n      if (byName) {\n        diffGroupChildren({\n          api: api,\n          oldChildren: el.children() || [],\n          newChildren: newChildren || [],\n          dataIndex: dataIndex,\n          seriesModel: seriesModel,\n          group: el\n        });\n        return;\n      }\n\n      notMerge && el.removeAll(); // Mapping children of a group simply by index, which\n      // might be better performance.\n\n      var index = 0;\n\n      for (; index < newLen; index++) {\n        var newChild = newChildren[index];\n        var oldChild = el.childAt(index);\n\n        if (newChild) {\n          if (newChild.ignore == null) {\n            // The old child is set to be ignored if null (see comments\n            // below). So we need to set ignore to be false back.\n            newChild.ignore = false;\n          }\n\n          doCreateOrUpdateEl(api, oldChild, dataIndex, newChild, seriesModel, el);\n        } else {\n          if (\"development\" !== 'production') {\n            assert(oldChild, 'renderItem should not return a group containing elements' + ' as null/undefined/{} if they do not exist before.');\n          } // If the new element option is null, it means to remove the old\n          // element. But we cannot really remove the element from the group\n          // directly, because the element order may not be stable when this\n          // element is added back. So we set the element to be ignored.\n\n\n          oldChild.ignore = true;\n        }\n      }\n\n      for (var i = el.childCount() - 1; i >= index; i--) {\n        var child = el.childAt(i);\n        removeChildFromGroup(el, child, seriesModel);\n      }\n    }\n\n    function removeChildFromGroup(group, child, seriesModel) {\n      // Do not support leave elements that are not mentioned in the latest\n      // `renderItem` return. Otherwise users may not have a clear and simple\n      // concept that how to control all of the elements.\n      child && applyLeaveTransition(child, customInnerStore(group).option, seriesModel);\n    }\n\n    function diffGroupChildren(context) {\n      new DataDiffer(context.oldChildren, context.newChildren, getKey, getKey, context).add(processAddUpdate).update(processAddUpdate).remove(processRemove).execute();\n    }\n\n    function getKey(item, idx) {\n      var name = item && item.name;\n      return name != null ? name : GROUP_DIFF_PREFIX + idx;\n    }\n\n    function processAddUpdate(newIndex, oldIndex) {\n      var context = this.context;\n      var childOption = newIndex != null ? context.newChildren[newIndex] : null;\n      var child = oldIndex != null ? context.oldChildren[oldIndex] : null;\n      doCreateOrUpdateEl(context.api, child, context.dataIndex, childOption, context.seriesModel, context.group);\n    }\n\n    function processRemove(oldIndex) {\n      var context = this.context;\n      var child = context.oldChildren[oldIndex];\n      child && applyLeaveTransition(child, customInnerStore(child).option, context.seriesModel);\n    }\n    /**\n     * @return SVG Path data.\n     */\n\n\n    function getPathData(shape) {\n      // \"d\" follows the SVG convention.\n      return shape && (shape.pathData || shape.d);\n    }\n\n    function hasOwnPathData(shape) {\n      return shape && (hasOwn(shape, 'pathData') || hasOwn(shape, 'd'));\n    }\n\n    function install$r(registers) {\n      registers.registerChartView(CustomChartView);\n      registers.registerSeriesModel(CustomSeriesModel);\n    }\n\n    var inner$a = makeInner();\n    var clone$3 = clone;\n    var bind$1 = bind;\n    /**\n     * Base axis pointer class in 2D.\n     */\n\n    var BaseAxisPointer =\n    /** @class */\n    function () {\n      function BaseAxisPointer() {\n        this._dragging = false;\n        /**\n         * In px, arbitrary value. Do not set too small,\n         * no animation is ok for most cases.\n         */\n\n        this.animationThreshold = 15;\n      }\n      /**\n       * @implement\n       */\n\n\n      BaseAxisPointer.prototype.render = function (axisModel, axisPointerModel, api, forceRender) {\n        var value = axisPointerModel.get('value');\n        var status = axisPointerModel.get('status'); // Bind them to `this`, not in closure, otherwise they will not\n        // be replaced when user calling setOption in not merge mode.\n\n        this._axisModel = axisModel;\n        this._axisPointerModel = axisPointerModel;\n        this._api = api; // Optimize: `render` will be called repeatly during mouse move.\n        // So it is power consuming if performing `render` each time,\n        // especially on mobile device.\n\n        if (!forceRender && this._lastValue === value && this._lastStatus === status) {\n          return;\n        }\n\n        this._lastValue = value;\n        this._lastStatus = status;\n        var group = this._group;\n        var handle = this._handle;\n\n        if (!status || status === 'hide') {\n          // Do not clear here, for animation better.\n          group && group.hide();\n          handle && handle.hide();\n          return;\n        }\n\n        group && group.show();\n        handle && handle.show(); // Otherwise status is 'show'\n\n        var elOption = {};\n        this.makeElOption(elOption, value, axisModel, axisPointerModel, api); // Enable change axis pointer type.\n\n        var graphicKey = elOption.graphicKey;\n\n        if (graphicKey !== this._lastGraphicKey) {\n          this.clear(api);\n        }\n\n        this._lastGraphicKey = graphicKey;\n        var moveAnimation = this._moveAnimation = this.determineAnimation(axisModel, axisPointerModel);\n\n        if (!group) {\n          group = this._group = new Group();\n          this.createPointerEl(group, elOption, axisModel, axisPointerModel);\n          this.createLabelEl(group, elOption, axisModel, axisPointerModel);\n          api.getZr().add(group);\n        } else {\n          var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation);\n          this.updatePointerEl(group, elOption, doUpdateProps);\n          this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);\n        }\n\n        updateMandatoryProps(group, axisPointerModel, true);\n\n        this._renderHandle(value);\n      };\n      /**\n       * @implement\n       */\n\n\n      BaseAxisPointer.prototype.remove = function (api) {\n        this.clear(api);\n      };\n      /**\n       * @implement\n       */\n\n\n      BaseAxisPointer.prototype.dispose = function (api) {\n        this.clear(api);\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.determineAnimation = function (axisModel, axisPointerModel) {\n        var animation = axisPointerModel.get('animation');\n        var axis = axisModel.axis;\n        var isCategoryAxis = axis.type === 'category';\n        var useSnap = axisPointerModel.get('snap'); // Value axis without snap always do not snap.\n\n        if (!useSnap && !isCategoryAxis) {\n          return false;\n        }\n\n        if (animation === 'auto' || animation == null) {\n          var animationThreshold = this.animationThreshold;\n\n          if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {\n            return true;\n          } // It is important to auto animation when snap used. Consider if there is\n          // a dataZoom, animation will be disabled when too many points exist, while\n          // it will be enabled for better visual effect when little points exist.\n\n\n          if (useSnap) {\n            var seriesDataCount = getAxisInfo(axisModel).seriesDataCount;\n            var axisExtent = axis.getExtent(); // Approximate band width\n\n            return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;\n          }\n\n          return false;\n        }\n\n        return animation === true;\n      };\n      /**\n       * add {pointer, label, graphicKey} to elOption\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {// Shoule be implemenented by sub-class.\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.createPointerEl = function (group, elOption, axisModel, axisPointerModel) {\n        var pointerOption = elOption.pointer;\n\n        if (pointerOption) {\n          var pointerEl = inner$a(group).pointerEl = new graphic[pointerOption.type](clone$3(elOption.pointer));\n          group.add(pointerEl);\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.createLabelEl = function (group, elOption, axisModel, axisPointerModel) {\n        if (elOption.label) {\n          var labelEl = inner$a(group).labelEl = new ZRText(clone$3(elOption.label));\n          group.add(labelEl);\n          updateLabelShowHide(labelEl, axisPointerModel);\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.updatePointerEl = function (group, elOption, updateProps) {\n        var pointerEl = inner$a(group).pointerEl;\n\n        if (pointerEl && elOption.pointer) {\n          pointerEl.setStyle(elOption.pointer.style);\n          updateProps(pointerEl, {\n            shape: elOption.pointer.shape\n          });\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.updateLabelEl = function (group, elOption, updateProps, axisPointerModel) {\n        var labelEl = inner$a(group).labelEl;\n\n        if (labelEl) {\n          labelEl.setStyle(elOption.label.style);\n          updateProps(labelEl, {\n            // Consider text length change in vertical axis, animation should\n            // be used on shape, otherwise the effect will be weird.\n            // TODOTODO\n            // shape: elOption.label.shape,\n            x: elOption.label.x,\n            y: elOption.label.y\n          });\n          updateLabelShowHide(labelEl, axisPointerModel);\n        }\n      };\n      /**\n       * @private\n       */\n\n\n      BaseAxisPointer.prototype._renderHandle = function (value) {\n        if (this._dragging || !this.updateHandleTransform) {\n          return;\n        }\n\n        var axisPointerModel = this._axisPointerModel;\n\n        var zr = this._api.getZr();\n\n        var handle = this._handle;\n        var handleModel = axisPointerModel.getModel('handle');\n        var status = axisPointerModel.get('status');\n\n        if (!handleModel.get('show') || !status || status === 'hide') {\n          handle && zr.remove(handle);\n          this._handle = null;\n          return;\n        }\n\n        var isInit;\n\n        if (!this._handle) {\n          isInit = true;\n          handle = this._handle = createIcon(handleModel.get('icon'), {\n            cursor: 'move',\n            draggable: true,\n            onmousemove: function (e) {\n              // Fot mobile devicem, prevent screen slider on the button.\n              stop(e.event);\n            },\n            onmousedown: bind$1(this._onHandleDragMove, this, 0, 0),\n            drift: bind$1(this._onHandleDragMove, this),\n            ondragend: bind$1(this._onHandleDragEnd, this)\n          });\n          zr.add(handle);\n        }\n\n        updateMandatoryProps(handle, axisPointerModel, false); // update style\n\n        handle.setStyle(handleModel.getItemStyle(null, ['color', 'borderColor', 'borderWidth', 'opacity', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'])); // update position\n\n        var handleSize = handleModel.get('size');\n\n        if (!isArray(handleSize)) {\n          handleSize = [handleSize, handleSize];\n        }\n\n        handle.scaleX = handleSize[0] / 2;\n        handle.scaleY = handleSize[1] / 2;\n        createOrUpdate(this, '_doDispatchAxisPointer', handleModel.get('throttle') || 0, 'fixRate');\n\n        this._moveHandleToValue(value, isInit);\n      };\n\n      BaseAxisPointer.prototype._moveHandleToValue = function (value, isInit) {\n        updateProps$1(this._axisPointerModel, !isInit && this._moveAnimation, this._handle, getHandleTransProps(this.getHandleTransform(value, this._axisModel, this._axisPointerModel)));\n      };\n\n      BaseAxisPointer.prototype._onHandleDragMove = function (dx, dy) {\n        var handle = this._handle;\n\n        if (!handle) {\n          return;\n        }\n\n        this._dragging = true; // Persistent for throttle.\n\n        var trans = this.updateHandleTransform(getHandleTransProps(handle), [dx, dy], this._axisModel, this._axisPointerModel);\n        this._payloadInfo = trans;\n        handle.stopAnimation();\n        handle.attr(getHandleTransProps(trans));\n        inner$a(handle).lastProp = null;\n\n        this._doDispatchAxisPointer();\n      };\n      /**\n       * Throttled method.\n       */\n\n\n      BaseAxisPointer.prototype._doDispatchAxisPointer = function () {\n        var handle = this._handle;\n\n        if (!handle) {\n          return;\n        }\n\n        var payloadInfo = this._payloadInfo;\n        var axisModel = this._axisModel;\n\n        this._api.dispatchAction({\n          type: 'updateAxisPointer',\n          x: payloadInfo.cursorPoint[0],\n          y: payloadInfo.cursorPoint[1],\n          tooltipOption: payloadInfo.tooltipOption,\n          axesInfo: [{\n            axisDim: axisModel.axis.dim,\n            axisIndex: axisModel.componentIndex\n          }]\n        });\n      };\n\n      BaseAxisPointer.prototype._onHandleDragEnd = function () {\n        this._dragging = false;\n        var handle = this._handle;\n\n        if (!handle) {\n          return;\n        }\n\n        var value = this._axisPointerModel.get('value'); // Consider snap or categroy axis, handle may be not consistent with\n        // axisPointer. So move handle to align the exact value position when\n        // drag ended.\n\n\n        this._moveHandleToValue(value); // For the effect: tooltip will be shown when finger holding on handle\n        // button, and will be hidden after finger left handle button.\n\n\n        this._api.dispatchAction({\n          type: 'hideTip'\n        });\n      };\n      /**\n       * @private\n       */\n\n\n      BaseAxisPointer.prototype.clear = function (api) {\n        this._lastValue = null;\n        this._lastStatus = null;\n        var zr = api.getZr();\n        var group = this._group;\n        var handle = this._handle;\n\n        if (zr && group) {\n          this._lastGraphicKey = null;\n          group && zr.remove(group);\n          handle && zr.remove(handle);\n          this._group = null;\n          this._handle = null;\n          this._payloadInfo = null;\n        }\n\n        clear(this, '_doDispatchAxisPointer');\n      };\n      /**\n       * @protected\n       */\n\n\n      BaseAxisPointer.prototype.doClear = function () {// Implemented by sub-class if necessary.\n      };\n\n      BaseAxisPointer.prototype.buildLabel = function (xy, wh, xDimIndex) {\n        xDimIndex = xDimIndex || 0;\n        return {\n          x: xy[xDimIndex],\n          y: xy[1 - xDimIndex],\n          width: wh[xDimIndex],\n          height: wh[1 - xDimIndex]\n        };\n      };\n\n      return BaseAxisPointer;\n    }();\n\n    function updateProps$1(animationModel, moveAnimation, el, props) {\n      // Animation optimize.\n      if (!propsEqual(inner$a(el).lastProp, props)) {\n        inner$a(el).lastProp = props;\n        moveAnimation ? updateProps(el, props, animationModel) : (el.stopAnimation(), el.attr(props));\n      }\n    }\n\n    function propsEqual(lastProps, newProps) {\n      if (isObject(lastProps) && isObject(newProps)) {\n        var equals_1 = true;\n        each(newProps, function (item, key) {\n          equals_1 = equals_1 && propsEqual(lastProps[key], item);\n        });\n        return !!equals_1;\n      } else {\n        return lastProps === newProps;\n      }\n    }\n\n    function updateLabelShowHide(labelEl, axisPointerModel) {\n      labelEl[axisPointerModel.get(['label', 'show']) ? 'show' : 'hide']();\n    }\n\n    function getHandleTransProps(trans) {\n      return {\n        x: trans.x || 0,\n        y: trans.y || 0,\n        rotation: trans.rotation || 0\n      };\n    }\n\n    function updateMandatoryProps(group, axisPointerModel, silent) {\n      var z = axisPointerModel.get('z');\n      var zlevel = axisPointerModel.get('zlevel');\n      group && group.traverse(function (el) {\n        if (el.type !== 'group') {\n          z != null && (el.z = z);\n          zlevel != null && (el.zlevel = zlevel);\n          el.silent = silent;\n        }\n      });\n    }\n\n    function buildElStyle(axisPointerModel) {\n      var axisPointerType = axisPointerModel.get('type');\n      var styleModel = axisPointerModel.getModel(axisPointerType + 'Style');\n      var style;\n\n      if (axisPointerType === 'line') {\n        style = styleModel.getLineStyle();\n        style.fill = null;\n      } else if (axisPointerType === 'shadow') {\n        style = styleModel.getAreaStyle();\n        style.stroke = null;\n      }\n\n      return style;\n    }\n    /**\n     * @param {Function} labelPos {align, verticalAlign, position}\n     */\n\n    function buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos) {\n      var value = axisPointerModel.get('value');\n      var text = getValueLabel(value, axisModel.axis, axisModel.ecModel, axisPointerModel.get('seriesDataIndices'), {\n        precision: axisPointerModel.get(['label', 'precision']),\n        formatter: axisPointerModel.get(['label', 'formatter'])\n      });\n      var labelModel = axisPointerModel.getModel('label');\n      var paddings = normalizeCssArray$1(labelModel.get('padding') || 0);\n      var font = labelModel.getFont();\n      var textRect = getBoundingRect(text, font);\n      var position = labelPos.position;\n      var width = textRect.width + paddings[1] + paddings[3];\n      var height = textRect.height + paddings[0] + paddings[2]; // Adjust by align.\n\n      var align = labelPos.align;\n      align === 'right' && (position[0] -= width);\n      align === 'center' && (position[0] -= width / 2);\n      var verticalAlign = labelPos.verticalAlign;\n      verticalAlign === 'bottom' && (position[1] -= height);\n      verticalAlign === 'middle' && (position[1] -= height / 2); // Not overflow ec container\n\n      confineInContainer(position, width, height, api);\n      var bgColor = labelModel.get('backgroundColor');\n\n      if (!bgColor || bgColor === 'auto') {\n        bgColor = axisModel.get(['axisLine', 'lineStyle', 'color']);\n      }\n\n      elOption.label = {\n        // shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},\n        x: position[0],\n        y: position[1],\n        style: createTextStyle(labelModel, {\n          text: text,\n          font: font,\n          fill: labelModel.getTextColor(),\n          padding: paddings,\n          backgroundColor: bgColor\n        }),\n        // Lable should be over axisPointer.\n        z2: 10\n      };\n    } // Do not overflow ec container\n\n    function confineInContainer(position, width, height, api) {\n      var viewWidth = api.getWidth();\n      var viewHeight = api.getHeight();\n      position[0] = Math.min(position[0] + width, viewWidth) - width;\n      position[1] = Math.min(position[1] + height, viewHeight) - height;\n      position[0] = Math.max(position[0], 0);\n      position[1] = Math.max(position[1], 0);\n    }\n\n    function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {\n      value = axis.scale.parse(value);\n      var text = axis.scale.getLabel({\n        value: value\n      }, {\n        // If `precision` is set, width can be fixed (like '12.00500'), which\n        // helps to debounce when when moving label.\n        precision: opt.precision\n      });\n      var formatter = opt.formatter;\n\n      if (formatter) {\n        var params_1 = {\n          value: getAxisRawValue(axis, {\n            value: value\n          }),\n          axisDimension: axis.dim,\n          axisIndex: axis.index,\n          seriesData: []\n        };\n        each(seriesDataIndices, function (idxItem) {\n          var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);\n          var dataIndex = idxItem.dataIndexInside;\n          var dataParams = series && series.getDataParams(dataIndex);\n          dataParams && params_1.seriesData.push(dataParams);\n        });\n\n        if (isString(formatter)) {\n          text = formatter.replace('{value}', text);\n        } else if (isFunction(formatter)) {\n          text = formatter(params_1);\n        }\n      }\n\n      return text;\n    }\n    function getTransformedPosition(axis, value, layoutInfo) {\n      var transform = create$1();\n      rotate(transform, transform, layoutInfo.rotation);\n      translate(transform, transform, layoutInfo.position);\n      return applyTransform$1([axis.dataToCoord(value), (layoutInfo.labelOffset || 0) + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)], transform);\n    }\n    function buildCartesianSingleLabelElOption(value, elOption, layoutInfo, axisModel, axisPointerModel, api) {\n      // @ts-ignore\n      var textLayout = AxisBuilder.innerTextLayout(layoutInfo.rotation, 0, layoutInfo.labelDirection);\n      layoutInfo.labelMargin = axisPointerModel.get(['label', 'margin']);\n      buildLabelElOption(elOption, axisModel, axisPointerModel, api, {\n        position: getTransformedPosition(axisModel.axis, value, layoutInfo),\n        align: textLayout.textAlign,\n        verticalAlign: textLayout.textVerticalAlign\n      });\n    }\n    function makeLineShape(p1, p2, xDimIndex) {\n      xDimIndex = xDimIndex || 0;\n      return {\n        x1: p1[xDimIndex],\n        y1: p1[1 - xDimIndex],\n        x2: p2[xDimIndex],\n        y2: p2[1 - xDimIndex]\n      };\n    }\n    function makeRectShape(xy, wh, xDimIndex) {\n      xDimIndex = xDimIndex || 0;\n      return {\n        x: xy[xDimIndex],\n        y: xy[1 - xDimIndex],\n        width: wh[xDimIndex],\n        height: wh[1 - xDimIndex]\n      };\n    }\n    function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) {\n      return {\n        cx: cx,\n        cy: cy,\n        r0: r0,\n        r: r,\n        startAngle: startAngle,\n        endAngle: endAngle,\n        clockwise: true\n      };\n    }\n\n    var CartesianAxisPointer =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisPointer, _super);\n\n      function CartesianAxisPointer() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n      /**\n       * @override\n       */\n\n\n      CartesianAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {\n        var axis = axisModel.axis;\n        var grid = axis.grid;\n        var axisPointerType = axisPointerModel.get('type');\n        var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();\n        var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true));\n\n        if (axisPointerType && axisPointerType !== 'none') {\n          var elStyle = buildElStyle(axisPointerModel);\n          var pointerOption = pointerShapeBuilder[axisPointerType](axis, pixelValue, otherExtent);\n          pointerOption.style = elStyle;\n          elOption.graphicKey = pointerOption.type;\n          elOption.pointer = pointerOption;\n        }\n\n        var layoutInfo = layout$1(grid.model, axisModel);\n        buildCartesianSingleLabelElOption( // @ts-ignore\n        value, elOption, layoutInfo, axisModel, axisPointerModel, api);\n      };\n      /**\n       * @override\n       */\n\n\n      CartesianAxisPointer.prototype.getHandleTransform = function (value, axisModel, axisPointerModel) {\n        var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, {\n          labelInside: false\n        }); // @ts-ignore\n\n        layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']);\n        var pos = getTransformedPosition(axisModel.axis, value, layoutInfo);\n        return {\n          x: pos[0],\n          y: pos[1],\n          rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)\n        };\n      };\n      /**\n       * @override\n       */\n\n\n      CartesianAxisPointer.prototype.updateHandleTransform = function (transform, delta, axisModel, axisPointerModel) {\n        var axis = axisModel.axis;\n        var grid = axis.grid;\n        var axisExtent = axis.getGlobalExtent(true);\n        var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();\n        var dimIndex = axis.dim === 'x' ? 0 : 1;\n        var currPosition = [transform.x, transform.y];\n        currPosition[dimIndex] += delta[dimIndex];\n        currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);\n        currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);\n        var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;\n        var cursorPoint = [cursorOtherValue, cursorOtherValue];\n        cursorPoint[dimIndex] = currPosition[dimIndex]; // Make tooltip do not overlap axisPointer and in the middle of the grid.\n\n        var tooltipOptions = [{\n          verticalAlign: 'middle'\n        }, {\n          align: 'center'\n        }];\n        return {\n          x: currPosition[0],\n          y: currPosition[1],\n          rotation: transform.rotation,\n          cursorPoint: cursorPoint,\n          tooltipOption: tooltipOptions[dimIndex]\n        };\n      };\n\n      return CartesianAxisPointer;\n    }(BaseAxisPointer);\n\n    function getCartesian(grid, axis) {\n      var opt = {};\n      opt[axis.dim + 'AxisIndex'] = axis.index;\n      return grid.getCartesian(opt);\n    }\n\n    var pointerShapeBuilder = {\n      line: function (axis, pixelValue, otherExtent) {\n        var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getAxisDimIndex(axis));\n        return {\n          type: 'Line',\n          subPixelOptimize: true,\n          shape: targetShape\n        };\n      },\n      shadow: function (axis, pixelValue, otherExtent) {\n        var bandWidth = Math.max(1, axis.getBandWidth());\n        var span = otherExtent[1] - otherExtent[0];\n        return {\n          type: 'Rect',\n          shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getAxisDimIndex(axis))\n        };\n      }\n    };\n\n    function getAxisDimIndex(axis) {\n      return axis.dim === 'x' ? 0 : 1;\n    }\n\n    var AxisPointerModel =\n    /** @class */\n    function (_super) {\n      __extends(AxisPointerModel, _super);\n\n      function AxisPointerModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AxisPointerModel.type;\n        return _this;\n      }\n\n      AxisPointerModel.type = 'axisPointer';\n      AxisPointerModel.defaultOption = {\n        // 'auto' means that show when triggered by tooltip or handle.\n        show: 'auto',\n        // zlevel: 0,\n        z: 50,\n        type: 'line',\n        // axispointer triggered by tootip determine snap automatically,\n        // see `modelHelper`.\n        snap: false,\n        triggerTooltip: true,\n        value: null,\n        status: null,\n        link: [],\n        // Do not set 'auto' here, otherwise global animation: false\n        // will not effect at this axispointer.\n        animation: null,\n        animationDurationUpdate: 200,\n        lineStyle: {\n          color: '#B9BEC9',\n          width: 1,\n          type: 'dashed'\n        },\n        shadowStyle: {\n          color: 'rgba(210,219,238,0.2)'\n        },\n        label: {\n          show: true,\n          formatter: null,\n          precision: 'auto',\n          margin: 3,\n          color: '#fff',\n          padding: [5, 7, 5, 7],\n          backgroundColor: 'auto',\n          borderColor: null,\n          borderWidth: 0,\n          borderRadius: 3\n        },\n        handle: {\n          show: false,\n          // eslint-disable-next-line\n          icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z',\n          size: 45,\n          // handle margin is from symbol center to axis, which is stable when circular move.\n          margin: 50,\n          // color: '#1b8bbd'\n          // color: '#2f4554'\n          color: '#333',\n          shadowBlur: 3,\n          shadowColor: '#aaa',\n          shadowOffsetX: 0,\n          shadowOffsetY: 2,\n          // For mobile performance\n          throttle: 40\n        }\n      };\n      return AxisPointerModel;\n    }(ComponentModel);\n\n    var inner$b = makeInner();\n    var each$7 = each;\n    /**\n     * @param {string} key\n     * @param {module:echarts/ExtensionAPI} api\n     * @param {Function} handler\n     *      param: {string} currTrigger\n     *      param: {Array.<number>} point\n     */\n\n    function register(key, api, handler) {\n      if (env.node) {\n        return;\n      }\n\n      var zr = api.getZr();\n      inner$b(zr).records || (inner$b(zr).records = {});\n      initGlobalListeners(zr, api);\n      var record = inner$b(zr).records[key] || (inner$b(zr).records[key] = {});\n      record.handler = handler;\n    }\n\n    function initGlobalListeners(zr, api) {\n      if (inner$b(zr).initialized) {\n        return;\n      }\n\n      inner$b(zr).initialized = true;\n      useHandler('click', curry(doEnter, 'click'));\n      useHandler('mousemove', curry(doEnter, 'mousemove')); // useHandler('mouseout', onLeave);\n\n      useHandler('globalout', onLeave);\n\n      function useHandler(eventType, cb) {\n        zr.on(eventType, function (e) {\n          var dis = makeDispatchAction(api);\n          each$7(inner$b(zr).records, function (record) {\n            record && cb(record, e, dis.dispatchAction);\n          });\n          dispatchTooltipFinally(dis.pendings, api);\n        });\n      }\n    }\n\n    function dispatchTooltipFinally(pendings, api) {\n      var showLen = pendings.showTip.length;\n      var hideLen = pendings.hideTip.length;\n      var actuallyPayload;\n\n      if (showLen) {\n        actuallyPayload = pendings.showTip[showLen - 1];\n      } else if (hideLen) {\n        actuallyPayload = pendings.hideTip[hideLen - 1];\n      }\n\n      if (actuallyPayload) {\n        actuallyPayload.dispatchAction = null;\n        api.dispatchAction(actuallyPayload);\n      }\n    }\n\n    function onLeave(record, e, dispatchAction) {\n      record.handler('leave', null, dispatchAction);\n    }\n\n    function doEnter(currTrigger, record, e, dispatchAction) {\n      record.handler(currTrigger, e, dispatchAction);\n    }\n\n    function makeDispatchAction(api) {\n      var pendings = {\n        showTip: [],\n        hideTip: []\n      }; // FIXME\n      // better approach?\n      // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,\n      // which may be conflict, (axisPointer call showTip but tooltip call hideTip);\n      // So we have to add \"final stage\" to merge those dispatched actions.\n\n      var dispatchAction = function (payload) {\n        var pendingList = pendings[payload.type];\n\n        if (pendingList) {\n          pendingList.push(payload);\n        } else {\n          payload.dispatchAction = dispatchAction;\n          api.dispatchAction(payload);\n        }\n      };\n\n      return {\n        dispatchAction: dispatchAction,\n        pendings: pendings\n      };\n    }\n\n    function unregister(key, api) {\n      if (env.node) {\n        return;\n      }\n\n      var zr = api.getZr();\n      var record = (inner$b(zr).records || {})[key];\n\n      if (record) {\n        inner$b(zr).records[key] = null;\n      }\n    }\n\n    var AxisPointerView =\n    /** @class */\n    function (_super) {\n      __extends(AxisPointerView, _super);\n\n      function AxisPointerView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AxisPointerView.type;\n        return _this;\n      }\n\n      AxisPointerView.prototype.render = function (globalAxisPointerModel, ecModel, api) {\n        var globalTooltipModel = ecModel.getComponent('tooltip');\n        var triggerOn = globalAxisPointerModel.get('triggerOn') || globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click'; // Register global listener in AxisPointerView to enable\n        // AxisPointerView to be independent to Tooltip.\n\n        register('axisPointer', api, function (currTrigger, e, dispatchAction) {\n          // If 'none', it is not controlled by mouse totally.\n          if (triggerOn !== 'none' && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)) {\n            dispatchAction({\n              type: 'updateAxisPointer',\n              currTrigger: currTrigger,\n              x: e && e.offsetX,\n              y: e && e.offsetY\n            });\n          }\n        });\n      };\n\n      AxisPointerView.prototype.remove = function (ecModel, api) {\n        unregister('axisPointer', api);\n      };\n\n      AxisPointerView.prototype.dispose = function (ecModel, api) {\n        unregister('axisPointer', api);\n      };\n\n      AxisPointerView.type = 'axisPointer';\n      return AxisPointerView;\n    }(ComponentView);\n\n    /**\n     * @param finder contains {seriesIndex, dataIndex, dataIndexInside}\n     * @param ecModel\n     * @return  {point: [x, y], el: ...} point Will not be null.\n     */\n\n    function findPointFromSeries(finder, ecModel) {\n      var point = [];\n      var seriesIndex = finder.seriesIndex;\n      var seriesModel;\n\n      if (seriesIndex == null || !(seriesModel = ecModel.getSeriesByIndex(seriesIndex))) {\n        return {\n          point: []\n        };\n      }\n\n      var data = seriesModel.getData();\n      var dataIndex = queryDataIndex(data, finder);\n\n      if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) {\n        return {\n          point: []\n        };\n      }\n\n      var el = data.getItemGraphicEl(dataIndex);\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (seriesModel.getTooltipPosition) {\n        point = seriesModel.getTooltipPosition(dataIndex) || [];\n      } else if (coordSys && coordSys.dataToPoint) {\n        if (finder.isStacked) {\n          var baseAxis = coordSys.getBaseAxis();\n          var valueAxis = coordSys.getOtherAxis(baseAxis);\n          var valueAxisDim = valueAxis.dim;\n          var baseAxisDim = baseAxis.dim;\n          var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;\n          var baseDim = data.mapDimension(baseAxisDim);\n          var stackedData = [];\n          stackedData[baseDataOffset] = data.get(baseDim, dataIndex);\n          stackedData[1 - baseDataOffset] = data.get(data.getCalculationInfo('stackResultDimension'), dataIndex);\n          point = coordSys.dataToPoint(stackedData) || [];\n        } else {\n          point = coordSys.dataToPoint(data.getValues(map(coordSys.dimensions, function (dim) {\n            return data.mapDimension(dim);\n          }), dataIndex)) || [];\n        }\n      } else if (el) {\n        // Use graphic bounding rect\n        var rect = el.getBoundingRect().clone();\n        rect.applyTransform(el.transform);\n        point = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      }\n\n      return {\n        point: point,\n        el: el\n      };\n    }\n\n    var inner$c = makeInner();\n    /**\n     * Basic logic: check all axis, if they do not demand show/highlight,\n     * then hide/downplay them.\n     *\n     * @return content of event obj for echarts.connect.\n     */\n\n    function axisTrigger(payload, ecModel, api) {\n      var currTrigger = payload.currTrigger;\n      var point = [payload.x, payload.y];\n      var finder = payload;\n      var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);\n      var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; // Pending\n      // See #6121. But we are not able to reproduce it yet.\n\n      if (!coordSysAxesInfo) {\n        return;\n      }\n\n      if (illegalPoint(point)) {\n        // Used in the default behavior of `connection`: use the sample seriesIndex\n        // and dataIndex. And also used in the tooltipView trigger.\n        point = findPointFromSeries({\n          seriesIndex: finder.seriesIndex,\n          // Do not use dataIndexInside from other ec instance.\n          // FIXME: auto detect it?\n          dataIndex: finder.dataIndex\n        }, ecModel).point;\n      }\n\n      var isIllegalPoint = illegalPoint(point); // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).\n      // Notice: In this case, it is difficult to get the `point` (which is necessary to show\n      // tooltip, so if point is not given, we just use the point found by sample seriesIndex\n      // and dataIndex.\n\n      var inputAxesInfo = finder.axesInfo;\n      var axesInfo = coordSysAxesInfo.axesInfo;\n      var shouldHide = currTrigger === 'leave' || illegalPoint(point);\n      var outputPayload = {};\n      var showValueMap = {};\n      var dataByCoordSys = {\n        list: [],\n        map: {}\n      };\n      var updaters = {\n        showPointer: curry(showPointer, showValueMap),\n        showTooltip: curry(showTooltip, dataByCoordSys)\n      }; // Process for triggered axes.\n\n      each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {\n        // If a point given, it must be contained by the coordinate system.\n        var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);\n        each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {\n          var axis = axisInfo.axis;\n          var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); // If no inputAxesInfo, no axis is restricted.\n\n          if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {\n            var val = inputAxisInfo && inputAxisInfo.value;\n\n            if (val == null && !isIllegalPoint) {\n              val = axis.pointToData(point);\n            }\n\n            val != null && processOnAxis(axisInfo, val, updaters, false, outputPayload);\n          }\n        });\n      }); // Process for linked axes.\n\n      var linkTriggers = {};\n      each(axesInfo, function (tarAxisInfo, tarKey) {\n        var linkGroup = tarAxisInfo.linkGroup; // If axis has been triggered in the previous stage, it should not be triggered by link.\n\n        if (linkGroup && !showValueMap[tarKey]) {\n          each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {\n            var srcValItem = showValueMap[srcKey]; // If srcValItem exist, source axis is triggered, so link to target axis.\n\n            if (srcAxisInfo !== tarAxisInfo && srcValItem) {\n              var val = srcValItem.value;\n              linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo))));\n              linkTriggers[tarAxisInfo.key] = val;\n            }\n          });\n        }\n      });\n      each(linkTriggers, function (val, tarKey) {\n        processOnAxis(axesInfo[tarKey], val, updaters, true, outputPayload);\n      });\n      updateModelActually(showValueMap, axesInfo, outputPayload);\n      dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);\n      dispatchHighDownActually(axesInfo, dispatchAction, api);\n      return outputPayload;\n    }\n\n    function processOnAxis(axisInfo, newValue, updaters, noSnap, outputFinder) {\n      var axis = axisInfo.axis;\n\n      if (axis.scale.isBlank() || !axis.containData(newValue)) {\n        return;\n      }\n\n      if (!axisInfo.involveSeries) {\n        updaters.showPointer(axisInfo, newValue);\n        return;\n      } // Heavy calculation. So put it after axis.containData checking.\n\n\n      var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);\n      var payloadBatch = payloadInfo.payloadBatch;\n      var snapToValue = payloadInfo.snapToValue; // Fill content of event obj for echarts.connect.\n      // By default use the first involved series data as a sample to connect.\n\n      if (payloadBatch[0] && outputFinder.seriesIndex == null) {\n        extend(outputFinder, payloadBatch[0]);\n      } // If no linkSource input, this process is for collecting link\n      // target, where snap should not be accepted.\n\n\n      if (!noSnap && axisInfo.snap) {\n        if (axis.containData(snapToValue) && snapToValue != null) {\n          newValue = snapToValue;\n        }\n      }\n\n      updaters.showPointer(axisInfo, newValue, payloadBatch); // Tooltip should always be snapToValue, otherwise there will be\n      // incorrect \"axis value ~ series value\" mapping displayed in tooltip.\n\n      updaters.showTooltip(axisInfo, payloadInfo, snapToValue);\n    }\n\n    function buildPayloadsBySeries(value, axisInfo) {\n      var axis = axisInfo.axis;\n      var dim = axis.dim;\n      var snapToValue = value;\n      var payloadBatch = [];\n      var minDist = Number.MAX_VALUE;\n      var minDiff = -1;\n      each(axisInfo.seriesModels, function (series, idx) {\n        var dataDim = series.getData().mapDimensionsAll(dim);\n        var seriesNestestValue;\n        var dataIndices;\n\n        if (series.getAxisTooltipData) {\n          var result = series.getAxisTooltipData(dataDim, value, axis);\n          dataIndices = result.dataIndices;\n          seriesNestestValue = result.nestestValue;\n        } else {\n          dataIndices = series.getData().indicesOfNearest(dataDim[0], value, // Add a threshold to avoid find the wrong dataIndex\n          // when data length is not same.\n          // false,\n          axis.type === 'category' ? 0.5 : null);\n\n          if (!dataIndices.length) {\n            return;\n          }\n\n          seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);\n        }\n\n        if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {\n          return;\n        }\n\n        var diff = value - seriesNestestValue;\n        var dist = Math.abs(diff); // Consider category case\n\n        if (dist <= minDist) {\n          if (dist < minDist || diff >= 0 && minDiff < 0) {\n            minDist = dist;\n            minDiff = diff;\n            snapToValue = seriesNestestValue;\n            payloadBatch.length = 0;\n          }\n\n          each(dataIndices, function (dataIndex) {\n            payloadBatch.push({\n              seriesIndex: series.seriesIndex,\n              dataIndexInside: dataIndex,\n              dataIndex: series.getData().getRawIndex(dataIndex)\n            });\n          });\n        }\n      });\n      return {\n        payloadBatch: payloadBatch,\n        snapToValue: snapToValue\n      };\n    }\n\n    function showPointer(showValueMap, axisInfo, value, payloadBatch) {\n      showValueMap[axisInfo.key] = {\n        value: value,\n        payloadBatch: payloadBatch\n      };\n    }\n\n    function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {\n      var payloadBatch = payloadInfo.payloadBatch;\n      var axis = axisInfo.axis;\n      var axisModel = axis.model;\n      var axisPointerModel = axisInfo.axisPointerModel; // If no data, do not create anything in dataByCoordSys,\n      // whose length will be used to judge whether dispatch action.\n\n      if (!axisInfo.triggerTooltip || !payloadBatch.length) {\n        return;\n      }\n\n      var coordSysModel = axisInfo.coordSys.model;\n      var coordSysKey = makeKey(coordSysModel);\n      var coordSysItem = dataByCoordSys.map[coordSysKey];\n\n      if (!coordSysItem) {\n        coordSysItem = dataByCoordSys.map[coordSysKey] = {\n          coordSysId: coordSysModel.id,\n          coordSysIndex: coordSysModel.componentIndex,\n          coordSysType: coordSysModel.type,\n          coordSysMainType: coordSysModel.mainType,\n          dataByAxis: []\n        };\n        dataByCoordSys.list.push(coordSysItem);\n      }\n\n      coordSysItem.dataByAxis.push({\n        axisDim: axis.dim,\n        axisIndex: axisModel.componentIndex,\n        axisType: axisModel.type,\n        axisId: axisModel.id,\n        value: value,\n        // Caustion: viewHelper.getValueLabel is actually on \"view stage\", which\n        // depends that all models have been updated. So it should not be performed\n        // here. Considering axisPointerModel used here is volatile, which is hard\n        // to be retrieve in TooltipView, we prepare parameters here.\n        valueLabelOpt: {\n          precision: axisPointerModel.get(['label', 'precision']),\n          formatter: axisPointerModel.get(['label', 'formatter'])\n        },\n        seriesDataIndices: payloadBatch.slice()\n      });\n    }\n\n    function updateModelActually(showValueMap, axesInfo, outputPayload) {\n      var outputAxesInfo = outputPayload.axesInfo = []; // Basic logic: If no 'show' required, 'hide' this axisPointer.\n\n      each(axesInfo, function (axisInfo, key) {\n        var option = axisInfo.axisPointerModel.option;\n        var valItem = showValueMap[key];\n\n        if (valItem) {\n          !axisInfo.useHandle && (option.status = 'show');\n          option.value = valItem.value; // For label formatter param and highlight.\n\n          option.seriesDataIndices = (valItem.payloadBatch || []).slice();\n        } // When always show (e.g., handle used), remain\n        // original value and status.\n        else {\n            // If hide, value still need to be set, consider\n            // click legend to toggle axis blank.\n            !axisInfo.useHandle && (option.status = 'hide');\n          } // If status is 'hide', should be no info in payload.\n\n\n        option.status === 'show' && outputAxesInfo.push({\n          axisDim: axisInfo.axis.dim,\n          axisIndex: axisInfo.axis.model.componentIndex,\n          value: option.value\n        });\n      });\n    }\n\n    function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {\n      // Basic logic: If no showTip required, hideTip will be dispatched.\n      if (illegalPoint(point) || !dataByCoordSys.list.length) {\n        dispatchAction({\n          type: 'hideTip'\n        });\n        return;\n      } // In most case only one axis (or event one series is used). It is\n      // convinient to fetch payload.seriesIndex and payload.dataIndex\n      // dirtectly. So put the first seriesIndex and dataIndex of the first\n      // axis on the payload.\n\n\n      var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};\n      dispatchAction({\n        type: 'showTip',\n        escapeConnect: true,\n        x: point[0],\n        y: point[1],\n        tooltipOption: payload.tooltipOption,\n        position: payload.position,\n        dataIndexInside: sampleItem.dataIndexInside,\n        dataIndex: sampleItem.dataIndex,\n        seriesIndex: sampleItem.seriesIndex,\n        dataByCoordSys: dataByCoordSys.list\n      });\n    }\n\n    function dispatchHighDownActually(axesInfo, dispatchAction, api) {\n      // FIXME\n      // highlight status modification shoule be a stage of main process?\n      // (Consider confilct (e.g., legend and axisPointer) and setOption)\n      var zr = api.getZr();\n      var highDownKey = 'axisPointerLastHighlights';\n      var lastHighlights = inner$c(zr)[highDownKey] || {};\n      var newHighlights = inner$c(zr)[highDownKey] = {}; // Update highlight/downplay status according to axisPointer model.\n      // Build hash map and remove duplicate incidentally.\n\n      each(axesInfo, function (axisInfo, key) {\n        var option = axisInfo.axisPointerModel.option;\n        option.status === 'show' && each(option.seriesDataIndices, function (batchItem) {\n          var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;\n          newHighlights[key] = batchItem;\n        });\n      }); // Diff.\n\n      var toHighlight = [];\n      var toDownplay = [];\n      each(lastHighlights, function (batchItem, key) {\n        !newHighlights[key] && toDownplay.push(batchItem);\n      });\n      each(newHighlights, function (batchItem, key) {\n        !lastHighlights[key] && toHighlight.push(batchItem);\n      });\n      toDownplay.length && api.dispatchAction({\n        type: 'downplay',\n        escapeConnect: true,\n        // Not blur others when highlight in axisPointer.\n        notBlur: true,\n        batch: toDownplay\n      });\n      toHighlight.length && api.dispatchAction({\n        type: 'highlight',\n        escapeConnect: true,\n        // Not blur others when highlight in axisPointer.\n        notBlur: true,\n        batch: toHighlight\n      });\n    }\n\n    function findInputAxisInfo(inputAxesInfo, axisInfo) {\n      for (var i = 0; i < (inputAxesInfo || []).length; i++) {\n        var inputAxisInfo = inputAxesInfo[i];\n\n        if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) {\n          return inputAxisInfo;\n        }\n      }\n    }\n\n    function makeMapperParam(axisInfo) {\n      var axisModel = axisInfo.axis.model;\n      var item = {};\n      var dim = item.axisDim = axisInfo.axis.dim;\n      item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;\n      item.axisName = item[dim + 'AxisName'] = axisModel.name;\n      item.axisId = item[dim + 'AxisId'] = axisModel.id;\n      return item;\n    }\n\n    function illegalPoint(point) {\n      return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);\n    }\n\n    function install$s(registers) {\n      // CartesianAxisPointer is not supposed to be required here. But consider\n      // echarts.simple.js and online build tooltip, which only require gridSimple,\n      // CartesianAxisPointer should be able to required somewhere.\n      AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer);\n      registers.registerComponentModel(AxisPointerModel);\n      registers.registerComponentView(AxisPointerView);\n      registers.registerPreprocessor(function (option) {\n        // Always has a global axisPointerModel for default setting.\n        if (option) {\n          (!option.axisPointer || option.axisPointer.length === 0) && (option.axisPointer = {});\n          var link = option.axisPointer.link; // Normalize to array to avoid object mergin. But if link\n          // is not set, remain null/undefined, otherwise it will\n          // override existent link setting.\n\n          if (link && !isArray(link)) {\n            option.axisPointer.link = [link];\n          }\n        }\n      }); // This process should proformed after coordinate systems created\n      // and series data processed. So put it on statistic processing stage.\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) {\n        // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.\n        // allAxesInfo should be updated when setOption performed.\n        ecModel.getComponent('axisPointer').coordSysAxesInfo = collect(ecModel, api);\n      }); // Broadcast to all views.\n\n      registers.registerAction({\n        type: 'updateAxisPointer',\n        event: 'updateAxisPointer',\n        update: ':updateAxisPointer'\n      }, axisTrigger);\n    }\n\n    function install$t(registers) {\n      use(install$5);\n      use(install$s);\n    }\n\n    var PolarAxisPointer =\n    /** @class */\n    function (_super) {\n      __extends(PolarAxisPointer, _super);\n\n      function PolarAxisPointer() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n      /**\n       * @override\n       */\n\n\n      PolarAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {\n        var axis = axisModel.axis;\n\n        if (axis.dim === 'angle') {\n          this.animationThreshold = Math.PI / 18;\n        }\n\n        var polar = axis.polar;\n        var otherAxis = polar.getOtherAxis(axis);\n        var otherExtent = otherAxis.getExtent();\n        var coordValue = axis.dataToCoord(value);\n        var axisPointerType = axisPointerModel.get('type');\n\n        if (axisPointerType && axisPointerType !== 'none') {\n          var elStyle = buildElStyle(axisPointerModel);\n          var pointerOption = pointerShapeBuilder$1[axisPointerType](axis, polar, coordValue, otherExtent);\n          pointerOption.style = elStyle;\n          elOption.graphicKey = pointerOption.type;\n          elOption.pointer = pointerOption;\n        }\n\n        var labelMargin = axisPointerModel.get(['label', 'margin']);\n        var labelPos = getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin);\n        buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos);\n      };\n\n      return PolarAxisPointer;\n    }(BaseAxisPointer);\n\n    function getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin) {\n      var axis = axisModel.axis;\n      var coord = axis.dataToCoord(value);\n      var axisAngle = polar.getAngleAxis().getExtent()[0];\n      axisAngle = axisAngle / 180 * Math.PI;\n      var radiusExtent = polar.getRadiusAxis().getExtent();\n      var position;\n      var align;\n      var verticalAlign;\n\n      if (axis.dim === 'radius') {\n        var transform = create$1();\n        rotate(transform, transform, axisAngle);\n        translate(transform, transform, [polar.cx, polar.cy]);\n        position = applyTransform$1([coord, -labelMargin], transform);\n        var labelRotation = axisModel.getModel('axisLabel').get('rotate') || 0; // @ts-ignore\n\n        var labelLayout = AxisBuilder.innerTextLayout(axisAngle, labelRotation * Math.PI / 180, -1);\n        align = labelLayout.textAlign;\n        verticalAlign = labelLayout.textVerticalAlign;\n      } else {\n        // angle axis\n        var r = radiusExtent[1];\n        position = polar.coordToPoint([r + labelMargin, coord]);\n        var cx = polar.cx;\n        var cy = polar.cy;\n        align = Math.abs(position[0] - cx) / r < 0.3 ? 'center' : position[0] > cx ? 'left' : 'right';\n        verticalAlign = Math.abs(position[1] - cy) / r < 0.3 ? 'middle' : position[1] > cy ? 'top' : 'bottom';\n      }\n\n      return {\n        position: position,\n        align: align,\n        verticalAlign: verticalAlign\n      };\n    }\n\n    var pointerShapeBuilder$1 = {\n      line: function (axis, polar, coordValue, otherExtent) {\n        return axis.dim === 'angle' ? {\n          type: 'Line',\n          shape: makeLineShape(polar.coordToPoint([otherExtent[0], coordValue]), polar.coordToPoint([otherExtent[1], coordValue]))\n        } : {\n          type: 'Circle',\n          shape: {\n            cx: polar.cx,\n            cy: polar.cy,\n            r: coordValue\n          }\n        };\n      },\n      shadow: function (axis, polar, coordValue, otherExtent) {\n        var bandWidth = Math.max(1, axis.getBandWidth());\n        var radian = Math.PI / 180;\n        return axis.dim === 'angle' ? {\n          type: 'Sector',\n          shape: makeSectorShape(polar.cx, polar.cy, otherExtent[0], otherExtent[1], // In ECharts y is negative if angle is positive\n          (-coordValue - bandWidth / 2) * radian, (-coordValue + bandWidth / 2) * radian)\n        } : {\n          type: 'Sector',\n          shape: makeSectorShape(polar.cx, polar.cy, coordValue - bandWidth / 2, coordValue + bandWidth / 2, 0, Math.PI * 2)\n        };\n      }\n    };\n\n    var PolarModel =\n    /** @class */\n    function (_super) {\n      __extends(PolarModel, _super);\n\n      function PolarModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = PolarModel.type;\n        return _this;\n      }\n\n      PolarModel.prototype.findAxisModel = function (axisType) {\n        var foundAxisModel;\n        var ecModel = this.ecModel;\n        ecModel.eachComponent(axisType, function (axisModel) {\n          if (axisModel.getCoordSysModel() === this) {\n            foundAxisModel = axisModel;\n          }\n        }, this);\n        return foundAxisModel;\n      };\n\n      PolarModel.type = 'polar';\n      PolarModel.dependencies = ['radiusAxis', 'angleAxis'];\n      PolarModel.defaultOption = {\n        // zlevel: 0,\n        z: 0,\n        center: ['50%', '50%'],\n        radius: '80%'\n      };\n      return PolarModel;\n    }(ComponentModel);\n\n    var PolarAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(PolarAxisModel, _super);\n\n      function PolarAxisModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      PolarAxisModel.prototype.getCoordSysModel = function () {\n        return this.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n      };\n\n      PolarAxisModel.type = 'polarAxis';\n      return PolarAxisModel;\n    }(ComponentModel);\n\n    mixin(PolarAxisModel, AxisModelCommonMixin);\n\n    var AngleAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(AngleAxisModel, _super);\n\n      function AngleAxisModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AngleAxisModel.type;\n        return _this;\n      }\n\n      AngleAxisModel.type = 'angleAxis';\n      return AngleAxisModel;\n    }(PolarAxisModel);\n\n    var RadiusAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(RadiusAxisModel, _super);\n\n      function RadiusAxisModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = RadiusAxisModel.type;\n        return _this;\n      }\n\n      RadiusAxisModel.type = 'radiusAxis';\n      return RadiusAxisModel;\n    }(PolarAxisModel);\n\n    var RadiusAxis =\n    /** @class */\n    function (_super) {\n      __extends(RadiusAxis, _super);\n\n      function RadiusAxis(scale, radiusExtent) {\n        return _super.call(this, 'radius', scale, radiusExtent) || this;\n      }\n\n      RadiusAxis.prototype.pointToData = function (point, clamp) {\n        return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1];\n      };\n\n      return RadiusAxis;\n    }(Axis);\n\n    RadiusAxis.prototype.dataToRadius = Axis.prototype.dataToCoord;\n    RadiusAxis.prototype.radiusToData = Axis.prototype.coordToData;\n\n    var inner$d = makeInner();\n\n    var AngleAxis =\n    /** @class */\n    function (_super) {\n      __extends(AngleAxis, _super);\n\n      function AngleAxis(scale, angleExtent) {\n        return _super.call(this, 'angle', scale, angleExtent || [0, 360]) || this;\n      }\n\n      AngleAxis.prototype.pointToData = function (point, clamp) {\n        return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1];\n      };\n      /**\n       * Only be called in category axis.\n       * Angle axis uses text height to decide interval\n       *\n       * @override\n       * @return {number} Auto interval for cateogry axis tick and label\n       */\n\n\n      AngleAxis.prototype.calculateCategoryInterval = function () {\n        var axis = this;\n        var labelModel = axis.getLabelModel();\n        var ordinalScale = axis.scale;\n        var ordinalExtent = ordinalScale.getExtent(); // Providing this method is for optimization:\n        // avoid generating a long array by `getTicks`\n        // in large category data case.\n\n        var tickCount = ordinalScale.count();\n\n        if (ordinalExtent[1] - ordinalExtent[0] < 1) {\n          return 0;\n        }\n\n        var tickValue = ordinalExtent[0];\n        var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);\n        var unitH = Math.abs(unitSpan); // Not precise, just use height as text width\n        // and each distance from axis line yet.\n\n        var rect = getBoundingRect(tickValue == null ? '' : tickValue + '', labelModel.getFont(), 'center', 'top');\n        var maxH = Math.max(rect.height, 7);\n        var dh = maxH / unitH; // 0/0 is NaN, 1/0 is Infinity.\n\n        isNaN(dh) && (dh = Infinity);\n        var interval = Math.max(0, Math.floor(dh));\n        var cache = inner$d(axis.model);\n        var lastAutoInterval = cache.lastAutoInterval;\n        var lastTickCount = cache.lastTickCount; // Use cache to keep interval stable while moving zoom window,\n        // otherwise the calculated interval might jitter when the zoom\n        // window size is close to the interval-changing size.\n\n        if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 // Always choose the bigger one, otherwise the critical\n        // point is not the same when zooming in or zooming out.\n        && lastAutoInterval > interval) {\n          interval = lastAutoInterval;\n        } // Only update cache if cache not used, otherwise the\n        // changing of interval is too insensitive.\n        else {\n            cache.lastTickCount = tickCount;\n            cache.lastAutoInterval = interval;\n          }\n\n        return interval;\n      };\n\n      return AngleAxis;\n    }(Axis);\n\n    AngleAxis.prototype.dataToAngle = Axis.prototype.dataToCoord;\n    AngleAxis.prototype.angleToData = Axis.prototype.coordToData;\n\n    var polarDimensions = ['radius', 'angle'];\n\n    var Polar =\n    /** @class */\n    function () {\n      function Polar(name) {\n        this.dimensions = polarDimensions;\n        this.type = 'polar';\n        /**\n         * x of polar center\n         */\n\n        this.cx = 0;\n        /**\n         * y of polar center\n         */\n\n        this.cy = 0;\n        this._radiusAxis = new RadiusAxis();\n        this._angleAxis = new AngleAxis();\n        this.axisPointerEnabled = true;\n        this.name = name || '';\n        this._radiusAxis.polar = this._angleAxis.polar = this;\n      }\n      /**\n       * If contain coord\n       */\n\n\n      Polar.prototype.containPoint = function (point) {\n        var coord = this.pointToCoord(point);\n        return this._radiusAxis.contain(coord[0]) && this._angleAxis.contain(coord[1]);\n      };\n      /**\n       * If contain data\n       */\n\n\n      Polar.prototype.containData = function (data) {\n        return this._radiusAxis.containData(data[0]) && this._angleAxis.containData(data[1]);\n      };\n\n      Polar.prototype.getAxis = function (dim) {\n        var key = '_' + dim + 'Axis';\n        return this[key];\n      };\n\n      Polar.prototype.getAxes = function () {\n        return [this._radiusAxis, this._angleAxis];\n      };\n      /**\n       * Get axes by type of scale\n       */\n\n\n      Polar.prototype.getAxesByScale = function (scaleType) {\n        var axes = [];\n        var angleAxis = this._angleAxis;\n        var radiusAxis = this._radiusAxis;\n        angleAxis.scale.type === scaleType && axes.push(angleAxis);\n        radiusAxis.scale.type === scaleType && axes.push(radiusAxis);\n        return axes;\n      };\n\n      Polar.prototype.getAngleAxis = function () {\n        return this._angleAxis;\n      };\n\n      Polar.prototype.getRadiusAxis = function () {\n        return this._radiusAxis;\n      };\n\n      Polar.prototype.getOtherAxis = function (axis) {\n        var angleAxis = this._angleAxis;\n        return axis === angleAxis ? this._radiusAxis : angleAxis;\n      };\n      /**\n       * Base axis will be used on stacking.\n       *\n       */\n\n\n      Polar.prototype.getBaseAxis = function () {\n        return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAngleAxis();\n      };\n\n      Polar.prototype.getTooltipAxes = function (dim) {\n        var baseAxis = dim != null && dim !== 'auto' ? this.getAxis(dim) : this.getBaseAxis();\n        return {\n          baseAxes: [baseAxis],\n          otherAxes: [this.getOtherAxis(baseAxis)]\n        };\n      };\n      /**\n       * Convert a single data item to (x, y) point.\n       * Parameter data is an array which the first element is radius and the second is angle\n       */\n\n\n      Polar.prototype.dataToPoint = function (data, clamp) {\n        return this.coordToPoint([this._radiusAxis.dataToRadius(data[0], clamp), this._angleAxis.dataToAngle(data[1], clamp)]);\n      };\n      /**\n       * Convert a (x, y) point to data\n       */\n\n\n      Polar.prototype.pointToData = function (point, clamp) {\n        var coord = this.pointToCoord(point);\n        return [this._radiusAxis.radiusToData(coord[0], clamp), this._angleAxis.angleToData(coord[1], clamp)];\n      };\n      /**\n       * Convert a (x, y) point to (radius, angle) coord\n       */\n\n\n      Polar.prototype.pointToCoord = function (point) {\n        var dx = point[0] - this.cx;\n        var dy = point[1] - this.cy;\n        var angleAxis = this.getAngleAxis();\n        var extent = angleAxis.getExtent();\n        var minAngle = Math.min(extent[0], extent[1]);\n        var maxAngle = Math.max(extent[0], extent[1]); // Fix fixed extent in polarCreator\n        // FIXME\n\n        angleAxis.inverse ? minAngle = maxAngle - 360 : maxAngle = minAngle + 360;\n        var radius = Math.sqrt(dx * dx + dy * dy);\n        dx /= radius;\n        dy /= radius;\n        var radian = Math.atan2(-dy, dx) / Math.PI * 180; // move to angleExtent\n\n        var dir = radian < minAngle ? 1 : -1;\n\n        while (radian < minAngle || radian > maxAngle) {\n          radian += dir * 360;\n        }\n\n        return [radius, radian];\n      };\n      /**\n       * Convert a (radius, angle) coord to (x, y) point\n       */\n\n\n      Polar.prototype.coordToPoint = function (coord) {\n        var radius = coord[0];\n        var radian = coord[1] / 180 * Math.PI;\n        var x = Math.cos(radian) * radius + this.cx; // Inverse the y\n\n        var y = -Math.sin(radian) * radius + this.cy;\n        return [x, y];\n      };\n      /**\n       * Get ring area of cartesian.\n       * Area will have a contain function to determine if a point is in the coordinate system.\n       */\n\n\n      Polar.prototype.getArea = function () {\n        var angleAxis = this.getAngleAxis();\n        var radiusAxis = this.getRadiusAxis();\n        var radiusExtent = radiusAxis.getExtent().slice();\n        radiusExtent[0] > radiusExtent[1] && radiusExtent.reverse();\n        var angleExtent = angleAxis.getExtent();\n        var RADIAN = Math.PI / 180;\n        return {\n          cx: this.cx,\n          cy: this.cy,\n          r0: radiusExtent[0],\n          r: radiusExtent[1],\n          startAngle: -angleExtent[0] * RADIAN,\n          endAngle: -angleExtent[1] * RADIAN,\n          clockwise: angleAxis.inverse,\n          contain: function (x, y) {\n            // It's a ring shape.\n            // Start angle and end angle don't matter\n            var dx = x - this.cx;\n            var dy = y - this.cy; // minus a tiny value 1e-4 to avoid being clipped unexpectedly\n\n            var d2 = dx * dx + dy * dy - 1e-4;\n            var r = this.r;\n            var r0 = this.r0;\n            return d2 <= r * r && d2 >= r0 * r0;\n          }\n        };\n      };\n\n      Polar.prototype.convertToPixel = function (ecModel, finder, value) {\n        var coordSys = getCoordSys$2(finder);\n        return coordSys === this ? this.dataToPoint(value) : null;\n      };\n\n      Polar.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n        var coordSys = getCoordSys$2(finder);\n        return coordSys === this ? this.pointToData(pixel) : null;\n      };\n\n      return Polar;\n    }();\n\n    function getCoordSys$2(finder) {\n      var seriesModel = finder.seriesModel;\n      var polarModel = finder.polarModel;\n      return polarModel && polarModel.coordinateSystem || seriesModel && seriesModel.coordinateSystem;\n    }\n\n    /**\n     * Resize method bound to the polar\n     */\n\n    function resizePolar(polar, polarModel, api) {\n      var center = polarModel.get('center');\n      var width = api.getWidth();\n      var height = api.getHeight();\n      polar.cx = parsePercent$1(center[0], width);\n      polar.cy = parsePercent$1(center[1], height);\n      var radiusAxis = polar.getRadiusAxis();\n      var size = Math.min(width, height) / 2;\n      var radius = polarModel.get('radius');\n\n      if (radius == null) {\n        radius = [0, '100%'];\n      } else if (!isArray(radius)) {\n        // r0 = 0\n        radius = [0, radius];\n      }\n\n      var parsedRadius = [parsePercent$1(radius[0], size), parsePercent$1(radius[1], size)];\n      radiusAxis.inverse ? radiusAxis.setExtent(parsedRadius[1], parsedRadius[0]) : radiusAxis.setExtent(parsedRadius[0], parsedRadius[1]);\n    }\n    /**\n     * Update polar\n     */\n\n\n    function updatePolarScale(ecModel, api) {\n      var polar = this;\n      var angleAxis = polar.getAngleAxis();\n      var radiusAxis = polar.getRadiusAxis(); // Reset scale\n\n      angleAxis.scale.setExtent(Infinity, -Infinity);\n      radiusAxis.scale.setExtent(Infinity, -Infinity);\n      ecModel.eachSeries(function (seriesModel) {\n        if (seriesModel.coordinateSystem === polar) {\n          var data_1 = seriesModel.getData();\n          each(getDataDimensionsOnAxis(data_1, 'radius'), function (dim) {\n            radiusAxis.scale.unionExtentFromData(data_1, dim);\n          });\n          each(getDataDimensionsOnAxis(data_1, 'angle'), function (dim) {\n            angleAxis.scale.unionExtentFromData(data_1, dim);\n          });\n        }\n      });\n      niceScaleExtent(angleAxis.scale, angleAxis.model);\n      niceScaleExtent(radiusAxis.scale, radiusAxis.model); // Fix extent of category angle axis\n\n      if (angleAxis.type === 'category' && !angleAxis.onBand) {\n        var extent = angleAxis.getExtent();\n        var diff = 360 / angleAxis.scale.count();\n        angleAxis.inverse ? extent[1] += diff : extent[1] -= diff;\n        angleAxis.setExtent(extent[0], extent[1]);\n      }\n    }\n\n    function isAngleAxisModel(axisModel) {\n      return axisModel.mainType === 'angleAxis';\n    }\n    /**\n     * Set common axis properties\n     */\n\n\n    function setAxis(axis, axisModel) {\n      axis.type = axisModel.get('type');\n      axis.scale = createScaleByModel(axisModel);\n      axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category';\n      axis.inverse = axisModel.get('inverse');\n\n      if (isAngleAxisModel(axisModel)) {\n        axis.inverse = axis.inverse !== axisModel.get('clockwise');\n        var startAngle = axisModel.get('startAngle');\n        axis.setExtent(startAngle, startAngle + (axis.inverse ? -360 : 360));\n      } // Inject axis instance\n\n\n      axisModel.axis = axis;\n      axis.model = axisModel;\n    }\n\n    var polarCreator = {\n      dimensions: polarDimensions,\n      create: function (ecModel, api) {\n        var polarList = [];\n        ecModel.eachComponent('polar', function (polarModel, idx) {\n          var polar = new Polar(idx + ''); // Inject resize and update method\n\n          polar.update = updatePolarScale;\n          var radiusAxis = polar.getRadiusAxis();\n          var angleAxis = polar.getAngleAxis();\n          var radiusAxisModel = polarModel.findAxisModel('radiusAxis');\n          var angleAxisModel = polarModel.findAxisModel('angleAxis');\n          setAxis(radiusAxis, radiusAxisModel);\n          setAxis(angleAxis, angleAxisModel);\n          resizePolar(polar, polarModel, api);\n          polarList.push(polar);\n          polarModel.coordinateSystem = polar;\n          polar.model = polarModel;\n        }); // Inject coordinateSystem to series\n\n        ecModel.eachSeries(function (seriesModel) {\n          if (seriesModel.get('coordinateSystem') === 'polar') {\n            var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n\n            if (\"development\" !== 'production') {\n              if (!polarModel) {\n                throw new Error('Polar \"' + retrieve(seriesModel.get('polarIndex'), seriesModel.get('polarId'), 0) + '\" not found');\n              }\n            }\n\n            seriesModel.coordinateSystem = polarModel.coordinateSystem;\n          }\n        });\n        return polarList;\n      }\n    };\n\n    var elementList$1 = ['axisLine', 'axisLabel', 'axisTick', 'minorTick', 'splitLine', 'minorSplitLine', 'splitArea'];\n\n    function getAxisLineShape(polar, rExtent, angle) {\n      rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse());\n      var start = polar.coordToPoint([rExtent[0], angle]);\n      var end = polar.coordToPoint([rExtent[1], angle]);\n      return {\n        x1: start[0],\n        y1: start[1],\n        x2: end[0],\n        y2: end[1]\n      };\n    }\n\n    function getRadiusIdx(polar) {\n      var radiusAxis = polar.getRadiusAxis();\n      return radiusAxis.inverse ? 0 : 1;\n    } // Remove the last tick which will overlap the first tick\n\n\n    function fixAngleOverlap(list) {\n      var firstItem = list[0];\n      var lastItem = list[list.length - 1];\n\n      if (firstItem && lastItem && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4) {\n        list.pop();\n      }\n    }\n\n    var AngleAxisView =\n    /** @class */\n    function (_super) {\n      __extends(AngleAxisView, _super);\n\n      function AngleAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AngleAxisView.type;\n        _this.axisPointerClass = 'PolarAxisPointer';\n        return _this;\n      }\n\n      AngleAxisView.prototype.render = function (angleAxisModel, ecModel) {\n        this.group.removeAll();\n\n        if (!angleAxisModel.get('show')) {\n          return;\n        }\n\n        var angleAxis = angleAxisModel.axis;\n        var polar = angleAxis.polar;\n        var radiusExtent = polar.getRadiusAxis().getExtent();\n        var ticksAngles = angleAxis.getTicksCoords();\n        var minorTickAngles = angleAxis.getMinorTicksCoords();\n        var labels = map(angleAxis.getViewLabels(), function (labelItem) {\n          labelItem = clone(labelItem);\n          var scale = angleAxis.scale;\n          var tickValue = scale.type === 'ordinal' ? scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue;\n          labelItem.coord = angleAxis.dataToCoord(tickValue);\n          return labelItem;\n        });\n        fixAngleOverlap(labels);\n        fixAngleOverlap(ticksAngles);\n        each(elementList$1, function (name) {\n          if (angleAxisModel.get([name, 'show']) && (!angleAxis.scale.isBlank() || name === 'axisLine')) {\n            angelAxisElementsBuilders[name](this.group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels);\n          }\n        }, this);\n      };\n\n      AngleAxisView.type = 'angleAxis';\n      return AngleAxisView;\n    }(AxisView);\n\n    var angelAxisElementsBuilders = {\n      axisLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n        var lineStyleModel = angleAxisModel.getModel(['axisLine', 'lineStyle']); // extent id of the axis radius (r0 and r)\n\n        var rId = getRadiusIdx(polar);\n        var r0Id = rId ? 0 : 1;\n        var shape;\n\n        if (radiusExtent[r0Id] === 0) {\n          shape = new Circle({\n            shape: {\n              cx: polar.cx,\n              cy: polar.cy,\n              r: radiusExtent[rId]\n            },\n            style: lineStyleModel.getLineStyle(),\n            z2: 1,\n            silent: true\n          });\n        } else {\n          shape = new Ring({\n            shape: {\n              cx: polar.cx,\n              cy: polar.cy,\n              r: radiusExtent[rId],\n              r0: radiusExtent[r0Id]\n            },\n            style: lineStyleModel.getLineStyle(),\n            z2: 1,\n            silent: true\n          });\n        }\n\n        shape.style.fill = null;\n        group.add(shape);\n      },\n      axisTick: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n        var tickModel = angleAxisModel.getModel('axisTick');\n        var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length');\n        var radius = radiusExtent[getRadiusIdx(polar)];\n        var lines = map(ticksAngles, function (tickAngleItem) {\n          return new Line({\n            shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord)\n          });\n        });\n        group.add(mergePath$1(lines, {\n          style: defaults(tickModel.getModel('lineStyle').getLineStyle(), {\n            stroke: angleAxisModel.get(['axisLine', 'lineStyle', 'color'])\n          })\n        }));\n      },\n      minorTick: function (group, angleAxisModel, polar, tickAngles, minorTickAngles, radiusExtent) {\n        if (!minorTickAngles.length) {\n          return;\n        }\n\n        var tickModel = angleAxisModel.getModel('axisTick');\n        var minorTickModel = angleAxisModel.getModel('minorTick');\n        var tickLen = (tickModel.get('inside') ? -1 : 1) * minorTickModel.get('length');\n        var radius = radiusExtent[getRadiusIdx(polar)];\n        var lines = [];\n\n        for (var i = 0; i < minorTickAngles.length; i++) {\n          for (var k = 0; k < minorTickAngles[i].length; k++) {\n            lines.push(new Line({\n              shape: getAxisLineShape(polar, [radius, radius + tickLen], minorTickAngles[i][k].coord)\n            }));\n          }\n        }\n\n        group.add(mergePath$1(lines, {\n          style: defaults(minorTickModel.getModel('lineStyle').getLineStyle(), defaults(tickModel.getLineStyle(), {\n            stroke: angleAxisModel.get(['axisLine', 'lineStyle', 'color'])\n          }))\n        }));\n      },\n      axisLabel: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels) {\n        var rawCategoryData = angleAxisModel.getCategories(true);\n        var commonLabelModel = angleAxisModel.getModel('axisLabel');\n        var labelMargin = commonLabelModel.get('margin');\n        var triggerEvent = angleAxisModel.get('triggerEvent'); // Use length of ticksAngles because it may remove the last tick to avoid overlapping\n\n        each(labels, function (labelItem, idx) {\n          var labelModel = commonLabelModel;\n          var tickValue = labelItem.tickValue;\n          var r = radiusExtent[getRadiusIdx(polar)];\n          var p = polar.coordToPoint([r + labelMargin, labelItem.coord]);\n          var cx = polar.cx;\n          var cy = polar.cy;\n          var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3 ? 'center' : p[0] > cx ? 'left' : 'right';\n          var labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3 ? 'middle' : p[1] > cy ? 'top' : 'bottom';\n\n          if (rawCategoryData && rawCategoryData[tickValue]) {\n            var rawCategoryItem = rawCategoryData[tickValue];\n\n            if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) {\n              labelModel = new Model(rawCategoryItem.textStyle, commonLabelModel, commonLabelModel.ecModel);\n            }\n          }\n\n          var textEl = new ZRText({\n            silent: AxisBuilder.isLabelSilent(angleAxisModel),\n            style: createTextStyle(labelModel, {\n              x: p[0],\n              y: p[1],\n              fill: labelModel.getTextColor() || angleAxisModel.get(['axisLine', 'lineStyle', 'color']),\n              text: labelItem.formattedLabel,\n              align: labelTextAlign,\n              verticalAlign: labelTextVerticalAlign\n            })\n          });\n          group.add(textEl); // Pack data for mouse event\n\n          if (triggerEvent) {\n            var eventData = AxisBuilder.makeAxisEventDataBase(angleAxisModel);\n            eventData.targetType = 'axisLabel';\n            eventData.value = labelItem.rawLabel;\n            getECData(textEl).eventData = eventData;\n          }\n        }, this);\n      },\n      splitLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n        var splitLineModel = angleAxisModel.getModel('splitLine');\n        var lineStyleModel = splitLineModel.getModel('lineStyle');\n        var lineColors = lineStyleModel.get('color');\n        var lineCount = 0;\n        lineColors = lineColors instanceof Array ? lineColors : [lineColors];\n        var splitLines = [];\n\n        for (var i = 0; i < ticksAngles.length; i++) {\n          var colorIndex = lineCount++ % lineColors.length;\n          splitLines[colorIndex] = splitLines[colorIndex] || [];\n          splitLines[colorIndex].push(new Line({\n            shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord)\n          }));\n        } // Simple optimization\n        // Batching the lines if color are the same\n\n\n        for (var i = 0; i < splitLines.length; i++) {\n          group.add(mergePath$1(splitLines[i], {\n            style: defaults({\n              stroke: lineColors[i % lineColors.length]\n            }, lineStyleModel.getLineStyle()),\n            silent: true,\n            z: angleAxisModel.get('z')\n          }));\n        }\n      },\n      minorSplitLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n        if (!minorTickAngles.length) {\n          return;\n        }\n\n        var minorSplitLineModel = angleAxisModel.getModel('minorSplitLine');\n        var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n        var lines = [];\n\n        for (var i = 0; i < minorTickAngles.length; i++) {\n          for (var k = 0; k < minorTickAngles[i].length; k++) {\n            lines.push(new Line({\n              shape: getAxisLineShape(polar, radiusExtent, minorTickAngles[i][k].coord)\n            }));\n          }\n        }\n\n        group.add(mergePath$1(lines, {\n          style: lineStyleModel.getLineStyle(),\n          silent: true,\n          z: angleAxisModel.get('z')\n        }));\n      },\n      splitArea: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {\n        if (!ticksAngles.length) {\n          return;\n        }\n\n        var splitAreaModel = angleAxisModel.getModel('splitArea');\n        var areaStyleModel = splitAreaModel.getModel('areaStyle');\n        var areaColors = areaStyleModel.get('color');\n        var lineCount = 0;\n        areaColors = areaColors instanceof Array ? areaColors : [areaColors];\n        var splitAreas = [];\n        var RADIAN = Math.PI / 180;\n        var prevAngle = -ticksAngles[0].coord * RADIAN;\n        var r0 = Math.min(radiusExtent[0], radiusExtent[1]);\n        var r1 = Math.max(radiusExtent[0], radiusExtent[1]);\n        var clockwise = angleAxisModel.get('clockwise');\n\n        for (var i = 1, len = ticksAngles.length; i <= len; i++) {\n          var coord = i === len ? ticksAngles[0].coord : ticksAngles[i].coord;\n          var colorIndex = lineCount++ % areaColors.length;\n          splitAreas[colorIndex] = splitAreas[colorIndex] || [];\n          splitAreas[colorIndex].push(new Sector({\n            shape: {\n              cx: polar.cx,\n              cy: polar.cy,\n              r0: r0,\n              r: r1,\n              startAngle: prevAngle,\n              endAngle: -coord * RADIAN,\n              clockwise: clockwise\n            },\n            silent: true\n          }));\n          prevAngle = -coord * RADIAN;\n        } // Simple optimization\n        // Batching the lines if color are the same\n\n\n        for (var i = 0; i < splitAreas.length; i++) {\n          group.add(mergePath$1(splitAreas[i], {\n            style: defaults({\n              fill: areaColors[i % areaColors.length]\n            }, areaStyleModel.getAreaStyle()),\n            silent: true\n          }));\n        }\n      }\n    };\n\n    var axisBuilderAttrs$2 = ['axisLine', 'axisTickLabel', 'axisName'];\n    var selfBuilderAttrs$1 = ['splitLine', 'splitArea', 'minorSplitLine'];\n\n    var RadiusAxisView =\n    /** @class */\n    function (_super) {\n      __extends(RadiusAxisView, _super);\n\n      function RadiusAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = RadiusAxisView.type;\n        _this.axisPointerClass = 'PolarAxisPointer';\n        return _this;\n      }\n\n      RadiusAxisView.prototype.render = function (radiusAxisModel, ecModel) {\n        this.group.removeAll();\n\n        if (!radiusAxisModel.get('show')) {\n          return;\n        }\n\n        var oldAxisGroup = this._axisGroup;\n        var newAxisGroup = this._axisGroup = new Group();\n        this.group.add(newAxisGroup);\n        var radiusAxis = radiusAxisModel.axis;\n        var polar = radiusAxis.polar;\n        var angleAxis = polar.getAngleAxis();\n        var ticksCoords = radiusAxis.getTicksCoords();\n        var minorTicksCoords = radiusAxis.getMinorTicksCoords();\n        var axisAngle = angleAxis.getExtent()[0];\n        var radiusExtent = radiusAxis.getExtent();\n        var layout = layoutAxis(polar, radiusAxisModel, axisAngle);\n        var axisBuilder = new AxisBuilder(radiusAxisModel, layout);\n        each(axisBuilderAttrs$2, axisBuilder.add, axisBuilder);\n        newAxisGroup.add(axisBuilder.getGroup());\n        groupTransition(oldAxisGroup, newAxisGroup, radiusAxisModel);\n        each(selfBuilderAttrs$1, function (name) {\n          if (radiusAxisModel.get([name, 'show']) && !radiusAxis.scale.isBlank()) {\n            axisElementBuilders$1[name](this.group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords, minorTicksCoords);\n          }\n        }, this);\n      };\n\n      RadiusAxisView.type = 'radiusAxis';\n      return RadiusAxisView;\n    }(AxisView);\n\n    var axisElementBuilders$1 = {\n      splitLine: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) {\n        var splitLineModel = radiusAxisModel.getModel('splitLine');\n        var lineStyleModel = splitLineModel.getModel('lineStyle');\n        var lineColors = lineStyleModel.get('color');\n        var lineCount = 0;\n        lineColors = lineColors instanceof Array ? lineColors : [lineColors];\n        var splitLines = [];\n\n        for (var i = 0; i < ticksCoords.length; i++) {\n          var colorIndex = lineCount++ % lineColors.length;\n          splitLines[colorIndex] = splitLines[colorIndex] || [];\n          splitLines[colorIndex].push(new Circle({\n            shape: {\n              cx: polar.cx,\n              cy: polar.cy,\n              // ensure circle radius >= 0\n              r: Math.max(ticksCoords[i].coord, 0)\n            }\n          }));\n        } // Simple optimization\n        // Batching the lines if color are the same\n\n\n        for (var i = 0; i < splitLines.length; i++) {\n          group.add(mergePath$1(splitLines[i], {\n            style: defaults({\n              stroke: lineColors[i % lineColors.length],\n              fill: null\n            }, lineStyleModel.getLineStyle()),\n            silent: true\n          }));\n        }\n      },\n      minorSplitLine: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords, minorTicksCoords) {\n        if (!minorTicksCoords.length) {\n          return;\n        }\n\n        var minorSplitLineModel = radiusAxisModel.getModel('minorSplitLine');\n        var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n        var lines = [];\n\n        for (var i = 0; i < minorTicksCoords.length; i++) {\n          for (var k = 0; k < minorTicksCoords[i].length; k++) {\n            lines.push(new Circle({\n              shape: {\n                cx: polar.cx,\n                cy: polar.cy,\n                r: minorTicksCoords[i][k].coord\n              }\n            }));\n          }\n        }\n\n        group.add(mergePath$1(lines, {\n          style: defaults({\n            fill: null\n          }, lineStyleModel.getLineStyle()),\n          silent: true\n        }));\n      },\n      splitArea: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) {\n        if (!ticksCoords.length) {\n          return;\n        }\n\n        var splitAreaModel = radiusAxisModel.getModel('splitArea');\n        var areaStyleModel = splitAreaModel.getModel('areaStyle');\n        var areaColors = areaStyleModel.get('color');\n        var lineCount = 0;\n        areaColors = areaColors instanceof Array ? areaColors : [areaColors];\n        var splitAreas = [];\n        var prevRadius = ticksCoords[0].coord;\n\n        for (var i = 1; i < ticksCoords.length; i++) {\n          var colorIndex = lineCount++ % areaColors.length;\n          splitAreas[colorIndex] = splitAreas[colorIndex] || [];\n          splitAreas[colorIndex].push(new Sector({\n            shape: {\n              cx: polar.cx,\n              cy: polar.cy,\n              r0: prevRadius,\n              r: ticksCoords[i].coord,\n              startAngle: 0,\n              endAngle: Math.PI * 2\n            },\n            silent: true\n          }));\n          prevRadius = ticksCoords[i].coord;\n        } // Simple optimization\n        // Batching the lines if color are the same\n\n\n        for (var i = 0; i < splitAreas.length; i++) {\n          group.add(mergePath$1(splitAreas[i], {\n            style: defaults({\n              fill: areaColors[i % areaColors.length]\n            }, areaStyleModel.getAreaStyle()),\n            silent: true\n          }));\n        }\n      }\n    };\n    /**\n     * @inner\n     */\n\n    function layoutAxis(polar, radiusAxisModel, axisAngle) {\n      return {\n        position: [polar.cx, polar.cy],\n        rotation: axisAngle / 180 * Math.PI,\n        labelDirection: -1,\n        tickDirection: -1,\n        nameDirection: 1,\n        labelRotate: radiusAxisModel.getModel('axisLabel').get('rotate'),\n        // Over splitLine and splitArea\n        z2: 1\n      };\n    }\n\n    function getSeriesStackId$1(seriesModel) {\n      return seriesModel.get('stack') || '__ec_stack_' + seriesModel.seriesIndex;\n    }\n\n    function getAxisKey$1(polar, axis) {\n      return axis.dim + polar.model.componentIndex;\n    }\n\n    function barLayoutPolar(seriesType, ecModel, api) {\n      var lastStackCoords = {};\n      var barWidthAndOffset = calRadialBar(filter(ecModel.getSeriesByType(seriesType), function (seriesModel) {\n        return !ecModel.isSeriesFiltered(seriesModel) && seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'polar';\n      }));\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        // Check series coordinate, do layout for polar only\n        if (seriesModel.coordinateSystem.type !== 'polar') {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var polar = seriesModel.coordinateSystem;\n        var baseAxis = polar.getBaseAxis();\n        var axisKey = getAxisKey$1(polar, baseAxis);\n        var stackId = getSeriesStackId$1(seriesModel);\n        var columnLayoutInfo = barWidthAndOffset[axisKey][stackId];\n        var columnOffset = columnLayoutInfo.offset;\n        var columnWidth = columnLayoutInfo.width;\n        var valueAxis = polar.getOtherAxis(baseAxis);\n        var cx = seriesModel.coordinateSystem.cx;\n        var cy = seriesModel.coordinateSystem.cy;\n        var barMinHeight = seriesModel.get('barMinHeight') || 0;\n        var barMinAngle = seriesModel.get('barMinAngle') || 0;\n        lastStackCoords[stackId] = lastStackCoords[stackId] || [];\n        var valueDim = data.mapDimension(valueAxis.dim);\n        var baseDim = data.mapDimension(baseAxis.dim);\n        var stacked = isDimensionStacked(data, valueDim\n        /* , baseDim */\n        );\n        var clampLayout = baseAxis.dim !== 'radius' || !seriesModel.get('roundCap', true);\n        var valueAxisStart = valueAxis.dataToCoord(0);\n\n        for (var idx = 0, len = data.count(); idx < len; idx++) {\n          var value = data.get(valueDim, idx);\n          var baseValue = data.get(baseDim, idx);\n          var sign = value >= 0 ? 'p' : 'n';\n          var baseCoord = valueAxisStart; // Because of the barMinHeight, we can not use the value in\n          // stackResultDimension directly.\n          // Only ordinal axis can be stacked.\n\n          if (stacked) {\n            if (!lastStackCoords[stackId][baseValue]) {\n              lastStackCoords[stackId][baseValue] = {\n                p: valueAxisStart,\n                n: valueAxisStart // Negative stack\n\n              };\n            } // Should also consider #4243\n\n\n            baseCoord = lastStackCoords[stackId][baseValue][sign];\n          }\n\n          var r0 = void 0;\n          var r = void 0;\n          var startAngle = void 0;\n          var endAngle = void 0; // radial sector\n\n          if (valueAxis.dim === 'radius') {\n            var radiusSpan = valueAxis.dataToCoord(value) - valueAxisStart;\n            var angle = baseAxis.dataToCoord(baseValue);\n\n            if (Math.abs(radiusSpan) < barMinHeight) {\n              radiusSpan = (radiusSpan < 0 ? -1 : 1) * barMinHeight;\n            }\n\n            r0 = baseCoord;\n            r = baseCoord + radiusSpan;\n            startAngle = angle - columnOffset;\n            endAngle = startAngle - columnWidth;\n            stacked && (lastStackCoords[stackId][baseValue][sign] = r);\n          } // tangential sector\n          else {\n              var angleSpan = valueAxis.dataToCoord(value, clampLayout) - valueAxisStart;\n              var radius = baseAxis.dataToCoord(baseValue);\n\n              if (Math.abs(angleSpan) < barMinAngle) {\n                angleSpan = (angleSpan < 0 ? -1 : 1) * barMinAngle;\n              }\n\n              r0 = radius + columnOffset;\n              r = r0 + columnWidth;\n              startAngle = baseCoord;\n              endAngle = baseCoord + angleSpan; // if the previous stack is at the end of the ring,\n              // add a round to differentiate it from origin\n              // let extent = angleAxis.getExtent();\n              // let stackCoord = angle;\n              // if (stackCoord === extent[0] && value > 0) {\n              //     stackCoord = extent[1];\n              // }\n              // else if (stackCoord === extent[1] && value < 0) {\n              //     stackCoord = extent[0];\n              // }\n\n              stacked && (lastStackCoords[stackId][baseValue][sign] = endAngle);\n            }\n\n          data.setItemLayout(idx, {\n            cx: cx,\n            cy: cy,\n            r0: r0,\n            r: r,\n            // Consider that positive angle is anti-clockwise,\n            // while positive radian of sector is clockwise\n            startAngle: -startAngle * Math.PI / 180,\n            endAngle: -endAngle * Math.PI / 180,\n\n            /**\n             * Keep the same logic with bar in catesion: use end value to\n             * control direction. Notice that if clockwise is true (by\n             * default), the sector will always draw clockwisely, no matter\n             * whether endAngle is greater or less than startAngle.\n             */\n            clockwise: startAngle >= endAngle\n          });\n        }\n      });\n    }\n    /**\n     * Calculate bar width and offset for radial bar charts\n     */\n\n\n    function calRadialBar(barSeries) {\n      // Columns info on each category axis. Key is polar name\n      var columnsMap = {};\n      each(barSeries, function (seriesModel, idx) {\n        var data = seriesModel.getData();\n        var polar = seriesModel.coordinateSystem;\n        var baseAxis = polar.getBaseAxis();\n        var axisKey = getAxisKey$1(polar, baseAxis);\n        var axisExtent = baseAxis.getExtent();\n        var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : Math.abs(axisExtent[1] - axisExtent[0]) / data.count();\n        var columnsOnAxis = columnsMap[axisKey] || {\n          bandWidth: bandWidth,\n          remainedWidth: bandWidth,\n          autoWidthCount: 0,\n          categoryGap: '20%',\n          gap: '30%',\n          stacks: {}\n        };\n        var stacks = columnsOnAxis.stacks;\n        columnsMap[axisKey] = columnsOnAxis;\n        var stackId = getSeriesStackId$1(seriesModel);\n\n        if (!stacks[stackId]) {\n          columnsOnAxis.autoWidthCount++;\n        }\n\n        stacks[stackId] = stacks[stackId] || {\n          width: 0,\n          maxWidth: 0\n        };\n        var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth);\n        var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth);\n        var barGap = seriesModel.get('barGap');\n        var barCategoryGap = seriesModel.get('barCategoryGap');\n\n        if (barWidth && !stacks[stackId].width) {\n          barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);\n          stacks[stackId].width = barWidth;\n          columnsOnAxis.remainedWidth -= barWidth;\n        }\n\n        barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);\n        barGap != null && (columnsOnAxis.gap = barGap);\n        barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap);\n      });\n      var result = {};\n      each(columnsMap, function (columnsOnAxis, coordSysName) {\n        result[coordSysName] = {};\n        var stacks = columnsOnAxis.stacks;\n        var bandWidth = columnsOnAxis.bandWidth;\n        var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth);\n        var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);\n        var remainedWidth = columnsOnAxis.remainedWidth;\n        var autoWidthCount = columnsOnAxis.autoWidthCount;\n        var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0); // Find if any auto calculated bar exceeded maxBarWidth\n\n        each(stacks, function (column, stack) {\n          var maxWidth = column.maxWidth;\n\n          if (maxWidth && maxWidth < autoWidth) {\n            maxWidth = Math.min(maxWidth, remainedWidth);\n\n            if (column.width) {\n              maxWidth = Math.min(maxWidth, column.width);\n            }\n\n            remainedWidth -= maxWidth;\n            column.width = maxWidth;\n            autoWidthCount--;\n          }\n        }); // Recalculate width again\n\n        autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0);\n        var widthSum = 0;\n        var lastColumn;\n        each(stacks, function (column, idx) {\n          if (!column.width) {\n            column.width = autoWidth;\n          }\n\n          lastColumn = column;\n          widthSum += column.width * (1 + barGapPercent);\n        });\n\n        if (lastColumn) {\n          widthSum -= lastColumn.width * barGapPercent;\n        }\n\n        var offset = -widthSum / 2;\n        each(stacks, function (column, stackId) {\n          result[coordSysName][stackId] = result[coordSysName][stackId] || {\n            offset: offset,\n            width: column.width\n          };\n          offset += column.width * (1 + barGapPercent);\n        });\n      });\n      return result;\n    }\n\n    var angleAxisExtraOption = {\n      startAngle: 90,\n      clockwise: true,\n      splitNumber: 12,\n      axisLabel: {\n        rotate: 0\n      }\n    };\n    var radiusAxisExtraOption = {\n      splitNumber: 5\n    };\n\n    var PolarView =\n    /** @class */\n    function (_super) {\n      __extends(PolarView, _super);\n\n      function PolarView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = PolarView.type;\n        return _this;\n      }\n\n      PolarView.type = 'polar';\n      return PolarView;\n    }(ComponentView);\n\n    function install$u(registers) {\n      use(install$s);\n      AxisView.registerAxisPointerClass('PolarAxisPointer', PolarAxisPointer);\n      registers.registerCoordinateSystem('polar', polarCreator);\n      registers.registerComponentModel(PolarModel);\n      registers.registerComponentView(PolarView); // Model and view for angleAxis and radiusAxis\n\n      axisModelCreator(registers, 'angle', AngleAxisModel, angleAxisExtraOption);\n      axisModelCreator(registers, 'radius', RadiusAxisModel, radiusAxisExtraOption);\n      registers.registerComponentView(AngleAxisView);\n      registers.registerComponentView(RadiusAxisView);\n      registers.registerLayout(curry(barLayoutPolar, 'bar'));\n    }\n\n    function layout$2(axisModel, opt) {\n      opt = opt || {};\n      var single = axisModel.coordinateSystem;\n      var axis = axisModel.axis;\n      var layout = {};\n      var axisPosition = axis.position;\n      var orient = axis.orient;\n      var rect = single.getRect();\n      var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];\n      var positionMap = {\n        horizontal: {\n          top: rectBound[2],\n          bottom: rectBound[3]\n        },\n        vertical: {\n          left: rectBound[0],\n          right: rectBound[1]\n        }\n      };\n      layout.position = [orient === 'vertical' ? positionMap.vertical[axisPosition] : rectBound[0], orient === 'horizontal' ? positionMap.horizontal[axisPosition] : rectBound[3]];\n      var r = {\n        horizontal: 0,\n        vertical: 1\n      };\n      layout.rotation = Math.PI / 2 * r[orient];\n      var directionMap = {\n        top: -1,\n        bottom: 1,\n        right: 1,\n        left: -1\n      };\n      layout.labelDirection = layout.tickDirection = layout.nameDirection = directionMap[axisPosition];\n\n      if (axisModel.get(['axisTick', 'inside'])) {\n        layout.tickDirection = -layout.tickDirection;\n      }\n\n      if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) {\n        layout.labelDirection = -layout.labelDirection;\n      }\n\n      var labelRotation = opt.rotate;\n      labelRotation == null && (labelRotation = axisModel.get(['axisLabel', 'rotate']));\n      layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation;\n      layout.z2 = 1;\n      return layout;\n    }\n\n    var axisBuilderAttrs$3 = ['axisLine', 'axisTickLabel', 'axisName'];\n    var selfBuilderAttrs$2 = ['splitArea', 'splitLine'];\n\n    var SingleAxisView =\n    /** @class */\n    function (_super) {\n      __extends(SingleAxisView, _super);\n\n      function SingleAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SingleAxisView.type;\n        _this.axisPointerClass = 'SingleAxisPointer';\n        return _this;\n      }\n\n      SingleAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        var group = this.group;\n        group.removeAll();\n        var oldAxisGroup = this._axisGroup;\n        this._axisGroup = new Group();\n        var layout = layout$2(axisModel);\n        var axisBuilder = new AxisBuilder(axisModel, layout);\n        each(axisBuilderAttrs$3, axisBuilder.add, axisBuilder);\n        group.add(this._axisGroup);\n        group.add(axisBuilder.getGroup());\n        each(selfBuilderAttrs$2, function (name) {\n          if (axisModel.get([name, 'show'])) {\n            axisElementBuilders$2[name](this, this.group, this._axisGroup, axisModel);\n          }\n        }, this);\n        groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n\n        _super.prototype.render.call(this, axisModel, ecModel, api, payload);\n      };\n\n      SingleAxisView.prototype.remove = function () {\n        rectCoordAxisHandleRemove(this);\n      };\n\n      SingleAxisView.type = 'singleAxis';\n      return SingleAxisView;\n    }(AxisView);\n\n    var axisElementBuilders$2 = {\n      splitLine: function (axisView, group, axisGroup, axisModel) {\n        var axis = axisModel.axis;\n\n        if (axis.scale.isBlank()) {\n          return;\n        }\n\n        var splitLineModel = axisModel.getModel('splitLine');\n        var lineStyleModel = splitLineModel.getModel('lineStyle');\n        var lineColors = lineStyleModel.get('color');\n        lineColors = lineColors instanceof Array ? lineColors : [lineColors];\n        var lineWidth = lineStyleModel.get('width');\n        var gridRect = axisModel.coordinateSystem.getRect();\n        var isHorizontal = axis.isHorizontal();\n        var splitLines = [];\n        var lineCount = 0;\n        var ticksCoords = axis.getTicksCoords({\n          tickModel: splitLineModel\n        });\n        var p1 = [];\n        var p2 = [];\n\n        for (var i = 0; i < ticksCoords.length; ++i) {\n          var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n\n          if (isHorizontal) {\n            p1[0] = tickCoord;\n            p1[1] = gridRect.y;\n            p2[0] = tickCoord;\n            p2[1] = gridRect.y + gridRect.height;\n          } else {\n            p1[0] = gridRect.x;\n            p1[1] = tickCoord;\n            p2[0] = gridRect.x + gridRect.width;\n            p2[1] = tickCoord;\n          }\n\n          var line = new Line({\n            shape: {\n              x1: p1[0],\n              y1: p1[1],\n              x2: p2[0],\n              y2: p2[1]\n            },\n            silent: true\n          });\n          subPixelOptimizeLine$1(line.shape, lineWidth);\n          var colorIndex = lineCount++ % lineColors.length;\n          splitLines[colorIndex] = splitLines[colorIndex] || [];\n          splitLines[colorIndex].push(line);\n        }\n\n        var lineStyle = lineStyleModel.getLineStyle(['color']);\n\n        for (var i = 0; i < splitLines.length; ++i) {\n          group.add(mergePath$1(splitLines[i], {\n            style: defaults({\n              stroke: lineColors[i % lineColors.length]\n            }, lineStyle),\n            silent: true\n          }));\n        }\n      },\n      splitArea: function (axisView, group, axisGroup, axisModel) {\n        rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, axisModel);\n      }\n    };\n\n    var SingleAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(SingleAxisModel, _super);\n\n      function SingleAxisModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SingleAxisModel.type;\n        return _this;\n      }\n\n      SingleAxisModel.prototype.getCoordSysModel = function () {\n        return this;\n      };\n\n      SingleAxisModel.type = 'singleAxis';\n      SingleAxisModel.layoutMode = 'box';\n      SingleAxisModel.defaultOption = {\n        left: '5%',\n        top: '5%',\n        right: '5%',\n        bottom: '5%',\n        type: 'value',\n        position: 'bottom',\n        orient: 'horizontal',\n        axisLine: {\n          show: true,\n          lineStyle: {\n            width: 1,\n            type: 'solid'\n          }\n        },\n        // Single coordinate system and single axis is the,\n        // which is used as the parent tooltip model.\n        // same model, so we set default tooltip show as true.\n        tooltip: {\n          show: true\n        },\n        axisTick: {\n          show: true,\n          length: 6,\n          lineStyle: {\n            width: 1\n          }\n        },\n        axisLabel: {\n          show: true,\n          interval: 'auto'\n        },\n        splitLine: {\n          show: true,\n          lineStyle: {\n            type: 'dashed',\n            opacity: 0.2\n          }\n        }\n      };\n      return SingleAxisModel;\n    }(ComponentModel);\n\n    mixin(SingleAxisModel, AxisModelCommonMixin.prototype);\n\n    var SingleAxis =\n    /** @class */\n    function (_super) {\n      __extends(SingleAxis, _super);\n\n      function SingleAxis(dim, scale, coordExtent, axisType, position) {\n        var _this = _super.call(this, dim, scale, coordExtent) || this;\n\n        _this.type = axisType || 'value';\n        _this.position = position || 'bottom';\n        return _this;\n      }\n      /**\n       * Judge the orient of the axis.\n       */\n\n\n      SingleAxis.prototype.isHorizontal = function () {\n        var position = this.position;\n        return position === 'top' || position === 'bottom';\n      };\n\n      SingleAxis.prototype.pointToData = function (point, clamp) {\n        return this.coordinateSystem.pointToData(point)[0];\n      };\n\n      return SingleAxis;\n    }(Axis);\n\n    var singleDimensions = ['single'];\n    /**\n     * Create a single coordinates system.\n     */\n\n    var Single =\n    /** @class */\n    function () {\n      function Single(axisModel, ecModel, api) {\n        this.type = 'single';\n        this.dimension = 'single';\n        /**\n         * Add it just for draw tooltip.\n         */\n\n        this.dimensions = singleDimensions;\n        this.axisPointerEnabled = true;\n        this.model = axisModel;\n\n        this._init(axisModel, ecModel, api);\n      }\n      /**\n       * Initialize single coordinate system.\n       */\n\n\n      Single.prototype._init = function (axisModel, ecModel, api) {\n        var dim = this.dimension;\n        var axis = new SingleAxis(dim, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisModel.get('position'));\n        var isCategory = axis.type === 'category';\n        axis.onBand = isCategory && axisModel.get('boundaryGap');\n        axis.inverse = axisModel.get('inverse');\n        axis.orient = axisModel.get('orient');\n        axisModel.axis = axis;\n        axis.model = axisModel;\n        axis.coordinateSystem = this;\n        this._axis = axis;\n      };\n      /**\n       * Update axis scale after data processed\n       */\n\n\n      Single.prototype.update = function (ecModel, api) {\n        ecModel.eachSeries(function (seriesModel) {\n          if (seriesModel.coordinateSystem === this) {\n            var data_1 = seriesModel.getData();\n            each(data_1.mapDimensionsAll(this.dimension), function (dim) {\n              this._axis.scale.unionExtentFromData(data_1, dim);\n            }, this);\n            niceScaleExtent(this._axis.scale, this._axis.model);\n          }\n        }, this);\n      };\n      /**\n       * Resize the single coordinate system.\n       */\n\n\n      Single.prototype.resize = function (axisModel, api) {\n        this._rect = getLayoutRect({\n          left: axisModel.get('left'),\n          top: axisModel.get('top'),\n          right: axisModel.get('right'),\n          bottom: axisModel.get('bottom'),\n          width: axisModel.get('width'),\n          height: axisModel.get('height')\n        }, {\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n\n        this._adjustAxis();\n      };\n\n      Single.prototype.getRect = function () {\n        return this._rect;\n      };\n\n      Single.prototype._adjustAxis = function () {\n        var rect = this._rect;\n        var axis = this._axis;\n        var isHorizontal = axis.isHorizontal();\n        var extent = isHorizontal ? [0, rect.width] : [0, rect.height];\n        var idx = axis.inverse ? 1 : 0;\n        axis.setExtent(extent[idx], extent[1 - idx]);\n\n        this._updateAxisTransform(axis, isHorizontal ? rect.x : rect.y);\n      };\n\n      Single.prototype._updateAxisTransform = function (axis, coordBase) {\n        var axisExtent = axis.getExtent();\n        var extentSum = axisExtent[0] + axisExtent[1];\n        var isHorizontal = axis.isHorizontal();\n        axis.toGlobalCoord = isHorizontal ? function (coord) {\n          return coord + coordBase;\n        } : function (coord) {\n          return extentSum - coord + coordBase;\n        };\n        axis.toLocalCoord = isHorizontal ? function (coord) {\n          return coord - coordBase;\n        } : function (coord) {\n          return extentSum - coord + coordBase;\n        };\n      };\n      /**\n       * Get axis.\n       */\n\n\n      Single.prototype.getAxis = function () {\n        return this._axis;\n      };\n      /**\n       * Get axis, add it just for draw tooltip.\n       */\n\n\n      Single.prototype.getBaseAxis = function () {\n        return this._axis;\n      };\n\n      Single.prototype.getAxes = function () {\n        return [this._axis];\n      };\n\n      Single.prototype.getTooltipAxes = function () {\n        return {\n          baseAxes: [this.getAxis()],\n          // Empty otherAxes\n          otherAxes: []\n        };\n      };\n      /**\n       * If contain point.\n       */\n\n\n      Single.prototype.containPoint = function (point) {\n        var rect = this.getRect();\n        var axis = this.getAxis();\n        var orient = axis.orient;\n\n        if (orient === 'horizontal') {\n          return axis.contain(axis.toLocalCoord(point[0])) && point[1] >= rect.y && point[1] <= rect.y + rect.height;\n        } else {\n          return axis.contain(axis.toLocalCoord(point[1])) && point[0] >= rect.y && point[0] <= rect.y + rect.height;\n        }\n      };\n\n      Single.prototype.pointToData = function (point) {\n        var axis = this.getAxis();\n        return [axis.coordToData(axis.toLocalCoord(point[axis.orient === 'horizontal' ? 0 : 1]))];\n      };\n      /**\n       * Convert the series data to concrete point.\n       * Can be [val] | val\n       */\n\n\n      Single.prototype.dataToPoint = function (val) {\n        var axis = this.getAxis();\n        var rect = this.getRect();\n        var pt = [];\n        var idx = axis.orient === 'horizontal' ? 0 : 1;\n\n        if (val instanceof Array) {\n          val = val[0];\n        }\n\n        pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val));\n        pt[1 - idx] = idx === 0 ? rect.y + rect.height / 2 : rect.x + rect.width / 2;\n        return pt;\n      };\n\n      Single.prototype.convertToPixel = function (ecModel, finder, value) {\n        var coordSys = getCoordSys$3(finder);\n        return coordSys === this ? this.dataToPoint(value) : null;\n      };\n\n      Single.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n        var coordSys = getCoordSys$3(finder);\n        return coordSys === this ? this.pointToData(pixel) : null;\n      };\n\n      return Single;\n    }();\n\n    function getCoordSys$3(finder) {\n      var seriesModel = finder.seriesModel;\n      var singleModel = finder.singleAxisModel;\n      return singleModel && singleModel.coordinateSystem || seriesModel && seriesModel.coordinateSystem;\n    }\n\n    /**\n     * Create single coordinate system and inject it into seriesModel.\n     */\n\n    function create$2(ecModel, api) {\n      var singles = [];\n      ecModel.eachComponent('singleAxis', function (axisModel, idx) {\n        var single = new Single(axisModel, ecModel, api);\n        single.name = 'single_' + idx;\n        single.resize(axisModel, api);\n        axisModel.coordinateSystem = single;\n        singles.push(single);\n      });\n      ecModel.eachSeries(function (seriesModel) {\n        if (seriesModel.get('coordinateSystem') === 'singleAxis') {\n          var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n          seriesModel.coordinateSystem = singleAxisModel && singleAxisModel.coordinateSystem;\n        }\n      });\n      return singles;\n    }\n\n    var singleCreator = {\n      create: create$2,\n      dimensions: singleDimensions\n    };\n\n    var XY = ['x', 'y'];\n    var WH = ['width', 'height'];\n\n    var SingleAxisPointer =\n    /** @class */\n    function (_super) {\n      __extends(SingleAxisPointer, _super);\n\n      function SingleAxisPointer() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n      /**\n       * @override\n       */\n\n\n      SingleAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) {\n        var axis = axisModel.axis;\n        var coordSys = axis.coordinateSystem;\n        var otherExtent = getGlobalExtent(coordSys, 1 - getPointDimIndex(axis));\n        var pixelValue = coordSys.dataToPoint(value)[0];\n        var axisPointerType = axisPointerModel.get('type');\n\n        if (axisPointerType && axisPointerType !== 'none') {\n          var elStyle = buildElStyle(axisPointerModel);\n          var pointerOption = pointerShapeBuilder$2[axisPointerType](axis, pixelValue, otherExtent);\n          pointerOption.style = elStyle;\n          elOption.graphicKey = pointerOption.type;\n          elOption.pointer = pointerOption;\n        }\n\n        var layoutInfo = layout$2(axisModel);\n        buildCartesianSingleLabelElOption( // @ts-ignore\n        value, elOption, layoutInfo, axisModel, axisPointerModel, api);\n      };\n      /**\n       * @override\n       */\n\n\n      SingleAxisPointer.prototype.getHandleTransform = function (value, axisModel, axisPointerModel) {\n        var layoutInfo = layout$2(axisModel, {\n          labelInside: false\n        }); // @ts-ignore\n\n        layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']);\n        var position = getTransformedPosition(axisModel.axis, value, layoutInfo);\n        return {\n          x: position[0],\n          y: position[1],\n          rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)\n        };\n      };\n      /**\n       * @override\n       */\n\n\n      SingleAxisPointer.prototype.updateHandleTransform = function (transform, delta, axisModel, axisPointerModel) {\n        var axis = axisModel.axis;\n        var coordSys = axis.coordinateSystem;\n        var dimIndex = getPointDimIndex(axis);\n        var axisExtent = getGlobalExtent(coordSys, dimIndex);\n        var currPosition = [transform.x, transform.y];\n        currPosition[dimIndex] += delta[dimIndex];\n        currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);\n        currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);\n        var otherExtent = getGlobalExtent(coordSys, 1 - dimIndex);\n        var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;\n        var cursorPoint = [cursorOtherValue, cursorOtherValue];\n        cursorPoint[dimIndex] = currPosition[dimIndex];\n        return {\n          x: currPosition[0],\n          y: currPosition[1],\n          rotation: transform.rotation,\n          cursorPoint: cursorPoint,\n          tooltipOption: {\n            verticalAlign: 'middle'\n          }\n        };\n      };\n\n      return SingleAxisPointer;\n    }(BaseAxisPointer);\n\n    var pointerShapeBuilder$2 = {\n      line: function (axis, pixelValue, otherExtent) {\n        var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getPointDimIndex(axis));\n        return {\n          type: 'Line',\n          subPixelOptimize: true,\n          shape: targetShape\n        };\n      },\n      shadow: function (axis, pixelValue, otherExtent) {\n        var bandWidth = axis.getBandWidth();\n        var span = otherExtent[1] - otherExtent[0];\n        return {\n          type: 'Rect',\n          shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getPointDimIndex(axis))\n        };\n      }\n    };\n\n    function getPointDimIndex(axis) {\n      return axis.isHorizontal() ? 0 : 1;\n    }\n\n    function getGlobalExtent(coordSys, dimIndex) {\n      var rect = coordSys.getRect();\n      return [rect[XY[dimIndex]], rect[XY[dimIndex]] + rect[WH[dimIndex]]];\n    }\n\n    var SingleView =\n    /** @class */\n    function (_super) {\n      __extends(SingleView, _super);\n\n      function SingleView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SingleView.type;\n        return _this;\n      }\n\n      SingleView.type = 'single';\n      return SingleView;\n    }(ComponentView);\n\n    function install$v(registers) {\n      use(install$s);\n      AxisView.registerAxisPointerClass('SingleAxisPointer', SingleAxisPointer);\n      registers.registerComponentView(SingleView); // Axis\n\n      registers.registerComponentView(SingleAxisView);\n      registers.registerComponentModel(SingleAxisModel);\n      axisModelCreator(registers, 'single', SingleAxisModel, SingleAxisModel.defaultOption);\n      registers.registerCoordinateSystem('single', singleCreator);\n    }\n\n    var CalendarModel =\n    /** @class */\n    function (_super) {\n      __extends(CalendarModel, _super);\n\n      function CalendarModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CalendarModel.type;\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      CalendarModel.prototype.init = function (option, parentModel, ecModel) {\n        var inputPositionParams = getLayoutParams(option);\n\n        _super.prototype.init.apply(this, arguments);\n\n        mergeAndNormalizeLayoutParams(option, inputPositionParams);\n      };\n      /**\n       * @override\n       */\n\n\n      CalendarModel.prototype.mergeOption = function (option) {\n        _super.prototype.mergeOption.apply(this, arguments);\n\n        mergeAndNormalizeLayoutParams(this.option, option);\n      };\n\n      CalendarModel.prototype.getCellSize = function () {\n        // Has been normalized\n        return this.option.cellSize;\n      };\n\n      CalendarModel.type = 'calendar';\n      CalendarModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        left: 80,\n        top: 60,\n        cellSize: 20,\n        // horizontal vertical\n        orient: 'horizontal',\n        // month separate line style\n        splitLine: {\n          show: true,\n          lineStyle: {\n            color: '#000',\n            width: 1,\n            type: 'solid'\n          }\n        },\n        // rect style  temporarily unused emphasis\n        itemStyle: {\n          color: '#fff',\n          borderWidth: 1,\n          borderColor: '#ccc'\n        },\n        // week text style\n        dayLabel: {\n          show: true,\n          firstDay: 0,\n          // start end\n          position: 'start',\n          margin: '50%',\n          color: '#000'\n        },\n        // month text style\n        monthLabel: {\n          show: true,\n          // start end\n          position: 'start',\n          margin: 5,\n          // center or left\n          align: 'center',\n          formatter: null,\n          color: '#000'\n        },\n        // year text style\n        yearLabel: {\n          show: true,\n          // top bottom left right\n          position: null,\n          margin: 30,\n          formatter: null,\n          color: '#ccc',\n          fontFamily: 'sans-serif',\n          fontWeight: 'bolder',\n          fontSize: 20\n        }\n      };\n      return CalendarModel;\n    }(ComponentModel);\n\n    function mergeAndNormalizeLayoutParams(target, raw) {\n      // Normalize cellSize\n      var cellSize = target.cellSize;\n      var cellSizeArr;\n\n      if (!isArray(cellSize)) {\n        cellSizeArr = target.cellSize = [cellSize, cellSize];\n      } else {\n        cellSizeArr = cellSize;\n      }\n\n      if (cellSizeArr.length === 1) {\n        cellSizeArr[1] = cellSizeArr[0];\n      }\n\n      var ignoreSize = map([0, 1], function (hvIdx) {\n        // If user have set `width` or both `left` and `right`, cellSizeArr\n        // will be automatically set to 'auto', otherwise the default\n        // setting of cellSizeArr will make `width` setting not work.\n        if (sizeCalculable(raw, hvIdx)) {\n          cellSizeArr[hvIdx] = 'auto';\n        }\n\n        return cellSizeArr[hvIdx] != null && cellSizeArr[hvIdx] !== 'auto';\n      });\n      mergeLayoutParam(target, raw, {\n        type: 'box',\n        ignoreSize: ignoreSize\n      });\n    }\n\n    var CalendarView =\n    /** @class */\n    function (_super) {\n      __extends(CalendarView, _super);\n\n      function CalendarView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CalendarView.type;\n        return _this;\n      }\n\n      CalendarView.prototype.render = function (calendarModel, ecModel, api) {\n        var group = this.group;\n        group.removeAll();\n        var coordSys = calendarModel.coordinateSystem; // range info\n\n        var rangeData = coordSys.getRangeInfo();\n        var orient = coordSys.getOrient(); // locale\n\n        var localeModel = ecModel.getLocaleModel();\n\n        this._renderDayRect(calendarModel, rangeData, group); // _renderLines must be called prior to following function\n\n\n        this._renderLines(calendarModel, rangeData, orient, group);\n\n        this._renderYearText(calendarModel, rangeData, orient, group);\n\n        this._renderMonthText(calendarModel, localeModel, orient, group);\n\n        this._renderWeekText(calendarModel, localeModel, rangeData, orient, group);\n      }; // render day rect\n\n\n      CalendarView.prototype._renderDayRect = function (calendarModel, rangeData, group) {\n        var coordSys = calendarModel.coordinateSystem;\n        var itemRectStyleModel = calendarModel.getModel('itemStyle').getItemStyle();\n        var sw = coordSys.getCellWidth();\n        var sh = coordSys.getCellHeight();\n\n        for (var i = rangeData.start.time; i <= rangeData.end.time; i = coordSys.getNextNDay(i, 1).time) {\n          var point = coordSys.dataToRect([i], false).tl; // every rect\n\n          var rect = new Rect({\n            shape: {\n              x: point[0],\n              y: point[1],\n              width: sw,\n              height: sh\n            },\n            cursor: 'default',\n            style: itemRectStyleModel\n          });\n          group.add(rect);\n        }\n      }; // render separate line\n\n\n      CalendarView.prototype._renderLines = function (calendarModel, rangeData, orient, group) {\n        var self = this;\n        var coordSys = calendarModel.coordinateSystem;\n        var lineStyleModel = calendarModel.getModel(['splitLine', 'lineStyle']).getLineStyle();\n        var show = calendarModel.get(['splitLine', 'show']);\n        var lineWidth = lineStyleModel.lineWidth;\n        this._tlpoints = [];\n        this._blpoints = [];\n        this._firstDayOfMonth = [];\n        this._firstDayPoints = [];\n        var firstDay = rangeData.start;\n\n        for (var i = 0; firstDay.time <= rangeData.end.time; i++) {\n          addPoints(firstDay.formatedDate);\n\n          if (i === 0) {\n            firstDay = coordSys.getDateInfo(rangeData.start.y + '-' + rangeData.start.m);\n          }\n\n          var date = firstDay.date;\n          date.setMonth(date.getMonth() + 1);\n          firstDay = coordSys.getDateInfo(date);\n        }\n\n        addPoints(coordSys.getNextNDay(rangeData.end.time, 1).formatedDate);\n\n        function addPoints(date) {\n          self._firstDayOfMonth.push(coordSys.getDateInfo(date));\n\n          self._firstDayPoints.push(coordSys.dataToRect([date], false).tl);\n\n          var points = self._getLinePointsOfOneWeek(calendarModel, date, orient);\n\n          self._tlpoints.push(points[0]);\n\n          self._blpoints.push(points[points.length - 1]);\n\n          show && self._drawSplitline(points, lineStyleModel, group);\n        } // render top/left line\n\n\n        show && this._drawSplitline(self._getEdgesPoints(self._tlpoints, lineWidth, orient), lineStyleModel, group); // render bottom/right line\n\n        show && this._drawSplitline(self._getEdgesPoints(self._blpoints, lineWidth, orient), lineStyleModel, group);\n      }; // get points at both ends\n\n\n      CalendarView.prototype._getEdgesPoints = function (points, lineWidth, orient) {\n        var rs = [points[0].slice(), points[points.length - 1].slice()];\n        var idx = orient === 'horizontal' ? 0 : 1; // both ends of the line are extend half lineWidth\n\n        rs[0][idx] = rs[0][idx] - lineWidth / 2;\n        rs[1][idx] = rs[1][idx] + lineWidth / 2;\n        return rs;\n      }; // render split line\n\n\n      CalendarView.prototype._drawSplitline = function (points, lineStyle, group) {\n        var poyline = new Polyline({\n          z2: 20,\n          shape: {\n            points: points\n          },\n          style: lineStyle\n        });\n        group.add(poyline);\n      }; // render month line of one week points\n\n\n      CalendarView.prototype._getLinePointsOfOneWeek = function (calendarModel, date, orient) {\n        var coordSys = calendarModel.coordinateSystem;\n        var parsedDate = coordSys.getDateInfo(date);\n        var points = [];\n\n        for (var i = 0; i < 7; i++) {\n          var tmpD = coordSys.getNextNDay(parsedDate.time, i);\n          var point = coordSys.dataToRect([tmpD.time], false);\n          points[2 * tmpD.day] = point.tl;\n          points[2 * tmpD.day + 1] = point[orient === 'horizontal' ? 'bl' : 'tr'];\n        }\n\n        return points;\n      };\n\n      CalendarView.prototype._formatterLabel = function (formatter, params) {\n        if (isString(formatter) && formatter) {\n          return formatTplSimple(formatter, params);\n        }\n\n        if (isFunction(formatter)) {\n          return formatter(params);\n        }\n\n        return params.nameMap;\n      };\n\n      CalendarView.prototype._yearTextPositionControl = function (textEl, point, orient, position, margin) {\n        var x = point[0];\n        var y = point[1];\n        var aligns = ['center', 'bottom'];\n\n        if (position === 'bottom') {\n          y += margin;\n          aligns = ['center', 'top'];\n        } else if (position === 'left') {\n          x -= margin;\n        } else if (position === 'right') {\n          x += margin;\n          aligns = ['center', 'top'];\n        } else {\n          // top\n          y -= margin;\n        }\n\n        var rotate = 0;\n\n        if (position === 'left' || position === 'right') {\n          rotate = Math.PI / 2;\n        }\n\n        return {\n          rotation: rotate,\n          x: x,\n          y: y,\n          style: {\n            align: aligns[0],\n            verticalAlign: aligns[1]\n          }\n        };\n      }; // render year\n\n\n      CalendarView.prototype._renderYearText = function (calendarModel, rangeData, orient, group) {\n        var yearLabel = calendarModel.getModel('yearLabel');\n\n        if (!yearLabel.get('show')) {\n          return;\n        }\n\n        var margin = yearLabel.get('margin');\n        var pos = yearLabel.get('position');\n\n        if (!pos) {\n          pos = orient !== 'horizontal' ? 'top' : 'left';\n        }\n\n        var points = [this._tlpoints[this._tlpoints.length - 1], this._blpoints[0]];\n        var xc = (points[0][0] + points[1][0]) / 2;\n        var yc = (points[0][1] + points[1][1]) / 2;\n        var idx = orient === 'horizontal' ? 0 : 1;\n        var posPoints = {\n          top: [xc, points[idx][1]],\n          bottom: [xc, points[1 - idx][1]],\n          left: [points[1 - idx][0], yc],\n          right: [points[idx][0], yc]\n        };\n        var name = rangeData.start.y;\n\n        if (+rangeData.end.y > +rangeData.start.y) {\n          name = name + '-' + rangeData.end.y;\n        }\n\n        var formatter = yearLabel.get('formatter');\n        var params = {\n          start: rangeData.start.y,\n          end: rangeData.end.y,\n          nameMap: name\n        };\n\n        var content = this._formatterLabel(formatter, params);\n\n        var yearText = new ZRText({\n          z2: 30,\n          style: createTextStyle(yearLabel, {\n            text: content\n          })\n        });\n        yearText.attr(this._yearTextPositionControl(yearText, posPoints[pos], orient, pos, margin));\n        group.add(yearText);\n      };\n\n      CalendarView.prototype._monthTextPositionControl = function (point, isCenter, orient, position, margin) {\n        var align = 'left';\n        var vAlign = 'top';\n        var x = point[0];\n        var y = point[1];\n\n        if (orient === 'horizontal') {\n          y = y + margin;\n\n          if (isCenter) {\n            align = 'center';\n          }\n\n          if (position === 'start') {\n            vAlign = 'bottom';\n          }\n        } else {\n          x = x + margin;\n\n          if (isCenter) {\n            vAlign = 'middle';\n          }\n\n          if (position === 'start') {\n            align = 'right';\n          }\n        }\n\n        return {\n          x: x,\n          y: y,\n          align: align,\n          verticalAlign: vAlign\n        };\n      }; // render month and year text\n\n\n      CalendarView.prototype._renderMonthText = function (calendarModel, localeModel, orient, group) {\n        var monthLabel = calendarModel.getModel('monthLabel');\n\n        if (!monthLabel.get('show')) {\n          return;\n        }\n\n        var nameMap = monthLabel.get('nameMap');\n        var margin = monthLabel.get('margin');\n        var pos = monthLabel.get('position');\n        var align = monthLabel.get('align');\n        var termPoints = [this._tlpoints, this._blpoints];\n\n        if (!nameMap || isString(nameMap)) {\n          if (nameMap) {\n            // case-sensitive\n            localeModel = getLocaleModel(nameMap) || localeModel;\n          } // PENDING\n          // for ZH locale, original form is `一月` but current form is `1月`\n\n\n          nameMap = localeModel.get(['time', 'monthAbbr']) || [];\n        }\n\n        var idx = pos === 'start' ? 0 : 1;\n        var axis = orient === 'horizontal' ? 0 : 1;\n        margin = pos === 'start' ? -margin : margin;\n        var isCenter = align === 'center';\n\n        for (var i = 0; i < termPoints[idx].length - 1; i++) {\n          var tmp = termPoints[idx][i].slice();\n          var firstDay = this._firstDayOfMonth[i];\n\n          if (isCenter) {\n            var firstDayPoints = this._firstDayPoints[i];\n            tmp[axis] = (firstDayPoints[axis] + termPoints[0][i + 1][axis]) / 2;\n          }\n\n          var formatter = monthLabel.get('formatter');\n          var name_1 = nameMap[+firstDay.m - 1];\n          var params = {\n            yyyy: firstDay.y,\n            yy: (firstDay.y + '').slice(2),\n            MM: firstDay.m,\n            M: +firstDay.m,\n            nameMap: name_1\n          };\n\n          var content = this._formatterLabel(formatter, params);\n\n          var monthText = new ZRText({\n            z2: 30,\n            style: extend(createTextStyle(monthLabel, {\n              text: content\n            }), this._monthTextPositionControl(tmp, isCenter, orient, pos, margin))\n          });\n          group.add(monthText);\n        }\n      };\n\n      CalendarView.prototype._weekTextPositionControl = function (point, orient, position, margin, cellSize) {\n        var align = 'center';\n        var vAlign = 'middle';\n        var x = point[0];\n        var y = point[1];\n        var isStart = position === 'start';\n\n        if (orient === 'horizontal') {\n          x = x + margin + (isStart ? 1 : -1) * cellSize[0] / 2;\n          align = isStart ? 'right' : 'left';\n        } else {\n          y = y + margin + (isStart ? 1 : -1) * cellSize[1] / 2;\n          vAlign = isStart ? 'bottom' : 'top';\n        }\n\n        return {\n          x: x,\n          y: y,\n          align: align,\n          verticalAlign: vAlign\n        };\n      }; // render weeks\n\n\n      CalendarView.prototype._renderWeekText = function (calendarModel, localeModel, rangeData, orient, group) {\n        var dayLabel = calendarModel.getModel('dayLabel');\n\n        if (!dayLabel.get('show')) {\n          return;\n        }\n\n        var coordSys = calendarModel.coordinateSystem;\n        var pos = dayLabel.get('position');\n        var nameMap = dayLabel.get('nameMap');\n        var margin = dayLabel.get('margin');\n        var firstDayOfWeek = coordSys.getFirstDayOfWeek();\n\n        if (!nameMap || isString(nameMap)) {\n          if (nameMap) {\n            // case-sensitive\n            localeModel = getLocaleModel(nameMap) || localeModel;\n          } // Use the first letter of `dayOfWeekAbbr` if `dayOfWeekShort` doesn't exist in the locale file\n\n\n          var dayOfWeekShort = localeModel.get(['time', 'dayOfWeekShort']);\n          nameMap = dayOfWeekShort || map(localeModel.get(['time', 'dayOfWeekAbbr']), function (val) {\n            return val[0];\n          });\n        }\n\n        var start = coordSys.getNextNDay(rangeData.end.time, 7 - rangeData.lweek).time;\n        var cellSize = [coordSys.getCellWidth(), coordSys.getCellHeight()];\n        margin = parsePercent$1(margin, Math.min(cellSize[1], cellSize[0]));\n\n        if (pos === 'start') {\n          start = coordSys.getNextNDay(rangeData.start.time, -(7 + rangeData.fweek)).time;\n          margin = -margin;\n        }\n\n        for (var i = 0; i < 7; i++) {\n          var tmpD = coordSys.getNextNDay(start, i);\n          var point = coordSys.dataToRect([tmpD.time], false).center;\n          var day = i;\n          day = Math.abs((i + firstDayOfWeek) % 7);\n          var weekText = new ZRText({\n            z2: 30,\n            style: extend(createTextStyle(dayLabel, {\n              text: nameMap[day]\n            }), this._weekTextPositionControl(point, orient, pos, margin, cellSize))\n          });\n          group.add(weekText);\n        }\n      };\n\n      CalendarView.type = 'calendar';\n      return CalendarView;\n    }(ComponentView);\n\n    var PROXIMATE_ONE_DAY = 86400000;\n\n    var Calendar =\n    /** @class */\n    function () {\n      function Calendar(calendarModel, ecModel, api) {\n        this.type = 'calendar';\n        this.dimensions = Calendar.dimensions; // Required in createListFromData\n\n        this.getDimensionsInfo = Calendar.getDimensionsInfo;\n        this._model = calendarModel;\n      }\n\n      Calendar.getDimensionsInfo = function () {\n        return [{\n          name: 'time',\n          type: 'time'\n        }, 'value'];\n      };\n\n      Calendar.prototype.getRangeInfo = function () {\n        return this._rangeInfo;\n      };\n\n      Calendar.prototype.getModel = function () {\n        return this._model;\n      };\n\n      Calendar.prototype.getRect = function () {\n        return this._rect;\n      };\n\n      Calendar.prototype.getCellWidth = function () {\n        return this._sw;\n      };\n\n      Calendar.prototype.getCellHeight = function () {\n        return this._sh;\n      };\n\n      Calendar.prototype.getOrient = function () {\n        return this._orient;\n      };\n      /**\n       * getFirstDayOfWeek\n       *\n       * @example\n       *     0 : start at Sunday\n       *     1 : start at Monday\n       *\n       * @return {number}\n       */\n\n\n      Calendar.prototype.getFirstDayOfWeek = function () {\n        return this._firstDayOfWeek;\n      };\n      /**\n       * get date info\n       * }\n       */\n\n\n      Calendar.prototype.getDateInfo = function (date) {\n        date = parseDate(date);\n        var y = date.getFullYear();\n        var m = date.getMonth() + 1;\n        var mStr = m < 10 ? '0' + m : '' + m;\n        var d = date.getDate();\n        var dStr = d < 10 ? '0' + d : '' + d;\n        var day = date.getDay();\n        day = Math.abs((day + 7 - this.getFirstDayOfWeek()) % 7);\n        return {\n          y: y + '',\n          m: mStr,\n          d: dStr,\n          day: day,\n          time: date.getTime(),\n          formatedDate: y + '-' + mStr + '-' + dStr,\n          date: date\n        };\n      };\n\n      Calendar.prototype.getNextNDay = function (date, n) {\n        n = n || 0;\n\n        if (n === 0) {\n          return this.getDateInfo(date);\n        }\n\n        date = new Date(this.getDateInfo(date).time);\n        date.setDate(date.getDate() + n);\n        return this.getDateInfo(date);\n      };\n\n      Calendar.prototype.update = function (ecModel, api) {\n        this._firstDayOfWeek = +this._model.getModel('dayLabel').get('firstDay');\n        this._orient = this._model.get('orient');\n        this._lineWidth = this._model.getModel('itemStyle').getItemStyle().lineWidth || 0;\n        this._rangeInfo = this._getRangeInfo(this._initRangeOption());\n        var weeks = this._rangeInfo.weeks || 1;\n        var whNames = ['width', 'height'];\n\n        var cellSize = this._model.getCellSize().slice();\n\n        var layoutParams = this._model.getBoxLayoutParams();\n\n        var cellNumbers = this._orient === 'horizontal' ? [weeks, 7] : [7, weeks];\n        each([0, 1], function (idx) {\n          if (cellSizeSpecified(cellSize, idx)) {\n            layoutParams[whNames[idx]] = cellSize[idx] * cellNumbers[idx];\n          }\n        });\n        var whGlobal = {\n          width: api.getWidth(),\n          height: api.getHeight()\n        };\n        var calendarRect = this._rect = getLayoutRect(layoutParams, whGlobal);\n        each([0, 1], function (idx) {\n          if (!cellSizeSpecified(cellSize, idx)) {\n            cellSize[idx] = calendarRect[whNames[idx]] / cellNumbers[idx];\n          }\n        });\n\n        function cellSizeSpecified(cellSize, idx) {\n          return cellSize[idx] != null && cellSize[idx] !== 'auto';\n        } // Has been calculated out number.\n\n\n        this._sw = cellSize[0];\n        this._sh = cellSize[1];\n      };\n      /**\n       * Convert a time data(time, value) item to (x, y) point.\n       */\n      // TODO Clamp of calendar is not same with cartesian coordinate systems.\n      // It will return NaN if data exceeds.\n\n\n      Calendar.prototype.dataToPoint = function (data, clamp) {\n        isArray(data) && (data = data[0]);\n        clamp == null && (clamp = true);\n        var dayInfo = this.getDateInfo(data);\n        var range = this._rangeInfo;\n        var date = dayInfo.formatedDate; // if not in range return [NaN, NaN]\n\n        if (clamp && !(dayInfo.time >= range.start.time && dayInfo.time < range.end.time + PROXIMATE_ONE_DAY)) {\n          return [NaN, NaN];\n        }\n\n        var week = dayInfo.day;\n\n        var nthWeek = this._getRangeInfo([range.start.time, date]).nthWeek;\n\n        if (this._orient === 'vertical') {\n          return [this._rect.x + week * this._sw + this._sw / 2, this._rect.y + nthWeek * this._sh + this._sh / 2];\n        }\n\n        return [this._rect.x + nthWeek * this._sw + this._sw / 2, this._rect.y + week * this._sh + this._sh / 2];\n      };\n      /**\n       * Convert a (x, y) point to time data\n       */\n\n\n      Calendar.prototype.pointToData = function (point) {\n        var date = this.pointToDate(point);\n        return date && date.time;\n      };\n      /**\n       * Convert a time date item to (x, y) four point.\n       */\n\n\n      Calendar.prototype.dataToRect = function (data, clamp) {\n        var point = this.dataToPoint(data, clamp);\n        return {\n          contentShape: {\n            x: point[0] - (this._sw - this._lineWidth) / 2,\n            y: point[1] - (this._sh - this._lineWidth) / 2,\n            width: this._sw - this._lineWidth,\n            height: this._sh - this._lineWidth\n          },\n          center: point,\n          tl: [point[0] - this._sw / 2, point[1] - this._sh / 2],\n          tr: [point[0] + this._sw / 2, point[1] - this._sh / 2],\n          br: [point[0] + this._sw / 2, point[1] + this._sh / 2],\n          bl: [point[0] - this._sw / 2, point[1] + this._sh / 2]\n        };\n      };\n      /**\n       * Convert a (x, y) point to time date\n       *\n       * @param  {Array} point point\n       * @return {Object}       date\n       */\n\n\n      Calendar.prototype.pointToDate = function (point) {\n        var nthX = Math.floor((point[0] - this._rect.x) / this._sw) + 1;\n        var nthY = Math.floor((point[1] - this._rect.y) / this._sh) + 1;\n        var range = this._rangeInfo.range;\n\n        if (this._orient === 'vertical') {\n          return this._getDateByWeeksAndDay(nthY, nthX - 1, range);\n        }\n\n        return this._getDateByWeeksAndDay(nthX, nthY - 1, range);\n      };\n\n      Calendar.prototype.convertToPixel = function (ecModel, finder, value) {\n        var coordSys = getCoordSys$4(finder);\n        return coordSys === this ? coordSys.dataToPoint(value) : null;\n      };\n\n      Calendar.prototype.convertFromPixel = function (ecModel, finder, pixel) {\n        var coordSys = getCoordSys$4(finder);\n        return coordSys === this ? coordSys.pointToData(pixel) : null;\n      };\n\n      Calendar.prototype.containPoint = function (point) {\n        console.warn('Not implemented.');\n        return false;\n      };\n      /**\n       * initRange\n       * Normalize to an [start, end] array\n       */\n\n\n      Calendar.prototype._initRangeOption = function () {\n        var range = this._model.get('range');\n\n        var normalizedRange; // Convert [1990] to 1990\n\n        if (isArray(range) && range.length === 1) {\n          range = range[0];\n        }\n\n        if (!isArray(range)) {\n          var rangeStr = range.toString(); // One year.\n\n          if (/^\\d{4}$/.test(rangeStr)) {\n            normalizedRange = [rangeStr + '-01-01', rangeStr + '-12-31'];\n          } // One month\n\n\n          if (/^\\d{4}[\\/|-]\\d{1,2}$/.test(rangeStr)) {\n            var start = this.getDateInfo(rangeStr);\n            var firstDay = start.date;\n            firstDay.setMonth(firstDay.getMonth() + 1);\n            var end = this.getNextNDay(firstDay, -1);\n            normalizedRange = [start.formatedDate, end.formatedDate];\n          } // One day\n\n\n          if (/^\\d{4}[\\/|-]\\d{1,2}[\\/|-]\\d{1,2}$/.test(rangeStr)) {\n            normalizedRange = [rangeStr, rangeStr];\n          }\n        } else {\n          normalizedRange = range;\n        }\n\n        if (!normalizedRange) {\n          if (\"development\" !== 'production') {\n            logError('Invalid date range.');\n          } // Not handling it.\n\n\n          return range;\n        }\n\n        var tmp = this._getRangeInfo(normalizedRange);\n\n        if (tmp.start.time > tmp.end.time) {\n          normalizedRange.reverse();\n        }\n\n        return normalizedRange;\n      };\n      /**\n       * range info\n       *\n       * @private\n       * @param  {Array} range range ['2017-01-01', '2017-07-08']\n       *  If range[0] > range[1], they will not be reversed.\n       * @return {Object}       obj\n       */\n\n\n      Calendar.prototype._getRangeInfo = function (range) {\n        var parsedRange = [this.getDateInfo(range[0]), this.getDateInfo(range[1])];\n        var reversed;\n\n        if (parsedRange[0].time > parsedRange[1].time) {\n          reversed = true;\n          parsedRange.reverse();\n        }\n\n        var allDay = Math.floor(parsedRange[1].time / PROXIMATE_ONE_DAY) - Math.floor(parsedRange[0].time / PROXIMATE_ONE_DAY) + 1; // Consider case1 (#11677 #10430):\n        // Set the system timezone as \"UK\", set the range to `['2016-07-01', '2016-12-31']`\n        // Consider case2:\n        // Firstly set system timezone as \"Time Zone: America/Toronto\",\n        // ```\n        // let first = new Date(1478412000000 - 3600 * 1000 * 2.5);\n        // let second = new Date(1478412000000);\n        // let allDays = Math.floor(second / ONE_DAY) - Math.floor(first / ONE_DAY) + 1;\n        // ```\n        // will get wrong result because of DST. So we should fix it.\n\n        var date = new Date(parsedRange[0].time);\n        var startDateNum = date.getDate();\n        var endDateNum = parsedRange[1].date.getDate();\n        date.setDate(startDateNum + allDay - 1); // The bias can not over a month, so just compare date.\n\n        var dateNum = date.getDate();\n\n        if (dateNum !== endDateNum) {\n          var sign = date.getTime() - parsedRange[1].time > 0 ? 1 : -1;\n\n          while ((dateNum = date.getDate()) !== endDateNum && (date.getTime() - parsedRange[1].time) * sign > 0) {\n            allDay -= sign;\n            date.setDate(dateNum - sign);\n          }\n        }\n\n        var weeks = Math.floor((allDay + parsedRange[0].day + 6) / 7);\n        var nthWeek = reversed ? -weeks + 1 : weeks - 1;\n        reversed && parsedRange.reverse();\n        return {\n          range: [parsedRange[0].formatedDate, parsedRange[1].formatedDate],\n          start: parsedRange[0],\n          end: parsedRange[1],\n          allDay: allDay,\n          weeks: weeks,\n          // From 0.\n          nthWeek: nthWeek,\n          fweek: parsedRange[0].day,\n          lweek: parsedRange[1].day\n        };\n      };\n      /**\n       * get date by nthWeeks and week day in range\n       *\n       * @private\n       * @param  {number} nthWeek the week\n       * @param  {number} day   the week day\n       * @param  {Array} range [d1, d2]\n       * @return {Object}\n       */\n\n\n      Calendar.prototype._getDateByWeeksAndDay = function (nthWeek, day, range) {\n        var rangeInfo = this._getRangeInfo(range);\n\n        if (nthWeek > rangeInfo.weeks || nthWeek === 0 && day < rangeInfo.fweek || nthWeek === rangeInfo.weeks && day > rangeInfo.lweek) {\n          return null;\n        }\n\n        var nthDay = (nthWeek - 1) * 7 - rangeInfo.fweek + day;\n        var date = new Date(rangeInfo.start.time);\n        date.setDate(+rangeInfo.start.d + nthDay);\n        return this.getDateInfo(date);\n      };\n\n      Calendar.create = function (ecModel, api) {\n        var calendarList = [];\n        ecModel.eachComponent('calendar', function (calendarModel) {\n          var calendar = new Calendar(calendarModel, ecModel, api);\n          calendarList.push(calendar);\n          calendarModel.coordinateSystem = calendar;\n        });\n        ecModel.eachSeries(function (calendarSeries) {\n          if (calendarSeries.get('coordinateSystem') === 'calendar') {\n            // Inject coordinate system\n            calendarSeries.coordinateSystem = calendarList[calendarSeries.get('calendarIndex') || 0];\n          }\n        });\n        return calendarList;\n      };\n\n      Calendar.dimensions = ['time', 'value'];\n      return Calendar;\n    }();\n\n    function getCoordSys$4(finder) {\n      var calendarModel = finder.calendarModel;\n      var seriesModel = finder.seriesModel;\n      var coordSys = calendarModel ? calendarModel.coordinateSystem : seriesModel ? seriesModel.coordinateSystem : null;\n      return coordSys;\n    }\n\n    function install$w(registers) {\n      registers.registerComponentModel(CalendarModel);\n      registers.registerComponentView(CalendarView);\n      registers.registerCoordinateSystem('calendar', Calendar);\n    }\n\n    function setKeyInfoToNewElOption(resultItem, newElOption) {\n      var existElOption = resultItem.existing; // Set id and type after id assigned.\n\n      newElOption.id = resultItem.keyInfo.id;\n      !newElOption.type && existElOption && (newElOption.type = existElOption.type); // Set parent id if not specified\n\n      if (newElOption.parentId == null) {\n        var newElParentOption = newElOption.parentOption;\n\n        if (newElParentOption) {\n          newElOption.parentId = newElParentOption.id;\n        } else if (existElOption) {\n          newElOption.parentId = existElOption.parentId;\n        }\n      } // Clear\n\n\n      newElOption.parentOption = null;\n    }\n\n    function isSetLoc(obj, props) {\n      var isSet;\n      each(props, function (prop) {\n        obj[prop] != null && obj[prop] !== 'auto' && (isSet = true);\n      });\n      return isSet;\n    }\n\n    function mergeNewElOptionToExist(existList, index, newElOption) {\n      // Update existing options, for `getOption` feature.\n      var newElOptCopy = extend({}, newElOption);\n      var existElOption = existList[index];\n      var $action = newElOption.$action || 'merge';\n\n      if ($action === 'merge') {\n        if (existElOption) {\n          if (\"development\" !== 'production') {\n            var newType = newElOption.type;\n            assert(!newType || existElOption.type === newType, 'Please set $action: \"replace\" to change `type`');\n          } // We can ensure that newElOptCopy and existElOption are not\n          // the same object, so `merge` will not change newElOptCopy.\n\n\n          merge(existElOption, newElOptCopy, true); // Rigid body, use ignoreSize.\n\n          mergeLayoutParam(existElOption, newElOptCopy, {\n            ignoreSize: true\n          }); // Will be used in render.\n\n          copyLayoutParams(newElOption, existElOption); // Copy transition info to new option so it can be used in the transition.\n          // DO IT AFTER merge\n\n          copyTransitionInfo(newElOption, existElOption);\n          copyTransitionInfo(newElOption, existElOption, 'shape');\n          copyTransitionInfo(newElOption, existElOption, 'style');\n          copyTransitionInfo(newElOption, existElOption, 'extra'); // Copy clipPath\n\n          newElOption.clipPath = existElOption.clipPath;\n        } else {\n          existList[index] = newElOptCopy;\n        }\n      } else if ($action === 'replace') {\n        existList[index] = newElOptCopy;\n      } else if ($action === 'remove') {\n        // null will be cleaned later.\n        existElOption && (existList[index] = null);\n      }\n    }\n\n    var TRANSITION_PROPS_TO_COPY = ['transition', 'enterFrom', 'leaveTo'];\n    var ROOT_TRANSITION_PROPS_TO_COPY = TRANSITION_PROPS_TO_COPY.concat(['enterAnimation', 'updateAnimation', 'leaveAnimation']);\n\n    function copyTransitionInfo(target, source, targetProp) {\n      if (targetProp) {\n        if (!target[targetProp] && source[targetProp]) {\n          // TODO avoid creating this empty object when there is no transition configuration.\n          target[targetProp] = {};\n        }\n\n        target = target[targetProp];\n        source = source[targetProp];\n      }\n\n      if (!target || !source) {\n        return;\n      }\n\n      var props = targetProp ? TRANSITION_PROPS_TO_COPY : ROOT_TRANSITION_PROPS_TO_COPY;\n\n      for (var i = 0; i < props.length; i++) {\n        var prop = props[i];\n\n        if (target[prop] == null && source[prop] != null) {\n          target[prop] = source[prop];\n        }\n      }\n    }\n\n    function setLayoutInfoToExist(existItem, newElOption) {\n      if (!existItem) {\n        return;\n      }\n\n      existItem.hv = newElOption.hv = [// Rigid body, don't care about `width`.\n      isSetLoc(newElOption, ['left', 'right']), // Rigid body, don't care about `height`.\n      isSetLoc(newElOption, ['top', 'bottom'])]; // Give default group size. Otherwise layout error may occur.\n\n      if (existItem.type === 'group') {\n        var existingGroupOpt = existItem;\n        var newGroupOpt = newElOption;\n        existingGroupOpt.width == null && (existingGroupOpt.width = newGroupOpt.width = 0);\n        existingGroupOpt.height == null && (existingGroupOpt.height = newGroupOpt.height = 0);\n      }\n    }\n\n    var GraphicComponentModel =\n    /** @class */\n    function (_super) {\n      __extends(GraphicComponentModel, _super);\n\n      function GraphicComponentModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GraphicComponentModel.type;\n        _this.preventAutoZ = true;\n        return _this;\n      }\n\n      GraphicComponentModel.prototype.mergeOption = function (option, ecModel) {\n        // Prevent default merge to elements\n        var elements = this.option.elements;\n        this.option.elements = null;\n\n        _super.prototype.mergeOption.call(this, option, ecModel);\n\n        this.option.elements = elements;\n      };\n\n      GraphicComponentModel.prototype.optionUpdated = function (newOption, isInit) {\n        var thisOption = this.option;\n        var newList = (isInit ? thisOption : newOption).elements;\n        var existList = thisOption.elements = isInit ? [] : thisOption.elements;\n        var flattenedList = [];\n\n        this._flatten(newList, flattenedList, null);\n\n        var mappingResult = mappingToExists(existList, flattenedList, 'normalMerge'); // Clear elOptionsToUpdate\n\n        var elOptionsToUpdate = this._elOptionsToUpdate = [];\n        each(mappingResult, function (resultItem, index) {\n          var newElOption = resultItem.newOption;\n\n          if (\"development\" !== 'production') {\n            assert(isObject(newElOption) || resultItem.existing, 'Empty graphic option definition');\n          }\n\n          if (!newElOption) {\n            return;\n          }\n\n          elOptionsToUpdate.push(newElOption);\n          setKeyInfoToNewElOption(resultItem, newElOption);\n          mergeNewElOptionToExist(existList, index, newElOption);\n          setLayoutInfoToExist(existList[index], newElOption);\n        }, this); // Clean\n\n        thisOption.elements = filter(existList, function (item) {\n          // $action should be volatile, otherwise option gotten from\n          // `getOption` will contain unexpected $action.\n          item && delete item.$action;\n          return item != null;\n        });\n      };\n      /**\n       * Convert\n       * [{\n       *  type: 'group',\n       *  id: 'xx',\n       *  children: [{type: 'circle'}, {type: 'polygon'}]\n       * }]\n       * to\n       * [\n       *  {type: 'group', id: 'xx'},\n       *  {type: 'circle', parentId: 'xx'},\n       *  {type: 'polygon', parentId: 'xx'}\n       * ]\n       */\n\n\n      GraphicComponentModel.prototype._flatten = function (optionList, result, parentOption) {\n        each(optionList, function (option) {\n          if (!option) {\n            return;\n          }\n\n          if (parentOption) {\n            option.parentOption = parentOption;\n          }\n\n          result.push(option);\n          var children = option.children; // here we don't judge if option.type is `group`\n          // when new option doesn't provide `type`, it will cause that the children can't be updated.\n\n          if (children && children.length) {\n            this._flatten(children, result, option);\n          } // Deleting for JSON output, and for not affecting group creation.\n\n\n          delete option.children;\n        }, this);\n      }; // FIXME\n      // Pass to view using payload? setOption has a payload?\n\n\n      GraphicComponentModel.prototype.useElOptionsToUpdate = function () {\n        var els = this._elOptionsToUpdate; // Clear to avoid render duplicately when zooming.\n\n        this._elOptionsToUpdate = null;\n        return els;\n      };\n\n      GraphicComponentModel.type = 'graphic';\n      GraphicComponentModel.defaultOption = {\n        elements: [] // parentId: null\n\n      };\n      return GraphicComponentModel;\n    }(ComponentModel);\n\n    var nonShapeGraphicElements = {\n      // Reserved but not supported in graphic component.\n      path: null,\n      compoundPath: null,\n      // Supported in graphic component.\n      group: Group,\n      image: ZRImage,\n      text: ZRText\n    };\n    var inner$e = makeInner(); // ------------------------\n    // View\n    // ------------------------\n\n    var GraphicComponentView =\n    /** @class */\n    function (_super) {\n      __extends(GraphicComponentView, _super);\n\n      function GraphicComponentView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = GraphicComponentView.type;\n        return _this;\n      }\n\n      GraphicComponentView.prototype.init = function () {\n        this._elMap = createHashMap();\n      };\n\n      GraphicComponentView.prototype.render = function (graphicModel, ecModel, api) {\n        // Having leveraged between use cases and algorithm complexity, a very\n        // simple layout mechanism is used:\n        // The size(width/height) can be determined by itself or its parent (not\n        // implemented yet), but can not by its children. (Top-down travel)\n        // The location(x/y) can be determined by the bounding rect of itself\n        // (can including its descendants or not) and the size of its parent.\n        // (Bottom-up travel)\n        // When `chart.clear()` or `chart.setOption({...}, true)` with the same id,\n        // view will be reused.\n        if (graphicModel !== this._lastGraphicModel) {\n          this._clear();\n        }\n\n        this._lastGraphicModel = graphicModel;\n\n        this._updateElements(graphicModel);\n\n        this._relocate(graphicModel, api);\n      };\n      /**\n       * Update graphic elements.\n       */\n\n\n      GraphicComponentView.prototype._updateElements = function (graphicModel) {\n        var elOptionsToUpdate = graphicModel.useElOptionsToUpdate();\n\n        if (!elOptionsToUpdate) {\n          return;\n        }\n\n        var elMap = this._elMap;\n        var rootGroup = this.group;\n        var globalZ = graphicModel.get('z');\n        var globalZLevel = graphicModel.get('zlevel'); // Top-down tranverse to assign graphic settings to each elements.\n\n        each(elOptionsToUpdate, function (elOption) {\n          var id = convertOptionIdName(elOption.id, null);\n          var elExisting = id != null ? elMap.get(id) : null;\n          var parentId = convertOptionIdName(elOption.parentId, null);\n          var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup;\n          var elType = elOption.type;\n          var elOptionStyle = elOption.style;\n\n          if (elType === 'text' && elOptionStyle) {\n            // In top/bottom mode, textVerticalAlign should not be used, which cause\n            // inaccurately locating.\n            if (elOption.hv && elOption.hv[1]) {\n              elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = elOptionStyle.verticalAlign = elOptionStyle.align = null;\n            }\n          }\n\n          var textContentOption = elOption.textContent;\n          var textConfig = elOption.textConfig;\n\n          if (elOptionStyle && isEC4CompatibleStyle(elOptionStyle, elType, !!textConfig, !!textContentOption)) {\n            var convertResult = convertFromEC4CompatibleStyle(elOptionStyle, elType, true);\n\n            if (!textConfig && convertResult.textConfig) {\n              textConfig = elOption.textConfig = convertResult.textConfig;\n            }\n\n            if (!textContentOption && convertResult.textContent) {\n              textContentOption = convertResult.textContent;\n            }\n          } // Remove unnecessary props to avoid potential problems.\n\n\n          var elOptionCleaned = getCleanedElOption(elOption); // For simple, do not support parent change, otherwise reorder is needed.\n\n          if (\"development\" !== 'production') {\n            elExisting && assert(targetElParent === elExisting.parent, 'Changing parent is not supported.');\n          }\n\n          var $action = elOption.$action || 'merge';\n          var isMerge = $action === 'merge';\n          var isReplace = $action === 'replace';\n\n          if (isMerge) {\n            var isInit = !elExisting;\n            var el_1 = elExisting;\n\n            if (isInit) {\n              el_1 = createEl$1(id, targetElParent, elOption.type, elMap);\n            } else {\n              el_1 && (inner$e(el_1).isNew = false); // Stop and restore before update any other attributes.\n\n              stopPreviousKeyframeAnimationAndRestore(el_1);\n            }\n\n            if (el_1) {\n              applyUpdateTransition(el_1, elOptionCleaned, graphicModel, {\n                isInit: isInit\n              });\n              updateCommonAttrs(el_1, elOption, globalZ, globalZLevel);\n            }\n          } else if (isReplace) {\n            removeEl(elExisting, elOption, elMap, graphicModel);\n            var el_2 = createEl$1(id, targetElParent, elOption.type, elMap);\n\n            if (el_2) {\n              applyUpdateTransition(el_2, elOptionCleaned, graphicModel, {\n                isInit: true\n              });\n              updateCommonAttrs(el_2, elOption, globalZ, globalZLevel);\n            }\n          } else if ($action === 'remove') {\n            updateLeaveTo(elExisting, elOption);\n            removeEl(elExisting, elOption, elMap, graphicModel);\n          }\n\n          var el = elMap.get(id);\n\n          if (el && textContentOption) {\n            if (isMerge) {\n              var textContentExisting = el.getTextContent();\n              textContentExisting ? textContentExisting.attr(textContentOption) : el.setTextContent(new ZRText(textContentOption));\n            } else if (isReplace) {\n              el.setTextContent(new ZRText(textContentOption));\n            }\n          }\n\n          if (el) {\n            var clipPathOption = elOption.clipPath;\n\n            if (clipPathOption) {\n              var clipPathType = clipPathOption.type;\n              var clipPath = void 0;\n              var isInit = false;\n\n              if (isMerge) {\n                var oldClipPath = el.getClipPath();\n                isInit = !oldClipPath || inner$e(oldClipPath).type !== clipPathType;\n                clipPath = isInit ? newEl(clipPathType) : oldClipPath;\n              } else if (isReplace) {\n                isInit = true;\n                clipPath = newEl(clipPathType);\n              }\n\n              el.setClipPath(clipPath);\n              applyUpdateTransition(clipPath, clipPathOption, graphicModel, {\n                isInit: isInit\n              });\n              applyKeyframeAnimation(clipPath, clipPathOption.keyframeAnimation, graphicModel);\n            }\n\n            var elInner = inner$e(el);\n            el.setTextConfig(textConfig);\n            elInner.option = elOption;\n            setEventData(el, graphicModel, elOption);\n            setTooltipConfig({\n              el: el,\n              componentModel: graphicModel,\n              itemName: el.name,\n              itemTooltipOption: elOption.tooltip\n            });\n            applyKeyframeAnimation(el, elOption.keyframeAnimation, graphicModel);\n          }\n        });\n      };\n      /**\n       * Locate graphic elements.\n       */\n\n\n      GraphicComponentView.prototype._relocate = function (graphicModel, api) {\n        var elOptions = graphicModel.option.elements;\n        var rootGroup = this.group;\n        var elMap = this._elMap;\n        var apiWidth = api.getWidth();\n        var apiHeight = api.getHeight();\n        var xy = ['x', 'y']; // Top-down to calculate percentage width/height of group\n\n        for (var i = 0; i < elOptions.length; i++) {\n          var elOption = elOptions[i];\n          var id = convertOptionIdName(elOption.id, null);\n          var el = id != null ? elMap.get(id) : null;\n\n          if (!el || !el.isGroup) {\n            continue;\n          }\n\n          var parentEl = el.parent;\n          var isParentRoot = parentEl === rootGroup; // Like 'position:absolut' in css, default 0.\n\n          var elInner = inner$e(el);\n          var parentElInner = inner$e(parentEl);\n          elInner.width = parsePercent$1(elInner.option.width, isParentRoot ? apiWidth : parentElInner.width) || 0;\n          elInner.height = parsePercent$1(elInner.option.height, isParentRoot ? apiHeight : parentElInner.height) || 0;\n        } // Bottom-up tranvese all elements (consider ec resize) to locate elements.\n\n\n        for (var i = elOptions.length - 1; i >= 0; i--) {\n          var elOption = elOptions[i];\n          var id = convertOptionIdName(elOption.id, null);\n          var el = id != null ? elMap.get(id) : null;\n\n          if (!el) {\n            continue;\n          }\n\n          var parentEl = el.parent;\n          var parentElInner = inner$e(parentEl);\n          var containerInfo = parentEl === rootGroup ? {\n            width: apiWidth,\n            height: apiHeight\n          } : {\n            width: parentElInner.width,\n            height: parentElInner.height\n          }; // PENDING\n          // Currently, when `bounding: 'all'`, the union bounding rect of the group\n          // does not include the rect of [0, 0, group.width, group.height], which\n          // is probably weird for users. Should we make a break change for it?\n\n          var layoutPos = {};\n          var layouted = positionElement(el, elOption, containerInfo, null, {\n            hv: elOption.hv,\n            boundingMode: elOption.bounding\n          }, layoutPos);\n\n          if (!inner$e(el).isNew && layouted) {\n            var transition = elOption.transition;\n            var animatePos = {};\n\n            for (var k = 0; k < xy.length; k++) {\n              var key = xy[k];\n              var val = layoutPos[key];\n\n              if (transition && (isTransitionAll(transition) || indexOf(transition, key) >= 0)) {\n                animatePos[key] = val;\n              } else {\n                el[key] = val;\n              }\n            }\n\n            updateProps(el, animatePos, graphicModel, 0);\n          } else {\n            el.attr(layoutPos);\n          }\n        }\n      };\n      /**\n       * Clear all elements.\n       */\n\n\n      GraphicComponentView.prototype._clear = function () {\n        var _this = this;\n\n        var elMap = this._elMap;\n        elMap.each(function (el) {\n          removeEl(el, inner$e(el).option, elMap, _this._lastGraphicModel);\n        });\n        this._elMap = createHashMap();\n      };\n\n      GraphicComponentView.prototype.dispose = function () {\n        this._clear();\n      };\n\n      GraphicComponentView.type = 'graphic';\n      return GraphicComponentView;\n    }(ComponentView);\n\n    function newEl(graphicType) {\n      if (\"development\" !== 'production') {\n        assert(graphicType, 'graphic type MUST be set');\n      }\n\n      var Clz = hasOwn(nonShapeGraphicElements, graphicType) // Those graphic elements are not shapes. They should not be\n      // overwritten by users, so do them first.\n      ? nonShapeGraphicElements[graphicType] : getShapeClass(graphicType);\n\n      if (\"development\" !== 'production') {\n        assert(Clz, \"graphic type \" + graphicType + \" can not be found\");\n      }\n\n      var el = new Clz({});\n      inner$e(el).type = graphicType;\n      return el;\n    }\n\n    function createEl$1(id, targetElParent, graphicType, elMap) {\n      var el = newEl(graphicType);\n      targetElParent.add(el);\n      elMap.set(id, el);\n      inner$e(el).id = id;\n      inner$e(el).isNew = true;\n      return el;\n    }\n\n    function removeEl(elExisting, elOption, elMap, graphicModel) {\n      var existElParent = elExisting && elExisting.parent;\n\n      if (existElParent) {\n        elExisting.type === 'group' && elExisting.traverse(function (el) {\n          removeEl(el, elOption, elMap, graphicModel);\n        });\n        applyLeaveTransition(elExisting, elOption, graphicModel);\n        elMap.removeKey(inner$e(elExisting).id);\n      }\n    }\n\n    function updateCommonAttrs(el, elOption, defaultZ, defaultZlevel) {\n      if (!el.isGroup) {\n        each([['cursor', Displayable.prototype.cursor], // We should not support configure z and zlevel in the element level.\n        // But seems we didn't limit it previously. So here still use it to avoid breaking.\n        ['zlevel', defaultZlevel || 0], ['z', defaultZ || 0], // z2 must not be null/undefined, otherwise sort error may occur.\n        ['z2', 0]], function (item) {\n          var prop = item[0];\n\n          if (hasOwn(elOption, prop)) {\n            el[prop] = retrieve2(elOption[prop], item[1]);\n          } else if (el[prop] == null) {\n            el[prop] = item[1];\n          }\n        });\n      }\n\n      each(keys(elOption), function (key) {\n        // Assign event handlers.\n        // PENDING: should enumerate all event names or use pattern matching?\n        if (key.indexOf('on') === 0) {\n          var val = elOption[key];\n          el[key] = isFunction(val) ? val : null;\n        }\n      });\n\n      if (hasOwn(elOption, 'draggable')) {\n        el.draggable = elOption.draggable;\n      } // Other attributes\n\n\n      elOption.name != null && (el.name = elOption.name);\n      elOption.id != null && (el.id = elOption.id);\n    } // Remove unnecessary props to avoid potential problems.\n\n\n    function getCleanedElOption(elOption) {\n      elOption = extend({}, elOption);\n      each(['id', 'parentId', '$action', 'hv', 'bounding', 'textContent', 'clipPath'].concat(LOCATION_PARAMS), function (name) {\n        delete elOption[name];\n      });\n      return elOption;\n    }\n\n    function setEventData(el, graphicModel, elOption) {\n      var eventData = getECData(el).eventData; // Simple optimize for large amount of elements that no need event.\n\n      if (!el.silent && !el.ignore && !eventData) {\n        eventData = getECData(el).eventData = {\n          componentType: 'graphic',\n          componentIndex: graphicModel.componentIndex,\n          name: el.name\n        };\n      } // `elOption.info` enables user to mount some info on\n      // elements and use them in event handlers.\n\n\n      if (eventData) {\n        eventData.info = elOption.info;\n      }\n    }\n\n    function install$x(registers) {\n      registers.registerComponentModel(GraphicComponentModel);\n      registers.registerComponentView(GraphicComponentView);\n      registers.registerPreprocessor(function (option) {\n        var graphicOption = option.graphic; // Convert\n        // {graphic: [{left: 10, type: 'circle'}, ...]}\n        // or\n        // {graphic: {left: 10, type: 'circle'}}\n        // to\n        // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]}\n\n        if (isArray(graphicOption)) {\n          if (!graphicOption[0] || !graphicOption[0].elements) {\n            option.graphic = [{\n              elements: graphicOption\n            }];\n          } else {\n            // Only one graphic instance can be instantiated. (We don't\n            // want that too many views are created in echarts._viewMap.)\n            option.graphic = [option.graphic[0]];\n          }\n        } else if (graphicOption && !graphicOption.elements) {\n          option.graphic = [{\n            elements: [graphicOption]\n          }];\n        }\n      });\n    }\n\n    var DATA_ZOOM_AXIS_DIMENSIONS = ['x', 'y', 'radius', 'angle', 'single']; // Supported coords.\n    // FIXME: polar has been broken (but rarely used).\n\n    var SERIES_COORDS = ['cartesian2d', 'polar', 'singleAxis'];\n    function isCoordSupported(seriesModel) {\n      var coordType = seriesModel.get('coordinateSystem');\n      return indexOf(SERIES_COORDS, coordType) >= 0;\n    }\n    function getAxisMainType(axisDim) {\n      if (\"development\" !== 'production') {\n        assert(axisDim);\n      }\n\n      return axisDim + 'Axis';\n    }\n    /**\n     * If two dataZoomModels has the same axis controlled, we say that they are 'linked'.\n     * This function finds all linked dataZoomModels start from the given payload.\n     */\n\n    function findEffectedDataZooms(ecModel, payload) {\n      // Key: `DataZoomAxisDimension`\n      var axisRecords = createHashMap();\n      var effectedModels = []; // Key: uid of dataZoomModel\n\n      var effectedModelMap = createHashMap(); // Find the dataZooms specified by payload.\n\n      ecModel.eachComponent({\n        mainType: 'dataZoom',\n        query: payload\n      }, function (dataZoomModel) {\n        if (!effectedModelMap.get(dataZoomModel.uid)) {\n          addToEffected(dataZoomModel);\n        }\n      }); // Start from the given dataZoomModels, travel the graph to find\n      // all of the linked dataZoom models.\n\n      var foundNewLink;\n\n      do {\n        foundNewLink = false;\n        ecModel.eachComponent('dataZoom', processSingle);\n      } while (foundNewLink);\n\n      function processSingle(dataZoomModel) {\n        if (!effectedModelMap.get(dataZoomModel.uid) && isLinked(dataZoomModel)) {\n          addToEffected(dataZoomModel);\n          foundNewLink = true;\n        }\n      }\n\n      function addToEffected(dataZoom) {\n        effectedModelMap.set(dataZoom.uid, true);\n        effectedModels.push(dataZoom);\n        markAxisControlled(dataZoom);\n      }\n\n      function isLinked(dataZoomModel) {\n        var isLink = false;\n        dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n          var axisIdxArr = axisRecords.get(axisDim);\n\n          if (axisIdxArr && axisIdxArr[axisIndex]) {\n            isLink = true;\n          }\n        });\n        return isLink;\n      }\n\n      function markAxisControlled(dataZoomModel) {\n        dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n          (axisRecords.get(axisDim) || axisRecords.set(axisDim, []))[axisIndex] = true;\n        });\n      }\n\n      return effectedModels;\n    }\n    /**\n     * Find the first target coordinate system.\n     * Available after model built.\n     *\n     * @return Like {\n     *                  grid: [\n     *                      {model: coord0, axisModels: [axis1, axis3], coordIndex: 1},\n     *                      {model: coord1, axisModels: [axis0, axis2], coordIndex: 0},\n     *                      ...\n     *                  ],  // cartesians must not be null/undefined.\n     *                  polar: [\n     *                      {model: coord0, axisModels: [axis4], coordIndex: 0},\n     *                      ...\n     *                  ],  // polars must not be null/undefined.\n     *                  singleAxis: [\n     *                      {model: coord0, axisModels: [], coordIndex: 0}\n     *                  ]\n     *              }\n     */\n\n    function collectReferCoordSysModelInfo(dataZoomModel) {\n      var ecModel = dataZoomModel.ecModel;\n      var coordSysInfoWrap = {\n        infoList: [],\n        infoMap: createHashMap()\n      };\n      dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n        var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n\n        if (!axisModel) {\n          return;\n        }\n\n        var coordSysModel = axisModel.getCoordSysModel();\n\n        if (!coordSysModel) {\n          return;\n        }\n\n        var coordSysUid = coordSysModel.uid;\n        var coordSysInfo = coordSysInfoWrap.infoMap.get(coordSysUid);\n\n        if (!coordSysInfo) {\n          coordSysInfo = {\n            model: coordSysModel,\n            axisModels: []\n          };\n          coordSysInfoWrap.infoList.push(coordSysInfo);\n          coordSysInfoWrap.infoMap.set(coordSysUid, coordSysInfo);\n        }\n\n        coordSysInfo.axisModels.push(axisModel);\n      });\n      return coordSysInfoWrap;\n    }\n\n    var DataZoomAxisInfo =\n    /** @class */\n    function () {\n      function DataZoomAxisInfo() {\n        this.indexList = [];\n        this.indexMap = [];\n      }\n\n      DataZoomAxisInfo.prototype.add = function (axisCmptIdx) {\n        // Remove duplication.\n        if (!this.indexMap[axisCmptIdx]) {\n          this.indexList.push(axisCmptIdx);\n          this.indexMap[axisCmptIdx] = true;\n        }\n      };\n\n      return DataZoomAxisInfo;\n    }();\n\n    var DataZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(DataZoomModel, _super);\n\n      function DataZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = DataZoomModel.type;\n        _this._autoThrottle = true;\n        _this._noTarget = true;\n        /**\n         * It is `[rangeModeForMin, rangeModeForMax]`.\n         * The optional values for `rangeMode`:\n         * + `'value'` mode: the axis extent will always be determined by\n         *     `dataZoom.startValue` and `dataZoom.endValue`, despite\n         *     how data like and how `axis.min` and `axis.max` are.\n         * + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`,\n         *     where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`,\n         *     and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`.\n         *     Axis extent will be determined by the result of the percent of `[dMin, dMax]`.\n         *\n         * For example, when users are using dynamic data (update data periodically via `setOption`),\n         * if in `'value`' mode, the window will be kept in a fixed value range despite how\n         * data are appended, while if in `'percent'` mode, whe window range will be changed alone with\n         * the appended data (suppose `axis.min` and `axis.max` are not specified).\n         */\n\n        _this._rangePropMode = ['percent', 'percent'];\n        return _this;\n      }\n\n      DataZoomModel.prototype.init = function (option, parentModel, ecModel) {\n        var inputRawOption = retrieveRawOption(option);\n        /**\n         * Suppose a \"main process\" start at the point that model prepared (that is,\n         * model initialized or merged or method called in `action`).\n         * We should keep the `main process` idempotent, that is, given a set of values\n         * on `option`, we get the same result.\n         *\n         * But sometimes, values on `option` will be updated for providing users\n         * a \"final calculated value\" (`dataZoomProcessor` will do that). Those value\n         * should not be the base/input of the `main process`.\n         *\n         * So in that case we should save and keep the input of the `main process`\n         * separately, called `settledOption`.\n         *\n         * For example, consider the case:\n         * (Step_1) brush zoom the grid by `toolbox.dataZoom`,\n         *     where the original input `option.startValue`, `option.endValue` are earsed by\n         *     calculated value.\n         * (Step)2) click the legend to hide and show a series,\n         *     where the new range is calculated by the earsed `startValue` and `endValue`,\n         *     which brings incorrect result.\n         */\n\n        this.settledOption = inputRawOption;\n        this.mergeDefaultAndTheme(option, ecModel);\n\n        this._doInit(inputRawOption);\n      };\n\n      DataZoomModel.prototype.mergeOption = function (newOption) {\n        var inputRawOption = retrieveRawOption(newOption); // FIX #2591\n\n        merge(this.option, newOption, true);\n        merge(this.settledOption, inputRawOption, true);\n\n        this._doInit(inputRawOption);\n      };\n\n      DataZoomModel.prototype._doInit = function (inputRawOption) {\n        var thisOption = this.option;\n\n        this._setDefaultThrottle(inputRawOption);\n\n        this._updateRangeUse(inputRawOption);\n\n        var settledOption = this.settledOption;\n        each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {\n          // start/end has higher priority over startValue/endValue if they\n          // both set, but we should make chart.setOption({endValue: 1000})\n          // effective, rather than chart.setOption({endValue: 1000, end: null}).\n          if (this._rangePropMode[index] === 'value') {\n            thisOption[names[0]] = settledOption[names[0]] = null;\n          } // Otherwise do nothing and use the merge result.\n\n        }, this);\n\n        this._resetTarget();\n      };\n\n      DataZoomModel.prototype._resetTarget = function () {\n        var optionOrient = this.get('orient', true);\n        var targetAxisIndexMap = this._targetAxisInfoMap = createHashMap();\n\n        var hasAxisSpecified = this._fillSpecifiedTargetAxis(targetAxisIndexMap);\n\n        if (hasAxisSpecified) {\n          this._orient = optionOrient || this._makeAutoOrientByTargetAxis();\n        } else {\n          this._orient = optionOrient || 'horizontal';\n\n          this._fillAutoTargetAxisByOrient(targetAxisIndexMap, this._orient);\n        }\n\n        this._noTarget = true;\n        targetAxisIndexMap.each(function (axisInfo) {\n          if (axisInfo.indexList.length) {\n            this._noTarget = false;\n          }\n        }, this);\n      };\n\n      DataZoomModel.prototype._fillSpecifiedTargetAxis = function (targetAxisIndexMap) {\n        var hasAxisSpecified = false;\n        each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) {\n          var refering = this.getReferringComponents(getAxisMainType(axisDim), MULTIPLE_REFERRING); // When user set axisIndex as a empty array, we think that user specify axisIndex\n          // but do not want use auto mode. Because empty array may be encountered when\n          // some error occurred.\n\n          if (!refering.specified) {\n            return;\n          }\n\n          hasAxisSpecified = true;\n          var axisInfo = new DataZoomAxisInfo();\n          each(refering.models, function (axisModel) {\n            axisInfo.add(axisModel.componentIndex);\n          });\n          targetAxisIndexMap.set(axisDim, axisInfo);\n        }, this);\n        return hasAxisSpecified;\n      };\n\n      DataZoomModel.prototype._fillAutoTargetAxisByOrient = function (targetAxisIndexMap, orient) {\n        var ecModel = this.ecModel;\n        var needAuto = true; // Find axis that parallel to dataZoom as default.\n\n        if (needAuto) {\n          var axisDim = orient === 'vertical' ? 'y' : 'x';\n          var axisModels = ecModel.findComponents({\n            mainType: axisDim + 'Axis'\n          });\n          setParallelAxis(axisModels, axisDim);\n        } // Find axis that parallel to dataZoom as default.\n\n\n        if (needAuto) {\n          var axisModels = ecModel.findComponents({\n            mainType: 'singleAxis',\n            filter: function (axisModel) {\n              return axisModel.get('orient', true) === orient;\n            }\n          });\n          setParallelAxis(axisModels, 'single');\n        }\n\n        function setParallelAxis(axisModels, axisDim) {\n          // At least use the first parallel axis as the target axis.\n          var axisModel = axisModels[0];\n\n          if (!axisModel) {\n            return;\n          }\n\n          var axisInfo = new DataZoomAxisInfo();\n          axisInfo.add(axisModel.componentIndex);\n          targetAxisIndexMap.set(axisDim, axisInfo);\n          needAuto = false; // Find parallel axes in the same grid.\n\n          if (axisDim === 'x' || axisDim === 'y') {\n            var gridModel_1 = axisModel.getReferringComponents('grid', SINGLE_REFERRING).models[0];\n            gridModel_1 && each(axisModels, function (axModel) {\n              if (axisModel.componentIndex !== axModel.componentIndex && gridModel_1 === axModel.getReferringComponents('grid', SINGLE_REFERRING).models[0]) {\n                axisInfo.add(axModel.componentIndex);\n              }\n            });\n          }\n        }\n\n        if (needAuto) {\n          // If no parallel axis, find the first category axis as default. (Also consider polar).\n          each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) {\n            if (!needAuto) {\n              return;\n            }\n\n            var axisModels = ecModel.findComponents({\n              mainType: getAxisMainType(axisDim),\n              filter: function (axisModel) {\n                return axisModel.get('type', true) === 'category';\n              }\n            });\n\n            if (axisModels[0]) {\n              var axisInfo = new DataZoomAxisInfo();\n              axisInfo.add(axisModels[0].componentIndex);\n              targetAxisIndexMap.set(axisDim, axisInfo);\n              needAuto = false;\n            }\n          }, this);\n        }\n      };\n\n      DataZoomModel.prototype._makeAutoOrientByTargetAxis = function () {\n        var dim; // Find the first axis\n\n        this.eachTargetAxis(function (axisDim) {\n          !dim && (dim = axisDim);\n        }, this);\n        return dim === 'y' ? 'vertical' : 'horizontal';\n      };\n\n      DataZoomModel.prototype._setDefaultThrottle = function (inputRawOption) {\n        // When first time user set throttle, auto throttle ends.\n        if (inputRawOption.hasOwnProperty('throttle')) {\n          this._autoThrottle = false;\n        }\n\n        if (this._autoThrottle) {\n          var globalOption = this.ecModel.option;\n          this.option.throttle = globalOption.animation && globalOption.animationDurationUpdate > 0 ? 100 : 20;\n        }\n      };\n\n      DataZoomModel.prototype._updateRangeUse = function (inputRawOption) {\n        var rangePropMode = this._rangePropMode;\n        var rangeModeInOption = this.get('rangeMode');\n        each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {\n          var percentSpecified = inputRawOption[names[0]] != null;\n          var valueSpecified = inputRawOption[names[1]] != null;\n\n          if (percentSpecified && !valueSpecified) {\n            rangePropMode[index] = 'percent';\n          } else if (!percentSpecified && valueSpecified) {\n            rangePropMode[index] = 'value';\n          } else if (rangeModeInOption) {\n            rangePropMode[index] = rangeModeInOption[index];\n          } else if (percentSpecified) {\n            // percentSpecified && valueSpecified\n            rangePropMode[index] = 'percent';\n          } // else remain its original setting.\n\n        });\n      };\n\n      DataZoomModel.prototype.noTarget = function () {\n        return this._noTarget;\n      };\n\n      DataZoomModel.prototype.getFirstTargetAxisModel = function () {\n        var firstAxisModel;\n        this.eachTargetAxis(function (axisDim, axisIndex) {\n          if (firstAxisModel == null) {\n            firstAxisModel = this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n          }\n        }, this);\n        return firstAxisModel;\n      };\n      /**\n       * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel\n       */\n\n\n      DataZoomModel.prototype.eachTargetAxis = function (callback, context) {\n        this._targetAxisInfoMap.each(function (axisInfo, axisDim) {\n          each(axisInfo.indexList, function (axisIndex) {\n            callback.call(context, axisDim, axisIndex);\n          });\n        });\n      };\n      /**\n       * @return If not found, return null/undefined.\n       */\n\n\n      DataZoomModel.prototype.getAxisProxy = function (axisDim, axisIndex) {\n        var axisModel = this.getAxisModel(axisDim, axisIndex);\n\n        if (axisModel) {\n          return axisModel.__dzAxisProxy;\n        }\n      };\n      /**\n       * @return If not found, return null/undefined.\n       */\n\n\n      DataZoomModel.prototype.getAxisModel = function (axisDim, axisIndex) {\n        if (\"development\" !== 'production') {\n          assert(axisDim && axisIndex != null);\n        }\n\n        var axisInfo = this._targetAxisInfoMap.get(axisDim);\n\n        if (axisInfo && axisInfo.indexMap[axisIndex]) {\n          return this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n        }\n      };\n      /**\n       * If not specified, set to undefined.\n       */\n\n\n      DataZoomModel.prototype.setRawRange = function (opt) {\n        var thisOption = this.option;\n        var settledOption = this.settledOption;\n        each([['start', 'startValue'], ['end', 'endValue']], function (names) {\n          // Consider the pair <start, startValue>:\n          // If one has value and the other one is `null/undefined`, we both set them\n          // to `settledOption`. This strategy enables the feature to clear the original\n          // value in `settledOption` to `null/undefined`.\n          // But if both of them are `null/undefined`, we do not set them to `settledOption`\n          // and keep `settledOption` with the original value. This strategy enables users to\n          // only set <end or endValue> but not set <start or startValue> when calling\n          // `dispatchAction`.\n          // The pair <end, endValue> is treated in the same way.\n          if (opt[names[0]] != null || opt[names[1]] != null) {\n            thisOption[names[0]] = settledOption[names[0]] = opt[names[0]];\n            thisOption[names[1]] = settledOption[names[1]] = opt[names[1]];\n          }\n        }, this);\n\n        this._updateRangeUse(opt);\n      };\n\n      DataZoomModel.prototype.setCalculatedRange = function (opt) {\n        var option = this.option;\n        each(['start', 'startValue', 'end', 'endValue'], function (name) {\n          option[name] = opt[name];\n        });\n      };\n\n      DataZoomModel.prototype.getPercentRange = function () {\n        var axisProxy = this.findRepresentativeAxisProxy();\n\n        if (axisProxy) {\n          return axisProxy.getDataPercentWindow();\n        }\n      };\n      /**\n       * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);\n       *\n       * @return [startValue, endValue] value can only be '-' or finite number.\n       */\n\n\n      DataZoomModel.prototype.getValueRange = function (axisDim, axisIndex) {\n        if (axisDim == null && axisIndex == null) {\n          var axisProxy = this.findRepresentativeAxisProxy();\n\n          if (axisProxy) {\n            return axisProxy.getDataValueWindow();\n          }\n        } else {\n          return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow();\n        }\n      };\n      /**\n       * @param axisModel If axisModel given, find axisProxy\n       *      corresponding to the axisModel\n       */\n\n\n      DataZoomModel.prototype.findRepresentativeAxisProxy = function (axisModel) {\n        if (axisModel) {\n          return axisModel.__dzAxisProxy;\n        } // Find the first hosted axisProxy\n\n\n        var firstProxy;\n\n        var axisDimList = this._targetAxisInfoMap.keys();\n\n        for (var i = 0; i < axisDimList.length; i++) {\n          var axisDim = axisDimList[i];\n\n          var axisInfo = this._targetAxisInfoMap.get(axisDim);\n\n          for (var j = 0; j < axisInfo.indexList.length; j++) {\n            var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]);\n\n            if (proxy.hostedBy(this)) {\n              return proxy;\n            }\n\n            if (!firstProxy) {\n              firstProxy = proxy;\n            }\n          }\n        } // If no hosted proxy found, still need to return a proxy.\n        // This case always happens in toolbox dataZoom, where axes are all hosted by\n        // other dataZooms.\n\n\n        return firstProxy;\n      };\n\n      DataZoomModel.prototype.getRangePropMode = function () {\n        return this._rangePropMode.slice();\n      };\n\n      DataZoomModel.prototype.getOrient = function () {\n        if (\"development\" !== 'production') {\n          // Should not be called before initialized.\n          assert(this._orient);\n        }\n\n        return this._orient;\n      };\n\n      DataZoomModel.type = 'dataZoom';\n      DataZoomModel.dependencies = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series', 'toolbox'];\n      DataZoomModel.defaultOption = {\n        // zlevel: 0,\n        z: 4,\n        filterMode: 'filter',\n        start: 0,\n        end: 100\n      };\n      return DataZoomModel;\n    }(ComponentModel);\n    /**\n     * Retrieve those raw params from option, which will be cached separately,\n     * because they will be overwritten by normalized/calculated values in the main\n     * process.\n     */\n\n\n    function retrieveRawOption(option) {\n      var ret = {};\n      each(['start', 'end', 'startValue', 'endValue', 'throttle'], function (name) {\n        option.hasOwnProperty(name) && (ret[name] = option[name]);\n      });\n      return ret;\n    }\n\n    var SelectDataZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(SelectDataZoomModel, _super);\n\n      function SelectDataZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SelectDataZoomModel.type;\n        return _this;\n      }\n\n      SelectDataZoomModel.type = 'dataZoom.select';\n      return SelectDataZoomModel;\n    }(DataZoomModel);\n\n    var DataZoomView =\n    /** @class */\n    function (_super) {\n      __extends(DataZoomView, _super);\n\n      function DataZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = DataZoomView.type;\n        return _this;\n      }\n\n      DataZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) {\n        this.dataZoomModel = dataZoomModel;\n        this.ecModel = ecModel;\n        this.api = api;\n      };\n\n      DataZoomView.type = 'dataZoom';\n      return DataZoomView;\n    }(ComponentView);\n\n    var SelectDataZoomView =\n    /** @class */\n    function (_super) {\n      __extends(SelectDataZoomView, _super);\n\n      function SelectDataZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SelectDataZoomView.type;\n        return _this;\n      }\n\n      SelectDataZoomView.type = 'dataZoom.select';\n      return SelectDataZoomView;\n    }(DataZoomView);\n\n    var each$8 = each;\n    var asc$1 = asc;\n    /**\n     * Operate single axis.\n     * One axis can only operated by one axis operator.\n     * Different dataZoomModels may be defined to operate the same axis.\n     * (i.e. 'inside' data zoom and 'slider' data zoom components)\n     * So dataZoomModels share one axisProxy in that case.\n     */\n\n    var AxisProxy =\n    /** @class */\n    function () {\n      function AxisProxy(dimName, axisIndex, dataZoomModel, ecModel) {\n        this._dimName = dimName;\n        this._axisIndex = axisIndex;\n        this.ecModel = ecModel;\n        this._dataZoomModel = dataZoomModel; // /**\n        //  * @readOnly\n        //  * @private\n        //  */\n        // this.hasSeriesStacked;\n      }\n      /**\n       * Whether the axisProxy is hosted by dataZoomModel.\n       */\n\n\n      AxisProxy.prototype.hostedBy = function (dataZoomModel) {\n        return this._dataZoomModel === dataZoomModel;\n      };\n      /**\n       * @return Value can only be NaN or finite value.\n       */\n\n\n      AxisProxy.prototype.getDataValueWindow = function () {\n        return this._valueWindow.slice();\n      };\n      /**\n       * @return {Array.<number>}\n       */\n\n\n      AxisProxy.prototype.getDataPercentWindow = function () {\n        return this._percentWindow.slice();\n      };\n\n      AxisProxy.prototype.getTargetSeriesModels = function () {\n        var seriesModels = [];\n        this.ecModel.eachSeries(function (seriesModel) {\n          if (isCoordSupported(seriesModel)) {\n            var axisMainType = getAxisMainType(this._dimName);\n            var axisModel = seriesModel.getReferringComponents(axisMainType, SINGLE_REFERRING).models[0];\n\n            if (axisModel && this._axisIndex === axisModel.componentIndex) {\n              seriesModels.push(seriesModel);\n            }\n          }\n        }, this);\n        return seriesModels;\n      };\n\n      AxisProxy.prototype.getAxisModel = function () {\n        return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex);\n      };\n\n      AxisProxy.prototype.getMinMaxSpan = function () {\n        return clone(this._minMaxSpan);\n      };\n      /**\n       * Only calculate by given range and this._dataExtent, do not change anything.\n       */\n\n\n      AxisProxy.prototype.calculateDataWindow = function (opt) {\n        var dataExtent = this._dataExtent;\n        var axisModel = this.getAxisModel();\n        var scale = axisModel.axis.scale;\n\n        var rangePropMode = this._dataZoomModel.getRangePropMode();\n\n        var percentExtent = [0, 100];\n        var percentWindow = [];\n        var valueWindow = [];\n        var hasPropModeValue;\n        each$8(['start', 'end'], function (prop, idx) {\n          var boundPercent = opt[prop];\n          var boundValue = opt[prop + 'Value']; // Notice: dataZoom is based either on `percentProp` ('start', 'end') or\n          // on `valueProp` ('startValue', 'endValue'). (They are based on the data extent\n          // but not min/max of axis, which will be calculated by data window then).\n          // The former one is suitable for cases that a dataZoom component controls multiple\n          // axes with different unit or extent, and the latter one is suitable for accurate\n          // zoom by pixel (e.g., in dataZoomSelect).\n          // we use `getRangePropMode()` to mark which prop is used. `rangePropMode` is updated\n          // only when setOption or dispatchAction, otherwise it remains its original value.\n          // (Why not only record `percentProp` and always map to `valueProp`? Because\n          // the map `valueProp` -> `percentProp` -> `valueProp` probably not the original\n          // `valueProp`. consider two axes constrolled by one dataZoom. They have different\n          // data extent. All of values that are overflow the `dataExtent` will be calculated\n          // to percent '100%').\n\n          if (rangePropMode[idx] === 'percent') {\n            boundPercent == null && (boundPercent = percentExtent[idx]); // Use scale.parse to math round for category or time axis.\n\n            boundValue = scale.parse(linearMap(boundPercent, percentExtent, dataExtent));\n          } else {\n            hasPropModeValue = true;\n            boundValue = boundValue == null ? dataExtent[idx] : scale.parse(boundValue); // Calculating `percent` from `value` may be not accurate, because\n            // This calculation can not be inversed, because all of values that\n            // are overflow the `dataExtent` will be calculated to percent '100%'\n\n            boundPercent = linearMap(boundValue, dataExtent, percentExtent);\n          } // valueWindow[idx] = round(boundValue);\n          // percentWindow[idx] = round(boundPercent);\n          // fallback to extent start/end when parsed value or percent is invalid\n\n\n          valueWindow[idx] = boundValue == null || isNaN(boundValue) ? dataExtent[idx] : boundValue;\n          percentWindow[idx] = boundPercent == null || isNaN(boundPercent) ? percentExtent[idx] : boundPercent;\n        });\n        asc$1(valueWindow);\n        asc$1(percentWindow); // The windows from user calling of `dispatchAction` might be out of the extent,\n        // or do not obey the `min/maxSpan`, `min/maxValueSpan`. But we don't restrict window\n        // by `zoomLock` here, because we see `zoomLock` just as a interaction constraint,\n        // where API is able to initialize/modify the window size even though `zoomLock`\n        // specified.\n\n        var spans = this._minMaxSpan;\n        hasPropModeValue ? restrictSet(valueWindow, percentWindow, dataExtent, percentExtent, false) : restrictSet(percentWindow, valueWindow, percentExtent, dataExtent, true);\n\n        function restrictSet(fromWindow, toWindow, fromExtent, toExtent, toValue) {\n          var suffix = toValue ? 'Span' : 'ValueSpan';\n          sliderMove(0, fromWindow, fromExtent, 'all', spans['min' + suffix], spans['max' + suffix]);\n\n          for (var i = 0; i < 2; i++) {\n            toWindow[i] = linearMap(fromWindow[i], fromExtent, toExtent, true);\n            toValue && (toWindow[i] = scale.parse(toWindow[i]));\n          }\n        }\n\n        return {\n          valueWindow: valueWindow,\n          percentWindow: percentWindow\n        };\n      };\n      /**\n       * Notice: reset should not be called before series.restoreData() is called,\n       * so it is recommended to be called in \"process stage\" but not \"model init\n       * stage\".\n       */\n\n\n      AxisProxy.prototype.reset = function (dataZoomModel) {\n        if (dataZoomModel !== this._dataZoomModel) {\n          return;\n        }\n\n        var targetSeries = this.getTargetSeriesModels(); // Culculate data window and data extent, and record them.\n\n        this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries); // `calculateDataWindow` uses min/maxSpan.\n\n        this._updateMinMaxSpan();\n\n        var dataWindow = this.calculateDataWindow(dataZoomModel.settledOption);\n        this._valueWindow = dataWindow.valueWindow;\n        this._percentWindow = dataWindow.percentWindow; // Update axis setting then.\n\n        this._setAxisModel();\n      };\n\n      AxisProxy.prototype.filterData = function (dataZoomModel, api) {\n        if (dataZoomModel !== this._dataZoomModel) {\n          return;\n        }\n\n        var axisDim = this._dimName;\n        var seriesModels = this.getTargetSeriesModels();\n        var filterMode = dataZoomModel.get('filterMode');\n        var valueWindow = this._valueWindow;\n\n        if (filterMode === 'none') {\n          return;\n        } // FIXME\n        // Toolbox may has dataZoom injected. And if there are stacked bar chart\n        // with NaN data, NaN will be filtered and stack will be wrong.\n        // So we need to force the mode to be set empty.\n        // In fect, it is not a big deal that do not support filterMode-'filter'\n        // when using toolbox#dataZoom, utill tooltip#dataZoom support \"single axis\n        // selection\" some day, which might need \"adapt to data extent on the\n        // otherAxis\", which is disabled by filterMode-'empty'.\n        // But currently, stack has been fixed to based on value but not index,\n        // so this is not an issue any more.\n        // let otherAxisModel = this.getOtherAxisModel();\n        // if (dataZoomModel.get('$fromToolbox')\n        //     && otherAxisModel\n        //     && otherAxisModel.hasSeriesStacked\n        // ) {\n        //     filterMode = 'empty';\n        // }\n        // TODO\n        // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet.\n\n\n        each$8(seriesModels, function (seriesModel) {\n          var seriesData = seriesModel.getData();\n          var dataDims = seriesData.mapDimensionsAll(axisDim);\n\n          if (!dataDims.length) {\n            return;\n          }\n\n          if (filterMode === 'weakFilter') {\n            var store_1 = seriesData.getStore();\n            var dataDimIndices_1 = map(dataDims, function (dim) {\n              return seriesData.getDimensionIndex(dim);\n            }, seriesData);\n            seriesData.filterSelf(function (dataIndex) {\n              var leftOut;\n              var rightOut;\n              var hasValue;\n\n              for (var i = 0; i < dataDims.length; i++) {\n                var value = store_1.get(dataDimIndices_1[i], dataIndex);\n                var thisHasValue = !isNaN(value);\n                var thisLeftOut = value < valueWindow[0];\n                var thisRightOut = value > valueWindow[1];\n\n                if (thisHasValue && !thisLeftOut && !thisRightOut) {\n                  return true;\n                }\n\n                thisHasValue && (hasValue = true);\n                thisLeftOut && (leftOut = true);\n                thisRightOut && (rightOut = true);\n              } // If both left out and right out, do not filter.\n\n\n              return hasValue && leftOut && rightOut;\n            });\n          } else {\n            each$8(dataDims, function (dim) {\n              if (filterMode === 'empty') {\n                seriesModel.setData(seriesData = seriesData.map(dim, function (value) {\n                  return !isInWindow(value) ? NaN : value;\n                }));\n              } else {\n                var range = {};\n                range[dim] = valueWindow; // console.time('select');\n\n                seriesData.selectRange(range); // console.timeEnd('select');\n              }\n            });\n          }\n\n          each$8(dataDims, function (dim) {\n            seriesData.setApproximateExtent(valueWindow, dim);\n          });\n        });\n\n        function isInWindow(value) {\n          return value >= valueWindow[0] && value <= valueWindow[1];\n        }\n      };\n\n      AxisProxy.prototype._updateMinMaxSpan = function () {\n        var minMaxSpan = this._minMaxSpan = {};\n        var dataZoomModel = this._dataZoomModel;\n        var dataExtent = this._dataExtent;\n        each$8(['min', 'max'], function (minMax) {\n          var percentSpan = dataZoomModel.get(minMax + 'Span');\n          var valueSpan = dataZoomModel.get(minMax + 'ValueSpan');\n          valueSpan != null && (valueSpan = this.getAxisModel().axis.scale.parse(valueSpan)); // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan\n\n          if (valueSpan != null) {\n            percentSpan = linearMap(dataExtent[0] + valueSpan, dataExtent, [0, 100], true);\n          } else if (percentSpan != null) {\n            valueSpan = linearMap(percentSpan, [0, 100], dataExtent, true) - dataExtent[0];\n          }\n\n          minMaxSpan[minMax + 'Span'] = percentSpan;\n          minMaxSpan[minMax + 'ValueSpan'] = valueSpan;\n        }, this);\n      };\n\n      AxisProxy.prototype._setAxisModel = function () {\n        var axisModel = this.getAxisModel();\n        var percentWindow = this._percentWindow;\n        var valueWindow = this._valueWindow;\n\n        if (!percentWindow) {\n          return;\n        } // [0, 500]: arbitrary value, guess axis extent.\n\n\n        var precision = getPixelPrecision(valueWindow, [0, 500]);\n        precision = Math.min(precision, 20); // For value axis, if min/max/scale are not set, we just use the extent obtained\n        // by series data, which may be a little different from the extent calculated by\n        // `axisHelper.getScaleExtent`. But the different just affects the experience a\n        // little when zooming. So it will not be fixed until some users require it strongly.\n\n        var rawExtentInfo = axisModel.axis.scale.rawExtentInfo;\n\n        if (percentWindow[0] !== 0) {\n          rawExtentInfo.setDeterminedMinMax('min', +valueWindow[0].toFixed(precision));\n        }\n\n        if (percentWindow[1] !== 100) {\n          rawExtentInfo.setDeterminedMinMax('max', +valueWindow[1].toFixed(precision));\n        }\n\n        rawExtentInfo.freeze();\n      };\n\n      return AxisProxy;\n    }();\n\n    function calculateDataExtent(axisProxy, axisDim, seriesModels) {\n      var dataExtent = [Infinity, -Infinity];\n      each$8(seriesModels, function (seriesModel) {\n        unionAxisExtentFromData(dataExtent, seriesModel.getData(), axisDim);\n      }); // It is important to get \"consistent\" extent when more then one axes is\n      // controlled by a `dataZoom`, otherwise those axes will not be synchronized\n      // when zooming. But it is difficult to know what is \"consistent\", considering\n      // axes have different type or even different meanings (For example, two\n      // time axes are used to compare data of the same date in different years).\n      // So basically dataZoom just obtains extent by series.data (in category axis\n      // extent can be obtained from axis.data).\n      // Nevertheless, user can set min/max/scale on axes to make extent of axes\n      // consistent.\n\n      var axisModel = axisProxy.getAxisModel();\n      var rawExtentResult = ensureScaleRawExtentInfo(axisModel.axis.scale, axisModel, dataExtent).calculate();\n      return [rawExtentResult.min, rawExtentResult.max];\n    }\n\n    var dataZoomProcessor = {\n      // `dataZoomProcessor` will only be performed in needed series. Consider if\n      // there is a line series and a pie series, it is better not to update the\n      // line series if only pie series is needed to be updated.\n      getTargetSeries: function (ecModel) {\n        function eachAxisModel(cb) {\n          ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n            dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n              var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex);\n              cb(axisDim, axisIndex, axisModel, dataZoomModel);\n            });\n          });\n        } // FIXME: it brings side-effect to `getTargetSeries`.\n        // Prepare axis proxies.\n\n\n        eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {\n          // dispose all last axis proxy, in case that some axis are deleted.\n          axisModel.__dzAxisProxy = null;\n        });\n        var proxyList = [];\n        eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {\n          // Different dataZooms may constrol the same axis. In that case,\n          // an axisProxy serves both of them.\n          if (!axisModel.__dzAxisProxy) {\n            // Use the first dataZoomModel as the main model of axisProxy.\n            axisModel.__dzAxisProxy = new AxisProxy(axisDim, axisIndex, dataZoomModel, ecModel);\n            proxyList.push(axisModel.__dzAxisProxy);\n          }\n        });\n        var seriesModelMap = createHashMap();\n        each(proxyList, function (axisProxy) {\n          each(axisProxy.getTargetSeriesModels(), function (seriesModel) {\n            seriesModelMap.set(seriesModel.uid, seriesModel);\n          });\n        });\n        return seriesModelMap;\n      },\n      // Consider appendData, where filter should be performed. Because data process is\n      // in block mode currently, it is not need to worry about that the overallProgress\n      // execute every frame.\n      overallReset: function (ecModel, api) {\n        ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n          // We calculate window and reset axis here but not in model\n          // init stage and not after action dispatch handler, because\n          // reset should be called after seriesData.restoreData.\n          dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n            dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel);\n          }); // Caution: data zoom filtering is order sensitive when using\n          // percent range and no min/max/scale set on axis.\n          // For example, we have dataZoom definition:\n          // [\n          //      {xAxisIndex: 0, start: 30, end: 70},\n          //      {yAxisIndex: 0, start: 20, end: 80}\n          // ]\n          // In this case, [20, 80] of y-dataZoom should be based on data\n          // that have filtered by x-dataZoom using range of [30, 70],\n          // but should not be based on full raw data. Thus sliding\n          // x-dataZoom will change both ranges of xAxis and yAxis,\n          // while sliding y-dataZoom will only change the range of yAxis.\n          // So we should filter x-axis after reset x-axis immediately,\n          // and then reset y-axis and filter y-axis.\n\n          dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n            dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api);\n          });\n        });\n        ecModel.eachComponent('dataZoom', function (dataZoomModel) {\n          // Fullfill all of the range props so that user\n          // is able to get them from chart.getOption().\n          var axisProxy = dataZoomModel.findRepresentativeAxisProxy();\n\n          if (axisProxy) {\n            var percentRange = axisProxy.getDataPercentWindow();\n            var valueRange = axisProxy.getDataValueWindow();\n            dataZoomModel.setCalculatedRange({\n              start: percentRange[0],\n              end: percentRange[1],\n              startValue: valueRange[0],\n              endValue: valueRange[1]\n            });\n          }\n        });\n      }\n    };\n\n    function installDataZoomAction(registers) {\n      registers.registerAction('dataZoom', function (payload, ecModel) {\n        var effectedModels = findEffectedDataZooms(ecModel, payload);\n        each(effectedModels, function (dataZoomModel) {\n          dataZoomModel.setRawRange({\n            start: payload.start,\n            end: payload.end,\n            startValue: payload.startValue,\n            endValue: payload.endValue\n          });\n        });\n      });\n    }\n\n    var installed = false;\n    function installCommon(registers) {\n      if (installed) {\n        return;\n      }\n\n      installed = true;\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, dataZoomProcessor);\n      installDataZoomAction(registers);\n      registers.registerSubTypeDefaulter('dataZoom', function () {\n        // Default 'slider' when no type specified.\n        return 'slider';\n      });\n    }\n\n    function install$y(registers) {\n      registers.registerComponentModel(SelectDataZoomModel);\n      registers.registerComponentView(SelectDataZoomView);\n      installCommon(registers);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    var ToolboxFeature =\n    /** @class */\n    function () {\n      function ToolboxFeature() {}\n\n      return ToolboxFeature;\n    }();\n    var features = {};\n    function registerFeature(name, ctor) {\n      features[name] = ctor;\n    }\n    function getFeature(name) {\n      return features[name];\n    }\n\n    var ToolboxModel =\n    /** @class */\n    function (_super) {\n      __extends(ToolboxModel, _super);\n\n      function ToolboxModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ToolboxModel.type;\n        return _this;\n      }\n\n      ToolboxModel.prototype.optionUpdated = function () {\n        _super.prototype.optionUpdated.apply(this, arguments);\n\n        var ecModel = this.ecModel;\n        each(this.option.feature, function (featureOpt, featureName) {\n          var Feature = getFeature(featureName);\n\n          if (Feature) {\n            if (Feature.getDefaultOption) {\n              Feature.defaultOption = Feature.getDefaultOption(ecModel);\n            }\n\n            merge(featureOpt, Feature.defaultOption);\n          }\n        });\n      };\n\n      ToolboxModel.type = 'toolbox';\n      ToolboxModel.layoutMode = {\n        type: 'box',\n        ignoreSize: true\n      };\n      ToolboxModel.defaultOption = {\n        show: true,\n        z: 6,\n        // zlevel: 0,\n        orient: 'horizontal',\n        left: 'right',\n        top: 'top',\n        // right\n        // bottom\n        backgroundColor: 'transparent',\n        borderColor: '#ccc',\n        borderRadius: 0,\n        borderWidth: 0,\n        padding: 5,\n        itemSize: 15,\n        itemGap: 8,\n        showTitle: true,\n        iconStyle: {\n          borderColor: '#666',\n          color: 'none'\n        },\n        emphasis: {\n          iconStyle: {\n            borderColor: '#3E98C5'\n          }\n        },\n        // textStyle: {},\n        // feature\n        tooltip: {\n          show: false,\n          position: 'bottom'\n        }\n      };\n      return ToolboxModel;\n    }(ComponentModel);\n\n    /**\n     * Layout list like component.\n     * It will box layout each items in group of component and then position the whole group in the viewport\n     * @param {module:zrender/group/Group} group\n     * @param {module:echarts/model/Component} componentModel\n     * @param {module:echarts/ExtensionAPI}\n     */\n\n    function layout$3(group, componentModel, api) {\n      var boxLayoutParams = componentModel.getBoxLayoutParams();\n      var padding = componentModel.get('padding');\n      var viewportSize = {\n        width: api.getWidth(),\n        height: api.getHeight()\n      };\n      var rect = getLayoutRect(boxLayoutParams, viewportSize, padding);\n      box(componentModel.get('orient'), group, componentModel.get('itemGap'), rect.width, rect.height);\n      positionElement(group, boxLayoutParams, viewportSize, padding);\n    }\n    function makeBackground(rect, componentModel) {\n      var padding = normalizeCssArray$1(componentModel.get('padding'));\n      var style = componentModel.getItemStyle(['color', 'opacity']);\n      style.fill = componentModel.get('backgroundColor');\n      rect = new Rect({\n        shape: {\n          x: rect.x - padding[3],\n          y: rect.y - padding[0],\n          width: rect.width + padding[1] + padding[3],\n          height: rect.height + padding[0] + padding[2],\n          r: componentModel.get('borderRadius')\n        },\n        style: style,\n        silent: true,\n        z2: -1\n      }); // FIXME\n      // `subPixelOptimizeRect` may bring some gap between edge of viewpart\n      // and background rect when setting like `left: 0`, `top: 0`.\n      // graphic.subPixelOptimizeRect(rect);\n\n      return rect;\n    }\n\n    var ToolboxView =\n    /** @class */\n    function (_super) {\n      __extends(ToolboxView, _super);\n\n      function ToolboxView() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      ToolboxView.prototype.render = function (toolboxModel, ecModel, api, payload) {\n        var group = this.group;\n        group.removeAll();\n\n        if (!toolboxModel.get('show')) {\n          return;\n        }\n\n        var itemSize = +toolboxModel.get('itemSize');\n        var isVertical = toolboxModel.get('orient') === 'vertical';\n        var featureOpts = toolboxModel.get('feature') || {};\n        var features = this._features || (this._features = {});\n        var featureNames = [];\n        each(featureOpts, function (opt, name) {\n          featureNames.push(name);\n        });\n        new DataDiffer(this._featureNames || [], featureNames).add(processFeature).update(processFeature).remove(curry(processFeature, null)).execute(); // Keep for diff.\n\n        this._featureNames = featureNames;\n\n        function processFeature(newIndex, oldIndex) {\n          var featureName = featureNames[newIndex];\n          var oldName = featureNames[oldIndex];\n          var featureOpt = featureOpts[featureName];\n          var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel);\n          var feature; // FIX#11236, merge feature title from MagicType newOption. TODO: consider seriesIndex ?\n\n          if (payload && payload.newTitle != null && payload.featureName === featureName) {\n            featureOpt.title = payload.newTitle;\n          }\n\n          if (featureName && !oldName) {\n            // Create\n            if (isUserFeatureName(featureName)) {\n              feature = {\n                onclick: featureModel.option.onclick,\n                featureName: featureName\n              };\n            } else {\n              var Feature = getFeature(featureName);\n\n              if (!Feature) {\n                return;\n              }\n\n              feature = new Feature();\n            }\n\n            features[featureName] = feature;\n          } else {\n            feature = features[oldName]; // If feature does not exsit.\n\n            if (!feature) {\n              return;\n            }\n          }\n\n          feature.uid = getUID('toolbox-feature');\n          feature.model = featureModel;\n          feature.ecModel = ecModel;\n          feature.api = api;\n          var isToolboxFeature = feature instanceof ToolboxFeature;\n\n          if (!featureName && oldName) {\n            isToolboxFeature && feature.dispose && feature.dispose(ecModel, api);\n            return;\n          }\n\n          if (!featureModel.get('show') || isToolboxFeature && feature.unusable) {\n            isToolboxFeature && feature.remove && feature.remove(ecModel, api);\n            return;\n          }\n\n          createIconPaths(featureModel, feature, featureName);\n\n          featureModel.setIconStatus = function (iconName, status) {\n            var option = this.option;\n            var iconPaths = this.iconPaths;\n            option.iconStatus = option.iconStatus || {};\n            option.iconStatus[iconName] = status;\n\n            if (iconPaths[iconName]) {\n              (status === 'emphasis' ? enterEmphasis : leaveEmphasis)(iconPaths[iconName]);\n            }\n          };\n\n          if (feature instanceof ToolboxFeature) {\n            if (feature.render) {\n              feature.render(featureModel, ecModel, api, payload);\n            }\n          }\n        }\n\n        function createIconPaths(featureModel, feature, featureName) {\n          var iconStyleModel = featureModel.getModel('iconStyle');\n          var iconStyleEmphasisModel = featureModel.getModel(['emphasis', 'iconStyle']); // If one feature has mutiple icon. they are orginaized as\n          // {\n          //     icon: {\n          //         foo: '',\n          //         bar: ''\n          //     },\n          //     title: {\n          //         foo: '',\n          //         bar: ''\n          //     }\n          // }\n\n          var icons = feature instanceof ToolboxFeature && feature.getIcons ? feature.getIcons() : featureModel.get('icon');\n          var titles = featureModel.get('title') || {};\n          var iconsMap;\n          var titlesMap;\n\n          if (isString(icons)) {\n            iconsMap = {};\n            iconsMap[featureName] = icons;\n          } else {\n            iconsMap = icons;\n          }\n\n          if (isString(titles)) {\n            titlesMap = {};\n            titlesMap[featureName] = titles;\n          } else {\n            titlesMap = titles;\n          }\n\n          var iconPaths = featureModel.iconPaths = {};\n          each(iconsMap, function (iconStr, iconName) {\n            var path = createIcon(iconStr, {}, {\n              x: -itemSize / 2,\n              y: -itemSize / 2,\n              width: itemSize,\n              height: itemSize\n            }); // TODO handling image\n\n            path.setStyle(iconStyleModel.getItemStyle());\n            var pathEmphasisState = path.ensureState('emphasis');\n            pathEmphasisState.style = iconStyleEmphasisModel.getItemStyle(); // Text position calculation\n\n            var textContent = new ZRText({\n              style: {\n                text: titlesMap[iconName],\n                align: iconStyleEmphasisModel.get('textAlign'),\n                borderRadius: iconStyleEmphasisModel.get('textBorderRadius'),\n                padding: iconStyleEmphasisModel.get('textPadding'),\n                fill: null\n              },\n              ignore: true\n            });\n            path.setTextContent(textContent);\n            setTooltipConfig({\n              el: path,\n              componentModel: toolboxModel,\n              itemName: iconName,\n              formatterParamsExtra: {\n                title: titlesMap[iconName]\n              }\n            });\n            path.__title = titlesMap[iconName];\n            path.on('mouseover', function () {\n              // Should not reuse above hoverStyle, which might be modified.\n              var hoverStyle = iconStyleEmphasisModel.getItemStyle();\n              var defaultTextPosition = isVertical ? toolboxModel.get('right') == null && toolboxModel.get('left') !== 'right' ? 'right' : 'left' : toolboxModel.get('bottom') == null && toolboxModel.get('top') !== 'bottom' ? 'bottom' : 'top';\n              textContent.setStyle({\n                fill: iconStyleEmphasisModel.get('textFill') || hoverStyle.fill || hoverStyle.stroke || '#000',\n                backgroundColor: iconStyleEmphasisModel.get('textBackgroundColor')\n              });\n              path.setTextConfig({\n                position: iconStyleEmphasisModel.get('textPosition') || defaultTextPosition\n              });\n              textContent.ignore = !toolboxModel.get('showTitle'); // Use enterEmphasis and leaveEmphasis provide by ec.\n              // There are flags managed by the echarts.\n\n              api.enterEmphasis(this);\n            }).on('mouseout', function () {\n              if (featureModel.get(['iconStatus', iconName]) !== 'emphasis') {\n                api.leaveEmphasis(this);\n              }\n\n              textContent.hide();\n            });\n            (featureModel.get(['iconStatus', iconName]) === 'emphasis' ? enterEmphasis : leaveEmphasis)(path);\n            group.add(path);\n            path.on('click', bind(feature.onclick, feature, ecModel, api, iconName));\n            iconPaths[iconName] = path;\n          });\n        }\n\n        layout$3(group, toolboxModel, api); // Render background after group is layout\n        // FIXME\n\n        group.add(makeBackground(group.getBoundingRect(), toolboxModel)); // Adjust icon title positions to avoid them out of screen\n\n        isVertical || group.eachChild(function (icon) {\n          var titleText = icon.__title; // const hoverStyle = icon.hoverStyle;\n          // TODO simplify code?\n\n          var emphasisState = icon.ensureState('emphasis');\n          var emphasisTextConfig = emphasisState.textConfig || (emphasisState.textConfig = {});\n          var textContent = icon.getTextContent();\n          var emphasisTextState = textContent && textContent.ensureState('emphasis'); // May be background element\n\n          if (emphasisTextState && !isFunction(emphasisTextState) && titleText) {\n            var emphasisTextStyle = emphasisTextState.style || (emphasisTextState.style = {});\n            var rect = getBoundingRect(titleText, ZRText.makeFont(emphasisTextStyle));\n            var offsetX = icon.x + group.x;\n            var offsetY = icon.y + group.y + itemSize;\n            var needPutOnTop = false;\n\n            if (offsetY + rect.height > api.getHeight()) {\n              emphasisTextConfig.position = 'top';\n              needPutOnTop = true;\n            }\n\n            var topOffset = needPutOnTop ? -5 - rect.height : itemSize + 10;\n\n            if (offsetX + rect.width / 2 > api.getWidth()) {\n              emphasisTextConfig.position = ['100%', topOffset];\n              emphasisTextStyle.align = 'right';\n            } else if (offsetX - rect.width / 2 < 0) {\n              emphasisTextConfig.position = [0, topOffset];\n              emphasisTextStyle.align = 'left';\n            }\n          }\n        });\n      };\n\n      ToolboxView.prototype.updateView = function (toolboxModel, ecModel, api, payload) {\n        each(this._features, function (feature) {\n          feature instanceof ToolboxFeature && feature.updateView && feature.updateView(feature.model, ecModel, api, payload);\n        });\n      }; // updateLayout(toolboxModel, ecModel, api, payload) {\n      //     zrUtil.each(this._features, function (feature) {\n      //         feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload);\n      //     });\n      // },\n\n\n      ToolboxView.prototype.remove = function (ecModel, api) {\n        each(this._features, function (feature) {\n          feature instanceof ToolboxFeature && feature.remove && feature.remove(ecModel, api);\n        });\n        this.group.removeAll();\n      };\n\n      ToolboxView.prototype.dispose = function (ecModel, api) {\n        each(this._features, function (feature) {\n          feature instanceof ToolboxFeature && feature.dispose && feature.dispose(ecModel, api);\n        });\n      };\n\n      ToolboxView.type = 'toolbox';\n      return ToolboxView;\n    }(ComponentView);\n\n    function isUserFeatureName(featureName) {\n      return featureName.indexOf('my') === 0;\n    }\n\n    /* global window, document */\n\n    var SaveAsImage =\n    /** @class */\n    function (_super) {\n      __extends(SaveAsImage, _super);\n\n      function SaveAsImage() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      SaveAsImage.prototype.onclick = function (ecModel, api) {\n        var model = this.model;\n        var title = model.get('name') || ecModel.get('title.0.text') || 'echarts';\n        var isSvg = api.getZr().painter.getType() === 'svg';\n        var type = isSvg ? 'svg' : model.get('type', true) || 'png';\n        var url = api.getConnectedDataURL({\n          type: type,\n          backgroundColor: model.get('backgroundColor', true) || ecModel.get('backgroundColor') || '#fff',\n          connectedBackgroundColor: model.get('connectedBackgroundColor'),\n          excludeComponents: model.get('excludeComponents'),\n          pixelRatio: model.get('pixelRatio')\n        });\n        var browser = env.browser; // Chrome, Firefox, New Edge\n\n        if (isFunction(MouseEvent) && (browser.newEdge || !browser.ie && !browser.edge)) {\n          var $a = document.createElement('a');\n          $a.download = title + '.' + type;\n          $a.target = '_blank';\n          $a.href = url;\n          var evt = new MouseEvent('click', {\n            // some micro front-end framework， window maybe is a Proxy\n            view: document.defaultView,\n            bubbles: true,\n            cancelable: false\n          });\n          $a.dispatchEvent(evt);\n        } // IE or old Edge\n        else {\n            // @ts-ignore\n            if (window.navigator.msSaveOrOpenBlob || isSvg) {\n              var parts = url.split(','); // data:[<mime type>][;charset=<charset>][;base64],<encoded data>\n\n              var base64Encoded = parts[0].indexOf('base64') > -1;\n              var bstr = isSvg // should decode the svg data uri first\n              ? decodeURIComponent(parts[1]) : parts[1]; // only `atob` when the data uri is encoded with base64\n              // otherwise, like `svg` data uri exported by zrender,\n              // there will be an error, for it's not encoded with base64.\n              // (just a url-encoded string through `encodeURIComponent`)\n\n              base64Encoded && (bstr = window.atob(bstr));\n              var filename = title + '.' + type; // @ts-ignore\n\n              if (window.navigator.msSaveOrOpenBlob) {\n                var n = bstr.length;\n                var u8arr = new Uint8Array(n);\n\n                while (n--) {\n                  u8arr[n] = bstr.charCodeAt(n);\n                }\n\n                var blob = new Blob([u8arr]); // @ts-ignore\n\n                window.navigator.msSaveOrOpenBlob(blob, filename);\n              } else {\n                var frame = document.createElement('iframe');\n                document.body.appendChild(frame);\n                var cw = frame.contentWindow;\n                var doc = cw.document;\n                doc.open('image/svg+xml', 'replace');\n                doc.write(bstr);\n                doc.close();\n                cw.focus();\n                doc.execCommand('SaveAs', true, filename);\n                document.body.removeChild(frame);\n              }\n            } else {\n              var lang = model.get('lang');\n              var html = '' + '<body style=\"margin:0;\">' + '<img src=\"' + url + '\" style=\"max-width:100%;\" title=\"' + (lang && lang[0] || '') + '\" />' + '</body>';\n              var tab = window.open();\n              tab.document.write(html);\n              tab.document.title = title;\n            }\n          }\n      };\n\n      SaveAsImage.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0',\n          title: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'title']),\n          type: 'png',\n          // Default use option.backgroundColor\n          // backgroundColor: '#fff',\n          connectedBackgroundColor: '#fff',\n          name: '',\n          excludeComponents: ['toolbox'],\n          // use current pixel ratio of device by default\n          // pixelRatio: 1,\n          lang: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'lang'])\n        };\n        return defaultOption;\n      };\n\n      return SaveAsImage;\n    }(ToolboxFeature);\n\n    var INNER_STACK_KEYWORD = '__ec_magicType_stack__';\n    var radioTypes = [['line', 'bar'], ['stack']];\n\n    var MagicType =\n    /** @class */\n    function (_super) {\n      __extends(MagicType, _super);\n\n      function MagicType() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      MagicType.prototype.getIcons = function () {\n        var model = this.model;\n        var availableIcons = model.get('icon');\n        var icons = {};\n        each(model.get('type'), function (type) {\n          if (availableIcons[type]) {\n            icons[type] = availableIcons[type];\n          }\n        });\n        return icons;\n      };\n\n      MagicType.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          type: [],\n          // Icon group\n          icon: {\n            line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4',\n            bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7',\n            // eslint-disable-next-line\n            stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z' // jshint ignore:line\n\n          },\n          // `line`, `bar`, `stack`, `tiled`\n          title: ecModel.getLocaleModel().get(['toolbox', 'magicType', 'title']),\n          option: {},\n          seriesIndex: {}\n        };\n        return defaultOption;\n      };\n\n      MagicType.prototype.onclick = function (ecModel, api, type) {\n        var model = this.model;\n        var seriesIndex = model.get(['seriesIndex', type]); // Not supported magicType\n\n        if (!seriesOptGenreator[type]) {\n          return;\n        }\n\n        var newOption = {\n          series: []\n        };\n\n        var generateNewSeriesTypes = function (seriesModel) {\n          var seriesType = seriesModel.subType;\n          var seriesId = seriesModel.id;\n          var newSeriesOpt = seriesOptGenreator[type](seriesType, seriesId, seriesModel, model);\n\n          if (newSeriesOpt) {\n            // PENDING If merge original option?\n            defaults(newSeriesOpt, seriesModel.option);\n            newOption.series.push(newSeriesOpt);\n          } // Modify boundaryGap\n\n\n          var coordSys = seriesModel.coordinateSystem;\n\n          if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) {\n            var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n\n            if (categoryAxis) {\n              var axisDim = categoryAxis.dim;\n              var axisType = axisDim + 'Axis';\n              var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0];\n              var axisIndex = axisModel.componentIndex;\n              newOption[axisType] = newOption[axisType] || [];\n\n              for (var i = 0; i <= axisIndex; i++) {\n                newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {};\n              }\n\n              newOption[axisType][axisIndex].boundaryGap = type === 'bar';\n            }\n          }\n        };\n\n        each(radioTypes, function (radio) {\n          if (indexOf(radio, type) >= 0) {\n            each(radio, function (item) {\n              model.setIconStatus(item, 'normal');\n            });\n          }\n        });\n        model.setIconStatus(type, 'emphasis');\n        ecModel.eachComponent({\n          mainType: 'series',\n          query: seriesIndex == null ? null : {\n            seriesIndex: seriesIndex\n          }\n        }, generateNewSeriesTypes);\n        var newTitle;\n        var currentType = type; // Change title of stack\n\n        if (type === 'stack') {\n          // use titles in model instead of ecModel\n          // as stack and tiled appears in pair, just flip them\n          // no need of checking stack state\n          newTitle = merge({\n            stack: model.option.title.tiled,\n            tiled: model.option.title.stack\n          }, model.option.title);\n\n          if (model.get(['iconStatus', type]) !== 'emphasis') {\n            currentType = 'tiled';\n          }\n        }\n\n        api.dispatchAction({\n          type: 'changeMagicType',\n          currentType: currentType,\n          newOption: newOption,\n          newTitle: newTitle,\n          featureName: 'magicType'\n        });\n      };\n\n      return MagicType;\n    }(ToolboxFeature);\n\n    var seriesOptGenreator = {\n      'line': function (seriesType, seriesId, seriesModel, model) {\n        if (seriesType === 'bar') {\n          return merge({\n            id: seriesId,\n            type: 'line',\n            // Preserve data related option\n            data: seriesModel.get('data'),\n            stack: seriesModel.get('stack'),\n            markPoint: seriesModel.get('markPoint'),\n            markLine: seriesModel.get('markLine')\n          }, model.get(['option', 'line']) || {}, true);\n        }\n      },\n      'bar': function (seriesType, seriesId, seriesModel, model) {\n        if (seriesType === 'line') {\n          return merge({\n            id: seriesId,\n            type: 'bar',\n            // Preserve data related option\n            data: seriesModel.get('data'),\n            stack: seriesModel.get('stack'),\n            markPoint: seriesModel.get('markPoint'),\n            markLine: seriesModel.get('markLine')\n          }, model.get(['option', 'bar']) || {}, true);\n        }\n      },\n      'stack': function (seriesType, seriesId, seriesModel, model) {\n        var isStack = seriesModel.get('stack') === INNER_STACK_KEYWORD;\n\n        if (seriesType === 'line' || seriesType === 'bar') {\n          model.setIconStatus('stack', isStack ? 'normal' : 'emphasis');\n          return merge({\n            id: seriesId,\n            stack: isStack ? '' : INNER_STACK_KEYWORD\n          }, model.get(['option', 'stack']) || {}, true);\n        }\n      }\n    }; // TODO: SELF REGISTERED.\n\n    registerAction({\n      type: 'changeMagicType',\n      event: 'magicTypeChanged',\n      update: 'prepareAndUpdate'\n    }, function (payload, ecModel) {\n      ecModel.mergeOption(payload.newOption);\n    });\n\n    /* global document */\n\n    var BLOCK_SPLITER = new Array(60).join('-');\n    var ITEM_SPLITER = '\\t';\n    /**\n     * Group series into two types\n     *  1. on category axis, like line, bar\n     *  2. others, like scatter, pie\n     */\n\n    function groupSeries(ecModel) {\n      var seriesGroupByCategoryAxis = {};\n      var otherSeries = [];\n      var meta = [];\n      ecModel.eachRawSeries(function (seriesModel) {\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) {\n          // TODO: TYPE Consider polar? Include polar may increase unecessary bundle size.\n          var baseAxis = coordSys.getBaseAxis();\n\n          if (baseAxis.type === 'category') {\n            var key = baseAxis.dim + '_' + baseAxis.index;\n\n            if (!seriesGroupByCategoryAxis[key]) {\n              seriesGroupByCategoryAxis[key] = {\n                categoryAxis: baseAxis,\n                valueAxis: coordSys.getOtherAxis(baseAxis),\n                series: []\n              };\n              meta.push({\n                axisDim: baseAxis.dim,\n                axisIndex: baseAxis.index\n              });\n            }\n\n            seriesGroupByCategoryAxis[key].series.push(seriesModel);\n          } else {\n            otherSeries.push(seriesModel);\n          }\n        } else {\n          otherSeries.push(seriesModel);\n        }\n      });\n      return {\n        seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,\n        other: otherSeries,\n        meta: meta\n      };\n    }\n    /**\n     * Assemble content of series on cateogory axis\n     * @inner\n     */\n\n\n    function assembleSeriesWithCategoryAxis(groups) {\n      var tables = [];\n      each(groups, function (group, key) {\n        var categoryAxis = group.categoryAxis;\n        var valueAxis = group.valueAxis;\n        var valueAxisDim = valueAxis.dim;\n        var headers = [' '].concat(map(group.series, function (series) {\n          return series.name;\n        })); // @ts-ignore TODO Polar\n\n        var columns = [categoryAxis.model.getCategories()];\n        each(group.series, function (series) {\n          var rawData = series.getRawData();\n          columns.push(series.getRawData().mapArray(rawData.mapDimension(valueAxisDim), function (val) {\n            return val;\n          }));\n        }); // Assemble table content\n\n        var lines = [headers.join(ITEM_SPLITER)];\n\n        for (var i = 0; i < columns[0].length; i++) {\n          var items = [];\n\n          for (var j = 0; j < columns.length; j++) {\n            items.push(columns[j][i]);\n          }\n\n          lines.push(items.join(ITEM_SPLITER));\n        }\n\n        tables.push(lines.join('\\n'));\n      });\n      return tables.join('\\n\\n' + BLOCK_SPLITER + '\\n\\n');\n    }\n    /**\n     * Assemble content of other series\n     */\n\n\n    function assembleOtherSeries(series) {\n      return map(series, function (series) {\n        var data = series.getRawData();\n        var lines = [series.name];\n        var vals = [];\n        data.each(data.dimensions, function () {\n          var argLen = arguments.length;\n          var dataIndex = arguments[argLen - 1];\n          var name = data.getName(dataIndex);\n\n          for (var i = 0; i < argLen - 1; i++) {\n            vals[i] = arguments[i];\n          }\n\n          lines.push((name ? name + ITEM_SPLITER : '') + vals.join(ITEM_SPLITER));\n        });\n        return lines.join('\\n');\n      }).join('\\n\\n' + BLOCK_SPLITER + '\\n\\n');\n    }\n\n    function getContentFromModel(ecModel) {\n      var result = groupSeries(ecModel);\n      return {\n        value: filter([assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), assembleOtherSeries(result.other)], function (str) {\n          return !!str.replace(/[\\n\\t\\s]/g, '');\n        }).join('\\n\\n' + BLOCK_SPLITER + '\\n\\n'),\n        meta: result.meta\n      };\n    }\n\n    function trim$1(str) {\n      return str.replace(/^\\s\\s*/, '').replace(/\\s\\s*$/, '');\n    }\n    /**\n     * If a block is tsv format\n     */\n\n\n    function isTSVFormat(block) {\n      // Simple method to find out if a block is tsv format\n      var firstLine = block.slice(0, block.indexOf('\\n'));\n\n      if (firstLine.indexOf(ITEM_SPLITER) >= 0) {\n        return true;\n      }\n    }\n\n    var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g');\n    /**\n     * @param {string} tsv\n     * @return {Object}\n     */\n\n    function parseTSVContents(tsv) {\n      var tsvLines = tsv.split(/\\n+/g);\n      var headers = trim$1(tsvLines.shift()).split(itemSplitRegex);\n      var categories = [];\n      var series = map(headers, function (header) {\n        return {\n          name: header,\n          data: []\n        };\n      });\n\n      for (var i = 0; i < tsvLines.length; i++) {\n        var items = trim$1(tsvLines[i]).split(itemSplitRegex);\n        categories.push(items.shift());\n\n        for (var j = 0; j < items.length; j++) {\n          series[j] && (series[j].data[i] = items[j]);\n        }\n      }\n\n      return {\n        series: series,\n        categories: categories\n      };\n    }\n\n    function parseListContents(str) {\n      var lines = str.split(/\\n+/g);\n      var seriesName = trim$1(lines.shift());\n      var data = [];\n\n      for (var i = 0; i < lines.length; i++) {\n        // if line is empty, ignore it.\n        // there is a case that a user forgot to delete `\\n`.\n        var line = trim$1(lines[i]);\n\n        if (!line) {\n          continue;\n        }\n\n        var items = line.split(itemSplitRegex);\n        var name_1 = '';\n        var value = void 0;\n        var hasName = false;\n\n        if (isNaN(items[0])) {\n          // First item is name\n          hasName = true;\n          name_1 = items[0];\n          items = items.slice(1);\n          data[i] = {\n            name: name_1,\n            value: []\n          };\n          value = data[i].value;\n        } else {\n          value = data[i] = [];\n        }\n\n        for (var j = 0; j < items.length; j++) {\n          value.push(+items[j]);\n        }\n\n        if (value.length === 1) {\n          hasName ? data[i].value = value[0] : data[i] = value[0];\n        }\n      }\n\n      return {\n        name: seriesName,\n        data: data\n      };\n    }\n\n    function parseContents(str, blockMetaList) {\n      var blocks = str.split(new RegExp('\\n*' + BLOCK_SPLITER + '\\n*', 'g'));\n      var newOption = {\n        series: []\n      };\n      each(blocks, function (block, idx) {\n        if (isTSVFormat(block)) {\n          var result = parseTSVContents(block);\n          var blockMeta = blockMetaList[idx];\n          var axisKey = blockMeta.axisDim + 'Axis';\n\n          if (blockMeta) {\n            newOption[axisKey] = newOption[axisKey] || [];\n            newOption[axisKey][blockMeta.axisIndex] = {\n              data: result.categories\n            };\n            newOption.series = newOption.series.concat(result.series);\n          }\n        } else {\n          var result = parseListContents(block);\n          newOption.series.push(result);\n        }\n      });\n      return newOption;\n    }\n\n    var DataView =\n    /** @class */\n    function (_super) {\n      __extends(DataView, _super);\n\n      function DataView() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      DataView.prototype.onclick = function (ecModel, api) {\n        // FIXME: better way?\n        setTimeout(function () {\n          api.dispatchAction({\n            type: 'hideTip'\n          });\n        });\n        var container = api.getDom();\n        var model = this.model;\n\n        if (this._dom) {\n          container.removeChild(this._dom);\n        }\n\n        var root = document.createElement('div'); // use padding to avoid 5px whitespace\n\n        root.style.cssText = 'position:absolute;top:0;bottom:0;left:0;right:0;padding:5px';\n        root.style.backgroundColor = model.get('backgroundColor') || '#fff'; // Create elements\n\n        var header = document.createElement('h4');\n        var lang = model.get('lang') || [];\n        header.innerHTML = lang[0] || model.get('title');\n        header.style.cssText = 'margin:10px 20px';\n        header.style.color = model.get('textColor');\n        var viewMain = document.createElement('div');\n        var textarea = document.createElement('textarea');\n        viewMain.style.cssText = 'overflow:auto';\n        var optionToContent = model.get('optionToContent');\n        var contentToOption = model.get('contentToOption');\n        var result = getContentFromModel(ecModel);\n\n        if (isFunction(optionToContent)) {\n          var htmlOrDom = optionToContent(api.getOption());\n\n          if (isString(htmlOrDom)) {\n            viewMain.innerHTML = htmlOrDom;\n          } else if (isDom(htmlOrDom)) {\n            viewMain.appendChild(htmlOrDom);\n          }\n        } else {\n          // Use default textarea\n          textarea.readOnly = model.get('readOnly');\n          var style = textarea.style; // eslint-disable-next-line max-len\n\n          style.cssText = 'display:block;width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;resize:none;box-sizing:border-box;outline:none';\n          style.color = model.get('textColor');\n          style.borderColor = model.get('textareaBorderColor');\n          style.backgroundColor = model.get('textareaColor');\n          textarea.value = result.value;\n          viewMain.appendChild(textarea);\n        }\n\n        var blockMetaList = result.meta;\n        var buttonContainer = document.createElement('div');\n        buttonContainer.style.cssText = 'position:absolute;bottom:5px;left:0;right:0'; // eslint-disable-next-line max-len\n\n        var buttonStyle = 'float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px';\n        var closeButton = document.createElement('div');\n        var refreshButton = document.createElement('div');\n        buttonStyle += ';background-color:' + model.get('buttonColor');\n        buttonStyle += ';color:' + model.get('buttonTextColor');\n        var self = this;\n\n        function close() {\n          container.removeChild(root);\n          self._dom = null;\n        }\n\n        addEventListener(closeButton, 'click', close);\n        addEventListener(refreshButton, 'click', function () {\n          if (contentToOption == null && optionToContent != null || contentToOption != null && optionToContent == null) {\n            if (\"development\" !== 'production') {\n              // eslint-disable-next-line\n              warn('It seems you have just provided one of `contentToOption` and `optionToContent` functions but missed the other one. Data change is ignored.');\n            }\n\n            close();\n            return;\n          }\n\n          var newOption;\n\n          try {\n            if (isFunction(contentToOption)) {\n              newOption = contentToOption(viewMain, api.getOption());\n            } else {\n              newOption = parseContents(textarea.value, blockMetaList);\n            }\n          } catch (e) {\n            close();\n            throw new Error('Data view format error ' + e);\n          }\n\n          if (newOption) {\n            api.dispatchAction({\n              type: 'changeDataView',\n              newOption: newOption\n            });\n          }\n\n          close();\n        });\n        closeButton.innerHTML = lang[1];\n        refreshButton.innerHTML = lang[2];\n        refreshButton.style.cssText = closeButton.style.cssText = buttonStyle;\n        !model.get('readOnly') && buttonContainer.appendChild(refreshButton);\n        buttonContainer.appendChild(closeButton);\n        root.appendChild(header);\n        root.appendChild(viewMain);\n        root.appendChild(buttonContainer);\n        viewMain.style.height = container.clientHeight - 80 + 'px';\n        container.appendChild(root);\n        this._dom = root;\n      };\n\n      DataView.prototype.remove = function (ecModel, api) {\n        this._dom && api.getDom().removeChild(this._dom);\n      };\n\n      DataView.prototype.dispose = function (ecModel, api) {\n        this.remove(ecModel, api);\n      };\n\n      DataView.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          readOnly: false,\n          optionToContent: null,\n          contentToOption: null,\n          // eslint-disable-next-line\n          icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',\n          title: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'title']),\n          lang: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'lang']),\n          backgroundColor: '#fff',\n          textColor: '#000',\n          textareaColor: '#fff',\n          textareaBorderColor: '#333',\n          buttonColor: '#c23531',\n          buttonTextColor: '#fff'\n        };\n        return defaultOption;\n      };\n\n      return DataView;\n    }(ToolboxFeature);\n    /**\n     * @inner\n     */\n\n\n    function tryMergeDataOption(newData, originalData) {\n      return map(newData, function (newVal, idx) {\n        var original = originalData && originalData[idx];\n\n        if (isObject(original) && !isArray(original)) {\n          var newValIsObject = isObject(newVal) && !isArray(newVal);\n\n          if (!newValIsObject) {\n            newVal = {\n              value: newVal\n            };\n          } // original data has name but new data has no name\n\n\n          var shouldDeleteName = original.name != null && newVal.name == null; // Original data has option\n\n          newVal = defaults(newVal, original);\n          shouldDeleteName && delete newVal.name;\n          return newVal;\n        } else {\n          return newVal;\n        }\n      });\n    } // TODO: SELF REGISTERED.\n\n\n    registerAction({\n      type: 'changeDataView',\n      event: 'dataViewChanged',\n      update: 'prepareAndUpdate'\n    }, function (payload, ecModel) {\n      var newSeriesOptList = [];\n      each(payload.newOption.series, function (seriesOpt) {\n        var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];\n\n        if (!seriesModel) {\n          // New created series\n          // Geuss the series type\n          newSeriesOptList.push(extend({\n            // Default is scatter\n            type: 'scatter'\n          }, seriesOpt));\n        } else {\n          var originalData = seriesModel.get('data');\n          newSeriesOptList.push({\n            name: seriesOpt.name,\n            data: tryMergeDataOption(seriesOpt.data, originalData)\n          });\n        }\n      });\n      ecModel.mergeOption(defaults({\n        series: newSeriesOptList\n      }, payload.newOption));\n    });\n\n    var each$9 = each;\n    var inner$f = makeInner();\n    /**\n     * @param ecModel\n     * @param newSnapshot key is dataZoomId\n     */\n\n    function push(ecModel, newSnapshot) {\n      var storedSnapshots = getStoreSnapshots(ecModel); // If previous dataZoom can not be found,\n      // complete an range with current range.\n\n      each$9(newSnapshot, function (batchItem, dataZoomId) {\n        var i = storedSnapshots.length - 1;\n\n        for (; i >= 0; i--) {\n          var snapshot = storedSnapshots[i];\n\n          if (snapshot[dataZoomId]) {\n            break;\n          }\n        }\n\n        if (i < 0) {\n          // No origin range set, create one by current range.\n          var dataZoomModel = ecModel.queryComponents({\n            mainType: 'dataZoom',\n            subType: 'select',\n            id: dataZoomId\n          })[0];\n\n          if (dataZoomModel) {\n            var percentRange = dataZoomModel.getPercentRange();\n            storedSnapshots[0][dataZoomId] = {\n              dataZoomId: dataZoomId,\n              start: percentRange[0],\n              end: percentRange[1]\n            };\n          }\n        }\n      });\n      storedSnapshots.push(newSnapshot);\n    }\n    function pop(ecModel) {\n      var storedSnapshots = getStoreSnapshots(ecModel);\n      var head = storedSnapshots[storedSnapshots.length - 1];\n      storedSnapshots.length > 1 && storedSnapshots.pop(); // Find top for all dataZoom.\n\n      var snapshot = {};\n      each$9(head, function (batchItem, dataZoomId) {\n        for (var i = storedSnapshots.length - 1; i >= 0; i--) {\n          batchItem = storedSnapshots[i][dataZoomId];\n\n          if (batchItem) {\n            snapshot[dataZoomId] = batchItem;\n            break;\n          }\n        }\n      });\n      return snapshot;\n    }\n    function clear$1(ecModel) {\n      inner$f(ecModel).snapshots = null;\n    }\n    function count(ecModel) {\n      return getStoreSnapshots(ecModel).length;\n    }\n    /**\n     * History length of each dataZoom may be different.\n     * this._history[0] is used to store origin range.\n     */\n\n    function getStoreSnapshots(ecModel) {\n      var store = inner$f(ecModel);\n\n      if (!store.snapshots) {\n        store.snapshots = [{}];\n      }\n\n      return store.snapshots;\n    }\n\n    var RestoreOption =\n    /** @class */\n    function (_super) {\n      __extends(RestoreOption, _super);\n\n      function RestoreOption() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      RestoreOption.prototype.onclick = function (ecModel, api) {\n        clear$1(ecModel);\n        api.dispatchAction({\n          type: 'restore',\n          from: this.uid\n        });\n      };\n\n      RestoreOption.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          // eslint-disable-next-line\n          icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5',\n          title: ecModel.getLocaleModel().get(['toolbox', 'restore', 'title'])\n        };\n        return defaultOption;\n      };\n\n      return RestoreOption;\n    }(ToolboxFeature); // TODO: SELF REGISTERED.\n\n\n    registerAction({\n      type: 'restore',\n      event: 'restore',\n      update: 'prepareAndUpdate'\n    }, function (payload, ecModel) {\n      ecModel.resetOption('recreate');\n    });\n\n    // how to genarialize to more coordinate systems.\n\n    var INCLUDE_FINDER_MAIN_TYPES = ['grid', 'xAxis', 'yAxis', 'geo', 'graph', 'polar', 'radiusAxis', 'angleAxis', 'bmap'];\n\n    var BrushTargetManager =\n    /** @class */\n    function () {\n      /**\n       * @param finder contains Index/Id/Name of xAxis/yAxis/geo/grid\n       *        Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}\n       * @param opt.include include coordinate system types.\n       */\n      function BrushTargetManager(finder, ecModel, opt) {\n        var _this = this;\n\n        this._targetInfoList = [];\n        var foundCpts = parseFinder$1(ecModel, finder);\n        each(targetInfoBuilders, function (builder, type) {\n          if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {\n            builder(foundCpts, _this._targetInfoList);\n          }\n        });\n      }\n\n      BrushTargetManager.prototype.setOutputRanges = function (areas, ecModel) {\n        this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {\n          (area.coordRanges || (area.coordRanges = [])).push(coordRange); // area.coordRange is the first of area.coordRanges\n\n          if (!area.coordRange) {\n            area.coordRange = coordRange; // In 'category' axis, coord to pixel is not reversible, so we can not\n            // rebuild range by coordRange accrately, which may bring trouble when\n            // brushing only one item. So we use __rangeOffset to rebuilding range\n            // by coordRange. And this it only used in brush component so it is no\n            // need to be adapted to coordRanges.\n\n            var result = coordConvert[area.brushType](0, coordSys, coordRange);\n            area.__rangeOffset = {\n              offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),\n              xyMinMax: result.xyMinMax\n            };\n          }\n        });\n        return areas;\n      };\n\n      BrushTargetManager.prototype.matchOutputRanges = function (areas, ecModel, cb) {\n        each(areas, function (area) {\n          var targetInfo = this.findTargetInfo(area, ecModel);\n\n          if (targetInfo && targetInfo !== true) {\n            each(targetInfo.coordSyses, function (coordSys) {\n              var result = coordConvert[area.brushType](1, coordSys, area.range, true);\n              cb(area, result.values, coordSys, ecModel);\n            });\n          }\n        }, this);\n      };\n      /**\n       * the `areas` is `BrushModel.areas`.\n       * Called in layout stage.\n       * convert `area.coordRange` to global range and set panelId to `area.range`.\n       */\n\n\n      BrushTargetManager.prototype.setInputRanges = function (areas, ecModel) {\n        each(areas, function (area) {\n          var targetInfo = this.findTargetInfo(area, ecModel);\n\n          if (\"development\" !== 'production') {\n            assert(!targetInfo || targetInfo === true || area.coordRange, 'coordRange must be specified when coord index specified.');\n            assert(!targetInfo || targetInfo !== true || area.range, 'range must be specified in global brush.');\n          }\n\n          area.range = area.range || []; // convert coordRange to global range and set panelId.\n\n          if (targetInfo && targetInfo !== true) {\n            area.panelId = targetInfo.panelId; // (1) area.range shoule always be calculate from coordRange but does\n            // not keep its original value, for the sake of the dataZoom scenario,\n            // where area.coordRange remains unchanged but area.range may be changed.\n            // (2) Only support converting one coordRange to pixel range in brush\n            // component. So do not consider `coordRanges`.\n            // (3) About __rangeOffset, see comment above.\n\n            var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);\n            var rangeOffset = area.__rangeOffset;\n            area.range = rangeOffset ? diffProcessor[area.brushType](result.values, rangeOffset.offset, getScales(result.xyMinMax, rangeOffset.xyMinMax)) : result.values;\n          }\n        }, this);\n      };\n\n      BrushTargetManager.prototype.makePanelOpts = function (api, getDefaultBrushType) {\n        return map(this._targetInfoList, function (targetInfo) {\n          var rect = targetInfo.getPanelRect();\n          return {\n            panelId: targetInfo.panelId,\n            defaultBrushType: getDefaultBrushType ? getDefaultBrushType(targetInfo) : null,\n            clipPath: makeRectPanelClipPath(rect),\n            isTargetByCursor: makeRectIsTargetByCursor(rect, api, targetInfo.coordSysModel),\n            getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect)\n          };\n        });\n      };\n\n      BrushTargetManager.prototype.controlSeries = function (area, seriesModel, ecModel) {\n        // Check whether area is bound in coord, and series do not belong to that coord.\n        // If do not do this check, some brush (like lineX) will controll all axes.\n        var targetInfo = this.findTargetInfo(area, ecModel);\n        return targetInfo === true || targetInfo && indexOf(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0;\n      };\n      /**\n       * If return Object, a coord found.\n       * If reutrn true, global found.\n       * Otherwise nothing found.\n       */\n\n\n      BrushTargetManager.prototype.findTargetInfo = function (area, ecModel) {\n        var targetInfoList = this._targetInfoList;\n        var foundCpts = parseFinder$1(ecModel, area);\n\n        for (var i = 0; i < targetInfoList.length; i++) {\n          var targetInfo = targetInfoList[i];\n          var areaPanelId = area.panelId;\n\n          if (areaPanelId) {\n            if (targetInfo.panelId === areaPanelId) {\n              return targetInfo;\n            }\n          } else {\n            for (var j = 0; j < targetInfoMatchers.length; j++) {\n              if (targetInfoMatchers[j](foundCpts, targetInfo)) {\n                return targetInfo;\n              }\n            }\n          }\n        }\n\n        return true;\n      };\n\n      return BrushTargetManager;\n    }();\n\n    function formatMinMax(minMax) {\n      minMax[0] > minMax[1] && minMax.reverse();\n      return minMax;\n    }\n\n    function parseFinder$1(ecModel, finder) {\n      return parseFinder(ecModel, finder, {\n        includeMainTypes: INCLUDE_FINDER_MAIN_TYPES\n      });\n    }\n\n    var targetInfoBuilders = {\n      grid: function (foundCpts, targetInfoList) {\n        var xAxisModels = foundCpts.xAxisModels;\n        var yAxisModels = foundCpts.yAxisModels;\n        var gridModels = foundCpts.gridModels; // Remove duplicated.\n\n        var gridModelMap = createHashMap();\n        var xAxesHas = {};\n        var yAxesHas = {};\n\n        if (!xAxisModels && !yAxisModels && !gridModels) {\n          return;\n        }\n\n        each(xAxisModels, function (axisModel) {\n          var gridModel = axisModel.axis.grid.model;\n          gridModelMap.set(gridModel.id, gridModel);\n          xAxesHas[gridModel.id] = true;\n        });\n        each(yAxisModels, function (axisModel) {\n          var gridModel = axisModel.axis.grid.model;\n          gridModelMap.set(gridModel.id, gridModel);\n          yAxesHas[gridModel.id] = true;\n        });\n        each(gridModels, function (gridModel) {\n          gridModelMap.set(gridModel.id, gridModel);\n          xAxesHas[gridModel.id] = true;\n          yAxesHas[gridModel.id] = true;\n        });\n        gridModelMap.each(function (gridModel) {\n          var grid = gridModel.coordinateSystem;\n          var cartesians = [];\n          each(grid.getCartesians(), function (cartesian, index) {\n            if (indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0 || indexOf(yAxisModels, cartesian.getAxis('y').model) >= 0) {\n              cartesians.push(cartesian);\n            }\n          });\n          targetInfoList.push({\n            panelId: 'grid--' + gridModel.id,\n            gridModel: gridModel,\n            coordSysModel: gridModel,\n            // Use the first one as the representitive coordSys.\n            coordSys: cartesians[0],\n            coordSyses: cartesians,\n            getPanelRect: panelRectBuilders.grid,\n            xAxisDeclared: xAxesHas[gridModel.id],\n            yAxisDeclared: yAxesHas[gridModel.id]\n          });\n        });\n      },\n      geo: function (foundCpts, targetInfoList) {\n        each(foundCpts.geoModels, function (geoModel) {\n          var coordSys = geoModel.coordinateSystem;\n          targetInfoList.push({\n            panelId: 'geo--' + geoModel.id,\n            geoModel: geoModel,\n            coordSysModel: geoModel,\n            coordSys: coordSys,\n            coordSyses: [coordSys],\n            getPanelRect: panelRectBuilders.geo\n          });\n        });\n      }\n    };\n    var targetInfoMatchers = [// grid\n    function (foundCpts, targetInfo) {\n      var xAxisModel = foundCpts.xAxisModel;\n      var yAxisModel = foundCpts.yAxisModel;\n      var gridModel = foundCpts.gridModel;\n      !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);\n      !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);\n      return gridModel && gridModel === targetInfo.gridModel;\n    }, // geo\n    function (foundCpts, targetInfo) {\n      var geoModel = foundCpts.geoModel;\n      return geoModel && geoModel === targetInfo.geoModel;\n    }];\n    var panelRectBuilders = {\n      grid: function () {\n        // grid is not Transformable.\n        return this.coordSys.master.getRect().clone();\n      },\n      geo: function () {\n        var coordSys = this.coordSys;\n        var rect = coordSys.getBoundingRect().clone(); // geo roam and zoom transform\n\n        rect.applyTransform(getTransform(coordSys));\n        return rect;\n      }\n    };\n    var coordConvert = {\n      lineX: curry(axisConvert, 0),\n      lineY: curry(axisConvert, 1),\n      rect: function (to, coordSys, rangeOrCoordRange, clamp) {\n        var xminymin = to ? coordSys.pointToData([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp);\n        var xmaxymax = to ? coordSys.pointToData([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp);\n        var values = [formatMinMax([xminymin[0], xmaxymax[0]]), formatMinMax([xminymin[1], xmaxymax[1]])];\n        return {\n          values: values,\n          xyMinMax: values\n        };\n      },\n      polygon: function (to, coordSys, rangeOrCoordRange, clamp) {\n        var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];\n        var values = map(rangeOrCoordRange, function (item) {\n          var p = to ? coordSys.pointToData(item, clamp) : coordSys.dataToPoint(item, clamp);\n          xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);\n          xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);\n          xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]);\n          xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]);\n          return p;\n        });\n        return {\n          values: values,\n          xyMinMax: xyMinMax\n        };\n      }\n    };\n\n    function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {\n      if (\"development\" !== 'production') {\n        assert(coordSys.type === 'cartesian2d', 'lineX/lineY brush is available only in cartesian2d.');\n      }\n\n      var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);\n      var values = formatMinMax(map([0, 1], function (i) {\n        return to ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]), true) : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));\n      }));\n      var xyMinMax = [];\n      xyMinMax[axisNameIndex] = values;\n      xyMinMax[1 - axisNameIndex] = [NaN, NaN];\n      return {\n        values: values,\n        xyMinMax: xyMinMax\n      };\n    }\n\n    var diffProcessor = {\n      lineX: curry(axisDiffProcessor, 0),\n      lineY: curry(axisDiffProcessor, 1),\n      rect: function (values, refer, scales) {\n        return [[values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]], [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]];\n      },\n      polygon: function (values, refer, scales) {\n        return map(values, function (item, idx) {\n          return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];\n        });\n      }\n    };\n\n    function axisDiffProcessor(axisNameIndex, values, refer, scales) {\n      return [values[0] - scales[axisNameIndex] * refer[0], values[1] - scales[axisNameIndex] * refer[1]];\n    } // We have to process scale caused by dataZoom manually,\n    // although it might be not accurate.\n    // Return [0~1, 0~1]\n\n\n    function getScales(xyMinMaxCurr, xyMinMaxOrigin) {\n      var sizeCurr = getSize$1(xyMinMaxCurr);\n      var sizeOrigin = getSize$1(xyMinMaxOrigin);\n      var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];\n      isNaN(scales[0]) && (scales[0] = 1);\n      isNaN(scales[1]) && (scales[1] = 1);\n      return scales;\n    }\n\n    function getSize$1(xyMinMax) {\n      return xyMinMax ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] : [NaN, NaN];\n    }\n\n    var each$a = each;\n    var DATA_ZOOM_ID_BASE = makeInternalComponentId('toolbox-dataZoom_');\n\n    var DataZoomFeature =\n    /** @class */\n    function (_super) {\n      __extends(DataZoomFeature, _super);\n\n      function DataZoomFeature() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      DataZoomFeature.prototype.render = function (featureModel, ecModel, api, payload) {\n        if (!this._brushController) {\n          this._brushController = new BrushController(api.getZr());\n\n          this._brushController.on('brush', bind(this._onBrush, this)).mount();\n        }\n\n        updateZoomBtnStatus(featureModel, ecModel, this, payload, api);\n        updateBackBtnStatus(featureModel, ecModel);\n      };\n\n      DataZoomFeature.prototype.onclick = function (ecModel, api, type) {\n        handlers$1[type].call(this);\n      };\n\n      DataZoomFeature.prototype.remove = function (ecModel, api) {\n        this._brushController && this._brushController.unmount();\n      };\n\n      DataZoomFeature.prototype.dispose = function (ecModel, api) {\n        this._brushController && this._brushController.dispose();\n      };\n\n      DataZoomFeature.prototype._onBrush = function (eventParam) {\n        var areas = eventParam.areas;\n\n        if (!eventParam.isEnd || !areas.length) {\n          return;\n        }\n\n        var snapshot = {};\n        var ecModel = this.ecModel;\n\n        this._brushController.updateCovers([]); // remove cover\n\n\n        var brushTargetManager = new BrushTargetManager(makeAxisFinder(this.model), ecModel, {\n          include: ['grid']\n        });\n        brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {\n          if (coordSys.type !== 'cartesian2d') {\n            return;\n          }\n\n          var brushType = area.brushType;\n\n          if (brushType === 'rect') {\n            setBatch('x', coordSys, coordRange[0]);\n            setBatch('y', coordSys, coordRange[1]);\n          } else {\n            setBatch({\n              lineX: 'x',\n              lineY: 'y'\n            }[brushType], coordSys, coordRange);\n          }\n        });\n        push(ecModel, snapshot);\n\n        this._dispatchZoomAction(snapshot);\n\n        function setBatch(dimName, coordSys, minMax) {\n          var axis = coordSys.getAxis(dimName);\n          var axisModel = axis.model;\n          var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); // Restrict range.\n\n          var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan();\n\n          if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) {\n            minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan);\n          }\n\n          dataZoomModel && (snapshot[dataZoomModel.id] = {\n            dataZoomId: dataZoomModel.id,\n            startValue: minMax[0],\n            endValue: minMax[1]\n          });\n        }\n\n        function findDataZoom(dimName, axisModel, ecModel) {\n          var found;\n          ecModel.eachComponent({\n            mainType: 'dataZoom',\n            subType: 'select'\n          }, function (dzModel) {\n            var has = dzModel.getAxisModel(dimName, axisModel.componentIndex);\n            has && (found = dzModel);\n          });\n          return found;\n        }\n      };\n\n      DataZoomFeature.prototype._dispatchZoomAction = function (snapshot) {\n        var batch = []; // Convert from hash map to array.\n\n        each$a(snapshot, function (batchItem, dataZoomId) {\n          batch.push(clone(batchItem));\n        });\n        batch.length && this.api.dispatchAction({\n          type: 'dataZoom',\n          from: this.uid,\n          batch: batch\n        });\n      };\n\n      DataZoomFeature.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          filterMode: 'filter',\n          // Icon group\n          icon: {\n            zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1',\n            back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26'\n          },\n          // `zoom`, `back`\n          title: ecModel.getLocaleModel().get(['toolbox', 'dataZoom', 'title']),\n          brushStyle: {\n            borderWidth: 0,\n            color: 'rgba(210,219,238,0.2)'\n          }\n        };\n        return defaultOption;\n      };\n\n      return DataZoomFeature;\n    }(ToolboxFeature);\n\n    var handlers$1 = {\n      zoom: function () {\n        var nextActive = !this._isZoomActive;\n        this.api.dispatchAction({\n          type: 'takeGlobalCursor',\n          key: 'dataZoomSelect',\n          dataZoomSelectActive: nextActive\n        });\n      },\n      back: function () {\n        this._dispatchZoomAction(pop(this.ecModel));\n      }\n    };\n\n    function makeAxisFinder(dzFeatureModel) {\n      var setting = {\n        xAxisIndex: dzFeatureModel.get('xAxisIndex', true),\n        yAxisIndex: dzFeatureModel.get('yAxisIndex', true),\n        xAxisId: dzFeatureModel.get('xAxisId', true),\n        yAxisId: dzFeatureModel.get('yAxisId', true)\n      }; // If both `xAxisIndex` `xAxisId` not set, it means 'all'.\n      // If both `yAxisIndex` `yAxisId` not set, it means 'all'.\n      // Some old cases set like this below to close yAxis control but leave xAxis control:\n      // `{ feature: { dataZoom: { yAxisIndex: false } }`.\n\n      if (setting.xAxisIndex == null && setting.xAxisId == null) {\n        setting.xAxisIndex = 'all';\n      }\n\n      if (setting.yAxisIndex == null && setting.yAxisId == null) {\n        setting.yAxisIndex = 'all';\n      }\n\n      return setting;\n    }\n\n    function updateBackBtnStatus(featureModel, ecModel) {\n      featureModel.setIconStatus('back', count(ecModel) > 1 ? 'emphasis' : 'normal');\n    }\n\n    function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) {\n      var zoomActive = view._isZoomActive;\n\n      if (payload && payload.type === 'takeGlobalCursor') {\n        zoomActive = payload.key === 'dataZoomSelect' ? payload.dataZoomSelectActive : false;\n      }\n\n      view._isZoomActive = zoomActive;\n      featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal');\n      var brushTargetManager = new BrushTargetManager(makeAxisFinder(featureModel), ecModel, {\n        include: ['grid']\n      });\n      var panels = brushTargetManager.makePanelOpts(api, function (targetInfo) {\n        return targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared ? 'lineX' : !targetInfo.xAxisDeclared && targetInfo.yAxisDeclared ? 'lineY' : 'rect';\n      });\n\n      view._brushController.setPanels(panels).enableBrush(zoomActive && panels.length ? {\n        brushType: 'auto',\n        brushStyle: featureModel.getModel('brushStyle').getItemStyle()\n      } : false);\n    }\n\n    registerInternalOptionCreator('dataZoom', function (ecModel) {\n      var toolboxModel = ecModel.getComponent('toolbox', 0);\n      var featureDataZoomPath = ['feature', 'dataZoom'];\n\n      if (!toolboxModel || toolboxModel.get(featureDataZoomPath) == null) {\n        return;\n      }\n\n      var dzFeatureModel = toolboxModel.getModel(featureDataZoomPath);\n      var dzOptions = [];\n      var finder = makeAxisFinder(dzFeatureModel);\n      var finderResult = parseFinder(ecModel, finder);\n      each$a(finderResult.xAxisModels, function (axisModel) {\n        return buildInternalOptions(axisModel, 'xAxis', 'xAxisIndex');\n      });\n      each$a(finderResult.yAxisModels, function (axisModel) {\n        return buildInternalOptions(axisModel, 'yAxis', 'yAxisIndex');\n      });\n\n      function buildInternalOptions(axisModel, axisMainType, axisIndexPropName) {\n        var axisIndex = axisModel.componentIndex;\n        var newOpt = {\n          type: 'select',\n          $fromToolbox: true,\n          // Default to be filter\n          filterMode: dzFeatureModel.get('filterMode', true) || 'filter',\n          // Id for merge mapping.\n          id: DATA_ZOOM_ID_BASE + axisMainType + axisIndex\n        };\n        newOpt[axisIndexPropName] = axisIndex;\n        dzOptions.push(newOpt);\n      }\n\n      return dzOptions;\n    });\n\n    function install$z(registers) {\n      registers.registerComponentModel(ToolboxModel);\n      registers.registerComponentView(ToolboxView);\n      registerFeature('saveAsImage', SaveAsImage);\n      registerFeature('magicType', MagicType);\n      registerFeature('dataView', DataView);\n      registerFeature('dataZoom', DataZoomFeature);\n      registerFeature('restore', RestoreOption);\n      use(install$y);\n    }\n\n    var TooltipModel =\n    /** @class */\n    function (_super) {\n      __extends(TooltipModel, _super);\n\n      function TooltipModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TooltipModel.type;\n        return _this;\n      }\n\n      TooltipModel.type = 'tooltip';\n      TooltipModel.dependencies = ['axisPointer'];\n      TooltipModel.defaultOption = {\n        // zlevel: 0,\n        z: 60,\n        show: true,\n        // tooltip main content\n        showContent: true,\n        // 'trigger' only works on coordinate system.\n        // 'item' | 'axis' | 'none'\n        trigger: 'item',\n        // 'click' | 'mousemove' | 'none'\n        triggerOn: 'mousemove|click',\n        alwaysShowContent: false,\n        displayMode: 'single',\n        renderMode: 'auto',\n        // whether restraint content inside viewRect.\n        // If renderMode: 'richText', default true.\n        // If renderMode: 'html', defaut false (for backward compat).\n        confine: null,\n        showDelay: 0,\n        hideDelay: 100,\n        // Animation transition time, unit is second\n        transitionDuration: 0.4,\n        enterable: false,\n        backgroundColor: '#fff',\n        // box shadow\n        shadowBlur: 10,\n        shadowColor: 'rgba(0, 0, 0, .2)',\n        shadowOffsetX: 1,\n        shadowOffsetY: 2,\n        // tooltip border radius, unit is px, default is 4\n        borderRadius: 4,\n        // tooltip border width, unit is px, default is 0 (no border)\n        borderWidth: 1,\n        // Tooltip inside padding, default is 5 for all direction\n        // Array is allowed to set up, right, bottom, left, same with css\n        // The default value: See `tooltip/tooltipMarkup.ts#getPaddingFromTooltipModel`.\n        padding: null,\n        // Extra css text\n        extraCssText: '',\n        // axis indicator, trigger by axis\n        axisPointer: {\n          // default is line\n          // legal values: 'line' | 'shadow' | 'cross'\n          type: 'line',\n          // Valid when type is line, appoint tooltip line locate on which line. Optional\n          // legal values: 'x' | 'y' | 'angle' | 'radius' | 'auto'\n          // default is 'auto', chose the axis which type is category.\n          // for multiply y axis, cartesian coord chose x axis, polar chose angle axis\n          axis: 'auto',\n          animation: 'auto',\n          animationDurationUpdate: 200,\n          animationEasingUpdate: 'exponentialOut',\n          crossStyle: {\n            color: '#999',\n            width: 1,\n            type: 'dashed',\n            // TODO formatter\n            textStyle: {}\n          } // lineStyle and shadowStyle should not be specified here,\n          // otherwise it will always override those styles on option.axisPointer.\n\n        },\n        textStyle: {\n          color: '#666',\n          fontSize: 14\n        }\n      };\n      return TooltipModel;\n    }(ComponentModel);\n\n    /* global document */\n\n    function shouldTooltipConfine(tooltipModel) {\n      var confineOption = tooltipModel.get('confine');\n      return confineOption != null ? !!confineOption // In richText mode, the outside part can not be visible.\n      : tooltipModel.get('renderMode') === 'richText';\n    }\n\n    function testStyle(styleProps) {\n      if (!env.domSupported) {\n        return;\n      }\n\n      var style = document.documentElement.style;\n\n      for (var i = 0, len = styleProps.length; i < len; i++) {\n        if (styleProps[i] in style) {\n          return styleProps[i];\n        }\n      }\n    }\n\n    var TRANSFORM_VENDOR = testStyle(['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);\n    var TRANSITION_VENDOR = testStyle(['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);\n    function toCSSVendorPrefix(styleVendor, styleProp) {\n      if (!styleVendor) {\n        return styleProp;\n      }\n\n      styleProp = toCamelCase(styleProp, true);\n      var idx = styleVendor.indexOf(styleProp);\n      styleVendor = idx === -1 ? styleProp : \"-\" + styleVendor.slice(0, idx) + \"-\" + styleProp;\n      return styleVendor.toLowerCase();\n    }\n    function getComputedStyle(el, style) {\n      var stl = el.currentStyle || document.defaultView && document.defaultView.getComputedStyle(el);\n      return stl ? style ? stl[style] : stl : null;\n    }\n\n    /* global document, window */\n\n    var CSS_TRANSITION_VENDOR = toCSSVendorPrefix(TRANSITION_VENDOR, 'transition');\n    var CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(TRANSFORM_VENDOR, 'transform'); // eslint-disable-next-line\n\n    var gCssText = \"position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;\" + (env.transform3dSupported ? 'will-change:transform;' : '');\n\n    function mirrorPos(pos) {\n      pos = pos === 'left' ? 'right' : pos === 'right' ? 'left' : pos === 'top' ? 'bottom' : 'top';\n      return pos;\n    }\n\n    function assembleArrow(tooltipModel, borderColor, arrowPosition) {\n      if (!isString(arrowPosition) || arrowPosition === 'inside') {\n        return '';\n      }\n\n      var backgroundColor = tooltipModel.get('backgroundColor');\n      var borderWidth = tooltipModel.get('borderWidth');\n      borderColor = convertToColorString(borderColor);\n      var arrowPos = mirrorPos(arrowPosition);\n      var arrowSize = Math.max(Math.round(borderWidth) * 1.5, 6);\n      var positionStyle = '';\n      var transformStyle = CSS_TRANSFORM_VENDOR + ':';\n      var rotateDeg;\n\n      if (indexOf(['left', 'right'], arrowPos) > -1) {\n        positionStyle += 'top:50%';\n        transformStyle += \"translateY(-50%) rotate(\" + (rotateDeg = arrowPos === 'left' ? -225 : -45) + \"deg)\";\n      } else {\n        positionStyle += 'left:50%';\n        transformStyle += \"translateX(-50%) rotate(\" + (rotateDeg = arrowPos === 'top' ? 225 : 45) + \"deg)\";\n      }\n\n      var rotateRadian = rotateDeg * Math.PI / 180;\n      var arrowWH = arrowSize + borderWidth;\n      var rotatedWH = arrowWH * Math.abs(Math.cos(rotateRadian)) + arrowWH * Math.abs(Math.sin(rotateRadian));\n      var arrowOffset = Math.round(((rotatedWH - Math.SQRT2 * borderWidth) / 2 + Math.SQRT2 * borderWidth - (rotatedWH - arrowWH) / 2) * 100) / 100;\n      positionStyle += \";\" + arrowPos + \":-\" + arrowOffset + \"px\";\n      var borderStyle = borderColor + \" solid \" + borderWidth + \"px;\";\n      var styleCss = [\"position:absolute;width:\" + arrowSize + \"px;height:\" + arrowSize + \"px;z-index:-1;\", positionStyle + \";\" + transformStyle + \";\", \"border-bottom:\" + borderStyle, \"border-right:\" + borderStyle, \"background-color:\" + backgroundColor + \";\"];\n      return \"<div style=\\\"\" + styleCss.join('') + \"\\\"></div>\";\n    }\n\n    function assembleTransition(duration, onlyFade) {\n      var transitionCurve = 'cubic-bezier(0.23,1,0.32,1)';\n      var transitionOption = \" \" + duration / 2 + \"s \" + transitionCurve;\n      var transitionText = \"opacity\" + transitionOption + \",visibility\" + transitionOption;\n\n      if (!onlyFade) {\n        transitionOption = \" \" + duration + \"s \" + transitionCurve;\n        transitionText += env.transformSupported ? \",\" + CSS_TRANSFORM_VENDOR + transitionOption : \",left\" + transitionOption + \",top\" + transitionOption;\n      }\n\n      return CSS_TRANSITION_VENDOR + ':' + transitionText;\n    }\n\n    function assembleTransform(x, y, toString) {\n      // If using float on style, the final width of the dom might\n      // keep changing slightly while mouse move. So `toFixed(0)` them.\n      var x0 = x.toFixed(0) + 'px';\n      var y0 = y.toFixed(0) + 'px'; // not support transform, use `left` and `top` instead.\n\n      if (!env.transformSupported) {\n        return toString ? \"top:\" + y0 + \";left:\" + x0 + \";\" : [['top', y0], ['left', x0]];\n      } // support transform\n\n\n      var is3d = env.transform3dSupported;\n      var translate = \"translate\" + (is3d ? '3d' : '') + \"(\" + x0 + \",\" + y0 + (is3d ? ',0' : '') + \")\";\n      return toString ? 'top:0;left:0;' + CSS_TRANSFORM_VENDOR + ':' + translate + ';' : [['top', 0], ['left', 0], [TRANSFORM_VENDOR, translate]];\n    }\n    /**\n     * @param {Object} textStyle\n     * @return {string}\n     * @inner\n     */\n\n\n    function assembleFont(textStyleModel) {\n      var cssText = [];\n      var fontSize = textStyleModel.get('fontSize');\n      var color = textStyleModel.getTextColor();\n      color && cssText.push('color:' + color);\n      cssText.push('font:' + textStyleModel.getFont());\n      fontSize // @ts-ignore, leave it to the tooltip refactor.\n      && cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');\n      var shadowColor = textStyleModel.get('textShadowColor');\n      var shadowBlur = textStyleModel.get('textShadowBlur') || 0;\n      var shadowOffsetX = textStyleModel.get('textShadowOffsetX') || 0;\n      var shadowOffsetY = textStyleModel.get('textShadowOffsetY') || 0;\n      shadowColor && shadowBlur && cssText.push('text-shadow:' + shadowOffsetX + 'px ' + shadowOffsetY + 'px ' + shadowBlur + 'px ' + shadowColor);\n      each(['decoration', 'align'], function (name) {\n        var val = textStyleModel.get(name);\n        val && cssText.push('text-' + name + ':' + val);\n      });\n      return cssText.join(';');\n    }\n\n    function assembleCssText(tooltipModel, enableTransition, onlyFade) {\n      var cssText = [];\n      var transitionDuration = tooltipModel.get('transitionDuration');\n      var backgroundColor = tooltipModel.get('backgroundColor');\n      var shadowBlur = tooltipModel.get('shadowBlur');\n      var shadowColor = tooltipModel.get('shadowColor');\n      var shadowOffsetX = tooltipModel.get('shadowOffsetX');\n      var shadowOffsetY = tooltipModel.get('shadowOffsetY');\n      var textStyleModel = tooltipModel.getModel('textStyle');\n      var padding = getPaddingFromTooltipModel(tooltipModel, 'html');\n      var boxShadow = shadowOffsetX + \"px \" + shadowOffsetY + \"px \" + shadowBlur + \"px \" + shadowColor;\n      cssText.push('box-shadow:' + boxShadow); // Animation transition. Do not animate when transitionDuration is 0.\n\n      enableTransition && transitionDuration && cssText.push(assembleTransition(transitionDuration, onlyFade));\n\n      if (backgroundColor) {\n        cssText.push('background-color:' + backgroundColor);\n      } // Border style\n\n\n      each(['width', 'color', 'radius'], function (name) {\n        var borderName = 'border-' + name;\n        var camelCase = toCamelCase(borderName);\n        var val = tooltipModel.get(camelCase);\n        val != null && cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));\n      }); // Text style\n\n      cssText.push(assembleFont(textStyleModel)); // Padding\n\n      if (padding != null) {\n        cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px');\n      }\n\n      return cssText.join(';') + ';';\n    } // If not able to make, do not modify the input `out`.\n\n\n    function makeStyleCoord(out, zr, appendToBody, zrX, zrY) {\n      var zrPainter = zr && zr.painter;\n\n      if (appendToBody) {\n        var zrViewportRoot = zrPainter && zrPainter.getViewportRoot();\n\n        if (zrViewportRoot) {\n          // Some APPs might use scale on body, so we support CSS transform here.\n          transformLocalCoord(out, zrViewportRoot, document.body, zrX, zrY);\n        }\n      } else {\n        out[0] = zrX;\n        out[1] = zrY; // xy should be based on canvas root. But tooltipContent is\n        // the sibling of canvas root. So padding of ec container\n        // should be considered here.\n\n        var viewportRootOffset = zrPainter && zrPainter.getViewportRootOffset();\n\n        if (viewportRootOffset) {\n          out[0] += viewportRootOffset.offsetLeft;\n          out[1] += viewportRootOffset.offsetTop;\n        }\n      }\n\n      out[2] = out[0] / zr.getWidth();\n      out[3] = out[1] / zr.getHeight();\n    }\n\n    var TooltipHTMLContent =\n    /** @class */\n    function () {\n      function TooltipHTMLContent(container, api, opt) {\n        this._show = false;\n        this._styleCoord = [0, 0, 0, 0];\n        this._enterable = true;\n        this._firstShow = true;\n        this._longHide = true;\n\n        if (env.wxa) {\n          return null;\n        }\n\n        var el = document.createElement('div'); // TODO: TYPE\n\n        el.domBelongToZr = true;\n        this.el = el;\n        var zr = this._zr = api.getZr();\n        var appendToBody = this._appendToBody = opt && opt.appendToBody;\n        makeStyleCoord(this._styleCoord, zr, appendToBody, api.getWidth() / 2, api.getHeight() / 2);\n\n        if (appendToBody) {\n          document.body.appendChild(el);\n        } else {\n          container.appendChild(el);\n        }\n\n        this._container = container; // FIXME\n        // Is it needed to trigger zr event manually if\n        // the browser do not support `pointer-events: none`.\n\n        var self = this;\n\n        el.onmouseenter = function () {\n          // clear the timeout in hideLater and keep showing tooltip\n          if (self._enterable) {\n            clearTimeout(self._hideTimeout);\n            self._show = true;\n          }\n\n          self._inContent = true;\n        };\n\n        el.onmousemove = function (e) {\n          e = e || window.event;\n\n          if (!self._enterable) {\n            // `pointer-events: none` is set to tooltip content div\n            // if `enterable` is set as `false`, and `el.onmousemove`\n            // can not be triggered. But in browser that do not\n            // support `pointer-events`, we need to do this:\n            // Try trigger zrender event to avoid mouse\n            // in and out shape too frequently\n            var handler = zr.handler;\n            var zrViewportRoot = zr.painter.getViewportRoot();\n            normalizeEvent(zrViewportRoot, e, true);\n            handler.dispatch('mousemove', e);\n          }\n        };\n\n        el.onmouseleave = function () {\n          // set `_inContent` to `false` before `hideLater`\n          self._inContent = false;\n\n          if (self._enterable) {\n            if (self._show) {\n              self.hideLater(self._hideDelay);\n            }\n          }\n        };\n      }\n      /**\n       * Update when tooltip is rendered\n       */\n\n\n      TooltipHTMLContent.prototype.update = function (tooltipModel) {\n        // FIXME\n        // Move this logic to ec main?\n        var container = this._container;\n        var position = getComputedStyle(container, 'position');\n        var domStyle = container.style;\n\n        if (domStyle.position !== 'absolute' && position !== 'absolute') {\n          domStyle.position = 'relative';\n        } // move tooltip if chart resized\n\n\n        var alwaysShowContent = tooltipModel.get('alwaysShowContent');\n        alwaysShowContent && this._moveIfResized(); // update className\n\n        this.el.className = tooltipModel.get('className') || ''; // Hide the tooltip\n        // PENDING\n        // this.hide();\n      };\n\n      TooltipHTMLContent.prototype.show = function (tooltipModel, nearPointColor) {\n        clearTimeout(this._hideTimeout);\n        clearTimeout(this._longHideTimeout);\n        var el = this.el;\n        var style = el.style;\n        var styleCoord = this._styleCoord;\n\n        if (!el.innerHTML) {\n          style.display = 'none';\n        } else {\n          style.cssText = gCssText + assembleCssText(tooltipModel, !this._firstShow, this._longHide) // initial transform\n          + assembleTransform(styleCoord[0], styleCoord[1], true) + (\"border-color:\" + convertToColorString(nearPointColor) + \";\") + (tooltipModel.get('extraCssText') || '') // If mouse occasionally move over the tooltip, a mouseout event will be\n          // triggered by canvas, and cause some unexpectable result like dragging\n          // stop, \"unfocusAdjacency\". Here `pointer-events: none` is used to solve\n          // it. Although it is not supported by IE8~IE10, fortunately it is a rare\n          // scenario.\n          + (\";pointer-events:\" + (this._enterable ? 'auto' : 'none'));\n        }\n\n        this._show = true;\n        this._firstShow = false;\n        this._longHide = false;\n      };\n\n      TooltipHTMLContent.prototype.setContent = function (content, markers, tooltipModel, borderColor, arrowPosition) {\n        var el = this.el;\n\n        if (content == null) {\n          el.innerHTML = '';\n          return;\n        }\n\n        var arrow = '';\n\n        if (isString(arrowPosition) && tooltipModel.get('trigger') === 'item' && !shouldTooltipConfine(tooltipModel)) {\n          arrow = assembleArrow(tooltipModel, borderColor, arrowPosition);\n        }\n\n        if (isString(content)) {\n          el.innerHTML = content + arrow;\n        } else if (content) {\n          // Clear previous\n          el.innerHTML = '';\n\n          if (!isArray(content)) {\n            content = [content];\n          }\n\n          for (var i = 0; i < content.length; i++) {\n            if (isDom(content[i]) && content[i].parentNode !== el) {\n              el.appendChild(content[i]);\n            }\n          } // no arrow if empty\n\n\n          if (arrow && el.childNodes.length) {\n            // no need to create a new parent element, but it's not supported by IE 10 and older.\n            // const arrowEl = document.createRange().createContextualFragment(arrow);\n            var arrowEl = document.createElement('div');\n            arrowEl.innerHTML = arrow;\n            el.appendChild(arrowEl);\n          }\n        }\n      };\n\n      TooltipHTMLContent.prototype.setEnterable = function (enterable) {\n        this._enterable = enterable;\n      };\n\n      TooltipHTMLContent.prototype.getSize = function () {\n        var el = this.el;\n        return [el.offsetWidth, el.offsetHeight];\n      };\n\n      TooltipHTMLContent.prototype.moveTo = function (zrX, zrY) {\n        var styleCoord = this._styleCoord;\n        makeStyleCoord(styleCoord, this._zr, this._appendToBody, zrX, zrY);\n\n        if (styleCoord[0] != null && styleCoord[1] != null) {\n          var style_1 = this.el.style;\n          var transforms = assembleTransform(styleCoord[0], styleCoord[1]);\n          each(transforms, function (transform) {\n            style_1[transform[0]] = transform[1];\n          });\n        }\n      };\n      /**\n       * when `alwaysShowContent` is true,\n       * move the tooltip after chart resized\n       */\n\n\n      TooltipHTMLContent.prototype._moveIfResized = function () {\n        // The ratio of left to width\n        var ratioX = this._styleCoord[2]; // The ratio of top to height\n\n        var ratioY = this._styleCoord[3];\n        this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight());\n      };\n\n      TooltipHTMLContent.prototype.hide = function () {\n        var _this = this;\n\n        var style = this.el.style;\n        style.visibility = 'hidden';\n        style.opacity = '0';\n        env.transform3dSupported && (style.willChange = '');\n        this._show = false;\n        this._longHideTimeout = setTimeout(function () {\n          return _this._longHide = true;\n        }, 500);\n      };\n\n      TooltipHTMLContent.prototype.hideLater = function (time) {\n        if (this._show && !(this._inContent && this._enterable)) {\n          if (time) {\n            this._hideDelay = time; // Set show false to avoid invoke hideLater multiple times\n\n            this._show = false;\n            this._hideTimeout = setTimeout(bind(this.hide, this), time);\n          } else {\n            this.hide();\n          }\n        }\n      };\n\n      TooltipHTMLContent.prototype.isShow = function () {\n        return this._show;\n      };\n\n      TooltipHTMLContent.prototype.dispose = function () {\n        this.el.parentNode.removeChild(this.el);\n      };\n\n      return TooltipHTMLContent;\n    }();\n\n    var TooltipRichContent =\n    /** @class */\n    function () {\n      function TooltipRichContent(api) {\n        this._show = false;\n        this._styleCoord = [0, 0, 0, 0];\n        this._enterable = true;\n        this._zr = api.getZr();\n        makeStyleCoord$1(this._styleCoord, this._zr, api.getWidth() / 2, api.getHeight() / 2);\n      }\n      /**\n       * Update when tooltip is rendered\n       */\n\n\n      TooltipRichContent.prototype.update = function (tooltipModel) {\n        var alwaysShowContent = tooltipModel.get('alwaysShowContent');\n        alwaysShowContent && this._moveIfResized();\n      };\n\n      TooltipRichContent.prototype.show = function () {\n        if (this._hideTimeout) {\n          clearTimeout(this._hideTimeout);\n        }\n\n        this.el.show();\n        this._show = true;\n      };\n      /**\n       * Set tooltip content\n       */\n\n\n      TooltipRichContent.prototype.setContent = function (content, markupStyleCreator, tooltipModel, borderColor, arrowPosition) {\n        var _this = this;\n\n        if (isObject(content)) {\n          throwError(\"development\" !== 'production' ? 'Passing DOM nodes as content is not supported in richText tooltip!' : '');\n        }\n\n        if (this.el) {\n          this._zr.remove(this.el);\n        }\n\n        var textStyleModel = tooltipModel.getModel('textStyle');\n        this.el = new ZRText({\n          style: {\n            rich: markupStyleCreator.richTextStyles,\n            text: content,\n            lineHeight: 22,\n            borderWidth: 1,\n            borderColor: borderColor,\n            textShadowColor: textStyleModel.get('textShadowColor'),\n            fill: tooltipModel.get(['textStyle', 'color']),\n            padding: getPaddingFromTooltipModel(tooltipModel, 'richText'),\n            verticalAlign: 'top',\n            align: 'left'\n          },\n          z: tooltipModel.get('z')\n        });\n        each(['backgroundColor', 'borderRadius', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'], function (propName) {\n          _this.el.style[propName] = tooltipModel.get(propName);\n        });\n        each(['textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'], function (propName) {\n          _this.el.style[propName] = textStyleModel.get(propName) || 0;\n        });\n\n        this._zr.add(this.el);\n\n        var self = this;\n        this.el.on('mouseover', function () {\n          // clear the timeout in hideLater and keep showing tooltip\n          if (self._enterable) {\n            clearTimeout(self._hideTimeout);\n            self._show = true;\n          }\n\n          self._inContent = true;\n        });\n        this.el.on('mouseout', function () {\n          if (self._enterable) {\n            if (self._show) {\n              self.hideLater(self._hideDelay);\n            }\n          }\n\n          self._inContent = false;\n        });\n      };\n\n      TooltipRichContent.prototype.setEnterable = function (enterable) {\n        this._enterable = enterable;\n      };\n\n      TooltipRichContent.prototype.getSize = function () {\n        var el = this.el;\n        var bounding = this.el.getBoundingRect(); // bounding rect does not include shadow. For renderMode richText,\n        // if overflow, it will be cut. So calculate them accurately.\n\n        var shadowOuterSize = calcShadowOuterSize(el.style);\n        return [bounding.width + shadowOuterSize.left + shadowOuterSize.right, bounding.height + shadowOuterSize.top + shadowOuterSize.bottom];\n      };\n\n      TooltipRichContent.prototype.moveTo = function (x, y) {\n        var el = this.el;\n\n        if (el) {\n          var styleCoord = this._styleCoord;\n          makeStyleCoord$1(styleCoord, this._zr, x, y);\n          x = styleCoord[0];\n          y = styleCoord[1];\n          var style = el.style;\n          var borderWidth = mathMaxWith0(style.borderWidth || 0);\n          var shadowOuterSize = calcShadowOuterSize(style); // rich text x, y do not include border.\n\n          el.x = x + borderWidth + shadowOuterSize.left;\n          el.y = y + borderWidth + shadowOuterSize.top;\n          el.markRedraw();\n        }\n      };\n      /**\n       * when `alwaysShowContent` is true,\n       * move the tooltip after chart resized\n       */\n\n\n      TooltipRichContent.prototype._moveIfResized = function () {\n        // The ratio of left to width\n        var ratioX = this._styleCoord[2]; // The ratio of top to height\n\n        var ratioY = this._styleCoord[3];\n        this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight());\n      };\n\n      TooltipRichContent.prototype.hide = function () {\n        if (this.el) {\n          this.el.hide();\n        }\n\n        this._show = false;\n      };\n\n      TooltipRichContent.prototype.hideLater = function (time) {\n        if (this._show && !(this._inContent && this._enterable)) {\n          if (time) {\n            this._hideDelay = time; // Set show false to avoid invoke hideLater multiple times\n\n            this._show = false;\n            this._hideTimeout = setTimeout(bind(this.hide, this), time);\n          } else {\n            this.hide();\n          }\n        }\n      };\n\n      TooltipRichContent.prototype.isShow = function () {\n        return this._show;\n      };\n\n      TooltipRichContent.prototype.dispose = function () {\n        this._zr.remove(this.el);\n      };\n\n      return TooltipRichContent;\n    }();\n\n    function mathMaxWith0(val) {\n      return Math.max(0, val);\n    }\n\n    function calcShadowOuterSize(style) {\n      var shadowBlur = mathMaxWith0(style.shadowBlur || 0);\n      var shadowOffsetX = mathMaxWith0(style.shadowOffsetX || 0);\n      var shadowOffsetY = mathMaxWith0(style.shadowOffsetY || 0);\n      return {\n        left: mathMaxWith0(shadowBlur - shadowOffsetX),\n        right: mathMaxWith0(shadowBlur + shadowOffsetX),\n        top: mathMaxWith0(shadowBlur - shadowOffsetY),\n        bottom: mathMaxWith0(shadowBlur + shadowOffsetY)\n      };\n    }\n\n    function makeStyleCoord$1(out, zr, zrX, zrY) {\n      out[0] = zrX;\n      out[1] = zrY;\n      out[2] = out[0] / zr.getWidth();\n      out[3] = out[1] / zr.getHeight();\n    }\n\n    var proxyRect = new Rect({\n      shape: {\n        x: -1,\n        y: -1,\n        width: 2,\n        height: 2\n      }\n    });\n\n    var TooltipView =\n    /** @class */\n    function (_super) {\n      __extends(TooltipView, _super);\n\n      function TooltipView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TooltipView.type;\n        return _this;\n      }\n\n      TooltipView.prototype.init = function (ecModel, api) {\n        if (env.node || !api.getDom()) {\n          return;\n        }\n\n        var tooltipModel = ecModel.getComponent('tooltip');\n        var renderMode = this._renderMode = getTooltipRenderMode(tooltipModel.get('renderMode'));\n        this._tooltipContent = renderMode === 'richText' ? new TooltipRichContent(api) : new TooltipHTMLContent(api.getDom(), api, {\n          appendToBody: tooltipModel.get('appendToBody', true)\n        });\n      };\n\n      TooltipView.prototype.render = function (tooltipModel, ecModel, api) {\n        if (env.node || !api.getDom()) {\n          return;\n        } // Reset\n\n\n        this.group.removeAll();\n        this._tooltipModel = tooltipModel;\n        this._ecModel = ecModel;\n        this._api = api;\n        /**\n         * @private\n         * @type {boolean}\n         */\n\n        this._alwaysShowContent = tooltipModel.get('alwaysShowContent');\n        var tooltipContent = this._tooltipContent;\n        tooltipContent.update(tooltipModel);\n        tooltipContent.setEnterable(tooltipModel.get('enterable'));\n\n        this._initGlobalListener();\n\n        this._keepShow(); // PENDING\n        // `mousemove` event will be triggered very frequently when the mouse moves fast,\n        // which causes that the `updatePosition` function was also called frequently.\n        // In Chrome with devtools open and Firefox, tooltip looks laggy and shakes. See #14695 #16101\n        // To avoid frequent triggering,\n        // consider throttling it in 50ms when transition is enabled\n\n\n        if (this._renderMode !== 'richText' && tooltipModel.get('transitionDuration')) {\n          createOrUpdate(this, '_updatePosition', 50, 'fixRate');\n        } else {\n          clear(this, '_updatePosition');\n        }\n      };\n\n      TooltipView.prototype._initGlobalListener = function () {\n        var tooltipModel = this._tooltipModel;\n        var triggerOn = tooltipModel.get('triggerOn');\n        register('itemTooltip', this._api, bind(function (currTrigger, e, dispatchAction) {\n          // If 'none', it is not controlled by mouse totally.\n          if (triggerOn !== 'none') {\n            if (triggerOn.indexOf(currTrigger) >= 0) {\n              this._tryShow(e, dispatchAction);\n            } else if (currTrigger === 'leave') {\n              this._hide(dispatchAction);\n            }\n          }\n        }, this));\n      };\n\n      TooltipView.prototype._keepShow = function () {\n        var tooltipModel = this._tooltipModel;\n        var ecModel = this._ecModel;\n        var api = this._api;\n        var triggerOn = tooltipModel.get('triggerOn'); // Try to keep the tooltip show when refreshing\n\n        if (this._lastX != null && this._lastY != null // When user is willing to control tooltip totally using API,\n        // self.manuallyShowTip({x, y}) might cause tooltip hide,\n        // which is not expected.\n        && triggerOn !== 'none' && triggerOn !== 'click') {\n          var self_1 = this;\n          clearTimeout(this._refreshUpdateTimeout);\n          this._refreshUpdateTimeout = setTimeout(function () {\n            // Show tip next tick after other charts are rendered\n            // In case highlight action has wrong result\n            // FIXME\n            !api.isDisposed() && self_1.manuallyShowTip(tooltipModel, ecModel, api, {\n              x: self_1._lastX,\n              y: self_1._lastY,\n              dataByCoordSys: self_1._lastDataByCoordSys\n            });\n          });\n        }\n      };\n      /**\n       * Show tip manually by\n       * dispatchAction({\n       *     type: 'showTip',\n       *     x: 10,\n       *     y: 10\n       * });\n       * Or\n       * dispatchAction({\n       *      type: 'showTip',\n       *      seriesIndex: 0,\n       *      dataIndex or dataIndexInside or name\n       * });\n       *\n       *  TODO Batch\n       */\n\n\n      TooltipView.prototype.manuallyShowTip = function (tooltipModel, ecModel, api, payload) {\n        if (payload.from === this.uid || env.node || !api.getDom()) {\n          return;\n        }\n\n        var dispatchAction = makeDispatchAction$1(payload, api); // Reset ticket\n\n        this._ticket = ''; // When triggered from axisPointer.\n\n        var dataByCoordSys = payload.dataByCoordSys;\n        var cmptRef = findComponentReference(payload, ecModel, api);\n\n        if (cmptRef) {\n          var rect = cmptRef.el.getBoundingRect().clone();\n          rect.applyTransform(cmptRef.el.transform);\n\n          this._tryShow({\n            offsetX: rect.x + rect.width / 2,\n            offsetY: rect.y + rect.height / 2,\n            target: cmptRef.el,\n            position: payload.position,\n            // When manully trigger, the mouse is not on the el, so we'd better to\n            // position tooltip on the bottom of the el and display arrow is possible.\n            positionDefault: 'bottom'\n          }, dispatchAction);\n        } else if (payload.tooltip && payload.x != null && payload.y != null) {\n          var el = proxyRect;\n          el.x = payload.x;\n          el.y = payload.y;\n          el.update();\n          getECData(el).tooltipConfig = {\n            name: null,\n            option: payload.tooltip\n          }; // Manually show tooltip while view is not using zrender elements.\n\n          this._tryShow({\n            offsetX: payload.x,\n            offsetY: payload.y,\n            target: el\n          }, dispatchAction);\n        } else if (dataByCoordSys) {\n          this._tryShow({\n            offsetX: payload.x,\n            offsetY: payload.y,\n            position: payload.position,\n            dataByCoordSys: dataByCoordSys,\n            tooltipOption: payload.tooltipOption\n          }, dispatchAction);\n        } else if (payload.seriesIndex != null) {\n          if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) {\n            return;\n          }\n\n          var pointInfo = findPointFromSeries(payload, ecModel);\n          var cx = pointInfo.point[0];\n          var cy = pointInfo.point[1];\n\n          if (cx != null && cy != null) {\n            this._tryShow({\n              offsetX: cx,\n              offsetY: cy,\n              target: pointInfo.el,\n              position: payload.position,\n              // When manully trigger, the mouse is not on the el, so we'd better to\n              // position tooltip on the bottom of the el and display arrow is possible.\n              positionDefault: 'bottom'\n            }, dispatchAction);\n          }\n        } else if (payload.x != null && payload.y != null) {\n          // FIXME\n          // should wrap dispatchAction like `axisPointer/globalListener` ?\n          api.dispatchAction({\n            type: 'updateAxisPointer',\n            x: payload.x,\n            y: payload.y\n          });\n\n          this._tryShow({\n            offsetX: payload.x,\n            offsetY: payload.y,\n            position: payload.position,\n            target: api.getZr().findHover(payload.x, payload.y).target\n          }, dispatchAction);\n        }\n      };\n\n      TooltipView.prototype.manuallyHideTip = function (tooltipModel, ecModel, api, payload) {\n        var tooltipContent = this._tooltipContent;\n\n        if (!this._alwaysShowContent && this._tooltipModel) {\n          tooltipContent.hideLater(this._tooltipModel.get('hideDelay'));\n        }\n\n        this._lastX = this._lastY = this._lastDataByCoordSys = null;\n\n        if (payload.from !== this.uid) {\n          this._hide(makeDispatchAction$1(payload, api));\n        }\n      }; // Be compatible with previous design, that is, when tooltip.type is 'axis' and\n      // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer\n      // and tooltip.\n\n\n      TooltipView.prototype._manuallyAxisShowTip = function (tooltipModel, ecModel, api, payload) {\n        var seriesIndex = payload.seriesIndex;\n        var dataIndex = payload.dataIndex; // @ts-ignore\n\n        var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;\n\n        if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {\n          return;\n        }\n\n        var seriesModel = ecModel.getSeriesByIndex(seriesIndex);\n\n        if (!seriesModel) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var tooltipCascadedModel = buildTooltipModel([data.getItemModel(dataIndex), seriesModel, (seriesModel.coordinateSystem || {}).model], this._tooltipModel);\n\n        if (tooltipCascadedModel.get('trigger') !== 'axis') {\n          return;\n        }\n\n        api.dispatchAction({\n          type: 'updateAxisPointer',\n          seriesIndex: seriesIndex,\n          dataIndex: dataIndex,\n          position: payload.position\n        });\n        return true;\n      };\n\n      TooltipView.prototype._tryShow = function (e, dispatchAction) {\n        var el = e.target;\n        var tooltipModel = this._tooltipModel;\n\n        if (!tooltipModel) {\n          return;\n        } // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed\n\n\n        this._lastX = e.offsetX;\n        this._lastY = e.offsetY;\n        var dataByCoordSys = e.dataByCoordSys;\n\n        if (dataByCoordSys && dataByCoordSys.length) {\n          this._showAxisTooltip(dataByCoordSys, e);\n        } else if (el) {\n          this._lastDataByCoordSys = null;\n          var seriesDispatcher_1;\n          var cmptDispatcher_1;\n          findEventDispatcher(el, function (target) {\n            // Always show item tooltip if mouse is on the element with dataIndex\n            if (getECData(target).dataIndex != null) {\n              seriesDispatcher_1 = target;\n              return true;\n            } // Tooltip provided directly. Like legend.\n\n\n            if (getECData(target).tooltipConfig != null) {\n              cmptDispatcher_1 = target;\n              return true;\n            }\n          }, true);\n\n          if (seriesDispatcher_1) {\n            this._showSeriesItemTooltip(e, seriesDispatcher_1, dispatchAction);\n          } else if (cmptDispatcher_1) {\n            this._showComponentItemTooltip(e, cmptDispatcher_1, dispatchAction);\n          } else {\n            this._hide(dispatchAction);\n          }\n        } else {\n          this._lastDataByCoordSys = null;\n\n          this._hide(dispatchAction);\n        }\n      };\n\n      TooltipView.prototype._showOrMove = function (tooltipModel, cb) {\n        // showDelay is used in this case: tooltip.enterable is set\n        // as true. User intent to move mouse into tooltip and click\n        // something. `showDelay` makes it easier to enter the content\n        // but tooltip do not move immediately.\n        var delay = tooltipModel.get('showDelay');\n        cb = bind(cb, this);\n        clearTimeout(this._showTimout);\n        delay > 0 ? this._showTimout = setTimeout(cb, delay) : cb();\n      };\n\n      TooltipView.prototype._showAxisTooltip = function (dataByCoordSys, e) {\n        var ecModel = this._ecModel;\n        var globalTooltipModel = this._tooltipModel;\n        var point = [e.offsetX, e.offsetY];\n        var singleTooltipModel = buildTooltipModel([e.tooltipOption], globalTooltipModel);\n        var renderMode = this._renderMode;\n        var cbParamsList = [];\n        var articleMarkup = createTooltipMarkup('section', {\n          blocks: [],\n          noHeader: true\n        }); // Only for legacy: `Serise['formatTooltip']` returns a string.\n\n        var markupTextArrLegacy = [];\n        var markupStyleCreator = new TooltipMarkupStyleCreator();\n        each(dataByCoordSys, function (itemCoordSys) {\n          each(itemCoordSys.dataByAxis, function (axisItem) {\n            var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex);\n            var axisValue = axisItem.value;\n\n            if (!axisModel || axisValue == null) {\n              return;\n            }\n\n            var axisValueLabel = getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt);\n            var axisSectionMarkup = createTooltipMarkup('section', {\n              header: axisValueLabel,\n              noHeader: !trim(axisValueLabel),\n              sortBlocks: true,\n              blocks: []\n            });\n            articleMarkup.blocks.push(axisSectionMarkup);\n            each(axisItem.seriesDataIndices, function (idxItem) {\n              var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);\n              var dataIndex = idxItem.dataIndexInside;\n              var cbParams = series.getDataParams(dataIndex); // Can't find data.\n\n              if (cbParams.dataIndex < 0) {\n                return;\n              }\n\n              cbParams.axisDim = axisItem.axisDim;\n              cbParams.axisIndex = axisItem.axisIndex;\n              cbParams.axisType = axisItem.axisType;\n              cbParams.axisId = axisItem.axisId;\n              cbParams.axisValue = getAxisRawValue(axisModel.axis, {\n                value: axisValue\n              });\n              cbParams.axisValueLabel = axisValueLabel; // Pre-create marker style for makers. Users can assemble richText\n              // text in `formatter` callback and use those markers style.\n\n              cbParams.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(cbParams.color), renderMode);\n              var seriesTooltipResult = normalizeTooltipFormatResult(series.formatTooltip(dataIndex, true, null));\n              var frag = seriesTooltipResult.frag;\n\n              if (frag) {\n                var valueFormatter = buildTooltipModel([series], globalTooltipModel).get('valueFormatter');\n                axisSectionMarkup.blocks.push(valueFormatter ? extend({\n                  valueFormatter: valueFormatter\n                }, frag) : frag);\n              }\n\n              if (seriesTooltipResult.text) {\n                markupTextArrLegacy.push(seriesTooltipResult.text);\n              }\n\n              cbParamsList.push(cbParams);\n            });\n          });\n        }); // In most cases, the second axis is displays upper on the first one.\n        // So we reverse it to look better.\n\n        articleMarkup.blocks.reverse();\n        markupTextArrLegacy.reverse();\n        var positionExpr = e.position;\n        var orderMode = singleTooltipModel.get('order');\n        var builtMarkupText = buildTooltipMarkup(articleMarkup, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), singleTooltipModel.get('textStyle'));\n        builtMarkupText && markupTextArrLegacy.unshift(builtMarkupText);\n        var blockBreak = renderMode === 'richText' ? '\\n\\n' : '<br/>';\n        var allMarkupText = markupTextArrLegacy.join(blockBreak);\n\n        this._showOrMove(singleTooltipModel, function () {\n          if (this._updateContentNotChangedOnAxis(dataByCoordSys, cbParamsList)) {\n            this._updatePosition(singleTooltipModel, positionExpr, point[0], point[1], this._tooltipContent, cbParamsList);\n          } else {\n            this._showTooltipContent(singleTooltipModel, allMarkupText, cbParamsList, Math.random() + '', point[0], point[1], positionExpr, null, markupStyleCreator);\n          }\n        }); // Do not trigger events here, because this branch only be entered\n        // from dispatchAction.\n\n      };\n\n      TooltipView.prototype._showSeriesItemTooltip = function (e, dispatcher, dispatchAction) {\n        var ecModel = this._ecModel;\n        var ecData = getECData(dispatcher); // Use dataModel in element if possible\n        // Used when mouseover on a element like markPoint or edge\n        // In which case, the data is not main data in series.\n\n        var seriesIndex = ecData.seriesIndex;\n        var seriesModel = ecModel.getSeriesByIndex(seriesIndex); // For example, graph link.\n\n        var dataModel = ecData.dataModel || seriesModel;\n        var dataIndex = ecData.dataIndex;\n        var dataType = ecData.dataType;\n        var data = dataModel.getData(dataType);\n        var renderMode = this._renderMode;\n        var positionDefault = e.positionDefault;\n        var tooltipModel = buildTooltipModel([data.getItemModel(dataIndex), dataModel, seriesModel && (seriesModel.coordinateSystem || {}).model], this._tooltipModel, positionDefault ? {\n          position: positionDefault\n        } : null);\n        var tooltipTrigger = tooltipModel.get('trigger');\n\n        if (tooltipTrigger != null && tooltipTrigger !== 'item') {\n          return;\n        }\n\n        var params = dataModel.getDataParams(dataIndex, dataType);\n        var markupStyleCreator = new TooltipMarkupStyleCreator(); // Pre-create marker style for makers. Users can assemble richText\n        // text in `formatter` callback and use those markers style.\n\n        params.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(params.color), renderMode);\n        var seriesTooltipResult = normalizeTooltipFormatResult(dataModel.formatTooltip(dataIndex, false, dataType));\n        var orderMode = tooltipModel.get('order');\n        var valueFormatter = tooltipModel.get('valueFormatter');\n        var frag = seriesTooltipResult.frag;\n        var markupText = frag ? buildTooltipMarkup(valueFormatter ? extend({\n          valueFormatter: valueFormatter\n        }, frag) : frag, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), tooltipModel.get('textStyle')) : seriesTooltipResult.text;\n        var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;\n\n        this._showOrMove(tooltipModel, function () {\n          this._showTooltipContent(tooltipModel, markupText, params, asyncTicket, e.offsetX, e.offsetY, e.position, e.target, markupStyleCreator);\n        }); // FIXME\n        // duplicated showtip if manuallyShowTip is called from dispatchAction.\n\n\n        dispatchAction({\n          type: 'showTip',\n          dataIndexInside: dataIndex,\n          dataIndex: data.getRawIndex(dataIndex),\n          seriesIndex: seriesIndex,\n          from: this.uid\n        });\n      };\n\n      TooltipView.prototype._showComponentItemTooltip = function (e, el, dispatchAction) {\n        var ecData = getECData(el);\n        var tooltipConfig = ecData.tooltipConfig;\n        var tooltipOpt = tooltipConfig.option || {};\n\n        if (isString(tooltipOpt)) {\n          var content = tooltipOpt;\n          tooltipOpt = {\n            content: content,\n            // Fixed formatter\n            formatter: content\n          };\n        }\n\n        var tooltipModelCascade = [tooltipOpt];\n\n        var cmpt = this._ecModel.getComponent(ecData.componentMainType, ecData.componentIndex);\n\n        if (cmpt) {\n          tooltipModelCascade.push(cmpt);\n        } // In most cases, component tooltip formatter has different params with series tooltip formatter,\n        // so that they can not share the same formatter. Since the global tooltip formatter is used for series\n        // by convension, we do not use it as the default formatter for component.\n\n\n        tooltipModelCascade.push({\n          formatter: tooltipOpt.content\n        });\n        var positionDefault = e.positionDefault;\n        var subTooltipModel = buildTooltipModel(tooltipModelCascade, this._tooltipModel, positionDefault ? {\n          position: positionDefault\n        } : null);\n        var defaultHtml = subTooltipModel.get('content');\n        var asyncTicket = Math.random() + ''; // PENDING: this case do not support richText style yet.\n\n        var markupStyleCreator = new TooltipMarkupStyleCreator(); // Do not check whether `trigger` is 'none' here, because `trigger`\n        // only works on coordinate system. In fact, we have not found case\n        // that requires setting `trigger` nothing on component yet.\n\n        this._showOrMove(subTooltipModel, function () {\n          // Use formatterParams from element defined in component\n          // Avoid users modify it.\n          var formatterParams = clone(subTooltipModel.get('formatterParams') || {});\n\n          this._showTooltipContent(subTooltipModel, defaultHtml, formatterParams, asyncTicket, e.offsetX, e.offsetY, e.position, el, markupStyleCreator);\n        }); // If not dispatch showTip, tip may be hide triggered by axis.\n\n\n        dispatchAction({\n          type: 'showTip',\n          from: this.uid\n        });\n      };\n\n      TooltipView.prototype._showTooltipContent = function ( // Use Model<TooltipOption> insteadof TooltipModel because this model may be from series or other options.\n      // Instead of top level tooltip.\n      tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el, markupStyleCreator) {\n        // Reset ticket\n        this._ticket = '';\n\n        if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) {\n          return;\n        }\n\n        var tooltipContent = this._tooltipContent;\n        tooltipContent.setEnterable(tooltipModel.get('enterable'));\n        var formatter = tooltipModel.get('formatter');\n        positionExpr = positionExpr || tooltipModel.get('position');\n        var html = defaultHtml;\n\n        var nearPoint = this._getNearestPoint([x, y], params, tooltipModel.get('trigger'), tooltipModel.get('borderColor'));\n\n        var nearPointColor = nearPoint.color;\n\n        if (formatter) {\n          if (isString(formatter)) {\n            var useUTC = tooltipModel.ecModel.get('useUTC');\n            var params0 = isArray(params) ? params[0] : params;\n            var isTimeAxis = params0 && params0.axisType && params0.axisType.indexOf('time') >= 0;\n            html = formatter;\n\n            if (isTimeAxis) {\n              html = format(params0.axisValue, html, useUTC);\n            }\n\n            html = formatTpl(html, params, true);\n          } else if (isFunction(formatter)) {\n            var callback = bind(function (cbTicket, html) {\n              if (cbTicket === this._ticket) {\n                tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);\n\n                this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);\n              }\n            }, this);\n            this._ticket = asyncTicket;\n            html = formatter(params, asyncTicket, callback);\n          } else {\n            html = formatter;\n          }\n        }\n\n        tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);\n        tooltipContent.show(tooltipModel, nearPointColor);\n\n        this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);\n      };\n\n      TooltipView.prototype._getNearestPoint = function (point, tooltipDataParams, trigger, borderColor) {\n        if (trigger === 'axis' || isArray(tooltipDataParams)) {\n          return {\n            color: borderColor || (this._renderMode === 'html' ? '#fff' : 'none')\n          };\n        }\n\n        if (!isArray(tooltipDataParams)) {\n          return {\n            color: borderColor || tooltipDataParams.color || tooltipDataParams.borderColor\n          };\n        }\n      };\n\n      TooltipView.prototype._updatePosition = function (tooltipModel, positionExpr, x, // Mouse x\n      y, // Mouse y\n      content, params, el) {\n        var viewWidth = this._api.getWidth();\n\n        var viewHeight = this._api.getHeight();\n\n        positionExpr = positionExpr || tooltipModel.get('position');\n        var contentSize = content.getSize();\n        var align = tooltipModel.get('align');\n        var vAlign = tooltipModel.get('verticalAlign');\n        var rect = el && el.getBoundingRect().clone();\n        el && rect.applyTransform(el.transform);\n\n        if (isFunction(positionExpr)) {\n          // Callback of position can be an array or a string specify the position\n          positionExpr = positionExpr([x, y], params, content.el, rect, {\n            viewSize: [viewWidth, viewHeight],\n            contentSize: contentSize.slice()\n          });\n        }\n\n        if (isArray(positionExpr)) {\n          x = parsePercent$1(positionExpr[0], viewWidth);\n          y = parsePercent$1(positionExpr[1], viewHeight);\n        } else if (isObject(positionExpr)) {\n          var boxLayoutPosition = positionExpr;\n          boxLayoutPosition.width = contentSize[0];\n          boxLayoutPosition.height = contentSize[1];\n          var layoutRect = getLayoutRect(boxLayoutPosition, {\n            width: viewWidth,\n            height: viewHeight\n          });\n          x = layoutRect.x;\n          y = layoutRect.y;\n          align = null; // When positionExpr is left/top/right/bottom,\n          // align and verticalAlign will not work.\n\n          vAlign = null;\n        } // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element\n        else if (isString(positionExpr) && el) {\n            var pos = calcTooltipPosition(positionExpr, rect, contentSize, tooltipModel.get('borderWidth'));\n            x = pos[0];\n            y = pos[1];\n          } else {\n            var pos = refixTooltipPosition(x, y, content, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20);\n            x = pos[0];\n            y = pos[1];\n          }\n\n        align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0);\n        vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);\n\n        if (shouldTooltipConfine(tooltipModel)) {\n          var pos = confineTooltipPosition(x, y, content, viewWidth, viewHeight);\n          x = pos[0];\n          y = pos[1];\n        }\n\n        content.moveTo(x, y);\n      }; // FIXME\n      // Should we remove this but leave this to user?\n\n\n      TooltipView.prototype._updateContentNotChangedOnAxis = function (dataByCoordSys, cbParamsList) {\n        var lastCoordSys = this._lastDataByCoordSys;\n        var lastCbParamsList = this._cbParamsList;\n        var contentNotChanged = !!lastCoordSys && lastCoordSys.length === dataByCoordSys.length;\n        contentNotChanged && each(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {\n          var lastDataByAxis = lastItemCoordSys.dataByAxis || [];\n          var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};\n          var thisDataByAxis = thisItemCoordSys.dataByAxis || [];\n          contentNotChanged = contentNotChanged && lastDataByAxis.length === thisDataByAxis.length;\n          contentNotChanged && each(lastDataByAxis, function (lastItem, indexAxis) {\n            var thisItem = thisDataByAxis[indexAxis] || {};\n            var lastIndices = lastItem.seriesDataIndices || [];\n            var newIndices = thisItem.seriesDataIndices || [];\n            contentNotChanged = contentNotChanged && lastItem.value === thisItem.value && lastItem.axisType === thisItem.axisType && lastItem.axisId === thisItem.axisId && lastIndices.length === newIndices.length;\n            contentNotChanged && each(lastIndices, function (lastIdxItem, j) {\n              var newIdxItem = newIndices[j];\n              contentNotChanged = contentNotChanged && lastIdxItem.seriesIndex === newIdxItem.seriesIndex && lastIdxItem.dataIndex === newIdxItem.dataIndex;\n            }); // check is cbParams data value changed\n\n            lastCbParamsList && each(lastItem.seriesDataIndices, function (idxItem) {\n              var seriesIdx = idxItem.seriesIndex;\n              var cbParams = cbParamsList[seriesIdx];\n              var lastCbParams = lastCbParamsList[seriesIdx];\n\n              if (cbParams && lastCbParams && lastCbParams.data !== cbParams.data) {\n                contentNotChanged = false;\n              }\n            });\n          });\n        });\n        this._lastDataByCoordSys = dataByCoordSys;\n        this._cbParamsList = cbParamsList;\n        return !!contentNotChanged;\n      };\n\n      TooltipView.prototype._hide = function (dispatchAction) {\n        // Do not directly hideLater here, because this behavior may be prevented\n        // in dispatchAction when showTip is dispatched.\n        // FIXME\n        // duplicated hideTip if manuallyHideTip is called from dispatchAction.\n        this._lastDataByCoordSys = null;\n        dispatchAction({\n          type: 'hideTip',\n          from: this.uid\n        });\n      };\n\n      TooltipView.prototype.dispose = function (ecModel, api) {\n        if (env.node || !api.getDom()) {\n          return;\n        }\n\n        clear(this, '_updatePosition');\n\n        this._tooltipContent.dispose();\n\n        unregister('itemTooltip', api);\n      };\n\n      TooltipView.type = 'tooltip';\n      return TooltipView;\n    }(ComponentView);\n    /**\n     * From top to bottom. (the last one should be globalTooltipModel);\n     */\n\n\n    function buildTooltipModel(modelCascade, globalTooltipModel, defaultTooltipOption) {\n      // Last is always tooltip model.\n      var ecModel = globalTooltipModel.ecModel;\n      var resultModel;\n\n      if (defaultTooltipOption) {\n        resultModel = new Model(defaultTooltipOption, ecModel, ecModel);\n        resultModel = new Model(globalTooltipModel.option, resultModel, ecModel);\n      } else {\n        resultModel = globalTooltipModel;\n      }\n\n      for (var i = modelCascade.length - 1; i >= 0; i--) {\n        var tooltipOpt = modelCascade[i];\n\n        if (tooltipOpt) {\n          if (tooltipOpt instanceof Model) {\n            tooltipOpt = tooltipOpt.get('tooltip', true);\n          } // In each data item tooltip can be simply write:\n          // {\n          //  value: 10,\n          //  tooltip: 'Something you need to know'\n          // }\n\n\n          if (isString(tooltipOpt)) {\n            tooltipOpt = {\n              formatter: tooltipOpt\n            };\n          }\n\n          if (tooltipOpt) {\n            resultModel = new Model(tooltipOpt, resultModel, ecModel);\n          }\n        }\n      }\n\n      return resultModel;\n    }\n\n    function makeDispatchAction$1(payload, api) {\n      return payload.dispatchAction || bind(api.dispatchAction, api);\n    }\n\n    function refixTooltipPosition(x, y, content, viewWidth, viewHeight, gapH, gapV) {\n      var size = content.getSize();\n      var width = size[0];\n      var height = size[1];\n\n      if (gapH != null) {\n        // Add extra 2 pixels for this case:\n        // At present the \"values\" in defaut tooltip are using CSS `float: right`.\n        // When the right edge of the tooltip box is on the right side of the\n        // viewport, the `float` layout might push the \"values\" to the second line.\n        if (x + width + gapH + 2 > viewWidth) {\n          x -= width + gapH;\n        } else {\n          x += gapH;\n        }\n      }\n\n      if (gapV != null) {\n        if (y + height + gapV > viewHeight) {\n          y -= height + gapV;\n        } else {\n          y += gapV;\n        }\n      }\n\n      return [x, y];\n    }\n\n    function confineTooltipPosition(x, y, content, viewWidth, viewHeight) {\n      var size = content.getSize();\n      var width = size[0];\n      var height = size[1];\n      x = Math.min(x + width, viewWidth) - width;\n      y = Math.min(y + height, viewHeight) - height;\n      x = Math.max(x, 0);\n      y = Math.max(y, 0);\n      return [x, y];\n    }\n\n    function calcTooltipPosition(position, rect, contentSize, borderWidth) {\n      var domWidth = contentSize[0];\n      var domHeight = contentSize[1];\n      var offset = Math.ceil(Math.SQRT2 * borderWidth) + 8;\n      var x = 0;\n      var y = 0;\n      var rectWidth = rect.width;\n      var rectHeight = rect.height;\n\n      switch (position) {\n        case 'inside':\n          x = rect.x + rectWidth / 2 - domWidth / 2;\n          y = rect.y + rectHeight / 2 - domHeight / 2;\n          break;\n\n        case 'top':\n          x = rect.x + rectWidth / 2 - domWidth / 2;\n          y = rect.y - domHeight - offset;\n          break;\n\n        case 'bottom':\n          x = rect.x + rectWidth / 2 - domWidth / 2;\n          y = rect.y + rectHeight + offset;\n          break;\n\n        case 'left':\n          x = rect.x - domWidth - offset;\n          y = rect.y + rectHeight / 2 - domHeight / 2;\n          break;\n\n        case 'right':\n          x = rect.x + rectWidth + offset;\n          y = rect.y + rectHeight / 2 - domHeight / 2;\n      }\n\n      return [x, y];\n    }\n\n    function isCenterAlign(align) {\n      return align === 'center' || align === 'middle';\n    }\n    /**\n     * Find target component by payload like:\n     * ```js\n     * { legendId: 'some_id', name: 'xxx' }\n     * { toolboxIndex: 1, name: 'xxx' }\n     * { geoName: 'some_name', name: 'xxx' }\n     * ```\n     * PENDING: at present only\n     *\n     * If not found, return null/undefined.\n     */\n\n\n    function findComponentReference(payload, ecModel, api) {\n      var queryOptionMap = preParseFinder(payload).queryOptionMap;\n      var componentMainType = queryOptionMap.keys()[0];\n\n      if (!componentMainType || componentMainType === 'series') {\n        return;\n      }\n\n      var queryResult = queryReferringComponents(ecModel, componentMainType, queryOptionMap.get(componentMainType), {\n        useDefault: false,\n        enableAll: false,\n        enableNone: false\n      });\n      var model = queryResult.models[0];\n\n      if (!model) {\n        return;\n      }\n\n      var view = api.getViewOfComponentModel(model);\n      var el;\n      view.group.traverse(function (subEl) {\n        var tooltipConfig = getECData(subEl).tooltipConfig;\n\n        if (tooltipConfig && tooltipConfig.name === payload.name) {\n          el = subEl;\n          return true; // stop\n        }\n      });\n\n      if (el) {\n        return {\n          componentMainType: componentMainType,\n          componentIndex: model.componentIndex,\n          el: el\n        };\n      }\n    }\n\n    function install$A(registers) {\n      use(install$s);\n      registers.registerComponentModel(TooltipModel);\n      registers.registerComponentView(TooltipView);\n      /**\n       * @action\n       * @property {string} type\n       * @property {number} seriesIndex\n       * @property {number} dataIndex\n       * @property {number} [x]\n       * @property {number} [y]\n       */\n\n      registers.registerAction({\n        type: 'showTip',\n        event: 'showTip',\n        update: 'tooltip:manuallyShowTip'\n      }, noop);\n      registers.registerAction({\n        type: 'hideTip',\n        event: 'hideTip',\n        update: 'tooltip:manuallyHideTip'\n      }, noop);\n    }\n\n    var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear'];\n    function brushPreprocessor(option, isNew) {\n      var brushComponents = normalizeToArray(option ? option.brush : []);\n\n      if (!brushComponents.length) {\n        return;\n      }\n\n      var brushComponentSpecifiedBtns = [];\n      each(brushComponents, function (brushOpt) {\n        var tbs = brushOpt.hasOwnProperty('toolbox') ? brushOpt.toolbox : [];\n\n        if (tbs instanceof Array) {\n          brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs);\n        }\n      });\n      var toolbox = option && option.toolbox;\n\n      if (isArray(toolbox)) {\n        toolbox = toolbox[0];\n      }\n\n      if (!toolbox) {\n        toolbox = {\n          feature: {}\n        };\n        option.toolbox = [toolbox];\n      }\n\n      var toolboxFeature = toolbox.feature || (toolbox.feature = {});\n      var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {});\n      var brushTypes = toolboxBrush.type || (toolboxBrush.type = []);\n      brushTypes.push.apply(brushTypes, brushComponentSpecifiedBtns);\n      removeDuplicate(brushTypes);\n\n      if (isNew && !brushTypes.length) {\n        brushTypes.push.apply(brushTypes, DEFAULT_TOOLBOX_BTNS);\n      }\n    }\n\n    function removeDuplicate(arr) {\n      var map = {};\n      each(arr, function (val) {\n        map[val] = 1;\n      });\n      arr.length = 0;\n      each(map, function (flag, val) {\n        arr.push(val);\n      });\n    }\n\n    var each$b = each;\n\n    function hasKeys(obj) {\n      if (obj) {\n        for (var name_1 in obj) {\n          if (obj.hasOwnProperty(name_1)) {\n            return true;\n          }\n        }\n      }\n    }\n\n    function createVisualMappings(option, stateList, supplementVisualOption) {\n      var visualMappings = {};\n      each$b(stateList, function (state) {\n        var mappings = visualMappings[state] = createMappings();\n        each$b(option[state], function (visualData, visualType) {\n          if (!VisualMapping.isValidType(visualType)) {\n            return;\n          }\n\n          var mappingOption = {\n            type: visualType,\n            visual: visualData\n          };\n          supplementVisualOption && supplementVisualOption(mappingOption, state);\n          mappings[visualType] = new VisualMapping(mappingOption); // Prepare a alpha for opacity, for some case that opacity\n          // is not supported, such as rendering using gradient color.\n\n          if (visualType === 'opacity') {\n            mappingOption = clone(mappingOption);\n            mappingOption.type = 'colorAlpha';\n            mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption);\n          }\n        });\n      });\n      return visualMappings;\n\n      function createMappings() {\n        var Creater = function () {}; // Make sure hidden fields will not be visited by\n        // object iteration (with hasOwnProperty checking).\n\n\n        Creater.prototype.__hidden = Creater.prototype;\n        var obj = new Creater();\n        return obj;\n      }\n    }\n    function replaceVisualOption(thisOption, newOption, keys) {\n      // Visual attributes merge is not supported, otherwise it\n      // brings overcomplicated merge logic. See #2853. So if\n      // newOption has anyone of these keys, all of these keys\n      // will be reset. Otherwise, all keys remain.\n      var has;\n      each(keys, function (key) {\n        if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) {\n          has = true;\n        }\n      });\n      has && each(keys, function (key) {\n        if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) {\n          thisOption[key] = clone(newOption[key]);\n        } else {\n          delete thisOption[key];\n        }\n      });\n    }\n    /**\n     * @param stateList\n     * @param visualMappings\n     * @param list\n     * @param getValueState param: valueOrIndex, return: state.\n     * @param scope Scope for getValueState\n     * @param dimension Concrete dimension, if used.\n     */\n    // ???! handle brush?\n\n    function applyVisual(stateList, visualMappings, data, getValueState, scope, dimension) {\n      var visualTypesMap = {};\n      each(stateList, function (state) {\n        var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]);\n        visualTypesMap[state] = visualTypes;\n      });\n      var dataIndex;\n\n      function getVisual(key) {\n        return getItemVisualFromData(data, dataIndex, key);\n      }\n\n      function setVisual(key, value) {\n        setItemVisualFromData(data, dataIndex, key, value);\n      }\n\n      if (dimension == null) {\n        data.each(eachItem);\n      } else {\n        data.each([dimension], eachItem);\n      }\n\n      function eachItem(valueOrIndex, index) {\n        dataIndex = dimension == null ? valueOrIndex // First argument is index\n        : index;\n        var rawDataItem = data.getRawDataItem(dataIndex); // Consider performance\n        // @ts-ignore\n\n        if (rawDataItem && rawDataItem.visualMap === false) {\n          return;\n        }\n\n        var valueState = getValueState.call(scope, valueOrIndex);\n        var mappings = visualMappings[valueState];\n        var visualTypes = visualTypesMap[valueState];\n\n        for (var i = 0, len = visualTypes.length; i < len; i++) {\n          var type = visualTypes[i];\n          mappings[type] && mappings[type].applyVisual(valueOrIndex, getVisual, setVisual);\n        }\n      }\n    }\n    /**\n     * @param data\n     * @param stateList\n     * @param visualMappings <state, Object.<visualType, module:echarts/visual/VisualMapping>>\n     * @param getValueState param: valueOrIndex, return: state.\n     * @param dim dimension or dimension index.\n     */\n\n    function incrementalApplyVisual(stateList, visualMappings, getValueState, dim) {\n      var visualTypesMap = {};\n      each(stateList, function (state) {\n        var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]);\n        visualTypesMap[state] = visualTypes;\n      });\n      return {\n        progress: function progress(params, data) {\n          var dimIndex;\n\n          if (dim != null) {\n            dimIndex = data.getDimensionIndex(dim);\n          }\n\n          function getVisual(key) {\n            return getItemVisualFromData(data, dataIndex, key);\n          }\n\n          function setVisual(key, value) {\n            setItemVisualFromData(data, dataIndex, key, value);\n          }\n\n          var dataIndex;\n          var store = data.getStore();\n\n          while ((dataIndex = params.next()) != null) {\n            var rawDataItem = data.getRawDataItem(dataIndex); // Consider performance\n            // @ts-ignore\n\n            if (rawDataItem && rawDataItem.visualMap === false) {\n              continue;\n            }\n\n            var value = dim != null ? store.get(dimIndex, dataIndex) : dataIndex;\n            var valueState = getValueState(value);\n            var mappings = visualMappings[valueState];\n            var visualTypes = visualTypesMap[valueState];\n\n            for (var i = 0, len = visualTypes.length; i < len; i++) {\n              var type = visualTypes[i];\n              mappings[type] && mappings[type].applyVisual(value, getVisual, setVisual);\n            }\n          }\n        }\n      };\n    }\n\n    function makeBrushCommonSelectorForSeries(area) {\n      var brushType = area.brushType; // Do not use function binding or curry for performance.\n\n      var selectors = {\n        point: function (itemLayout) {\n          return selector[brushType].point(itemLayout, selectors, area);\n        },\n        rect: function (itemLayout) {\n          return selector[brushType].rect(itemLayout, selectors, area);\n        }\n      };\n      return selectors;\n    }\n    var selector = {\n      lineX: getLineSelectors(0),\n      lineY: getLineSelectors(1),\n      rect: {\n        point: function (itemLayout, selectors, area) {\n          return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]);\n        },\n        rect: function (itemLayout, selectors, area) {\n          return itemLayout && area.boundingRect.intersect(itemLayout);\n        }\n      },\n      polygon: {\n        point: function (itemLayout, selectors, area) {\n          return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]) && contain$2(area.range, itemLayout[0], itemLayout[1]);\n        },\n        rect: function (itemLayout, selectors, area) {\n          var points = area.range;\n\n          if (!itemLayout || points.length <= 1) {\n            return false;\n          }\n\n          var x = itemLayout.x;\n          var y = itemLayout.y;\n          var width = itemLayout.width;\n          var height = itemLayout.height;\n          var p = points[0];\n\n          if (contain$2(points, x, y) || contain$2(points, x + width, y) || contain$2(points, x, y + height) || contain$2(points, x + width, y + height) || BoundingRect.create(itemLayout).contain(p[0], p[1]) || linePolygonIntersect(x, y, x + width, y, points) || linePolygonIntersect(x, y, x, y + height, points) || linePolygonIntersect(x + width, y, x + width, y + height, points) || linePolygonIntersect(x, y + height, x + width, y + height, points)) {\n            return true;\n          }\n        }\n      }\n    };\n\n    function getLineSelectors(xyIndex) {\n      var xy = ['x', 'y'];\n      var wh = ['width', 'height'];\n      return {\n        point: function (itemLayout, selectors, area) {\n          if (itemLayout) {\n            var range = area.range;\n            var p = itemLayout[xyIndex];\n            return inLineRange(p, range);\n          }\n        },\n        rect: function (itemLayout, selectors, area) {\n          if (itemLayout) {\n            var range = area.range;\n            var layoutRange = [itemLayout[xy[xyIndex]], itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]]];\n            layoutRange[1] < layoutRange[0] && layoutRange.reverse();\n            return inLineRange(layoutRange[0], range) || inLineRange(layoutRange[1], range) || inLineRange(range[0], layoutRange) || inLineRange(range[1], layoutRange);\n          }\n        }\n      };\n    }\n\n    function inLineRange(p, range) {\n      return range[0] <= p && p <= range[1];\n    }\n\n    var STATE_LIST = ['inBrush', 'outOfBrush'];\n    var DISPATCH_METHOD = '__ecBrushSelect';\n    var DISPATCH_FLAG = '__ecInBrushSelectEvent';\n    function layoutCovers(ecModel) {\n      ecModel.eachComponent({\n        mainType: 'brush'\n      }, function (brushModel) {\n        var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel);\n        brushTargetManager.setInputRanges(brushModel.areas, ecModel);\n      });\n    }\n    /**\n     * Register the visual encoding if this modules required.\n     */\n\n    function brushVisual(ecModel, api, payload) {\n      var brushSelected = [];\n      var throttleType;\n      var throttleDelay;\n      ecModel.eachComponent({\n        mainType: 'brush'\n      }, function (brushModel) {\n        payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption(payload.key === 'brush' ? payload.brushOption : {\n          brushType: false\n        });\n      });\n      layoutCovers(ecModel);\n      ecModel.eachComponent({\n        mainType: 'brush'\n      }, function (brushModel, brushIndex) {\n        var thisBrushSelected = {\n          brushId: brushModel.id,\n          brushIndex: brushIndex,\n          brushName: brushModel.name,\n          areas: clone(brushModel.areas),\n          selected: []\n        }; // Every brush component exists in event params, convenient\n        // for user to find by index.\n\n        brushSelected.push(thisBrushSelected);\n        var brushOption = brushModel.option;\n        var brushLink = brushOption.brushLink;\n        var linkedSeriesMap = [];\n        var selectedDataIndexForLink = [];\n        var rangeInfoBySeries = [];\n        var hasBrushExists = false;\n\n        if (!brushIndex) {\n          // Only the first throttle setting works.\n          throttleType = brushOption.throttleType;\n          throttleDelay = brushOption.throttleDelay;\n        } // Add boundingRect and selectors to range.\n\n\n        var areas = map(brushModel.areas, function (area) {\n          var builder = boundingRectBuilders[area.brushType];\n          var selectableArea = defaults({\n            boundingRect: builder ? builder(area) : void 0\n          }, area);\n          selectableArea.selectors = makeBrushCommonSelectorForSeries(selectableArea);\n          return selectableArea;\n        });\n        var visualMappings = createVisualMappings(brushModel.option, STATE_LIST, function (mappingOption) {\n          mappingOption.mappingMethod = 'fixed';\n        });\n        isArray(brushLink) && each(brushLink, function (seriesIndex) {\n          linkedSeriesMap[seriesIndex] = 1;\n        });\n\n        function linkOthers(seriesIndex) {\n          return brushLink === 'all' || !!linkedSeriesMap[seriesIndex];\n        } // If no supported brush or no brush on the series,\n        // all visuals should be in original state.\n\n\n        function brushed(rangeInfoList) {\n          return !!rangeInfoList.length;\n        }\n        /**\n         * Logic for each series: (If the logic has to be modified one day, do it carefully!)\n         *\n         * ( brushed ┬ && ┬hasBrushExist ┬ && linkOthers  ) => StepA: ┬record, ┬ StepB: ┬visualByRecord.\n         *   !brushed┘    ├hasBrushExist ┤                            └nothing,┘        ├visualByRecord.\n         *                └!hasBrushExist┘                                              └nothing.\n         * ( !brushed  && ┬hasBrushExist ┬ && linkOthers  ) => StepA:  nothing,  StepB: ┬visualByRecord.\n         *                └!hasBrushExist┘                                              └nothing.\n         * ( brushed ┬ &&                     !linkOthers ) => StepA:  nothing,  StepB: ┬visualByCheck.\n         *   !brushed┘                                                                  └nothing.\n         * ( !brushed  &&                     !linkOthers ) => StepA:  nothing,  StepB:  nothing.\n         */\n        // Step A\n\n\n        ecModel.eachSeries(function (seriesModel, seriesIndex) {\n          var rangeInfoList = rangeInfoBySeries[seriesIndex] = [];\n          seriesModel.subType === 'parallel' ? stepAParallel(seriesModel, seriesIndex) : stepAOthers(seriesModel, seriesIndex, rangeInfoList);\n        });\n\n        function stepAParallel(seriesModel, seriesIndex) {\n          var coordSys = seriesModel.coordinateSystem;\n          hasBrushExists = hasBrushExists || coordSys.hasAxisBrushed();\n          linkOthers(seriesIndex) && coordSys.eachActiveState(seriesModel.getData(), function (activeState, dataIndex) {\n            activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1);\n          });\n        }\n\n        function stepAOthers(seriesModel, seriesIndex, rangeInfoList) {\n          if (!seriesModel.brushSelector || brushModelNotControll(brushModel, seriesIndex)) {\n            return;\n          }\n\n          each(areas, function (area) {\n            if (brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel)) {\n              rangeInfoList.push(area);\n            }\n\n            hasBrushExists = hasBrushExists || brushed(rangeInfoList);\n          });\n\n          if (linkOthers(seriesIndex) && brushed(rangeInfoList)) {\n            var data_1 = seriesModel.getData();\n            data_1.each(function (dataIndex) {\n              if (checkInRange(seriesModel, rangeInfoList, data_1, dataIndex)) {\n                selectedDataIndexForLink[dataIndex] = 1;\n              }\n            });\n          }\n        } // Step B\n\n\n        ecModel.eachSeries(function (seriesModel, seriesIndex) {\n          var seriesBrushSelected = {\n            seriesId: seriesModel.id,\n            seriesIndex: seriesIndex,\n            seriesName: seriesModel.name,\n            dataIndex: []\n          }; // Every series exists in event params, convenient\n          // for user to find series by seriesIndex.\n\n          thisBrushSelected.selected.push(seriesBrushSelected);\n          var rangeInfoList = rangeInfoBySeries[seriesIndex];\n          var data = seriesModel.getData();\n          var getValueState = linkOthers(seriesIndex) ? function (dataIndex) {\n            return selectedDataIndexForLink[dataIndex] ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush';\n          } : function (dataIndex) {\n            return checkInRange(seriesModel, rangeInfoList, data, dataIndex) ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush';\n          }; // If no supported brush or no brush, all visuals are in original state.\n\n          (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList)) && applyVisual(STATE_LIST, visualMappings, data, getValueState);\n        });\n      });\n      dispatchAction(api, throttleType, throttleDelay, brushSelected, payload);\n    }\n\n    function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) {\n      // This event will not be triggered when `setOpion`, otherwise dead lock may\n      // triggered when do `setOption` in event listener, which we do not find\n      // satisfactory way to solve yet. Some considered resolutions:\n      // (a) Diff with prevoius selected data ant only trigger event when changed.\n      // But store previous data and diff precisely (i.e., not only by dataIndex, but\n      // also detect value changes in selected data) might bring complexity or fragility.\n      // (b) Use spectial param like `silent` to suppress event triggering.\n      // But such kind of volatile param may be weird in `setOption`.\n      if (!payload) {\n        return;\n      }\n\n      var zr = api.getZr();\n\n      if (zr[DISPATCH_FLAG]) {\n        return;\n      }\n\n      if (!zr[DISPATCH_METHOD]) {\n        zr[DISPATCH_METHOD] = doDispatch;\n      }\n\n      var fn = createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType);\n      fn(api, brushSelected);\n    }\n\n    function doDispatch(api, brushSelected) {\n      if (!api.isDisposed()) {\n        var zr = api.getZr();\n        zr[DISPATCH_FLAG] = true;\n        api.dispatchAction({\n          type: 'brushSelect',\n          batch: brushSelected\n        });\n        zr[DISPATCH_FLAG] = false;\n      }\n    }\n\n    function checkInRange(seriesModel, rangeInfoList, data, dataIndex) {\n      for (var i = 0, len = rangeInfoList.length; i < len; i++) {\n        var area = rangeInfoList[i];\n\n        if (seriesModel.brushSelector(dataIndex, data, area.selectors, area)) {\n          return true;\n        }\n      }\n    }\n\n    function brushModelNotControll(brushModel, seriesIndex) {\n      var seriesIndices = brushModel.option.seriesIndex;\n      return seriesIndices != null && seriesIndices !== 'all' && (isArray(seriesIndices) ? indexOf(seriesIndices, seriesIndex) < 0 : seriesIndex !== seriesIndices);\n    }\n\n    var boundingRectBuilders = {\n      rect: function (area) {\n        return getBoundingRectFromMinMax(area.range);\n      },\n      polygon: function (area) {\n        var minMax;\n        var range = area.range;\n\n        for (var i = 0, len = range.length; i < len; i++) {\n          minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]];\n          var rg = range[i];\n          rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]);\n          rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]);\n          rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]);\n          rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]);\n        }\n\n        return minMax && getBoundingRectFromMinMax(minMax);\n      }\n    };\n\n    function getBoundingRectFromMinMax(minMax) {\n      return new BoundingRect(minMax[0][0], minMax[1][0], minMax[0][1] - minMax[0][0], minMax[1][1] - minMax[1][0]);\n    }\n\n    var BrushView =\n    /** @class */\n    function (_super) {\n      __extends(BrushView, _super);\n\n      function BrushView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BrushView.type;\n        return _this;\n      }\n\n      BrushView.prototype.init = function (ecModel, api) {\n        this.ecModel = ecModel;\n        this.api = api;\n        this.model;\n        (this._brushController = new BrushController(api.getZr())).on('brush', bind(this._onBrush, this)).mount();\n      };\n\n      BrushView.prototype.render = function (brushModel, ecModel, api, payload) {\n        this.model = brushModel;\n\n        this._updateController(brushModel, ecModel, api, payload);\n      };\n\n      BrushView.prototype.updateTransform = function (brushModel, ecModel, api, payload) {\n        // PENDING: `updateTransform` is a little tricky, whose layout need\n        // to be calculate mandatorily and other stages will not be performed.\n        // Take care the correctness of the logic. See #11754 .\n        layoutCovers(ecModel);\n\n        this._updateController(brushModel, ecModel, api, payload);\n      };\n\n      BrushView.prototype.updateVisual = function (brushModel, ecModel, api, payload) {\n        this.updateTransform(brushModel, ecModel, api, payload);\n      };\n\n      BrushView.prototype.updateView = function (brushModel, ecModel, api, payload) {\n        this._updateController(brushModel, ecModel, api, payload);\n      };\n\n      BrushView.prototype._updateController = function (brushModel, ecModel, api, payload) {\n        // Do not update controller when drawing.\n        (!payload || payload.$from !== brushModel.id) && this._brushController.setPanels(brushModel.brushTargetManager.makePanelOpts(api)).enableBrush(brushModel.brushOption).updateCovers(brushModel.areas.slice());\n      }; // updateLayout: updateController,\n      // updateVisual: updateController,\n\n\n      BrushView.prototype.dispose = function () {\n        this._brushController.dispose();\n      };\n\n      BrushView.prototype._onBrush = function (eventParam) {\n        var modelId = this.model.id;\n        var areas = this.model.brushTargetManager.setOutputRanges(eventParam.areas, this.ecModel); // Action is not dispatched on drag end, because the drag end\n        // emits the same params with the last drag move event, and\n        // may have some delay when using touch pad, which makes\n        // animation not smooth (when using debounce).\n\n        (!eventParam.isEnd || eventParam.removeOnClick) && this.api.dispatchAction({\n          type: 'brush',\n          brushId: modelId,\n          areas: clone(areas),\n          $from: modelId\n        });\n        eventParam.isEnd && this.api.dispatchAction({\n          type: 'brushEnd',\n          brushId: modelId,\n          areas: clone(areas),\n          $from: modelId\n        });\n      };\n\n      BrushView.type = 'brush';\n      return BrushView;\n    }(ComponentView);\n\n    var DEFAULT_OUT_OF_BRUSH_COLOR = '#ddd';\n\n    var BrushModel =\n    /** @class */\n    function (_super) {\n      __extends(BrushModel, _super);\n\n      function BrushModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BrushModel.type;\n        /**\n         * @readOnly\n         */\n\n        _this.areas = [];\n        /**\n         * Current brush painting area settings.\n         * @readOnly\n         */\n\n        _this.brushOption = {};\n        return _this;\n      }\n\n      BrushModel.prototype.optionUpdated = function (newOption, isInit) {\n        var thisOption = this.option;\n        !isInit && replaceVisualOption(thisOption, newOption, ['inBrush', 'outOfBrush']);\n        var inBrush = thisOption.inBrush = thisOption.inBrush || {}; // Always give default visual, consider setOption at the second time.\n\n        thisOption.outOfBrush = thisOption.outOfBrush || {\n          color: DEFAULT_OUT_OF_BRUSH_COLOR\n        };\n\n        if (!inBrush.hasOwnProperty('liftZ')) {\n          // Bigger than the highlight z lift, otherwise it will\n          // be effected by the highlight z when brush.\n          inBrush.liftZ = 5;\n        }\n      };\n      /**\n       * If `areas` is null/undefined, range state remain.\n       */\n\n\n      BrushModel.prototype.setAreas = function (areas) {\n        if (\"development\" !== 'production') {\n          assert(isArray(areas));\n          each(areas, function (area) {\n            assert(area.brushType, 'Illegal areas');\n          });\n        } // If areas is null/undefined, range state remain.\n        // This helps user to dispatchAction({type: 'brush'}) with no areas\n        // set but just want to get the current brush select info from a `brush` event.\n\n\n        if (!areas) {\n          return;\n        }\n\n        this.areas = map(areas, function (area) {\n          return generateBrushOption(this.option, area);\n        }, this);\n      };\n      /**\n       * Set the current painting brush option.\n       */\n\n\n      BrushModel.prototype.setBrushOption = function (brushOption) {\n        this.brushOption = generateBrushOption(this.option, brushOption);\n        this.brushType = this.brushOption.brushType;\n      };\n\n      BrushModel.type = 'brush';\n      BrushModel.dependencies = ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'];\n      BrushModel.defaultOption = {\n        seriesIndex: 'all',\n        brushType: 'rect',\n        brushMode: 'single',\n        transformable: true,\n        brushStyle: {\n          borderWidth: 1,\n          color: 'rgba(210,219,238,0.3)',\n          borderColor: '#D2DBEE'\n        },\n        throttleType: 'fixRate',\n        throttleDelay: 0,\n        removeOnClick: true,\n        z: 10000\n      };\n      return BrushModel;\n    }(ComponentModel);\n\n    function generateBrushOption(option, brushOption) {\n      return merge({\n        brushType: option.brushType,\n        brushMode: option.brushMode,\n        transformable: option.transformable,\n        brushStyle: new Model(option.brushStyle).getItemStyle(),\n        removeOnClick: option.removeOnClick,\n        z: option.z\n      }, brushOption, true);\n    }\n\n    var ICON_TYPES = ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'];\n\n    var BrushFeature =\n    /** @class */\n    function (_super) {\n      __extends(BrushFeature, _super);\n\n      function BrushFeature() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      BrushFeature.prototype.render = function (featureModel, ecModel, api) {\n        var brushType;\n        var brushMode;\n        var isBrushed;\n        ecModel.eachComponent({\n          mainType: 'brush'\n        }, function (brushModel) {\n          brushType = brushModel.brushType;\n          brushMode = brushModel.brushOption.brushMode || 'single';\n          isBrushed = isBrushed || !!brushModel.areas.length;\n        });\n        this._brushType = brushType;\n        this._brushMode = brushMode;\n        each(featureModel.get('type', true), function (type) {\n          featureModel.setIconStatus(type, (type === 'keep' ? brushMode === 'multiple' : type === 'clear' ? isBrushed : type === brushType) ? 'emphasis' : 'normal');\n        });\n      };\n\n      BrushFeature.prototype.updateView = function (featureModel, ecModel, api) {\n        this.render(featureModel, ecModel, api);\n      };\n\n      BrushFeature.prototype.getIcons = function () {\n        var model = this.model;\n        var availableIcons = model.get('icon', true);\n        var icons = {};\n        each(model.get('type', true), function (type) {\n          if (availableIcons[type]) {\n            icons[type] = availableIcons[type];\n          }\n        });\n        return icons;\n      };\n\n      BrushFeature.prototype.onclick = function (ecModel, api, type) {\n        var brushType = this._brushType;\n        var brushMode = this._brushMode;\n\n        if (type === 'clear') {\n          // Trigger parallel action firstly\n          api.dispatchAction({\n            type: 'axisAreaSelect',\n            intervals: []\n          });\n          api.dispatchAction({\n            type: 'brush',\n            command: 'clear',\n            // Clear all areas of all brush components.\n            areas: []\n          });\n        } else {\n          api.dispatchAction({\n            type: 'takeGlobalCursor',\n            key: 'brush',\n            brushOption: {\n              brushType: type === 'keep' ? brushType : brushType === type ? false : type,\n              brushMode: type === 'keep' ? brushMode === 'multiple' ? 'single' : 'multiple' : brushMode\n            }\n          });\n        }\n      };\n\n      BrushFeature.getDefaultOption = function (ecModel) {\n        var defaultOption = {\n          show: true,\n          type: ICON_TYPES.slice(),\n          icon: {\n            /* eslint-disable */\n            rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13',\n            polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2',\n            lineX: 'M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4',\n            lineY: 'M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4',\n            keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z',\n            clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2' // jshint ignore:line\n\n            /* eslint-enable */\n\n          },\n          // `rect`, `polygon`, `lineX`, `lineY`, `keep`, `clear`\n          title: ecModel.getLocaleModel().get(['toolbox', 'brush', 'title'])\n        };\n        return defaultOption;\n      };\n\n      return BrushFeature;\n    }(ToolboxFeature);\n\n    function install$B(registers) {\n      registers.registerComponentView(BrushView);\n      registers.registerComponentModel(BrushModel);\n      registers.registerPreprocessor(brushPreprocessor);\n      registers.registerVisual(registers.PRIORITY.VISUAL.BRUSH, brushVisual);\n      registers.registerAction({\n        type: 'brush',\n        event: 'brush',\n        update: 'updateVisual'\n      }, function (payload, ecModel) {\n        ecModel.eachComponent({\n          mainType: 'brush',\n          query: payload\n        }, function (brushModel) {\n          brushModel.setAreas(payload.areas);\n        });\n      });\n      /**\n       * payload: {\n       *      brushComponents: [\n       *          {\n       *              brushId,\n       *              brushIndex,\n       *              brushName,\n       *              series: [\n       *                  {\n       *                      seriesId,\n       *                      seriesIndex,\n       *                      seriesName,\n       *                      rawIndices: [21, 34, ...]\n       *                  },\n       *                  ...\n       *              ]\n       *          },\n       *          ...\n       *      ]\n       * }\n       */\n\n      registers.registerAction({\n        type: 'brushSelect',\n        event: 'brushSelected',\n        update: 'none'\n      }, noop);\n      registers.registerAction({\n        type: 'brushEnd',\n        event: 'brushEnd',\n        update: 'none'\n      }, noop);\n      registerFeature('brush', BrushFeature);\n    }\n\n    var TitleModel =\n    /** @class */\n    function (_super) {\n      __extends(TitleModel, _super);\n\n      function TitleModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TitleModel.type;\n        _this.layoutMode = {\n          type: 'box',\n          ignoreSize: true\n        };\n        return _this;\n      }\n\n      TitleModel.type = 'title';\n      TitleModel.defaultOption = {\n        // zlevel: 0,\n        z: 6,\n        show: true,\n        text: '',\n        target: 'blank',\n        subtext: '',\n        subtarget: 'blank',\n        left: 0,\n        top: 0,\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderColor: '#ccc',\n        borderWidth: 0,\n        padding: 5,\n        itemGap: 10,\n        textStyle: {\n          fontSize: 18,\n          fontWeight: 'bold',\n          color: '#464646'\n        },\n        subtextStyle: {\n          fontSize: 12,\n          color: '#6E7079'\n        }\n      };\n      return TitleModel;\n    }(ComponentModel); // View\n\n\n    var TitleView =\n    /** @class */\n    function (_super) {\n      __extends(TitleView, _super);\n\n      function TitleView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TitleView.type;\n        return _this;\n      }\n\n      TitleView.prototype.render = function (titleModel, ecModel, api) {\n        this.group.removeAll();\n\n        if (!titleModel.get('show')) {\n          return;\n        }\n\n        var group = this.group;\n        var textStyleModel = titleModel.getModel('textStyle');\n        var subtextStyleModel = titleModel.getModel('subtextStyle');\n        var textAlign = titleModel.get('textAlign');\n        var textVerticalAlign = retrieve2(titleModel.get('textBaseline'), titleModel.get('textVerticalAlign'));\n        var textEl = new ZRText({\n          style: createTextStyle(textStyleModel, {\n            text: titleModel.get('text'),\n            fill: textStyleModel.getTextColor()\n          }, {\n            disableBox: true\n          }),\n          z2: 10\n        });\n        var textRect = textEl.getBoundingRect();\n        var subText = titleModel.get('subtext');\n        var subTextEl = new ZRText({\n          style: createTextStyle(subtextStyleModel, {\n            text: subText,\n            fill: subtextStyleModel.getTextColor(),\n            y: textRect.height + titleModel.get('itemGap'),\n            verticalAlign: 'top'\n          }, {\n            disableBox: true\n          }),\n          z2: 10\n        });\n        var link = titleModel.get('link');\n        var sublink = titleModel.get('sublink');\n        var triggerEvent = titleModel.get('triggerEvent', true);\n        textEl.silent = !link && !triggerEvent;\n        subTextEl.silent = !sublink && !triggerEvent;\n\n        if (link) {\n          textEl.on('click', function () {\n            windowOpen(link, '_' + titleModel.get('target'));\n          });\n        }\n\n        if (sublink) {\n          subTextEl.on('click', function () {\n            windowOpen(sublink, '_' + titleModel.get('subtarget'));\n          });\n        }\n\n        getECData(textEl).eventData = getECData(subTextEl).eventData = triggerEvent ? {\n          componentType: 'title',\n          componentIndex: titleModel.componentIndex\n        } : null;\n        group.add(textEl);\n        subText && group.add(subTextEl); // If no subText, but add subTextEl, there will be an empty line.\n\n        var groupRect = group.getBoundingRect();\n        var layoutOption = titleModel.getBoxLayoutParams();\n        layoutOption.width = groupRect.width;\n        layoutOption.height = groupRect.height;\n        var layoutRect = getLayoutRect(layoutOption, {\n          width: api.getWidth(),\n          height: api.getHeight()\n        }, titleModel.get('padding')); // Adjust text align based on position\n\n        if (!textAlign) {\n          // Align left if title is on the left. center and right is same\n          textAlign = titleModel.get('left') || titleModel.get('right'); // @ts-ignore\n\n          if (textAlign === 'middle') {\n            textAlign = 'center';\n          } // Adjust layout by text align\n\n\n          if (textAlign === 'right') {\n            layoutRect.x += layoutRect.width;\n          } else if (textAlign === 'center') {\n            layoutRect.x += layoutRect.width / 2;\n          }\n        }\n\n        if (!textVerticalAlign) {\n          textVerticalAlign = titleModel.get('top') || titleModel.get('bottom'); // @ts-ignore\n\n          if (textVerticalAlign === 'center') {\n            textVerticalAlign = 'middle';\n          }\n\n          if (textVerticalAlign === 'bottom') {\n            layoutRect.y += layoutRect.height;\n          } else if (textVerticalAlign === 'middle') {\n            layoutRect.y += layoutRect.height / 2;\n          }\n\n          textVerticalAlign = textVerticalAlign || 'top';\n        }\n\n        group.x = layoutRect.x;\n        group.y = layoutRect.y;\n        group.markRedraw();\n        var alignStyle = {\n          align: textAlign,\n          verticalAlign: textVerticalAlign\n        };\n        textEl.setStyle(alignStyle);\n        subTextEl.setStyle(alignStyle); // Render background\n        // Get groupRect again because textAlign has been changed\n\n        groupRect = group.getBoundingRect();\n        var padding = layoutRect.margin;\n        var style = titleModel.getItemStyle(['color', 'opacity']);\n        style.fill = titleModel.get('backgroundColor');\n        var rect = new Rect({\n          shape: {\n            x: groupRect.x - padding[3],\n            y: groupRect.y - padding[0],\n            width: groupRect.width + padding[1] + padding[3],\n            height: groupRect.height + padding[0] + padding[2],\n            r: titleModel.get('borderRadius')\n          },\n          style: style,\n          subPixelOptimize: true,\n          silent: true\n        });\n        group.add(rect);\n      };\n\n      TitleView.type = 'title';\n      return TitleView;\n    }(ComponentView);\n\n    function install$C(registers) {\n      registers.registerComponentModel(TitleModel);\n      registers.registerComponentView(TitleView);\n    }\n\n    var TimelineModel =\n    /** @class */\n    function (_super) {\n      __extends(TimelineModel, _super);\n\n      function TimelineModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TimelineModel.type;\n        _this.layoutMode = 'box';\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      TimelineModel.prototype.init = function (option, parentModel, ecModel) {\n        this.mergeDefaultAndTheme(option, ecModel);\n\n        this._initData();\n      };\n      /**\n       * @override\n       */\n\n\n      TimelineModel.prototype.mergeOption = function (option) {\n        _super.prototype.mergeOption.apply(this, arguments);\n\n        this._initData();\n      };\n\n      TimelineModel.prototype.setCurrentIndex = function (currentIndex) {\n        if (currentIndex == null) {\n          currentIndex = this.option.currentIndex;\n        }\n\n        var count = this._data.count();\n\n        if (this.option.loop) {\n          currentIndex = (currentIndex % count + count) % count;\n        } else {\n          currentIndex >= count && (currentIndex = count - 1);\n          currentIndex < 0 && (currentIndex = 0);\n        }\n\n        this.option.currentIndex = currentIndex;\n      };\n      /**\n       * @return {number} currentIndex\n       */\n\n\n      TimelineModel.prototype.getCurrentIndex = function () {\n        return this.option.currentIndex;\n      };\n      /**\n       * @return {boolean}\n       */\n\n\n      TimelineModel.prototype.isIndexMax = function () {\n        return this.getCurrentIndex() >= this._data.count() - 1;\n      };\n      /**\n       * @param {boolean} state true: play, false: stop\n       */\n\n\n      TimelineModel.prototype.setPlayState = function (state) {\n        this.option.autoPlay = !!state;\n      };\n      /**\n       * @return {boolean} true: play, false: stop\n       */\n\n\n      TimelineModel.prototype.getPlayState = function () {\n        return !!this.option.autoPlay;\n      };\n      /**\n       * @private\n       */\n\n\n      TimelineModel.prototype._initData = function () {\n        var thisOption = this.option;\n        var dataArr = thisOption.data || [];\n        var axisType = thisOption.axisType;\n        var names = this._names = [];\n        var processedDataArr;\n\n        if (axisType === 'category') {\n          processedDataArr = [];\n          each(dataArr, function (item, index) {\n            var value = convertOptionIdName(getDataItemValue(item), '');\n            var newItem;\n\n            if (isObject(item)) {\n              newItem = clone(item);\n              newItem.value = index;\n            } else {\n              newItem = index;\n            }\n\n            processedDataArr.push(newItem);\n            names.push(value);\n          });\n        } else {\n          processedDataArr = dataArr;\n        }\n\n        var dimType = {\n          category: 'ordinal',\n          time: 'time',\n          value: 'number'\n        }[axisType] || 'number';\n        var data = this._data = new SeriesData([{\n          name: 'value',\n          type: dimType\n        }], this);\n        data.initData(processedDataArr, names);\n      };\n\n      TimelineModel.prototype.getData = function () {\n        return this._data;\n      };\n      /**\n       * @public\n       * @return {Array.<string>} categoreis\n       */\n\n\n      TimelineModel.prototype.getCategories = function () {\n        if (this.get('axisType') === 'category') {\n          return this._names.slice();\n        }\n      };\n\n      TimelineModel.type = 'timeline';\n      /**\n       * @protected\n       */\n\n      TimelineModel.defaultOption = {\n        // zlevel: 0,                  // 一级层叠\n        z: 4,\n        show: true,\n        axisType: 'time',\n        realtime: true,\n        left: '20%',\n        top: null,\n        right: '20%',\n        bottom: 0,\n        width: null,\n        height: 40,\n        padding: 5,\n        controlPosition: 'left',\n        autoPlay: false,\n        rewind: false,\n        loop: true,\n        playInterval: 2000,\n        currentIndex: 0,\n        itemStyle: {},\n        label: {\n          color: '#000'\n        },\n        data: []\n      };\n      return TimelineModel;\n    }(ComponentModel);\n\n    var SliderTimelineModel =\n    /** @class */\n    function (_super) {\n      __extends(SliderTimelineModel, _super);\n\n      function SliderTimelineModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SliderTimelineModel.type;\n        return _this;\n      }\n\n      SliderTimelineModel.type = 'timeline.slider';\n      /**\n       * @protected\n       */\n\n      SliderTimelineModel.defaultOption = inheritDefaultOption(TimelineModel.defaultOption, {\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderColor: '#ccc',\n        borderWidth: 0,\n        orient: 'horizontal',\n        inverse: false,\n        tooltip: {\n          trigger: 'item' // data item may also have tootip attr.\n\n        },\n        symbol: 'circle',\n        symbolSize: 12,\n        lineStyle: {\n          show: true,\n          width: 2,\n          color: '#DAE1F5'\n        },\n        label: {\n          position: 'auto',\n          // When using number, label position is not\n          // restricted by viewRect.\n          // positive: right/bottom, negative: left/top\n          show: true,\n          interval: 'auto',\n          rotate: 0,\n          // formatter: null,\n          // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n          color: '#A4B1D7'\n        },\n        itemStyle: {\n          color: '#A4B1D7',\n          borderWidth: 1\n        },\n        checkpointStyle: {\n          symbol: 'circle',\n          symbolSize: 15,\n          color: '#316bf3',\n          borderColor: '#fff',\n          borderWidth: 2,\n          shadowBlur: 2,\n          shadowOffsetX: 1,\n          shadowOffsetY: 1,\n          shadowColor: 'rgba(0, 0, 0, 0.3)',\n          // borderColor: 'rgba(194,53,49, 0.5)',\n          animation: true,\n          animationDuration: 300,\n          animationEasing: 'quinticInOut'\n        },\n        controlStyle: {\n          show: true,\n          showPlayBtn: true,\n          showPrevBtn: true,\n          showNextBtn: true,\n          itemSize: 24,\n          itemGap: 12,\n          position: 'left',\n          playIcon: 'path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z',\n          stopIcon: 'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z',\n          // eslint-disable-next-line max-len\n          nextIcon: 'M2,18.5A1.52,1.52,0,0,1,.92,18a1.49,1.49,0,0,1,0-2.12L7.81,9.36,1,3.11A1.5,1.5,0,1,1,3,.89l8,7.34a1.48,1.48,0,0,1,.49,1.09,1.51,1.51,0,0,1-.46,1.1L3,18.08A1.5,1.5,0,0,1,2,18.5Z',\n          // eslint-disable-next-line max-len\n          prevIcon: 'M10,.5A1.52,1.52,0,0,1,11.08,1a1.49,1.49,0,0,1,0,2.12L4.19,9.64,11,15.89a1.5,1.5,0,1,1-2,2.22L1,10.77A1.48,1.48,0,0,1,.5,9.68,1.51,1.51,0,0,1,1,8.58L9,.92A1.5,1.5,0,0,1,10,.5Z',\n          prevBtnSize: 18,\n          nextBtnSize: 18,\n          color: '#A4B1D7',\n          borderColor: '#A4B1D7',\n          borderWidth: 1\n        },\n        emphasis: {\n          label: {\n            show: true,\n            // 其余属性默认使用全局文本样式，详见TEXTSTYLE\n            color: '#6f778d'\n          },\n          itemStyle: {\n            color: '#316BF3'\n          },\n          controlStyle: {\n            color: '#316BF3',\n            borderColor: '#316BF3',\n            borderWidth: 2\n          }\n        },\n        progress: {\n          lineStyle: {\n            color: '#316BF3'\n          },\n          itemStyle: {\n            color: '#316BF3'\n          },\n          label: {\n            color: '#6f778d'\n          }\n        },\n        data: []\n      });\n      return SliderTimelineModel;\n    }(TimelineModel);\n\n    mixin(SliderTimelineModel, DataFormatMixin.prototype);\n\n    var TimelineView =\n    /** @class */\n    function (_super) {\n      __extends(TimelineView, _super);\n\n      function TimelineView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = TimelineView.type;\n        return _this;\n      }\n\n      TimelineView.type = 'timeline';\n      return TimelineView;\n    }(ComponentView);\n\n    /**\n     * Extend axis 2d\n     */\n\n    var TimelineAxis =\n    /** @class */\n    function (_super) {\n      __extends(TimelineAxis, _super);\n\n      function TimelineAxis(dim, scale, coordExtent, axisType) {\n        var _this = _super.call(this, dim, scale, coordExtent) || this;\n\n        _this.type = axisType || 'value';\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      TimelineAxis.prototype.getLabelModel = function () {\n        // Force override\n        return this.model.getModel('label');\n      };\n      /**\n       * @override\n       */\n\n\n      TimelineAxis.prototype.isHorizontal = function () {\n        return this.model.get('orient') === 'horizontal';\n      };\n\n      return TimelineAxis;\n    }(Axis);\n\n    var PI$8 = Math.PI;\n    var labelDataIndexStore = makeInner();\n\n    var SliderTimelineView =\n    /** @class */\n    function (_super) {\n      __extends(SliderTimelineView, _super);\n\n      function SliderTimelineView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SliderTimelineView.type;\n        return _this;\n      }\n\n      SliderTimelineView.prototype.init = function (ecModel, api) {\n        this.api = api;\n      };\n      /**\n       * @override\n       */\n\n\n      SliderTimelineView.prototype.render = function (timelineModel, ecModel, api) {\n        this.model = timelineModel;\n        this.api = api;\n        this.ecModel = ecModel;\n        this.group.removeAll();\n\n        if (timelineModel.get('show', true)) {\n          var layoutInfo_1 = this._layout(timelineModel, api);\n\n          var mainGroup_1 = this._createGroup('_mainGroup');\n\n          var labelGroup = this._createGroup('_labelGroup');\n\n          var axis_1 = this._axis = this._createAxis(layoutInfo_1, timelineModel);\n\n          timelineModel.formatTooltip = function (dataIndex) {\n            var name = axis_1.scale.getLabel({\n              value: dataIndex\n            });\n            return createTooltipMarkup('nameValue', {\n              noName: true,\n              value: name\n            });\n          };\n\n          each(['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], function (name) {\n            this['_render' + name](layoutInfo_1, mainGroup_1, axis_1, timelineModel);\n          }, this);\n\n          this._renderAxisLabel(layoutInfo_1, labelGroup, axis_1, timelineModel);\n\n          this._position(layoutInfo_1, timelineModel);\n        }\n\n        this._doPlayStop();\n\n        this._updateTicksStatus();\n      };\n      /**\n       * @override\n       */\n\n\n      SliderTimelineView.prototype.remove = function () {\n        this._clearTimer();\n\n        this.group.removeAll();\n      };\n      /**\n       * @override\n       */\n\n\n      SliderTimelineView.prototype.dispose = function () {\n        this._clearTimer();\n      };\n\n      SliderTimelineView.prototype._layout = function (timelineModel, api) {\n        var labelPosOpt = timelineModel.get(['label', 'position']);\n        var orient = timelineModel.get('orient');\n        var viewRect = getViewRect$5(timelineModel, api);\n        var parsedLabelPos; // Auto label offset.\n\n        if (labelPosOpt == null || labelPosOpt === 'auto') {\n          parsedLabelPos = orient === 'horizontal' ? viewRect.y + viewRect.height / 2 < api.getHeight() / 2 ? '-' : '+' : viewRect.x + viewRect.width / 2 < api.getWidth() / 2 ? '+' : '-';\n        } else if (isString(labelPosOpt)) {\n          parsedLabelPos = {\n            horizontal: {\n              top: '-',\n              bottom: '+'\n            },\n            vertical: {\n              left: '-',\n              right: '+'\n            }\n          }[orient][labelPosOpt];\n        } else {\n          // is number\n          parsedLabelPos = labelPosOpt;\n        }\n\n        var labelAlignMap = {\n          horizontal: 'center',\n          vertical: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'left' : 'right'\n        };\n        var labelBaselineMap = {\n          horizontal: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'top' : 'bottom',\n          vertical: 'middle'\n        };\n        var rotationMap = {\n          horizontal: 0,\n          vertical: PI$8 / 2\n        }; // Position\n\n        var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width;\n        var controlModel = timelineModel.getModel('controlStyle');\n        var showControl = controlModel.get('show', true);\n        var controlSize = showControl ? controlModel.get('itemSize') : 0;\n        var controlGap = showControl ? controlModel.get('itemGap') : 0;\n        var sizePlusGap = controlSize + controlGap; // Special label rotate.\n\n        var labelRotation = timelineModel.get(['label', 'rotate']) || 0;\n        labelRotation = labelRotation * PI$8 / 180; // To radian.\n\n        var playPosition;\n        var prevBtnPosition;\n        var nextBtnPosition;\n        var controlPosition = controlModel.get('position', true);\n        var showPlayBtn = showControl && controlModel.get('showPlayBtn', true);\n        var showPrevBtn = showControl && controlModel.get('showPrevBtn', true);\n        var showNextBtn = showControl && controlModel.get('showNextBtn', true);\n        var xLeft = 0;\n        var xRight = mainLength; // position[0] means left, position[1] means middle.\n\n        if (controlPosition === 'left' || controlPosition === 'bottom') {\n          showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap);\n          showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap);\n          showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);\n        } else {\n          // 'top' 'right'\n          showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);\n          showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap);\n          showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);\n        }\n\n        var axisExtent = [xLeft, xRight];\n\n        if (timelineModel.get('inverse')) {\n          axisExtent.reverse();\n        }\n\n        return {\n          viewRect: viewRect,\n          mainLength: mainLength,\n          orient: orient,\n          rotation: rotationMap[orient],\n          labelRotation: labelRotation,\n          labelPosOpt: parsedLabelPos,\n          labelAlign: timelineModel.get(['label', 'align']) || labelAlignMap[orient],\n          labelBaseline: timelineModel.get(['label', 'verticalAlign']) || timelineModel.get(['label', 'baseline']) || labelBaselineMap[orient],\n          // Based on mainGroup.\n          playPosition: playPosition,\n          prevBtnPosition: prevBtnPosition,\n          nextBtnPosition: nextBtnPosition,\n          axisExtent: axisExtent,\n          controlSize: controlSize,\n          controlGap: controlGap\n        };\n      };\n\n      SliderTimelineView.prototype._position = function (layoutInfo, timelineModel) {\n        // Position is be called finally, because bounding rect is needed for\n        // adapt content to fill viewRect (auto adapt offset).\n        // Timeline may be not all in the viewRect when 'offset' is specified\n        // as a number, because it is more appropriate that label aligns at\n        // 'offset' but not the other edge defined by viewRect.\n        var mainGroup = this._mainGroup;\n        var labelGroup = this._labelGroup;\n        var viewRect = layoutInfo.viewRect;\n\n        if (layoutInfo.orient === 'vertical') {\n          // transform to horizontal, inverse rotate by left-top point.\n          var m = create$1();\n          var rotateOriginX = viewRect.x;\n          var rotateOriginY = viewRect.y + viewRect.height;\n          translate(m, m, [-rotateOriginX, -rotateOriginY]);\n          rotate(m, m, -PI$8 / 2);\n          translate(m, m, [rotateOriginX, rotateOriginY]);\n          viewRect = viewRect.clone();\n          viewRect.applyTransform(m);\n        }\n\n        var viewBound = getBound(viewRect);\n        var mainBound = getBound(mainGroup.getBoundingRect());\n        var labelBound = getBound(labelGroup.getBoundingRect());\n        var mainPosition = [mainGroup.x, mainGroup.y];\n        var labelsPosition = [labelGroup.x, labelGroup.y];\n        labelsPosition[0] = mainPosition[0] = viewBound[0][0];\n        var labelPosOpt = layoutInfo.labelPosOpt;\n\n        if (labelPosOpt == null || isString(labelPosOpt)) {\n          // '+' or '-'\n          var mainBoundIdx = labelPosOpt === '+' ? 0 : 1;\n          toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);\n          toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx);\n        } else {\n          var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1;\n          toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);\n          labelsPosition[1] = mainPosition[1] + labelPosOpt;\n        }\n\n        mainGroup.setPosition(mainPosition);\n        labelGroup.setPosition(labelsPosition);\n        mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation;\n        setOrigin(mainGroup);\n        setOrigin(labelGroup);\n\n        function setOrigin(targetGroup) {\n          targetGroup.originX = viewBound[0][0] - targetGroup.x;\n          targetGroup.originY = viewBound[1][0] - targetGroup.y;\n        }\n\n        function getBound(rect) {\n          // [[xmin, xmax], [ymin, ymax]]\n          return [[rect.x, rect.x + rect.width], [rect.y, rect.y + rect.height]];\n        }\n\n        function toBound(fromPos, from, to, dimIdx, boundIdx) {\n          fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx];\n        }\n      };\n\n      SliderTimelineView.prototype._createAxis = function (layoutInfo, timelineModel) {\n        var data = timelineModel.getData();\n        var axisType = timelineModel.get('axisType');\n        var scale = createScaleByModel$1(timelineModel, axisType); // Customize scale. The `tickValue` is `dataIndex`.\n\n        scale.getTicks = function () {\n          return data.mapArray(['value'], function (value) {\n            return {\n              value: value\n            };\n          });\n        };\n\n        var dataExtent = data.getDataExtent('value');\n        scale.setExtent(dataExtent[0], dataExtent[1]);\n        scale.calcNiceTicks();\n        var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType);\n        axis.model = timelineModel;\n        return axis;\n      };\n\n      SliderTimelineView.prototype._createGroup = function (key) {\n        var newGroup = this[key] = new Group();\n        this.group.add(newGroup);\n        return newGroup;\n      };\n\n      SliderTimelineView.prototype._renderAxisLine = function (layoutInfo, group, axis, timelineModel) {\n        var axisExtent = axis.getExtent();\n\n        if (!timelineModel.get(['lineStyle', 'show'])) {\n          return;\n        }\n\n        var line = new Line({\n          shape: {\n            x1: axisExtent[0],\n            y1: 0,\n            x2: axisExtent[1],\n            y2: 0\n          },\n          style: extend({\n            lineCap: 'round'\n          }, timelineModel.getModel('lineStyle').getLineStyle()),\n          silent: true,\n          z2: 1\n        });\n        group.add(line);\n        var progressLine = this._progressLine = new Line({\n          shape: {\n            x1: axisExtent[0],\n            x2: this._currentPointer ? this._currentPointer.x : axisExtent[0],\n            y1: 0,\n            y2: 0\n          },\n          style: defaults({\n            lineCap: 'round',\n            lineWidth: line.style.lineWidth\n          }, timelineModel.getModel(['progress', 'lineStyle']).getLineStyle()),\n          silent: true,\n          z2: 1\n        });\n        group.add(progressLine);\n      };\n\n      SliderTimelineView.prototype._renderAxisTick = function (layoutInfo, group, axis, timelineModel) {\n        var _this = this;\n\n        var data = timelineModel.getData(); // Show all ticks, despite ignoring strategy.\n\n        var ticks = axis.scale.getTicks();\n        this._tickSymbols = []; // The value is dataIndex, see the customized scale.\n\n        each(ticks, function (tick) {\n          var tickCoord = axis.dataToCoord(tick.value);\n          var itemModel = data.getItemModel(tick.value);\n          var itemStyleModel = itemModel.getModel('itemStyle');\n          var hoverStyleModel = itemModel.getModel(['emphasis', 'itemStyle']);\n          var progressStyleModel = itemModel.getModel(['progress', 'itemStyle']);\n          var symbolOpt = {\n            x: tickCoord,\n            y: 0,\n            onclick: bind(_this._changeTimeline, _this, tick.value)\n          };\n          var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt);\n          el.ensureState('emphasis').style = hoverStyleModel.getItemStyle();\n          el.ensureState('progress').style = progressStyleModel.getItemStyle();\n          enableHoverEmphasis(el);\n          var ecData = getECData(el);\n\n          if (itemModel.get('tooltip')) {\n            ecData.dataIndex = tick.value;\n            ecData.dataModel = timelineModel;\n          } else {\n            ecData.dataIndex = ecData.dataModel = null;\n          }\n\n          _this._tickSymbols.push(el);\n        });\n      };\n\n      SliderTimelineView.prototype._renderAxisLabel = function (layoutInfo, group, axis, timelineModel) {\n        var _this = this;\n\n        var labelModel = axis.getLabelModel();\n\n        if (!labelModel.get('show')) {\n          return;\n        }\n\n        var data = timelineModel.getData();\n        var labels = axis.getViewLabels();\n        this._tickLabels = [];\n        each(labels, function (labelItem) {\n          // The tickValue is dataIndex, see the customized scale.\n          var dataIndex = labelItem.tickValue;\n          var itemModel = data.getItemModel(dataIndex);\n          var normalLabelModel = itemModel.getModel('label');\n          var hoverLabelModel = itemModel.getModel(['emphasis', 'label']);\n          var progressLabelModel = itemModel.getModel(['progress', 'label']);\n          var tickCoord = axis.dataToCoord(labelItem.tickValue);\n          var textEl = new ZRText({\n            x: tickCoord,\n            y: 0,\n            rotation: layoutInfo.labelRotation - layoutInfo.rotation,\n            onclick: bind(_this._changeTimeline, _this, dataIndex),\n            silent: false,\n            style: createTextStyle(normalLabelModel, {\n              text: labelItem.formattedLabel,\n              align: layoutInfo.labelAlign,\n              verticalAlign: layoutInfo.labelBaseline\n            })\n          });\n          textEl.ensureState('emphasis').style = createTextStyle(hoverLabelModel);\n          textEl.ensureState('progress').style = createTextStyle(progressLabelModel);\n          group.add(textEl);\n          enableHoverEmphasis(textEl);\n          labelDataIndexStore(textEl).dataIndex = dataIndex;\n\n          _this._tickLabels.push(textEl);\n        });\n      };\n\n      SliderTimelineView.prototype._renderControl = function (layoutInfo, group, axis, timelineModel) {\n        var controlSize = layoutInfo.controlSize;\n        var rotation = layoutInfo.rotation;\n        var itemStyle = timelineModel.getModel('controlStyle').getItemStyle();\n        var hoverStyle = timelineModel.getModel(['emphasis', 'controlStyle']).getItemStyle();\n        var playState = timelineModel.getPlayState();\n        var inverse = timelineModel.get('inverse', true);\n        makeBtn(layoutInfo.nextBtnPosition, 'next', bind(this._changeTimeline, this, inverse ? '-' : '+'));\n        makeBtn(layoutInfo.prevBtnPosition, 'prev', bind(this._changeTimeline, this, inverse ? '+' : '-'));\n        makeBtn(layoutInfo.playPosition, playState ? 'stop' : 'play', bind(this._handlePlayClick, this, !playState), true);\n\n        function makeBtn(position, iconName, onclick, willRotate) {\n          if (!position) {\n            return;\n          }\n\n          var iconSize = parsePercent(retrieve2(timelineModel.get(['controlStyle', iconName + 'BtnSize']), controlSize), controlSize);\n          var rect = [0, -iconSize / 2, iconSize, iconSize];\n          var btn = makeControlIcon(timelineModel, iconName + 'Icon', rect, {\n            x: position[0],\n            y: position[1],\n            originX: controlSize / 2,\n            originY: 0,\n            rotation: willRotate ? -rotation : 0,\n            rectHover: true,\n            style: itemStyle,\n            onclick: onclick\n          });\n          btn.ensureState('emphasis').style = hoverStyle;\n          group.add(btn);\n          enableHoverEmphasis(btn);\n        }\n      };\n\n      SliderTimelineView.prototype._renderCurrentPointer = function (layoutInfo, group, axis, timelineModel) {\n        var data = timelineModel.getData();\n        var currentIndex = timelineModel.getCurrentIndex();\n        var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle');\n        var me = this;\n        var callback = {\n          onCreate: function (pointer) {\n            pointer.draggable = true;\n            pointer.drift = bind(me._handlePointerDrag, me);\n            pointer.ondragend = bind(me._handlePointerDragend, me);\n            pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel, true);\n          },\n          onUpdate: function (pointer) {\n            pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel);\n          }\n        }; // Reuse when exists, for animation and drag.\n\n        this._currentPointer = giveSymbol(pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback);\n      };\n\n      SliderTimelineView.prototype._handlePlayClick = function (nextState) {\n        this._clearTimer();\n\n        this.api.dispatchAction({\n          type: 'timelinePlayChange',\n          playState: nextState,\n          from: this.uid\n        });\n      };\n\n      SliderTimelineView.prototype._handlePointerDrag = function (dx, dy, e) {\n        this._clearTimer();\n\n        this._pointerChangeTimeline([e.offsetX, e.offsetY]);\n      };\n\n      SliderTimelineView.prototype._handlePointerDragend = function (e) {\n        this._pointerChangeTimeline([e.offsetX, e.offsetY], true);\n      };\n\n      SliderTimelineView.prototype._pointerChangeTimeline = function (mousePos, trigger) {\n        var toCoord = this._toAxisCoord(mousePos)[0];\n\n        var axis = this._axis;\n        var axisExtent = asc(axis.getExtent().slice());\n        toCoord > axisExtent[1] && (toCoord = axisExtent[1]);\n        toCoord < axisExtent[0] && (toCoord = axisExtent[0]);\n        this._currentPointer.x = toCoord;\n\n        this._currentPointer.markRedraw();\n\n        var progressLine = this._progressLine;\n\n        if (progressLine) {\n          progressLine.shape.x2 = toCoord;\n          progressLine.dirty();\n        }\n\n        var targetDataIndex = this._findNearestTick(toCoord);\n\n        var timelineModel = this.model;\n\n        if (trigger || targetDataIndex !== timelineModel.getCurrentIndex() && timelineModel.get('realtime')) {\n          this._changeTimeline(targetDataIndex);\n        }\n      };\n\n      SliderTimelineView.prototype._doPlayStop = function () {\n        var _this = this;\n\n        this._clearTimer();\n\n        if (this.model.getPlayState()) {\n          this._timer = setTimeout(function () {\n            // Do not cache\n            var timelineModel = _this.model;\n\n            _this._changeTimeline(timelineModel.getCurrentIndex() + (timelineModel.get('rewind', true) ? -1 : 1));\n          }, this.model.get('playInterval'));\n        }\n      };\n\n      SliderTimelineView.prototype._toAxisCoord = function (vertex) {\n        var trans = this._mainGroup.getLocalTransform();\n\n        return applyTransform$1(vertex, trans, true);\n      };\n\n      SliderTimelineView.prototype._findNearestTick = function (axisCoord) {\n        var data = this.model.getData();\n        var dist = Infinity;\n        var targetDataIndex;\n        var axis = this._axis;\n        data.each(['value'], function (value, dataIndex) {\n          var coord = axis.dataToCoord(value);\n          var d = Math.abs(coord - axisCoord);\n\n          if (d < dist) {\n            dist = d;\n            targetDataIndex = dataIndex;\n          }\n        });\n        return targetDataIndex;\n      };\n\n      SliderTimelineView.prototype._clearTimer = function () {\n        if (this._timer) {\n          clearTimeout(this._timer);\n          this._timer = null;\n        }\n      };\n\n      SliderTimelineView.prototype._changeTimeline = function (nextIndex) {\n        var currentIndex = this.model.getCurrentIndex();\n\n        if (nextIndex === '+') {\n          nextIndex = currentIndex + 1;\n        } else if (nextIndex === '-') {\n          nextIndex = currentIndex - 1;\n        }\n\n        this.api.dispatchAction({\n          type: 'timelineChange',\n          currentIndex: nextIndex,\n          from: this.uid\n        });\n      };\n\n      SliderTimelineView.prototype._updateTicksStatus = function () {\n        var currentIndex = this.model.getCurrentIndex();\n        var tickSymbols = this._tickSymbols;\n        var tickLabels = this._tickLabels;\n\n        if (tickSymbols) {\n          for (var i = 0; i < tickSymbols.length; i++) {\n            tickSymbols && tickSymbols[i] && tickSymbols[i].toggleState('progress', i < currentIndex);\n          }\n        }\n\n        if (tickLabels) {\n          for (var i = 0; i < tickLabels.length; i++) {\n            tickLabels && tickLabels[i] && tickLabels[i].toggleState('progress', labelDataIndexStore(tickLabels[i]).dataIndex <= currentIndex);\n          }\n        }\n      };\n\n      SliderTimelineView.type = 'timeline.slider';\n      return SliderTimelineView;\n    }(TimelineView);\n\n    function createScaleByModel$1(model, axisType) {\n      axisType = axisType || model.get('type');\n\n      if (axisType) {\n        switch (axisType) {\n          // Buildin scale\n          case 'category':\n            return new OrdinalScale({\n              ordinalMeta: model.getCategories(),\n              extent: [Infinity, -Infinity]\n            });\n\n          case 'time':\n            return new TimeScale({\n              locale: model.ecModel.getLocaleModel(),\n              useUTC: model.ecModel.get('useUTC')\n            });\n\n          default:\n            // default to be value\n            return new IntervalScale();\n        }\n      }\n    }\n\n    function getViewRect$5(model, api) {\n      return getLayoutRect(model.getBoxLayoutParams(), {\n        width: api.getWidth(),\n        height: api.getHeight()\n      }, model.get('padding'));\n    }\n\n    function makeControlIcon(timelineModel, objPath, rect, opts) {\n      var style = opts.style;\n      var icon = createIcon(timelineModel.get(['controlStyle', objPath]), opts || {}, new BoundingRect(rect[0], rect[1], rect[2], rect[3])); // TODO createIcon won't use style in opt.\n\n      if (style) {\n        icon.setStyle(style);\n      }\n\n      return icon;\n    }\n    /**\n     * Create symbol or update symbol\n     * opt: basic position and event handlers\n     */\n\n\n    function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) {\n      var color = itemStyleModel.get('color');\n\n      if (!symbol) {\n        var symbolType = hostModel.get('symbol');\n        symbol = createSymbol(symbolType, -1, -1, 2, 2, color);\n        symbol.setStyle('strokeNoScale', true);\n        group.add(symbol);\n        callback && callback.onCreate(symbol);\n      } else {\n        symbol.setColor(color);\n        group.add(symbol); // Group may be new, also need to add.\n\n        callback && callback.onUpdate(symbol);\n      } // Style\n\n\n      var itemStyle = itemStyleModel.getItemStyle(['color']);\n      symbol.setStyle(itemStyle); // Transform and events.\n\n      opt = merge({\n        rectHover: true,\n        z2: 100\n      }, opt, true);\n      var symbolSize = normalizeSymbolSize(hostModel.get('symbolSize'));\n      opt.scaleX = symbolSize[0] / 2;\n      opt.scaleY = symbolSize[1] / 2;\n      var symbolOffset = normalizeSymbolOffset(hostModel.get('symbolOffset'), symbolSize);\n\n      if (symbolOffset) {\n        opt.x = (opt.x || 0) + symbolOffset[0];\n        opt.y = (opt.y || 0) + symbolOffset[1];\n      }\n\n      var symbolRotate = hostModel.get('symbolRotate');\n      opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;\n      symbol.attr(opt); // FIXME\n      // (1) When symbol.style.strokeNoScale is true and updateTransform is not performed,\n      // getBoundingRect will return wrong result.\n      // (This is supposed to be resolved in zrender, but it is a little difficult to\n      // leverage performance and auto updateTransform)\n      // (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol.\n\n      symbol.updateTransform();\n      return symbol;\n    }\n\n    function pointerMoveTo(pointer, progressLine, dataIndex, axis, timelineModel, noAnimation) {\n      if (pointer.dragging) {\n        return;\n      }\n\n      var pointerModel = timelineModel.getModel('checkpointStyle');\n      var toCoord = axis.dataToCoord(timelineModel.getData().get('value', dataIndex));\n\n      if (noAnimation || !pointerModel.get('animation', true)) {\n        pointer.attr({\n          x: toCoord,\n          y: 0\n        });\n        progressLine && progressLine.attr({\n          shape: {\n            x2: toCoord\n          }\n        });\n      } else {\n        var animationCfg = {\n          duration: pointerModel.get('animationDuration', true),\n          easing: pointerModel.get('animationEasing', true)\n        };\n        pointer.stopAnimation(null, true);\n        pointer.animateTo({\n          x: toCoord,\n          y: 0\n        }, animationCfg);\n        progressLine && progressLine.animateTo({\n          shape: {\n            x2: toCoord\n          }\n        }, animationCfg);\n      }\n    }\n\n    function installTimelineAction(registers) {\n      registers.registerAction({\n        type: 'timelineChange',\n        event: 'timelineChanged',\n        update: 'prepareAndUpdate'\n      }, function (payload, ecModel, api) {\n        var timelineModel = ecModel.getComponent('timeline');\n\n        if (timelineModel && payload.currentIndex != null) {\n          timelineModel.setCurrentIndex(payload.currentIndex);\n\n          if (!timelineModel.get('loop', true) && timelineModel.isIndexMax() && timelineModel.getPlayState()) {\n            timelineModel.setPlayState(false); // The timeline has played to the end, trigger event\n\n            api.dispatchAction({\n              type: 'timelinePlayChange',\n              playState: false,\n              from: payload.from\n            });\n          }\n        } // Set normalized currentIndex to payload.\n\n\n        ecModel.resetOption('timeline', {\n          replaceMerge: timelineModel.get('replaceMerge', true)\n        });\n        return defaults({\n          currentIndex: timelineModel.option.currentIndex\n        }, payload);\n      });\n      registers.registerAction({\n        type: 'timelinePlayChange',\n        event: 'timelinePlayChanged',\n        update: 'update'\n      }, function (payload, ecModel) {\n        var timelineModel = ecModel.getComponent('timeline');\n\n        if (timelineModel && payload.playState != null) {\n          timelineModel.setPlayState(payload.playState);\n        }\n      });\n    }\n\n    function timelinePreprocessor(option) {\n      var timelineOpt = option && option.timeline;\n\n      if (!isArray(timelineOpt)) {\n        timelineOpt = timelineOpt ? [timelineOpt] : [];\n      }\n\n      each(timelineOpt, function (opt) {\n        if (!opt) {\n          return;\n        }\n\n        compatibleEC2(opt);\n      });\n    }\n\n    function compatibleEC2(opt) {\n      var type = opt.type;\n      var ec2Types = {\n        'number': 'value',\n        'time': 'time'\n      }; // Compatible with ec2\n\n      if (ec2Types[type]) {\n        opt.axisType = ec2Types[type];\n        delete opt.type;\n      }\n\n      transferItem(opt);\n\n      if (has(opt, 'controlPosition')) {\n        var controlStyle = opt.controlStyle || (opt.controlStyle = {});\n\n        if (!has(controlStyle, 'position')) {\n          controlStyle.position = opt.controlPosition;\n        }\n\n        if (controlStyle.position === 'none' && !has(controlStyle, 'show')) {\n          controlStyle.show = false;\n          delete controlStyle.position;\n        }\n\n        delete opt.controlPosition;\n      }\n\n      each(opt.data || [], function (dataItem) {\n        if (isObject(dataItem) && !isArray(dataItem)) {\n          if (!has(dataItem, 'value') && has(dataItem, 'name')) {\n            // In ec2, using name as value.\n            dataItem.value = dataItem.name;\n          }\n\n          transferItem(dataItem);\n        }\n      });\n    }\n\n    function transferItem(opt) {\n      var itemStyle = opt.itemStyle || (opt.itemStyle = {});\n      var itemStyleEmphasis = itemStyle.emphasis || (itemStyle.emphasis = {}); // Transfer label out\n\n      var label = opt.label || opt.label || {};\n      var labelNormal = label.normal || (label.normal = {});\n      var excludeLabelAttr = {\n        normal: 1,\n        emphasis: 1\n      };\n      each(label, function (value, name) {\n        if (!excludeLabelAttr[name] && !has(labelNormal, name)) {\n          labelNormal[name] = value;\n        }\n      });\n\n      if (itemStyleEmphasis.label && !has(label, 'emphasis')) {\n        label.emphasis = itemStyleEmphasis.label;\n        delete itemStyleEmphasis.label;\n      }\n    }\n\n    function has(obj, attr) {\n      return obj.hasOwnProperty(attr);\n    }\n\n    function install$D(registers) {\n      registers.registerComponentModel(SliderTimelineModel);\n      registers.registerComponentView(SliderTimelineView);\n      registers.registerSubTypeDefaulter('timeline', function () {\n        // Only slider now.\n        return 'slider';\n      });\n      installTimelineAction(registers);\n      registers.registerPreprocessor(timelinePreprocessor);\n    }\n\n    function checkMarkerInSeries(seriesOpts, markerType) {\n      if (!seriesOpts) {\n        return false;\n      }\n\n      var seriesOptArr = isArray(seriesOpts) ? seriesOpts : [seriesOpts];\n\n      for (var idx = 0; idx < seriesOptArr.length; idx++) {\n        if (seriesOptArr[idx] && seriesOptArr[idx][markerType]) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    function fillLabel(opt) {\n      defaultEmphasis(opt, 'label', ['show']);\n    } // { [componentType]: MarkerModel }\n\n\n    var inner$g = makeInner();\n\n    var MarkerModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkerModel, _super);\n\n      function MarkerModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkerModel.type;\n        /**\n         * If marker model is created by self from series\n         */\n\n        _this.createdBySelf = false;\n        return _this;\n      }\n      /**\n       * @overrite\n       */\n\n\n      MarkerModel.prototype.init = function (option, parentModel, ecModel) {\n        if (\"development\" !== 'production') {\n          if (this.type === 'marker') {\n            throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.');\n          }\n        }\n\n        this.mergeDefaultAndTheme(option, ecModel);\n\n        this._mergeOption(option, ecModel, false, true);\n      };\n\n      MarkerModel.prototype.isAnimationEnabled = function () {\n        if (env.node) {\n          return false;\n        }\n\n        var hostSeries = this.__hostSeries;\n        return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled();\n      };\n      /**\n       * @overrite\n       */\n\n\n      MarkerModel.prototype.mergeOption = function (newOpt, ecModel) {\n        this._mergeOption(newOpt, ecModel, false, false);\n      };\n\n      MarkerModel.prototype._mergeOption = function (newOpt, ecModel, createdBySelf, isInit) {\n        var componentType = this.mainType;\n\n        if (!createdBySelf) {\n          ecModel.eachSeries(function (seriesModel) {\n            // mainType can be markPoint, markLine, markArea\n            var markerOpt = seriesModel.get(this.mainType, true);\n            var markerModel = inner$g(seriesModel)[componentType];\n\n            if (!markerOpt || !markerOpt.data) {\n              inner$g(seriesModel)[componentType] = null;\n              return;\n            }\n\n            if (!markerModel) {\n              if (isInit) {\n                // Default label emphasis `position` and `show`\n                fillLabel(markerOpt);\n              }\n\n              each(markerOpt.data, function (item) {\n                // FIXME Overwrite fillLabel method ?\n                if (item instanceof Array) {\n                  fillLabel(item[0]);\n                  fillLabel(item[1]);\n                } else {\n                  fillLabel(item);\n                }\n              });\n              markerModel = this.createMarkerModelFromSeries(markerOpt, this, ecModel); // markerModel = new ImplementedMarkerModel(\n              //     markerOpt, this, ecModel\n              // );\n\n              extend(markerModel, {\n                mainType: this.mainType,\n                // Use the same series index and name\n                seriesIndex: seriesModel.seriesIndex,\n                name: seriesModel.name,\n                createdBySelf: true\n              });\n              markerModel.__hostSeries = seriesModel;\n            } else {\n              markerModel._mergeOption(markerOpt, ecModel, true);\n            }\n\n            inner$g(seriesModel)[componentType] = markerModel;\n          }, this);\n        }\n      };\n\n      MarkerModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        var data = this.getData();\n        var value = this.getRawValue(dataIndex);\n        var itemName = data.getName(dataIndex);\n        return createTooltipMarkup('section', {\n          header: this.name,\n          blocks: [createTooltipMarkup('nameValue', {\n            name: itemName,\n            value: value,\n            noName: !itemName,\n            noValue: value == null\n          })]\n        });\n      };\n\n      MarkerModel.prototype.getData = function () {\n        return this._data;\n      };\n\n      MarkerModel.prototype.setData = function (data) {\n        this._data = data;\n      };\n\n      MarkerModel.getMarkerModelFromSeries = function (seriesModel, // Support three types of markers. Strict check.\n      componentType) {\n        return inner$g(seriesModel)[componentType];\n      };\n\n      MarkerModel.type = 'marker';\n      MarkerModel.dependencies = ['series', 'grid', 'polar', 'geo'];\n      return MarkerModel;\n    }(ComponentModel);\n\n    mixin(MarkerModel, DataFormatMixin.prototype);\n\n    var MarkPointModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkPointModel, _super);\n\n      function MarkPointModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkPointModel.type;\n        return _this;\n      }\n\n      MarkPointModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n        return new MarkPointModel(markerOpt, masterMarkerModel, ecModel);\n      };\n\n      MarkPointModel.type = 'markPoint';\n      MarkPointModel.defaultOption = {\n        // zlevel: 0,\n        z: 5,\n        symbol: 'pin',\n        symbolSize: 50,\n        // symbolRotate: 0,\n        // symbolOffset: [0, 0]\n        tooltip: {\n          trigger: 'item'\n        },\n        label: {\n          show: true,\n          position: 'inside'\n        },\n        itemStyle: {\n          borderWidth: 2\n        },\n        emphasis: {\n          label: {\n            show: true\n          }\n        }\n      };\n      return MarkPointModel;\n    }(MarkerModel);\n\n    function hasXOrY(item) {\n      return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y)));\n    }\n\n    function hasXAndY(item) {\n      return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y));\n    }\n\n    function markerTypeCalculatorWithExtent(markerType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex) {\n      var coordArr = [];\n      var stacked = isDimensionStacked(data, targetDataDim\n      /* , otherDataDim */\n      );\n      var calcDataDim = stacked ? data.getCalculationInfo('stackResultDimension') : targetDataDim;\n      var value = numCalculate(data, calcDataDim, markerType);\n      var dataIndex = data.indicesOfNearest(calcDataDim, value)[0];\n      coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex);\n      coordArr[targetCoordIndex] = data.get(calcDataDim, dataIndex);\n      var coordArrValue = data.get(targetDataDim, dataIndex); // Make it simple, do not visit all stacked value to count precision.\n\n      var precision = getPrecision(data.get(targetDataDim, dataIndex));\n      precision = Math.min(precision, 20);\n\n      if (precision >= 0) {\n        coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision);\n      }\n\n      return [coordArr, coordArrValue];\n    } // TODO Specified percent\n\n\n    var markerTypeCalculator = {\n      min: curry(markerTypeCalculatorWithExtent, 'min'),\n      max: curry(markerTypeCalculatorWithExtent, 'max'),\n      average: curry(markerTypeCalculatorWithExtent, 'average'),\n      median: curry(markerTypeCalculatorWithExtent, 'median')\n    };\n    /**\n     * Transform markPoint data item to format used in List by do the following\n     * 1. Calculate statistic like `max`, `min`, `average`\n     * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array\n     */\n\n    function dataTransform(seriesModel, item) {\n      if (!item) {\n        return;\n      }\n\n      var data = seriesModel.getData();\n      var coordSys = seriesModel.coordinateSystem;\n      var dims = coordSys.dimensions; // 1. If not specify the position with pixel directly\n      // 2. If `coord` is not a data array. Which uses `xAxis`,\n      // `yAxis` to specify the coord on each dimension\n      // parseFloat first because item.x and item.y can be percent string like '20%'\n\n      if (!hasXAndY(item) && !isArray(item.coord) && coordSys) {\n        var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); // Clone the option\n        // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value\n\n        item = clone(item);\n\n        if (item.type && markerTypeCalculator[item.type] && axisInfo.baseAxis && axisInfo.valueAxis) {\n          var otherCoordIndex = indexOf(dims, axisInfo.baseAxis.dim);\n          var targetCoordIndex = indexOf(dims, axisInfo.valueAxis.dim);\n          var coordInfo = markerTypeCalculator[item.type](data, axisInfo.baseDataDim, axisInfo.valueDataDim, otherCoordIndex, targetCoordIndex);\n          item.coord = coordInfo[0]; // Force to use the value of calculated value.\n          // let item use the value without stack.\n\n          item.value = coordInfo[1];\n        } else {\n          // FIXME Only has one of xAxis and yAxis.\n          item.coord = [item.xAxis != null ? item.xAxis : item.radiusAxis, item.yAxis != null ? item.yAxis : item.angleAxis];\n        }\n      } // x y is provided\n\n\n      if (item.coord == null) {\n        item.coord = [];\n      } else {\n        // Each coord support max, min, average\n        var coord = item.coord;\n\n        for (var i = 0; i < 2; i++) {\n          if (markerTypeCalculator[coord[i]]) {\n            coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]);\n          }\n        }\n      }\n\n      return item;\n    }\n    function getAxisInfo$1(item, data, coordSys, seriesModel) {\n      var ret = {};\n\n      if (item.valueIndex != null || item.valueDim != null) {\n        ret.valueDataDim = item.valueIndex != null ? data.getDimension(item.valueIndex) : item.valueDim;\n        ret.valueAxis = coordSys.getAxis(dataDimToCoordDim(seriesModel, ret.valueDataDim));\n        ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis);\n        ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);\n      } else {\n        ret.baseAxis = seriesModel.getBaseAxis();\n        ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis);\n        ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);\n        ret.valueDataDim = data.mapDimension(ret.valueAxis.dim);\n      }\n\n      return ret;\n    }\n\n    function dataDimToCoordDim(seriesModel, dataDim) {\n      var dimItem = seriesModel.getData().getDimensionInfo(dataDim);\n      return dimItem && dimItem.coordDim;\n    }\n    /**\n     * Filter data which is out of coordinateSystem range\n     * [dataFilter description]\n     */\n\n\n    function dataFilter$1( // Currently only polar and cartesian has containData.\n    coordSys, item) {\n      // Always return true if there is no coordSys\n      return coordSys && coordSys.containData && item.coord && !hasXOrY(item) ? coordSys.containData(item.coord) : true;\n    }\n    function zoneFilter( // Currently only polar and cartesian has containData.\n    coordSys, item1, item2) {\n      // Always return true if there is no coordSys\n      return coordSys && coordSys.containZone && item1.coord && item2.coord && !hasXOrY(item1) && !hasXOrY(item2) ? coordSys.containZone(item1.coord, item2.coord) : true;\n    }\n    function createMarkerDimValueGetter(inCoordSys, dims) {\n      return inCoordSys ? function (item, dimName, dataIndex, dimIndex) {\n        var rawVal = dimIndex < 2 // x, y, radius, angle\n        ? item.coord && item.coord[dimIndex] : item.value;\n        return parseDataValue(rawVal, dims[dimIndex]);\n      } : function (item, dimName, dataIndex, dimIndex) {\n        return parseDataValue(item.value, dims[dimIndex]);\n      };\n    }\n    function numCalculate(data, valueDataDim, type) {\n      if (type === 'average') {\n        var sum_1 = 0;\n        var count_1 = 0;\n        data.each(valueDataDim, function (val, idx) {\n          if (!isNaN(val)) {\n            sum_1 += val;\n            count_1++;\n          }\n        });\n        return sum_1 / count_1;\n      } else if (type === 'median') {\n        return data.getMedian(valueDataDim);\n      } else {\n        // max & min\n        return data.getDataExtent(valueDataDim)[type === 'max' ? 1 : 0];\n      }\n    }\n\n    var inner$h = makeInner();\n\n    var MarkerView =\n    /** @class */\n    function (_super) {\n      __extends(MarkerView, _super);\n\n      function MarkerView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkerView.type;\n        return _this;\n      }\n\n      MarkerView.prototype.init = function () {\n        this.markerGroupMap = createHashMap();\n      };\n\n      MarkerView.prototype.render = function (markerModel, ecModel, api) {\n        var _this = this;\n\n        var markerGroupMap = this.markerGroupMap;\n        markerGroupMap.each(function (item) {\n          inner$h(item).keep = false;\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type);\n          markerModel && _this.renderSeries(seriesModel, markerModel, ecModel, api);\n        });\n        markerGroupMap.each(function (item) {\n          !inner$h(item).keep && _this.group.remove(item.group);\n        });\n      };\n\n      MarkerView.prototype.markKeep = function (drawGroup) {\n        inner$h(drawGroup).keep = true;\n      };\n\n      MarkerView.prototype.toggleBlurSeries = function (seriesModelList, isBlur) {\n        var _this = this;\n\n        each(seriesModelList, function (seriesModel) {\n          var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type);\n\n          if (markerModel) {\n            var data = markerModel.getData();\n            data.eachItemGraphicEl(function (el) {\n              if (el) {\n                isBlur ? enterBlur(el) : leaveBlur(el);\n              }\n            });\n          }\n        });\n      };\n\n      MarkerView.type = 'marker';\n      return MarkerView;\n    }(ComponentView);\n\n    function updateMarkerLayout(mpData, seriesModel, api) {\n      var coordSys = seriesModel.coordinateSystem;\n      mpData.each(function (idx) {\n        var itemModel = mpData.getItemModel(idx);\n        var point;\n        var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());\n        var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());\n\n        if (!isNaN(xPx) && !isNaN(yPx)) {\n          point = [xPx, yPx];\n        } // Chart like bar may have there own marker positioning logic\n        else if (seriesModel.getMarkerPosition) {\n            // Use the getMarkerPosition\n            point = seriesModel.getMarkerPosition(mpData.getValues(mpData.dimensions, idx));\n          } else if (coordSys) {\n            var x = mpData.get(coordSys.dimensions[0], idx);\n            var y = mpData.get(coordSys.dimensions[1], idx);\n            point = coordSys.dataToPoint([x, y]);\n          } // Use x, y if has any\n\n\n        if (!isNaN(xPx)) {\n          point[0] = xPx;\n        }\n\n        if (!isNaN(yPx)) {\n          point[1] = yPx;\n        }\n\n        mpData.setItemLayout(idx, point);\n      });\n    }\n\n    var MarkPointView =\n    /** @class */\n    function (_super) {\n      __extends(MarkPointView, _super);\n\n      function MarkPointView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkPointView.type;\n        return _this;\n      }\n\n      MarkPointView.prototype.updateTransform = function (markPointModel, ecModel, api) {\n        ecModel.eachSeries(function (seriesModel) {\n          var mpModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markPoint');\n\n          if (mpModel) {\n            updateMarkerLayout(mpModel.getData(), seriesModel, api);\n            this.markerGroupMap.get(seriesModel.id).updateLayout();\n          }\n        }, this);\n      };\n\n      MarkPointView.prototype.renderSeries = function (seriesModel, mpModel, ecModel, api) {\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesId = seriesModel.id;\n        var seriesData = seriesModel.getData();\n        var symbolDrawMap = this.markerGroupMap;\n        var symbolDraw = symbolDrawMap.get(seriesId) || symbolDrawMap.set(seriesId, new SymbolDraw());\n        var mpData = createData(coordSys, seriesModel, mpModel); // FIXME\n\n        mpModel.setData(mpData);\n        updateMarkerLayout(mpModel.getData(), seriesModel, api);\n        mpData.each(function (idx) {\n          var itemModel = mpData.getItemModel(idx);\n          var symbol = itemModel.getShallow('symbol');\n          var symbolSize = itemModel.getShallow('symbolSize');\n          var symbolRotate = itemModel.getShallow('symbolRotate');\n          var symbolOffset = itemModel.getShallow('symbolOffset');\n          var symbolKeepAspect = itemModel.getShallow('symbolKeepAspect'); // TODO: refactor needed: single data item should not support callback function\n\n          if (isFunction(symbol) || isFunction(symbolSize) || isFunction(symbolRotate) || isFunction(symbolOffset)) {\n            var rawIdx = mpModel.getRawValue(idx);\n            var dataParams = mpModel.getDataParams(idx);\n\n            if (isFunction(symbol)) {\n              symbol = symbol(rawIdx, dataParams);\n            }\n\n            if (isFunction(symbolSize)) {\n              // FIXME 这里不兼容 ECharts 2.x，2.x 貌似参数是整个数据？\n              symbolSize = symbolSize(rawIdx, dataParams);\n            }\n\n            if (isFunction(symbolRotate)) {\n              symbolRotate = symbolRotate(rawIdx, dataParams);\n            }\n\n            if (isFunction(symbolOffset)) {\n              symbolOffset = symbolOffset(rawIdx, dataParams);\n            }\n          }\n\n          var style = itemModel.getModel('itemStyle').getItemStyle();\n          var color = getVisualFromData(seriesData, 'color');\n\n          if (!style.fill) {\n            style.fill = color;\n          }\n\n          mpData.setItemVisual(idx, {\n            symbol: symbol,\n            symbolSize: symbolSize,\n            symbolRotate: symbolRotate,\n            symbolOffset: symbolOffset,\n            symbolKeepAspect: symbolKeepAspect,\n            style: style\n          });\n        }); // TODO Text are wrong\n\n        symbolDraw.updateData(mpData);\n        this.group.add(symbolDraw.group); // Set host model for tooltip\n        // FIXME\n\n        mpData.eachItemGraphicEl(function (el) {\n          el.traverse(function (child) {\n            getECData(child).dataModel = mpModel;\n          });\n        });\n        this.markKeep(symbolDraw);\n        symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent');\n      };\n\n      MarkPointView.type = 'markPoint';\n      return MarkPointView;\n    }(MarkerView);\n\n    function createData(coordSys, seriesModel, mpModel) {\n      var coordDimsInfos;\n\n      if (coordSys) {\n        coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {\n          var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n          return extend(extend({}, info), {\n            name: coordDim,\n            // DON'T use ordinalMeta to parse and collect ordinal.\n            ordinalMeta: null\n          });\n        });\n      } else {\n        coordDimsInfos = [{\n          name: 'value',\n          type: 'float'\n        }];\n      }\n\n      var mpData = new SeriesData(coordDimsInfos, mpModel);\n      var dataOpt = map(mpModel.get('data'), curry(dataTransform, seriesModel));\n\n      if (coordSys) {\n        dataOpt = filter(dataOpt, curry(dataFilter$1, coordSys));\n      }\n\n      var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos);\n      mpData.initData(dataOpt, null, dimValueGetter);\n      return mpData;\n    }\n\n    function install$E(registers) {\n      registers.registerComponentModel(MarkPointModel);\n      registers.registerComponentView(MarkPointView);\n      registers.registerPreprocessor(function (opt) {\n        if (checkMarkerInSeries(opt.series, 'markPoint')) {\n          // Make sure markPoint component is enabled\n          opt.markPoint = opt.markPoint || {};\n        }\n      });\n    }\n\n    var MarkLineModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkLineModel, _super);\n\n      function MarkLineModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkLineModel.type;\n        return _this;\n      }\n\n      MarkLineModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n        return new MarkLineModel(markerOpt, masterMarkerModel, ecModel);\n      };\n\n      MarkLineModel.type = 'markLine';\n      MarkLineModel.defaultOption = {\n        // zlevel: 0,\n        z: 5,\n        symbol: ['circle', 'arrow'],\n        symbolSize: [8, 16],\n        // symbolRotate: 0,\n        symbolOffset: 0,\n        precision: 2,\n        tooltip: {\n          trigger: 'item'\n        },\n        label: {\n          show: true,\n          position: 'end',\n          distance: 5\n        },\n        lineStyle: {\n          type: 'dashed'\n        },\n        emphasis: {\n          label: {\n            show: true\n          },\n          lineStyle: {\n            width: 3\n          }\n        },\n        animationEasing: 'linear'\n      };\n      return MarkLineModel;\n    }(MarkerModel);\n\n    var inner$i = makeInner();\n\n    var markLineTransform = function (seriesModel, coordSys, mlModel, item) {\n      var data = seriesModel.getData();\n      var itemArray;\n\n      if (!isArray(item)) {\n        // Special type markLine like 'min', 'max', 'average', 'median'\n        var mlType = item.type;\n\n        if (mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median' // In case\n        // data: [{\n        //   yAxis: 10\n        // }]\n        || item.xAxis != null || item.yAxis != null) {\n          var valueAxis = void 0;\n          var value = void 0;\n\n          if (item.yAxis != null || item.xAxis != null) {\n            valueAxis = coordSys.getAxis(item.yAxis != null ? 'y' : 'x');\n            value = retrieve(item.yAxis, item.xAxis);\n          } else {\n            var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel);\n            valueAxis = axisInfo.valueAxis;\n            var valueDataDim = getStackedDimension(data, axisInfo.valueDataDim);\n            value = numCalculate(data, valueDataDim, mlType);\n          }\n\n          var valueIndex = valueAxis.dim === 'x' ? 0 : 1;\n          var baseIndex = 1 - valueIndex; // Normized to 2d data with start and end point\n\n          var mlFrom = clone(item);\n          var mlTo = {\n            coord: []\n          };\n          mlFrom.type = null;\n          mlFrom.coord = [];\n          mlFrom.coord[baseIndex] = -Infinity;\n          mlTo.coord[baseIndex] = Infinity;\n          var precision = mlModel.get('precision');\n\n          if (precision >= 0 && isNumber(value)) {\n            value = +value.toFixed(Math.min(precision, 20));\n          }\n\n          mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;\n          itemArray = [mlFrom, mlTo, {\n            type: mlType,\n            valueIndex: item.valueIndex,\n            // Force to use the value of calculated value.\n            value: value\n          }];\n        } else {\n          // Invalid data\n          if (\"development\" !== 'production') {\n            logError('Invalid markLine data.');\n          }\n\n          itemArray = [];\n        }\n      } else {\n        itemArray = item;\n      }\n\n      var normalizedItem = [dataTransform(seriesModel, itemArray[0]), dataTransform(seriesModel, itemArray[1]), extend({}, itemArray[2])]; // Avoid line data type is extended by from(to) data type\n\n      normalizedItem[2].type = normalizedItem[2].type || null; // Merge from option and to option into line option\n\n      merge(normalizedItem[2], normalizedItem[0]);\n      merge(normalizedItem[2], normalizedItem[1]);\n      return normalizedItem;\n    };\n\n    function isInfinity(val) {\n      return !isNaN(val) && !isFinite(val);\n    } // If a markLine has one dim\n\n\n    function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {\n      var otherDimIndex = 1 - dimIndex;\n      var dimName = coordSys.dimensions[dimIndex];\n      return isInfinity(fromCoord[otherDimIndex]) && isInfinity(toCoord[otherDimIndex]) && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);\n    }\n\n    function markLineFilter(coordSys, item) {\n      if (coordSys.type === 'cartesian2d') {\n        var fromCoord = item[0].coord;\n        var toCoord = item[1].coord; // In case\n        // {\n        //  markLine: {\n        //    data: [{ yAxis: 2 }]\n        //  }\n        // }\n\n        if (fromCoord && toCoord && (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))) {\n          return true;\n        }\n      }\n\n      return dataFilter$1(coordSys, item[0]) && dataFilter$1(coordSys, item[1]);\n    }\n\n    function updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api) {\n      var coordSys = seriesModel.coordinateSystem;\n      var itemModel = data.getItemModel(idx);\n      var point;\n      var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());\n      var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());\n\n      if (!isNaN(xPx) && !isNaN(yPx)) {\n        point = [xPx, yPx];\n      } else {\n        // Chart like bar may have there own marker positioning logic\n        if (seriesModel.getMarkerPosition) {\n          // Use the getMarkerPosition\n          point = seriesModel.getMarkerPosition(data.getValues(data.dimensions, idx));\n        } else {\n          var dims = coordSys.dimensions;\n          var x = data.get(dims[0], idx);\n          var y = data.get(dims[1], idx);\n          point = coordSys.dataToPoint([x, y]);\n        } // Expand line to the edge of grid if value on one axis is Inifnity\n        // In case\n        //  markLine: {\n        //    data: [{\n        //      yAxis: 2\n        //      // or\n        //      type: 'average'\n        //    }]\n        //  }\n\n\n        if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n          // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug\n          var xAxis = coordSys.getAxis('x');\n          var yAxis = coordSys.getAxis('y');\n          var dims = coordSys.dimensions;\n\n          if (isInfinity(data.get(dims[0], idx))) {\n            point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);\n          } else if (isInfinity(data.get(dims[1], idx))) {\n            point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);\n          }\n        } // Use x, y if has any\n\n\n        if (!isNaN(xPx)) {\n          point[0] = xPx;\n        }\n\n        if (!isNaN(yPx)) {\n          point[1] = yPx;\n        }\n      }\n\n      data.setItemLayout(idx, point);\n    }\n\n    var MarkLineView =\n    /** @class */\n    function (_super) {\n      __extends(MarkLineView, _super);\n\n      function MarkLineView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkLineView.type;\n        return _this;\n      }\n\n      MarkLineView.prototype.updateTransform = function (markLineModel, ecModel, api) {\n        ecModel.eachSeries(function (seriesModel) {\n          var mlModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markLine');\n\n          if (mlModel) {\n            var mlData_1 = mlModel.getData();\n            var fromData_1 = inner$i(mlModel).from;\n            var toData_1 = inner$i(mlModel).to; // Update visual and layout of from symbol and to symbol\n\n            fromData_1.each(function (idx) {\n              updateSingleMarkerEndLayout(fromData_1, idx, true, seriesModel, api);\n              updateSingleMarkerEndLayout(toData_1, idx, false, seriesModel, api);\n            }); // Update layout of line\n\n            mlData_1.each(function (idx) {\n              mlData_1.setItemLayout(idx, [fromData_1.getItemLayout(idx), toData_1.getItemLayout(idx)]);\n            });\n            this.markerGroupMap.get(seriesModel.id).updateLayout();\n          }\n        }, this);\n      };\n\n      MarkLineView.prototype.renderSeries = function (seriesModel, mlModel, ecModel, api) {\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesId = seriesModel.id;\n        var seriesData = seriesModel.getData();\n        var lineDrawMap = this.markerGroupMap;\n        var lineDraw = lineDrawMap.get(seriesId) || lineDrawMap.set(seriesId, new LineDraw());\n        this.group.add(lineDraw.group);\n        var mlData = createList$1(coordSys, seriesModel, mlModel);\n        var fromData = mlData.from;\n        var toData = mlData.to;\n        var lineData = mlData.line;\n        inner$i(mlModel).from = fromData;\n        inner$i(mlModel).to = toData; // Line data for tooltip and formatter\n\n        mlModel.setData(lineData); // TODO\n        // Functionally, `symbolSize` & `symbolOffset` can also be 2D array now.\n        // But the related logic and type definition are not finished yet.\n        // Finish it if required\n\n        var symbolType = mlModel.get('symbol');\n        var symbolSize = mlModel.get('symbolSize');\n        var symbolRotate = mlModel.get('symbolRotate');\n        var symbolOffset = mlModel.get('symbolOffset'); // TODO: support callback function like markPoint\n\n        if (!isArray(symbolType)) {\n          symbolType = [symbolType, symbolType];\n        }\n\n        if (!isArray(symbolSize)) {\n          symbolSize = [symbolSize, symbolSize];\n        }\n\n        if (!isArray(symbolRotate)) {\n          symbolRotate = [symbolRotate, symbolRotate];\n        }\n\n        if (!isArray(symbolOffset)) {\n          symbolOffset = [symbolOffset, symbolOffset];\n        } // Update visual and layout of from symbol and to symbol\n\n\n        mlData.from.each(function (idx) {\n          updateDataVisualAndLayout(fromData, idx, true);\n          updateDataVisualAndLayout(toData, idx, false);\n        }); // Update visual and layout of line\n\n        lineData.each(function (idx) {\n          var lineStyle = lineData.getItemModel(idx).getModel('lineStyle').getLineStyle(); // lineData.setItemVisual(idx, {\n          //     color: lineColor || fromData.getItemVisual(idx, 'color')\n          // });\n\n          lineData.setItemLayout(idx, [fromData.getItemLayout(idx), toData.getItemLayout(idx)]);\n\n          if (lineStyle.stroke == null) {\n            lineStyle.stroke = fromData.getItemVisual(idx, 'style').fill;\n          }\n\n          lineData.setItemVisual(idx, {\n            fromSymbolKeepAspect: fromData.getItemVisual(idx, 'symbolKeepAspect'),\n            fromSymbolOffset: fromData.getItemVisual(idx, 'symbolOffset'),\n            fromSymbolRotate: fromData.getItemVisual(idx, 'symbolRotate'),\n            fromSymbolSize: fromData.getItemVisual(idx, 'symbolSize'),\n            fromSymbol: fromData.getItemVisual(idx, 'symbol'),\n            toSymbolKeepAspect: toData.getItemVisual(idx, 'symbolKeepAspect'),\n            toSymbolOffset: toData.getItemVisual(idx, 'symbolOffset'),\n            toSymbolRotate: toData.getItemVisual(idx, 'symbolRotate'),\n            toSymbolSize: toData.getItemVisual(idx, 'symbolSize'),\n            toSymbol: toData.getItemVisual(idx, 'symbol'),\n            style: lineStyle\n          });\n        });\n        lineDraw.updateData(lineData); // Set host model for tooltip\n        // FIXME\n\n        mlData.line.eachItemGraphicEl(function (el) {\n          getECData(el).dataModel = mlModel;\n          el.traverse(function (child) {\n            getECData(child).dataModel = mlModel;\n          });\n        });\n\n        function updateDataVisualAndLayout(data, idx, isFrom) {\n          var itemModel = data.getItemModel(idx);\n          updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api);\n          var style = itemModel.getModel('itemStyle').getItemStyle();\n\n          if (style.fill == null) {\n            style.fill = getVisualFromData(seriesData, 'color');\n          }\n\n          data.setItemVisual(idx, {\n            symbolKeepAspect: itemModel.get('symbolKeepAspect'),\n            // `0` should be considered as a valid value, so use `retrieve2` instead of `||`\n            symbolOffset: retrieve2(itemModel.get('symbolOffset', true), symbolOffset[isFrom ? 0 : 1]),\n            symbolRotate: retrieve2(itemModel.get('symbolRotate', true), symbolRotate[isFrom ? 0 : 1]),\n            // TODO: when 2d array is supported, it should ignore parent\n            symbolSize: retrieve2(itemModel.get('symbolSize'), symbolSize[isFrom ? 0 : 1]),\n            symbol: retrieve2(itemModel.get('symbol', true), symbolType[isFrom ? 0 : 1]),\n            style: style\n          });\n        }\n\n        this.markKeep(lineDraw);\n        lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');\n      };\n\n      MarkLineView.type = 'markLine';\n      return MarkLineView;\n    }(MarkerView);\n\n    function createList$1(coordSys, seriesModel, mlModel) {\n      var coordDimsInfos;\n\n      if (coordSys) {\n        coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {\n          var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n          return extend(extend({}, info), {\n            name: coordDim,\n            // DON'T use ordinalMeta to parse and collect ordinal.\n            ordinalMeta: null\n          });\n        });\n      } else {\n        coordDimsInfos = [{\n          name: 'value',\n          type: 'float'\n        }];\n      }\n\n      var fromData = new SeriesData(coordDimsInfos, mlModel);\n      var toData = new SeriesData(coordDimsInfos, mlModel); // No dimensions\n\n      var lineData = new SeriesData([], mlModel);\n      var optData = map(mlModel.get('data'), curry(markLineTransform, seriesModel, coordSys, mlModel));\n\n      if (coordSys) {\n        optData = filter(optData, curry(markLineFilter, coordSys));\n      }\n\n      var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos);\n      fromData.initData(map(optData, function (item) {\n        return item[0];\n      }), null, dimValueGetter);\n      toData.initData(map(optData, function (item) {\n        return item[1];\n      }), null, dimValueGetter);\n      lineData.initData(map(optData, function (item) {\n        return item[2];\n      }));\n      lineData.hasItemOption = true;\n      return {\n        from: fromData,\n        to: toData,\n        line: lineData\n      };\n    }\n\n    function install$F(registers) {\n      registers.registerComponentModel(MarkLineModel);\n      registers.registerComponentView(MarkLineView);\n      registers.registerPreprocessor(function (opt) {\n        if (checkMarkerInSeries(opt.series, 'markLine')) {\n          // Make sure markLine component is enabled\n          opt.markLine = opt.markLine || {};\n        }\n      });\n    }\n\n    var MarkAreaModel =\n    /** @class */\n    function (_super) {\n      __extends(MarkAreaModel, _super);\n\n      function MarkAreaModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkAreaModel.type;\n        return _this;\n      }\n\n      MarkAreaModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) {\n        return new MarkAreaModel(markerOpt, masterMarkerModel, ecModel);\n      };\n\n      MarkAreaModel.type = 'markArea';\n      MarkAreaModel.defaultOption = {\n        // zlevel: 0,\n        // PENDING\n        z: 1,\n        tooltip: {\n          trigger: 'item'\n        },\n        // markArea should fixed on the coordinate system\n        animation: false,\n        label: {\n          show: true,\n          position: 'top'\n        },\n        itemStyle: {\n          // color and borderColor default to use color from series\n          // color: 'auto'\n          // borderColor: 'auto'\n          borderWidth: 0\n        },\n        emphasis: {\n          label: {\n            show: true,\n            position: 'top'\n          }\n        }\n      };\n      return MarkAreaModel;\n    }(MarkerModel);\n\n    var inner$j = makeInner();\n\n    var markAreaTransform = function (seriesModel, coordSys, maModel, item) {\n      // item may be null\n      var item0 = item[0];\n      var item1 = item[1];\n\n      if (!item0 || !item1) {\n        return;\n      }\n\n      var lt = dataTransform(seriesModel, item0);\n      var rb = dataTransform(seriesModel, item1); // FIXME make sure lt is less than rb\n\n      var ltCoord = lt.coord;\n      var rbCoord = rb.coord;\n      ltCoord[0] = retrieve(ltCoord[0], -Infinity);\n      ltCoord[1] = retrieve(ltCoord[1], -Infinity);\n      rbCoord[0] = retrieve(rbCoord[0], Infinity);\n      rbCoord[1] = retrieve(rbCoord[1], Infinity); // Merge option into one\n\n      var result = mergeAll([{}, lt, rb]);\n      result.coord = [lt.coord, rb.coord];\n      result.x0 = lt.x;\n      result.y0 = lt.y;\n      result.x1 = rb.x;\n      result.y1 = rb.y;\n      return result;\n    };\n\n    function isInfinity$1(val) {\n      return !isNaN(val) && !isFinite(val);\n    } // If a markArea has one dim\n\n\n    function ifMarkAreaHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {\n      var otherDimIndex = 1 - dimIndex;\n      return isInfinity$1(fromCoord[otherDimIndex]) && isInfinity$1(toCoord[otherDimIndex]);\n    }\n\n    function markAreaFilter(coordSys, item) {\n      var fromCoord = item.coord[0];\n      var toCoord = item.coord[1];\n      var item0 = {\n        coord: fromCoord,\n        x: item.x0,\n        y: item.y0\n      };\n      var item1 = {\n        coord: toCoord,\n        x: item.x1,\n        y: item.y1\n      };\n\n      if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n        // In case\n        // {\n        //  markArea: {\n        //    data: [{ yAxis: 2 }]\n        //  }\n        // }\n        if (fromCoord && toCoord && (ifMarkAreaHasOnlyDim(1, fromCoord, toCoord) || ifMarkAreaHasOnlyDim(0, fromCoord, toCoord))) {\n          return true;\n        } // Directly returning true may also do the work,\n        // because markArea will not be shown automatically\n        // when it's not included in coordinate system.\n        // But filtering ahead can avoid keeping rendering markArea\n        // when there are too many of them.\n\n\n        return zoneFilter(coordSys, item0, item1);\n      }\n\n      return dataFilter$1(coordSys, item0) || dataFilter$1(coordSys, item1);\n    } // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0']\n\n\n    function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) {\n      var coordSys = seriesModel.coordinateSystem;\n      var itemModel = data.getItemModel(idx);\n      var point;\n      var xPx = parsePercent$1(itemModel.get(dims[0]), api.getWidth());\n      var yPx = parsePercent$1(itemModel.get(dims[1]), api.getHeight());\n\n      if (!isNaN(xPx) && !isNaN(yPx)) {\n        point = [xPx, yPx];\n      } else {\n        // Chart like bar may have there own marker positioning logic\n        if (seriesModel.getMarkerPosition) {\n          // Consider the case that user input the right-bottom point first\n          // Pick the larger x and y as 'x1' and 'y1'\n          var pointValue0 = data.getValues(['x0', 'y0'], idx);\n          var pointValue1 = data.getValues(['x1', 'y1'], idx);\n          var clampPointValue0 = coordSys.clampData(pointValue0);\n          var clampPointValue1 = coordSys.clampData(pointValue1);\n          var pointValue = [];\n\n          if (dims[0] === 'x0') {\n            pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue1[0] : pointValue0[0];\n          } else {\n            pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue0[0] : pointValue1[0];\n          }\n\n          if (dims[1] === 'y0') {\n            pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue1[1] : pointValue0[1];\n          } else {\n            pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue0[1] : pointValue1[1];\n          } // Use the getMarkerPosition\n\n\n          point = seriesModel.getMarkerPosition(pointValue, dims, true);\n        } else {\n          var x = data.get(dims[0], idx);\n          var y = data.get(dims[1], idx);\n          var pt = [x, y];\n          coordSys.clampData && coordSys.clampData(pt, pt);\n          point = coordSys.dataToPoint(pt, true);\n        }\n\n        if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n          // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug\n          var xAxis = coordSys.getAxis('x');\n          var yAxis = coordSys.getAxis('y');\n          var x = data.get(dims[0], idx);\n          var y = data.get(dims[1], idx);\n\n          if (isInfinity$1(x)) {\n            point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]);\n          } else if (isInfinity$1(y)) {\n            point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]);\n          }\n        } // Use x, y if has any\n\n\n        if (!isNaN(xPx)) {\n          point[0] = xPx;\n        }\n\n        if (!isNaN(yPx)) {\n          point[1] = yPx;\n        }\n      }\n\n      return point;\n    }\n\n    var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']];\n\n    var MarkAreaView =\n    /** @class */\n    function (_super) {\n      __extends(MarkAreaView, _super);\n\n      function MarkAreaView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = MarkAreaView.type;\n        return _this;\n      }\n\n      MarkAreaView.prototype.updateTransform = function (markAreaModel, ecModel, api) {\n        ecModel.eachSeries(function (seriesModel) {\n          var maModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markArea');\n\n          if (maModel) {\n            var areaData_1 = maModel.getData();\n            areaData_1.each(function (idx) {\n              var points = map(dimPermutations, function (dim) {\n                return getSingleMarkerEndPoint(areaData_1, idx, dim, seriesModel, api);\n              }); // Layout\n\n              areaData_1.setItemLayout(idx, points);\n              var el = areaData_1.getItemGraphicEl(idx);\n              el.setShape('points', points);\n            });\n          }\n        }, this);\n      };\n\n      MarkAreaView.prototype.renderSeries = function (seriesModel, maModel, ecModel, api) {\n        var coordSys = seriesModel.coordinateSystem;\n        var seriesId = seriesModel.id;\n        var seriesData = seriesModel.getData();\n        var areaGroupMap = this.markerGroupMap;\n        var polygonGroup = areaGroupMap.get(seriesId) || areaGroupMap.set(seriesId, {\n          group: new Group()\n        });\n        this.group.add(polygonGroup.group);\n        this.markKeep(polygonGroup);\n        var areaData = createList$2(coordSys, seriesModel, maModel); // Line data for tooltip and formatter\n\n        maModel.setData(areaData); // Update visual and layout of line\n\n        areaData.each(function (idx) {\n          // Layout\n          var points = map(dimPermutations, function (dim) {\n            return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);\n          });\n          var xAxisScale = coordSys.getAxis('x').scale;\n          var yAxisScale = coordSys.getAxis('y').scale;\n          var xAxisExtent = xAxisScale.getExtent();\n          var yAxisExtent = yAxisScale.getExtent();\n          var xPointExtent = [xAxisScale.parse(areaData.get('x0', idx)), xAxisScale.parse(areaData.get('x1', idx))];\n          var yPointExtent = [yAxisScale.parse(areaData.get('y0', idx)), yAxisScale.parse(areaData.get('y1', idx))];\n          asc(xPointExtent);\n          asc(yPointExtent);\n          var overlapped = !(xAxisExtent[0] > xPointExtent[1] || xAxisExtent[1] < xPointExtent[0] || yAxisExtent[0] > yPointExtent[1] || yAxisExtent[1] < yPointExtent[0]); // If none of the area is inside coordSys, allClipped is set to be true\n          // in layout so that label will not be displayed. See #12591\n\n          var allClipped = !overlapped;\n          areaData.setItemLayout(idx, {\n            points: points,\n            allClipped: allClipped\n          });\n          var style = areaData.getItemModel(idx).getModel('itemStyle').getItemStyle();\n          var color$1 = getVisualFromData(seriesData, 'color');\n\n          if (!style.fill) {\n            style.fill = color$1;\n\n            if (isString(style.fill)) {\n              style.fill = modifyAlpha(style.fill, 0.4);\n            }\n          }\n\n          if (!style.stroke) {\n            style.stroke = color$1;\n          } // Visual\n\n\n          areaData.setItemVisual(idx, 'style', style);\n        });\n        areaData.diff(inner$j(polygonGroup).data).add(function (idx) {\n          var layout = areaData.getItemLayout(idx);\n\n          if (!layout.allClipped) {\n            var polygon = new Polygon({\n              shape: {\n                points: layout.points\n              }\n            });\n            areaData.setItemGraphicEl(idx, polygon);\n            polygonGroup.group.add(polygon);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var polygon = inner$j(polygonGroup).data.getItemGraphicEl(oldIdx);\n          var layout = areaData.getItemLayout(newIdx);\n\n          if (!layout.allClipped) {\n            if (polygon) {\n              updateProps(polygon, {\n                shape: {\n                  points: layout.points\n                }\n              }, maModel, newIdx);\n            } else {\n              polygon = new Polygon({\n                shape: {\n                  points: layout.points\n                }\n              });\n            }\n\n            areaData.setItemGraphicEl(newIdx, polygon);\n            polygonGroup.group.add(polygon);\n          } else if (polygon) {\n            polygonGroup.group.remove(polygon);\n          }\n        }).remove(function (idx) {\n          var polygon = inner$j(polygonGroup).data.getItemGraphicEl(idx);\n          polygonGroup.group.remove(polygon);\n        }).execute();\n        areaData.eachItemGraphicEl(function (polygon, idx) {\n          var itemModel = areaData.getItemModel(idx);\n          var style = areaData.getItemVisual(idx, 'style');\n          polygon.useStyle(areaData.getItemVisual(idx, 'style'));\n          setLabelStyle(polygon, getLabelStatesModels(itemModel), {\n            labelFetcher: maModel,\n            labelDataIndex: idx,\n            defaultText: areaData.getName(idx) || '',\n            inheritColor: isString(style.fill) ? modifyAlpha(style.fill, 1) : '#000'\n          });\n          setStatesStylesFromModel(polygon, itemModel);\n          toggleHoverEmphasis(polygon, null, null, itemModel.get(['emphasis', 'disabled']));\n          getECData(polygon).dataModel = maModel;\n        });\n        inner$j(polygonGroup).data = areaData;\n        polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent');\n      };\n\n      MarkAreaView.type = 'markArea';\n      return MarkAreaView;\n    }(MarkerView);\n\n    function createList$2(coordSys, seriesModel, maModel) {\n      var areaData;\n      var dataDims;\n      var dims = ['x0', 'y0', 'x1', 'y1'];\n\n      if (coordSys) {\n        var coordDimsInfos_1 = map(coordSys && coordSys.dimensions, function (coordDim) {\n          var data = seriesModel.getData();\n          var info = data.getDimensionInfo(data.mapDimension(coordDim)) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys\n\n          return extend(extend({}, info), {\n            name: coordDim,\n            // DON'T use ordinalMeta to parse and collect ordinal.\n            ordinalMeta: null\n          });\n        });\n        dataDims = map(dims, function (dim, idx) {\n          return {\n            name: dim,\n            type: coordDimsInfos_1[idx % 2].type\n          };\n        });\n        areaData = new SeriesData(dataDims, maModel);\n      } else {\n        dataDims = [{\n          name: 'value',\n          type: 'float'\n        }];\n        areaData = new SeriesData(dataDims, maModel);\n      }\n\n      var optData = map(maModel.get('data'), curry(markAreaTransform, seriesModel, coordSys, maModel));\n\n      if (coordSys) {\n        optData = filter(optData, curry(markAreaFilter, coordSys));\n      }\n\n      var dimValueGetter = coordSys ? function (item, dimName, dataIndex, dimIndex) {\n        // TODO should convert to ParsedValue?\n        var rawVal = item.coord[Math.floor(dimIndex / 2)][dimIndex % 2];\n        return parseDataValue(rawVal, dataDims[dimIndex]);\n      } : function (item, dimName, dataIndex, dimIndex) {\n        return parseDataValue(item.value, dataDims[dimIndex]);\n      };\n      areaData.initData(optData, null, dimValueGetter);\n      areaData.hasItemOption = true;\n      return areaData;\n    }\n\n    function install$G(registers) {\n      registers.registerComponentModel(MarkAreaModel);\n      registers.registerComponentView(MarkAreaView);\n      registers.registerPreprocessor(function (opt) {\n        if (checkMarkerInSeries(opt.series, 'markArea')) {\n          // Make sure markArea component is enabled\n          opt.markArea = opt.markArea || {};\n        }\n      });\n    }\n\n    var getDefaultSelectorOptions = function (ecModel, type) {\n      if (type === 'all') {\n        return {\n          type: 'all',\n          title: ecModel.getLocaleModel().get(['legend', 'selector', 'all'])\n        };\n      } else if (type === 'inverse') {\n        return {\n          type: 'inverse',\n          title: ecModel.getLocaleModel().get(['legend', 'selector', 'inverse'])\n        };\n      }\n    };\n\n    var LegendModel =\n    /** @class */\n    function (_super) {\n      __extends(LegendModel, _super);\n\n      function LegendModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LegendModel.type;\n        _this.layoutMode = {\n          type: 'box',\n          // legend.width/height are maxWidth/maxHeight actually,\n          // whereas real width/height is calculated by its content.\n          // (Setting {left: 10, right: 10} does not make sense).\n          // So consider the case:\n          // `setOption({legend: {left: 10});`\n          // then `setOption({legend: {right: 10});`\n          // The previous `left` should be cleared by setting `ignoreSize`.\n          ignoreSize: true\n        };\n        return _this;\n      }\n\n      LegendModel.prototype.init = function (option, parentModel, ecModel) {\n        this.mergeDefaultAndTheme(option, ecModel);\n        option.selected = option.selected || {};\n\n        this._updateSelector(option);\n      };\n\n      LegendModel.prototype.mergeOption = function (option, ecModel) {\n        _super.prototype.mergeOption.call(this, option, ecModel);\n\n        this._updateSelector(option);\n      };\n\n      LegendModel.prototype._updateSelector = function (option) {\n        var selector = option.selector;\n        var ecModel = this.ecModel;\n\n        if (selector === true) {\n          selector = option.selector = ['all', 'inverse'];\n        }\n\n        if (isArray(selector)) {\n          each(selector, function (item, index) {\n            isString(item) && (item = {\n              type: item\n            });\n            selector[index] = merge(item, getDefaultSelectorOptions(ecModel, item.type));\n          });\n        }\n      };\n\n      LegendModel.prototype.optionUpdated = function () {\n        this._updateData(this.ecModel);\n\n        var legendData = this._data; // If selectedMode is single, try to select one\n\n        if (legendData[0] && this.get('selectedMode') === 'single') {\n          var hasSelected = false; // If has any selected in option.selected\n\n          for (var i = 0; i < legendData.length; i++) {\n            var name_1 = legendData[i].get('name');\n\n            if (this.isSelected(name_1)) {\n              // Force to unselect others\n              this.select(name_1);\n              hasSelected = true;\n              break;\n            }\n          } // Try select the first if selectedMode is single\n\n\n          !hasSelected && this.select(legendData[0].get('name'));\n        }\n      };\n\n      LegendModel.prototype._updateData = function (ecModel) {\n        var potentialData = [];\n        var availableNames = [];\n        ecModel.eachRawSeries(function (seriesModel) {\n          var seriesName = seriesModel.name;\n          availableNames.push(seriesName);\n          var isPotential;\n\n          if (seriesModel.legendVisualProvider) {\n            var provider = seriesModel.legendVisualProvider;\n            var names = provider.getAllNames();\n\n            if (!ecModel.isSeriesFiltered(seriesModel)) {\n              availableNames = availableNames.concat(names);\n            }\n\n            if (names.length) {\n              potentialData = potentialData.concat(names);\n            } else {\n              isPotential = true;\n            }\n          } else {\n            isPotential = true;\n          }\n\n          if (isPotential && isNameSpecified(seriesModel)) {\n            potentialData.push(seriesModel.name);\n          }\n        });\n        /**\n         * @type {Array.<string>}\n         * @private\n         */\n\n        this._availableNames = availableNames; // If legend.data is not specified in option, use availableNames as data,\n        // which is convenient for user preparing option.\n\n        var rawData = this.get('data') || potentialData;\n        var legendNameMap = createHashMap();\n        var legendData = map(rawData, function (dataItem) {\n          // Can be string or number\n          if (isString(dataItem) || isNumber(dataItem)) {\n            dataItem = {\n              name: dataItem\n            };\n          }\n\n          if (legendNameMap.get(dataItem.name)) {\n            // remove legend name duplicate\n            return null;\n          }\n\n          legendNameMap.set(dataItem.name, true);\n          return new Model(dataItem, this, this.ecModel);\n        }, this);\n        /**\n         * @type {Array.<module:echarts/model/Model>}\n         * @private\n         */\n\n        this._data = filter(legendData, function (item) {\n          return !!item;\n        });\n      };\n\n      LegendModel.prototype.getData = function () {\n        return this._data;\n      };\n\n      LegendModel.prototype.select = function (name) {\n        var selected = this.option.selected;\n        var selectedMode = this.get('selectedMode');\n\n        if (selectedMode === 'single') {\n          var data = this._data;\n          each(data, function (dataItem) {\n            selected[dataItem.get('name')] = false;\n          });\n        }\n\n        selected[name] = true;\n      };\n\n      LegendModel.prototype.unSelect = function (name) {\n        if (this.get('selectedMode') !== 'single') {\n          this.option.selected[name] = false;\n        }\n      };\n\n      LegendModel.prototype.toggleSelected = function (name) {\n        var selected = this.option.selected; // Default is true\n\n        if (!selected.hasOwnProperty(name)) {\n          selected[name] = true;\n        }\n\n        this[selected[name] ? 'unSelect' : 'select'](name);\n      };\n\n      LegendModel.prototype.allSelect = function () {\n        var data = this._data;\n        var selected = this.option.selected;\n        each(data, function (dataItem) {\n          selected[dataItem.get('name', true)] = true;\n        });\n      };\n\n      LegendModel.prototype.inverseSelect = function () {\n        var data = this._data;\n        var selected = this.option.selected;\n        each(data, function (dataItem) {\n          var name = dataItem.get('name', true); // Initially, default value is true\n\n          if (!selected.hasOwnProperty(name)) {\n            selected[name] = true;\n          }\n\n          selected[name] = !selected[name];\n        });\n      };\n\n      LegendModel.prototype.isSelected = function (name) {\n        var selected = this.option.selected;\n        return !(selected.hasOwnProperty(name) && !selected[name]) && indexOf(this._availableNames, name) >= 0;\n      };\n\n      LegendModel.prototype.getOrient = function () {\n        return this.get('orient') === 'vertical' ? {\n          index: 1,\n          name: 'vertical'\n        } : {\n          index: 0,\n          name: 'horizontal'\n        };\n      };\n\n      LegendModel.type = 'legend.plain';\n      LegendModel.dependencies = ['series'];\n      LegendModel.defaultOption = {\n        // zlevel: 0,\n        z: 4,\n        show: true,\n        orient: 'horizontal',\n        left: 'center',\n        // right: 'center',\n        top: 0,\n        // bottom: null,\n        align: 'auto',\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderColor: '#ccc',\n        borderRadius: 0,\n        borderWidth: 0,\n        padding: 5,\n        itemGap: 10,\n        itemWidth: 25,\n        itemHeight: 14,\n        symbolRotate: 'inherit',\n        symbolKeepAspect: true,\n        inactiveColor: '#ccc',\n        inactiveBorderColor: '#ccc',\n        inactiveBorderWidth: 'auto',\n        itemStyle: {\n          color: 'inherit',\n          opacity: 'inherit',\n          borderColor: 'inherit',\n          borderWidth: 'auto',\n          borderCap: 'inherit',\n          borderJoin: 'inherit',\n          borderDashOffset: 'inherit',\n          borderMiterLimit: 'inherit'\n        },\n        lineStyle: {\n          width: 'auto',\n          color: 'inherit',\n          inactiveColor: '#ccc',\n          inactiveWidth: 2,\n          opacity: 'inherit',\n          type: 'inherit',\n          cap: 'inherit',\n          join: 'inherit',\n          dashOffset: 'inherit',\n          miterLimit: 'inherit'\n        },\n        textStyle: {\n          color: '#333'\n        },\n        selectedMode: true,\n        selector: false,\n        selectorLabel: {\n          show: true,\n          borderRadius: 10,\n          padding: [3, 5, 3, 5],\n          fontSize: 12,\n          fontFamily: 'sans-serif',\n          color: '#666',\n          borderWidth: 1,\n          borderColor: '#666'\n        },\n        emphasis: {\n          selectorLabel: {\n            show: true,\n            color: '#eee',\n            backgroundColor: '#666'\n          }\n        },\n        selectorPosition: 'auto',\n        selectorItemGap: 7,\n        selectorButtonGap: 10,\n        tooltip: {\n          show: false\n        }\n      };\n      return LegendModel;\n    }(ComponentModel);\n\n    var curry$1 = curry;\n    var each$c = each;\n    var Group$2 = Group;\n\n    var LegendView =\n    /** @class */\n    function (_super) {\n      __extends(LegendView, _super);\n\n      function LegendView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LegendView.type;\n        _this.newlineDisabled = false;\n        return _this;\n      }\n\n      LegendView.prototype.init = function () {\n        this.group.add(this._contentGroup = new Group$2());\n        this.group.add(this._selectorGroup = new Group$2());\n        this._isFirstRender = true;\n      };\n      /**\n       * @protected\n       */\n\n\n      LegendView.prototype.getContentGroup = function () {\n        return this._contentGroup;\n      };\n      /**\n       * @protected\n       */\n\n\n      LegendView.prototype.getSelectorGroup = function () {\n        return this._selectorGroup;\n      };\n      /**\n       * @override\n       */\n\n\n      LegendView.prototype.render = function (legendModel, ecModel, api) {\n        var isFirstRender = this._isFirstRender;\n        this._isFirstRender = false;\n        this.resetInner();\n\n        if (!legendModel.get('show', true)) {\n          return;\n        }\n\n        var itemAlign = legendModel.get('align');\n        var orient = legendModel.get('orient');\n\n        if (!itemAlign || itemAlign === 'auto') {\n          itemAlign = legendModel.get('left') === 'right' && orient === 'vertical' ? 'right' : 'left';\n        } // selector has been normalized to an array in model\n\n\n        var selector = legendModel.get('selector', true);\n        var selectorPosition = legendModel.get('selectorPosition', true);\n\n        if (selector && (!selectorPosition || selectorPosition === 'auto')) {\n          selectorPosition = orient === 'horizontal' ? 'end' : 'start';\n        }\n\n        this.renderInner(itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition); // Perform layout.\n\n        var positionInfo = legendModel.getBoxLayoutParams();\n        var viewportSize = {\n          width: api.getWidth(),\n          height: api.getHeight()\n        };\n        var padding = legendModel.get('padding');\n        var maxSize = getLayoutRect(positionInfo, viewportSize, padding);\n        var mainRect = this.layoutInner(legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition); // Place mainGroup, based on the calculated `mainRect`.\n\n        var layoutRect = getLayoutRect(defaults({\n          width: mainRect.width,\n          height: mainRect.height\n        }, positionInfo), viewportSize, padding);\n        this.group.x = layoutRect.x - mainRect.x;\n        this.group.y = layoutRect.y - mainRect.y;\n        this.group.markRedraw(); // Render background after group is layout.\n\n        this.group.add(this._backgroundEl = makeBackground(mainRect, legendModel));\n      };\n\n      LegendView.prototype.resetInner = function () {\n        this.getContentGroup().removeAll();\n        this._backgroundEl && this.group.remove(this._backgroundEl);\n        this.getSelectorGroup().removeAll();\n      };\n\n      LegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {\n        var contentGroup = this.getContentGroup();\n        var legendDrawnMap = createHashMap();\n        var selectMode = legendModel.get('selectedMode');\n        var excludeSeriesId = [];\n        ecModel.eachRawSeries(function (seriesModel) {\n          !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id);\n        });\n        each$c(legendModel.getData(), function (legendItemModel, dataIndex) {\n          var name = legendItemModel.get('name'); // Use empty string or \\n as a newline string\n\n          if (!this.newlineDisabled && (name === '' || name === '\\n')) {\n            var g = new Group$2(); // @ts-ignore\n\n            g.newline = true;\n            contentGroup.add(g);\n            return;\n          } // Representitive series.\n\n\n          var seriesModel = ecModel.getSeriesByName(name)[0];\n\n          if (legendDrawnMap.get(name)) {\n            // Have been drawn\n            return;\n          } // Legend to control series.\n\n\n          if (seriesModel) {\n            var data = seriesModel.getData();\n            var lineVisualStyle = data.getVisual('legendLineStyle') || {};\n            var legendIcon = data.getVisual('legendIcon');\n            /**\n             * `data.getVisual('style')` may be the color from the register\n             * in series. For example, for line series,\n             */\n\n            var style = data.getVisual('style');\n\n            var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, style, legendIcon, selectMode, api);\n\n            itemGroup.on('click', curry$1(dispatchSelectAction, name, null, api, excludeSeriesId)).on('mouseover', curry$1(dispatchHighlightAction, seriesModel.name, null, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, seriesModel.name, null, api, excludeSeriesId));\n            legendDrawnMap.set(name, true);\n          } else {\n            // Legend to control data. In pie and funnel.\n            ecModel.eachRawSeries(function (seriesModel) {\n              // In case multiple series has same data name\n              if (legendDrawnMap.get(name)) {\n                return;\n              }\n\n              if (seriesModel.legendVisualProvider) {\n                var provider = seriesModel.legendVisualProvider;\n\n                if (!provider.containName(name)) {\n                  return;\n                }\n\n                var idx = provider.indexOfName(name);\n                var style = provider.getItemVisual(idx, 'style');\n                var legendIcon = provider.getItemVisual(idx, 'legendIcon');\n                var colorArr = parse(style.fill); // Color may be set to transparent in visualMap when data is out of range.\n                // Do not show nothing.\n\n                if (colorArr && colorArr[3] === 0) {\n                  colorArr[3] = 0.2; // TODO color is set to 0, 0, 0, 0. Should show correct RGBA\n\n                  style = extend(extend({}, style), {\n                    fill: stringify(colorArr, 'rgba')\n                  });\n                }\n\n                var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, {}, style, legendIcon, selectMode, api); // FIXME: consider different series has items with the same name.\n\n\n                itemGroup.on('click', curry$1(dispatchSelectAction, null, name, api, excludeSeriesId)) // Should not specify the series name, consider legend controls\n                // more than one pie series.\n                .on('mouseover', curry$1(dispatchHighlightAction, null, name, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, null, name, api, excludeSeriesId));\n                legendDrawnMap.set(name, true);\n              }\n            }, this);\n          }\n\n          if (\"development\" !== 'production') {\n            if (!legendDrawnMap.get(name)) {\n              console.warn(name + ' series not exists. Legend data should be same with series name or data name.');\n            }\n          }\n        }, this);\n\n        if (selector) {\n          this._createSelector(selector, legendModel, api, orient, selectorPosition);\n        }\n      };\n\n      LegendView.prototype._createSelector = function (selector, legendModel, api, orient, selectorPosition) {\n        var selectorGroup = this.getSelectorGroup();\n        each$c(selector, function createSelectorButton(selectorItem) {\n          var type = selectorItem.type;\n          var labelText = new ZRText({\n            style: {\n              x: 0,\n              y: 0,\n              align: 'center',\n              verticalAlign: 'middle'\n            },\n            onclick: function () {\n              api.dispatchAction({\n                type: type === 'all' ? 'legendAllSelect' : 'legendInverseSelect'\n              });\n            }\n          });\n          selectorGroup.add(labelText);\n          var labelModel = legendModel.getModel('selectorLabel');\n          var emphasisLabelModel = legendModel.getModel(['emphasis', 'selectorLabel']);\n          setLabelStyle(labelText, {\n            normal: labelModel,\n            emphasis: emphasisLabelModel\n          }, {\n            defaultText: selectorItem.title\n          });\n          enableHoverEmphasis(labelText);\n        });\n      };\n\n      LegendView.prototype._createItem = function (seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, itemVisualStyle, legendIcon, selectMode, api) {\n        var drawType = seriesModel.visualDrawType;\n        var itemWidth = legendModel.get('itemWidth');\n        var itemHeight = legendModel.get('itemHeight');\n        var isSelected = legendModel.isSelected(name);\n        var iconRotate = legendItemModel.get('symbolRotate');\n        var symbolKeepAspect = legendItemModel.get('symbolKeepAspect');\n        var legendIconType = legendItemModel.get('icon');\n        legendIcon = legendIconType || legendIcon || 'roundRect';\n        var style = getLegendStyle(legendIcon, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api);\n        var itemGroup = new Group$2();\n        var textStyleModel = legendItemModel.getModel('textStyle');\n\n        if (isFunction(seriesModel.getLegendIcon) && (!legendIconType || legendIconType === 'inherit')) {\n          // Series has specific way to define legend icon\n          itemGroup.add(seriesModel.getLegendIcon({\n            itemWidth: itemWidth,\n            itemHeight: itemHeight,\n            icon: legendIcon,\n            iconRotate: iconRotate,\n            itemStyle: style.itemStyle,\n            lineStyle: style.lineStyle,\n            symbolKeepAspect: symbolKeepAspect\n          }));\n        } else {\n          // Use default legend icon policy for most series\n          var rotate = legendIconType === 'inherit' && seriesModel.getData().getVisual('symbol') ? iconRotate === 'inherit' ? seriesModel.getData().getVisual('symbolRotate') : iconRotate : 0; // No rotation for no icon\n\n          itemGroup.add(getDefaultLegendIcon({\n            itemWidth: itemWidth,\n            itemHeight: itemHeight,\n            icon: legendIcon,\n            iconRotate: rotate,\n            itemStyle: style.itemStyle,\n            lineStyle: style.lineStyle,\n            symbolKeepAspect: symbolKeepAspect\n          }));\n        }\n\n        var textX = itemAlign === 'left' ? itemWidth + 5 : -5;\n        var textAlign = itemAlign;\n        var formatter = legendModel.get('formatter');\n        var content = name;\n\n        if (isString(formatter) && formatter) {\n          content = formatter.replace('{name}', name != null ? name : '');\n        } else if (isFunction(formatter)) {\n          content = formatter(name);\n        }\n\n        var inactiveColor = legendItemModel.get('inactiveColor');\n        itemGroup.add(new ZRText({\n          style: createTextStyle(textStyleModel, {\n            text: content,\n            x: textX,\n            y: itemHeight / 2,\n            fill: isSelected ? textStyleModel.getTextColor() : inactiveColor,\n            align: textAlign,\n            verticalAlign: 'middle'\n          })\n        })); // Add a invisible rect to increase the area of mouse hover\n\n        var hitRect = new Rect({\n          shape: itemGroup.getBoundingRect(),\n          invisible: true\n        });\n        var tooltipModel = legendItemModel.getModel('tooltip');\n\n        if (tooltipModel.get('show')) {\n          setTooltipConfig({\n            el: hitRect,\n            componentModel: legendModel,\n            itemName: name,\n            itemTooltipOption: tooltipModel.option\n          });\n        }\n\n        itemGroup.add(hitRect);\n        itemGroup.eachChild(function (child) {\n          child.silent = true;\n        });\n        hitRect.silent = !selectMode;\n        this.getContentGroup().add(itemGroup);\n        enableHoverEmphasis(itemGroup); // @ts-ignore\n\n        itemGroup.__legendDataIndex = dataIndex;\n        return itemGroup;\n      };\n\n      LegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {\n        var contentGroup = this.getContentGroup();\n        var selectorGroup = this.getSelectorGroup(); // Place items in contentGroup.\n\n        box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), maxSize.width, maxSize.height);\n        var contentRect = contentGroup.getBoundingRect();\n        var contentPos = [-contentRect.x, -contentRect.y];\n        selectorGroup.markRedraw();\n        contentGroup.markRedraw();\n\n        if (selector) {\n          // Place buttons in selectorGroup\n          box( // Buttons in selectorGroup always layout horizontally\n          'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));\n          var selectorRect = selectorGroup.getBoundingRect();\n          var selectorPos = [-selectorRect.x, -selectorRect.y];\n          var selectorButtonGap = legendModel.get('selectorButtonGap', true);\n          var orientIdx = legendModel.getOrient().index;\n          var wh = orientIdx === 0 ? 'width' : 'height';\n          var hw = orientIdx === 0 ? 'height' : 'width';\n          var yx = orientIdx === 0 ? 'y' : 'x';\n\n          if (selectorPosition === 'end') {\n            selectorPos[orientIdx] += contentRect[wh] + selectorButtonGap;\n          } else {\n            contentPos[orientIdx] += selectorRect[wh] + selectorButtonGap;\n          } // Always align selector to content as 'middle'\n\n\n          selectorPos[1 - orientIdx] += contentRect[hw] / 2 - selectorRect[hw] / 2;\n          selectorGroup.x = selectorPos[0];\n          selectorGroup.y = selectorPos[1];\n          contentGroup.x = contentPos[0];\n          contentGroup.y = contentPos[1];\n          var mainRect = {\n            x: 0,\n            y: 0\n          };\n          mainRect[wh] = contentRect[wh] + selectorButtonGap + selectorRect[wh];\n          mainRect[hw] = Math.max(contentRect[hw], selectorRect[hw]);\n          mainRect[yx] = Math.min(0, selectorRect[yx] + selectorPos[1 - orientIdx]);\n          return mainRect;\n        } else {\n          contentGroup.x = contentPos[0];\n          contentGroup.y = contentPos[1];\n          return this.group.getBoundingRect();\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      LegendView.prototype.remove = function () {\n        this.getContentGroup().removeAll();\n        this._isFirstRender = true;\n      };\n\n      LegendView.type = 'legend.plain';\n      return LegendView;\n    }(ComponentView);\n\n    function getLegendStyle(iconType, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api) {\n      /**\n       * Use series style if is inherit;\n       * elsewise, use legend style\n       */\n      function handleCommonProps(style, visualStyle) {\n        // If lineStyle.width is 'auto', it is set to be 2 if series has border\n        if (style.lineWidth === 'auto') {\n          style.lineWidth = visualStyle.lineWidth > 0 ? 2 : 0;\n        }\n\n        each$c(style, function (propVal, propName) {\n          style[propName] === 'inherit' && (style[propName] = visualStyle[propName]);\n        });\n      } // itemStyle\n\n\n      var itemStyleModel = legendItemModel.getModel('itemStyle');\n      var itemStyle = itemStyleModel.getItemStyle();\n      var iconBrushType = iconType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke';\n      var decalStyle = itemStyleModel.getShallow('decal');\n      itemStyle.decal = !decalStyle || decalStyle === 'inherit' ? itemVisualStyle.decal : createOrUpdatePatternFromDecal(decalStyle, api);\n\n      if (itemStyle.fill === 'inherit') {\n        /**\n         * Series with visualDrawType as 'stroke' should have\n         * series stroke as legend fill\n         */\n        itemStyle.fill = itemVisualStyle[drawType];\n      }\n\n      if (itemStyle.stroke === 'inherit') {\n        /**\n         * icon type with \"emptyXXX\" should use fill color\n         * in visual style\n         */\n        itemStyle.stroke = itemVisualStyle[iconBrushType];\n      }\n\n      if (itemStyle.opacity === 'inherit') {\n        /**\n         * Use lineStyle.opacity if drawType is stroke\n         */\n        itemStyle.opacity = (drawType === 'fill' ? itemVisualStyle : lineVisualStyle).opacity;\n      }\n\n      handleCommonProps(itemStyle, itemVisualStyle); // lineStyle\n\n      var legendLineModel = legendItemModel.getModel('lineStyle');\n      var lineStyle = legendLineModel.getLineStyle();\n      handleCommonProps(lineStyle, lineVisualStyle); // Fix auto color to real color\n\n      itemStyle.fill === 'auto' && (itemStyle.fill = itemVisualStyle.fill);\n      itemStyle.stroke === 'auto' && (itemStyle.stroke = itemVisualStyle.fill);\n      lineStyle.stroke === 'auto' && (lineStyle.stroke = itemVisualStyle.fill);\n\n      if (!isSelected) {\n        var borderWidth = legendItemModel.get('inactiveBorderWidth');\n        /**\n         * Since stroke is set to be inactiveBorderColor, it may occur that\n         * there is no border in series but border in legend, so we need to\n         * use border only when series has border if is set to be auto\n         */\n\n        var visualHasBorder = itemStyle[iconBrushType];\n        itemStyle.lineWidth = borderWidth === 'auto' ? itemVisualStyle.lineWidth > 0 && visualHasBorder ? 2 : 0 : itemStyle.lineWidth;\n        itemStyle.fill = legendItemModel.get('inactiveColor');\n        itemStyle.stroke = legendItemModel.get('inactiveBorderColor');\n        lineStyle.stroke = legendLineModel.get('inactiveColor');\n        lineStyle.lineWidth = legendLineModel.get('inactiveWidth');\n      }\n\n      return {\n        itemStyle: itemStyle,\n        lineStyle: lineStyle\n      };\n    }\n\n    function getDefaultLegendIcon(opt) {\n      var symboType = opt.icon || 'roundRect';\n      var icon = createSymbol(symboType, 0, 0, opt.itemWidth, opt.itemHeight, opt.itemStyle.fill, opt.symbolKeepAspect);\n      icon.setStyle(opt.itemStyle);\n      icon.rotation = (opt.iconRotate || 0) * Math.PI / 180;\n      icon.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);\n\n      if (symboType.indexOf('empty') > -1) {\n        icon.style.stroke = icon.style.fill;\n        icon.style.fill = '#fff';\n        icon.style.lineWidth = 2;\n      }\n\n      return icon;\n    }\n\n    function dispatchSelectAction(seriesName, dataName, api, excludeSeriesId) {\n      // downplay before unselect\n      dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId);\n      api.dispatchAction({\n        type: 'legendToggleSelect',\n        name: seriesName != null ? seriesName : dataName\n      }); // highlight after select\n      // TODO highlight immediately may cause animation loss.\n\n      dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId);\n    }\n\n    function isUseHoverLayer(api) {\n      var list = api.getZr().storage.getDisplayList();\n      var emphasisState;\n      var i = 0;\n      var len = list.length;\n\n      while (i < len && !(emphasisState = list[i].states.emphasis)) {\n        i++;\n      }\n\n      return emphasisState && emphasisState.hoverLayer;\n    }\n\n    function dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId) {\n      // If element hover will move to a hoverLayer.\n      if (!isUseHoverLayer(api)) {\n        api.dispatchAction({\n          type: 'highlight',\n          seriesName: seriesName,\n          name: dataName,\n          excludeSeriesId: excludeSeriesId\n        });\n      }\n    }\n\n    function dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId) {\n      // If element hover will move to a hoverLayer.\n      if (!isUseHoverLayer(api)) {\n        api.dispatchAction({\n          type: 'downplay',\n          seriesName: seriesName,\n          name: dataName,\n          excludeSeriesId: excludeSeriesId\n        });\n      }\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function legendFilter(ecModel) {\n      var legendModels = ecModel.findComponents({\n        mainType: 'legend'\n      });\n\n      if (legendModels && legendModels.length) {\n        ecModel.filterSeries(function (series) {\n          // If in any legend component the status is not selected.\n          // Because in legend series is assumed selected when it is not in the legend data.\n          for (var i = 0; i < legendModels.length; i++) {\n            if (!legendModels[i].isSelected(series.name)) {\n              return false;\n            }\n          }\n\n          return true;\n        });\n      }\n    }\n\n    function legendSelectActionHandler(methodName, payload, ecModel) {\n      var selectedMap = {};\n      var isToggleSelect = methodName === 'toggleSelected';\n      var isSelected; // Update all legend components\n\n      ecModel.eachComponent('legend', function (legendModel) {\n        if (isToggleSelect && isSelected != null) {\n          // Force other legend has same selected status\n          // Or the first is toggled to true and other are toggled to false\n          // In the case one legend has some item unSelected in option. And if other legend\n          // doesn't has the item, they will assume it is selected.\n          legendModel[isSelected ? 'select' : 'unSelect'](payload.name);\n        } else if (methodName === 'allSelect' || methodName === 'inverseSelect') {\n          legendModel[methodName]();\n        } else {\n          legendModel[methodName](payload.name);\n          isSelected = legendModel.isSelected(payload.name);\n        }\n\n        var legendData = legendModel.getData();\n        each(legendData, function (model) {\n          var name = model.get('name'); // Wrap element\n\n          if (name === '\\n' || name === '') {\n            return;\n          }\n\n          var isItemSelected = legendModel.isSelected(name);\n\n          if (selectedMap.hasOwnProperty(name)) {\n            // Unselected if any legend is unselected\n            selectedMap[name] = selectedMap[name] && isItemSelected;\n          } else {\n            selectedMap[name] = isItemSelected;\n          }\n        });\n      }); // Return the event explicitly\n\n      return methodName === 'allSelect' || methodName === 'inverseSelect' ? {\n        selected: selectedMap\n      } : {\n        name: payload.name,\n        selected: selectedMap\n      };\n    }\n\n    function installLegendAction(registers) {\n      /**\n       * @event legendToggleSelect\n       * @type {Object}\n       * @property {string} type 'legendToggleSelect'\n       * @property {string} [from]\n       * @property {string} name Series name or data item name\n       */\n      registers.registerAction('legendToggleSelect', 'legendselectchanged', curry(legendSelectActionHandler, 'toggleSelected'));\n      registers.registerAction('legendAllSelect', 'legendselectall', curry(legendSelectActionHandler, 'allSelect'));\n      registers.registerAction('legendInverseSelect', 'legendinverseselect', curry(legendSelectActionHandler, 'inverseSelect'));\n      /**\n       * @event legendSelect\n       * @type {Object}\n       * @property {string} type 'legendSelect'\n       * @property {string} name Series name or data item name\n       */\n\n      registers.registerAction('legendSelect', 'legendselected', curry(legendSelectActionHandler, 'select'));\n      /**\n       * @event legendUnSelect\n       * @type {Object}\n       * @property {string} type 'legendUnSelect'\n       * @property {string} name Series name or data item name\n       */\n\n      registers.registerAction('legendUnSelect', 'legendunselected', curry(legendSelectActionHandler, 'unSelect'));\n    }\n\n    function install$H(registers) {\n      registers.registerComponentModel(LegendModel);\n      registers.registerComponentView(LegendView);\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.SERIES_FILTER, legendFilter);\n      registers.registerSubTypeDefaulter('legend', function () {\n        return 'plain';\n      });\n      installLegendAction(registers);\n    }\n\n    var ScrollableLegendModel =\n    /** @class */\n    function (_super) {\n      __extends(ScrollableLegendModel, _super);\n\n      function ScrollableLegendModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScrollableLegendModel.type;\n        return _this;\n      }\n      /**\n       * @param {number} scrollDataIndex\n       */\n\n\n      ScrollableLegendModel.prototype.setScrollDataIndex = function (scrollDataIndex) {\n        this.option.scrollDataIndex = scrollDataIndex;\n      };\n\n      ScrollableLegendModel.prototype.init = function (option, parentModel, ecModel) {\n        var inputPositionParams = getLayoutParams(option);\n\n        _super.prototype.init.call(this, option, parentModel, ecModel);\n\n        mergeAndNormalizeLayoutParams$1(this, option, inputPositionParams);\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendModel.prototype.mergeOption = function (option, ecModel) {\n        _super.prototype.mergeOption.call(this, option, ecModel);\n\n        mergeAndNormalizeLayoutParams$1(this, this.option, option);\n      };\n\n      ScrollableLegendModel.type = 'legend.scroll';\n      ScrollableLegendModel.defaultOption = inheritDefaultOption(LegendModel.defaultOption, {\n        scrollDataIndex: 0,\n        pageButtonItemGap: 5,\n        pageButtonGap: null,\n        pageButtonPosition: 'end',\n        pageFormatter: '{current}/{total}',\n        pageIcons: {\n          horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'],\n          vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z']\n        },\n        pageIconColor: '#2f4554',\n        pageIconInactiveColor: '#aaa',\n        pageIconSize: 15,\n        pageTextStyle: {\n          color: '#333'\n        },\n        animationDurationUpdate: 800\n      });\n      return ScrollableLegendModel;\n    }(LegendModel);\n\n    function mergeAndNormalizeLayoutParams$1(legendModel, target, raw) {\n      var orient = legendModel.getOrient();\n      var ignoreSize = [1, 1];\n      ignoreSize[orient.index] = 0;\n      mergeLayoutParam(target, raw, {\n        type: 'box',\n        ignoreSize: !!ignoreSize\n      });\n    }\n\n    var Group$3 = Group;\n    var WH$1 = ['width', 'height'];\n    var XY$1 = ['x', 'y'];\n\n    var ScrollableLegendView =\n    /** @class */\n    function (_super) {\n      __extends(ScrollableLegendView, _super);\n\n      function ScrollableLegendView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ScrollableLegendView.type;\n        _this.newlineDisabled = true;\n        _this._currentIndex = 0;\n        return _this;\n      }\n\n      ScrollableLegendView.prototype.init = function () {\n        _super.prototype.init.call(this);\n\n        this.group.add(this._containerGroup = new Group$3());\n\n        this._containerGroup.add(this.getContentGroup());\n\n        this.group.add(this._controllerGroup = new Group$3());\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendView.prototype.resetInner = function () {\n        _super.prototype.resetInner.call(this);\n\n        this._controllerGroup.removeAll();\n\n        this._containerGroup.removeClipPath();\n\n        this._containerGroup.__rectSize = null;\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {\n        var self = this; // Render content items.\n\n        _super.prototype.renderInner.call(this, itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition);\n\n        var controllerGroup = this._controllerGroup; // FIXME: support be 'auto' adapt to size number text length,\n        // e.g., '3/12345' should not overlap with the control arrow button.\n\n        var pageIconSize = legendModel.get('pageIconSize', true);\n        var pageIconSizeArr = isArray(pageIconSize) ? pageIconSize : [pageIconSize, pageIconSize];\n        createPageButton('pagePrev', 0);\n        var pageTextStyleModel = legendModel.getModel('pageTextStyle');\n        controllerGroup.add(new ZRText({\n          name: 'pageText',\n          style: {\n            // Placeholder to calculate a proper layout.\n            text: 'xx/xx',\n            fill: pageTextStyleModel.getTextColor(),\n            font: pageTextStyleModel.getFont(),\n            verticalAlign: 'middle',\n            align: 'center'\n          },\n          silent: true\n        }));\n        createPageButton('pageNext', 1);\n\n        function createPageButton(name, iconIdx) {\n          var pageDataIndexName = name + 'DataIndex';\n          var icon = createIcon(legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], {\n            // Buttons will be created in each render, so we do not need\n            // to worry about avoiding using legendModel kept in scope.\n            onclick: bind(self._pageGo, self, pageDataIndexName, legendModel, api)\n          }, {\n            x: -pageIconSizeArr[0] / 2,\n            y: -pageIconSizeArr[1] / 2,\n            width: pageIconSizeArr[0],\n            height: pageIconSizeArr[1]\n          });\n          icon.name = name;\n          controllerGroup.add(icon);\n        }\n      };\n      /**\n       * @override\n       */\n\n\n      ScrollableLegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {\n        var selectorGroup = this.getSelectorGroup();\n        var orientIdx = legendModel.getOrient().index;\n        var wh = WH$1[orientIdx];\n        var xy = XY$1[orientIdx];\n        var hw = WH$1[1 - orientIdx];\n        var yx = XY$1[1 - orientIdx];\n        selector && box( // Buttons in selectorGroup always layout horizontally\n        'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));\n        var selectorButtonGap = legendModel.get('selectorButtonGap', true);\n        var selectorRect = selectorGroup.getBoundingRect();\n        var selectorPos = [-selectorRect.x, -selectorRect.y];\n        var processMaxSize = clone(maxSize);\n        selector && (processMaxSize[wh] = maxSize[wh] - selectorRect[wh] - selectorButtonGap);\n\n        var mainRect = this._layoutContentAndController(legendModel, isFirstRender, processMaxSize, orientIdx, wh, hw, yx, xy);\n\n        if (selector) {\n          if (selectorPosition === 'end') {\n            selectorPos[orientIdx] += mainRect[wh] + selectorButtonGap;\n          } else {\n            var offset = selectorRect[wh] + selectorButtonGap;\n            selectorPos[orientIdx] -= offset;\n            mainRect[xy] -= offset;\n          }\n\n          mainRect[wh] += selectorRect[wh] + selectorButtonGap;\n          selectorPos[1 - orientIdx] += mainRect[yx] + mainRect[hw] / 2 - selectorRect[hw] / 2;\n          mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw]);\n          mainRect[yx] = Math.min(mainRect[yx], selectorRect[yx] + selectorPos[1 - orientIdx]);\n          selectorGroup.x = selectorPos[0];\n          selectorGroup.y = selectorPos[1];\n          selectorGroup.markRedraw();\n        }\n\n        return mainRect;\n      };\n\n      ScrollableLegendView.prototype._layoutContentAndController = function (legendModel, isFirstRender, maxSize, orientIdx, wh, hw, yx, xy) {\n        var contentGroup = this.getContentGroup();\n        var containerGroup = this._containerGroup;\n        var controllerGroup = this._controllerGroup; // Place items in contentGroup.\n\n        box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), !orientIdx ? null : maxSize.width, orientIdx ? null : maxSize.height);\n        box( // Buttons in controller are layout always horizontally.\n        'horizontal', controllerGroup, legendModel.get('pageButtonItemGap', true));\n        var contentRect = contentGroup.getBoundingRect();\n        var controllerRect = controllerGroup.getBoundingRect();\n        var showController = this._showController = contentRect[wh] > maxSize[wh]; // In case that the inner elements of contentGroup layout do not based on [0, 0]\n\n        var contentPos = [-contentRect.x, -contentRect.y]; // Remain contentPos when scroll animation perfroming.\n        // If first rendering, `contentGroup.position` is [0, 0], which\n        // does not make sense and may cause unexepcted animation if adopted.\n\n        if (!isFirstRender) {\n          contentPos[orientIdx] = contentGroup[xy];\n        } // Layout container group based on 0.\n\n\n        var containerPos = [0, 0];\n        var controllerPos = [-controllerRect.x, -controllerRect.y];\n        var pageButtonGap = retrieve2(legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)); // Place containerGroup and controllerGroup and contentGroup.\n\n        if (showController) {\n          var pageButtonPosition = legendModel.get('pageButtonPosition', true); // controller is on the right / bottom.\n\n          if (pageButtonPosition === 'end') {\n            controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];\n          } // controller is on the left / top.\n          else {\n              containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;\n            }\n        } // Always align controller to content as 'middle'.\n\n\n        controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;\n        contentGroup.setPosition(contentPos);\n        containerGroup.setPosition(containerPos);\n        controllerGroup.setPosition(controllerPos); // Calculate `mainRect` and set `clipPath`.\n        // mainRect should not be calculated by `this.group.getBoundingRect()`\n        // for sake of the overflow.\n\n        var mainRect = {\n          x: 0,\n          y: 0\n        }; // Consider content may be overflow (should be clipped).\n\n        mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];\n        mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.\n\n        mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);\n        containerGroup.__rectSize = maxSize[wh];\n\n        if (showController) {\n          var clipShape = {\n            x: 0,\n            y: 0\n          };\n          clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);\n          clipShape[hw] = mainRect[hw];\n          containerGroup.setClipPath(new Rect({\n            shape: clipShape\n          })); // Consider content may be larger than container, container rect\n          // can not be obtained from `containerGroup.getBoundingRect()`.\n\n          containerGroup.__rectSize = clipShape[wh];\n        } else {\n          // Do not remove or ignore controller. Keep them set as placeholders.\n          controllerGroup.eachChild(function (child) {\n            child.attr({\n              invisible: true,\n              silent: true\n            });\n          });\n        } // Content translate animation.\n\n\n        var pageInfo = this._getPageInfo(legendModel);\n\n        pageInfo.pageIndex != null && updateProps(contentGroup, {\n          x: pageInfo.contentPosition[0],\n          y: pageInfo.contentPosition[1]\n        }, // When switch from \"show controller\" to \"not show controller\", view should be\n        // updated immediately without animation, otherwise causes weird effect.\n        showController ? legendModel : null);\n\n        this._updatePageInfoView(legendModel, pageInfo);\n\n        return mainRect;\n      };\n\n      ScrollableLegendView.prototype._pageGo = function (to, legendModel, api) {\n        var scrollDataIndex = this._getPageInfo(legendModel)[to];\n\n        scrollDataIndex != null && api.dispatchAction({\n          type: 'legendScroll',\n          scrollDataIndex: scrollDataIndex,\n          legendId: legendModel.id\n        });\n      };\n\n      ScrollableLegendView.prototype._updatePageInfoView = function (legendModel, pageInfo) {\n        var controllerGroup = this._controllerGroup;\n        each(['pagePrev', 'pageNext'], function (name) {\n          var key = name + 'DataIndex';\n          var canJump = pageInfo[key] != null;\n          var icon = controllerGroup.childOfName(name);\n\n          if (icon) {\n            icon.setStyle('fill', canJump ? legendModel.get('pageIconColor', true) : legendModel.get('pageIconInactiveColor', true));\n            icon.cursor = canJump ? 'pointer' : 'default';\n          }\n        });\n        var pageText = controllerGroup.childOfName('pageText');\n        var pageFormatter = legendModel.get('pageFormatter');\n        var pageIndex = pageInfo.pageIndex;\n        var current = pageIndex != null ? pageIndex + 1 : 0;\n        var total = pageInfo.pageCount;\n        pageText && pageFormatter && pageText.setStyle('text', isString(pageFormatter) ? pageFormatter.replace('{current}', current == null ? '' : current + '').replace('{total}', total == null ? '' : total + '') : pageFormatter({\n          current: current,\n          total: total\n        }));\n      };\n      /**\n       *  contentPosition: Array.<number>, null when data item not found.\n       *  pageIndex: number, null when data item not found.\n       *  pageCount: number, always be a number, can be 0.\n       *  pagePrevDataIndex: number, null when no previous page.\n       *  pageNextDataIndex: number, null when no next page.\n       * }\n       */\n\n\n      ScrollableLegendView.prototype._getPageInfo = function (legendModel) {\n        var scrollDataIndex = legendModel.get('scrollDataIndex', true);\n        var contentGroup = this.getContentGroup();\n        var containerRectSize = this._containerGroup.__rectSize;\n        var orientIdx = legendModel.getOrient().index;\n        var wh = WH$1[orientIdx];\n        var xy = XY$1[orientIdx];\n\n        var targetItemIndex = this._findTargetItemIndex(scrollDataIndex);\n\n        var children = contentGroup.children();\n        var targetItem = children[targetItemIndex];\n        var itemCount = children.length;\n        var pCount = !itemCount ? 0 : 1;\n        var result = {\n          contentPosition: [contentGroup.x, contentGroup.y],\n          pageCount: pCount,\n          pageIndex: pCount - 1,\n          pagePrevDataIndex: null,\n          pageNextDataIndex: null\n        };\n\n        if (!targetItem) {\n          return result;\n        }\n\n        var targetItemInfo = getItemInfo(targetItem);\n        result.contentPosition[orientIdx] = -targetItemInfo.s; // Strategy:\n        // (1) Always align based on the left/top most item.\n        // (2) It is user-friendly that the last item shown in the\n        // current window is shown at the begining of next window.\n        // Otherwise if half of the last item is cut by the window,\n        // it will have no chance to display entirely.\n        // (3) Consider that item size probably be different, we\n        // have calculate pageIndex by size rather than item index,\n        // and we can not get page index directly by division.\n        // (4) The window is to narrow to contain more than\n        // one item, we should make sure that the page can be fliped.\n\n        for (var i = targetItemIndex + 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i <= itemCount; ++i) {\n          currItemInfo = getItemInfo(children[i]);\n\n          if ( // Half of the last item is out of the window.\n          !currItemInfo && winEndItemInfo.e > winStartItemInfo.s + containerRectSize || // If the current item does not intersect with the window, the new page\n          // can be started at the current item or the last item.\n          currItemInfo && !intersect(currItemInfo, winStartItemInfo.s)) {\n            if (winEndItemInfo.i > winStartItemInfo.i) {\n              winStartItemInfo = winEndItemInfo;\n            } else {\n              // e.g., when page size is smaller than item size.\n              winStartItemInfo = currItemInfo;\n            }\n\n            if (winStartItemInfo) {\n              if (result.pageNextDataIndex == null) {\n                result.pageNextDataIndex = winStartItemInfo.i;\n              }\n\n              ++result.pageCount;\n            }\n          }\n\n          winEndItemInfo = currItemInfo;\n        }\n\n        for (var i = targetItemIndex - 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i >= -1; --i) {\n          currItemInfo = getItemInfo(children[i]);\n\n          if ( // If the the end item does not intersect with the window started\n          // from the current item, a page can be settled.\n          (!currItemInfo || !intersect(winEndItemInfo, currItemInfo.s)) && // e.g., when page size is smaller than item size.\n          winStartItemInfo.i < winEndItemInfo.i) {\n            winEndItemInfo = winStartItemInfo;\n\n            if (result.pagePrevDataIndex == null) {\n              result.pagePrevDataIndex = winStartItemInfo.i;\n            }\n\n            ++result.pageCount;\n            ++result.pageIndex;\n          }\n\n          winStartItemInfo = currItemInfo;\n        }\n\n        return result;\n\n        function getItemInfo(el) {\n          if (el) {\n            var itemRect = el.getBoundingRect();\n            var start = itemRect[xy] + el[xy];\n            return {\n              s: start,\n              e: start + itemRect[wh],\n              i: el.__legendDataIndex\n            };\n          }\n        }\n\n        function intersect(itemInfo, winStart) {\n          return itemInfo.e >= winStart && itemInfo.s <= winStart + containerRectSize;\n        }\n      };\n\n      ScrollableLegendView.prototype._findTargetItemIndex = function (targetDataIndex) {\n        if (!this._showController) {\n          return 0;\n        }\n\n        var index;\n        var contentGroup = this.getContentGroup();\n        var defaultIndex;\n        contentGroup.eachChild(function (child, idx) {\n          var legendDataIdx = child.__legendDataIndex; // FIXME\n          // If the given targetDataIndex (from model) is illegal,\n          // we use defaultIndex. But the index on the legend model and\n          // action payload is still illegal. That case will not be\n          // changed until some scenario requires.\n\n          if (defaultIndex == null && legendDataIdx != null) {\n            defaultIndex = idx;\n          }\n\n          if (legendDataIdx === targetDataIndex) {\n            index = idx;\n          }\n        });\n        return index != null ? index : defaultIndex;\n      };\n\n      ScrollableLegendView.type = 'legend.scroll';\n      return ScrollableLegendView;\n    }(LegendView);\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function installScrollableLegendAction(registers) {\n      /**\n       * @event legendScroll\n       * @type {Object}\n       * @property {string} type 'legendScroll'\n       * @property {string} scrollDataIndex\n       */\n      registers.registerAction('legendScroll', 'legendscroll', function (payload, ecModel) {\n        var scrollDataIndex = payload.scrollDataIndex;\n        scrollDataIndex != null && ecModel.eachComponent({\n          mainType: 'legend',\n          subType: 'scroll',\n          query: payload\n        }, function (legendModel) {\n          legendModel.setScrollDataIndex(scrollDataIndex);\n        });\n      });\n    }\n\n    function install$I(registers) {\n      use(install$H);\n      registers.registerComponentModel(ScrollableLegendModel);\n      registers.registerComponentView(ScrollableLegendView);\n      installScrollableLegendAction(registers);\n    }\n\n    function install$J(registers) {\n      use(install$H);\n      use(install$I);\n    }\n\n    var InsideZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(InsideZoomModel, _super);\n\n      function InsideZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = InsideZoomModel.type;\n        return _this;\n      }\n\n      InsideZoomModel.type = 'dataZoom.inside';\n      InsideZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, {\n        disabled: false,\n        zoomLock: false,\n        zoomOnMouseWheel: true,\n        moveOnMouseMove: true,\n        moveOnMouseWheel: false,\n        preventDefaultMouseMove: true\n      });\n      return InsideZoomModel;\n    }(DataZoomModel);\n\n    var inner$k = makeInner();\n    function setViewInfoToCoordSysRecord(api, dataZoomModel, getRange) {\n      inner$k(api).coordSysRecordMap.each(function (coordSysRecord) {\n        var dzInfo = coordSysRecord.dataZoomInfoMap.get(dataZoomModel.uid);\n\n        if (dzInfo) {\n          dzInfo.getRange = getRange;\n        }\n      });\n    }\n    function disposeCoordSysRecordIfNeeded(api, dataZoomModel) {\n      var coordSysRecordMap = inner$k(api).coordSysRecordMap;\n      var coordSysKeyArr = coordSysRecordMap.keys();\n\n      for (var i = 0; i < coordSysKeyArr.length; i++) {\n        var coordSysKey = coordSysKeyArr[i];\n        var coordSysRecord = coordSysRecordMap.get(coordSysKey);\n        var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap;\n\n        if (dataZoomInfoMap) {\n          var dzUid = dataZoomModel.uid;\n          var dzInfo = dataZoomInfoMap.get(dzUid);\n\n          if (dzInfo) {\n            dataZoomInfoMap.removeKey(dzUid);\n\n            if (!dataZoomInfoMap.keys().length) {\n              disposeCoordSysRecord(coordSysRecordMap, coordSysRecord);\n            }\n          }\n        }\n      }\n    }\n\n    function disposeCoordSysRecord(coordSysRecordMap, coordSysRecord) {\n      if (coordSysRecord) {\n        coordSysRecordMap.removeKey(coordSysRecord.model.uid);\n        var controller = coordSysRecord.controller;\n        controller && controller.dispose();\n      }\n    }\n\n    function createCoordSysRecord(api, coordSysModel) {\n      // These init props will never change after record created.\n      var coordSysRecord = {\n        model: coordSysModel,\n        containsPoint: curry(containsPoint, coordSysModel),\n        dispatchAction: curry(dispatchAction$1, api),\n        dataZoomInfoMap: null,\n        controller: null\n      }; // Must not do anything depends on coordSysRecord outside the event handler here,\n      // because coordSysRecord not completed yet.\n\n      var controller = coordSysRecord.controller = new RoamController(api.getZr());\n      each(['pan', 'zoom', 'scrollMove'], function (eventName) {\n        controller.on(eventName, function (event) {\n          var batch = [];\n          coordSysRecord.dataZoomInfoMap.each(function (dzInfo) {\n            // Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove,\n            // moveOnMouseWheel, ...) enabled.\n            if (!event.isAvailableBehavior(dzInfo.model.option)) {\n              return;\n            }\n\n            var method = (dzInfo.getRange || {})[eventName];\n            var range = method && method(dzInfo.dzReferCoordSysInfo, coordSysRecord.model.mainType, coordSysRecord.controller, event);\n            !dzInfo.model.get('disabled', true) && range && batch.push({\n              dataZoomId: dzInfo.model.id,\n              start: range[0],\n              end: range[1]\n            });\n          });\n          batch.length && coordSysRecord.dispatchAction(batch);\n        });\n      });\n      return coordSysRecord;\n    }\n    /**\n     * This action will be throttled.\n     */\n\n\n    function dispatchAction$1(api, batch) {\n      if (!api.isDisposed()) {\n        api.dispatchAction({\n          type: 'dataZoom',\n          animation: {\n            easing: 'cubicOut',\n            duration: 100\n          },\n          batch: batch\n        });\n      }\n    }\n\n    function containsPoint(coordSysModel, e, x, y) {\n      return coordSysModel.coordinateSystem.containPoint([x, y]);\n    }\n    /**\n     * Merge roamController settings when multiple dataZooms share one roamController.\n     */\n\n\n    function mergeControllerParams(dataZoomInfoMap) {\n      var controlType; // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated\n      // as string, it is probably revert to reserved word by compress tool. See #7411.\n\n      var prefix = 'type_';\n      var typePriority = {\n        'type_true': 2,\n        'type_move': 1,\n        'type_false': 0,\n        'type_undefined': -1\n      };\n      var preventDefaultMouseMove = true;\n      dataZoomInfoMap.each(function (dataZoomInfo) {\n        var dataZoomModel = dataZoomInfo.model;\n        var oneType = dataZoomModel.get('disabled', true) ? false : dataZoomModel.get('zoomLock', true) ? 'move' : true;\n\n        if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) {\n          controlType = oneType;\n        } // Prevent default move event by default. If one false, do not prevent. Otherwise\n        // users may be confused why it does not work when multiple insideZooms exist.\n\n\n        preventDefaultMouseMove = preventDefaultMouseMove && dataZoomModel.get('preventDefaultMouseMove', true);\n      });\n      return {\n        controlType: controlType,\n        opt: {\n          // RoamController will enable all of these functionalities,\n          // and the final behavior is determined by its event listener\n          // provided by each inside zoom.\n          zoomOnMouseWheel: true,\n          moveOnMouseMove: true,\n          moveOnMouseWheel: true,\n          preventDefaultMouseMove: !!preventDefaultMouseMove\n        }\n      };\n    }\n\n    function installDataZoomRoamProcessor(registers) {\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, function (ecModel, api) {\n        var apiInner = inner$k(api);\n        var coordSysRecordMap = apiInner.coordSysRecordMap || (apiInner.coordSysRecordMap = createHashMap());\n        coordSysRecordMap.each(function (coordSysRecord) {\n          // `coordSysRecordMap` always exists (because it holds the `roam controller`, which should\n          // better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow.\n          coordSysRecord.dataZoomInfoMap = null;\n        });\n        ecModel.eachComponent({\n          mainType: 'dataZoom',\n          subType: 'inside'\n        }, function (dataZoomModel) {\n          var dzReferCoordSysWrap = collectReferCoordSysModelInfo(dataZoomModel);\n          each(dzReferCoordSysWrap.infoList, function (dzCoordSysInfo) {\n            var coordSysUid = dzCoordSysInfo.model.uid;\n            var coordSysRecord = coordSysRecordMap.get(coordSysUid) || coordSysRecordMap.set(coordSysUid, createCoordSysRecord(api, dzCoordSysInfo.model));\n            var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap || (coordSysRecord.dataZoomInfoMap = createHashMap()); // Notice these props might be changed each time for a single dataZoomModel.\n\n            dataZoomInfoMap.set(dataZoomModel.uid, {\n              dzReferCoordSysInfo: dzCoordSysInfo,\n              model: dataZoomModel,\n              getRange: null\n            });\n          });\n        }); // (1) Merge dataZoom settings for each coord sys and set to the roam controller.\n        // (2) Clear coord sys if not refered by any dataZoom.\n\n        coordSysRecordMap.each(function (coordSysRecord) {\n          var controller = coordSysRecord.controller;\n          var firstDzInfo;\n          var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap;\n\n          if (dataZoomInfoMap) {\n            var firstDzKey = dataZoomInfoMap.keys()[0];\n\n            if (firstDzKey != null) {\n              firstDzInfo = dataZoomInfoMap.get(firstDzKey);\n            }\n          }\n\n          if (!firstDzInfo) {\n            disposeCoordSysRecord(coordSysRecordMap, coordSysRecord);\n            return;\n          }\n\n          var controllerParams = mergeControllerParams(dataZoomInfoMap);\n          controller.enable(controllerParams.controlType, controllerParams.opt);\n          controller.setPointerChecker(coordSysRecord.containsPoint);\n          createOrUpdate(coordSysRecord, 'dispatchAction', firstDzInfo.model.get('throttle', true), 'fixRate');\n        });\n      });\n    }\n\n    var InsideZoomView =\n    /** @class */\n    function (_super) {\n      __extends(InsideZoomView, _super);\n\n      function InsideZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataZoom.inside';\n        return _this;\n      }\n\n      InsideZoomView.prototype.render = function (dataZoomModel, ecModel, api) {\n        _super.prototype.render.apply(this, arguments);\n\n        if (dataZoomModel.noTarget()) {\n          this._clear();\n\n          return;\n        } // Hence the `throttle` util ensures to preserve command order,\n        // here simply updating range all the time will not cause missing\n        // any of the the roam change.\n\n\n        this.range = dataZoomModel.getPercentRange(); // Reset controllers.\n\n        setViewInfoToCoordSysRecord(api, dataZoomModel, {\n          pan: bind(getRangeHandlers.pan, this),\n          zoom: bind(getRangeHandlers.zoom, this),\n          scrollMove: bind(getRangeHandlers.scrollMove, this)\n        });\n      };\n\n      InsideZoomView.prototype.dispose = function () {\n        this._clear();\n\n        _super.prototype.dispose.apply(this, arguments);\n      };\n\n      InsideZoomView.prototype._clear = function () {\n        disposeCoordSysRecordIfNeeded(this.api, this.dataZoomModel);\n        this.range = null;\n      };\n\n      InsideZoomView.type = 'dataZoom.inside';\n      return InsideZoomView;\n    }(DataZoomView);\n\n    var getRangeHandlers = {\n      zoom: function (coordSysInfo, coordSysMainType, controller, e) {\n        var lastRange = this.range;\n        var range = lastRange.slice(); // Calculate transform by the first axis.\n\n        var axisModel = coordSysInfo.axisModels[0];\n\n        if (!axisModel) {\n          return;\n        }\n\n        var directionInfo = getDirectionInfo[coordSysMainType](null, [e.originX, e.originY], axisModel, controller, coordSysInfo);\n        var percentPoint = (directionInfo.signal > 0 ? directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel : directionInfo.pixel - directionInfo.pixelStart) / directionInfo.pixelLength * (range[1] - range[0]) + range[0];\n        var scale = Math.max(1 / e.scale, 0);\n        range[0] = (range[0] - percentPoint) * scale + percentPoint;\n        range[1] = (range[1] - percentPoint) * scale + percentPoint; // Restrict range.\n\n        var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();\n        sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);\n        this.range = range;\n\n        if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {\n          return range;\n        }\n      },\n      pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) {\n        var directionInfo = getDirectionInfo[coordSysMainType]([e.oldX, e.oldY], [e.newX, e.newY], axisModel, controller, coordSysInfo);\n        return directionInfo.signal * (range[1] - range[0]) * directionInfo.pixel / directionInfo.pixelLength;\n      }),\n      scrollMove: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) {\n        var directionInfo = getDirectionInfo[coordSysMainType]([0, 0], [e.scrollDelta, e.scrollDelta], axisModel, controller, coordSysInfo);\n        return directionInfo.signal * (range[1] - range[0]) * e.scrollDelta;\n      })\n    };\n\n    function makeMover(getPercentDelta) {\n      return function (coordSysInfo, coordSysMainType, controller, e) {\n        var lastRange = this.range;\n        var range = lastRange.slice(); // Calculate transform by the first axis.\n\n        var axisModel = coordSysInfo.axisModels[0];\n\n        if (!axisModel) {\n          return;\n        }\n\n        var percentDelta = getPercentDelta(range, axisModel, coordSysInfo, coordSysMainType, controller, e);\n        sliderMove(percentDelta, range, [0, 100], 'all');\n        this.range = range;\n\n        if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {\n          return range;\n        }\n      };\n    }\n\n    var getDirectionInfo = {\n      grid: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n        var axis = axisModel.axis;\n        var ret = {};\n        var rect = coordSysInfo.model.coordinateSystem.getRect();\n        oldPoint = oldPoint || [0, 0];\n\n        if (axis.dim === 'x') {\n          ret.pixel = newPoint[0] - oldPoint[0];\n          ret.pixelLength = rect.width;\n          ret.pixelStart = rect.x;\n          ret.signal = axis.inverse ? 1 : -1;\n        } else {\n          // axis.dim === 'y'\n          ret.pixel = newPoint[1] - oldPoint[1];\n          ret.pixelLength = rect.height;\n          ret.pixelStart = rect.y;\n          ret.signal = axis.inverse ? -1 : 1;\n        }\n\n        return ret;\n      },\n      polar: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n        var axis = axisModel.axis;\n        var ret = {};\n        var polar = coordSysInfo.model.coordinateSystem;\n        var radiusExtent = polar.getRadiusAxis().getExtent();\n        var angleExtent = polar.getAngleAxis().getExtent();\n        oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0];\n        newPoint = polar.pointToCoord(newPoint);\n\n        if (axisModel.mainType === 'radiusAxis') {\n          ret.pixel = newPoint[0] - oldPoint[0]; // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]);\n          // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]);\n\n          ret.pixelLength = radiusExtent[1] - radiusExtent[0];\n          ret.pixelStart = radiusExtent[0];\n          ret.signal = axis.inverse ? 1 : -1;\n        } else {\n          // 'angleAxis'\n          ret.pixel = newPoint[1] - oldPoint[1]; // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]);\n          // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]);\n\n          ret.pixelLength = angleExtent[1] - angleExtent[0];\n          ret.pixelStart = angleExtent[0];\n          ret.signal = axis.inverse ? -1 : 1;\n        }\n\n        return ret;\n      },\n      singleAxis: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) {\n        var axis = axisModel.axis;\n        var rect = coordSysInfo.model.coordinateSystem.getRect();\n        var ret = {};\n        oldPoint = oldPoint || [0, 0];\n\n        if (axis.orient === 'horizontal') {\n          ret.pixel = newPoint[0] - oldPoint[0];\n          ret.pixelLength = rect.width;\n          ret.pixelStart = rect.x;\n          ret.signal = axis.inverse ? 1 : -1;\n        } else {\n          // 'vertical'\n          ret.pixel = newPoint[1] - oldPoint[1];\n          ret.pixelLength = rect.height;\n          ret.pixelStart = rect.y;\n          ret.signal = axis.inverse ? -1 : 1;\n        }\n\n        return ret;\n      }\n    };\n\n    function install$K(registers) {\n      installCommon(registers);\n      registers.registerComponentModel(InsideZoomModel);\n      registers.registerComponentView(InsideZoomView);\n      installDataZoomRoamProcessor(registers);\n    }\n\n    var SliderZoomModel =\n    /** @class */\n    function (_super) {\n      __extends(SliderZoomModel, _super);\n\n      function SliderZoomModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SliderZoomModel.type;\n        return _this;\n      }\n\n      SliderZoomModel.type = 'dataZoom.slider';\n      SliderZoomModel.layoutMode = 'box';\n      SliderZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, {\n        show: true,\n        // deault value can only be drived in view stage.\n        right: 'ph',\n        top: 'ph',\n        width: 'ph',\n        height: 'ph',\n        left: null,\n        bottom: null,\n        borderColor: '#d2dbee',\n        borderRadius: 3,\n        backgroundColor: 'rgba(47,69,84,0)',\n        // dataBackgroundColor: '#ddd',\n        dataBackground: {\n          lineStyle: {\n            color: '#d2dbee',\n            width: 0.5\n          },\n          areaStyle: {\n            color: '#d2dbee',\n            opacity: 0.2\n          }\n        },\n        selectedDataBackground: {\n          lineStyle: {\n            color: '#8fb0f7',\n            width: 0.5\n          },\n          areaStyle: {\n            color: '#8fb0f7',\n            opacity: 0.2\n          }\n        },\n        // Color of selected window.\n        fillerColor: 'rgba(135,175,274,0.2)',\n        handleIcon: 'path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z',\n        // Percent of the slider height\n        handleSize: '100%',\n        handleStyle: {\n          color: '#fff',\n          borderColor: '#ACB8D1'\n        },\n        moveHandleSize: 7,\n        moveHandleIcon: 'path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z',\n        moveHandleStyle: {\n          color: '#D2DBEE',\n          opacity: 0.7\n        },\n        showDetail: true,\n        showDataShadow: 'auto',\n        realtime: true,\n        zoomLock: false,\n        textStyle: {\n          color: '#6E7079'\n        },\n        brushSelect: true,\n        brushStyle: {\n          color: 'rgba(135,175,274,0.15)'\n        },\n        emphasis: {\n          handleStyle: {\n            borderColor: '#8FB0F7'\n          },\n          moveHandleStyle: {\n            color: '#8FB0F7'\n          }\n        }\n      });\n      return SliderZoomModel;\n    }(DataZoomModel);\n\n    var Rect$2 = Rect; // Constants\n\n    var DEFAULT_LOCATION_EDGE_GAP = 7;\n    var DEFAULT_FRAME_BORDER_WIDTH = 1;\n    var DEFAULT_FILLER_SIZE = 30;\n    var DEFAULT_MOVE_HANDLE_SIZE = 7;\n    var HORIZONTAL = 'horizontal';\n    var VERTICAL = 'vertical';\n    var LABEL_GAP = 5;\n    var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];\n    var REALTIME_ANIMATION_CONFIG = {\n      easing: 'cubicOut',\n      duration: 100,\n      delay: 0\n    };\n\n    var SliderZoomView =\n    /** @class */\n    function (_super) {\n      __extends(SliderZoomView, _super);\n\n      function SliderZoomView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = SliderZoomView.type;\n        _this._displayables = {};\n        return _this;\n      }\n\n      SliderZoomView.prototype.init = function (ecModel, api) {\n        this.api = api; // A unique handler for each dataZoom component\n\n        this._onBrush = bind(this._onBrush, this);\n        this._onBrushEnd = bind(this._onBrushEnd, this);\n      };\n\n      SliderZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) {\n        _super.prototype.render.apply(this, arguments);\n\n        createOrUpdate(this, '_dispatchZoomAction', dataZoomModel.get('throttle'), 'fixRate');\n        this._orient = dataZoomModel.getOrient();\n\n        if (dataZoomModel.get('show') === false) {\n          this.group.removeAll();\n          return;\n        }\n\n        if (dataZoomModel.noTarget()) {\n          this._clear();\n\n          this.group.removeAll();\n          return;\n        } // Notice: this._resetInterval() should not be executed when payload.type\n        // is 'dataZoom', origin this._range should be maintained, otherwise 'pan'\n        // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,\n\n\n        if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) {\n          this._buildView();\n        }\n\n        this._updateView();\n      };\n\n      SliderZoomView.prototype.dispose = function () {\n        this._clear();\n\n        _super.prototype.dispose.apply(this, arguments);\n      };\n\n      SliderZoomView.prototype._clear = function () {\n        clear(this, '_dispatchZoomAction');\n        var zr = this.api.getZr();\n        zr.off('mousemove', this._onBrush);\n        zr.off('mouseup', this._onBrushEnd);\n      };\n\n      SliderZoomView.prototype._buildView = function () {\n        var thisGroup = this.group;\n        thisGroup.removeAll();\n        this._brushing = false;\n        this._displayables.brushRect = null;\n\n        this._resetLocation();\n\n        this._resetInterval();\n\n        var barGroup = this._displayables.sliderGroup = new Group();\n\n        this._renderBackground();\n\n        this._renderHandle();\n\n        this._renderDataShadow();\n\n        thisGroup.add(barGroup);\n\n        this._positionGroup();\n      };\n\n      SliderZoomView.prototype._resetLocation = function () {\n        var dataZoomModel = this.dataZoomModel;\n        var api = this.api;\n        var showMoveHandle = dataZoomModel.get('brushSelect');\n        var moveHandleSize = showMoveHandle ? DEFAULT_MOVE_HANDLE_SIZE : 0; // If some of x/y/width/height are not specified,\n        // auto-adapt according to target grid.\n\n        var coordRect = this._findCoordRect();\n\n        var ecSize = {\n          width: api.getWidth(),\n          height: api.getHeight()\n        }; // Default align by coordinate system rect.\n\n        var positionInfo = this._orient === HORIZONTAL ? {\n          // Why using 'right', because right should be used in vertical,\n          // and it is better to be consistent for dealing with position param merge.\n          right: ecSize.width - coordRect.x - coordRect.width,\n          top: ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP - moveHandleSize,\n          width: coordRect.width,\n          height: DEFAULT_FILLER_SIZE\n        } : {\n          right: DEFAULT_LOCATION_EDGE_GAP,\n          top: coordRect.y,\n          width: DEFAULT_FILLER_SIZE,\n          height: coordRect.height\n        }; // Do not write back to option and replace value 'ph', because\n        // the 'ph' value should be recalculated when resize.\n\n        var layoutParams = getLayoutParams(dataZoomModel.option); // Replace the placeholder value.\n\n        each(['right', 'top', 'width', 'height'], function (name) {\n          if (layoutParams[name] === 'ph') {\n            layoutParams[name] = positionInfo[name];\n          }\n        });\n        var layoutRect = getLayoutRect(layoutParams, ecSize);\n        this._location = {\n          x: layoutRect.x,\n          y: layoutRect.y\n        };\n        this._size = [layoutRect.width, layoutRect.height];\n        this._orient === VERTICAL && this._size.reverse();\n      };\n\n      SliderZoomView.prototype._positionGroup = function () {\n        var thisGroup = this.group;\n        var location = this._location;\n        var orient = this._orient; // Just use the first axis to determine mapping.\n\n        var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel();\n        var inverse = targetAxisModel && targetAxisModel.get('inverse');\n        var sliderGroup = this._displayables.sliderGroup;\n        var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse; // Transform barGroup.\n\n        sliderGroup.attr(orient === HORIZONTAL && !inverse ? {\n          scaleY: otherAxisInverse ? 1 : -1,\n          scaleX: 1\n        } : orient === HORIZONTAL && inverse ? {\n          scaleY: otherAxisInverse ? 1 : -1,\n          scaleX: -1\n        } : orient === VERTICAL && !inverse ? {\n          scaleY: otherAxisInverse ? -1 : 1,\n          scaleX: 1,\n          rotation: Math.PI / 2\n        } // Dont use Math.PI, considering shadow direction.\n        : {\n          scaleY: otherAxisInverse ? -1 : 1,\n          scaleX: -1,\n          rotation: Math.PI / 2\n        }); // Position barGroup\n\n        var rect = thisGroup.getBoundingRect([sliderGroup]);\n        thisGroup.x = location.x - rect.x;\n        thisGroup.y = location.y - rect.y;\n        thisGroup.markRedraw();\n      };\n\n      SliderZoomView.prototype._getViewExtent = function () {\n        return [0, this._size[0]];\n      };\n\n      SliderZoomView.prototype._renderBackground = function () {\n        var dataZoomModel = this.dataZoomModel;\n        var size = this._size;\n        var barGroup = this._displayables.sliderGroup;\n        var brushSelect = dataZoomModel.get('brushSelect');\n        barGroup.add(new Rect$2({\n          silent: true,\n          shape: {\n            x: 0,\n            y: 0,\n            width: size[0],\n            height: size[1]\n          },\n          style: {\n            fill: dataZoomModel.get('backgroundColor')\n          },\n          z2: -40\n        })); // Click panel, over shadow, below handles.\n\n        var clickPanel = new Rect$2({\n          shape: {\n            x: 0,\n            y: 0,\n            width: size[0],\n            height: size[1]\n          },\n          style: {\n            fill: 'transparent'\n          },\n          z2: 0,\n          onclick: bind(this._onClickPanel, this)\n        });\n        var zr = this.api.getZr();\n\n        if (brushSelect) {\n          clickPanel.on('mousedown', this._onBrushStart, this);\n          clickPanel.cursor = 'crosshair';\n          zr.on('mousemove', this._onBrush);\n          zr.on('mouseup', this._onBrushEnd);\n        } else {\n          zr.off('mousemove', this._onBrush);\n          zr.off('mouseup', this._onBrushEnd);\n        }\n\n        barGroup.add(clickPanel);\n      };\n\n      SliderZoomView.prototype._renderDataShadow = function () {\n        var info = this._dataShadowInfo = this._prepareDataShadowInfo();\n\n        this._displayables.dataShadowSegs = [];\n\n        if (!info) {\n          return;\n        }\n\n        var size = this._size;\n        var oldSize = this._shadowSize || [];\n        var seriesModel = info.series;\n        var data = seriesModel.getRawData();\n        var candlestickDim = seriesModel.getShadowDim && seriesModel.getShadowDim();\n        var otherDim = candlestickDim && data.getDimensionInfo(candlestickDim) ? seriesModel.getShadowDim() // @see candlestick\n        : info.otherDim;\n\n        if (otherDim == null) {\n          return;\n        }\n\n        var polygonPts = this._shadowPolygonPts;\n        var polylinePts = this._shadowPolylinePts; // Not re-render if data doesn't change.\n\n        if (data !== this._shadowData || otherDim !== this._shadowDim || size[0] !== oldSize[0] || size[1] !== oldSize[1]) {\n          var otherDataExtent_1 = data.getDataExtent(otherDim); // Nice extent.\n\n          var otherOffset = (otherDataExtent_1[1] - otherDataExtent_1[0]) * 0.3;\n          otherDataExtent_1 = [otherDataExtent_1[0] - otherOffset, otherDataExtent_1[1] + otherOffset];\n          var otherShadowExtent_1 = [0, size[1]];\n          var thisShadowExtent = [0, size[0]];\n          var areaPoints_1 = [[size[0], 0], [0, 0]];\n          var linePoints_1 = [];\n          var step_1 = thisShadowExtent[1] / (data.count() - 1);\n          var thisCoord_1 = 0; // Optimize for large data shadow\n\n          var stride_1 = Math.round(data.count() / size[0]);\n          var lastIsEmpty_1;\n          data.each([otherDim], function (value, index) {\n            if (stride_1 > 0 && index % stride_1) {\n              thisCoord_1 += step_1;\n              return;\n            } // FIXME\n            // Should consider axis.min/axis.max when drawing dataShadow.\n            // FIXME\n            // 应该使用统一的空判断？还是在list里进行空判断？\n\n\n            var isEmpty = value == null || isNaN(value) || value === ''; // See #4235.\n\n            var otherCoord = isEmpty ? 0 : linearMap(value, otherDataExtent_1, otherShadowExtent_1, true); // Attempt to draw data shadow precisely when there are empty value.\n\n            if (isEmpty && !lastIsEmpty_1 && index) {\n              areaPoints_1.push([areaPoints_1[areaPoints_1.length - 1][0], 0]);\n              linePoints_1.push([linePoints_1[linePoints_1.length - 1][0], 0]);\n            } else if (!isEmpty && lastIsEmpty_1) {\n              areaPoints_1.push([thisCoord_1, 0]);\n              linePoints_1.push([thisCoord_1, 0]);\n            }\n\n            areaPoints_1.push([thisCoord_1, otherCoord]);\n            linePoints_1.push([thisCoord_1, otherCoord]);\n            thisCoord_1 += step_1;\n            lastIsEmpty_1 = isEmpty;\n          });\n          polygonPts = this._shadowPolygonPts = areaPoints_1;\n          polylinePts = this._shadowPolylinePts = linePoints_1;\n        }\n\n        this._shadowData = data;\n        this._shadowDim = otherDim;\n        this._shadowSize = [size[0], size[1]];\n        var dataZoomModel = this.dataZoomModel;\n\n        function createDataShadowGroup(isSelectedArea) {\n          var model = dataZoomModel.getModel(isSelectedArea ? 'selectedDataBackground' : 'dataBackground');\n          var group = new Group();\n          var polygon = new Polygon({\n            shape: {\n              points: polygonPts\n            },\n            segmentIgnoreThreshold: 1,\n            style: model.getModel('areaStyle').getAreaStyle(),\n            silent: true,\n            z2: -20\n          });\n          var polyline = new Polyline({\n            shape: {\n              points: polylinePts\n            },\n            segmentIgnoreThreshold: 1,\n            style: model.getModel('lineStyle').getLineStyle(),\n            silent: true,\n            z2: -19\n          });\n          group.add(polygon);\n          group.add(polyline);\n          return group;\n        } // let dataBackgroundModel = dataZoomModel.getModel('dataBackground');\n\n\n        for (var i = 0; i < 3; i++) {\n          var group = createDataShadowGroup(i === 1);\n\n          this._displayables.sliderGroup.add(group);\n\n          this._displayables.dataShadowSegs.push(group);\n        }\n      };\n\n      SliderZoomView.prototype._prepareDataShadowInfo = function () {\n        var dataZoomModel = this.dataZoomModel;\n        var showDataShadow = dataZoomModel.get('showDataShadow');\n\n        if (showDataShadow === false) {\n          return;\n        } // Find a representative series.\n\n\n        var result;\n        var ecModel = this.ecModel;\n        dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {\n          var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels();\n          each(seriesModels, function (seriesModel) {\n            if (result) {\n              return;\n            }\n\n            if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) {\n              return;\n            }\n\n            var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis;\n            var otherDim = getOtherDim(axisDim);\n            var otherAxisInverse;\n            var coordSys = seriesModel.coordinateSystem;\n\n            if (otherDim != null && coordSys.getOtherAxis) {\n              otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;\n            }\n\n            otherDim = seriesModel.getData().mapDimension(otherDim);\n            result = {\n              thisAxis: thisAxis,\n              series: seriesModel,\n              thisDim: axisDim,\n              otherDim: otherDim,\n              otherAxisInverse: otherAxisInverse\n            };\n          }, this);\n        }, this);\n        return result;\n      };\n\n      SliderZoomView.prototype._renderHandle = function () {\n        var thisGroup = this.group;\n        var displayables = this._displayables;\n        var handles = displayables.handles = [null, null];\n        var handleLabels = displayables.handleLabels = [null, null];\n        var sliderGroup = this._displayables.sliderGroup;\n        var size = this._size;\n        var dataZoomModel = this.dataZoomModel;\n        var api = this.api;\n        var borderRadius = dataZoomModel.get('borderRadius') || 0;\n        var brushSelect = dataZoomModel.get('brushSelect');\n        var filler = displayables.filler = new Rect$2({\n          silent: brushSelect,\n          style: {\n            fill: dataZoomModel.get('fillerColor')\n          },\n          textConfig: {\n            position: 'inside'\n          }\n        });\n        sliderGroup.add(filler); // Frame border.\n\n        sliderGroup.add(new Rect$2({\n          silent: true,\n          subPixelOptimize: true,\n          shape: {\n            x: 0,\n            y: 0,\n            width: size[0],\n            height: size[1],\n            r: borderRadius\n          },\n          style: {\n            // deprecated option\n            stroke: dataZoomModel.get('dataBackgroundColor') || dataZoomModel.get('borderColor'),\n            lineWidth: DEFAULT_FRAME_BORDER_WIDTH,\n            fill: 'rgba(0,0,0,0)'\n          }\n        })); // Left and right handle to resize\n\n        each([0, 1], function (handleIndex) {\n          var iconStr = dataZoomModel.get('handleIcon');\n\n          if (!symbolBuildProxies[iconStr] && iconStr.indexOf('path://') < 0 && iconStr.indexOf('image://') < 0) {\n            // Compatitable with the old icon parsers. Which can use a path string without path://\n            iconStr = 'path://' + iconStr;\n\n            if (\"development\" !== 'production') {\n              deprecateLog('handleIcon now needs \\'path://\\' prefix when using a path string');\n            }\n          }\n\n          var path = createSymbol(iconStr, -1, 0, 2, 2, null, true);\n          path.attr({\n            cursor: getCursor(this._orient),\n            draggable: true,\n            drift: bind(this._onDragMove, this, handleIndex),\n            ondragend: bind(this._onDragEnd, this),\n            onmouseover: bind(this._showDataInfo, this, true),\n            onmouseout: bind(this._showDataInfo, this, false),\n            z2: 5\n          });\n          var bRect = path.getBoundingRect();\n          var handleSize = dataZoomModel.get('handleSize');\n          this._handleHeight = parsePercent$1(handleSize, this._size[1]);\n          this._handleWidth = bRect.width / bRect.height * this._handleHeight;\n          path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle());\n          path.style.strokeNoScale = true;\n          path.rectHover = true;\n          path.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'handleStyle']).getItemStyle();\n          enableHoverEmphasis(path);\n          var handleColor = dataZoomModel.get('handleColor'); // deprecated option\n          // Compatitable with previous version\n\n          if (handleColor != null) {\n            path.style.fill = handleColor;\n          }\n\n          sliderGroup.add(handles[handleIndex] = path);\n          var textStyleModel = dataZoomModel.getModel('textStyle');\n          thisGroup.add(handleLabels[handleIndex] = new ZRText({\n            silent: true,\n            invisible: true,\n            style: createTextStyle(textStyleModel, {\n              x: 0,\n              y: 0,\n              text: '',\n              verticalAlign: 'middle',\n              align: 'center',\n              fill: textStyleModel.getTextColor(),\n              font: textStyleModel.getFont()\n            }),\n            z2: 10\n          }));\n        }, this); // Handle to move. Only visible when brushSelect is set true.\n\n        var actualMoveZone = filler;\n\n        if (brushSelect) {\n          var moveHandleHeight = parsePercent$1(dataZoomModel.get('moveHandleSize'), size[1]);\n          var moveHandle_1 = displayables.moveHandle = new Rect({\n            style: dataZoomModel.getModel('moveHandleStyle').getItemStyle(),\n            silent: true,\n            shape: {\n              r: [0, 0, 2, 2],\n              y: size[1] - 0.5,\n              height: moveHandleHeight\n            }\n          });\n          var iconSize = moveHandleHeight * 0.8;\n          var moveHandleIcon = displayables.moveHandleIcon = createSymbol(dataZoomModel.get('moveHandleIcon'), -iconSize / 2, -iconSize / 2, iconSize, iconSize, '#fff', true);\n          moveHandleIcon.silent = true;\n          moveHandleIcon.y = size[1] + moveHandleHeight / 2 - 0.5;\n          moveHandle_1.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'moveHandleStyle']).getItemStyle();\n          var moveZoneExpandSize = Math.min(size[1] / 2, Math.max(moveHandleHeight, 10));\n          actualMoveZone = displayables.moveZone = new Rect({\n            invisible: true,\n            shape: {\n              y: size[1] - moveZoneExpandSize,\n              height: moveHandleHeight + moveZoneExpandSize\n            }\n          });\n          actualMoveZone.on('mouseover', function () {\n            api.enterEmphasis(moveHandle_1);\n          }).on('mouseout', function () {\n            api.leaveEmphasis(moveHandle_1);\n          });\n          sliderGroup.add(moveHandle_1);\n          sliderGroup.add(moveHandleIcon);\n          sliderGroup.add(actualMoveZone);\n        }\n\n        actualMoveZone.attr({\n          draggable: true,\n          cursor: getCursor(this._orient),\n          drift: bind(this._onDragMove, this, 'all'),\n          ondragstart: bind(this._showDataInfo, this, true),\n          ondragend: bind(this._onDragEnd, this),\n          onmouseover: bind(this._showDataInfo, this, true),\n          onmouseout: bind(this._showDataInfo, this, false)\n        });\n      };\n\n      SliderZoomView.prototype._resetInterval = function () {\n        var range = this._range = this.dataZoomModel.getPercentRange();\n\n        var viewExtent = this._getViewExtent();\n\n        this._handleEnds = [linearMap(range[0], [0, 100], viewExtent, true), linearMap(range[1], [0, 100], viewExtent, true)];\n      };\n\n      SliderZoomView.prototype._updateInterval = function (handleIndex, delta) {\n        var dataZoomModel = this.dataZoomModel;\n        var handleEnds = this._handleEnds;\n\n        var viewExtend = this._getViewExtent();\n\n        var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();\n        var percentExtent = [0, 100];\n        sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null);\n        var lastRange = this._range;\n        var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]);\n        return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];\n      };\n\n      SliderZoomView.prototype._updateView = function (nonRealtime) {\n        var displaybles = this._displayables;\n        var handleEnds = this._handleEnds;\n        var handleInterval = asc(handleEnds.slice());\n        var size = this._size;\n        each([0, 1], function (handleIndex) {\n          // Handles\n          var handle = displaybles.handles[handleIndex];\n          var handleHeight = this._handleHeight;\n          handle.attr({\n            scaleX: handleHeight / 2,\n            scaleY: handleHeight / 2,\n            // This is a trick, by adding an extra tiny offset to let the default handle's end point align to the drag window.\n            // NOTE: It may affect some custom shapes a bit. But we prefer to have better result by default.\n            x: handleEnds[handleIndex] + (handleIndex ? -1 : 1),\n            y: size[1] / 2 - handleHeight / 2\n          });\n        }, this); // Filler\n\n        displaybles.filler.setShape({\n          x: handleInterval[0],\n          y: 0,\n          width: handleInterval[1] - handleInterval[0],\n          height: size[1]\n        });\n        var viewExtent = {\n          x: handleInterval[0],\n          width: handleInterval[1] - handleInterval[0]\n        }; // Move handle\n\n        if (displaybles.moveHandle) {\n          displaybles.moveHandle.setShape(viewExtent);\n          displaybles.moveZone.setShape(viewExtent); // Force update path on the invisible object\n\n          displaybles.moveZone.getBoundingRect();\n          displaybles.moveHandleIcon && displaybles.moveHandleIcon.attr('x', viewExtent.x + viewExtent.width / 2);\n        } // update clip path of shadow.\n\n\n        var dataShadowSegs = displaybles.dataShadowSegs;\n        var segIntervals = [0, handleInterval[0], handleInterval[1], size[0]];\n\n        for (var i = 0; i < dataShadowSegs.length; i++) {\n          var segGroup = dataShadowSegs[i];\n          var clipPath = segGroup.getClipPath();\n\n          if (!clipPath) {\n            clipPath = new Rect();\n            segGroup.setClipPath(clipPath);\n          }\n\n          clipPath.setShape({\n            x: segIntervals[i],\n            y: 0,\n            width: segIntervals[i + 1] - segIntervals[i],\n            height: size[1]\n          });\n        }\n\n        this._updateDataInfo(nonRealtime);\n      };\n\n      SliderZoomView.prototype._updateDataInfo = function (nonRealtime) {\n        var dataZoomModel = this.dataZoomModel;\n        var displaybles = this._displayables;\n        var handleLabels = displaybles.handleLabels;\n        var orient = this._orient;\n        var labelTexts = ['', '']; // FIXME\n        // date型，支持formatter，autoformatter（ec2 date.getAutoFormatter）\n\n        if (dataZoomModel.get('showDetail')) {\n          var axisProxy = dataZoomModel.findRepresentativeAxisProxy();\n\n          if (axisProxy) {\n            var axis = axisProxy.getAxisModel().axis;\n            var range = this._range;\n            var dataInterval = nonRealtime // See #4434, data and axis are not processed and reset yet in non-realtime mode.\n            ? axisProxy.calculateDataWindow({\n              start: range[0],\n              end: range[1]\n            }).valueWindow : axisProxy.getDataValueWindow();\n            labelTexts = [this._formatLabel(dataInterval[0], axis), this._formatLabel(dataInterval[1], axis)];\n          }\n        }\n\n        var orderedHandleEnds = asc(this._handleEnds.slice());\n        setLabel.call(this, 0);\n        setLabel.call(this, 1);\n\n        function setLabel(handleIndex) {\n          // Label\n          // Text should not transform by barGroup.\n          // Ignore handlers transform\n          var barTransform = getTransform(displaybles.handles[handleIndex].parent, this.group);\n          var direction = transformDirection(handleIndex === 0 ? 'right' : 'left', barTransform);\n          var offset = this._handleWidth / 2 + LABEL_GAP;\n          var textPoint = applyTransform$1([orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), this._size[1] / 2], barTransform);\n          handleLabels[handleIndex].setStyle({\n            x: textPoint[0],\n            y: textPoint[1],\n            verticalAlign: orient === HORIZONTAL ? 'middle' : direction,\n            align: orient === HORIZONTAL ? direction : 'center',\n            text: labelTexts[handleIndex]\n          });\n        }\n      };\n\n      SliderZoomView.prototype._formatLabel = function (value, axis) {\n        var dataZoomModel = this.dataZoomModel;\n        var labelFormatter = dataZoomModel.get('labelFormatter');\n        var labelPrecision = dataZoomModel.get('labelPrecision');\n\n        if (labelPrecision == null || labelPrecision === 'auto') {\n          labelPrecision = axis.getPixelPrecision();\n        }\n\n        var valueStr = value == null || isNaN(value) ? '' // FIXME Glue code\n        : axis.type === 'category' || axis.type === 'time' ? axis.scale.getLabel({\n          value: Math.round(value)\n        }) // param of toFixed should less then 20.\n        : value.toFixed(Math.min(labelPrecision, 20));\n        return isFunction(labelFormatter) ? labelFormatter(value, valueStr) : isString(labelFormatter) ? labelFormatter.replace('{value}', valueStr) : valueStr;\n      };\n      /**\n       * @param showOrHide true: show, false: hide\n       */\n\n\n      SliderZoomView.prototype._showDataInfo = function (showOrHide) {\n        // Always show when drgging.\n        showOrHide = this._dragging || showOrHide;\n        var displayables = this._displayables;\n        var handleLabels = displayables.handleLabels;\n        handleLabels[0].attr('invisible', !showOrHide);\n        handleLabels[1].attr('invisible', !showOrHide); // Highlight move handle\n\n        displayables.moveHandle && this.api[showOrHide ? 'enterEmphasis' : 'leaveEmphasis'](displayables.moveHandle, 1);\n      };\n\n      SliderZoomView.prototype._onDragMove = function (handleIndex, dx, dy, event) {\n        this._dragging = true; // For mobile device, prevent screen slider on the button.\n\n        stop(event.event); // Transform dx, dy to bar coordination.\n\n        var barTransform = this._displayables.sliderGroup.getLocalTransform();\n\n        var vertex = applyTransform$1([dx, dy], barTransform, true);\n\n        var changed = this._updateInterval(handleIndex, vertex[0]);\n\n        var realtime = this.dataZoomModel.get('realtime');\n\n        this._updateView(!realtime); // Avoid dispatch dataZoom repeatly but range not changed,\n        // which cause bad visual effect when progressive enabled.\n\n\n        changed && realtime && this._dispatchZoomAction(true);\n      };\n\n      SliderZoomView.prototype._onDragEnd = function () {\n        this._dragging = false;\n\n        this._showDataInfo(false); // While in realtime mode and stream mode, dispatch action when\n        // drag end will cause the whole view rerender, which is unnecessary.\n\n\n        var realtime = this.dataZoomModel.get('realtime');\n        !realtime && this._dispatchZoomAction(false);\n      };\n\n      SliderZoomView.prototype._onClickPanel = function (e) {\n        var size = this._size;\n\n        var localPoint = this._displayables.sliderGroup.transformCoordToLocal(e.offsetX, e.offsetY);\n\n        if (localPoint[0] < 0 || localPoint[0] > size[0] || localPoint[1] < 0 || localPoint[1] > size[1]) {\n          return;\n        }\n\n        var handleEnds = this._handleEnds;\n        var center = (handleEnds[0] + handleEnds[1]) / 2;\n\n        var changed = this._updateInterval('all', localPoint[0] - center);\n\n        this._updateView();\n\n        changed && this._dispatchZoomAction(false);\n      };\n\n      SliderZoomView.prototype._onBrushStart = function (e) {\n        var x = e.offsetX;\n        var y = e.offsetY;\n        this._brushStart = new Point(x, y);\n        this._brushing = true;\n        this._brushStartTime = +new Date(); // this._updateBrushRect(x, y);\n      };\n\n      SliderZoomView.prototype._onBrushEnd = function (e) {\n        if (!this._brushing) {\n          return;\n        }\n\n        var brushRect = this._displayables.brushRect;\n        this._brushing = false;\n\n        if (!brushRect) {\n          return;\n        }\n\n        brushRect.attr('ignore', true);\n        var brushShape = brushRect.shape;\n        var brushEndTime = +new Date(); // console.log(brushEndTime - this._brushStartTime);\n\n        if (brushEndTime - this._brushStartTime < 200 && Math.abs(brushShape.width) < 5) {\n          // Will treat it as a click\n          return;\n        }\n\n        var viewExtend = this._getViewExtent();\n\n        var percentExtent = [0, 100];\n        this._range = asc([linearMap(brushShape.x, viewExtend, percentExtent, true), linearMap(brushShape.x + brushShape.width, viewExtend, percentExtent, true)]);\n        this._handleEnds = [brushShape.x, brushShape.x + brushShape.width];\n\n        this._updateView();\n\n        this._dispatchZoomAction(false);\n      };\n\n      SliderZoomView.prototype._onBrush = function (e) {\n        if (this._brushing) {\n          // For mobile device, prevent screen slider on the button.\n          stop(e.event);\n\n          this._updateBrushRect(e.offsetX, e.offsetY);\n        }\n      };\n\n      SliderZoomView.prototype._updateBrushRect = function (mouseX, mouseY) {\n        var displayables = this._displayables;\n        var dataZoomModel = this.dataZoomModel;\n        var brushRect = displayables.brushRect;\n\n        if (!brushRect) {\n          brushRect = displayables.brushRect = new Rect$2({\n            silent: true,\n            style: dataZoomModel.getModel('brushStyle').getItemStyle()\n          });\n          displayables.sliderGroup.add(brushRect);\n        }\n\n        brushRect.attr('ignore', false);\n        var brushStart = this._brushStart;\n        var sliderGroup = this._displayables.sliderGroup;\n        var endPoint = sliderGroup.transformCoordToLocal(mouseX, mouseY);\n        var startPoint = sliderGroup.transformCoordToLocal(brushStart.x, brushStart.y);\n        var size = this._size;\n        endPoint[0] = Math.max(Math.min(size[0], endPoint[0]), 0);\n        brushRect.setShape({\n          x: startPoint[0],\n          y: 0,\n          width: endPoint[0] - startPoint[0],\n          height: size[1]\n        });\n      };\n      /**\n       * This action will be throttled.\n       */\n\n\n      SliderZoomView.prototype._dispatchZoomAction = function (realtime) {\n        var range = this._range;\n        this.api.dispatchAction({\n          type: 'dataZoom',\n          from: this.uid,\n          dataZoomId: this.dataZoomModel.id,\n          animation: realtime ? REALTIME_ANIMATION_CONFIG : null,\n          start: range[0],\n          end: range[1]\n        });\n      };\n\n      SliderZoomView.prototype._findCoordRect = function () {\n        // Find the grid coresponding to the first axis referred by dataZoom.\n        var rect;\n        var coordSysInfoList = collectReferCoordSysModelInfo(this.dataZoomModel).infoList;\n\n        if (!rect && coordSysInfoList.length) {\n          var coordSys = coordSysInfoList[0].model.coordinateSystem;\n          rect = coordSys.getRect && coordSys.getRect();\n        }\n\n        if (!rect) {\n          var width = this.api.getWidth();\n          var height = this.api.getHeight();\n          rect = {\n            x: width * 0.2,\n            y: height * 0.2,\n            width: width * 0.6,\n            height: height * 0.6\n          };\n        }\n\n        return rect;\n      };\n\n      SliderZoomView.type = 'dataZoom.slider';\n      return SliderZoomView;\n    }(DataZoomView);\n\n    function getOtherDim(thisDim) {\n      // FIXME\n      // 这个逻辑和getOtherAxis里一致，但是写在这里是否不好\n      var map = {\n        x: 'y',\n        y: 'x',\n        radius: 'angle',\n        angle: 'radius'\n      };\n      return map[thisDim];\n    }\n\n    function getCursor(orient) {\n      return orient === 'vertical' ? 'ns-resize' : 'ew-resize';\n    }\n\n    function install$L(registers) {\n      registers.registerComponentModel(SliderZoomModel);\n      registers.registerComponentView(SliderZoomView);\n      installCommon(registers);\n    }\n\n    function install$M(registers) {\n      use(install$K);\n      use(install$L); // Do not install './dataZoomSelect',\n      // since it only work for toolbox dataZoom.\n    }\n\n    var visualDefault = {\n      /**\n       * @public\n       */\n      get: function (visualType, key, isCategory) {\n        var value = clone((defaultOption$1[visualType] || {})[key]);\n        return isCategory ? isArray(value) ? value[value.length - 1] : value : value;\n      }\n    };\n    var defaultOption$1 = {\n      color: {\n        active: ['#006edd', '#e0ffff'],\n        inactive: ['rgba(0,0,0,0)']\n      },\n      colorHue: {\n        active: [0, 360],\n        inactive: [0, 0]\n      },\n      colorSaturation: {\n        active: [0.3, 1],\n        inactive: [0, 0]\n      },\n      colorLightness: {\n        active: [0.9, 0.5],\n        inactive: [0, 0]\n      },\n      colorAlpha: {\n        active: [0.3, 1],\n        inactive: [0, 0]\n      },\n      opacity: {\n        active: [0.3, 1],\n        inactive: [0, 0]\n      },\n      symbol: {\n        active: ['circle', 'roundRect', 'diamond'],\n        inactive: ['none']\n      },\n      symbolSize: {\n        active: [10, 50],\n        inactive: [0, 0]\n      }\n    };\n\n    var mapVisual$1 = VisualMapping.mapVisual;\n    var eachVisual = VisualMapping.eachVisual;\n    var isArray$1 = isArray;\n    var each$d = each;\n    var asc$2 = asc;\n    var linearMap$1 = linearMap;\n\n    var VisualMapModel =\n    /** @class */\n    function (_super) {\n      __extends(VisualMapModel, _super);\n\n      function VisualMapModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = VisualMapModel.type;\n        _this.stateList = ['inRange', 'outOfRange'];\n        _this.replacableOptionKeys = ['inRange', 'outOfRange', 'target', 'controller', 'color'];\n        _this.layoutMode = {\n          type: 'box',\n          ignoreSize: true\n        };\n        /**\n         * [lowerBound, upperBound]\n         */\n\n        _this.dataBound = [-Infinity, Infinity];\n        _this.targetVisuals = {};\n        _this.controllerVisuals = {};\n        return _this;\n      }\n\n      VisualMapModel.prototype.init = function (option, parentModel, ecModel) {\n        this.mergeDefaultAndTheme(option, ecModel);\n      };\n      /**\n       * @protected\n       */\n\n\n      VisualMapModel.prototype.optionUpdated = function (newOption, isInit) {\n        var thisOption = this.option;\n        !isInit && replaceVisualOption(thisOption, newOption, this.replacableOptionKeys);\n        this.textStyleModel = this.getModel('textStyle');\n        this.resetItemSize();\n        this.completeVisualOption();\n      };\n      /**\n       * @protected\n       */\n\n\n      VisualMapModel.prototype.resetVisual = function (supplementVisualOption) {\n        var stateList = this.stateList;\n        supplementVisualOption = bind(supplementVisualOption, this);\n        this.controllerVisuals = createVisualMappings(this.option.controller, stateList, supplementVisualOption);\n        this.targetVisuals = createVisualMappings(this.option.target, stateList, supplementVisualOption);\n      };\n      /**\n       * @public\n       */\n\n\n      VisualMapModel.prototype.getItemSymbol = function () {\n        return null;\n      };\n      /**\n       * @protected\n       * @return {Array.<number>} An array of series indices.\n       */\n\n\n      VisualMapModel.prototype.getTargetSeriesIndices = function () {\n        var optionSeriesIndex = this.option.seriesIndex;\n        var seriesIndices = [];\n\n        if (optionSeriesIndex == null || optionSeriesIndex === 'all') {\n          this.ecModel.eachSeries(function (seriesModel, index) {\n            seriesIndices.push(index);\n          });\n        } else {\n          seriesIndices = normalizeToArray(optionSeriesIndex);\n        }\n\n        return seriesIndices;\n      };\n      /**\n       * @public\n       */\n\n\n      VisualMapModel.prototype.eachTargetSeries = function (callback, context) {\n        each(this.getTargetSeriesIndices(), function (seriesIndex) {\n          var seriesModel = this.ecModel.getSeriesByIndex(seriesIndex);\n\n          if (seriesModel) {\n            callback.call(context, seriesModel);\n          }\n        }, this);\n      };\n      /**\n       * @pubilc\n       */\n\n\n      VisualMapModel.prototype.isTargetSeries = function (seriesModel) {\n        var is = false;\n        this.eachTargetSeries(function (model) {\n          model === seriesModel && (is = true);\n        });\n        return is;\n      };\n      /**\n       * @example\n       * this.formatValueText(someVal); // format single numeric value to text.\n       * this.formatValueText(someVal, true); // format single category value to text.\n       * this.formatValueText([min, max]); // format numeric min-max to text.\n       * this.formatValueText([this.dataBound[0], max]); // using data lower bound.\n       * this.formatValueText([min, this.dataBound[1]]); // using data upper bound.\n       *\n       * @param value Real value, or this.dataBound[0 or 1].\n       * @param isCategory Only available when value is number.\n       * @param edgeSymbols Open-close symbol when value is interval.\n       * @protected\n       */\n\n\n      VisualMapModel.prototype.formatValueText = function (value, isCategory, edgeSymbols) {\n        var option = this.option;\n        var precision = option.precision;\n        var dataBound = this.dataBound;\n        var formatter = option.formatter;\n        var isMinMax;\n        edgeSymbols = edgeSymbols || ['<', '>'];\n\n        if (isArray(value)) {\n          value = value.slice();\n          isMinMax = true;\n        }\n\n        var textValue = isCategory ? value // Value is string when isCategory\n        : isMinMax ? [toFixed(value[0]), toFixed(value[1])] : toFixed(value);\n\n        if (isString(formatter)) {\n          return formatter.replace('{value}', isMinMax ? textValue[0] : textValue).replace('{value2}', isMinMax ? textValue[1] : textValue);\n        } else if (isFunction(formatter)) {\n          return isMinMax ? formatter(value[0], value[1]) : formatter(value);\n        }\n\n        if (isMinMax) {\n          if (value[0] === dataBound[0]) {\n            return edgeSymbols[0] + ' ' + textValue[1];\n          } else if (value[1] === dataBound[1]) {\n            return edgeSymbols[1] + ' ' + textValue[0];\n          } else {\n            return textValue[0] + ' - ' + textValue[1];\n          }\n        } else {\n          // Format single value (includes category case).\n          return textValue;\n        }\n\n        function toFixed(val) {\n          return val === dataBound[0] ? 'min' : val === dataBound[1] ? 'max' : (+val).toFixed(Math.min(precision, 20));\n        }\n      };\n      /**\n       * @protected\n       */\n\n\n      VisualMapModel.prototype.resetExtent = function () {\n        var thisOption = this.option; // Can not calculate data extent by data here.\n        // Because series and data may be modified in processing stage.\n        // So we do not support the feature \"auto min/max\".\n\n        var extent = asc$2([thisOption.min, thisOption.max]);\n        this._dataExtent = extent;\n      };\n      /**\n       * PENDING:\n       * delete this method if no outer usage.\n       *\n       * Return  Concrete dimention. If return null/undefined, no dimension used.\n       */\n      // getDataDimension(data: SeriesData) {\n      //     const optDim = this.option.dimension;\n      //     if (optDim != null) {\n      //         return data.getDimension(optDim);\n      //     }\n      //     const dimNames = data.dimensions;\n      //     for (let i = dimNames.length - 1; i >= 0; i--) {\n      //         const dimName = dimNames[i];\n      //         const dimInfo = data.getDimensionInfo(dimName);\n      //         if (!dimInfo.isCalculationCoord) {\n      //             return dimName;\n      //         }\n      //     }\n      // }\n\n\n      VisualMapModel.prototype.getDataDimensionIndex = function (data) {\n        var optDim = this.option.dimension;\n\n        if (optDim != null) {\n          return data.getDimensionIndex(optDim);\n        }\n\n        var dimNames = data.dimensions;\n\n        for (var i = dimNames.length - 1; i >= 0; i--) {\n          var dimName = dimNames[i];\n          var dimInfo = data.getDimensionInfo(dimName);\n\n          if (!dimInfo.isCalculationCoord) {\n            return dimInfo.storeDimIndex;\n          }\n        }\n      };\n\n      VisualMapModel.prototype.getExtent = function () {\n        return this._dataExtent.slice();\n      };\n\n      VisualMapModel.prototype.completeVisualOption = function () {\n        var ecModel = this.ecModel;\n        var thisOption = this.option;\n        var base = {\n          inRange: thisOption.inRange,\n          outOfRange: thisOption.outOfRange\n        };\n        var target = thisOption.target || (thisOption.target = {});\n        var controller = thisOption.controller || (thisOption.controller = {});\n        merge(target, base); // Do not override\n\n        merge(controller, base); // Do not override\n\n        var isCategory = this.isCategory();\n        completeSingle.call(this, target);\n        completeSingle.call(this, controller);\n        completeInactive.call(this, target, 'inRange', 'outOfRange'); // completeInactive.call(this, target, 'outOfRange', 'inRange');\n\n        completeController.call(this, controller);\n\n        function completeSingle(base) {\n          // Compatible with ec2 dataRange.color.\n          // The mapping order of dataRange.color is: [high value, ..., low value]\n          // whereas inRange.color and outOfRange.color is [low value, ..., high value]\n          // Notice: ec2 has no inverse.\n          if (isArray$1(thisOption.color) // If there has been inRange: {symbol: ...}, adding color is a mistake.\n          // So adding color only when no inRange defined.\n          && !base.inRange) {\n            base.inRange = {\n              color: thisOption.color.slice().reverse()\n            };\n          } // Compatible with previous logic, always give a defautl color, otherwise\n          // simple config with no inRange and outOfRange will not work.\n          // Originally we use visualMap.color as the default color, but setOption at\n          // the second time the default color will be erased. So we change to use\n          // constant DEFAULT_COLOR.\n          // If user do not want the default color, set inRange: {color: null}.\n\n\n          base.inRange = base.inRange || {\n            color: ecModel.get('gradientColor')\n          };\n        }\n\n        function completeInactive(base, stateExist, stateAbsent) {\n          var optExist = base[stateExist];\n          var optAbsent = base[stateAbsent];\n\n          if (optExist && !optAbsent) {\n            optAbsent = base[stateAbsent] = {};\n            each$d(optExist, function (visualData, visualType) {\n              if (!VisualMapping.isValidType(visualType)) {\n                return;\n              }\n\n              var defa = visualDefault.get(visualType, 'inactive', isCategory);\n\n              if (defa != null) {\n                optAbsent[visualType] = defa; // Compatibable with ec2:\n                // Only inactive color to rgba(0,0,0,0) can not\n                // make label transparent, so use opacity also.\n\n                if (visualType === 'color' && !optAbsent.hasOwnProperty('opacity') && !optAbsent.hasOwnProperty('colorAlpha')) {\n                  optAbsent.opacity = [0, 0];\n                }\n              }\n            });\n          }\n        }\n\n        function completeController(controller) {\n          var symbolExists = (controller.inRange || {}).symbol || (controller.outOfRange || {}).symbol;\n          var symbolSizeExists = (controller.inRange || {}).symbolSize || (controller.outOfRange || {}).symbolSize;\n          var inactiveColor = this.get('inactiveColor');\n          var itemSymbol = this.getItemSymbol();\n          var defaultSymbol = itemSymbol || 'roundRect';\n          each$d(this.stateList, function (state) {\n            var itemSize = this.itemSize;\n            var visuals = controller[state]; // Set inactive color for controller if no other color\n            // attr (like colorAlpha) specified.\n\n            if (!visuals) {\n              visuals = controller[state] = {\n                color: isCategory ? inactiveColor : [inactiveColor]\n              };\n            } // Consistent symbol and symbolSize if not specified.\n\n\n            if (visuals.symbol == null) {\n              visuals.symbol = symbolExists && clone(symbolExists) || (isCategory ? defaultSymbol : [defaultSymbol]);\n            }\n\n            if (visuals.symbolSize == null) {\n              visuals.symbolSize = symbolSizeExists && clone(symbolSizeExists) || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]);\n            } // Filter none\n\n\n            visuals.symbol = mapVisual$1(visuals.symbol, function (symbol) {\n              return symbol === 'none' ? defaultSymbol : symbol;\n            }); // Normalize symbolSize\n\n            var symbolSize = visuals.symbolSize;\n\n            if (symbolSize != null) {\n              var max_1 = -Infinity; // symbolSize can be object when categories defined.\n\n              eachVisual(symbolSize, function (value) {\n                value > max_1 && (max_1 = value);\n              });\n              visuals.symbolSize = mapVisual$1(symbolSize, function (value) {\n                return linearMap$1(value, [0, max_1], [0, itemSize[0]], true);\n              });\n            }\n          }, this);\n        }\n      };\n\n      VisualMapModel.prototype.resetItemSize = function () {\n        this.itemSize = [parseFloat(this.get('itemWidth')), parseFloat(this.get('itemHeight'))];\n      };\n\n      VisualMapModel.prototype.isCategory = function () {\n        return !!this.option.categories;\n      };\n      /**\n       * @public\n       * @abstract\n       */\n\n\n      VisualMapModel.prototype.setSelected = function (selected) {};\n\n      VisualMapModel.prototype.getSelected = function () {\n        return null;\n      };\n      /**\n       * @public\n       * @abstract\n       */\n\n\n      VisualMapModel.prototype.getValueState = function (value) {\n        return null;\n      };\n      /**\n       * FIXME\n       * Do not publish to thirt-part-dev temporarily\n       * util the interface is stable. (Should it return\n       * a function but not visual meta?)\n       *\n       * @pubilc\n       * @abstract\n       * @param getColorVisual\n       *        params: value, valueState\n       *        return: color\n       * @return {Object} visualMeta\n       *        should includes {stops, outerColors}\n       *        outerColor means [colorBeyondMinValue, colorBeyondMaxValue]\n       */\n\n\n      VisualMapModel.prototype.getVisualMeta = function (getColorVisual) {\n        return null;\n      };\n\n      VisualMapModel.type = 'visualMap';\n      VisualMapModel.dependencies = ['series'];\n      VisualMapModel.defaultOption = {\n        show: true,\n        // zlevel: 0,\n        z: 4,\n        seriesIndex: 'all',\n        min: 0,\n        max: 200,\n        left: 0,\n        right: null,\n        top: null,\n        bottom: 0,\n        itemWidth: null,\n        itemHeight: null,\n        inverse: false,\n        orient: 'vertical',\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderColor: '#ccc',\n        contentColor: '#5793f3',\n        inactiveColor: '#aaa',\n        borderWidth: 0,\n        padding: 5,\n        // 接受数组分别设定上右下左边距，同css\n        textGap: 10,\n        precision: 0,\n        textStyle: {\n          color: '#333' // 值域文字颜色\n\n        }\n      };\n      return VisualMapModel;\n    }(ComponentModel);\n\n    var DEFAULT_BAR_BOUND = [20, 140];\n\n    var ContinuousModel =\n    /** @class */\n    function (_super) {\n      __extends(ContinuousModel, _super);\n\n      function ContinuousModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ContinuousModel.type;\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      ContinuousModel.prototype.optionUpdated = function (newOption, isInit) {\n        _super.prototype.optionUpdated.apply(this, arguments);\n\n        this.resetExtent();\n        this.resetVisual(function (mappingOption) {\n          mappingOption.mappingMethod = 'linear';\n          mappingOption.dataExtent = this.getExtent();\n        });\n\n        this._resetRange();\n      };\n      /**\n       * @protected\n       * @override\n       */\n\n\n      ContinuousModel.prototype.resetItemSize = function () {\n        _super.prototype.resetItemSize.apply(this, arguments);\n\n        var itemSize = this.itemSize;\n        (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]);\n        (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]);\n      };\n      /**\n       * @private\n       */\n\n\n      ContinuousModel.prototype._resetRange = function () {\n        var dataExtent = this.getExtent();\n        var range = this.option.range;\n\n        if (!range || range.auto) {\n          // `range` should always be array (so we dont use other\n          // value like 'auto') for user-friend. (consider getOption).\n          dataExtent.auto = 1;\n          this.option.range = dataExtent;\n        } else if (isArray(range)) {\n          if (range[0] > range[1]) {\n            range.reverse();\n          }\n\n          range[0] = Math.max(range[0], dataExtent[0]);\n          range[1] = Math.min(range[1], dataExtent[1]);\n        }\n      };\n      /**\n       * @protected\n       * @override\n       */\n\n\n      ContinuousModel.prototype.completeVisualOption = function () {\n        _super.prototype.completeVisualOption.apply(this, arguments);\n\n        each(this.stateList, function (state) {\n          var symbolSize = this.option.controller[state].symbolSize;\n\n          if (symbolSize && symbolSize[0] !== symbolSize[1]) {\n            symbolSize[0] = symbolSize[1] / 3; // For good looking.\n          }\n        }, this);\n      };\n      /**\n       * @override\n       */\n\n\n      ContinuousModel.prototype.setSelected = function (selected) {\n        this.option.range = selected.slice();\n\n        this._resetRange();\n      };\n      /**\n       * @public\n       */\n\n\n      ContinuousModel.prototype.getSelected = function () {\n        var dataExtent = this.getExtent();\n        var dataInterval = asc((this.get('range') || []).slice()); // Clamp\n\n        dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]);\n        dataInterval[1] > dataExtent[1] && (dataInterval[1] = dataExtent[1]);\n        dataInterval[0] < dataExtent[0] && (dataInterval[0] = dataExtent[0]);\n        dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]);\n        return dataInterval;\n      };\n      /**\n       * @override\n       */\n\n\n      ContinuousModel.prototype.getValueState = function (value) {\n        var range = this.option.range;\n        var dataExtent = this.getExtent(); // When range[0] === dataExtent[0], any value larger than dataExtent[0] maps to 'inRange'.\n        // range[1] is processed likewise.\n\n        return (range[0] <= dataExtent[0] || range[0] <= value) && (range[1] >= dataExtent[1] || value <= range[1]) ? 'inRange' : 'outOfRange';\n      };\n\n      ContinuousModel.prototype.findTargetDataIndices = function (range) {\n        var result = [];\n        this.eachTargetSeries(function (seriesModel) {\n          var dataIndices = [];\n          var data = seriesModel.getData();\n          data.each(this.getDataDimensionIndex(data), function (value, dataIndex) {\n            range[0] <= value && value <= range[1] && dataIndices.push(dataIndex);\n          }, this);\n          result.push({\n            seriesId: seriesModel.id,\n            dataIndex: dataIndices\n          });\n        }, this);\n        return result;\n      };\n      /**\n       * @implement\n       */\n\n\n      ContinuousModel.prototype.getVisualMeta = function (getColorVisual) {\n        var oVals = getColorStopValues(this, 'outOfRange', this.getExtent());\n        var iVals = getColorStopValues(this, 'inRange', this.option.range.slice());\n        var stops = [];\n\n        function setStop(value, valueState) {\n          stops.push({\n            value: value,\n            color: getColorVisual(value, valueState)\n          });\n        } // Format to: outOfRange -- inRange -- outOfRange.\n\n\n        var iIdx = 0;\n        var oIdx = 0;\n        var iLen = iVals.length;\n        var oLen = oVals.length;\n\n        for (; oIdx < oLen && (!iVals.length || oVals[oIdx] <= iVals[0]); oIdx++) {\n          // If oVal[oIdx] === iVals[iIdx], oVal[oIdx] should be ignored.\n          if (oVals[oIdx] < iVals[iIdx]) {\n            setStop(oVals[oIdx], 'outOfRange');\n          }\n        }\n\n        for (var first = 1; iIdx < iLen; iIdx++, first = 0) {\n          // If range is full, value beyond min, max will be clamped.\n          // make a singularity\n          first && stops.length && setStop(iVals[iIdx], 'outOfRange');\n          setStop(iVals[iIdx], 'inRange');\n        }\n\n        for (var first = 1; oIdx < oLen; oIdx++) {\n          if (!iVals.length || iVals[iVals.length - 1] < oVals[oIdx]) {\n            // make a singularity\n            if (first) {\n              stops.length && setStop(stops[stops.length - 1].value, 'outOfRange');\n              first = 0;\n            }\n\n            setStop(oVals[oIdx], 'outOfRange');\n          }\n        }\n\n        var stopsLen = stops.length;\n        return {\n          stops: stops,\n          outerColors: [stopsLen ? stops[0].color : 'transparent', stopsLen ? stops[stopsLen - 1].color : 'transparent']\n        };\n      };\n\n      ContinuousModel.type = 'visualMap.continuous';\n      ContinuousModel.defaultOption = inheritDefaultOption(VisualMapModel.defaultOption, {\n        align: 'auto',\n        calculable: false,\n        hoverLink: true,\n        realtime: true,\n        handleIcon: 'path://M-11.39,9.77h0a3.5,3.5,0,0,1-3.5,3.5h-22a3.5,3.5,0,0,1-3.5-3.5h0a3.5,3.5,0,0,1,3.5-3.5h22A3.5,3.5,0,0,1-11.39,9.77Z',\n        handleSize: '120%',\n        handleStyle: {\n          borderColor: '#fff',\n          borderWidth: 1\n        },\n        indicatorIcon: 'circle',\n        indicatorSize: '50%',\n        indicatorStyle: {\n          borderColor: '#fff',\n          borderWidth: 2,\n          shadowBlur: 2,\n          shadowOffsetX: 1,\n          shadowOffsetY: 1,\n          shadowColor: 'rgba(0,0,0,0.2)'\n        } // emphasis: {\n        //     handleStyle: {\n        //         shadowBlur: 3,\n        //         shadowOffsetX: 1,\n        //         shadowOffsetY: 1,\n        //         shadowColor: 'rgba(0,0,0,0.2)'\n        //     }\n        // }\n\n      });\n      return ContinuousModel;\n    }(VisualMapModel);\n\n    function getColorStopValues(visualMapModel, valueState, dataExtent) {\n      if (dataExtent[0] === dataExtent[1]) {\n        return dataExtent.slice();\n      } // When using colorHue mapping, it is not linear color any more.\n      // Moreover, canvas gradient seems not to be accurate linear.\n      // FIXME\n      // Should be arbitrary value 100? or based on pixel size?\n\n\n      var count = 200;\n      var step = (dataExtent[1] - dataExtent[0]) / count;\n      var value = dataExtent[0];\n      var stopValues = [];\n\n      for (var i = 0; i <= count && value < dataExtent[1]; i++) {\n        stopValues.push(value);\n        value += step;\n      }\n\n      stopValues.push(dataExtent[1]);\n      return stopValues;\n    }\n\n    var VisualMapView =\n    /** @class */\n    function (_super) {\n      __extends(VisualMapView, _super);\n\n      function VisualMapView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = VisualMapView.type;\n        _this.autoPositionValues = {\n          left: 1,\n          right: 1,\n          top: 1,\n          bottom: 1\n        };\n        return _this;\n      }\n\n      VisualMapView.prototype.init = function (ecModel, api) {\n        this.ecModel = ecModel;\n        this.api = api;\n      };\n      /**\n       * @protected\n       */\n\n\n      VisualMapView.prototype.render = function (visualMapModel, ecModel, api, payload // TODO: TYPE\n      ) {\n        this.visualMapModel = visualMapModel;\n\n        if (visualMapModel.get('show') === false) {\n          this.group.removeAll();\n          return;\n        }\n\n        this.doRender(visualMapModel, ecModel, api, payload);\n      };\n      /**\n       * @protected\n       */\n\n\n      VisualMapView.prototype.renderBackground = function (group) {\n        var visualMapModel = this.visualMapModel;\n        var padding = normalizeCssArray$1(visualMapModel.get('padding') || 0);\n        var rect = group.getBoundingRect();\n        group.add(new Rect({\n          z2: -1,\n          silent: true,\n          shape: {\n            x: rect.x - padding[3],\n            y: rect.y - padding[0],\n            width: rect.width + padding[3] + padding[1],\n            height: rect.height + padding[0] + padding[2]\n          },\n          style: {\n            fill: visualMapModel.get('backgroundColor'),\n            stroke: visualMapModel.get('borderColor'),\n            lineWidth: visualMapModel.get('borderWidth')\n          }\n        }));\n      };\n      /**\n       * @protected\n       * @param targetValue can be Infinity or -Infinity\n       * @param visualCluster Only can be 'color' 'opacity' 'symbol' 'symbolSize'\n       * @param opts\n       * @param opts.forceState Specify state, instead of using getValueState method.\n       * @param opts.convertOpacityToAlpha For color gradient in controller widget.\n       * @return {*} Visual value.\n       */\n\n\n      VisualMapView.prototype.getControllerVisual = function (targetValue, visualCluster, opts) {\n        opts = opts || {};\n        var forceState = opts.forceState;\n        var visualMapModel = this.visualMapModel;\n        var visualObj = {}; // Default values.\n\n        if (visualCluster === 'color') {\n          var defaultColor = visualMapModel.get('contentColor');\n          visualObj.color = defaultColor;\n        }\n\n        function getter(key) {\n          return visualObj[key];\n        }\n\n        function setter(key, value) {\n          visualObj[key] = value;\n        }\n\n        var mappings = visualMapModel.controllerVisuals[forceState || visualMapModel.getValueState(targetValue)];\n        var visualTypes = VisualMapping.prepareVisualTypes(mappings);\n        each(visualTypes, function (type) {\n          var visualMapping = mappings[type];\n\n          if (opts.convertOpacityToAlpha && type === 'opacity') {\n            type = 'colorAlpha';\n            visualMapping = mappings.__alphaForOpacity;\n          }\n\n          if (VisualMapping.dependsOn(type, visualCluster)) {\n            visualMapping && visualMapping.applyVisual(targetValue, getter, setter);\n          }\n        });\n        return visualObj[visualCluster];\n      };\n\n      VisualMapView.prototype.positionGroup = function (group) {\n        var model = this.visualMapModel;\n        var api = this.api;\n        positionElement(group, model.getBoxLayoutParams(), {\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n      };\n\n      VisualMapView.prototype.doRender = function (visualMapModel, ecModel, api, payload) {};\n\n      VisualMapView.type = 'visualMap';\n      return VisualMapView;\n    }(ComponentView);\n\n    var paramsSet = [['left', 'right', 'width'], ['top', 'bottom', 'height']];\n    /**\n     * @param visualMapModel\n     * @param api\n     * @param itemSize always [short, long]\n     * @return {string} 'left' or 'right' or 'top' or 'bottom'\n     */\n\n    function getItemAlign(visualMapModel, api, itemSize) {\n      var modelOption = visualMapModel.option;\n      var itemAlign = modelOption.align;\n\n      if (itemAlign != null && itemAlign !== 'auto') {\n        return itemAlign;\n      } // Auto decision align.\n\n\n      var ecSize = {\n        width: api.getWidth(),\n        height: api.getHeight()\n      };\n      var realIndex = modelOption.orient === 'horizontal' ? 1 : 0;\n      var reals = paramsSet[realIndex];\n      var fakeValue = [0, null, 10];\n      var layoutInput = {};\n\n      for (var i = 0; i < 3; i++) {\n        layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i];\n        layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]];\n      }\n\n      var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex];\n      var rect = getLayoutRect(layoutInput, ecSize, modelOption.padding);\n      return reals[(rect.margin[rParam[2]] || 0) + rect[rParam[0]] + rect[rParam[1]] * 0.5 < ecSize[rParam[1]] * 0.5 ? 0 : 1];\n    }\n    /**\n     * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and\n     * dataIndexInside means filtered index.\n     */\n    // TODO: TYPE more specified payload types.\n\n    function makeHighDownBatch(batch, visualMapModel) {\n      each(batch || [], function (batchItem) {\n        if (batchItem.dataIndex != null) {\n          batchItem.dataIndexInside = batchItem.dataIndex;\n          batchItem.dataIndex = null;\n        }\n\n        batchItem.highlightKey = 'visualMap' + (visualMapModel ? visualMapModel.componentIndex : '');\n      });\n      return batch;\n    }\n\n    var linearMap$2 = linearMap;\n    var each$e = each;\n    var mathMin$a = Math.min;\n    var mathMax$a = Math.max; // Arbitrary value\n\n    var HOVER_LINK_SIZE = 12;\n    var HOVER_LINK_OUT = 6; // Notice:\n    // Any \"interval\" should be by the order of [low, high].\n    // \"handle0\" (handleIndex === 0) maps to\n    // low data value: this._dataInterval[0] and has low coord.\n    // \"handle1\" (handleIndex === 1) maps to\n    // high data value: this._dataInterval[1] and has high coord.\n    // The logic of transform is implemented in this._createBarGroup.\n\n    var ContinuousView =\n    /** @class */\n    function (_super) {\n      __extends(ContinuousView, _super);\n\n      function ContinuousView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = ContinuousView.type;\n        _this._shapes = {};\n        _this._dataInterval = [];\n        _this._handleEnds = [];\n        _this._hoverLinkDataIndices = [];\n        return _this;\n      }\n\n      ContinuousView.prototype.doRender = function (visualMapModel, ecModel, api, payload) {\n        this._api = api;\n\n        if (!payload || payload.type !== 'selectDataRange' || payload.from !== this.uid) {\n          this._buildView();\n        }\n      };\n\n      ContinuousView.prototype._buildView = function () {\n        this.group.removeAll();\n        var visualMapModel = this.visualMapModel;\n        var thisGroup = this.group;\n        this._orient = visualMapModel.get('orient');\n        this._useHandle = visualMapModel.get('calculable');\n\n        this._resetInterval();\n\n        this._renderBar(thisGroup);\n\n        var dataRangeText = visualMapModel.get('text');\n\n        this._renderEndsText(thisGroup, dataRangeText, 0);\n\n        this._renderEndsText(thisGroup, dataRangeText, 1); // Do this for background size calculation.\n\n\n        this._updateView(true); // After updating view, inner shapes is built completely,\n        // and then background can be rendered.\n\n\n        this.renderBackground(thisGroup); // Real update view\n\n        this._updateView();\n\n        this._enableHoverLinkToSeries();\n\n        this._enableHoverLinkFromSeries();\n\n        this.positionGroup(thisGroup);\n      };\n\n      ContinuousView.prototype._renderEndsText = function (group, dataRangeText, endsIndex) {\n        if (!dataRangeText) {\n          return;\n        } // Compatible with ec2, text[0] map to high value, text[1] map low value.\n\n\n        var text = dataRangeText[1 - endsIndex];\n        text = text != null ? text + '' : '';\n        var visualMapModel = this.visualMapModel;\n        var textGap = visualMapModel.get('textGap');\n        var itemSize = visualMapModel.itemSize;\n        var barGroup = this._shapes.mainGroup;\n\n        var position = this._applyTransform([itemSize[0] / 2, endsIndex === 0 ? -textGap : itemSize[1] + textGap], barGroup);\n\n        var align = this._applyTransform(endsIndex === 0 ? 'bottom' : 'top', barGroup);\n\n        var orient = this._orient;\n        var textStyleModel = this.visualMapModel.textStyleModel;\n        this.group.add(new ZRText({\n          style: createTextStyle(textStyleModel, {\n            x: position[0],\n            y: position[1],\n            verticalAlign: orient === 'horizontal' ? 'middle' : align,\n            align: orient === 'horizontal' ? align : 'center',\n            text: text\n          })\n        }));\n      };\n\n      ContinuousView.prototype._renderBar = function (targetGroup) {\n        var visualMapModel = this.visualMapModel;\n        var shapes = this._shapes;\n        var itemSize = visualMapModel.itemSize;\n        var orient = this._orient;\n        var useHandle = this._useHandle;\n        var itemAlign = getItemAlign(visualMapModel, this.api, itemSize);\n\n        var mainGroup = shapes.mainGroup = this._createBarGroup(itemAlign);\n\n        var gradientBarGroup = new Group();\n        mainGroup.add(gradientBarGroup); // Bar\n\n        gradientBarGroup.add(shapes.outOfRange = createPolygon());\n        gradientBarGroup.add(shapes.inRange = createPolygon(null, useHandle ? getCursor$1(this._orient) : null, bind(this._dragHandle, this, 'all', false), bind(this._dragHandle, this, 'all', true))); // A border radius clip.\n\n        gradientBarGroup.setClipPath(new Rect({\n          shape: {\n            x: 0,\n            y: 0,\n            width: itemSize[0],\n            height: itemSize[1],\n            r: 3\n          }\n        }));\n        var textRect = visualMapModel.textStyleModel.getTextRect('国');\n        var textSize = mathMax$a(textRect.width, textRect.height); // Handle\n\n        if (useHandle) {\n          shapes.handleThumbs = [];\n          shapes.handleLabels = [];\n          shapes.handleLabelPoints = [];\n\n          this._createHandle(visualMapModel, mainGroup, 0, itemSize, textSize, orient);\n\n          this._createHandle(visualMapModel, mainGroup, 1, itemSize, textSize, orient);\n        }\n\n        this._createIndicator(visualMapModel, mainGroup, itemSize, textSize, orient);\n\n        targetGroup.add(mainGroup);\n      };\n\n      ContinuousView.prototype._createHandle = function (visualMapModel, mainGroup, handleIndex, itemSize, textSize, orient) {\n        var onDrift = bind(this._dragHandle, this, handleIndex, false);\n        var onDragEnd = bind(this._dragHandle, this, handleIndex, true);\n        var handleSize = parsePercent(visualMapModel.get('handleSize'), itemSize[0]);\n        var handleThumb = createSymbol(visualMapModel.get('handleIcon'), -handleSize / 2, -handleSize / 2, handleSize, handleSize, null, true);\n        var cursor = getCursor$1(this._orient);\n        handleThumb.attr({\n          cursor: cursor,\n          draggable: true,\n          drift: onDrift,\n          ondragend: onDragEnd,\n          onmousemove: function (e) {\n            stop(e.event);\n          }\n        });\n        handleThumb.x = itemSize[0] / 2;\n        handleThumb.useStyle(visualMapModel.getModel('handleStyle').getItemStyle());\n        handleThumb.setStyle({\n          strokeNoScale: true,\n          strokeFirst: true\n        });\n        handleThumb.style.lineWidth *= 2;\n        handleThumb.ensureState('emphasis').style = visualMapModel.getModel(['emphasis', 'handleStyle']).getItemStyle();\n        setAsHighDownDispatcher(handleThumb, true);\n        mainGroup.add(handleThumb); // Text is always horizontal layout but should not be effected by\n        // transform (orient/inverse). So label is built separately but not\n        // use zrender/graphic/helper/RectText, and is located based on view\n        // group (according to handleLabelPoint) but not barGroup.\n\n        var textStyleModel = this.visualMapModel.textStyleModel;\n        var handleLabel = new ZRText({\n          cursor: cursor,\n          draggable: true,\n          drift: onDrift,\n          onmousemove: function (e) {\n            // Fot mobile devicem, prevent screen slider on the button.\n            stop(e.event);\n          },\n          ondragend: onDragEnd,\n          style: createTextStyle(textStyleModel, {\n            x: 0,\n            y: 0,\n            text: ''\n          })\n        });\n        handleLabel.ensureState('blur').style = {\n          opacity: 0.1\n        };\n        handleLabel.stateTransition = {\n          duration: 200\n        };\n        this.group.add(handleLabel);\n        var handleLabelPoint = [handleSize, 0];\n        var shapes = this._shapes;\n        shapes.handleThumbs[handleIndex] = handleThumb;\n        shapes.handleLabelPoints[handleIndex] = handleLabelPoint;\n        shapes.handleLabels[handleIndex] = handleLabel;\n      };\n\n      ContinuousView.prototype._createIndicator = function (visualMapModel, mainGroup, itemSize, textSize, orient) {\n        var scale = parsePercent(visualMapModel.get('indicatorSize'), itemSize[0]);\n        var indicator = createSymbol(visualMapModel.get('indicatorIcon'), -scale / 2, -scale / 2, scale, scale, null, true);\n        indicator.attr({\n          cursor: 'move',\n          invisible: true,\n          silent: true,\n          x: itemSize[0] / 2\n        });\n        var indicatorStyle = visualMapModel.getModel('indicatorStyle').getItemStyle();\n\n        if (indicator instanceof ZRImage) {\n          var pathStyle = indicator.style;\n          indicator.useStyle(extend({\n            // TODO other properties like x, y ?\n            image: pathStyle.image,\n            x: pathStyle.x,\n            y: pathStyle.y,\n            width: pathStyle.width,\n            height: pathStyle.height\n          }, indicatorStyle));\n        } else {\n          indicator.useStyle(indicatorStyle);\n        }\n\n        mainGroup.add(indicator);\n        var textStyleModel = this.visualMapModel.textStyleModel;\n        var indicatorLabel = new ZRText({\n          silent: true,\n          invisible: true,\n          style: createTextStyle(textStyleModel, {\n            x: 0,\n            y: 0,\n            text: ''\n          })\n        });\n        this.group.add(indicatorLabel);\n        var indicatorLabelPoint = [(orient === 'horizontal' ? textSize / 2 : HOVER_LINK_OUT) + itemSize[0] / 2, 0];\n        var shapes = this._shapes;\n        shapes.indicator = indicator;\n        shapes.indicatorLabel = indicatorLabel;\n        shapes.indicatorLabelPoint = indicatorLabelPoint;\n        this._firstShowIndicator = true;\n      };\n\n      ContinuousView.prototype._dragHandle = function (handleIndex, isEnd, // dx is event from ondragend if isEnd is true. It's not used\n      dx, dy) {\n        if (!this._useHandle) {\n          return;\n        }\n\n        this._dragging = !isEnd;\n\n        if (!isEnd) {\n          // Transform dx, dy to bar coordination.\n          var vertex = this._applyTransform([dx, dy], this._shapes.mainGroup, true);\n\n          this._updateInterval(handleIndex, vertex[1]);\n\n          this._hideIndicator(); // Considering realtime, update view should be executed\n          // before dispatch action.\n\n\n          this._updateView();\n        } // dragEnd do not dispatch action when realtime.\n\n\n        if (isEnd === !this.visualMapModel.get('realtime')) {\n          // jshint ignore:line\n          this.api.dispatchAction({\n            type: 'selectDataRange',\n            from: this.uid,\n            visualMapId: this.visualMapModel.id,\n            selected: this._dataInterval.slice()\n          });\n        }\n\n        if (isEnd) {\n          !this._hovering && this._clearHoverLinkToSeries();\n        } else if (useHoverLinkOnHandle(this.visualMapModel)) {\n          this._doHoverLinkToSeries(this._handleEnds[handleIndex], false);\n        }\n      };\n\n      ContinuousView.prototype._resetInterval = function () {\n        var visualMapModel = this.visualMapModel;\n        var dataInterval = this._dataInterval = visualMapModel.getSelected();\n        var dataExtent = visualMapModel.getExtent();\n        var sizeExtent = [0, visualMapModel.itemSize[1]];\n        this._handleEnds = [linearMap$2(dataInterval[0], dataExtent, sizeExtent, true), linearMap$2(dataInterval[1], dataExtent, sizeExtent, true)];\n      };\n      /**\n       * @private\n       * @param {(number|string)} handleIndex 0 or 1 or 'all'\n       * @param {number} dx\n       * @param {number} dy\n       */\n\n\n      ContinuousView.prototype._updateInterval = function (handleIndex, delta) {\n        delta = delta || 0;\n        var visualMapModel = this.visualMapModel;\n        var handleEnds = this._handleEnds;\n        var sizeExtent = [0, visualMapModel.itemSize[1]];\n        sliderMove(delta, handleEnds, sizeExtent, handleIndex, // cross is forbiden\n        0);\n        var dataExtent = visualMapModel.getExtent(); // Update data interval.\n\n        this._dataInterval = [linearMap$2(handleEnds[0], sizeExtent, dataExtent, true), linearMap$2(handleEnds[1], sizeExtent, dataExtent, true)];\n      };\n\n      ContinuousView.prototype._updateView = function (forSketch) {\n        var visualMapModel = this.visualMapModel;\n        var dataExtent = visualMapModel.getExtent();\n        var shapes = this._shapes;\n        var outOfRangeHandleEnds = [0, visualMapModel.itemSize[1]];\n        var inRangeHandleEnds = forSketch ? outOfRangeHandleEnds : this._handleEnds;\n\n        var visualInRange = this._createBarVisual(this._dataInterval, dataExtent, inRangeHandleEnds, 'inRange');\n\n        var visualOutOfRange = this._createBarVisual(dataExtent, dataExtent, outOfRangeHandleEnds, 'outOfRange');\n\n        shapes.inRange.setStyle({\n          fill: visualInRange.barColor // opacity: visualInRange.opacity\n\n        }).setShape('points', visualInRange.barPoints);\n        shapes.outOfRange.setStyle({\n          fill: visualOutOfRange.barColor // opacity: visualOutOfRange.opacity\n\n        }).setShape('points', visualOutOfRange.barPoints);\n\n        this._updateHandle(inRangeHandleEnds, visualInRange);\n      };\n\n      ContinuousView.prototype._createBarVisual = function (dataInterval, dataExtent, handleEnds, forceState) {\n        var opts = {\n          forceState: forceState,\n          convertOpacityToAlpha: true\n        };\n\n        var colorStops = this._makeColorGradient(dataInterval, opts);\n\n        var symbolSizes = [this.getControllerVisual(dataInterval[0], 'symbolSize', opts), this.getControllerVisual(dataInterval[1], 'symbolSize', opts)];\n\n        var barPoints = this._createBarPoints(handleEnds, symbolSizes);\n\n        return {\n          barColor: new LinearGradient(0, 0, 0, 1, colorStops),\n          barPoints: barPoints,\n          handlesColor: [colorStops[0].color, colorStops[colorStops.length - 1].color]\n        };\n      };\n\n      ContinuousView.prototype._makeColorGradient = function (dataInterval, opts) {\n        // Considering colorHue, which is not linear, so we have to sample\n        // to calculate gradient color stops, but not only caculate head\n        // and tail.\n        var sampleNumber = 100; // Arbitrary value.\n\n        var colorStops = [];\n        var step = (dataInterval[1] - dataInterval[0]) / sampleNumber;\n        colorStops.push({\n          color: this.getControllerVisual(dataInterval[0], 'color', opts),\n          offset: 0\n        });\n\n        for (var i = 1; i < sampleNumber; i++) {\n          var currValue = dataInterval[0] + step * i;\n\n          if (currValue > dataInterval[1]) {\n            break;\n          }\n\n          colorStops.push({\n            color: this.getControllerVisual(currValue, 'color', opts),\n            offset: i / sampleNumber\n          });\n        }\n\n        colorStops.push({\n          color: this.getControllerVisual(dataInterval[1], 'color', opts),\n          offset: 1\n        });\n        return colorStops;\n      };\n\n      ContinuousView.prototype._createBarPoints = function (handleEnds, symbolSizes) {\n        var itemSize = this.visualMapModel.itemSize;\n        return [[itemSize[0] - symbolSizes[0], handleEnds[0]], [itemSize[0], handleEnds[0]], [itemSize[0], handleEnds[1]], [itemSize[0] - symbolSizes[1], handleEnds[1]]];\n      };\n\n      ContinuousView.prototype._createBarGroup = function (itemAlign) {\n        var orient = this._orient;\n        var inverse = this.visualMapModel.get('inverse');\n        return new Group(orient === 'horizontal' && !inverse ? {\n          scaleX: itemAlign === 'bottom' ? 1 : -1,\n          rotation: Math.PI / 2\n        } : orient === 'horizontal' && inverse ? {\n          scaleX: itemAlign === 'bottom' ? -1 : 1,\n          rotation: -Math.PI / 2\n        } : orient === 'vertical' && !inverse ? {\n          scaleX: itemAlign === 'left' ? 1 : -1,\n          scaleY: -1\n        } : {\n          scaleX: itemAlign === 'left' ? 1 : -1\n        });\n      };\n\n      ContinuousView.prototype._updateHandle = function (handleEnds, visualInRange) {\n        if (!this._useHandle) {\n          return;\n        }\n\n        var shapes = this._shapes;\n        var visualMapModel = this.visualMapModel;\n        var handleThumbs = shapes.handleThumbs;\n        var handleLabels = shapes.handleLabels;\n        var itemSize = visualMapModel.itemSize;\n        var dataExtent = visualMapModel.getExtent();\n        each$e([0, 1], function (handleIndex) {\n          var handleThumb = handleThumbs[handleIndex];\n          handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]);\n          handleThumb.y = handleEnds[handleIndex];\n          var val = linearMap$2(handleEnds[handleIndex], [0, itemSize[1]], dataExtent, true);\n          var symbolSize = this.getControllerVisual(val, 'symbolSize');\n          handleThumb.scaleX = handleThumb.scaleY = symbolSize / itemSize[0];\n          handleThumb.x = itemSize[0] - symbolSize / 2; // Update handle label position.\n\n          var textPoint = applyTransform$1(shapes.handleLabelPoints[handleIndex], getTransform(handleThumb, this.group));\n          handleLabels[handleIndex].setStyle({\n            x: textPoint[0],\n            y: textPoint[1],\n            text: visualMapModel.formatValueText(this._dataInterval[handleIndex]),\n            verticalAlign: 'middle',\n            align: this._orient === 'vertical' ? this._applyTransform('left', shapes.mainGroup) : 'center'\n          });\n        }, this);\n      };\n\n      ContinuousView.prototype._showIndicator = function (cursorValue, textValue, rangeSymbol, halfHoverLinkSize) {\n        var visualMapModel = this.visualMapModel;\n        var dataExtent = visualMapModel.getExtent();\n        var itemSize = visualMapModel.itemSize;\n        var sizeExtent = [0, itemSize[1]];\n        var shapes = this._shapes;\n        var indicator = shapes.indicator;\n\n        if (!indicator) {\n          return;\n        }\n\n        indicator.attr('invisible', false);\n        var opts = {\n          convertOpacityToAlpha: true\n        };\n        var color = this.getControllerVisual(cursorValue, 'color', opts);\n        var symbolSize = this.getControllerVisual(cursorValue, 'symbolSize');\n        var y = linearMap$2(cursorValue, dataExtent, sizeExtent, true);\n        var x = itemSize[0] - symbolSize / 2;\n        var oldIndicatorPos = {\n          x: indicator.x,\n          y: indicator.y\n        }; // Update handle label position.\n\n        indicator.y = y;\n        indicator.x = x;\n        var textPoint = applyTransform$1(shapes.indicatorLabelPoint, getTransform(indicator, this.group));\n        var indicatorLabel = shapes.indicatorLabel;\n        indicatorLabel.attr('invisible', false);\n\n        var align = this._applyTransform('left', shapes.mainGroup);\n\n        var orient = this._orient;\n        var isHorizontal = orient === 'horizontal';\n        indicatorLabel.setStyle({\n          text: (rangeSymbol ? rangeSymbol : '') + visualMapModel.formatValueText(textValue),\n          verticalAlign: isHorizontal ? align : 'middle',\n          align: isHorizontal ? 'center' : align\n        });\n        var indicatorNewProps = {\n          x: x,\n          y: y,\n          style: {\n            fill: color\n          }\n        };\n        var labelNewProps = {\n          style: {\n            x: textPoint[0],\n            y: textPoint[1]\n          }\n        };\n\n        if (visualMapModel.ecModel.isAnimationEnabled() && !this._firstShowIndicator) {\n          var animationCfg = {\n            duration: 100,\n            easing: 'cubicInOut',\n            additive: true\n          };\n          indicator.x = oldIndicatorPos.x;\n          indicator.y = oldIndicatorPos.y;\n          indicator.animateTo(indicatorNewProps, animationCfg);\n          indicatorLabel.animateTo(labelNewProps, animationCfg);\n        } else {\n          indicator.attr(indicatorNewProps);\n          indicatorLabel.attr(labelNewProps);\n        }\n\n        this._firstShowIndicator = false;\n        var handleLabels = this._shapes.handleLabels;\n\n        if (handleLabels) {\n          for (var i = 0; i < handleLabels.length; i++) {\n            // Fade out handle labels.\n            // NOTE: Must use api enter/leave on emphasis/blur/select state. Or the global states manager will change it.\n            this._api.enterBlur(handleLabels[i]);\n          }\n        }\n      };\n\n      ContinuousView.prototype._enableHoverLinkToSeries = function () {\n        var self = this;\n\n        this._shapes.mainGroup.on('mousemove', function (e) {\n          self._hovering = true;\n\n          if (!self._dragging) {\n            var itemSize = self.visualMapModel.itemSize;\n\n            var pos = self._applyTransform([e.offsetX, e.offsetY], self._shapes.mainGroup, true, true); // For hover link show when hover handle, which might be\n            // below or upper than sizeExtent.\n\n\n            pos[1] = mathMin$a(mathMax$a(0, pos[1]), itemSize[1]);\n\n            self._doHoverLinkToSeries(pos[1], 0 <= pos[0] && pos[0] <= itemSize[0]);\n          }\n        }).on('mouseout', function () {\n          // When mouse is out of handle, hoverLink still need\n          // to be displayed when realtime is set as false.\n          self._hovering = false;\n          !self._dragging && self._clearHoverLinkToSeries();\n        });\n      };\n\n      ContinuousView.prototype._enableHoverLinkFromSeries = function () {\n        var zr = this.api.getZr();\n\n        if (this.visualMapModel.option.hoverLink) {\n          zr.on('mouseover', this._hoverLinkFromSeriesMouseOver, this);\n          zr.on('mouseout', this._hideIndicator, this);\n        } else {\n          this._clearHoverLinkFromSeries();\n        }\n      };\n\n      ContinuousView.prototype._doHoverLinkToSeries = function (cursorPos, hoverOnBar) {\n        var visualMapModel = this.visualMapModel;\n        var itemSize = visualMapModel.itemSize;\n\n        if (!visualMapModel.option.hoverLink) {\n          return;\n        }\n\n        var sizeExtent = [0, itemSize[1]];\n        var dataExtent = visualMapModel.getExtent(); // For hover link show when hover handle, which might be below or upper than sizeExtent.\n\n        cursorPos = mathMin$a(mathMax$a(sizeExtent[0], cursorPos), sizeExtent[1]);\n        var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent);\n        var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize];\n        var cursorValue = linearMap$2(cursorPos, sizeExtent, dataExtent, true);\n        var valueRange = [linearMap$2(hoverRange[0], sizeExtent, dataExtent, true), linearMap$2(hoverRange[1], sizeExtent, dataExtent, true)]; // Consider data range is out of visualMap range, see test/visualMap-continuous.html,\n        // where china and india has very large population.\n\n        hoverRange[0] < sizeExtent[0] && (valueRange[0] = -Infinity);\n        hoverRange[1] > sizeExtent[1] && (valueRange[1] = Infinity); // Do not show indicator when mouse is over handle,\n        // otherwise labels overlap, especially when dragging.\n\n        if (hoverOnBar) {\n          if (valueRange[0] === -Infinity) {\n            this._showIndicator(cursorValue, valueRange[1], '< ', halfHoverLinkSize);\n          } else if (valueRange[1] === Infinity) {\n            this._showIndicator(cursorValue, valueRange[0], '> ', halfHoverLinkSize);\n          } else {\n            this._showIndicator(cursorValue, cursorValue, '≈ ', halfHoverLinkSize);\n          }\n        } // When realtime is set as false, handles, which are in barGroup,\n        // also trigger hoverLink, which help user to realize where they\n        // focus on when dragging. (see test/heatmap-large.html)\n        // When realtime is set as true, highlight will not show when hover\n        // handle, because the label on handle, which displays a exact value\n        // but not range, might mislead users.\n\n\n        var oldBatch = this._hoverLinkDataIndices;\n        var newBatch = [];\n\n        if (hoverOnBar || useHoverLinkOnHandle(visualMapModel)) {\n          newBatch = this._hoverLinkDataIndices = visualMapModel.findTargetDataIndices(valueRange);\n        }\n\n        var resultBatches = compressBatches(oldBatch, newBatch);\n\n        this._dispatchHighDown('downplay', makeHighDownBatch(resultBatches[0], visualMapModel));\n\n        this._dispatchHighDown('highlight', makeHighDownBatch(resultBatches[1], visualMapModel));\n      };\n\n      ContinuousView.prototype._hoverLinkFromSeriesMouseOver = function (e) {\n        var ecData;\n        findEventDispatcher(e.target, function (target) {\n          var currECData = getECData(target);\n\n          if (currECData.dataIndex != null) {\n            ecData = currECData;\n            return true;\n          }\n        }, true);\n\n        if (!ecData) {\n          return;\n        }\n\n        var dataModel = this.ecModel.getSeriesByIndex(ecData.seriesIndex);\n        var visualMapModel = this.visualMapModel;\n\n        if (!visualMapModel.isTargetSeries(dataModel)) {\n          return;\n        }\n\n        var data = dataModel.getData(ecData.dataType);\n        var value = data.getStore().get(visualMapModel.getDataDimensionIndex(data), ecData.dataIndex);\n\n        if (!isNaN(value)) {\n          this._showIndicator(value, value);\n        }\n      };\n\n      ContinuousView.prototype._hideIndicator = function () {\n        var shapes = this._shapes;\n        shapes.indicator && shapes.indicator.attr('invisible', true);\n        shapes.indicatorLabel && shapes.indicatorLabel.attr('invisible', true);\n        var handleLabels = this._shapes.handleLabels;\n\n        if (handleLabels) {\n          for (var i = 0; i < handleLabels.length; i++) {\n            // Fade out handle labels.\n            // NOTE: Must use api enter/leave on emphasis/blur/select state. Or the global states manager will change it.\n            this._api.leaveBlur(handleLabels[i]);\n          }\n        }\n      };\n\n      ContinuousView.prototype._clearHoverLinkToSeries = function () {\n        this._hideIndicator();\n\n        var indices = this._hoverLinkDataIndices;\n\n        this._dispatchHighDown('downplay', makeHighDownBatch(indices, this.visualMapModel));\n\n        indices.length = 0;\n      };\n\n      ContinuousView.prototype._clearHoverLinkFromSeries = function () {\n        this._hideIndicator();\n\n        var zr = this.api.getZr();\n        zr.off('mouseover', this._hoverLinkFromSeriesMouseOver);\n        zr.off('mouseout', this._hideIndicator);\n      };\n\n      ContinuousView.prototype._applyTransform = function (vertex, element, inverse, global) {\n        var transform = getTransform(element, global ? null : this.group);\n        return isArray(vertex) ? applyTransform$1(vertex, transform, inverse) : transformDirection(vertex, transform, inverse);\n      }; // TODO: TYPE more specified payload types.\n\n\n      ContinuousView.prototype._dispatchHighDown = function (type, batch) {\n        batch && batch.length && this.api.dispatchAction({\n          type: type,\n          batch: batch\n        });\n      };\n      /**\n       * @override\n       */\n\n\n      ContinuousView.prototype.dispose = function () {\n        this._clearHoverLinkFromSeries();\n\n        this._clearHoverLinkToSeries();\n      };\n      /**\n       * @override\n       */\n\n\n      ContinuousView.prototype.remove = function () {\n        this._clearHoverLinkFromSeries();\n\n        this._clearHoverLinkToSeries();\n      };\n\n      ContinuousView.type = 'visualMap.continuous';\n      return ContinuousView;\n    }(VisualMapView);\n\n    function createPolygon(points, cursor, onDrift, onDragEnd) {\n      return new Polygon({\n        shape: {\n          points: points\n        },\n        draggable: !!onDrift,\n        cursor: cursor,\n        drift: onDrift,\n        onmousemove: function (e) {\n          // Fot mobile devicem, prevent screen slider on the button.\n          stop(e.event);\n        },\n        ondragend: onDragEnd\n      });\n    }\n\n    function getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent) {\n      var halfHoverLinkSize = HOVER_LINK_SIZE / 2;\n      var hoverLinkDataSize = visualMapModel.get('hoverLinkDataSize');\n\n      if (hoverLinkDataSize) {\n        halfHoverLinkSize = linearMap$2(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2;\n      }\n\n      return halfHoverLinkSize;\n    }\n\n    function useHoverLinkOnHandle(visualMapModel) {\n      var hoverLinkOnHandle = visualMapModel.get('hoverLinkOnHandle');\n      return !!(hoverLinkOnHandle == null ? visualMapModel.get('realtime') : hoverLinkOnHandle);\n    }\n\n    function getCursor$1(orient) {\n      return orient === 'vertical' ? 'ns-resize' : 'ew-resize';\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var visualMapActionInfo = {\n      type: 'selectDataRange',\n      event: 'dataRangeSelected',\n      // FIXME use updateView appears wrong\n      update: 'update'\n    };\n    var visualMapActionHander = function (payload, ecModel) {\n      ecModel.eachComponent({\n        mainType: 'visualMap',\n        query: payload\n      }, function (model) {\n        model.setSelected(payload.selected);\n      });\n    };\n\n    var visualMapEncodingHandlers = [{\n      createOnAllSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var resetDefines = [];\n        ecModel.eachComponent('visualMap', function (visualMapModel) {\n          var pipelineContext = seriesModel.pipelineContext;\n\n          if (!visualMapModel.isTargetSeries(seriesModel) || pipelineContext && pipelineContext.large) {\n            return;\n          }\n\n          resetDefines.push(incrementalApplyVisual(visualMapModel.stateList, visualMapModel.targetVisuals, bind(visualMapModel.getValueState, visualMapModel), visualMapModel.getDataDimensionIndex(seriesModel.getData())));\n        });\n        return resetDefines;\n      }\n    }, // Only support color.\n    {\n      createOnAllSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var data = seriesModel.getData();\n        var visualMetaList = [];\n        ecModel.eachComponent('visualMap', function (visualMapModel) {\n          if (visualMapModel.isTargetSeries(seriesModel)) {\n            var visualMeta = visualMapModel.getVisualMeta(bind(getColorVisual, null, seriesModel, visualMapModel)) || {\n              stops: [],\n              outerColors: []\n            };\n            var dimIdx = visualMapModel.getDataDimensionIndex(data);\n\n            if (dimIdx >= 0) {\n              // visualMeta.dimension should be dimension index, but not concrete dimension.\n              visualMeta.dimension = dimIdx;\n              visualMetaList.push(visualMeta);\n            }\n          }\n        }); // console.log(JSON.stringify(visualMetaList.map(a => a.stops)));\n\n        seriesModel.getData().setVisual('visualMeta', visualMetaList);\n      }\n    }]; // FIXME\n    // performance and export for heatmap?\n    // value can be Infinity or -Infinity\n\n    function getColorVisual(seriesModel, visualMapModel, value, valueState) {\n      var mappings = visualMapModel.targetVisuals[valueState];\n      var visualTypes = VisualMapping.prepareVisualTypes(mappings);\n      var resultVisual = {\n        color: getVisualFromData(seriesModel.getData(), 'color') // default color.\n\n      };\n\n      for (var i = 0, len = visualTypes.length; i < len; i++) {\n        var type = visualTypes[i];\n        var mapping = mappings[type === 'opacity' ? '__alphaForOpacity' : type];\n        mapping && mapping.applyVisual(value, getVisual, setVisual);\n      }\n\n      return resultVisual.color;\n\n      function getVisual(key) {\n        return resultVisual[key];\n      }\n\n      function setVisual(key, value) {\n        resultVisual[key] = value;\n      }\n    }\n\n    var each$f = each;\n    function visualMapPreprocessor(option) {\n      var visualMap = option && option.visualMap;\n\n      if (!isArray(visualMap)) {\n        visualMap = visualMap ? [visualMap] : [];\n      }\n\n      each$f(visualMap, function (opt) {\n        if (!opt) {\n          return;\n        } // rename splitList to pieces\n\n\n        if (has$1(opt, 'splitList') && !has$1(opt, 'pieces')) {\n          opt.pieces = opt.splitList;\n          delete opt.splitList;\n        }\n\n        var pieces = opt.pieces;\n\n        if (pieces && isArray(pieces)) {\n          each$f(pieces, function (piece) {\n            if (isObject(piece)) {\n              if (has$1(piece, 'start') && !has$1(piece, 'min')) {\n                piece.min = piece.start;\n              }\n\n              if (has$1(piece, 'end') && !has$1(piece, 'max')) {\n                piece.max = piece.end;\n              }\n            }\n          });\n        }\n      });\n    }\n\n    function has$1(obj, name) {\n      return obj && obj.hasOwnProperty && obj.hasOwnProperty(name);\n    }\n\n    var installed$1 = false;\n    function installCommon$1(registers) {\n      if (installed$1) {\n        return;\n      }\n\n      installed$1 = true;\n      registers.registerSubTypeDefaulter('visualMap', function (option) {\n        // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used.\n        return !option.categories && (!(option.pieces ? option.pieces.length > 0 : option.splitNumber > 0) || option.calculable) ? 'continuous' : 'piecewise';\n      });\n      registers.registerAction(visualMapActionInfo, visualMapActionHander);\n      each(visualMapEncodingHandlers, function (handler) {\n        registers.registerVisual(registers.PRIORITY.VISUAL.COMPONENT, handler);\n      });\n      registers.registerPreprocessor(visualMapPreprocessor);\n    }\n\n    function install$N(registers) {\n      registers.registerComponentModel(ContinuousModel);\n      registers.registerComponentView(ContinuousView);\n      installCommon$1(registers);\n    }\n\n    var PiecewiseModel =\n    /** @class */\n    function (_super) {\n      __extends(PiecewiseModel, _super);\n\n      function PiecewiseModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = PiecewiseModel.type;\n        /**\n         * The order is always [low, ..., high].\n         * [{text: string, interval: Array.<number>}, ...]\n         */\n\n        _this._pieceList = [];\n        return _this;\n      }\n\n      PiecewiseModel.prototype.optionUpdated = function (newOption, isInit) {\n        _super.prototype.optionUpdated.apply(this, arguments);\n\n        this.resetExtent();\n\n        var mode = this._mode = this._determineMode();\n\n        this._pieceList = [];\n\n        resetMethods[this._mode].call(this, this._pieceList);\n\n        this._resetSelected(newOption, isInit);\n\n        var categories = this.option.categories;\n        this.resetVisual(function (mappingOption, state) {\n          if (mode === 'categories') {\n            mappingOption.mappingMethod = 'category';\n            mappingOption.categories = clone(categories);\n          } else {\n            mappingOption.dataExtent = this.getExtent();\n            mappingOption.mappingMethod = 'piecewise';\n            mappingOption.pieceList = map(this._pieceList, function (piece) {\n              piece = clone(piece);\n\n              if (state !== 'inRange') {\n                // FIXME\n                // outOfRange do not support special visual in pieces.\n                piece.visual = null;\n              }\n\n              return piece;\n            });\n          }\n        });\n      };\n      /**\n       * @protected\n       * @override\n       */\n\n\n      PiecewiseModel.prototype.completeVisualOption = function () {\n        // Consider this case:\n        // visualMap: {\n        //      pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}]\n        // }\n        // where no inRange/outOfRange set but only pieces. So we should make\n        // default inRange/outOfRange for this case, otherwise visuals that only\n        // appear in `pieces` will not be taken into account in visual encoding.\n        var option = this.option;\n        var visualTypesInPieces = {};\n        var visualTypes = VisualMapping.listVisualTypes();\n        var isCategory = this.isCategory();\n        each(option.pieces, function (piece) {\n          each(visualTypes, function (visualType) {\n            if (piece.hasOwnProperty(visualType)) {\n              visualTypesInPieces[visualType] = 1;\n            }\n          });\n        });\n        each(visualTypesInPieces, function (v, visualType) {\n          var exists = false;\n          each(this.stateList, function (state) {\n            exists = exists || has(option, state, visualType) || has(option.target, state, visualType);\n          }, this);\n          !exists && each(this.stateList, function (state) {\n            (option[state] || (option[state] = {}))[visualType] = visualDefault.get(visualType, state === 'inRange' ? 'active' : 'inactive', isCategory);\n          });\n        }, this);\n\n        function has(obj, state, visualType) {\n          return obj && obj[state] && obj[state].hasOwnProperty(visualType);\n        }\n\n        _super.prototype.completeVisualOption.apply(this, arguments);\n      };\n\n      PiecewiseModel.prototype._resetSelected = function (newOption, isInit) {\n        var thisOption = this.option;\n        var pieceList = this._pieceList; // Selected do not merge but all override.\n\n        var selected = (isInit ? thisOption : newOption).selected || {};\n        thisOption.selected = selected; // Consider 'not specified' means true.\n\n        each(pieceList, function (piece, index) {\n          var key = this.getSelectedMapKey(piece);\n\n          if (!selected.hasOwnProperty(key)) {\n            selected[key] = true;\n          }\n        }, this);\n\n        if (thisOption.selectedMode === 'single') {\n          // Ensure there is only one selected.\n          var hasSel_1 = false;\n          each(pieceList, function (piece, index) {\n            var key = this.getSelectedMapKey(piece);\n\n            if (selected[key]) {\n              hasSel_1 ? selected[key] = false : hasSel_1 = true;\n            }\n          }, this);\n        } // thisOption.selectedMode === 'multiple', default: all selected.\n\n      };\n      /**\n       * @public\n       */\n\n\n      PiecewiseModel.prototype.getItemSymbol = function () {\n        return this.get('itemSymbol');\n      };\n      /**\n       * @public\n       */\n\n\n      PiecewiseModel.prototype.getSelectedMapKey = function (piece) {\n        return this._mode === 'categories' ? piece.value + '' : piece.index + '';\n      };\n      /**\n       * @public\n       */\n\n\n      PiecewiseModel.prototype.getPieceList = function () {\n        return this._pieceList;\n      };\n      /**\n       * @return {string}\n       */\n\n\n      PiecewiseModel.prototype._determineMode = function () {\n        var option = this.option;\n        return option.pieces && option.pieces.length > 0 ? 'pieces' : this.option.categories ? 'categories' : 'splitNumber';\n      };\n      /**\n       * @override\n       */\n\n\n      PiecewiseModel.prototype.setSelected = function (selected) {\n        this.option.selected = clone(selected);\n      };\n      /**\n       * @override\n       */\n\n\n      PiecewiseModel.prototype.getValueState = function (value) {\n        var index = VisualMapping.findPieceIndex(value, this._pieceList);\n        return index != null ? this.option.selected[this.getSelectedMapKey(this._pieceList[index])] ? 'inRange' : 'outOfRange' : 'outOfRange';\n      };\n      /**\n       * @public\n       * @param pieceIndex piece index in visualMapModel.getPieceList()\n       */\n\n\n      PiecewiseModel.prototype.findTargetDataIndices = function (pieceIndex) {\n        var result = [];\n        var pieceList = this._pieceList;\n        this.eachTargetSeries(function (seriesModel) {\n          var dataIndices = [];\n          var data = seriesModel.getData();\n          data.each(this.getDataDimensionIndex(data), function (value, dataIndex) {\n            // Should always base on model pieceList, because it is order sensitive.\n            var pIdx = VisualMapping.findPieceIndex(value, pieceList);\n            pIdx === pieceIndex && dataIndices.push(dataIndex);\n          }, this);\n          result.push({\n            seriesId: seriesModel.id,\n            dataIndex: dataIndices\n          });\n        }, this);\n        return result;\n      };\n      /**\n       * @private\n       * @param piece piece.value or piece.interval is required.\n       * @return  Can be Infinity or -Infinity\n       */\n\n\n      PiecewiseModel.prototype.getRepresentValue = function (piece) {\n        var representValue;\n\n        if (this.isCategory()) {\n          representValue = piece.value;\n        } else {\n          if (piece.value != null) {\n            representValue = piece.value;\n          } else {\n            var pieceInterval = piece.interval || [];\n            representValue = pieceInterval[0] === -Infinity && pieceInterval[1] === Infinity ? 0 : (pieceInterval[0] + pieceInterval[1]) / 2;\n          }\n        }\n\n        return representValue;\n      };\n\n      PiecewiseModel.prototype.getVisualMeta = function (getColorVisual) {\n        // Do not support category. (category axis is ordinal, numerical)\n        if (this.isCategory()) {\n          return;\n        }\n\n        var stops = [];\n        var outerColors = ['', ''];\n        var visualMapModel = this;\n\n        function setStop(interval, valueState) {\n          var representValue = visualMapModel.getRepresentValue({\n            interval: interval\n          }); // Not category\n\n          if (!valueState) {\n            valueState = visualMapModel.getValueState(representValue);\n          }\n\n          var color = getColorVisual(representValue, valueState);\n\n          if (interval[0] === -Infinity) {\n            outerColors[0] = color;\n          } else if (interval[1] === Infinity) {\n            outerColors[1] = color;\n          } else {\n            stops.push({\n              value: interval[0],\n              color: color\n            }, {\n              value: interval[1],\n              color: color\n            });\n          }\n        } // Suplement\n\n\n        var pieceList = this._pieceList.slice();\n\n        if (!pieceList.length) {\n          pieceList.push({\n            interval: [-Infinity, Infinity]\n          });\n        } else {\n          var edge = pieceList[0].interval[0];\n          edge !== -Infinity && pieceList.unshift({\n            interval: [-Infinity, edge]\n          });\n          edge = pieceList[pieceList.length - 1].interval[1];\n          edge !== Infinity && pieceList.push({\n            interval: [edge, Infinity]\n          });\n        }\n\n        var curr = -Infinity;\n        each(pieceList, function (piece) {\n          var interval = piece.interval;\n\n          if (interval) {\n            // Fulfill gap.\n            interval[0] > curr && setStop([curr, interval[0]], 'outOfRange');\n            setStop(interval.slice());\n            curr = interval[1];\n          }\n        }, this);\n        return {\n          stops: stops,\n          outerColors: outerColors\n        };\n      };\n\n      PiecewiseModel.type = 'visualMap.piecewise';\n      PiecewiseModel.defaultOption = inheritDefaultOption(VisualMapModel.defaultOption, {\n        selected: null,\n        minOpen: false,\n        maxOpen: false,\n        align: 'auto',\n        itemWidth: 20,\n        itemHeight: 14,\n        itemSymbol: 'roundRect',\n        pieces: null,\n        categories: null,\n        splitNumber: 5,\n        selectedMode: 'multiple',\n        itemGap: 10,\n        hoverLink: true // Enable hover highlight.\n\n      });\n      return PiecewiseModel;\n    }(VisualMapModel);\n    /**\n     * Key is this._mode\n     * @type {Object}\n     * @this {module:echarts/component/viusalMap/PiecewiseMode}\n     */\n\n    var resetMethods = {\n      splitNumber: function (outPieceList) {\n        var thisOption = this.option;\n        var precision = Math.min(thisOption.precision, 20);\n        var dataExtent = this.getExtent();\n        var splitNumber = thisOption.splitNumber;\n        splitNumber = Math.max(parseInt(splitNumber, 10), 1);\n        thisOption.splitNumber = splitNumber;\n        var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; // Precision auto-adaption\n\n        while (+splitStep.toFixed(precision) !== splitStep && precision < 5) {\n          precision++;\n        }\n\n        thisOption.precision = precision;\n        splitStep = +splitStep.toFixed(precision);\n\n        if (thisOption.minOpen) {\n          outPieceList.push({\n            interval: [-Infinity, dataExtent[0]],\n            close: [0, 0]\n          });\n        }\n\n        for (var index = 0, curr = dataExtent[0]; index < splitNumber; curr += splitStep, index++) {\n          var max = index === splitNumber - 1 ? dataExtent[1] : curr + splitStep;\n          outPieceList.push({\n            interval: [curr, max],\n            close: [1, 1]\n          });\n        }\n\n        if (thisOption.maxOpen) {\n          outPieceList.push({\n            interval: [dataExtent[1], Infinity],\n            close: [0, 0]\n          });\n        }\n\n        reformIntervals(outPieceList);\n        each(outPieceList, function (piece, index) {\n          piece.index = index;\n          piece.text = this.formatValueText(piece.interval);\n        }, this);\n      },\n      categories: function (outPieceList) {\n        var thisOption = this.option;\n        each(thisOption.categories, function (cate) {\n          // FIXME category模式也使用pieceList，但在visualMapping中不是使用pieceList。\n          // 是否改一致。\n          outPieceList.push({\n            text: this.formatValueText(cate, true),\n            value: cate\n          });\n        }, this); // See \"Order Rule\".\n\n        normalizeReverse(thisOption, outPieceList);\n      },\n      pieces: function (outPieceList) {\n        var thisOption = this.option;\n        each(thisOption.pieces, function (pieceListItem, index) {\n          if (!isObject(pieceListItem)) {\n            pieceListItem = {\n              value: pieceListItem\n            };\n          }\n\n          var item = {\n            text: '',\n            index: index\n          };\n\n          if (pieceListItem.label != null) {\n            item.text = pieceListItem.label;\n          }\n\n          if (pieceListItem.hasOwnProperty('value')) {\n            var value = item.value = pieceListItem.value;\n            item.interval = [value, value];\n            item.close = [1, 1];\n          } else {\n            // `min` `max` is legacy option.\n            // `lt` `gt` `lte` `gte` is recommanded.\n            var interval = item.interval = [];\n            var close_1 = item.close = [0, 0];\n            var closeList = [1, 0, 1];\n            var infinityList = [-Infinity, Infinity];\n            var useMinMax = [];\n\n            for (var lg = 0; lg < 2; lg++) {\n              var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg];\n\n              for (var i = 0; i < 3 && interval[lg] == null; i++) {\n                interval[lg] = pieceListItem[names[i]];\n                close_1[lg] = closeList[i];\n                useMinMax[lg] = i === 2;\n              }\n\n              interval[lg] == null && (interval[lg] = infinityList[lg]);\n            }\n\n            useMinMax[0] && interval[1] === Infinity && (close_1[0] = 0);\n            useMinMax[1] && interval[0] === -Infinity && (close_1[1] = 0);\n\n            if (\"development\" !== 'production') {\n              if (interval[0] > interval[1]) {\n                console.warn('Piece ' + index + 'is illegal: ' + interval + ' lower bound should not greater then uppper bound.');\n              }\n            }\n\n            if (interval[0] === interval[1] && close_1[0] && close_1[1]) {\n              // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}],\n              // we use value to lift the priority when min === max\n              item.value = interval[0];\n            }\n          }\n\n          item.visual = VisualMapping.retrieveVisuals(pieceListItem);\n          outPieceList.push(item);\n        }, this); // See \"Order Rule\".\n\n        normalizeReverse(thisOption, outPieceList); // Only pieces\n\n        reformIntervals(outPieceList);\n        each(outPieceList, function (piece) {\n          var close = piece.close;\n          var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]];\n          piece.text = piece.text || this.formatValueText(piece.value != null ? piece.value : piece.interval, false, edgeSymbols);\n        }, this);\n      }\n    };\n\n    function normalizeReverse(thisOption, pieceList) {\n      var inverse = thisOption.inverse;\n\n      if (thisOption.orient === 'vertical' ? !inverse : inverse) {\n        pieceList.reverse();\n      }\n    }\n\n    var PiecewiseVisualMapView =\n    /** @class */\n    function (_super) {\n      __extends(PiecewiseVisualMapView, _super);\n\n      function PiecewiseVisualMapView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = PiecewiseVisualMapView.type;\n        return _this;\n      }\n\n      PiecewiseVisualMapView.prototype.doRender = function () {\n        var thisGroup = this.group;\n        thisGroup.removeAll();\n        var visualMapModel = this.visualMapModel;\n        var textGap = visualMapModel.get('textGap');\n        var textStyleModel = visualMapModel.textStyleModel;\n        var textFont = textStyleModel.getFont();\n        var textFill = textStyleModel.getTextColor();\n\n        var itemAlign = this._getItemAlign();\n\n        var itemSize = visualMapModel.itemSize;\n\n        var viewData = this._getViewData();\n\n        var endsText = viewData.endsText;\n        var showLabel = retrieve(visualMapModel.get('showLabel', true), !endsText);\n        endsText && this._renderEndsText(thisGroup, endsText[0], itemSize, showLabel, itemAlign);\n        each(viewData.viewPieceList, function (item) {\n          var piece = item.piece;\n          var itemGroup = new Group();\n          itemGroup.onclick = bind(this._onItemClick, this, piece);\n\n          this._enableHoverLink(itemGroup, item.indexInModelPieceList); // TODO Category\n\n\n          var representValue = visualMapModel.getRepresentValue(piece);\n\n          this._createItemSymbol(itemGroup, representValue, [0, 0, itemSize[0], itemSize[1]]);\n\n          if (showLabel) {\n            var visualState = this.visualMapModel.getValueState(representValue);\n            itemGroup.add(new ZRText({\n              style: {\n                x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap,\n                y: itemSize[1] / 2,\n                text: piece.text,\n                verticalAlign: 'middle',\n                align: itemAlign,\n                font: textFont,\n                fill: textFill,\n                opacity: visualState === 'outOfRange' ? 0.5 : 1\n              }\n            }));\n          }\n\n          thisGroup.add(itemGroup);\n        }, this);\n        endsText && this._renderEndsText(thisGroup, endsText[1], itemSize, showLabel, itemAlign);\n        box(visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap'));\n        this.renderBackground(thisGroup);\n        this.positionGroup(thisGroup);\n      };\n\n      PiecewiseVisualMapView.prototype._enableHoverLink = function (itemGroup, pieceIndex) {\n        var _this = this;\n\n        itemGroup.on('mouseover', function () {\n          return onHoverLink('highlight');\n        }).on('mouseout', function () {\n          return onHoverLink('downplay');\n        });\n\n        var onHoverLink = function (method) {\n          var visualMapModel = _this.visualMapModel; // TODO: TYPE More detailed action types\n\n          visualMapModel.option.hoverLink && _this.api.dispatchAction({\n            type: method,\n            batch: makeHighDownBatch(visualMapModel.findTargetDataIndices(pieceIndex), visualMapModel)\n          });\n        };\n      };\n\n      PiecewiseVisualMapView.prototype._getItemAlign = function () {\n        var visualMapModel = this.visualMapModel;\n        var modelOption = visualMapModel.option;\n\n        if (modelOption.orient === 'vertical') {\n          return getItemAlign(visualMapModel, this.api, visualMapModel.itemSize);\n        } else {\n          // horizontal, most case left unless specifying right.\n          var align = modelOption.align;\n\n          if (!align || align === 'auto') {\n            align = 'left';\n          }\n\n          return align;\n        }\n      };\n\n      PiecewiseVisualMapView.prototype._renderEndsText = function (group, text, itemSize, showLabel, itemAlign) {\n        if (!text) {\n          return;\n        }\n\n        var itemGroup = new Group();\n        var textStyleModel = this.visualMapModel.textStyleModel;\n        itemGroup.add(new ZRText({\n          style: createTextStyle(textStyleModel, {\n            x: showLabel ? itemAlign === 'right' ? itemSize[0] : 0 : itemSize[0] / 2,\n            y: itemSize[1] / 2,\n            verticalAlign: 'middle',\n            align: showLabel ? itemAlign : 'center',\n            text: text\n          })\n        }));\n        group.add(itemGroup);\n      };\n      /**\n       * @private\n       * @return {Object} {peiceList, endsText} The order is the same as screen pixel order.\n       */\n\n\n      PiecewiseVisualMapView.prototype._getViewData = function () {\n        var visualMapModel = this.visualMapModel;\n        var viewPieceList = map(visualMapModel.getPieceList(), function (piece, index) {\n          return {\n            piece: piece,\n            indexInModelPieceList: index\n          };\n        });\n        var endsText = visualMapModel.get('text'); // Consider orient and inverse.\n\n        var orient = visualMapModel.get('orient');\n        var inverse = visualMapModel.get('inverse'); // Order of model pieceList is always [low, ..., high]\n\n        if (orient === 'horizontal' ? inverse : !inverse) {\n          viewPieceList.reverse();\n        } // Origin order of endsText is [high, low]\n        else if (endsText) {\n            endsText = endsText.slice().reverse();\n          }\n\n        return {\n          viewPieceList: viewPieceList,\n          endsText: endsText\n        };\n      };\n\n      PiecewiseVisualMapView.prototype._createItemSymbol = function (group, representValue, shapeParam) {\n        group.add(createSymbol( // symbol will be string\n        this.getControllerVisual(representValue, 'symbol'), shapeParam[0], shapeParam[1], shapeParam[2], shapeParam[3], // color will be string\n        this.getControllerVisual(representValue, 'color')));\n      };\n\n      PiecewiseVisualMapView.prototype._onItemClick = function (piece) {\n        var visualMapModel = this.visualMapModel;\n        var option = visualMapModel.option;\n        var selectedMode = option.selectedMode;\n\n        if (!selectedMode) {\n          return;\n        }\n\n        var selected = clone(option.selected);\n        var newKey = visualMapModel.getSelectedMapKey(piece);\n\n        if (selectedMode === 'single' || selectedMode === true) {\n          selected[newKey] = true;\n          each(selected, function (o, key) {\n            selected[key] = key === newKey;\n          });\n        } else {\n          selected[newKey] = !selected[newKey];\n        }\n\n        this.api.dispatchAction({\n          type: 'selectDataRange',\n          from: this.uid,\n          visualMapId: this.visualMapModel.id,\n          selected: selected\n        });\n      };\n\n      PiecewiseVisualMapView.type = 'visualMap.piecewise';\n      return PiecewiseVisualMapView;\n    }(VisualMapView);\n\n    function install$O(registers) {\n      registers.registerComponentModel(PiecewiseModel);\n      registers.registerComponentView(PiecewiseVisualMapView);\n      installCommon$1(registers);\n    }\n\n    function install$P(registers) {\n      use(install$N);\n      use(install$O); // Do not install './dataZoomSelect',\n      // since it only work for toolbox dataZoom.\n    }\n\n    var DEFAULT_OPTION = {\n      label: {\n        enabled: true\n      },\n      decal: {\n        show: false\n      }\n    };\n    var inner$l = makeInner();\n    var decalPaletteScope = {};\n    function ariaVisual(ecModel, api) {\n      var ariaModel = ecModel.getModel('aria'); // See \"area enabled\" detection code in `GlobalModel.ts`.\n\n      if (!ariaModel.get('enabled')) {\n        return;\n      }\n\n      var defaultOption = clone(DEFAULT_OPTION);\n      merge(defaultOption.label, ecModel.getLocaleModel().get('aria'), false);\n      merge(ariaModel.option, defaultOption, false);\n      setDecal();\n      setLabel();\n\n      function setDecal() {\n        var decalModel = ariaModel.getModel('decal');\n        var useDecal = decalModel.get('show');\n\n        if (useDecal) {\n          // Each type of series use one scope.\n          // Pie and funnel are using different scopes.\n          var paletteScopeGroupByType_1 = createHashMap();\n          ecModel.eachSeries(function (seriesModel) {\n            if (seriesModel.isColorBySeries()) {\n              return;\n            }\n\n            var decalScope = paletteScopeGroupByType_1.get(seriesModel.type);\n\n            if (!decalScope) {\n              decalScope = {};\n              paletteScopeGroupByType_1.set(seriesModel.type, decalScope);\n            }\n\n            inner$l(seriesModel).scope = decalScope;\n          });\n          ecModel.eachRawSeries(function (seriesModel) {\n            if (ecModel.isSeriesFiltered(seriesModel)) {\n              return;\n            }\n\n            if (isFunction(seriesModel.enableAriaDecal)) {\n              // Let series define how to use decal palette on data\n              seriesModel.enableAriaDecal();\n              return;\n            }\n\n            var data = seriesModel.getData();\n\n            if (!seriesModel.isColorBySeries()) {\n              var dataAll_1 = seriesModel.getRawData();\n              var idxMap_1 = {};\n              var decalScope_1 = inner$l(seriesModel).scope;\n              data.each(function (idx) {\n                var rawIdx = data.getRawIndex(idx);\n                idxMap_1[rawIdx] = idx;\n              });\n              var dataCount_1 = dataAll_1.count();\n              dataAll_1.each(function (rawIdx) {\n                var idx = idxMap_1[rawIdx];\n                var name = dataAll_1.getName(rawIdx) || rawIdx + '';\n                var paletteDecal = getDecalFromPalette(seriesModel.ecModel, name, decalScope_1, dataCount_1);\n                var specifiedDecal = data.getItemVisual(idx, 'decal');\n                data.setItemVisual(idx, 'decal', mergeDecal(specifiedDecal, paletteDecal));\n              });\n            } else {\n              var paletteDecal = getDecalFromPalette(seriesModel.ecModel, seriesModel.name, decalPaletteScope, ecModel.getSeriesCount());\n              var specifiedDecal = data.getVisual('decal');\n              data.setVisual('decal', mergeDecal(specifiedDecal, paletteDecal));\n            }\n\n            function mergeDecal(specifiedDecal, paletteDecal) {\n              // Merge decal from palette to decal from itemStyle.\n              // User do not need to specify all of the decal props.\n              var resultDecal = specifiedDecal ? extend(extend({}, paletteDecal), specifiedDecal) : paletteDecal;\n              resultDecal.dirty = true;\n              return resultDecal;\n            }\n          });\n        }\n      }\n\n      function setLabel() {\n        var labelLocale = ecModel.getLocaleModel().get('aria');\n        var labelModel = ariaModel.getModel('label');\n        labelModel.option = defaults(labelModel.option, labelLocale);\n\n        if (!labelModel.get('enabled')) {\n          return;\n        }\n\n        var dom = api.getZr().dom;\n\n        if (labelModel.get('description')) {\n          dom.setAttribute('aria-label', labelModel.get('description'));\n          return;\n        }\n\n        var seriesCnt = ecModel.getSeriesCount();\n        var maxDataCnt = labelModel.get(['data', 'maxCount']) || 10;\n        var maxSeriesCnt = labelModel.get(['series', 'maxCount']) || 10;\n        var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt);\n        var ariaLabel;\n\n        if (seriesCnt < 1) {\n          // No series, no aria label\n          return;\n        } else {\n          var title = getTitle();\n\n          if (title) {\n            var withTitle = labelModel.get(['general', 'withTitle']);\n            ariaLabel = replace(withTitle, {\n              title: title\n            });\n          } else {\n            ariaLabel = labelModel.get(['general', 'withoutTitle']);\n          }\n\n          var seriesLabels_1 = [];\n          var prefix = seriesCnt > 1 ? labelModel.get(['series', 'multiple', 'prefix']) : labelModel.get(['series', 'single', 'prefix']);\n          ariaLabel += replace(prefix, {\n            seriesCount: seriesCnt\n          });\n          ecModel.eachSeries(function (seriesModel, idx) {\n            if (idx < displaySeriesCnt) {\n              var seriesLabel = void 0;\n              var seriesName = seriesModel.get('name');\n              var withName = seriesName ? 'withName' : 'withoutName';\n              seriesLabel = seriesCnt > 1 ? labelModel.get(['series', 'multiple', withName]) : labelModel.get(['series', 'single', withName]);\n              seriesLabel = replace(seriesLabel, {\n                seriesId: seriesModel.seriesIndex,\n                seriesName: seriesModel.get('name'),\n                seriesType: getSeriesTypeName(seriesModel.subType)\n              });\n              var data = seriesModel.getData();\n\n              if (data.count() > maxDataCnt) {\n                // Show part of data\n                var partialLabel = labelModel.get(['data', 'partialData']);\n                seriesLabel += replace(partialLabel, {\n                  displayCnt: maxDataCnt\n                });\n              } else {\n                seriesLabel += labelModel.get(['data', 'allData']);\n              }\n\n              var middleSeparator_1 = labelModel.get(['data', 'separator', 'middle']);\n              var endSeparator_1 = labelModel.get(['data', 'separator', 'end']);\n              var dataLabels = [];\n\n              for (var i = 0; i < data.count(); i++) {\n                if (i < maxDataCnt) {\n                  var name_1 = data.getName(i);\n                  var value = data.getValues(i);\n                  var dataLabel = labelModel.get(['data', name_1 ? 'withName' : 'withoutName']);\n                  dataLabels.push(replace(dataLabel, {\n                    name: name_1,\n                    value: value.join(middleSeparator_1)\n                  }));\n                }\n              }\n\n              seriesLabel += dataLabels.join(middleSeparator_1) + endSeparator_1;\n              seriesLabels_1.push(seriesLabel);\n            }\n          });\n          var separatorModel = labelModel.getModel(['series', 'multiple', 'separator']);\n          var middleSeparator = separatorModel.get('middle');\n          var endSeparator = separatorModel.get('end');\n          ariaLabel += seriesLabels_1.join(middleSeparator) + endSeparator;\n          dom.setAttribute('aria-label', ariaLabel);\n        }\n      }\n\n      function replace(str, keyValues) {\n        if (!isString(str)) {\n          return str;\n        }\n\n        var result = str;\n        each(keyValues, function (value, key) {\n          result = result.replace(new RegExp('\\\\{\\\\s*' + key + '\\\\s*\\\\}', 'g'), value);\n        });\n        return result;\n      }\n\n      function getTitle() {\n        var title = ecModel.get('title');\n\n        if (title && title.length) {\n          title = title[0];\n        }\n\n        return title && title.text;\n      }\n\n      function getSeriesTypeName(type) {\n        return ecModel.getLocaleModel().get(['series', 'typeNames'])[type] || '自定义图';\n      }\n    }\n\n    function ariaPreprocessor(option) {\n      if (!option || !option.aria) {\n        return;\n      }\n\n      var aria = option.aria; // aria.show is deprecated and should use aria.enabled instead\n\n      if (aria.show != null) {\n        aria.enabled = aria.show;\n      }\n\n      aria.label = aria.label || {}; // move description, general, series, data to be under aria.label\n\n      each(['description', 'general', 'series', 'data'], function (name) {\n        if (aria[name] != null) {\n          aria.label[name] = aria[name];\n        }\n      });\n    }\n\n    function install$Q(registers) {\n      registers.registerPreprocessor(ariaPreprocessor);\n      registers.registerVisual(registers.PRIORITY.VISUAL.ARIA, ariaVisual);\n    }\n\n    var RELATIONAL_EXPRESSION_OP_ALIAS_MAP = {\n      value: 'eq',\n      // PENDING: not good for literal semantic?\n      '<': 'lt',\n      '<=': 'lte',\n      '>': 'gt',\n      '>=': 'gte',\n      '=': 'eq',\n      '!=': 'ne',\n      '<>': 'ne' // Might be misleading for sake of the difference between '==' and '===',\n      // so don't support them.\n      // '==': 'eq',\n      // '===': 'seq',\n      // '!==': 'sne'\n      // PENDING: Whether support some common alias \"ge\", \"le\", \"neq\"?\n      // ge: 'gte',\n      // le: 'lte',\n      // neq: 'ne',\n\n    }; // type RelationalExpressionOpEvaluate = (tarVal: unknown, condVal: unknown) => boolean;\n\n    var RegExpEvaluator =\n    /** @class */\n    function () {\n      function RegExpEvaluator(rVal) {\n        // Support condVal: RegExp | string\n        var condValue = this._condVal = isString(rVal) ? new RegExp(rVal) : isRegExp(rVal) ? rVal : null;\n\n        if (condValue == null) {\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('Illegal regexp', rVal, 'in');\n          }\n\n          throwError(errMsg);\n        }\n      }\n\n      RegExpEvaluator.prototype.evaluate = function (lVal) {\n        var type = typeof lVal;\n        return isString(type) ? this._condVal.test(lVal) : isNumber(type) ? this._condVal.test(lVal + '') : false;\n      };\n\n      return RegExpEvaluator;\n    }();\n\n    var ConstConditionInternal =\n    /** @class */\n    function () {\n      function ConstConditionInternal() {}\n\n      ConstConditionInternal.prototype.evaluate = function () {\n        return this.value;\n      };\n\n      return ConstConditionInternal;\n    }();\n\n    var AndConditionInternal =\n    /** @class */\n    function () {\n      function AndConditionInternal() {}\n\n      AndConditionInternal.prototype.evaluate = function () {\n        var children = this.children;\n\n        for (var i = 0; i < children.length; i++) {\n          if (!children[i].evaluate()) {\n            return false;\n          }\n        }\n\n        return true;\n      };\n\n      return AndConditionInternal;\n    }();\n\n    var OrConditionInternal =\n    /** @class */\n    function () {\n      function OrConditionInternal() {}\n\n      OrConditionInternal.prototype.evaluate = function () {\n        var children = this.children;\n\n        for (var i = 0; i < children.length; i++) {\n          if (children[i].evaluate()) {\n            return true;\n          }\n        }\n\n        return false;\n      };\n\n      return OrConditionInternal;\n    }();\n\n    var NotConditionInternal =\n    /** @class */\n    function () {\n      function NotConditionInternal() {}\n\n      NotConditionInternal.prototype.evaluate = function () {\n        return !this.child.evaluate();\n      };\n\n      return NotConditionInternal;\n    }();\n\n    var RelationalConditionInternal =\n    /** @class */\n    function () {\n      function RelationalConditionInternal() {}\n\n      RelationalConditionInternal.prototype.evaluate = function () {\n        var needParse = !!this.valueParser; // Call getValue with no `this`.\n\n        var getValue = this.getValue;\n        var tarValRaw = getValue(this.valueGetterParam);\n        var tarValParsed = needParse ? this.valueParser(tarValRaw) : null; // Relational cond follow \"and\" logic internally.\n\n        for (var i = 0; i < this.subCondList.length; i++) {\n          if (!this.subCondList[i].evaluate(needParse ? tarValParsed : tarValRaw)) {\n            return false;\n          }\n        }\n\n        return true;\n      };\n\n      return RelationalConditionInternal;\n    }();\n\n    function parseOption(exprOption, getters) {\n      if (exprOption === true || exprOption === false) {\n        var cond = new ConstConditionInternal();\n        cond.value = exprOption;\n        return cond;\n      }\n\n      var errMsg = '';\n\n      if (!isObjectNotArray(exprOption)) {\n        if (\"development\" !== 'production') {\n          errMsg = makePrintable('Illegal config. Expect a plain object but actually', exprOption);\n        }\n\n        throwError(errMsg);\n      }\n\n      if (exprOption.and) {\n        return parseAndOrOption('and', exprOption, getters);\n      } else if (exprOption.or) {\n        return parseAndOrOption('or', exprOption, getters);\n      } else if (exprOption.not) {\n        return parseNotOption(exprOption, getters);\n      }\n\n      return parseRelationalOption(exprOption, getters);\n    }\n\n    function parseAndOrOption(op, exprOption, getters) {\n      var subOptionArr = exprOption[op];\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        errMsg = makePrintable('\"and\"/\"or\" condition should only be `' + op + ': [...]` and must not be empty array.', 'Illegal condition:', exprOption);\n      }\n\n      if (!isArray(subOptionArr)) {\n        throwError(errMsg);\n      }\n\n      if (!subOptionArr.length) {\n        throwError(errMsg);\n      }\n\n      var cond = op === 'and' ? new AndConditionInternal() : new OrConditionInternal();\n      cond.children = map(subOptionArr, function (subOption) {\n        return parseOption(subOption, getters);\n      });\n\n      if (!cond.children.length) {\n        throwError(errMsg);\n      }\n\n      return cond;\n    }\n\n    function parseNotOption(exprOption, getters) {\n      var subOption = exprOption.not;\n      var errMsg = '';\n\n      if (\"development\" !== 'production') {\n        errMsg = makePrintable('\"not\" condition should only be `not: {}`.', 'Illegal condition:', exprOption);\n      }\n\n      if (!isObjectNotArray(subOption)) {\n        throwError(errMsg);\n      }\n\n      var cond = new NotConditionInternal();\n      cond.child = parseOption(subOption, getters);\n\n      if (!cond.child) {\n        throwError(errMsg);\n      }\n\n      return cond;\n    }\n\n    function parseRelationalOption(exprOption, getters) {\n      var errMsg = '';\n      var valueGetterParam = getters.prepareGetValue(exprOption);\n      var subCondList = [];\n      var exprKeys = keys(exprOption);\n      var parserName = exprOption.parser;\n      var valueParser = parserName ? getRawValueParser(parserName) : null;\n\n      for (var i = 0; i < exprKeys.length; i++) {\n        var keyRaw = exprKeys[i];\n\n        if (keyRaw === 'parser' || getters.valueGetterAttrMap.get(keyRaw)) {\n          continue;\n        }\n\n        var op = hasOwn(RELATIONAL_EXPRESSION_OP_ALIAS_MAP, keyRaw) ? RELATIONAL_EXPRESSION_OP_ALIAS_MAP[keyRaw] : keyRaw;\n        var condValueRaw = exprOption[keyRaw];\n        var condValueParsed = valueParser ? valueParser(condValueRaw) : condValueRaw;\n        var evaluator = createFilterComparator(op, condValueParsed) || op === 'reg' && new RegExpEvaluator(condValueParsed);\n\n        if (!evaluator) {\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('Illegal relational operation: \"' + keyRaw + '\" in condition:', exprOption);\n          }\n\n          throwError(errMsg);\n        }\n\n        subCondList.push(evaluator);\n      }\n\n      if (!subCondList.length) {\n        if (\"development\" !== 'production') {\n          errMsg = makePrintable('Relational condition must have at least one operator.', 'Illegal condition:', exprOption);\n        } // No relational operator always disabled in case of dangers result.\n\n\n        throwError(errMsg);\n      }\n\n      var cond = new RelationalConditionInternal();\n      cond.valueGetterParam = valueGetterParam;\n      cond.valueParser = valueParser;\n      cond.getValue = getters.getValue;\n      cond.subCondList = subCondList;\n      return cond;\n    }\n\n    function isObjectNotArray(val) {\n      return isObject(val) && !isArrayLike(val);\n    }\n\n    var ConditionalExpressionParsed =\n    /** @class */\n    function () {\n      function ConditionalExpressionParsed(exprOption, getters) {\n        this._cond = parseOption(exprOption, getters);\n      }\n\n      ConditionalExpressionParsed.prototype.evaluate = function () {\n        return this._cond.evaluate();\n      };\n\n      return ConditionalExpressionParsed;\n    }();\n    function parseConditionalExpression(exprOption, getters) {\n      return new ConditionalExpressionParsed(exprOption, getters);\n    }\n\n    var filterTransform = {\n      type: 'echarts:filter',\n      // PEDING: enhance to filter by index rather than create new data\n      transform: function (params) {\n        // [Caveat] Fail-Fast:\n        // Do not return the whole dataset unless user config indicate it explicitly.\n        // For example, if no condition specified by mistake, return an empty result\n        // is better than return the entire raw soruce for user to find the mistake.\n        var upstream = params.upstream;\n        var rawItem;\n        var condition = parseConditionalExpression(params.config, {\n          valueGetterAttrMap: createHashMap({\n            dimension: true\n          }),\n          prepareGetValue: function (exprOption) {\n            var errMsg = '';\n            var dimLoose = exprOption.dimension;\n\n            if (!hasOwn(exprOption, 'dimension')) {\n              if (\"development\" !== 'production') {\n                errMsg = makePrintable('Relation condition must has prop \"dimension\" specified.', 'Illegal condition:', exprOption);\n              }\n\n              throwError(errMsg);\n            }\n\n            var dimInfo = upstream.getDimensionInfo(dimLoose);\n\n            if (!dimInfo) {\n              if (\"development\" !== 'production') {\n                errMsg = makePrintable('Can not find dimension info via: ' + dimLoose + '.\\n', 'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\\n', 'Illegal condition:', exprOption, '.\\n');\n              }\n\n              throwError(errMsg);\n            }\n\n            return {\n              dimIdx: dimInfo.index\n            };\n          },\n          getValue: function (param) {\n            return upstream.retrieveValueFromItem(rawItem, param.dimIdx);\n          }\n        });\n        var resultData = [];\n\n        for (var i = 0, len = upstream.count(); i < len; i++) {\n          rawItem = upstream.getRawDataItem(i);\n\n          if (condition.evaluate()) {\n            resultData.push(rawItem);\n          }\n        }\n\n        return {\n          data: resultData\n        };\n      }\n    };\n\n    var sampleLog = '';\n\n    if (\"development\" !== 'production') {\n      sampleLog = ['Valid config is like:', '{ dimension: \"age\", order: \"asc\" }', 'or [{ dimension: \"age\", order: \"asc\"], { dimension: \"date\", order: \"desc\" }]'].join(' ');\n    }\n\n    var sortTransform = {\n      type: 'echarts:sort',\n      transform: function (params) {\n        var upstream = params.upstream;\n        var config = params.config;\n        var errMsg = ''; // Normalize\n        // const orderExprList: OrderExpression[] = isArray(config[0])\n        //     ? config as OrderExpression[]\n        //     : [config as OrderExpression];\n\n        var orderExprList = normalizeToArray(config);\n\n        if (!orderExprList.length) {\n          if (\"development\" !== 'production') {\n            errMsg = 'Empty `config` in sort transform.';\n          }\n\n          throwError(errMsg);\n        }\n\n        var orderDefList = [];\n        each(orderExprList, function (orderExpr) {\n          var dimLoose = orderExpr.dimension;\n          var order = orderExpr.order;\n          var parserName = orderExpr.parser;\n          var incomparable = orderExpr.incomparable;\n\n          if (dimLoose == null) {\n            if (\"development\" !== 'production') {\n              errMsg = 'Sort transform config must has \"dimension\" specified.' + sampleLog;\n            }\n\n            throwError(errMsg);\n          }\n\n          if (order !== 'asc' && order !== 'desc') {\n            if (\"development\" !== 'production') {\n              errMsg = 'Sort transform config must has \"order\" specified.' + sampleLog;\n            }\n\n            throwError(errMsg);\n          }\n\n          if (incomparable && incomparable !== 'min' && incomparable !== 'max') {\n            var errMsg_1 = '';\n\n            if (\"development\" !== 'production') {\n              errMsg_1 = 'incomparable must be \"min\" or \"max\" rather than \"' + incomparable + '\".';\n            }\n\n            throwError(errMsg_1);\n          }\n\n          if (order !== 'asc' && order !== 'desc') {\n            var errMsg_2 = '';\n\n            if (\"development\" !== 'production') {\n              errMsg_2 = 'order must be \"asc\" or \"desc\" rather than \"' + order + '\".';\n            }\n\n            throwError(errMsg_2);\n          }\n\n          var dimInfo = upstream.getDimensionInfo(dimLoose);\n\n          if (!dimInfo) {\n            if (\"development\" !== 'production') {\n              errMsg = makePrintable('Can not find dimension info via: ' + dimLoose + '.\\n', 'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\\n', 'Illegal config:', orderExpr, '.\\n');\n            }\n\n            throwError(errMsg);\n          }\n\n          var parser = parserName ? getRawValueParser(parserName) : null;\n\n          if (parserName && !parser) {\n            if (\"development\" !== 'production') {\n              errMsg = makePrintable('Invalid parser name ' + parserName + '.\\n', 'Illegal config:', orderExpr, '.\\n');\n            }\n\n            throwError(errMsg);\n          }\n\n          orderDefList.push({\n            dimIdx: dimInfo.index,\n            parser: parser,\n            comparator: new SortOrderComparator(order, incomparable)\n          });\n        }); // TODO: support it?\n\n        var sourceFormat = upstream.sourceFormat;\n\n        if (sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS) {\n          if (\"development\" !== 'production') {\n            errMsg = 'sourceFormat \"' + sourceFormat + '\" is not supported yet';\n          }\n\n          throwError(errMsg);\n        } // Other upstream format are all array.\n\n\n        var resultData = [];\n\n        for (var i = 0, len = upstream.count(); i < len; i++) {\n          resultData.push(upstream.getRawDataItem(i));\n        }\n\n        resultData.sort(function (item0, item1) {\n          for (var i = 0; i < orderDefList.length; i++) {\n            var orderDef = orderDefList[i];\n            var val0 = upstream.retrieveValueFromItem(item0, orderDef.dimIdx);\n            var val1 = upstream.retrieveValueFromItem(item1, orderDef.dimIdx);\n\n            if (orderDef.parser) {\n              val0 = orderDef.parser(val0);\n              val1 = orderDef.parser(val1);\n            }\n\n            var result = orderDef.comparator.evaluate(val0, val1);\n\n            if (result !== 0) {\n              return result;\n            }\n          }\n\n          return 0;\n        });\n        return {\n          data: resultData\n        };\n      }\n    };\n\n    function install$R(registers) {\n      registers.registerTransform(filterTransform);\n      registers.registerTransform(sortTransform);\n    }\n\n    var DatasetModel =\n    /** @class */\n    function (_super) {\n      __extends(DatasetModel, _super);\n\n      function DatasetModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataset';\n        return _this;\n      }\n\n      DatasetModel.prototype.init = function (option, parentModel, ecModel) {\n        _super.prototype.init.call(this, option, parentModel, ecModel);\n\n        this._sourceManager = new SourceManager(this);\n        disableTransformOptionMerge(this);\n      };\n\n      DatasetModel.prototype.mergeOption = function (newOption, ecModel) {\n        _super.prototype.mergeOption.call(this, newOption, ecModel);\n\n        disableTransformOptionMerge(this);\n      };\n\n      DatasetModel.prototype.optionUpdated = function () {\n        this._sourceManager.dirty();\n      };\n\n      DatasetModel.prototype.getSourceManager = function () {\n        return this._sourceManager;\n      };\n\n      DatasetModel.type = 'dataset';\n      DatasetModel.defaultOption = {\n        seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN\n      };\n      return DatasetModel;\n    }(ComponentModel);\n\n    var DatasetView =\n    /** @class */\n    function (_super) {\n      __extends(DatasetView, _super);\n\n      function DatasetView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataset';\n        return _this;\n      }\n\n      DatasetView.type = 'dataset';\n      return DatasetView;\n    }(ComponentView);\n\n    function install$S(registers) {\n      registers.registerComponentModel(DatasetModel);\n      registers.registerComponentView(DatasetView);\n    }\n\n    var CMD$4 = PathProxy.CMD;\n    function aroundEqual(a, b) {\n        return Math.abs(a - b) < 1e-5;\n    }\n    function pathToBezierCurves(path) {\n        var data = path.data;\n        var len = path.len();\n        var bezierArrayGroups = [];\n        var currentSubpath;\n        var xi = 0;\n        var yi = 0;\n        var x0 = 0;\n        var y0 = 0;\n        function createNewSubpath(x, y) {\n            if (currentSubpath && currentSubpath.length > 2) {\n                bezierArrayGroups.push(currentSubpath);\n            }\n            currentSubpath = [x, y];\n        }\n        function addLine(x0, y0, x1, y1) {\n            if (!(aroundEqual(x0, x1) && aroundEqual(y0, y1))) {\n                currentSubpath.push(x0, y0, x1, y1, x1, y1);\n            }\n        }\n        function addArc(startAngle, endAngle, cx, cy, rx, ry) {\n            var delta = Math.abs(endAngle - startAngle);\n            var len = Math.tan(delta / 4) * 4 / 3;\n            var dir = endAngle < startAngle ? -1 : 1;\n            var c1 = Math.cos(startAngle);\n            var s1 = Math.sin(startAngle);\n            var c2 = Math.cos(endAngle);\n            var s2 = Math.sin(endAngle);\n            var x1 = c1 * rx + cx;\n            var y1 = s1 * ry + cy;\n            var x4 = c2 * rx + cx;\n            var y4 = s2 * ry + cy;\n            var hx = rx * len * dir;\n            var hy = ry * len * dir;\n            currentSubpath.push(x1 - hx * s1, y1 + hy * c1, x4 + hx * s2, y4 - hy * c2, x4, y4);\n        }\n        var x1;\n        var y1;\n        var x2;\n        var y2;\n        for (var i = 0; i < len;) {\n            var cmd = data[i++];\n            var isFirst = i === 1;\n            if (isFirst) {\n                xi = data[i];\n                yi = data[i + 1];\n                x0 = xi;\n                y0 = yi;\n                if (cmd === CMD$4.L || cmd === CMD$4.C || cmd === CMD$4.Q) {\n                    currentSubpath = [x0, y0];\n                }\n            }\n            switch (cmd) {\n                case CMD$4.M:\n                    xi = x0 = data[i++];\n                    yi = y0 = data[i++];\n                    createNewSubpath(x0, y0);\n                    break;\n                case CMD$4.L:\n                    x1 = data[i++];\n                    y1 = data[i++];\n                    addLine(xi, yi, x1, y1);\n                    xi = x1;\n                    yi = y1;\n                    break;\n                case CMD$4.C:\n                    currentSubpath.push(data[i++], data[i++], data[i++], data[i++], xi = data[i++], yi = data[i++]);\n                    break;\n                case CMD$4.Q:\n                    x1 = data[i++];\n                    y1 = data[i++];\n                    x2 = data[i++];\n                    y2 = data[i++];\n                    currentSubpath.push(xi + 2 / 3 * (x1 - xi), yi + 2 / 3 * (y1 - yi), x2 + 2 / 3 * (x1 - x2), y2 + 2 / 3 * (y1 - y2), x2, y2);\n                    xi = x2;\n                    yi = y2;\n                    break;\n                case CMD$4.A:\n                    var cx = data[i++];\n                    var cy = data[i++];\n                    var rx = data[i++];\n                    var ry = data[i++];\n                    var startAngle = data[i++];\n                    var endAngle = data[i++] + startAngle;\n                    i += 1;\n                    var anticlockwise = !data[i++];\n                    x1 = Math.cos(startAngle) * rx + cx;\n                    y1 = Math.sin(startAngle) * ry + cy;\n                    if (isFirst) {\n                        x0 = x1;\n                        y0 = y1;\n                        createNewSubpath(x0, y0);\n                    }\n                    else {\n                        addLine(xi, yi, x1, y1);\n                    }\n                    xi = Math.cos(endAngle) * rx + cx;\n                    yi = Math.sin(endAngle) * ry + cy;\n                    var step = (anticlockwise ? -1 : 1) * Math.PI / 2;\n                    for (var angle = startAngle; anticlockwise ? angle > endAngle : angle < endAngle; angle += step) {\n                        var nextAngle = anticlockwise ? Math.max(angle + step, endAngle)\n                            : Math.min(angle + step, endAngle);\n                        addArc(angle, nextAngle, cx, cy, rx, ry);\n                    }\n                    break;\n                case CMD$4.R:\n                    x0 = xi = data[i++];\n                    y0 = yi = data[i++];\n                    x1 = x0 + data[i++];\n                    y1 = y0 + data[i++];\n                    createNewSubpath(x1, y0);\n                    addLine(x1, y0, x1, y1);\n                    addLine(x1, y1, x0, y1);\n                    addLine(x0, y1, x0, y0);\n                    addLine(x0, y0, x1, y0);\n                    break;\n                case CMD$4.Z:\n                    currentSubpath && addLine(xi, yi, x0, y0);\n                    xi = x0;\n                    yi = y0;\n                    break;\n            }\n        }\n        if (currentSubpath && currentSubpath.length > 2) {\n            bezierArrayGroups.push(currentSubpath);\n        }\n        return bezierArrayGroups;\n    }\n    function adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, out, scale) {\n        if (aroundEqual(x0, x1) && aroundEqual(y0, y1) && aroundEqual(x2, x3) && aroundEqual(y2, y3)) {\n            out.push(x3, y3);\n            return;\n        }\n        var PIXEL_DISTANCE = 2 / scale;\n        var PIXEL_DISTANCE_SQR = PIXEL_DISTANCE * PIXEL_DISTANCE;\n        var dx = x3 - x0;\n        var dy = y3 - y0;\n        var d = Math.sqrt(dx * dx + dy * dy);\n        dx /= d;\n        dy /= d;\n        var dx1 = x1 - x0;\n        var dy1 = y1 - y0;\n        var dx2 = x2 - x3;\n        var dy2 = y2 - y3;\n        var cp1LenSqr = dx1 * dx1 + dy1 * dy1;\n        var cp2LenSqr = dx2 * dx2 + dy2 * dy2;\n        if (cp1LenSqr < PIXEL_DISTANCE_SQR && cp2LenSqr < PIXEL_DISTANCE_SQR) {\n            out.push(x3, y3);\n            return;\n        }\n        var projLen1 = dx * dx1 + dy * dy1;\n        var projLen2 = -dx * dx2 - dy * dy2;\n        var d1Sqr = cp1LenSqr - projLen1 * projLen1;\n        var d2Sqr = cp2LenSqr - projLen2 * projLen2;\n        if (d1Sqr < PIXEL_DISTANCE_SQR && projLen1 >= 0\n            && d2Sqr < PIXEL_DISTANCE_SQR && projLen2 >= 0) {\n            out.push(x3, y3);\n            return;\n        }\n        var tmpSegX = [];\n        var tmpSegY = [];\n        cubicSubdivide(x0, x1, x2, x3, 0.5, tmpSegX);\n        cubicSubdivide(y0, y1, y2, y3, 0.5, tmpSegY);\n        adpativeBezier(tmpSegX[0], tmpSegY[0], tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], tmpSegX[3], tmpSegY[3], out, scale);\n        adpativeBezier(tmpSegX[4], tmpSegY[4], tmpSegX[5], tmpSegY[5], tmpSegX[6], tmpSegY[6], tmpSegX[7], tmpSegY[7], out, scale);\n    }\n    function pathToPolygons(path, scale) {\n        var bezierArrayGroups = pathToBezierCurves(path);\n        var polygons = [];\n        scale = scale || 1;\n        for (var i = 0; i < bezierArrayGroups.length; i++) {\n            var beziers = bezierArrayGroups[i];\n            var polygon = [];\n            var x0 = beziers[0];\n            var y0 = beziers[1];\n            polygon.push(x0, y0);\n            for (var k = 2; k < beziers.length;) {\n                var x1 = beziers[k++];\n                var y1 = beziers[k++];\n                var x2 = beziers[k++];\n                var y2 = beziers[k++];\n                var x3 = beziers[k++];\n                var y3 = beziers[k++];\n                adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, polygon, scale);\n                x0 = x3;\n                y0 = y3;\n            }\n            polygons.push(polygon);\n        }\n        return polygons;\n    }\n\n    function getDividingGrids(dimSize, rowDim, count) {\n        var rowSize = dimSize[rowDim];\n        var columnSize = dimSize[1 - rowDim];\n        var ratio = Math.abs(rowSize / columnSize);\n        var rowCount = Math.ceil(Math.sqrt(ratio * count));\n        var columnCount = Math.floor(count / rowCount);\n        if (columnCount === 0) {\n            columnCount = 1;\n            rowCount = count;\n        }\n        var grids = [];\n        for (var i = 0; i < rowCount; i++) {\n            grids.push(columnCount);\n        }\n        var currentCount = rowCount * columnCount;\n        var remained = count - currentCount;\n        if (remained > 0) {\n            for (var i = 0; i < remained; i++) {\n                grids[i % rowCount] += 1;\n            }\n        }\n        return grids;\n    }\n    function divideSector(sectorShape, count, outShapes) {\n        var r0 = sectorShape.r0;\n        var r = sectorShape.r;\n        var startAngle = sectorShape.startAngle;\n        var endAngle = sectorShape.endAngle;\n        var angle = Math.abs(endAngle - startAngle);\n        var arcLen = angle * r;\n        var deltaR = r - r0;\n        var isAngleRow = arcLen > Math.abs(deltaR);\n        var grids = getDividingGrids([arcLen, deltaR], isAngleRow ? 0 : 1, count);\n        var rowSize = (isAngleRow ? angle : deltaR) / grids.length;\n        for (var row = 0; row < grids.length; row++) {\n            var columnSize = (isAngleRow ? deltaR : angle) / grids[row];\n            for (var column = 0; column < grids[row]; column++) {\n                var newShape = {};\n                if (isAngleRow) {\n                    newShape.startAngle = startAngle + rowSize * row;\n                    newShape.endAngle = startAngle + rowSize * (row + 1);\n                    newShape.r0 = r0 + columnSize * column;\n                    newShape.r = r0 + columnSize * (column + 1);\n                }\n                else {\n                    newShape.startAngle = startAngle + columnSize * column;\n                    newShape.endAngle = startAngle + columnSize * (column + 1);\n                    newShape.r0 = r0 + rowSize * row;\n                    newShape.r = r0 + rowSize * (row + 1);\n                }\n                newShape.clockwise = sectorShape.clockwise;\n                newShape.cx = sectorShape.cx;\n                newShape.cy = sectorShape.cy;\n                outShapes.push(newShape);\n            }\n        }\n    }\n    function divideRect(rectShape, count, outShapes) {\n        var width = rectShape.width;\n        var height = rectShape.height;\n        var isHorizontalRow = width > height;\n        var grids = getDividingGrids([width, height], isHorizontalRow ? 0 : 1, count);\n        var rowSizeDim = isHorizontalRow ? 'width' : 'height';\n        var columnSizeDim = isHorizontalRow ? 'height' : 'width';\n        var rowDim = isHorizontalRow ? 'x' : 'y';\n        var columnDim = isHorizontalRow ? 'y' : 'x';\n        var rowSize = rectShape[rowSizeDim] / grids.length;\n        for (var row = 0; row < grids.length; row++) {\n            var columnSize = rectShape[columnSizeDim] / grids[row];\n            for (var column = 0; column < grids[row]; column++) {\n                var newShape = {};\n                newShape[rowDim] = row * rowSize;\n                newShape[columnDim] = column * columnSize;\n                newShape[rowSizeDim] = rowSize;\n                newShape[columnSizeDim] = columnSize;\n                newShape.x += rectShape.x;\n                newShape.y += rectShape.y;\n                outShapes.push(newShape);\n            }\n        }\n    }\n    function crossProduct2d$1(x1, y1, x2, y2) {\n        return x1 * y2 - x2 * y1;\n    }\n    function lineLineIntersect$1(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {\n        var mx = a2x - a1x;\n        var my = a2y - a1y;\n        var nx = b2x - b1x;\n        var ny = b2y - b1y;\n        var nmCrossProduct = crossProduct2d$1(nx, ny, mx, my);\n        if (Math.abs(nmCrossProduct) < 1e-6) {\n            return null;\n        }\n        var b1a1x = a1x - b1x;\n        var b1a1y = a1y - b1y;\n        var p = crossProduct2d$1(b1a1x, b1a1y, nx, ny) / nmCrossProduct;\n        if (p < 0 || p > 1) {\n            return null;\n        }\n        return new Point(p * mx + a1x, p * my + a1y);\n    }\n    function projPtOnLine(pt, lineA, lineB) {\n        var dir = new Point();\n        Point.sub(dir, lineB, lineA);\n        dir.normalize();\n        var dir2 = new Point();\n        Point.sub(dir2, pt, lineA);\n        var len = dir2.dot(dir);\n        return len;\n    }\n    function addToPoly(poly, pt) {\n        var last = poly[poly.length - 1];\n        if (last && last[0] === pt[0] && last[1] === pt[1]) {\n            return;\n        }\n        poly.push(pt);\n    }\n    function splitPolygonByLine(points, lineA, lineB) {\n        var len = points.length;\n        var intersections = [];\n        for (var i = 0; i < len; i++) {\n            var p0 = points[i];\n            var p1 = points[(i + 1) % len];\n            var intersectionPt = lineLineIntersect$1(p0[0], p0[1], p1[0], p1[1], lineA.x, lineA.y, lineB.x, lineB.y);\n            if (intersectionPt) {\n                intersections.push({\n                    projPt: projPtOnLine(intersectionPt, lineA, lineB),\n                    pt: intersectionPt,\n                    idx: i\n                });\n            }\n        }\n        if (intersections.length < 2) {\n            return [{ points: points }, { points: points }];\n        }\n        intersections.sort(function (a, b) {\n            return a.projPt - b.projPt;\n        });\n        var splitPt0 = intersections[0];\n        var splitPt1 = intersections[intersections.length - 1];\n        if (splitPt1.idx < splitPt0.idx) {\n            var tmp = splitPt0;\n            splitPt0 = splitPt1;\n            splitPt1 = tmp;\n        }\n        var splitPt0Arr = [splitPt0.pt.x, splitPt0.pt.y];\n        var splitPt1Arr = [splitPt1.pt.x, splitPt1.pt.y];\n        var newPolyA = [splitPt0Arr];\n        var newPolyB = [splitPt1Arr];\n        for (var i = splitPt0.idx + 1; i <= splitPt1.idx; i++) {\n            addToPoly(newPolyA, points[i].slice());\n        }\n        addToPoly(newPolyA, splitPt1Arr);\n        addToPoly(newPolyA, splitPt0Arr);\n        for (var i = splitPt1.idx + 1; i <= splitPt0.idx + len; i++) {\n            addToPoly(newPolyB, points[i % len].slice());\n        }\n        addToPoly(newPolyB, splitPt0Arr);\n        addToPoly(newPolyB, splitPt1Arr);\n        return [{\n                points: newPolyA\n            }, {\n                points: newPolyB\n            }];\n    }\n    function binaryDividePolygon(polygonShape) {\n        var points = polygonShape.points;\n        var min = [];\n        var max = [];\n        fromPoints(points, min, max);\n        var boundingRect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n        var width = boundingRect.width;\n        var height = boundingRect.height;\n        var x = boundingRect.x;\n        var y = boundingRect.y;\n        var pt0 = new Point();\n        var pt1 = new Point();\n        if (width > height) {\n            pt0.x = pt1.x = x + width / 2;\n            pt0.y = y;\n            pt1.y = y + height;\n        }\n        else {\n            pt0.y = pt1.y = y + height / 2;\n            pt0.x = x;\n            pt1.x = x + width;\n        }\n        return splitPolygonByLine(points, pt0, pt1);\n    }\n    function binaryDivideRecursive(divider, shape, count, out) {\n        if (count === 1) {\n            out.push(shape);\n        }\n        else {\n            var mid = Math.floor(count / 2);\n            var sub = divider(shape);\n            binaryDivideRecursive(divider, sub[0], mid, out);\n            binaryDivideRecursive(divider, sub[1], count - mid, out);\n        }\n        return out;\n    }\n    function clone$4(path, count) {\n        var paths = [];\n        for (var i = 0; i < count; i++) {\n            paths.push(clonePath(path));\n        }\n        return paths;\n    }\n    function copyPathProps(source, target) {\n        target.setStyle(source.style);\n        target.z = source.z;\n        target.z2 = source.z2;\n        target.zlevel = source.zlevel;\n    }\n    function polygonConvert(points) {\n        var out = [];\n        for (var i = 0; i < points.length;) {\n            out.push([points[i++], points[i++]]);\n        }\n        return out;\n    }\n    function split(path, count) {\n        var outShapes = [];\n        var shape = path.shape;\n        var OutShapeCtor;\n        switch (path.type) {\n            case 'rect':\n                divideRect(shape, count, outShapes);\n                OutShapeCtor = Rect;\n                break;\n            case 'sector':\n                divideSector(shape, count, outShapes);\n                OutShapeCtor = Sector;\n                break;\n            case 'circle':\n                divideSector({\n                    r0: 0, r: shape.r, startAngle: 0, endAngle: Math.PI * 2,\n                    cx: shape.cx, cy: shape.cy\n                }, count, outShapes);\n                OutShapeCtor = Sector;\n                break;\n            default:\n                var m = path.getComputedTransform();\n                var scale = m ? Math.sqrt(Math.max(m[0] * m[0] + m[1] * m[1], m[2] * m[2] + m[3] * m[3])) : 1;\n                var polygons = map(pathToPolygons(path.getUpdatedPathProxy(), scale), function (poly) { return polygonConvert(poly); });\n                var polygonCount = polygons.length;\n                if (polygonCount === 0) {\n                    binaryDivideRecursive(binaryDividePolygon, {\n                        points: polygons[0]\n                    }, count, outShapes);\n                }\n                else if (polygonCount === count) {\n                    for (var i = 0; i < polygonCount; i++) {\n                        outShapes.push({\n                            points: polygons[i]\n                        });\n                    }\n                }\n                else {\n                    var totalArea_1 = 0;\n                    var items = map(polygons, function (poly) {\n                        var min = [];\n                        var max = [];\n                        fromPoints(poly, min, max);\n                        var area = (max[1] - min[1]) * (max[0] - min[0]);\n                        totalArea_1 += area;\n                        return { poly: poly, area: area };\n                    });\n                    items.sort(function (a, b) { return b.area - a.area; });\n                    var left = count;\n                    for (var i = 0; i < polygonCount; i++) {\n                        var item = items[i];\n                        if (left <= 0) {\n                            break;\n                        }\n                        var selfCount = i === polygonCount - 1\n                            ? left\n                            : Math.ceil(item.area / totalArea_1 * count);\n                        if (selfCount < 0) {\n                            continue;\n                        }\n                        binaryDivideRecursive(binaryDividePolygon, {\n                            points: item.poly\n                        }, selfCount, outShapes);\n                        left -= selfCount;\n                    }\n                }\n                OutShapeCtor = Polygon;\n                break;\n        }\n        if (!OutShapeCtor) {\n            return clone$4(path, count);\n        }\n        var out = [];\n        for (var i = 0; i < outShapes.length; i++) {\n            var subPath = new OutShapeCtor();\n            subPath.setShape(outShapes[i]);\n            copyPathProps(path, subPath);\n            out.push(subPath);\n        }\n        return out;\n    }\n\n    function alignSubpath(subpath1, subpath2) {\n        var len1 = subpath1.length;\n        var len2 = subpath2.length;\n        if (len1 === len2) {\n            return [subpath1, subpath2];\n        }\n        var tmpSegX = [];\n        var tmpSegY = [];\n        var shorterPath = len1 < len2 ? subpath1 : subpath2;\n        var shorterLen = Math.min(len1, len2);\n        var diff = Math.abs(len2 - len1) / 6;\n        var shorterBezierCount = (shorterLen - 2) / 6;\n        var eachCurveSubDivCount = Math.ceil(diff / shorterBezierCount) + 1;\n        var newSubpath = [shorterPath[0], shorterPath[1]];\n        var remained = diff;\n        for (var i = 2; i < shorterLen;) {\n            var x0 = shorterPath[i - 2];\n            var y0 = shorterPath[i - 1];\n            var x1 = shorterPath[i++];\n            var y1 = shorterPath[i++];\n            var x2 = shorterPath[i++];\n            var y2 = shorterPath[i++];\n            var x3 = shorterPath[i++];\n            var y3 = shorterPath[i++];\n            if (remained <= 0) {\n                newSubpath.push(x1, y1, x2, y2, x3, y3);\n                continue;\n            }\n            var actualSubDivCount = Math.min(remained, eachCurveSubDivCount - 1) + 1;\n            for (var k = 1; k <= actualSubDivCount; k++) {\n                var p = k / actualSubDivCount;\n                cubicSubdivide(x0, x1, x2, x3, p, tmpSegX);\n                cubicSubdivide(y0, y1, y2, y3, p, tmpSegY);\n                x0 = tmpSegX[3];\n                y0 = tmpSegY[3];\n                newSubpath.push(tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], x0, y0);\n                x1 = tmpSegX[5];\n                y1 = tmpSegY[5];\n                x2 = tmpSegX[6];\n                y2 = tmpSegY[6];\n            }\n            remained -= actualSubDivCount - 1;\n        }\n        return shorterPath === subpath1 ? [newSubpath, subpath2] : [subpath1, newSubpath];\n    }\n    function createSubpath(lastSubpathSubpath, otherSubpath) {\n        var len = lastSubpathSubpath.length;\n        var lastX = lastSubpathSubpath[len - 2];\n        var lastY = lastSubpathSubpath[len - 1];\n        var newSubpath = [];\n        for (var i = 0; i < otherSubpath.length;) {\n            newSubpath[i++] = lastX;\n            newSubpath[i++] = lastY;\n        }\n        return newSubpath;\n    }\n    function alignBezierCurves(array1, array2) {\n        var _a;\n        var lastSubpath1;\n        var lastSubpath2;\n        var newArray1 = [];\n        var newArray2 = [];\n        for (var i = 0; i < Math.max(array1.length, array2.length); i++) {\n            var subpath1 = array1[i];\n            var subpath2 = array2[i];\n            var newSubpath1 = void 0;\n            var newSubpath2 = void 0;\n            if (!subpath1) {\n                newSubpath1 = createSubpath(lastSubpath1 || subpath2, subpath2);\n                newSubpath2 = subpath2;\n            }\n            else if (!subpath2) {\n                newSubpath2 = createSubpath(lastSubpath2 || subpath1, subpath1);\n                newSubpath1 = subpath1;\n            }\n            else {\n                _a = alignSubpath(subpath1, subpath2), newSubpath1 = _a[0], newSubpath2 = _a[1];\n                lastSubpath1 = newSubpath1;\n                lastSubpath2 = newSubpath2;\n            }\n            newArray1.push(newSubpath1);\n            newArray2.push(newSubpath2);\n        }\n        return [newArray1, newArray2];\n    }\n    function centroid$1(array) {\n        var signedArea = 0;\n        var cx = 0;\n        var cy = 0;\n        var len = array.length;\n        for (var i = 0, j = len - 2; i < len; j = i, i += 2) {\n            var x0 = array[j];\n            var y0 = array[j + 1];\n            var x1 = array[i];\n            var y1 = array[i + 1];\n            var a = x0 * y1 - x1 * y0;\n            signedArea += a;\n            cx += (x0 + x1) * a;\n            cy += (y0 + y1) * a;\n        }\n        if (signedArea === 0) {\n            return [array[0] || 0, array[1] || 0];\n        }\n        return [cx / signedArea / 3, cy / signedArea / 3, signedArea];\n    }\n    function findBestRingOffset(fromSubBeziers, toSubBeziers, fromCp, toCp) {\n        var bezierCount = (fromSubBeziers.length - 2) / 6;\n        var bestScore = Infinity;\n        var bestOffset = 0;\n        var len = fromSubBeziers.length;\n        var len2 = len - 2;\n        for (var offset = 0; offset < bezierCount; offset++) {\n            var cursorOffset = offset * 6;\n            var score = 0;\n            for (var k = 0; k < len; k += 2) {\n                var idx = k === 0 ? cursorOffset : ((cursorOffset + k - 2) % len2 + 2);\n                var x0 = fromSubBeziers[idx] - fromCp[0];\n                var y0 = fromSubBeziers[idx + 1] - fromCp[1];\n                var x1 = toSubBeziers[k] - toCp[0];\n                var y1 = toSubBeziers[k + 1] - toCp[1];\n                var dx = x1 - x0;\n                var dy = y1 - y0;\n                score += dx * dx + dy * dy;\n            }\n            if (score < bestScore) {\n                bestScore = score;\n                bestOffset = offset;\n            }\n        }\n        return bestOffset;\n    }\n    function reverse(array) {\n        var newArr = [];\n        var len = array.length;\n        for (var i = 0; i < len; i += 2) {\n            newArr[i] = array[len - i - 2];\n            newArr[i + 1] = array[len - i - 1];\n        }\n        return newArr;\n    }\n    function findBestMorphingRotation(fromArr, toArr, searchAngleIteration, searchAngleRange) {\n        var result = [];\n        var fromNeedsReverse;\n        for (var i = 0; i < fromArr.length; i++) {\n            var fromSubpathBezier = fromArr[i];\n            var toSubpathBezier = toArr[i];\n            var fromCp = centroid$1(fromSubpathBezier);\n            var toCp = centroid$1(toSubpathBezier);\n            if (fromNeedsReverse == null) {\n                fromNeedsReverse = fromCp[2] < 0 !== toCp[2] < 0;\n            }\n            var newFromSubpathBezier = [];\n            var newToSubpathBezier = [];\n            var bestAngle = 0;\n            var bestScore = Infinity;\n            var tmpArr = [];\n            var len = fromSubpathBezier.length;\n            if (fromNeedsReverse) {\n                fromSubpathBezier = reverse(fromSubpathBezier);\n            }\n            var offset = findBestRingOffset(fromSubpathBezier, toSubpathBezier, fromCp, toCp) * 6;\n            var len2 = len - 2;\n            for (var k = 0; k < len2; k += 2) {\n                var idx = (offset + k) % len2 + 2;\n                newFromSubpathBezier[k + 2] = fromSubpathBezier[idx] - fromCp[0];\n                newFromSubpathBezier[k + 3] = fromSubpathBezier[idx + 1] - fromCp[1];\n            }\n            newFromSubpathBezier[0] = fromSubpathBezier[offset] - fromCp[0];\n            newFromSubpathBezier[1] = fromSubpathBezier[offset + 1] - fromCp[1];\n            if (searchAngleIteration > 0) {\n                var step = searchAngleRange / searchAngleIteration;\n                for (var angle = -searchAngleRange / 2; angle <= searchAngleRange / 2; angle += step) {\n                    var sa = Math.sin(angle);\n                    var ca = Math.cos(angle);\n                    var score = 0;\n                    for (var k = 0; k < fromSubpathBezier.length; k += 2) {\n                        var x0 = newFromSubpathBezier[k];\n                        var y0 = newFromSubpathBezier[k + 1];\n                        var x1 = toSubpathBezier[k] - toCp[0];\n                        var y1 = toSubpathBezier[k + 1] - toCp[1];\n                        var newX1 = x1 * ca - y1 * sa;\n                        var newY1 = x1 * sa + y1 * ca;\n                        tmpArr[k] = newX1;\n                        tmpArr[k + 1] = newY1;\n                        var dx = newX1 - x0;\n                        var dy = newY1 - y0;\n                        score += dx * dx + dy * dy;\n                    }\n                    if (score < bestScore) {\n                        bestScore = score;\n                        bestAngle = angle;\n                        for (var m = 0; m < tmpArr.length; m++) {\n                            newToSubpathBezier[m] = tmpArr[m];\n                        }\n                    }\n                }\n            }\n            else {\n                for (var i_1 = 0; i_1 < len; i_1 += 2) {\n                    newToSubpathBezier[i_1] = toSubpathBezier[i_1] - toCp[0];\n                    newToSubpathBezier[i_1 + 1] = toSubpathBezier[i_1 + 1] - toCp[1];\n                }\n            }\n            result.push({\n                from: newFromSubpathBezier,\n                to: newToSubpathBezier,\n                fromCp: fromCp,\n                toCp: toCp,\n                rotation: -bestAngle\n            });\n        }\n        return result;\n    }\n    function isCombineMorphing(path) {\n        return path.__isCombineMorphing;\n    }\n    var SAVED_METHOD_PREFIX = '__mOriginal_';\n    function saveAndModifyMethod(obj, methodName, modifiers) {\n        var savedMethodName = SAVED_METHOD_PREFIX + methodName;\n        var originalMethod = obj[savedMethodName] || obj[methodName];\n        if (!obj[savedMethodName]) {\n            obj[savedMethodName] = obj[methodName];\n        }\n        var replace = modifiers.replace;\n        var after = modifiers.after;\n        var before = modifiers.before;\n        obj[methodName] = function () {\n            var args = arguments;\n            var res;\n            before && before.apply(this, args);\n            if (replace) {\n                res = replace.apply(this, args);\n            }\n            else {\n                res = originalMethod.apply(this, args);\n            }\n            after && after.apply(this, args);\n            return res;\n        };\n    }\n    function restoreMethod(obj, methodName) {\n        var savedMethodName = SAVED_METHOD_PREFIX + methodName;\n        if (obj[savedMethodName]) {\n            obj[methodName] = obj[savedMethodName];\n            obj[savedMethodName] = null;\n        }\n    }\n    function applyTransformOnBeziers(bezierCurves, mm) {\n        for (var i = 0; i < bezierCurves.length; i++) {\n            var subBeziers = bezierCurves[i];\n            for (var k = 0; k < subBeziers.length;) {\n                var x = subBeziers[k];\n                var y = subBeziers[k + 1];\n                subBeziers[k++] = mm[0] * x + mm[2] * y + mm[4];\n                subBeziers[k++] = mm[1] * x + mm[3] * y + mm[5];\n            }\n        }\n    }\n    function prepareMorphPath(fromPath, toPath) {\n        var fromPathProxy = fromPath.getUpdatedPathProxy();\n        var toPathProxy = toPath.getUpdatedPathProxy();\n        var _a = alignBezierCurves(pathToBezierCurves(fromPathProxy), pathToBezierCurves(toPathProxy)), fromBezierCurves = _a[0], toBezierCurves = _a[1];\n        var fromPathTransform = fromPath.getComputedTransform();\n        var toPathTransform = toPath.getComputedTransform();\n        function updateIdentityTransform() {\n            this.transform = null;\n        }\n        fromPathTransform && applyTransformOnBeziers(fromBezierCurves, fromPathTransform);\n        toPathTransform && applyTransformOnBeziers(toBezierCurves, toPathTransform);\n        saveAndModifyMethod(toPath, 'updateTransform', { replace: updateIdentityTransform });\n        toPath.transform = null;\n        var morphingData = findBestMorphingRotation(fromBezierCurves, toBezierCurves, 10, Math.PI);\n        var tmpArr = [];\n        saveAndModifyMethod(toPath, 'buildPath', { replace: function (path) {\n                var t = toPath.__morphT;\n                var onet = 1 - t;\n                var newCp = [];\n                for (var i = 0; i < morphingData.length; i++) {\n                    var item = morphingData[i];\n                    var from = item.from;\n                    var to = item.to;\n                    var angle = item.rotation * t;\n                    var fromCp = item.fromCp;\n                    var toCp = item.toCp;\n                    var sa = Math.sin(angle);\n                    var ca = Math.cos(angle);\n                    lerp(newCp, fromCp, toCp, t);\n                    for (var m = 0; m < from.length; m += 2) {\n                        var x0_1 = from[m];\n                        var y0_1 = from[m + 1];\n                        var x1 = to[m];\n                        var y1 = to[m + 1];\n                        var x = x0_1 * onet + x1 * t;\n                        var y = y0_1 * onet + y1 * t;\n                        tmpArr[m] = (x * ca - y * sa) + newCp[0];\n                        tmpArr[m + 1] = (x * sa + y * ca) + newCp[1];\n                    }\n                    var x0 = tmpArr[0];\n                    var y0 = tmpArr[1];\n                    path.moveTo(x0, y0);\n                    for (var m = 2; m < from.length;) {\n                        var x1 = tmpArr[m++];\n                        var y1 = tmpArr[m++];\n                        var x2 = tmpArr[m++];\n                        var y2 = tmpArr[m++];\n                        var x3 = tmpArr[m++];\n                        var y3 = tmpArr[m++];\n                        if (x0 === x1 && y0 === y1 && x2 === x3 && y2 === y3) {\n                            path.lineTo(x3, y3);\n                        }\n                        else {\n                            path.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n                        }\n                        x0 = x3;\n                        y0 = y3;\n                    }\n                }\n            } });\n    }\n    function morphPath(fromPath, toPath, animationOpts) {\n        if (!fromPath || !toPath) {\n            return toPath;\n        }\n        var oldDone = animationOpts.done;\n        var oldDuring = animationOpts.during;\n        prepareMorphPath(fromPath, toPath);\n        toPath.__morphT = 0;\n        function restoreToPath() {\n            restoreMethod(toPath, 'buildPath');\n            restoreMethod(toPath, 'updateTransform');\n            toPath.__morphT = -1;\n            toPath.createPathProxy();\n            toPath.dirtyShape();\n        }\n        toPath.animateTo({\n            __morphT: 1\n        }, defaults({\n            during: function (p) {\n                toPath.dirtyShape();\n                oldDuring && oldDuring(p);\n            },\n            done: function () {\n                restoreToPath();\n                oldDone && oldDone();\n            }\n        }, animationOpts));\n        return toPath;\n    }\n    function hilbert(x, y, minX, minY, maxX, maxY) {\n        var bits = 16;\n        x = (maxX === minX) ? 0 : Math.round(32767 * (x - minX) / (maxX - minX));\n        y = (maxY === minY) ? 0 : Math.round(32767 * (y - minY) / (maxY - minY));\n        var d = 0;\n        var tmp;\n        for (var s = (1 << bits) / 2; s > 0; s /= 2) {\n            var rx = 0;\n            var ry = 0;\n            if ((x & s) > 0) {\n                rx = 1;\n            }\n            if ((y & s) > 0) {\n                ry = 1;\n            }\n            d += s * s * ((3 * rx) ^ ry);\n            if (ry === 0) {\n                if (rx === 1) {\n                    x = s - 1 - x;\n                    y = s - 1 - y;\n                }\n                tmp = x;\n                x = y;\n                y = tmp;\n            }\n        }\n        return d;\n    }\n    function sortPaths(pathList) {\n        var xMin = Infinity;\n        var yMin = Infinity;\n        var xMax = -Infinity;\n        var yMax = -Infinity;\n        var cps = map(pathList, function (path) {\n            var rect = path.getBoundingRect();\n            var m = path.getComputedTransform();\n            var x = rect.x + rect.width / 2 + (m ? m[4] : 0);\n            var y = rect.y + rect.height / 2 + (m ? m[5] : 0);\n            xMin = Math.min(x, xMin);\n            yMin = Math.min(y, yMin);\n            xMax = Math.max(x, xMax);\n            yMax = Math.max(y, yMax);\n            return [x, y];\n        });\n        var items = map(cps, function (cp, idx) {\n            return {\n                cp: cp,\n                z: hilbert(cp[0], cp[1], xMin, yMin, xMax, yMax),\n                path: pathList[idx]\n            };\n        });\n        return items.sort(function (a, b) { return a.z - b.z; }).map(function (item) { return item.path; });\n    }\n    function defaultDividePath(param) {\n        return split(param.path, param.count);\n    }\n    function createEmptyReturn() {\n        return {\n            fromIndividuals: [],\n            toIndividuals: [],\n            count: 0\n        };\n    }\n    function combineMorph(fromList, toPath, animationOpts) {\n        var fromPathList = [];\n        function addFromPath(fromList) {\n            for (var i = 0; i < fromList.length; i++) {\n                var from = fromList[i];\n                if (isCombineMorphing(from)) {\n                    addFromPath(from.childrenRef());\n                }\n                else if (from instanceof Path) {\n                    fromPathList.push(from);\n                }\n            }\n        }\n        addFromPath(fromList);\n        var separateCount = fromPathList.length;\n        if (!separateCount) {\n            return createEmptyReturn();\n        }\n        var dividePath = animationOpts.dividePath || defaultDividePath;\n        var toSubPathList = dividePath({\n            path: toPath, count: separateCount\n        });\n        if (toSubPathList.length !== separateCount) {\n            console.error('Invalid morphing: unmatched splitted path');\n            return createEmptyReturn();\n        }\n        fromPathList = sortPaths(fromPathList);\n        toSubPathList = sortPaths(toSubPathList);\n        var oldDone = animationOpts.done;\n        var oldDuring = animationOpts.during;\n        var individualDelay = animationOpts.individualDelay;\n        var identityTransform = new Transformable();\n        for (var i = 0; i < separateCount; i++) {\n            var from = fromPathList[i];\n            var to = toSubPathList[i];\n            to.parent = toPath;\n            to.copyTransform(identityTransform);\n            if (!individualDelay) {\n                prepareMorphPath(from, to);\n            }\n        }\n        toPath.__isCombineMorphing = true;\n        toPath.childrenRef = function () {\n            return toSubPathList;\n        };\n        function addToSubPathListToZr(zr) {\n            for (var i = 0; i < toSubPathList.length; i++) {\n                toSubPathList[i].addSelfToZr(zr);\n            }\n        }\n        saveAndModifyMethod(toPath, 'addSelfToZr', {\n            after: function (zr) {\n                addToSubPathListToZr(zr);\n            }\n        });\n        saveAndModifyMethod(toPath, 'removeSelfFromZr', {\n            after: function (zr) {\n                for (var i = 0; i < toSubPathList.length; i++) {\n                    toSubPathList[i].removeSelfFromZr(zr);\n                }\n            }\n        });\n        function restoreToPath() {\n            toPath.__isCombineMorphing = false;\n            toPath.__morphT = -1;\n            toPath.childrenRef = null;\n            restoreMethod(toPath, 'addSelfToZr');\n            restoreMethod(toPath, 'removeSelfFromZr');\n        }\n        var toLen = toSubPathList.length;\n        if (individualDelay) {\n            var animating_1 = toLen;\n            var eachDone = function () {\n                animating_1--;\n                if (animating_1 === 0) {\n                    restoreToPath();\n                    oldDone && oldDone();\n                }\n            };\n            for (var i = 0; i < toLen; i++) {\n                var indivdualAnimationOpts = individualDelay ? defaults({\n                    delay: (animationOpts.delay || 0) + individualDelay(i, toLen, fromPathList[i], toSubPathList[i]),\n                    done: eachDone\n                }, animationOpts) : animationOpts;\n                morphPath(fromPathList[i], toSubPathList[i], indivdualAnimationOpts);\n            }\n        }\n        else {\n            toPath.__morphT = 0;\n            toPath.animateTo({\n                __morphT: 1\n            }, defaults({\n                during: function (p) {\n                    for (var i = 0; i < toLen; i++) {\n                        var child = toSubPathList[i];\n                        child.__morphT = toPath.__morphT;\n                        child.dirtyShape();\n                    }\n                    oldDuring && oldDuring(p);\n                },\n                done: function () {\n                    restoreToPath();\n                    for (var i = 0; i < fromList.length; i++) {\n                        restoreMethod(fromList[i], 'updateTransform');\n                    }\n                    oldDone && oldDone();\n                }\n            }, animationOpts));\n        }\n        if (toPath.__zr) {\n            addToSubPathListToZr(toPath.__zr);\n        }\n        return {\n            fromIndividuals: fromPathList,\n            toIndividuals: toSubPathList,\n            count: toLen\n        };\n    }\n    function separateMorph(fromPath, toPathList, animationOpts) {\n        var toLen = toPathList.length;\n        var fromPathList = [];\n        var dividePath = animationOpts.dividePath || defaultDividePath;\n        function addFromPath(fromList) {\n            for (var i = 0; i < fromList.length; i++) {\n                var from = fromList[i];\n                if (isCombineMorphing(from)) {\n                    addFromPath(from.childrenRef());\n                }\n                else if (from instanceof Path) {\n                    fromPathList.push(from);\n                }\n            }\n        }\n        if (isCombineMorphing(fromPath)) {\n            addFromPath(fromPath.childrenRef());\n            var fromLen = fromPathList.length;\n            if (fromLen < toLen) {\n                var k = 0;\n                for (var i = fromLen; i < toLen; i++) {\n                    fromPathList.push(clonePath(fromPathList[k++ % fromLen]));\n                }\n            }\n            fromPathList.length = toLen;\n        }\n        else {\n            fromPathList = dividePath({ path: fromPath, count: toLen });\n            var fromPathTransform = fromPath.getComputedTransform();\n            for (var i = 0; i < fromPathList.length; i++) {\n                fromPathList[i].setLocalTransform(fromPathTransform);\n            }\n            if (fromPathList.length !== toLen) {\n                console.error('Invalid morphing: unmatched splitted path');\n                return createEmptyReturn();\n            }\n        }\n        fromPathList = sortPaths(fromPathList);\n        toPathList = sortPaths(toPathList);\n        var individualDelay = animationOpts.individualDelay;\n        for (var i = 0; i < toLen; i++) {\n            var indivdualAnimationOpts = individualDelay ? defaults({\n                delay: (animationOpts.delay || 0) + individualDelay(i, toLen, fromPathList[i], toPathList[i])\n            }, animationOpts) : animationOpts;\n            morphPath(fromPathList[i], toPathList[i], indivdualAnimationOpts);\n        }\n        return {\n            fromIndividuals: fromPathList,\n            toIndividuals: toPathList,\n            count: toPathList.length\n        };\n    }\n\n    function isMultiple(elements) {\n      return isArray(elements[0]);\n    }\n\n    function prepareMorphBatches(one, many) {\n      var batches = [];\n      var batchCount = one.length;\n\n      for (var i = 0; i < batchCount; i++) {\n        batches.push({\n          one: one[i],\n          many: []\n        });\n      }\n\n      for (var i = 0; i < many.length; i++) {\n        var len = many[i].length;\n        var k = void 0;\n\n        for (k = 0; k < len; k++) {\n          batches[k % batchCount].many.push(many[i][k]);\n        }\n      }\n\n      var off = 0; // If one has more paths than each one of many. average them.\n\n      for (var i = batchCount - 1; i >= 0; i--) {\n        if (!batches[i].many.length) {\n          var moveFrom = batches[off].many;\n\n          if (moveFrom.length <= 1) {\n            // Not enough\n            // Start from the first one.\n            if (off) {\n              off = 0;\n            } else {\n              return batches;\n            }\n          }\n\n          var len = moveFrom.length;\n          var mid = Math.ceil(len / 2);\n          batches[i].many = moveFrom.slice(mid, len);\n          batches[off].many = moveFrom.slice(0, mid);\n          off++;\n        }\n      }\n\n      return batches;\n    }\n\n    var pathDividers = {\n      clone: function (params) {\n        var ret = []; // Fitting the alpha\n\n        var approxOpacity = 1 - Math.pow(1 - params.path.style.opacity, 1 / params.count);\n\n        for (var i = 0; i < params.count; i++) {\n          var cloned = clonePath(params.path);\n          cloned.setStyle('opacity', approxOpacity);\n          ret.push(cloned);\n        }\n\n        return ret;\n      },\n      // Use the default divider\n      split: null\n    };\n    function applyMorphAnimation(from, to, divideShape, seriesModel, dataIndex, animateOtherProps) {\n      if (!from.length || !to.length) {\n        return;\n      }\n\n      var updateAnimationCfg = getAnimationConfig('update', seriesModel, dataIndex);\n\n      if (!(updateAnimationCfg && updateAnimationCfg.duration > 0)) {\n        return;\n      }\n\n      var animationDelay = seriesModel.getModel('universalTransition').get('delay');\n      var animationCfg = Object.assign({\n        // Need to setToFinal so the further calculation based on the style can be correct.\n        // Like emphasis color.\n        setToFinal: true\n      }, updateAnimationCfg);\n      var many;\n      var one;\n\n      if (isMultiple(from)) {\n        // manyToOne\n        many = from;\n        one = to;\n      }\n\n      if (isMultiple(to)) {\n        // oneToMany\n        many = to;\n        one = from;\n      }\n\n      function morphOneBatch(batch, fromIsMany, animateIndex, animateCount, forceManyOne) {\n        var batchMany = batch.many;\n        var batchOne = batch.one;\n\n        if (batchMany.length === 1 && !forceManyOne) {\n          // Is one to one\n          var batchFrom = fromIsMany ? batchMany[0] : batchOne;\n          var batchTo = fromIsMany ? batchOne : batchMany[0];\n\n          if (isCombineMorphing(batchFrom)) {\n            // Keep doing combine animation.\n            morphOneBatch({\n              many: [batchFrom],\n              one: batchTo\n            }, true, animateIndex, animateCount, true);\n          } else {\n            var individualAnimationCfg = animationDelay ? defaults({\n              delay: animationDelay(animateIndex, animateCount)\n            }, animationCfg) : animationCfg;\n            morphPath(batchFrom, batchTo, individualAnimationCfg);\n            animateOtherProps(batchFrom, batchTo, batchFrom, batchTo, individualAnimationCfg);\n          }\n        } else {\n          var separateAnimationCfg = defaults({\n            dividePath: pathDividers[divideShape],\n            individualDelay: animationDelay && function (idx, count, fromPath, toPath) {\n              return animationDelay(idx + animateIndex, animateCount);\n            }\n          }, animationCfg);\n\n          var _a = fromIsMany ? combineMorph(batchMany, batchOne, separateAnimationCfg) : separateMorph(batchOne, batchMany, separateAnimationCfg),\n              fromIndividuals = _a.fromIndividuals,\n              toIndividuals = _a.toIndividuals;\n\n          var count = fromIndividuals.length;\n\n          for (var k = 0; k < count; k++) {\n            var individualAnimationCfg = animationDelay ? defaults({\n              delay: animationDelay(k, count)\n            }, animationCfg) : animationCfg;\n            animateOtherProps(fromIndividuals[k], toIndividuals[k], fromIsMany ? batchMany[k] : batch.one, fromIsMany ? batch.one : batchMany[k], individualAnimationCfg);\n          }\n        }\n      }\n\n      var fromIsMany = many ? many === from // Is one to one. If the path number not match. also needs do merge and separate morphing.\n      : from.length > to.length;\n      var morphBatches = many ? prepareMorphBatches(one, many) : prepareMorphBatches(fromIsMany ? to : from, [fromIsMany ? from : to]);\n      var animateCount = 0;\n\n      for (var i = 0; i < morphBatches.length; i++) {\n        animateCount += morphBatches[i].many.length;\n      }\n\n      var animateIndex = 0;\n\n      for (var i = 0; i < morphBatches.length; i++) {\n        morphOneBatch(morphBatches[i], fromIsMany, animateIndex, animateCount);\n        animateIndex += morphBatches[i].many.length;\n      }\n    }\n    function getPathList(elements) {\n      if (!elements) {\n        return [];\n      }\n\n      if (isArray(elements)) {\n        var pathList_1 = [];\n\n        for (var i = 0; i < elements.length; i++) {\n          pathList_1.push(getPathList(elements[i]));\n        }\n\n        return pathList_1;\n      }\n\n      var pathList = [];\n      elements.traverse(function (el) {\n        if (el instanceof Path && !el.disableMorphing && !el.invisible && !el.ignore) {\n          pathList.push(el);\n        }\n      });\n      return pathList;\n    }\n\n    var DATA_COUNT_THRESHOLD = 1e4;\n    var getUniversalTransitionGlobalStore = makeInner();\n\n    function getGroupIdDimension(data) {\n      var dimensions = data.dimensions;\n\n      for (var i = 0; i < dimensions.length; i++) {\n        var dimInfo = data.getDimensionInfo(dimensions[i]);\n\n        if (dimInfo && dimInfo.otherDims.itemGroupId === 0) {\n          return dimensions[i];\n        }\n      }\n    }\n\n    function flattenDataDiffItems(list) {\n      var items = [];\n      each(list, function (seriesInfo) {\n        var data = seriesInfo.data;\n\n        if (data.count() > DATA_COUNT_THRESHOLD) {\n          if (\"development\" !== 'production') {\n            warn('Universal transition is disabled on large data > 10k.');\n          }\n\n          return;\n        }\n\n        var indices = data.getIndices();\n        var groupDim = getGroupIdDimension(data);\n\n        for (var dataIndex = 0; dataIndex < indices.length; dataIndex++) {\n          items.push({\n            dataGroupId: seriesInfo.dataGroupId,\n            data: data,\n            dim: seriesInfo.dim || groupDim,\n            divide: seriesInfo.divide,\n            dataIndex: dataIndex\n          });\n        }\n      });\n      return items;\n    }\n\n    function fadeInElement(newEl, newSeries, newIndex) {\n      newEl.traverse(function (el) {\n        if (el instanceof Path) {\n          // TODO use fade in animation for target element.\n          initProps(el, {\n            style: {\n              opacity: 0\n            }\n          }, newSeries, {\n            dataIndex: newIndex,\n            isFrom: true\n          });\n        }\n      });\n    }\n\n    function removeEl$1(el) {\n      if (el.parent) {\n        // Bake parent transform to element.\n        // So it can still have proper transform to transition after it's removed.\n        var computedTransform = el.getComputedTransform();\n        el.setLocalTransform(computedTransform);\n        el.parent.remove(el);\n      }\n    }\n\n    function stopAnimation(el) {\n      el.stopAnimation();\n\n      if (el.isGroup) {\n        el.traverse(function (child) {\n          child.stopAnimation();\n        });\n      }\n    }\n\n    function animateElementStyles(el, dataIndex, seriesModel) {\n      var animationConfig = getAnimationConfig('update', seriesModel, dataIndex);\n      animationConfig && el.traverse(function (child) {\n        if (child instanceof Displayable) {\n          var oldStyle = getOldStyle(child);\n\n          if (oldStyle) {\n            child.animateFrom({\n              style: oldStyle\n            }, animationConfig);\n          }\n        }\n      });\n    }\n\n    function isAllIdSame(oldDiffItems, newDiffItems) {\n      var len = oldDiffItems.length;\n\n      if (len !== newDiffItems.length) {\n        return false;\n      }\n\n      for (var i = 0; i < len; i++) {\n        var oldItem = oldDiffItems[i];\n        var newItem = newDiffItems[i];\n\n        if (oldItem.data.getId(oldItem.dataIndex) !== newItem.data.getId(newItem.dataIndex)) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    function transitionBetween(oldList, newList, api) {\n      var oldDiffItems = flattenDataDiffItems(oldList);\n      var newDiffItems = flattenDataDiffItems(newList);\n\n      function updateMorphingPathProps(from, to, rawFrom, rawTo, animationCfg) {\n        if (rawFrom || from) {\n          to.animateFrom({\n            style: rawFrom && rawFrom !== from ? // dividingMethod like clone may override the style(opacity)\n            // So extend it to raw style.\n            extend(extend({}, rawFrom.style), from.style) : from.style\n          }, animationCfg);\n        }\n      }\n\n      function findKeyDim(items) {\n        for (var i = 0; i < items.length; i++) {\n          if (items[i].dim) {\n            return items[i].dim;\n          }\n        }\n      }\n\n      var oldKeyDim = findKeyDim(oldDiffItems);\n      var newKeyDim = findKeyDim(newDiffItems);\n      var hasMorphAnimation = false;\n\n      function createKeyGetter(isOld, onlyGetId) {\n        return function (diffItem) {\n          var data = diffItem.data;\n          var dataIndex = diffItem.dataIndex; // TODO if specified dim\n\n          if (onlyGetId) {\n            return data.getId(dataIndex);\n          } // Use group id as transition key by default.\n          // So we can achieve multiple to multiple animation like drilldown / up naturally.\n          // If group id not exits. Use id instead. If so, only one to one transition will be applied.\n\n\n          var dataGroupId = diffItem.dataGroupId; // If specified key dimension(itemGroupId by default). Use this same dimension from other data.\n          // PENDING: If only use key dimension of newData.\n\n          var keyDim = isOld ? oldKeyDim || newKeyDim : newKeyDim || oldKeyDim;\n          var dimInfo = keyDim && data.getDimensionInfo(keyDim);\n          var dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta;\n\n          if (dimInfo) {\n            // Get from encode.itemGroupId.\n            var key = data.get(dimInfo.name, dataIndex);\n\n            if (dimOrdinalMeta) {\n              return dimOrdinalMeta.categories[key] || key + '';\n            }\n\n            return key + '';\n          } // Get groupId from raw item. { groupId: '' }\n\n\n          var itemVal = data.getRawDataItem(dataIndex);\n\n          if (itemVal && itemVal.groupId) {\n            return itemVal.groupId + '';\n          }\n\n          return dataGroupId || data.getId(dataIndex);\n        };\n      } // Use id if it's very likely to be an one to one animation\n      // It's more robust than groupId\n      // TODO Check if key dimension is specified.\n\n\n      var useId = isAllIdSame(oldDiffItems, newDiffItems);\n      var isElementStillInChart = {};\n\n      if (!useId) {\n        // We may have different diff strategy with basicTransition if we use other dimension as key.\n        // If so, we can't simply check if oldEl is same with newEl. We need a map to check if oldEl is still being used in the new chart.\n        // We can't use the elements that already being morphed. Let it keep it's original basic transition.\n        for (var i = 0; i < newDiffItems.length; i++) {\n          var newItem = newDiffItems[i];\n          var el = newItem.data.getItemGraphicEl(newItem.dataIndex);\n\n          if (el) {\n            isElementStillInChart[el.id] = true;\n          }\n        }\n      }\n\n      function updateOneToOne(newIndex, oldIndex) {\n        var oldItem = oldDiffItems[oldIndex];\n        var newItem = newDiffItems[newIndex];\n        var newSeries = newItem.data.hostModel; // TODO Mark this elements is morphed and don't morph them anymore\n\n        var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex);\n        var newEl = newItem.data.getItemGraphicEl(newItem.dataIndex); // Can't handle same elements.\n\n        if (oldEl === newEl) {\n          newEl && animateElementStyles(newEl, newItem.dataIndex, newSeries);\n          return;\n        }\n\n        if ( // We can't use the elements that already being morphed\n        oldEl && isElementStillInChart[oldEl.id]) {\n          return;\n        }\n\n        if (newEl) {\n          // TODO: If keep animating the group in case\n          // some of the elements don't want to be morphed.\n          // TODO Label?\n          stopAnimation(newEl);\n\n          if (oldEl) {\n            stopAnimation(oldEl); // If old element is doing leaving animation. stop it and remove it immediately.\n\n            removeEl$1(oldEl);\n            hasMorphAnimation = true;\n            applyMorphAnimation(getPathList(oldEl), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps);\n          } else {\n            fadeInElement(newEl, newSeries, newIndex);\n          }\n        } // else keep oldEl leaving animation.\n\n      }\n\n      new DataDiffer(oldDiffItems, newDiffItems, createKeyGetter(true, useId), createKeyGetter(false, useId), null, 'multiple').update(updateOneToOne).updateManyToOne(function (newIndex, oldIndices) {\n        var newItem = newDiffItems[newIndex];\n        var newData = newItem.data;\n        var newSeries = newData.hostModel;\n        var newEl = newData.getItemGraphicEl(newItem.dataIndex);\n        var oldElsList = filter(map(oldIndices, function (idx) {\n          return oldDiffItems[idx].data.getItemGraphicEl(oldDiffItems[idx].dataIndex);\n        }), function (oldEl) {\n          return oldEl && oldEl !== newEl && !isElementStillInChart[oldEl.id];\n        });\n\n        if (newEl) {\n          stopAnimation(newEl);\n\n          if (oldElsList.length) {\n            // If old element is doing leaving animation. stop it and remove it immediately.\n            each(oldElsList, function (oldEl) {\n              stopAnimation(oldEl);\n              removeEl$1(oldEl);\n            });\n            hasMorphAnimation = true;\n            applyMorphAnimation(getPathList(oldElsList), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps);\n          } else {\n            fadeInElement(newEl, newSeries, newItem.dataIndex);\n          }\n        } // else keep oldEl leaving animation.\n\n      }).updateOneToMany(function (newIndices, oldIndex) {\n        var oldItem = oldDiffItems[oldIndex];\n        var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex); // We can't use the elements that already being morphed\n\n        if (oldEl && isElementStillInChart[oldEl.id]) {\n          return;\n        }\n\n        var newElsList = filter(map(newIndices, function (idx) {\n          return newDiffItems[idx].data.getItemGraphicEl(newDiffItems[idx].dataIndex);\n        }), function (el) {\n          return el && el !== oldEl;\n        });\n        var newSeris = newDiffItems[newIndices[0]].data.hostModel;\n\n        if (newElsList.length) {\n          each(newElsList, function (newEl) {\n            return stopAnimation(newEl);\n          });\n\n          if (oldEl) {\n            stopAnimation(oldEl); // If old element is doing leaving animation. stop it and remove it immediately.\n\n            removeEl$1(oldEl);\n            hasMorphAnimation = true;\n            applyMorphAnimation(getPathList(oldEl), getPathList(newElsList), oldItem.divide, // Use divide on old.\n            newSeris, newIndices[0], updateMorphingPathProps);\n          } else {\n            each(newElsList, function (newEl) {\n              return fadeInElement(newEl, newSeris, newIndices[0]);\n            });\n          }\n        } // else keep oldEl leaving animation.\n\n      }).updateManyToMany(function (newIndices, oldIndices) {\n        // If two data are same and both have groupId.\n        // Normally they should be diff by id.\n        new DataDiffer(oldIndices, newIndices, function (rawIdx) {\n          return oldDiffItems[rawIdx].data.getId(oldDiffItems[rawIdx].dataIndex);\n        }, function (rawIdx) {\n          return newDiffItems[rawIdx].data.getId(newDiffItems[rawIdx].dataIndex);\n        }).update(function (newIndex, oldIndex) {\n          // Use the original index\n          updateOneToOne(newIndices[newIndex], oldIndices[oldIndex]);\n        }).execute();\n      }).execute();\n\n      if (hasMorphAnimation) {\n        each(newList, function (_a) {\n          var data = _a.data;\n          var seriesModel = data.hostModel;\n          var view = seriesModel && api.getViewOfSeriesModel(seriesModel);\n          var animationCfg = getAnimationConfig('update', seriesModel, 0); // use 0 index.\n\n          if (view && seriesModel.isAnimationEnabled() && animationCfg && animationCfg.duration > 0) {\n            view.group.traverse(function (el) {\n              if (el instanceof Path && !el.animators.length) {\n                // We can't accept there still exists element that has no animation\n                // if universalTransition is enabled\n                el.animateFrom({\n                  style: {\n                    opacity: 0\n                  }\n                }, animationCfg);\n              }\n            });\n          }\n        });\n      }\n    }\n\n    function getSeriesTransitionKey(series) {\n      var seriesKey = series.getModel('universalTransition').get('seriesKey');\n\n      if (!seriesKey) {\n        // Use series id by default.\n        return series.id;\n      }\n\n      return seriesKey;\n    }\n\n    function convertArraySeriesKeyToString(seriesKey) {\n      if (isArray(seriesKey)) {\n        // Order independent.\n        return seriesKey.sort().join(',');\n      }\n\n      return seriesKey;\n    }\n\n    function getDivideShapeFromData(data) {\n      if (data.hostModel) {\n        return data.hostModel.getModel('universalTransition').get('divideShape');\n      }\n    }\n\n    function findTransitionSeriesBatches(globalStore, params) {\n      var updateBatches = createHashMap();\n      var oldDataMap = createHashMap(); // Map that only store key in array seriesKey.\n      // Which is used to query the old data when transition from one to multiple series.\n\n      var oldDataMapForSplit = createHashMap();\n      each(globalStore.oldSeries, function (series, idx) {\n        var oldDataGroupId = globalStore.oldDataGroupIds[idx];\n        var oldData = globalStore.oldData[idx];\n        var transitionKey = getSeriesTransitionKey(series);\n        var transitionKeyStr = convertArraySeriesKeyToString(transitionKey);\n        oldDataMap.set(transitionKeyStr, {\n          dataGroupId: oldDataGroupId,\n          data: oldData\n        });\n\n        if (isArray(transitionKey)) {\n          // Same key can't in different array seriesKey.\n          each(transitionKey, function (key) {\n            oldDataMapForSplit.set(key, {\n              key: transitionKeyStr,\n              dataGroupId: oldDataGroupId,\n              data: oldData\n            });\n          });\n        }\n      });\n\n      function checkTransitionSeriesKeyDuplicated(transitionKeyStr) {\n        if (updateBatches.get(transitionKeyStr)) {\n          warn(\"Duplicated seriesKey in universalTransition \" + transitionKeyStr);\n        }\n      }\n\n      each(params.updatedSeries, function (series) {\n        if (series.isUniversalTransitionEnabled() && series.isAnimationEnabled()) {\n          var newDataGroupId = series.get('dataGroupId');\n          var newData = series.getData();\n          var transitionKey = getSeriesTransitionKey(series);\n          var transitionKeyStr = convertArraySeriesKeyToString(transitionKey); // Only transition between series with same id.\n\n          var oldData = oldDataMap.get(transitionKeyStr); // string transition key is the best match.\n\n          if (oldData) {\n            if (\"development\" !== 'production') {\n              checkTransitionSeriesKeyDuplicated(transitionKeyStr);\n            } // TODO check if data is same?\n\n\n            updateBatches.set(transitionKeyStr, {\n              oldSeries: [{\n                dataGroupId: oldData.dataGroupId,\n                divide: getDivideShapeFromData(oldData.data),\n                data: oldData.data\n              }],\n              newSeries: [{\n                dataGroupId: newDataGroupId,\n                divide: getDivideShapeFromData(newData),\n                data: newData\n              }]\n            });\n          } else {\n            // Transition from multiple series.\n            if (isArray(transitionKey)) {\n              if (\"development\" !== 'production') {\n                checkTransitionSeriesKeyDuplicated(transitionKeyStr);\n              }\n\n              var oldSeries_1 = [];\n              each(transitionKey, function (key) {\n                var oldData = oldDataMap.get(key);\n\n                if (oldData.data) {\n                  oldSeries_1.push({\n                    dataGroupId: oldData.dataGroupId,\n                    divide: getDivideShapeFromData(oldData.data),\n                    data: oldData.data\n                  });\n                }\n              });\n\n              if (oldSeries_1.length) {\n                updateBatches.set(transitionKeyStr, {\n                  oldSeries: oldSeries_1,\n                  newSeries: [{\n                    dataGroupId: newDataGroupId,\n                    data: newData,\n                    divide: getDivideShapeFromData(newData)\n                  }]\n                });\n              }\n            } else {\n              // Try transition to multiple series.\n              var oldData_1 = oldDataMapForSplit.get(transitionKey);\n\n              if (oldData_1) {\n                var batch = updateBatches.get(oldData_1.key);\n\n                if (!batch) {\n                  batch = {\n                    oldSeries: [{\n                      dataGroupId: oldData_1.dataGroupId,\n                      data: oldData_1.data,\n                      divide: getDivideShapeFromData(oldData_1.data)\n                    }],\n                    newSeries: []\n                  };\n                  updateBatches.set(oldData_1.key, batch);\n                }\n\n                batch.newSeries.push({\n                  dataGroupId: newDataGroupId,\n                  data: newData,\n                  divide: getDivideShapeFromData(newData)\n                });\n              }\n            }\n          }\n        }\n      });\n      return updateBatches;\n    }\n\n    function querySeries(series, finder) {\n      for (var i = 0; i < series.length; i++) {\n        var found = finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id;\n\n        if (found) {\n          return i;\n        }\n      }\n    }\n\n    function transitionSeriesFromOpt(transitionOpt, globalStore, params, api) {\n      var from = [];\n      var to = [];\n      each(normalizeToArray(transitionOpt.from), function (finder) {\n        var idx = querySeries(globalStore.oldSeries, finder);\n\n        if (idx >= 0) {\n          from.push({\n            dataGroupId: globalStore.oldDataGroupIds[idx],\n            data: globalStore.oldData[idx],\n            // TODO can specify divideShape in transition.\n            divide: getDivideShapeFromData(globalStore.oldData[idx]),\n            dim: finder.dimension\n          });\n        }\n      });\n      each(normalizeToArray(transitionOpt.to), function (finder) {\n        var idx = querySeries(params.updatedSeries, finder);\n\n        if (idx >= 0) {\n          var data = params.updatedSeries[idx].getData();\n          to.push({\n            dataGroupId: globalStore.oldDataGroupIds[idx],\n            data: data,\n            divide: getDivideShapeFromData(data),\n            dim: finder.dimension\n          });\n        }\n      });\n\n      if (from.length > 0 && to.length > 0) {\n        transitionBetween(from, to, api);\n      }\n    }\n\n    function installUniversalTransition(registers) {\n      registers.registerUpdateLifecycle('series:beforeupdate', function (ecMOdel, api, params) {\n        each(normalizeToArray(params.seriesTransition), function (transOpt) {\n          each(normalizeToArray(transOpt.to), function (finder) {\n            var series = params.updatedSeries;\n\n            for (var i = 0; i < series.length; i++) {\n              if (finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id) {\n                series[i][SERIES_UNIVERSAL_TRANSITION_PROP] = true;\n              }\n            }\n          });\n        });\n      });\n      registers.registerUpdateLifecycle('series:transition', function (ecModel, api, params) {\n        // TODO api provide an namespace that can save stuff per instance\n        var globalStore = getUniversalTransitionGlobalStore(api); // TODO multiple to multiple series.\n\n        if (globalStore.oldSeries && params.updatedSeries && params.optionChanged) {\n          // Use give transition config if its' give;\n          var transitionOpt = params.seriesTransition;\n\n          if (transitionOpt) {\n            each(normalizeToArray(transitionOpt), function (opt) {\n              transitionSeriesFromOpt(opt, globalStore, params, api);\n            });\n          } else {\n            // Else guess from series based on transition series key.\n            var updateBatches_1 = findTransitionSeriesBatches(globalStore, params);\n            each(updateBatches_1.keys(), function (key) {\n              var batch = updateBatches_1.get(key);\n              transitionBetween(batch.oldSeries, batch.newSeries, api);\n            });\n          } // Reset\n\n\n          each(params.updatedSeries, function (series) {\n            // Reset;\n            if (series[SERIES_UNIVERSAL_TRANSITION_PROP]) {\n              series[SERIES_UNIVERSAL_TRANSITION_PROP] = false;\n            }\n          });\n        } // Save all series of current update. Not only the updated one.\n\n\n        var allSeries = ecModel.getSeries();\n        var savedSeries = globalStore.oldSeries = [];\n        var savedDataGroupIds = globalStore.oldDataGroupIds = [];\n        var savedData = globalStore.oldData = [];\n\n        for (var i = 0; i < allSeries.length; i++) {\n          var data = allSeries[i].getData(); // Only save the data that can have transition.\n          // Avoid large data costing too much extra memory\n\n          if (data.count() < DATA_COUNT_THRESHOLD) {\n            savedSeries.push(allSeries[i]);\n            savedDataGroupIds.push(allSeries[i].get('dataGroupId'));\n            savedData.push(data);\n          }\n        }\n      });\n    }\n\n    // Render engines\n    // -----------------\n    // Render via Canvas.\n    // echarts.init(dom, null, { renderer: 'canvas' })\n\n    use([install$1]); // Render via SVG.\n    // echarts.init(dom, null, { renderer: 'svg' })\n\n    use([install]); // ----------------\n    // Charts (series)\n    // ----------------\n    // All of the series types, for example:\n    // chart.setOption({\n    //     series: [{\n    //         type: 'line' // or 'bar', 'pie', ...\n    //     }]\n    // });\n\n    use([install$2, install$3, install$4, install$6, install$8, install$a, install$b, install$c, install$d, install$e, install$f, install$h, install$i, install$j, install$k, install$l, install$m, install$n, install$o, install$p, install$q, install$r]); // -------------------\n    // Coordinate systems\n    // -------------------\n    // All of the axis modules have been included in the\n    // coordinate system module below, do not need to\n    // make extra import.\n    // `cartesian` coordinate system. For some historical\n    // reasons, it is named as grid, for example:\n    // chart.setOption({\n    //     grid: {...},\n    //     xAxis: {...},\n    //     yAxis: {...},\n    //     series: [{...}]\n    // });\n\n    use(install$t); // `polar` coordinate system, for example:\n    // chart.setOption({\n    //     polar: {...},\n    //     radiusAxis: {...},\n    //     angleAxis: {...},\n    //     series: [{\n    //         coordinateSystem: 'polar'\n    //     }]\n    // });\n\n    use(install$u); // `geo` coordinate system, for example:\n    // chart.setOption({\n    //     geo: {...},\n    //     series: [{\n    //         coordinateSystem: 'geo'\n    //     }]\n    // });\n\n    use(install$9); // `singleAxis` coordinate system (notice, it is a coordinate system\n    // with only one axis, work for chart like theme river), for example:\n    // chart.setOption({\n    //     singleAxis: {...}\n    //     series: [{type: 'themeRiver', ...}]\n    // });\n\n    use(install$v); // `parallel` coordinate system, only work for parallel series, for example:\n    // chart.setOption({\n    //     parallel: {...},\n    //     parallelAxis: [{...}, ...],\n    //     series: [{\n    //         type: 'parallel'\n    //     }]\n    // });\n\n    use(install$g); // `calendar` coordinate system. for example,\n    // chart.setOptionp({\n    //     calendar: {...},\n    //     series: [{\n    //         coordinateSystem: 'calendar'\n    //     }]\n    // );\n\n    use(install$w); // ------------------\n    // Other components\n    // ------------------\n    // `graphic` component, for example:\n    // chart.setOption({\n    //     graphic: {...}\n    // });\n\n    use(install$x); // `toolbox` component, for example:\n    // chart.setOption({\n    //     toolbox: {...}\n    // });\n\n    use(install$z); // `tooltip` component, for example:\n    // chart.setOption({\n    //     tooltip: {...}\n    // });\n\n    use(install$A); // `axisPointer` component, for example:\n    // chart.setOption({\n    //     tooltip: {axisPointer: {...}, ...}\n    // });\n    // Or\n    // chart.setOption({\n    //     axisPointer: {...}\n    // });\n\n    use(install$s); // `brush` component, for example:\n    // chart.setOption({\n    //     brush: {...}\n    // });\n    // Or\n    // chart.setOption({\n    //     tooltip: {feature: {brush: {...}}\n    // })\n\n    use(install$B); // `title` component, for example:\n    // chart.setOption({\n    //     title: {...}\n    // });\n\n    use(install$C); // `timeline` component, for example:\n    // chart.setOption({\n    //     timeline: {...}\n    // });\n\n    use(install$D); // `markPoint` component, for example:\n    // chart.setOption({\n    //     series: [{markPoint: {...}}]\n    // });\n\n    use(install$E); // `markLine` component, for example:\n    // chart.setOption({\n    //     series: [{markLine: {...}}]\n    // });\n\n    use(install$F); // `markArea` component, for example:\n    // chart.setOption({\n    //     series: [{markArea: {...}}]\n    // });\n\n    use(install$G); // `legend` component not scrollable. for example:\n    // chart.setOption({\n    //     legend: {...}\n    // });\n\n    use(install$J); // `dataZoom` component including both `dataZoomInside` and `dataZoomSlider`.\n\n    use(install$M); // `dataZoom` component providing drag, pinch, wheel behaviors\n    // inside coodinate system, for example:\n    // chart.setOption({\n    //     dataZoom: {type: 'inside'}\n    // });\n\n    use(install$K); // `dataZoom` component providing a slider bar, for example:\n    // chart.setOption({\n    //     dataZoom: {type: 'slider'}\n    // });\n\n    use(install$L); // `visualMap` component including both `visualMapContinuous` and `visualMapPiecewise`.\n\n    use(install$P); // `visualMap` component providing continuous bar, for example:\n    // chart.setOption({\n    //     visualMap: {type: 'continuous'}\n    // });\n\n    use(install$N); // `visualMap` component providing pieces bar, for example:\n    // chart.setOption({\n    //     visualMap: {type: 'piecewise'}\n    // });\n\n    use(install$O); // `aria` component providing aria, for example:\n    // chart.setOption({\n    //     aria: {...}\n    // });\n\n    use(install$Q); // dataset transform\n    // chart.setOption({\n    //     dataset: {\n    //          transform: []\n    //     }\n    // });\n\n    use(install$R);\n    use(install$S); // universal transition\n    // chart.setOption({\n    //     series: {\n    //         universalTransition: { enabled: true }\n    //     }\n    // })\n\n    use(installUniversalTransition); // label layout\n    // chart.setOption({\n    //     series: {\n    //         labelLayout: { hideOverlap: true }\n    //     }\n    // })\n\n    use(installLabelLayout);\n\n    exports.Axis = Axis;\n    exports.ChartView = ChartView;\n    exports.ComponentModel = ComponentModel;\n    exports.ComponentView = ComponentView;\n    exports.List = SeriesData;\n    exports.Model = Model;\n    exports.PRIORITY = PRIORITY;\n    exports.SeriesModel = SeriesModel;\n    exports.color = color;\n    exports.connect = connect;\n    exports.dataTool = dataTool;\n    exports.dependencies = dependencies;\n    exports.disConnect = disConnect;\n    exports.disconnect = disconnect;\n    exports.dispose = dispose$1;\n    exports.env = env;\n    exports.extendChartView = extendChartView;\n    exports.extendComponentModel = extendComponentModel;\n    exports.extendComponentView = extendComponentView;\n    exports.extendSeriesModel = extendSeriesModel;\n    exports.format = format$1;\n    exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;\n    exports.getInstanceByDom = getInstanceByDom;\n    exports.getInstanceById = getInstanceById;\n    exports.getMap = getMap;\n    exports.graphic = graphic$1;\n    exports.helper = helper;\n    exports.init = init$1;\n    exports.innerDrawElementOnCanvas = brushSingle;\n    exports.matrix = matrix;\n    exports.number = number;\n    exports.parseGeoJSON = parseGeoJSON;\n    exports.parseGeoJson = parseGeoJSON;\n    exports.registerAction = registerAction;\n    exports.registerCoordinateSystem = registerCoordinateSystem;\n    exports.registerLayout = registerLayout;\n    exports.registerLoading = registerLoading;\n    exports.registerLocale = registerLocale;\n    exports.registerMap = registerMap;\n    exports.registerPostInit = registerPostInit;\n    exports.registerPostUpdate = registerPostUpdate;\n    exports.registerPreprocessor = registerPreprocessor;\n    exports.registerProcessor = registerProcessor;\n    exports.registerTheme = registerTheme;\n    exports.registerTransform = registerTransform;\n    exports.registerUpdateLifecycle = registerUpdateLifecycle;\n    exports.registerVisual = registerVisual;\n    exports.setCanvasCreator = setCanvasCreator;\n    exports.setPlatformAPI = setPlatformAPI;\n    exports.throttle = throttle;\n    exports.time = time;\n    exports.use = use;\n    exports.util = util$1;\n    exports.vector = vector;\n    exports.version = version$1;\n    exports.zrUtil = util;\n    exports.zrender = zrender;\n\n    Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=echarts.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/echarts/echarts.simple.js",
    "content": "\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n    typeof define === 'function' && define.amd ? define(['exports'], factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.echarts = {}));\n}(this, (function (exports) { 'use strict';\n\n    /*! *****************************************************************************\n    Copyright (c) Microsoft Corporation.\n\n    Permission to use, copy, modify, and/or distribute this software for any\n    purpose with or without fee is hereby granted.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\n    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n    PERFORMANCE OF THIS SOFTWARE.\n    ***************************************************************************** */\n    /* global Reflect, Promise */\n\n    var extendStatics = function(d, b) {\n        extendStatics = Object.setPrototypeOf ||\n            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n        return extendStatics(d, b);\n    };\n\n    function __extends(d, b) {\n        if (typeof b !== \"function\" && b !== null)\n            throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    }\n\n    var Browser = (function () {\n        function Browser() {\n            this.firefox = false;\n            this.ie = false;\n            this.edge = false;\n            this.newEdge = false;\n            this.weChat = false;\n        }\n        return Browser;\n    }());\n    var Env = (function () {\n        function Env() {\n            this.browser = new Browser();\n            this.node = false;\n            this.wxa = false;\n            this.worker = false;\n            this.svgSupported = false;\n            this.touchEventsSupported = false;\n            this.pointerEventsSupported = false;\n            this.domSupported = false;\n            this.transformSupported = false;\n            this.transform3dSupported = false;\n            this.hasGlobalWindow = typeof window !== 'undefined';\n        }\n        return Env;\n    }());\n    var env = new Env();\n    if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {\n        env.wxa = true;\n        env.touchEventsSupported = true;\n    }\n    else if (typeof document === 'undefined' && typeof self !== 'undefined') {\n        env.worker = true;\n    }\n    else if (typeof navigator === 'undefined') {\n        env.node = true;\n        env.svgSupported = true;\n    }\n    else {\n        detect(navigator.userAgent, env);\n    }\n    function detect(ua, env) {\n        var browser = env.browser;\n        var firefox = ua.match(/Firefox\\/([\\d.]+)/);\n        var ie = ua.match(/MSIE\\s([\\d.]+)/)\n            || ua.match(/Trident\\/.+?rv:(([\\d.]+))/);\n        var edge = ua.match(/Edge?\\/([\\d.]+)/);\n        var weChat = (/micromessenger/i).test(ua);\n        if (firefox) {\n            browser.firefox = true;\n            browser.version = firefox[1];\n        }\n        if (ie) {\n            browser.ie = true;\n            browser.version = ie[1];\n        }\n        if (edge) {\n            browser.edge = true;\n            browser.version = edge[1];\n            browser.newEdge = +edge[1].split('.')[0] > 18;\n        }\n        if (weChat) {\n            browser.weChat = true;\n        }\n        env.svgSupported = typeof SVGRect !== 'undefined';\n        env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge;\n        env.pointerEventsSupported = 'onpointerdown' in window\n            && (browser.edge || (browser.ie && +browser.version >= 11));\n        env.domSupported = typeof document !== 'undefined';\n        var style = document.documentElement.style;\n        env.transform3dSupported = ((browser.ie && 'transition' in style)\n            || browser.edge\n            || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix()))\n            || 'MozPerspective' in style)\n            && !('OTransition' in style);\n        env.transformSupported = env.transform3dSupported\n            || (browser.ie && +browser.version >= 9);\n    }\n\n    var DEFAULT_FONT_SIZE = 12;\n    var DEFAULT_FONT_FAMILY = 'sans-serif';\n    var DEFAULT_FONT = DEFAULT_FONT_SIZE + \"px \" + DEFAULT_FONT_FAMILY;\n    var OFFSET = 20;\n    var SCALE = 100;\n    var defaultWidthMapStr = \"007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\\\\\WQb\\\\0FWLg\\\\bWb\\\\WQ\\\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\\\FFF5.5N\";\n    function getTextWidthMap(mapStr) {\n        var map = {};\n        if (typeof JSON === 'undefined') {\n            return map;\n        }\n        for (var i = 0; i < mapStr.length; i++) {\n            var char = String.fromCharCode(i + 32);\n            var size = (mapStr.charCodeAt(i) - OFFSET) / SCALE;\n            map[char] = size;\n        }\n        return map;\n    }\n    var DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr);\n    var platformApi = {\n        createCanvas: function () {\n            return typeof document !== 'undefined'\n                && document.createElement('canvas');\n        },\n        measureText: (function () {\n            var _ctx;\n            var _cachedFont;\n            return function (text, font) {\n                if (!_ctx) {\n                    var canvas = platformApi.createCanvas();\n                    _ctx = canvas && canvas.getContext('2d');\n                }\n                if (_ctx) {\n                    if (_cachedFont !== font) {\n                        _cachedFont = _ctx.font = font || DEFAULT_FONT;\n                    }\n                    return _ctx.measureText(text);\n                }\n                else {\n                    text = text || '';\n                    font = font || DEFAULT_FONT;\n                    var res = /(\\d+)px/.exec(font);\n                    var fontSize = res && +res[1] || DEFAULT_FONT_SIZE;\n                    var width = 0;\n                    if (font.indexOf('mono') >= 0) {\n                        width = fontSize * text.length;\n                    }\n                    else {\n                        for (var i = 0; i < text.length; i++) {\n                            var preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]];\n                            width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize);\n                        }\n                    }\n                    return { width: width };\n                }\n            };\n        })(),\n        loadImage: function (src, onload, onerror) {\n            var image = new Image();\n            image.onload = onload;\n            image.onerror = onerror;\n            image.src = src;\n            return image;\n        }\n    };\n    function setPlatformAPI(newPlatformApis) {\n        for (var key in platformApi) {\n            if (newPlatformApis[key]) {\n                platformApi[key] = newPlatformApis[key];\n            }\n        }\n    }\n\n    var BUILTIN_OBJECT = reduce([\n        'Function',\n        'RegExp',\n        'Date',\n        'Error',\n        'CanvasGradient',\n        'CanvasPattern',\n        'Image',\n        'Canvas'\n    ], function (obj, val) {\n        obj['[object ' + val + ']'] = true;\n        return obj;\n    }, {});\n    var TYPED_ARRAY = reduce([\n        'Int8',\n        'Uint8',\n        'Uint8Clamped',\n        'Int16',\n        'Uint16',\n        'Int32',\n        'Uint32',\n        'Float32',\n        'Float64'\n    ], function (obj, val) {\n        obj['[object ' + val + 'Array]'] = true;\n        return obj;\n    }, {});\n    var objToString = Object.prototype.toString;\n    var arrayProto = Array.prototype;\n    var nativeForEach = arrayProto.forEach;\n    var nativeFilter = arrayProto.filter;\n    var nativeSlice = arrayProto.slice;\n    var nativeMap = arrayProto.map;\n    var ctorFunction = function () { }.constructor;\n    var protoFunction = ctorFunction ? ctorFunction.prototype : null;\n    var protoKey = '__proto__';\n    var idStart = 0x0907;\n    function guid() {\n        return idStart++;\n    }\n    function logError() {\n        var args = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            args[_i] = arguments[_i];\n        }\n        if (typeof console !== 'undefined') {\n            console.error.apply(console, args);\n        }\n    }\n    function clone(source) {\n        if (source == null || typeof source !== 'object') {\n            return source;\n        }\n        var result = source;\n        var typeStr = objToString.call(source);\n        if (typeStr === '[object Array]') {\n            if (!isPrimitive(source)) {\n                result = [];\n                for (var i = 0, len = source.length; i < len; i++) {\n                    result[i] = clone(source[i]);\n                }\n            }\n        }\n        else if (TYPED_ARRAY[typeStr]) {\n            if (!isPrimitive(source)) {\n                var Ctor = source.constructor;\n                if (Ctor.from) {\n                    result = Ctor.from(source);\n                }\n                else {\n                    result = new Ctor(source.length);\n                    for (var i = 0, len = source.length; i < len; i++) {\n                        result[i] = source[i];\n                    }\n                }\n            }\n        }\n        else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {\n            result = {};\n            for (var key in source) {\n                if (source.hasOwnProperty(key) && key !== protoKey) {\n                    result[key] = clone(source[key]);\n                }\n            }\n        }\n        return result;\n    }\n    function merge(target, source, overwrite) {\n        if (!isObject(source) || !isObject(target)) {\n            return overwrite ? clone(source) : target;\n        }\n        for (var key in source) {\n            if (source.hasOwnProperty(key) && key !== protoKey) {\n                var targetProp = target[key];\n                var sourceProp = source[key];\n                if (isObject(sourceProp)\n                    && isObject(targetProp)\n                    && !isArray(sourceProp)\n                    && !isArray(targetProp)\n                    && !isDom(sourceProp)\n                    && !isDom(targetProp)\n                    && !isBuiltInObject(sourceProp)\n                    && !isBuiltInObject(targetProp)\n                    && !isPrimitive(sourceProp)\n                    && !isPrimitive(targetProp)) {\n                    merge(targetProp, sourceProp, overwrite);\n                }\n                else if (overwrite || !(key in target)) {\n                    target[key] = clone(source[key]);\n                }\n            }\n        }\n        return target;\n    }\n    function mergeAll(targetAndSources, overwrite) {\n        var result = targetAndSources[0];\n        for (var i = 1, len = targetAndSources.length; i < len; i++) {\n            result = merge(result, targetAndSources[i], overwrite);\n        }\n        return result;\n    }\n    function extend(target, source) {\n        if (Object.assign) {\n            Object.assign(target, source);\n        }\n        else {\n            for (var key in source) {\n                if (source.hasOwnProperty(key) && key !== protoKey) {\n                    target[key] = source[key];\n                }\n            }\n        }\n        return target;\n    }\n    function defaults(target, source, overlay) {\n        var keysArr = keys(source);\n        for (var i = 0; i < keysArr.length; i++) {\n            var key = keysArr[i];\n            if ((overlay ? source[key] != null : target[key] == null)) {\n                target[key] = source[key];\n            }\n        }\n        return target;\n    }\n    var createCanvas = platformApi.createCanvas;\n    function indexOf(array, value) {\n        if (array) {\n            if (array.indexOf) {\n                return array.indexOf(value);\n            }\n            for (var i = 0, len = array.length; i < len; i++) {\n                if (array[i] === value) {\n                    return i;\n                }\n            }\n        }\n        return -1;\n    }\n    function inherits(clazz, baseClazz) {\n        var clazzPrototype = clazz.prototype;\n        function F() { }\n        F.prototype = baseClazz.prototype;\n        clazz.prototype = new F();\n        for (var prop in clazzPrototype) {\n            if (clazzPrototype.hasOwnProperty(prop)) {\n                clazz.prototype[prop] = clazzPrototype[prop];\n            }\n        }\n        clazz.prototype.constructor = clazz;\n        clazz.superClass = baseClazz;\n    }\n    function mixin(target, source, override) {\n        target = 'prototype' in target ? target.prototype : target;\n        source = 'prototype' in source ? source.prototype : source;\n        if (Object.getOwnPropertyNames) {\n            var keyList = Object.getOwnPropertyNames(source);\n            for (var i = 0; i < keyList.length; i++) {\n                var key = keyList[i];\n                if (key !== 'constructor') {\n                    if ((override ? source[key] != null : target[key] == null)) {\n                        target[key] = source[key];\n                    }\n                }\n            }\n        }\n        else {\n            defaults(target, source, override);\n        }\n    }\n    function isArrayLike(data) {\n        if (!data) {\n            return false;\n        }\n        if (typeof data === 'string') {\n            return false;\n        }\n        return typeof data.length === 'number';\n    }\n    function each(arr, cb, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        if (arr.forEach && arr.forEach === nativeForEach) {\n            arr.forEach(cb, context);\n        }\n        else if (arr.length === +arr.length) {\n            for (var i = 0, len = arr.length; i < len; i++) {\n                cb.call(context, arr[i], i, arr);\n            }\n        }\n        else {\n            for (var key in arr) {\n                if (arr.hasOwnProperty(key)) {\n                    cb.call(context, arr[key], key, arr);\n                }\n            }\n        }\n    }\n    function map(arr, cb, context) {\n        if (!arr) {\n            return [];\n        }\n        if (!cb) {\n            return slice(arr);\n        }\n        if (arr.map && arr.map === nativeMap) {\n            return arr.map(cb, context);\n        }\n        else {\n            var result = [];\n            for (var i = 0, len = arr.length; i < len; i++) {\n                result.push(cb.call(context, arr[i], i, arr));\n            }\n            return result;\n        }\n    }\n    function reduce(arr, cb, memo, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        for (var i = 0, len = arr.length; i < len; i++) {\n            memo = cb.call(context, memo, arr[i], i, arr);\n        }\n        return memo;\n    }\n    function filter(arr, cb, context) {\n        if (!arr) {\n            return [];\n        }\n        if (!cb) {\n            return slice(arr);\n        }\n        if (arr.filter && arr.filter === nativeFilter) {\n            return arr.filter(cb, context);\n        }\n        else {\n            var result = [];\n            for (var i = 0, len = arr.length; i < len; i++) {\n                if (cb.call(context, arr[i], i, arr)) {\n                    result.push(arr[i]);\n                }\n            }\n            return result;\n        }\n    }\n    function find(arr, cb, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        for (var i = 0, len = arr.length; i < len; i++) {\n            if (cb.call(context, arr[i], i, arr)) {\n                return arr[i];\n            }\n        }\n    }\n    function keys(obj) {\n        if (!obj) {\n            return [];\n        }\n        if (Object.keys) {\n            return Object.keys(obj);\n        }\n        var keyList = [];\n        for (var key in obj) {\n            if (obj.hasOwnProperty(key)) {\n                keyList.push(key);\n            }\n        }\n        return keyList;\n    }\n    function bindPolyfill(func, context) {\n        var args = [];\n        for (var _i = 2; _i < arguments.length; _i++) {\n            args[_i - 2] = arguments[_i];\n        }\n        return function () {\n            return func.apply(context, args.concat(nativeSlice.call(arguments)));\n        };\n    }\n    var bind = (protoFunction && isFunction(protoFunction.bind))\n        ? protoFunction.call.bind(protoFunction.bind)\n        : bindPolyfill;\n    function curry(func) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        return function () {\n            return func.apply(this, args.concat(nativeSlice.call(arguments)));\n        };\n    }\n    function isArray(value) {\n        if (Array.isArray) {\n            return Array.isArray(value);\n        }\n        return objToString.call(value) === '[object Array]';\n    }\n    function isFunction(value) {\n        return typeof value === 'function';\n    }\n    function isString(value) {\n        return typeof value === 'string';\n    }\n    function isStringSafe(value) {\n        return objToString.call(value) === '[object String]';\n    }\n    function isNumber(value) {\n        return typeof value === 'number';\n    }\n    function isObject(value) {\n        var type = typeof value;\n        return type === 'function' || (!!value && type === 'object');\n    }\n    function isBuiltInObject(value) {\n        return !!BUILTIN_OBJECT[objToString.call(value)];\n    }\n    function isTypedArray(value) {\n        return !!TYPED_ARRAY[objToString.call(value)];\n    }\n    function isDom(value) {\n        return typeof value === 'object'\n            && typeof value.nodeType === 'number'\n            && typeof value.ownerDocument === 'object';\n    }\n    function isGradientObject(value) {\n        return value.colorStops != null;\n    }\n    function isImagePatternObject(value) {\n        return value.image != null;\n    }\n    function isRegExp(value) {\n        return objToString.call(value) === '[object RegExp]';\n    }\n    function eqNaN(value) {\n        return value !== value;\n    }\n    function retrieve() {\n        var args = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            args[_i] = arguments[_i];\n        }\n        for (var i = 0, len = args.length; i < len; i++) {\n            if (args[i] != null) {\n                return args[i];\n            }\n        }\n    }\n    function retrieve2(value0, value1) {\n        return value0 != null\n            ? value0\n            : value1;\n    }\n    function retrieve3(value0, value1, value2) {\n        return value0 != null\n            ? value0\n            : value1 != null\n                ? value1\n                : value2;\n    }\n    function slice(arr) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        return nativeSlice.apply(arr, args);\n    }\n    function normalizeCssArray(val) {\n        if (typeof (val) === 'number') {\n            return [val, val, val, val];\n        }\n        var len = val.length;\n        if (len === 2) {\n            return [val[0], val[1], val[0], val[1]];\n        }\n        else if (len === 3) {\n            return [val[0], val[1], val[2], val[1]];\n        }\n        return val;\n    }\n    function assert(condition, message) {\n        if (!condition) {\n            throw new Error(message);\n        }\n    }\n    function trim(str) {\n        if (str == null) {\n            return null;\n        }\n        else if (typeof str.trim === 'function') {\n            return str.trim();\n        }\n        else {\n            return str.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n        }\n    }\n    var primitiveKey = '__ec_primitive__';\n    function setAsPrimitive(obj) {\n        obj[primitiveKey] = true;\n    }\n    function isPrimitive(obj) {\n        return obj[primitiveKey];\n    }\n    var MapPolyfill = (function () {\n        function MapPolyfill() {\n            this.data = {};\n        }\n        MapPolyfill.prototype[\"delete\"] = function (key) {\n            var existed = this.has(key);\n            if (existed) {\n                delete this.data[key];\n            }\n            return existed;\n        };\n        MapPolyfill.prototype.has = function (key) {\n            return this.data.hasOwnProperty(key);\n        };\n        MapPolyfill.prototype.get = function (key) {\n            return this.data[key];\n        };\n        MapPolyfill.prototype.set = function (key, value) {\n            this.data[key] = value;\n            return this;\n        };\n        MapPolyfill.prototype.keys = function () {\n            return keys(this.data);\n        };\n        MapPolyfill.prototype.forEach = function (callback) {\n            var data = this.data;\n            for (var key in data) {\n                if (data.hasOwnProperty(key)) {\n                    callback(data[key], key);\n                }\n            }\n        };\n        return MapPolyfill;\n    }());\n    var isNativeMapSupported = typeof Map === 'function';\n    function maybeNativeMap() {\n        return (isNativeMapSupported ? new Map() : new MapPolyfill());\n    }\n    var HashMap = (function () {\n        function HashMap(obj) {\n            var isArr = isArray(obj);\n            this.data = maybeNativeMap();\n            var thisMap = this;\n            (obj instanceof HashMap)\n                ? obj.each(visit)\n                : (obj && each(obj, visit));\n            function visit(value, key) {\n                isArr ? thisMap.set(value, key) : thisMap.set(key, value);\n            }\n        }\n        HashMap.prototype.hasKey = function (key) {\n            return this.data.has(key);\n        };\n        HashMap.prototype.get = function (key) {\n            return this.data.get(key);\n        };\n        HashMap.prototype.set = function (key, value) {\n            this.data.set(key, value);\n            return value;\n        };\n        HashMap.prototype.each = function (cb, context) {\n            this.data.forEach(function (value, key) {\n                cb.call(context, value, key);\n            });\n        };\n        HashMap.prototype.keys = function () {\n            var keys = this.data.keys();\n            return isNativeMapSupported\n                ? Array.from(keys)\n                : keys;\n        };\n        HashMap.prototype.removeKey = function (key) {\n            this.data[\"delete\"](key);\n        };\n        return HashMap;\n    }());\n    function createHashMap(obj) {\n        return new HashMap(obj);\n    }\n    function concatArray(a, b) {\n        var newArray = new a.constructor(a.length + b.length);\n        for (var i = 0; i < a.length; i++) {\n            newArray[i] = a[i];\n        }\n        var offset = a.length;\n        for (var i = 0; i < b.length; i++) {\n            newArray[i + offset] = b[i];\n        }\n        return newArray;\n    }\n    function createObject(proto, properties) {\n        var obj;\n        if (Object.create) {\n            obj = Object.create(proto);\n        }\n        else {\n            var StyleCtor = function () { };\n            StyleCtor.prototype = proto;\n            obj = new StyleCtor();\n        }\n        if (properties) {\n            extend(obj, properties);\n        }\n        return obj;\n    }\n    function disableUserSelect(dom) {\n        var domStyle = dom.style;\n        domStyle.webkitUserSelect = 'none';\n        domStyle.userSelect = 'none';\n        domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)';\n        domStyle['-webkit-touch-callout'] = 'none';\n    }\n    function hasOwn(own, prop) {\n        return own.hasOwnProperty(prop);\n    }\n    function noop() { }\n    var RADIAN_TO_DEGREE = 180 / Math.PI;\n\n    var util = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        guid: guid,\n        logError: logError,\n        clone: clone,\n        merge: merge,\n        mergeAll: mergeAll,\n        extend: extend,\n        defaults: defaults,\n        createCanvas: createCanvas,\n        indexOf: indexOf,\n        inherits: inherits,\n        mixin: mixin,\n        isArrayLike: isArrayLike,\n        each: each,\n        map: map,\n        reduce: reduce,\n        filter: filter,\n        find: find,\n        keys: keys,\n        bind: bind,\n        curry: curry,\n        isArray: isArray,\n        isFunction: isFunction,\n        isString: isString,\n        isStringSafe: isStringSafe,\n        isNumber: isNumber,\n        isObject: isObject,\n        isBuiltInObject: isBuiltInObject,\n        isTypedArray: isTypedArray,\n        isDom: isDom,\n        isGradientObject: isGradientObject,\n        isImagePatternObject: isImagePatternObject,\n        isRegExp: isRegExp,\n        eqNaN: eqNaN,\n        retrieve: retrieve,\n        retrieve2: retrieve2,\n        retrieve3: retrieve3,\n        slice: slice,\n        normalizeCssArray: normalizeCssArray,\n        assert: assert,\n        trim: trim,\n        setAsPrimitive: setAsPrimitive,\n        isPrimitive: isPrimitive,\n        HashMap: HashMap,\n        createHashMap: createHashMap,\n        concatArray: concatArray,\n        createObject: createObject,\n        disableUserSelect: disableUserSelect,\n        hasOwn: hasOwn,\n        noop: noop,\n        RADIAN_TO_DEGREE: RADIAN_TO_DEGREE\n    });\n\n    function create(x, y) {\n        if (x == null) {\n            x = 0;\n        }\n        if (y == null) {\n            y = 0;\n        }\n        return [x, y];\n    }\n    function copy(out, v) {\n        out[0] = v[0];\n        out[1] = v[1];\n        return out;\n    }\n    function clone$1(v) {\n        return [v[0], v[1]];\n    }\n    function set(out, a, b) {\n        out[0] = a;\n        out[1] = b;\n        return out;\n    }\n    function add(out, v1, v2) {\n        out[0] = v1[0] + v2[0];\n        out[1] = v1[1] + v2[1];\n        return out;\n    }\n    function scaleAndAdd(out, v1, v2, a) {\n        out[0] = v1[0] + v2[0] * a;\n        out[1] = v1[1] + v2[1] * a;\n        return out;\n    }\n    function sub(out, v1, v2) {\n        out[0] = v1[0] - v2[0];\n        out[1] = v1[1] - v2[1];\n        return out;\n    }\n    function len(v) {\n        return Math.sqrt(lenSquare(v));\n    }\n    var length = len;\n    function lenSquare(v) {\n        return v[0] * v[0] + v[1] * v[1];\n    }\n    var lengthSquare = lenSquare;\n    function mul(out, v1, v2) {\n        out[0] = v1[0] * v2[0];\n        out[1] = v1[1] * v2[1];\n        return out;\n    }\n    function div(out, v1, v2) {\n        out[0] = v1[0] / v2[0];\n        out[1] = v1[1] / v2[1];\n        return out;\n    }\n    function dot(v1, v2) {\n        return v1[0] * v2[0] + v1[1] * v2[1];\n    }\n    function scale(out, v, s) {\n        out[0] = v[0] * s;\n        out[1] = v[1] * s;\n        return out;\n    }\n    function normalize(out, v) {\n        var d = len(v);\n        if (d === 0) {\n            out[0] = 0;\n            out[1] = 0;\n        }\n        else {\n            out[0] = v[0] / d;\n            out[1] = v[1] / d;\n        }\n        return out;\n    }\n    function distance(v1, v2) {\n        return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0])\n            + (v1[1] - v2[1]) * (v1[1] - v2[1]));\n    }\n    var dist = distance;\n    function distanceSquare(v1, v2) {\n        return (v1[0] - v2[0]) * (v1[0] - v2[0])\n            + (v1[1] - v2[1]) * (v1[1] - v2[1]);\n    }\n    var distSquare = distanceSquare;\n    function negate(out, v) {\n        out[0] = -v[0];\n        out[1] = -v[1];\n        return out;\n    }\n    function lerp(out, v1, v2, t) {\n        out[0] = v1[0] + t * (v2[0] - v1[0]);\n        out[1] = v1[1] + t * (v2[1] - v1[1]);\n        return out;\n    }\n    function applyTransform(out, v, m) {\n        var x = v[0];\n        var y = v[1];\n        out[0] = m[0] * x + m[2] * y + m[4];\n        out[1] = m[1] * x + m[3] * y + m[5];\n        return out;\n    }\n    function min(out, v1, v2) {\n        out[0] = Math.min(v1[0], v2[0]);\n        out[1] = Math.min(v1[1], v2[1]);\n        return out;\n    }\n    function max(out, v1, v2) {\n        out[0] = Math.max(v1[0], v2[0]);\n        out[1] = Math.max(v1[1], v2[1]);\n        return out;\n    }\n\n    var vector = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        create: create,\n        copy: copy,\n        clone: clone$1,\n        set: set,\n        add: add,\n        scaleAndAdd: scaleAndAdd,\n        sub: sub,\n        len: len,\n        length: length,\n        lenSquare: lenSquare,\n        lengthSquare: lengthSquare,\n        mul: mul,\n        div: div,\n        dot: dot,\n        scale: scale,\n        normalize: normalize,\n        distance: distance,\n        dist: dist,\n        distanceSquare: distanceSquare,\n        distSquare: distSquare,\n        negate: negate,\n        lerp: lerp,\n        applyTransform: applyTransform,\n        min: min,\n        max: max\n    });\n\n    var Param = (function () {\n        function Param(target, e) {\n            this.target = target;\n            this.topTarget = e && e.topTarget;\n        }\n        return Param;\n    }());\n    var Draggable = (function () {\n        function Draggable(handler) {\n            this.handler = handler;\n            handler.on('mousedown', this._dragStart, this);\n            handler.on('mousemove', this._drag, this);\n            handler.on('mouseup', this._dragEnd, this);\n        }\n        Draggable.prototype._dragStart = function (e) {\n            var draggingTarget = e.target;\n            while (draggingTarget && !draggingTarget.draggable) {\n                draggingTarget = draggingTarget.parent || draggingTarget.__hostTarget;\n            }\n            if (draggingTarget) {\n                this._draggingTarget = draggingTarget;\n                draggingTarget.dragging = true;\n                this._x = e.offsetX;\n                this._y = e.offsetY;\n                this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragstart', e.event);\n            }\n        };\n        Draggable.prototype._drag = function (e) {\n            var draggingTarget = this._draggingTarget;\n            if (draggingTarget) {\n                var x = e.offsetX;\n                var y = e.offsetY;\n                var dx = x - this._x;\n                var dy = y - this._y;\n                this._x = x;\n                this._y = y;\n                draggingTarget.drift(dx, dy, e);\n                this.handler.dispatchToElement(new Param(draggingTarget, e), 'drag', e.event);\n                var dropTarget = this.handler.findHover(x, y, draggingTarget).target;\n                var lastDropTarget = this._dropTarget;\n                this._dropTarget = dropTarget;\n                if (draggingTarget !== dropTarget) {\n                    if (lastDropTarget && dropTarget !== lastDropTarget) {\n                        this.handler.dispatchToElement(new Param(lastDropTarget, e), 'dragleave', e.event);\n                    }\n                    if (dropTarget && dropTarget !== lastDropTarget) {\n                        this.handler.dispatchToElement(new Param(dropTarget, e), 'dragenter', e.event);\n                    }\n                }\n            }\n        };\n        Draggable.prototype._dragEnd = function (e) {\n            var draggingTarget = this._draggingTarget;\n            if (draggingTarget) {\n                draggingTarget.dragging = false;\n            }\n            this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragend', e.event);\n            if (this._dropTarget) {\n                this.handler.dispatchToElement(new Param(this._dropTarget, e), 'drop', e.event);\n            }\n            this._draggingTarget = null;\n            this._dropTarget = null;\n        };\n        return Draggable;\n    }());\n\n    var Eventful = (function () {\n        function Eventful(eventProcessors) {\n            if (eventProcessors) {\n                this._$eventProcessor = eventProcessors;\n            }\n        }\n        Eventful.prototype.on = function (event, query, handler, context) {\n            if (!this._$handlers) {\n                this._$handlers = {};\n            }\n            var _h = this._$handlers;\n            if (typeof query === 'function') {\n                context = handler;\n                handler = query;\n                query = null;\n            }\n            if (!handler || !event) {\n                return this;\n            }\n            var eventProcessor = this._$eventProcessor;\n            if (query != null && eventProcessor && eventProcessor.normalizeQuery) {\n                query = eventProcessor.normalizeQuery(query);\n            }\n            if (!_h[event]) {\n                _h[event] = [];\n            }\n            for (var i = 0; i < _h[event].length; i++) {\n                if (_h[event][i].h === handler) {\n                    return this;\n                }\n            }\n            var wrap = {\n                h: handler,\n                query: query,\n                ctx: (context || this),\n                callAtLast: handler.zrEventfulCallAtLast\n            };\n            var lastIndex = _h[event].length - 1;\n            var lastWrap = _h[event][lastIndex];\n            (lastWrap && lastWrap.callAtLast)\n                ? _h[event].splice(lastIndex, 0, wrap)\n                : _h[event].push(wrap);\n            return this;\n        };\n        Eventful.prototype.isSilent = function (eventName) {\n            var _h = this._$handlers;\n            return !_h || !_h[eventName] || !_h[eventName].length;\n        };\n        Eventful.prototype.off = function (eventType, handler) {\n            var _h = this._$handlers;\n            if (!_h) {\n                return this;\n            }\n            if (!eventType) {\n                this._$handlers = {};\n                return this;\n            }\n            if (handler) {\n                if (_h[eventType]) {\n                    var newList = [];\n                    for (var i = 0, l = _h[eventType].length; i < l; i++) {\n                        if (_h[eventType][i].h !== handler) {\n                            newList.push(_h[eventType][i]);\n                        }\n                    }\n                    _h[eventType] = newList;\n                }\n                if (_h[eventType] && _h[eventType].length === 0) {\n                    delete _h[eventType];\n                }\n            }\n            else {\n                delete _h[eventType];\n            }\n            return this;\n        };\n        Eventful.prototype.trigger = function (eventType) {\n            var args = [];\n            for (var _i = 1; _i < arguments.length; _i++) {\n                args[_i - 1] = arguments[_i];\n            }\n            if (!this._$handlers) {\n                return this;\n            }\n            var _h = this._$handlers[eventType];\n            var eventProcessor = this._$eventProcessor;\n            if (_h) {\n                var argLen = args.length;\n                var len = _h.length;\n                for (var i = 0; i < len; i++) {\n                    var hItem = _h[i];\n                    if (eventProcessor\n                        && eventProcessor.filter\n                        && hItem.query != null\n                        && !eventProcessor.filter(eventType, hItem.query)) {\n                        continue;\n                    }\n                    switch (argLen) {\n                        case 0:\n                            hItem.h.call(hItem.ctx);\n                            break;\n                        case 1:\n                            hItem.h.call(hItem.ctx, args[0]);\n                            break;\n                        case 2:\n                            hItem.h.call(hItem.ctx, args[0], args[1]);\n                            break;\n                        default:\n                            hItem.h.apply(hItem.ctx, args);\n                            break;\n                    }\n                }\n            }\n            eventProcessor && eventProcessor.afterTrigger\n                && eventProcessor.afterTrigger(eventType);\n            return this;\n        };\n        Eventful.prototype.triggerWithContext = function (type) {\n            var args = [];\n            for (var _i = 1; _i < arguments.length; _i++) {\n                args[_i - 1] = arguments[_i];\n            }\n            if (!this._$handlers) {\n                return this;\n            }\n            var _h = this._$handlers[type];\n            var eventProcessor = this._$eventProcessor;\n            if (_h) {\n                var argLen = args.length;\n                var ctx = args[argLen - 1];\n                var len = _h.length;\n                for (var i = 0; i < len; i++) {\n                    var hItem = _h[i];\n                    if (eventProcessor\n                        && eventProcessor.filter\n                        && hItem.query != null\n                        && !eventProcessor.filter(type, hItem.query)) {\n                        continue;\n                    }\n                    switch (argLen) {\n                        case 0:\n                            hItem.h.call(ctx);\n                            break;\n                        case 1:\n                            hItem.h.call(ctx, args[0]);\n                            break;\n                        case 2:\n                            hItem.h.call(ctx, args[0], args[1]);\n                            break;\n                        default:\n                            hItem.h.apply(ctx, args.slice(1, argLen - 1));\n                            break;\n                    }\n                }\n            }\n            eventProcessor && eventProcessor.afterTrigger\n                && eventProcessor.afterTrigger(type);\n            return this;\n        };\n        return Eventful;\n    }());\n\n    var LN2 = Math.log(2);\n    function determinant(rows, rank, rowStart, rowMask, colMask, detCache) {\n        var cacheKey = rowMask + '-' + colMask;\n        var fullRank = rows.length;\n        if (detCache.hasOwnProperty(cacheKey)) {\n            return detCache[cacheKey];\n        }\n        if (rank === 1) {\n            var colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2);\n            return rows[rowStart][colStart];\n        }\n        var subRowMask = rowMask | (1 << rowStart);\n        var subRowStart = rowStart + 1;\n        while (rowMask & (1 << subRowStart)) {\n            subRowStart++;\n        }\n        var sum = 0;\n        for (var j = 0, colLocalIdx = 0; j < fullRank; j++) {\n            var colTag = 1 << j;\n            if (!(colTag & colMask)) {\n                sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j]\n                    * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache);\n                colLocalIdx++;\n            }\n        }\n        detCache[cacheKey] = sum;\n        return sum;\n    }\n    function buildTransformer(src, dest) {\n        var mA = [\n            [src[0], src[1], 1, 0, 0, 0, -dest[0] * src[0], -dest[0] * src[1]],\n            [0, 0, 0, src[0], src[1], 1, -dest[1] * src[0], -dest[1] * src[1]],\n            [src[2], src[3], 1, 0, 0, 0, -dest[2] * src[2], -dest[2] * src[3]],\n            [0, 0, 0, src[2], src[3], 1, -dest[3] * src[2], -dest[3] * src[3]],\n            [src[4], src[5], 1, 0, 0, 0, -dest[4] * src[4], -dest[4] * src[5]],\n            [0, 0, 0, src[4], src[5], 1, -dest[5] * src[4], -dest[5] * src[5]],\n            [src[6], src[7], 1, 0, 0, 0, -dest[6] * src[6], -dest[6] * src[7]],\n            [0, 0, 0, src[6], src[7], 1, -dest[7] * src[6], -dest[7] * src[7]]\n        ];\n        var detCache = {};\n        var det = determinant(mA, 8, 0, 0, 0, detCache);\n        if (det === 0) {\n            return;\n        }\n        var vh = [];\n        for (var i = 0; i < 8; i++) {\n            for (var j = 0; j < 8; j++) {\n                vh[j] == null && (vh[j] = 0);\n                vh[j] += ((i + j) % 2 ? -1 : 1)\n                    * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache)\n                    / det * dest[i];\n            }\n        }\n        return function (out, srcPointX, srcPointY) {\n            var pk = srcPointX * vh[6] + srcPointY * vh[7] + 1;\n            out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk;\n            out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk;\n        };\n    }\n\n    var EVENT_SAVED_PROP = '___zrEVENTSAVED';\n    function transformCoordWithViewport(out, el, inX, inY, inverse) {\n        if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) {\n            var saved = el[EVENT_SAVED_PROP] || (el[EVENT_SAVED_PROP] = {});\n            var markers = prepareCoordMarkers(el, saved);\n            var transformer = preparePointerTransformer(markers, saved, inverse);\n            if (transformer) {\n                transformer(out, inX, inY);\n                return true;\n            }\n        }\n        return false;\n    }\n    function prepareCoordMarkers(el, saved) {\n        var markers = saved.markers;\n        if (markers) {\n            return markers;\n        }\n        markers = saved.markers = [];\n        var propLR = ['left', 'right'];\n        var propTB = ['top', 'bottom'];\n        for (var i = 0; i < 4; i++) {\n            var marker = document.createElement('div');\n            var stl = marker.style;\n            var idxLR = i % 2;\n            var idxTB = (i >> 1) % 2;\n            stl.cssText = [\n                'position: absolute',\n                'visibility: hidden',\n                'padding: 0',\n                'margin: 0',\n                'border-width: 0',\n                'user-select: none',\n                'width:0',\n                'height:0',\n                propLR[idxLR] + ':0',\n                propTB[idxTB] + ':0',\n                propLR[1 - idxLR] + ':auto',\n                propTB[1 - idxTB] + ':auto',\n                ''\n            ].join('!important;');\n            el.appendChild(marker);\n            markers.push(marker);\n        }\n        return markers;\n    }\n    function preparePointerTransformer(markers, saved, inverse) {\n        var transformerName = inverse ? 'invTrans' : 'trans';\n        var transformer = saved[transformerName];\n        var oldSrcCoords = saved.srcCoords;\n        var srcCoords = [];\n        var destCoords = [];\n        var oldCoordTheSame = true;\n        for (var i = 0; i < 4; i++) {\n            var rect = markers[i].getBoundingClientRect();\n            var ii = 2 * i;\n            var x = rect.left;\n            var y = rect.top;\n            srcCoords.push(x, y);\n            oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1];\n            destCoords.push(markers[i].offsetLeft, markers[i].offsetTop);\n        }\n        return (oldCoordTheSame && transformer)\n            ? transformer\n            : (saved.srcCoords = srcCoords,\n                saved[transformerName] = inverse\n                    ? buildTransformer(destCoords, srcCoords)\n                    : buildTransformer(srcCoords, destCoords));\n    }\n    function isCanvasEl(el) {\n        return el.nodeName.toUpperCase() === 'CANVAS';\n    }\n    var replaceReg = /([&<>\"'])/g;\n    var replaceMap = {\n        '&': '&amp;',\n        '<': '&lt;',\n        '>': '&gt;',\n        '\"': '&quot;',\n        '\\'': '&#39;'\n    };\n    function encodeHTML(source) {\n        return source == null\n            ? ''\n            : (source + '').replace(replaceReg, function (str, c) {\n                return replaceMap[c];\n            });\n    }\n\n    var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;\n    var _calcOut = [];\n    var firefoxNotSupportOffsetXY = env.browser.firefox\n        && +env.browser.version.split('.')[0] < 39;\n    function clientToLocal(el, e, out, calculate) {\n        out = out || {};\n        if (calculate) {\n            calculateZrXY(el, e, out);\n        }\n        else if (firefoxNotSupportOffsetXY\n            && e.layerX != null\n            && e.layerX !== e.offsetX) {\n            out.zrX = e.layerX;\n            out.zrY = e.layerY;\n        }\n        else if (e.offsetX != null) {\n            out.zrX = e.offsetX;\n            out.zrY = e.offsetY;\n        }\n        else {\n            calculateZrXY(el, e, out);\n        }\n        return out;\n    }\n    function calculateZrXY(el, e, out) {\n        if (env.domSupported && el.getBoundingClientRect) {\n            var ex = e.clientX;\n            var ey = e.clientY;\n            if (isCanvasEl(el)) {\n                var box = el.getBoundingClientRect();\n                out.zrX = ex - box.left;\n                out.zrY = ey - box.top;\n                return;\n            }\n            else {\n                if (transformCoordWithViewport(_calcOut, el, ex, ey)) {\n                    out.zrX = _calcOut[0];\n                    out.zrY = _calcOut[1];\n                    return;\n                }\n            }\n        }\n        out.zrX = out.zrY = 0;\n    }\n    function getNativeEvent(e) {\n        return e\n            || window.event;\n    }\n    function normalizeEvent(el, e, calculate) {\n        e = getNativeEvent(e);\n        if (e.zrX != null) {\n            return e;\n        }\n        var eventType = e.type;\n        var isTouch = eventType && eventType.indexOf('touch') >= 0;\n        if (!isTouch) {\n            clientToLocal(el, e, e, calculate);\n            var wheelDelta = getWheelDeltaMayPolyfill(e);\n            e.zrDelta = wheelDelta ? wheelDelta / 120 : -(e.detail || 0) / 3;\n        }\n        else {\n            var touch = eventType !== 'touchend'\n                ? e.targetTouches[0]\n                : e.changedTouches[0];\n            touch && clientToLocal(el, touch, e, calculate);\n        }\n        var button = e.button;\n        if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {\n            e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n        }\n        return e;\n    }\n    function getWheelDeltaMayPolyfill(e) {\n        var rawWheelDelta = e.wheelDelta;\n        if (rawWheelDelta) {\n            return rawWheelDelta;\n        }\n        var deltaX = e.deltaX;\n        var deltaY = e.deltaY;\n        if (deltaX == null || deltaY == null) {\n            return rawWheelDelta;\n        }\n        var delta = deltaY !== 0 ? Math.abs(deltaY) : Math.abs(deltaX);\n        var sign = deltaY > 0 ? -1\n            : deltaY < 0 ? 1\n                : deltaX > 0 ? -1\n                    : 1;\n        return 3 * delta * sign;\n    }\n    function addEventListener(el, name, handler, opt) {\n        el.addEventListener(name, handler, opt);\n    }\n    function removeEventListener(el, name, handler, opt) {\n        el.removeEventListener(name, handler, opt);\n    }\n    var stop = function (e) {\n        e.preventDefault();\n        e.stopPropagation();\n        e.cancelBubble = true;\n    };\n\n    var GestureMgr = (function () {\n        function GestureMgr() {\n            this._track = [];\n        }\n        GestureMgr.prototype.recognize = function (event, target, root) {\n            this._doTrack(event, target, root);\n            return this._recognize(event);\n        };\n        GestureMgr.prototype.clear = function () {\n            this._track.length = 0;\n            return this;\n        };\n        GestureMgr.prototype._doTrack = function (event, target, root) {\n            var touches = event.touches;\n            if (!touches) {\n                return;\n            }\n            var trackItem = {\n                points: [],\n                touches: [],\n                target: target,\n                event: event\n            };\n            for (var i = 0, len = touches.length; i < len; i++) {\n                var touch = touches[i];\n                var pos = clientToLocal(root, touch, {});\n                trackItem.points.push([pos.zrX, pos.zrY]);\n                trackItem.touches.push(touch);\n            }\n            this._track.push(trackItem);\n        };\n        GestureMgr.prototype._recognize = function (event) {\n            for (var eventName in recognizers) {\n                if (recognizers.hasOwnProperty(eventName)) {\n                    var gestureInfo = recognizers[eventName](this._track, event);\n                    if (gestureInfo) {\n                        return gestureInfo;\n                    }\n                }\n            }\n        };\n        return GestureMgr;\n    }());\n    function dist$1(pointPair) {\n        var dx = pointPair[1][0] - pointPair[0][0];\n        var dy = pointPair[1][1] - pointPair[0][1];\n        return Math.sqrt(dx * dx + dy * dy);\n    }\n    function center(pointPair) {\n        return [\n            (pointPair[0][0] + pointPair[1][0]) / 2,\n            (pointPair[0][1] + pointPair[1][1]) / 2\n        ];\n    }\n    var recognizers = {\n        pinch: function (tracks, event) {\n            var trackLen = tracks.length;\n            if (!trackLen) {\n                return;\n            }\n            var pinchEnd = (tracks[trackLen - 1] || {}).points;\n            var pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd;\n            if (pinchPre\n                && pinchPre.length > 1\n                && pinchEnd\n                && pinchEnd.length > 1) {\n                var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);\n                !isFinite(pinchScale) && (pinchScale = 1);\n                event.pinchScale = pinchScale;\n                var pinchCenter = center(pinchEnd);\n                event.pinchX = pinchCenter[0];\n                event.pinchY = pinchCenter[1];\n                return {\n                    type: 'pinch',\n                    target: tracks[0].target,\n                    event: event\n                };\n            }\n        }\n    };\n\n    function create$1() {\n        return [1, 0, 0, 1, 0, 0];\n    }\n    function identity(out) {\n        out[0] = 1;\n        out[1] = 0;\n        out[2] = 0;\n        out[3] = 1;\n        out[4] = 0;\n        out[5] = 0;\n        return out;\n    }\n    function copy$1(out, m) {\n        out[0] = m[0];\n        out[1] = m[1];\n        out[2] = m[2];\n        out[3] = m[3];\n        out[4] = m[4];\n        out[5] = m[5];\n        return out;\n    }\n    function mul$1(out, m1, m2) {\n        var out0 = m1[0] * m2[0] + m1[2] * m2[1];\n        var out1 = m1[1] * m2[0] + m1[3] * m2[1];\n        var out2 = m1[0] * m2[2] + m1[2] * m2[3];\n        var out3 = m1[1] * m2[2] + m1[3] * m2[3];\n        var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];\n        var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];\n        out[0] = out0;\n        out[1] = out1;\n        out[2] = out2;\n        out[3] = out3;\n        out[4] = out4;\n        out[5] = out5;\n        return out;\n    }\n    function translate(out, a, v) {\n        out[0] = a[0];\n        out[1] = a[1];\n        out[2] = a[2];\n        out[3] = a[3];\n        out[4] = a[4] + v[0];\n        out[5] = a[5] + v[1];\n        return out;\n    }\n    function rotate(out, a, rad) {\n        var aa = a[0];\n        var ac = a[2];\n        var atx = a[4];\n        var ab = a[1];\n        var ad = a[3];\n        var aty = a[5];\n        var st = Math.sin(rad);\n        var ct = Math.cos(rad);\n        out[0] = aa * ct + ab * st;\n        out[1] = -aa * st + ab * ct;\n        out[2] = ac * ct + ad * st;\n        out[3] = -ac * st + ct * ad;\n        out[4] = ct * atx + st * aty;\n        out[5] = ct * aty - st * atx;\n        return out;\n    }\n    function scale$1(out, a, v) {\n        var vx = v[0];\n        var vy = v[1];\n        out[0] = a[0] * vx;\n        out[1] = a[1] * vy;\n        out[2] = a[2] * vx;\n        out[3] = a[3] * vy;\n        out[4] = a[4] * vx;\n        out[5] = a[5] * vy;\n        return out;\n    }\n    function invert(out, a) {\n        var aa = a[0];\n        var ac = a[2];\n        var atx = a[4];\n        var ab = a[1];\n        var ad = a[3];\n        var aty = a[5];\n        var det = aa * ad - ab * ac;\n        if (!det) {\n            return null;\n        }\n        det = 1.0 / det;\n        out[0] = ad * det;\n        out[1] = -ab * det;\n        out[2] = -ac * det;\n        out[3] = aa * det;\n        out[4] = (ac * aty - ad * atx) * det;\n        out[5] = (ab * atx - aa * aty) * det;\n        return out;\n    }\n    function clone$2(a) {\n        var b = create$1();\n        copy$1(b, a);\n        return b;\n    }\n\n    var matrix = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        create: create$1,\n        identity: identity,\n        copy: copy$1,\n        mul: mul$1,\n        translate: translate,\n        rotate: rotate,\n        scale: scale$1,\n        invert: invert,\n        clone: clone$2\n    });\n\n    var Point = (function () {\n        function Point(x, y) {\n            this.x = x || 0;\n            this.y = y || 0;\n        }\n        Point.prototype.copy = function (other) {\n            this.x = other.x;\n            this.y = other.y;\n            return this;\n        };\n        Point.prototype.clone = function () {\n            return new Point(this.x, this.y);\n        };\n        Point.prototype.set = function (x, y) {\n            this.x = x;\n            this.y = y;\n            return this;\n        };\n        Point.prototype.equal = function (other) {\n            return other.x === this.x && other.y === this.y;\n        };\n        Point.prototype.add = function (other) {\n            this.x += other.x;\n            this.y += other.y;\n            return this;\n        };\n        Point.prototype.scale = function (scalar) {\n            this.x *= scalar;\n            this.y *= scalar;\n        };\n        Point.prototype.scaleAndAdd = function (other, scalar) {\n            this.x += other.x * scalar;\n            this.y += other.y * scalar;\n        };\n        Point.prototype.sub = function (other) {\n            this.x -= other.x;\n            this.y -= other.y;\n            return this;\n        };\n        Point.prototype.dot = function (other) {\n            return this.x * other.x + this.y * other.y;\n        };\n        Point.prototype.len = function () {\n            return Math.sqrt(this.x * this.x + this.y * this.y);\n        };\n        Point.prototype.lenSquare = function () {\n            return this.x * this.x + this.y * this.y;\n        };\n        Point.prototype.normalize = function () {\n            var len = this.len();\n            this.x /= len;\n            this.y /= len;\n            return this;\n        };\n        Point.prototype.distance = function (other) {\n            var dx = this.x - other.x;\n            var dy = this.y - other.y;\n            return Math.sqrt(dx * dx + dy * dy);\n        };\n        Point.prototype.distanceSquare = function (other) {\n            var dx = this.x - other.x;\n            var dy = this.y - other.y;\n            return dx * dx + dy * dy;\n        };\n        Point.prototype.negate = function () {\n            this.x = -this.x;\n            this.y = -this.y;\n            return this;\n        };\n        Point.prototype.transform = function (m) {\n            if (!m) {\n                return;\n            }\n            var x = this.x;\n            var y = this.y;\n            this.x = m[0] * x + m[2] * y + m[4];\n            this.y = m[1] * x + m[3] * y + m[5];\n            return this;\n        };\n        Point.prototype.toArray = function (out) {\n            out[0] = this.x;\n            out[1] = this.y;\n            return out;\n        };\n        Point.prototype.fromArray = function (input) {\n            this.x = input[0];\n            this.y = input[1];\n        };\n        Point.set = function (p, x, y) {\n            p.x = x;\n            p.y = y;\n        };\n        Point.copy = function (p, p2) {\n            p.x = p2.x;\n            p.y = p2.y;\n        };\n        Point.len = function (p) {\n            return Math.sqrt(p.x * p.x + p.y * p.y);\n        };\n        Point.lenSquare = function (p) {\n            return p.x * p.x + p.y * p.y;\n        };\n        Point.dot = function (p0, p1) {\n            return p0.x * p1.x + p0.y * p1.y;\n        };\n        Point.add = function (out, p0, p1) {\n            out.x = p0.x + p1.x;\n            out.y = p0.y + p1.y;\n        };\n        Point.sub = function (out, p0, p1) {\n            out.x = p0.x - p1.x;\n            out.y = p0.y - p1.y;\n        };\n        Point.scale = function (out, p0, scalar) {\n            out.x = p0.x * scalar;\n            out.y = p0.y * scalar;\n        };\n        Point.scaleAndAdd = function (out, p0, p1, scalar) {\n            out.x = p0.x + p1.x * scalar;\n            out.y = p0.y + p1.y * scalar;\n        };\n        Point.lerp = function (out, p0, p1, t) {\n            var onet = 1 - t;\n            out.x = onet * p0.x + t * p1.x;\n            out.y = onet * p0.y + t * p1.y;\n        };\n        return Point;\n    }());\n\n    var mathMin = Math.min;\n    var mathMax = Math.max;\n    var lt = new Point();\n    var rb = new Point();\n    var lb = new Point();\n    var rt = new Point();\n    var minTv = new Point();\n    var maxTv = new Point();\n    var BoundingRect = (function () {\n        function BoundingRect(x, y, width, height) {\n            if (width < 0) {\n                x = x + width;\n                width = -width;\n            }\n            if (height < 0) {\n                y = y + height;\n                height = -height;\n            }\n            this.x = x;\n            this.y = y;\n            this.width = width;\n            this.height = height;\n        }\n        BoundingRect.prototype.union = function (other) {\n            var x = mathMin(other.x, this.x);\n            var y = mathMin(other.y, this.y);\n            if (isFinite(this.x) && isFinite(this.width)) {\n                this.width = mathMax(other.x + other.width, this.x + this.width) - x;\n            }\n            else {\n                this.width = other.width;\n            }\n            if (isFinite(this.y) && isFinite(this.height)) {\n                this.height = mathMax(other.y + other.height, this.y + this.height) - y;\n            }\n            else {\n                this.height = other.height;\n            }\n            this.x = x;\n            this.y = y;\n        };\n        BoundingRect.prototype.applyTransform = function (m) {\n            BoundingRect.applyTransform(this, this, m);\n        };\n        BoundingRect.prototype.calculateTransform = function (b) {\n            var a = this;\n            var sx = b.width / a.width;\n            var sy = b.height / a.height;\n            var m = create$1();\n            translate(m, m, [-a.x, -a.y]);\n            scale$1(m, m, [sx, sy]);\n            translate(m, m, [b.x, b.y]);\n            return m;\n        };\n        BoundingRect.prototype.intersect = function (b, mtv) {\n            if (!b) {\n                return false;\n            }\n            if (!(b instanceof BoundingRect)) {\n                b = BoundingRect.create(b);\n            }\n            var a = this;\n            var ax0 = a.x;\n            var ax1 = a.x + a.width;\n            var ay0 = a.y;\n            var ay1 = a.y + a.height;\n            var bx0 = b.x;\n            var bx1 = b.x + b.width;\n            var by0 = b.y;\n            var by1 = b.y + b.height;\n            var overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);\n            if (mtv) {\n                var dMin = Infinity;\n                var dMax = 0;\n                var d0 = Math.abs(ax1 - bx0);\n                var d1 = Math.abs(bx1 - ax0);\n                var d2 = Math.abs(ay1 - by0);\n                var d3 = Math.abs(by1 - ay0);\n                var dx = Math.min(d0, d1);\n                var dy = Math.min(d2, d3);\n                if (ax1 < bx0 || bx1 < ax0) {\n                    if (dx > dMax) {\n                        dMax = dx;\n                        if (d0 < d1) {\n                            Point.set(maxTv, -d0, 0);\n                        }\n                        else {\n                            Point.set(maxTv, d1, 0);\n                        }\n                    }\n                }\n                else {\n                    if (dx < dMin) {\n                        dMin = dx;\n                        if (d0 < d1) {\n                            Point.set(minTv, d0, 0);\n                        }\n                        else {\n                            Point.set(minTv, -d1, 0);\n                        }\n                    }\n                }\n                if (ay1 < by0 || by1 < ay0) {\n                    if (dy > dMax) {\n                        dMax = dy;\n                        if (d2 < d3) {\n                            Point.set(maxTv, 0, -d2);\n                        }\n                        else {\n                            Point.set(maxTv, 0, d3);\n                        }\n                    }\n                }\n                else {\n                    if (dx < dMin) {\n                        dMin = dx;\n                        if (d2 < d3) {\n                            Point.set(minTv, 0, d2);\n                        }\n                        else {\n                            Point.set(minTv, 0, -d3);\n                        }\n                    }\n                }\n            }\n            if (mtv) {\n                Point.copy(mtv, overlap ? minTv : maxTv);\n            }\n            return overlap;\n        };\n        BoundingRect.prototype.contain = function (x, y) {\n            var rect = this;\n            return x >= rect.x\n                && x <= (rect.x + rect.width)\n                && y >= rect.y\n                && y <= (rect.y + rect.height);\n        };\n        BoundingRect.prototype.clone = function () {\n            return new BoundingRect(this.x, this.y, this.width, this.height);\n        };\n        BoundingRect.prototype.copy = function (other) {\n            BoundingRect.copy(this, other);\n        };\n        BoundingRect.prototype.plain = function () {\n            return {\n                x: this.x,\n                y: this.y,\n                width: this.width,\n                height: this.height\n            };\n        };\n        BoundingRect.prototype.isFinite = function () {\n            return isFinite(this.x)\n                && isFinite(this.y)\n                && isFinite(this.width)\n                && isFinite(this.height);\n        };\n        BoundingRect.prototype.isZero = function () {\n            return this.width === 0 || this.height === 0;\n        };\n        BoundingRect.create = function (rect) {\n            return new BoundingRect(rect.x, rect.y, rect.width, rect.height);\n        };\n        BoundingRect.copy = function (target, source) {\n            target.x = source.x;\n            target.y = source.y;\n            target.width = source.width;\n            target.height = source.height;\n        };\n        BoundingRect.applyTransform = function (target, source, m) {\n            if (!m) {\n                if (target !== source) {\n                    BoundingRect.copy(target, source);\n                }\n                return;\n            }\n            if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) {\n                var sx = m[0];\n                var sy = m[3];\n                var tx = m[4];\n                var ty = m[5];\n                target.x = source.x * sx + tx;\n                target.y = source.y * sy + ty;\n                target.width = source.width * sx;\n                target.height = source.height * sy;\n                if (target.width < 0) {\n                    target.x += target.width;\n                    target.width = -target.width;\n                }\n                if (target.height < 0) {\n                    target.y += target.height;\n                    target.height = -target.height;\n                }\n                return;\n            }\n            lt.x = lb.x = source.x;\n            lt.y = rt.y = source.y;\n            rb.x = rt.x = source.x + source.width;\n            rb.y = lb.y = source.y + source.height;\n            lt.transform(m);\n            rt.transform(m);\n            rb.transform(m);\n            lb.transform(m);\n            target.x = mathMin(lt.x, rb.x, lb.x, rt.x);\n            target.y = mathMin(lt.y, rb.y, lb.y, rt.y);\n            var maxX = mathMax(lt.x, rb.x, lb.x, rt.x);\n            var maxY = mathMax(lt.y, rb.y, lb.y, rt.y);\n            target.width = maxX - target.x;\n            target.height = maxY - target.y;\n        };\n        return BoundingRect;\n    }());\n\n    var SILENT = 'silent';\n    function makeEventPacket(eveType, targetInfo, event) {\n        return {\n            type: eveType,\n            event: event,\n            target: targetInfo.target,\n            topTarget: targetInfo.topTarget,\n            cancelBubble: false,\n            offsetX: event.zrX,\n            offsetY: event.zrY,\n            gestureEvent: event.gestureEvent,\n            pinchX: event.pinchX,\n            pinchY: event.pinchY,\n            pinchScale: event.pinchScale,\n            wheelDelta: event.zrDelta,\n            zrByTouch: event.zrByTouch,\n            which: event.which,\n            stop: stopEvent\n        };\n    }\n    function stopEvent() {\n        stop(this.event);\n    }\n    var EmptyProxy = (function (_super) {\n        __extends(EmptyProxy, _super);\n        function EmptyProxy() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.handler = null;\n            return _this;\n        }\n        EmptyProxy.prototype.dispose = function () { };\n        EmptyProxy.prototype.setCursor = function () { };\n        return EmptyProxy;\n    }(Eventful));\n    var HoveredResult = (function () {\n        function HoveredResult(x, y) {\n            this.x = x;\n            this.y = y;\n        }\n        return HoveredResult;\n    }());\n    var handlerNames = [\n        'click', 'dblclick', 'mousewheel', 'mouseout',\n        'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n    ];\n    var tmpRect = new BoundingRect(0, 0, 0, 0);\n    var Handler = (function (_super) {\n        __extends(Handler, _super);\n        function Handler(storage, painter, proxy, painterRoot, pointerSize) {\n            var _this = _super.call(this) || this;\n            _this._hovered = new HoveredResult(0, 0);\n            _this.storage = storage;\n            _this.painter = painter;\n            _this.painterRoot = painterRoot;\n            _this._pointerSize = pointerSize;\n            proxy = proxy || new EmptyProxy();\n            _this.proxy = null;\n            _this.setHandlerProxy(proxy);\n            _this._draggingMgr = new Draggable(_this);\n            return _this;\n        }\n        Handler.prototype.setHandlerProxy = function (proxy) {\n            if (this.proxy) {\n                this.proxy.dispose();\n            }\n            if (proxy) {\n                each(handlerNames, function (name) {\n                    proxy.on && proxy.on(name, this[name], this);\n                }, this);\n                proxy.handler = this;\n            }\n            this.proxy = proxy;\n        };\n        Handler.prototype.mousemove = function (event) {\n            var x = event.zrX;\n            var y = event.zrY;\n            var isOutside = isOutsideBoundary(this, x, y);\n            var lastHovered = this._hovered;\n            var lastHoveredTarget = lastHovered.target;\n            if (lastHoveredTarget && !lastHoveredTarget.__zr) {\n                lastHovered = this.findHover(lastHovered.x, lastHovered.y);\n                lastHoveredTarget = lastHovered.target;\n            }\n            var hovered = this._hovered = isOutside ? new HoveredResult(x, y) : this.findHover(x, y);\n            var hoveredTarget = hovered.target;\n            var proxy = this.proxy;\n            proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');\n            if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {\n                this.dispatchToElement(lastHovered, 'mouseout', event);\n            }\n            this.dispatchToElement(hovered, 'mousemove', event);\n            if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {\n                this.dispatchToElement(hovered, 'mouseover', event);\n            }\n        };\n        Handler.prototype.mouseout = function (event) {\n            var eventControl = event.zrEventControl;\n            if (eventControl !== 'only_globalout') {\n                this.dispatchToElement(this._hovered, 'mouseout', event);\n            }\n            if (eventControl !== 'no_globalout') {\n                this.trigger('globalout', { type: 'globalout', event: event });\n            }\n        };\n        Handler.prototype.resize = function () {\n            this._hovered = new HoveredResult(0, 0);\n        };\n        Handler.prototype.dispatch = function (eventName, eventArgs) {\n            var handler = this[eventName];\n            handler && handler.call(this, eventArgs);\n        };\n        Handler.prototype.dispose = function () {\n            this.proxy.dispose();\n            this.storage = null;\n            this.proxy = null;\n            this.painter = null;\n        };\n        Handler.prototype.setCursorStyle = function (cursorStyle) {\n            var proxy = this.proxy;\n            proxy.setCursor && proxy.setCursor(cursorStyle);\n        };\n        Handler.prototype.dispatchToElement = function (targetInfo, eventName, event) {\n            targetInfo = targetInfo || {};\n            var el = targetInfo.target;\n            if (el && el.silent) {\n                return;\n            }\n            var eventKey = ('on' + eventName);\n            var eventPacket = makeEventPacket(eventName, targetInfo, event);\n            while (el) {\n                el[eventKey]\n                    && (eventPacket.cancelBubble = !!el[eventKey].call(el, eventPacket));\n                el.trigger(eventName, eventPacket);\n                el = el.__hostTarget ? el.__hostTarget : el.parent;\n                if (eventPacket.cancelBubble) {\n                    break;\n                }\n            }\n            if (!eventPacket.cancelBubble) {\n                this.trigger(eventName, eventPacket);\n                if (this.painter && this.painter.eachOtherLayer) {\n                    this.painter.eachOtherLayer(function (layer) {\n                        if (typeof (layer[eventKey]) === 'function') {\n                            layer[eventKey].call(layer, eventPacket);\n                        }\n                        if (layer.trigger) {\n                            layer.trigger(eventName, eventPacket);\n                        }\n                    });\n                }\n            }\n        };\n        Handler.prototype.findHover = function (x, y, exclude) {\n            var list = this.storage.getDisplayList();\n            var out = new HoveredResult(x, y);\n            setHoverTarget(list, out, x, y, exclude);\n            if (this._pointerSize && !out.target) {\n                var candidates = [];\n                var pointerSize = this._pointerSize;\n                var targetSizeHalf = pointerSize / 2;\n                var pointerRect = new BoundingRect(x - targetSizeHalf, y - targetSizeHalf, pointerSize, pointerSize);\n                for (var i = list.length - 1; i >= 0; i--) {\n                    var el = list[i];\n                    if (el !== exclude\n                        && !el.ignore\n                        && !el.ignoreCoarsePointer\n                        && (!el.parent || !el.parent.ignoreCoarsePointer)) {\n                        tmpRect.copy(el.getBoundingRect());\n                        if (el.transform) {\n                            tmpRect.applyTransform(el.transform);\n                        }\n                        if (tmpRect.intersect(pointerRect)) {\n                            candidates.push(el);\n                        }\n                    }\n                }\n                if (candidates.length) {\n                    var rStep = 4;\n                    var thetaStep = Math.PI / 12;\n                    var PI2 = Math.PI * 2;\n                    for (var r = 0; r < targetSizeHalf; r += rStep) {\n                        for (var theta = 0; theta < PI2; theta += thetaStep) {\n                            var x1 = x + r * Math.cos(theta);\n                            var y1 = y + r * Math.sin(theta);\n                            setHoverTarget(candidates, out, x1, y1, exclude);\n                            if (out.target) {\n                                return out;\n                            }\n                        }\n                    }\n                }\n            }\n            return out;\n        };\n        Handler.prototype.processGesture = function (event, stage) {\n            if (!this._gestureMgr) {\n                this._gestureMgr = new GestureMgr();\n            }\n            var gestureMgr = this._gestureMgr;\n            stage === 'start' && gestureMgr.clear();\n            var gestureInfo = gestureMgr.recognize(event, this.findHover(event.zrX, event.zrY, null).target, this.proxy.dom);\n            stage === 'end' && gestureMgr.clear();\n            if (gestureInfo) {\n                var type = gestureInfo.type;\n                event.gestureEvent = type;\n                var res = new HoveredResult();\n                res.target = gestureInfo.target;\n                this.dispatchToElement(res, type, gestureInfo.event);\n            }\n        };\n        return Handler;\n    }(Eventful));\n    each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {\n        Handler.prototype[name] = function (event) {\n            var x = event.zrX;\n            var y = event.zrY;\n            var isOutside = isOutsideBoundary(this, x, y);\n            var hovered;\n            var hoveredTarget;\n            if (name !== 'mouseup' || !isOutside) {\n                hovered = this.findHover(x, y);\n                hoveredTarget = hovered.target;\n            }\n            if (name === 'mousedown') {\n                this._downEl = hoveredTarget;\n                this._downPoint = [event.zrX, event.zrY];\n                this._upEl = hoveredTarget;\n            }\n            else if (name === 'mouseup') {\n                this._upEl = hoveredTarget;\n            }\n            else if (name === 'click') {\n                if (this._downEl !== this._upEl\n                    || !this._downPoint\n                    || dist(this._downPoint, [event.zrX, event.zrY]) > 4) {\n                    return;\n                }\n                this._downPoint = null;\n            }\n            this.dispatchToElement(hovered, name, event);\n        };\n    });\n    function isHover(displayable, x, y) {\n        if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {\n            var el = displayable;\n            var isSilent = void 0;\n            var ignoreClip = false;\n            while (el) {\n                if (el.ignoreClip) {\n                    ignoreClip = true;\n                }\n                if (!ignoreClip) {\n                    var clipPath = el.getClipPath();\n                    if (clipPath && !clipPath.contain(x, y)) {\n                        return false;\n                    }\n                    if (el.silent) {\n                        isSilent = true;\n                    }\n                }\n                var hostEl = el.__hostTarget;\n                el = hostEl ? hostEl : el.parent;\n            }\n            return isSilent ? SILENT : true;\n        }\n        return false;\n    }\n    function setHoverTarget(list, out, x, y, exclude) {\n        for (var i = list.length - 1; i >= 0; i--) {\n            var el = list[i];\n            var hoverCheckResult = void 0;\n            if (el !== exclude\n                && !el.ignore\n                && (hoverCheckResult = isHover(el, x, y))) {\n                !out.topTarget && (out.topTarget = el);\n                if (hoverCheckResult !== SILENT) {\n                    out.target = el;\n                    break;\n                }\n            }\n        }\n    }\n    function isOutsideBoundary(handlerInstance, x, y) {\n        var painter = handlerInstance.painter;\n        return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight();\n    }\n\n    var DEFAULT_MIN_MERGE = 32;\n    var DEFAULT_MIN_GALLOPING = 7;\n    function minRunLength(n) {\n        var r = 0;\n        while (n >= DEFAULT_MIN_MERGE) {\n            r |= n & 1;\n            n >>= 1;\n        }\n        return n + r;\n    }\n    function makeAscendingRun(array, lo, hi, compare) {\n        var runHi = lo + 1;\n        if (runHi === hi) {\n            return 1;\n        }\n        if (compare(array[runHi++], array[lo]) < 0) {\n            while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {\n                runHi++;\n            }\n            reverseRun(array, lo, runHi);\n        }\n        else {\n            while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {\n                runHi++;\n            }\n        }\n        return runHi - lo;\n    }\n    function reverseRun(array, lo, hi) {\n        hi--;\n        while (lo < hi) {\n            var t = array[lo];\n            array[lo++] = array[hi];\n            array[hi--] = t;\n        }\n    }\n    function binaryInsertionSort(array, lo, hi, start, compare) {\n        if (start === lo) {\n            start++;\n        }\n        for (; start < hi; start++) {\n            var pivot = array[start];\n            var left = lo;\n            var right = start;\n            var mid;\n            while (left < right) {\n                mid = left + right >>> 1;\n                if (compare(pivot, array[mid]) < 0) {\n                    right = mid;\n                }\n                else {\n                    left = mid + 1;\n                }\n            }\n            var n = start - left;\n            switch (n) {\n                case 3:\n                    array[left + 3] = array[left + 2];\n                case 2:\n                    array[left + 2] = array[left + 1];\n                case 1:\n                    array[left + 1] = array[left];\n                    break;\n                default:\n                    while (n > 0) {\n                        array[left + n] = array[left + n - 1];\n                        n--;\n                    }\n            }\n            array[left] = pivot;\n        }\n    }\n    function gallopLeft(value, array, start, length, hint, compare) {\n        var lastOffset = 0;\n        var maxOffset = 0;\n        var offset = 1;\n        if (compare(value, array[start + hint]) > 0) {\n            maxOffset = length - hint;\n            while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            lastOffset += hint;\n            offset += hint;\n        }\n        else {\n            maxOffset = hint + 1;\n            while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            var tmp = lastOffset;\n            lastOffset = hint - offset;\n            offset = hint - tmp;\n        }\n        lastOffset++;\n        while (lastOffset < offset) {\n            var m = lastOffset + (offset - lastOffset >>> 1);\n            if (compare(value, array[start + m]) > 0) {\n                lastOffset = m + 1;\n            }\n            else {\n                offset = m;\n            }\n        }\n        return offset;\n    }\n    function gallopRight(value, array, start, length, hint, compare) {\n        var lastOffset = 0;\n        var maxOffset = 0;\n        var offset = 1;\n        if (compare(value, array[start + hint]) < 0) {\n            maxOffset = hint + 1;\n            while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            var tmp = lastOffset;\n            lastOffset = hint - offset;\n            offset = hint - tmp;\n        }\n        else {\n            maxOffset = length - hint;\n            while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {\n                lastOffset = offset;\n                offset = (offset << 1) + 1;\n                if (offset <= 0) {\n                    offset = maxOffset;\n                }\n            }\n            if (offset > maxOffset) {\n                offset = maxOffset;\n            }\n            lastOffset += hint;\n            offset += hint;\n        }\n        lastOffset++;\n        while (lastOffset < offset) {\n            var m = lastOffset + (offset - lastOffset >>> 1);\n            if (compare(value, array[start + m]) < 0) {\n                offset = m;\n            }\n            else {\n                lastOffset = m + 1;\n            }\n        }\n        return offset;\n    }\n    function TimSort(array, compare) {\n        var minGallop = DEFAULT_MIN_GALLOPING;\n        var length = 0;\n        var runStart;\n        var runLength;\n        var stackSize = 0;\n        length = array.length;\n        var tmp = [];\n        runStart = [];\n        runLength = [];\n        function pushRun(_runStart, _runLength) {\n            runStart[stackSize] = _runStart;\n            runLength[stackSize] = _runLength;\n            stackSize += 1;\n        }\n        function mergeRuns() {\n            while (stackSize > 1) {\n                var n = stackSize - 2;\n                if ((n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1])\n                    || (n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1])) {\n                    if (runLength[n - 1] < runLength[n + 1]) {\n                        n--;\n                    }\n                }\n                else if (runLength[n] > runLength[n + 1]) {\n                    break;\n                }\n                mergeAt(n);\n            }\n        }\n        function forceMergeRuns() {\n            while (stackSize > 1) {\n                var n = stackSize - 2;\n                if (n > 0 && runLength[n - 1] < runLength[n + 1]) {\n                    n--;\n                }\n                mergeAt(n);\n            }\n        }\n        function mergeAt(i) {\n            var start1 = runStart[i];\n            var length1 = runLength[i];\n            var start2 = runStart[i + 1];\n            var length2 = runLength[i + 1];\n            runLength[i] = length1 + length2;\n            if (i === stackSize - 3) {\n                runStart[i + 1] = runStart[i + 2];\n                runLength[i + 1] = runLength[i + 2];\n            }\n            stackSize--;\n            var k = gallopRight(array[start2], array, start1, length1, 0, compare);\n            start1 += k;\n            length1 -= k;\n            if (length1 === 0) {\n                return;\n            }\n            length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);\n            if (length2 === 0) {\n                return;\n            }\n            if (length1 <= length2) {\n                mergeLow(start1, length1, start2, length2);\n            }\n            else {\n                mergeHigh(start1, length1, start2, length2);\n            }\n        }\n        function mergeLow(start1, length1, start2, length2) {\n            var i = 0;\n            for (i = 0; i < length1; i++) {\n                tmp[i] = array[start1 + i];\n            }\n            var cursor1 = 0;\n            var cursor2 = start2;\n            var dest = start1;\n            array[dest++] = array[cursor2++];\n            if (--length2 === 0) {\n                for (i = 0; i < length1; i++) {\n                    array[dest + i] = tmp[cursor1 + i];\n                }\n                return;\n            }\n            if (length1 === 1) {\n                for (i = 0; i < length2; i++) {\n                    array[dest + i] = array[cursor2 + i];\n                }\n                array[dest + length2] = tmp[cursor1];\n                return;\n            }\n            var _minGallop = minGallop;\n            var count1;\n            var count2;\n            var exit;\n            while (1) {\n                count1 = 0;\n                count2 = 0;\n                exit = false;\n                do {\n                    if (compare(array[cursor2], tmp[cursor1]) < 0) {\n                        array[dest++] = array[cursor2++];\n                        count2++;\n                        count1 = 0;\n                        if (--length2 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    else {\n                        array[dest++] = tmp[cursor1++];\n                        count1++;\n                        count2 = 0;\n                        if (--length1 === 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                } while ((count1 | count2) < _minGallop);\n                if (exit) {\n                    break;\n                }\n                do {\n                    count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);\n                    if (count1 !== 0) {\n                        for (i = 0; i < count1; i++) {\n                            array[dest + i] = tmp[cursor1 + i];\n                        }\n                        dest += count1;\n                        cursor1 += count1;\n                        length1 -= count1;\n                        if (length1 <= 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest++] = array[cursor2++];\n                    if (--length2 === 0) {\n                        exit = true;\n                        break;\n                    }\n                    count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);\n                    if (count2 !== 0) {\n                        for (i = 0; i < count2; i++) {\n                            array[dest + i] = array[cursor2 + i];\n                        }\n                        dest += count2;\n                        cursor2 += count2;\n                        length2 -= count2;\n                        if (length2 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest++] = tmp[cursor1++];\n                    if (--length1 === 1) {\n                        exit = true;\n                        break;\n                    }\n                    _minGallop--;\n                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n                if (exit) {\n                    break;\n                }\n                if (_minGallop < 0) {\n                    _minGallop = 0;\n                }\n                _minGallop += 2;\n            }\n            minGallop = _minGallop;\n            minGallop < 1 && (minGallop = 1);\n            if (length1 === 1) {\n                for (i = 0; i < length2; i++) {\n                    array[dest + i] = array[cursor2 + i];\n                }\n                array[dest + length2] = tmp[cursor1];\n            }\n            else if (length1 === 0) {\n                throw new Error();\n            }\n            else {\n                for (i = 0; i < length1; i++) {\n                    array[dest + i] = tmp[cursor1 + i];\n                }\n            }\n        }\n        function mergeHigh(start1, length1, start2, length2) {\n            var i = 0;\n            for (i = 0; i < length2; i++) {\n                tmp[i] = array[start2 + i];\n            }\n            var cursor1 = start1 + length1 - 1;\n            var cursor2 = length2 - 1;\n            var dest = start2 + length2 - 1;\n            var customCursor = 0;\n            var customDest = 0;\n            array[dest--] = array[cursor1--];\n            if (--length1 === 0) {\n                customCursor = dest - (length2 - 1);\n                for (i = 0; i < length2; i++) {\n                    array[customCursor + i] = tmp[i];\n                }\n                return;\n            }\n            if (length2 === 1) {\n                dest -= length1;\n                cursor1 -= length1;\n                customDest = dest + 1;\n                customCursor = cursor1 + 1;\n                for (i = length1 - 1; i >= 0; i--) {\n                    array[customDest + i] = array[customCursor + i];\n                }\n                array[dest] = tmp[cursor2];\n                return;\n            }\n            var _minGallop = minGallop;\n            while (true) {\n                var count1 = 0;\n                var count2 = 0;\n                var exit = false;\n                do {\n                    if (compare(tmp[cursor2], array[cursor1]) < 0) {\n                        array[dest--] = array[cursor1--];\n                        count1++;\n                        count2 = 0;\n                        if (--length1 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    else {\n                        array[dest--] = tmp[cursor2--];\n                        count2++;\n                        count1 = 0;\n                        if (--length2 === 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                } while ((count1 | count2) < _minGallop);\n                if (exit) {\n                    break;\n                }\n                do {\n                    count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);\n                    if (count1 !== 0) {\n                        dest -= count1;\n                        cursor1 -= count1;\n                        length1 -= count1;\n                        customDest = dest + 1;\n                        customCursor = cursor1 + 1;\n                        for (i = count1 - 1; i >= 0; i--) {\n                            array[customDest + i] = array[customCursor + i];\n                        }\n                        if (length1 === 0) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest--] = tmp[cursor2--];\n                    if (--length2 === 1) {\n                        exit = true;\n                        break;\n                    }\n                    count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);\n                    if (count2 !== 0) {\n                        dest -= count2;\n                        cursor2 -= count2;\n                        length2 -= count2;\n                        customDest = dest + 1;\n                        customCursor = cursor2 + 1;\n                        for (i = 0; i < count2; i++) {\n                            array[customDest + i] = tmp[customCursor + i];\n                        }\n                        if (length2 <= 1) {\n                            exit = true;\n                            break;\n                        }\n                    }\n                    array[dest--] = array[cursor1--];\n                    if (--length1 === 0) {\n                        exit = true;\n                        break;\n                    }\n                    _minGallop--;\n                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n                if (exit) {\n                    break;\n                }\n                if (_minGallop < 0) {\n                    _minGallop = 0;\n                }\n                _minGallop += 2;\n            }\n            minGallop = _minGallop;\n            if (minGallop < 1) {\n                minGallop = 1;\n            }\n            if (length2 === 1) {\n                dest -= length1;\n                cursor1 -= length1;\n                customDest = dest + 1;\n                customCursor = cursor1 + 1;\n                for (i = length1 - 1; i >= 0; i--) {\n                    array[customDest + i] = array[customCursor + i];\n                }\n                array[dest] = tmp[cursor2];\n            }\n            else if (length2 === 0) {\n                throw new Error();\n            }\n            else {\n                customCursor = dest - (length2 - 1);\n                for (i = 0; i < length2; i++) {\n                    array[customCursor + i] = tmp[i];\n                }\n            }\n        }\n        return {\n            mergeRuns: mergeRuns,\n            forceMergeRuns: forceMergeRuns,\n            pushRun: pushRun\n        };\n    }\n    function sort(array, compare, lo, hi) {\n        if (!lo) {\n            lo = 0;\n        }\n        if (!hi) {\n            hi = array.length;\n        }\n        var remaining = hi - lo;\n        if (remaining < 2) {\n            return;\n        }\n        var runLength = 0;\n        if (remaining < DEFAULT_MIN_MERGE) {\n            runLength = makeAscendingRun(array, lo, hi, compare);\n            binaryInsertionSort(array, lo, hi, lo + runLength, compare);\n            return;\n        }\n        var ts = TimSort(array, compare);\n        var minRun = minRunLength(remaining);\n        do {\n            runLength = makeAscendingRun(array, lo, hi, compare);\n            if (runLength < minRun) {\n                var force = remaining;\n                if (force > minRun) {\n                    force = minRun;\n                }\n                binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);\n                runLength = force;\n            }\n            ts.pushRun(lo, runLength);\n            ts.mergeRuns();\n            remaining -= runLength;\n            lo += runLength;\n        } while (remaining !== 0);\n        ts.forceMergeRuns();\n    }\n\n    var REDRAW_BIT = 1;\n    var STYLE_CHANGED_BIT = 2;\n    var SHAPE_CHANGED_BIT = 4;\n\n    var invalidZErrorLogged = false;\n    function logInvalidZError() {\n        if (invalidZErrorLogged) {\n            return;\n        }\n        invalidZErrorLogged = true;\n        console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors');\n    }\n    function shapeCompareFunc(a, b) {\n        if (a.zlevel === b.zlevel) {\n            if (a.z === b.z) {\n                return a.z2 - b.z2;\n            }\n            return a.z - b.z;\n        }\n        return a.zlevel - b.zlevel;\n    }\n    var Storage = (function () {\n        function Storage() {\n            this._roots = [];\n            this._displayList = [];\n            this._displayListLen = 0;\n            this.displayableSortFunc = shapeCompareFunc;\n        }\n        Storage.prototype.traverse = function (cb, context) {\n            for (var i = 0; i < this._roots.length; i++) {\n                this._roots[i].traverse(cb, context);\n            }\n        };\n        Storage.prototype.getDisplayList = function (update, includeIgnore) {\n            includeIgnore = includeIgnore || false;\n            var displayList = this._displayList;\n            if (update || !displayList.length) {\n                this.updateDisplayList(includeIgnore);\n            }\n            return displayList;\n        };\n        Storage.prototype.updateDisplayList = function (includeIgnore) {\n            this._displayListLen = 0;\n            var roots = this._roots;\n            var displayList = this._displayList;\n            for (var i = 0, len = roots.length; i < len; i++) {\n                this._updateAndAddDisplayable(roots[i], null, includeIgnore);\n            }\n            displayList.length = this._displayListLen;\n            sort(displayList, shapeCompareFunc);\n        };\n        Storage.prototype._updateAndAddDisplayable = function (el, clipPaths, includeIgnore) {\n            if (el.ignore && !includeIgnore) {\n                return;\n            }\n            el.beforeUpdate();\n            el.update();\n            el.afterUpdate();\n            var userSetClipPath = el.getClipPath();\n            if (el.ignoreClip) {\n                clipPaths = null;\n            }\n            else if (userSetClipPath) {\n                if (clipPaths) {\n                    clipPaths = clipPaths.slice();\n                }\n                else {\n                    clipPaths = [];\n                }\n                var currentClipPath = userSetClipPath;\n                var parentClipPath = el;\n                while (currentClipPath) {\n                    currentClipPath.parent = parentClipPath;\n                    currentClipPath.updateTransform();\n                    clipPaths.push(currentClipPath);\n                    parentClipPath = currentClipPath;\n                    currentClipPath = currentClipPath.getClipPath();\n                }\n            }\n            if (el.childrenRef) {\n                var children = el.childrenRef();\n                for (var i = 0; i < children.length; i++) {\n                    var child = children[i];\n                    if (el.__dirty) {\n                        child.__dirty |= REDRAW_BIT;\n                    }\n                    this._updateAndAddDisplayable(child, clipPaths, includeIgnore);\n                }\n                el.__dirty = 0;\n            }\n            else {\n                var disp = el;\n                if (clipPaths && clipPaths.length) {\n                    disp.__clipPaths = clipPaths;\n                }\n                else if (disp.__clipPaths && disp.__clipPaths.length > 0) {\n                    disp.__clipPaths = [];\n                }\n                if (isNaN(disp.z)) {\n                    logInvalidZError();\n                    disp.z = 0;\n                }\n                if (isNaN(disp.z2)) {\n                    logInvalidZError();\n                    disp.z2 = 0;\n                }\n                if (isNaN(disp.zlevel)) {\n                    logInvalidZError();\n                    disp.zlevel = 0;\n                }\n                this._displayList[this._displayListLen++] = disp;\n            }\n            var decalEl = el.getDecalElement && el.getDecalElement();\n            if (decalEl) {\n                this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore);\n            }\n            var textGuide = el.getTextGuideLine();\n            if (textGuide) {\n                this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore);\n            }\n            var textEl = el.getTextContent();\n            if (textEl) {\n                this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore);\n            }\n        };\n        Storage.prototype.addRoot = function (el) {\n            if (el.__zr && el.__zr.storage === this) {\n                return;\n            }\n            this._roots.push(el);\n        };\n        Storage.prototype.delRoot = function (el) {\n            if (el instanceof Array) {\n                for (var i = 0, l = el.length; i < l; i++) {\n                    this.delRoot(el[i]);\n                }\n                return;\n            }\n            var idx = indexOf(this._roots, el);\n            if (idx >= 0) {\n                this._roots.splice(idx, 1);\n            }\n        };\n        Storage.prototype.delAllRoots = function () {\n            this._roots = [];\n            this._displayList = [];\n            this._displayListLen = 0;\n            return;\n        };\n        Storage.prototype.getRoots = function () {\n            return this._roots;\n        };\n        Storage.prototype.dispose = function () {\n            this._displayList = null;\n            this._roots = null;\n        };\n        return Storage;\n    }());\n\n    var requestAnimationFrame;\n    requestAnimationFrame = (env.hasGlobalWindow\n        && ((window.requestAnimationFrame && window.requestAnimationFrame.bind(window))\n            || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))\n            || window.mozRequestAnimationFrame\n            || window.webkitRequestAnimationFrame)) || function (func) {\n        return setTimeout(func, 16);\n    };\n    var requestAnimationFrame$1 = requestAnimationFrame;\n\n    var easingFuncs = {\n        linear: function (k) {\n            return k;\n        },\n        quadraticIn: function (k) {\n            return k * k;\n        },\n        quadraticOut: function (k) {\n            return k * (2 - k);\n        },\n        quadraticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k;\n            }\n            return -0.5 * (--k * (k - 2) - 1);\n        },\n        cubicIn: function (k) {\n            return k * k * k;\n        },\n        cubicOut: function (k) {\n            return --k * k * k + 1;\n        },\n        cubicInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k;\n            }\n            return 0.5 * ((k -= 2) * k * k + 2);\n        },\n        quarticIn: function (k) {\n            return k * k * k * k;\n        },\n        quarticOut: function (k) {\n            return 1 - (--k * k * k * k);\n        },\n        quarticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k * k;\n            }\n            return -0.5 * ((k -= 2) * k * k * k - 2);\n        },\n        quinticIn: function (k) {\n            return k * k * k * k * k;\n        },\n        quinticOut: function (k) {\n            return --k * k * k * k * k + 1;\n        },\n        quinticInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return 0.5 * k * k * k * k * k;\n            }\n            return 0.5 * ((k -= 2) * k * k * k * k + 2);\n        },\n        sinusoidalIn: function (k) {\n            return 1 - Math.cos(k * Math.PI / 2);\n        },\n        sinusoidalOut: function (k) {\n            return Math.sin(k * Math.PI / 2);\n        },\n        sinusoidalInOut: function (k) {\n            return 0.5 * (1 - Math.cos(Math.PI * k));\n        },\n        exponentialIn: function (k) {\n            return k === 0 ? 0 : Math.pow(1024, k - 1);\n        },\n        exponentialOut: function (k) {\n            return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);\n        },\n        exponentialInOut: function (k) {\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if ((k *= 2) < 1) {\n                return 0.5 * Math.pow(1024, k - 1);\n            }\n            return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);\n        },\n        circularIn: function (k) {\n            return 1 - Math.sqrt(1 - k * k);\n        },\n        circularOut: function (k) {\n            return Math.sqrt(1 - (--k * k));\n        },\n        circularInOut: function (k) {\n            if ((k *= 2) < 1) {\n                return -0.5 * (Math.sqrt(1 - k * k) - 1);\n            }\n            return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);\n        },\n        elasticIn: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            return -(a * Math.pow(2, 10 * (k -= 1))\n                * Math.sin((k - s) * (2 * Math.PI) / p));\n        },\n        elasticOut: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            return (a * Math.pow(2, -10 * k)\n                * Math.sin((k - s) * (2 * Math.PI) / p) + 1);\n        },\n        elasticInOut: function (k) {\n            var s;\n            var a = 0.1;\n            var p = 0.4;\n            if (k === 0) {\n                return 0;\n            }\n            if (k === 1) {\n                return 1;\n            }\n            if (!a || a < 1) {\n                a = 1;\n                s = p / 4;\n            }\n            else {\n                s = p * Math.asin(1 / a) / (2 * Math.PI);\n            }\n            if ((k *= 2) < 1) {\n                return -0.5 * (a * Math.pow(2, 10 * (k -= 1))\n                    * Math.sin((k - s) * (2 * Math.PI) / p));\n            }\n            return a * Math.pow(2, -10 * (k -= 1))\n                * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;\n        },\n        backIn: function (k) {\n            var s = 1.70158;\n            return k * k * ((s + 1) * k - s);\n        },\n        backOut: function (k) {\n            var s = 1.70158;\n            return --k * k * ((s + 1) * k + s) + 1;\n        },\n        backInOut: function (k) {\n            var s = 1.70158 * 1.525;\n            if ((k *= 2) < 1) {\n                return 0.5 * (k * k * ((s + 1) * k - s));\n            }\n            return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);\n        },\n        bounceIn: function (k) {\n            return 1 - easingFuncs.bounceOut(1 - k);\n        },\n        bounceOut: function (k) {\n            if (k < (1 / 2.75)) {\n                return 7.5625 * k * k;\n            }\n            else if (k < (2 / 2.75)) {\n                return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;\n            }\n            else if (k < (2.5 / 2.75)) {\n                return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;\n            }\n            else {\n                return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;\n            }\n        },\n        bounceInOut: function (k) {\n            if (k < 0.5) {\n                return easingFuncs.bounceIn(k * 2) * 0.5;\n            }\n            return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5;\n        }\n    };\n\n    var mathPow = Math.pow;\n    var mathSqrt = Math.sqrt;\n    var EPSILON = 1e-8;\n    var EPSILON_NUMERIC = 1e-4;\n    var THREE_SQRT = mathSqrt(3);\n    var ONE_THIRD = 1 / 3;\n    var _v0 = create();\n    var _v1 = create();\n    var _v2 = create();\n    function isAroundZero(val) {\n        return val > -EPSILON && val < EPSILON;\n    }\n    function isNotAroundZero(val) {\n        return val > EPSILON || val < -EPSILON;\n    }\n    function cubicAt(p0, p1, p2, p3, t) {\n        var onet = 1 - t;\n        return onet * onet * (onet * p0 + 3 * t * p1)\n            + t * t * (t * p3 + 3 * onet * p2);\n    }\n    function cubicDerivativeAt(p0, p1, p2, p3, t) {\n        var onet = 1 - t;\n        return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet\n            + (p3 - p2) * t * t);\n    }\n    function cubicRootAt(p0, p1, p2, p3, val, roots) {\n        var a = p3 + 3 * (p1 - p2) - p0;\n        var b = 3 * (p2 - p1 * 2 + p0);\n        var c = 3 * (p1 - p0);\n        var d = p0 - val;\n        var A = b * b - 3 * a * c;\n        var B = b * c - 9 * a * d;\n        var C = c * c - 3 * b * d;\n        var n = 0;\n        if (isAroundZero(A) && isAroundZero(B)) {\n            if (isAroundZero(b)) {\n                roots[0] = 0;\n            }\n            else {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = B * B - 4 * A * C;\n            if (isAroundZero(disc)) {\n                var K = B / A;\n                var t1 = -b / a + K;\n                var t2 = -K / 2;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var Y1 = A * b + 1.5 * a * (-B + discSqrt);\n                var Y2 = A * b + 1.5 * a * (-B - discSqrt);\n                if (Y1 < 0) {\n                    Y1 = -mathPow(-Y1, ONE_THIRD);\n                }\n                else {\n                    Y1 = mathPow(Y1, ONE_THIRD);\n                }\n                if (Y2 < 0) {\n                    Y2 = -mathPow(-Y2, ONE_THIRD);\n                }\n                else {\n                    Y2 = mathPow(Y2, ONE_THIRD);\n                }\n                var t1 = (-b - (Y1 + Y2)) / (3 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n            else {\n                var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));\n                var theta = Math.acos(T) / 3;\n                var ASqrt = mathSqrt(A);\n                var tmp = Math.cos(theta);\n                var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);\n                var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);\n                var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n                if (t3 >= 0 && t3 <= 1) {\n                    roots[n++] = t3;\n                }\n            }\n        }\n        return n;\n    }\n    function cubicExtrema(p0, p1, p2, p3, extrema) {\n        var b = 6 * p2 - 12 * p1 + 6 * p0;\n        var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;\n        var c = 3 * p1 - 3 * p0;\n        var n = 0;\n        if (isAroundZero(a)) {\n            if (isNotAroundZero(b)) {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    extrema[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = b * b - 4 * a * c;\n            if (isAroundZero(disc)) {\n                extrema[0] = -b / (2 * a);\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var t1 = (-b + discSqrt) / (2 * a);\n                var t2 = (-b - discSqrt) / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    extrema[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    extrema[n++] = t2;\n                }\n            }\n        }\n        return n;\n    }\n    function cubicSubdivide(p0, p1, p2, p3, t, out) {\n        var p01 = (p1 - p0) * t + p0;\n        var p12 = (p2 - p1) * t + p1;\n        var p23 = (p3 - p2) * t + p2;\n        var p012 = (p12 - p01) * t + p01;\n        var p123 = (p23 - p12) * t + p12;\n        var p0123 = (p123 - p012) * t + p012;\n        out[0] = p0;\n        out[1] = p01;\n        out[2] = p012;\n        out[3] = p0123;\n        out[4] = p0123;\n        out[5] = p123;\n        out[6] = p23;\n        out[7] = p3;\n    }\n    function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) {\n        var t;\n        var interval = 0.005;\n        var d = Infinity;\n        var prev;\n        var next;\n        var d1;\n        var d2;\n        _v0[0] = x;\n        _v0[1] = y;\n        for (var _t = 0; _t < 1; _t += 0.05) {\n            _v1[0] = cubicAt(x0, x1, x2, x3, _t);\n            _v1[1] = cubicAt(y0, y1, y2, y3, _t);\n            d1 = distSquare(_v0, _v1);\n            if (d1 < d) {\n                t = _t;\n                d = d1;\n            }\n        }\n        d = Infinity;\n        for (var i = 0; i < 32; i++) {\n            if (interval < EPSILON_NUMERIC) {\n                break;\n            }\n            prev = t - interval;\n            next = t + interval;\n            _v1[0] = cubicAt(x0, x1, x2, x3, prev);\n            _v1[1] = cubicAt(y0, y1, y2, y3, prev);\n            d1 = distSquare(_v1, _v0);\n            if (prev >= 0 && d1 < d) {\n                t = prev;\n                d = d1;\n            }\n            else {\n                _v2[0] = cubicAt(x0, x1, x2, x3, next);\n                _v2[1] = cubicAt(y0, y1, y2, y3, next);\n                d2 = distSquare(_v2, _v0);\n                if (next <= 1 && d2 < d) {\n                    t = next;\n                    d = d2;\n                }\n                else {\n                    interval *= 0.5;\n                }\n            }\n        }\n        if (out) {\n            out[0] = cubicAt(x0, x1, x2, x3, t);\n            out[1] = cubicAt(y0, y1, y2, y3, t);\n        }\n        return mathSqrt(d);\n    }\n    function cubicLength(x0, y0, x1, y1, x2, y2, x3, y3, iteration) {\n        var px = x0;\n        var py = y0;\n        var d = 0;\n        var step = 1 / iteration;\n        for (var i = 1; i <= iteration; i++) {\n            var t = i * step;\n            var x = cubicAt(x0, x1, x2, x3, t);\n            var y = cubicAt(y0, y1, y2, y3, t);\n            var dx = x - px;\n            var dy = y - py;\n            d += Math.sqrt(dx * dx + dy * dy);\n            px = x;\n            py = y;\n        }\n        return d;\n    }\n    function quadraticAt(p0, p1, p2, t) {\n        var onet = 1 - t;\n        return onet * (onet * p0 + 2 * t * p1) + t * t * p2;\n    }\n    function quadraticDerivativeAt(p0, p1, p2, t) {\n        return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));\n    }\n    function quadraticRootAt(p0, p1, p2, val, roots) {\n        var a = p0 - 2 * p1 + p2;\n        var b = 2 * (p1 - p0);\n        var c = p0 - val;\n        var n = 0;\n        if (isAroundZero(a)) {\n            if (isNotAroundZero(b)) {\n                var t1 = -c / b;\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n        }\n        else {\n            var disc = b * b - 4 * a * c;\n            if (isAroundZero(disc)) {\n                var t1 = -b / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n            }\n            else if (disc > 0) {\n                var discSqrt = mathSqrt(disc);\n                var t1 = (-b + discSqrt) / (2 * a);\n                var t2 = (-b - discSqrt) / (2 * a);\n                if (t1 >= 0 && t1 <= 1) {\n                    roots[n++] = t1;\n                }\n                if (t2 >= 0 && t2 <= 1) {\n                    roots[n++] = t2;\n                }\n            }\n        }\n        return n;\n    }\n    function quadraticExtremum(p0, p1, p2) {\n        var divider = p0 + p2 - 2 * p1;\n        if (divider === 0) {\n            return 0.5;\n        }\n        else {\n            return (p0 - p1) / divider;\n        }\n    }\n    function quadraticSubdivide(p0, p1, p2, t, out) {\n        var p01 = (p1 - p0) * t + p0;\n        var p12 = (p2 - p1) * t + p1;\n        var p012 = (p12 - p01) * t + p01;\n        out[0] = p0;\n        out[1] = p01;\n        out[2] = p012;\n        out[3] = p012;\n        out[4] = p12;\n        out[5] = p2;\n    }\n    function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) {\n        var t;\n        var interval = 0.005;\n        var d = Infinity;\n        _v0[0] = x;\n        _v0[1] = y;\n        for (var _t = 0; _t < 1; _t += 0.05) {\n            _v1[0] = quadraticAt(x0, x1, x2, _t);\n            _v1[1] = quadraticAt(y0, y1, y2, _t);\n            var d1 = distSquare(_v0, _v1);\n            if (d1 < d) {\n                t = _t;\n                d = d1;\n            }\n        }\n        d = Infinity;\n        for (var i = 0; i < 32; i++) {\n            if (interval < EPSILON_NUMERIC) {\n                break;\n            }\n            var prev = t - interval;\n            var next = t + interval;\n            _v1[0] = quadraticAt(x0, x1, x2, prev);\n            _v1[1] = quadraticAt(y0, y1, y2, prev);\n            var d1 = distSquare(_v1, _v0);\n            if (prev >= 0 && d1 < d) {\n                t = prev;\n                d = d1;\n            }\n            else {\n                _v2[0] = quadraticAt(x0, x1, x2, next);\n                _v2[1] = quadraticAt(y0, y1, y2, next);\n                var d2 = distSquare(_v2, _v0);\n                if (next <= 1 && d2 < d) {\n                    t = next;\n                    d = d2;\n                }\n                else {\n                    interval *= 0.5;\n                }\n            }\n        }\n        if (out) {\n            out[0] = quadraticAt(x0, x1, x2, t);\n            out[1] = quadraticAt(y0, y1, y2, t);\n        }\n        return mathSqrt(d);\n    }\n    function quadraticLength(x0, y0, x1, y1, x2, y2, iteration) {\n        var px = x0;\n        var py = y0;\n        var d = 0;\n        var step = 1 / iteration;\n        for (var i = 1; i <= iteration; i++) {\n            var t = i * step;\n            var x = quadraticAt(x0, x1, x2, t);\n            var y = quadraticAt(y0, y1, y2, t);\n            var dx = x - px;\n            var dy = y - py;\n            d += Math.sqrt(dx * dx + dy * dy);\n            px = x;\n            py = y;\n        }\n        return d;\n    }\n\n    var regexp = /cubic-bezier\\(([0-9,\\.e ]+)\\)/;\n    function createCubicEasingFunc(cubicEasingStr) {\n        var cubic = cubicEasingStr && regexp.exec(cubicEasingStr);\n        if (cubic) {\n            var points = cubic[1].split(',');\n            var a_1 = +trim(points[0]);\n            var b_1 = +trim(points[1]);\n            var c_1 = +trim(points[2]);\n            var d_1 = +trim(points[3]);\n            if (isNaN(a_1 + b_1 + c_1 + d_1)) {\n                return;\n            }\n            var roots_1 = [];\n            return function (p) {\n                return p <= 0\n                    ? 0 : p >= 1\n                    ? 1\n                    : cubicRootAt(0, a_1, c_1, 1, p, roots_1) && cubicAt(0, b_1, d_1, 1, roots_1[0]);\n            };\n        }\n    }\n\n    var Clip = (function () {\n        function Clip(opts) {\n            this._inited = false;\n            this._startTime = 0;\n            this._pausedTime = 0;\n            this._paused = false;\n            this._life = opts.life || 1000;\n            this._delay = opts.delay || 0;\n            this.loop = opts.loop || false;\n            this.onframe = opts.onframe || noop;\n            this.ondestroy = opts.ondestroy || noop;\n            this.onrestart = opts.onrestart || noop;\n            opts.easing && this.setEasing(opts.easing);\n        }\n        Clip.prototype.step = function (globalTime, deltaTime) {\n            if (!this._inited) {\n                this._startTime = globalTime + this._delay;\n                this._inited = true;\n            }\n            if (this._paused) {\n                this._pausedTime += deltaTime;\n                return;\n            }\n            var life = this._life;\n            var elapsedTime = globalTime - this._startTime - this._pausedTime;\n            var percent = elapsedTime / life;\n            if (percent < 0) {\n                percent = 0;\n            }\n            percent = Math.min(percent, 1);\n            var easingFunc = this.easingFunc;\n            var schedule = easingFunc ? easingFunc(percent) : percent;\n            this.onframe(schedule);\n            if (percent === 1) {\n                if (this.loop) {\n                    var remainder = elapsedTime % life;\n                    this._startTime = globalTime - remainder;\n                    this._pausedTime = 0;\n                    this.onrestart();\n                }\n                else {\n                    return true;\n                }\n            }\n            return false;\n        };\n        Clip.prototype.pause = function () {\n            this._paused = true;\n        };\n        Clip.prototype.resume = function () {\n            this._paused = false;\n        };\n        Clip.prototype.setEasing = function (easing) {\n            this.easing = easing;\n            this.easingFunc = isFunction(easing)\n                ? easing\n                : easingFuncs[easing] || createCubicEasingFunc(easing);\n        };\n        return Clip;\n    }());\n\n    var Entry = (function () {\n        function Entry(val) {\n            this.value = val;\n        }\n        return Entry;\n    }());\n    var LinkedList = (function () {\n        function LinkedList() {\n            this._len = 0;\n        }\n        LinkedList.prototype.insert = function (val) {\n            var entry = new Entry(val);\n            this.insertEntry(entry);\n            return entry;\n        };\n        LinkedList.prototype.insertEntry = function (entry) {\n            if (!this.head) {\n                this.head = this.tail = entry;\n            }\n            else {\n                this.tail.next = entry;\n                entry.prev = this.tail;\n                entry.next = null;\n                this.tail = entry;\n            }\n            this._len++;\n        };\n        LinkedList.prototype.remove = function (entry) {\n            var prev = entry.prev;\n            var next = entry.next;\n            if (prev) {\n                prev.next = next;\n            }\n            else {\n                this.head = next;\n            }\n            if (next) {\n                next.prev = prev;\n            }\n            else {\n                this.tail = prev;\n            }\n            entry.next = entry.prev = null;\n            this._len--;\n        };\n        LinkedList.prototype.len = function () {\n            return this._len;\n        };\n        LinkedList.prototype.clear = function () {\n            this.head = this.tail = null;\n            this._len = 0;\n        };\n        return LinkedList;\n    }());\n    var LRU = (function () {\n        function LRU(maxSize) {\n            this._list = new LinkedList();\n            this._maxSize = 10;\n            this._map = {};\n            this._maxSize = maxSize;\n        }\n        LRU.prototype.put = function (key, value) {\n            var list = this._list;\n            var map = this._map;\n            var removed = null;\n            if (map[key] == null) {\n                var len = list.len();\n                var entry = this._lastRemovedEntry;\n                if (len >= this._maxSize && len > 0) {\n                    var leastUsedEntry = list.head;\n                    list.remove(leastUsedEntry);\n                    delete map[leastUsedEntry.key];\n                    removed = leastUsedEntry.value;\n                    this._lastRemovedEntry = leastUsedEntry;\n                }\n                if (entry) {\n                    entry.value = value;\n                }\n                else {\n                    entry = new Entry(value);\n                }\n                entry.key = key;\n                list.insertEntry(entry);\n                map[key] = entry;\n            }\n            return removed;\n        };\n        LRU.prototype.get = function (key) {\n            var entry = this._map[key];\n            var list = this._list;\n            if (entry != null) {\n                if (entry !== list.tail) {\n                    list.remove(entry);\n                    list.insertEntry(entry);\n                }\n                return entry.value;\n            }\n        };\n        LRU.prototype.clear = function () {\n            this._list.clear();\n            this._map = {};\n        };\n        LRU.prototype.len = function () {\n            return this._list.len();\n        };\n        return LRU;\n    }());\n\n    var kCSSColorTable = {\n        'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1],\n        'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1],\n        'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1],\n        'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1],\n        'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1],\n        'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1],\n        'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1],\n        'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1],\n        'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1],\n        'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1],\n        'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1],\n        'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1],\n        'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1],\n        'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1],\n        'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1],\n        'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1],\n        'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1],\n        'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1],\n        'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1],\n        'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1],\n        'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1],\n        'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1],\n        'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1],\n        'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1],\n        'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1],\n        'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1],\n        'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1],\n        'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1],\n        'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1],\n        'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1],\n        'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1],\n        'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1],\n        'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1],\n        'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1],\n        'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1],\n        'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1],\n        'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1],\n        'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1],\n        'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1],\n        'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1],\n        'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1],\n        'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1],\n        'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1],\n        'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1],\n        'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1],\n        'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1],\n        'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1],\n        'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1],\n        'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1],\n        'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1],\n        'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1],\n        'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1],\n        'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1],\n        'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1],\n        'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1],\n        'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1],\n        'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1],\n        'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1],\n        'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1],\n        'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1],\n        'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1],\n        'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1],\n        'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1],\n        'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1],\n        'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1],\n        'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1],\n        'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1],\n        'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1],\n        'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1],\n        'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1],\n        'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1],\n        'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1],\n        'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1],\n        'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1]\n    };\n    function clampCssByte(i) {\n        i = Math.round(i);\n        return i < 0 ? 0 : i > 255 ? 255 : i;\n    }\n    function clampCssAngle(i) {\n        i = Math.round(i);\n        return i < 0 ? 0 : i > 360 ? 360 : i;\n    }\n    function clampCssFloat(f) {\n        return f < 0 ? 0 : f > 1 ? 1 : f;\n    }\n    function parseCssInt(val) {\n        var str = val;\n        if (str.length && str.charAt(str.length - 1) === '%') {\n            return clampCssByte(parseFloat(str) / 100 * 255);\n        }\n        return clampCssByte(parseInt(str, 10));\n    }\n    function parseCssFloat(val) {\n        var str = val;\n        if (str.length && str.charAt(str.length - 1) === '%') {\n            return clampCssFloat(parseFloat(str) / 100);\n        }\n        return clampCssFloat(parseFloat(str));\n    }\n    function cssHueToRgb(m1, m2, h) {\n        if (h < 0) {\n            h += 1;\n        }\n        else if (h > 1) {\n            h -= 1;\n        }\n        if (h * 6 < 1) {\n            return m1 + (m2 - m1) * h * 6;\n        }\n        if (h * 2 < 1) {\n            return m2;\n        }\n        if (h * 3 < 2) {\n            return m1 + (m2 - m1) * (2 / 3 - h) * 6;\n        }\n        return m1;\n    }\n    function lerpNumber(a, b, p) {\n        return a + (b - a) * p;\n    }\n    function setRgba(out, r, g, b, a) {\n        out[0] = r;\n        out[1] = g;\n        out[2] = b;\n        out[3] = a;\n        return out;\n    }\n    function copyRgba(out, a) {\n        out[0] = a[0];\n        out[1] = a[1];\n        out[2] = a[2];\n        out[3] = a[3];\n        return out;\n    }\n    var colorCache = new LRU(20);\n    var lastRemovedArr = null;\n    function putToCache(colorStr, rgbaArr) {\n        if (lastRemovedArr) {\n            copyRgba(lastRemovedArr, rgbaArr);\n        }\n        lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));\n    }\n    function parse(colorStr, rgbaArr) {\n        if (!colorStr) {\n            return;\n        }\n        rgbaArr = rgbaArr || [];\n        var cached = colorCache.get(colorStr);\n        if (cached) {\n            return copyRgba(rgbaArr, cached);\n        }\n        colorStr = colorStr + '';\n        var str = colorStr.replace(/ /g, '').toLowerCase();\n        if (str in kCSSColorTable) {\n            copyRgba(rgbaArr, kCSSColorTable[str]);\n            putToCache(colorStr, rgbaArr);\n            return rgbaArr;\n        }\n        var strLen = str.length;\n        if (str.charAt(0) === '#') {\n            if (strLen === 4 || strLen === 5) {\n                var iv = parseInt(str.slice(1, 4), 16);\n                if (!(iv >= 0 && iv <= 0xfff)) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                setRgba(rgbaArr, ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), (iv & 0xf0) | ((iv & 0xf0) >> 4), (iv & 0xf) | ((iv & 0xf) << 4), strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            }\n            else if (strLen === 7 || strLen === 9) {\n                var iv = parseInt(str.slice(1, 7), 16);\n                if (!(iv >= 0 && iv <= 0xffffff)) {\n                    setRgba(rgbaArr, 0, 0, 0, 1);\n                    return;\n                }\n                setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1);\n                putToCache(colorStr, rgbaArr);\n                return rgbaArr;\n            }\n            return;\n        }\n        var op = str.indexOf('(');\n        var ep = str.indexOf(')');\n        if (op !== -1 && ep + 1 === strLen) {\n            var fname = str.substr(0, op);\n            var params = str.substr(op + 1, ep - (op + 1)).split(',');\n            var alpha = 1;\n            switch (fname) {\n                case 'rgba':\n                    if (params.length !== 4) {\n                        return params.length === 3\n                            ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1)\n                            : setRgba(rgbaArr, 0, 0, 0, 1);\n                    }\n                    alpha = parseCssFloat(params.pop());\n                case 'rgb':\n                    if (params.length >= 3) {\n                        setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), params.length === 3 ? alpha : parseCssFloat(params[3]));\n                        putToCache(colorStr, rgbaArr);\n                        return rgbaArr;\n                    }\n                    else {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                case 'hsla':\n                    if (params.length !== 4) {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                    params[3] = parseCssFloat(params[3]);\n                    hsla2rgba(params, rgbaArr);\n                    putToCache(colorStr, rgbaArr);\n                    return rgbaArr;\n                case 'hsl':\n                    if (params.length !== 3) {\n                        setRgba(rgbaArr, 0, 0, 0, 1);\n                        return;\n                    }\n                    hsla2rgba(params, rgbaArr);\n                    putToCache(colorStr, rgbaArr);\n                    return rgbaArr;\n                default:\n                    return;\n            }\n        }\n        setRgba(rgbaArr, 0, 0, 0, 1);\n        return;\n    }\n    function hsla2rgba(hsla, rgba) {\n        var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360;\n        var s = parseCssFloat(hsla[1]);\n        var l = parseCssFloat(hsla[2]);\n        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n        var m1 = l * 2 - m2;\n        rgba = rgba || [];\n        setRgba(rgba, clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), clampCssByte(cssHueToRgb(m1, m2, h) * 255), clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 1);\n        if (hsla.length === 4) {\n            rgba[3] = hsla[3];\n        }\n        return rgba;\n    }\n    function rgba2hsla(rgba) {\n        if (!rgba) {\n            return;\n        }\n        var R = rgba[0] / 255;\n        var G = rgba[1] / 255;\n        var B = rgba[2] / 255;\n        var vMin = Math.min(R, G, B);\n        var vMax = Math.max(R, G, B);\n        var delta = vMax - vMin;\n        var L = (vMax + vMin) / 2;\n        var H;\n        var S;\n        if (delta === 0) {\n            H = 0;\n            S = 0;\n        }\n        else {\n            if (L < 0.5) {\n                S = delta / (vMax + vMin);\n            }\n            else {\n                S = delta / (2 - vMax - vMin);\n            }\n            var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;\n            var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;\n            var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;\n            if (R === vMax) {\n                H = deltaB - deltaG;\n            }\n            else if (G === vMax) {\n                H = (1 / 3) + deltaR - deltaB;\n            }\n            else if (B === vMax) {\n                H = (2 / 3) + deltaG - deltaR;\n            }\n            if (H < 0) {\n                H += 1;\n            }\n            if (H > 1) {\n                H -= 1;\n            }\n        }\n        var hsla = [H * 360, S, L];\n        if (rgba[3] != null) {\n            hsla.push(rgba[3]);\n        }\n        return hsla;\n    }\n    function lift(color, level) {\n        var colorArr = parse(color);\n        if (colorArr) {\n            for (var i = 0; i < 3; i++) {\n                if (level < 0) {\n                    colorArr[i] = colorArr[i] * (1 - level) | 0;\n                }\n                else {\n                    colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;\n                }\n                if (colorArr[i] > 255) {\n                    colorArr[i] = 255;\n                }\n                else if (colorArr[i] < 0) {\n                    colorArr[i] = 0;\n                }\n            }\n            return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');\n        }\n    }\n    function toHex(color) {\n        var colorArr = parse(color);\n        if (colorArr) {\n            return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);\n        }\n    }\n    function fastLerp(normalizedValue, colors, out) {\n        if (!(colors && colors.length)\n            || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n            return;\n        }\n        out = out || [];\n        var value = normalizedValue * (colors.length - 1);\n        var leftIndex = Math.floor(value);\n        var rightIndex = Math.ceil(value);\n        var leftColor = colors[leftIndex];\n        var rightColor = colors[rightIndex];\n        var dv = value - leftIndex;\n        out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));\n        out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));\n        out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));\n        out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));\n        return out;\n    }\n    var fastMapToColor = fastLerp;\n    function lerp$1(normalizedValue, colors, fullOutput) {\n        if (!(colors && colors.length)\n            || !(normalizedValue >= 0 && normalizedValue <= 1)) {\n            return;\n        }\n        var value = normalizedValue * (colors.length - 1);\n        var leftIndex = Math.floor(value);\n        var rightIndex = Math.ceil(value);\n        var leftColor = parse(colors[leftIndex]);\n        var rightColor = parse(colors[rightIndex]);\n        var dv = value - leftIndex;\n        var color = stringify([\n            clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),\n            clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),\n            clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),\n            clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))\n        ], 'rgba');\n        return fullOutput\n            ? {\n                color: color,\n                leftIndex: leftIndex,\n                rightIndex: rightIndex,\n                value: value\n            }\n            : color;\n    }\n    var mapToColor = lerp$1;\n    function modifyHSL(color, h, s, l) {\n        var colorArr = parse(color);\n        if (color) {\n            colorArr = rgba2hsla(colorArr);\n            h != null && (colorArr[0] = clampCssAngle(h));\n            s != null && (colorArr[1] = parseCssFloat(s));\n            l != null && (colorArr[2] = parseCssFloat(l));\n            return stringify(hsla2rgba(colorArr), 'rgba');\n        }\n    }\n    function modifyAlpha(color, alpha) {\n        var colorArr = parse(color);\n        if (colorArr && alpha != null) {\n            colorArr[3] = clampCssFloat(alpha);\n            return stringify(colorArr, 'rgba');\n        }\n    }\n    function stringify(arrColor, type) {\n        if (!arrColor || !arrColor.length) {\n            return;\n        }\n        var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];\n        if (type === 'rgba' || type === 'hsva' || type === 'hsla') {\n            colorStr += ',' + arrColor[3];\n        }\n        return type + '(' + colorStr + ')';\n    }\n    function lum(color, backgroundLum) {\n        var arr = parse(color);\n        return arr\n            ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255\n                + (1 - arr[3]) * backgroundLum\n            : 0;\n    }\n    function random() {\n        return stringify([\n            Math.round(Math.random() * 255),\n            Math.round(Math.random() * 255),\n            Math.round(Math.random() * 255)\n        ], 'rgb');\n    }\n\n    var color = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        parse: parse,\n        lift: lift,\n        toHex: toHex,\n        fastLerp: fastLerp,\n        fastMapToColor: fastMapToColor,\n        lerp: lerp$1,\n        mapToColor: mapToColor,\n        modifyHSL: modifyHSL,\n        modifyAlpha: modifyAlpha,\n        stringify: stringify,\n        lum: lum,\n        random: random\n    });\n\n    function isLinearGradient(val) {\n        return val.type === 'linear';\n    }\n    function isRadialGradient(val) {\n        return val.type === 'radial';\n    }\n\n    var arraySlice = Array.prototype.slice;\n    function interpolateNumber(p0, p1, percent) {\n        return (p1 - p0) * percent + p0;\n    }\n    function interpolate1DArray(out, p0, p1, percent) {\n        var len = p0.length;\n        for (var i = 0; i < len; i++) {\n            out[i] = interpolateNumber(p0[i], p1[i], percent);\n        }\n        return out;\n    }\n    function interpolate2DArray(out, p0, p1, percent) {\n        var len = p0.length;\n        var len2 = len && p0[0].length;\n        for (var i = 0; i < len; i++) {\n            if (!out[i]) {\n                out[i] = [];\n            }\n            for (var j = 0; j < len2; j++) {\n                out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent);\n            }\n        }\n        return out;\n    }\n    function add1DArray(out, p0, p1, sign) {\n        var len = p0.length;\n        for (var i = 0; i < len; i++) {\n            out[i] = p0[i] + p1[i] * sign;\n        }\n        return out;\n    }\n    function add2DArray(out, p0, p1, sign) {\n        var len = p0.length;\n        var len2 = len && p0[0].length;\n        for (var i = 0; i < len; i++) {\n            if (!out[i]) {\n                out[i] = [];\n            }\n            for (var j = 0; j < len2; j++) {\n                out[i][j] = p0[i][j] + p1[i][j] * sign;\n            }\n        }\n        return out;\n    }\n    function fillColorStops(val0, val1) {\n        var len0 = val0.length;\n        var len1 = val1.length;\n        var shorterArr = len0 > len1 ? val1 : val0;\n        var shorterLen = Math.min(len0, len1);\n        var last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 };\n        for (var i = shorterLen; i < Math.max(len0, len1); i++) {\n            shorterArr.push({\n                offset: last.offset,\n                color: last.color.slice()\n            });\n        }\n    }\n    function fillArray(val0, val1, arrDim) {\n        var arr0 = val0;\n        var arr1 = val1;\n        if (!arr0.push || !arr1.push) {\n            return;\n        }\n        var arr0Len = arr0.length;\n        var arr1Len = arr1.length;\n        if (arr0Len !== arr1Len) {\n            var isPreviousLarger = arr0Len > arr1Len;\n            if (isPreviousLarger) {\n                arr0.length = arr1Len;\n            }\n            else {\n                for (var i = arr0Len; i < arr1Len; i++) {\n                    arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]));\n                }\n            }\n        }\n        var len2 = arr0[0] && arr0[0].length;\n        for (var i = 0; i < arr0.length; i++) {\n            if (arrDim === 1) {\n                if (isNaN(arr0[i])) {\n                    arr0[i] = arr1[i];\n                }\n            }\n            else {\n                for (var j = 0; j < len2; j++) {\n                    if (isNaN(arr0[i][j])) {\n                        arr0[i][j] = arr1[i][j];\n                    }\n                }\n            }\n        }\n    }\n    function cloneValue(value) {\n        if (isArrayLike(value)) {\n            var len = value.length;\n            if (isArrayLike(value[0])) {\n                var ret = [];\n                for (var i = 0; i < len; i++) {\n                    ret.push(arraySlice.call(value[i]));\n                }\n                return ret;\n            }\n            return arraySlice.call(value);\n        }\n        return value;\n    }\n    function rgba2String(rgba) {\n        rgba[0] = Math.floor(rgba[0]) || 0;\n        rgba[1] = Math.floor(rgba[1]) || 0;\n        rgba[2] = Math.floor(rgba[2]) || 0;\n        rgba[3] = rgba[3] == null ? 1 : rgba[3];\n        return 'rgba(' + rgba.join(',') + ')';\n    }\n    function guessArrayDim(value) {\n        return isArrayLike(value && value[0]) ? 2 : 1;\n    }\n    var VALUE_TYPE_NUMBER = 0;\n    var VALUE_TYPE_1D_ARRAY = 1;\n    var VALUE_TYPE_2D_ARRAY = 2;\n    var VALUE_TYPE_COLOR = 3;\n    var VALUE_TYPE_LINEAR_GRADIENT = 4;\n    var VALUE_TYPE_RADIAL_GRADIENT = 5;\n    var VALUE_TYPE_UNKOWN = 6;\n    function isGradientValueType(valType) {\n        return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT;\n    }\n    function isArrayValueType(valType) {\n        return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY;\n    }\n    var tmpRgba = [0, 0, 0, 0];\n    var Track = (function () {\n        function Track(propName) {\n            this.keyframes = [];\n            this.discrete = false;\n            this._invalid = false;\n            this._needsSort = false;\n            this._lastFr = 0;\n            this._lastFrP = 0;\n            this.propName = propName;\n        }\n        Track.prototype.isFinished = function () {\n            return this._finished;\n        };\n        Track.prototype.setFinished = function () {\n            this._finished = true;\n            if (this._additiveTrack) {\n                this._additiveTrack.setFinished();\n            }\n        };\n        Track.prototype.needsAnimate = function () {\n            return this.keyframes.length >= 1;\n        };\n        Track.prototype.getAdditiveTrack = function () {\n            return this._additiveTrack;\n        };\n        Track.prototype.addKeyframe = function (time, rawValue, easing) {\n            this._needsSort = true;\n            var keyframes = this.keyframes;\n            var len = keyframes.length;\n            var discrete = false;\n            var valType = VALUE_TYPE_UNKOWN;\n            var value = rawValue;\n            if (isArrayLike(rawValue)) {\n                var arrayDim = guessArrayDim(rawValue);\n                valType = arrayDim;\n                if (arrayDim === 1 && !isNumber(rawValue[0])\n                    || arrayDim === 2 && !isNumber(rawValue[0][0])) {\n                    discrete = true;\n                }\n            }\n            else {\n                if (isNumber(rawValue) && !eqNaN(rawValue)) {\n                    valType = VALUE_TYPE_NUMBER;\n                }\n                else if (isString(rawValue)) {\n                    if (!isNaN(+rawValue)) {\n                        valType = VALUE_TYPE_NUMBER;\n                    }\n                    else {\n                        var colorArray = parse(rawValue);\n                        if (colorArray) {\n                            value = colorArray;\n                            valType = VALUE_TYPE_COLOR;\n                        }\n                    }\n                }\n                else if (isGradientObject(rawValue)) {\n                    var parsedGradient = extend({}, value);\n                    parsedGradient.colorStops = map(rawValue.colorStops, function (colorStop) { return ({\n                        offset: colorStop.offset,\n                        color: parse(colorStop.color)\n                    }); });\n                    if (isLinearGradient(rawValue)) {\n                        valType = VALUE_TYPE_LINEAR_GRADIENT;\n                    }\n                    else if (isRadialGradient(rawValue)) {\n                        valType = VALUE_TYPE_RADIAL_GRADIENT;\n                    }\n                    value = parsedGradient;\n                }\n            }\n            if (len === 0) {\n                this.valType = valType;\n            }\n            else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) {\n                discrete = true;\n            }\n            this.discrete = this.discrete || discrete;\n            var kf = {\n                time: time,\n                value: value,\n                rawValue: rawValue,\n                percent: 0\n            };\n            if (easing) {\n                kf.easing = easing;\n                kf.easingFunc = isFunction(easing)\n                    ? easing\n                    : easingFuncs[easing] || createCubicEasingFunc(easing);\n            }\n            keyframes.push(kf);\n            return kf;\n        };\n        Track.prototype.prepare = function (maxTime, additiveTrack) {\n            var kfs = this.keyframes;\n            if (this._needsSort) {\n                kfs.sort(function (a, b) {\n                    return a.time - b.time;\n                });\n            }\n            var valType = this.valType;\n            var kfsLen = kfs.length;\n            var lastKf = kfs[kfsLen - 1];\n            var isDiscrete = this.discrete;\n            var isArr = isArrayValueType(valType);\n            var isGradient = isGradientValueType(valType);\n            for (var i = 0; i < kfsLen; i++) {\n                var kf = kfs[i];\n                var value = kf.value;\n                var lastValue = lastKf.value;\n                kf.percent = kf.time / maxTime;\n                if (!isDiscrete) {\n                    if (isArr && i !== kfsLen - 1) {\n                        fillArray(value, lastValue, valType);\n                    }\n                    else if (isGradient) {\n                        fillColorStops(value.colorStops, lastValue.colorStops);\n                    }\n                }\n            }\n            if (!isDiscrete\n                && valType !== VALUE_TYPE_RADIAL_GRADIENT\n                && additiveTrack\n                && this.needsAnimate()\n                && additiveTrack.needsAnimate()\n                && valType === additiveTrack.valType\n                && !additiveTrack._finished) {\n                this._additiveTrack = additiveTrack;\n                var startValue = kfs[0].value;\n                for (var i = 0; i < kfsLen; i++) {\n                    if (valType === VALUE_TYPE_NUMBER) {\n                        kfs[i].additiveValue = kfs[i].value - startValue;\n                    }\n                    else if (valType === VALUE_TYPE_COLOR) {\n                        kfs[i].additiveValue =\n                            add1DArray([], kfs[i].value, startValue, -1);\n                    }\n                    else if (isArrayValueType(valType)) {\n                        kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY\n                            ? add1DArray([], kfs[i].value, startValue, -1)\n                            : add2DArray([], kfs[i].value, startValue, -1);\n                    }\n                }\n            }\n        };\n        Track.prototype.step = function (target, percent) {\n            if (this._finished) {\n                return;\n            }\n            if (this._additiveTrack && this._additiveTrack._finished) {\n                this._additiveTrack = null;\n            }\n            var isAdditive = this._additiveTrack != null;\n            var valueKey = isAdditive ? 'additiveValue' : 'value';\n            var valType = this.valType;\n            var keyframes = this.keyframes;\n            var kfsNum = keyframes.length;\n            var propName = this.propName;\n            var isValueColor = valType === VALUE_TYPE_COLOR;\n            var frameIdx;\n            var lastFrame = this._lastFr;\n            var mathMin = Math.min;\n            var frame;\n            var nextFrame;\n            if (kfsNum === 1) {\n                frame = nextFrame = keyframes[0];\n            }\n            else {\n                if (percent < 0) {\n                    frameIdx = 0;\n                }\n                else if (percent < this._lastFrP) {\n                    var start = mathMin(lastFrame + 1, kfsNum - 1);\n                    for (frameIdx = start; frameIdx >= 0; frameIdx--) {\n                        if (keyframes[frameIdx].percent <= percent) {\n                            break;\n                        }\n                    }\n                    frameIdx = mathMin(frameIdx, kfsNum - 2);\n                }\n                else {\n                    for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) {\n                        if (keyframes[frameIdx].percent > percent) {\n                            break;\n                        }\n                    }\n                    frameIdx = mathMin(frameIdx - 1, kfsNum - 2);\n                }\n                nextFrame = keyframes[frameIdx + 1];\n                frame = keyframes[frameIdx];\n            }\n            if (!(frame && nextFrame)) {\n                return;\n            }\n            this._lastFr = frameIdx;\n            this._lastFrP = percent;\n            var interval = (nextFrame.percent - frame.percent);\n            var w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1);\n            if (nextFrame.easingFunc) {\n                w = nextFrame.easingFunc(w);\n            }\n            var targetArr = isAdditive ? this._additiveValue\n                : (isValueColor ? tmpRgba : target[propName]);\n            if ((isArrayValueType(valType) || isValueColor) && !targetArr) {\n                targetArr = this._additiveValue = [];\n            }\n            if (this.discrete) {\n                target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue;\n            }\n            else if (isArrayValueType(valType)) {\n                valType === VALUE_TYPE_1D_ARRAY\n                    ? interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w)\n                    : interpolate2DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n            }\n            else if (isGradientValueType(valType)) {\n                var val = frame[valueKey];\n                var nextVal_1 = nextFrame[valueKey];\n                var isLinearGradient_1 = valType === VALUE_TYPE_LINEAR_GRADIENT;\n                target[propName] = {\n                    type: isLinearGradient_1 ? 'linear' : 'radial',\n                    x: interpolateNumber(val.x, nextVal_1.x, w),\n                    y: interpolateNumber(val.y, nextVal_1.y, w),\n                    colorStops: map(val.colorStops, function (colorStop, idx) {\n                        var nextColorStop = nextVal_1.colorStops[idx];\n                        return {\n                            offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w),\n                            color: rgba2String(interpolate1DArray([], colorStop.color, nextColorStop.color, w))\n                        };\n                    }),\n                    global: nextVal_1.global\n                };\n                if (isLinearGradient_1) {\n                    target[propName].x2 = interpolateNumber(val.x2, nextVal_1.x2, w);\n                    target[propName].y2 = interpolateNumber(val.y2, nextVal_1.y2, w);\n                }\n                else {\n                    target[propName].r = interpolateNumber(val.r, nextVal_1.r, w);\n                }\n            }\n            else if (isValueColor) {\n                interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w);\n                if (!isAdditive) {\n                    target[propName] = rgba2String(targetArr);\n                }\n            }\n            else {\n                var value = interpolateNumber(frame[valueKey], nextFrame[valueKey], w);\n                if (isAdditive) {\n                    this._additiveValue = value;\n                }\n                else {\n                    target[propName] = value;\n                }\n            }\n            if (isAdditive) {\n                this._addToTarget(target);\n            }\n        };\n        Track.prototype._addToTarget = function (target) {\n            var valType = this.valType;\n            var propName = this.propName;\n            var additiveValue = this._additiveValue;\n            if (valType === VALUE_TYPE_NUMBER) {\n                target[propName] = target[propName] + additiveValue;\n            }\n            else if (valType === VALUE_TYPE_COLOR) {\n                parse(target[propName], tmpRgba);\n                add1DArray(tmpRgba, tmpRgba, additiveValue, 1);\n                target[propName] = rgba2String(tmpRgba);\n            }\n            else if (valType === VALUE_TYPE_1D_ARRAY) {\n                add1DArray(target[propName], target[propName], additiveValue, 1);\n            }\n            else if (valType === VALUE_TYPE_2D_ARRAY) {\n                add2DArray(target[propName], target[propName], additiveValue, 1);\n            }\n        };\n        return Track;\n    }());\n    var Animator = (function () {\n        function Animator(target, loop, allowDiscreteAnimation, additiveTo) {\n            this._tracks = {};\n            this._trackKeys = [];\n            this._maxTime = 0;\n            this._started = 0;\n            this._clip = null;\n            this._target = target;\n            this._loop = loop;\n            if (loop && additiveTo) {\n                logError('Can\\' use additive animation on looped animation.');\n                return;\n            }\n            this._additiveAnimators = additiveTo;\n            this._allowDiscrete = allowDiscreteAnimation;\n        }\n        Animator.prototype.getMaxTime = function () {\n            return this._maxTime;\n        };\n        Animator.prototype.getDelay = function () {\n            return this._delay;\n        };\n        Animator.prototype.getLoop = function () {\n            return this._loop;\n        };\n        Animator.prototype.getTarget = function () {\n            return this._target;\n        };\n        Animator.prototype.changeTarget = function (target) {\n            this._target = target;\n        };\n        Animator.prototype.when = function (time, props, easing) {\n            return this.whenWithKeys(time, props, keys(props), easing);\n        };\n        Animator.prototype.whenWithKeys = function (time, props, propNames, easing) {\n            var tracks = this._tracks;\n            for (var i = 0; i < propNames.length; i++) {\n                var propName = propNames[i];\n                var track = tracks[propName];\n                if (!track) {\n                    track = tracks[propName] = new Track(propName);\n                    var initialValue = void 0;\n                    var additiveTrack = this._getAdditiveTrack(propName);\n                    if (additiveTrack) {\n                        var addtiveTrackKfs = additiveTrack.keyframes;\n                        var lastFinalKf = addtiveTrackKfs[addtiveTrackKfs.length - 1];\n                        initialValue = lastFinalKf && lastFinalKf.value;\n                        if (additiveTrack.valType === VALUE_TYPE_COLOR && initialValue) {\n                            initialValue = rgba2String(initialValue);\n                        }\n                    }\n                    else {\n                        initialValue = this._target[propName];\n                    }\n                    if (initialValue == null) {\n                        continue;\n                    }\n                    if (time > 0) {\n                        track.addKeyframe(0, cloneValue(initialValue), easing);\n                    }\n                    this._trackKeys.push(propName);\n                }\n                track.addKeyframe(time, cloneValue(props[propName]), easing);\n            }\n            this._maxTime = Math.max(this._maxTime, time);\n            return this;\n        };\n        Animator.prototype.pause = function () {\n            this._clip.pause();\n            this._paused = true;\n        };\n        Animator.prototype.resume = function () {\n            this._clip.resume();\n            this._paused = false;\n        };\n        Animator.prototype.isPaused = function () {\n            return !!this._paused;\n        };\n        Animator.prototype.duration = function (duration) {\n            this._maxTime = duration;\n            this._force = true;\n            return this;\n        };\n        Animator.prototype._doneCallback = function () {\n            this._setTracksFinished();\n            this._clip = null;\n            var doneList = this._doneCbs;\n            if (doneList) {\n                var len = doneList.length;\n                for (var i = 0; i < len; i++) {\n                    doneList[i].call(this);\n                }\n            }\n        };\n        Animator.prototype._abortedCallback = function () {\n            this._setTracksFinished();\n            var animation = this.animation;\n            var abortedList = this._abortedCbs;\n            if (animation) {\n                animation.removeClip(this._clip);\n            }\n            this._clip = null;\n            if (abortedList) {\n                for (var i = 0; i < abortedList.length; i++) {\n                    abortedList[i].call(this);\n                }\n            }\n        };\n        Animator.prototype._setTracksFinished = function () {\n            var tracks = this._tracks;\n            var tracksKeys = this._trackKeys;\n            for (var i = 0; i < tracksKeys.length; i++) {\n                tracks[tracksKeys[i]].setFinished();\n            }\n        };\n        Animator.prototype._getAdditiveTrack = function (trackName) {\n            var additiveTrack;\n            var additiveAnimators = this._additiveAnimators;\n            if (additiveAnimators) {\n                for (var i = 0; i < additiveAnimators.length; i++) {\n                    var track = additiveAnimators[i].getTrack(trackName);\n                    if (track) {\n                        additiveTrack = track;\n                    }\n                }\n            }\n            return additiveTrack;\n        };\n        Animator.prototype.start = function (easing) {\n            if (this._started > 0) {\n                return;\n            }\n            this._started = 1;\n            var self = this;\n            var tracks = [];\n            var maxTime = this._maxTime || 0;\n            for (var i = 0; i < this._trackKeys.length; i++) {\n                var propName = this._trackKeys[i];\n                var track = this._tracks[propName];\n                var additiveTrack = this._getAdditiveTrack(propName);\n                var kfs = track.keyframes;\n                var kfsNum = kfs.length;\n                track.prepare(maxTime, additiveTrack);\n                if (track.needsAnimate()) {\n                    if (!this._allowDiscrete && track.discrete) {\n                        var lastKf = kfs[kfsNum - 1];\n                        if (lastKf) {\n                            self._target[track.propName] = lastKf.rawValue;\n                        }\n                        track.setFinished();\n                    }\n                    else {\n                        tracks.push(track);\n                    }\n                }\n            }\n            if (tracks.length || this._force) {\n                var clip = new Clip({\n                    life: maxTime,\n                    loop: this._loop,\n                    delay: this._delay || 0,\n                    onframe: function (percent) {\n                        self._started = 2;\n                        var additiveAnimators = self._additiveAnimators;\n                        if (additiveAnimators) {\n                            var stillHasAdditiveAnimator = false;\n                            for (var i = 0; i < additiveAnimators.length; i++) {\n                                if (additiveAnimators[i]._clip) {\n                                    stillHasAdditiveAnimator = true;\n                                    break;\n                                }\n                            }\n                            if (!stillHasAdditiveAnimator) {\n                                self._additiveAnimators = null;\n                            }\n                        }\n                        for (var i = 0; i < tracks.length; i++) {\n                            tracks[i].step(self._target, percent);\n                        }\n                        var onframeList = self._onframeCbs;\n                        if (onframeList) {\n                            for (var i = 0; i < onframeList.length; i++) {\n                                onframeList[i](self._target, percent);\n                            }\n                        }\n                    },\n                    ondestroy: function () {\n                        self._doneCallback();\n                    }\n                });\n                this._clip = clip;\n                if (this.animation) {\n                    this.animation.addClip(clip);\n                }\n                if (easing) {\n                    clip.setEasing(easing);\n                }\n            }\n            else {\n                this._doneCallback();\n            }\n            return this;\n        };\n        Animator.prototype.stop = function (forwardToLast) {\n            if (!this._clip) {\n                return;\n            }\n            var clip = this._clip;\n            if (forwardToLast) {\n                clip.onframe(1);\n            }\n            this._abortedCallback();\n        };\n        Animator.prototype.delay = function (time) {\n            this._delay = time;\n            return this;\n        };\n        Animator.prototype.during = function (cb) {\n            if (cb) {\n                if (!this._onframeCbs) {\n                    this._onframeCbs = [];\n                }\n                this._onframeCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.done = function (cb) {\n            if (cb) {\n                if (!this._doneCbs) {\n                    this._doneCbs = [];\n                }\n                this._doneCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.aborted = function (cb) {\n            if (cb) {\n                if (!this._abortedCbs) {\n                    this._abortedCbs = [];\n                }\n                this._abortedCbs.push(cb);\n            }\n            return this;\n        };\n        Animator.prototype.getClip = function () {\n            return this._clip;\n        };\n        Animator.prototype.getTrack = function (propName) {\n            return this._tracks[propName];\n        };\n        Animator.prototype.getTracks = function () {\n            var _this = this;\n            return map(this._trackKeys, function (key) { return _this._tracks[key]; });\n        };\n        Animator.prototype.stopTracks = function (propNames, forwardToLast) {\n            if (!propNames.length || !this._clip) {\n                return true;\n            }\n            var tracks = this._tracks;\n            var tracksKeys = this._trackKeys;\n            for (var i = 0; i < propNames.length; i++) {\n                var track = tracks[propNames[i]];\n                if (track && !track.isFinished()) {\n                    if (forwardToLast) {\n                        track.step(this._target, 1);\n                    }\n                    else if (this._started === 1) {\n                        track.step(this._target, 0);\n                    }\n                    track.setFinished();\n                }\n            }\n            var allAborted = true;\n            for (var i = 0; i < tracksKeys.length; i++) {\n                if (!tracks[tracksKeys[i]].isFinished()) {\n                    allAborted = false;\n                    break;\n                }\n            }\n            if (allAborted) {\n                this._abortedCallback();\n            }\n            return allAborted;\n        };\n        Animator.prototype.saveTo = function (target, trackKeys, firstOrLast) {\n            if (!target) {\n                return;\n            }\n            trackKeys = trackKeys || this._trackKeys;\n            for (var i = 0; i < trackKeys.length; i++) {\n                var propName = trackKeys[i];\n                var track = this._tracks[propName];\n                if (!track || track.isFinished()) {\n                    continue;\n                }\n                var kfs = track.keyframes;\n                var kf = kfs[firstOrLast ? 0 : kfs.length - 1];\n                if (kf) {\n                    target[propName] = cloneValue(kf.rawValue);\n                }\n            }\n        };\n        Animator.prototype.__changeFinalValue = function (finalProps, trackKeys) {\n            trackKeys = trackKeys || keys(finalProps);\n            for (var i = 0; i < trackKeys.length; i++) {\n                var propName = trackKeys[i];\n                var track = this._tracks[propName];\n                if (!track) {\n                    continue;\n                }\n                var kfs = track.keyframes;\n                if (kfs.length > 1) {\n                    var lastKf = kfs.pop();\n                    track.addKeyframe(lastKf.time, finalProps[propName]);\n                    track.prepare(this._maxTime, track.getAdditiveTrack());\n                }\n            }\n        };\n        return Animator;\n    }());\n\n    function getTime() {\n        return new Date().getTime();\n    }\n    var Animation = (function (_super) {\n        __extends(Animation, _super);\n        function Animation(opts) {\n            var _this = _super.call(this) || this;\n            _this._running = false;\n            _this._time = 0;\n            _this._pausedTime = 0;\n            _this._pauseStart = 0;\n            _this._paused = false;\n            opts = opts || {};\n            _this.stage = opts.stage || {};\n            return _this;\n        }\n        Animation.prototype.addClip = function (clip) {\n            if (clip.animation) {\n                this.removeClip(clip);\n            }\n            if (!this._head) {\n                this._head = this._tail = clip;\n            }\n            else {\n                this._tail.next = clip;\n                clip.prev = this._tail;\n                clip.next = null;\n                this._tail = clip;\n            }\n            clip.animation = this;\n        };\n        Animation.prototype.addAnimator = function (animator) {\n            animator.animation = this;\n            var clip = animator.getClip();\n            if (clip) {\n                this.addClip(clip);\n            }\n        };\n        Animation.prototype.removeClip = function (clip) {\n            if (!clip.animation) {\n                return;\n            }\n            var prev = clip.prev;\n            var next = clip.next;\n            if (prev) {\n                prev.next = next;\n            }\n            else {\n                this._head = next;\n            }\n            if (next) {\n                next.prev = prev;\n            }\n            else {\n                this._tail = prev;\n            }\n            clip.next = clip.prev = clip.animation = null;\n        };\n        Animation.prototype.removeAnimator = function (animator) {\n            var clip = animator.getClip();\n            if (clip) {\n                this.removeClip(clip);\n            }\n            animator.animation = null;\n        };\n        Animation.prototype.update = function (notTriggerFrameAndStageUpdate) {\n            var time = getTime() - this._pausedTime;\n            var delta = time - this._time;\n            var clip = this._head;\n            while (clip) {\n                var nextClip = clip.next;\n                var finished = clip.step(time, delta);\n                if (finished) {\n                    clip.ondestroy();\n                    this.removeClip(clip);\n                    clip = nextClip;\n                }\n                else {\n                    clip = nextClip;\n                }\n            }\n            this._time = time;\n            if (!notTriggerFrameAndStageUpdate) {\n                this.trigger('frame', delta);\n                this.stage.update && this.stage.update();\n            }\n        };\n        Animation.prototype._startLoop = function () {\n            var self = this;\n            this._running = true;\n            function step() {\n                if (self._running) {\n                    requestAnimationFrame$1(step);\n                    !self._paused && self.update();\n                }\n            }\n            requestAnimationFrame$1(step);\n        };\n        Animation.prototype.start = function () {\n            if (this._running) {\n                return;\n            }\n            this._time = getTime();\n            this._pausedTime = 0;\n            this._startLoop();\n        };\n        Animation.prototype.stop = function () {\n            this._running = false;\n        };\n        Animation.prototype.pause = function () {\n            if (!this._paused) {\n                this._pauseStart = getTime();\n                this._paused = true;\n            }\n        };\n        Animation.prototype.resume = function () {\n            if (this._paused) {\n                this._pausedTime += getTime() - this._pauseStart;\n                this._paused = false;\n            }\n        };\n        Animation.prototype.clear = function () {\n            var clip = this._head;\n            while (clip) {\n                var nextClip = clip.next;\n                clip.prev = clip.next = clip.animation = null;\n                clip = nextClip;\n            }\n            this._head = this._tail = null;\n        };\n        Animation.prototype.isFinished = function () {\n            return this._head == null;\n        };\n        Animation.prototype.animate = function (target, options) {\n            options = options || {};\n            this.start();\n            var animator = new Animator(target, options.loop);\n            this.addAnimator(animator);\n            return animator;\n        };\n        return Animation;\n    }(Eventful));\n\n    var TOUCH_CLICK_DELAY = 300;\n    var globalEventSupported = env.domSupported;\n    var localNativeListenerNames = (function () {\n        var mouseHandlerNames = [\n            'click', 'dblclick', 'mousewheel', 'wheel', 'mouseout',\n            'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n        ];\n        var touchHandlerNames = [\n            'touchstart', 'touchend', 'touchmove'\n        ];\n        var pointerEventNameMap = {\n            pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1\n        };\n        var pointerHandlerNames = map(mouseHandlerNames, function (name) {\n            var nm = name.replace('mouse', 'pointer');\n            return pointerEventNameMap.hasOwnProperty(nm) ? nm : name;\n        });\n        return {\n            mouse: mouseHandlerNames,\n            touch: touchHandlerNames,\n            pointer: pointerHandlerNames\n        };\n    })();\n    var globalNativeListenerNames = {\n        mouse: ['mousemove', 'mouseup'],\n        pointer: ['pointermove', 'pointerup']\n    };\n    var wheelEventSupported = false;\n    function isPointerFromTouch(event) {\n        var pointerType = event.pointerType;\n        return pointerType === 'pen' || pointerType === 'touch';\n    }\n    function setTouchTimer(scope) {\n        scope.touching = true;\n        if (scope.touchTimer != null) {\n            clearTimeout(scope.touchTimer);\n            scope.touchTimer = null;\n        }\n        scope.touchTimer = setTimeout(function () {\n            scope.touching = false;\n            scope.touchTimer = null;\n        }, 700);\n    }\n    function markTouch(event) {\n        event && (event.zrByTouch = true);\n    }\n    function normalizeGlobalEvent(instance, event) {\n        return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true);\n    }\n    function isLocalEl(instance, el) {\n        var elTmp = el;\n        var isLocal = false;\n        while (elTmp && elTmp.nodeType !== 9\n            && !(isLocal = elTmp.domBelongToZr\n                || (elTmp !== el && elTmp === instance.painterRoot))) {\n            elTmp = elTmp.parentNode;\n        }\n        return isLocal;\n    }\n    var FakeGlobalEvent = (function () {\n        function FakeGlobalEvent(instance, event) {\n            this.stopPropagation = noop;\n            this.stopImmediatePropagation = noop;\n            this.preventDefault = noop;\n            this.type = event.type;\n            this.target = this.currentTarget = instance.dom;\n            this.pointerType = event.pointerType;\n            this.clientX = event.clientX;\n            this.clientY = event.clientY;\n        }\n        return FakeGlobalEvent;\n    }());\n    var localDOMHandlers = {\n        mousedown: function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.__mayPointerCapture = [event.zrX, event.zrY];\n            this.trigger('mousedown', event);\n        },\n        mousemove: function (event) {\n            event = normalizeEvent(this.dom, event);\n            var downPoint = this.__mayPointerCapture;\n            if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) {\n                this.__togglePointerCapture(true);\n            }\n            this.trigger('mousemove', event);\n        },\n        mouseup: function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.__togglePointerCapture(false);\n            this.trigger('mouseup', event);\n        },\n        mouseout: function (event) {\n            event = normalizeEvent(this.dom, event);\n            var element = event.toElement || event.relatedTarget;\n            if (!isLocalEl(this, element)) {\n                if (this.__pointerCapturing) {\n                    event.zrEventControl = 'no_globalout';\n                }\n                this.trigger('mouseout', event);\n            }\n        },\n        wheel: function (event) {\n            wheelEventSupported = true;\n            event = normalizeEvent(this.dom, event);\n            this.trigger('mousewheel', event);\n        },\n        mousewheel: function (event) {\n            if (wheelEventSupported) {\n                return;\n            }\n            event = normalizeEvent(this.dom, event);\n            this.trigger('mousewheel', event);\n        },\n        touchstart: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.__lastTouchMoment = new Date();\n            this.handler.processGesture(event, 'start');\n            localDOMHandlers.mousemove.call(this, event);\n            localDOMHandlers.mousedown.call(this, event);\n        },\n        touchmove: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.handler.processGesture(event, 'change');\n            localDOMHandlers.mousemove.call(this, event);\n        },\n        touchend: function (event) {\n            event = normalizeEvent(this.dom, event);\n            markTouch(event);\n            this.handler.processGesture(event, 'end');\n            localDOMHandlers.mouseup.call(this, event);\n            if (+new Date() - (+this.__lastTouchMoment) < TOUCH_CLICK_DELAY) {\n                localDOMHandlers.click.call(this, event);\n            }\n        },\n        pointerdown: function (event) {\n            localDOMHandlers.mousedown.call(this, event);\n        },\n        pointermove: function (event) {\n            if (!isPointerFromTouch(event)) {\n                localDOMHandlers.mousemove.call(this, event);\n            }\n        },\n        pointerup: function (event) {\n            localDOMHandlers.mouseup.call(this, event);\n        },\n        pointerout: function (event) {\n            if (!isPointerFromTouch(event)) {\n                localDOMHandlers.mouseout.call(this, event);\n            }\n        }\n    };\n    each(['click', 'dblclick', 'contextmenu'], function (name) {\n        localDOMHandlers[name] = function (event) {\n            event = normalizeEvent(this.dom, event);\n            this.trigger(name, event);\n        };\n    });\n    var globalDOMHandlers = {\n        pointermove: function (event) {\n            if (!isPointerFromTouch(event)) {\n                globalDOMHandlers.mousemove.call(this, event);\n            }\n        },\n        pointerup: function (event) {\n            globalDOMHandlers.mouseup.call(this, event);\n        },\n        mousemove: function (event) {\n            this.trigger('mousemove', event);\n        },\n        mouseup: function (event) {\n            var pointerCaptureReleasing = this.__pointerCapturing;\n            this.__togglePointerCapture(false);\n            this.trigger('mouseup', event);\n            if (pointerCaptureReleasing) {\n                event.zrEventControl = 'only_globalout';\n                this.trigger('mouseout', event);\n            }\n        }\n    };\n    function mountLocalDOMEventListeners(instance, scope) {\n        var domHandlers = scope.domHandlers;\n        if (env.pointerEventsSupported) {\n            each(localNativeListenerNames.pointer, function (nativeEventName) {\n                mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                    domHandlers[nativeEventName].call(instance, event);\n                });\n            });\n        }\n        else {\n            if (env.touchEventsSupported) {\n                each(localNativeListenerNames.touch, function (nativeEventName) {\n                    mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                        domHandlers[nativeEventName].call(instance, event);\n                        setTouchTimer(scope);\n                    });\n                });\n            }\n            each(localNativeListenerNames.mouse, function (nativeEventName) {\n                mountSingleDOMEventListener(scope, nativeEventName, function (event) {\n                    event = getNativeEvent(event);\n                    if (!scope.touching) {\n                        domHandlers[nativeEventName].call(instance, event);\n                    }\n                });\n            });\n        }\n    }\n    function mountGlobalDOMEventListeners(instance, scope) {\n        if (env.pointerEventsSupported) {\n            each(globalNativeListenerNames.pointer, mount);\n        }\n        else if (!env.touchEventsSupported) {\n            each(globalNativeListenerNames.mouse, mount);\n        }\n        function mount(nativeEventName) {\n            function nativeEventListener(event) {\n                event = getNativeEvent(event);\n                if (!isLocalEl(instance, event.target)) {\n                    event = normalizeGlobalEvent(instance, event);\n                    scope.domHandlers[nativeEventName].call(instance, event);\n                }\n            }\n            mountSingleDOMEventListener(scope, nativeEventName, nativeEventListener, { capture: true });\n        }\n    }\n    function mountSingleDOMEventListener(scope, nativeEventName, listener, opt) {\n        scope.mounted[nativeEventName] = listener;\n        scope.listenerOpts[nativeEventName] = opt;\n        addEventListener(scope.domTarget, nativeEventName, listener, opt);\n    }\n    function unmountDOMEventListeners(scope) {\n        var mounted = scope.mounted;\n        for (var nativeEventName in mounted) {\n            if (mounted.hasOwnProperty(nativeEventName)) {\n                removeEventListener(scope.domTarget, nativeEventName, mounted[nativeEventName], scope.listenerOpts[nativeEventName]);\n            }\n        }\n        scope.mounted = {};\n    }\n    var DOMHandlerScope = (function () {\n        function DOMHandlerScope(domTarget, domHandlers) {\n            this.mounted = {};\n            this.listenerOpts = {};\n            this.touching = false;\n            this.domTarget = domTarget;\n            this.domHandlers = domHandlers;\n        }\n        return DOMHandlerScope;\n    }());\n    var HandlerDomProxy = (function (_super) {\n        __extends(HandlerDomProxy, _super);\n        function HandlerDomProxy(dom, painterRoot) {\n            var _this = _super.call(this) || this;\n            _this.__pointerCapturing = false;\n            _this.dom = dom;\n            _this.painterRoot = painterRoot;\n            _this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers);\n            if (globalEventSupported) {\n                _this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers);\n            }\n            mountLocalDOMEventListeners(_this, _this._localHandlerScope);\n            return _this;\n        }\n        HandlerDomProxy.prototype.dispose = function () {\n            unmountDOMEventListeners(this._localHandlerScope);\n            if (globalEventSupported) {\n                unmountDOMEventListeners(this._globalHandlerScope);\n            }\n        };\n        HandlerDomProxy.prototype.setCursor = function (cursorStyle) {\n            this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');\n        };\n        HandlerDomProxy.prototype.__togglePointerCapture = function (isPointerCapturing) {\n            this.__mayPointerCapture = null;\n            if (globalEventSupported\n                && ((+this.__pointerCapturing) ^ (+isPointerCapturing))) {\n                this.__pointerCapturing = isPointerCapturing;\n                var globalHandlerScope = this._globalHandlerScope;\n                isPointerCapturing\n                    ? mountGlobalDOMEventListeners(this, globalHandlerScope)\n                    : unmountDOMEventListeners(globalHandlerScope);\n            }\n        };\n        return HandlerDomProxy;\n    }(Eventful));\n\n    var dpr = 1;\n    if (env.hasGlobalWindow) {\n        dpr = Math.max(window.devicePixelRatio\n            || (window.screen && window.screen.deviceXDPI / window.screen.logicalXDPI)\n            || 1, 1);\n    }\n    var devicePixelRatio = dpr;\n    var DARK_MODE_THRESHOLD = 0.4;\n    var DARK_LABEL_COLOR = '#333';\n    var LIGHT_LABEL_COLOR = '#ccc';\n    var LIGHTER_LABEL_COLOR = '#eee';\n\n    var mIdentity = identity;\n    var EPSILON$1 = 5e-5;\n    function isNotAroundZero$1(val) {\n        return val > EPSILON$1 || val < -EPSILON$1;\n    }\n    var scaleTmp = [];\n    var tmpTransform = [];\n    var originTransform = create$1();\n    var abs = Math.abs;\n    var Transformable = (function () {\n        function Transformable() {\n        }\n        Transformable.prototype.getLocalTransform = function (m) {\n            return Transformable.getLocalTransform(this, m);\n        };\n        Transformable.prototype.setPosition = function (arr) {\n            this.x = arr[0];\n            this.y = arr[1];\n        };\n        Transformable.prototype.setScale = function (arr) {\n            this.scaleX = arr[0];\n            this.scaleY = arr[1];\n        };\n        Transformable.prototype.setSkew = function (arr) {\n            this.skewX = arr[0];\n            this.skewY = arr[1];\n        };\n        Transformable.prototype.setOrigin = function (arr) {\n            this.originX = arr[0];\n            this.originY = arr[1];\n        };\n        Transformable.prototype.needLocalTransform = function () {\n            return isNotAroundZero$1(this.rotation)\n                || isNotAroundZero$1(this.x)\n                || isNotAroundZero$1(this.y)\n                || isNotAroundZero$1(this.scaleX - 1)\n                || isNotAroundZero$1(this.scaleY - 1)\n                || isNotAroundZero$1(this.skewX)\n                || isNotAroundZero$1(this.skewY);\n        };\n        Transformable.prototype.updateTransform = function () {\n            var parentTransform = this.parent && this.parent.transform;\n            var needLocalTransform = this.needLocalTransform();\n            var m = this.transform;\n            if (!(needLocalTransform || parentTransform)) {\n                m && mIdentity(m);\n                return;\n            }\n            m = m || create$1();\n            if (needLocalTransform) {\n                this.getLocalTransform(m);\n            }\n            else {\n                mIdentity(m);\n            }\n            if (parentTransform) {\n                if (needLocalTransform) {\n                    mul$1(m, parentTransform, m);\n                }\n                else {\n                    copy$1(m, parentTransform);\n                }\n            }\n            this.transform = m;\n            this._resolveGlobalScaleRatio(m);\n        };\n        Transformable.prototype._resolveGlobalScaleRatio = function (m) {\n            var globalScaleRatio = this.globalScaleRatio;\n            if (globalScaleRatio != null && globalScaleRatio !== 1) {\n                this.getGlobalScale(scaleTmp);\n                var relX = scaleTmp[0] < 0 ? -1 : 1;\n                var relY = scaleTmp[1] < 0 ? -1 : 1;\n                var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;\n                var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;\n                m[0] *= sx;\n                m[1] *= sx;\n                m[2] *= sy;\n                m[3] *= sy;\n            }\n            this.invTransform = this.invTransform || create$1();\n            invert(this.invTransform, m);\n        };\n        Transformable.prototype.getComputedTransform = function () {\n            var transformNode = this;\n            var ancestors = [];\n            while (transformNode) {\n                ancestors.push(transformNode);\n                transformNode = transformNode.parent;\n            }\n            while (transformNode = ancestors.pop()) {\n                transformNode.updateTransform();\n            }\n            return this.transform;\n        };\n        Transformable.prototype.setLocalTransform = function (m) {\n            if (!m) {\n                return;\n            }\n            var sx = m[0] * m[0] + m[1] * m[1];\n            var sy = m[2] * m[2] + m[3] * m[3];\n            var rotation = Math.atan2(m[1], m[0]);\n            var shearX = Math.PI / 2 + rotation - Math.atan2(m[3], m[2]);\n            sy = Math.sqrt(sy) * Math.cos(shearX);\n            sx = Math.sqrt(sx);\n            this.skewX = shearX;\n            this.skewY = 0;\n            this.rotation = -rotation;\n            this.x = +m[4];\n            this.y = +m[5];\n            this.scaleX = sx;\n            this.scaleY = sy;\n            this.originX = 0;\n            this.originY = 0;\n        };\n        Transformable.prototype.decomposeTransform = function () {\n            if (!this.transform) {\n                return;\n            }\n            var parent = this.parent;\n            var m = this.transform;\n            if (parent && parent.transform) {\n                mul$1(tmpTransform, parent.invTransform, m);\n                m = tmpTransform;\n            }\n            var ox = this.originX;\n            var oy = this.originY;\n            if (ox || oy) {\n                originTransform[4] = ox;\n                originTransform[5] = oy;\n                mul$1(tmpTransform, m, originTransform);\n                tmpTransform[4] -= ox;\n                tmpTransform[5] -= oy;\n                m = tmpTransform;\n            }\n            this.setLocalTransform(m);\n        };\n        Transformable.prototype.getGlobalScale = function (out) {\n            var m = this.transform;\n            out = out || [];\n            if (!m) {\n                out[0] = 1;\n                out[1] = 1;\n                return out;\n            }\n            out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);\n            out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);\n            if (m[0] < 0) {\n                out[0] = -out[0];\n            }\n            if (m[3] < 0) {\n                out[1] = -out[1];\n            }\n            return out;\n        };\n        Transformable.prototype.transformCoordToLocal = function (x, y) {\n            var v2 = [x, y];\n            var invTransform = this.invTransform;\n            if (invTransform) {\n                applyTransform(v2, v2, invTransform);\n            }\n            return v2;\n        };\n        Transformable.prototype.transformCoordToGlobal = function (x, y) {\n            var v2 = [x, y];\n            var transform = this.transform;\n            if (transform) {\n                applyTransform(v2, v2, transform);\n            }\n            return v2;\n        };\n        Transformable.prototype.getLineScale = function () {\n            var m = this.transform;\n            return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10\n                ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))\n                : 1;\n        };\n        Transformable.prototype.copyTransform = function (source) {\n            copyTransform(this, source);\n        };\n        Transformable.getLocalTransform = function (target, m) {\n            m = m || [];\n            var ox = target.originX || 0;\n            var oy = target.originY || 0;\n            var sx = target.scaleX;\n            var sy = target.scaleY;\n            var ax = target.anchorX;\n            var ay = target.anchorY;\n            var rotation = target.rotation || 0;\n            var x = target.x;\n            var y = target.y;\n            var skewX = target.skewX ? Math.tan(target.skewX) : 0;\n            var skewY = target.skewY ? Math.tan(-target.skewY) : 0;\n            if (ox || oy || ax || ay) {\n                var dx = ox + ax;\n                var dy = oy + ay;\n                m[4] = -dx * sx - skewX * dy * sy;\n                m[5] = -dy * sy - skewY * dx * sx;\n            }\n            else {\n                m[4] = m[5] = 0;\n            }\n            m[0] = sx;\n            m[3] = sy;\n            m[1] = skewY * sx;\n            m[2] = skewX * sy;\n            rotation && rotate(m, m, rotation);\n            m[4] += ox + x;\n            m[5] += oy + y;\n            return m;\n        };\n        Transformable.initDefaultProps = (function () {\n            var proto = Transformable.prototype;\n            proto.scaleX =\n                proto.scaleY =\n                    proto.globalScaleRatio = 1;\n            proto.x =\n                proto.y =\n                    proto.originX =\n                        proto.originY =\n                            proto.skewX =\n                                proto.skewY =\n                                    proto.rotation =\n                                        proto.anchorX =\n                                            proto.anchorY = 0;\n        })();\n        return Transformable;\n    }());\n    var TRANSFORMABLE_PROPS = [\n        'x', 'y', 'originX', 'originY', 'anchorX', 'anchorY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY'\n    ];\n    function copyTransform(target, source) {\n        for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {\n            var propName = TRANSFORMABLE_PROPS[i];\n            target[propName] = source[propName];\n        }\n    }\n\n    var textWidthCache = {};\n    function getWidth(text, font) {\n        font = font || DEFAULT_FONT;\n        var cacheOfFont = textWidthCache[font];\n        if (!cacheOfFont) {\n            cacheOfFont = textWidthCache[font] = new LRU(500);\n        }\n        var width = cacheOfFont.get(text);\n        if (width == null) {\n            width = platformApi.measureText(text, font).width;\n            cacheOfFont.put(text, width);\n        }\n        return width;\n    }\n    function innerGetBoundingRect(text, font, textAlign, textBaseline) {\n        var width = getWidth(text, font);\n        var height = getLineHeight(font);\n        var x = adjustTextX(0, width, textAlign);\n        var y = adjustTextY(0, height, textBaseline);\n        var rect = new BoundingRect(x, y, width, height);\n        return rect;\n    }\n    function getBoundingRect(text, font, textAlign, textBaseline) {\n        var textLines = ((text || '') + '').split('\\n');\n        var len = textLines.length;\n        if (len === 1) {\n            return innerGetBoundingRect(textLines[0], font, textAlign, textBaseline);\n        }\n        else {\n            var uniondRect = new BoundingRect(0, 0, 0, 0);\n            for (var i = 0; i < textLines.length; i++) {\n                var rect = innerGetBoundingRect(textLines[i], font, textAlign, textBaseline);\n                i === 0 ? uniondRect.copy(rect) : uniondRect.union(rect);\n            }\n            return uniondRect;\n        }\n    }\n    function adjustTextX(x, width, textAlign) {\n        if (textAlign === 'right') {\n            x -= width;\n        }\n        else if (textAlign === 'center') {\n            x -= width / 2;\n        }\n        return x;\n    }\n    function adjustTextY(y, height, verticalAlign) {\n        if (verticalAlign === 'middle') {\n            y -= height / 2;\n        }\n        else if (verticalAlign === 'bottom') {\n            y -= height;\n        }\n        return y;\n    }\n    function getLineHeight(font) {\n        return getWidth('国', font);\n    }\n    function parsePercent(value, maxValue) {\n        if (typeof value === 'string') {\n            if (value.lastIndexOf('%') >= 0) {\n                return parseFloat(value) / 100 * maxValue;\n            }\n            return parseFloat(value);\n        }\n        return value;\n    }\n    function calculateTextPosition(out, opts, rect) {\n        var textPosition = opts.position || 'inside';\n        var distance = opts.distance != null ? opts.distance : 5;\n        var height = rect.height;\n        var width = rect.width;\n        var halfHeight = height / 2;\n        var x = rect.x;\n        var y = rect.y;\n        var textAlign = 'left';\n        var textVerticalAlign = 'top';\n        if (textPosition instanceof Array) {\n            x += parsePercent(textPosition[0], rect.width);\n            y += parsePercent(textPosition[1], rect.height);\n            textAlign = null;\n            textVerticalAlign = null;\n        }\n        else {\n            switch (textPosition) {\n                case 'left':\n                    x -= distance;\n                    y += halfHeight;\n                    textAlign = 'right';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'right':\n                    x += distance + width;\n                    y += halfHeight;\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'top':\n                    x += width / 2;\n                    y -= distance;\n                    textAlign = 'center';\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'bottom':\n                    x += width / 2;\n                    y += height + distance;\n                    textAlign = 'center';\n                    break;\n                case 'inside':\n                    x += width / 2;\n                    y += halfHeight;\n                    textAlign = 'center';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideLeft':\n                    x += distance;\n                    y += halfHeight;\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideRight':\n                    x += width - distance;\n                    y += halfHeight;\n                    textAlign = 'right';\n                    textVerticalAlign = 'middle';\n                    break;\n                case 'insideTop':\n                    x += width / 2;\n                    y += distance;\n                    textAlign = 'center';\n                    break;\n                case 'insideBottom':\n                    x += width / 2;\n                    y += height - distance;\n                    textAlign = 'center';\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'insideTopLeft':\n                    x += distance;\n                    y += distance;\n                    break;\n                case 'insideTopRight':\n                    x += width - distance;\n                    y += distance;\n                    textAlign = 'right';\n                    break;\n                case 'insideBottomLeft':\n                    x += distance;\n                    y += height - distance;\n                    textVerticalAlign = 'bottom';\n                    break;\n                case 'insideBottomRight':\n                    x += width - distance;\n                    y += height - distance;\n                    textAlign = 'right';\n                    textVerticalAlign = 'bottom';\n                    break;\n            }\n        }\n        out = out || {};\n        out.x = x;\n        out.y = y;\n        out.align = textAlign;\n        out.verticalAlign = textVerticalAlign;\n        return out;\n    }\n\n    var PRESERVED_NORMAL_STATE = '__zr_normal__';\n    var PRIMARY_STATES_KEYS = TRANSFORMABLE_PROPS.concat(['ignore']);\n    var DEFAULT_ANIMATABLE_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) {\n        obj[key] = true;\n        return obj;\n    }, { ignore: false });\n    var tmpTextPosCalcRes = {};\n    var tmpBoundingRect = new BoundingRect(0, 0, 0, 0);\n    var Element = (function () {\n        function Element(props) {\n            this.id = guid();\n            this.animators = [];\n            this.currentStates = [];\n            this.states = {};\n            this._init(props);\n        }\n        Element.prototype._init = function (props) {\n            this.attr(props);\n        };\n        Element.prototype.drift = function (dx, dy, e) {\n            switch (this.draggable) {\n                case 'horizontal':\n                    dy = 0;\n                    break;\n                case 'vertical':\n                    dx = 0;\n                    break;\n            }\n            var m = this.transform;\n            if (!m) {\n                m = this.transform = [1, 0, 0, 1, 0, 0];\n            }\n            m[4] += dx;\n            m[5] += dy;\n            this.decomposeTransform();\n            this.markRedraw();\n        };\n        Element.prototype.beforeUpdate = function () { };\n        Element.prototype.afterUpdate = function () { };\n        Element.prototype.update = function () {\n            this.updateTransform();\n            if (this.__dirty) {\n                this.updateInnerText();\n            }\n        };\n        Element.prototype.updateInnerText = function (forceUpdate) {\n            var textEl = this._textContent;\n            if (textEl && (!textEl.ignore || forceUpdate)) {\n                if (!this.textConfig) {\n                    this.textConfig = {};\n                }\n                var textConfig = this.textConfig;\n                var isLocal = textConfig.local;\n                var innerTransformable = textEl.innerTransformable;\n                var textAlign = void 0;\n                var textVerticalAlign = void 0;\n                var textStyleChanged = false;\n                innerTransformable.parent = isLocal ? this : null;\n                var innerOrigin = false;\n                innerTransformable.copyTransform(textEl);\n                if (textConfig.position != null) {\n                    var layoutRect = tmpBoundingRect;\n                    if (textConfig.layoutRect) {\n                        layoutRect.copy(textConfig.layoutRect);\n                    }\n                    else {\n                        layoutRect.copy(this.getBoundingRect());\n                    }\n                    if (!isLocal) {\n                        layoutRect.applyTransform(this.transform);\n                    }\n                    if (this.calculateTextPosition) {\n                        this.calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                    }\n                    else {\n                        calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);\n                    }\n                    innerTransformable.x = tmpTextPosCalcRes.x;\n                    innerTransformable.y = tmpTextPosCalcRes.y;\n                    textAlign = tmpTextPosCalcRes.align;\n                    textVerticalAlign = tmpTextPosCalcRes.verticalAlign;\n                    var textOrigin = textConfig.origin;\n                    if (textOrigin && textConfig.rotation != null) {\n                        var relOriginX = void 0;\n                        var relOriginY = void 0;\n                        if (textOrigin === 'center') {\n                            relOriginX = layoutRect.width * 0.5;\n                            relOriginY = layoutRect.height * 0.5;\n                        }\n                        else {\n                            relOriginX = parsePercent(textOrigin[0], layoutRect.width);\n                            relOriginY = parsePercent(textOrigin[1], layoutRect.height);\n                        }\n                        innerOrigin = true;\n                        innerTransformable.originX = -innerTransformable.x + relOriginX + (isLocal ? 0 : layoutRect.x);\n                        innerTransformable.originY = -innerTransformable.y + relOriginY + (isLocal ? 0 : layoutRect.y);\n                    }\n                }\n                if (textConfig.rotation != null) {\n                    innerTransformable.rotation = textConfig.rotation;\n                }\n                var textOffset = textConfig.offset;\n                if (textOffset) {\n                    innerTransformable.x += textOffset[0];\n                    innerTransformable.y += textOffset[1];\n                    if (!innerOrigin) {\n                        innerTransformable.originX = -textOffset[0];\n                        innerTransformable.originY = -textOffset[1];\n                    }\n                }\n                var isInside = textConfig.inside == null\n                    ? (typeof textConfig.position === 'string' && textConfig.position.indexOf('inside') >= 0)\n                    : textConfig.inside;\n                var innerTextDefaultStyle = this._innerTextDefaultStyle || (this._innerTextDefaultStyle = {});\n                var textFill = void 0;\n                var textStroke = void 0;\n                var autoStroke = void 0;\n                if (isInside && this.canBeInsideText()) {\n                    textFill = textConfig.insideFill;\n                    textStroke = textConfig.insideStroke;\n                    if (textFill == null || textFill === 'auto') {\n                        textFill = this.getInsideTextFill();\n                    }\n                    if (textStroke == null || textStroke === 'auto') {\n                        textStroke = this.getInsideTextStroke(textFill);\n                        autoStroke = true;\n                    }\n                }\n                else {\n                    textFill = textConfig.outsideFill;\n                    textStroke = textConfig.outsideStroke;\n                    if (textFill == null || textFill === 'auto') {\n                        textFill = this.getOutsideFill();\n                    }\n                    if (textStroke == null || textStroke === 'auto') {\n                        textStroke = this.getOutsideStroke(textFill);\n                        autoStroke = true;\n                    }\n                }\n                textFill = textFill || '#000';\n                if (textFill !== innerTextDefaultStyle.fill\n                    || textStroke !== innerTextDefaultStyle.stroke\n                    || autoStroke !== innerTextDefaultStyle.autoStroke\n                    || textAlign !== innerTextDefaultStyle.align\n                    || textVerticalAlign !== innerTextDefaultStyle.verticalAlign) {\n                    textStyleChanged = true;\n                    innerTextDefaultStyle.fill = textFill;\n                    innerTextDefaultStyle.stroke = textStroke;\n                    innerTextDefaultStyle.autoStroke = autoStroke;\n                    innerTextDefaultStyle.align = textAlign;\n                    innerTextDefaultStyle.verticalAlign = textVerticalAlign;\n                    textEl.setDefaultTextStyle(innerTextDefaultStyle);\n                }\n                textEl.__dirty |= REDRAW_BIT;\n                if (textStyleChanged) {\n                    textEl.dirtyStyle(true);\n                }\n            }\n        };\n        Element.prototype.canBeInsideText = function () {\n            return true;\n        };\n        Element.prototype.getInsideTextFill = function () {\n            return '#fff';\n        };\n        Element.prototype.getInsideTextStroke = function (textFill) {\n            return '#000';\n        };\n        Element.prototype.getOutsideFill = function () {\n            return this.__zr && this.__zr.isDarkMode() ? LIGHT_LABEL_COLOR : DARK_LABEL_COLOR;\n        };\n        Element.prototype.getOutsideStroke = function (textFill) {\n            var backgroundColor = this.__zr && this.__zr.getBackgroundColor();\n            var colorArr = typeof backgroundColor === 'string' && parse(backgroundColor);\n            if (!colorArr) {\n                colorArr = [255, 255, 255, 1];\n            }\n            var alpha = colorArr[3];\n            var isDark = this.__zr.isDarkMode();\n            for (var i = 0; i < 3; i++) {\n                colorArr[i] = colorArr[i] * alpha + (isDark ? 0 : 255) * (1 - alpha);\n            }\n            colorArr[3] = 1;\n            return stringify(colorArr, 'rgba');\n        };\n        Element.prototype.traverse = function (cb, context) { };\n        Element.prototype.attrKV = function (key, value) {\n            if (key === 'textConfig') {\n                this.setTextConfig(value);\n            }\n            else if (key === 'textContent') {\n                this.setTextContent(value);\n            }\n            else if (key === 'clipPath') {\n                this.setClipPath(value);\n            }\n            else if (key === 'extra') {\n                this.extra = this.extra || {};\n                extend(this.extra, value);\n            }\n            else {\n                this[key] = value;\n            }\n        };\n        Element.prototype.hide = function () {\n            this.ignore = true;\n            this.markRedraw();\n        };\n        Element.prototype.show = function () {\n            this.ignore = false;\n            this.markRedraw();\n        };\n        Element.prototype.attr = function (keyOrObj, value) {\n            if (typeof keyOrObj === 'string') {\n                this.attrKV(keyOrObj, value);\n            }\n            else if (isObject(keyOrObj)) {\n                var obj = keyOrObj;\n                var keysArr = keys(obj);\n                for (var i = 0; i < keysArr.length; i++) {\n                    var key = keysArr[i];\n                    this.attrKV(key, keyOrObj[key]);\n                }\n            }\n            this.markRedraw();\n            return this;\n        };\n        Element.prototype.saveCurrentToNormalState = function (toState) {\n            this._innerSaveToNormal(toState);\n            var normalState = this._normalState;\n            for (var i = 0; i < this.animators.length; i++) {\n                var animator = this.animators[i];\n                var fromStateTransition = animator.__fromStateTransition;\n                if (animator.getLoop() || fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) {\n                    continue;\n                }\n                var targetName = animator.targetName;\n                var target = targetName\n                    ? normalState[targetName] : normalState;\n                animator.saveTo(target);\n            }\n        };\n        Element.prototype._innerSaveToNormal = function (toState) {\n            var normalState = this._normalState;\n            if (!normalState) {\n                normalState = this._normalState = {};\n            }\n            if (toState.textConfig && !normalState.textConfig) {\n                normalState.textConfig = this.textConfig;\n            }\n            this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS);\n        };\n        Element.prototype._savePrimaryToNormal = function (toState, normalState, primaryKeys) {\n            for (var i = 0; i < primaryKeys.length; i++) {\n                var key = primaryKeys[i];\n                if (toState[key] != null && !(key in normalState)) {\n                    normalState[key] = this[key];\n                }\n            }\n        };\n        Element.prototype.hasState = function () {\n            return this.currentStates.length > 0;\n        };\n        Element.prototype.getState = function (name) {\n            return this.states[name];\n        };\n        Element.prototype.ensureState = function (name) {\n            var states = this.states;\n            if (!states[name]) {\n                states[name] = {};\n            }\n            return states[name];\n        };\n        Element.prototype.clearStates = function (noAnimation) {\n            this.useState(PRESERVED_NORMAL_STATE, false, noAnimation);\n        };\n        Element.prototype.useState = function (stateName, keepCurrentStates, noAnimation, forceUseHoverLayer) {\n            var toNormalState = stateName === PRESERVED_NORMAL_STATE;\n            var hasStates = this.hasState();\n            if (!hasStates && toNormalState) {\n                return;\n            }\n            var currentStates = this.currentStates;\n            var animationCfg = this.stateTransition;\n            if (indexOf(currentStates, stateName) >= 0 && (keepCurrentStates || currentStates.length === 1)) {\n                return;\n            }\n            var state;\n            if (this.stateProxy && !toNormalState) {\n                state = this.stateProxy(stateName);\n            }\n            if (!state) {\n                state = (this.states && this.states[stateName]);\n            }\n            if (!state && !toNormalState) {\n                logError(\"State \" + stateName + \" not exists.\");\n                return;\n            }\n            if (!toNormalState) {\n                this.saveCurrentToNormalState(state);\n            }\n            var useHoverLayer = !!((state && state.hoverLayer) || forceUseHoverLayer);\n            if (useHoverLayer) {\n                this._toggleHoverLayerFlag(true);\n            }\n            this._applyStateObj(stateName, state, this._normalState, keepCurrentStates, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n            var textContent = this._textContent;\n            var textGuide = this._textGuide;\n            if (textContent) {\n                textContent.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n            }\n            if (textGuide) {\n                textGuide.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer);\n            }\n            if (toNormalState) {\n                this.currentStates = [];\n                this._normalState = {};\n            }\n            else {\n                if (!keepCurrentStates) {\n                    this.currentStates = [stateName];\n                }\n                else {\n                    this.currentStates.push(stateName);\n                }\n            }\n            this._updateAnimationTargets();\n            this.markRedraw();\n            if (!useHoverLayer && this.__inHover) {\n                this._toggleHoverLayerFlag(false);\n                this.__dirty &= ~REDRAW_BIT;\n            }\n            return state;\n        };\n        Element.prototype.useStates = function (states, noAnimation, forceUseHoverLayer) {\n            if (!states.length) {\n                this.clearStates();\n            }\n            else {\n                var stateObjects = [];\n                var currentStates = this.currentStates;\n                var len = states.length;\n                var notChange = len === currentStates.length;\n                if (notChange) {\n                    for (var i = 0; i < len; i++) {\n                        if (states[i] !== currentStates[i]) {\n                            notChange = false;\n                            break;\n                        }\n                    }\n                }\n                if (notChange) {\n                    return;\n                }\n                for (var i = 0; i < len; i++) {\n                    var stateName = states[i];\n                    var stateObj = void 0;\n                    if (this.stateProxy) {\n                        stateObj = this.stateProxy(stateName, states);\n                    }\n                    if (!stateObj) {\n                        stateObj = this.states[stateName];\n                    }\n                    if (stateObj) {\n                        stateObjects.push(stateObj);\n                    }\n                }\n                var lastStateObj = stateObjects[len - 1];\n                var useHoverLayer = !!((lastStateObj && lastStateObj.hoverLayer) || forceUseHoverLayer);\n                if (useHoverLayer) {\n                    this._toggleHoverLayerFlag(true);\n                }\n                var mergedState = this._mergeStates(stateObjects);\n                var animationCfg = this.stateTransition;\n                this.saveCurrentToNormalState(mergedState);\n                this._applyStateObj(states.join(','), mergedState, this._normalState, false, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg);\n                var textContent = this._textContent;\n                var textGuide = this._textGuide;\n                if (textContent) {\n                    textContent.useStates(states, noAnimation, useHoverLayer);\n                }\n                if (textGuide) {\n                    textGuide.useStates(states, noAnimation, useHoverLayer);\n                }\n                this._updateAnimationTargets();\n                this.currentStates = states.slice();\n                this.markRedraw();\n                if (!useHoverLayer && this.__inHover) {\n                    this._toggleHoverLayerFlag(false);\n                    this.__dirty &= ~REDRAW_BIT;\n                }\n            }\n        };\n        Element.prototype._updateAnimationTargets = function () {\n            for (var i = 0; i < this.animators.length; i++) {\n                var animator = this.animators[i];\n                if (animator.targetName) {\n                    animator.changeTarget(this[animator.targetName]);\n                }\n            }\n        };\n        Element.prototype.removeState = function (state) {\n            var idx = indexOf(this.currentStates, state);\n            if (idx >= 0) {\n                var currentStates = this.currentStates.slice();\n                currentStates.splice(idx, 1);\n                this.useStates(currentStates);\n            }\n        };\n        Element.prototype.replaceState = function (oldState, newState, forceAdd) {\n            var currentStates = this.currentStates.slice();\n            var idx = indexOf(currentStates, oldState);\n            var newStateExists = indexOf(currentStates, newState) >= 0;\n            if (idx >= 0) {\n                if (!newStateExists) {\n                    currentStates[idx] = newState;\n                }\n                else {\n                    currentStates.splice(idx, 1);\n                }\n            }\n            else if (forceAdd && !newStateExists) {\n                currentStates.push(newState);\n            }\n            this.useStates(currentStates);\n        };\n        Element.prototype.toggleState = function (state, enable) {\n            if (enable) {\n                this.useState(state, true);\n            }\n            else {\n                this.removeState(state);\n            }\n        };\n        Element.prototype._mergeStates = function (states) {\n            var mergedState = {};\n            var mergedTextConfig;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                extend(mergedState, state);\n                if (state.textConfig) {\n                    mergedTextConfig = mergedTextConfig || {};\n                    extend(mergedTextConfig, state.textConfig);\n                }\n            }\n            if (mergedTextConfig) {\n                mergedState.textConfig = mergedTextConfig;\n            }\n            return mergedState;\n        };\n        Element.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            if (state && state.textConfig) {\n                this.textConfig = extend({}, keepCurrentStates ? this.textConfig : normalState.textConfig);\n                extend(this.textConfig, state.textConfig);\n            }\n            else if (needsRestoreToNormal) {\n                if (normalState.textConfig) {\n                    this.textConfig = normalState.textConfig;\n                }\n            }\n            var transitionTarget = {};\n            var hasTransition = false;\n            for (var i = 0; i < PRIMARY_STATES_KEYS.length; i++) {\n                var key = PRIMARY_STATES_KEYS[i];\n                var propNeedsTransition = transition && DEFAULT_ANIMATABLE_MAP[key];\n                if (state && state[key] != null) {\n                    if (propNeedsTransition) {\n                        hasTransition = true;\n                        transitionTarget[key] = state[key];\n                    }\n                    else {\n                        this[key] = state[key];\n                    }\n                }\n                else if (needsRestoreToNormal) {\n                    if (normalState[key] != null) {\n                        if (propNeedsTransition) {\n                            hasTransition = true;\n                            transitionTarget[key] = normalState[key];\n                        }\n                        else {\n                            this[key] = normalState[key];\n                        }\n                    }\n                }\n            }\n            if (!transition) {\n                for (var i = 0; i < this.animators.length; i++) {\n                    var animator = this.animators[i];\n                    var targetName = animator.targetName;\n                    if (!animator.getLoop()) {\n                        animator.__changeFinalValue(targetName\n                            ? (state || normalState)[targetName]\n                            : (state || normalState));\n                    }\n                }\n            }\n            if (hasTransition) {\n                this._transitionState(stateName, transitionTarget, animationCfg);\n            }\n        };\n        Element.prototype._attachComponent = function (componentEl) {\n            if (componentEl.__zr && !componentEl.__hostTarget) {\n                if (\"development\" !== 'production') {\n                    throw new Error('Text element has been added to zrender.');\n                }\n                return;\n            }\n            if (componentEl === this) {\n                if (\"development\" !== 'production') {\n                    throw new Error('Recursive component attachment.');\n                }\n                return;\n            }\n            var zr = this.__zr;\n            if (zr) {\n                componentEl.addSelfToZr(zr);\n            }\n            componentEl.__zr = zr;\n            componentEl.__hostTarget = this;\n        };\n        Element.prototype._detachComponent = function (componentEl) {\n            if (componentEl.__zr) {\n                componentEl.removeSelfFromZr(componentEl.__zr);\n            }\n            componentEl.__zr = null;\n            componentEl.__hostTarget = null;\n        };\n        Element.prototype.getClipPath = function () {\n            return this._clipPath;\n        };\n        Element.prototype.setClipPath = function (clipPath) {\n            if (this._clipPath && this._clipPath !== clipPath) {\n                this.removeClipPath();\n            }\n            this._attachComponent(clipPath);\n            this._clipPath = clipPath;\n            this.markRedraw();\n        };\n        Element.prototype.removeClipPath = function () {\n            var clipPath = this._clipPath;\n            if (clipPath) {\n                this._detachComponent(clipPath);\n                this._clipPath = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.getTextContent = function () {\n            return this._textContent;\n        };\n        Element.prototype.setTextContent = function (textEl) {\n            var previousTextContent = this._textContent;\n            if (previousTextContent === textEl) {\n                return;\n            }\n            if (previousTextContent && previousTextContent !== textEl) {\n                this.removeTextContent();\n            }\n            if (\"development\" !== 'production') {\n                if (textEl.__zr && !textEl.__hostTarget) {\n                    throw new Error('Text element has been added to zrender.');\n                }\n            }\n            textEl.innerTransformable = new Transformable();\n            this._attachComponent(textEl);\n            this._textContent = textEl;\n            this.markRedraw();\n        };\n        Element.prototype.setTextConfig = function (cfg) {\n            if (!this.textConfig) {\n                this.textConfig = {};\n            }\n            extend(this.textConfig, cfg);\n            this.markRedraw();\n        };\n        Element.prototype.removeTextConfig = function () {\n            this.textConfig = null;\n            this.markRedraw();\n        };\n        Element.prototype.removeTextContent = function () {\n            var textEl = this._textContent;\n            if (textEl) {\n                textEl.innerTransformable = null;\n                this._detachComponent(textEl);\n                this._textContent = null;\n                this._innerTextDefaultStyle = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.getTextGuideLine = function () {\n            return this._textGuide;\n        };\n        Element.prototype.setTextGuideLine = function (guideLine) {\n            if (this._textGuide && this._textGuide !== guideLine) {\n                this.removeTextGuideLine();\n            }\n            this._attachComponent(guideLine);\n            this._textGuide = guideLine;\n            this.markRedraw();\n        };\n        Element.prototype.removeTextGuideLine = function () {\n            var textGuide = this._textGuide;\n            if (textGuide) {\n                this._detachComponent(textGuide);\n                this._textGuide = null;\n                this.markRedraw();\n            }\n        };\n        Element.prototype.markRedraw = function () {\n            this.__dirty |= REDRAW_BIT;\n            var zr = this.__zr;\n            if (zr) {\n                if (this.__inHover) {\n                    zr.refreshHover();\n                }\n                else {\n                    zr.refresh();\n                }\n            }\n            if (this.__hostTarget) {\n                this.__hostTarget.markRedraw();\n            }\n        };\n        Element.prototype.dirty = function () {\n            this.markRedraw();\n        };\n        Element.prototype._toggleHoverLayerFlag = function (inHover) {\n            this.__inHover = inHover;\n            var textContent = this._textContent;\n            var textGuide = this._textGuide;\n            if (textContent) {\n                textContent.__inHover = inHover;\n            }\n            if (textGuide) {\n                textGuide.__inHover = inHover;\n            }\n        };\n        Element.prototype.addSelfToZr = function (zr) {\n            if (this.__zr === zr) {\n                return;\n            }\n            this.__zr = zr;\n            var animators = this.animators;\n            if (animators) {\n                for (var i = 0; i < animators.length; i++) {\n                    zr.animation.addAnimator(animators[i]);\n                }\n            }\n            if (this._clipPath) {\n                this._clipPath.addSelfToZr(zr);\n            }\n            if (this._textContent) {\n                this._textContent.addSelfToZr(zr);\n            }\n            if (this._textGuide) {\n                this._textGuide.addSelfToZr(zr);\n            }\n        };\n        Element.prototype.removeSelfFromZr = function (zr) {\n            if (!this.__zr) {\n                return;\n            }\n            this.__zr = null;\n            var animators = this.animators;\n            if (animators) {\n                for (var i = 0; i < animators.length; i++) {\n                    zr.animation.removeAnimator(animators[i]);\n                }\n            }\n            if (this._clipPath) {\n                this._clipPath.removeSelfFromZr(zr);\n            }\n            if (this._textContent) {\n                this._textContent.removeSelfFromZr(zr);\n            }\n            if (this._textGuide) {\n                this._textGuide.removeSelfFromZr(zr);\n            }\n        };\n        Element.prototype.animate = function (key, loop, allowDiscreteAnimation) {\n            var target = key ? this[key] : this;\n            if (\"development\" !== 'production') {\n                if (!target) {\n                    logError('Property \"'\n                        + key\n                        + '\" is not existed in element '\n                        + this.id);\n                    return;\n                }\n            }\n            var animator = new Animator(target, loop, allowDiscreteAnimation);\n            key && (animator.targetName = key);\n            this.addAnimator(animator, key);\n            return animator;\n        };\n        Element.prototype.addAnimator = function (animator, key) {\n            var zr = this.__zr;\n            var el = this;\n            animator.during(function () {\n                el.updateDuringAnimation(key);\n            }).done(function () {\n                var animators = el.animators;\n                var idx = indexOf(animators, animator);\n                if (idx >= 0) {\n                    animators.splice(idx, 1);\n                }\n            });\n            this.animators.push(animator);\n            if (zr) {\n                zr.animation.addAnimator(animator);\n            }\n            zr && zr.wakeUp();\n        };\n        Element.prototype.updateDuringAnimation = function (key) {\n            this.markRedraw();\n        };\n        Element.prototype.stopAnimation = function (scope, forwardToLast) {\n            var animators = this.animators;\n            var len = animators.length;\n            var leftAnimators = [];\n            for (var i = 0; i < len; i++) {\n                var animator = animators[i];\n                if (!scope || scope === animator.scope) {\n                    animator.stop(forwardToLast);\n                }\n                else {\n                    leftAnimators.push(animator);\n                }\n            }\n            this.animators = leftAnimators;\n            return this;\n        };\n        Element.prototype.animateTo = function (target, cfg, animationProps) {\n            animateTo(this, target, cfg, animationProps);\n        };\n        Element.prototype.animateFrom = function (target, cfg, animationProps) {\n            animateTo(this, target, cfg, animationProps, true);\n        };\n        Element.prototype._transitionState = function (stateName, target, cfg, animationProps) {\n            var animators = animateTo(this, target, cfg, animationProps);\n            for (var i = 0; i < animators.length; i++) {\n                animators[i].__fromStateTransition = stateName;\n            }\n        };\n        Element.prototype.getBoundingRect = function () {\n            return null;\n        };\n        Element.prototype.getPaintRect = function () {\n            return null;\n        };\n        Element.initDefaultProps = (function () {\n            var elProto = Element.prototype;\n            elProto.type = 'element';\n            elProto.name = '';\n            elProto.ignore =\n                elProto.silent =\n                    elProto.isGroup =\n                        elProto.draggable =\n                            elProto.dragging =\n                                elProto.ignoreClip =\n                                    elProto.__inHover = false;\n            elProto.__dirty = REDRAW_BIT;\n            var logs = {};\n            function logDeprecatedError(key, xKey, yKey) {\n                if (!logs[key + xKey + yKey]) {\n                    console.warn(\"DEPRECATED: '\" + key + \"' has been deprecated. use '\" + xKey + \"', '\" + yKey + \"' instead\");\n                    logs[key + xKey + yKey] = true;\n                }\n            }\n            function createLegacyProperty(key, privateKey, xKey, yKey) {\n                Object.defineProperty(elProto, key, {\n                    get: function () {\n                        if (\"development\" !== 'production') {\n                            logDeprecatedError(key, xKey, yKey);\n                        }\n                        if (!this[privateKey]) {\n                            var pos = this[privateKey] = [];\n                            enhanceArray(this, pos);\n                        }\n                        return this[privateKey];\n                    },\n                    set: function (pos) {\n                        if (\"development\" !== 'production') {\n                            logDeprecatedError(key, xKey, yKey);\n                        }\n                        this[xKey] = pos[0];\n                        this[yKey] = pos[1];\n                        this[privateKey] = pos;\n                        enhanceArray(this, pos);\n                    }\n                });\n                function enhanceArray(self, pos) {\n                    Object.defineProperty(pos, 0, {\n                        get: function () {\n                            return self[xKey];\n                        },\n                        set: function (val) {\n                            self[xKey] = val;\n                        }\n                    });\n                    Object.defineProperty(pos, 1, {\n                        get: function () {\n                            return self[yKey];\n                        },\n                        set: function (val) {\n                            self[yKey] = val;\n                        }\n                    });\n                }\n            }\n            if (Object.defineProperty) {\n                createLegacyProperty('position', '_legacyPos', 'x', 'y');\n                createLegacyProperty('scale', '_legacyScale', 'scaleX', 'scaleY');\n                createLegacyProperty('origin', '_legacyOrigin', 'originX', 'originY');\n            }\n        })();\n        return Element;\n    }());\n    mixin(Element, Eventful);\n    mixin(Element, Transformable);\n    function animateTo(animatable, target, cfg, animationProps, reverse) {\n        cfg = cfg || {};\n        var animators = [];\n        animateToShallow(animatable, '', animatable, target, cfg, animationProps, animators, reverse);\n        var finishCount = animators.length;\n        var doneHappened = false;\n        var cfgDone = cfg.done;\n        var cfgAborted = cfg.aborted;\n        var doneCb = function () {\n            doneHappened = true;\n            finishCount--;\n            if (finishCount <= 0) {\n                doneHappened\n                    ? (cfgDone && cfgDone())\n                    : (cfgAborted && cfgAborted());\n            }\n        };\n        var abortedCb = function () {\n            finishCount--;\n            if (finishCount <= 0) {\n                doneHappened\n                    ? (cfgDone && cfgDone())\n                    : (cfgAborted && cfgAborted());\n            }\n        };\n        if (!finishCount) {\n            cfgDone && cfgDone();\n        }\n        if (animators.length > 0 && cfg.during) {\n            animators[0].during(function (target, percent) {\n                cfg.during(percent);\n            });\n        }\n        for (var i = 0; i < animators.length; i++) {\n            var animator = animators[i];\n            if (doneCb) {\n                animator.done(doneCb);\n            }\n            if (abortedCb) {\n                animator.aborted(abortedCb);\n            }\n            if (cfg.force) {\n                animator.duration(cfg.duration);\n            }\n            animator.start(cfg.easing);\n        }\n        return animators;\n    }\n    function copyArrShallow(source, target, len) {\n        for (var i = 0; i < len; i++) {\n            source[i] = target[i];\n        }\n    }\n    function is2DArray(value) {\n        return isArrayLike(value[0]);\n    }\n    function copyValue(target, source, key) {\n        if (isArrayLike(source[key])) {\n            if (!isArrayLike(target[key])) {\n                target[key] = [];\n            }\n            if (isTypedArray(source[key])) {\n                var len = source[key].length;\n                if (target[key].length !== len) {\n                    target[key] = new (source[key].constructor)(len);\n                    copyArrShallow(target[key], source[key], len);\n                }\n            }\n            else {\n                var sourceArr = source[key];\n                var targetArr = target[key];\n                var len0 = sourceArr.length;\n                if (is2DArray(sourceArr)) {\n                    var len1 = sourceArr[0].length;\n                    for (var i = 0; i < len0; i++) {\n                        if (!targetArr[i]) {\n                            targetArr[i] = Array.prototype.slice.call(sourceArr[i]);\n                        }\n                        else {\n                            copyArrShallow(targetArr[i], sourceArr[i], len1);\n                        }\n                    }\n                }\n                else {\n                    copyArrShallow(targetArr, sourceArr, len0);\n                }\n                targetArr.length = sourceArr.length;\n            }\n        }\n        else {\n            target[key] = source[key];\n        }\n    }\n    function isValueSame(val1, val2) {\n        return val1 === val2\n            || isArrayLike(val1) && isArrayLike(val2) && is1DArraySame(val1, val2);\n    }\n    function is1DArraySame(arr0, arr1) {\n        var len = arr0.length;\n        if (len !== arr1.length) {\n            return false;\n        }\n        for (var i = 0; i < len; i++) {\n            if (arr0[i] !== arr1[i]) {\n                return false;\n            }\n        }\n        return true;\n    }\n    function animateToShallow(animatable, topKey, animateObj, target, cfg, animationProps, animators, reverse) {\n        var targetKeys = keys(target);\n        var duration = cfg.duration;\n        var delay = cfg.delay;\n        var additive = cfg.additive;\n        var setToFinal = cfg.setToFinal;\n        var animateAll = !isObject(animationProps);\n        var existsAnimators = animatable.animators;\n        var animationKeys = [];\n        for (var k = 0; k < targetKeys.length; k++) {\n            var innerKey = targetKeys[k];\n            var targetVal = target[innerKey];\n            if (targetVal != null && animateObj[innerKey] != null\n                && (animateAll || animationProps[innerKey])) {\n                if (isObject(targetVal)\n                    && !isArrayLike(targetVal)\n                    && !isGradientObject(targetVal)) {\n                    if (topKey) {\n                        if (!reverse) {\n                            animateObj[innerKey] = targetVal;\n                            animatable.updateDuringAnimation(topKey);\n                        }\n                        continue;\n                    }\n                    animateToShallow(animatable, innerKey, animateObj[innerKey], targetVal, cfg, animationProps && animationProps[innerKey], animators, reverse);\n                }\n                else {\n                    animationKeys.push(innerKey);\n                }\n            }\n            else if (!reverse) {\n                animateObj[innerKey] = targetVal;\n                animatable.updateDuringAnimation(topKey);\n                animationKeys.push(innerKey);\n            }\n        }\n        var keyLen = animationKeys.length;\n        if (!additive && keyLen) {\n            for (var i = 0; i < existsAnimators.length; i++) {\n                var animator = existsAnimators[i];\n                if (animator.targetName === topKey) {\n                    var allAborted = animator.stopTracks(animationKeys);\n                    if (allAborted) {\n                        var idx = indexOf(existsAnimators, animator);\n                        existsAnimators.splice(idx, 1);\n                    }\n                }\n            }\n        }\n        if (!cfg.force) {\n            animationKeys = filter(animationKeys, function (key) { return !isValueSame(target[key], animateObj[key]); });\n            keyLen = animationKeys.length;\n        }\n        if (keyLen > 0\n            || (cfg.force && !animators.length)) {\n            var revertedSource = void 0;\n            var reversedTarget = void 0;\n            var sourceClone = void 0;\n            if (reverse) {\n                reversedTarget = {};\n                if (setToFinal) {\n                    revertedSource = {};\n                }\n                for (var i = 0; i < keyLen; i++) {\n                    var innerKey = animationKeys[i];\n                    reversedTarget[innerKey] = animateObj[innerKey];\n                    if (setToFinal) {\n                        revertedSource[innerKey] = target[innerKey];\n                    }\n                    else {\n                        animateObj[innerKey] = target[innerKey];\n                    }\n                }\n            }\n            else if (setToFinal) {\n                sourceClone = {};\n                for (var i = 0; i < keyLen; i++) {\n                    var innerKey = animationKeys[i];\n                    sourceClone[innerKey] = cloneValue(animateObj[innerKey]);\n                    copyValue(animateObj, target, innerKey);\n                }\n            }\n            var animator = new Animator(animateObj, false, false, additive ? filter(existsAnimators, function (animator) { return animator.targetName === topKey; }) : null);\n            animator.targetName = topKey;\n            if (cfg.scope) {\n                animator.scope = cfg.scope;\n            }\n            if (setToFinal && revertedSource) {\n                animator.whenWithKeys(0, revertedSource, animationKeys);\n            }\n            if (sourceClone) {\n                animator.whenWithKeys(0, sourceClone, animationKeys);\n            }\n            animator.whenWithKeys(duration == null ? 500 : duration, reverse ? reversedTarget : target, animationKeys).delay(delay || 0);\n            animatable.addAnimator(animator, topKey);\n            animators.push(animator);\n        }\n    }\n\n    var Group = (function (_super) {\n        __extends(Group, _super);\n        function Group(opts) {\n            var _this = _super.call(this) || this;\n            _this.isGroup = true;\n            _this._children = [];\n            _this.attr(opts);\n            return _this;\n        }\n        Group.prototype.childrenRef = function () {\n            return this._children;\n        };\n        Group.prototype.children = function () {\n            return this._children.slice();\n        };\n        Group.prototype.childAt = function (idx) {\n            return this._children[idx];\n        };\n        Group.prototype.childOfName = function (name) {\n            var children = this._children;\n            for (var i = 0; i < children.length; i++) {\n                if (children[i].name === name) {\n                    return children[i];\n                }\n            }\n        };\n        Group.prototype.childCount = function () {\n            return this._children.length;\n        };\n        Group.prototype.add = function (child) {\n            if (child) {\n                if (child !== this && child.parent !== this) {\n                    this._children.push(child);\n                    this._doAdd(child);\n                }\n                if (\"development\" !== 'production') {\n                    if (child.__hostTarget) {\n                        throw 'This elemenet has been used as an attachment';\n                    }\n                }\n            }\n            return this;\n        };\n        Group.prototype.addBefore = function (child, nextSibling) {\n            if (child && child !== this && child.parent !== this\n                && nextSibling && nextSibling.parent === this) {\n                var children = this._children;\n                var idx = children.indexOf(nextSibling);\n                if (idx >= 0) {\n                    children.splice(idx, 0, child);\n                    this._doAdd(child);\n                }\n            }\n            return this;\n        };\n        Group.prototype.replace = function (oldChild, newChild) {\n            var idx = indexOf(this._children, oldChild);\n            if (idx >= 0) {\n                this.replaceAt(newChild, idx);\n            }\n            return this;\n        };\n        Group.prototype.replaceAt = function (child, index) {\n            var children = this._children;\n            var old = children[index];\n            if (child && child !== this && child.parent !== this && child !== old) {\n                children[index] = child;\n                old.parent = null;\n                var zr = this.__zr;\n                if (zr) {\n                    old.removeSelfFromZr(zr);\n                }\n                this._doAdd(child);\n            }\n            return this;\n        };\n        Group.prototype._doAdd = function (child) {\n            if (child.parent) {\n                child.parent.remove(child);\n            }\n            child.parent = this;\n            var zr = this.__zr;\n            if (zr && zr !== child.__zr) {\n                child.addSelfToZr(zr);\n            }\n            zr && zr.refresh();\n        };\n        Group.prototype.remove = function (child) {\n            var zr = this.__zr;\n            var children = this._children;\n            var idx = indexOf(children, child);\n            if (idx < 0) {\n                return this;\n            }\n            children.splice(idx, 1);\n            child.parent = null;\n            if (zr) {\n                child.removeSelfFromZr(zr);\n            }\n            zr && zr.refresh();\n            return this;\n        };\n        Group.prototype.removeAll = function () {\n            var children = this._children;\n            var zr = this.__zr;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                if (zr) {\n                    child.removeSelfFromZr(zr);\n                }\n                child.parent = null;\n            }\n            children.length = 0;\n            return this;\n        };\n        Group.prototype.eachChild = function (cb, context) {\n            var children = this._children;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                cb.call(context, child, i);\n            }\n            return this;\n        };\n        Group.prototype.traverse = function (cb, context) {\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                var stopped = cb.call(context, child);\n                if (child.isGroup && !stopped) {\n                    child.traverse(cb, context);\n                }\n            }\n            return this;\n        };\n        Group.prototype.addSelfToZr = function (zr) {\n            _super.prototype.addSelfToZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.addSelfToZr(zr);\n            }\n        };\n        Group.prototype.removeSelfFromZr = function (zr) {\n            _super.prototype.removeSelfFromZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.removeSelfFromZr(zr);\n            }\n        };\n        Group.prototype.getBoundingRect = function (includeChildren) {\n            var tmpRect = new BoundingRect(0, 0, 0, 0);\n            var children = includeChildren || this._children;\n            var tmpMat = [];\n            var rect = null;\n            for (var i = 0; i < children.length; i++) {\n                var child = children[i];\n                if (child.ignore || child.invisible) {\n                    continue;\n                }\n                var childRect = child.getBoundingRect();\n                var transform = child.getLocalTransform(tmpMat);\n                if (transform) {\n                    BoundingRect.applyTransform(tmpRect, childRect, transform);\n                    rect = rect || tmpRect.clone();\n                    rect.union(tmpRect);\n                }\n                else {\n                    rect = rect || childRect.clone();\n                    rect.union(childRect);\n                }\n            }\n            return rect || tmpRect;\n        };\n        return Group;\n    }(Element));\n    Group.prototype.type = 'group';\n\n    /*!\n    * ZRender, a high performance 2d drawing library.\n    *\n    * Copyright (c) 2013, Baidu Inc.\n    * All rights reserved.\n    *\n    * LICENSE\n    * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt\n    */\n    var painterCtors = {};\n    var instances = {};\n    function delInstance(id) {\n        delete instances[id];\n    }\n    function isDarkMode(backgroundColor) {\n        if (!backgroundColor) {\n            return false;\n        }\n        if (typeof backgroundColor === 'string') {\n            return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD;\n        }\n        else if (backgroundColor.colorStops) {\n            var colorStops = backgroundColor.colorStops;\n            var totalLum = 0;\n            var len = colorStops.length;\n            for (var i = 0; i < len; i++) {\n                totalLum += lum(colorStops[i].color, 1);\n            }\n            totalLum /= len;\n            return totalLum < DARK_MODE_THRESHOLD;\n        }\n        return false;\n    }\n    var ZRender = (function () {\n        function ZRender(id, dom, opts) {\n            var _this = this;\n            this._sleepAfterStill = 10;\n            this._stillFrameAccum = 0;\n            this._needsRefresh = true;\n            this._needsRefreshHover = true;\n            this._darkMode = false;\n            opts = opts || {};\n            this.dom = dom;\n            this.id = id;\n            var storage = new Storage();\n            var rendererType = opts.renderer || 'canvas';\n            if (!painterCtors[rendererType]) {\n                rendererType = keys(painterCtors)[0];\n            }\n            if (\"development\" !== 'production') {\n                if (!painterCtors[rendererType]) {\n                    throw new Error(\"Renderer '\" + rendererType + \"' is not imported. Please import it first.\");\n                }\n            }\n            opts.useDirtyRect = opts.useDirtyRect == null\n                ? false\n                : opts.useDirtyRect;\n            var painter = new painterCtors[rendererType](dom, storage, opts, id);\n            var ssrMode = opts.ssr || painter.ssrOnly;\n            this.storage = storage;\n            this.painter = painter;\n            var handerProxy = (!env.node && !env.worker && !ssrMode)\n                ? new HandlerDomProxy(painter.getViewportRoot(), painter.root)\n                : null;\n            var useCoarsePointer = opts.useCoarsePointer;\n            var usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto')\n                ? env.touchEventsSupported\n                : !!useCoarsePointer;\n            var defaultPointerSize = 44;\n            var pointerSize;\n            if (usePointerSize) {\n                pointerSize = retrieve2(opts.pointerSize, defaultPointerSize);\n            }\n            this.handler = new Handler(storage, painter, handerProxy, painter.root, pointerSize);\n            this.animation = new Animation({\n                stage: {\n                    update: ssrMode ? null : function () { return _this._flush(true); }\n                }\n            });\n            if (!ssrMode) {\n                this.animation.start();\n            }\n        }\n        ZRender.prototype.add = function (el) {\n            if (!el) {\n                return;\n            }\n            this.storage.addRoot(el);\n            el.addSelfToZr(this);\n            this.refresh();\n        };\n        ZRender.prototype.remove = function (el) {\n            if (!el) {\n                return;\n            }\n            this.storage.delRoot(el);\n            el.removeSelfFromZr(this);\n            this.refresh();\n        };\n        ZRender.prototype.configLayer = function (zLevel, config) {\n            if (this.painter.configLayer) {\n                this.painter.configLayer(zLevel, config);\n            }\n            this.refresh();\n        };\n        ZRender.prototype.setBackgroundColor = function (backgroundColor) {\n            if (this.painter.setBackgroundColor) {\n                this.painter.setBackgroundColor(backgroundColor);\n            }\n            this.refresh();\n            this._backgroundColor = backgroundColor;\n            this._darkMode = isDarkMode(backgroundColor);\n        };\n        ZRender.prototype.getBackgroundColor = function () {\n            return this._backgroundColor;\n        };\n        ZRender.prototype.setDarkMode = function (darkMode) {\n            this._darkMode = darkMode;\n        };\n        ZRender.prototype.isDarkMode = function () {\n            return this._darkMode;\n        };\n        ZRender.prototype.refreshImmediately = function (fromInside) {\n            if (!fromInside) {\n                this.animation.update(true);\n            }\n            this._needsRefresh = false;\n            this.painter.refresh();\n            this._needsRefresh = false;\n        };\n        ZRender.prototype.refresh = function () {\n            this._needsRefresh = true;\n            this.animation.start();\n        };\n        ZRender.prototype.flush = function () {\n            this._flush(false);\n        };\n        ZRender.prototype._flush = function (fromInside) {\n            var triggerRendered;\n            var start = getTime();\n            if (this._needsRefresh) {\n                triggerRendered = true;\n                this.refreshImmediately(fromInside);\n            }\n            if (this._needsRefreshHover) {\n                triggerRendered = true;\n                this.refreshHoverImmediately();\n            }\n            var end = getTime();\n            if (triggerRendered) {\n                this._stillFrameAccum = 0;\n                this.trigger('rendered', {\n                    elapsedTime: end - start\n                });\n            }\n            else if (this._sleepAfterStill > 0) {\n                this._stillFrameAccum++;\n                if (this._stillFrameAccum > this._sleepAfterStill) {\n                    this.animation.stop();\n                }\n            }\n        };\n        ZRender.prototype.setSleepAfterStill = function (stillFramesCount) {\n            this._sleepAfterStill = stillFramesCount;\n        };\n        ZRender.prototype.wakeUp = function () {\n            this.animation.start();\n            this._stillFrameAccum = 0;\n        };\n        ZRender.prototype.refreshHover = function () {\n            this._needsRefreshHover = true;\n        };\n        ZRender.prototype.refreshHoverImmediately = function () {\n            this._needsRefreshHover = false;\n            if (this.painter.refreshHover && this.painter.getType() === 'canvas') {\n                this.painter.refreshHover();\n            }\n        };\n        ZRender.prototype.resize = function (opts) {\n            opts = opts || {};\n            this.painter.resize(opts.width, opts.height);\n            this.handler.resize();\n        };\n        ZRender.prototype.clearAnimation = function () {\n            this.animation.clear();\n        };\n        ZRender.prototype.getWidth = function () {\n            return this.painter.getWidth();\n        };\n        ZRender.prototype.getHeight = function () {\n            return this.painter.getHeight();\n        };\n        ZRender.prototype.setCursorStyle = function (cursorStyle) {\n            this.handler.setCursorStyle(cursorStyle);\n        };\n        ZRender.prototype.findHover = function (x, y) {\n            return this.handler.findHover(x, y);\n        };\n        ZRender.prototype.on = function (eventName, eventHandler, context) {\n            this.handler.on(eventName, eventHandler, context);\n            return this;\n        };\n        ZRender.prototype.off = function (eventName, eventHandler) {\n            this.handler.off(eventName, eventHandler);\n        };\n        ZRender.prototype.trigger = function (eventName, event) {\n            this.handler.trigger(eventName, event);\n        };\n        ZRender.prototype.clear = function () {\n            var roots = this.storage.getRoots();\n            for (var i = 0; i < roots.length; i++) {\n                if (roots[i] instanceof Group) {\n                    roots[i].removeSelfFromZr(this);\n                }\n            }\n            this.storage.delAllRoots();\n            this.painter.clear();\n        };\n        ZRender.prototype.dispose = function () {\n            this.animation.stop();\n            this.clear();\n            this.storage.dispose();\n            this.painter.dispose();\n            this.handler.dispose();\n            this.animation =\n                this.storage =\n                    this.painter =\n                        this.handler = null;\n            delInstance(this.id);\n        };\n        return ZRender;\n    }());\n    function init(dom, opts) {\n        var zr = new ZRender(guid(), dom, opts);\n        instances[zr.id] = zr;\n        return zr;\n    }\n    function dispose(zr) {\n        zr.dispose();\n    }\n    function disposeAll() {\n        for (var key in instances) {\n            if (instances.hasOwnProperty(key)) {\n                instances[key].dispose();\n            }\n        }\n        instances = {};\n    }\n    function getInstance(id) {\n        return instances[id];\n    }\n    function registerPainter(name, Ctor) {\n        painterCtors[name] = Ctor;\n    }\n    var version = '5.4.1';\n\n    var zrender = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init,\n        dispose: dispose,\n        disposeAll: disposeAll,\n        getInstance: getInstance,\n        registerPainter: registerPainter,\n        version: version\n    });\n\n    var RADIAN_EPSILON = 1e-4; // Although chrome already enlarge this number to 100 for `toFixed`, but\n    // we sill follow the spec for compatibility.\n\n    var ROUND_SUPPORTED_PRECISION_MAX = 20;\n\n    function _trim(str) {\n      return str.replace(/^\\s+|\\s+$/g, '');\n    }\n    /**\n     * Linear mapping a value from domain to range\n     * @param  val\n     * @param  domain Domain extent domain[0] can be bigger than domain[1]\n     * @param  range  Range extent range[0] can be bigger than range[1]\n     * @param  clamp Default to be false\n     */\n\n\n    function linearMap(val, domain, range, clamp) {\n      var d0 = domain[0];\n      var d1 = domain[1];\n      var r0 = range[0];\n      var r1 = range[1];\n      var subDomain = d1 - d0;\n      var subRange = r1 - r0;\n\n      if (subDomain === 0) {\n        return subRange === 0 ? r0 : (r0 + r1) / 2;\n      } // Avoid accuracy problem in edge, such as\n      // 146.39 - 62.83 === 83.55999999999999.\n      // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError\n      // It is a little verbose for efficiency considering this method\n      // is a hotspot.\n\n\n      if (clamp) {\n        if (subDomain > 0) {\n          if (val <= d0) {\n            return r0;\n          } else if (val >= d1) {\n            return r1;\n          }\n        } else {\n          if (val >= d0) {\n            return r0;\n          } else if (val <= d1) {\n            return r1;\n          }\n        }\n      } else {\n        if (val === d0) {\n          return r0;\n        }\n\n        if (val === d1) {\n          return r1;\n        }\n      }\n\n      return (val - d0) / subDomain * subRange + r0;\n    }\n    /**\n     * Convert a percent string to absolute number.\n     * Returns NaN if percent is not a valid string or number\n     */\n\n    function parsePercent$1(percent, all) {\n      switch (percent) {\n        case 'center':\n        case 'middle':\n          percent = '50%';\n          break;\n\n        case 'left':\n        case 'top':\n          percent = '0%';\n          break;\n\n        case 'right':\n        case 'bottom':\n          percent = '100%';\n          break;\n      }\n\n      if (isString(percent)) {\n        if (_trim(percent).match(/%$/)) {\n          return parseFloat(percent) / 100 * all;\n        }\n\n        return parseFloat(percent);\n      }\n\n      return percent == null ? NaN : +percent;\n    }\n    function round(x, precision, returnStr) {\n      if (precision == null) {\n        precision = 10;\n      } // Avoid range error\n\n\n      precision = Math.min(Math.max(0, precision), ROUND_SUPPORTED_PRECISION_MAX); // PENDING: 1.005.toFixed(2) is '1.00' rather than '1.01'\n\n      x = (+x).toFixed(precision);\n      return returnStr ? x : +x;\n    }\n    /**\n     * Inplacd asc sort arr.\n     * The input arr will be modified.\n     */\n\n    function asc(arr) {\n      arr.sort(function (a, b) {\n        return a - b;\n      });\n      return arr;\n    }\n    /**\n     * Get precision.\n     */\n\n    function getPrecision(val) {\n      val = +val;\n\n      if (isNaN(val)) {\n        return 0;\n      } // It is much faster than methods converting number to string as follows\n      //      let tmp = val.toString();\n      //      return tmp.length - 1 - tmp.indexOf('.');\n      // especially when precision is low\n      // Notice:\n      // (1) If the loop count is over about 20, it is slower than `getPrecisionSafe`.\n      //     (see https://jsbench.me/2vkpcekkvw/1)\n      // (2) If the val is less than for example 1e-15, the result may be incorrect.\n      //     (see test/ut/spec/util/number.test.ts `getPrecision_equal_random`)\n\n\n      if (val > 1e-14) {\n        var e = 1;\n\n        for (var i = 0; i < 15; i++, e *= 10) {\n          if (Math.round(val * e) / e === val) {\n            return i;\n          }\n        }\n      }\n\n      return getPrecisionSafe(val);\n    }\n    /**\n     * Get precision with slow but safe method\n     */\n\n    function getPrecisionSafe(val) {\n      // toLowerCase for: '3.4E-12'\n      var str = val.toString().toLowerCase(); // Consider scientific notation: '3.4e-12' '3.4e+12'\n\n      var eIndex = str.indexOf('e');\n      var exp = eIndex > 0 ? +str.slice(eIndex + 1) : 0;\n      var significandPartLen = eIndex > 0 ? eIndex : str.length;\n      var dotIndex = str.indexOf('.');\n      var decimalPartLen = dotIndex < 0 ? 0 : significandPartLen - 1 - dotIndex;\n      return Math.max(0, decimalPartLen - exp);\n    }\n    /**\n     * Minimal dicernible data precisioin according to a single pixel.\n     */\n\n    function getPixelPrecision(dataExtent, pixelExtent) {\n      var log = Math.log;\n      var LN10 = Math.LN10;\n      var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);\n      var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); // toFixed() digits argument must be between 0 and 20.\n\n      var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);\n      return !isFinite(precision) ? 20 : precision;\n    }\n    /**\n     * Get a data of given precision, assuring the sum of percentages\n     * in valueList is 1.\n     * The largest remainder method is used.\n     * https://en.wikipedia.org/wiki/Largest_remainder_method\n     *\n     * @param valueList a list of all data\n     * @param idx index of the data to be processed in valueList\n     * @param precision integer number showing digits of precision\n     * @return percent ranging from 0 to 100\n     */\n\n    function getPercentWithPrecision(valueList, idx, precision) {\n      if (!valueList[idx]) {\n        return 0;\n      }\n\n      var seats = getPercentSeats(valueList, precision);\n      return seats[idx] || 0;\n    }\n    /**\n     * Get a data of given precision, assuring the sum of percentages\n     * in valueList is 1.\n     * The largest remainder method is used.\n     * https://en.wikipedia.org/wiki/Largest_remainder_method\n     *\n     * @param valueList a list of all data\n     * @param precision integer number showing digits of precision\n     * @return {Array<number>}\n     */\n\n    function getPercentSeats(valueList, precision) {\n      var sum = reduce(valueList, function (acc, val) {\n        return acc + (isNaN(val) ? 0 : val);\n      }, 0);\n\n      if (sum === 0) {\n        return [];\n      }\n\n      var digits = Math.pow(10, precision);\n      var votesPerQuota = map(valueList, function (val) {\n        return (isNaN(val) ? 0 : val) / sum * digits * 100;\n      });\n      var targetSeats = digits * 100;\n      var seats = map(votesPerQuota, function (votes) {\n        // Assign automatic seats.\n        return Math.floor(votes);\n      });\n      var currentSum = reduce(seats, function (acc, val) {\n        return acc + val;\n      }, 0);\n      var remainder = map(votesPerQuota, function (votes, idx) {\n        return votes - seats[idx];\n      }); // Has remainding votes.\n\n      while (currentSum < targetSeats) {\n        // Find next largest remainder.\n        var max = Number.NEGATIVE_INFINITY;\n        var maxId = null;\n\n        for (var i = 0, len = remainder.length; i < len; ++i) {\n          if (remainder[i] > max) {\n            max = remainder[i];\n            maxId = i;\n          }\n        } // Add a vote to max remainder.\n\n\n        ++seats[maxId];\n        remainder[maxId] = 0;\n        ++currentSum;\n      }\n\n      return map(seats, function (seat) {\n        return seat / digits;\n      });\n    }\n    /**\n     * Solve the floating point adding problem like 0.1 + 0.2 === 0.30000000000000004\n     * See <http://0.30000000000000004.com/>\n     */\n\n    function addSafe(val0, val1) {\n      var maxPrecision = Math.max(getPrecision(val0), getPrecision(val1)); // const multiplier = Math.pow(10, maxPrecision);\n      // return (Math.round(val0 * multiplier) + Math.round(val1 * multiplier)) / multiplier;\n\n      var sum = val0 + val1; // // PENDING: support more?\n\n      return maxPrecision > ROUND_SUPPORTED_PRECISION_MAX ? sum : round(sum, maxPrecision);\n    } // Number.MAX_SAFE_INTEGER, ie do not support.\n\n    var MAX_SAFE_INTEGER = 9007199254740991;\n    /**\n     * To 0 - 2 * PI, considering negative radian.\n     */\n\n    function remRadian(radian) {\n      var pi2 = Math.PI * 2;\n      return (radian % pi2 + pi2) % pi2;\n    }\n    /**\n     * @param {type} radian\n     * @return {boolean}\n     */\n\n    function isRadianAroundZero(val) {\n      return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;\n    } // eslint-disable-next-line\n\n    var TIME_REG = /^(?:(\\d{4})(?:[-\\/](\\d{1,2})(?:[-\\/](\\d{1,2})(?:[T ](\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2})(?:[.,](\\d+))?)?)?(Z|[\\+\\-]\\d\\d:?\\d\\d)?)?)?)?)?$/; // jshint ignore:line\n\n    /**\n     * @param value valid type: number | string | Date, otherwise return `new Date(NaN)`\n     *   These values can be accepted:\n     *   + An instance of Date, represent a time in its own time zone.\n     *   + Or string in a subset of ISO 8601, only including:\n     *     + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',\n     *     + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',\n     *     + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',\n     *     all of which will be treated as local time if time zone is not specified\n     *     (see <https://momentjs.com/>).\n     *   + Or other string format, including (all of which will be treated as local time):\n     *     '2012', '2012-3-1', '2012/3/1', '2012/03/01',\n     *     '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'\n     *   + a timestamp, which represent a time in UTC.\n     * @return date Never be null/undefined. If invalid, return `new Date(NaN)`.\n     */\n\n    function parseDate(value) {\n      if (value instanceof Date) {\n        return value;\n      } else if (isString(value)) {\n        // Different browsers parse date in different way, so we parse it manually.\n        // Some other issues:\n        // new Date('1970-01-01') is UTC,\n        // new Date('1970/01/01') and new Date('1970-1-01') is local.\n        // See issue #3623\n        var match = TIME_REG.exec(value);\n\n        if (!match) {\n          // return Invalid Date.\n          return new Date(NaN);\n        } // Use local time when no timezone offset is specified.\n\n\n        if (!match[8]) {\n          // match[n] can only be string or undefined.\n          // But take care of '12' + 1 => '121'.\n          return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0);\n        } // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,\n        // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).\n        // For example, system timezone is set as \"Time Zone: America/Toronto\",\n        // then these code will get different result:\n        // `new Date(1478411999999).getTimezoneOffset();  // get 240`\n        // `new Date(1478412000000).getTimezoneOffset();  // get 300`\n        // So we should not use `new Date`, but use `Date.UTC`.\n        else {\n            var hour = +match[4] || 0;\n\n            if (match[8].toUpperCase() !== 'Z') {\n              hour -= +match[8].slice(0, 3);\n            }\n\n            return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0));\n          }\n      } else if (value == null) {\n        return new Date(NaN);\n      }\n\n      return new Date(Math.round(value));\n    }\n    /**\n     * Quantity of a number. e.g. 0.1, 1, 10, 100\n     *\n     * @param val\n     * @return\n     */\n\n    function quantity(val) {\n      return Math.pow(10, quantityExponent(val));\n    }\n    /**\n     * Exponent of the quantity of a number\n     * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3\n     *\n     * @param val non-negative value\n     * @return\n     */\n\n    function quantityExponent(val) {\n      if (val === 0) {\n        return 0;\n      }\n\n      var exp = Math.floor(Math.log(val) / Math.LN10);\n      /**\n       * exp is expected to be the rounded-down result of the base-10 log of val.\n       * But due to the precision loss with Math.log(val), we need to restore it\n       * using 10^exp to make sure we can get val back from exp. #11249\n       */\n\n      if (val / Math.pow(10, exp) >= 10) {\n        exp++;\n      }\n\n      return exp;\n    }\n    /**\n     * find a “nice” number approximately equal to x. Round the number if round = true,\n     * take ceiling if round = false. The primary observation is that the “nicest”\n     * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.\n     *\n     * See \"Nice Numbers for Graph Labels\" of Graphic Gems.\n     *\n     * @param  val Non-negative value.\n     * @param  round\n     * @return Niced number\n     */\n\n    function nice(val, round) {\n      var exponent = quantityExponent(val);\n      var exp10 = Math.pow(10, exponent);\n      var f = val / exp10; // 1 <= f < 10\n\n      var nf;\n\n      if (round) {\n        if (f < 1.5) {\n          nf = 1;\n        } else if (f < 2.5) {\n          nf = 2;\n        } else if (f < 4) {\n          nf = 3;\n        } else if (f < 7) {\n          nf = 5;\n        } else {\n          nf = 10;\n        }\n      } else {\n        if (f < 1) {\n          nf = 1;\n        } else if (f < 2) {\n          nf = 2;\n        } else if (f < 3) {\n          nf = 3;\n        } else if (f < 5) {\n          nf = 5;\n        } else {\n          nf = 10;\n        }\n      }\n\n      val = nf * exp10; // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).\n      // 20 is the uppper bound of toFixed.\n\n      return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;\n    }\n    /**\n     * This code was copied from \"d3.js\"\n     * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.\n     * See the license statement at the head of this file.\n     * @param ascArr\n     */\n\n    function quantile(ascArr, p) {\n      var H = (ascArr.length - 1) * p + 1;\n      var h = Math.floor(H);\n      var v = +ascArr[h - 1];\n      var e = H - h;\n      return e ? v + e * (ascArr[h] - v) : v;\n    }\n    /**\n     * Order intervals asc, and split them when overlap.\n     * expect(numberUtil.reformIntervals([\n     *     {interval: [18, 62], close: [1, 1]},\n     *     {interval: [-Infinity, -70], close: [0, 0]},\n     *     {interval: [-70, -26], close: [1, 1]},\n     *     {interval: [-26, 18], close: [1, 1]},\n     *     {interval: [62, 150], close: [1, 1]},\n     *     {interval: [106, 150], close: [1, 1]},\n     *     {interval: [150, Infinity], close: [0, 0]}\n     * ])).toEqual([\n     *     {interval: [-Infinity, -70], close: [0, 0]},\n     *     {interval: [-70, -26], close: [1, 1]},\n     *     {interval: [-26, 18], close: [0, 1]},\n     *     {interval: [18, 62], close: [0, 1]},\n     *     {interval: [62, 150], close: [0, 1]},\n     *     {interval: [150, Infinity], close: [0, 0]}\n     * ]);\n     * @param list, where `close` mean open or close\n     *        of the interval, and Infinity can be used.\n     * @return The origin list, which has been reformed.\n     */\n\n    function reformIntervals(list) {\n      list.sort(function (a, b) {\n        return littleThan(a, b, 0) ? -1 : 1;\n      });\n      var curr = -Infinity;\n      var currClose = 1;\n\n      for (var i = 0; i < list.length;) {\n        var interval = list[i].interval;\n        var close_1 = list[i].close;\n\n        for (var lg = 0; lg < 2; lg++) {\n          if (interval[lg] <= curr) {\n            interval[lg] = curr;\n            close_1[lg] = !lg ? 1 - currClose : 1;\n          }\n\n          curr = interval[lg];\n          currClose = close_1[lg];\n        }\n\n        if (interval[0] === interval[1] && close_1[0] * close_1[1] !== 1) {\n          list.splice(i, 1);\n        } else {\n          i++;\n        }\n      }\n\n      return list;\n\n      function littleThan(a, b, lg) {\n        return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1));\n      }\n    }\n    /**\n     * [Numeric is defined as]:\n     *     `parseFloat(val) == val`\n     * For example:\n     * numeric:\n     *     typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity,\n     *     and they rounded by white-spaces or line-terminal like ' -123 \\n ' (see es spec)\n     * not-numeric:\n     *     null, undefined, [], {}, true, false, 'NaN', NaN, '123ab',\n     *     empty string, string with only white-spaces or line-terminal (see es spec),\n     *     0x12, '0x12', '-0x12', 012, '012', '-012',\n     *     non-string, ...\n     *\n     * @test See full test cases in `test/ut/spec/util/number.js`.\n     * @return Must be a typeof number. If not numeric, return NaN.\n     */\n\n    function numericToNumber(val) {\n      var valFloat = parseFloat(val);\n      return valFloat == val // eslint-disable-line eqeqeq\n      && (valFloat !== 0 || !isString(val) || val.indexOf('x') <= 0) // For case ' 0x0 '.\n      ? valFloat : NaN;\n    }\n    /**\n     * Definition of \"numeric\": see `numericToNumber`.\n     */\n\n    function isNumeric(val) {\n      return !isNaN(numericToNumber(val));\n    }\n    /**\n     * Use random base to prevent users hard code depending on\n     * this auto generated marker id.\n     * @return An positive integer.\n     */\n\n    function getRandomIdBase() {\n      return Math.round(Math.random() * 9);\n    }\n    /**\n     * Get the greatest common divisor.\n     *\n     * @param {number} a one number\n     * @param {number} b the other number\n     */\n\n    function getGreatestCommonDividor(a, b) {\n      if (b === 0) {\n        return a;\n      }\n\n      return getGreatestCommonDividor(b, a % b);\n    }\n    /**\n     * Get the least common multiple.\n     *\n     * @param {number} a one number\n     * @param {number} b the other number\n     */\n\n    function getLeastCommonMultiple(a, b) {\n      if (a == null) {\n        return b;\n      }\n\n      if (b == null) {\n        return a;\n      }\n\n      return a * b / getGreatestCommonDividor(a, b);\n    }\n\n    var ECHARTS_PREFIX = '[ECharts] ';\n    var storedLogs = {};\n    var hasConsole = typeof console !== 'undefined' // eslint-disable-next-line\n    && console.warn && console.log;\n\n    function outputLog(type, str, onlyOnce) {\n      if (hasConsole) {\n        if (onlyOnce) {\n          if (storedLogs[str]) {\n            return;\n          }\n\n          storedLogs[str] = true;\n        } // eslint-disable-next-line\n\n\n        console[type](ECHARTS_PREFIX + str);\n      }\n    }\n\n    function log(str, onlyOnce) {\n      outputLog('log', str, onlyOnce);\n    }\n    function warn(str, onlyOnce) {\n      outputLog('warn', str, onlyOnce);\n    }\n    function error(str, onlyOnce) {\n      outputLog('error', str, onlyOnce);\n    }\n    function deprecateLog(str) {\n      if (\"development\" !== 'production') {\n        // Not display duplicate message.\n        outputLog('warn', 'DEPRECATED: ' + str, true);\n      }\n    }\n    function deprecateReplaceLog(oldOpt, newOpt, scope) {\n      if (\"development\" !== 'production') {\n        deprecateLog((scope ? \"[\" + scope + \"]\" : '') + (oldOpt + \" is deprecated, use \" + newOpt + \" instead.\"));\n      }\n    }\n    /**\n     * If in __DEV__ environment, get console printable message for users hint.\n     * Parameters are separated by ' '.\n     * @usage\n     * makePrintable('This is an error on', someVar, someObj);\n     *\n     * @param hintInfo anything about the current execution context to hint users.\n     * @throws Error\n     */\n\n    function makePrintable() {\n      var hintInfo = [];\n\n      for (var _i = 0; _i < arguments.length; _i++) {\n        hintInfo[_i] = arguments[_i];\n      }\n\n      var msg = '';\n\n      if (\"development\" !== 'production') {\n        // Fuzzy stringify for print.\n        // This code only exist in dev environment.\n        var makePrintableStringIfPossible_1 = function (val) {\n          return val === void 0 ? 'undefined' : val === Infinity ? 'Infinity' : val === -Infinity ? '-Infinity' : eqNaN(val) ? 'NaN' : val instanceof Date ? 'Date(' + val.toISOString() + ')' : isFunction(val) ? 'function () { ... }' : isRegExp(val) ? val + '' : null;\n        };\n\n        msg = map(hintInfo, function (arg) {\n          if (isString(arg)) {\n            // Print without quotation mark for some statement.\n            return arg;\n          } else {\n            var printableStr = makePrintableStringIfPossible_1(arg);\n\n            if (printableStr != null) {\n              return printableStr;\n            } else if (typeof JSON !== 'undefined' && JSON.stringify) {\n              try {\n                return JSON.stringify(arg, function (n, val) {\n                  var printableStr = makePrintableStringIfPossible_1(val);\n                  return printableStr == null ? val : printableStr;\n                }); // In most cases the info object is small, so do not line break.\n              } catch (err) {\n                return '?';\n              }\n            } else {\n              return '?';\n            }\n          }\n        }).join(' ');\n      }\n\n      return msg;\n    }\n    /**\n     * @throws Error\n     */\n\n    function throwError(msg) {\n      throw new Error(msg);\n    }\n\n    function interpolateNumber$1(p0, p1, percent) {\n      return (p1 - p0) * percent + p0;\n    }\n    /**\n     * Make the name displayable. But we should\n     * make sure it is not duplicated with user\n     * specified name, so use '\\0';\n     */\n\n\n    var DUMMY_COMPONENT_NAME_PREFIX = 'series\\0';\n    var INTERNAL_COMPONENT_ID_PREFIX = '\\0_ec_\\0';\n    /**\n     * If value is not array, then translate it to array.\n     * @param  {*} value\n     * @return {Array} [value] or value\n     */\n\n    function normalizeToArray(value) {\n      return value instanceof Array ? value : value == null ? [] : [value];\n    }\n    /**\n     * Sync default option between normal and emphasis like `position` and `show`\n     * In case some one will write code like\n     *     label: {\n     *          show: false,\n     *          position: 'outside',\n     *          fontSize: 18\n     *     },\n     *     emphasis: {\n     *          label: { show: true }\n     *     }\n     */\n\n    function defaultEmphasis(opt, key, subOpts) {\n      // Caution: performance sensitive.\n      if (opt) {\n        opt[key] = opt[key] || {};\n        opt.emphasis = opt.emphasis || {};\n        opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal\n\n        for (var i = 0, len = subOpts.length; i < len; i++) {\n          var subOptName = subOpts[i];\n\n          if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) {\n            opt.emphasis[key][subOptName] = opt[key][subOptName];\n          }\n        }\n      }\n    }\n    var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([\n    //     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',\n    //     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',\n    //     // FIXME: deprecated, check and remove it.\n    //     'textStyle'\n    // ]);\n\n    /**\n     * The method does not ensure performance.\n     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n     * This helper method retrieves value from data.\n     */\n\n    function getDataItemValue(dataItem) {\n      return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;\n    }\n    /**\n     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n     * This helper method determine if dataItem has extra option besides value\n     */\n\n    function isDataItemOption(dataItem) {\n      return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array\n      // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));\n    }\n    /**\n     * Mapping to existings for merge.\n     *\n     * Mode \"normalMege\":\n     *     The mapping result (merge result) will keep the order of the existing\n     *     component, rather than the order of new option. Because we should ensure\n     *     some specified index reference (like xAxisIndex) keep work.\n     *     And in most cases, \"merge option\" is used to update partial option but not\n     *     be expected to change the order.\n     *\n     * Mode \"replaceMege\":\n     *     (1) Only the id mapped components will be merged.\n     *     (2) Other existing components (except internal components) will be removed.\n     *     (3) Other new options will be used to create new component.\n     *     (4) The index of the existing components will not be modified.\n     *     That means their might be \"hole\" after the removal.\n     *     The new components are created first at those available index.\n     *\n     * Mode \"replaceAll\":\n     *     This mode try to support that reproduce an echarts instance from another\n     *     echarts instance (via `getOption`) in some simple cases.\n     *     In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`,\n     *     which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is,\n     *     the \"hole\" in `newCmptOptions` will also be kept.\n     *     On the contrary, other modes try best to eliminate holes.\n     *     PENDING: This is an experimental mode yet.\n     *\n     * @return See the comment of <MappingResult>.\n     */\n\n    function mappingToExists(existings, newCmptOptions, mode) {\n      var isNormalMergeMode = mode === 'normalMerge';\n      var isReplaceMergeMode = mode === 'replaceMerge';\n      var isReplaceAllMode = mode === 'replaceAll';\n      existings = existings || [];\n      newCmptOptions = (newCmptOptions || []).slice();\n      var existingIdIdxMap = createHashMap(); // Validate id and name on user input option.\n\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!isObject(cmptOption)) {\n          newCmptOptions[index] = null;\n          return;\n        }\n\n        if (\"development\" !== 'production') {\n          // There is some legacy case that name is set as `false`.\n          // But should work normally rather than throw error.\n          if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) {\n            warnInvalidateIdOrName(cmptOption.id);\n          }\n\n          if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) {\n            warnInvalidateIdOrName(cmptOption.name);\n          }\n        }\n      });\n      var result = prepareResult(existings, existingIdIdxMap, mode);\n\n      if (isNormalMergeMode || isReplaceMergeMode) {\n        mappingById(result, existings, existingIdIdxMap, newCmptOptions);\n      }\n\n      if (isNormalMergeMode) {\n        mappingByName(result, newCmptOptions);\n      }\n\n      if (isNormalMergeMode || isReplaceMergeMode) {\n        mappingByIndex(result, newCmptOptions, isReplaceMergeMode);\n      } else if (isReplaceAllMode) {\n        mappingInReplaceAllMode(result, newCmptOptions);\n      }\n\n      makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the\n      // forEach will omit those items and result in incorrect result.\n\n      return result;\n    }\n\n    function prepareResult(existings, existingIdIdxMap, mode) {\n      var result = [];\n\n      if (mode === 'replaceAll') {\n        return result;\n      } // Do not use native `map` to in case that the array `existings`\n      // contains elided items, which will be omitted.\n\n\n      for (var index = 0; index < existings.length; index++) {\n        var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined.\n\n        if (existing && existing.id != null) {\n          existingIdIdxMap.set(existing.id, index);\n        } // For non-internal-componnets:\n        //     Mode \"normalMerge\": all existings kept.\n        //     Mode \"replaceMerge\": all existing removed unless mapped by id.\n        // For internal-components:\n        //     go with \"replaceMerge\" approach in both mode.\n\n\n        result.push({\n          existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing,\n          newOption: null,\n          keyInfo: null,\n          brandNew: null\n        });\n      }\n\n      return result;\n    }\n\n    function mappingById(result, existings, existingIdIdxMap, newCmptOptions) {\n      // Mapping by id if specified.\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!cmptOption || cmptOption.id == null) {\n          return;\n        }\n\n        var optionId = makeComparableKey(cmptOption.id);\n        var existingIdx = existingIdIdxMap.get(optionId);\n\n        if (existingIdx != null) {\n          var resultItem = result[existingIdx];\n          assert(!resultItem.newOption, 'Duplicated option on id \"' + optionId + '\".');\n          resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to\n          // the existings rather than creating new component model.\n\n          resultItem.existing = existings[existingIdx];\n          newCmptOptions[index] = null;\n        }\n      });\n    }\n\n    function mappingByName(result, newCmptOptions) {\n      // Mapping by name if specified.\n      each(newCmptOptions, function (cmptOption, index) {\n        if (!cmptOption || cmptOption.name == null) {\n          return;\n        }\n\n        for (var i = 0; i < result.length; i++) {\n          var existing = result[i].existing;\n\n          if (!result[i].newOption // Consider name: two map to one.\n          // Can not match when both ids existing but different.\n          && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {\n            result[i].newOption = cmptOption;\n            newCmptOptions[index] = null;\n            return;\n          }\n        }\n      });\n    }\n\n    function mappingByIndex(result, newCmptOptions, brandNew) {\n      each(newCmptOptions, function (cmptOption) {\n        if (!cmptOption) {\n          return;\n        } // Find the first place that not mapped by id and not internal component (consider the \"hole\").\n\n\n        var resultItem;\n        var nextIdx = 0;\n\n        while ( // Be `!resultItem` only when `nextIdx >= result.length`.\n        (resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because\n        // after mapping performed, model will always be assigned with an id if user not given.\n        // After that all models have id.\n        // (2) If new option has id, it can only set to a hole or append to the last. It should\n        // not be merged to the existings with different id. Because id should not be overwritten.\n        // (3) Name can be overwritten, because axis use name as 'show label text'.\n        resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode \"replaceMerge\", here no not-mapped-non-internal-existing.\n        resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {\n          nextIdx++;\n        }\n\n        if (resultItem) {\n          resultItem.newOption = cmptOption;\n          resultItem.brandNew = brandNew;\n        } else {\n          result.push({\n            newOption: cmptOption,\n            brandNew: brandNew,\n            existing: null,\n            keyInfo: null\n          });\n        }\n\n        nextIdx++;\n      });\n    }\n\n    function mappingInReplaceAllMode(result, newCmptOptions) {\n      each(newCmptOptions, function (cmptOption) {\n        // The feature \"reproduce\" requires \"hole\" will also reproduced\n        // in case that component index referring are broken.\n        result.push({\n          newOption: cmptOption,\n          brandNew: true,\n          existing: null,\n          keyInfo: null\n        });\n      });\n    }\n    /**\n     * Make id and name for mapping result (result of mappingToExists)\n     * into `keyInfo` field.\n     */\n\n\n    function makeIdAndName(mapResult) {\n      // We use this id to hash component models and view instances\n      // in echarts. id can be specified by user, or auto generated.\n      // The id generation rule ensures new view instance are able\n      // to mapped to old instance when setOption are called in\n      // no-merge mode. So we generate model id by name and plus\n      // type in view id.\n      // name can be duplicated among components, which is convenient\n      // to specify multi components (like series) by one name.\n      // Ensure that each id is distinct.\n      var idMap = createHashMap();\n      each(mapResult, function (item) {\n        var existing = item.existing;\n        existing && idMap.set(existing.id, item);\n      });\n      each(mapResult, function (item) {\n        var opt = item.newOption; // Force ensure id not duplicated.\n\n        assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));\n        opt && opt.id != null && idMap.set(opt.id, item);\n        !item.keyInfo && (item.keyInfo = {});\n      }); // Make name and id.\n\n      each(mapResult, function (item, index) {\n        var existing = item.existing;\n        var opt = item.newOption;\n        var keyInfo = item.keyInfo;\n\n        if (!isObject(opt)) {\n          return;\n        } // Name can be overwritten. Consider case: axis.name = '20km'.\n        // But id generated by name will not be changed, which affect\n        // only in that case: setOption with 'not merge mode' and view\n        // instance will be recreated, which can be accepted.\n\n\n        keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid that different series has the same name,\n        // because name may be used like in color pallet.\n        : DUMMY_COMPONENT_NAME_PREFIX + index;\n\n        if (existing) {\n          keyInfo.id = makeComparableKey(existing.id);\n        } else if (opt.id != null) {\n          keyInfo.id = makeComparableKey(opt.id);\n        } else {\n          // Consider this situatoin:\n          //  optionA: [{name: 'a'}, {name: 'a'}, {..}]\n          //  optionB [{..}, {name: 'a'}, {name: 'a'}]\n          // Series with the same name between optionA and optionB\n          // should be mapped.\n          var idNum = 0;\n\n          do {\n            keyInfo.id = '\\0' + keyInfo.name + '\\0' + idNum++;\n          } while (idMap.get(keyInfo.id));\n        }\n\n        idMap.set(keyInfo.id, item);\n      });\n    }\n\n    function keyExistAndEqual(attr, obj1, obj2) {\n      var key1 = convertOptionIdName(obj1[attr], null);\n      var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number.\n\n      return key1 != null && key2 != null && key1 === key2;\n    }\n    /**\n     * @return return null if not exist.\n     */\n\n\n    function makeComparableKey(val) {\n      if (\"development\" !== 'production') {\n        if (val == null) {\n          throw new Error();\n        }\n      }\n\n      return convertOptionIdName(val, '');\n    }\n\n    function convertOptionIdName(idOrName, defaultValue) {\n      if (idOrName == null) {\n        return defaultValue;\n      }\n\n      return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue;\n    }\n\n    function warnInvalidateIdOrName(idOrName) {\n      if (\"development\" !== 'production') {\n        warn('`' + idOrName + '` is invalid id or name. Must be a string or number.');\n      }\n    }\n\n    function isValidIdOrName(idOrName) {\n      return isStringSafe(idOrName) || isNumeric(idOrName);\n    }\n\n    function isNameSpecified(componentModel) {\n      var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.\n\n      return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));\n    }\n    /**\n     * @public\n     * @param {Object} cmptOption\n     * @return {boolean}\n     */\n\n    function isComponentIdInternal(cmptOption) {\n      return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;\n    }\n    function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) {\n      // Set mainType and complete subType.\n      each(mappingResult, function (item) {\n        var newOption = item.newOption;\n\n        if (isObject(newOption)) {\n          item.keyInfo.mainType = mainType;\n          item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor);\n        }\n      });\n    }\n\n    function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) {\n      var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.\n      : componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType\n\n      return subType;\n    }\n    /**\n     * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name\n     *                         each of which can be Array or primary type.\n     * @return dataIndex If not found, return undefined/null.\n     */\n\n    function queryDataIndex(data, payload) {\n      if (payload.dataIndexInside != null) {\n        return payload.dataIndexInside;\n      } else if (payload.dataIndex != null) {\n        return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) {\n          return data.indexOfRawIndex(value);\n        }) : data.indexOfRawIndex(payload.dataIndex);\n      } else if (payload.name != null) {\n        return isArray(payload.name) ? map(payload.name, function (value) {\n          return data.indexOfName(value);\n        }) : data.indexOfName(payload.name);\n      }\n    }\n    /**\n     * Enable property storage to any host object.\n     * Notice: Serialization is not supported.\n     *\n     * For example:\n     * let inner = zrUitl.makeInner();\n     *\n     * function some1(hostObj) {\n     *      inner(hostObj).someProperty = 1212;\n     *      ...\n     * }\n     * function some2() {\n     *      let fields = inner(this);\n     *      fields.someProperty1 = 1212;\n     *      fields.someProperty2 = 'xx';\n     *      ...\n     * }\n     *\n     * @return {Function}\n     */\n\n    function makeInner() {\n      var key = '__ec_inner_' + innerUniqueIndex++;\n      return function (hostObj) {\n        return hostObj[key] || (hostObj[key] = {});\n      };\n    }\n    var innerUniqueIndex = getRandomIdBase();\n    /**\n     * The same behavior as `component.getReferringComponents`.\n     */\n\n    function parseFinder(ecModel, finderInput, opt) {\n      var _a = preParseFinder(finderInput, opt),\n          mainTypeSpecified = _a.mainTypeSpecified,\n          queryOptionMap = _a.queryOptionMap,\n          others = _a.others;\n\n      var result = others;\n      var defaultMainType = opt ? opt.defaultMainType : null;\n\n      if (!mainTypeSpecified && defaultMainType) {\n        queryOptionMap.set(defaultMainType, {});\n      }\n\n      queryOptionMap.each(function (queryOption, mainType) {\n        var queryResult = queryReferringComponents(ecModel, mainType, queryOption, {\n          useDefault: defaultMainType === mainType,\n          enableAll: opt && opt.enableAll != null ? opt.enableAll : true,\n          enableNone: opt && opt.enableNone != null ? opt.enableNone : true\n        });\n        result[mainType + 'Models'] = queryResult.models;\n        result[mainType + 'Model'] = queryResult.models[0];\n      });\n      return result;\n    }\n    function preParseFinder(finderInput, opt) {\n      var finder;\n\n      if (isString(finderInput)) {\n        var obj = {};\n        obj[finderInput + 'Index'] = 0;\n        finder = obj;\n      } else {\n        finder = finderInput;\n      }\n\n      var queryOptionMap = createHashMap();\n      var others = {};\n      var mainTypeSpecified = false;\n      each(finder, function (value, key) {\n        // Exclude 'dataIndex' and other illgal keys.\n        if (key === 'dataIndex' || key === 'dataIndexInside') {\n          others[key] = value;\n          return;\n        }\n\n        var parsedKey = key.match(/^(\\w+)(Index|Id|Name)$/) || [];\n        var mainType = parsedKey[1];\n        var queryType = (parsedKey[2] || '').toLowerCase();\n\n        if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) {\n          return;\n        }\n\n        mainTypeSpecified = mainTypeSpecified || !!mainType;\n        var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {});\n        queryOption[queryType] = value;\n      });\n      return {\n        mainTypeSpecified: mainTypeSpecified,\n        queryOptionMap: queryOptionMap,\n        others: others\n      };\n    }\n    var SINGLE_REFERRING = {\n      useDefault: true,\n      enableAll: false,\n      enableNone: false\n    };\n    function queryReferringComponents(ecModel, mainType, userOption, opt) {\n      opt = opt || SINGLE_REFERRING;\n      var indexOption = userOption.index;\n      var idOption = userOption.id;\n      var nameOption = userOption.name;\n      var result = {\n        models: null,\n        specified: indexOption != null || idOption != null || nameOption != null\n      };\n\n      if (!result.specified) {\n        // Use the first as default if `useDefault`.\n        var firstCmpt = void 0;\n        result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : [];\n        return result;\n      }\n\n      if (indexOption === 'none' || indexOption === false) {\n        assert(opt.enableNone, '`\"none\"` or `false` is not a valid value on index option.');\n        result.models = [];\n        return result;\n      } // `queryComponents` will return all components if\n      // both all of index/id/name are null/undefined.\n\n\n      if (indexOption === 'all') {\n        assert(opt.enableAll, '`\"all\"` is not a valid value on index option.');\n        indexOption = idOption = nameOption = null;\n      }\n\n      result.models = ecModel.queryComponents({\n        mainType: mainType,\n        index: indexOption,\n        id: idOption,\n        name: nameOption\n      });\n      return result;\n    }\n    function setAttribute(dom, key, value) {\n      dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value;\n    }\n    function getAttribute(dom, key) {\n      return dom.getAttribute ? dom.getAttribute(key) : dom[key];\n    }\n    /**\n     * Interpolate raw values of a series with percent\n     *\n     * @param data         data\n     * @param labelModel   label model of the text element\n     * @param sourceValue  start value. May be null/undefined when init.\n     * @param targetValue  end value\n     * @param percent      0~1 percentage; 0 uses start value while 1 uses end value\n     * @return             interpolated values\n     *                     If `sourceValue` and `targetValue` are `number`, return `number`.\n     *                     If `sourceValue` and `targetValue` are `string`, return `string`.\n     *                     If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`.\n     *                     Other cases do not supported.\n     */\n\n    function interpolateRawValues(data, precision, sourceValue, targetValue, percent) {\n      var isAutoPrecision = precision == null || precision === 'auto';\n\n      if (targetValue == null) {\n        return targetValue;\n      }\n\n      if (isNumber(targetValue)) {\n        var value = interpolateNumber$1(sourceValue || 0, targetValue, percent);\n        return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision);\n      } else if (isString(targetValue)) {\n        return percent < 1 ? sourceValue : targetValue;\n      } else {\n        var interpolated = [];\n        var leftArr = sourceValue;\n        var rightArr = targetValue;\n        var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length);\n\n        for (var i = 0; i < length_1; ++i) {\n          var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims\n\n          if (info && info.type === 'ordinal') {\n            // In init, there is no `sourceValue`, but should better not to get undefined result.\n            interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i];\n          } else {\n            var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0;\n            var rightVal = rightArr[i];\n            var value = interpolateNumber$1(leftVal, rightVal, percent);\n            interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision);\n          }\n        }\n\n        return interpolated;\n      }\n    }\n\n    var TYPE_DELIMITER = '.';\n    var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';\n    var IS_EXTENDED_CLASS = '___EC__EXTENDED_CLASS___';\n    /**\n     * Notice, parseClassType('') should returns {main: '', sub: ''}\n     * @public\n     */\n\n    function parseClassType(componentType) {\n      var ret = {\n        main: '',\n        sub: ''\n      };\n\n      if (componentType) {\n        var typeArr = componentType.split(TYPE_DELIMITER);\n        ret.main = typeArr[0] || '';\n        ret.sub = typeArr[1] || '';\n      }\n\n      return ret;\n    }\n    /**\n     * @public\n     */\n\n    function checkClassType(componentType) {\n      assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType \"' + componentType + '\" illegal');\n    }\n\n    function isExtendedClass(clz) {\n      return !!(clz && clz[IS_EXTENDED_CLASS]);\n    }\n    /**\n     * Implements `ExtendableConstructor` for `rootClz`.\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & ExtendableConstructor\n     * enableClassExtend(Xxx as XxxConstructor);\n     * ```\n     */\n\n    function enableClassExtend(rootClz, mandatoryMethods) {\n      rootClz.$constructor = rootClz; // FIXME: not necessary?\n\n      rootClz.extend = function (proto) {\n        if (\"development\" !== 'production') {\n          each(mandatoryMethods, function (method) {\n            if (!proto[method]) {\n              console.warn('Method `' + method + '` should be implemented' + (proto.type ? ' in ' + proto.type : '') + '.');\n            }\n          });\n        }\n\n        var superClass = this;\n        var ExtendedClass;\n\n        if (isESClass(superClass)) {\n          ExtendedClass =\n          /** @class */\n          function (_super) {\n            __extends(class_1, _super);\n\n            function class_1() {\n              return _super.apply(this, arguments) || this;\n            }\n\n            return class_1;\n          }(superClass);\n        } else {\n          // For backward compat, we both support ts class inheritance and this\n          // \"extend\" approach.\n          // The constructor should keep the same behavior as ts class inheritance:\n          // If this constructor/$constructor is not declared, auto invoke the super\n          // constructor.\n          // If this constructor/$constructor is declared, it is responsible for\n          // calling the super constructor.\n          ExtendedClass = function () {\n            (proto.$constructor || superClass).apply(this, arguments);\n          };\n\n          inherits(ExtendedClass, this);\n        }\n\n        extend(ExtendedClass.prototype, proto);\n        ExtendedClass[IS_EXTENDED_CLASS] = true;\n        ExtendedClass.extend = this.extend;\n        ExtendedClass.superCall = superCall;\n        ExtendedClass.superApply = superApply;\n        ExtendedClass.superClass = superClass;\n        return ExtendedClass;\n      };\n    }\n\n    function isESClass(fn) {\n      return isFunction(fn) && /^class\\s/.test(Function.prototype.toString.call(fn));\n    }\n    /**\n     * A work around to both support ts extend and this extend mechanism.\n     * on sub-class.\n     * @usage\n     * ```ts\n     * class Component { ... }\n     * classUtil.enableClassExtend(Component);\n     * classUtil.enableClassManagement(Component, {registerWhenExtend: true});\n     *\n     * class Series extends Component { ... }\n     * // Without calling `markExtend`, `registerWhenExtend` will not work.\n     * Component.markExtend(Series);\n     * ```\n     */\n\n\n    function mountExtend(SubClz, SupperClz) {\n      SubClz.extend = SupperClz.extend;\n    } // A random offset.\n\n    var classBase = Math.round(Math.random() * 10);\n    /**\n     * Implements `CheckableConstructor` for `target`.\n     * Can not use instanceof, consider different scope by\n     * cross domain or es module import in ec extensions.\n     * Mount a method \"isInstance()\" to Clz.\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & CheckableConstructor;\n     * enableClassCheck(Xxx as XxxConstructor)\n     * ```\n     */\n\n    function enableClassCheck(target) {\n      var classAttr = ['__\\0is_clz', classBase++].join('_');\n      target.prototype[classAttr] = true;\n\n      if (\"development\" !== 'production') {\n        assert(!target.isInstance, 'The method \"is\" can not be defined.');\n      }\n\n      target.isInstance = function (obj) {\n        return !!(obj && obj[classAttr]);\n      };\n    } // superCall should have class info, which can not be fetched from 'this'.\n    // Consider this case:\n    // class A has method f,\n    // class B inherits class A, overrides method f, f call superApply('f'),\n    // class C inherits class B, does not override method f,\n    // then when method of class C is called, dead loop occurred.\n\n    function superCall(context, methodName) {\n      var args = [];\n\n      for (var _i = 2; _i < arguments.length; _i++) {\n        args[_i - 2] = arguments[_i];\n      }\n\n      return this.superClass.prototype[methodName].apply(context, args);\n    }\n\n    function superApply(context, methodName, args) {\n      return this.superClass.prototype[methodName].apply(context, args);\n    }\n    /**\n     * Implements `ClassManager` for `target`\n     *\n     * @usage\n     * ```ts\n     * class Xxx {}\n     * type XxxConstructor = typeof Xxx & ClassManager\n     * enableClassManagement(Xxx as XxxConstructor);\n     * ```\n     */\n\n\n    function enableClassManagement(target) {\n      /**\n       * Component model classes\n       * key: componentType,\n       * value:\n       *     componentClass, when componentType is 'a'\n       *     or Object.<subKey, componentClass>, when componentType is 'a.b'\n       */\n      var storage = {};\n\n      target.registerClass = function (clz) {\n        // `type` should not be a \"instance member\".\n        // If using TS class, should better declared as `static type = 'series.pie'`.\n        // otherwise users have to mount `type` on prototype manually.\n        // For backward compat and enable instance visit type via `this.type`,\n        // we still support fetch `type` from prototype.\n        var componentFullType = clz.type || clz.prototype.type;\n\n        if (componentFullType) {\n          checkClassType(componentFullType); // If only static type declared, we assign it to prototype mandatorily.\n\n          clz.prototype.type = componentFullType;\n          var componentTypeInfo = parseClassType(componentFullType);\n\n          if (!componentTypeInfo.sub) {\n            if (\"development\" !== 'production') {\n              if (storage[componentTypeInfo.main]) {\n                console.warn(componentTypeInfo.main + ' exists.');\n              }\n            }\n\n            storage[componentTypeInfo.main] = clz;\n          } else if (componentTypeInfo.sub !== IS_CONTAINER) {\n            var container = makeContainer(componentTypeInfo);\n            container[componentTypeInfo.sub] = clz;\n          }\n        }\n\n        return clz;\n      };\n\n      target.getClass = function (mainType, subType, throwWhenNotFound) {\n        var clz = storage[mainType];\n\n        if (clz && clz[IS_CONTAINER]) {\n          clz = subType ? clz[subType] : null;\n        }\n\n        if (throwWhenNotFound && !clz) {\n          throw new Error(!subType ? mainType + '.' + 'type should be specified.' : 'Component ' + mainType + '.' + (subType || '') + ' is used but not imported.');\n        }\n\n        return clz;\n      };\n\n      target.getClassesByMainType = function (componentType) {\n        var componentTypeInfo = parseClassType(componentType);\n        var result = [];\n        var obj = storage[componentTypeInfo.main];\n\n        if (obj && obj[IS_CONTAINER]) {\n          each(obj, function (o, type) {\n            type !== IS_CONTAINER && result.push(o);\n          });\n        } else {\n          result.push(obj);\n        }\n\n        return result;\n      };\n\n      target.hasClass = function (componentType) {\n        // Just consider componentType.main.\n        var componentTypeInfo = parseClassType(componentType);\n        return !!storage[componentTypeInfo.main];\n      };\n      /**\n       * @return Like ['aa', 'bb'], but can not be ['aa.xx']\n       */\n\n\n      target.getAllClassMainTypes = function () {\n        var types = [];\n        each(storage, function (obj, type) {\n          types.push(type);\n        });\n        return types;\n      };\n      /**\n       * If a main type is container and has sub types\n       */\n\n\n      target.hasSubTypes = function (componentType) {\n        var componentTypeInfo = parseClassType(componentType);\n        var obj = storage[componentTypeInfo.main];\n        return obj && obj[IS_CONTAINER];\n      };\n\n      function makeContainer(componentTypeInfo) {\n        var container = storage[componentTypeInfo.main];\n\n        if (!container || !container[IS_CONTAINER]) {\n          container = storage[componentTypeInfo.main] = {};\n          container[IS_CONTAINER] = true;\n        }\n\n        return container;\n      }\n    } // /**\n    //  * @param {string|Array.<string>} properties\n    //  */\n    // export function setReadOnly(obj, properties) {\n    // FIXME It seems broken in IE8 simulation of IE11\n    // if (!zrUtil.isArray(properties)) {\n    //     properties = properties != null ? [properties] : [];\n    // }\n    // zrUtil.each(properties, function (prop) {\n    //     let value = obj[prop];\n    //     Object.defineProperty\n    //         && Object.defineProperty(obj, prop, {\n    //             value: value, writable: false\n    //         });\n    //     zrUtil.isArray(obj[prop])\n    //         && Object.freeze\n    //         && Object.freeze(obj[prop]);\n    // });\n    // }\n\n    function makeStyleMapper(properties, ignoreParent) {\n      // Normalize\n      for (var i = 0; i < properties.length; i++) {\n        if (!properties[i][1]) {\n          properties[i][1] = properties[i][0];\n        }\n      }\n\n      ignoreParent = ignoreParent || false;\n      return function (model, excludes, includes) {\n        var style = {};\n\n        for (var i = 0; i < properties.length; i++) {\n          var propName = properties[i][1];\n\n          if (excludes && indexOf(excludes, propName) >= 0 || includes && indexOf(includes, propName) < 0) {\n            continue;\n          }\n\n          var val = model.getShallow(propName, ignoreParent);\n\n          if (val != null) {\n            style[properties[i][0]] = val;\n          }\n        } // TODO Text or image?\n\n\n        return style;\n      };\n    }\n\n    var AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getAreaStyle = makeStyleMapper(AREA_STYLE_KEY_MAP);\n\n    var AreaStyleMixin =\n    /** @class */\n    function () {\n      function AreaStyleMixin() {}\n\n      AreaStyleMixin.prototype.getAreaStyle = function (excludes, includes) {\n        return getAreaStyle(this, excludes, includes);\n      };\n\n      return AreaStyleMixin;\n    }();\n\n    var globalImageCache = new LRU(50);\n    function findExistImage(newImageOrSrc) {\n        if (typeof newImageOrSrc === 'string') {\n            var cachedImgObj = globalImageCache.get(newImageOrSrc);\n            return cachedImgObj && cachedImgObj.image;\n        }\n        else {\n            return newImageOrSrc;\n        }\n    }\n    function createOrUpdateImage(newImageOrSrc, image, hostEl, onload, cbPayload) {\n        if (!newImageOrSrc) {\n            return image;\n        }\n        else if (typeof newImageOrSrc === 'string') {\n            if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {\n                return image;\n            }\n            var cachedImgObj = globalImageCache.get(newImageOrSrc);\n            var pendingWrap = { hostEl: hostEl, cb: onload, cbPayload: cbPayload };\n            if (cachedImgObj) {\n                image = cachedImgObj.image;\n                !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);\n            }\n            else {\n                image = platformApi.loadImage(newImageOrSrc, imageOnLoad, imageOnLoad);\n                image.__zrImageSrc = newImageOrSrc;\n                globalImageCache.put(newImageOrSrc, image.__cachedImgObj = {\n                    image: image,\n                    pending: [pendingWrap]\n                });\n            }\n            return image;\n        }\n        else {\n            return newImageOrSrc;\n        }\n    }\n    function imageOnLoad() {\n        var cachedImgObj = this.__cachedImgObj;\n        this.onload = this.onerror = this.__cachedImgObj = null;\n        for (var i = 0; i < cachedImgObj.pending.length; i++) {\n            var pendingWrap = cachedImgObj.pending[i];\n            var cb = pendingWrap.cb;\n            cb && cb(this, pendingWrap.cbPayload);\n            pendingWrap.hostEl.dirty();\n        }\n        cachedImgObj.pending.length = 0;\n    }\n    function isImageReady(image) {\n        return image && image.width && image.height;\n    }\n\n    var STYLE_REG = /\\{([a-zA-Z0-9_]+)\\|([^}]*)\\}/g;\n    function truncateText(text, containerWidth, font, ellipsis, options) {\n        if (!containerWidth) {\n            return '';\n        }\n        var textLines = (text + '').split('\\n');\n        options = prepareTruncateOptions(containerWidth, font, ellipsis, options);\n        for (var i = 0, len = textLines.length; i < len; i++) {\n            textLines[i] = truncateSingleLine(textLines[i], options);\n        }\n        return textLines.join('\\n');\n    }\n    function prepareTruncateOptions(containerWidth, font, ellipsis, options) {\n        options = options || {};\n        var preparedOpts = extend({}, options);\n        preparedOpts.font = font;\n        ellipsis = retrieve2(ellipsis, '...');\n        preparedOpts.maxIterations = retrieve2(options.maxIterations, 2);\n        var minChar = preparedOpts.minChar = retrieve2(options.minChar, 0);\n        preparedOpts.cnCharWidth = getWidth('国', font);\n        var ascCharWidth = preparedOpts.ascCharWidth = getWidth('a', font);\n        preparedOpts.placeholder = retrieve2(options.placeholder, '');\n        var contentWidth = containerWidth = Math.max(0, containerWidth - 1);\n        for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {\n            contentWidth -= ascCharWidth;\n        }\n        var ellipsisWidth = getWidth(ellipsis, font);\n        if (ellipsisWidth > contentWidth) {\n            ellipsis = '';\n            ellipsisWidth = 0;\n        }\n        contentWidth = containerWidth - ellipsisWidth;\n        preparedOpts.ellipsis = ellipsis;\n        preparedOpts.ellipsisWidth = ellipsisWidth;\n        preparedOpts.contentWidth = contentWidth;\n        preparedOpts.containerWidth = containerWidth;\n        return preparedOpts;\n    }\n    function truncateSingleLine(textLine, options) {\n        var containerWidth = options.containerWidth;\n        var font = options.font;\n        var contentWidth = options.contentWidth;\n        if (!containerWidth) {\n            return '';\n        }\n        var lineWidth = getWidth(textLine, font);\n        if (lineWidth <= containerWidth) {\n            return textLine;\n        }\n        for (var j = 0;; j++) {\n            if (lineWidth <= contentWidth || j >= options.maxIterations) {\n                textLine += options.ellipsis;\n                break;\n            }\n            var subLength = j === 0\n                ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)\n                : lineWidth > 0\n                    ? Math.floor(textLine.length * contentWidth / lineWidth)\n                    : 0;\n            textLine = textLine.substr(0, subLength);\n            lineWidth = getWidth(textLine, font);\n        }\n        if (textLine === '') {\n            textLine = options.placeholder;\n        }\n        return textLine;\n    }\n    function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {\n        var width = 0;\n        var i = 0;\n        for (var len = text.length; i < len && width < contentWidth; i++) {\n            var charCode = text.charCodeAt(i);\n            width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;\n        }\n        return i;\n    }\n    function parsePlainText(text, style) {\n        text != null && (text += '');\n        var overflow = style.overflow;\n        var padding = style.padding;\n        var font = style.font;\n        var truncate = overflow === 'truncate';\n        var calculatedLineHeight = getLineHeight(font);\n        var lineHeight = retrieve2(style.lineHeight, calculatedLineHeight);\n        var bgColorDrawn = !!(style.backgroundColor);\n        var truncateLineOverflow = style.lineOverflow === 'truncate';\n        var width = style.width;\n        var lines;\n        if (width != null && (overflow === 'break' || overflow === 'breakAll')) {\n            lines = text ? wrapText(text, style.font, width, overflow === 'breakAll', 0).lines : [];\n        }\n        else {\n            lines = text ? text.split('\\n') : [];\n        }\n        var contentHeight = lines.length * lineHeight;\n        var height = retrieve2(style.height, contentHeight);\n        if (contentHeight > height && truncateLineOverflow) {\n            var lineCount = Math.floor(height / lineHeight);\n            lines = lines.slice(0, lineCount);\n        }\n        if (text && truncate && width != null) {\n            var options = prepareTruncateOptions(width, font, style.ellipsis, {\n                minChar: style.truncateMinChar,\n                placeholder: style.placeholder\n            });\n            for (var i = 0; i < lines.length; i++) {\n                lines[i] = truncateSingleLine(lines[i], options);\n            }\n        }\n        var outerHeight = height;\n        var contentWidth = 0;\n        for (var i = 0; i < lines.length; i++) {\n            contentWidth = Math.max(getWidth(lines[i], font), contentWidth);\n        }\n        if (width == null) {\n            width = contentWidth;\n        }\n        var outerWidth = contentWidth;\n        if (padding) {\n            outerHeight += padding[0] + padding[2];\n            outerWidth += padding[1] + padding[3];\n            width += padding[1] + padding[3];\n        }\n        if (bgColorDrawn) {\n            outerWidth = width;\n        }\n        return {\n            lines: lines,\n            height: height,\n            outerWidth: outerWidth,\n            outerHeight: outerHeight,\n            lineHeight: lineHeight,\n            calculatedLineHeight: calculatedLineHeight,\n            contentWidth: contentWidth,\n            contentHeight: contentHeight,\n            width: width\n        };\n    }\n    var RichTextToken = (function () {\n        function RichTextToken() {\n        }\n        return RichTextToken;\n    }());\n    var RichTextLine = (function () {\n        function RichTextLine(tokens) {\n            this.tokens = [];\n            if (tokens) {\n                this.tokens = tokens;\n            }\n        }\n        return RichTextLine;\n    }());\n    var RichTextContentBlock = (function () {\n        function RichTextContentBlock() {\n            this.width = 0;\n            this.height = 0;\n            this.contentWidth = 0;\n            this.contentHeight = 0;\n            this.outerWidth = 0;\n            this.outerHeight = 0;\n            this.lines = [];\n        }\n        return RichTextContentBlock;\n    }());\n    function parseRichText(text, style) {\n        var contentBlock = new RichTextContentBlock();\n        text != null && (text += '');\n        if (!text) {\n            return contentBlock;\n        }\n        var topWidth = style.width;\n        var topHeight = style.height;\n        var overflow = style.overflow;\n        var wrapInfo = (overflow === 'break' || overflow === 'breakAll') && topWidth != null\n            ? { width: topWidth, accumWidth: 0, breakAll: overflow === 'breakAll' }\n            : null;\n        var lastIndex = STYLE_REG.lastIndex = 0;\n        var result;\n        while ((result = STYLE_REG.exec(text)) != null) {\n            var matchedIndex = result.index;\n            if (matchedIndex > lastIndex) {\n                pushTokens(contentBlock, text.substring(lastIndex, matchedIndex), style, wrapInfo);\n            }\n            pushTokens(contentBlock, result[2], style, wrapInfo, result[1]);\n            lastIndex = STYLE_REG.lastIndex;\n        }\n        if (lastIndex < text.length) {\n            pushTokens(contentBlock, text.substring(lastIndex, text.length), style, wrapInfo);\n        }\n        var pendingList = [];\n        var calculatedHeight = 0;\n        var calculatedWidth = 0;\n        var stlPadding = style.padding;\n        var truncate = overflow === 'truncate';\n        var truncateLine = style.lineOverflow === 'truncate';\n        function finishLine(line, lineWidth, lineHeight) {\n            line.width = lineWidth;\n            line.lineHeight = lineHeight;\n            calculatedHeight += lineHeight;\n            calculatedWidth = Math.max(calculatedWidth, lineWidth);\n        }\n        outer: for (var i = 0; i < contentBlock.lines.length; i++) {\n            var line = contentBlock.lines[i];\n            var lineHeight = 0;\n            var lineWidth = 0;\n            for (var j = 0; j < line.tokens.length; j++) {\n                var token = line.tokens[j];\n                var tokenStyle = token.styleName && style.rich[token.styleName] || {};\n                var textPadding = token.textPadding = tokenStyle.padding;\n                var paddingH = textPadding ? textPadding[1] + textPadding[3] : 0;\n                var font = token.font = tokenStyle.font || style.font;\n                token.contentHeight = getLineHeight(font);\n                var tokenHeight = retrieve2(tokenStyle.height, token.contentHeight);\n                token.innerHeight = tokenHeight;\n                textPadding && (tokenHeight += textPadding[0] + textPadding[2]);\n                token.height = tokenHeight;\n                token.lineHeight = retrieve3(tokenStyle.lineHeight, style.lineHeight, tokenHeight);\n                token.align = tokenStyle && tokenStyle.align || style.align;\n                token.verticalAlign = tokenStyle && tokenStyle.verticalAlign || 'middle';\n                if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) {\n                    if (j > 0) {\n                        line.tokens = line.tokens.slice(0, j);\n                        finishLine(line, lineWidth, lineHeight);\n                        contentBlock.lines = contentBlock.lines.slice(0, i + 1);\n                    }\n                    else {\n                        contentBlock.lines = contentBlock.lines.slice(0, i);\n                    }\n                    break outer;\n                }\n                var styleTokenWidth = tokenStyle.width;\n                var tokenWidthNotSpecified = styleTokenWidth == null || styleTokenWidth === 'auto';\n                if (typeof styleTokenWidth === 'string' && styleTokenWidth.charAt(styleTokenWidth.length - 1) === '%') {\n                    token.percentWidth = styleTokenWidth;\n                    pendingList.push(token);\n                    token.contentWidth = getWidth(token.text, font);\n                }\n                else {\n                    if (tokenWidthNotSpecified) {\n                        var textBackgroundColor = tokenStyle.backgroundColor;\n                        var bgImg = textBackgroundColor && textBackgroundColor.image;\n                        if (bgImg) {\n                            bgImg = findExistImage(bgImg);\n                            if (isImageReady(bgImg)) {\n                                token.width = Math.max(token.width, bgImg.width * tokenHeight / bgImg.height);\n                            }\n                        }\n                    }\n                    var remainTruncWidth = truncate && topWidth != null\n                        ? topWidth - lineWidth : null;\n                    if (remainTruncWidth != null && remainTruncWidth < token.width) {\n                        if (!tokenWidthNotSpecified || remainTruncWidth < paddingH) {\n                            token.text = '';\n                            token.width = token.contentWidth = 0;\n                        }\n                        else {\n                            token.text = truncateText(token.text, remainTruncWidth - paddingH, font, style.ellipsis, { minChar: style.truncateMinChar });\n                            token.width = token.contentWidth = getWidth(token.text, font);\n                        }\n                    }\n                    else {\n                        token.contentWidth = getWidth(token.text, font);\n                    }\n                }\n                token.width += paddingH;\n                lineWidth += token.width;\n                tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));\n            }\n            finishLine(line, lineWidth, lineHeight);\n        }\n        contentBlock.outerWidth = contentBlock.width = retrieve2(topWidth, calculatedWidth);\n        contentBlock.outerHeight = contentBlock.height = retrieve2(topHeight, calculatedHeight);\n        contentBlock.contentHeight = calculatedHeight;\n        contentBlock.contentWidth = calculatedWidth;\n        if (stlPadding) {\n            contentBlock.outerWidth += stlPadding[1] + stlPadding[3];\n            contentBlock.outerHeight += stlPadding[0] + stlPadding[2];\n        }\n        for (var i = 0; i < pendingList.length; i++) {\n            var token = pendingList[i];\n            var percentWidth = token.percentWidth;\n            token.width = parseInt(percentWidth, 10) / 100 * contentBlock.width;\n        }\n        return contentBlock;\n    }\n    function pushTokens(block, str, style, wrapInfo, styleName) {\n        var isEmptyStr = str === '';\n        var tokenStyle = styleName && style.rich[styleName] || {};\n        var lines = block.lines;\n        var font = tokenStyle.font || style.font;\n        var newLine = false;\n        var strLines;\n        var linesWidths;\n        if (wrapInfo) {\n            var tokenPadding = tokenStyle.padding;\n            var tokenPaddingH = tokenPadding ? tokenPadding[1] + tokenPadding[3] : 0;\n            if (tokenStyle.width != null && tokenStyle.width !== 'auto') {\n                var outerWidth_1 = parsePercent(tokenStyle.width, wrapInfo.width) + tokenPaddingH;\n                if (lines.length > 0) {\n                    if (outerWidth_1 + wrapInfo.accumWidth > wrapInfo.width) {\n                        strLines = str.split('\\n');\n                        newLine = true;\n                    }\n                }\n                wrapInfo.accumWidth = outerWidth_1;\n            }\n            else {\n                var res = wrapText(str, font, wrapInfo.width, wrapInfo.breakAll, wrapInfo.accumWidth);\n                wrapInfo.accumWidth = res.accumWidth + tokenPaddingH;\n                linesWidths = res.linesWidths;\n                strLines = res.lines;\n            }\n        }\n        else {\n            strLines = str.split('\\n');\n        }\n        for (var i = 0; i < strLines.length; i++) {\n            var text = strLines[i];\n            var token = new RichTextToken();\n            token.styleName = styleName;\n            token.text = text;\n            token.isLineHolder = !text && !isEmptyStr;\n            if (typeof tokenStyle.width === 'number') {\n                token.width = tokenStyle.width;\n            }\n            else {\n                token.width = linesWidths\n                    ? linesWidths[i]\n                    : getWidth(text, font);\n            }\n            if (!i && !newLine) {\n                var tokens = (lines[lines.length - 1] || (lines[0] = new RichTextLine())).tokens;\n                var tokensLen = tokens.length;\n                (tokensLen === 1 && tokens[0].isLineHolder)\n                    ? (tokens[0] = token)\n                    : ((text || !tokensLen || isEmptyStr) && tokens.push(token));\n            }\n            else {\n                lines.push(new RichTextLine([token]));\n            }\n        }\n    }\n    function isLatin(ch) {\n        var code = ch.charCodeAt(0);\n        return code >= 0x21 && code <= 0x17F;\n    }\n    var breakCharMap = reduce(',&?/;] '.split(''), function (obj, ch) {\n        obj[ch] = true;\n        return obj;\n    }, {});\n    function isWordBreakChar(ch) {\n        if (isLatin(ch)) {\n            if (breakCharMap[ch]) {\n                return true;\n            }\n            return false;\n        }\n        return true;\n    }\n    function wrapText(text, font, lineWidth, isBreakAll, lastAccumWidth) {\n        var lines = [];\n        var linesWidths = [];\n        var line = '';\n        var currentWord = '';\n        var currentWordWidth = 0;\n        var accumWidth = 0;\n        for (var i = 0; i < text.length; i++) {\n            var ch = text.charAt(i);\n            if (ch === '\\n') {\n                if (currentWord) {\n                    line += currentWord;\n                    accumWidth += currentWordWidth;\n                }\n                lines.push(line);\n                linesWidths.push(accumWidth);\n                line = '';\n                currentWord = '';\n                currentWordWidth = 0;\n                accumWidth = 0;\n                continue;\n            }\n            var chWidth = getWidth(ch, font);\n            var inWord = isBreakAll ? false : !isWordBreakChar(ch);\n            if (!lines.length\n                ? lastAccumWidth + accumWidth + chWidth > lineWidth\n                : accumWidth + chWidth > lineWidth) {\n                if (!accumWidth) {\n                    if (inWord) {\n                        lines.push(currentWord);\n                        linesWidths.push(currentWordWidth);\n                        currentWord = ch;\n                        currentWordWidth = chWidth;\n                    }\n                    else {\n                        lines.push(ch);\n                        linesWidths.push(chWidth);\n                    }\n                }\n                else if (line || currentWord) {\n                    if (inWord) {\n                        if (!line) {\n                            line = currentWord;\n                            currentWord = '';\n                            currentWordWidth = 0;\n                            accumWidth = currentWordWidth;\n                        }\n                        lines.push(line);\n                        linesWidths.push(accumWidth - currentWordWidth);\n                        currentWord += ch;\n                        currentWordWidth += chWidth;\n                        line = '';\n                        accumWidth = currentWordWidth;\n                    }\n                    else {\n                        if (currentWord) {\n                            line += currentWord;\n                            currentWord = '';\n                            currentWordWidth = 0;\n                        }\n                        lines.push(line);\n                        linesWidths.push(accumWidth);\n                        line = ch;\n                        accumWidth = chWidth;\n                    }\n                }\n                continue;\n            }\n            accumWidth += chWidth;\n            if (inWord) {\n                currentWord += ch;\n                currentWordWidth += chWidth;\n            }\n            else {\n                if (currentWord) {\n                    line += currentWord;\n                    currentWord = '';\n                    currentWordWidth = 0;\n                }\n                line += ch;\n            }\n        }\n        if (!lines.length && !line) {\n            line = text;\n            currentWord = '';\n            currentWordWidth = 0;\n        }\n        if (currentWord) {\n            line += currentWord;\n        }\n        if (line) {\n            lines.push(line);\n            linesWidths.push(accumWidth);\n        }\n        if (lines.length === 1) {\n            accumWidth += lastAccumWidth;\n        }\n        return {\n            accumWidth: accumWidth,\n            lines: lines,\n            linesWidths: linesWidths\n        };\n    }\n\n    var STYLE_MAGIC_KEY = '__zr_style_' + Math.round((Math.random() * 10));\n    var DEFAULT_COMMON_STYLE = {\n        shadowBlur: 0,\n        shadowOffsetX: 0,\n        shadowOffsetY: 0,\n        shadowColor: '#000',\n        opacity: 1,\n        blend: 'source-over'\n    };\n    var DEFAULT_COMMON_ANIMATION_PROPS = {\n        style: {\n            shadowBlur: true,\n            shadowOffsetX: true,\n            shadowOffsetY: true,\n            shadowColor: true,\n            opacity: true\n        }\n    };\n    DEFAULT_COMMON_STYLE[STYLE_MAGIC_KEY] = true;\n    var PRIMARY_STATES_KEYS$1 = ['z', 'z2', 'invisible'];\n    var PRIMARY_STATES_KEYS_IN_HOVER_LAYER = ['invisible'];\n    var Displayable = (function (_super) {\n        __extends(Displayable, _super);\n        function Displayable(props) {\n            return _super.call(this, props) || this;\n        }\n        Displayable.prototype._init = function (props) {\n            var keysArr = keys(props);\n            for (var i = 0; i < keysArr.length; i++) {\n                var key = keysArr[i];\n                if (key === 'style') {\n                    this.useStyle(props[key]);\n                }\n                else {\n                    _super.prototype.attrKV.call(this, key, props[key]);\n                }\n            }\n            if (!this.style) {\n                this.useStyle({});\n            }\n        };\n        Displayable.prototype.beforeBrush = function () { };\n        Displayable.prototype.afterBrush = function () { };\n        Displayable.prototype.innerBeforeBrush = function () { };\n        Displayable.prototype.innerAfterBrush = function () { };\n        Displayable.prototype.shouldBePainted = function (viewWidth, viewHeight, considerClipPath, considerAncestors) {\n            var m = this.transform;\n            if (this.ignore\n                || this.invisible\n                || this.style.opacity === 0\n                || (this.culling\n                    && isDisplayableCulled(this, viewWidth, viewHeight))\n                || (m && !m[0] && !m[3])) {\n                return false;\n            }\n            if (considerClipPath && this.__clipPaths) {\n                for (var i = 0; i < this.__clipPaths.length; ++i) {\n                    if (this.__clipPaths[i].isZeroArea()) {\n                        return false;\n                    }\n                }\n            }\n            if (considerAncestors && this.parent) {\n                var parent_1 = this.parent;\n                while (parent_1) {\n                    if (parent_1.ignore) {\n                        return false;\n                    }\n                    parent_1 = parent_1.parent;\n                }\n            }\n            return true;\n        };\n        Displayable.prototype.contain = function (x, y) {\n            return this.rectContain(x, y);\n        };\n        Displayable.prototype.traverse = function (cb, context) {\n            cb.call(context, this);\n        };\n        Displayable.prototype.rectContain = function (x, y) {\n            var coord = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            return rect.contain(coord[0], coord[1]);\n        };\n        Displayable.prototype.getPaintRect = function () {\n            var rect = this._paintRect;\n            if (!this._paintRect || this.__dirty) {\n                var transform = this.transform;\n                var elRect = this.getBoundingRect();\n                var style = this.style;\n                var shadowSize = style.shadowBlur || 0;\n                var shadowOffsetX = style.shadowOffsetX || 0;\n                var shadowOffsetY = style.shadowOffsetY || 0;\n                rect = this._paintRect || (this._paintRect = new BoundingRect(0, 0, 0, 0));\n                if (transform) {\n                    BoundingRect.applyTransform(rect, elRect, transform);\n                }\n                else {\n                    rect.copy(elRect);\n                }\n                if (shadowSize || shadowOffsetX || shadowOffsetY) {\n                    rect.width += shadowSize * 2 + Math.abs(shadowOffsetX);\n                    rect.height += shadowSize * 2 + Math.abs(shadowOffsetY);\n                    rect.x = Math.min(rect.x, rect.x + shadowOffsetX - shadowSize);\n                    rect.y = Math.min(rect.y, rect.y + shadowOffsetY - shadowSize);\n                }\n                var tolerance = this.dirtyRectTolerance;\n                if (!rect.isZero()) {\n                    rect.x = Math.floor(rect.x - tolerance);\n                    rect.y = Math.floor(rect.y - tolerance);\n                    rect.width = Math.ceil(rect.width + 1 + tolerance * 2);\n                    rect.height = Math.ceil(rect.height + 1 + tolerance * 2);\n                }\n            }\n            return rect;\n        };\n        Displayable.prototype.setPrevPaintRect = function (paintRect) {\n            if (paintRect) {\n                this._prevPaintRect = this._prevPaintRect || new BoundingRect(0, 0, 0, 0);\n                this._prevPaintRect.copy(paintRect);\n            }\n            else {\n                this._prevPaintRect = null;\n            }\n        };\n        Displayable.prototype.getPrevPaintRect = function () {\n            return this._prevPaintRect;\n        };\n        Displayable.prototype.animateStyle = function (loop) {\n            return this.animate('style', loop);\n        };\n        Displayable.prototype.updateDuringAnimation = function (targetKey) {\n            if (targetKey === 'style') {\n                this.dirtyStyle();\n            }\n            else {\n                this.markRedraw();\n            }\n        };\n        Displayable.prototype.attrKV = function (key, value) {\n            if (key !== 'style') {\n                _super.prototype.attrKV.call(this, key, value);\n            }\n            else {\n                if (!this.style) {\n                    this.useStyle(value);\n                }\n                else {\n                    this.setStyle(value);\n                }\n            }\n        };\n        Displayable.prototype.setStyle = function (keyOrObj, value) {\n            if (typeof keyOrObj === 'string') {\n                this.style[keyOrObj] = value;\n            }\n            else {\n                extend(this.style, keyOrObj);\n            }\n            this.dirtyStyle();\n            return this;\n        };\n        Displayable.prototype.dirtyStyle = function (notRedraw) {\n            if (!notRedraw) {\n                this.markRedraw();\n            }\n            this.__dirty |= STYLE_CHANGED_BIT;\n            if (this._rect) {\n                this._rect = null;\n            }\n        };\n        Displayable.prototype.dirty = function () {\n            this.dirtyStyle();\n        };\n        Displayable.prototype.styleChanged = function () {\n            return !!(this.__dirty & STYLE_CHANGED_BIT);\n        };\n        Displayable.prototype.styleUpdated = function () {\n            this.__dirty &= ~STYLE_CHANGED_BIT;\n        };\n        Displayable.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_COMMON_STYLE, obj);\n        };\n        Displayable.prototype.useStyle = function (obj) {\n            if (!obj[STYLE_MAGIC_KEY]) {\n                obj = this.createStyle(obj);\n            }\n            if (this.__inHover) {\n                this.__hoverStyle = obj;\n            }\n            else {\n                this.style = obj;\n            }\n            this.dirtyStyle();\n        };\n        Displayable.prototype.isStyleObject = function (obj) {\n            return obj[STYLE_MAGIC_KEY];\n        };\n        Displayable.prototype._innerSaveToNormal = function (toState) {\n            _super.prototype._innerSaveToNormal.call(this, toState);\n            var normalState = this._normalState;\n            if (toState.style && !normalState.style) {\n                normalState.style = this._mergeStyle(this.createStyle(), this.style);\n            }\n            this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS$1);\n        };\n        Displayable.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            var targetStyle;\n            if (state && state.style) {\n                if (transition) {\n                    if (keepCurrentStates) {\n                        targetStyle = state.style;\n                    }\n                    else {\n                        targetStyle = this._mergeStyle(this.createStyle(), normalState.style);\n                        this._mergeStyle(targetStyle, state.style);\n                    }\n                }\n                else {\n                    targetStyle = this._mergeStyle(this.createStyle(), keepCurrentStates ? this.style : normalState.style);\n                    this._mergeStyle(targetStyle, state.style);\n                }\n            }\n            else if (needsRestoreToNormal) {\n                targetStyle = normalState.style;\n            }\n            if (targetStyle) {\n                if (transition) {\n                    var sourceStyle = this.style;\n                    this.style = this.createStyle(needsRestoreToNormal ? {} : sourceStyle);\n                    if (needsRestoreToNormal) {\n                        var changedKeys = keys(sourceStyle);\n                        for (var i = 0; i < changedKeys.length; i++) {\n                            var key = changedKeys[i];\n                            if (key in targetStyle) {\n                                targetStyle[key] = targetStyle[key];\n                                this.style[key] = sourceStyle[key];\n                            }\n                        }\n                    }\n                    var targetKeys = keys(targetStyle);\n                    for (var i = 0; i < targetKeys.length; i++) {\n                        var key = targetKeys[i];\n                        this.style[key] = this.style[key];\n                    }\n                    this._transitionState(stateName, {\n                        style: targetStyle\n                    }, animationCfg, this.getAnimationStyleProps());\n                }\n                else {\n                    this.useStyle(targetStyle);\n                }\n            }\n            var statesKeys = this.__inHover ? PRIMARY_STATES_KEYS_IN_HOVER_LAYER : PRIMARY_STATES_KEYS$1;\n            for (var i = 0; i < statesKeys.length; i++) {\n                var key = statesKeys[i];\n                if (state && state[key] != null) {\n                    this[key] = state[key];\n                }\n                else if (needsRestoreToNormal) {\n                    if (normalState[key] != null) {\n                        this[key] = normalState[key];\n                    }\n                }\n            }\n        };\n        Displayable.prototype._mergeStates = function (states) {\n            var mergedState = _super.prototype._mergeStates.call(this, states);\n            var mergedStyle;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                if (state.style) {\n                    mergedStyle = mergedStyle || {};\n                    this._mergeStyle(mergedStyle, state.style);\n                }\n            }\n            if (mergedStyle) {\n                mergedState.style = mergedStyle;\n            }\n            return mergedState;\n        };\n        Displayable.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n            extend(targetStyle, sourceStyle);\n            return targetStyle;\n        };\n        Displayable.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_COMMON_ANIMATION_PROPS;\n        };\n        Displayable.initDefaultProps = (function () {\n            var dispProto = Displayable.prototype;\n            dispProto.type = 'displayable';\n            dispProto.invisible = false;\n            dispProto.z = 0;\n            dispProto.z2 = 0;\n            dispProto.zlevel = 0;\n            dispProto.culling = false;\n            dispProto.cursor = 'pointer';\n            dispProto.rectHover = false;\n            dispProto.incremental = false;\n            dispProto._rect = null;\n            dispProto.dirtyRectTolerance = 0;\n            dispProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT;\n        })();\n        return Displayable;\n    }(Element));\n    var tmpRect$1 = new BoundingRect(0, 0, 0, 0);\n    var viewRect = new BoundingRect(0, 0, 0, 0);\n    function isDisplayableCulled(el, width, height) {\n        tmpRect$1.copy(el.getBoundingRect());\n        if (el.transform) {\n            tmpRect$1.applyTransform(el.transform);\n        }\n        viewRect.width = width;\n        viewRect.height = height;\n        return !tmpRect$1.intersect(viewRect);\n    }\n\n    var mathMin$1 = Math.min;\n    var mathMax$1 = Math.max;\n    var mathSin = Math.sin;\n    var mathCos = Math.cos;\n    var PI2 = Math.PI * 2;\n    var start = create();\n    var end = create();\n    var extremity = create();\n    function fromLine(x0, y0, x1, y1, min, max) {\n        min[0] = mathMin$1(x0, x1);\n        min[1] = mathMin$1(y0, y1);\n        max[0] = mathMax$1(x0, x1);\n        max[1] = mathMax$1(y0, y1);\n    }\n    var xDim = [];\n    var yDim = [];\n    function fromCubic(x0, y0, x1, y1, x2, y2, x3, y3, min, max) {\n        var cubicExtrema$1 = cubicExtrema;\n        var cubicAt$1 = cubicAt;\n        var n = cubicExtrema$1(x0, x1, x2, x3, xDim);\n        min[0] = Infinity;\n        min[1] = Infinity;\n        max[0] = -Infinity;\n        max[1] = -Infinity;\n        for (var i = 0; i < n; i++) {\n            var x = cubicAt$1(x0, x1, x2, x3, xDim[i]);\n            min[0] = mathMin$1(x, min[0]);\n            max[0] = mathMax$1(x, max[0]);\n        }\n        n = cubicExtrema$1(y0, y1, y2, y3, yDim);\n        for (var i = 0; i < n; i++) {\n            var y = cubicAt$1(y0, y1, y2, y3, yDim[i]);\n            min[1] = mathMin$1(y, min[1]);\n            max[1] = mathMax$1(y, max[1]);\n        }\n        min[0] = mathMin$1(x0, min[0]);\n        max[0] = mathMax$1(x0, max[0]);\n        min[0] = mathMin$1(x3, min[0]);\n        max[0] = mathMax$1(x3, max[0]);\n        min[1] = mathMin$1(y0, min[1]);\n        max[1] = mathMax$1(y0, max[1]);\n        min[1] = mathMin$1(y3, min[1]);\n        max[1] = mathMax$1(y3, max[1]);\n    }\n    function fromQuadratic(x0, y0, x1, y1, x2, y2, min, max) {\n        var quadraticExtremum$1 = quadraticExtremum;\n        var quadraticAt$1 = quadraticAt;\n        var tx = mathMax$1(mathMin$1(quadraticExtremum$1(x0, x1, x2), 1), 0);\n        var ty = mathMax$1(mathMin$1(quadraticExtremum$1(y0, y1, y2), 1), 0);\n        var x = quadraticAt$1(x0, x1, x2, tx);\n        var y = quadraticAt$1(y0, y1, y2, ty);\n        min[0] = mathMin$1(x0, x2, x);\n        min[1] = mathMin$1(y0, y2, y);\n        max[0] = mathMax$1(x0, x2, x);\n        max[1] = mathMax$1(y0, y2, y);\n    }\n    function fromArc(x, y, rx, ry, startAngle, endAngle, anticlockwise, min$1, max$1) {\n        var vec2Min = min;\n        var vec2Max = max;\n        var diff = Math.abs(startAngle - endAngle);\n        if (diff % PI2 < 1e-4 && diff > 1e-4) {\n            min$1[0] = x - rx;\n            min$1[1] = y - ry;\n            max$1[0] = x + rx;\n            max$1[1] = y + ry;\n            return;\n        }\n        start[0] = mathCos(startAngle) * rx + x;\n        start[1] = mathSin(startAngle) * ry + y;\n        end[0] = mathCos(endAngle) * rx + x;\n        end[1] = mathSin(endAngle) * ry + y;\n        vec2Min(min$1, start, end);\n        vec2Max(max$1, start, end);\n        startAngle = startAngle % (PI2);\n        if (startAngle < 0) {\n            startAngle = startAngle + PI2;\n        }\n        endAngle = endAngle % (PI2);\n        if (endAngle < 0) {\n            endAngle = endAngle + PI2;\n        }\n        if (startAngle > endAngle && !anticlockwise) {\n            endAngle += PI2;\n        }\n        else if (startAngle < endAngle && anticlockwise) {\n            startAngle += PI2;\n        }\n        if (anticlockwise) {\n            var tmp = endAngle;\n            endAngle = startAngle;\n            startAngle = tmp;\n        }\n        for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {\n            if (angle > startAngle) {\n                extremity[0] = mathCos(angle) * rx + x;\n                extremity[1] = mathSin(angle) * ry + y;\n                vec2Min(min$1, extremity, min$1);\n                vec2Max(max$1, extremity, max$1);\n            }\n        }\n    }\n\n    var CMD = {\n        M: 1,\n        L: 2,\n        C: 3,\n        Q: 4,\n        A: 5,\n        Z: 6,\n        R: 7\n    };\n    var tmpOutX = [];\n    var tmpOutY = [];\n    var min$1 = [];\n    var max$1 = [];\n    var min2 = [];\n    var max2 = [];\n    var mathMin$2 = Math.min;\n    var mathMax$2 = Math.max;\n    var mathCos$1 = Math.cos;\n    var mathSin$1 = Math.sin;\n    var mathAbs = Math.abs;\n    var PI = Math.PI;\n    var PI2$1 = PI * 2;\n    var hasTypedArray = typeof Float32Array !== 'undefined';\n    var tmpAngles = [];\n    function modPI2(radian) {\n        var n = Math.round(radian / PI * 1e8) / 1e8;\n        return (n % 2) * PI;\n    }\n    function normalizeArcAngles(angles, anticlockwise) {\n        var newStartAngle = modPI2(angles[0]);\n        if (newStartAngle < 0) {\n            newStartAngle += PI2$1;\n        }\n        var delta = newStartAngle - angles[0];\n        var newEndAngle = angles[1];\n        newEndAngle += delta;\n        if (!anticlockwise && newEndAngle - newStartAngle >= PI2$1) {\n            newEndAngle = newStartAngle + PI2$1;\n        }\n        else if (anticlockwise && newStartAngle - newEndAngle >= PI2$1) {\n            newEndAngle = newStartAngle - PI2$1;\n        }\n        else if (!anticlockwise && newStartAngle > newEndAngle) {\n            newEndAngle = newStartAngle + (PI2$1 - modPI2(newStartAngle - newEndAngle));\n        }\n        else if (anticlockwise && newStartAngle < newEndAngle) {\n            newEndAngle = newStartAngle - (PI2$1 - modPI2(newEndAngle - newStartAngle));\n        }\n        angles[0] = newStartAngle;\n        angles[1] = newEndAngle;\n    }\n    var PathProxy = (function () {\n        function PathProxy(notSaveData) {\n            this.dpr = 1;\n            this._xi = 0;\n            this._yi = 0;\n            this._x0 = 0;\n            this._y0 = 0;\n            this._len = 0;\n            if (notSaveData) {\n                this._saveData = false;\n            }\n            if (this._saveData) {\n                this.data = [];\n            }\n        }\n        PathProxy.prototype.increaseVersion = function () {\n            this._version++;\n        };\n        PathProxy.prototype.getVersion = function () {\n            return this._version;\n        };\n        PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) {\n            segmentIgnoreThreshold = segmentIgnoreThreshold || 0;\n            if (segmentIgnoreThreshold > 0) {\n                this._ux = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sx) || 0;\n                this._uy = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sy) || 0;\n            }\n        };\n        PathProxy.prototype.setDPR = function (dpr) {\n            this.dpr = dpr;\n        };\n        PathProxy.prototype.setContext = function (ctx) {\n            this._ctx = ctx;\n        };\n        PathProxy.prototype.getContext = function () {\n            return this._ctx;\n        };\n        PathProxy.prototype.beginPath = function () {\n            this._ctx && this._ctx.beginPath();\n            this.reset();\n            return this;\n        };\n        PathProxy.prototype.reset = function () {\n            if (this._saveData) {\n                this._len = 0;\n            }\n            if (this._pathSegLen) {\n                this._pathSegLen = null;\n                this._pathLen = 0;\n            }\n            this._version++;\n        };\n        PathProxy.prototype.moveTo = function (x, y) {\n            this._drawPendingPt();\n            this.addData(CMD.M, x, y);\n            this._ctx && this._ctx.moveTo(x, y);\n            this._x0 = x;\n            this._y0 = y;\n            this._xi = x;\n            this._yi = y;\n            return this;\n        };\n        PathProxy.prototype.lineTo = function (x, y) {\n            var dx = mathAbs(x - this._xi);\n            var dy = mathAbs(y - this._yi);\n            var exceedUnit = dx > this._ux || dy > this._uy;\n            this.addData(CMD.L, x, y);\n            if (this._ctx && exceedUnit) {\n                this._ctx.lineTo(x, y);\n            }\n            if (exceedUnit) {\n                this._xi = x;\n                this._yi = y;\n                this._pendingPtDist = 0;\n            }\n            else {\n                var d2 = dx * dx + dy * dy;\n                if (d2 > this._pendingPtDist) {\n                    this._pendingPtX = x;\n                    this._pendingPtY = y;\n                    this._pendingPtDist = d2;\n                }\n            }\n            return this;\n        };\n        PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) {\n            this._drawPendingPt();\n            this.addData(CMD.C, x1, y1, x2, y2, x3, y3);\n            if (this._ctx) {\n                this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n            }\n            this._xi = x3;\n            this._yi = y3;\n            return this;\n        };\n        PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) {\n            this._drawPendingPt();\n            this.addData(CMD.Q, x1, y1, x2, y2);\n            if (this._ctx) {\n                this._ctx.quadraticCurveTo(x1, y1, x2, y2);\n            }\n            this._xi = x2;\n            this._yi = y2;\n            return this;\n        };\n        PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n            this._drawPendingPt();\n            tmpAngles[0] = startAngle;\n            tmpAngles[1] = endAngle;\n            normalizeArcAngles(tmpAngles, anticlockwise);\n            startAngle = tmpAngles[0];\n            endAngle = tmpAngles[1];\n            var delta = endAngle - startAngle;\n            this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 0, anticlockwise ? 0 : 1);\n            this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n            this._xi = mathCos$1(endAngle) * r + cx;\n            this._yi = mathSin$1(endAngle) * r + cy;\n            return this;\n        };\n        PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) {\n            this._drawPendingPt();\n            if (this._ctx) {\n                this._ctx.arcTo(x1, y1, x2, y2, radius);\n            }\n            return this;\n        };\n        PathProxy.prototype.rect = function (x, y, w, h) {\n            this._drawPendingPt();\n            this._ctx && this._ctx.rect(x, y, w, h);\n            this.addData(CMD.R, x, y, w, h);\n            return this;\n        };\n        PathProxy.prototype.closePath = function () {\n            this._drawPendingPt();\n            this.addData(CMD.Z);\n            var ctx = this._ctx;\n            var x0 = this._x0;\n            var y0 = this._y0;\n            if (ctx) {\n                ctx.closePath();\n            }\n            this._xi = x0;\n            this._yi = y0;\n            return this;\n        };\n        PathProxy.prototype.fill = function (ctx) {\n            ctx && ctx.fill();\n            this.toStatic();\n        };\n        PathProxy.prototype.stroke = function (ctx) {\n            ctx && ctx.stroke();\n            this.toStatic();\n        };\n        PathProxy.prototype.len = function () {\n            return this._len;\n        };\n        PathProxy.prototype.setData = function (data) {\n            var len = data.length;\n            if (!(this.data && this.data.length === len) && hasTypedArray) {\n                this.data = new Float32Array(len);\n            }\n            for (var i = 0; i < len; i++) {\n                this.data[i] = data[i];\n            }\n            this._len = len;\n        };\n        PathProxy.prototype.appendPath = function (path) {\n            if (!(path instanceof Array)) {\n                path = [path];\n            }\n            var len = path.length;\n            var appendSize = 0;\n            var offset = this._len;\n            for (var i = 0; i < len; i++) {\n                appendSize += path[i].len();\n            }\n            if (hasTypedArray && (this.data instanceof Float32Array)) {\n                this.data = new Float32Array(offset + appendSize);\n            }\n            for (var i = 0; i < len; i++) {\n                var appendPathData = path[i].data;\n                for (var k = 0; k < appendPathData.length; k++) {\n                    this.data[offset++] = appendPathData[k];\n                }\n            }\n            this._len = offset;\n        };\n        PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) {\n            if (!this._saveData) {\n                return;\n            }\n            var data = this.data;\n            if (this._len + arguments.length > data.length) {\n                this._expandData();\n                data = this.data;\n            }\n            for (var i = 0; i < arguments.length; i++) {\n                data[this._len++] = arguments[i];\n            }\n        };\n        PathProxy.prototype._drawPendingPt = function () {\n            if (this._pendingPtDist > 0) {\n                this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY);\n                this._pendingPtDist = 0;\n            }\n        };\n        PathProxy.prototype._expandData = function () {\n            if (!(this.data instanceof Array)) {\n                var newData = [];\n                for (var i = 0; i < this._len; i++) {\n                    newData[i] = this.data[i];\n                }\n                this.data = newData;\n            }\n        };\n        PathProxy.prototype.toStatic = function () {\n            if (!this._saveData) {\n                return;\n            }\n            this._drawPendingPt();\n            var data = this.data;\n            if (data instanceof Array) {\n                data.length = this._len;\n                if (hasTypedArray && this._len > 11) {\n                    this.data = new Float32Array(data);\n                }\n            }\n        };\n        PathProxy.prototype.getBoundingRect = function () {\n            min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;\n            max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;\n            var data = this.data;\n            var xi = 0;\n            var yi = 0;\n            var x0 = 0;\n            var y0 = 0;\n            var i;\n            for (i = 0; i < this._len;) {\n                var cmd = data[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = data[i];\n                    yi = data[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                switch (cmd) {\n                    case CMD.M:\n                        xi = x0 = data[i++];\n                        yi = y0 = data[i++];\n                        min2[0] = x0;\n                        min2[1] = y0;\n                        max2[0] = x0;\n                        max2[1] = y0;\n                        break;\n                    case CMD.L:\n                        fromLine(xi, yi, data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.C:\n                        fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.Q:\n                        fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);\n                        xi = data[i++];\n                        yi = data[i++];\n                        break;\n                    case CMD.A:\n                        var cx = data[i++];\n                        var cy = data[i++];\n                        var rx = data[i++];\n                        var ry = data[i++];\n                        var startAngle = data[i++];\n                        var endAngle = data[i++] + startAngle;\n                        i += 1;\n                        var anticlockwise = !data[i++];\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R:\n                        x0 = xi = data[i++];\n                        y0 = yi = data[i++];\n                        var width = data[i++];\n                        var height = data[i++];\n                        fromLine(x0, y0, x0 + width, y0 + height, min2, max2);\n                        break;\n                    case CMD.Z:\n                        xi = x0;\n                        yi = y0;\n                        break;\n                }\n                min(min$1, min$1, min2);\n                max(max$1, max$1, max2);\n            }\n            if (i === 0) {\n                min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;\n            }\n            return new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]);\n        };\n        PathProxy.prototype._calculateLength = function () {\n            var data = this.data;\n            var len = this._len;\n            var ux = this._ux;\n            var uy = this._uy;\n            var xi = 0;\n            var yi = 0;\n            var x0 = 0;\n            var y0 = 0;\n            if (!this._pathSegLen) {\n                this._pathSegLen = [];\n            }\n            var pathSegLen = this._pathSegLen;\n            var pathTotalLen = 0;\n            var segCount = 0;\n            for (var i = 0; i < len;) {\n                var cmd = data[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = data[i];\n                    yi = data[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                var l = -1;\n                switch (cmd) {\n                    case CMD.M:\n                        xi = x0 = data[i++];\n                        yi = y0 = data[i++];\n                        break;\n                    case CMD.L: {\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        var dx = x2 - xi;\n                        var dy = y2 - yi;\n                        if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) {\n                            l = Math.sqrt(dx * dx + dy * dy);\n                            xi = x2;\n                            yi = y2;\n                        }\n                        break;\n                    }\n                    case CMD.C: {\n                        var x1 = data[i++];\n                        var y1 = data[i++];\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        var x3 = data[i++];\n                        var y3 = data[i++];\n                        l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10);\n                        xi = x3;\n                        yi = y3;\n                        break;\n                    }\n                    case CMD.Q: {\n                        var x1 = data[i++];\n                        var y1 = data[i++];\n                        var x2 = data[i++];\n                        var y2 = data[i++];\n                        l = quadraticLength(xi, yi, x1, y1, x2, y2, 10);\n                        xi = x2;\n                        yi = y2;\n                        break;\n                    }\n                    case CMD.A:\n                        var cx = data[i++];\n                        var cy = data[i++];\n                        var rx = data[i++];\n                        var ry = data[i++];\n                        var startAngle = data[i++];\n                        var delta = data[i++];\n                        var endAngle = delta + startAngle;\n                        i += 1;\n                        var anticlockwise = !data[i++];\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        l = mathMax$2(rx, ry) * mathMin$2(PI2$1, Math.abs(delta));\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R: {\n                        x0 = xi = data[i++];\n                        y0 = yi = data[i++];\n                        var width = data[i++];\n                        var height = data[i++];\n                        l = width * 2 + height * 2;\n                        break;\n                    }\n                    case CMD.Z: {\n                        var dx = x0 - xi;\n                        var dy = y0 - yi;\n                        l = Math.sqrt(dx * dx + dy * dy);\n                        xi = x0;\n                        yi = y0;\n                        break;\n                    }\n                }\n                if (l >= 0) {\n                    pathSegLen[segCount++] = l;\n                    pathTotalLen += l;\n                }\n            }\n            this._pathLen = pathTotalLen;\n            return pathTotalLen;\n        };\n        PathProxy.prototype.rebuildPath = function (ctx, percent) {\n            var d = this.data;\n            var ux = this._ux;\n            var uy = this._uy;\n            var len = this._len;\n            var x0;\n            var y0;\n            var xi;\n            var yi;\n            var x;\n            var y;\n            var drawPart = percent < 1;\n            var pathSegLen;\n            var pathTotalLen;\n            var accumLength = 0;\n            var segCount = 0;\n            var displayedLength;\n            var pendingPtDist = 0;\n            var pendingPtX;\n            var pendingPtY;\n            if (drawPart) {\n                if (!this._pathSegLen) {\n                    this._calculateLength();\n                }\n                pathSegLen = this._pathSegLen;\n                pathTotalLen = this._pathLen;\n                displayedLength = percent * pathTotalLen;\n                if (!displayedLength) {\n                    return;\n                }\n            }\n            lo: for (var i = 0; i < len;) {\n                var cmd = d[i++];\n                var isFirst = i === 1;\n                if (isFirst) {\n                    xi = d[i];\n                    yi = d[i + 1];\n                    x0 = xi;\n                    y0 = yi;\n                }\n                if (cmd !== CMD.L && pendingPtDist > 0) {\n                    ctx.lineTo(pendingPtX, pendingPtY);\n                    pendingPtDist = 0;\n                }\n                switch (cmd) {\n                    case CMD.M:\n                        x0 = xi = d[i++];\n                        y0 = yi = d[i++];\n                        ctx.moveTo(xi, yi);\n                        break;\n                    case CMD.L: {\n                        x = d[i++];\n                        y = d[i++];\n                        var dx = mathAbs(x - xi);\n                        var dy = mathAbs(y - yi);\n                        if (dx > ux || dy > uy) {\n                            if (drawPart) {\n                                var l = pathSegLen[segCount++];\n                                if (accumLength + l > displayedLength) {\n                                    var t = (displayedLength - accumLength) / l;\n                                    ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t);\n                                    break lo;\n                                }\n                                accumLength += l;\n                            }\n                            ctx.lineTo(x, y);\n                            xi = x;\n                            yi = y;\n                            pendingPtDist = 0;\n                        }\n                        else {\n                            var d2 = dx * dx + dy * dy;\n                            if (d2 > pendingPtDist) {\n                                pendingPtX = x;\n                                pendingPtY = y;\n                                pendingPtDist = d2;\n                            }\n                        }\n                        break;\n                    }\n                    case CMD.C: {\n                        var x1 = d[i++];\n                        var y1 = d[i++];\n                        var x2 = d[i++];\n                        var y2 = d[i++];\n                        var x3 = d[i++];\n                        var y3 = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                cubicSubdivide(xi, x1, x2, x3, t, tmpOutX);\n                                cubicSubdivide(yi, y1, y2, y3, t, tmpOutY);\n                                ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n                        xi = x3;\n                        yi = y3;\n                        break;\n                    }\n                    case CMD.Q: {\n                        var x1 = d[i++];\n                        var y1 = d[i++];\n                        var x2 = d[i++];\n                        var y2 = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                quadraticSubdivide(xi, x1, x2, t, tmpOutX);\n                                quadraticSubdivide(yi, y1, y2, t, tmpOutY);\n                                ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.quadraticCurveTo(x1, y1, x2, y2);\n                        xi = x2;\n                        yi = y2;\n                        break;\n                    }\n                    case CMD.A:\n                        var cx = d[i++];\n                        var cy = d[i++];\n                        var rx = d[i++];\n                        var ry = d[i++];\n                        var startAngle = d[i++];\n                        var delta = d[i++];\n                        var psi = d[i++];\n                        var anticlockwise = !d[i++];\n                        var r = (rx > ry) ? rx : ry;\n                        var isEllipse = mathAbs(rx - ry) > 1e-3;\n                        var endAngle = startAngle + delta;\n                        var breakBuild = false;\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                endAngle = startAngle + delta * (displayedLength - accumLength) / l;\n                                breakBuild = true;\n                            }\n                            accumLength += l;\n                        }\n                        if (isEllipse && ctx.ellipse) {\n                            ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise);\n                        }\n                        else {\n                            ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n                        }\n                        if (breakBuild) {\n                            break lo;\n                        }\n                        if (isFirst) {\n                            x0 = mathCos$1(startAngle) * rx + cx;\n                            y0 = mathSin$1(startAngle) * ry + cy;\n                        }\n                        xi = mathCos$1(endAngle) * rx + cx;\n                        yi = mathSin$1(endAngle) * ry + cy;\n                        break;\n                    case CMD.R:\n                        x0 = xi = d[i];\n                        y0 = yi = d[i + 1];\n                        x = d[i++];\n                        y = d[i++];\n                        var width = d[i++];\n                        var height = d[i++];\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var d_1 = displayedLength - accumLength;\n                                ctx.moveTo(x, y);\n                                ctx.lineTo(x + mathMin$2(d_1, width), y);\n                                d_1 -= width;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x + width, y + mathMin$2(d_1, height));\n                                }\n                                d_1 -= height;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x + mathMax$2(width - d_1, 0), y + height);\n                                }\n                                d_1 -= width;\n                                if (d_1 > 0) {\n                                    ctx.lineTo(x, y + mathMax$2(height - d_1, 0));\n                                }\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.rect(x, y, width, height);\n                        break;\n                    case CMD.Z:\n                        if (drawPart) {\n                            var l = pathSegLen[segCount++];\n                            if (accumLength + l > displayedLength) {\n                                var t = (displayedLength - accumLength) / l;\n                                ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t);\n                                break lo;\n                            }\n                            accumLength += l;\n                        }\n                        ctx.closePath();\n                        xi = x0;\n                        yi = y0;\n                }\n            }\n        };\n        PathProxy.prototype.clone = function () {\n            var newProxy = new PathProxy();\n            var data = this.data;\n            newProxy.data = data.slice ? data.slice()\n                : Array.prototype.slice.call(data);\n            newProxy._len = this._len;\n            return newProxy;\n        };\n        PathProxy.CMD = CMD;\n        PathProxy.initDefaultProps = (function () {\n            var proto = PathProxy.prototype;\n            proto._saveData = true;\n            proto._ux = 0;\n            proto._uy = 0;\n            proto._pendingPtDist = 0;\n            proto._version = 0;\n        })();\n        return PathProxy;\n    }());\n\n    function containStroke(x0, y0, x1, y1, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        var _a = 0;\n        var _b = x0;\n        if ((y > y0 + _l && y > y1 + _l)\n            || (y < y0 - _l && y < y1 - _l)\n            || (x > x0 + _l && x > x1 + _l)\n            || (x < x0 - _l && x < x1 - _l)) {\n            return false;\n        }\n        if (x0 !== x1) {\n            _a = (y0 - y1) / (x0 - x1);\n            _b = (x0 * y1 - x1 * y0) / (x0 - x1);\n        }\n        else {\n            return Math.abs(x - x0) <= _l / 2;\n        }\n        var tmp = _a * x - y + _b;\n        var _s = tmp * tmp / (_a * _a + 1);\n        return _s <= _l / 2 * _l / 2;\n    }\n\n    function containStroke$1(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        if ((y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)\n            || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)\n            || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)\n            || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)) {\n            return false;\n        }\n        var d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null);\n        return d <= _l / 2;\n    }\n\n    function containStroke$2(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        if ((y > y0 + _l && y > y1 + _l && y > y2 + _l)\n            || (y < y0 - _l && y < y1 - _l && y < y2 - _l)\n            || (x > x0 + _l && x > x1 + _l && x > x2 + _l)\n            || (x < x0 - _l && x < x1 - _l && x < x2 - _l)) {\n            return false;\n        }\n        var d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null);\n        return d <= _l / 2;\n    }\n\n    var PI2$2 = Math.PI * 2;\n    function normalizeRadian(angle) {\n        angle %= PI2$2;\n        if (angle < 0) {\n            angle += PI2$2;\n        }\n        return angle;\n    }\n\n    var PI2$3 = Math.PI * 2;\n    function containStroke$3(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) {\n        if (lineWidth === 0) {\n            return false;\n        }\n        var _l = lineWidth;\n        x -= cx;\n        y -= cy;\n        var d = Math.sqrt(x * x + y * y);\n        if ((d - _l > r) || (d + _l < r)) {\n            return false;\n        }\n        if (Math.abs(startAngle - endAngle) % PI2$3 < 1e-4) {\n            return true;\n        }\n        if (anticlockwise) {\n            var tmp = startAngle;\n            startAngle = normalizeRadian(endAngle);\n            endAngle = normalizeRadian(tmp);\n        }\n        else {\n            startAngle = normalizeRadian(startAngle);\n            endAngle = normalizeRadian(endAngle);\n        }\n        if (startAngle > endAngle) {\n            endAngle += PI2$3;\n        }\n        var angle = Math.atan2(y, x);\n        if (angle < 0) {\n            angle += PI2$3;\n        }\n        return (angle >= startAngle && angle <= endAngle)\n            || (angle + PI2$3 >= startAngle && angle + PI2$3 <= endAngle);\n    }\n\n    function windingLine(x0, y0, x1, y1, x, y) {\n        if ((y > y0 && y > y1) || (y < y0 && y < y1)) {\n            return 0;\n        }\n        if (y1 === y0) {\n            return 0;\n        }\n        var t = (y - y0) / (y1 - y0);\n        var dir = y1 < y0 ? 1 : -1;\n        if (t === 1 || t === 0) {\n            dir = y1 < y0 ? 0.5 : -0.5;\n        }\n        var x_ = t * (x1 - x0) + x0;\n        return x_ === x ? Infinity : x_ > x ? dir : 0;\n    }\n\n    var CMD$1 = PathProxy.CMD;\n    var PI2$4 = Math.PI * 2;\n    var EPSILON$2 = 1e-4;\n    function isAroundEqual(a, b) {\n        return Math.abs(a - b) < EPSILON$2;\n    }\n    var roots = [-1, -1, -1];\n    var extrema = [-1, -1];\n    function swapExtrema() {\n        var tmp = extrema[0];\n        extrema[0] = extrema[1];\n        extrema[1] = tmp;\n    }\n    function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {\n        if ((y > y0 && y > y1 && y > y2 && y > y3)\n            || (y < y0 && y < y1 && y < y2 && y < y3)) {\n            return 0;\n        }\n        var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);\n        if (nRoots === 0) {\n            return 0;\n        }\n        else {\n            var w = 0;\n            var nExtrema = -1;\n            var y0_ = void 0;\n            var y1_ = void 0;\n            for (var i = 0; i < nRoots; i++) {\n                var t = roots[i];\n                var unit = (t === 0 || t === 1) ? 0.5 : 1;\n                var x_ = cubicAt(x0, x1, x2, x3, t);\n                if (x_ < x) {\n                    continue;\n                }\n                if (nExtrema < 0) {\n                    nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);\n                    if (extrema[1] < extrema[0] && nExtrema > 1) {\n                        swapExtrema();\n                    }\n                    y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);\n                    if (nExtrema > 1) {\n                        y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);\n                    }\n                }\n                if (nExtrema === 2) {\n                    if (t < extrema[0]) {\n                        w += y0_ < y0 ? unit : -unit;\n                    }\n                    else if (t < extrema[1]) {\n                        w += y1_ < y0_ ? unit : -unit;\n                    }\n                    else {\n                        w += y3 < y1_ ? unit : -unit;\n                    }\n                }\n                else {\n                    if (t < extrema[0]) {\n                        w += y0_ < y0 ? unit : -unit;\n                    }\n                    else {\n                        w += y3 < y0_ ? unit : -unit;\n                    }\n                }\n            }\n            return w;\n        }\n    }\n    function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {\n        if ((y > y0 && y > y1 && y > y2)\n            || (y < y0 && y < y1 && y < y2)) {\n            return 0;\n        }\n        var nRoots = quadraticRootAt(y0, y1, y2, y, roots);\n        if (nRoots === 0) {\n            return 0;\n        }\n        else {\n            var t = quadraticExtremum(y0, y1, y2);\n            if (t >= 0 && t <= 1) {\n                var w = 0;\n                var y_ = quadraticAt(y0, y1, y2, t);\n                for (var i = 0; i < nRoots; i++) {\n                    var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;\n                    var x_ = quadraticAt(x0, x1, x2, roots[i]);\n                    if (x_ < x) {\n                        continue;\n                    }\n                    if (roots[i] < t) {\n                        w += y_ < y0 ? unit : -unit;\n                    }\n                    else {\n                        w += y2 < y_ ? unit : -unit;\n                    }\n                }\n                return w;\n            }\n            else {\n                var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;\n                var x_ = quadraticAt(x0, x1, x2, roots[0]);\n                if (x_ < x) {\n                    return 0;\n                }\n                return y2 < y0 ? unit : -unit;\n            }\n        }\n    }\n    function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {\n        y -= cy;\n        if (y > r || y < -r) {\n            return 0;\n        }\n        var tmp = Math.sqrt(r * r - y * y);\n        roots[0] = -tmp;\n        roots[1] = tmp;\n        var dTheta = Math.abs(startAngle - endAngle);\n        if (dTheta < 1e-4) {\n            return 0;\n        }\n        if (dTheta >= PI2$4 - 1e-4) {\n            startAngle = 0;\n            endAngle = PI2$4;\n            var dir = anticlockwise ? 1 : -1;\n            if (x >= roots[0] + cx && x <= roots[1] + cx) {\n                return dir;\n            }\n            else {\n                return 0;\n            }\n        }\n        if (startAngle > endAngle) {\n            var tmp_1 = startAngle;\n            startAngle = endAngle;\n            endAngle = tmp_1;\n        }\n        if (startAngle < 0) {\n            startAngle += PI2$4;\n            endAngle += PI2$4;\n        }\n        var w = 0;\n        for (var i = 0; i < 2; i++) {\n            var x_ = roots[i];\n            if (x_ + cx > x) {\n                var angle = Math.atan2(y, x_);\n                var dir = anticlockwise ? 1 : -1;\n                if (angle < 0) {\n                    angle = PI2$4 + angle;\n                }\n                if ((angle >= startAngle && angle <= endAngle)\n                    || (angle + PI2$4 >= startAngle && angle + PI2$4 <= endAngle)) {\n                    if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {\n                        dir = -dir;\n                    }\n                    w += dir;\n                }\n            }\n        }\n        return w;\n    }\n    function containPath(path, lineWidth, isStroke, x, y) {\n        var data = path.data;\n        var len = path.len();\n        var w = 0;\n        var xi = 0;\n        var yi = 0;\n        var x0 = 0;\n        var y0 = 0;\n        var x1;\n        var y1;\n        for (var i = 0; i < len;) {\n            var cmd = data[i++];\n            var isFirst = i === 1;\n            if (cmd === CMD$1.M && i > 1) {\n                if (!isStroke) {\n                    w += windingLine(xi, yi, x0, y0, x, y);\n                }\n            }\n            if (isFirst) {\n                xi = data[i];\n                yi = data[i + 1];\n                x0 = xi;\n                y0 = yi;\n            }\n            switch (cmd) {\n                case CMD$1.M:\n                    x0 = data[i++];\n                    y0 = data[i++];\n                    xi = x0;\n                    yi = y0;\n                    break;\n                case CMD$1.L:\n                    if (isStroke) {\n                        if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.C:\n                    if (isStroke) {\n                        if (containStroke$1(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.Q:\n                    if (isStroke) {\n                        if (containStroke$2(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;\n                    }\n                    xi = data[i++];\n                    yi = data[i++];\n                    break;\n                case CMD$1.A:\n                    var cx = data[i++];\n                    var cy = data[i++];\n                    var rx = data[i++];\n                    var ry = data[i++];\n                    var theta = data[i++];\n                    var dTheta = data[i++];\n                    i += 1;\n                    var anticlockwise = !!(1 - data[i++]);\n                    x1 = Math.cos(theta) * rx + cx;\n                    y1 = Math.sin(theta) * ry + cy;\n                    if (!isFirst) {\n                        w += windingLine(xi, yi, x1, y1, x, y);\n                    }\n                    else {\n                        x0 = x1;\n                        y0 = y1;\n                    }\n                    var _x = (x - cx) * ry / rx + cx;\n                    if (isStroke) {\n                        if (containStroke$3(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);\n                    }\n                    xi = Math.cos(theta + dTheta) * rx + cx;\n                    yi = Math.sin(theta + dTheta) * ry + cy;\n                    break;\n                case CMD$1.R:\n                    x0 = xi = data[i++];\n                    y0 = yi = data[i++];\n                    var width = data[i++];\n                    var height = data[i++];\n                    x1 = x0 + width;\n                    y1 = y0 + height;\n                    if (isStroke) {\n                        if (containStroke(x0, y0, x1, y0, lineWidth, x, y)\n                            || containStroke(x1, y0, x1, y1, lineWidth, x, y)\n                            || containStroke(x1, y1, x0, y1, lineWidth, x, y)\n                            || containStroke(x0, y1, x0, y0, lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(x1, y0, x1, y1, x, y);\n                        w += windingLine(x0, y1, x0, y0, x, y);\n                    }\n                    break;\n                case CMD$1.Z:\n                    if (isStroke) {\n                        if (containStroke(xi, yi, x0, y0, lineWidth, x, y)) {\n                            return true;\n                        }\n                    }\n                    else {\n                        w += windingLine(xi, yi, x0, y0, x, y);\n                    }\n                    xi = x0;\n                    yi = y0;\n                    break;\n            }\n        }\n        if (!isStroke && !isAroundEqual(yi, y0)) {\n            w += windingLine(xi, yi, x0, y0, x, y) || 0;\n        }\n        return w !== 0;\n    }\n    function contain(pathProxy, x, y) {\n        return containPath(pathProxy, 0, false, x, y);\n    }\n    function containStroke$4(pathProxy, lineWidth, x, y) {\n        return containPath(pathProxy, lineWidth, true, x, y);\n    }\n\n    var DEFAULT_PATH_STYLE = defaults({\n        fill: '#000',\n        stroke: null,\n        strokePercent: 1,\n        fillOpacity: 1,\n        strokeOpacity: 1,\n        lineDashOffset: 0,\n        lineWidth: 1,\n        lineCap: 'butt',\n        miterLimit: 10,\n        strokeNoScale: false,\n        strokeFirst: false\n    }, DEFAULT_COMMON_STYLE);\n    var DEFAULT_PATH_ANIMATION_PROPS = {\n        style: defaults({\n            fill: true,\n            stroke: true,\n            strokePercent: true,\n            fillOpacity: true,\n            strokeOpacity: true,\n            lineDashOffset: true,\n            lineWidth: true,\n            miterLimit: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    var pathCopyParams = TRANSFORMABLE_PROPS.concat(['invisible',\n        'culling', 'z', 'z2', 'zlevel', 'parent'\n    ]);\n    var Path = (function (_super) {\n        __extends(Path, _super);\n        function Path(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Path.prototype.update = function () {\n            var _this = this;\n            _super.prototype.update.call(this);\n            var style = this.style;\n            if (style.decal) {\n                var decalEl = this._decalEl = this._decalEl || new Path();\n                if (decalEl.buildPath === Path.prototype.buildPath) {\n                    decalEl.buildPath = function (ctx) {\n                        _this.buildPath(ctx, _this.shape);\n                    };\n                }\n                decalEl.silent = true;\n                var decalElStyle = decalEl.style;\n                for (var key in style) {\n                    if (decalElStyle[key] !== style[key]) {\n                        decalElStyle[key] = style[key];\n                    }\n                }\n                decalElStyle.fill = style.fill ? style.decal : null;\n                decalElStyle.decal = null;\n                decalElStyle.shadowColor = null;\n                style.strokeFirst && (decalElStyle.stroke = null);\n                for (var i = 0; i < pathCopyParams.length; ++i) {\n                    decalEl[pathCopyParams[i]] = this[pathCopyParams[i]];\n                }\n                decalEl.__dirty |= REDRAW_BIT;\n            }\n            else if (this._decalEl) {\n                this._decalEl = null;\n            }\n        };\n        Path.prototype.getDecalElement = function () {\n            return this._decalEl;\n        };\n        Path.prototype._init = function (props) {\n            var keysArr = keys(props);\n            this.shape = this.getDefaultShape();\n            var defaultStyle = this.getDefaultStyle();\n            if (defaultStyle) {\n                this.useStyle(defaultStyle);\n            }\n            for (var i = 0; i < keysArr.length; i++) {\n                var key = keysArr[i];\n                var value = props[key];\n                if (key === 'style') {\n                    if (!this.style) {\n                        this.useStyle(value);\n                    }\n                    else {\n                        extend(this.style, value);\n                    }\n                }\n                else if (key === 'shape') {\n                    extend(this.shape, value);\n                }\n                else {\n                    _super.prototype.attrKV.call(this, key, value);\n                }\n            }\n            if (!this.style) {\n                this.useStyle({});\n            }\n        };\n        Path.prototype.getDefaultStyle = function () {\n            return null;\n        };\n        Path.prototype.getDefaultShape = function () {\n            return {};\n        };\n        Path.prototype.canBeInsideText = function () {\n            return this.hasFill();\n        };\n        Path.prototype.getInsideTextFill = function () {\n            var pathFill = this.style.fill;\n            if (pathFill !== 'none') {\n                if (isString(pathFill)) {\n                    var fillLum = lum(pathFill, 0);\n                    if (fillLum > 0.5) {\n                        return DARK_LABEL_COLOR;\n                    }\n                    else if (fillLum > 0.2) {\n                        return LIGHTER_LABEL_COLOR;\n                    }\n                    return LIGHT_LABEL_COLOR;\n                }\n                else if (pathFill) {\n                    return LIGHT_LABEL_COLOR;\n                }\n            }\n            return DARK_LABEL_COLOR;\n        };\n        Path.prototype.getInsideTextStroke = function (textFill) {\n            var pathFill = this.style.fill;\n            if (isString(pathFill)) {\n                var zr = this.__zr;\n                var isDarkMode = !!(zr && zr.isDarkMode());\n                var isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD;\n                if (isDarkMode === isDarkLabel) {\n                    return pathFill;\n                }\n            }\n        };\n        Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { };\n        Path.prototype.pathUpdated = function () {\n            this.__dirty &= ~SHAPE_CHANGED_BIT;\n        };\n        Path.prototype.getUpdatedPathProxy = function (inBatch) {\n            !this.path && this.createPathProxy();\n            this.path.beginPath();\n            this.buildPath(this.path, this.shape, inBatch);\n            return this.path;\n        };\n        Path.prototype.createPathProxy = function () {\n            this.path = new PathProxy(false);\n        };\n        Path.prototype.hasStroke = function () {\n            var style = this.style;\n            var stroke = style.stroke;\n            return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n        };\n        Path.prototype.hasFill = function () {\n            var style = this.style;\n            var fill = style.fill;\n            return fill != null && fill !== 'none';\n        };\n        Path.prototype.getBoundingRect = function () {\n            var rect = this._rect;\n            var style = this.style;\n            var needsUpdateRect = !rect;\n            if (needsUpdateRect) {\n                var firstInvoke = false;\n                if (!this.path) {\n                    firstInvoke = true;\n                    this.createPathProxy();\n                }\n                var path = this.path;\n                if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) {\n                    path.beginPath();\n                    this.buildPath(path, this.shape, false);\n                    this.pathUpdated();\n                }\n                rect = path.getBoundingRect();\n            }\n            this._rect = rect;\n            if (this.hasStroke() && this.path && this.path.len() > 0) {\n                var rectStroke = this._rectStroke || (this._rectStroke = rect.clone());\n                if (this.__dirty || needsUpdateRect) {\n                    rectStroke.copy(rect);\n                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                    var w = style.lineWidth;\n                    if (!this.hasFill()) {\n                        var strokeContainThreshold = this.strokeContainThreshold;\n                        w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold);\n                    }\n                    if (lineScale > 1e-10) {\n                        rectStroke.width += w / lineScale;\n                        rectStroke.height += w / lineScale;\n                        rectStroke.x -= w / lineScale / 2;\n                        rectStroke.y -= w / lineScale / 2;\n                    }\n                }\n                return rectStroke;\n            }\n            return rect;\n        };\n        Path.prototype.contain = function (x, y) {\n            var localPos = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            var style = this.style;\n            x = localPos[0];\n            y = localPos[1];\n            if (rect.contain(x, y)) {\n                var pathProxy = this.path;\n                if (this.hasStroke()) {\n                    var lineWidth = style.lineWidth;\n                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n                    if (lineScale > 1e-10) {\n                        if (!this.hasFill()) {\n                            lineWidth = Math.max(lineWidth, this.strokeContainThreshold);\n                        }\n                        if (containStroke$4(pathProxy, lineWidth / lineScale, x, y)) {\n                            return true;\n                        }\n                    }\n                }\n                if (this.hasFill()) {\n                    return contain(pathProxy, x, y);\n                }\n            }\n            return false;\n        };\n        Path.prototype.dirtyShape = function () {\n            this.__dirty |= SHAPE_CHANGED_BIT;\n            if (this._rect) {\n                this._rect = null;\n            }\n            if (this._decalEl) {\n                this._decalEl.dirtyShape();\n            }\n            this.markRedraw();\n        };\n        Path.prototype.dirty = function () {\n            this.dirtyStyle();\n            this.dirtyShape();\n        };\n        Path.prototype.animateShape = function (loop) {\n            return this.animate('shape', loop);\n        };\n        Path.prototype.updateDuringAnimation = function (targetKey) {\n            if (targetKey === 'style') {\n                this.dirtyStyle();\n            }\n            else if (targetKey === 'shape') {\n                this.dirtyShape();\n            }\n            else {\n                this.markRedraw();\n            }\n        };\n        Path.prototype.attrKV = function (key, value) {\n            if (key === 'shape') {\n                this.setShape(value);\n            }\n            else {\n                _super.prototype.attrKV.call(this, key, value);\n            }\n        };\n        Path.prototype.setShape = function (keyOrObj, value) {\n            var shape = this.shape;\n            if (!shape) {\n                shape = this.shape = {};\n            }\n            if (typeof keyOrObj === 'string') {\n                shape[keyOrObj] = value;\n            }\n            else {\n                extend(shape, keyOrObj);\n            }\n            this.dirtyShape();\n            return this;\n        };\n        Path.prototype.shapeChanged = function () {\n            return !!(this.__dirty & SHAPE_CHANGED_BIT);\n        };\n        Path.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_PATH_STYLE, obj);\n        };\n        Path.prototype._innerSaveToNormal = function (toState) {\n            _super.prototype._innerSaveToNormal.call(this, toState);\n            var normalState = this._normalState;\n            if (toState.shape && !normalState.shape) {\n                normalState.shape = extend({}, this.shape);\n            }\n        };\n        Path.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {\n            _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);\n            var needsRestoreToNormal = !(state && keepCurrentStates);\n            var targetShape;\n            if (state && state.shape) {\n                if (transition) {\n                    if (keepCurrentStates) {\n                        targetShape = state.shape;\n                    }\n                    else {\n                        targetShape = extend({}, normalState.shape);\n                        extend(targetShape, state.shape);\n                    }\n                }\n                else {\n                    targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape);\n                    extend(targetShape, state.shape);\n                }\n            }\n            else if (needsRestoreToNormal) {\n                targetShape = normalState.shape;\n            }\n            if (targetShape) {\n                if (transition) {\n                    this.shape = extend({}, this.shape);\n                    var targetShapePrimaryProps = {};\n                    var shapeKeys = keys(targetShape);\n                    for (var i = 0; i < shapeKeys.length; i++) {\n                        var key = shapeKeys[i];\n                        if (typeof targetShape[key] === 'object') {\n                            this.shape[key] = targetShape[key];\n                        }\n                        else {\n                            targetShapePrimaryProps[key] = targetShape[key];\n                        }\n                    }\n                    this._transitionState(stateName, {\n                        shape: targetShapePrimaryProps\n                    }, animationCfg);\n                }\n                else {\n                    this.shape = targetShape;\n                    this.dirtyShape();\n                }\n            }\n        };\n        Path.prototype._mergeStates = function (states) {\n            var mergedState = _super.prototype._mergeStates.call(this, states);\n            var mergedShape;\n            for (var i = 0; i < states.length; i++) {\n                var state = states[i];\n                if (state.shape) {\n                    mergedShape = mergedShape || {};\n                    this._mergeStyle(mergedShape, state.shape);\n                }\n            }\n            if (mergedShape) {\n                mergedState.shape = mergedShape;\n            }\n            return mergedState;\n        };\n        Path.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_PATH_ANIMATION_PROPS;\n        };\n        Path.prototype.isZeroArea = function () {\n            return false;\n        };\n        Path.extend = function (defaultProps) {\n            var Sub = (function (_super) {\n                __extends(Sub, _super);\n                function Sub(opts) {\n                    var _this = _super.call(this, opts) || this;\n                    defaultProps.init && defaultProps.init.call(_this, opts);\n                    return _this;\n                }\n                Sub.prototype.getDefaultStyle = function () {\n                    return clone(defaultProps.style);\n                };\n                Sub.prototype.getDefaultShape = function () {\n                    return clone(defaultProps.shape);\n                };\n                return Sub;\n            }(Path));\n            for (var key in defaultProps) {\n                if (typeof defaultProps[key] === 'function') {\n                    Sub.prototype[key] = defaultProps[key];\n                }\n            }\n            return Sub;\n        };\n        Path.initDefaultProps = (function () {\n            var pathProto = Path.prototype;\n            pathProto.type = 'path';\n            pathProto.strokeContainThreshold = 5;\n            pathProto.segmentIgnoreThreshold = 0;\n            pathProto.subPixelOptimize = false;\n            pathProto.autoBatch = false;\n            pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT;\n        })();\n        return Path;\n    }(Displayable));\n\n    var DEFAULT_TSPAN_STYLE = defaults({\n        strokeFirst: true,\n        font: DEFAULT_FONT,\n        x: 0,\n        y: 0,\n        textAlign: 'left',\n        textBaseline: 'top',\n        miterLimit: 2\n    }, DEFAULT_PATH_STYLE);\n    var TSpan = (function (_super) {\n        __extends(TSpan, _super);\n        function TSpan() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        TSpan.prototype.hasStroke = function () {\n            var style = this.style;\n            var stroke = style.stroke;\n            return stroke != null && stroke !== 'none' && style.lineWidth > 0;\n        };\n        TSpan.prototype.hasFill = function () {\n            var style = this.style;\n            var fill = style.fill;\n            return fill != null && fill !== 'none';\n        };\n        TSpan.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_TSPAN_STYLE, obj);\n        };\n        TSpan.prototype.setBoundingRect = function (rect) {\n            this._rect = rect;\n        };\n        TSpan.prototype.getBoundingRect = function () {\n            var style = this.style;\n            if (!this._rect) {\n                var text = style.text;\n                text != null ? (text += '') : (text = '');\n                var rect = getBoundingRect(text, style.font, style.textAlign, style.textBaseline);\n                rect.x += style.x || 0;\n                rect.y += style.y || 0;\n                if (this.hasStroke()) {\n                    var w = style.lineWidth;\n                    rect.x -= w / 2;\n                    rect.y -= w / 2;\n                    rect.width += w;\n                    rect.height += w;\n                }\n                this._rect = rect;\n            }\n            return this._rect;\n        };\n        TSpan.initDefaultProps = (function () {\n            var tspanProto = TSpan.prototype;\n            tspanProto.dirtyRectTolerance = 10;\n        })();\n        return TSpan;\n    }(Displayable));\n    TSpan.prototype.type = 'tspan';\n\n    var DEFAULT_IMAGE_STYLE = defaults({\n        x: 0,\n        y: 0\n    }, DEFAULT_COMMON_STYLE);\n    var DEFAULT_IMAGE_ANIMATION_PROPS = {\n        style: defaults({\n            x: true,\n            y: true,\n            width: true,\n            height: true,\n            sx: true,\n            sy: true,\n            sWidth: true,\n            sHeight: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    function isImageLike(source) {\n        return !!(source\n            && typeof source !== 'string'\n            && source.width && source.height);\n    }\n    var ZRImage = (function (_super) {\n        __extends(ZRImage, _super);\n        function ZRImage() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        ZRImage.prototype.createStyle = function (obj) {\n            return createObject(DEFAULT_IMAGE_STYLE, obj);\n        };\n        ZRImage.prototype._getSize = function (dim) {\n            var style = this.style;\n            var size = style[dim];\n            if (size != null) {\n                return size;\n            }\n            var imageSource = isImageLike(style.image)\n                ? style.image : this.__image;\n            if (!imageSource) {\n                return 0;\n            }\n            var otherDim = dim === 'width' ? 'height' : 'width';\n            var otherDimSize = style[otherDim];\n            if (otherDimSize == null) {\n                return imageSource[dim];\n            }\n            else {\n                return imageSource[dim] / imageSource[otherDim] * otherDimSize;\n            }\n        };\n        ZRImage.prototype.getWidth = function () {\n            return this._getSize('width');\n        };\n        ZRImage.prototype.getHeight = function () {\n            return this._getSize('height');\n        };\n        ZRImage.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_IMAGE_ANIMATION_PROPS;\n        };\n        ZRImage.prototype.getBoundingRect = function () {\n            var style = this.style;\n            if (!this._rect) {\n                this._rect = new BoundingRect(style.x || 0, style.y || 0, this.getWidth(), this.getHeight());\n            }\n            return this._rect;\n        };\n        return ZRImage;\n    }(Displayable));\n    ZRImage.prototype.type = 'image';\n\n    function buildPath(ctx, shape) {\n        var x = shape.x;\n        var y = shape.y;\n        var width = shape.width;\n        var height = shape.height;\n        var r = shape.r;\n        var r1;\n        var r2;\n        var r3;\n        var r4;\n        if (width < 0) {\n            x = x + width;\n            width = -width;\n        }\n        if (height < 0) {\n            y = y + height;\n            height = -height;\n        }\n        if (typeof r === 'number') {\n            r1 = r2 = r3 = r4 = r;\n        }\n        else if (r instanceof Array) {\n            if (r.length === 1) {\n                r1 = r2 = r3 = r4 = r[0];\n            }\n            else if (r.length === 2) {\n                r1 = r3 = r[0];\n                r2 = r4 = r[1];\n            }\n            else if (r.length === 3) {\n                r1 = r[0];\n                r2 = r4 = r[1];\n                r3 = r[2];\n            }\n            else {\n                r1 = r[0];\n                r2 = r[1];\n                r3 = r[2];\n                r4 = r[3];\n            }\n        }\n        else {\n            r1 = r2 = r3 = r4 = 0;\n        }\n        var total;\n        if (r1 + r2 > width) {\n            total = r1 + r2;\n            r1 *= width / total;\n            r2 *= width / total;\n        }\n        if (r3 + r4 > width) {\n            total = r3 + r4;\n            r3 *= width / total;\n            r4 *= width / total;\n        }\n        if (r2 + r3 > height) {\n            total = r2 + r3;\n            r2 *= height / total;\n            r3 *= height / total;\n        }\n        if (r1 + r4 > height) {\n            total = r1 + r4;\n            r1 *= height / total;\n            r4 *= height / total;\n        }\n        ctx.moveTo(x + r1, y);\n        ctx.lineTo(x + width - r2, y);\n        r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0);\n        ctx.lineTo(x + width, y + height - r3);\n        r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2);\n        ctx.lineTo(x + r4, y + height);\n        r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI);\n        ctx.lineTo(x, y + r1);\n        r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5);\n    }\n\n    var round$1 = Math.round;\n    function subPixelOptimizeLine(outputShape, inputShape, style) {\n        if (!inputShape) {\n            return;\n        }\n        var x1 = inputShape.x1;\n        var x2 = inputShape.x2;\n        var y1 = inputShape.y1;\n        var y2 = inputShape.y2;\n        outputShape.x1 = x1;\n        outputShape.x2 = x2;\n        outputShape.y1 = y1;\n        outputShape.y2 = y2;\n        var lineWidth = style && style.lineWidth;\n        if (!lineWidth) {\n            return outputShape;\n        }\n        if (round$1(x1 * 2) === round$1(x2 * 2)) {\n            outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true);\n        }\n        if (round$1(y1 * 2) === round$1(y2 * 2)) {\n            outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true);\n        }\n        return outputShape;\n    }\n    function subPixelOptimizeRect(outputShape, inputShape, style) {\n        if (!inputShape) {\n            return;\n        }\n        var originX = inputShape.x;\n        var originY = inputShape.y;\n        var originWidth = inputShape.width;\n        var originHeight = inputShape.height;\n        outputShape.x = originX;\n        outputShape.y = originY;\n        outputShape.width = originWidth;\n        outputShape.height = originHeight;\n        var lineWidth = style && style.lineWidth;\n        if (!lineWidth) {\n            return outputShape;\n        }\n        outputShape.x = subPixelOptimize(originX, lineWidth, true);\n        outputShape.y = subPixelOptimize(originY, lineWidth, true);\n        outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1);\n        outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1);\n        return outputShape;\n    }\n    function subPixelOptimize(position, lineWidth, positiveOrNegative) {\n        if (!lineWidth) {\n            return position;\n        }\n        var doubledPosition = round$1(position * 2);\n        return (doubledPosition + round$1(lineWidth)) % 2 === 0\n            ? doubledPosition / 2\n            : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;\n    }\n\n    var RectShape = (function () {\n        function RectShape() {\n            this.x = 0;\n            this.y = 0;\n            this.width = 0;\n            this.height = 0;\n        }\n        return RectShape;\n    }());\n    var subPixelOptimizeOutputShape = {};\n    var Rect = (function (_super) {\n        __extends(Rect, _super);\n        function Rect(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Rect.prototype.getDefaultShape = function () {\n            return new RectShape();\n        };\n        Rect.prototype.buildPath = function (ctx, shape) {\n            var x;\n            var y;\n            var width;\n            var height;\n            if (this.subPixelOptimize) {\n                var optimizedShape = subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style);\n                x = optimizedShape.x;\n                y = optimizedShape.y;\n                width = optimizedShape.width;\n                height = optimizedShape.height;\n                optimizedShape.r = shape.r;\n                shape = optimizedShape;\n            }\n            else {\n                x = shape.x;\n                y = shape.y;\n                width = shape.width;\n                height = shape.height;\n            }\n            if (!shape.r) {\n                ctx.rect(x, y, width, height);\n            }\n            else {\n                buildPath(ctx, shape);\n            }\n        };\n        Rect.prototype.isZeroArea = function () {\n            return !this.shape.width || !this.shape.height;\n        };\n        return Rect;\n    }(Path));\n    Rect.prototype.type = 'rect';\n\n    var DEFAULT_RICH_TEXT_COLOR = {\n        fill: '#000'\n    };\n    var DEFAULT_STROKE_LINE_WIDTH = 2;\n    var DEFAULT_TEXT_ANIMATION_PROPS = {\n        style: defaults({\n            fill: true,\n            stroke: true,\n            fillOpacity: true,\n            strokeOpacity: true,\n            lineWidth: true,\n            fontSize: true,\n            lineHeight: true,\n            width: true,\n            height: true,\n            textShadowColor: true,\n            textShadowBlur: true,\n            textShadowOffsetX: true,\n            textShadowOffsetY: true,\n            backgroundColor: true,\n            padding: true,\n            borderColor: true,\n            borderWidth: true,\n            borderRadius: true\n        }, DEFAULT_COMMON_ANIMATION_PROPS.style)\n    };\n    var ZRText = (function (_super) {\n        __extends(ZRText, _super);\n        function ZRText(opts) {\n            var _this = _super.call(this) || this;\n            _this.type = 'text';\n            _this._children = [];\n            _this._defaultStyle = DEFAULT_RICH_TEXT_COLOR;\n            _this.attr(opts);\n            return _this;\n        }\n        ZRText.prototype.childrenRef = function () {\n            return this._children;\n        };\n        ZRText.prototype.update = function () {\n            _super.prototype.update.call(this);\n            if (this.styleChanged()) {\n                this._updateSubTexts();\n            }\n            for (var i = 0; i < this._children.length; i++) {\n                var child = this._children[i];\n                child.zlevel = this.zlevel;\n                child.z = this.z;\n                child.z2 = this.z2;\n                child.culling = this.culling;\n                child.cursor = this.cursor;\n                child.invisible = this.invisible;\n            }\n        };\n        ZRText.prototype.updateTransform = function () {\n            var innerTransformable = this.innerTransformable;\n            if (innerTransformable) {\n                innerTransformable.updateTransform();\n                if (innerTransformable.transform) {\n                    this.transform = innerTransformable.transform;\n                }\n            }\n            else {\n                _super.prototype.updateTransform.call(this);\n            }\n        };\n        ZRText.prototype.getLocalTransform = function (m) {\n            var innerTransformable = this.innerTransformable;\n            return innerTransformable\n                ? innerTransformable.getLocalTransform(m)\n                : _super.prototype.getLocalTransform.call(this, m);\n        };\n        ZRText.prototype.getComputedTransform = function () {\n            if (this.__hostTarget) {\n                this.__hostTarget.getComputedTransform();\n                this.__hostTarget.updateInnerText(true);\n            }\n            return _super.prototype.getComputedTransform.call(this);\n        };\n        ZRText.prototype._updateSubTexts = function () {\n            this._childCursor = 0;\n            normalizeTextStyle(this.style);\n            this.style.rich\n                ? this._updateRichTexts()\n                : this._updatePlainTexts();\n            this._children.length = this._childCursor;\n            this.styleUpdated();\n        };\n        ZRText.prototype.addSelfToZr = function (zr) {\n            _super.prototype.addSelfToZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                this._children[i].__zr = zr;\n            }\n        };\n        ZRText.prototype.removeSelfFromZr = function (zr) {\n            _super.prototype.removeSelfFromZr.call(this, zr);\n            for (var i = 0; i < this._children.length; i++) {\n                this._children[i].__zr = null;\n            }\n        };\n        ZRText.prototype.getBoundingRect = function () {\n            if (this.styleChanged()) {\n                this._updateSubTexts();\n            }\n            if (!this._rect) {\n                var tmpRect = new BoundingRect(0, 0, 0, 0);\n                var children = this._children;\n                var tmpMat = [];\n                var rect = null;\n                for (var i = 0; i < children.length; i++) {\n                    var child = children[i];\n                    var childRect = child.getBoundingRect();\n                    var transform = child.getLocalTransform(tmpMat);\n                    if (transform) {\n                        tmpRect.copy(childRect);\n                        tmpRect.applyTransform(transform);\n                        rect = rect || tmpRect.clone();\n                        rect.union(tmpRect);\n                    }\n                    else {\n                        rect = rect || childRect.clone();\n                        rect.union(childRect);\n                    }\n                }\n                this._rect = rect || tmpRect;\n            }\n            return this._rect;\n        };\n        ZRText.prototype.setDefaultTextStyle = function (defaultTextStyle) {\n            this._defaultStyle = defaultTextStyle || DEFAULT_RICH_TEXT_COLOR;\n        };\n        ZRText.prototype.setTextContent = function (textContent) {\n            if (\"development\" !== 'production') {\n                throw new Error('Can\\'t attach text on another text');\n            }\n        };\n        ZRText.prototype._mergeStyle = function (targetStyle, sourceStyle) {\n            if (!sourceStyle) {\n                return targetStyle;\n            }\n            var sourceRich = sourceStyle.rich;\n            var targetRich = targetStyle.rich || (sourceRich && {});\n            extend(targetStyle, sourceStyle);\n            if (sourceRich && targetRich) {\n                this._mergeRich(targetRich, sourceRich);\n                targetStyle.rich = targetRich;\n            }\n            else if (targetRich) {\n                targetStyle.rich = targetRich;\n            }\n            return targetStyle;\n        };\n        ZRText.prototype._mergeRich = function (targetRich, sourceRich) {\n            var richNames = keys(sourceRich);\n            for (var i = 0; i < richNames.length; i++) {\n                var richName = richNames[i];\n                targetRich[richName] = targetRich[richName] || {};\n                extend(targetRich[richName], sourceRich[richName]);\n            }\n        };\n        ZRText.prototype.getAnimationStyleProps = function () {\n            return DEFAULT_TEXT_ANIMATION_PROPS;\n        };\n        ZRText.prototype._getOrCreateChild = function (Ctor) {\n            var child = this._children[this._childCursor];\n            if (!child || !(child instanceof Ctor)) {\n                child = new Ctor();\n            }\n            this._children[this._childCursor++] = child;\n            child.__zr = this.__zr;\n            child.parent = this;\n            return child;\n        };\n        ZRText.prototype._updatePlainTexts = function () {\n            var style = this.style;\n            var textFont = style.font || DEFAULT_FONT;\n            var textPadding = style.padding;\n            var text = getStyleText(style);\n            var contentBlock = parsePlainText(text, style);\n            var needDrawBg = needDrawBackground(style);\n            var bgColorDrawn = !!(style.backgroundColor);\n            var outerHeight = contentBlock.outerHeight;\n            var outerWidth = contentBlock.outerWidth;\n            var contentWidth = contentBlock.contentWidth;\n            var textLines = contentBlock.lines;\n            var lineHeight = contentBlock.lineHeight;\n            var defaultStyle = this._defaultStyle;\n            var baseX = style.x || 0;\n            var baseY = style.y || 0;\n            var textAlign = style.align || defaultStyle.align || 'left';\n            var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign || 'top';\n            var textX = baseX;\n            var textY = adjustTextY(baseY, contentBlock.contentHeight, verticalAlign);\n            if (needDrawBg || textPadding) {\n                var boxX = adjustTextX(baseX, outerWidth, textAlign);\n                var boxY = adjustTextY(baseY, outerHeight, verticalAlign);\n                needDrawBg && this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n            }\n            textY += lineHeight / 2;\n            if (textPadding) {\n                textX = getTextXForPadding(baseX, textAlign, textPadding);\n                if (verticalAlign === 'top') {\n                    textY += textPadding[0];\n                }\n                else if (verticalAlign === 'bottom') {\n                    textY -= textPadding[2];\n                }\n            }\n            var defaultLineWidth = 0;\n            var useDefaultFill = false;\n            var textFill = getFill('fill' in style\n                ? style.fill\n                : (useDefaultFill = true, defaultStyle.fill));\n            var textStroke = getStroke('stroke' in style\n                ? style.stroke\n                : (!bgColorDrawn\n                    && (!defaultStyle.autoStroke || useDefaultFill))\n                    ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                    : null);\n            var hasShadow = style.textShadowBlur > 0;\n            var fixedBoundingRect = style.width != null\n                && (style.overflow === 'truncate' || style.overflow === 'break' || style.overflow === 'breakAll');\n            var calculatedLineHeight = contentBlock.calculatedLineHeight;\n            for (var i = 0; i < textLines.length; i++) {\n                var el = this._getOrCreateChild(TSpan);\n                var subElStyle = el.createStyle();\n                el.useStyle(subElStyle);\n                subElStyle.text = textLines[i];\n                subElStyle.x = textX;\n                subElStyle.y = textY;\n                if (textAlign) {\n                    subElStyle.textAlign = textAlign;\n                }\n                subElStyle.textBaseline = 'middle';\n                subElStyle.opacity = style.opacity;\n                subElStyle.strokeFirst = true;\n                if (hasShadow) {\n                    subElStyle.shadowBlur = style.textShadowBlur || 0;\n                    subElStyle.shadowColor = style.textShadowColor || 'transparent';\n                    subElStyle.shadowOffsetX = style.textShadowOffsetX || 0;\n                    subElStyle.shadowOffsetY = style.textShadowOffsetY || 0;\n                }\n                subElStyle.stroke = textStroke;\n                subElStyle.fill = textFill;\n                if (textStroke) {\n                    subElStyle.lineWidth = style.lineWidth || defaultLineWidth;\n                    subElStyle.lineDash = style.lineDash;\n                    subElStyle.lineDashOffset = style.lineDashOffset || 0;\n                }\n                subElStyle.font = textFont;\n                setSeparateFont(subElStyle, style);\n                textY += lineHeight;\n                if (fixedBoundingRect) {\n                    el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, style.width, subElStyle.textAlign), adjustTextY(subElStyle.y, calculatedLineHeight, subElStyle.textBaseline), contentWidth, calculatedLineHeight));\n                }\n            }\n        };\n        ZRText.prototype._updateRichTexts = function () {\n            var style = this.style;\n            var text = getStyleText(style);\n            var contentBlock = parseRichText(text, style);\n            var contentWidth = contentBlock.width;\n            var outerWidth = contentBlock.outerWidth;\n            var outerHeight = contentBlock.outerHeight;\n            var textPadding = style.padding;\n            var baseX = style.x || 0;\n            var baseY = style.y || 0;\n            var defaultStyle = this._defaultStyle;\n            var textAlign = style.align || defaultStyle.align;\n            var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign;\n            var boxX = adjustTextX(baseX, outerWidth, textAlign);\n            var boxY = adjustTextY(baseY, outerHeight, verticalAlign);\n            var xLeft = boxX;\n            var lineTop = boxY;\n            if (textPadding) {\n                xLeft += textPadding[3];\n                lineTop += textPadding[0];\n            }\n            var xRight = xLeft + contentWidth;\n            if (needDrawBackground(style)) {\n                this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight);\n            }\n            var bgColorDrawn = !!(style.backgroundColor);\n            for (var i = 0; i < contentBlock.lines.length; i++) {\n                var line = contentBlock.lines[i];\n                var tokens = line.tokens;\n                var tokenCount = tokens.length;\n                var lineHeight = line.lineHeight;\n                var remainedWidth = line.width;\n                var leftIndex = 0;\n                var lineXLeft = xLeft;\n                var lineXRight = xRight;\n                var rightIndex = tokenCount - 1;\n                var token = void 0;\n                while (leftIndex < tokenCount\n                    && (token = tokens[leftIndex], !token.align || token.align === 'left')) {\n                    this._placeToken(token, style, lineHeight, lineTop, lineXLeft, 'left', bgColorDrawn);\n                    remainedWidth -= token.width;\n                    lineXLeft += token.width;\n                    leftIndex++;\n                }\n                while (rightIndex >= 0\n                    && (token = tokens[rightIndex], token.align === 'right')) {\n                    this._placeToken(token, style, lineHeight, lineTop, lineXRight, 'right', bgColorDrawn);\n                    remainedWidth -= token.width;\n                    lineXRight -= token.width;\n                    rightIndex--;\n                }\n                lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - remainedWidth) / 2;\n                while (leftIndex <= rightIndex) {\n                    token = tokens[leftIndex];\n                    this._placeToken(token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center', bgColorDrawn);\n                    lineXLeft += token.width;\n                    leftIndex++;\n                }\n                lineTop += lineHeight;\n            }\n        };\n        ZRText.prototype._placeToken = function (token, style, lineHeight, lineTop, x, textAlign, parentBgColorDrawn) {\n            var tokenStyle = style.rich[token.styleName] || {};\n            tokenStyle.text = token.text;\n            var verticalAlign = token.verticalAlign;\n            var y = lineTop + lineHeight / 2;\n            if (verticalAlign === 'top') {\n                y = lineTop + token.height / 2;\n            }\n            else if (verticalAlign === 'bottom') {\n                y = lineTop + lineHeight - token.height / 2;\n            }\n            var needDrawBg = !token.isLineHolder && needDrawBackground(tokenStyle);\n            needDrawBg && this._renderBackground(tokenStyle, style, textAlign === 'right'\n                ? x - token.width\n                : textAlign === 'center'\n                    ? x - token.width / 2\n                    : x, y - token.height / 2, token.width, token.height);\n            var bgColorDrawn = !!tokenStyle.backgroundColor;\n            var textPadding = token.textPadding;\n            if (textPadding) {\n                x = getTextXForPadding(x, textAlign, textPadding);\n                y -= token.height / 2 - textPadding[0] - token.innerHeight / 2;\n            }\n            var el = this._getOrCreateChild(TSpan);\n            var subElStyle = el.createStyle();\n            el.useStyle(subElStyle);\n            var defaultStyle = this._defaultStyle;\n            var useDefaultFill = false;\n            var defaultLineWidth = 0;\n            var textFill = getFill('fill' in tokenStyle ? tokenStyle.fill\n                : 'fill' in style ? style.fill\n                    : (useDefaultFill = true, defaultStyle.fill));\n            var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke\n                : 'stroke' in style ? style.stroke\n                    : (!bgColorDrawn\n                        && !parentBgColorDrawn\n                        && (!defaultStyle.autoStroke || useDefaultFill)) ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke)\n                        : null);\n            var hasShadow = tokenStyle.textShadowBlur > 0\n                || style.textShadowBlur > 0;\n            subElStyle.text = token.text;\n            subElStyle.x = x;\n            subElStyle.y = y;\n            if (hasShadow) {\n                subElStyle.shadowBlur = tokenStyle.textShadowBlur || style.textShadowBlur || 0;\n                subElStyle.shadowColor = tokenStyle.textShadowColor || style.textShadowColor || 'transparent';\n                subElStyle.shadowOffsetX = tokenStyle.textShadowOffsetX || style.textShadowOffsetX || 0;\n                subElStyle.shadowOffsetY = tokenStyle.textShadowOffsetY || style.textShadowOffsetY || 0;\n            }\n            subElStyle.textAlign = textAlign;\n            subElStyle.textBaseline = 'middle';\n            subElStyle.font = token.font || DEFAULT_FONT;\n            subElStyle.opacity = retrieve3(tokenStyle.opacity, style.opacity, 1);\n            setSeparateFont(subElStyle, tokenStyle);\n            if (textStroke) {\n                subElStyle.lineWidth = retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultLineWidth);\n                subElStyle.lineDash = retrieve2(tokenStyle.lineDash, style.lineDash);\n                subElStyle.lineDashOffset = style.lineDashOffset || 0;\n                subElStyle.stroke = textStroke;\n            }\n            if (textFill) {\n                subElStyle.fill = textFill;\n            }\n            var textWidth = token.contentWidth;\n            var textHeight = token.contentHeight;\n            el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, textWidth, subElStyle.textAlign), adjustTextY(subElStyle.y, textHeight, subElStyle.textBaseline), textWidth, textHeight));\n        };\n        ZRText.prototype._renderBackground = function (style, topStyle, x, y, width, height) {\n            var textBackgroundColor = style.backgroundColor;\n            var textBorderWidth = style.borderWidth;\n            var textBorderColor = style.borderColor;\n            var isImageBg = textBackgroundColor && textBackgroundColor.image;\n            var isPlainOrGradientBg = textBackgroundColor && !isImageBg;\n            var textBorderRadius = style.borderRadius;\n            var self = this;\n            var rectEl;\n            var imgEl;\n            if (isPlainOrGradientBg || style.lineHeight || (textBorderWidth && textBorderColor)) {\n                rectEl = this._getOrCreateChild(Rect);\n                rectEl.useStyle(rectEl.createStyle());\n                rectEl.style.fill = null;\n                var rectShape = rectEl.shape;\n                rectShape.x = x;\n                rectShape.y = y;\n                rectShape.width = width;\n                rectShape.height = height;\n                rectShape.r = textBorderRadius;\n                rectEl.dirtyShape();\n            }\n            if (isPlainOrGradientBg) {\n                var rectStyle = rectEl.style;\n                rectStyle.fill = textBackgroundColor || null;\n                rectStyle.fillOpacity = retrieve2(style.fillOpacity, 1);\n            }\n            else if (isImageBg) {\n                imgEl = this._getOrCreateChild(ZRImage);\n                imgEl.onload = function () {\n                    self.dirtyStyle();\n                };\n                var imgStyle = imgEl.style;\n                imgStyle.image = textBackgroundColor.image;\n                imgStyle.x = x;\n                imgStyle.y = y;\n                imgStyle.width = width;\n                imgStyle.height = height;\n            }\n            if (textBorderWidth && textBorderColor) {\n                var rectStyle = rectEl.style;\n                rectStyle.lineWidth = textBorderWidth;\n                rectStyle.stroke = textBorderColor;\n                rectStyle.strokeOpacity = retrieve2(style.strokeOpacity, 1);\n                rectStyle.lineDash = style.borderDash;\n                rectStyle.lineDashOffset = style.borderDashOffset || 0;\n                rectEl.strokeContainThreshold = 0;\n                if (rectEl.hasFill() && rectEl.hasStroke()) {\n                    rectStyle.strokeFirst = true;\n                    rectStyle.lineWidth *= 2;\n                }\n            }\n            var commonStyle = (rectEl || imgEl).style;\n            commonStyle.shadowBlur = style.shadowBlur || 0;\n            commonStyle.shadowColor = style.shadowColor || 'transparent';\n            commonStyle.shadowOffsetX = style.shadowOffsetX || 0;\n            commonStyle.shadowOffsetY = style.shadowOffsetY || 0;\n            commonStyle.opacity = retrieve3(style.opacity, topStyle.opacity, 1);\n        };\n        ZRText.makeFont = function (style) {\n            var font = '';\n            if (hasSeparateFont(style)) {\n                font = [\n                    style.fontStyle,\n                    style.fontWeight,\n                    parseFontSize(style.fontSize),\n                    style.fontFamily || 'sans-serif'\n                ].join(' ');\n            }\n            return font && trim(font) || style.textFont || style.font;\n        };\n        return ZRText;\n    }(Displayable));\n    var VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 };\n    var VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 };\n    var FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];\n    function parseFontSize(fontSize) {\n        if (typeof fontSize === 'string'\n            && (fontSize.indexOf('px') !== -1\n                || fontSize.indexOf('rem') !== -1\n                || fontSize.indexOf('em') !== -1)) {\n            return fontSize;\n        }\n        else if (!isNaN(+fontSize)) {\n            return fontSize + 'px';\n        }\n        else {\n            return DEFAULT_FONT_SIZE + 'px';\n        }\n    }\n    function setSeparateFont(targetStyle, sourceStyle) {\n        for (var i = 0; i < FONT_PARTS.length; i++) {\n            var fontProp = FONT_PARTS[i];\n            var val = sourceStyle[fontProp];\n            if (val != null) {\n                targetStyle[fontProp] = val;\n            }\n        }\n    }\n    function hasSeparateFont(style) {\n        return style.fontSize != null || style.fontFamily || style.fontWeight;\n    }\n    function normalizeTextStyle(style) {\n        normalizeStyle(style);\n        each(style.rich, normalizeStyle);\n        return style;\n    }\n    function normalizeStyle(style) {\n        if (style) {\n            style.font = ZRText.makeFont(style);\n            var textAlign = style.align;\n            textAlign === 'middle' && (textAlign = 'center');\n            style.align = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left';\n            var verticalAlign = style.verticalAlign;\n            verticalAlign === 'center' && (verticalAlign = 'middle');\n            style.verticalAlign = (verticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[verticalAlign]) ? verticalAlign : 'top';\n            var textPadding = style.padding;\n            if (textPadding) {\n                style.padding = normalizeCssArray(style.padding);\n            }\n        }\n    }\n    function getStroke(stroke, lineWidth) {\n        return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')\n            ? null\n            : (stroke.image || stroke.colorStops)\n                ? '#000'\n                : stroke;\n    }\n    function getFill(fill) {\n        return (fill == null || fill === 'none')\n            ? null\n            : (fill.image || fill.colorStops)\n                ? '#000'\n                : fill;\n    }\n    function getTextXForPadding(x, textAlign, textPadding) {\n        return textAlign === 'right'\n            ? (x - textPadding[1])\n            : textAlign === 'center'\n                ? (x + textPadding[3] / 2 - textPadding[1] / 2)\n                : (x + textPadding[3]);\n    }\n    function getStyleText(style) {\n        var text = style.text;\n        text != null && (text += '');\n        return text;\n    }\n    function needDrawBackground(style) {\n        return !!(style.backgroundColor\n            || style.lineHeight\n            || (style.borderWidth && style.borderColor));\n    }\n\n    var getECData = makeInner();\n    var setCommonECData = function (seriesIndex, dataType, dataIdx, el) {\n      if (el) {\n        var ecData = getECData(el); // Add data index and series index for indexing the data by element\n        // Useful in tooltip\n\n        ecData.dataIndex = dataIdx;\n        ecData.dataType = dataType;\n        ecData.seriesIndex = seriesIndex; // TODO: not store dataIndex on children.\n\n        if (el.type === 'group') {\n          el.traverse(function (child) {\n            var childECData = getECData(child);\n            childECData.seriesIndex = seriesIndex;\n            childECData.dataIndex = dataIdx;\n            childECData.dataType = dataType;\n          });\n        }\n      }\n    };\n\n    var _highlightNextDigit = 1;\n    var _highlightKeyMap = {};\n    var getSavedStates = makeInner();\n    var getComponentStates = makeInner();\n    var HOVER_STATE_NORMAL = 0;\n    var HOVER_STATE_BLUR = 1;\n    var HOVER_STATE_EMPHASIS = 2;\n    var SPECIAL_STATES = ['emphasis', 'blur', 'select'];\n    var DISPLAY_STATES = ['normal', 'emphasis', 'blur', 'select'];\n    var Z2_EMPHASIS_LIFT = 10;\n    var Z2_SELECT_LIFT = 9;\n    var HIGHLIGHT_ACTION_TYPE = 'highlight';\n    var DOWNPLAY_ACTION_TYPE = 'downplay';\n    var SELECT_ACTION_TYPE = 'select';\n    var UNSELECT_ACTION_TYPE = 'unselect';\n    var TOGGLE_SELECT_ACTION_TYPE = 'toggleSelect';\n\n    function hasFillOrStroke(fillOrStroke) {\n      return fillOrStroke != null && fillOrStroke !== 'none';\n    } // Most lifted color are duplicated.\n\n\n    var liftedColorCache = new LRU(100);\n\n    function liftColor(color$1) {\n      if (isString(color$1)) {\n        var liftedColor = liftedColorCache.get(color$1);\n\n        if (!liftedColor) {\n          liftedColor = lift(color$1, -0.1);\n          liftedColorCache.put(color$1, liftedColor);\n        }\n\n        return liftedColor;\n      } else if (isGradientObject(color$1)) {\n        var ret = extend({}, color$1);\n        ret.colorStops = map(color$1.colorStops, function (stop) {\n          return {\n            offset: stop.offset,\n            color: lift(stop.color, -0.1)\n          };\n        });\n        return ret;\n      } // Change nothing.\n\n\n      return color$1;\n    }\n\n    function doChangeHoverState(el, stateName, hoverStateEnum) {\n      if (el.onHoverStateChange && (el.hoverState || 0) !== hoverStateEnum) {\n        el.onHoverStateChange(stateName);\n      }\n\n      el.hoverState = hoverStateEnum;\n    }\n\n    function singleEnterEmphasis(el) {\n      // Only mark the flag.\n      // States will be applied in the echarts.ts in next frame.\n      doChangeHoverState(el, 'emphasis', HOVER_STATE_EMPHASIS);\n    }\n\n    function singleLeaveEmphasis(el) {\n      // Only mark the flag.\n      // States will be applied in the echarts.ts in next frame.\n      if (el.hoverState === HOVER_STATE_EMPHASIS) {\n        doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n      }\n    }\n\n    function singleEnterBlur(el) {\n      doChangeHoverState(el, 'blur', HOVER_STATE_BLUR);\n    }\n\n    function singleLeaveBlur(el) {\n      if (el.hoverState === HOVER_STATE_BLUR) {\n        doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);\n      }\n    }\n\n    function singleEnterSelect(el) {\n      el.selected = true;\n    }\n\n    function singleLeaveSelect(el) {\n      el.selected = false;\n    }\n\n    function updateElementState(el, updater, commonParam) {\n      updater(el, commonParam);\n    }\n\n    function traverseUpdateState(el, updater, commonParam) {\n      updateElementState(el, updater, commonParam);\n      el.isGroup && el.traverse(function (child) {\n        updateElementState(child, updater, commonParam);\n      });\n    }\n\n    function setStatesFlag(el, stateName) {\n      switch (stateName) {\n        case 'emphasis':\n          el.hoverState = HOVER_STATE_EMPHASIS;\n          break;\n\n        case 'normal':\n          el.hoverState = HOVER_STATE_NORMAL;\n          break;\n\n        case 'blur':\n          el.hoverState = HOVER_STATE_BLUR;\n          break;\n\n        case 'select':\n          el.selected = true;\n      }\n    }\n\n    function getFromStateStyle(el, props, toStateName, defaultValue) {\n      var style = el.style;\n      var fromState = {};\n\n      for (var i = 0; i < props.length; i++) {\n        var propName = props[i];\n        var val = style[propName];\n        fromState[propName] = val == null ? defaultValue && defaultValue[propName] : val;\n      }\n\n      for (var i = 0; i < el.animators.length; i++) {\n        var animator = el.animators[i];\n\n        if (animator.__fromStateTransition // Don't consider the animation to emphasis state.\n        && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') {\n          animator.saveTo(fromState, props);\n        }\n      }\n\n      return fromState;\n    }\n\n    function createEmphasisDefaultState(el, stateName, targetStates, state) {\n      var hasSelect = targetStates && indexOf(targetStates, 'select') >= 0;\n      var cloned = false;\n\n      if (el instanceof Path) {\n        var store = getSavedStates(el);\n        var fromFill = hasSelect ? store.selectFill || store.normalFill : store.normalFill;\n        var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke;\n\n        if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) {\n          state = state || {};\n          var emphasisStyle = state.style || {}; // inherit case\n\n          if (emphasisStyle.fill === 'inherit') {\n            cloned = true;\n            state = extend({}, state);\n            emphasisStyle = extend({}, emphasisStyle);\n            emphasisStyle.fill = fromFill;\n          } // Apply default color lift\n          else if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) {\n              cloned = true; // Not modify the original value.\n\n              state = extend({}, state);\n              emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times.\n\n              emphasisStyle.fill = liftColor(fromFill);\n            } // Not highlight stroke if fill has been highlighted.\n            else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) {\n                if (!cloned) {\n                  state = extend({}, state);\n                  emphasisStyle = extend({}, emphasisStyle);\n                }\n\n                emphasisStyle.stroke = liftColor(fromStroke);\n              }\n\n          state.style = emphasisStyle;\n        }\n      }\n\n      if (state) {\n        // TODO Share with textContent?\n        if (state.z2 == null) {\n          if (!cloned) {\n            state = extend({}, state);\n          }\n\n          var z2EmphasisLift = el.z2EmphasisLift;\n          state.z2 = el.z2 + (z2EmphasisLift != null ? z2EmphasisLift : Z2_EMPHASIS_LIFT);\n        }\n      }\n\n      return state;\n    }\n\n    function createSelectDefaultState(el, stateName, state) {\n      // const hasSelect = indexOf(el.currentStates, stateName) >= 0;\n      if (state) {\n        // TODO Share with textContent?\n        if (state.z2 == null) {\n          state = extend({}, state);\n          var z2SelectLift = el.z2SelectLift;\n          state.z2 = el.z2 + (z2SelectLift != null ? z2SelectLift : Z2_SELECT_LIFT);\n        }\n      }\n\n      return state;\n    }\n\n    function createBlurDefaultState(el, stateName, state) {\n      var hasBlur = indexOf(el.currentStates, stateName) >= 0;\n      var currentOpacity = el.style.opacity;\n      var fromState = !hasBlur ? getFromStateStyle(el, ['opacity'], stateName, {\n        opacity: 1\n      }) : null;\n      state = state || {};\n      var blurStyle = state.style || {};\n\n      if (blurStyle.opacity == null) {\n        // clone state\n        state = extend({}, state);\n        blurStyle = extend({\n          // Already being applied 'emphasis'. DON'T mul opacity multiple times.\n          opacity: hasBlur ? currentOpacity : fromState.opacity * 0.1\n        }, blurStyle);\n        state.style = blurStyle;\n      }\n\n      return state;\n    }\n\n    function elementStateProxy(stateName, targetStates) {\n      var state = this.states[stateName];\n\n      if (this.style) {\n        if (stateName === 'emphasis') {\n          return createEmphasisDefaultState(this, stateName, targetStates, state);\n        } else if (stateName === 'blur') {\n          return createBlurDefaultState(this, stateName, state);\n        } else if (stateName === 'select') {\n          return createSelectDefaultState(this, stateName, state);\n        }\n      }\n\n      return state;\n    }\n    /**\n     * Set hover style (namely \"emphasis style\") of element.\n     * @param el Should not be `zrender/graphic/Group`.\n     * @param focus 'self' | 'selfInSeries' | 'series'\n     */\n\n\n    function setDefaultStateProxy(el) {\n      el.stateProxy = elementStateProxy;\n      var textContent = el.getTextContent();\n      var textGuide = el.getTextGuideLine();\n\n      if (textContent) {\n        textContent.stateProxy = elementStateProxy;\n      }\n\n      if (textGuide) {\n        textGuide.stateProxy = elementStateProxy;\n      }\n    }\n    function enterEmphasisWhenMouseOver(el, e) {\n      !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n      && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis);\n    }\n    function leaveEmphasisWhenMouseOut(el, e) {\n      !shouldSilent(el, e) // \"emphasis\" event highlight has higher priority than mouse highlight.\n      && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis);\n    }\n    function enterEmphasis(el, highlightDigit) {\n      el.__highByOuter |= 1 << (highlightDigit || 0);\n      traverseUpdateState(el, singleEnterEmphasis);\n    }\n    function leaveEmphasis(el, highlightDigit) {\n      !(el.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdateState(el, singleLeaveEmphasis);\n    }\n    function enterBlur(el) {\n      traverseUpdateState(el, singleEnterBlur);\n    }\n    function leaveBlur(el) {\n      traverseUpdateState(el, singleLeaveBlur);\n    }\n    function enterSelect(el) {\n      traverseUpdateState(el, singleEnterSelect);\n    }\n    function leaveSelect(el) {\n      traverseUpdateState(el, singleLeaveSelect);\n    }\n\n    function shouldSilent(el, e) {\n      return el.__highDownSilentOnTouch && e.zrByTouch;\n    }\n\n    function allLeaveBlur(api) {\n      var model = api.getModel();\n      var leaveBlurredSeries = [];\n      var allComponentViews = [];\n      model.eachComponent(function (componentType, componentModel) {\n        var componentStates = getComponentStates(componentModel);\n        var isSeries = componentType === 'series';\n        var view = isSeries ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel);\n        !isSeries && allComponentViews.push(view);\n\n        if (componentStates.isBlured) {\n          // Leave blur anyway\n          view.group.traverse(function (child) {\n            singleLeaveBlur(child);\n          });\n          isSeries && leaveBlurredSeries.push(componentModel);\n        }\n\n        componentStates.isBlured = false;\n      });\n      each(allComponentViews, function (view) {\n        if (view && view.toggleBlurSeries) {\n          view.toggleBlurSeries(leaveBlurredSeries, false, model);\n        }\n      });\n    }\n    function blurSeries(targetSeriesIndex, focus, blurScope, api) {\n      var ecModel = api.getModel();\n      blurScope = blurScope || 'coordinateSystem';\n\n      function leaveBlurOfIndices(data, dataIndices) {\n        for (var i = 0; i < dataIndices.length; i++) {\n          var itemEl = data.getItemGraphicEl(dataIndices[i]);\n          itemEl && leaveBlur(itemEl);\n        }\n      }\n\n      if (targetSeriesIndex == null) {\n        return;\n      }\n\n      if (!focus || focus === 'none') {\n        return;\n      }\n\n      var targetSeriesModel = ecModel.getSeriesByIndex(targetSeriesIndex);\n      var targetCoordSys = targetSeriesModel.coordinateSystem;\n\n      if (targetCoordSys && targetCoordSys.master) {\n        targetCoordSys = targetCoordSys.master;\n      }\n\n      var blurredSeries = [];\n      ecModel.eachSeries(function (seriesModel) {\n        var sameSeries = targetSeriesModel === seriesModel;\n        var coordSys = seriesModel.coordinateSystem;\n\n        if (coordSys && coordSys.master) {\n          coordSys = coordSys.master;\n        }\n\n        var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries; // If there is no coordinate system. use sameSeries instead.\n\n        if (!( // Not blur other series if blurScope series\n        blurScope === 'series' && !sameSeries // Not blur other coordinate system if blurScope is coordinateSystem\n        || blurScope === 'coordinateSystem' && !sameCoordSys // Not blur self series if focus is series.\n        || focus === 'series' && sameSeries // TODO blurScope: coordinate system\n        )) {\n          var view = api.getViewOfSeriesModel(seriesModel);\n          view.group.traverse(function (child) {\n            singleEnterBlur(child);\n          });\n\n          if (isArrayLike(focus)) {\n            leaveBlurOfIndices(seriesModel.getData(), focus);\n          } else if (isObject(focus)) {\n            var dataTypes = keys(focus);\n\n            for (var d = 0; d < dataTypes.length; d++) {\n              leaveBlurOfIndices(seriesModel.getData(dataTypes[d]), focus[dataTypes[d]]);\n            }\n          }\n\n          blurredSeries.push(seriesModel);\n          getComponentStates(seriesModel).isBlured = true;\n        }\n      });\n      ecModel.eachComponent(function (componentType, componentModel) {\n        if (componentType === 'series') {\n          return;\n        }\n\n        var view = api.getViewOfComponentModel(componentModel);\n\n        if (view && view.toggleBlurSeries) {\n          view.toggleBlurSeries(blurredSeries, true, ecModel);\n        }\n      });\n    }\n    function blurComponent(componentMainType, componentIndex, api) {\n      if (componentMainType == null || componentIndex == null) {\n        return;\n      }\n\n      var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n      if (!componentModel) {\n        return;\n      }\n\n      getComponentStates(componentModel).isBlured = true;\n      var view = api.getViewOfComponentModel(componentModel);\n\n      if (!view || !view.focusBlurEnabled) {\n        return;\n      }\n\n      view.group.traverse(function (child) {\n        singleEnterBlur(child);\n      });\n    }\n    function blurSeriesFromHighlightPayload(seriesModel, payload, api) {\n      var seriesIndex = seriesModel.seriesIndex;\n      var data = seriesModel.getData(payload.dataType);\n\n      if (!data) {\n        if (\"development\" !== 'production') {\n          error(\"Unknown dataType \" + payload.dataType);\n        }\n\n        return;\n      }\n\n      var dataIndex = queryDataIndex(data, payload); // Pick the first one if there is multiple/none exists.\n\n      dataIndex = (isArray(dataIndex) ? dataIndex[0] : dataIndex) || 0;\n      var el = data.getItemGraphicEl(dataIndex);\n\n      if (!el) {\n        var count = data.count();\n        var current = 0; // If data on dataIndex is NaN.\n\n        while (!el && current < count) {\n          el = data.getItemGraphicEl(current++);\n        }\n      }\n\n      if (el) {\n        var ecData = getECData(el);\n        blurSeries(seriesIndex, ecData.focus, ecData.blurScope, api);\n      } else {\n        // If there is no element put on the data. Try getting it from raw option\n        // TODO Should put it on seriesModel?\n        var focus_1 = seriesModel.get(['emphasis', 'focus']);\n        var blurScope = seriesModel.get(['emphasis', 'blurScope']);\n\n        if (focus_1 != null) {\n          blurSeries(seriesIndex, focus_1, blurScope, api);\n        }\n      }\n    }\n    function findComponentHighDownDispatchers(componentMainType, componentIndex, name, api) {\n      var ret = {\n        focusSelf: false,\n        dispatchers: null\n      };\n\n      if (componentMainType == null || componentMainType === 'series' || componentIndex == null || name == null) {\n        return ret;\n      }\n\n      var componentModel = api.getModel().getComponent(componentMainType, componentIndex);\n\n      if (!componentModel) {\n        return ret;\n      }\n\n      var view = api.getViewOfComponentModel(componentModel);\n\n      if (!view || !view.findHighDownDispatchers) {\n        return ret;\n      }\n\n      var dispatchers = view.findHighDownDispatchers(name); // At presnet, the component (like Geo) only blur inside itself.\n      // So we do not use `blurScope` in component.\n\n      var focusSelf;\n\n      for (var i = 0; i < dispatchers.length; i++) {\n        if (\"development\" !== 'production' && !isHighDownDispatcher(dispatchers[i])) {\n          error('param should be highDownDispatcher');\n        }\n\n        if (getECData(dispatchers[i]).focus === 'self') {\n          focusSelf = true;\n          break;\n        }\n      }\n\n      return {\n        focusSelf: focusSelf,\n        dispatchers: dispatchers\n      };\n    }\n    function handleGlobalMouseOverForHighDown(dispatcher, e, api) {\n      if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n        error('param should be highDownDispatcher');\n      }\n\n      var ecData = getECData(dispatcher);\n\n      var _a = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api),\n          dispatchers = _a.dispatchers,\n          focusSelf = _a.focusSelf; // If `findHighDownDispatchers` is supported on the component,\n      // highlight/downplay elements with the same name.\n\n\n      if (dispatchers) {\n        if (focusSelf) {\n          blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n        }\n\n        each(dispatchers, function (dispatcher) {\n          return enterEmphasisWhenMouseOver(dispatcher, e);\n        });\n      } else {\n        // Try blur all in the related series. Then emphasis the hoverred.\n        // TODO. progressive mode.\n        blurSeries(ecData.seriesIndex, ecData.focus, ecData.blurScope, api);\n\n        if (ecData.focus === 'self') {\n          blurComponent(ecData.componentMainType, ecData.componentIndex, api);\n        } // Other than series, component that not support `findHighDownDispatcher` will\n        // also use it. But in this case, highlight/downplay are only supported in\n        // mouse hover but not in dispatchAction.\n\n\n        enterEmphasisWhenMouseOver(dispatcher, e);\n      }\n    }\n    function handleGlobalMouseOutForHighDown(dispatcher, e, api) {\n      if (\"development\" !== 'production' && !isHighDownDispatcher(dispatcher)) {\n        error('param should be highDownDispatcher');\n      }\n\n      allLeaveBlur(api);\n      var ecData = getECData(dispatcher);\n      var dispatchers = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api).dispatchers;\n\n      if (dispatchers) {\n        each(dispatchers, function (dispatcher) {\n          return leaveEmphasisWhenMouseOut(dispatcher, e);\n        });\n      } else {\n        leaveEmphasisWhenMouseOut(dispatcher, e);\n      }\n    }\n    function toggleSelectionFromPayload(seriesModel, payload, api) {\n      if (!isSelectChangePayload(payload)) {\n        return;\n      }\n\n      var dataType = payload.dataType;\n      var data = seriesModel.getData(dataType);\n      var dataIndex = queryDataIndex(data, payload);\n\n      if (!isArray(dataIndex)) {\n        dataIndex = [dataIndex];\n      }\n\n      seriesModel[payload.type === TOGGLE_SELECT_ACTION_TYPE ? 'toggleSelect' : payload.type === SELECT_ACTION_TYPE ? 'select' : 'unselect'](dataIndex, dataType);\n    }\n    function updateSeriesElementSelection(seriesModel) {\n      var allData = seriesModel.getAllData();\n      each(allData, function (_a) {\n        var data = _a.data,\n            type = _a.type;\n        data.eachItemGraphicEl(function (el, idx) {\n          seriesModel.isSelected(idx, type) ? enterSelect(el) : leaveSelect(el);\n        });\n      });\n    }\n    function getAllSelectedIndices(ecModel) {\n      var ret = [];\n      ecModel.eachSeries(function (seriesModel) {\n        var allData = seriesModel.getAllData();\n        each(allData, function (_a) {\n          var data = _a.data,\n              type = _a.type;\n          var dataIndices = seriesModel.getSelectedDataIndices();\n\n          if (dataIndices.length > 0) {\n            var item = {\n              dataIndex: dataIndices,\n              seriesIndex: seriesModel.seriesIndex\n            };\n\n            if (type != null) {\n              item.dataType = type;\n            }\n\n            ret.push(item);\n          }\n        });\n      });\n      return ret;\n    }\n    /**\n     * Enable the function that mouseover will trigger the emphasis state.\n     *\n     * NOTE:\n     * This function should be used on the element with dataIndex, seriesIndex.\n     *\n     */\n\n    function enableHoverEmphasis(el, focus, blurScope) {\n      setAsHighDownDispatcher(el, true);\n      traverseUpdateState(el, setDefaultStateProxy);\n      enableHoverFocus(el, focus, blurScope);\n    }\n    function disableHoverEmphasis(el) {\n      setAsHighDownDispatcher(el, false);\n    }\n    function toggleHoverEmphasis(el, focus, blurScope, isDisabled) {\n      isDisabled ? disableHoverEmphasis(el) : enableHoverEmphasis(el, focus, blurScope);\n    }\n    function enableHoverFocus(el, focus, blurScope) {\n      var ecData = getECData(el);\n\n      if (focus != null) {\n        // TODO dataIndex may be set after this function. This check is not useful.\n        // if (ecData.dataIndex == null) {\n        //     if (__DEV__) {\n        //         console.warn('focus can only been set on element with dataIndex');\n        //     }\n        // }\n        // else {\n        ecData.focus = focus;\n        ecData.blurScope = blurScope; // }\n      } else if (ecData.focus) {\n        ecData.focus = null;\n      }\n    }\n    var OTHER_STATES = ['emphasis', 'blur', 'select'];\n    var defaultStyleGetterMap = {\n      itemStyle: 'getItemStyle',\n      lineStyle: 'getLineStyle',\n      areaStyle: 'getAreaStyle'\n    };\n    /**\n     * Set emphasis/blur/selected states of element.\n     */\n\n    function setStatesStylesFromModel(el, itemModel, styleType, // default itemStyle\n    getter) {\n      styleType = styleType || 'itemStyle';\n\n      for (var i = 0; i < OTHER_STATES.length; i++) {\n        var stateName = OTHER_STATES[i];\n        var model = itemModel.getModel([stateName, styleType]);\n        var state = el.ensureState(stateName); // Let it throw error if getterType is not found.\n\n        state.style = getter ? getter(model) : model[defaultStyleGetterMap[styleType]]();\n      }\n    }\n    /**\n     *\n     * Set element as highlight / downplay dispatcher.\n     * It will be checked when element received mouseover event or from highlight action.\n     * It's in change of all highlight/downplay behavior of it's children.\n     *\n     * @param el\n     * @param el.highDownSilentOnTouch\n     *        In touch device, mouseover event will be trigger on touchstart event\n     *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can\n     *        conveniently use hoverStyle when tap on touch screen without additional\n     *        code for compatibility.\n     *        But if the chart/component has select feature, which usually also use\n     *        hoverStyle, there might be conflict between 'select-highlight' and\n     *        'hover-highlight' especially when roam is enabled (see geo for example).\n     *        In this case, `highDownSilentOnTouch` should be used to disable\n     *        hover-highlight on touch device.\n     * @param asDispatcher If `false`, do not set as \"highDownDispatcher\".\n     */\n\n    function setAsHighDownDispatcher(el, asDispatcher) {\n      var disable = asDispatcher === false;\n      var extendedEl = el; // Make `highDownSilentOnTouch` and `onStateChange` only work after\n      // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.\n\n      if (el.highDownSilentOnTouch) {\n        extendedEl.__highDownSilentOnTouch = el.highDownSilentOnTouch;\n      } // Simple optimize, since this method might be\n      // called for each elements of a group in some cases.\n\n\n      if (!disable || extendedEl.__highDownDispatcher) {\n        // Emphasis, normal can be triggered manually by API or other components like hover link.\n        // el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent);\n        // Also keep previous record.\n        extendedEl.__highByOuter = extendedEl.__highByOuter || 0;\n        extendedEl.__highDownDispatcher = !disable;\n      }\n    }\n    function isHighDownDispatcher(el) {\n      return !!(el && el.__highDownDispatcher);\n    }\n    /**\n     * Support highlight/downplay record on each elements.\n     * For the case: hover highlight/downplay (legend, visualMap, ...) and\n     * user triggered highlight/downplay should not conflict.\n     * Only all of the highlightDigit cleared, return to normal.\n     * @param {string} highlightKey\n     * @return {number} highlightDigit\n     */\n\n    function getHighlightDigit(highlightKey) {\n      var highlightDigit = _highlightKeyMap[highlightKey];\n\n      if (highlightDigit == null && _highlightNextDigit <= 32) {\n        highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++;\n      }\n\n      return highlightDigit;\n    }\n    function isSelectChangePayload(payload) {\n      var payloadType = payload.type;\n      return payloadType === SELECT_ACTION_TYPE || payloadType === UNSELECT_ACTION_TYPE || payloadType === TOGGLE_SELECT_ACTION_TYPE;\n    }\n    function isHighDownPayload(payload) {\n      var payloadType = payload.type;\n      return payloadType === HIGHLIGHT_ACTION_TYPE || payloadType === DOWNPLAY_ACTION_TYPE;\n    }\n    function savePathStates(el) {\n      var store = getSavedStates(el);\n      store.normalFill = el.style.fill;\n      store.normalStroke = el.style.stroke;\n      var selectState = el.states.select || {};\n      store.selectFill = selectState.style && selectState.style.fill || null;\n      store.selectStroke = selectState.style && selectState.style.stroke || null;\n    }\n\n    var CMD$2 = PathProxy.CMD;\n    var points = [[], [], []];\n    var mathSqrt$1 = Math.sqrt;\n    var mathAtan2 = Math.atan2;\n    function transformPath(path, m) {\n        if (!m) {\n            return;\n        }\n        var data = path.data;\n        var len = path.len();\n        var cmd;\n        var nPoint;\n        var i;\n        var j;\n        var k;\n        var p;\n        var M = CMD$2.M;\n        var C = CMD$2.C;\n        var L = CMD$2.L;\n        var R = CMD$2.R;\n        var A = CMD$2.A;\n        var Q = CMD$2.Q;\n        for (i = 0, j = 0; i < len;) {\n            cmd = data[i++];\n            j = i;\n            nPoint = 0;\n            switch (cmd) {\n                case M:\n                    nPoint = 1;\n                    break;\n                case L:\n                    nPoint = 1;\n                    break;\n                case C:\n                    nPoint = 3;\n                    break;\n                case Q:\n                    nPoint = 2;\n                    break;\n                case A:\n                    var x = m[4];\n                    var y = m[5];\n                    var sx = mathSqrt$1(m[0] * m[0] + m[1] * m[1]);\n                    var sy = mathSqrt$1(m[2] * m[2] + m[3] * m[3]);\n                    var angle = mathAtan2(-m[1] / sy, m[0] / sx);\n                    data[i] *= sx;\n                    data[i++] += x;\n                    data[i] *= sy;\n                    data[i++] += y;\n                    data[i++] *= sx;\n                    data[i++] *= sy;\n                    data[i++] += angle;\n                    data[i++] += angle;\n                    i += 2;\n                    j = i;\n                    break;\n                case R:\n                    p[0] = data[i++];\n                    p[1] = data[i++];\n                    applyTransform(p, p, m);\n                    data[j++] = p[0];\n                    data[j++] = p[1];\n                    p[0] += data[i++];\n                    p[1] += data[i++];\n                    applyTransform(p, p, m);\n                    data[j++] = p[0];\n                    data[j++] = p[1];\n            }\n            for (k = 0; k < nPoint; k++) {\n                var p_1 = points[k];\n                p_1[0] = data[i++];\n                p_1[1] = data[i++];\n                applyTransform(p_1, p_1, m);\n                data[j++] = p_1[0];\n                data[j++] = p_1[1];\n            }\n        }\n        path.increaseVersion();\n    }\n\n    var mathSqrt$2 = Math.sqrt;\n    var mathSin$2 = Math.sin;\n    var mathCos$2 = Math.cos;\n    var PI$1 = Math.PI;\n    function vMag(v) {\n        return Math.sqrt(v[0] * v[0] + v[1] * v[1]);\n    }\n    function vRatio(u, v) {\n        return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));\n    }\n    function vAngle(u, v) {\n        return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)\n            * Math.acos(vRatio(u, v));\n    }\n    function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {\n        var psi = psiDeg * (PI$1 / 180.0);\n        var xp = mathCos$2(psi) * (x1 - x2) / 2.0\n            + mathSin$2(psi) * (y1 - y2) / 2.0;\n        var yp = -1 * mathSin$2(psi) * (x1 - x2) / 2.0\n            + mathCos$2(psi) * (y1 - y2) / 2.0;\n        var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);\n        if (lambda > 1) {\n            rx *= mathSqrt$2(lambda);\n            ry *= mathSqrt$2(lambda);\n        }\n        var f = (fa === fs ? -1 : 1)\n            * mathSqrt$2((((rx * rx) * (ry * ry))\n                - ((rx * rx) * (yp * yp))\n                - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)\n                + (ry * ry) * (xp * xp))) || 0;\n        var cxp = f * rx * yp / ry;\n        var cyp = f * -ry * xp / rx;\n        var cx = (x1 + x2) / 2.0\n            + mathCos$2(psi) * cxp\n            - mathSin$2(psi) * cyp;\n        var cy = (y1 + y2) / 2.0\n            + mathSin$2(psi) * cxp\n            + mathCos$2(psi) * cyp;\n        var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);\n        var u = [(xp - cxp) / rx, (yp - cyp) / ry];\n        var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];\n        var dTheta = vAngle(u, v);\n        if (vRatio(u, v) <= -1) {\n            dTheta = PI$1;\n        }\n        if (vRatio(u, v) >= 1) {\n            dTheta = 0;\n        }\n        if (dTheta < 0) {\n            var n = Math.round(dTheta / PI$1 * 1e6) / 1e6;\n            dTheta = PI$1 * 2 + (n % 2) * PI$1;\n        }\n        path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);\n    }\n    var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig;\n    var numberReg = /-?([0-9]*\\.)?[0-9]+([eE]-?[0-9]+)?/g;\n    function createPathProxyFromString(data) {\n        var path = new PathProxy();\n        if (!data) {\n            return path;\n        }\n        var cpx = 0;\n        var cpy = 0;\n        var subpathX = cpx;\n        var subpathY = cpy;\n        var prevCmd;\n        var CMD = PathProxy.CMD;\n        var cmdList = data.match(commandReg);\n        if (!cmdList) {\n            return path;\n        }\n        for (var l = 0; l < cmdList.length; l++) {\n            var cmdText = cmdList[l];\n            var cmdStr = cmdText.charAt(0);\n            var cmd = void 0;\n            var p = cmdText.match(numberReg) || [];\n            var pLen = p.length;\n            for (var i = 0; i < pLen; i++) {\n                p[i] = parseFloat(p[i]);\n            }\n            var off = 0;\n            while (off < pLen) {\n                var ctlPtx = void 0;\n                var ctlPty = void 0;\n                var rx = void 0;\n                var ry = void 0;\n                var psi = void 0;\n                var fa = void 0;\n                var fs = void 0;\n                var x1 = cpx;\n                var y1 = cpy;\n                var len = void 0;\n                var pathData = void 0;\n                switch (cmdStr) {\n                    case 'l':\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'L':\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'm':\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.M;\n                        path.addData(cmd, cpx, cpy);\n                        subpathX = cpx;\n                        subpathY = cpy;\n                        cmdStr = 'l';\n                        break;\n                    case 'M':\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.M;\n                        path.addData(cmd, cpx, cpy);\n                        subpathX = cpx;\n                        subpathY = cpy;\n                        cmdStr = 'L';\n                        break;\n                    case 'h':\n                        cpx += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'H':\n                        cpx = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'v':\n                        cpy += p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'V':\n                        cpy = p[off++];\n                        cmd = CMD.L;\n                        path.addData(cmd, cpx, cpy);\n                        break;\n                    case 'C':\n                        cmd = CMD.C;\n                        path.addData(cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]);\n                        cpx = p[off - 2];\n                        cpy = p[off - 1];\n                        break;\n                    case 'c':\n                        cmd = CMD.C;\n                        path.addData(cmd, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy);\n                        cpx += p[off - 2];\n                        cpy += p[off - 1];\n                        break;\n                    case 'S':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.C) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cmd = CMD.C;\n                        x1 = p[off++];\n                        y1 = p[off++];\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                        break;\n                    case 's':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.C) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cmd = CMD.C;\n                        x1 = cpx + p[off++];\n                        y1 = cpy + p[off++];\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n                        break;\n                    case 'Q':\n                        x1 = p[off++];\n                        y1 = p[off++];\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, x1, y1, cpx, cpy);\n                        break;\n                    case 'q':\n                        x1 = p[off++] + cpx;\n                        y1 = p[off++] + cpy;\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, x1, y1, cpx, cpy);\n                        break;\n                    case 'T':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.Q) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                        break;\n                    case 't':\n                        ctlPtx = cpx;\n                        ctlPty = cpy;\n                        len = path.len();\n                        pathData = path.data;\n                        if (prevCmd === CMD.Q) {\n                            ctlPtx += cpx - pathData[len - 4];\n                            ctlPty += cpy - pathData[len - 3];\n                        }\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.Q;\n                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n                        break;\n                    case 'A':\n                        rx = p[off++];\n                        ry = p[off++];\n                        psi = p[off++];\n                        fa = p[off++];\n                        fs = p[off++];\n                        x1 = cpx, y1 = cpy;\n                        cpx = p[off++];\n                        cpy = p[off++];\n                        cmd = CMD.A;\n                        processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                        break;\n                    case 'a':\n                        rx = p[off++];\n                        ry = p[off++];\n                        psi = p[off++];\n                        fa = p[off++];\n                        fs = p[off++];\n                        x1 = cpx, y1 = cpy;\n                        cpx += p[off++];\n                        cpy += p[off++];\n                        cmd = CMD.A;\n                        processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);\n                        break;\n                }\n            }\n            if (cmdStr === 'z' || cmdStr === 'Z') {\n                cmd = CMD.Z;\n                path.addData(cmd);\n                cpx = subpathX;\n                cpy = subpathY;\n            }\n            prevCmd = cmd;\n        }\n        path.toStatic();\n        return path;\n    }\n    var SVGPath = (function (_super) {\n        __extends(SVGPath, _super);\n        function SVGPath() {\n            return _super !== null && _super.apply(this, arguments) || this;\n        }\n        SVGPath.prototype.applyTransform = function (m) { };\n        return SVGPath;\n    }(Path));\n    function isPathProxy(path) {\n        return path.setData != null;\n    }\n    function createPathOptions(str, opts) {\n        var pathProxy = createPathProxyFromString(str);\n        var innerOpts = extend({}, opts);\n        innerOpts.buildPath = function (path) {\n            if (isPathProxy(path)) {\n                path.setData(pathProxy.data);\n                var ctx = path.getContext();\n                if (ctx) {\n                    path.rebuildPath(ctx, 1);\n                }\n            }\n            else {\n                var ctx = path;\n                pathProxy.rebuildPath(ctx, 1);\n            }\n        };\n        innerOpts.applyTransform = function (m) {\n            transformPath(pathProxy, m);\n            this.dirtyShape();\n        };\n        return innerOpts;\n    }\n    function createFromString(str, opts) {\n        return new SVGPath(createPathOptions(str, opts));\n    }\n    function extendFromString(str, defaultOpts) {\n        var innerOpts = createPathOptions(str, defaultOpts);\n        var Sub = (function (_super) {\n            __extends(Sub, _super);\n            function Sub(opts) {\n                var _this = _super.call(this, opts) || this;\n                _this.applyTransform = innerOpts.applyTransform;\n                _this.buildPath = innerOpts.buildPath;\n                return _this;\n            }\n            return Sub;\n        }(SVGPath));\n        return Sub;\n    }\n    function mergePath(pathEls, opts) {\n        var pathList = [];\n        var len = pathEls.length;\n        for (var i = 0; i < len; i++) {\n            var pathEl = pathEls[i];\n            pathList.push(pathEl.getUpdatedPathProxy(true));\n        }\n        var pathBundle = new Path(opts);\n        pathBundle.createPathProxy();\n        pathBundle.buildPath = function (path) {\n            if (isPathProxy(path)) {\n                path.appendPath(pathList);\n                var ctx = path.getContext();\n                if (ctx) {\n                    path.rebuildPath(ctx, 1);\n                }\n            }\n        };\n        return pathBundle;\n    }\n\n    var CircleShape = (function () {\n        function CircleShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n        }\n        return CircleShape;\n    }());\n    var Circle = (function (_super) {\n        __extends(Circle, _super);\n        function Circle(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Circle.prototype.getDefaultShape = function () {\n            return new CircleShape();\n        };\n        Circle.prototype.buildPath = function (ctx, shape) {\n            ctx.moveTo(shape.cx + shape.r, shape.cy);\n            ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);\n        };\n        return Circle;\n    }(Path));\n    Circle.prototype.type = 'circle';\n\n    var EllipseShape = (function () {\n        function EllipseShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.rx = 0;\n            this.ry = 0;\n        }\n        return EllipseShape;\n    }());\n    var Ellipse = (function (_super) {\n        __extends(Ellipse, _super);\n        function Ellipse(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Ellipse.prototype.getDefaultShape = function () {\n            return new EllipseShape();\n        };\n        Ellipse.prototype.buildPath = function (ctx, shape) {\n            var k = 0.5522848;\n            var x = shape.cx;\n            var y = shape.cy;\n            var a = shape.rx;\n            var b = shape.ry;\n            var ox = a * k;\n            var oy = b * k;\n            ctx.moveTo(x - a, y);\n            ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);\n            ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);\n            ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);\n            ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);\n            ctx.closePath();\n        };\n        return Ellipse;\n    }(Path));\n    Ellipse.prototype.type = 'ellipse';\n\n    var PI$2 = Math.PI;\n    var PI2$5 = PI$2 * 2;\n    var mathSin$3 = Math.sin;\n    var mathCos$3 = Math.cos;\n    var mathACos = Math.acos;\n    var mathATan2 = Math.atan2;\n    var mathAbs$1 = Math.abs;\n    var mathSqrt$3 = Math.sqrt;\n    var mathMax$3 = Math.max;\n    var mathMin$3 = Math.min;\n    var e = 1e-4;\n    function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n        var dx10 = x1 - x0;\n        var dy10 = y1 - y0;\n        var dx32 = x3 - x2;\n        var dy32 = y3 - y2;\n        var t = dy32 * dx10 - dx32 * dy10;\n        if (t * t < e) {\n            return;\n        }\n        t = (dx32 * (y0 - y2) - dy32 * (x0 - x2)) / t;\n        return [x0 + t * dx10, y0 + t * dy10];\n    }\n    function computeCornerTangents(x0, y0, x1, y1, radius, cr, clockwise) {\n        var x01 = x0 - x1;\n        var y01 = y0 - y1;\n        var lo = (clockwise ? cr : -cr) / mathSqrt$3(x01 * x01 + y01 * y01);\n        var ox = lo * y01;\n        var oy = -lo * x01;\n        var x11 = x0 + ox;\n        var y11 = y0 + oy;\n        var x10 = x1 + ox;\n        var y10 = y1 + oy;\n        var x00 = (x11 + x10) / 2;\n        var y00 = (y11 + y10) / 2;\n        var dx = x10 - x11;\n        var dy = y10 - y11;\n        var d2 = dx * dx + dy * dy;\n        var r = radius - cr;\n        var s = x11 * y10 - x10 * y11;\n        var d = (dy < 0 ? -1 : 1) * mathSqrt$3(mathMax$3(0, r * r * d2 - s * s));\n        var cx0 = (s * dy - dx * d) / d2;\n        var cy0 = (-s * dx - dy * d) / d2;\n        var cx1 = (s * dy + dx * d) / d2;\n        var cy1 = (-s * dx + dy * d) / d2;\n        var dx0 = cx0 - x00;\n        var dy0 = cy0 - y00;\n        var dx1 = cx1 - x00;\n        var dy1 = cy1 - y00;\n        if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) {\n            cx0 = cx1;\n            cy0 = cy1;\n        }\n        return {\n            cx: cx0,\n            cy: cy0,\n            x0: -ox,\n            y0: -oy,\n            x1: cx0 * (radius / r - 1),\n            y1: cy0 * (radius / r - 1)\n        };\n    }\n    function normalizeCornerRadius(cr) {\n        var arr;\n        if (isArray(cr)) {\n            var len = cr.length;\n            if (!len) {\n                return cr;\n            }\n            if (len === 1) {\n                arr = [cr[0], cr[0], 0, 0];\n            }\n            else if (len === 2) {\n                arr = [cr[0], cr[0], cr[1], cr[1]];\n            }\n            else if (len === 3) {\n                arr = cr.concat(cr[2]);\n            }\n            else {\n                arr = cr;\n            }\n        }\n        else {\n            arr = [cr, cr, cr, cr];\n        }\n        return arr;\n    }\n    function buildPath$1(ctx, shape) {\n        var _a;\n        var radius = mathMax$3(shape.r, 0);\n        var innerRadius = mathMax$3(shape.r0 || 0, 0);\n        var hasRadius = radius > 0;\n        var hasInnerRadius = innerRadius > 0;\n        if (!hasRadius && !hasInnerRadius) {\n            return;\n        }\n        if (!hasRadius) {\n            radius = innerRadius;\n            innerRadius = 0;\n        }\n        if (innerRadius > radius) {\n            var tmp = radius;\n            radius = innerRadius;\n            innerRadius = tmp;\n        }\n        var startAngle = shape.startAngle, endAngle = shape.endAngle;\n        if (isNaN(startAngle) || isNaN(endAngle)) {\n            return;\n        }\n        var cx = shape.cx, cy = shape.cy;\n        var clockwise = !!shape.clockwise;\n        var arc = mathAbs$1(endAngle - startAngle);\n        var mod = arc > PI2$5 && arc % PI2$5;\n        mod > e && (arc = mod);\n        if (!(radius > e)) {\n            ctx.moveTo(cx, cy);\n        }\n        else if (arc > PI2$5 - e) {\n            ctx.moveTo(cx + radius * mathCos$3(startAngle), cy + radius * mathSin$3(startAngle));\n            ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n            if (innerRadius > e) {\n                ctx.moveTo(cx + innerRadius * mathCos$3(endAngle), cy + innerRadius * mathSin$3(endAngle));\n                ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n            }\n        }\n        else {\n            var icrStart = void 0;\n            var icrEnd = void 0;\n            var ocrStart = void 0;\n            var ocrEnd = void 0;\n            var ocrs = void 0;\n            var ocre = void 0;\n            var icrs = void 0;\n            var icre = void 0;\n            var ocrMax = void 0;\n            var icrMax = void 0;\n            var limitedOcrMax = void 0;\n            var limitedIcrMax = void 0;\n            var xre = void 0;\n            var yre = void 0;\n            var xirs = void 0;\n            var yirs = void 0;\n            var xrs = radius * mathCos$3(startAngle);\n            var yrs = radius * mathSin$3(startAngle);\n            var xire = innerRadius * mathCos$3(endAngle);\n            var yire = innerRadius * mathSin$3(endAngle);\n            var hasArc = arc > e;\n            if (hasArc) {\n                var cornerRadius = shape.cornerRadius;\n                if (cornerRadius) {\n                    _a = normalizeCornerRadius(cornerRadius), icrStart = _a[0], icrEnd = _a[1], ocrStart = _a[2], ocrEnd = _a[3];\n                }\n                var halfRd = mathAbs$1(radius - innerRadius) / 2;\n                ocrs = mathMin$3(halfRd, ocrStart);\n                ocre = mathMin$3(halfRd, ocrEnd);\n                icrs = mathMin$3(halfRd, icrStart);\n                icre = mathMin$3(halfRd, icrEnd);\n                limitedOcrMax = ocrMax = mathMax$3(ocrs, ocre);\n                limitedIcrMax = icrMax = mathMax$3(icrs, icre);\n                if (ocrMax > e || icrMax > e) {\n                    xre = radius * mathCos$3(endAngle);\n                    yre = radius * mathSin$3(endAngle);\n                    xirs = innerRadius * mathCos$3(startAngle);\n                    yirs = innerRadius * mathSin$3(startAngle);\n                    if (arc < PI$2) {\n                        var it_1 = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire);\n                        if (it_1) {\n                            var x0 = xrs - it_1[0];\n                            var y0 = yrs - it_1[1];\n                            var x1 = xre - it_1[0];\n                            var y1 = yre - it_1[1];\n                            var a = 1 / mathSin$3(mathACos((x0 * x1 + y0 * y1) / (mathSqrt$3(x0 * x0 + y0 * y0) * mathSqrt$3(x1 * x1 + y1 * y1))) / 2);\n                            var b = mathSqrt$3(it_1[0] * it_1[0] + it_1[1] * it_1[1]);\n                            limitedOcrMax = mathMin$3(ocrMax, (radius - b) / (a + 1));\n                            limitedIcrMax = mathMin$3(icrMax, (innerRadius - b) / (a - 1));\n                        }\n                    }\n                }\n            }\n            if (!hasArc) {\n                ctx.moveTo(cx + xrs, cy + yrs);\n            }\n            else if (limitedOcrMax > e) {\n                var crStart = mathMin$3(ocrStart, limitedOcrMax);\n                var crEnd = mathMin$3(ocrEnd, limitedOcrMax);\n                var ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, crStart, clockwise);\n                var ct1 = computeCornerTangents(xre, yre, xire, yire, radius, crEnd, clockwise);\n                ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n                if (limitedOcrMax < ocrMax && crStart === crEnd) {\n                    ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n                else {\n                    crStart > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                    ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise);\n                    crEnd > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n            }\n            else {\n                ctx.moveTo(cx + xrs, cy + yrs);\n                ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise);\n            }\n            if (!(innerRadius > e) || !hasArc) {\n                ctx.lineTo(cx + xire, cy + yire);\n            }\n            else if (limitedIcrMax > e) {\n                var crStart = mathMin$3(icrStart, limitedIcrMax);\n                var crEnd = mathMin$3(icrEnd, limitedIcrMax);\n                var ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -crEnd, clockwise);\n                var ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -crStart, clockwise);\n                ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0);\n                if (limitedIcrMax < icrMax && crStart === crEnd) {\n                    ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n                else {\n                    crEnd > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise);\n                    ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise);\n                    crStart > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise);\n                }\n            }\n            else {\n                ctx.lineTo(cx + xire, cy + yire);\n                ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise);\n            }\n        }\n        ctx.closePath();\n    }\n\n    var SectorShape = (function () {\n        function SectorShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r0 = 0;\n            this.r = 0;\n            this.startAngle = 0;\n            this.endAngle = Math.PI * 2;\n            this.clockwise = true;\n            this.cornerRadius = 0;\n        }\n        return SectorShape;\n    }());\n    var Sector = (function (_super) {\n        __extends(Sector, _super);\n        function Sector(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Sector.prototype.getDefaultShape = function () {\n            return new SectorShape();\n        };\n        Sector.prototype.buildPath = function (ctx, shape) {\n            buildPath$1(ctx, shape);\n        };\n        Sector.prototype.isZeroArea = function () {\n            return this.shape.startAngle === this.shape.endAngle\n                || this.shape.r === this.shape.r0;\n        };\n        return Sector;\n    }(Path));\n    Sector.prototype.type = 'sector';\n\n    var RingShape = (function () {\n        function RingShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n            this.r0 = 0;\n        }\n        return RingShape;\n    }());\n    var Ring = (function (_super) {\n        __extends(Ring, _super);\n        function Ring(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Ring.prototype.getDefaultShape = function () {\n            return new RingShape();\n        };\n        Ring.prototype.buildPath = function (ctx, shape) {\n            var x = shape.cx;\n            var y = shape.cy;\n            var PI2 = Math.PI * 2;\n            ctx.moveTo(x + shape.r, y);\n            ctx.arc(x, y, shape.r, 0, PI2, false);\n            ctx.moveTo(x + shape.r0, y);\n            ctx.arc(x, y, shape.r0, 0, PI2, true);\n        };\n        return Ring;\n    }(Path));\n    Ring.prototype.type = 'ring';\n\n    function smoothBezier(points, smooth, isLoop, constraint) {\n        var cps = [];\n        var v = [];\n        var v1 = [];\n        var v2 = [];\n        var prevPoint;\n        var nextPoint;\n        var min$1;\n        var max$1;\n        if (constraint) {\n            min$1 = [Infinity, Infinity];\n            max$1 = [-Infinity, -Infinity];\n            for (var i = 0, len = points.length; i < len; i++) {\n                min(min$1, min$1, points[i]);\n                max(max$1, max$1, points[i]);\n            }\n            min(min$1, min$1, constraint[0]);\n            max(max$1, max$1, constraint[1]);\n        }\n        for (var i = 0, len = points.length; i < len; i++) {\n            var point = points[i];\n            if (isLoop) {\n                prevPoint = points[i ? i - 1 : len - 1];\n                nextPoint = points[(i + 1) % len];\n            }\n            else {\n                if (i === 0 || i === len - 1) {\n                    cps.push(clone$1(points[i]));\n                    continue;\n                }\n                else {\n                    prevPoint = points[i - 1];\n                    nextPoint = points[i + 1];\n                }\n            }\n            sub(v, nextPoint, prevPoint);\n            scale(v, v, smooth);\n            var d0 = distance(point, prevPoint);\n            var d1 = distance(point, nextPoint);\n            var sum = d0 + d1;\n            if (sum !== 0) {\n                d0 /= sum;\n                d1 /= sum;\n            }\n            scale(v1, v, -d0);\n            scale(v2, v, d1);\n            var cp0 = add([], point, v1);\n            var cp1 = add([], point, v2);\n            if (constraint) {\n                max(cp0, cp0, min$1);\n                min(cp0, cp0, max$1);\n                max(cp1, cp1, min$1);\n                min(cp1, cp1, max$1);\n            }\n            cps.push(cp0);\n            cps.push(cp1);\n        }\n        if (isLoop) {\n            cps.push(cps.shift());\n        }\n        return cps;\n    }\n\n    function buildPath$2(ctx, shape, closePath) {\n        var smooth = shape.smooth;\n        var points = shape.points;\n        if (points && points.length >= 2) {\n            if (smooth) {\n                var controlPoints = smoothBezier(points, smooth, closePath, shape.smoothConstraint);\n                ctx.moveTo(points[0][0], points[0][1]);\n                var len = points.length;\n                for (var i = 0; i < (closePath ? len : len - 1); i++) {\n                    var cp1 = controlPoints[i * 2];\n                    var cp2 = controlPoints[i * 2 + 1];\n                    var p = points[(i + 1) % len];\n                    ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]);\n                }\n            }\n            else {\n                ctx.moveTo(points[0][0], points[0][1]);\n                for (var i = 1, l = points.length; i < l; i++) {\n                    ctx.lineTo(points[i][0], points[i][1]);\n                }\n            }\n            closePath && ctx.closePath();\n        }\n    }\n\n    var PolygonShape = (function () {\n        function PolygonShape() {\n            this.points = null;\n            this.smooth = 0;\n            this.smoothConstraint = null;\n        }\n        return PolygonShape;\n    }());\n    var Polygon = (function (_super) {\n        __extends(Polygon, _super);\n        function Polygon(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Polygon.prototype.getDefaultShape = function () {\n            return new PolygonShape();\n        };\n        Polygon.prototype.buildPath = function (ctx, shape) {\n            buildPath$2(ctx, shape, true);\n        };\n        return Polygon;\n    }(Path));\n    Polygon.prototype.type = 'polygon';\n\n    var PolylineShape = (function () {\n        function PolylineShape() {\n            this.points = null;\n            this.percent = 1;\n            this.smooth = 0;\n            this.smoothConstraint = null;\n        }\n        return PolylineShape;\n    }());\n    var Polyline = (function (_super) {\n        __extends(Polyline, _super);\n        function Polyline(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Polyline.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Polyline.prototype.getDefaultShape = function () {\n            return new PolylineShape();\n        };\n        Polyline.prototype.buildPath = function (ctx, shape) {\n            buildPath$2(ctx, shape, false);\n        };\n        return Polyline;\n    }(Path));\n    Polyline.prototype.type = 'polyline';\n\n    var subPixelOptimizeOutputShape$1 = {};\n    var LineShape = (function () {\n        function LineShape() {\n            this.x1 = 0;\n            this.y1 = 0;\n            this.x2 = 0;\n            this.y2 = 0;\n            this.percent = 1;\n        }\n        return LineShape;\n    }());\n    var Line = (function (_super) {\n        __extends(Line, _super);\n        function Line(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Line.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Line.prototype.getDefaultShape = function () {\n            return new LineShape();\n        };\n        Line.prototype.buildPath = function (ctx, shape) {\n            var x1;\n            var y1;\n            var x2;\n            var y2;\n            if (this.subPixelOptimize) {\n                var optimizedShape = subPixelOptimizeLine(subPixelOptimizeOutputShape$1, shape, this.style);\n                x1 = optimizedShape.x1;\n                y1 = optimizedShape.y1;\n                x2 = optimizedShape.x2;\n                y2 = optimizedShape.y2;\n            }\n            else {\n                x1 = shape.x1;\n                y1 = shape.y1;\n                x2 = shape.x2;\n                y2 = shape.y2;\n            }\n            var percent = shape.percent;\n            if (percent === 0) {\n                return;\n            }\n            ctx.moveTo(x1, y1);\n            if (percent < 1) {\n                x2 = x1 * (1 - percent) + x2 * percent;\n                y2 = y1 * (1 - percent) + y2 * percent;\n            }\n            ctx.lineTo(x2, y2);\n        };\n        Line.prototype.pointAt = function (p) {\n            var shape = this.shape;\n            return [\n                shape.x1 * (1 - p) + shape.x2 * p,\n                shape.y1 * (1 - p) + shape.y2 * p\n            ];\n        };\n        return Line;\n    }(Path));\n    Line.prototype.type = 'line';\n\n    var out = [];\n    var BezierCurveShape = (function () {\n        function BezierCurveShape() {\n            this.x1 = 0;\n            this.y1 = 0;\n            this.x2 = 0;\n            this.y2 = 0;\n            this.cpx1 = 0;\n            this.cpy1 = 0;\n            this.percent = 1;\n        }\n        return BezierCurveShape;\n    }());\n    function someVectorAt(shape, t, isTangent) {\n        var cpx2 = shape.cpx2;\n        var cpy2 = shape.cpy2;\n        if (cpx2 != null || cpy2 != null) {\n            return [\n                (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),\n                (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)\n            ];\n        }\n        else {\n            return [\n                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),\n                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)\n            ];\n        }\n    }\n    var BezierCurve = (function (_super) {\n        __extends(BezierCurve, _super);\n        function BezierCurve(opts) {\n            return _super.call(this, opts) || this;\n        }\n        BezierCurve.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        BezierCurve.prototype.getDefaultShape = function () {\n            return new BezierCurveShape();\n        };\n        BezierCurve.prototype.buildPath = function (ctx, shape) {\n            var x1 = shape.x1;\n            var y1 = shape.y1;\n            var x2 = shape.x2;\n            var y2 = shape.y2;\n            var cpx1 = shape.cpx1;\n            var cpy1 = shape.cpy1;\n            var cpx2 = shape.cpx2;\n            var cpy2 = shape.cpy2;\n            var percent = shape.percent;\n            if (percent === 0) {\n                return;\n            }\n            ctx.moveTo(x1, y1);\n            if (cpx2 == null || cpy2 == null) {\n                if (percent < 1) {\n                    quadraticSubdivide(x1, cpx1, x2, percent, out);\n                    cpx1 = out[1];\n                    x2 = out[2];\n                    quadraticSubdivide(y1, cpy1, y2, percent, out);\n                    cpy1 = out[1];\n                    y2 = out[2];\n                }\n                ctx.quadraticCurveTo(cpx1, cpy1, x2, y2);\n            }\n            else {\n                if (percent < 1) {\n                    cubicSubdivide(x1, cpx1, cpx2, x2, percent, out);\n                    cpx1 = out[1];\n                    cpx2 = out[2];\n                    x2 = out[3];\n                    cubicSubdivide(y1, cpy1, cpy2, y2, percent, out);\n                    cpy1 = out[1];\n                    cpy2 = out[2];\n                    y2 = out[3];\n                }\n                ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2);\n            }\n        };\n        BezierCurve.prototype.pointAt = function (t) {\n            return someVectorAt(this.shape, t, false);\n        };\n        BezierCurve.prototype.tangentAt = function (t) {\n            var p = someVectorAt(this.shape, t, true);\n            return normalize(p, p);\n        };\n        return BezierCurve;\n    }(Path));\n    BezierCurve.prototype.type = 'bezier-curve';\n\n    var ArcShape = (function () {\n        function ArcShape() {\n            this.cx = 0;\n            this.cy = 0;\n            this.r = 0;\n            this.startAngle = 0;\n            this.endAngle = Math.PI * 2;\n            this.clockwise = true;\n        }\n        return ArcShape;\n    }());\n    var Arc = (function (_super) {\n        __extends(Arc, _super);\n        function Arc(opts) {\n            return _super.call(this, opts) || this;\n        }\n        Arc.prototype.getDefaultStyle = function () {\n            return {\n                stroke: '#000',\n                fill: null\n            };\n        };\n        Arc.prototype.getDefaultShape = function () {\n            return new ArcShape();\n        };\n        Arc.prototype.buildPath = function (ctx, shape) {\n            var x = shape.cx;\n            var y = shape.cy;\n            var r = Math.max(shape.r, 0);\n            var startAngle = shape.startAngle;\n            var endAngle = shape.endAngle;\n            var clockwise = shape.clockwise;\n            var unitX = Math.cos(startAngle);\n            var unitY = Math.sin(startAngle);\n            ctx.moveTo(unitX * r + x, unitY * r + y);\n            ctx.arc(x, y, r, startAngle, endAngle, !clockwise);\n        };\n        return Arc;\n    }(Path));\n    Arc.prototype.type = 'arc';\n\n    var CompoundPath = (function (_super) {\n        __extends(CompoundPath, _super);\n        function CompoundPath() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.type = 'compound';\n            return _this;\n        }\n        CompoundPath.prototype._updatePathDirty = function () {\n            var paths = this.shape.paths;\n            var dirtyPath = this.shapeChanged();\n            for (var i = 0; i < paths.length; i++) {\n                dirtyPath = dirtyPath || paths[i].shapeChanged();\n            }\n            if (dirtyPath) {\n                this.dirtyShape();\n            }\n        };\n        CompoundPath.prototype.beforeBrush = function () {\n            this._updatePathDirty();\n            var paths = this.shape.paths || [];\n            var scale = this.getGlobalScale();\n            for (var i = 0; i < paths.length; i++) {\n                if (!paths[i].path) {\n                    paths[i].createPathProxy();\n                }\n                paths[i].path.setScale(scale[0], scale[1], paths[i].segmentIgnoreThreshold);\n            }\n        };\n        CompoundPath.prototype.buildPath = function (ctx, shape) {\n            var paths = shape.paths || [];\n            for (var i = 0; i < paths.length; i++) {\n                paths[i].buildPath(ctx, paths[i].shape, true);\n            }\n        };\n        CompoundPath.prototype.afterBrush = function () {\n            var paths = this.shape.paths || [];\n            for (var i = 0; i < paths.length; i++) {\n                paths[i].pathUpdated();\n            }\n        };\n        CompoundPath.prototype.getBoundingRect = function () {\n            this._updatePathDirty.call(this);\n            return Path.prototype.getBoundingRect.call(this);\n        };\n        return CompoundPath;\n    }(Path));\n\n    var Gradient = (function () {\n        function Gradient(colorStops) {\n            this.colorStops = colorStops || [];\n        }\n        Gradient.prototype.addColorStop = function (offset, color) {\n            this.colorStops.push({\n                offset: offset,\n                color: color\n            });\n        };\n        return Gradient;\n    }());\n\n    var LinearGradient = (function (_super) {\n        __extends(LinearGradient, _super);\n        function LinearGradient(x, y, x2, y2, colorStops, globalCoord) {\n            var _this = _super.call(this, colorStops) || this;\n            _this.x = x == null ? 0 : x;\n            _this.y = y == null ? 0 : y;\n            _this.x2 = x2 == null ? 1 : x2;\n            _this.y2 = y2 == null ? 0 : y2;\n            _this.type = 'linear';\n            _this.global = globalCoord || false;\n            return _this;\n        }\n        return LinearGradient;\n    }(Gradient));\n\n    var RadialGradient = (function (_super) {\n        __extends(RadialGradient, _super);\n        function RadialGradient(x, y, r, colorStops, globalCoord) {\n            var _this = _super.call(this, colorStops) || this;\n            _this.x = x == null ? 0.5 : x;\n            _this.y = y == null ? 0.5 : y;\n            _this.r = r == null ? 0.5 : r;\n            _this.type = 'radial';\n            _this.global = globalCoord || false;\n            return _this;\n        }\n        return RadialGradient;\n    }(Gradient));\n\n    var extent = [0, 0];\n    var extent2 = [0, 0];\n    var minTv$1 = new Point();\n    var maxTv$1 = new Point();\n    var OrientedBoundingRect = (function () {\n        function OrientedBoundingRect(rect, transform) {\n            this._corners = [];\n            this._axes = [];\n            this._origin = [0, 0];\n            for (var i = 0; i < 4; i++) {\n                this._corners[i] = new Point();\n            }\n            for (var i = 0; i < 2; i++) {\n                this._axes[i] = new Point();\n            }\n            if (rect) {\n                this.fromBoundingRect(rect, transform);\n            }\n        }\n        OrientedBoundingRect.prototype.fromBoundingRect = function (rect, transform) {\n            var corners = this._corners;\n            var axes = this._axes;\n            var x = rect.x;\n            var y = rect.y;\n            var x2 = x + rect.width;\n            var y2 = y + rect.height;\n            corners[0].set(x, y);\n            corners[1].set(x2, y);\n            corners[2].set(x2, y2);\n            corners[3].set(x, y2);\n            if (transform) {\n                for (var i = 0; i < 4; i++) {\n                    corners[i].transform(transform);\n                }\n            }\n            Point.sub(axes[0], corners[1], corners[0]);\n            Point.sub(axes[1], corners[3], corners[0]);\n            axes[0].normalize();\n            axes[1].normalize();\n            for (var i = 0; i < 2; i++) {\n                this._origin[i] = axes[i].dot(corners[0]);\n            }\n        };\n        OrientedBoundingRect.prototype.intersect = function (other, mtv) {\n            var overlapped = true;\n            var noMtv = !mtv;\n            minTv$1.set(Infinity, Infinity);\n            maxTv$1.set(0, 0);\n            if (!this._intersectCheckOneSide(this, other, minTv$1, maxTv$1, noMtv, 1)) {\n                overlapped = false;\n                if (noMtv) {\n                    return overlapped;\n                }\n            }\n            if (!this._intersectCheckOneSide(other, this, minTv$1, maxTv$1, noMtv, -1)) {\n                overlapped = false;\n                if (noMtv) {\n                    return overlapped;\n                }\n            }\n            if (!noMtv) {\n                Point.copy(mtv, overlapped ? minTv$1 : maxTv$1);\n            }\n            return overlapped;\n        };\n        OrientedBoundingRect.prototype._intersectCheckOneSide = function (self, other, minTv, maxTv, noMtv, inverse) {\n            var overlapped = true;\n            for (var i = 0; i < 2; i++) {\n                var axis = this._axes[i];\n                this._getProjMinMaxOnAxis(i, self._corners, extent);\n                this._getProjMinMaxOnAxis(i, other._corners, extent2);\n                if (extent[1] < extent2[0] || extent[0] > extent2[1]) {\n                    overlapped = false;\n                    if (noMtv) {\n                        return overlapped;\n                    }\n                    var dist0 = Math.abs(extent2[0] - extent[1]);\n                    var dist1 = Math.abs(extent[0] - extent2[1]);\n                    if (Math.min(dist0, dist1) > maxTv.len()) {\n                        if (dist0 < dist1) {\n                            Point.scale(maxTv, axis, -dist0 * inverse);\n                        }\n                        else {\n                            Point.scale(maxTv, axis, dist1 * inverse);\n                        }\n                    }\n                }\n                else if (minTv) {\n                    var dist0 = Math.abs(extent2[0] - extent[1]);\n                    var dist1 = Math.abs(extent[0] - extent2[1]);\n                    if (Math.min(dist0, dist1) < minTv.len()) {\n                        if (dist0 < dist1) {\n                            Point.scale(minTv, axis, dist0 * inverse);\n                        }\n                        else {\n                            Point.scale(minTv, axis, -dist1 * inverse);\n                        }\n                    }\n                }\n            }\n            return overlapped;\n        };\n        OrientedBoundingRect.prototype._getProjMinMaxOnAxis = function (dim, corners, out) {\n            var axis = this._axes[dim];\n            var origin = this._origin;\n            var proj = corners[0].dot(axis) + origin[dim];\n            var min = proj;\n            var max = proj;\n            for (var i = 1; i < corners.length; i++) {\n                var proj_1 = corners[i].dot(axis) + origin[dim];\n                min = Math.min(proj_1, min);\n                max = Math.max(proj_1, max);\n            }\n            out[0] = min;\n            out[1] = max;\n        };\n        return OrientedBoundingRect;\n    }());\n\n    var m = [];\n    var IncrementalDisplayable = (function (_super) {\n        __extends(IncrementalDisplayable, _super);\n        function IncrementalDisplayable() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n            _this.notClear = true;\n            _this.incremental = true;\n            _this._displayables = [];\n            _this._temporaryDisplayables = [];\n            _this._cursor = 0;\n            return _this;\n        }\n        IncrementalDisplayable.prototype.traverse = function (cb, context) {\n            cb.call(context, this);\n        };\n        IncrementalDisplayable.prototype.useStyle = function () {\n            this.style = {};\n        };\n        IncrementalDisplayable.prototype.getCursor = function () {\n            return this._cursor;\n        };\n        IncrementalDisplayable.prototype.innerAfterBrush = function () {\n            this._cursor = this._displayables.length;\n        };\n        IncrementalDisplayable.prototype.clearDisplaybles = function () {\n            this._displayables = [];\n            this._temporaryDisplayables = [];\n            this._cursor = 0;\n            this.markRedraw();\n            this.notClear = false;\n        };\n        IncrementalDisplayable.prototype.clearTemporalDisplayables = function () {\n            this._temporaryDisplayables = [];\n        };\n        IncrementalDisplayable.prototype.addDisplayable = function (displayable, notPersistent) {\n            if (notPersistent) {\n                this._temporaryDisplayables.push(displayable);\n            }\n            else {\n                this._displayables.push(displayable);\n            }\n            this.markRedraw();\n        };\n        IncrementalDisplayable.prototype.addDisplayables = function (displayables, notPersistent) {\n            notPersistent = notPersistent || false;\n            for (var i = 0; i < displayables.length; i++) {\n                this.addDisplayable(displayables[i], notPersistent);\n            }\n        };\n        IncrementalDisplayable.prototype.getDisplayables = function () {\n            return this._displayables;\n        };\n        IncrementalDisplayable.prototype.getTemporalDisplayables = function () {\n            return this._temporaryDisplayables;\n        };\n        IncrementalDisplayable.prototype.eachPendingDisplayable = function (cb) {\n            for (var i = this._cursor; i < this._displayables.length; i++) {\n                cb && cb(this._displayables[i]);\n            }\n            for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n                cb && cb(this._temporaryDisplayables[i]);\n            }\n        };\n        IncrementalDisplayable.prototype.update = function () {\n            this.updateTransform();\n            for (var i = this._cursor; i < this._displayables.length; i++) {\n                var displayable = this._displayables[i];\n                displayable.parent = this;\n                displayable.update();\n                displayable.parent = null;\n            }\n            for (var i = 0; i < this._temporaryDisplayables.length; i++) {\n                var displayable = this._temporaryDisplayables[i];\n                displayable.parent = this;\n                displayable.update();\n                displayable.parent = null;\n            }\n        };\n        IncrementalDisplayable.prototype.getBoundingRect = function () {\n            if (!this._rect) {\n                var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);\n                for (var i = 0; i < this._displayables.length; i++) {\n                    var displayable = this._displayables[i];\n                    var childRect = displayable.getBoundingRect().clone();\n                    if (displayable.needLocalTransform()) {\n                        childRect.applyTransform(displayable.getLocalTransform(m));\n                    }\n                    rect.union(childRect);\n                }\n                this._rect = rect;\n            }\n            return this._rect;\n        };\n        IncrementalDisplayable.prototype.contain = function (x, y) {\n            var localPos = this.transformCoordToLocal(x, y);\n            var rect = this.getBoundingRect();\n            if (rect.contain(localPos[0], localPos[1])) {\n                for (var i = 0; i < this._displayables.length; i++) {\n                    var displayable = this._displayables[i];\n                    if (displayable.contain(x, y)) {\n                        return true;\n                    }\n                }\n            }\n            return false;\n        };\n        return IncrementalDisplayable;\n    }(Displayable));\n\n    var transitionStore = makeInner();\n    /**\n     * Return null if animation is disabled.\n     */\n\n    function getAnimationConfig(animationType, animatableModel, dataIndex, // Extra opts can override the option in animatable model.\n    extraOpts, // TODO It's only for pictorial bar now.\n    extraDelayParams) {\n      var animationPayload; // Check if there is global animation configuration from dataZoom/resize can override the config in option.\n      // If animation is enabled. Will use this animation config in payload.\n      // If animation is disabled. Just ignore it.\n\n      if (animatableModel && animatableModel.ecModel) {\n        var updatePayload = animatableModel.ecModel.getUpdatePayload();\n        animationPayload = updatePayload && updatePayload.animation;\n      }\n\n      var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();\n      var isUpdate = animationType === 'update';\n\n      if (animationEnabled) {\n        var duration = void 0;\n        var easing = void 0;\n        var delay = void 0;\n\n        if (extraOpts) {\n          duration = retrieve2(extraOpts.duration, 200);\n          easing = retrieve2(extraOpts.easing, 'cubicOut');\n          delay = 0;\n        } else {\n          duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration');\n          easing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing');\n          delay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay');\n        } // animation from payload has highest priority.\n\n\n        if (animationPayload) {\n          animationPayload.duration != null && (duration = animationPayload.duration);\n          animationPayload.easing != null && (easing = animationPayload.easing);\n          animationPayload.delay != null && (delay = animationPayload.delay);\n        }\n\n        if (isFunction(delay)) {\n          delay = delay(dataIndex, extraDelayParams);\n        }\n\n        if (isFunction(duration)) {\n          duration = duration(dataIndex);\n        }\n\n        var config = {\n          duration: duration || 0,\n          delay: delay,\n          easing: easing\n        };\n        return config;\n      } else {\n        return null;\n      }\n    }\n\n    function animateOrSetProps(animationType, el, props, animatableModel, dataIndex, cb, during) {\n      var isFrom = false;\n      var removeOpt;\n\n      if (isFunction(dataIndex)) {\n        during = cb;\n        cb = dataIndex;\n        dataIndex = null;\n      } else if (isObject(dataIndex)) {\n        cb = dataIndex.cb;\n        during = dataIndex.during;\n        isFrom = dataIndex.isFrom;\n        removeOpt = dataIndex.removeOpt;\n        dataIndex = dataIndex.dataIndex;\n      }\n\n      var isRemove = animationType === 'leave';\n\n      if (!isRemove) {\n        // Must stop the remove animation.\n        el.stopAnimation('leave');\n      }\n\n      var animationConfig = getAnimationConfig(animationType, animatableModel, dataIndex, isRemove ? removeOpt || {} : null, animatableModel && animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);\n\n      if (animationConfig && animationConfig.duration > 0) {\n        var duration = animationConfig.duration;\n        var animationDelay = animationConfig.delay;\n        var animationEasing = animationConfig.easing;\n        var animateConfig = {\n          duration: duration,\n          delay: animationDelay || 0,\n          easing: animationEasing,\n          done: cb,\n          force: !!cb || !!during,\n          // Set to final state in update/init animation.\n          // So the post processing based on the path shape can be done correctly.\n          setToFinal: !isRemove,\n          scope: animationType,\n          during: during\n        };\n        isFrom ? el.animateFrom(props, animateConfig) : el.animateTo(props, animateConfig);\n      } else {\n        el.stopAnimation(); // If `isFrom`, the props is the \"from\" props.\n\n        !isFrom && el.attr(props); // Call during at least once.\n\n        during && during(1);\n        cb && cb();\n      }\n    }\n    /**\n     * Update graphic element properties with or without animation according to the\n     * configuration in series.\n     *\n     * Caution: this method will stop previous animation.\n     * So do not use this method to one element twice before\n     * animation starts, unless you know what you are doing.\n     * @example\n     *     graphic.updateProps(el, {\n     *         position: [100, 100]\n     *     }, seriesModel, dataIndex, function () { console.log('Animation done!'); });\n     *     // Or\n     *     graphic.updateProps(el, {\n     *         position: [100, 100]\n     *     }, seriesModel, function () { console.log('Animation done!'); });\n     */\n\n\n    function updateProps(el, props, // TODO: TYPE AnimatableModel\n    animatableModel, dataIndex, cb, during) {\n      animateOrSetProps('update', el, props, animatableModel, dataIndex, cb, during);\n    }\n    /**\n     * Init graphic element properties with or without animation according to the\n     * configuration in series.\n     *\n     * Caution: this method will stop previous animation.\n     * So do not use this method to one element twice before\n     * animation starts, unless you know what you are doing.\n     */\n\n    function initProps(el, props, animatableModel, dataIndex, cb, during) {\n      animateOrSetProps('enter', el, props, animatableModel, dataIndex, cb, during);\n    }\n    /**\n     * If element is removed.\n     * It can determine if element is having remove animation.\n     */\n\n    function isElementRemoved(el) {\n      if (!el.__zr) {\n        return true;\n      }\n\n      for (var i = 0; i < el.animators.length; i++) {\n        var animator = el.animators[i];\n\n        if (animator.scope === 'leave') {\n          return true;\n        }\n      }\n\n      return false;\n    }\n    /**\n     * Remove graphic element\n     */\n\n    function removeElement(el, props, animatableModel, dataIndex, cb, during) {\n      // Don't do remove animation twice.\n      if (isElementRemoved(el)) {\n        return;\n      }\n\n      animateOrSetProps('leave', el, props, animatableModel, dataIndex, cb, during);\n    }\n\n    function fadeOutDisplayable(el, animatableModel, dataIndex, done) {\n      el.removeTextContent();\n      el.removeTextGuideLine();\n      removeElement(el, {\n        style: {\n          opacity: 0\n        }\n      }, animatableModel, dataIndex, done);\n    }\n\n    function removeElementWithFadeOut(el, animatableModel, dataIndex) {\n      function doRemove() {\n        el.parent && el.parent.remove(el);\n      } // Hide label and labelLine first\n      // TODO Also use fade out animation?\n\n\n      if (!el.isGroup) {\n        fadeOutDisplayable(el, animatableModel, dataIndex, doRemove);\n      } else {\n        el.traverse(function (disp) {\n          if (!disp.isGroup) {\n            // Can invoke doRemove multiple times.\n            fadeOutDisplayable(disp, animatableModel, dataIndex, doRemove);\n          }\n        });\n      }\n    }\n    /**\n     * Save old style for style transition in universalTransition module.\n     * It's used when element will be reused in each render.\n     * For chart like map, heatmap, which will always create new element.\n     * We don't need to save this because universalTransition can get old style from the old element\n     */\n\n    function saveOldStyle(el) {\n      transitionStore(el).oldStyle = el.style;\n    }\n\n    var mathMax$4 = Math.max;\n    var mathMin$4 = Math.min;\n    var _customShapeMap = {};\n    /**\n     * Extend shape with parameters\n     */\n\n    function extendShape(opts) {\n      return Path.extend(opts);\n    }\n    var extendPathFromString = extendFromString;\n    /**\n     * Extend path\n     */\n\n    function extendPath(pathData, opts) {\n      return extendPathFromString(pathData, opts);\n    }\n    /**\n     * Register a user defined shape.\n     * The shape class can be fetched by `getShapeClass`\n     * This method will overwrite the registered shapes, including\n     * the registered built-in shapes, if using the same `name`.\n     * The shape can be used in `custom series` and\n     * `graphic component` by declaring `{type: name}`.\n     *\n     * @param name\n     * @param ShapeClass Can be generated by `extendShape`.\n     */\n\n    function registerShape(name, ShapeClass) {\n      _customShapeMap[name] = ShapeClass;\n    }\n    /**\n     * Find shape class registered by `registerShape`. Usually used in\n     * fetching user defined shape.\n     *\n     * [Caution]:\n     * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared\n     * to use user registered shapes.\n     * Because the built-in shape (see `getBuiltInShape`) will be registered by\n     * `registerShape` by default. That enables users to get both built-in\n     * shapes as well as the shapes belonging to themsleves. But users can overwrite\n     * the built-in shapes by using names like 'circle', 'rect' via calling\n     * `registerShape`. So the echarts inner featrues should not fetch shapes from here\n     * in case that it is overwritten by users, except that some features, like\n     * `custom series`, `graphic component`, do it deliberately.\n     *\n     * (2) In the features like `custom series`, `graphic component`, the user input\n     * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic\n     * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names\n     * are reserved names, that is, if some user registers a shape named `'image'`,\n     * the shape will not be used. If we intending to add some more reserved names\n     * in feature, that might bring break changes (disable some existing user shape\n     * names). But that case probably rarely happens. So we don't make more mechanism\n     * to resolve this issue here.\n     *\n     * @param name\n     * @return The shape class. If not found, return nothing.\n     */\n\n    function getShapeClass(name) {\n      if (_customShapeMap.hasOwnProperty(name)) {\n        return _customShapeMap[name];\n      }\n    }\n    /**\n     * Create a path element from path data string\n     * @param pathData\n     * @param opts\n     * @param rect\n     * @param layout 'center' or 'cover' default to be cover\n     */\n\n    function makePath(pathData, opts, rect, layout) {\n      var path = createFromString(pathData, opts);\n\n      if (rect) {\n        if (layout === 'center') {\n          rect = centerGraphic(rect, path.getBoundingRect());\n        }\n\n        resizePath(path, rect);\n      }\n\n      return path;\n    }\n    /**\n     * Create a image element from image url\n     * @param imageUrl image url\n     * @param opts options\n     * @param rect constrain rect\n     * @param layout 'center' or 'cover'. Default to be 'cover'\n     */\n\n    function makeImage(imageUrl, rect, layout) {\n      var zrImg = new ZRImage({\n        style: {\n          image: imageUrl,\n          x: rect.x,\n          y: rect.y,\n          width: rect.width,\n          height: rect.height\n        },\n        onload: function (img) {\n          if (layout === 'center') {\n            var boundingRect = {\n              width: img.width,\n              height: img.height\n            };\n            zrImg.setStyle(centerGraphic(rect, boundingRect));\n          }\n        }\n      });\n      return zrImg;\n    }\n    /**\n     * Get position of centered element in bounding box.\n     *\n     * @param  rect         element local bounding box\n     * @param  boundingRect constraint bounding box\n     * @return element position containing x, y, width, and height\n     */\n\n    function centerGraphic(rect, boundingRect) {\n      // Set rect to center, keep width / height ratio.\n      var aspect = boundingRect.width / boundingRect.height;\n      var width = rect.height * aspect;\n      var height;\n\n      if (width <= rect.width) {\n        height = rect.height;\n      } else {\n        width = rect.width;\n        height = width / aspect;\n      }\n\n      var cx = rect.x + rect.width / 2;\n      var cy = rect.y + rect.height / 2;\n      return {\n        x: cx - width / 2,\n        y: cy - height / 2,\n        width: width,\n        height: height\n      };\n    }\n\n    var mergePath$1 = mergePath;\n    /**\n     * Resize a path to fit the rect\n     * @param path\n     * @param rect\n     */\n\n    function resizePath(path, rect) {\n      if (!path.applyTransform) {\n        return;\n      }\n\n      var pathRect = path.getBoundingRect();\n      var m = pathRect.calculateTransform(rect);\n      path.applyTransform(m);\n    }\n    /**\n     * Sub pixel optimize line for canvas\n     */\n\n    function subPixelOptimizeLine$1(shape, lineWidth) {\n      subPixelOptimizeLine(shape, shape, {\n        lineWidth: lineWidth\n      });\n      return shape;\n    }\n    /**\n     * Get transform matrix of target (param target),\n     * in coordinate of its ancestor (param ancestor)\n     *\n     * @param target\n     * @param [ancestor]\n     */\n\n    function getTransform(target, ancestor) {\n      var mat = identity([]);\n\n      while (target && target !== ancestor) {\n        mul$1(mat, target.getLocalTransform(), mat);\n        target = target.parent;\n      }\n\n      return mat;\n    }\n\n    function isNotGroup(el) {\n      return !el.isGroup;\n    }\n\n    function isPath(el) {\n      return el.shape != null;\n    }\n    /**\n     * Apply group transition animation from g1 to g2.\n     * If no animatableModel, no animation.\n     */\n\n\n    function groupTransition(g1, g2, animatableModel) {\n      if (!g1 || !g2) {\n        return;\n      }\n\n      function getElMap(g) {\n        var elMap = {};\n        g.traverse(function (el) {\n          if (isNotGroup(el) && el.anid) {\n            elMap[el.anid] = el;\n          }\n        });\n        return elMap;\n      }\n\n      function getAnimatableProps(el) {\n        var obj = {\n          x: el.x,\n          y: el.y,\n          rotation: el.rotation\n        };\n\n        if (isPath(el)) {\n          obj.shape = extend({}, el.shape);\n        }\n\n        return obj;\n      }\n\n      var elMap1 = getElMap(g1);\n      g2.traverse(function (el) {\n        if (isNotGroup(el) && el.anid) {\n          var oldEl = elMap1[el.anid];\n\n          if (oldEl) {\n            var newProp = getAnimatableProps(el);\n            el.attr(getAnimatableProps(oldEl));\n            updateProps(el, newProp, animatableModel, getECData(el).dataIndex);\n          }\n        }\n      });\n    }\n    function clipPointsByRect(points, rect) {\n      // FIXME: This way might be incorrect when graphic clipped by a corner\n      // and when element has a border.\n      return map(points, function (point) {\n        var x = point[0];\n        x = mathMax$4(x, rect.x);\n        x = mathMin$4(x, rect.x + rect.width);\n        var y = point[1];\n        y = mathMax$4(y, rect.y);\n        y = mathMin$4(y, rect.y + rect.height);\n        return [x, y];\n      });\n    }\n    /**\n     * Return a new clipped rect. If rect size are negative, return undefined.\n     */\n\n    function clipRectByRect(targetRect, rect) {\n      var x = mathMax$4(targetRect.x, rect.x);\n      var x2 = mathMin$4(targetRect.x + targetRect.width, rect.x + rect.width);\n      var y = mathMax$4(targetRect.y, rect.y);\n      var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border,\n      // should be painted. So return undefined.\n\n      if (x2 >= x && y2 >= y) {\n        return {\n          x: x,\n          y: y,\n          width: x2 - x,\n          height: y2 - y\n        };\n      }\n    }\n    function createIcon(iconStr, // Support 'image://' or 'path://' or direct svg path.\n    opt, rect) {\n      var innerOpts = extend({\n        rectHover: true\n      }, opt);\n      var style = innerOpts.style = {\n        strokeNoScale: true\n      };\n      rect = rect || {\n        x: -1,\n        y: -1,\n        width: 2,\n        height: 2\n      };\n\n      if (iconStr) {\n        return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), defaults(style, rect), new ZRImage(innerOpts)) : makePath(iconStr.replace('path://', ''), innerOpts, rect, 'center');\n      }\n    }\n\n    function setTooltipConfig(opt) {\n      var itemTooltipOption = opt.itemTooltipOption;\n      var componentModel = opt.componentModel;\n      var itemName = opt.itemName;\n      var itemTooltipOptionObj = isString(itemTooltipOption) ? {\n        formatter: itemTooltipOption\n      } : itemTooltipOption;\n      var mainType = componentModel.mainType;\n      var componentIndex = componentModel.componentIndex;\n      var formatterParams = {\n        componentType: mainType,\n        name: itemName,\n        $vars: ['name']\n      };\n      formatterParams[mainType + 'Index'] = componentIndex;\n      var formatterParamsExtra = opt.formatterParamsExtra;\n\n      if (formatterParamsExtra) {\n        each(keys(formatterParamsExtra), function (key) {\n          if (!hasOwn(formatterParams, key)) {\n            formatterParams[key] = formatterParamsExtra[key];\n            formatterParams.$vars.push(key);\n          }\n        });\n      }\n\n      var ecData = getECData(opt.el);\n      ecData.componentMainType = mainType;\n      ecData.componentIndex = componentIndex;\n      ecData.tooltipConfig = {\n        name: itemName,\n        option: defaults({\n          content: itemName,\n          formatterParams: formatterParams\n        }, itemTooltipOptionObj)\n      };\n    }\n\n    function traverseElement(el, cb) {\n      var stopped; // TODO\n      // Polyfill for fixing zrender group traverse don't visit it's root issue.\n\n      if (el.isGroup) {\n        stopped = cb(el);\n      }\n\n      if (!stopped) {\n        el.traverse(cb);\n      }\n    }\n\n    function traverseElements(els, cb) {\n      if (els) {\n        if (isArray(els)) {\n          for (var i = 0; i < els.length; i++) {\n            traverseElement(els[i], cb);\n          }\n        } else {\n          traverseElement(els, cb);\n        }\n      }\n    } // Register built-in shapes. These shapes might be overwritten\n    // by users, although we do not recommend that.\n\n    registerShape('circle', Circle);\n    registerShape('ellipse', Ellipse);\n    registerShape('sector', Sector);\n    registerShape('ring', Ring);\n    registerShape('polygon', Polygon);\n    registerShape('polyline', Polyline);\n    registerShape('rect', Rect);\n    registerShape('line', Line);\n    registerShape('bezierCurve', BezierCurve);\n    registerShape('arc', Arc);\n\n    var EMPTY_OBJ = {};\n    function setLabelText(label, labelTexts) {\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        var text = labelTexts[stateName];\n        var state = label.ensureState(stateName);\n        state.style = state.style || {};\n        state.style.text = text;\n      }\n\n      var oldStates = label.currentStates.slice();\n      label.clearStates(true);\n      label.setStyle({\n        text: labelTexts.normal\n      });\n      label.useStates(oldStates, true);\n    }\n\n    function getLabelText(opt, stateModels, interpolatedValue) {\n      var labelFetcher = opt.labelFetcher;\n      var labelDataIndex = opt.labelDataIndex;\n      var labelDimIndex = opt.labelDimIndex;\n      var normalModel = stateModels.normal;\n      var baseText;\n\n      if (labelFetcher) {\n        baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, normalModel && normalModel.get('formatter'), interpolatedValue != null ? {\n          interpolatedValue: interpolatedValue\n        } : null);\n      }\n\n      if (baseText == null) {\n        baseText = isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt, interpolatedValue) : opt.defaultText;\n      }\n\n      var statesText = {\n        normal: baseText\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        var stateModel = stateModels[stateName];\n        statesText[stateName] = retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, stateName, null, labelDimIndex, stateModel && stateModel.get('formatter')) : null, baseText);\n      }\n\n      return statesText;\n    }\n\n    function setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified // TODO specified position?\n    ) {\n      opt = opt || EMPTY_OBJ;\n      var isSetOnText = targetEl instanceof ZRText;\n      var needsCreateText = false;\n\n      for (var i = 0; i < DISPLAY_STATES.length; i++) {\n        var stateModel = labelStatesModels[DISPLAY_STATES[i]];\n\n        if (stateModel && stateModel.getShallow('show')) {\n          needsCreateText = true;\n          break;\n        }\n      }\n\n      var textContent = isSetOnText ? targetEl : targetEl.getTextContent();\n\n      if (needsCreateText) {\n        if (!isSetOnText) {\n          // Reuse the previous\n          if (!textContent) {\n            textContent = new ZRText();\n            targetEl.setTextContent(textContent);\n          } // Use same state proxy\n\n\n          if (targetEl.stateProxy) {\n            textContent.stateProxy = targetEl.stateProxy;\n          }\n        }\n\n        var labelStatesTexts = getLabelText(opt, labelStatesModels);\n        var normalModel = labelStatesModels.normal;\n        var showNormal = !!normalModel.getShallow('show');\n        var normalStyle = createTextStyle(normalModel, stateSpecified && stateSpecified.normal, opt, false, !isSetOnText);\n        normalStyle.text = labelStatesTexts.normal;\n\n        if (!isSetOnText) {\n          // Always create new\n          targetEl.setTextConfig(createTextConfig(normalModel, opt, false));\n        }\n\n        for (var i = 0; i < SPECIAL_STATES.length; i++) {\n          var stateName = SPECIAL_STATES[i];\n          var stateModel = labelStatesModels[stateName];\n\n          if (stateModel) {\n            var stateObj = textContent.ensureState(stateName);\n            var stateShow = !!retrieve2(stateModel.getShallow('show'), showNormal);\n\n            if (stateShow !== showNormal) {\n              stateObj.ignore = !stateShow;\n            }\n\n            stateObj.style = createTextStyle(stateModel, stateSpecified && stateSpecified[stateName], opt, true, !isSetOnText);\n            stateObj.style.text = labelStatesTexts[stateName];\n\n            if (!isSetOnText) {\n              var targetElEmphasisState = targetEl.ensureState(stateName);\n              targetElEmphasisState.textConfig = createTextConfig(stateModel, opt, true);\n            }\n          }\n        } // PENDING: if there is many requirements that emphasis position\n        // need to be different from normal position, we might consider\n        // auto silent is those cases.\n\n\n        textContent.silent = !!normalModel.getShallow('silent'); // Keep x and y\n\n        if (textContent.style.x != null) {\n          normalStyle.x = textContent.style.x;\n        }\n\n        if (textContent.style.y != null) {\n          normalStyle.y = textContent.style.y;\n        }\n\n        textContent.ignore = !showNormal; // Always create new style.\n\n        textContent.useStyle(normalStyle);\n        textContent.dirty();\n\n        if (opt.enableTextSetter) {\n          labelInner(textContent).setLabelText = function (interpolatedValue) {\n            var labelStatesTexts = getLabelText(opt, labelStatesModels, interpolatedValue);\n            setLabelText(textContent, labelStatesTexts);\n          };\n        }\n      } else if (textContent) {\n        // Not display rich text.\n        textContent.ignore = true;\n      }\n\n      targetEl.dirty();\n    }\n    function getLabelStatesModels(itemModel, labelName) {\n      labelName = labelName || 'label';\n      var statesModels = {\n        normal: itemModel.getModel(labelName)\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        statesModels[stateName] = itemModel.getModel([stateName, labelName]);\n      }\n\n      return statesModels;\n    }\n    /**\n     * Set basic textStyle properties.\n     */\n\n    function createTextStyle(textStyleModel, specifiedTextStyle, // Fixed style in the code. Can't be set by model.\n    opt, isNotNormal, isAttached // If text is attached on an element. If so, auto color will handling in zrender.\n    ) {\n      var textStyle = {};\n      setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached);\n      specifiedTextStyle && extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\n\n      return textStyle;\n    }\n    function createTextConfig(textStyleModel, opt, isNotNormal) {\n      opt = opt || {};\n      var textConfig = {};\n      var labelPosition;\n      var labelRotate = textStyleModel.getShallow('rotate');\n      var labelDistance = retrieve2(textStyleModel.getShallow('distance'), isNotNormal ? null : 5);\n      var labelOffset = textStyleModel.getShallow('offset');\n      labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used\n      // in bar series, and magric type should be considered.\n\n      labelPosition === 'outside' && (labelPosition = opt.defaultOutsidePosition || 'top');\n\n      if (labelPosition != null) {\n        textConfig.position = labelPosition;\n      }\n\n      if (labelOffset != null) {\n        textConfig.offset = labelOffset;\n      }\n\n      if (labelRotate != null) {\n        labelRotate *= Math.PI / 180;\n        textConfig.rotation = labelRotate;\n      }\n\n      if (labelDistance != null) {\n        textConfig.distance = labelDistance;\n      } // fill and auto is determined by the color of path fill if it's not specified by developers.\n\n\n      textConfig.outsideFill = textStyleModel.get('color') === 'inherit' ? opt.inheritColor || null : 'auto';\n      return textConfig;\n    }\n    /**\n     * The uniform entry of set text style, that is, retrieve style definitions\n     * from `model` and set to `textStyle` object.\n     *\n     * Never in merge mode, but in overwrite mode, that is, all of the text style\n     * properties will be set. (Consider the states of normal and emphasis and\n     * default value can be adopted, merge would make the logic too complicated\n     * to manage.)\n     */\n\n    function setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached) {\n      // Consider there will be abnormal when merge hover style to normal style if given default value.\n      opt = opt || EMPTY_OBJ;\n      var ecModel = textStyleModel.ecModel;\n      var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:\n      // {\n      //     data: [{\n      //         value: 12,\n      //         label: {\n      //             rich: {\n      //                 // no 'a' here but using parent 'a'.\n      //             }\n      //         }\n      //     }],\n      //     rich: {\n      //         a: { ... }\n      //     }\n      // }\n\n      var richItemNames = getRichItemNames(textStyleModel);\n      var richResult;\n\n      if (richItemNames) {\n        richResult = {};\n\n        for (var name_1 in richItemNames) {\n          if (richItemNames.hasOwnProperty(name_1)) {\n            // Cascade is supported in rich.\n            var richTextStyle = textStyleModel.getModel(['rich', name_1]); // In rich, never `disableBox`.\n            // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,\n            // the default color `'blue'` will not be adopted if no color declared in `rich`.\n            // That might confuses users. So probably we should put `textStyleModel` as the\n            // root ancestor of the `richTextStyle`. But that would be a break change.\n\n            setTokenTextStyle(richResult[name_1] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true);\n          }\n        }\n      }\n\n      if (richResult) {\n        textStyle.rich = richResult;\n      }\n\n      var overflow = textStyleModel.get('overflow');\n\n      if (overflow) {\n        textStyle.overflow = overflow;\n      }\n\n      var margin = textStyleModel.get('minMargin');\n\n      if (margin != null) {\n        textStyle.margin = margin;\n      }\n\n      setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false);\n    } // Consider case:\n    // {\n    //     data: [{\n    //         value: 12,\n    //         label: {\n    //             rich: {\n    //                 // no 'a' here but using parent 'a'.\n    //             }\n    //         }\n    //     }],\n    //     rich: {\n    //         a: { ... }\n    //     }\n    // }\n    // TODO TextStyleModel\n\n\n    function getRichItemNames(textStyleModel) {\n      // Use object to remove duplicated names.\n      var richItemNameMap;\n\n      while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {\n        var rich = (textStyleModel.option || EMPTY_OBJ).rich;\n\n        if (rich) {\n          richItemNameMap = richItemNameMap || {};\n          var richKeys = keys(rich);\n\n          for (var i = 0; i < richKeys.length; i++) {\n            var richKey = richKeys[i];\n            richItemNameMap[richKey] = 1;\n          }\n        }\n\n        textStyleModel = textStyleModel.parentModel;\n      }\n\n      return richItemNameMap;\n    }\n\n    var TEXT_PROPS_WITH_GLOBAL = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'];\n    var TEXT_PROPS_SELF = ['align', 'lineHeight', 'width', 'height', 'tag', 'verticalAlign'];\n    var TEXT_PROPS_BOX = ['padding', 'borderWidth', 'borderRadius', 'borderDashOffset', 'backgroundColor', 'borderColor', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\n\n    function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, isBlock, inRich) {\n      // In merge mode, default value should not be given.\n      globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ;\n      var inheritColor = opt && opt.inheritColor;\n      var fillColor = textStyleModel.getShallow('color');\n      var strokeColor = textStyleModel.getShallow('textBorderColor');\n      var opacity = retrieve2(textStyleModel.getShallow('opacity'), globalTextStyle.opacity);\n\n      if (fillColor === 'inherit' || fillColor === 'auto') {\n        if (\"development\" !== 'production') {\n          if (fillColor === 'auto') {\n            deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n          }\n        }\n\n        if (inheritColor) {\n          fillColor = inheritColor;\n        } else {\n          fillColor = null;\n        }\n      }\n\n      if (strokeColor === 'inherit' || strokeColor === 'auto') {\n        if (\"development\" !== 'production') {\n          if (strokeColor === 'auto') {\n            deprecateReplaceLog('color: \\'auto\\'', 'color: \\'inherit\\'');\n          }\n        }\n\n        if (inheritColor) {\n          strokeColor = inheritColor;\n        } else {\n          strokeColor = null;\n        }\n      }\n\n      if (!isAttached) {\n        // Only use default global textStyle.color if text is individual.\n        // Otherwise it will use the strategy of attached text color because text may be on a path.\n        fillColor = fillColor || globalTextStyle.color;\n        strokeColor = strokeColor || globalTextStyle.textBorderColor;\n      }\n\n      if (fillColor != null) {\n        textStyle.fill = fillColor;\n      }\n\n      if (strokeColor != null) {\n        textStyle.stroke = strokeColor;\n      }\n\n      var textBorderWidth = retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth);\n\n      if (textBorderWidth != null) {\n        textStyle.lineWidth = textBorderWidth;\n      }\n\n      var textBorderType = retrieve2(textStyleModel.getShallow('textBorderType'), globalTextStyle.textBorderType);\n\n      if (textBorderType != null) {\n        textStyle.lineDash = textBorderType;\n      }\n\n      var textBorderDashOffset = retrieve2(textStyleModel.getShallow('textBorderDashOffset'), globalTextStyle.textBorderDashOffset);\n\n      if (textBorderDashOffset != null) {\n        textStyle.lineDashOffset = textBorderDashOffset;\n      }\n\n      if (!isNotNormal && opacity == null && !inRich) {\n        opacity = opt && opt.defaultOpacity;\n      }\n\n      if (opacity != null) {\n        textStyle.opacity = opacity;\n      } // TODO\n\n\n      if (!isNotNormal && !isAttached) {\n        // Set default finally.\n        if (textStyle.fill == null && opt.inheritColor) {\n          textStyle.fill = opt.inheritColor;\n        }\n      } // Do not use `getFont` here, because merge should be supported, where\n      // part of these properties may be changed in emphasis style, and the\n      // others should remain their original value got from normal style.\n\n\n      for (var i = 0; i < TEXT_PROPS_WITH_GLOBAL.length; i++) {\n        var key = TEXT_PROPS_WITH_GLOBAL[i];\n        var val = retrieve2(textStyleModel.getShallow(key), globalTextStyle[key]);\n\n        if (val != null) {\n          textStyle[key] = val;\n        }\n      }\n\n      for (var i = 0; i < TEXT_PROPS_SELF.length; i++) {\n        var key = TEXT_PROPS_SELF[i];\n        var val = textStyleModel.getShallow(key);\n\n        if (val != null) {\n          textStyle[key] = val;\n        }\n      }\n\n      if (textStyle.verticalAlign == null) {\n        var baseline = textStyleModel.getShallow('baseline');\n\n        if (baseline != null) {\n          textStyle.verticalAlign = baseline;\n        }\n      }\n\n      if (!isBlock || !opt.disableBox) {\n        for (var i = 0; i < TEXT_PROPS_BOX.length; i++) {\n          var key = TEXT_PROPS_BOX[i];\n          var val = textStyleModel.getShallow(key);\n\n          if (val != null) {\n            textStyle[key] = val;\n          }\n        }\n\n        var borderType = textStyleModel.getShallow('borderType');\n\n        if (borderType != null) {\n          textStyle.borderDash = borderType;\n        }\n\n        if ((textStyle.backgroundColor === 'auto' || textStyle.backgroundColor === 'inherit') && inheritColor) {\n          if (\"development\" !== 'production') {\n            if (textStyle.backgroundColor === 'auto') {\n              deprecateReplaceLog('backgroundColor: \\'auto\\'', 'backgroundColor: \\'inherit\\'');\n            }\n          }\n\n          textStyle.backgroundColor = inheritColor;\n        }\n\n        if ((textStyle.borderColor === 'auto' || textStyle.borderColor === 'inherit') && inheritColor) {\n          if (\"development\" !== 'production') {\n            if (textStyle.borderColor === 'auto') {\n              deprecateReplaceLog('borderColor: \\'auto\\'', 'borderColor: \\'inherit\\'');\n            }\n          }\n\n          textStyle.borderColor = inheritColor;\n        }\n      }\n    }\n\n    function getFont(opt, ecModel) {\n      var gTextStyleModel = ecModel && ecModel.getModel('textStyle');\n      return trim([// FIXME in node-canvas fontWeight is before fontStyle\n      opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));\n    }\n    var labelInner = makeInner();\n    function setLabelValueAnimation(label, labelStatesModels, value, getDefaultText) {\n      if (!label) {\n        return;\n      }\n\n      var obj = labelInner(label);\n      obj.prevValue = obj.value;\n      obj.value = value;\n      var normalLabelModel = labelStatesModels.normal;\n      obj.valueAnimation = normalLabelModel.get('valueAnimation');\n\n      if (obj.valueAnimation) {\n        obj.precision = normalLabelModel.get('precision');\n        obj.defaultInterpolatedText = getDefaultText;\n        obj.statesModels = labelStatesModels;\n      }\n    }\n\n    var PATH_COLOR = ['textStyle', 'color'];\n    var textStyleParams = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'padding', 'lineHeight', 'rich', 'width', 'height', 'overflow']; // TODO Performance improvement?\n\n    var tmpText = new ZRText();\n\n    var TextStyleMixin =\n    /** @class */\n    function () {\n      function TextStyleMixin() {}\n      /**\n       * Get color property or get color from option.textStyle.color\n       */\n      // TODO Callback\n\n\n      TextStyleMixin.prototype.getTextColor = function (isEmphasis) {\n        var ecModel = this.ecModel;\n        return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null);\n      };\n      /**\n       * Create font string from fontStyle, fontWeight, fontSize, fontFamily\n       * @return {string}\n       */\n\n\n      TextStyleMixin.prototype.getFont = function () {\n        return getFont({\n          fontStyle: this.getShallow('fontStyle'),\n          fontWeight: this.getShallow('fontWeight'),\n          fontSize: this.getShallow('fontSize'),\n          fontFamily: this.getShallow('fontFamily')\n        }, this.ecModel);\n      };\n\n      TextStyleMixin.prototype.getTextRect = function (text) {\n        var style = {\n          text: text,\n          verticalAlign: this.getShallow('verticalAlign') || this.getShallow('baseline')\n        };\n\n        for (var i = 0; i < textStyleParams.length; i++) {\n          style[textStyleParams[i]] = this.getShallow(textStyleParams[i]);\n        }\n\n        tmpText.useStyle(style);\n        tmpText.update();\n        return tmpText.getBoundingRect();\n      };\n\n      return TextStyleMixin;\n    }();\n\n    var LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getLineStyle = makeStyleMapper(LINE_STYLE_KEY_MAP);\n\n    var LineStyleMixin =\n    /** @class */\n    function () {\n      function LineStyleMixin() {}\n\n      LineStyleMixin.prototype.getLineStyle = function (excludes) {\n        return getLineStyle(this, excludes);\n      };\n\n      return LineStyleMixin;\n    }();\n\n    var ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.\n    // So do not transfer decal directly.\n    ];\n    var getItemStyle = makeStyleMapper(ITEM_STYLE_KEY_MAP);\n\n    var ItemStyleMixin =\n    /** @class */\n    function () {\n      function ItemStyleMixin() {}\n\n      ItemStyleMixin.prototype.getItemStyle = function (excludes, includes) {\n        return getItemStyle(this, excludes, includes);\n      };\n\n      return ItemStyleMixin;\n    }();\n\n    var Model =\n    /** @class */\n    function () {\n      function Model(option, parentModel, ecModel) {\n        this.parentModel = parentModel;\n        this.ecModel = ecModel;\n        this.option = option; // Simple optimization\n        // if (this.init) {\n        //     if (arguments.length <= 4) {\n        //         this.init(option, parentModel, ecModel, extraOpt);\n        //     }\n        //     else {\n        //         this.init.apply(this, arguments);\n        //     }\n        // }\n      }\n\n      Model.prototype.init = function (option, parentModel, ecModel) {\n        var rest = [];\n\n        for (var _i = 3; _i < arguments.length; _i++) {\n          rest[_i - 3] = arguments[_i];\n        }\n      };\n      /**\n       * Merge the input option to me.\n       */\n\n\n      Model.prototype.mergeOption = function (option, ecModel) {\n        merge(this.option, option, true);\n      }; // `path` can be 'a.b.c', so the return value type have to be `ModelOption`\n      // TODO: TYPE strict key check?\n      // get(path: string | string[], ignoreParent?: boolean): ModelOption;\n\n\n      Model.prototype.get = function (path, ignoreParent) {\n        if (path == null) {\n          return this.option;\n        }\n\n        return this._doGet(this.parsePath(path), !ignoreParent && this.parentModel);\n      };\n\n      Model.prototype.getShallow = function (key, ignoreParent) {\n        var option = this.option;\n        var val = option == null ? option : option[key];\n\n        if (val == null && !ignoreParent) {\n          var parentModel = this.parentModel;\n\n          if (parentModel) {\n            // FIXME:TS do not know how to make it works\n            val = parentModel.getShallow(key);\n          }\n        }\n\n        return val;\n      }; // `path` can be 'a.b.c', so the return value type have to be `Model<ModelOption>`\n      // getModel(path: string | string[], parentModel?: Model): Model;\n      // TODO 'a.b.c' is deprecated\n\n\n      Model.prototype.getModel = function (path, parentModel) {\n        var hasPath = path != null;\n        var pathFinal = hasPath ? this.parsePath(path) : null;\n        var obj = hasPath ? this._doGet(pathFinal) : this.option;\n        parentModel = parentModel || this.parentModel && this.parentModel.getModel(this.resolveParentPath(pathFinal));\n        return new Model(obj, parentModel, this.ecModel);\n      };\n      /**\n       * If model has option\n       */\n\n\n      Model.prototype.isEmpty = function () {\n        return this.option == null;\n      };\n\n      Model.prototype.restoreData = function () {}; // Pending\n\n\n      Model.prototype.clone = function () {\n        var Ctor = this.constructor;\n        return new Ctor(clone(this.option));\n      }; // setReadOnly(properties): void {\n      // clazzUtil.setReadOnly(this, properties);\n      // }\n      // If path is null/undefined, return null/undefined.\n\n\n      Model.prototype.parsePath = function (path) {\n        if (typeof path === 'string') {\n          return path.split('.');\n        }\n\n        return path;\n      }; // Resolve path for parent. Perhaps useful when parent use a different property.\n      // Default to be a identity resolver.\n      // Can be modified to a different resolver.\n\n\n      Model.prototype.resolveParentPath = function (path) {\n        return path;\n      }; // FIXME:TS check whether put this method here\n\n\n      Model.prototype.isAnimationEnabled = function () {\n        if (!env.node && this.option) {\n          if (this.option.animation != null) {\n            return !!this.option.animation;\n          } else if (this.parentModel) {\n            return this.parentModel.isAnimationEnabled();\n          }\n        }\n      };\n\n      Model.prototype._doGet = function (pathArr, parentModel) {\n        var obj = this.option;\n\n        if (!pathArr) {\n          return obj;\n        }\n\n        for (var i = 0; i < pathArr.length; i++) {\n          // Ignore empty\n          if (!pathArr[i]) {\n            continue;\n          } // obj could be number/string/... (like 0)\n\n\n          obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null;\n\n          if (obj == null) {\n            break;\n          }\n        }\n\n        if (obj == null && parentModel) {\n          obj = parentModel._doGet(this.resolveParentPath(pathArr), parentModel.parentModel);\n        }\n\n        return obj;\n      };\n\n      return Model;\n    }();\n\n    enableClassExtend(Model);\n    enableClassCheck(Model);\n    mixin(Model, LineStyleMixin);\n    mixin(Model, ItemStyleMixin);\n    mixin(Model, AreaStyleMixin);\n    mixin(Model, TextStyleMixin);\n\n    var base = Math.round(Math.random() * 10);\n    /**\n     * @public\n     * @param {string} type\n     * @return {string}\n     */\n\n    function getUID(type) {\n      // Considering the case of crossing js context,\n      // use Math.random to make id as unique as possible.\n      return [type || '', base++].join('_');\n    }\n    /**\n     * Implements `SubTypeDefaulterManager` for `target`.\n     */\n\n    function enableSubTypeDefaulter(target) {\n      var subTypeDefaulters = {};\n\n      target.registerSubTypeDefaulter = function (componentType, defaulter) {\n        var componentTypeInfo = parseClassType(componentType);\n        subTypeDefaulters[componentTypeInfo.main] = defaulter;\n      };\n\n      target.determineSubType = function (componentType, option) {\n        var type = option.type;\n\n        if (!type) {\n          var componentTypeMain = parseClassType(componentType).main;\n\n          if (target.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {\n            type = subTypeDefaulters[componentTypeMain](option);\n          }\n        }\n\n        return type;\n      };\n    }\n    /**\n     * Implements `TopologicalTravelable<any>` for `entity`.\n     *\n     * Topological travel on Activity Network (Activity On Vertices).\n     * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].\n     * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.\n     * If there is circular dependencey, Error will be thrown.\n     */\n\n    function enableTopologicalTravel(entity, dependencyGetter) {\n      /**\n       * @param targetNameList Target Component type list.\n       *                       Can be ['aa', 'bb', 'aa.xx']\n       * @param fullNameList By which we can build dependency graph.\n       * @param callback Params: componentType, dependencies.\n       * @param context Scope of callback.\n       */\n      entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {\n        if (!targetNameList.length) {\n          return;\n        }\n\n        var result = makeDepndencyGraph(fullNameList);\n        var graph = result.graph;\n        var noEntryList = result.noEntryList;\n        var targetNameSet = {};\n        each(targetNameList, function (name) {\n          targetNameSet[name] = true;\n        });\n\n        while (noEntryList.length) {\n          var currComponentType = noEntryList.pop();\n          var currVertex = graph[currComponentType];\n          var isInTargetNameSet = !!targetNameSet[currComponentType];\n\n          if (isInTargetNameSet) {\n            callback.call(context, currComponentType, currVertex.originalDeps.slice());\n            delete targetNameSet[currComponentType];\n          }\n\n          each(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge);\n        }\n\n        each(targetNameSet, function () {\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            errMsg = makePrintable('Circular dependency may exists: ', targetNameSet, targetNameList, fullNameList);\n          }\n\n          throw new Error(errMsg);\n        });\n\n        function removeEdge(succComponentType) {\n          graph[succComponentType].entryCount--;\n\n          if (graph[succComponentType].entryCount === 0) {\n            noEntryList.push(succComponentType);\n          }\n        } // Consider this case: legend depends on series, and we call\n        // chart.setOption({series: [...]}), where only series is in option.\n        // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will\n        // not be called, but only sereis.mergeOption is called. Thus legend\n        // have no chance to update its local record about series (like which\n        // name of series is available in legend).\n\n\n        function removeEdgeAndAdd(succComponentType) {\n          targetNameSet[succComponentType] = true;\n          removeEdge(succComponentType);\n        }\n      };\n\n      function makeDepndencyGraph(fullNameList) {\n        var graph = {};\n        var noEntryList = [];\n        each(fullNameList, function (name) {\n          var thisItem = createDependencyGraphItem(graph, name);\n          var originalDeps = thisItem.originalDeps = dependencyGetter(name);\n          var availableDeps = getAvailableDependencies(originalDeps, fullNameList);\n          thisItem.entryCount = availableDeps.length;\n\n          if (thisItem.entryCount === 0) {\n            noEntryList.push(name);\n          }\n\n          each(availableDeps, function (dependentName) {\n            if (indexOf(thisItem.predecessor, dependentName) < 0) {\n              thisItem.predecessor.push(dependentName);\n            }\n\n            var thatItem = createDependencyGraphItem(graph, dependentName);\n\n            if (indexOf(thatItem.successor, dependentName) < 0) {\n              thatItem.successor.push(name);\n            }\n          });\n        });\n        return {\n          graph: graph,\n          noEntryList: noEntryList\n        };\n      }\n\n      function createDependencyGraphItem(graph, name) {\n        if (!graph[name]) {\n          graph[name] = {\n            predecessor: [],\n            successor: []\n          };\n        }\n\n        return graph[name];\n      }\n\n      function getAvailableDependencies(originalDeps, fullNameList) {\n        var availableDeps = [];\n        each(originalDeps, function (dep) {\n          indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);\n        });\n        return availableDeps;\n      }\n    }\n    function inheritDefaultOption(superOption, subOption) {\n      // See also `model/Component.ts#getDefaultOption`\n      return merge(merge({}, superOption, true), subOption, true);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * Language: English.\n     */\n    var langEN = {\n      time: {\n        month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\n        monthAbbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n        dayOfWeek: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],\n        dayOfWeekAbbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']\n      },\n      legend: {\n        selector: {\n          all: 'All',\n          inverse: 'Inv'\n        }\n      },\n      toolbox: {\n        brush: {\n          title: {\n            rect: 'Box Select',\n            polygon: 'Lasso Select',\n            lineX: 'Horizontally Select',\n            lineY: 'Vertically Select',\n            keep: 'Keep Selections',\n            clear: 'Clear Selections'\n          }\n        },\n        dataView: {\n          title: 'Data View',\n          lang: ['Data View', 'Close', 'Refresh']\n        },\n        dataZoom: {\n          title: {\n            zoom: 'Zoom',\n            back: 'Zoom Reset'\n          }\n        },\n        magicType: {\n          title: {\n            line: 'Switch to Line Chart',\n            bar: 'Switch to Bar Chart',\n            stack: 'Stack',\n            tiled: 'Tile'\n          }\n        },\n        restore: {\n          title: 'Restore'\n        },\n        saveAsImage: {\n          title: 'Save as Image',\n          lang: ['Right Click to Save Image']\n        }\n      },\n      series: {\n        typeNames: {\n          pie: 'Pie chart',\n          bar: 'Bar chart',\n          line: 'Line chart',\n          scatter: 'Scatter plot',\n          effectScatter: 'Ripple scatter plot',\n          radar: 'Radar chart',\n          tree: 'Tree',\n          treemap: 'Treemap',\n          boxplot: 'Boxplot',\n          candlestick: 'Candlestick',\n          k: 'K line chart',\n          heatmap: 'Heat map',\n          map: 'Map',\n          parallel: 'Parallel coordinate map',\n          lines: 'Line graph',\n          graph: 'Relationship graph',\n          sankey: 'Sankey diagram',\n          funnel: 'Funnel chart',\n          gauge: 'Gauge',\n          pictorialBar: 'Pictorial bar',\n          themeRiver: 'Theme River Map',\n          sunburst: 'Sunburst'\n        }\n      },\n      aria: {\n        general: {\n          withTitle: 'This is a chart about \"{title}\"',\n          withoutTitle: 'This is a chart'\n        },\n        series: {\n          single: {\n            prefix: '',\n            withName: ' with type {seriesType} named {seriesName}.',\n            withoutName: ' with type {seriesType}.'\n          },\n          multiple: {\n            prefix: '. It consists of {seriesCount} series count.',\n            withName: ' The {seriesId} series is a {seriesType} representing {seriesName}.',\n            withoutName: ' The {seriesId} series is a {seriesType}.',\n            separator: {\n              middle: '',\n              end: ''\n            }\n          }\n        },\n        data: {\n          allData: 'The data is as follows: ',\n          partialData: 'The first {displayCnt} items are: ',\n          withName: 'the data for {name} is {value}',\n          withoutName: '{value}',\n          separator: {\n            middle: ', ',\n            end: '. '\n          }\n        }\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n     * Licensed to the Apache Software Foundation (ASF) under one\n     * or more contributor license agreements.  See the NOTICE file\n     * distributed with this work for additional information\n     * regarding copyright ownership.  The ASF licenses this file\n     * to you under the Apache License, Version 2.0 (the\n     * \"License\"); you may not use this file except in compliance\n     * with the License.  You may obtain a copy of the License at\n     *\n     *   http://www.apache.org/licenses/LICENSE-2.0\n     *\n     * Unless required by applicable law or agreed to in writing,\n     * software distributed under the License is distributed on an\n     * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n     * KIND, either express or implied.  See the License for the\n     * specific language governing permissions and limitations\n     * under the License.\n     */\n    var langZH = {\n      time: {\n        month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],\n        monthAbbr: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],\n        dayOfWeek: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],\n        dayOfWeekAbbr: ['日', '一', '二', '三', '四', '五', '六']\n      },\n      legend: {\n        selector: {\n          all: '全选',\n          inverse: '反选'\n        }\n      },\n      toolbox: {\n        brush: {\n          title: {\n            rect: '矩形选择',\n            polygon: '圈选',\n            lineX: '横向选择',\n            lineY: '纵向选择',\n            keep: '保持选择',\n            clear: '清除选择'\n          }\n        },\n        dataView: {\n          title: '数据视图',\n          lang: ['数据视图', '关闭', '刷新']\n        },\n        dataZoom: {\n          title: {\n            zoom: '区域缩放',\n            back: '区域缩放还原'\n          }\n        },\n        magicType: {\n          title: {\n            line: '切换为折线图',\n            bar: '切换为柱状图',\n            stack: '切换为堆叠',\n            tiled: '切换为平铺'\n          }\n        },\n        restore: {\n          title: '还原'\n        },\n        saveAsImage: {\n          title: '保存为图片',\n          lang: ['右键另存为图片']\n        }\n      },\n      series: {\n        typeNames: {\n          pie: '饼图',\n          bar: '柱状图',\n          line: '折线图',\n          scatter: '散点图',\n          effectScatter: '涟漪散点图',\n          radar: '雷达图',\n          tree: '树图',\n          treemap: '矩形树图',\n          boxplot: '箱型图',\n          candlestick: 'K线图',\n          k: 'K线图',\n          heatmap: '热力图',\n          map: '地图',\n          parallel: '平行坐标图',\n          lines: '线图',\n          graph: '关系图',\n          sankey: '桑基图',\n          funnel: '漏斗图',\n          gauge: '仪表盘图',\n          pictorialBar: '象形柱图',\n          themeRiver: '主题河流图',\n          sunburst: '旭日图'\n        }\n      },\n      aria: {\n        general: {\n          withTitle: '这是一个关于“{title}”的图表。',\n          withoutTitle: '这是一个图表，'\n        },\n        series: {\n          single: {\n            prefix: '',\n            withName: '图表类型是{seriesType}，表示{seriesName}。',\n            withoutName: '图表类型是{seriesType}。'\n          },\n          multiple: {\n            prefix: '它由{seriesCount}个图表系列组成。',\n            withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType}，',\n            withoutName: '第{seriesId}个系列是一个{seriesType}，',\n            separator: {\n              middle: '；',\n              end: '。'\n            }\n          }\n        },\n        data: {\n          allData: '其数据是——',\n          partialData: '其中，前{displayCnt}项是——',\n          withName: '{name}的数据是{value}',\n          withoutName: '{value}',\n          separator: {\n            middle: '，',\n            end: ''\n          }\n        }\n      }\n    };\n\n    var LOCALE_ZH = 'ZH';\n    var LOCALE_EN = 'EN';\n    var DEFAULT_LOCALE = LOCALE_EN;\n    var localeStorage = {};\n    var localeModels = {};\n    var SYSTEM_LANG = !env.domSupported ? DEFAULT_LOCALE : function () {\n      var langStr = (\n      /* eslint-disable-next-line */\n      document.documentElement.lang || navigator.language || navigator.browserLanguage).toUpperCase();\n      return langStr.indexOf(LOCALE_ZH) > -1 ? LOCALE_ZH : DEFAULT_LOCALE;\n    }();\n    function registerLocale(locale, localeObj) {\n      locale = locale.toUpperCase();\n      localeModels[locale] = new Model(localeObj);\n      localeStorage[locale] = localeObj;\n    } // export function getLocale(locale: string) {\n    //     return localeStorage[locale];\n    // }\n\n    function createLocaleObject(locale) {\n      if (isString(locale)) {\n        var localeObj = localeStorage[locale.toUpperCase()] || {};\n\n        if (locale === LOCALE_ZH || locale === LOCALE_EN) {\n          return clone(localeObj);\n        } else {\n          return merge(clone(localeObj), clone(localeStorage[DEFAULT_LOCALE]), false);\n        }\n      } else {\n        return merge(clone(locale), clone(localeStorage[DEFAULT_LOCALE]), false);\n      }\n    }\n    function getLocaleModel(lang) {\n      return localeModels[lang];\n    }\n    function getDefaultLocaleModel() {\n      return localeModels[DEFAULT_LOCALE];\n    } // Default locale\n\n    registerLocale(LOCALE_EN, langEN);\n    registerLocale(LOCALE_ZH, langZH);\n\n    var ONE_SECOND = 1000;\n    var ONE_MINUTE = ONE_SECOND * 60;\n    var ONE_HOUR = ONE_MINUTE * 60;\n    var ONE_DAY = ONE_HOUR * 24;\n    var ONE_YEAR = ONE_DAY * 365;\n    var defaultLeveledFormatter = {\n      year: '{yyyy}',\n      month: '{MMM}',\n      day: '{d}',\n      hour: '{HH}:{mm}',\n      minute: '{HH}:{mm}',\n      second: '{HH}:{mm}:{ss}',\n      millisecond: '{HH}:{mm}:{ss} {SSS}',\n      none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}'\n    };\n    var fullDayFormatter = '{yyyy}-{MM}-{dd}';\n    var fullLeveledFormatter = {\n      year: '{yyyy}',\n      month: '{yyyy}-{MM}',\n      day: fullDayFormatter,\n      hour: fullDayFormatter + ' ' + defaultLeveledFormatter.hour,\n      minute: fullDayFormatter + ' ' + defaultLeveledFormatter.minute,\n      second: fullDayFormatter + ' ' + defaultLeveledFormatter.second,\n      millisecond: defaultLeveledFormatter.none\n    };\n    var primaryTimeUnits = ['year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond'];\n    var timeUnits = ['year', 'half-year', 'quarter', 'month', 'week', 'half-week', 'day', 'half-day', 'quarter-day', 'hour', 'minute', 'second', 'millisecond'];\n    function pad(str, len) {\n      str += '';\n      return '0000'.substr(0, len - str.length) + str;\n    }\n    function getPrimaryTimeUnit(timeUnit) {\n      switch (timeUnit) {\n        case 'half-year':\n        case 'quarter':\n          return 'month';\n\n        case 'week':\n        case 'half-week':\n          return 'day';\n\n        case 'half-day':\n        case 'quarter-day':\n          return 'hour';\n\n        default:\n          // year, minutes, second, milliseconds\n          return timeUnit;\n      }\n    }\n    function isPrimaryTimeUnit(timeUnit) {\n      return timeUnit === getPrimaryTimeUnit(timeUnit);\n    }\n    function getDefaultFormatPrecisionOfInterval(timeUnit) {\n      switch (timeUnit) {\n        case 'year':\n        case 'month':\n          return 'day';\n\n        case 'millisecond':\n          return 'millisecond';\n\n        default:\n          // Also for day, hour, minute, second\n          return 'second';\n      }\n    }\n    function format( // Note: The result based on `isUTC` are totally different, which can not be just simply\n    // substituted by the result without `isUTC`. So we make the param `isUTC` mandatory.\n    time, template, isUTC, lang) {\n      var date = parseDate(time);\n      var y = date[fullYearGetterName(isUTC)]();\n      var M = date[monthGetterName(isUTC)]() + 1;\n      var q = Math.floor((M - 1) / 3) + 1;\n      var d = date[dateGetterName(isUTC)]();\n      var e = date['get' + (isUTC ? 'UTC' : '') + 'Day']();\n      var H = date[hoursGetterName(isUTC)]();\n      var h = (H - 1) % 12 + 1;\n      var m = date[minutesGetterName(isUTC)]();\n      var s = date[secondsGetterName(isUTC)]();\n      var S = date[millisecondsGetterName(isUTC)]();\n      var localeModel = lang instanceof Model ? lang : getLocaleModel(lang || SYSTEM_LANG) || getDefaultLocaleModel();\n      var timeModel = localeModel.getModel('time');\n      var month = timeModel.get('month');\n      var monthAbbr = timeModel.get('monthAbbr');\n      var dayOfWeek = timeModel.get('dayOfWeek');\n      var dayOfWeekAbbr = timeModel.get('dayOfWeekAbbr');\n      return (template || '').replace(/{yyyy}/g, y + '').replace(/{yy}/g, y % 100 + '').replace(/{Q}/g, q + '').replace(/{MMMM}/g, month[M - 1]).replace(/{MMM}/g, monthAbbr[M - 1]).replace(/{MM}/g, pad(M, 2)).replace(/{M}/g, M + '').replace(/{dd}/g, pad(d, 2)).replace(/{d}/g, d + '').replace(/{eeee}/g, dayOfWeek[e]).replace(/{ee}/g, dayOfWeekAbbr[e]).replace(/{e}/g, e + '').replace(/{HH}/g, pad(H, 2)).replace(/{H}/g, H + '').replace(/{hh}/g, pad(h + '', 2)).replace(/{h}/g, h + '').replace(/{mm}/g, pad(m, 2)).replace(/{m}/g, m + '').replace(/{ss}/g, pad(s, 2)).replace(/{s}/g, s + '').replace(/{SSS}/g, pad(S, 3)).replace(/{S}/g, S + '');\n    }\n    function leveledFormat(tick, idx, formatter, lang, isUTC) {\n      var template = null;\n\n      if (isString(formatter)) {\n        // Single formatter for all units at all levels\n        template = formatter;\n      } else if (isFunction(formatter)) {\n        // Callback formatter\n        template = formatter(tick.value, idx, {\n          level: tick.level\n        });\n      } else {\n        var defaults$1 = extend({}, defaultLeveledFormatter);\n\n        if (tick.level > 0) {\n          for (var i = 0; i < primaryTimeUnits.length; ++i) {\n            defaults$1[primaryTimeUnits[i]] = \"{primary|\" + defaults$1[primaryTimeUnits[i]] + \"}\";\n          }\n        }\n\n        var mergedFormatter = formatter ? formatter.inherit === false ? formatter // Use formatter with bigger units\n        : defaults(formatter, defaults$1) : defaults$1;\n        var unit = getUnitFromValue(tick.value, isUTC);\n\n        if (mergedFormatter[unit]) {\n          template = mergedFormatter[unit];\n        } else if (mergedFormatter.inherit) {\n          // Unit formatter is not defined and should inherit from bigger units\n          var targetId = timeUnits.indexOf(unit);\n\n          for (var i = targetId - 1; i >= 0; --i) {\n            if (mergedFormatter[unit]) {\n              template = mergedFormatter[unit];\n              break;\n            }\n          }\n\n          template = template || defaults$1.none;\n        }\n\n        if (isArray(template)) {\n          var levelId = tick.level == null ? 0 : tick.level >= 0 ? tick.level : template.length + tick.level;\n          levelId = Math.min(levelId, template.length - 1);\n          template = template[levelId];\n        }\n      }\n\n      return format(new Date(tick.value), template, isUTC, lang);\n    }\n    function getUnitFromValue(value, isUTC) {\n      var date = parseDate(value);\n      var M = date[monthGetterName(isUTC)]() + 1;\n      var d = date[dateGetterName(isUTC)]();\n      var h = date[hoursGetterName(isUTC)]();\n      var m = date[minutesGetterName(isUTC)]();\n      var s = date[secondsGetterName(isUTC)]();\n      var S = date[millisecondsGetterName(isUTC)]();\n      var isSecond = S === 0;\n      var isMinute = isSecond && s === 0;\n      var isHour = isMinute && m === 0;\n      var isDay = isHour && h === 0;\n      var isMonth = isDay && d === 1;\n      var isYear = isMonth && M === 1;\n\n      if (isYear) {\n        return 'year';\n      } else if (isMonth) {\n        return 'month';\n      } else if (isDay) {\n        return 'day';\n      } else if (isHour) {\n        return 'hour';\n      } else if (isMinute) {\n        return 'minute';\n      } else if (isSecond) {\n        return 'second';\n      } else {\n        return 'millisecond';\n      }\n    }\n    function getUnitValue(value, unit, isUTC) {\n      var date = isNumber(value) ? parseDate(value) : value;\n      unit = unit || getUnitFromValue(value, isUTC);\n\n      switch (unit) {\n        case 'year':\n          return date[fullYearGetterName(isUTC)]();\n\n        case 'half-year':\n          return date[monthGetterName(isUTC)]() >= 6 ? 1 : 0;\n\n        case 'quarter':\n          return Math.floor((date[monthGetterName(isUTC)]() + 1) / 4);\n\n        case 'month':\n          return date[monthGetterName(isUTC)]();\n\n        case 'day':\n          return date[dateGetterName(isUTC)]();\n\n        case 'half-day':\n          return date[hoursGetterName(isUTC)]() / 24;\n\n        case 'hour':\n          return date[hoursGetterName(isUTC)]();\n\n        case 'minute':\n          return date[minutesGetterName(isUTC)]();\n\n        case 'second':\n          return date[secondsGetterName(isUTC)]();\n\n        case 'millisecond':\n          return date[millisecondsGetterName(isUTC)]();\n      }\n    }\n    function fullYearGetterName(isUTC) {\n      return isUTC ? 'getUTCFullYear' : 'getFullYear';\n    }\n    function monthGetterName(isUTC) {\n      return isUTC ? 'getUTCMonth' : 'getMonth';\n    }\n    function dateGetterName(isUTC) {\n      return isUTC ? 'getUTCDate' : 'getDate';\n    }\n    function hoursGetterName(isUTC) {\n      return isUTC ? 'getUTCHours' : 'getHours';\n    }\n    function minutesGetterName(isUTC) {\n      return isUTC ? 'getUTCMinutes' : 'getMinutes';\n    }\n    function secondsGetterName(isUTC) {\n      return isUTC ? 'getUTCSeconds' : 'getSeconds';\n    }\n    function millisecondsGetterName(isUTC) {\n      return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds';\n    }\n    function fullYearSetterName(isUTC) {\n      return isUTC ? 'setUTCFullYear' : 'setFullYear';\n    }\n    function monthSetterName(isUTC) {\n      return isUTC ? 'setUTCMonth' : 'setMonth';\n    }\n    function dateSetterName(isUTC) {\n      return isUTC ? 'setUTCDate' : 'setDate';\n    }\n    function hoursSetterName(isUTC) {\n      return isUTC ? 'setUTCHours' : 'setHours';\n    }\n    function minutesSetterName(isUTC) {\n      return isUTC ? 'setUTCMinutes' : 'setMinutes';\n    }\n    function secondsSetterName(isUTC) {\n      return isUTC ? 'setUTCSeconds' : 'setSeconds';\n    }\n    function millisecondsSetterName(isUTC) {\n      return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds';\n    }\n\n    function getTextRect(text, font, align, verticalAlign, padding, rich, truncate, lineHeight) {\n      var textEl = new ZRText({\n        style: {\n          text: text,\n          font: font,\n          align: align,\n          verticalAlign: verticalAlign,\n          padding: padding,\n          rich: rich,\n          overflow: truncate ? 'truncate' : null,\n          lineHeight: lineHeight\n        }\n      });\n      return textEl.getBoundingRect();\n    }\n\n    /**\n     * Add a comma each three digit.\n     */\n\n    function addCommas(x) {\n      if (!isNumeric(x)) {\n        return isString(x) ? x : '-';\n      }\n\n      var parts = (x + '').split('.');\n      return parts[0].replace(/(\\d{1,3})(?=(?:\\d{3})+(?!\\d))/g, '$1,') + (parts.length > 1 ? '.' + parts[1] : '');\n    }\n    function toCamelCase(str, upperCaseFirst) {\n      str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {\n        return group1.toUpperCase();\n      });\n\n      if (upperCaseFirst && str) {\n        str = str.charAt(0).toUpperCase() + str.slice(1);\n      }\n\n      return str;\n    }\n    var normalizeCssArray$1 = normalizeCssArray;\n    var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];\n\n    var wrapVar = function (varName, seriesIdx) {\n      return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';\n    };\n    /**\n     * Template formatter\n     * @param {Array.<Object>|Object} paramsList\n     */\n\n\n    function formatTpl(tpl, paramsList, encode) {\n      if (!isArray(paramsList)) {\n        paramsList = [paramsList];\n      }\n\n      var seriesLen = paramsList.length;\n\n      if (!seriesLen) {\n        return '';\n      }\n\n      var $vars = paramsList[0].$vars || [];\n\n      for (var i = 0; i < $vars.length; i++) {\n        var alias = TPL_VAR_ALIAS[i];\n        tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0));\n      }\n\n      for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {\n        for (var k = 0; k < $vars.length; k++) {\n          var val = paramsList[seriesIdx][$vars[k]];\n          tpl = tpl.replace(wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val);\n        }\n      }\n\n      return tpl;\n    }\n    function getTooltipMarker(inOpt, extraCssText) {\n      var opt = isString(inOpt) ? {\n        color: inOpt,\n        extraCssText: extraCssText\n      } : inOpt || {};\n      var color = opt.color;\n      var type = opt.type;\n      extraCssText = opt.extraCssText;\n      var renderMode = opt.renderMode || 'html';\n\n      if (!color) {\n        return '';\n      }\n\n      if (renderMode === 'html') {\n        return type === 'subItem' ? '<span style=\"display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' + 'border-radius:4px;width:4px;height:4px;background-color:' // Only support string\n        + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>' : '<span style=\"display:inline-block;margin-right:4px;' + 'border-radius:10px;width:10px;height:10px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>';\n      } else {\n        // Should better not to auto generate style name by auto-increment number here.\n        // Because this util is usually called in tooltip formatter, which is probably\n        // called repeatedly when mouse move and the auto-increment number increases fast.\n        // Users can make their own style name by theirselves, make it unique and readable.\n        var markerId = opt.markerId || 'markerX';\n        return {\n          renderMode: renderMode,\n          content: '{' + markerId + '|}  ',\n          style: type === 'subItem' ? {\n            width: 4,\n            height: 4,\n            borderRadius: 2,\n            backgroundColor: color\n          } : {\n            width: 10,\n            height: 10,\n            borderRadius: 5,\n            backgroundColor: color\n          }\n        };\n      }\n    }\n    /**\n     * @deprecated Use `time/format` instead.\n     * ISO Date format\n     * @param {string} tpl\n     * @param {number} value\n     * @param {boolean} [isUTC=false] Default in local time.\n     *           see `module:echarts/scale/Time`\n     *           and `module:echarts/util/number#parseDate`.\n     * @inner\n     */\n\n    function formatTime(tpl, value, isUTC) {\n      if (\"development\" !== 'production') {\n        deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format');\n      }\n\n      if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year') {\n        tpl = 'MM-dd\\nyyyy';\n      }\n\n      var date = parseDate(value);\n      var getUTC = isUTC ? 'getUTC' : 'get';\n      var y = date[getUTC + 'FullYear']();\n      var M = date[getUTC + 'Month']() + 1;\n      var d = date[getUTC + 'Date']();\n      var h = date[getUTC + 'Hours']();\n      var m = date[getUTC + 'Minutes']();\n      var s = date[getUTC + 'Seconds']();\n      var S = date[getUTC + 'Milliseconds']();\n      tpl = tpl.replace('MM', pad(M, 2)).replace('M', M).replace('yyyy', y).replace('yy', pad(y % 100 + '', 2)).replace('dd', pad(d, 2)).replace('d', d).replace('hh', pad(h, 2)).replace('h', h).replace('mm', pad(m, 2)).replace('m', m).replace('ss', pad(s, 2)).replace('s', s).replace('SSS', pad(S, 3));\n      return tpl;\n    }\n    /**\n     * Capital first\n     * @param {string} str\n     * @return {string}\n     */\n\n    function capitalFirst(str) {\n      return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;\n    }\n    /**\n     * @return Never be null/undefined.\n     */\n\n    function convertToColorString(color, defaultColor) {\n      defaultColor = defaultColor || 'transparent';\n      return isString(color) ? color : isObject(color) ? color.colorStops && (color.colorStops[0] || {}).color || defaultColor : defaultColor;\n    }\n\n    var each$1 = each;\n    /**\n     * @public\n     */\n\n    var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];\n    /**\n     * @public\n     */\n\n    var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];\n\n    function boxLayout(orient, group, gap, maxWidth, maxHeight) {\n      var x = 0;\n      var y = 0;\n\n      if (maxWidth == null) {\n        maxWidth = Infinity;\n      }\n\n      if (maxHeight == null) {\n        maxHeight = Infinity;\n      }\n\n      var currentLineMaxSize = 0;\n      group.eachChild(function (child, idx) {\n        var rect = child.getBoundingRect();\n        var nextChild = group.childAt(idx + 1);\n        var nextChildRect = nextChild && nextChild.getBoundingRect();\n        var nextX;\n        var nextY;\n\n        if (orient === 'horizontal') {\n          var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);\n          nextX = x + moveX; // Wrap when width exceeds maxWidth or meet a `newline` group\n          // FIXME compare before adding gap?\n\n          if (nextX > maxWidth || child.newline) {\n            x = 0;\n            nextX = moveX;\n            y += currentLineMaxSize + gap;\n            currentLineMaxSize = rect.height;\n          } else {\n            // FIXME: consider rect.y is not `0`?\n            currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);\n          }\n        } else {\n          var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);\n          nextY = y + moveY; // Wrap when width exceeds maxHeight or meet a `newline` group\n\n          if (nextY > maxHeight || child.newline) {\n            x += currentLineMaxSize + gap;\n            y = 0;\n            nextY = moveY;\n            currentLineMaxSize = rect.width;\n          } else {\n            currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);\n          }\n        }\n\n        if (child.newline) {\n          return;\n        }\n\n        child.x = x;\n        child.y = y;\n        child.markRedraw();\n        orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;\n      });\n    }\n    /**\n     * VBox layouting\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n    var vbox = curry(boxLayout, 'vertical');\n    /**\n     * HBox layouting\n     * @param {module:zrender/graphic/Group} group\n     * @param {number} gap\n     * @param {number} [width=Infinity]\n     * @param {number} [height=Infinity]\n     */\n\n    var hbox = curry(boxLayout, 'horizontal');\n    /**\n     * Parse position info.\n     */\n\n    function getLayoutRect(positionInfo, containerRect, margin) {\n      margin = normalizeCssArray$1(margin || 0);\n      var containerWidth = containerRect.width;\n      var containerHeight = containerRect.height;\n      var left = parsePercent$1(positionInfo.left, containerWidth);\n      var top = parsePercent$1(positionInfo.top, containerHeight);\n      var right = parsePercent$1(positionInfo.right, containerWidth);\n      var bottom = parsePercent$1(positionInfo.bottom, containerHeight);\n      var width = parsePercent$1(positionInfo.width, containerWidth);\n      var height = parsePercent$1(positionInfo.height, containerHeight);\n      var verticalMargin = margin[2] + margin[0];\n      var horizontalMargin = margin[1] + margin[3];\n      var aspect = positionInfo.aspect; // If width is not specified, calculate width from left and right\n\n      if (isNaN(width)) {\n        width = containerWidth - right - horizontalMargin - left;\n      }\n\n      if (isNaN(height)) {\n        height = containerHeight - bottom - verticalMargin - top;\n      }\n\n      if (aspect != null) {\n        // If width and height are not given\n        // 1. Graph should not exceeds the container\n        // 2. Aspect must be keeped\n        // 3. Graph should take the space as more as possible\n        // FIXME\n        // Margin is not considered, because there is no case that both\n        // using margin and aspect so far.\n        if (isNaN(width) && isNaN(height)) {\n          if (aspect > containerWidth / containerHeight) {\n            width = containerWidth * 0.8;\n          } else {\n            height = containerHeight * 0.8;\n          }\n        } // Calculate width or height with given aspect\n\n\n        if (isNaN(width)) {\n          width = aspect * height;\n        }\n\n        if (isNaN(height)) {\n          height = width / aspect;\n        }\n      } // If left is not specified, calculate left from right and width\n\n\n      if (isNaN(left)) {\n        left = containerWidth - right - width - horizontalMargin;\n      }\n\n      if (isNaN(top)) {\n        top = containerHeight - bottom - height - verticalMargin;\n      } // Align left and top\n\n\n      switch (positionInfo.left || positionInfo.right) {\n        case 'center':\n          left = containerWidth / 2 - width / 2 - margin[3];\n          break;\n\n        case 'right':\n          left = containerWidth - width - horizontalMargin;\n          break;\n      }\n\n      switch (positionInfo.top || positionInfo.bottom) {\n        case 'middle':\n        case 'center':\n          top = containerHeight / 2 - height / 2 - margin[0];\n          break;\n\n        case 'bottom':\n          top = containerHeight - height - verticalMargin;\n          break;\n      } // If something is wrong and left, top, width, height are calculated as NaN\n\n\n      left = left || 0;\n      top = top || 0;\n\n      if (isNaN(width)) {\n        // Width may be NaN if only one value is given except width\n        width = containerWidth - horizontalMargin - left - (right || 0);\n      }\n\n      if (isNaN(height)) {\n        // Height may be NaN if only one value is given except height\n        height = containerHeight - verticalMargin - top - (bottom || 0);\n      }\n\n      var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);\n      rect.margin = margin;\n      return rect;\n    }\n    function fetchLayoutMode(ins) {\n      var layoutMode = ins.layoutMode || ins.constructor.layoutMode;\n      return isObject(layoutMode) ? layoutMode : layoutMode ? {\n        type: layoutMode\n      } : null;\n    }\n    /**\n     * Consider Case:\n     * When default option has {left: 0, width: 100}, and we set {right: 0}\n     * through setOption or media query, using normal zrUtil.merge will cause\n     * {right: 0} does not take effect.\n     *\n     * @example\n     * ComponentModel.extend({\n     *     init: function () {\n     *         ...\n     *         let inputPositionParams = layout.getLayoutParams(option);\n     *         this.mergeOption(inputPositionParams);\n     *     },\n     *     mergeOption: function (newOption) {\n     *         newOption && zrUtil.merge(thisOption, newOption, true);\n     *         layout.mergeLayoutParam(thisOption, newOption);\n     *     }\n     * });\n     *\n     * @param targetOption\n     * @param newOption\n     * @param opt\n     */\n\n    function mergeLayoutParam(targetOption, newOption, opt) {\n      var ignoreSize = opt && opt.ignoreSize;\n      !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);\n      var hResult = merge(HV_NAMES[0], 0);\n      var vResult = merge(HV_NAMES[1], 1);\n      copy(HV_NAMES[0], targetOption, hResult);\n      copy(HV_NAMES[1], targetOption, vResult);\n\n      function merge(names, hvIdx) {\n        var newParams = {};\n        var newValueCount = 0;\n        var merged = {};\n        var mergedValueCount = 0;\n        var enoughParamNumber = 2;\n        each$1(names, function (name) {\n          merged[name] = targetOption[name];\n        });\n        each$1(names, function (name) {\n          // Consider case: newOption.width is null, which is\n          // set by user for removing width setting.\n          hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);\n          hasValue(newParams, name) && newValueCount++;\n          hasValue(merged, name) && mergedValueCount++;\n        });\n\n        if (ignoreSize[hvIdx]) {\n          // Only one of left/right is premitted to exist.\n          if (hasValue(newOption, names[1])) {\n            merged[names[2]] = null;\n          } else if (hasValue(newOption, names[2])) {\n            merged[names[1]] = null;\n          }\n\n          return merged;\n        } // Case: newOption: {width: ..., right: ...},\n        // or targetOption: {right: ...} and newOption: {width: ...},\n        // There is no conflict when merged only has params count\n        // little than enoughParamNumber.\n\n\n        if (mergedValueCount === enoughParamNumber || !newValueCount) {\n          return merged;\n        } // Case: newOption: {width: ..., right: ...},\n        // Than we can make sure user only want those two, and ignore\n        // all origin params in targetOption.\n        else if (newValueCount >= enoughParamNumber) {\n            return newParams;\n          } else {\n            // Chose another param from targetOption by priority.\n            for (var i = 0; i < names.length; i++) {\n              var name_1 = names[i];\n\n              if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) {\n                newParams[name_1] = targetOption[name_1];\n                break;\n              }\n            }\n\n            return newParams;\n          }\n      }\n\n      function hasProp(obj, name) {\n        return obj.hasOwnProperty(name);\n      }\n\n      function hasValue(obj, name) {\n        return obj[name] != null && obj[name] !== 'auto';\n      }\n\n      function copy(names, target, source) {\n        each$1(names, function (name) {\n          target[name] = source[name];\n        });\n      }\n    }\n    /**\n     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n     */\n\n    function getLayoutParams(source) {\n      return copyLayoutParams({}, source);\n    }\n    /**\n     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n     * @param {Object} source\n     * @return {Object} Result contains those props.\n     */\n\n    function copyLayoutParams(target, source) {\n      source && target && each$1(LOCATION_PARAMS, function (name) {\n        source.hasOwnProperty(name) && (target[name] = source[name]);\n      });\n      return target;\n    }\n\n    var inner = makeInner();\n\n    var ComponentModel =\n    /** @class */\n    function (_super) {\n      __extends(ComponentModel, _super);\n\n      function ComponentModel(option, parentModel, ecModel) {\n        var _this = _super.call(this, option, parentModel, ecModel) || this;\n\n        _this.uid = getUID('ec_cpt_model');\n        return _this;\n      }\n\n      ComponentModel.prototype.init = function (option, parentModel, ecModel) {\n        this.mergeDefaultAndTheme(option, ecModel);\n      };\n\n      ComponentModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n        var layoutMode = fetchLayoutMode(this);\n        var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n        var themeModel = ecModel.getTheme();\n        merge(option, themeModel.get(this.mainType));\n        merge(option, this.getDefaultOption());\n\n        if (layoutMode) {\n          mergeLayoutParam(option, inputPositionParams, layoutMode);\n        }\n      };\n\n      ComponentModel.prototype.mergeOption = function (option, ecModel) {\n        merge(this.option, option, true);\n        var layoutMode = fetchLayoutMode(this);\n\n        if (layoutMode) {\n          mergeLayoutParam(this.option, option, layoutMode);\n        }\n      };\n      /**\n       * Called immediately after `init` or `mergeOption` of this instance called.\n       */\n\n\n      ComponentModel.prototype.optionUpdated = function (newCptOption, isInit) {};\n      /**\n       * [How to declare defaultOption]:\n       *\n       * (A) If using class declaration in typescript (since echarts 5):\n       * ```ts\n       * import {ComponentOption} from '../model/option.js';\n       * export interface XxxOption extends ComponentOption {\n       *     aaa: number\n       * }\n       * export class XxxModel extends Component {\n       *     static type = 'xxx';\n       *     static defaultOption: XxxOption = {\n       *         aaa: 123\n       *     }\n       * }\n       * Component.registerClass(XxxModel);\n       * ```\n       * ```ts\n       * import {inheritDefaultOption} from '../util/component.js';\n       * import {XxxModel, XxxOption} from './XxxModel.js';\n       * export interface XxxSubOption extends XxxOption {\n       *     bbb: number\n       * }\n       * class XxxSubModel extends XxxModel {\n       *     static defaultOption: XxxSubOption = inheritDefaultOption(XxxModel.defaultOption, {\n       *         bbb: 456\n       *     })\n       *     fn() {\n       *         let opt = this.getDefaultOption();\n       *         // opt is {aaa: 123, bbb: 456}\n       *     }\n       * }\n       * ```\n       *\n       * (B) If using class extend (previous approach in echarts 3 & 4):\n       * ```js\n       * let XxxComponent = Component.extend({\n       *     defaultOption: {\n       *         xx: 123\n       *     }\n       * })\n       * ```\n       * ```js\n       * let XxxSubComponent = XxxComponent.extend({\n       *     defaultOption: {\n       *         yy: 456\n       *     },\n       *     fn: function () {\n       *         let opt = this.getDefaultOption();\n       *         // opt is {xx: 123, yy: 456}\n       *     }\n       * })\n       * ```\n       */\n\n\n      ComponentModel.prototype.getDefaultOption = function () {\n        var ctor = this.constructor; // If using class declaration, it is different to travel super class\n        // in legacy env and auto merge defaultOption. So if using class\n        // declaration, defaultOption should be merged manually.\n\n        if (!isExtendedClass(ctor)) {\n          // When using ts class, defaultOption must be declared as static.\n          return ctor.defaultOption;\n        } // FIXME: remove this approach?\n\n\n        var fields = inner(this);\n\n        if (!fields.defaultOption) {\n          var optList = [];\n          var clz = ctor;\n\n          while (clz) {\n            var opt = clz.prototype.defaultOption;\n            opt && optList.push(opt);\n            clz = clz.superClass;\n          }\n\n          var defaultOption = {};\n\n          for (var i = optList.length - 1; i >= 0; i--) {\n            defaultOption = merge(defaultOption, optList[i], true);\n          }\n\n          fields.defaultOption = defaultOption;\n        }\n\n        return fields.defaultOption;\n      };\n      /**\n       * Notice: always force to input param `useDefault` in case that forget to consider it.\n       * The same behavior as `modelUtil.parseFinder`.\n       *\n       * @param useDefault In many cases like series refer axis and axis refer grid,\n       *        If axis index / axis id not specified, use the first target as default.\n       *        In other cases like dataZoom refer axis, if not specified, measn no refer.\n       */\n\n\n      ComponentModel.prototype.getReferringComponents = function (mainType, opt) {\n        var indexKey = mainType + 'Index';\n        var idKey = mainType + 'Id';\n        return queryReferringComponents(this.ecModel, mainType, {\n          index: this.get(indexKey, true),\n          id: this.get(idKey, true)\n        }, opt);\n      };\n\n      ComponentModel.prototype.getBoxLayoutParams = function () {\n        // Consider itself having box layout configs.\n        var boxLayoutModel = this;\n        return {\n          left: boxLayoutModel.get('left'),\n          top: boxLayoutModel.get('top'),\n          right: boxLayoutModel.get('right'),\n          bottom: boxLayoutModel.get('bottom'),\n          width: boxLayoutModel.get('width'),\n          height: boxLayoutModel.get('height')\n        };\n      };\n      /**\n       * Get key for zlevel.\n       * If developers don't configure zlevel. We will assign zlevel to series based on the key.\n       * For example, lines with trail effect and progressive series will in an individual zlevel.\n       */\n\n\n      ComponentModel.prototype.getZLevelKey = function () {\n        return '';\n      };\n\n      ComponentModel.prototype.setZLevel = function (zlevel) {\n        this.option.zlevel = zlevel;\n      };\n\n      ComponentModel.protoInitialize = function () {\n        var proto = ComponentModel.prototype;\n        proto.type = 'component';\n        proto.id = '';\n        proto.name = '';\n        proto.mainType = '';\n        proto.subType = '';\n        proto.componentIndex = 0;\n      }();\n\n      return ComponentModel;\n    }(Model);\n\n    mountExtend(ComponentModel, Model);\n    enableClassManagement(ComponentModel);\n    enableSubTypeDefaulter(ComponentModel);\n    enableTopologicalTravel(ComponentModel, getDependencies);\n\n    function getDependencies(componentType) {\n      var deps = [];\n      each(ComponentModel.getClassesByMainType(componentType), function (clz) {\n        deps = deps.concat(clz.dependencies || clz.prototype.dependencies || []);\n      }); // Ensure main type.\n\n      deps = map(deps, function (type) {\n        return parseClassType(type).main;\n      }); // Hack dataset for convenience.\n\n      if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {\n        deps.unshift('dataset');\n      }\n\n      return deps;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var platform = ''; // Navigator not exists in node\n\n    if (typeof navigator !== 'undefined') {\n      /* global navigator */\n      platform = navigator.platform || '';\n    }\n\n    var decalColor = 'rgba(0, 0, 0, 0.2)';\n    var globalDefault = {\n      darkMode: 'auto',\n      // backgroundColor: 'rgba(0,0,0,0)',\n      colorBy: 'series',\n      color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],\n      gradientColor: ['#f6efa6', '#d88273', '#bf444c'],\n      aria: {\n        decal: {\n          decals: [{\n            color: decalColor,\n            dashArrayX: [1, 0],\n            dashArrayY: [2, 5],\n            symbolSize: 1,\n            rotation: Math.PI / 6\n          }, {\n            color: decalColor,\n            symbol: 'circle',\n            dashArrayX: [[8, 8], [0, 8, 8, 0]],\n            dashArrayY: [6, 0],\n            symbolSize: 0.8\n          }, {\n            color: decalColor,\n            dashArrayX: [1, 0],\n            dashArrayY: [4, 3],\n            rotation: -Math.PI / 4\n          }, {\n            color: decalColor,\n            dashArrayX: [[6, 6], [0, 6, 6, 0]],\n            dashArrayY: [6, 0]\n          }, {\n            color: decalColor,\n            dashArrayX: [[1, 0], [1, 6]],\n            dashArrayY: [1, 0, 6, 0],\n            rotation: Math.PI / 4\n          }, {\n            color: decalColor,\n            symbol: 'triangle',\n            dashArrayX: [[9, 9], [0, 9, 9, 0]],\n            dashArrayY: [7, 2],\n            symbolSize: 0.75\n          }]\n        }\n      },\n      // If xAxis and yAxis declared, grid is created by default.\n      // grid: {},\n      textStyle: {\n        // color: '#000',\n        // decoration: 'none',\n        // PENDING\n        fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',\n        // fontFamily: 'Arial, Verdana, sans-serif',\n        fontSize: 12,\n        fontStyle: 'normal',\n        fontWeight: 'normal'\n      },\n      // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/\n      // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n      // Default is source-over\n      blendMode: null,\n      stateAnimation: {\n        duration: 300,\n        easing: 'cubicOut'\n      },\n      animation: 'auto',\n      animationDuration: 1000,\n      animationDurationUpdate: 500,\n      animationEasing: 'cubicInOut',\n      animationEasingUpdate: 'cubicInOut',\n      animationThreshold: 2000,\n      // Configuration for progressive/incremental rendering\n      progressiveThreshold: 3000,\n      progressive: 400,\n      // Threshold of if use single hover layer to optimize.\n      // It is recommended that `hoverLayerThreshold` is equivalent to or less than\n      // `progressiveThreshold`, otherwise hover will cause restart of progressive,\n      // which is unexpected.\n      // see example <echarts/test/heatmap-large.html>.\n      hoverLayerThreshold: 3000,\n      // See: module:echarts/scale/Time\n      useUTC: false\n    };\n\n    var VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName']);\n    var SOURCE_FORMAT_ORIGINAL = 'original';\n    var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';\n    var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';\n    var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';\n    var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';\n    var SOURCE_FORMAT_UNKNOWN = 'unknown';\n    var SERIES_LAYOUT_BY_COLUMN = 'column';\n    var SERIES_LAYOUT_BY_ROW = 'row';\n\n    var BE_ORDINAL = {\n      Must: 1,\n      Might: 2,\n      Not: 3 // Other cases\n\n    };\n    var innerGlobalModel = makeInner();\n    /**\n     * MUST be called before mergeOption of all series.\n     */\n\n    function resetSourceDefaulter(ecModel) {\n      // `datasetMap` is used to make default encode.\n      innerGlobalModel(ecModel).datasetMap = createHashMap();\n    }\n    /**\n     * [The strategy of the arrengment of data dimensions for dataset]:\n     * \"value way\": all axes are non-category axes. So series one by one take\n     *     several (the number is coordSysDims.length) dimensions from dataset.\n     *     The result of data arrengment of data dimensions like:\n     *     | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y |\n     * \"category way\": at least one axis is category axis. So the the first data\n     *     dimension is always mapped to the first category axis and shared by\n     *     all of the series. The other data dimensions are taken by series like\n     *     \"value way\" does.\n     *     The result of data arrengment of data dimensions like:\n     *     | ser_shared_x | ser0_y | ser1_y | ser2_y |\n     *\n     * @return encode Never be `null/undefined`.\n     */\n\n    function makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) {\n      var encode = {};\n      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n      if (!datasetModel || !coordDimensions) {\n        return encode;\n      }\n\n      var encodeItemName = [];\n      var encodeSeriesName = [];\n      var ecModel = seriesModel.ecModel;\n      var datasetMap = innerGlobalModel(ecModel).datasetMap;\n      var key = datasetModel.uid + '_' + source.seriesLayoutBy;\n      var baseCategoryDimIndex;\n      var categoryWayValueDimStart;\n      coordDimensions = coordDimensions.slice();\n      each(coordDimensions, function (coordDimInfoLoose, coordDimIdx) {\n        var coordDimInfo = isObject(coordDimInfoLoose) ? coordDimInfoLoose : coordDimensions[coordDimIdx] = {\n          name: coordDimInfoLoose\n        };\n\n        if (coordDimInfo.type === 'ordinal' && baseCategoryDimIndex == null) {\n          baseCategoryDimIndex = coordDimIdx;\n          categoryWayValueDimStart = getDataDimCountOnCoordDim(coordDimInfo);\n        }\n\n        encode[coordDimInfo.name] = [];\n      });\n      var datasetRecord = datasetMap.get(key) || datasetMap.set(key, {\n        categoryWayDim: categoryWayValueDimStart,\n        valueWayDim: 0\n      }); // TODO\n      // Auto detect first time axis and do arrangement.\n\n      each(coordDimensions, function (coordDimInfo, coordDimIdx) {\n        var coordDimName = coordDimInfo.name;\n        var count = getDataDimCountOnCoordDim(coordDimInfo); // In value way.\n\n        if (baseCategoryDimIndex == null) {\n          var start = datasetRecord.valueWayDim;\n          pushDim(encode[coordDimName], start, count);\n          pushDim(encodeSeriesName, start, count);\n          datasetRecord.valueWayDim += count; // ??? TODO give a better default series name rule?\n          // especially when encode x y specified.\n          // consider: when multiple series share one dimension\n          // category axis, series name should better use\n          // the other dimension name. On the other hand, use\n          // both dimensions name.\n        } // In category way, the first category axis.\n        else if (baseCategoryDimIndex === coordDimIdx) {\n            pushDim(encode[coordDimName], 0, count);\n            pushDim(encodeItemName, 0, count);\n          } // In category way, the other axis.\n          else {\n              var start = datasetRecord.categoryWayDim;\n              pushDim(encode[coordDimName], start, count);\n              pushDim(encodeSeriesName, start, count);\n              datasetRecord.categoryWayDim += count;\n            }\n      });\n\n      function pushDim(dimIdxArr, idxFrom, idxCount) {\n        for (var i = 0; i < idxCount; i++) {\n          dimIdxArr.push(idxFrom + i);\n        }\n      }\n\n      function getDataDimCountOnCoordDim(coordDimInfo) {\n        var dimsDef = coordDimInfo.dimsDef;\n        return dimsDef ? dimsDef.length : 1;\n      }\n\n      encodeItemName.length && (encode.itemName = encodeItemName);\n      encodeSeriesName.length && (encode.seriesName = encodeSeriesName);\n      return encode;\n    }\n    /**\n     * Work for data like [{name: ..., value: ...}, ...].\n     *\n     * @return encode Never be `null/undefined`.\n     */\n\n    function makeSeriesEncodeForNameBased(seriesModel, source, dimCount) {\n      var encode = {};\n      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.\n\n      if (!datasetModel) {\n        return encode;\n      }\n\n      var sourceFormat = source.sourceFormat;\n      var dimensionsDefine = source.dimensionsDefine;\n      var potentialNameDimIndex;\n\n      if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        each(dimensionsDefine, function (dim, idx) {\n          if ((isObject(dim) ? dim.name : dim) === 'name') {\n            potentialNameDimIndex = idx;\n          }\n        });\n      }\n\n      var idxResult = function () {\n        var idxRes0 = {};\n        var idxRes1 = {};\n        var guessRecords = []; // 5 is an experience value.\n\n        for (var i = 0, len = Math.min(5, dimCount); i < len; i++) {\n          var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i);\n          guessRecords.push(guessResult);\n          var isPureNumber = guessResult === BE_ORDINAL.Not; // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim,\n          // and then find a name dim with the priority:\n          // \"BE_ORDINAL.Might|BE_ORDINAL.Must\" > \"other dim\" > \"the value dim itself\".\n\n          if (isPureNumber && idxRes0.v == null && i !== potentialNameDimIndex) {\n            idxRes0.v = i;\n          }\n\n          if (idxRes0.n == null || idxRes0.n === idxRes0.v || !isPureNumber && guessRecords[idxRes0.n] === BE_ORDINAL.Not) {\n            idxRes0.n = i;\n          }\n\n          if (fulfilled(idxRes0) && guessRecords[idxRes0.n] !== BE_ORDINAL.Not) {\n            return idxRes0;\n          } // [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not),\n          // find the first BE_ORDINAL.Might as the value dim,\n          // and then find a name dim with the priority:\n          // \"other dim\" > \"the value dim itself\".\n          // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be\n          // treated as number.\n\n\n          if (!isPureNumber) {\n            if (guessResult === BE_ORDINAL.Might && idxRes1.v == null && i !== potentialNameDimIndex) {\n              idxRes1.v = i;\n            }\n\n            if (idxRes1.n == null || idxRes1.n === idxRes1.v) {\n              idxRes1.n = i;\n            }\n          }\n        }\n\n        function fulfilled(idxResult) {\n          return idxResult.v != null && idxResult.n != null;\n        }\n\n        return fulfilled(idxRes0) ? idxRes0 : fulfilled(idxRes1) ? idxRes1 : null;\n      }();\n\n      if (idxResult) {\n        encode.value = [idxResult.v]; // `potentialNameDimIndex` has highest priority.\n\n        var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; // By default, label uses itemName in charts.\n        // So we don't set encodeLabel here.\n\n        encode.itemName = [nameDimIndex];\n        encode.seriesName = [nameDimIndex];\n      }\n\n      return encode;\n    }\n    /**\n     * @return If return null/undefined, indicate that should not use datasetModel.\n     */\n\n    function querySeriesUpstreamDatasetModel(seriesModel) {\n      // Caution: consider the scenario:\n      // A dataset is declared and a series is not expected to use the dataset,\n      // and at the beginning `setOption({series: { noData })` (just prepare other\n      // option but no data), then `setOption({series: {data: [...]}); In this case,\n      // the user should set an empty array to avoid that dataset is used by default.\n      var thisData = seriesModel.get('data', true);\n\n      if (!thisData) {\n        return queryReferringComponents(seriesModel.ecModel, 'dataset', {\n          index: seriesModel.get('datasetIndex', true),\n          id: seriesModel.get('datasetId', true)\n        }, SINGLE_REFERRING).models[0];\n      }\n    }\n    /**\n     * @return Always return an array event empty.\n     */\n\n    function queryDatasetUpstreamDatasetModels(datasetModel) {\n      // Only these attributes declared, we by defualt reference to `datasetIndex: 0`.\n      // Otherwise, no reference.\n      if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true)) {\n        return [];\n      }\n\n      return queryReferringComponents(datasetModel.ecModel, 'dataset', {\n        index: datasetModel.get('fromDatasetIndex', true),\n        id: datasetModel.get('fromDatasetId', true)\n      }, SINGLE_REFERRING).models;\n    }\n    /**\n     * The rule should not be complex, otherwise user might not\n     * be able to known where the data is wrong.\n     * The code is ugly, but how to make it neat?\n     */\n\n    function guessOrdinal(source, dimIndex) {\n      return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex);\n    } // dimIndex may be overflow source data.\n    // return {BE_ORDINAL}\n\n    function doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) {\n      var result; // Experience value.\n\n      var maxLoop = 5;\n\n      if (isTypedArray(data)) {\n        return BE_ORDINAL.Not;\n      } // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine\n      // always exists in source.\n\n\n      var dimName;\n      var dimType;\n\n      if (dimensionsDefine) {\n        var dimDefItem = dimensionsDefine[dimIndex];\n\n        if (isObject(dimDefItem)) {\n          dimName = dimDefItem.name;\n          dimType = dimDefItem.type;\n        } else if (isString(dimDefItem)) {\n          dimName = dimDefItem;\n        }\n      }\n\n      if (dimType != null) {\n        return dimType === 'ordinal' ? BE_ORDINAL.Must : BE_ORDINAL.Not;\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var dataArrayRows = data;\n\n        if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n          var sample = dataArrayRows[dimIndex];\n\n          for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {\n            if ((result = detectValue(sample[startIndex + i])) != null) {\n              return result;\n            }\n          }\n        } else {\n          for (var i = 0; i < dataArrayRows.length && i < maxLoop; i++) {\n            var row = dataArrayRows[startIndex + i];\n\n            if (row && (result = detectValue(row[dimIndex])) != null) {\n              return result;\n            }\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        var dataObjectRows = data;\n\n        if (!dimName) {\n          return BE_ORDINAL.Not;\n        }\n\n        for (var i = 0; i < dataObjectRows.length && i < maxLoop; i++) {\n          var item = dataObjectRows[i];\n\n          if (item && (result = detectValue(item[dimName])) != null) {\n            return result;\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        var dataKeyedColumns = data;\n\n        if (!dimName) {\n          return BE_ORDINAL.Not;\n        }\n\n        var sample = dataKeyedColumns[dimName];\n\n        if (!sample || isTypedArray(sample)) {\n          return BE_ORDINAL.Not;\n        }\n\n        for (var i = 0; i < sample.length && i < maxLoop; i++) {\n          if ((result = detectValue(sample[i])) != null) {\n            return result;\n          }\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var dataOriginal = data;\n\n        for (var i = 0; i < dataOriginal.length && i < maxLoop; i++) {\n          var item = dataOriginal[i];\n          var val = getDataItemValue(item);\n\n          if (!isArray(val)) {\n            return BE_ORDINAL.Not;\n          }\n\n          if ((result = detectValue(val[dimIndex])) != null) {\n            return result;\n          }\n        }\n      }\n\n      function detectValue(val) {\n        var beStr = isString(val); // Consider usage convenience, '1', '2' will be treated as \"number\".\n        // `isFinit('')` get `true`.\n\n        if (val != null && isFinite(val) && val !== '') {\n          return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not;\n        } else if (beStr && val !== '-') {\n          return BE_ORDINAL.Must;\n        }\n      }\n\n      return BE_ORDINAL.Not;\n    }\n\n    var internalOptionCreatorMap = createHashMap();\n    function concatInternalOptions(ecModel, mainType, newCmptOptionList) {\n      var internalOptionCreator = internalOptionCreatorMap.get(mainType);\n\n      if (!internalOptionCreator) {\n        return newCmptOptionList;\n      }\n\n      var internalOptions = internalOptionCreator(ecModel);\n\n      if (!internalOptions) {\n        return newCmptOptionList;\n      }\n\n      if (\"development\" !== 'production') {\n        for (var i = 0; i < internalOptions.length; i++) {\n          assert(isComponentIdInternal(internalOptions[i]));\n        }\n      }\n\n      return newCmptOptionList.concat(internalOptions);\n    }\n\n    var innerColor = makeInner();\n    var innerDecal = makeInner();\n\n    var PaletteMixin =\n    /** @class */\n    function () {\n      function PaletteMixin() {}\n\n      PaletteMixin.prototype.getColorFromPalette = function (name, scope, requestNum) {\n        var defaultPalette = normalizeToArray(this.get('color', true));\n        var layeredPalette = this.get('colorLayer', true);\n        return getFromPalette(this, innerColor, defaultPalette, layeredPalette, name, scope, requestNum);\n      };\n\n      PaletteMixin.prototype.clearColorPalette = function () {\n        clearPalette(this, innerColor);\n      };\n\n      return PaletteMixin;\n    }();\n\n    function getDecalFromPalette(ecModel, name, scope, requestNum) {\n      var defaultDecals = normalizeToArray(ecModel.get(['aria', 'decal', 'decals']));\n      return getFromPalette(ecModel, innerDecal, defaultDecals, null, name, scope, requestNum);\n    }\n\n    function getNearestPalette(palettes, requestColorNum) {\n      var paletteNum = palettes.length; // TODO palettes must be in order\n\n      for (var i = 0; i < paletteNum; i++) {\n        if (palettes[i].length > requestColorNum) {\n          return palettes[i];\n        }\n      }\n\n      return palettes[paletteNum - 1];\n    }\n    /**\n     * @param name MUST NOT be null/undefined. Otherwise call this function\n     *             twise with the same parameters will get different result.\n     * @param scope default this.\n     * @return Can be null/undefined\n     */\n\n\n    function getFromPalette(that, inner, defaultPalette, layeredPalette, name, scope, requestNum) {\n      scope = scope || that;\n      var scopeFields = inner(scope);\n      var paletteIdx = scopeFields.paletteIdx || 0;\n      var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; // Use `hasOwnProperty` to avoid conflict with Object.prototype.\n\n      if (paletteNameMap.hasOwnProperty(name)) {\n        return paletteNameMap[name];\n      }\n\n      var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum); // In case can't find in layered color palette.\n\n      palette = palette || defaultPalette;\n\n      if (!palette || !palette.length) {\n        return;\n      }\n\n      var pickedPaletteItem = palette[paletteIdx];\n\n      if (name) {\n        paletteNameMap[name] = pickedPaletteItem;\n      }\n\n      scopeFields.paletteIdx = (paletteIdx + 1) % palette.length;\n      return pickedPaletteItem;\n    }\n\n    function clearPalette(that, inner) {\n      inner(that).paletteIdx = 0;\n      inner(that).paletteNameMap = {};\n    }\n\n    // Internal method names:\n    // -----------------------\n\n    var reCreateSeriesIndices;\n    var assertSeriesInitialized;\n    var initBase;\n    var OPTION_INNER_KEY = '\\0_ec_inner';\n    var OPTION_INNER_VALUE = 1;\n    var BUITIN_COMPONENTS_MAP = {\n      grid: 'GridComponent',\n      polar: 'PolarComponent',\n      geo: 'GeoComponent',\n      singleAxis: 'SingleAxisComponent',\n      parallel: 'ParallelComponent',\n      calendar: 'CalendarComponent',\n      graphic: 'GraphicComponent',\n      toolbox: 'ToolboxComponent',\n      tooltip: 'TooltipComponent',\n      axisPointer: 'AxisPointerComponent',\n      brush: 'BrushComponent',\n      title: 'TitleComponent',\n      timeline: 'TimelineComponent',\n      markPoint: 'MarkPointComponent',\n      markLine: 'MarkLineComponent',\n      markArea: 'MarkAreaComponent',\n      legend: 'LegendComponent',\n      dataZoom: 'DataZoomComponent',\n      visualMap: 'VisualMapComponent',\n      // aria: 'AriaComponent',\n      // dataset: 'DatasetComponent',\n      // Dependencies\n      xAxis: 'GridComponent',\n      yAxis: 'GridComponent',\n      angleAxis: 'PolarComponent',\n      radiusAxis: 'PolarComponent'\n    };\n    var BUILTIN_CHARTS_MAP = {\n      line: 'LineChart',\n      bar: 'BarChart',\n      pie: 'PieChart',\n      scatter: 'ScatterChart',\n      radar: 'RadarChart',\n      map: 'MapChart',\n      tree: 'TreeChart',\n      treemap: 'TreemapChart',\n      graph: 'GraphChart',\n      gauge: 'GaugeChart',\n      funnel: 'FunnelChart',\n      parallel: 'ParallelChart',\n      sankey: 'SankeyChart',\n      boxplot: 'BoxplotChart',\n      candlestick: 'CandlestickChart',\n      effectScatter: 'EffectScatterChart',\n      lines: 'LinesChart',\n      heatmap: 'HeatmapChart',\n      pictorialBar: 'PictorialBarChart',\n      themeRiver: 'ThemeRiverChart',\n      sunburst: 'SunburstChart',\n      custom: 'CustomChart'\n    };\n    var componetsMissingLogPrinted = {};\n\n    function checkMissingComponents(option) {\n      each(option, function (componentOption, mainType) {\n        if (!ComponentModel.hasClass(mainType)) {\n          var componentImportName = BUITIN_COMPONENTS_MAP[mainType];\n\n          if (componentImportName && !componetsMissingLogPrinted[componentImportName]) {\n            error(\"Component \" + mainType + \" is used but not imported.\\nimport { \" + componentImportName + \" } from 'echarts/components';\\necharts.use([\" + componentImportName + \"]);\");\n            componetsMissingLogPrinted[componentImportName] = true;\n          }\n        }\n      });\n    }\n\n    var GlobalModel =\n    /** @class */\n    function (_super) {\n      __extends(GlobalModel, _super);\n\n      function GlobalModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) {\n        theme = theme || {};\n        this.option = null; // Mark as not initialized.\n\n        this._theme = new Model(theme);\n        this._locale = new Model(locale);\n        this._optionManager = optionManager;\n      };\n\n      GlobalModel.prototype.setOption = function (option, opts, optionPreprocessorFuncs) {\n        if (\"development\" !== 'production') {\n          assert(option != null, 'option is null/undefined');\n          assert(option[OPTION_INNER_KEY] !== OPTION_INNER_VALUE, 'please use chart.getOption()');\n        }\n\n        var innerOpt = normalizeSetOptionInput(opts);\n\n        this._optionManager.setOption(option, optionPreprocessorFuncs, innerOpt);\n\n        this._resetOption(null, innerOpt);\n      };\n      /**\n       * @param type null/undefined: reset all.\n       *        'recreate': force recreate all.\n       *        'timeline': only reset timeline option\n       *        'media': only reset media query option\n       * @return Whether option changed.\n       */\n\n\n      GlobalModel.prototype.resetOption = function (type, opt) {\n        return this._resetOption(type, normalizeSetOptionInput(opt));\n      };\n\n      GlobalModel.prototype._resetOption = function (type, opt) {\n        var optionChanged = false;\n        var optionManager = this._optionManager;\n\n        if (!type || type === 'recreate') {\n          var baseOption = optionManager.mountOption(type === 'recreate');\n\n          if (\"development\" !== 'production') {\n            checkMissingComponents(baseOption);\n          }\n\n          if (!this.option || type === 'recreate') {\n            initBase(this, baseOption);\n          } else {\n            this.restoreData();\n\n            this._mergeOption(baseOption, opt);\n          }\n\n          optionChanged = true;\n        }\n\n        if (type === 'timeline' || type === 'media') {\n          this.restoreData();\n        } // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`,\n        // it should better not have the same props with `MediaUnit['option']`.\n        // Because either `option2` or `MediaUnit['option']` will be always merged to \"current option\"\n        // rather than original \"baseOption\". If they both override a prop, the result might be\n        // unexpected when media state changed after `setOption` called.\n        // If we really need to modify a props in each `MediaUnit['option']`, use the full version\n        // (`{baseOption, media}`) in `setOption`.\n        // For `timeline`, the case is the same.\n\n\n        if (!type || type === 'recreate' || type === 'timeline') {\n          var timelineOption = optionManager.getTimelineOption(this);\n\n          if (timelineOption) {\n            optionChanged = true;\n\n            this._mergeOption(timelineOption, opt);\n          }\n        }\n\n        if (!type || type === 'recreate' || type === 'media') {\n          var mediaOptions = optionManager.getMediaOption(this);\n\n          if (mediaOptions.length) {\n            each(mediaOptions, function (mediaOption) {\n              optionChanged = true;\n\n              this._mergeOption(mediaOption, opt);\n            }, this);\n          }\n        }\n\n        return optionChanged;\n      };\n\n      GlobalModel.prototype.mergeOption = function (option) {\n        this._mergeOption(option, null);\n      };\n\n      GlobalModel.prototype._mergeOption = function (newOption, opt) {\n        var option = this.option;\n        var componentsMap = this._componentsMap;\n        var componentsCount = this._componentsCount;\n        var newCmptTypes = [];\n        var newCmptTypeMap = createHashMap();\n        var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap;\n        resetSourceDefaulter(this); // If no component class, merge directly.\n        // For example: color, animaiton options, etc.\n\n        each(newOption, function (componentOption, mainType) {\n          if (componentOption == null) {\n            return;\n          }\n\n          if (!ComponentModel.hasClass(mainType)) {\n            // globalSettingTask.dirty();\n            option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true);\n          } else if (mainType) {\n            newCmptTypes.push(mainType);\n            newCmptTypeMap.set(mainType, true);\n          }\n        });\n\n        if (replaceMergeMainTypeMap) {\n          // If there is a mainType `xxx` in `replaceMerge` but not declared in option,\n          // we trade it as it is declared in option as `{xxx: []}`. Because:\n          // (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`.\n          // (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`.\n          replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) {\n            if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) {\n              newCmptTypes.push(mainTypeInReplaceMerge);\n              newCmptTypeMap.set(mainTypeInReplaceMerge, true);\n            }\n          });\n        }\n\n        ComponentModel.topologicalTravel(newCmptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this);\n\n        function visitComponent(mainType) {\n          var newCmptOptionList = concatInternalOptions(this, mainType, normalizeToArray(newOption[mainType]));\n          var oldCmptList = componentsMap.get(mainType);\n          var mergeMode = // `!oldCmptList` means init. See the comment in `mappingToExists`\n          !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge';\n          var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode); // Set mainType and complete subType.\n\n          setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); // Empty it before the travel, in order to prevent `this._componentsMap`\n          // from being used in the `init`/`mergeOption`/`optionUpdated` of some\n          // components, which is probably incorrect logic.\n\n          option[mainType] = null;\n          componentsMap.set(mainType, null);\n          componentsCount.set(mainType, 0);\n          var optionsByMainType = [];\n          var cmptsByMainType = [];\n          var cmptsCountByMainType = 0;\n          var tooltipExists;\n          var tooltipWarningLogged;\n          each(mappingResult, function (resultItem, index) {\n            var componentModel = resultItem.existing;\n            var newCmptOption = resultItem.newOption;\n\n            if (!newCmptOption) {\n              if (componentModel) {\n                // Consider where is no new option and should be merged using {},\n                // see removeEdgeAndAdd in topologicalTravel and\n                // ComponentModel.getAllClassMainTypes.\n                componentModel.mergeOption({}, this);\n                componentModel.optionUpdated({}, false);\n              } // If no both `resultItem.exist` and `resultItem.option`,\n              // either it is in `replaceMerge` and not matched by any id,\n              // or it has been removed in previous `replaceMerge` and left a \"hole\" in this component index.\n\n            } else {\n              var isSeriesType = mainType === 'series';\n              var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, !isSeriesType // Give a more detailed warn later if series don't exists\n              );\n\n              if (!ComponentModelClass) {\n                if (\"development\" !== 'production') {\n                  var subType = resultItem.keyInfo.subType;\n                  var seriesImportName = BUILTIN_CHARTS_MAP[subType];\n\n                  if (!componetsMissingLogPrinted[subType]) {\n                    componetsMissingLogPrinted[subType] = true;\n\n                    if (seriesImportName) {\n                      error(\"Series \" + subType + \" is used but not imported.\\nimport { \" + seriesImportName + \" } from 'echarts/charts';\\necharts.use([\" + seriesImportName + \"]);\");\n                    } else {\n                      error(\"Unknown series \" + subType);\n                    }\n                  }\n                }\n\n                return;\n              } // TODO Before multiple tooltips get supported, we do this check to avoid unexpected exception.\n\n\n              if (mainType === 'tooltip') {\n                if (tooltipExists) {\n                  if (\"development\" !== 'production') {\n                    if (!tooltipWarningLogged) {\n                      warn('Currently only one tooltip component is allowed.');\n                      tooltipWarningLogged = true;\n                    }\n                  }\n\n                  return;\n                }\n\n                tooltipExists = true;\n              }\n\n              if (componentModel && componentModel.constructor === ComponentModelClass) {\n                componentModel.name = resultItem.keyInfo.name; // componentModel.settingTask && componentModel.settingTask.dirty();\n\n                componentModel.mergeOption(newCmptOption, this);\n                componentModel.optionUpdated(newCmptOption, false);\n              } else {\n                // PENDING Global as parent ?\n                var extraOpt = extend({\n                  componentIndex: index\n                }, resultItem.keyInfo);\n                componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); // Assign `keyInfo`\n\n                extend(componentModel, extraOpt);\n\n                if (resultItem.brandNew) {\n                  componentModel.__requireNewView = true;\n                }\n\n                componentModel.init(newCmptOption, this, this); // Call optionUpdated after init.\n                // newCmptOption has been used as componentModel.option\n                // and may be merged with theme and default, so pass null\n                // to avoid confusion.\n\n                componentModel.optionUpdated(null, true);\n              }\n            }\n\n            if (componentModel) {\n              optionsByMainType.push(componentModel.option);\n              cmptsByMainType.push(componentModel);\n              cmptsCountByMainType++;\n            } else {\n              // Always do assign to avoid elided item in array.\n              optionsByMainType.push(void 0);\n              cmptsByMainType.push(void 0);\n            }\n          }, this);\n          option[mainType] = optionsByMainType;\n          componentsMap.set(mainType, cmptsByMainType);\n          componentsCount.set(mainType, cmptsCountByMainType); // Backup series for filtering.\n\n          if (mainType === 'series') {\n            reCreateSeriesIndices(this);\n          }\n        } // If no series declared, ensure `_seriesIndices` initialized.\n\n\n        if (!this._seriesIndices) {\n          reCreateSeriesIndices(this);\n        }\n      };\n      /**\n       * Get option for output (cloned option and inner info removed)\n       */\n\n\n      GlobalModel.prototype.getOption = function () {\n        var option = clone(this.option);\n        each(option, function (optInMainType, mainType) {\n          if (ComponentModel.hasClass(mainType)) {\n            var opts = normalizeToArray(optInMainType); // Inner cmpts need to be removed.\n            // Inner cmpts might not be at last since ec5.0, but still\n            // compatible for users: if inner cmpt at last, splice the returned array.\n\n            var realLen = opts.length;\n            var metNonInner = false;\n\n            for (var i = realLen - 1; i >= 0; i--) {\n              // Remove options with inner id.\n              if (opts[i] && !isComponentIdInternal(opts[i])) {\n                metNonInner = true;\n              } else {\n                opts[i] = null;\n                !metNonInner && realLen--;\n              }\n            }\n\n            opts.length = realLen;\n            option[mainType] = opts;\n          }\n        });\n        delete option[OPTION_INNER_KEY];\n        return option;\n      };\n\n      GlobalModel.prototype.getTheme = function () {\n        return this._theme;\n      };\n\n      GlobalModel.prototype.getLocaleModel = function () {\n        return this._locale;\n      };\n\n      GlobalModel.prototype.setUpdatePayload = function (payload) {\n        this._payload = payload;\n      };\n\n      GlobalModel.prototype.getUpdatePayload = function () {\n        return this._payload;\n      };\n      /**\n       * @param idx If not specified, return the first one.\n       */\n\n\n      GlobalModel.prototype.getComponent = function (mainType, idx) {\n        var list = this._componentsMap.get(mainType);\n\n        if (list) {\n          var cmpt = list[idx || 0];\n\n          if (cmpt) {\n            return cmpt;\n          } else if (idx == null) {\n            for (var i = 0; i < list.length; i++) {\n              if (list[i]) {\n                return list[i];\n              }\n            }\n          }\n        }\n      };\n      /**\n       * @return Never be null/undefined.\n       */\n\n\n      GlobalModel.prototype.queryComponents = function (condition) {\n        var mainType = condition.mainType;\n\n        if (!mainType) {\n          return [];\n        }\n\n        var index = condition.index;\n        var id = condition.id;\n        var name = condition.name;\n\n        var cmpts = this._componentsMap.get(mainType);\n\n        if (!cmpts || !cmpts.length) {\n          return [];\n        }\n\n        var result;\n\n        if (index != null) {\n          result = [];\n          each(normalizeToArray(index), function (idx) {\n            cmpts[idx] && result.push(cmpts[idx]);\n          });\n        } else if (id != null) {\n          result = queryByIdOrName('id', id, cmpts);\n        } else if (name != null) {\n          result = queryByIdOrName('name', name, cmpts);\n        } else {\n          // Return all non-empty components in that mainType\n          result = filter(cmpts, function (cmpt) {\n            return !!cmpt;\n          });\n        }\n\n        return filterBySubType(result, condition);\n      };\n      /**\n       * The interface is different from queryComponents,\n       * which is convenient for inner usage.\n       *\n       * @usage\n       * let result = findComponents(\n       *     {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}\n       * );\n       * let result = findComponents(\n       *     {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}\n       * );\n       * let result = findComponents(\n       *     {mainType: 'series',\n       *     filter: function (model, index) {...}}\n       * );\n       * // result like [component0, componnet1, ...]\n       */\n\n\n      GlobalModel.prototype.findComponents = function (condition) {\n        var query = condition.query;\n        var mainType = condition.mainType;\n        var queryCond = getQueryCond(query);\n        var result = queryCond ? this.queryComponents(queryCond) // Retrieve all non-empty components.\n        : filter(this._componentsMap.get(mainType), function (cmpt) {\n          return !!cmpt;\n        });\n        return doFilter(filterBySubType(result, condition));\n\n        function getQueryCond(q) {\n          var indexAttr = mainType + 'Index';\n          var idAttr = mainType + 'Id';\n          var nameAttr = mainType + 'Name';\n          return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? {\n            mainType: mainType,\n            // subType will be filtered finally.\n            index: q[indexAttr],\n            id: q[idAttr],\n            name: q[nameAttr]\n          } : null;\n        }\n\n        function doFilter(res) {\n          return condition.filter ? filter(res, condition.filter) : res;\n        }\n      };\n\n      GlobalModel.prototype.eachComponent = function (mainType, cb, context) {\n        var componentsMap = this._componentsMap;\n\n        if (isFunction(mainType)) {\n          var ctxForAll_1 = cb;\n          var cbForAll_1 = mainType;\n          componentsMap.each(function (cmpts, componentType) {\n            for (var i = 0; cmpts && i < cmpts.length; i++) {\n              var cmpt = cmpts[i];\n              cmpt && cbForAll_1.call(ctxForAll_1, componentType, cmpt, cmpt.componentIndex);\n            }\n          });\n        } else {\n          var cmpts = isString(mainType) ? componentsMap.get(mainType) : isObject(mainType) ? this.findComponents(mainType) : null;\n\n          for (var i = 0; cmpts && i < cmpts.length; i++) {\n            var cmpt = cmpts[i];\n            cmpt && cb.call(context, cmpt, cmpt.componentIndex);\n          }\n        }\n      };\n      /**\n       * Get series list before filtered by name.\n       */\n\n\n      GlobalModel.prototype.getSeriesByName = function (name) {\n        var nameStr = convertOptionIdName(name, null);\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries && nameStr != null && oneSeries.name === nameStr;\n        });\n      };\n      /**\n       * Get series list before filtered by index.\n       */\n\n\n      GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) {\n        return this._componentsMap.get('series')[seriesIndex];\n      };\n      /**\n       * Get series list before filtered by type.\n       * FIXME: rename to getRawSeriesByType?\n       */\n\n\n      GlobalModel.prototype.getSeriesByType = function (subType) {\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries && oneSeries.subType === subType;\n        });\n      };\n      /**\n       * Get all series before filtered.\n       */\n\n\n      GlobalModel.prototype.getSeries = function () {\n        return filter(this._componentsMap.get('series'), function (oneSeries) {\n          return !!oneSeries;\n        });\n      };\n      /**\n       * Count series before filtered.\n       */\n\n\n      GlobalModel.prototype.getSeriesCount = function () {\n        return this._componentsCount.get('series');\n      };\n      /**\n       * After filtering, series may be different\n       * from raw series.\n       */\n\n\n      GlobalModel.prototype.eachSeries = function (cb, context) {\n        assertSeriesInitialized(this);\n        each(this._seriesIndices, function (rawSeriesIndex) {\n          var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n          cb.call(context, series, rawSeriesIndex);\n        }, this);\n      };\n      /**\n       * Iterate raw series before filtered.\n       *\n       * @param {Function} cb\n       * @param {*} context\n       */\n\n\n      GlobalModel.prototype.eachRawSeries = function (cb, context) {\n        each(this._componentsMap.get('series'), function (series) {\n          series && cb.call(context, series, series.componentIndex);\n        });\n      };\n      /**\n       * After filtering, series may be different.\n       * from raw series.\n       */\n\n\n      GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) {\n        assertSeriesInitialized(this);\n        each(this._seriesIndices, function (rawSeriesIndex) {\n          var series = this._componentsMap.get('series')[rawSeriesIndex];\n\n          if (series.subType === subType) {\n            cb.call(context, series, rawSeriesIndex);\n          }\n        }, this);\n      };\n      /**\n       * Iterate raw series before filtered of given type.\n       */\n\n\n      GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) {\n        return each(this.getSeriesByType(subType), cb, context);\n      };\n\n      GlobalModel.prototype.isSeriesFiltered = function (seriesModel) {\n        assertSeriesInitialized(this);\n        return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;\n      };\n\n      GlobalModel.prototype.getCurrentSeriesIndices = function () {\n        return (this._seriesIndices || []).slice();\n      };\n\n      GlobalModel.prototype.filterSeries = function (cb, context) {\n        assertSeriesInitialized(this);\n        var newSeriesIndices = [];\n        each(this._seriesIndices, function (seriesRawIdx) {\n          var series = this._componentsMap.get('series')[seriesRawIdx];\n\n          cb.call(context, series, seriesRawIdx) && newSeriesIndices.push(seriesRawIdx);\n        }, this);\n        this._seriesIndices = newSeriesIndices;\n        this._seriesIndicesMap = createHashMap(newSeriesIndices);\n      };\n\n      GlobalModel.prototype.restoreData = function (payload) {\n        reCreateSeriesIndices(this);\n        var componentsMap = this._componentsMap;\n        var componentTypes = [];\n        componentsMap.each(function (components, componentType) {\n          if (ComponentModel.hasClass(componentType)) {\n            componentTypes.push(componentType);\n          }\n        });\n        ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType) {\n          each(componentsMap.get(componentType), function (component) {\n            if (component && (componentType !== 'series' || !isNotTargetSeries(component, payload))) {\n              component.restoreData();\n            }\n          });\n        });\n      };\n\n      GlobalModel.internalField = function () {\n        reCreateSeriesIndices = function (ecModel) {\n          var seriesIndices = ecModel._seriesIndices = [];\n          each(ecModel._componentsMap.get('series'), function (series) {\n            // series may have been removed by `replaceMerge`.\n            series && seriesIndices.push(series.componentIndex);\n          });\n          ecModel._seriesIndicesMap = createHashMap(seriesIndices);\n        };\n\n        assertSeriesInitialized = function (ecModel) {\n          // Components that use _seriesIndices should depends on series component,\n          // which make sure that their initialization is after series.\n          if (\"development\" !== 'production') {\n            if (!ecModel._seriesIndices) {\n              throw new Error('Option should contains series.');\n            }\n          }\n        };\n\n        initBase = function (ecModel, baseOption) {\n          // Using OPTION_INNER_KEY to mark that this option cannot be used outside,\n          // i.e. `chart.setOption(chart.getModel().option);` is forbidden.\n          ecModel.option = {};\n          ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; // Init with series: [], in case of calling findSeries method\n          // before series initialized.\n\n          ecModel._componentsMap = createHashMap({\n            series: []\n          });\n          ecModel._componentsCount = createHashMap(); // If user spefied `option.aria`, aria will be enable. This detection should be\n          // performed before theme and globalDefault merge.\n\n          var airaOption = baseOption.aria;\n\n          if (isObject(airaOption) && airaOption.enabled == null) {\n            airaOption.enabled = true;\n          }\n\n          mergeTheme(baseOption, ecModel._theme.option); // TODO Needs clone when merging to the unexisted property\n\n          merge(baseOption, globalDefault, false);\n\n          ecModel._mergeOption(baseOption, null);\n        };\n      }();\n\n      return GlobalModel;\n    }(Model);\n\n    function isNotTargetSeries(seriesModel, payload) {\n      if (payload) {\n        var index = payload.seriesIndex;\n        var id = payload.seriesId;\n        var name_1 = payload.seriesName;\n        return index != null && seriesModel.componentIndex !== index || id != null && seriesModel.id !== id || name_1 != null && seriesModel.name !== name_1;\n      }\n    }\n\n    function mergeTheme(option, theme) {\n      // PENDING\n      // NOT use `colorLayer` in theme if option has `color`\n      var notMergeColorLayer = option.color && !option.colorLayer;\n      each(theme, function (themeItem, name) {\n        if (name === 'colorLayer' && notMergeColorLayer) {\n          return;\n        } // If it is component model mainType, the model handles that merge later.\n        // otherwise, merge them here.\n\n\n        if (!ComponentModel.hasClass(name)) {\n          if (typeof themeItem === 'object') {\n            option[name] = !option[name] ? clone(themeItem) : merge(option[name], themeItem, false);\n          } else {\n            if (option[name] == null) {\n              option[name] = themeItem;\n            }\n          }\n        }\n      });\n    }\n\n    function queryByIdOrName(attr, idOrName, cmpts) {\n      // Here is a break from echarts4: string and number are\n      // treated as equal.\n      if (isArray(idOrName)) {\n        var keyMap_1 = createHashMap();\n        each(idOrName, function (idOrNameItem) {\n          if (idOrNameItem != null) {\n            var idName = convertOptionIdName(idOrNameItem, null);\n            idName != null && keyMap_1.set(idOrNameItem, true);\n          }\n        });\n        return filter(cmpts, function (cmpt) {\n          return cmpt && keyMap_1.get(cmpt[attr]);\n        });\n      } else {\n        var idName_1 = convertOptionIdName(idOrName, null);\n        return filter(cmpts, function (cmpt) {\n          return cmpt && idName_1 != null && cmpt[attr] === idName_1;\n        });\n      }\n    }\n\n    function filterBySubType(components, condition) {\n      // Using hasOwnProperty for restrict. Consider\n      // subType is undefined in user payload.\n      return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) {\n        return cmpt && cmpt.subType === condition.subType;\n      }) : components;\n    }\n\n    function normalizeSetOptionInput(opts) {\n      var replaceMergeMainTypeMap = createHashMap();\n      opts && each(normalizeToArray(opts.replaceMerge), function (mainType) {\n        if (\"development\" !== 'production') {\n          assert(ComponentModel.hasClass(mainType), '\"' + mainType + '\" is not valid component main type in \"replaceMerge\"');\n        }\n\n        replaceMergeMainTypeMap.set(mainType, true);\n      });\n      return {\n        replaceMergeMainTypeMap: replaceMergeMainTypeMap\n      };\n    }\n\n    mixin(GlobalModel, PaletteMixin);\n\n    var availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isSSR', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', // 'getModel',\n    'getOption', // 'getViewOfComponentModel',\n    // 'getViewOfSeriesModel',\n    'getId', 'updateLabelLayout'];\n\n    var ExtensionAPI =\n    /** @class */\n    function () {\n      function ExtensionAPI(ecInstance) {\n        each(availableMethods, function (methodName) {\n          this[methodName] = bind(ecInstance[methodName], ecInstance);\n        }, this);\n      }\n\n      return ExtensionAPI;\n    }();\n\n    var coordinateSystemCreators = {};\n\n    var CoordinateSystemManager =\n    /** @class */\n    function () {\n      function CoordinateSystemManager() {\n        this._coordinateSystems = [];\n      }\n\n      CoordinateSystemManager.prototype.create = function (ecModel, api) {\n        var coordinateSystems = [];\n        each(coordinateSystemCreators, function (creator, type) {\n          var list = creator.create(ecModel, api);\n          coordinateSystems = coordinateSystems.concat(list || []);\n        });\n        this._coordinateSystems = coordinateSystems;\n      };\n\n      CoordinateSystemManager.prototype.update = function (ecModel, api) {\n        each(this._coordinateSystems, function (coordSys) {\n          coordSys.update && coordSys.update(ecModel, api);\n        });\n      };\n\n      CoordinateSystemManager.prototype.getCoordinateSystems = function () {\n        return this._coordinateSystems.slice();\n      };\n\n      CoordinateSystemManager.register = function (type, creator) {\n        coordinateSystemCreators[type] = creator;\n      };\n\n      CoordinateSystemManager.get = function (type) {\n        return coordinateSystemCreators[type];\n      };\n\n      return CoordinateSystemManager;\n    }();\n\n    var QUERY_REG = /^(min|max)?(.+)$/; // Key: mainType\n    // type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>;\n\n    /**\n     * TERM EXPLANATIONS:\n     * See `ECOption` and `ECUnitOption` in `src/util/types.ts`.\n     */\n\n    var OptionManager =\n    /** @class */\n    function () {\n      // timeline.notMerge is not supported in ec3. Firstly there is rearly\n      // case that notMerge is needed. Secondly supporting 'notMerge' requires\n      // rawOption cloned and backuped when timeline changed, which does no\n      // good to performance. What's more, that both timeline and setOption\n      // method supply 'notMerge' brings complex and some problems.\n      // Consider this case:\n      // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);\n      // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);\n      function OptionManager(api) {\n        this._timelineOptions = [];\n        this._mediaList = [];\n        /**\n         * -1, means default.\n         * empty means no media.\n         */\n\n        this._currentMediaIndices = [];\n        this._api = api;\n      }\n\n      OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) {\n        if (rawOption) {\n          // That set dat primitive is dangerous if user reuse the data when setOption again.\n          each(normalizeToArray(rawOption.series), function (series) {\n            series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);\n          });\n          each(normalizeToArray(rawOption.dataset), function (dataset) {\n            dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source);\n          });\n        } // Caution: some series modify option data, if do not clone,\n        // it should ensure that the repeat modify correctly\n        // (create a new object when modify itself).\n\n\n        rawOption = clone(rawOption); // FIXME\n        // If some property is set in timeline options or media option but\n        // not set in baseOption, a warning should be given.\n\n        var optionBackup = this._optionBackup;\n        var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup);\n        this._newBaseOption = newParsedOption.baseOption; // For setOption at second time (using merge mode);\n\n        if (optionBackup) {\n          // FIXME\n          // the restore merge solution is essentially incorrect.\n          // the mapping can not be 100% consistent with ecModel, which probably brings\n          // potential bug!\n          // The first merge is delayed, because in most cases, users do not call `setOption` twice.\n          // let fakeCmptsMap = this._fakeCmptsMap;\n          // if (!fakeCmptsMap) {\n          //     fakeCmptsMap = this._fakeCmptsMap = createHashMap();\n          //     mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null);\n          // }\n          // mergeToBackupOption(\n          //     fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt\n          // );\n          // For simplicity, timeline options and media options do not support merge,\n          // that is, if you `setOption` twice and both has timeline options, the latter\n          // timeline options will not be merged to the former, but just substitute them.\n          if (newParsedOption.timelineOptions.length) {\n            optionBackup.timelineOptions = newParsedOption.timelineOptions;\n          }\n\n          if (newParsedOption.mediaList.length) {\n            optionBackup.mediaList = newParsedOption.mediaList;\n          }\n\n          if (newParsedOption.mediaDefault) {\n            optionBackup.mediaDefault = newParsedOption.mediaDefault;\n          }\n        } else {\n          this._optionBackup = newParsedOption;\n        }\n      };\n\n      OptionManager.prototype.mountOption = function (isRecreate) {\n        var optionBackup = this._optionBackup;\n        this._timelineOptions = optionBackup.timelineOptions;\n        this._mediaList = optionBackup.mediaList;\n        this._mediaDefault = optionBackup.mediaDefault;\n        this._currentMediaIndices = [];\n        return clone(isRecreate // this._optionBackup.baseOption, which is created at the first `setOption`\n        // called, and is merged into every new option by inner method `mergeToBackupOption`\n        // each time `setOption` called, can be only used in `isRecreate`, because\n        // its reliability is under suspicion. In other cases option merge is\n        // performed by `model.mergeOption`.\n        ? optionBackup.baseOption : this._newBaseOption);\n      };\n\n      OptionManager.prototype.getTimelineOption = function (ecModel) {\n        var option;\n        var timelineOptions = this._timelineOptions;\n\n        if (timelineOptions.length) {\n          // getTimelineOption can only be called after ecModel inited,\n          // so we can get currentIndex from timelineModel.\n          var timelineModel = ecModel.getComponent('timeline');\n\n          if (timelineModel) {\n            option = clone( // FIXME:TS as TimelineModel or quivlant interface\n            timelineOptions[timelineModel.getCurrentIndex()]);\n          }\n        }\n\n        return option;\n      };\n\n      OptionManager.prototype.getMediaOption = function (ecModel) {\n        var ecWidth = this._api.getWidth();\n\n        var ecHeight = this._api.getHeight();\n\n        var mediaList = this._mediaList;\n        var mediaDefault = this._mediaDefault;\n        var indices = [];\n        var result = []; // No media defined.\n\n        if (!mediaList.length && !mediaDefault) {\n          return result;\n        } // Multi media may be applied, the latter defined media has higher priority.\n\n\n        for (var i = 0, len = mediaList.length; i < len; i++) {\n          if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {\n            indices.push(i);\n          }\n        } // FIXME\n        // Whether mediaDefault should force users to provide? Otherwise\n        // the change by media query can not be recorvered.\n\n\n        if (!indices.length && mediaDefault) {\n          indices = [-1];\n        }\n\n        if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {\n          result = map(indices, function (index) {\n            return clone(index === -1 ? mediaDefault.option : mediaList[index].option);\n          });\n        } // Otherwise return nothing.\n\n\n        this._currentMediaIndices = indices;\n        return result;\n      };\n\n      return OptionManager;\n    }();\n    /**\n     * [RAW_OPTION_PATTERNS]\n     * (Note: \"series: []\" represents all other props in `ECUnitOption`)\n     *\n     * (1) No prop \"baseOption\" declared:\n     * Root option is used as \"baseOption\" (except prop \"options\" and \"media\").\n     * ```js\n     * option = {\n     *     series: [],\n     *     timeline: {},\n     *     options: [],\n     * };\n     * option = {\n     *     series: [],\n     *     media: {},\n     * };\n     * option = {\n     *     series: [],\n     *     timeline: {},\n     *     options: [],\n     *     media: {},\n     * }\n     * ```\n     *\n     * (2) Prop \"baseOption\" declared:\n     * If \"baseOption\" declared, `ECUnitOption` props can only be declared\n     * inside \"baseOption\" except prop \"timeline\" (compat ec2).\n     * ```js\n     * option = {\n     *     baseOption: {\n     *         timeline: {},\n     *         series: [],\n     *     },\n     *     options: []\n     * };\n     * option = {\n     *     baseOption: {\n     *         series: [],\n     *     },\n     *     media: []\n     * };\n     * option = {\n     *     baseOption: {\n     *         timeline: {},\n     *         series: [],\n     *     },\n     *     options: []\n     *     media: []\n     * };\n     * option = {\n     *     // ec3 compat ec2: allow (only) `timeline` declared\n     *     // outside baseOption. Keep this setting for compat.\n     *     timeline: {},\n     *     baseOption: {\n     *         series: [],\n     *     },\n     *     options: [],\n     *     media: []\n     * };\n     * ```\n     */\n\n\n    function parseRawOption( // `rawOption` May be modified\n    rawOption, optionPreprocessorFuncs, isNew) {\n      var mediaList = [];\n      var mediaDefault;\n      var baseOption;\n      var declaredBaseOption = rawOption.baseOption; // Compatible with ec2, [RAW_OPTION_PATTERNS] above.\n\n      var timelineOnRoot = rawOption.timeline;\n      var timelineOptionsOnRoot = rawOption.options;\n      var mediaOnRoot = rawOption.media;\n      var hasMedia = !!rawOption.media;\n      var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline);\n\n      if (declaredBaseOption) {\n        baseOption = declaredBaseOption; // For merge option.\n\n        if (!baseOption.timeline) {\n          baseOption.timeline = timelineOnRoot;\n        }\n      } // For convenience, enable to use the root option as the `baseOption`:\n      // `{ ...normalOptionProps, media: [{ ... }, { ... }] }`\n      else {\n          if (hasTimeline || hasMedia) {\n            rawOption.options = rawOption.media = null;\n          }\n\n          baseOption = rawOption;\n        }\n\n      if (hasMedia) {\n        if (isArray(mediaOnRoot)) {\n          each(mediaOnRoot, function (singleMedia) {\n            if (\"development\" !== 'production') {\n              // Real case of wrong config.\n              if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) {\n                error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }');\n              }\n            }\n\n            if (singleMedia && singleMedia.option) {\n              if (singleMedia.query) {\n                mediaList.push(singleMedia);\n              } else if (!mediaDefault) {\n                // Use the first media default.\n                mediaDefault = singleMedia;\n              }\n            }\n          });\n        } else {\n          if (\"development\" !== 'production') {\n            // Real case of wrong config.\n            error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }');\n          }\n        }\n      }\n\n      doPreprocess(baseOption);\n      each(timelineOptionsOnRoot, function (option) {\n        return doPreprocess(option);\n      });\n      each(mediaList, function (media) {\n        return doPreprocess(media.option);\n      });\n\n      function doPreprocess(option) {\n        each(optionPreprocessorFuncs, function (preProcess) {\n          preProcess(option, isNew);\n        });\n      }\n\n      return {\n        baseOption: baseOption,\n        timelineOptions: timelineOptionsOnRoot || [],\n        mediaDefault: mediaDefault,\n        mediaList: mediaList\n      };\n    }\n    /**\n     * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>\n     * Support: width, height, aspectRatio\n     * Can use max or min as prefix.\n     */\n\n\n    function applyMediaQuery(query, ecWidth, ecHeight) {\n      var realMap = {\n        width: ecWidth,\n        height: ecHeight,\n        aspectratio: ecWidth / ecHeight // lower case for convenience.\n\n      };\n      var applicable = true;\n      each(query, function (value, attr) {\n        var matched = attr.match(QUERY_REG);\n\n        if (!matched || !matched[1] || !matched[2]) {\n          return;\n        }\n\n        var operator = matched[1];\n        var realAttr = matched[2].toLowerCase();\n\n        if (!compare(realMap[realAttr], value, operator)) {\n          applicable = false;\n        }\n      });\n      return applicable;\n    }\n\n    function compare(real, expect, operator) {\n      if (operator === 'min') {\n        return real >= expect;\n      } else if (operator === 'max') {\n        return real <= expect;\n      } else {\n        // Equals\n        return real === expect;\n      }\n    }\n\n    function indicesEquals(indices1, indices2) {\n      // indices is always order by asc and has only finite number.\n      return indices1.join(',') === indices2.join(',');\n    }\n\n    var each$2 = each;\n    var isObject$1 = isObject;\n    var POSSIBLE_STYLES = ['areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', 'chordStyle', 'label', 'labelLine'];\n\n    function compatEC2ItemStyle(opt) {\n      var itemStyleOpt = opt && opt.itemStyle;\n\n      if (!itemStyleOpt) {\n        return;\n      }\n\n      for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {\n        var styleName = POSSIBLE_STYLES[i];\n        var normalItemStyleOpt = itemStyleOpt.normal;\n        var emphasisItemStyleOpt = itemStyleOpt.emphasis;\n\n        if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(\"itemStyle.normal.\" + styleName, styleName);\n          }\n\n          opt[styleName] = opt[styleName] || {};\n\n          if (!opt[styleName].normal) {\n            opt[styleName].normal = normalItemStyleOpt[styleName];\n          } else {\n            merge(opt[styleName].normal, normalItemStyleOpt[styleName]);\n          }\n\n          normalItemStyleOpt[styleName] = null;\n        }\n\n        if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(\"itemStyle.emphasis.\" + styleName, \"emphasis.\" + styleName);\n          }\n\n          opt[styleName] = opt[styleName] || {};\n\n          if (!opt[styleName].emphasis) {\n            opt[styleName].emphasis = emphasisItemStyleOpt[styleName];\n          } else {\n            merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);\n          }\n\n          emphasisItemStyleOpt[styleName] = null;\n        }\n      }\n    }\n\n    function convertNormalEmphasis(opt, optType, useExtend) {\n      if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) {\n        var normalOpt = opt[optType].normal;\n        var emphasisOpt = opt[optType].emphasis;\n\n        if (normalOpt) {\n          if (\"development\" !== 'production') {\n            // eslint-disable-next-line max-len\n            deprecateLog(\"'normal' hierarchy in \" + optType + \" has been removed since 4.0. All style properties are configured in \" + optType + \" directly now.\");\n          } // Timeline controlStyle has other properties besides normal and emphasis\n\n\n          if (useExtend) {\n            opt[optType].normal = opt[optType].emphasis = null;\n            defaults(opt[optType], normalOpt);\n          } else {\n            opt[optType] = normalOpt;\n          }\n        }\n\n        if (emphasisOpt) {\n          if (\"development\" !== 'production') {\n            deprecateLog(optType + \".emphasis has been changed to emphasis.\" + optType + \" since 4.0\");\n          }\n\n          opt.emphasis = opt.emphasis || {};\n          opt.emphasis[optType] = emphasisOpt; // Also compat the case user mix the style and focus together in ec3 style\n          // for example: { itemStyle: { normal: {}, emphasis: {focus, shadowBlur} } }\n\n          if (emphasisOpt.focus) {\n            opt.emphasis.focus = emphasisOpt.focus;\n          }\n\n          if (emphasisOpt.blurScope) {\n            opt.emphasis.blurScope = emphasisOpt.blurScope;\n          }\n        }\n      }\n    }\n\n    function removeEC3NormalStatus(opt) {\n      convertNormalEmphasis(opt, 'itemStyle');\n      convertNormalEmphasis(opt, 'lineStyle');\n      convertNormalEmphasis(opt, 'areaStyle');\n      convertNormalEmphasis(opt, 'label');\n      convertNormalEmphasis(opt, 'labelLine'); // treemap\n\n      convertNormalEmphasis(opt, 'upperLabel'); // graph\n\n      convertNormalEmphasis(opt, 'edgeLabel');\n    }\n\n    function compatTextStyle(opt, propName) {\n      // Check whether is not object (string\\null\\undefined ...)\n      var labelOptSingle = isObject$1(opt) && opt[propName];\n      var textStyle = isObject$1(labelOptSingle) && labelOptSingle.textStyle;\n\n      if (textStyle) {\n        if (\"development\" !== 'production') {\n          // eslint-disable-next-line max-len\n          deprecateLog(\"textStyle hierarchy in \" + propName + \" has been removed since 4.0. All textStyle properties are configured in \" + propName + \" directly now.\");\n        }\n\n        for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {\n          var textPropName = TEXT_STYLE_OPTIONS[i];\n\n          if (textStyle.hasOwnProperty(textPropName)) {\n            labelOptSingle[textPropName] = textStyle[textPropName];\n          }\n        }\n      }\n    }\n\n    function compatEC3CommonStyles(opt) {\n      if (opt) {\n        removeEC3NormalStatus(opt);\n        compatTextStyle(opt, 'label');\n        opt.emphasis && compatTextStyle(opt.emphasis, 'label');\n      }\n    }\n\n    function processSeries(seriesOpt) {\n      if (!isObject$1(seriesOpt)) {\n        return;\n      }\n\n      compatEC2ItemStyle(seriesOpt);\n      removeEC3NormalStatus(seriesOpt);\n      compatTextStyle(seriesOpt, 'label'); // treemap\n\n      compatTextStyle(seriesOpt, 'upperLabel'); // graph\n\n      compatTextStyle(seriesOpt, 'edgeLabel');\n\n      if (seriesOpt.emphasis) {\n        compatTextStyle(seriesOpt.emphasis, 'label'); // treemap\n\n        compatTextStyle(seriesOpt.emphasis, 'upperLabel'); // graph\n\n        compatTextStyle(seriesOpt.emphasis, 'edgeLabel');\n      }\n\n      var markPoint = seriesOpt.markPoint;\n\n      if (markPoint) {\n        compatEC2ItemStyle(markPoint);\n        compatEC3CommonStyles(markPoint);\n      }\n\n      var markLine = seriesOpt.markLine;\n\n      if (markLine) {\n        compatEC2ItemStyle(markLine);\n        compatEC3CommonStyles(markLine);\n      }\n\n      var markArea = seriesOpt.markArea;\n\n      if (markArea) {\n        compatEC3CommonStyles(markArea);\n      }\n\n      var data = seriesOpt.data; // Break with ec3: if `setOption` again, there may be no `type` in option,\n      // then the backward compat based on option type will not be performed.\n\n      if (seriesOpt.type === 'graph') {\n        data = data || seriesOpt.nodes;\n        var edgeData = seriesOpt.links || seriesOpt.edges;\n\n        if (edgeData && !isTypedArray(edgeData)) {\n          for (var i = 0; i < edgeData.length; i++) {\n            compatEC3CommonStyles(edgeData[i]);\n          }\n        }\n\n        each(seriesOpt.categories, function (opt) {\n          removeEC3NormalStatus(opt);\n        });\n      }\n\n      if (data && !isTypedArray(data)) {\n        for (var i = 0; i < data.length; i++) {\n          compatEC3CommonStyles(data[i]);\n        }\n      } // mark point data\n\n\n      markPoint = seriesOpt.markPoint;\n\n      if (markPoint && markPoint.data) {\n        var mpData = markPoint.data;\n\n        for (var i = 0; i < mpData.length; i++) {\n          compatEC3CommonStyles(mpData[i]);\n        }\n      } // mark line data\n\n\n      markLine = seriesOpt.markLine;\n\n      if (markLine && markLine.data) {\n        var mlData = markLine.data;\n\n        for (var i = 0; i < mlData.length; i++) {\n          if (isArray(mlData[i])) {\n            compatEC3CommonStyles(mlData[i][0]);\n            compatEC3CommonStyles(mlData[i][1]);\n          } else {\n            compatEC3CommonStyles(mlData[i]);\n          }\n        }\n      } // Series\n\n\n      if (seriesOpt.type === 'gauge') {\n        compatTextStyle(seriesOpt, 'axisLabel');\n        compatTextStyle(seriesOpt, 'title');\n        compatTextStyle(seriesOpt, 'detail');\n      } else if (seriesOpt.type === 'treemap') {\n        convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle');\n        each(seriesOpt.levels, function (opt) {\n          removeEC3NormalStatus(opt);\n        });\n      } else if (seriesOpt.type === 'tree') {\n        removeEC3NormalStatus(seriesOpt.leaves);\n      } // sunburst starts from ec4, so it does not need to compat levels.\n\n    }\n\n    function toArr(o) {\n      return isArray(o) ? o : o ? [o] : [];\n    }\n\n    function toObj(o) {\n      return (isArray(o) ? o[0] : o) || {};\n    }\n\n    function globalCompatStyle(option, isTheme) {\n      each$2(toArr(option.series), function (seriesOpt) {\n        isObject$1(seriesOpt) && processSeries(seriesOpt);\n      });\n      var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];\n      isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');\n      each$2(axes, function (axisName) {\n        each$2(toArr(option[axisName]), function (axisOpt) {\n          if (axisOpt) {\n            compatTextStyle(axisOpt, 'axisLabel');\n            compatTextStyle(axisOpt.axisPointer, 'label');\n          }\n        });\n      });\n      each$2(toArr(option.parallel), function (parallelOpt) {\n        var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;\n        compatTextStyle(parallelAxisDefault, 'axisLabel');\n        compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');\n      });\n      each$2(toArr(option.calendar), function (calendarOpt) {\n        convertNormalEmphasis(calendarOpt, 'itemStyle');\n        compatTextStyle(calendarOpt, 'dayLabel');\n        compatTextStyle(calendarOpt, 'monthLabel');\n        compatTextStyle(calendarOpt, 'yearLabel');\n      }); // radar.name.textStyle\n\n      each$2(toArr(option.radar), function (radarOpt) {\n        compatTextStyle(radarOpt, 'name'); // Use axisName instead of name because component has name property\n\n        if (radarOpt.name && radarOpt.axisName == null) {\n          radarOpt.axisName = radarOpt.name;\n          delete radarOpt.name;\n\n          if (\"development\" !== 'production') {\n            deprecateLog('name property in radar component has been changed to axisName');\n          }\n        }\n\n        if (radarOpt.nameGap != null && radarOpt.axisNameGap == null) {\n          radarOpt.axisNameGap = radarOpt.nameGap;\n          delete radarOpt.nameGap;\n\n          if (\"development\" !== 'production') {\n            deprecateLog('nameGap property in radar component has been changed to axisNameGap');\n          }\n        }\n\n        if (\"development\" !== 'production') {\n          each$2(radarOpt.indicator, function (indicatorOpt) {\n            if (indicatorOpt.text) {\n              deprecateReplaceLog('text', 'name', 'radar.indicator');\n            }\n          });\n        }\n      });\n      each$2(toArr(option.geo), function (geoOpt) {\n        if (isObject$1(geoOpt)) {\n          compatEC3CommonStyles(geoOpt);\n          each$2(toArr(geoOpt.regions), function (regionObj) {\n            compatEC3CommonStyles(regionObj);\n          });\n        }\n      });\n      each$2(toArr(option.timeline), function (timelineOpt) {\n        compatEC3CommonStyles(timelineOpt);\n        convertNormalEmphasis(timelineOpt, 'label');\n        convertNormalEmphasis(timelineOpt, 'itemStyle');\n        convertNormalEmphasis(timelineOpt, 'controlStyle', true);\n        var data = timelineOpt.data;\n        isArray(data) && each(data, function (item) {\n          if (isObject(item)) {\n            convertNormalEmphasis(item, 'label');\n            convertNormalEmphasis(item, 'itemStyle');\n          }\n        });\n      });\n      each$2(toArr(option.toolbox), function (toolboxOpt) {\n        convertNormalEmphasis(toolboxOpt, 'iconStyle');\n        each$2(toolboxOpt.feature, function (featureOpt) {\n          convertNormalEmphasis(featureOpt, 'iconStyle');\n        });\n      });\n      compatTextStyle(toObj(option.axisPointer), 'label');\n      compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); // Clean logs\n      // storedLogs = {};\n    }\n\n    function get(opt, path) {\n      var pathArr = path.split(',');\n      var obj = opt;\n\n      for (var i = 0; i < pathArr.length; i++) {\n        obj = obj && obj[pathArr[i]];\n\n        if (obj == null) {\n          break;\n        }\n      }\n\n      return obj;\n    }\n\n    function set$1(opt, path, val, overwrite) {\n      var pathArr = path.split(',');\n      var obj = opt;\n      var key;\n      var i = 0;\n\n      for (; i < pathArr.length - 1; i++) {\n        key = pathArr[i];\n\n        if (obj[key] == null) {\n          obj[key] = {};\n        }\n\n        obj = obj[key];\n      }\n\n      if (overwrite || obj[pathArr[i]] == null) {\n        obj[pathArr[i]] = val;\n      }\n    }\n\n    function compatLayoutProperties(option) {\n      option && each(LAYOUT_PROPERTIES, function (prop) {\n        if (prop[0] in option && !(prop[1] in option)) {\n          option[prop[1]] = option[prop[0]];\n        }\n      });\n    }\n\n    var LAYOUT_PROPERTIES = [['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']];\n    var COMPATITABLE_COMPONENTS = ['grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'];\n    var BAR_ITEM_STYLE_MAP = [['borderRadius', 'barBorderRadius'], ['borderColor', 'barBorderColor'], ['borderWidth', 'barBorderWidth']];\n\n    function compatBarItemStyle(option) {\n      var itemStyle = option && option.itemStyle;\n\n      if (itemStyle) {\n        for (var i = 0; i < BAR_ITEM_STYLE_MAP.length; i++) {\n          var oldName = BAR_ITEM_STYLE_MAP[i][1];\n          var newName = BAR_ITEM_STYLE_MAP[i][0];\n\n          if (itemStyle[oldName] != null) {\n            itemStyle[newName] = itemStyle[oldName];\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog(oldName, newName);\n            }\n          }\n        }\n      }\n    }\n\n    function compatPieLabel(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.alignTo === 'edge' && option.margin != null && option.edgeDistance == null) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('label.margin', 'label.edgeDistance', 'pie');\n        }\n\n        option.edgeDistance = option.margin;\n      }\n    }\n\n    function compatSunburstState(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.downplay && !option.blur) {\n        option.blur = option.downplay;\n\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('downplay', 'blur', 'sunburst');\n        }\n      }\n    }\n\n    function compatGraphFocus(option) {\n      if (!option) {\n        return;\n      }\n\n      if (option.focusNodeAdjacency != null) {\n        option.emphasis = option.emphasis || {};\n\n        if (option.emphasis.focus == null) {\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog('focusNodeAdjacency', 'emphasis: { focus: \\'adjacency\\'}', 'graph/sankey');\n          }\n\n          option.emphasis.focus = 'adjacency';\n        }\n      }\n    }\n\n    function traverseTree(data, cb) {\n      if (data) {\n        for (var i = 0; i < data.length; i++) {\n          cb(data[i]);\n          data[i] && traverseTree(data[i].children, cb);\n        }\n      }\n    }\n\n    function globalBackwardCompat(option, isTheme) {\n      globalCompatStyle(option, isTheme); // Make sure series array for model initialization.\n\n      option.series = normalizeToArray(option.series);\n      each(option.series, function (seriesOpt) {\n        if (!isObject(seriesOpt)) {\n          return;\n        }\n\n        var seriesType = seriesOpt.type;\n\n        if (seriesType === 'line') {\n          if (seriesOpt.clipOverflow != null) {\n            seriesOpt.clip = seriesOpt.clipOverflow;\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('clipOverflow', 'clip', 'line');\n            }\n          }\n        } else if (seriesType === 'pie' || seriesType === 'gauge') {\n          if (seriesOpt.clockWise != null) {\n            seriesOpt.clockwise = seriesOpt.clockWise;\n\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('clockWise', 'clockwise');\n            }\n          }\n\n          compatPieLabel(seriesOpt.label);\n          var data = seriesOpt.data;\n\n          if (data && !isTypedArray(data)) {\n            for (var i = 0; i < data.length; i++) {\n              compatPieLabel(data[i]);\n            }\n          }\n\n          if (seriesOpt.hoverOffset != null) {\n            seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n            if (seriesOpt.emphasis.scaleSize = null) {\n              if (\"development\" !== 'production') {\n                deprecateReplaceLog('hoverOffset', 'emphasis.scaleSize');\n              }\n\n              seriesOpt.emphasis.scaleSize = seriesOpt.hoverOffset;\n            }\n          }\n        } else if (seriesType === 'gauge') {\n          var pointerColor = get(seriesOpt, 'pointer.color');\n          pointerColor != null && set$1(seriesOpt, 'itemStyle.color', pointerColor);\n        } else if (seriesType === 'bar') {\n          compatBarItemStyle(seriesOpt);\n          compatBarItemStyle(seriesOpt.backgroundStyle);\n          compatBarItemStyle(seriesOpt.emphasis);\n          var data = seriesOpt.data;\n\n          if (data && !isTypedArray(data)) {\n            for (var i = 0; i < data.length; i++) {\n              if (typeof data[i] === 'object') {\n                compatBarItemStyle(data[i]);\n                compatBarItemStyle(data[i] && data[i].emphasis);\n              }\n            }\n          }\n        } else if (seriesType === 'sunburst') {\n          var highlightPolicy = seriesOpt.highlightPolicy;\n\n          if (highlightPolicy) {\n            seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n            if (!seriesOpt.emphasis.focus) {\n              seriesOpt.emphasis.focus = highlightPolicy;\n\n              if (\"development\" !== 'production') {\n                deprecateReplaceLog('highlightPolicy', 'emphasis.focus', 'sunburst');\n              }\n            }\n          }\n\n          compatSunburstState(seriesOpt);\n          traverseTree(seriesOpt.data, compatSunburstState);\n        } else if (seriesType === 'graph' || seriesType === 'sankey') {\n          compatGraphFocus(seriesOpt); // TODO nodes, edges?\n        } else if (seriesType === 'map') {\n          if (seriesOpt.mapType && !seriesOpt.map) {\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('mapType', 'map', 'map');\n            }\n\n            seriesOpt.map = seriesOpt.mapType;\n          }\n\n          if (seriesOpt.mapLocation) {\n            if (\"development\" !== 'production') {\n              deprecateLog('`mapLocation` is not used anymore.');\n            }\n\n            defaults(seriesOpt, seriesOpt.mapLocation);\n          }\n        }\n\n        if (seriesOpt.hoverAnimation != null) {\n          seriesOpt.emphasis = seriesOpt.emphasis || {};\n\n          if (seriesOpt.emphasis && seriesOpt.emphasis.scale == null) {\n            if (\"development\" !== 'production') {\n              deprecateReplaceLog('hoverAnimation', 'emphasis.scale');\n            }\n\n            seriesOpt.emphasis.scale = seriesOpt.hoverAnimation;\n          }\n        }\n\n        compatLayoutProperties(seriesOpt);\n      }); // dataRange has changed to visualMap\n\n      if (option.dataRange) {\n        option.visualMap = option.dataRange;\n      }\n\n      each(COMPATITABLE_COMPONENTS, function (componentName) {\n        var options = option[componentName];\n\n        if (options) {\n          if (!isArray(options)) {\n            options = [options];\n          }\n\n          each(options, function (option) {\n            compatLayoutProperties(option);\n          });\n        }\n      });\n    }\n\n    //     data processing stage is blocked in stream.\n    //     See <module:echarts/stream/Scheduler#performDataProcessorTasks>\n    // (2) Only register once when import repeatedly.\n    //     Should be executed after series is filtered and before stack calculation.\n\n    function dataStack(ecModel) {\n      var stackInfoMap = createHashMap();\n      ecModel.eachSeries(function (seriesModel) {\n        var stack = seriesModel.get('stack'); // Compatible: when `stack` is set as '', do not stack.\n\n        if (stack) {\n          var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []);\n          var data = seriesModel.getData();\n          var stackInfo = {\n            // Used for calculate axis extent automatically.\n            // TODO: Type getCalculationInfo return more specific type?\n            stackResultDimension: data.getCalculationInfo('stackResultDimension'),\n            stackedOverDimension: data.getCalculationInfo('stackedOverDimension'),\n            stackedDimension: data.getCalculationInfo('stackedDimension'),\n            stackedByDimension: data.getCalculationInfo('stackedByDimension'),\n            isStackedByIndex: data.getCalculationInfo('isStackedByIndex'),\n            data: data,\n            seriesModel: seriesModel\n          }; // If stacked on axis that do not support data stack.\n\n          if (!stackInfo.stackedDimension || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)) {\n            return;\n          }\n\n          stackInfoList.length && data.setCalculationInfo('stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel);\n          stackInfoList.push(stackInfo);\n        }\n      });\n      stackInfoMap.each(calculateStack);\n    }\n\n    function calculateStack(stackInfoList) {\n      each(stackInfoList, function (targetStackInfo, idxInStack) {\n        var resultVal = [];\n        var resultNaN = [NaN, NaN];\n        var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension];\n        var targetData = targetStackInfo.data;\n        var isStackedByIndex = targetStackInfo.isStackedByIndex;\n        var stackStrategy = targetStackInfo.seriesModel.get('stackStrategy') || 'samesign'; // Should not write on raw data, because stack series model list changes\n        // depending on legend selection.\n\n        targetData.modify(dims, function (v0, v1, dataIndex) {\n          var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); // Consider `connectNulls` of line area, if value is NaN, stackedOver\n          // should also be NaN, to draw a appropriate belt area.\n\n          if (isNaN(sum)) {\n            return resultNaN;\n          }\n\n          var byValue;\n          var stackedDataRawIndex;\n\n          if (isStackedByIndex) {\n            stackedDataRawIndex = targetData.getRawIndex(dataIndex);\n          } else {\n            byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex);\n          } // If stackOver is NaN, chart view will render point on value start.\n\n\n          var stackedOver = NaN;\n\n          for (var j = idxInStack - 1; j >= 0; j--) {\n            var stackInfo = stackInfoList[j]; // Has been optimized by inverted indices on `stackedByDimension`.\n\n            if (!isStackedByIndex) {\n              stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue);\n            }\n\n            if (stackedDataRawIndex >= 0) {\n              var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); // Considering positive stack, negative stack and empty data\n\n              if (stackStrategy === 'all' // single stack group\n              || stackStrategy === 'positive' && val > 0 || stackStrategy === 'negative' && val < 0 || stackStrategy === 'samesign' && sum >= 0 && val > 0 // All positive stack\n              || stackStrategy === 'samesign' && sum <= 0 && val < 0 // All negative stack\n              ) {\n                  // The sum has to be very small to be affected by the\n                  // floating arithmetic problem. An incorrect result will probably\n                  // cause axis min/max to be filtered incorrectly.\n                  sum = addSafe(sum, val);\n                  stackedOver = val;\n                  break;\n                }\n            }\n          }\n\n          resultVal[0] = sum;\n          resultVal[1] = stackedOver;\n          return resultVal;\n        });\n      });\n    }\n\n    var SourceImpl =\n    /** @class */\n    function () {\n      function SourceImpl(fields) {\n        this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []);\n        this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; // Visit config\n\n        this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;\n        this.startIndex = fields.startIndex || 0;\n        this.dimensionsDetectedCount = fields.dimensionsDetectedCount;\n        this.metaRawOption = fields.metaRawOption;\n        var dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine;\n\n        if (dimensionsDefine) {\n          for (var i = 0; i < dimensionsDefine.length; i++) {\n            var dim = dimensionsDefine[i];\n\n            if (dim.type == null) {\n              if (guessOrdinal(this, i) === BE_ORDINAL.Must) {\n                dim.type = 'ordinal';\n              }\n            }\n          }\n        }\n      }\n\n      return SourceImpl;\n    }();\n\n    function isSourceInstance(val) {\n      return val instanceof SourceImpl;\n    }\n    /**\n     * Create a source from option.\n     * NOTE: Created source is immutable. Don't change any properties in it.\n     */\n\n    function createSource(sourceData, thisMetaRawOption, // can be null. If not provided, auto detect it from `sourceData`.\n    sourceFormat) {\n      sourceFormat = sourceFormat || detectSourceFormat(sourceData);\n      var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy;\n      var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions);\n      var source = new SourceImpl({\n        data: sourceData,\n        sourceFormat: sourceFormat,\n        seriesLayoutBy: seriesLayoutBy,\n        dimensionsDefine: determined.dimensionsDefine,\n        startIndex: determined.startIndex,\n        dimensionsDetectedCount: determined.dimensionsDetectedCount,\n        metaRawOption: clone(thisMetaRawOption)\n      });\n      return source;\n    }\n    /**\n     * Wrap original series data for some compatibility cases.\n     */\n\n    function createSourceFromSeriesDataOption(data) {\n      return new SourceImpl({\n        data: data,\n        sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL\n      });\n    }\n    /**\n     * Clone source but excludes source data.\n     */\n\n    function cloneSourceShallow(source) {\n      return new SourceImpl({\n        data: source.data,\n        sourceFormat: source.sourceFormat,\n        seriesLayoutBy: source.seriesLayoutBy,\n        dimensionsDefine: clone(source.dimensionsDefine),\n        startIndex: source.startIndex,\n        dimensionsDetectedCount: source.dimensionsDetectedCount\n      });\n    }\n    /**\n     * Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`.\n     */\n\n    function detectSourceFormat(data) {\n      var sourceFormat = SOURCE_FORMAT_UNKNOWN;\n\n      if (isTypedArray(data)) {\n        sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;\n      } else if (isArray(data)) {\n        // FIXME Whether tolerate null in top level array?\n        if (data.length === 0) {\n          sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n        }\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          var item = data[i];\n\n          if (item == null) {\n            continue;\n          } else if (isArray(item)) {\n            sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;\n            break;\n          } else if (isObject(item)) {\n            sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;\n            break;\n          }\n        }\n      } else if (isObject(data)) {\n        for (var key in data) {\n          if (hasOwn(data, key) && isArrayLike(data[key])) {\n            sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;\n            break;\n          }\n        }\n      }\n\n      return sourceFormat;\n    }\n    /**\n     * Determine the source definitions from data standalone dimensions definitions\n     * are not specified.\n     */\n\n    function determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, // standalone raw dimensions definition, like:\n    // {\n    //     dimensions: ['aa', 'bb', { name: 'cc', type: 'time' }]\n    // }\n    // in `dataset` or `series`\n    dimensionsDefine) {\n      var dimensionsDetectedCount;\n      var startIndex; // PENDING: Could data be null/undefined here?\n      // currently, if `dataset.source` not specified, error thrown.\n      // if `series.data` not specified, nothing rendered without error thrown.\n      // Should test these cases.\n\n      if (!data) {\n        return {\n          dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n          startIndex: startIndex,\n          dimensionsDetectedCount: dimensionsDetectedCount\n        };\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var dataArrayRows = data; // Rule: Most of the first line are string: it is header.\n        // Caution: consider a line with 5 string and 1 number,\n        // it still can not be sure it is a head, because the\n        // 5 string may be 5 values of category columns.\n\n        if (sourceHeader === 'auto' || sourceHeader == null) {\n          arrayRowsTravelFirst(function (val) {\n            // '-' is regarded as null/undefined.\n            if (val != null && val !== '-') {\n              if (isString(val)) {\n                startIndex == null && (startIndex = 1);\n              } else {\n                startIndex = 0;\n              }\n            } // 10 is an experience number, avoid long loop.\n\n          }, seriesLayoutBy, dataArrayRows, 10);\n        } else {\n          startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0;\n        }\n\n        if (!dimensionsDefine && startIndex === 1) {\n          dimensionsDefine = [];\n          arrayRowsTravelFirst(function (val, index) {\n            dimensionsDefine[index] = val != null ? val + '' : '';\n          }, seriesLayoutBy, dataArrayRows, Infinity);\n        }\n\n        dimensionsDetectedCount = dimensionsDefine ? dimensionsDefine.length : seriesLayoutBy === SERIES_LAYOUT_BY_ROW ? dataArrayRows.length : dataArrayRows[0] ? dataArrayRows[0].length : null;\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        if (!dimensionsDefine) {\n          dimensionsDefine = objectRowsCollectDimensions(data);\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {\n        if (!dimensionsDefine) {\n          dimensionsDefine = [];\n          each(data, function (colArr, key) {\n            dimensionsDefine.push(key);\n          });\n        }\n      } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var value0 = getDataItemValue(data[0]);\n        dimensionsDetectedCount = isArray(value0) && value0.length || 1;\n      } else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n        if (\"development\" !== 'production') {\n          assert(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.');\n        }\n      }\n\n      return {\n        startIndex: startIndex,\n        dimensionsDefine: normalizeDimensionsOption(dimensionsDefine),\n        dimensionsDetectedCount: dimensionsDetectedCount\n      };\n    }\n\n    function objectRowsCollectDimensions(data) {\n      var firstIndex = 0;\n      var obj;\n\n      while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line\n\n\n      if (obj) {\n        var dimensions_1 = [];\n        each(obj, function (value, key) {\n          dimensions_1.push(key);\n        });\n        return dimensions_1;\n      }\n    } // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],\n    // which is reasonable. But dimension name is duplicated.\n    // Returns undefined or an array contains only object without null/undefined or string.\n\n\n    function normalizeDimensionsOption(dimensionsDefine) {\n      if (!dimensionsDefine) {\n        // The meaning of null/undefined is different from empty array.\n        return;\n      }\n\n      var nameMap = createHashMap();\n      return map(dimensionsDefine, function (rawItem, index) {\n        rawItem = isObject(rawItem) ? rawItem : {\n          name: rawItem\n        }; // Other fields will be discarded.\n\n        var item = {\n          name: rawItem.name,\n          displayName: rawItem.displayName,\n          type: rawItem.type\n        }; // User can set null in dimensions.\n        // We don't auto specify name, otherwise a given name may\n        // cause it to be referred unexpectedly.\n\n        if (item.name == null) {\n          return item;\n        } // Also consider number form like 2012.\n\n\n        item.name += ''; // User may also specify displayName.\n        // displayName will always exists except user not\n        // specified or dim name is not specified or detected.\n        // (A auto generated dim name will not be used as\n        // displayName).\n\n        if (item.displayName == null) {\n          item.displayName = item.name;\n        }\n\n        var exist = nameMap.get(item.name);\n\n        if (!exist) {\n          nameMap.set(item.name, {\n            count: 1\n          });\n        } else {\n          item.name += '-' + exist.count++;\n        }\n\n        return item;\n      });\n    }\n\n    function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {\n      if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {\n        for (var i = 0; i < data.length && i < maxLoop; i++) {\n          cb(data[i] ? data[i][0] : null, i);\n        }\n      } else {\n        var value0 = data[0] || [];\n\n        for (var i = 0; i < value0.length && i < maxLoop; i++) {\n          cb(value0[i], i);\n        }\n      }\n    }\n\n    function shouldRetrieveDataByName(source) {\n      var sourceFormat = source.sourceFormat;\n      return sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var _a, _b, _c; // TODO\n    var providerMethods;\n    var mountMethods;\n    /**\n     * If normal array used, mutable chunk size is supported.\n     * If typed array used, chunk size must be fixed.\n     */\n\n    var DefaultDataProvider =\n    /** @class */\n    function () {\n      function DefaultDataProvider(sourceParam, dimSize) {\n        // let source: Source;\n        var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam; // declare source is Source;\n\n        this._source = source;\n        var data = this._data = source.data; // Typed array. TODO IE10+?\n\n        if (source.sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n          if (\"development\" !== 'production') {\n            if (dimSize == null) {\n              throw new Error('Typed array data must specify dimension size');\n            }\n          }\n\n          this._offset = 0;\n          this._dimSize = dimSize;\n          this._data = data;\n        }\n\n        mountMethods(this, data, source);\n      }\n\n      DefaultDataProvider.prototype.getSource = function () {\n        return this._source;\n      };\n\n      DefaultDataProvider.prototype.count = function () {\n        return 0;\n      };\n\n      DefaultDataProvider.prototype.getItem = function (idx, out) {\n        return;\n      };\n\n      DefaultDataProvider.prototype.appendData = function (newData) {};\n\n      DefaultDataProvider.prototype.clean = function () {};\n\n      DefaultDataProvider.protoInitialize = function () {\n        // PENDING: To avoid potential incompat (e.g., prototype\n        // is visited somewhere), still init them on prototype.\n        var proto = DefaultDataProvider.prototype;\n        proto.pure = false;\n        proto.persistent = true;\n      }();\n\n      DefaultDataProvider.internalField = function () {\n        var _a;\n\n        mountMethods = function (provider, data, source) {\n          var sourceFormat = source.sourceFormat;\n          var seriesLayoutBy = source.seriesLayoutBy;\n          var startIndex = source.startIndex;\n          var dimsDef = source.dimensionsDefine;\n          var methods = providerMethods[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n          if (\"development\" !== 'production') {\n            assert(methods, 'Invalide sourceFormat: ' + sourceFormat);\n          }\n\n          extend(provider, methods);\n\n          if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {\n            provider.getItem = getItemForTypedArray;\n            provider.count = countForTypedArray;\n            provider.fillStorage = fillStorageForTypedArray;\n          } else {\n            var rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy);\n            provider.getItem = bind(rawItemGetter, null, data, startIndex, dimsDef);\n            var rawCounter = getRawSourceDataCounter(sourceFormat, seriesLayoutBy);\n            provider.count = bind(rawCounter, null, data, startIndex, dimsDef);\n          }\n        };\n\n        var getItemForTypedArray = function (idx, out) {\n          idx = idx - this._offset;\n          out = out || [];\n          var data = this._data;\n          var dimSize = this._dimSize;\n          var offset = dimSize * idx;\n\n          for (var i = 0; i < dimSize; i++) {\n            out[i] = data[offset + i];\n          }\n\n          return out;\n        };\n\n        var fillStorageForTypedArray = function (start, end, storage, extent) {\n          var data = this._data;\n          var dimSize = this._dimSize;\n\n          for (var dim = 0; dim < dimSize; dim++) {\n            var dimExtent = extent[dim];\n            var min = dimExtent[0] == null ? Infinity : dimExtent[0];\n            var max = dimExtent[1] == null ? -Infinity : dimExtent[1];\n            var count = end - start;\n            var arr = storage[dim];\n\n            for (var i = 0; i < count; i++) {\n              // appendData with TypedArray will always do replace in provider.\n              var val = data[i * dimSize + dim];\n              arr[start + i] = val;\n              val < min && (min = val);\n              val > max && (max = val);\n            }\n\n            dimExtent[0] = min;\n            dimExtent[1] = max;\n          }\n        };\n\n        var countForTypedArray = function () {\n          return this._data ? this._data.length / this._dimSize : 0;\n        };\n\n        providerMethods = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = {\n          pure: true,\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = {\n          pure: true,\n          appendData: function () {\n            throw new Error('Do not support appendData when set seriesLayoutBy: \"row\".');\n          }\n        }, _a[SOURCE_FORMAT_OBJECT_ROWS] = {\n          pure: true,\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_KEYED_COLUMNS] = {\n          pure: true,\n          appendData: function (newData) {\n            var data = this._data;\n            each(newData, function (newCol, key) {\n              var oldCol = data[key] || (data[key] = []);\n\n              for (var i = 0; i < (newCol || []).length; i++) {\n                oldCol.push(newCol[i]);\n              }\n            });\n          }\n        }, _a[SOURCE_FORMAT_ORIGINAL] = {\n          appendData: appendDataSimply\n        }, _a[SOURCE_FORMAT_TYPED_ARRAY] = {\n          persistent: false,\n          pure: true,\n          appendData: function (newData) {\n            if (\"development\" !== 'production') {\n              assert(isTypedArray(newData), 'Added data must be TypedArray if data in initialization is TypedArray');\n            }\n\n            this._data = newData;\n          },\n          // Clean self if data is already used.\n          clean: function () {\n            // PENDING\n            this._offset += this.count();\n            this._data = null;\n          }\n        }, _a);\n\n        function appendDataSimply(newData) {\n          for (var i = 0; i < newData.length; i++) {\n            this._data.push(newData[i]);\n          }\n        }\n      }();\n\n      return DefaultDataProvider;\n    }();\n\n    var getItemSimply = function (rawData, startIndex, dimsDef, idx) {\n      return rawData[idx];\n    };\n\n    var rawSourceItemGetterMap = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef, idx) {\n      return rawData[idx + startIndex];\n    }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx, out) {\n      idx += startIndex;\n      var item = out || [];\n      var data = rawData;\n\n      for (var i = 0; i < data.length; i++) {\n        var row = data[i];\n        item[i] = row ? row[idx] : null;\n      }\n\n      return item;\n    }, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx, out) {\n      var item = out || [];\n\n      for (var i = 0; i < dimsDef.length; i++) {\n        var dimName = dimsDef[i].name;\n\n        if (\"development\" !== 'production') {\n          if (dimName == null) {\n            throw new Error();\n          }\n        }\n\n        var col = rawData[dimName];\n        item[i] = col ? col[idx] : null;\n      }\n\n      return item;\n    }, _a[SOURCE_FORMAT_ORIGINAL] = getItemSimply, _a);\n    function getRawSourceItemGetter(sourceFormat, seriesLayoutBy) {\n      var method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support get item on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n      }\n\n      return method;\n    }\n\n    var countSimply = function (rawData, startIndex, dimsDef) {\n      return rawData.length;\n    };\n\n    var rawSourceDataCounterMap = (_b = {}, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef) {\n      return Math.max(0, rawData.length - startIndex);\n    }, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef) {\n      var row = rawData[0];\n      return row ? Math.max(0, row.length - startIndex) : 0;\n    }, _b[SOURCE_FORMAT_OBJECT_ROWS] = countSimply, _b[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef) {\n      var dimName = dimsDef[0].name;\n\n      if (\"development\" !== 'production') {\n        if (dimName == null) {\n          throw new Error();\n        }\n      }\n\n      var col = rawData[dimName];\n      return col ? col.length : 0;\n    }, _b[SOURCE_FORMAT_ORIGINAL] = countSimply, _b);\n    function getRawSourceDataCounter(sourceFormat, seriesLayoutBy) {\n      var method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support count on \"' + sourceFormat + '\", \"' + seriesLayoutBy + '\".');\n      }\n\n      return method;\n    }\n\n    var getRawValueSimply = function (dataItem, dimIndex, property) {\n      return dataItem[dimIndex];\n    };\n\n    var rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, property) {\n      return dataItem[property];\n    }, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, property) {\n      // FIXME: In some case (markpoint in geo (geo-map.html)),\n      // dataItem is {coord: [...]}\n      var value = getDataItemValue(dataItem);\n      return !(value instanceof Array) ? value : value[dimIndex];\n    }, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c);\n    function getRawSourceValueGetter(sourceFormat) {\n      var method = rawSourceValueGetterMap[sourceFormat];\n\n      if (\"development\" !== 'production') {\n        assert(method, 'Do not support get value on \"' + sourceFormat + '\".');\n      }\n\n      return method;\n    }\n\n    function getMethodMapKey(sourceFormat, seriesLayoutBy) {\n      return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + seriesLayoutBy : sourceFormat;\n    } // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,\n    // Consider persistent.\n    // Caution: why use raw value to display on label or tooltip?\n    // A reason is to avoid format. For example time value we do not know\n    // how to format is expected. More over, if stack is used, calculated\n    // value may be 0.91000000001, which have brings trouble to display.\n    // TODO: consider how to treat null/undefined/NaN when display?\n\n\n    function retrieveRawValue(data, dataIndex, // If dimIndex is null/undefined, return OptionDataItem.\n    // Otherwise, return OptionDataValue.\n    dim) {\n      if (!data) {\n        return;\n      } // Consider data may be not persistent.\n\n\n      var dataItem = data.getRawDataItem(dataIndex);\n\n      if (dataItem == null) {\n        return;\n      }\n\n      var store = data.getStore();\n      var sourceFormat = store.getSource().sourceFormat;\n\n      if (dim != null) {\n        var dimIndex = data.getDimensionIndex(dim);\n        var property = store.getDimensionProperty(dimIndex);\n        return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, property);\n      } else {\n        var result = dataItem;\n\n        if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n          result = getDataItemValue(dataItem);\n        }\n\n        return result;\n      }\n    }\n\n    var DIMENSION_LABEL_REG = /\\{@(.+?)\\}/g;\n\n    var DataFormatMixin =\n    /** @class */\n    function () {\n      function DataFormatMixin() {}\n      /**\n       * Get params for formatter\n       */\n\n\n      DataFormatMixin.prototype.getDataParams = function (dataIndex, dataType) {\n        var data = this.getData(dataType);\n        var rawValue = this.getRawValue(dataIndex, dataType);\n        var rawDataIndex = data.getRawIndex(dataIndex);\n        var name = data.getName(dataIndex);\n        var itemOpt = data.getRawDataItem(dataIndex);\n        var style = data.getItemVisual(dataIndex, 'style');\n        var color = style && style[data.getItemVisual(dataIndex, 'drawType') || 'fill'];\n        var borderColor = style && style.stroke;\n        var mainType = this.mainType;\n        var isSeries = mainType === 'series';\n        var userOutput = data.userOutput && data.userOutput.get();\n        return {\n          componentType: mainType,\n          componentSubType: this.subType,\n          componentIndex: this.componentIndex,\n          seriesType: isSeries ? this.subType : null,\n          seriesIndex: this.seriesIndex,\n          seriesId: isSeries ? this.id : null,\n          seriesName: isSeries ? this.name : null,\n          name: name,\n          dataIndex: rawDataIndex,\n          data: itemOpt,\n          dataType: dataType,\n          value: rawValue,\n          color: color,\n          borderColor: borderColor,\n          dimensionNames: userOutput ? userOutput.fullDimensions : null,\n          encode: userOutput ? userOutput.encode : null,\n          // Param name list for mapping `a`, `b`, `c`, `d`, `e`\n          $vars: ['seriesName', 'name', 'value']\n        };\n      };\n      /**\n       * Format label\n       * @param dataIndex\n       * @param status 'normal' by default\n       * @param dataType\n       * @param labelDimIndex Only used in some chart that\n       *        use formatter in different dimensions, like radar.\n       * @param formatter Formatter given outside.\n       * @return return null/undefined if no formatter\n       */\n\n\n      DataFormatMixin.prototype.getFormattedLabel = function (dataIndex, status, dataType, labelDimIndex, formatter, extendParams) {\n        status = status || 'normal';\n        var data = this.getData(dataType);\n        var params = this.getDataParams(dataIndex, dataType);\n\n        if (extendParams) {\n          params.value = extendParams.interpolatedValue;\n        }\n\n        if (labelDimIndex != null && isArray(params.value)) {\n          params.value = params.value[labelDimIndex];\n        }\n\n        if (!formatter) {\n          var itemModel = data.getItemModel(dataIndex); // @ts-ignore\n\n          formatter = itemModel.get(status === 'normal' ? ['label', 'formatter'] : [status, 'label', 'formatter']);\n        }\n\n        if (isFunction(formatter)) {\n          params.status = status;\n          params.dimensionIndex = labelDimIndex;\n          return formatter(params);\n        } else if (isString(formatter)) {\n          var str = formatTpl(formatter, params); // Support 'aaa{@[3]}bbb{@product}ccc'.\n          // Do not support '}' in dim name util have to.\n\n          return str.replace(DIMENSION_LABEL_REG, function (origin, dimStr) {\n            var len = dimStr.length;\n            var dimLoose = dimStr;\n\n            if (dimLoose.charAt(0) === '[' && dimLoose.charAt(len - 1) === ']') {\n              dimLoose = +dimLoose.slice(1, len - 1); // Also support: '[]' => 0\n\n              if (\"development\" !== 'production') {\n                if (isNaN(dimLoose)) {\n                  error(\"Invalide label formatter: @\" + dimStr + \", only support @[0], @[1], @[2], ...\");\n                }\n              }\n            }\n\n            var val = retrieveRawValue(data, dataIndex, dimLoose);\n\n            if (extendParams && isArray(extendParams.interpolatedValue)) {\n              var dimIndex = data.getDimensionIndex(dimLoose);\n\n              if (dimIndex >= 0) {\n                val = extendParams.interpolatedValue[dimIndex];\n              }\n            }\n\n            return val != null ? val + '' : '';\n          });\n        }\n      };\n      /**\n       * Get raw value in option\n       */\n\n\n      DataFormatMixin.prototype.getRawValue = function (idx, dataType) {\n        return retrieveRawValue(this.getData(dataType), idx);\n      };\n      /**\n       * Should be implemented.\n       * @param {number} dataIndex\n       * @param {boolean} [multipleSeries=false]\n       * @param {string} [dataType]\n       */\n\n\n      DataFormatMixin.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        // Empty function\n        return;\n      };\n\n      return DataFormatMixin;\n    }();\n\n    /**\n     * @param {Object} define\n     * @return See the return of `createTask`.\n     */\n\n    function createTask(define) {\n      return new Task(define);\n    }\n\n    var Task =\n    /** @class */\n    function () {\n      function Task(define) {\n        define = define || {};\n        this._reset = define.reset;\n        this._plan = define.plan;\n        this._count = define.count;\n        this._onDirty = define.onDirty;\n        this._dirty = true;\n      }\n      /**\n       * @param step Specified step.\n       * @param skip Skip customer perform call.\n       * @param modBy Sampling window size.\n       * @param modDataCount Sampling count.\n       * @return whether unfinished.\n       */\n\n\n      Task.prototype.perform = function (performArgs) {\n        var upTask = this._upstream;\n        var skip = performArgs && performArgs.skip; // TODO some refactor.\n        // Pull data. Must pull data each time, because context.data\n        // may be updated by Series.setData.\n\n        if (this._dirty && upTask) {\n          var context = this.context;\n          context.data = context.outputData = upTask.context.outputData;\n        }\n\n        if (this.__pipeline) {\n          this.__pipeline.currentTask = this;\n        }\n\n        var planResult;\n\n        if (this._plan && !skip) {\n          planResult = this._plan(this.context);\n        } // Support sharding by mod, which changes the render sequence and makes the rendered graphic\n        // elements uniformed distributed when progress, especially when moving or zooming.\n\n\n        var lastModBy = normalizeModBy(this._modBy);\n        var lastModDataCount = this._modDataCount || 0;\n        var modBy = normalizeModBy(performArgs && performArgs.modBy);\n        var modDataCount = performArgs && performArgs.modDataCount || 0;\n\n        if (lastModBy !== modBy || lastModDataCount !== modDataCount) {\n          planResult = 'reset';\n        }\n\n        function normalizeModBy(val) {\n          !(val >= 1) && (val = 1); // jshint ignore:line\n\n          return val;\n        }\n\n        var forceFirstProgress;\n\n        if (this._dirty || planResult === 'reset') {\n          this._dirty = false;\n          forceFirstProgress = this._doReset(skip);\n        }\n\n        this._modBy = modBy;\n        this._modDataCount = modDataCount;\n        var step = performArgs && performArgs.step;\n\n        if (upTask) {\n          if (\"development\" !== 'production') {\n            assert(upTask._outputDueEnd != null);\n          }\n\n          this._dueEnd = upTask._outputDueEnd;\n        } // DataTask or overallTask\n        else {\n            if (\"development\" !== 'production') {\n              assert(!this._progress || this._count);\n            }\n\n            this._dueEnd = this._count ? this._count(this.context) : Infinity;\n          } // Note: Stubs, that its host overall task let it has progress, has progress.\n        // If no progress, pass index from upstream to downstream each time plan called.\n\n\n        if (this._progress) {\n          var start = this._dueIndex;\n          var end = Math.min(step != null ? this._dueIndex + step : Infinity, this._dueEnd);\n\n          if (!skip && (forceFirstProgress || start < end)) {\n            var progress = this._progress;\n\n            if (isArray(progress)) {\n              for (var i = 0; i < progress.length; i++) {\n                this._doProgress(progress[i], start, end, modBy, modDataCount);\n              }\n            } else {\n              this._doProgress(progress, start, end, modBy, modDataCount);\n            }\n          }\n\n          this._dueIndex = end; // If no `outputDueEnd`, assume that output data and\n          // input data is the same, so use `dueIndex` as `outputDueEnd`.\n\n          var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end;\n\n          if (\"development\" !== 'production') {\n            // ??? Can not rollback.\n            assert(outputDueEnd >= this._outputDueEnd);\n          }\n\n          this._outputDueEnd = outputDueEnd;\n        } else {\n          // (1) Some overall task has no progress.\n          // (2) Stubs, that its host overall task do not let it has progress, has no progress.\n          // This should always be performed so it can be passed to downstream.\n          this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd;\n        }\n\n        return this.unfinished();\n      };\n\n      Task.prototype.dirty = function () {\n        this._dirty = true;\n        this._onDirty && this._onDirty(this.context);\n      };\n\n      Task.prototype._doProgress = function (progress, start, end, modBy, modDataCount) {\n        iterator.reset(start, end, modBy, modDataCount);\n        this._callingProgress = progress;\n\n        this._callingProgress({\n          start: start,\n          end: end,\n          count: end - start,\n          next: iterator.next\n        }, this.context);\n      };\n\n      Task.prototype._doReset = function (skip) {\n        this._dueIndex = this._outputDueEnd = this._dueEnd = 0;\n        this._settedOutputEnd = null;\n        var progress;\n        var forceFirstProgress;\n\n        if (!skip && this._reset) {\n          progress = this._reset(this.context);\n\n          if (progress && progress.progress) {\n            forceFirstProgress = progress.forceFirstProgress;\n            progress = progress.progress;\n          } // To simplify no progress checking, array must has item.\n\n\n          if (isArray(progress) && !progress.length) {\n            progress = null;\n          }\n        }\n\n        this._progress = progress;\n        this._modBy = this._modDataCount = null;\n        var downstream = this._downstream;\n        downstream && downstream.dirty();\n        return forceFirstProgress;\n      };\n\n      Task.prototype.unfinished = function () {\n        return this._progress && this._dueIndex < this._dueEnd;\n      };\n      /**\n       * @param downTask The downstream task.\n       * @return The downstream task.\n       */\n\n\n      Task.prototype.pipe = function (downTask) {\n        if (\"development\" !== 'production') {\n          assert(downTask && !downTask._disposed && downTask !== this);\n        } // If already downstream, do not dirty downTask.\n\n\n        if (this._downstream !== downTask || this._dirty) {\n          this._downstream = downTask;\n          downTask._upstream = this;\n          downTask.dirty();\n        }\n      };\n\n      Task.prototype.dispose = function () {\n        if (this._disposed) {\n          return;\n        }\n\n        this._upstream && (this._upstream._downstream = null);\n        this._downstream && (this._downstream._upstream = null);\n        this._dirty = false;\n        this._disposed = true;\n      };\n\n      Task.prototype.getUpstream = function () {\n        return this._upstream;\n      };\n\n      Task.prototype.getDownstream = function () {\n        return this._downstream;\n      };\n\n      Task.prototype.setOutputEnd = function (end) {\n        // This only happens in dataTask, dataZoom, map, currently.\n        // where dataZoom do not set end each time, but only set\n        // when reset. So we should record the set end, in case\n        // that the stub of dataZoom perform again and earse the\n        // set end by upstream.\n        this._outputDueEnd = this._settedOutputEnd = end;\n      };\n\n      return Task;\n    }();\n\n    var iterator = function () {\n      var end;\n      var current;\n      var modBy;\n      var modDataCount;\n      var winCount;\n      var it = {\n        reset: function (s, e, sStep, sCount) {\n          current = s;\n          end = e;\n          modBy = sStep;\n          modDataCount = sCount;\n          winCount = Math.ceil(modDataCount / modBy);\n          it.next = modBy > 1 && modDataCount > 0 ? modNext : sequentialNext;\n        }\n      };\n      return it;\n\n      function sequentialNext() {\n        return current < end ? current++ : null;\n      }\n\n      function modNext() {\n        var dataIndex = current % winCount * modBy + Math.ceil(current / winCount);\n        var result = current >= end ? null : dataIndex < modDataCount ? dataIndex // If modDataCount is smaller than data.count() (consider `appendData` case),\n        // Use normal linear rendering mode.\n        : current;\n        current++;\n        return result;\n      }\n    }(); // -----------------------------------------------------------------------------\n    // For stream debug (Should be commented out after used!)\n    // @usage: printTask(this, 'begin');\n    // @usage: printTask(this, null, {someExtraProp});\n    // @usage: Use `__idxInPipeline` as conditional breakpiont.\n    //\n    // window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void {\n    //     window.ecTaskUID == null && (window.ecTaskUID = 0);\n    //     task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);\n    //     task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);\n    //     let props = [];\n    //     if (task.__pipeline) {\n    //         let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;\n    //         props.push({text: '__idxInPipeline/total', value: val});\n    //     } else {\n    //         let stubCount = 0;\n    //         task.agentStubMap.each(() => stubCount++);\n    //         props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});\n    //     }\n    //     props.push({text: 'uid', value: task.uidDebug});\n    //     if (task.__pipeline) {\n    //         props.push({text: 'pipelineId', value: task.__pipeline.id});\n    //         task.agent && props.push(\n    //             {text: 'stubFor', value: task.agent.uidDebug}\n    //         );\n    //     }\n    //     props.push(\n    //         {text: 'dirty', value: task._dirty},\n    //         {text: 'dueIndex', value: task._dueIndex},\n    //         {text: 'dueEnd', value: task._dueEnd},\n    //         {text: 'outputDueEnd', value: task._outputDueEnd}\n    //     );\n    //     if (extra) {\n    //         Object.keys(extra).forEach(key => {\n    //             props.push({text: key, value: extra[key]});\n    //         });\n    //     }\n    //     let args = ['color: blue'];\n    //     let msg = `%c[${prefix || 'T'}] %c` + props.map(item => (\n    //         args.push('color: green', 'color: red'),\n    //         `${item.text}: %c${item.value}`\n    //     )).join('%c, ');\n    //     console.log.apply(console, [msg].concat(args));\n    //     // console.log(this);\n    // };\n    // window.printPipeline = function (task: any, prefix: string) {\n    //     const pipeline = task.__pipeline;\n    //     let currTask = pipeline.head;\n    //     while (currTask) {\n    //         window.printTask(currTask, prefix);\n    //         currTask = currTask._downstream;\n    //     }\n    // };\n    // window.showChain = function (chainHeadTask) {\n    //     var chain = [];\n    //     var task = chainHeadTask;\n    //     while (task) {\n    //         chain.push({\n    //             task: task,\n    //             up: task._upstream,\n    //             down: task._downstream,\n    //             idxInPipeline: task.__idxInPipeline\n    //         });\n    //         task = task._downstream;\n    //     }\n    //     return chain;\n    // };\n    // window.findTaskInChain = function (task, chainHeadTask) {\n    //     let chain = window.showChain(chainHeadTask);\n    //     let result = [];\n    //     for (let i = 0; i < chain.length; i++) {\n    //         let chainItem = chain[i];\n    //         if (chainItem.task === task) {\n    //             result.push(i);\n    //         }\n    //     }\n    //     return result;\n    // };\n    // window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) {\n    //     let chainA = window.showChain(chainHeadTaskA);\n    //     for (let i = 0; i < chainA.length; i++) {\n    //         console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB));\n    //     }\n    // };\n\n    /**\n     * Convert raw the value in to inner value in List.\n     *\n     * [Performance sensitive]\n     *\n     * [Caution]: this is the key logic of user value parser.\n     * For backward compatibility, do not modify it until you have to!\n     */\n\n    function parseDataValue(value, // For high performance, do not omit the second param.\n    opt) {\n      // Performance sensitive.\n      var dimType = opt && opt.type;\n\n      if (dimType === 'ordinal') {\n        // If given value is a category string\n        return value;\n      }\n\n      if (dimType === 'time' // spead up when using timestamp\n      && !isNumber(value) && value != null && value !== '-') {\n        value = +parseDate(value);\n      } // dimType defaults 'number'.\n      // If dimType is not ordinal and value is null or undefined or NaN or '-',\n      // parse to NaN.\n      // number-like string (like ' 123 ') can be converted to a number.\n      // where null/undefined or other string will be converted to NaN.\n\n\n      return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN\n      // If object, also parse to NaN\n      : +value;\n    }\n    var valueParserMap = createHashMap({\n      'number': function (val) {\n        // Do not use `numericToNumber` here. We have `numericToNumber` by default.\n        // Here the number parser can have loose rule:\n        // enable to cut suffix: \"120px\" => 120, \"14%\" => 14.\n        return parseFloat(val);\n      },\n      'time': function (val) {\n        // return timestamp.\n        return +parseDate(val);\n      },\n      'trim': function (val) {\n        return isString(val) ? trim(val) : val;\n      }\n    });\n\n    /**\n     * TODO: disable writable.\n     * This structure will be exposed to users.\n     */\n\n    var ExternalSource =\n    /** @class */\n    function () {\n      function ExternalSource() {}\n\n      ExternalSource.prototype.getRawData = function () {\n        // Only built-in transform available.\n        throw new Error('not supported');\n      };\n\n      ExternalSource.prototype.getRawDataItem = function (dataIndex) {\n        // Only built-in transform available.\n        throw new Error('not supported');\n      };\n\n      ExternalSource.prototype.cloneRawData = function () {\n        return;\n      };\n      /**\n       * @return If dimension not found, return null/undefined.\n       */\n\n\n      ExternalSource.prototype.getDimensionInfo = function (dim) {\n        return;\n      };\n      /**\n       * dimensions defined if and only if either:\n       * (a) dataset.dimensions are declared.\n       * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).\n       * If dimensions are defined, `dimensionInfoAll` is corresponding to\n       * the defined dimensions.\n       * Otherwise, `dimensionInfoAll` is determined by data columns.\n       * @return Always return an array (even empty array).\n       */\n\n\n      ExternalSource.prototype.cloneAllDimensionInfo = function () {\n        return;\n      };\n\n      ExternalSource.prototype.count = function () {\n        return;\n      };\n      /**\n       * Only support by dimension index.\n       * No need to support by dimension name in transform function,\n       * because transform function is not case-specific, no need to use name literally.\n       */\n\n\n      ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {\n        return;\n      };\n\n      ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {\n        return;\n      };\n\n      ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {\n        return parseDataValue(rawVal, dimInfo);\n      };\n\n      return ExternalSource;\n    }();\n\n    function createExternalSource(internalSource, externalTransform) {\n      var extSource = new ExternalSource();\n      var data = internalSource.data;\n      var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;\n      var sourceHeaderCount = internalSource.startIndex;\n      var errMsg = '';\n\n      if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {\n        // For the logic simplicity in transformer, only 'culumn' is\n        // supported in data transform. Otherwise, the `dimensionsDefine`\n        // might be detected by 'row', which probably confuses users.\n        if (\"development\" !== 'production') {\n          errMsg = '`seriesLayoutBy` of upstream dataset can only be \"column\" in data transform.';\n        }\n\n        throwError(errMsg);\n      } // [MEMO]\n      // Create a new dimensions structure for exposing.\n      // Do not expose all dimension info to users directly.\n      // Because the dimension is probably auto detected from data and not might reliable.\n      // Should not lead the transformers to think that is reliable and return it.\n      // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n\n\n      var dimensions = [];\n      var dimsByName = {};\n      var dimsDef = internalSource.dimensionsDefine;\n\n      if (dimsDef) {\n        each(dimsDef, function (dimDef, idx) {\n          var name = dimDef.name;\n          var dimDefExt = {\n            index: idx,\n            name: name,\n            displayName: dimDef.displayName\n          };\n          dimensions.push(dimDefExt); // Users probably do not specify dimension name. For simplicity, data transform\n          // does not generate dimension name.\n\n          if (name != null) {\n            // Dimension name should not be duplicated.\n            // For simplicity, data transform forbids name duplication, do not generate\n            // new name like module `completeDimensions.ts` did, but just tell users.\n            var errMsg_1 = '';\n\n            if (hasOwn(dimsByName, name)) {\n              if (\"development\" !== 'production') {\n                errMsg_1 = 'dimension name \"' + name + '\" duplicated.';\n              }\n\n              throwError(errMsg_1);\n            }\n\n            dimsByName[name] = dimDefExt;\n          }\n        });\n      } // If dimension definitions are not defined and can not be detected.\n      // e.g., pure data `[[11, 22], ...]`.\n      else {\n          for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {\n            // Do not generete name or anything others. The consequence process in\n            // `transform` or `series` probably have there own name generation strategry.\n            dimensions.push({\n              index: i\n            });\n          }\n        } // Implement public methods:\n\n\n      var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n\n      if (externalTransform.__isBuiltIn) {\n        extSource.getRawDataItem = function (dataIndex) {\n          return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n        };\n\n        extSource.getRawData = bind(getRawData, null, internalSource);\n      }\n\n      extSource.cloneRawData = bind(cloneRawData, null, internalSource);\n      var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);\n      extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);\n      var rawValueGetter = getRawSourceValueGetter(sourceFormat);\n\n      extSource.retrieveValue = function (dataIndex, dimIndex) {\n        var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);\n        return retrieveValueFromItem(rawItem, dimIndex);\n      };\n\n      var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {\n        if (dataItem == null) {\n          return;\n        }\n\n        var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item.\n\n        if (dimDef) {\n          return rawValueGetter(dataItem, dimIndex, dimDef.name);\n        }\n      };\n\n      extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);\n      extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);\n      return extSource;\n    }\n\n    function getRawData(upstream) {\n      var sourceFormat = upstream.sourceFormat;\n\n      if (!isSupportedSourceFormat(sourceFormat)) {\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '`getRawData` is not supported in source format ' + sourceFormat;\n        }\n\n        throwError(errMsg);\n      }\n\n      return upstream.data;\n    }\n\n    function cloneRawData(upstream) {\n      var sourceFormat = upstream.sourceFormat;\n      var data = upstream.data;\n\n      if (!isSupportedSourceFormat(sourceFormat)) {\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;\n        }\n\n        throwError(errMsg);\n      }\n\n      if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {\n        var result = [];\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          // Not strictly clone for performance\n          result.push(data[i].slice());\n        }\n\n        return result;\n      } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {\n        var result = [];\n\n        for (var i = 0, len = data.length; i < len; i++) {\n          // Not strictly clone for performance\n          result.push(extend({}, data[i]));\n        }\n\n        return result;\n      }\n    }\n\n    function getDimensionInfo(dimensions, dimsByName, dim) {\n      if (dim == null) {\n        return;\n      } // Keep the same logic as `List::getDimension` did.\n\n\n      if (isNumber(dim) // If being a number-like string but not being defined a dimension name.\n      || !isNaN(dim) && !hasOwn(dimsByName, dim)) {\n        return dimensions[dim];\n      } else if (hasOwn(dimsByName, dim)) {\n        return dimsByName[dim];\n      }\n    }\n\n    function cloneAllDimensionInfo(dimensions) {\n      return clone(dimensions);\n    }\n\n    var externalTransformMap = createHashMap();\n    function registerExternalTransform(externalTransform) {\n      externalTransform = clone(externalTransform);\n      var type = externalTransform.type;\n      var errMsg = '';\n\n      if (!type) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Must have a `type` when `registerTransform`.';\n        }\n\n        throwError(errMsg);\n      }\n\n      var typeParsed = type.split(':');\n\n      if (typeParsed.length !== 2) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Name must include namespace like \"ns:regression\".';\n        }\n\n        throwError(errMsg);\n      } // Namespace 'echarts:xxx' is official namespace, where the transforms should\n      // be called directly via 'xxx' rather than 'echarts:xxx'.\n\n\n      var isBuiltIn = false;\n\n      if (typeParsed[0] === 'echarts') {\n        type = typeParsed[1];\n        isBuiltIn = true;\n      }\n\n      externalTransform.__isBuiltIn = isBuiltIn;\n      externalTransformMap.set(type, externalTransform);\n    }\n    function applyDataTransform(rawTransOption, sourceList, infoForPrint) {\n      var pipedTransOption = normalizeToArray(rawTransOption);\n      var pipeLen = pipedTransOption.length;\n      var errMsg = '';\n\n      if (!pipeLen) {\n        if (\"development\" !== 'production') {\n          errMsg = 'If `transform` declared, it should at least contain one transform.';\n        }\n\n        throwError(errMsg);\n      }\n\n      for (var i = 0, len = pipeLen; i < len; i++) {\n        var transOption = pipedTransOption[i];\n        sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one.\n        // piped transform only support single output, except the last one.\n\n        if (i !== len - 1) {\n          sourceList.length = Math.max(sourceList.length, 1);\n        }\n      }\n\n      return sourceList;\n    }\n\n    function applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform.\n    pipeIndex) {\n      var errMsg = '';\n\n      if (!upSourceList.length) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Must have at least one upstream dataset.';\n        }\n\n        throwError(errMsg);\n      }\n\n      if (!isObject(transOption)) {\n        if (\"development\" !== 'production') {\n          errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';\n        }\n\n        throwError(errMsg);\n      }\n\n      var transType = transOption.type;\n      var externalTransform = externalTransformMap.get(transType);\n\n      if (!externalTransform) {\n        if (\"development\" !== 'production') {\n          errMsg = 'Can not find transform on type \"' + transType + '\".';\n        }\n\n        throwError(errMsg);\n      } // Prepare source\n\n\n      var extUpSourceList = map(upSourceList, function (upSource) {\n        return createExternalSource(upSource, externalTransform);\n      });\n      var resultList = normalizeToArray(externalTransform.transform({\n        upstream: extUpSourceList[0],\n        upstreamList: extUpSourceList,\n        config: clone(transOption.config)\n      }));\n\n      if (\"development\" !== 'production') {\n        if (transOption.print) {\n          var printStrArr = map(resultList, function (extSource) {\n            var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';\n            return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\\n');\n          }).join('\\n');\n          log(printStrArr);\n        }\n      }\n\n      return map(resultList, function (result, resultIndex) {\n        var errMsg = '';\n\n        if (!isObject(result)) {\n          if (\"development\" !== 'production') {\n            errMsg = 'A transform should not return some empty results.';\n          }\n\n          throwError(errMsg);\n        }\n\n        if (!result.data) {\n          if (\"development\" !== 'production') {\n            errMsg = 'Transform result data should be not be null or undefined';\n          }\n\n          throwError(errMsg);\n        }\n\n        var sourceFormat = detectSourceFormat(result.data);\n\n        if (!isSupportedSourceFormat(sourceFormat)) {\n          if (\"development\" !== 'production') {\n            errMsg = 'Transform result data should be array rows or object rows.';\n          }\n\n          throwError(errMsg);\n        }\n\n        var resultMetaRawOption;\n        var firstUpSource = upSourceList[0];\n        /**\n         * Intuitively, the end users known the content of the original `dataset.source`,\n         * calucating the transform result in mind.\n         * Suppose the original `dataset.source` is:\n         * ```js\n         * [\n         *     ['product', '2012', '2013', '2014', '2015'],\n         *     ['AAA', 41.1, 30.4, 65.1, 53.3],\n         *     ['BBB', 86.5, 92.1, 85.7, 83.1],\n         *     ['CCC', 24.1, 67.2, 79.5, 86.4]\n         * ]\n         * ```\n         * The dimension info have to be detected from the source data.\n         * Some of the transformers (like filter, sort) will follow the dimension info\n         * of upstream, while others use new dimensions (like aggregate).\n         * Transformer can output a field `dimensions` to define the its own output dimensions.\n         * We also allow transformers to ignore the output `dimensions` field, and\n         * inherit the upstream dimensions definition. It can reduce the burden of handling\n         * dimensions in transformers.\n         *\n         * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.\n         */\n\n        if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different\n        // dimensions definitions. We do not inherit anything from upstream.\n        && !result.dimensions) {\n          var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result, because:\n          // (1) The returned data always does not contain header line and can not be used\n          // as dimension-detection. In this case we can not use \"detected dimensions\" of\n          // upstream directly, because it might be detected based on different `seriesLayoutBy`.\n          // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.\n          // So the original detected header should be add to the result, otherwise they can not be read.\n\n          if (startIndex) {\n            result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);\n          }\n\n          resultMetaRawOption = {\n            seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n            sourceHeader: startIndex,\n            dimensions: firstUpSource.metaRawOption.dimensions\n          };\n        } else {\n          resultMetaRawOption = {\n            seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,\n            sourceHeader: 0,\n            dimensions: result.dimensions\n          };\n        }\n\n        return createSource(result.data, resultMetaRawOption, null);\n      });\n    }\n\n    function isSupportedSourceFormat(sourceFormat) {\n      return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;\n    }\n\n    var UNDEFINED = 'undefined';\n    /* global Float64Array, Int32Array, Uint32Array, Uint16Array */\n    // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is\n    // different from the Ctor of typed array.\n\n    var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;\n    var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;\n    var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;\n    var CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array;\n    /**\n     * Multi dimensional data store\n     */\n\n    var dataCtors = {\n      'float': CtorFloat64Array,\n      'int': CtorInt32Array,\n      // Ordinal data type can be string or int\n      'ordinal': Array,\n      'number': Array,\n      'time': CtorFloat64Array\n    };\n    var defaultDimValueGetters;\n\n    function getIndicesCtor(rawCount) {\n      // The possible max value in this._indicies is always this._rawCount despite of filtering.\n      return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;\n    }\n\n    function getInitialExtent() {\n      return [Infinity, -Infinity];\n    }\n\n    function cloneChunk(originalChunk) {\n      var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array.\n\n      return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);\n    }\n\n    function prepareStore(store, dimIdx, dimType, end, append) {\n      var DataCtor = dataCtors[dimType || 'float'];\n\n      if (append) {\n        var oldStore = store[dimIdx];\n        var oldLen = oldStore && oldStore.length;\n\n        if (!(oldLen === end)) {\n          var newStore = new DataCtor(end); // The cost of the copy is probably inconsiderable\n          // within the initial chunkSize.\n\n          for (var j = 0; j < oldLen; j++) {\n            newStore[j] = oldStore[j];\n          }\n\n          store[dimIdx] = newStore;\n        }\n      } else {\n        store[dimIdx] = new DataCtor(end);\n      }\n    }\n    /**\n     * Basically, DataStore API keep immutable.\n     */\n\n    var DataStore =\n    /** @class */\n    function () {\n      function DataStore() {\n        this._chunks = []; // It will not be calculated util needed.\n\n        this._rawExtent = [];\n        this._extent = [];\n        this._count = 0;\n        this._rawCount = 0;\n        this._calcDimNameToIdx = createHashMap();\n      }\n      /**\n       * Initialize from data\n       */\n\n\n      DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {\n        if (\"development\" !== 'production') {\n          assert(isFunction(provider.getItem) && isFunction(provider.count), 'Invalid data provider.');\n        }\n\n        this._provider = provider; // Clear\n\n        this._chunks = [];\n        this._indices = null;\n        this.getRawIndex = this._getRawIdxIdentity;\n        var source = provider.getSource();\n        var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; // Default dim value getter\n\n        this._dimValueGetter = dimValueGetter || defaultGetter; // Reset raw extent.\n\n        this._rawExtent = [];\n        var willRetrieveDataByName = shouldRetrieveDataByName(source);\n        this._dimensions = map(inputDimensions, function (dim) {\n          if (\"development\" !== 'production') {\n            if (willRetrieveDataByName) {\n              assert(dim.property != null);\n            }\n          }\n\n          return {\n            // Only pick these two props. Not leak other properties like orderMeta.\n            type: dim.type,\n            property: dim.property\n          };\n        });\n\n        this._initDataFromProvider(0, provider.count());\n      };\n\n      DataStore.prototype.getProvider = function () {\n        return this._provider;\n      };\n      /**\n       * Caution: even when a `source` instance owned by a series, the created data store\n       * may still be shared by different sereis (the source hash does not use all `source`\n       * props, see `sourceManager`). In this case, the `source` props that are not used in\n       * hash (like `source.dimensionDefine`) probably only belongs to a certain series and\n       * thus should not be fetch here.\n       */\n\n\n      DataStore.prototype.getSource = function () {\n        return this._provider.getSource();\n      };\n      /**\n       * @caution Only used in dataStack.\n       */\n\n\n      DataStore.prototype.ensureCalculationDimension = function (dimName, type) {\n        var calcDimNameToIdx = this._calcDimNameToIdx;\n        var dimensions = this._dimensions;\n        var calcDimIdx = calcDimNameToIdx.get(dimName);\n\n        if (calcDimIdx != null) {\n          if (dimensions[calcDimIdx].type === type) {\n            return calcDimIdx;\n          }\n        } else {\n          calcDimIdx = dimensions.length;\n        }\n\n        dimensions[calcDimIdx] = {\n          type: type\n        };\n        calcDimNameToIdx.set(dimName, calcDimIdx);\n        this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);\n        this._rawExtent[calcDimIdx] = getInitialExtent();\n        return calcDimIdx;\n      };\n\n      DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) {\n        var chunk = this._chunks[dimIdx];\n        var dim = this._dimensions[dimIdx];\n        var rawExtents = this._rawExtent;\n        var offset = dim.ordinalOffset || 0;\n        var len = chunk.length;\n\n        if (offset === 0) {\n          // We need to reset the rawExtent if collect is from start.\n          // Because this dimension may be guessed as number and calcuating a wrong extent.\n          rawExtents[dimIdx] = getInitialExtent();\n        }\n\n        var dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData\n\n        for (var i = offset; i < len; i++) {\n          var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]);\n\n          if (!isNaN(val)) {\n            dimRawExtent[0] = Math.min(val, dimRawExtent[0]);\n            dimRawExtent[1] = Math.max(val, dimRawExtent[1]);\n          }\n        }\n\n        dim.ordinalMeta = ordinalMeta;\n        dim.ordinalOffset = len;\n        dim.type = 'ordinal'; // Force to be ordinal\n      };\n\n      DataStore.prototype.getOrdinalMeta = function (dimIdx) {\n        var dimInfo = this._dimensions[dimIdx];\n        var ordinalMeta = dimInfo.ordinalMeta;\n        return ordinalMeta;\n      };\n\n      DataStore.prototype.getDimensionProperty = function (dimIndex) {\n        var item = this._dimensions[dimIndex];\n        return item && item.property;\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       */\n\n\n      DataStore.prototype.appendData = function (data) {\n        if (\"development\" !== 'production') {\n          assert(!this._indices, 'appendData can only be called on raw data.');\n        }\n\n        var provider = this._provider;\n        var start = this.count();\n        provider.appendData(data);\n        var end = provider.count();\n\n        if (!provider.persistent) {\n          end += start;\n        }\n\n        if (start < end) {\n          this._initDataFromProvider(start, end, true);\n        }\n\n        return [start, end];\n      };\n\n      DataStore.prototype.appendValues = function (values, minFillLen) {\n        var chunks = this._chunks;\n        var dimensions = this._dimensions;\n        var dimLen = dimensions.length;\n        var rawExtent = this._rawExtent;\n        var start = this.count();\n        var end = start + Math.max(values.length, minFillLen || 0);\n\n        for (var i = 0; i < dimLen; i++) {\n          var dim = dimensions[i];\n          prepareStore(chunks, i, dim.type, end, true);\n        }\n\n        var emptyDataItem = [];\n\n        for (var idx = start; idx < end; idx++) {\n          var sourceIdx = idx - start; // Store the data by dimensions\n\n          for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n            var dim = dimensions[dimIdx];\n            var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx);\n            chunks[dimIdx][idx] = val;\n            var dimRawExtent = rawExtent[dimIdx];\n            val < dimRawExtent[0] && (dimRawExtent[0] = val);\n            val > dimRawExtent[1] && (dimRawExtent[1] = val);\n          }\n        }\n\n        this._rawCount = this._count = end;\n        return {\n          start: start,\n          end: end\n        };\n      };\n\n      DataStore.prototype._initDataFromProvider = function (start, end, append) {\n        var provider = this._provider;\n        var chunks = this._chunks;\n        var dimensions = this._dimensions;\n        var dimLen = dimensions.length;\n        var rawExtent = this._rawExtent;\n        var dimNames = map(dimensions, function (dim) {\n          return dim.property;\n        });\n\n        for (var i = 0; i < dimLen; i++) {\n          var dim = dimensions[i];\n\n          if (!rawExtent[i]) {\n            rawExtent[i] = getInitialExtent();\n          }\n\n          prepareStore(chunks, i, dim.type, end, append);\n        }\n\n        if (provider.fillStorage) {\n          provider.fillStorage(start, end, chunks, rawExtent);\n        } else {\n          var dataItem = [];\n\n          for (var idx = start; idx < end; idx++) {\n            // NOTICE: Try not to write things into dataItem\n            dataItem = provider.getItem(idx, dataItem); // Each data item is value\n            // [1, 2]\n            // 2\n            // Bar chart, line chart which uses category axis\n            // only gives the 'y' value. 'x' value is the indices of category\n            // Use a tempValue to normalize the value to be a (x, y) value\n            // Store the data by dimensions\n\n            for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {\n              var dimStorage = chunks[dimIdx]; // PENDING NULL is empty or zero\n\n              var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx);\n\n              dimStorage[idx] = val;\n              var dimRawExtent = rawExtent[dimIdx];\n              val < dimRawExtent[0] && (dimRawExtent[0] = val);\n              val > dimRawExtent[1] && (dimRawExtent[1] = val);\n            }\n          }\n        }\n\n        if (!provider.persistent && provider.clean) {\n          // Clean unused data if data source is typed array.\n          provider.clean();\n        }\n\n        this._rawCount = this._count = end; // Reset data extent\n\n        this._extent = [];\n      };\n\n      DataStore.prototype.count = function () {\n        return this._count;\n      };\n      /**\n       * Get value. Return NaN if idx is out of range.\n       */\n\n\n      DataStore.prototype.get = function (dim, idx) {\n        if (!(idx >= 0 && idx < this._count)) {\n          return NaN;\n        }\n\n        var dimStore = this._chunks[dim];\n        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;\n      };\n\n      DataStore.prototype.getValues = function (dimensions, idx) {\n        var values = [];\n        var dimArr = [];\n\n        if (idx == null) {\n          idx = dimensions; // TODO get all from store?\n\n          dimensions = []; // All dimensions\n\n          for (var i = 0; i < this._dimensions.length; i++) {\n            dimArr.push(i);\n          }\n        } else {\n          dimArr = dimensions;\n        }\n\n        for (var i = 0, len = dimArr.length; i < len; i++) {\n          values.push(this.get(dimArr[i], idx));\n        }\n\n        return values;\n      };\n      /**\n       * @param dim concrete dim\n       */\n\n\n      DataStore.prototype.getByRawIndex = function (dim, rawIdx) {\n        if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {\n          return NaN;\n        }\n\n        var dimStore = this._chunks[dim];\n        return dimStore ? dimStore[rawIdx] : NaN;\n      };\n      /**\n       * Get sum of data in one dimension\n       */\n\n\n      DataStore.prototype.getSum = function (dim) {\n        var dimData = this._chunks[dim];\n        var sum = 0;\n\n        if (dimData) {\n          for (var i = 0, len = this.count(); i < len; i++) {\n            var value = this.get(dim, i);\n\n            if (!isNaN(value)) {\n              sum += value;\n            }\n          }\n        }\n\n        return sum;\n      };\n      /**\n       * Get median of data in one dimension\n       */\n\n\n      DataStore.prototype.getMedian = function (dim) {\n        var dimDataArray = []; // map all data of one dimension\n\n        this.each([dim], function (val) {\n          if (!isNaN(val)) {\n            dimDataArray.push(val);\n          }\n        }); // TODO\n        // Use quick select?\n\n        var sortedDimDataArray = dimDataArray.sort(function (a, b) {\n          return a - b;\n        });\n        var len = this.count(); // calculate median\n\n        return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;\n      };\n      /**\n       * Retrieve the index with given raw data index.\n       */\n\n\n      DataStore.prototype.indexOfRawIndex = function (rawIndex) {\n        if (rawIndex >= this._rawCount || rawIndex < 0) {\n          return -1;\n        }\n\n        if (!this._indices) {\n          return rawIndex;\n        } // Indices are ascending\n\n\n        var indices = this._indices; // If rawIndex === dataIndex\n\n        var rawDataIndex = indices[rawIndex];\n\n        if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {\n          return rawIndex;\n        }\n\n        var left = 0;\n        var right = this._count - 1;\n\n        while (left <= right) {\n          var mid = (left + right) / 2 | 0;\n\n          if (indices[mid] < rawIndex) {\n            left = mid + 1;\n          } else if (indices[mid] > rawIndex) {\n            right = mid - 1;\n          } else {\n            return mid;\n          }\n        }\n\n        return -1;\n      };\n      /**\n       * Retrieve the index of nearest value.\n       * @param dim\n       * @param value\n       * @param [maxDistance=Infinity]\n       * @return If and only if multiple indices have\n       *         the same value, they are put to the result.\n       */\n\n\n      DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n        var chunks = this._chunks;\n        var dimData = chunks[dim];\n        var nearestIndices = [];\n\n        if (!dimData) {\n          return nearestIndices;\n        }\n\n        if (maxDistance == null) {\n          maxDistance = Infinity;\n        }\n\n        var minDist = Infinity;\n        var minDiff = -1;\n        var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/SeriesData.js`.\n\n        for (var i = 0, len = this.count(); i < len; i++) {\n          var dataIndex = this.getRawIndex(i);\n          var diff = value - dimData[dataIndex];\n          var dist = Math.abs(diff);\n\n          if (dist <= maxDistance) {\n            // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,\n            // we'd better not push both of them to `nearestIndices`, otherwise it is easy to\n            // get more than one item in `nearestIndices` (more specifically, in `tooltip`).\n            // So we chose the one that `diff >= 0` in this csae.\n            // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them\n            // should be push to `nearestIndices`.\n            if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) {\n              minDist = dist;\n              minDiff = diff;\n              nearestIndicesLen = 0;\n            }\n\n            if (diff === minDiff) {\n              nearestIndices[nearestIndicesLen++] = i;\n            }\n          }\n        }\n\n        nearestIndices.length = nearestIndicesLen;\n        return nearestIndices;\n      };\n\n      DataStore.prototype.getIndices = function () {\n        var newIndices;\n        var indices = this._indices;\n\n        if (indices) {\n          var Ctor = indices.constructor;\n          var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.\n\n          if (Ctor === Array) {\n            newIndices = new Ctor(thisCount);\n\n            for (var i = 0; i < thisCount; i++) {\n              newIndices[i] = indices[i];\n            }\n          } else {\n            newIndices = new Ctor(indices.buffer, 0, thisCount);\n          }\n        } else {\n          var Ctor = getIndicesCtor(this._rawCount);\n          newIndices = new Ctor(this.count());\n\n          for (var i = 0; i < newIndices.length; i++) {\n            newIndices[i] = i;\n          }\n        }\n\n        return newIndices;\n      };\n      /**\n       * Data filter.\n       */\n\n\n      DataStore.prototype.filter = function (dims, cb) {\n        if (!this._count) {\n          return this;\n        }\n\n        var newStore = this.clone();\n        var count = newStore.count();\n        var Ctor = getIndicesCtor(newStore._rawCount);\n        var newIndices = new Ctor(count);\n        var value = [];\n        var dimSize = dims.length;\n        var offset = 0;\n        var dim0 = dims[0];\n        var chunks = newStore._chunks;\n\n        for (var i = 0; i < count; i++) {\n          var keep = void 0;\n          var rawIdx = newStore.getRawIndex(i); // Simple optimization\n\n          if (dimSize === 0) {\n            keep = cb(i);\n          } else if (dimSize === 1) {\n            var val = chunks[dim0][rawIdx];\n            keep = cb(val, i);\n          } else {\n            var k = 0;\n\n            for (; k < dimSize; k++) {\n              value[k] = chunks[dims[k]][rawIdx];\n            }\n\n            value[k] = i;\n            keep = cb.apply(null, value);\n          }\n\n          if (keep) {\n            newIndices[offset++] = rawIdx;\n          }\n        } // Set indices after filtered.\n\n\n        if (offset < count) {\n          newStore._indices = newIndices;\n        }\n\n        newStore._count = offset; // Reset data extent\n\n        newStore._extent = [];\n\n        newStore._updateGetRawIdx();\n\n        return newStore;\n      };\n      /**\n       * Select data in range. (For optimization of filter)\n       * (Manually inline code, support 5 million data filtering in data zoom.)\n       */\n\n\n      DataStore.prototype.selectRange = function (range) {\n        var newStore = this.clone();\n        var len = newStore._count;\n\n        if (!len) {\n          return this;\n        }\n\n        var dims = keys(range);\n        var dimSize = dims.length;\n\n        if (!dimSize) {\n          return this;\n        }\n\n        var originalCount = newStore.count();\n        var Ctor = getIndicesCtor(newStore._rawCount);\n        var newIndices = new Ctor(originalCount);\n        var offset = 0;\n        var dim0 = dims[0];\n        var min = range[dim0][0];\n        var max = range[dim0][1];\n        var storeArr = newStore._chunks;\n        var quickFinished = false;\n\n        if (!newStore._indices) {\n          // Extreme optimization for common case. About 2x faster in chrome.\n          var idx = 0;\n\n          if (dimSize === 1) {\n            var dimStorage = storeArr[dims[0]];\n\n            for (var i = 0; i < len; i++) {\n              var val = dimStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty\n              // value indicates the line should be broken. But for the case like\n              // scatter plot, a data item with empty value will not be rendered,\n              // but the axis extent may be effected if some other dim of the data\n              // item has value. Fortunately it is not a significant negative effect.\n\n              if (val >= min && val <= max || isNaN(val)) {\n                newIndices[offset++] = idx;\n              }\n\n              idx++;\n            }\n\n            quickFinished = true;\n          } else if (dimSize === 2) {\n            var dimStorage = storeArr[dims[0]];\n            var dimStorage2 = storeArr[dims[1]];\n            var min2 = range[dims[1]][0];\n            var max2 = range[dims[1]][1];\n\n            for (var i = 0; i < len; i++) {\n              var val = dimStorage[i];\n              var val2 = dimStorage2[i]; // Do not filter NaN, see comment above.\n\n              if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {\n                newIndices[offset++] = idx;\n              }\n\n              idx++;\n            }\n\n            quickFinished = true;\n          }\n        }\n\n        if (!quickFinished) {\n          if (dimSize === 1) {\n            for (var i = 0; i < originalCount; i++) {\n              var rawIndex = newStore.getRawIndex(i);\n              var val = storeArr[dims[0]][rawIndex]; // Do not filter NaN, see comment above.\n\n              if (val >= min && val <= max || isNaN(val)) {\n                newIndices[offset++] = rawIndex;\n              }\n            }\n          } else {\n            for (var i = 0; i < originalCount; i++) {\n              var keep = true;\n              var rawIndex = newStore.getRawIndex(i);\n\n              for (var k = 0; k < dimSize; k++) {\n                var dimk = dims[k];\n                var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above.\n\n                if (val < range[dimk][0] || val > range[dimk][1]) {\n                  keep = false;\n                }\n              }\n\n              if (keep) {\n                newIndices[offset++] = newStore.getRawIndex(i);\n              }\n            }\n          }\n        } // Set indices after filtered.\n\n\n        if (offset < originalCount) {\n          newStore._indices = newIndices;\n        }\n\n        newStore._count = offset; // Reset data extent\n\n        newStore._extent = [];\n\n        newStore._updateGetRawIdx();\n\n        return newStore;\n      }; // /**\n      //  * Data mapping to a plain array\n      //  */\n      // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {\n      //     const result: any[] = [];\n      //     this.each(dims, function () {\n      //         result.push(cb && (cb as MapArrayCb).apply(null, arguments));\n      //     });\n      //     return result;\n      // }\n\n      /**\n       * Data mapping to a new List with given dimensions\n       */\n\n\n      DataStore.prototype.map = function (dims, cb) {\n        // TODO only clone picked chunks.\n        var target = this.clone(dims);\n\n        this._updateDims(target, dims, cb);\n\n        return target;\n      };\n      /**\n       * @caution Danger!! Only used in dataStack.\n       */\n\n\n      DataStore.prototype.modify = function (dims, cb) {\n        this._updateDims(this, dims, cb);\n      };\n\n      DataStore.prototype._updateDims = function (target, dims, cb) {\n        var targetChunks = target._chunks;\n        var tmpRetValue = [];\n        var dimSize = dims.length;\n        var dataCount = target.count();\n        var values = [];\n        var rawExtent = target._rawExtent;\n\n        for (var i = 0; i < dims.length; i++) {\n          rawExtent[dims[i]] = getInitialExtent();\n        }\n\n        for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {\n          var rawIndex = target.getRawIndex(dataIndex);\n\n          for (var k = 0; k < dimSize; k++) {\n            values[k] = targetChunks[dims[k]][rawIndex];\n          }\n\n          values[dimSize] = dataIndex;\n          var retValue = cb && cb.apply(null, values);\n\n          if (retValue != null) {\n            // a number or string (in oridinal dimension)?\n            if (typeof retValue !== 'object') {\n              tmpRetValue[0] = retValue;\n              retValue = tmpRetValue;\n            }\n\n            for (var i = 0; i < retValue.length; i++) {\n              var dim = dims[i];\n              var val = retValue[i];\n              var rawExtentOnDim = rawExtent[dim];\n              var dimStore = targetChunks[dim];\n\n              if (dimStore) {\n                dimStore[rawIndex] = val;\n              }\n\n              if (val < rawExtentOnDim[0]) {\n                rawExtentOnDim[0] = val;\n              }\n\n              if (val > rawExtentOnDim[1]) {\n                rawExtentOnDim[1] = val;\n              }\n            }\n          }\n        }\n      };\n      /**\n       * Large data down sampling using largest-triangle-three-buckets\n       * @param {string} valueDimension\n       * @param {number} targetCount\n       */\n\n\n      DataStore.prototype.lttbDownSample = function (valueDimension, rate) {\n        var target = this.clone([valueDimension], true);\n        var targetStorage = target._chunks;\n        var dimStore = targetStorage[valueDimension];\n        var len = this.count();\n        var sampledIndex = 0;\n        var frameSize = Math.floor(1 / rate);\n        var currentRawIndex = this.getRawIndex(0);\n        var maxArea;\n        var area;\n        var nextRawIndex;\n        var newIndices = new (getIndicesCtor(this._rawCount))(Math.min((Math.ceil(len / frameSize) + 2) * 2, len)); // First frame use the first data.\n\n        newIndices[sampledIndex++] = currentRawIndex;\n\n        for (var i = 1; i < len - 1; i += frameSize) {\n          var nextFrameStart = Math.min(i + frameSize, len - 1);\n          var nextFrameEnd = Math.min(i + frameSize * 2, len);\n          var avgX = (nextFrameEnd + nextFrameStart) / 2;\n          var avgY = 0;\n\n          for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {\n            var rawIndex = this.getRawIndex(idx);\n            var y = dimStore[rawIndex];\n\n            if (isNaN(y)) {\n              continue;\n            }\n\n            avgY += y;\n          }\n\n          avgY /= nextFrameEnd - nextFrameStart;\n          var frameStart = i;\n          var frameEnd = Math.min(i + frameSize, len);\n          var pointAX = i - 1;\n          var pointAY = dimStore[currentRawIndex];\n          maxArea = -1;\n          nextRawIndex = frameStart;\n          var firstNaNIndex = -1;\n          var countNaN = 0; // Find a point from current frame that construct a triangel with largest area with previous selected point\n          // And the average of next frame.\n\n          for (var idx = frameStart; idx < frameEnd; idx++) {\n            var rawIndex = this.getRawIndex(idx);\n            var y = dimStore[rawIndex];\n\n            if (isNaN(y)) {\n              countNaN++;\n\n              if (firstNaNIndex < 0) {\n                firstNaNIndex = rawIndex;\n              }\n\n              continue;\n            } // Calculate triangle area over three buckets\n\n\n            area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));\n\n            if (area > maxArea) {\n              maxArea = area;\n              nextRawIndex = rawIndex; // Next a is this b\n            }\n          }\n\n          if (countNaN > 0 && countNaN < frameEnd - frameStart) {\n            // Append first NaN point in every bucket.\n            // It is necessary to ensure the correct order of indices.\n            newIndices[sampledIndex++] = Math.min(firstNaNIndex, nextRawIndex);\n            nextRawIndex = Math.max(firstNaNIndex, nextRawIndex);\n          }\n\n          newIndices[sampledIndex++] = nextRawIndex;\n          currentRawIndex = nextRawIndex; // This a is the next a (chosen b)\n        } // First frame use the last data.\n\n\n        newIndices[sampledIndex++] = this.getRawIndex(len - 1);\n        target._count = sampledIndex;\n        target._indices = newIndices;\n        target.getRawIndex = this._getRawIdx;\n        return target;\n      };\n      /**\n       * Large data down sampling on given dimension\n       * @param sampleIndex Sample index for name and id\n       */\n\n\n      DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n        var target = this.clone([dimension], true);\n        var targetStorage = target._chunks;\n        var frameValues = [];\n        var frameSize = Math.floor(1 / rate);\n        var dimStore = targetStorage[dimension];\n        var len = this.count();\n        var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent();\n        var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize));\n        var offset = 0;\n\n        for (var i = 0; i < len; i += frameSize) {\n          // Last frame\n          if (frameSize > len - i) {\n            frameSize = len - i;\n            frameValues.length = frameSize;\n          }\n\n          for (var k = 0; k < frameSize; k++) {\n            var dataIdx = this.getRawIndex(i + k);\n            frameValues[k] = dimStore[dataIdx];\n          }\n\n          var value = sampleValue(frameValues);\n          var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data\n\n          dimStore[sampleFrameIdx] = value;\n\n          if (value < rawExtentOnDim[0]) {\n            rawExtentOnDim[0] = value;\n          }\n\n          if (value > rawExtentOnDim[1]) {\n            rawExtentOnDim[1] = value;\n          }\n\n          newIndices[offset++] = sampleFrameIdx;\n        }\n\n        target._count = offset;\n        target._indices = newIndices;\n\n        target._updateGetRawIdx();\n\n        return target;\n      };\n      /**\n       * Data iteration\n       * @param ctx default this\n       * @example\n       *  list.each('x', function (x, idx) {});\n       *  list.each(['x', 'y'], function (x, y, idx) {});\n       *  list.each(function (idx) {})\n       */\n\n\n      DataStore.prototype.each = function (dims, cb) {\n        if (!this._count) {\n          return;\n        }\n\n        var dimSize = dims.length;\n        var chunks = this._chunks;\n\n        for (var i = 0, len = this.count(); i < len; i++) {\n          var rawIdx = this.getRawIndex(i); // Simple optimization\n\n          switch (dimSize) {\n            case 0:\n              cb(i);\n              break;\n\n            case 1:\n              cb(chunks[dims[0]][rawIdx], i);\n              break;\n\n            case 2:\n              cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);\n              break;\n\n            default:\n              var k = 0;\n              var value = [];\n\n              for (; k < dimSize; k++) {\n                value[k] = chunks[dims[k]][rawIdx];\n              } // Index\n\n\n              value[k] = i;\n              cb.apply(null, value);\n          }\n        }\n      };\n      /**\n       * Get extent of data in one dimension\n       */\n\n\n      DataStore.prototype.getDataExtent = function (dim) {\n        // Make sure use concrete dim as cache name.\n        var dimData = this._chunks[dim];\n        var initialExtent = getInitialExtent();\n\n        if (!dimData) {\n          return initialExtent;\n        } // Make more strict checkings to ensure hitting cache.\n\n\n        var currEnd = this.count(); // Consider the most cases when using data zoom, `getDataExtent`\n        // happened before filtering. We cache raw extent, which is not\n        // necessary to be cleared and recalculated when restore data.\n\n        var useRaw = !this._indices;\n        var dimExtent;\n\n        if (useRaw) {\n          return this._rawExtent[dim].slice();\n        }\n\n        dimExtent = this._extent[dim];\n\n        if (dimExtent) {\n          return dimExtent.slice();\n        }\n\n        dimExtent = initialExtent;\n        var min = dimExtent[0];\n        var max = dimExtent[1];\n\n        for (var i = 0; i < currEnd; i++) {\n          var rawIdx = this.getRawIndex(i);\n          var value = dimData[rawIdx];\n          value < min && (min = value);\n          value > max && (max = value);\n        }\n\n        dimExtent = [min, max];\n        this._extent[dim] = dimExtent;\n        return dimExtent;\n      };\n      /**\n       * Get raw data item\n       */\n\n\n      DataStore.prototype.getRawDataItem = function (idx) {\n        var rawIdx = this.getRawIndex(idx);\n\n        if (!this._provider.persistent) {\n          var val = [];\n          var chunks = this._chunks;\n\n          for (var i = 0; i < chunks.length; i++) {\n            val.push(chunks[i][rawIdx]);\n          }\n\n          return val;\n        } else {\n          return this._provider.getItem(rawIdx);\n        }\n      };\n      /**\n       * Clone shallow.\n       *\n       * @param clonedDims Determine which dims to clone. Will share the data if not specified.\n       */\n\n\n      DataStore.prototype.clone = function (clonedDims, ignoreIndices) {\n        var target = new DataStore();\n        var chunks = this._chunks;\n        var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {\n          obj[dimIdx] = true;\n          return obj;\n        }, {});\n\n        if (clonedDimsMap) {\n          for (var i = 0; i < chunks.length; i++) {\n            // Not clone if dim is not picked.\n            target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);\n          }\n        } else {\n          target._chunks = chunks;\n        }\n\n        this._copyCommonProps(target);\n\n        if (!ignoreIndices) {\n          target._indices = this._cloneIndices();\n        }\n\n        target._updateGetRawIdx();\n\n        return target;\n      };\n\n      DataStore.prototype._copyCommonProps = function (target) {\n        target._count = this._count;\n        target._rawCount = this._rawCount;\n        target._provider = this._provider;\n        target._dimensions = this._dimensions;\n        target._extent = clone(this._extent);\n        target._rawExtent = clone(this._rawExtent);\n      };\n\n      DataStore.prototype._cloneIndices = function () {\n        if (this._indices) {\n          var Ctor = this._indices.constructor;\n          var indices = void 0;\n\n          if (Ctor === Array) {\n            var thisCount = this._indices.length;\n            indices = new Ctor(thisCount);\n\n            for (var i = 0; i < thisCount; i++) {\n              indices[i] = this._indices[i];\n            }\n          } else {\n            indices = new Ctor(this._indices);\n          }\n\n          return indices;\n        }\n\n        return null;\n      };\n\n      DataStore.prototype._getRawIdxIdentity = function (idx) {\n        return idx;\n      };\n\n      DataStore.prototype._getRawIdx = function (idx) {\n        if (idx < this._count && idx >= 0) {\n          return this._indices[idx];\n        }\n\n        return -1;\n      };\n\n      DataStore.prototype._updateGetRawIdx = function () {\n        this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;\n      };\n\n      DataStore.internalField = function () {\n        function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {\n          return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);\n        }\n\n        defaultDimValueGetters = {\n          arrayRows: getDimValueSimply,\n          objectRows: function (dataItem, property, dataIndex, dimIndex) {\n            return parseDataValue(dataItem[property], this._dimensions[dimIndex]);\n          },\n          keyedColumns: getDimValueSimply,\n          original: function (dataItem, property, dataIndex, dimIndex) {\n            // Performance sensitive, do not use modelUtil.getDataItemValue.\n            // If dataItem is an plain object with no value field, the let `value`\n            // will be assigned with the object, but it will be tread correctly\n            // in the `convertValue`.\n            var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);\n            return parseDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.\n            : value, this._dimensions[dimIndex]);\n          },\n          typedArray: function (dataItem, property, dataIndex, dimIndex) {\n            return dataItem[dimIndex];\n          }\n        };\n      }();\n\n      return DataStore;\n    }();\n\n    /**\n     * [REQUIREMENT_MEMO]:\n     * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option.\n     * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and\n     * `root-dataset`. Them on `series` has higher priority.\n     * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might\n     * confuse users: whether those props indicate how to visit the upstream source or visit\n     * the transform result source, and some transforms has nothing to do with these props,\n     * and some transforms might have multiple upstream.\n     * (3) Transforms should specify `metaRawOption` in each output, just like they can be\n     * declared in `root-dataset`.\n     * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms.\n     * That is for reducing complexity in transforms.\n     * PENDING: Whether to provide transposition transform?\n     *\n     * [IMPLEMENTAION_MEMO]:\n     * \"sourceVisitConfig\" are calculated from `metaRawOption` and `data`.\n     * They will not be calculated until `source` is about to be visited (to prevent from\n     * duplicate calcuation). `source` is visited only in series and input to transforms.\n     *\n     * [DIMENSION_INHERIT_RULE]:\n     * By default the dimensions are inherited from ancestors, unless a transform return\n     * a new dimensions definition.\n     * Consider the case:\n     * ```js\n     * dataset: [{\n     *     source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...]\n     * }, {\n     *     transform: { type: 'filter', ... }\n     * }]\n     * dataset: [{\n     *     dimension: ['Product', 'Sales', 'Prise'],\n     *     source: [ ['Cookies', 321, 44.21], ...]\n     * }, {\n     *     transform: { type: 'filter', ... }\n     * }]\n     * ```\n     * The two types of option should have the same behavior after transform.\n     *\n     *\n     * [SCENARIO]:\n     * (1) Provide source data directly:\n     * ```js\n     * series: {\n     *     encode: {...},\n     *     dimensions: [...]\n     *     seriesLayoutBy: 'row',\n     *     data: [[...]]\n     * }\n     * ```\n     * (2) Series refer to dataset.\n     * ```js\n     * series: [{\n     *     encode: {...}\n     *     // Ignore datasetIndex means `datasetIndex: 0`\n     *     // and the dimensions defination in dataset is used\n     * }, {\n     *     encode: {...},\n     *     seriesLayoutBy: 'column',\n     *     datasetIndex: 1\n     * }]\n     * ```\n     * (3) dataset transform\n     * ```js\n     * dataset: [{\n     *     source: [...]\n     * }, {\n     *     source: [...]\n     * }, {\n     *     // By default from 0.\n     *     transform: { type: 'filter', config: {...} }\n     * }, {\n     *     // Piped.\n     *     transform: [\n     *         { type: 'filter', config: {...} },\n     *         { type: 'sort', config: {...} }\n     *     ]\n     * }, {\n     *     id: 'regressionData',\n     *     fromDatasetIndex: 1,\n     *     // Third-party transform\n     *     transform: { type: 'ecStat:regression', config: {...} }\n     * }, {\n     *     // retrieve the extra result.\n     *     id: 'regressionFormula',\n     *     fromDatasetId: 'regressionData',\n     *     fromTransformResult: 1\n     * }]\n     * ```\n     */\n\n    var SourceManager =\n    /** @class */\n    function () {\n      function SourceManager(sourceHost) {\n        // Cached source. Do not repeat calculating if not dirty.\n        this._sourceList = [];\n        this._storeList = []; // version sign of each upstream source manager.\n\n        this._upstreamSignList = [];\n        this._versionSignBase = 0;\n        this._dirty = true;\n        this._sourceHost = sourceHost;\n      }\n      /**\n       * Mark dirty.\n       */\n\n\n      SourceManager.prototype.dirty = function () {\n        this._setLocalSource([], []);\n\n        this._storeList = [];\n        this._dirty = true;\n      };\n\n      SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {\n        this._sourceList = sourceList;\n        this._upstreamSignList = upstreamSignList;\n        this._versionSignBase++;\n\n        if (this._versionSignBase > 9e10) {\n          this._versionSignBase = 0;\n        }\n      };\n      /**\n       * For detecting whether the upstream source is dirty, so that\n       * the local cached source (in `_sourceList`) should be discarded.\n       */\n\n\n      SourceManager.prototype._getVersionSign = function () {\n        return this._sourceHost.uid + '_' + this._versionSignBase;\n      };\n      /**\n       * Always return a source instance. Otherwise throw error.\n       */\n\n\n      SourceManager.prototype.prepareSource = function () {\n        // For the case that call `setOption` multiple time but no data changed,\n        // cache the result source to prevent from repeating transform.\n        if (this._isDirty()) {\n          this._createSource();\n\n          this._dirty = false;\n        }\n      };\n\n      SourceManager.prototype._createSource = function () {\n        this._setLocalSource([], []);\n\n        var sourceHost = this._sourceHost;\n\n        var upSourceMgrList = this._getUpstreamSourceManagers();\n\n        var hasUpstream = !!upSourceMgrList.length;\n        var resultSourceList;\n        var upstreamSignList;\n\n        if (isSeries(sourceHost)) {\n          var seriesModel = sourceHost;\n          var data = void 0;\n          var sourceFormat = void 0;\n          var upSource = void 0; // Has upstream dataset\n\n          if (hasUpstream) {\n            var upSourceMgr = upSourceMgrList[0];\n            upSourceMgr.prepareSource();\n            upSource = upSourceMgr.getSource();\n            data = upSource.data;\n            sourceFormat = upSource.sourceFormat;\n            upstreamSignList = [upSourceMgr._getVersionSign()];\n          } // Series data is from own.\n          else {\n              data = seriesModel.get('data', true);\n              sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;\n              upstreamSignList = [];\n            } // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root.\n\n\n          var newMetaRawOption = this._getSourceMetaRawOption() || {};\n          var upMetaRawOption = upSource && upSource.metaRawOption || {};\n          var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null;\n          var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); // Note here we should not use `upSource.dimensionsDefine`. Consider the case:\n          // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`,\n          // but series need `seriesLayoutBy: 'row'`.\n\n          var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); // We share source with dataset as much as possible\n          // to avoid extra memory cost of high dimensional data.\n\n          var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions;\n          resultSourceList = needsCreateSource ? [createSource(data, {\n            seriesLayoutBy: seriesLayoutBy,\n            sourceHeader: sourceHeader,\n            dimensions: dimensions\n          }, sourceFormat)] : [];\n        } else {\n          var datasetModel = sourceHost; // Has upstream dataset.\n\n          if (hasUpstream) {\n            var result = this._applyTransform(upSourceMgrList);\n\n            resultSourceList = result.sourceList;\n            upstreamSignList = result.upstreamSignList;\n          } // Is root dataset.\n          else {\n              var sourceData = datasetModel.get('source', true);\n              resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)];\n              upstreamSignList = [];\n            }\n        }\n\n        if (\"development\" !== 'production') {\n          assert(resultSourceList && upstreamSignList);\n        }\n\n        this._setLocalSource(resultSourceList, upstreamSignList);\n      };\n\n      SourceManager.prototype._applyTransform = function (upMgrList) {\n        var datasetModel = this._sourceHost;\n        var transformOption = datasetModel.get('transform', true);\n        var fromTransformResult = datasetModel.get('fromTransformResult', true);\n\n        if (\"development\" !== 'production') {\n          assert(fromTransformResult != null || transformOption != null);\n        }\n\n        if (fromTransformResult != null) {\n          var errMsg = '';\n\n          if (upMgrList.length !== 1) {\n            if (\"development\" !== 'production') {\n              errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset';\n            }\n\n            doThrow(errMsg);\n          }\n        }\n\n        var sourceList;\n        var upSourceList = [];\n        var upstreamSignList = [];\n        each(upMgrList, function (upMgr) {\n          upMgr.prepareSource();\n          var upSource = upMgr.getSource(fromTransformResult || 0);\n          var errMsg = '';\n\n          if (fromTransformResult != null && !upSource) {\n            if (\"development\" !== 'production') {\n              errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult;\n            }\n\n            doThrow(errMsg);\n          }\n\n          upSourceList.push(upSource);\n          upstreamSignList.push(upMgr._getVersionSign());\n        });\n\n        if (transformOption) {\n          sourceList = applyDataTransform(transformOption, upSourceList, {\n            datasetIndex: datasetModel.componentIndex\n          });\n        } else if (fromTransformResult != null) {\n          sourceList = [cloneSourceShallow(upSourceList[0])];\n        }\n\n        return {\n          sourceList: sourceList,\n          upstreamSignList: upstreamSignList\n        };\n      };\n\n      SourceManager.prototype._isDirty = function () {\n        if (this._dirty) {\n          return true;\n        } // All sourceList is from the some upstream.\n\n\n        var upSourceMgrList = this._getUpstreamSourceManagers();\n\n        for (var i = 0; i < upSourceMgrList.length; i++) {\n          var upSrcMgr = upSourceMgrList[i];\n\n          if ( // Consider the case that there is ancestor diry, call it recursively.\n          // The performance is probably not an issue because usually the chain is not long.\n          upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {\n            return true;\n          }\n        }\n      };\n      /**\n       * @param sourceIndex By default 0, means \"main source\".\n       *                    In most cases there is only one source.\n       */\n\n\n      SourceManager.prototype.getSource = function (sourceIndex) {\n        sourceIndex = sourceIndex || 0;\n        var source = this._sourceList[sourceIndex];\n\n        if (!source) {\n          // Series may share source instance with dataset.\n          var upSourceMgrList = this._getUpstreamSourceManagers();\n\n          return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex);\n        }\n\n        return source;\n      };\n      /**\n       *\n       * Get a data store which can be shared across series.\n       * Only available for series.\n       *\n       * @param seriesDimRequest Dimensions that are generated in series.\n       *        Should have been sorted by `storeDimIndex` asc.\n       */\n\n\n      SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) {\n        if (\"development\" !== 'production') {\n          assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.');\n        }\n\n        var schema = seriesDimRequest.makeStoreSchema();\n        return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash);\n      };\n\n      SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) {\n        // TODO Can use other sourceIndex?\n        var sourceIndex = 0;\n        var storeList = this._storeList;\n        var cachedStoreMap = storeList[sourceIndex];\n\n        if (!cachedStoreMap) {\n          cachedStoreMap = storeList[sourceIndex] = {};\n        }\n\n        var cachedStore = cachedStoreMap[sourceReadKey];\n\n        if (!cachedStore) {\n          var upSourceMgr = this._getUpstreamSourceManagers()[0];\n\n          if (isSeries(this._sourceHost) && upSourceMgr) {\n            cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey);\n          } else {\n            cachedStore = new DataStore(); // Always create store from source of series.\n\n            cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims);\n          }\n\n          cachedStoreMap[sourceReadKey] = cachedStore;\n        }\n\n        return cachedStore;\n      };\n      /**\n       * PENDING: Is it fast enough?\n       * If no upstream, return empty array.\n       */\n\n\n      SourceManager.prototype._getUpstreamSourceManagers = function () {\n        // Always get the relationship from the raw option.\n        // Do not cache the link of the dependency graph, so that\n        // there is no need to update them when change happens.\n        var sourceHost = this._sourceHost;\n\n        if (isSeries(sourceHost)) {\n          var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);\n          return !datasetModel ? [] : [datasetModel.getSourceManager()];\n        } else {\n          return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {\n            return datasetModel.getSourceManager();\n          });\n        }\n      };\n\n      SourceManager.prototype._getSourceMetaRawOption = function () {\n        var sourceHost = this._sourceHost;\n        var seriesLayoutBy;\n        var sourceHeader;\n        var dimensions;\n\n        if (isSeries(sourceHost)) {\n          seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);\n          sourceHeader = sourceHost.get('sourceHeader', true);\n          dimensions = sourceHost.get('dimensions', true);\n        } // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them.\n        else if (!this._getUpstreamSourceManagers().length) {\n            var model = sourceHost;\n            seriesLayoutBy = model.get('seriesLayoutBy', true);\n            sourceHeader = model.get('sourceHeader', true);\n            dimensions = model.get('dimensions', true);\n          }\n\n        return {\n          seriesLayoutBy: seriesLayoutBy,\n          sourceHeader: sourceHeader,\n          dimensions: dimensions\n        };\n      };\n\n      return SourceManager;\n    }();\n    // disable the transform merge, but do not disable transform clone from rawOption.\n\n    function disableTransformOptionMerge(datasetModel) {\n      var transformOption = datasetModel.option.transform;\n      transformOption && setAsPrimitive(datasetModel.option.transform);\n    }\n\n    function isSeries(sourceHost) {\n      // Avoid circular dependency with Series.ts\n      return sourceHost.mainType === 'series';\n    }\n\n    function doThrow(errMsg) {\n      throw new Error(errMsg);\n    }\n\n    function createTooltipMarkup(type, option) {\n      option.type = type;\n      return option;\n    }\n\n    function retrieveVisualColorForTooltipMarker(series, dataIndex) {\n      var style = series.getData().getItemVisual(dataIndex, 'style');\n      var color = style[series.visualDrawType];\n      return convertToColorString(color);\n    }\n\n    function defaultSeriesFormatTooltip(opt) {\n      var series = opt.series;\n      var dataIndex = opt.dataIndex;\n      var multipleSeries = opt.multipleSeries;\n      var data = series.getData();\n      var tooltipDims = data.mapDimensionsAll('defaultedTooltip');\n      var tooltipDimLen = tooltipDims.length;\n      var value = series.getRawValue(dataIndex);\n      var isValueArr = isArray(value);\n      var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip.\n\n      var inlineValue;\n      var inlineValueType;\n      var subBlocks;\n      var sortParam;\n\n      if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) {\n        var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);\n        inlineValue = formatArrResult.inlineValues;\n        inlineValueType = formatArrResult.inlineValueTypes;\n        subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases.\n\n        sortParam = formatArrResult.inlineValues[0];\n      } else if (tooltipDimLen) {\n        var dimInfo = data.getDimensionInfo(tooltipDims[0]);\n        sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]);\n        inlineValueType = dimInfo.type;\n      } else {\n        sortParam = inlineValue = isValueArr ? value[0] : value;\n      } // Do not show generated series name. It might not be readable.\n\n\n      var seriesNameSpecified = isNameSpecified(series);\n      var seriesName = seriesNameSpecified && series.name || '';\n      var itemName = data.getName(dataIndex);\n      var inlineName = multipleSeries ? seriesName : itemName;\n      return createTooltipMarkup('section', {\n        header: seriesName,\n        // When series name not specified, do not show a header line with only '-'.\n        // This case alway happen in tooltip.trigger: 'item'.\n        noHeader: multipleSeries || !seriesNameSpecified,\n        sortParam: sortParam,\n        blocks: [createTooltipMarkup('nameValue', {\n          markerType: 'item',\n          markerColor: markerColor,\n          // Do not mix display seriesName and itemName in one tooltip,\n          // which might confuses users.\n          name: inlineName,\n          // name dimension might be auto assigned, where the name might\n          // be not readable. So we check trim here.\n          noName: !trim(inlineName),\n          value: inlineValue,\n          valueType: inlineValueType\n        })].concat(subBlocks || [])\n      });\n    }\n\n    function formatTooltipArrayValue(value, series, dataIndex, tooltipDims, colorStr) {\n      // check: category-no-encode-has-axis-data in dataset.html\n      var data = series.getData();\n      var isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) {\n        var dimItem = data.getDimensionInfo(idx);\n        return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null;\n      }, false);\n      var inlineValues = [];\n      var inlineValueTypes = [];\n      var blocks = [];\n      tooltipDims.length ? each(tooltipDims, function (dim) {\n        setEachItem(retrieveRawValue(data, dataIndex, dim), dim);\n      }) // By default, all dims is used on tooltip.\n      : each(value, setEachItem);\n\n      function setEachItem(val, dim) {\n        var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip.\n\n        if (!dimInfo || dimInfo.otherDims.tooltip === false) {\n          return;\n        }\n\n        if (isValueMultipleLine) {\n          blocks.push(createTooltipMarkup('nameValue', {\n            markerType: 'subItem',\n            markerColor: colorStr,\n            name: dimInfo.displayName,\n            value: val,\n            valueType: dimInfo.type\n          }));\n        } else {\n          inlineValues.push(val);\n          inlineValueTypes.push(dimInfo.type);\n        }\n      }\n\n      return {\n        inlineValues: inlineValues,\n        inlineValueTypes: inlineValueTypes,\n        blocks: blocks\n      };\n    }\n\n    var inner$1 = makeInner();\n\n    function getSelectionKey(data, dataIndex) {\n      return data.getName(dataIndex) || data.getId(dataIndex);\n    }\n\n    var SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled';\n\n    var SeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(SeriesModel, _super);\n\n      function SeriesModel() {\n        // [Caution]: Because this class or desecendants can be used as `XXX.extend(subProto)`,\n        // the class members must not be initialized in constructor or declaration place.\n        // Otherwise there is bad case:\n        //   class A {xxx = 1;}\n        //   enableClassExtend(A);\n        //   class B extends A {}\n        //   var C = B.extend({xxx: 5});\n        //   var c = new C();\n        //   console.log(c.xxx); // expect 5 but always 1.\n        var _this = _super !== null && _super.apply(this, arguments) || this; // ---------------------------------------\n        // Props about data selection\n        // ---------------------------------------\n\n\n        _this._selectedDataIndicesMap = {};\n        return _this;\n      }\n\n      SeriesModel.prototype.init = function (option, parentModel, ecModel) {\n        this.seriesIndex = this.componentIndex;\n        this.dataTask = createTask({\n          count: dataTaskCount,\n          reset: dataTaskReset\n        });\n        this.dataTask.context = {\n          model: this\n        };\n        this.mergeDefaultAndTheme(option, ecModel);\n        var sourceManager = inner$1(this).sourceManager = new SourceManager(this);\n        sourceManager.prepareSource();\n        var data = this.getInitialData(option, ecModel);\n        wrapData(data, this);\n        this.dataTask.context.data = data;\n\n        if (\"development\" !== 'production') {\n          assert(data, 'getInitialData returned invalid data.');\n        }\n\n        inner$1(this).dataBeforeProcessed = data; // If we reverse the order (make data firstly, and then make\n        // dataBeforeProcessed by cloneShallow), cloneShallow will\n        // cause data.graph.data !== data when using\n        // module:echarts/data/Graph or module:echarts/data/Tree.\n        // See module:echarts/data/helper/linkSeriesData\n        // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model\n        // init or merge stage, because the data can be restored. So we do not `restoreData`\n        // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.\n        // Call `seriesModel.getRawData()` instead.\n        // this.restoreData();\n\n        autoSeriesName(this);\n\n        this._initSelectedMapFromData(data);\n      };\n      /**\n       * Util for merge default and theme to option\n       */\n\n\n      SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n        var layoutMode = fetchLayoutMode(this);\n        var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme.\n        // But if name duplicate between series subType\n        // (for example: parallel) add component mainType,\n        // add suffix 'Series'.\n\n        var themeSubType = this.subType;\n\n        if (ComponentModel.hasClass(themeSubType)) {\n          themeSubType += 'Series';\n        }\n\n        merge(option, ecModel.getTheme().get(this.subType));\n        merge(option, this.getDefaultOption()); // Default label emphasis `show`\n\n        defaultEmphasis(option, 'label', ['show']);\n        this.fillDataTextStyle(option.data);\n\n        if (layoutMode) {\n          mergeLayoutParam(option, inputPositionParams, layoutMode);\n        }\n      };\n\n      SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {\n        // this.settingTask.dirty();\n        newSeriesOption = merge(this.option, newSeriesOption, true);\n        this.fillDataTextStyle(newSeriesOption.data);\n        var layoutMode = fetchLayoutMode(this);\n\n        if (layoutMode) {\n          mergeLayoutParam(this.option, newSeriesOption, layoutMode);\n        }\n\n        var sourceManager = inner$1(this).sourceManager;\n        sourceManager.dirty();\n        sourceManager.prepareSource();\n        var data = this.getInitialData(newSeriesOption, ecModel);\n        wrapData(data, this);\n        this.dataTask.dirty();\n        this.dataTask.context.data = data;\n        inner$1(this).dataBeforeProcessed = data;\n        autoSeriesName(this);\n\n        this._initSelectedMapFromData(data);\n      };\n\n      SeriesModel.prototype.fillDataTextStyle = function (data) {\n        // Default data label emphasis `show`\n        // FIXME Tree structure data ?\n        // FIXME Performance ?\n        if (data && !isTypedArray(data)) {\n          var props = ['show'];\n\n          for (var i = 0; i < data.length; i++) {\n            if (data[i] && data[i].label) {\n              defaultEmphasis(data[i], 'label', props);\n            }\n          }\n        }\n      };\n      /**\n       * Init a data structure from data related option in series\n       * Must be overridden.\n       */\n\n\n      SeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return;\n      };\n      /**\n       * Append data to list\n       */\n\n\n      SeriesModel.prototype.appendData = function (params) {\n        // FIXME ???\n        // (1) If data from dataset, forbidden append.\n        // (2) support append data of dataset.\n        var data = this.getRawData();\n        data.appendData(params.data);\n      };\n      /**\n       * Consider some method like `filter`, `map` need make new data,\n       * We should make sure that `seriesModel.getData()` get correct\n       * data in the stream procedure. So we fetch data from upstream\n       * each time `task.perform` called.\n       */\n\n\n      SeriesModel.prototype.getData = function (dataType) {\n        var task = getCurrentTask(this);\n\n        if (task) {\n          var data = task.context.data;\n          return dataType == null ? data : data.getLinkedData(dataType);\n        } else {\n          // When series is not alive (that may happen when click toolbox\n          // restore or setOption with not merge mode), series data may\n          // be still need to judge animation or something when graphic\n          // elements want to know whether fade out.\n          return inner$1(this).data;\n        }\n      };\n\n      SeriesModel.prototype.getAllData = function () {\n        var mainData = this.getData();\n        return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{\n          data: mainData\n        }];\n      };\n\n      SeriesModel.prototype.setData = function (data) {\n        var task = getCurrentTask(this);\n\n        if (task) {\n          var context = task.context; // Consider case: filter, data sample.\n          // FIXME:TS never used, so comment it\n          // if (context.data !== data && task.modifyOutputEnd) {\n          //     task.setOutputEnd(data.count());\n          // }\n\n          context.outputData = data; // Caution: setData should update context.data,\n          // Because getData may be called multiply in a\n          // single stage and expect to get the data just\n          // set. (For example, AxisProxy, x y both call\n          // getData and setDate sequentially).\n          // So the context.data should be fetched from\n          // upstream each time when a stage starts to be\n          // performed.\n\n          if (task !== this.dataTask) {\n            context.data = data;\n          }\n        }\n\n        inner$1(this).data = data;\n      };\n\n      SeriesModel.prototype.getEncode = function () {\n        var encode = this.get('encode', true);\n\n        if (encode) {\n          return createHashMap(encode);\n        }\n      };\n\n      SeriesModel.prototype.getSourceManager = function () {\n        return inner$1(this).sourceManager;\n      };\n\n      SeriesModel.prototype.getSource = function () {\n        return this.getSourceManager().getSource();\n      };\n      /**\n       * Get data before processed\n       */\n\n\n      SeriesModel.prototype.getRawData = function () {\n        return inner$1(this).dataBeforeProcessed;\n      };\n\n      SeriesModel.prototype.getColorBy = function () {\n        var colorBy = this.get('colorBy');\n        return colorBy || 'series';\n      };\n\n      SeriesModel.prototype.isColorBySeries = function () {\n        return this.getColorBy() === 'series';\n      };\n      /**\n       * Get base axis if has coordinate system and has axis.\n       * By default use coordSys.getBaseAxis();\n       * Can be overridden for some chart.\n       * @return {type} description\n       */\n\n\n      SeriesModel.prototype.getBaseAxis = function () {\n        var coordSys = this.coordinateSystem; // @ts-ignore\n\n        return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();\n      };\n      /**\n       * Default tooltip formatter\n       *\n       * @param dataIndex\n       * @param multipleSeries\n       * @param dataType\n       * @param renderMode valid values: 'html'(by default) and 'richText'.\n       *        'html' is used for rendering tooltip in extra DOM form, and the result\n       *        string is used as DOM HTML content.\n       *        'richText' is used for rendering tooltip in rich text form, for those where\n       *        DOM operation is not supported.\n       * @return formatted tooltip with `html` and `markers`\n       *        Notice: The override method can also return string\n       */\n\n\n      SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {\n        return defaultSeriesFormatTooltip({\n          series: this,\n          dataIndex: dataIndex,\n          multipleSeries: multipleSeries\n        });\n      };\n\n      SeriesModel.prototype.isAnimationEnabled = function () {\n        var ecModel = this.ecModel; // Disable animation if using echarts in node but not give ssr flag.\n        // In ssr mode, renderToString will generate svg with css animation.\n\n        if (env.node && !(ecModel && ecModel.ssr)) {\n          return false;\n        }\n\n        var animationEnabled = this.getShallow('animation');\n\n        if (animationEnabled) {\n          if (this.getData().count() > this.getShallow('animationThreshold')) {\n            animationEnabled = false;\n          }\n        }\n\n        return !!animationEnabled;\n      };\n\n      SeriesModel.prototype.restoreData = function () {\n        this.dataTask.dirty();\n      };\n\n      SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {\n        var ecModel = this.ecModel; // PENDING\n\n        var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);\n\n        if (!color) {\n          color = ecModel.getColorFromPalette(name, scope, requestColorNum);\n        }\n\n        return color;\n      };\n      /**\n       * Use `data.mapDimensionsAll(coordDim)` instead.\n       * @deprecated\n       */\n\n\n      SeriesModel.prototype.coordDimToDataDim = function (coordDim) {\n        return this.getRawData().mapDimensionsAll(coordDim);\n      };\n      /**\n       * Get progressive rendering count each step\n       */\n\n\n      SeriesModel.prototype.getProgressive = function () {\n        return this.get('progressive');\n      };\n      /**\n       * Get progressive rendering count each step\n       */\n\n\n      SeriesModel.prototype.getProgressiveThreshold = function () {\n        return this.get('progressiveThreshold');\n      }; // PENGING If selectedMode is null ?\n\n\n      SeriesModel.prototype.select = function (innerDataIndices, dataType) {\n        this._innerSelect(this.getData(dataType), innerDataIndices);\n      };\n\n      SeriesModel.prototype.unselect = function (innerDataIndices, dataType) {\n        var selectedMap = this.option.selectedMap;\n\n        if (!selectedMap) {\n          return;\n        }\n\n        var selectedMode = this.option.selectedMode;\n        var data = this.getData(dataType);\n\n        if (selectedMode === 'series' || selectedMap === 'all') {\n          this.option.selectedMap = {};\n          this._selectedDataIndicesMap = {};\n          return;\n        }\n\n        for (var i = 0; i < innerDataIndices.length; i++) {\n          var dataIndex = innerDataIndices[i];\n          var nameOrId = getSelectionKey(data, dataIndex);\n          selectedMap[nameOrId] = false;\n          this._selectedDataIndicesMap[nameOrId] = -1;\n        }\n      };\n\n      SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) {\n        var tmpArr = [];\n\n        for (var i = 0; i < innerDataIndices.length; i++) {\n          tmpArr[0] = innerDataIndices[i];\n          this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType);\n        }\n      };\n\n      SeriesModel.prototype.getSelectedDataIndices = function () {\n        if (this.option.selectedMap === 'all') {\n          return [].slice.call(this.getData().getIndices());\n        }\n\n        var selectedDataIndicesMap = this._selectedDataIndicesMap;\n        var nameOrIds = keys(selectedDataIndicesMap);\n        var dataIndices = [];\n\n        for (var i = 0; i < nameOrIds.length; i++) {\n          var dataIndex = selectedDataIndicesMap[nameOrIds[i]];\n\n          if (dataIndex >= 0) {\n            dataIndices.push(dataIndex);\n          }\n        }\n\n        return dataIndices;\n      };\n\n      SeriesModel.prototype.isSelected = function (dataIndex, dataType) {\n        var selectedMap = this.option.selectedMap;\n\n        if (!selectedMap) {\n          return false;\n        }\n\n        var data = this.getData(dataType);\n        return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) && !data.getItemModel(dataIndex).get(['select', 'disabled']);\n      };\n\n      SeriesModel.prototype.isUniversalTransitionEnabled = function () {\n        if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) {\n          return true;\n        }\n\n        var universalTransitionOpt = this.option.universalTransition; // Quick reject\n\n        if (!universalTransitionOpt) {\n          return false;\n        }\n\n        if (universalTransitionOpt === true) {\n          return true;\n        } // Can be simply 'universalTransition: true'\n\n\n        return universalTransitionOpt && universalTransitionOpt.enabled;\n      };\n\n      SeriesModel.prototype._innerSelect = function (data, innerDataIndices) {\n        var _a, _b;\n\n        var option = this.option;\n        var selectedMode = option.selectedMode;\n        var len = innerDataIndices.length;\n\n        if (!selectedMode || !len) {\n          return;\n        }\n\n        if (selectedMode === 'series') {\n          option.selectedMap = 'all';\n        } else if (selectedMode === 'multiple') {\n          if (!isObject(option.selectedMap)) {\n            option.selectedMap = {};\n          }\n\n          var selectedMap = option.selectedMap;\n\n          for (var i = 0; i < len; i++) {\n            var dataIndex = innerDataIndices[i]; // TODO different types of data share same object.\n\n            var nameOrId = getSelectionKey(data, dataIndex);\n            selectedMap[nameOrId] = true;\n            this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex);\n          }\n        } else if (selectedMode === 'single' || selectedMode === true) {\n          var lastDataIndex = innerDataIndices[len - 1];\n          var nameOrId = getSelectionKey(data, lastDataIndex);\n          option.selectedMap = (_a = {}, _a[nameOrId] = true, _a);\n          this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b);\n        }\n      };\n\n      SeriesModel.prototype._initSelectedMapFromData = function (data) {\n        // Ignore select info in data if selectedMap exists.\n        // NOTE It's only for legacy usage. edge data is not supported.\n        if (this.option.selectedMap) {\n          return;\n        }\n\n        var dataIndices = [];\n\n        if (data.hasItemOption) {\n          data.each(function (idx) {\n            var rawItem = data.getRawDataItem(idx);\n\n            if (rawItem && rawItem.selected) {\n              dataIndices.push(idx);\n            }\n          });\n        }\n\n        if (dataIndices.length > 0) {\n          this._innerSelect(data, dataIndices);\n        }\n      }; // /**\n      //  * @see {module:echarts/stream/Scheduler}\n      //  */\n      // abstract pipeTask: null\n\n\n      SeriesModel.registerClass = function (clz) {\n        return ComponentModel.registerClass(clz);\n      };\n\n      SeriesModel.protoInitialize = function () {\n        var proto = SeriesModel.prototype;\n        proto.type = 'series.__base__';\n        proto.seriesIndex = 0;\n        proto.ignoreStyleOnData = false;\n        proto.hasSymbolVisual = false;\n        proto.defaultSymbol = 'circle'; // Make sure the values can be accessed!\n\n        proto.visualStyleAccessPath = 'itemStyle';\n        proto.visualDrawType = 'fill';\n      }();\n\n      return SeriesModel;\n    }(ComponentModel);\n\n    mixin(SeriesModel, DataFormatMixin);\n    mixin(SeriesModel, PaletteMixin);\n    mountExtend(SeriesModel, ComponentModel);\n    /**\n     * MUST be called after `prepareSource` called\n     * Here we need to make auto series, especially for auto legend. But we\n     * do not modify series.name in option to avoid side effects.\n     */\n\n    function autoSeriesName(seriesModel) {\n      // User specified name has higher priority, otherwise it may cause\n      // series can not be queried unexpectedly.\n      var name = seriesModel.name;\n\n      if (!isNameSpecified(seriesModel)) {\n        seriesModel.name = getSeriesAutoName(seriesModel) || name;\n      }\n    }\n\n    function getSeriesAutoName(seriesModel) {\n      var data = seriesModel.getRawData();\n      var dataDims = data.mapDimensionsAll('seriesName');\n      var nameArr = [];\n      each(dataDims, function (dataDim) {\n        var dimInfo = data.getDimensionInfo(dataDim);\n        dimInfo.displayName && nameArr.push(dimInfo.displayName);\n      });\n      return nameArr.join(' ');\n    }\n\n    function dataTaskCount(context) {\n      return context.model.getRawData().count();\n    }\n\n    function dataTaskReset(context) {\n      var seriesModel = context.model;\n      seriesModel.setData(seriesModel.getRawData().cloneShallow());\n      return dataTaskProgress;\n    }\n\n    function dataTaskProgress(param, context) {\n      // Avoid repead cloneShallow when data just created in reset.\n      if (context.outputData && param.end > context.outputData.count()) {\n        context.model.getRawData().cloneShallow(context.outputData);\n      }\n    } // TODO refactor\n\n\n    function wrapData(data, seriesModel) {\n      each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {\n        data.wrapMethod(methodName, curry(onDataChange, seriesModel));\n      });\n    }\n\n    function onDataChange(seriesModel, newList) {\n      var task = getCurrentTask(seriesModel);\n\n      if (task) {\n        // Consider case: filter, selectRange\n        task.setOutputEnd((newList || this).count());\n      }\n\n      return newList;\n    }\n\n    function getCurrentTask(seriesModel) {\n      var scheduler = (seriesModel.ecModel || {}).scheduler;\n      var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);\n\n      if (pipeline) {\n        // When pipline finished, the currrentTask keep the last\n        // task (renderTask).\n        var task = pipeline.currentTask;\n\n        if (task) {\n          var agentStubMap = task.agentStubMap;\n\n          if (agentStubMap) {\n            task = agentStubMap.get(seriesModel.uid);\n          }\n        }\n\n        return task;\n      }\n    }\n\n    var ComponentView =\n    /** @class */\n    function () {\n      function ComponentView() {\n        this.group = new Group();\n        this.uid = getUID('viewComponent');\n      }\n\n      ComponentView.prototype.init = function (ecModel, api) {};\n\n      ComponentView.prototype.render = function (model, ecModel, api, payload) {};\n\n      ComponentView.prototype.dispose = function (ecModel, api) {};\n\n      ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;\n      };\n\n      ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing;\n      };\n\n      ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing;\n      };\n      /**\n       * Hook for toggle blur target series.\n       * Can be used in marker for blur or leave blur the markers\n       */\n\n\n      ComponentView.prototype.toggleBlurSeries = function (seriesModels, isBlur, ecModel) {// Do nothing;\n      };\n      /**\n       * Traverse the new rendered elements.\n       *\n       * It will traverse the new added element in progressive rendering.\n       * And traverse all in normal rendering.\n       */\n\n\n      ComponentView.prototype.eachRendered = function (cb) {\n        var group = this.group;\n\n        if (group) {\n          group.traverse(cb);\n        }\n      };\n\n      return ComponentView;\n    }();\n    enableClassExtend(ComponentView);\n    enableClassManagement(ComponentView);\n\n    /**\n     * @return {string} If large mode changed, return string 'reset';\n     */\n\n    function createRenderPlanner() {\n      var inner = makeInner();\n      return function (seriesModel) {\n        var fields = inner(seriesModel);\n        var pipelineContext = seriesModel.pipelineContext;\n        var originalLarge = !!fields.large;\n        var originalProgressive = !!fields.progressiveRender; // FIXME: if the planner works on a filtered series, `pipelineContext` does not\n        // exists. See #11611 . Probably we need to modify this structure, see the comment\n        // on `performRawSeries` in `Schedular.js`.\n\n        var large = fields.large = !!(pipelineContext && pipelineContext.large);\n        var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender);\n        return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset';\n      };\n    }\n\n    var inner$2 = makeInner();\n    var renderPlanner = createRenderPlanner();\n\n    var ChartView =\n    /** @class */\n    function () {\n      function ChartView() {\n        this.group = new Group();\n        this.uid = getUID('viewChart');\n        this.renderTask = createTask({\n          plan: renderTaskPlan,\n          reset: renderTaskReset\n        });\n        this.renderTask.context = {\n          view: this\n        };\n      }\n\n      ChartView.prototype.init = function (ecModel, api) {};\n\n      ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        if (\"development\" !== 'production') {\n          throw new Error('render method must been implemented');\n        }\n      };\n      /**\n       * Highlight series or specified data item.\n       */\n\n\n      ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData(payload && payload.dataType);\n\n        if (!data) {\n          if (\"development\" !== 'production') {\n            error(\"Unknown dataType \" + payload.dataType);\n          }\n\n          return;\n        }\n\n        toggleHighlight(data, payload, 'emphasis');\n      };\n      /**\n       * Downplay series or specified data item.\n       */\n\n\n      ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData(payload && payload.dataType);\n\n        if (!data) {\n          if (\"development\" !== 'production') {\n            error(\"Unknown dataType \" + payload.dataType);\n          }\n\n          return;\n        }\n\n        toggleHighlight(data, payload, 'normal');\n      };\n      /**\n       * Remove self.\n       */\n\n\n      ChartView.prototype.remove = function (ecModel, api) {\n        this.group.removeAll();\n      };\n      /**\n       * Dispose self.\n       */\n\n\n      ChartView.prototype.dispose = function (ecModel, api) {};\n\n      ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      }; // FIXME never used?\n\n\n      ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      }; // FIXME never used?\n\n\n      ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {\n        this.render(seriesModel, ecModel, api, payload);\n      };\n      /**\n       * Traverse the new rendered elements.\n       *\n       * It will traverse the new added element in progressive rendering.\n       * And traverse all in normal rendering.\n       */\n\n\n      ChartView.prototype.eachRendered = function (cb) {\n        traverseElements(this.group, cb);\n      };\n\n      ChartView.markUpdateMethod = function (payload, methodName) {\n        inner$2(payload).updateMethod = methodName;\n      };\n\n      ChartView.protoInitialize = function () {\n        var proto = ChartView.prototype;\n        proto.type = 'chart';\n      }();\n\n      return ChartView;\n    }();\n    /**\n     * Set state of single element\n     */\n\n    function elSetState(el, state, highlightDigit) {\n      if (el && isHighDownDispatcher(el)) {\n        (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit);\n      }\n    }\n\n    function toggleHighlight(data, payload, state) {\n      var dataIndex = queryDataIndex(data, payload);\n      var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null;\n\n      if (dataIndex != null) {\n        each(normalizeToArray(dataIndex), function (dataIdx) {\n          elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);\n        });\n      } else {\n        data.eachItemGraphicEl(function (el) {\n          elSetState(el, state, highlightDigit);\n        });\n      }\n    }\n\n    enableClassExtend(ChartView, ['dispose']);\n    enableClassManagement(ChartView);\n\n    function renderTaskPlan(context) {\n      return renderPlanner(context.model);\n    }\n\n    function renderTaskReset(context) {\n      var seriesModel = context.model;\n      var ecModel = context.ecModel;\n      var api = context.api;\n      var payload = context.payload; // FIXME: remove updateView updateVisual\n\n      var progressiveRender = seriesModel.pipelineContext.progressiveRender;\n      var view = context.view;\n      var updateMethod = payload && inner$2(payload).updateMethod;\n      var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount\n      // is less than progressive threshold.\n      : 'render';\n\n      if (methodName !== 'render') {\n        view[methodName](seriesModel, ecModel, api, payload);\n      }\n\n      return progressMethodMap[methodName];\n    }\n\n    var progressMethodMap = {\n      incrementalPrepareRender: {\n        progress: function (params, context) {\n          context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload);\n        }\n      },\n      render: {\n        // Put view.render in `progress` to support appendData. But in this case\n        // view.render should not be called in reset, otherwise it will be called\n        // twise. Use `forceFirstProgress` to make sure that view.render is called\n        // in any cases.\n        forceFirstProgress: true,\n        progress: function (params, context) {\n          context.view.render(context.model, context.ecModel, context.api, context.payload);\n        }\n      }\n    };\n\n    /**\n     * @public\n     * @param {(Function)} fn\n     * @param {number} [delay=0] Unit: ms.\n     * @param {boolean} [debounce=false]\n     *        true: If call interval less than `delay`, only the last call works.\n     *        false: If call interval less than `delay, call works on fixed rate.\n     * @return {(Function)} throttled fn.\n     */\n\n    function throttle(fn, delay, debounce) {\n      var currCall;\n      var lastCall = 0;\n      var lastExec = 0;\n      var timer = null;\n      var diff;\n      var scope;\n      var args;\n      var debounceNextCall;\n      delay = delay || 0;\n\n      function exec() {\n        lastExec = new Date().getTime();\n        timer = null;\n        fn.apply(scope, args || []);\n      }\n\n      var cb = function () {\n        var cbArgs = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          cbArgs[_i] = arguments[_i];\n        }\n\n        currCall = new Date().getTime();\n        scope = this;\n        args = cbArgs;\n        var thisDelay = debounceNextCall || delay;\n        var thisDebounce = debounceNextCall || debounce;\n        debounceNextCall = null;\n        diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;\n        clearTimeout(timer); // Here we should make sure that: the `exec` SHOULD NOT be called later\n        // than a new call of `cb`, that is, preserving the command order. Consider\n        // calculating \"scale rate\" when roaming as an example. When a call of `cb`\n        // happens, either the `exec` is called dierectly, or the call is delayed.\n        // But the delayed call should never be later than next call of `cb`. Under\n        // this assurance, we can simply update view state each time `dispatchAction`\n        // triggered by user roaming, but not need to add extra code to avoid the\n        // state being \"rolled-back\".\n\n        if (thisDebounce) {\n          timer = setTimeout(exec, thisDelay);\n        } else {\n          if (diff >= 0) {\n            exec();\n          } else {\n            timer = setTimeout(exec, -diff);\n          }\n        }\n\n        lastCall = currCall;\n      };\n      /**\n       * Clear throttle.\n       * @public\n       */\n\n\n      cb.clear = function () {\n        if (timer) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      /**\n       * Enable debounce once.\n       */\n\n\n      cb.debounceNextCall = function (debounceDelay) {\n        debounceNextCall = debounceDelay;\n      };\n\n      return cb;\n    }\n\n    var inner$3 = makeInner();\n    var defaultStyleMappers = {\n      itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true),\n      lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true)\n    };\n    var defaultColorKey = {\n      lineStyle: 'stroke',\n      itemStyle: 'fill'\n    };\n\n    function getStyleMapper(seriesModel, stylePath) {\n      var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath];\n\n      if (!styleMapper) {\n        console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n        return defaultStyleMappers.itemStyle;\n      }\n\n      return styleMapper;\n    }\n\n    function getDefaultColorKey(seriesModel, stylePath) {\n      // return defaultColorKey[stylePath] ||\n      var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath];\n\n      if (!colorKey) {\n        console.warn(\"Unknown style type '\" + stylePath + \"'.\");\n        return 'fill';\n      }\n\n      return colorKey;\n    }\n\n    var seriesStyleTask = {\n      createOnAllSeries: true,\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var data = seriesModel.getData();\n        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n        var styleModel = seriesModel.getModel(stylePath);\n        var getStyle = getStyleMapper(seriesModel, stylePath);\n        var globalStyle = getStyle(styleModel);\n        var decalOption = styleModel.getShallow('decal');\n\n        if (decalOption) {\n          data.setVisual('decal', decalOption);\n          decalOption.dirty = true;\n        } // TODO\n\n\n        var colorKey = getDefaultColorKey(seriesModel, stylePath);\n        var color = globalStyle[colorKey]; // TODO style callback\n\n        var colorCallback = isFunction(color) ? color : null;\n        var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; // Get from color palette by default.\n\n        if (!globalStyle[colorKey] || colorCallback || hasAutoColor) {\n          // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT\n          // make it effect palette. Because some scenarios users need to make some series\n          // transparent or as background, which should better not effect the palette.\n          var colorPalette = seriesModel.getColorFromPalette( // TODO series count changed.\n          seriesModel.name, null, ecModel.getSeriesCount());\n\n          if (!globalStyle[colorKey]) {\n            globalStyle[colorKey] = colorPalette;\n            data.setVisual('colorFromPalette', true);\n          }\n\n          globalStyle.fill = globalStyle.fill === 'auto' || isFunction(globalStyle.fill) ? colorPalette : globalStyle.fill;\n          globalStyle.stroke = globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke) ? colorPalette : globalStyle.stroke;\n        }\n\n        data.setVisual('style', globalStyle);\n        data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded\n\n        if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) {\n          data.setVisual('colorFromPalette', false);\n          return {\n            dataEach: function (data, idx) {\n              var dataParams = seriesModel.getDataParams(idx);\n              var itemStyle = extend({}, globalStyle);\n              itemStyle[colorKey] = colorCallback(dataParams);\n              data.setItemVisual(idx, 'style', itemStyle);\n            }\n          };\n        }\n      }\n    };\n    var sharedModel = new Model();\n    var dataStyleTask = {\n      createOnAllSeries: true,\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle\n\n        var getStyle = getStyleMapper(seriesModel, stylePath);\n        var colorKey = data.getVisual('drawType');\n        return {\n          dataEach: data.hasItemOption ? function (data, idx) {\n            // Not use getItemModel for performance considuration\n            var rawItem = data.getRawDataItem(idx);\n\n            if (rawItem && rawItem[stylePath]) {\n              sharedModel.option = rawItem[stylePath];\n              var style = getStyle(sharedModel);\n              var existsStyle = data.ensureUniqueItemVisual(idx, 'style');\n              extend(existsStyle, style);\n\n              if (sharedModel.option.decal) {\n                data.setItemVisual(idx, 'decal', sharedModel.option.decal);\n                sharedModel.option.decal.dirty = true;\n              }\n\n              if (colorKey in style) {\n                data.setItemVisual(idx, 'colorFromPalette', false);\n              }\n            }\n          } : null\n        };\n      }\n    }; // Pick color from palette for the data which has not been set with color yet.\n    // Note: do not support stream rendering. No such cases yet.\n\n    var dataColorPaletteTask = {\n      performRawSeries: true,\n      overallReset: function (ecModel) {\n        // Each type of series uses one scope.\n        // Pie and funnel are using different scopes.\n        var paletteScopeGroupByType = createHashMap();\n        ecModel.eachSeries(function (seriesModel) {\n          var colorBy = seriesModel.getColorBy();\n\n          if (seriesModel.isColorBySeries()) {\n            return;\n          }\n\n          var key = seriesModel.type + '-' + colorBy;\n          var colorScope = paletteScopeGroupByType.get(key);\n\n          if (!colorScope) {\n            colorScope = {};\n            paletteScopeGroupByType.set(key, colorScope);\n          }\n\n          inner$3(seriesModel).scope = colorScope;\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) {\n            return;\n          }\n\n          var dataAll = seriesModel.getRawData();\n          var idxMap = {};\n          var data = seriesModel.getData();\n          var colorScope = inner$3(seriesModel).scope;\n          var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';\n          var colorKey = getDefaultColorKey(seriesModel, stylePath);\n          data.each(function (idx) {\n            var rawIdx = data.getRawIndex(idx);\n            idxMap[rawIdx] = idx;\n          }); // Iterate on data before filtered. To make sure color from palette can be\n          // Consistent when toggling legend.\n\n          dataAll.each(function (rawIdx) {\n            var idx = idxMap[rawIdx];\n            var fromPalette = data.getItemVisual(idx, 'colorFromPalette'); // Get color from palette for each data only when the color is inherited from series color, which is\n            // also picked from color palette. So following situation is not in the case:\n            // 1. series.itemStyle.color is set\n            // 2. color is encoded by visualMap\n\n            if (fromPalette) {\n              var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n              var name_1 = dataAll.getName(rawIdx) || rawIdx + '';\n              var dataCount = dataAll.count();\n              itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount);\n            }\n          });\n        });\n      }\n    };\n\n    var PI$3 = Math.PI;\n    /**\n     * @param {module:echarts/ExtensionAPI} api\n     * @param {Object} [opts]\n     * @param {string} [opts.text]\n     * @param {string} [opts.color]\n     * @param {string} [opts.textColor]\n     * @return {module:zrender/Element}\n     */\n\n    function defaultLoading(api, opts) {\n      opts = opts || {};\n      defaults(opts, {\n        text: 'loading',\n        textColor: '#000',\n        fontSize: 12,\n        fontWeight: 'normal',\n        fontStyle: 'normal',\n        fontFamily: 'sans-serif',\n        maskColor: 'rgba(255, 255, 255, 0.8)',\n        showSpinner: true,\n        color: '#5470c6',\n        spinnerRadius: 10,\n        lineWidth: 5,\n        zlevel: 0\n      });\n      var group = new Group();\n      var mask = new Rect({\n        style: {\n          fill: opts.maskColor\n        },\n        zlevel: opts.zlevel,\n        z: 10000\n      });\n      group.add(mask);\n      var textContent = new ZRText({\n        style: {\n          text: opts.text,\n          fill: opts.textColor,\n          fontSize: opts.fontSize,\n          fontWeight: opts.fontWeight,\n          fontStyle: opts.fontStyle,\n          fontFamily: opts.fontFamily\n        },\n        zlevel: opts.zlevel,\n        z: 10001\n      });\n      var labelRect = new Rect({\n        style: {\n          fill: 'none'\n        },\n        textContent: textContent,\n        textConfig: {\n          position: 'right',\n          distance: 10\n        },\n        zlevel: opts.zlevel,\n        z: 10001\n      });\n      group.add(labelRect);\n      var arc;\n\n      if (opts.showSpinner) {\n        arc = new Arc({\n          shape: {\n            startAngle: -PI$3 / 2,\n            endAngle: -PI$3 / 2 + 0.1,\n            r: opts.spinnerRadius\n          },\n          style: {\n            stroke: opts.color,\n            lineCap: 'round',\n            lineWidth: opts.lineWidth\n          },\n          zlevel: opts.zlevel,\n          z: 10001\n        });\n        arc.animateShape(true).when(1000, {\n          endAngle: PI$3 * 3 / 2\n        }).start('circularInOut');\n        arc.animateShape(true).when(1000, {\n          startAngle: PI$3 * 3 / 2\n        }).delay(300).start('circularInOut');\n        group.add(arc);\n      } // Inject resize\n\n\n      group.resize = function () {\n        var textWidth = textContent.getBoundingRect().width;\n        var r = opts.showSpinner ? opts.spinnerRadius : 0; // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2\n        // textDistance needs to be calculated when both animation and text exist\n\n        var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) // only show the text\n        + (opts.showSpinner ? 0 : textWidth / 2) // only show the spinner\n        + (textWidth ? 0 : r);\n        var cy = api.getHeight() / 2;\n        opts.showSpinner && arc.setShape({\n          cx: cx,\n          cy: cy\n        });\n        labelRect.setShape({\n          x: cx - r,\n          y: cy - r,\n          width: r * 2,\n          height: r * 2\n        });\n        mask.setShape({\n          x: 0,\n          y: 0,\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n      };\n\n      group.resize();\n      return group;\n    }\n\n    var Scheduler =\n    /** @class */\n    function () {\n      function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {\n        // key: handlerUID\n        this._stageTaskMap = createHashMap();\n        this.ecInstance = ecInstance;\n        this.api = api; // Fix current processors in case that in some rear cases that\n        // processors might be registered after echarts instance created.\n        // Register processors incrementally for a echarts instance is\n        // not supported by this stream architecture.\n\n        dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();\n        visualHandlers = this._visualHandlers = visualHandlers.slice();\n        this._allHandlers = dataProcessorHandlers.concat(visualHandlers);\n      }\n\n      Scheduler.prototype.restoreData = function (ecModel, payload) {\n        // TODO: Only restore needed series and components, but not all components.\n        // Currently `restoreData` of all of the series and component will be called.\n        // But some independent components like `title`, `legend`, `graphic`, `toolbox`,\n        // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`,\n        // and some components like coordinate system, axes, dataZoom, visualMap only\n        // need their target series refresh.\n        // (1) If we are implementing this feature some day, we should consider these cases:\n        // if a data processor depends on a component (e.g., dataZoomProcessor depends\n        // on the settings of `dataZoom`), it should be re-performed if the component\n        // is modified by `setOption`.\n        // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`,\n        // it should be re-performed when the result array of `getTargetSeries` changed.\n        // We use `dependencies` to cover these issues.\n        // (3) How to update target series when coordinate system related components modified.\n        // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty,\n        // and this case all of the tasks will be set as dirty.\n        ecModel.restoreData(payload); // Theoretically an overall task not only depends on each of its target series, but also\n        // depends on all of the series.\n        // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks\n        // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure\n        // that the overall task is set as dirty and to be performed, otherwise it probably cause\n        // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it\n        // probably cause state chaos (consider `dataZoomProcessor`).\n\n        this._stageTaskMap.each(function (taskRecord) {\n          var overallTask = taskRecord.overallTask;\n          overallTask && overallTask.dirty();\n        });\n      }; // If seriesModel provided, incremental threshold is check by series data.\n\n\n      Scheduler.prototype.getPerformArgs = function (task, isBlock) {\n        // For overall task\n        if (!task.__pipeline) {\n          return;\n        }\n\n        var pipeline = this._pipelineMap.get(task.__pipeline.id);\n\n        var pCtx = pipeline.context;\n        var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex;\n        var step = incremental ? pipeline.step : null;\n        var modDataCount = pCtx && pCtx.modDataCount;\n        var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null;\n        return {\n          step: step,\n          modBy: modBy,\n          modDataCount: modDataCount\n        };\n      };\n\n      Scheduler.prototype.getPipeline = function (pipelineId) {\n        return this._pipelineMap.get(pipelineId);\n      };\n      /**\n       * Current, progressive rendering starts from visual and layout.\n       * Always detect render mode in the same stage, avoiding that incorrect\n       * detection caused by data filtering.\n       * Caution:\n       * `updateStreamModes` use `seriesModel.getData()`.\n       */\n\n\n      Scheduler.prototype.updateStreamModes = function (seriesModel, view) {\n        var pipeline = this._pipelineMap.get(seriesModel.uid);\n\n        var data = seriesModel.getData();\n        var dataLen = data.count(); // `progressiveRender` means that can render progressively in each\n        // animation frame. Note that some types of series do not provide\n        // `view.incrementalPrepareRender` but support `chart.appendData`. We\n        // use the term `incremental` but not `progressive` to describe the\n        // case that `chart.appendData`.\n\n        var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;\n        var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.\n        // see `test/candlestick-large3.html`\n\n        var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;\n        seriesModel.pipelineContext = pipeline.context = {\n          progressiveRender: progressiveRender,\n          modDataCount: modDataCount,\n          large: large\n        };\n      };\n\n      Scheduler.prototype.restorePipelines = function (ecModel) {\n        var scheduler = this;\n        var pipelineMap = scheduler._pipelineMap = createHashMap();\n        ecModel.eachSeries(function (seriesModel) {\n          var progressive = seriesModel.getProgressive();\n          var pipelineId = seriesModel.uid;\n          pipelineMap.set(pipelineId, {\n            id: pipelineId,\n            head: null,\n            tail: null,\n            threshold: seriesModel.getProgressiveThreshold(),\n            progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),\n            blockIndex: -1,\n            step: Math.round(progressive || 700),\n            count: 0\n          });\n\n          scheduler._pipe(seriesModel, seriesModel.dataTask);\n        });\n      };\n\n      Scheduler.prototype.prepareStageTasks = function () {\n        var stageTaskMap = this._stageTaskMap;\n        var ecModel = this.api.getModel();\n        var api = this.api;\n        each(this._allHandlers, function (handler) {\n          var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {});\n          var errMsg = '';\n\n          if (\"development\" !== 'production') {\n            // Currently do not need to support to sepecify them both.\n            errMsg = '\"reset\" and \"overallReset\" must not be both specified.';\n          }\n\n          assert(!(handler.reset && handler.overallReset), errMsg);\n          handler.reset && this._createSeriesStageTask(handler, record, ecModel, api);\n          handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api);\n        }, this);\n      };\n\n      Scheduler.prototype.prepareView = function (view, model, ecModel, api) {\n        var renderTask = view.renderTask;\n        var context = renderTask.context;\n        context.model = model;\n        context.ecModel = ecModel;\n        context.api = api;\n        renderTask.__block = !view.incrementalPrepareRender;\n\n        this._pipe(model, renderTask);\n      };\n\n      Scheduler.prototype.performDataProcessorTasks = function (ecModel, payload) {\n        // If we do not use `block` here, it should be considered when to update modes.\n        this._performStageTasks(this._dataProcessorHandlers, ecModel, payload, {\n          block: true\n        });\n      };\n\n      Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) {\n        this._performStageTasks(this._visualHandlers, ecModel, payload, opt);\n      };\n\n      Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) {\n        opt = opt || {};\n        var unfinished = false;\n        var scheduler = this;\n        each(stageHandlers, function (stageHandler, idx) {\n          if (opt.visualType && opt.visualType !== stageHandler.visualType) {\n            return;\n          }\n\n          var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);\n\n          var seriesTaskMap = stageHandlerRecord.seriesTaskMap;\n          var overallTask = stageHandlerRecord.overallTask;\n\n          if (overallTask) {\n            var overallNeedDirty_1;\n            var agentStubMap = overallTask.agentStubMap;\n            agentStubMap.each(function (stub) {\n              if (needSetDirty(opt, stub)) {\n                stub.dirty();\n                overallNeedDirty_1 = true;\n              }\n            });\n            overallNeedDirty_1 && overallTask.dirty();\n            scheduler.updatePayload(overallTask, payload);\n            var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block); // Execute stubs firstly, which may set the overall task dirty,\n            // then execute the overall task. And stub will call seriesModel.setData,\n            // which ensures that in the overallTask seriesModel.getData() will not\n            // return incorrect data.\n\n            agentStubMap.each(function (stub) {\n              stub.perform(performArgs_1);\n            });\n\n            if (overallTask.perform(performArgs_1)) {\n              unfinished = true;\n            }\n          } else if (seriesTaskMap) {\n            seriesTaskMap.each(function (task, pipelineId) {\n              if (needSetDirty(opt, task)) {\n                task.dirty();\n              }\n\n              var performArgs = scheduler.getPerformArgs(task, opt.block); // FIXME\n              // if intending to declare `performRawSeries` in handlers, only\n              // stream-independent (specifically, data item independent) operations can be\n              // performed. Because if a series is filtered, most of the tasks will not\n              // be performed. A stream-dependent operation probably cause wrong biz logic.\n              // Perhaps we should not provide a separate callback for this case instead\n              // of providing the config `performRawSeries`. The stream-dependent operations\n              // and stream-independent operations should better not be mixed.\n\n              performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);\n              scheduler.updatePayload(task, payload);\n\n              if (task.perform(performArgs)) {\n                unfinished = true;\n              }\n            });\n          }\n        });\n\n        function needSetDirty(opt, task) {\n          return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));\n        }\n\n        this.unfinished = unfinished || this.unfinished;\n      };\n\n      Scheduler.prototype.performSeriesTasks = function (ecModel) {\n        var unfinished;\n        ecModel.eachSeries(function (seriesModel) {\n          // Progress to the end for dataInit and dataRestore.\n          unfinished = seriesModel.dataTask.perform() || unfinished;\n        });\n        this.unfinished = unfinished || this.unfinished;\n      };\n\n      Scheduler.prototype.plan = function () {\n        // Travel pipelines, check block.\n        this._pipelineMap.each(function (pipeline) {\n          var task = pipeline.tail;\n\n          do {\n            if (task.__block) {\n              pipeline.blockIndex = task.__idxInPipeline;\n              break;\n            }\n\n            task = task.getUpstream();\n          } while (task);\n        });\n      };\n\n      Scheduler.prototype.updatePayload = function (task, payload) {\n        payload !== 'remain' && (task.context.payload = payload);\n      };\n\n      Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n        var scheduler = this;\n        var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; // The count of stages are totally about only several dozen, so\n        // do not need to reuse the map.\n\n        var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap();\n        var seriesType = stageHandler.seriesType;\n        var getTargetSeries = stageHandler.getTargetSeries; // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,\n        // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,\n        // it works but it may cause other irrelevant charts blocked.\n\n        if (stageHandler.createOnAllSeries) {\n          ecModel.eachRawSeries(create);\n        } else if (seriesType) {\n          ecModel.eachRawSeriesByType(seriesType, create);\n        } else if (getTargetSeries) {\n          getTargetSeries(ecModel, api).each(create);\n        }\n\n        function create(seriesModel) {\n          var pipelineId = seriesModel.uid; // Init tasks for each seriesModel only once.\n          // Reuse original task instance.\n\n          var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({\n            plan: seriesTaskPlan,\n            reset: seriesTaskReset,\n            count: seriesTaskCount\n          }));\n          task.context = {\n            model: seriesModel,\n            ecModel: ecModel,\n            api: api,\n            // PENDING: `useClearVisual` not used?\n            useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,\n            plan: stageHandler.plan,\n            reset: stageHandler.reset,\n            scheduler: scheduler\n          };\n\n          scheduler._pipe(seriesModel, task);\n        }\n      };\n\n      Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {\n        var scheduler = this;\n        var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage.\n        || createTask({\n          reset: overallTaskReset\n        });\n        overallTask.context = {\n          ecModel: ecModel,\n          api: api,\n          overallReset: stageHandler.overallReset,\n          scheduler: scheduler\n        };\n        var oldAgentStubMap = overallTask.agentStubMap; // The count of stages are totally about only several dozen, so\n        // do not need to reuse the map.\n\n        var newAgentStubMap = overallTask.agentStubMap = createHashMap();\n        var seriesType = stageHandler.seriesType;\n        var getTargetSeries = stageHandler.getTargetSeries;\n        var overallProgress = true;\n        var shouldOverallTaskDirty = false; // FIXME:TS never used, so comment it\n        // let modifyOutputEnd = stageHandler.modifyOutputEnd;\n        // An overall task with seriesType detected or has `getTargetSeries`, we add\n        // stub in each pipelines, it will set the overall task dirty when the pipeline\n        // progress. Moreover, to avoid call the overall task each frame (too frequent),\n        // we set the pipeline block.\n\n        var errMsg = '';\n\n        if (\"development\" !== 'production') {\n          errMsg = '\"createOnAllSeries\" is not supported for \"overallReset\", ' + 'because it will block all streams.';\n        }\n\n        assert(!stageHandler.createOnAllSeries, errMsg);\n\n        if (seriesType) {\n          ecModel.eachRawSeriesByType(seriesType, createStub);\n        } else if (getTargetSeries) {\n          getTargetSeries(ecModel, api).each(createStub);\n        } // Otherwise, (usually it is legacy case), the overall task will only be\n        // executed when upstream is dirty. Otherwise the progressive rendering of all\n        // pipelines will be disabled unexpectedly. But it still needs stubs to receive\n        // dirty info from upstream.\n        else {\n            overallProgress = false;\n            each(ecModel.getSeries(), createStub);\n          }\n\n        function createStub(seriesModel) {\n          var pipelineId = seriesModel.uid;\n          var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || ( // When the result of `getTargetSeries` changed, the overallTask\n          // should be set as dirty and re-performed.\n          shouldOverallTaskDirty = true, createTask({\n            reset: stubReset,\n            onDirty: stubOnDirty\n          })));\n          stub.context = {\n            model: seriesModel,\n            overallProgress: overallProgress // FIXME:TS never used, so comment it\n            // modifyOutputEnd: modifyOutputEnd\n\n          };\n          stub.agent = overallTask;\n          stub.__block = overallProgress;\n\n          scheduler._pipe(seriesModel, stub);\n        }\n\n        if (shouldOverallTaskDirty) {\n          overallTask.dirty();\n        }\n      };\n\n      Scheduler.prototype._pipe = function (seriesModel, task) {\n        var pipelineId = seriesModel.uid;\n\n        var pipeline = this._pipelineMap.get(pipelineId);\n\n        !pipeline.head && (pipeline.head = task);\n        pipeline.tail && pipeline.tail.pipe(task);\n        pipeline.tail = task;\n        task.__idxInPipeline = pipeline.count++;\n        task.__pipeline = pipeline;\n      };\n\n      Scheduler.wrapStageHandler = function (stageHandler, visualType) {\n        if (isFunction(stageHandler)) {\n          stageHandler = {\n            overallReset: stageHandler,\n            seriesType: detectSeriseType(stageHandler)\n          };\n        }\n\n        stageHandler.uid = getUID('stageHandler');\n        visualType && (stageHandler.visualType = visualType);\n        return stageHandler;\n      };\n      return Scheduler;\n    }();\n\n    function overallTaskReset(context) {\n      context.overallReset(context.ecModel, context.api, context.payload);\n    }\n\n    function stubReset(context) {\n      return context.overallProgress && stubProgress;\n    }\n\n    function stubProgress() {\n      this.agent.dirty();\n      this.getDownstream().dirty();\n    }\n\n    function stubOnDirty() {\n      this.agent && this.agent.dirty();\n    }\n\n    function seriesTaskPlan(context) {\n      return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null;\n    }\n\n    function seriesTaskReset(context) {\n      if (context.useClearVisual) {\n        context.data.clearAllVisual();\n      }\n\n      var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload));\n      return resetDefines.length > 1 ? map(resetDefines, function (v, idx) {\n        return makeSeriesTaskProgress(idx);\n      }) : singleSeriesTaskProgress;\n    }\n\n    var singleSeriesTaskProgress = makeSeriesTaskProgress(0);\n\n    function makeSeriesTaskProgress(resetDefineIdx) {\n      return function (params, context) {\n        var data = context.data;\n        var resetDefine = context.resetDefines[resetDefineIdx];\n\n        if (resetDefine && resetDefine.dataEach) {\n          for (var i = params.start; i < params.end; i++) {\n            resetDefine.dataEach(data, i);\n          }\n        } else if (resetDefine && resetDefine.progress) {\n          resetDefine.progress(params, data);\n        }\n      };\n    }\n\n    function seriesTaskCount(context) {\n      return context.data.count();\n    }\n    /**\n     * Only some legacy stage handlers (usually in echarts extensions) are pure function.\n     * To ensure that they can work normally, they should work in block mode, that is,\n     * they should not be started util the previous tasks finished. So they cause the\n     * progressive rendering disabled. We try to detect the series type, to narrow down\n     * the block range to only the series type they concern, but not all series.\n     */\n\n\n    function detectSeriseType(legacyFunc) {\n      seriesType = null;\n\n      try {\n        // Assume there is no async when calling `eachSeriesByType`.\n        legacyFunc(ecModelMock, apiMock);\n      } catch (e) {}\n\n      return seriesType;\n    }\n\n    var ecModelMock = {};\n    var apiMock = {};\n    var seriesType;\n    mockMethods(ecModelMock, GlobalModel);\n    mockMethods(apiMock, ExtensionAPI);\n\n    ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {\n      seriesType = type;\n    };\n\n    ecModelMock.eachComponent = function (cond) {\n      if (cond.mainType === 'series' && cond.subType) {\n        seriesType = cond.subType;\n      }\n    };\n\n    function mockMethods(target, Clz) {\n      /* eslint-disable */\n      for (var name_1 in Clz.prototype) {\n        // Do not use hasOwnProperty\n        target[name_1] = noop;\n      }\n      /* eslint-enable */\n\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];\n    var lightTheme = {\n      color: colorAll,\n      colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll]\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var contrastColor = '#B9B8CE';\n    var backgroundColor = '#100C2A';\n\n    var axisCommon = function () {\n      return {\n        axisLine: {\n          lineStyle: {\n            color: contrastColor\n          }\n        },\n        splitLine: {\n          lineStyle: {\n            color: '#484753'\n          }\n        },\n        splitArea: {\n          areaStyle: {\n            color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)']\n          }\n        },\n        minorSplitLine: {\n          lineStyle: {\n            color: '#20203B'\n          }\n        }\n      };\n    };\n\n    var colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff'];\n    var theme = {\n      darkMode: true,\n      color: colorPalette,\n      backgroundColor: backgroundColor,\n      axisPointer: {\n        lineStyle: {\n          color: '#817f91'\n        },\n        crossStyle: {\n          color: '#817f91'\n        },\n        label: {\n          // TODO Contrast of label backgorundColor\n          color: '#fff'\n        }\n      },\n      legend: {\n        textStyle: {\n          color: contrastColor\n        }\n      },\n      textStyle: {\n        color: contrastColor\n      },\n      title: {\n        textStyle: {\n          color: '#EEF1FA'\n        },\n        subtextStyle: {\n          color: '#B9B8CE'\n        }\n      },\n      toolbox: {\n        iconStyle: {\n          borderColor: contrastColor\n        }\n      },\n      dataZoom: {\n        borderColor: '#71708A',\n        textStyle: {\n          color: contrastColor\n        },\n        brushStyle: {\n          color: 'rgba(135,163,206,0.3)'\n        },\n        handleStyle: {\n          color: '#353450',\n          borderColor: '#C5CBE3'\n        },\n        moveHandleStyle: {\n          color: '#B0B6C3',\n          opacity: 0.3\n        },\n        fillerColor: 'rgba(135,163,206,0.2)',\n        emphasis: {\n          handleStyle: {\n            borderColor: '#91B7F2',\n            color: '#4D587D'\n          },\n          moveHandleStyle: {\n            color: '#636D9A',\n            opacity: 0.7\n          }\n        },\n        dataBackground: {\n          lineStyle: {\n            color: '#71708A',\n            width: 1\n          },\n          areaStyle: {\n            color: '#71708A'\n          }\n        },\n        selectedDataBackground: {\n          lineStyle: {\n            color: '#87A3CE'\n          },\n          areaStyle: {\n            color: '#87A3CE'\n          }\n        }\n      },\n      visualMap: {\n        textStyle: {\n          color: contrastColor\n        }\n      },\n      timeline: {\n        lineStyle: {\n          color: contrastColor\n        },\n        label: {\n          color: contrastColor\n        },\n        controlStyle: {\n          color: contrastColor,\n          borderColor: contrastColor\n        }\n      },\n      calendar: {\n        itemStyle: {\n          color: backgroundColor\n        },\n        dayLabel: {\n          color: contrastColor\n        },\n        monthLabel: {\n          color: contrastColor\n        },\n        yearLabel: {\n          color: contrastColor\n        }\n      },\n      timeAxis: axisCommon(),\n      logAxis: axisCommon(),\n      valueAxis: axisCommon(),\n      categoryAxis: axisCommon(),\n      line: {\n        symbol: 'circle'\n      },\n      graph: {\n        color: colorPalette\n      },\n      gauge: {\n        title: {\n          color: contrastColor\n        },\n        axisLine: {\n          lineStyle: {\n            color: [[1, 'rgba(207,212,219,0.2)']]\n          }\n        },\n        axisLabel: {\n          color: contrastColor\n        },\n        detail: {\n          color: '#EEF1FA'\n        }\n      },\n      candlestick: {\n        itemStyle: {\n          color: '#f64e56',\n          color0: '#54ea92',\n          borderColor: '#f64e56',\n          borderColor0: '#54ea92' // borderColor: '#ca2824',\n          // borderColor0: '#09a443'\n\n        }\n      }\n    };\n    theme.categoryAxis.splitLine.show = false;\n\n    /**\n     * Usage of query:\n     * `chart.on('click', query, handler);`\n     * The `query` can be:\n     * + The component type query string, only `mainType` or `mainType.subType`,\n     *   like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.\n     * + The component query object, like:\n     *   `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,\n     *   `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.\n     * + The data query object, like:\n     *   `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.\n     * + The other query object (cmponent customized query), like:\n     *   `{element: 'some'}` (only available in custom series).\n     *\n     * Caveat: If a prop in the `query` object is `null/undefined`, it is the\n     * same as there is no such prop in the `query` object.\n     */\n\n    var ECEventProcessor =\n    /** @class */\n    function () {\n      function ECEventProcessor() {}\n\n      ECEventProcessor.prototype.normalizeQuery = function (query) {\n        var cptQuery = {};\n        var dataQuery = {};\n        var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.\n\n        if (isString(query)) {\n          var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.\n\n          cptQuery.mainType = condCptType.main || null;\n          cptQuery.subType = condCptType.sub || null;\n        } // `query` is an object, convert to {mainType, index, name, id}.\n        else {\n            // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,\n            // can not be used in `compomentModel.filterForExposedEvent`.\n            var suffixes_1 = ['Index', 'Name', 'Id'];\n            var dataKeys_1 = {\n              name: 1,\n              dataIndex: 1,\n              dataType: 1\n            };\n            each(query, function (val, key) {\n              var reserved = false;\n\n              for (var i = 0; i < suffixes_1.length; i++) {\n                var propSuffix = suffixes_1[i];\n                var suffixPos = key.lastIndexOf(propSuffix);\n\n                if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {\n                  var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.\n\n                  if (mainType !== 'data') {\n                    cptQuery.mainType = mainType;\n                    cptQuery[propSuffix.toLowerCase()] = val;\n                    reserved = true;\n                  }\n                }\n              }\n\n              if (dataKeys_1.hasOwnProperty(key)) {\n                dataQuery[key] = val;\n                reserved = true;\n              }\n\n              if (!reserved) {\n                otherQuery[key] = val;\n              }\n            });\n          }\n\n        return {\n          cptQuery: cptQuery,\n          dataQuery: dataQuery,\n          otherQuery: otherQuery\n        };\n      };\n\n      ECEventProcessor.prototype.filter = function (eventType, query) {\n        // They should be assigned before each trigger call.\n        var eventInfo = this.eventInfo;\n\n        if (!eventInfo) {\n          return true;\n        }\n\n        var targetEl = eventInfo.targetEl;\n        var packedEvent = eventInfo.packedEvent;\n        var model = eventInfo.model;\n        var view = eventInfo.view; // For event like 'globalout'.\n\n        if (!model || !view) {\n          return true;\n        }\n\n        var cptQuery = query.cptQuery;\n        var dataQuery = query.dataQuery;\n        return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent));\n\n        function check(query, host, prop, propOnHost) {\n          return query[prop] == null || host[propOnHost || prop] === query[prop];\n        }\n      };\n\n      ECEventProcessor.prototype.afterTrigger = function () {\n        // Make sure the eventInfo won't be used in next trigger.\n        this.eventInfo = null;\n      };\n\n      return ECEventProcessor;\n    }();\n\n    var SYMBOL_PROPS_WITH_CB = ['symbol', 'symbolSize', 'symbolRotate', 'symbolOffset'];\n    var SYMBOL_PROPS = SYMBOL_PROPS_WITH_CB.concat(['symbolKeepAspect']); // Encoding visual for all series include which is filtered for legend drawing\n\n    var seriesSymbolTask = {\n      createOnAllSeries: true,\n      // For legend.\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        var data = seriesModel.getData();\n\n        if (seriesModel.legendIcon) {\n          data.setVisual('legendIcon', seriesModel.legendIcon);\n        }\n\n        if (!seriesModel.hasSymbolVisual) {\n          return;\n        }\n\n        var symbolOptions = {};\n        var symbolOptionsCb = {};\n        var hasCallback = false;\n\n        for (var i = 0; i < SYMBOL_PROPS_WITH_CB.length; i++) {\n          var symbolPropName = SYMBOL_PROPS_WITH_CB[i];\n          var val = seriesModel.get(symbolPropName);\n\n          if (isFunction(val)) {\n            hasCallback = true;\n            symbolOptionsCb[symbolPropName] = val;\n          } else {\n            symbolOptions[symbolPropName] = val;\n          }\n        }\n\n        symbolOptions.symbol = symbolOptions.symbol || seriesModel.defaultSymbol;\n        data.setVisual(extend({\n          legendIcon: seriesModel.legendIcon || symbolOptions.symbol,\n          symbolKeepAspect: seriesModel.get('symbolKeepAspect')\n        }, symbolOptions)); // Only visible series has each data be visual encoded\n\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var symbolPropsCb = keys(symbolOptionsCb);\n\n        function dataEach(data, idx) {\n          var rawValue = seriesModel.getRawValue(idx);\n          var params = seriesModel.getDataParams(idx);\n\n          for (var i = 0; i < symbolPropsCb.length; i++) {\n            var symbolPropName = symbolPropsCb[i];\n            data.setItemVisual(idx, symbolPropName, symbolOptionsCb[symbolPropName](rawValue, params));\n          }\n        }\n\n        return {\n          dataEach: hasCallback ? dataEach : null\n        };\n      }\n    };\n    var dataSymbolTask = {\n      createOnAllSeries: true,\n      // For legend.\n      performRawSeries: true,\n      reset: function (seriesModel, ecModel) {\n        if (!seriesModel.hasSymbolVisual) {\n          return;\n        } // Only visible series has each data be visual encoded\n\n\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n\n        function dataEach(data, idx) {\n          var itemModel = data.getItemModel(idx);\n\n          for (var i = 0; i < SYMBOL_PROPS.length; i++) {\n            var symbolPropName = SYMBOL_PROPS[i];\n            var val = itemModel.getShallow(symbolPropName, true);\n\n            if (val != null) {\n              data.setItemVisual(idx, symbolPropName, val);\n            }\n          }\n        }\n\n        return {\n          dataEach: data.hasItemOption ? dataEach : null\n        };\n      }\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function getItemVisualFromData(data, dataIndex, key) {\n      switch (key) {\n        case 'color':\n          var style = data.getItemVisual(dataIndex, 'style');\n          return style[data.getVisual('drawType')];\n\n        case 'opacity':\n          return data.getItemVisual(dataIndex, 'style').opacity;\n\n        case 'symbol':\n        case 'symbolSize':\n        case 'liftZ':\n          return data.getItemVisual(dataIndex, key);\n\n        default:\n          if (\"development\" !== 'production') {\n            console.warn(\"Unknown visual type \" + key);\n          }\n\n      }\n    }\n    function getVisualFromData(data, key) {\n      switch (key) {\n        case 'color':\n          var style = data.getVisual('style');\n          return style[data.getVisual('drawType')];\n\n        case 'opacity':\n          return data.getVisual('style').opacity;\n\n        case 'symbol':\n        case 'symbolSize':\n        case 'liftZ':\n          return data.getVisual(key);\n\n        default:\n          if (\"development\" !== 'production') {\n            console.warn(\"Unknown visual type \" + key);\n          }\n\n      }\n    }\n\n    // Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect\n\n    function createLegacyDataSelectAction(seriesType, ecRegisterAction) {\n      function getSeriesIndices(ecModel, payload) {\n        var seriesIndices = [];\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: seriesType,\n          query: payload\n        }, function (seriesModel) {\n          seriesIndices.push(seriesModel.seriesIndex);\n        });\n        return seriesIndices;\n      }\n\n      each([[seriesType + 'ToggleSelect', 'toggleSelect'], [seriesType + 'Select', 'select'], [seriesType + 'UnSelect', 'unselect']], function (eventsMap) {\n        ecRegisterAction(eventsMap[0], function (payload, ecModel, api) {\n          payload = extend({}, payload);\n\n          if (\"development\" !== 'production') {\n            deprecateReplaceLog(payload.type, eventsMap[1]);\n          }\n\n          api.dispatchAction(extend(payload, {\n            type: eventsMap[1],\n            seriesIndex: getSeriesIndices(ecModel, payload)\n          }));\n        });\n      });\n    }\n\n    function handleSeriesLegacySelectEvents(type, eventPostfix, ecIns, ecModel, payload) {\n      var legacyEventName = type + eventPostfix;\n\n      if (!ecIns.isSilent(legacyEventName)) {\n        if (\"development\" !== 'production') {\n          deprecateLog(\"event \" + legacyEventName + \" is deprecated.\");\n        }\n\n        ecModel.eachComponent({\n          mainType: 'series',\n          subType: 'pie'\n        }, function (seriesModel) {\n          var seriesIndex = seriesModel.seriesIndex;\n          var selectedMap = seriesModel.option.selectedMap;\n          var selected = payload.selected;\n\n          for (var i = 0; i < selected.length; i++) {\n            if (selected[i].seriesIndex === seriesIndex) {\n              var data = seriesModel.getData();\n              var dataIndex = queryDataIndex(data, payload.fromActionPayload);\n              ecIns.trigger(legacyEventName, {\n                type: legacyEventName,\n                seriesId: seriesModel.id,\n                name: isArray(dataIndex) ? data.getName(dataIndex[0]) : data.getName(dataIndex),\n                selected: isString(selectedMap) ? selectedMap : extend({}, selectedMap)\n              });\n            }\n          }\n        });\n      }\n    }\n\n    function handleLegacySelectEvents(messageCenter, ecIns, api) {\n      messageCenter.on('selectchanged', function (params) {\n        var ecModel = api.getModel();\n\n        if (params.isFromClick) {\n          handleSeriesLegacySelectEvents('map', 'selectchanged', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'selectchanged', ecIns, ecModel, params);\n        } else if (params.fromAction === 'select') {\n          handleSeriesLegacySelectEvents('map', 'selected', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'selected', ecIns, ecModel, params);\n        } else if (params.fromAction === 'unselect') {\n          handleSeriesLegacySelectEvents('map', 'unselected', ecIns, ecModel, params);\n          handleSeriesLegacySelectEvents('pie', 'unselected', ecIns, ecModel, params);\n        }\n      });\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function findEventDispatcher(target, det, returnFirstMatch) {\n      var found;\n\n      while (target) {\n        if (det(target)) {\n          found = target;\n\n          if (returnFirstMatch) {\n            break;\n          }\n        }\n\n        target = target.__hostTarget || target.parent;\n      }\n\n      return found;\n    }\n\n    var wmUniqueIndex = Math.round(Math.random() * 9);\n    var supportDefineProperty = typeof Object.defineProperty === 'function';\n    var WeakMap = (function () {\n        function WeakMap() {\n            this._id = '__ec_inner_' + wmUniqueIndex++;\n        }\n        WeakMap.prototype.get = function (key) {\n            return this._guard(key)[this._id];\n        };\n        WeakMap.prototype.set = function (key, value) {\n            var target = this._guard(key);\n            if (supportDefineProperty) {\n                Object.defineProperty(target, this._id, {\n                    value: value,\n                    enumerable: false,\n                    configurable: true\n                });\n            }\n            else {\n                target[this._id] = value;\n            }\n            return this;\n        };\n        WeakMap.prototype[\"delete\"] = function (key) {\n            if (this.has(key)) {\n                delete this._guard(key)[this._id];\n                return true;\n            }\n            return false;\n        };\n        WeakMap.prototype.has = function (key) {\n            return !!this._guard(key)[this._id];\n        };\n        WeakMap.prototype._guard = function (key) {\n            if (key !== Object(key)) {\n                throw TypeError('Value of WeakMap is not a non-null object.');\n            }\n            return key;\n        };\n        return WeakMap;\n    }());\n\n    /**\n     * Triangle shape\n     * @inner\n     */\n\n    var Triangle = Path.extend({\n      type: 'triangle',\n      shape: {\n        cx: 0,\n        cy: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var width = shape.width / 2;\n        var height = shape.height / 2;\n        path.moveTo(cx, cy - height);\n        path.lineTo(cx + width, cy + height);\n        path.lineTo(cx - width, cy + height);\n        path.closePath();\n      }\n    });\n    /**\n     * Diamond shape\n     * @inner\n     */\n\n    var Diamond = Path.extend({\n      type: 'diamond',\n      shape: {\n        cx: 0,\n        cy: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var width = shape.width / 2;\n        var height = shape.height / 2;\n        path.moveTo(cx, cy - height);\n        path.lineTo(cx + width, cy);\n        path.lineTo(cx, cy + height);\n        path.lineTo(cx - width, cy);\n        path.closePath();\n      }\n    });\n    /**\n     * Pin shape\n     * @inner\n     */\n\n    var Pin = Path.extend({\n      type: 'pin',\n      shape: {\n        // x, y on the cusp\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (path, shape) {\n        var x = shape.x;\n        var y = shape.y;\n        var w = shape.width / 5 * 3; // Height must be larger than width\n\n        var h = Math.max(w, shape.height);\n        var r = w / 2; // Dist on y with tangent point and circle center\n\n        var dy = r * r / (h - r);\n        var cy = y - h + r + dy;\n        var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center\n\n        var dx = Math.cos(angle) * r;\n        var tanX = Math.sin(angle);\n        var tanY = Math.cos(angle);\n        var cpLen = r * 0.6;\n        var cpLen2 = r * 0.7;\n        path.moveTo(x - dx, cy + dy);\n        path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);\n        path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y);\n        path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy);\n        path.closePath();\n      }\n    });\n    /**\n     * Arrow shape\n     * @inner\n     */\n\n    var Arrow = Path.extend({\n      type: 'arrow',\n      shape: {\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      buildPath: function (ctx, shape) {\n        var height = shape.height;\n        var width = shape.width;\n        var x = shape.x;\n        var y = shape.y;\n        var dx = width / 3 * 2;\n        ctx.moveTo(x, y);\n        ctx.lineTo(x + dx, y + height);\n        ctx.lineTo(x, y + height / 4 * 3);\n        ctx.lineTo(x - dx, y + height);\n        ctx.lineTo(x, y);\n        ctx.closePath();\n      }\n    });\n    /**\n     * Map of path constructors\n     */\n    // TODO Use function to build symbol path.\n\n    var symbolCtors = {\n      line: Line,\n      rect: Rect,\n      roundRect: Rect,\n      square: Rect,\n      circle: Circle,\n      diamond: Diamond,\n      pin: Pin,\n      arrow: Arrow,\n      triangle: Triangle\n    };\n    var symbolShapeMakers = {\n      line: function (x, y, w, h, shape) {\n        shape.x1 = x;\n        shape.y1 = y + h / 2;\n        shape.x2 = x + w;\n        shape.y2 = y + h / 2;\n      },\n      rect: function (x, y, w, h, shape) {\n        shape.x = x;\n        shape.y = y;\n        shape.width = w;\n        shape.height = h;\n      },\n      roundRect: function (x, y, w, h, shape) {\n        shape.x = x;\n        shape.y = y;\n        shape.width = w;\n        shape.height = h;\n        shape.r = Math.min(w, h) / 4;\n      },\n      square: function (x, y, w, h, shape) {\n        var size = Math.min(w, h);\n        shape.x = x;\n        shape.y = y;\n        shape.width = size;\n        shape.height = size;\n      },\n      circle: function (x, y, w, h, shape) {\n        // Put circle in the center of square\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.r = Math.min(w, h) / 2;\n      },\n      diamond: function (x, y, w, h, shape) {\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      pin: function (x, y, w, h, shape) {\n        shape.x = x + w / 2;\n        shape.y = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      arrow: function (x, y, w, h, shape) {\n        shape.x = x + w / 2;\n        shape.y = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      },\n      triangle: function (x, y, w, h, shape) {\n        shape.cx = x + w / 2;\n        shape.cy = y + h / 2;\n        shape.width = w;\n        shape.height = h;\n      }\n    };\n    var symbolBuildProxies = {};\n    each(symbolCtors, function (Ctor, name) {\n      symbolBuildProxies[name] = new Ctor();\n    });\n    var SymbolClz = Path.extend({\n      type: 'symbol',\n      shape: {\n        symbolType: '',\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      },\n      calculateTextPosition: function (out, config, rect) {\n        var res = calculateTextPosition(out, config, rect);\n        var shape = this.shape;\n\n        if (shape && shape.symbolType === 'pin' && config.position === 'inside') {\n          res.y = rect.y + rect.height * 0.4;\n        }\n\n        return res;\n      },\n      buildPath: function (ctx, shape, inBundle) {\n        var symbolType = shape.symbolType;\n\n        if (symbolType !== 'none') {\n          var proxySymbol = symbolBuildProxies[symbolType];\n\n          if (!proxySymbol) {\n            // Default rect\n            symbolType = 'rect';\n            proxySymbol = symbolBuildProxies[symbolType];\n          }\n\n          symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape);\n          proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);\n        }\n      }\n    }); // Provide setColor helper method to avoid determine if set the fill or stroke outside\n\n    function symbolPathSetColor(color, innerColor) {\n      if (this.type !== 'image') {\n        var symbolStyle = this.style;\n\n        if (this.__isEmptyBrush) {\n          symbolStyle.stroke = color;\n          symbolStyle.fill = innerColor || '#fff'; // TODO Same width with lineStyle in LineView\n\n          symbolStyle.lineWidth = 2;\n        } else if (this.shape.symbolType === 'line') {\n          symbolStyle.stroke = color;\n        } else {\n          symbolStyle.fill = color;\n        }\n\n        this.markRedraw();\n      }\n    }\n    /**\n     * Create a symbol element with given symbol configuration: shape, x, y, width, height, color\n     */\n\n\n    function createSymbol(symbolType, x, y, w, h, color, // whether to keep the ratio of w/h,\n    keepAspect) {\n      // TODO Support image object, DynamicImage.\n      var isEmpty = symbolType.indexOf('empty') === 0;\n\n      if (isEmpty) {\n        symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);\n      }\n\n      var symbolPath;\n\n      if (symbolType.indexOf('image://') === 0) {\n        symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n      } else if (symbolType.indexOf('path://') === 0) {\n        symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');\n      } else {\n        symbolPath = new SymbolClz({\n          shape: {\n            symbolType: symbolType,\n            x: x,\n            y: y,\n            width: w,\n            height: h\n          }\n        });\n      }\n\n      symbolPath.__isEmptyBrush = isEmpty; // TODO Should deprecate setColor\n\n      symbolPath.setColor = symbolPathSetColor;\n\n      if (color) {\n        symbolPath.setColor(color);\n      }\n\n      return symbolPath;\n    }\n    function normalizeSymbolSize(symbolSize) {\n      if (!isArray(symbolSize)) {\n        symbolSize = [+symbolSize, +symbolSize];\n      }\n\n      return [symbolSize[0] || 0, symbolSize[1] || 0];\n    }\n    function normalizeSymbolOffset(symbolOffset, symbolSize) {\n      if (symbolOffset == null) {\n        return;\n      }\n\n      if (!isArray(symbolOffset)) {\n        symbolOffset = [symbolOffset, symbolOffset];\n      }\n\n      return [parsePercent$1(symbolOffset[0], symbolSize[0]) || 0, parsePercent$1(retrieve2(symbolOffset[1], symbolOffset[0]), symbolSize[1]) || 0];\n    }\n\n    function isSafeNum(num) {\n        return isFinite(num);\n    }\n    function createLinearGradient(ctx, obj, rect) {\n        var x = obj.x == null ? 0 : obj.x;\n        var x2 = obj.x2 == null ? 1 : obj.x2;\n        var y = obj.y == null ? 0 : obj.y;\n        var y2 = obj.y2 == null ? 0 : obj.y2;\n        if (!obj.global) {\n            x = x * rect.width + rect.x;\n            x2 = x2 * rect.width + rect.x;\n            y = y * rect.height + rect.y;\n            y2 = y2 * rect.height + rect.y;\n        }\n        x = isSafeNum(x) ? x : 0;\n        x2 = isSafeNum(x2) ? x2 : 1;\n        y = isSafeNum(y) ? y : 0;\n        y2 = isSafeNum(y2) ? y2 : 0;\n        var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);\n        return canvasGradient;\n    }\n    function createRadialGradient(ctx, obj, rect) {\n        var width = rect.width;\n        var height = rect.height;\n        var min = Math.min(width, height);\n        var x = obj.x == null ? 0.5 : obj.x;\n        var y = obj.y == null ? 0.5 : obj.y;\n        var r = obj.r == null ? 0.5 : obj.r;\n        if (!obj.global) {\n            x = x * width + rect.x;\n            y = y * height + rect.y;\n            r = r * min;\n        }\n        x = isSafeNum(x) ? x : 0.5;\n        y = isSafeNum(y) ? y : 0.5;\n        r = r >= 0 && isSafeNum(r) ? r : 0.5;\n        var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);\n        return canvasGradient;\n    }\n    function getCanvasGradient(ctx, obj, rect) {\n        var canvasGradient = obj.type === 'radial'\n            ? createRadialGradient(ctx, obj, rect)\n            : createLinearGradient(ctx, obj, rect);\n        var colorStops = obj.colorStops;\n        for (var i = 0; i < colorStops.length; i++) {\n            canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color);\n        }\n        return canvasGradient;\n    }\n    function isClipPathChanged(clipPaths, prevClipPaths) {\n        if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) {\n            return false;\n        }\n        if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {\n            return true;\n        }\n        for (var i = 0; i < clipPaths.length; i++) {\n            if (clipPaths[i] !== prevClipPaths[i]) {\n                return true;\n            }\n        }\n        return false;\n    }\n    function parseInt10(val) {\n        return parseInt(val, 10);\n    }\n    function getSize(root, whIdx, opts) {\n        var wh = ['width', 'height'][whIdx];\n        var cwh = ['clientWidth', 'clientHeight'][whIdx];\n        var plt = ['paddingLeft', 'paddingTop'][whIdx];\n        var prb = ['paddingRight', 'paddingBottom'][whIdx];\n        if (opts[wh] != null && opts[wh] !== 'auto') {\n            return parseFloat(opts[wh]);\n        }\n        var stl = document.defaultView.getComputedStyle(root);\n        return ((root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))\n            - (parseInt10(stl[plt]) || 0)\n            - (parseInt10(stl[prb]) || 0)) | 0;\n    }\n\n    function normalizeLineDash(lineType, lineWidth) {\n        if (!lineType || lineType === 'solid' || !(lineWidth > 0)) {\n            return null;\n        }\n        return lineType === 'dashed'\n            ? [4 * lineWidth, 2 * lineWidth]\n            : lineType === 'dotted'\n                ? [lineWidth]\n                : isNumber(lineType)\n                    ? [lineType] : isArray(lineType) ? lineType : null;\n    }\n    function getLineDash(el) {\n        var style = el.style;\n        var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);\n        var lineDashOffset = style.lineDashOffset;\n        if (lineDash) {\n            var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;\n            if (lineScale_1 && lineScale_1 !== 1) {\n                lineDash = map(lineDash, function (rawVal) {\n                    return rawVal / lineScale_1;\n                });\n                lineDashOffset /= lineScale_1;\n            }\n        }\n        return [lineDash, lineDashOffset];\n    }\n\n    var pathProxyForDraw = new PathProxy(true);\n    function styleHasStroke(style) {\n        var stroke = style.stroke;\n        return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));\n    }\n    function isValidStrokeFillStyle(strokeOrFill) {\n        return typeof strokeOrFill === 'string' && strokeOrFill !== 'none';\n    }\n    function styleHasFill(style) {\n        var fill = style.fill;\n        return fill != null && fill !== 'none';\n    }\n    function doFillPath(ctx, style) {\n        if (style.fillOpacity != null && style.fillOpacity !== 1) {\n            var originalGlobalAlpha = ctx.globalAlpha;\n            ctx.globalAlpha = style.fillOpacity * style.opacity;\n            ctx.fill();\n            ctx.globalAlpha = originalGlobalAlpha;\n        }\n        else {\n            ctx.fill();\n        }\n    }\n    function doStrokePath(ctx, style) {\n        if (style.strokeOpacity != null && style.strokeOpacity !== 1) {\n            var originalGlobalAlpha = ctx.globalAlpha;\n            ctx.globalAlpha = style.strokeOpacity * style.opacity;\n            ctx.stroke();\n            ctx.globalAlpha = originalGlobalAlpha;\n        }\n        else {\n            ctx.stroke();\n        }\n    }\n    function createCanvasPattern(ctx, pattern, el) {\n        var image = createOrUpdateImage(pattern.image, pattern.__image, el);\n        if (isImageReady(image)) {\n            var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat');\n            if (typeof DOMMatrix === 'function'\n                && canvasPattern\n                && canvasPattern.setTransform) {\n                var matrix = new DOMMatrix();\n                matrix.translateSelf((pattern.x || 0), (pattern.y || 0));\n                matrix.rotateSelf(0, 0, (pattern.rotation || 0) * RADIAN_TO_DEGREE);\n                matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1));\n                canvasPattern.setTransform(matrix);\n            }\n            return canvasPattern;\n        }\n    }\n    function brushPath(ctx, el, style, inBatch) {\n        var _a;\n        var hasStroke = styleHasStroke(style);\n        var hasFill = styleHasFill(style);\n        var strokePercent = style.strokePercent;\n        var strokePart = strokePercent < 1;\n        var firstDraw = !el.path;\n        if ((!el.silent || strokePart) && firstDraw) {\n            el.createPathProxy();\n        }\n        var path = el.path || pathProxyForDraw;\n        var dirtyFlag = el.__dirty;\n        if (!inBatch) {\n            var fill = style.fill;\n            var stroke = style.stroke;\n            var hasFillGradient = hasFill && !!fill.colorStops;\n            var hasStrokeGradient = hasStroke && !!stroke.colorStops;\n            var hasFillPattern = hasFill && !!fill.image;\n            var hasStrokePattern = hasStroke && !!stroke.image;\n            var fillGradient = void 0;\n            var strokeGradient = void 0;\n            var fillPattern = void 0;\n            var strokePattern = void 0;\n            var rect = void 0;\n            if (hasFillGradient || hasStrokeGradient) {\n                rect = el.getBoundingRect();\n            }\n            if (hasFillGradient) {\n                fillGradient = dirtyFlag\n                    ? getCanvasGradient(ctx, fill, rect)\n                    : el.__canvasFillGradient;\n                el.__canvasFillGradient = fillGradient;\n            }\n            if (hasStrokeGradient) {\n                strokeGradient = dirtyFlag\n                    ? getCanvasGradient(ctx, stroke, rect)\n                    : el.__canvasStrokeGradient;\n                el.__canvasStrokeGradient = strokeGradient;\n            }\n            if (hasFillPattern) {\n                fillPattern = (dirtyFlag || !el.__canvasFillPattern)\n                    ? createCanvasPattern(ctx, fill, el)\n                    : el.__canvasFillPattern;\n                el.__canvasFillPattern = fillPattern;\n            }\n            if (hasStrokePattern) {\n                strokePattern = (dirtyFlag || !el.__canvasStrokePattern)\n                    ? createCanvasPattern(ctx, stroke, el)\n                    : el.__canvasStrokePattern;\n                el.__canvasStrokePattern = fillPattern;\n            }\n            if (hasFillGradient) {\n                ctx.fillStyle = fillGradient;\n            }\n            else if (hasFillPattern) {\n                if (fillPattern) {\n                    ctx.fillStyle = fillPattern;\n                }\n                else {\n                    hasFill = false;\n                }\n            }\n            if (hasStrokeGradient) {\n                ctx.strokeStyle = strokeGradient;\n            }\n            else if (hasStrokePattern) {\n                if (strokePattern) {\n                    ctx.strokeStyle = strokePattern;\n                }\n                else {\n                    hasStroke = false;\n                }\n            }\n        }\n        var scale = el.getGlobalScale();\n        path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold);\n        var lineDash;\n        var lineDashOffset;\n        if (ctx.setLineDash && style.lineDash) {\n            _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n        }\n        var needsRebuild = true;\n        if (firstDraw || (dirtyFlag & SHAPE_CHANGED_BIT)) {\n            path.setDPR(ctx.dpr);\n            if (strokePart) {\n                path.setContext(null);\n            }\n            else {\n                path.setContext(ctx);\n                needsRebuild = false;\n            }\n            path.reset();\n            el.buildPath(path, el.shape, inBatch);\n            path.toStatic();\n            el.pathUpdated();\n        }\n        if (needsRebuild) {\n            path.rebuildPath(ctx, strokePart ? strokePercent : 1);\n        }\n        if (lineDash) {\n            ctx.setLineDash(lineDash);\n            ctx.lineDashOffset = lineDashOffset;\n        }\n        if (!inBatch) {\n            if (style.strokeFirst) {\n                if (hasStroke) {\n                    doStrokePath(ctx, style);\n                }\n                if (hasFill) {\n                    doFillPath(ctx, style);\n                }\n            }\n            else {\n                if (hasFill) {\n                    doFillPath(ctx, style);\n                }\n                if (hasStroke) {\n                    doStrokePath(ctx, style);\n                }\n            }\n        }\n        if (lineDash) {\n            ctx.setLineDash([]);\n        }\n    }\n    function brushImage(ctx, el, style) {\n        var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload);\n        if (!image || !isImageReady(image)) {\n            return;\n        }\n        var x = style.x || 0;\n        var y = style.y || 0;\n        var width = el.getWidth();\n        var height = el.getHeight();\n        var aspect = image.width / image.height;\n        if (width == null && height != null) {\n            width = height * aspect;\n        }\n        else if (height == null && width != null) {\n            height = width / aspect;\n        }\n        else if (width == null && height == null) {\n            width = image.width;\n            height = image.height;\n        }\n        if (style.sWidth && style.sHeight) {\n            var sx = style.sx || 0;\n            var sy = style.sy || 0;\n            ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height);\n        }\n        else if (style.sx && style.sy) {\n            var sx = style.sx;\n            var sy = style.sy;\n            var sWidth = width - sx;\n            var sHeight = height - sy;\n            ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height);\n        }\n        else {\n            ctx.drawImage(image, x, y, width, height);\n        }\n    }\n    function brushText(ctx, el, style) {\n        var _a;\n        var text = style.text;\n        text != null && (text += '');\n        if (text) {\n            ctx.font = style.font || DEFAULT_FONT;\n            ctx.textAlign = style.textAlign;\n            ctx.textBaseline = style.textBaseline;\n            var lineDash = void 0;\n            var lineDashOffset = void 0;\n            if (ctx.setLineDash && style.lineDash) {\n                _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1];\n            }\n            if (lineDash) {\n                ctx.setLineDash(lineDash);\n                ctx.lineDashOffset = lineDashOffset;\n            }\n            if (style.strokeFirst) {\n                if (styleHasStroke(style)) {\n                    ctx.strokeText(text, style.x, style.y);\n                }\n                if (styleHasFill(style)) {\n                    ctx.fillText(text, style.x, style.y);\n                }\n            }\n            else {\n                if (styleHasFill(style)) {\n                    ctx.fillText(text, style.x, style.y);\n                }\n                if (styleHasStroke(style)) {\n                    ctx.strokeText(text, style.x, style.y);\n                }\n            }\n            if (lineDash) {\n                ctx.setLineDash([]);\n            }\n        }\n    }\n    var SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];\n    var STROKE_PROPS = [\n        ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]\n    ];\n    function bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) {\n        var styleChanged = false;\n        if (!forceSetAll) {\n            prevStyle = prevStyle || {};\n            if (style === prevStyle) {\n                return false;\n            }\n        }\n        if (forceSetAll || style.opacity !== prevStyle.opacity) {\n            flushPathDrawn(ctx, scope);\n            styleChanged = true;\n            var opacity = Math.max(Math.min(style.opacity, 1), 0);\n            ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity;\n        }\n        if (forceSetAll || style.blend !== prevStyle.blend) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend;\n        }\n        for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) {\n            var propName = SHADOW_NUMBER_PROPS[i];\n            if (forceSetAll || style[propName] !== prevStyle[propName]) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx[propName] = ctx.dpr * (style[propName] || 0);\n            }\n        }\n        if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor;\n        }\n        return styleChanged;\n    }\n    function bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) {\n        var style = getStyle(el, scope.inHover);\n        var prevStyle = forceSetAll\n            ? null\n            : (prevEl && getStyle(prevEl, scope.inHover) || {});\n        if (style === prevStyle) {\n            return false;\n        }\n        var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope);\n        if (forceSetAll || style.fill !== prevStyle.fill) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill);\n        }\n        if (forceSetAll || style.stroke !== prevStyle.stroke) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke);\n        }\n        if (forceSetAll || style.opacity !== prevStyle.opacity) {\n            if (!styleChanged) {\n                flushPathDrawn(ctx, scope);\n                styleChanged = true;\n            }\n            ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;\n        }\n        if (el.hasStroke()) {\n            var lineWidth = style.lineWidth;\n            var newLineWidth = lineWidth / ((style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1);\n            if (ctx.lineWidth !== newLineWidth) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx.lineWidth = newLineWidth;\n            }\n        }\n        for (var i = 0; i < STROKE_PROPS.length; i++) {\n            var prop = STROKE_PROPS[i];\n            var propName = prop[0];\n            if (forceSetAll || style[propName] !== prevStyle[propName]) {\n                if (!styleChanged) {\n                    flushPathDrawn(ctx, scope);\n                    styleChanged = true;\n                }\n                ctx[propName] = style[propName] || prop[1];\n            }\n        }\n        return styleChanged;\n    }\n    function bindImageStyle(ctx, el, prevEl, forceSetAll, scope) {\n        return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope);\n    }\n    function setContextTransform(ctx, el) {\n        var m = el.transform;\n        var dpr = ctx.dpr || 1;\n        if (m) {\n            ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);\n        }\n        else {\n            ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n        }\n    }\n    function updateClipStatus(clipPaths, ctx, scope) {\n        var allClipped = false;\n        for (var i = 0; i < clipPaths.length; i++) {\n            var clipPath = clipPaths[i];\n            allClipped = allClipped || clipPath.isZeroArea();\n            setContextTransform(ctx, clipPath);\n            ctx.beginPath();\n            clipPath.buildPath(ctx, clipPath.shape);\n            ctx.clip();\n        }\n        scope.allClipped = allClipped;\n    }\n    function isTransformChanged(m0, m1) {\n        if (m0 && m1) {\n            return m0[0] !== m1[0]\n                || m0[1] !== m1[1]\n                || m0[2] !== m1[2]\n                || m0[3] !== m1[3]\n                || m0[4] !== m1[4]\n                || m0[5] !== m1[5];\n        }\n        else if (!m0 && !m1) {\n            return false;\n        }\n        return true;\n    }\n    var DRAW_TYPE_PATH = 1;\n    var DRAW_TYPE_IMAGE = 2;\n    var DRAW_TYPE_TEXT = 3;\n    var DRAW_TYPE_INCREMENTAL = 4;\n    function canPathBatch(style) {\n        var hasFill = styleHasFill(style);\n        var hasStroke = styleHasStroke(style);\n        return !(style.lineDash\n            || !(+hasFill ^ +hasStroke)\n            || (hasFill && typeof style.fill !== 'string')\n            || (hasStroke && typeof style.stroke !== 'string')\n            || style.strokePercent < 1\n            || style.strokeOpacity < 1\n            || style.fillOpacity < 1);\n    }\n    function flushPathDrawn(ctx, scope) {\n        scope.batchFill && ctx.fill();\n        scope.batchStroke && ctx.stroke();\n        scope.batchFill = '';\n        scope.batchStroke = '';\n    }\n    function getStyle(el, inHover) {\n        return inHover ? (el.__hoverStyle || el.style) : el.style;\n    }\n    function brushSingle(ctx, el) {\n        brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true);\n    }\n    function brush(ctx, el, scope, isLast) {\n        var m = el.transform;\n        if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) {\n            el.__dirty &= ~REDRAW_BIT;\n            el.__isRendered = false;\n            return;\n        }\n        var clipPaths = el.__clipPaths;\n        var prevElClipPaths = scope.prevElClipPaths;\n        var forceSetTransform = false;\n        var forceSetStyle = false;\n        if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) {\n            if (prevElClipPaths && prevElClipPaths.length) {\n                flushPathDrawn(ctx, scope);\n                ctx.restore();\n                forceSetStyle = forceSetTransform = true;\n                scope.prevElClipPaths = null;\n                scope.allClipped = false;\n                scope.prevEl = null;\n            }\n            if (clipPaths && clipPaths.length) {\n                flushPathDrawn(ctx, scope);\n                ctx.save();\n                updateClipStatus(clipPaths, ctx, scope);\n                forceSetTransform = true;\n            }\n            scope.prevElClipPaths = clipPaths;\n        }\n        if (scope.allClipped) {\n            el.__isRendered = false;\n            return;\n        }\n        el.beforeBrush && el.beforeBrush();\n        el.innerBeforeBrush();\n        var prevEl = scope.prevEl;\n        if (!prevEl) {\n            forceSetStyle = forceSetTransform = true;\n        }\n        var canBatchPath = el instanceof Path\n            && el.autoBatch\n            && canPathBatch(el.style);\n        if (forceSetTransform || isTransformChanged(m, prevEl.transform)) {\n            flushPathDrawn(ctx, scope);\n            setContextTransform(ctx, el);\n        }\n        else if (!canBatchPath) {\n            flushPathDrawn(ctx, scope);\n        }\n        var style = getStyle(el, scope.inHover);\n        if (el instanceof Path) {\n            if (scope.lastDrawType !== DRAW_TYPE_PATH) {\n                forceSetStyle = true;\n                scope.lastDrawType = DRAW_TYPE_PATH;\n            }\n            bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n            if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) {\n                ctx.beginPath();\n            }\n            brushPath(ctx, el, style, canBatchPath);\n            if (canBatchPath) {\n                scope.batchFill = style.fill || '';\n                scope.batchStroke = style.stroke || '';\n            }\n        }\n        else {\n            if (el instanceof TSpan) {\n                if (scope.lastDrawType !== DRAW_TYPE_TEXT) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_TEXT;\n                }\n                bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);\n                brushText(ctx, el, style);\n            }\n            else if (el instanceof ZRImage) {\n                if (scope.lastDrawType !== DRAW_TYPE_IMAGE) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_IMAGE;\n                }\n                bindImageStyle(ctx, el, prevEl, forceSetStyle, scope);\n                brushImage(ctx, el, style);\n            }\n            else if (el.getTemporalDisplayables) {\n                if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) {\n                    forceSetStyle = true;\n                    scope.lastDrawType = DRAW_TYPE_INCREMENTAL;\n                }\n                brushIncremental(ctx, el, scope);\n            }\n        }\n        if (canBatchPath && isLast) {\n            flushPathDrawn(ctx, scope);\n        }\n        el.innerAfterBrush();\n        el.afterBrush && el.afterBrush();\n        scope.prevEl = el;\n        el.__dirty = 0;\n        el.__isRendered = true;\n    }\n    function brushIncremental(ctx, el, scope) {\n        var displayables = el.getDisplayables();\n        var temporalDisplayables = el.getTemporalDisplayables();\n        ctx.save();\n        var innerScope = {\n            prevElClipPaths: null,\n            prevEl: null,\n            allClipped: false,\n            viewWidth: scope.viewWidth,\n            viewHeight: scope.viewHeight,\n            inHover: scope.inHover\n        };\n        var i;\n        var len;\n        for (i = el.getCursor(), len = displayables.length; i < len; i++) {\n            var displayable = displayables[i];\n            displayable.beforeBrush && displayable.beforeBrush();\n            displayable.innerBeforeBrush();\n            brush(ctx, displayable, innerScope, i === len - 1);\n            displayable.innerAfterBrush();\n            displayable.afterBrush && displayable.afterBrush();\n            innerScope.prevEl = displayable;\n        }\n        for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) {\n            var displayable = temporalDisplayables[i_1];\n            displayable.beforeBrush && displayable.beforeBrush();\n            displayable.innerBeforeBrush();\n            brush(ctx, displayable, innerScope, i_1 === len_1 - 1);\n            displayable.innerAfterBrush();\n            displayable.afterBrush && displayable.afterBrush();\n            innerScope.prevEl = displayable;\n        }\n        el.clearTemporalDisplayables();\n        el.notClear = true;\n        ctx.restore();\n    }\n\n    var decalMap = new WeakMap();\n    var decalCache = new LRU(100);\n    var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight'];\n    /**\n     * Create or update pattern image from decal options\n     *\n     * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal\n     * @return {Pattern} pattern with generated image, null if no decal\n     */\n\n    function createOrUpdatePatternFromDecal(decalObject, api) {\n      if (decalObject === 'none') {\n        return null;\n      }\n\n      var dpr = api.getDevicePixelRatio();\n      var zr = api.getZr();\n      var isSVG = zr.painter.type === 'svg';\n\n      if (decalObject.dirty) {\n        decalMap[\"delete\"](decalObject);\n      }\n\n      var oldPattern = decalMap.get(decalObject);\n\n      if (oldPattern) {\n        return oldPattern;\n      }\n\n      var decalOpt = defaults(decalObject, {\n        symbol: 'rect',\n        symbolSize: 1,\n        symbolKeepAspect: true,\n        color: 'rgba(0, 0, 0, 0.2)',\n        backgroundColor: null,\n        dashArrayX: 5,\n        dashArrayY: 5,\n        rotation: 0,\n        maxTileWidth: 512,\n        maxTileHeight: 512\n      });\n\n      if (decalOpt.backgroundColor === 'none') {\n        decalOpt.backgroundColor = null;\n      }\n\n      var pattern = {\n        repeat: 'repeat'\n      };\n      setPatternnSource(pattern);\n      pattern.rotation = decalOpt.rotation;\n      pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr;\n      decalMap.set(decalObject, pattern);\n      decalObject.dirty = false;\n      return pattern;\n\n      function setPatternnSource(pattern) {\n        var keys = [dpr];\n        var isValidKey = true;\n\n        for (var i = 0; i < decalKeys.length; ++i) {\n          var value = decalOpt[decalKeys[i]];\n\n          if (value != null && !isArray(value) && !isString(value) && !isNumber(value) && typeof value !== 'boolean') {\n            isValidKey = false;\n            break;\n          }\n\n          keys.push(value);\n        }\n\n        var cacheKey;\n\n        if (isValidKey) {\n          cacheKey = keys.join(',') + (isSVG ? '-svg' : '');\n          var cache = decalCache.get(cacheKey);\n\n          if (cache) {\n            isSVG ? pattern.svgElement = cache : pattern.image = cache;\n          }\n        }\n\n        var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX);\n        var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY);\n        var symbolArray = normalizeSymbolArray(decalOpt.symbol);\n        var lineBlockLengthsX = getLineBlockLengthX(dashArrayX);\n        var lineBlockLengthY = getLineBlockLengthY(dashArrayY);\n        var canvas = !isSVG && platformApi.createCanvas();\n        var svgRoot = isSVG && {\n          tag: 'g',\n          attrs: {},\n          key: 'dcl',\n          children: []\n        };\n        var pSize = getPatternSize();\n        var ctx;\n\n        if (canvas) {\n          canvas.width = pSize.width * dpr;\n          canvas.height = pSize.height * dpr;\n          ctx = canvas.getContext('2d');\n        }\n\n        brushDecal();\n\n        if (isValidKey) {\n          decalCache.put(cacheKey, canvas || svgRoot);\n        }\n\n        pattern.image = canvas;\n        pattern.svgElement = svgRoot;\n        pattern.svgWidth = pSize.width;\n        pattern.svgHeight = pSize.height;\n        /**\n         * Get minimum length that can make a repeatable pattern.\n         *\n         * @return {Object} pattern width and height\n         */\n\n        function getPatternSize() {\n          /**\n           * For example, if dash is [[3, 2], [2, 1]] for X, it looks like\n           * |---  ---  ---  ---  --- ...\n           * |-- -- -- -- -- -- -- -- ...\n           * |---  ---  ---  ---  --- ...\n           * |-- -- -- -- -- -- -- -- ...\n           * So the minimum length of X is 15,\n           * which is the least common multiple of `3 + 2` and `2 + 1`\n           * |---  ---  ---  |---  --- ...\n           * |-- -- -- -- -- |-- -- -- ...\n           */\n          var width = 1;\n\n          for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) {\n            width = getLeastCommonMultiple(width, lineBlockLengthsX[i]);\n          }\n\n          var symbolRepeats = 1;\n\n          for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) {\n            symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length);\n          }\n\n          width *= symbolRepeats;\n          var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length;\n\n          if (\"development\" !== 'production') {\n            var warn = function (attrName) {\n              /* eslint-disable-next-line */\n              console.warn(\"Calculated decal size is greater than \" + attrName + \" due to decal option settings so \" + attrName + \" is used for the decal size. Please consider changing the decal option to make a smaller decal or set \" + attrName + \" to be larger to avoid incontinuity.\");\n            };\n\n            if (width > decalOpt.maxTileWidth) {\n              warn('maxTileWidth');\n            }\n\n            if (height > decalOpt.maxTileHeight) {\n              warn('maxTileHeight');\n            }\n          }\n\n          return {\n            width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)),\n            height: Math.max(1, Math.min(height, decalOpt.maxTileHeight))\n          };\n        }\n\n        function brushDecal() {\n          if (ctx) {\n            ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n            if (decalOpt.backgroundColor) {\n              ctx.fillStyle = decalOpt.backgroundColor;\n              ctx.fillRect(0, 0, canvas.width, canvas.height);\n            }\n          }\n\n          var ySum = 0;\n\n          for (var i = 0; i < dashArrayY.length; ++i) {\n            ySum += dashArrayY[i];\n          }\n\n          if (ySum <= 0) {\n            // dashArrayY is 0, draw nothing\n            return;\n          }\n\n          var y = -lineBlockLengthY;\n          var yId = 0;\n          var yIdTotal = 0;\n          var xId0 = 0;\n\n          while (y < pSize.height) {\n            if (yId % 2 === 0) {\n              var symbolYId = yIdTotal / 2 % symbolArray.length;\n              var x = 0;\n              var xId1 = 0;\n              var xId1Total = 0;\n\n              while (x < pSize.width * 2) {\n                var xSum = 0;\n\n                for (var i = 0; i < dashArrayX[xId0].length; ++i) {\n                  xSum += dashArrayX[xId0][i];\n                }\n\n                if (xSum <= 0) {\n                  // Skip empty line\n                  break;\n                } // E.g., [15, 5, 20, 5] draws only for 15 and 20\n\n\n                if (xId1 % 2 === 0) {\n                  var size = (1 - decalOpt.symbolSize) * 0.5;\n                  var left = x + dashArrayX[xId0][xId1] * size;\n                  var top_1 = y + dashArrayY[yId] * size;\n                  var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize;\n                  var height = dashArrayY[yId] * decalOpt.symbolSize;\n                  var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length;\n                  brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]);\n                }\n\n                x += dashArrayX[xId0][xId1];\n                ++xId1Total;\n                ++xId1;\n\n                if (xId1 === dashArrayX[xId0].length) {\n                  xId1 = 0;\n                }\n              }\n\n              ++xId0;\n\n              if (xId0 === dashArrayX.length) {\n                xId0 = 0;\n              }\n            }\n\n            y += dashArrayY[yId];\n            ++yIdTotal;\n            ++yId;\n\n            if (yId === dashArrayY.length) {\n              yId = 0;\n            }\n          }\n\n          function brushSymbol(x, y, width, height, symbolType) {\n            var scale = isSVG ? 1 : dpr;\n            var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect);\n\n            if (isSVG) {\n              var symbolVNode = zr.painter.renderOneToVNode(symbol);\n\n              if (symbolVNode) {\n                svgRoot.children.push(symbolVNode);\n              }\n            } else {\n              // Paint to canvas for all other renderers.\n              brushSingle(ctx, symbol);\n            }\n          }\n        }\n      }\n    }\n    /**\n     * Convert symbol array into normalized array\n     *\n     * @param {string | (string | string[])[]} symbol symbol input\n     * @return {string[][]} normolized symbol array\n     */\n\n    function normalizeSymbolArray(symbol) {\n      if (!symbol || symbol.length === 0) {\n        return [['rect']];\n      }\n\n      if (isString(symbol)) {\n        return [[symbol]];\n      }\n\n      var isAllString = true;\n\n      for (var i = 0; i < symbol.length; ++i) {\n        if (!isString(symbol[i])) {\n          isAllString = false;\n          break;\n        }\n      }\n\n      if (isAllString) {\n        return normalizeSymbolArray([symbol]);\n      }\n\n      var result = [];\n\n      for (var i = 0; i < symbol.length; ++i) {\n        if (isString(symbol[i])) {\n          result.push([symbol[i]]);\n        } else {\n          result.push(symbol[i]);\n        }\n      }\n\n      return result;\n    }\n    /**\n     * Convert dash input into dashArray\n     *\n     * @param {DecalDashArrayX} dash dash input\n     * @return {number[][]} normolized dash array\n     */\n\n\n    function normalizeDashArrayX(dash) {\n      if (!dash || dash.length === 0) {\n        return [[0, 0]];\n      }\n\n      if (isNumber(dash)) {\n        var dashValue = Math.ceil(dash);\n        return [[dashValue, dashValue]];\n      }\n      /**\n       * [20, 5] should be normalized into [[20, 5]],\n       * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]]\n       */\n\n\n      var isAllNumber = true;\n\n      for (var i = 0; i < dash.length; ++i) {\n        if (!isNumber(dash[i])) {\n          isAllNumber = false;\n          break;\n        }\n      }\n\n      if (isAllNumber) {\n        return normalizeDashArrayX([dash]);\n      }\n\n      var result = [];\n\n      for (var i = 0; i < dash.length; ++i) {\n        if (isNumber(dash[i])) {\n          var dashValue = Math.ceil(dash[i]);\n          result.push([dashValue, dashValue]);\n        } else {\n          var dashValue = map(dash[i], function (n) {\n            return Math.ceil(n);\n          });\n\n          if (dashValue.length % 2 === 1) {\n            // [4, 2, 1] means |----  -    -- |----  -    -- |\n            // so normalize it to be [4, 2, 1, 4, 2, 1]\n            result.push(dashValue.concat(dashValue));\n          } else {\n            result.push(dashValue);\n          }\n        }\n      }\n\n      return result;\n    }\n    /**\n     * Convert dash input into dashArray\n     *\n     * @param {DecalDashArrayY} dash dash input\n     * @return {number[]} normolized dash array\n     */\n\n\n    function normalizeDashArrayY(dash) {\n      if (!dash || typeof dash === 'object' && dash.length === 0) {\n        return [0, 0];\n      }\n\n      if (isNumber(dash)) {\n        var dashValue_1 = Math.ceil(dash);\n        return [dashValue_1, dashValue_1];\n      }\n\n      var dashValue = map(dash, function (n) {\n        return Math.ceil(n);\n      });\n      return dash.length % 2 ? dashValue.concat(dashValue) : dashValue;\n    }\n    /**\n     * Get block length of each line. A block is the length of dash line and space.\n     * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after\n     * that, so the block length of this line is 5.\n     *\n     * @param {number[][]} dash dash array of X or Y\n     * @return {number[]} block length of each line\n     */\n\n\n    function getLineBlockLengthX(dash) {\n      return map(dash, function (line) {\n        return getLineBlockLengthY(line);\n      });\n    }\n\n    function getLineBlockLengthY(dash) {\n      var blockLength = 0;\n\n      for (var i = 0; i < dash.length; ++i) {\n        blockLength += dash[i];\n      }\n\n      if (dash.length % 2 === 1) {\n        // [4, 2, 1] means |----  -    -- |----  -    -- |\n        // So total length is (4 + 2 + 1) * 2\n        return blockLength * 2;\n      }\n\n      return blockLength;\n    }\n\n    function decalVisual(ecModel, api) {\n      ecModel.eachRawSeries(function (seriesModel) {\n        if (ecModel.isSeriesFiltered(seriesModel)) {\n          return;\n        }\n\n        var data = seriesModel.getData();\n\n        if (data.hasItemVisual()) {\n          data.each(function (idx) {\n            var decal = data.getItemVisual(idx, 'decal');\n\n            if (decal) {\n              var itemStyle = data.ensureUniqueItemVisual(idx, 'style');\n              itemStyle.decal = createOrUpdatePatternFromDecal(decal, api);\n            }\n          });\n        }\n\n        var decal = data.getVisual('decal');\n\n        if (decal) {\n          var style = data.getVisual('style');\n          style.decal = createOrUpdatePatternFromDecal(decal, api);\n        }\n      });\n    }\n\n    var lifecycle = new Eventful();\n\n    // The implementations will be registered when installing the component.\n    // Avoid these code being bundled to the core module.\n\n    var implsStore = {}; // TODO Type\n\n    function registerImpl(name, impl) {\n      if (\"development\" !== 'production') {\n        if (implsStore[name]) {\n          error(\"Already has an implementation of \" + name + \".\");\n        }\n      }\n\n      implsStore[name] = impl;\n    }\n    function getImpl(name) {\n      if (\"development\" !== 'production') {\n        if (!implsStore[name]) {\n          error(\"Implementation of \" + name + \" doesn't exists.\");\n        }\n      }\n\n      return implsStore[name];\n    }\n\n    var version$1 = '5.4.1';\n    var dependencies = {\n      zrender: '5.4.1'\n    };\n    var TEST_FRAME_REMAIN_TIME = 1;\n    var PRIORITY_PROCESSOR_SERIES_FILTER = 800; // Some data processors depends on the stack result dimension (to calculate data extent).\n    // So data stack stage should be in front of data processing stage.\n\n    var PRIORITY_PROCESSOR_DATASTACK = 900; // \"Data filter\" will block the stream, so it should be\n    // put at the beginning of data processing.\n\n    var PRIORITY_PROCESSOR_FILTER = 1000;\n    var PRIORITY_PROCESSOR_DEFAULT = 2000;\n    var PRIORITY_PROCESSOR_STATISTIC = 5000;\n    var PRIORITY_VISUAL_LAYOUT = 1000;\n    var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100;\n    var PRIORITY_VISUAL_GLOBAL = 2000;\n    var PRIORITY_VISUAL_CHART = 3000;\n    var PRIORITY_VISUAL_COMPONENT = 4000; // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to\n    // overwrite the viusal result of component (like `visualMap`)\n    // using data item specific setting (like itemStyle.xxx on data item)\n\n    var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on\n    // visual result like `symbolSize`.\n\n    var PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600;\n    var PRIORITY_VISUAL_BRUSH = 5000;\n    var PRIORITY_VISUAL_ARIA = 6000;\n    var PRIORITY_VISUAL_DECAL = 7000;\n    var PRIORITY = {\n      PROCESSOR: {\n        FILTER: PRIORITY_PROCESSOR_FILTER,\n        SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER,\n        STATISTIC: PRIORITY_PROCESSOR_STATISTIC\n      },\n      VISUAL: {\n        LAYOUT: PRIORITY_VISUAL_LAYOUT,\n        PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT,\n        GLOBAL: PRIORITY_VISUAL_GLOBAL,\n        CHART: PRIORITY_VISUAL_CHART,\n        POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT,\n        COMPONENT: PRIORITY_VISUAL_COMPONENT,\n        BRUSH: PRIORITY_VISUAL_BRUSH,\n        CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM,\n        ARIA: PRIORITY_VISUAL_ARIA,\n        DECAL: PRIORITY_VISUAL_DECAL\n      }\n    }; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,\n    // where they must not be invoked nestedly, except the only case: invoke\n    // dispatchAction with updateMethod \"none\" in main process.\n    // This flag is used to carry out this rule.\n    // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).\n\n    var IN_MAIN_PROCESS_KEY = '__flagInMainProcess';\n    var PENDING_UPDATE = '__pendingUpdate';\n    var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus';\n    var ACTION_REG = /^[a-zA-Z0-9_]+$/;\n    var CONNECT_STATUS_KEY = '__connectUpdateStatus';\n    var CONNECT_STATUS_PENDING = 0;\n    var CONNECT_STATUS_UPDATING = 1;\n    var CONNECT_STATUS_UPDATED = 2;\n\n    function createRegisterEventWithLowercaseECharts(method) {\n      return function () {\n        var args = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args[_i] = arguments[_i];\n        }\n\n        if (this.isDisposed()) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        return toLowercaseNameAndCallEventful(this, method, args);\n      };\n    }\n\n    function createRegisterEventWithLowercaseMessageCenter(method) {\n      return function () {\n        var args = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args[_i] = arguments[_i];\n        }\n\n        return toLowercaseNameAndCallEventful(this, method, args);\n      };\n    }\n\n    function toLowercaseNameAndCallEventful(host, method, args) {\n      // `args[0]` is event name. Event name is all lowercase.\n      args[0] = args[0] && args[0].toLowerCase();\n      return Eventful.prototype[method].apply(host, args);\n    }\n\n    var MessageCenter =\n    /** @class */\n    function (_super) {\n      __extends(MessageCenter, _super);\n\n      function MessageCenter() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return MessageCenter;\n    }(Eventful);\n\n    var messageCenterProto = MessageCenter.prototype;\n    messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on');\n    messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); // ---------------------------------------\n    // Internal method names for class ECharts\n    // ---------------------------------------\n\n    var prepare;\n    var prepareView;\n    var updateDirectly;\n    var updateMethods;\n    var doConvertPixel;\n    var updateStreamModes;\n    var doDispatchAction;\n    var flushPendingActions;\n    var triggerUpdatedEvent;\n    var bindRenderedEvent;\n    var bindMouseEvent;\n    var render;\n    var renderComponents;\n    var renderSeries;\n    var createExtensionAPI;\n    var enableConnect;\n    var markStatusToUpdate;\n    var applyChangedStates;\n\n    var ECharts =\n    /** @class */\n    function (_super) {\n      __extends(ECharts, _super);\n\n      function ECharts(dom, // Theme name or themeOption.\n      theme, opts) {\n        var _this = _super.call(this, new ECEventProcessor()) || this;\n\n        _this._chartsViews = [];\n        _this._chartsMap = {};\n        _this._componentsViews = [];\n        _this._componentsMap = {}; // Can't dispatch action during rendering procedure\n\n        _this._pendingActions = [];\n        opts = opts || {}; // Get theme by name\n\n        if (isString(theme)) {\n          theme = themeStorage[theme];\n        }\n\n        _this._dom = dom;\n        var defaultRenderer = 'canvas';\n        var defaultCoarsePointer = 'auto';\n        var defaultUseDirtyRect = false;\n\n        if (\"development\" !== 'production') {\n          var root =\n          /* eslint-disable-next-line */\n          env.hasGlobalWindow ? window : global;\n          defaultRenderer = root.__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;\n          defaultCoarsePointer = retrieve2(root.__ECHARTS__DEFAULT__COARSE_POINTER, defaultCoarsePointer);\n          var devUseDirtyRect = root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__;\n          defaultUseDirtyRect = devUseDirtyRect == null ? defaultUseDirtyRect : devUseDirtyRect;\n        }\n\n        var zr = _this._zr = init(dom, {\n          renderer: opts.renderer || defaultRenderer,\n          devicePixelRatio: opts.devicePixelRatio,\n          width: opts.width,\n          height: opts.height,\n          ssr: opts.ssr,\n          useDirtyRect: retrieve2(opts.useDirtyRect, defaultUseDirtyRect),\n          useCoarsePointer: retrieve2(opts.useCoarsePointer, defaultCoarsePointer),\n          pointerSize: opts.pointerSize\n        });\n        _this._ssr = opts.ssr; // Expect 60 fps.\n\n        _this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);\n        theme = clone(theme);\n        theme && globalBackwardCompat(theme, true);\n        _this._theme = theme;\n        _this._locale = createLocaleObject(opts.locale || SYSTEM_LANG);\n        _this._coordSysMgr = new CoordinateSystemManager();\n        var api = _this._api = createExtensionAPI(_this); // Sort on demand\n\n        function prioritySortFunc(a, b) {\n          return a.__prio - b.__prio;\n        }\n\n        sort(visualFuncs, prioritySortFunc);\n        sort(dataProcessorFuncs, prioritySortFunc);\n        _this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs);\n        _this._messageCenter = new MessageCenter(); // Init mouse events\n\n        _this._initEvents(); // In case some people write `window.onresize = chart.resize`\n\n\n        _this.resize = bind(_this.resize, _this);\n        zr.animation.on('frame', _this._onframe, _this);\n        bindRenderedEvent(zr, _this);\n        bindMouseEvent(zr, _this); // ECharts instance can be used as value.\n\n        setAsPrimitive(_this);\n        return _this;\n      }\n\n      ECharts.prototype._onframe = function () {\n        if (this._disposed) {\n          return;\n        }\n\n        applyChangedStates(this);\n        var scheduler = this._scheduler; // Lazy update\n\n        if (this[PENDING_UPDATE]) {\n          var silent = this[PENDING_UPDATE].silent;\n          this[IN_MAIN_PROCESS_KEY] = true;\n\n          try {\n            prepare(this);\n            updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams);\n          } catch (e) {\n            this[IN_MAIN_PROCESS_KEY] = false;\n            this[PENDING_UPDATE] = null;\n            throw e;\n          } // At present, in each frame, zrender performs:\n          //   (1) animation step forward.\n          //   (2) trigger('frame') (where this `_onframe` is called)\n          //   (3) zrender flush (render).\n          // If we do nothing here, since we use `setToFinal: true`, the step (3) above\n          // will render the final state of the elements before the real animation started.\n\n\n          this._zr.flush();\n\n          this[IN_MAIN_PROCESS_KEY] = false;\n          this[PENDING_UPDATE] = null;\n          flushPendingActions.call(this, silent);\n          triggerUpdatedEvent.call(this, silent);\n        } // Avoid do both lazy update and progress in one frame.\n        else if (scheduler.unfinished) {\n            // Stream progress.\n            var remainTime = TEST_FRAME_REMAIN_TIME;\n            var ecModel = this._model;\n            var api = this._api;\n            scheduler.unfinished = false;\n\n            do {\n              var startTime = +new Date();\n              scheduler.performSeriesTasks(ecModel); // Currently dataProcessorFuncs do not check threshold.\n\n              scheduler.performDataProcessorTasks(ecModel);\n              updateStreamModes(this, ecModel); // Do not update coordinate system here. Because that coord system update in\n              // each frame is not a good user experience. So we follow the rule that\n              // the extent of the coordinate system is determined in the first frame (the\n              // frame is executed immediately after task reset.\n              // this._coordSysMgr.update(ecModel, api);\n              // console.log('--- ec frame visual ---', remainTime);\n\n              scheduler.performVisualTasks(ecModel);\n              renderSeries(this, this._model, api, 'remain', {});\n              remainTime -= +new Date() - startTime;\n            } while (remainTime > 0 && scheduler.unfinished); // Call flush explicitly for trigger finished event.\n\n\n            if (!scheduler.unfinished) {\n              this._zr.flush();\n            } // Else, zr flushing be ensue within the same frame,\n            // because zr flushing is after onframe event.\n\n          }\n      };\n\n      ECharts.prototype.getDom = function () {\n        return this._dom;\n      };\n\n      ECharts.prototype.getId = function () {\n        return this.id;\n      };\n\n      ECharts.prototype.getZr = function () {\n        return this._zr;\n      };\n\n      ECharts.prototype.isSSR = function () {\n        return this._ssr;\n      };\n      /* eslint-disable-next-line */\n\n\n      ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) {\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          if (\"development\" !== 'production') {\n            error('`setOption` should not be called during main process.');\n          }\n\n          return;\n        }\n\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var silent;\n        var replaceMerge;\n        var transitionOpt;\n\n        if (isObject(notMerge)) {\n          lazyUpdate = notMerge.lazyUpdate;\n          silent = notMerge.silent;\n          replaceMerge = notMerge.replaceMerge;\n          transitionOpt = notMerge.transition;\n          notMerge = notMerge.notMerge;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = true;\n\n        if (!this._model || notMerge) {\n          var optionManager = new OptionManager(this._api);\n          var theme = this._theme;\n          var ecModel = this._model = new GlobalModel();\n          ecModel.scheduler = this._scheduler;\n          ecModel.ssr = this._ssr;\n          ecModel.init(null, null, null, theme, this._locale, optionManager);\n        }\n\n        this._model.setOption(option, {\n          replaceMerge: replaceMerge\n        }, optionPreprocessorFuncs);\n\n        var updateParams = {\n          seriesTransition: transitionOpt,\n          optionChanged: true\n        };\n\n        if (lazyUpdate) {\n          this[PENDING_UPDATE] = {\n            silent: silent,\n            updateParams: updateParams\n          };\n          this[IN_MAIN_PROCESS_KEY] = false; // `setOption(option, {lazyMode: true})` may be called when zrender has been slept.\n          // It should wake it up to make sure zrender start to render at the next frame.\n\n          this.getZr().wakeUp();\n        } else {\n          try {\n            prepare(this);\n            updateMethods.update.call(this, null, updateParams);\n          } catch (e) {\n            this[PENDING_UPDATE] = null;\n            this[IN_MAIN_PROCESS_KEY] = false;\n            throw e;\n          } // Ensure zr refresh sychronously, and then pixel in canvas can be\n          // fetched after `setOption`.\n\n\n          if (!this._ssr) {\n            // not use flush when using ssr mode.\n            this._zr.flush();\n          }\n\n          this[PENDING_UPDATE] = null;\n          this[IN_MAIN_PROCESS_KEY] = false;\n          flushPendingActions.call(this, silent);\n          triggerUpdatedEvent.call(this, silent);\n        }\n      };\n      /**\n       * @deprecated\n       */\n\n\n      ECharts.prototype.setTheme = function () {\n        deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0');\n      }; // We don't want developers to use getModel directly.\n\n\n      ECharts.prototype.getModel = function () {\n        return this._model;\n      };\n\n      ECharts.prototype.getOption = function () {\n        return this._model && this._model.getOption();\n      };\n\n      ECharts.prototype.getWidth = function () {\n        return this._zr.getWidth();\n      };\n\n      ECharts.prototype.getHeight = function () {\n        return this._zr.getHeight();\n      };\n\n      ECharts.prototype.getDevicePixelRatio = function () {\n        return this._zr.painter.dpr\n        /* eslint-disable-next-line */\n        || env.hasGlobalWindow && window.devicePixelRatio || 1;\n      };\n      /**\n       * Get canvas which has all thing rendered\n       * @deprecated Use renderToCanvas instead.\n       */\n\n\n      ECharts.prototype.getRenderedCanvas = function (opts) {\n        if (\"development\" !== 'production') {\n          deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas');\n        }\n\n        return this.renderToCanvas(opts);\n      };\n\n      ECharts.prototype.renderToCanvas = function (opts) {\n        opts = opts || {};\n        var painter = this._zr.painter;\n\n        if (\"development\" !== 'production') {\n          if (painter.type !== 'canvas') {\n            throw new Error('renderToCanvas can only be used in the canvas renderer.');\n          }\n        }\n\n        return painter.getRenderedCanvas({\n          backgroundColor: opts.backgroundColor || this._model.get('backgroundColor'),\n          pixelRatio: opts.pixelRatio || this.getDevicePixelRatio()\n        });\n      };\n\n      ECharts.prototype.renderToSVGString = function (opts) {\n        opts = opts || {};\n        var painter = this._zr.painter;\n\n        if (\"development\" !== 'production') {\n          if (painter.type !== 'svg') {\n            throw new Error('renderToSVGString can only be used in the svg renderer.');\n          }\n        }\n\n        return painter.renderToString({\n          useViewBox: opts.useViewBox\n        });\n      };\n      /**\n       * Get svg data url\n       */\n\n\n      ECharts.prototype.getSvgDataURL = function () {\n        if (!env.svgSupported) {\n          return;\n        }\n\n        var zr = this._zr;\n        var list = zr.storage.getDisplayList(); // Stop animations\n\n        each(list, function (el) {\n          el.stopAnimation(null, true);\n        });\n        return zr.painter.toDataURL();\n      };\n\n      ECharts.prototype.getDataURL = function (opts) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        opts = opts || {};\n        var excludeComponents = opts.excludeComponents;\n        var ecModel = this._model;\n        var excludesComponentViews = [];\n        var self = this;\n        each(excludeComponents, function (componentType) {\n          ecModel.eachComponent({\n            mainType: componentType\n          }, function (component) {\n            var view = self._componentsMap[component.__viewId];\n\n            if (!view.group.ignore) {\n              excludesComponentViews.push(view);\n              view.group.ignore = true;\n            }\n          });\n        });\n        var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() : this.renderToCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));\n        each(excludesComponentViews, function (view) {\n          view.group.ignore = false;\n        });\n        return url;\n      };\n\n      ECharts.prototype.getConnectedDataURL = function (opts) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var isSvg = opts.type === 'svg';\n        var groupId = this.group;\n        var mathMin = Math.min;\n        var mathMax = Math.max;\n        var MAX_NUMBER = Infinity;\n\n        if (connectedGroups[groupId]) {\n          var left_1 = MAX_NUMBER;\n          var top_1 = MAX_NUMBER;\n          var right_1 = -MAX_NUMBER;\n          var bottom_1 = -MAX_NUMBER;\n          var canvasList_1 = [];\n          var dpr_1 = opts && opts.pixelRatio || this.getDevicePixelRatio();\n          each(instances$1, function (chart, id) {\n            if (chart.group === groupId) {\n              var canvas = isSvg ? chart.getZr().painter.getSvgDom().innerHTML : chart.renderToCanvas(clone(opts));\n              var boundingRect = chart.getDom().getBoundingClientRect();\n              left_1 = mathMin(boundingRect.left, left_1);\n              top_1 = mathMin(boundingRect.top, top_1);\n              right_1 = mathMax(boundingRect.right, right_1);\n              bottom_1 = mathMax(boundingRect.bottom, bottom_1);\n              canvasList_1.push({\n                dom: canvas,\n                left: boundingRect.left,\n                top: boundingRect.top\n              });\n            }\n          });\n          left_1 *= dpr_1;\n          top_1 *= dpr_1;\n          right_1 *= dpr_1;\n          bottom_1 *= dpr_1;\n          var width = right_1 - left_1;\n          var height = bottom_1 - top_1;\n          var targetCanvas = platformApi.createCanvas();\n          var zr_1 = init(targetCanvas, {\n            renderer: isSvg ? 'svg' : 'canvas'\n          });\n          zr_1.resize({\n            width: width,\n            height: height\n          });\n\n          if (isSvg) {\n            var content_1 = '';\n            each(canvasList_1, function (item) {\n              var x = item.left - left_1;\n              var y = item.top - top_1;\n              content_1 += '<g transform=\"translate(' + x + ',' + y + ')\">' + item.dom + '</g>';\n            });\n            zr_1.painter.getSvgRoot().innerHTML = content_1;\n\n            if (opts.connectedBackgroundColor) {\n              zr_1.painter.setBackgroundColor(opts.connectedBackgroundColor);\n            }\n\n            zr_1.refreshImmediately();\n            return zr_1.painter.toDataURL();\n          } else {\n            // Background between the charts\n            if (opts.connectedBackgroundColor) {\n              zr_1.add(new Rect({\n                shape: {\n                  x: 0,\n                  y: 0,\n                  width: width,\n                  height: height\n                },\n                style: {\n                  fill: opts.connectedBackgroundColor\n                }\n              }));\n            }\n\n            each(canvasList_1, function (item) {\n              var img = new ZRImage({\n                style: {\n                  x: item.left * dpr_1 - left_1,\n                  y: item.top * dpr_1 - top_1,\n                  image: item.dom\n                }\n              });\n              zr_1.add(img);\n            });\n            zr_1.refreshImmediately();\n            return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));\n          }\n        } else {\n          return this.getDataURL(opts);\n        }\n      };\n\n      ECharts.prototype.convertToPixel = function (finder, value) {\n        return doConvertPixel(this, 'convertToPixel', finder, value);\n      };\n\n      ECharts.prototype.convertFromPixel = function (finder, value) {\n        return doConvertPixel(this, 'convertFromPixel', finder, value);\n      };\n      /**\n       * Is the specified coordinate systems or components contain the given pixel point.\n       * @param {Array|number} value\n       * @return {boolean} result\n       */\n\n\n      ECharts.prototype.containPixel = function (finder, value) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var ecModel = this._model;\n        var result;\n        var findResult = parseFinder(ecModel, finder);\n        each(findResult, function (models, key) {\n          key.indexOf('Models') >= 0 && each(models, function (model) {\n            var coordSys = model.coordinateSystem;\n\n            if (coordSys && coordSys.containPoint) {\n              result = result || !!coordSys.containPoint(value);\n            } else if (key === 'seriesModels') {\n              var view = this._chartsMap[model.__viewId];\n\n              if (view && view.containPoint) {\n                result = result || view.containPoint(value, model);\n              } else {\n                if (\"development\" !== 'production') {\n                  warn(key + ': ' + (view ? 'The found component do not support containPoint.' : 'No view mapping to the found component.'));\n                }\n              }\n            } else {\n              if (\"development\" !== 'production') {\n                warn(key + ': containPoint is not supported');\n              }\n            }\n          }, this);\n        }, this);\n        return !!result;\n      };\n      /**\n       * Get visual from series or data.\n       * @param finder\n       *        If string, e.g., 'series', means {seriesIndex: 0}.\n       *        If Object, could contain some of these properties below:\n       *        {\n       *            seriesIndex / seriesId / seriesName,\n       *            dataIndex / dataIndexInside\n       *        }\n       *        If dataIndex is not specified, series visual will be fetched,\n       *        but not data item visual.\n       *        If all of seriesIndex, seriesId, seriesName are not specified,\n       *        visual will be fetched from first series.\n       * @param visualType 'color', 'symbol', 'symbolSize'\n       */\n\n\n      ECharts.prototype.getVisual = function (finder, visualType) {\n        var ecModel = this._model;\n        var parsedFinder = parseFinder(ecModel, finder, {\n          defaultMainType: 'series'\n        });\n        var seriesModel = parsedFinder.seriesModel;\n\n        if (\"development\" !== 'production') {\n          if (!seriesModel) {\n            warn('There is no specified series model');\n          }\n        }\n\n        var data = seriesModel.getData();\n        var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') ? parsedFinder.dataIndexInside : parsedFinder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(parsedFinder.dataIndex) : null;\n        return dataIndexInside != null ? getItemVisualFromData(data, dataIndexInside, visualType) : getVisualFromData(data, visualType);\n      };\n      /**\n       * Get view of corresponding component model\n       */\n\n\n      ECharts.prototype.getViewOfComponentModel = function (componentModel) {\n        return this._componentsMap[componentModel.__viewId];\n      };\n      /**\n       * Get view of corresponding series model\n       */\n\n\n      ECharts.prototype.getViewOfSeriesModel = function (seriesModel) {\n        return this._chartsMap[seriesModel.__viewId];\n      };\n\n      ECharts.prototype._initEvents = function () {\n        var _this = this;\n\n        each(MOUSE_EVENT_NAMES, function (eveName) {\n          var handler = function (e) {\n            var ecModel = _this.getModel();\n\n            var el = e.target;\n            var params;\n            var isGlobalOut = eveName === 'globalout'; // no e.target when 'globalout'.\n\n            if (isGlobalOut) {\n              params = {};\n            } else {\n              el && findEventDispatcher(el, function (parent) {\n                var ecData = getECData(parent);\n\n                if (ecData && ecData.dataIndex != null) {\n                  var dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex);\n                  params = dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType) || {};\n                  return true;\n                } // If element has custom eventData of components\n                else if (ecData.eventData) {\n                    params = extend({}, ecData.eventData);\n                    return true;\n                  }\n              }, true);\n            } // Contract: if params prepared in mouse event,\n            // these properties must be specified:\n            // {\n            //    componentType: string (component main type)\n            //    componentIndex: number\n            // }\n            // Otherwise event query can not work.\n\n\n            if (params) {\n              var componentType = params.componentType;\n              var componentIndex = params.componentIndex; // Special handling for historic reason: when trigger by\n              // markLine/markPoint/markArea, the componentType is\n              // 'markLine'/'markPoint'/'markArea', but we should better\n              // enable them to be queried by seriesIndex, since their\n              // option is set in each series.\n\n              if (componentType === 'markLine' || componentType === 'markPoint' || componentType === 'markArea') {\n                componentType = 'series';\n                componentIndex = params.seriesIndex;\n              }\n\n              var model = componentType && componentIndex != null && ecModel.getComponent(componentType, componentIndex);\n              var view = model && _this[model.mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId];\n\n              if (\"development\" !== 'production') {\n                // `event.componentType` and `event[componentTpype + 'Index']` must not\n                // be missed, otherwise there is no way to distinguish source component.\n                // See `dataFormat.getDataParams`.\n                if (!isGlobalOut && !(model && view)) {\n                  warn('model or view can not be found by params');\n                }\n              }\n\n              params.event = e;\n              params.type = eveName;\n              _this._$eventProcessor.eventInfo = {\n                targetEl: el,\n                packedEvent: params,\n                model: model,\n                view: view\n              };\n\n              _this.trigger(eveName, params);\n            }\n          }; // Consider that some component (like tooltip, brush, ...)\n          // register zr event handler, but user event handler might\n          // do anything, such as call `setOption` or `dispatchAction`,\n          // which probably update any of the content and probably\n          // cause problem if it is called previous other inner handlers.\n\n\n          handler.zrEventfulCallAtLast = true;\n\n          _this._zr.on(eveName, handler, _this);\n        });\n        each(eventActionMap, function (actionType, eventType) {\n          _this._messageCenter.on(eventType, function (event) {\n            this.trigger(eventType, event);\n          }, _this);\n        }); // Extra events\n        // TODO register?\n\n        each(['selectchanged'], function (eventType) {\n          _this._messageCenter.on(eventType, function (event) {\n            this.trigger(eventType, event);\n          }, _this);\n        });\n        handleLegacySelectEvents(this._messageCenter, this, this._api);\n      };\n\n      ECharts.prototype.isDisposed = function () {\n        return this._disposed;\n      };\n\n      ECharts.prototype.clear = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this.setOption({\n          series: []\n        }, true);\n      };\n\n      ECharts.prototype.dispose = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._disposed = true;\n        var dom = this.getDom();\n\n        if (dom) {\n          setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');\n        }\n\n        var chart = this;\n        var api = chart._api;\n        var ecModel = chart._model;\n        each(chart._componentsViews, function (component) {\n          component.dispose(ecModel, api);\n        });\n        each(chart._chartsViews, function (chart) {\n          chart.dispose(ecModel, api);\n        }); // Dispose after all views disposed\n\n        chart._zr.dispose(); // Set properties to null.\n        // To reduce the memory cost in case the top code still holds this instance unexpectedly.\n\n\n        chart._dom = chart._model = chart._chartsMap = chart._componentsMap = chart._chartsViews = chart._componentsViews = chart._scheduler = chart._api = chart._zr = chart._throttledZrFlush = chart._theme = chart._coordSysMgr = chart._messageCenter = null;\n        delete instances$1[chart.id];\n      };\n      /**\n       * Resize the chart\n       */\n\n\n      ECharts.prototype.resize = function (opts) {\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          if (\"development\" !== 'production') {\n            error('`resize` should not be called during main process.');\n          }\n\n          return;\n        }\n\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._zr.resize(opts);\n\n        var ecModel = this._model; // Resize loading effect\n\n        this._loadingFX && this._loadingFX.resize();\n\n        if (!ecModel) {\n          return;\n        }\n\n        var needPrepare = ecModel.resetOption('media');\n        var silent = opts && opts.silent; // There is some real cases that:\n        // chart.setOption(option, { lazyUpdate: true });\n        // chart.resize();\n\n        if (this[PENDING_UPDATE]) {\n          if (silent == null) {\n            silent = this[PENDING_UPDATE].silent;\n          }\n\n          needPrepare = true;\n          this[PENDING_UPDATE] = null;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = true;\n\n        try {\n          needPrepare && prepare(this);\n          updateMethods.update.call(this, {\n            type: 'resize',\n            animation: extend({\n              // Disable animation\n              duration: 0\n            }, opts && opts.animation)\n          });\n        } catch (e) {\n          this[IN_MAIN_PROCESS_KEY] = false;\n          throw e;\n        }\n\n        this[IN_MAIN_PROCESS_KEY] = false;\n        flushPendingActions.call(this, silent);\n        triggerUpdatedEvent.call(this, silent);\n      };\n\n      ECharts.prototype.showLoading = function (name, cfg) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        if (isObject(name)) {\n          cfg = name;\n          name = '';\n        }\n\n        name = name || 'default';\n        this.hideLoading();\n\n        if (!loadingEffects[name]) {\n          if (\"development\" !== 'production') {\n            warn('Loading effects ' + name + ' not exists.');\n          }\n\n          return;\n        }\n\n        var el = loadingEffects[name](this._api, cfg);\n        var zr = this._zr;\n        this._loadingFX = el;\n        zr.add(el);\n      };\n      /**\n       * Hide loading effect\n       */\n\n\n      ECharts.prototype.hideLoading = function () {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        this._loadingFX && this._zr.remove(this._loadingFX);\n        this._loadingFX = null;\n      };\n\n      ECharts.prototype.makeActionFromEvent = function (eventObj) {\n        var payload = extend({}, eventObj);\n        payload.type = eventActionMap[eventObj.type];\n        return payload;\n      };\n      /**\n       * @param opt If pass boolean, means opt.silent\n       * @param opt.silent Default `false`. Whether trigger events.\n       * @param opt.flush Default `undefined`.\n       *        true: Flush immediately, and then pixel in canvas can be fetched\n       *            immediately. Caution: it might affect performance.\n       *        false: Not flush.\n       *        undefined: Auto decide whether perform flush.\n       */\n\n\n      ECharts.prototype.dispatchAction = function (payload, opt) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        if (!isObject(opt)) {\n          opt = {\n            silent: !!opt\n          };\n        }\n\n        if (!actions[payload.type]) {\n          return;\n        } // Avoid dispatch action before setOption. Especially in `connect`.\n\n\n        if (!this._model) {\n          return;\n        } // May dispatchAction in rendering procedure\n\n\n        if (this[IN_MAIN_PROCESS_KEY]) {\n          this._pendingActions.push(payload);\n\n          return;\n        }\n\n        var silent = opt.silent;\n        doDispatchAction.call(this, payload, silent);\n        var flush = opt.flush;\n\n        if (flush) {\n          this._zr.flush();\n        } else if (flush !== false && env.browser.weChat) {\n          // In WeChat embedded browser, `requestAnimationFrame` and `setInterval`\n          // hang when sliding page (on touch event), which cause that zr does not\n          // refresh until user interaction finished, which is not expected.\n          // But `dispatchAction` may be called too frequently when pan on touch\n          // screen, which impacts performance if do not throttle them.\n          this._throttledZrFlush();\n        }\n\n        flushPendingActions.call(this, silent);\n        triggerUpdatedEvent.call(this, silent);\n      };\n\n      ECharts.prototype.updateLabelLayout = function () {\n        lifecycle.trigger('series:layoutlabels', this._model, this._api, {\n          // Not adding series labels.\n          // TODO\n          updatedSeries: []\n        });\n      };\n\n      ECharts.prototype.appendData = function (params) {\n        if (this._disposed) {\n          disposedWarning(this.id);\n          return;\n        }\n\n        var seriesIndex = params.seriesIndex;\n        var ecModel = this.getModel();\n        var seriesModel = ecModel.getSeriesByIndex(seriesIndex);\n\n        if (\"development\" !== 'production') {\n          assert(params.data && seriesModel);\n        }\n\n        seriesModel.appendData(params); // Note: `appendData` does not support that update extent of coordinate\n        // system, util some scenario require that. In the expected usage of\n        // `appendData`, the initial extent of coordinate system should better\n        // be fixed by axis `min`/`max` setting or initial data, otherwise if\n        // the extent changed while `appendData`, the location of the painted\n        // graphic elements have to be changed, which make the usage of\n        // `appendData` meaningless.\n\n        this._scheduler.unfinished = true;\n        this.getZr().wakeUp();\n      }; // A work around for no `internal` modifier in ts yet but\n      // need to strictly hide private methods to JS users.\n\n\n      ECharts.internalField = function () {\n        prepare = function (ecIns) {\n          var scheduler = ecIns._scheduler;\n          scheduler.restorePipelines(ecIns._model);\n          scheduler.prepareStageTasks();\n          prepareView(ecIns, true);\n          prepareView(ecIns, false);\n          scheduler.plan();\n        };\n        /**\n         * Prepare view instances of charts and components\n         */\n\n\n        prepareView = function (ecIns, isComponent) {\n          var ecModel = ecIns._model;\n          var scheduler = ecIns._scheduler;\n          var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;\n          var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;\n          var zr = ecIns._zr;\n          var api = ecIns._api;\n\n          for (var i = 0; i < viewList.length; i++) {\n            viewList[i].__alive = false;\n          }\n\n          isComponent ? ecModel.eachComponent(function (componentType, model) {\n            componentType !== 'series' && doPrepare(model);\n          }) : ecModel.eachSeries(doPrepare);\n\n          function doPrepare(model) {\n            // By default view will be reused if possible for the case that `setOption` with \"notMerge\"\n            // mode and need to enable transition animation. (Usually, when they have the same id, or\n            // especially no id but have the same type & name & index. See the `model.id` generation\n            // rule in `makeIdAndName` and `viewId` generation rule here).\n            // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that\n            // the new model has nothing to do with the old model.\n            var requireNewView = model.__requireNewView; // This command should not work twice.\n\n            model.__requireNewView = false; // Consider: id same and type changed.\n\n            var viewId = '_ec_' + model.id + '_' + model.type;\n            var view = !requireNewView && viewMap[viewId];\n\n            if (!view) {\n              var classType = parseClassType(model.type);\n              var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : // FIXME:TS\n              // (ChartView as ChartViewConstructor).getClass('series', classType.sub)\n              // For backward compat, still support a chart type declared as only subType\n              // like \"liquidfill\", but recommend \"series.liquidfill\"\n              // But need a base class to make a type series.\n              ChartView.getClass(classType.sub);\n\n              if (\"development\" !== 'production') {\n                assert(Clazz, classType.sub + ' does not exist.');\n              }\n\n              view = new Clazz();\n              view.init(ecModel, api);\n              viewMap[viewId] = view;\n              viewList.push(view);\n              zr.add(view.group);\n            }\n\n            model.__viewId = view.__id = viewId;\n            view.__alive = true;\n            view.__model = model;\n            view.group.__ecComponentInfo = {\n              mainType: model.mainType,\n              index: model.componentIndex\n            };\n            !isComponent && scheduler.prepareView(view, model, ecModel, api);\n          }\n\n          for (var i = 0; i < viewList.length;) {\n            var view = viewList[i];\n\n            if (!view.__alive) {\n              !isComponent && view.renderTask.dispose();\n              zr.remove(view.group);\n              view.dispose(ecModel, api);\n              viewList.splice(i, 1);\n\n              if (viewMap[view.__id] === view) {\n                delete viewMap[view.__id];\n              }\n\n              view.__id = view.group.__ecComponentInfo = null;\n            } else {\n              i++;\n            }\n          }\n        };\n\n        updateDirectly = function (ecIns, method, payload, mainType, subType) {\n          var ecModel = ecIns._model;\n          ecModel.setUpdatePayload(payload); // broadcast\n\n          if (!mainType) {\n            // FIXME\n            // Chart will not be update directly here, except set dirty.\n            // But there is no such scenario now.\n            each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView);\n            return;\n          }\n\n          var query = {};\n          query[mainType + 'Id'] = payload[mainType + 'Id'];\n          query[mainType + 'Index'] = payload[mainType + 'Index'];\n          query[mainType + 'Name'] = payload[mainType + 'Name'];\n          var condition = {\n            mainType: mainType,\n            query: query\n          };\n          subType && (condition.subType = subType); // subType may be '' by parseClassType;\n\n          var excludeSeriesId = payload.excludeSeriesId;\n          var excludeSeriesIdMap;\n\n          if (excludeSeriesId != null) {\n            excludeSeriesIdMap = createHashMap();\n            each(normalizeToArray(excludeSeriesId), function (id) {\n              var modelId = convertOptionIdName(id, null);\n\n              if (modelId != null) {\n                excludeSeriesIdMap.set(modelId, true);\n              }\n            });\n          } // If dispatchAction before setOption, do nothing.\n\n\n          ecModel && ecModel.eachComponent(condition, function (model) {\n            var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n            if (isExcluded) {\n              return;\n            }\n\n            if (isHighDownPayload(payload)) {\n              if (model instanceof SeriesModel) {\n                if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur && !model.get(['emphasis', 'disabled'])) {\n                  blurSeriesFromHighlightPayload(model, payload, ecIns._api);\n                }\n              } else {\n                var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api),\n                    focusSelf = _a.focusSelf,\n                    dispatchers = _a.dispatchers;\n\n                if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) {\n                  blurComponent(model.mainType, model.componentIndex, ecIns._api);\n                } // PENDING:\n                // Whether to put this \"enter emphasis\" code in `ComponentView`,\n                // which will be the same as `ChartView` but might be not necessary\n                // and will be far from this logic.\n\n\n                if (dispatchers) {\n                  each(dispatchers, function (dispatcher) {\n                    payload.type === HIGHLIGHT_ACTION_TYPE ? enterEmphasis(dispatcher) : leaveEmphasis(dispatcher);\n                  });\n                }\n              }\n            } else if (isSelectChangePayload(payload)) {\n              // TODO geo\n              if (model instanceof SeriesModel) {\n                toggleSelectionFromPayload(model, payload, ecIns._api);\n                updateSeriesElementSelection(model);\n                markStatusToUpdate(ecIns);\n              }\n            }\n          }, ecIns);\n          ecModel && ecModel.eachComponent(condition, function (model) {\n            var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null;\n\n            if (isExcluded) {\n              return;\n            }\n            callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]);\n          }, ecIns);\n\n          function callView(view) {\n            view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload);\n          }\n        };\n\n        updateMethods = {\n          prepareAndUpdate: function (payload) {\n            prepare(this);\n            updateMethods.update.call(this, payload, {\n              // Needs to mark option changed if newOption is given.\n              // It's from MagicType.\n              // TODO If use a separate flag optionChanged in payload?\n              optionChanged: payload.newOption != null\n            });\n          },\n          update: function (payload, updateParams) {\n            var ecModel = this._model;\n            var api = this._api;\n            var zr = this._zr;\n            var coordSysMgr = this._coordSysMgr;\n            var scheduler = this._scheduler; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload);\n            scheduler.restoreData(ecModel, payload);\n            scheduler.performSeriesTasks(ecModel); // TODO\n            // Save total ecModel here for undo/redo (after restoring data and before processing data).\n            // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.\n            // Create new coordinate system each update\n            // In LineView may save the old coordinate system and use it to get the original point.\n\n            coordSysMgr.create(ecModel, api);\n            scheduler.performDataProcessorTasks(ecModel, payload); // Current stream render is not supported in data process. So we can update\n            // stream modes after data processing, where the filtered data is used to\n            // determine whether to use progressive rendering.\n\n            updateStreamModes(this, ecModel); // We update stream modes before coordinate system updated, then the modes info\n            // can be fetched when coord sys updating (consider the barGrid extent fix). But\n            // the drawback is the full coord info can not be fetched. Fortunately this full\n            // coord is not required in stream mode updater currently.\n\n            coordSysMgr.update(ecModel, api);\n            clearColorPalette(ecModel);\n            scheduler.performVisualTasks(ecModel, payload);\n            render(this, ecModel, api, payload, updateParams); // Set background\n\n            var backgroundColor = ecModel.get('backgroundColor') || 'transparent';\n            var darkMode = ecModel.get('darkMode');\n            zr.setBackgroundColor(backgroundColor); // Force set dark mode.\n\n            if (darkMode != null && darkMode !== 'auto') {\n              zr.setDarkMode(darkMode);\n            }\n\n            lifecycle.trigger('afterupdate', ecModel, api);\n          },\n          updateTransform: function (payload) {\n            var _this = this;\n\n            var ecModel = this._model;\n            var api = this._api; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload); // ChartView.markUpdateMethod(payload, 'updateTransform');\n\n            var componentDirtyList = [];\n            ecModel.eachComponent(function (componentType, componentModel) {\n              if (componentType === 'series') {\n                return;\n              }\n\n              var componentView = _this.getViewOfComponentModel(componentModel);\n\n              if (componentView && componentView.__alive) {\n                if (componentView.updateTransform) {\n                  var result = componentView.updateTransform(componentModel, ecModel, api, payload);\n                  result && result.update && componentDirtyList.push(componentView);\n                } else {\n                  componentDirtyList.push(componentView);\n                }\n              }\n            });\n            var seriesDirtyMap = createHashMap();\n            ecModel.eachSeries(function (seriesModel) {\n              var chartView = _this._chartsMap[seriesModel.__viewId];\n\n              if (chartView.updateTransform) {\n                var result = chartView.updateTransform(seriesModel, ecModel, api, payload);\n                result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);\n              } else {\n                seriesDirtyMap.set(seriesModel.uid, 1);\n              }\n            });\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n            // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              setDirty: true,\n              dirtyMap: seriesDirtyMap\n            }); // Currently, not call render of components. Geo render cost a lot.\n            // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);\n\n\n            renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap);\n            lifecycle.trigger('afterupdate', ecModel, api);\n          },\n          updateView: function (payload) {\n            var ecModel = this._model; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload);\n            ChartView.markUpdateMethod(payload, 'updateView');\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              setDirty: true\n            });\n\n            render(this, ecModel, this._api, payload, {});\n            lifecycle.trigger('afterupdate', ecModel, this._api);\n          },\n          updateVisual: function (payload) {\n            // updateMethods.update.call(this, payload);\n            var _this = this;\n\n            var ecModel = this._model; // update before setOption\n\n            if (!ecModel) {\n              return;\n            }\n\n            ecModel.setUpdatePayload(payload); // clear all visual\n\n            ecModel.eachSeries(function (seriesModel) {\n              seriesModel.getData().clearAllVisual();\n            }); // Perform visual\n\n            ChartView.markUpdateMethod(payload, 'updateVisual');\n            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.\n\n            this._scheduler.performVisualTasks(ecModel, payload, {\n              visualType: 'visual',\n              setDirty: true\n            });\n\n            ecModel.eachComponent(function (componentType, componentModel) {\n              if (componentType !== 'series') {\n                var componentView = _this.getViewOfComponentModel(componentModel);\n\n                componentView && componentView.__alive && componentView.updateVisual(componentModel, ecModel, _this._api, payload);\n              }\n            });\n            ecModel.eachSeries(function (seriesModel) {\n              var chartView = _this._chartsMap[seriesModel.__viewId];\n              chartView.updateVisual(seriesModel, ecModel, _this._api, payload);\n            });\n            lifecycle.trigger('afterupdate', ecModel, this._api);\n          },\n          updateLayout: function (payload) {\n            updateMethods.update.call(this, payload);\n          }\n        };\n\n        doConvertPixel = function (ecIns, methodName, finder, value) {\n          if (ecIns._disposed) {\n            disposedWarning(ecIns.id);\n            return;\n          }\n\n          var ecModel = ecIns._model;\n\n          var coordSysList = ecIns._coordSysMgr.getCoordinateSystems();\n\n          var result;\n          var parsedFinder = parseFinder(ecModel, finder);\n\n          for (var i = 0; i < coordSysList.length; i++) {\n            var coordSys = coordSysList[i];\n\n            if (coordSys[methodName] && (result = coordSys[methodName](ecModel, parsedFinder, value)) != null) {\n              return result;\n            }\n          }\n\n          if (\"development\" !== 'production') {\n            warn('No coordinate system that supports ' + methodName + ' found by the given finder.');\n          }\n        };\n\n        updateStreamModes = function (ecIns, ecModel) {\n          var chartsMap = ecIns._chartsMap;\n          var scheduler = ecIns._scheduler;\n          ecModel.eachSeries(function (seriesModel) {\n            scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);\n          });\n        };\n\n        doDispatchAction = function (payload, silent) {\n          var _this = this;\n\n          var ecModel = this.getModel();\n          var payloadType = payload.type;\n          var escapeConnect = payload.escapeConnect;\n          var actionWrap = actions[payloadType];\n          var actionInfo = actionWrap.actionInfo;\n          var cptTypeTmp = (actionInfo.update || 'update').split(':');\n          var updateMethod = cptTypeTmp.pop();\n          var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]);\n          this[IN_MAIN_PROCESS_KEY] = true;\n          var payloads = [payload];\n          var batched = false; // Batch action\n\n          if (payload.batch) {\n            batched = true;\n            payloads = map(payload.batch, function (item) {\n              item = defaults(extend({}, item), payload);\n              item.batch = null;\n              return item;\n            });\n          }\n\n          var eventObjBatch = [];\n          var eventObj;\n          var isSelectChange = isSelectChangePayload(payload);\n          var isHighDown = isHighDownPayload(payload); // Only leave blur once if there are multiple batches.\n\n          if (isHighDown) {\n            allLeaveBlur(this._api);\n          }\n\n          each(payloads, function (batchItem) {\n            // Action can specify the event by return it.\n            eventObj = actionWrap.action(batchItem, _this._model, _this._api); // Emit event outside\n\n            eventObj = eventObj || extend({}, batchItem); // Convert type to eventType\n\n            eventObj.type = actionInfo.event || eventObj.type;\n            eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.\n\n            if (isHighDown) {\n              var _a = preParseFinder(payload),\n                  queryOptionMap = _a.queryOptionMap,\n                  mainTypeSpecified = _a.mainTypeSpecified;\n\n              var componentMainType = mainTypeSpecified ? queryOptionMap.keys()[0] : 'series';\n              updateDirectly(_this, updateMethod, batchItem, componentMainType);\n              markStatusToUpdate(_this);\n            } else if (isSelectChange) {\n              // At present `dispatchAction({ type: 'select', ... })` is not supported on components.\n              // geo still use 'geoselect'.\n              updateDirectly(_this, updateMethod, batchItem, 'series');\n              markStatusToUpdate(_this);\n            } else if (cptType) {\n              updateDirectly(_this, updateMethod, batchItem, cptType.main, cptType.sub);\n            }\n          });\n\n          if (updateMethod !== 'none' && !isHighDown && !isSelectChange && !cptType) {\n            try {\n              // Still dirty\n              if (this[PENDING_UPDATE]) {\n                prepare(this);\n                updateMethods.update.call(this, payload);\n                this[PENDING_UPDATE] = null;\n              } else {\n                updateMethods[updateMethod].call(this, payload);\n              }\n            } catch (e) {\n              this[IN_MAIN_PROCESS_KEY] = false;\n              throw e;\n            }\n          } // Follow the rule of action batch\n\n\n          if (batched) {\n            eventObj = {\n              type: actionInfo.event || payloadType,\n              escapeConnect: escapeConnect,\n              batch: eventObjBatch\n            };\n          } else {\n            eventObj = eventObjBatch[0];\n          }\n\n          this[IN_MAIN_PROCESS_KEY] = false;\n\n          if (!silent) {\n            var messageCenter = this._messageCenter;\n            messageCenter.trigger(eventObj.type, eventObj); // Extra triggered 'selectchanged' event\n\n            if (isSelectChange) {\n              var newObj = {\n                type: 'selectchanged',\n                escapeConnect: escapeConnect,\n                selected: getAllSelectedIndices(ecModel),\n                isFromClick: payload.isFromClick || false,\n                fromAction: payload.type,\n                fromActionPayload: payload\n              };\n              messageCenter.trigger(newObj.type, newObj);\n            }\n          }\n        };\n\n        flushPendingActions = function (silent) {\n          var pendingActions = this._pendingActions;\n\n          while (pendingActions.length) {\n            var payload = pendingActions.shift();\n            doDispatchAction.call(this, payload, silent);\n          }\n        };\n\n        triggerUpdatedEvent = function (silent) {\n          !silent && this.trigger('updated');\n        };\n        /**\n         * Event `rendered` is triggered when zr\n         * rendered. It is useful for realtime\n         * snapshot (reflect animation).\n         *\n         * Event `finished` is triggered when:\n         * (1) zrender rendering finished.\n         * (2) initial animation finished.\n         * (3) progressive rendering finished.\n         * (4) no pending action.\n         * (5) no delayed setOption needs to be processed.\n         */\n\n\n        bindRenderedEvent = function (zr, ecIns) {\n          zr.on('rendered', function (params) {\n            ecIns.trigger('rendered', params); // The `finished` event should not be triggered repeatedly,\n            // so it should only be triggered when rendering indeed happens\n            // in zrender. (Consider the case that dipatchAction is keep\n            // triggering when mouse move).\n\n            if ( // Although zr is dirty if initial animation is not finished\n            // and this checking is called on frame, we also check\n            // animation finished for robustness.\n            zr.animation.isFinished() && !ecIns[PENDING_UPDATE] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) {\n              ecIns.trigger('finished');\n            }\n          });\n        };\n\n        bindMouseEvent = function (zr, ecIns) {\n          zr.on('mouseover', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n            if (dispatcher) {\n              handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api);\n              markStatusToUpdate(ecIns);\n            }\n          }).on('mouseout', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, isHighDownDispatcher);\n\n            if (dispatcher) {\n              handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api);\n              markStatusToUpdate(ecIns);\n            }\n          }).on('click', function (e) {\n            var el = e.target;\n            var dispatcher = findEventDispatcher(el, function (target) {\n              return getECData(target).dataIndex != null;\n            }, true);\n\n            if (dispatcher) {\n              var actionType = dispatcher.selected ? 'unselect' : 'select';\n              var ecData = getECData(dispatcher);\n\n              ecIns._api.dispatchAction({\n                type: actionType,\n                dataType: ecData.dataType,\n                dataIndexInside: ecData.dataIndex,\n                seriesIndex: ecData.seriesIndex,\n                isFromClick: true\n              });\n            }\n          });\n        };\n\n        function clearColorPalette(ecModel) {\n          ecModel.clearColorPalette();\n          ecModel.eachSeries(function (seriesModel) {\n            seriesModel.clearColorPalette();\n          });\n        }\n\n        function allocateZlevels(ecModel) {\n          var componentZLevels = [];\n          var seriesZLevels = [];\n          var hasSeperateZLevel = false;\n          ecModel.eachComponent(function (componentType, componentModel) {\n            var zlevel = componentModel.get('zlevel') || 0;\n            var z = componentModel.get('z') || 0;\n            var zlevelKey = componentModel.getZLevelKey();\n            hasSeperateZLevel = hasSeperateZLevel || !!zlevelKey;\n            (componentType === 'series' ? seriesZLevels : componentZLevels).push({\n              zlevel: zlevel,\n              z: z,\n              idx: componentModel.componentIndex,\n              type: componentType,\n              key: zlevelKey\n            });\n          });\n\n          if (hasSeperateZLevel) {\n            // Series after component\n            var zLevels = componentZLevels.concat(seriesZLevels);\n            var lastSeriesZLevel_1;\n            var lastSeriesKey_1;\n            sort(zLevels, function (a, b) {\n              if (a.zlevel === b.zlevel) {\n                return a.z - b.z;\n              }\n\n              return a.zlevel - b.zlevel;\n            });\n            each(zLevels, function (item) {\n              var componentModel = ecModel.getComponent(item.type, item.idx);\n              var zlevel = item.zlevel;\n              var key = item.key;\n\n              if (lastSeriesZLevel_1 != null) {\n                zlevel = Math.max(lastSeriesZLevel_1, zlevel);\n              }\n\n              if (key) {\n                if (zlevel === lastSeriesZLevel_1 && key !== lastSeriesKey_1) {\n                  zlevel++;\n                }\n\n                lastSeriesKey_1 = key;\n              } else if (lastSeriesKey_1) {\n                if (zlevel === lastSeriesZLevel_1) {\n                  zlevel++;\n                }\n\n                lastSeriesKey_1 = '';\n              }\n\n              lastSeriesZLevel_1 = zlevel;\n              componentModel.setZLevel(zlevel);\n            });\n          }\n        }\n\n        render = function (ecIns, ecModel, api, payload, updateParams) {\n          allocateZlevels(ecModel);\n          renderComponents(ecIns, ecModel, api, payload, updateParams);\n          each(ecIns._chartsViews, function (chart) {\n            chart.__alive = false;\n          });\n          renderSeries(ecIns, ecModel, api, payload, updateParams); // Remove groups of unrendered charts\n\n          each(ecIns._chartsViews, function (chart) {\n            if (!chart.__alive) {\n              chart.remove(ecModel, api);\n            }\n          });\n        };\n\n        renderComponents = function (ecIns, ecModel, api, payload, updateParams, dirtyList) {\n          each(dirtyList || ecIns._componentsViews, function (componentView) {\n            var componentModel = componentView.__model;\n            clearStates(componentModel, componentView);\n            componentView.render(componentModel, ecModel, api, payload);\n            updateZ(componentModel, componentView);\n            updateStates(componentModel, componentView);\n          });\n        };\n        /**\n         * Render each chart and component\n         */\n\n\n        renderSeries = function (ecIns, ecModel, api, payload, updateParams, dirtyMap) {\n          // Render all charts\n          var scheduler = ecIns._scheduler;\n          updateParams = extend(updateParams || {}, {\n            updatedSeries: ecModel.getSeries()\n          }); // TODO progressive?\n\n          lifecycle.trigger('series:beforeupdate', ecModel, api, updateParams);\n          var unfinished = false;\n          ecModel.eachSeries(function (seriesModel) {\n            var chartView = ecIns._chartsMap[seriesModel.__viewId];\n            chartView.__alive = true;\n            var renderTask = chartView.renderTask;\n            scheduler.updatePayload(renderTask, payload); // TODO states on marker.\n\n            clearStates(seriesModel, chartView);\n\n            if (dirtyMap && dirtyMap.get(seriesModel.uid)) {\n              renderTask.dirty();\n            }\n\n            if (renderTask.perform(scheduler.getPerformArgs(renderTask))) {\n              unfinished = true;\n            }\n\n            chartView.group.silent = !!seriesModel.get('silent'); // Should not call markRedraw on group, because it will disable zrender\n            // incremental render (always render from the __startIndex each frame)\n            // chartView.group.markRedraw();\n\n            updateBlend(seriesModel, chartView);\n            updateSeriesElementSelection(seriesModel);\n          });\n          scheduler.unfinished = unfinished || scheduler.unfinished;\n          lifecycle.trigger('series:layoutlabels', ecModel, api, updateParams); // transition after label is layouted.\n\n          lifecycle.trigger('series:transition', ecModel, api, updateParams);\n          ecModel.eachSeries(function (seriesModel) {\n            var chartView = ecIns._chartsMap[seriesModel.__viewId]; // Update Z after labels updated. Before applying states.\n\n            updateZ(seriesModel, chartView); // NOTE: Update states after label is updated.\n            // label should be in normal status when layouting.\n\n            updateStates(seriesModel, chartView);\n          }); // If use hover layer\n\n          updateHoverLayerStatus(ecIns, ecModel);\n          lifecycle.trigger('series:afterupdate', ecModel, api, updateParams);\n        };\n\n        markStatusToUpdate = function (ecIns) {\n          ecIns[STATUS_NEEDS_UPDATE_KEY] = true; // Wake up zrender if it's sleep. Let it update states in the next frame.\n\n          ecIns.getZr().wakeUp();\n        };\n\n        applyChangedStates = function (ecIns) {\n          if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) {\n            return;\n          }\n\n          ecIns.getZr().storage.traverse(function (el) {\n            // Not applied on removed elements, it may still in fading.\n            if (isElementRemoved(el)) {\n              return;\n            }\n\n            applyElementStates(el);\n          });\n          ecIns[STATUS_NEEDS_UPDATE_KEY] = false;\n        };\n\n        function applyElementStates(el) {\n          var newStates = [];\n          var oldStates = el.currentStates; // Keep other states.\n\n          for (var i = 0; i < oldStates.length; i++) {\n            var stateName = oldStates[i];\n\n            if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) {\n              newStates.push(stateName);\n            }\n          } // Only use states when it's exists.\n\n\n          if (el.selected && el.states.select) {\n            newStates.push('select');\n          }\n\n          if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) {\n            newStates.push('emphasis');\n          } else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) {\n            newStates.push('blur');\n          }\n\n          el.useStates(newStates);\n        }\n\n        function updateHoverLayerStatus(ecIns, ecModel) {\n          var zr = ecIns._zr;\n          var storage = zr.storage;\n          var elCount = 0;\n          storage.traverse(function (el) {\n            if (!el.isGroup) {\n              elCount++;\n            }\n          });\n\n          if (elCount > ecModel.get('hoverLayerThreshold') && !env.node && !env.worker) {\n            ecModel.eachSeries(function (seriesModel) {\n              if (seriesModel.preventUsingHoverLayer) {\n                return;\n              }\n\n              var chartView = ecIns._chartsMap[seriesModel.__viewId];\n\n              if (chartView.__alive) {\n                chartView.eachRendered(function (el) {\n                  if (el.states.emphasis) {\n                    el.states.emphasis.hoverLayer = true;\n                  }\n                });\n              }\n            });\n          }\n        }\n        /**\n         * Update chart and blend.\n         */\n\n        function updateBlend(seriesModel, chartView) {\n          var blendMode = seriesModel.get('blendMode') || null;\n          chartView.eachRendered(function (el) {\n            // FIXME marker and other components\n            if (!el.isGroup) {\n              // DON'T mark the element dirty. In case element is incremental and don't want to rerender.\n              el.style.blend = blendMode;\n            }\n          });\n        }\n\n        function updateZ(model, view) {\n          if (model.preventAutoZ) {\n            return;\n          }\n\n          var z = model.get('z') || 0;\n          var zlevel = model.get('zlevel') || 0; // Set z and zlevel\n\n          view.eachRendered(function (el) {\n            doUpdateZ(el, z, zlevel, -Infinity); // Don't traverse the children because it has been traversed in _updateZ.\n\n            return true;\n          });\n        }\n\n        function doUpdateZ(el, z, zlevel, maxZ2) {\n          // Group may also have textContent\n          var label = el.getTextContent();\n          var labelLine = el.getTextGuideLine();\n          var isGroup = el.isGroup;\n\n          if (isGroup) {\n            // set z & zlevel of children elements of Group\n            var children = el.childrenRef();\n\n            for (var i = 0; i < children.length; i++) {\n              maxZ2 = Math.max(doUpdateZ(children[i], z, zlevel, maxZ2), maxZ2);\n            }\n          } else {\n            // not Group\n            el.z = z;\n            el.zlevel = zlevel;\n            maxZ2 = Math.max(el.z2, maxZ2);\n          } // always set z and zlevel if label/labelLine exists\n\n\n          if (label) {\n            label.z = z;\n            label.zlevel = zlevel; // lift z2 of text content\n            // TODO if el.emphasis.z2 is spcefied, what about textContent.\n\n            isFinite(maxZ2) && (label.z2 = maxZ2 + 2);\n          }\n\n          if (labelLine) {\n            var textGuideLineConfig = el.textGuideLineConfig;\n            labelLine.z = z;\n            labelLine.zlevel = zlevel;\n            isFinite(maxZ2) && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1));\n          }\n\n          return maxZ2;\n        } // Clear states without animation.\n        // TODO States on component.\n\n\n        function clearStates(model, view) {\n          view.eachRendered(function (el) {\n            // Not applied on removed elements, it may still in fading.\n            if (isElementRemoved(el)) {\n              return;\n            }\n\n            var textContent = el.getTextContent();\n            var textGuide = el.getTextGuideLine();\n\n            if (el.stateTransition) {\n              el.stateTransition = null;\n            }\n\n            if (textContent && textContent.stateTransition) {\n              textContent.stateTransition = null;\n            }\n\n            if (textGuide && textGuide.stateTransition) {\n              textGuide.stateTransition = null;\n            } // TODO If el is incremental.\n\n\n            if (el.hasState()) {\n              el.prevStates = el.currentStates;\n              el.clearStates();\n            } else if (el.prevStates) {\n              el.prevStates = null;\n            }\n          });\n        }\n\n        function updateStates(model, view) {\n          var stateAnimationModel = model.getModel('stateAnimation');\n          var enableAnimation = model.isAnimationEnabled();\n          var duration = stateAnimationModel.get('duration');\n          var stateTransition = duration > 0 ? {\n            duration: duration,\n            delay: stateAnimationModel.get('delay'),\n            easing: stateAnimationModel.get('easing') // additive: stateAnimationModel.get('additive')\n\n          } : null;\n          view.eachRendered(function (el) {\n            if (el.states && el.states.emphasis) {\n              // Not applied on removed elements, it may still in fading.\n              if (isElementRemoved(el)) {\n                return;\n              }\n\n              if (el instanceof Path) {\n                savePathStates(el);\n              } // Only updated on changed element. In case element is incremental and don't want to rerender.\n              // TODO, a more proper way?\n\n\n              if (el.__dirty) {\n                var prevStates = el.prevStates; // Restore states without animation\n\n                if (prevStates) {\n                  el.useStates(prevStates);\n                }\n              } // Update state transition and enable animation again.\n\n\n              if (enableAnimation) {\n                el.stateTransition = stateTransition;\n                var textContent = el.getTextContent();\n                var textGuide = el.getTextGuideLine(); // TODO Is it necessary to animate label?\n\n                if (textContent) {\n                  textContent.stateTransition = stateTransition;\n                }\n\n                if (textGuide) {\n                  textGuide.stateTransition = stateTransition;\n                }\n              } // Use highlighted and selected flag to toggle states.\n\n\n              if (el.__dirty) {\n                applyElementStates(el);\n              }\n            }\n          });\n        }\n\n        createExtensionAPI = function (ecIns) {\n          return new (\n          /** @class */\n          function (_super) {\n            __extends(class_1, _super);\n\n            function class_1() {\n              return _super !== null && _super.apply(this, arguments) || this;\n            }\n\n            class_1.prototype.getCoordinateSystems = function () {\n              return ecIns._coordSysMgr.getCoordinateSystems();\n            };\n\n            class_1.prototype.getComponentByElement = function (el) {\n              while (el) {\n                var modelInfo = el.__ecComponentInfo;\n\n                if (modelInfo != null) {\n                  return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index);\n                }\n\n                el = el.parent;\n              }\n            };\n\n            class_1.prototype.enterEmphasis = function (el, highlightDigit) {\n              enterEmphasis(el, highlightDigit);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveEmphasis = function (el, highlightDigit) {\n              leaveEmphasis(el, highlightDigit);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.enterBlur = function (el) {\n              enterBlur(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveBlur = function (el) {\n              leaveBlur(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.enterSelect = function (el) {\n              enterSelect(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.leaveSelect = function (el) {\n              leaveSelect(el);\n              markStatusToUpdate(ecIns);\n            };\n\n            class_1.prototype.getModel = function () {\n              return ecIns.getModel();\n            };\n\n            class_1.prototype.getViewOfComponentModel = function (componentModel) {\n              return ecIns.getViewOfComponentModel(componentModel);\n            };\n\n            class_1.prototype.getViewOfSeriesModel = function (seriesModel) {\n              return ecIns.getViewOfSeriesModel(seriesModel);\n            };\n\n            return class_1;\n          }(ExtensionAPI))(ecIns);\n        };\n\n        enableConnect = function (chart) {\n          function updateConnectedChartsStatus(charts, status) {\n            for (var i = 0; i < charts.length; i++) {\n              var otherChart = charts[i];\n              otherChart[CONNECT_STATUS_KEY] = status;\n            }\n          }\n\n          each(eventActionMap, function (actionType, eventType) {\n            chart._messageCenter.on(eventType, function (event) {\n              if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) {\n                if (event && event.escapeConnect) {\n                  return;\n                }\n\n                var action_1 = chart.makeActionFromEvent(event);\n                var otherCharts_1 = [];\n                each(instances$1, function (otherChart) {\n                  if (otherChart !== chart && otherChart.group === chart.group) {\n                    otherCharts_1.push(otherChart);\n                  }\n                });\n                updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_PENDING);\n                each(otherCharts_1, function (otherChart) {\n                  if (otherChart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_UPDATING) {\n                    otherChart.dispatchAction(action_1);\n                  }\n                });\n                updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_UPDATED);\n              }\n            });\n          });\n        };\n      }();\n\n      return ECharts;\n    }(Eventful);\n\n    var echartsProto = ECharts.prototype;\n    echartsProto.on = createRegisterEventWithLowercaseECharts('on');\n    echartsProto.off = createRegisterEventWithLowercaseECharts('off');\n    /**\n     * @deprecated\n     */\n    // @ts-ignore\n\n    echartsProto.one = function (eventName, cb, ctx) {\n      var self = this;\n      deprecateLog('ECharts#one is deprecated.');\n\n      function wrapped() {\n        var args2 = [];\n\n        for (var _i = 0; _i < arguments.length; _i++) {\n          args2[_i] = arguments[_i];\n        }\n\n        cb && cb.apply && cb.apply(this, args2); // @ts-ignore\n\n        self.off(eventName, wrapped);\n      }\n\n      this.on.call(this, eventName, wrapped, ctx);\n    };\n\n    var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];\n\n    function disposedWarning(id) {\n      if (\"development\" !== 'production') {\n        warn('Instance ' + id + ' has been disposed');\n      }\n    }\n\n    var actions = {};\n    /**\n     * Map eventType to actionType\n     */\n\n    var eventActionMap = {};\n    var dataProcessorFuncs = [];\n    var optionPreprocessorFuncs = [];\n    var visualFuncs = [];\n    var themeStorage = {};\n    var loadingEffects = {};\n    var instances$1 = {};\n    var connectedGroups = {};\n    var idBase = +new Date() - 0;\n    var groupIdBase = +new Date() - 0;\n    var DOM_ATTRIBUTE_KEY = '_echarts_instance_';\n    /**\n     * @param opts.devicePixelRatio Use window.devicePixelRatio by default\n     * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart.\n     * @param opts.width Use clientWidth of the input `dom` by default.\n     *        Can be 'auto' (the same as null/undefined)\n     * @param opts.height Use clientHeight of the input `dom` by default.\n     *        Can be 'auto' (the same as null/undefined)\n     * @param opts.locale Specify the locale.\n     * @param opts.useDirtyRect Enable dirty rectangle rendering or not.\n     */\n\n    function init$1(dom, theme, opts) {\n      var isClient = !(opts && opts.ssr);\n\n      if (isClient) {\n        if (\"development\" !== 'production') {\n          if (!dom) {\n            throw new Error('Initialize failed: invalid dom.');\n          }\n        }\n\n        var existInstance = getInstanceByDom(dom);\n\n        if (existInstance) {\n          if (\"development\" !== 'production') {\n            warn('There is a chart instance already initialized on the dom.');\n          }\n\n          return existInstance;\n        }\n\n        if (\"development\" !== 'production') {\n          if (isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth && (!opts || opts.width == null) || !dom.clientHeight && (!opts || opts.height == null))) {\n            warn('Can\\'t get DOM width or height. Please check ' + 'dom.clientWidth and dom.clientHeight. They should not be 0.' + 'For example, you may need to call this in the callback ' + 'of window.onload.');\n          }\n        }\n      }\n\n      var chart = new ECharts(dom, theme, opts);\n      chart.id = 'ec_' + idBase++;\n      instances$1[chart.id] = chart;\n      isClient && setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);\n      enableConnect(chart);\n      lifecycle.trigger('afterinit', chart);\n      return chart;\n    }\n    /**\n     * @usage\n     * (A)\n     * ```js\n     * let chart1 = echarts.init(dom1);\n     * let chart2 = echarts.init(dom2);\n     * chart1.group = 'xxx';\n     * chart2.group = 'xxx';\n     * echarts.connect('xxx');\n     * ```\n     * (B)\n     * ```js\n     * let chart1 = echarts.init(dom1);\n     * let chart2 = echarts.init(dom2);\n     * echarts.connect('xxx', [chart1, chart2]);\n     * ```\n     */\n\n    function connect(groupId) {\n      // Is array of charts\n      if (isArray(groupId)) {\n        var charts = groupId;\n        groupId = null; // If any chart has group\n\n        each(charts, function (chart) {\n          if (chart.group != null) {\n            groupId = chart.group;\n          }\n        });\n        groupId = groupId || 'g_' + groupIdBase++;\n        each(charts, function (chart) {\n          chart.group = groupId;\n        });\n      }\n\n      connectedGroups[groupId] = true;\n      return groupId;\n    }\n    /**\n     * @deprecated\n     */\n\n    function disConnect(groupId) {\n      connectedGroups[groupId] = false;\n    }\n    /**\n     * Alias and backward compatibility\n     */\n\n    var disconnect = disConnect;\n    /**\n     * Dispose a chart instance\n     */\n\n    function dispose$1(chart) {\n      if (isString(chart)) {\n        chart = instances$1[chart];\n      } else if (!(chart instanceof ECharts)) {\n        // Try to treat as dom\n        chart = getInstanceByDom(chart);\n      }\n\n      if (chart instanceof ECharts && !chart.isDisposed()) {\n        chart.dispose();\n      }\n    }\n    function getInstanceByDom(dom) {\n      return instances$1[getAttribute(dom, DOM_ATTRIBUTE_KEY)];\n    }\n    function getInstanceById(key) {\n      return instances$1[key];\n    }\n    /**\n     * Register theme\n     */\n\n    function registerTheme(name, theme) {\n      themeStorage[name] = theme;\n    }\n    /**\n     * Register option preprocessor\n     */\n\n    function registerPreprocessor(preprocessorFunc) {\n      if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) {\n        optionPreprocessorFuncs.push(preprocessorFunc);\n      }\n    }\n    function registerProcessor(priority, processor) {\n      normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT);\n    }\n    /**\n     * Register postIniter\n     * @param {Function} postInitFunc\n     */\n\n    function registerPostInit(postInitFunc) {\n      registerUpdateLifecycle('afterinit', postInitFunc);\n    }\n    /**\n     * Register postUpdater\n     * @param {Function} postUpdateFunc\n     */\n\n    function registerPostUpdate(postUpdateFunc) {\n      registerUpdateLifecycle('afterupdate', postUpdateFunc);\n    }\n    function registerUpdateLifecycle(name, cb) {\n      lifecycle.on(name, cb);\n    }\n    function registerAction(actionInfo, eventName, action) {\n      if (isFunction(eventName)) {\n        action = eventName;\n        eventName = '';\n      }\n\n      var actionType = isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = {\n        event: eventName\n      }][0]; // Event name is all lowercase\n\n      actionInfo.event = (actionInfo.event || actionType).toLowerCase();\n      eventName = actionInfo.event;\n\n      if (eventActionMap[eventName]) {\n        // Already registered.\n        return;\n      } // Validate action type and event name.\n\n\n      assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));\n\n      if (!actions[actionType]) {\n        actions[actionType] = {\n          action: action,\n          actionInfo: actionInfo\n        };\n      }\n\n      eventActionMap[eventName] = actionType;\n    }\n    function registerCoordinateSystem(type, coordSysCreator) {\n      CoordinateSystemManager.register(type, coordSysCreator);\n    }\n    /**\n     * Get dimensions of specified coordinate system.\n     * @param {string} type\n     * @return {Array.<string|Object>}\n     */\n\n    function getCoordinateSystemDimensions(type) {\n      var coordSysCreator = CoordinateSystemManager.get(type);\n\n      if (coordSysCreator) {\n        return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice();\n      }\n    }\n\n    function registerLayout(priority, layoutTask) {\n      normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');\n    }\n\n    function registerVisual(priority, visualTask) {\n      normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');\n    }\n    var registeredTasks = [];\n\n    function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {\n      if (isFunction(priority) || isObject(priority)) {\n        fn = priority;\n        priority = defaultPriority;\n      }\n\n      if (\"development\" !== 'production') {\n        if (isNaN(priority) || priority == null) {\n          throw new Error('Illegal priority');\n        } // Check duplicate\n\n\n        each(targetList, function (wrap) {\n          assert(wrap.__raw !== fn);\n        });\n      } // Already registered\n\n\n      if (indexOf(registeredTasks, fn) >= 0) {\n        return;\n      }\n\n      registeredTasks.push(fn);\n      var stageHandler = Scheduler.wrapStageHandler(fn, visualType);\n      stageHandler.__prio = priority;\n      stageHandler.__raw = fn;\n      targetList.push(stageHandler);\n    }\n\n    function registerLoading(name, loadingFx) {\n      loadingEffects[name] = loadingFx;\n    }\n    /**\n     * ZRender need a canvas context to do measureText.\n     * But in node environment canvas may be created by node-canvas.\n     * So we need to specify how to create a canvas instead of using document.createElement('canvas')\n     *\n     *\n     * @deprecated use setPlatformAPI({ createCanvas }) instead.\n     *\n     * @example\n     *     let Canvas = require('canvas');\n     *     let echarts = require('echarts');\n     *     echarts.setCanvasCreator(function () {\n     *         // Small size is enough.\n     *         return new Canvas(32, 32);\n     *     });\n     */\n\n    function setCanvasCreator(creator) {\n      if (\"development\" !== 'production') {\n        deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.');\n      }\n\n      setPlatformAPI({\n        createCanvas: creator\n      });\n    }\n    /**\n     * The parameters and usage: see `geoSourceManager.registerMap`.\n     * Compatible with previous `echarts.registerMap`.\n     */\n\n    function registerMap(mapName, geoJson, specialAreas) {\n      var registerMap = getImpl('registerMap');\n      registerMap && registerMap(mapName, geoJson, specialAreas);\n    }\n    function getMap(mapName) {\n      var getMap = getImpl('getMap');\n      return getMap && getMap(mapName);\n    }\n    var registerTransform = registerExternalTransform;\n    /**\n     * Globa dispatchAction to a specified chart instance.\n     */\n    // export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters<ECharts['dispatchAction']>[1]) {\n    //     if (!payload || !payload.chartId) {\n    //         // Must have chartId to find chart\n    //         return;\n    //     }\n    //     const chart = instances[payload.chartId];\n    //     if (chart) {\n    //         chart.dispatchAction(payload, opt);\n    //     }\n    // }\n    // Builtin global visual\n\n    registerVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataColorPaletteTask);\n    registerVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask);\n    registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask);\n    registerVisual(PRIORITY_VISUAL_DECAL, decalVisual);\n    registerPreprocessor(globalBackwardCompat);\n    registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack);\n    registerLoading('default', defaultLoading); // Default actions\n\n    registerAction({\n      type: HIGHLIGHT_ACTION_TYPE,\n      event: HIGHLIGHT_ACTION_TYPE,\n      update: HIGHLIGHT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: DOWNPLAY_ACTION_TYPE,\n      event: DOWNPLAY_ACTION_TYPE,\n      update: DOWNPLAY_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: SELECT_ACTION_TYPE,\n      event: SELECT_ACTION_TYPE,\n      update: SELECT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: UNSELECT_ACTION_TYPE,\n      event: UNSELECT_ACTION_TYPE,\n      update: UNSELECT_ACTION_TYPE\n    }, noop);\n    registerAction({\n      type: TOGGLE_SELECT_ACTION_TYPE,\n      event: TOGGLE_SELECT_ACTION_TYPE,\n      update: TOGGLE_SELECT_ACTION_TYPE\n    }, noop); // Default theme\n\n    registerTheme('light', lightTheme);\n    registerTheme('dark', theme); // For backward compatibility, where the namespace `dataTool` will\n    // be mounted on `echarts` is the extension `dataTool` is imported.\n\n    var dataTool = {};\n\n    var extensions = [];\n    var extensionRegisters = {\n      registerPreprocessor: registerPreprocessor,\n      registerProcessor: registerProcessor,\n      registerPostInit: registerPostInit,\n      registerPostUpdate: registerPostUpdate,\n      registerUpdateLifecycle: registerUpdateLifecycle,\n      registerAction: registerAction,\n      registerCoordinateSystem: registerCoordinateSystem,\n      registerLayout: registerLayout,\n      registerVisual: registerVisual,\n      registerTransform: registerTransform,\n      registerLoading: registerLoading,\n      registerMap: registerMap,\n      registerImpl: registerImpl,\n      PRIORITY: PRIORITY,\n      ComponentModel: ComponentModel,\n      ComponentView: ComponentView,\n      SeriesModel: SeriesModel,\n      ChartView: ChartView,\n      // TODO Use ComponentModel and SeriesModel instead of Constructor\n      registerComponentModel: function (ComponentModelClass) {\n        ComponentModel.registerClass(ComponentModelClass);\n      },\n      registerComponentView: function (ComponentViewClass) {\n        ComponentView.registerClass(ComponentViewClass);\n      },\n      registerSeriesModel: function (SeriesModelClass) {\n        SeriesModel.registerClass(SeriesModelClass);\n      },\n      registerChartView: function (ChartViewClass) {\n        ChartView.registerClass(ChartViewClass);\n      },\n      registerSubTypeDefaulter: function (componentType, defaulter) {\n        ComponentModel.registerSubTypeDefaulter(componentType, defaulter);\n      },\n      registerPainter: function (painterType, PainterCtor) {\n        registerPainter(painterType, PainterCtor);\n      }\n    };\n    function use(ext) {\n      if (isArray(ext)) {\n        // use([ChartLine, ChartBar]);\n        each(ext, function (singleExt) {\n          use(singleExt);\n        });\n        return;\n      }\n\n      if (indexOf(extensions, ext) >= 0) {\n        return;\n      }\n\n      extensions.push(ext);\n\n      if (isFunction(ext)) {\n        ext = {\n          install: ext\n        };\n      }\n\n      ext.install(extensionRegisters);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) {\n      return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1;\n    }\n\n    function defaultKeyGetter(item) {\n      return item;\n    }\n\n    var DataDiffer =\n    /** @class */\n    function () {\n      /**\n       * @param context Can be visited by this.context in callback.\n       */\n      function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'.\n      diffMode) {\n        this._old = oldArr;\n        this._new = newArr;\n        this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;\n        this._newKeyGetter = newKeyGetter || defaultKeyGetter; // Visible in callback via `this.context`;\n\n        this.context = context;\n        this._diffModeMultiple = diffMode === 'multiple';\n      }\n      /**\n       * Callback function when add a data\n       */\n\n\n      DataDiffer.prototype.add = function (func) {\n        this._add = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data\n       */\n\n\n      DataDiffer.prototype.update = function (func) {\n        this._update = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateManyToOne = function (func) {\n        this._updateManyToOne = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateOneToMany = function (func) {\n        this._updateOneToMany = func;\n        return this;\n      };\n      /**\n       * Callback function when update a data and only work in `cbMode: 'byKey'`.\n       */\n\n\n      DataDiffer.prototype.updateManyToMany = function (func) {\n        this._updateManyToMany = func;\n        return this;\n      };\n      /**\n       * Callback function when remove a data\n       */\n\n\n      DataDiffer.prototype.remove = function (func) {\n        this._remove = func;\n        return this;\n      };\n\n      DataDiffer.prototype.execute = function () {\n        this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne']();\n      };\n\n      DataDiffer.prototype._executeOneToOne = function () {\n        var oldArr = this._old;\n        var newArr = this._new;\n        var newDataIndexMap = {};\n        var oldDataKeyArr = new Array(oldArr.length);\n        var newDataKeyArr = new Array(newArr.length);\n\n        this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter');\n\n        this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n        for (var i = 0; i < oldArr.length; i++) {\n          var oldKey = oldDataKeyArr[i];\n          var newIdxMapVal = newDataIndexMap[oldKey];\n          var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); // idx can never be empty array here. see 'set null' logic below.\n\n          if (newIdxMapValLen > 1) {\n            // Consider there is duplicate key (for example, use dataItem.name as key).\n            // We should make sure every item in newArr and oldArr can be visited.\n            var newIdx = newIdxMapVal.shift();\n\n            if (newIdxMapVal.length === 1) {\n              newDataIndexMap[oldKey] = newIdxMapVal[0];\n            }\n\n            this._update && this._update(newIdx, i);\n          } else if (newIdxMapValLen === 1) {\n            newDataIndexMap[oldKey] = null;\n            this._update && this._update(newIdxMapVal, i);\n          } else {\n            this._remove && this._remove(i);\n          }\n        }\n\n        this._performRestAdd(newDataKeyArr, newDataIndexMap);\n      };\n      /**\n       * For example, consider the case:\n       * oldData: [o0, o1, o2, o3, o4, o5, o6, o7],\n       * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],\n       * Where:\n       *     o0, o1, n0 has key 'a' (many to one)\n       *     o5, n4, n5, n6 has key 'b' (one to many)\n       *     o2, n1 has key 'c' (one to one)\n       *     n2, n3 has key 'd' (add)\n       *     o3, o4 has key 'e' (remove)\n       *     o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)\n       * Then:\n       *     (The order of the following directives are not ensured.)\n       *     this._updateManyToOne(n0, [o0, o1]);\n       *     this._updateOneToMany([n4, n5, n6], o5);\n       *     this._update(n1, o2);\n       *     this._remove(o3);\n       *     this._remove(o4);\n       *     this._remove(o6);\n       *     this._remove(o7);\n       *     this._add(n2);\n       *     this._add(n3);\n       *     this._add(n7);\n       *     this._add(n8);\n       */\n\n\n      DataDiffer.prototype._executeMultiple = function () {\n        var oldArr = this._old;\n        var newArr = this._new;\n        var oldDataIndexMap = {};\n        var newDataIndexMap = {};\n        var oldDataKeyArr = [];\n        var newDataKeyArr = [];\n\n        this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');\n\n        this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');\n\n        for (var i = 0; i < oldDataKeyArr.length; i++) {\n          var oldKey = oldDataKeyArr[i];\n          var oldIdxMapVal = oldDataIndexMap[oldKey];\n          var newIdxMapVal = newDataIndexMap[oldKey];\n          var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal);\n          var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n          if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {\n            this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {\n            this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {\n            this._update && this._update(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {\n            this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal);\n            newDataIndexMap[oldKey] = null;\n          } else if (oldIdxMapValLen > 1) {\n            for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {\n              this._remove && this._remove(oldIdxMapVal[i_1]);\n            }\n          } else {\n            this._remove && this._remove(oldIdxMapVal);\n          }\n        }\n\n        this._performRestAdd(newDataKeyArr, newDataIndexMap);\n      };\n\n      DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) {\n        for (var i = 0; i < newDataKeyArr.length; i++) {\n          var newKey = newDataKeyArr[i];\n          var newIdxMapVal = newDataIndexMap[newKey];\n          var idxMapValLen = dataIndexMapValueLength(newIdxMapVal);\n\n          if (idxMapValLen > 1) {\n            for (var j = 0; j < idxMapValLen; j++) {\n              this._add && this._add(newIdxMapVal[j]);\n            }\n          } else if (idxMapValLen === 1) {\n            this._add && this._add(newIdxMapVal);\n          } // Support both `newDataKeyArr` are duplication removed or not removed.\n\n\n          newDataIndexMap[newKey] = null;\n        }\n      };\n\n      DataDiffer.prototype._initIndexMap = function (arr, // Can be null.\n      map, // In 'byKey', the output `keyArr` is duplication removed.\n      // In 'byIndex', the output `keyArr` is not duplication removed and\n      //     its indices are accurately corresponding to `arr`.\n      keyArr, keyGetterName) {\n        var cbModeMultiple = this._diffModeMultiple;\n\n        for (var i = 0; i < arr.length; i++) {\n          // Add prefix to avoid conflict with Object.prototype.\n          var key = '_ec_' + this[keyGetterName](arr[i], i);\n\n          if (!cbModeMultiple) {\n            keyArr[i] = key;\n          }\n\n          if (!map) {\n            continue;\n          }\n\n          var idxMapVal = map[key];\n          var idxMapValLen = dataIndexMapValueLength(idxMapVal);\n\n          if (idxMapValLen === 0) {\n            // Simple optimize: in most cases, one index has one key,\n            // do not need array.\n            map[key] = i;\n\n            if (cbModeMultiple) {\n              keyArr.push(key);\n            }\n          } else if (idxMapValLen === 1) {\n            map[key] = [idxMapVal, i];\n          } else {\n            idxMapVal.push(i);\n          }\n        }\n      };\n\n      return DataDiffer;\n    }();\n\n    var DimensionUserOuput =\n    /** @class */\n    function () {\n      function DimensionUserOuput(encode, dimRequest) {\n        this._encode = encode;\n        this._schema = dimRequest;\n      }\n\n      DimensionUserOuput.prototype.get = function () {\n        return {\n          // Do not generate full dimension name until fist used.\n          fullDimensions: this._getFullDimensionNames(),\n          encode: this._encode\n        };\n      };\n      /**\n       * Get all data store dimension names.\n       * Theoretically a series data store is defined both by series and used dataset (if any).\n       * If some dimensions are omitted for performance reason in `this.dimensions`,\n       * the dimension name may not be auto-generated if user does not specify a dimension name.\n       * In this case, the dimension name is `null`/`undefined`.\n       */\n\n\n      DimensionUserOuput.prototype._getFullDimensionNames = function () {\n        if (!this._cachedDimNames) {\n          this._cachedDimNames = this._schema ? this._schema.makeOutputDimensionNames() : [];\n        }\n\n        return this._cachedDimNames;\n      };\n\n      return DimensionUserOuput;\n    }();\n    function summarizeDimensions(data, schema) {\n      var summary = {};\n      var encode = summary.encode = {};\n      var notExtraCoordDimMap = createHashMap();\n      var defaultedLabel = [];\n      var defaultedTooltip = [];\n      var userOutputEncode = {};\n      each(data.dimensions, function (dimName) {\n        var dimItem = data.getDimensionInfo(dimName);\n        var coordDim = dimItem.coordDim;\n\n        if (coordDim) {\n          if (\"development\" !== 'production') {\n            assert(VISUAL_DIMENSIONS.get(coordDim) == null);\n          }\n\n          var coordDimIndex = dimItem.coordDimIndex;\n          getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName;\n\n          if (!dimItem.isExtraCoord) {\n            notExtraCoordDimMap.set(coordDim, 1); // Use the last coord dim (and label friendly) as default label,\n            // because when dataset is used, it is hard to guess which dimension\n            // can be value dimension. If both show x, y on label is not look good,\n            // and conventionally y axis is focused more.\n\n            if (mayLabelDimType(dimItem.type)) {\n              defaultedLabel[0] = dimName;\n            } // User output encode do not contain generated coords.\n            // And it only has index. User can use index to retrieve value from the raw item array.\n\n\n            getOrCreateEncodeArr(userOutputEncode, coordDim)[coordDimIndex] = data.getDimensionIndex(dimItem.name);\n          }\n\n          if (dimItem.defaultTooltip) {\n            defaultedTooltip.push(dimName);\n          }\n        }\n\n        VISUAL_DIMENSIONS.each(function (v, otherDim) {\n          var encodeArr = getOrCreateEncodeArr(encode, otherDim);\n          var dimIndex = dimItem.otherDims[otherDim];\n\n          if (dimIndex != null && dimIndex !== false) {\n            encodeArr[dimIndex] = dimItem.name;\n          }\n        });\n      });\n      var dataDimsOnCoord = [];\n      var encodeFirstDimNotExtra = {};\n      notExtraCoordDimMap.each(function (v, coordDim) {\n        var dimArr = encode[coordDim];\n        encodeFirstDimNotExtra[coordDim] = dimArr[0]; // Not necessary to remove duplicate, because a data\n        // dim canot on more than one coordDim.\n\n        dataDimsOnCoord = dataDimsOnCoord.concat(dimArr);\n      });\n      summary.dataDimsOnCoord = dataDimsOnCoord;\n      summary.dataDimIndicesOnCoord = map(dataDimsOnCoord, function (dimName) {\n        return data.getDimensionInfo(dimName).storeDimIndex;\n      });\n      summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra;\n      var encodeLabel = encode.label; // FIXME `encode.label` is not recommended, because formatter cannot be set\n      // in this way. Use label.formatter instead. Maybe remove this approach someday.\n\n      if (encodeLabel && encodeLabel.length) {\n        defaultedLabel = encodeLabel.slice();\n      }\n\n      var encodeTooltip = encode.tooltip;\n\n      if (encodeTooltip && encodeTooltip.length) {\n        defaultedTooltip = encodeTooltip.slice();\n      } else if (!defaultedTooltip.length) {\n        defaultedTooltip = defaultedLabel.slice();\n      }\n\n      encode.defaultedLabel = defaultedLabel;\n      encode.defaultedTooltip = defaultedTooltip;\n      summary.userOutput = new DimensionUserOuput(userOutputEncode, schema);\n      return summary;\n    }\n\n    function getOrCreateEncodeArr(encode, dim) {\n      if (!encode.hasOwnProperty(dim)) {\n        encode[dim] = [];\n      }\n\n      return encode[dim];\n    } // FIXME:TS should be type `AxisType`\n\n\n    function getDimensionTypeByAxis(axisType) {\n      return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float';\n    }\n\n    function mayLabelDimType(dimType) {\n      // In most cases, ordinal and time do not suitable for label.\n      // Ordinal info can be displayed on axis. Time is too long.\n      return !(dimType === 'ordinal' || dimType === 'time');\n    } // function findTheLastDimMayLabel(data) {\n    //     // Get last value dim\n    //     let dimensions = data.dimensions.slice();\n    //     let valueType;\n    //     let valueDim;\n    //     while (dimensions.length && (\n    //         valueDim = dimensions.pop(),\n    //         valueType = data.getDimensionInfo(valueDim).type,\n    //         valueType === 'ordinal' || valueType === 'time'\n    //     )) {} // jshint ignore:line\n    //     return valueDim;\n    // }\n\n    var SeriesDimensionDefine =\n    /** @class */\n    function () {\n      /**\n       * @param opt All of the fields will be shallow copied.\n       */\n      function SeriesDimensionDefine(opt) {\n        /**\n         * The format of `otherDims` is:\n         * ```js\n         * {\n         *     tooltip?: number\n         *     label?: number\n         *     itemName?: number\n         *     seriesName?: number\n         * }\n         * ```\n         *\n         * A `series.encode` can specified these fields:\n         * ```js\n         * encode: {\n         *     // \"3, 1, 5\" is the index of data dimension.\n         *     tooltip: [3, 1, 5],\n         *     label: [0, 3],\n         *     ...\n         * }\n         * ```\n         * `otherDims` is the parse result of the `series.encode` above, like:\n         * ```js\n         * // Suppose the index of this data dimension is `3`.\n         * this.otherDims = {\n         *     // `3` is at the index `0` of the `encode.tooltip`\n         *     tooltip: 0,\n         *     // `3` is at the index `1` of the `encode.label`\n         *     label: 1\n         * };\n         * ```\n         *\n         * This prop should never be `null`/`undefined` after initialized.\n         */\n        this.otherDims = {};\n\n        if (opt != null) {\n          extend(this, opt);\n        }\n      }\n\n      return SeriesDimensionDefine;\n    }();\n\n    var inner$4 = makeInner();\n    var dimTypeShort = {\n      float: 'f',\n      int: 'i',\n      ordinal: 'o',\n      number: 'n',\n      time: 't'\n    };\n    /**\n     * Represents the dimension requirement of a series.\n     *\n     * NOTICE:\n     * When there are too many dimensions in dataset and many series, only the used dimensions\n     * (i.e., used by coord sys and declared in `series.encode`) are add to `dimensionDefineList`.\n     * But users may query data by other unused dimension names.\n     * In this case, users can only query data if and only if they have defined dimension names\n     * via ec option, so we provide `getDimensionIndexFromSource`, which only query them from\n     * `source` dimensions.\n     */\n\n    var SeriesDataSchema =\n    /** @class */\n    function () {\n      function SeriesDataSchema(opt) {\n        this.dimensions = opt.dimensions;\n        this._dimOmitted = opt.dimensionOmitted;\n        this.source = opt.source;\n        this._fullDimCount = opt.fullDimensionCount;\n\n        this._updateDimOmitted(opt.dimensionOmitted);\n      }\n\n      SeriesDataSchema.prototype.isDimensionOmitted = function () {\n        return this._dimOmitted;\n      };\n\n      SeriesDataSchema.prototype._updateDimOmitted = function (dimensionOmitted) {\n        this._dimOmitted = dimensionOmitted;\n\n        if (!dimensionOmitted) {\n          return;\n        }\n\n        if (!this._dimNameMap) {\n          this._dimNameMap = ensureSourceDimNameMap(this.source);\n        }\n      };\n      /**\n       * @caution Can only be used when `dimensionOmitted: true`.\n       *\n       * Get index by user defined dimension name (i.e., not internal generate name).\n       * That is, get index from `dimensionsDefine`.\n       * If no `dimensionsDefine`, or no name get, return -1.\n       */\n\n\n      SeriesDataSchema.prototype.getSourceDimensionIndex = function (dimName) {\n        return retrieve2(this._dimNameMap.get(dimName), -1);\n      };\n      /**\n       * @caution Can only be used when `dimensionOmitted: true`.\n       *\n       * Notice: may return `null`/`undefined` if user not specify dimension names.\n       */\n\n\n      SeriesDataSchema.prototype.getSourceDimension = function (dimIndex) {\n        var dimensionsDefine = this.source.dimensionsDefine;\n\n        if (dimensionsDefine) {\n          return dimensionsDefine[dimIndex];\n        }\n      };\n\n      SeriesDataSchema.prototype.makeStoreSchema = function () {\n        var dimCount = this._fullDimCount;\n        var willRetrieveDataByName = shouldRetrieveDataByName(this.source);\n        var makeHashStrict = !shouldOmitUnusedDimensions(dimCount); // If source don't have dimensions or series don't omit unsed dimensions.\n        // Generate from seriesDimList directly\n\n        var dimHash = '';\n        var dims = [];\n\n        for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < dimCount; fullDimIdx++) {\n          var property = void 0;\n          var type = void 0;\n          var ordinalMeta = void 0;\n          var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n          if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n            property = willRetrieveDataByName ? seriesDimDef.name : null;\n            type = seriesDimDef.type;\n            ordinalMeta = seriesDimDef.ordinalMeta;\n            seriesDimIdx++;\n          } else {\n            var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n            if (sourceDimDef) {\n              property = willRetrieveDataByName ? sourceDimDef.name : null;\n              type = sourceDimDef.type;\n            }\n          }\n\n          dims.push({\n            property: property,\n            type: type,\n            ordinalMeta: ordinalMeta\n          }); // If retrieving data by index,\n          //   use <index, type, ordinalMeta> to determine whether data can be shared.\n          //   (Because in this case there might be no dimension name defined in dataset, but indices always exists).\n          //   (Indices are always 0, 1, 2, ..., so we can ignore them to shorten the hash).\n          // Otherwise if retrieving data by property name (like `data: [{aa: 123, bb: 765}, ...]`),\n          //   use <property, type, ordinalMeta> in hash.\n\n          if (willRetrieveDataByName && property != null // For data stack, we have make sure each series has its own dim on this store.\n          // So we do not add property to hash to make sure they can share this store.\n          && (!seriesDimDef || !seriesDimDef.isCalculationCoord)) {\n            dimHash += makeHashStrict // Use escape character '`' in case that property name contains '$'.\n            ? property.replace(/\\`/g, '`1').replace(/\\$/g, '`2') // For better performance, when there are large dimensions, tolerant this defects that hardly meet.\n            : property;\n          }\n\n          dimHash += '$';\n          dimHash += dimTypeShort[type] || 'f';\n\n          if (ordinalMeta) {\n            dimHash += ordinalMeta.uid;\n          }\n\n          dimHash += '$';\n        } // Source from endpoint(usually series) will be read differently\n        // when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different.\n        // So we use this three props as key.\n\n\n        var source = this.source;\n        var hash = [source.seriesLayoutBy, source.startIndex, dimHash].join('$$');\n        return {\n          dimensions: dims,\n          hash: hash\n        };\n      };\n\n      SeriesDataSchema.prototype.makeOutputDimensionNames = function () {\n        var result = [];\n\n        for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < this._fullDimCount; fullDimIdx++) {\n          var name_1 = void 0;\n          var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc.\n\n          if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {\n            if (!seriesDimDef.isCalculationCoord) {\n              name_1 = seriesDimDef.name;\n            }\n\n            seriesDimIdx++;\n          } else {\n            var sourceDimDef = this.getSourceDimension(fullDimIdx);\n\n            if (sourceDimDef) {\n              name_1 = sourceDimDef.name;\n            }\n          }\n\n          result.push(name_1);\n        }\n\n        return result;\n      };\n\n      SeriesDataSchema.prototype.appendCalculationDimension = function (dimDef) {\n        this.dimensions.push(dimDef);\n        dimDef.isCalculationCoord = true;\n        this._fullDimCount++; // If append dimension on a data store, consider the store\n        // might be shared by different series, series dimensions not\n        // really map to store dimensions.\n\n        this._updateDimOmitted(true);\n      };\n\n      return SeriesDataSchema;\n    }();\n    function isSeriesDataSchema(schema) {\n      return schema instanceof SeriesDataSchema;\n    }\n    function createDimNameMap(dimsDef) {\n      var dataDimNameMap = createHashMap();\n\n      for (var i = 0; i < (dimsDef || []).length; i++) {\n        var dimDefItemRaw = dimsDef[i];\n        var userDimName = isObject(dimDefItemRaw) ? dimDefItemRaw.name : dimDefItemRaw;\n\n        if (userDimName != null && dataDimNameMap.get(userDimName) == null) {\n          dataDimNameMap.set(userDimName, i);\n        }\n      }\n\n      return dataDimNameMap;\n    }\n    function ensureSourceDimNameMap(source) {\n      var innerSource = inner$4(source);\n      return innerSource.dimNameMap || (innerSource.dimNameMap = createDimNameMap(source.dimensionsDefine));\n    }\n    function shouldOmitUnusedDimensions(dimCount) {\n      return dimCount > 30;\n    }\n\n    var isObject$2 = isObject;\n    var map$1 = map;\n    var CtorInt32Array$1 = typeof Int32Array === 'undefined' ? Array : Int32Array; // Use prefix to avoid index to be the same as otherIdList[idx],\n    // which will cause weird update animation.\n\n    var ID_PREFIX = 'e\\0\\0';\n    var INDEX_NOT_FOUND = -1; // type SeriesDimensionIndex = DimensionIndex;\n\n    var TRANSFERABLE_PROPERTIES = ['hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', '_dimSummary', 'userOutput', '_rawData', '_dimValueGetter', '_nameDimIdx', '_idDimIdx', '_nameRepeatCount'];\n    var CLONE_PROPERTIES = ['_approximateExtent']; // -----------------------------\n    // Internal method declarations:\n    // -----------------------------\n\n    var prepareInvertedIndex;\n    var getId;\n    var getIdNameFromStore;\n    var normalizeDimensions;\n    var transferProperties;\n    var cloneListForMapAndSample;\n    var makeIdFromName;\n\n    var SeriesData =\n    /** @class */\n    function () {\n      /**\n       * @param dimensionsInput.dimensions\n       *        For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].\n       *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius\n       */\n      function SeriesData(dimensionsInput, hostModel) {\n        this.type = 'list';\n        this._dimOmitted = false;\n        this._nameList = [];\n        this._idList = []; // Models of data option is stored sparse for optimizing memory cost\n        // Never used yet (not used yet).\n        // private _optionModels: Model[] = [];\n        // Global visual properties after visual coding\n\n        this._visual = {}; // Global layout properties.\n\n        this._layout = {}; // Item visual properties after visual coding\n\n        this._itemVisuals = []; // Item layout properties after layout\n\n        this._itemLayouts = []; // Graphic elements\n\n        this._graphicEls = []; // key: dim, value: extent\n\n        this._approximateExtent = {};\n        this._calculationInfo = {}; // Having detected that there is data item is non primitive type\n        // (in type `OptionDataItemObject`).\n        // Like `data: [ { value: xx, itemStyle: {...} }, ...]`\n        // At present it only happen in `SOURCE_FORMAT_ORIGINAL`.\n\n        this.hasItemOption = false; // Methods that create a new list based on this list should be listed here.\n        // Notice that those method should `RETURN` the new list.\n\n        this.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map']; // Methods that change indices of this list should be listed here.\n\n        this.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];\n        this.DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'];\n        var dimensions;\n        var assignStoreDimIdx = false;\n\n        if (isSeriesDataSchema(dimensionsInput)) {\n          dimensions = dimensionsInput.dimensions;\n          this._dimOmitted = dimensionsInput.isDimensionOmitted();\n          this._schema = dimensionsInput;\n        } else {\n          assignStoreDimIdx = true;\n          dimensions = dimensionsInput;\n        }\n\n        dimensions = dimensions || ['x', 'y'];\n        var dimensionInfos = {};\n        var dimensionNames = [];\n        var invertedIndicesMap = {};\n        var needsHasOwn = false;\n        var emptyObj = {};\n\n        for (var i = 0; i < dimensions.length; i++) {\n          // Use the original dimensions[i], where other flag props may exists.\n          var dimInfoInput = dimensions[i];\n          var dimensionInfo = isString(dimInfoInput) ? new SeriesDimensionDefine({\n            name: dimInfoInput\n          }) : !(dimInfoInput instanceof SeriesDimensionDefine) ? new SeriesDimensionDefine(dimInfoInput) : dimInfoInput;\n          var dimensionName = dimensionInfo.name;\n          dimensionInfo.type = dimensionInfo.type || 'float';\n\n          if (!dimensionInfo.coordDim) {\n            dimensionInfo.coordDim = dimensionName;\n            dimensionInfo.coordDimIndex = 0;\n          }\n\n          var otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {};\n          dimensionNames.push(dimensionName);\n          dimensionInfos[dimensionName] = dimensionInfo;\n\n          if (emptyObj[dimensionName] != null) {\n            needsHasOwn = true;\n          }\n\n          if (dimensionInfo.createInvertedIndices) {\n            invertedIndicesMap[dimensionName] = [];\n          }\n\n          if (otherDims.itemName === 0) {\n            this._nameDimIdx = i;\n          }\n\n          if (otherDims.itemId === 0) {\n            this._idDimIdx = i;\n          }\n\n          if (\"development\" !== 'production') {\n            assert(assignStoreDimIdx || dimensionInfo.storeDimIndex >= 0);\n          }\n\n          if (assignStoreDimIdx) {\n            dimensionInfo.storeDimIndex = i;\n          }\n        }\n\n        this.dimensions = dimensionNames;\n        this._dimInfos = dimensionInfos;\n\n        this._initGetDimensionInfo(needsHasOwn);\n\n        this.hostModel = hostModel;\n        this._invertedIndicesMap = invertedIndicesMap;\n\n        if (this._dimOmitted) {\n          var dimIdxToName_1 = this._dimIdxToName = createHashMap();\n          each(dimensionNames, function (dimName) {\n            dimIdxToName_1.set(dimensionInfos[dimName].storeDimIndex, dimName);\n          });\n        }\n      }\n      /**\n       *\n       * Get concrete dimension name by dimension name or dimension index.\n       * If input a dimension name, do not validate whether the dimension name exits.\n       *\n       * @caution\n       * @param dim Must make sure the dimension is `SeriesDimensionLoose`.\n       * Because only those dimensions will have auto-generated dimension names if not\n       * have a user-specified name, and other dimensions will get a return of null/undefined.\n       *\n       * @notice Because of this reason, should better use `getDimensionIndex` instead, for examples:\n       * ```js\n       * const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx);\n       * ```\n       *\n       * @return Concrete dim name.\n       */\n\n\n      SeriesData.prototype.getDimension = function (dim) {\n        var dimIdx = this._recognizeDimIndex(dim);\n\n        if (dimIdx == null) {\n          return dim;\n        }\n\n        dimIdx = dim;\n\n        if (!this._dimOmitted) {\n          return this.dimensions[dimIdx];\n        } // Retrieve from series dimension definition because it probably contains\n        // generated dimension name (like 'x', 'y').\n\n\n        var dimName = this._dimIdxToName.get(dimIdx);\n\n        if (dimName != null) {\n          return dimName;\n        }\n\n        var sourceDimDef = this._schema.getSourceDimension(dimIdx);\n\n        if (sourceDimDef) {\n          return sourceDimDef.name;\n        }\n      };\n      /**\n       * Get dimension index in data store. Return -1 if not found.\n       * Can be used to index value from getRawValue.\n       */\n\n\n      SeriesData.prototype.getDimensionIndex = function (dim) {\n        var dimIdx = this._recognizeDimIndex(dim);\n\n        if (dimIdx != null) {\n          return dimIdx;\n        }\n\n        if (dim == null) {\n          return -1;\n        }\n\n        var dimInfo = this._getDimInfo(dim);\n\n        return dimInfo ? dimInfo.storeDimIndex : this._dimOmitted ? this._schema.getSourceDimensionIndex(dim) : -1;\n      };\n      /**\n       * The meanings of the input parameter `dim`:\n       *\n       * + If dim is a number (e.g., `1`), it means the index of the dimension.\n       *   For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.\n       * + If dim is a number-like string (e.g., `\"1\"`):\n       *     + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`,\n       *        it means that concrete name.\n       *     + If not, it will be converted to a number, which means the index of the dimension.\n       *        (why? because of the backward compatibility. We have been tolerating number-like string in\n       *        dimension setting, although now it seems that it is not a good idea.)\n       *     For example, `visualMap[i].dimension: \"1\"` is the same meaning as `visualMap[i].dimension: 1`,\n       *     if no dimension name is defined as `\"1\"`.\n       * + If dim is a not-number-like string, it means the concrete dim name.\n       *   For example, it can be be default name `\"x\"`, `\"y\"`, `\"z\"`, `\"lng\"`, `\"lat\"`, `\"angle\"`, `\"radius\"`,\n       *   or customized in `dimensions` property of option like `\"age\"`.\n       *\n       * @return recognized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`).\n       */\n\n\n      SeriesData.prototype._recognizeDimIndex = function (dim) {\n        if (isNumber(dim) // If being a number-like string but not being defined as a dimension name.\n        || dim != null && !isNaN(dim) && !this._getDimInfo(dim) && (!this._dimOmitted || this._schema.getSourceDimensionIndex(dim) < 0)) {\n          return +dim;\n        }\n      };\n\n      SeriesData.prototype._getStoreDimIndex = function (dim) {\n        var dimIdx = this.getDimensionIndex(dim);\n\n        if (\"development\" !== 'production') {\n          if (dimIdx == null) {\n            throw new Error('Unknown dimension ' + dim);\n          }\n        }\n\n        return dimIdx;\n      };\n      /**\n       * Get type and calculation info of particular dimension\n       * @param dim\n       *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius\n       *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'\n       */\n\n\n      SeriesData.prototype.getDimensionInfo = function (dim) {\n        // Do not clone, because there may be categories in dimInfo.\n        return this._getDimInfo(this.getDimension(dim));\n      };\n\n      SeriesData.prototype._initGetDimensionInfo = function (needsHasOwn) {\n        var dimensionInfos = this._dimInfos;\n        this._getDimInfo = needsHasOwn ? function (dimName) {\n          return dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined;\n        } : function (dimName) {\n          return dimensionInfos[dimName];\n        };\n      };\n      /**\n       * concrete dimension name list on coord.\n       */\n\n\n      SeriesData.prototype.getDimensionsOnCoord = function () {\n        return this._dimSummary.dataDimsOnCoord.slice();\n      };\n\n      SeriesData.prototype.mapDimension = function (coordDim, idx) {\n        var dimensionsSummary = this._dimSummary;\n\n        if (idx == null) {\n          return dimensionsSummary.encodeFirstDimNotExtra[coordDim];\n        }\n\n        var dims = dimensionsSummary.encode[coordDim];\n        return dims ? dims[idx] : null;\n      };\n\n      SeriesData.prototype.mapDimensionsAll = function (coordDim) {\n        var dimensionsSummary = this._dimSummary;\n        var dims = dimensionsSummary.encode[coordDim];\n        return (dims || []).slice();\n      };\n\n      SeriesData.prototype.getStore = function () {\n        return this._store;\n      };\n      /**\n       * Initialize from data\n       * @param data source or data or data store.\n       * @param nameList The name of a datum is used on data diff and\n       *        default label/tooltip.\n       *        A name can be specified in encode.itemName,\n       *        or dataItem.name (only for series option data),\n       *        or provided in nameList from outside.\n       */\n\n\n      SeriesData.prototype.initData = function (data, nameList, dimValueGetter) {\n        var _this = this;\n\n        var store;\n\n        if (data instanceof DataStore) {\n          store = data;\n        }\n\n        if (!store) {\n          var dimensions = this.dimensions;\n          var provider = isSourceInstance(data) || isArrayLike(data) ? new DefaultDataProvider(data, dimensions.length) : data;\n          store = new DataStore();\n          var dimensionInfos = map$1(dimensions, function (dimName) {\n            return {\n              type: _this._dimInfos[dimName].type,\n              property: dimName\n            };\n          });\n          store.initData(provider, dimensionInfos, dimValueGetter);\n        }\n\n        this._store = store; // Reset\n\n        this._nameList = (nameList || []).slice();\n        this._idList = [];\n        this._nameRepeatCount = {};\n\n        this._doInit(0, store.count()); // Cache summary info for fast visit. See \"dimensionHelper\".\n        // Needs to be initialized after store is prepared.\n\n\n        this._dimSummary = summarizeDimensions(this, this._schema);\n        this.userOutput = this._dimSummary.userOutput;\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       */\n\n\n      SeriesData.prototype.appendData = function (data) {\n        var range = this._store.appendData(data);\n\n        this._doInit(range[0], range[1]);\n      };\n      /**\n       * Caution: Can be only called on raw data (before `this._indices` created).\n       * This method does not modify `rawData` (`dataProvider`), but only\n       * add values to store.\n       *\n       * The final count will be increased by `Math.max(values.length, names.length)`.\n       *\n       * @param values That is the SourceType: 'arrayRows', like\n       *        [\n       *            [12, 33, 44],\n       *            [NaN, 43, 1],\n       *            ['-', 'asdf', 0]\n       *        ]\n       *        Each item is exactly corresponding to a dimension.\n       */\n\n\n      SeriesData.prototype.appendValues = function (values, names) {\n        var _a = this._store.appendValues(values, names.length),\n            start = _a.start,\n            end = _a.end;\n\n        var shouldMakeIdFromName = this._shouldMakeIdFromName();\n\n        this._updateOrdinalMeta();\n\n        if (names) {\n          for (var idx = start; idx < end; idx++) {\n            var sourceIdx = idx - start;\n            this._nameList[idx] = names[sourceIdx];\n\n            if (shouldMakeIdFromName) {\n              makeIdFromName(this, idx);\n            }\n          }\n        }\n      };\n\n      SeriesData.prototype._updateOrdinalMeta = function () {\n        var store = this._store;\n        var dimensions = this.dimensions;\n\n        for (var i = 0; i < dimensions.length; i++) {\n          var dimInfo = this._dimInfos[dimensions[i]];\n\n          if (dimInfo.ordinalMeta) {\n            store.collectOrdinalMeta(dimInfo.storeDimIndex, dimInfo.ordinalMeta);\n          }\n        }\n      };\n\n      SeriesData.prototype._shouldMakeIdFromName = function () {\n        var provider = this._store.getProvider();\n\n        return this._idDimIdx == null && provider.getSource().sourceFormat !== SOURCE_FORMAT_TYPED_ARRAY && !provider.fillStorage;\n      };\n\n      SeriesData.prototype._doInit = function (start, end) {\n        if (start >= end) {\n          return;\n        }\n\n        var store = this._store;\n        var provider = store.getProvider();\n\n        this._updateOrdinalMeta();\n\n        var nameList = this._nameList;\n        var idList = this._idList;\n        var sourceFormat = provider.getSource().sourceFormat;\n        var isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL; // Each data item is value\n        // [1, 2]\n        // 2\n        // Bar chart, line chart which uses category axis\n        // only gives the 'y' value. 'x' value is the indices of category\n        // Use a tempValue to normalize the value to be a (x, y) value\n        // If dataItem is {name: ...} or {id: ...}, it has highest priority.\n        // This kind of ids and names are always stored `_nameList` and `_idList`.\n\n        if (isFormatOriginal && !provider.pure) {\n          var sharedDataItem = [];\n\n          for (var idx = start; idx < end; idx++) {\n            // NOTICE: Try not to write things into dataItem\n            var dataItem = provider.getItem(idx, sharedDataItem);\n\n            if (!this.hasItemOption && isDataItemOption(dataItem)) {\n              this.hasItemOption = true;\n            }\n\n            if (dataItem) {\n              var itemName = dataItem.name;\n\n              if (nameList[idx] == null && itemName != null) {\n                nameList[idx] = convertOptionIdName(itemName, null);\n              }\n\n              var itemId = dataItem.id;\n\n              if (idList[idx] == null && itemId != null) {\n                idList[idx] = convertOptionIdName(itemId, null);\n              }\n            }\n          }\n        }\n\n        if (this._shouldMakeIdFromName()) {\n          for (var idx = start; idx < end; idx++) {\n            makeIdFromName(this, idx);\n          }\n        }\n\n        prepareInvertedIndex(this);\n      };\n      /**\n       * PENDING: In fact currently this function is only used to short-circuit\n       * the calling of `scale.unionExtentFromData` when data have been filtered by modules\n       * like \"dataZoom\". `scale.unionExtentFromData` is used to calculate data extent for series on\n       * an axis, but if a \"axis related data filter module\" is used, the extent of the axis have\n       * been fixed and no need to calling `scale.unionExtentFromData` actually.\n       * But if we add \"custom data filter\" in future, which is not \"axis related\", this method may\n       * be still needed.\n       *\n       * Optimize for the scenario that data is filtered by a given extent.\n       * Consider that if data amount is more than hundreds of thousand,\n       * extent calculation will cost more than 10ms and the cache will\n       * be erased because of the filtering.\n       */\n\n\n      SeriesData.prototype.getApproximateExtent = function (dim) {\n        return this._approximateExtent[dim] || this._store.getDataExtent(this._getStoreDimIndex(dim));\n      };\n      /**\n       * Calculate extent on a filtered data might be time consuming.\n       * Approximate extent is only used for: calculate extent of filtered data outside.\n       */\n\n\n      SeriesData.prototype.setApproximateExtent = function (extent, dim) {\n        dim = this.getDimension(dim);\n        this._approximateExtent[dim] = extent.slice();\n      };\n\n      SeriesData.prototype.getCalculationInfo = function (key) {\n        return this._calculationInfo[key];\n      };\n\n      SeriesData.prototype.setCalculationInfo = function (key, value) {\n        isObject$2(key) ? extend(this._calculationInfo, key) : this._calculationInfo[key] = value;\n      };\n      /**\n       * @return Never be null/undefined. `number` will be converted to string. Because:\n       * In most cases, name is used in display, where returning a string is more convenient.\n       * In other cases, name is used in query (see `indexOfName`), where we can keep the\n       * rule that name `2` equals to name `'2'`.\n       */\n\n\n      SeriesData.prototype.getName = function (idx) {\n        var rawIndex = this.getRawIndex(idx);\n        var name = this._nameList[rawIndex];\n\n        if (name == null && this._nameDimIdx != null) {\n          name = getIdNameFromStore(this, this._nameDimIdx, rawIndex);\n        }\n\n        if (name == null) {\n          name = '';\n        }\n\n        return name;\n      };\n\n      SeriesData.prototype._getCategory = function (dimIdx, idx) {\n        var ordinal = this._store.get(dimIdx, idx);\n\n        var ordinalMeta = this._store.getOrdinalMeta(dimIdx);\n\n        if (ordinalMeta) {\n          return ordinalMeta.categories[ordinal];\n        }\n\n        return ordinal;\n      };\n      /**\n       * @return Never null/undefined. `number` will be converted to string. Because:\n       * In all cases having encountered at present, id is used in making diff comparison, which\n       * are usually based on hash map. We can keep the rule that the internal id are always string\n       * (treat `2` is the same as `'2'`) to make the related logic simple.\n       */\n\n\n      SeriesData.prototype.getId = function (idx) {\n        return getId(this, this.getRawIndex(idx));\n      };\n\n      SeriesData.prototype.count = function () {\n        return this._store.count();\n      };\n      /**\n       * Get value. Return NaN if idx is out of range.\n       *\n       * @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead.\n       */\n\n\n      SeriesData.prototype.get = function (dim, idx) {\n        var store = this._store;\n        var dimInfo = this._dimInfos[dim];\n\n        if (dimInfo) {\n          return store.get(dimInfo.storeDimIndex, idx);\n        }\n      };\n      /**\n       * @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead.\n       */\n\n\n      SeriesData.prototype.getByRawIndex = function (dim, rawIdx) {\n        var store = this._store;\n        var dimInfo = this._dimInfos[dim];\n\n        if (dimInfo) {\n          return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx);\n        }\n      };\n\n      SeriesData.prototype.getIndices = function () {\n        return this._store.getIndices();\n      };\n\n      SeriesData.prototype.getDataExtent = function (dim) {\n        return this._store.getDataExtent(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getSum = function (dim) {\n        return this._store.getSum(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getMedian = function (dim) {\n        return this._store.getMedian(this._getStoreDimIndex(dim));\n      };\n\n      SeriesData.prototype.getValues = function (dimensions, idx) {\n        var _this = this;\n\n        var store = this._store;\n        return isArray(dimensions) ? store.getValues(map$1(dimensions, function (dim) {\n          return _this._getStoreDimIndex(dim);\n        }), idx) : store.getValues(dimensions);\n      };\n      /**\n       * If value is NaN. Including '-'\n       * Only check the coord dimensions.\n       */\n\n\n      SeriesData.prototype.hasValue = function (idx) {\n        var dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord;\n\n        for (var i = 0, len = dataDimIndicesOnCoord.length; i < len; i++) {\n          // Ordinal type originally can be string or number.\n          // But when an ordinal type is used on coord, it can\n          // not be string but only number. So we can also use isNaN.\n          if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx))) {\n            return false;\n          }\n        }\n\n        return true;\n      };\n      /**\n       * Retrieve the index with given name\n       */\n\n\n      SeriesData.prototype.indexOfName = function (name) {\n        for (var i = 0, len = this._store.count(); i < len; i++) {\n          if (this.getName(i) === name) {\n            return i;\n          }\n        }\n\n        return -1;\n      };\n\n      SeriesData.prototype.getRawIndex = function (idx) {\n        return this._store.getRawIndex(idx);\n      };\n\n      SeriesData.prototype.indexOfRawIndex = function (rawIndex) {\n        return this._store.indexOfRawIndex(rawIndex);\n      };\n      /**\n       * Only support the dimension which inverted index created.\n       * Do not support other cases until required.\n       * @param dim concrete dim\n       * @param value ordinal index\n       * @return rawIndex\n       */\n\n\n      SeriesData.prototype.rawIndexOf = function (dim, value) {\n        var invertedIndices = dim && this._invertedIndicesMap[dim];\n\n        if (\"development\" !== 'production') {\n          if (!invertedIndices) {\n            throw new Error('Do not supported yet');\n          }\n        }\n\n        var rawIndex = invertedIndices[value];\n\n        if (rawIndex == null || isNaN(rawIndex)) {\n          return INDEX_NOT_FOUND;\n        }\n\n        return rawIndex;\n      };\n      /**\n       * Retrieve the index of nearest value\n       * @param dim\n       * @param value\n       * @param [maxDistance=Infinity]\n       * @return If and only if multiple indices has\n       *         the same value, they are put to the result.\n       */\n\n\n      SeriesData.prototype.indicesOfNearest = function (dim, value, maxDistance) {\n        return this._store.indicesOfNearest(this._getStoreDimIndex(dim), value, maxDistance);\n      };\n\n      SeriesData.prototype.each = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n\n        this._store.each(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n      };\n\n      SeriesData.prototype.filterSelf = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n        this._store = this._store.filter(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n        return this;\n      };\n      /**\n       * Select data in range. (For optimization of filter)\n       * (Manually inline code, support 5 million data filtering in data zoom.)\n       */\n\n\n      SeriesData.prototype.selectRange = function (range) {\n\n        var _this = this;\n\n        var innerRange = {};\n        var dims = keys(range);\n        each(dims, function (dim) {\n          var dimIdx = _this._getStoreDimIndex(dim);\n\n          innerRange[dimIdx] = range[dim];\n        });\n        this._store = this._store.selectRange(innerRange);\n        return this;\n      };\n      /* eslint-enable max-len */\n\n\n      SeriesData.prototype.mapArray = function (dims, cb, ctx) {\n\n        if (isFunction(dims)) {\n          ctx = cb;\n          cb = dims;\n          dims = [];\n        } // ctxCompat just for compat echarts3\n\n\n        ctx = ctx || this;\n        var result = [];\n        this.each(dims, function () {\n          result.push(cb && cb.apply(this, arguments));\n        }, ctx);\n        return result;\n      };\n\n      SeriesData.prototype.map = function (dims, cb, ctx, ctxCompat) {\n\n        var fCtx = ctx || ctxCompat || this;\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this);\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.map(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n        return list;\n      };\n\n      SeriesData.prototype.modify = function (dims, cb, ctx, ctxCompat) {\n        var _this = this; // ctxCompat just for compat echarts3\n\n\n        var fCtx = ctx || ctxCompat || this;\n\n        if (\"development\" !== 'production') {\n          each(normalizeDimensions(dims), function (dim) {\n            var dimInfo = _this.getDimensionInfo(dim);\n\n            if (!dimInfo.isCalculationCoord) {\n              console.error('Danger: only stack dimension can be modified');\n            }\n          });\n        }\n\n        var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); // If do shallow clone here, if there are too many stacked series,\n        // it still cost lots of memory, because `_store.dimensions` are not shared.\n        // We should consider there probably be shallow clone happen in each series\n        // in consequent filter/map.\n\n        this._store.modify(dimIndices, fCtx ? bind(cb, fCtx) : cb);\n      };\n      /**\n       * Large data down sampling on given dimension\n       * @param sampleIndex Sample index for name and id\n       */\n\n\n      SeriesData.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.downSample(this._getStoreDimIndex(dimension), rate, sampleValue, sampleIndex);\n        return list;\n      };\n      /**\n       * Large data down sampling using largest-triangle-three-buckets\n       * @param {string} valueDimension\n       * @param {number} targetCount\n       */\n\n\n      SeriesData.prototype.lttbDownSample = function (valueDimension, rate) {\n        var list = cloneListForMapAndSample(this);\n        list._store = this._store.lttbDownSample(this._getStoreDimIndex(valueDimension), rate);\n        return list;\n      };\n\n      SeriesData.prototype.getRawDataItem = function (idx) {\n        return this._store.getRawDataItem(idx);\n      };\n      /**\n       * Get model of one data item.\n       */\n      // TODO: Type of data item\n\n\n      SeriesData.prototype.getItemModel = function (idx) {\n        var hostModel = this.hostModel;\n        var dataItem = this.getRawDataItem(idx);\n        return new Model(dataItem, hostModel, hostModel && hostModel.ecModel);\n      };\n      /**\n       * Create a data differ\n       */\n\n\n      SeriesData.prototype.diff = function (otherList) {\n        var thisList = this;\n        return new DataDiffer(otherList ? otherList.getStore().getIndices() : [], this.getStore().getIndices(), function (idx) {\n          return getId(otherList, idx);\n        }, function (idx) {\n          return getId(thisList, idx);\n        });\n      };\n      /**\n       * Get visual property.\n       */\n\n\n      SeriesData.prototype.getVisual = function (key) {\n        var visual = this._visual;\n        return visual && visual[key];\n      };\n\n      SeriesData.prototype.setVisual = function (kvObj, val) {\n        this._visual = this._visual || {};\n\n        if (isObject$2(kvObj)) {\n          extend(this._visual, kvObj);\n        } else {\n          this._visual[kvObj] = val;\n        }\n      };\n      /**\n       * Get visual property of single data item\n       */\n      // eslint-disable-next-line\n\n\n      SeriesData.prototype.getItemVisual = function (idx, key) {\n        var itemVisual = this._itemVisuals[idx];\n        var val = itemVisual && itemVisual[key];\n\n        if (val == null) {\n          // Use global visual property\n          return this.getVisual(key);\n        }\n\n        return val;\n      };\n      /**\n       * If exists visual property of single data item\n       */\n\n\n      SeriesData.prototype.hasItemVisual = function () {\n        return this._itemVisuals.length > 0;\n      };\n      /**\n       * Make sure itemVisual property is unique\n       */\n      // TODO: use key to save visual to reduce memory.\n\n\n      SeriesData.prototype.ensureUniqueItemVisual = function (idx, key) {\n        var itemVisuals = this._itemVisuals;\n        var itemVisual = itemVisuals[idx];\n\n        if (!itemVisual) {\n          itemVisual = itemVisuals[idx] = {};\n        }\n\n        var val = itemVisual[key];\n\n        if (val == null) {\n          val = this.getVisual(key); // TODO Performance?\n\n          if (isArray(val)) {\n            val = val.slice();\n          } else if (isObject$2(val)) {\n            val = extend({}, val);\n          }\n\n          itemVisual[key] = val;\n        }\n\n        return val;\n      }; // eslint-disable-next-line\n\n\n      SeriesData.prototype.setItemVisual = function (idx, key, value) {\n        var itemVisual = this._itemVisuals[idx] || {};\n        this._itemVisuals[idx] = itemVisual;\n\n        if (isObject$2(key)) {\n          extend(itemVisual, key);\n        } else {\n          itemVisual[key] = value;\n        }\n      };\n      /**\n       * Clear itemVisuals and list visual.\n       */\n\n\n      SeriesData.prototype.clearAllVisual = function () {\n        this._visual = {};\n        this._itemVisuals = [];\n      };\n\n      SeriesData.prototype.setLayout = function (key, val) {\n        isObject$2(key) ? extend(this._layout, key) : this._layout[key] = val;\n      };\n      /**\n       * Get layout property.\n       */\n\n\n      SeriesData.prototype.getLayout = function (key) {\n        return this._layout[key];\n      };\n      /**\n       * Get layout of single data item\n       */\n\n\n      SeriesData.prototype.getItemLayout = function (idx) {\n        return this._itemLayouts[idx];\n      };\n      /**\n       * Set layout of single data item\n       */\n\n\n      SeriesData.prototype.setItemLayout = function (idx, layout, merge) {\n        this._itemLayouts[idx] = merge ? extend(this._itemLayouts[idx] || {}, layout) : layout;\n      };\n      /**\n       * Clear all layout of single data item\n       */\n\n\n      SeriesData.prototype.clearItemLayouts = function () {\n        this._itemLayouts.length = 0;\n      };\n      /**\n       * Set graphic element relative to data. It can be set as null\n       */\n\n\n      SeriesData.prototype.setItemGraphicEl = function (idx, el) {\n        var seriesIndex = this.hostModel && this.hostModel.seriesIndex;\n        setCommonECData(seriesIndex, this.dataType, idx, el);\n        this._graphicEls[idx] = el;\n      };\n\n      SeriesData.prototype.getItemGraphicEl = function (idx) {\n        return this._graphicEls[idx];\n      };\n\n      SeriesData.prototype.eachItemGraphicEl = function (cb, context) {\n        each(this._graphicEls, function (el, idx) {\n          if (el) {\n            cb && cb.call(context, el, idx);\n          }\n        });\n      };\n      /**\n       * Shallow clone a new list except visual and layout properties, and graph elements.\n       * New list only change the indices.\n       */\n\n\n      SeriesData.prototype.cloneShallow = function (list) {\n        if (!list) {\n          list = new SeriesData(this._schema ? this._schema : map$1(this.dimensions, this._getDimInfo, this), this.hostModel);\n        }\n\n        transferProperties(list, this);\n        list._store = this._store;\n        return list;\n      };\n      /**\n       * Wrap some method to add more feature\n       */\n\n\n      SeriesData.prototype.wrapMethod = function (methodName, injectFunction) {\n        var originalMethod = this[methodName];\n\n        if (!isFunction(originalMethod)) {\n          return;\n        }\n\n        this.__wrappedMethods = this.__wrappedMethods || [];\n\n        this.__wrappedMethods.push(methodName);\n\n        this[methodName] = function () {\n          var res = originalMethod.apply(this, arguments);\n          return injectFunction.apply(this, [res].concat(slice(arguments)));\n        };\n      }; // ----------------------------------------------------------\n      // A work around for internal method visiting private member.\n      // ----------------------------------------------------------\n\n\n      SeriesData.internalField = function () {\n        prepareInvertedIndex = function (data) {\n          var invertedIndicesMap = data._invertedIndicesMap;\n          each(invertedIndicesMap, function (invertedIndices, dim) {\n            var dimInfo = data._dimInfos[dim]; // Currently, only dimensions that has ordinalMeta can create inverted indices.\n\n            var ordinalMeta = dimInfo.ordinalMeta;\n            var store = data._store;\n\n            if (ordinalMeta) {\n              invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array$1(ordinalMeta.categories.length); // The default value of TypedArray is 0. To avoid miss\n              // mapping to 0, we should set it as INDEX_NOT_FOUND.\n\n              for (var i = 0; i < invertedIndices.length; i++) {\n                invertedIndices[i] = INDEX_NOT_FOUND;\n              }\n\n              for (var i = 0; i < store.count(); i++) {\n                // Only support the case that all values are distinct.\n                invertedIndices[store.get(dimInfo.storeDimIndex, i)] = i;\n              }\n            }\n          });\n        };\n\n        getIdNameFromStore = function (data, dimIdx, idx) {\n          return convertOptionIdName(data._getCategory(dimIdx, idx), null);\n        };\n        /**\n         * @see the comment of `List['getId']`.\n         */\n\n\n        getId = function (data, rawIndex) {\n          var id = data._idList[rawIndex];\n\n          if (id == null && data._idDimIdx != null) {\n            id = getIdNameFromStore(data, data._idDimIdx, rawIndex);\n          }\n\n          if (id == null) {\n            id = ID_PREFIX + rawIndex;\n          }\n\n          return id;\n        };\n\n        normalizeDimensions = function (dimensions) {\n          if (!isArray(dimensions)) {\n            dimensions = dimensions != null ? [dimensions] : [];\n          }\n\n          return dimensions;\n        };\n        /**\n         * Data in excludeDimensions is copied, otherwise transferred.\n         */\n\n\n        cloneListForMapAndSample = function (original) {\n          var list = new SeriesData(original._schema ? original._schema : map$1(original.dimensions, original._getDimInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked\n\n          transferProperties(list, original);\n          return list;\n        };\n\n        transferProperties = function (target, source) {\n          each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {\n            if (source.hasOwnProperty(propName)) {\n              target[propName] = source[propName];\n            }\n          });\n          target.__wrappedMethods = source.__wrappedMethods;\n          each(CLONE_PROPERTIES, function (propName) {\n            target[propName] = clone(source[propName]);\n          });\n          target._calculationInfo = extend({}, source._calculationInfo);\n        };\n\n        makeIdFromName = function (data, idx) {\n          var nameList = data._nameList;\n          var idList = data._idList;\n          var nameDimIdx = data._nameDimIdx;\n          var idDimIdx = data._idDimIdx;\n          var name = nameList[idx];\n          var id = idList[idx];\n\n          if (name == null && nameDimIdx != null) {\n            nameList[idx] = name = getIdNameFromStore(data, nameDimIdx, idx);\n          }\n\n          if (id == null && idDimIdx != null) {\n            idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx);\n          }\n\n          if (id == null && name != null) {\n            var nameRepeatCount = data._nameRepeatCount;\n            var nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1;\n            id = name;\n\n            if (nmCnt > 1) {\n              id += '__ec__' + nmCnt;\n            }\n\n            idList[idx] = id;\n          }\n        };\n      }();\n\n      return SeriesData;\n    }();\n\n    /**\n     * For outside usage compat (like echarts-gl are using it).\n     */\n\n    function createDimensions(source, opt) {\n      return prepareSeriesDataSchema(source, opt).dimensions;\n    }\n    /**\n     * This method builds the relationship between:\n     * + \"what the coord sys or series requires (see `coordDimensions`)\",\n     * + \"what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)\"\n     * + \"what the data source provids (see `source`)\".\n     *\n     * Some guess strategy will be adapted if user does not define something.\n     * If no 'value' dimension specified, the first no-named dimension will be\n     * named as 'value'.\n     *\n     * @return The results are always sorted by `storeDimIndex` asc.\n     */\n\n    function prepareSeriesDataSchema( // TODO: TYPE completeDimensions type\n    source, opt) {\n      if (!isSourceInstance(source)) {\n        source = createSourceFromSeriesDataOption(source);\n      }\n\n      opt = opt || {};\n      var sysDims = opt.coordDimensions || [];\n      var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || [];\n      var coordDimNameMap = createHashMap();\n      var resultList = [];\n      var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount); // Try to ignore unused dimensions if sharing a high dimension datastore\n      // 30 is an experience value.\n\n      var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount);\n      var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine;\n      var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef);\n      var encodeDef = opt.encodeDefine;\n\n      if (!encodeDef && opt.encodeDefaulter) {\n        encodeDef = opt.encodeDefaulter(source, dimCount);\n      }\n\n      var encodeDefMap = createHashMap(encodeDef);\n      var indicesMap = new CtorInt32Array(dimCount);\n\n      for (var i = 0; i < indicesMap.length; i++) {\n        indicesMap[i] = -1;\n      }\n\n      function getResultItem(dimIdx) {\n        var idx = indicesMap[dimIdx];\n\n        if (idx < 0) {\n          var dimDefItemRaw = dimsDef[dimIdx];\n          var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : {\n            name: dimDefItemRaw\n          };\n          var resultItem = new SeriesDimensionDefine();\n          var userDimName = dimDefItem.name;\n\n          if (userDimName != null && dataDimNameMap.get(userDimName) != null) {\n            // Only if `series.dimensions` is defined in option\n            // displayName, will be set, and dimension will be displayed vertically in\n            // tooltip by default.\n            resultItem.name = resultItem.displayName = userDimName;\n          }\n\n          dimDefItem.type != null && (resultItem.type = dimDefItem.type);\n          dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);\n          var newIdx = resultList.length;\n          indicesMap[dimIdx] = newIdx;\n          resultItem.storeDimIndex = dimIdx;\n          resultList.push(resultItem);\n          return resultItem;\n        }\n\n        return resultList[idx];\n      }\n\n      if (!omitUnusedDimensions) {\n        for (var i = 0; i < dimCount; i++) {\n          getResultItem(i);\n        }\n      } // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.\n\n\n      encodeDefMap.each(function (dataDimsRaw, coordDim) {\n        var dataDims = normalizeToArray(dataDimsRaw).slice(); // Note: It is allowed that `dataDims.length` is `0`, e.g., options is\n        // `{encode: {x: -1, y: 1}}`. Should not filter anything in\n        // this case.\n\n        if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) {\n          encodeDefMap.set(coordDim, false);\n          return;\n        }\n\n        var validDataDims = encodeDefMap.set(coordDim, []);\n        each(dataDims, function (resultDimIdxOrName, idx) {\n          // The input resultDimIdx can be dim name or index.\n          var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName;\n\n          if (resultDimIdx != null && resultDimIdx < dimCount) {\n            validDataDims[idx] = resultDimIdx;\n            applyDim(getResultItem(resultDimIdx), coordDim, idx);\n          }\n        });\n      }); // Apply templates and default order from `sysDims`.\n\n      var availDimIdx = 0;\n      each(sysDims, function (sysDimItemRaw) {\n        var coordDim;\n        var sysDimItemDimsDef;\n        var sysDimItemOtherDims;\n        var sysDimItem;\n\n        if (isString(sysDimItemRaw)) {\n          coordDim = sysDimItemRaw;\n          sysDimItem = {};\n        } else {\n          sysDimItem = sysDimItemRaw;\n          coordDim = sysDimItem.name;\n          var ordinalMeta = sysDimItem.ordinalMeta;\n          sysDimItem.ordinalMeta = null;\n          sysDimItem = extend({}, sysDimItem);\n          sysDimItem.ordinalMeta = ordinalMeta; // `coordDimIndex` should not be set directly.\n\n          sysDimItemDimsDef = sysDimItem.dimsDef;\n          sysDimItemOtherDims = sysDimItem.otherDims;\n          sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null;\n        }\n\n        var dataDims = encodeDefMap.get(coordDim); // negative resultDimIdx means no need to mapping.\n\n        if (dataDims === false) {\n          return;\n        }\n\n        dataDims = normalizeToArray(dataDims); // dimensions provides default dim sequences.\n\n        if (!dataDims.length) {\n          for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {\n            while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) {\n              availDimIdx++;\n            }\n\n            availDimIdx < dimCount && dataDims.push(availDimIdx++);\n          }\n        } // Apply templates.\n\n\n        each(dataDims, function (resultDimIdx, coordDimIndex) {\n          var resultItem = getResultItem(resultDimIdx); // Coordinate system has a higher priority on dim type than source.\n\n          if (isUsingSourceDimensionsDef && sysDimItem.type != null) {\n            resultItem.type = sysDimItem.type;\n          }\n\n          applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);\n\n          if (resultItem.name == null && sysDimItemDimsDef) {\n            var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];\n            !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {\n              name: sysDimItemDimsDefItem\n            });\n            resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;\n            resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;\n          } // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}\n\n\n          sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);\n        });\n      });\n\n      function applyDim(resultItem, coordDim, coordDimIndex) {\n        if (VISUAL_DIMENSIONS.get(coordDim) != null) {\n          resultItem.otherDims[coordDim] = coordDimIndex;\n        } else {\n          resultItem.coordDim = coordDim;\n          resultItem.coordDimIndex = coordDimIndex;\n          coordDimNameMap.set(coordDim, true);\n        }\n      } // Make sure the first extra dim is 'value'.\n\n\n      var generateCoord = opt.generateCoord;\n      var generateCoordCount = opt.generateCoordCount;\n      var fromZero = generateCoordCount != null;\n      generateCoordCount = generateCoord ? generateCoordCount || 1 : 0;\n      var extra = generateCoord || 'value';\n\n      function ifNoNameFillWithCoordName(resultItem) {\n        if (resultItem.name == null) {\n          // Duplication will be removed in the next step.\n          resultItem.name = resultItem.coordDim;\n        }\n      } // Set dim `name` and other `coordDim` and other props.\n\n\n      if (!omitUnusedDimensions) {\n        for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {\n          var resultItem = getResultItem(resultDimIdx);\n          var coordDim = resultItem.coordDim;\n\n          if (coordDim == null) {\n            // TODO no need to generate coordDim for isExtraCoord?\n            resultItem.coordDim = genCoordDimName(extra, coordDimNameMap, fromZero);\n            resultItem.coordDimIndex = 0; // Series specified generateCoord is using out.\n\n            if (!generateCoord || generateCoordCount <= 0) {\n              resultItem.isExtraCoord = true;\n            }\n\n            generateCoordCount--;\n          }\n\n          ifNoNameFillWithCoordName(resultItem);\n\n          if (resultItem.type == null && (guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must // Consider the case:\n          // {\n          //    dataset: {source: [\n          //        ['2001', 123],\n          //        ['2002', 456],\n          //        ...\n          //        ['The others', 987],\n          //    ]},\n          //    series: {type: 'pie'}\n          // }\n          // The first column should better be treated as a \"ordinal\" although it\n          // might not be detected as an \"ordinal\" by `guessOrdinal`.\n          || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) {\n            resultItem.type = 'ordinal';\n          }\n        }\n      } else {\n        each(resultList, function (resultItem) {\n          // PENDING: guessOrdinal or let user specify type: 'ordinal' manually?\n          ifNoNameFillWithCoordName(resultItem);\n        }); // Sort dimensions: there are some rule that use the last dim as label,\n        // and for some latter travel process easier.\n\n        resultList.sort(function (item0, item1) {\n          return item0.storeDimIndex - item1.storeDimIndex;\n        });\n      }\n\n      removeDuplication(resultList);\n      return new SeriesDataSchema({\n        source: source,\n        dimensions: resultList,\n        fullDimensionCount: dimCount,\n        dimensionOmitted: omitUnusedDimensions\n      });\n    }\n\n    function removeDuplication(result) {\n      var duplicationMap = createHashMap();\n\n      for (var i = 0; i < result.length; i++) {\n        var dim = result[i];\n        var dimOriginalName = dim.name;\n        var count = duplicationMap.get(dimOriginalName) || 0;\n\n        if (count > 0) {\n          // Starts from 0.\n          dim.name = dimOriginalName + (count - 1);\n        }\n\n        count++;\n        duplicationMap.set(dimOriginalName, count);\n      }\n    } // ??? TODO\n    // Originally detect dimCount by data[0]. Should we\n    // optimize it to only by sysDims and dimensions and encode.\n    // So only necessary dims will be initialized.\n    // But\n    // (1) custom series should be considered. where other dims\n    // may be visited.\n    // (2) sometimes user need to calculate bubble size or use visualMap\n    // on other dimensions besides coordSys needed.\n    // So, dims that is not used by system, should be shared in data store?\n\n\n    function getDimCount(source, sysDims, dimsDef, optDimCount) {\n      // Note that the result dimCount should not small than columns count\n      // of data, otherwise `dataDimNameMap` checking will be incorrect.\n      var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0);\n      each(sysDims, function (sysDimItem) {\n        var sysDimItemDimsDef;\n\n        if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) {\n          dimCount = Math.max(dimCount, sysDimItemDimsDef.length);\n        }\n      });\n      return dimCount;\n    }\n\n    function genCoordDimName(name, map, fromZero) {\n      if (fromZero || map.hasKey(name)) {\n        var i = 0;\n\n        while (map.hasKey(name + i)) {\n          i++;\n        }\n\n        name += i;\n      }\n\n      map.set(name, true);\n      return name;\n    }\n\n    /**\n     * @class\n     * For example:\n     * {\n     *     coordSysName: 'cartesian2d',\n     *     coordSysDims: ['x', 'y', ...],\n     *     axisMap: HashMap({\n     *         x: xAxisModel,\n     *         y: yAxisModel\n     *     }),\n     *     categoryAxisMap: HashMap({\n     *         x: xAxisModel,\n     *         y: undefined\n     *     }),\n     *     // The index of the first category axis in `coordSysDims`.\n     *     // `null/undefined` means no category axis exists.\n     *     firstCategoryDimIndex: 1,\n     *     // To replace user specified encode.\n     * }\n     */\n\n    var CoordSysInfo =\n    /** @class */\n    function () {\n      function CoordSysInfo(coordSysName) {\n        this.coordSysDims = [];\n        this.axisMap = createHashMap();\n        this.categoryAxisMap = createHashMap();\n        this.coordSysName = coordSysName;\n      }\n\n      return CoordSysInfo;\n    }();\n\n    function getCoordSysInfoBySeries(seriesModel) {\n      var coordSysName = seriesModel.get('coordinateSystem');\n      var result = new CoordSysInfo(coordSysName);\n      var fetch = fetchers[coordSysName];\n\n      if (fetch) {\n        fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);\n        return result;\n      }\n    }\n    var fetchers = {\n      cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var xAxisModel = seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n        var yAxisModel = seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!xAxisModel) {\n            throw new Error('xAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('xAxisId'), 0) + '\" not found');\n          }\n\n          if (!yAxisModel) {\n            throw new Error('yAxis \"' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('yAxisId'), 0) + '\" not found');\n          }\n        }\n\n        result.coordSysDims = ['x', 'y'];\n        axisMap.set('x', xAxisModel);\n        axisMap.set('y', yAxisModel);\n\n        if (isCategory(xAxisModel)) {\n          categoryAxisMap.set('x', xAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n\n        if (isCategory(yAxisModel)) {\n          categoryAxisMap.set('y', yAxisModel);\n          result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n        }\n      },\n      singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!singleAxisModel) {\n            throw new Error('singleAxis should be specified.');\n          }\n        }\n\n        result.coordSysDims = ['single'];\n        axisMap.set('single', singleAxisModel);\n\n        if (isCategory(singleAxisModel)) {\n          categoryAxisMap.set('single', singleAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n      },\n      polar: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0];\n        var radiusAxisModel = polarModel.findAxisModel('radiusAxis');\n        var angleAxisModel = polarModel.findAxisModel('angleAxis');\n\n        if (\"development\" !== 'production') {\n          if (!angleAxisModel) {\n            throw new Error('angleAxis option not found');\n          }\n\n          if (!radiusAxisModel) {\n            throw new Error('radiusAxis option not found');\n          }\n        }\n\n        result.coordSysDims = ['radius', 'angle'];\n        axisMap.set('radius', radiusAxisModel);\n        axisMap.set('angle', angleAxisModel);\n\n        if (isCategory(radiusAxisModel)) {\n          categoryAxisMap.set('radius', radiusAxisModel);\n          result.firstCategoryDimIndex = 0;\n        }\n\n        if (isCategory(angleAxisModel)) {\n          categoryAxisMap.set('angle', angleAxisModel);\n          result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1);\n        }\n      },\n      geo: function (seriesModel, result, axisMap, categoryAxisMap) {\n        result.coordSysDims = ['lng', 'lat'];\n      },\n      parallel: function (seriesModel, result, axisMap, categoryAxisMap) {\n        var ecModel = seriesModel.ecModel;\n        var parallelModel = ecModel.getComponent('parallel', seriesModel.get('parallelIndex'));\n        var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice();\n        each(parallelModel.parallelAxisIndex, function (axisIndex, index) {\n          var axisModel = ecModel.getComponent('parallelAxis', axisIndex);\n          var axisDim = coordSysDims[index];\n          axisMap.set(axisDim, axisModel);\n\n          if (isCategory(axisModel)) {\n            categoryAxisMap.set(axisDim, axisModel);\n\n            if (result.firstCategoryDimIndex == null) {\n              result.firstCategoryDimIndex = index;\n            }\n          }\n        });\n      }\n    };\n\n    function isCategory(axisModel) {\n      return axisModel.get('type') === 'category';\n    }\n\n    /**\n     * Note that it is too complicated to support 3d stack by value\n     * (have to create two-dimension inverted index), so in 3d case\n     * we just support that stacked by index.\n     *\n     * @param seriesModel\n     * @param dimensionsInput The same as the input of <module:echarts/data/SeriesData>.\n     *        The input will be modified.\n     * @param opt\n     * @param opt.stackedCoordDimension Specify a coord dimension if needed.\n     * @param opt.byIndex=false\n     * @return calculationInfo\n     * {\n     *     stackedDimension: string\n     *     stackedByDimension: string\n     *     isStackedByIndex: boolean\n     *     stackedOverDimension: string\n     *     stackResultDimension: string\n     * }\n     */\n\n    function enableDataStack(seriesModel, dimensionsInput, opt) {\n      opt = opt || {};\n      var byIndex = opt.byIndex;\n      var stackedCoordDimension = opt.stackedCoordDimension;\n      var dimensionDefineList;\n      var schema;\n      var store;\n\n      if (isLegacyDimensionsInput(dimensionsInput)) {\n        dimensionDefineList = dimensionsInput;\n      } else {\n        schema = dimensionsInput.schema;\n        dimensionDefineList = schema.dimensions;\n        store = dimensionsInput.store;\n      } // Compatibal: when `stack` is set as '', do not stack.\n\n\n      var mayStack = !!(seriesModel && seriesModel.get('stack'));\n      var stackedByDimInfo;\n      var stackedDimInfo;\n      var stackResultDimension;\n      var stackedOverDimension;\n      each(dimensionDefineList, function (dimensionInfo, index) {\n        if (isString(dimensionInfo)) {\n          dimensionDefineList[index] = dimensionInfo = {\n            name: dimensionInfo\n          };\n        }\n\n        if (mayStack && !dimensionInfo.isExtraCoord) {\n          // Find the first ordinal dimension as the stackedByDimInfo.\n          if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) {\n            stackedByDimInfo = dimensionInfo;\n          } // Find the first stackable dimension as the stackedDimInfo.\n\n\n          if (!stackedDimInfo && dimensionInfo.type !== 'ordinal' && dimensionInfo.type !== 'time' && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)) {\n            stackedDimInfo = dimensionInfo;\n          }\n        }\n      });\n\n      if (stackedDimInfo && !byIndex && !stackedByDimInfo) {\n        // Compatible with previous design, value axis (time axis) only stack by index.\n        // It may make sense if the user provides elaborately constructed data.\n        byIndex = true;\n      } // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`.\n      // That put stack logic in List is for using conveniently in echarts extensions, but it\n      // might not be a good way.\n\n\n      if (stackedDimInfo) {\n        // Use a weird name that not duplicated with other names.\n        // Also need to use seriesModel.id as postfix because different\n        // series may share same data store. The stack dimension needs to be distinguished.\n        stackResultDimension = '__\\0ecstackresult_' + seriesModel.id;\n        stackedOverDimension = '__\\0ecstackedover_' + seriesModel.id; // Create inverted index to fast query index by value.\n\n        if (stackedByDimInfo) {\n          stackedByDimInfo.createInvertedIndices = true;\n        }\n\n        var stackedDimCoordDim_1 = stackedDimInfo.coordDim;\n        var stackedDimType = stackedDimInfo.type;\n        var stackedDimCoordIndex_1 = 0;\n        each(dimensionDefineList, function (dimensionInfo) {\n          if (dimensionInfo.coordDim === stackedDimCoordDim_1) {\n            stackedDimCoordIndex_1++;\n          }\n        });\n        var stackedOverDimensionDefine = {\n          name: stackResultDimension,\n          coordDim: stackedDimCoordDim_1,\n          coordDimIndex: stackedDimCoordIndex_1,\n          type: stackedDimType,\n          isExtraCoord: true,\n          isCalculationCoord: true,\n          storeDimIndex: dimensionDefineList.length\n        };\n        var stackResultDimensionDefine = {\n          name: stackedOverDimension,\n          // This dimension contains stack base (generally, 0), so do not set it as\n          // `stackedDimCoordDim` to avoid extent calculation, consider log scale.\n          coordDim: stackedOverDimension,\n          coordDimIndex: stackedDimCoordIndex_1 + 1,\n          type: stackedDimType,\n          isExtraCoord: true,\n          isCalculationCoord: true,\n          storeDimIndex: dimensionDefineList.length + 1\n        };\n\n        if (schema) {\n          if (store) {\n            stackedOverDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackedOverDimension, stackedDimType);\n            stackResultDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackResultDimension, stackedDimType);\n          }\n\n          schema.appendCalculationDimension(stackedOverDimensionDefine);\n          schema.appendCalculationDimension(stackResultDimensionDefine);\n        } else {\n          dimensionDefineList.push(stackedOverDimensionDefine);\n          dimensionDefineList.push(stackResultDimensionDefine);\n        }\n      }\n\n      return {\n        stackedDimension: stackedDimInfo && stackedDimInfo.name,\n        stackedByDimension: stackedByDimInfo && stackedByDimInfo.name,\n        isStackedByIndex: byIndex,\n        stackedOverDimension: stackedOverDimension,\n        stackResultDimension: stackResultDimension\n      };\n    }\n\n    function isLegacyDimensionsInput(dimensionsInput) {\n      return !isSeriesDataSchema(dimensionsInput.schema);\n    }\n\n    function isDimensionStacked(data, stackedDim) {\n      // Each single series only maps to one pair of axis. So we do not need to\n      // check stackByDim, whatever stacked by a dimension or stacked by index.\n      return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension');\n    }\n    function getStackedDimension(data, targetDim) {\n      return isDimensionStacked(data, targetDim) ? data.getCalculationInfo('stackResultDimension') : targetDim;\n    }\n\n    function getCoordSysDimDefs(seriesModel, coordSysInfo) {\n      var coordSysName = seriesModel.get('coordinateSystem');\n      var registeredCoordSys = CoordinateSystemManager.get(coordSysName);\n      var coordSysDimDefs;\n\n      if (coordSysInfo && coordSysInfo.coordSysDims) {\n        coordSysDimDefs = map(coordSysInfo.coordSysDims, function (dim) {\n          var dimInfo = {\n            name: dim\n          };\n          var axisModel = coordSysInfo.axisMap.get(dim);\n\n          if (axisModel) {\n            var axisType = axisModel.get('type');\n            dimInfo.type = getDimensionTypeByAxis(axisType);\n          }\n\n          return dimInfo;\n        });\n      }\n\n      if (!coordSysDimDefs) {\n        // Get dimensions from registered coordinate system\n        coordSysDimDefs = registeredCoordSys && (registeredCoordSys.getDimensionsInfo ? registeredCoordSys.getDimensionsInfo() : registeredCoordSys.dimensions.slice()) || ['x', 'y'];\n      }\n\n      return coordSysDimDefs;\n    }\n\n    function injectOrdinalMeta(dimInfoList, createInvertedIndices, coordSysInfo) {\n      var firstCategoryDimIndex;\n      var hasNameEncode;\n      coordSysInfo && each(dimInfoList, function (dimInfo, dimIndex) {\n        var coordDim = dimInfo.coordDim;\n        var categoryAxisModel = coordSysInfo.categoryAxisMap.get(coordDim);\n\n        if (categoryAxisModel) {\n          if (firstCategoryDimIndex == null) {\n            firstCategoryDimIndex = dimIndex;\n          }\n\n          dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta();\n\n          if (createInvertedIndices) {\n            dimInfo.createInvertedIndices = true;\n          }\n        }\n\n        if (dimInfo.otherDims.itemName != null) {\n          hasNameEncode = true;\n        }\n      });\n\n      if (!hasNameEncode && firstCategoryDimIndex != null) {\n        dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0;\n      }\n\n      return firstCategoryDimIndex;\n    }\n    /**\n     * Caution: there are side effects to `sourceManager` in this method.\n     * Should better only be called in `Series['getInitialData']`.\n     */\n\n\n    function createSeriesData(sourceRaw, seriesModel, opt) {\n      opt = opt || {};\n      var sourceManager = seriesModel.getSourceManager();\n      var source;\n      var isOriginalSource = false;\n\n      if (sourceRaw) {\n        isOriginalSource = true;\n        source = createSourceFromSeriesDataOption(sourceRaw);\n      } else {\n        source = sourceManager.getSource(); // Is series.data. not dataset.\n\n        isOriginalSource = source.sourceFormat === SOURCE_FORMAT_ORIGINAL;\n      }\n\n      var coordSysInfo = getCoordSysInfoBySeries(seriesModel);\n      var coordSysDimDefs = getCoordSysDimDefs(seriesModel, coordSysInfo);\n      var useEncodeDefaulter = opt.useEncodeDefaulter;\n      var encodeDefaulter = isFunction(useEncodeDefaulter) ? useEncodeDefaulter : useEncodeDefaulter ? curry(makeSeriesEncodeForAxisCoordSys, coordSysDimDefs, seriesModel) : null;\n      var createDimensionOptions = {\n        coordDimensions: coordSysDimDefs,\n        generateCoord: opt.generateCoord,\n        encodeDefine: seriesModel.getEncode(),\n        encodeDefaulter: encodeDefaulter,\n        canOmitUnusedDimensions: !isOriginalSource\n      };\n      var schema = prepareSeriesDataSchema(source, createDimensionOptions);\n      var firstCategoryDimIndex = injectOrdinalMeta(schema.dimensions, opt.createInvertedIndices, coordSysInfo);\n      var store = !isOriginalSource ? sourceManager.getSharedDataStore(schema) : null;\n      var stackCalculationInfo = enableDataStack(seriesModel, {\n        schema: schema,\n        store: store\n      });\n      var data = new SeriesData(schema, seriesModel);\n      data.setCalculationInfo(stackCalculationInfo);\n      var dimValueGetter = firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source) ? function (itemOpt, dimName, dataIndex, dimIndex) {\n        // Use dataIndex as ordinal value in categoryAxis\n        return dimIndex === firstCategoryDimIndex ? dataIndex : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);\n      } : null;\n      data.hasItemOption = false;\n      data.initData( // Try to reuse the data store in sourceManager if using dataset.\n      isOriginalSource ? source : store, null, dimValueGetter);\n      return data;\n    }\n\n    function isNeedCompleteOrdinalData(source) {\n      if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {\n        var sampleItem = firstDataNotNull(source.data || []);\n        return !isArray(getDataItemValue(sampleItem));\n      }\n    }\n\n    function firstDataNotNull(arr) {\n      var i = 0;\n\n      while (i < arr.length && arr[i] == null) {\n        i++;\n      }\n\n      return arr[i];\n    }\n\n    var Scale =\n    /** @class */\n    function () {\n      function Scale(setting) {\n        this._setting = setting || {};\n        this._extent = [Infinity, -Infinity];\n      }\n\n      Scale.prototype.getSetting = function (name) {\n        return this._setting[name];\n      };\n      /**\n       * Set extent from data\n       */\n\n\n      Scale.prototype.unionExtent = function (other) {\n        var extent = this._extent;\n        other[0] < extent[0] && (extent[0] = other[0]);\n        other[1] > extent[1] && (extent[1] = other[1]); // not setExtent because in log axis it may transformed to power\n        // this.setExtent(extent[0], extent[1]);\n      };\n      /**\n       * Set extent from data\n       */\n\n\n      Scale.prototype.unionExtentFromData = function (data, dim) {\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * Get extent\n       *\n       * Extent is always in increase order.\n       */\n\n\n      Scale.prototype.getExtent = function () {\n        return this._extent.slice();\n      };\n      /**\n       * Set extent\n       */\n\n\n      Scale.prototype.setExtent = function (start, end) {\n        var thisExtent = this._extent;\n\n        if (!isNaN(start)) {\n          thisExtent[0] = start;\n        }\n\n        if (!isNaN(end)) {\n          thisExtent[1] = end;\n        }\n      };\n      /**\n       * If value is in extent range\n       */\n\n\n      Scale.prototype.isInExtentRange = function (value) {\n        return this._extent[0] <= value && this._extent[1] >= value;\n      };\n      /**\n       * When axis extent depends on data and no data exists,\n       * axis ticks should not be drawn, which is named 'blank'.\n       */\n\n\n      Scale.prototype.isBlank = function () {\n        return this._isBlank;\n      };\n      /**\n       * When axis extent depends on data and no data exists,\n       * axis ticks should not be drawn, which is named 'blank'.\n       */\n\n\n      Scale.prototype.setBlank = function (isBlank) {\n        this._isBlank = isBlank;\n      };\n\n      return Scale;\n    }();\n\n    enableClassManagement(Scale);\n\n    var uidBase = 0;\n\n    var OrdinalMeta =\n    /** @class */\n    function () {\n      function OrdinalMeta(opt) {\n        this.categories = opt.categories || [];\n        this._needCollect = opt.needCollect;\n        this._deduplication = opt.deduplication;\n        this.uid = ++uidBase;\n      }\n\n      OrdinalMeta.createByAxisModel = function (axisModel) {\n        var option = axisModel.option;\n        var data = option.data;\n        var categories = data && map(data, getName);\n        return new OrdinalMeta({\n          categories: categories,\n          needCollect: !categories,\n          // deduplication is default in axis.\n          deduplication: option.dedplication !== false\n        });\n      };\n\n      OrdinalMeta.prototype.getOrdinal = function (category) {\n        // @ts-ignore\n        return this._getOrCreateMap().get(category);\n      };\n      /**\n       * @return The ordinal. If not found, return NaN.\n       */\n\n\n      OrdinalMeta.prototype.parseAndCollect = function (category) {\n        var index;\n        var needCollect = this._needCollect; // The value of category dim can be the index of the given category set.\n        // This feature is only supported when !needCollect, because we should\n        // consider a common case: a value is 2017, which is a number but is\n        // expected to be tread as a category. This case usually happen in dataset,\n        // where it happent to be no need of the index feature.\n\n        if (!isString(category) && !needCollect) {\n          return category;\n        } // Optimize for the scenario:\n        // category is ['2012-01-01', '2012-01-02', ...], where the input\n        // data has been ensured not duplicate and is large data.\n        // Notice, if a dataset dimension provide categroies, usually echarts\n        // should remove duplication except user tell echarts dont do that\n        // (set axis.deduplication = false), because echarts do not know whether\n        // the values in the category dimension has duplication (consider the\n        // parallel-aqi example)\n\n\n        if (needCollect && !this._deduplication) {\n          index = this.categories.length;\n          this.categories[index] = category;\n          return index;\n        }\n\n        var map = this._getOrCreateMap(); // @ts-ignore\n\n\n        index = map.get(category);\n\n        if (index == null) {\n          if (needCollect) {\n            index = this.categories.length;\n            this.categories[index] = category; // @ts-ignore\n\n            map.set(category, index);\n          } else {\n            index = NaN;\n          }\n        }\n\n        return index;\n      }; // Consider big data, do not create map until needed.\n\n\n      OrdinalMeta.prototype._getOrCreateMap = function () {\n        return this._map || (this._map = createHashMap(this.categories));\n      };\n\n      return OrdinalMeta;\n    }();\n\n    function getName(obj) {\n      if (isObject(obj) && obj.value != null) {\n        return obj.value;\n      } else {\n        return obj + '';\n      }\n    }\n\n    function isValueNice(val) {\n      var exp10 = Math.pow(10, quantityExponent(Math.abs(val)));\n      var f = Math.abs(val / exp10);\n      return f === 0 || f === 1 || f === 2 || f === 3 || f === 5;\n    }\n    function isIntervalOrLogScale(scale) {\n      return scale.type === 'interval' || scale.type === 'log';\n    }\n    /**\n     * @param extent Both extent[0] and extent[1] should be valid number.\n     *               Should be extent[0] < extent[1].\n     * @param splitNumber splitNumber should be >= 1.\n     */\n\n    function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {\n      var result = {};\n      var span = extent[1] - extent[0];\n      var interval = result.interval = nice(span / splitNumber, true);\n\n      if (minInterval != null && interval < minInterval) {\n        interval = result.interval = minInterval;\n      }\n\n      if (maxInterval != null && interval > maxInterval) {\n        interval = result.interval = maxInterval;\n      } // Tow more digital for tick.\n\n\n      var precision = result.intervalPrecision = getIntervalPrecision(interval); // Niced extent inside original extent\n\n      var niceTickExtent = result.niceTickExtent = [round(Math.ceil(extent[0] / interval) * interval, precision), round(Math.floor(extent[1] / interval) * interval, precision)];\n      fixExtent(niceTickExtent, extent);\n      return result;\n    }\n    function increaseInterval(interval) {\n      var exp10 = Math.pow(10, quantityExponent(interval)); // Increase interval\n\n      var f = interval / exp10;\n\n      if (!f) {\n        f = 1;\n      } else if (f === 2) {\n        f = 3;\n      } else if (f === 3) {\n        f = 5;\n      } else {\n        // f is 1 or 5\n        f *= 2;\n      }\n\n      return round(f * exp10);\n    }\n    /**\n     * @return interval precision\n     */\n\n    function getIntervalPrecision(interval) {\n      // Tow more digital for tick.\n      return getPrecision(interval) + 2;\n    }\n\n    function clamp(niceTickExtent, idx, extent) {\n      niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);\n    } // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.\n\n\n    function fixExtent(niceTickExtent, extent) {\n      !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);\n      !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);\n      clamp(niceTickExtent, 0, extent);\n      clamp(niceTickExtent, 1, extent);\n\n      if (niceTickExtent[0] > niceTickExtent[1]) {\n        niceTickExtent[0] = niceTickExtent[1];\n      }\n    }\n    function contain$1(val, extent) {\n      return val >= extent[0] && val <= extent[1];\n    }\n    function normalize$1(val, extent) {\n      if (extent[1] === extent[0]) {\n        return 0.5;\n      }\n\n      return (val - extent[0]) / (extent[1] - extent[0]);\n    }\n    function scale$2(val, extent) {\n      return val * (extent[1] - extent[0]) + extent[0];\n    }\n\n    var OrdinalScale =\n    /** @class */\n    function (_super) {\n      __extends(OrdinalScale, _super);\n\n      function OrdinalScale(setting) {\n        var _this = _super.call(this, setting) || this;\n\n        _this.type = 'ordinal';\n\n        var ordinalMeta = _this.getSetting('ordinalMeta'); // Caution: Should not use instanceof, consider ec-extensions using\n        // import approach to get OrdinalMeta class.\n\n\n        if (!ordinalMeta) {\n          ordinalMeta = new OrdinalMeta({});\n        }\n\n        if (isArray(ordinalMeta)) {\n          ordinalMeta = new OrdinalMeta({\n            categories: map(ordinalMeta, function (item) {\n              return isObject(item) ? item.value : item;\n            })\n          });\n        }\n\n        _this._ordinalMeta = ordinalMeta;\n        _this._extent = _this.getSetting('extent') || [0, ordinalMeta.categories.length - 1];\n        return _this;\n      }\n\n      OrdinalScale.prototype.parse = function (val) {\n        // Caution: Math.round(null) will return `0` rather than `NaN`\n        if (val == null) {\n          return NaN;\n        }\n\n        return isString(val) ? this._ordinalMeta.getOrdinal(val) // val might be float.\n        : Math.round(val);\n      };\n\n      OrdinalScale.prototype.contain = function (rank) {\n        rank = this.parse(rank);\n        return contain$1(rank, this._extent) && this._ordinalMeta.categories[rank] != null;\n      };\n      /**\n       * Normalize given rank or name to linear [0, 1]\n       * @param val raw ordinal number.\n       * @return normalized value in [0, 1].\n       */\n\n\n      OrdinalScale.prototype.normalize = function (val) {\n        val = this._getTickNumber(this.parse(val));\n        return normalize$1(val, this._extent);\n      };\n      /**\n       * @param val normalized value in [0, 1].\n       * @return raw ordinal number.\n       */\n\n\n      OrdinalScale.prototype.scale = function (val) {\n        val = Math.round(scale$2(val, this._extent));\n        return this.getRawOrdinalNumber(val);\n      };\n\n      OrdinalScale.prototype.getTicks = function () {\n        var ticks = [];\n        var extent = this._extent;\n        var rank = extent[0];\n\n        while (rank <= extent[1]) {\n          ticks.push({\n            value: rank\n          });\n          rank++;\n        }\n\n        return ticks;\n      };\n\n      OrdinalScale.prototype.getMinorTicks = function (splitNumber) {\n        // Not support.\n        return;\n      };\n      /**\n       * @see `Ordinal['_ordinalNumbersByTick']`\n       */\n\n\n      OrdinalScale.prototype.setSortInfo = function (info) {\n        if (info == null) {\n          this._ordinalNumbersByTick = this._ticksByOrdinalNumber = null;\n          return;\n        }\n\n        var infoOrdinalNumbers = info.ordinalNumbers;\n        var ordinalsByTick = this._ordinalNumbersByTick = [];\n        var ticksByOrdinal = this._ticksByOrdinalNumber = []; // Unnecessary support negative tick in `realtimeSort`.\n\n        var tickNum = 0;\n        var allCategoryLen = this._ordinalMeta.categories.length;\n\n        for (var len = Math.min(allCategoryLen, infoOrdinalNumbers.length); tickNum < len; ++tickNum) {\n          var ordinalNumber = infoOrdinalNumbers[tickNum];\n          ordinalsByTick[tickNum] = ordinalNumber;\n          ticksByOrdinal[ordinalNumber] = tickNum;\n        } // Handle that `series.data` only covers part of the `axis.category.data`.\n\n\n        var unusedOrdinal = 0;\n\n        for (; tickNum < allCategoryLen; ++tickNum) {\n          while (ticksByOrdinal[unusedOrdinal] != null) {\n            unusedOrdinal++;\n          }\n          ordinalsByTick.push(unusedOrdinal);\n          ticksByOrdinal[unusedOrdinal] = tickNum;\n        }\n      };\n\n      OrdinalScale.prototype._getTickNumber = function (ordinal) {\n        var ticksByOrdinalNumber = this._ticksByOrdinalNumber; // also support ordinal out of range of `ordinalMeta.categories.length`,\n        // where ordinal numbers are used as tick value directly.\n\n        return ticksByOrdinalNumber && ordinal >= 0 && ordinal < ticksByOrdinalNumber.length ? ticksByOrdinalNumber[ordinal] : ordinal;\n      };\n      /**\n       * @usage\n       * ```js\n       * const ordinalNumber = ordinalScale.getRawOrdinalNumber(tickVal);\n       *\n       * // case0\n       * const rawOrdinalValue = axisModel.getCategories()[ordinalNumber];\n       * // case1\n       * const rawOrdinalValue = this._ordinalMeta.categories[ordinalNumber];\n       * // case2\n       * const coord = axis.dataToCoord(ordinalNumber);\n       * ```\n       *\n       * @param {OrdinalNumber} tickNumber index of display\n       */\n\n\n      OrdinalScale.prototype.getRawOrdinalNumber = function (tickNumber) {\n        var ordinalNumbersByTick = this._ordinalNumbersByTick; // tickNumber may be out of range, e.g., when axis max is larger than `ordinalMeta.categories.length`.,\n        // where ordinal numbers are used as tick value directly.\n\n        return ordinalNumbersByTick && tickNumber >= 0 && tickNumber < ordinalNumbersByTick.length ? ordinalNumbersByTick[tickNumber] : tickNumber;\n      };\n      /**\n       * Get item on tick\n       */\n\n\n      OrdinalScale.prototype.getLabel = function (tick) {\n        if (!this.isBlank()) {\n          var ordinalNumber = this.getRawOrdinalNumber(tick.value);\n          var cateogry = this._ordinalMeta.categories[ordinalNumber]; // Note that if no data, ordinalMeta.categories is an empty array.\n          // Return empty if it's not exist.\n\n          return cateogry == null ? '' : cateogry + '';\n        }\n      };\n\n      OrdinalScale.prototype.count = function () {\n        return this._extent[1] - this._extent[0] + 1;\n      };\n\n      OrdinalScale.prototype.unionExtentFromData = function (data, dim) {\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * @override\n       * If value is in extent range\n       */\n\n\n      OrdinalScale.prototype.isInExtentRange = function (value) {\n        value = this._getTickNumber(value);\n        return this._extent[0] <= value && this._extent[1] >= value;\n      };\n\n      OrdinalScale.prototype.getOrdinalMeta = function () {\n        return this._ordinalMeta;\n      };\n\n      OrdinalScale.prototype.calcNiceTicks = function () {};\n\n      OrdinalScale.prototype.calcNiceExtent = function () {};\n\n      OrdinalScale.type = 'ordinal';\n      return OrdinalScale;\n    }(Scale);\n\n    Scale.registerClass(OrdinalScale);\n\n    var roundNumber = round;\n\n    var IntervalScale =\n    /** @class */\n    function (_super) {\n      __extends(IntervalScale, _super);\n\n      function IntervalScale() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'interval'; // Step is calculated in adjustExtent.\n\n        _this._interval = 0;\n        _this._intervalPrecision = 2;\n        return _this;\n      }\n\n      IntervalScale.prototype.parse = function (val) {\n        return val;\n      };\n\n      IntervalScale.prototype.contain = function (val) {\n        return contain$1(val, this._extent);\n      };\n\n      IntervalScale.prototype.normalize = function (val) {\n        return normalize$1(val, this._extent);\n      };\n\n      IntervalScale.prototype.scale = function (val) {\n        return scale$2(val, this._extent);\n      };\n\n      IntervalScale.prototype.setExtent = function (start, end) {\n        var thisExtent = this._extent; // start,end may be a Number like '25',so...\n\n        if (!isNaN(start)) {\n          thisExtent[0] = parseFloat(start);\n        }\n\n        if (!isNaN(end)) {\n          thisExtent[1] = parseFloat(end);\n        }\n      };\n\n      IntervalScale.prototype.unionExtent = function (other) {\n        var extent = this._extent;\n        other[0] < extent[0] && (extent[0] = other[0]);\n        other[1] > extent[1] && (extent[1] = other[1]); // unionExtent may called by it's sub classes\n\n        this.setExtent(extent[0], extent[1]);\n      };\n\n      IntervalScale.prototype.getInterval = function () {\n        return this._interval;\n      };\n\n      IntervalScale.prototype.setInterval = function (interval) {\n        this._interval = interval; // Dropped auto calculated niceExtent and use user-set extent.\n        // We assume user wants to set both interval, min, max to get a better result.\n\n        this._niceExtent = this._extent.slice();\n        this._intervalPrecision = getIntervalPrecision(interval);\n      };\n      /**\n       * @param expandToNicedExtent Whether expand the ticks to niced extent.\n       */\n\n\n      IntervalScale.prototype.getTicks = function (expandToNicedExtent) {\n        var interval = this._interval;\n        var extent = this._extent;\n        var niceTickExtent = this._niceExtent;\n        var intervalPrecision = this._intervalPrecision;\n        var ticks = []; // If interval is 0, return [];\n\n        if (!interval) {\n          return ticks;\n        } // Consider this case: using dataZoom toolbox, zoom and zoom.\n\n\n        var safeLimit = 10000;\n\n        if (extent[0] < niceTickExtent[0]) {\n          if (expandToNicedExtent) {\n            ticks.push({\n              value: roundNumber(niceTickExtent[0] - interval, intervalPrecision)\n            });\n          } else {\n            ticks.push({\n              value: extent[0]\n            });\n          }\n        }\n\n        var tick = niceTickExtent[0];\n\n        while (tick <= niceTickExtent[1]) {\n          ticks.push({\n            value: tick\n          }); // Avoid rounding error\n\n          tick = roundNumber(tick + interval, intervalPrecision);\n\n          if (tick === ticks[ticks.length - 1].value) {\n            // Consider out of safe float point, e.g.,\n            // -3711126.9907707 + 2e-10 === -3711126.9907707\n            break;\n          }\n\n          if (ticks.length > safeLimit) {\n            return [];\n          }\n        } // Consider this case: the last item of ticks is smaller\n        // than niceTickExtent[1] and niceTickExtent[1] === extent[1].\n\n\n        var lastNiceTick = ticks.length ? ticks[ticks.length - 1].value : niceTickExtent[1];\n\n        if (extent[1] > lastNiceTick) {\n          if (expandToNicedExtent) {\n            ticks.push({\n              value: roundNumber(lastNiceTick + interval, intervalPrecision)\n            });\n          } else {\n            ticks.push({\n              value: extent[1]\n            });\n          }\n        }\n\n        return ticks;\n      };\n\n      IntervalScale.prototype.getMinorTicks = function (splitNumber) {\n        var ticks = this.getTicks(true);\n        var minorTicks = [];\n        var extent = this.getExtent();\n\n        for (var i = 1; i < ticks.length; i++) {\n          var nextTick = ticks[i];\n          var prevTick = ticks[i - 1];\n          var count = 0;\n          var minorTicksGroup = [];\n          var interval = nextTick.value - prevTick.value;\n          var minorInterval = interval / splitNumber;\n\n          while (count < splitNumber - 1) {\n            var minorTick = roundNumber(prevTick.value + (count + 1) * minorInterval); // For the first and last interval. The count may be less than splitNumber.\n\n            if (minorTick > extent[0] && minorTick < extent[1]) {\n              minorTicksGroup.push(minorTick);\n            }\n\n            count++;\n          }\n\n          minorTicks.push(minorTicksGroup);\n        }\n\n        return minorTicks;\n      };\n      /**\n       * @param opt.precision If 'auto', use nice presision.\n       * @param opt.pad returns 1.50 but not 1.5 if precision is 2.\n       */\n\n\n      IntervalScale.prototype.getLabel = function (data, opt) {\n        if (data == null) {\n          return '';\n        }\n\n        var precision = opt && opt.precision;\n\n        if (precision == null) {\n          precision = getPrecision(data.value) || 0;\n        } else if (precision === 'auto') {\n          // Should be more precise then tick.\n          precision = this._intervalPrecision;\n        } // (1) If `precision` is set, 12.005 should be display as '12.00500'.\n        // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.\n\n\n        var dataNum = roundNumber(data.value, precision, true);\n        return addCommas(dataNum);\n      };\n      /**\n       * @param splitNumber By default `5`.\n       */\n\n\n      IntervalScale.prototype.calcNiceTicks = function (splitNumber, minInterval, maxInterval) {\n        splitNumber = splitNumber || 5;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n\n        if (!isFinite(span)) {\n          return;\n        } // User may set axis min 0 and data are all negative\n        // FIXME If it needs to reverse ?\n\n\n        if (span < 0) {\n          span = -span;\n          extent.reverse();\n        }\n\n        var result = intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval);\n        this._intervalPrecision = result.intervalPrecision;\n        this._interval = result.interval;\n        this._niceExtent = result.niceTickExtent;\n      };\n\n      IntervalScale.prototype.calcNiceExtent = function (opt) {\n        var extent = this._extent; // If extent start and end are same, expand them\n\n        if (extent[0] === extent[1]) {\n          if (extent[0] !== 0) {\n            // Expand extent\n            // Note that extents can be both negative. See #13154\n            var expandSize = Math.abs(extent[0]); // In the fowllowing case\n            //      Axis has been fixed max 100\n            //      Plus data are all 100 and axis extent are [100, 100].\n            // Extend to the both side will cause expanded max is larger than fixed max.\n            // So only expand to the smaller side.\n\n            if (!opt.fixMax) {\n              extent[1] += expandSize / 2;\n              extent[0] -= expandSize / 2;\n            } else {\n              extent[0] -= expandSize / 2;\n            }\n          } else {\n            extent[1] = 1;\n          }\n        }\n\n        var span = extent[1] - extent[0]; // If there are no data and extent are [Infinity, -Infinity]\n\n        if (!isFinite(span)) {\n          extent[0] = 0;\n          extent[1] = 1;\n        }\n\n        this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // let extent = this._extent;\n\n        var interval = this._interval;\n\n        if (!opt.fixMin) {\n          extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);\n        }\n\n        if (!opt.fixMax) {\n          extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);\n        }\n      };\n\n      IntervalScale.prototype.setNiceExtent = function (min, max) {\n        this._niceExtent = [min, max];\n      };\n\n      IntervalScale.type = 'interval';\n      return IntervalScale;\n    }(Scale);\n\n    Scale.registerClass(IntervalScale);\n\n    /* global Float32Array */\n\n    var supportFloat32Array = typeof Float32Array !== 'undefined';\n    var Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array;\n    function createFloat32Array(arg) {\n      if (isArray(arg)) {\n        // Return self directly if don't support TypedArray.\n        return supportFloat32Array ? new Float32Array(arg) : arg;\n      } // Else is number\n\n\n      return new Float32ArrayCtor(arg);\n    }\n\n    var STACK_PREFIX = '__ec_stack_';\n\n    function getSeriesStackId(seriesModel) {\n      return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;\n    }\n\n    function getAxisKey(axis) {\n      return axis.dim + axis.index;\n    }\n    function prepareLayoutBarSeries(seriesType, ecModel) {\n      var seriesModels = [];\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        // Check series coordinate, do layout for cartesian2d only\n        if (isOnCartesian(seriesModel)) {\n          seriesModels.push(seriesModel);\n        }\n      });\n      return seriesModels;\n    }\n    /**\n     * Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent\n     * values.\n     * This works for time axes, value axes, and log axes.\n     * For a single time axis, return value is in the form like\n     * {'x_0': [1000000]}.\n     * The value of 1000000 is in milliseconds.\n     */\n\n    function getValueAxesMinGaps(barSeries) {\n      /**\n       * Map from axis.index to values.\n       * For a single time axis, axisValues is in the form like\n       * {'x_0': [1495555200000, 1495641600000, 1495728000000]}.\n       * Items in axisValues[x], e.g. 1495555200000, are time values of all\n       * series.\n       */\n      var axisValues = {};\n      each(barSeries, function (seriesModel) {\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n\n        if (baseAxis.type !== 'time' && baseAxis.type !== 'value') {\n          return;\n        }\n\n        var data = seriesModel.getData();\n        var key = baseAxis.dim + '_' + baseAxis.index;\n        var dimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n        var store = data.getStore();\n\n        for (var i = 0, cnt = store.count(); i < cnt; ++i) {\n          var value = store.get(dimIdx, i);\n\n          if (!axisValues[key]) {\n            // No previous data for the axis\n            axisValues[key] = [value];\n          } else {\n            // No value in previous series\n            axisValues[key].push(value);\n          } // Ignore duplicated time values in the same axis\n\n        }\n      });\n      var axisMinGaps = {};\n\n      for (var key in axisValues) {\n        if (axisValues.hasOwnProperty(key)) {\n          var valuesInAxis = axisValues[key];\n\n          if (valuesInAxis) {\n            // Sort axis values into ascending order to calculate gaps\n            valuesInAxis.sort(function (a, b) {\n              return a - b;\n            });\n            var min = null;\n\n            for (var j = 1; j < valuesInAxis.length; ++j) {\n              var delta = valuesInAxis[j] - valuesInAxis[j - 1];\n\n              if (delta > 0) {\n                // Ignore 0 delta because they are of the same axis value\n                min = min === null ? delta : Math.min(min, delta);\n              }\n            } // Set to null if only have one data\n\n\n            axisMinGaps[key] = min;\n          }\n        }\n      }\n\n      return axisMinGaps;\n    }\n\n    function makeColumnLayout(barSeries) {\n      var axisMinGaps = getValueAxesMinGaps(barSeries);\n      var seriesInfoList = [];\n      each(barSeries, function (seriesModel) {\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n        var axisExtent = baseAxis.getExtent();\n        var bandWidth;\n\n        if (baseAxis.type === 'category') {\n          bandWidth = baseAxis.getBandWidth();\n        } else if (baseAxis.type === 'value' || baseAxis.type === 'time') {\n          var key = baseAxis.dim + '_' + baseAxis.index;\n          var minGap = axisMinGaps[key];\n          var extentSpan = Math.abs(axisExtent[1] - axisExtent[0]);\n          var scale = baseAxis.scale.getExtent();\n          var scaleSpan = Math.abs(scale[1] - scale[0]);\n          bandWidth = minGap ? extentSpan / scaleSpan * minGap : extentSpan; // When there is only one data value\n        } else {\n          var data = seriesModel.getData();\n          bandWidth = Math.abs(axisExtent[1] - axisExtent[0]) / data.count();\n        }\n\n        var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth);\n        var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth);\n        var barMinWidth = parsePercent$1( // barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis,\n        // the auto-calculated bar width might be less than 0.5 / 1.\n        seriesModel.get('barMinWidth') || (isInLargeMode(seriesModel) ? 0.5 : 1), bandWidth);\n        var barGap = seriesModel.get('barGap');\n        var barCategoryGap = seriesModel.get('barCategoryGap');\n        seriesInfoList.push({\n          bandWidth: bandWidth,\n          barWidth: barWidth,\n          barMaxWidth: barMaxWidth,\n          barMinWidth: barMinWidth,\n          barGap: barGap,\n          barCategoryGap: barCategoryGap,\n          axisKey: getAxisKey(baseAxis),\n          stackId: getSeriesStackId(seriesModel)\n        });\n      });\n      return doCalBarWidthAndOffset(seriesInfoList);\n    }\n\n    function doCalBarWidthAndOffset(seriesInfoList) {\n      // Columns info on each category axis. Key is cartesian name\n      var columnsMap = {};\n      each(seriesInfoList, function (seriesInfo, idx) {\n        var axisKey = seriesInfo.axisKey;\n        var bandWidth = seriesInfo.bandWidth;\n        var columnsOnAxis = columnsMap[axisKey] || {\n          bandWidth: bandWidth,\n          remainedWidth: bandWidth,\n          autoWidthCount: 0,\n          categoryGap: null,\n          gap: '20%',\n          stacks: {}\n        };\n        var stacks = columnsOnAxis.stacks;\n        columnsMap[axisKey] = columnsOnAxis;\n        var stackId = seriesInfo.stackId;\n\n        if (!stacks[stackId]) {\n          columnsOnAxis.autoWidthCount++;\n        }\n\n        stacks[stackId] = stacks[stackId] || {\n          width: 0,\n          maxWidth: 0\n        }; // Caution: In a single coordinate system, these barGrid attributes\n        // will be shared by series. Consider that they have default values,\n        // only the attributes set on the last series will work.\n        // Do not change this fact unless there will be a break change.\n\n        var barWidth = seriesInfo.barWidth;\n\n        if (barWidth && !stacks[stackId].width) {\n          // See #6312, do not restrict width.\n          stacks[stackId].width = barWidth;\n          barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);\n          columnsOnAxis.remainedWidth -= barWidth;\n        }\n\n        var barMaxWidth = seriesInfo.barMaxWidth;\n        barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);\n        var barMinWidth = seriesInfo.barMinWidth;\n        barMinWidth && (stacks[stackId].minWidth = barMinWidth);\n        var barGap = seriesInfo.barGap;\n        barGap != null && (columnsOnAxis.gap = barGap);\n        var barCategoryGap = seriesInfo.barCategoryGap;\n        barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap);\n      });\n      var result = {};\n      each(columnsMap, function (columnsOnAxis, coordSysName) {\n        result[coordSysName] = {};\n        var stacks = columnsOnAxis.stacks;\n        var bandWidth = columnsOnAxis.bandWidth;\n        var categoryGapPercent = columnsOnAxis.categoryGap;\n\n        if (categoryGapPercent == null) {\n          var columnCount = keys(stacks).length; // More columns in one group\n          // the spaces between group is smaller. Or the column will be too thin.\n\n          categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%';\n        }\n\n        var categoryGap = parsePercent$1(categoryGapPercent, bandWidth);\n        var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);\n        var remainedWidth = columnsOnAxis.remainedWidth;\n        var autoWidthCount = columnsOnAxis.autoWidthCount;\n        var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0); // Find if any auto calculated bar exceeded maxBarWidth\n\n        each(stacks, function (column) {\n          var maxWidth = column.maxWidth;\n          var minWidth = column.minWidth;\n\n          if (!column.width) {\n            var finalWidth = autoWidth;\n\n            if (maxWidth && maxWidth < finalWidth) {\n              finalWidth = Math.min(maxWidth, remainedWidth);\n            } // `minWidth` has higher priority. `minWidth` decide that whether the\n            // bar is able to be visible. So `minWidth` should not be restricted\n            // by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In\n            // the extreme cases for `value` axis, bars are allowed to overlap\n            // with each other if `minWidth` specified.\n\n\n            if (minWidth && minWidth > finalWidth) {\n              finalWidth = minWidth;\n            }\n\n            if (finalWidth !== autoWidth) {\n              column.width = finalWidth;\n              remainedWidth -= finalWidth + barGapPercent * finalWidth;\n              autoWidthCount--;\n            }\n          } else {\n            // `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as\n            // CSS does. Because barWidth can be a percent value, where\n            // `barMaxWidth` can be used to restrict the final width.\n            var finalWidth = column.width;\n\n            if (maxWidth) {\n              finalWidth = Math.min(finalWidth, maxWidth);\n            } // `minWidth` has higher priority, as described above\n\n\n            if (minWidth) {\n              finalWidth = Math.max(finalWidth, minWidth);\n            }\n\n            column.width = finalWidth;\n            remainedWidth -= finalWidth + barGapPercent * finalWidth;\n            autoWidthCount--;\n          }\n        }); // Recalculate width again\n\n        autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n        autoWidth = Math.max(autoWidth, 0);\n        var widthSum = 0;\n        var lastColumn;\n        each(stacks, function (column, idx) {\n          if (!column.width) {\n            column.width = autoWidth;\n          }\n\n          lastColumn = column;\n          widthSum += column.width * (1 + barGapPercent);\n        });\n\n        if (lastColumn) {\n          widthSum -= lastColumn.width * barGapPercent;\n        }\n\n        var offset = -widthSum / 2;\n        each(stacks, function (column, stackId) {\n          result[coordSysName][stackId] = result[coordSysName][stackId] || {\n            bandWidth: bandWidth,\n            offset: offset,\n            width: column.width\n          };\n          offset += column.width * (1 + barGapPercent);\n        });\n      });\n      return result;\n    }\n\n    function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) {\n      if (barWidthAndOffset && axis) {\n        var result = barWidthAndOffset[getAxisKey(axis)];\n\n        if (result != null && seriesModel != null) {\n          return result[getSeriesStackId(seriesModel)];\n        }\n\n        return result;\n      }\n    }\n    function layout(seriesType, ecModel) {\n      var seriesModels = prepareLayoutBarSeries(seriesType, ecModel);\n      var barWidthAndOffset = makeColumnLayout(seriesModels);\n      each(seriesModels, function (seriesModel) {\n        var data = seriesModel.getData();\n        var cartesian = seriesModel.coordinateSystem;\n        var baseAxis = cartesian.getBaseAxis();\n        var stackId = getSeriesStackId(seriesModel);\n        var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];\n        var columnOffset = columnLayoutInfo.offset;\n        var columnWidth = columnLayoutInfo.width;\n        data.setLayout({\n          bandWidth: columnLayoutInfo.bandWidth,\n          offset: columnOffset,\n          size: columnWidth\n        });\n      });\n    } // TODO: Do not support stack in large mode yet.\n\n    function createProgressiveLayout(seriesType) {\n      return {\n        seriesType: seriesType,\n        plan: createRenderPlanner(),\n        reset: function (seriesModel) {\n          if (!isOnCartesian(seriesModel)) {\n            return;\n          }\n\n          var data = seriesModel.getData();\n          var cartesian = seriesModel.coordinateSystem;\n          var baseAxis = cartesian.getBaseAxis();\n          var valueAxis = cartesian.getOtherAxis(baseAxis);\n          var valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim));\n          var baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim));\n          var drawBackground = seriesModel.get('showBackground', true);\n          var valueDim = data.mapDimension(valueAxis.dim);\n          var stackResultDim = data.getCalculationInfo('stackResultDimension');\n          var stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries');\n          var isValueAxisH = valueAxis.isHorizontal();\n          var valueAxisStart = getValueAxisStart(baseAxis, valueAxis);\n          var isLarge = isInLargeMode(seriesModel);\n          var barMinHeight = seriesModel.get('barMinHeight') || 0;\n          var stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim); // Layout info.\n\n          var columnWidth = data.getLayout('size');\n          var columnOffset = data.getLayout('offset');\n          return {\n            progress: function (params, data) {\n              var count = params.count;\n              var largePoints = isLarge && createFloat32Array(count * 3);\n              var largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3);\n              var largeDataIndices = isLarge && createFloat32Array(count);\n              var coordLayout = cartesian.master.getRect();\n              var bgSize = isValueAxisH ? coordLayout.width : coordLayout.height;\n              var dataIndex;\n              var store = data.getStore();\n              var idxOffset = 0;\n\n              while ((dataIndex = params.next()) != null) {\n                var value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex);\n                var baseValue = store.get(baseDimIdx, dataIndex);\n                var baseCoord = valueAxisStart;\n                var startValue = void 0; // Because of the barMinHeight, we can not use the value in\n                // stackResultDimension directly.\n\n                if (stacked) {\n                  startValue = +value - store.get(valueDimIdx, dataIndex);\n                }\n\n                var x = void 0;\n                var y = void 0;\n                var width = void 0;\n                var height = void 0;\n\n                if (isValueAxisH) {\n                  var coord = cartesian.dataToPoint([value, baseValue]);\n\n                  if (stacked) {\n                    var startCoord = cartesian.dataToPoint([startValue, baseValue]);\n                    baseCoord = startCoord[0];\n                  }\n\n                  x = baseCoord;\n                  y = coord[1] + columnOffset;\n                  width = coord[0] - baseCoord;\n                  height = columnWidth;\n\n                  if (Math.abs(width) < barMinHeight) {\n                    width = (width < 0 ? -1 : 1) * barMinHeight;\n                  }\n                } else {\n                  var coord = cartesian.dataToPoint([baseValue, value]);\n\n                  if (stacked) {\n                    var startCoord = cartesian.dataToPoint([baseValue, startValue]);\n                    baseCoord = startCoord[1];\n                  }\n\n                  x = coord[0] + columnOffset;\n                  y = baseCoord;\n                  width = columnWidth;\n                  height = coord[1] - baseCoord;\n\n                  if (Math.abs(height) < barMinHeight) {\n                    // Include zero to has a positive bar\n                    height = (height <= 0 ? -1 : 1) * barMinHeight;\n                  }\n                }\n\n                if (!isLarge) {\n                  data.setItemLayout(dataIndex, {\n                    x: x,\n                    y: y,\n                    width: width,\n                    height: height\n                  });\n                } else {\n                  largePoints[idxOffset] = x;\n                  largePoints[idxOffset + 1] = y;\n                  largePoints[idxOffset + 2] = isValueAxisH ? width : height;\n\n                  if (largeBackgroundPoints) {\n                    largeBackgroundPoints[idxOffset] = isValueAxisH ? coordLayout.x : x;\n                    largeBackgroundPoints[idxOffset + 1] = isValueAxisH ? y : coordLayout.y;\n                    largeBackgroundPoints[idxOffset + 2] = bgSize;\n                  }\n\n                  largeDataIndices[dataIndex] = dataIndex;\n                }\n\n                idxOffset += 3;\n              }\n\n              if (isLarge) {\n                data.setLayout({\n                  largePoints: largePoints,\n                  largeDataIndices: largeDataIndices,\n                  largeBackgroundPoints: largeBackgroundPoints,\n                  valueAxisHorizontal: isValueAxisH\n                });\n              }\n            }\n          };\n        }\n      };\n    }\n\n    function isOnCartesian(seriesModel) {\n      return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d';\n    }\n\n    function isInLargeMode(seriesModel) {\n      return seriesModel.pipelineContext && seriesModel.pipelineContext.large;\n    } // See cases in `test/bar-start.html` and `#7412`, `#8747`.\n\n\n    function getValueAxisStart(baseAxis, valueAxis) {\n      return valueAxis.toGlobalCoord(valueAxis.dataToCoord(valueAxis.type === 'log' ? 1 : 0));\n    }\n\n    var bisect = function (a, x, lo, hi) {\n      while (lo < hi) {\n        var mid = lo + hi >>> 1;\n\n        if (a[mid][1] < x) {\n          lo = mid + 1;\n        } else {\n          hi = mid;\n        }\n      }\n\n      return lo;\n    };\n\n    var TimeScale =\n    /** @class */\n    function (_super) {\n      __extends(TimeScale, _super);\n\n      function TimeScale(settings) {\n        var _this = _super.call(this, settings) || this;\n\n        _this.type = 'time';\n        return _this;\n      }\n      /**\n       * Get label is mainly for other components like dataZoom, tooltip.\n       */\n\n\n      TimeScale.prototype.getLabel = function (tick) {\n        var useUTC = this.getSetting('useUTC');\n        return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale'));\n      };\n\n      TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) {\n        var isUTC = this.getSetting('useUTC');\n        var lang = this.getSetting('locale');\n        return leveledFormat(tick, idx, labelFormatter, lang, isUTC);\n      };\n      /**\n       * @override\n       */\n\n\n      TimeScale.prototype.getTicks = function () {\n        var interval = this._interval;\n        var extent = this._extent;\n        var ticks = []; // If interval is 0, return [];\n\n        if (!interval) {\n          return ticks;\n        }\n\n        ticks.push({\n          value: extent[0],\n          level: 0\n        });\n        var useUTC = this.getSetting('useUTC');\n        var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent);\n        ticks = ticks.concat(innerTicks);\n        ticks.push({\n          value: extent[1],\n          level: 0\n        });\n        return ticks;\n      };\n\n      TimeScale.prototype.calcNiceExtent = function (opt) {\n        var extent = this._extent; // If extent start and end are same, expand them\n\n        if (extent[0] === extent[1]) {\n          // Expand extent\n          extent[0] -= ONE_DAY;\n          extent[1] += ONE_DAY;\n        } // If there are no data and extent are [Infinity, -Infinity]\n\n\n        if (extent[1] === -Infinity && extent[0] === Infinity) {\n          var d = new Date();\n          extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());\n          extent[0] = extent[1] - ONE_DAY;\n        }\n\n        this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);\n      };\n\n      TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) {\n        approxTickNum = approxTickNum || 10;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n        this._approxInterval = span / approxTickNum;\n\n        if (minInterval != null && this._approxInterval < minInterval) {\n          this._approxInterval = minInterval;\n        }\n\n        if (maxInterval != null && this._approxInterval > maxInterval) {\n          this._approxInterval = maxInterval;\n        }\n\n        var scaleIntervalsLen = scaleIntervals.length;\n        var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1); // Interval that can be used to calculate ticks\n\n        this._interval = scaleIntervals[idx][1]; // Min level used when picking ticks from top down.\n        // We check one more level to avoid the ticks are to sparse in some case.\n\n        this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0];\n      };\n\n      TimeScale.prototype.parse = function (val) {\n        // val might be float.\n        return isNumber(val) ? val : +parseDate(val);\n      };\n\n      TimeScale.prototype.contain = function (val) {\n        return contain$1(this.parse(val), this._extent);\n      };\n\n      TimeScale.prototype.normalize = function (val) {\n        return normalize$1(this.parse(val), this._extent);\n      };\n\n      TimeScale.prototype.scale = function (val) {\n        return scale$2(val, this._extent);\n      };\n\n      TimeScale.type = 'time';\n      return TimeScale;\n    }(IntervalScale);\n    /**\n     * This implementation was originally copied from \"d3.js\"\n     * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>\n     * with some modifications made for this program.\n     * See the license statement at the head of this file.\n     */\n\n\n    var scaleIntervals = [// Format                           interval\n    ['second', ONE_SECOND], ['minute', ONE_MINUTE], ['hour', ONE_HOUR], ['quarter-day', ONE_HOUR * 6], ['half-day', ONE_HOUR * 12], ['day', ONE_DAY * 1.2], ['half-week', ONE_DAY * 3.5], ['week', ONE_DAY * 7], ['month', ONE_DAY * 31], ['quarter', ONE_DAY * 95], ['half-year', ONE_YEAR / 2], ['year', ONE_YEAR] // 1Y\n    ];\n\n    function isUnitValueSame(unit, valueA, valueB, isUTC) {\n      var dateA = parseDate(valueA);\n      var dateB = parseDate(valueB);\n\n      var isSame = function (unit) {\n        return getUnitValue(dateA, unit, isUTC) === getUnitValue(dateB, unit, isUTC);\n      };\n\n      var isSameYear = function () {\n        return isSame('year');\n      }; // const isSameHalfYear = () => isSameYear() && isSame('half-year');\n      // const isSameQuater = () => isSameYear() && isSame('quarter');\n\n\n      var isSameMonth = function () {\n        return isSameYear() && isSame('month');\n      };\n\n      var isSameDay = function () {\n        return isSameMonth() && isSame('day');\n      }; // const isSameHalfDay = () => isSameDay() && isSame('half-day');\n\n\n      var isSameHour = function () {\n        return isSameDay() && isSame('hour');\n      };\n\n      var isSameMinute = function () {\n        return isSameHour() && isSame('minute');\n      };\n\n      var isSameSecond = function () {\n        return isSameMinute() && isSame('second');\n      };\n\n      var isSameMilliSecond = function () {\n        return isSameSecond() && isSame('millisecond');\n      };\n\n      switch (unit) {\n        case 'year':\n          return isSameYear();\n\n        case 'month':\n          return isSameMonth();\n\n        case 'day':\n          return isSameDay();\n\n        case 'hour':\n          return isSameHour();\n\n        case 'minute':\n          return isSameMinute();\n\n        case 'second':\n          return isSameSecond();\n\n        case 'millisecond':\n          return isSameMilliSecond();\n      }\n    } // const primaryUnitGetters = {\n    //     year: fullYearGetterName(),\n    //     month: monthGetterName(),\n    //     day: dateGetterName(),\n    //     hour: hoursGetterName(),\n    //     minute: minutesGetterName(),\n    //     second: secondsGetterName(),\n    //     millisecond: millisecondsGetterName()\n    // };\n    // const primaryUnitUTCGetters = {\n    //     year: fullYearGetterName(true),\n    //     month: monthGetterName(true),\n    //     day: dateGetterName(true),\n    //     hour: hoursGetterName(true),\n    //     minute: minutesGetterName(true),\n    //     second: secondsGetterName(true),\n    //     millisecond: millisecondsGetterName(true)\n    // };\n    // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) {\n    //     step = step || 1;\n    //     switch (getPrimaryTimeUnit(unitName)) {\n    //         case 'year':\n    //             date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'month':\n    //             date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'day':\n    //             date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'hour':\n    //             date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'minute':\n    //             date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'second':\n    //             date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step);\n    //             break;\n    //         case 'millisecond':\n    //             date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step);\n    //             break;\n    //     }\n    //     return date.getTime();\n    // }\n    // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]];\n    // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]];\n    // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]];\n\n\n    function getDateInterval(approxInterval, daysInMonth) {\n      approxInterval /= ONE_DAY;\n      return approxInterval > 16 ? 16 // Math.floor(daysInMonth / 2) + 1  // In this case we only want one tick between two months.\n      : approxInterval > 7.5 ? 7 // TODO week 7 or day 8?\n      : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1;\n    }\n\n    function getMonthInterval(approxInterval) {\n      var APPROX_ONE_MONTH = 30 * ONE_DAY;\n      approxInterval /= APPROX_ONE_MONTH;\n      return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getHourInterval(approxInterval) {\n      approxInterval /= ONE_HOUR;\n      return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getMinutesAndSecondsInterval(approxInterval, isMinutes) {\n      approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND;\n      return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1;\n    }\n\n    function getMillisecondsInterval(approxInterval) {\n      return nice(approxInterval, true);\n    }\n\n    function getFirstTimestampOfUnit(date, unitName, isUTC) {\n      var outDate = new Date(date);\n\n      switch (getPrimaryTimeUnit(unitName)) {\n        case 'year':\n        case 'month':\n          outDate[monthSetterName(isUTC)](0);\n\n        case 'day':\n          outDate[dateSetterName(isUTC)](1);\n\n        case 'hour':\n          outDate[hoursSetterName(isUTC)](0);\n\n        case 'minute':\n          outDate[minutesSetterName(isUTC)](0);\n\n        case 'second':\n          outDate[secondsSetterName(isUTC)](0);\n          outDate[millisecondsSetterName(isUTC)](0);\n      }\n\n      return outDate.getTime();\n    }\n\n    function getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent) {\n      var safeLimit = 10000;\n      var unitNames = timeUnits;\n      var iter = 0;\n\n      function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) {\n        var date = new Date(minTimestamp);\n        var dateTime = minTimestamp;\n        var d = date[getMethodName](); // if (isDate) {\n        //     d -= 1; // Starts with 0;   PENDING\n        // }\n\n        while (dateTime < maxTimestamp && dateTime <= extent[1]) {\n          out.push({\n            value: dateTime\n          });\n          d += interval;\n          date[setMethodName](d);\n          dateTime = date.getTime();\n        } // This extra tick is for calcuating ticks of next level. Will not been added to the final result\n\n\n        out.push({\n          value: dateTime,\n          notAdd: true\n        });\n      }\n\n      function addLevelTicks(unitName, lastLevelTicks, levelTicks) {\n        var newAddedTicks = [];\n        var isFirstLevel = !lastLevelTicks.length;\n\n        if (isUnitValueSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) {\n          return;\n        }\n\n        if (isFirstLevel) {\n          lastLevelTicks = [{\n            // TODO Optimize. Not include so may ticks.\n            value: getFirstTimestampOfUnit(new Date(extent[0]), unitName, isUTC)\n          }, {\n            value: extent[1]\n          }];\n        }\n\n        for (var i = 0; i < lastLevelTicks.length - 1; i++) {\n          var startTick = lastLevelTicks[i].value;\n          var endTick = lastLevelTicks[i + 1].value;\n\n          if (startTick === endTick) {\n            continue;\n          }\n\n          var interval = void 0;\n          var getterName = void 0;\n          var setterName = void 0;\n          var isDate = false;\n\n          switch (unitName) {\n            case 'year':\n              interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365));\n              getterName = fullYearGetterName(isUTC);\n              setterName = fullYearSetterName(isUTC);\n              break;\n\n            case 'half-year':\n            case 'quarter':\n            case 'month':\n              interval = getMonthInterval(approxInterval);\n              getterName = monthGetterName(isUTC);\n              setterName = monthSetterName(isUTC);\n              break;\n\n            case 'week': // PENDING If week is added. Ignore day.\n\n            case 'half-week':\n            case 'day':\n              interval = getDateInterval(approxInterval); // Use 32 days and let interval been 16\n\n              getterName = dateGetterName(isUTC);\n              setterName = dateSetterName(isUTC);\n              isDate = true;\n              break;\n\n            case 'half-day':\n            case 'quarter-day':\n            case 'hour':\n              interval = getHourInterval(approxInterval);\n              getterName = hoursGetterName(isUTC);\n              setterName = hoursSetterName(isUTC);\n              break;\n\n            case 'minute':\n              interval = getMinutesAndSecondsInterval(approxInterval, true);\n              getterName = minutesGetterName(isUTC);\n              setterName = minutesSetterName(isUTC);\n              break;\n\n            case 'second':\n              interval = getMinutesAndSecondsInterval(approxInterval, false);\n              getterName = secondsGetterName(isUTC);\n              setterName = secondsSetterName(isUTC);\n              break;\n\n            case 'millisecond':\n              interval = getMillisecondsInterval(approxInterval);\n              getterName = millisecondsGetterName(isUTC);\n              setterName = millisecondsSetterName(isUTC);\n              break;\n          }\n\n          addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks);\n\n          if (unitName === 'year' && levelTicks.length > 1 && i === 0) {\n            // Add nearest years to the left extent.\n            levelTicks.unshift({\n              value: levelTicks[0].value - interval\n            });\n          }\n        }\n\n        for (var i = 0; i < newAddedTicks.length; i++) {\n          levelTicks.push(newAddedTicks[i]);\n        } // newAddedTicks.length && console.log(unitName, newAddedTicks);\n\n\n        return newAddedTicks;\n      }\n\n      var levelsTicks = [];\n      var currentLevelTicks = [];\n      var tickCount = 0;\n      var lastLevelTickCount = 0;\n\n      for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) {\n        var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]);\n\n        if (!isPrimaryTimeUnit(unitNames[i])) {\n          // TODO\n          continue;\n        }\n\n        addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks);\n        var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null;\n\n        if (primaryTimeUnit !== nextPrimaryTimeUnit) {\n          if (currentLevelTicks.length) {\n            lastLevelTickCount = tickCount; // Remove the duplicate so the tick count can be precisely.\n\n            currentLevelTicks.sort(function (a, b) {\n              return a.value - b.value;\n            });\n            var levelTicksRemoveDuplicated = [];\n\n            for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) {\n              var tickValue = currentLevelTicks[i_1].value;\n\n              if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) {\n                levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]);\n\n                if (tickValue >= extent[0] && tickValue <= extent[1]) {\n                  tickCount++;\n                }\n              }\n            }\n\n            var targetTickNum = (extent[1] - extent[0]) / approxInterval; // Added too much in this level and not too less in last level\n\n            if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) {\n              break;\n            } // Only treat primary time unit as one level.\n\n\n            levelsTicks.push(levelTicksRemoveDuplicated);\n\n            if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) {\n              break;\n            }\n          } // Reset if next unitName is primary\n\n\n          currentLevelTicks = [];\n        }\n      }\n\n      if (\"development\" !== 'production') {\n        if (iter >= safeLimit) {\n          warn('Exceed safe limit.');\n        }\n      }\n\n      var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) {\n        return filter(levelTicks, function (tick) {\n          return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd;\n        });\n      }), function (levelTicks) {\n        return levelTicks.length > 0;\n      });\n      var ticks = [];\n      var maxLevel = levelsTicksInExtent.length - 1;\n\n      for (var i = 0; i < levelsTicksInExtent.length; ++i) {\n        var levelTicks = levelsTicksInExtent[i];\n\n        for (var k = 0; k < levelTicks.length; ++k) {\n          ticks.push({\n            value: levelTicks[k].value,\n            level: maxLevel - i\n          });\n        }\n      }\n\n      ticks.sort(function (a, b) {\n        return a.value - b.value;\n      }); // Remove duplicates\n\n      var result = [];\n\n      for (var i = 0; i < ticks.length; ++i) {\n        if (i === 0 || ticks[i].value !== ticks[i - 1].value) {\n          result.push(ticks[i]);\n        }\n      }\n\n      return result;\n    }\n\n    Scale.registerClass(TimeScale);\n\n    var scaleProto = Scale.prototype; // FIXME:TS refactor: not good to call it directly with `this`?\n\n    var intervalScaleProto = IntervalScale.prototype;\n    var roundingErrorFix = round;\n    var mathFloor = Math.floor;\n    var mathCeil = Math.ceil;\n    var mathPow$1 = Math.pow;\n    var mathLog = Math.log;\n\n    var LogScale =\n    /** @class */\n    function (_super) {\n      __extends(LogScale, _super);\n\n      function LogScale() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'log';\n        _this.base = 10;\n        _this._originalScale = new IntervalScale(); // FIXME:TS actually used by `IntervalScale`\n\n        _this._interval = 0;\n        return _this;\n      }\n      /**\n       * @param Whether expand the ticks to niced extent.\n       */\n\n\n      LogScale.prototype.getTicks = function (expandToNicedExtent) {\n        var originalScale = this._originalScale;\n        var extent = this._extent;\n        var originalExtent = originalScale.getExtent();\n        var ticks = intervalScaleProto.getTicks.call(this, expandToNicedExtent);\n        return map(ticks, function (tick) {\n          var val = tick.value;\n          var powVal = round(mathPow$1(this.base, val)); // Fix #4158\n\n          powVal = val === extent[0] && this._fixMin ? fixRoundingError(powVal, originalExtent[0]) : powVal;\n          powVal = val === extent[1] && this._fixMax ? fixRoundingError(powVal, originalExtent[1]) : powVal;\n          return {\n            value: powVal\n          };\n        }, this);\n      };\n\n      LogScale.prototype.setExtent = function (start, end) {\n        var base = mathLog(this.base); // log(-Infinity) is NaN, so safe guard here\n\n        start = mathLog(Math.max(0, start)) / base;\n        end = mathLog(Math.max(0, end)) / base;\n        intervalScaleProto.setExtent.call(this, start, end);\n      };\n      /**\n       * @return {number} end\n       */\n\n\n      LogScale.prototype.getExtent = function () {\n        var base = this.base;\n        var extent = scaleProto.getExtent.call(this);\n        extent[0] = mathPow$1(base, extent[0]);\n        extent[1] = mathPow$1(base, extent[1]); // Fix #4158\n\n        var originalScale = this._originalScale;\n        var originalExtent = originalScale.getExtent();\n        this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));\n        this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));\n        return extent;\n      };\n\n      LogScale.prototype.unionExtent = function (extent) {\n        this._originalScale.unionExtent(extent);\n\n        var base = this.base;\n        extent[0] = mathLog(extent[0]) / mathLog(base);\n        extent[1] = mathLog(extent[1]) / mathLog(base);\n        scaleProto.unionExtent.call(this, extent);\n      };\n\n      LogScale.prototype.unionExtentFromData = function (data, dim) {\n        // TODO\n        // filter value that <= 0\n        this.unionExtent(data.getApproximateExtent(dim));\n      };\n      /**\n       * Update interval and extent of intervals for nice ticks\n       * @param approxTickNum default 10 Given approx tick number\n       */\n\n\n      LogScale.prototype.calcNiceTicks = function (approxTickNum) {\n        approxTickNum = approxTickNum || 10;\n        var extent = this._extent;\n        var span = extent[1] - extent[0];\n\n        if (span === Infinity || span <= 0) {\n          return;\n        }\n\n        var interval = quantity(span);\n        var err = approxTickNum / span * interval; // Filter ticks to get closer to the desired count.\n\n        if (err <= 0.5) {\n          interval *= 10;\n        } // Interval should be integer\n\n\n        while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {\n          interval *= 10;\n        }\n\n        var niceExtent = [round(mathCeil(extent[0] / interval) * interval), round(mathFloor(extent[1] / interval) * interval)];\n        this._interval = interval;\n        this._niceExtent = niceExtent;\n      };\n\n      LogScale.prototype.calcNiceExtent = function (opt) {\n        intervalScaleProto.calcNiceExtent.call(this, opt);\n        this._fixMin = opt.fixMin;\n        this._fixMax = opt.fixMax;\n      };\n\n      LogScale.prototype.parse = function (val) {\n        return val;\n      };\n\n      LogScale.prototype.contain = function (val) {\n        val = mathLog(val) / mathLog(this.base);\n        return contain$1(val, this._extent);\n      };\n\n      LogScale.prototype.normalize = function (val) {\n        val = mathLog(val) / mathLog(this.base);\n        return normalize$1(val, this._extent);\n      };\n\n      LogScale.prototype.scale = function (val) {\n        val = scale$2(val, this._extent);\n        return mathPow$1(this.base, val);\n      };\n\n      LogScale.type = 'log';\n      return LogScale;\n    }(Scale);\n\n    var proto = LogScale.prototype;\n    proto.getMinorTicks = intervalScaleProto.getMinorTicks;\n    proto.getLabel = intervalScaleProto.getLabel;\n\n    function fixRoundingError(val, originalVal) {\n      return roundingErrorFix(val, getPrecision(originalVal));\n    }\n\n    Scale.registerClass(LogScale);\n\n    var ScaleRawExtentInfo =\n    /** @class */\n    function () {\n      function ScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\n      originalExtent) {\n        this._prepareParams(scale, model, originalExtent);\n      }\n      /**\n       * Parameters depending on outside (like model, user callback)\n       * are prepared and fixed here.\n       */\n\n\n      ScaleRawExtentInfo.prototype._prepareParams = function (scale, model, // Usually: data extent from all series on this axis.\n      dataExtent) {\n        if (dataExtent[1] < dataExtent[0]) {\n          dataExtent = [NaN, NaN];\n        }\n\n        this._dataMin = dataExtent[0];\n        this._dataMax = dataExtent[1];\n        var isOrdinal = this._isOrdinal = scale.type === 'ordinal';\n        this._needCrossZero = scale.type === 'interval' && model.getNeedCrossZero && model.getNeedCrossZero();\n        var modelMinRaw = this._modelMinRaw = model.get('min', true);\n\n        if (isFunction(modelMinRaw)) {\n          // This callback always provides users the full data extent (before data is filtered).\n          this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw({\n            min: dataExtent[0],\n            max: dataExtent[1]\n          }));\n        } else if (modelMinRaw !== 'dataMin') {\n          this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw);\n        }\n\n        var modelMaxRaw = this._modelMaxRaw = model.get('max', true);\n\n        if (isFunction(modelMaxRaw)) {\n          // This callback always provides users the full data extent (before data is filtered).\n          this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw({\n            min: dataExtent[0],\n            max: dataExtent[1]\n          }));\n        } else if (modelMaxRaw !== 'dataMax') {\n          this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw);\n        }\n\n        if (isOrdinal) {\n          // FIXME: there is a flaw here: if there is no \"block\" data processor like `dataZoom`,\n          // and progressive rendering is using, here the category result might just only contain\n          // the processed chunk rather than the entire result.\n          this._axisDataLen = model.getCategories().length;\n        } else {\n          var boundaryGap = model.get('boundaryGap');\n          var boundaryGapArr = isArray(boundaryGap) ? boundaryGap : [boundaryGap || 0, boundaryGap || 0];\n\n          if (typeof boundaryGapArr[0] === 'boolean' || typeof boundaryGapArr[1] === 'boolean') {\n            if (\"development\" !== 'production') {\n              console.warn('Boolean type for boundaryGap is only ' + 'allowed for ordinal axis. Please use string in ' + 'percentage instead, e.g., \"20%\". Currently, ' + 'boundaryGap is set to be 0.');\n            }\n\n            this._boundaryGapInner = [0, 0];\n          } else {\n            this._boundaryGapInner = [parsePercent(boundaryGapArr[0], 1), parsePercent(boundaryGapArr[1], 1)];\n          }\n        }\n      };\n      /**\n       * Calculate extent by prepared parameters.\n       * This method has no external dependency and can be called duplicatedly,\n       * getting the same result.\n       * If parameters changed, should call this method to recalcuate.\n       */\n\n\n      ScaleRawExtentInfo.prototype.calculate = function () {\n        // Notice: When min/max is not set (that is, when there are null/undefined,\n        // which is the most common case), these cases should be ensured:\n        // (1) For 'ordinal', show all axis.data.\n        // (2) For others:\n        //      + `boundaryGap` is applied (if min/max set, boundaryGap is\n        //      disabled).\n        //      + If `needCrossZero`, min/max should be zero, otherwise, min/max should\n        //      be the result that originalExtent enlarged by boundaryGap.\n        // (3) If no data, it should be ensured that `scale.setBlank` is set.\n        var isOrdinal = this._isOrdinal;\n        var dataMin = this._dataMin;\n        var dataMax = this._dataMax;\n        var axisDataLen = this._axisDataLen;\n        var boundaryGapInner = this._boundaryGapInner;\n        var span = !isOrdinal ? dataMax - dataMin || Math.abs(dataMin) : null; // Currently if a `'value'` axis model min is specified as 'dataMin'/'dataMax',\n        // `boundaryGap` will not be used. It's the different from specifying as `null`/`undefined`.\n\n        var min = this._modelMinRaw === 'dataMin' ? dataMin : this._modelMinNum;\n        var max = this._modelMaxRaw === 'dataMax' ? dataMax : this._modelMaxNum; // If `_modelMinNum`/`_modelMaxNum` is `null`/`undefined`, should not be fixed.\n\n        var minFixed = min != null;\n        var maxFixed = max != null;\n\n        if (min == null) {\n          min = isOrdinal ? axisDataLen ? 0 : NaN : dataMin - boundaryGapInner[0] * span;\n        }\n\n        if (max == null) {\n          max = isOrdinal ? axisDataLen ? axisDataLen - 1 : NaN : dataMax + boundaryGapInner[1] * span;\n        }\n\n        (min == null || !isFinite(min)) && (min = NaN);\n        (max == null || !isFinite(max)) && (max = NaN);\n        var isBlank = eqNaN(min) || eqNaN(max) || isOrdinal && !axisDataLen; // If data extent modified, need to recalculated to ensure cross zero.\n\n        if (this._needCrossZero) {\n          // Axis is over zero and min is not set\n          if (min > 0 && max > 0 && !minFixed) {\n            min = 0; // minFixed = true;\n          } // Axis is under zero and max is not set\n\n\n          if (min < 0 && max < 0 && !maxFixed) {\n            max = 0; // maxFixed = true;\n          } // PENDING:\n          // When `needCrossZero` and all data is positive/negative, should it be ensured\n          // that the results processed by boundaryGap are positive/negative?\n          // If so, here `minFixed`/`maxFixed` need to be set.\n\n        }\n\n        var determinedMin = this._determinedMin;\n        var determinedMax = this._determinedMax;\n\n        if (determinedMin != null) {\n          min = determinedMin;\n          minFixed = true;\n        }\n\n        if (determinedMax != null) {\n          max = determinedMax;\n          maxFixed = true;\n        } // Ensure min/max be finite number or NaN here. (not to be null/undefined)\n        // `NaN` means min/max axis is blank.\n\n\n        return {\n          min: min,\n          max: max,\n          minFixed: minFixed,\n          maxFixed: maxFixed,\n          isBlank: isBlank\n        };\n      };\n\n      ScaleRawExtentInfo.prototype.modifyDataMinMax = function (minMaxName, val) {\n        if (\"development\" !== 'production') {\n          assert(!this.frozen);\n        }\n\n        this[DATA_MIN_MAX_ATTR[minMaxName]] = val;\n      };\n\n      ScaleRawExtentInfo.prototype.setDeterminedMinMax = function (minMaxName, val) {\n        var attr = DETERMINED_MIN_MAX_ATTR[minMaxName];\n\n        if (\"development\" !== 'production') {\n          assert(!this.frozen // Earse them usually means logic flaw.\n          && this[attr] == null);\n        }\n\n        this[attr] = val;\n      };\n\n      ScaleRawExtentInfo.prototype.freeze = function () {\n        // @ts-ignore\n        this.frozen = true;\n      };\n\n      return ScaleRawExtentInfo;\n    }();\n    var DETERMINED_MIN_MAX_ATTR = {\n      min: '_determinedMin',\n      max: '_determinedMax'\n    };\n    var DATA_MIN_MAX_ATTR = {\n      min: '_dataMin',\n      max: '_dataMax'\n    };\n    /**\n     * Get scale min max and related info only depends on model settings.\n     * This method can be called after coordinate system created.\n     * For example, in data processing stage.\n     *\n     * Scale extent info probably be required multiple times during a workflow.\n     * For example:\n     * (1) `dataZoom` depends it to get the axis extent in \"100%\" state.\n     * (2) `processor/extentCalculator` depends it to make sure whether axis extent is specified.\n     * (3) `coordSys.update` use it to finally decide the scale extent.\n     * But the callback of `min`/`max` should not be called multiple times.\n     * The code below should not be implemented repeatedly either.\n     * So we cache the result in the scale instance, which will be recreated at the beginning\n     * of the workflow (because `scale` instance will be recreated each round of the workflow).\n     */\n\n    function ensureScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis.\n    originalExtent) {\n      // Do not permit to recreate.\n      var rawExtentInfo = scale.rawExtentInfo;\n\n      if (rawExtentInfo) {\n        return rawExtentInfo;\n      }\n\n      rawExtentInfo = new ScaleRawExtentInfo(scale, model, originalExtent); // @ts-ignore\n\n      scale.rawExtentInfo = rawExtentInfo;\n      return rawExtentInfo;\n    }\n    function parseAxisModelMinMax(scale, minMax) {\n      return minMax == null ? null : eqNaN(minMax) ? NaN : scale.parse(minMax);\n    }\n\n    /**\n     * Get axis scale extent before niced.\n     * Item of returned array can only be number (including Infinity and NaN).\n     *\n     * Caution:\n     * Precondition of calling this method:\n     * The scale extent has been initialized using series data extent via\n     * `scale.setExtent` or `scale.unionExtentFromData`;\n     */\n\n    function getScaleExtent(scale, model) {\n      var scaleType = scale.type;\n      var rawExtentResult = ensureScaleRawExtentInfo(scale, model, scale.getExtent()).calculate();\n      scale.setBlank(rawExtentResult.isBlank);\n      var min = rawExtentResult.min;\n      var max = rawExtentResult.max; // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis\n      // is base axis\n      // FIXME\n      // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly.\n      // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent?\n      //     Should not depend on series type `bar`?\n      // (3) Fix that might overlap when using dataZoom.\n      // (4) Consider other chart types using `barGrid`?\n      // See #6728, #4862, `test/bar-overflow-time-plot.html`\n\n      var ecModel = model.ecModel;\n\n      if (ecModel && scaleType === 'time'\n      /* || scaleType === 'interval' */\n      ) {\n        var barSeriesModels = prepareLayoutBarSeries('bar', ecModel);\n        var isBaseAxisAndHasBarSeries_1 = false;\n        each(barSeriesModels, function (seriesModel) {\n          isBaseAxisAndHasBarSeries_1 = isBaseAxisAndHasBarSeries_1 || seriesModel.getBaseAxis() === model.axis;\n        });\n\n        if (isBaseAxisAndHasBarSeries_1) {\n          // Calculate placement of bars on axis. TODO should be decoupled\n          // with barLayout\n          var barWidthAndOffset = makeColumnLayout(barSeriesModels); // Adjust axis min and max to account for overflow\n\n          var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset);\n          min = adjustedScale.min;\n          max = adjustedScale.max;\n        }\n      }\n\n      return {\n        extent: [min, max],\n        // \"fix\" means \"fixed\", the value should not be\n        // changed in the subsequent steps.\n        fixMin: rawExtentResult.minFixed,\n        fixMax: rawExtentResult.maxFixed\n      };\n    }\n\n    function adjustScaleForOverflow(min, max, model, // Only support cartesian coord yet.\n    barWidthAndOffset) {\n      // Get Axis Length\n      var axisExtent = model.axis.getExtent();\n      var axisLength = axisExtent[1] - axisExtent[0]; // Get bars on current base axis and calculate min and max overflow\n\n      var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis);\n\n      if (barsOnCurrentAxis === undefined) {\n        return {\n          min: min,\n          max: max\n        };\n      }\n\n      var minOverflow = Infinity;\n      each(barsOnCurrentAxis, function (item) {\n        minOverflow = Math.min(item.offset, minOverflow);\n      });\n      var maxOverflow = -Infinity;\n      each(barsOnCurrentAxis, function (item) {\n        maxOverflow = Math.max(item.offset + item.width, maxOverflow);\n      });\n      minOverflow = Math.abs(minOverflow);\n      maxOverflow = Math.abs(maxOverflow);\n      var totalOverFlow = minOverflow + maxOverflow; // Calculate required buffer based on old range and overflow\n\n      var oldRange = max - min;\n      var oldRangePercentOfNew = 1 - (minOverflow + maxOverflow) / axisLength;\n      var overflowBuffer = oldRange / oldRangePercentOfNew - oldRange;\n      max += overflowBuffer * (maxOverflow / totalOverFlow);\n      min -= overflowBuffer * (minOverflow / totalOverFlow);\n      return {\n        min: min,\n        max: max\n      };\n    } // Precondition of calling this method:\n    // The scale extent has been initialized using series data extent via\n    // `scale.setExtent` or `scale.unionExtentFromData`;\n\n\n    function niceScaleExtent(scale, inModel) {\n      var model = inModel;\n      var extentInfo = getScaleExtent(scale, model);\n      var extent = extentInfo.extent;\n      var splitNumber = model.get('splitNumber');\n\n      if (scale instanceof LogScale) {\n        scale.base = model.get('logBase');\n      }\n\n      var scaleType = scale.type;\n      var interval = model.get('interval');\n      var isIntervalOrTime = scaleType === 'interval' || scaleType === 'time';\n      scale.setExtent(extent[0], extent[1]);\n      scale.calcNiceExtent({\n        splitNumber: splitNumber,\n        fixMin: extentInfo.fixMin,\n        fixMax: extentInfo.fixMax,\n        minInterval: isIntervalOrTime ? model.get('minInterval') : null,\n        maxInterval: isIntervalOrTime ? model.get('maxInterval') : null\n      }); // If some one specified the min, max. And the default calculated interval\n      // is not good enough. He can specify the interval. It is often appeared\n      // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard\n      // to be 60.\n      // FIXME\n\n      if (interval != null) {\n        scale.setInterval && scale.setInterval(interval);\n      }\n    }\n    /**\n     * @param axisType Default retrieve from model.type\n     */\n\n    function createScaleByModel(model, axisType) {\n      axisType = axisType || model.get('type');\n\n      if (axisType) {\n        switch (axisType) {\n          // Buildin scale\n          case 'category':\n            return new OrdinalScale({\n              ordinalMeta: model.getOrdinalMeta ? model.getOrdinalMeta() : model.getCategories(),\n              extent: [Infinity, -Infinity]\n            });\n\n          case 'time':\n            return new TimeScale({\n              locale: model.ecModel.getLocaleModel(),\n              useUTC: model.ecModel.get('useUTC')\n            });\n\n          default:\n            // case 'value'/'interval', 'log', or others.\n            return new (Scale.getClass(axisType) || IntervalScale)();\n        }\n      }\n    }\n    /**\n     * Check if the axis cross 0\n     */\n\n    function ifAxisCrossZero(axis) {\n      var dataExtent = axis.scale.getExtent();\n      var min = dataExtent[0];\n      var max = dataExtent[1];\n      return !(min > 0 && max > 0 || min < 0 && max < 0);\n    }\n    /**\n     * @param axis\n     * @return Label formatter function.\n     *         param: {number} tickValue,\n     *         param: {number} idx, the index in all ticks.\n     *                         If category axis, this param is not required.\n     *         return: {string} label string.\n     */\n\n    function makeLabelFormatter(axis) {\n      var labelFormatter = axis.getLabelModel().get('formatter');\n      var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null;\n\n      if (axis.scale.type === 'time') {\n        return function (tpl) {\n          return function (tick, idx) {\n            return axis.scale.getFormattedLabel(tick, idx, tpl);\n          };\n        }(labelFormatter);\n      } else if (isString(labelFormatter)) {\n        return function (tpl) {\n          return function (tick) {\n            // For category axis, get raw value; for numeric axis,\n            // get formatted label like '1,333,444'.\n            var label = axis.scale.getLabel(tick);\n            var text = tpl.replace('{value}', label != null ? label : '');\n            return text;\n          };\n        }(labelFormatter);\n      } else if (isFunction(labelFormatter)) {\n        return function (cb) {\n          return function (tick, idx) {\n            // The original intention of `idx` is \"the index of the tick in all ticks\".\n            // But the previous implementation of category axis do not consider the\n            // `axisLabel.interval`, which cause that, for example, the `interval` is\n            // `1`, then the ticks \"name5\", \"name7\", \"name9\" are displayed, where the\n            // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep\n            // the definition here for back compatibility.\n            if (categoryTickStart != null) {\n              idx = tick.value - categoryTickStart;\n            }\n\n            return cb(getAxisRawValue(axis, tick), idx, tick.level != null ? {\n              level: tick.level\n            } : null);\n          };\n        }(labelFormatter);\n      } else {\n        return function (tick) {\n          return axis.scale.getLabel(tick);\n        };\n      }\n    }\n    function getAxisRawValue(axis, tick) {\n      // In category axis with data zoom, tick is not the original\n      // index of axis.data. So tick should not be exposed to user\n      // in category axis.\n      return axis.type === 'category' ? axis.scale.getLabel(tick) : tick.value;\n    }\n    /**\n     * @param axis\n     * @return Be null/undefined if no labels.\n     */\n\n    function estimateLabelUnionRect(axis) {\n      var axisModel = axis.model;\n      var scale = axis.scale;\n\n      if (!axisModel.get(['axisLabel', 'show']) || scale.isBlank()) {\n        return;\n      }\n\n      var realNumberScaleTicks;\n      var tickCount;\n      var categoryScaleExtent = scale.getExtent(); // Optimize for large category data, avoid call `getTicks()`.\n\n      if (scale instanceof OrdinalScale) {\n        tickCount = scale.count();\n      } else {\n        realNumberScaleTicks = scale.getTicks();\n        tickCount = realNumberScaleTicks.length;\n      }\n\n      var axisLabelModel = axis.getLabelModel();\n      var labelFormatter = makeLabelFormatter(axis);\n      var rect;\n      var step = 1; // Simple optimization for large amount of labels\n\n      if (tickCount > 40) {\n        step = Math.ceil(tickCount / 40);\n      }\n\n      for (var i = 0; i < tickCount; i += step) {\n        var tick = realNumberScaleTicks ? realNumberScaleTicks[i] : {\n          value: categoryScaleExtent[0] + i\n        };\n        var label = labelFormatter(tick, i);\n        var unrotatedSingleRect = axisLabelModel.getTextRect(label);\n        var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);\n        rect ? rect.union(singleRect) : rect = singleRect;\n      }\n\n      return rect;\n    }\n\n    function rotateTextRect(textRect, rotate) {\n      var rotateRadians = rotate * Math.PI / 180;\n      var beforeWidth = textRect.width;\n      var beforeHeight = textRect.height;\n      var afterWidth = beforeWidth * Math.abs(Math.cos(rotateRadians)) + Math.abs(beforeHeight * Math.sin(rotateRadians));\n      var afterHeight = beforeWidth * Math.abs(Math.sin(rotateRadians)) + Math.abs(beforeHeight * Math.cos(rotateRadians));\n      var rotatedRect = new BoundingRect(textRect.x, textRect.y, afterWidth, afterHeight);\n      return rotatedRect;\n    }\n    /**\n     * @param model axisLabelModel or axisTickModel\n     * @return {number|String} Can be null|'auto'|number|function\n     */\n\n\n    function getOptionCategoryInterval(model) {\n      var interval = model.get('interval');\n      return interval == null ? 'auto' : interval;\n    }\n    /**\n     * Set `categoryInterval` as 0 implicitly indicates that\n     * show all labels regardless of overlap.\n     * @param {Object} axis axisModel.axis\n     */\n\n    function shouldShowAllLabels(axis) {\n      return axis.type === 'category' && getOptionCategoryInterval(axis.getLabelModel()) === 0;\n    }\n    function getDataDimensionsOnAxis(data, axisDim) {\n      // Remove duplicated dat dimensions caused by `getStackedDimension`.\n      var dataDimMap = {}; // Currently `mapDimensionsAll` will contain stack result dimension ('__\\0ecstackresult').\n      // PENDING: is it reasonable? Do we need to remove the original dim from \"coord dim\" since\n      // there has been stacked result dim?\n\n      each(data.mapDimensionsAll(axisDim), function (dataDim) {\n        // For example, the extent of the original dimension\n        // is [0.1, 0.5], the extent of the `stackResultDimension`\n        // is [7, 9], the final extent should NOT include [0.1, 0.5],\n        // because there is no graphic corresponding to [0.1, 0.5].\n        // See the case in `test/area-stack.html` `main1`, where area line\n        // stack needs `yAxis` not start from 0.\n        dataDimMap[getStackedDimension(data, dataDim)] = true;\n      });\n      return keys(dataDimMap);\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    var AxisModelCommonMixin =\n    /** @class */\n    function () {\n      function AxisModelCommonMixin() {}\n\n      AxisModelCommonMixin.prototype.getNeedCrossZero = function () {\n        var option = this.option;\n        return !option.scale;\n      };\n      /**\n       * Should be implemented by each axis model if necessary.\n       * @return coordinate system model\n       */\n\n\n      AxisModelCommonMixin.prototype.getCoordSysModel = function () {\n        return;\n      };\n\n      return AxisModelCommonMixin;\n    }();\n\n    /**\n     * Create a multi dimension List structure from seriesModel.\n     */\n\n    function createList(seriesModel) {\n      return createSeriesData(null, seriesModel);\n    } // export function createGraph(seriesModel) {\n    var dataStack$1 = {\n      isDimensionStacked: isDimensionStacked,\n      enableDataStack: enableDataStack,\n      getStackedDimension: getStackedDimension\n    };\n    /**\n     * Create scale\n     * @param {Array.<number>} dataExtent\n     * @param {Object|module:echarts/Model} option If `optoin.type`\n     *        is secified, it can only be `'value'` currently.\n     */\n\n    function createScale(dataExtent, option) {\n      var axisModel = option;\n\n      if (!(option instanceof Model)) {\n        axisModel = new Model(option); // FIXME\n        // Currently AxisModelCommonMixin has nothing to do with the\n        // the requirements of `axisHelper.createScaleByModel`. For\n        // example the methods `getCategories` and `getOrdinalMeta`\n        // are required for `'category'` axis, and ecModel is required\n        // for `'time'` axis. But occasionally echarts-gl happened\n        // to only use `'value'` axis.\n        // zrUtil.mixin(axisModel, AxisModelCommonMixin);\n      }\n\n      var scale = createScaleByModel(axisModel);\n      scale.setExtent(dataExtent[0], dataExtent[1]);\n      niceScaleExtent(scale, axisModel);\n      return scale;\n    }\n    /**\n     * Mixin common methods to axis model,\n     *\n     * Include methods\n     * `getFormattedLabels() => Array.<string>`\n     * `getCategories() => Array.<string>`\n     * `getMin(origin: boolean) => number`\n     * `getMax(origin: boolean) => number`\n     * `getNeedCrossZero() => boolean`\n     */\n\n    function mixinAxisModelCommonMethods(Model) {\n      mixin(Model, AxisModelCommonMixin);\n    }\n    function createTextStyle$1(textStyleModel, opts) {\n      opts = opts || {};\n      return createTextStyle(textStyleModel, null, null, opts.state !== 'normal');\n    }\n\n    var helper = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        createList: createList,\n        getLayoutRect: getLayoutRect,\n        dataStack: dataStack$1,\n        createScale: createScale,\n        mixinAxisModelCommonMethods: mixinAxisModelCommonMethods,\n        getECData: getECData,\n        createTextStyle: createTextStyle$1,\n        createDimensions: createDimensions,\n        createSymbol: createSymbol,\n        enableHoverEmphasis: enableHoverEmphasis\n    });\n\n    var EPSILON$3 = 1e-8;\n    function isAroundEqual$1(a, b) {\n        return Math.abs(a - b) < EPSILON$3;\n    }\n    function contain$2(points, x, y) {\n        var w = 0;\n        var p = points[0];\n        if (!p) {\n            return false;\n        }\n        for (var i = 1; i < points.length; i++) {\n            var p2 = points[i];\n            w += windingLine(p[0], p[1], p2[0], p2[1], x, y);\n            p = p2;\n        }\n        var p0 = points[0];\n        if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) {\n            w += windingLine(p[0], p[1], p0[0], p0[1], x, y);\n        }\n        return w !== 0;\n    }\n\n    var TMP_TRANSFORM = [];\n\n    function transformPoints(points, transform) {\n      for (var p = 0; p < points.length; p++) {\n        applyTransform(points[p], points[p], transform);\n      }\n    }\n\n    function updateBBoxFromPoints(points, min$1, max$1, projection) {\n      for (var i = 0; i < points.length; i++) {\n        var p = points[i];\n\n        if (projection) {\n          // projection may return null point.\n          p = projection.project(p);\n        }\n\n        if (p && isFinite(p[0]) && isFinite(p[1])) {\n          min(min$1, min$1, p);\n          max(max$1, max$1, p);\n        }\n      }\n    }\n\n    function centroid(points) {\n      var signedArea = 0;\n      var cx = 0;\n      var cy = 0;\n      var len = points.length;\n      var x0 = points[len - 1][0];\n      var y0 = points[len - 1][1]; // Polygon should been closed.\n\n      for (var i = 0; i < len; i++) {\n        var x1 = points[i][0];\n        var y1 = points[i][1];\n        var a = x0 * y1 - x1 * y0;\n        signedArea += a;\n        cx += (x0 + x1) * a;\n        cy += (y0 + y1) * a;\n        x0 = x1;\n        y0 = y1;\n      }\n\n      return signedArea ? [cx / signedArea / 3, cy / signedArea / 3, signedArea] : [points[0][0] || 0, points[0][1] || 0];\n    }\n\n    var Region =\n    /** @class */\n    function () {\n      function Region(name) {\n        this.name = name;\n      }\n\n      Region.prototype.setCenter = function (center) {\n        this._center = center;\n      };\n      /**\n       * Get center point in data unit. That is,\n       * for GeoJSONRegion, the unit is lat/lng,\n       * for GeoSVGRegion, the unit is SVG local coord.\n       */\n\n\n      Region.prototype.getCenter = function () {\n        var center = this._center;\n\n        if (!center) {\n          // In most cases there are no need to calculate this center.\n          // So calculate only when called.\n          center = this._center = this.calcCenter();\n        }\n\n        return center;\n      };\n\n      return Region;\n    }();\n\n    var GeoJSONPolygonGeometry =\n    /** @class */\n    function () {\n      function GeoJSONPolygonGeometry(exterior, interiors) {\n        this.type = 'polygon';\n        this.exterior = exterior;\n        this.interiors = interiors;\n      }\n\n      return GeoJSONPolygonGeometry;\n    }();\n\n    var GeoJSONLineStringGeometry =\n    /** @class */\n    function () {\n      function GeoJSONLineStringGeometry(points) {\n        this.type = 'linestring';\n        this.points = points;\n      }\n\n      return GeoJSONLineStringGeometry;\n    }();\n\n    var GeoJSONRegion =\n    /** @class */\n    function (_super) {\n      __extends(GeoJSONRegion, _super);\n\n      function GeoJSONRegion(name, geometries, cp) {\n        var _this = _super.call(this, name) || this;\n\n        _this.type = 'geoJSON';\n        _this.geometries = geometries;\n        _this._center = cp && [cp[0], cp[1]];\n        return _this;\n      }\n\n      GeoJSONRegion.prototype.calcCenter = function () {\n        var geometries = this.geometries;\n        var largestGeo;\n        var largestGeoSize = 0;\n\n        for (var i = 0; i < geometries.length; i++) {\n          var geo = geometries[i];\n          var exterior = geo.exterior; // Simple trick to use points count instead of polygon area as region size.\n          // Ignore linestring\n\n          var size = exterior && exterior.length;\n\n          if (size > largestGeoSize) {\n            largestGeo = geo;\n            largestGeoSize = size;\n          }\n        }\n\n        if (largestGeo) {\n          return centroid(largestGeo.exterior);\n        } // from bounding rect by default.\n\n\n        var rect = this.getBoundingRect();\n        return [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      };\n\n      GeoJSONRegion.prototype.getBoundingRect = function (projection) {\n        var rect = this._rect; // Always recalculate if using projection.\n\n        if (rect && !projection) {\n          return rect;\n        }\n\n        var min = [Infinity, Infinity];\n        var max = [-Infinity, -Infinity];\n        var geometries = this.geometries;\n        each(geometries, function (geo) {\n          if (geo.type === 'polygon') {\n            // Doesn't consider hole\n            updateBBoxFromPoints(geo.exterior, min, max, projection);\n          } else {\n            each(geo.points, function (points) {\n              updateBBoxFromPoints(points, min, max, projection);\n            });\n          }\n        }); // Normalie invalid bounding.\n\n        if (!(isFinite(min[0]) && isFinite(min[1]) && isFinite(max[0]) && isFinite(max[1]))) {\n          min[0] = min[1] = max[0] = max[1] = 0;\n        }\n\n        rect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n\n        if (!projection) {\n          this._rect = rect;\n        }\n\n        return rect;\n      };\n\n      GeoJSONRegion.prototype.contain = function (coord) {\n        var rect = this.getBoundingRect();\n        var geometries = this.geometries;\n\n        if (!rect.contain(coord[0], coord[1])) {\n          return false;\n        }\n\n        loopGeo: for (var i = 0, len = geometries.length; i < len; i++) {\n          var geo = geometries[i]; // Only support polygon.\n\n          if (geo.type !== 'polygon') {\n            continue;\n          }\n\n          var exterior = geo.exterior;\n          var interiors = geo.interiors;\n\n          if (contain$2(exterior, coord[0], coord[1])) {\n            // Not in the region if point is in the hole.\n            for (var k = 0; k < (interiors ? interiors.length : 0); k++) {\n              if (contain$2(interiors[k], coord[0], coord[1])) {\n                continue loopGeo;\n              }\n            }\n\n            return true;\n          }\n        }\n\n        return false;\n      };\n      /**\n       * Transform the raw coords to target bounding.\n       * @param x\n       * @param y\n       * @param width\n       * @param height\n       */\n\n\n      GeoJSONRegion.prototype.transformTo = function (x, y, width, height) {\n        var rect = this.getBoundingRect();\n        var aspect = rect.width / rect.height;\n\n        if (!width) {\n          width = aspect * height;\n        } else if (!height) {\n          height = width / aspect;\n        }\n\n        var target = new BoundingRect(x, y, width, height);\n        var transform = rect.calculateTransform(target);\n        var geometries = this.geometries;\n\n        for (var i = 0; i < geometries.length; i++) {\n          var geo = geometries[i];\n\n          if (geo.type === 'polygon') {\n            transformPoints(geo.exterior, transform);\n            each(geo.interiors, function (interior) {\n              transformPoints(interior, transform);\n            });\n          } else {\n            each(geo.points, function (points) {\n              transformPoints(points, transform);\n            });\n          }\n        }\n\n        rect = this._rect;\n        rect.copy(target); // Update center\n\n        this._center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n      };\n\n      GeoJSONRegion.prototype.cloneShallow = function (name) {\n        name == null && (name = this.name);\n        var newRegion = new GeoJSONRegion(name, this.geometries, this._center);\n        newRegion._rect = this._rect;\n        newRegion.transformTo = null; // Simply avoid to be called.\n\n        return newRegion;\n      };\n\n      return GeoJSONRegion;\n    }(Region);\n\n    var GeoSVGRegion =\n    /** @class */\n    function (_super) {\n      __extends(GeoSVGRegion, _super);\n\n      function GeoSVGRegion(name, elOnlyForCalculate) {\n        var _this = _super.call(this, name) || this;\n\n        _this.type = 'geoSVG';\n        _this._elOnlyForCalculate = elOnlyForCalculate;\n        return _this;\n      }\n\n      GeoSVGRegion.prototype.calcCenter = function () {\n        var el = this._elOnlyForCalculate;\n        var rect = el.getBoundingRect();\n        var center = [rect.x + rect.width / 2, rect.y + rect.height / 2];\n        var mat = identity(TMP_TRANSFORM);\n        var target = el;\n\n        while (target && !target.isGeoSVGGraphicRoot) {\n          mul$1(mat, target.getLocalTransform(), mat);\n          target = target.parent;\n        }\n\n        invert(mat, mat);\n        applyTransform(center, center, mat);\n        return center;\n      };\n\n      return GeoSVGRegion;\n    }(Region);\n\n    function decode(json) {\n      if (!json.UTF8Encoding) {\n        return json;\n      }\n\n      var jsonCompressed = json;\n      var encodeScale = jsonCompressed.UTF8Scale;\n\n      if (encodeScale == null) {\n        encodeScale = 1024;\n      }\n\n      var features = jsonCompressed.features;\n      each(features, function (feature) {\n        var geometry = feature.geometry;\n        var encodeOffsets = geometry.encodeOffsets;\n        var coordinates = geometry.coordinates; // Geometry may be appeded manually in the script after json loaded.\n        // In this case this geometry is usually not encoded.\n\n        if (!encodeOffsets) {\n          return;\n        }\n\n        switch (geometry.type) {\n          case 'LineString':\n            geometry.coordinates = decodeRing(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'Polygon':\n            decodeRings(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'MultiLineString':\n            decodeRings(coordinates, encodeOffsets, encodeScale);\n            break;\n\n          case 'MultiPolygon':\n            each(coordinates, function (rings, idx) {\n              return decodeRings(rings, encodeOffsets[idx], encodeScale);\n            });\n        }\n      }); // Has been decoded\n\n      jsonCompressed.UTF8Encoding = false;\n      return jsonCompressed;\n    }\n\n    function decodeRings(rings, encodeOffsets, encodeScale) {\n      for (var c = 0; c < rings.length; c++) {\n        rings[c] = decodeRing(rings[c], encodeOffsets[c], encodeScale);\n      }\n    }\n\n    function decodeRing(coordinate, encodeOffsets, encodeScale) {\n      var result = [];\n      var prevX = encodeOffsets[0];\n      var prevY = encodeOffsets[1];\n\n      for (var i = 0; i < coordinate.length; i += 2) {\n        var x = coordinate.charCodeAt(i) - 64;\n        var y = coordinate.charCodeAt(i + 1) - 64; // ZigZag decoding\n\n        x = x >> 1 ^ -(x & 1);\n        y = y >> 1 ^ -(y & 1); // Delta deocding\n\n        x += prevX;\n        y += prevY;\n        prevX = x;\n        prevY = y; // Dequantize\n\n        result.push([x / encodeScale, y / encodeScale]);\n      }\n\n      return result;\n    }\n\n    function parseGeoJSON(geoJson, nameProperty) {\n      geoJson = decode(geoJson);\n      return map(filter(geoJson.features, function (featureObj) {\n        // Output of mapshaper may have geometry null\n        return featureObj.geometry && featureObj.properties && featureObj.geometry.coordinates.length > 0;\n      }), function (featureObj) {\n        var properties = featureObj.properties;\n        var geo = featureObj.geometry;\n        var geometries = [];\n\n        switch (geo.type) {\n          case 'Polygon':\n            var coordinates = geo.coordinates; // According to the GeoJSON specification.\n            // First must be exterior, and the rest are all interior(holes).\n\n            geometries.push(new GeoJSONPolygonGeometry(coordinates[0], coordinates.slice(1)));\n            break;\n\n          case 'MultiPolygon':\n            each(geo.coordinates, function (item) {\n              if (item[0]) {\n                geometries.push(new GeoJSONPolygonGeometry(item[0], item.slice(1)));\n              }\n            });\n            break;\n\n          case 'LineString':\n            geometries.push(new GeoJSONLineStringGeometry([geo.coordinates]));\n            break;\n\n          case 'MultiLineString':\n            geometries.push(new GeoJSONLineStringGeometry(geo.coordinates));\n        }\n\n        var region = new GeoJSONRegion(properties[nameProperty || 'name'], geometries, properties.cp);\n        region.properties = properties;\n        return region;\n      });\n    }\n\n    var number = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        linearMap: linearMap,\n        round: round,\n        asc: asc,\n        getPrecision: getPrecision,\n        getPrecisionSafe: getPrecisionSafe,\n        getPixelPrecision: getPixelPrecision,\n        getPercentWithPrecision: getPercentWithPrecision,\n        MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,\n        remRadian: remRadian,\n        isRadianAroundZero: isRadianAroundZero,\n        parseDate: parseDate,\n        quantity: quantity,\n        quantityExponent: quantityExponent,\n        nice: nice,\n        quantile: quantile,\n        reformIntervals: reformIntervals,\n        isNumeric: isNumeric,\n        numericToNumber: numericToNumber\n    });\n\n    var time = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        parse: parseDate,\n        format: format\n    });\n\n    var graphic = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        extendShape: extendShape,\n        extendPath: extendPath,\n        makePath: makePath,\n        makeImage: makeImage,\n        mergePath: mergePath$1,\n        resizePath: resizePath,\n        createIcon: createIcon,\n        updateProps: updateProps,\n        initProps: initProps,\n        getTransform: getTransform,\n        clipPointsByRect: clipPointsByRect,\n        clipRectByRect: clipRectByRect,\n        registerShape: registerShape,\n        getShapeClass: getShapeClass,\n        Group: Group,\n        Image: ZRImage,\n        Text: ZRText,\n        Circle: Circle,\n        Ellipse: Ellipse,\n        Sector: Sector,\n        Ring: Ring,\n        Polygon: Polygon,\n        Polyline: Polyline,\n        Rect: Rect,\n        Line: Line,\n        BezierCurve: BezierCurve,\n        Arc: Arc,\n        IncrementalDisplayable: IncrementalDisplayable,\n        CompoundPath: CompoundPath,\n        LinearGradient: LinearGradient,\n        RadialGradient: RadialGradient,\n        BoundingRect: BoundingRect\n    });\n\n    var format$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        addCommas: addCommas,\n        toCamelCase: toCamelCase,\n        normalizeCssArray: normalizeCssArray$1,\n        encodeHTML: encodeHTML,\n        formatTpl: formatTpl,\n        getTooltipMarker: getTooltipMarker,\n        formatTime: formatTime,\n        capitalFirst: capitalFirst,\n        truncateText: truncateText,\n        getTextRect: getTextRect\n    });\n\n    var util$1 = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        map: map,\n        each: each,\n        indexOf: indexOf,\n        inherits: inherits,\n        reduce: reduce,\n        filter: filter,\n        bind: bind,\n        curry: curry,\n        isArray: isArray,\n        isString: isString,\n        isObject: isObject,\n        isFunction: isFunction,\n        extend: extend,\n        defaults: defaults,\n        clone: clone,\n        merge: merge\n    });\n\n    var inner$5 = makeInner();\n    function createAxisLabels(axis) {\n      // Only ordinal scale support tick interval\n      return axis.type === 'category' ? makeCategoryLabels(axis) : makeRealNumberLabels(axis);\n    }\n    /**\n     * @param {module:echats/coord/Axis} axis\n     * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea.\n     * @return {Object} {\n     *     ticks: Array.<number>\n     *     tickCategoryInterval: number\n     * }\n     */\n\n    function createAxisTicks(axis, tickModel) {\n      // Only ordinal scale support tick interval\n      return axis.type === 'category' ? makeCategoryTicks(axis, tickModel) : {\n        ticks: map(axis.scale.getTicks(), function (tick) {\n          return tick.value;\n        })\n      };\n    }\n\n    function makeCategoryLabels(axis) {\n      var labelModel = axis.getLabelModel();\n      var result = makeCategoryLabelsActually(axis, labelModel);\n      return !labelModel.get('show') || axis.scale.isBlank() ? {\n        labels: [],\n        labelCategoryInterval: result.labelCategoryInterval\n      } : result;\n    }\n\n    function makeCategoryLabelsActually(axis, labelModel) {\n      var labelsCache = getListCache(axis, 'labels');\n      var optionLabelInterval = getOptionCategoryInterval(labelModel);\n      var result = listCacheGet(labelsCache, optionLabelInterval);\n\n      if (result) {\n        return result;\n      }\n\n      var labels;\n      var numericLabelInterval;\n\n      if (isFunction(optionLabelInterval)) {\n        labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval);\n      } else {\n        numericLabelInterval = optionLabelInterval === 'auto' ? makeAutoCategoryInterval(axis) : optionLabelInterval;\n        labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval);\n      } // Cache to avoid calling interval function repeatedly.\n\n\n      return listCacheSet(labelsCache, optionLabelInterval, {\n        labels: labels,\n        labelCategoryInterval: numericLabelInterval\n      });\n    }\n\n    function makeCategoryTicks(axis, tickModel) {\n      var ticksCache = getListCache(axis, 'ticks');\n      var optionTickInterval = getOptionCategoryInterval(tickModel);\n      var result = listCacheGet(ticksCache, optionTickInterval);\n\n      if (result) {\n        return result;\n      }\n\n      var ticks;\n      var tickCategoryInterval; // Optimize for the case that large category data and no label displayed,\n      // we should not return all ticks.\n\n      if (!tickModel.get('show') || axis.scale.isBlank()) {\n        ticks = [];\n      }\n\n      if (isFunction(optionTickInterval)) {\n        ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true);\n      } // Always use label interval by default despite label show. Consider this\n      // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows\n      // labels. `splitLine` and `axisTick` should be consistent in this case.\n      else if (optionTickInterval === 'auto') {\n          var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel());\n          tickCategoryInterval = labelsResult.labelCategoryInterval;\n          ticks = map(labelsResult.labels, function (labelItem) {\n            return labelItem.tickValue;\n          });\n        } else {\n          tickCategoryInterval = optionTickInterval;\n          ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true);\n        } // Cache to avoid calling interval function repeatedly.\n\n\n      return listCacheSet(ticksCache, optionTickInterval, {\n        ticks: ticks,\n        tickCategoryInterval: tickCategoryInterval\n      });\n    }\n\n    function makeRealNumberLabels(axis) {\n      var ticks = axis.scale.getTicks();\n      var labelFormatter = makeLabelFormatter(axis);\n      return {\n        labels: map(ticks, function (tick, idx) {\n          return {\n            level: tick.level,\n            formattedLabel: labelFormatter(tick, idx),\n            rawLabel: axis.scale.getLabel(tick),\n            tickValue: tick.value\n          };\n        })\n      };\n    }\n\n    function getListCache(axis, prop) {\n      // Because key can be a function, and cache size always is small, we use array cache.\n      return inner$5(axis)[prop] || (inner$5(axis)[prop] = []);\n    }\n\n    function listCacheGet(cache, key) {\n      for (var i = 0; i < cache.length; i++) {\n        if (cache[i].key === key) {\n          return cache[i].value;\n        }\n      }\n    }\n\n    function listCacheSet(cache, key, value) {\n      cache.push({\n        key: key,\n        value: value\n      });\n      return value;\n    }\n\n    function makeAutoCategoryInterval(axis) {\n      var result = inner$5(axis).autoInterval;\n      return result != null ? result : inner$5(axis).autoInterval = axis.calculateCategoryInterval();\n    }\n    /**\n     * Calculate interval for category axis ticks and labels.\n     * To get precise result, at least one of `getRotate` and `isHorizontal`\n     * should be implemented in axis.\n     */\n\n\n    function calculateCategoryInterval(axis) {\n      var params = fetchAutoCategoryIntervalCalculationParams(axis);\n      var labelFormatter = makeLabelFormatter(axis);\n      var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI;\n      var ordinalScale = axis.scale;\n      var ordinalExtent = ordinalScale.getExtent(); // Providing this method is for optimization:\n      // avoid generating a long array by `getTicks`\n      // in large category data case.\n\n      var tickCount = ordinalScale.count();\n\n      if (ordinalExtent[1] - ordinalExtent[0] < 1) {\n        return 0;\n      }\n\n      var step = 1; // Simple optimization. Empirical value: tick count should less than 40.\n\n      if (tickCount > 40) {\n        step = Math.max(1, Math.floor(tickCount / 40));\n      }\n\n      var tickValue = ordinalExtent[0];\n      var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);\n      var unitW = Math.abs(unitSpan * Math.cos(rotation));\n      var unitH = Math.abs(unitSpan * Math.sin(rotation));\n      var maxW = 0;\n      var maxH = 0; // Caution: Performance sensitive for large category data.\n      // Consider dataZoom, we should make appropriate step to avoid O(n) loop.\n\n      for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n        var width = 0;\n        var height = 0; // Not precise, do not consider align and vertical align\n        // and each distance from axis line yet.\n\n        var rect = getBoundingRect(labelFormatter({\n          value: tickValue\n        }), params.font, 'center', 'top'); // Magic number\n\n        width = rect.width * 1.3;\n        height = rect.height * 1.3; // Min size, void long loop.\n\n        maxW = Math.max(maxW, width, 7);\n        maxH = Math.max(maxH, height, 7);\n      }\n\n      var dw = maxW / unitW;\n      var dh = maxH / unitH; // 0/0 is NaN, 1/0 is Infinity.\n\n      isNaN(dw) && (dw = Infinity);\n      isNaN(dh) && (dh = Infinity);\n      var interval = Math.max(0, Math.floor(Math.min(dw, dh)));\n      var cache = inner$5(axis.model);\n      var axisExtent = axis.getExtent();\n      var lastAutoInterval = cache.lastAutoInterval;\n      var lastTickCount = cache.lastTickCount; // Use cache to keep interval stable while moving zoom window,\n      // otherwise the calculated interval might jitter when the zoom\n      // window size is close to the interval-changing size.\n      // For example, if all of the axis labels are `a, b, c, d, e, f, g`.\n      // The jitter will cause that sometimes the displayed labels are\n      // `a, d, g` (interval: 2) sometimes `a, c, e`(interval: 1).\n\n      if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 // Always choose the bigger one, otherwise the critical\n      // point is not the same when zooming in or zooming out.\n      && lastAutoInterval > interval // If the axis change is caused by chart resize, the cache should not\n      // be used. Otherwise some hidden labels might not be shown again.\n      && cache.axisExtent0 === axisExtent[0] && cache.axisExtent1 === axisExtent[1]) {\n        interval = lastAutoInterval;\n      } // Only update cache if cache not used, otherwise the\n      // changing of interval is too insensitive.\n      else {\n          cache.lastTickCount = tickCount;\n          cache.lastAutoInterval = interval;\n          cache.axisExtent0 = axisExtent[0];\n          cache.axisExtent1 = axisExtent[1];\n        }\n\n      return interval;\n    }\n\n    function fetchAutoCategoryIntervalCalculationParams(axis) {\n      var labelModel = axis.getLabelModel();\n      return {\n        axisRotate: axis.getRotate ? axis.getRotate() : axis.isHorizontal && !axis.isHorizontal() ? 90 : 0,\n        labelRotate: labelModel.get('rotate') || 0,\n        font: labelModel.getFont()\n      };\n    }\n\n    function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) {\n      var labelFormatter = makeLabelFormatter(axis);\n      var ordinalScale = axis.scale;\n      var ordinalExtent = ordinalScale.getExtent();\n      var labelModel = axis.getLabelModel();\n      var result = []; // TODO: axisType: ordinalTime, pick the tick from each month/day/year/...\n\n      var step = Math.max((categoryInterval || 0) + 1, 1);\n      var startTick = ordinalExtent[0];\n      var tickCount = ordinalScale.count(); // Calculate start tick based on zero if possible to keep label consistent\n      // while zooming and moving while interval > 0. Otherwise the selection\n      // of displayable ticks and symbols probably keep changing.\n      // 3 is empirical value.\n\n      if (startTick !== 0 && step > 1 && tickCount / step > 2) {\n        startTick = Math.round(Math.ceil(startTick / step) * step);\n      } // (1) Only add min max label here but leave overlap checking\n      // to render stage, which also ensure the returned list\n      // suitable for splitLine and splitArea rendering.\n      // (2) Scales except category always contain min max label so\n      // do not need to perform this process.\n\n\n      var showAllLabel = shouldShowAllLabels(axis);\n      var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel;\n      var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel;\n\n      if (includeMinLabel && startTick !== ordinalExtent[0]) {\n        addItem(ordinalExtent[0]);\n      } // Optimize: avoid generating large array by `ordinalScale.getTicks()`.\n\n\n      var tickValue = startTick;\n\n      for (; tickValue <= ordinalExtent[1]; tickValue += step) {\n        addItem(tickValue);\n      }\n\n      if (includeMaxLabel && tickValue - step !== ordinalExtent[1]) {\n        addItem(ordinalExtent[1]);\n      }\n\n      function addItem(tickValue) {\n        var tickObj = {\n          value: tickValue\n        };\n        result.push(onlyTick ? tickValue : {\n          formattedLabel: labelFormatter(tickObj),\n          rawLabel: ordinalScale.getLabel(tickObj),\n          tickValue: tickValue\n        });\n      }\n\n      return result;\n    }\n\n    function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) {\n      var ordinalScale = axis.scale;\n      var labelFormatter = makeLabelFormatter(axis);\n      var result = [];\n      each(ordinalScale.getTicks(), function (tick) {\n        var rawLabel = ordinalScale.getLabel(tick);\n        var tickValue = tick.value;\n\n        if (categoryInterval(tick.value, rawLabel)) {\n          result.push(onlyTick ? tickValue : {\n            formattedLabel: labelFormatter(tick),\n            rawLabel: rawLabel,\n            tickValue: tickValue\n          });\n        }\n      });\n      return result;\n    }\n\n    var NORMALIZED_EXTENT = [0, 1];\n    /**\n     * Base class of Axis.\n     */\n\n    var Axis =\n    /** @class */\n    function () {\n      function Axis(dim, scale, extent) {\n        this.onBand = false;\n        this.inverse = false;\n        this.dim = dim;\n        this.scale = scale;\n        this._extent = extent || [0, 0];\n      }\n      /**\n       * If axis extent contain given coord\n       */\n\n\n      Axis.prototype.contain = function (coord) {\n        var extent = this._extent;\n        var min = Math.min(extent[0], extent[1]);\n        var max = Math.max(extent[0], extent[1]);\n        return coord >= min && coord <= max;\n      };\n      /**\n       * If axis extent contain given data\n       */\n\n\n      Axis.prototype.containData = function (data) {\n        return this.scale.contain(data);\n      };\n      /**\n       * Get coord extent.\n       */\n\n\n      Axis.prototype.getExtent = function () {\n        return this._extent.slice();\n      };\n      /**\n       * Get precision used for formatting\n       */\n\n\n      Axis.prototype.getPixelPrecision = function (dataExtent) {\n        return getPixelPrecision(dataExtent || this.scale.getExtent(), this._extent);\n      };\n      /**\n       * Set coord extent\n       */\n\n\n      Axis.prototype.setExtent = function (start, end) {\n        var extent = this._extent;\n        extent[0] = start;\n        extent[1] = end;\n      };\n      /**\n       * Convert data to coord. Data is the rank if it has an ordinal scale\n       */\n\n\n      Axis.prototype.dataToCoord = function (data, clamp) {\n        var extent = this._extent;\n        var scale = this.scale;\n        data = scale.normalize(data);\n\n        if (this.onBand && scale.type === 'ordinal') {\n          extent = extent.slice();\n          fixExtentWithBands(extent, scale.count());\n        }\n\n        return linearMap(data, NORMALIZED_EXTENT, extent, clamp);\n      };\n      /**\n       * Convert coord to data. Data is the rank if it has an ordinal scale\n       */\n\n\n      Axis.prototype.coordToData = function (coord, clamp) {\n        var extent = this._extent;\n        var scale = this.scale;\n\n        if (this.onBand && scale.type === 'ordinal') {\n          extent = extent.slice();\n          fixExtentWithBands(extent, scale.count());\n        }\n\n        var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp);\n        return this.scale.scale(t);\n      };\n      /**\n       * Convert pixel point to data in axis\n       */\n\n\n      Axis.prototype.pointToData = function (point, clamp) {\n        // Should be implemented in derived class if necessary.\n        return;\n      };\n      /**\n       * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`,\n       * `axis.getTicksCoords` considers `onBand`, which is used by\n       * `boundaryGap:true` of category axis and splitLine and splitArea.\n       * @param opt.tickModel default: axis.model.getModel('axisTick')\n       * @param opt.clamp If `true`, the first and the last\n       *        tick must be at the axis end points. Otherwise, clip ticks\n       *        that outside the axis extent.\n       */\n\n\n      Axis.prototype.getTicksCoords = function (opt) {\n        opt = opt || {};\n        var tickModel = opt.tickModel || this.getTickModel();\n        var result = createAxisTicks(this, tickModel);\n        var ticks = result.ticks;\n        var ticksCoords = map(ticks, function (tickVal) {\n          return {\n            coord: this.dataToCoord(this.scale.type === 'ordinal' ? this.scale.getRawOrdinalNumber(tickVal) : tickVal),\n            tickValue: tickVal\n          };\n        }, this);\n        var alignWithLabel = tickModel.get('alignWithLabel');\n        fixOnBandTicksCoords(this, ticksCoords, alignWithLabel, opt.clamp);\n        return ticksCoords;\n      };\n\n      Axis.prototype.getMinorTicksCoords = function () {\n        if (this.scale.type === 'ordinal') {\n          // Category axis doesn't support minor ticks\n          return [];\n        }\n\n        var minorTickModel = this.model.getModel('minorTick');\n        var splitNumber = minorTickModel.get('splitNumber'); // Protection.\n\n        if (!(splitNumber > 0 && splitNumber < 100)) {\n          splitNumber = 5;\n        }\n\n        var minorTicks = this.scale.getMinorTicks(splitNumber);\n        var minorTicksCoords = map(minorTicks, function (minorTicksGroup) {\n          return map(minorTicksGroup, function (minorTick) {\n            return {\n              coord: this.dataToCoord(minorTick),\n              tickValue: minorTick\n            };\n          }, this);\n        }, this);\n        return minorTicksCoords;\n      };\n\n      Axis.prototype.getViewLabels = function () {\n        return createAxisLabels(this).labels;\n      };\n\n      Axis.prototype.getLabelModel = function () {\n        return this.model.getModel('axisLabel');\n      };\n      /**\n       * Notice here we only get the default tick model. For splitLine\n       * or splitArea, we should pass the splitLineModel or splitAreaModel\n       * manually when calling `getTicksCoords`.\n       * In GL, this method may be overridden to:\n       * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`\n       */\n\n\n      Axis.prototype.getTickModel = function () {\n        return this.model.getModel('axisTick');\n      };\n      /**\n       * Get width of band\n       */\n\n\n      Axis.prototype.getBandWidth = function () {\n        var axisExtent = this._extent;\n        var dataExtent = this.scale.getExtent();\n        var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); // Fix #2728, avoid NaN when only one data.\n\n        len === 0 && (len = 1);\n        var size = Math.abs(axisExtent[1] - axisExtent[0]);\n        return Math.abs(size) / len;\n      };\n      /**\n       * Only be called in category axis.\n       * Can be overridden, consider other axes like in 3D.\n       * @return Auto interval for cateogry axis tick and label\n       */\n\n\n      Axis.prototype.calculateCategoryInterval = function () {\n        return calculateCategoryInterval(this);\n      };\n\n      return Axis;\n    }();\n\n    function fixExtentWithBands(extent, nTick) {\n      var size = extent[1] - extent[0];\n      var len = nTick;\n      var margin = size / len / 2;\n      extent[0] += margin;\n      extent[1] -= margin;\n    } // If axis has labels [1, 2, 3, 4]. Bands on the axis are\n    // |---1---|---2---|---3---|---4---|.\n    // So the displayed ticks and splitLine/splitArea should between\n    // each data item, otherwise cause misleading (e.g., split tow bars\n    // of a single data item when there are two bar series).\n    // Also consider if tickCategoryInterval > 0 and onBand, ticks and\n    // splitLine/spliteArea should layout appropriately corresponding\n    // to displayed labels. (So we should not use `getBandWidth` in this\n    // case).\n\n\n    function fixOnBandTicksCoords(axis, ticksCoords, alignWithLabel, clamp) {\n      var ticksLen = ticksCoords.length;\n\n      if (!axis.onBand || alignWithLabel || !ticksLen) {\n        return;\n      }\n\n      var axisExtent = axis.getExtent();\n      var last;\n      var diffSize;\n\n      if (ticksLen === 1) {\n        ticksCoords[0].coord = axisExtent[0];\n        last = ticksCoords[1] = {\n          coord: axisExtent[0]\n        };\n      } else {\n        var crossLen = ticksCoords[ticksLen - 1].tickValue - ticksCoords[0].tickValue;\n        var shift_1 = (ticksCoords[ticksLen - 1].coord - ticksCoords[0].coord) / crossLen;\n        each(ticksCoords, function (ticksItem) {\n          ticksItem.coord -= shift_1 / 2;\n        });\n        var dataExtent = axis.scale.getExtent();\n        diffSize = 1 + dataExtent[1] - ticksCoords[ticksLen - 1].tickValue;\n        last = {\n          coord: ticksCoords[ticksLen - 1].coord + shift_1 * diffSize\n        };\n        ticksCoords.push(last);\n      }\n\n      var inverse = axisExtent[0] > axisExtent[1]; // Handling clamp.\n\n      if (littleThan(ticksCoords[0].coord, axisExtent[0])) {\n        clamp ? ticksCoords[0].coord = axisExtent[0] : ticksCoords.shift();\n      }\n\n      if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) {\n        ticksCoords.unshift({\n          coord: axisExtent[0]\n        });\n      }\n\n      if (littleThan(axisExtent[1], last.coord)) {\n        clamp ? last.coord = axisExtent[1] : ticksCoords.pop();\n      }\n\n      if (clamp && littleThan(last.coord, axisExtent[1])) {\n        ticksCoords.push({\n          coord: axisExtent[1]\n        });\n      }\n\n      function littleThan(a, b) {\n        // Avoid rounding error cause calculated tick coord different with extent.\n        // It may cause an extra unnecessary tick added.\n        a = round(a);\n        b = round(b);\n        return inverse ? a > b : a < b;\n      }\n    }\n\n    // Should use `ComponentModel.extend` or `class XXXX extend ComponentModel` to create class.\n    // Then use `registerComponentModel` in `install` parameter when `use` this extension. For example:\n    // class Bar3DModel extends ComponentModel {}\n    // export function install(registers) { registers.registerComponentModel(Bar3DModel); }\n    // echarts.use(install);\n\n    function extendComponentModel(proto) {\n      var Model = ComponentModel.extend(proto);\n      ComponentModel.registerClass(Model);\n      return Model;\n    }\n    function extendComponentView(proto) {\n      var View = ComponentView.extend(proto);\n      ComponentView.registerClass(View);\n      return View;\n    }\n    function extendSeriesModel(proto) {\n      var Model = SeriesModel.extend(proto);\n      SeriesModel.registerClass(Model);\n      return Model;\n    }\n    function extendChartView(proto) {\n      var View = ChartView.extend(proto);\n      ChartView.registerClass(View);\n      return View;\n    }\n\n    function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) {\n      var dx = x - x1;\n      var dy = y - y1;\n      var dx1 = x2 - x1;\n      var dy1 = y2 - y1;\n      var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n      dx1 /= lineLen;\n      dy1 /= lineLen; // dot product\n\n      var projectedLen = dx * dx1 + dy * dy1;\n      var t = projectedLen / lineLen;\n\n      if (limitToEnds) {\n        t = Math.min(Math.max(t, 0), 1);\n      }\n\n      t *= lineLen;\n      var ox = out[0] = x1 + t * dx1;\n      var oy = out[1] = y1 + t * dy1;\n      return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));\n    }\n\n\n    var pt0 = new Point();\n    var pt1 = new Point();\n    var pt2 = new Point();\n    var dir = new Point();\n    var dir2 = new Point();\n\n    var tmpArr = [];\n    var tmpProjPoint = new Point();\n    /**\n     * Reduce the line segment attached to the label to limit the turn angle between two segments.\n     * @param linePoints\n     * @param minTurnAngle Radian of minimum turn angle. 0 - 180\n     */\n\n    function limitTurnAngle(linePoints, minTurnAngle) {\n      if (!(minTurnAngle <= 180 && minTurnAngle > 0)) {\n        return;\n      }\n\n      minTurnAngle = minTurnAngle / 180 * Math.PI; // The line points can be\n      //      /pt1----pt2 (label)\n      //     /\n      // pt0/\n\n      pt0.fromArray(linePoints[0]);\n      pt1.fromArray(linePoints[1]);\n      pt2.fromArray(linePoints[2]);\n      Point.sub(dir, pt0, pt1);\n      Point.sub(dir2, pt2, pt1);\n      var len1 = dir.len();\n      var len2 = dir2.len();\n\n      if (len1 < 1e-3 || len2 < 1e-3) {\n        return;\n      }\n\n      dir.scale(1 / len1);\n      dir2.scale(1 / len2);\n      var angleCos = dir.dot(dir2);\n      var minTurnAngleCos = Math.cos(minTurnAngle);\n\n      if (minTurnAngleCos < angleCos) {\n        // Smaller than minTurnAngle\n        // Calculate project point of pt0 on pt1-pt2\n        var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n        tmpProjPoint.fromArray(tmpArr); // Calculate new projected length with limited minTurnAngle and get the new connect point\n\n        tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n        var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n        if (isNaN(t)) {\n          return;\n        }\n\n        if (t < 0) {\n          Point.copy(tmpProjPoint, pt1);\n        } else if (t > 1) {\n          Point.copy(tmpProjPoint, pt2);\n        }\n\n        tmpProjPoint.toArray(linePoints[1]);\n      }\n    }\n    /**\n     * Limit the angle of line and the surface\n     * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite\n     */\n\n    function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) {\n      if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {\n        return;\n      }\n\n      maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI;\n      pt0.fromArray(linePoints[0]);\n      pt1.fromArray(linePoints[1]);\n      pt2.fromArray(linePoints[2]);\n      Point.sub(dir, pt1, pt0);\n      Point.sub(dir2, pt2, pt1);\n      var len1 = dir.len();\n      var len2 = dir2.len();\n\n      if (len1 < 1e-3 || len2 < 1e-3) {\n        return;\n      }\n\n      dir.scale(1 / len1);\n      dir2.scale(1 / len2);\n      var angleCos = dir.dot(surfaceNormal);\n      var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle);\n\n      if (angleCos < maxSurfaceAngleCos) {\n        // Calculate project point of pt0 on pt1-pt2\n        var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);\n        tmpProjPoint.fromArray(tmpArr);\n        var HALF_PI = Math.PI / 2;\n        var angle2 = Math.acos(dir2.dot(surfaceNormal));\n        var newAngle = HALF_PI + angle2 - maxSurfaceAngle;\n\n        if (newAngle >= HALF_PI) {\n          // parallel\n          Point.copy(tmpProjPoint, pt2);\n        } else {\n          // Calculate new projected length with limited minTurnAngle and get the new connect point\n          tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); // Limit the new calculated connect point between pt1 and pt2.\n\n          var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);\n\n          if (isNaN(t)) {\n            return;\n          }\n\n          if (t < 0) {\n            Point.copy(tmpProjPoint, pt1);\n          } else if (t > 1) {\n            Point.copy(tmpProjPoint, pt2);\n          }\n        }\n\n        tmpProjPoint.toArray(linePoints[1]);\n      }\n    }\n\n    function setLabelLineState(labelLine, ignore, stateName, stateModel) {\n      var isNormal = stateName === 'normal';\n      var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); // Make sure display.\n\n      stateObj.ignore = ignore; // Set smooth\n\n      var smooth = stateModel.get('smooth');\n\n      if (smooth && smooth === true) {\n        smooth = 0.3;\n      }\n\n      stateObj.shape = stateObj.shape || {};\n\n      if (smooth > 0) {\n        stateObj.shape.smooth = smooth;\n      }\n\n      var styleObj = stateModel.getModel('lineStyle').getLineStyle();\n      isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj;\n    }\n\n    function buildLabelLinePath(path, shape) {\n      var smooth = shape.smooth;\n      var points = shape.points;\n\n      if (!points) {\n        return;\n      }\n\n      path.moveTo(points[0][0], points[0][1]);\n\n      if (smooth > 0 && points.length >= 3) {\n        var len1 = dist(points[0], points[1]);\n        var len2 = dist(points[1], points[2]);\n\n        if (!len1 || !len2) {\n          path.lineTo(points[1][0], points[1][1]);\n          path.lineTo(points[2][0], points[2][1]);\n          return;\n        }\n\n        var moveLen = Math.min(len1, len2) * smooth;\n        var midPoint0 = lerp([], points[1], points[0], moveLen / len1);\n        var midPoint2 = lerp([], points[1], points[2], moveLen / len2);\n        var midPoint1 = lerp([], midPoint0, midPoint2, 0.5);\n        path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]);\n        path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]);\n      } else {\n        for (var i = 1; i < points.length; i++) {\n          path.lineTo(points[i][0], points[i][1]);\n        }\n      }\n    }\n    /**\n     * Create a label line if necessary and set it's style.\n     */\n\n\n    function setLabelLineStyle(targetEl, statesModels, defaultStyle) {\n      var labelLine = targetEl.getTextGuideLine();\n      var label = targetEl.getTextContent();\n\n      if (!label) {\n        // Not show label line if there is no label.\n        if (labelLine) {\n          targetEl.removeTextGuideLine();\n        }\n\n        return;\n      }\n\n      var normalModel = statesModels.normal;\n      var showNormal = normalModel.get('show');\n      var labelIgnoreNormal = label.ignore;\n\n      for (var i = 0; i < DISPLAY_STATES.length; i++) {\n        var stateName = DISPLAY_STATES[i];\n        var stateModel = statesModels[stateName];\n        var isNormal = stateName === 'normal';\n\n        if (stateModel) {\n          var stateShow = stateModel.get('show');\n          var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal);\n\n          if (isLabelIgnored // Not show when label is not shown in this state.\n          || !retrieve2(stateShow, showNormal) // Use normal state by default if not set.\n          ) {\n              var stateObj = isNormal ? labelLine : labelLine && labelLine.states[stateName];\n\n              if (stateObj) {\n                stateObj.ignore = true;\n              }\n\n              continue;\n            } // Create labelLine if not exists\n\n\n          if (!labelLine) {\n            labelLine = new Polyline();\n            targetEl.setTextGuideLine(labelLine); // Reset state of normal because it's new created.\n            // NOTE: NORMAL should always been the first!\n\n            if (!isNormal && (labelIgnoreNormal || !showNormal)) {\n              setLabelLineState(labelLine, true, 'normal', statesModels.normal);\n            } // Use same state proxy.\n\n\n            if (targetEl.stateProxy) {\n              labelLine.stateProxy = targetEl.stateProxy;\n            }\n          }\n\n          setLabelLineState(labelLine, false, stateName, stateModel);\n        }\n      }\n\n      if (labelLine) {\n        defaults(labelLine.style, defaultStyle); // Not fill.\n\n        labelLine.style.fill = null;\n        var showAbove = normalModel.get('showAbove');\n        var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {};\n        labelLineConfig.showAbove = showAbove || false; // Custom the buildPath.\n\n        labelLine.buildPath = buildLabelLinePath;\n      }\n    }\n    function getLabelLineStatesModels(itemModel, labelLineName) {\n      labelLineName = labelLineName || 'labelLine';\n      var statesModels = {\n        normal: itemModel.getModel(labelLineName)\n      };\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        var stateName = SPECIAL_STATES[i];\n        statesModels[stateName] = itemModel.getModel([stateName, labelLineName]);\n      }\n\n      return statesModels;\n    }\n\n    function prepareLayoutList(input) {\n      var list = [];\n\n      for (var i = 0; i < input.length; i++) {\n        var rawItem = input[i];\n\n        if (rawItem.defaultAttr.ignore) {\n          continue;\n        }\n\n        var label = rawItem.label;\n        var transform = label.getComputedTransform(); // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el.\n\n        var localRect = label.getBoundingRect();\n        var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5;\n        var minMargin = label.style.margin || 0;\n        var globalRect = localRect.clone();\n        globalRect.applyTransform(transform);\n        globalRect.x -= minMargin / 2;\n        globalRect.y -= minMargin / 2;\n        globalRect.width += minMargin;\n        globalRect.height += minMargin;\n        var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null;\n        list.push({\n          label: label,\n          labelLine: rawItem.labelLine,\n          rect: globalRect,\n          localRect: localRect,\n          obb: obb,\n          priority: rawItem.priority,\n          defaultAttr: rawItem.defaultAttr,\n          layoutOption: rawItem.computedLayoutOption,\n          axisAligned: isAxisAligned,\n          transform: transform\n        });\n      }\n\n      return list;\n    }\n\n    function shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) {\n      var len = list.length;\n\n      if (len < 2) {\n        return;\n      }\n\n      list.sort(function (a, b) {\n        return a.rect[xyDim] - b.rect[xyDim];\n      });\n      var lastPos = 0;\n      var delta;\n      var adjusted = false;\n      var totalShifts = 0;\n\n      for (var i = 0; i < len; i++) {\n        var item = list[i];\n        var rect = item.rect;\n        delta = rect[xyDim] - lastPos;\n\n        if (delta < 0) {\n          // shiftForward(i, len, -delta);\n          rect[xyDim] -= delta;\n          item.label[xyDim] -= delta;\n          adjusted = true;\n        }\n\n        var shift = Math.max(-delta, 0);\n        totalShifts += shift;\n        lastPos = rect[xyDim] + rect[sizeDim];\n      }\n\n      if (totalShifts > 0 && balanceShift) {\n        // Shift back to make the distribution more equally.\n        shiftList(-totalShifts / len, 0, len);\n      } // TODO bleedMargin?\n\n\n      var first = list[0];\n      var last = list[len - 1];\n      var minGap;\n      var maxGap;\n      updateMinMaxGap(); // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds.\n\n      minGap < 0 && squeezeGaps(-minGap, 0.8);\n      maxGap < 0 && squeezeGaps(maxGap, 0.8);\n      updateMinMaxGap();\n      takeBoundsGap(minGap, maxGap, 1);\n      takeBoundsGap(maxGap, minGap, -1); // Handle bailout when there is not enough space.\n\n      updateMinMaxGap();\n\n      if (minGap < 0) {\n        squeezeWhenBailout(-minGap);\n      }\n\n      if (maxGap < 0) {\n        squeezeWhenBailout(maxGap);\n      }\n\n      function updateMinMaxGap() {\n        minGap = first.rect[xyDim] - minBound;\n        maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim];\n      }\n\n      function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) {\n        if (gapThisBound < 0) {\n          // Move from other gap if can.\n          var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound);\n\n          if (moveFromMaxGap > 0) {\n            shiftList(moveFromMaxGap * moveDir, 0, len);\n            var remained = moveFromMaxGap + gapThisBound;\n\n            if (remained < 0) {\n              squeezeGaps(-remained * moveDir, 1);\n            }\n          } else {\n            squeezeGaps(-gapThisBound * moveDir, 1);\n          }\n        }\n      }\n\n      function shiftList(delta, start, end) {\n        if (delta !== 0) {\n          adjusted = true;\n        }\n\n        for (var i = start; i < end; i++) {\n          var item = list[i];\n          var rect = item.rect;\n          rect[xyDim] += delta;\n          item.label[xyDim] += delta;\n        }\n      } // Squeeze gaps if the labels exceed margin.\n\n\n      function squeezeGaps(delta, maxSqeezePercent) {\n        var gaps = [];\n        var totalGaps = 0;\n\n        for (var i = 1; i < len; i++) {\n          var prevItemRect = list[i - 1].rect;\n          var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0);\n          gaps.push(gap);\n          totalGaps += gap;\n        }\n\n        if (!totalGaps) {\n          return;\n        }\n\n        var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent);\n\n        if (delta > 0) {\n          for (var i = 0; i < len - 1; i++) {\n            // Distribute the shift delta to all gaps.\n            var movement = gaps[i] * squeezePercent; // Forward\n\n            shiftList(movement, 0, i + 1);\n          }\n        } else {\n          // Backward\n          for (var i = len - 1; i > 0; i--) {\n            // Distribute the shift delta to all gaps.\n            var movement = gaps[i - 1] * squeezePercent;\n            shiftList(-movement, i, len);\n          }\n        }\n      }\n      /**\n       * Squeeze to allow overlap if there is no more space available.\n       * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds.\n       */\n\n\n      function squeezeWhenBailout(delta) {\n        var dir = delta < 0 ? -1 : 1;\n        delta = Math.abs(delta);\n        var moveForEachLabel = Math.ceil(delta / (len - 1));\n\n        for (var i = 0; i < len - 1; i++) {\n          if (dir > 0) {\n            // Forward\n            shiftList(moveForEachLabel, 0, i + 1);\n          } else {\n            // Backward\n            shiftList(-moveForEachLabel, len - i - 1, len);\n          }\n\n          delta -= moveForEachLabel;\n\n          if (delta <= 0) {\n            return;\n          }\n        }\n      }\n\n      return adjusted;\n    }\n    /**\n     * Adjust labels on y direction to avoid overlap.\n     */\n\n    function shiftLayoutOnY(list, topBound, bottomBound, // If average the shifts on all labels and add them to 0\n    balanceShift) {\n      return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift);\n    }\n    function hideOverlap(labelList) {\n      var displayedLabels = []; // TODO, render overflow visible first, put in the displayedLabels.\n\n      labelList.sort(function (a, b) {\n        return b.priority - a.priority;\n      });\n      var globalRect = new BoundingRect(0, 0, 0, 0);\n\n      function hideEl(el) {\n        if (!el.ignore) {\n          // Show on emphasis.\n          var emphasisState = el.ensureState('emphasis');\n\n          if (emphasisState.ignore == null) {\n            emphasisState.ignore = false;\n          }\n        }\n\n        el.ignore = true;\n      }\n\n      for (var i = 0; i < labelList.length; i++) {\n        var labelItem = labelList[i];\n        var isAxisAligned = labelItem.axisAligned;\n        var localRect = labelItem.localRect;\n        var transform = labelItem.transform;\n        var label = labelItem.label;\n        var labelLine = labelItem.labelLine;\n        globalRect.copy(labelItem.rect); // Add a threshold because layout may be aligned precisely.\n\n        globalRect.width -= 0.1;\n        globalRect.height -= 0.1;\n        globalRect.x += 0.05;\n        globalRect.y += 0.05;\n        var obb = labelItem.obb;\n        var overlapped = false;\n\n        for (var j = 0; j < displayedLabels.length; j++) {\n          var existsTextCfg = displayedLabels[j]; // Fast rejection.\n\n          if (!globalRect.intersect(existsTextCfg.rect)) {\n            continue;\n          }\n\n          if (isAxisAligned && existsTextCfg.axisAligned) {\n            // Is overlapped\n            overlapped = true;\n            break;\n          }\n\n          if (!existsTextCfg.obb) {\n            // If self is not axis aligned. But other is.\n            existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform);\n          }\n\n          if (!obb) {\n            // If self is axis aligned. But other is not.\n            obb = new OrientedBoundingRect(localRect, transform);\n          }\n\n          if (obb.intersect(existsTextCfg.obb)) {\n            overlapped = true;\n            break;\n          }\n        } // TODO Callback to determine if this overlap should be handled?\n\n\n        if (overlapped) {\n          hideEl(label);\n          labelLine && hideEl(labelLine);\n        } else {\n          label.attr('ignore', labelItem.defaultAttr.ignore);\n          labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore);\n          displayedLabels.push(labelItem);\n        }\n      }\n    }\n\n    function createDom(id, painter, dpr) {\n        var newDom = platformApi.createCanvas();\n        var width = painter.getWidth();\n        var height = painter.getHeight();\n        var newDomStyle = newDom.style;\n        if (newDomStyle) {\n            newDomStyle.position = 'absolute';\n            newDomStyle.left = '0';\n            newDomStyle.top = '0';\n            newDomStyle.width = width + 'px';\n            newDomStyle.height = height + 'px';\n            newDom.setAttribute('data-zr-dom-id', id);\n        }\n        newDom.width = width * dpr;\n        newDom.height = height * dpr;\n        return newDom;\n    }\n    var Layer = (function (_super) {\n        __extends(Layer, _super);\n        function Layer(id, painter, dpr) {\n            var _this = _super.call(this) || this;\n            _this.motionBlur = false;\n            _this.lastFrameAlpha = 0.7;\n            _this.dpr = 1;\n            _this.virtual = false;\n            _this.config = {};\n            _this.incremental = false;\n            _this.zlevel = 0;\n            _this.maxRepaintRectCount = 5;\n            _this.__dirty = true;\n            _this.__firstTimePaint = true;\n            _this.__used = false;\n            _this.__drawIndex = 0;\n            _this.__startIndex = 0;\n            _this.__endIndex = 0;\n            _this.__prevStartIndex = null;\n            _this.__prevEndIndex = null;\n            var dom;\n            dpr = dpr || devicePixelRatio;\n            if (typeof id === 'string') {\n                dom = createDom(id, painter, dpr);\n            }\n            else if (isObject(id)) {\n                dom = id;\n                id = dom.id;\n            }\n            _this.id = id;\n            _this.dom = dom;\n            var domStyle = dom.style;\n            if (domStyle) {\n                disableUserSelect(dom);\n                dom.onselectstart = function () { return false; };\n                domStyle.padding = '0';\n                domStyle.margin = '0';\n                domStyle.borderWidth = '0';\n            }\n            _this.painter = painter;\n            _this.dpr = dpr;\n            return _this;\n        }\n        Layer.prototype.getElementCount = function () {\n            return this.__endIndex - this.__startIndex;\n        };\n        Layer.prototype.afterBrush = function () {\n            this.__prevStartIndex = this.__startIndex;\n            this.__prevEndIndex = this.__endIndex;\n        };\n        Layer.prototype.initContext = function () {\n            this.ctx = this.dom.getContext('2d');\n            this.ctx.dpr = this.dpr;\n        };\n        Layer.prototype.setUnpainted = function () {\n            this.__firstTimePaint = true;\n        };\n        Layer.prototype.createBackBuffer = function () {\n            var dpr = this.dpr;\n            this.domBack = createDom('back-' + this.id, this.painter, dpr);\n            this.ctxBack = this.domBack.getContext('2d');\n            if (dpr !== 1) {\n                this.ctxBack.scale(dpr, dpr);\n            }\n        };\n        Layer.prototype.createRepaintRects = function (displayList, prevList, viewWidth, viewHeight) {\n            if (this.__firstTimePaint) {\n                this.__firstTimePaint = false;\n                return null;\n            }\n            var mergedRepaintRects = [];\n            var maxRepaintRectCount = this.maxRepaintRectCount;\n            var full = false;\n            var pendingRect = new BoundingRect(0, 0, 0, 0);\n            function addRectToMergePool(rect) {\n                if (!rect.isFinite() || rect.isZero()) {\n                    return;\n                }\n                if (mergedRepaintRects.length === 0) {\n                    var boundingRect = new BoundingRect(0, 0, 0, 0);\n                    boundingRect.copy(rect);\n                    mergedRepaintRects.push(boundingRect);\n                }\n                else {\n                    var isMerged = false;\n                    var minDeltaArea = Infinity;\n                    var bestRectToMergeIdx = 0;\n                    for (var i = 0; i < mergedRepaintRects.length; ++i) {\n                        var mergedRect = mergedRepaintRects[i];\n                        if (mergedRect.intersect(rect)) {\n                            var pendingRect_1 = new BoundingRect(0, 0, 0, 0);\n                            pendingRect_1.copy(mergedRect);\n                            pendingRect_1.union(rect);\n                            mergedRepaintRects[i] = pendingRect_1;\n                            isMerged = true;\n                            break;\n                        }\n                        else if (full) {\n                            pendingRect.copy(rect);\n                            pendingRect.union(mergedRect);\n                            var aArea = rect.width * rect.height;\n                            var bArea = mergedRect.width * mergedRect.height;\n                            var pendingArea = pendingRect.width * pendingRect.height;\n                            var deltaArea = pendingArea - aArea - bArea;\n                            if (deltaArea < minDeltaArea) {\n                                minDeltaArea = deltaArea;\n                                bestRectToMergeIdx = i;\n                            }\n                        }\n                    }\n                    if (full) {\n                        mergedRepaintRects[bestRectToMergeIdx].union(rect);\n                        isMerged = true;\n                    }\n                    if (!isMerged) {\n                        var boundingRect = new BoundingRect(0, 0, 0, 0);\n                        boundingRect.copy(rect);\n                        mergedRepaintRects.push(boundingRect);\n                    }\n                    if (!full) {\n                        full = mergedRepaintRects.length >= maxRepaintRectCount;\n                    }\n                }\n            }\n            for (var i = this.__startIndex; i < this.__endIndex; ++i) {\n                var el = displayList[i];\n                if (el) {\n                    var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n                    var prevRect = el.__isRendered && ((el.__dirty & REDRAW_BIT) || !shouldPaint)\n                        ? el.getPrevPaintRect()\n                        : null;\n                    if (prevRect) {\n                        addRectToMergePool(prevRect);\n                    }\n                    var curRect = shouldPaint && ((el.__dirty & REDRAW_BIT) || !el.__isRendered)\n                        ? el.getPaintRect()\n                        : null;\n                    if (curRect) {\n                        addRectToMergePool(curRect);\n                    }\n                }\n            }\n            for (var i = this.__prevStartIndex; i < this.__prevEndIndex; ++i) {\n                var el = prevList[i];\n                var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true);\n                if (el && (!shouldPaint || !el.__zr) && el.__isRendered) {\n                    var prevRect = el.getPrevPaintRect();\n                    if (prevRect) {\n                        addRectToMergePool(prevRect);\n                    }\n                }\n            }\n            var hasIntersections;\n            do {\n                hasIntersections = false;\n                for (var i = 0; i < mergedRepaintRects.length;) {\n                    if (mergedRepaintRects[i].isZero()) {\n                        mergedRepaintRects.splice(i, 1);\n                        continue;\n                    }\n                    for (var j = i + 1; j < mergedRepaintRects.length;) {\n                        if (mergedRepaintRects[i].intersect(mergedRepaintRects[j])) {\n                            hasIntersections = true;\n                            mergedRepaintRects[i].union(mergedRepaintRects[j]);\n                            mergedRepaintRects.splice(j, 1);\n                        }\n                        else {\n                            j++;\n                        }\n                    }\n                    i++;\n                }\n            } while (hasIntersections);\n            this._paintRects = mergedRepaintRects;\n            return mergedRepaintRects;\n        };\n        Layer.prototype.debugGetPaintRects = function () {\n            return (this._paintRects || []).slice();\n        };\n        Layer.prototype.resize = function (width, height) {\n            var dpr = this.dpr;\n            var dom = this.dom;\n            var domStyle = dom.style;\n            var domBack = this.domBack;\n            if (domStyle) {\n                domStyle.width = width + 'px';\n                domStyle.height = height + 'px';\n            }\n            dom.width = width * dpr;\n            dom.height = height * dpr;\n            if (domBack) {\n                domBack.width = width * dpr;\n                domBack.height = height * dpr;\n                if (dpr !== 1) {\n                    this.ctxBack.scale(dpr, dpr);\n                }\n            }\n        };\n        Layer.prototype.clear = function (clearAll, clearColor, repaintRects) {\n            var dom = this.dom;\n            var ctx = this.ctx;\n            var width = dom.width;\n            var height = dom.height;\n            clearColor = clearColor || this.clearColor;\n            var haveMotionBLur = this.motionBlur && !clearAll;\n            var lastFrameAlpha = this.lastFrameAlpha;\n            var dpr = this.dpr;\n            var self = this;\n            if (haveMotionBLur) {\n                if (!this.domBack) {\n                    this.createBackBuffer();\n                }\n                this.ctxBack.globalCompositeOperation = 'copy';\n                this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr);\n            }\n            var domBack = this.domBack;\n            function doClear(x, y, width, height) {\n                ctx.clearRect(x, y, width, height);\n                if (clearColor && clearColor !== 'transparent') {\n                    var clearColorGradientOrPattern = void 0;\n                    if (isGradientObject(clearColor)) {\n                        var shouldCache = clearColor.global || (clearColor.__width === width\n                            && clearColor.__height === height);\n                        clearColorGradientOrPattern = shouldCache\n                            && clearColor.__canvasGradient\n                            || getCanvasGradient(ctx, clearColor, {\n                                x: 0,\n                                y: 0,\n                                width: width,\n                                height: height\n                            });\n                        clearColor.__canvasGradient = clearColorGradientOrPattern;\n                        clearColor.__width = width;\n                        clearColor.__height = height;\n                    }\n                    else if (isImagePatternObject(clearColor)) {\n                        clearColor.scaleX = clearColor.scaleX || dpr;\n                        clearColor.scaleY = clearColor.scaleY || dpr;\n                        clearColorGradientOrPattern = createCanvasPattern(ctx, clearColor, {\n                            dirty: function () {\n                                self.setUnpainted();\n                                self.__painter.refresh();\n                            }\n                        });\n                    }\n                    ctx.save();\n                    ctx.fillStyle = clearColorGradientOrPattern || clearColor;\n                    ctx.fillRect(x, y, width, height);\n                    ctx.restore();\n                }\n                if (haveMotionBLur) {\n                    ctx.save();\n                    ctx.globalAlpha = lastFrameAlpha;\n                    ctx.drawImage(domBack, x, y, width, height);\n                    ctx.restore();\n                }\n            }\n            if (!repaintRects || haveMotionBLur) {\n                doClear(0, 0, width, height);\n            }\n            else if (repaintRects.length) {\n                each(repaintRects, function (rect) {\n                    doClear(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n                });\n            }\n        };\n        return Layer;\n    }(Eventful));\n\n    var HOVER_LAYER_ZLEVEL = 1e5;\n    var CANVAS_ZLEVEL = 314159;\n    var EL_AFTER_INCREMENTAL_INC = 0.01;\n    var INCREMENTAL_INC = 0.001;\n    function isLayerValid(layer) {\n        if (!layer) {\n            return false;\n        }\n        if (layer.__builtin__) {\n            return true;\n        }\n        if (typeof (layer.resize) !== 'function'\n            || typeof (layer.refresh) !== 'function') {\n            return false;\n        }\n        return true;\n    }\n    function createRoot(width, height) {\n        var domRoot = document.createElement('div');\n        domRoot.style.cssText = [\n            'position:relative',\n            'width:' + width + 'px',\n            'height:' + height + 'px',\n            'padding:0',\n            'margin:0',\n            'border-width:0'\n        ].join(';') + ';';\n        return domRoot;\n    }\n    var CanvasPainter = (function () {\n        function CanvasPainter(root, storage, opts, id) {\n            this.type = 'canvas';\n            this._zlevelList = [];\n            this._prevDisplayList = [];\n            this._layers = {};\n            this._layerConfig = {};\n            this._needsManuallyCompositing = false;\n            this.type = 'canvas';\n            var singleCanvas = !root.nodeName\n                || root.nodeName.toUpperCase() === 'CANVAS';\n            this._opts = opts = extend({}, opts || {});\n            this.dpr = opts.devicePixelRatio || devicePixelRatio;\n            this._singleCanvas = singleCanvas;\n            this.root = root;\n            var rootStyle = root.style;\n            if (rootStyle) {\n                disableUserSelect(root);\n                root.innerHTML = '';\n            }\n            this.storage = storage;\n            var zlevelList = this._zlevelList;\n            this._prevDisplayList = [];\n            var layers = this._layers;\n            if (!singleCanvas) {\n                this._width = getSize(root, 0, opts);\n                this._height = getSize(root, 1, opts);\n                var domRoot = this._domRoot = createRoot(this._width, this._height);\n                root.appendChild(domRoot);\n            }\n            else {\n                var rootCanvas = root;\n                var width = rootCanvas.width;\n                var height = rootCanvas.height;\n                if (opts.width != null) {\n                    width = opts.width;\n                }\n                if (opts.height != null) {\n                    height = opts.height;\n                }\n                this.dpr = opts.devicePixelRatio || 1;\n                rootCanvas.width = width * this.dpr;\n                rootCanvas.height = height * this.dpr;\n                this._width = width;\n                this._height = height;\n                var mainLayer = new Layer(rootCanvas, this, this.dpr);\n                mainLayer.__builtin__ = true;\n                mainLayer.initContext();\n                layers[CANVAS_ZLEVEL] = mainLayer;\n                mainLayer.zlevel = CANVAS_ZLEVEL;\n                zlevelList.push(CANVAS_ZLEVEL);\n                this._domRoot = root;\n            }\n        }\n        CanvasPainter.prototype.getType = function () {\n            return 'canvas';\n        };\n        CanvasPainter.prototype.isSingleCanvas = function () {\n            return this._singleCanvas;\n        };\n        CanvasPainter.prototype.getViewportRoot = function () {\n            return this._domRoot;\n        };\n        CanvasPainter.prototype.getViewportRootOffset = function () {\n            var viewportRoot = this.getViewportRoot();\n            if (viewportRoot) {\n                return {\n                    offsetLeft: viewportRoot.offsetLeft || 0,\n                    offsetTop: viewportRoot.offsetTop || 0\n                };\n            }\n        };\n        CanvasPainter.prototype.refresh = function (paintAll) {\n            var list = this.storage.getDisplayList(true);\n            var prevList = this._prevDisplayList;\n            var zlevelList = this._zlevelList;\n            this._redrawId = Math.random();\n            this._paintList(list, prevList, paintAll, this._redrawId);\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (!layer.__builtin__ && layer.refresh) {\n                    var clearColor = i === 0 ? this._backgroundColor : null;\n                    layer.refresh(clearColor);\n                }\n            }\n            if (this._opts.useDirtyRect) {\n                this._prevDisplayList = list.slice();\n            }\n            return this;\n        };\n        CanvasPainter.prototype.refreshHover = function () {\n            this._paintHoverList(this.storage.getDisplayList(false));\n        };\n        CanvasPainter.prototype._paintHoverList = function (list) {\n            var len = list.length;\n            var hoverLayer = this._hoverlayer;\n            hoverLayer && hoverLayer.clear();\n            if (!len) {\n                return;\n            }\n            var scope = {\n                inHover: true,\n                viewWidth: this._width,\n                viewHeight: this._height\n            };\n            var ctx;\n            for (var i = 0; i < len; i++) {\n                var el = list[i];\n                if (el.__inHover) {\n                    if (!hoverLayer) {\n                        hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);\n                    }\n                    if (!ctx) {\n                        ctx = hoverLayer.ctx;\n                        ctx.save();\n                    }\n                    brush(ctx, el, scope, i === len - 1);\n                }\n            }\n            if (ctx) {\n                ctx.restore();\n            }\n        };\n        CanvasPainter.prototype.getHoverLayer = function () {\n            return this.getLayer(HOVER_LAYER_ZLEVEL);\n        };\n        CanvasPainter.prototype.paintOne = function (ctx, el) {\n            brushSingle(ctx, el);\n        };\n        CanvasPainter.prototype._paintList = function (list, prevList, paintAll, redrawId) {\n            if (this._redrawId !== redrawId) {\n                return;\n            }\n            paintAll = paintAll || false;\n            this._updateLayerStatus(list);\n            var _a = this._doPaintList(list, prevList, paintAll), finished = _a.finished, needsRefreshHover = _a.needsRefreshHover;\n            if (this._needsManuallyCompositing) {\n                this._compositeManually();\n            }\n            if (needsRefreshHover) {\n                this._paintHoverList(list);\n            }\n            if (!finished) {\n                var self_1 = this;\n                requestAnimationFrame$1(function () {\n                    self_1._paintList(list, prevList, paintAll, redrawId);\n                });\n            }\n            else {\n                this.eachLayer(function (layer) {\n                    layer.afterBrush && layer.afterBrush();\n                });\n            }\n        };\n        CanvasPainter.prototype._compositeManually = function () {\n            var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;\n            var width = this._domRoot.width;\n            var height = this._domRoot.height;\n            ctx.clearRect(0, 0, width, height);\n            this.eachBuiltinLayer(function (layer) {\n                if (layer.virtual) {\n                    ctx.drawImage(layer.dom, 0, 0, width, height);\n                }\n            });\n        };\n        CanvasPainter.prototype._doPaintList = function (list, prevList, paintAll) {\n            var _this = this;\n            var layerList = [];\n            var useDirtyRect = this._opts.useDirtyRect;\n            for (var zi = 0; zi < this._zlevelList.length; zi++) {\n                var zlevel = this._zlevelList[zi];\n                var layer = this._layers[zlevel];\n                if (layer.__builtin__\n                    && layer !== this._hoverlayer\n                    && (layer.__dirty || paintAll)) {\n                    layerList.push(layer);\n                }\n            }\n            var finished = true;\n            var needsRefreshHover = false;\n            var _loop_1 = function (k) {\n                var layer = layerList[k];\n                var ctx = layer.ctx;\n                var repaintRects = useDirtyRect\n                    && layer.createRepaintRects(list, prevList, this_1._width, this_1._height);\n                var start = paintAll ? layer.__startIndex : layer.__drawIndex;\n                var useTimer = !paintAll && layer.incremental && Date.now;\n                var startTime = useTimer && Date.now();\n                var clearColor = layer.zlevel === this_1._zlevelList[0]\n                    ? this_1._backgroundColor : null;\n                if (layer.__startIndex === layer.__endIndex) {\n                    layer.clear(false, clearColor, repaintRects);\n                }\n                else if (start === layer.__startIndex) {\n                    var firstEl = list[start];\n                    if (!firstEl.incremental || !firstEl.notClear || paintAll) {\n                        layer.clear(false, clearColor, repaintRects);\n                    }\n                }\n                if (start === -1) {\n                    console.error('For some unknown reason. drawIndex is -1');\n                    start = layer.__startIndex;\n                }\n                var i;\n                var repaint = function (repaintRect) {\n                    var scope = {\n                        inHover: false,\n                        allClipped: false,\n                        prevEl: null,\n                        viewWidth: _this._width,\n                        viewHeight: _this._height\n                    };\n                    for (i = start; i < layer.__endIndex; i++) {\n                        var el = list[i];\n                        if (el.__inHover) {\n                            needsRefreshHover = true;\n                        }\n                        _this._doPaintEl(el, layer, useDirtyRect, repaintRect, scope, i === layer.__endIndex - 1);\n                        if (useTimer) {\n                            var dTime = Date.now() - startTime;\n                            if (dTime > 15) {\n                                break;\n                            }\n                        }\n                    }\n                    if (scope.prevElClipPaths) {\n                        ctx.restore();\n                    }\n                };\n                if (repaintRects) {\n                    if (repaintRects.length === 0) {\n                        i = layer.__endIndex;\n                    }\n                    else {\n                        var dpr = this_1.dpr;\n                        for (var r = 0; r < repaintRects.length; ++r) {\n                            var rect = repaintRects[r];\n                            ctx.save();\n                            ctx.beginPath();\n                            ctx.rect(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr);\n                            ctx.clip();\n                            repaint(rect);\n                            ctx.restore();\n                        }\n                    }\n                }\n                else {\n                    ctx.save();\n                    repaint();\n                    ctx.restore();\n                }\n                layer.__drawIndex = i;\n                if (layer.__drawIndex < layer.__endIndex) {\n                    finished = false;\n                }\n            };\n            var this_1 = this;\n            for (var k = 0; k < layerList.length; k++) {\n                _loop_1(k);\n            }\n            if (env.wxa) {\n                each(this._layers, function (layer) {\n                    if (layer && layer.ctx && layer.ctx.draw) {\n                        layer.ctx.draw();\n                    }\n                });\n            }\n            return {\n                finished: finished,\n                needsRefreshHover: needsRefreshHover\n            };\n        };\n        CanvasPainter.prototype._doPaintEl = function (el, currentLayer, useDirtyRect, repaintRect, scope, isLast) {\n            var ctx = currentLayer.ctx;\n            if (useDirtyRect) {\n                var paintRect = el.getPaintRect();\n                if (!repaintRect || paintRect && paintRect.intersect(repaintRect)) {\n                    brush(ctx, el, scope, isLast);\n                    el.setPrevPaintRect(paintRect);\n                }\n            }\n            else {\n                brush(ctx, el, scope, isLast);\n            }\n        };\n        CanvasPainter.prototype.getLayer = function (zlevel, virtual) {\n            if (this._singleCanvas && !this._needsManuallyCompositing) {\n                zlevel = CANVAS_ZLEVEL;\n            }\n            var layer = this._layers[zlevel];\n            if (!layer) {\n                layer = new Layer('zr_' + zlevel, this, this.dpr);\n                layer.zlevel = zlevel;\n                layer.__builtin__ = true;\n                if (this._layerConfig[zlevel]) {\n                    merge(layer, this._layerConfig[zlevel], true);\n                }\n                else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) {\n                    merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true);\n                }\n                if (virtual) {\n                    layer.virtual = virtual;\n                }\n                this.insertLayer(zlevel, layer);\n                layer.initContext();\n            }\n            return layer;\n        };\n        CanvasPainter.prototype.insertLayer = function (zlevel, layer) {\n            var layersMap = this._layers;\n            var zlevelList = this._zlevelList;\n            var len = zlevelList.length;\n            var domRoot = this._domRoot;\n            var prevLayer = null;\n            var i = -1;\n            if (layersMap[zlevel]) {\n                if (\"development\" !== 'production') {\n                    logError('ZLevel ' + zlevel + ' has been used already');\n                }\n                return;\n            }\n            if (!isLayerValid(layer)) {\n                if (\"development\" !== 'production') {\n                    logError('Layer of zlevel ' + zlevel + ' is not valid');\n                }\n                return;\n            }\n            if (len > 0 && zlevel > zlevelList[0]) {\n                for (i = 0; i < len - 1; i++) {\n                    if (zlevelList[i] < zlevel\n                        && zlevelList[i + 1] > zlevel) {\n                        break;\n                    }\n                }\n                prevLayer = layersMap[zlevelList[i]];\n            }\n            zlevelList.splice(i + 1, 0, zlevel);\n            layersMap[zlevel] = layer;\n            if (!layer.virtual) {\n                if (prevLayer) {\n                    var prevDom = prevLayer.dom;\n                    if (prevDom.nextSibling) {\n                        domRoot.insertBefore(layer.dom, prevDom.nextSibling);\n                    }\n                    else {\n                        domRoot.appendChild(layer.dom);\n                    }\n                }\n                else {\n                    if (domRoot.firstChild) {\n                        domRoot.insertBefore(layer.dom, domRoot.firstChild);\n                    }\n                    else {\n                        domRoot.appendChild(layer.dom);\n                    }\n                }\n            }\n            layer.__painter = this;\n        };\n        CanvasPainter.prototype.eachLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                cb.call(context, this._layers[z], z);\n            }\n        };\n        CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (layer.__builtin__) {\n                    cb.call(context, layer, z);\n                }\n            }\n        };\n        CanvasPainter.prototype.eachOtherLayer = function (cb, context) {\n            var zlevelList = this._zlevelList;\n            for (var i = 0; i < zlevelList.length; i++) {\n                var z = zlevelList[i];\n                var layer = this._layers[z];\n                if (!layer.__builtin__) {\n                    cb.call(context, layer, z);\n                }\n            }\n        };\n        CanvasPainter.prototype.getLayers = function () {\n            return this._layers;\n        };\n        CanvasPainter.prototype._updateLayerStatus = function (list) {\n            this.eachBuiltinLayer(function (layer, z) {\n                layer.__dirty = layer.__used = false;\n            });\n            function updatePrevLayer(idx) {\n                if (prevLayer) {\n                    if (prevLayer.__endIndex !== idx) {\n                        prevLayer.__dirty = true;\n                    }\n                    prevLayer.__endIndex = idx;\n                }\n            }\n            if (this._singleCanvas) {\n                for (var i_1 = 1; i_1 < list.length; i_1++) {\n                    var el = list[i_1];\n                    if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) {\n                        this._needsManuallyCompositing = true;\n                        break;\n                    }\n                }\n            }\n            var prevLayer = null;\n            var incrementalLayerCount = 0;\n            var prevZlevel;\n            var i;\n            for (i = 0; i < list.length; i++) {\n                var el = list[i];\n                var zlevel = el.zlevel;\n                var layer = void 0;\n                if (prevZlevel !== zlevel) {\n                    prevZlevel = zlevel;\n                    incrementalLayerCount = 0;\n                }\n                if (el.incremental) {\n                    layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);\n                    layer.incremental = true;\n                    incrementalLayerCount = 1;\n                }\n                else {\n                    layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);\n                }\n                if (!layer.__builtin__) {\n                    logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);\n                }\n                if (layer !== prevLayer) {\n                    layer.__used = true;\n                    if (layer.__startIndex !== i) {\n                        layer.__dirty = true;\n                    }\n                    layer.__startIndex = i;\n                    if (!layer.incremental) {\n                        layer.__drawIndex = i;\n                    }\n                    else {\n                        layer.__drawIndex = -1;\n                    }\n                    updatePrevLayer(i);\n                    prevLayer = layer;\n                }\n                if ((el.__dirty & REDRAW_BIT) && !el.__inHover) {\n                    layer.__dirty = true;\n                    if (layer.incremental && layer.__drawIndex < 0) {\n                        layer.__drawIndex = i;\n                    }\n                }\n            }\n            updatePrevLayer(i);\n            this.eachBuiltinLayer(function (layer, z) {\n                if (!layer.__used && layer.getElementCount() > 0) {\n                    layer.__dirty = true;\n                    layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;\n                }\n                if (layer.__dirty && layer.__drawIndex < 0) {\n                    layer.__drawIndex = layer.__startIndex;\n                }\n            });\n        };\n        CanvasPainter.prototype.clear = function () {\n            this.eachBuiltinLayer(this._clearLayer);\n            return this;\n        };\n        CanvasPainter.prototype._clearLayer = function (layer) {\n            layer.clear();\n        };\n        CanvasPainter.prototype.setBackgroundColor = function (backgroundColor) {\n            this._backgroundColor = backgroundColor;\n            each(this._layers, function (layer) {\n                layer.setUnpainted();\n            });\n        };\n        CanvasPainter.prototype.configLayer = function (zlevel, config) {\n            if (config) {\n                var layerConfig = this._layerConfig;\n                if (!layerConfig[zlevel]) {\n                    layerConfig[zlevel] = config;\n                }\n                else {\n                    merge(layerConfig[zlevel], config, true);\n                }\n                for (var i = 0; i < this._zlevelList.length; i++) {\n                    var _zlevel = this._zlevelList[i];\n                    if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {\n                        var layer = this._layers[_zlevel];\n                        merge(layer, layerConfig[zlevel], true);\n                    }\n                }\n            }\n        };\n        CanvasPainter.prototype.delLayer = function (zlevel) {\n            var layers = this._layers;\n            var zlevelList = this._zlevelList;\n            var layer = layers[zlevel];\n            if (!layer) {\n                return;\n            }\n            layer.dom.parentNode.removeChild(layer.dom);\n            delete layers[zlevel];\n            zlevelList.splice(indexOf(zlevelList, zlevel), 1);\n        };\n        CanvasPainter.prototype.resize = function (width, height) {\n            if (!this._domRoot.style) {\n                if (width == null || height == null) {\n                    return;\n                }\n                this._width = width;\n                this._height = height;\n                this.getLayer(CANVAS_ZLEVEL).resize(width, height);\n            }\n            else {\n                var domRoot = this._domRoot;\n                domRoot.style.display = 'none';\n                var opts = this._opts;\n                var root = this.root;\n                width != null && (opts.width = width);\n                height != null && (opts.height = height);\n                width = getSize(root, 0, opts);\n                height = getSize(root, 1, opts);\n                domRoot.style.display = '';\n                if (this._width !== width || height !== this._height) {\n                    domRoot.style.width = width + 'px';\n                    domRoot.style.height = height + 'px';\n                    for (var id in this._layers) {\n                        if (this._layers.hasOwnProperty(id)) {\n                            this._layers[id].resize(width, height);\n                        }\n                    }\n                    this.refresh(true);\n                }\n                this._width = width;\n                this._height = height;\n            }\n            return this;\n        };\n        CanvasPainter.prototype.clearLayer = function (zlevel) {\n            var layer = this._layers[zlevel];\n            if (layer) {\n                layer.clear();\n            }\n        };\n        CanvasPainter.prototype.dispose = function () {\n            this.root.innerHTML = '';\n            this.root =\n                this.storage =\n                    this._domRoot =\n                        this._layers = null;\n        };\n        CanvasPainter.prototype.getRenderedCanvas = function (opts) {\n            opts = opts || {};\n            if (this._singleCanvas && !this._compositeManually) {\n                return this._layers[CANVAS_ZLEVEL].dom;\n            }\n            var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);\n            imageLayer.initContext();\n            imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);\n            var ctx = imageLayer.ctx;\n            if (opts.pixelRatio <= this.dpr) {\n                this.refresh();\n                var width_1 = imageLayer.dom.width;\n                var height_1 = imageLayer.dom.height;\n                this.eachLayer(function (layer) {\n                    if (layer.__builtin__) {\n                        ctx.drawImage(layer.dom, 0, 0, width_1, height_1);\n                    }\n                    else if (layer.renderToCanvas) {\n                        ctx.save();\n                        layer.renderToCanvas(ctx);\n                        ctx.restore();\n                    }\n                });\n            }\n            else {\n                var scope = {\n                    inHover: false,\n                    viewWidth: this._width,\n                    viewHeight: this._height\n                };\n                var displayList = this.storage.getDisplayList(true);\n                for (var i = 0, len = displayList.length; i < len; i++) {\n                    var el = displayList[i];\n                    brush(ctx, el, scope, i === len - 1);\n                }\n            }\n            return imageLayer.dom;\n        };\n        CanvasPainter.prototype.getWidth = function () {\n            return this._width;\n        };\n        CanvasPainter.prototype.getHeight = function () {\n            return this._height;\n        };\n        return CanvasPainter;\n    }());\n\n    function install(registers) {\n      registers.registerPainter('canvas', CanvasPainter);\n    }\n\n    var LineSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(LineSeriesModel, _super);\n\n      function LineSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = LineSeriesModel.type;\n        _this.hasSymbolVisual = true;\n        return _this;\n      }\n\n      LineSeriesModel.prototype.getInitialData = function (option) {\n        if (\"development\" !== 'production') {\n          var coordSys = option.coordinateSystem;\n\n          if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {\n            throw new Error('Line not support coordinateSystem besides cartesian and polar');\n          }\n        }\n\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      LineSeriesModel.prototype.getLegendIcon = function (opt) {\n        var group = new Group();\n        var line = createSymbol('line', 0, opt.itemHeight / 2, opt.itemWidth, 0, opt.lineStyle.stroke, false);\n        group.add(line);\n        line.setStyle(opt.lineStyle);\n        var visualType = this.getData().getVisual('symbol');\n        var visualRotate = this.getData().getVisual('symbolRotate');\n        var symbolType = visualType === 'none' ? 'circle' : visualType; // Symbol size is 80% when there is a line\n\n        var size = opt.itemHeight * 0.8;\n        var symbol = createSymbol(symbolType, (opt.itemWidth - size) / 2, (opt.itemHeight - size) / 2, size, size, opt.itemStyle.fill);\n        group.add(symbol);\n        symbol.setStyle(opt.itemStyle);\n        var symbolRotate = opt.iconRotate === 'inherit' ? visualRotate : opt.iconRotate || 0;\n        symbol.rotation = symbolRotate * Math.PI / 180;\n        symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);\n\n        if (symbolType.indexOf('empty') > -1) {\n          symbol.style.stroke = symbol.style.fill;\n          symbol.style.fill = '#fff';\n          symbol.style.lineWidth = 2;\n        }\n\n        return group;\n      };\n\n      LineSeriesModel.type = 'series.line';\n      LineSeriesModel.dependencies = ['grid', 'polar'];\n      LineSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 3,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        clip: true,\n        label: {\n          position: 'top'\n        },\n        // itemStyle: {\n        // },\n        endLabel: {\n          show: false,\n          valueAnimation: true,\n          distance: 8\n        },\n        lineStyle: {\n          width: 2,\n          type: 'solid'\n        },\n        emphasis: {\n          scale: true\n        },\n        // areaStyle: {\n        // origin of areaStyle. Valid values:\n        // `'auto'/null/undefined`: from axisLine to data\n        // `'start'`: from min to data\n        // `'end'`: from data to max\n        // origin: 'auto'\n        // },\n        // false, 'start', 'end', 'middle'\n        step: false,\n        // Disabled if step is true\n        smooth: false,\n        smoothMonotone: null,\n        symbol: 'emptyCircle',\n        symbolSize: 4,\n        symbolRotate: null,\n        showSymbol: true,\n        // `false`: follow the label interval strategy.\n        // `true`: show all symbols.\n        // `'auto'`: If possible, show all symbols, otherwise\n        //           follow the label interval strategy.\n        showAllSymbol: 'auto',\n        // Whether to connect break point.\n        connectNulls: false,\n        // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'.\n        sampling: 'none',\n        animationEasing: 'linear',\n        // Disable progressive\n        progressive: 0,\n        hoverLayerThreshold: Infinity,\n        universalTransition: {\n          divideShape: 'clone'\n        },\n        triggerLineEvent: false\n      };\n      return LineSeriesModel;\n    }(SeriesModel);\n\n    /**\n     * @return label string. Not null/undefined\n     */\n\n    function getDefaultLabel(data, dataIndex) {\n      var labelDims = data.mapDimensionsAll('defaultedLabel');\n      var len = labelDims.length; // Simple optimization (in lots of cases, label dims length is 1)\n\n      if (len === 1) {\n        var rawVal = retrieveRawValue(data, dataIndex, labelDims[0]);\n        return rawVal != null ? rawVal + '' : null;\n      } else if (len) {\n        var vals = [];\n\n        for (var i = 0; i < labelDims.length; i++) {\n          vals.push(retrieveRawValue(data, dataIndex, labelDims[i]));\n        }\n\n        return vals.join(' ');\n      }\n    }\n    function getDefaultInterpolatedLabel(data, interpolatedValue) {\n      var labelDims = data.mapDimensionsAll('defaultedLabel');\n\n      if (!isArray(interpolatedValue)) {\n        return interpolatedValue + '';\n      }\n\n      var vals = [];\n\n      for (var i = 0; i < labelDims.length; i++) {\n        var dimIndex = data.getDimensionIndex(labelDims[i]);\n\n        if (dimIndex >= 0) {\n          vals.push(interpolatedValue[dimIndex]);\n        }\n      }\n\n      return vals.join(' ');\n    }\n\n    var Symbol =\n    /** @class */\n    function (_super) {\n      __extends(Symbol, _super);\n\n      function Symbol(data, idx, seriesScope, opts) {\n        var _this = _super.call(this) || this;\n\n        _this.updateData(data, idx, seriesScope, opts);\n\n        return _this;\n      }\n\n      Symbol.prototype._createSymbol = function (symbolType, data, idx, symbolSize, keepAspect) {\n        // Remove paths created before\n        this.removeAll(); // let symbolPath = createSymbol(\n        //     symbolType, -0.5, -0.5, 1, 1, color\n        // );\n        // If width/height are set too small (e.g., set to 1) on ios10\n        // and macOS Sierra, a circle stroke become a rect, no matter what\n        // the scale is set. So we set width/height as 2. See #4150.\n\n        var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, null, keepAspect);\n        symbolPath.attr({\n          z2: 100,\n          culling: true,\n          scaleX: symbolSize[0] / 2,\n          scaleY: symbolSize[1] / 2\n        }); // Rewrite drift method\n\n        symbolPath.drift = driftSymbol;\n        this._symbolType = symbolType;\n        this.add(symbolPath);\n      };\n      /**\n       * Stop animation\n       * @param {boolean} toLastFrame\n       */\n\n\n      Symbol.prototype.stopSymbolAnimation = function (toLastFrame) {\n        this.childAt(0).stopAnimation(null, toLastFrame);\n      };\n\n      Symbol.prototype.getSymbolType = function () {\n        return this._symbolType;\n      };\n      /**\n       * FIXME:\n       * Caution: This method breaks the encapsulation of this module,\n       * but it indeed brings convenience. So do not use the method\n       * unless you detailedly know all the implements of `Symbol`,\n       * especially animation.\n       *\n       * Get symbol path element.\n       */\n\n\n      Symbol.prototype.getSymbolPath = function () {\n        return this.childAt(0);\n      };\n      /**\n       * Highlight symbol\n       */\n\n\n      Symbol.prototype.highlight = function () {\n        enterEmphasis(this.childAt(0));\n      };\n      /**\n       * Downplay symbol\n       */\n\n\n      Symbol.prototype.downplay = function () {\n        leaveEmphasis(this.childAt(0));\n      };\n      /**\n       * @param {number} zlevel\n       * @param {number} z\n       */\n\n\n      Symbol.prototype.setZ = function (zlevel, z) {\n        var symbolPath = this.childAt(0);\n        symbolPath.zlevel = zlevel;\n        symbolPath.z = z;\n      };\n\n      Symbol.prototype.setDraggable = function (draggable, hasCursorOption) {\n        var symbolPath = this.childAt(0);\n        symbolPath.draggable = draggable;\n        symbolPath.cursor = !hasCursorOption && draggable ? 'move' : symbolPath.cursor;\n      };\n      /**\n       * Update symbol properties\n       */\n\n\n      Symbol.prototype.updateData = function (data, idx, seriesScope, opts) {\n        this.silent = false;\n        var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';\n        var seriesModel = data.hostModel;\n        var symbolSize = Symbol.getSymbolSize(data, idx);\n        var isInit = symbolType !== this._symbolType;\n        var disableAnimation = opts && opts.disableAnimation;\n\n        if (isInit) {\n          var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect');\n\n          this._createSymbol(symbolType, data, idx, symbolSize, keepAspect);\n        } else {\n          var symbolPath = this.childAt(0);\n          symbolPath.silent = false;\n          var target = {\n            scaleX: symbolSize[0] / 2,\n            scaleY: symbolSize[1] / 2\n          };\n          disableAnimation ? symbolPath.attr(target) : updateProps(symbolPath, target, seriesModel, idx);\n          saveOldStyle(symbolPath);\n        }\n\n        this._updateCommon(data, idx, symbolSize, seriesScope, opts);\n\n        if (isInit) {\n          var symbolPath = this.childAt(0);\n\n          if (!disableAnimation) {\n            var target = {\n              scaleX: this._sizeX,\n              scaleY: this._sizeY,\n              style: {\n                // Always fadeIn. Because it has fadeOut animation when symbol is removed..\n                opacity: symbolPath.style.opacity\n              }\n            };\n            symbolPath.scaleX = symbolPath.scaleY = 0;\n            symbolPath.style.opacity = 0;\n            initProps(symbolPath, target, seriesModel, idx);\n          }\n        }\n\n        if (disableAnimation) {\n          // Must stop leave transition manually if don't call initProps or updateProps.\n          this.childAt(0).stopAnimation('leave');\n        }\n      };\n\n      Symbol.prototype._updateCommon = function (data, idx, symbolSize, seriesScope, opts) {\n        var symbolPath = this.childAt(0);\n        var seriesModel = data.hostModel;\n        var emphasisItemStyle;\n        var blurItemStyle;\n        var selectItemStyle;\n        var focus;\n        var blurScope;\n        var emphasisDisabled;\n        var labelStatesModels;\n        var hoverScale;\n        var cursorStyle;\n\n        if (seriesScope) {\n          emphasisItemStyle = seriesScope.emphasisItemStyle;\n          blurItemStyle = seriesScope.blurItemStyle;\n          selectItemStyle = seriesScope.selectItemStyle;\n          focus = seriesScope.focus;\n          blurScope = seriesScope.blurScope;\n          labelStatesModels = seriesScope.labelStatesModels;\n          hoverScale = seriesScope.hoverScale;\n          cursorStyle = seriesScope.cursorStyle;\n          emphasisDisabled = seriesScope.emphasisDisabled;\n        }\n\n        if (!seriesScope || data.hasItemOption) {\n          var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx);\n          var emphasisModel = itemModel.getModel('emphasis');\n          emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle();\n          selectItemStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();\n          blurItemStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();\n          focus = emphasisModel.get('focus');\n          blurScope = emphasisModel.get('blurScope');\n          emphasisDisabled = emphasisModel.get('disabled');\n          labelStatesModels = getLabelStatesModels(itemModel);\n          hoverScale = emphasisModel.getShallow('scale');\n          cursorStyle = itemModel.getShallow('cursor');\n        }\n\n        var symbolRotate = data.getItemVisual(idx, 'symbolRotate');\n        symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);\n        var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize);\n\n        if (symbolOffset) {\n          symbolPath.x = symbolOffset[0];\n          symbolPath.y = symbolOffset[1];\n        }\n\n        cursorStyle && symbolPath.attr('cursor', cursorStyle);\n        var symbolStyle = data.getItemVisual(idx, 'style');\n        var visualColor = symbolStyle.fill;\n\n        if (symbolPath instanceof ZRImage) {\n          var pathStyle = symbolPath.style;\n          symbolPath.useStyle(extend({\n            // TODO other properties like x, y ?\n            image: pathStyle.image,\n            x: pathStyle.x,\n            y: pathStyle.y,\n            width: pathStyle.width,\n            height: pathStyle.height\n          }, symbolStyle));\n        } else {\n          if (symbolPath.__isEmptyBrush) {\n            // fill and stroke will be swapped if it's empty.\n            // So we cloned a new style to avoid it affecting the original style in visual storage.\n            // TODO Better implementation. No empty logic!\n            symbolPath.useStyle(extend({}, symbolStyle));\n          } else {\n            symbolPath.useStyle(symbolStyle);\n          } // Disable decal because symbol scale will been applied on the decal.\n\n\n          symbolPath.style.decal = null;\n          symbolPath.setColor(visualColor, opts && opts.symbolInnerColor);\n          symbolPath.style.strokeNoScale = true;\n        }\n\n        var liftZ = data.getItemVisual(idx, 'liftZ');\n        var z2Origin = this._z2;\n\n        if (liftZ != null) {\n          if (z2Origin == null) {\n            this._z2 = symbolPath.z2;\n            symbolPath.z2 += liftZ;\n          }\n        } else if (z2Origin != null) {\n          symbolPath.z2 = z2Origin;\n          this._z2 = null;\n        }\n\n        var useNameLabel = opts && opts.useNameLabel;\n        setLabelStyle(symbolPath, labelStatesModels, {\n          labelFetcher: seriesModel,\n          labelDataIndex: idx,\n          defaultText: getLabelDefaultText,\n          inheritColor: visualColor,\n          defaultOpacity: symbolStyle.opacity\n        }); // Do not execute util needed.\n\n        function getLabelDefaultText(idx) {\n          return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);\n        }\n\n        this._sizeX = symbolSize[0] / 2;\n        this._sizeY = symbolSize[1] / 2;\n        var emphasisState = symbolPath.ensureState('emphasis');\n        emphasisState.style = emphasisItemStyle;\n        symbolPath.ensureState('select').style = selectItemStyle;\n        symbolPath.ensureState('blur').style = blurItemStyle; // null / undefined / true means to use default strategy.\n        // 0 / false / negative number / NaN / Infinity means no scale.\n\n        var scaleRatio = hoverScale == null || hoverScale === true ? Math.max(1.1, 3 / this._sizeY) // PENDING: restrict hoverScale > 1? It seems unreasonable to scale down\n        : isFinite(hoverScale) && hoverScale > 0 ? +hoverScale : 1; // always set scale to allow resetting\n\n        emphasisState.scaleX = this._sizeX * scaleRatio;\n        emphasisState.scaleY = this._sizeY * scaleRatio;\n        this.setSymbolScale(1);\n        toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled);\n      };\n\n      Symbol.prototype.setSymbolScale = function (scale) {\n        this.scaleX = this.scaleY = scale;\n      };\n\n      Symbol.prototype.fadeOut = function (cb, seriesModel, opt) {\n        var symbolPath = this.childAt(0);\n        var dataIndex = getECData(this).dataIndex;\n        var animationOpt = opt && opt.animation; // Avoid mistaken hover when fading out\n\n        this.silent = symbolPath.silent = true; // Not show text when animating\n\n        if (opt && opt.fadeLabel) {\n          var textContent = symbolPath.getTextContent();\n\n          if (textContent) {\n            removeElement(textContent, {\n              style: {\n                opacity: 0\n              }\n            }, seriesModel, {\n              dataIndex: dataIndex,\n              removeOpt: animationOpt,\n              cb: function () {\n                symbolPath.removeTextContent();\n              }\n            });\n          }\n        } else {\n          symbolPath.removeTextContent();\n        }\n\n        removeElement(symbolPath, {\n          style: {\n            opacity: 0\n          },\n          scaleX: 0,\n          scaleY: 0\n        }, seriesModel, {\n          dataIndex: dataIndex,\n          cb: cb,\n          removeOpt: animationOpt\n        });\n      };\n\n      Symbol.getSymbolSize = function (data, idx) {\n        return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));\n      };\n\n      return Symbol;\n    }(Group);\n\n    function driftSymbol(dx, dy) {\n      this.parent.drift(dx, dy);\n    }\n\n    function symbolNeedsDraw(data, point, idx, opt) {\n      return point && !isNaN(point[0]) && !isNaN(point[1]) && !(opt.isIgnore && opt.isIgnore(idx)) // We do not set clipShape on group, because it will cut part of\n      // the symbol element shape. We use the same clip shape here as\n      // the line clip.\n      && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) && data.getItemVisual(idx, 'symbol') !== 'none';\n    }\n\n    function normalizeUpdateOpt(opt) {\n      if (opt != null && !isObject(opt)) {\n        opt = {\n          isIgnore: opt\n        };\n      }\n\n      return opt || {};\n    }\n\n    function makeSeriesScope(data) {\n      var seriesModel = data.hostModel;\n      var emphasisModel = seriesModel.getModel('emphasis');\n      return {\n        emphasisItemStyle: emphasisModel.getModel('itemStyle').getItemStyle(),\n        blurItemStyle: seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(),\n        selectItemStyle: seriesModel.getModel(['select', 'itemStyle']).getItemStyle(),\n        focus: emphasisModel.get('focus'),\n        blurScope: emphasisModel.get('blurScope'),\n        emphasisDisabled: emphasisModel.get('disabled'),\n        hoverScale: emphasisModel.get('scale'),\n        labelStatesModels: getLabelStatesModels(seriesModel),\n        cursorStyle: seriesModel.get('cursor')\n      };\n    }\n\n    var SymbolDraw =\n    /** @class */\n    function () {\n      function SymbolDraw(SymbolCtor) {\n        this.group = new Group();\n        this._SymbolCtor = SymbolCtor || Symbol;\n      }\n      /**\n       * Update symbols draw by new data\n       */\n\n\n      SymbolDraw.prototype.updateData = function (data, opt) {\n        // Remove progressive els.\n        this._progressiveEls = null;\n        opt = normalizeUpdateOpt(opt);\n        var group = this.group;\n        var seriesModel = data.hostModel;\n        var oldData = this._data;\n        var SymbolCtor = this._SymbolCtor;\n        var disableAnimation = opt.disableAnimation;\n        var seriesScope = makeSeriesScope(data);\n        var symbolUpdateOpt = {\n          disableAnimation: disableAnimation\n        };\n\n        var getSymbolPoint = opt.getSymbolPoint || function (idx) {\n          return data.getItemLayout(idx);\n        }; // There is no oldLineData only when first rendering or switching from\n        // stream mode to normal mode, where previous elements should be removed.\n\n\n        if (!oldData) {\n          group.removeAll();\n        }\n\n        data.diff(oldData).add(function (newIdx) {\n          var point = getSymbolPoint(newIdx);\n\n          if (symbolNeedsDraw(data, point, newIdx, opt)) {\n            var symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n            symbolEl.setPosition(point);\n            data.setItemGraphicEl(newIdx, symbolEl);\n            group.add(symbolEl);\n          }\n        }).update(function (newIdx, oldIdx) {\n          var symbolEl = oldData.getItemGraphicEl(oldIdx);\n          var point = getSymbolPoint(newIdx);\n\n          if (!symbolNeedsDraw(data, point, newIdx, opt)) {\n            group.remove(symbolEl);\n            return;\n          }\n\n          var newSymbolType = data.getItemVisual(newIdx, 'symbol') || 'circle';\n          var oldSymbolType = symbolEl && symbolEl.getSymbolType && symbolEl.getSymbolType();\n\n          if (!symbolEl // Create a new if symbol type changed.\n          || oldSymbolType && oldSymbolType !== newSymbolType) {\n            group.remove(symbolEl);\n            symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt);\n            symbolEl.setPosition(point);\n          } else {\n            symbolEl.updateData(data, newIdx, seriesScope, symbolUpdateOpt);\n            var target = {\n              x: point[0],\n              y: point[1]\n            };\n            disableAnimation ? symbolEl.attr(target) : updateProps(symbolEl, target, seriesModel);\n          } // Add back\n\n\n          group.add(symbolEl);\n          data.setItemGraphicEl(newIdx, symbolEl);\n        }).remove(function (oldIdx) {\n          var el = oldData.getItemGraphicEl(oldIdx);\n          el && el.fadeOut(function () {\n            group.remove(el);\n          }, seriesModel);\n        }).execute();\n        this._getSymbolPoint = getSymbolPoint;\n        this._data = data;\n      };\n\n      SymbolDraw.prototype.updateLayout = function () {\n        var _this = this;\n\n        var data = this._data;\n\n        if (data) {\n          // Not use animation\n          data.eachItemGraphicEl(function (el, idx) {\n            var point = _this._getSymbolPoint(idx);\n\n            el.setPosition(point);\n            el.markRedraw();\n          });\n        }\n      };\n\n      SymbolDraw.prototype.incrementalPrepareUpdate = function (data) {\n        this._seriesScope = makeSeriesScope(data);\n        this._data = null;\n        this.group.removeAll();\n      };\n      /**\n       * Update symbols draw by new data\n       */\n\n      SymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) {\n        // Clear\n        this._progressiveEls = [];\n        opt = normalizeUpdateOpt(opt);\n\n        function updateIncrementalAndHover(el) {\n          if (!el.isGroup) {\n            el.incremental = true;\n            el.ensureState('emphasis').hoverLayer = true;\n          }\n        }\n\n        for (var idx = taskParams.start; idx < taskParams.end; idx++) {\n          var point = data.getItemLayout(idx);\n\n          if (symbolNeedsDraw(data, point, idx, opt)) {\n            var el = new this._SymbolCtor(data, idx, this._seriesScope);\n            el.traverse(updateIncrementalAndHover);\n            el.setPosition(point);\n            this.group.add(el);\n            data.setItemGraphicEl(idx, el);\n\n            this._progressiveEls.push(el);\n          }\n        }\n      };\n\n      SymbolDraw.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      SymbolDraw.prototype.remove = function (enableAnimation) {\n        var group = this.group;\n        var data = this._data; // Incremental model do not have this._data.\n\n        if (data && enableAnimation) {\n          data.eachItemGraphicEl(function (el) {\n            el.fadeOut(function () {\n              group.remove(el);\n            }, data.hostModel);\n          });\n        } else {\n          group.removeAll();\n        }\n      };\n      return SymbolDraw;\n    }();\n\n    function prepareDataCoordInfo(coordSys, data, valueOrigin) {\n      var baseAxis = coordSys.getBaseAxis();\n      var valueAxis = coordSys.getOtherAxis(baseAxis);\n      var valueStart = getValueStart(valueAxis, valueOrigin);\n      var baseAxisDim = baseAxis.dim;\n      var valueAxisDim = valueAxis.dim;\n      var valueDim = data.mapDimension(valueAxisDim);\n      var baseDim = data.mapDimension(baseAxisDim);\n      var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;\n      var dims = map(coordSys.dimensions, function (coordDim) {\n        return data.mapDimension(coordDim);\n      });\n      var stacked = false;\n      var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n      if (isDimensionStacked(data, dims[0]\n      /* , dims[1] */\n      )) {\n        // jshint ignore:line\n        stacked = true;\n        dims[0] = stackResultDim;\n      }\n\n      if (isDimensionStacked(data, dims[1]\n      /* , dims[0] */\n      )) {\n        // jshint ignore:line\n        stacked = true;\n        dims[1] = stackResultDim;\n      }\n\n      return {\n        dataDimsForPoint: dims,\n        valueStart: valueStart,\n        valueAxisDim: valueAxisDim,\n        baseAxisDim: baseAxisDim,\n        stacked: !!stacked,\n        valueDim: valueDim,\n        baseDim: baseDim,\n        baseDataOffset: baseDataOffset,\n        stackedOverDimension: data.getCalculationInfo('stackedOverDimension')\n      };\n    }\n\n    function getValueStart(valueAxis, valueOrigin) {\n      var valueStart = 0;\n      var extent = valueAxis.scale.getExtent();\n\n      if (valueOrigin === 'start') {\n        valueStart = extent[0];\n      } else if (valueOrigin === 'end') {\n        valueStart = extent[1];\n      } // If origin is specified as a number, use it as\n      // valueStart directly\n      else if (isNumber(valueOrigin) && !isNaN(valueOrigin)) {\n          valueStart = valueOrigin;\n        } // auto\n        else {\n            // Both positive\n            if (extent[0] > 0) {\n              valueStart = extent[0];\n            } // Both negative\n            else if (extent[1] < 0) {\n                valueStart = extent[1];\n              } // If is one positive, and one negative, onZero shall be true\n\n          }\n\n      return valueStart;\n    }\n\n    function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) {\n      var value = NaN;\n\n      if (dataCoordInfo.stacked) {\n        value = data.get(data.getCalculationInfo('stackedOverDimension'), idx);\n      }\n\n      if (isNaN(value)) {\n        value = dataCoordInfo.valueStart;\n      }\n\n      var baseDataOffset = dataCoordInfo.baseDataOffset;\n      var stackedData = [];\n      stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx);\n      stackedData[1 - baseDataOffset] = value;\n      return coordSys.dataToPoint(stackedData);\n    }\n\n    function diffData(oldData, newData) {\n      var diffResult = [];\n      newData.diff(oldData).add(function (idx) {\n        diffResult.push({\n          cmd: '+',\n          idx: idx\n        });\n      }).update(function (newIdx, oldIdx) {\n        diffResult.push({\n          cmd: '=',\n          idx: oldIdx,\n          idx1: newIdx\n        });\n      }).remove(function (idx) {\n        diffResult.push({\n          cmd: '-',\n          idx: idx\n        });\n      }).execute();\n      return diffResult;\n    }\n\n    function lineAnimationDiff(oldData, newData, oldStackedOnPoints, newStackedOnPoints, oldCoordSys, newCoordSys, oldValueOrigin, newValueOrigin) {\n      var diff = diffData(oldData, newData); // let newIdList = newData.mapArray(newData.getId);\n      // let oldIdList = oldData.mapArray(oldData.getId);\n      // convertToIntId(newIdList, oldIdList);\n      // // FIXME One data ?\n      // diff = arrayDiff(oldIdList, newIdList);\n\n      var currPoints = [];\n      var nextPoints = []; // Points for stacking base line\n\n      var currStackedPoints = [];\n      var nextStackedPoints = [];\n      var status = [];\n      var sortedIndices = [];\n      var rawIndices = [];\n      var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); // const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin);\n\n      var oldPoints = oldData.getLayout('points') || [];\n      var newPoints = newData.getLayout('points') || [];\n\n      for (var i = 0; i < diff.length; i++) {\n        var diffItem = diff[i];\n        var pointAdded = true;\n        var oldIdx2 = void 0;\n        var newIdx2 = void 0; // FIXME, animation is not so perfect when dataZoom window moves fast\n        // Which is in case remvoing or add more than one data in the tail or head\n\n        switch (diffItem.cmd) {\n          case '=':\n            oldIdx2 = diffItem.idx * 2;\n            newIdx2 = diffItem.idx1 * 2;\n            var currentX = oldPoints[oldIdx2];\n            var currentY = oldPoints[oldIdx2 + 1];\n            var nextX = newPoints[newIdx2];\n            var nextY = newPoints[newIdx2 + 1]; // If previous data is NaN, use next point directly\n\n            if (isNaN(currentX) || isNaN(currentY)) {\n              currentX = nextX;\n              currentY = nextY;\n            }\n\n            currPoints.push(currentX, currentY);\n            nextPoints.push(nextX, nextY);\n            currStackedPoints.push(oldStackedOnPoints[oldIdx2], oldStackedOnPoints[oldIdx2 + 1]);\n            nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n            rawIndices.push(newData.getRawIndex(diffItem.idx1));\n            break;\n\n          case '+':\n            var newIdx = diffItem.idx;\n            var newDataDimsForPoint = newDataOldCoordInfo.dataDimsForPoint;\n            var oldPt = oldCoordSys.dataToPoint([newData.get(newDataDimsForPoint[0], newIdx), newData.get(newDataDimsForPoint[1], newIdx)]);\n            newIdx2 = newIdx * 2;\n            currPoints.push(oldPt[0], oldPt[1]);\n            nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1]);\n            var stackedOnPoint = getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, newIdx);\n            currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1]);\n            nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]);\n            rawIndices.push(newData.getRawIndex(newIdx));\n            break;\n\n          case '-':\n            pointAdded = false;\n        } // Original indices\n\n\n        if (pointAdded) {\n          status.push(diffItem);\n          sortedIndices.push(sortedIndices.length);\n        }\n      } // Diff result may be crossed if all items are changed\n      // Sort by data index\n\n\n      sortedIndices.sort(function (a, b) {\n        return rawIndices[a] - rawIndices[b];\n      });\n      var len = currPoints.length;\n      var sortedCurrPoints = createFloat32Array(len);\n      var sortedNextPoints = createFloat32Array(len);\n      var sortedCurrStackedPoints = createFloat32Array(len);\n      var sortedNextStackedPoints = createFloat32Array(len);\n      var sortedStatus = [];\n\n      for (var i = 0; i < sortedIndices.length; i++) {\n        var idx = sortedIndices[i];\n        var i2 = i * 2;\n        var idx2 = idx * 2;\n        sortedCurrPoints[i2] = currPoints[idx2];\n        sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1];\n        sortedNextPoints[i2] = nextPoints[idx2];\n        sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1];\n        sortedCurrStackedPoints[i2] = currStackedPoints[idx2];\n        sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1];\n        sortedNextStackedPoints[i2] = nextStackedPoints[idx2];\n        sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1];\n        sortedStatus[i] = status[idx];\n      }\n\n      return {\n        current: sortedCurrPoints,\n        next: sortedNextPoints,\n        stackedOnCurrent: sortedCurrStackedPoints,\n        stackedOnNext: sortedNextStackedPoints,\n        status: sortedStatus\n      };\n    }\n\n    var mathMin$5 = Math.min;\n    var mathMax$5 = Math.max;\n\n    function isPointNull(x, y) {\n      return isNaN(x) || isNaN(y);\n    }\n    /**\n     * Draw smoothed line in non-monotone, in may cause undesired curve in extreme\n     * situations. This should be used when points are non-monotone neither in x or\n     * y dimension.\n     */\n\n\n    function drawSegment(ctx, points, start, segLen, allLen, dir, smooth, smoothMonotone, connectNulls) {\n      var prevX;\n      var prevY;\n      var cpx0;\n      var cpy0;\n      var cpx1;\n      var cpy1;\n      var idx = start;\n      var k = 0;\n\n      for (; k < segLen; k++) {\n        var x = points[idx * 2];\n        var y = points[idx * 2 + 1];\n\n        if (idx >= allLen || idx < 0) {\n          break;\n        }\n\n        if (isPointNull(x, y)) {\n          if (connectNulls) {\n            idx += dir;\n            continue;\n          }\n\n          break;\n        }\n\n        if (idx === start) {\n          ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y);\n          cpx0 = x;\n          cpy0 = y;\n        } else {\n          var dx = x - prevX;\n          var dy = y - prevY; // Ignore tiny segment.\n\n          if (dx * dx + dy * dy < 0.5) {\n            idx += dir;\n            continue;\n          }\n\n          if (smooth > 0) {\n            var nextIdx = idx + dir;\n            var nextX = points[nextIdx * 2];\n            var nextY = points[nextIdx * 2 + 1]; // Ignore duplicate point\n\n            while (nextX === x && nextY === y && k < segLen) {\n              k++;\n              nextIdx += dir;\n              idx += dir;\n              nextX = points[nextIdx * 2];\n              nextY = points[nextIdx * 2 + 1];\n              x = points[idx * 2];\n              y = points[idx * 2 + 1];\n              dx = x - prevX;\n              dy = y - prevY;\n            }\n\n            var tmpK = k + 1;\n\n            if (connectNulls) {\n              // Find next point not null\n              while (isPointNull(nextX, nextY) && tmpK < segLen) {\n                tmpK++;\n                nextIdx += dir;\n                nextX = points[nextIdx * 2];\n                nextY = points[nextIdx * 2 + 1];\n              }\n            }\n\n            var ratioNextSeg = 0.5;\n            var vx = 0;\n            var vy = 0;\n            var nextCpx0 = void 0;\n            var nextCpy0 = void 0; // Is last point\n\n            if (tmpK >= segLen || isPointNull(nextX, nextY)) {\n              cpx1 = x;\n              cpy1 = y;\n            } else {\n              vx = nextX - prevX;\n              vy = nextY - prevY;\n              var dx0 = x - prevX;\n              var dx1 = nextX - x;\n              var dy0 = y - prevY;\n              var dy1 = nextY - y;\n              var lenPrevSeg = void 0;\n              var lenNextSeg = void 0;\n\n              if (smoothMonotone === 'x') {\n                lenPrevSeg = Math.abs(dx0);\n                lenNextSeg = Math.abs(dx1);\n                var dir_1 = vx > 0 ? 1 : -1;\n                cpx1 = x - dir_1 * lenPrevSeg * smooth;\n                cpy1 = y;\n                nextCpx0 = x + dir_1 * lenNextSeg * smooth;\n                nextCpy0 = y;\n              } else if (smoothMonotone === 'y') {\n                lenPrevSeg = Math.abs(dy0);\n                lenNextSeg = Math.abs(dy1);\n                var dir_2 = vy > 0 ? 1 : -1;\n                cpx1 = x;\n                cpy1 = y - dir_2 * lenPrevSeg * smooth;\n                nextCpx0 = x;\n                nextCpy0 = y + dir_2 * lenNextSeg * smooth;\n              } else {\n                lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0);\n                lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1); // Use ratio of seg length\n\n                ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);\n                cpx1 = x - vx * smooth * (1 - ratioNextSeg);\n                cpy1 = y - vy * smooth * (1 - ratioNextSeg); // cp0 of next segment\n\n                nextCpx0 = x + vx * smooth * ratioNextSeg;\n                nextCpy0 = y + vy * smooth * ratioNextSeg; // Smooth constraint between point and next point.\n                // Avoid exceeding extreme after smoothing.\n\n                nextCpx0 = mathMin$5(nextCpx0, mathMax$5(nextX, x));\n                nextCpy0 = mathMin$5(nextCpy0, mathMax$5(nextY, y));\n                nextCpx0 = mathMax$5(nextCpx0, mathMin$5(nextX, x));\n                nextCpy0 = mathMax$5(nextCpy0, mathMin$5(nextY, y)); // Reclaculate cp1 based on the adjusted cp0 of next seg.\n\n                vx = nextCpx0 - x;\n                vy = nextCpy0 - y;\n                cpx1 = x - vx * lenPrevSeg / lenNextSeg;\n                cpy1 = y - vy * lenPrevSeg / lenNextSeg; // Smooth constraint between point and prev point.\n                // Avoid exceeding extreme after smoothing.\n\n                cpx1 = mathMin$5(cpx1, mathMax$5(prevX, x));\n                cpy1 = mathMin$5(cpy1, mathMax$5(prevY, y));\n                cpx1 = mathMax$5(cpx1, mathMin$5(prevX, x));\n                cpy1 = mathMax$5(cpy1, mathMin$5(prevY, y)); // Adjust next cp0 again.\n\n                vx = x - cpx1;\n                vy = y - cpy1;\n                nextCpx0 = x + vx * lenNextSeg / lenPrevSeg;\n                nextCpy0 = y + vy * lenNextSeg / lenPrevSeg;\n              }\n            }\n\n            ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y);\n            cpx0 = nextCpx0;\n            cpy0 = nextCpy0;\n          } else {\n            ctx.lineTo(x, y);\n          }\n        }\n\n        prevX = x;\n        prevY = y;\n        idx += dir;\n      }\n\n      return k;\n    }\n\n    var ECPolylineShape =\n    /** @class */\n    function () {\n      function ECPolylineShape() {\n        this.smooth = 0;\n        this.smoothConstraint = true;\n      }\n\n      return ECPolylineShape;\n    }();\n\n    var ECPolyline =\n    /** @class */\n    function (_super) {\n      __extends(ECPolyline, _super);\n\n      function ECPolyline(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-polyline';\n        return _this;\n      }\n\n      ECPolyline.prototype.getDefaultStyle = function () {\n        return {\n          stroke: '#000',\n          fill: null\n        };\n      };\n\n      ECPolyline.prototype.getDefaultShape = function () {\n        return new ECPolylineShape();\n      };\n\n      ECPolyline.prototype.buildPath = function (ctx, shape) {\n        var points = shape.points;\n        var i = 0;\n        var len = points.length / 2; // const result = getBoundingBox(points, shape.smoothConstraint);\n\n        if (shape.connectNulls) {\n          // Must remove first and last null values avoid draw error in polygon\n          for (; len > 0; len--) {\n            if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n              break;\n            }\n          }\n\n          for (; i < len; i++) {\n            if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n              break;\n            }\n          }\n        }\n\n        while (i < len) {\n          i += drawSegment(ctx, points, i, len, len, 1, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1;\n        }\n      };\n\n      ECPolyline.prototype.getPointOn = function (xOrY, dim) {\n        if (!this.path) {\n          this.createPathProxy();\n          this.buildPath(this.path, this.shape);\n        }\n\n        var path = this.path;\n        var data = path.data;\n        var CMD = PathProxy.CMD;\n        var x0;\n        var y0;\n        var isDimX = dim === 'x';\n        var roots = [];\n\n        for (var i = 0; i < data.length;) {\n          var cmd = data[i++];\n          var x = void 0;\n          var y = void 0;\n          var x2 = void 0;\n          var y2 = void 0;\n          var x3 = void 0;\n          var y3 = void 0;\n          var t = void 0;\n\n          switch (cmd) {\n            case CMD.M:\n              x0 = data[i++];\n              y0 = data[i++];\n              break;\n\n            case CMD.L:\n              x = data[i++];\n              y = data[i++];\n              t = isDimX ? (xOrY - x0) / (x - x0) : (xOrY - y0) / (y - y0);\n\n              if (t <= 1 && t >= 0) {\n                var val = isDimX ? (y - y0) * t + y0 : (x - x0) * t + x0;\n                return isDimX ? [xOrY, val] : [val, xOrY];\n              }\n\n              x0 = x;\n              y0 = y;\n              break;\n\n            case CMD.C:\n              x = data[i++];\n              y = data[i++];\n              x2 = data[i++];\n              y2 = data[i++];\n              x3 = data[i++];\n              y3 = data[i++];\n              var nRoot = isDimX ? cubicRootAt(x0, x, x2, x3, xOrY, roots) : cubicRootAt(y0, y, y2, y3, xOrY, roots);\n\n              if (nRoot > 0) {\n                for (var i_1 = 0; i_1 < nRoot; i_1++) {\n                  var t_1 = roots[i_1];\n\n                  if (t_1 <= 1 && t_1 >= 0) {\n                    var val = isDimX ? cubicAt(y0, y, y2, y3, t_1) : cubicAt(x0, x, x2, x3, t_1);\n                    return isDimX ? [xOrY, val] : [val, xOrY];\n                  }\n                }\n              }\n\n              x0 = x3;\n              y0 = y3;\n              break;\n          }\n        }\n      };\n\n      return ECPolyline;\n    }(Path);\n\n    var ECPolygonShape =\n    /** @class */\n    function (_super) {\n      __extends(ECPolygonShape, _super);\n\n      function ECPolygonShape() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      return ECPolygonShape;\n    }(ECPolylineShape);\n\n    var ECPolygon =\n    /** @class */\n    function (_super) {\n      __extends(ECPolygon, _super);\n\n      function ECPolygon(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'ec-polygon';\n        return _this;\n      }\n\n      ECPolygon.prototype.getDefaultShape = function () {\n        return new ECPolygonShape();\n      };\n\n      ECPolygon.prototype.buildPath = function (ctx, shape) {\n        var points = shape.points;\n        var stackedOnPoints = shape.stackedOnPoints;\n        var i = 0;\n        var len = points.length / 2;\n        var smoothMonotone = shape.smoothMonotone;\n\n        if (shape.connectNulls) {\n          // Must remove first and last null values avoid draw error in polygon\n          for (; len > 0; len--) {\n            if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {\n              break;\n            }\n          }\n\n          for (; i < len; i++) {\n            if (!isPointNull(points[i * 2], points[i * 2 + 1])) {\n              break;\n            }\n          }\n        }\n\n        while (i < len) {\n          var k = drawSegment(ctx, points, i, len, len, 1, shape.smooth, smoothMonotone, shape.connectNulls);\n          drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls);\n          i += k + 1;\n          ctx.closePath();\n        }\n      };\n\n      return ECPolygon;\n    }(Path);\n\n    function createGridClipPath(cartesian, hasAnimation, seriesModel, done, during) {\n      var rect = cartesian.getArea();\n      var x = rect.x;\n      var y = rect.y;\n      var width = rect.width;\n      var height = rect.height;\n      var lineWidth = seriesModel.get(['lineStyle', 'width']) || 2; // Expand the clip path a bit to avoid the border is clipped and looks thinner\n\n      x -= lineWidth / 2;\n      y -= lineWidth / 2;\n      width += lineWidth;\n      height += lineWidth; // fix: https://github.com/apache/incubator-echarts/issues/11369\n\n      x = Math.floor(x);\n      width = Math.round(width);\n      var clipPath = new Rect({\n        shape: {\n          x: x,\n          y: y,\n          width: width,\n          height: height\n        }\n      });\n\n      if (hasAnimation) {\n        var baseAxis = cartesian.getBaseAxis();\n        var isHorizontal = baseAxis.isHorizontal();\n        var isAxisInversed = baseAxis.inverse;\n\n        if (isHorizontal) {\n          if (isAxisInversed) {\n            clipPath.shape.x += width;\n          }\n\n          clipPath.shape.width = 0;\n        } else {\n          if (!isAxisInversed) {\n            clipPath.shape.y += height;\n          }\n\n          clipPath.shape.height = 0;\n        }\n\n        var duringCb = isFunction(during) ? function (percent) {\n          during(percent, clipPath);\n        } : null;\n        initProps(clipPath, {\n          shape: {\n            width: width,\n            height: height,\n            x: x,\n            y: y\n          }\n        }, seriesModel, null, done, duringCb);\n      }\n\n      return clipPath;\n    }\n\n    function createPolarClipPath(polar, hasAnimation, seriesModel) {\n      var sectorArea = polar.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n\n      var r0 = round(sectorArea.r0, 1);\n      var r = round(sectorArea.r, 1);\n      var clipPath = new Sector({\n        shape: {\n          cx: round(polar.cx, 1),\n          cy: round(polar.cy, 1),\n          r0: r0,\n          r: r,\n          startAngle: sectorArea.startAngle,\n          endAngle: sectorArea.endAngle,\n          clockwise: sectorArea.clockwise\n        }\n      });\n\n      if (hasAnimation) {\n        var isRadial = polar.getBaseAxis().dim === 'angle';\n\n        if (isRadial) {\n          clipPath.shape.endAngle = sectorArea.startAngle;\n        } else {\n          clipPath.shape.r = r0;\n        }\n\n        initProps(clipPath, {\n          shape: {\n            endAngle: sectorArea.endAngle,\n            r: r\n          }\n        }, seriesModel);\n      }\n\n      return clipPath;\n    }\n\n    function createClipPath(coordSys, hasAnimation, seriesModel, done, during) {\n      if (!coordSys) {\n        return null;\n      } else if (coordSys.type === 'polar') {\n        return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n      } else if (coordSys.type === 'cartesian2d') {\n        return createGridClipPath(coordSys, hasAnimation, seriesModel, done, during);\n      }\n\n      return null;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function isCoordinateSystemType(coordSys, type) {\n      return coordSys.type === type;\n    }\n\n    function isPointsSame(points1, points2) {\n      if (points1.length !== points2.length) {\n        return;\n      }\n\n      for (var i = 0; i < points1.length; i++) {\n        if (points1[i] !== points2[i]) {\n          return;\n        }\n      }\n\n      return true;\n    }\n\n    function bboxFromPoints(points) {\n      var minX = Infinity;\n      var minY = Infinity;\n      var maxX = -Infinity;\n      var maxY = -Infinity;\n\n      for (var i = 0; i < points.length;) {\n        var x = points[i++];\n        var y = points[i++];\n\n        if (!isNaN(x)) {\n          minX = Math.min(x, minX);\n          maxX = Math.max(x, maxX);\n        }\n\n        if (!isNaN(y)) {\n          minY = Math.min(y, minY);\n          maxY = Math.max(y, maxY);\n        }\n      }\n\n      return [[minX, minY], [maxX, maxY]];\n    }\n\n    function getBoundingDiff(points1, points2) {\n      var _a = bboxFromPoints(points1),\n          min1 = _a[0],\n          max1 = _a[1];\n\n      var _b = bboxFromPoints(points2),\n          min2 = _b[0],\n          max2 = _b[1]; // Get a max value from each corner of two boundings.\n\n\n      return Math.max(Math.abs(min1[0] - min2[0]), Math.abs(min1[1] - min2[1]), Math.abs(max1[0] - max2[0]), Math.abs(max1[1] - max2[1]));\n    }\n\n    function getSmooth(smooth) {\n      return isNumber(smooth) ? smooth : smooth ? 0.5 : 0;\n    }\n\n    function getStackedOnPoints(coordSys, data, dataCoordInfo) {\n      if (!dataCoordInfo.valueDim) {\n        return [];\n      }\n\n      var len = data.count();\n      var points = createFloat32Array(len * 2);\n\n      for (var idx = 0; idx < len; idx++) {\n        var pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx);\n        points[idx * 2] = pt[0];\n        points[idx * 2 + 1] = pt[1];\n      }\n\n      return points;\n    }\n\n    function turnPointsIntoStep(points, coordSys, stepTurnAt, connectNulls) {\n      var baseAxis = coordSys.getBaseAxis();\n      var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;\n      var stepPoints = [];\n      var i = 0;\n      var stepPt = [];\n      var pt = [];\n      var nextPt = [];\n      var filteredPoints = [];\n\n      if (connectNulls) {\n        for (i = 0; i < points.length; i += 2) {\n          if (!isNaN(points[i]) && !isNaN(points[i + 1])) {\n            filteredPoints.push(points[i], points[i + 1]);\n          }\n        }\n\n        points = filteredPoints;\n      }\n\n      for (i = 0; i < points.length - 2; i += 2) {\n        nextPt[0] = points[i + 2];\n        nextPt[1] = points[i + 3];\n        pt[0] = points[i];\n        pt[1] = points[i + 1];\n        stepPoints.push(pt[0], pt[1]);\n\n        switch (stepTurnAt) {\n          case 'end':\n            stepPt[baseIndex] = nextPt[baseIndex];\n            stepPt[1 - baseIndex] = pt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n            break;\n\n          case 'middle':\n            var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;\n            var stepPt2 = [];\n            stepPt[baseIndex] = stepPt2[baseIndex] = middle;\n            stepPt[1 - baseIndex] = pt[1 - baseIndex];\n            stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n            stepPoints.push(stepPt2[0], stepPt2[1]);\n            break;\n\n          default:\n            // default is start\n            stepPt[baseIndex] = pt[baseIndex];\n            stepPt[1 - baseIndex] = nextPt[1 - baseIndex];\n            stepPoints.push(stepPt[0], stepPt[1]);\n        }\n      } // Last points\n\n\n      stepPoints.push(points[i++], points[i++]);\n      return stepPoints;\n    }\n    /**\n     * Clip color stops to edge. Avoid creating too large gradients.\n     * Which may lead to blurry when GPU acceleration is enabled. See #15680\n     *\n     * The stops has been sorted from small to large.\n     */\n\n\n    function clipColorStops(colorStops, maxSize) {\n      var newColorStops = [];\n      var len = colorStops.length; // coord will always < 0 in prevOutOfRangeColorStop.\n\n      var prevOutOfRangeColorStop;\n      var prevInRangeColorStop;\n\n      function lerpStop(stop0, stop1, clippedCoord) {\n        var coord0 = stop0.coord;\n        var p = (clippedCoord - coord0) / (stop1.coord - coord0);\n        var color = lerp$1(p, [stop0.color, stop1.color]);\n        return {\n          coord: clippedCoord,\n          color: color\n        };\n      }\n\n      for (var i = 0; i < len; i++) {\n        var stop_1 = colorStops[i];\n        var coord = stop_1.coord;\n\n        if (coord < 0) {\n          prevOutOfRangeColorStop = stop_1;\n        } else if (coord > maxSize) {\n          if (prevInRangeColorStop) {\n            newColorStops.push(lerpStop(prevInRangeColorStop, stop_1, maxSize));\n          } else if (prevOutOfRangeColorStop) {\n            // If there are two stops and coord range is between these two stops\n            newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0), lerpStop(prevOutOfRangeColorStop, stop_1, maxSize));\n          } // All following stop will be out of range. So just ignore them.\n\n\n          break;\n        } else {\n          if (prevOutOfRangeColorStop) {\n            newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0)); // Reset\n\n            prevOutOfRangeColorStop = null;\n          }\n\n          newColorStops.push(stop_1);\n          prevInRangeColorStop = stop_1;\n        }\n      }\n\n      return newColorStops;\n    }\n\n    function getVisualGradient(data, coordSys, api) {\n      var visualMetaList = data.getVisual('visualMeta');\n\n      if (!visualMetaList || !visualMetaList.length || !data.count()) {\n        // When data.count() is 0, gradient range can not be calculated.\n        return;\n      }\n\n      if (coordSys.type !== 'cartesian2d') {\n        if (\"development\" !== 'production') {\n          console.warn('Visual map on line style is only supported on cartesian2d.');\n        }\n\n        return;\n      }\n\n      var coordDim;\n      var visualMeta;\n\n      for (var i = visualMetaList.length - 1; i >= 0; i--) {\n        var dimInfo = data.getDimensionInfo(visualMetaList[i].dimension);\n        coordDim = dimInfo && dimInfo.coordDim; // Can only be x or y\n\n        if (coordDim === 'x' || coordDim === 'y') {\n          visualMeta = visualMetaList[i];\n          break;\n        }\n      }\n\n      if (!visualMeta) {\n        if (\"development\" !== 'production') {\n          console.warn('Visual map on line style only support x or y dimension.');\n        }\n\n        return;\n      } // If the area to be rendered is bigger than area defined by LinearGradient,\n      // the canvas spec prescribes that the color of the first stop and the last\n      // stop should be used. But if two stops are added at offset 0, in effect\n      // browsers use the color of the second stop to render area outside\n      // LinearGradient. So we can only infinitesimally extend area defined in\n      // LinearGradient to render `outerColors`.\n\n\n      var axis = coordSys.getAxis(coordDim); // dataToCoord mapping may not be linear, but must be monotonic.\n\n      var colorStops = map(visualMeta.stops, function (stop) {\n        // offset will be calculated later.\n        return {\n          coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),\n          color: stop.color\n        };\n      });\n      var stopLen = colorStops.length;\n      var outerColors = visualMeta.outerColors.slice();\n\n      if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {\n        colorStops.reverse();\n        outerColors.reverse();\n      }\n\n      var colorStopsInRange = clipColorStops(colorStops, coordDim === 'x' ? api.getWidth() : api.getHeight());\n      var inRangeStopLen = colorStopsInRange.length;\n\n      if (!inRangeStopLen && stopLen) {\n        // All stops are out of range. All will be the same color.\n        return colorStops[0].coord < 0 ? outerColors[1] ? outerColors[1] : colorStops[stopLen - 1].color : outerColors[0] ? outerColors[0] : colorStops[0].color;\n      }\n\n      var tinyExtent = 10; // Arbitrary value: 10px\n\n      var minCoord = colorStopsInRange[0].coord - tinyExtent;\n      var maxCoord = colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent;\n      var coordSpan = maxCoord - minCoord;\n\n      if (coordSpan < 1e-3) {\n        return 'transparent';\n      }\n\n      each(colorStopsInRange, function (stop) {\n        stop.offset = (stop.coord - minCoord) / coordSpan;\n      });\n      colorStopsInRange.push({\n        // NOTE: inRangeStopLen may still be 0 if stoplen is zero.\n        offset: inRangeStopLen ? colorStopsInRange[inRangeStopLen - 1].offset : 0.5,\n        color: outerColors[1] || 'transparent'\n      });\n      colorStopsInRange.unshift({\n        offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5,\n        color: outerColors[0] || 'transparent'\n      });\n      var gradient = new LinearGradient(0, 0, 0, 0, colorStopsInRange, true);\n      gradient[coordDim] = minCoord;\n      gradient[coordDim + '2'] = maxCoord;\n      return gradient;\n    }\n\n    function getIsIgnoreFunc(seriesModel, data, coordSys) {\n      var showAllSymbol = seriesModel.get('showAllSymbol');\n      var isAuto = showAllSymbol === 'auto';\n\n      if (showAllSymbol && !isAuto) {\n        return;\n      }\n\n      var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n\n      if (!categoryAxis) {\n        return;\n      } // Note that category label interval strategy might bring some weird effect\n      // in some scenario: users may wonder why some of the symbols are not\n      // displayed. So we show all symbols as possible as we can.\n\n\n      if (isAuto // Simplify the logic, do not determine label overlap here.\n      && canShowAllSymbolForCategory(categoryAxis, data)) {\n        return;\n      } // Otherwise follow the label interval strategy on category axis.\n\n\n      var categoryDataDim = data.mapDimension(categoryAxis.dim);\n      var labelMap = {};\n      each(categoryAxis.getViewLabels(), function (labelItem) {\n        var ordinalNumber = categoryAxis.scale.getRawOrdinalNumber(labelItem.tickValue);\n        labelMap[ordinalNumber] = 1;\n      });\n      return function (dataIndex) {\n        return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex));\n      };\n    }\n\n    function canShowAllSymbolForCategory(categoryAxis, data) {\n      // In most cases, line is monotonous on category axis, and the label size\n      // is close with each other. So we check the symbol size and some of the\n      // label size alone with the category axis to estimate whether all symbol\n      // can be shown without overlap.\n      var axisExtent = categoryAxis.getExtent();\n      var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count();\n      isNaN(availSize) && (availSize = 0); // 0/0 is NaN.\n      // Sampling some points, max 5.\n\n      var dataLen = data.count();\n      var step = Math.max(1, Math.round(dataLen / 5));\n\n      for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) {\n        if (Symbol.getSymbolSize(data, dataIndex // Only for cartesian, where `isHorizontal` exists.\n        )[categoryAxis.isHorizontal() ? 1 : 0] // Empirical number\n        * 1.5 > availSize) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    function isPointNull$1(x, y) {\n      return isNaN(x) || isNaN(y);\n    }\n\n    function getLastIndexNotNull(points) {\n      var len = points.length / 2;\n\n      for (; len > 0; len--) {\n        if (!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])) {\n          break;\n        }\n      }\n\n      return len - 1;\n    }\n\n    function getPointAtIndex(points, idx) {\n      return [points[idx * 2], points[idx * 2 + 1]];\n    }\n\n    function getIndexRange(points, xOrY, dim) {\n      var len = points.length / 2;\n      var dimIdx = dim === 'x' ? 0 : 1;\n      var a;\n      var b;\n      var prevIndex = 0;\n      var nextIndex = -1;\n\n      for (var i = 0; i < len; i++) {\n        b = points[i * 2 + dimIdx];\n\n        if (isNaN(b) || isNaN(points[i * 2 + 1 - dimIdx])) {\n          continue;\n        }\n\n        if (i === 0) {\n          a = b;\n          continue;\n        }\n\n        if (a <= xOrY && b >= xOrY || a >= xOrY && b <= xOrY) {\n          nextIndex = i;\n          break;\n        }\n\n        prevIndex = i;\n        a = b;\n      }\n\n      return {\n        range: [prevIndex, nextIndex],\n        t: (xOrY - a) / (b - a)\n      };\n    }\n\n    function anyStateShowEndLabel(seriesModel) {\n      if (seriesModel.get(['endLabel', 'show'])) {\n        return true;\n      }\n\n      for (var i = 0; i < SPECIAL_STATES.length; i++) {\n        if (seriesModel.get([SPECIAL_STATES[i], 'endLabel', 'show'])) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    function createLineClipPath(lineView, coordSys, hasAnimation, seriesModel) {\n      if (isCoordinateSystemType(coordSys, 'cartesian2d')) {\n        var endLabelModel_1 = seriesModel.getModel('endLabel');\n        var valueAnimation_1 = endLabelModel_1.get('valueAnimation');\n        var data_1 = seriesModel.getData();\n        var labelAnimationRecord_1 = {\n          lastFrameIndex: 0\n        };\n        var during = anyStateShowEndLabel(seriesModel) ? function (percent, clipRect) {\n          lineView._endLabelOnDuring(percent, clipRect, data_1, labelAnimationRecord_1, valueAnimation_1, endLabelModel_1, coordSys);\n        } : null;\n        var isHorizontal = coordSys.getBaseAxis().isHorizontal();\n        var clipPath = createGridClipPath(coordSys, hasAnimation, seriesModel, function () {\n          var endLabel = lineView._endLabel;\n\n          if (endLabel && hasAnimation) {\n            if (labelAnimationRecord_1.originalX != null) {\n              endLabel.attr({\n                x: labelAnimationRecord_1.originalX,\n                y: labelAnimationRecord_1.originalY\n              });\n            }\n          }\n        }, during); // Expand clip shape to avoid clipping when line value exceeds axis\n\n        if (!seriesModel.get('clip', true)) {\n          var rectShape = clipPath.shape;\n          var expandSize = Math.max(rectShape.width, rectShape.height);\n\n          if (isHorizontal) {\n            rectShape.y -= expandSize;\n            rectShape.height += expandSize * 2;\n          } else {\n            rectShape.x -= expandSize;\n            rectShape.width += expandSize * 2;\n          }\n        } // Set to the final frame. To make sure label layout is right.\n\n\n        if (during) {\n          during(1, clipPath);\n        }\n\n        return clipPath;\n      } else {\n        if (\"development\" !== 'production') {\n          if (seriesModel.get(['endLabel', 'show'])) {\n            console.warn('endLabel is not supported for lines in polar systems.');\n          }\n        }\n\n        return createPolarClipPath(coordSys, hasAnimation, seriesModel);\n      }\n    }\n\n    function getEndLabelStateSpecified(endLabelModel, coordSys) {\n      var baseAxis = coordSys.getBaseAxis();\n      var isHorizontal = baseAxis.isHorizontal();\n      var isBaseInversed = baseAxis.inverse;\n      var align = isHorizontal ? isBaseInversed ? 'right' : 'left' : 'center';\n      var verticalAlign = isHorizontal ? 'middle' : isBaseInversed ? 'top' : 'bottom';\n      return {\n        normal: {\n          align: endLabelModel.get('align') || align,\n          verticalAlign: endLabelModel.get('verticalAlign') || verticalAlign\n        }\n      };\n    }\n\n    var LineView =\n    /** @class */\n    function (_super) {\n      __extends(LineView, _super);\n\n      function LineView() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      LineView.prototype.init = function () {\n        var lineGroup = new Group();\n        var symbolDraw = new SymbolDraw();\n        this.group.add(symbolDraw.group);\n        this._symbolDraw = symbolDraw;\n        this._lineGroup = lineGroup;\n      };\n\n      LineView.prototype.render = function (seriesModel, ecModel, api) {\n        var _this = this;\n\n        var coordSys = seriesModel.coordinateSystem;\n        var group = this.group;\n        var data = seriesModel.getData();\n        var lineStyleModel = seriesModel.getModel('lineStyle');\n        var areaStyleModel = seriesModel.getModel('areaStyle');\n        var points = data.getLayout('points') || [];\n        var isCoordSysPolar = coordSys.type === 'polar';\n        var prevCoordSys = this._coordSys;\n        var symbolDraw = this._symbolDraw;\n        var polyline = this._polyline;\n        var polygon = this._polygon;\n        var lineGroup = this._lineGroup;\n        var hasAnimation = seriesModel.get('animation');\n        var isAreaChart = !areaStyleModel.isEmpty();\n        var valueOrigin = areaStyleModel.get('origin');\n        var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin);\n        var stackedOnPoints = isAreaChart && getStackedOnPoints(coordSys, data, dataCoordInfo);\n        var showSymbol = seriesModel.get('showSymbol');\n        var connectNulls = seriesModel.get('connectNulls');\n        var isIgnoreFunc = showSymbol && !isCoordSysPolar && getIsIgnoreFunc(seriesModel, data, coordSys); // Remove temporary symbols\n\n        var oldData = this._data;\n        oldData && oldData.eachItemGraphicEl(function (el, idx) {\n          if (el.__temp) {\n            group.remove(el);\n            oldData.setItemGraphicEl(idx, null);\n          }\n        }); // Remove previous created symbols if showSymbol changed to false\n\n        if (!showSymbol) {\n          symbolDraw.remove();\n        }\n\n        group.add(lineGroup); // FIXME step not support polar\n\n        var step = !isCoordSysPolar ? seriesModel.get('step') : false;\n        var clipShapeForSymbol;\n\n        if (coordSys && coordSys.getArea && seriesModel.get('clip', true)) {\n          clipShapeForSymbol = coordSys.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent.\n          // See #7913 and `test/dataZoom-clip.html`.\n\n          if (clipShapeForSymbol.width != null) {\n            clipShapeForSymbol.x -= 0.1;\n            clipShapeForSymbol.y -= 0.1;\n            clipShapeForSymbol.width += 0.2;\n            clipShapeForSymbol.height += 0.2;\n          } else if (clipShapeForSymbol.r0) {\n            clipShapeForSymbol.r0 -= 0.5;\n            clipShapeForSymbol.r += 0.5;\n          }\n        }\n\n        this._clipShapeForSymbol = clipShapeForSymbol;\n        var visualColor = getVisualGradient(data, coordSys, api) || data.getVisual('style')[data.getVisual('drawType')]; // Initialization animation or coordinate system changed\n\n        if (!(polyline && prevCoordSys.type === coordSys.type && step === this._step)) {\n          showSymbol && symbolDraw.updateData(data, {\n            isIgnore: isIgnoreFunc,\n            clipShape: clipShapeForSymbol,\n            disableAnimation: true,\n            getSymbolPoint: function (idx) {\n              return [points[idx * 2], points[idx * 2 + 1]];\n            }\n          });\n          hasAnimation && this._initSymbolLabelAnimation(data, coordSys, clipShapeForSymbol);\n\n          if (step) {\n            // TODO If stacked series is not step\n            points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n            if (stackedOnPoints) {\n              stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n            }\n          }\n\n          polyline = this._newPolyline(points);\n\n          if (isAreaChart) {\n            polygon = this._newPolygon(points, stackedOnPoints);\n          } // If areaStyle is removed\n          else if (polygon) {\n              lineGroup.remove(polygon);\n              polygon = this._polygon = null;\n            } // NOTE: Must update _endLabel before setClipPath.\n\n\n          if (!isCoordSysPolar) {\n            this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n          }\n\n          lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n        } else {\n          if (isAreaChart && !polygon) {\n            // If areaStyle is added\n            polygon = this._newPolygon(points, stackedOnPoints);\n          } else if (polygon && !isAreaChart) {\n            // If areaStyle is removed\n            lineGroup.remove(polygon);\n            polygon = this._polygon = null;\n          } // NOTE: Must update _endLabel before setClipPath.\n\n\n          if (!isCoordSysPolar) {\n            this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor));\n          } // Update clipPath\n\n\n          var oldClipPath = lineGroup.getClipPath();\n\n          if (oldClipPath) {\n            var newClipPath = createLineClipPath(this, coordSys, false, seriesModel);\n            initProps(oldClipPath, {\n              shape: newClipPath.shape\n            }, seriesModel);\n          } else {\n            lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel));\n          } // Always update, or it is wrong in the case turning on legend\n          // because points are not changed.\n\n\n          showSymbol && symbolDraw.updateData(data, {\n            isIgnore: isIgnoreFunc,\n            clipShape: clipShapeForSymbol,\n            disableAnimation: true,\n            getSymbolPoint: function (idx) {\n              return [points[idx * 2], points[idx * 2 + 1]];\n            }\n          }); // In the case data zoom triggered refreshing frequently\n          // Data may not change if line has a category axis. So it should animate nothing.\n\n          if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) || !isPointsSame(this._points, points)) {\n            if (hasAnimation) {\n              this._doUpdateAnimation(data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls);\n            } else {\n              // Not do it in update with animation\n              if (step) {\n                // TODO If stacked series is not step\n                points = turnPointsIntoStep(points, coordSys, step, connectNulls);\n\n                if (stackedOnPoints) {\n                  stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls);\n                }\n              }\n\n              polyline.setShape({\n                points: points\n              });\n              polygon && polygon.setShape({\n                points: points,\n                stackedOnPoints: stackedOnPoints\n              });\n            }\n          }\n        }\n\n        var emphasisModel = seriesModel.getModel('emphasis');\n        var focus = emphasisModel.get('focus');\n        var blurScope = emphasisModel.get('blurScope');\n        var emphasisDisabled = emphasisModel.get('disabled');\n        polyline.useStyle(defaults( // Use color in lineStyle first\n        lineStyleModel.getLineStyle(), {\n          fill: 'none',\n          stroke: visualColor,\n          lineJoin: 'bevel'\n        }));\n        setStatesStylesFromModel(polyline, seriesModel, 'lineStyle');\n\n        if (polyline.style.lineWidth > 0 && seriesModel.get(['emphasis', 'lineStyle', 'width']) === 'bolder') {\n          var emphasisLineStyle = polyline.getState('emphasis').style;\n          emphasisLineStyle.lineWidth = +polyline.style.lineWidth + 1;\n        } // Needs seriesIndex for focus\n\n\n        getECData(polyline).seriesIndex = seriesModel.seriesIndex;\n        toggleHoverEmphasis(polyline, focus, blurScope, emphasisDisabled);\n        var smooth = getSmooth(seriesModel.get('smooth'));\n        var smoothMonotone = seriesModel.get('smoothMonotone');\n        polyline.setShape({\n          smooth: smooth,\n          smoothMonotone: smoothMonotone,\n          connectNulls: connectNulls\n        });\n\n        if (polygon) {\n          var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');\n          var stackedOnSmooth = 0;\n          polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), {\n            fill: visualColor,\n            opacity: 0.7,\n            lineJoin: 'bevel',\n            decal: data.getVisual('style').decal\n          }));\n\n          if (stackedOnSeries) {\n            stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));\n          }\n\n          polygon.setShape({\n            smooth: smooth,\n            stackedOnSmooth: stackedOnSmooth,\n            smoothMonotone: smoothMonotone,\n            connectNulls: connectNulls\n          });\n          setStatesStylesFromModel(polygon, seriesModel, 'areaStyle'); // Needs seriesIndex for focus\n\n          getECData(polygon).seriesIndex = seriesModel.seriesIndex;\n          toggleHoverEmphasis(polygon, focus, blurScope, emphasisDisabled);\n        }\n\n        var changePolyState = function (toState) {\n          _this._changePolyState(toState);\n        };\n\n        data.eachItemGraphicEl(function (el) {\n          // Switch polyline / polygon state if element changed its state.\n          el && (el.onHoverStateChange = changePolyState);\n        });\n        this._polyline.onHoverStateChange = changePolyState;\n        this._data = data; // Save the coordinate system for transition animation when data changed\n\n        this._coordSys = coordSys;\n        this._stackedOnPoints = stackedOnPoints;\n        this._points = points;\n        this._step = step;\n        this._valueOrigin = valueOrigin;\n\n        if (seriesModel.get('triggerLineEvent')) {\n          this.packEventData(seriesModel, polyline);\n          polygon && this.packEventData(seriesModel, polygon);\n        }\n      };\n\n      LineView.prototype.packEventData = function (seriesModel, el) {\n        getECData(el).eventData = {\n          componentType: 'series',\n          componentSubType: 'line',\n          componentIndex: seriesModel.componentIndex,\n          seriesIndex: seriesModel.seriesIndex,\n          seriesName: seriesModel.name,\n          seriesType: 'line'\n        };\n      };\n\n      LineView.prototype.highlight = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var dataIndex = queryDataIndex(data, payload);\n\n        this._changePolyState('emphasis');\n\n        if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {\n          var points = data.getLayout('points');\n          var symbol = data.getItemGraphicEl(dataIndex);\n\n          if (!symbol) {\n            // Create a temporary symbol if it is not exists\n            var x = points[dataIndex * 2];\n            var y = points[dataIndex * 2 + 1];\n\n            if (isNaN(x) || isNaN(y)) {\n              // Null data\n              return;\n            } // fix #11360: shouldn't draw symbol outside clipShapeForSymbol\n\n\n            if (this._clipShapeForSymbol && !this._clipShapeForSymbol.contain(x, y)) {\n              return;\n            }\n\n            var zlevel = seriesModel.get('zlevel') || 0;\n            var z = seriesModel.get('z') || 0;\n            symbol = new Symbol(data, dataIndex);\n            symbol.x = x;\n            symbol.y = y;\n            symbol.setZ(zlevel, z); // ensure label text of the temporary symbol is in front of line and area polygon\n\n            var symbolLabel = symbol.getSymbolPath().getTextContent();\n\n            if (symbolLabel) {\n              symbolLabel.zlevel = zlevel;\n              symbolLabel.z = z;\n              symbolLabel.z2 = this._polyline.z2 + 1;\n            }\n\n            symbol.__temp = true;\n            data.setItemGraphicEl(dataIndex, symbol); // Stop scale animation\n\n            symbol.stopSymbolAnimation(true);\n            this.group.add(symbol);\n          }\n\n          symbol.highlight();\n        } else {\n          // Highlight whole series\n          ChartView.prototype.highlight.call(this, seriesModel, ecModel, api, payload);\n        }\n      };\n\n      LineView.prototype.downplay = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var dataIndex = queryDataIndex(data, payload);\n\n        this._changePolyState('normal');\n\n        if (dataIndex != null && dataIndex >= 0) {\n          var symbol = data.getItemGraphicEl(dataIndex);\n\n          if (symbol) {\n            if (symbol.__temp) {\n              data.setItemGraphicEl(dataIndex, null);\n              this.group.remove(symbol);\n            } else {\n              symbol.downplay();\n            }\n          }\n        } else {\n          // FIXME\n          // can not downplay completely.\n          // Downplay whole series\n          ChartView.prototype.downplay.call(this, seriesModel, ecModel, api, payload);\n        }\n      };\n\n      LineView.prototype._changePolyState = function (toState) {\n        var polygon = this._polygon;\n        setStatesFlag(this._polyline, toState);\n        polygon && setStatesFlag(polygon, toState);\n      };\n\n      LineView.prototype._newPolyline = function (points) {\n        var polyline = this._polyline; // Remove previous created polyline\n\n        if (polyline) {\n          this._lineGroup.remove(polyline);\n        }\n\n        polyline = new ECPolyline({\n          shape: {\n            points: points\n          },\n          segmentIgnoreThreshold: 2,\n          z2: 10\n        });\n\n        this._lineGroup.add(polyline);\n\n        this._polyline = polyline;\n        return polyline;\n      };\n\n      LineView.prototype._newPolygon = function (points, stackedOnPoints) {\n        var polygon = this._polygon; // Remove previous created polygon\n\n        if (polygon) {\n          this._lineGroup.remove(polygon);\n        }\n\n        polygon = new ECPolygon({\n          shape: {\n            points: points,\n            stackedOnPoints: stackedOnPoints\n          },\n          segmentIgnoreThreshold: 2\n        });\n\n        this._lineGroup.add(polygon);\n\n        this._polygon = polygon;\n        return polygon;\n      };\n\n      LineView.prototype._initSymbolLabelAnimation = function (data, coordSys, clipShape) {\n        var isHorizontalOrRadial;\n        var isCoordSysPolar;\n        var baseAxis = coordSys.getBaseAxis();\n        var isAxisInverse = baseAxis.inverse;\n\n        if (coordSys.type === 'cartesian2d') {\n          isHorizontalOrRadial = baseAxis.isHorizontal();\n          isCoordSysPolar = false;\n        } else if (coordSys.type === 'polar') {\n          isHorizontalOrRadial = baseAxis.dim === 'angle';\n          isCoordSysPolar = true;\n        }\n\n        var seriesModel = data.hostModel;\n        var seriesDuration = seriesModel.get('animationDuration');\n\n        if (isFunction(seriesDuration)) {\n          seriesDuration = seriesDuration(null);\n        }\n\n        var seriesDalay = seriesModel.get('animationDelay') || 0;\n        var seriesDalayValue = isFunction(seriesDalay) ? seriesDalay(null) : seriesDalay;\n        data.eachItemGraphicEl(function (symbol, idx) {\n          var el = symbol;\n\n          if (el) {\n            var point = [symbol.x, symbol.y];\n            var start = void 0;\n            var end = void 0;\n            var current = void 0;\n\n            if (clipShape) {\n              if (isCoordSysPolar) {\n                var polarClip = clipShape;\n                var coord = coordSys.pointToCoord(point);\n\n                if (isHorizontalOrRadial) {\n                  start = polarClip.startAngle;\n                  end = polarClip.endAngle;\n                  current = -coord[1] / 180 * Math.PI;\n                } else {\n                  start = polarClip.r0;\n                  end = polarClip.r;\n                  current = coord[0];\n                }\n              } else {\n                var gridClip = clipShape;\n\n                if (isHorizontalOrRadial) {\n                  start = gridClip.x;\n                  end = gridClip.x + gridClip.width;\n                  current = symbol.x;\n                } else {\n                  start = gridClip.y + gridClip.height;\n                  end = gridClip.y;\n                  current = symbol.y;\n                }\n              }\n            }\n\n            var ratio = end === start ? 0 : (current - start) / (end - start);\n\n            if (isAxisInverse) {\n              ratio = 1 - ratio;\n            }\n\n            var delay = isFunction(seriesDalay) ? seriesDalay(idx) : seriesDuration * ratio + seriesDalayValue;\n            var symbolPath = el.getSymbolPath();\n            var text = symbolPath.getTextContent();\n            el.attr({\n              scaleX: 0,\n              scaleY: 0\n            });\n            el.animateTo({\n              scaleX: 1,\n              scaleY: 1\n            }, {\n              duration: 200,\n              setToFinal: true,\n              delay: delay\n            });\n\n            if (text) {\n              text.animateFrom({\n                style: {\n                  opacity: 0\n                }\n              }, {\n                duration: 300,\n                delay: delay\n              });\n            }\n\n            symbolPath.disableLabelAnimation = true;\n          }\n        });\n      };\n\n      LineView.prototype._initOrUpdateEndLabel = function (seriesModel, coordSys, inheritColor) {\n        var endLabelModel = seriesModel.getModel('endLabel');\n\n        if (anyStateShowEndLabel(seriesModel)) {\n          var data_2 = seriesModel.getData();\n          var polyline = this._polyline; // series may be filtered.\n\n          var points = data_2.getLayout('points');\n\n          if (!points) {\n            polyline.removeTextContent();\n            this._endLabel = null;\n            return;\n          }\n\n          var endLabel = this._endLabel;\n\n          if (!endLabel) {\n            endLabel = this._endLabel = new ZRText({\n              z2: 200 // should be higher than item symbol\n\n            });\n            endLabel.ignoreClip = true;\n            polyline.setTextContent(this._endLabel);\n            polyline.disableLabelAnimation = true;\n          } // Find last non-NaN data to display data\n\n\n          var dataIndex = getLastIndexNotNull(points);\n\n          if (dataIndex >= 0) {\n            setLabelStyle(polyline, getLabelStatesModels(seriesModel, 'endLabel'), {\n              inheritColor: inheritColor,\n              labelFetcher: seriesModel,\n              labelDataIndex: dataIndex,\n              defaultText: function (dataIndex, opt, interpolatedValue) {\n                return interpolatedValue != null ? getDefaultInterpolatedLabel(data_2, interpolatedValue) : getDefaultLabel(data_2, dataIndex);\n              },\n              enableTextSetter: true\n            }, getEndLabelStateSpecified(endLabelModel, coordSys));\n            polyline.textConfig.position = null;\n          }\n        } else if (this._endLabel) {\n          this._polyline.removeTextContent();\n\n          this._endLabel = null;\n        }\n      };\n\n      LineView.prototype._endLabelOnDuring = function (percent, clipRect, data, animationRecord, valueAnimation, endLabelModel, coordSys) {\n        var endLabel = this._endLabel;\n        var polyline = this._polyline;\n\n        if (endLabel) {\n          // NOTE: Don't remove percent < 1. percent === 1 means the first frame during render.\n          // The label is not prepared at this time.\n          if (percent < 1 && animationRecord.originalX == null) {\n            animationRecord.originalX = endLabel.x;\n            animationRecord.originalY = endLabel.y;\n          }\n\n          var points = data.getLayout('points');\n          var seriesModel = data.hostModel;\n          var connectNulls = seriesModel.get('connectNulls');\n          var precision = endLabelModel.get('precision');\n          var distance = endLabelModel.get('distance') || 0;\n          var baseAxis = coordSys.getBaseAxis();\n          var isHorizontal = baseAxis.isHorizontal();\n          var isBaseInversed = baseAxis.inverse;\n          var clipShape = clipRect.shape;\n          var xOrY = isBaseInversed ? isHorizontal ? clipShape.x : clipShape.y + clipShape.height : isHorizontal ? clipShape.x + clipShape.width : clipShape.y;\n          var distanceX = (isHorizontal ? distance : 0) * (isBaseInversed ? -1 : 1);\n          var distanceY = (isHorizontal ? 0 : -distance) * (isBaseInversed ? -1 : 1);\n          var dim = isHorizontal ? 'x' : 'y';\n          var dataIndexRange = getIndexRange(points, xOrY, dim);\n          var indices = dataIndexRange.range;\n          var diff = indices[1] - indices[0];\n          var value = void 0;\n\n          if (diff >= 1) {\n            // diff > 1 && connectNulls, which is on the null data.\n            if (diff > 1 && !connectNulls) {\n              var pt = getPointAtIndex(points, indices[0]);\n              endLabel.attr({\n                x: pt[0] + distanceX,\n                y: pt[1] + distanceY\n              });\n              valueAnimation && (value = seriesModel.getRawValue(indices[0]));\n            } else {\n              var pt = polyline.getPointOn(xOrY, dim);\n              pt && endLabel.attr({\n                x: pt[0] + distanceX,\n                y: pt[1] + distanceY\n              });\n              var startValue = seriesModel.getRawValue(indices[0]);\n              var endValue = seriesModel.getRawValue(indices[1]);\n              valueAnimation && (value = interpolateRawValues(data, precision, startValue, endValue, dataIndexRange.t));\n            }\n\n            animationRecord.lastFrameIndex = indices[0];\n          } else {\n            // If diff <= 0, which is the range is not found(Include NaN)\n            // Choose the first point or last point.\n            var idx = percent === 1 || animationRecord.lastFrameIndex > 0 ? indices[0] : 0;\n            var pt = getPointAtIndex(points, idx);\n            valueAnimation && (value = seriesModel.getRawValue(idx));\n            endLabel.attr({\n              x: pt[0] + distanceX,\n              y: pt[1] + distanceY\n            });\n          }\n\n          if (valueAnimation) {\n            labelInner(endLabel).setLabelText(value);\n          }\n        }\n      };\n      /**\n       * @private\n       */\n      // FIXME Two value axis\n\n\n      LineView.prototype._doUpdateAnimation = function (data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls) {\n        var polyline = this._polyline;\n        var polygon = this._polygon;\n        var seriesModel = data.hostModel;\n        var diff = lineAnimationDiff(this._data, data, this._stackedOnPoints, stackedOnPoints, this._coordSys, coordSys, this._valueOrigin);\n        var current = diff.current;\n        var stackedOnCurrent = diff.stackedOnCurrent;\n        var next = diff.next;\n        var stackedOnNext = diff.stackedOnNext;\n\n        if (step) {\n          // TODO If stacked series is not step\n          current = turnPointsIntoStep(diff.current, coordSys, step, connectNulls);\n          stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step, connectNulls);\n          next = turnPointsIntoStep(diff.next, coordSys, step, connectNulls);\n          stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step, connectNulls);\n        } // Don't apply animation if diff is large.\n        // For better result and avoid memory explosion problems like\n        // https://github.com/apache/incubator-echarts/issues/12229\n\n\n        if (getBoundingDiff(current, next) > 3000 || polygon && getBoundingDiff(stackedOnCurrent, stackedOnNext) > 3000) {\n          polyline.stopAnimation();\n          polyline.setShape({\n            points: next\n          });\n\n          if (polygon) {\n            polygon.stopAnimation();\n            polygon.setShape({\n              points: next,\n              stackedOnPoints: stackedOnNext\n            });\n          }\n\n          return;\n        }\n\n        polyline.shape.__points = diff.current;\n        polyline.shape.points = current;\n        var target = {\n          shape: {\n            points: next\n          }\n        }; // Also animate the original points.\n        // If points reference is changed when turning into step line.\n\n        if (diff.current !== current) {\n          target.shape.__points = diff.next;\n        } // Stop previous animation.\n\n\n        polyline.stopAnimation();\n        updateProps(polyline, target, seriesModel);\n\n        if (polygon) {\n          polygon.setShape({\n            // Reuse the points with polyline.\n            points: current,\n            stackedOnPoints: stackedOnCurrent\n          });\n          polygon.stopAnimation();\n          updateProps(polygon, {\n            shape: {\n              stackedOnPoints: stackedOnNext\n            }\n          }, seriesModel); // If use attr directly in updateProps.\n\n          if (polyline.shape.points !== polygon.shape.points) {\n            polygon.shape.points = polyline.shape.points;\n          }\n        }\n\n        var updatedDataInfo = [];\n        var diffStatus = diff.status;\n\n        for (var i = 0; i < diffStatus.length; i++) {\n          var cmd = diffStatus[i].cmd;\n\n          if (cmd === '=') {\n            var el = data.getItemGraphicEl(diffStatus[i].idx1);\n\n            if (el) {\n              updatedDataInfo.push({\n                el: el,\n                ptIdx: i // Index of points\n\n              });\n            }\n          }\n        }\n\n        if (polyline.animators && polyline.animators.length) {\n          polyline.animators[0].during(function () {\n            polygon && polygon.dirtyShape();\n            var points = polyline.shape.__points;\n\n            for (var i = 0; i < updatedDataInfo.length; i++) {\n              var el = updatedDataInfo[i].el;\n              var offset = updatedDataInfo[i].ptIdx * 2;\n              el.x = points[offset];\n              el.y = points[offset + 1];\n              el.markRedraw();\n            }\n          });\n        }\n      };\n\n      LineView.prototype.remove = function (ecModel) {\n        var group = this.group;\n        var oldData = this._data;\n\n        this._lineGroup.removeAll();\n\n        this._symbolDraw.remove(true); // Remove temporary created elements when highlighting\n\n\n        oldData && oldData.eachItemGraphicEl(function (el, idx) {\n          if (el.__temp) {\n            group.remove(el);\n            oldData.setItemGraphicEl(idx, null);\n          }\n        });\n        this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._endLabel = this._data = null;\n      };\n\n      LineView.type = 'line';\n      return LineView;\n    }(ChartView);\n\n    function pointsLayout(seriesType, forceStoreInTypedArray) {\n      return {\n        seriesType: seriesType,\n        plan: createRenderPlanner(),\n        reset: function (seriesModel) {\n          var data = seriesModel.getData();\n          var coordSys = seriesModel.coordinateSystem;\n          var pipelineContext = seriesModel.pipelineContext;\n          var useTypedArray = forceStoreInTypedArray || pipelineContext.large;\n\n          if (!coordSys) {\n            return;\n          }\n\n          var dims = map(coordSys.dimensions, function (dim) {\n            return data.mapDimension(dim);\n          }).slice(0, 2);\n          var dimLen = dims.length;\n          var stackResultDim = data.getCalculationInfo('stackResultDimension');\n\n          if (isDimensionStacked(data, dims[0])) {\n            dims[0] = stackResultDim;\n          }\n\n          if (isDimensionStacked(data, dims[1])) {\n            dims[1] = stackResultDim;\n          }\n\n          var store = data.getStore();\n          var dimIdx0 = data.getDimensionIndex(dims[0]);\n          var dimIdx1 = data.getDimensionIndex(dims[1]);\n          return dimLen && {\n            progress: function (params, data) {\n              var segCount = params.end - params.start;\n              var points = useTypedArray && createFloat32Array(segCount * dimLen);\n              var tmpIn = [];\n              var tmpOut = [];\n\n              for (var i = params.start, offset = 0; i < params.end; i++) {\n                var point = void 0;\n\n                if (dimLen === 1) {\n                  var x = store.get(dimIdx0, i); // NOTE: Make sure the second parameter is null to use default strategy.\n\n                  point = coordSys.dataToPoint(x, null, tmpOut);\n                } else {\n                  tmpIn[0] = store.get(dimIdx0, i);\n                  tmpIn[1] = store.get(dimIdx1, i); // Let coordinate system to handle the NaN data.\n\n                  point = coordSys.dataToPoint(tmpIn, null, tmpOut);\n                }\n\n                if (useTypedArray) {\n                  points[offset++] = point[0];\n                  points[offset++] = point[1];\n                } else {\n                  data.setItemLayout(i, point.slice());\n                }\n              }\n\n              useTypedArray && data.setLayout('points', points);\n            }\n          };\n        }\n      };\n    }\n\n    var samplers = {\n      average: function (frame) {\n        var sum = 0;\n        var count = 0;\n\n        for (var i = 0; i < frame.length; i++) {\n          if (!isNaN(frame[i])) {\n            sum += frame[i];\n            count++;\n          }\n        } // Return NaN if count is 0\n\n\n        return count === 0 ? NaN : sum / count;\n      },\n      sum: function (frame) {\n        var sum = 0;\n\n        for (var i = 0; i < frame.length; i++) {\n          // Ignore NaN\n          sum += frame[i] || 0;\n        }\n\n        return sum;\n      },\n      max: function (frame) {\n        var max = -Infinity;\n\n        for (var i = 0; i < frame.length; i++) {\n          frame[i] > max && (max = frame[i]);\n        } // NaN will cause illegal axis extent.\n\n\n        return isFinite(max) ? max : NaN;\n      },\n      min: function (frame) {\n        var min = Infinity;\n\n        for (var i = 0; i < frame.length; i++) {\n          frame[i] < min && (min = frame[i]);\n        } // NaN will cause illegal axis extent.\n\n\n        return isFinite(min) ? min : NaN;\n      },\n      // TODO\n      // Median\n      nearest: function (frame) {\n        return frame[0];\n      }\n    };\n\n    var indexSampler = function (frame) {\n      return Math.round(frame.length / 2);\n    };\n\n    function dataSample(seriesType) {\n      return {\n        seriesType: seriesType,\n        // FIXME:TS never used, so comment it\n        // modifyOutputEnd: true,\n        reset: function (seriesModel, ecModel, api) {\n          var data = seriesModel.getData();\n          var sampling = seriesModel.get('sampling');\n          var coordSys = seriesModel.coordinateSystem;\n          var count = data.count(); // Only cartesian2d support down sampling. Disable it when there is few data.\n\n          if (count > 10 && coordSys.type === 'cartesian2d' && sampling) {\n            var baseAxis = coordSys.getBaseAxis();\n            var valueAxis = coordSys.getOtherAxis(baseAxis);\n            var extent = baseAxis.getExtent();\n            var dpr = api.getDevicePixelRatio(); // Coordinste system has been resized\n\n            var size = Math.abs(extent[1] - extent[0]) * (dpr || 1);\n            var rate = Math.round(count / size);\n\n            if (isFinite(rate) && rate > 1) {\n              if (sampling === 'lttb') {\n                seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate));\n              }\n\n              var sampler = void 0;\n\n              if (isString(sampling)) {\n                sampler = samplers[sampling];\n              } else if (isFunction(sampling)) {\n                sampler = sampling;\n              }\n\n              if (sampler) {\n                // Only support sample the first dim mapped from value axis.\n                seriesModel.setData(data.downSample(data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler));\n              }\n            }\n          }\n        }\n      };\n    }\n\n    function install$1(registers) {\n      registers.registerChartView(LineView);\n      registers.registerSeriesModel(LineSeriesModel);\n      registers.registerLayout(pointsLayout('line', true));\n      registers.registerVisual({\n        seriesType: 'line',\n        reset: function (seriesModel) {\n          var data = seriesModel.getData(); // Visual coding for legend\n\n          var lineStyle = seriesModel.getModel('lineStyle').getLineStyle();\n\n          if (lineStyle && !lineStyle.stroke) {\n            // Fill in visual should be palette color if\n            // has color callback\n            lineStyle.stroke = data.getVisual('style').fill;\n          }\n\n          data.setVisual('legendLineStyle', lineStyle);\n        }\n      }); // Down sample after filter\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('line'));\n    }\n\n    var BaseBarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(BaseBarSeriesModel, _super);\n\n      function BaseBarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BaseBarSeriesModel.type;\n        return _this;\n      }\n\n      BaseBarSeriesModel.prototype.getInitialData = function (option, ecModel) {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true\n        });\n      };\n\n      BaseBarSeriesModel.prototype.getMarkerPosition = function (value, dims, startingAtTick) {\n        var coordSys = this.coordinateSystem;\n\n        if (coordSys && coordSys.clampData) {\n          // PENDING if clamp ?\n          var pt_1 = coordSys.dataToPoint(coordSys.clampData(value));\n\n          if (startingAtTick) {\n            each(coordSys.getAxes(), function (axis, idx) {\n              // If axis type is category, use tick coords instead\n              if (axis.type === 'category') {\n                var tickCoords = axis.getTicksCoords();\n                var tickIdx = coordSys.clampData(value)[idx]; // The index of rightmost tick of markArea is 1 larger than x1/y1 index\n\n                if (dims && (dims[idx] === 'x1' || dims[idx] === 'y1')) {\n                  tickIdx += 1;\n                }\n\n                tickIdx > tickCoords.length - 1 && (tickIdx = tickCoords.length - 1);\n                tickIdx < 0 && (tickIdx = 0);\n                tickCoords[tickIdx] && (pt_1[idx] = axis.toGlobalCoord(tickCoords[tickIdx].coord));\n              }\n            });\n          } else {\n            var data = this.getData();\n            var offset = data.getLayout('offset');\n            var size = data.getLayout('size');\n            var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;\n            pt_1[offsetIndex] += offset + size / 2;\n          }\n\n          return pt_1;\n        }\n\n        return [NaN, NaN];\n      };\n\n      BaseBarSeriesModel.type = 'series.__base_bar__';\n      BaseBarSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        coordinateSystem: 'cartesian2d',\n        legendHoverLink: true,\n        // stack: null\n        // Cartesian coordinate system\n        // xAxisIndex: 0,\n        // yAxisIndex: 0,\n        barMinHeight: 0,\n        barMinAngle: 0,\n        // cursor: null,\n        large: false,\n        largeThreshold: 400,\n        progressive: 3e3,\n        progressiveChunkMode: 'mod'\n      };\n      return BaseBarSeriesModel;\n    }(SeriesModel);\n\n    SeriesModel.registerClass(BaseBarSeriesModel);\n\n    var BarSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(BarSeriesModel, _super);\n\n      function BarSeriesModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = BarSeriesModel.type;\n        return _this;\n      }\n\n      BarSeriesModel.prototype.getInitialData = function () {\n        return createSeriesData(null, this, {\n          useEncodeDefaulter: true,\n          createInvertedIndices: !!this.get('realtimeSort', true) || null\n        });\n      };\n      /**\n       * @override\n       */\n\n\n      BarSeriesModel.prototype.getProgressive = function () {\n        // Do not support progressive in normal mode.\n        return this.get('large') ? this.get('progressive') : false;\n      };\n      /**\n       * @override\n       */\n\n\n      BarSeriesModel.prototype.getProgressiveThreshold = function () {\n        // Do not support progressive in normal mode.\n        var progressiveThreshold = this.get('progressiveThreshold');\n        var largeThreshold = this.get('largeThreshold');\n\n        if (largeThreshold > progressiveThreshold) {\n          progressiveThreshold = largeThreshold;\n        }\n\n        return progressiveThreshold;\n      };\n\n      BarSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) {\n        return selectors.rect(data.getItemLayout(dataIndex));\n      };\n\n      BarSeriesModel.type = 'series.bar';\n      BarSeriesModel.dependencies = ['grid', 'polar'];\n      BarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, {\n        // If clipped\n        // Only available on cartesian2d\n        clip: true,\n        roundCap: false,\n        showBackground: false,\n        backgroundStyle: {\n          color: 'rgba(180, 180, 180, 0.2)',\n          borderColor: null,\n          borderWidth: 0,\n          borderType: 'solid',\n          borderRadius: 0,\n          shadowBlur: 0,\n          shadowColor: null,\n          shadowOffsetX: 0,\n          shadowOffsetY: 0,\n          opacity: 1\n        },\n        select: {\n          itemStyle: {\n            borderColor: '#212121'\n          }\n        },\n        realtimeSort: false\n      });\n      return BarSeriesModel;\n    }(BaseBarSeriesModel);\n\n    /**\n     * Sausage: similar to sector, but have half circle on both sides\n     */\n\n    var SausageShape =\n    /** @class */\n    function () {\n      function SausageShape() {\n        this.cx = 0;\n        this.cy = 0;\n        this.r0 = 0;\n        this.r = 0;\n        this.startAngle = 0;\n        this.endAngle = Math.PI * 2;\n        this.clockwise = true;\n      }\n\n      return SausageShape;\n    }();\n\n    var SausagePath =\n    /** @class */\n    function (_super) {\n      __extends(SausagePath, _super);\n\n      function SausagePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'sausage';\n        return _this;\n      }\n\n      SausagePath.prototype.getDefaultShape = function () {\n        return new SausageShape();\n      };\n\n      SausagePath.prototype.buildPath = function (ctx, shape) {\n        var cx = shape.cx;\n        var cy = shape.cy;\n        var r0 = Math.max(shape.r0 || 0, 0);\n        var r = Math.max(shape.r, 0);\n        var dr = (r - r0) * 0.5;\n        var rCenter = r0 + dr;\n        var startAngle = shape.startAngle;\n        var endAngle = shape.endAngle;\n        var clockwise = shape.clockwise;\n        var PI2 = Math.PI * 2;\n        var lessThanCircle = clockwise ? endAngle - startAngle < PI2 : startAngle - endAngle < PI2;\n\n        if (!lessThanCircle) {\n          // Normalize angles\n          startAngle = endAngle - (clockwise ? PI2 : -PI2);\n        }\n\n        var unitStartX = Math.cos(startAngle);\n        var unitStartY = Math.sin(startAngle);\n        var unitEndX = Math.cos(endAngle);\n        var unitEndY = Math.sin(endAngle);\n\n        if (lessThanCircle) {\n          ctx.moveTo(unitStartX * r0 + cx, unitStartY * r0 + cy);\n          ctx.arc(unitStartX * rCenter + cx, unitStartY * rCenter + cy, dr, -Math.PI + startAngle, startAngle, !clockwise);\n        } else {\n          ctx.moveTo(unitStartX * r + cx, unitStartY * r + cy);\n        }\n\n        ctx.arc(cx, cy, r, startAngle, endAngle, !clockwise);\n        ctx.arc(unitEndX * rCenter + cx, unitEndY * rCenter + cy, dr, endAngle - Math.PI * 2, endAngle - Math.PI, !clockwise);\n\n        if (r0 !== 0) {\n          ctx.arc(cx, cy, r0, endAngle, startAngle, clockwise);\n        } // ctx.closePath();\n\n      };\n\n      return SausagePath;\n    }(Path);\n\n    function createSectorCalculateTextPosition(positionMapping, opts) {\n      opts = opts || {};\n      var isRoundCap = opts.isRoundCap;\n      return function (out, opts, boundingRect) {\n        var textPosition = opts.position;\n\n        if (!textPosition || textPosition instanceof Array) {\n          return calculateTextPosition(out, opts, boundingRect);\n        }\n\n        var mappedSectorPosition = positionMapping(textPosition);\n        var distance = opts.distance != null ? opts.distance : 5;\n        var sector = this.shape;\n        var cx = sector.cx;\n        var cy = sector.cy;\n        var r = sector.r;\n        var r0 = sector.r0;\n        var middleR = (r + r0) / 2;\n        var startAngle = sector.startAngle;\n        var endAngle = sector.endAngle;\n        var middleAngle = (startAngle + endAngle) / 2;\n        var extraDist = isRoundCap ? Math.abs(r - r0) / 2 : 0;\n        var mathCos = Math.cos;\n        var mathSin = Math.sin; // base position: top-left\n\n        var x = cx + r * mathCos(startAngle);\n        var y = cy + r * mathSin(startAngle);\n        var textAlign = 'left';\n        var textVerticalAlign = 'top';\n\n        switch (mappedSectorPosition) {\n          case 'startArc':\n            x = cx + (r0 - distance) * mathCos(middleAngle);\n            y = cy + (r0 - distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'top';\n            break;\n\n          case 'insideStartArc':\n            x = cx + (r0 + distance) * mathCos(middleAngle);\n            y = cy + (r0 + distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'bottom';\n            break;\n\n          case 'startAngle':\n            x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, distance + extraDist, false);\n            y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, distance + extraDist, false);\n            textAlign = 'right';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'insideStartAngle':\n            x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, -distance + extraDist, false);\n            y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, -distance + extraDist, false);\n            textAlign = 'left';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'middle':\n            x = cx + middleR * mathCos(middleAngle);\n            y = cy + middleR * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'endArc':\n            x = cx + (r + distance) * mathCos(middleAngle);\n            y = cy + (r + distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'bottom';\n            break;\n\n          case 'insideEndArc':\n            x = cx + (r - distance) * mathCos(middleAngle);\n            y = cy + (r - distance) * mathSin(middleAngle);\n            textAlign = 'center';\n            textVerticalAlign = 'top';\n            break;\n\n          case 'endAngle':\n            x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, distance + extraDist, true);\n            y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, distance + extraDist, true);\n            textAlign = 'left';\n            textVerticalAlign = 'middle';\n            break;\n\n          case 'insideEndAngle':\n            x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, -distance + extraDist, true);\n            y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, -distance + extraDist, true);\n            textAlign = 'right';\n            textVerticalAlign = 'middle';\n            break;\n\n          default:\n            return calculateTextPosition(out, opts, boundingRect);\n        }\n\n        out = out || {};\n        out.x = x;\n        out.y = y;\n        out.align = textAlign;\n        out.verticalAlign = textVerticalAlign;\n        return out;\n      };\n    }\n    function setSectorTextRotation(sector, textPosition, positionMapping, rotateType) {\n      if (isNumber(rotateType)) {\n        // user-set rotation\n        sector.setTextConfig({\n          rotation: rotateType\n        });\n        return;\n      } else if (isArray(textPosition)) {\n        // user-set position, use 0 as auto rotation\n        sector.setTextConfig({\n          rotation: 0\n        });\n        return;\n      }\n\n      var shape = sector.shape;\n      var startAngle = shape.clockwise ? shape.startAngle : shape.endAngle;\n      var endAngle = shape.clockwise ? shape.endAngle : shape.startAngle;\n      var middleAngle = (startAngle + endAngle) / 2;\n      var anchorAngle;\n      var mappedSectorPosition = positionMapping(textPosition);\n\n      switch (mappedSectorPosition) {\n        case 'startArc':\n        case 'insideStartArc':\n        case 'middle':\n        case 'insideEndArc':\n        case 'endArc':\n          anchorAngle = middleAngle;\n          break;\n\n        case 'startAngle':\n        case 'insideStartAngle':\n          anchorAngle = startAngle;\n          break;\n\n        case 'endAngle':\n        case 'insideEndAngle':\n          anchorAngle = endAngle;\n          break;\n\n        default:\n          sector.setTextConfig({\n            rotation: 0\n          });\n          return;\n      }\n\n      var rotate = Math.PI * 1.5 - anchorAngle;\n      /**\n       * TODO: labels with rotate > Math.PI / 2 should be rotate another\n       * half round flipped to increase readability. However, only middle\n       * position supports this for now, because in other positions, the\n       * anchor point is not at the center of the text, so the positions\n       * after rotating is not as expected.\n       */\n\n      if (mappedSectorPosition === 'middle' && rotate > Math.PI / 2 && rotate < Math.PI * 1.5) {\n        rotate -= Math.PI;\n      }\n\n      sector.setTextConfig({\n        rotation: rotate\n      });\n    }\n\n    function adjustAngleDistanceX(angle, distance, isEnd) {\n      return distance * Math.sin(angle) * (isEnd ? -1 : 1);\n    }\n\n    function adjustAngleDistanceY(angle, distance, isEnd) {\n      return distance * Math.cos(angle) * (isEnd ? 1 : -1);\n    }\n\n    var mathMax$6 = Math.max;\n    var mathMin$6 = Math.min;\n\n    function getClipArea(coord, data) {\n      var coordSysClipArea = coord.getArea && coord.getArea();\n\n      if (isCoordinateSystemType(coord, 'cartesian2d')) {\n        var baseAxis = coord.getBaseAxis(); // When boundaryGap is false or using time axis. bar may exceed the grid.\n        // We should not clip this part.\n        // See test/bar2.html\n\n        if (baseAxis.type !== 'category' || !baseAxis.onBand) {\n          var expandWidth = data.getLayout('bandWidth');\n\n          if (baseAxis.isHorizontal()) {\n            coordSysClipArea.x -= expandWidth;\n            coordSysClipArea.width += expandWidth * 2;\n          } else {\n            coordSysClipArea.y -= expandWidth;\n            coordSysClipArea.height += expandWidth * 2;\n          }\n        }\n      }\n\n      return coordSysClipArea;\n    }\n\n    var BarView =\n    /** @class */\n    function (_super) {\n      __extends(BarView, _super);\n\n      function BarView() {\n        var _this = _super.call(this) || this;\n\n        _this.type = BarView.type;\n        _this._isFirstFrame = true;\n        return _this;\n      }\n\n      BarView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        this._model = seriesModel;\n\n        this._removeOnRenderedListener(api);\n\n        this._updateDrawMode(seriesModel);\n\n        var coordinateSystemType = seriesModel.get('coordinateSystem');\n\n        if (coordinateSystemType === 'cartesian2d' || coordinateSystemType === 'polar') {\n          // Clear previously rendered progressive elements.\n          this._progressiveEls = null;\n          this._isLargeDraw ? this._renderLarge(seriesModel, ecModel, api) : this._renderNormal(seriesModel, ecModel, api, payload);\n        } else if (\"development\" !== 'production') {\n          warn('Only cartesian2d and polar supported for bar.');\n        }\n      };\n\n      BarView.prototype.incrementalPrepareRender = function (seriesModel) {\n        this._clear();\n\n        this._updateDrawMode(seriesModel); // incremental also need to clip, otherwise might be overlow.\n        // But must not set clip in each frame, otherwise all of the children will be marked redraw.\n\n\n        this._updateLargeClip(seriesModel);\n      };\n\n      BarView.prototype.incrementalRender = function (params, seriesModel) {\n        // Reset\n        this._progressiveEls = []; // Do not support progressive in normal mode.\n\n        this._incrementalRenderLarge(params, seriesModel);\n      };\n\n      BarView.prototype.eachRendered = function (cb) {\n        traverseElements(this._progressiveEls || this.group, cb);\n      };\n\n      BarView.prototype._updateDrawMode = function (seriesModel) {\n        var isLargeDraw = seriesModel.pipelineContext.large;\n\n        if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) {\n          this._isLargeDraw = isLargeDraw;\n\n          this._clear();\n        }\n      };\n\n      BarView.prototype._renderNormal = function (seriesModel, ecModel, api, payload) {\n        var group = this.group;\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var coord = seriesModel.coordinateSystem;\n        var baseAxis = coord.getBaseAxis();\n        var isHorizontalOrRadial;\n\n        if (coord.type === 'cartesian2d') {\n          isHorizontalOrRadial = baseAxis.isHorizontal();\n        } else if (coord.type === 'polar') {\n          isHorizontalOrRadial = baseAxis.dim === 'angle';\n        }\n\n        var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;\n        var realtimeSortCfg = shouldRealtimeSort(seriesModel, coord);\n\n        if (realtimeSortCfg) {\n          this._enableRealtimeSort(realtimeSortCfg, data, api);\n        }\n\n        var needsClip = seriesModel.get('clip', true) || realtimeSortCfg;\n        var coordSysClipArea = getClipArea(coord, data); // If there is clipPath created in large mode. Remove it.\n\n        group.removeClipPath(); // We don't use clipPath in normal mode because we needs a perfect animation\n        // And don't want the label are clipped.\n\n        var roundCap = seriesModel.get('roundCap', true);\n        var drawBackground = seriesModel.get('showBackground', true);\n        var backgroundModel = seriesModel.getModel('backgroundStyle');\n        var barBorderRadius = backgroundModel.get('borderRadius') || 0;\n        var bgEls = [];\n        var oldBgEls = this._backgroundEls;\n        var isInitSort = payload && payload.isInitSort;\n        var isChangeOrder = payload && payload.type === 'changeAxisOrder';\n\n        function createBackground(dataIndex) {\n          var bgLayout = getLayout[coord.type](data, dataIndex);\n          var bgEl = createBackgroundEl(coord, isHorizontalOrRadial, bgLayout);\n          bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n          if (coord.type === 'cartesian2d') {\n            bgEl.setShape('r', barBorderRadius);\n          }\n\n          bgEls[dataIndex] = bgEl;\n          return bgEl;\n        }\n        data.diff(oldData).add(function (dataIndex) {\n          var itemModel = data.getItemModel(dataIndex);\n          var layout = getLayout[coord.type](data, dataIndex, itemModel);\n\n          if (drawBackground) {\n            createBackground(dataIndex);\n          } // If dataZoom in filteMode: 'empty', the baseValue can be set as NaN in \"axisProxy\".\n\n\n          if (!data.hasValue(dataIndex) || !isValidLayout[coord.type](layout)) {\n            return;\n          }\n\n          var isClipped = false;\n\n          if (needsClip) {\n            // Clip will modify the layout params.\n            // And return a boolean to determine if the shape are fully clipped.\n            isClipped = clip[coord.type](coordSysClipArea, layout);\n          }\n\n          var el = elementCreator[coord.type](seriesModel, data, dataIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, false, roundCap);\n\n          if (realtimeSortCfg) {\n            /**\n             * Force label animation because even if the element is\n             * ignored because it's clipped, it may not be clipped after\n             * changing order. Then, if not using forceLabelAnimation,\n             * the label animation was never started, in which case,\n             * the label will be the final value and doesn't have label\n             * animation.\n             */\n            el.forceLabelAnimation = true;\n          }\n\n          updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n\n          if (isInitSort) {\n            el.attr({\n              shape: layout\n            });\n          } else if (realtimeSortCfg) {\n            updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, dataIndex, isHorizontalOrRadial, false, false);\n          } else {\n            initProps(el, {\n              shape: layout\n            }, seriesModel, dataIndex);\n          }\n\n          data.setItemGraphicEl(dataIndex, el);\n          group.add(el);\n          el.ignore = isClipped;\n        }).update(function (newIndex, oldIndex) {\n          var itemModel = data.getItemModel(newIndex);\n          var layout = getLayout[coord.type](data, newIndex, itemModel);\n\n          if (drawBackground) {\n            var bgEl = void 0;\n\n            if (oldBgEls.length === 0) {\n              bgEl = createBackground(oldIndex);\n            } else {\n              bgEl = oldBgEls[oldIndex];\n              bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius.\n\n              if (coord.type === 'cartesian2d') {\n                bgEl.setShape('r', barBorderRadius);\n              }\n\n              bgEls[newIndex] = bgEl;\n            }\n\n            var bgLayout = getLayout[coord.type](data, newIndex);\n            var shape = createBackgroundShape(isHorizontalOrRadial, bgLayout, coord);\n            updateProps(bgEl, {\n              shape: shape\n            }, animationModel, newIndex);\n          }\n\n          var el = oldData.getItemGraphicEl(oldIndex);\n\n          if (!data.hasValue(newIndex) || !isValidLayout[coord.type](layout)) {\n            group.remove(el);\n            return;\n          }\n\n          var isClipped = false;\n\n          if (needsClip) {\n            isClipped = clip[coord.type](coordSysClipArea, layout);\n\n            if (isClipped) {\n              group.remove(el);\n            }\n          }\n\n          if (!el) {\n            el = elementCreator[coord.type](seriesModel, data, newIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, !!el, roundCap);\n          } else {\n            saveOldStyle(el);\n          }\n\n          if (realtimeSortCfg) {\n            el.forceLabelAnimation = true;\n          }\n\n          if (isChangeOrder) {\n            var textEl = el.getTextContent();\n\n            if (textEl) {\n              var labelInnerStore = labelInner(textEl);\n\n              if (labelInnerStore.prevValue != null) {\n                /**\n                 * Set preValue to be value so that no new label\n                 * should be started, otherwise, it will take a full\n                 * `animationDurationUpdate` time to finish the\n                 * animation, which is not expected.\n                 */\n                labelInnerStore.prevValue = labelInnerStore.value;\n              }\n            }\n          } // Not change anything if only order changed.\n          // Especially not change label.\n          else {\n              updateStyle(el, data, newIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar');\n            }\n\n          if (isInitSort) {\n            el.attr({\n              shape: layout\n            });\n          } else if (realtimeSortCfg) {\n            updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, newIndex, isHorizontalOrRadial, true, isChangeOrder);\n          } else {\n            updateProps(el, {\n              shape: layout\n            }, seriesModel, newIndex, null);\n          }\n\n          data.setItemGraphicEl(newIndex, el);\n          el.ignore = isClipped;\n          group.add(el);\n        }).remove(function (dataIndex) {\n          var el = oldData.getItemGraphicEl(dataIndex);\n          el && removeElementWithFadeOut(el, seriesModel, dataIndex);\n        }).execute();\n        var bgGroup = this._backgroundGroup || (this._backgroundGroup = new Group());\n        bgGroup.removeAll();\n\n        for (var i = 0; i < bgEls.length; ++i) {\n          bgGroup.add(bgEls[i]);\n        }\n\n        group.add(bgGroup);\n        this._backgroundEls = bgEls;\n        this._data = data;\n      };\n\n      BarView.prototype._renderLarge = function (seriesModel, ecModel, api) {\n        this._clear();\n\n        createLarge(seriesModel, this.group);\n\n        this._updateLargeClip(seriesModel);\n      };\n\n      BarView.prototype._incrementalRenderLarge = function (params, seriesModel) {\n        this._removeBackground();\n\n        createLarge(seriesModel, this.group, this._progressiveEls, true);\n      };\n\n      BarView.prototype._updateLargeClip = function (seriesModel) {\n        // Use clipPath in large mode.\n        var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel);\n        var group = this.group;\n\n        if (clipPath) {\n          group.setClipPath(clipPath);\n        } else {\n          group.removeClipPath();\n        }\n      };\n\n      BarView.prototype._enableRealtimeSort = function (realtimeSortCfg, data, api) {\n        var _this = this; // If no data in the first frame, wait for data to initSort\n\n\n        if (!data.count()) {\n          return;\n        }\n\n        var baseAxis = realtimeSortCfg.baseAxis;\n\n        if (this._isFirstFrame) {\n          this._dispatchInitSort(data, realtimeSortCfg, api);\n\n          this._isFirstFrame = false;\n        } else {\n          var orderMapping_1 = function (idx) {\n            var el = data.getItemGraphicEl(idx);\n            var shape = el && el.shape;\n            return shape && // The result should be consistent with the initial sort by data value.\n            // Do not support the case that both positive and negative exist.\n            Math.abs(baseAxis.isHorizontal() ? shape.height : shape.width) // If data is NaN, shape.xxx may be NaN, so use || 0 here in case\n            || 0;\n          };\n\n          this._onRendered = function () {\n            _this._updateSortWithinSameData(data, orderMapping_1, baseAxis, api);\n          };\n\n          api.getZr().on('rendered', this._onRendered);\n        }\n      };\n\n      BarView.prototype._dataSort = function (data, baseAxis, orderMapping) {\n        var info = [];\n        data.each(data.mapDimension(baseAxis.dim), function (ordinalNumber, dataIdx) {\n          var mappedValue = orderMapping(dataIdx);\n          mappedValue = mappedValue == null ? NaN : mappedValue;\n          info.push({\n            dataIndex: dataIdx,\n            mappedValue: mappedValue,\n            ordinalNumber: ordinalNumber\n          });\n        });\n        info.sort(function (a, b) {\n          // If NaN, it will be treated as min val.\n          return b.mappedValue - a.mappedValue;\n        });\n        return {\n          ordinalNumbers: map(info, function (item) {\n            return item.ordinalNumber;\n          })\n        };\n      };\n\n      BarView.prototype._isOrderChangedWithinSameData = function (data, orderMapping, baseAxis) {\n        var scale = baseAxis.scale;\n        var ordinalDataDim = data.mapDimension(baseAxis.dim);\n        var lastValue = Number.MAX_VALUE;\n\n        for (var tickNum = 0, len = scale.getOrdinalMeta().categories.length; tickNum < len; ++tickNum) {\n          var rawIdx = data.rawIndexOf(ordinalDataDim, scale.getRawOrdinalNumber(tickNum));\n          var value = rawIdx < 0 // If some tick have no bar, the tick will be treated as min.\n          ? Number.MIN_VALUE // PENDING: if dataZoom on baseAxis exits, is it a performance issue?\n          : orderMapping(data.indexOfRawIndex(rawIdx));\n\n          if (value > lastValue) {\n            return true;\n          }\n\n          lastValue = value;\n        }\n\n        return false;\n      };\n      /*\n       * Consider the case when A and B changed order, whose representing\n       * bars are both out of sight, we don't wish to trigger reorder action\n       * as long as the order in the view doesn't change.\n       */\n\n\n      BarView.prototype._isOrderDifferentInView = function (orderInfo, baseAxis) {\n        var scale = baseAxis.scale;\n        var extent = scale.getExtent();\n        var tickNum = Math.max(0, extent[0]);\n        var tickMax = Math.min(extent[1], scale.getOrdinalMeta().categories.length - 1);\n\n        for (; tickNum <= tickMax; ++tickNum) {\n          if (orderInfo.ordinalNumbers[tickNum] !== scale.getRawOrdinalNumber(tickNum)) {\n            return true;\n          }\n        }\n      };\n\n      BarView.prototype._updateSortWithinSameData = function (data, orderMapping, baseAxis, api) {\n        if (!this._isOrderChangedWithinSameData(data, orderMapping, baseAxis)) {\n          return;\n        }\n\n        var sortInfo = this._dataSort(data, baseAxis, orderMapping);\n\n        if (this._isOrderDifferentInView(sortInfo, baseAxis)) {\n          this._removeOnRenderedListener(api);\n\n          api.dispatchAction({\n            type: 'changeAxisOrder',\n            componentType: baseAxis.dim + 'Axis',\n            axisId: baseAxis.index,\n            sortInfo: sortInfo\n          });\n        }\n      };\n\n      BarView.prototype._dispatchInitSort = function (data, realtimeSortCfg, api) {\n        var baseAxis = realtimeSortCfg.baseAxis;\n\n        var sortResult = this._dataSort(data, baseAxis, function (dataIdx) {\n          return data.get(data.mapDimension(realtimeSortCfg.otherAxis.dim), dataIdx);\n        });\n\n        api.dispatchAction({\n          type: 'changeAxisOrder',\n          componentType: baseAxis.dim + 'Axis',\n          isInitSort: true,\n          axisId: baseAxis.index,\n          sortInfo: sortResult\n        });\n      };\n\n      BarView.prototype.remove = function (ecModel, api) {\n        this._clear(this._model);\n\n        this._removeOnRenderedListener(api);\n      };\n\n      BarView.prototype.dispose = function (ecModel, api) {\n        this._removeOnRenderedListener(api);\n      };\n\n      BarView.prototype._removeOnRenderedListener = function (api) {\n        if (this._onRendered) {\n          api.getZr().off('rendered', this._onRendered);\n          this._onRendered = null;\n        }\n      };\n\n      BarView.prototype._clear = function (model) {\n        var group = this.group;\n        var data = this._data;\n\n        if (model && model.isAnimationEnabled() && data && !this._isLargeDraw) {\n          this._removeBackground();\n\n          this._backgroundEls = [];\n          data.eachItemGraphicEl(function (el) {\n            removeElementWithFadeOut(el, model, getECData(el).dataIndex);\n          });\n        } else {\n          group.removeAll();\n        }\n\n        this._data = null;\n        this._isFirstFrame = true;\n      };\n\n      BarView.prototype._removeBackground = function () {\n        this.group.remove(this._backgroundGroup);\n        this._backgroundGroup = null;\n      };\n\n      BarView.type = 'bar';\n      return BarView;\n    }(ChartView);\n\n    var clip = {\n      cartesian2d: function (coordSysBoundingRect, layout) {\n        var signWidth = layout.width < 0 ? -1 : 1;\n        var signHeight = layout.height < 0 ? -1 : 1; // Needs positive width and height\n\n        if (signWidth < 0) {\n          layout.x += layout.width;\n          layout.width = -layout.width;\n        }\n\n        if (signHeight < 0) {\n          layout.y += layout.height;\n          layout.height = -layout.height;\n        }\n\n        var coordSysX2 = coordSysBoundingRect.x + coordSysBoundingRect.width;\n        var coordSysY2 = coordSysBoundingRect.y + coordSysBoundingRect.height;\n        var x = mathMax$6(layout.x, coordSysBoundingRect.x);\n        var x2 = mathMin$6(layout.x + layout.width, coordSysX2);\n        var y = mathMax$6(layout.y, coordSysBoundingRect.y);\n        var y2 = mathMin$6(layout.y + layout.height, coordSysY2);\n        var xClipped = x2 < x;\n        var yClipped = y2 < y; // When xClipped or yClipped, the element will be marked as `ignore`.\n        // But we should also place the element at the edge of the coord sys bounding rect.\n        // Because if data changed and the bar shows again, its transition animation\n        // will begin at this place.\n\n        layout.x = xClipped && x > coordSysX2 ? x2 : x;\n        layout.y = yClipped && y > coordSysY2 ? y2 : y;\n        layout.width = xClipped ? 0 : x2 - x;\n        layout.height = yClipped ? 0 : y2 - y; // Reverse back\n\n        if (signWidth < 0) {\n          layout.x += layout.width;\n          layout.width = -layout.width;\n        }\n\n        if (signHeight < 0) {\n          layout.y += layout.height;\n          layout.height = -layout.height;\n        }\n\n        return xClipped || yClipped;\n      },\n      polar: function (coordSysClipArea, layout) {\n        var signR = layout.r0 <= layout.r ? 1 : -1; // Make sure r is larger than r0\n\n        if (signR < 0) {\n          var tmp = layout.r;\n          layout.r = layout.r0;\n          layout.r0 = tmp;\n        }\n\n        var r = mathMin$6(layout.r, coordSysClipArea.r);\n        var r0 = mathMax$6(layout.r0, coordSysClipArea.r0);\n        layout.r = r;\n        layout.r0 = r0;\n        var clipped = r - r0 < 0; // Reverse back\n\n        if (signR < 0) {\n          var tmp = layout.r;\n          layout.r = layout.r0;\n          layout.r0 = tmp;\n        }\n\n        return clipped;\n      }\n    };\n    var elementCreator = {\n      cartesian2d: function (seriesModel, data, newIndex, layout, isHorizontal, animationModel, axisModel, isUpdate, roundCap) {\n        var rect = new Rect({\n          shape: extend({}, layout),\n          z2: 1\n        });\n        rect.__dataIndex = newIndex;\n        rect.name = 'item';\n\n        if (animationModel) {\n          var rectShape = rect.shape;\n          var animateProperty = isHorizontal ? 'height' : 'width';\n          rectShape[animateProperty] = 0;\n        }\n\n        return rect;\n      },\n      polar: function (seriesModel, data, newIndex, layout, isRadial, animationModel, axisModel, isUpdate, roundCap) {\n        var ShapeClass = !isRadial && roundCap ? SausagePath : Sector;\n        var sector = new ShapeClass({\n          shape: layout,\n          z2: 1\n        });\n        sector.name = 'item';\n        var positionMap = createPolarPositionMapping(isRadial);\n        sector.calculateTextPosition = createSectorCalculateTextPosition(positionMap, {\n          isRoundCap: ShapeClass === SausagePath\n        }); // Animation\n\n        if (animationModel) {\n          var sectorShape = sector.shape;\n          var animateProperty = isRadial ? 'r' : 'endAngle';\n          var animateTarget = {};\n          sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;\n          animateTarget[animateProperty] = layout[animateProperty];\n          (isUpdate ? updateProps : initProps)(sector, {\n            shape: animateTarget // __value: typeof dataValue === 'string' ? parseInt(dataValue, 10) : dataValue\n\n          }, animationModel);\n        }\n\n        return sector;\n      }\n    };\n\n    function shouldRealtimeSort(seriesModel, coordSys) {\n      var realtimeSortOption = seriesModel.get('realtimeSort', true);\n      var baseAxis = coordSys.getBaseAxis();\n\n      if (\"development\" !== 'production') {\n        if (realtimeSortOption) {\n          if (baseAxis.type !== 'category') {\n            warn('`realtimeSort` will not work because this bar series is not based on a category axis.');\n          }\n\n          if (coordSys.type !== 'cartesian2d') {\n            warn('`realtimeSort` will not work because this bar series is not on cartesian2d.');\n          }\n        }\n      }\n\n      if (realtimeSortOption && baseAxis.type === 'category' && coordSys.type === 'cartesian2d') {\n        return {\n          baseAxis: baseAxis,\n          otherAxis: coordSys.getOtherAxis(baseAxis)\n        };\n      }\n    }\n\n    function updateRealtimeAnimation(realtimeSortCfg, seriesAnimationModel, el, layout, newIndex, isHorizontal, isUpdate, isChangeOrder) {\n      var seriesTarget;\n      var axisTarget;\n\n      if (isHorizontal) {\n        axisTarget = {\n          x: layout.x,\n          width: layout.width\n        };\n        seriesTarget = {\n          y: layout.y,\n          height: layout.height\n        };\n      } else {\n        axisTarget = {\n          y: layout.y,\n          height: layout.height\n        };\n        seriesTarget = {\n          x: layout.x,\n          width: layout.width\n        };\n      }\n\n      if (!isChangeOrder) {\n        // Keep the original growth animation if only axis order changed.\n        // Not start a new animation.\n        (isUpdate ? updateProps : initProps)(el, {\n          shape: seriesTarget\n        }, seriesAnimationModel, newIndex, null);\n      }\n\n      var axisAnimationModel = seriesAnimationModel ? realtimeSortCfg.baseAxis.model : null;\n      (isUpdate ? updateProps : initProps)(el, {\n        shape: axisTarget\n      }, axisAnimationModel, newIndex);\n    }\n\n    function checkPropertiesNotValid(obj, props) {\n      for (var i = 0; i < props.length; i++) {\n        if (!isFinite(obj[props[i]])) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n\n    var rectPropties = ['x', 'y', 'width', 'height'];\n    var polarPropties = ['cx', 'cy', 'r', 'startAngle', 'endAngle'];\n    var isValidLayout = {\n      cartesian2d: function (layout) {\n        return !checkPropertiesNotValid(layout, rectPropties);\n      },\n      polar: function (layout) {\n        return !checkPropertiesNotValid(layout, polarPropties);\n      }\n    };\n    var getLayout = {\n      // itemModel is only used to get borderWidth, which is not needed\n      // when calculating bar background layout.\n      cartesian2d: function (data, dataIndex, itemModel) {\n        var layout = data.getItemLayout(dataIndex);\n        var fixedLineWidth = itemModel ? getLineWidth(itemModel, layout) : 0; // fix layout with lineWidth\n\n        var signX = layout.width > 0 ? 1 : -1;\n        var signY = layout.height > 0 ? 1 : -1;\n        return {\n          x: layout.x + signX * fixedLineWidth / 2,\n          y: layout.y + signY * fixedLineWidth / 2,\n          width: layout.width - signX * fixedLineWidth,\n          height: layout.height - signY * fixedLineWidth\n        };\n      },\n      polar: function (data, dataIndex, itemModel) {\n        var layout = data.getItemLayout(dataIndex);\n        return {\n          cx: layout.cx,\n          cy: layout.cy,\n          r0: layout.r0,\n          r: layout.r,\n          startAngle: layout.startAngle,\n          endAngle: layout.endAngle,\n          clockwise: layout.clockwise\n        };\n      }\n    };\n\n    function isZeroOnPolar(layout) {\n      return layout.startAngle != null && layout.endAngle != null && layout.startAngle === layout.endAngle;\n    }\n\n    function createPolarPositionMapping(isRadial) {\n      return function (isRadial) {\n        var arcOrAngle = isRadial ? 'Arc' : 'Angle';\n        return function (position) {\n          switch (position) {\n            case 'start':\n            case 'insideStart':\n            case 'end':\n            case 'insideEnd':\n              return position + arcOrAngle;\n\n            default:\n              return position;\n          }\n        };\n      }(isRadial);\n    }\n\n    function updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, isPolar) {\n      var style = data.getItemVisual(dataIndex, 'style');\n\n      if (!isPolar) {\n        el.setShape('r', itemModel.get(['itemStyle', 'borderRadius']) || 0);\n      }\n\n      el.useStyle(style);\n      var cursorStyle = itemModel.getShallow('cursor');\n      cursorStyle && el.attr('cursor', cursorStyle);\n      var labelPositionOutside = isPolar ? isHorizontalOrRadial ? layout.r >= layout.r0 ? 'endArc' : 'startArc' : layout.endAngle >= layout.startAngle ? 'endAngle' : 'startAngle' : isHorizontalOrRadial ? layout.height >= 0 ? 'bottom' : 'top' : layout.width >= 0 ? 'right' : 'left';\n      var labelStatesModels = getLabelStatesModels(itemModel);\n      setLabelStyle(el, labelStatesModels, {\n        labelFetcher: seriesModel,\n        labelDataIndex: dataIndex,\n        defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),\n        inheritColor: style.fill,\n        defaultOpacity: style.opacity,\n        defaultOutsidePosition: labelPositionOutside\n      });\n      var label = el.getTextContent();\n\n      if (isPolar && label) {\n        var position = itemModel.get(['label', 'position']);\n        el.textConfig.inside = position === 'middle' ? true : null;\n        setSectorTextRotation(el, position === 'outside' ? labelPositionOutside : position, createPolarPositionMapping(isHorizontalOrRadial), itemModel.get(['label', 'rotate']));\n      }\n\n      setLabelValueAnimation(label, labelStatesModels, seriesModel.getRawValue(dataIndex), function (value) {\n        return getDefaultInterpolatedLabel(data, value);\n      });\n      var emphasisModel = itemModel.getModel(['emphasis']);\n      toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      setStatesStylesFromModel(el, itemModel);\n\n      if (isZeroOnPolar(layout)) {\n        el.style.fill = 'none';\n        el.style.stroke = 'none';\n        each(el.states, function (state) {\n          if (state.style) {\n            state.style.fill = state.style.stroke = 'none';\n          }\n        });\n      }\n    } // In case width or height are too small.\n\n\n    function getLineWidth(itemModel, rawLayout) {\n      // Has no border.\n      var borderColor = itemModel.get(['itemStyle', 'borderColor']);\n\n      if (!borderColor || borderColor === 'none') {\n        return 0;\n      }\n\n      var lineWidth = itemModel.get(['itemStyle', 'borderWidth']) || 0; // width or height may be NaN for empty data\n\n      var width = isNaN(rawLayout.width) ? Number.MAX_VALUE : Math.abs(rawLayout.width);\n      var height = isNaN(rawLayout.height) ? Number.MAX_VALUE : Math.abs(rawLayout.height);\n      return Math.min(lineWidth, width, height);\n    }\n\n    var LagePathShape =\n    /** @class */\n    function () {\n      function LagePathShape() {}\n\n      return LagePathShape;\n    }();\n\n    var LargePath =\n    /** @class */\n    function (_super) {\n      __extends(LargePath, _super);\n\n      function LargePath(opts) {\n        var _this = _super.call(this, opts) || this;\n\n        _this.type = 'largeBar';\n        return _this;\n      }\n\n      LargePath.prototype.getDefaultShape = function () {\n        return new LagePathShape();\n      };\n\n      LargePath.prototype.buildPath = function (ctx, shape) {\n        // Drawing lines is more efficient than drawing\n        // a whole line or drawing rects.\n        var points = shape.points;\n        var baseDimIdx = this.baseDimIdx;\n        var valueDimIdx = 1 - this.baseDimIdx;\n        var startPoint = [];\n        var size = [];\n        var barWidth = this.barWidth;\n\n        for (var i = 0; i < points.length; i += 3) {\n          size[baseDimIdx] = barWidth;\n          size[valueDimIdx] = points[i + 2];\n          startPoint[baseDimIdx] = points[i + baseDimIdx];\n          startPoint[valueDimIdx] = points[i + valueDimIdx];\n          ctx.rect(startPoint[0], startPoint[1], size[0], size[1]);\n        }\n      };\n\n      return LargePath;\n    }(Path);\n\n    function createLarge(seriesModel, group, progressiveEls, incremental) {\n      // TODO support polar\n      var data = seriesModel.getData();\n      var baseDimIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0;\n      var largeDataIndices = data.getLayout('largeDataIndices');\n      var barWidth = data.getLayout('size');\n      var backgroundModel = seriesModel.getModel('backgroundStyle');\n      var bgPoints = data.getLayout('largeBackgroundPoints');\n\n      if (bgPoints) {\n        var bgEl = new LargePath({\n          shape: {\n            points: bgPoints\n          },\n          incremental: !!incremental,\n          silent: true,\n          z2: 0\n        });\n        bgEl.baseDimIdx = baseDimIdx;\n        bgEl.largeDataIndices = largeDataIndices;\n        bgEl.barWidth = barWidth;\n        bgEl.useStyle(backgroundModel.getItemStyle());\n        group.add(bgEl);\n        progressiveEls && progressiveEls.push(bgEl);\n      }\n\n      var el = new LargePath({\n        shape: {\n          points: data.getLayout('largePoints')\n        },\n        incremental: !!incremental,\n        ignoreCoarsePointer: true,\n        z2: 1\n      });\n      el.baseDimIdx = baseDimIdx;\n      el.largeDataIndices = largeDataIndices;\n      el.barWidth = barWidth;\n      group.add(el);\n      el.useStyle(data.getVisual('style')); // Enable tooltip and user mouse/touch event handlers.\n\n      getECData(el).seriesIndex = seriesModel.seriesIndex;\n\n      if (!seriesModel.get('silent')) {\n        el.on('mousedown', largePathUpdateDataIndex);\n        el.on('mousemove', largePathUpdateDataIndex);\n      }\n\n      progressiveEls && progressiveEls.push(el);\n    } // Use throttle to avoid frequently traverse to find dataIndex.\n\n\n    var largePathUpdateDataIndex = throttle(function (event) {\n      var largePath = this;\n      var dataIndex = largePathFindDataIndex(largePath, event.offsetX, event.offsetY);\n      getECData(largePath).dataIndex = dataIndex >= 0 ? dataIndex : null;\n    }, 30, false);\n\n    function largePathFindDataIndex(largePath, x, y) {\n      var baseDimIdx = largePath.baseDimIdx;\n      var valueDimIdx = 1 - baseDimIdx;\n      var points = largePath.shape.points;\n      var largeDataIndices = largePath.largeDataIndices;\n      var startPoint = [];\n      var size = [];\n      var barWidth = largePath.barWidth;\n\n      for (var i = 0, len = points.length / 3; i < len; i++) {\n        var ii = i * 3;\n        size[baseDimIdx] = barWidth;\n        size[valueDimIdx] = points[ii + 2];\n        startPoint[baseDimIdx] = points[ii + baseDimIdx];\n        startPoint[valueDimIdx] = points[ii + valueDimIdx];\n\n        if (size[valueDimIdx] < 0) {\n          startPoint[valueDimIdx] += size[valueDimIdx];\n          size[valueDimIdx] = -size[valueDimIdx];\n        }\n\n        if (x >= startPoint[0] && x <= startPoint[0] + size[0] && y >= startPoint[1] && y <= startPoint[1] + size[1]) {\n          return largeDataIndices[i];\n        }\n      }\n\n      return -1;\n    }\n\n    function createBackgroundShape(isHorizontalOrRadial, layout, coord) {\n      if (isCoordinateSystemType(coord, 'cartesian2d')) {\n        var rectShape = layout;\n        var coordLayout = coord.getArea();\n        return {\n          x: isHorizontalOrRadial ? rectShape.x : coordLayout.x,\n          y: isHorizontalOrRadial ? coordLayout.y : rectShape.y,\n          width: isHorizontalOrRadial ? rectShape.width : coordLayout.width,\n          height: isHorizontalOrRadial ? coordLayout.height : rectShape.height\n        };\n      } else {\n        var coordLayout = coord.getArea();\n        var sectorShape = layout;\n        return {\n          cx: coordLayout.cx,\n          cy: coordLayout.cy,\n          r0: isHorizontalOrRadial ? coordLayout.r0 : sectorShape.r0,\n          r: isHorizontalOrRadial ? coordLayout.r : sectorShape.r,\n          startAngle: isHorizontalOrRadial ? sectorShape.startAngle : 0,\n          endAngle: isHorizontalOrRadial ? sectorShape.endAngle : Math.PI * 2\n        };\n      }\n    }\n\n    function createBackgroundEl(coord, isHorizontalOrRadial, layout) {\n      var ElementClz = coord.type === 'polar' ? Sector : Rect;\n      return new ElementClz({\n        shape: createBackgroundShape(isHorizontalOrRadial, layout, coord),\n        silent: true,\n        z2: 0\n      });\n    }\n\n    function install$2(registers) {\n      registers.registerChartView(BarView);\n      registers.registerSeriesModel(BarSeriesModel);\n      registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'bar')); // Do layout after other overall layout, which can prepare some information.\n\n      registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('bar')); // Down sample after filter\n\n      registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('bar'));\n      /**\n       * @payload\n       * @property {string} [componentType=series]\n       * @property {number} [dx]\n       * @property {number} [dy]\n       * @property {number} [zoom]\n       * @property {number} [originX]\n       * @property {number} [originY]\n       */\n\n      registers.registerAction({\n        type: 'changeAxisOrder',\n        event: 'changeAxisOrder',\n        update: 'update'\n      }, function (payload, ecModel) {\n        var componentType = payload.componentType || 'series';\n        ecModel.eachComponent({\n          mainType: componentType,\n          query: payload\n        }, function (componentModel) {\n          if (payload.sortInfo) {\n            componentModel.axis.setCategorySortInfo(payload.sortInfo);\n          }\n        });\n      });\n    }\n\n    var PI2$6 = Math.PI * 2;\n    var RADIAN = Math.PI / 180;\n\n    function getViewRect(seriesModel, api) {\n      return getLayoutRect(seriesModel.getBoxLayoutParams(), {\n        width: api.getWidth(),\n        height: api.getHeight()\n      });\n    }\n\n    function getBasicPieLayout(seriesModel, api) {\n      var viewRect = getViewRect(seriesModel, api); // center can be string or number when coordinateSystem is specified\n\n      var center = seriesModel.get('center');\n      var radius = seriesModel.get('radius');\n\n      if (!isArray(radius)) {\n        radius = [0, radius];\n      }\n\n      var width = parsePercent$1(viewRect.width, api.getWidth());\n      var height = parsePercent$1(viewRect.height, api.getHeight());\n      var size = Math.min(width, height);\n      var r0 = parsePercent$1(radius[0], size / 2);\n      var r = parsePercent$1(radius[1], size / 2);\n      var cx;\n      var cy;\n      var coordSys = seriesModel.coordinateSystem;\n\n      if (coordSys) {\n        // percentage is not allowed when coordinate system is specified\n        var point = coordSys.dataToPoint(center);\n        cx = point[0] || 0;\n        cy = point[1] || 0;\n      } else {\n        if (!isArray(center)) {\n          center = [center, center];\n        }\n\n        cx = parsePercent$1(center[0], width) + viewRect.x;\n        cy = parsePercent$1(center[1], height) + viewRect.y;\n      }\n\n      return {\n        cx: cx,\n        cy: cy,\n        r0: r0,\n        r: r\n      };\n    }\n    function pieLayout(seriesType, ecModel, api) {\n      ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n        var data = seriesModel.getData();\n        var valueDim = data.mapDimension('value');\n        var viewRect = getViewRect(seriesModel, api);\n\n        var _a = getBasicPieLayout(seriesModel, api),\n            cx = _a.cx,\n            cy = _a.cy,\n            r = _a.r,\n            r0 = _a.r0;\n\n        var startAngle = -seriesModel.get('startAngle') * RADIAN;\n        var minAngle = seriesModel.get('minAngle') * RADIAN;\n        var validDataCount = 0;\n        data.each(valueDim, function (value) {\n          !isNaN(value) && validDataCount++;\n        });\n        var sum = data.getSum(valueDim); // Sum may be 0\n\n        var unitRadian = Math.PI / (sum || validDataCount) * 2;\n        var clockwise = seriesModel.get('clockwise');\n        var roseType = seriesModel.get('roseType');\n        var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); // [0...max]\n\n        var extent = data.getDataExtent(valueDim);\n        extent[0] = 0; // In the case some sector angle is smaller than minAngle\n\n        var restAngle = PI2$6;\n        var valueSumLargerThanMinAngle = 0;\n        var currentAngle = startAngle;\n        var dir = clockwise ? 1 : -1;\n        data.setLayout({\n          viewRect: viewRect,\n          r: r\n        });\n        data.each(valueDim, function (value, idx) {\n          var angle;\n\n          if (isNaN(value)) {\n            data.setItemLayout(idx, {\n              angle: NaN,\n              startAngle: NaN,\n              endAngle: NaN,\n              clockwise: clockwise,\n              cx: cx,\n              cy: cy,\n              r0: r0,\n              r: roseType ? NaN : r\n            });\n            return;\n          } // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样？\n\n\n          if (roseType !== 'area') {\n            angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian;\n          } else {\n            angle = PI2$6 / validDataCount;\n          }\n\n          if (angle < minAngle) {\n            angle = minAngle;\n            restAngle -= minAngle;\n          } else {\n            valueSumLargerThanMinAngle += value;\n          }\n\n          var endAngle = currentAngle + dir * angle;\n          data.setItemLayout(idx, {\n            angle: angle,\n            startAngle: currentAngle,\n            endAngle: endAngle,\n            clockwise: clockwise,\n            cx: cx,\n            cy: cy,\n            r0: r0,\n            r: roseType ? linearMap(value, extent, [r0, r]) : r\n          });\n          currentAngle = endAngle;\n        }); // Some sector is constrained by minAngle\n        // Rest sectors needs recalculate angle\n\n        if (restAngle < PI2$6 && validDataCount) {\n          // Average the angle if rest angle is not enough after all angles is\n          // Constrained by minAngle\n          if (restAngle <= 1e-3) {\n            var angle_1 = PI2$6 / validDataCount;\n            data.each(valueDim, function (value, idx) {\n              if (!isNaN(value)) {\n                var layout_1 = data.getItemLayout(idx);\n                layout_1.angle = angle_1;\n                layout_1.startAngle = startAngle + dir * idx * angle_1;\n                layout_1.endAngle = startAngle + dir * (idx + 1) * angle_1;\n              }\n            });\n          } else {\n            unitRadian = restAngle / valueSumLargerThanMinAngle;\n            currentAngle = startAngle;\n            data.each(valueDim, function (value, idx) {\n              if (!isNaN(value)) {\n                var layout_2 = data.getItemLayout(idx);\n                var angle = layout_2.angle === minAngle ? minAngle : value * unitRadian;\n                layout_2.startAngle = currentAngle;\n                layout_2.endAngle = currentAngle + dir * angle;\n                currentAngle += dir * angle;\n              }\n            });\n          }\n        }\n      });\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function dataFilter(seriesType) {\n      return {\n        seriesType: seriesType,\n        reset: function (seriesModel, ecModel) {\n          var legendModels = ecModel.findComponents({\n            mainType: 'legend'\n          });\n\n          if (!legendModels || !legendModels.length) {\n            return;\n          }\n\n          var data = seriesModel.getData();\n          data.filterSelf(function (idx) {\n            var name = data.getName(idx); // If in any legend component the status is not selected.\n\n            for (var i = 0; i < legendModels.length; i++) {\n              // @ts-ignore FIXME: LegendModel\n              if (!legendModels[i].isSelected(name)) {\n                return false;\n              }\n            }\n\n            return true;\n          });\n        }\n      };\n    }\n\n    var RADIAN$1 = Math.PI / 180;\n\n    function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight, viewLeft, viewTop, farthestX) {\n      if (list.length < 2) {\n        return;\n      }\n\n      function recalculateXOnSemiToAlignOnEllipseCurve(semi) {\n        var rB = semi.rB;\n        var rB2 = rB * rB;\n\n        for (var i = 0; i < semi.list.length; i++) {\n          var item = semi.list[i];\n          var dy = Math.abs(item.label.y - cy); // horizontal r is always same with original r because x is not changed.\n\n          var rA = r + item.len;\n          var rA2 = rA * rA; // Use ellipse implicit function to calculate x\n\n          var dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2);\n          var newX = cx + (dx + item.len2) * dir;\n          var deltaX = newX - item.label.x;\n          var newTargetWidth = item.targetTextWidth - deltaX * dir; // text x is changed, so need to recalculate width.\n\n          constrainTextWidth(item, newTargetWidth, true);\n          item.label.x = newX;\n        }\n      } // Adjust X based on the shifted y. Make tight labels aligned on an ellipse curve.\n\n\n      function recalculateX(items) {\n        // Extremes of\n        var topSemi = {\n          list: [],\n          maxY: 0\n        };\n        var bottomSemi = {\n          list: [],\n          maxY: 0\n        };\n\n        for (var i = 0; i < items.length; i++) {\n          if (items[i].labelAlignTo !== 'none') {\n            continue;\n          }\n\n          var item = items[i];\n          var semi = item.label.y > cy ? bottomSemi : topSemi;\n          var dy = Math.abs(item.label.y - cy);\n\n          if (dy >= semi.maxY) {\n            var dx = item.label.x - cx - item.len2 * dir; // horizontal r is always same with original r because x is not changed.\n\n            var rA = r + item.len; // Canculate rB based on the topest / bottemest label.\n\n            var rB = Math.abs(dx) < rA ? Math.sqrt(dy * dy / (1 - dx * dx / rA / rA)) : rA;\n            semi.rB = rB;\n            semi.maxY = dy;\n          }\n\n          semi.list.push(item);\n        }\n\n        recalculateXOnSemiToAlignOnEllipseCurve(topSemi);\n        recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi);\n      }\n\n      var len = list.length;\n\n      for (var i = 0; i < len; i++) {\n        if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') {\n          var dx = list[i].label.x - farthestX;\n          list[i].linePoints[1][0] += dx;\n          list[i].label.x = farthestX;\n        }\n      }\n\n      if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) {\n        recalculateX(list);\n      }\n    }\n\n    function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop) {\n      var leftList = [];\n      var rightList = [];\n      var leftmostX = Number.MAX_VALUE;\n      var rightmostX = -Number.MAX_VALUE;\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var label = labelLayoutList[i].label;\n\n        if (isPositionCenter(labelLayoutList[i])) {\n          continue;\n        }\n\n        if (label.x < cx) {\n          leftmostX = Math.min(leftmostX, label.x);\n          leftList.push(labelLayoutList[i]);\n        } else {\n          rightmostX = Math.max(rightmostX, label.x);\n          rightList.push(labelLayoutList[i]);\n        }\n      }\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n\n        if (!isPositionCenter(layout) && layout.linePoints) {\n          if (layout.labelStyleWidth != null) {\n            continue;\n          }\n\n          var label = layout.label;\n          var linePoints = layout.linePoints;\n          var targetTextWidth = void 0;\n\n          if (layout.labelAlignTo === 'edge') {\n            if (label.x < cx) {\n              targetTextWidth = linePoints[2][0] - layout.labelDistance - viewLeft - layout.edgeDistance;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - layout.edgeDistance - linePoints[2][0] - layout.labelDistance;\n            }\n          } else if (layout.labelAlignTo === 'labelLine') {\n            if (label.x < cx) {\n              targetTextWidth = leftmostX - viewLeft - layout.bleedMargin;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - rightmostX - layout.bleedMargin;\n            }\n          } else {\n            if (label.x < cx) {\n              targetTextWidth = label.x - viewLeft - layout.bleedMargin;\n            } else {\n              targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin;\n            }\n          }\n\n          layout.targetTextWidth = targetTextWidth;\n          constrainTextWidth(layout, targetTextWidth);\n        }\n      }\n\n      adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX);\n      adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX);\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n\n        if (!isPositionCenter(layout) && layout.linePoints) {\n          var label = layout.label;\n          var linePoints = layout.linePoints;\n          var isAlignToEdge = layout.labelAlignTo === 'edge';\n          var padding = label.style.padding;\n          var paddingH = padding ? padding[1] + padding[3] : 0; // textRect.width already contains paddingH if bgColor is set\n\n          var extraPaddingH = label.style.backgroundColor ? 0 : paddingH;\n          var realTextWidth = layout.rect.width + extraPaddingH;\n          var dist = linePoints[1][0] - linePoints[2][0];\n\n          if (isAlignToEdge) {\n            if (label.x < cx) {\n              linePoints[2][0] = viewLeft + layout.edgeDistance + realTextWidth + layout.labelDistance;\n            } else {\n              linePoints[2][0] = viewLeft + viewWidth - layout.edgeDistance - realTextWidth - layout.labelDistance;\n            }\n          } else {\n            if (label.x < cx) {\n              linePoints[2][0] = label.x + layout.labelDistance;\n            } else {\n              linePoints[2][0] = label.x - layout.labelDistance;\n            }\n\n            linePoints[1][0] = linePoints[2][0] + dist;\n          }\n\n          linePoints[1][1] = linePoints[2][1] = label.y;\n        }\n      }\n    }\n    /**\n     * Set max width of each label, and then wrap each label to the max width.\n     *\n     * @param layout label layout\n     * @param availableWidth max width for the label to display\n     * @param forceRecalculate recaculate the text layout even if the current width\n     * is smaller than `availableWidth`. This is useful when the text was previously\n     * wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in\n     * which case, previous wrapping should be redo.\n     */\n\n\n    function constrainTextWidth(layout, availableWidth, forceRecalculate) {\n      if (forceRecalculate === void 0) {\n        forceRecalculate = false;\n      }\n\n      if (layout.labelStyleWidth != null) {\n        // User-defined style.width has the highest priority.\n        return;\n      }\n\n      var label = layout.label;\n      var style = label.style;\n      var textRect = layout.rect;\n      var bgColor = style.backgroundColor;\n      var padding = style.padding;\n      var paddingH = padding ? padding[1] + padding[3] : 0;\n      var overflow = style.overflow; // textRect.width already contains paddingH if bgColor is set\n\n      var oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH);\n\n      if (availableWidth < oldOuterWidth || forceRecalculate) {\n        var oldHeight = textRect.height;\n\n        if (overflow && overflow.match('break')) {\n          // Temporarily set background to be null to calculate\n          // the bounding box without background.\n          label.setStyle('backgroundColor', null); // Set constraining width\n\n          label.setStyle('width', availableWidth - paddingH); // This is the real bounding box of the text without padding.\n\n          var innerRect = label.getBoundingRect();\n          label.setStyle('width', Math.ceil(innerRect.width));\n          label.setStyle('backgroundColor', bgColor);\n        } else {\n          var availableInnerWidth = availableWidth - paddingH;\n          var newWidth = availableWidth < oldOuterWidth // Current text is too wide, use `availableWidth` as max width.\n          ? availableInnerWidth : // Current available width is enough, but the text may have\n          // already been wrapped with a smaller available width.\n          forceRecalculate ? availableInnerWidth > layout.unconstrainedWidth // Current available is larger than text width,\n          // so don't constrain width (otherwise it may have\n          // empty space in the background).\n          ? null // Current available is smaller than text width, so\n          // use the current available width as constraining\n          // width.\n          : availableInnerWidth : // Current available width is enough, so no need to\n          // constrain.\n          null;\n          label.setStyle('width', newWidth);\n        }\n\n        var newRect = label.getBoundingRect();\n        textRect.width = newRect.width;\n        var margin = (label.style.margin || 0) + 2.1;\n        textRect.height = newRect.height + margin;\n        textRect.y -= (textRect.height - oldHeight) / 2;\n      }\n    }\n\n    function isPositionCenter(sectorShape) {\n      // Not change x for center label\n      return sectorShape.position === 'center';\n    }\n\n    function pieLabelLayout(seriesModel) {\n      var data = seriesModel.getData();\n      var labelLayoutList = [];\n      var cx;\n      var cy;\n      var hasLabelRotate = false;\n      var minShowLabelRadian = (seriesModel.get('minShowLabelAngle') || 0) * RADIAN$1;\n      var viewRect = data.getLayout('viewRect');\n      var r = data.getLayout('r');\n      var viewWidth = viewRect.width;\n      var viewLeft = viewRect.x;\n      var viewTop = viewRect.y;\n      var viewHeight = viewRect.height;\n\n      function setNotShow(el) {\n        el.ignore = true;\n      }\n\n      function isLabelShown(label) {\n        if (!label.ignore) {\n          return true;\n        }\n\n        for (var key in label.states) {\n          if (label.states[key].ignore === false) {\n            return true;\n          }\n        }\n\n        return false;\n      }\n\n      data.each(function (idx) {\n        var sector = data.getItemGraphicEl(idx);\n        var sectorShape = sector.shape;\n        var label = sector.getTextContent();\n        var labelLine = sector.getTextGuideLine();\n        var itemModel = data.getItemModel(idx);\n        var labelModel = itemModel.getModel('label'); // Use position in normal or emphasis\n\n        var labelPosition = labelModel.get('position') || itemModel.get(['emphasis', 'label', 'position']);\n        var labelDistance = labelModel.get('distanceToLabelLine');\n        var labelAlignTo = labelModel.get('alignTo');\n        var edgeDistance = parsePercent$1(labelModel.get('edgeDistance'), viewWidth);\n        var bleedMargin = labelModel.get('bleedMargin');\n        var labelLineModel = itemModel.getModel('labelLine');\n        var labelLineLen = labelLineModel.get('length');\n        labelLineLen = parsePercent$1(labelLineLen, viewWidth);\n        var labelLineLen2 = labelLineModel.get('length2');\n        labelLineLen2 = parsePercent$1(labelLineLen2, viewWidth);\n\n        if (Math.abs(sectorShape.endAngle - sectorShape.startAngle) < minShowLabelRadian) {\n          each(label.states, setNotShow);\n          label.ignore = true;\n\n          if (labelLine) {\n            each(labelLine.states, setNotShow);\n            labelLine.ignore = true;\n          }\n\n          return;\n        }\n\n        if (!isLabelShown(label)) {\n          return;\n        }\n\n        var midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2;\n        var nx = Math.cos(midAngle);\n        var ny = Math.sin(midAngle);\n        var textX;\n        var textY;\n        var linePoints;\n        var textAlign;\n        cx = sectorShape.cx;\n        cy = sectorShape.cy;\n        var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';\n\n        if (labelPosition === 'center') {\n          textX = sectorShape.cx;\n          textY = sectorShape.cy;\n          textAlign = 'center';\n        } else {\n          var x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * nx : sectorShape.r * nx) + cx;\n          var y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * ny : sectorShape.r * ny) + cy;\n          textX = x1 + nx * 3;\n          textY = y1 + ny * 3;\n\n          if (!isLabelInside) {\n            // For roseType\n            var x2 = x1 + nx * (labelLineLen + r - sectorShape.r);\n            var y2 = y1 + ny * (labelLineLen + r - sectorShape.r);\n            var x3 = x2 + (nx < 0 ? -1 : 1) * labelLineLen2;\n            var y3 = y2;\n\n            if (labelAlignTo === 'edge') {\n              // Adjust textX because text align of edge is opposite\n              textX = nx < 0 ? viewLeft + edgeDistance : viewLeft + viewWidth - edgeDistance;\n            } else {\n              textX = x3 + (nx < 0 ? -labelDistance : labelDistance);\n            }\n\n            textY = y3;\n            linePoints = [[x1, y1], [x2, y2], [x3, y3]];\n          }\n\n          textAlign = isLabelInside ? 'center' : labelAlignTo === 'edge' ? nx > 0 ? 'right' : 'left' : nx > 0 ? 'left' : 'right';\n        }\n\n        var PI = Math.PI;\n        var labelRotate = 0;\n        var rotate = labelModel.get('rotate');\n\n        if (isNumber(rotate)) {\n          labelRotate = rotate * (PI / 180);\n        } else if (labelPosition === 'center') {\n          labelRotate = 0;\n        } else if (rotate === 'radial' || rotate === true) {\n          var radialAngle = nx < 0 ? -midAngle + PI : -midAngle;\n          labelRotate = radialAngle;\n        } else if (rotate === 'tangential' && labelPosition !== 'outside' && labelPosition !== 'outer') {\n          var rad = Math.atan2(nx, ny);\n\n          if (rad < 0) {\n            rad = PI * 2 + rad;\n          }\n\n          var isDown = ny > 0;\n\n          if (isDown) {\n            rad = PI + rad;\n          }\n\n          labelRotate = rad - PI;\n        }\n\n        hasLabelRotate = !!labelRotate;\n        label.x = textX;\n        label.y = textY;\n        label.rotation = labelRotate;\n        label.setStyle({\n          verticalAlign: 'middle'\n        }); // Not sectorShape the inside label\n\n        if (!isLabelInside) {\n          var textRect = label.getBoundingRect().clone();\n          textRect.applyTransform(label.getComputedTransform()); // Text has a default 1px stroke. Exclude this.\n\n          var margin = (label.style.margin || 0) + 2.1;\n          textRect.y -= margin / 2;\n          textRect.height += margin;\n          labelLayoutList.push({\n            label: label,\n            labelLine: labelLine,\n            position: labelPosition,\n            len: labelLineLen,\n            len2: labelLineLen2,\n            minTurnAngle: labelLineModel.get('minTurnAngle'),\n            maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'),\n            surfaceNormal: new Point(nx, ny),\n            linePoints: linePoints,\n            textAlign: textAlign,\n            labelDistance: labelDistance,\n            labelAlignTo: labelAlignTo,\n            edgeDistance: edgeDistance,\n            bleedMargin: bleedMargin,\n            rect: textRect,\n            unconstrainedWidth: textRect.width,\n            labelStyleWidth: label.style.width\n          });\n        } else {\n          label.setStyle({\n            align: textAlign\n          });\n          var selectState = label.states.select;\n\n          if (selectState) {\n            selectState.x += label.x;\n            selectState.y += label.y;\n          }\n        }\n\n        sector.setTextConfig({\n          inside: isLabelInside\n        });\n      });\n\n      if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {\n        avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop);\n      }\n\n      for (var i = 0; i < labelLayoutList.length; i++) {\n        var layout = labelLayoutList[i];\n        var label = layout.label;\n        var labelLine = layout.labelLine;\n        var notShowLabel = isNaN(label.x) || isNaN(label.y);\n\n        if (label) {\n          label.setStyle({\n            align: layout.textAlign\n          });\n\n          if (notShowLabel) {\n            each(label.states, setNotShow);\n            label.ignore = true;\n          }\n\n          var selectState = label.states.select;\n\n          if (selectState) {\n            selectState.x += label.x;\n            selectState.y += label.y;\n          }\n        }\n\n        if (labelLine) {\n          var linePoints = layout.linePoints;\n\n          if (notShowLabel || !linePoints) {\n            each(labelLine.states, setNotShow);\n            labelLine.ignore = true;\n          } else {\n            limitTurnAngle(linePoints, layout.minTurnAngle);\n            limitSurfaceAngle(linePoints, layout.surfaceNormal, layout.maxSurfaceAngle);\n            labelLine.setShape({\n              points: linePoints\n            }); // Set the anchor to the midpoint of sector\n\n            label.__hostTarget.textGuideLineConfig = {\n              anchor: new Point(linePoints[0][0], linePoints[0][1])\n            };\n          }\n        }\n      }\n    }\n\n    function getSectorCornerRadius(model, shape, zeroIfNull) {\n      var cornerRadius = model.get('borderRadius');\n\n      if (cornerRadius == null) {\n        return zeroIfNull ? {\n          cornerRadius: 0\n        } : null;\n      }\n\n      if (!isArray(cornerRadius)) {\n        cornerRadius = [cornerRadius, cornerRadius, cornerRadius, cornerRadius];\n      }\n\n      var dr = Math.abs(shape.r || 0 - shape.r0 || 0);\n      return {\n        cornerRadius: map(cornerRadius, function (cr) {\n          return parsePercent(cr, dr);\n        })\n      };\n    }\n\n    /**\n     * Piece of pie including Sector, Label, LabelLine\n     */\n\n    var PiePiece =\n    /** @class */\n    function (_super) {\n      __extends(PiePiece, _super);\n\n      function PiePiece(data, idx, startAngle) {\n        var _this = _super.call(this) || this;\n\n        _this.z2 = 2;\n        var text = new ZRText();\n\n        _this.setTextContent(text);\n\n        _this.updateData(data, idx, startAngle, true);\n\n        return _this;\n      }\n\n      PiePiece.prototype.updateData = function (data, idx, startAngle, firstCreate) {\n        var sector = this;\n        var seriesModel = data.hostModel;\n        var itemModel = data.getItemModel(idx);\n        var emphasisModel = itemModel.getModel('emphasis');\n        var layout = data.getItemLayout(idx); // cornerRadius & innerCornerRadius doesn't exist in the item layout. Use `0` if null value is specified.\n        // see `setItemLayout` in `pieLayout.ts`.\n\n        var sectorShape = extend(getSectorCornerRadius(itemModel.getModel('itemStyle'), layout, true), layout); // Ignore NaN data.\n\n        if (isNaN(sectorShape.startAngle)) {\n          // Use NaN shape to avoid drawing shape.\n          sector.setShape(sectorShape);\n          return;\n        }\n\n        if (firstCreate) {\n          sector.setShape(sectorShape);\n          var animationType = seriesModel.getShallow('animationType');\n\n          if (seriesModel.ecModel.ssr) {\n            // Use scale animation in SSR mode(opacity?)\n            // Because CSS SVG animation doesn't support very customized shape animation.\n            initProps(sector, {\n              scaleX: 0,\n              scaleY: 0\n            }, seriesModel, {\n              dataIndex: idx,\n              isFrom: true\n            });\n            sector.originX = sectorShape.cx;\n            sector.originY = sectorShape.cy;\n          } else if (animationType === 'scale') {\n            sector.shape.r = layout.r0;\n            initProps(sector, {\n              shape: {\n                r: layout.r\n              }\n            }, seriesModel, idx);\n          } // Expansion\n          else {\n              if (startAngle != null) {\n                sector.setShape({\n                  startAngle: startAngle,\n                  endAngle: startAngle\n                });\n                initProps(sector, {\n                  shape: {\n                    startAngle: layout.startAngle,\n                    endAngle: layout.endAngle\n                  }\n                }, seriesModel, idx);\n              } else {\n                sector.shape.endAngle = layout.startAngle;\n                updateProps(sector, {\n                  shape: {\n                    endAngle: layout.endAngle\n                  }\n                }, seriesModel, idx);\n              }\n            }\n        } else {\n          saveOldStyle(sector); // Transition animation from the old shape\n\n          updateProps(sector, {\n            shape: sectorShape\n          }, seriesModel, idx);\n        }\n\n        sector.useStyle(data.getItemVisual(idx, 'style'));\n        setStatesStylesFromModel(sector, itemModel);\n        var midAngle = (layout.startAngle + layout.endAngle) / 2;\n        var offset = seriesModel.get('selectedOffset');\n        var dx = Math.cos(midAngle) * offset;\n        var dy = Math.sin(midAngle) * offset;\n        var cursorStyle = itemModel.getShallow('cursor');\n        cursorStyle && sector.attr('cursor', cursorStyle);\n\n        this._updateLabel(seriesModel, data, idx);\n\n        sector.ensureState('emphasis').shape = extend({\n          r: layout.r + (emphasisModel.get('scale') ? emphasisModel.get('scaleSize') || 0 : 0)\n        }, getSectorCornerRadius(emphasisModel.getModel('itemStyle'), layout));\n        extend(sector.ensureState('select'), {\n          x: dx,\n          y: dy,\n          shape: getSectorCornerRadius(itemModel.getModel(['select', 'itemStyle']), layout)\n        });\n        extend(sector.ensureState('blur'), {\n          shape: getSectorCornerRadius(itemModel.getModel(['blur', 'itemStyle']), layout)\n        });\n        var labelLine = sector.getTextGuideLine();\n        var labelText = sector.getTextContent();\n        labelLine && extend(labelLine.ensureState('select'), {\n          x: dx,\n          y: dy\n        }); // TODO: needs dx, dy in zrender?\n\n        extend(labelText.ensureState('select'), {\n          x: dx,\n          y: dy\n        });\n        toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));\n      };\n\n      PiePiece.prototype._updateLabel = function (seriesModel, data, idx) {\n        var sector = this;\n        var itemModel = data.getItemModel(idx);\n        var labelLineModel = itemModel.getModel('labelLine');\n        var style = data.getItemVisual(idx, 'style');\n        var visualColor = style && style.fill;\n        var visualOpacity = style && style.opacity;\n        setLabelStyle(sector, getLabelStatesModels(itemModel), {\n          labelFetcher: data.hostModel,\n          labelDataIndex: idx,\n          inheritColor: visualColor,\n          defaultOpacity: visualOpacity,\n          defaultText: seriesModel.getFormattedLabel(idx, 'normal') || data.getName(idx)\n        });\n        var labelText = sector.getTextContent(); // Set textConfig on sector.\n\n        sector.setTextConfig({\n          // reset position, rotation\n          position: null,\n          rotation: null\n        }); // Make sure update style on labelText after setLabelStyle.\n        // Because setLabelStyle will replace a new style on it.\n\n        labelText.attr({\n          z2: 10\n        });\n        var labelPosition = seriesModel.get(['label', 'position']);\n\n        if (labelPosition !== 'outside' && labelPosition !== 'outer') {\n          sector.removeTextGuideLine();\n        } else {\n          var polyline = this.getTextGuideLine();\n\n          if (!polyline) {\n            polyline = new Polyline();\n            this.setTextGuideLine(polyline);\n          } // Default use item visual color\n\n\n          setLabelLineStyle(this, getLabelLineStatesModels(itemModel), {\n            stroke: visualColor,\n            opacity: retrieve3(labelLineModel.get(['lineStyle', 'opacity']), visualOpacity, 1)\n          });\n        }\n      };\n\n      return PiePiece;\n    }(Sector); // Pie view\n\n\n    var PieView =\n    /** @class */\n    function (_super) {\n      __extends(PieView, _super);\n\n      function PieView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.ignoreLabelLineUpdate = true;\n        return _this;\n      }\n\n      PieView.prototype.render = function (seriesModel, ecModel, api, payload) {\n        var data = seriesModel.getData();\n        var oldData = this._data;\n        var group = this.group;\n        var startAngle; // First render\n\n        if (!oldData && data.count() > 0) {\n          var shape = data.getItemLayout(0);\n\n          for (var s = 1; isNaN(shape && shape.startAngle) && s < data.count(); ++s) {\n            shape = data.getItemLayout(s);\n          }\n\n          if (shape) {\n            startAngle = shape.startAngle;\n          }\n        } // remove empty-circle if it exists\n\n\n        if (this._emptyCircleSector) {\n          group.remove(this._emptyCircleSector);\n        } // when all data are filtered, show lightgray empty circle\n\n\n        if (data.count() === 0 && seriesModel.get('showEmptyCircle')) {\n          var sector = new Sector({\n            shape: getBasicPieLayout(seriesModel, api)\n          });\n          sector.useStyle(seriesModel.getModel('emptyCircleStyle').getItemStyle());\n          this._emptyCircleSector = sector;\n          group.add(sector);\n        }\n\n        data.diff(oldData).add(function (idx) {\n          var piePiece = new PiePiece(data, idx, startAngle);\n          data.setItemGraphicEl(idx, piePiece);\n          group.add(piePiece);\n        }).update(function (newIdx, oldIdx) {\n          var piePiece = oldData.getItemGraphicEl(oldIdx);\n          piePiece.updateData(data, newIdx, startAngle);\n          piePiece.off('click');\n          group.add(piePiece);\n          data.setItemGraphicEl(newIdx, piePiece);\n        }).remove(function (idx) {\n          var piePiece = oldData.getItemGraphicEl(idx);\n          removeElementWithFadeOut(piePiece, seriesModel, idx);\n        }).execute();\n        pieLabelLayout(seriesModel); // Always use initial animation.\n\n        if (seriesModel.get('animationTypeUpdate') !== 'expansion') {\n          this._data = data;\n        }\n      };\n\n      PieView.prototype.dispose = function () {};\n\n      PieView.prototype.containPoint = function (point, seriesModel) {\n        var data = seriesModel.getData();\n        var itemLayout = data.getItemLayout(0);\n\n        if (itemLayout) {\n          var dx = point[0] - itemLayout.cx;\n          var dy = point[1] - itemLayout.cy;\n          var radius = Math.sqrt(dx * dx + dy * dy);\n          return radius <= itemLayout.r && radius >= itemLayout.r0;\n        }\n      };\n\n      PieView.type = 'pie';\n      return PieView;\n    }(ChartView);\n\n    /**\n     * [Usage]:\n     * (1)\n     * createListSimply(seriesModel, ['value']);\n     * (2)\n     * createListSimply(seriesModel, {\n     *     coordDimensions: ['value'],\n     *     dimensionsCount: 5\n     * });\n     */\n\n    function createSeriesDataSimply(seriesModel, opt, nameList) {\n      opt = isArray(opt) && {\n        coordDimensions: opt\n      } || extend({\n        encodeDefine: seriesModel.getEncode()\n      }, opt);\n      var source = seriesModel.getSource();\n      var dimensions = prepareSeriesDataSchema(source, opt).dimensions;\n      var list = new SeriesData(dimensions, seriesModel);\n      list.initData(source, nameList);\n      return list;\n    }\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n    /**\n     * LegendVisualProvider is an bridge that pick encoded color from data and\n     * provide to the legend component.\n     */\n    var LegendVisualProvider =\n    /** @class */\n    function () {\n      function LegendVisualProvider( // Function to get data after filtered. It stores all the encoding info\n      getDataWithEncodedVisual, // Function to get raw data before filtered.\n      getRawData) {\n        this._getDataWithEncodedVisual = getDataWithEncodedVisual;\n        this._getRawData = getRawData;\n      }\n\n      LegendVisualProvider.prototype.getAllNames = function () {\n        var rawData = this._getRawData(); // We find the name from the raw data. In case it's filtered by the legend component.\n        // Normally, the name can be found in rawData, but can't be found in filtered data will display as gray.\n\n\n        return rawData.mapArray(rawData.getName);\n      };\n\n      LegendVisualProvider.prototype.containName = function (name) {\n        var rawData = this._getRawData();\n\n        return rawData.indexOfName(name) >= 0;\n      };\n\n      LegendVisualProvider.prototype.indexOfName = function (name) {\n        // Only get data when necessary.\n        // Because LegendVisualProvider constructor may be new in the stage that data is not prepared yet.\n        // Invoking Series#getData immediately will throw an error.\n        var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n        return dataWithEncodedVisual.indexOfName(name);\n      };\n\n      LegendVisualProvider.prototype.getItemVisual = function (dataIndex, key) {\n        // Get encoded visual properties from final filtered data.\n        var dataWithEncodedVisual = this._getDataWithEncodedVisual();\n\n        return dataWithEncodedVisual.getItemVisual(dataIndex, key);\n      };\n\n      return LegendVisualProvider;\n    }();\n\n    var innerData = makeInner();\n\n    var PieSeriesModel =\n    /** @class */\n    function (_super) {\n      __extends(PieSeriesModel, _super);\n\n      function PieSeriesModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.init = function (option) {\n        _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item\n        // Use a function instead of direct access because data reference may changed\n\n\n        this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this));\n\n        this._defaultLabelLine(option);\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.mergeOption = function () {\n        _super.prototype.mergeOption.apply(this, arguments);\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.getInitialData = function () {\n        return createSeriesDataSimply(this, {\n          coordDimensions: ['value'],\n          encodeDefaulter: curry(makeSeriesEncodeForNameBased, this)\n        });\n      };\n      /**\n       * @overwrite\n       */\n\n\n      PieSeriesModel.prototype.getDataParams = function (dataIndex) {\n        var data = this.getData(); // update seats when data is changed\n\n        var dataInner = innerData(data);\n        var seats = dataInner.seats;\n\n        if (!seats) {\n          var valueList_1 = [];\n          data.each(data.mapDimension('value'), function (value) {\n            valueList_1.push(value);\n          });\n          seats = dataInner.seats = getPercentSeats(valueList_1, data.hostModel.get('percentPrecision'));\n        }\n\n        var params = _super.prototype.getDataParams.call(this, dataIndex); // seats may be empty when sum is 0\n\n\n        params.percent = seats[dataIndex] || 0;\n        params.$vars.push('percent');\n        return params;\n      };\n\n      PieSeriesModel.prototype._defaultLabelLine = function (option) {\n        // Extend labelLine emphasis\n        defaultEmphasis(option, 'labelLine', ['show']);\n        var labelLineNormalOpt = option.labelLine;\n        var labelLineEmphasisOpt = option.emphasis.labelLine; // Not show label line if `label.normal.show = false`\n\n        labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show;\n        labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show;\n      };\n\n      PieSeriesModel.type = 'series.pie';\n      PieSeriesModel.defaultOption = {\n        // zlevel: 0,\n        z: 2,\n        legendHoverLink: true,\n        colorBy: 'data',\n        // 默认全局居中\n        center: ['50%', '50%'],\n        radius: [0, '75%'],\n        // 默认顺时针\n        clockwise: true,\n        startAngle: 90,\n        // 最小角度改为0\n        minAngle: 0,\n        // If the angle of a sector less than `minShowLabelAngle`,\n        // the label will not be displayed.\n        minShowLabelAngle: 0,\n        // 选中时扇区偏移量\n        selectedOffset: 10,\n        // 选择模式，默认关闭，可选single，multiple\n        // selectedMode: false,\n        // 南丁格尔玫瑰图模式，'radius'（半径） | 'area'（面积）\n        // roseType: null,\n        percentPrecision: 2,\n        // If still show when all data zero.\n        stillShowZeroSum: true,\n        // cursor: null,\n        left: 0,\n        top: 0,\n        right: 0,\n        bottom: 0,\n        width: null,\n        height: null,\n        label: {\n          // color: 'inherit',\n          // If rotate around circle\n          rotate: 0,\n          show: true,\n          overflow: 'truncate',\n          // 'outer', 'inside', 'center'\n          position: 'outer',\n          // 'none', 'labelLine', 'edge'. Works only when position is 'outer'\n          alignTo: 'none',\n          // Closest distance between label and chart edge.\n          // Works only position is 'outer' and alignTo is 'edge'.\n          edgeDistance: '25%',\n          // Works only position is 'outer' and alignTo is not 'edge'.\n          bleedMargin: 10,\n          // Distance between text and label line.\n          distanceToLabelLine: 5 // formatter: 标签文本格式器，同 tooltip.formatter，不支持异步回调\n          // 默认使用全局文本样式，详见 textStyle\n          // distance: 当position为inner时有效，为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数\n\n        },\n        // Enabled when label.normal.position is 'outer'\n        labelLine: {\n          show: true,\n          // 引导线两段中的第一段长度\n          length: 15,\n          // 引导线两段中的第二段长度\n          length2: 15,\n          smooth: false,\n          minTurnAngle: 90,\n          maxSurfaceAngle: 90,\n          lineStyle: {\n            // color: 各异,\n            width: 1,\n            type: 'solid'\n          }\n        },\n        itemStyle: {\n          borderWidth: 1,\n          borderJoin: 'round'\n        },\n        showEmptyCircle: true,\n        emptyCircleStyle: {\n          color: 'lightgray',\n          opacity: 1\n        },\n        labelLayout: {\n          // Hide the overlapped label.\n          hideOverlap: true\n        },\n        emphasis: {\n          scale: true,\n          scaleSize: 5\n        },\n        // If use strategy to avoid label overlapping\n        avoidLabelOverlap: true,\n        // Animation type. Valid values: expansion, scale\n        animationType: 'expansion',\n        animationDuration: 1000,\n        // Animation type when update. Valid values: transition, expansion\n        animationTypeUpdate: 'transition',\n        animationEasingUpdate: 'cubicInOut',\n        animationDurationUpdate: 500,\n        animationEasing: 'cubicInOut'\n      };\n      return PieSeriesModel;\n    }(SeriesModel);\n\n    function negativeDataFilter(seriesType) {\n      return {\n        seriesType: seriesType,\n        reset: function (seriesModel, ecModel) {\n          var data = seriesModel.getData();\n          data.filterSelf(function (idx) {\n            // handle negative value condition\n            var valueDim = data.mapDimension('value');\n            var curValue = data.get(valueDim, idx);\n\n            if (isNumber(curValue) && !isNaN(curValue) && curValue < 0) {\n              return false;\n            }\n\n            return true;\n          });\n        }\n      };\n    }\n\n    function install$3(registers) {\n      registers.registerChartView(PieView);\n      registers.registerSeriesModel(PieSeriesModel);\n      createLegacyDataSelectAction('pie', registers.registerAction);\n      registers.registerLayout(curry(pieLayout, 'pie'));\n      registers.registerProcessor(dataFilter('pie'));\n      registers.registerProcessor(negativeDataFilter('pie'));\n    }\n\n    var GridModel =\n    /** @class */\n    function (_super) {\n      __extends(GridModel, _super);\n\n      function GridModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      GridModel.type = 'grid';\n      GridModel.dependencies = ['xAxis', 'yAxis'];\n      GridModel.layoutMode = 'box';\n      GridModel.defaultOption = {\n        show: false,\n        // zlevel: 0,\n        z: 0,\n        left: '10%',\n        top: 60,\n        right: '10%',\n        bottom: 70,\n        // If grid size contain label\n        containLabel: false,\n        // width: {totalWidth} - left - right,\n        // height: {totalHeight} - top - bottom,\n        backgroundColor: 'rgba(0,0,0,0)',\n        borderWidth: 1,\n        borderColor: '#ccc'\n      };\n      return GridModel;\n    }(ComponentModel);\n\n    var CartesianAxisModel =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisModel, _super);\n\n      function CartesianAxisModel() {\n        return _super !== null && _super.apply(this, arguments) || this;\n      }\n\n      CartesianAxisModel.prototype.getCoordSysModel = function () {\n        return this.getReferringComponents('grid', SINGLE_REFERRING).models[0];\n      };\n\n      CartesianAxisModel.type = 'cartesian2dAxis';\n      return CartesianAxisModel;\n    }(ComponentModel);\n    mixin(CartesianAxisModel, AxisModelCommonMixin);\n\n    var defaultOption = {\n      show: true,\n      // zlevel: 0,\n      z: 0,\n      // Inverse the axis.\n      inverse: false,\n      // Axis name displayed.\n      name: '',\n      // 'start' | 'middle' | 'end'\n      nameLocation: 'end',\n      // By degree. By default auto rotate by nameLocation.\n      nameRotate: null,\n      nameTruncate: {\n        maxWidth: null,\n        ellipsis: '...',\n        placeholder: '.'\n      },\n      // Use global text style by default.\n      nameTextStyle: {},\n      // The gap between axisName and axisLine.\n      nameGap: 15,\n      // Default `false` to support tooltip.\n      silent: false,\n      // Default `false` to avoid legacy user event listener fail.\n      triggerEvent: false,\n      tooltip: {\n        show: false\n      },\n      axisPointer: {},\n      axisLine: {\n        show: true,\n        onZero: true,\n        onZeroAxisIndex: null,\n        lineStyle: {\n          color: '#6E7079',\n          width: 1,\n          type: 'solid'\n        },\n        // The arrow at both ends the the axis.\n        symbol: ['none', 'none'],\n        symbolSize: [10, 15]\n      },\n      axisTick: {\n        show: true,\n        // Whether axisTick is inside the grid or outside the grid.\n        inside: false,\n        // The length of axisTick.\n        length: 5,\n        lineStyle: {\n          width: 1\n        }\n      },\n      axisLabel: {\n        show: true,\n        // Whether axisLabel is inside the grid or outside the grid.\n        inside: false,\n        rotate: 0,\n        // true | false | null/undefined (auto)\n        showMinLabel: null,\n        // true | false | null/undefined (auto)\n        showMaxLabel: null,\n        margin: 8,\n        // formatter: null,\n        fontSize: 12\n      },\n      splitLine: {\n        show: true,\n        lineStyle: {\n          color: ['#E0E6F1'],\n          width: 1,\n          type: 'solid'\n        }\n      },\n      splitArea: {\n        show: false,\n        areaStyle: {\n          color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)']\n        }\n      }\n    };\n    var categoryAxis = merge({\n      // The gap at both ends of the axis. For categoryAxis, boolean.\n      boundaryGap: true,\n      // Set false to faster category collection.\n      deduplication: null,\n      // splitArea: {\n      // show: false\n      // },\n      splitLine: {\n        show: false\n      },\n      axisTick: {\n        // If tick is align with label when boundaryGap is true\n        alignWithLabel: false,\n        interval: 'auto'\n      },\n      axisLabel: {\n        interval: 'auto'\n      }\n    }, defaultOption);\n    var valueAxis = merge({\n      boundaryGap: [0, 0],\n      axisLine: {\n        // Not shown when other axis is categoryAxis in cartesian\n        show: 'auto'\n      },\n      axisTick: {\n        // Not shown when other axis is categoryAxis in cartesian\n        show: 'auto'\n      },\n      // TODO\n      // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]\n      splitNumber: 5,\n      minorTick: {\n        // Minor tick, not available for cateogry axis.\n        show: false,\n        // Split number of minor ticks. The value should be in range of (0, 100)\n        splitNumber: 5,\n        // Length of minor tick\n        length: 3,\n        // Line style\n        lineStyle: {// Default to be same with axisTick\n        }\n      },\n      minorSplitLine: {\n        show: false,\n        lineStyle: {\n          color: '#F4F7FD',\n          width: 1\n        }\n      }\n    }, defaultOption);\n    var timeAxis = merge({\n      splitNumber: 6,\n      axisLabel: {\n        // To eliminate labels that are not nice\n        showMinLabel: false,\n        showMaxLabel: false,\n        rich: {\n          primary: {\n            fontWeight: 'bold'\n          }\n        }\n      },\n      splitLine: {\n        show: false\n      }\n    }, valueAxis);\n    var logAxis = defaults({\n      logBase: 10\n    }, valueAxis);\n    var axisDefault = {\n      category: categoryAxis,\n      value: valueAxis,\n      time: timeAxis,\n      log: logAxis\n    };\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    var AXIS_TYPES = {\n      value: 1,\n      category: 1,\n      time: 1,\n      log: 1\n    };\n\n    /**\n     * Generate sub axis model class\n     * @param axisName 'x' 'y' 'radius' 'angle' 'parallel' ...\n     */\n\n    function axisModelCreator(registers, axisName, BaseAxisModelClass, extraDefaultOption) {\n      each(AXIS_TYPES, function (v, axisType) {\n        var defaultOption = merge(merge({}, axisDefault[axisType], true), extraDefaultOption, true);\n\n        var AxisModel =\n        /** @class */\n        function (_super) {\n          __extends(AxisModel, _super);\n\n          function AxisModel() {\n            var _this = _super !== null && _super.apply(this, arguments) || this;\n\n            _this.type = axisName + 'Axis.' + axisType;\n            return _this;\n          }\n\n          AxisModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {\n            var layoutMode = fetchLayoutMode(this);\n            var inputPositionParams = layoutMode ? getLayoutParams(option) : {};\n            var themeModel = ecModel.getTheme();\n            merge(option, themeModel.get(axisType + 'Axis'));\n            merge(option, this.getDefaultOption());\n            option.type = getAxisType(option);\n\n            if (layoutMode) {\n              mergeLayoutParam(option, inputPositionParams, layoutMode);\n            }\n          };\n\n          AxisModel.prototype.optionUpdated = function () {\n            var thisOption = this.option;\n\n            if (thisOption.type === 'category') {\n              this.__ordinalMeta = OrdinalMeta.createByAxisModel(this);\n            }\n          };\n          /**\n           * Should not be called before all of 'getInitailData' finished.\n           * Because categories are collected during initializing data.\n           */\n\n\n          AxisModel.prototype.getCategories = function (rawData) {\n            var option = this.option; // FIXME\n            // warning if called before all of 'getInitailData' finished.\n\n            if (option.type === 'category') {\n              if (rawData) {\n                return option.data;\n              }\n\n              return this.__ordinalMeta.categories;\n            }\n          };\n\n          AxisModel.prototype.getOrdinalMeta = function () {\n            return this.__ordinalMeta;\n          };\n\n          AxisModel.type = axisName + 'Axis.' + axisType;\n          AxisModel.defaultOption = defaultOption;\n          return AxisModel;\n        }(BaseAxisModelClass);\n\n        registers.registerComponentModel(AxisModel);\n      });\n      registers.registerSubTypeDefaulter(axisName + 'Axis', getAxisType);\n    }\n\n    function getAxisType(option) {\n      // Default axis with data is category axis\n      return option.type || (option.data ? 'category' : 'value');\n    }\n\n    var Cartesian =\n    /** @class */\n    function () {\n      function Cartesian(name) {\n        this.type = 'cartesian';\n        this._dimList = [];\n        this._axes = {};\n        this.name = name || '';\n      }\n\n      Cartesian.prototype.getAxis = function (dim) {\n        return this._axes[dim];\n      };\n\n      Cartesian.prototype.getAxes = function () {\n        return map(this._dimList, function (dim) {\n          return this._axes[dim];\n        }, this);\n      };\n\n      Cartesian.prototype.getAxesByScale = function (scaleType) {\n        scaleType = scaleType.toLowerCase();\n        return filter(this.getAxes(), function (axis) {\n          return axis.scale.type === scaleType;\n        });\n      };\n\n      Cartesian.prototype.addAxis = function (axis) {\n        var dim = axis.dim;\n        this._axes[dim] = axis;\n\n        this._dimList.push(dim);\n      };\n\n      return Cartesian;\n    }();\n\n    var cartesian2DDimensions = ['x', 'y'];\n\n    function canCalculateAffineTransform(scale) {\n      return scale.type === 'interval' || scale.type === 'time';\n    }\n\n    var Cartesian2D =\n    /** @class */\n    function (_super) {\n      __extends(Cartesian2D, _super);\n\n      function Cartesian2D() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'cartesian2d';\n        _this.dimensions = cartesian2DDimensions;\n        return _this;\n      }\n      /**\n       * Calculate an affine transform matrix if two axes are time or value.\n       * It's mainly for accelartion on the large time series data.\n       */\n\n\n      Cartesian2D.prototype.calcAffineTransform = function () {\n        this._transform = this._invTransform = null;\n        var xAxisScale = this.getAxis('x').scale;\n        var yAxisScale = this.getAxis('y').scale;\n\n        if (!canCalculateAffineTransform(xAxisScale) || !canCalculateAffineTransform(yAxisScale)) {\n          return;\n        }\n\n        var xScaleExtent = xAxisScale.getExtent();\n        var yScaleExtent = yAxisScale.getExtent();\n        var start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]]);\n        var end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]]);\n        var xScaleSpan = xScaleExtent[1] - xScaleExtent[0];\n        var yScaleSpan = yScaleExtent[1] - yScaleExtent[0];\n\n        if (!xScaleSpan || !yScaleSpan) {\n          return;\n        } // Accelerate data to point calculation on the special large time series data.\n\n\n        var scaleX = (end[0] - start[0]) / xScaleSpan;\n        var scaleY = (end[1] - start[1]) / yScaleSpan;\n        var translateX = start[0] - xScaleExtent[0] * scaleX;\n        var translateY = start[1] - yScaleExtent[0] * scaleY;\n        var m = this._transform = [scaleX, 0, 0, scaleY, translateX, translateY];\n        this._invTransform = invert([], m);\n      };\n      /**\n       * Base axis will be used on stacking.\n       */\n\n\n      Cartesian2D.prototype.getBaseAxis = function () {\n        return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAxis('x');\n      };\n\n      Cartesian2D.prototype.containPoint = function (point) {\n        var axisX = this.getAxis('x');\n        var axisY = this.getAxis('y');\n        return axisX.contain(axisX.toLocalCoord(point[0])) && axisY.contain(axisY.toLocalCoord(point[1]));\n      };\n\n      Cartesian2D.prototype.containData = function (data) {\n        return this.getAxis('x').containData(data[0]) && this.getAxis('y').containData(data[1]);\n      };\n\n      Cartesian2D.prototype.containZone = function (data1, data2) {\n        var zoneDiag1 = this.dataToPoint(data1);\n        var zoneDiag2 = this.dataToPoint(data2);\n        var area = this.getArea();\n        var zone = new BoundingRect(zoneDiag1[0], zoneDiag1[1], zoneDiag2[0] - zoneDiag1[0], zoneDiag2[1] - zoneDiag1[1]);\n        return area.intersect(zone);\n      };\n\n      Cartesian2D.prototype.dataToPoint = function (data, clamp, out) {\n        out = out || [];\n        var xVal = data[0];\n        var yVal = data[1]; // Fast path\n\n        if (this._transform // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated.\n        && xVal != null && isFinite(xVal) && yVal != null && isFinite(yVal)) {\n          return applyTransform(out, data, this._transform);\n        }\n\n        var xAxis = this.getAxis('x');\n        var yAxis = this.getAxis('y');\n        out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal, clamp));\n        out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal, clamp));\n        return out;\n      };\n\n      Cartesian2D.prototype.clampData = function (data, out) {\n        var xScale = this.getAxis('x').scale;\n        var yScale = this.getAxis('y').scale;\n        var xAxisExtent = xScale.getExtent();\n        var yAxisExtent = yScale.getExtent();\n        var x = xScale.parse(data[0]);\n        var y = yScale.parse(data[1]);\n        out = out || [];\n        out[0] = Math.min(Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), Math.max(xAxisExtent[0], xAxisExtent[1]));\n        out[1] = Math.min(Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), Math.max(yAxisExtent[0], yAxisExtent[1]));\n        return out;\n      };\n\n      Cartesian2D.prototype.pointToData = function (point, clamp) {\n        var out = [];\n\n        if (this._invTransform) {\n          return applyTransform(out, point, this._invTransform);\n        }\n\n        var xAxis = this.getAxis('x');\n        var yAxis = this.getAxis('y');\n        out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp);\n        out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp);\n        return out;\n      };\n\n      Cartesian2D.prototype.getOtherAxis = function (axis) {\n        return this.getAxis(axis.dim === 'x' ? 'y' : 'x');\n      };\n      /**\n       * Get rect area of cartesian.\n       * Area will have a contain function to determine if a point is in the coordinate system.\n       */\n\n\n      Cartesian2D.prototype.getArea = function () {\n        var xExtent = this.getAxis('x').getGlobalExtent();\n        var yExtent = this.getAxis('y').getGlobalExtent();\n        var x = Math.min(xExtent[0], xExtent[1]);\n        var y = Math.min(yExtent[0], yExtent[1]);\n        var width = Math.max(xExtent[0], xExtent[1]) - x;\n        var height = Math.max(yExtent[0], yExtent[1]) - y;\n        return new BoundingRect(x, y, width, height);\n      };\n\n      return Cartesian2D;\n    }(Cartesian);\n\n    var Axis2D =\n    /** @class */\n    function (_super) {\n      __extends(Axis2D, _super);\n\n      function Axis2D(dim, scale, coordExtent, axisType, position) {\n        var _this = _super.call(this, dim, scale, coordExtent) || this;\n        /**\n         * Index of axis, can be used as key\n         * Injected outside.\n         */\n\n\n        _this.index = 0;\n        _this.type = axisType || 'value';\n        _this.position = position || 'bottom';\n        return _this;\n      }\n\n      Axis2D.prototype.isHorizontal = function () {\n        var position = this.position;\n        return position === 'top' || position === 'bottom';\n      };\n      /**\n       * Each item cooresponds to this.getExtent(), which\n       * means globalExtent[0] may greater than globalExtent[1],\n       * unless `asc` is input.\n       *\n       * @param {boolean} [asc]\n       * @return {Array.<number>}\n       */\n\n\n      Axis2D.prototype.getGlobalExtent = function (asc) {\n        var ret = this.getExtent();\n        ret[0] = this.toGlobalCoord(ret[0]);\n        ret[1] = this.toGlobalCoord(ret[1]);\n        asc && ret[0] > ret[1] && ret.reverse();\n        return ret;\n      };\n\n      Axis2D.prototype.pointToData = function (point, clamp) {\n        return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);\n      };\n      /**\n       * Set ordinalSortInfo\n       * @param info new OrdinalSortInfo\n       */\n\n\n      Axis2D.prototype.setCategorySortInfo = function (info) {\n        if (this.type !== 'category') {\n          return false;\n        }\n\n        this.model.option.categorySortInfo = info;\n        this.scale.setSortInfo(info);\n      };\n\n      return Axis2D;\n    }(Axis);\n\n    /**\n     * Can only be called after coordinate system creation stage.\n     * (Can be called before coordinate system update stage).\n     */\n\n    function layout$1(gridModel, axisModel, opt) {\n      opt = opt || {};\n      var grid = gridModel.coordinateSystem;\n      var axis = axisModel.axis;\n      var layout = {};\n      var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0];\n      var rawAxisPosition = axis.position;\n      var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition;\n      var axisDim = axis.dim;\n      var rect = grid.getRect();\n      var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];\n      var idx = {\n        left: 0,\n        right: 1,\n        top: 0,\n        bottom: 1,\n        onZero: 2\n      };\n      var axisOffset = axisModel.get('offset') || 0;\n      var posBound = axisDim === 'x' ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];\n\n      if (otherAxisOnZeroOf) {\n        var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0));\n        posBound[idx.onZero] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);\n      } // Axis position\n\n\n      layout.position = [axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]]; // Axis rotation\n\n      layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); // Tick and label direction, x y is axisDim\n\n      var dirMap = {\n        top: -1,\n        bottom: 1,\n        left: -1,\n        right: 1\n      };\n      layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];\n      layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0;\n\n      if (axisModel.get(['axisTick', 'inside'])) {\n        layout.tickDirection = -layout.tickDirection;\n      }\n\n      if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) {\n        layout.labelDirection = -layout.labelDirection;\n      } // Special label rotation\n\n\n      var labelRotate = axisModel.get(['axisLabel', 'rotate']);\n      layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; // Over splitLine and splitArea\n\n      layout.z2 = 1;\n      return layout;\n    }\n    function isCartesian2DSeries(seriesModel) {\n      return seriesModel.get('coordinateSystem') === 'cartesian2d';\n    }\n    function findAxisModels(seriesModel) {\n      var axisModelMap = {\n        xAxisModel: null,\n        yAxisModel: null\n      };\n      each(axisModelMap, function (v, key) {\n        var axisType = key.replace(/Model$/, '');\n        var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0];\n\n        if (\"development\" !== 'production') {\n          if (!axisModel) {\n            throw new Error(axisType + ' \"' + retrieve3(seriesModel.get(axisType + 'Index'), seriesModel.get(axisType + 'Id'), 0) + '\" not found');\n          }\n        }\n\n        axisModelMap[key] = axisModel;\n      });\n      return axisModelMap;\n    }\n\n    var mathLog$1 = Math.log;\n    function alignScaleTicks(scale, axisModel, alignToScale) {\n      var intervalScaleProto = IntervalScale.prototype; // NOTE: There is a precondition for log scale  here:\n      // In log scale we store _interval and _extent of exponent value.\n      // So if we use the method of InternalScale to set/get these data.\n      // It process the exponent value, which is linear and what we want here.\n\n      var alignToTicks = intervalScaleProto.getTicks.call(alignToScale);\n      var alignToNicedTicks = intervalScaleProto.getTicks.call(alignToScale, true);\n      var alignToSplitNumber = alignToTicks.length - 1;\n      var alignToInterval = intervalScaleProto.getInterval.call(alignToScale);\n      var scaleExtent = getScaleExtent(scale, axisModel);\n      var rawExtent = scaleExtent.extent;\n      var isMinFixed = scaleExtent.fixMin;\n      var isMaxFixed = scaleExtent.fixMax;\n\n      if (scale.type === 'log') {\n        var logBase = mathLog$1(scale.base);\n        rawExtent = [mathLog$1(rawExtent[0]) / logBase, mathLog$1(rawExtent[1]) / logBase];\n      }\n\n      scale.setExtent(rawExtent[0], rawExtent[1]);\n      scale.calcNiceExtent({\n        splitNumber: alignToSplitNumber,\n        fixMin: isMinFixed,\n        fixMax: isMaxFixed\n      });\n      var extent = intervalScaleProto.getExtent.call(scale); // Need to update the rawExtent.\n      // Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax'\n\n      if (isMinFixed) {\n        rawExtent[0] = extent[0];\n      }\n\n      if (isMaxFixed) {\n        rawExtent[1] = extent[1];\n      }\n\n      var interval = intervalScaleProto.getInterval.call(scale);\n      var min = rawExtent[0];\n      var max = rawExtent[1];\n\n      if (isMinFixed && isMaxFixed) {\n        // User set min, max, divide to get new interval\n        interval = (max - min) / alignToSplitNumber;\n      } else if (isMinFixed) {\n        max = rawExtent[0] + interval * alignToSplitNumber; // User set min, expand extent on the other side\n\n        while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])) {\n          interval = increaseInterval(interval);\n          max = rawExtent[0] + interval * alignToSplitNumber;\n        }\n      } else if (isMaxFixed) {\n        // User set max, expand extent on the other side\n        min = rawExtent[1] - interval * alignToSplitNumber;\n\n        while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])) {\n          interval = increaseInterval(interval);\n          min = rawExtent[1] - interval * alignToSplitNumber;\n        }\n      } else {\n        var nicedSplitNumber = scale.getTicks().length - 1;\n\n        if (nicedSplitNumber > alignToSplitNumber) {\n          interval = increaseInterval(interval);\n        }\n\n        var range = interval * alignToSplitNumber;\n        max = Math.ceil(rawExtent[1] / interval) * interval;\n        min = round(max - range); // Not change the result that crossing zero.\n\n        if (min < 0 && rawExtent[0] >= 0) {\n          min = 0;\n          max = round(range);\n        } else if (max > 0 && rawExtent[1] <= 0) {\n          max = 0;\n          min = -round(range);\n        }\n      } // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale\n\n\n      var t0 = (alignToTicks[0].value - alignToNicedTicks[0].value) / alignToInterval;\n      var t1 = (alignToTicks[alignToSplitNumber].value - alignToNicedTicks[alignToSplitNumber].value) / alignToInterval; // NOTE: Must in setExtent -> setInterval -> setNiceExtent order.\n\n      intervalScaleProto.setExtent.call(scale, min + interval * t0, max + interval * t1);\n      intervalScaleProto.setInterval.call(scale, interval);\n\n      if (t0 || t1) {\n        intervalScaleProto.setNiceExtent.call(scale, min + interval, max - interval);\n      }\n\n      if (\"development\" !== 'production') {\n        var ticks = intervalScaleProto.getTicks.call(scale);\n\n        if (ticks[1] && (!isValueNice(interval) || getPrecisionSafe(ticks[1].value) > getPrecisionSafe(interval))) {\n          warn( // eslint-disable-next-line\n          \"The ticks may be not readable when set min: \" + axisModel.get('min') + \", max: \" + axisModel.get('max') + \" and alignTicks: true\");\n        }\n      }\n    }\n\n    var Grid =\n    /** @class */\n    function () {\n      function Grid(gridModel, ecModel, api) {\n        // FIXME:TS where used (different from registered type 'cartesian2d')?\n        this.type = 'grid';\n        this._coordsMap = {};\n        this._coordsList = [];\n        this._axesMap = {};\n        this._axesList = [];\n        this.axisPointerEnabled = true;\n        this.dimensions = cartesian2DDimensions;\n\n        this._initCartesian(gridModel, ecModel, api);\n\n        this.model = gridModel;\n      }\n\n      Grid.prototype.getRect = function () {\n        return this._rect;\n      };\n\n      Grid.prototype.update = function (ecModel, api) {\n        var axesMap = this._axesMap;\n\n        this._updateScale(ecModel, this.model);\n\n        function updateAxisTicks(axes) {\n          var alignTo; // Axis is added in order of axisIndex.\n\n          var axesIndices = keys(axes);\n          var len = axesIndices.length;\n\n          if (!len) {\n            return;\n          }\n\n          var axisNeedsAlign = []; // Process once and calculate the ticks for those don't use alignTicks.\n\n          for (var i = len - 1; i >= 0; i--) {\n            var idx = +axesIndices[i]; // Convert to number.\n\n            var axis = axes[idx];\n            var model = axis.model;\n            var scale = axis.scale;\n\n            if ( // Only value and log axis without interval support alignTicks.\n            isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) {\n              axisNeedsAlign.push(axis);\n            } else {\n              niceScaleExtent(scale, model);\n\n              if (isIntervalOrLogScale(scale)) {\n                // Can only align to interval or log axis.\n                alignTo = axis;\n              }\n            }\n          }\n          // PENDING. Should we find the axis that both set interval, min, max and align to this one?\n\n          if (axisNeedsAlign.length) {\n            if (!alignTo) {\n              alignTo = axisNeedsAlign.pop();\n              niceScaleExtent(alignTo.scale, alignTo.model);\n            }\n\n            each(axisNeedsAlign, function (axis) {\n              alignScaleTicks(axis.scale, axis.model, alignTo.scale);\n            });\n          }\n        }\n\n        updateAxisTicks(axesMap.x);\n        updateAxisTicks(axesMap.y); // Key: axisDim_axisIndex, value: boolean, whether onZero target.\n\n        var onZeroRecords = {};\n        each(axesMap.x, function (xAxis) {\n          fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords);\n        });\n        each(axesMap.y, function (yAxis) {\n          fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords);\n        }); // Resize again if containLabel is enabled\n        // FIXME It may cause getting wrong grid size in data processing stage\n\n        this.resize(this.model, api);\n      };\n      /**\n       * Resize the grid\n       */\n\n\n      Grid.prototype.resize = function (gridModel, api, ignoreContainLabel) {\n        var boxLayoutParams = gridModel.getBoxLayoutParams();\n        var isContainLabel = !ignoreContainLabel && gridModel.get('containLabel');\n        var gridRect = getLayoutRect(boxLayoutParams, {\n          width: api.getWidth(),\n          height: api.getHeight()\n        });\n        this._rect = gridRect;\n        var axesList = this._axesList;\n        adjustAxes(); // Minus label size\n\n        if (isContainLabel) {\n          each(axesList, function (axis) {\n            if (!axis.model.get(['axisLabel', 'inside'])) {\n              var labelUnionRect = estimateLabelUnionRect(axis);\n\n              if (labelUnionRect) {\n                var dim = axis.isHorizontal() ? 'height' : 'width';\n                var margin = axis.model.get(['axisLabel', 'margin']);\n                gridRect[dim] -= labelUnionRect[dim] + margin;\n\n                if (axis.position === 'top') {\n                  gridRect.y += labelUnionRect.height + margin;\n                } else if (axis.position === 'left') {\n                  gridRect.x += labelUnionRect.width + margin;\n                }\n              }\n            }\n          });\n          adjustAxes();\n        }\n\n        each(this._coordsList, function (coord) {\n          // Calculate affine matrix to accelerate the data to point transform.\n          // If all the axes scales are time or value.\n          coord.calcAffineTransform();\n        });\n\n        function adjustAxes() {\n          each(axesList, function (axis) {\n            var isHorizontal = axis.isHorizontal();\n            var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];\n            var idx = axis.inverse ? 1 : 0;\n            axis.setExtent(extent[idx], extent[1 - idx]);\n            updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);\n          });\n        }\n      };\n\n      Grid.prototype.getAxis = function (dim, axisIndex) {\n        var axesMapOnDim = this._axesMap[dim];\n\n        if (axesMapOnDim != null) {\n          return axesMapOnDim[axisIndex || 0];\n        }\n      };\n\n      Grid.prototype.getAxes = function () {\n        return this._axesList.slice();\n      };\n\n      Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) {\n        if (xAxisIndex != null && yAxisIndex != null) {\n          var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n          return this._coordsMap[key];\n        }\n\n        if (isObject(xAxisIndex)) {\n          yAxisIndex = xAxisIndex.yAxisIndex;\n          xAxisIndex = xAxisIndex.xAxisIndex;\n        }\n\n        for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {\n          if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {\n            return coordList[i];\n          }\n        }\n      };\n\n      Grid.prototype.getCartesians = function () {\n        return this._coordsList.slice();\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.convertToPixel = function (ecModel, finder, value) {\n        var target = this._findConvertTarget(finder);\n\n        return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.convertFromPixel = function (ecModel, finder, value) {\n        var target = this._findConvertTarget(finder);\n\n        return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;\n      };\n\n      Grid.prototype._findConvertTarget = function (finder) {\n        var seriesModel = finder.seriesModel;\n        var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];\n        var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];\n        var gridModel = finder.gridModel;\n        var coordsList = this._coordsList;\n        var cartesian;\n        var axis;\n\n        if (seriesModel) {\n          cartesian = seriesModel.coordinateSystem;\n          indexOf(coordsList, cartesian) < 0 && (cartesian = null);\n        } else if (xAxisModel && yAxisModel) {\n          cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n        } else if (xAxisModel) {\n          axis = this.getAxis('x', xAxisModel.componentIndex);\n        } else if (yAxisModel) {\n          axis = this.getAxis('y', yAxisModel.componentIndex);\n        } // Lowest priority.\n        else if (gridModel) {\n            var grid = gridModel.coordinateSystem;\n\n            if (grid === this) {\n              cartesian = this._coordsList[0];\n            }\n          }\n\n        return {\n          cartesian: cartesian,\n          axis: axis\n        };\n      };\n      /**\n       * @implements\n       */\n\n\n      Grid.prototype.containPoint = function (point) {\n        var coord = this._coordsList[0];\n\n        if (coord) {\n          return coord.containPoint(point);\n        }\n      };\n      /**\n       * Initialize cartesian coordinate systems\n       */\n\n\n      Grid.prototype._initCartesian = function (gridModel, ecModel, api) {\n        var _this = this;\n\n        var grid = this;\n        var axisPositionUsed = {\n          left: false,\n          right: false,\n          top: false,\n          bottom: false\n        };\n        var axesMap = {\n          x: {},\n          y: {}\n        };\n        var axesCount = {\n          x: 0,\n          y: 0\n        }; // Create axis\n\n        ecModel.eachComponent('xAxis', createAxisCreator('x'), this);\n        ecModel.eachComponent('yAxis', createAxisCreator('y'), this);\n\n        if (!axesCount.x || !axesCount.y) {\n          // Roll back when there no either x or y axis\n          this._axesMap = {};\n          this._axesList = [];\n          return;\n        }\n\n        this._axesMap = axesMap; // Create cartesian2d\n\n        each(axesMap.x, function (xAxis, xAxisIndex) {\n          each(axesMap.y, function (yAxis, yAxisIndex) {\n            var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n            var cartesian = new Cartesian2D(key);\n            cartesian.master = _this;\n            cartesian.model = gridModel;\n            _this._coordsMap[key] = cartesian;\n\n            _this._coordsList.push(cartesian);\n\n            cartesian.addAxis(xAxis);\n            cartesian.addAxis(yAxis);\n          });\n        });\n\n        function createAxisCreator(dimName) {\n          return function (axisModel, idx) {\n            if (!isAxisUsedInTheGrid(axisModel, gridModel)) {\n              return;\n            }\n\n            var axisPosition = axisModel.get('position');\n\n            if (dimName === 'x') {\n              // Fix position\n              if (axisPosition !== 'top' && axisPosition !== 'bottom') {\n                // Default bottom of X\n                axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom';\n              }\n            } else {\n              // Fix position\n              if (axisPosition !== 'left' && axisPosition !== 'right') {\n                // Default left of Y\n                axisPosition = axisPositionUsed.left ? 'right' : 'left';\n              }\n            }\n\n            axisPositionUsed[axisPosition] = true;\n            var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);\n            var isCategory = axis.type === 'category';\n            axis.onBand = isCategory && axisModel.get('boundaryGap');\n            axis.inverse = axisModel.get('inverse'); // Inject axis into axisModel\n\n            axisModel.axis = axis; // Inject axisModel into axis\n\n            axis.model = axisModel; // Inject grid info axis\n\n            axis.grid = grid; // Index of axis, can be used as key\n\n            axis.index = idx;\n\n            grid._axesList.push(axis);\n\n            axesMap[dimName][idx] = axis;\n            axesCount[dimName]++;\n          };\n        }\n      };\n      /**\n       * Update cartesian properties from series.\n       */\n\n\n      Grid.prototype._updateScale = function (ecModel, gridModel) {\n        // Reset scale\n        each(this._axesList, function (axis) {\n          axis.scale.setExtent(Infinity, -Infinity);\n\n          if (axis.type === 'category') {\n            var categorySortInfo = axis.model.get('categorySortInfo');\n            axis.scale.setSortInfo(categorySortInfo);\n          }\n        });\n        ecModel.eachSeries(function (seriesModel) {\n          if (isCartesian2DSeries(seriesModel)) {\n            var axesModelMap = findAxisModels(seriesModel);\n            var xAxisModel = axesModelMap.xAxisModel;\n            var yAxisModel = axesModelMap.yAxisModel;\n\n            if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) {\n              return;\n            }\n\n            var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n            var data = seriesModel.getData();\n            var xAxis = cartesian.getAxis('x');\n            var yAxis = cartesian.getAxis('y');\n            unionExtent(data, xAxis);\n            unionExtent(data, yAxis);\n          }\n        }, this);\n\n        function unionExtent(data, axis) {\n          each(getDataDimensionsOnAxis(data, axis.dim), function (dim) {\n            axis.scale.unionExtentFromData(data, dim);\n          });\n        }\n      };\n      /**\n       * @param dim 'x' or 'y' or 'auto' or null/undefined\n       */\n\n\n      Grid.prototype.getTooltipAxes = function (dim) {\n        var baseAxes = [];\n        var otherAxes = [];\n        each(this.getCartesians(), function (cartesian) {\n          var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();\n          var otherAxis = cartesian.getOtherAxis(baseAxis);\n          indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);\n          indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);\n        });\n        return {\n          baseAxes: baseAxes,\n          otherAxes: otherAxes\n        };\n      };\n\n      Grid.create = function (ecModel, api) {\n        var grids = [];\n        ecModel.eachComponent('grid', function (gridModel, idx) {\n          var grid = new Grid(gridModel, ecModel, api);\n          grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize\n          // should be performed in create stage.\n\n          grid.resize(gridModel, api, true);\n          gridModel.coordinateSystem = grid;\n          grids.push(grid);\n        }); // Inject the coordinateSystems into seriesModel\n\n        ecModel.eachSeries(function (seriesModel) {\n          if (!isCartesian2DSeries(seriesModel)) {\n            return;\n          }\n\n          var axesModelMap = findAxisModels(seriesModel);\n          var xAxisModel = axesModelMap.xAxisModel;\n          var yAxisModel = axesModelMap.yAxisModel;\n          var gridModel = xAxisModel.getCoordSysModel();\n\n          if (\"development\" !== 'production') {\n            if (!gridModel) {\n              throw new Error('Grid \"' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '\" not found');\n            }\n\n            if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {\n              throw new Error('xAxis and yAxis must use the same grid');\n            }\n          }\n\n          var grid = gridModel.coordinateSystem;\n          seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n        });\n        return grids;\n      }; // For deciding which dimensions to use when creating list data\n\n\n      Grid.dimensions = cartesian2DDimensions;\n      return Grid;\n    }();\n    /**\n     * Check if the axis is used in the specified grid.\n     */\n\n\n    function isAxisUsedInTheGrid(axisModel, gridModel) {\n      return axisModel.getCoordSysModel() === gridModel;\n    }\n\n    function fixAxisOnZero(axesMap, otherAxisDim, axis, // Key: see `getOnZeroRecordKey`\n    onZeroRecords) {\n      axis.getAxesOnZeroOf = function () {\n        // TODO: onZero of multiple axes.\n        return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : [];\n      }; // onZero can not be enabled in these two situations:\n      // 1. When any other axis is a category axis.\n      // 2. When no axis is cross 0 point.\n\n\n      var otherAxes = axesMap[otherAxisDim];\n      var otherAxisOnZeroOf;\n      var axisModel = axis.model;\n      var onZero = axisModel.get(['axisLine', 'onZero']);\n      var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']);\n\n      if (!onZero) {\n        return;\n      } // If target axis is specified.\n\n\n      if (onZeroAxisIndex != null) {\n        if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {\n          otherAxisOnZeroOf = otherAxes[onZeroAxisIndex];\n        }\n      } else {\n        // Find the first available other axis.\n        for (var idx in otherAxes) {\n          if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx]) // Consider that two Y axes on one value axis,\n          // if both onZero, the two Y axes overlap.\n          && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) {\n            otherAxisOnZeroOf = otherAxes[idx];\n            break;\n          }\n        }\n      }\n\n      if (otherAxisOnZeroOf) {\n        onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true;\n      }\n\n      function getOnZeroRecordKey(axis) {\n        return axis.dim + '_' + axis.index;\n      }\n    }\n\n    function canOnZeroToAxis(axis) {\n      return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);\n    }\n\n    function updateAxisTransform(axis, coordBase) {\n      var axisExtent = axis.getExtent();\n      var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform\n\n      axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {\n        return coord + coordBase;\n      } : function (coord) {\n        return axisExtentSum - coord + coordBase;\n      };\n      axis.toLocalCoord = axis.dim === 'x' ? function (coord) {\n        return coord - coordBase;\n      } : function (coord) {\n        return axisExtentSum - coord + coordBase;\n      };\n    }\n\n    var PI$4 = Math.PI;\n    /**\n     * A final axis is translated and rotated from a \"standard axis\".\n     * So opt.position and opt.rotation is required.\n     *\n     * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],\n     * for example: (0, 0) ------------> (0, 50)\n     *\n     * nameDirection or tickDirection or labelDirection is 1 means tick\n     * or label is below the standard axis, whereas is -1 means above\n     * the standard axis. labelOffset means offset between label and axis,\n     * which is useful when 'onZero', where axisLabel is in the grid and\n     * label in outside grid.\n     *\n     * Tips: like always,\n     * positive rotation represents anticlockwise, and negative rotation\n     * represents clockwise.\n     * The direction of position coordinate is the same as the direction\n     * of screen coordinate.\n     *\n     * Do not need to consider axis 'inverse', which is auto processed by\n     * axis extent.\n     */\n\n    var AxisBuilder =\n    /** @class */\n    function () {\n      function AxisBuilder(axisModel, opt) {\n        this.group = new Group();\n        this.opt = opt;\n        this.axisModel = axisModel; // Default value\n\n        defaults(opt, {\n          labelOffset: 0,\n          nameDirection: 1,\n          tickDirection: 1,\n          labelDirection: 1,\n          silent: true,\n          handleAutoShown: function () {\n            return true;\n          }\n        }); // FIXME Not use a seperate text group?\n\n        var transformGroup = new Group({\n          x: opt.position[0],\n          y: opt.position[1],\n          rotation: opt.rotation\n        }); // this.group.add(transformGroup);\n        // this._transformGroup = transformGroup;\n\n        transformGroup.updateTransform();\n        this._transformGroup = transformGroup;\n      }\n\n      AxisBuilder.prototype.hasBuilder = function (name) {\n        return !!builders[name];\n      };\n\n      AxisBuilder.prototype.add = function (name) {\n        builders[name](this.opt, this.axisModel, this.group, this._transformGroup);\n      };\n\n      AxisBuilder.prototype.getGroup = function () {\n        return this.group;\n      };\n\n      AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {\n        var rotationDiff = remRadian(textRotation - axisRotation);\n        var textAlign;\n        var textVerticalAlign;\n\n        if (isRadianAroundZero(rotationDiff)) {\n          // Label is parallel with axis line.\n          textVerticalAlign = direction > 0 ? 'top' : 'bottom';\n          textAlign = 'center';\n        } else if (isRadianAroundZero(rotationDiff - PI$4)) {\n          // Label is inverse parallel with axis line.\n          textVerticalAlign = direction > 0 ? 'bottom' : 'top';\n          textAlign = 'center';\n        } else {\n          textVerticalAlign = 'middle';\n\n          if (rotationDiff > 0 && rotationDiff < PI$4) {\n            textAlign = direction > 0 ? 'right' : 'left';\n          } else {\n            textAlign = direction > 0 ? 'left' : 'right';\n          }\n        }\n\n        return {\n          rotation: rotationDiff,\n          textAlign: textAlign,\n          textVerticalAlign: textVerticalAlign\n        };\n      };\n\n      AxisBuilder.makeAxisEventDataBase = function (axisModel) {\n        var eventData = {\n          componentType: axisModel.mainType,\n          componentIndex: axisModel.componentIndex\n        };\n        eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;\n        return eventData;\n      };\n\n      AxisBuilder.isLabelSilent = function (axisModel) {\n        var tooltipOpt = axisModel.get('tooltip');\n        return axisModel.get('silent') // Consider mouse cursor, add these restrictions.\n        || !(axisModel.get('triggerEvent') || tooltipOpt && tooltipOpt.show);\n      };\n\n      return AxisBuilder;\n    }();\n    var builders = {\n      axisLine: function (opt, axisModel, group, transformGroup) {\n        var shown = axisModel.get(['axisLine', 'show']);\n\n        if (shown === 'auto' && opt.handleAutoShown) {\n          shown = opt.handleAutoShown('axisLine');\n        }\n\n        if (!shown) {\n          return;\n        }\n\n        var extent = axisModel.axis.getExtent();\n        var matrix = transformGroup.transform;\n        var pt1 = [extent[0], 0];\n        var pt2 = [extent[1], 0];\n        var inverse = pt1[0] > pt2[0];\n\n        if (matrix) {\n          applyTransform(pt1, pt1, matrix);\n          applyTransform(pt2, pt2, matrix);\n        }\n\n        var lineStyle = extend({\n          lineCap: 'round'\n        }, axisModel.getModel(['axisLine', 'lineStyle']).getLineStyle());\n        var line = new Line({\n          shape: {\n            x1: pt1[0],\n            y1: pt1[1],\n            x2: pt2[0],\n            y2: pt2[1]\n          },\n          style: lineStyle,\n          strokeContainThreshold: opt.strokeContainThreshold || 5,\n          silent: true,\n          z2: 1\n        });\n        subPixelOptimizeLine$1(line.shape, line.style.lineWidth);\n        line.anid = 'line';\n        group.add(line);\n        var arrows = axisModel.get(['axisLine', 'symbol']);\n\n        if (arrows != null) {\n          var arrowSize = axisModel.get(['axisLine', 'symbolSize']);\n\n          if (isString(arrows)) {\n            // Use the same arrow for start and end point\n            arrows = [arrows, arrows];\n          }\n\n          if (isString(arrowSize) || isNumber(arrowSize)) {\n            // Use the same size for width and height\n            arrowSize = [arrowSize, arrowSize];\n          }\n\n          var arrowOffset = normalizeSymbolOffset(axisModel.get(['axisLine', 'symbolOffset']) || 0, arrowSize);\n          var symbolWidth_1 = arrowSize[0];\n          var symbolHeight_1 = arrowSize[1];\n          each([{\n            rotate: opt.rotation + Math.PI / 2,\n            offset: arrowOffset[0],\n            r: 0\n          }, {\n            rotate: opt.rotation - Math.PI / 2,\n            offset: arrowOffset[1],\n            r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1]))\n          }], function (point, index) {\n            if (arrows[index] !== 'none' && arrows[index] != null) {\n              var symbol = createSymbol(arrows[index], -symbolWidth_1 / 2, -symbolHeight_1 / 2, symbolWidth_1, symbolHeight_1, lineStyle.stroke, true); // Calculate arrow position with offset\n\n              var r = point.r + point.offset;\n              var pt = inverse ? pt2 : pt1;\n              symbol.attr({\n                rotation: point.rotate,\n                x: pt[0] + r * Math.cos(opt.rotation),\n                y: pt[1] - r * Math.sin(opt.rotation),\n                silent: true,\n                z2: 11\n              });\n              group.add(symbol);\n            }\n          });\n        }\n      },\n      axisTickLabel: function (opt, axisModel, group, transformGroup) {\n        var ticksEls = buildAxisMajorTicks(group, transformGroup, axisModel, opt);\n        var labelEls = buildAxisLabel(group, transformGroup, axisModel, opt);\n        fixMinMaxLabelShow(axisModel, labelEls, ticksEls);\n        buildAxisMinorTicks(group, transformGroup, axisModel, opt.tickDirection); // This bit fixes the label overlap issue for the time chart.\n        // See https://github.com/apache/echarts/issues/14266 for more.\n\n        if (axisModel.get(['axisLabel', 'hideOverlap'])) {\n          var labelList = prepareLayoutList(map(labelEls, function (label) {\n            return {\n              label: label,\n              priority: label.z2,\n              defaultAttr: {\n                ignore: label.ignore\n              }\n            };\n          }));\n          hideOverlap(labelList);\n        }\n      },\n      axisName: function (opt, axisModel, group, transformGroup) {\n        var name = retrieve(opt.axisName, axisModel.get('name'));\n\n        if (!name) {\n          return;\n        }\n\n        var nameLocation = axisModel.get('nameLocation');\n        var nameDirection = opt.nameDirection;\n        var textStyleModel = axisModel.getModel('nameTextStyle');\n        var gap = axisModel.get('nameGap') || 0;\n        var extent = axisModel.axis.getExtent();\n        var gapSignal = extent[0] > extent[1] ? -1 : 1;\n        var pos = [nameLocation === 'start' ? extent[0] - gapSignal * gap : nameLocation === 'end' ? extent[1] + gapSignal * gap : (extent[0] + extent[1]) / 2, // Reuse labelOffset.\n        isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0];\n        var labelLayout;\n        var nameRotation = axisModel.get('nameRotate');\n\n        if (nameRotation != null) {\n          nameRotation = nameRotation * PI$4 / 180; // To radian.\n        }\n\n        var axisNameAvailableWidth;\n\n        if (isNameLocationCenter(nameLocation)) {\n          labelLayout = AxisBuilder.innerTextLayout(opt.rotation, nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.\n          nameDirection);\n        } else {\n          labelLayout = endTextLayout(opt.rotation, nameLocation, nameRotation || 0, extent);\n          axisNameAvailableWidth = opt.axisNameAvailableWidth;\n\n          if (axisNameAvailableWidth != null) {\n            axisNameAvailableWidth = Math.abs(axisNameAvailableWidth / Math.sin(labelLayout.rotation));\n            !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);\n          }\n        }\n\n        var textFont = textStyleModel.getFont();\n        var truncateOpt = axisModel.get('nameTruncate', true) || {};\n        var ellipsis = truncateOpt.ellipsis;\n        var maxWidth = retrieve(opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth);\n        var textEl = new ZRText({\n          x: pos[0],\n          y: pos[1],\n          rotation: labelLayout.rotation,\n          silent: AxisBuilder.isLabelSilent(axisModel),\n          style: createTextStyle(textStyleModel, {\n            text: name,\n            font: textFont,\n            overflow: 'truncate',\n            width: maxWidth,\n            ellipsis: ellipsis,\n            fill: textStyleModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']),\n            align: textStyleModel.get('align') || labelLayout.textAlign,\n            verticalAlign: textStyleModel.get('verticalAlign') || labelLayout.textVerticalAlign\n          }),\n          z2: 1\n        });\n        setTooltipConfig({\n          el: textEl,\n          componentModel: axisModel,\n          itemName: name\n        });\n        textEl.__fullText = name; // Id for animation\n\n        textEl.anid = 'name';\n\n        if (axisModel.get('triggerEvent')) {\n          var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n          eventData.targetType = 'axisName';\n          eventData.name = name;\n          getECData(textEl).eventData = eventData;\n        } // FIXME\n\n\n        transformGroup.add(textEl);\n        textEl.updateTransform();\n        group.add(textEl);\n        textEl.decomposeTransform();\n      }\n    };\n\n    function endTextLayout(rotation, textPosition, textRotate, extent) {\n      var rotationDiff = remRadian(textRotate - rotation);\n      var textAlign;\n      var textVerticalAlign;\n      var inverse = extent[0] > extent[1];\n      var onLeft = textPosition === 'start' && !inverse || textPosition !== 'start' && inverse;\n\n      if (isRadianAroundZero(rotationDiff - PI$4 / 2)) {\n        textVerticalAlign = onLeft ? 'bottom' : 'top';\n        textAlign = 'center';\n      } else if (isRadianAroundZero(rotationDiff - PI$4 * 1.5)) {\n        textVerticalAlign = onLeft ? 'top' : 'bottom';\n        textAlign = 'center';\n      } else {\n        textVerticalAlign = 'middle';\n\n        if (rotationDiff < PI$4 * 1.5 && rotationDiff > PI$4 / 2) {\n          textAlign = onLeft ? 'left' : 'right';\n        } else {\n          textAlign = onLeft ? 'right' : 'left';\n        }\n      }\n\n      return {\n        rotation: rotationDiff,\n        textAlign: textAlign,\n        textVerticalAlign: textVerticalAlign\n      };\n    }\n\n    function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {\n      if (shouldShowAllLabels(axisModel.axis)) {\n        return;\n      } // If min or max are user set, we need to check\n      // If the tick on min(max) are overlap on their neighbour tick\n      // If they are overlapped, we need to hide the min(max) tick label\n\n\n      var showMinLabel = axisModel.get(['axisLabel', 'showMinLabel']);\n      var showMaxLabel = axisModel.get(['axisLabel', 'showMaxLabel']); // FIXME\n      // Have not consider onBand yet, where tick els is more than label els.\n\n      labelEls = labelEls || [];\n      tickEls = tickEls || [];\n      var firstLabel = labelEls[0];\n      var nextLabel = labelEls[1];\n      var lastLabel = labelEls[labelEls.length - 1];\n      var prevLabel = labelEls[labelEls.length - 2];\n      var firstTick = tickEls[0];\n      var nextTick = tickEls[1];\n      var lastTick = tickEls[tickEls.length - 1];\n      var prevTick = tickEls[tickEls.length - 2];\n\n      if (showMinLabel === false) {\n        ignoreEl(firstLabel);\n        ignoreEl(firstTick);\n      } else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {\n        if (showMinLabel) {\n          ignoreEl(nextLabel);\n          ignoreEl(nextTick);\n        } else {\n          ignoreEl(firstLabel);\n          ignoreEl(firstTick);\n        }\n      }\n\n      if (showMaxLabel === false) {\n        ignoreEl(lastLabel);\n        ignoreEl(lastTick);\n      } else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {\n        if (showMaxLabel) {\n          ignoreEl(prevLabel);\n          ignoreEl(prevTick);\n        } else {\n          ignoreEl(lastLabel);\n          ignoreEl(lastTick);\n        }\n      }\n    }\n\n    function ignoreEl(el) {\n      el && (el.ignore = true);\n    }\n\n    function isTwoLabelOverlapped(current, next) {\n      // current and next has the same rotation.\n      var firstRect = current && current.getBoundingRect().clone();\n      var nextRect = next && next.getBoundingRect().clone();\n\n      if (!firstRect || !nextRect) {\n        return;\n      } // When checking intersect of two rotated labels, we use mRotationBack\n      // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.\n\n\n      var mRotationBack = identity([]);\n      rotate(mRotationBack, mRotationBack, -current.rotation);\n      firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));\n      nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));\n      return firstRect.intersect(nextRect);\n    }\n\n    function isNameLocationCenter(nameLocation) {\n      return nameLocation === 'middle' || nameLocation === 'center';\n    }\n\n    function createTicks(ticksCoords, tickTransform, tickEndCoord, tickLineStyle, anidPrefix) {\n      var tickEls = [];\n      var pt1 = [];\n      var pt2 = [];\n\n      for (var i = 0; i < ticksCoords.length; i++) {\n        var tickCoord = ticksCoords[i].coord;\n        pt1[0] = tickCoord;\n        pt1[1] = 0;\n        pt2[0] = tickCoord;\n        pt2[1] = tickEndCoord;\n\n        if (tickTransform) {\n          applyTransform(pt1, pt1, tickTransform);\n          applyTransform(pt2, pt2, tickTransform);\n        } // Tick line, Not use group transform to have better line draw\n\n\n        var tickEl = new Line({\n          shape: {\n            x1: pt1[0],\n            y1: pt1[1],\n            x2: pt2[0],\n            y2: pt2[1]\n          },\n          style: tickLineStyle,\n          z2: 2,\n          autoBatch: true,\n          silent: true\n        });\n        subPixelOptimizeLine$1(tickEl.shape, tickEl.style.lineWidth);\n        tickEl.anid = anidPrefix + '_' + ticksCoords[i].tickValue;\n        tickEls.push(tickEl);\n      }\n\n      return tickEls;\n    }\n\n    function buildAxisMajorTicks(group, transformGroup, axisModel, opt) {\n      var axis = axisModel.axis;\n      var tickModel = axisModel.getModel('axisTick');\n      var shown = tickModel.get('show');\n\n      if (shown === 'auto' && opt.handleAutoShown) {\n        shown = opt.handleAutoShown('axisTick');\n      }\n\n      if (!shown || axis.scale.isBlank()) {\n        return;\n      }\n\n      var lineStyleModel = tickModel.getModel('lineStyle');\n      var tickEndCoord = opt.tickDirection * tickModel.get('length');\n      var ticksCoords = axis.getTicksCoords();\n      var ticksEls = createTicks(ticksCoords, transformGroup.transform, tickEndCoord, defaults(lineStyleModel.getLineStyle(), {\n        stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n      }), 'ticks');\n\n      for (var i = 0; i < ticksEls.length; i++) {\n        group.add(ticksEls[i]);\n      }\n\n      return ticksEls;\n    }\n\n    function buildAxisMinorTicks(group, transformGroup, axisModel, tickDirection) {\n      var axis = axisModel.axis;\n      var minorTickModel = axisModel.getModel('minorTick');\n\n      if (!minorTickModel.get('show') || axis.scale.isBlank()) {\n        return;\n      }\n\n      var minorTicksCoords = axis.getMinorTicksCoords();\n\n      if (!minorTicksCoords.length) {\n        return;\n      }\n\n      var lineStyleModel = minorTickModel.getModel('lineStyle');\n      var tickEndCoord = tickDirection * minorTickModel.get('length');\n      var minorTickLineStyle = defaults(lineStyleModel.getLineStyle(), defaults(axisModel.getModel('axisTick').getLineStyle(), {\n        stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])\n      }));\n\n      for (var i = 0; i < minorTicksCoords.length; i++) {\n        var minorTicksEls = createTicks(minorTicksCoords[i], transformGroup.transform, tickEndCoord, minorTickLineStyle, 'minorticks_' + i);\n\n        for (var k = 0; k < minorTicksEls.length; k++) {\n          group.add(minorTicksEls[k]);\n        }\n      }\n    }\n\n    function buildAxisLabel(group, transformGroup, axisModel, opt) {\n      var axis = axisModel.axis;\n      var show = retrieve(opt.axisLabelShow, axisModel.get(['axisLabel', 'show']));\n\n      if (!show || axis.scale.isBlank()) {\n        return;\n      }\n\n      var labelModel = axisModel.getModel('axisLabel');\n      var labelMargin = labelModel.get('margin');\n      var labels = axis.getViewLabels(); // Special label rotate.\n\n      var labelRotation = (retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) * PI$4 / 180;\n      var labelLayout = AxisBuilder.innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);\n      var rawCategoryData = axisModel.getCategories && axisModel.getCategories(true);\n      var labelEls = [];\n      var silent = AxisBuilder.isLabelSilent(axisModel);\n      var triggerEvent = axisModel.get('triggerEvent');\n      each(labels, function (labelItem, index) {\n        var tickValue = axis.scale.type === 'ordinal' ? axis.scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue;\n        var formattedLabel = labelItem.formattedLabel;\n        var rawLabel = labelItem.rawLabel;\n        var itemLabelModel = labelModel;\n\n        if (rawCategoryData && rawCategoryData[tickValue]) {\n          var rawCategoryItem = rawCategoryData[tickValue];\n\n          if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) {\n            itemLabelModel = new Model(rawCategoryItem.textStyle, labelModel, axisModel.ecModel);\n          }\n        }\n\n        var textColor = itemLabelModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']);\n        var tickCoord = axis.dataToCoord(tickValue);\n        var textEl = new ZRText({\n          x: tickCoord,\n          y: opt.labelOffset + opt.labelDirection * labelMargin,\n          rotation: labelLayout.rotation,\n          silent: silent,\n          z2: 10 + (labelItem.level || 0),\n          style: createTextStyle(itemLabelModel, {\n            text: formattedLabel,\n            align: itemLabelModel.getShallow('align', true) || labelLayout.textAlign,\n            verticalAlign: itemLabelModel.getShallow('verticalAlign', true) || itemLabelModel.getShallow('baseline', true) || labelLayout.textVerticalAlign,\n            fill: isFunction(textColor) ? textColor( // (1) In category axis with data zoom, tick is not the original\n            // index of axis.data. So tick should not be exposed to user\n            // in category axis.\n            // (2) Compatible with previous version, which always use formatted label as\n            // input. But in interval scale the formatted label is like '223,445', which\n            // maked user repalce ','. So we modify it to return original val but remain\n            // it as 'string' to avoid error in replacing.\n            axis.type === 'category' ? rawLabel : axis.type === 'value' ? tickValue + '' : tickValue, index) : textColor\n          })\n        });\n        textEl.anid = 'label_' + tickValue; // Pack data for mouse event\n\n        if (triggerEvent) {\n          var eventData = AxisBuilder.makeAxisEventDataBase(axisModel);\n          eventData.targetType = 'axisLabel';\n          eventData.value = rawLabel;\n          eventData.tickIndex = index;\n\n          if (axis.type === 'category') {\n            eventData.dataIndex = tickValue;\n          }\n\n          getECData(textEl).eventData = eventData;\n        } // FIXME\n\n\n        transformGroup.add(textEl);\n        textEl.updateTransform();\n        labelEls.push(textEl);\n        group.add(textEl);\n        textEl.decomposeTransform();\n      });\n      return labelEls;\n    }\n\n    function fixValue(axisModel) {\n      var axisInfo = getAxisInfo(axisModel);\n\n      if (!axisInfo) {\n        return;\n      }\n\n      var axisPointerModel = axisInfo.axisPointerModel;\n      var scale = axisInfo.axis.scale;\n      var option = axisPointerModel.option;\n      var status = axisPointerModel.get('status');\n      var value = axisPointerModel.get('value'); // Parse init value for category and time axis.\n\n      if (value != null) {\n        value = scale.parse(value);\n      }\n\n      var useHandle = isHandleTrigger(axisPointerModel); // If `handle` used, `axisPointer` will always be displayed, so value\n      // and status should be initialized.\n\n      if (status == null) {\n        option.status = useHandle ? 'show' : 'hide';\n      }\n\n      var extent = scale.getExtent().slice();\n      extent[0] > extent[1] && extent.reverse();\n\n      if ( // Pick a value on axis when initializing.\n      value == null // If both `handle` and `dataZoom` are used, value may be out of axis extent,\n      // where we should re-pick a value to keep `handle` displaying normally.\n      || value > extent[1]) {\n        // Make handle displayed on the end of the axis when init, which looks better.\n        value = extent[1];\n      }\n\n      if (value < extent[0]) {\n        value = extent[0];\n      }\n\n      option.value = value;\n\n      if (useHandle) {\n        option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';\n      }\n    }\n    function getAxisInfo(axisModel) {\n      var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;\n      return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];\n    }\n    function getAxisPointerModel(axisModel) {\n      var axisInfo = getAxisInfo(axisModel);\n      return axisInfo && axisInfo.axisPointerModel;\n    }\n\n    function isHandleTrigger(axisPointerModel) {\n      return !!axisPointerModel.get(['handle', 'show']);\n    }\n    /**\n     * @param {module:echarts/model/Model} model\n     * @return {string} unique key\n     */\n\n\n    function makeKey(model) {\n      return model.type + '||' + model.id;\n    }\n\n    var axisPointerClazz = {};\n    /**\n     * Base class of AxisView.\n     */\n\n    var AxisView =\n    /** @class */\n    function (_super) {\n      __extends(AxisView, _super);\n\n      function AxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = AxisView.type;\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        // FIXME\n        // This process should proformed after coordinate systems updated\n        // (axis scale updated), and should be performed each time update.\n        // So put it here temporarily, although it is not appropriate to\n        // put a model-writing procedure in `view`.\n        this.axisPointerClass && fixValue(axisModel);\n\n        _super.prototype.render.apply(this, arguments);\n\n        this._doUpdateAxisPointerClass(axisModel, api, true);\n      };\n      /**\n       * Action handler.\n       */\n\n\n      AxisView.prototype.updateAxisPointer = function (axisModel, ecModel, api, payload) {\n        this._doUpdateAxisPointerClass(axisModel, api, false);\n      };\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.remove = function (ecModel, api) {\n        var axisPointer = this._axisPointer;\n        axisPointer && axisPointer.remove(api);\n      };\n      /**\n       * @override\n       */\n\n\n      AxisView.prototype.dispose = function (ecModel, api) {\n        this._disposeAxisPointer(api);\n\n        _super.prototype.dispose.apply(this, arguments);\n      };\n\n      AxisView.prototype._doUpdateAxisPointerClass = function (axisModel, api, forceRender) {\n        var Clazz = AxisView.getAxisPointerClass(this.axisPointerClass);\n\n        if (!Clazz) {\n          return;\n        }\n\n        var axisPointerModel = getAxisPointerModel(axisModel);\n        axisPointerModel ? (this._axisPointer || (this._axisPointer = new Clazz())).render(axisModel, axisPointerModel, api, forceRender) : this._disposeAxisPointer(api);\n      };\n\n      AxisView.prototype._disposeAxisPointer = function (api) {\n        this._axisPointer && this._axisPointer.dispose(api);\n        this._axisPointer = null;\n      };\n\n      AxisView.registerAxisPointerClass = function (type, clazz) {\n        if (\"development\" !== 'production') {\n          if (axisPointerClazz[type]) {\n            throw new Error('axisPointer ' + type + ' exists');\n          }\n        }\n\n        axisPointerClazz[type] = clazz;\n      };\n\n      AxisView.getAxisPointerClass = function (type) {\n        return type && axisPointerClazz[type];\n      };\n      AxisView.type = 'axis';\n      return AxisView;\n    }(ComponentView);\n\n    var inner$6 = makeInner();\n    function rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel) {\n      var axis = axisModel.axis;\n\n      if (axis.scale.isBlank()) {\n        return;\n      } // TODO: TYPE\n\n\n      var splitAreaModel = axisModel.getModel('splitArea');\n      var areaStyleModel = splitAreaModel.getModel('areaStyle');\n      var areaColors = areaStyleModel.get('color');\n      var gridRect = gridModel.coordinateSystem.getRect();\n      var ticksCoords = axis.getTicksCoords({\n        tickModel: splitAreaModel,\n        clamp: true\n      });\n\n      if (!ticksCoords.length) {\n        return;\n      } // For Making appropriate splitArea animation, the color and anid\n      // should be corresponding to previous one if possible.\n\n\n      var areaColorsLen = areaColors.length;\n      var lastSplitAreaColors = inner$6(axisView).splitAreaColors;\n      var newSplitAreaColors = createHashMap();\n      var colorIndex = 0;\n\n      if (lastSplitAreaColors) {\n        for (var i = 0; i < ticksCoords.length; i++) {\n          var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue);\n\n          if (cIndex != null) {\n            colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen;\n            break;\n          }\n        }\n      }\n\n      var prev = axis.toGlobalCoord(ticksCoords[0].coord);\n      var areaStyle = areaStyleModel.getAreaStyle();\n      areaColors = isArray(areaColors) ? areaColors : [areaColors];\n\n      for (var i = 1; i < ticksCoords.length; i++) {\n        var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n        var x = void 0;\n        var y = void 0;\n        var width = void 0;\n        var height = void 0;\n\n        if (axis.isHorizontal()) {\n          x = prev;\n          y = gridRect.y;\n          width = tickCoord - x;\n          height = gridRect.height;\n          prev = x + width;\n        } else {\n          x = gridRect.x;\n          y = prev;\n          width = gridRect.width;\n          height = tickCoord - y;\n          prev = y + height;\n        }\n\n        var tickValue = ticksCoords[i - 1].tickValue;\n        tickValue != null && newSplitAreaColors.set(tickValue, colorIndex);\n        axisGroup.add(new Rect({\n          anid: tickValue != null ? 'area_' + tickValue : null,\n          shape: {\n            x: x,\n            y: y,\n            width: width,\n            height: height\n          },\n          style: defaults({\n            fill: areaColors[colorIndex]\n          }, areaStyle),\n          autoBatch: true,\n          silent: true\n        }));\n        colorIndex = (colorIndex + 1) % areaColorsLen;\n      }\n\n      inner$6(axisView).splitAreaColors = newSplitAreaColors;\n    }\n    function rectCoordAxisHandleRemove(axisView) {\n      inner$6(axisView).splitAreaColors = null;\n    }\n\n    var axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName'];\n    var selfBuilderAttrs = ['splitArea', 'splitLine', 'minorSplitLine'];\n\n    var CartesianAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianAxisView, _super);\n\n      function CartesianAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianAxisView.type;\n        _this.axisPointerClass = 'CartesianAxisPointer';\n        return _this;\n      }\n      /**\n       * @override\n       */\n\n\n      CartesianAxisView.prototype.render = function (axisModel, ecModel, api, payload) {\n        this.group.removeAll();\n        var oldAxisGroup = this._axisGroup;\n        this._axisGroup = new Group();\n        this.group.add(this._axisGroup);\n\n        if (!axisModel.get('show')) {\n          return;\n        }\n\n        var gridModel = axisModel.getCoordSysModel();\n        var layout = layout$1(gridModel, axisModel);\n        var axisBuilder = new AxisBuilder(axisModel, extend({\n          handleAutoShown: function (elementType) {\n            var cartesians = gridModel.coordinateSystem.getCartesians();\n\n            for (var i = 0; i < cartesians.length; i++) {\n              if (isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) {\n                // Still show axis tick or axisLine if other axis is value / log\n                return true;\n              }\n            } // Not show axisTick or axisLine if other axis is category / time\n\n\n            return false;\n          }\n        }, layout));\n        each(axisBuilderAttrs, axisBuilder.add, axisBuilder);\n\n        this._axisGroup.add(axisBuilder.getGroup());\n\n        each(selfBuilderAttrs, function (name) {\n          if (axisModel.get([name, 'show'])) {\n            axisElementBuilders[name](this, this._axisGroup, axisModel, gridModel);\n          }\n        }, this); // THIS is a special case for bar racing chart.\n        // Update the axis label from the natural initial layout to\n        // sorted layout should has no animation.\n\n        var isInitialSortFromBarRacing = payload && payload.type === 'changeAxisOrder' && payload.isInitSort;\n\n        if (!isInitialSortFromBarRacing) {\n          groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n        }\n\n        _super.prototype.render.call(this, axisModel, ecModel, api, payload);\n      };\n\n      CartesianAxisView.prototype.remove = function () {\n        rectCoordAxisHandleRemove(this);\n      };\n\n      CartesianAxisView.type = 'cartesianAxis';\n      return CartesianAxisView;\n    }(AxisView);\n\n    var axisElementBuilders = {\n      splitLine: function (axisView, axisGroup, axisModel, gridModel) {\n        var axis = axisModel.axis;\n\n        if (axis.scale.isBlank()) {\n          return;\n        }\n\n        var splitLineModel = axisModel.getModel('splitLine');\n        var lineStyleModel = splitLineModel.getModel('lineStyle');\n        var lineColors = lineStyleModel.get('color');\n        lineColors = isArray(lineColors) ? lineColors : [lineColors];\n        var gridRect = gridModel.coordinateSystem.getRect();\n        var isHorizontal = axis.isHorizontal();\n        var lineCount = 0;\n        var ticksCoords = axis.getTicksCoords({\n          tickModel: splitLineModel\n        });\n        var p1 = [];\n        var p2 = [];\n        var lineStyle = lineStyleModel.getLineStyle();\n\n        for (var i = 0; i < ticksCoords.length; i++) {\n          var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);\n\n          if (isHorizontal) {\n            p1[0] = tickCoord;\n            p1[1] = gridRect.y;\n            p2[0] = tickCoord;\n            p2[1] = gridRect.y + gridRect.height;\n          } else {\n            p1[0] = gridRect.x;\n            p1[1] = tickCoord;\n            p2[0] = gridRect.x + gridRect.width;\n            p2[1] = tickCoord;\n          }\n\n          var colorIndex = lineCount++ % lineColors.length;\n          var tickValue = ticksCoords[i].tickValue;\n          var line = new Line({\n            anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null,\n            autoBatch: true,\n            shape: {\n              x1: p1[0],\n              y1: p1[1],\n              x2: p2[0],\n              y2: p2[1]\n            },\n            style: defaults({\n              stroke: lineColors[colorIndex]\n            }, lineStyle),\n            silent: true\n          });\n          subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n          axisGroup.add(line);\n        }\n      },\n      minorSplitLine: function (axisView, axisGroup, axisModel, gridModel) {\n        var axis = axisModel.axis;\n        var minorSplitLineModel = axisModel.getModel('minorSplitLine');\n        var lineStyleModel = minorSplitLineModel.getModel('lineStyle');\n        var gridRect = gridModel.coordinateSystem.getRect();\n        var isHorizontal = axis.isHorizontal();\n        var minorTicksCoords = axis.getMinorTicksCoords();\n\n        if (!minorTicksCoords.length) {\n          return;\n        }\n\n        var p1 = [];\n        var p2 = [];\n        var lineStyle = lineStyleModel.getLineStyle();\n\n        for (var i = 0; i < minorTicksCoords.length; i++) {\n          for (var k = 0; k < minorTicksCoords[i].length; k++) {\n            var tickCoord = axis.toGlobalCoord(minorTicksCoords[i][k].coord);\n\n            if (isHorizontal) {\n              p1[0] = tickCoord;\n              p1[1] = gridRect.y;\n              p2[0] = tickCoord;\n              p2[1] = gridRect.y + gridRect.height;\n            } else {\n              p1[0] = gridRect.x;\n              p1[1] = tickCoord;\n              p2[0] = gridRect.x + gridRect.width;\n              p2[1] = tickCoord;\n            }\n\n            var line = new Line({\n              anid: 'minor_line_' + minorTicksCoords[i][k].tickValue,\n              autoBatch: true,\n              shape: {\n                x1: p1[0],\n                y1: p1[1],\n                x2: p2[0],\n                y2: p2[1]\n              },\n              style: lineStyle,\n              silent: true\n            });\n            subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth);\n            axisGroup.add(line);\n          }\n        }\n      },\n      splitArea: function (axisView, axisGroup, axisModel, gridModel) {\n        rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel);\n      }\n    };\n\n    var CartesianXAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianXAxisView, _super);\n\n      function CartesianXAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianXAxisView.type;\n        return _this;\n      }\n\n      CartesianXAxisView.type = 'xAxis';\n      return CartesianXAxisView;\n    }(CartesianAxisView);\n\n    var CartesianYAxisView =\n    /** @class */\n    function (_super) {\n      __extends(CartesianYAxisView, _super);\n\n      function CartesianYAxisView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = CartesianXAxisView.type;\n        return _this;\n      }\n\n      CartesianYAxisView.type = 'yAxis';\n      return CartesianYAxisView;\n    }(CartesianAxisView);\n\n    var GridView =\n    /** @class */\n    function (_super) {\n      __extends(GridView, _super);\n\n      function GridView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'grid';\n        return _this;\n      }\n\n      GridView.prototype.render = function (gridModel, ecModel) {\n        this.group.removeAll();\n\n        if (gridModel.get('show')) {\n          this.group.add(new Rect({\n            shape: gridModel.coordinateSystem.getRect(),\n            style: defaults({\n              fill: gridModel.get('backgroundColor')\n            }, gridModel.getItemStyle()),\n            silent: true,\n            z2: -1\n          }));\n        }\n      };\n\n      GridView.type = 'grid';\n      return GridView;\n    }(ComponentView);\n\n    var extraOption = {\n      // gridIndex: 0,\n      // gridId: '',\n      offset: 0\n    };\n    function install$4(registers) {\n      registers.registerComponentView(GridView);\n      registers.registerComponentModel(GridModel);\n      registers.registerCoordinateSystem('cartesian2d', Grid);\n      axisModelCreator(registers, 'x', CartesianAxisModel, extraOption);\n      axisModelCreator(registers, 'y', CartesianAxisModel, extraOption);\n      registers.registerComponentView(CartesianXAxisView);\n      registers.registerComponentView(CartesianYAxisView);\n      registers.registerPreprocessor(function (option) {\n        // Only create grid when need\n        if (option.xAxis && option.yAxis && !option.grid) {\n          option.grid = {};\n        }\n      });\n    }\n\n    var DEFAULT_OPTION = {\n      label: {\n        enabled: true\n      },\n      decal: {\n        show: false\n      }\n    };\n    var inner$7 = makeInner();\n    var decalPaletteScope = {};\n    function ariaVisual(ecModel, api) {\n      var ariaModel = ecModel.getModel('aria'); // See \"area enabled\" detection code in `GlobalModel.ts`.\n\n      if (!ariaModel.get('enabled')) {\n        return;\n      }\n\n      var defaultOption = clone(DEFAULT_OPTION);\n      merge(defaultOption.label, ecModel.getLocaleModel().get('aria'), false);\n      merge(ariaModel.option, defaultOption, false);\n      setDecal();\n      setLabel();\n\n      function setDecal() {\n        var decalModel = ariaModel.getModel('decal');\n        var useDecal = decalModel.get('show');\n\n        if (useDecal) {\n          // Each type of series use one scope.\n          // Pie and funnel are using different scopes.\n          var paletteScopeGroupByType_1 = createHashMap();\n          ecModel.eachSeries(function (seriesModel) {\n            if (seriesModel.isColorBySeries()) {\n              return;\n            }\n\n            var decalScope = paletteScopeGroupByType_1.get(seriesModel.type);\n\n            if (!decalScope) {\n              decalScope = {};\n              paletteScopeGroupByType_1.set(seriesModel.type, decalScope);\n            }\n\n            inner$7(seriesModel).scope = decalScope;\n          });\n          ecModel.eachRawSeries(function (seriesModel) {\n            if (ecModel.isSeriesFiltered(seriesModel)) {\n              return;\n            }\n\n            if (isFunction(seriesModel.enableAriaDecal)) {\n              // Let series define how to use decal palette on data\n              seriesModel.enableAriaDecal();\n              return;\n            }\n\n            var data = seriesModel.getData();\n\n            if (!seriesModel.isColorBySeries()) {\n              var dataAll_1 = seriesModel.getRawData();\n              var idxMap_1 = {};\n              var decalScope_1 = inner$7(seriesModel).scope;\n              data.each(function (idx) {\n                var rawIdx = data.getRawIndex(idx);\n                idxMap_1[rawIdx] = idx;\n              });\n              var dataCount_1 = dataAll_1.count();\n              dataAll_1.each(function (rawIdx) {\n                var idx = idxMap_1[rawIdx];\n                var name = dataAll_1.getName(rawIdx) || rawIdx + '';\n                var paletteDecal = getDecalFromPalette(seriesModel.ecModel, name, decalScope_1, dataCount_1);\n                var specifiedDecal = data.getItemVisual(idx, 'decal');\n                data.setItemVisual(idx, 'decal', mergeDecal(specifiedDecal, paletteDecal));\n              });\n            } else {\n              var paletteDecal = getDecalFromPalette(seriesModel.ecModel, seriesModel.name, decalPaletteScope, ecModel.getSeriesCount());\n              var specifiedDecal = data.getVisual('decal');\n              data.setVisual('decal', mergeDecal(specifiedDecal, paletteDecal));\n            }\n\n            function mergeDecal(specifiedDecal, paletteDecal) {\n              // Merge decal from palette to decal from itemStyle.\n              // User do not need to specify all of the decal props.\n              var resultDecal = specifiedDecal ? extend(extend({}, paletteDecal), specifiedDecal) : paletteDecal;\n              resultDecal.dirty = true;\n              return resultDecal;\n            }\n          });\n        }\n      }\n\n      function setLabel() {\n        var labelLocale = ecModel.getLocaleModel().get('aria');\n        var labelModel = ariaModel.getModel('label');\n        labelModel.option = defaults(labelModel.option, labelLocale);\n\n        if (!labelModel.get('enabled')) {\n          return;\n        }\n\n        var dom = api.getZr().dom;\n\n        if (labelModel.get('description')) {\n          dom.setAttribute('aria-label', labelModel.get('description'));\n          return;\n        }\n\n        var seriesCnt = ecModel.getSeriesCount();\n        var maxDataCnt = labelModel.get(['data', 'maxCount']) || 10;\n        var maxSeriesCnt = labelModel.get(['series', 'maxCount']) || 10;\n        var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt);\n        var ariaLabel;\n\n        if (seriesCnt < 1) {\n          // No series, no aria label\n          return;\n        } else {\n          var title = getTitle();\n\n          if (title) {\n            var withTitle = labelModel.get(['general', 'withTitle']);\n            ariaLabel = replace(withTitle, {\n              title: title\n            });\n          } else {\n            ariaLabel = labelModel.get(['general', 'withoutTitle']);\n          }\n\n          var seriesLabels_1 = [];\n          var prefix = seriesCnt > 1 ? labelModel.get(['series', 'multiple', 'prefix']) : labelModel.get(['series', 'single', 'prefix']);\n          ariaLabel += replace(prefix, {\n            seriesCount: seriesCnt\n          });\n          ecModel.eachSeries(function (seriesModel, idx) {\n            if (idx < displaySeriesCnt) {\n              var seriesLabel = void 0;\n              var seriesName = seriesModel.get('name');\n              var withName = seriesName ? 'withName' : 'withoutName';\n              seriesLabel = seriesCnt > 1 ? labelModel.get(['series', 'multiple', withName]) : labelModel.get(['series', 'single', withName]);\n              seriesLabel = replace(seriesLabel, {\n                seriesId: seriesModel.seriesIndex,\n                seriesName: seriesModel.get('name'),\n                seriesType: getSeriesTypeName(seriesModel.subType)\n              });\n              var data = seriesModel.getData();\n\n              if (data.count() > maxDataCnt) {\n                // Show part of data\n                var partialLabel = labelModel.get(['data', 'partialData']);\n                seriesLabel += replace(partialLabel, {\n                  displayCnt: maxDataCnt\n                });\n              } else {\n                seriesLabel += labelModel.get(['data', 'allData']);\n              }\n\n              var middleSeparator_1 = labelModel.get(['data', 'separator', 'middle']);\n              var endSeparator_1 = labelModel.get(['data', 'separator', 'end']);\n              var dataLabels = [];\n\n              for (var i = 0; i < data.count(); i++) {\n                if (i < maxDataCnt) {\n                  var name_1 = data.getName(i);\n                  var value = data.getValues(i);\n                  var dataLabel = labelModel.get(['data', name_1 ? 'withName' : 'withoutName']);\n                  dataLabels.push(replace(dataLabel, {\n                    name: name_1,\n                    value: value.join(middleSeparator_1)\n                  }));\n                }\n              }\n\n              seriesLabel += dataLabels.join(middleSeparator_1) + endSeparator_1;\n              seriesLabels_1.push(seriesLabel);\n            }\n          });\n          var separatorModel = labelModel.getModel(['series', 'multiple', 'separator']);\n          var middleSeparator = separatorModel.get('middle');\n          var endSeparator = separatorModel.get('end');\n          ariaLabel += seriesLabels_1.join(middleSeparator) + endSeparator;\n          dom.setAttribute('aria-label', ariaLabel);\n        }\n      }\n\n      function replace(str, keyValues) {\n        if (!isString(str)) {\n          return str;\n        }\n\n        var result = str;\n        each(keyValues, function (value, key) {\n          result = result.replace(new RegExp('\\\\{\\\\s*' + key + '\\\\s*\\\\}', 'g'), value);\n        });\n        return result;\n      }\n\n      function getTitle() {\n        var title = ecModel.get('title');\n\n        if (title && title.length) {\n          title = title[0];\n        }\n\n        return title && title.text;\n      }\n\n      function getSeriesTypeName(type) {\n        return ecModel.getLocaleModel().get(['series', 'typeNames'])[type] || '自定义图';\n      }\n    }\n\n    function ariaPreprocessor(option) {\n      if (!option || !option.aria) {\n        return;\n      }\n\n      var aria = option.aria; // aria.show is deprecated and should use aria.enabled instead\n\n      if (aria.show != null) {\n        aria.enabled = aria.show;\n      }\n\n      aria.label = aria.label || {}; // move description, general, series, data to be under aria.label\n\n      each(['description', 'general', 'series', 'data'], function (name) {\n        if (aria[name] != null) {\n          aria.label[name] = aria[name];\n        }\n      });\n    }\n\n    function install$5(registers) {\n      registers.registerPreprocessor(ariaPreprocessor);\n      registers.registerVisual(registers.PRIORITY.VISUAL.ARIA, ariaVisual);\n    }\n\n    var DatasetModel =\n    /** @class */\n    function (_super) {\n      __extends(DatasetModel, _super);\n\n      function DatasetModel() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataset';\n        return _this;\n      }\n\n      DatasetModel.prototype.init = function (option, parentModel, ecModel) {\n        _super.prototype.init.call(this, option, parentModel, ecModel);\n\n        this._sourceManager = new SourceManager(this);\n        disableTransformOptionMerge(this);\n      };\n\n      DatasetModel.prototype.mergeOption = function (newOption, ecModel) {\n        _super.prototype.mergeOption.call(this, newOption, ecModel);\n\n        disableTransformOptionMerge(this);\n      };\n\n      DatasetModel.prototype.optionUpdated = function () {\n        this._sourceManager.dirty();\n      };\n\n      DatasetModel.prototype.getSourceManager = function () {\n        return this._sourceManager;\n      };\n\n      DatasetModel.type = 'dataset';\n      DatasetModel.defaultOption = {\n        seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN\n      };\n      return DatasetModel;\n    }(ComponentModel);\n\n    var DatasetView =\n    /** @class */\n    function (_super) {\n      __extends(DatasetView, _super);\n\n      function DatasetView() {\n        var _this = _super !== null && _super.apply(this, arguments) || this;\n\n        _this.type = 'dataset';\n        return _this;\n      }\n\n      DatasetView.type = 'dataset';\n      return DatasetView;\n    }(ComponentView);\n\n    function install$6(registers) {\n      registers.registerComponentModel(DatasetModel);\n      registers.registerComponentView(DatasetView);\n    }\n\n    use([install]);\n    use([install$1, install$2, install$3]);\n    use([install$4, install$5, install$6]);\n\n    exports.Axis = Axis;\n    exports.ChartView = ChartView;\n    exports.ComponentModel = ComponentModel;\n    exports.ComponentView = ComponentView;\n    exports.List = SeriesData;\n    exports.Model = Model;\n    exports.PRIORITY = PRIORITY;\n    exports.SeriesModel = SeriesModel;\n    exports.color = color;\n    exports.connect = connect;\n    exports.dataTool = dataTool;\n    exports.dependencies = dependencies;\n    exports.disConnect = disConnect;\n    exports.disconnect = disconnect;\n    exports.dispose = dispose$1;\n    exports.env = env;\n    exports.extendChartView = extendChartView;\n    exports.extendComponentModel = extendComponentModel;\n    exports.extendComponentView = extendComponentView;\n    exports.extendSeriesModel = extendSeriesModel;\n    exports.format = format$1;\n    exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;\n    exports.getInstanceByDom = getInstanceByDom;\n    exports.getInstanceById = getInstanceById;\n    exports.getMap = getMap;\n    exports.graphic = graphic;\n    exports.helper = helper;\n    exports.init = init$1;\n    exports.innerDrawElementOnCanvas = brushSingle;\n    exports.matrix = matrix;\n    exports.number = number;\n    exports.parseGeoJSON = parseGeoJSON;\n    exports.parseGeoJson = parseGeoJSON;\n    exports.registerAction = registerAction;\n    exports.registerCoordinateSystem = registerCoordinateSystem;\n    exports.registerLayout = registerLayout;\n    exports.registerLoading = registerLoading;\n    exports.registerLocale = registerLocale;\n    exports.registerMap = registerMap;\n    exports.registerPostInit = registerPostInit;\n    exports.registerPostUpdate = registerPostUpdate;\n    exports.registerPreprocessor = registerPreprocessor;\n    exports.registerProcessor = registerProcessor;\n    exports.registerTheme = registerTheme;\n    exports.registerTransform = registerTransform;\n    exports.registerUpdateLifecycle = registerUpdateLifecycle;\n    exports.registerVisual = registerVisual;\n    exports.setCanvasCreator = setCanvasCreator;\n    exports.setPlatformAPI = setPlatformAPI;\n    exports.throttle = throttle;\n    exports.time = time;\n    exports.use = use;\n    exports.util = util$1;\n    exports.vector = vector;\n    exports.version = version$1;\n    exports.zrUtil = util;\n    exports.zrender = zrender;\n\n    Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=echarts.simple.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/echarts/extension/bmap.js",
    "content": "\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('echarts')) :\n  typeof define === 'function' && define.amd ? define(['exports', 'echarts'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bmap = {}, global.echarts));\n}(this, (function (exports, echarts) { 'use strict';\n\n  function BMapCoordSys(bmap, api) {\n    this._bmap = bmap;\n    this.dimensions = ['lng', 'lat'];\n    this._mapOffset = [0, 0];\n    this._api = api;\n    this._projection = new BMap.MercatorProjection();\n  }\n\n  BMapCoordSys.prototype.type = 'bmap';\n  BMapCoordSys.prototype.dimensions = ['lng', 'lat'];\n\n  BMapCoordSys.prototype.setZoom = function (zoom) {\n    this._zoom = zoom;\n  };\n\n  BMapCoordSys.prototype.setCenter = function (center) {\n    this._center = this._projection.lngLatToPoint(new BMap.Point(center[0], center[1]));\n  };\n\n  BMapCoordSys.prototype.setMapOffset = function (mapOffset) {\n    this._mapOffset = mapOffset;\n  };\n\n  BMapCoordSys.prototype.getBMap = function () {\n    return this._bmap;\n  };\n\n  BMapCoordSys.prototype.dataToPoint = function (data) {\n    var point = new BMap.Point(data[0], data[1]); // TODO mercator projection is toooooooo slow\n    // let mercatorPoint = this._projection.lngLatToPoint(point);\n    // let width = this._api.getZr().getWidth();\n    // let height = this._api.getZr().getHeight();\n    // let divider = Math.pow(2, 18 - 10);\n    // return [\n    //     Math.round((mercatorPoint.x - this._center.x) / divider + width / 2),\n    //     Math.round((this._center.y - mercatorPoint.y) / divider + height / 2)\n    // ];\n\n    var px = this._bmap.pointToOverlayPixel(point);\n\n    var mapOffset = this._mapOffset;\n    return [px.x - mapOffset[0], px.y - mapOffset[1]];\n  };\n\n  BMapCoordSys.prototype.pointToData = function (pt) {\n    var mapOffset = this._mapOffset;\n    pt = this._bmap.overlayPixelToPoint({\n      x: pt[0] + mapOffset[0],\n      y: pt[1] + mapOffset[1]\n    });\n    return [pt.lng, pt.lat];\n  };\n\n  BMapCoordSys.prototype.getViewRect = function () {\n    var api = this._api;\n    return new echarts.graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight());\n  };\n\n  BMapCoordSys.prototype.getRoamTransform = function () {\n    return echarts.matrix.create();\n  };\n\n  BMapCoordSys.prototype.prepareCustoms = function () {\n    var rect = this.getViewRect();\n    return {\n      coordSys: {\n        // The name exposed to user is always 'cartesian2d' but not 'grid'.\n        type: 'bmap',\n        x: rect.x,\n        y: rect.y,\n        width: rect.width,\n        height: rect.height\n      },\n      api: {\n        coord: echarts.util.bind(this.dataToPoint, this),\n        size: echarts.util.bind(dataToCoordSize, this)\n      }\n    };\n  };\n\n  BMapCoordSys.prototype.convertToPixel = function (ecModel, finder, value) {\n    // here we ignore finder as only one bmap component is allowed\n    return this.dataToPoint(value);\n  };\n\n  BMapCoordSys.prototype.convertFromPixel = function (ecModel, finder, value) {\n    return this.pointToData(value);\n  };\n\n  function dataToCoordSize(dataSize, dataItem) {\n    dataItem = dataItem || [0, 0];\n    return echarts.util.map([0, 1], function (dimIdx) {\n      var val = dataItem[dimIdx];\n      var halfSize = dataSize[dimIdx] / 2;\n      var p1 = [];\n      var p2 = [];\n      p1[dimIdx] = val - halfSize;\n      p2[dimIdx] = val + halfSize;\n      p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx];\n      return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]);\n    }, this);\n  }\n\n  var Overlay; // For deciding which dimensions to use when creating list data\n\n  BMapCoordSys.dimensions = BMapCoordSys.prototype.dimensions;\n\n  function createOverlayCtor() {\n    function Overlay(root) {\n      this._root = root;\n    }\n\n    Overlay.prototype = new BMap.Overlay();\n    /**\n     * 初始化\n     *\n     * @param {BMap.Map} map\n     * @override\n     */\n\n    Overlay.prototype.initialize = function (map) {\n      map.getPanes().labelPane.appendChild(this._root);\n      return this._root;\n    };\n    /**\n     * @override\n     */\n\n\n    Overlay.prototype.draw = function () {};\n\n    return Overlay;\n  }\n\n  BMapCoordSys.create = function (ecModel, api) {\n    var bmapCoordSys;\n    var root = api.getDom(); // TODO Dispose\n\n    ecModel.eachComponent('bmap', function (bmapModel) {\n      var painter = api.getZr().painter;\n      var viewportRoot = painter.getViewportRoot();\n\n      if (typeof BMap === 'undefined') {\n        throw new Error('BMap api is not loaded');\n      }\n\n      Overlay = Overlay || createOverlayCtor();\n\n      if (bmapCoordSys) {\n        throw new Error('Only one bmap component can exist');\n      }\n\n      var bmap;\n\n      if (!bmapModel.__bmap) {\n        // Not support IE8\n        var bmapRoot = root.querySelector('.ec-extension-bmap');\n\n        if (bmapRoot) {\n          // Reset viewport left and top, which will be changed\n          // in moving handler in BMapView\n          viewportRoot.style.left = '0px';\n          viewportRoot.style.top = '0px';\n          root.removeChild(bmapRoot);\n        }\n\n        bmapRoot = document.createElement('div');\n        bmapRoot.className = 'ec-extension-bmap'; // fix #13424\n\n        bmapRoot.style.cssText = 'position:absolute;width:100%;height:100%';\n        root.appendChild(bmapRoot); // initializes bmap\n\n        var mapOptions = bmapModel.get('mapOptions');\n\n        if (mapOptions) {\n          mapOptions = echarts.util.clone(mapOptions); // Not support `mapType`, use `bmap.setMapType(MapType)` instead.\n\n          delete mapOptions.mapType;\n        }\n\n        bmap = bmapModel.__bmap = new BMap.Map(bmapRoot, mapOptions);\n        var overlay = new Overlay(viewportRoot);\n        bmap.addOverlay(overlay); // Override\n\n        painter.getViewportRootOffset = function () {\n          return {\n            offsetLeft: 0,\n            offsetTop: 0\n          };\n        };\n      }\n\n      bmap = bmapModel.__bmap; // Set bmap options\n      // centerAndZoom before layout and render\n\n      var center = bmapModel.get('center');\n      var zoom = bmapModel.get('zoom');\n\n      if (center && zoom) {\n        var bmapCenter = bmap.getCenter();\n        var bmapZoom = bmap.getZoom();\n        var centerOrZoomChanged = bmapModel.centerOrZoomChanged([bmapCenter.lng, bmapCenter.lat], bmapZoom);\n\n        if (centerOrZoomChanged) {\n          var pt = new BMap.Point(center[0], center[1]);\n          bmap.centerAndZoom(pt, zoom);\n        }\n      }\n\n      bmapCoordSys = new BMapCoordSys(bmap, api);\n      bmapCoordSys.setMapOffset(bmapModel.__mapOffset || [0, 0]);\n      bmapCoordSys.setZoom(zoom);\n      bmapCoordSys.setCenter(center);\n      bmapModel.coordinateSystem = bmapCoordSys;\n    });\n    ecModel.eachSeries(function (seriesModel) {\n      if (seriesModel.get('coordinateSystem') === 'bmap') {\n        seriesModel.coordinateSystem = bmapCoordSys;\n      }\n    }); // return created coordinate systems\n\n    return bmapCoordSys && [bmapCoordSys];\n  };\n\n  function v2Equal(a, b) {\n    return a && b && a[0] === b[0] && a[1] === b[1];\n  }\n\n  echarts.extendComponentModel({\n    type: 'bmap',\n    getBMap: function () {\n      // __bmap is injected when creating BMapCoordSys\n      return this.__bmap;\n    },\n    setCenterAndZoom: function (center, zoom) {\n      this.option.center = center;\n      this.option.zoom = zoom;\n    },\n    centerOrZoomChanged: function (center, zoom) {\n      var option = this.option;\n      return !(v2Equal(center, option.center) && zoom === option.zoom);\n    },\n    defaultOption: {\n      center: [104.114129, 37.550339],\n      zoom: 5,\n      // 2.0 https://lbsyun.baidu.com/custom/index.htm\n      mapStyle: {},\n      // 3.0 https://lbsyun.baidu.com/index.php?title=open/custom\n      mapStyleV2: {},\n      // See https://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference.html#a0b1\n      mapOptions: {},\n      roam: false\n    }\n  });\n\n  function isEmptyObject(obj) {\n    for (var key in obj) {\n      if (obj.hasOwnProperty(key)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  echarts.extendComponentView({\n    type: 'bmap',\n    render: function (bMapModel, ecModel, api) {\n      var rendering = true;\n      var bmap = bMapModel.getBMap();\n      var viewportRoot = api.getZr().painter.getViewportRoot();\n      var coordSys = bMapModel.coordinateSystem;\n\n      var moveHandler = function (type, target) {\n        if (rendering) {\n          return;\n        }\n\n        var offsetEl = viewportRoot.parentNode.parentNode.parentNode;\n        var mapOffset = [-parseInt(offsetEl.style.left, 10) || 0, -parseInt(offsetEl.style.top, 10) || 0]; // only update style when map offset changed\n\n        var viewportRootStyle = viewportRoot.style;\n        var offsetLeft = mapOffset[0] + 'px';\n        var offsetTop = mapOffset[1] + 'px';\n\n        if (viewportRootStyle.left !== offsetLeft) {\n          viewportRootStyle.left = offsetLeft;\n        }\n\n        if (viewportRootStyle.top !== offsetTop) {\n          viewportRootStyle.top = offsetTop;\n        }\n\n        coordSys.setMapOffset(mapOffset);\n        bMapModel.__mapOffset = mapOffset;\n        api.dispatchAction({\n          type: 'bmapRoam',\n          animation: {\n            duration: 0\n          }\n        });\n      };\n\n      function zoomEndHandler() {\n        if (rendering) {\n          return;\n        }\n\n        api.dispatchAction({\n          type: 'bmapRoam',\n          animation: {\n            duration: 0\n          }\n        });\n      }\n\n      bmap.removeEventListener('moving', this._oldMoveHandler);\n      bmap.removeEventListener('moveend', this._oldMoveHandler);\n      bmap.removeEventListener('zoomend', this._oldZoomEndHandler);\n      bmap.addEventListener('moving', moveHandler);\n      bmap.addEventListener('moveend', moveHandler);\n      bmap.addEventListener('zoomend', zoomEndHandler);\n      this._oldMoveHandler = moveHandler;\n      this._oldZoomEndHandler = zoomEndHandler;\n      var roam = bMapModel.get('roam');\n\n      if (roam && roam !== 'scale') {\n        bmap.enableDragging();\n      } else {\n        bmap.disableDragging();\n      }\n\n      if (roam && roam !== 'move') {\n        bmap.enableScrollWheelZoom();\n        bmap.enableDoubleClickZoom();\n        bmap.enablePinchToZoom();\n      } else {\n        bmap.disableScrollWheelZoom();\n        bmap.disableDoubleClickZoom();\n        bmap.disablePinchToZoom();\n      }\n      /* map 2.0 */\n\n\n      var originalStyle = bMapModel.__mapStyle;\n      var newMapStyle = bMapModel.get('mapStyle') || {}; // FIXME, Not use JSON methods\n\n      var mapStyleStr = JSON.stringify(newMapStyle);\n\n      if (JSON.stringify(originalStyle) !== mapStyleStr) {\n        // FIXME May have blank tile when dragging if setMapStyle\n        if (!isEmptyObject(newMapStyle)) {\n          bmap.setMapStyle(echarts.util.clone(newMapStyle));\n        }\n\n        bMapModel.__mapStyle = JSON.parse(mapStyleStr);\n      }\n      /* map 3.0 */\n\n\n      var originalStyle2 = bMapModel.__mapStyle2;\n      var newMapStyle2 = bMapModel.get('mapStyleV2') || {}; // FIXME, Not use JSON methods\n\n      var mapStyleStr2 = JSON.stringify(newMapStyle2);\n\n      if (JSON.stringify(originalStyle2) !== mapStyleStr2) {\n        // FIXME May have blank tile when dragging if setMapStyle\n        if (!isEmptyObject(newMapStyle2)) {\n          bmap.setMapStyleV2(echarts.util.clone(newMapStyle2));\n        }\n\n        bMapModel.__mapStyle2 = JSON.parse(mapStyleStr2);\n      }\n\n      rendering = false;\n    }\n  });\n\n  echarts.registerCoordinateSystem('bmap', BMapCoordSys); // Action\n\n  echarts.registerAction({\n    type: 'bmapRoam',\n    event: 'bmapRoam',\n    update: 'updateLayout'\n  }, function (payload, ecModel) {\n    ecModel.eachComponent('bmap', function (bMapModel) {\n      var bmap = bMapModel.getBMap();\n      var center = bmap.getCenter();\n      bMapModel.setCenterAndZoom([center.lng, center.lat], bmap.getZoom());\n    });\n  });\n  var version = '1.0.0';\n\n  exports.version = version;\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=bmap.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/echarts/extension/dataTool.js",
    "content": "\n/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied.  See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('echarts')) :\n    typeof define === 'function' && define.amd ? define(['exports', 'echarts'], factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.dataTool = {}, global.echarts));\n}(this, (function (exports, echarts) { 'use strict';\n\n    var BUILTIN_OBJECT = reduce([\n        'Function',\n        'RegExp',\n        'Date',\n        'Error',\n        'CanvasGradient',\n        'CanvasPattern',\n        'Image',\n        'Canvas'\n    ], function (obj, val) {\n        obj['[object ' + val + ']'] = true;\n        return obj;\n    }, {});\n    var TYPED_ARRAY = reduce([\n        'Int8',\n        'Uint8',\n        'Uint8Clamped',\n        'Int16',\n        'Uint16',\n        'Int32',\n        'Uint32',\n        'Float32',\n        'Float64'\n    ], function (obj, val) {\n        obj['[object ' + val + 'Array]'] = true;\n        return obj;\n    }, {});\n    var arrayProto = Array.prototype;\n    var nativeSlice = arrayProto.slice;\n    var nativeMap = arrayProto.map;\n    var ctorFunction = function () { }.constructor;\n    var protoFunction = ctorFunction ? ctorFunction.prototype : null;\n    function map(arr, cb, context) {\n        if (!arr) {\n            return [];\n        }\n        if (!cb) {\n            return slice(arr);\n        }\n        if (arr.map && arr.map === nativeMap) {\n            return arr.map(cb, context);\n        }\n        else {\n            var result = [];\n            for (var i = 0, len = arr.length; i < len; i++) {\n                result.push(cb.call(context, arr[i], i, arr));\n            }\n            return result;\n        }\n    }\n    function reduce(arr, cb, memo, context) {\n        if (!(arr && cb)) {\n            return;\n        }\n        for (var i = 0, len = arr.length; i < len; i++) {\n            memo = cb.call(context, memo, arr[i], i, arr);\n        }\n        return memo;\n    }\n    function bindPolyfill(func, context) {\n        var args = [];\n        for (var _i = 2; _i < arguments.length; _i++) {\n            args[_i - 2] = arguments[_i];\n        }\n        return function () {\n            return func.apply(context, args.concat(nativeSlice.call(arguments)));\n        };\n    }\n    var bind = (protoFunction && isFunction(protoFunction.bind))\n        ? protoFunction.call.bind(protoFunction.bind)\n        : bindPolyfill;\n    function isFunction(value) {\n        return typeof value === 'function';\n    }\n    function slice(arr) {\n        var args = [];\n        for (var _i = 1; _i < arguments.length; _i++) {\n            args[_i - 1] = arguments[_i];\n        }\n        return nativeSlice.apply(arr, args);\n    }\n\n    function parse(xml) {\n      var doc;\n\n      if (typeof xml === 'string') {\n        var parser = new DOMParser();\n        doc = parser.parseFromString(xml, 'text/xml');\n      } else {\n        doc = xml;\n      }\n\n      if (!doc || doc.getElementsByTagName('parsererror').length) {\n        return null;\n      }\n\n      var gexfRoot = getChildByTagName(doc, 'gexf');\n\n      if (!gexfRoot) {\n        return null;\n      }\n\n      var graphRoot = getChildByTagName(gexfRoot, 'graph');\n      var attributes = parseAttributes(getChildByTagName(graphRoot, 'attributes'));\n      var attributesMap = {};\n\n      for (var i = 0; i < attributes.length; i++) {\n        attributesMap[attributes[i].id] = attributes[i];\n      }\n\n      return {\n        nodes: parseNodes(getChildByTagName(graphRoot, 'nodes'), attributesMap),\n        links: parseEdges(getChildByTagName(graphRoot, 'edges'))\n      };\n    }\n\n    function parseAttributes(parent) {\n      return parent ? map(getChildrenByTagName(parent, 'attribute'), function (attribDom) {\n        return {\n          id: getAttr(attribDom, 'id'),\n          title: getAttr(attribDom, 'title'),\n          type: getAttr(attribDom, 'type')\n        };\n      }) : [];\n    }\n\n    function parseNodes(parent, attributesMap) {\n      return parent ? map(getChildrenByTagName(parent, 'node'), function (nodeDom) {\n        var id = getAttr(nodeDom, 'id');\n        var label = getAttr(nodeDom, 'label');\n        var node = {\n          id: id,\n          name: label,\n          itemStyle: {\n            normal: {}\n          }\n        };\n        var vizSizeDom = getChildByTagName(nodeDom, 'viz:size');\n        var vizPosDom = getChildByTagName(nodeDom, 'viz:position');\n        var vizColorDom = getChildByTagName(nodeDom, 'viz:color'); // let vizShapeDom = getChildByTagName(nodeDom, 'viz:shape');\n\n        var attvaluesDom = getChildByTagName(nodeDom, 'attvalues');\n\n        if (vizSizeDom) {\n          node.symbolSize = parseFloat(getAttr(vizSizeDom, 'value'));\n        }\n\n        if (vizPosDom) {\n          node.x = parseFloat(getAttr(vizPosDom, 'x'));\n          node.y = parseFloat(getAttr(vizPosDom, 'y')); // z\n        }\n\n        if (vizColorDom) {\n          node.itemStyle.normal.color = 'rgb(' + [getAttr(vizColorDom, 'r') | 0, getAttr(vizColorDom, 'g') | 0, getAttr(vizColorDom, 'b') | 0].join(',') + ')';\n        } // if (vizShapeDom) {\n        // node.shape = getAttr(vizShapeDom, 'shape');\n        // }\n\n\n        if (attvaluesDom) {\n          var attvalueDomList = getChildrenByTagName(attvaluesDom, 'attvalue');\n          node.attributes = {};\n\n          for (var j = 0; j < attvalueDomList.length; j++) {\n            var attvalueDom = attvalueDomList[j];\n            var attId = getAttr(attvalueDom, 'for');\n            var attValue = getAttr(attvalueDom, 'value');\n            var attribute = attributesMap[attId];\n\n            if (attribute) {\n              switch (attribute.type) {\n                case 'integer':\n                case 'long':\n                  attValue = parseInt(attValue, 10);\n                  break;\n\n                case 'float':\n                case 'double':\n                  attValue = parseFloat(attValue);\n                  break;\n\n                case 'boolean':\n                  attValue = attValue.toLowerCase() === 'true';\n                  break;\n              }\n\n              node.attributes[attId] = attValue;\n            }\n          }\n        }\n\n        return node;\n      }) : [];\n    }\n\n    function parseEdges(parent) {\n      return parent ? map(getChildrenByTagName(parent, 'edge'), function (edgeDom) {\n        var id = getAttr(edgeDom, 'id');\n        var label = getAttr(edgeDom, 'label');\n        var sourceId = getAttr(edgeDom, 'source');\n        var targetId = getAttr(edgeDom, 'target');\n        var edge = {\n          id: id,\n          name: label,\n          source: sourceId,\n          target: targetId,\n          lineStyle: {\n            normal: {}\n          }\n        };\n        var lineStyle = edge.lineStyle.normal;\n        var vizThicknessDom = getChildByTagName(edgeDom, 'viz:thickness');\n        var vizColorDom = getChildByTagName(edgeDom, 'viz:color'); // let vizShapeDom = getChildByTagName(edgeDom, 'viz:shape');\n\n        if (vizThicknessDom) {\n          lineStyle.width = parseFloat(vizThicknessDom.getAttribute('value'));\n        }\n\n        if (vizColorDom) {\n          lineStyle.color = 'rgb(' + [getAttr(vizColorDom, 'r') | 0, getAttr(vizColorDom, 'g') | 0, getAttr(vizColorDom, 'b') | 0].join(',') + ')';\n        } // if (vizShapeDom) {\n        //     edge.shape = vizShapeDom.getAttribute('shape');\n        // }\n\n\n        return edge;\n      }) : [];\n    }\n\n    function getAttr(el, attrName) {\n      return el.getAttribute(attrName);\n    }\n\n    function getChildByTagName(parent, tagName) {\n      var node = parent.firstChild;\n\n      while (node) {\n        if (node.nodeType !== 1 || node.nodeName.toLowerCase() !== tagName.toLowerCase()) {\n          node = node.nextSibling;\n        } else {\n          return node;\n        }\n      }\n\n      return null;\n    }\n\n    function getChildrenByTagName(parent, tagName) {\n      var node = parent.firstChild;\n      var children = [];\n\n      while (node) {\n        if (node.nodeName.toLowerCase() === tagName.toLowerCase()) {\n          children.push(node);\n        }\n\n        node = node.nextSibling;\n      }\n\n      return children;\n    }\n\n    var gexf = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        parse: parse\n    });\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n\n\n    /**\n     * AUTO-GENERATED FILE. DO NOT MODIFY.\n     */\n\n    /*\n    * Licensed to the Apache Software Foundation (ASF) under one\n    * or more contributor license agreements.  See the NOTICE file\n    * distributed with this work for additional information\n    * regarding copyright ownership.  The ASF licenses this file\n    * to you under the Apache License, Version 2.0 (the\n    * \"License\"); you may not use this file except in compliance\n    * with the License.  You may obtain a copy of the License at\n    *\n    *   http://www.apache.org/licenses/LICENSE-2.0\n    *\n    * Unless required by applicable law or agreed to in writing,\n    * software distributed under the License is distributed on an\n    * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    * KIND, either express or implied.  See the License for the\n    * specific language governing permissions and limitations\n    * under the License.\n    */\n    function asc(arr) {\n      arr.sort(function (a, b) {\n        return a - b;\n      });\n      return arr;\n    }\n\n    function quantile(ascArr, p) {\n      var H = (ascArr.length - 1) * p + 1;\n      var h = Math.floor(H);\n      var v = +ascArr[h - 1];\n      var e = H - h;\n      return e ? v + e * (ascArr[h] - v) : v;\n    }\n    /**\n     * See:\n     *  <https://en.wikipedia.org/wiki/Box_plot#cite_note-frigge_hoaglin_iglewicz-2>\n     *  <http://stat.ethz.ch/R-manual/R-devel/library/grDevices/html/boxplot.stats.html>\n     *\n     * Helper method for preparing data.\n     *\n     * @param {Array.<number>} rawData like\n     *        [\n     *            [12,232,443], (raw data set for the first box)\n     *            [3843,5545,1232], (raw data set for the second box)\n     *            ...\n     *        ]\n     * @param {Object} [opt]\n     *\n     * @param {(number|string)} [opt.boundIQR=1.5] Data less than min bound is outlier.\n     *      default 1.5, means Q1 - 1.5 * (Q3 - Q1).\n     *      If 'none'/0 passed, min bound will not be used.\n     * @param {(number|string)} [opt.layout='horizontal']\n     *      Box plot layout, can be 'horizontal' or 'vertical'\n     * @return {Object} {\n     *      boxData: Array.<Array.<number>>\n     *      outliers: Array.<Array.<number>>\n     *      axisData: Array.<string>\n     * }\n     */\n\n\n    function prepareBoxplotData (rawData, opt) {\n      opt = opt || {};\n      var boxData = [];\n      var outliers = [];\n      var axisData = [];\n      var boundIQR = opt.boundIQR;\n      var useExtreme = boundIQR === 'none' || boundIQR === 0;\n\n      for (var i = 0; i < rawData.length; i++) {\n        axisData.push(i + '');\n        var ascList = asc(rawData[i].slice());\n        var Q1 = quantile(ascList, 0.25);\n        var Q2 = quantile(ascList, 0.5);\n        var Q3 = quantile(ascList, 0.75);\n        var min = ascList[0];\n        var max = ascList[ascList.length - 1];\n        var bound = (boundIQR == null ? 1.5 : boundIQR) * (Q3 - Q1);\n        var low = useExtreme ? min : Math.max(min, Q1 - bound);\n        var high = useExtreme ? max : Math.min(max, Q3 + bound);\n        boxData.push([low, Q1, Q2, Q3, high]);\n\n        for (var j = 0; j < ascList.length; j++) {\n          var dataItem = ascList[j];\n\n          if (dataItem < low || dataItem > high) {\n            var outlier = [i, dataItem];\n            opt.layout === 'vertical' && outlier.reverse();\n            outliers.push(outlier);\n          }\n        }\n      }\n\n      return {\n        boxData: boxData,\n        outliers: outliers,\n        axisData: axisData\n      };\n    }\n\n    var version = '1.0.0';\n    // For backward compatibility, where the namespace `dataTool` will\n    // be mounted on `echarts` is the extension `dataTool` is imported.\n    // But the old version of echarts do not have `dataTool` namespace,\n    // so check it before mounting.\n\n    if (echarts.dataTool) {\n      echarts.dataTool.version = version;\n      echarts.dataTool.gexf = gexf;\n      echarts.dataTool.prepareBoxplotData = prepareBoxplotData; // echarts.dataTool.boxplotTransform = boxplotTransform;\n    }\n\n    exports.gexf = gexf;\n    exports.prepareBoxplotData = prepareBoxplotData;\n    exports.version = version;\n\n    Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=dataTool.js.map\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/fonts/fonts-googleapis-source-sans-pro.css",
    "content": "/* cyrillic-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: italic;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7qsDJT9g.woff2) format('woff2');\n  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: italic;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7jsDJT9g.woff2) format('woff2');\n  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: italic;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7rsDJT9g.woff2) format('woff2');\n  unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: italic;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7ksDJT9g.woff2) format('woff2');\n  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: italic;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7osDJT9g.woff2) format('woff2');\n  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: italic;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7psDJT9g.woff2) format('woff2');\n  unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: italic;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDI.woff2) format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 300;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2) format('woff2');\n  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 300;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2) format('woff2');\n  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 300;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2) format('woff2');\n  unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 300;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2) format('woff2');\n  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 300;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2) format('woff2');\n  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 300;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2) format('woff2');\n  unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 300;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2) format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.woff2) format('woff2');\n  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2) format('woff2');\n  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2) format('woff2');\n  unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2) format('woff2');\n  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2) format('woff2');\n  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.woff2) format('woff2');\n  unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 400;\n  font-display: fallback;\n  src: url(source-sans-pro/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2) format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 700;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2) format('woff2');\n  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 700;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2) format('woff2');\n  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 700;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2) format('woff2');\n  unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 700;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2) format('woff2');\n  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 700;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2) format('woff2');\n  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 700;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2) format('woff2');\n  unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n  font-family: 'Source Sans Pro';\n  font-style: normal;\n  font-weight: 700;\n  font-display: fallback;\n  src: url(source-sans-pro/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/highchart/highcharts.js",
    "content": "/*\n Highcharts JS v11.1.0 (2023-06-05)\n\n (c) 2009-2021 Torstein Honsi\n\n License: www.highcharts.com/license\n*/\n'use strict';(function(U,M){\"object\"===typeof module&&module.exports?(M[\"default\"]=M,module.exports=U.document?M(U):M):\"function\"===typeof define&&define.amd?define(\"highcharts/highcharts\",function(){return M(U)}):(U.Highcharts&&U.Highcharts.error(16,!0),U.Highcharts=M(U))})(\"undefined\"!==typeof window?window:this,function(U){function M(a,y,I,L){a.hasOwnProperty(y)||(a[y]=L.apply(null,I),\"function\"===typeof CustomEvent&&U.dispatchEvent(new CustomEvent(\"HighchartsModuleLoaded\",{detail:{path:y,module:a[y]}})))}\nvar a={};M(a,\"Core/Globals.js\",[],function(){var a;(function(a){a.SVG_NS=\"http://www.w3.org/2000/svg\";a.product=\"Highcharts\";a.version=\"11.1.0\";a.win=\"undefined\"!==typeof U?U:{};a.doc=a.win.document;a.svg=a.doc&&a.doc.createElementNS&&!!a.doc.createElementNS(a.SVG_NS,\"svg\").createSVGRect;a.userAgent=a.win.navigator&&a.win.navigator.userAgent||\"\";a.isChrome=-1!==a.userAgent.indexOf(\"Chrome\");a.isFirefox=-1!==a.userAgent.indexOf(\"Firefox\");a.isMS=/(edge|msie|trident)/i.test(a.userAgent)&&!a.win.opera;\na.isSafari=!a.isChrome&&-1!==a.userAgent.indexOf(\"Safari\");a.isTouchDevice=/(Mobile|Android|Windows Phone)/.test(a.userAgent);a.isWebKit=-1!==a.userAgent.indexOf(\"AppleWebKit\");a.deg2rad=2*Math.PI/360;a.hasBidiBug=a.isFirefox&&4>parseInt(a.userAgent.split(\"Firefox/\")[1],10);a.hasTouch=!!a.win.TouchEvent;a.marginNames=[\"plotTop\",\"marginRight\",\"marginBottom\",\"plotLeft\"];a.noop=function(){};a.supportsPassiveEvents=function(){let x=!1;if(!a.isMS){const y=Object.defineProperty({},\"passive\",{get:function(){x=\n!0}});a.win.addEventListener&&a.win.removeEventListener&&(a.win.addEventListener(\"testPassive\",a.noop,y),a.win.removeEventListener(\"testPassive\",a.noop,y))}return x}();a.charts=[];a.dateFormats={};a.seriesTypes={};a.symbolSizes={};a.chartCount=0})(a||(a={}));\"\";return a});M(a,\"Core/Utilities.js\",[a[\"Core/Globals.js\"]],function(a){function x(c,b,f,k){const n=b?\"Highcharts error\":\"Highcharts warning\";32===c&&(c=`${n}: Deprecated member`);const r=u(c);let e=r?`${n} #${c}: www.highcharts.com/errors/${c}/`:\nc.toString();if(\"undefined\"!==typeof k){let c=\"\";r&&(e+=\"?\");E(k,function(b,n){c+=`\\n - ${n}: ${b}`;r&&(e+=encodeURI(n)+\"=\"+encodeURI(b))});e+=c}d(a,\"displayError\",{chart:f,code:c,message:e,params:k},function(){if(b)throw Error(e);q.console&&-1===x.messages.indexOf(e)&&console.warn(e)});x.messages.push(e)}function I(c,b){return parseInt(c,b||10)}function L(c){return\"string\"===typeof c}function C(c){c=Object.prototype.toString.call(c);return\"[object Array]\"===c||\"[object Array Iterator]\"===c}function z(c,\nb){return!!c&&\"object\"===typeof c&&(!b||!C(c))}function H(c){return z(c)&&\"number\"===typeof c.nodeType}function B(c){const b=c&&c.constructor;return!(!z(c,!0)||H(c)||!b||!b.name||\"Object\"===b.name)}function u(c){return\"number\"===typeof c&&!isNaN(c)&&Infinity>c&&-Infinity<c}function v(c){return\"undefined\"!==typeof c&&null!==c}function l(c,b,f){const n=L(b)&&!v(f);let d;const k=(b,f)=>{v(b)?c.setAttribute(f,b):n?(d=c.getAttribute(f))||\"class\"!==f||(d=c.getAttribute(f+\"Name\")):c.removeAttribute(f)};\nL(b)?k(f,b):E(b,k);return d}function p(c){return C(c)?c:[c]}function t(c,b){let n;c||(c={});for(n in b)c[n]=b[n];return c}function m(){const c=arguments,b=c.length;for(let n=0;n<b;n++){const b=c[n];if(\"undefined\"!==typeof b&&null!==b)return b}}function h(c,b){a.isMS&&!a.svg&&b&&v(b.opacity)&&(b.filter=`alpha(opacity=${100*b.opacity})`);t(c.style,b)}function g(c){return Math.pow(10,Math.floor(Math.log(c)/Math.LN10))}function e(c,b){return 1E14<c?c:parseFloat(c.toPrecision(b||14))}function w(c,b,f){let n;\nif(\"width\"===b)return b=Math.min(c.offsetWidth,c.scrollWidth),f=c.getBoundingClientRect&&c.getBoundingClientRect().width,f<b&&f>=b-1&&(b=Math.floor(f)),Math.max(0,b-(w(c,\"padding-left\",!0)||0)-(w(c,\"padding-right\",!0)||0));if(\"height\"===b)return Math.max(0,Math.min(c.offsetHeight,c.scrollHeight)-(w(c,\"padding-top\",!0)||0)-(w(c,\"padding-bottom\",!0)||0));if(c=q.getComputedStyle(c,void 0))n=c.getPropertyValue(b),m(f,\"opacity\"!==b)&&(n=I(n));return n}function E(c,b,f){for(const n in c)Object.hasOwnProperty.call(c,\nn)&&b.call(f||c[n],c[n],n,c)}function F(c,b,f){function n(b,n){const f=c.removeEventListener;f&&f.call(c,b,n,!1)}function d(f){let d,K;c.nodeName&&(b?(d={},d[b]=!0):d=f,E(d,function(c,b){if(f[b])for(K=f[b].length;K--;)n(b,f[b][K].fn)}))}var k=\"function\"===typeof c&&c.prototype||c;if(Object.hasOwnProperty.call(k,\"hcEvents\")){const c=k.hcEvents;b?(k=c[b]||[],f?(c[b]=k.filter(function(c){return f!==c.fn}),n(b,f)):(d(c),c[b]=[])):(d(c),delete k.hcEvents)}}function d(c,b,f,d){f=f||{};if(r.createEvent&&\n(c.dispatchEvent||c.fireEvent&&c!==a)){var n=r.createEvent(\"Events\");n.initEvent(b,!0,!0);f=t(n,f);c.dispatchEvent?c.dispatchEvent(f):c.fireEvent(b,f)}else if(c.hcEvents){f.target||t(f,{preventDefault:function(){f.defaultPrevented=!0},target:c,type:b});n=[];let d=c,K=!1;for(;d.hcEvents;)Object.hasOwnProperty.call(d,\"hcEvents\")&&d.hcEvents[b]&&(n.length&&(K=!0),n.unshift.apply(n,d.hcEvents[b])),d=Object.getPrototypeOf(d);K&&n.sort((c,b)=>c.order-b.order);n.forEach(b=>{!1===b.fn.call(c,f)&&f.preventDefault()})}d&&\n!f.defaultPrevented&&d.call(c,f)}const {charts:k,doc:r,win:q}=a;(x||(x={})).messages=[];Math.easeInOutSine=function(c){return-.5*(Math.cos(Math.PI*c)-1)};var G=Array.prototype.find?function(c,b){return c.find(b)}:function(c,b){let f;const n=c.length;for(f=0;f<n;f++)if(b(c[f],f))return c[f]};E({map:\"map\",each:\"forEach\",grep:\"filter\",reduce:\"reduce\",some:\"some\"},function(c,b){a[b]=function(f){x(32,!1,void 0,{[`Highcharts.${b}`]:`use Array.${c}`});return Array.prototype[c].apply(f,[].slice.call(arguments,\n1))}});let b;const f=function(){const c=Math.random().toString(36).substring(2,9)+\"-\";let f=0;return function(){return\"highcharts-\"+(b?\"\":c)+f++}}();q.jQuery&&(q.jQuery.fn.highcharts=function(){const c=[].slice.call(arguments);if(this[0])return c[0]?(new (a[L(c[0])?c.shift():\"Chart\"])(this[0],c[0],c[1]),this):k[l(this[0],\"data-highcharts-chart\")]});G={addEvent:function(c,b,f,d={}){var n=\"function\"===typeof c&&c.prototype||c;Object.hasOwnProperty.call(n,\"hcEvents\")||(n.hcEvents={});n=n.hcEvents;a.Point&&\nc instanceof a.Point&&c.series&&c.series.chart&&(c.series.chart.runTrackerClick=!0);const k=c.addEventListener;k&&k.call(c,b,f,a.supportsPassiveEvents?{passive:void 0===d.passive?-1!==b.indexOf(\"touch\"):d.passive,capture:!1}:!1);n[b]||(n[b]=[]);n[b].push({fn:f,order:\"number\"===typeof d.order?d.order:Infinity});n[b].sort((c,b)=>c.order-b.order);return function(){F(c,b,f)}},arrayMax:function(c){let b=c.length,f=c[0];for(;b--;)c[b]>f&&(f=c[b]);return f},arrayMin:function(c){let b=c.length,f=c[0];for(;b--;)c[b]<\nf&&(f=c[b]);return f},attr:l,clamp:function(c,b,f){return c>b?c<f?c:f:b},clearTimeout:function(c){v(c)&&clearTimeout(c)},correctFloat:e,createElement:function(c,b,f,d,K){c=r.createElement(c);b&&t(c,b);K&&h(c,{padding:\"0\",border:\"none\",margin:\"0\"});f&&h(c,f);d&&d.appendChild(c);return c},css:h,defined:v,destroyObjectProperties:function(c,b){E(c,function(f,n){f&&f!==b&&f.destroy&&f.destroy();delete c[n]})},diffObjects:function(c,b,f,d){function n(b,c,K,k){const A=f?c:b;E(b,function(f,q){if(!k&&d&&-1<\nd.indexOf(q)&&c[q]){f=p(f);K[q]=[];for(let b=0;b<Math.max(f.length,c[q].length);b++)c[q][b]&&(void 0===f[b]?K[q][b]=c[q][b]:(K[q][b]={},n(f[b],c[q][b],K[q][b],k+1)))}else if(z(f,!0)&&!f.nodeType)K[q]=C(f)?[]:{},n(f,c[q]||{},K[q],k+1),0!==Object.keys(K[q]).length||\"colorAxis\"===q&&0===k||delete K[q];else if(b[q]!==c[q]||q in b&&!(q in c))K[q]=A[q]})}const k={};n(c,b,k,0);return k},discardElement:function(b){b&&b.parentElement&&b.parentElement.removeChild(b)},erase:function(b,f){let c=b.length;for(;c--;)if(b[c]===\nf){b.splice(c,1);break}},error:x,extend:t,extendClass:function(b,f){const c=function(){};c.prototype=new b;t(c.prototype,f);return c},find:G,fireEvent:d,getClosestDistance:function(b,f){const c=!f;let d,n,k,q;b.forEach(b=>{if(1<b.length)for(q=n=b.length-1;0<q;q--)k=b[q]-b[q-1],0>k&&!c?(null===f||void 0===f?void 0:f(),f=void 0):k&&(\"undefined\"===typeof d||k<d)&&(d=k)});return d},getMagnitude:g,getNestedProperty:function(b,f){for(b=b.split(\".\");b.length&&v(f);){const c=b.shift();if(\"undefined\"===typeof c||\n\"__proto__\"===c)return;if(\"this\"===c){let b;z(f)&&(b=f[\"@this\"]);return null!==b&&void 0!==b?b:f}f=f[c];if(!v(f)||\"function\"===typeof f||\"number\"===typeof f.nodeType||f===q)return}return f},getStyle:w,inArray:function(b,f,d){x(32,!1,void 0,{\"Highcharts.inArray\":\"use Array.indexOf\"});return f.indexOf(b,d)},insertItem:function(b,f){const c=b.options.index,d=f.length;let n;for(n=b.options.isInternal?d:0;n<d+1;n++)if(!f[n]||u(c)&&c<m(f[n].options.index,f[n]._i)||f[n].options.isInternal){f.splice(n,0,\nb);break}return n},isArray:C,isClass:B,isDOMElement:H,isFunction:function(b){return\"function\"===typeof b},isNumber:u,isObject:z,isString:L,keys:function(b){x(32,!1,void 0,{\"Highcharts.keys\":\"use Object.keys\"});return Object.keys(b)},merge:function(){let b,f=arguments,d={};const k=function(b,c){\"object\"!==typeof b&&(b={});E(c,function(f,d){\"__proto__\"!==d&&\"constructor\"!==d&&(!z(f,!0)||B(f)||H(f)?b[d]=c[d]:b[d]=k(b[d]||{},f))});return b};!0===f[0]&&(d=f[1],f=Array.prototype.slice.call(f,2));const K=\nf.length;for(b=0;b<K;b++)d=k(d,f[b]);return d},normalizeTickInterval:function(b,f,d,k,K){let c=b;d=m(d,g(b));const n=b/d;f||(f=K?[1,1.2,1.5,2,2.5,3,4,5,6,8,10]:[1,2,2.5,5,10],!1===k&&(1===d?f=f.filter(function(b){return 0===b%1}):.1>=d&&(f=[1/d])));for(k=0;k<f.length&&!(c=f[k],K&&c*d>=b||!K&&n<=(f[k]+(f[k+1]||f[k]))/2);k++);return c=e(c*d,-Math.round(Math.log(.001)/Math.LN10))},objectEach:E,offset:function(b){const c=r.documentElement;b=b.parentElement||b.parentNode?b.getBoundingClientRect():{top:0,\nleft:0,width:0,height:0};return{top:b.top+(q.pageYOffset||c.scrollTop)-(c.clientTop||0),left:b.left+(q.pageXOffset||c.scrollLeft)-(c.clientLeft||0),width:b.width,height:b.height}},pad:function(b,f,d){return Array((f||2)+1-String(b).replace(\"-\",\"\").length).join(d||\"0\")+b},pick:m,pInt:I,pushUnique:function(b,f){return 0>b.indexOf(f)&&!!b.push(f)},relativeLength:function(b,f,d){return/%$/.test(b)?f*parseFloat(b)/100+(d||0):parseFloat(b)},removeEvent:F,splat:p,stableSort:function(b,f){const c=b.length;\nlet d,k;for(k=0;k<c;k++)b[k].safeI=k;b.sort(function(b,c){d=f(b,c);return 0===d?b.safeI-c.safeI:d});for(k=0;k<c;k++)delete b[k].safeI},syncTimeout:function(b,f,d){if(0<f)return setTimeout(b,f,d);b.call(0,d);return-1},timeUnits:{millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5,month:24192E5,year:314496E5},uniqueKey:f,useSerialIds:function(c){return b=m(c,b)},wrap:function(b,f,d){const c=b[f];b[f]=function(){const b=arguments,f=this;return d.apply(this,[function(){return c.apply(f,\narguments.length?arguments:b)}].concat([].slice.call(arguments)))}}};\"\";return G});M(a,\"Core/Chart/ChartDefaults.js\",[],function(){return{alignThresholds:!1,panning:{enabled:!1,type:\"x\"},styledMode:!1,borderRadius:0,colorCount:10,allowMutatingData:!0,ignoreHiddenSeries:!0,spacing:[10,10,15,10],resetZoomButton:{theme:{zIndex:6},position:{align:\"right\",x:-10,y:10}},reflow:!0,type:\"line\",zooming:{singleTouch:!1,resetButton:{theme:{zIndex:6},position:{align:\"right\",x:-10,y:10}}},width:null,height:null,\nborderColor:\"#334eff\",backgroundColor:\"#ffffff\",plotBorderColor:\"#cccccc\"}});M(a,\"Core/Color/Color.js\",[a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y){const {isNumber:x,merge:L,pInt:C}=y;class z{static parse(a){return a?new z(a):z.None}constructor(x){this.rgba=[NaN,NaN,NaN,NaN];this.input=x;const B=a.Color;if(B&&B!==z)return new B(x);this.init(x)}init(a){let B;let u;if(\"object\"===typeof a&&\"undefined\"!==typeof a.stops)this.stops=a.stops.map(l=>new z(l[1]));else if(\"string\"===typeof a){this.input=\na=z.names[a.toLowerCase()]||a;if(\"#\"===a.charAt(0)){var v=a.length;var l=parseInt(a.substr(1),16);7===v?B=[(l&16711680)>>16,(l&65280)>>8,l&255,1]:4===v&&(B=[(l&3840)>>4|(l&3840)>>8,(l&240)>>4|l&240,(l&15)<<4|l&15,1])}if(!B)for(l=z.parsers.length;l--&&!B;)u=z.parsers[l],(v=u.regex.exec(a))&&(B=u.parse(v))}B&&(this.rgba=B)}get(a){const B=this.input,u=this.rgba;if(\"object\"===typeof B&&\"undefined\"!==typeof this.stops){const v=L(B);v.stops=[].slice.call(v.stops);this.stops.forEach((l,p)=>{v.stops[p]=[v.stops[p][0],\nl.get(a)]});return v}return u&&x(u[0])?\"rgb\"===a||!a&&1===u[3]?\"rgb(\"+u[0]+\",\"+u[1]+\",\"+u[2]+\")\":\"a\"===a?`${u[3]}`:\"rgba(\"+u.join(\",\")+\")\":B}brighten(a){const B=this.rgba;if(this.stops)this.stops.forEach(function(u){u.brighten(a)});else if(x(a)&&0!==a)for(let u=0;3>u;u++)B[u]+=C(255*a),0>B[u]&&(B[u]=0),255<B[u]&&(B[u]=255);return this}setOpacity(a){this.rgba[3]=a;return this}tweenTo(a,B){const u=this.rgba,v=a.rgba;if(!x(u[0])||!x(v[0]))return a.input||\"none\";a=1!==v[3]||1!==u[3];return(a?\"rgba(\":\n\"rgb(\")+Math.round(v[0]+(u[0]-v[0])*(1-B))+\",\"+Math.round(v[1]+(u[1]-v[1])*(1-B))+\",\"+Math.round(v[2]+(u[2]-v[2])*(1-B))+(a?\",\"+(v[3]+(u[3]-v[3])*(1-B)):\"\")+\")\"}}z.names={white:\"#ffffff\",black:\"#000000\"};z.parsers=[{regex:/rgba\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]?(?:\\.[0-9]+)?)\\s*\\)/,parse:function(a){return[C(a[1]),C(a[2]),C(a[3]),parseFloat(a[4],10)]}},{regex:/rgb\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*\\)/,parse:function(a){return[C(a[1]),C(a[2]),\nC(a[3]),1]}}];z.None=new z(\"\");\"\";return z});M(a,\"Core/Color/Palettes.js\",[],function(){return{colors:\"#2caffe #544fc5 #00e272 #fe6a35 #6b8abc #d568fb #2ee0ca #fa4b42 #feb56a #91e8e1\".split(\" \")}});M(a,\"Core/Time.js\",[a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y){const {win:x}=a,{defined:L,error:C,extend:z,isObject:H,merge:B,objectEach:u,pad:v,pick:l,splat:p,timeUnits:t}=y,m=a.isSafari&&x.Intl&&x.Intl.DateTimeFormat.prototype.formatRange,h=a.isSafari&&x.Intl&&!x.Intl.DateTimeFormat.prototype.formatRange;\nclass g{constructor(e){this.options={};this.variableTimezone=this.useUTC=!1;this.Date=x.Date;this.getTimezoneOffset=this.timezoneOffsetFunction();this.update(e)}get(e,g){if(this.variableTimezone||this.timezoneOffset){const h=g.getTime(),m=h-this.getTimezoneOffset(g);g.setTime(m);e=g[\"getUTC\"+e]();g.setTime(h);return e}return this.useUTC?g[\"getUTC\"+e]():g[\"get\"+e]()}set(e,g,h){if(this.variableTimezone||this.timezoneOffset){if(\"Milliseconds\"===e||\"Seconds\"===e||\"Minutes\"===e&&0===this.getTimezoneOffset(g)%\n36E5)return g[\"setUTC\"+e](h);var w=this.getTimezoneOffset(g);w=g.getTime()-w;g.setTime(w);g[\"setUTC\"+e](h);e=this.getTimezoneOffset(g);w=g.getTime()+e;return g.setTime(w)}return this.useUTC||m&&\"FullYear\"===e?g[\"setUTC\"+e](h):g[\"set\"+e](h)}update(e={}){const g=l(e.useUTC,!0);this.options=e=B(!0,this.options,e);this.Date=e.Date||x.Date||Date;this.timezoneOffset=(this.useUTC=g)&&e.timezoneOffset||void 0;this.getTimezoneOffset=this.timezoneOffsetFunction();this.variableTimezone=g&&!(!e.getTimezoneOffset&&\n!e.timezone)}makeTime(e,g,m,p,d,k){let r,q,w;this.useUTC?(r=this.Date.UTC.apply(0,arguments),q=this.getTimezoneOffset(r),r+=q,w=this.getTimezoneOffset(r),q!==w?r+=w-q:q-36E5!==this.getTimezoneOffset(r-36E5)||h||(r-=36E5)):r=(new this.Date(e,g,l(m,1),l(p,0),l(d,0),l(k,0))).getTime();return r}timezoneOffsetFunction(){const e=this,g=this.options,h=g.getTimezoneOffset,m=g.moment||x.moment;if(!this.useUTC)return function(d){return 6E4*(new Date(d.toString())).getTimezoneOffset()};if(g.timezone){if(m)return function(d){return 6E4*\n-m.tz(d,g.timezone).utcOffset()};C(25)}return this.useUTC&&h?function(d){return 6E4*h(d.valueOf())}:function(){return 6E4*(e.timezoneOffset||0)}}dateFormat(e,g,h){if(!L(g)||isNaN(g))return a.defaultOptions.lang&&a.defaultOptions.lang.invalidDate||\"\";e=l(e,\"%Y-%m-%d %H:%M:%S\");const m=this;var d=new this.Date(g);const k=this.get(\"Hours\",d),r=this.get(\"Day\",d),q=this.get(\"Date\",d),w=this.get(\"Month\",d),b=this.get(\"FullYear\",d),f=a.defaultOptions.lang,c=f&&f.weekdays,n=f&&f.shortWeekdays;d=z({a:n?n[r]:\nc[r].substr(0,3),A:c[r],d:v(q),e:v(q,2,\" \"),w:r,b:f.shortMonths[w],B:f.months[w],m:v(w+1),o:w+1,y:b.toString().substr(2,2),Y:b,H:v(k),k,I:v(k%12||12),l:k%12||12,M:v(this.get(\"Minutes\",d)),p:12>k?\"AM\":\"PM\",P:12>k?\"am\":\"pm\",S:v(d.getSeconds()),L:v(Math.floor(g%1E3),3)},a.dateFormats);u(d,function(b,c){for(;-1!==e.indexOf(\"%\"+c);)e=e.replace(\"%\"+c,\"function\"===typeof b?b.call(m,g):b)});return h?e.substr(0,1).toUpperCase()+e.substr(1):e}resolveDTLFormat(e){return H(e,!0)?e:(e=p(e),{main:e[0],from:e[1],\nto:e[2]})}getTimeTicks(e,g,h,m){const d=this,k=[],r={};var q=new d.Date(g);const w=e.unitRange,b=e.count||1;let f;m=l(m,1);if(L(g)){d.set(\"Milliseconds\",q,w>=t.second?0:b*Math.floor(d.get(\"Milliseconds\",q)/b));w>=t.second&&d.set(\"Seconds\",q,w>=t.minute?0:b*Math.floor(d.get(\"Seconds\",q)/b));w>=t.minute&&d.set(\"Minutes\",q,w>=t.hour?0:b*Math.floor(d.get(\"Minutes\",q)/b));w>=t.hour&&d.set(\"Hours\",q,w>=t.day?0:b*Math.floor(d.get(\"Hours\",q)/b));w>=t.day&&d.set(\"Date\",q,w>=t.month?1:Math.max(1,b*Math.floor(d.get(\"Date\",\nq)/b)));if(w>=t.month){d.set(\"Month\",q,w>=t.year?0:b*Math.floor(d.get(\"Month\",q)/b));var c=d.get(\"FullYear\",q)}w>=t.year&&d.set(\"FullYear\",q,c-c%b);w===t.week&&(c=d.get(\"Day\",q),d.set(\"Date\",q,d.get(\"Date\",q)-c+m+(c<m?-7:0)));c=d.get(\"FullYear\",q);m=d.get(\"Month\",q);const n=d.get(\"Date\",q),e=d.get(\"Hours\",q);g=q.getTime();!d.variableTimezone&&d.useUTC||!L(h)||(f=h-g>4*t.month||d.getTimezoneOffset(g)!==d.getTimezoneOffset(h));g=q.getTime();for(q=1;g<h;)k.push(g),g=w===t.year?d.makeTime(c+q*b,0):w===\nt.month?d.makeTime(c,m+q*b):!f||w!==t.day&&w!==t.week?f&&w===t.hour&&1<b?d.makeTime(c,m,n,e+q*b):g+w*b:d.makeTime(c,m,n+q*b*(w===t.day?1:7)),q++;k.push(g);w<=t.hour&&1E4>k.length&&k.forEach(function(b){0===b%18E5&&\"000000000\"===d.dateFormat(\"%H%M%S%L\",b)&&(r[b]=\"day\")})}k.info=z(e,{higherRanks:r,totalRange:w*b});return k}getDateFormat(e,g,h,m){const d=this.dateFormat(\"%m-%d %H:%M:%S.%L\",g),k={millisecond:15,second:12,minute:9,hour:6,day:3};let r,q=\"millisecond\";for(r in t){if(e===t.week&&+this.dateFormat(\"%w\",\ng)===h&&\"00:00:00.000\"===d.substr(6)){r=\"week\";break}if(t[r]>e){r=q;break}if(k[r]&&d.substr(k[r])!==\"01-01 00:00:00.000\".substr(k[r]))break;\"week\"!==r&&(q=r)}return this.resolveDTLFormat(m[r]).main}}\"\";return g});M(a,\"Core/Defaults.js\",[a[\"Core/Chart/ChartDefaults.js\"],a[\"Core/Color/Color.js\"],a[\"Core/Globals.js\"],a[\"Core/Color/Palettes.js\"],a[\"Core/Time.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C,z){const {isTouchDevice:x,svg:B}=I,{merge:u}=z,v={colors:L.colors,symbols:[\"circle\",\"diamond\",\"square\",\n\"triangle\",\"triangle-down\"],lang:{loading:\"Loading...\",months:\"January February March April May June July August September October November December\".split(\" \"),shortMonths:\"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\".split(\" \"),weekdays:\"Sunday Monday Tuesday Wednesday Thursday Friday Saturday\".split(\" \"),decimalPoint:\".\",numericSymbols:\"kMGTPE\".split(\"\"),resetZoom:\"Reset zoom\",resetZoomTitle:\"Reset zoom level 1:1\",thousandsSep:\" \"},global:{},time:{Date:void 0,getTimezoneOffset:void 0,timezone:void 0,\ntimezoneOffset:0,useUTC:!0},chart:a,title:{style:{color:\"#333333\",fontWeight:\"bold\"},text:\"Chart title\",align:\"center\",margin:15,widthAdjust:-44},subtitle:{style:{color:\"#666666\",fontSize:\"0.8em\"},text:\"\",align:\"center\",widthAdjust:-44},caption:{margin:15,style:{color:\"#666666\",fontSize:\"0.8em\"},text:\"\",align:\"left\",verticalAlign:\"bottom\"},plotOptions:{},legend:{enabled:!0,align:\"center\",alignColumns:!0,className:\"highcharts-no-tooltip\",layout:\"horizontal\",itemMarginBottom:2,itemMarginTop:2,labelFormatter:function(){return this.name},\nborderColor:\"#999999\",borderRadius:0,navigation:{style:{fontSize:\"0.8em\"},activeColor:\"#0022ff\",inactiveColor:\"#cccccc\"},itemStyle:{color:\"#333333\",cursor:\"pointer\",fontSize:\"0.8em\",textDecoration:\"none\",textOverflow:\"ellipsis\"},itemHoverStyle:{color:\"#000000\"},itemHiddenStyle:{color:\"#666666\",textDecoration:\"line-through\"},shadow:!1,itemCheckboxStyle:{position:\"absolute\",width:\"13px\",height:\"13px\"},squareSymbol:!0,symbolPadding:5,verticalAlign:\"bottom\",x:0,y:0,title:{style:{fontSize:\"0.8em\",fontWeight:\"bold\"}}},\nloading:{labelStyle:{fontWeight:\"bold\",position:\"relative\",top:\"45%\"},style:{position:\"absolute\",backgroundColor:\"#ffffff\",opacity:.5,textAlign:\"center\"}},tooltip:{enabled:!0,animation:B,borderRadius:3,dateTimeLabelFormats:{millisecond:\"%A, %e %b, %H:%M:%S.%L\",second:\"%A, %e %b, %H:%M:%S\",minute:\"%A, %e %b, %H:%M\",hour:\"%A, %e %b, %H:%M\",day:\"%A, %e %b %Y\",week:\"Week from %A, %e %b %Y\",month:\"%B %Y\",year:\"%Y\"},footerFormat:\"\",headerShape:\"callout\",hideDelay:500,padding:8,shape:\"callout\",shared:!1,\nsnap:x?25:10,headerFormat:'<span style=\"font-size: 0.8em\">{point.key}</span><br/>',pointFormat:'<span style=\"color:{point.color}\">\\u25cf</span> {series.name}: <b>{point.y}</b><br/>',backgroundColor:\"#ffffff\",borderWidth:void 0,shadow:!0,stickOnContact:!1,style:{color:\"#333333\",cursor:\"default\",fontSize:\"0.8em\"},useHTML:!1},credits:{enabled:!0,href:\"https://www.highcharts.com?credits\",position:{align:\"right\",x:-10,verticalAlign:\"bottom\",y:-5},style:{cursor:\"pointer\",color:\"#999999\",fontSize:\"0.6em\"},\ntext:\"Highcharts.com\"}};v.chart.styledMode=!1;\"\";const l=new C(v.time);a={defaultOptions:v,defaultTime:l,getOptions:function(){return v},setOptions:function(a){u(!0,v,a);if(a.time||a.global)I.time?I.time.update(u(v.global,v.time,a.global,a.time)):I.time=l;return v}};\"\";return a});M(a,\"Core/Animation/Fx.js\",[a[\"Core/Color/Color.js\"],a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {parse:x}=a,{win:C}=y,{isNumber:z,objectEach:H}=I;class B{constructor(a,v,l){this.pos=NaN;this.options=\nv;this.elem=a;this.prop=l}dSetter(){var a=this.paths;const v=a&&a[0];a=a&&a[1];const l=this.now||0;let p=[];if(1!==l&&v&&a)if(v.length===a.length&&1>l)for(let t=0;t<a.length;t++){const m=v[t],h=a[t],g=[];for(let e=0;e<h.length;e++){const w=m[e],a=h[e];z(w)&&z(a)&&(\"A\"!==h[0]||4!==e&&5!==e)?g[e]=w+l*(a-w):g[e]=a}p.push(g)}else p=a;else p=this.toD||[];this.elem.attr(\"d\",p,void 0,!0)}update(){const a=this.elem,v=this.prop,l=this.now,p=this.options.step;if(this[v+\"Setter\"])this[v+\"Setter\"]();else a.attr?\na.element&&a.attr(v,l,null,!0):a.style[v]=l+this.unit;p&&p.call(a,l,this)}run(a,v,l){const p=this,t=p.options,m=function(e){return m.stopped?!1:p.step(e)},h=C.requestAnimationFrame||function(e){setTimeout(e,13)},g=function(){for(let e=0;e<B.timers.length;e++)B.timers[e]()||B.timers.splice(e--,1);B.timers.length&&h(g)};a!==v||this.elem[\"forceAnimate:\"+this.prop]?(this.startTime=+new Date,this.start=a,this.end=v,this.unit=l,this.now=this.start,this.pos=0,m.elem=this.elem,m.prop=this.prop,m()&&1===B.timers.push(m)&&\nh(g)):(delete t.curAnim[this.prop],t.complete&&0===Object.keys(t.curAnim).length&&t.complete.call(this.elem))}step(a){const v=+new Date,l=this.options,p=this.elem,t=l.complete,m=l.duration,h=l.curAnim;let g;p.attr&&!p.element?a=!1:a||v>=m+this.startTime?(this.now=this.end,this.pos=1,this.update(),g=h[this.prop]=!0,H(h,function(e){!0!==e&&(g=!1)}),g&&t&&t.call(p),a=!1):(this.pos=l.easing((v-this.startTime)/m),this.now=this.start+(this.end-this.start)*this.pos,this.update(),a=!0);return a}initPath(a,\nv,l){function p(d,k){for(;d.length<E;){var r=d[0];const q=k[E-d.length];q&&\"M\"===r[0]&&(d[0]=\"C\"===q[0]?[\"C\",r[1],r[2],r[1],r[2],r[1],r[2]]:[\"L\",r[1],r[2]]);d.unshift(r);g&&(r=d.pop(),d.push(d[d.length-1],r))}}function t(d,k){for(;d.length<E;)if(k=d[Math.floor(d.length/e)-1].slice(),\"C\"===k[0]&&(k[1]=k[5],k[2]=k[6]),g){const r=d[Math.floor(d.length/e)].slice();d.splice(d.length/2,0,k,r)}else d.push(k)}const m=a.startX,h=a.endX;l=l.slice();const g=a.isArea,e=g?2:1;let w,E,F;v=v&&v.slice();if(!v)return[l,\nl];if(m&&h&&h.length){for(a=0;a<m.length;a++)if(m[a]===h[0]){w=a;break}else if(m[0]===h[h.length-m.length+a]){w=a;F=!0;break}else if(m[m.length-1]===h[h.length-m.length+a]){w=m.length-a;break}\"undefined\"===typeof w&&(v=[])}v.length&&z(w)&&(E=l.length+w*e,F?(p(v,l),t(l,v)):(p(l,v),t(v,l)));return[v,l]}fillSetter(){B.prototype.strokeSetter.apply(this,arguments)}strokeSetter(){this.elem.attr(this.prop,x(this.start).tweenTo(x(this.end),this.pos),void 0,!0)}}B.timers=[];return B});M(a,\"Core/Animation/AnimationUtilities.js\",\n[a[\"Core/Animation/Fx.js\"],a[\"Core/Utilities.js\"]],function(a,y){function x(a){return u(a)?v({duration:500,defer:0},a):{duration:a?500:0,defer:0}}function L(l,m){let h=a.timers.length;for(;h--;)a.timers[h].elem!==l||m&&m!==a.timers[h].prop||(a.timers[h].stopped=!0)}const {defined:C,getStyle:z,isArray:H,isNumber:B,isObject:u,merge:v,objectEach:l,pick:p}=y;return{animate:function(p,m,h){let g,e=\"\",w,E,F;u(h)||(F=arguments,h={duration:F[2],easing:F[3],complete:F[4]});B(h.duration)||(h.duration=400);\nh.easing=\"function\"===typeof h.easing?h.easing:Math[h.easing]||Math.easeInOutSine;h.curAnim=v(m);l(m,function(d,k){L(p,k);E=new a(p,h,k);w=void 0;\"d\"===k&&H(m.d)?(E.paths=E.initPath(p,p.pathArray,m.d),E.toD=m.d,g=0,w=1):p.attr?g=p.attr(k):(g=parseFloat(z(p,k))||0,\"opacity\"!==k&&(e=\"px\"));w||(w=d);\"string\"===typeof w&&w.match(\"px\")&&(w=w.replace(/px/g,\"\"));E.run(g,w,e)})},animObject:x,getDeferredAnimation:function(a,m,h){const g=x(m);let e=0,w=0;(h?[h]:a.series).forEach(h=>{h=x(h.options.animation);\ne=m&&C(m.defer)?g.defer:Math.max(e,h.duration+h.defer);w=Math.min(g.duration,h.duration)});a.renderer.forExport&&(e=0);return{defer:Math.max(0,e-w),duration:Math.min(e,w)}},setAnimation:function(a,m){m.renderer.globalAnimation=p(a,m.options.chart.animation,!0)},stop:L}});M(a,\"Core/Renderer/HTML/AST.js\",[a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y){const {SVG_NS:x,win:L}=a,{attr:C,createElement:z,css:H,error:B,isFunction:u,isString:v,objectEach:l,splat:p}=y;({trustedTypes:y}=L);const t=\ny&&u(y.createPolicy)&&y.createPolicy(\"highcharts\",{createHTML:e=>e});y=t?t.createHTML(\"\"):\"\";try{var m=!!(new DOMParser).parseFromString(y,\"text/html\")}catch(e){m=!1}const h=m;class g{static filterUserAttributes(e){l(e,(h,m)=>{let a=!0;-1===g.allowedAttributes.indexOf(m)&&(a=!1);-1!==[\"background\",\"dynsrc\",\"href\",\"lowsrc\",\"src\"].indexOf(m)&&(a=v(h)&&g.allowedReferences.some(d=>0===h.indexOf(d)));a||(B(33,!1,void 0,{\"Invalid attribute in config\":`${m}`}),delete e[m]);v(h)&&e[m]&&(e[m]=h.replace(/</g,\n\"&lt;\"))});return e}static parseStyle(e){return e.split(\";\").reduce((e,g)=>{g=g.split(\":\").map(d=>d.trim());const h=g.shift();h&&g.length&&(e[h.replace(/-([a-z])/g,d=>d[1].toUpperCase())]=g.join(\":\"));return e},{})}static setElementHTML(e,h){e.innerHTML=g.emptyHTML;h&&(new g(h)).addToDOM(e)}constructor(e){this.nodes=\"string\"===typeof e?this.parseMarkup(e):e}addToDOM(e){function h(e,m){let d;p(e).forEach(function(k){var e=k.tagName;const q=k.textContent?a.doc.createTextNode(k.textContent):void 0,w=\ng.bypassHTMLFiltering;let b;if(e)if(\"#text\"===e)b=q;else if(-1!==g.allowedTags.indexOf(e)||w){e=a.doc.createElementNS(\"svg\"===e?x:m.namespaceURI||x,e);const f=k.attributes||{};l(k,function(b,d){\"tagName\"!==d&&\"attributes\"!==d&&\"children\"!==d&&\"style\"!==d&&\"textContent\"!==d&&(f[d]=b)});C(e,w?f:g.filterUserAttributes(f));k.style&&H(e,k.style);q&&e.appendChild(q);h(k.children||[],e);b=e}else B(33,!1,void 0,{\"Invalid tagName in config\":e});b&&m.appendChild(b);d=b});return d}return h(this.nodes,e)}parseMarkup(e){const m=\n[];e=e.trim().replace(/ style=([\"'])/g,\" data-style=$1\");if(h)e=(new DOMParser).parseFromString(t?t.createHTML(e):e,\"text/html\");else{const g=z(\"div\");g.innerHTML=e;e={body:g}}const a=(e,d)=>{var k=e.nodeName.toLowerCase();const r={tagName:k};\"#text\"===k&&(r.textContent=e.textContent||\"\");if(k=e.attributes){const d={};[].forEach.call(k,k=>{\"data-style\"===k.name?r.style=g.parseStyle(k.value):d[k.name]=k.value});r.attributes=d}if(e.childNodes.length){const d=[];[].forEach.call(e.childNodes,k=>{a(k,\nd)});d.length&&(r.children=d)}d.push(r)};[].forEach.call(e.body.childNodes,e=>a(e,m));return m}}g.allowedAttributes=\"alt aria-controls aria-describedby aria-expanded aria-haspopup aria-hidden aria-label aria-labelledby aria-live aria-pressed aria-readonly aria-roledescription aria-selected class clip-path color colspan cx cy d dx dy disabled fill flood-color flood-opacity height href id in markerHeight markerWidth offset opacity orient padding paddingLeft paddingRight patternUnits r refX refY role scope slope src startOffset stdDeviation stroke stroke-linecap stroke-width style tableValues result rowspan summary target tabindex text-align text-anchor textAnchor textLength title type valign width x x1 x2 xlink:href y y1 y2 zIndex\".split(\" \");\ng.allowedReferences=\"https:// http:// mailto: / ../ ./ #\".split(\" \");g.allowedTags=\"a abbr b br button caption circle clipPath code dd defs div dl dt em feComponentTransfer feDropShadow feFuncA feFuncB feFuncG feFuncR feGaussianBlur feOffset feMerge feMergeNode filter h1 h2 h3 h4 h5 h6 hr i img li linearGradient marker ol p path pattern pre rect small span stop strong style sub sup svg table text textPath thead title tbody tspan td th tr u ul #text\".split(\" \");g.emptyHTML=y;g.bypassHTMLFiltering=\n!1;\"\";return g});M(a,\"Core/Templating.js\",[a[\"Core/Defaults.js\"],a[\"Core/Utilities.js\"]],function(a,y){function x(h=\"\",g,e){const a=/\\{([a-zA-Z0-9:\\.,;\\-\\/<>%_@\"'= #\\(\\)]+)\\}/g,l=/\\(([a-zA-Z0-9:\\.,;\\-\\/<>%_@\"'= ]+)\\)/g,v=[],d=/f$/,k=/\\.([0-9])/,r=C.lang,q=e&&e.time||z,G=e&&e.numberFormatter||L,b=(b=\"\")=>{let c;return\"true\"===b?!0:\"false\"===b?!1:(c=Number(b)).toString()===b?c:B(b,g)};let f,c,n=0,P;for(;null!==(f=a.exec(h));){const b=l.exec(f[1]);b&&(f=b,P=!0);c&&c.isBlock||(c={ctx:g,expression:f[1],\nfind:f[0],isBlock:\"#\"===f[1].charAt(0),start:f.index,startInner:f.index+f[0].length,length:f[0].length});var D=f[1].split(\" \")[0].replace(\"#\",\"\");m[D]&&(c.isBlock&&D===c.fn&&n++,c.fn||(c.fn=D));D=\"else\"===f[1];if(c.isBlock&&c.fn&&(f[1]===`/${c.fn}`||D))if(n)D||n--;else{var K=c.startInner;K=h.substr(K,f.index-K);void 0===c.body?(c.body=K,c.startInner=f.index+f[0].length):c.elseBody=K;c.find+=K+f[0];D||(v.push(c),c=void 0)}else c.isBlock||v.push(c);if(b&&(null===c||void 0===c||!c.isBlock))break}v.forEach(c=>\n{const {body:f,elseBody:n,expression:K,fn:e}=c;var A;if(e){var a=[c],w=K.split(\" \");for(A=m[e].length;A--;)a.unshift(b(w[A+1]));A=m[e].apply(g,a);c.isBlock&&\"boolean\"===typeof A&&(A=x(A?f:n,g))}else a=K.split(\":\"),A=b(a.shift()||\"\"),a.length&&\"number\"===typeof A&&(a=a.join(\":\"),d.test(a)?(w=parseInt((a.match(k)||[\"\",\"-1\"])[1],10),null!==A&&(A=G(A,w,r.decimalPoint,-1<a.indexOf(\",\")?r.thousandsSep:\"\"))):A=q.dateFormat(a,A));h=h.replace(c.find,p(A,\"\"))});return P?x(h,g,e):h}function L(h,g,e,a){h=+h||\n0;g=+g;const m=C.lang;var w=(h.toString().split(\".\")[1]||\"\").split(\"e\")[0].length;const d=h.toString().split(\"e\"),k=g;if(-1===g)g=Math.min(w,20);else if(!v(g))g=2;else if(g&&d[1]&&0>d[1]){var r=g+ +d[1];0<=r?(d[0]=(+d[0]).toExponential(r).split(\"e\")[0],g=r):(d[0]=d[0].split(\".\")[0]||0,h=20>g?(d[0]*Math.pow(10,d[1])).toFixed(g):0,d[1]=0)}r=(Math.abs(d[1]?d[0]:h)+Math.pow(10,-Math.max(g,w)-1)).toFixed(g);w=String(t(r));const q=3<w.length?w.length%3:0;e=p(e,m.decimalPoint);a=p(a,m.thousandsSep);h=(0>\nh?\"-\":\"\")+(q?w.substr(0,q)+a:\"\");h=0>+d[1]&&!k?\"0\":h+w.substr(q).replace(/(\\d{3})(?=\\d)/g,\"$1\"+a);g&&(h+=e+r.slice(-g));d[1]&&0!==+h&&(h+=\"e\"+d[1]);return h}const {defaultOptions:C,defaultTime:z}=a,{extend:H,getNestedProperty:B,isArray:u,isNumber:v,isObject:l,pick:p,pInt:t}=y,m={add:(h,g)=>h+g,divide:(h,g)=>0!==g?h/g:\"\",eq:(h,g)=>h==g,each:function(h){const g=arguments[arguments.length-1];return u(h)?h.map((e,a)=>x(g.body,H(l(e)?e:{\"@this\":e},{\"@index\":a,\"@first\":0===a,\"@last\":a===h.length-1}))).join(\"\"):\n!1},ge:(h,g)=>h>=g,gt:(h,g)=>h>g,\"if\":h=>!!h,le:(h,g)=>h<=g,lt:(h,g)=>h<g,multiply:(h,g)=>h*g,ne:(h,g)=>h!=g,subtract:(h,g)=>h-g,unless:h=>!h};return{dateFormat:function(h,g,e){return z.dateFormat(h,g,e)},format:x,helpers:m,numberFormat:L}});M(a,\"Core/Renderer/RendererUtilities.js\",[a[\"Core/Utilities.js\"]],function(a){const {clamp:x,pick:I,stableSort:L}=a;var C;(function(a){function y(a,u,v){const l=a;var p=l.reducedLen||u,t=(e,g)=>(g.rank||0)-(e.rank||0);const m=(e,g)=>e.target-g.target;let h,g=\n!0,e=[],w=0;for(h=a.length;h--;)w+=a[h].size;if(w>p){L(a,t);for(w=h=0;w<=p;)w+=a[h].size,h++;e=a.splice(h-1,a.length)}L(a,m);for(a=a.map(e=>({size:e.size,targets:[e.target],align:I(e.align,.5)}));g;){for(h=a.length;h--;)p=a[h],t=(Math.min.apply(0,p.targets)+Math.max.apply(0,p.targets))/2,p.pos=x(t-p.size*p.align,0,u-p.size);h=a.length;for(g=!1;h--;)0<h&&a[h-1].pos+a[h-1].size>a[h].pos&&(a[h-1].size+=a[h].size,a[h-1].targets=a[h-1].targets.concat(a[h].targets),a[h-1].align=.5,a[h-1].pos+a[h-1].size>\nu&&(a[h-1].pos=u-a[h-1].size),a.splice(h,1),g=!0)}l.push.apply(l,e);h=0;a.some(e=>{let g=0;return(e.targets||[]).some(()=>{l[h].pos=e.pos+g;if(\"undefined\"!==typeof v&&Math.abs(l[h].pos-l[h].target)>v)return l.slice(0,h+1).forEach(d=>delete d.pos),l.reducedLen=(l.reducedLen||u)-.1*u,l.reducedLen>.1*u&&y(l,u,v),!0;g+=l[h].size;h++;return!1})});L(l,m);return l}a.distribute=y})(C||(C={}));return C});M(a,\"Core/Renderer/SVG/SVGElement.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Color/Color.js\"],\na[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L){const {animate:x,animObject:z,stop:H}=a,{deg2rad:B,doc:u,svg:v,SVG_NS:l,win:p}=I,{addEvent:t,attr:m,createElement:h,css:g,defined:e,erase:w,extend:E,fireEvent:F,isArray:d,isFunction:k,isObject:r,isString:q,merge:G,objectEach:b,pick:f,pInt:c,syncTimeout:n,uniqueKey:P}=L;class D{constructor(){this.element=void 0;this.onEvents={};this.opacity=1;this.renderer=void 0;this.SVG_NS=l}_defaultGetter(b){b=f(this[b+\"Value\"],this[b],this.element?\nthis.element.getAttribute(b):null,0);/^[\\-0-9\\.]+$/.test(b)&&(b=parseFloat(b));return b}_defaultSetter(b,c,f){f.setAttribute(c,b)}add(b){const c=this.renderer,f=this.element;let d;b&&(this.parentGroup=b);\"undefined\"!==typeof this.textStr&&\"text\"===this.element.nodeName&&c.buildText(this);this.added=!0;if(!b||b.handleZ||this.zIndex)d=this.zIndexSetter();d||(b?b.element:c.box).appendChild(f);if(this.onAdd)this.onAdd();return this}addClass(b,c){const f=c?\"\":this.attr(\"class\")||\"\";b=(b||\"\").split(/ /g).reduce(function(b,\nc){-1===f.indexOf(c)&&b.push(c);return b},f?[f]:[]).join(\" \");b!==f&&this.attr(\"class\",b);return this}afterSetters(){this.doTransform&&(this.updateTransform(),this.doTransform=!1)}align(b,c,d){const k={};var n=this.renderer,e=n.alignedObjects,A;let K,g;if(b){if(this.alignOptions=b,this.alignByTranslate=c,!d||q(d))this.alignTo=A=d||\"renderer\",w(e,this),e.push(this),d=void 0}else b=this.alignOptions,c=this.alignByTranslate,A=this.alignTo;d=f(d,n[A],\"scrollablePlotBox\"===A?n.plotBox:void 0,n);A=b.align;\nconst a=b.verticalAlign;n=(d.x||0)+(b.x||0);e=(d.y||0)+(b.y||0);\"right\"===A?K=1:\"center\"===A&&(K=2);K&&(n+=(d.width-(b.width||0))/K);k[c?\"translateX\":\"x\"]=Math.round(n);\"bottom\"===a?g=1:\"middle\"===a&&(g=2);g&&(e+=(d.height-(b.height||0))/g);k[c?\"translateY\":\"y\"]=Math.round(e);this[this.placed?\"animate\":\"attr\"](k);this.placed=!0;this.alignAttr=k;return this}alignSetter(b){const c={left:\"start\",center:\"middle\",right:\"end\"};c[b]&&(this.alignValue=b,this.element.setAttribute(\"text-anchor\",c[b]))}animate(c,\nd,k){const e=z(f(d,this.renderer.globalAnimation,!0));d=e.defer;u.hidden&&(e.duration=0);0!==e.duration?(k&&(e.complete=k),n(()=>{this.element&&x(this,c,e)},d)):(this.attr(c,void 0,k||e.complete),b(c,function(b,c){e.step&&e.step.call(this,b,{prop:c,pos:1,elem:this})},this));return this}applyTextOutline(b){const c=this.element;-1!==b.indexOf(\"contrast\")&&(b=b.replace(/contrast/g,this.renderer.getContrast(c.style.fill)));var f=b.split(\" \");b=f[f.length-1];if((f=f[0])&&\"none\"!==f&&I.svg){this.fakeTS=\n!0;f=f.replace(/(^[\\d\\.]+)(.*?)$/g,function(b,c,f){return 2*Number(c)+f});this.removeTextOutline();const d=u.createElementNS(l,\"tspan\");m(d,{\"class\":\"highcharts-text-outline\",fill:b,stroke:b,\"stroke-width\":f,\"stroke-linejoin\":\"round\"});b=c.querySelector(\"textPath\")||c;[].forEach.call(b.childNodes,b=>{const c=b.cloneNode(!0);c.removeAttribute&&[\"fill\",\"stroke\",\"stroke-width\",\"stroke\"].forEach(b=>c.removeAttribute(b));d.appendChild(c)});let k=0;[].forEach.call(b.querySelectorAll(\"text tspan\"),b=>{k+=\nNumber(b.getAttribute(\"dy\"))});f=u.createElementNS(l,\"tspan\");f.textContent=\"\\u200b\";m(f,{x:Number(c.getAttribute(\"x\")),dy:-k});d.appendChild(f);b.insertBefore(d,b.firstChild)}}attr(c,f,d,k){const n=this.element,e=D.symbolCustomAttribs;let A,q,g=this,a,K;\"string\"===typeof c&&\"undefined\"!==typeof f&&(A=c,c={},c[A]=f);\"string\"===typeof c?g=(this[c+\"Getter\"]||this._defaultGetter).call(this,c,n):(b(c,function(b,f){a=!1;k||H(this,f);this.symbolName&&-1!==e.indexOf(f)&&(q||(this.symbolAttr(c),q=!0),a=!0);\n!this.rotation||\"x\"!==f&&\"y\"!==f||(this.doTransform=!0);a||(K=this[f+\"Setter\"]||this._defaultSetter,K.call(this,b,f,n))},this),this.afterSetters());d&&d.call(this);return g}clip(b){return this.attr(\"clip-path\",b?\"url(\"+this.renderer.url+\"#\"+b.id+\")\":\"none\")}crisp(b,c){c=c||b.strokeWidth||0;const f=Math.round(c)%2/2;b.x=Math.floor(b.x||this.x||0)+f;b.y=Math.floor(b.y||this.y||0)+f;b.width=Math.floor((b.width||this.width||0)-2*f);b.height=Math.floor((b.height||this.height||0)-2*f);e(b.strokeWidth)&&\n(b.strokeWidth=c);return b}complexColor(c,f,k){const n=this.renderer;let q,g,A,a,r,K,h,J,m,O,w=[],l;F(this.renderer,\"complexColor\",{args:arguments},function(){c.radialGradient?g=\"radialGradient\":c.linearGradient&&(g=\"linearGradient\");if(g){A=c[g];r=n.gradients;K=c.stops;m=k.radialReference;d(A)&&(c[g]=A={x1:A[0],y1:A[1],x2:A[2],y2:A[3],gradientUnits:\"userSpaceOnUse\"});\"radialGradient\"===g&&m&&!e(A.gradientUnits)&&(a=A,A=G(A,n.getRadialAttr(m,a),{gradientUnits:\"userSpaceOnUse\"}));b(A,function(b,c){\"id\"!==\nc&&w.push(c,b)});b(K,function(b){w.push(b)});w=w.join(\",\");if(r[w])O=r[w].attr(\"id\");else{A.id=O=P();const b=r[w]=n.createElement(g).attr(A).add(n.defs);b.radAttr=a;b.stops=[];K.forEach(function(c){0===c[1].indexOf(\"rgba\")?(q=y.parse(c[1]),h=q.get(\"rgb\"),J=q.get(\"a\")):(h=c[1],J=1);c=n.createElement(\"stop\").attr({offset:c[0],\"stop-color\":h,\"stop-opacity\":J}).add(b);b.stops.push(c)})}l=\"url(\"+n.url+\"#\"+O+\")\";k.setAttribute(f,l);k.gradient=w;c.toString=function(){return l}}})}css(f){const d=this.styles,\nk={},n=this.element;let e,q=!d;d&&b(f,function(b,c){d&&d[c]!==b&&(k[c]=b,q=!0)});if(q){d&&(f=E(d,k));null===f.width||\"auto\"===f.width?delete this.textWidth:\"text\"===n.nodeName.toLowerCase()&&f.width&&(e=this.textWidth=c(f.width));this.styles=f;e&&!v&&this.renderer.forExport&&delete f.width;const b=G(f);n.namespaceURI===this.SVG_NS&&([\"textOutline\",\"textOverflow\",\"width\"].forEach(c=>b&&delete b[c]),b.color&&(b.fill=b.color));g(n,b)}this.added&&(\"text\"===this.element.nodeName&&this.renderer.buildText(this),\nf.textOutline&&this.applyTextOutline(f.textOutline));return this}dashstyleSetter(b){let d=this[\"stroke-width\"];\"inherit\"===d&&(d=1);if(b=b&&b.toLowerCase()){const k=b.replace(\"shortdashdotdot\",\"3,1,1,1,1,1,\").replace(\"shortdashdot\",\"3,1,1,1\").replace(\"shortdot\",\"1,1,\").replace(\"shortdash\",\"3,1,\").replace(\"longdash\",\"8,3,\").replace(/dot/g,\"1,3,\").replace(\"dash\",\"4,3,\").replace(/,$/,\"\").split(\",\");for(b=k.length;b--;)k[b]=\"\"+c(k[b])*f(d,NaN);b=k.join(\",\").replace(/NaN/g,\"none\");this.element.setAttribute(\"stroke-dasharray\",\nb)}}destroy(){const c=this;var f=c.element||{};const d=c.renderer;var k=f.ownerSVGElement;let n=\"SPAN\"===f.nodeName&&c.parentGroup||void 0;f.onclick=f.onmouseout=f.onmouseover=f.onmousemove=f.point=null;H(c);if(c.clipPath&&k){const b=c.clipPath;[].forEach.call(k.querySelectorAll(\"[clip-path],[CLIP-PATH]\"),function(c){-1<c.getAttribute(\"clip-path\").indexOf(b.element.id)&&c.removeAttribute(\"clip-path\")});c.clipPath=b.destroy()}if(c.stops){for(k=0;k<c.stops.length;k++)c.stops[k].destroy();c.stops.length=\n0;c.stops=void 0}for(c.safeRemoveChild(f);n&&n.div&&0===n.div.childNodes.length;)f=n.parentGroup,c.safeRemoveChild(n.div),delete n.div,n=f;c.alignTo&&w(d.alignedObjects,c);b(c,function(b,f){c[f]&&c[f].parentGroup===c&&c[f].destroy&&c[f].destroy();delete c[f]})}dSetter(b,c,f){d(b)&&(\"string\"===typeof b[0]&&(b=this.renderer.pathToSegments(b)),this.pathArray=b,b=b.reduce((b,c,f)=>c&&c.join?(f?b+\" \":\"\")+c.join(\" \"):(c||\"\").toString(),\"\"));/(NaN| {2}|^$)/.test(b)&&(b=\"M 0 0\");this[c]!==b&&(f.setAttribute(c,\nb),this[c]=b)}fadeOut(b){const c=this;c.animate({opacity:0},{duration:f(b,150),complete:function(){c.hide()}})}fillSetter(b,c,f){\"string\"===typeof b?f.setAttribute(c,b):b&&this.complexColor(b,c,f)}getBBox(b,c){const {alignValue:d,element:n,renderer:q,styles:a,textStr:A}=this,{cache:r,cacheKeys:h}=q;var m=n.namespaceURI===this.SVG_NS;c=f(c,this.rotation,0);var K=q.styledMode?n&&D.prototype.getStyle.call(n,\"font-size\"):a&&a.fontSize;let J;let N;e(A)&&(N=A.toString(),-1===N.indexOf(\"<\")&&(N=N.replace(/[0-9]/g,\n\"0\")),N+=[\"\",q.rootFontSize,K,c,this.textWidth,d,a&&a.textOverflow,a&&a.fontWeight].join());N&&!b&&(J=r[N]);if(!J){if(m||q.forExport){try{var O=this.fakeTS&&function(b){const c=n.querySelector(\".highcharts-text-outline\");c&&g(c,{display:b})};k(O)&&O(\"none\");J=n.getBBox?E({},n.getBBox()):{width:n.offsetWidth,height:n.offsetHeight,x:0,y:0};k(O)&&O(\"\")}catch(fa){\"\"}if(!J||0>J.width)J={x:0,y:0,width:0,height:0}}else J=this.htmlGetBBox();O=J.width;b=J.height;m&&(J.height=b={\"11px,17\":14,\"13px,20\":16}[`${K||\n\"\"},${Math.round(b)}`]||b);if(c){m=Number(n.getAttribute(\"y\")||0)-J.y;K={right:1,center:.5}[d||0]||0;var w=c*B,l=(c-90)*B,p=O*Math.cos(w);c=O*Math.sin(w);var G=Math.cos(l);w=Math.sin(l);O=J.x+K*(O-p)+m*G;l=O+p;G=l-b*G;p=G-p;m=J.y+m-K*c+m*w;K=m+c;b=K-b*w;c=b-c;J.x=Math.min(O,l,G,p);J.y=Math.min(m,K,b,c);J.width=Math.max(O,l,G,p)-J.x;J.height=Math.max(m,K,b,c)-J.y}}if(N&&(\"\"===A||0<J.height)){for(;250<h.length;)delete r[h.shift()];r[N]||h.push(N);r[N]=J}return J}getStyle(b){return p.getComputedStyle(this.element||\nthis,\"\").getPropertyValue(b)}hasClass(b){return-1!==(\"\"+this.attr(\"class\")).split(\" \").indexOf(b)}hide(){return this.attr({visibility:\"hidden\"})}htmlGetBBox(){return{height:0,width:0,x:0,y:0}}init(b,c){this.element=\"span\"===c?h(c):u.createElementNS(this.SVG_NS,c);this.renderer=b;F(this,\"afterInit\")}on(b,c){const {onEvents:f}=this;if(f[b])f[b]();f[b]=t(this.element,b,c);return this}opacitySetter(b,c,f){this.opacity=b=Number(Number(b).toFixed(3));f.setAttribute(c,b)}removeClass(b){return this.attr(\"class\",\n(\"\"+this.attr(\"class\")).replace(q(b)?new RegExp(`(^| )${b}( |$)`):b,\" \").replace(/ +/g,\" \").trim())}removeTextOutline(){const b=this.element.querySelector(\"tspan.highcharts-text-outline\");b&&this.safeRemoveChild(b)}safeRemoveChild(b){const c=b.parentNode;c&&c.removeChild(b)}setRadialReference(b){const c=this.element.gradient&&this.renderer.gradients[this.element.gradient];this.element.radialReference=b;c&&c.radAttr&&c.animate(this.renderer.getRadialAttr(b,c.radAttr));return this}setTextPath(b,c){c=\nG(!0,{enabled:!0,attributes:{dy:-5,startOffset:\"50%\",textAnchor:\"middle\"}},c);const f=this.renderer.url,d=this.text||this,k=d.textPath,{attributes:n,enabled:A}=c;b=b||k&&k.path;k&&k.undo();b&&A?(c=t(d,\"afterModifyTree\",c=>{if(b&&A){let A=b.attr(\"id\");A||b.attr(\"id\",A=P());var k={x:0,y:0};e(n.dx)&&(k.dx=n.dx,delete n.dx);e(n.dy)&&(k.dy=n.dy,delete n.dy);d.attr(k);this.attr({transform:\"\"});this.box&&(this.box=this.box.destroy());k=c.nodes.slice(0);c.nodes.length=0;c.nodes[0]={tagName:\"textPath\",attributes:E(n,\n{\"text-anchor\":n.textAnchor,href:`${f}#${A}`}),children:k}}}),d.textPath={path:b,undo:c}):(d.attr({dx:0,dy:0}),delete d.textPath);this.added&&(d.textCache=\"\",this.renderer.buildText(d));return this}shadow(b){var c;const {renderer:f}=this,d=G(90===(null===(c=this.parentGroup)||void 0===c?void 0:c.rotation)?{offsetX:-1,offsetY:-1}:{},r(b)?b:{});c=f.shadowDefinition(d);return this.attr({filter:b?`url(${f.url}#${c})`:\"none\"})}show(b=!0){return this.attr({visibility:b?\"inherit\":\"visible\"})}[\"stroke-widthSetter\"](b,\nc,f){this[c]=b;f.setAttribute(c,b)}strokeWidth(){if(!this.renderer.styledMode)return this[\"stroke-width\"]||0;const b=this.getStyle(\"stroke-width\");let f=0,d;b.indexOf(\"px\")===b.length-2?f=c(b):\"\"!==b&&(d=u.createElementNS(l,\"rect\"),m(d,{width:b,\"stroke-width\":0}),this.element.parentNode.appendChild(d),f=d.getBBox().width,d.parentNode.removeChild(d));return f}symbolAttr(b){const c=this;D.symbolCustomAttribs.forEach(function(d){c[d]=f(b[d],c[d])});c.attr({d:c.renderer.symbols[c.symbolName](c.x,c.y,\nc.width,c.height,c)})}textSetter(b){b!==this.textStr&&(delete this.textPxLength,this.textStr=b,this.added&&this.renderer.buildText(this))}titleSetter(b){const c=this.element,d=c.getElementsByTagName(\"title\")[0]||u.createElementNS(this.SVG_NS,\"title\");c.insertBefore?c.insertBefore(d,c.firstChild):c.appendChild(d);d.textContent=String(f(b,\"\")).replace(/<[^>]*>/g,\"\").replace(/&lt;/g,\"<\").replace(/&gt;/g,\">\")}toFront(){const b=this.element;b.parentNode.appendChild(b);return this}translate(b,c){return this.attr({translateX:b,\ntranslateY:c})}updateTransform(){const {element:b,matrix:c,rotation:d=0,scaleX:k,scaleY:n,translateX:q=0,translateY:A=0}=this,g=[\"translate(\"+q+\",\"+A+\")\"];e(c)&&g.push(\"matrix(\"+c.join(\",\")+\")\");d&&g.push(\"rotate(\"+d+\" \"+f(this.rotationOriginX,b.getAttribute(\"x\"),0)+\" \"+f(this.rotationOriginY,b.getAttribute(\"y\")||0)+\")\");(e(k)||e(n))&&g.push(\"scale(\"+f(k,1)+\" \"+f(n,1)+\")\");g.length&&!(this.text||this).textPath&&b.setAttribute(\"transform\",g.join(\" \"))}visibilitySetter(b,c,f){\"inherit\"===b?f.removeAttribute(c):\nthis[c]!==b&&f.setAttribute(c,b);this[c]=b}xGetter(b){\"circle\"===this.element.nodeName&&(\"x\"===b?b=\"cx\":\"y\"===b&&(b=\"cy\"));return this._defaultGetter(b)}zIndexSetter(b,f){var d=this.renderer,k=this.parentGroup;const n=(k||d).element||d.box,q=this.element;d=n===d.box;let A=!1,g;var a=this.added;let r;e(b)?(q.setAttribute(\"data-z-index\",b),b=+b,this[f]===b&&(a=!1)):e(this[f])&&q.removeAttribute(\"data-z-index\");this[f]=b;if(a){(b=this.zIndex)&&k&&(k.handleZ=!0);f=n.childNodes;for(r=f.length-1;0<=r&&\n!A;r--)if(k=f[r],a=k.getAttribute(\"data-z-index\"),g=!e(a),k!==q)if(0>b&&g&&!d&&!r)n.insertBefore(q,f[r]),A=!0;else if(c(a)<=b||g&&(!e(b)||0<=b))n.insertBefore(q,f[r+1]),A=!0;A||(n.insertBefore(q,f[d?3:0]),A=!0)}return A}}D.symbolCustomAttribs=\"anchorX anchorY clockwise end height innerR r start width x y\".split(\" \");D.prototype.strokeSetter=D.prototype.fillSetter;D.prototype.yGetter=D.prototype.xGetter;D.prototype.matrixSetter=D.prototype.rotationOriginXSetter=D.prototype.rotationOriginYSetter=D.prototype.rotationSetter=\nD.prototype.scaleXSetter=D.prototype.scaleYSetter=D.prototype.translateXSetter=D.prototype.translateYSetter=D.prototype.verticalAlignSetter=function(b,c){this[c]=b;this.doTransform=!0};\"\";return D});M(a,\"Core/Renderer/RendererRegistry.js\",[a[\"Core/Globals.js\"]],function(a){var x;(function(x){x.rendererTypes={};let y;x.getRendererType=function(a=y){return x.rendererTypes[a]||x.rendererTypes[y]};x.registerRendererType=function(C,z,H){x.rendererTypes[C]=z;if(!y||H)y=C,a.Renderer=z}})(x||(x={}));return x});\nM(a,\"Core/Renderer/SVG/SVGLabel.js\",[a[\"Core/Renderer/SVG/SVGElement.js\"],a[\"Core/Utilities.js\"]],function(a,y){const {defined:x,extend:L,isNumber:C,merge:z,pick:H,removeEvent:B}=y;class u extends a{constructor(a,l,p,t,m,h,g,e,w,E){super();this.paddingRightSetter=this.paddingLeftSetter=this.paddingSetter;this.init(a,\"g\");this.textStr=l;this.x=p;this.y=t;this.anchorX=h;this.anchorY=g;this.baseline=w;this.className=E;this.addClass(\"button\"===E?\"highcharts-no-tooltip\":\"highcharts-label\");E&&this.addClass(\"highcharts-\"+\nE);this.text=a.text(void 0,0,0,e).attr({zIndex:1});let v;\"string\"===typeof m&&((v=/^url\\((.*?)\\)$/.test(m))||this.renderer.symbols[m])&&(this.symbolKey=m);this.bBox=u.emptyBBox;this.padding=3;this.baselineOffset=0;this.needsBox=a.styledMode||v;this.deferredAttr={};this.alignFactor=0}alignSetter(a){a={left:0,center:.5,right:1}[a];a!==this.alignFactor&&(this.alignFactor=a,this.bBox&&C(this.xSetting)&&this.attr({x:this.xSetting}))}anchorXSetter(a,l){this.anchorX=a;this.boxAttr(l,Math.round(a)-this.getCrispAdjust()-\nthis.xSetting)}anchorYSetter(a,l){this.anchorY=a;this.boxAttr(l,a-this.ySetting)}boxAttr(a,l){this.box?this.box.attr(a,l):this.deferredAttr[a]=l}css(v){if(v){const a={};v=z(v);u.textProps.forEach(l=>{\"undefined\"!==typeof v[l]&&(a[l]=v[l],delete v[l])});this.text.css(a);\"fontSize\"in a||\"fontWeight\"in a?this.updateTextPadding():(\"width\"in a||\"textOverflow\"in a)&&this.updateBoxSize()}return a.prototype.css.call(this,v)}destroy(){B(this.element,\"mouseenter\");B(this.element,\"mouseleave\");this.text&&this.text.destroy();\nthis.box&&(this.box=this.box.destroy());a.prototype.destroy.call(this)}fillSetter(a,l){a&&(this.needsBox=!0);this.fill=a;this.boxAttr(l,a)}getBBox(){this.textStr&&0===this.bBox.width&&0===this.bBox.height&&this.updateBoxSize();const a=this.padding,l=H(this.paddingLeft,a);return{width:this.width,height:this.height,x:this.bBox.x-l,y:this.bBox.y-a}}getCrispAdjust(){return this.renderer.styledMode&&this.box?this.box.strokeWidth()%2/2:(this[\"stroke-width\"]?parseInt(this[\"stroke-width\"],10):0)%2/2}heightSetter(a){this.heightSetting=\na}onAdd(){this.text.add(this);this.attr({text:H(this.textStr,\"\"),x:this.x||0,y:this.y||0});this.box&&x(this.anchorX)&&this.attr({anchorX:this.anchorX,anchorY:this.anchorY})}paddingSetter(a,l){C(a)?a!==this[l]&&(this[l]=a,this.updateTextPadding()):this[l]=void 0}rSetter(a,l){this.boxAttr(l,a)}strokeSetter(a,l){this.stroke=a;this.boxAttr(l,a)}[\"stroke-widthSetter\"](a,l){a&&(this.needsBox=!0);this[\"stroke-width\"]=a;this.boxAttr(l,a)}[\"text-alignSetter\"](a){this.textAlign=a}textSetter(a){\"undefined\"!==\ntypeof a&&this.text.attr({text:a});this.updateTextPadding()}updateBoxSize(){var a=this.text;const l={},p=this.padding,t=this.bBox=C(this.widthSetting)&&C(this.heightSetting)&&!this.textAlign||!x(a.textStr)?u.emptyBBox:a.getBBox();this.width=this.getPaddedWidth();this.height=(this.heightSetting||t.height||0)+2*p;const m=this.renderer.fontMetrics(a);this.baselineOffset=p+Math.min((this.text.firstLineMetrics||m).b,t.height||Infinity);this.heightSetting&&(this.baselineOffset+=(this.heightSetting-m.h)/\n2);this.needsBox&&!a.textPath&&(this.box||(a=this.box=this.symbolKey?this.renderer.symbol(this.symbolKey):this.renderer.rect(),a.addClass((\"button\"===this.className?\"\":\"highcharts-label-box\")+(this.className?\" highcharts-\"+this.className+\"-box\":\"\")),a.add(this)),a=this.getCrispAdjust(),l.x=a,l.y=(this.baseline?-this.baselineOffset:0)+a,l.width=Math.round(this.width),l.height=Math.round(this.height),this.box.attr(L(l,this.deferredAttr)),this.deferredAttr={})}updateTextPadding(){const a=this.text;if(!a.textPath){this.updateBoxSize();\nconst l=this.baseline?0:this.baselineOffset;let p=H(this.paddingLeft,this.padding);x(this.widthSetting)&&this.bBox&&(\"center\"===this.textAlign||\"right\"===this.textAlign)&&(p+={center:.5,right:1}[this.textAlign]*(this.widthSetting-this.bBox.width));if(p!==a.x||l!==a.y)a.attr(\"x\",p),a.hasBoxWidthChanged&&(this.bBox=a.getBBox(!0)),\"undefined\"!==typeof l&&a.attr(\"y\",l);a.x=p;a.y=l}}widthSetter(a){this.widthSetting=C(a)?a:void 0}getPaddedWidth(){var a=this.padding;const l=H(this.paddingLeft,a);a=H(this.paddingRight,\na);return(this.widthSetting||this.bBox.width||0)+l+a}xSetter(a){this.x=a;this.alignFactor&&(a-=this.alignFactor*this.getPaddedWidth(),this[\"forceAnimate:x\"]=!0);this.xSetting=Math.round(a);this.attr(\"translateX\",this.xSetting)}ySetter(a){this.ySetting=this.y=Math.round(a);this.attr(\"translateY\",this.ySetting)}}u.emptyBBox={width:0,height:0,x:0,y:0};u.textProps=\"color direction fontFamily fontSize fontStyle fontWeight lineHeight textAlign textDecoration textOutline textOverflow whiteSpace width\".split(\" \");\nreturn u});M(a,\"Core/Renderer/SVG/Symbols.js\",[a[\"Core/Utilities.js\"]],function(a){function x(a,u,v,l,p){const t=[];if(p){const m=p.start||0,h=H(p.r,v);v=H(p.r,l||v);l=(p.end||0)-.001;const g=p.innerR,e=H(p.open,.001>Math.abs((p.end||0)-m-2*Math.PI)),w=Math.cos(m),E=Math.sin(m),F=Math.cos(l),d=Math.sin(l),k=H(p.longArc,.001>l-m-Math.PI?0:1);let r=[\"A\",h,v,0,k,H(p.clockwise,1),a+h*F,u+v*d];r.params={start:m,end:l,cx:a,cy:u};t.push([\"M\",a+h*w,u+v*E],r);C(g)&&(r=[\"A\",g,g,0,k,C(p.clockwise)?1-p.clockwise:\n0,a+g*w,u+g*E],r.params={start:l,end:m,cx:a,cy:u},t.push(e?[\"M\",a+g*F,u+g*d]:[\"L\",a+g*F,u+g*d],r));e||t.push([\"Z\"])}return t}function I(a,u,v,l,p){return p&&p.r?L(a,u,v,l,p):[[\"M\",a,u],[\"L\",a+v,u],[\"L\",a+v,u+l],[\"L\",a,u+l],[\"Z\"]]}function L(a,u,v,l,p){p=(null===p||void 0===p?void 0:p.r)||0;return[[\"M\",a+p,u],[\"L\",a+v-p,u],[\"A\",p,p,0,0,1,a+v,u+p],[\"L\",a+v,u+l-p],[\"A\",p,p,0,0,1,a+v-p,u+l],[\"L\",a+p,u+l],[\"A\",p,p,0,0,1,a,u+l-p],[\"L\",a,u+p],[\"A\",p,p,0,0,1,a+p,u],[\"Z\"]]}const {defined:C,isNumber:z,pick:H}=\na;return{arc:x,callout:function(a,u,v,l,p){const t=Math.min(p&&p.r||0,v,l),m=t+6,h=p&&p.anchorX;p=p&&p.anchorY||0;const g=L(a,u,v,l,{r:t});if(!z(h))return g;a+h>=v?p>u+m&&p<u+l-m?g.splice(3,1,[\"L\",a+v,p-6],[\"L\",a+v+6,p],[\"L\",a+v,p+6],[\"L\",a+v,u+l-t]):g.splice(3,1,[\"L\",a+v,l/2],[\"L\",h,p],[\"L\",a+v,l/2],[\"L\",a+v,u+l-t]):0>=a+h?p>u+m&&p<u+l-m?g.splice(7,1,[\"L\",a,p+6],[\"L\",a-6,p],[\"L\",a,p-6],[\"L\",a,u+t]):g.splice(7,1,[\"L\",a,l/2],[\"L\",h,p],[\"L\",a,l/2],[\"L\",a,u+t]):p&&p>l&&h>a+m&&h<a+v-m?g.splice(5,1,[\"L\",\nh+6,u+l],[\"L\",h,u+l+6],[\"L\",h-6,u+l],[\"L\",a+t,u+l]):p&&0>p&&h>a+m&&h<a+v-m&&g.splice(1,1,[\"L\",h-6,u],[\"L\",h,u-6],[\"L\",h+6,u],[\"L\",v-t,u]);return g},circle:function(a,u,v,l){return x(a+v/2,u+l/2,v/2,l/2,{start:.5*Math.PI,end:2.5*Math.PI,open:!1})},diamond:function(a,u,v,l){return[[\"M\",a+v/2,u],[\"L\",a+v,u+l/2],[\"L\",a+v/2,u+l],[\"L\",a,u+l/2],[\"Z\"]]},rect:I,roundedRect:L,square:I,triangle:function(a,u,v,l){return[[\"M\",a+v/2,u],[\"L\",a+v,u+l],[\"L\",a,u+l],[\"Z\"]]},\"triangle-down\":function(a,u,v,l){return[[\"M\",\na,u],[\"L\",a+v,u],[\"L\",a+v/2,u+l],[\"Z\"]]}}});M(a,\"Core/Renderer/SVG/TextBuilder.js\",[a[\"Core/Renderer/HTML/AST.js\"],a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {doc:x,SVG_NS:C,win:z}=y,{attr:H,extend:B,fireEvent:u,isString:v,objectEach:l,pick:p}=I;class t{constructor(a){const h=a.styles;this.renderer=a.renderer;this.svgElement=a;this.width=a.textWidth;this.textLineHeight=h&&h.lineHeight;this.textOutline=h&&h.textOutline;this.ellipsis=!(!h||\"ellipsis\"!==h.textOverflow);this.noWrap=\n!(!h||\"nowrap\"!==h.whiteSpace)}buildSVG(){const m=this.svgElement,h=m.element;var g=m.renderer,e=p(m.textStr,\"\").toString();const w=-1!==e.indexOf(\"<\"),l=h.childNodes;g=!m.added&&g.box;const t=/<br.*?>/g;var d=[e,this.ellipsis,this.noWrap,this.textLineHeight,this.textOutline,m.getStyle(\"font-size\"),this.width].join();if(d!==m.textCache){m.textCache=d;delete m.actualWidth;for(d=l.length;d--;)h.removeChild(l[d]);w||this.ellipsis||this.width||m.textPath||-1!==e.indexOf(\" \")&&(!this.noWrap||t.test(e))?\n\"\"!==e&&(g&&g.appendChild(h),e=new a(e),this.modifyTree(e.nodes),e.addToDOM(h),this.modifyDOM(),this.ellipsis&&-1!==(h.textContent||\"\").indexOf(\"\\u2026\")&&m.attr(\"title\",this.unescapeEntities(m.textStr||\"\",[\"&lt;\",\"&gt;\"])),g&&g.removeChild(h)):h.appendChild(x.createTextNode(this.unescapeEntities(e)));v(this.textOutline)&&m.applyTextOutline&&m.applyTextOutline(this.textOutline)}}modifyDOM(){const a=this.svgElement,h=H(a.element,\"x\");a.firstLineMetrics=void 0;let g;for(;g=a.element.firstChild;)if(/^[\\s\\u200B]*$/.test(g.textContent||\n\" \"))a.element.removeChild(g);else break;[].forEach.call(a.element.querySelectorAll(\"tspan.highcharts-br\"),(e,d)=>{e.nextSibling&&e.previousSibling&&(0===d&&1===e.previousSibling.nodeType&&(a.firstLineMetrics=a.renderer.fontMetrics(e.previousSibling)),H(e,{dy:this.getLineHeight(e.nextSibling),x:h}))});const e=this.width||0;if(e){var w=(g,d)=>{var k=g.textContent||\"\";const r=k.replace(/([^\\^])-/g,\"$1- \").split(\" \");var q=!this.noWrap&&(1<r.length||1<a.element.childNodes.length);const m=this.getLineHeight(d);\nlet b=0,f=a.actualWidth;if(this.ellipsis)k&&this.truncate(g,k,void 0,0,Math.max(0,e-.8*m),(b,f)=>b.substring(0,f)+\"\\u2026\");else if(q){k=[];for(q=[];d.firstChild&&d.firstChild!==g;)q.push(d.firstChild),d.removeChild(d.firstChild);for(;r.length;)r.length&&!this.noWrap&&0<b&&(k.push(g.textContent||\"\"),g.textContent=r.join(\" \").replace(/- /g,\"-\")),this.truncate(g,void 0,r,0===b?f||0:0,e,(b,f)=>r.slice(0,f).join(\" \").replace(/- /g,\"-\")),f=a.actualWidth,b++;q.forEach(b=>{d.insertBefore(b,g)});k.forEach(b=>\n{d.insertBefore(x.createTextNode(b),g);b=x.createElementNS(C,\"tspan\");b.textContent=\"\\u200b\";H(b,{dy:m,x:h});d.insertBefore(b,g)})}},l=e=>{[].slice.call(e.childNodes).forEach(d=>{d.nodeType===z.Node.TEXT_NODE?w(d,e):(-1!==d.className.baseVal.indexOf(\"highcharts-br\")&&(a.actualWidth=0),l(d))})};l(a.element)}}getLineHeight(a){a=a.nodeType===z.Node.TEXT_NODE?a.parentElement:a;return this.textLineHeight?parseInt(this.textLineHeight.toString(),10):this.renderer.fontMetrics(a||this.svgElement.element).h}modifyTree(a){const h=\n(g,e)=>{const {attributes:m={},children:l,style:p={},tagName:d}=g,k=this.renderer.styledMode;if(\"b\"===d||\"strong\"===d)k?m[\"class\"]=\"highcharts-strong\":p.fontWeight=\"bold\";else if(\"i\"===d||\"em\"===d)k?m[\"class\"]=\"highcharts-emphasized\":p.fontStyle=\"italic\";p&&p.color&&(p.fill=p.color);\"br\"===d?(m[\"class\"]=\"highcharts-br\",g.textContent=\"\\u200b\",(e=a[e+1])&&e.textContent&&(e.textContent=e.textContent.replace(/^ +/gm,\"\"))):\"a\"===d&&l&&l.some(d=>\"#text\"===d.tagName)&&(g.children=[{children:l,tagName:\"tspan\"}]);\n\"#text\"!==d&&\"a\"!==d&&(g.tagName=\"tspan\");B(g,{attributes:m,style:p});l&&l.filter(d=>\"#text\"!==d.tagName).forEach(h)};a.forEach(h);u(this.svgElement,\"afterModifyTree\",{nodes:a})}truncate(a,h,g,e,l,p){const m=this.svgElement,{rotation:d}=m,k=[];let r=g?1:0,q=(h||g||\"\").length,w=q,b,f;const c=function(b,c){b=c||b;if((c=a.parentNode)&&\"undefined\"===typeof k[b]&&c.getSubStringLength)try{k[b]=e+c.getSubStringLength(0,g?b+1:b)}catch(D){\"\"}return k[b]};m.rotation=0;f=c(a.textContent.length);if(e+f>l){for(;r<=\nq;)w=Math.ceil((r+q)/2),g&&(b=p(g,w)),f=c(w,b&&b.length-1),r===q?r=q+1:f>l?q=w-1:r=w;0===q?a.textContent=\"\":h&&q===h.length-1||(a.textContent=b||p(h||g,w))}g&&g.splice(0,w);m.actualWidth=f;m.rotation=d}unescapeEntities(a,h){l(this.renderer.escapes,function(g,e){h&&-1!==h.indexOf(g)||(a=a.toString().replace(new RegExp(g,\"g\"),e))});return a}}return t});M(a,\"Core/Renderer/SVG/SVGRenderer.js\",[a[\"Core/Renderer/HTML/AST.js\"],a[\"Core/Color/Color.js\"],a[\"Core/Globals.js\"],a[\"Core/Renderer/RendererRegistry.js\"],\na[\"Core/Renderer/SVG/SVGElement.js\"],a[\"Core/Renderer/SVG/SVGLabel.js\"],a[\"Core/Renderer/SVG/Symbols.js\"],a[\"Core/Renderer/SVG/TextBuilder.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C,z,H,B,u){const {charts:v,deg2rad:l,doc:p,isFirefox:t,isMS:m,isWebKit:h,noop:g,SVG_NS:e,symbolSizes:w,win:E}=I,{addEvent:F,attr:d,createElement:k,css:r,defined:q,destroyObjectProperties:G,extend:b,isArray:f,isNumber:c,isObject:n,isString:P,merge:D,pick:K,pInt:x,uniqueKey:T}=u;let Z;class V{constructor(b,c,f,d,a,k,\nn){this.width=this.url=this.style=this.imgCount=this.height=this.gradients=this.globalAnimation=this.defs=this.chartIndex=this.cacheKeys=this.cache=this.boxWrapper=this.box=this.alignedObjects=void 0;this.init(b,c,f,d,a,k,n)}init(b,c,f,a,k,n,J){const A=this.createElement(\"svg\").attr({version:\"1.1\",\"class\":\"highcharts-root\"}),e=A.element;J||A.css(this.getStyle(a));b.appendChild(e);d(b,\"dir\",\"ltr\");-1===b.innerHTML.indexOf(\"xmlns\")&&d(e,\"xmlns\",this.SVG_NS);this.box=e;this.boxWrapper=A;this.alignedObjects=\n[];this.url=this.getReferenceURL();this.createElement(\"desc\").add().element.appendChild(p.createTextNode(\"Created with Highcharts 11.1.0\"));this.defs=this.createElement(\"defs\").add();this.allowHTML=n;this.forExport=k;this.styledMode=J;this.gradients={};this.cache={};this.cacheKeys=[];this.imgCount=0;this.rootFontSize=A.getStyle(\"font-size\");this.setSize(c,f,!1);let q;t&&b.getBoundingClientRect&&(c=function(){r(b,{left:0,top:0});q=b.getBoundingClientRect();r(b,{left:Math.ceil(q.left)-q.left+\"px\",top:Math.ceil(q.top)-\nq.top+\"px\"})},c(),this.unSubPixelFix=F(E,\"resize\",c))}definition(b){return(new a([b])).addToDOM(this.defs.element)}getReferenceURL(){if((t||h)&&p.getElementsByTagName(\"base\").length){if(!q(Z)){var b=T();b=(new a([{tagName:\"svg\",attributes:{width:8,height:8},children:[{tagName:\"defs\",children:[{tagName:\"clipPath\",attributes:{id:b},children:[{tagName:\"rect\",attributes:{width:4,height:4}}]}]},{tagName:\"rect\",attributes:{id:\"hitme\",width:8,height:8,\"clip-path\":`url(#${b})`,fill:\"rgba(0,0,0,0.001)\"}}]}])).addToDOM(p.body);\nr(b,{position:\"fixed\",top:0,left:0,zIndex:9E5});const c=p.elementFromPoint(6,6);Z=\"hitme\"===(c&&c.id);p.body.removeChild(b)}if(Z)return E.location.href.split(\"#\")[0].replace(/<[^>]*>/g,\"\").replace(/([\\('\\)])/g,\"\\\\$1\").replace(/ /g,\"%20\")}return\"\"}getStyle(c){return this.style=b({fontFamily:\"Helvetica, Arial, sans-serif\",fontSize:\"1rem\"},c)}setStyle(b){this.boxWrapper.css(this.getStyle(b))}isHidden(){return!this.boxWrapper.getBBox().width}destroy(){const b=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();\nG(this.gradients||{});this.gradients=null;this.defs=b.destroy();this.unSubPixelFix&&this.unSubPixelFix();return this.alignedObjects=null}createElement(b){const c=new this.Element;c.init(this,b);return c}getRadialAttr(b,c){return{cx:b[0]-b[2]/2+(c.cx||0)*b[2],cy:b[1]-b[2]/2+(c.cy||0)*b[2],r:(c.r||0)*b[2]}}shadowDefinition(b){const c=[`highcharts-drop-shadow-${this.chartIndex}`,...Object.keys(b).map(c=>b[c])].join(\"-\").replace(/[^a-z0-9\\-]/g,\"\"),f=D({color:\"#000000\",offsetX:1,offsetY:1,opacity:.15,\nwidth:5},b);this.defs.element.querySelector(`#${c}`)||this.definition({tagName:\"filter\",attributes:{id:c},children:[{tagName:\"feDropShadow\",attributes:{dx:f.offsetX,dy:f.offsetY,\"flood-color\":f.color,\"flood-opacity\":Math.min(5*f.opacity,1),stdDeviation:f.width/2}}]});return c}buildText(b){(new B(b)).buildSVG()}getContrast(b){b=y.parse(b).rgba.map(b=>{b/=255;return.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4)});b=.2126*b[0]+.7152*b[1]+.0722*b[2];return 1.05/(b+.05)>(b+.05)/.05?\"#FFFFFF\":\"#000000\"}button(c,\nf,d,k,e={},q,J,g,r,h){const A=this.label(c,f,d,r,void 0,void 0,h,void 0,\"button\"),O=this.styledMode;c=e.states||{};let N=0;e=D(e);delete e.states;const l=D({color:\"#333333\",cursor:\"pointer\",fontSize:\"0.8em\",fontWeight:\"normal\"},e.style);delete e.style;let w=a.filterUserAttributes(e);A.attr(D({padding:8,r:2},w));let p,G,R;O||(w=D({fill:\"#f7f7f7\",stroke:\"#cccccc\",\"stroke-width\":1},w),q=D(w,{fill:\"#e6e6e6\"},a.filterUserAttributes(q||c.hover||{})),p=q.style,delete q.style,J=D(w,{fill:\"#e6e9ff\",style:{color:\"#000000\",\nfontWeight:\"bold\"}},a.filterUserAttributes(J||c.select||{})),G=J.style,delete J.style,g=D(w,{style:{color:\"#cccccc\"}},a.filterUserAttributes(g||c.disabled||{})),R=g.style,delete g.style);F(A.element,m?\"mouseover\":\"mouseenter\",function(){3!==N&&A.setState(1)});F(A.element,m?\"mouseout\":\"mouseleave\",function(){3!==N&&A.setState(N)});A.setState=function(b){1!==b&&(A.state=N=b);A.removeClass(/highcharts-button-(normal|hover|pressed|disabled)/).addClass(\"highcharts-button-\"+[\"normal\",\"hover\",\"pressed\",\n\"disabled\"][b||0]);O||(A.attr([w,q,J,g][b||0]),b=[l,p,G,R][b||0],n(b)&&A.css(b))};O||(A.attr(w).css(b({cursor:\"default\"},l)),h&&A.text.css({pointerEvents:\"none\"}));return A.on(\"touchstart\",b=>b.stopPropagation()).on(\"click\",function(b){3!==N&&k.call(A,b)})}crispLine(b,c,f=\"round\"){const d=b[0],a=b[1];q(d[1])&&d[1]===a[1]&&(d[1]=a[1]=Math[f](d[1])-c%2/2);q(d[2])&&d[2]===a[2]&&(d[2]=a[2]=Math[f](d[2])+c%2/2);return b}path(c){const d=this.styledMode?{}:{fill:\"none\"};f(c)?d.d=c:n(c)&&b(d,c);return this.createElement(\"path\").attr(d)}circle(b,\nc,f){b=n(b)?b:\"undefined\"===typeof b?{}:{x:b,y:c,r:f};c=this.createElement(\"circle\");c.xSetter=c.ySetter=function(b,c,f){f.setAttribute(\"c\"+c,b)};return c.attr(b)}arc(b,c,f,d,a,k){n(b)?(d=b,c=d.y,f=d.r,b=d.x):d={innerR:d,start:a,end:k};b=this.symbol(\"arc\",b,c,f,f,d);b.r=f;return b}rect(c,f,a,k,e,q){c=n(c)?c:\"undefined\"===typeof c?{}:{x:c,y:f,r:e,width:Math.max(a||0,0),height:Math.max(k||0,0)};const A=this.createElement(\"rect\");this.styledMode||(\"undefined\"!==typeof q&&(c[\"stroke-width\"]=q,b(c,A.crisp(c))),\nc.fill=\"none\");A.rSetter=function(b,c,f){A.r=b;d(f,{rx:b,ry:b})};A.rGetter=function(){return A.r||0};return A.attr(c)}roundedRect(b){return this.symbol(\"roundedRect\").attr(b)}setSize(b,c,f){this.width=b;this.height=c;this.boxWrapper.animate({width:b,height:c},{step:function(){this.attr({viewBox:\"0 0 \"+this.attr(\"width\")+\" \"+this.attr(\"height\")})},duration:K(f,!0)?void 0:0});this.alignElements()}g(b){const c=this.createElement(\"g\");return b?c.attr({\"class\":\"highcharts-\"+b}):c}image(b,f,d,a,k,n){const A=\n{preserveAspectRatio:\"none\"};c(f)&&(A.x=f);c(d)&&(A.y=d);c(a)&&(A.width=a);c(k)&&(A.height=k);const e=this.createElement(\"image\").attr(A);f=function(c){e.attr({href:b});n.call(e,c)};n?(e.attr({href:\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\"}),d=new E.Image,F(d,\"load\",f),d.src=b,d.complete&&f({})):e.attr({href:b});return e}symbol(c,f,a,n,e,g){const A=this,h=/^url\\((.*?)\\)$/,O=h.test(c),m=!O&&(this.symbols[c]?c:\"circle\"),l=m&&this.symbols[m];let D,G,P,t;if(l)\"number\"===\ntypeof f&&(G=l.call(this.symbols,Math.round(f||0),Math.round(a||0),n||0,e||0,g)),D=this.path(G),A.styledMode||D.attr(\"fill\",\"none\"),b(D,{symbolName:m||void 0,x:f,y:a,width:n,height:e}),g&&b(D,g);else if(O){P=c.match(h)[1];const b=D=this.image(P);b.imgwidth=K(g&&g.width,w[P]&&w[P].width);b.imgheight=K(g&&g.height,w[P]&&w[P].height);t=b=>b.attr({width:b.width,height:b.height});[\"width\",\"height\"].forEach(function(c){b[c+\"Setter\"]=function(b,c){this[c]=b;const {alignByTranslate:f,element:a,width:k,height:A,\nimgwidth:n,imgheight:e}=this;b=this[\"img\"+c];if(q(b)){let J=1;g&&\"within\"===g.backgroundSize&&k&&A?(J=Math.min(k/n,A/e),d(a,{width:Math.round(n*J),height:Math.round(e*J)})):a&&a.setAttribute(c,b);f||this.translate(((k||0)-n*J)/2,((A||0)-e*J)/2)}}});q(f)&&b.attr({x:f,y:a});b.isImg=!0;q(b.imgwidth)&&q(b.imgheight)?t(b):(b.attr({width:0,height:0}),k(\"img\",{onload:function(){const c=v[A.chartIndex];0===this.width&&(r(this,{position:\"absolute\",top:\"-999em\"}),p.body.appendChild(this));w[P]={width:this.width,\nheight:this.height};b.imgwidth=this.width;b.imgheight=this.height;b.element&&t(b);this.parentNode&&this.parentNode.removeChild(this);A.imgCount--;if(!A.imgCount&&c&&!c.hasLoaded)c.onload()},src:P}),this.imgCount++)}return D}clipRect(b,c,f,d){const a=T()+\"-\",k=this.createElement(\"clipPath\").attr({id:a}).add(this.defs);b=this.rect(b,c,f,d,0).add(k);b.id=a;b.clipPath=k;b.count=0;return b}text(b,c,f,d){const a={};if(d&&(this.allowHTML||!this.forExport))return this.html(b,c,f);a.x=Math.round(c||0);f&&\n(a.y=Math.round(f));q(b)&&(a.text=b);b=this.createElement(\"text\").attr(a);if(!d||this.forExport&&!this.allowHTML)b.xSetter=function(b,c,f){const d=f.getElementsByTagName(\"tspan\"),a=f.getAttribute(c);for(let f=0,k;f<d.length;f++)k=d[f],k.getAttribute(c)===a&&k.setAttribute(c,b);f.setAttribute(c,b)};return b}fontMetrics(b){b=x(C.prototype.getStyle.call(b,\"font-size\")||0);const c=24>b?b+3:Math.round(1.2*b);return{h:c,b:Math.round(.8*c),f:b}}rotCorr(b,c,f){let d=b;c&&f&&(d=Math.max(d*Math.cos(c*l),4));\nreturn{x:-b/3*Math.sin(c*l),y:d}}pathToSegments(b){const f=[],d=[],a={A:8,C:7,H:2,L:3,M:3,Q:5,S:5,T:3,V:2};for(let k=0;k<b.length;k++)P(d[0])&&c(b[k])&&d.length===a[d[0].toUpperCase()]&&b.splice(k,0,d[0].replace(\"M\",\"L\").replace(\"m\",\"l\")),\"string\"===typeof b[k]&&(d.length&&f.push(d.slice(0)),d.length=0),d.push(b[k]);f.push(d.slice(0));return f}label(b,c,f,d,a,k,n,e,q){return new z(this,b,c,f,d,a,k,n,e,q)}alignElements(){this.alignedObjects.forEach(b=>b.align())}}b(V.prototype,{Element:C,SVG_NS:e,\nescapes:{\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",\"'\":\"&#39;\",'\"':\"&quot;\"},symbols:H,draw:g});L.registerRendererType(\"svg\",V,!0);\"\";return V});M(a,\"Core/Renderer/HTML/HTMLElement.js\",[a[\"Core/Globals.js\"],a[\"Core/Renderer/SVG/SVGElement.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {isFirefox:x,isMS:C,isWebKit:z,win:H}=a,{css:B,defined:u,extend:v,pick:l,pInt:p}=I,t=[];class m extends y{static compose(a){if(I.pushUnique(t,a)){const g=m.prototype,e=a.prototype;e.getSpanCorrection=g.getSpanCorrection;\ne.htmlCss=g.htmlCss;e.htmlGetBBox=g.htmlGetBBox;e.htmlUpdateTransform=g.htmlUpdateTransform;e.setSpanRotation=g.setSpanRotation}return a}getSpanCorrection(a,g,e){this.xCorr=-a*e;this.yCorr=-g}htmlCss(a){const g=\"SPAN\"===this.element.tagName&&a&&\"width\"in a,e=l(g&&a.width,void 0);let h;g&&(delete a.width,this.textWidth=e,h=!0);a&&\"ellipsis\"===a.textOverflow&&(a.whiteSpace=\"nowrap\",a.overflow=\"hidden\");this.styles=v(this.styles,a);B(this.element,a);h&&this.htmlUpdateTransform();return this}htmlGetBBox(){const a=\nthis.element;return{x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}htmlUpdateTransform(){if(this.added){var a=this.renderer,g=this.element,e=this.x||0,m=this.y||0,l=this.textAlign||\"left\",t={left:0,center:.5,right:1}[l],d=this.styles,k=d&&d.whiteSpace;B(g,{marginLeft:this.translateX||0,marginTop:this.translateY||0});if(\"SPAN\"===g.tagName){d=this.rotation;const q=this.textWidth&&p(this.textWidth),h=[d,l,g.innerHTML,this.textWidth,this.textAlign].join();let b=!1;if(q!==this.oldTextWidth){if(this.textPxLength)var r=\nthis.textPxLength;else B(g,{width:\"\",whiteSpace:k||\"nowrap\"}),r=g.offsetWidth;(q>this.oldTextWidth||r>q)&&(/[ \\-]/.test(g.textContent||g.innerText)||\"ellipsis\"===g.style.textOverflow)&&(B(g,{width:r>q||d?q+\"px\":\"auto\",display:\"block\",whiteSpace:k||\"normal\"}),this.oldTextWidth=q,b=!0)}this.hasBoxWidthChanged=b;h!==this.cTT&&(a=a.fontMetrics(g).b,!u(d)||d===(this.oldRotation||0)&&l===this.oldAlign||this.setSpanRotation(d,t,a),this.getSpanCorrection(!u(d)&&this.textPxLength||g.offsetWidth,a,t,d,l));\nB(g,{left:e+(this.xCorr||0)+\"px\",top:m+(this.yCorr||0)+\"px\"});this.cTT=h;this.oldRotation=d;this.oldAlign=l}}else this.alignOnAdd=!0}setSpanRotation(a,g,e){const h={},m=C&&!/Edge/.test(H.navigator.userAgent)?\"-ms-transform\":z?\"-webkit-transform\":x?\"MozTransform\":H.opera?\"-o-transform\":void 0;m&&(h[m]=h.transform=\"rotate(\"+a+\"deg)\",h[m+(x?\"Origin\":\"-origin\")]=h.transformOrigin=100*g+\"% \"+e+\"px\",B(this.element,h))}}return m});M(a,\"Core/Renderer/HTML/HTMLRenderer.js\",[a[\"Core/Renderer/HTML/AST.js\"],\na[\"Core/Renderer/SVG/SVGElement.js\"],a[\"Core/Renderer/SVG/SVGRenderer.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L){const {attr:x,createElement:z,extend:H,pick:B}=L,u=[];class v extends I{static compose(a){L.pushUnique(u,a)&&(a.prototype.html=v.prototype.html);return a}html(l,p,t){const m=this.createElement(\"span\"),h=m.element,g=m.renderer,e=function(a,e){[\"opacity\",\"visibility\"].forEach(function(g){a[g+\"Setter\"]=function(d,k,r){const q=a.div?a.div.style:e;y.prototype[g+\"Setter\"].call(this,d,k,r);\nq&&(q[k]=d)}});a.addedSetters=!0};m.textSetter=function(e){e!==this.textStr&&(delete this.bBox,delete this.oldTextWidth,a.setElementHTML(this.element,B(e,\"\")),this.textStr=e,m.doTransform=!0)};e(m,m.element.style);m.xSetter=m.ySetter=m.alignSetter=m.rotationSetter=function(a,e){\"align\"===e?m.alignValue=m.textAlign=a:m[e]=a;m.doTransform=!0};m.afterSetters=function(){this.doTransform&&(this.htmlUpdateTransform(),this.doTransform=!1)};m.attr({text:l,x:Math.round(p),y:Math.round(t)}).css({position:\"absolute\"});\ng.styledMode||m.css({fontFamily:this.style.fontFamily,fontSize:this.style.fontSize});h.style.whiteSpace=\"nowrap\";m.css=m.htmlCss;m.add=function(a){const l=g.box.parentNode,w=[];let d;if(this.parentGroup=a){if(d=a.div,!d){for(;a;)w.push(a),a=a.parentGroup;w.reverse().forEach(function(a){function k(f,c){a[c]=f;\"translateX\"===c?b.left=f+\"px\":b.top=f+\"px\";a.doTransform=!0}const q=x(a.element,\"class\"),g=a.styles||{};d=a.div=a.div||z(\"div\",q?{className:q}:void 0,{position:\"absolute\",left:(a.translateX||\n0)+\"px\",top:(a.translateY||0)+\"px\",display:a.display,opacity:a.opacity,visibility:a.visibility},d||l);const b=d.style;H(a,{classSetter:function(b){return function(c){this.element.setAttribute(\"class\",c);b.className=c}}(d),css:function(f){m.css.call(a,f);[\"cursor\",\"pointerEvents\"].forEach(c=>{f[c]&&(b[c]=f[c])});return a},on:function(){w[0].div&&m.on.apply({element:w[0].div,onEvents:a.onEvents},arguments);return a},translateXSetter:k,translateYSetter:k});a.addedSetters||e(a);a.css(g)})}}else d=l;d.appendChild(h);\nm.added=!0;m.alignOnAdd&&m.htmlUpdateTransform();return m};return m}}return v});M(a,\"Core/Axis/AxisDefaults.js\",[],function(){var a;(function(a){a.defaultXAxisOptions={alignTicks:!0,allowDecimals:void 0,panningEnabled:!0,zIndex:2,zoomEnabled:!0,dateTimeLabelFormats:{millisecond:{main:\"%H:%M:%S.%L\",range:!1},second:{main:\"%H:%M:%S\",range:!1},minute:{main:\"%H:%M\",range:!1},hour:{main:\"%H:%M\",range:!1},day:{main:\"%e %b\"},week:{main:\"%e %b\"},month:{main:\"%b '%y\"},year:{main:\"%Y\"}},endOnTick:!1,gridLineDashStyle:\"Solid\",\ngridZIndex:1,labels:{autoRotation:void 0,autoRotationLimit:80,distance:15,enabled:!0,indentation:10,overflow:\"justify\",padding:5,reserveSpace:void 0,rotation:void 0,staggerLines:0,step:0,useHTML:!1,zIndex:7,style:{color:\"#333333\",cursor:\"default\",fontSize:\"0.8em\"}},maxPadding:.01,minorGridLineDashStyle:\"Solid\",minorTickLength:2,minorTickPosition:\"outside\",minorTicksPerMajor:5,minPadding:.01,offset:void 0,opposite:!1,reversed:void 0,reversedStacks:!1,showEmpty:!0,showFirstLabel:!0,showLastLabel:!0,\nstartOfWeek:1,startOnTick:!1,tickLength:10,tickPixelInterval:100,tickmarkPlacement:\"between\",tickPosition:\"outside\",title:{align:\"middle\",rotation:0,useHTML:!1,x:0,y:0,style:{color:\"#666666\",fontSize:\"0.8em\"}},type:\"linear\",uniqueNames:!0,visible:!0,minorGridLineColor:\"#f2f2f2\",minorGridLineWidth:1,minorTickColor:\"#999999\",lineColor:\"#333333\",lineWidth:1,gridLineColor:\"#e6e6e6\",gridLineWidth:void 0,tickColor:\"#333333\"};a.defaultYAxisOptions={reversedStacks:!0,endOnTick:!0,maxPadding:.05,minPadding:.05,\ntickPixelInterval:72,showLastLabel:!0,labels:{x:void 0},startOnTick:!0,title:{rotation:270,text:\"Values\"},stackLabels:{animation:{},allowOverlap:!1,enabled:!1,crop:!0,overflow:\"justify\",formatter:function(){const {numberFormatter:a}=this.axis.chart;return a(this.total||0,-1)},style:{color:\"#000000\",fontSize:\"0.7em\",fontWeight:\"bold\",textOutline:\"1px contrast\"}},gridLineWidth:1,lineWidth:0};a.defaultLeftAxisOptions={title:{rotation:270}};a.defaultRightAxisOptions={title:{rotation:90}};a.defaultBottomAxisOptions=\n{labels:{autoRotation:[-45]},margin:15,title:{rotation:0}};a.defaultTopAxisOptions={labels:{autoRotation:[-45]},margin:15,title:{rotation:0}}})(a||(a={}));return a});M(a,\"Core/Foundation.js\",[a[\"Core/Utilities.js\"]],function(a){const {addEvent:x,isFunction:I,objectEach:L,removeEvent:C}=a;var z;(function(a){a.registerEventOptions=function(a,u){a.eventOptions=a.eventOptions||{};L(u.events,function(v,l){a.eventOptions[l]!==v&&(a.eventOptions[l]&&(C(a,l,a.eventOptions[l]),delete a.eventOptions[l]),I(v)&&\n(a.eventOptions[l]=v,x(a,l,v,{order:0})))})}})(z||(z={}));return z});M(a,\"Core/Axis/Tick.js\",[a[\"Core/Templating.js\"],a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {deg2rad:x}=y,{clamp:C,correctFloat:z,defined:H,destroyObjectProperties:B,extend:u,fireEvent:v,isNumber:l,merge:p,objectEach:t,pick:m}=I;class h{constructor(a,e,h,m,l){this.isNewLabel=this.isNew=!0;this.axis=a;this.pos=e;this.type=h||\"\";this.parameters=l||{};this.tickmarkOffset=this.parameters.tickmarkOffset;this.options=\nthis.parameters.options;v(this,\"init\");h||m||this.addLabel()}addLabel(){const g=this,e=g.axis;var h=e.options;const p=e.chart;var t=e.categories;const d=e.logarithmic,k=e.names,r=g.pos,q=m(g.options&&g.options.labels,h.labels);var G=e.tickPositions;const b=r===G[0],f=r===G[G.length-1],c=(!q.step||1===q.step)&&1===e.tickInterval;G=G.info;let n=g.label,P,D,K;t=this.parameters.category||(t?m(t[r],k[r],r):r);d&&l(t)&&(t=z(d.lin2log(t)));e.dateTime&&(G?(D=p.time.resolveDTLFormat(h.dateTimeLabelFormats[!h.grid&&\nG.higherRanks[r]||G.unitName]),P=D.main):l(t)&&(P=e.dateTime.getXDateFormat(t,h.dateTimeLabelFormats||{})));g.isFirst=b;g.isLast=f;const x={axis:e,chart:p,dateTimeLabelFormat:P,isFirst:b,isLast:f,pos:r,tick:g,tickPositionInfo:G,value:t};v(this,\"labelFormat\",x);const B=b=>q.formatter?q.formatter.call(b,b):q.format?(b.text=e.defaultLabelFormatter.call(b,b),a.format(q.format,b,p)):e.defaultLabelFormatter.call(b,b);h=B.call(x,x);const y=D&&D.list;g.shortenLabel=y?function(){for(K=0;K<y.length;K++)if(u(x,\n{dateTimeLabelFormat:y[K]}),n.attr({text:B.call(x,x)}),n.getBBox().width<e.getSlotWidth(g)-2*q.padding)return;n.attr({text:\"\"})}:void 0;c&&e._addedPlotLB&&g.moveLabel(h,q);H(n)||g.movedLabel?n&&n.textStr!==h&&!c&&(!n.textWidth||q.style.width||n.styles.width||n.css({width:null}),n.attr({text:h}),n.textPxLength=n.getBBox().width):(g.label=n=g.createLabel({x:0,y:0},h,q),g.rotation=0)}createLabel(a,e,h){const g=this.axis,m=g.chart;if(a=H(e)&&h.enabled?m.renderer.text(e,a.x,a.y,h.useHTML).add(g.labelGroup):\nnull)m.styledMode||a.css(p(h.style)),a.textPxLength=a.getBBox().width;return a}destroy(){B(this,this.axis)}getPosition(a,e,h,m){const g=this.axis,d=g.chart,k=m&&d.oldChartHeight||d.chartHeight;a={x:a?z(g.translate(e+h,void 0,void 0,m)+g.transB):g.left+g.offset+(g.opposite?(m&&d.oldChartWidth||d.chartWidth)-g.right-g.left:0),y:a?k-g.bottom+g.offset-(g.opposite?g.height:0):z(k-g.translate(e+h,void 0,void 0,m)-g.transB)};a.y=C(a.y,-1E5,1E5);v(this,\"afterGetPosition\",{pos:a});return a}getLabelPosition(a,\ne,h,l,p,d,k,r){const q=this.axis,g=q.transA,b=q.isLinked&&q.linkedParent?q.linkedParent.reversed:q.reversed,f=q.staggerLines,c=q.tickRotCorr||{x:0,y:0},n=l||q.reserveSpaceDefault?0:-q.labelOffset*(\"center\"===q.labelAlign?.5:1),w=p.distance,D={};h=0===q.side?h.rotation?-w:-h.getBBox().height:2===q.side?c.y+w:Math.cos(h.rotation*x)*(c.y-h.getBBox(!1,0).height/2);H(p.y)&&(h=0===q.side&&q.horiz?p.y+h:p.y);a=a+m(p.x,[0,1,0,-1][q.side]*w)+n+c.x-(d&&l?d*g*(b?-1:1):0);e=e+h-(d&&!l?d*g*(b?1:-1):0);f&&(l=k/\n(r||1)%f,q.opposite&&(l=f-l-1),e+=q.labelOffset/f*l);D.x=a;D.y=Math.round(e);v(this,\"afterGetLabelPosition\",{pos:D,tickmarkOffset:d,index:k});return D}getLabelSize(){return this.label?this.label.getBBox()[this.axis.horiz?\"height\":\"width\"]:0}getMarkPath(a,e,h,m,l,d){return d.crispLine([[\"M\",a,e],[\"L\",a+(l?0:-h),e+(l?h:0)]],m)}handleOverflow(a){const e=this.axis,g=e.options.labels,h=a.x;var l=e.chart.chartWidth,d=e.chart.spacing;const k=m(e.labelLeft,Math.min(e.pos,d[3]));d=m(e.labelRight,Math.max(e.isRadial?\n0:e.pos+e.len,l-d[1]));const r=this.label,q=this.rotation,p={left:0,center:.5,right:1}[e.labelAlign||r.attr(\"align\")],b=r.getBBox().width,f=e.getSlotWidth(this),c={};let n=f,t=1,D;if(q||\"justify\"!==g.overflow)0>q&&h-p*b<k?D=Math.round(h/Math.cos(q*x)-k):0<q&&h+p*b>d&&(D=Math.round((l-h)/Math.cos(q*x)));else if(l=h+(1-p)*b,h-p*b<k?n=a.x+n*(1-p)-k:l>d&&(n=d-a.x+n*p,t=-1),n=Math.min(f,n),n<f&&\"center\"===e.labelAlign&&(a.x+=t*(f-n-p*(f-Math.min(b,n)))),b>n||e.autoRotation&&(r.styles||{}).width)D=n;D&&\n(this.shortenLabel?this.shortenLabel():(c.width=Math.floor(D)+\"px\",(g.style||{}).textOverflow||(c.textOverflow=\"ellipsis\"),r.css(c)))}moveLabel(a,e){const g=this;var h=g.label;const m=g.axis;let d=!1;h&&h.textStr===a?(g.movedLabel=h,d=!0,delete g.label):t(m.ticks,function(k){d||k.isNew||k===g||!k.label||k.label.textStr!==a||(g.movedLabel=k.label,d=!0,k.labelPos=g.movedLabel.xy,delete k.label)});d||!g.labelPos&&!h||(h=g.labelPos||h.xy,g.movedLabel=g.createLabel(h,a,e),g.movedLabel&&g.movedLabel.attr({opacity:0}))}render(a,\ne,h){var g=this.axis,l=g.horiz,d=this.pos,k=m(this.tickmarkOffset,g.tickmarkOffset);d=this.getPosition(l,d,k,e);k=d.x;const r=d.y;g=l&&k===g.pos+g.len||!l&&r===g.pos?-1:1;l=m(h,this.label&&this.label.newOpacity,1);h=m(h,1);this.isActive=!0;this.renderGridLine(e,h,g);this.renderMark(d,h,g);this.renderLabel(d,e,l,a);this.isNew=!1;v(this,\"afterRender\")}renderGridLine(a,e,h){const g=this.axis,l=g.options,d={},k=this.pos,r=this.type,q=m(this.tickmarkOffset,g.tickmarkOffset),p=g.chart.renderer;let b=this.gridLine,\nf=l.gridLineWidth,c=l.gridLineColor,n=l.gridLineDashStyle;\"minor\"===this.type&&(f=l.minorGridLineWidth,c=l.minorGridLineColor,n=l.minorGridLineDashStyle);b||(g.chart.styledMode||(d.stroke=c,d[\"stroke-width\"]=f||0,d.dashstyle=n),r||(d.zIndex=1),a&&(e=0),this.gridLine=b=p.path().attr(d).addClass(\"highcharts-\"+(r?r+\"-\":\"\")+\"grid-line\").add(g.gridGroup));if(b&&(h=g.getPlotLinePath({value:k+q,lineWidth:b.strokeWidth()*h,force:\"pass\",old:a,acrossPanes:!1})))b[a||this.isNew?\"attr\":\"animate\"]({d:h,opacity:e})}renderMark(a,\ne,h){const g=this.axis;var l=g.options;const d=g.chart.renderer,k=this.type,r=g.tickSize(k?k+\"Tick\":\"tick\"),q=a.x;a=a.y;const p=m(l[\"minor\"!==k?\"tickWidth\":\"minorTickWidth\"],!k&&g.isXAxis?1:0);l=l[\"minor\"!==k?\"tickColor\":\"minorTickColor\"];let b=this.mark;const f=!b;r&&(g.opposite&&(r[0]=-r[0]),b||(this.mark=b=d.path().addClass(\"highcharts-\"+(k?k+\"-\":\"\")+\"tick\").add(g.axisGroup),g.chart.styledMode||b.attr({stroke:l,\"stroke-width\":p})),b[f?\"attr\":\"animate\"]({d:this.getMarkPath(q,a,r[0],b.strokeWidth()*\nh,g.horiz,d),opacity:e}))}renderLabel(a,e,h,p){var g=this.axis;const d=g.horiz,k=g.options,r=this.label,q=k.labels,t=q.step;g=m(this.tickmarkOffset,g.tickmarkOffset);const b=a.x;a=a.y;let f=!0;r&&l(b)&&(r.xy=a=this.getLabelPosition(b,a,r,d,q,g,p,t),this.isFirst&&!this.isLast&&!k.showFirstLabel||this.isLast&&!this.isFirst&&!k.showLastLabel?f=!1:!d||q.step||q.rotation||e||0===h||this.handleOverflow(a),t&&p%t&&(f=!1),f&&l(a.y)?(a.opacity=h,r[this.isNewLabel?\"attr\":\"animate\"](a).show(!0),this.isNewLabel=\n!1):(r.hide(),this.isNewLabel=!0))}replaceMovedLabel(){const a=this.label,e=this.axis;a&&!this.isNew&&(a.animate({opacity:0},void 0,a.destroy),delete this.label);e.isDirty=!0;this.label=this.movedLabel;delete this.movedLabel}}\"\";return h});M(a,\"Core/Axis/Axis.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Axis/AxisDefaults.js\"],a[\"Core/Color/Color.js\"],a[\"Core/Defaults.js\"],a[\"Core/Foundation.js\"],a[\"Core/Globals.js\"],a[\"Core/Axis/Tick.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C,z,H,B){const {animObject:u}=\na,{defaultOptions:v}=L,{registerEventOptions:l}=C,{deg2rad:p}=z,{arrayMax:t,arrayMin:m,clamp:h,correctFloat:g,defined:e,destroyObjectProperties:w,erase:x,error:F,extend:d,fireEvent:k,getClosestDistance:r,insertItem:q,isArray:G,isNumber:b,isString:f,merge:c,normalizeTickInterval:n,objectEach:P,pick:D,relativeLength:K,removeEvent:X,splat:T,syncTimeout:Z}=B,V=(b,c)=>n(c,void 0,void 0,D(b.options.allowDecimals,.5>c||void 0!==b.tickAmount),!!b.tickAmount);class Y{constructor(b,c,f){this.zoomEnabled=this.width=\nthis.visible=this.userOptions=this.translationSlope=this.transB=this.transA=this.top=this.ticks=this.tickRotCorr=this.tickPositions=this.tickmarkOffset=this.tickInterval=this.tickAmount=this.side=this.series=this.right=this.positiveValuesOnly=this.pos=this.pointRangePadding=this.pointRange=this.plotLinesAndBandsGroups=this.plotLinesAndBands=this.paddedTicks=this.overlap=this.options=this.offset=this.names=this.minPixelPadding=this.minorTicks=this.minorTickInterval=this.min=this.maxLabelLength=this.max=\nthis.len=this.left=this.labelFormatter=this.labelEdge=this.isLinked=this.index=this.height=this.hasVisibleSeries=this.hasNames=this.eventOptions=this.coll=this.closestPointRange=this.chart=this.bottom=this.alternateBands=void 0;this.init(b,c,f)}init(c,f,a=this.coll){const d=\"xAxis\"===a;this.chart=c;this.horiz=this.isZAxis||(c.inverted?!d:d);this.isXAxis=d;this.coll=a;k(this,\"init\",{userOptions:f});this.opposite=D(f.opposite,this.opposite);this.side=D(f.side,this.side,this.horiz?this.opposite?0:2:\nthis.opposite?1:3);this.setOptions(f);a=this.options;const A=a.labels,n=a.type;this.userOptions=f;this.minPixelPadding=0;this.reversed=D(a.reversed,this.reversed);this.visible=a.visible;this.zoomEnabled=a.zoomEnabled;this.hasNames=\"category\"===n||!0===a.categories;this.categories=a.categories||(this.hasNames?[]:void 0);this.names||(this.names=[],this.names.keys={});this.plotLinesAndBandsGroups={};this.positiveValuesOnly=!!this.logarithmic;this.isLinked=e(a.linkedTo);this.ticks={};this.labelEdge=[];\nthis.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=a.minRange||a.maxZoom;this.range=a.range;this.offset=a.offset||0;this.min=this.max=null;f=D(a.crosshair,T(c.options.tooltip.crosshairs)[d?0:1]);this.crosshair=!0===f?{}:f;-1===c.axes.indexOf(this)&&(d?c.axes.splice(c.xAxis.length,0,this):c.axes.push(this),q(this,c[this.coll]));c.orderItems(this.coll);this.series=this.series||[];c.inverted&&!this.isZAxis&&d&&\"undefined\"===typeof this.reversed&&\n(this.reversed=!0);this.labelRotation=b(A.rotation)?A.rotation:void 0;l(this,a);k(this,\"afterInit\")}setOptions(b){this.options=c(y.defaultXAxisOptions,\"yAxis\"===this.coll&&y.defaultYAxisOptions,[y.defaultTopAxisOptions,y.defaultRightAxisOptions,y.defaultBottomAxisOptions,y.defaultLeftAxisOptions][this.side],c(v[this.coll],b));k(this,\"afterSetOptions\",{userOptions:b})}defaultLabelFormatter(c){var f=this.axis;({numberFormatter:c}=this.chart);const a=b(this.value)?this.value:NaN,d=f.chart.time,k=this.dateTimeLabelFormat;\nvar n=v.lang;const A=n.numericSymbols;n=n.numericSymbolMagnitude||1E3;const e=f.logarithmic?Math.abs(a):f.tickInterval;let q=A&&A.length,g;if(f.categories)g=`${this.value}`;else if(k)g=d.dateFormat(k,a);else if(q&&1E3<=e)for(;q--&&\"undefined\"===typeof g;)f=Math.pow(n,q+1),e>=f&&0===10*a%f&&null!==A[q]&&0!==a&&(g=c(a/f,-1)+A[q]);\"undefined\"===typeof g&&(g=1E4<=Math.abs(a)?c(a,-1):c(a,-1,void 0,\"\"));return g}getSeriesExtremes(){const c=this,f=c.chart;let a;k(this,\"getSeriesExtremes\",null,function(){c.hasVisibleSeries=\n!1;c.dataMin=c.dataMax=c.threshold=null;c.softThreshold=!c.isXAxis;c.series.forEach(function(d){if(d.visible||!f.options.chart.ignoreHiddenSeries){var k=d.options;let f=k.threshold,n,A;c.hasVisibleSeries=!0;c.positiveValuesOnly&&0>=f&&(f=null);if(c.isXAxis)(k=d.xData)&&k.length&&(k=c.logarithmic?k.filter(b=>0<b):k,a=d.getXExtremes(k),n=a.min,A=a.max,b(n)||n instanceof Date||(k=k.filter(b),a=d.getXExtremes(k),n=a.min,A=a.max),k.length&&(c.dataMin=Math.min(D(c.dataMin,n),n),c.dataMax=Math.max(D(c.dataMax,\nA),A)));else if(d=d.applyExtremes(),b(d.dataMin)&&(n=d.dataMin,c.dataMin=Math.min(D(c.dataMin,n),n)),b(d.dataMax)&&(A=d.dataMax,c.dataMax=Math.max(D(c.dataMax,A),A)),e(f)&&(c.threshold=f),!k.softThreshold||c.positiveValuesOnly)c.softThreshold=!1}})});k(this,\"afterGetSeriesExtremes\")}translate(c,f,a,d,k,n){const e=this.linkedParent||this,A=d&&e.old?e.old.min:e.min;if(!b(A))return NaN;const q=e.minPixelPadding;k=(e.isOrdinal||e.brokenAxis&&e.brokenAxis.hasBreaks||e.logarithmic&&k)&&e.lin2val;let J=\n1,h=0;d=d&&e.old?e.old.transA:e.transA;d||(d=e.transA);a&&(J*=-1,h=e.len);e.reversed&&(J*=-1,h-=J*(e.sector||e.len));f?(n=(c*J+h-q)/d+A,k&&(n=e.lin2val(n))):(k&&(c=e.val2lin(c)),c=J*(c-A)*d,n=(e.isRadial?c:g(c))+h+J*q+(b(n)?d*n:0));return n}toPixels(b,c){return this.translate(b,!1,!this.horiz,void 0,!0)+(c?0:this.pos)}toValue(b,c){return this.translate(b-(c?0:this.pos),!0,!this.horiz,void 0,!0)}getPlotLinePath(c){function f(b,c,f){\"pass\"!==t&&(b<c||b>f)&&(t?b=h(b,c,f):K=!0);return b}const a=this,\nd=a.chart,n=a.left,e=a.top,A=c.old,q=c.value,g=c.lineWidth,r=A&&d.oldChartHeight||d.chartHeight,m=A&&d.oldChartWidth||d.chartWidth,l=a.transB;let p=c.translatedValue,t=c.force,P,w,R,Q,K;c={value:q,lineWidth:g,old:A,force:t,acrossPanes:c.acrossPanes,translatedValue:p};k(this,\"getPlotLinePath\",c,function(c){p=D(p,a.translate(q,void 0,void 0,A));p=h(p,-1E5,1E5);P=R=Math.round(p+l);w=Q=Math.round(r-p-l);b(p)?a.horiz?(w=e,Q=r-a.bottom,P=R=f(P,n,n+a.width)):(P=n,R=m-a.right,w=Q=f(w,e,e+a.height)):(K=!0,\nt=!1);c.path=K&&!t?null:d.renderer.crispLine([[\"M\",P,w],[\"L\",R,Q]],g||1)});return c.path}getLinearTickPositions(b,c,f){const a=g(Math.floor(c/b)*b);f=g(Math.ceil(f/b)*b);const d=[];let k,n;g(a+b)===a&&(n=20);if(this.single)return[c];for(c=a;c<=f;){d.push(c);c=g(c+b,n);if(c===k)break;k=c}return d}getMinorTickInterval(){const b=this.options;return!0===b.minorTicks?D(b.minorTickInterval,\"auto\"):!1===b.minorTicks?null:b.minorTickInterval}getMinorTickPositions(){var b=this.options;const c=this.tickPositions,\nf=this.minorTickInterval;var a=this.pointRangePadding||0;const d=this.min-a;a=this.max+a;const k=a-d;let n=[];if(k&&k/f<this.len/3){const k=this.logarithmic;if(k)this.paddedTicks.forEach(function(b,c,a){c&&n.push.apply(n,k.getLogTickPositions(f,a[c-1],a[c],!0))});else if(this.dateTime&&\"auto\"===this.getMinorTickInterval())n=n.concat(this.getTimeTicks(this.dateTime.normalizeTimeTickInterval(f),d,a,b.startOfWeek));else for(b=d+(c[0]-d)%f;b<=a&&b!==n[0];b+=f)n.push(b)}0!==n.length&&this.trimTicks(n);\nreturn n}adjustForMinRange(){const b=this.options,c=this.logarithmic;let f=this.min;var a=this.max;let d,k;if(this.isXAxis&&\"undefined\"===typeof this.minRange&&!c)if(e(b.min)||e(b.max)||e(b.floor)||e(b.ceiling))this.minRange=null;else{var n=r(this.series.map(b=>{var c;return(b.xIncrement?null===(c=b.xData)||void 0===c?void 0:c.slice(0,2):b.xData)||[]}))||0;this.minRange=Math.min(5*n,this.dataMax-this.dataMin)}a-f<this.minRange&&(n=this.dataMax-this.dataMin>=this.minRange,k=this.minRange,a=(k-a+f)/\n2,d=[f-a,D(b.min,f-a)],n&&(d[2]=c?c.log2lin(this.dataMin):this.dataMin),f=t(d),a=[f+k,D(b.max,f+k)],n&&(a[2]=c?c.log2lin(this.dataMax):this.dataMax),a=m(a),a-f<k&&(d[0]=a-k,d[1]=D(b.min,a-k),f=t(d)));this.min=f;this.max=a}getClosest(){let b,c;if(this.categories)c=1;else{const f=[];this.series.forEach(function(b){var a;const d=b.closestPointRange,k=b.visible||!b.chart.options.chart.ignoreHiddenSeries;1===(null===(a=b.xData)||void 0===a?void 0:a.length)?f.push(b.xData[0]):!b.noSharedTooltip&&e(d)&&\nk&&(c=e(c)?Math.min(c,d):d)});f.length&&(f.sort((b,c)=>b-c),b=r([f]))}return b&&c?Math.min(b,c):b||c}nameToX(b){const c=G(this.options.categories),f=c?this.categories:this.names;let a=b.options.x,d;b.series.requireSorting=!1;e(a)||(a=this.options.uniqueNames&&f?c?f.indexOf(b.name):D(f.keys[b.name],-1):b.series.autoIncrement());-1===a?!c&&f&&(d=f.length):d=a;\"undefined\"!==typeof d?(this.names[d]=b.name,this.names.keys[b.name]=d):b.x&&(d=b.x);return d}updateNames(){const b=this,c=this.names;0<c.length&&\n(Object.keys(c.keys).forEach(function(b){delete c.keys[b]}),c.length=0,this.minRange=this.userMinRange,(this.series||[]).forEach(function(c){c.xIncrement=null;if(!c.points||c.isDirtyData)b.max=Math.max(b.max,c.xData.length-1),c.processData(),c.generatePoints();c.data.forEach(function(f,a){let d;f&&f.options&&\"undefined\"!==typeof f.name&&(d=b.nameToX(f),\"undefined\"!==typeof d&&d!==f.x&&(f.x=d,c.xData[a]=d))})}))}setAxisTranslation(){const b=this,c=b.max-b.min;var a=b.linkedParent;const d=!!b.categories,\nn=b.isXAxis;let e=b.axisPointRange||0,q,g=0,h=0,r=b.transA;if(n||d||e)q=b.getClosest(),a?(g=a.minPointOffset,h=a.pointRangePadding):b.series.forEach(function(c){const a=d?1:n?D(c.options.pointRange,q,0):b.axisPointRange||0,k=c.options.pointPlacement;e=Math.max(e,a);if(!b.single||d)c=c.is(\"xrange\")?!n:n,g=Math.max(g,c&&f(k)?0:a/2),h=Math.max(h,c&&\"on\"===k?0:a)}),a=b.ordinal&&b.ordinal.slope&&q?b.ordinal.slope/q:1,b.minPointOffset=g*=a,b.pointRangePadding=h*=a,b.pointRange=Math.min(e,b.single&&d?1:\nc),n&&q&&(b.closestPointRange=q);b.translationSlope=b.transA=r=b.staticScale||b.len/(c+h||1);b.transB=b.horiz?b.left:b.bottom;b.minPixelPadding=r*g;k(this,\"afterSetAxisTranslation\")}minFromRange(){return this.max-this.range}setTickInterval(c){var f=this.chart;const a=this.logarithmic,d=this.options,n=this.isXAxis,q=this.isLinked,h=d.tickPixelInterval,A=this.categories,r=this.softThreshold;let m=d.maxPadding,l=d.minPadding;let p=b(d.tickInterval)&&0<=d.tickInterval?d.tickInterval:void 0,t=b(this.threshold)?\nthis.threshold:null,P,w,K;this.dateTime||A||q||this.getTickAmount();w=D(this.userMin,d.min);K=D(this.userMax,d.max);if(q){this.linkedParent=f[this.coll][d.linkedTo];var R=this.linkedParent.getExtremes();this.min=D(R.min,R.dataMin);this.max=D(R.max,R.dataMax);d.type!==this.linkedParent.options.type&&F(11,1,f)}else r&&e(t)&&(this.dataMin>=t?(R=t,l=0):this.dataMax<=t&&(P=t,m=0)),this.min=D(w,R,this.dataMin),this.max=D(K,P,this.dataMax);a&&(this.positiveValuesOnly&&!c&&0>=Math.min(this.min,D(this.dataMin,\nthis.min))&&F(10,1,f),this.min=g(a.log2lin(this.min),16),this.max=g(a.log2lin(this.max),16));this.range&&e(this.max)&&(this.userMin=this.min=w=Math.max(this.dataMin,this.minFromRange()),this.userMax=K=this.max,this.range=null);k(this,\"foundExtremes\");this.beforePadding&&this.beforePadding();this.adjustForMinRange();!b(this.userMin)&&b(d.softMin)&&d.softMin<this.min&&(this.min=w=d.softMin);!b(this.userMax)&&b(d.softMax)&&d.softMax>this.max&&(this.max=K=d.softMax);!(A||this.axisPointRange||this.stacking&&\nthis.stacking.usePercentage||q)&&e(this.min)&&e(this.max)&&(f=this.max-this.min)&&(!e(w)&&l&&(this.min-=f*l),!e(K)&&m&&(this.max+=f*m));!b(this.userMin)&&b(d.floor)&&(this.min=Math.max(this.min,d.floor));!b(this.userMax)&&b(d.ceiling)&&(this.max=Math.min(this.max,d.ceiling));r&&e(this.dataMin)&&(t=t||0,!e(w)&&this.min<t&&this.dataMin>=t?this.min=this.options.minRange?Math.min(t,this.max-this.minRange):t:!e(K)&&this.max>t&&this.dataMax<=t&&(this.max=this.options.minRange?Math.max(t,this.min+this.minRange):\nt));b(this.min)&&b(this.max)&&!this.chart.polar&&this.min>this.max&&(e(this.options.min)?this.max=this.min:e(this.options.max)&&(this.min=this.max));this.tickInterval=this.min===this.max||\"undefined\"===typeof this.min||\"undefined\"===typeof this.max?1:q&&this.linkedParent&&!p&&h===this.linkedParent.options.tickPixelInterval?p=this.linkedParent.tickInterval:D(p,this.tickAmount?(this.max-this.min)/Math.max(this.tickAmount-1,1):void 0,A?1:(this.max-this.min)*h/Math.max(this.len,h));if(n&&!c){const b=\nthis.min!==(this.old&&this.old.min)||this.max!==(this.old&&this.old.max);this.series.forEach(function(c){c.forceCrop=c.forceCropping&&c.forceCropping();c.processData(b)});k(this,\"postProcessData\",{hasExtremesChanged:b})}this.setAxisTranslation();k(this,\"initialAxisTranslation\");this.pointRange&&!p&&(this.tickInterval=Math.max(this.pointRange,this.tickInterval));c=D(d.minTickInterval,this.dateTime&&!this.series.some(b=>b.noSharedTooltip)?this.closestPointRange:0);!p&&this.tickInterval<c&&(this.tickInterval=\nc);this.dateTime||this.logarithmic||p||(this.tickInterval=V(this,this.tickInterval));this.tickAmount||(this.tickInterval=this.unsquish());this.setTickPositions()}setTickPositions(){var c=this.options;const f=c.tickPositions,a=c.tickPositioner;var d=this.getMinorTickInterval(),n=this.hasVerticalPanning(),q=\"colorAxis\"===this.coll;const g=(q||!n)&&c.startOnTick;n=(q||!n)&&c.endOnTick;q=[];let h;this.tickmarkOffset=this.categories&&\"between\"===c.tickmarkPlacement&&1===this.tickInterval?.5:0;this.minorTickInterval=\n\"auto\"===d&&this.tickInterval?this.tickInterval/c.minorTicksPerMajor:d;this.single=this.min===this.max&&e(this.min)&&!this.tickAmount&&(parseInt(this.min,10)===this.min||!1!==c.allowDecimals);if(f)q=f.slice();else if(b(this.min)&&b(this.max)){if(this.ordinal&&this.ordinal.positions||!((this.max-this.min)/this.tickInterval>Math.max(2*this.len,200)))if(this.dateTime)q=this.getTimeTicks(this.dateTime.normalizeTimeTickInterval(this.tickInterval,c.units),this.min,this.max,c.startOfWeek,this.ordinal&&this.ordinal.positions,\nthis.closestPointRange,!0);else if(this.logarithmic)q=this.logarithmic.getLogTickPositions(this.tickInterval,this.min,this.max);else for(d=c=this.tickInterval;d<=2*c;)if(q=this.getLinearTickPositions(this.tickInterval,this.min,this.max),this.tickAmount&&q.length>this.tickAmount)this.tickInterval=V(this,d*=1.1);else break;else q=[this.min,this.max],F(19,!1,this.chart);q.length>this.len&&(q=[q[0],q[q.length-1]],q[0]===q[1]&&(q.length=1));a&&(this.tickPositions=q,(h=a.apply(this,[this.min,this.max]))&&\n(q=h))}this.tickPositions=q;this.paddedTicks=q.slice(0);this.trimTicks(q,g,n);!this.isLinked&&b(this.min)&&b(this.max)&&(this.single&&2>q.length&&!this.categories&&!this.series.some(b=>b.is(\"heatmap\")&&\"between\"===b.options.pointPlacement)&&(this.min-=.5,this.max+=.5),f||h||this.adjustTickAmount());k(this,\"afterSetTickPositions\")}trimTicks(b,c,f){const a=b[0],d=b[b.length-1],n=!this.isOrdinal&&this.minPointOffset||0;k(this,\"trimTicks\");if(!this.isLinked){if(c&&-Infinity!==a)this.min=a;else for(;this.min-\nn>b[0];)b.shift();if(f)this.max=d;else for(;this.max+n<b[b.length-1];)b.pop();0===b.length&&e(a)&&!this.options.tickPositions&&b.push((d+a)/2)}}alignToOthers(){const c=this,f=[this],a=c.options,d=\"yAxis\"===this.coll&&this.chart.options.chart.alignThresholds,k=[];let n;c.thresholdAlignment=void 0;if((!1!==this.chart.options.chart.alignTicks&&a.alignTicks||d)&&!1!==a.startOnTick&&!1!==a.endOnTick&&!c.logarithmic){const b=b=>{const {horiz:c,options:f}=b;return[c?f.left:f.top,f.width,f.height,f.pane].join()},\na=b(this);this.chart[this.coll].forEach(function(d){const {series:k}=d;k.length&&k.some(b=>b.visible)&&d!==c&&b(d)===a&&(n=!0,f.push(d))})}if(n&&d){f.forEach(f=>{f=f.getThresholdAlignment(c);b(f)&&k.push(f)});const a=1<k.length?k.reduce((b,c)=>b+c,0)/k.length:void 0;f.forEach(b=>{b.thresholdAlignment=a})}return n}getThresholdAlignment(c){(!b(this.dataMin)||this!==c&&this.series.some(b=>b.isDirty||b.isDirtyData))&&this.getSeriesExtremes();if(b(this.threshold))return c=h((this.threshold-(this.dataMin||\n0))/((this.dataMax||0)-(this.dataMin||0)),0,1),this.options.reversed&&(c=1-c),c}getTickAmount(){const b=this.options,c=b.tickPixelInterval;let f=b.tickAmount;!e(b.tickInterval)&&!f&&this.len<c&&!this.isRadial&&!this.logarithmic&&b.startOnTick&&b.endOnTick&&(f=2);!f&&this.alignToOthers()&&(f=Math.ceil(this.len/c)+1);4>f&&(this.finalTickAmt=f,f=5);this.tickAmount=f}adjustTickAmount(){const c=this,{finalTickAmt:f,max:a,min:d,options:k,tickPositions:n,tickAmount:q,thresholdAlignment:h}=c,r=n&&n.length;\nvar m=D(c.threshold,c.softThreshold?0:null);var l=c.tickInterval;let p;b(h)&&(p=.5>h?Math.ceil(h*(q-1)):Math.floor(h*(q-1)),k.reversed&&(p=q-1-p));if(c.hasData()&&b(d)&&b(a)){const h=()=>{c.transA*=(r-1)/(q-1);c.min=k.startOnTick?n[0]:Math.min(d,n[0]);c.max=k.endOnTick?n[n.length-1]:Math.max(a,n[n.length-1])};if(b(p)&&b(c.threshold)){for(;n[p]!==m||n.length!==q||n[0]>d||n[n.length-1]<a;){n.length=0;for(n.push(c.threshold);n.length<q;)void 0===n[p]||n[p]>c.threshold?n.unshift(g(n[0]-l)):n.push(g(n[n.length-\n1]+l));if(l>8*c.tickInterval)break;l*=2}h()}else if(r<q){for(;n.length<q;)n.length%2||d===m?n.push(g(n[n.length-1]+l)):n.unshift(g(n[0]-l));h()}if(e(f)){for(l=m=n.length;l--;)(3===f&&1===l%2||2>=f&&0<l&&l<m-1)&&n.splice(l,1);c.finalTickAmt=void 0}}}setScale(){let b=!1,c=!1;this.series.forEach(function(f){b=b||f.isDirtyData||f.isDirty;c=c||f.xAxis&&f.xAxis.isDirty||!1});this.setAxisSize();const f=this.len!==(this.old&&this.old.len);f||b||c||this.isLinked||this.forceRedraw||this.userMin!==(this.old&&\nthis.old.userMin)||this.userMax!==(this.old&&this.old.userMax)||this.alignToOthers()?(this.stacking&&(this.stacking.resetStacks(),this.stacking.buildStacks()),this.forceRedraw=!1,this.userMinRange||(this.minRange=void 0),this.getSeriesExtremes(),this.setTickInterval(),this.isDirty||(this.isDirty=f||this.min!==(this.old&&this.old.min)||this.max!==(this.old&&this.old.max))):this.stacking&&this.stacking.cleanStacks();b&&this.panningState&&(this.panningState.isDirty=!0);k(this,\"afterSetScale\")}setExtremes(b,\nc,f,a,n){const e=this,q=e.chart;f=D(f,!0);e.series.forEach(function(b){delete b.kdTree});n=d(n,{min:b,max:c});k(e,\"setExtremes\",n,function(){e.userMin=b;e.userMax=c;e.eventArgs=n;f&&q.redraw(a)})}zoom(b,c){const f=this,a=this.dataMin,d=this.dataMax,n=this.options,q=Math.min(a,D(n.min,a)),g=Math.max(d,D(n.max,d));b={newMin:b,newMax:c};k(this,\"zoom\",b,function(b){let c=b.newMin,n=b.newMax;if(c!==f.min||n!==f.max)f.allowZoomOutside||(e(a)&&(c<q&&(c=q),c>g&&(c=g)),e(d)&&(n<q&&(n=q),n>g&&(n=g))),f.displayBtn=\n\"undefined\"!==typeof c||\"undefined\"!==typeof n,f.setExtremes(c,n,!1,void 0,{trigger:\"zoom\"});b.zoomed=!0});return b.zoomed}setAxisSize(){const b=this.chart;var c=this.options;const f=c.offsets||[0,0,0,0],a=this.horiz,d=this.width=Math.round(K(D(c.width,b.plotWidth-f[3]+f[1]),b.plotWidth)),n=this.height=Math.round(K(D(c.height,b.plotHeight-f[0]+f[2]),b.plotHeight)),k=this.top=Math.round(K(D(c.top,b.plotTop+f[0]),b.plotHeight,b.plotTop));c=this.left=Math.round(K(D(c.left,b.plotLeft+f[3]),b.plotWidth,\nb.plotLeft));this.bottom=b.chartHeight-n-k;this.right=b.chartWidth-d-c;this.len=Math.max(a?d:n,0);this.pos=a?c:k}getExtremes(){const b=this.logarithmic;return{min:b?g(b.lin2log(this.min)):this.min,max:b?g(b.lin2log(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}}getThreshold(b){var c=this.logarithmic;const f=c?c.lin2log(this.min):this.min;c=c?c.lin2log(this.max):this.max;null===b||-Infinity===b?b=f:Infinity===b?b=c:f>b?b=f:c<b&&(b=c);return this.translate(b,\n0,1,0,1)}autoLabelAlign(b){const c=(D(b,0)-90*this.side+720)%360;b={align:\"center\"};k(this,\"autoLabelAlign\",b,function(b){15<c&&165>c?b.align=\"right\":195<c&&345>c&&(b.align=\"left\")});return b.align}tickSize(b){const c=this.options,f=D(c[\"tick\"===b?\"tickWidth\":\"minorTickWidth\"],\"tick\"===b&&this.isXAxis&&!this.categories?1:0);let a=c[\"tick\"===b?\"tickLength\":\"minorTickLength\"],d;f&&a&&(\"inside\"===c[b+\"Position\"]&&(a=-a),d=[a,f]);b={tickSize:d};k(this,\"afterTickSize\",b);return b.tickSize}labelMetrics(){const b=\nthis.chart.renderer;var c=this.ticks;c=c[Object.keys(c)[0]]||{};return this.chart.renderer.fontMetrics(c.label||c.movedLabel||b.box)}unsquish(){const c=this.options.labels;var f=this.horiz;const a=this.tickInterval,d=this.len/(((this.categories?1:0)+this.max-this.min)/a),n=c.rotation,k=.75*this.labelMetrics().h,e=Math.max(this.max-this.min,0),q=function(b){let c=b/(d||1);c=1<c?Math.ceil(c):1;c*a>e&&Infinity!==b&&Infinity!==d&&e&&(c=Math.ceil(e/a));return g(c*a)};let h=a,r,l=Number.MAX_VALUE,m;if(f){if(c.staggerLines||\n(b(n)?m=[n]:d<c.autoRotationLimit&&(m=c.autoRotation)),m){let b;for(const c of m)if(c===n||c&&-90<=c&&90>=c)f=q(Math.abs(k/Math.sin(p*c))),b=f+Math.abs(c/360),b<l&&(l=b,r=c,h=f)}}else h=q(k);this.autoRotation=m;this.labelRotation=D(r,b(n)?n:0);return c.step?a:h}getSlotWidth(c){const f=this.chart,a=this.horiz,d=this.options.labels,n=Math.max(this.tickPositions.length-(this.categories?0:1),1),k=f.margin[3];if(c&&b(c.slotWidth))return c.slotWidth;if(a&&2>d.step)return d.rotation?0:(this.staggerLines||\n1)*this.len/n;if(!a){c=d.style.width;if(void 0!==c)return parseInt(String(c),10);if(k)return k-f.spacing[3]}return.33*f.chartWidth}renderUnsquish(){const b=this.chart,c=b.renderer,a=this.tickPositions,d=this.ticks,n=this.options.labels,k=n.style,e=this.horiz,q=this.getSlotWidth();var g=Math.max(1,Math.round(q-2*n.padding));const h={},r=this.labelMetrics(),m=k.textOverflow;let l,p,D=0;f(n.rotation)||(h.rotation=n.rotation||0);a.forEach(function(b){b=d[b];b.movedLabel&&b.replaceMovedLabel();b&&b.label&&\nb.label.textPxLength>D&&(D=b.label.textPxLength)});this.maxLabelLength=D;if(this.autoRotation)D>g&&D>r.h?h.rotation=this.labelRotation:this.labelRotation=0;else if(q&&(l=g,!m))for(p=\"clip\",g=a.length;!e&&g--;){var t=a[g];if(t=d[t].label)t.styles&&\"ellipsis\"===t.styles.textOverflow?t.css({textOverflow:\"clip\"}):t.textPxLength>q&&t.css({width:q+\"px\"}),t.getBBox().height>this.len/a.length-(r.h-r.f)&&(t.specificTextOverflow=\"ellipsis\")}h.rotation&&(l=D>.5*b.chartHeight?.33*b.chartHeight:D,m||(p=\"ellipsis\"));\nif(this.labelAlign=n.align||this.autoLabelAlign(this.labelRotation))h.align=this.labelAlign;a.forEach(function(b){const c=(b=d[b])&&b.label,f=k.width,a={};c&&(c.attr(h),b.shortenLabel?b.shortenLabel():l&&!f&&\"nowrap\"!==k.whiteSpace&&(l<c.textPxLength||\"SPAN\"===c.element.tagName)?(a.width=l+\"px\",m||(a.textOverflow=c.specificTextOverflow||p),c.css(a)):c.styles&&c.styles.width&&!a.width&&!f&&c.css({width:null}),delete c.specificTextOverflow,b.rotation=h.rotation)},this);this.tickRotCorr=c.rotCorr(r.b,\nthis.labelRotation||0,0!==this.side)}hasData(){return this.series.some(function(b){return b.hasData()})||this.options.showEmpty&&e(this.min)&&e(this.max)}addTitle(b){const f=this.chart.renderer,a=this.horiz,d=this.opposite,n=this.options.title,k=this.chart.styledMode;let e;this.axisTitle||((e=n.textAlign)||(e=(a?{low:\"left\",middle:\"center\",high:\"right\"}:{low:d?\"right\":\"left\",middle:\"center\",high:d?\"left\":\"right\"})[n.align]),this.axisTitle=f.text(n.text||\"\",0,0,n.useHTML).attr({zIndex:7,rotation:n.rotation,\nalign:e}).addClass(\"highcharts-axis-title\"),k||this.axisTitle.css(c(n.style)),this.axisTitle.add(this.axisGroup),this.axisTitle.isNew=!0);k||n.style.width||this.isRadial||this.axisTitle.css({width:this.len+\"px\"});this.axisTitle[b?\"show\":\"hide\"](b)}generateTick(b){const c=this.ticks;c[b]?c[b].addLabel():c[b]=new H(this,b)}getOffset(){const c=this,{chart:f,horiz:a,options:d,side:n,ticks:q,tickPositions:g,coll:h,axisParent:r}=c,m=f.renderer,l=f.inverted&&!c.isZAxis?[1,0,3,2][n]:n;var p=c.hasData();const t=\nd.title;var w=d.labels;const K=b(d.crossing);var G=f.axisOffset;const R=f.clipOffset,Q=[-1,1,1,-1][n],v=d.className;let ja,u=0,x;var E=0;let F=0;c.showAxis=ja=p||d.showEmpty;c.staggerLines=c.horiz&&w.staggerLines||void 0;if(!c.axisGroup){const b=(b,c,f)=>m.g(b).attr({zIndex:f}).addClass(`highcharts-${h.toLowerCase()}${c} `+(this.isRadial?`highcharts-radial-axis${c} `:\"\")+(v||\"\")).add(r);c.gridGroup=b(\"grid\",\"-grid\",d.gridZIndex);c.axisGroup=b(\"axis\",\"\",d.zIndex);c.labelGroup=b(\"axis-labels\",\"-labels\",\nw.zIndex)}p||c.isLinked?(g.forEach(function(b){c.generateTick(b)}),c.renderUnsquish(),c.reserveSpaceDefault=0===n||2===n||{1:\"left\",3:\"right\"}[n]===c.labelAlign,D(w.reserveSpace,K?!1:null,\"center\"===c.labelAlign?!0:null,c.reserveSpaceDefault)&&g.forEach(function(b){F=Math.max(q[b].getLabelSize(),F)}),c.staggerLines&&(F*=c.staggerLines),c.labelOffset=F*(c.opposite?-1:1)):P(q,function(b,c){b.destroy();delete q[c]});t&&t.text&&!1!==t.enabled&&(c.addTitle(ja),ja&&!K&&!1!==t.reserveSpace&&(c.titleOffset=\nu=c.axisTitle.getBBox()[a?\"height\":\"width\"],x=t.offset,E=e(x)?0:D(t.margin,a?5:10)));c.renderLine();c.offset=Q*D(d.offset,G[n]?G[n]+(d.margin||0):0);c.tickRotCorr=c.tickRotCorr||{x:0,y:0};p=0===n?-c.labelMetrics().h:2===n?c.tickRotCorr.y:0;E=Math.abs(F)+E;F&&(E=E-p+Q*(a?D(w.y,c.tickRotCorr.y+Q*w.distance):D(w.x,Q*w.distance)));c.axisTitleMargin=D(x,E);c.getMaxLabelDimensions&&(c.maxLabelDimensions=c.getMaxLabelDimensions(q,g));\"colorAxis\"!==h&&(w=this.tickSize(\"tick\"),G[n]=Math.max(G[n],(c.axisTitleMargin||\n0)+u+Q*c.offset,E,g&&g.length&&w?w[0]+Q*c.offset:0),G=!c.axisLine||d.offset?0:2*Math.floor(c.axisLine.strokeWidth()/2),R[l]=Math.max(R[l],G));k(this,\"afterGetOffset\")}getLinePath(b){const c=this.chart,f=this.opposite;var a=this.offset;const d=this.horiz,n=this.left+(f?this.width:0)+a;a=c.chartHeight-this.bottom-(f?this.height:0)+a;f&&(b*=-1);return c.renderer.crispLine([[\"M\",d?this.left:n,d?a:this.top],[\"L\",d?c.chartWidth-this.right:n,d?a:c.chartHeight-this.bottom]],b)}renderLine(){this.axisLine||\n(this.axisLine=this.chart.renderer.path().addClass(\"highcharts-axis-line\").add(this.axisGroup),this.chart.styledMode||this.axisLine.attr({stroke:this.options.lineColor,\"stroke-width\":this.options.lineWidth,zIndex:7}))}getTitlePosition(b){var c=this.horiz,f=this.left;const a=this.top;var d=this.len;const n=this.options.title,e=c?f:a,q=this.opposite,g=this.offset,h=n.x,r=n.y,l=this.chart.renderer.fontMetrics(b);b=b?Math.max(b.getBBox(!1,0).height-l.h-1,0):0;d={low:e+(c?0:d),middle:e+d/2,high:e+(c?d:\n0)}[n.align];f=(c?a+this.height:f)+(c?1:-1)*(q?-1:1)*(this.axisTitleMargin||0)+[-b,b,l.f,-b][this.side];c={x:c?d+h:f+(q?this.width:0)+g+h,y:c?f+r-(q?this.height:0)+g:d+r};k(this,\"afterGetTitlePosition\",{titlePosition:c});return c}renderMinorTick(b,c){const f=this.minorTicks;f[b]||(f[b]=new H(this,b,\"minor\"));c&&f[b].isNew&&f[b].render(null,!0);f[b].render(null,!1,1)}renderTick(b,c,f){const a=this.ticks;if(!this.isLinked||b>=this.min&&b<=this.max||this.grid&&this.grid.isColumn)a[b]||(a[b]=new H(this,\nb)),f&&a[b].isNew&&a[b].render(c,!0,-1),a[b].render(c)}render(){const c=this,f=c.chart,a=c.logarithmic,d=c.options,n=c.isLinked,e=c.tickPositions,q=c.axisTitle,g=c.ticks,h=c.minorTicks,r=c.alternateBands,l=d.stackLabels,m=d.alternateGridColor;var p=d.crossing;const D=c.tickmarkOffset,t=c.axisLine,w=c.showAxis,K=u(f.renderer.globalAnimation);let Q,G;c.labelEdge.length=0;c.overlap=!1;[g,h,r].forEach(function(b){P(b,function(b){b.isActive=!1})});if(b(p)){const b=this.isXAxis?f.yAxis[0]:f.xAxis[0],a=\n[1,-1,-1,1][this.side];b&&(p=b.toPixels(p,!0),c.horiz&&(p=b.len-p),c.offset=a*p)}if(c.hasData()||n){const n=c.chart.hasRendered&&c.old&&b(c.old.min);c.minorTickInterval&&!c.categories&&c.getMinorTickPositions().forEach(function(b){c.renderMinorTick(b,n)});e.length&&(e.forEach(function(b,f){c.renderTick(b,f,n)}),D&&(0===c.min||c.single)&&(g[-1]||(g[-1]=new H(c,-1,null,!0)),g[-1].render(-1)));m&&e.forEach(function(b,d){G=\"undefined\"!==typeof e[d+1]?e[d+1]+D:c.max-D;0===d%2&&b<c.max&&G<=c.max+(f.polar?\n-D:D)&&(r[b]||(r[b]=new z.PlotLineOrBand(c)),Q=b+D,r[b].options={from:a?a.lin2log(Q):Q,to:a?a.lin2log(G):G,color:m,className:\"highcharts-alternate-grid\"},r[b].render(),r[b].isActive=!0)});c._addedPlotLB||(c._addedPlotLB=!0,(d.plotLines||[]).concat(d.plotBands||[]).forEach(function(b){c.addPlotBandOrLine(b)}))}[g,h,r].forEach(function(b){const c=[],a=K.duration;P(b,function(b,f){b.isActive||(b.render(f,!1,0),b.isActive=!1,c.push(f))});Z(function(){let f=c.length;for(;f--;)b[c[f]]&&!b[c[f]].isActive&&\n(b[c[f]].destroy(),delete b[c[f]])},b!==r&&f.hasRendered&&a?a:0)});t&&(t[t.isPlaced?\"animate\":\"attr\"]({d:this.getLinePath(t.strokeWidth())}),t.isPlaced=!0,t[w?\"show\":\"hide\"](w));q&&w&&(q[q.isNew?\"attr\":\"animate\"](c.getTitlePosition(q)),q.isNew=!1);l&&l.enabled&&c.stacking&&c.stacking.renderStackTotals();c.old={len:c.len,max:c.max,min:c.min,transA:c.transA,userMax:c.userMax,userMin:c.userMin};c.isDirty=!1;k(this,\"afterRender\")}redraw(){this.visible&&(this.render(),this.plotLinesAndBands.forEach(function(b){b.render()}));\nthis.series.forEach(function(b){b.isDirty=!0})}getKeepProps(){return this.keepProps||Y.keepProps}destroy(b){const c=this,f=c.plotLinesAndBands,a=this.eventOptions;k(this,\"destroy\",{keepEvents:b});b||X(c);[c.ticks,c.minorTicks,c.alternateBands].forEach(function(b){w(b)});if(f)for(b=f.length;b--;)f[b].destroy();\"axisLine axisTitle axisGroup gridGroup labelGroup cross scrollbar\".split(\" \").forEach(function(b){c[b]&&(c[b]=c[b].destroy())});for(const b in c.plotLinesAndBandsGroups)c.plotLinesAndBandsGroups[b]=\nc.plotLinesAndBandsGroups[b].destroy();P(c,function(b,f){-1===c.getKeepProps().indexOf(f)&&delete c[f]});this.eventOptions=a}drawCrosshair(b,c){const f=this.crosshair;var a=D(f&&f.snap,!0);const n=this.chart;let q,g=this.cross;k(this,\"drawCrosshair\",{e:b,point:c});b||(b=this.cross&&this.cross.e);if(f&&!1!==(e(c)||!a)){a?e(c)&&(q=D(\"colorAxis\"!==this.coll?c.crosshairPos:null,this.isXAxis?c.plotX:this.len-c.plotY)):q=b&&(this.horiz?b.chartX-this.pos:this.len-b.chartY+this.pos);if(e(q)){var h={value:c&&\n(this.isXAxis?c.x:D(c.stackY,c.y)),translatedValue:q};n.polar&&d(h,{isCrosshair:!0,chartX:b&&b.chartX,chartY:b&&b.chartY,point:c});h=this.getPlotLinePath(h)||null}if(!e(h)){this.hideCrosshair();return}a=this.categories&&!this.isRadial;g||(this.cross=g=n.renderer.path().addClass(\"highcharts-crosshair highcharts-crosshair-\"+(a?\"category \":\"thin \")+(f.className||\"\")).attr({zIndex:D(f.zIndex,2)}).add(),n.styledMode||(g.attr({stroke:f.color||(a?I.parse(\"#ccd3ff\").setOpacity(.25).get():\"#cccccc\"),\"stroke-width\":D(f.width,\n1)}).css({\"pointer-events\":\"none\"}),f.dashStyle&&g.attr({dashstyle:f.dashStyle})));g.show().attr({d:h});a&&!f.width&&g.attr({\"stroke-width\":this.transA});this.cross.e=b}else this.hideCrosshair();k(this,\"afterDrawCrosshair\",{e:b,point:c})}hideCrosshair(){this.cross&&this.cross.hide();k(this,\"afterHideCrosshair\")}hasVerticalPanning(){const b=this.chart.options.chart.panning;return!!(b&&b.enabled&&/y/.test(b.type))}update(b,f){const a=this.chart;b=c(this.userOptions,b);this.destroy(!0);this.init(a,b);\na.isDirtyBox=!0;D(f,!0)&&a.redraw()}remove(b){const c=this.chart,f=this.coll,a=this.series;let d=a.length;for(;d--;)a[d]&&a[d].remove(!1);x(c.axes,this);x(c[f]||[],this);c.orderItems(f);this.destroy();c.isDirtyBox=!0;D(b,!0)&&c.redraw()}setTitle(b,c){this.update({title:b},c)}setCategories(b,c){this.update({categories:b},c)}}Y.defaultOptions=y.defaultXAxisOptions;Y.keepProps=\"coll extKey hcEvents names series userMax userMin\".split(\" \");\"\";return Y});M(a,\"Core/Axis/DateTimeAxis.js\",[a[\"Core/Utilities.js\"]],\nfunction(a){const {addEvent:x,getMagnitude:I,normalizeTickInterval:L,timeUnits:C}=a;var z;(function(y){function B(){return this.chart.time.getTimeTicks.apply(this.chart.time,arguments)}function u(a){\"datetime\"!==a.userOptions.type?this.dateTime=void 0:this.dateTime||(this.dateTime=new l(this))}const v=[];y.compose=function(l){a.pushUnique(v,l)&&(l.keepProps.push(\"dateTime\"),l.prototype.getTimeTicks=B,x(l,\"init\",u));return l};class l{constructor(a){this.axis=a}normalizeTimeTickInterval(a,l){const m=\nl||[[\"millisecond\",[1,2,5,10,20,25,50,100,200,500]],[\"second\",[1,2,5,10,15,30]],[\"minute\",[1,2,5,10,15,30]],[\"hour\",[1,2,3,4,6,8,12]],[\"day\",[1,2]],[\"week\",[1,2]],[\"month\",[1,2,3,4,6]],[\"year\",null]];l=m[m.length-1];let h=C[l[0]],g=l[1],e;for(e=0;e<m.length&&!(l=m[e],h=C[l[0]],g=l[1],m[e+1]&&a<=(h*g[g.length-1]+C[m[e+1][0]])/2);e++);h===C.year&&a<5*h&&(g=[1,2,5]);a=L(a/h,g,\"year\"===l[0]?Math.max(I(a/h),1):1);return{unitRange:h,count:a,unitName:l[0]}}getXDateFormat(a,l){const {axis:m}=this,h=m.chart.time;\nreturn m.closestPointRange?h.getDateFormat(m.closestPointRange,a,m.options.startOfWeek,l)||h.resolveDTLFormat(l.year).main:h.resolveDTLFormat(l.day).main}}y.Additions=l})(z||(z={}));return z});M(a,\"Core/Axis/LogarithmicAxis.js\",[a[\"Core/Utilities.js\"]],function(a){const {addEvent:x,normalizeTickInterval:I,pick:L}=a;var C;(function(y){function H(a){let l=this.logarithmic;\"logarithmic\"!==a.userOptions.type?this.logarithmic=void 0:l||(this.logarithmic=new v(this))}function B(){const a=this.logarithmic;\na&&(this.lin2val=function(l){return a.lin2log(l)},this.val2lin=function(l){return a.log2lin(l)})}const u=[];y.compose=function(l){a.pushUnique(u,l)&&(l.keepProps.push(\"logarithmic\"),x(l,\"init\",H),x(l,\"afterInit\",B));return l};class v{constructor(a){this.axis=a}getLogTickPositions(a,p,t,m){const h=this.axis;var g=h.len,e=h.options;let l=[];m||(this.minorAutoInterval=void 0);if(.5<=a)a=Math.round(a),l=h.getLinearTickPositions(a,p,t);else if(.08<=a){e=Math.floor(p);let h,w,d,k,r;for(g=.3<a?[1,2,4]:.15<\na?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];e<t+1&&!r;e++)for(w=g.length,h=0;h<w&&!r;h++)d=this.log2lin(this.lin2log(e)*g[h]),d>p&&(!m||k<=t)&&\"undefined\"!==typeof k&&l.push(k),k>t&&(r=!0),k=d}else p=this.lin2log(p),t=this.lin2log(t),a=m?h.getMinorTickInterval():e.tickInterval,a=L(\"auto\"===a?null:a,this.minorAutoInterval,e.tickPixelInterval/(m?5:1)*(t-p)/((m?g/h.tickPositions.length:g)||1)),a=I(a),l=h.getLinearTickPositions(a,p,t).map(this.log2lin),m||(this.minorAutoInterval=a/5);m||(h.tickInterval=a);return l}lin2log(a){return Math.pow(10,\na)}log2lin(a){return Math.log(a)/Math.LN10}}y.Additions=v})(C||(C={}));return C});M(a,\"Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js\",[a[\"Core/Utilities.js\"]],function(a){const {erase:x,extend:I,isNumber:L}=a;var C;(function(y){function H(a){return this.addPlotBandOrLine(a,\"plotBands\")}function B(a,e){const g=this.userOptions;let l=new h(this,a);this.visible&&(l=l.render());if(l){this._addedPlotLB||(this._addedPlotLB=!0,(g.plotLines||[]).concat(g.plotBands||[]).forEach(a=>{this.addPlotBandOrLine(a)}));\nif(e){const h=g[e]||[];h.push(a);g[e]=h}this.plotLinesAndBands.push(l)}return l}function u(a){return this.addPlotBandOrLine(a,\"plotLines\")}function v(a,e,h=this.options){const g=this.getPlotLinePath({value:e,force:!0,acrossPanes:h.acrossPanes}),l=[],d=this.horiz;e=!L(this.min)||!L(this.max)||a<this.min&&e<this.min||a>this.max&&e>this.max;a=this.getPlotLinePath({value:a,force:!0,acrossPanes:h.acrossPanes});h=1;let k;if(a&&g)for(e&&(k=a.toString()===g.toString(),h=0),e=0;e<a.length;e+=2){const r=a[e],\nq=a[e+1],m=g[e],b=g[e+1];\"M\"!==r[0]&&\"L\"!==r[0]||\"M\"!==q[0]&&\"L\"!==q[0]||\"M\"!==m[0]&&\"L\"!==m[0]||\"M\"!==b[0]&&\"L\"!==b[0]||(d&&m[1]===r[1]?(m[1]+=h,b[1]+=h):d||m[2]!==r[2]||(m[2]+=h,b[2]+=h),l.push([\"M\",r[1],r[2]],[\"L\",q[1],q[2]],[\"L\",b[1],b[2]],[\"L\",m[1],m[2]],[\"Z\"]));l.isFlat=k}return l}function l(a){this.removePlotBandOrLine(a)}function p(a){const e=this.plotLinesAndBands,g=this.options,h=this.userOptions;if(e){let l=e.length;for(;l--;)e[l].id===a&&e[l].destroy();[g.plotLines||[],h.plotLines||[],\ng.plotBands||[],h.plotBands||[]].forEach(function(d){for(l=d.length;l--;)(d[l]||{}).id===a&&x(d,d[l])})}}function t(a){this.removePlotBandOrLine(a)}const m=[];let h;y.compose=function(g,e){h||(h=g);a.pushUnique(m,e)&&I(e.prototype,{addPlotBand:H,addPlotLine:u,addPlotBandOrLine:B,getPlotBandPath:v,removePlotBand:l,removePlotLine:t,removePlotBandOrLine:p});return e}})(C||(C={}));return C});M(a,\"Core/Axis/PlotLineOrBand/PlotLineOrBand.js\",[a[\"Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js\"],a[\"Core/Utilities.js\"]],\nfunction(a,y){const {arrayMax:x,arrayMin:L,defined:C,destroyObjectProperties:z,erase:H,fireEvent:B,merge:u,objectEach:v,pick:l}=y;class p{static compose(l){return a.compose(p,l)}constructor(a,l){this.axis=a;l&&(this.options=l,this.id=l.id)}render(){B(this,\"render\");const a=this,m=a.axis,h=m.horiz;var g=m.logarithmic;const e=a.options,p=e.color,x=l(e.zIndex,0),F=e.events,d={},k=m.chart.renderer;let r=e.label,q=a.label,G=e.to,b=e.from,f=e.value,c=a.svgElem;var n=[];const P=C(b)&&C(G);n=C(f);const D=\n!c,K={\"class\":\"highcharts-plot-\"+(P?\"band \":\"line \")+(e.className||\"\")};let X=P?\"bands\":\"lines\";g&&(b=g.log2lin(b),G=g.log2lin(G),f=g.log2lin(f));m.chart.styledMode||(n?(K.stroke=p||\"#999999\",K[\"stroke-width\"]=l(e.width,1),e.dashStyle&&(K.dashstyle=e.dashStyle)):P&&(K.fill=p||\"#e6e9ff\",e.borderWidth&&(K.stroke=e.borderColor,K[\"stroke-width\"]=e.borderWidth)));d.zIndex=x;X+=\"-\"+x;(g=m.plotLinesAndBandsGroups[X])||(m.plotLinesAndBandsGroups[X]=g=k.g(\"plot-\"+X).attr(d).add());D&&(a.svgElem=c=k.path().attr(K).add(g));\nif(n)n=m.getPlotLinePath({value:f,lineWidth:c.strokeWidth(),acrossPanes:e.acrossPanes});else if(P)n=m.getPlotBandPath(b,G,e);else return;!a.eventsAdded&&F&&(v(F,function(b,f){c.on(f,function(b){F[f].apply(a,[b])})}),a.eventsAdded=!0);(D||!c.d)&&n&&n.length?c.attr({d:n}):c&&(n?(c.show(),c.animate({d:n})):c.d&&(c.hide(),q&&(a.label=q=q.destroy())));r&&(C(r.text)||C(r.formatter))&&n&&n.length&&0<m.width&&0<m.height&&!n.isFlat?(r=u({align:h&&P&&\"center\",x:h?!P&&4:10,verticalAlign:!h&&P&&\"middle\",y:h?\nP?16:10:P?6:-4,rotation:h&&!P&&90},r),this.renderLabel(r,n,P,x)):q&&q.hide();return a}renderLabel(a,l,h,g){const e=this.axis;var m=e.chart.renderer;let p=this.label;p||(this.label=p=m.text(this.getLabelText(a),0,0,a.useHTML).attr({align:a.textAlign||a.align,rotation:a.rotation,\"class\":\"highcharts-plot-\"+(h?\"band\":\"line\")+\"-label \"+(a.className||\"\"),zIndex:g}).add(),e.chart.styledMode||p.css(u({fontSize:\"0.8em\",textOverflow:\"ellipsis\"},a.style)));g=l.xBounds||[l[0][1],l[1][1],h?l[2][1]:l[0][1]];l=\nl.yBounds||[l[0][2],l[1][2],h?l[2][2]:l[0][2]];h=L(g);m=L(l);p.align(a,!1,{x:h,y:m,width:x(g)-h,height:x(l)-m});p.alignValue&&\"left\"!==p.alignValue||(a=a.clip?e.width:e.chart.chartWidth,p.css({width:(90===p.rotation?e.height-(p.alignAttr.y-e.top):a-(p.alignAttr.x-e.left))+\"px\"}));p.show(!0)}getLabelText(a){return C(a.formatter)?a.formatter.call(this):a.text}destroy(){H(this.axis.plotLinesAndBands,this);delete this.axis;z(this)}}\"\";\"\";return p});M(a,\"Core/Tooltip.js\",[a[\"Core/Templating.js\"],a[\"Core/Globals.js\"],\na[\"Core/Renderer/RendererUtilities.js\"],a[\"Core/Renderer/RendererRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C){const {format:x}=a,{doc:H,isSafari:B}=y,{distribute:u}=I,{addEvent:v,clamp:l,css:p,discardElement:t,extend:m,fireEvent:h,isArray:g,isNumber:e,isString:w,merge:E,pick:F,splat:d,syncTimeout:k}=C;class r{constructor(a,d){this.allowShared=!0;this.container=void 0;this.crosshairs=[];this.distance=0;this.isHidden=!0;this.isSticky=!1;this.now={};this.options={};this.outside=!1;this.chart=\na;this.init(a,d)}bodyFormatter(a){return a.map(function(a){const b=a.series.tooltipOptions;return(b[(a.point.formatPrefix||\"point\")+\"Formatter\"]||a.point.tooltipFormatter).call(a.point,b[(a.point.formatPrefix||\"point\")+\"Format\"]||\"\")})}cleanSplit(a){this.chart.series.forEach(function(d){const b=d&&d.tt;b&&(!b.isActive||a?d.tt=b.destroy():b.isActive=!1)})}defaultFormatter(a){const k=this.points||d(this);let b;b=[a.tooltipFooterHeaderFormatter(k[0])];b=b.concat(a.bodyFormatter(k));b.push(a.tooltipFooterHeaderFormatter(k[0],\n!0));return b}destroy(){this.label&&(this.label=this.label.destroy());this.split&&(this.cleanSplit(!0),this.tt&&(this.tt=this.tt.destroy()));this.renderer&&(this.renderer=this.renderer.destroy(),t(this.container));C.clearTimeout(this.hideTimer);C.clearTimeout(this.tooltipTimeout)}getAnchor(a,k){var b=this.chart;const f=b.pointer,c=b.inverted,n=b.plotTop;b=b.plotLeft;a=d(a);a[0].series&&a[0].series.yAxis&&!a[0].series.yAxis.options.reversedStacks&&(a=a.slice().reverse());if(this.followPointer&&k)\"undefined\"===\ntypeof k.chartX&&(k=f.normalize(k)),a=[k.chartX-b,k.chartY-n];else if(a[0].tooltipPos)a=a[0].tooltipPos;else{let f=0,d=0;a.forEach(function(b){if(b=b.pos(!0))f+=b[0],d+=b[1]});f/=a.length;d/=a.length;this.shared&&1<a.length&&k&&(c?f=k.chartX:d=k.chartY);a=[f-b,d-n]}return a.map(Math.round)}getClassName(a,d,b){const f=a.series,c=f.options;return[this.options.className,\"highcharts-label\",b&&\"highcharts-tooltip-header\",d?\"highcharts-tooltip-box\":\"highcharts-tooltip\",!b&&\"highcharts-color-\"+F(a.colorIndex,\nf.colorIndex),c&&c.className].filter(w).join(\" \")}getLabel(){const a=this,d=this.chart.styledMode,b=this.options,f=this.split&&this.allowShared,c=b.style.pointerEvents||(this.shouldStickOnContact()?\"auto\":\"none\");let n,k=this.chart.renderer;if(this.label){var e=!this.label.hasClass(\"highcharts-label\");(!f&&e||f&&!e)&&this.destroy()}if(!this.label){if(this.outside){e=this.chart.options.chart.style;const b=L.getRendererType();this.container=n=y.doc.createElement(\"div\");n.className=\"highcharts-tooltip-container\";\np(n,{position:\"absolute\",top:\"1px\",pointerEvents:c,zIndex:Math.max(this.options.style.zIndex||0,(e&&e.zIndex||0)+3)});y.doc.body.appendChild(n);this.renderer=k=new b(n,0,0,e,void 0,void 0,k.styledMode)}f?this.label=k.g(\"tooltip\"):(this.label=k.label(\"\",0,0,b.shape,void 0,void 0,b.useHTML,void 0,\"tooltip\").attr({padding:b.padding,r:b.borderRadius}),d||this.label.attr({fill:b.backgroundColor,\"stroke-width\":b.borderWidth||0}).css(b.style).css({pointerEvents:c}));if(a.outside){const b=this.label,{xSetter:c,\nySetter:f}=b;b.xSetter=function(f){c.call(b,a.distance);n.style.left=f+\"px\"};b.ySetter=function(c){f.call(b,a.distance);n.style.top=c+\"px\"}}this.label.attr({zIndex:8}).shadow(b.shadow).add()}return this.label}getPlayingField(){const {body:a,documentElement:d}=H,{chart:b,distance:f,outside:c}=this;return{width:c?Math.max(a.scrollWidth,d.scrollWidth,a.offsetWidth,d.offsetWidth,d.clientWidth)-2*f:b.chartWidth,height:c?Math.max(a.scrollHeight,d.scrollHeight,a.offsetHeight,d.offsetHeight,d.clientHeight):\nb.chartHeight}}getPosition(a,d,b){const f=this.chart,c=this.distance,n={},k=f.inverted&&b.h||0,e=this.outside;var q=this.getPlayingField();const g=q.width,h=q.height,r=f.pointer.getChartPosition();q=n=>{const k=\"x\"===n;return[n,k?g:h,k?a:d].concat(e?[k?a*r.scaleX:d*r.scaleY,k?r.left-c+(b.plotX+f.plotLeft)*r.scaleX:r.top-c+(b.plotY+f.plotTop)*r.scaleY,0,k?g:h]:[k?a:d,k?b.plotX+f.plotLeft:b.plotY+f.plotTop,k?f.plotLeft:f.plotTop,k?f.plotLeft+f.plotWidth:f.plotTop+f.plotHeight])};let l=q(\"y\"),m=q(\"x\"),\np;q=!!b.negative;!f.polar&&f.hoverSeries&&f.hoverSeries.yAxis&&f.hoverSeries.yAxis.reversed&&(q=!q);const t=!this.followPointer&&F(b.ttBelow,!f.inverted===q),w=function(b,a,f,d,q,g,h){const l=e?\"y\"===b?c*r.scaleY:c*r.scaleX:c,m=(f-d)/2,p=d<q-c,J=q+c+d<a,D=q-l-f+m;q=q+l-m;if(t&&J)n[b]=q;else if(!t&&p)n[b]=D;else if(p)n[b]=Math.min(h-d,0>D-k?D:D-k);else if(J)n[b]=Math.max(g,q+k+f>a?q:q+k);else return!1},G=function(b,a,f,d,k){let e;k<c||k>a-c?e=!1:n[b]=k<f/2?1:k>a-d/2?a-d-2:k-f/2;return e},v=function(b){const c=\nl;l=m;m=c;p=b},J=function(){!1!==w.apply(0,l)?!1!==G.apply(0,m)||p||(v(!0),J()):p?n.x=n.y=0:(v(!0),J())};(f.inverted||1<this.len)&&v();J();return n}hide(a){const d=this;C.clearTimeout(this.hideTimer);a=F(a,this.options.hideDelay);this.isHidden||(this.hideTimer=k(function(){d.getLabel().fadeOut(a?void 0:a);d.isHidden=!0},a))}init(a,d){this.chart=a;this.options=d;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.split=d.split&&!a.inverted&&!a.polar;this.shared=d.shared||this.split;this.outside=\nF(d.outside,!(!a.scrollablePixelsX&&!a.scrollablePixelsY))}shouldStickOnContact(a){return!(this.followPointer||!this.options.stickOnContact||a&&!this.chart.pointer.inClass(a.target,\"highcharts-tooltip\"))}move(a,d,b,f){const c=this,n=c.now,k=!1!==c.options.animation&&!c.isHidden&&(1<Math.abs(a-n.x)||1<Math.abs(d-n.y)),e=c.followPointer||1<c.len;m(n,{x:k?(2*n.x+a)/3:a,y:k?(n.y+d)/2:d,anchorX:e?void 0:k?(2*n.anchorX+b)/3:b,anchorY:e?void 0:k?(n.anchorY+f)/2:f});c.getLabel().attr(n);c.drawTracker();k&&\n(C.clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){c&&c.move(a,d,b,f)},32))}refresh(a,k){const b=this.chart,f=this.options,c=b.pointer,n=d(a),e=n[0],q=[];var r=f.format,l=f.formatter||this.defaultFormatter;const m=this.shared,p=b.styledMode;let t={};if(f.enabled&&e.series){C.clearTimeout(this.hideTimer);this.allowShared=!(!g(a)&&a.series&&a.series.noSharedTooltip);this.followPointer=!this.split&&e.series.tooltipOptions.followPointer;a=this.getAnchor(a,k);var v=a[0],G=a[1];\nm&&this.allowShared?(c.applyInactiveState(n),n.forEach(function(b){b.setState(\"hover\");q.push(b.getLabelConfig())}),t=e.getLabelConfig(),t.points=q):t=e.getLabelConfig();this.len=q.length;r=w(r)?x(r,t,b):l.call(t,this);l=e.series;this.distance=F(l.tooltipOptions.distance,16);if(!1===r)this.hide();else{if(this.split&&this.allowShared)this.renderSplit(r,n);else{let d=v,g=G;k&&c.isDirectTouch&&(d=k.chartX-b.plotLeft,g=k.chartY-b.plotTop);if(b.polar||!1===l.options.clip||n.some(b=>c.isDirectTouch||b.series.shouldShowTooltip(d,\ng)))k=this.getLabel(),f.style.width&&!p||k.css({width:(this.outside?this.getPlayingField():b.spacingBox).width+\"px\"}),k.attr({text:r&&r.join?r.join(\"\"):r}),k.addClass(this.getClassName(e),!0),p||k.attr({stroke:f.borderColor||e.color||l.color||\"#666666\"}),this.updatePosition({plotX:v,plotY:G,negative:e.negative,ttBelow:e.ttBelow,h:a[2]||0});else{this.hide();return}}this.isHidden&&this.label&&this.label.attr({opacity:1}).show();this.isHidden=!1}h(this,\"refresh\")}}renderSplit(a,d){function b(b,c,a,d,\nn=!0){a?(c=S?0:z,b=l(b-d/2,J.left,J.right-d-(f.outside?W:0))):(c-=da,b=n?b-d-x:b+x,b=l(b,n?b:J.left,J.right));return{x:b,y:c}}const f=this,{chart:c,chart:{chartWidth:n,chartHeight:k,plotHeight:e,plotLeft:g,plotTop:h,pointer:q,scrollablePixelsY:r=0,scrollablePixelsX:p,scrollingContainer:{scrollLeft:t,scrollTop:v}={scrollLeft:0,scrollTop:0},styledMode:G},distance:x,options:E,options:{positioner:y}}=f,J=f.outside&&\"number\"!==typeof p?H.documentElement.getBoundingClientRect():{left:t,right:t+n,top:v,\nbottom:v+k},N=f.getLabel(),O=this.renderer||c.renderer,S=!(!c.xAxis[0]||!c.xAxis[0].opposite),{left:W,top:ha}=q.getChartPosition();let da=h+v,C=0,z=e-r;w(a)&&(a=[!1,a]);a=a.slice(0,d.length+1).reduce(function(c,a,n){if(!1!==a&&\"\"!==a){n=d[n-1]||{isHeader:!0,plotX:d[0].plotX,plotY:e,series:{}};const D=n.isHeader;var k=D?f:n.series,q;{var r=n;a=a.toString();var m=k.tt;const {isHeader:b,series:c}=r;m||(m={padding:E.padding,r:E.borderRadius},G||(m.fill=E.backgroundColor,m[\"stroke-width\"]=null!==(q=E.borderWidth)&&\nvoid 0!==q?q:1),m=O.label(\"\",0,0,E[b?\"headerShape\":\"shape\"],void 0,void 0,E.useHTML).addClass(f.getClassName(r,!0,b)).attr(m).add(N));m.isActive=!0;m.attr({text:a});G||m.css(E.style).attr({stroke:E.borderColor||r.color||c.color||\"#333333\"});q=m}q=k.tt=q;r=q.getBBox();k=r.width+q.strokeWidth();D&&(C=r.height,z+=C,S&&(da-=C));{const {isHeader:b,plotX:c=0,plotY:f=0,series:d}=n;if(b){a=g+c;var p=h+e/2}else{const {xAxis:b,yAxis:n}=d;a=b.pos+l(c,-x,b.len+x);d.shouldShowTooltip(0,n.pos-h+f,{ignoreX:!0})&&\n(p=n.pos+f)}a=l(a,J.left-x,J.right+x);p={anchorX:a,anchorY:p}}const {anchorX:t,anchorY:Q}=p;\"number\"===typeof Q?(p=r.height+1,r=y?y.call(f,k,p,n):b(t,Q,D,k),c.push({align:y?0:void 0,anchorX:t,anchorY:Q,boxWidth:k,point:n,rank:F(r.rank,D?1:0),size:p,target:r.y,tt:q,x:r.x})):q.isActive=!1}return c},[]);!y&&a.some(b=>{var {outside:c}=f;c=(c?W:0)+b.anchorX;return c<J.left&&c+b.boxWidth<J.right?!0:c<W-J.left+b.boxWidth&&J.right-c>c})&&(a=a.map(c=>{const {x:a,y:f}=b(c.anchorX,c.anchorY,c.point.isHeader,\nc.boxWidth,!1);return m(c,{target:f,x:a})}));f.cleanSplit();u(a,z);var ca=W,L=W;a.forEach(function(b){const {x:c,boxWidth:a,isHeader:d}=b;d||(f.outside&&W+c<ca&&(ca=W+c),!d&&f.outside&&ca+a>L&&(L=W+c))});a.forEach(function(b){const {x:c,anchorX:a,anchorY:d,pos:n,point:{isHeader:k}}=b,e={visibility:\"undefined\"===typeof n?\"hidden\":\"inherit\",x:c,y:(n||0)+da,anchorX:a,anchorY:d};if(f.outside&&c<a){const b=W-ca;0<b&&(k||(e.x=c+b,e.anchorX=a+b),k&&(e.x=(L-ca)/2,e.anchorX=a+b))}b.tt.attr(e)});const {container:R,\noutside:Q,renderer:la}=f;if(Q&&R&&la){const {width:b,height:c,x:a,y:f}=N.getBBox();la.setSize(b+a,c+f,!1);R.style.left=ca+\"px\";R.style.top=ha+\"px\"}B&&N.attr({opacity:1===N.opacity?.999:1})}drawTracker(){if(this.shouldStickOnContact()){var a=this.chart,d=this.label,b=this.shared?a.hoverPoints:a.hoverPoint;if(d&&b){var f={x:0,y:0,width:0,height:0};b=this.getAnchor(b);var c=d.getBBox();b[0]+=a.plotLeft-d.translateX;b[1]+=a.plotTop-d.translateY;f.x=Math.min(0,b[0]);f.y=Math.min(0,b[1]);f.width=0>b[0]?\nMath.max(Math.abs(b[0]),c.width-b[0]):Math.max(Math.abs(b[0]),c.width);f.height=0>b[1]?Math.max(Math.abs(b[1]),c.height-Math.abs(b[1])):Math.max(Math.abs(b[1]),c.height);this.tracker?this.tracker.attr(f):(this.tracker=d.renderer.rect(f).addClass(\"highcharts-tracker\").add(d),a.styledMode||this.tracker.attr({fill:\"rgba(0,0,0,0)\"}))}}else this.tracker&&(this.tracker=this.tracker.destroy())}styledModeFormat(a){return a.replace('style=\"font-size: 0.8em\"','class=\"highcharts-header\"').replace(/style=\"color:{(point|series)\\.color}\"/g,\n'class=\"highcharts-color-{$1.colorIndex} {series.options.className} {point.options.className}\"')}tooltipFooterHeaderFormatter(a,d){const b=a.series,f=b.tooltipOptions;var c=b.xAxis;const n=c&&c.dateTime;c={isFooter:d,labelConfig:a};let k=f.xDateFormat,g=f[d?\"footerFormat\":\"headerFormat\"];h(this,\"headerFormatter\",c,function(c){n&&!k&&e(a.key)&&(k=n.getXDateFormat(a.key,f.dateTimeLabelFormats));n&&k&&(a.point&&a.point.tooltipDateKeys||[\"key\"]).forEach(function(b){g=g.replace(\"{point.\"+b+\"}\",\"{point.\"+\nb+\":\"+k+\"}\")});b.chart.styledMode&&(g=this.styledModeFormat(g));c.text=x(g,{point:a,series:b},this.chart)});return c.text}update(a){this.destroy();this.init(this.chart,E(!0,this.options,a))}updatePosition(a){const {chart:d,distance:b,options:f}=this;var c=d.pointer;const n=this.getLabel(),{left:k,top:e,scaleX:g,scaleY:h}=c.getChartPosition();c=(f.positioner||this.getPosition).call(this,n.width,n.height,a);let q=(a.plotX||0)+d.plotLeft;a=(a.plotY||0)+d.plotTop;let r;if(this.outside){f.positioner&&\n(c.x+=k-b,c.y+=e-b);r=(f.borderWidth||0)+2*b;this.renderer.setSize(n.width+r,n.height+r,!1);if(1!==g||1!==h)p(this.container,{transform:`scale(${g}, ${h})`}),q*=g,a*=h;q+=k-c.x;a+=e-c.y}this.move(Math.round(c.x),Math.round(c.y||0),q,a)}}(function(a){const d=[];a.compose=function(b){C.pushUnique(d,b)&&v(b,\"afterInit\",function(){const b=this.chart;b.options.tooltip&&(b.tooltip=new a(b,b.options.tooltip))})}})(r||(r={}));\"\";return r});M(a,\"Core/Series/Point.js\",[a[\"Core/Renderer/HTML/AST.js\"],a[\"Core/Animation/AnimationUtilities.js\"],\na[\"Core/Defaults.js\"],a[\"Core/Templating.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C){const {animObject:x}=y,{defaultOptions:H}=I,{format:B}=L,{addEvent:u,defined:v,erase:l,extend:p,fireEvent:t,getNestedProperty:m,isArray:h,isFunction:g,isNumber:e,isObject:w,merge:E,objectEach:F,pick:d,syncTimeout:k,removeEvent:r,uniqueKey:q}=C;class G{constructor(){this.category=void 0;this.destroyed=!1;this.formatPrefix=\"point\";this.id=void 0;this.isNull=!1;this.percentage=this.options=this.name=void 0;this.selected=\n!1;this.total=this.shapeArgs=this.series=void 0;this.visible=!0;this.x=void 0}animateBeforeDestroy(){const b=this,a={x:b.startXPos,opacity:0},c=b.getGraphicalProps();c.singular.forEach(function(c){b[c]=b[c].animate(\"dataLabel\"===c?{x:b[c].startXPos,y:b[c].startYPos,opacity:0}:a)});c.plural.forEach(function(c){b[c].forEach(function(c){c.element&&c.animate(p({x:b.startXPos},c.startYPos?{x:c.startXPos,y:c.startYPos}:{}))})})}applyOptions(b,a){const c=this.series,f=c.options.pointValKey||c.pointValKey;\nb=G.prototype.optionsToObject.call(this,b);p(this,b);this.options=this.options?p(this.options,b):b;b.group&&delete this.group;b.dataLabels&&delete this.dataLabels;f&&(this.y=G.prototype.getNestedProperty.call(this,f));this.formatPrefix=(this.isNull=this.isValid&&!this.isValid())?\"null\":\"point\";this.selected&&(this.state=\"select\");\"name\"in this&&\"undefined\"===typeof a&&c.xAxis&&c.xAxis.hasNames&&(this.x=c.xAxis.nameToX(this));\"undefined\"===typeof this.x&&c?this.x=\"undefined\"===typeof a?c.autoIncrement():\na:e(b.x)&&c.options.relativeXValue&&(this.x=c.autoIncrement(b.x));return this}destroy(){if(!this.destroyed){const a=this;var b=a.series;const c=b.chart;b=b.options.dataSorting;const d=c.hoverPoints,e=x(a.series.chart.renderer.globalAnimation),g=()=>{if(a.graphic||a.graphics||a.dataLabel||a.dataLabels)r(a),a.destroyElements();for(const b in a)delete a[b]};a.legendItem&&c.legend.destroyItem(a);d&&(a.setState(),l(d,a),d.length||(c.hoverPoints=null));if(a===c.hoverPoint)a.onMouseOut();b&&b.enabled?(this.animateBeforeDestroy(),\nk(g,e.duration)):g();c.pointCount--}this.destroyed=!0}destroyElements(b){const a=this;b=a.getGraphicalProps(b);b.singular.forEach(function(b){a[b]=a[b].destroy()});b.plural.forEach(function(b){a[b].forEach(function(b){b&&b.element&&b.destroy()});delete a[b]})}firePointEvent(b,a,c){const f=this,d=this.series.options;(d.point.events[b]||f.options&&f.options.events&&f.options.events[b])&&f.importEvents();\"click\"===b&&d.allowPointSelect&&(c=function(b){f.select&&f.select(null,b.ctrlKey||b.metaKey||b.shiftKey)});\nt(f,b,a,c)}getClassName(){return\"highcharts-point\"+(this.selected?\" highcharts-point-select\":\"\")+(this.negative?\" highcharts-negative\":\"\")+(this.isNull?\" highcharts-null-point\":\"\")+(\"undefined\"!==typeof this.colorIndex?\" highcharts-color-\"+this.colorIndex:\"\")+(this.options.className?\" \"+this.options.className:\"\")+(this.zone&&this.zone.className?\" \"+this.zone.className.replace(\"highcharts-negative\",\"\"):\"\")}getGraphicalProps(b){const a=this,c=[],d={singular:[],plural:[]};let k,e;b=b||{graphic:1,dataLabel:1};\nb.graphic&&c.push(\"graphic\");b.dataLabel&&c.push(\"dataLabel\",\"dataLabelPath\",\"dataLabelUpper\",\"connector\");for(e=c.length;e--;)k=c[e],a[k]&&d.singular.push(k);[\"graphic\",\"dataLabel\",\"connector\"].forEach(function(c){const f=c+\"s\";b[c]&&a[f]&&d.plural.push(f)});return d}getLabelConfig(){return{x:this.category,y:this.y,color:this.color,colorIndex:this.colorIndex,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}}getNestedProperty(b){if(b)return 0===\nb.indexOf(\"custom.\")?m(b,this.options):this[b]}getZone(){var b=this.series;const a=b.zones;b=b.zoneAxis||\"y\";let c,d=0;for(c=a[d];this[b]>=c.value;)c=a[++d];this.nonZonedColor||(this.nonZonedColor=this.color);this.color=c&&c.color&&!this.options.color?c.color:this.nonZonedColor;return c}hasNewShapeType(){return(this.graphic&&(this.graphic.symbolName||this.graphic.element.nodeName))!==this.shapeType}init(b,a,c){this.series=b;this.applyOptions(a,c);this.id=v(this.id)?this.id:q();this.resolveColor();\nb.chart.pointCount++;t(this,\"afterInit\");return this}isValid(){return null!==this.x&&e(this.y)}optionsToObject(b){var a=this.series;const c=a.options.keys,d=c||a.pointArrayMap||[\"y\"],k=d.length;let g={},q=0,r=0;if(e(b)||null===b)g[d[0]]=b;else if(h(b))for(!c&&b.length>k&&(a=typeof b[0],\"string\"===a?g.name=b[0]:\"number\"===a&&(g.x=b[0]),q++);r<k;)c&&\"undefined\"===typeof b[q]||(0<d[r].indexOf(\".\")?G.prototype.setNestedProperty(g,b[q],d[r]):g[d[r]]=b[q]),q++,r++;else\"object\"===typeof b&&(g=b,b.dataLabels&&\n(a._hasPointLabels=!0),b.marker&&(a._hasPointMarkers=!0));return g}pos(b,a=this.plotY){if(!this.destroyed){const {plotX:c,series:f}=this,{chart:d,xAxis:k,yAxis:g}=f;let h=0,q=0;if(e(c)&&e(a))return b&&(h=k?k.pos:d.plotLeft,q=g?g.pos:d.plotTop),d.inverted&&k&&g?[g.len-a+q,k.len-c+h]:[c+h,a+q]}}resolveColor(){const b=this.series;var a=b.chart.styledMode;let c;var k=b.chart.options.chart.colorCount;delete this.nonZonedColor;b.options.colorByPoint?(a||(k=b.options.colors||b.chart.options.colors,c=k[b.colorCounter],\nk=k.length),a=b.colorCounter,b.colorCounter++,b.colorCounter===k&&(b.colorCounter=0)):(a||(c=b.color),a=b.colorIndex);this.colorIndex=d(this.options.colorIndex,a);this.color=d(this.options.color,c)}setNestedProperty(b,a,c){c.split(\".\").reduce(function(b,c,f,d){b[c]=d.length-1===f?a:w(b[c],!0)?b[c]:{};return b[c]},b);return b}shouldDraw(){return!this.isNull}tooltipFormatter(b){const a=this.series,c=a.tooltipOptions,k=d(c.valueDecimals,\"\"),e=c.valuePrefix||\"\",g=c.valueSuffix||\"\";a.chart.styledMode&&\n(b=a.chart.tooltip.styledModeFormat(b));(a.pointArrayMap||[\"y\"]).forEach(function(c){c=\"{point.\"+c;if(e||g)b=b.replace(RegExp(c+\"}\",\"g\"),e+c+\"}\"+g);b=b.replace(RegExp(c+\"}\",\"g\"),c+\":,.\"+k+\"f}\")});return B(b,{point:this,series:this.series},a.chart)}update(b,a,c,k){function f(){n.applyOptions(b);var f=g&&n.hasMockGraphic;f=null===n.y?!f:f;g&&f&&(n.graphic=g.destroy(),delete n.hasMockGraphic);w(b,!0)&&(g&&g.element&&b&&b.marker&&\"undefined\"!==typeof b.marker.symbol&&(n.graphic=g.destroy()),b&&b.dataLabels&&\nn.dataLabel&&(n.dataLabel=n.dataLabel.destroy()),n.connector&&(n.connector=n.connector.destroy()));r=n.index;e.updateParallelArrays(n,r);q.data[r]=w(q.data[r],!0)||w(b,!0)?n.options:d(b,q.data[r]);e.isDirty=e.isDirtyData=!0;!e.fixedBox&&e.hasCartesianSeries&&(h.isDirtyBox=!0);\"point\"===q.legendType&&(h.isDirtyLegend=!0);a&&h.redraw(c)}const n=this,e=n.series,g=n.graphic,h=e.chart,q=e.options;let r;a=d(a,!0);!1===k?f():n.firePointEvent(\"update\",{options:b},f)}remove(b,a){this.series.removePoint(this.series.data.indexOf(this),\nb,a)}select(b,a){const c=this,f=c.series,k=f.chart;this.selectedStaging=b=d(b,!c.selected);c.firePointEvent(b?\"select\":\"unselect\",{accumulate:a},function(){c.selected=c.options.selected=b;f.options.data[f.data.indexOf(c)]=c.options;c.setState(b&&\"select\");a||k.getSelectedPoints().forEach(function(b){const a=b.series;b.selected&&b!==c&&(b.selected=b.options.selected=!1,a.options.data[a.data.indexOf(b)]=b.options,b.setState(k.hoverPoints&&a.options.inactiveOtherPoints?\"inactive\":\"\"),b.firePointEvent(\"unselect\"))})});\ndelete this.selectedStaging}onMouseOver(b){const a=this.series.chart,c=a.pointer;b=b?c.normalize(b):c.getChartCoordinatesFromPoint(this,a.inverted);c.runPointActions(b,this)}onMouseOut(){const b=this.series.chart;this.firePointEvent(\"mouseOut\");this.series.options.inactiveOtherPoints||(b.hoverPoints||[]).forEach(function(b){b.setState()});b.hoverPoints=b.hoverPoint=null}importEvents(){if(!this.hasImportedEvents){const b=this,a=E(b.series.options.point,b.options).events;b.events=a;F(a,function(c,a){g(c)&&\nu(b,a,c)});this.hasImportedEvents=!0}}setState(b,f){const c=this.series;var k=this.state,g=c.options.states[b||\"normal\"]||{},h=H.plotOptions[c.type].marker&&c.options.marker;const q=h&&!1===h.enabled,r=h&&h.states&&h.states[b||\"normal\"]||{},l=!1===r.enabled,m=this.marker||{},w=c.chart,v=h&&c.markerAttribs;let G=c.halo;var u;let x;var E=c.stateMarkerGraphic;b=b||\"\";if(!(b===this.state&&!f||this.selected&&\"select\"!==b||!1===g.enabled||b&&(l||q&&!1===r.enabled)||b&&m.states&&m.states[b]&&!1===m.states[b].enabled)){this.state=\nb;v&&(u=c.markerAttribs(this,b));if(this.graphic&&!this.hasMockGraphic){k&&this.graphic.removeClass(\"highcharts-point-\"+k);b&&this.graphic.addClass(\"highcharts-point-\"+b);if(!w.styledMode){k=c.pointAttribs(this,b);x=d(w.options.chart.animation,g.animation);const a=k.opacity;c.options.inactiveOtherPoints&&e(a)&&((this.dataLabels||[]).forEach(function(b){b&&!b.hasClass(\"highcharts-data-label-hidden\")&&b.animate({opacity:a},x)}),this.connector&&this.connector.animate({opacity:a},x));this.graphic.animate(k,\nx)}u&&this.graphic.animate(u,d(w.options.chart.animation,r.animation,h.animation));E&&E.hide()}else{if(b&&r){h=m.symbol||c.symbol;E&&E.currentSymbol!==h&&(E=E.destroy());if(u)if(E)E[f?\"animate\":\"attr\"]({x:u.x,y:u.y});else h&&(c.stateMarkerGraphic=E=w.renderer.symbol(h,u.x,u.y,u.width,u.height).add(c.markerGroup),E.currentSymbol=h);!w.styledMode&&E&&\"inactive\"!==this.state&&E.attr(c.pointAttribs(this,b))}E&&(E[b&&this.isInside?\"show\":\"hide\"](),E.element.point=this,E.addClass(this.getClassName(),!0))}g=\ng.halo;u=(E=this.graphic||E)&&E.visibility||\"inherit\";g&&g.size&&E&&\"hidden\"!==u&&!this.isCluster?(G||(c.halo=G=w.renderer.path().add(E.parentGroup)),G.show()[f?\"animate\":\"attr\"]({d:this.haloPath(g.size)}),G.attr({\"class\":\"highcharts-halo highcharts-color-\"+d(this.colorIndex,c.colorIndex)+(this.className?\" \"+this.className:\"\"),visibility:u,zIndex:-1}),G.point=this,w.styledMode||G.attr(p({fill:this.color||c.color,\"fill-opacity\":g.opacity},a.filterUserAttributes(g.attributes||{})))):G&&G.point&&G.point.haloPath&&\nG.animate({d:G.point.haloPath(0)},null,G.hide);t(this,\"afterSetState\",{state:b})}}haloPath(b){const a=this.pos();return a?this.series.chart.renderer.symbols.circle(Math.floor(a[0])-b,a[1]-b,2*b,2*b):[]}}\"\";return G});M(a,\"Core/Pointer.js\",[a[\"Core/Color/Color.js\"],a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {parse:x}=a,{charts:C,noop:z}=y,{addEvent:H,attr:B,css:u,defined:v,extend:l,find:p,fireEvent:t,isNumber:m,isObject:h,objectEach:g,offset:e,pick:w,splat:E}=I;class F{constructor(a,\nk){this.lastValidTouch={};this.pinchDown=[];this.runChartClick=!1;this.eventsToUnbind=[];this.chart=a;this.hasDragged=!1;this.options=k;this.init(a,k)}applyInactiveState(a){let d=[],e;(a||[]).forEach(function(a){e=a.series;d.push(e);e.linkedParent&&d.push(e.linkedParent);e.linkedSeries&&(d=d.concat(e.linkedSeries));e.navigatorSeries&&d.push(e.navigatorSeries)});this.chart.series.forEach(function(a){-1===d.indexOf(a)?a.setState(\"inactive\",!0):a.options.inactiveOtherPoints&&a.setAllPointsToState(\"inactive\")})}destroy(){const a=\nthis;this.eventsToUnbind.forEach(a=>a());this.eventsToUnbind=[];y.chartCount||(F.unbindDocumentMouseUp&&(F.unbindDocumentMouseUp=F.unbindDocumentMouseUp()),F.unbindDocumentTouchEnd&&(F.unbindDocumentTouchEnd=F.unbindDocumentTouchEnd()));clearInterval(a.tooltipTimeout);g(a,function(d,e){a[e]=void 0})}getSelectionMarkerAttrs(a,k){const d={args:{chartX:a,chartY:k},attrs:{},shapeType:\"rect\"};t(this,\"getSelectionMarkerAttrs\",d,d=>{const {chart:e,mouseDownX:b=0,mouseDownY:f=0,zoomHor:c,zoomVert:n}=this;\nd=d.attrs;let g;d.x=e.plotLeft;d.y=e.plotTop;d.width=c?1:e.plotWidth;d.height=n?1:e.plotHeight;c&&(g=a-b,d.width=Math.abs(g),d.x=(0<g?0:g)+b);n&&(g=k-f,d.height=Math.abs(g),d.y=(0<g?0:g)+f)});return d}drag(a){const d=this.chart,e=d.options.chart;var g=d.plotLeft;const l=d.plotTop,b=d.plotWidth,f=d.plotHeight,c=this.mouseDownX||0,n=this.mouseDownY||0,m=h(e.panning)?e.panning&&e.panning.enabled:e.panning,p=e.panKey&&a[e.panKey+\"Key\"];let t=a.chartX,w=a.chartY,v=this.selectionMarker;if(!v||!v.touch)if(t<\ng?t=g:t>g+b&&(t=g+b),w<l?w=l:w>l+f&&(w=l+f),this.hasDragged=Math.sqrt(Math.pow(c-t,2)+Math.pow(n-w,2)),10<this.hasDragged){g=d.isInsidePlot(c-g,n-l,{visiblePlotOnly:!0});const {shapeType:b,attrs:f}=this.getSelectionMarkerAttrs(t,w);!d.hasCartesianSeries&&!d.mapView||!this.zoomX&&!this.zoomY||!g||p||v||(this.selectionMarker=v=d.renderer[b](),v.attr({\"class\":\"highcharts-selection-marker\",zIndex:7}).add(),d.styledMode||v.attr({fill:e.selectionMarkerFill||x(\"#334eff\").setOpacity(.25).get()}));v&&v.attr(f);\ng&&!v&&m&&d.pan(a,e.panning)}}dragStart(a){const d=this.chart;d.mouseIsDown=a.type;d.cancelClick=!1;d.mouseDownX=this.mouseDownX=a.chartX;d.mouseDownY=this.mouseDownY=a.chartY}getSelectionBox(a){const d={args:{marker:a},result:{}};t(this,\"getSelectionBox\",d,d=>{d.result={x:a.attr?+a.attr(\"x\"):a.x,y:a.attr?+a.attr(\"y\"):a.y,width:a.attr?a.attr(\"width\"):a.width,height:a.attr?a.attr(\"height\"):a.height}});return d.result}drop(a){const d=this,e=this.chart,g=this.hasPinched;if(this.selectionMarker){const {x:k,\ny:b,width:f,height:c}=this.getSelectionBox(this.selectionMarker),n={originalEvent:a,xAxis:[],yAxis:[],x:k,y:b,width:f,height:c};let h=!!e.mapView;if(this.hasDragged||g)e.axes.forEach(function(e){if(e.zoomEnabled&&v(e.min)&&(g||d[{xAxis:\"zoomX\",yAxis:\"zoomY\"}[e.coll]])&&m(k)&&m(b)&&m(f)&&m(c)){var q=e.horiz;const d=\"touchend\"===a.type?e.minPixelPadding:0,g=e.toValue((q?k:b)+d);q=e.toValue((q?k+f:b+c)-d);n[e.coll].push({axis:e,min:Math.min(g,q),max:Math.max(g,q)});h=!0}}),h&&t(e,\"selection\",n,function(b){e.zoom(l(b,\ng?{animation:!1}:null))});m(e.index)&&(this.selectionMarker=this.selectionMarker.destroy());g&&this.scaleGroups()}e&&m(e.index)&&(u(e.container,{cursor:e._cursor}),e.cancelClick=10<this.hasDragged,e.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[])}findNearestKDPoint(a,k,e){let d;a.forEach(function(a){var b=!(a.noSharedTooltip&&k)&&0>a.options.findNearestPointBy.indexOf(\"y\");a=a.searchPoint(e,b);if((b=h(a,!0)&&a.series)&&!(b=!h(d,!0))){{b=d.distX-a.distX;const f=d.dist-a.dist,c=(a.series.group&&\na.series.group.zIndex)-(d.series.group&&d.series.group.zIndex);b=0!==b&&k?b:0!==f?f:0!==c?c:d.series.index>a.series.index?-1:1}b=0<b}b&&(d=a)});return d}getChartCoordinatesFromPoint(a,k){var d=a.series;const e=d.xAxis;d=d.yAxis;const g=a.shapeArgs;if(e&&d){let b=w(a.clientX,a.plotX),f=a.plotY||0;a.isNode&&g&&m(g.x)&&m(g.y)&&(b=g.x,f=g.y);return k?{chartX:d.len+d.pos-f,chartY:e.len+e.pos-b}:{chartX:b+e.pos,chartY:f+d.pos}}if(g&&g.x&&g.y)return{chartX:g.x,chartY:g.y}}getChartPosition(){if(this.chartPosition)return this.chartPosition;\nvar {container:a}=this.chart;const k=e(a);this.chartPosition={left:k.left,top:k.top,scaleX:1,scaleY:1};const g=a.offsetWidth;a=a.offsetHeight;2<g&&2<a&&(this.chartPosition.scaleX=k.width/g,this.chartPosition.scaleY=k.height/a);return this.chartPosition}getCoordinates(a){const d={xAxis:[],yAxis:[]};this.chart.axes.forEach(function(k){d[k.isXAxis?\"xAxis\":\"yAxis\"].push({axis:k,value:k.toValue(a[k.horiz?\"chartX\":\"chartY\"])})});return d}getHoverData(a,k,e,g,l,b){const f=[];g=!(!g||!a);const c=function(b){return b.visible&&\n!(!l&&b.directTouch)&&w(b.options.enableMouseTracking,!0)};let d,q={chartX:b?b.chartX:void 0,chartY:b?b.chartY:void 0,shared:l};t(this,\"beforeGetHoverData\",q);d=k&&!k.stickyTracking?[k]:e.filter(b=>b.stickyTracking&&(q.filter||c)(b));const r=g||!b?a:this.findNearestKDPoint(d,l,b);k=r&&r.series;r&&(l&&!k.noSharedTooltip?(d=e.filter(function(b){return q.filter?q.filter(b):c(b)&&!b.noSharedTooltip}),d.forEach(function(b){let c=p(b.points,function(b){return b.x===r.x&&!b.isNull});h(c)&&(b.boosted&&b.boost&&\n(c=b.boost.getPoint(c)),f.push(c))})):f.push(r));q={hoverPoint:r};t(this,\"afterGetHoverData\",q);return{hoverPoint:q.hoverPoint,hoverSeries:k,hoverPoints:f}}getPointFromEvent(a){a=a.target;let d;for(;a&&!d;)d=a.point,a=a.parentNode;return d}onTrackerMouseOut(a){a=a.relatedTarget;const d=this.chart.hoverSeries;this.isDirectTouch=!1;if(!(!d||!a||d.stickyTracking||this.inClass(a,\"highcharts-tooltip\")||this.inClass(a,\"highcharts-series-\"+d.index)&&this.inClass(a,\"highcharts-tracker\")))d.onMouseOut()}inClass(a,\nk){let d;for(;a;){if(d=B(a,\"class\")){if(-1!==d.indexOf(k))return!0;if(-1!==d.indexOf(\"highcharts-container\"))return!1}a=a.parentElement}}init(a,k){this.options=k;this.chart=a;this.runChartClick=!(!k.chart.events||!k.chart.events.click);this.pinchDown=[];this.lastValidTouch={};this.setDOMEvents();t(this,\"afterInit\")}normalize(a,k){var d=a.touches,e=d?d.length?d.item(0):w(d.changedTouches,a.changedTouches)[0]:a;k||(k=this.getChartPosition());d=e.pageX-k.left;e=e.pageY-k.top;d/=k.scaleX;e/=k.scaleY;\nreturn l(a,{chartX:Math.round(d),chartY:Math.round(e)})}onContainerClick(a){const d=this.chart,e=d.hoverPoint;a=this.normalize(a);const g=d.plotLeft,h=d.plotTop;d.cancelClick||(e&&this.inClass(a.target,\"highcharts-tracker\")?(t(e.series,\"click\",l(a,{point:e})),d.hoverPoint&&e.firePointEvent(\"click\",a)):(l(a,this.getCoordinates(a)),d.isInsidePlot(a.chartX-g,a.chartY-h,{visiblePlotOnly:!0})&&t(d,\"click\",a)))}onContainerMouseDown(a){const d=1===((a.buttons||a.button)&1);a=this.normalize(a);if(y.isFirefox&&\n0!==a.button)this.onContainerMouseMove(a);if(\"undefined\"===typeof a.button||d)this.zoomOption(a),d&&a.preventDefault&&a.preventDefault(),this.dragStart(a)}onContainerMouseLeave(a){const d=C[w(F.hoverChartIndex,-1)];a=this.normalize(a);d&&a.relatedTarget&&!this.inClass(a.relatedTarget,\"highcharts-tooltip\")&&(d.pointer.reset(),d.pointer.chartPosition=void 0)}onContainerMouseEnter(a){delete this.chartPosition}onContainerMouseMove(a){const d=this.chart,e=d.tooltip;a=this.normalize(a);this.setHoverChartIndex();\n(\"mousedown\"===d.mouseIsDown||this.touchSelect(a))&&this.drag(a);d.openMenu||!this.inClass(a.target,\"highcharts-tracker\")&&!d.isInsidePlot(a.chartX-d.plotLeft,a.chartY-d.plotTop,{visiblePlotOnly:!0})||e&&e.shouldStickOnContact(a)||(this.inClass(a.target,\"highcharts-no-tooltip\")?this.reset(!1,0):this.runPointActions(a))}onDocumentTouchEnd(a){const d=C[w(F.hoverChartIndex,-1)];d&&d.pointer.drop(a)}onContainerTouchMove(a){if(this.touchSelect(a))this.onContainerMouseMove(a);else this.touch(a)}onContainerTouchStart(a){if(this.touchSelect(a))this.onContainerMouseDown(a);\nelse this.zoomOption(a),this.touch(a,!0)}onDocumentMouseMove(a){const d=this.chart,e=d.tooltip,g=this.chartPosition;a=this.normalize(a,g);!g||d.isInsidePlot(a.chartX-d.plotLeft,a.chartY-d.plotTop,{visiblePlotOnly:!0})||e&&e.shouldStickOnContact(a)||this.inClass(a.target,\"highcharts-tracker\")||this.reset()}onDocumentMouseUp(a){const d=C[w(F.hoverChartIndex,-1)];d&&d.pointer.drop(a)}pinch(a){const d=this,e=d.chart,g=d.pinchDown,h=a.touches||[],b=h.length,f=d.lastValidTouch,c=d.hasZoom,n={},m=1===b&&\n(d.inClass(a.target,\"highcharts-tracker\")&&e.runTrackerClick||d.runChartClick),p={};var v=d.chart.tooltip;v=1===b&&w(v&&v.options.followTouchMove,!0);let u=d.selectionMarker;1<b?d.initiated=!0:v&&(d.initiated=!1);c&&d.initiated&&!m&&!1!==a.cancelable&&a.preventDefault();[].map.call(h,function(b){return d.normalize(b)});\"touchstart\"===a.type?([].forEach.call(h,function(b,a){g[a]={chartX:b.chartX,chartY:b.chartY}}),f.x=[g[0].chartX,g[1]&&g[1].chartX],f.y=[g[0].chartY,g[1]&&g[1].chartY],e.axes.forEach(function(b){if(b.zoomEnabled){const a=\ne.bounds[b.horiz?\"h\":\"v\"],c=b.minPixelPadding,f=b.toPixels(Math.min(w(b.options.min,b.dataMin),b.dataMin)),d=b.toPixels(Math.max(w(b.options.max,b.dataMax),b.dataMax)),n=Math.max(f,d);a.min=Math.min(b.pos,Math.min(f,d)-c);a.max=Math.max(b.pos+b.len,n+c)}}),d.res=!0):v?this.runPointActions(d.normalize(a)):g.length&&(t(e,\"touchpan\",{originalEvent:a},()=>{u||(d.selectionMarker=u=l({destroy:z,touch:!0},e.plotBox));d.pinchTranslate(g,h,n,u,p,f);d.hasPinched=c;d.scaleGroups(n,p)}),d.res&&(d.res=!1,this.reset(!1,\n0)))}pinchTranslate(a,e,g,h,l,b){this.zoomHor&&this.pinchTranslateDirection(!0,a,e,g,h,l,b);this.zoomVert&&this.pinchTranslateDirection(!1,a,e,g,h,l,b)}pinchTranslateDirection(a,e,g,h,l,b,f,c){const d=this.chart,k=a?\"x\":\"y\",q=a?\"X\":\"Y\",m=\"chart\"+q,r=a?\"width\":\"height\",p=d[\"plot\"+(a?\"Left\":\"Top\")],t=d.inverted,w=d.bounds[a?\"h\":\"v\"],v=1===e.length,u=e[0][m],x=!v&&e[1][m];e=function(){\"number\"===typeof N&&20<Math.abs(u-x)&&(F=c||Math.abs(J-N)/Math.abs(u-x));E=(p-J)/F+u;G=d[\"plot\"+(a?\"Width\":\"Height\")]/\nF};let G,E,F=c||1,J=g[0][m],N=!v&&g[1][m],O;e();g=E;g<w.min?(g=w.min,O=!0):g+G>w.max&&(g=w.max-G,O=!0);O?(J-=.8*(J-f[k][0]),\"number\"===typeof N&&(N-=.8*(N-f[k][1])),e()):f[k]=[J,N];t||(b[k]=E-p,b[r]=G);b=t?1/F:F;l[r]=G;l[k]=g;h[t?a?\"scaleY\":\"scaleX\":\"scale\"+q]=F;h[\"translate\"+q]=b*p+(J-b*u)}reset(a,e){const d=this.chart,k=d.hoverSeries,g=d.hoverPoint,b=d.hoverPoints,f=d.tooltip,c=f&&f.shared?b:g;a&&c&&E(c).forEach(function(b){b.series.isCartesian&&\"undefined\"===typeof b.plotX&&(a=!1)});if(a)f&&c&&\nE(c).length&&(f.refresh(c),f.shared&&b?b.forEach(function(b){b.setState(b.state,!0);b.series.isCartesian&&(b.series.xAxis.crosshair&&b.series.xAxis.drawCrosshair(null,b),b.series.yAxis.crosshair&&b.series.yAxis.drawCrosshair(null,b))}):g&&(g.setState(g.state,!0),d.axes.forEach(function(b){b.crosshair&&g.series[b.coll]===b&&b.drawCrosshair(null,g)})));else{if(g)g.onMouseOut();b&&b.forEach(function(b){b.setState()});if(k)k.onMouseOut();f&&f.hide(e);this.unDocMouseMove&&(this.unDocMouseMove=this.unDocMouseMove());\nd.axes.forEach(function(b){b.hideCrosshair()});this.hoverX=d.hoverPoints=d.hoverPoint=null}}runPointActions(a,e,g){const d=this.chart,k=d.tooltip&&d.tooltip.options.enabled?d.tooltip:void 0,b=k?k.shared:!1;let f=e||d.hoverPoint,c=f&&f.series||d.hoverSeries;e=this.getHoverData(f,c,d.series,(!a||\"touchmove\"!==a.type)&&(!!e||c&&c.directTouch&&this.isDirectTouch),b,a);f=e.hoverPoint;c=e.hoverSeries;const n=e.hoverPoints;e=c&&c.tooltipOptions.followPointer&&!c.tooltipOptions.split;const h=b&&c&&!c.noSharedTooltip;\nif(f&&(g||f!==d.hoverPoint||k&&k.isHidden)){(d.hoverPoints||[]).forEach(function(b){-1===n.indexOf(b)&&b.setState()});if(d.hoverSeries!==c)c.onMouseOver();this.applyInactiveState(n);(n||[]).forEach(function(b){b.setState(\"hover\")});d.hoverPoint&&d.hoverPoint.firePointEvent(\"mouseOut\");if(!f.series)return;d.hoverPoints=n;d.hoverPoint=f;f.firePointEvent(\"mouseOver\",void 0,()=>{k&&f&&k.refresh(h?n:f,a)})}else e&&k&&!k.isHidden&&(g=k.getAnchor([{}],a),d.isInsidePlot(g[0],g[1],{visiblePlotOnly:!0})&&k.updatePosition({plotX:g[0],\nplotY:g[1]}));this.unDocMouseMove||(this.unDocMouseMove=H(d.container.ownerDocument,\"mousemove\",function(b){const a=C[F.hoverChartIndex];if(a)a.pointer.onDocumentMouseMove(b)}),this.eventsToUnbind.push(this.unDocMouseMove));d.axes.forEach(function(b){const c=w((b.crosshair||{}).snap,!0);let f;c&&((f=d.hoverPoint)&&f.series[b.coll]===b||(f=p(n,a=>a.series&&a.series[b.coll]===b)));f||!c?b.drawCrosshair(a,f):b.hideCrosshair()})}scaleGroups(a,e){const d=this.chart;d.series.forEach(function(k){const g=\na||k.getPlotBox();k.group&&(k.xAxis&&k.xAxis.zoomEnabled||d.mapView)&&(k.group.attr(g),k.markerGroup&&(k.markerGroup.attr(g),k.markerGroup.clip(e?d.clipRect:null)),k.dataLabelsGroup&&k.dataLabelsGroup.attr(g))});d.clipRect.attr(e||d.clipBox)}setDOMEvents(){const a=this.chart.container,e=a.ownerDocument;a.onmousedown=this.onContainerMouseDown.bind(this);a.onmousemove=this.onContainerMouseMove.bind(this);a.onclick=this.onContainerClick.bind(this);this.eventsToUnbind.push(H(a,\"mouseenter\",this.onContainerMouseEnter.bind(this)));\nthis.eventsToUnbind.push(H(a,\"mouseleave\",this.onContainerMouseLeave.bind(this)));F.unbindDocumentMouseUp||(F.unbindDocumentMouseUp=H(e,\"mouseup\",this.onDocumentMouseUp.bind(this)));let g=this.chart.renderTo.parentElement;for(;g&&\"BODY\"!==g.tagName;)this.eventsToUnbind.push(H(g,\"scroll\",()=>{delete this.chartPosition})),g=g.parentElement;y.hasTouch&&(this.eventsToUnbind.push(H(a,\"touchstart\",this.onContainerTouchStart.bind(this),{passive:!1})),this.eventsToUnbind.push(H(a,\"touchmove\",this.onContainerTouchMove.bind(this),\n{passive:!1})),F.unbindDocumentTouchEnd||(F.unbindDocumentTouchEnd=H(e,\"touchend\",this.onDocumentTouchEnd.bind(this),{passive:!1})))}setHoverChartIndex(){const a=this.chart,e=y.charts[w(F.hoverChartIndex,-1)];if(e&&e!==a)e.pointer.onContainerMouseLeave({relatedTarget:a.container});e&&e.mouseIsDown||(F.hoverChartIndex=a.index)}touch(a,e){const d=this.chart;let g,k;this.setHoverChartIndex();1===a.touches.length?(a=this.normalize(a),(k=d.isInsidePlot(a.chartX-d.plotLeft,a.chartY-d.plotTop,{visiblePlotOnly:!0}))&&\n!d.openMenu?(e&&this.runPointActions(a),\"touchmove\"===a.type&&(e=this.pinchDown,g=e[0]?4<=Math.sqrt(Math.pow(e[0].chartX-a.chartX,2)+Math.pow(e[0].chartY-a.chartY,2)):!1),w(g,!0)&&this.pinch(a)):e&&this.reset()):2===a.touches.length&&this.pinch(a)}touchSelect(a){return!(!this.chart.zooming.singleTouch||!a.touches||1!==a.touches.length)}zoomOption(a){const d=this.chart,e=d.inverted;var g=d.zooming.type||\"\";/touch/.test(a.type)&&(g=w(d.zooming.pinchType,g));this.zoomX=a=/x/.test(g);this.zoomY=g=/y/.test(g);\nthis.zoomHor=a&&!e||g&&e;this.zoomVert=g&&!e||a&&e;this.hasZoom=a||g}}(function(a){const d=[],e=[];a.compose=function(d){I.pushUnique(e,d)&&H(d,\"beforeRender\",function(){this.pointer=new a(this,this.options)})};a.dissolve=function(){for(let a=0,e=d.length;a<e;++a)d[a]();d.length=0}})(F||(F={}));\"\";return F});M(a,\"Core/Legend/Legend.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Templating.js\"],a[\"Core/Globals.js\"],a[\"Core/Series/Point.js\"],a[\"Core/Renderer/RendererUtilities.js\"],a[\"Core/Utilities.js\"]],\nfunction(a,y,I,L,C,z){const {animObject:x,setAnimation:B}=a,{format:u}=y,{marginNames:v}=I,{distribute:l}=C,{addEvent:p,createElement:t,css:m,defined:h,discardElement:g,find:e,fireEvent:w,isNumber:E,merge:F,pick:d,relativeLength:k,stableSort:r,syncTimeout:q}=z;class G{constructor(b,a){this.allItems=[];this.contentGroup=this.box=void 0;this.display=!1;this.group=void 0;this.offsetWidth=this.maxLegendWidth=this.maxItemWidth=this.legendWidth=this.legendHeight=this.lastLineHeight=this.lastItemY=this.itemY=\nthis.itemX=this.itemMarginTop=this.itemMarginBottom=this.itemHeight=this.initialItemY=0;this.options=void 0;this.padding=0;this.pages=[];this.proximate=!1;this.scrollGroup=void 0;this.widthOption=this.totalItemWidth=this.titleHeight=this.symbolWidth=this.symbolHeight=0;this.chart=b;this.init(b,a)}init(b,a){this.chart=b;this.setOptions(a);a.enabled&&(this.render(),p(this.chart,\"endResize\",function(){this.legend.positionCheckboxes()}),p(this.chart,\"render\",()=>{this.proximate&&(this.proximatePositions(),\nthis.positionItems())}))}setOptions(b){const a=d(b.padding,8);this.options=b;this.chart.styledMode||(this.itemStyle=b.itemStyle,this.itemHiddenStyle=F(this.itemStyle,b.itemHiddenStyle));this.itemMarginTop=b.itemMarginTop;this.itemMarginBottom=b.itemMarginBottom;this.padding=a;this.initialItemY=a-5;this.symbolWidth=d(b.symbolWidth,16);this.pages=[];this.proximate=\"proximate\"===b.layout&&!this.chart.inverted;this.baseline=void 0}update(b,a){const c=this.chart;this.setOptions(F(!0,this.options,b));this.destroy();\nc.isDirtyLegend=c.isDirtyBox=!0;d(a,!0)&&c.redraw();w(this,\"afterUpdate\")}colorizeItem(b,a){const {group:c,label:f,line:d,symbol:e}=b.legendItem||{};if(c)c[a?\"removeClass\":\"addClass\"](\"highcharts-legend-item-hidden\");if(!this.chart.styledMode){const {itemHiddenStyle:c}=this,g=c.color,k=a?b.color||g:g,n=b.options&&b.options.marker;let h={fill:k};null===f||void 0===f?void 0:f.css(F(a?this.itemStyle:c));null===d||void 0===d?void 0:d.attr({stroke:k});e&&(n&&e.isMarker&&(h=b.pointAttribs(),a||(h.stroke=\nh.fill=g)),e.attr(h))}w(this,\"afterColorizeItem\",{item:b,visible:a})}positionItems(){this.allItems.forEach(this.positionItem,this);this.chart.isResizing||this.positionCheckboxes()}positionItem(b){const {group:a,x:c=0,y:d=0}=b.legendItem||{};var e=this.options,g=e.symbolPadding;const k=!e.rtl;e=b.checkbox;a&&a.element&&(g={translateX:k?c:this.legendWidth-c-2*g-4,translateY:d},a[h(a.translateY)?\"animate\":\"attr\"](g,void 0,()=>{w(this,\"afterPositionItem\",{item:b})}));e&&(e.x=c,e.y=d)}destroyItem(b){const a=\nb.checkbox,c=b.legendItem||{};for(const b of[\"group\",\"label\",\"line\",\"symbol\"])c[b]&&(c[b]=c[b].destroy());a&&g(a);b.legendItem=void 0}destroy(){for(const b of this.getAllItems())this.destroyItem(b);for(const b of\"clipRect up down pager nav box title group\".split(\" \"))this[b]&&(this[b]=this[b].destroy());this.display=null}positionCheckboxes(){const b=this.group&&this.group.alignAttr,a=this.clipHeight||this.legendHeight,c=this.titleHeight;let d;b&&(d=b.translateY,this.allItems.forEach(function(f){const e=\nf.checkbox;let g;e&&(g=d+c+e.y+(this.scrollOffset||0)+3,m(e,{left:b.translateX+f.checkboxOffset+e.x-20+\"px\",top:g+\"px\",display:this.proximate||g>d-6&&g<d+a-6?\"\":\"none\"}))},this))}renderTitle(){var b=this.options;const a=this.padding,c=b.title;let d=0;c.text&&(this.title||(this.title=this.chart.renderer.label(c.text,a-3,a-4,void 0,void 0,void 0,b.useHTML,void 0,\"legend-title\").attr({zIndex:1}),this.chart.styledMode||this.title.css(c.style),this.title.add(this.group)),c.width||this.title.css({width:this.maxLegendWidth+\n\"px\"}),b=this.title.getBBox(),d=b.height,this.offsetWidth=b.width,this.contentGroup.attr({translateY:d}));this.titleHeight=d}setText(b){const a=this.options;b.legendItem.label.attr({text:a.labelFormat?u(a.labelFormat,b,this.chart):a.labelFormatter.call(b)})}renderItem(b){const a=b.legendItem=b.legendItem||{};var c=this.chart,e=c.renderer;const g=this.options,k=this.symbolWidth,h=g.symbolPadding||0,l=this.itemStyle,m=this.itemHiddenStyle,q=\"horizontal\"===g.layout?d(g.itemDistance,20):0,r=!g.rtl,p=\n!b.series,t=!p&&b.series.drawLegendSymbol?b.series:b;var w=t.options;const v=this.createCheckboxForItem&&w&&w.showCheckbox,u=g.useHTML,x=b.options.className;let J=a.label;w=k+h+q+(v?20:0);J||(a.group=e.g(\"legend-item\").addClass(\"highcharts-\"+t.type+\"-series highcharts-color-\"+b.colorIndex+(x?\" \"+x:\"\")+(p?\" highcharts-series-\"+b.index:\"\")).attr({zIndex:1}).add(this.scrollGroup),a.label=J=e.text(\"\",r?k+h:-h,this.baseline||0,u),c.styledMode||J.css(F(b.visible?l:m)),J.attr({align:r?\"left\":\"right\",zIndex:2}).add(a.group),\nthis.baseline||(this.fontMetrics=e.fontMetrics(J),this.baseline=this.fontMetrics.f+3+this.itemMarginTop,J.attr(\"y\",this.baseline),this.symbolHeight=d(g.symbolHeight,this.fontMetrics.f),g.squareSymbol&&(this.symbolWidth=d(g.symbolWidth,Math.max(this.symbolHeight,16)),w=this.symbolWidth+h+q+(v?20:0),r&&J.attr(\"x\",this.symbolWidth+h))),t.drawLegendSymbol(this,b),this.setItemEvents&&this.setItemEvents(b,J,u));v&&!b.checkbox&&this.createCheckboxForItem&&this.createCheckboxForItem(b);this.colorizeItem(b,\nb.visible);!c.styledMode&&l.width||J.css({width:(g.itemWidth||this.widthOption||c.spacingBox.width)-w+\"px\"});this.setText(b);c=J.getBBox();e=this.fontMetrics&&this.fontMetrics.h||0;b.itemWidth=b.checkboxOffset=g.itemWidth||a.labelWidth||c.width+w;this.maxItemWidth=Math.max(this.maxItemWidth,b.itemWidth);this.totalItemWidth+=b.itemWidth;this.itemHeight=b.itemHeight=Math.round(a.labelHeight||(c.height>1.5*e?c.height:e))}layoutItem(b){var a=this.options;const c=this.padding,e=\"horizontal\"===a.layout,\ng=b.itemHeight,k=this.itemMarginBottom,h=this.itemMarginTop,l=e?d(a.itemDistance,20):0,m=this.maxLegendWidth;a=a.alignColumns&&this.totalItemWidth>m?this.maxItemWidth:b.itemWidth;const q=b.legendItem||{};e&&this.itemX-c+a>m&&(this.itemX=c,this.lastLineHeight&&(this.itemY+=h+this.lastLineHeight+k),this.lastLineHeight=0);this.lastItemY=h+this.itemY+k;this.lastLineHeight=Math.max(g,this.lastLineHeight);q.x=this.itemX;q.y=this.itemY;e?this.itemX+=a:(this.itemY+=h+g+k,this.lastLineHeight=g);this.offsetWidth=\nthis.widthOption||Math.max((e?this.itemX-c-(b.checkbox?0:l):a)+c,this.offsetWidth)}getAllItems(){let b=[];this.chart.series.forEach(function(a){const c=a&&a.options;a&&d(c.showInLegend,h(c.linkedTo)?!1:void 0,!0)&&(b=b.concat((a.legendItem||{}).labels||(\"point\"===c.legendType?a.data:a)))});w(this,\"afterGetAllItems\",{allItems:b});return b}getAlignment(){const b=this.options;return this.proximate?b.align.charAt(0)+\"tv\":b.floating?\"\":b.align.charAt(0)+b.verticalAlign.charAt(0)+b.layout.charAt(0)}adjustMargins(b,\na){const c=this.chart,f=this.options,e=this.getAlignment();e&&[/(lth|ct|rth)/,/(rtv|rm|rbv)/,/(rbh|cb|lbh)/,/(lbv|lm|ltv)/].forEach(function(g,k){g.test(e)&&!h(b[k])&&(c[v[k]]=Math.max(c[v[k]],c.legend[(k+1)%2?\"legendHeight\":\"legendWidth\"]+[1,-1,-1,1][k]*f[k%2?\"x\":\"y\"]+d(f.margin,12)+a[k]+(c.titleOffset[k]||0)))})}proximatePositions(){const b=this.chart,a=[],c=\"left\"===this.options.align;this.allItems.forEach(function(d){var f;var g=c;let k;d.yAxis&&(d.xAxis.options.reversed&&(g=!g),d.points&&(f=\ne(g?d.points:d.points.slice(0).reverse(),function(b){return E(b.plotY)})),g=this.itemMarginTop+d.legendItem.label.getBBox().height+this.itemMarginBottom,k=d.yAxis.top-b.plotTop,d.visible?(f=f?f.plotY:d.yAxis.height,f+=k-.3*g):f=k+d.yAxis.height,a.push({target:f,size:g,item:d}))},this);let d;for(const c of l(a,b.plotHeight))d=c.item.legendItem||{},E(c.pos)&&(d.y=b.plotTop-b.spacing[0]+c.pos)}render(){const b=this.chart,a=b.renderer,c=this.options,d=this.padding;var e=this.getAllItems();let g,h=this.group,\nl=this.box;this.itemX=d;this.itemY=this.initialItemY;this.lastItemY=this.offsetWidth=0;this.widthOption=k(c.width,b.spacingBox.width-d);var m=b.spacingBox.width-2*d-c.x;-1<[\"rm\",\"lm\"].indexOf(this.getAlignment().substring(0,2))&&(m/=2);this.maxLegendWidth=this.widthOption||m;h||(this.group=h=a.g(\"legend\").addClass(c.className||\"\").attr({zIndex:7}).add(),this.contentGroup=a.g().attr({zIndex:1}).add(h),this.scrollGroup=a.g().add(this.contentGroup));this.renderTitle();r(e,(b,a)=>(b.options&&b.options.legendIndex||\n0)-(a.options&&a.options.legendIndex||0));c.reversed&&e.reverse();this.allItems=e;this.display=m=!!e.length;this.itemHeight=this.totalItemWidth=this.maxItemWidth=this.lastLineHeight=0;e.forEach(this.renderItem,this);e.forEach(this.layoutItem,this);e=(this.widthOption||this.offsetWidth)+d;g=this.lastItemY+this.lastLineHeight+this.titleHeight;g=this.handleOverflow(g);g+=d;l||(this.box=l=a.rect().addClass(\"highcharts-legend-box\").attr({r:c.borderRadius}).add(h));b.styledMode||l.attr({stroke:c.borderColor,\n\"stroke-width\":c.borderWidth||0,fill:c.backgroundColor||\"none\"}).shadow(c.shadow);if(0<e&&0<g)l[l.placed?\"animate\":\"attr\"](l.crisp.call({},{x:0,y:0,width:e,height:g},l.strokeWidth()));h[m?\"show\":\"hide\"]();b.styledMode&&\"none\"===h.getStyle(\"display\")&&(e=g=0);this.legendWidth=e;this.legendHeight=g;m&&this.align();this.proximate||this.positionItems();w(this,\"afterRender\")}align(b=this.chart.spacingBox){const a=this.chart,c=this.options;let d=b.y;/(lth|ct|rth)/.test(this.getAlignment())&&0<a.titleOffset[0]?\nd+=a.titleOffset[0]:/(lbh|cb|rbh)/.test(this.getAlignment())&&0<a.titleOffset[2]&&(d-=a.titleOffset[2]);d!==b.y&&(b=F(b,{y:d}));a.hasRendered||(this.group.placed=!1);this.group.align(F(c,{width:this.legendWidth,height:this.legendHeight,verticalAlign:this.proximate?\"top\":c.verticalAlign}),!0,b)}handleOverflow(b){const a=this,c=this.chart,e=c.renderer,g=this.options;var k=g.y;const h=\"top\"===g.verticalAlign,l=this.padding,m=g.maxHeight,q=g.navigation,r=d(q.animation,!0),p=q.arrowSize||12,t=this.pages,\nw=this.allItems,v=function(b){\"number\"===typeof b?E.attr({height:b}):E&&(a.clipRect=E.destroy(),a.contentGroup.clip());a.contentGroup.div&&(a.contentGroup.div.style.clip=b?\"rect(\"+l+\"px,9999px,\"+(l+b)+\"px,0)\":\"auto\")},u=function(b){a[b]=e.circle(0,0,1.3*p).translate(p/2,p/2).add(O);c.styledMode||a[b].attr(\"fill\",\"rgba(0,0,0,0.0001)\");return a[b]};let x,J,N;k=c.spacingBox.height+(h?-k:k)-l;let O=this.nav,E=this.clipRect;\"horizontal\"!==g.layout||\"middle\"===g.verticalAlign||g.floating||(k/=2);m&&(k=\nMath.min(k,m));t.length=0;b&&0<k&&b>k&&!1!==q.enabled?(this.clipHeight=x=Math.max(k-20-this.titleHeight-l,0),this.currentPage=d(this.currentPage,1),this.fullHeight=b,w.forEach((b,a)=>{N=b.legendItem||{};b=N.y||0;const c=Math.round(N.label.getBBox().height);let d=t.length;if(!d||b-t[d-1]>x&&(J||b)!==t[d-1])t.push(J||b),d++;N.pageIx=d-1;J&&((w[a-1].legendItem||{}).pageIx=d-1);a===w.length-1&&b+c-t[d-1]>x&&b>t[d-1]&&(t.push(b),N.pageIx=d);b!==J&&(J=b)}),E||(E=a.clipRect=e.clipRect(0,l-2,9999,0),a.contentGroup.clip(E)),\nv(x),O||(this.nav=O=e.g().attr({zIndex:1}).add(this.group),this.up=e.symbol(\"triangle\",0,0,p,p).add(O),u(\"upTracker\").on(\"click\",function(){a.scroll(-1,r)}),this.pager=e.text(\"\",15,10).addClass(\"highcharts-legend-navigation\"),!c.styledMode&&q.style&&this.pager.css(q.style),this.pager.add(O),this.down=e.symbol(\"triangle-down\",0,0,p,p).add(O),u(\"downTracker\").on(\"click\",function(){a.scroll(1,r)})),a.scroll(0),b=k):O&&(v(),this.nav=O.destroy(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0);\nreturn b}scroll(b,a){const c=this.chart,f=this.pages,e=f.length,g=this.clipHeight,k=this.options.navigation,h=this.pager,l=this.padding;let m=this.currentPage+b;m>e&&(m=e);0<m&&(\"undefined\"!==typeof a&&B(a,c),this.nav.attr({translateX:l,translateY:g+this.padding+7+this.titleHeight,visibility:\"inherit\"}),[this.up,this.upTracker].forEach(function(b){b.attr({\"class\":1===m?\"highcharts-legend-nav-inactive\":\"highcharts-legend-nav-active\"})}),h.attr({text:m+\"/\"+e}),[this.down,this.downTracker].forEach(function(b){b.attr({x:18+\nthis.pager.getBBox().width,\"class\":m===e?\"highcharts-legend-nav-inactive\":\"highcharts-legend-nav-active\"})},this),c.styledMode||(this.up.attr({fill:1===m?k.inactiveColor:k.activeColor}),this.upTracker.css({cursor:1===m?\"default\":\"pointer\"}),this.down.attr({fill:m===e?k.inactiveColor:k.activeColor}),this.downTracker.css({cursor:m===e?\"default\":\"pointer\"})),this.scrollOffset=-f[m-1]+this.initialItemY,this.scrollGroup.animate({translateY:this.scrollOffset}),this.currentPage=m,this.positionCheckboxes(),\nb=x(d(a,c.renderer.globalAnimation,!0)),q(()=>{w(this,\"afterScroll\",{currentPage:m})},b.duration))}setItemEvents(b,a,c){const d=this,f=b.legendItem||{},e=d.chart.renderer.boxWrapper,g=b instanceof L,k=\"highcharts-legend-\"+(g?\"point\":\"series\")+\"-active\",h=d.chart.styledMode;c=c?[a,f.symbol]:[f.group];const l=a=>{d.allItems.forEach(c=>{b!==c&&[c].concat(c.linkedSeries||[]).forEach(b=>{b.setState(a,!g)})})};for(const f of c)if(f)f.on(\"mouseover\",function(){b.visible&&l(\"inactive\");b.setState(\"hover\");\nb.visible&&e.addClass(k);h||a.css(d.options.itemHoverStyle)}).on(\"mouseout\",function(){d.chart.styledMode||a.css(F(b.visible?d.itemStyle:d.itemHiddenStyle));l(\"\");e.removeClass(k);b.setState()}).on(\"click\",function(a){const c=function(){b.setVisible&&b.setVisible();l(b.visible?\"inactive\":\"\")};e.removeClass(k);a={browserEvent:a};b.firePointEvent?b.firePointEvent(\"legendItemClick\",a,c):w(b,\"legendItemClick\",a,c)})}createCheckboxForItem(b){b.checkbox=t(\"input\",{type:\"checkbox\",className:\"highcharts-legend-checkbox\",\nchecked:b.selected,defaultChecked:b.selected},this.options.itemCheckboxStyle,this.chart.container);p(b.checkbox,\"click\",function(a){w(b.series||b,\"checkboxClick\",{checked:a.target.checked,item:b},function(){b.select()})})}}(function(b){const a=[];b.compose=function(c){z.pushUnique(a,c)&&p(c,\"beforeMargins\",function(){this.legend=new b(this,this.options.legend)})}})(G||(G={}));\"\";return G});M(a,\"Core/Legend/LegendSymbol.js\",[a[\"Core/Utilities.js\"]],function(a){const {extend:x,merge:I,pick:L}=a;var C;\n(function(a){a.lineMarker=function(a,B){B=this.legendItem=this.legendItem||{};var u=this.options;const v=a.symbolWidth,l=a.symbolHeight,p=l/2,t=this.chart.renderer,m=B.group;a=a.baseline-Math.round(.3*a.fontMetrics.b);let h={},g=u.marker,e=0;this.chart.styledMode||(h={\"stroke-width\":Math.min(u.lineWidth||0,24)},u.dashStyle?h.dashstyle=u.dashStyle:\"square\"!==u.linecap&&(h[\"stroke-linecap\"]=\"round\"));B.line=t.path().addClass(\"highcharts-graph\").attr(h).add(m);h[\"stroke-linecap\"]&&(e=Math.min(B.line.strokeWidth(),\nv)/2);v&&B.line.attr({d:[[\"M\",e,a],[\"L\",v-e,a]]});g&&!1!==g.enabled&&v&&(u=Math.min(L(g.radius,p),p),0===this.symbol.indexOf(\"url\")&&(g=I(g,{width:l,height:l}),u=0),B.symbol=B=t.symbol(this.symbol,v/2-u,a-u,2*u,2*u,x({context:\"legend\"},g)).addClass(\"highcharts-point\").add(m),B.isMarker=!0)};a.rectangle=function(a,x){x=x.legendItem||{};const u=a.symbolHeight,v=a.options.squareSymbol;x.symbol=this.chart.renderer.rect(v?(a.symbolWidth-u)/2:0,a.baseline-u+1,v?u:a.symbolWidth,u,L(a.options.symbolRadius,\nu/2)).addClass(\"highcharts-point\").attr({zIndex:3}).add(x.group)}})(C||(C={}));return C});M(a,\"Core/Series/SeriesDefaults.js\",[],function(){return{lineWidth:1,allowPointSelect:!1,crisp:!0,showCheckbox:!1,animation:{duration:1E3},enableMouseTracking:!0,events:{},marker:{enabledThreshold:2,lineColor:\"#ffffff\",lineWidth:0,radius:4,states:{normal:{animation:!0},hover:{animation:{duration:150},enabled:!0,radiusPlus:2,lineWidthPlus:1},select:{fillColor:\"#cccccc\",lineColor:\"#000000\",lineWidth:2}}},point:{events:{}},\ndataLabels:{animation:{},align:\"center\",borderWidth:0,defer:!0,formatter:function(){const {numberFormatter:a}=this.series.chart;return\"number\"!==typeof this.y?\"\":a(this.y,-1)},padding:5,style:{fontSize:\"0.7em\",fontWeight:\"bold\",color:\"contrast\",textOutline:\"1px contrast\"},verticalAlign:\"bottom\",x:0,y:0},cropThreshold:300,opacity:1,pointRange:0,softThreshold:!0,states:{normal:{animation:!0},hover:{animation:{duration:150},lineWidthPlus:1,marker:{},halo:{size:10,opacity:.25}},select:{animation:{duration:0}},\ninactive:{animation:{duration:150},opacity:.2}},stickyTracking:!0,turboThreshold:1E3,findNearestPointBy:\"x\"}});M(a,\"Core/Series/SeriesRegistry.js\",[a[\"Core/Globals.js\"],a[\"Core/Defaults.js\"],a[\"Core/Series/Point.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L){const {defaultOptions:x}=y,{extendClass:z,merge:H}=L;var B;(function(u){function v(a,p){const l=x.plotOptions||{},m=p.defaultOptions,h=p.prototype;h.type=a;h.pointClass||(h.pointClass=I);m&&(l[a]=m);u.seriesTypes[a]=p}u.seriesTypes=a.seriesTypes;\nu.registerSeriesType=v;u.seriesType=function(a,p,t,m,h){const g=x.plotOptions||{};p=p||\"\";g[a]=H(g[p],t);v(a,z(u.seriesTypes[p]||function(){},m));u.seriesTypes[a].prototype.type=a;h&&(u.seriesTypes[a].prototype.pointClass=z(I,h));return u.seriesTypes[a]}})(B||(B={}));return B});M(a,\"Core/Series/Series.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Defaults.js\"],a[\"Core/Foundation.js\"],a[\"Core/Globals.js\"],a[\"Core/Legend/LegendSymbol.js\"],a[\"Core/Series/Point.js\"],a[\"Core/Series/SeriesDefaults.js\"],\na[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Renderer/SVG/SVGElement.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C,z,H,B,u,v){const {animObject:l,setAnimation:p}=a,{defaultOptions:t}=y,{registerEventOptions:m}=I,{hasTouch:h,svg:g,win:e}=L,{seriesTypes:w}=B,{arrayMax:x,arrayMin:F,clamp:d,correctFloat:k,defined:r,diffObjects:q,erase:G,error:b,extend:f,find:c,fireEvent:n,getClosestDistance:P,getNestedProperty:D,insertItem:K,isArray:X,isNumber:T,isString:Z,merge:V,objectEach:Y,pick:A,removeEvent:M,splat:ia,\nsyncTimeout:ba}=v;class aa{constructor(){this.zones=this.yAxis=this.xAxis=this.userOptions=this.tooltipOptions=this.processedYData=this.processedXData=this.points=this.options=this.linkedSeries=this.index=this.eventsToUnbind=this.eventOptions=this.data=this.chart=this._i=void 0}init(b,a){n(this,\"init\",{options:a});const c=this,d=b.series;this.eventsToUnbind=[];c.chart=b;c.options=c.setOptions(a);a=c.options;c.linkedSeries=[];c.bindAxes();f(c,{name:a.name,state:\"\",visible:!1!==a.visible,selected:!0===\na.selected});m(this,a);const e=a.events;if(e&&e.click||a.point&&a.point.events&&a.point.events.click||a.allowPointSelect)b.runTrackerClick=!0;c.getColor();c.getSymbol();c.parallelArrays.forEach(function(b){c[b+\"Data\"]||(c[b+\"Data\"]=[])});c.isCartesian&&(b.hasCartesianSeries=!0);let g;d.length&&(g=d[d.length-1]);c._i=A(g&&g._i,-1)+1;c.opacity=c.options.opacity;b.orderItems(\"series\",K(this,d));a.dataSorting&&a.dataSorting.enabled?c.setDataSortingOptions():c.points||c.data||c.setData(a.data,!1);n(this,\n\"afterInit\")}is(b){return w[b]&&this instanceof w[b]}bindAxes(){const a=this,c=a.options,d=a.chart;let f;n(this,\"bindAxes\",null,function(){(a.axisTypes||[]).forEach(function(e){d[e].forEach(function(b){f=b.options;if(A(c[e],0)===b.index||\"undefined\"!==typeof c[e]&&c[e]===f.id)K(a,b.series),a[e]=b,b.isDirty=!0});a[e]||a.optionalAxis===e||b(18,!0,d)})});n(this,\"afterBindAxes\")}updateParallelArrays(b,a,c){const d=b.series,f=T(a)?function(c){const f=\"y\"===c&&d.toYData?d.toYData(b):b[c];d[c+\"Data\"][a]=\nf}:function(b){Array.prototype[a].apply(d[b+\"Data\"],c)};d.parallelArrays.forEach(f)}hasData(){return this.visible&&\"undefined\"!==typeof this.dataMax&&\"undefined\"!==typeof this.dataMin||this.visible&&this.yData&&0<this.yData.length}autoIncrement(b){var a=this.options;const c=a.pointIntervalUnit,d=a.relativeXValue,f=this.chart.time;let e=this.xIncrement,g;e=A(e,a.pointStart,0);this.pointInterval=g=A(this.pointInterval,a.pointInterval,1);d&&T(b)&&(g*=b);c&&(a=new f.Date(e),\"day\"===c?f.set(\"Date\",a,f.get(\"Date\",\na)+g):\"month\"===c?f.set(\"Month\",a,f.get(\"Month\",a)+g):\"year\"===c&&f.set(\"FullYear\",a,f.get(\"FullYear\",a)+g),g=a.getTime()-e);if(d&&T(b))return e+g;this.xIncrement=e+g;return e}setDataSortingOptions(){const b=this.options;f(this,{requireSorting:!1,sorted:!1,enabledDataSorting:!0,allowDG:!1});r(b.pointRange)||(b.pointRange=1)}setOptions(b){var a,c;const d=this.chart;var f=d.options.plotOptions,e=d.userOptions||{};const g=V(b);b=d.styledMode;const k={plotOptions:f,userOptions:g};n(this,\"setOptions\",\nk);const h=k.plotOptions[this.type];e=e.plotOptions||{};const l=e.series||{},m=t.plotOptions[this.type]||{},q=e[this.type]||{};this.userOptions=k.userOptions;f=V(h,f.series,q,g);this.tooltipOptions=V(t.tooltip,null===(a=t.plotOptions.series)||void 0===a?void 0:a.tooltip,null===m||void 0===m?void 0:m.tooltip,d.userOptions.tooltip,null===(c=e.series)||void 0===c?void 0:c.tooltip,q.tooltip,g.tooltip);this.stickyTracking=A(g.stickyTracking,q.stickyTracking,l.stickyTracking,this.tooltipOptions.shared&&\n!this.noSharedTooltip?!0:f.stickyTracking);null===h.marker&&delete f.marker;this.zoneAxis=f.zoneAxis;c=this.zones=(f.zones||[]).slice();!f.negativeColor&&!f.negativeFillColor||f.zones||(a={value:f[this.zoneAxis+\"Threshold\"]||f.threshold||0,className:\"highcharts-negative\"},b||(a.color=f.negativeColor,a.fillColor=f.negativeFillColor),c.push(a));c.length&&r(c[c.length-1].value)&&c.push(b?{}:{color:this.color,fillColor:this.fillColor});n(this,\"afterSetOptions\",{options:f});return f}getName(){return A(this.options.name,\n\"Series \"+(this.index+1))}getCyclic(b,a,c){const d=this.chart,f=`${b}Index`,e=`${b}Counter`,g=(null===c||void 0===c?void 0:c.length)||d.options.chart.colorCount;if(!a){var k=A(\"color\"===b?this.options.colorIndex:void 0,this[f]);r(k)||(d.series.length||(d[e]=0),k=d[e]%g,d[e]+=1);c&&(a=c[k])}\"undefined\"!==typeof k&&(this[f]=k);this[b]=a}getColor(){this.chart.styledMode?this.getCyclic(\"color\"):this.options.colorByPoint?this.color=\"#cccccc\":this.getCyclic(\"color\",this.options.color||t.plotOptions[this.type].color,\nthis.chart.options.colors)}getPointsCollection(){return(this.hasGroupedData?this.points:this.data)||[]}getSymbol(){this.getCyclic(\"symbol\",this.options.marker.symbol,this.chart.options.symbols)}findPointIndex(b,a){const d=b.id,f=b.x,e=this.points;var g=this.options.dataSorting,k;let h,n;if(d)g=this.chart.get(d),g instanceof z&&(k=g);else if(this.linkedParent||this.enabledDataSorting||this.options.relativeXValue)if(k=a=>!a.touched&&a.index===b.index,g&&g.matchByName?k=a=>!a.touched&&a.name===b.name:\nthis.options.relativeXValue&&(k=a=>!a.touched&&a.options.x===b.x),k=c(e,k),!k)return;k&&(n=k&&k.index,\"undefined\"!==typeof n&&(h=!0));\"undefined\"===typeof n&&T(f)&&(n=this.xData.indexOf(f,a));-1!==n&&\"undefined\"!==typeof n&&this.cropped&&(n=n>=this.cropStart?n-this.cropStart:n);!h&&T(n)&&e[n]&&e[n].touched&&(n=void 0);return n}updateData(b,a){const c=this.options,d=c.dataSorting,f=this.points,e=[],g=this.requireSorting,k=b.length===f.length;let n,h,l,m=!0;this.xIncrement=null;b.forEach(function(b,\na){var h=r(b)&&this.pointClass.prototype.optionsToObject.call({series:this},b)||{};const m=h.x;if(h.id||T(m)){if(h=this.findPointIndex(h,l),-1===h||\"undefined\"===typeof h?e.push(b):f[h]&&b!==c.data[h]?(f[h].update(b,!1,null,!1),f[h].touched=!0,g&&(l=h+1)):f[h]&&(f[h].touched=!0),!k||a!==h||d&&d.enabled||this.hasDerivedData)n=!0}else e.push(b)},this);if(n)for(b=f.length;b--;)(h=f[b])&&!h.touched&&h.remove&&h.remove(!1,a);else!k||d&&d.enabled?m=!1:(b.forEach(function(b,a){b===f[a].y||f[a].destroyed||\nf[a].update(b,!1,null,!1)}),e.length=0);f.forEach(function(b){b&&(b.touched=!1)});if(!m)return!1;e.forEach(function(b){this.addPoint(b,!1,null,null,!1)},this);null===this.xIncrement&&this.xData&&this.xData.length&&(this.xIncrement=x(this.xData),this.autoIncrement());return!0}setData(a,c=!0,d,f){var e;const g=this,k=g.points,h=k&&k.length||0,n=g.options,l=g.chart,m=n.dataSorting,q=g.xAxis,p=n.turboThreshold,r=this.xData,t=this.yData;var w=g.pointArrayMap;w=w&&w.length;const J=n.keys;let v,u=0,O=1,\nx=null;if(!l.options.chart.allowMutatingData){n.data&&delete g.options.data;g.userOptions.data&&delete g.userOptions.data;var N=V(!0,a)}a=N||a||[];N=a.length;m&&m.enabled&&(a=this.sortData(a));l.options.chart.allowMutatingData&&!1!==f&&N&&h&&!g.cropped&&!g.hasGroupedData&&g.visible&&!g.boosted&&(v=this.updateData(a,d));if(!v){g.xIncrement=null;g.colorCounter=0;this.parallelArrays.forEach(function(b){g[b+\"Data\"].length=0});if(p&&N>p)if(x=g.getFirstValidPoint(a),T(x))for(d=0;d<N;d++)r[d]=this.autoIncrement(),\nt[d]=a[d];else if(X(x))if(w)if(x.length===w)for(d=0;d<N;d++)r[d]=this.autoIncrement(),t[d]=a[d];else for(d=0;d<N;d++)f=a[d],r[d]=f[0],t[d]=f.slice(1,w+1);else if(J&&(u=J.indexOf(\"x\"),O=J.indexOf(\"y\"),u=0<=u?u:0,O=0<=O?O:1),1===x.length&&(O=0),u===O)for(d=0;d<N;d++)r[d]=this.autoIncrement(),t[d]=a[d][O];else for(d=0;d<N;d++)f=a[d],r[d]=f[u],t[d]=f[O];else b(12,!1,l);else for(d=0;d<N;d++)f={series:g},g.pointClass.prototype.applyOptions.apply(f,[a[d]]),g.updateParallelArrays(f,d);t&&Z(t[0])&&b(14,!0,\nl);g.data=[];g.options.data=g.userOptions.data=a;for(d=h;d--;)null===(e=k[d])||void 0===e?void 0:e.destroy();q&&(q.minRange=q.userMinRange);g.isDirty=l.isDirtyBox=!0;g.isDirtyData=!!k;d=!1}\"point\"===n.legendType&&(this.processData(),this.generatePoints());c&&l.redraw(d)}sortData(b){const a=this,c=a.options.dataSorting.sortKey||\"y\",d=function(b,a){return r(a)&&b.pointClass.prototype.optionsToObject.call({series:b},a)||{}};b.forEach(function(c,f){b[f]=d(a,c);b[f].index=f},this);b.concat().sort((b,a)=>\n{b=D(c,b);a=D(c,a);return a<b?-1:a>b?1:0}).forEach(function(b,a){b.x=a},this);a.linkedSeries&&a.linkedSeries.forEach(function(a){const c=a.options,f=c.data;c.dataSorting&&c.dataSorting.enabled||!f||(f.forEach(function(c,e){f[e]=d(a,c);b[e]&&(f[e].x=b[e].x,f[e].index=e)}),a.setData(f,!1))});return b}getProcessedData(a){const c=this;var d=c.xAxis,f=c.options;const e=f.cropThreshold,g=a||c.getExtremesFromAll||f.getExtremesFromAll,k=null===d||void 0===d?void 0:d.logarithmic,h=c.isCartesian;let n=0;let l;\na=c.xData;f=c.yData;let m=!1;const q=a.length;if(d){var r=d.getExtremes();l=r.min;r=r.max;m=!(!d.categories||d.names.length)}if(h&&c.sorted&&!g&&(!e||q>e||c.forceCrop))if(a[q-1]<l||a[0]>r)a=[],f=[];else if(c.yData&&(a[0]<l||a[q-1]>r)){var p=this.cropData(c.xData,c.yData,l,r);a=p.xData;f=p.yData;n=p.start;p=!0}d=P([k?a.map(k.log2lin):a],()=>c.requireSorting&&!m&&b(15,!1,c.chart));return{xData:a,yData:f,cropped:p,cropStart:n,closestPointRange:d}}processData(b){const a=this.xAxis;if(this.isCartesian&&\n!this.isDirty&&!a.isDirty&&!this.yAxis.isDirty&&!b)return!1;b=this.getProcessedData();this.cropped=b.cropped;this.cropStart=b.cropStart;this.processedXData=b.xData;this.processedYData=b.yData;this.closestPointRange=this.basePointRange=b.closestPointRange;n(this,\"afterProcessData\")}cropData(b,a,c,d,f){const e=b.length;let g,k=0,h=e;f=A(f,this.cropShoulder);for(g=0;g<e;g++)if(b[g]>=c){k=Math.max(0,g-f);break}for(c=g;c<e;c++)if(b[c]>d){h=c+f;break}return{xData:b.slice(k,h),yData:a.slice(k,h),start:k,\nend:h}}generatePoints(){var b=this.options;const a=this.processedData||b.data,c=this.processedXData,d=this.processedYData,e=this.pointClass,g=c.length,k=this.cropStart||0,h=this.hasGroupedData,l=b.keys,m=[];b=b.dataGrouping&&b.dataGrouping.groupAll?k:0;let q;let r,p,t=this.data;if(!t&&!h){var w=[];w.length=a.length;t=this.data=w}l&&h&&(this.options.keys=!1);for(p=0;p<g;p++)w=k+p,h?(r=(new e).init(this,[c[p]].concat(ia(d[p]))),r.dataGroup=this.groupMap[b+p],r.dataGroup.options&&(r.options=r.dataGroup.options,\nf(r,r.dataGroup.options),delete r.dataLabels)):(r=t[w])||\"undefined\"===typeof a[w]||(t[w]=r=(new e).init(this,a[w],c[p])),r&&(r.index=h?b+p:w,m[p]=r);this.options.keys=l;if(t&&(g!==(q=t.length)||h))for(p=0;p<q;p++)p!==k||h||(p+=g),t[p]&&(t[p].destroyElements(),t[p].plotX=void 0);this.data=t;this.points=m;n(this,\"afterGeneratePoints\")}getXExtremes(b){return{min:F(b),max:x(b)}}getExtremes(b,a){const c=this.xAxis;var d=this.yAxis;const f=this.processedXData||this.xData,e=[],g=this.requireSorting?this.cropShoulder:\n0;d=d?d.positiveValuesOnly:!1;let k,h=0,l=0,m=0;b=b||this.stackedYData||this.processedYData||[];const q=b.length;if(c){var p=c.getExtremes();h=p.min;l=p.max}for(k=0;k<q;k++){var r=f[k];p=b[k];var t=(T(p)||X(p))&&(p.length||0<p||!d);r=a||this.getExtremesFromAll||this.options.getExtremesFromAll||this.cropped||!c||(f[k+g]||r)>=h&&(f[k-g]||r)<=l;if(t&&r)if(t=p.length)for(;t--;)T(p[t])&&(e[m++]=p[t]);else e[m++]=p}b={activeYData:e,dataMin:F(e),dataMax:x(e)};n(this,\"afterGetExtremes\",{dataExtremes:b});\nreturn b}applyExtremes(){const b=this.getExtremes();this.dataMin=b.dataMin;this.dataMax=b.dataMax;return b}getFirstValidPoint(b){const a=b.length;let c=0,d=null;for(;null===d&&c<a;)d=b[c],c++;return d}translate(){var b;this.processedXData||this.processData();this.generatePoints();var a=this.options;const c=a.stacking,f=this.xAxis,e=f.categories,g=this.enabledDataSorting,h=this.yAxis,l=this.points,m=l.length,q=this.pointPlacementToXValue(),p=!!q,t=a.threshold;a=a.startFromThreshold?t:0;let w,v,u,x,\nD=Number.MAX_VALUE;for(w=0;w<m;w++){const n=l[w],m=n.x;let J,Q,R=n.y,O=n.low;const E=c&&(null===(b=h.stacking)||void 0===b?void 0:b.stacks[(this.negStacks&&R<(a?0:t)?\"-\":\"\")+this.stackKey]);v=f.translate(m,!1,!1,!1,!0,q);n.plotX=T(v)?k(d(v,-1E5,1E5)):void 0;c&&this.visible&&E&&E[m]&&(x=this.getStackIndicator(x,m,this.index),!n.isNull&&x.key&&(J=E[m],Q=J.points[x.key]),J&&X(Q)&&(O=Q[0],R=Q[1],O===a&&x.key===E[m].base&&(O=A(T(t)?t:h.min)),h.positiveValuesOnly&&r(O)&&0>=O&&(O=void 0),n.total=n.stackTotal=\nA(J.total),n.percentage=r(n.y)&&J.total?n.y/J.total*100:void 0,n.stackY=R,this.irregularWidths||J.setOffset(this.pointXOffset||0,this.barW||0,void 0,void 0,void 0,this.xAxis)));n.yBottom=r(O)?d(h.translate(O,!1,!0,!1,!0),-1E5,1E5):void 0;this.dataModify&&(R=this.dataModify.modifyValue(R,w));let N;T(R)&&void 0!==n.plotX&&(N=h.translate(R,!1,!0,!1,!0),N=T(N)?d(N,-1E5,1E5):void 0);n.plotY=N;n.isInside=this.isPointInside(n);n.clientX=p?k(f.translate(m,!1,!1,!1,!0,q)):v;n.negative=(n.y||0)<(t||0);n.category=\nA(e&&e[n.x],n.x);n.isNull||!1===n.visible||(\"undefined\"!==typeof u&&(D=Math.min(D,Math.abs(v-u))),u=v);n.zone=this.zones.length?n.getZone():void 0;!n.graphic&&this.group&&g&&(n.isNew=!0)}this.closestPointRangePx=D;n(this,\"afterTranslate\")}getValidPoints(b,a,c){const d=this.chart;return(b||this.points||[]).filter(function(b){const {plotX:f,plotY:e}=b;return!c&&(b.isNull||!T(e))||a&&!d.isInsidePlot(f,e,{inverted:d.inverted})?!1:!1!==b.visible})}getClipBox(){const {chart:b,xAxis:a,yAxis:c}=this,d=V(b.clipBox);\na&&a.len!==b.plotSizeX&&(d.width=a.len);c&&c.len!==b.plotSizeY&&(d.height=c.len);return d}getSharedClipKey(){return this.sharedClipKey=(this.options.xAxis||0)+\",\"+(this.options.yAxis||0)}setClip(){const {chart:b,group:a,markerGroup:c}=this,d=b.sharedClips,f=b.renderer,e=this.getClipBox(),g=this.getSharedClipKey();let k=d[g];k?k.animate(e):d[g]=k=f.clipRect(e);a&&a.clip(!1===this.options.clip?void 0:k);c&&c.clip()}animate(b){const {chart:a,group:c,markerGroup:d}=this,f=a.inverted;var e=l(this.options.animation),\ng=[this.getSharedClipKey(),e.duration,e.easing,e.defer].join();let k=a.sharedClips[g],n=a.sharedClips[g+\"m\"];if(b&&c)e=this.getClipBox(),k?k.attr(\"height\",e.height):(e.width=0,f&&(e.x=a.plotHeight),k=a.renderer.clipRect(e),a.sharedClips[g]=k,n=a.renderer.clipRect({x:-99,y:-99,width:f?a.plotWidth+199:99,height:f?99:a.plotHeight+199}),a.sharedClips[g+\"m\"]=n),c.clip(k),d&&d.clip(n);else if(k&&!k.hasClass(\"highcharts-animating\")){g=this.getClipBox();const b=e.step;d&&d.element.childNodes.length&&(e.step=\nfunction(a,c){b&&b.apply(c,arguments);\"width\"===c.prop&&n&&n.element&&n.attr(f?\"height\":\"width\",a+99)});k.addClass(\"highcharts-animating\").animate(g,e)}}afterAnimate(){this.setClip();Y(this.chart.sharedClips,(b,a,c)=>{b&&!this.chart.container.querySelector(`[clip-path=\"url(#${b.id})\"]`)&&(b.destroy(),delete c[a])});this.finishedAnimating=!0;n(this,\"afterAnimate\")}drawPoints(b=this.points){const a=this.chart,c=a.styledMode,{colorAxis:d,options:f}=this,e=f.marker,g=this[this.specialGroup||\"markerGroup\"],\nk=this.xAxis,n=A(e.enabled,!k||k.isRadial?!0:null,this.closestPointRangePx>=e.enabledThreshold*e.radius);let h,l,m,q;let p,r;if(!1!==e.enabled||this._hasPointMarkers)for(h=0;h<b.length;h++){l=b[h];q=(m=l.graphic)?\"animate\":\"attr\";var t=l.marker||{};p=!!l.marker;if((n&&\"undefined\"===typeof t.enabled||t.enabled)&&!l.isNull&&!1!==l.visible){const b=A(t.symbol,this.symbol,\"rect\");r=this.markerAttribs(l,l.selected&&\"select\");this.enabledDataSorting&&(l.startXPos=k.reversed?-(r.width||0):k.width);const f=\n!1!==l.isInside;!m&&f&&(0<(r.width||0)||l.hasImage)&&(l.graphic=m=a.renderer.symbol(b,r.x,r.y,r.width,r.height,p?t:e).add(g),this.enabledDataSorting&&a.hasRendered&&(m.attr({x:l.startXPos}),q=\"animate\"));m&&\"animate\"===q&&m[f?\"show\":\"hide\"](f).animate(r);if(m)if(t=this.pointAttribs(l,c||!l.selected?void 0:\"select\"),c)d&&m.css({fill:t.fill});else m[q](t);m&&m.addClass(l.getClassName(),!0)}else m&&(l.graphic=m.destroy())}}markerAttribs(b,a){const c=this.options;var d=c.marker;const f=b.marker||{},e=\nf.symbol||d.symbol,g={};let k=A(f.radius,d&&d.radius);a&&(d=d.states[a],a=f.states&&f.states[a],k=A(a&&a.radius,d&&d.radius,k&&k+(d&&d.radiusPlus||0)));b.hasImage=e&&0===e.indexOf(\"url\");b.hasImage&&(k=0);b=b.pos();T(k)&&b&&(g.x=b[0]-k,g.y=b[1]-k,c.crisp&&(g.x=Math.floor(g.x)));k&&(g.width=g.height=2*k);return g}pointAttribs(b,a){var c=this.options.marker,d=b&&b.options;const f=d&&d.marker||{};var e=d&&d.color,g=b&&b.color;const k=b&&b.zone&&b.zone.color;let n=this.color;b=A(f.lineWidth,c.lineWidth);\nd=1;n=e||k||g||n;e=f.fillColor||c.fillColor||n;g=f.lineColor||c.lineColor||n;a=a||\"normal\";c=c.states[a]||{};a=f.states&&f.states[a]||{};b=A(a.lineWidth,c.lineWidth,b+A(a.lineWidthPlus,c.lineWidthPlus,0));e=a.fillColor||c.fillColor||e;g=a.lineColor||c.lineColor||g;d=A(a.opacity,c.opacity,d);return{stroke:g,\"stroke-width\":b,fill:e,opacity:d}}destroy(b){const a=this,c=a.chart,d=/AppleWebKit\\/533/.test(e.navigator.userAgent),f=a.data||[];let g,k,h,l;n(a,\"destroy\",{keepEventsForUpdate:b});this.removeEvents(b);\n(a.axisTypes||[]).forEach(function(b){(l=a[b])&&l.series&&(G(l.series,a),l.isDirty=l.forceRedraw=!0)});a.legendItem&&a.chart.legend.destroyItem(a);for(k=f.length;k--;)(h=f[k])&&h.destroy&&h.destroy();a.clips&&a.clips.forEach(b=>b.destroy());v.clearTimeout(a.animationTimeout);Y(a,function(b,a){b instanceof u&&!b.survive&&(g=d&&\"group\"===a?\"hide\":\"destroy\",b[g]())});c.hoverSeries===a&&(c.hoverSeries=void 0);G(c.series,a);c.orderItems(\"series\");Y(a,function(c,d){b&&\"hcEvents\"===d||delete a[d]})}applyZones(){const b=\nthis,a=this.chart,c=a.renderer,f=this.zones,e=this.clips||[],g=this.graph,k=this.area,n=Math.max(a.plotWidth,a.plotHeight),h=this[(this.zoneAxis||\"y\")+\"Axis\"],l=a.inverted;let m,q,p,r,t,w,v,u,x,D,E,G=!1;f.length&&(g||k)&&h&&\"undefined\"!==typeof h.min?(t=h.reversed,w=h.horiz,g&&!this.showLine&&g.hide(),k&&k.hide(),r=h.getExtremes(),f.forEach(function(f,Q){m=t?w?a.plotWidth:0:w?0:h.toPixels(r.min)||0;m=d(A(q,m),0,n);q=d(Math.round(h.toPixels(A(f.value,r.max),!0)||0),0,n);G&&(m=q=h.toPixels(r.max));\nv=Math.abs(m-q);u=Math.min(m,q);x=Math.max(m,q);h.isXAxis?(p={x:l?x:u,y:0,width:v,height:n},w||(p.x=a.plotHeight-p.x)):(p={x:0,y:l?x:u,width:n,height:v},w&&(p.y=a.plotWidth-p.y));e[Q]?e[Q].animate(p):e[Q]=c.clipRect(p);D=b[\"zone-area-\"+Q];E=b[\"zone-graph-\"+Q];g&&E&&E.clip(e[Q]);k&&D&&D.clip(e[Q]);G=f.value>r.max;b.resetZones&&0===q&&(q=void 0)}),this.clips=e):b.visible&&(g&&g.show(),k&&k.show())}plotGroup(b,a,c,d,f){let e=this[b];const g=!e;c={visibility:c,zIndex:d||.1};\"undefined\"===typeof this.opacity||\nthis.chart.styledMode||\"inactive\"===this.state||(c.opacity=this.opacity);g&&(this[b]=e=this.chart.renderer.g().add(f));e.addClass(\"highcharts-\"+a+\" highcharts-series-\"+this.index+\" highcharts-\"+this.type+\"-series \"+(r(this.colorIndex)?\"highcharts-color-\"+this.colorIndex+\" \":\"\")+(this.options.className||\"\")+(e.hasClass(\"highcharts-tracker\")?\" highcharts-tracker\":\"\"),!0);e.attr(c)[g?\"attr\":\"animate\"](this.getPlotBox(a));return e}getPlotBox(b){let a=this.xAxis,c=this.yAxis;const d=this.chart;b=d.inverted&&\n!d.polar&&a&&!1!==this.invertible&&\"series\"===b;d.inverted&&(a=c,c=this.xAxis);return{translateX:a?a.left:d.plotLeft,translateY:c?c.top:d.plotTop,rotation:b?90:0,rotationOriginX:b?(a.len-c.len)/2:0,rotationOriginY:b?(a.len+c.len)/2:0,scaleX:b?-1:1,scaleY:1}}removeEvents(b){b||M(this);this.eventsToUnbind.length&&(this.eventsToUnbind.forEach(function(b){b()}),this.eventsToUnbind.length=0)}render(){const b=this;var a=b.chart;const c=b.options,d=l(c.animation),f=b.visible?\"inherit\":\"hidden\",e=c.zIndex,\ng=b.hasRendered;a=a.seriesGroup;let k=b.finishedAnimating?0:d.duration;n(this,\"render\");b.plotGroup(\"group\",\"series\",f,e,a);b.markerGroup=b.plotGroup(\"markerGroup\",\"markers\",f,e,a);!1!==c.clip&&b.setClip();b.animate&&k&&b.animate(!0);b.drawGraph&&(b.drawGraph(),b.applyZones());b.visible&&b.drawPoints();b.drawDataLabels&&b.drawDataLabels();b.redrawPoints&&b.redrawPoints();b.drawTracker&&c.enableMouseTracking&&b.drawTracker();b.animate&&k&&b.animate();g||(k&&d.defer&&(k+=d.defer),b.animationTimeout=\nba(function(){b.afterAnimate()},k||0));b.isDirty=!1;b.hasRendered=!0;n(b,\"afterRender\")}redraw(){const b=this.isDirty||this.isDirtyData;this.translate();this.render();b&&delete this.kdTree}searchPoint(b,a){const c=this.xAxis,d=this.yAxis,f=this.chart.inverted;return this.searchKDTree({clientX:f?c.len-b.chartY+c.pos:b.chartX-c.pos,plotY:f?d.len-b.chartX+d.pos:b.chartY-d.pos},a,b)}buildKDTree(b){function a(b,d,f){var e=b&&b.length;let g;if(e)return g=c.kdAxisArray[d%f],b.sort(function(b,a){return b[g]-\na[g]}),e=Math.floor(e/2),{point:b[e],left:a(b.slice(0,e),d+1,f),right:a(b.slice(e+1),d+1,f)}}this.buildingKdTree=!0;const c=this,d=-1<c.options.findNearestPointBy.indexOf(\"y\")?2:1;delete c.kdTree;ba(function(){c.kdTree=a(c.getValidPoints(null,!c.directTouch),d,d);c.buildingKdTree=!1},c.options.kdNow||b&&\"touchstart\"===b.type?0:1)}searchKDTree(b,a,c){function d(b,a,c,n){const h=a.point;var l=f.kdAxisArray[c%n];let m=h;var q=r(b[e])&&r(h[e])?Math.pow(b[e]-h[e],2):null;var p=r(b[g])&&r(h[g])?Math.pow(b[g]-\nh[g],2):null;p=(q||0)+(p||0);h.dist=r(p)?Math.sqrt(p):Number.MAX_VALUE;h.distX=r(q)?Math.sqrt(q):Number.MAX_VALUE;l=b[l]-h[l];p=0>l?\"left\":\"right\";q=0>l?\"right\":\"left\";a[p]&&(p=d(b,a[p],c+1,n),m=p[k]<m[k]?p:h);a[q]&&Math.sqrt(l*l)<m[k]&&(b=d(b,a[q],c+1,n),m=b[k]<m[k]?b:m);return m}const f=this,e=this.kdAxisArray[0],g=this.kdAxisArray[1],k=a?\"distX\":\"dist\";a=-1<f.options.findNearestPointBy.indexOf(\"y\")?2:1;this.kdTree||this.buildingKdTree||this.buildKDTree(c);if(this.kdTree)return d(b,this.kdTree,\na,a)}pointPlacementToXValue(){const {options:{pointPlacement:b,pointRange:a},xAxis:c}=this;let d=b;\"between\"===d&&(d=c.reversed?-.5:.5);return T(d)?d*(a||c.pointRange):0}isPointInside(b){const {chart:a,xAxis:c,yAxis:d}=this;return\"undefined\"!==typeof b.plotY&&\"undefined\"!==typeof b.plotX&&0<=b.plotY&&b.plotY<=(d?d.len:a.plotHeight)&&0<=b.plotX&&b.plotX<=(c?c.len:a.plotWidth)}drawTracker(){const b=this,a=b.options,c=a.trackByArea,d=[].concat(c?b.areaPath:b.graphPath),f=b.chart,e=f.pointer,k=f.renderer,\nl=f.options.tooltip.snap,m=b.tracker,q=function(c){if(a.enableMouseTracking&&f.hoverSeries!==b)b.onMouseOver()},p=\"rgba(192,192,192,\"+(g?.0001:.002)+\")\";m?m.attr({d}):b.graph&&(b.tracker=k.path(d).attr({visibility:b.visible?\"inherit\":\"hidden\",zIndex:2}).addClass(c?\"highcharts-tracker-area\":\"highcharts-tracker-line\").add(b.group),f.styledMode||b.tracker.attr({\"stroke-linecap\":\"round\",\"stroke-linejoin\":\"round\",stroke:p,fill:c?p:\"none\",\"stroke-width\":b.graph.strokeWidth()+(c?0:2*l)}),[b.tracker,b.markerGroup,\nb.dataLabelsGroup].forEach(function(b){if(b&&(b.addClass(\"highcharts-tracker\").on(\"mouseover\",q).on(\"mouseout\",function(b){e.onTrackerMouseOut(b)}),a.cursor&&!f.styledMode&&b.css({cursor:a.cursor}),h))b.on(\"touchstart\",q)}));n(this,\"afterDrawTracker\")}addPoint(b,a,c,d,f){const e=this.options,g=this.data,k=this.chart;var h=this.xAxis;h=h&&h.hasNames&&h.names;const l=e.data,m=this.xData;let q,p;a=A(a,!0);const r={series:this};this.pointClass.prototype.applyOptions.apply(r,[b]);const t=r.x;p=m.length;\nif(this.requireSorting&&t<m[p-1])for(q=!0;p&&m[p-1]>t;)p--;this.updateParallelArrays(r,\"splice\",[p,0,0]);this.updateParallelArrays(r,p);h&&r.name&&(h[t]=r.name);l.splice(p,0,b);if(q||this.processedData)this.data.splice(p,0,null),this.processData();\"point\"===e.legendType&&this.generatePoints();c&&(g[0]&&g[0].remove?g[0].remove(!1):(g.shift(),this.updateParallelArrays(r,\"shift\"),l.shift()));!1!==f&&n(this,\"addPoint\",{point:r});this.isDirtyData=this.isDirty=!0;a&&k.redraw(d)}removePoint(b,a,c){const d=\nthis,f=d.data,e=f[b],g=d.points,k=d.chart,h=function(){g&&g.length===f.length&&g.splice(b,1);f.splice(b,1);d.options.data.splice(b,1);d.updateParallelArrays(e||{series:d},\"splice\",[b,1]);e&&e.destroy();d.isDirty=!0;d.isDirtyData=!0;a&&k.redraw()};p(c,k);a=A(a,!0);e?e.firePointEvent(\"remove\",null,h):h()}remove(b,a,c,d){function f(){e.destroy(d);g.isDirtyLegend=g.isDirtyBox=!0;g.linkSeries(d);A(b,!0)&&g.redraw(a)}const e=this,g=e.chart;!1!==c?n(e,\"remove\",null,f):f()}update(a,c){a=q(a,this.userOptions);\nn(this,\"update\",{options:a});const d=this,e=d.chart;var g=d.userOptions;const k=d.initialType||d.type;var h=e.options.plotOptions;const l=w[k].prototype;var m=d.finishedAnimating&&{animation:!1};const p={};let r,t=[\"colorIndex\",\"eventOptions\",\"navigatorSeries\",\"symbolIndex\",\"baseSeries\"],v=a.type||g.type||e.options.chart.type;const u=!(this.hasDerivedData||v&&v!==this.type||\"undefined\"!==typeof a.pointStart||\"undefined\"!==typeof a.pointInterval||\"undefined\"!==typeof a.relativeXValue||a.joinBy||a.mapData||\nd.hasOptionChanged(\"dataGrouping\")||d.hasOptionChanged(\"pointStart\")||d.hasOptionChanged(\"pointInterval\")||d.hasOptionChanged(\"pointIntervalUnit\")||d.hasOptionChanged(\"keys\"));v=v||k;u&&(t.push(\"data\",\"isDirtyData\",\"points\",\"processedData\",\"processedXData\",\"processedYData\",\"xIncrement\",\"cropped\",\"_hasPointMarkers\",\"_hasPointLabels\",\"clips\",\"nodes\",\"layout\",\"level\",\"mapMap\",\"mapData\",\"minY\",\"maxY\",\"minX\",\"maxX\"),!1!==a.visible&&t.push(\"area\",\"graph\"),d.parallelArrays.forEach(function(b){t.push(b+\"Data\")}),\na.data&&(a.dataSorting&&f(d.options.dataSorting,a.dataSorting),this.setData(a.data,!1)));a=V(g,m,{index:\"undefined\"===typeof g.index?d.index:g.index,pointStart:A(h&&h.series&&h.series.pointStart,g.pointStart,d.xData[0])},!u&&{data:d.options.data},a);u&&a.data&&(a.data=d.options.data);t=[\"group\",\"markerGroup\",\"dataLabelsGroup\",\"transformGroup\"].concat(t);t.forEach(function(b){t[b]=d[b];delete d[b]});h=!1;if(w[v]){if(h=v!==d.type,d.remove(!1,!1,!1,!0),h)if(Object.setPrototypeOf)Object.setPrototypeOf(d,\nw[v].prototype);else{m=Object.hasOwnProperty.call(d,\"hcEvents\")&&d.hcEvents;for(r in l)d[r]=void 0;f(d,w[v].prototype);m?d.hcEvents=m:delete d.hcEvents}}else b(17,!0,e,{missingModuleFor:v});t.forEach(function(b){d[b]=t[b]});d.init(e,a);if(u&&this.points){a=d.options;if(!1===a.visible)p.graphic=1,p.dataLabel=1;else if(!d._hasPointLabels){const {marker:b,dataLabels:c}=a;g=g.marker||{};!b||!1!==b.enabled&&g.symbol===b.symbol&&g.height===b.height&&g.width===b.width||(p.graphic=1);c&&!1===c.enabled&&(p.dataLabel=\n1)}for(const b of this.points)b&&b.series&&(b.resolveColor(),Object.keys(p).length&&b.destroyElements(p),!1===a.showInLegend&&b.legendItem&&e.legend.destroyItem(b))}d.initialType=k;e.linkSeries();h&&d.linkedSeries.length&&(d.isDirtyData=!0);n(this,\"afterUpdate\");A(c,!0)&&e.redraw(u?void 0:!1)}setName(b){this.name=this.options.name=this.userOptions.name=b;this.chart.isDirtyLegend=!0}hasOptionChanged(b){const a=this.options[b],c=this.chart.options.plotOptions,d=this.userOptions[b];return d?a!==d:a!==\nA(c&&c[this.type]&&c[this.type][b],c&&c.series&&c.series[b],a)}onMouseOver(){const b=this.chart,a=b.hoverSeries;b.pointer.setHoverChartIndex();if(a&&a!==this)a.onMouseOut();this.options.events.mouseOver&&n(this,\"mouseOver\");this.setState(\"hover\");b.hoverSeries=this}onMouseOut(){const b=this.options,a=this.chart,c=a.tooltip,d=a.hoverPoint;a.hoverSeries=null;if(d)d.onMouseOut();this&&b.events.mouseOut&&n(this,\"mouseOut\");!c||this.stickyTracking||c.shared&&!this.noSharedTooltip||c.hide();a.series.forEach(function(b){b.setState(\"\",\n!0)})}setState(b,a){const c=this;var d=c.options;const f=c.graph,e=d.inactiveOtherPoints,g=d.states,k=A(g[b||\"normal\"]&&g[b||\"normal\"].animation,c.chart.options.chart.animation);let h=d.lineWidth,n=0,l=d.opacity;b=b||\"\";if(c.state!==b&&([c.group,c.markerGroup,c.dataLabelsGroup].forEach(function(a){a&&(c.state&&a.removeClass(\"highcharts-series-\"+c.state),b&&a.addClass(\"highcharts-series-\"+b))}),c.state=b,!c.chart.styledMode)){if(g[b]&&!1===g[b].enabled)return;b&&(h=g[b].lineWidth||h+(g[b].lineWidthPlus||\n0),l=A(g[b].opacity,l));if(f&&!f.dashstyle&&T(h))for(d={\"stroke-width\":h},f.animate(d,k);c[\"zone-graph-\"+n];)c[\"zone-graph-\"+n].animate(d,k),n+=1;e||[c.group,c.markerGroup,c.dataLabelsGroup,c.labelBySeries].forEach(function(b){b&&b.animate({opacity:l},k)})}a&&e&&c.points&&c.setAllPointsToState(b||void 0)}setAllPointsToState(b){this.points.forEach(function(a){a.setState&&a.setState(b)})}setVisible(b,a){const c=this,d=c.chart,f=d.options.chart.ignoreHiddenSeries,e=c.visible,g=(c.visible=b=c.options.visible=\nc.userOptions.visible=\"undefined\"===typeof b?!e:b)?\"show\":\"hide\";[\"group\",\"dataLabelsGroup\",\"markerGroup\",\"tracker\",\"tt\"].forEach(function(b){if(c[b])c[b][g]()});if(d.hoverSeries===c||(d.hoverPoint&&d.hoverPoint.series)===c)c.onMouseOut();c.legendItem&&d.legend.colorizeItem(c,b);c.isDirty=!0;c.options.stacking&&d.series.forEach(function(b){b.options.stacking&&b.visible&&(b.isDirty=!0)});c.linkedSeries.forEach(function(a){a.setVisible(b,!1)});f&&(d.isDirtyBox=!0);n(c,g);!1!==a&&d.redraw()}show(){this.setVisible(!0)}hide(){this.setVisible(!1)}select(b){this.selected=\nb=this.options.selected=\"undefined\"===typeof b?!this.selected:b;this.checkbox&&(this.checkbox.checked=b);n(this,b?\"select\":\"unselect\")}shouldShowTooltip(b,a,c={}){c.series=this;c.visiblePlotOnly=!0;return this.chart.isInsidePlot(b,a,c)}drawLegendSymbol(b,a){var c;null===(c=C[this.options.legendSymbol||\"rectangle\"])||void 0===c?void 0:c.call(this,b,a)}}aa.defaultOptions=H;aa.types=B.seriesTypes;aa.registerType=B.registerSeriesType;f(aa.prototype,{axisTypes:[\"xAxis\",\"yAxis\"],coll:\"series\",colorCounter:0,\ncropShoulder:1,directTouch:!1,isCartesian:!0,kdAxisArray:[\"clientX\",\"plotY\"],parallelArrays:[\"x\",\"y\"],pointClass:z,requireSorting:!0,sorted:!0});B.series=aa;\"\";\"\";return aa});M(a,\"Core/Chart/Chart.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Axis/Axis.js\"],a[\"Core/Defaults.js\"],a[\"Core/Templating.js\"],a[\"Core/Foundation.js\"],a[\"Core/Globals.js\"],a[\"Core/Renderer/RendererRegistry.js\"],a[\"Core/Series/Series.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Renderer/SVG/SVGRenderer.js\"],a[\"Core/Time.js\"],\na[\"Core/Utilities.js\"],a[\"Core/Renderer/HTML/AST.js\"]],function(a,y,I,L,C,z,H,B,u,v,l,p,t){const {animate:m,animObject:h,setAnimation:g}=a,{defaultOptions:e,defaultTime:w}=I,{numberFormat:x}=L,{registerEventOptions:F}=C,{charts:d,doc:k,marginNames:r,svg:q,win:G}=z,{seriesTypes:b}=u,{addEvent:f,attr:c,createElement:n,css:P,defined:D,diffObjects:K,discardElement:X,erase:T,error:Z,extend:V,find:Y,fireEvent:A,getStyle:M,isArray:ia,isNumber:ba,isObject:aa,isString:J,merge:N,objectEach:O,pick:S,pInt:W,\nrelativeLength:ha,removeEvent:da,splat:fa,syncTimeout:ka,uniqueKey:ca}=p;class ea{static chart(b,a,c){return new ea(b,a,c)}constructor(b,a,c){this.series=this.renderTo=this.renderer=this.pointer=this.pointCount=this.plotWidth=this.plotTop=this.plotLeft=this.plotHeight=this.plotBox=this.options=this.numberFormatter=this.margin=this.labelCollectors=this.isResizing=this.index=this.eventOptions=this.container=this.colorCounter=this.clipBox=this.chartWidth=this.chartHeight=this.bounds=this.axisOffset=\nthis.axes=void 0;this.sharedClips={};this.zooming=this.yAxis=this.xAxis=this.userOptions=this.titleOffset=this.time=this.symbolCounter=this.spacingBox=this.spacing=void 0;this.getArgs(b,a,c)}getArgs(b,a,c){J(b)||b.nodeName?(this.renderTo=b,this.init(a,c)):this.init(b,a)}setZoomOptions(){const b=this.options.chart,a=b.zooming;this.zooming=Object.assign(Object.assign({},a),{type:S(b.zoomType,a.type),key:S(b.zoomKey,a.key),pinchType:S(b.pinchType,a.pinchType),singleTouch:S(b.zoomBySingleTouch,a.singleTouch,\n!1),resetButton:N(a.resetButton,b.resetZoomButton)})}init(b,a){A(this,\"init\",{args:arguments},function(){const c=N(e,b),f=c.chart;this.userOptions=V({},b);this.margin=[];this.spacing=[];this.bounds={h:{},v:{}};this.labelCollectors=[];this.callback=a;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.time=b.time&&Object.keys(b.time).length?new l(b.time):z.time;this.numberFormatter=f.numberFormatter||x;this.styledMode=f.styledMode;this.hasCartesianSeries=f.showAxes;this.index=d.length;\nd.push(this);z.chartCount++;F(this,f);this.xAxis=[];this.yAxis=[];this.pointCount=this.colorCounter=this.symbolCounter=0;this.setZoomOptions();A(this,\"afterInit\");this.firstRender()})}initSeries(a){var c=this.options.chart;c=a.type||c.type;const d=b[c];d||Z(17,!0,this,{missingModuleFor:c});c=new d;\"function\"===typeof c.init&&c.init(this,a);return c}setSeriesData(){this.getSeriesOrderByLinks().forEach(function(b){b.points||b.data||!b.enabledDataSorting||b.setData(b.options.data,!1)})}getSeriesOrderByLinks(){return this.series.concat().sort(function(b,\na){return b.linkedSeries.length||a.linkedSeries.length?a.linkedSeries.length-b.linkedSeries.length:0})}orderItems(b,a=0){const c=this[b],d=this.options[b]=fa(this.options[b]).slice();b=this.userOptions[b]=this.userOptions[b]?fa(this.userOptions[b]).slice():[];this.hasRendered&&(d.splice(a),b.splice(a));if(c)for(let f=a,e=c.length;f<e;++f)if(a=c[f])a.index=f,a instanceof B&&(a.name=a.getName()),a.options.isInternal||(d[f]=a.options,b[f]=a.userOptions)}isInsidePlot(b,a,c={}){const {inverted:d,plotBox:f,\nplotLeft:e,plotTop:g,scrollablePlotBox:k}=this;var h=0;let n=0;c.visiblePlotOnly&&this.scrollingContainer&&({scrollLeft:h,scrollTop:n}=this.scrollingContainer);const l=c.series,m=c.visiblePlotOnly&&k||f;var q=c.inverted?a:b;a=c.inverted?b:a;b={x:q,y:a,isInsidePlot:!0,options:c};if(!c.ignoreX){const a=l&&(d&&!this.polar?l.yAxis:l.xAxis)||{pos:e,len:Infinity};q=c.paneCoordinates?a.pos+q:e+q;q>=Math.max(h+e,a.pos)&&q<=Math.min(h+e+m.width,a.pos+a.len)||(b.isInsidePlot=!1)}!c.ignoreY&&b.isInsidePlot&&\n(h=!d&&c.axis&&!c.axis.isXAxis&&c.axis||l&&(d?l.xAxis:l.yAxis)||{pos:g,len:Infinity},c=c.paneCoordinates?h.pos+a:g+a,c>=Math.max(n+g,h.pos)&&c<=Math.min(n+g+m.height,h.pos+h.len)||(b.isInsidePlot=!1));A(this,\"afterIsInsidePlot\",b);return b.isInsidePlot}redraw(b){A(this,\"beforeRedraw\");const a=this.hasCartesianSeries?this.axes:this.colorAxis||[],c=this.series,d=this.pointer,f=this.legend,e=this.userOptions.legend,k=this.renderer,h=k.isHidden(),n=[];let l,m,q=this.isDirtyBox,p=this.isDirtyLegend,r;\nk.rootFontSize=k.boxWrapper.getStyle(\"font-size\");this.setResponsive&&this.setResponsive(!1);g(this.hasRendered?b:!1,this);h&&this.temporaryDisplay();this.layOutTitles(!1);for(b=c.length;b--;)if(r=c[b],r.options.stacking||r.options.centerInCategory)if(m=!0,r.isDirty){l=!0;break}if(l)for(b=c.length;b--;)r=c[b],r.options.stacking&&(r.isDirty=!0);c.forEach(function(b){b.isDirty&&(\"point\"===b.options.legendType?(\"function\"===typeof b.updateTotals&&b.updateTotals(),p=!0):e&&(e.labelFormatter||e.labelFormat)&&\n(p=!0));b.isDirtyData&&A(b,\"updatedData\")});p&&f&&f.options.enabled&&(f.render(),this.isDirtyLegend=!1);m&&this.getStacks();a.forEach(function(b){b.updateNames();b.setScale()});this.getMargins();a.forEach(function(b){b.isDirty&&(q=!0)});a.forEach(function(b){const a=b.min+\",\"+b.max;b.extKey!==a&&(b.extKey=a,n.push(function(){A(b,\"afterSetExtremes\",V(b.eventArgs,b.getExtremes()));delete b.eventArgs}));(q||m)&&b.redraw()});q&&this.drawChartBox();A(this,\"predraw\");c.forEach(function(b){(q||b.isDirty)&&\nb.visible&&b.redraw();b.isDirtyData=!1});d&&d.reset(!0);k.draw();A(this,\"redraw\");A(this,\"render\");h&&this.temporaryDisplay(!0);n.forEach(function(b){b.call()})}get(b){function a(a){return a.id===b||a.options&&a.options.id===b}const c=this.series;let d=Y(this.axes,a)||Y(this.series,a);for(let b=0;!d&&b<c.length;b++)d=Y(c[b].points||[],a);return d}getAxes(){const b=this.options;A(this,\"getAxes\");for(const a of[\"xAxis\",\"yAxis\"]){const c=b[a]=fa(b[a]||{});for(const b of c)new y(this,b,a)}A(this,\"afterGetAxes\")}getSelectedPoints(){return this.series.reduce((b,\na)=>{a.getPointsCollection().forEach(a=>{S(a.selectedStaging,a.selected)&&b.push(a)});return b},[])}getSelectedSeries(){return this.series.filter(function(b){return b.selected})}setTitle(b,a,c){this.applyDescription(\"title\",b);this.applyDescription(\"subtitle\",a);this.applyDescription(\"caption\",void 0);this.layOutTitles(c)}applyDescription(b,a){const c=this,d=this.options[b]=N(this.options[b],a);let f=this[b];f&&a&&(this[b]=f=f.destroy());d&&!f&&(f=this.renderer.text(d.text,0,0,d.useHTML).attr({align:d.align,\n\"class\":\"highcharts-\"+b,zIndex:d.zIndex||4}).add(),f.update=function(a,d){c.applyDescription(b,a);c.layOutTitles(d)},this.styledMode||f.css(V(\"title\"===b?{fontSize:this.options.isStock?\"1em\":\"1.2em\"}:{},d.style)),this[b]=f)}layOutTitles(b=!0){const a=[0,0,0],c=this.renderer,d=this.spacingBox;[\"title\",\"subtitle\",\"caption\"].forEach(function(b){const f=this[b],e=this.options[b],g=e.verticalAlign||\"top\";b=\"title\"===b?\"top\"===g?-3:0:\"top\"===g?a[0]+2:0;if(f){f.css({width:(e.width||d.width+(e.widthAdjust||\n0))+\"px\"});const k=c.fontMetrics(f).b,h=Math.round(f.getBBox(e.useHTML).height);f.align(V({y:\"bottom\"===g?k:b+k,height:h},e),!1,\"spacingBox\");e.floating||(\"top\"===g?a[0]=Math.ceil(a[0]+h):\"bottom\"===g&&(a[2]=Math.ceil(a[2]+h)))}},this);a[0]&&\"top\"===(this.options.title.verticalAlign||\"top\")&&(a[0]+=this.options.title.margin);a[2]&&\"bottom\"===this.options.caption.verticalAlign&&(a[2]+=this.options.caption.margin);const f=!this.titleOffset||this.titleOffset.join(\",\")!==a.join(\",\");this.titleOffset=\na;A(this,\"afterLayOutTitles\");!this.isDirtyBox&&f&&(this.isDirtyBox=this.isDirtyLegend=f,this.hasRendered&&b&&this.isDirtyBox&&this.redraw())}getContainerBox(){return{width:M(this.renderTo,\"width\",!0)||0,height:M(this.renderTo,\"height\",!0)||0}}getChartSize(){var b=this.options.chart;const a=b.width;b=b.height;const c=this.getContainerBox();this.chartWidth=Math.max(0,a||c.width||600);this.chartHeight=Math.max(0,ha(b,this.chartWidth)||(1<c.height?c.height:400));this.containerBox=c}temporaryDisplay(b){let a=\nthis.renderTo;if(b)for(;a&&a.style;)a.hcOrigStyle&&(P(a,a.hcOrigStyle),delete a.hcOrigStyle),a.hcOrigDetached&&(k.body.removeChild(a),a.hcOrigDetached=!1),a=a.parentNode;else for(;a&&a.style;){k.body.contains(a)||a.parentNode||(a.hcOrigDetached=!0,k.body.appendChild(a));if(\"none\"===M(a,\"display\",!1)||a.hcOricDetached)a.hcOrigStyle={display:a.style.display,height:a.style.height,overflow:a.style.overflow},b={display:\"block\",overflow:\"hidden\"},a!==this.renderTo&&(b.height=0),P(a,b),a.offsetWidth||a.style.setProperty(\"display\",\n\"block\",\"important\");a=a.parentNode;if(a===k.body)break}}setClassName(b){this.container.className=\"highcharts-container \"+(b||\"\")}getContainer(){const b=this.options,a=b.chart;var f=ca();let e,h=this.renderTo;h||(this.renderTo=h=a.renderTo);J(h)&&(this.renderTo=h=k.getElementById(h));h||Z(13,!0,this);var l=W(c(h,\"data-highcharts-chart\"));ba(l)&&d[l]&&d[l].hasRendered&&d[l].destroy();c(h,\"data-highcharts-chart\",this.index);h.innerHTML=t.emptyHTML;a.skipClone||h.offsetWidth||this.temporaryDisplay();\nthis.getChartSize();l=this.chartWidth;const m=this.chartHeight;P(h,{overflow:\"hidden\"});this.styledMode||(e=V({position:\"relative\",overflow:\"hidden\",width:l+\"px\",height:m+\"px\",textAlign:\"left\",lineHeight:\"normal\",zIndex:0,\"-webkit-tap-highlight-color\":\"rgba(0,0,0,0)\",userSelect:\"none\",\"touch-action\":\"manipulation\",outline:\"none\"},a.style||{}));this.container=f=n(\"div\",{id:f},e,h);this._cursor=f.style.cursor;this.renderer=new (a.renderer||!q?H.getRendererType(a.renderer):v)(f,l,m,void 0,a.forExport,\nb.exporting&&b.exporting.allowHTML,this.styledMode);this.containerBox=this.getContainerBox();g(void 0,this);this.setClassName(a.className);if(this.styledMode)for(const a in b.defs)this.renderer.definition(b.defs[a]);else this.renderer.setStyle(a.style);this.renderer.chartIndex=this.index;A(this,\"afterGetContainer\")}getMargins(b){const {spacing:a,margin:c,titleOffset:d}=this;this.resetMargins();d[0]&&!D(c[0])&&(this.plotTop=Math.max(this.plotTop,d[0]+a[0]));d[2]&&!D(c[2])&&(this.marginBottom=Math.max(this.marginBottom,\nd[2]+a[2]));this.legend&&this.legend.display&&this.legend.adjustMargins(c,a);A(this,\"getMargins\");b||this.getAxisMargins()}getAxisMargins(){const b=this,a=b.axisOffset=[0,0,0,0],c=b.colorAxis,d=b.margin,f=function(b){b.forEach(function(b){b.visible&&b.getOffset()})};b.hasCartesianSeries?f(b.axes):c&&c.length&&f(c);r.forEach(function(c,f){D(d[f])||(b[c]+=a[f])});b.setChartSize()}getOptions(){return K(this.userOptions,e)}reflow(b){const a=this;var c=a.options.chart;c=D(c.width)&&D(c.height);const d=\na.containerBox,f=a.getContainerBox();delete a.pointer.chartPosition;if(!c&&!a.isPrinting&&d&&f.width){if(f.width!==d.width||f.height!==d.height)p.clearTimeout(a.reflowTimeout),a.reflowTimeout=ka(function(){a.container&&a.setSize(void 0,void 0,!1)},b?100:0);a.containerBox=f}}setReflow(){const b=this;var a=a=>{var c;(null===(c=b.options)||void 0===c?0:c.chart.reflow)&&b.hasLoaded&&b.reflow(a)};\"function\"===typeof ResizeObserver?(new ResizeObserver(a)).observe(b.renderTo):(a=f(G,\"resize\",a),f(this,\"destroy\",\na))}setSize(b,a,c){const d=this,f=d.renderer;d.isResizing+=1;g(c,d);c=f.globalAnimation;d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;\"undefined\"!==typeof b&&(d.options.chart.width=b);\"undefined\"!==typeof a&&(d.options.chart.height=a);d.getChartSize();d.styledMode||(c?m:P)(d.container,{width:d.chartWidth+\"px\",height:d.chartHeight+\"px\"},c);d.setChartSize(!0);f.setSize(d.chartWidth,d.chartHeight,c);d.axes.forEach(function(b){b.isDirty=!0;b.setScale()});d.isDirtyLegend=!0;d.isDirtyBox=\n!0;d.layOutTitles();d.getMargins();d.redraw(c);d.oldChartHeight=null;A(d,\"resize\");ka(function(){d&&A(d,\"endResize\",null,function(){--d.isResizing})},h(c).duration)}setChartSize(b){var a=this.inverted;const c=this.renderer;var d=this.chartWidth,f=this.chartHeight;const e=this.options.chart,g=this.spacing,k=this.clipOffset;let h,n,l,m;this.plotLeft=h=Math.round(this.plotLeft);this.plotTop=n=Math.round(this.plotTop);this.plotWidth=l=Math.max(0,Math.round(d-h-this.marginRight));this.plotHeight=m=Math.max(0,\nMath.round(f-n-this.marginBottom));this.plotSizeX=a?m:l;this.plotSizeY=a?l:m;this.plotBorderWidth=e.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:g[3],y:g[0],width:d-g[3]-g[1],height:f-g[0]-g[2]};this.plotBox=c.plotBox={x:h,y:n,width:l,height:m};a=2*Math.floor(this.plotBorderWidth/2);d=Math.ceil(Math.max(a,k[3])/2);f=Math.ceil(Math.max(a,k[0])/2);this.clipBox={x:d,y:f,width:Math.floor(this.plotSizeX-Math.max(a,k[1])/2-d),height:Math.max(0,Math.floor(this.plotSizeY-Math.max(a,k[2])/2-f))};b||\n(this.axes.forEach(function(b){b.setAxisSize();b.setAxisTranslation()}),c.alignElements());A(this,\"afterSetChartSize\",{skipAxes:b})}resetMargins(){A(this,\"resetMargins\");const b=this,a=b.options.chart;[\"margin\",\"spacing\"].forEach(function(c){const d=a[c],f=aa(d)?d:[d,d,d,d];[\"Top\",\"Right\",\"Bottom\",\"Left\"].forEach(function(d,e){b[c][e]=S(a[c+d],f[e])})});r.forEach(function(a,c){b[a]=S(b.margin[c],b.spacing[c])});b.axisOffset=[0,0,0,0];b.clipOffset=[0,0,0,0]}drawChartBox(){const b=this.options.chart,\na=this.renderer,c=this.chartWidth,d=this.chartHeight,f=this.styledMode,e=this.plotBGImage;var g=b.backgroundColor;const k=b.plotBackgroundColor,h=b.plotBackgroundImage,n=this.plotLeft,l=this.plotTop,m=this.plotWidth,q=this.plotHeight,p=this.plotBox,r=this.clipRect,t=this.clipBox;let w=this.chartBackground,v=this.plotBackground,u=this.plotBorder,x,D,E=\"animate\";w||(this.chartBackground=w=a.rect().addClass(\"highcharts-background\").add(),E=\"attr\");if(f)x=D=w.strokeWidth();else{x=b.borderWidth||0;D=x+\n(b.shadow?8:0);g={fill:g||\"none\"};if(x||w[\"stroke-width\"])g.stroke=b.borderColor,g[\"stroke-width\"]=x;w.attr(g).shadow(b.shadow)}w[E]({x:D/2,y:D/2,width:c-D-x%2,height:d-D-x%2,r:b.borderRadius});E=\"animate\";v||(E=\"attr\",this.plotBackground=v=a.rect().addClass(\"highcharts-plot-background\").add());v[E](p);f||(v.attr({fill:k||\"none\"}).shadow(b.plotShadow),h&&(e?(h!==e.attr(\"href\")&&e.attr(\"href\",h),e.animate(p)):this.plotBGImage=a.image(h,n,l,m,q).add()));r?r.animate({width:t.width,height:t.height}):\nthis.clipRect=a.clipRect(t);E=\"animate\";u||(E=\"attr\",this.plotBorder=u=a.rect().addClass(\"highcharts-plot-border\").attr({zIndex:1}).add());f||u.attr({stroke:b.plotBorderColor,\"stroke-width\":b.plotBorderWidth||0,fill:\"none\"});u[E](u.crisp({x:n,y:l,width:m,height:q},-u.strokeWidth()));this.isDirtyBox=!1;A(this,\"afterDrawChartBox\")}propFromSeries(){const a=this,c=a.options.chart,d=a.options.series;let f,e,g;[\"inverted\",\"angular\",\"polar\"].forEach(function(k){e=b[c.type];g=c[k]||e&&e.prototype[k];for(f=\nd&&d.length;!g&&f--;)(e=b[d[f].type])&&e.prototype[k]&&(g=!0);a[k]=g})}linkSeries(b){const a=this,c=a.series;c.forEach(function(b){b.linkedSeries.length=0});c.forEach(function(b){let c=b.options.linkedTo;J(c)&&(c=\":previous\"===c?a.series[b.index-1]:a.get(c))&&c.linkedParent!==b&&(c.linkedSeries.push(b),b.linkedParent=c,c.enabledDataSorting&&b.setDataSortingOptions(),b.visible=S(b.options.visible,c.options.visible,b.visible))});A(this,\"afterLinkSeries\",{isUpdating:b})}renderSeries(){this.series.forEach(function(b){b.translate();\nb.render()})}render(){const b=this.axes,a=this.colorAxis,c=this.renderer,d=function(b){b.forEach(function(b){b.visible&&b.render()})};let f=0;this.setTitle();A(this,\"beforeMargins\");this.getStacks&&this.getStacks();this.getMargins(!0);this.setChartSize();const e=this.plotWidth;b.some(function(b){if(b.horiz&&b.visible&&b.options.labels.enabled&&b.series.length)return f=21,!0});const g=this.plotHeight=Math.max(this.plotHeight-f,0);b.forEach(function(b){b.setScale()});this.getAxisMargins();const k=1.1<\ne/this.plotWidth,h=1.05<g/this.plotHeight;if(k||h)b.forEach(function(b){(b.horiz&&k||!b.horiz&&h)&&b.setTickInterval(!0)}),this.getMargins();this.drawChartBox();this.hasCartesianSeries?d(b):a&&a.length&&d(a);this.seriesGroup||(this.seriesGroup=c.g(\"series-group\").attr({zIndex:3}).shadow(this.options.chart.seriesGroupShadow).add());this.renderSeries();this.addCredits();this.setResponsive&&this.setResponsive();this.hasRendered=!0}addCredits(b){const a=this,c=N(!0,this.options.credits,b);c.enabled&&\n!this.credits&&(this.credits=this.renderer.text(c.text+(this.mapCredits||\"\"),0,0).addClass(\"highcharts-credits\").on(\"click\",function(){c.href&&(G.location.href=c.href)}).attr({align:c.position.align,zIndex:8}),a.styledMode||this.credits.css(c.style),this.credits.add().align(c.position),this.credits.update=function(b){a.credits=a.credits.destroy();a.addCredits(b)})}destroy(){const b=this,a=b.axes,c=b.series,f=b.container,e=f&&f.parentNode;let g;A(b,\"destroy\");b.renderer.forExport?T(d,b):d[b.index]=\nvoid 0;z.chartCount--;b.renderTo.removeAttribute(\"data-highcharts-chart\");da(b);for(g=a.length;g--;)a[g]=a[g].destroy();this.scroller&&this.scroller.destroy&&this.scroller.destroy();for(g=c.length;g--;)c[g]=c[g].destroy();\"title subtitle chartBackground plotBackground plotBGImage plotBorder seriesGroup clipRect credits pointer rangeSelector legend resetZoomButton tooltip renderer\".split(\" \").forEach(function(a){const c=b[a];c&&c.destroy&&(b[a]=c.destroy())});f&&(f.innerHTML=t.emptyHTML,da(f),e&&X(f));\nO(b,function(a,c){delete b[c]})}firstRender(){const b=this,a=b.options;b.getContainer();b.resetMargins();b.setChartSize();b.propFromSeries();b.getAxes();const c=ia(a.series)?a.series:[];a.series=[];c.forEach(function(a){b.initSeries(a)});b.linkSeries();b.setSeriesData();A(b,\"beforeRender\");b.render();b.pointer.getChartPosition();if(!b.renderer.imgCount&&!b.hasLoaded)b.onload();b.temporaryDisplay(!0)}onload(){this.callbacks.concat([this.callback]).forEach(function(b){b&&\"undefined\"!==typeof this.index&&\nb.apply(this,[this])},this);A(this,\"load\");A(this,\"render\");D(this.index)&&this.setReflow();this.warnIfA11yModuleNotLoaded();this.hasLoaded=!0}warnIfA11yModuleNotLoaded(){const {options:b,title:a}=this;b&&!this.accessibility&&(this.renderer.boxWrapper.attr({role:\"img\",\"aria-label\":(a&&a.element.textContent||\"\").replace(/</g,\"&lt;\")}),b.accessibility&&!1===b.accessibility.enabled||Z('Highcharts warning: Consider including the \"accessibility.js\" module to make your chart more usable for people with disabilities. Set the \"accessibility.enabled\" option to false to remove this warning. See https://www.highcharts.com/docs/accessibility/accessibility-module.',\n!1,this))}addSeries(b,a,c){const d=this;let f;b&&(a=S(a,!0),A(d,\"addSeries\",{options:b},function(){f=d.initSeries(b);d.isDirtyLegend=!0;d.linkSeries();f.enabledDataSorting&&f.setData(b.data,!1);A(d,\"afterAddSeries\",{series:f});a&&d.redraw(c)}));return f}addAxis(b,a,c,d){return this.createAxis(a?\"xAxis\":\"yAxis\",{axis:b,redraw:c,animation:d})}addColorAxis(b,a,c){return this.createAxis(\"colorAxis\",{axis:b,redraw:a,animation:c})}createAxis(b,a){b=new y(this,a.axis,b);S(a.redraw,!0)&&this.redraw(a.animation);\nreturn b}showLoading(b){const a=this,c=a.options,d=c.loading,e=function(){g&&P(g,{left:a.plotLeft+\"px\",top:a.plotTop+\"px\",width:a.plotWidth+\"px\",height:a.plotHeight+\"px\"})};let g=a.loadingDiv,k=a.loadingSpan;g||(a.loadingDiv=g=n(\"div\",{className:\"highcharts-loading highcharts-loading-hidden\"},null,a.container));k||(a.loadingSpan=k=n(\"span\",{className:\"highcharts-loading-inner\"},null,g),f(a,\"redraw\",e));g.className=\"highcharts-loading\";t.setElementHTML(k,S(b,c.lang.loading,\"\"));a.styledMode||(P(g,\nV(d.style,{zIndex:10})),P(k,d.labelStyle),a.loadingShown||(P(g,{opacity:0,display:\"\"}),m(g,{opacity:d.style.opacity||.5},{duration:d.showDuration||0})));a.loadingShown=!0;e()}hideLoading(){const b=this.options,a=this.loadingDiv;a&&(a.className=\"highcharts-loading highcharts-loading-hidden\",this.styledMode||m(a,{opacity:0},{duration:b.loading.hideDuration||100,complete:function(){P(a,{display:\"none\"})}}));this.loadingShown=!1}update(b,a,c,d){const f=this,e={credits:\"addCredits\",title:\"setTitle\",subtitle:\"setSubtitle\",\ncaption:\"setCaption\"},g=b.isResponsiveOptions,k=[];let h,n;A(f,\"update\",{options:b});g||f.setResponsive(!1,!0);b=K(b,f.options);f.userOptions=N(f.userOptions,b);var m=b.chart;if(m){N(!0,f.options.chart,m);this.setZoomOptions();\"className\"in m&&f.setClassName(m.className);if(\"inverted\"in m||\"polar\"in m||\"type\"in m){f.propFromSeries();var q=!0}\"alignTicks\"in m&&(q=!0);\"events\"in m&&F(this,m);O(m,function(b,a){-1!==f.propsRequireUpdateSeries.indexOf(\"chart.\"+a)&&(h=!0);-1!==f.propsRequireDirtyBox.indexOf(a)&&\n(f.isDirtyBox=!0);-1!==f.propsRequireReflow.indexOf(a)&&(g?f.isDirtyBox=!0:n=!0)});!f.styledMode&&m.style&&f.renderer.setStyle(f.options.chart.style||{})}!f.styledMode&&b.colors&&(this.options.colors=b.colors);b.time&&(this.time===w&&(this.time=new l(b.time)),N(!0,f.options.time,b.time));O(b,function(a,c){if(f[c]&&\"function\"===typeof f[c].update)f[c].update(a,!1);else if(\"function\"===typeof f[e[c]])f[e[c]](a);else\"colors\"!==c&&-1===f.collectionsWithUpdate.indexOf(c)&&N(!0,f.options[c],b[c]);\"chart\"!==\nc&&-1!==f.propsRequireUpdateSeries.indexOf(c)&&(h=!0)});this.collectionsWithUpdate.forEach(function(a){b[a]&&(fa(b[a]).forEach(function(b,d){const e=D(b.id);let g;e&&(g=f.get(b.id));!g&&f[a]&&(g=f[a][S(b.index,d)])&&(e&&D(g.options.id)||g.options.isInternal)&&(g=void 0);g&&g.coll===a&&(g.update(b,!1),c&&(g.touched=!0));!g&&c&&f.collectionsWithInit[a]&&(f.collectionsWithInit[a][0].apply(f,[b].concat(f.collectionsWithInit[a][1]||[]).concat([!1])).touched=!0)}),c&&f[a].forEach(function(b){b.touched||\nb.options.isInternal?delete b.touched:k.push(b)}))});k.forEach(function(b){b.chart&&b.remove&&b.remove(!1)});q&&f.axes.forEach(function(b){b.update({},!1)});h&&f.getSeriesOrderByLinks().forEach(function(b){b.chart&&b.update({},!1)},this);q=m&&m.width;m=m&&(J(m.height)?ha(m.height,q||f.chartWidth):m.height);n||ba(q)&&q!==f.chartWidth||ba(m)&&m!==f.chartHeight?f.setSize(q,m,d):S(a,!0)&&f.redraw(d);A(f,\"afterUpdate\",{options:b,redraw:a,animation:d})}setSubtitle(b,a){this.applyDescription(\"subtitle\",\nb);this.layOutTitles(a)}setCaption(b,a){this.applyDescription(\"caption\",b);this.layOutTitles(a)}showResetZoom(){function b(){a.zoomOut()}const a=this,c=e.lang,d=a.zooming.resetButton,f=d.theme,g=\"chart\"===d.relativeTo||\"spacingBox\"===d.relativeTo?null:\"scrollablePlotBox\";A(this,\"beforeShowResetZoom\",null,function(){a.resetZoomButton=a.renderer.button(c.resetZoom,null,null,b,f).attr({align:d.position.align,title:c.resetZoomTitle}).addClass(\"highcharts-reset-zoom\").add().align(d.position,!1,g)});A(this,\n\"afterShowResetZoom\")}zoomOut(){A(this,\"selection\",{resetSelection:!0},this.zoom)}zoom(b){const a=this,c=a.pointer;let d=!1,f;!b||b.resetSelection?(a.axes.forEach(function(b){f=b.zoom()}),c.initiated=!1):b.xAxis.concat(b.yAxis).forEach(function(b){const e=b.axis;if(c[e.isXAxis?\"zoomX\":\"zoomY\"]&&D(c.mouseDownX)&&D(c.mouseDownY)&&a.isInsidePlot(c.mouseDownX-a.plotLeft,c.mouseDownY-a.plotTop,{axis:e})||!D(a.inverted?c.mouseDownX:c.mouseDownY))f=e.zoom(b.min,b.max),e.displayBtn&&(d=!0)});const e=a.resetZoomButton;\nd&&!e?a.showResetZoom():!d&&aa(e)&&(a.resetZoomButton=e.destroy());f&&a.redraw(S(a.options.chart.animation,b&&b.animation,100>a.pointCount))}pan(b,a){const c=this,d=c.hoverPoints;a=\"object\"===typeof a?a:{enabled:a,type:\"x\"};const f=c.options.chart;f&&f.panning&&(f.panning=a);const e=a.type;let g;A(this,\"pan\",{originalEvent:b},function(){d&&d.forEach(function(b){b.setState()});let a=c.xAxis;\"xy\"===e?a=a.concat(c.yAxis):\"y\"===e&&(a=c.yAxis);const f={};a.forEach(function(a){if(a.options.panningEnabled&&\n!a.options.isInternal){var d=a.horiz,k=b[d?\"chartX\":\"chartY\"];d=d?\"mouseDownX\":\"mouseDownY\";var h=c[d],n=a.minPointOffset||0,l=a.reversed&&!c.inverted||!a.reversed&&c.inverted?-1:1,m=a.getExtremes(),q=a.toValue(h-k,!0)+n*l,p=a.toValue(h+a.len-k,!0)-(n*l||a.isXAxis&&a.pointRangePadding||0),r=p<q;l=a.hasVerticalPanning();h=r?p:q;q=r?q:p;var t=a.panningState;!l||a.isXAxis||t&&!t.isDirty||a.series.forEach(function(b){var a=b.getProcessedData(!0);a=b.getExtremes(a.yData,!0);t||(t={startMin:Number.MAX_VALUE,\nstartMax:-Number.MAX_VALUE});ba(a.dataMin)&&ba(a.dataMax)&&(t.startMin=Math.min(S(b.options.threshold,Infinity),a.dataMin,t.startMin),t.startMax=Math.max(S(b.options.threshold,-Infinity),a.dataMax,t.startMax))});l=Math.min(S(t&&t.startMin,m.dataMin),n?m.min:a.toValue(a.toPixels(m.min)-a.minPixelPadding));p=Math.max(S(t&&t.startMax,m.dataMax),n?m.max:a.toValue(a.toPixels(m.max)+a.minPixelPadding));a.panningState=t;a.isOrdinal||(n=l-h,0<n&&(q+=n,h=l),n=q-p,0<n&&(q=p,h-=n),a.series.length&&h!==m.min&&\nq!==m.max&&h>=l&&q<=p&&(a.setExtremes(h,q,!1,!1,{trigger:\"pan\"}),!c.resetZoomButton&&h!==l&&q!==p&&e.match(\"y\")&&(c.showResetZoom(),a.displayBtn=!1),g=!0),f[d]=k)}});O(f,(b,a)=>{c[a]=b});g&&c.redraw(!1);P(c.container,{cursor:\"move\"})})}}V(ea.prototype,{callbacks:[],collectionsWithInit:{xAxis:[ea.prototype.addAxis,[!0]],yAxis:[ea.prototype.addAxis,[!1]],series:[ea.prototype.addSeries]},collectionsWithUpdate:[\"xAxis\",\"yAxis\",\"series\"],propsRequireDirtyBox:\"backgroundColor borderColor borderWidth borderRadius plotBackgroundColor plotBackgroundImage plotBorderColor plotBorderWidth plotShadow shadow\".split(\" \"),\npropsRequireReflow:\"margin marginTop marginRight marginBottom marginLeft spacing spacingTop spacingRight spacingBottom spacingLeft\".split(\" \"),propsRequireUpdateSeries:\"chart.inverted chart.polar chart.ignoreHiddenSeries chart.type colors plotOptions time tooltip\".split(\" \")});\"\";return ea});M(a,\"Extensions/ScrollablePlotArea.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Axis/Axis.js\"],a[\"Core/Chart/Chart.js\"],a[\"Core/Series/Series.js\"],a[\"Core/Renderer/RendererRegistry.js\"],a[\"Core/Utilities.js\"]],\nfunction(a,y,I,L,C,z){const {stop:x}=a,{addEvent:B,createElement:u,defined:v,merge:l,pick:p}=z;B(I,\"afterSetChartSize\",function(a){var m=this.options.chart.scrollablePlotArea,h=m&&m.minWidth;m=m&&m.minHeight;let g;if(!this.renderer.forExport){if(h){if(this.scrollablePixelsX=h=Math.max(0,h-this.chartWidth))this.scrollablePlotBox=this.renderer.scrollablePlotBox=l(this.plotBox),this.plotBox.width=this.plotWidth+=h,this.inverted?this.clipBox.height+=h:this.clipBox.width+=h,g={1:{name:\"right\",value:h}}}else m&&\n(this.scrollablePixelsY=h=Math.max(0,m-this.chartHeight),v(h)&&(this.scrollablePlotBox=this.renderer.scrollablePlotBox=l(this.plotBox),this.plotBox.height=this.plotHeight+=h,this.inverted?this.clipBox.width+=h:this.clipBox.height+=h,g={2:{name:\"bottom\",value:h}}));g&&!a.skipAxes&&this.axes.forEach(function(a){g[a.side]?a.getPlotLinePath=function(){let e=g[a.side].name,h=this[e],l;this[e]=h-g[a.side].value;l=y.prototype.getPlotLinePath.apply(this,arguments);this[e]=h;return l}:(a.setAxisSize(),a.setAxisTranslation())})}});\nB(I,\"render\",function(){this.scrollablePixelsX||this.scrollablePixelsY?(this.setUpScrolling&&this.setUpScrolling(),this.applyFixed()):this.fixedDiv&&this.applyFixed()});I.prototype.setUpScrolling=function(){const a={WebkitOverflowScrolling:\"touch\",overflowX:\"hidden\",overflowY:\"hidden\"};this.scrollablePixelsX&&(a.overflowX=\"auto\");this.scrollablePixelsY&&(a.overflowY=\"auto\");this.scrollingParent=u(\"div\",{className:\"highcharts-scrolling-parent\"},{position:\"relative\"},this.renderTo);this.scrollingContainer=\nu(\"div\",{className:\"highcharts-scrolling\"},a,this.scrollingParent);let l;B(this.scrollingContainer,\"scroll\",()=>{this.pointer&&(delete this.pointer.chartPosition,this.hoverPoint&&(l=this.hoverPoint),this.pointer.runPointActions(void 0,l,!0))});this.innerContainer=u(\"div\",{className:\"highcharts-inner-container\"},null,this.scrollingContainer);this.innerContainer.appendChild(this.container);this.setUpScrolling=null};I.prototype.moveFixedElements=function(){let a=this.container,l=this.fixedRenderer,h=\n\".highcharts-breadcrumbs-group .highcharts-contextbutton .highcharts-credits .highcharts-legend .highcharts-legend-checkbox .highcharts-navigator-series .highcharts-navigator-xaxis .highcharts-navigator-yaxis .highcharts-navigator .highcharts-reset-zoom .highcharts-drillup-button .highcharts-scrollbar .highcharts-subtitle .highcharts-title\".split(\" \"),g;this.scrollablePixelsX&&!this.inverted?g=\".highcharts-yaxis\":this.scrollablePixelsX&&this.inverted?g=\".highcharts-xaxis\":this.scrollablePixelsY&&\n!this.inverted?g=\".highcharts-xaxis\":this.scrollablePixelsY&&this.inverted&&(g=\".highcharts-yaxis\");g&&h.push(`${g}:not(.highcharts-radial-axis)`,`${g}-labels:not(.highcharts-radial-axis-labels)`);h.forEach(function(e){[].forEach.call(a.querySelectorAll(e),function(a){(a.namespaceURI===l.SVG_NS?l.box:l.box.parentNode).appendChild(a);a.style.pointerEvents=\"auto\"})})};I.prototype.applyFixed=function(){var a=!this.fixedDiv,l=this.options.chart,h=l.scrollablePlotArea,g=C.getRendererType();a?(this.fixedDiv=\nu(\"div\",{className:\"highcharts-fixed\"},{position:\"absolute\",overflow:\"hidden\",pointerEvents:\"none\",zIndex:(l.style&&l.style.zIndex||0)+2,top:0},null,!0),this.scrollingContainer&&this.scrollingContainer.parentNode.insertBefore(this.fixedDiv,this.scrollingContainer),this.renderTo.style.overflow=\"visible\",this.fixedRenderer=l=new g(this.fixedDiv,this.chartWidth,this.chartHeight,this.options.chart.style),this.scrollableMask=l.path().attr({fill:this.options.chart.backgroundColor||\"#fff\",\"fill-opacity\":p(h.opacity,\n.85),zIndex:-1}).addClass(\"highcharts-scrollable-mask\").add(),B(this,\"afterShowResetZoom\",this.moveFixedElements),B(this,\"afterApplyDrilldown\",this.moveFixedElements),B(this,\"afterLayOutTitles\",this.moveFixedElements)):this.fixedRenderer.setSize(this.chartWidth,this.chartHeight);if(this.scrollableDirty||a)this.scrollableDirty=!1,this.moveFixedElements();l=this.chartWidth+(this.scrollablePixelsX||0);g=this.chartHeight+(this.scrollablePixelsY||0);x(this.container);this.container.style.width=l+\"px\";\nthis.container.style.height=g+\"px\";this.renderer.boxWrapper.attr({width:l,height:g,viewBox:[0,0,l,g].join(\" \")});this.chartBackground.attr({width:l,height:g});this.scrollingContainer.style.height=this.chartHeight+\"px\";a&&(h.scrollPositionX&&(this.scrollingContainer.scrollLeft=this.scrollablePixelsX*h.scrollPositionX),h.scrollPositionY&&(this.scrollingContainer.scrollTop=this.scrollablePixelsY*h.scrollPositionY));g=this.axisOffset;a=this.plotTop-g[0]-1;h=this.plotLeft-g[3]-1;l=this.plotTop+this.plotHeight+\ng[2]+1;g=this.plotLeft+this.plotWidth+g[1]+1;let e=this.plotLeft+this.plotWidth-(this.scrollablePixelsX||0),w=this.plotTop+this.plotHeight-(this.scrollablePixelsY||0);a=this.scrollablePixelsX?[[\"M\",0,a],[\"L\",this.plotLeft-1,a],[\"L\",this.plotLeft-1,l],[\"L\",0,l],[\"Z\"],[\"M\",e,a],[\"L\",this.chartWidth,a],[\"L\",this.chartWidth,l],[\"L\",e,l],[\"Z\"]]:this.scrollablePixelsY?[[\"M\",h,0],[\"L\",h,this.plotTop-1],[\"L\",g,this.plotTop-1],[\"L\",g,0],[\"Z\"],[\"M\",h,w],[\"L\",h,this.chartHeight],[\"L\",g,this.chartHeight],[\"L\",\ng,w],[\"Z\"]]:[[\"M\",0,0]];\"adjustHeight\"!==this.redrawTrigger&&this.scrollableMask.attr({d:a})};B(y,\"afterInit\",function(){this.chart.scrollableDirty=!0});B(L,\"show\",function(){this.chart.scrollableDirty=!0});\"\"});M(a,\"Core/Axis/Stacking/StackItem.js\",[a[\"Core/Templating.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {format:x}=a,{series:C}=y,{destroyObjectProperties:z,fireEvent:H,isNumber:B,pick:u}=I;class v{constructor(a,p,t,m,h){const g=a.chart.inverted,e=a.reversed;\nthis.axis=a;a=this.isNegative=!!t!==!!e;this.options=p=p||{};this.x=m;this.cumulative=this.total=null;this.points={};this.hasValidPoints=!1;this.stack=h;this.rightCliff=this.leftCliff=0;this.alignOptions={align:p.align||(g?a?\"left\":\"right\":\"center\"),verticalAlign:p.verticalAlign||(g?\"middle\":a?\"bottom\":\"top\"),y:p.y,x:p.x};this.textAlign=p.textAlign||(g?a?\"right\":\"left\":\"center\")}destroy(){z(this,this.axis)}render(a){const l=this.axis.chart,t=this.options;var m=t.format;m=m?x(m,this,l):t.formatter.call(this);\nthis.label?this.label.attr({text:m,visibility:\"hidden\"}):(this.label=l.renderer.label(m,null,void 0,t.shape,void 0,void 0,t.useHTML,!1,\"stack-labels\"),m={r:t.borderRadius||0,text:m,padding:u(t.padding,5),visibility:\"hidden\"},l.styledMode||(m.fill=t.backgroundColor,m.stroke=t.borderColor,m[\"stroke-width\"]=t.borderWidth,this.label.css(t.style||{})),this.label.attr(m),this.label.added||this.label.add(a));this.label.labelrank=l.plotSizeY;H(this,\"afterRender\")}setOffset(a,p,t,m,h,g){const {alignOptions:e,\naxis:l,label:v,options:x,textAlign:d}=this,k=l.chart;t=this.getStackBox({xOffset:a,width:p,boxBottom:t,boxTop:m,defaultX:h,xAxis:g});var {verticalAlign:r}=e;if(v&&t){m=v.getBBox();h=v.padding;g=\"justify\"===u(x.overflow,\"justify\");e.x=x.x||0;e.y=x.y||0;const {x:a,y:p}=this.adjustStackPosition({labelBox:m,verticalAlign:r,textAlign:d});t.x-=a;t.y-=p;v.align(e,!1,t);(r=k.isInsidePlot(v.alignAttr.x+e.x+a,v.alignAttr.y+e.y+p))||(g=!1);g&&C.prototype.justifyDataLabel.call(l,v,e,v.alignAttr,m,t);v.attr({x:v.alignAttr.x,\ny:v.alignAttr.y,rotation:x.rotation,rotationOriginX:m.width/2,rotationOriginY:m.height/2});u(!g&&x.crop,!0)&&(r=B(v.x)&&B(v.y)&&k.isInsidePlot(v.x-h+v.width,v.y)&&k.isInsidePlot(v.x+h,v.y));v[r?\"show\":\"hide\"]()}H(this,\"afterSetOffset\",{xOffset:a,width:p})}adjustStackPosition({labelBox:a,verticalAlign:p,textAlign:t}){const l={bottom:0,middle:1,top:2,right:1,center:0,left:-1};return{x:a.width/2+a.width/2*l[t],y:a.height/2*l[p]}}getStackBox(a){var l=this.axis;const t=l.chart,{boxTop:m,defaultX:h,xOffset:g,\nwidth:e,boxBottom:w}=a;var v=l.stacking.usePercentage?100:u(m,this.total,0);v=l.toPixels(v);a=a.xAxis||t.xAxis[0];const x=u(h,a.translate(this.x))+g;l=l.toPixels(w||B(l.min)&&l.logarithmic&&l.logarithmic.lin2log(l.min)||0);l=Math.abs(v-l);const d=this.isNegative;return t.inverted?{x:(d?v:v-l)-t.plotLeft,y:a.height-x-e,width:l,height:e}:{x:x+a.transB-t.plotLeft,y:(d?v-l:v)-t.plotTop,width:e,height:l}}}\"\";return v});M(a,\"Core/Axis/Stacking/StackingAxis.js\",[a[\"Core/Animation/AnimationUtilities.js\"],\na[\"Core/Axis/Axis.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Axis/Stacking/StackItem.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C){function x(){const b=this,a=b.inverted;b.yAxis.forEach(b=>{b.stacking&&b.stacking.stacks&&b.hasVisibleSeries&&(b.stacking.oldStacks=b.stacking.stacks)});b.series.forEach(c=>{const d=c.xAxis&&c.xAxis.options||{};!c.options.stacking||!0!==c.visible&&!1!==b.options.chart.ignoreHiddenSeries||(c.stackKey=[c.type,q(c.options.stack,\"\"),a?d.top:d.left,a?d.height:d.width].join())})}\nfunction H(){const b=this.stacking;if(b){var a=b.stacks;r(a,function(b,c){E(b);a[c]=null});b&&b.stackTotalGroup&&b.stackTotalGroup.destroy()}}function B(){\"yAxis\"!==this.coll||this.stacking||(this.stacking=new G(this))}function u(b,a,d,e){!w(b)||b.x!==a||e&&b.stackKey!==e?b={x:a,index:0,key:e,stackKey:e}:b.index++;b.key=[d,a,b.index].join();return b}function v(){const b=this,a=b.stackKey,d=b.yAxis.stacking.stacks,e=b.processedXData,g=b[b.options.stacking+\"Stacker\"];let k;g&&[a,\"-\"+a].forEach(a=>{let c=\ne.length;let f;for(;c--;){var h=e[c];k=b.getStackIndicator(k,h,b.index,a);(f=(h=d[a]&&d[a][h])&&h.points[k.key])&&g.call(b,f,h,c)}})}function l(b,a,d){a=a.total?100/a.total:0;b[0]=e(b[0]*a);b[1]=e(b[1]*a);this.stackedYData[d]=b[1]}function p(){const b=this.yAxis.stacking;this.options.centerInCategory&&(this.is(\"column\")||this.is(\"columnrange\"))&&!this.options.stacking&&1<this.chart.series.length?h.setStackedPoints.call(this,\"group\"):b&&r(b.stacks,(a,d)=>{\"group\"===d.slice(-5)&&(r(a,b=>b.destroy()),\ndelete b.stacks[d])})}function t(b){var a=this.chart;const f=b||this.options.stacking;if(f&&(!0===this.visible||!1===a.options.chart.ignoreHiddenSeries)){var g=this.processedXData,k=this.processedYData,h=[],l=k.length,m=this.options,p=m.threshold,r=q(m.startFromThreshold&&p,0);m=m.stack;b=b?`${this.type},${f}`:this.stackKey;var t=\"-\"+b,v=this.negStacks;a=\"group\"===f?a.yAxis[0]:this.yAxis;var u=a.stacking.stacks,x=a.stacking.oldStacks,E,G;a.stacking.stacksTouched+=1;for(G=0;G<l;G++){var F=g[G];var B=\nk[G];var y=this.getStackIndicator(y,F,this.index);var C=y.key;var z=(E=v&&B<(r?0:p))?t:b;u[z]||(u[z]={});u[z][F]||(x[z]&&x[z][F]?(u[z][F]=x[z][F],u[z][F].total=null):u[z][F]=new L(a,a.options.stackLabels,!!E,F,m));z=u[z][F];null!==B?(z.points[C]=z.points[this.index]=[q(z.cumulative,r)],w(z.cumulative)||(z.base=C),z.touched=a.stacking.stacksTouched,0<y.index&&!1===this.singleStacks&&(z.points[C][0]=z.points[this.index+\",\"+F+\",0\"][0])):z.points[C]=z.points[this.index]=null;\"percent\"===f?(E=E?b:t,v&&\nu[E]&&u[E][F]?(E=u[E][F],z.total=E.total=Math.max(E.total,z.total)+Math.abs(B)||0):z.total=e(z.total+(Math.abs(B)||0))):\"group\"===f?(d(B)&&(B=B[0]),null!==B&&(z.total=(z.total||0)+1)):z.total=e(z.total+(B||0));z.cumulative=\"group\"===f?(z.total||1)-1:e(q(z.cumulative,r)+(B||0));null!==B&&(z.points[C].push(z.cumulative),h[G]=z.cumulative,z.hasValidPoints=!0)}\"percent\"===f&&(a.stacking.usePercentage=!0);\"group\"!==f&&(this.stackedYData=h);a.stacking.oldStacks={}}}const {getDeferredAnimation:m}=a,{series:{prototype:h}}=\nI,{addEvent:g,correctFloat:e,defined:w,destroyObjectProperties:E,fireEvent:F,isArray:d,isNumber:k,objectEach:r,pick:q}=C;class G{constructor(b){this.oldStacks={};this.stacks={};this.stacksTouched=0;this.axis=b}buildStacks(){const b=this.axis,a=b.series,d=b.options.reversedStacks,e=a.length;let g,k;this.usePercentage=!1;for(k=e;k--;)g=a[d?k:e-k-1],g.setStackedPoints(),g.setGroupedPoints();for(k=0;k<e;k++)a[k].modifyStacks();F(b,\"afterBuildStacks\")}cleanStacks(){let b;this.oldStacks&&(b=this.stacks=\nthis.oldStacks);r(b,function(b){r(b,function(b){b.cumulative=b.total})})}resetStacks(){r(this.stacks,b=>{r(b,(a,d)=>{k(a.touched)&&a.touched<this.stacksTouched?(a.destroy(),delete b[d]):(a.total=null,a.cumulative=null)})})}renderStackTotals(){var b=this.axis;const a=b.chart,d=a.renderer,e=this.stacks;b=m(a,b.options.stackLabels&&b.options.stackLabels.animation||!1);const g=this.stackTotalGroup=this.stackTotalGroup||d.g(\"stack-labels\").attr({zIndex:6,opacity:0}).add();g.translate(a.plotLeft,a.plotTop);\nr(e,function(b){r(b,function(b){b.render(g)})});g.animate({opacity:1},b)}}var b;(function(b){const a=[];b.compose=function(b,c,d){C.pushUnique(a,b)&&(g(b,\"init\",B),g(b,\"destroy\",H));C.pushUnique(a,c)&&(c.prototype.getStacks=x);C.pushUnique(a,d)&&(b=d.prototype,b.getStackIndicator=u,b.modifyStacks=v,b.percentStacker=l,b.setGroupedPoints=p,b.setStackedPoints=t)}})(b||(b={}));return b});M(a,\"Series/Line/LineSeries.js\",[a[\"Core/Series/Series.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],\nfunction(a,y,I){const {defined:x,merge:C}=I;class z extends a{constructor(){super(...arguments);this.points=this.options=this.data=void 0}drawGraph(){const a=this,x=this.options,u=(this.gappedPath||this.getGraphPath).call(this),v=this.chart.styledMode;let l=[[\"graph\",\"highcharts-graph\"]];v||l[0].push(x.lineColor||this.color||\"#cccccc\",x.dashStyle);l=a.getZonesGraphs(l);l.forEach(function(l,t){var m=l[0];let h=a[m];const g=h?\"animate\":\"attr\";h?(h.endX=a.preventGraphAnimation?null:u.xMap,h.animate({d:u})):\nu.length&&(a[m]=h=a.chart.renderer.path(u).addClass(l[1]).attr({zIndex:1}).add(a.group));h&&!v&&(m={stroke:l[2],\"stroke-width\":x.lineWidth||0,fill:a.fillGraph&&a.color||\"none\"},l[3]?m.dashstyle=l[3]:\"square\"!==x.linecap&&(m[\"stroke-linecap\"]=m[\"stroke-linejoin\"]=\"round\"),h[g](m).shadow(2>t&&x.shadow));h&&(h.startX=u.xMap,h.isArea=u.isArea)})}getGraphPath(a,B,u){const v=this,l=v.options,p=[],t=[];let m,h=l.step;a=a||v.points;const g=a.reversed;g&&a.reverse();(h={right:1,center:2}[h]||h&&3)&&g&&(h=\n4-h);a=this.getValidPoints(a,!1,!(l.connectNulls&&!B&&!u));a.forEach(function(e,g){const w=e.plotX,F=e.plotY,d=a[g-1],k=e.isNull||\"number\"!==typeof F;(e.leftCliff||d&&d.rightCliff)&&!u&&(m=!0);k&&!x(B)&&0<g?m=!l.connectNulls:k&&!B?m=!0:(0===g||m?g=[[\"M\",e.plotX,e.plotY]]:v.getPointSpline?g=[v.getPointSpline(a,e,g)]:h?(g=1===h?[[\"L\",d.plotX,F]]:2===h?[[\"L\",(d.plotX+w)/2,d.plotY],[\"L\",(d.plotX+w)/2,F]]:[[\"L\",w,d.plotY]],g.push([\"L\",w,F])):g=[[\"L\",w,F]],t.push(e.x),h&&(t.push(e.x),2===h&&t.push(e.x)),\np.push.apply(p,g),m=!1)});p.xMap=t;return v.graphPath=p}getZonesGraphs(a){this.zones.forEach(function(x,u){u=[\"zone-graph-\"+u,\"highcharts-graph highcharts-zone-graph-\"+u+\" \"+(x.className||\"\")];this.chart.styledMode||u.push(x.color||this.color,x.dashStyle||this.options.dashStyle);a.push(u)},this);return a}}z.defaultOptions=C(a.defaultOptions,{legendSymbol:\"lineMarker\"});y.registerSeriesType(\"line\",z);\"\";return z});M(a,\"Series/Area/AreaSeries.js\",[a[\"Core/Color/Color.js\"],a[\"Core/Series/SeriesRegistry.js\"],\na[\"Core/Utilities.js\"]],function(a,y,I){const {seriesTypes:{line:x}}=y,{extend:C,merge:z,objectEach:H,pick:B}=I;class u extends x{constructor(){super(...arguments);this.points=this.options=this.data=void 0}drawGraph(){this.areaPath=[];super.drawGraph.apply(this);const a=this,l=this.areaPath,p=this.options,t=[[\"area\",\"highcharts-area\",this.color,p.fillColor]];this.zones.forEach(function(l,h){t.push([\"zone-area-\"+h,\"highcharts-area highcharts-zone-area-\"+h+\" \"+l.className,l.color||a.color,l.fillColor||\np.fillColor])});t.forEach(function(m){const h=m[0],g={};let e=a[h];const t=e?\"animate\":\"attr\";e?(e.endX=a.preventGraphAnimation?null:l.xMap,e.animate({d:l})):(g.zIndex=0,e=a[h]=a.chart.renderer.path(l).addClass(m[1]).add(a.group),e.isArea=!0);a.chart.styledMode||(m[3]?g.fill=m[3]:(g.fill=m[2],g[\"fill-opacity\"]=B(p.fillOpacity,.75)));e[t](g);e.startX=l.xMap;e.shiftUnit=p.step?2:1})}getGraphPath(a){var l=x.prototype.getGraphPath,p=this.options;const t=p.stacking,m=this.yAxis,h=[],g=[],e=this.index,\nw=m.stacking.stacks[this.stackKey],v=p.threshold,u=Math.round(m.getThreshold(p.threshold));p=B(p.connectNulls,\"percent\"===t);var d=function(d,b,f){var c=a[d];d=t&&w[c.x].points[e];const n=c[f+\"Null\"]||0;f=c[f+\"Cliff\"]||0;let l,q;c=!0;f||n?(l=(n?d[0]:d[1])+f,q=d[0]+f,c=!!n):!t&&a[b]&&a[b].isNull&&(l=q=v);\"undefined\"!==typeof l&&(g.push({plotX:k,plotY:null===l?u:m.getThreshold(l),isNull:c,isCliff:!0}),h.push({plotX:k,plotY:null===q?u:m.getThreshold(q),doCurve:!1}))};let k;a=a||this.points;t&&(a=this.getStackPoints(a));\nfor(let e=0,b=a.length;e<b;++e){t||(a[e].leftCliff=a[e].rightCliff=a[e].leftNull=a[e].rightNull=void 0);var r=a[e].isNull;k=B(a[e].rectPlotX,a[e].plotX);var q=t?B(a[e].yBottom,u):u;if(!r||p)p||d(e,e-1,\"left\"),r&&!t&&p||(g.push(a[e]),h.push({x:e,plotX:k,plotY:q})),p||d(e,e+1,\"right\")}d=l.call(this,g,!0,!0);h.reversed=!0;r=l.call(this,h,!0,!0);(q=r[0])&&\"M\"===q[0]&&(r[0]=[\"L\",q[1],q[2]]);r=d.concat(r);r.length&&r.push([\"Z\"]);l=l.call(this,g,!1,p);r.xMap=d.xMap;this.areaPath=r;return l}getStackPoints(a){const l=\nthis,p=[],t=[],m=this.xAxis,h=this.yAxis,g=h.stacking.stacks[this.stackKey],e={},w=h.series,v=w.length,u=h.options.reversedStacks?1:-1,d=w.indexOf(l);a=a||this.points;if(this.options.stacking){for(let d=0;d<a.length;d++)a[d].leftNull=a[d].rightNull=void 0,e[a[d].x]=a[d];H(g,function(a,d){null!==a.total&&t.push(d)});t.sort(function(a,d){return a-d});const k=w.map(a=>a.visible);t.forEach(function(a,q){let r=0,b,f;if(e[a]&&!e[a].isNull)p.push(e[a]),[-1,1].forEach(function(c){const h=1===c?\"rightNull\":\n\"leftNull\",m=g[t[q+c]];let p=0;if(m){let c=d;for(;0<=c&&c<v;){const d=w[c].index;b=m.points[d];b||(d===l.index?e[a][h]=!0:k[c]&&(f=g[a].points[d])&&(p-=f[1]-f[0]));c+=u}}e[a][1===c?\"rightCliff\":\"leftCliff\"]=p});else{let c=d;for(;0<=c&&c<v;){if(b=g[a].points[w[c].index]){r=b[1];break}c+=u}r=B(r,0);r=h.translate(r,0,1,0,1);p.push({isNull:!0,plotX:m.translate(a,0,0,0,1),x:a,plotY:r,yBottom:r})}})}return p}}u.defaultOptions=z(x.defaultOptions,{threshold:0,legendSymbol:\"rectangle\"});C(u.prototype,{singleStacks:!1});\ny.registerSeriesType(\"area\",u);\"\";return u});M(a,\"Series/Spline/SplineSeries.js\",[a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y){const {line:x}=a.seriesTypes,{merge:L,pick:C}=y;class z extends x{constructor(){super(...arguments);this.points=this.options=this.data=void 0}getPointSpline(a,x,u){const v=x.plotX||0,l=x.plotY||0,p=a[u-1];u=a[u+1];let t,m;let h;if(p&&!p.isNull&&!1!==p.doCurve&&!x.isCliff&&u&&!u.isNull&&!1!==u.doCurve&&!x.isCliff){a=p.plotY||0;var g=u.plotX||0;u=\nu.plotY||0;let e=0;t=(1.5*v+(p.plotX||0))/2.5;m=(1.5*l+a)/2.5;g=(1.5*v+g)/2.5;h=(1.5*l+u)/2.5;g!==t&&(e=(h-m)*(g-v)/(g-t)+l-h);m+=e;h+=e;m>a&&m>l?(m=Math.max(a,l),h=2*l-m):m<a&&m<l&&(m=Math.min(a,l),h=2*l-m);h>u&&h>l?(h=Math.max(u,l),m=2*l-h):h<u&&h<l&&(h=Math.min(u,l),m=2*l-h);x.rightContX=g;x.rightContY=h}x=[\"C\",C(p.rightContX,p.plotX,0),C(p.rightContY,p.plotY,0),C(t,v,0),C(m,l,0),v,l];p.rightContX=p.rightContY=void 0;return x}}z.defaultOptions=L(x.defaultOptions);a.registerSeriesType(\"spline\",\nz);\"\";return z});M(a,\"Series/AreaSpline/AreaSplineSeries.js\",[a[\"Series/Spline/SplineSeries.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {area:x,area:{prototype:C}}=y.seriesTypes,{extend:z,merge:H}=I;class B extends a{constructor(){super(...arguments);this.options=this.points=this.data=void 0}}B.defaultOptions=H(a.defaultOptions,x.defaultOptions);z(B.prototype,{getGraphPath:C.getGraphPath,getStackPoints:C.getStackPoints,drawGraph:C.drawGraph});y.registerSeriesType(\"areaspline\",\nB);\"\";return B});M(a,\"Series/Column/ColumnSeriesDefaults.js\",[],function(){\"\";return{borderRadius:3,centerInCategory:!1,groupPadding:.2,marker:null,pointPadding:.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{halo:!1,brightness:.1},select:{color:\"#cccccc\",borderColor:\"#000000\"}},dataLabels:{align:void 0,verticalAlign:void 0,y:void 0},startFromThreshold:!0,stickyTracking:!1,tooltip:{distance:6},threshold:0,borderColor:\"#ffffff\"}});M(a,\"Series/Column/ColumnSeries.js\",[a[\"Core/Animation/AnimationUtilities.js\"],\na[\"Core/Color/Color.js\"],a[\"Series/Column/ColumnSeriesDefaults.js\"],a[\"Core/Globals.js\"],a[\"Core/Series/Series.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C,z,H){const {animObject:x}=a,{parse:u}=y,{hasTouch:v,noop:l}=L,{clamp:p,defined:t,extend:m,fireEvent:h,isArray:g,isNumber:e,merge:w,pick:E,objectEach:F}=H;class d extends C{constructor(){super(...arguments);this.points=this.options=this.group=this.data=this.borderWidth=void 0}animate(a){const d=this,e=this.yAxis,\ng=e.pos,b=d.options,f=this.chart.inverted,c={},k=f?\"translateX\":\"translateY\";let h;a?(c.scaleY=.001,a=p(e.toPixels(b.threshold),g,g+e.len),f?c.translateX=a-e.len:c.translateY=a,d.clipBox&&d.setClip(),d.group.attr(c)):(h=Number(d.group.attr(k)),d.group.animate({scaleY:1},m(x(d.options.animation),{step:function(b,a){d.group&&(c[k]=h+a.pos*(g-h),d.group.attr(c))}})))}init(a,d){super.init.apply(this,arguments);const e=this;a=e.chart;a.hasRendered&&a.series.forEach(function(a){a.type===e.type&&(a.isDirty=\n!0)})}getColumnMetrics(){const a=this;var d=a.options;const e=a.xAxis,g=a.yAxis;var b=e.options.reversedStacks;b=e.reversed&&!b||!e.reversed&&b;const f={};let c,h=0;!1===d.grouping?h=1:a.chart.series.forEach(function(b){const d=b.yAxis,e=b.options;let k;b.type!==a.type||!b.visible&&a.chart.options.chart.ignoreHiddenSeries||g.len!==d.len||g.pos!==d.pos||(e.stacking&&\"group\"!==e.stacking?(c=b.stackKey,\"undefined\"===typeof f[c]&&(f[c]=h++),k=f[c]):!1!==e.grouping&&(k=h++),b.columnIndex=k)});const l=\nMath.min(Math.abs(e.transA)*(e.ordinal&&e.ordinal.slope||d.pointRange||e.closestPointRange||e.tickInterval||1),e.len),m=l*d.groupPadding,p=(l-2*m)/(h||1);d=Math.min(d.maxPointWidth||e.len,E(d.pointWidth,p*(1-2*d.pointPadding)));a.columnMetrics={width:d,offset:(p-d)/2+(m+((a.columnIndex||0)+(b?1:0))*p-l/2)*(b?-1:1),paddedWidth:p,columnCount:h};return a.columnMetrics}crispCol(a,d,e,g){var b=this.borderWidth,f=-(b%2?.5:0);b=b%2?.5:1;this.options.crisp&&(e=Math.round(a+e)+f,a=Math.round(a)+f,e-=a);g=\nMath.round(d+g)+b;f=.5>=Math.abs(d)&&.5<g;d=Math.round(d)+b;g-=d;f&&g&&(--d,g+=1);return{x:a,y:d,width:e,height:g}}adjustForMissingColumns(a,d,e,h){const b=this.options.stacking;if(!e.isNull&&1<h.columnCount){const f=this.yAxis.options.reversedStacks;let c=0,k=f?0:-h.columnCount;F(this.yAxis.stacking&&this.yAxis.stacking.stacks,a=>{if(\"number\"===typeof e.x){const d=a[e.x.toString()];d&&(a=d.points[this.index],b?(a&&(c=k),d.hasValidPoints&&(f?k++:k--)):g(a)&&(a=Object.keys(d.points).filter(b=>!b.match(\",\")&&\nd.points[b]&&1<d.points[b].length).map(parseFloat).sort((b,a)=>a-b),c=a.indexOf(this.index),k=a.length))}});a=(e.plotX||0)+((k-1)*h.paddedWidth+d)/2-d-c*h.paddedWidth}return a}translate(){const a=this,d=a.chart,g=a.options;var l=a.dense=2>a.closestPointRange*a.xAxis.transA;l=a.borderWidth=E(g.borderWidth,l?0:1);const b=a.xAxis,f=a.yAxis,c=g.threshold,n=E(g.minPointLength,5),m=a.getColumnMetrics(),w=m.width,v=a.pointXOffset=m.offset,u=a.dataMin,x=a.dataMax;let F=a.barW=Math.max(w,1+2*l),y=a.translatedThreshold=\nf.getThreshold(c);d.inverted&&(y-=.5);g.pointPadding&&(F=Math.ceil(F));C.prototype.translate.apply(a);a.points.forEach(function(k){const h=E(k.yBottom,y);var l=999+Math.abs(h),q=k.plotX||0;l=p(k.plotY,-l,f.len+l);let r=Math.min(l,h),D=Math.max(l,h)-r,z=w,B=q+v,G=F;n&&Math.abs(D)<n&&(D=n,q=!f.reversed&&!k.negative||f.reversed&&k.negative,e(c)&&e(x)&&k.y===c&&x<=c&&(f.min||0)<c&&(u!==x||(f.max||0)<=c)&&(q=!q,k.negative=!k.negative),r=Math.abs(r-y)>n?h-n:y-(q?n:0));t(k.options.pointWidth)&&(z=G=Math.ceil(k.options.pointWidth),\nB-=Math.round((z-w)/2));g.centerInCategory&&(B=a.adjustForMissingColumns(B,z,k,m));k.barX=B;k.pointWidth=z;k.tooltipPos=d.inverted?[p(f.len+f.pos-d.plotLeft-l,f.pos-d.plotLeft,f.len+f.pos-d.plotLeft),b.len+b.pos-d.plotTop-B-G/2,D]:[b.left-d.plotLeft+B+G/2,p(l+f.pos-d.plotTop,f.pos-d.plotTop,f.len+f.pos-d.plotTop),D];k.shapeType=a.pointClass.prototype.shapeType||\"roundedRect\";k.shapeArgs=a.crispCol(B,k.isNull?y:r,G,k.isNull?0:D)});h(this,\"afterColumnTranslate\")}drawGraph(){this.group[this.dense?\"addClass\":\n\"removeClass\"](\"highcharts-dense-data\")}pointAttribs(a,d){const e=this.options;var g=this.pointAttrToOptions||{},b=g.stroke||\"borderColor\";const f=g[\"stroke-width\"]||\"borderWidth\";let c,k=a&&a.color||this.color,h=a&&a[b]||e[b]||k;g=a&&a.options.dashStyle||e.dashStyle;let l=a&&a[f]||e[f]||this[f]||0,m=E(a&&a.opacity,e.opacity,1);a&&this.zones.length&&(c=a.getZone(),k=a.options.color||c&&(c.color||a.nonZonedColor)||this.color,c&&(h=c.borderColor||h,g=c.dashStyle||g,l=c.borderWidth||l));d&&a&&(a=w(e.states[d],\na.options.states&&a.options.states[d]||{}),d=a.brightness,k=a.color||\"undefined\"!==typeof d&&u(k).brighten(a.brightness).get()||k,h=a[b]||h,l=a[f]||l,g=a.dashStyle||g,m=E(a.opacity,m));b={fill:k,stroke:h,\"stroke-width\":l,opacity:m};g&&(b.dashstyle=g);return b}drawPoints(a=this.points){const d=this,g=this.chart,k=d.options,b=g.renderer,f=k.animationLimit||250;let c;a.forEach(function(a){let h=a.graphic,l=!!h,n=h&&g.pointCount<f?\"animate\":\"attr\";if(e(a.plotY)&&null!==a.y){c=a.shapeArgs;h&&a.hasNewShapeType()&&\n(h=h.destroy());d.enabledDataSorting&&(a.startXPos=d.xAxis.reversed?-(c?c.width||0:0):d.xAxis.width);h||(a.graphic=h=b[a.shapeType](c).add(a.group||d.group))&&d.enabledDataSorting&&g.hasRendered&&g.pointCount<f&&(h.attr({x:a.startXPos}),l=!0,n=\"animate\");if(h&&l)h[n](w(c));g.styledMode||h[n](d.pointAttribs(a,a.selected&&\"select\")).shadow(!1!==a.allowShadow&&k.shadow);h&&(h.addClass(a.getClassName(),!0),h.attr({visibility:a.visible?\"inherit\":\"hidden\"}))}else h&&(a.graphic=h.destroy())})}drawTracker(a=\nthis.points){const d=this,e=d.chart,k=e.pointer,b=function(a){const b=k.getPointFromEvent(a);\"undefined\"!==typeof b&&d.options.enableMouseTracking&&(k.isDirectTouch=!0,b.onMouseOver(a))};let f;a.forEach(function(a){f=g(a.dataLabels)?a.dataLabels:a.dataLabel?[a.dataLabel]:[];a.graphic&&(a.graphic.element.point=a);f.forEach(function(b){b.div?b.div.point=a:b.element.point=a})});d._hasTracking||(d.trackerGroups.forEach(function(a){if(d[a]){d[a].addClass(\"highcharts-tracker\").on(\"mouseover\",b).on(\"mouseout\",\nfunction(a){k.onTrackerMouseOut(a)});if(v)d[a].on(\"touchstart\",b);!e.styledMode&&d.options.cursor&&d[a].css({cursor:d.options.cursor})}}),d._hasTracking=!0);h(this,\"afterDrawTracker\")}remove(){const a=this,d=a.chart;d.hasRendered&&d.series.forEach(function(d){d.type===a.type&&(d.isDirty=!0)});C.prototype.remove.apply(a,arguments)}}d.defaultOptions=w(C.defaultOptions,I);m(d.prototype,{cropShoulder:0,directTouch:!0,getSymbol:l,negStacks:!0,trackerGroups:[\"group\",\"dataLabelsGroup\"]});z.registerSeriesType(\"column\",\nd);\"\";return d});M(a,\"Core/Series/DataLabel.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Templating.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {getDeferredAnimation:x}=a,{format:C}=y,{defined:z,extend:H,fireEvent:B,isArray:u,isString:v,merge:l,objectEach:p,pick:t,splat:m}=I;var h;(function(a){function e(a,d,c,e,g){const b=this.chart;var f=this.isCartesian&&b.inverted;const k=this.enabledDataSorting;var h=a.plotX,l=a.plotY;const n=c.rotation;var m=c.align;l=z(h)&&z(l)&&b.isInsidePlot(h,\nMath.round(l),{inverted:f,paneCoordinates:!0,series:this});let p=\"justify\"===t(c.overflow,k?\"none\":\"justify\");f=this.visible&&!1!==a.visible&&z(h)&&(a.series.forceDL||k&&!p||l||t(c.inside,!!this.options.stacking)&&e&&b.isInsidePlot(h,f?e.x+1:e.y+e.height-1,{inverted:f,paneCoordinates:!0,series:this}));h=a.pos();if(f&&h){n&&d.attr({align:m});m=d.getBBox(!0);var q=[0,0];var r=b.renderer.fontMetrics(d).b;e=H({x:h[0],y:Math.round(h[1]),width:0,height:0},e);H(c,{width:m.width,height:m.height});n?(p=!1,\nq=b.renderer.rotCorr(r,n),r={x:e.x+(c.x||0)+e.width/2+q.x,y:e.y+(c.y||0)+{top:0,middle:.5,bottom:1}[c.verticalAlign]*e.height},q=[m.x-Number(d.attr(\"x\")),m.y-Number(d.attr(\"y\"))],k&&this.xAxis&&!p&&this.setDataLabelStartPos(a,d,g,l,r),d[g?\"attr\":\"animate\"](r)):(k&&this.xAxis&&!p&&this.setDataLabelStartPos(a,d,g,l,e),d.align(c,void 0,e),r=d.alignAttr);if(p&&0<=e.height)this.justifyDataLabel(d,c,r,m,e,g);else if(t(c.crop,!0)){let {x:a,y:c}=r;a+=q[0];c+=q[1];f=b.isInsidePlot(a,c,{paneCoordinates:!0,\nseries:this})&&b.isInsidePlot(a+m.width,c+m.height,{paneCoordinates:!0,series:this})}if(c.shape&&!n)d[g?\"attr\":\"animate\"]({anchorX:h[0],anchorY:h[1]})}g&&k&&(d.placed=!1);f||k&&!p?d.show():(d.hide(),d.placed=!1)}function g(a,d){var b=d.filter;return b?(d=b.operator,a=a[b.property],b=b.value,\">\"===d&&a>b||\"<\"===d&&a<b||\">=\"===d&&a>=b||\"<=\"===d&&a<=b||\"==\"===d&&a==b||\"===\"===d&&a===b?!0:!1):!0}function h(){return this.plotGroup(\"dataLabelsGroup\",\"data-labels\",this.hasRendered?\"inherit\":\"hidden\",this.options.dataLabels.zIndex||\n6)}function F(a){const b=this.hasRendered||0,c=this.initDataLabelsGroup().attr({opacity:+b});!b&&c&&(this.visible&&c.show(),this.options.animation?c.animate({opacity:1},a):c.attr({opacity:1}));return c}function d(a=this.points){var b,c;const d=this,e=d.chart,k=d.options,h=e.renderer,{backgroundColor:l,plotBackgroundColor:q}=e.options.chart,w=e.options.plotOptions,E=h.getContrast(v(q)&&q||v(l)&&l||\"#000000\");let F=k.dataLabels,A,y;var G=m(F)[0];const H=G.animation;G=G.defer?x(e,H,d):{defer:0,duration:0};\nF=r(r(null===(b=null===w||void 0===w?void 0:w.series)||void 0===b?void 0:b.dataLabels,null===(c=null===w||void 0===w?void 0:w[d.type])||void 0===c?void 0:c.dataLabels),F);B(this,\"drawDataLabels\");if(u(F)||F.enabled||d._hasPointLabels)y=this.initDataLabels(G),a.forEach(a=>{var b;const c=a.dataLabels||[];A=m(r(F,a.dlOptions||(null===(b=a.options)||void 0===b?void 0:b.dataLabels)));A.forEach((b,f)=>{var l,m=b.enabled&&(!a.isNull||a.dataLabelOnNull)&&g(a,b);const n=a.connectors?a.connectors[f]:a.connector,\nq=b.style||{};let r={},w=c[f],u=!w;const x=t(b.distance,a.labelDistance);if(m){var A=t(b[a.formatPrefix+\"Format\"],b.format);var F=a.getLabelConfig();F=z(A)?C(A,F,e):(b[a.formatPrefix+\"Formatter\"]||b.formatter).call(F,b);A=b.rotation;e.styledMode||(q.color=t(b.color,q.color,v(d.color)?d.color:void 0,\"#000000\"),\"contrast\"===q.color?(a.contrastColor=h.getContrast(a.color||d.color),q.color=!z(x)&&b.inside||0>(x||0)||k.stacking?a.contrastColor:E):delete a.contrastColor,k.cursor&&(q.cursor=k.cursor));r=\n{r:b.borderRadius||0,rotation:A,padding:b.padding,zIndex:1};if(!e.styledMode){const {backgroundColor:c,borderColor:d}=b;r.fill=\"auto\"===c?a.color:c;r.stroke=\"auto\"===d?a.color:d;r[\"stroke-width\"]=b.borderWidth}p(r,(a,b)=>{\"undefined\"===typeof a&&delete r[b]})}!w||m&&z(F)&&!!w.div===!!b.useHTML&&(w.rotation&&b.rotation||w.rotation===b.rotation)||(w=void 0,u=!0,n&&a.connector&&(a.connector=a.connector.destroy(),a.connectors&&(1===a.connectors.length?delete a.connectors:delete a.connectors[f])));m&&\nz(F)&&(w?r.text=F:(w=A?h.text(F,0,0,b.useHTML).addClass(\"highcharts-data-label\"):h.label(F,0,0,b.shape,void 0,void 0,b.useHTML,void 0,\"data-label\"))&&w.addClass(\" highcharts-data-label-color-\"+a.colorIndex+\" \"+(b.className||\"\")+(b.useHTML?\" highcharts-tracker\":\"\")),w&&(w.options=b,w.attr(r),e.styledMode||w.css(q).shadow(b.shadow),(m=b[a.formatPrefix+\"TextPath\"]||b.textPath)&&!b.useHTML&&(w.setTextPath((null===(l=a.getDataLabelPath)||void 0===l?void 0:l.call(a,w))||a.graphic,m),a.dataLabelPath&&!m.enabled&&\n(a.dataLabelPath=a.dataLabelPath.destroy())),w.added||w.add(y),d.alignDataLabel(a,w,b,void 0,u),w.isActive=!0,c[f]&&c[f]!==w&&c[f].destroy(),c[f]=w))});for(b=c.length;b--;)c[b].isActive?c[b].isActive=!1:(c[b].destroy(),c.splice(b,1));a.dataLabel=c[0];a.dataLabels=c});B(this,\"afterDrawDataLabels\")}function k(a,d,c,e,g,k){const b=this.chart,f=d.align,h=d.verticalAlign,l=a.box?0:a.padding||0;let {x:m=0,y:n=0}=d,p,q;p=(c.x||0)+l;0>p&&(\"right\"===f&&0<=m?(d.align=\"left\",d.inside=!0):m-=p,q=!0);p=(c.x||\n0)+e.width-l;p>b.plotWidth&&(\"left\"===f&&0>=m?(d.align=\"right\",d.inside=!0):m+=b.plotWidth-p,q=!0);p=c.y+l;0>p&&(\"bottom\"===h&&0<=n?(d.verticalAlign=\"top\",d.inside=!0):n-=p,q=!0);p=(c.y||0)+e.height-l;p>b.plotHeight&&(\"top\"===h&&0>=n?(d.verticalAlign=\"bottom\",d.inside=!0):n+=b.plotHeight-p,q=!0);q&&(d.x=m,d.y=n,a.placed=!k,a.align(d,void 0,g));return q}function r(a,d){let b=[],f;if(u(a)&&!u(d))b=a.map(function(a){return l(a,d)});else if(u(d)&&!u(a))b=d.map(function(b){return l(a,b)});else if(!u(a)&&\n!u(d))b=l(a,d);else if(u(a)&&u(d))for(f=Math.max(a.length,d.length);f--;)b[f]=l(a[f],d[f]);return b}function q(a,d,c,e,g){const b=this.chart,f=b.inverted,k=this.xAxis,h=k.reversed,l=f?d.height/2:d.width/2;a=(a=a.pointWidth)?a/2:0;d.startXPos=f?g.x:h?-l-a:k.width-l+a;d.startYPos=f?h?this.yAxis.height-l+a:-l-a:g.y;e?\"hidden\"===d.visibility&&(d.show(),d.attr({opacity:0}).animate({opacity:1})):d.attr({opacity:1}).animate({opacity:0},void 0,d.hide);b.hasRendered&&(c&&d.attr({x:d.startXPos,y:d.startYPos}),\nd.placed=!0)}const y=[];a.compose=function(a){I.pushUnique(y,a)&&(a=a.prototype,a.initDataLabelsGroup=h,a.initDataLabels=F,a.alignDataLabel=e,a.drawDataLabels=d,a.justifyDataLabel=k,a.setDataLabelStartPos=q)}})(h||(h={}));\"\";return h});M(a,\"Series/Column/ColumnDataLabel.js\",[a[\"Core/Series/DataLabel.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {series:x}=y,{merge:C,pick:z}=I;var H;(function(y){function u(a,p,t,m,h){let g=this.chart.inverted;var e=a.series;\nlet l=(e.xAxis?e.xAxis.len:this.chart.plotSizeX)||0;e=(e.yAxis?e.yAxis.len:this.chart.plotSizeY)||0;var v=a.dlBox||a.shapeArgs;let u=z(a.below,a.plotY>z(this.translatedThreshold,e)),d=z(t.inside,!!this.options.stacking);v&&(m=C(v),0>m.y&&(m.height+=m.y,m.y=0),v=m.y+m.height-e,0<v&&v<m.height&&(m.height-=v),g&&(m={x:e-m.y-m.height,y:l-m.x-m.width,width:m.height,height:m.width}),d||(g?(m.x+=u?0:m.width,m.width=0):(m.y+=u?m.height:0,m.height=0)));t.align=z(t.align,!g||d?\"center\":u?\"right\":\"left\");t.verticalAlign=\nz(t.verticalAlign,g||d?\"middle\":u?\"top\":\"bottom\");x.prototype.alignDataLabel.call(this,a,p,t,m,h);t.inside&&a.contrastColor&&p.css({color:a.contrastColor})}const v=[];y.compose=function(l){a.compose(x);I.pushUnique(v,l)&&(l.prototype.alignDataLabel=u)}})(H||(H={}));return H});M(a,\"Series/Bar/BarSeries.js\",[a[\"Series/Column/ColumnSeries.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {extend:x,merge:C}=I;class z extends a{constructor(){super(...arguments);this.points=\nthis.options=this.data=void 0}}z.defaultOptions=C(a.defaultOptions,{});x(z.prototype,{inverted:!0});y.registerSeriesType(\"bar\",z);\"\";return z});M(a,\"Series/Scatter/ScatterSeriesDefaults.js\",[],function(){\"\";return{lineWidth:0,findNearestPointBy:\"xy\",jitter:{x:0,y:0},marker:{enabled:!0},tooltip:{headerFormat:'<span style=\"color:{point.color}\">\\u25cf</span> <span style=\"font-size: 0.8em\"> {series.name}</span><br/>',pointFormat:\"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>\"}}});M(a,\"Series/Scatter/ScatterSeries.js\",\n[a[\"Series/Scatter/ScatterSeriesDefaults.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {column:x,line:C}=y.seriesTypes,{addEvent:z,extend:H,merge:B}=I;class u extends C{constructor(){super(...arguments);this.points=this.options=this.data=void 0}applyJitter(){const a=this,l=this.options.jitter,p=this.points.length;l&&this.points.forEach(function(t,m){[\"x\",\"y\"].forEach(function(h,g){let e=\"plot\"+h.toUpperCase(),w,v;if(l[h]&&!t.isNull){var u=a[h+\"Axis\"];v=l[h]*\nu.transA;u&&!u.isLog&&(w=Math.max(0,t[e]-v),u=Math.min(u.len,t[e]+v),g=1E4*Math.sin(m+g*p),g-=Math.floor(g),t[e]=w+(u-w)*g,\"x\"===h&&(t.clientX=t.plotX))}})})}drawGraph(){this.options.lineWidth?super.drawGraph():this.graph&&(this.graph=this.graph.destroy())}}u.defaultOptions=B(C.defaultOptions,a);H(u.prototype,{drawTracker:x.prototype.drawTracker,sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:[\"group\",\"markerGroup\",\"dataLabelsGroup\"],takeOrdinalPosition:!1});z(u,\"afterTranslate\",function(){this.applyJitter()});\ny.registerSeriesType(\"scatter\",u);return u});M(a,\"Series/CenteredUtilities.js\",[a[\"Core/Globals.js\"],a[\"Core/Series/Series.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {deg2rad:x}=a,{fireEvent:C,isNumber:z,pick:H,relativeLength:B}=I;var u;(function(a){a.getCenter=function(){var a=this.options,p=this.chart;const t=2*(a.slicedOffset||0),m=p.plotWidth-2*t,h=p.plotHeight-2*t;var g=a.center;const e=Math.min(m,h),w=a.thickness;var v=a.size;let u=a.innerSize||0;\"string\"===typeof v&&(v=parseFloat(v));\n\"string\"===typeof u&&(u=parseFloat(u));a=[H(g[0],\"50%\"),H(g[1],\"50%\"),H(v&&0>v?void 0:a.size,\"100%\"),H(u&&0>u?void 0:a.innerSize||0,\"0%\")];!p.angular||this instanceof y||(a[3]=0);for(g=0;4>g;++g)v=a[g],p=2>g||2===g&&/%$/.test(v),a[g]=B(v,[m,h,e,a[2]][g])+(p?t:0);a[3]>a[2]&&(a[3]=a[2]);z(w)&&2*w<a[2]&&0<w&&(a[3]=a[2]-2*w);C(this,\"afterGetCenter\",{positions:a});return a};a.getStartAndEndRadians=function(a,p){a=z(a)?a:0;p=z(p)&&p>a&&360>p-a?p:a+360;return{start:x*(a+-90),end:x*(p+-90)}}})(u||(u={}));\n\"\";return u});M(a,\"Series/Pie/PiePoint.js\",[a[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Series/Point.js\"],a[\"Core/Utilities.js\"]],function(a,y,I){const {setAnimation:x}=a,{addEvent:C,defined:z,extend:H,isNumber:B,pick:u,relativeLength:v}=I;class l extends y{constructor(){super(...arguments);this.series=this.options=this.labelDistance=void 0}getConnectorPath(){const a=this.labelPosition,l=this.series.options.dataLabels,m=this.connectorShapes;let h=l.connectorShape;m[h]&&(h=m[h]);return h.call(this,\n{x:a.computed.x,y:a.computed.y,alignment:a.alignment},a.connectorPosition,l)}getTranslate(){return this.sliced?this.slicedTranslation:{translateX:0,translateY:0}}haloPath(a){const l=this.shapeArgs;return this.sliced||!this.visible?[]:this.series.chart.renderer.symbols.arc(l.x,l.y,l.r+a,l.r+a,{innerR:l.r-1,start:l.start,end:l.end,borderRadius:l.borderRadius})}init(){super.init.apply(this,arguments);this.name=u(this.name,\"Slice\");const a=a=>{this.slice(\"select\"===a.type)};C(this,\"select\",a);C(this,\n\"unselect\",a);return this}isValid(){return B(this.y)&&0<=this.y}setVisible(a,l){const m=this.series,h=m.chart,g=m.options.ignoreHiddenPoint;l=u(l,g);a!==this.visible&&(this.visible=this.options.visible=a=\"undefined\"===typeof a?!this.visible:a,m.options.data[m.data.indexOf(this)]=this.options,[\"graphic\",\"dataLabel\",\"connector\"].forEach(e=>{if(this[e])this[e][a?\"show\":\"hide\"](a)}),this.legendItem&&h.legend.colorizeItem(this,a),a||\"hover\"!==this.state||this.setState(\"\"),g&&(m.isDirty=!0),l&&h.redraw())}slice(a,\nl,m){const h=this.series;x(m,h.chart);u(l,!0);this.sliced=this.options.sliced=z(a)?a:!this.sliced;h.options.data[h.data.indexOf(this)]=this.options;this.graphic&&this.graphic.animate(this.getTranslate())}}H(l.prototype,{connectorShapes:{fixedOffset:function(a,l,m){const h=l.breakAt;l=l.touchingSliceAt;return[[\"M\",a.x,a.y],m.softConnector?[\"C\",a.x+(\"left\"===a.alignment?-5:5),a.y,2*h.x-l.x,2*h.y-l.y,h.x,h.y]:[\"L\",h.x,h.y],[\"L\",l.x,l.y]]},straight:function(a,l){l=l.touchingSliceAt;return[[\"M\",a.x,a.y],\n[\"L\",l.x,l.y]]},crookedLine:function(a,l,m){const {breakAt:h,touchingSliceAt:g}=l;({series:l}=this);const [e,p,t]=l.center,u=t/2,d=l.chart.plotWidth,k=l.chart.plotLeft;l=\"left\"===a.alignment;const {x:r,y:q}=a;m.crookDistance?(a=v(m.crookDistance,1),a=l?e+u+(d+k-e-u)*(1-a):k+(e-u)*a):a=e+(p-q)*Math.tan((this.angle||0)-Math.PI/2);m=[[\"M\",r,q]];(l?a<=r&&a>=h.x:a>=r&&a<=h.x)&&m.push([\"L\",a,q]);m.push([\"L\",h.x,h.y],[\"L\",g.x,g.y]);return m}}});return l});M(a,\"Series/Pie/PieSeriesDefaults.js\",[],function(){\"\";\nreturn{borderRadius:3,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{allowOverlap:!0,connectorPadding:5,connectorShape:\"crookedLine\",crookDistance:void 0,distance:30,enabled:!0,formatter:function(){return this.point.isNull?void 0:this.point.name},softConnector:!0,x:0},fillColor:void 0,ignoreHiddenPoint:!0,inactiveOtherPoints:!0,legendType:\"point\",marker:null,size:null,showInLegend:!1,slicedOffset:10,stickyTracking:!1,tooltip:{followPointer:!0},borderColor:\"#ffffff\",borderWidth:1,lineWidth:void 0,\nstates:{hover:{brightness:.1}}}});M(a,\"Series/Pie/PieSeries.js\",[a[\"Series/CenteredUtilities.js\"],a[\"Series/Column/ColumnSeries.js\"],a[\"Core/Globals.js\"],a[\"Series/Pie/PiePoint.js\"],a[\"Series/Pie/PieSeriesDefaults.js\"],a[\"Core/Series/Series.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Renderer/SVG/Symbols.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C,z,H,B,u){const {getStartAndEndRadians:v}=a;({noop:I}=I);const {clamp:l,extend:p,fireEvent:t,merge:m,pick:h,relativeLength:g}=u;class e extends z{constructor(){super(...arguments);\nthis.points=this.options=this.maxLabelDistance=this.data=this.center=void 0}animate(a){const e=this,g=e.points,d=e.startAngleRad;a||g.forEach(function(a){const g=a.graphic,k=a.shapeArgs;g&&k&&(g.attr({r:h(a.startR,e.center&&e.center[3]/2),start:d,end:d}),g.animate({r:k.r,start:k.start,end:k.end},e.options.animation))})}drawEmpty(){const a=this.startAngleRad,e=this.endAngleRad,g=this.options;let d,k;0===this.total&&this.center?(d=this.center[0],k=this.center[1],this.graph||(this.graph=this.chart.renderer.arc(d,\nk,this.center[1]/2,0,a,e).addClass(\"highcharts-empty-series\").add(this.group)),this.graph.attr({d:B.arc(d,k,this.center[2]/2,0,{start:a,end:e,innerR:this.center[3]/2})}),this.chart.styledMode||this.graph.attr({\"stroke-width\":g.borderWidth,fill:g.fillColor||\"none\",stroke:g.color||\"#cccccc\"})):this.graph&&(this.graph=this.graph.destroy())}drawPoints(){const a=this.chart.renderer;this.points.forEach(function(e){e.graphic&&e.hasNewShapeType()&&(e.graphic=e.graphic.destroy());e.graphic||(e.graphic=a[e.shapeType](e.shapeArgs).add(e.series.group),\ne.delayedRendering=!0)})}generatePoints(){super.generatePoints();this.updateTotals()}getX(a,e,g){const d=this.center,k=this.radii?this.radii[g.index]||0:d[2]/2;a=Math.asin(l((a-d[1])/(k+g.labelDistance),-1,1));return d[0]+(e?-1:1)*Math.cos(a)*(k+g.labelDistance)+(0<g.labelDistance?(e?-1:1)*this.options.dataLabels.padding:0)}hasData(){return!!this.processedXData.length}redrawPoints(){const a=this,e=a.chart;let g,d,k,h;this.drawEmpty();a.group&&!e.styledMode&&a.group.shadow(a.options.shadow);a.points.forEach(function(l){const q=\n{};d=l.graphic;!l.isNull&&d?(h=l.shapeArgs,g=l.getTranslate(),e.styledMode||(k=a.pointAttribs(l,l.selected&&\"select\")),l.delayedRendering?(d.setRadialReference(a.center).attr(h).attr(g),e.styledMode||d.attr(k).attr({\"stroke-linejoin\":\"round\"}),l.delayedRendering=!1):(d.setRadialReference(a.center),e.styledMode||m(!0,q,k),m(!0,q,h,g),d.animate(q)),d.attr({visibility:l.visible?\"inherit\":\"hidden\"}),d.addClass(l.getClassName(),!0)):d&&(l.graphic=d.destroy())})}sortByAngle(a,e){a.sort(function(a,d){return\"undefined\"!==\ntypeof a.angle&&(d.angle-a.angle)*e})}translate(a){t(this,\"translate\");this.generatePoints();var e=this.options;const l=e.slicedOffset,d=l+(e.borderWidth||0);var k=v(e.startAngle,e.endAngle);const m=this.startAngleRad=k.start;k=(this.endAngleRad=k.end)-m;const q=this.points,p=e.dataLabels.distance;e=e.ignoreHiddenPoint;const b=q.length;let f,c,n,w=0;a||(this.center=a=this.getCenter());for(c=0;c<b;c++){n=q[c];var u=m+w*k;!n.isValid()||e&&!n.visible||(w+=n.percentage/100);var x=m+w*k;var y={x:a[0],\ny:a[1],r:a[2]/2,innerR:a[3]/2,start:Math.round(1E3*u)/1E3,end:Math.round(1E3*x)/1E3};n.shapeType=\"arc\";n.shapeArgs=y;n.labelDistance=h(n.options.dataLabels&&n.options.dataLabels.distance,p);n.labelDistance=g(n.labelDistance,y.r);this.maxLabelDistance=Math.max(this.maxLabelDistance||0,n.labelDistance);x=(x+u)/2;x>1.5*Math.PI?x-=2*Math.PI:x<-Math.PI/2&&(x+=2*Math.PI);n.slicedTranslation={translateX:Math.round(Math.cos(x)*l),translateY:Math.round(Math.sin(x)*l)};y=Math.cos(x)*a[2]/2;f=Math.sin(x)*a[2]/\n2;n.tooltipPos=[a[0]+.7*y,a[1]+.7*f];n.half=x<-Math.PI/2||x>Math.PI/2?1:0;n.angle=x;u=Math.min(d,n.labelDistance/5);n.labelPosition={natural:{x:a[0]+y+Math.cos(x)*n.labelDistance,y:a[1]+f+Math.sin(x)*n.labelDistance},computed:{},alignment:0>n.labelDistance?\"center\":n.half?\"right\":\"left\",connectorPosition:{breakAt:{x:a[0]+y+Math.cos(x)*u,y:a[1]+f+Math.sin(x)*u},touchingSliceAt:{x:a[0]+y,y:a[1]+f}}}}t(this,\"afterTranslate\")}updateTotals(){const a=this.points,e=a.length,g=this.options.ignoreHiddenPoint;\nlet d,k,h=0;for(d=0;d<e;d++)k=a[d],!k.isValid()||g&&!k.visible||(h+=k.y);this.total=h;for(d=0;d<e;d++)k=a[d],k.percentage=0<h&&(k.visible||!g)?k.y/h*100:0,k.total=h}}e.defaultOptions=m(z.defaultOptions,C);p(e.prototype,{axisTypes:[],directTouch:!0,drawGraph:void 0,drawTracker:y.prototype.drawTracker,getCenter:a.getCenter,getSymbol:I,isCartesian:!1,noSharedTooltip:!0,pointAttribs:y.prototype.pointAttribs,pointClass:L,requireSorting:!1,searchPoint:I,trackerGroups:[\"group\",\"dataLabelsGroup\"]});H.registerSeriesType(\"pie\",\ne);return e});M(a,\"Series/Pie/PieDataLabel.js\",[a[\"Core/Series/DataLabel.js\"],a[\"Core/Globals.js\"],a[\"Core/Renderer/RendererUtilities.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C){const {noop:x}=y,{distribute:H}=I,{series:B}=L,{arrayMax:u,clamp:v,defined:l,merge:p,pick:t,relativeLength:m}=C;var h;(function(g){function e(){const a=this,d=a.data,e=a.chart,g=a.options.dataLabels||{},b=g.connectorPadding,f=e.plotWidth,c=e.plotHeight,h=e.plotLeft,m=Math.round(e.chartWidth/\n3),w=a.center,v=w[2]/2,x=w[1],y=[[],[]],z=[0,0,0,0],F=a.dataLabelPositioners;let C,A,E,I,L,M,J,N,O,S,W,U;a.visible&&(g.enabled||a._hasPointLabels)&&(d.forEach(function(a){a.dataLabel&&a.visible&&a.dataLabel.shortened&&(a.dataLabel.attr({width:\"auto\"}).css({width:\"auto\",textOverflow:\"clip\"}),a.dataLabel.shortened=!1)}),B.prototype.drawDataLabels.apply(a),d.forEach(function(a){a.dataLabel&&(a.visible?(y[a.half].push(a),a.dataLabel._pos=null,!l(g.style.width)&&!l(a.options.dataLabels&&a.options.dataLabels.style&&\na.options.dataLabels.style.width)&&a.dataLabel.getBBox().width>m&&(a.dataLabel.css({width:Math.round(.7*m)+\"px\"}),a.dataLabel.shortened=!0)):(a.dataLabel=a.dataLabel.destroy(),a.dataLabels&&1===a.dataLabels.length&&delete a.dataLabels))}),y.forEach((d,k)=>{const m=d.length,n=[];let q,p=0;if(m){a.sortByAngle(d,k-.5);if(0<a.maxLabelDistance){var r=Math.max(0,x-v-a.maxLabelDistance);q=Math.min(x+v+a.maxLabelDistance,e.plotHeight);d.forEach(function(a){0<a.labelDistance&&a.dataLabel&&(a.top=Math.max(0,\nx-v-a.labelDistance),a.bottom=Math.min(x+v+a.labelDistance,e.plotHeight),p=a.dataLabel.getBBox().height||21,a.distributeBox={target:a.labelPosition.natural.y-a.top+p/2,size:p,rank:a.y},n.push(a.distributeBox))});r=q+p-r;H(n,r,r/5)}for(W=0;W<m;W++){C=d[W];M=C.labelPosition;I=C.dataLabel;S=!1===C.visible?\"hidden\":\"inherit\";O=r=M.natural.y;n&&l(C.distributeBox)&&(\"undefined\"===typeof C.distributeBox.pos?S=\"hidden\":(J=C.distributeBox.size,O=F.radialDistributionY(C)));delete C.positionIndex;if(g.justify)N=\nF.justify(C,v,w);else switch(g.alignTo){case \"connectors\":N=F.alignToConnectors(d,k,f,h);break;case \"plotEdges\":N=F.alignToPlotEdges(I,k,f,h);break;default:N=F.radialDistributionX(a,C,O,r)}I._attr={visibility:S,align:M.alignment};U=C.options.dataLabels||{};I._pos={x:N+t(U.x,g.x)+({left:b,right:-b}[M.alignment]||0),y:O+t(U.y,g.y)-I.getBBox().height/2};M&&(M.computed.x=N,M.computed.y=O);t(g.crop,!0)&&(L=I.getBBox().width,r=null,N-L<b&&1===k?(r=Math.round(L-N+b),z[3]=Math.max(r,z[3])):N+L>f-b&&0===k&&\n(r=Math.round(N+L-f+b),z[1]=Math.max(r,z[1])),0>O-J/2?z[0]=Math.max(Math.round(-O+J/2),z[0]):O+J/2>c&&(z[2]=Math.max(Math.round(O+J/2-c),z[2])),I.sideOverflow=r)}}}),0===u(z)||this.verifyDataLabelOverflow(z))&&(this.placeDataLabels(),this.points.forEach(function(b){U=p(g,b.options.dataLabels);if(A=t(U.connectorWidth,1)){let c;E=b.connector;if((I=b.dataLabel)&&I._pos&&b.visible&&0<b.labelDistance){S=I._attr.visibility;if(c=!E)b.connector=E=e.renderer.path().addClass(\"highcharts-data-label-connector  highcharts-color-\"+\nb.colorIndex+(b.className?\" \"+b.className:\"\")).add(a.dataLabelsGroup),e.styledMode||E.attr({\"stroke-width\":A,stroke:U.connectorColor||b.color||\"#666666\"});E[c?\"attr\":\"animate\"]({d:b.getConnectorPath()});E.attr(\"visibility\",S)}else E&&(b.connector=E.destroy())}}))}function h(){this.points.forEach(function(a){let d=a.dataLabel,e;d&&a.visible&&((e=d._pos)?(d.sideOverflow&&(d._attr.width=Math.max(d.getBBox().width-d.sideOverflow,0),d.css({width:d._attr.width+\"px\",textOverflow:(this.options.dataLabels.style||\n{}).textOverflow||\"ellipsis\"}),d.shortened=!0),d.attr(d._attr),d[d.moved?\"animate\":\"attr\"](e),d.moved=!0):d&&d.attr({y:-9999}));delete a.distributeBox},this)}function y(a){let d=this.center,e=this.options,g=e.center,b=e.minSize||80,f,c=null!==e.size;c||(null!==g[0]?f=Math.max(d[2]-Math.max(a[1],a[3]),b):(f=Math.max(d[2]-a[1]-a[3],b),d[0]+=(a[3]-a[1])/2),null!==g[1]?f=v(f,b,d[2]-Math.max(a[0],a[2])):(f=v(f,b,d[2]-a[0]-a[2]),d[1]+=(a[0]-a[2])/2),f<d[2]?(d[2]=f,d[3]=Math.min(e.thickness?Math.max(0,f-\n2*e.thickness):Math.max(0,m(e.innerSize||0,f)),f),this.translate(d),this.drawDataLabels&&this.drawDataLabels()):c=!0);return c}const z=[],d={radialDistributionY:function(a){return a.top+a.distributeBox.pos},radialDistributionX:function(a,d,e,g){return a.getX(e<d.top+2||e>d.bottom-2?g:e,d.half,d)},justify:function(a,d,e){return e[0]+(a.half?-1:1)*(d+a.labelDistance)},alignToPlotEdges:function(a,d,e,g){a=a.getBBox().width;return d?a+g:e-a-g},alignToConnectors:function(a,d,e,g){let b=0,f;a.forEach(function(a){f=\na.dataLabel.getBBox().width;f>b&&(b=f)});return d?b+g:e-b-g}};g.compose=function(g){a.compose(B);C.pushUnique(z,g)&&(g=g.prototype,g.dataLabelPositioners=d,g.alignDataLabel=x,g.drawDataLabels=e,g.placeDataLabels=h,g.verifyDataLabelOverflow=y)}})(h||(h={}));return h});M(a,\"Extensions/OverlappingDataLabels.js\",[a[\"Core/Chart/Chart.js\"],a[\"Core/Utilities.js\"]],function(a,y){function x(a,l){let p,t=!1;a&&(p=a.newOpacity,a.oldOpacity!==p&&(a.alignAttr&&a.placed?(a[p?\"removeClass\":\"addClass\"](\"highcharts-data-label-hidden\"),\nt=!0,a.alignAttr.opacity=p,a[a.isOld?\"animate\":\"attr\"](a.alignAttr,null,function(){l.styledMode||a.css({pointerEvents:p?\"auto\":\"none\"})}),C(l,\"afterHideOverlappingLabel\")):a.attr({opacity:p})),a.isOld=!0);return t}const {addEvent:L,fireEvent:C,isArray:z,isNumber:H,objectEach:B,pick:u}=y;L(a,\"render\",function(){let a=this,l=[];(this.labelCollectors||[]).forEach(function(a){l=l.concat(a())});(this.yAxis||[]).forEach(function(a){a.stacking&&a.options.stackLabels&&!a.options.stackLabels.allowOverlap&&\nB(a.stacking.stacks,function(a){B(a,function(a){a.label&&l.push(a.label)})})});(this.series||[]).forEach(function(p){var t=p.options.dataLabels;p.visible&&(!1!==t.enabled||p._hasPointLabels)&&(t=m=>m.forEach(h=>{h.visible&&(z(h.dataLabels)?h.dataLabels:h.dataLabel?[h.dataLabel]:[]).forEach(function(g){const e=g.options;g.labelrank=u(e.labelrank,h.labelrank,h.shapeArgs&&h.shapeArgs.height);e.allowOverlap?(g.oldOpacity=g.opacity,g.newOpacity=1,x(g,a)):l.push(g)})}),t(p.nodes||[]),t(p.points))});this.hideOverlappingLabels(l)});\na.prototype.hideOverlappingLabels=function(a){let l=this,p=a.length,t=l.renderer;var m;let h;let g,e,w,u=!1;var v=function(a){let d,e;var g;let h=a.box?0:a.padding||0,b=g=0,f,c;if(a&&(!a.alignAttr||a.placed))return d=a.alignAttr||{x:a.attr(\"x\"),y:a.attr(\"y\")},e=a.parentGroup,a.width||(g=a.getBBox(),a.width=g.width,a.height=g.height,g=t.fontMetrics(a.element).h),f=a.width-2*h,(c={left:\"0\",center:\"0.5\",right:\"1\"}[a.alignValue])?b=+c*f:H(a.x)&&Math.round(a.x)!==a.translateX&&(b=a.x-a.translateX),{x:d.x+\n(e.translateX||0)+h-(b||0),y:d.y+(e.translateY||0)+h-g,width:a.width-2*h,height:a.height-2*h}};for(h=0;h<p;h++)if(m=a[h])m.oldOpacity=m.opacity,m.newOpacity=1,m.absoluteBox=v(m);a.sort(function(a,e){return(e.labelrank||0)-(a.labelrank||0)});for(h=0;h<p;h++)for(e=(v=a[h])&&v.absoluteBox,m=h+1;m<p;++m)w=(g=a[m])&&g.absoluteBox,!e||!w||v===g||0===v.newOpacity||0===g.newOpacity||\"hidden\"===v.visibility||\"hidden\"===g.visibility||w.x>=e.x+e.width||w.x+w.width<=e.x||w.y>=e.y+e.height||w.y+w.height<=e.y||\n((v.labelrank<g.labelrank?v:g).newOpacity=0);a.forEach(function(a){x(a,l)&&(u=!0)});u&&C(l,\"afterHideAllOverlappingLabels\")}});M(a,\"Extensions/BorderRadius.js\",[a[\"Core/Defaults.js\"],a[\"Core/Series/Series.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Core/Renderer/SVG/SVGElement.js\"],a[\"Core/Renderer/SVG/SVGRenderer.js\"],a[\"Core/Utilities.js\"]],function(a,y,I,L,C,z){const {defaultOptions:x}=a;({seriesTypes:a}=I);const {addEvent:B,extend:u,isObject:v,merge:l,relativeLength:p}=z,t={radius:0,scope:\"stack\",\nwhere:void 0},m=(a,g)=>{v(a)||(a={radius:a||0});return l(t,g,a)};if(-1===L.symbolCustomAttribs.indexOf(\"borderRadius\")){L.symbolCustomAttribs.push(\"borderRadius\",\"brBoxHeight\",\"brBoxY\");const h=C.prototype.symbols.arc;C.prototype.symbols.arc=function(a,g,l,m,d={}){a=h(a,g,l,m,d);const {innerR:e=0,r=l,start:q=0,end:t=0}=d;if(d.open||!d.borderRadius)return a;l=t-q;g=Math.sin(l/2);d=Math.max(Math.min(p(d.borderRadius||0,r-e),(r-e)/2,r*g/(1+g)),0);l=Math.min(d,l/Math.PI*2*e);for(g=a.length-1;g--;){{let e=\nvoid 0,h=void 0,k=void 0;m=a;var b=g,f=1<g?l:d,c=m[b],n=m[b+1];\"Z\"===n[0]&&(n=m[0]);\"M\"!==c[0]&&\"L\"!==c[0]||\"A\"!==n[0]?\"A\"!==c[0]||\"M\"!==n[0]&&\"L\"!==n[0]||(k=n,h=c):(k=c,h=n,e=!0);if(k&&h&&h.params){c=h[1];var w=h[5];n=h.params;const {start:a,end:d,cx:g,cy:l}=n;var u=w?c-f:c+f;const p=u?Math.asin(f/u):0;w=w?p:-p;u*=Math.cos(p);e?(n.start=a+w,k[1]=g+u*Math.cos(a),k[2]=l+u*Math.sin(a),m.splice(b+1,0,[\"A\",f,f,0,0,1,g+c*Math.cos(n.start),l+c*Math.sin(n.start)])):(n.end=d-w,h[6]=g+c*Math.cos(n.end),h[7]=\nl+c*Math.sin(n.end),m.splice(b+1,0,[\"A\",f,f,0,0,1,g+u*Math.cos(d),l+u*Math.sin(d)]));h[4]=Math.abs(n.end-n.start)<Math.PI?0:1}}}return a};const g=C.prototype.symbols.roundedRect;C.prototype.symbols.roundedRect=function(a,h,l,m,d={}){const e=g(a,h,l,m,d),{r:p=0,brBoxHeight:q=m,brBoxY:t=h}=d;var b=h-t,f=t+q-(h+m);d=-.1<b-p?0:p;const c=-.1<f-p?0:p;var n=Math.max(d&&b,0);const u=Math.max(c&&f,0);f=[a+d,h];b=[a+l-d,h];const w=[a+l,h+d],v=[a+l,h+m-c],x=[a+l-c,h+m],y=[a+c,h+m],z=[a,h+m-c],B=[a,h+d];if(n){const a=\nMath.sqrt(Math.pow(d,2)-Math.pow(d-n,2));f[0]-=a;b[0]+=a;w[1]=B[1]=h+d-n}m<d-n&&(n=Math.sqrt(Math.pow(d,2)-Math.pow(d-n-m,2)),w[0]=v[0]=a+l-d+n,x[0]=Math.min(w[0],x[0]),y[0]=Math.max(v[0],y[0]),z[0]=B[0]=a+d-n,w[1]=B[1]=h+m);u&&(n=Math.sqrt(Math.pow(c,2)-Math.pow(c-u,2)),x[0]+=n,y[0]-=n,v[1]=z[1]=h+m-c+u);m<c-u&&(m=Math.sqrt(Math.pow(c,2)-Math.pow(c-u-m,2)),w[0]=v[0]=a+l-c+m,b[0]=Math.min(w[0],b[0]),f[0]=Math.max(v[0],f[0]),z[0]=B[0]=a+c-m,v[1]=z[1]=h);e.length=0;e.push([\"M\",...f],[\"L\",...b],[\"A\",\nd,d,0,0,1,...w],[\"L\",...v],[\"A\",c,c,0,0,1,...x],[\"L\",...y],[\"A\",c,c,0,0,1,...z],[\"L\",...B],[\"A\",d,d,0,0,1,...f],[\"Z\"]);return e};B(a.pie,\"afterTranslate\",function(){const a=m(this.options.borderRadius);for(const e of this.points){const g=e.shapeArgs;g&&(g.borderRadius=p(a.radius,(g.r||0)-(g.innerR||0)))}});B(y,\"afterColumnTranslate\",function(){var a,g;if(this.options.borderRadius&&(!this.chart.is3d||!this.chart.is3d())){const {options:e,yAxis:r}=this,q=\"percent\"===e.stacking;var h=null===(g=null===\n(a=x.plotOptions)||void 0===a?void 0:a[this.type])||void 0===g?void 0:g.borderRadius;a=m(e.borderRadius,v(h)?h:{});g=r.options.reversed;for(const k of this.points)if({shapeArgs:h}=k,\"roundedRect\"===k.shapeType&&h){const {width:b=0,height:f=0,y:c=0}=h;var l=c,d=f;\"stack\"===a.scope&&k.stackTotal&&(l=r.translate(q?100:k.stackTotal,!1,!0,!1,!0),d=r.translate(e.threshold||0,!1,!0,!1,!0),d=this.crispCol(0,Math.min(l,d),0,Math.abs(l-d)),l=d.y,d=d.height);const m=-1===(k.negative?-1:1)*(g?-1:1);let t=a.where;\n!t&&this.is(\"waterfall\")&&Math.abs((k.yBottom||0)-(this.translatedThreshold||0))>this.borderWidth&&(t=\"all\");t||(t=\"end\");const v=Math.min(p(a.radius,b),b/2,\"all\"===t?f/2:Infinity)||0;\"end\"===t&&(m&&(l-=v),d+=v);u(h,{brBoxHeight:d,brBoxY:l,r:v})}}},{order:9})}y={optionsToObject:m};\"\";return y});M(a,\"Core/Responsive.js\",[a[\"Core/Utilities.js\"]],function(a){const {diffObjects:x,extend:I,find:L,merge:C,pick:z,uniqueKey:H}=a;var B;(function(u){function v(a,l){const h=a.condition;(h.callback||function(){return this.chartWidth<=\nz(h.maxWidth,Number.MAX_VALUE)&&this.chartHeight<=z(h.maxHeight,Number.MAX_VALUE)&&this.chartWidth>=z(h.minWidth,0)&&this.chartHeight>=z(h.minHeight,0)}).call(this)&&l.push(a._id)}function l(a,l){const h=this.options.responsive;var g=this.currentResponsive;let e=[];!l&&h&&h.rules&&h.rules.forEach(a=>{\"undefined\"===typeof a._id&&(a._id=H());this.matchResponsiveRule(a,e)},this);l=C(...e.map(a=>L((h||{}).rules||[],e=>e._id===a)).map(a=>a&&a.chartOptions));l.isResponsiveOptions=!0;e=e.toString()||void 0;\ne!==(g&&g.ruleIds)&&(g&&this.update(g.undoOptions,a,!0),e?(g=x(l,this.options,!0,this.collectionsWithUpdate),g.isResponsiveOptions=!0,this.currentResponsive={ruleIds:e,mergedOptions:l,undoOptions:g},this.update(l,a,!0)):this.currentResponsive=void 0)}const p=[];u.compose=function(t){a.pushUnique(p,t)&&I(t.prototype,{matchResponsiveRule:v,setResponsive:l});return t}})(B||(B={}));\"\";\"\";return B});M(a,\"masters/highcharts.src.js\",[a[\"Core/Globals.js\"],a[\"Core/Utilities.js\"],a[\"Core/Defaults.js\"],a[\"Core/Animation/Fx.js\"],\na[\"Core/Animation/AnimationUtilities.js\"],a[\"Core/Renderer/HTML/AST.js\"],a[\"Core/Templating.js\"],a[\"Core/Renderer/RendererUtilities.js\"],a[\"Core/Renderer/SVG/SVGElement.js\"],a[\"Core/Renderer/SVG/SVGRenderer.js\"],a[\"Core/Renderer/HTML/HTMLElement.js\"],a[\"Core/Renderer/HTML/HTMLRenderer.js\"],a[\"Core/Axis/Axis.js\"],a[\"Core/Axis/DateTimeAxis.js\"],a[\"Core/Axis/LogarithmicAxis.js\"],a[\"Core/Axis/PlotLineOrBand/PlotLineOrBand.js\"],a[\"Core/Axis/Tick.js\"],a[\"Core/Tooltip.js\"],a[\"Core/Series/Point.js\"],a[\"Core/Pointer.js\"],\na[\"Core/Legend/Legend.js\"],a[\"Core/Chart/Chart.js\"],a[\"Core/Axis/Stacking/StackingAxis.js\"],a[\"Core/Axis/Stacking/StackItem.js\"],a[\"Core/Series/Series.js\"],a[\"Core/Series/SeriesRegistry.js\"],a[\"Series/Column/ColumnSeries.js\"],a[\"Series/Column/ColumnDataLabel.js\"],a[\"Series/Pie/PieSeries.js\"],a[\"Series/Pie/PieDataLabel.js\"],a[\"Core/Series/DataLabel.js\"],a[\"Core/Responsive.js\"],a[\"Core/Color/Color.js\"],a[\"Core/Time.js\"]],function(a,y,I,L,C,z,H,B,u,v,l,p,t,m,h,g,e,w,E,F,d,k,r,q,G,b,f,c,n,M,D,K,U,T){a.animate=\nC.animate;a.animObject=C.animObject;a.getDeferredAnimation=C.getDeferredAnimation;a.setAnimation=C.setAnimation;a.stop=C.stop;a.timers=L.timers;a.AST=z;a.Axis=t;a.Chart=k;a.chart=k.chart;a.Fx=L;a.Legend=d;a.PlotLineOrBand=g;a.Point=E;a.Pointer=F;a.Series=G;a.StackItem=q;a.SVGElement=u;a.SVGRenderer=v;a.Templating=H;a.Tick=e;a.Time=T;a.Tooltip=w;a.Color=U;a.color=U.parse;p.compose(v);l.compose(u);F.compose(k);d.compose(k);a.defaultOptions=I.defaultOptions;a.getOptions=I.getOptions;a.time=I.defaultTime;\na.setOptions=I.setOptions;a.dateFormat=H.dateFormat;a.format=H.format;a.numberFormat=H.numberFormat;a.addEvent=y.addEvent;a.arrayMax=y.arrayMax;a.arrayMin=y.arrayMin;a.attr=y.attr;a.clearTimeout=y.clearTimeout;a.correctFloat=y.correctFloat;a.createElement=y.createElement;a.css=y.css;a.defined=y.defined;a.destroyObjectProperties=y.destroyObjectProperties;a.discardElement=y.discardElement;a.distribute=B.distribute;a.erase=y.erase;a.error=y.error;a.extend=y.extend;a.extendClass=y.extendClass;a.find=\ny.find;a.fireEvent=y.fireEvent;a.getMagnitude=y.getMagnitude;a.getStyle=y.getStyle;a.inArray=y.inArray;a.isArray=y.isArray;a.isClass=y.isClass;a.isDOMElement=y.isDOMElement;a.isFunction=y.isFunction;a.isNumber=y.isNumber;a.isObject=y.isObject;a.isString=y.isString;a.keys=y.keys;a.merge=y.merge;a.normalizeTickInterval=y.normalizeTickInterval;a.objectEach=y.objectEach;a.offset=y.offset;a.pad=y.pad;a.pick=y.pick;a.pInt=y.pInt;a.relativeLength=y.relativeLength;a.removeEvent=y.removeEvent;a.seriesType=\nb.seriesType;a.splat=y.splat;a.stableSort=y.stableSort;a.syncTimeout=y.syncTimeout;a.timeUnits=y.timeUnits;a.uniqueKey=y.uniqueKey;a.useSerialIds=y.useSerialIds;a.wrap=y.wrap;c.compose(f);D.compose(G);m.compose(t);h.compose(t);M.compose(n);g.compose(t);K.compose(k);r.compose(t,k,G);w.compose(F);return a});a[\"masters/highcharts.src.js\"]._modules=a;return a[\"masters/highcharts.src.js\"]});\n//# sourceMappingURL=highcharts.js.map"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/jquery/jquery.md5.js",
    "content": "/*\n * jQuery MD5 Plugin 1.2.1\n * https://github.com/blueimp/jQuery-MD5\n *\n * Copyright 2010, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * http://creativecommons.org/licenses/MIT/\n *\n * Based on\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\n\n/*jslint bitwise: true */\n/*global unescape, jQuery */\n\n(function ($) {\n    'use strict';\n\n    /*\n    * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n    * to work around bugs in some JS interpreters.\n    */\n    function safe_add(x, y) {\n        var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n            msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n        return (msw << 16) | (lsw & 0xFFFF);\n    }\n\n    /*\n    * Bitwise rotate a 32-bit number to the left.\n    */\n    function bit_rol(num, cnt) {\n        return (num << cnt) | (num >>> (32 - cnt));\n    }\n\n    /*\n    * These functions implement the four basic operations the algorithm uses.\n    */\n    function md5_cmn(q, a, b, x, s, t) {\n        return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);\n    }\n    function md5_ff(a, b, c, d, x, s, t) {\n        return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);\n    }\n    function md5_gg(a, b, c, d, x, s, t) {\n        return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);\n    }\n    function md5_hh(a, b, c, d, x, s, t) {\n        return md5_cmn(b ^ c ^ d, a, b, x, s, t);\n    }\n    function md5_ii(a, b, c, d, x, s, t) {\n        return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);\n    }\n\n    /*\n    * Calculate the MD5 of an array of little-endian words, and a bit length.\n    */\n    function binl_md5(x, len) {\n        /* append padding */\n        x[len >> 5] |= 0x80 << ((len) % 32);\n        x[(((len + 64) >>> 9) << 4) + 14] = len;\n\n        var i, olda, oldb, oldc, oldd,\n            a =  1732584193,\n            b = -271733879,\n            c = -1732584194,\n            d =  271733878;\n\n        for (i = 0; i < x.length; i += 16) {\n            olda = a;\n            oldb = b;\n            oldc = c;\n            oldd = d;\n\n            a = md5_ff(a, b, c, d, x[i],       7, -680876936);\n            d = md5_ff(d, a, b, c, x[i +  1], 12, -389564586);\n            c = md5_ff(c, d, a, b, x[i +  2], 17,  606105819);\n            b = md5_ff(b, c, d, a, x[i +  3], 22, -1044525330);\n            a = md5_ff(a, b, c, d, x[i +  4],  7, -176418897);\n            d = md5_ff(d, a, b, c, x[i +  5], 12,  1200080426);\n            c = md5_ff(c, d, a, b, x[i +  6], 17, -1473231341);\n            b = md5_ff(b, c, d, a, x[i +  7], 22, -45705983);\n            a = md5_ff(a, b, c, d, x[i +  8],  7,  1770035416);\n            d = md5_ff(d, a, b, c, x[i +  9], 12, -1958414417);\n            c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);\n            b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);\n            a = md5_ff(a, b, c, d, x[i + 12],  7,  1804603682);\n            d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);\n            c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);\n            b = md5_ff(b, c, d, a, x[i + 15], 22,  1236535329);\n\n            a = md5_gg(a, b, c, d, x[i +  1],  5, -165796510);\n            d = md5_gg(d, a, b, c, x[i +  6],  9, -1069501632);\n            c = md5_gg(c, d, a, b, x[i + 11], 14,  643717713);\n            b = md5_gg(b, c, d, a, x[i],      20, -373897302);\n            a = md5_gg(a, b, c, d, x[i +  5],  5, -701558691);\n            d = md5_gg(d, a, b, c, x[i + 10],  9,  38016083);\n            c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);\n            b = md5_gg(b, c, d, a, x[i +  4], 20, -405537848);\n            a = md5_gg(a, b, c, d, x[i +  9],  5,  568446438);\n            d = md5_gg(d, a, b, c, x[i + 14],  9, -1019803690);\n            c = md5_gg(c, d, a, b, x[i +  3], 14, -187363961);\n            b = md5_gg(b, c, d, a, x[i +  8], 20,  1163531501);\n            a = md5_gg(a, b, c, d, x[i + 13],  5, -1444681467);\n            d = md5_gg(d, a, b, c, x[i +  2],  9, -51403784);\n            c = md5_gg(c, d, a, b, x[i +  7], 14,  1735328473);\n            b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);\n\n            a = md5_hh(a, b, c, d, x[i +  5],  4, -378558);\n            d = md5_hh(d, a, b, c, x[i +  8], 11, -2022574463);\n            c = md5_hh(c, d, a, b, x[i + 11], 16,  1839030562);\n            b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);\n            a = md5_hh(a, b, c, d, x[i +  1],  4, -1530992060);\n            d = md5_hh(d, a, b, c, x[i +  4], 11,  1272893353);\n            c = md5_hh(c, d, a, b, x[i +  7], 16, -155497632);\n            b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);\n            a = md5_hh(a, b, c, d, x[i + 13],  4,  681279174);\n            d = md5_hh(d, a, b, c, x[i],      11, -358537222);\n            c = md5_hh(c, d, a, b, x[i +  3], 16, -722521979);\n            b = md5_hh(b, c, d, a, x[i +  6], 23,  76029189);\n            a = md5_hh(a, b, c, d, x[i +  9],  4, -640364487);\n            d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);\n            c = md5_hh(c, d, a, b, x[i + 15], 16,  530742520);\n            b = md5_hh(b, c, d, a, x[i +  2], 23, -995338651);\n\n            a = md5_ii(a, b, c, d, x[i],       6, -198630844);\n            d = md5_ii(d, a, b, c, x[i +  7], 10,  1126891415);\n            c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);\n            b = md5_ii(b, c, d, a, x[i +  5], 21, -57434055);\n            a = md5_ii(a, b, c, d, x[i + 12],  6,  1700485571);\n            d = md5_ii(d, a, b, c, x[i +  3], 10, -1894986606);\n            c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);\n            b = md5_ii(b, c, d, a, x[i +  1], 21, -2054922799);\n            a = md5_ii(a, b, c, d, x[i +  8],  6,  1873313359);\n            d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);\n            c = md5_ii(c, d, a, b, x[i +  6], 15, -1560198380);\n            b = md5_ii(b, c, d, a, x[i + 13], 21,  1309151649);\n            a = md5_ii(a, b, c, d, x[i +  4],  6, -145523070);\n            d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);\n            c = md5_ii(c, d, a, b, x[i +  2], 15,  718787259);\n            b = md5_ii(b, c, d, a, x[i +  9], 21, -343485551);\n\n            a = safe_add(a, olda);\n            b = safe_add(b, oldb);\n            c = safe_add(c, oldc);\n            d = safe_add(d, oldd);\n        }\n        return [a, b, c, d];\n    }\n\n    /*\n    * Convert an array of little-endian words to a string\n    */\n    function binl2rstr(input) {\n        var i,\n            output = '';\n        for (i = 0; i < input.length * 32; i += 8) {\n            output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);\n        }\n        return output;\n    }\n\n    /*\n    * Convert a raw string to an array of little-endian words\n    * Characters >255 have their high-byte silently ignored.\n    */\n    function rstr2binl(input) {\n        var i,\n            output = [];\n        output[(input.length >> 2) - 1] = undefined;\n        for (i = 0; i < output.length; i += 1) {\n            output[i] = 0;\n        }\n        for (i = 0; i < input.length * 8; i += 8) {\n            output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);\n        }\n        return output;\n    }\n\n    /*\n    * Calculate the MD5 of a raw string\n    */\n    function rstr_md5(s) {\n        return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));\n    }\n\n    /*\n    * Calculate the HMAC-MD5, of a key and some data (raw strings)\n    */\n    function rstr_hmac_md5(key, data) {\n        var i,\n            bkey = rstr2binl(key),\n            ipad = [],\n            opad = [],\n            hash;\n        ipad[15] = opad[15] = undefined;\n        if (bkey.length > 16) {\n            bkey = binl_md5(bkey, key.length * 8);\n        }\n        for (i = 0; i < 16; i += 1) {\n            ipad[i] = bkey[i] ^ 0x36363636;\n            opad[i] = bkey[i] ^ 0x5C5C5C5C;\n        }\n        hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);\n        return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));\n    }\n\n    /*\n    * Convert a raw string to a hex string\n    */\n    function rstr2hex(input) {\n        var hex_tab = '0123456789abcdef',\n            output = '',\n            x,\n            i;\n        for (i = 0; i < input.length; i += 1) {\n            x = input.charCodeAt(i);\n            output += hex_tab.charAt((x >>> 4) & 0x0F) +\n                hex_tab.charAt(x & 0x0F);\n        }\n        return output;\n    }\n\n    /*\n    * Encode a string as utf-8\n    */\n    function str2rstr_utf8(input) {\n        return unescape(encodeURIComponent(input));\n    }\n\n    /*\n    * Take string arguments and return either raw or hex encoded strings\n    */\n    function raw_md5(s) {\n        return rstr_md5(str2rstr_utf8(s));\n    }\n    function hex_md5(s) {\n        return rstr2hex(raw_md5(s));\n    }\n    function raw_hmac_md5(k, d) {\n        return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d));\n    }\n    function hex_hmac_md5(k, d) {\n        return rstr2hex(raw_hmac_md5(k, d));\n    }\n\n    $.md5 = function (string, key, raw) {\n        if (!key) {\n            if (!raw) {\n                return hex_md5(string);\n            } else {\n                return raw_md5(string);\n            }\n        }\n        if (!raw) {\n            return hex_hmac_md5(key, string);\n        } else {\n            return raw_hmac_md5(key, string);\n        }\n    };\n\n}(typeof jQuery === 'function' ? jQuery : this));"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/paginator/bootstrap-paginator.js",
    "content": "/**\n * bootstrap-paginator.js v0.5\n * --\n * Copyright 2013 Yun Lai <lyonlai1984@gmail.com>\n * --\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n(function ($) {\n\n    \"use strict\"; // jshint ;_;\n\n\n    /* Paginator PUBLIC CLASS DEFINITION\n     * ================================= */\n\n    /**\n     * Boostrap Paginator Constructor\n     *\n     * @param element element of the paginator\n     * @param options the options to config the paginator\n     *\n     * */\n    var BootstrapPaginator = function (element, options) {\n        this.init(element, options);\n    },\n        old = null;\n\n    BootstrapPaginator.prototype = {\n\n        /**\n         * Initialization function of the paginator, accepting an element and the options as parameters\n         *\n         * @param element element of the paginator\n         * @param options the options to config the paginator\n         *\n         * */\n        init: function (element, options) {\n\n            this.$element = $(element);\n\n            var version = (options && options.bootstrapMajorVersion) ? options.bootstrapMajorVersion : $.fn.bootstrapPaginator.defaults.bootstrapMajorVersion,\n                id = this.$element.attr(\"id\");\n\n            if (version === 2 && !this.$element.is(\"div\")) {\n\n                throw \"in Bootstrap version 2 the pagination must be a div element. Or if you are using Bootstrap pagination 3. Please specify it in bootstrapMajorVersion in the option\";\n            } else if (version > 2 && !this.$element.is(\"ul\")) {\n                throw \"in Bootstrap version 3 the pagination root item must be an ul element.\"\n            }\n\n\n\n            this.currentPage = 1;\n\n            this.lastPage = 1;\n\n            this.setOptions(options);\n\n            this.initialized = true;\n        },\n\n        /**\n         * Update the properties of the paginator element\n         *\n         * @param options options to config the paginator\n         * */\n        setOptions: function (options) {\n\n            this.options = $.extend({}, (this.options || $.fn.bootstrapPaginator.defaults), options);\n\n            this.totalPages = parseInt(this.options.totalPages, 10);  //setup the total pages property.\n            this.numberOfPages = parseInt(this.options.numberOfPages, 10); //setup the numberOfPages to be shown\n\n            //move the set current page after the setting of total pages. otherwise it will cause out of page exception.\n            if (options && typeof (options.currentPage)  !== 'undefined') {\n\n                this.setCurrentPage(options.currentPage);\n            }\n\n            this.listen();\n\n            //render the paginator\n            this.render();\n\n            if (!this.initialized && this.lastPage !== this.currentPage) {\n                this.$element.trigger(\"page-changed\", [this.lastPage, this.currentPage]);\n            }\n\n        },\n\n        /**\n         * Sets up the events listeners. Currently the pageclicked and pagechanged events are linked if available.\n         *\n         * */\n        listen: function () {\n\n            this.$element.off(\"page-clicked\");\n\n            this.$element.off(\"page-changed\");// unload the events for the element\n\n            if (typeof (this.options.onPageClicked) === \"function\") {\n                this.$element.bind(\"page-clicked\", this.options.onPageClicked);\n            }\n\n            if (typeof (this.options.onPageChanged) === \"function\") {\n                this.$element.on(\"page-changed\", this.options.onPageChanged);\n            }\n\n            this.$element.bind(\"page-clicked\", this.onPageClicked);\n        },\n\n\n        /**\n         *\n         *  Destroys the paginator element, it unload the event first, then empty the content inside.\n         *\n         * */\n        destroy: function () {\n\n            this.$element.off(\"page-clicked\");\n\n            this.$element.off(\"page-changed\");\n\n            this.$element.removeData('bootstrapPaginator');\n\n            this.$element.empty();\n\n        },\n\n        /**\n         * Shows the page\n         *\n         * */\n        show: function (page) {\n\n            this.setCurrentPage(page);\n\n            this.render();\n\n            if (this.lastPage !== this.currentPage) {\n                this.$element.trigger(\"page-changed\", [this.lastPage, this.currentPage]);\n            }\n        },\n\n        /**\n         * Shows the next page\n         *\n         * */\n        showNext: function () {\n            var pages = this.getPages();\n\n            if (pages.next) {\n                this.show(pages.next);\n            }\n\n        },\n\n        /**\n         * Shows the previous page\n         *\n         * */\n        showPrevious: function () {\n            var pages = this.getPages();\n\n            if (pages.prev) {\n                this.show(pages.prev);\n            }\n\n        },\n\n        /**\n         * Shows the first page\n         *\n         * */\n        showFirst: function () {\n            var pages = this.getPages();\n\n            if (pages.first) {\n                this.show(pages.first);\n            }\n\n        },\n\n        /**\n         * Shows the last page\n         *\n         * */\n        showLast: function () {\n            var pages = this.getPages();\n\n            if (pages.last) {\n                this.show(pages.last);\n            }\n\n        },\n\n        /**\n         * Internal on page item click handler, when the page item is clicked, change the current page to the corresponding page and\n         * trigger the pageclick event for the listeners.\n         *\n         *\n         * */\n        onPageItemClicked: function (event) {\n\n            var type = event.data.type,\n                page = event.data.page;\n\n            this.$element.trigger(\"page-clicked\", [event, type, page]);\n\n        },\n\n        onPageClicked: function (event, originalEvent, type, page) {\n\n            //show the corresponding page and retrieve the newly built item related to the page clicked before for the event return\n\n            var currentTarget = $(event.currentTarget);\n\n            switch (type) {\n            case \"first\":\n                currentTarget.bootstrapPaginator(\"showFirst\");\n                break;\n            case \"prev\":\n                currentTarget.bootstrapPaginator(\"showPrevious\");\n                break;\n            case \"next\":\n                currentTarget.bootstrapPaginator(\"showNext\");\n                break;\n            case \"last\":\n                currentTarget.bootstrapPaginator(\"showLast\");\n                break;\n            case \"page\":\n                currentTarget.bootstrapPaginator(\"show\", page);\n                break;\n            }\n\n        },\n\n        /**\n         * Renders the paginator according to the internal properties and the settings.\n         *\n         *\n         * */\n        render: function () {\n\n            //fetch the container class and add them to the container\n            var containerClass = this.getValueFromOption(this.options.containerClass, this.$element),\n                size = this.options.size || \"normal\",\n                alignment = this.options.alignment || \"left\",\n                pages = this.getPages(),\n                listContainer = this.options.bootstrapMajorVersion === 2 ? $(\"<ul></ul>\") : this.$element,\n                listContainerClass = this.options.bootstrapMajorVersion === 2 ? this.getValueFromOption(this.options.listContainerClass, listContainer) : null,\n                first = null,\n                prev = null,\n                next = null,\n                last = null,\n                p = null,\n                i = 0;\n\n\n            this.$element.prop(\"class\", \"\");\n\n            this.$element.addClass(\"pagination\");\n\n            switch (size.toLowerCase()) {\n            case \"large\":\n            case \"small\":\n            case \"mini\":\n                this.$element.addClass($.fn.bootstrapPaginator.sizeArray[this.options.bootstrapMajorVersion][size.toLowerCase()]);\n                break;\n            default:\n                break;\n            }\n\n            if (this.options.bootstrapMajorVersion === 2) {\n                switch (alignment.toLowerCase()) {\n                case \"center\":\n                    this.$element.addClass(\"pagination-centered\");\n                    break;\n                case \"right\":\n                    this.$element.addClass(\"pagination-right\");\n                    break;\n                default:\n                    break;\n                }\n            }\n\n\n            this.$element.addClass(containerClass);\n\n            //empty the outter most container then add the listContainer inside.\n            this.$element.empty();\n\n            if (this.options.bootstrapMajorVersion === 2) {\n                this.$element.append(listContainer);\n\n                listContainer.addClass(listContainerClass);\n            }\n\n            //update the page element reference\n            this.pageRef = [];\n\n            if (pages.first) {//if the there is first page element\n                first = this.buildPageItem(\"first\", pages.first);\n\n                if (first) {\n                    listContainer.append(first);\n                }\n\n            }\n\n            if (pages.prev) {//if the there is previous page element\n\n                prev = this.buildPageItem(\"prev\", pages.prev);\n\n                if (prev) {\n                    listContainer.append(prev);\n                }\n\n            }\n\n\n            for (i = 0; i < pages.length; i = i + 1) {//fill the numeric pages.\n\n                p = this.buildPageItem(\"page\", pages[i]);\n\n                if (p) {\n                    listContainer.append(p);\n                }\n            }\n\n            if (pages.next) {//if there is next page\n\n                next = this.buildPageItem(\"next\", pages.next);\n\n                if (next) {\n                    listContainer.append(next);\n                }\n            }\n\n            if (pages.last) {//if there is last page\n\n                last = this.buildPageItem(\"last\", pages.last);\n\n                if (last) {\n                    listContainer.append(last);\n                }\n            }\n        },\n\n        /**\n         *\n         * Creates a page item base on the type and page number given.\n         *\n         * @param page page number\n         * @param type type of the page, whether it is the first, prev, page, next, last\n         *\n         * @return Object the constructed page element\n         * */\n        buildPageItem: function (type, page) {\n\n            var itemContainer = $(\"<li></li>\"),//creates the item container\n                itemContent = $(\"<a></a>\"),//creates the item content\n                text = \"\",\n                title = \"\",\n                itemContainerClass = this.options.itemContainerClass(type, page, this.currentPage),\n                itemContentClass = this.getValueFromOption(this.options.itemContentClass, type, page, this.currentPage),\n                tooltipOpts = null;\n\n\n            switch (type) {\n\n            case \"first\":\n                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }\n                text = this.options.itemTexts(type, page, this.currentPage);\n                title = this.options.tooltipTitles(type, page, this.currentPage);\n                break;\n            case \"last\":\n                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }\n                text = this.options.itemTexts(type, page, this.currentPage);\n                title = this.options.tooltipTitles(type, page, this.currentPage);\n                break;\n            case \"prev\":\n                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }\n                text = this.options.itemTexts(type, page, this.currentPage);\n                title = this.options.tooltipTitles(type, page, this.currentPage);\n                break;\n            case \"next\":\n                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }\n                text = this.options.itemTexts(type, page, this.currentPage);\n                title = this.options.tooltipTitles(type, page, this.currentPage);\n                break;\n            case \"page\":\n                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }\n                text = this.options.itemTexts(type, page, this.currentPage);\n                title = this.options.tooltipTitles(type, page, this.currentPage);\n                break;\n            }\n\n            itemContainer.addClass(itemContainerClass).append(itemContent);\n\n            itemContent.addClass(itemContentClass).html(text).on(\"click\", null, {type: type, page: page}, $.proxy(this.onPageItemClicked, this));\n\n            if (this.options.pageUrl) {\n                itemContent.attr(\"href\", this.getValueFromOption(this.options.pageUrl, type, page, this.currentPage));\n            }\n\n            if (this.options.useBootstrapTooltip) {\n                tooltipOpts = $.extend({}, this.options.bootstrapTooltipOptions, {title: title});\n\n                itemContent.tooltip(tooltipOpts);\n            } else {\n                itemContent.attr(\"title\", title);\n            }\n\n            return itemContainer;\n\n        },\n\n        setCurrentPage: function (page) {\n            if (page > this.totalPages || page < 1) {// if the current page is out of range, throw exception.\n\n                throw \"Page out of range\";\n\n            }\n\n            this.lastPage = this.currentPage;\n\n            this.currentPage = parseInt(page, 10);\n\n        },\n\n        /**\n         * Gets an array that represents the current status of the page object. Numeric pages can be access via array mode. length attributes describes how many numeric pages are there. First, previous, next and last page can be accessed via attributes first, prev, next and last. Current attribute marks the current page within the pages.\n         *\n         * @return object output objects that has first, prev, next, last and also the number of pages in between.\n         * */\n        getPages: function () {\n\n            var totalPages = this.totalPages,// get or calculate the total pages via the total records\n                pageStart = (this.currentPage % this.numberOfPages === 0) ? (parseInt(this.currentPage / this.numberOfPages, 10) - 1) * this.numberOfPages + 1 : parseInt(this.currentPage / this.numberOfPages, 10) * this.numberOfPages + 1,//calculates the start page.\n                output = [],\n                i = 0,\n                counter = 0;\n\n            pageStart = pageStart < 1 ? 1 : pageStart;//check the range of the page start to see if its less than 1.\n\n            for (i = pageStart, counter = 0; counter < this.numberOfPages && i <= totalPages; i = i + 1, counter = counter + 1) {//fill the pages\n                output.push(i);\n            }\n\n            output.first = 1;//add the first when the current page leaves the 1st page.\n\n            if (this.currentPage > 1) {// add the previous when the current page leaves the 1st page\n                output.prev = this.currentPage - 1;\n            } else {\n                output.prev = 1;\n            }\n\n            if (this.currentPage < totalPages) {// add the next page when the current page doesn't reach the last page\n                output.next = this.currentPage + 1;\n            } else {\n                output.next = totalPages;\n            }\n\n            output.last = totalPages;// add the last page when the current page doesn't reach the last page\n\n            output.current = this.currentPage;//mark the current page.\n\n            output.total = totalPages;\n\n            output.numberOfPages = this.options.numberOfPages;\n\n            return output;\n\n        },\n\n        /**\n         * Gets the value from the options, this is made to handle the situation where value is the return value of a function.\n         *\n         * @return mixed value that depends on the type of parameters, if the given parameter is a function, then the evaluated result is returned. Otherwise the parameter itself will get returned.\n         * */\n        getValueFromOption: function (value) {\n\n            var output = null,\n                args = Array.prototype.slice.call(arguments, 1);\n\n            if (typeof value === 'function') {\n                output = value.apply(this, args);\n            } else {\n                output = value;\n            }\n\n            return output;\n\n        }\n\n    };\n\n\n    /* TYPEAHEAD PLUGIN DEFINITION\n     * =========================== */\n\n    old = $.fn.bootstrapPaginator;\n\n    $.fn.bootstrapPaginator = function (option) {\n\n        var args = arguments,\n            result = null;\n\n        $(this).each(function (index, item) {\n            var $this = $(item),\n                data = $this.data('bootstrapPaginator'),\n                options = (typeof option !== 'object') ? null : option;\n\n            if (!data) {\n                data = new BootstrapPaginator(this, options);\n\n                $this = $(data.$element);\n\n                $this.data('bootstrapPaginator', data);\n\n                return;\n            }\n\n            if (typeof option === 'string') {\n\n                if (data[option]) {\n                    result = data[option].apply(data, Array.prototype.slice.call(args, 1));\n                } else {\n                    throw \"Method \" + option + \" does not exist\";\n                }\n\n            } else {\n                result = data.setOptions(option);\n            }\n        });\n\n        return result;\n\n    };\n\n    $.fn.bootstrapPaginator.sizeArray = {\n\n        \"2\": {\n            \"large\": \"pagination-large\",\n            \"small\": \"pagination-small\",\n            \"mini\": \"pagination-mini\"\n        },\n        \"3\": {\n            \"large\": \"pagination-lg\",\n            \"small\": \"pagination-sm\",\n            \"mini\": \"\"\n        }\n\n    };\n\n    $.fn.bootstrapPaginator.defaults = {\n        containerClass: \"\",\n        size: \"normal\",\n        alignment: \"left\",\n        bootstrapMajorVersion: 2,\n        listContainerClass: \"\",\n        itemContainerClass: function (type, page, current) {\n            return (page === current) ? \"active\" : \"\";\n        },\n        itemContentClass: function (type, page, current) {\n            return \"\";\n        },\n        currentPage: 1,\n        numberOfPages: 5,\n        totalPages: 1,\n        pageUrl: function (type, page, current) {\n            return null;\n        },\n        onPageClicked: null,\n        onPageChanged: null,\n        useBootstrapTooltip: false,\n        shouldShowPage: function (type, page, current) {\n\n            var result = true;\n\n            switch (type) {\n            case \"first\":\n                result = (current !== 1);\n                break;\n            case \"prev\":\n                result = (current !== 1);\n                break;\n            case \"next\":\n                result = (current !== this.totalPages);\n                break;\n            case \"last\":\n                result = (current !== this.totalPages);\n                break;\n            case \"page\":\n                result = true;\n                break;\n            }\n\n            return result;\n\n        },\n        itemTexts: function (type, page, current) {\n            switch (type) {\n            case \"first\":\n                return \"&lt;&lt;\";\n            case \"prev\":\n                return \"&lt;\";\n            case \"next\":\n                return \"&gt;\";\n            case \"last\":\n                return \"&gt;&gt;\";\n            case \"page\":\n                return page;\n            }\n        },\n        tooltipTitles: function (type, page, current) {\n\n            switch (type) {\n            case \"first\":\n                return \"Go to first page\";\n            case \"prev\":\n                return \"Go to previous page\";\n            case \"next\":\n                return \"Go to next page\";\n            case \"last\":\n                return \"Go to last page\";\n            case \"page\":\n                return (page === current) ? \"Current page is \" + page : \"Go to page \" + page;\n            }\n        },\n        bootstrapTooltipOptions: {\n            animation: true,\n            html: true,\n            placement: 'top',\n            selector: false,\n            title: \"\",\n            container: false\n        }\n    };\n\n    $.fn.bootstrapPaginator.Constructor = BootstrapPaginator;\n\n\n\n}(window.jQuery));\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/php-email-form/validate.js",
    "content": "/**\n* PHP Email Form Validation - v3.6\n* URL: https://bootstrapmade.com/php-email-form/\n* Author: BootstrapMade.com\n*/\n(function () {\n  \"use strict\";\n\n  let forms = document.querySelectorAll('.php-email-form');\n\n  forms.forEach( function(e) {\n    e.addEventListener('submit', function(event) {\n      event.preventDefault();\n\n      let thisForm = this;\n\n      let action = thisForm.getAttribute('action');\n      let recaptcha = thisForm.getAttribute('data-recaptcha-site-key');\n      \n      if( ! action ) {\n        displayError(thisForm, 'The form action property is not set!');\n        return;\n      }\n      thisForm.querySelector('.loading').classList.add('d-block');\n      thisForm.querySelector('.error-message').classList.remove('d-block');\n      thisForm.querySelector('.sent-message').classList.remove('d-block');\n\n      let formData = new FormData( thisForm );\n\n      if ( recaptcha ) {\n        if(typeof grecaptcha !== \"undefined\" ) {\n          grecaptcha.ready(function() {\n            try {\n              grecaptcha.execute(recaptcha, {action: 'php_email_form_submit'})\n              .then(token => {\n                formData.set('recaptcha-response', token);\n                php_email_form_submit(thisForm, action, formData);\n              })\n            } catch(error) {\n              displayError(thisForm, error);\n            }\n          });\n        } else {\n          displayError(thisForm, 'The reCaptcha javascript API url is not loaded!')\n        }\n      } else {\n        php_email_form_submit(thisForm, action, formData);\n      }\n    });\n  });\n\n  function php_email_form_submit(thisForm, action, formData) {\n    fetch(action, {\n      method: 'POST',\n      body: formData,\n      headers: {'X-Requested-With': 'XMLHttpRequest'}\n    })\n    .then(response => {\n      if( response.ok ) {\n        return response.text();\n      } else {\n        throw new Error(`${response.status} ${response.statusText} ${response.url}`); \n      }\n    })\n    .then(data => {\n      thisForm.querySelector('.loading').classList.remove('d-block');\n      if (data.trim() == 'OK') {\n        thisForm.querySelector('.sent-message').classList.add('d-block');\n        thisForm.reset(); \n      } else {\n        throw new Error(data ? data : 'Form submission failed and no error message returned from: ' + action); \n      }\n    })\n    .catch((error) => {\n      displayError(thisForm, error);\n    });\n  }\n\n  function displayError(thisForm, error) {\n    thisForm.querySelector('.loading').classList.remove('d-block');\n    thisForm.querySelector('.error-message').innerHTML = error;\n    thisForm.querySelector('.error-message').classList.add('d-block');\n  }\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/quill/quill.bubble.css",
    "content": "/*!\n * Quill Editor v1.3.7\n * https://quilljs.com/\n * Copyright (c) 2014, Jason Chen\n * Copyright (c) 2013, salesforce.com\n */\n.ql-container {\n  box-sizing: border-box;\n  font-family: Helvetica, Arial, sans-serif;\n  font-size: 13px;\n  height: 100%;\n  margin: 0px;\n  position: relative;\n}\n.ql-container.ql-disabled .ql-tooltip {\n  visibility: hidden;\n}\n.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {\n  pointer-events: none;\n}\n.ql-clipboard {\n  left: -100000px;\n  height: 1px;\n  overflow-y: hidden;\n  position: absolute;\n  top: 50%;\n}\n.ql-clipboard p {\n  margin: 0;\n  padding: 0;\n}\n.ql-editor {\n  box-sizing: border-box;\n  line-height: 1.42;\n  height: 100%;\n  outline: none;\n  overflow-y: auto;\n  padding: 12px 15px;\n  tab-size: 4;\n  -moz-tab-size: 4;\n  text-align: left;\n  white-space: pre-wrap;\n  word-wrap: break-word;\n}\n.ql-editor > * {\n  cursor: text;\n}\n.ql-editor p,\n.ql-editor ol,\n.ql-editor ul,\n.ql-editor pre,\n.ql-editor blockquote,\n.ql-editor h1,\n.ql-editor h2,\n.ql-editor h3,\n.ql-editor h4,\n.ql-editor h5,\n.ql-editor h6 {\n  margin: 0;\n  padding: 0;\n  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol,\n.ql-editor ul {\n  padding-left: 1.5em;\n}\n.ql-editor ol > li,\n.ql-editor ul > li {\n  list-style-type: none;\n}\n.ql-editor ul > li::before {\n  content: '\\2022';\n}\n.ql-editor ul[data-checked=true],\n.ql-editor ul[data-checked=false] {\n  pointer-events: none;\n}\n.ql-editor ul[data-checked=true] > li *,\n.ql-editor ul[data-checked=false] > li * {\n  pointer-events: all;\n}\n.ql-editor ul[data-checked=true] > li::before,\n.ql-editor ul[data-checked=false] > li::before {\n  color: #777;\n  cursor: pointer;\n  pointer-events: all;\n}\n.ql-editor ul[data-checked=true] > li::before {\n  content: '\\2611';\n}\n.ql-editor ul[data-checked=false] > li::before {\n  content: '\\2610';\n}\n.ql-editor li::before {\n  display: inline-block;\n  white-space: nowrap;\n  width: 1.2em;\n}\n.ql-editor li:not(.ql-direction-rtl)::before {\n  margin-left: -1.5em;\n  margin-right: 0.3em;\n  text-align: right;\n}\n.ql-editor li.ql-direction-rtl::before {\n  margin-left: 0.3em;\n  margin-right: -1.5em;\n}\n.ql-editor ol li:not(.ql-direction-rtl),\n.ql-editor ul li:not(.ql-direction-rtl) {\n  padding-left: 1.5em;\n}\n.ql-editor ol li.ql-direction-rtl,\n.ql-editor ul li.ql-direction-rtl {\n  padding-right: 1.5em;\n}\n.ql-editor ol li {\n  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n  counter-increment: list-0;\n}\n.ql-editor ol li:before {\n  content: counter(list-0, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-1 {\n  counter-increment: list-1;\n}\n.ql-editor ol li.ql-indent-1:before {\n  content: counter(list-1, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-1 {\n  counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-2 {\n  counter-increment: list-2;\n}\n.ql-editor ol li.ql-indent-2:before {\n  content: counter(list-2, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-2 {\n  counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-3 {\n  counter-increment: list-3;\n}\n.ql-editor ol li.ql-indent-3:before {\n  content: counter(list-3, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-3 {\n  counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-4 {\n  counter-increment: list-4;\n}\n.ql-editor ol li.ql-indent-4:before {\n  content: counter(list-4, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-4 {\n  counter-reset: list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-5 {\n  counter-increment: list-5;\n}\n.ql-editor ol li.ql-indent-5:before {\n  content: counter(list-5, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-5 {\n  counter-reset: list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-6 {\n  counter-increment: list-6;\n}\n.ql-editor ol li.ql-indent-6:before {\n  content: counter(list-6, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-6 {\n  counter-reset: list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-7 {\n  counter-increment: list-7;\n}\n.ql-editor ol li.ql-indent-7:before {\n  content: counter(list-7, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-7 {\n  counter-reset: list-8 list-9;\n}\n.ql-editor ol li.ql-indent-8 {\n  counter-increment: list-8;\n}\n.ql-editor ol li.ql-indent-8:before {\n  content: counter(list-8, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-8 {\n  counter-reset: list-9;\n}\n.ql-editor ol li.ql-indent-9 {\n  counter-increment: list-9;\n}\n.ql-editor ol li.ql-indent-9:before {\n  content: counter(list-9, decimal) '. ';\n}\n.ql-editor .ql-indent-1:not(.ql-direction-rtl) {\n  padding-left: 3em;\n}\n.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {\n  padding-left: 4.5em;\n}\n.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {\n  padding-right: 3em;\n}\n.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {\n  padding-right: 4.5em;\n}\n.ql-editor .ql-indent-2:not(.ql-direction-rtl) {\n  padding-left: 6em;\n}\n.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {\n  padding-left: 7.5em;\n}\n.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {\n  padding-right: 6em;\n}\n.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {\n  padding-right: 7.5em;\n}\n.ql-editor .ql-indent-3:not(.ql-direction-rtl) {\n  padding-left: 9em;\n}\n.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {\n  padding-left: 10.5em;\n}\n.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {\n  padding-right: 9em;\n}\n.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {\n  padding-right: 10.5em;\n}\n.ql-editor .ql-indent-4:not(.ql-direction-rtl) {\n  padding-left: 12em;\n}\n.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {\n  padding-left: 13.5em;\n}\n.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {\n  padding-right: 12em;\n}\n.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {\n  padding-right: 13.5em;\n}\n.ql-editor .ql-indent-5:not(.ql-direction-rtl) {\n  padding-left: 15em;\n}\n.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {\n  padding-left: 16.5em;\n}\n.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {\n  padding-right: 15em;\n}\n.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {\n  padding-right: 16.5em;\n}\n.ql-editor .ql-indent-6:not(.ql-direction-rtl) {\n  padding-left: 18em;\n}\n.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {\n  padding-left: 19.5em;\n}\n.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {\n  padding-right: 18em;\n}\n.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {\n  padding-right: 19.5em;\n}\n.ql-editor .ql-indent-7:not(.ql-direction-rtl) {\n  padding-left: 21em;\n}\n.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {\n  padding-left: 22.5em;\n}\n.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {\n  padding-right: 21em;\n}\n.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {\n  padding-right: 22.5em;\n}\n.ql-editor .ql-indent-8:not(.ql-direction-rtl) {\n  padding-left: 24em;\n}\n.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {\n  padding-left: 25.5em;\n}\n.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {\n  padding-right: 24em;\n}\n.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {\n  padding-right: 25.5em;\n}\n.ql-editor .ql-indent-9:not(.ql-direction-rtl) {\n  padding-left: 27em;\n}\n.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {\n  padding-left: 28.5em;\n}\n.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {\n  padding-right: 27em;\n}\n.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {\n  padding-right: 28.5em;\n}\n.ql-editor .ql-video {\n  display: block;\n  max-width: 100%;\n}\n.ql-editor .ql-video.ql-align-center {\n  margin: 0 auto;\n}\n.ql-editor .ql-video.ql-align-right {\n  margin: 0 0 0 auto;\n}\n.ql-editor .ql-bg-black {\n  background-color: #000;\n}\n.ql-editor .ql-bg-red {\n  background-color: #e60000;\n}\n.ql-editor .ql-bg-orange {\n  background-color: #f90;\n}\n.ql-editor .ql-bg-yellow {\n  background-color: #ff0;\n}\n.ql-editor .ql-bg-green {\n  background-color: #008a00;\n}\n.ql-editor .ql-bg-blue {\n  background-color: #06c;\n}\n.ql-editor .ql-bg-purple {\n  background-color: #93f;\n}\n.ql-editor .ql-color-white {\n  color: #fff;\n}\n.ql-editor .ql-color-red {\n  color: #e60000;\n}\n.ql-editor .ql-color-orange {\n  color: #f90;\n}\n.ql-editor .ql-color-yellow {\n  color: #ff0;\n}\n.ql-editor .ql-color-green {\n  color: #008a00;\n}\n.ql-editor .ql-color-blue {\n  color: #06c;\n}\n.ql-editor .ql-color-purple {\n  color: #93f;\n}\n.ql-editor .ql-font-serif {\n  font-family: Georgia, Times New Roman, serif;\n}\n.ql-editor .ql-font-monospace {\n  font-family: Monaco, Courier New, monospace;\n}\n.ql-editor .ql-size-small {\n  font-size: 0.75em;\n}\n.ql-editor .ql-size-large {\n  font-size: 1.5em;\n}\n.ql-editor .ql-size-huge {\n  font-size: 2.5em;\n}\n.ql-editor .ql-direction-rtl {\n  direction: rtl;\n  text-align: inherit;\n}\n.ql-editor .ql-align-center {\n  text-align: center;\n}\n.ql-editor .ql-align-justify {\n  text-align: justify;\n}\n.ql-editor .ql-align-right {\n  text-align: right;\n}\n.ql-editor.ql-blank::before {\n  color: rgba(0,0,0,0.6);\n  content: attr(data-placeholder);\n  font-style: italic;\n  left: 15px;\n  pointer-events: none;\n  position: absolute;\n  right: 15px;\n}\n.ql-bubble.ql-toolbar:after,\n.ql-bubble .ql-toolbar:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n.ql-bubble.ql-toolbar button,\n.ql-bubble .ql-toolbar button {\n  background: none;\n  border: none;\n  cursor: pointer;\n  display: inline-block;\n  float: left;\n  height: 24px;\n  padding: 3px 5px;\n  width: 28px;\n}\n.ql-bubble.ql-toolbar button svg,\n.ql-bubble .ql-toolbar button svg {\n  float: left;\n  height: 100%;\n}\n.ql-bubble.ql-toolbar button:active:hover,\n.ql-bubble .ql-toolbar button:active:hover {\n  outline: none;\n}\n.ql-bubble.ql-toolbar input.ql-image[type=file],\n.ql-bubble .ql-toolbar input.ql-image[type=file] {\n  display: none;\n}\n.ql-bubble.ql-toolbar button:hover,\n.ql-bubble .ql-toolbar button:hover,\n.ql-bubble.ql-toolbar button:focus,\n.ql-bubble .ql-toolbar button:focus,\n.ql-bubble.ql-toolbar button.ql-active,\n.ql-bubble .ql-toolbar button.ql-active,\n.ql-bubble.ql-toolbar .ql-picker-label:hover,\n.ql-bubble .ql-toolbar .ql-picker-label:hover,\n.ql-bubble.ql-toolbar .ql-picker-label.ql-active,\n.ql-bubble .ql-toolbar .ql-picker-label.ql-active,\n.ql-bubble.ql-toolbar .ql-picker-item:hover,\n.ql-bubble .ql-toolbar .ql-picker-item:hover,\n.ql-bubble.ql-toolbar .ql-picker-item.ql-selected,\n.ql-bubble .ql-toolbar .ql-picker-item.ql-selected {\n  color: #fff;\n}\n.ql-bubble.ql-toolbar button:hover .ql-fill,\n.ql-bubble .ql-toolbar button:hover .ql-fill,\n.ql-bubble.ql-toolbar button:focus .ql-fill,\n.ql-bubble .ql-toolbar button:focus .ql-fill,\n.ql-bubble.ql-toolbar button.ql-active .ql-fill,\n.ql-bubble .ql-toolbar button.ql-active .ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-fill,\n.ql-bubble.ql-toolbar button:hover .ql-stroke.ql-fill,\n.ql-bubble .ql-toolbar button:hover .ql-stroke.ql-fill,\n.ql-bubble.ql-toolbar button:focus .ql-stroke.ql-fill,\n.ql-bubble .ql-toolbar button:focus .ql-stroke.ql-fill,\n.ql-bubble.ql-toolbar button.ql-active .ql-stroke.ql-fill,\n.ql-bubble .ql-toolbar button.ql-active .ql-stroke.ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,\n.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,\n.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {\n  fill: #fff;\n}\n.ql-bubble.ql-toolbar button:hover .ql-stroke,\n.ql-bubble .ql-toolbar button:hover .ql-stroke,\n.ql-bubble.ql-toolbar button:focus .ql-stroke,\n.ql-bubble .ql-toolbar button:focus .ql-stroke,\n.ql-bubble.ql-toolbar button.ql-active .ql-stroke,\n.ql-bubble .ql-toolbar button.ql-active .ql-stroke,\n.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke,\n.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke,\n.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke,\n.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke,\n.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke,\n.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke,\n.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,\n.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,\n.ql-bubble.ql-toolbar button:hover .ql-stroke-miter,\n.ql-bubble .ql-toolbar button:hover .ql-stroke-miter,\n.ql-bubble.ql-toolbar button:focus .ql-stroke-miter,\n.ql-bubble .ql-toolbar button:focus .ql-stroke-miter,\n.ql-bubble.ql-toolbar button.ql-active .ql-stroke-miter,\n.ql-bubble .ql-toolbar button.ql-active .ql-stroke-miter,\n.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,\n.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,\n.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,\n.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,\n.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,\n.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,\n.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,\n.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {\n  stroke: #fff;\n}\n@media (pointer: coarse) {\n  .ql-bubble.ql-toolbar button:hover:not(.ql-active),\n  .ql-bubble .ql-toolbar button:hover:not(.ql-active) {\n    color: #ccc;\n  }\n  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-fill,\n  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-fill,\n  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,\n  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {\n    fill: #ccc;\n  }\n  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke,\n  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke,\n  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,\n  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {\n    stroke: #ccc;\n  }\n}\n.ql-bubble {\n  box-sizing: border-box;\n}\n.ql-bubble * {\n  box-sizing: border-box;\n}\n.ql-bubble .ql-hidden {\n  display: none;\n}\n.ql-bubble .ql-out-bottom,\n.ql-bubble .ql-out-top {\n  visibility: hidden;\n}\n.ql-bubble .ql-tooltip {\n  position: absolute;\n  transform: translateY(10px);\n}\n.ql-bubble .ql-tooltip a {\n  cursor: pointer;\n  text-decoration: none;\n}\n.ql-bubble .ql-tooltip.ql-flip {\n  transform: translateY(-10px);\n}\n.ql-bubble .ql-formats {\n  display: inline-block;\n  vertical-align: middle;\n}\n.ql-bubble .ql-formats:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n.ql-bubble .ql-stroke {\n  fill: none;\n  stroke: #ccc;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  stroke-width: 2;\n}\n.ql-bubble .ql-stroke-miter {\n  fill: none;\n  stroke: #ccc;\n  stroke-miterlimit: 10;\n  stroke-width: 2;\n}\n.ql-bubble .ql-fill,\n.ql-bubble .ql-stroke.ql-fill {\n  fill: #ccc;\n}\n.ql-bubble .ql-empty {\n  fill: none;\n}\n.ql-bubble .ql-even {\n  fill-rule: evenodd;\n}\n.ql-bubble .ql-thin,\n.ql-bubble .ql-stroke.ql-thin {\n  stroke-width: 1;\n}\n.ql-bubble .ql-transparent {\n  opacity: 0.4;\n}\n.ql-bubble .ql-direction svg:last-child {\n  display: none;\n}\n.ql-bubble .ql-direction.ql-active svg:last-child {\n  display: inline;\n}\n.ql-bubble .ql-direction.ql-active svg:first-child {\n  display: none;\n}\n.ql-bubble .ql-editor h1 {\n  font-size: 2em;\n}\n.ql-bubble .ql-editor h2 {\n  font-size: 1.5em;\n}\n.ql-bubble .ql-editor h3 {\n  font-size: 1.17em;\n}\n.ql-bubble .ql-editor h4 {\n  font-size: 1em;\n}\n.ql-bubble .ql-editor h5 {\n  font-size: 0.83em;\n}\n.ql-bubble .ql-editor h6 {\n  font-size: 0.67em;\n}\n.ql-bubble .ql-editor a {\n  text-decoration: underline;\n}\n.ql-bubble .ql-editor blockquote {\n  border-left: 4px solid #ccc;\n  margin-bottom: 5px;\n  margin-top: 5px;\n  padding-left: 16px;\n}\n.ql-bubble .ql-editor code,\n.ql-bubble .ql-editor pre {\n  background-color: #f0f0f0;\n  border-radius: 3px;\n}\n.ql-bubble .ql-editor pre {\n  white-space: pre-wrap;\n  margin-bottom: 5px;\n  margin-top: 5px;\n  padding: 5px 10px;\n}\n.ql-bubble .ql-editor code {\n  font-size: 85%;\n  padding: 2px 4px;\n}\n.ql-bubble .ql-editor pre.ql-syntax {\n  background-color: #23241f;\n  color: #f8f8f2;\n  overflow: visible;\n}\n.ql-bubble .ql-editor img {\n  max-width: 100%;\n}\n.ql-bubble .ql-picker {\n  color: #ccc;\n  display: inline-block;\n  float: left;\n  font-size: 14px;\n  font-weight: 500;\n  height: 24px;\n  position: relative;\n  vertical-align: middle;\n}\n.ql-bubble .ql-picker-label {\n  cursor: pointer;\n  display: inline-block;\n  height: 100%;\n  padding-left: 8px;\n  padding-right: 2px;\n  position: relative;\n  width: 100%;\n}\n.ql-bubble .ql-picker-label::before {\n  display: inline-block;\n  line-height: 22px;\n}\n.ql-bubble .ql-picker-options {\n  background-color: #444;\n  display: none;\n  min-width: 100%;\n  padding: 4px 8px;\n  position: absolute;\n  white-space: nowrap;\n}\n.ql-bubble .ql-picker-options .ql-picker-item {\n  cursor: pointer;\n  display: block;\n  padding-bottom: 5px;\n  padding-top: 5px;\n}\n.ql-bubble .ql-picker.ql-expanded .ql-picker-label {\n  color: #777;\n  z-index: 2;\n}\n.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-fill {\n  fill: #777;\n}\n.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-stroke {\n  stroke: #777;\n}\n.ql-bubble .ql-picker.ql-expanded .ql-picker-options {\n  display: block;\n  margin-top: -1px;\n  top: 100%;\n  z-index: 1;\n}\n.ql-bubble .ql-color-picker,\n.ql-bubble .ql-icon-picker {\n  width: 28px;\n}\n.ql-bubble .ql-color-picker .ql-picker-label,\n.ql-bubble .ql-icon-picker .ql-picker-label {\n  padding: 2px 4px;\n}\n.ql-bubble .ql-color-picker .ql-picker-label svg,\n.ql-bubble .ql-icon-picker .ql-picker-label svg {\n  right: 4px;\n}\n.ql-bubble .ql-icon-picker .ql-picker-options {\n  padding: 4px 0px;\n}\n.ql-bubble .ql-icon-picker .ql-picker-item {\n  height: 24px;\n  width: 24px;\n  padding: 2px 4px;\n}\n.ql-bubble .ql-color-picker .ql-picker-options {\n  padding: 3px 5px;\n  width: 152px;\n}\n.ql-bubble .ql-color-picker .ql-picker-item {\n  border: 1px solid transparent;\n  float: left;\n  height: 16px;\n  margin: 2px;\n  padding: 0px;\n  width: 16px;\n}\n.ql-bubble .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {\n  position: absolute;\n  margin-top: -9px;\n  right: 0;\n  top: 50%;\n  width: 18px;\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,\n.ql-bubble .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,\n.ql-bubble .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,\n.ql-bubble .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,\n.ql-bubble .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {\n  content: attr(data-label);\n}\n.ql-bubble .ql-picker.ql-header {\n  width: 98px;\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item::before {\n  content: 'Normal';\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"1\"]::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"1\"]::before {\n  content: 'Heading 1';\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"2\"]::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"2\"]::before {\n  content: 'Heading 2';\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"3\"]::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"3\"]::before {\n  content: 'Heading 3';\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"4\"]::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"4\"]::before {\n  content: 'Heading 4';\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"5\"]::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"5\"]::before {\n  content: 'Heading 5';\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"6\"]::before,\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"6\"]::before {\n  content: 'Heading 6';\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"1\"]::before {\n  font-size: 2em;\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"2\"]::before {\n  font-size: 1.5em;\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"3\"]::before {\n  font-size: 1.17em;\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"4\"]::before {\n  font-size: 1em;\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"5\"]::before {\n  font-size: 0.83em;\n}\n.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"6\"]::before {\n  font-size: 0.67em;\n}\n.ql-bubble .ql-picker.ql-font {\n  width: 108px;\n}\n.ql-bubble .ql-picker.ql-font .ql-picker-label::before,\n.ql-bubble .ql-picker.ql-font .ql-picker-item::before {\n  content: 'Sans Serif';\n}\n.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,\n.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {\n  content: 'Serif';\n}\n.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,\n.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {\n  content: 'Monospace';\n}\n.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {\n  font-family: Georgia, Times New Roman, serif;\n}\n.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {\n  font-family: Monaco, Courier New, monospace;\n}\n.ql-bubble .ql-picker.ql-size {\n  width: 98px;\n}\n.ql-bubble .ql-picker.ql-size .ql-picker-label::before,\n.ql-bubble .ql-picker.ql-size .ql-picker-item::before {\n  content: 'Normal';\n}\n.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=small]::before,\n.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before {\n  content: 'Small';\n}\n.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=large]::before,\n.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before {\n  content: 'Large';\n}\n.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,\n.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {\n  content: 'Huge';\n}\n.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before {\n  font-size: 10px;\n}\n.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before {\n  font-size: 18px;\n}\n.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {\n  font-size: 32px;\n}\n.ql-bubble .ql-color-picker.ql-background .ql-picker-item {\n  background-color: #fff;\n}\n.ql-bubble .ql-color-picker.ql-color .ql-picker-item {\n  background-color: #000;\n}\n.ql-bubble .ql-toolbar .ql-formats {\n  margin: 8px 12px 8px 0px;\n}\n.ql-bubble .ql-toolbar .ql-formats:first-child {\n  margin-left: 12px;\n}\n.ql-bubble .ql-color-picker svg {\n  margin: 1px;\n}\n.ql-bubble .ql-color-picker .ql-picker-item.ql-selected,\n.ql-bubble .ql-color-picker .ql-picker-item:hover {\n  border-color: #fff;\n}\n.ql-bubble .ql-tooltip {\n  background-color: #444;\n  border-radius: 25px;\n  color: #fff;\n}\n.ql-bubble .ql-tooltip-arrow {\n  border-left: 6px solid transparent;\n  border-right: 6px solid transparent;\n  content: \" \";\n  display: block;\n  left: 50%;\n  margin-left: -6px;\n  position: absolute;\n}\n.ql-bubble .ql-tooltip:not(.ql-flip) .ql-tooltip-arrow {\n  border-bottom: 6px solid #444;\n  top: -6px;\n}\n.ql-bubble .ql-tooltip.ql-flip .ql-tooltip-arrow {\n  border-top: 6px solid #444;\n  bottom: -6px;\n}\n.ql-bubble .ql-tooltip.ql-editing .ql-tooltip-editor {\n  display: block;\n}\n.ql-bubble .ql-tooltip.ql-editing .ql-formats {\n  visibility: hidden;\n}\n.ql-bubble .ql-tooltip-editor {\n  display: none;\n}\n.ql-bubble .ql-tooltip-editor input[type=text] {\n  background: transparent;\n  border: none;\n  color: #fff;\n  font-size: 13px;\n  height: 100%;\n  outline: none;\n  padding: 10px 20px;\n  position: absolute;\n  width: 100%;\n}\n.ql-bubble .ql-tooltip-editor a {\n  top: 10px;\n  position: absolute;\n  right: 20px;\n}\n.ql-bubble .ql-tooltip-editor a:before {\n  color: #ccc;\n  content: \"\\D7\";\n  font-size: 16px;\n  font-weight: bold;\n}\n.ql-container.ql-bubble:not(.ql-disabled) a {\n  position: relative;\n  white-space: nowrap;\n}\n.ql-container.ql-bubble:not(.ql-disabled) a::before {\n  background-color: #444;\n  border-radius: 15px;\n  top: -5px;\n  font-size: 12px;\n  color: #fff;\n  content: attr(href);\n  font-weight: normal;\n  overflow: hidden;\n  padding: 5px 15px;\n  text-decoration: none;\n  z-index: 1;\n}\n.ql-container.ql-bubble:not(.ql-disabled) a::after {\n  border-top: 6px solid #444;\n  border-left: 6px solid transparent;\n  border-right: 6px solid transparent;\n  top: 0;\n  content: \" \";\n  height: 0;\n  width: 0;\n}\n.ql-container.ql-bubble:not(.ql-disabled) a::before,\n.ql-container.ql-bubble:not(.ql-disabled) a::after {\n  left: 0;\n  margin-left: 50%;\n  position: absolute;\n  transform: translate(-50%, -100%);\n  transition: visibility 0s ease 200ms;\n  visibility: hidden;\n}\n.ql-container.ql-bubble:not(.ql-disabled) a:hover::before,\n.ql-container.ql-bubble:not(.ql-disabled) a:hover::after {\n  visibility: visible;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/quill/quill.core.css",
    "content": "/*!\n * Quill Editor v1.3.7\n * https://quilljs.com/\n * Copyright (c) 2014, Jason Chen\n * Copyright (c) 2013, salesforce.com\n */\n.ql-container {\n  box-sizing: border-box;\n  font-family: Helvetica, Arial, sans-serif;\n  font-size: 13px;\n  height: 100%;\n  margin: 0px;\n  position: relative;\n}\n.ql-container.ql-disabled .ql-tooltip {\n  visibility: hidden;\n}\n.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {\n  pointer-events: none;\n}\n.ql-clipboard {\n  left: -100000px;\n  height: 1px;\n  overflow-y: hidden;\n  position: absolute;\n  top: 50%;\n}\n.ql-clipboard p {\n  margin: 0;\n  padding: 0;\n}\n.ql-editor {\n  box-sizing: border-box;\n  line-height: 1.42;\n  height: 100%;\n  outline: none;\n  overflow-y: auto;\n  padding: 12px 15px;\n  tab-size: 4;\n  -moz-tab-size: 4;\n  text-align: left;\n  white-space: pre-wrap;\n  word-wrap: break-word;\n}\n.ql-editor > * {\n  cursor: text;\n}\n.ql-editor p,\n.ql-editor ol,\n.ql-editor ul,\n.ql-editor pre,\n.ql-editor blockquote,\n.ql-editor h1,\n.ql-editor h2,\n.ql-editor h3,\n.ql-editor h4,\n.ql-editor h5,\n.ql-editor h6 {\n  margin: 0;\n  padding: 0;\n  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol,\n.ql-editor ul {\n  padding-left: 1.5em;\n}\n.ql-editor ol > li,\n.ql-editor ul > li {\n  list-style-type: none;\n}\n.ql-editor ul > li::before {\n  content: '\\2022';\n}\n.ql-editor ul[data-checked=true],\n.ql-editor ul[data-checked=false] {\n  pointer-events: none;\n}\n.ql-editor ul[data-checked=true] > li *,\n.ql-editor ul[data-checked=false] > li * {\n  pointer-events: all;\n}\n.ql-editor ul[data-checked=true] > li::before,\n.ql-editor ul[data-checked=false] > li::before {\n  color: #777;\n  cursor: pointer;\n  pointer-events: all;\n}\n.ql-editor ul[data-checked=true] > li::before {\n  content: '\\2611';\n}\n.ql-editor ul[data-checked=false] > li::before {\n  content: '\\2610';\n}\n.ql-editor li::before {\n  display: inline-block;\n  white-space: nowrap;\n  width: 1.2em;\n}\n.ql-editor li:not(.ql-direction-rtl)::before {\n  margin-left: -1.5em;\n  margin-right: 0.3em;\n  text-align: right;\n}\n.ql-editor li.ql-direction-rtl::before {\n  margin-left: 0.3em;\n  margin-right: -1.5em;\n}\n.ql-editor ol li:not(.ql-direction-rtl),\n.ql-editor ul li:not(.ql-direction-rtl) {\n  padding-left: 1.5em;\n}\n.ql-editor ol li.ql-direction-rtl,\n.ql-editor ul li.ql-direction-rtl {\n  padding-right: 1.5em;\n}\n.ql-editor ol li {\n  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n  counter-increment: list-0;\n}\n.ql-editor ol li:before {\n  content: counter(list-0, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-1 {\n  counter-increment: list-1;\n}\n.ql-editor ol li.ql-indent-1:before {\n  content: counter(list-1, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-1 {\n  counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-2 {\n  counter-increment: list-2;\n}\n.ql-editor ol li.ql-indent-2:before {\n  content: counter(list-2, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-2 {\n  counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-3 {\n  counter-increment: list-3;\n}\n.ql-editor ol li.ql-indent-3:before {\n  content: counter(list-3, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-3 {\n  counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-4 {\n  counter-increment: list-4;\n}\n.ql-editor ol li.ql-indent-4:before {\n  content: counter(list-4, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-4 {\n  counter-reset: list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-5 {\n  counter-increment: list-5;\n}\n.ql-editor ol li.ql-indent-5:before {\n  content: counter(list-5, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-5 {\n  counter-reset: list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-6 {\n  counter-increment: list-6;\n}\n.ql-editor ol li.ql-indent-6:before {\n  content: counter(list-6, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-6 {\n  counter-reset: list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-7 {\n  counter-increment: list-7;\n}\n.ql-editor ol li.ql-indent-7:before {\n  content: counter(list-7, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-7 {\n  counter-reset: list-8 list-9;\n}\n.ql-editor ol li.ql-indent-8 {\n  counter-increment: list-8;\n}\n.ql-editor ol li.ql-indent-8:before {\n  content: counter(list-8, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-8 {\n  counter-reset: list-9;\n}\n.ql-editor ol li.ql-indent-9 {\n  counter-increment: list-9;\n}\n.ql-editor ol li.ql-indent-9:before {\n  content: counter(list-9, decimal) '. ';\n}\n.ql-editor .ql-indent-1:not(.ql-direction-rtl) {\n  padding-left: 3em;\n}\n.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {\n  padding-left: 4.5em;\n}\n.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {\n  padding-right: 3em;\n}\n.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {\n  padding-right: 4.5em;\n}\n.ql-editor .ql-indent-2:not(.ql-direction-rtl) {\n  padding-left: 6em;\n}\n.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {\n  padding-left: 7.5em;\n}\n.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {\n  padding-right: 6em;\n}\n.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {\n  padding-right: 7.5em;\n}\n.ql-editor .ql-indent-3:not(.ql-direction-rtl) {\n  padding-left: 9em;\n}\n.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {\n  padding-left: 10.5em;\n}\n.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {\n  padding-right: 9em;\n}\n.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {\n  padding-right: 10.5em;\n}\n.ql-editor .ql-indent-4:not(.ql-direction-rtl) {\n  padding-left: 12em;\n}\n.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {\n  padding-left: 13.5em;\n}\n.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {\n  padding-right: 12em;\n}\n.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {\n  padding-right: 13.5em;\n}\n.ql-editor .ql-indent-5:not(.ql-direction-rtl) {\n  padding-left: 15em;\n}\n.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {\n  padding-left: 16.5em;\n}\n.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {\n  padding-right: 15em;\n}\n.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {\n  padding-right: 16.5em;\n}\n.ql-editor .ql-indent-6:not(.ql-direction-rtl) {\n  padding-left: 18em;\n}\n.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {\n  padding-left: 19.5em;\n}\n.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {\n  padding-right: 18em;\n}\n.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {\n  padding-right: 19.5em;\n}\n.ql-editor .ql-indent-7:not(.ql-direction-rtl) {\n  padding-left: 21em;\n}\n.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {\n  padding-left: 22.5em;\n}\n.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {\n  padding-right: 21em;\n}\n.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {\n  padding-right: 22.5em;\n}\n.ql-editor .ql-indent-8:not(.ql-direction-rtl) {\n  padding-left: 24em;\n}\n.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {\n  padding-left: 25.5em;\n}\n.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {\n  padding-right: 24em;\n}\n.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {\n  padding-right: 25.5em;\n}\n.ql-editor .ql-indent-9:not(.ql-direction-rtl) {\n  padding-left: 27em;\n}\n.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {\n  padding-left: 28.5em;\n}\n.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {\n  padding-right: 27em;\n}\n.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {\n  padding-right: 28.5em;\n}\n.ql-editor .ql-video {\n  display: block;\n  max-width: 100%;\n}\n.ql-editor .ql-video.ql-align-center {\n  margin: 0 auto;\n}\n.ql-editor .ql-video.ql-align-right {\n  margin: 0 0 0 auto;\n}\n.ql-editor .ql-bg-black {\n  background-color: #000;\n}\n.ql-editor .ql-bg-red {\n  background-color: #e60000;\n}\n.ql-editor .ql-bg-orange {\n  background-color: #f90;\n}\n.ql-editor .ql-bg-yellow {\n  background-color: #ff0;\n}\n.ql-editor .ql-bg-green {\n  background-color: #008a00;\n}\n.ql-editor .ql-bg-blue {\n  background-color: #06c;\n}\n.ql-editor .ql-bg-purple {\n  background-color: #93f;\n}\n.ql-editor .ql-color-white {\n  color: #fff;\n}\n.ql-editor .ql-color-red {\n  color: #e60000;\n}\n.ql-editor .ql-color-orange {\n  color: #f90;\n}\n.ql-editor .ql-color-yellow {\n  color: #ff0;\n}\n.ql-editor .ql-color-green {\n  color: #008a00;\n}\n.ql-editor .ql-color-blue {\n  color: #06c;\n}\n.ql-editor .ql-color-purple {\n  color: #93f;\n}\n.ql-editor .ql-font-serif {\n  font-family: Georgia, Times New Roman, serif;\n}\n.ql-editor .ql-font-monospace {\n  font-family: Monaco, Courier New, monospace;\n}\n.ql-editor .ql-size-small {\n  font-size: 0.75em;\n}\n.ql-editor .ql-size-large {\n  font-size: 1.5em;\n}\n.ql-editor .ql-size-huge {\n  font-size: 2.5em;\n}\n.ql-editor .ql-direction-rtl {\n  direction: rtl;\n  text-align: inherit;\n}\n.ql-editor .ql-align-center {\n  text-align: center;\n}\n.ql-editor .ql-align-justify {\n  text-align: justify;\n}\n.ql-editor .ql-align-right {\n  text-align: right;\n}\n.ql-editor.ql-blank::before {\n  color: rgba(0,0,0,0.6);\n  content: attr(data-placeholder);\n  font-style: italic;\n  left: 15px;\n  pointer-events: none;\n  position: absolute;\n  right: 15px;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/quill/quill.core.js",
    "content": "/*!\n * Quill Editor v1.3.7\n * https://quilljs.com/\n * Copyright (c) 2014, Jason Chen\n * Copyright (c) 2013, salesforce.com\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Quill\"] = factory();\n\telse\n\t\troot[\"Quill\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 110);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = __webpack_require__(17);\nvar format_1 = __webpack_require__(18);\nvar leaf_1 = __webpack_require__(19);\nvar scroll_1 = __webpack_require__(45);\nvar inline_1 = __webpack_require__(46);\nvar block_1 = __webpack_require__(47);\nvar embed_1 = __webpack_require__(48);\nvar text_1 = __webpack_require__(49);\nvar attributor_1 = __webpack_require__(12);\nvar class_1 = __webpack_require__(32);\nvar style_1 = __webpack_require__(33);\nvar store_1 = __webpack_require__(31);\nvar Registry = __webpack_require__(1);\nvar Parchment = {\n    Scope: Registry.Scope,\n    create: Registry.create,\n    find: Registry.find,\n    query: Registry.query,\n    register: Registry.register,\n    Container: container_1.default,\n    Format: format_1.default,\n    Leaf: leaf_1.default,\n    Embed: embed_1.default,\n    Scroll: scroll_1.default,\n    Block: block_1.default,\n    Inline: inline_1.default,\n    Text: text_1.default,\n    Attributor: {\n        Attribute: attributor_1.default,\n        Class: class_1.default,\n        Style: style_1.default,\n        Store: store_1.default,\n    },\n};\nexports.default = Parchment;\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar ParchmentError = /** @class */ (function (_super) {\n    __extends(ParchmentError, _super);\n    function ParchmentError(message) {\n        var _this = this;\n        message = '[Parchment] ' + message;\n        _this = _super.call(this, message) || this;\n        _this.message = message;\n        _this.name = _this.constructor.name;\n        return _this;\n    }\n    return ParchmentError;\n}(Error));\nexports.ParchmentError = ParchmentError;\nvar attributes = {};\nvar classes = {};\nvar tags = {};\nvar types = {};\nexports.DATA_KEY = '__blot';\nvar Scope;\n(function (Scope) {\n    Scope[Scope[\"TYPE\"] = 3] = \"TYPE\";\n    Scope[Scope[\"LEVEL\"] = 12] = \"LEVEL\";\n    Scope[Scope[\"ATTRIBUTE\"] = 13] = \"ATTRIBUTE\";\n    Scope[Scope[\"BLOT\"] = 14] = \"BLOT\";\n    Scope[Scope[\"INLINE\"] = 7] = \"INLINE\";\n    Scope[Scope[\"BLOCK\"] = 11] = \"BLOCK\";\n    Scope[Scope[\"BLOCK_BLOT\"] = 10] = \"BLOCK_BLOT\";\n    Scope[Scope[\"INLINE_BLOT\"] = 6] = \"INLINE_BLOT\";\n    Scope[Scope[\"BLOCK_ATTRIBUTE\"] = 9] = \"BLOCK_ATTRIBUTE\";\n    Scope[Scope[\"INLINE_ATTRIBUTE\"] = 5] = \"INLINE_ATTRIBUTE\";\n    Scope[Scope[\"ANY\"] = 15] = \"ANY\";\n})(Scope = exports.Scope || (exports.Scope = {}));\nfunction create(input, value) {\n    var match = query(input);\n    if (match == null) {\n        throw new ParchmentError(\"Unable to create \" + input + \" blot\");\n    }\n    var BlotClass = match;\n    var node = \n    // @ts-ignore\n    input instanceof Node || input['nodeType'] === Node.TEXT_NODE ? input : BlotClass.create(value);\n    return new BlotClass(node, value);\n}\nexports.create = create;\nfunction find(node, bubble) {\n    if (bubble === void 0) { bubble = false; }\n    if (node == null)\n        return null;\n    // @ts-ignore\n    if (node[exports.DATA_KEY] != null)\n        return node[exports.DATA_KEY].blot;\n    if (bubble)\n        return find(node.parentNode, bubble);\n    return null;\n}\nexports.find = find;\nfunction query(query, scope) {\n    if (scope === void 0) { scope = Scope.ANY; }\n    var match;\n    if (typeof query === 'string') {\n        match = types[query] || attributes[query];\n        // @ts-ignore\n    }\n    else if (query instanceof Text || query['nodeType'] === Node.TEXT_NODE) {\n        match = types['text'];\n    }\n    else if (typeof query === 'number') {\n        if (query & Scope.LEVEL & Scope.BLOCK) {\n            match = types['block'];\n        }\n        else if (query & Scope.LEVEL & Scope.INLINE) {\n            match = types['inline'];\n        }\n    }\n    else if (query instanceof HTMLElement) {\n        var names = (query.getAttribute('class') || '').split(/\\s+/);\n        for (var i in names) {\n            match = classes[names[i]];\n            if (match)\n                break;\n        }\n        match = match || tags[query.tagName];\n    }\n    if (match == null)\n        return null;\n    // @ts-ignore\n    if (scope & Scope.LEVEL & match.scope && scope & Scope.TYPE & match.scope)\n        return match;\n    return null;\n}\nexports.query = query;\nfunction register() {\n    var Definitions = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        Definitions[_i] = arguments[_i];\n    }\n    if (Definitions.length > 1) {\n        return Definitions.map(function (d) {\n            return register(d);\n        });\n    }\n    var Definition = Definitions[0];\n    if (typeof Definition.blotName !== 'string' && typeof Definition.attrName !== 'string') {\n        throw new ParchmentError('Invalid definition');\n    }\n    else if (Definition.blotName === 'abstract') {\n        throw new ParchmentError('Cannot register abstract class');\n    }\n    types[Definition.blotName || Definition.attrName] = Definition;\n    if (typeof Definition.keyName === 'string') {\n        attributes[Definition.keyName] = Definition;\n    }\n    else {\n        if (Definition.className != null) {\n            classes[Definition.className] = Definition;\n        }\n        if (Definition.tagName != null) {\n            if (Array.isArray(Definition.tagName)) {\n                Definition.tagName = Definition.tagName.map(function (tagName) {\n                    return tagName.toUpperCase();\n                });\n            }\n            else {\n                Definition.tagName = Definition.tagName.toUpperCase();\n            }\n            var tagNames = Array.isArray(Definition.tagName) ? Definition.tagName : [Definition.tagName];\n            tagNames.forEach(function (tag) {\n                if (tags[tag] == null || Definition.className == null) {\n                    tags[tag] = Definition;\n                }\n            });\n        }\n    }\n    return Definition;\n}\nexports.register = register;\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar diff = __webpack_require__(51);\nvar equal = __webpack_require__(11);\nvar extend = __webpack_require__(3);\nvar op = __webpack_require__(20);\n\n\nvar NULL_CHARACTER = String.fromCharCode(0);  // Placeholder char for embed in diff()\n\n\nvar Delta = function (ops) {\n  // Assume we are given a well formed ops\n  if (Array.isArray(ops)) {\n    this.ops = ops;\n  } else if (ops != null && Array.isArray(ops.ops)) {\n    this.ops = ops.ops;\n  } else {\n    this.ops = [];\n  }\n};\n\n\nDelta.prototype.insert = function (text, attributes) {\n  var newOp = {};\n  if (text.length === 0) return this;\n  newOp.insert = text;\n  if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n    newOp.attributes = attributes;\n  }\n  return this.push(newOp);\n};\n\nDelta.prototype['delete'] = function (length) {\n  if (length <= 0) return this;\n  return this.push({ 'delete': length });\n};\n\nDelta.prototype.retain = function (length, attributes) {\n  if (length <= 0) return this;\n  var newOp = { retain: length };\n  if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n    newOp.attributes = attributes;\n  }\n  return this.push(newOp);\n};\n\nDelta.prototype.push = function (newOp) {\n  var index = this.ops.length;\n  var lastOp = this.ops[index - 1];\n  newOp = extend(true, {}, newOp);\n  if (typeof lastOp === 'object') {\n    if (typeof newOp['delete'] === 'number' && typeof lastOp['delete'] === 'number') {\n      this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] };\n      return this;\n    }\n    // Since it does not matter if we insert before or after deleting at the same index,\n    // always prefer to insert first\n    if (typeof lastOp['delete'] === 'number' && newOp.insert != null) {\n      index -= 1;\n      lastOp = this.ops[index - 1];\n      if (typeof lastOp !== 'object') {\n        this.ops.unshift(newOp);\n        return this;\n      }\n    }\n    if (equal(newOp.attributes, lastOp.attributes)) {\n      if (typeof newOp.insert === 'string' && typeof lastOp.insert === 'string') {\n        this.ops[index - 1] = { insert: lastOp.insert + newOp.insert };\n        if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n        return this;\n      } else if (typeof newOp.retain === 'number' && typeof lastOp.retain === 'number') {\n        this.ops[index - 1] = { retain: lastOp.retain + newOp.retain };\n        if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n        return this;\n      }\n    }\n  }\n  if (index === this.ops.length) {\n    this.ops.push(newOp);\n  } else {\n    this.ops.splice(index, 0, newOp);\n  }\n  return this;\n};\n\nDelta.prototype.chop = function () {\n  var lastOp = this.ops[this.ops.length - 1];\n  if (lastOp && lastOp.retain && !lastOp.attributes) {\n    this.ops.pop();\n  }\n  return this;\n};\n\nDelta.prototype.filter = function (predicate) {\n  return this.ops.filter(predicate);\n};\n\nDelta.prototype.forEach = function (predicate) {\n  this.ops.forEach(predicate);\n};\n\nDelta.prototype.map = function (predicate) {\n  return this.ops.map(predicate);\n};\n\nDelta.prototype.partition = function (predicate) {\n  var passed = [], failed = [];\n  this.forEach(function(op) {\n    var target = predicate(op) ? passed : failed;\n    target.push(op);\n  });\n  return [passed, failed];\n};\n\nDelta.prototype.reduce = function (predicate, initial) {\n  return this.ops.reduce(predicate, initial);\n};\n\nDelta.prototype.changeLength = function () {\n  return this.reduce(function (length, elem) {\n    if (elem.insert) {\n      return length + op.length(elem);\n    } else if (elem.delete) {\n      return length - elem.delete;\n    }\n    return length;\n  }, 0);\n};\n\nDelta.prototype.length = function () {\n  return this.reduce(function (length, elem) {\n    return length + op.length(elem);\n  }, 0);\n};\n\nDelta.prototype.slice = function (start, end) {\n  start = start || 0;\n  if (typeof end !== 'number') end = Infinity;\n  var ops = [];\n  var iter = op.iterator(this.ops);\n  var index = 0;\n  while (index < end && iter.hasNext()) {\n    var nextOp;\n    if (index < start) {\n      nextOp = iter.next(start - index);\n    } else {\n      nextOp = iter.next(end - index);\n      ops.push(nextOp);\n    }\n    index += op.length(nextOp);\n  }\n  return new Delta(ops);\n};\n\n\nDelta.prototype.compose = function (other) {\n  var thisIter = op.iterator(this.ops);\n  var otherIter = op.iterator(other.ops);\n  var ops = [];\n  var firstOther = otherIter.peek();\n  if (firstOther != null && typeof firstOther.retain === 'number' && firstOther.attributes == null) {\n    var firstLeft = firstOther.retain;\n    while (thisIter.peekType() === 'insert' && thisIter.peekLength() <= firstLeft) {\n      firstLeft -= thisIter.peekLength();\n      ops.push(thisIter.next());\n    }\n    if (firstOther.retain - firstLeft > 0) {\n      otherIter.next(firstOther.retain - firstLeft);\n    }\n  }\n  var delta = new Delta(ops);\n  while (thisIter.hasNext() || otherIter.hasNext()) {\n    if (otherIter.peekType() === 'insert') {\n      delta.push(otherIter.next());\n    } else if (thisIter.peekType() === 'delete') {\n      delta.push(thisIter.next());\n    } else {\n      var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n      var thisOp = thisIter.next(length);\n      var otherOp = otherIter.next(length);\n      if (typeof otherOp.retain === 'number') {\n        var newOp = {};\n        if (typeof thisOp.retain === 'number') {\n          newOp.retain = length;\n        } else {\n          newOp.insert = thisOp.insert;\n        }\n        // Preserve null when composing with a retain, otherwise remove it for inserts\n        var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number');\n        if (attributes) newOp.attributes = attributes;\n        delta.push(newOp);\n\n        // Optimization if rest of other is just retain\n        if (!otherIter.hasNext() && equal(delta.ops[delta.ops.length - 1], newOp)) {\n          var rest = new Delta(thisIter.rest());\n          return delta.concat(rest).chop();\n        }\n\n      // Other op should be delete, we could be an insert or retain\n      // Insert + delete cancels out\n      } else if (typeof otherOp['delete'] === 'number' && typeof thisOp.retain === 'number') {\n        delta.push(otherOp);\n      }\n    }\n  }\n  return delta.chop();\n};\n\nDelta.prototype.concat = function (other) {\n  var delta = new Delta(this.ops.slice());\n  if (other.ops.length > 0) {\n    delta.push(other.ops[0]);\n    delta.ops = delta.ops.concat(other.ops.slice(1));\n  }\n  return delta;\n};\n\nDelta.prototype.diff = function (other, index) {\n  if (this.ops === other.ops) {\n    return new Delta();\n  }\n  var strings = [this, other].map(function (delta) {\n    return delta.map(function (op) {\n      if (op.insert != null) {\n        return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER;\n      }\n      var prep = (delta === other) ? 'on' : 'with';\n      throw new Error('diff() called ' + prep + ' non-document');\n    }).join('');\n  });\n  var delta = new Delta();\n  var diffResult = diff(strings[0], strings[1], index);\n  var thisIter = op.iterator(this.ops);\n  var otherIter = op.iterator(other.ops);\n  diffResult.forEach(function (component) {\n    var length = component[1].length;\n    while (length > 0) {\n      var opLength = 0;\n      switch (component[0]) {\n        case diff.INSERT:\n          opLength = Math.min(otherIter.peekLength(), length);\n          delta.push(otherIter.next(opLength));\n          break;\n        case diff.DELETE:\n          opLength = Math.min(length, thisIter.peekLength());\n          thisIter.next(opLength);\n          delta['delete'](opLength);\n          break;\n        case diff.EQUAL:\n          opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length);\n          var thisOp = thisIter.next(opLength);\n          var otherOp = otherIter.next(opLength);\n          if (equal(thisOp.insert, otherOp.insert)) {\n            delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes));\n          } else {\n            delta.push(otherOp)['delete'](opLength);\n          }\n          break;\n      }\n      length -= opLength;\n    }\n  });\n  return delta.chop();\n};\n\nDelta.prototype.eachLine = function (predicate, newline) {\n  newline = newline || '\\n';\n  var iter = op.iterator(this.ops);\n  var line = new Delta();\n  var i = 0;\n  while (iter.hasNext()) {\n    if (iter.peekType() !== 'insert') return;\n    var thisOp = iter.peek();\n    var start = op.length(thisOp) - iter.peekLength();\n    var index = typeof thisOp.insert === 'string' ?\n      thisOp.insert.indexOf(newline, start) - start : -1;\n    if (index < 0) {\n      line.push(iter.next());\n    } else if (index > 0) {\n      line.push(iter.next(index));\n    } else {\n      if (predicate(line, iter.next(1).attributes || {}, i) === false) {\n        return;\n      }\n      i += 1;\n      line = new Delta();\n    }\n  }\n  if (line.length() > 0) {\n    predicate(line, {}, i);\n  }\n};\n\nDelta.prototype.transform = function (other, priority) {\n  priority = !!priority;\n  if (typeof other === 'number') {\n    return this.transformPosition(other, priority);\n  }\n  var thisIter = op.iterator(this.ops);\n  var otherIter = op.iterator(other.ops);\n  var delta = new Delta();\n  while (thisIter.hasNext() || otherIter.hasNext()) {\n    if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) {\n      delta.retain(op.length(thisIter.next()));\n    } else if (otherIter.peekType() === 'insert') {\n      delta.push(otherIter.next());\n    } else {\n      var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n      var thisOp = thisIter.next(length);\n      var otherOp = otherIter.next(length);\n      if (thisOp['delete']) {\n        // Our delete either makes their delete redundant or removes their retain\n        continue;\n      } else if (otherOp['delete']) {\n        delta.push(otherOp);\n      } else {\n        // We retain either their retain or insert\n        delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority));\n      }\n    }\n  }\n  return delta.chop();\n};\n\nDelta.prototype.transformPosition = function (index, priority) {\n  priority = !!priority;\n  var thisIter = op.iterator(this.ops);\n  var offset = 0;\n  while (thisIter.hasNext() && offset <= index) {\n    var length = thisIter.peekLength();\n    var nextType = thisIter.peekType();\n    thisIter.next();\n    if (nextType === 'delete') {\n      index -= Math.min(length, index - offset);\n      continue;\n    } else if (nextType === 'insert' && (offset < index || !priority)) {\n      index += length;\n    }\n    offset += length;\n  }\n  return index;\n};\n\n\nmodule.exports = Delta;\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports) {\n\n'use strict';\n\nvar hasOwn = Object.prototype.hasOwnProperty;\nvar toStr = Object.prototype.toString;\nvar defineProperty = Object.defineProperty;\nvar gOPD = Object.getOwnPropertyDescriptor;\n\nvar isArray = function isArray(arr) {\n\tif (typeof Array.isArray === 'function') {\n\t\treturn Array.isArray(arr);\n\t}\n\n\treturn toStr.call(arr) === '[object Array]';\n};\n\nvar isPlainObject = function isPlainObject(obj) {\n\tif (!obj || toStr.call(obj) !== '[object Object]') {\n\t\treturn false;\n\t}\n\n\tvar hasOwnConstructor = hasOwn.call(obj, 'constructor');\n\tvar hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');\n\t// Not own constructor property must be Object\n\tif (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {\n\t\treturn false;\n\t}\n\n\t// Own properties are enumerated firstly, so to speed up,\n\t// if last one is own, then all properties are own.\n\tvar key;\n\tfor (key in obj) { /**/ }\n\n\treturn typeof key === 'undefined' || hasOwn.call(obj, key);\n};\n\n// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target\nvar setProperty = function setProperty(target, options) {\n\tif (defineProperty && options.name === '__proto__') {\n\t\tdefineProperty(target, options.name, {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\t\t\tvalue: options.newValue,\n\t\t\twritable: true\n\t\t});\n\t} else {\n\t\ttarget[options.name] = options.newValue;\n\t}\n};\n\n// Return undefined instead of __proto__ if '__proto__' is not an own property\nvar getProperty = function getProperty(obj, name) {\n\tif (name === '__proto__') {\n\t\tif (!hasOwn.call(obj, name)) {\n\t\t\treturn void 0;\n\t\t} else if (gOPD) {\n\t\t\t// In early versions of node, obj['__proto__'] is buggy when obj has\n\t\t\t// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.\n\t\t\treturn gOPD(obj, name).value;\n\t\t}\n\t}\n\n\treturn obj[name];\n};\n\nmodule.exports = function extend() {\n\tvar options, name, src, copy, copyIsArray, clone;\n\tvar target = arguments[0];\n\tvar i = 1;\n\tvar length = arguments.length;\n\tvar deep = false;\n\n\t// Handle a deep copy situation\n\tif (typeof target === 'boolean') {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\tif (target == null || (typeof target !== 'object' && typeof target !== 'function')) {\n\t\ttarget = {};\n\t}\n\n\tfor (; i < length; ++i) {\n\t\toptions = arguments[i];\n\t\t// Only deal with non-null/undefined values\n\t\tif (options != null) {\n\t\t\t// Extend the base object\n\t\t\tfor (name in options) {\n\t\t\t\tsrc = getProperty(target, name);\n\t\t\t\tcopy = getProperty(options, name);\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif (target !== copy) {\n\t\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\t\tif (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {\n\t\t\t\t\t\tif (copyIsArray) {\n\t\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\t\tclone = src && isArray(src) ? src : [];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tclone = src && isPlainObject(src) ? src : {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\t\tsetProperty(target, { name: name, newValue: extend(deep, clone, copy) });\n\n\t\t\t\t\t// Don't bring in undefined values\n\t\t\t\t\t} else if (typeof copy !== 'undefined') {\n\t\t\t\t\t\tsetProperty(target, { name: name, newValue: copy });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.BlockEmbed = exports.bubbleFormats = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar NEWLINE_LENGTH = 1;\n\nvar BlockEmbed = function (_Parchment$Embed) {\n  _inherits(BlockEmbed, _Parchment$Embed);\n\n  function BlockEmbed() {\n    _classCallCheck(this, BlockEmbed);\n\n    return _possibleConstructorReturn(this, (BlockEmbed.__proto__ || Object.getPrototypeOf(BlockEmbed)).apply(this, arguments));\n  }\n\n  _createClass(BlockEmbed, [{\n    key: 'attach',\n    value: function attach() {\n      _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'attach', this).call(this);\n      this.attributes = new _parchment2.default.Attributor.Store(this.domNode);\n    }\n  }, {\n    key: 'delta',\n    value: function delta() {\n      return new _quillDelta2.default().insert(this.value(), (0, _extend2.default)(this.formats(), this.attributes.values()));\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      var attribute = _parchment2.default.query(name, _parchment2.default.Scope.BLOCK_ATTRIBUTE);\n      if (attribute != null) {\n        this.attributes.attribute(attribute, value);\n      }\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      this.format(name, value);\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (typeof value === 'string' && value.endsWith('\\n')) {\n        var block = _parchment2.default.create(Block.blotName);\n        this.parent.insertBefore(block, index === 0 ? this : this.next);\n        block.insertAt(0, value.slice(0, -1));\n      } else {\n        _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'insertAt', this).call(this, index, value, def);\n      }\n    }\n  }]);\n\n  return BlockEmbed;\n}(_parchment2.default.Embed);\n\nBlockEmbed.scope = _parchment2.default.Scope.BLOCK_BLOT;\n// It is important for cursor behavior BlockEmbeds use tags that are block level elements\n\n\nvar Block = function (_Parchment$Block) {\n  _inherits(Block, _Parchment$Block);\n\n  function Block(domNode) {\n    _classCallCheck(this, Block);\n\n    var _this2 = _possibleConstructorReturn(this, (Block.__proto__ || Object.getPrototypeOf(Block)).call(this, domNode));\n\n    _this2.cache = {};\n    return _this2;\n  }\n\n  _createClass(Block, [{\n    key: 'delta',\n    value: function delta() {\n      if (this.cache.delta == null) {\n        this.cache.delta = this.descendants(_parchment2.default.Leaf).reduce(function (delta, leaf) {\n          if (leaf.length() === 0) {\n            return delta;\n          } else {\n            return delta.insert(leaf.value(), bubbleFormats(leaf));\n          }\n        }, new _quillDelta2.default()).insert('\\n', bubbleFormats(this));\n      }\n      return this.cache.delta;\n    }\n  }, {\n    key: 'deleteAt',\n    value: function deleteAt(index, length) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'deleteAt', this).call(this, index, length);\n      this.cache = {};\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      if (length <= 0) return;\n      if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) {\n        if (index + length === this.length()) {\n          this.format(name, value);\n        }\n      } else {\n        _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'formatAt', this).call(this, index, Math.min(length, this.length() - index - 1), name, value);\n      }\n      this.cache = {};\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (def != null) return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, index, value, def);\n      if (value.length === 0) return;\n      var lines = value.split('\\n');\n      var text = lines.shift();\n      if (text.length > 0) {\n        if (index < this.length() - 1 || this.children.tail == null) {\n          _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, Math.min(index, this.length() - 1), text);\n        } else {\n          this.children.tail.insertAt(this.children.tail.length(), text);\n        }\n        this.cache = {};\n      }\n      var block = this;\n      lines.reduce(function (index, line) {\n        block = block.split(index, true);\n        block.insertAt(0, line);\n        return line.length;\n      }, index + text.length);\n    }\n  }, {\n    key: 'insertBefore',\n    value: function insertBefore(blot, ref) {\n      var head = this.children.head;\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertBefore', this).call(this, blot, ref);\n      if (head instanceof _break2.default) {\n        head.remove();\n      }\n      this.cache = {};\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      if (this.cache.length == null) {\n        this.cache.length = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'length', this).call(this) + NEWLINE_LENGTH;\n      }\n      return this.cache.length;\n    }\n  }, {\n    key: 'moveChildren',\n    value: function moveChildren(target, ref) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'moveChildren', this).call(this, target, ref);\n      this.cache = {};\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize(context) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'optimize', this).call(this, context);\n      this.cache = {};\n    }\n  }, {\n    key: 'path',\n    value: function path(index) {\n      return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'path', this).call(this, index, true);\n    }\n  }, {\n    key: 'removeChild',\n    value: function removeChild(child) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'removeChild', this).call(this, child);\n      this.cache = {};\n    }\n  }, {\n    key: 'split',\n    value: function split(index) {\n      var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n      if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {\n        var clone = this.clone();\n        if (index === 0) {\n          this.parent.insertBefore(clone, this);\n          return this;\n        } else {\n          this.parent.insertBefore(clone, this.next);\n          return clone;\n        }\n      } else {\n        var next = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'split', this).call(this, index, force);\n        this.cache = {};\n        return next;\n      }\n    }\n  }]);\n\n  return Block;\n}(_parchment2.default.Block);\n\nBlock.blotName = 'block';\nBlock.tagName = 'P';\nBlock.defaultChild = 'break';\nBlock.allowedChildren = [_inline2.default, _parchment2.default.Embed, _text2.default];\n\nfunction bubbleFormats(blot) {\n  var formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n  if (blot == null) return formats;\n  if (typeof blot.formats === 'function') {\n    formats = (0, _extend2.default)(formats, blot.formats());\n  }\n  if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {\n    return formats;\n  }\n  return bubbleFormats(blot.parent, formats);\n}\n\nexports.bubbleFormats = bubbleFormats;\nexports.BlockEmbed = BlockEmbed;\nexports.default = Block;\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.overload = exports.expandConfig = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\n__webpack_require__(50);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _editor = __webpack_require__(14);\n\nvar _editor2 = _interopRequireDefault(_editor);\n\nvar _emitter3 = __webpack_require__(8);\n\nvar _emitter4 = _interopRequireDefault(_emitter3);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _selection = __webpack_require__(15);\n\nvar _selection2 = _interopRequireDefault(_selection);\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _theme = __webpack_require__(34);\n\nvar _theme2 = _interopRequireDefault(_theme);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar debug = (0, _logger2.default)('quill');\n\nvar Quill = function () {\n  _createClass(Quill, null, [{\n    key: 'debug',\n    value: function debug(limit) {\n      if (limit === true) {\n        limit = 'log';\n      }\n      _logger2.default.level(limit);\n    }\n  }, {\n    key: 'find',\n    value: function find(node) {\n      return node.__quill || _parchment2.default.find(node);\n    }\n  }, {\n    key: 'import',\n    value: function _import(name) {\n      if (this.imports[name] == null) {\n        debug.error('Cannot import ' + name + '. Are you sure it was registered?');\n      }\n      return this.imports[name];\n    }\n  }, {\n    key: 'register',\n    value: function register(path, target) {\n      var _this = this;\n\n      var overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n      if (typeof path !== 'string') {\n        var name = path.attrName || path.blotName;\n        if (typeof name === 'string') {\n          // register(Blot | Attributor, overwrite)\n          this.register('formats/' + name, path, target);\n        } else {\n          Object.keys(path).forEach(function (key) {\n            _this.register(key, path[key], target);\n          });\n        }\n      } else {\n        if (this.imports[path] != null && !overwrite) {\n          debug.warn('Overwriting ' + path + ' with', target);\n        }\n        this.imports[path] = target;\n        if ((path.startsWith('blots/') || path.startsWith('formats/')) && target.blotName !== 'abstract') {\n          _parchment2.default.register(target);\n        } else if (path.startsWith('modules') && typeof target.register === 'function') {\n          target.register();\n        }\n      }\n    }\n  }]);\n\n  function Quill(container) {\n    var _this2 = this;\n\n    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n    _classCallCheck(this, Quill);\n\n    this.options = expandConfig(container, options);\n    this.container = this.options.container;\n    if (this.container == null) {\n      return debug.error('Invalid Quill container', container);\n    }\n    if (this.options.debug) {\n      Quill.debug(this.options.debug);\n    }\n    var html = this.container.innerHTML.trim();\n    this.container.classList.add('ql-container');\n    this.container.innerHTML = '';\n    this.container.__quill = this;\n    this.root = this.addContainer('ql-editor');\n    this.root.classList.add('ql-blank');\n    this.root.setAttribute('data-gramm', false);\n    this.scrollingContainer = this.options.scrollingContainer || this.root;\n    this.emitter = new _emitter4.default();\n    this.scroll = _parchment2.default.create(this.root, {\n      emitter: this.emitter,\n      whitelist: this.options.formats\n    });\n    this.editor = new _editor2.default(this.scroll);\n    this.selection = new _selection2.default(this.scroll, this.emitter);\n    this.theme = new this.options.theme(this, this.options);\n    this.keyboard = this.theme.addModule('keyboard');\n    this.clipboard = this.theme.addModule('clipboard');\n    this.history = this.theme.addModule('history');\n    this.theme.init();\n    this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type) {\n      if (type === _emitter4.default.events.TEXT_CHANGE) {\n        _this2.root.classList.toggle('ql-blank', _this2.editor.isBlank());\n      }\n    });\n    this.emitter.on(_emitter4.default.events.SCROLL_UPDATE, function (source, mutations) {\n      var range = _this2.selection.lastRange;\n      var index = range && range.length === 0 ? range.index : undefined;\n      modify.call(_this2, function () {\n        return _this2.editor.update(null, mutations, index);\n      }, source);\n    });\n    var contents = this.clipboard.convert('<div class=\\'ql-editor\\' style=\"white-space: normal;\">' + html + '<p><br></p></div>');\n    this.setContents(contents);\n    this.history.clear();\n    if (this.options.placeholder) {\n      this.root.setAttribute('data-placeholder', this.options.placeholder);\n    }\n    if (this.options.readOnly) {\n      this.disable();\n    }\n  }\n\n  _createClass(Quill, [{\n    key: 'addContainer',\n    value: function addContainer(container) {\n      var refNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n\n      if (typeof container === 'string') {\n        var className = container;\n        container = document.createElement('div');\n        container.classList.add(className);\n      }\n      this.container.insertBefore(container, refNode);\n      return container;\n    }\n  }, {\n    key: 'blur',\n    value: function blur() {\n      this.selection.setRange(null);\n    }\n  }, {\n    key: 'deleteText',\n    value: function deleteText(index, length, source) {\n      var _this3 = this;\n\n      var _overload = overload(index, length, source);\n\n      var _overload2 = _slicedToArray(_overload, 4);\n\n      index = _overload2[0];\n      length = _overload2[1];\n      source = _overload2[3];\n\n      return modify.call(this, function () {\n        return _this3.editor.deleteText(index, length);\n      }, source, index, -1 * length);\n    }\n  }, {\n    key: 'disable',\n    value: function disable() {\n      this.enable(false);\n    }\n  }, {\n    key: 'enable',\n    value: function enable() {\n      var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n\n      this.scroll.enable(enabled);\n      this.container.classList.toggle('ql-disabled', !enabled);\n    }\n  }, {\n    key: 'focus',\n    value: function focus() {\n      var scrollTop = this.scrollingContainer.scrollTop;\n      this.selection.focus();\n      this.scrollingContainer.scrollTop = scrollTop;\n      this.scrollIntoView();\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      var _this4 = this;\n\n      var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API;\n\n      return modify.call(this, function () {\n        var range = _this4.getSelection(true);\n        var change = new _quillDelta2.default();\n        if (range == null) {\n          return change;\n        } else if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) {\n          change = _this4.editor.formatLine(range.index, range.length, _defineProperty({}, name, value));\n        } else if (range.length === 0) {\n          _this4.selection.format(name, value);\n          return change;\n        } else {\n          change = _this4.editor.formatText(range.index, range.length, _defineProperty({}, name, value));\n        }\n        _this4.setSelection(range, _emitter4.default.sources.SILENT);\n        return change;\n      }, source);\n    }\n  }, {\n    key: 'formatLine',\n    value: function formatLine(index, length, name, value, source) {\n      var _this5 = this;\n\n      var formats = void 0;\n\n      var _overload3 = overload(index, length, name, value, source);\n\n      var _overload4 = _slicedToArray(_overload3, 4);\n\n      index = _overload4[0];\n      length = _overload4[1];\n      formats = _overload4[2];\n      source = _overload4[3];\n\n      return modify.call(this, function () {\n        return _this5.editor.formatLine(index, length, formats);\n      }, source, index, 0);\n    }\n  }, {\n    key: 'formatText',\n    value: function formatText(index, length, name, value, source) {\n      var _this6 = this;\n\n      var formats = void 0;\n\n      var _overload5 = overload(index, length, name, value, source);\n\n      var _overload6 = _slicedToArray(_overload5, 4);\n\n      index = _overload6[0];\n      length = _overload6[1];\n      formats = _overload6[2];\n      source = _overload6[3];\n\n      return modify.call(this, function () {\n        return _this6.editor.formatText(index, length, formats);\n      }, source, index, 0);\n    }\n  }, {\n    key: 'getBounds',\n    value: function getBounds(index) {\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      var bounds = void 0;\n      if (typeof index === 'number') {\n        bounds = this.selection.getBounds(index, length);\n      } else {\n        bounds = this.selection.getBounds(index.index, index.length);\n      }\n      var containerBounds = this.container.getBoundingClientRect();\n      return {\n        bottom: bounds.bottom - containerBounds.top,\n        height: bounds.height,\n        left: bounds.left - containerBounds.left,\n        right: bounds.right - containerBounds.left,\n        top: bounds.top - containerBounds.top,\n        width: bounds.width\n      };\n    }\n  }, {\n    key: 'getContents',\n    value: function getContents() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index;\n\n      var _overload7 = overload(index, length);\n\n      var _overload8 = _slicedToArray(_overload7, 2);\n\n      index = _overload8[0];\n      length = _overload8[1];\n\n      return this.editor.getContents(index, length);\n    }\n  }, {\n    key: 'getFormat',\n    value: function getFormat() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getSelection(true);\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      if (typeof index === 'number') {\n        return this.editor.getFormat(index, length);\n      } else {\n        return this.editor.getFormat(index.index, index.length);\n      }\n    }\n  }, {\n    key: 'getIndex',\n    value: function getIndex(blot) {\n      return blot.offset(this.scroll);\n    }\n  }, {\n    key: 'getLength',\n    value: function getLength() {\n      return this.scroll.length();\n    }\n  }, {\n    key: 'getLeaf',\n    value: function getLeaf(index) {\n      return this.scroll.leaf(index);\n    }\n  }, {\n    key: 'getLine',\n    value: function getLine(index) {\n      return this.scroll.line(index);\n    }\n  }, {\n    key: 'getLines',\n    value: function getLines() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;\n\n      if (typeof index !== 'number') {\n        return this.scroll.lines(index.index, index.length);\n      } else {\n        return this.scroll.lines(index, length);\n      }\n    }\n  }, {\n    key: 'getModule',\n    value: function getModule(name) {\n      return this.theme.modules[name];\n    }\n  }, {\n    key: 'getSelection',\n    value: function getSelection() {\n      var focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n      if (focus) this.focus();\n      this.update(); // Make sure we access getRange with editor in consistent state\n      return this.selection.getRange()[0];\n    }\n  }, {\n    key: 'getText',\n    value: function getText() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index;\n\n      var _overload9 = overload(index, length);\n\n      var _overload10 = _slicedToArray(_overload9, 2);\n\n      index = _overload10[0];\n      length = _overload10[1];\n\n      return this.editor.getText(index, length);\n    }\n  }, {\n    key: 'hasFocus',\n    value: function hasFocus() {\n      return this.selection.hasFocus();\n    }\n  }, {\n    key: 'insertEmbed',\n    value: function insertEmbed(index, embed, value) {\n      var _this7 = this;\n\n      var source = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Quill.sources.API;\n\n      return modify.call(this, function () {\n        return _this7.editor.insertEmbed(index, embed, value);\n      }, source, index);\n    }\n  }, {\n    key: 'insertText',\n    value: function insertText(index, text, name, value, source) {\n      var _this8 = this;\n\n      var formats = void 0;\n\n      var _overload11 = overload(index, 0, name, value, source);\n\n      var _overload12 = _slicedToArray(_overload11, 4);\n\n      index = _overload12[0];\n      formats = _overload12[2];\n      source = _overload12[3];\n\n      return modify.call(this, function () {\n        return _this8.editor.insertText(index, text, formats);\n      }, source, index, text.length);\n    }\n  }, {\n    key: 'isEnabled',\n    value: function isEnabled() {\n      return !this.container.classList.contains('ql-disabled');\n    }\n  }, {\n    key: 'off',\n    value: function off() {\n      return this.emitter.off.apply(this.emitter, arguments);\n    }\n  }, {\n    key: 'on',\n    value: function on() {\n      return this.emitter.on.apply(this.emitter, arguments);\n    }\n  }, {\n    key: 'once',\n    value: function once() {\n      return this.emitter.once.apply(this.emitter, arguments);\n    }\n  }, {\n    key: 'pasteHTML',\n    value: function pasteHTML(index, html, source) {\n      this.clipboard.dangerouslyPasteHTML(index, html, source);\n    }\n  }, {\n    key: 'removeFormat',\n    value: function removeFormat(index, length, source) {\n      var _this9 = this;\n\n      var _overload13 = overload(index, length, source);\n\n      var _overload14 = _slicedToArray(_overload13, 4);\n\n      index = _overload14[0];\n      length = _overload14[1];\n      source = _overload14[3];\n\n      return modify.call(this, function () {\n        return _this9.editor.removeFormat(index, length);\n      }, source, index);\n    }\n  }, {\n    key: 'scrollIntoView',\n    value: function scrollIntoView() {\n      this.selection.scrollIntoView(this.scrollingContainer);\n    }\n  }, {\n    key: 'setContents',\n    value: function setContents(delta) {\n      var _this10 = this;\n\n      var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n      return modify.call(this, function () {\n        delta = new _quillDelta2.default(delta);\n        var length = _this10.getLength();\n        var deleted = _this10.editor.deleteText(0, length);\n        var applied = _this10.editor.applyDelta(delta);\n        var lastOp = applied.ops[applied.ops.length - 1];\n        if (lastOp != null && typeof lastOp.insert === 'string' && lastOp.insert[lastOp.insert.length - 1] === '\\n') {\n          _this10.editor.deleteText(_this10.getLength() - 1, 1);\n          applied.delete(1);\n        }\n        var ret = deleted.compose(applied);\n        return ret;\n      }, source);\n    }\n  }, {\n    key: 'setSelection',\n    value: function setSelection(index, length, source) {\n      if (index == null) {\n        this.selection.setRange(null, length || Quill.sources.API);\n      } else {\n        var _overload15 = overload(index, length, source);\n\n        var _overload16 = _slicedToArray(_overload15, 4);\n\n        index = _overload16[0];\n        length = _overload16[1];\n        source = _overload16[3];\n\n        this.selection.setRange(new _selection.Range(index, length), source);\n        if (source !== _emitter4.default.sources.SILENT) {\n          this.selection.scrollIntoView(this.scrollingContainer);\n        }\n      }\n    }\n  }, {\n    key: 'setText',\n    value: function setText(text) {\n      var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n      var delta = new _quillDelta2.default().insert(text);\n      return this.setContents(delta, source);\n    }\n  }, {\n    key: 'update',\n    value: function update() {\n      var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER;\n\n      var change = this.scroll.update(source); // Will update selection before selection.update() does if text changes\n      this.selection.update(source);\n      return change;\n    }\n  }, {\n    key: 'updateContents',\n    value: function updateContents(delta) {\n      var _this11 = this;\n\n      var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n      return modify.call(this, function () {\n        delta = new _quillDelta2.default(delta);\n        return _this11.editor.applyDelta(delta, source);\n      }, source, true);\n    }\n  }]);\n\n  return Quill;\n}();\n\nQuill.DEFAULTS = {\n  bounds: null,\n  formats: null,\n  modules: {},\n  placeholder: '',\n  readOnly: false,\n  scrollingContainer: null,\n  strict: true,\n  theme: 'default'\n};\nQuill.events = _emitter4.default.events;\nQuill.sources = _emitter4.default.sources;\n// eslint-disable-next-line no-undef\nQuill.version =  false ? 'dev' : \"1.3.7\";\n\nQuill.imports = {\n  'delta': _quillDelta2.default,\n  'parchment': _parchment2.default,\n  'core/module': _module2.default,\n  'core/theme': _theme2.default\n};\n\nfunction expandConfig(container, userConfig) {\n  userConfig = (0, _extend2.default)(true, {\n    container: container,\n    modules: {\n      clipboard: true,\n      keyboard: true,\n      history: true\n    }\n  }, userConfig);\n  if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) {\n    userConfig.theme = _theme2.default;\n  } else {\n    userConfig.theme = Quill.import('themes/' + userConfig.theme);\n    if (userConfig.theme == null) {\n      throw new Error('Invalid theme ' + userConfig.theme + '. Did you register it?');\n    }\n  }\n  var themeConfig = (0, _extend2.default)(true, {}, userConfig.theme.DEFAULTS);\n  [themeConfig, userConfig].forEach(function (config) {\n    config.modules = config.modules || {};\n    Object.keys(config.modules).forEach(function (module) {\n      if (config.modules[module] === true) {\n        config.modules[module] = {};\n      }\n    });\n  });\n  var moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules));\n  var moduleConfig = moduleNames.reduce(function (config, name) {\n    var moduleClass = Quill.import('modules/' + name);\n    if (moduleClass == null) {\n      debug.error('Cannot load ' + name + ' module. Are you sure you registered it?');\n    } else {\n      config[name] = moduleClass.DEFAULTS || {};\n    }\n    return config;\n  }, {});\n  // Special case toolbar shorthand\n  if (userConfig.modules != null && userConfig.modules.toolbar && userConfig.modules.toolbar.constructor !== Object) {\n    userConfig.modules.toolbar = {\n      container: userConfig.modules.toolbar\n    };\n  }\n  userConfig = (0, _extend2.default)(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig);\n  ['bounds', 'container', 'scrollingContainer'].forEach(function (key) {\n    if (typeof userConfig[key] === 'string') {\n      userConfig[key] = document.querySelector(userConfig[key]);\n    }\n  });\n  userConfig.modules = Object.keys(userConfig.modules).reduce(function (config, name) {\n    if (userConfig.modules[name]) {\n      config[name] = userConfig.modules[name];\n    }\n    return config;\n  }, {});\n  return userConfig;\n}\n\n// Handle selection preservation and TEXT_CHANGE emission\n// common to modification APIs\nfunction modify(modifier, source, index, shift) {\n  if (this.options.strict && !this.isEnabled() && source === _emitter4.default.sources.USER) {\n    return new _quillDelta2.default();\n  }\n  var range = index == null ? null : this.getSelection();\n  var oldDelta = this.editor.delta;\n  var change = modifier();\n  if (range != null) {\n    if (index === true) index = range.index;\n    if (shift == null) {\n      range = shiftRange(range, change, source);\n    } else if (shift !== 0) {\n      range = shiftRange(range, index, shift, source);\n    }\n    this.setSelection(range, _emitter4.default.sources.SILENT);\n  }\n  if (change.length() > 0) {\n    var _emitter;\n\n    var args = [_emitter4.default.events.TEXT_CHANGE, change, oldDelta, source];\n    (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args));\n    if (source !== _emitter4.default.sources.SILENT) {\n      var _emitter2;\n\n      (_emitter2 = this.emitter).emit.apply(_emitter2, args);\n    }\n  }\n  return change;\n}\n\nfunction overload(index, length, name, value, source) {\n  var formats = {};\n  if (typeof index.index === 'number' && typeof index.length === 'number') {\n    // Allow for throwaway end (used by insertText/insertEmbed)\n    if (typeof length !== 'number') {\n      source = value, value = name, name = length, length = index.length, index = index.index;\n    } else {\n      length = index.length, index = index.index;\n    }\n  } else if (typeof length !== 'number') {\n    source = value, value = name, name = length, length = 0;\n  }\n  // Handle format being object, two format name/value strings or excluded\n  if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') {\n    formats = name;\n    source = value;\n  } else if (typeof name === 'string') {\n    if (value != null) {\n      formats[name] = value;\n    } else {\n      source = name;\n    }\n  }\n  // Handle optional source\n  source = source || _emitter4.default.sources.API;\n  return [index, length, formats, source];\n}\n\nfunction shiftRange(range, index, length, source) {\n  if (range == null) return null;\n  var start = void 0,\n      end = void 0;\n  if (index instanceof _quillDelta2.default) {\n    var _map = [range.index, range.index + range.length].map(function (pos) {\n      return index.transformPosition(pos, source !== _emitter4.default.sources.USER);\n    });\n\n    var _map2 = _slicedToArray(_map, 2);\n\n    start = _map2[0];\n    end = _map2[1];\n  } else {\n    var _map3 = [range.index, range.index + range.length].map(function (pos) {\n      if (pos < index || pos === index && source === _emitter4.default.sources.USER) return pos;\n      if (length >= 0) {\n        return pos + length;\n      } else {\n        return Math.max(index, pos + length);\n      }\n    });\n\n    var _map4 = _slicedToArray(_map3, 2);\n\n    start = _map4[0];\n    end = _map4[1];\n  }\n  return new _selection.Range(start, end - start);\n}\n\nexports.expandConfig = expandConfig;\nexports.overload = overload;\nexports.default = Quill;\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Inline = function (_Parchment$Inline) {\n  _inherits(Inline, _Parchment$Inline);\n\n  function Inline() {\n    _classCallCheck(this, Inline);\n\n    return _possibleConstructorReturn(this, (Inline.__proto__ || Object.getPrototypeOf(Inline)).apply(this, arguments));\n  }\n\n  _createClass(Inline, [{\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      if (Inline.compare(this.statics.blotName, name) < 0 && _parchment2.default.query(name, _parchment2.default.Scope.BLOT)) {\n        var blot = this.isolate(index, length);\n        if (value) {\n          blot.wrap(name, value);\n        }\n      } else {\n        _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'formatAt', this).call(this, index, length, name, value);\n      }\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize(context) {\n      _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'optimize', this).call(this, context);\n      if (this.parent instanceof Inline && Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) {\n        var parent = this.parent.isolate(this.offset(), this.length());\n        this.moveChildren(parent);\n        parent.wrap(this);\n      }\n    }\n  }], [{\n    key: 'compare',\n    value: function compare(self, other) {\n      var selfIndex = Inline.order.indexOf(self);\n      var otherIndex = Inline.order.indexOf(other);\n      if (selfIndex >= 0 || otherIndex >= 0) {\n        return selfIndex - otherIndex;\n      } else if (self === other) {\n        return 0;\n      } else if (self < other) {\n        return -1;\n      } else {\n        return 1;\n      }\n    }\n  }]);\n\n  return Inline;\n}(_parchment2.default.Inline);\n\nInline.allowedChildren = [Inline, _parchment2.default.Embed, _text2.default];\n// Lower index means deeper in the DOM tree, since not found (-1) is for embeds\nInline.order = ['cursor', 'inline', // Must be lower\n'underline', 'strike', 'italic', 'bold', 'script', 'link', 'code' // Must be higher\n];\n\nexports.default = Inline;\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar TextBlot = function (_Parchment$Text) {\n  _inherits(TextBlot, _Parchment$Text);\n\n  function TextBlot() {\n    _classCallCheck(this, TextBlot);\n\n    return _possibleConstructorReturn(this, (TextBlot.__proto__ || Object.getPrototypeOf(TextBlot)).apply(this, arguments));\n  }\n\n  return TextBlot;\n}(_parchment2.default.Text);\n\nexports.default = TextBlot;\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _eventemitter = __webpack_require__(54);\n\nvar _eventemitter2 = _interopRequireDefault(_eventemitter);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:events');\n\nvar EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click'];\n\nEVENTS.forEach(function (eventName) {\n  document.addEventListener(eventName, function () {\n    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {\n      args[_key] = arguments[_key];\n    }\n\n    [].slice.call(document.querySelectorAll('.ql-container')).forEach(function (node) {\n      // TODO use WeakMap\n      if (node.__quill && node.__quill.emitter) {\n        var _node$__quill$emitter;\n\n        (_node$__quill$emitter = node.__quill.emitter).handleDOM.apply(_node$__quill$emitter, args);\n      }\n    });\n  });\n});\n\nvar Emitter = function (_EventEmitter) {\n  _inherits(Emitter, _EventEmitter);\n\n  function Emitter() {\n    _classCallCheck(this, Emitter);\n\n    var _this = _possibleConstructorReturn(this, (Emitter.__proto__ || Object.getPrototypeOf(Emitter)).call(this));\n\n    _this.listeners = {};\n    _this.on('error', debug.error);\n    return _this;\n  }\n\n  _createClass(Emitter, [{\n    key: 'emit',\n    value: function emit() {\n      debug.log.apply(debug, arguments);\n      _get(Emitter.prototype.__proto__ || Object.getPrototypeOf(Emitter.prototype), 'emit', this).apply(this, arguments);\n    }\n  }, {\n    key: 'handleDOM',\n    value: function handleDOM(event) {\n      for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n        args[_key2 - 1] = arguments[_key2];\n      }\n\n      (this.listeners[event.type] || []).forEach(function (_ref) {\n        var node = _ref.node,\n            handler = _ref.handler;\n\n        if (event.target === node || node.contains(event.target)) {\n          handler.apply(undefined, [event].concat(args));\n        }\n      });\n    }\n  }, {\n    key: 'listenDOM',\n    value: function listenDOM(eventName, node, handler) {\n      if (!this.listeners[eventName]) {\n        this.listeners[eventName] = [];\n      }\n      this.listeners[eventName].push({ node: node, handler: handler });\n    }\n  }]);\n\n  return Emitter;\n}(_eventemitter2.default);\n\nEmitter.events = {\n  EDITOR_CHANGE: 'editor-change',\n  SCROLL_BEFORE_UPDATE: 'scroll-before-update',\n  SCROLL_OPTIMIZE: 'scroll-optimize',\n  SCROLL_UPDATE: 'scroll-update',\n  SELECTION_CHANGE: 'selection-change',\n  TEXT_CHANGE: 'text-change'\n};\nEmitter.sources = {\n  API: 'api',\n  SILENT: 'silent',\n  USER: 'user'\n};\n\nexports.default = Emitter;\n\n/***/ }),\n/* 9 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Module = function Module(quill) {\n  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n  _classCallCheck(this, Module);\n\n  this.quill = quill;\n  this.options = options;\n};\n\nModule.DEFAULTS = {};\n\nexports.default = Module;\n\n/***/ }),\n/* 10 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nvar levels = ['error', 'warn', 'log', 'info'];\nvar level = 'warn';\n\nfunction debug(method) {\n  if (levels.indexOf(method) <= levels.indexOf(level)) {\n    var _console;\n\n    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n      args[_key - 1] = arguments[_key];\n    }\n\n    (_console = console)[method].apply(_console, args); // eslint-disable-line no-console\n  }\n}\n\nfunction namespace(ns) {\n  return levels.reduce(function (logger, method) {\n    logger[method] = debug.bind(console, method, ns);\n    return logger;\n  }, {});\n}\n\ndebug.level = namespace.level = function (newLevel) {\n  level = newLevel;\n};\n\nexports.default = namespace;\n\n/***/ }),\n/* 11 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar pSlice = Array.prototype.slice;\nvar objectKeys = __webpack_require__(52);\nvar isArguments = __webpack_require__(53);\n\nvar deepEqual = module.exports = function (actual, expected, opts) {\n  if (!opts) opts = {};\n  // 7.1. All identical values are equivalent, as determined by ===.\n  if (actual === expected) {\n    return true;\n\n  } else if (actual instanceof Date && expected instanceof Date) {\n    return actual.getTime() === expected.getTime();\n\n  // 7.3. Other pairs that do not both pass typeof value == 'object',\n  // equivalence is determined by ==.\n  } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') {\n    return opts.strict ? actual === expected : actual == expected;\n\n  // 7.4. For all other Object pairs, including Array objects, equivalence is\n  // determined by having the same number of owned properties (as verified\n  // with Object.prototype.hasOwnProperty.call), the same set of keys\n  // (although not necessarily the same order), equivalent values for every\n  // corresponding key, and an identical 'prototype' property. Note: this\n  // accounts for both named and indexed properties on Arrays.\n  } else {\n    return objEquiv(actual, expected, opts);\n  }\n}\n\nfunction isUndefinedOrNull(value) {\n  return value === null || value === undefined;\n}\n\nfunction isBuffer (x) {\n  if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;\n  if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {\n    return false;\n  }\n  if (x.length > 0 && typeof x[0] !== 'number') return false;\n  return true;\n}\n\nfunction objEquiv(a, b, opts) {\n  var i, key;\n  if (isUndefinedOrNull(a) || isUndefinedOrNull(b))\n    return false;\n  // an identical 'prototype' property.\n  if (a.prototype !== b.prototype) return false;\n  //~~~I've managed to break Object.keys through screwy arguments passing.\n  //   Converting to array solves the problem.\n  if (isArguments(a)) {\n    if (!isArguments(b)) {\n      return false;\n    }\n    a = pSlice.call(a);\n    b = pSlice.call(b);\n    return deepEqual(a, b, opts);\n  }\n  if (isBuffer(a)) {\n    if (!isBuffer(b)) {\n      return false;\n    }\n    if (a.length !== b.length) return false;\n    for (i = 0; i < a.length; i++) {\n      if (a[i] !== b[i]) return false;\n    }\n    return true;\n  }\n  try {\n    var ka = objectKeys(a),\n        kb = objectKeys(b);\n  } catch (e) {//happens when one is a string literal and the other isn't\n    return false;\n  }\n  // having the same number of owned properties (keys incorporates\n  // hasOwnProperty)\n  if (ka.length != kb.length)\n    return false;\n  //the same set of keys (although not necessarily the same order),\n  ka.sort();\n  kb.sort();\n  //~~~cheap key test\n  for (i = ka.length - 1; i >= 0; i--) {\n    if (ka[i] != kb[i])\n      return false;\n  }\n  //equivalent values for every corresponding key, and\n  //~~~possibly expensive deep test\n  for (i = ka.length - 1; i >= 0; i--) {\n    key = ka[i];\n    if (!deepEqual(a[key], b[key], opts)) return false;\n  }\n  return typeof a === typeof b;\n}\n\n\n/***/ }),\n/* 12 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = __webpack_require__(1);\nvar Attributor = /** @class */ (function () {\n    function Attributor(attrName, keyName, options) {\n        if (options === void 0) { options = {}; }\n        this.attrName = attrName;\n        this.keyName = keyName;\n        var attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE;\n        if (options.scope != null) {\n            // Ignore type bits, force attribute bit\n            this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit;\n        }\n        else {\n            this.scope = Registry.Scope.ATTRIBUTE;\n        }\n        if (options.whitelist != null)\n            this.whitelist = options.whitelist;\n    }\n    Attributor.keys = function (node) {\n        return [].map.call(node.attributes, function (item) {\n            return item.name;\n        });\n    };\n    Attributor.prototype.add = function (node, value) {\n        if (!this.canAdd(node, value))\n            return false;\n        node.setAttribute(this.keyName, value);\n        return true;\n    };\n    Attributor.prototype.canAdd = function (node, value) {\n        var match = Registry.query(node, Registry.Scope.BLOT & (this.scope | Registry.Scope.TYPE));\n        if (match == null)\n            return false;\n        if (this.whitelist == null)\n            return true;\n        if (typeof value === 'string') {\n            return this.whitelist.indexOf(value.replace(/[\"']/g, '')) > -1;\n        }\n        else {\n            return this.whitelist.indexOf(value) > -1;\n        }\n    };\n    Attributor.prototype.remove = function (node) {\n        node.removeAttribute(this.keyName);\n    };\n    Attributor.prototype.value = function (node) {\n        var value = node.getAttribute(this.keyName);\n        if (this.canAdd(node, value) && value) {\n            return value;\n        }\n        return '';\n    };\n    return Attributor;\n}());\nexports.default = Attributor;\n\n\n/***/ }),\n/* 13 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.Code = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Code = function (_Inline) {\n  _inherits(Code, _Inline);\n\n  function Code() {\n    _classCallCheck(this, Code);\n\n    return _possibleConstructorReturn(this, (Code.__proto__ || Object.getPrototypeOf(Code)).apply(this, arguments));\n  }\n\n  return Code;\n}(_inline2.default);\n\nCode.blotName = 'code';\nCode.tagName = 'CODE';\n\nvar CodeBlock = function (_Block) {\n  _inherits(CodeBlock, _Block);\n\n  function CodeBlock() {\n    _classCallCheck(this, CodeBlock);\n\n    return _possibleConstructorReturn(this, (CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock)).apply(this, arguments));\n  }\n\n  _createClass(CodeBlock, [{\n    key: 'delta',\n    value: function delta() {\n      var _this3 = this;\n\n      var text = this.domNode.textContent;\n      if (text.endsWith('\\n')) {\n        // Should always be true\n        text = text.slice(0, -1);\n      }\n      return text.split('\\n').reduce(function (delta, frag) {\n        return delta.insert(frag).insert('\\n', _this3.formats());\n      }, new _quillDelta2.default());\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      if (name === this.statics.blotName && value) return;\n\n      var _descendant = this.descendant(_text2.default, this.length() - 1),\n          _descendant2 = _slicedToArray(_descendant, 1),\n          text = _descendant2[0];\n\n      if (text != null) {\n        text.deleteAt(text.length() - 1, 1);\n      }\n      _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'format', this).call(this, name, value);\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      if (length === 0) return;\n      if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK) == null || name === this.statics.blotName && value === this.statics.formats(this.domNode)) {\n        return;\n      }\n      var nextNewline = this.newlineIndex(index);\n      if (nextNewline < 0 || nextNewline >= index + length) return;\n      var prevNewline = this.newlineIndex(index, true) + 1;\n      var isolateLength = nextNewline - prevNewline + 1;\n      var blot = this.isolate(prevNewline, isolateLength);\n      var next = blot.next;\n      blot.format(name, value);\n      if (next instanceof CodeBlock) {\n        next.formatAt(0, index - prevNewline + length - isolateLength, name, value);\n      }\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (def != null) return;\n\n      var _descendant3 = this.descendant(_text2.default, index),\n          _descendant4 = _slicedToArray(_descendant3, 2),\n          text = _descendant4[0],\n          offset = _descendant4[1];\n\n      text.insertAt(offset, value);\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      var length = this.domNode.textContent.length;\n      if (!this.domNode.textContent.endsWith('\\n')) {\n        return length + 1;\n      }\n      return length;\n    }\n  }, {\n    key: 'newlineIndex',\n    value: function newlineIndex(searchIndex) {\n      var reverse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n      if (!reverse) {\n        var offset = this.domNode.textContent.slice(searchIndex).indexOf('\\n');\n        return offset > -1 ? searchIndex + offset : -1;\n      } else {\n        return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\\n');\n      }\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize(context) {\n      if (!this.domNode.textContent.endsWith('\\n')) {\n        this.appendChild(_parchment2.default.create('text', '\\n'));\n      }\n      _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'optimize', this).call(this, context);\n      var next = this.next;\n      if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) {\n        next.optimize(context);\n        next.moveChildren(this);\n        next.remove();\n      }\n    }\n  }, {\n    key: 'replace',\n    value: function replace(target) {\n      _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'replace', this).call(this, target);\n      [].slice.call(this.domNode.querySelectorAll('*')).forEach(function (node) {\n        var blot = _parchment2.default.find(node);\n        if (blot == null) {\n          node.parentNode.removeChild(node);\n        } else if (blot instanceof _parchment2.default.Embed) {\n          blot.remove();\n        } else {\n          blot.unwrap();\n        }\n      });\n    }\n  }], [{\n    key: 'create',\n    value: function create(value) {\n      var domNode = _get(CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock), 'create', this).call(this, value);\n      domNode.setAttribute('spellcheck', false);\n      return domNode;\n    }\n  }, {\n    key: 'formats',\n    value: function formats() {\n      return true;\n    }\n  }]);\n\n  return CodeBlock;\n}(_block2.default);\n\nCodeBlock.blotName = 'code-block';\nCodeBlock.tagName = 'PRE';\nCodeBlock.TAB = '  ';\n\nexports.Code = Code;\nexports.default = CodeBlock;\n\n/***/ }),\n/* 14 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _op = __webpack_require__(20);\n\nvar _op2 = _interopRequireDefault(_op);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _cursor = __webpack_require__(24);\n\nvar _cursor2 = _interopRequireDefault(_cursor);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(11);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar ASCII = /^[ -~]*$/;\n\nvar Editor = function () {\n  function Editor(scroll) {\n    _classCallCheck(this, Editor);\n\n    this.scroll = scroll;\n    this.delta = this.getDelta();\n  }\n\n  _createClass(Editor, [{\n    key: 'applyDelta',\n    value: function applyDelta(delta) {\n      var _this = this;\n\n      var consumeNextNewline = false;\n      this.scroll.update();\n      var scrollLength = this.scroll.length();\n      this.scroll.batchStart();\n      delta = normalizeDelta(delta);\n      delta.reduce(function (index, op) {\n        var length = op.retain || op.delete || op.insert.length || 1;\n        var attributes = op.attributes || {};\n        if (op.insert != null) {\n          if (typeof op.insert === 'string') {\n            var text = op.insert;\n            if (text.endsWith('\\n') && consumeNextNewline) {\n              consumeNextNewline = false;\n              text = text.slice(0, -1);\n            }\n            if (index >= scrollLength && !text.endsWith('\\n')) {\n              consumeNextNewline = true;\n            }\n            _this.scroll.insertAt(index, text);\n\n            var _scroll$line = _this.scroll.line(index),\n                _scroll$line2 = _slicedToArray(_scroll$line, 2),\n                line = _scroll$line2[0],\n                offset = _scroll$line2[1];\n\n            var formats = (0, _extend2.default)({}, (0, _block.bubbleFormats)(line));\n            if (line instanceof _block2.default) {\n              var _line$descendant = line.descendant(_parchment2.default.Leaf, offset),\n                  _line$descendant2 = _slicedToArray(_line$descendant, 1),\n                  leaf = _line$descendant2[0];\n\n              formats = (0, _extend2.default)(formats, (0, _block.bubbleFormats)(leaf));\n            }\n            attributes = _op2.default.attributes.diff(formats, attributes) || {};\n          } else if (_typeof(op.insert) === 'object') {\n            var key = Object.keys(op.insert)[0]; // There should only be one key\n            if (key == null) return index;\n            _this.scroll.insertAt(index, key, op.insert[key]);\n          }\n          scrollLength += length;\n        }\n        Object.keys(attributes).forEach(function (name) {\n          _this.scroll.formatAt(index, length, name, attributes[name]);\n        });\n        return index + length;\n      }, 0);\n      delta.reduce(function (index, op) {\n        if (typeof op.delete === 'number') {\n          _this.scroll.deleteAt(index, op.delete);\n          return index;\n        }\n        return index + (op.retain || op.insert.length || 1);\n      }, 0);\n      this.scroll.batchEnd();\n      return this.update(delta);\n    }\n  }, {\n    key: 'deleteText',\n    value: function deleteText(index, length) {\n      this.scroll.deleteAt(index, length);\n      return this.update(new _quillDelta2.default().retain(index).delete(length));\n    }\n  }, {\n    key: 'formatLine',\n    value: function formatLine(index, length) {\n      var _this2 = this;\n\n      var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      this.scroll.update();\n      Object.keys(formats).forEach(function (format) {\n        if (_this2.scroll.whitelist != null && !_this2.scroll.whitelist[format]) return;\n        var lines = _this2.scroll.lines(index, Math.max(length, 1));\n        var lengthRemaining = length;\n        lines.forEach(function (line) {\n          var lineLength = line.length();\n          if (!(line instanceof _code2.default)) {\n            line.format(format, formats[format]);\n          } else {\n            var codeIndex = index - line.offset(_this2.scroll);\n            var codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1;\n            line.formatAt(codeIndex, codeLength, format, formats[format]);\n          }\n          lengthRemaining -= lineLength;\n        });\n      });\n      this.scroll.optimize();\n      return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats)));\n    }\n  }, {\n    key: 'formatText',\n    value: function formatText(index, length) {\n      var _this3 = this;\n\n      var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      Object.keys(formats).forEach(function (format) {\n        _this3.scroll.formatAt(index, length, format, formats[format]);\n      });\n      return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats)));\n    }\n  }, {\n    key: 'getContents',\n    value: function getContents(index, length) {\n      return this.delta.slice(index, index + length);\n    }\n  }, {\n    key: 'getDelta',\n    value: function getDelta() {\n      return this.scroll.lines().reduce(function (delta, line) {\n        return delta.concat(line.delta());\n      }, new _quillDelta2.default());\n    }\n  }, {\n    key: 'getFormat',\n    value: function getFormat(index) {\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      var lines = [],\n          leaves = [];\n      if (length === 0) {\n        this.scroll.path(index).forEach(function (path) {\n          var _path = _slicedToArray(path, 1),\n              blot = _path[0];\n\n          if (blot instanceof _block2.default) {\n            lines.push(blot);\n          } else if (blot instanceof _parchment2.default.Leaf) {\n            leaves.push(blot);\n          }\n        });\n      } else {\n        lines = this.scroll.lines(index, length);\n        leaves = this.scroll.descendants(_parchment2.default.Leaf, index, length);\n      }\n      var formatsArr = [lines, leaves].map(function (blots) {\n        if (blots.length === 0) return {};\n        var formats = (0, _block.bubbleFormats)(blots.shift());\n        while (Object.keys(formats).length > 0) {\n          var blot = blots.shift();\n          if (blot == null) return formats;\n          formats = combineFormats((0, _block.bubbleFormats)(blot), formats);\n        }\n        return formats;\n      });\n      return _extend2.default.apply(_extend2.default, formatsArr);\n    }\n  }, {\n    key: 'getText',\n    value: function getText(index, length) {\n      return this.getContents(index, length).filter(function (op) {\n        return typeof op.insert === 'string';\n      }).map(function (op) {\n        return op.insert;\n      }).join('');\n    }\n  }, {\n    key: 'insertEmbed',\n    value: function insertEmbed(index, embed, value) {\n      this.scroll.insertAt(index, embed, value);\n      return this.update(new _quillDelta2.default().retain(index).insert(_defineProperty({}, embed, value)));\n    }\n  }, {\n    key: 'insertText',\n    value: function insertText(index, text) {\n      var _this4 = this;\n\n      var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      text = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n      this.scroll.insertAt(index, text);\n      Object.keys(formats).forEach(function (format) {\n        _this4.scroll.formatAt(index, text.length, format, formats[format]);\n      });\n      return this.update(new _quillDelta2.default().retain(index).insert(text, (0, _clone2.default)(formats)));\n    }\n  }, {\n    key: 'isBlank',\n    value: function isBlank() {\n      if (this.scroll.children.length == 0) return true;\n      if (this.scroll.children.length > 1) return false;\n      var block = this.scroll.children.head;\n      if (block.statics.blotName !== _block2.default.blotName) return false;\n      if (block.children.length > 1) return false;\n      return block.children.head instanceof _break2.default;\n    }\n  }, {\n    key: 'removeFormat',\n    value: function removeFormat(index, length) {\n      var text = this.getText(index, length);\n\n      var _scroll$line3 = this.scroll.line(index + length),\n          _scroll$line4 = _slicedToArray(_scroll$line3, 2),\n          line = _scroll$line4[0],\n          offset = _scroll$line4[1];\n\n      var suffixLength = 0,\n          suffix = new _quillDelta2.default();\n      if (line != null) {\n        if (!(line instanceof _code2.default)) {\n          suffixLength = line.length() - offset;\n        } else {\n          suffixLength = line.newlineIndex(offset) - offset + 1;\n        }\n        suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\\n');\n      }\n      var contents = this.getContents(index, length + suffixLength);\n      var diff = contents.diff(new _quillDelta2.default().insert(text).concat(suffix));\n      var delta = new _quillDelta2.default().retain(index).concat(diff);\n      return this.applyDelta(delta);\n    }\n  }, {\n    key: 'update',\n    value: function update(change) {\n      var mutations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];\n      var cursorIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n\n      var oldDelta = this.delta;\n      if (mutations.length === 1 && mutations[0].type === 'characterData' && mutations[0].target.data.match(ASCII) && _parchment2.default.find(mutations[0].target)) {\n        // Optimization for character changes\n        var textBlot = _parchment2.default.find(mutations[0].target);\n        var formats = (0, _block.bubbleFormats)(textBlot);\n        var index = textBlot.offset(this.scroll);\n        var oldValue = mutations[0].oldValue.replace(_cursor2.default.CONTENTS, '');\n        var oldText = new _quillDelta2.default().insert(oldValue);\n        var newText = new _quillDelta2.default().insert(textBlot.value());\n        var diffDelta = new _quillDelta2.default().retain(index).concat(oldText.diff(newText, cursorIndex));\n        change = diffDelta.reduce(function (delta, op) {\n          if (op.insert) {\n            return delta.insert(op.insert, formats);\n          } else {\n            return delta.push(op);\n          }\n        }, new _quillDelta2.default());\n        this.delta = oldDelta.compose(change);\n      } else {\n        this.delta = this.getDelta();\n        if (!change || !(0, _deepEqual2.default)(oldDelta.compose(change), this.delta)) {\n          change = oldDelta.diff(this.delta, cursorIndex);\n        }\n      }\n      return change;\n    }\n  }]);\n\n  return Editor;\n}();\n\nfunction combineFormats(formats, combined) {\n  return Object.keys(combined).reduce(function (merged, name) {\n    if (formats[name] == null) return merged;\n    if (combined[name] === formats[name]) {\n      merged[name] = combined[name];\n    } else if (Array.isArray(combined[name])) {\n      if (combined[name].indexOf(formats[name]) < 0) {\n        merged[name] = combined[name].concat([formats[name]]);\n      }\n    } else {\n      merged[name] = [combined[name], formats[name]];\n    }\n    return merged;\n  }, {});\n}\n\nfunction normalizeDelta(delta) {\n  return delta.reduce(function (delta, op) {\n    if (op.insert === 1) {\n      var attributes = (0, _clone2.default)(op.attributes);\n      delete attributes['image'];\n      return delta.insert({ image: op.attributes.image }, attributes);\n    }\n    if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) {\n      op = (0, _clone2.default)(op);\n      if (op.attributes.list) {\n        op.attributes.list = 'ordered';\n      } else {\n        op.attributes.list = 'bullet';\n        delete op.attributes.bullet;\n      }\n    }\n    if (typeof op.insert === 'string') {\n      var text = op.insert.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n      return delta.insert(text, op.attributes);\n    }\n    return delta.push(op);\n  }, new _quillDelta2.default());\n}\n\nexports.default = Editor;\n\n/***/ }),\n/* 15 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.Range = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(11);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _emitter3 = __webpack_require__(8);\n\nvar _emitter4 = _interopRequireDefault(_emitter3);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar debug = (0, _logger2.default)('quill:selection');\n\nvar Range = function Range(index) {\n  var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n  _classCallCheck(this, Range);\n\n  this.index = index;\n  this.length = length;\n};\n\nvar Selection = function () {\n  function Selection(scroll, emitter) {\n    var _this = this;\n\n    _classCallCheck(this, Selection);\n\n    this.emitter = emitter;\n    this.scroll = scroll;\n    this.composing = false;\n    this.mouseDown = false;\n    this.root = this.scroll.domNode;\n    this.cursor = _parchment2.default.create('cursor', this);\n    // savedRange is last non-null range\n    this.lastRange = this.savedRange = new Range(0, 0);\n    this.handleComposition();\n    this.handleDragging();\n    this.emitter.listenDOM('selectionchange', document, function () {\n      if (!_this.mouseDown) {\n        setTimeout(_this.update.bind(_this, _emitter4.default.sources.USER), 1);\n      }\n    });\n    this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type, delta) {\n      if (type === _emitter4.default.events.TEXT_CHANGE && delta.length() > 0) {\n        _this.update(_emitter4.default.sources.SILENT);\n      }\n    });\n    this.emitter.on(_emitter4.default.events.SCROLL_BEFORE_UPDATE, function () {\n      if (!_this.hasFocus()) return;\n      var native = _this.getNativeRange();\n      if (native == null) return;\n      if (native.start.node === _this.cursor.textNode) return; // cursor.restore() will handle\n      // TODO unclear if this has negative side effects\n      _this.emitter.once(_emitter4.default.events.SCROLL_UPDATE, function () {\n        try {\n          _this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset);\n        } catch (ignored) {}\n      });\n    });\n    this.emitter.on(_emitter4.default.events.SCROLL_OPTIMIZE, function (mutations, context) {\n      if (context.range) {\n        var _context$range = context.range,\n            startNode = _context$range.startNode,\n            startOffset = _context$range.startOffset,\n            endNode = _context$range.endNode,\n            endOffset = _context$range.endOffset;\n\n        _this.setNativeRange(startNode, startOffset, endNode, endOffset);\n      }\n    });\n    this.update(_emitter4.default.sources.SILENT);\n  }\n\n  _createClass(Selection, [{\n    key: 'handleComposition',\n    value: function handleComposition() {\n      var _this2 = this;\n\n      this.root.addEventListener('compositionstart', function () {\n        _this2.composing = true;\n      });\n      this.root.addEventListener('compositionend', function () {\n        _this2.composing = false;\n        if (_this2.cursor.parent) {\n          var range = _this2.cursor.restore();\n          if (!range) return;\n          setTimeout(function () {\n            _this2.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset);\n          }, 1);\n        }\n      });\n    }\n  }, {\n    key: 'handleDragging',\n    value: function handleDragging() {\n      var _this3 = this;\n\n      this.emitter.listenDOM('mousedown', document.body, function () {\n        _this3.mouseDown = true;\n      });\n      this.emitter.listenDOM('mouseup', document.body, function () {\n        _this3.mouseDown = false;\n        _this3.update(_emitter4.default.sources.USER);\n      });\n    }\n  }, {\n    key: 'focus',\n    value: function focus() {\n      if (this.hasFocus()) return;\n      this.root.focus();\n      this.setRange(this.savedRange);\n    }\n  }, {\n    key: 'format',\n    value: function format(_format, value) {\n      if (this.scroll.whitelist != null && !this.scroll.whitelist[_format]) return;\n      this.scroll.update();\n      var nativeRange = this.getNativeRange();\n      if (nativeRange == null || !nativeRange.native.collapsed || _parchment2.default.query(_format, _parchment2.default.Scope.BLOCK)) return;\n      if (nativeRange.start.node !== this.cursor.textNode) {\n        var blot = _parchment2.default.find(nativeRange.start.node, false);\n        if (blot == null) return;\n        // TODO Give blot ability to not split\n        if (blot instanceof _parchment2.default.Leaf) {\n          var after = blot.split(nativeRange.start.offset);\n          blot.parent.insertBefore(this.cursor, after);\n        } else {\n          blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen\n        }\n        this.cursor.attach();\n      }\n      this.cursor.format(_format, value);\n      this.scroll.optimize();\n      this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length);\n      this.update();\n    }\n  }, {\n    key: 'getBounds',\n    value: function getBounds(index) {\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      var scrollLength = this.scroll.length();\n      index = Math.min(index, scrollLength - 1);\n      length = Math.min(index + length, scrollLength - 1) - index;\n      var node = void 0,\n          _scroll$leaf = this.scroll.leaf(index),\n          _scroll$leaf2 = _slicedToArray(_scroll$leaf, 2),\n          leaf = _scroll$leaf2[0],\n          offset = _scroll$leaf2[1];\n      if (leaf == null) return null;\n\n      var _leaf$position = leaf.position(offset, true);\n\n      var _leaf$position2 = _slicedToArray(_leaf$position, 2);\n\n      node = _leaf$position2[0];\n      offset = _leaf$position2[1];\n\n      var range = document.createRange();\n      if (length > 0) {\n        range.setStart(node, offset);\n\n        var _scroll$leaf3 = this.scroll.leaf(index + length);\n\n        var _scroll$leaf4 = _slicedToArray(_scroll$leaf3, 2);\n\n        leaf = _scroll$leaf4[0];\n        offset = _scroll$leaf4[1];\n\n        if (leaf == null) return null;\n\n        var _leaf$position3 = leaf.position(offset, true);\n\n        var _leaf$position4 = _slicedToArray(_leaf$position3, 2);\n\n        node = _leaf$position4[0];\n        offset = _leaf$position4[1];\n\n        range.setEnd(node, offset);\n        return range.getBoundingClientRect();\n      } else {\n        var side = 'left';\n        var rect = void 0;\n        if (node instanceof Text) {\n          if (offset < node.data.length) {\n            range.setStart(node, offset);\n            range.setEnd(node, offset + 1);\n          } else {\n            range.setStart(node, offset - 1);\n            range.setEnd(node, offset);\n            side = 'right';\n          }\n          rect = range.getBoundingClientRect();\n        } else {\n          rect = leaf.domNode.getBoundingClientRect();\n          if (offset > 0) side = 'right';\n        }\n        return {\n          bottom: rect.top + rect.height,\n          height: rect.height,\n          left: rect[side],\n          right: rect[side],\n          top: rect.top,\n          width: 0\n        };\n      }\n    }\n  }, {\n    key: 'getNativeRange',\n    value: function getNativeRange() {\n      var selection = document.getSelection();\n      if (selection == null || selection.rangeCount <= 0) return null;\n      var nativeRange = selection.getRangeAt(0);\n      if (nativeRange == null) return null;\n      var range = this.normalizeNative(nativeRange);\n      debug.info('getNativeRange', range);\n      return range;\n    }\n  }, {\n    key: 'getRange',\n    value: function getRange() {\n      var normalized = this.getNativeRange();\n      if (normalized == null) return [null, null];\n      var range = this.normalizedToRange(normalized);\n      return [range, normalized];\n    }\n  }, {\n    key: 'hasFocus',\n    value: function hasFocus() {\n      return document.activeElement === this.root;\n    }\n  }, {\n    key: 'normalizedToRange',\n    value: function normalizedToRange(range) {\n      var _this4 = this;\n\n      var positions = [[range.start.node, range.start.offset]];\n      if (!range.native.collapsed) {\n        positions.push([range.end.node, range.end.offset]);\n      }\n      var indexes = positions.map(function (position) {\n        var _position = _slicedToArray(position, 2),\n            node = _position[0],\n            offset = _position[1];\n\n        var blot = _parchment2.default.find(node, true);\n        var index = blot.offset(_this4.scroll);\n        if (offset === 0) {\n          return index;\n        } else if (blot instanceof _parchment2.default.Container) {\n          return index + blot.length();\n        } else {\n          return index + blot.index(node, offset);\n        }\n      });\n      var end = Math.min(Math.max.apply(Math, _toConsumableArray(indexes)), this.scroll.length() - 1);\n      var start = Math.min.apply(Math, [end].concat(_toConsumableArray(indexes)));\n      return new Range(start, end - start);\n    }\n  }, {\n    key: 'normalizeNative',\n    value: function normalizeNative(nativeRange) {\n      if (!contains(this.root, nativeRange.startContainer) || !nativeRange.collapsed && !contains(this.root, nativeRange.endContainer)) {\n        return null;\n      }\n      var range = {\n        start: { node: nativeRange.startContainer, offset: nativeRange.startOffset },\n        end: { node: nativeRange.endContainer, offset: nativeRange.endOffset },\n        native: nativeRange\n      };\n      [range.start, range.end].forEach(function (position) {\n        var node = position.node,\n            offset = position.offset;\n        while (!(node instanceof Text) && node.childNodes.length > 0) {\n          if (node.childNodes.length > offset) {\n            node = node.childNodes[offset];\n            offset = 0;\n          } else if (node.childNodes.length === offset) {\n            node = node.lastChild;\n            offset = node instanceof Text ? node.data.length : node.childNodes.length + 1;\n          } else {\n            break;\n          }\n        }\n        position.node = node, position.offset = offset;\n      });\n      return range;\n    }\n  }, {\n    key: 'rangeToNative',\n    value: function rangeToNative(range) {\n      var _this5 = this;\n\n      var indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length];\n      var args = [];\n      var scrollLength = this.scroll.length();\n      indexes.forEach(function (index, i) {\n        index = Math.min(scrollLength - 1, index);\n        var node = void 0,\n            _scroll$leaf5 = _this5.scroll.leaf(index),\n            _scroll$leaf6 = _slicedToArray(_scroll$leaf5, 2),\n            leaf = _scroll$leaf6[0],\n            offset = _scroll$leaf6[1];\n        var _leaf$position5 = leaf.position(offset, i !== 0);\n\n        var _leaf$position6 = _slicedToArray(_leaf$position5, 2);\n\n        node = _leaf$position6[0];\n        offset = _leaf$position6[1];\n\n        args.push(node, offset);\n      });\n      if (args.length < 2) {\n        args = args.concat(args);\n      }\n      return args;\n    }\n  }, {\n    key: 'scrollIntoView',\n    value: function scrollIntoView(scrollingContainer) {\n      var range = this.lastRange;\n      if (range == null) return;\n      var bounds = this.getBounds(range.index, range.length);\n      if (bounds == null) return;\n      var limit = this.scroll.length() - 1;\n\n      var _scroll$line = this.scroll.line(Math.min(range.index, limit)),\n          _scroll$line2 = _slicedToArray(_scroll$line, 1),\n          first = _scroll$line2[0];\n\n      var last = first;\n      if (range.length > 0) {\n        var _scroll$line3 = this.scroll.line(Math.min(range.index + range.length, limit));\n\n        var _scroll$line4 = _slicedToArray(_scroll$line3, 1);\n\n        last = _scroll$line4[0];\n      }\n      if (first == null || last == null) return;\n      var scrollBounds = scrollingContainer.getBoundingClientRect();\n      if (bounds.top < scrollBounds.top) {\n        scrollingContainer.scrollTop -= scrollBounds.top - bounds.top;\n      } else if (bounds.bottom > scrollBounds.bottom) {\n        scrollingContainer.scrollTop += bounds.bottom - scrollBounds.bottom;\n      }\n    }\n  }, {\n    key: 'setNativeRange',\n    value: function setNativeRange(startNode, startOffset) {\n      var endNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : startNode;\n      var endOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : startOffset;\n      var force = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n\n      debug.info('setNativeRange', startNode, startOffset, endNode, endOffset);\n      if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) {\n        return;\n      }\n      var selection = document.getSelection();\n      if (selection == null) return;\n      if (startNode != null) {\n        if (!this.hasFocus()) this.root.focus();\n        var native = (this.getNativeRange() || {}).native;\n        if (native == null || force || startNode !== native.startContainer || startOffset !== native.startOffset || endNode !== native.endContainer || endOffset !== native.endOffset) {\n\n          if (startNode.tagName == \"BR\") {\n            startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode);\n            startNode = startNode.parentNode;\n          }\n          if (endNode.tagName == \"BR\") {\n            endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode);\n            endNode = endNode.parentNode;\n          }\n          var range = document.createRange();\n          range.setStart(startNode, startOffset);\n          range.setEnd(endNode, endOffset);\n          selection.removeAllRanges();\n          selection.addRange(range);\n        }\n      } else {\n        selection.removeAllRanges();\n        this.root.blur();\n        document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs)\n      }\n    }\n  }, {\n    key: 'setRange',\n    value: function setRange(range) {\n      var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n      var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API;\n\n      if (typeof force === 'string') {\n        source = force;\n        force = false;\n      }\n      debug.info('setRange', range);\n      if (range != null) {\n        var args = this.rangeToNative(range);\n        this.setNativeRange.apply(this, _toConsumableArray(args).concat([force]));\n      } else {\n        this.setNativeRange(null);\n      }\n      this.update(source);\n    }\n  }, {\n    key: 'update',\n    value: function update() {\n      var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER;\n\n      var oldRange = this.lastRange;\n\n      var _getRange = this.getRange(),\n          _getRange2 = _slicedToArray(_getRange, 2),\n          lastRange = _getRange2[0],\n          nativeRange = _getRange2[1];\n\n      this.lastRange = lastRange;\n      if (this.lastRange != null) {\n        this.savedRange = this.lastRange;\n      }\n      if (!(0, _deepEqual2.default)(oldRange, this.lastRange)) {\n        var _emitter;\n\n        if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) {\n          this.cursor.restore();\n        }\n        var args = [_emitter4.default.events.SELECTION_CHANGE, (0, _clone2.default)(this.lastRange), (0, _clone2.default)(oldRange), source];\n        (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args));\n        if (source !== _emitter4.default.sources.SILENT) {\n          var _emitter2;\n\n          (_emitter2 = this.emitter).emit.apply(_emitter2, args);\n        }\n      }\n    }\n  }]);\n\n  return Selection;\n}();\n\nfunction contains(parent, descendant) {\n  try {\n    // Firefox inserts inaccessible nodes around video elements\n    descendant.parentNode;\n  } catch (e) {\n    return false;\n  }\n  // IE11 has bug with Text nodes\n  // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect\n  if (descendant instanceof Text) {\n    descendant = descendant.parentNode;\n  }\n  return parent.contains(descendant);\n}\n\nexports.Range = Range;\nexports.default = Selection;\n\n/***/ }),\n/* 16 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Break = function (_Parchment$Embed) {\n  _inherits(Break, _Parchment$Embed);\n\n  function Break() {\n    _classCallCheck(this, Break);\n\n    return _possibleConstructorReturn(this, (Break.__proto__ || Object.getPrototypeOf(Break)).apply(this, arguments));\n  }\n\n  _createClass(Break, [{\n    key: 'insertInto',\n    value: function insertInto(parent, ref) {\n      if (parent.children.length === 0) {\n        _get(Break.prototype.__proto__ || Object.getPrototypeOf(Break.prototype), 'insertInto', this).call(this, parent, ref);\n      } else {\n        this.remove();\n      }\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      return 0;\n    }\n  }, {\n    key: 'value',\n    value: function value() {\n      return '';\n    }\n  }], [{\n    key: 'value',\n    value: function value() {\n      return undefined;\n    }\n  }]);\n\n  return Break;\n}(_parchment2.default.Embed);\n\nBreak.blotName = 'break';\nBreak.tagName = 'BR';\n\nexports.default = Break;\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar linked_list_1 = __webpack_require__(44);\nvar shadow_1 = __webpack_require__(30);\nvar Registry = __webpack_require__(1);\nvar ContainerBlot = /** @class */ (function (_super) {\n    __extends(ContainerBlot, _super);\n    function ContainerBlot(domNode) {\n        var _this = _super.call(this, domNode) || this;\n        _this.build();\n        return _this;\n    }\n    ContainerBlot.prototype.appendChild = function (other) {\n        this.insertBefore(other);\n    };\n    ContainerBlot.prototype.attach = function () {\n        _super.prototype.attach.call(this);\n        this.children.forEach(function (child) {\n            child.attach();\n        });\n    };\n    ContainerBlot.prototype.build = function () {\n        var _this = this;\n        this.children = new linked_list_1.default();\n        // Need to be reversed for if DOM nodes already in order\n        [].slice\n            .call(this.domNode.childNodes)\n            .reverse()\n            .forEach(function (node) {\n            try {\n                var child = makeBlot(node);\n                _this.insertBefore(child, _this.children.head || undefined);\n            }\n            catch (err) {\n                if (err instanceof Registry.ParchmentError)\n                    return;\n                else\n                    throw err;\n            }\n        });\n    };\n    ContainerBlot.prototype.deleteAt = function (index, length) {\n        if (index === 0 && length === this.length()) {\n            return this.remove();\n        }\n        this.children.forEachAt(index, length, function (child, offset, length) {\n            child.deleteAt(offset, length);\n        });\n    };\n    ContainerBlot.prototype.descendant = function (criteria, index) {\n        var _a = this.children.find(index), child = _a[0], offset = _a[1];\n        if ((criteria.blotName == null && criteria(child)) ||\n            (criteria.blotName != null && child instanceof criteria)) {\n            return [child, offset];\n        }\n        else if (child instanceof ContainerBlot) {\n            return child.descendant(criteria, offset);\n        }\n        else {\n            return [null, -1];\n        }\n    };\n    ContainerBlot.prototype.descendants = function (criteria, index, length) {\n        if (index === void 0) { index = 0; }\n        if (length === void 0) { length = Number.MAX_VALUE; }\n        var descendants = [];\n        var lengthLeft = length;\n        this.children.forEachAt(index, length, function (child, index, length) {\n            if ((criteria.blotName == null && criteria(child)) ||\n                (criteria.blotName != null && child instanceof criteria)) {\n                descendants.push(child);\n            }\n            if (child instanceof ContainerBlot) {\n                descendants = descendants.concat(child.descendants(criteria, index, lengthLeft));\n            }\n            lengthLeft -= length;\n        });\n        return descendants;\n    };\n    ContainerBlot.prototype.detach = function () {\n        this.children.forEach(function (child) {\n            child.detach();\n        });\n        _super.prototype.detach.call(this);\n    };\n    ContainerBlot.prototype.formatAt = function (index, length, name, value) {\n        this.children.forEachAt(index, length, function (child, offset, length) {\n            child.formatAt(offset, length, name, value);\n        });\n    };\n    ContainerBlot.prototype.insertAt = function (index, value, def) {\n        var _a = this.children.find(index), child = _a[0], offset = _a[1];\n        if (child) {\n            child.insertAt(offset, value, def);\n        }\n        else {\n            var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n            this.appendChild(blot);\n        }\n    };\n    ContainerBlot.prototype.insertBefore = function (childBlot, refBlot) {\n        if (this.statics.allowedChildren != null &&\n            !this.statics.allowedChildren.some(function (child) {\n                return childBlot instanceof child;\n            })) {\n            throw new Registry.ParchmentError(\"Cannot insert \" + childBlot.statics.blotName + \" into \" + this.statics.blotName);\n        }\n        childBlot.insertInto(this, refBlot);\n    };\n    ContainerBlot.prototype.length = function () {\n        return this.children.reduce(function (memo, child) {\n            return memo + child.length();\n        }, 0);\n    };\n    ContainerBlot.prototype.moveChildren = function (targetParent, refNode) {\n        this.children.forEach(function (child) {\n            targetParent.insertBefore(child, refNode);\n        });\n    };\n    ContainerBlot.prototype.optimize = function (context) {\n        _super.prototype.optimize.call(this, context);\n        if (this.children.length === 0) {\n            if (this.statics.defaultChild != null) {\n                var child = Registry.create(this.statics.defaultChild);\n                this.appendChild(child);\n                child.optimize(context);\n            }\n            else {\n                this.remove();\n            }\n        }\n    };\n    ContainerBlot.prototype.path = function (index, inclusive) {\n        if (inclusive === void 0) { inclusive = false; }\n        var _a = this.children.find(index, inclusive), child = _a[0], offset = _a[1];\n        var position = [[this, index]];\n        if (child instanceof ContainerBlot) {\n            return position.concat(child.path(offset, inclusive));\n        }\n        else if (child != null) {\n            position.push([child, offset]);\n        }\n        return position;\n    };\n    ContainerBlot.prototype.removeChild = function (child) {\n        this.children.remove(child);\n    };\n    ContainerBlot.prototype.replace = function (target) {\n        if (target instanceof ContainerBlot) {\n            target.moveChildren(this);\n        }\n        _super.prototype.replace.call(this, target);\n    };\n    ContainerBlot.prototype.split = function (index, force) {\n        if (force === void 0) { force = false; }\n        if (!force) {\n            if (index === 0)\n                return this;\n            if (index === this.length())\n                return this.next;\n        }\n        var after = this.clone();\n        this.parent.insertBefore(after, this.next);\n        this.children.forEachAt(index, this.length(), function (child, offset, length) {\n            child = child.split(offset, force);\n            after.appendChild(child);\n        });\n        return after;\n    };\n    ContainerBlot.prototype.unwrap = function () {\n        this.moveChildren(this.parent, this.next);\n        this.remove();\n    };\n    ContainerBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        var addedNodes = [];\n        var removedNodes = [];\n        mutations.forEach(function (mutation) {\n            if (mutation.target === _this.domNode && mutation.type === 'childList') {\n                addedNodes.push.apply(addedNodes, mutation.addedNodes);\n                removedNodes.push.apply(removedNodes, mutation.removedNodes);\n            }\n        });\n        removedNodes.forEach(function (node) {\n            // Check node has actually been removed\n            // One exception is Chrome does not immediately remove IFRAMEs\n            // from DOM but MutationRecord is correct in its reported removal\n            if (node.parentNode != null &&\n                // @ts-ignore\n                node.tagName !== 'IFRAME' &&\n                document.body.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n                return;\n            }\n            var blot = Registry.find(node);\n            if (blot == null)\n                return;\n            if (blot.domNode.parentNode == null || blot.domNode.parentNode === _this.domNode) {\n                blot.detach();\n            }\n        });\n        addedNodes\n            .filter(function (node) {\n            return node.parentNode == _this.domNode;\n        })\n            .sort(function (a, b) {\n            if (a === b)\n                return 0;\n            if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) {\n                return 1;\n            }\n            return -1;\n        })\n            .forEach(function (node) {\n            var refBlot = null;\n            if (node.nextSibling != null) {\n                refBlot = Registry.find(node.nextSibling);\n            }\n            var blot = makeBlot(node);\n            if (blot.next != refBlot || blot.next == null) {\n                if (blot.parent != null) {\n                    blot.parent.removeChild(_this);\n                }\n                _this.insertBefore(blot, refBlot || undefined);\n            }\n        });\n    };\n    return ContainerBlot;\n}(shadow_1.default));\nfunction makeBlot(node) {\n    var blot = Registry.find(node);\n    if (blot == null) {\n        try {\n            blot = Registry.create(node);\n        }\n        catch (e) {\n            blot = Registry.create(Registry.Scope.INLINE);\n            [].slice.call(node.childNodes).forEach(function (child) {\n                // @ts-ignore\n                blot.domNode.appendChild(child);\n            });\n            if (node.parentNode) {\n                node.parentNode.replaceChild(blot.domNode, node);\n            }\n            blot.attach();\n        }\n    }\n    return blot;\n}\nexports.default = ContainerBlot;\n\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nvar store_1 = __webpack_require__(31);\nvar container_1 = __webpack_require__(17);\nvar Registry = __webpack_require__(1);\nvar FormatBlot = /** @class */ (function (_super) {\n    __extends(FormatBlot, _super);\n    function FormatBlot(domNode) {\n        var _this = _super.call(this, domNode) || this;\n        _this.attributes = new store_1.default(_this.domNode);\n        return _this;\n    }\n    FormatBlot.formats = function (domNode) {\n        if (typeof this.tagName === 'string') {\n            return true;\n        }\n        else if (Array.isArray(this.tagName)) {\n            return domNode.tagName.toLowerCase();\n        }\n        return undefined;\n    };\n    FormatBlot.prototype.format = function (name, value) {\n        var format = Registry.query(name);\n        if (format instanceof attributor_1.default) {\n            this.attributes.attribute(format, value);\n        }\n        else if (value) {\n            if (format != null && (name !== this.statics.blotName || this.formats()[name] !== value)) {\n                this.replaceWith(name, value);\n            }\n        }\n    };\n    FormatBlot.prototype.formats = function () {\n        var formats = this.attributes.values();\n        var format = this.statics.formats(this.domNode);\n        if (format != null) {\n            formats[this.statics.blotName] = format;\n        }\n        return formats;\n    };\n    FormatBlot.prototype.replaceWith = function (name, value) {\n        var replacement = _super.prototype.replaceWith.call(this, name, value);\n        this.attributes.copy(replacement);\n        return replacement;\n    };\n    FormatBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        _super.prototype.update.call(this, mutations, context);\n        if (mutations.some(function (mutation) {\n            return mutation.target === _this.domNode && mutation.type === 'attributes';\n        })) {\n            this.attributes.build();\n        }\n    };\n    FormatBlot.prototype.wrap = function (name, value) {\n        var wrapper = _super.prototype.wrap.call(this, name, value);\n        if (wrapper instanceof FormatBlot && wrapper.statics.scope === this.statics.scope) {\n            this.attributes.move(wrapper);\n        }\n        return wrapper;\n    };\n    return FormatBlot;\n}(container_1.default));\nexports.default = FormatBlot;\n\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar shadow_1 = __webpack_require__(30);\nvar Registry = __webpack_require__(1);\nvar LeafBlot = /** @class */ (function (_super) {\n    __extends(LeafBlot, _super);\n    function LeafBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    LeafBlot.value = function (domNode) {\n        return true;\n    };\n    LeafBlot.prototype.index = function (node, offset) {\n        if (this.domNode === node ||\n            this.domNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n            return Math.min(offset, 1);\n        }\n        return -1;\n    };\n    LeafBlot.prototype.position = function (index, inclusive) {\n        var offset = [].indexOf.call(this.parent.domNode.childNodes, this.domNode);\n        if (index > 0)\n            offset += 1;\n        return [this.parent.domNode, offset];\n    };\n    LeafBlot.prototype.value = function () {\n        var _a;\n        return _a = {}, _a[this.statics.blotName] = this.statics.value(this.domNode) || true, _a;\n    };\n    LeafBlot.scope = Registry.Scope.INLINE_BLOT;\n    return LeafBlot;\n}(shadow_1.default));\nexports.default = LeafBlot;\n\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar equal = __webpack_require__(11);\nvar extend = __webpack_require__(3);\n\n\nvar lib = {\n  attributes: {\n    compose: function (a, b, keepNull) {\n      if (typeof a !== 'object') a = {};\n      if (typeof b !== 'object') b = {};\n      var attributes = extend(true, {}, b);\n      if (!keepNull) {\n        attributes = Object.keys(attributes).reduce(function (copy, key) {\n          if (attributes[key] != null) {\n            copy[key] = attributes[key];\n          }\n          return copy;\n        }, {});\n      }\n      for (var key in a) {\n        if (a[key] !== undefined && b[key] === undefined) {\n          attributes[key] = a[key];\n        }\n      }\n      return Object.keys(attributes).length > 0 ? attributes : undefined;\n    },\n\n    diff: function(a, b) {\n      if (typeof a !== 'object') a = {};\n      if (typeof b !== 'object') b = {};\n      var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) {\n        if (!equal(a[key], b[key])) {\n          attributes[key] = b[key] === undefined ? null : b[key];\n        }\n        return attributes;\n      }, {});\n      return Object.keys(attributes).length > 0 ? attributes : undefined;\n    },\n\n    transform: function (a, b, priority) {\n      if (typeof a !== 'object') return b;\n      if (typeof b !== 'object') return undefined;\n      if (!priority) return b;  // b simply overwrites us without priority\n      var attributes = Object.keys(b).reduce(function (attributes, key) {\n        if (a[key] === undefined) attributes[key] = b[key];  // null is a valid value\n        return attributes;\n      }, {});\n      return Object.keys(attributes).length > 0 ? attributes : undefined;\n    }\n  },\n\n  iterator: function (ops) {\n    return new Iterator(ops);\n  },\n\n  length: function (op) {\n    if (typeof op['delete'] === 'number') {\n      return op['delete'];\n    } else if (typeof op.retain === 'number') {\n      return op.retain;\n    } else {\n      return typeof op.insert === 'string' ? op.insert.length : 1;\n    }\n  }\n};\n\n\nfunction Iterator(ops) {\n  this.ops = ops;\n  this.index = 0;\n  this.offset = 0;\n};\n\nIterator.prototype.hasNext = function () {\n  return this.peekLength() < Infinity;\n};\n\nIterator.prototype.next = function (length) {\n  if (!length) length = Infinity;\n  var nextOp = this.ops[this.index];\n  if (nextOp) {\n    var offset = this.offset;\n    var opLength = lib.length(nextOp)\n    if (length >= opLength - offset) {\n      length = opLength - offset;\n      this.index += 1;\n      this.offset = 0;\n    } else {\n      this.offset += length;\n    }\n    if (typeof nextOp['delete'] === 'number') {\n      return { 'delete': length };\n    } else {\n      var retOp = {};\n      if (nextOp.attributes) {\n        retOp.attributes = nextOp.attributes;\n      }\n      if (typeof nextOp.retain === 'number') {\n        retOp.retain = length;\n      } else if (typeof nextOp.insert === 'string') {\n        retOp.insert = nextOp.insert.substr(offset, length);\n      } else {\n        // offset should === 0, length should === 1\n        retOp.insert = nextOp.insert;\n      }\n      return retOp;\n    }\n  } else {\n    return { retain: Infinity };\n  }\n};\n\nIterator.prototype.peek = function () {\n  return this.ops[this.index];\n};\n\nIterator.prototype.peekLength = function () {\n  if (this.ops[this.index]) {\n    // Should never return 0 if our index is being managed correctly\n    return lib.length(this.ops[this.index]) - this.offset;\n  } else {\n    return Infinity;\n  }\n};\n\nIterator.prototype.peekType = function () {\n  if (this.ops[this.index]) {\n    if (typeof this.ops[this.index]['delete'] === 'number') {\n      return 'delete';\n    } else if (typeof this.ops[this.index].retain === 'number') {\n      return 'retain';\n    } else {\n      return 'insert';\n    }\n  }\n  return 'retain';\n};\n\nIterator.prototype.rest = function () {\n  if (!this.hasNext()) {\n    return [];\n  } else if (this.offset === 0) {\n    return this.ops.slice(this.index);\n  } else {\n    var offset = this.offset;\n    var index = this.index;\n    var next = this.next();\n    var rest = this.ops.slice(this.index);\n    this.offset = offset;\n    this.index = index;\n    return [next].concat(rest);\n  }\n};\n\n\nmodule.exports = lib;\n\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports) {\n\nvar clone = (function() {\n'use strict';\n\nfunction _instanceof(obj, type) {\n  return type != null && obj instanceof type;\n}\n\nvar nativeMap;\ntry {\n  nativeMap = Map;\n} catch(_) {\n  // maybe a reference error because no `Map`. Give it a dummy value that no\n  // value will ever be an instanceof.\n  nativeMap = function() {};\n}\n\nvar nativeSet;\ntry {\n  nativeSet = Set;\n} catch(_) {\n  nativeSet = function() {};\n}\n\nvar nativePromise;\ntry {\n  nativePromise = Promise;\n} catch(_) {\n  nativePromise = function() {};\n}\n\n/**\n * Clones (copies) an Object using deep copying.\n *\n * This function supports circular references by default, but if you are certain\n * there are no circular references in your object, you can save some CPU time\n * by calling clone(obj, false).\n *\n * Caution: if `circular` is false and `parent` contains circular references,\n * your program may enter an infinite loop and crash.\n *\n * @param `parent` - the object to be cloned\n * @param `circular` - set to true if the object to be cloned may contain\n *    circular references. (optional - true by default)\n * @param `depth` - set to a number if the object is only to be cloned to\n *    a particular depth. (optional - defaults to Infinity)\n * @param `prototype` - sets the prototype to be used when cloning an object.\n *    (optional - defaults to parent prototype).\n * @param `includeNonEnumerable` - set to true if the non-enumerable properties\n *    should be cloned as well. Non-enumerable properties on the prototype\n *    chain will be ignored. (optional - false by default)\n*/\nfunction clone(parent, circular, depth, prototype, includeNonEnumerable) {\n  if (typeof circular === 'object') {\n    depth = circular.depth;\n    prototype = circular.prototype;\n    includeNonEnumerable = circular.includeNonEnumerable;\n    circular = circular.circular;\n  }\n  // maintain two arrays for circular references, where corresponding parents\n  // and children have the same index\n  var allParents = [];\n  var allChildren = [];\n\n  var useBuffer = typeof Buffer != 'undefined';\n\n  if (typeof circular == 'undefined')\n    circular = true;\n\n  if (typeof depth == 'undefined')\n    depth = Infinity;\n\n  // recurse this function so we don't reset allParents and allChildren\n  function _clone(parent, depth) {\n    // cloning null always returns null\n    if (parent === null)\n      return null;\n\n    if (depth === 0)\n      return parent;\n\n    var child;\n    var proto;\n    if (typeof parent != 'object') {\n      return parent;\n    }\n\n    if (_instanceof(parent, nativeMap)) {\n      child = new nativeMap();\n    } else if (_instanceof(parent, nativeSet)) {\n      child = new nativeSet();\n    } else if (_instanceof(parent, nativePromise)) {\n      child = new nativePromise(function (resolve, reject) {\n        parent.then(function(value) {\n          resolve(_clone(value, depth - 1));\n        }, function(err) {\n          reject(_clone(err, depth - 1));\n        });\n      });\n    } else if (clone.__isArray(parent)) {\n      child = [];\n    } else if (clone.__isRegExp(parent)) {\n      child = new RegExp(parent.source, __getRegExpFlags(parent));\n      if (parent.lastIndex) child.lastIndex = parent.lastIndex;\n    } else if (clone.__isDate(parent)) {\n      child = new Date(parent.getTime());\n    } else if (useBuffer && Buffer.isBuffer(parent)) {\n      if (Buffer.allocUnsafe) {\n        // Node.js >= 4.5.0\n        child = Buffer.allocUnsafe(parent.length);\n      } else {\n        // Older Node.js versions\n        child = new Buffer(parent.length);\n      }\n      parent.copy(child);\n      return child;\n    } else if (_instanceof(parent, Error)) {\n      child = Object.create(parent);\n    } else {\n      if (typeof prototype == 'undefined') {\n        proto = Object.getPrototypeOf(parent);\n        child = Object.create(proto);\n      }\n      else {\n        child = Object.create(prototype);\n        proto = prototype;\n      }\n    }\n\n    if (circular) {\n      var index = allParents.indexOf(parent);\n\n      if (index != -1) {\n        return allChildren[index];\n      }\n      allParents.push(parent);\n      allChildren.push(child);\n    }\n\n    if (_instanceof(parent, nativeMap)) {\n      parent.forEach(function(value, key) {\n        var keyChild = _clone(key, depth - 1);\n        var valueChild = _clone(value, depth - 1);\n        child.set(keyChild, valueChild);\n      });\n    }\n    if (_instanceof(parent, nativeSet)) {\n      parent.forEach(function(value) {\n        var entryChild = _clone(value, depth - 1);\n        child.add(entryChild);\n      });\n    }\n\n    for (var i in parent) {\n      var attrs;\n      if (proto) {\n        attrs = Object.getOwnPropertyDescriptor(proto, i);\n      }\n\n      if (attrs && attrs.set == null) {\n        continue;\n      }\n      child[i] = _clone(parent[i], depth - 1);\n    }\n\n    if (Object.getOwnPropertySymbols) {\n      var symbols = Object.getOwnPropertySymbols(parent);\n      for (var i = 0; i < symbols.length; i++) {\n        // Don't need to worry about cloning a symbol because it is a primitive,\n        // like a number or string.\n        var symbol = symbols[i];\n        var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);\n        if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {\n          continue;\n        }\n        child[symbol] = _clone(parent[symbol], depth - 1);\n        if (!descriptor.enumerable) {\n          Object.defineProperty(child, symbol, {\n            enumerable: false\n          });\n        }\n      }\n    }\n\n    if (includeNonEnumerable) {\n      var allPropertyNames = Object.getOwnPropertyNames(parent);\n      for (var i = 0; i < allPropertyNames.length; i++) {\n        var propertyName = allPropertyNames[i];\n        var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);\n        if (descriptor && descriptor.enumerable) {\n          continue;\n        }\n        child[propertyName] = _clone(parent[propertyName], depth - 1);\n        Object.defineProperty(child, propertyName, {\n          enumerable: false\n        });\n      }\n    }\n\n    return child;\n  }\n\n  return _clone(parent, depth);\n}\n\n/**\n * Simple flat clone using prototype, accepts only objects, usefull for property\n * override on FLAT configuration object (no nested props).\n *\n * USE WITH CAUTION! This may not behave as you wish if you do not know how this\n * works.\n */\nclone.clonePrototype = function clonePrototype(parent) {\n  if (parent === null)\n    return null;\n\n  var c = function () {};\n  c.prototype = parent;\n  return new c();\n};\n\n// private utility functions\n\nfunction __objToStr(o) {\n  return Object.prototype.toString.call(o);\n}\nclone.__objToStr = __objToStr;\n\nfunction __isDate(o) {\n  return typeof o === 'object' && __objToStr(o) === '[object Date]';\n}\nclone.__isDate = __isDate;\n\nfunction __isArray(o) {\n  return typeof o === 'object' && __objToStr(o) === '[object Array]';\n}\nclone.__isArray = __isArray;\n\nfunction __isRegExp(o) {\n  return typeof o === 'object' && __objToStr(o) === '[object RegExp]';\n}\nclone.__isRegExp = __isRegExp;\n\nfunction __getRegExpFlags(re) {\n  var flags = '';\n  if (re.global) flags += 'g';\n  if (re.ignoreCase) flags += 'i';\n  if (re.multiline) flags += 'm';\n  return flags;\n}\nclone.__getRegExpFlags = __getRegExpFlags;\n\nreturn clone;\n})();\n\nif (typeof module === 'object' && module.exports) {\n  module.exports = clone;\n}\n\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _emitter = __webpack_require__(8);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _container = __webpack_require__(25);\n\nvar _container2 = _interopRequireDefault(_container);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nfunction isLine(blot) {\n  return blot instanceof _block2.default || blot instanceof _block.BlockEmbed;\n}\n\nvar Scroll = function (_Parchment$Scroll) {\n  _inherits(Scroll, _Parchment$Scroll);\n\n  function Scroll(domNode, config) {\n    _classCallCheck(this, Scroll);\n\n    var _this = _possibleConstructorReturn(this, (Scroll.__proto__ || Object.getPrototypeOf(Scroll)).call(this, domNode));\n\n    _this.emitter = config.emitter;\n    if (Array.isArray(config.whitelist)) {\n      _this.whitelist = config.whitelist.reduce(function (whitelist, format) {\n        whitelist[format] = true;\n        return whitelist;\n      }, {});\n    }\n    // Some reason fixes composition issues with character languages in Windows/Chrome, Safari\n    _this.domNode.addEventListener('DOMNodeInserted', function () {});\n    _this.optimize();\n    _this.enable();\n    return _this;\n  }\n\n  _createClass(Scroll, [{\n    key: 'batchStart',\n    value: function batchStart() {\n      this.batch = true;\n    }\n  }, {\n    key: 'batchEnd',\n    value: function batchEnd() {\n      this.batch = false;\n      this.optimize();\n    }\n  }, {\n    key: 'deleteAt',\n    value: function deleteAt(index, length) {\n      var _line = this.line(index),\n          _line2 = _slicedToArray(_line, 2),\n          first = _line2[0],\n          offset = _line2[1];\n\n      var _line3 = this.line(index + length),\n          _line4 = _slicedToArray(_line3, 1),\n          last = _line4[0];\n\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'deleteAt', this).call(this, index, length);\n      if (last != null && first !== last && offset > 0) {\n        if (first instanceof _block.BlockEmbed || last instanceof _block.BlockEmbed) {\n          this.optimize();\n          return;\n        }\n        if (first instanceof _code2.default) {\n          var newlineIndex = first.newlineIndex(first.length(), true);\n          if (newlineIndex > -1) {\n            first = first.split(newlineIndex + 1);\n            if (first === last) {\n              this.optimize();\n              return;\n            }\n          }\n        } else if (last instanceof _code2.default) {\n          var _newlineIndex = last.newlineIndex(0);\n          if (_newlineIndex > -1) {\n            last.split(_newlineIndex + 1);\n          }\n        }\n        var ref = last.children.head instanceof _break2.default ? null : last.children.head;\n        first.moveChildren(last, ref);\n        first.remove();\n      }\n      this.optimize();\n    }\n  }, {\n    key: 'enable',\n    value: function enable() {\n      var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n\n      this.domNode.setAttribute('contenteditable', enabled);\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, format, value) {\n      if (this.whitelist != null && !this.whitelist[format]) return;\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'formatAt', this).call(this, index, length, format, value);\n      this.optimize();\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (def != null && this.whitelist != null && !this.whitelist[value]) return;\n      if (index >= this.length()) {\n        if (def == null || _parchment2.default.query(value, _parchment2.default.Scope.BLOCK) == null) {\n          var blot = _parchment2.default.create(this.statics.defaultChild);\n          this.appendChild(blot);\n          if (def == null && value.endsWith('\\n')) {\n            value = value.slice(0, -1);\n          }\n          blot.insertAt(0, value, def);\n        } else {\n          var embed = _parchment2.default.create(value, def);\n          this.appendChild(embed);\n        }\n      } else {\n        _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertAt', this).call(this, index, value, def);\n      }\n      this.optimize();\n    }\n  }, {\n    key: 'insertBefore',\n    value: function insertBefore(blot, ref) {\n      if (blot.statics.scope === _parchment2.default.Scope.INLINE_BLOT) {\n        var wrapper = _parchment2.default.create(this.statics.defaultChild);\n        wrapper.appendChild(blot);\n        blot = wrapper;\n      }\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertBefore', this).call(this, blot, ref);\n    }\n  }, {\n    key: 'leaf',\n    value: function leaf(index) {\n      return this.path(index).pop() || [null, -1];\n    }\n  }, {\n    key: 'line',\n    value: function line(index) {\n      if (index === this.length()) {\n        return this.line(index - 1);\n      }\n      return this.descendant(isLine, index);\n    }\n  }, {\n    key: 'lines',\n    value: function lines() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;\n\n      var getLines = function getLines(blot, index, length) {\n        var lines = [],\n            lengthLeft = length;\n        blot.children.forEachAt(index, length, function (child, index, length) {\n          if (isLine(child)) {\n            lines.push(child);\n          } else if (child instanceof _parchment2.default.Container) {\n            lines = lines.concat(getLines(child, index, lengthLeft));\n          }\n          lengthLeft -= length;\n        });\n        return lines;\n      };\n      return getLines(this, index, length);\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize() {\n      var mutations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n      var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n      if (this.batch === true) return;\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'optimize', this).call(this, mutations, context);\n      if (mutations.length > 0) {\n        this.emitter.emit(_emitter2.default.events.SCROLL_OPTIMIZE, mutations, context);\n      }\n    }\n  }, {\n    key: 'path',\n    value: function path(index) {\n      return _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'path', this).call(this, index).slice(1); // Exclude self\n    }\n  }, {\n    key: 'update',\n    value: function update(mutations) {\n      if (this.batch === true) return;\n      var source = _emitter2.default.sources.USER;\n      if (typeof mutations === 'string') {\n        source = mutations;\n      }\n      if (!Array.isArray(mutations)) {\n        mutations = this.observer.takeRecords();\n      }\n      if (mutations.length > 0) {\n        this.emitter.emit(_emitter2.default.events.SCROLL_BEFORE_UPDATE, source, mutations);\n      }\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'update', this).call(this, mutations.concat([])); // pass copy\n      if (mutations.length > 0) {\n        this.emitter.emit(_emitter2.default.events.SCROLL_UPDATE, source, mutations);\n      }\n    }\n  }]);\n\n  return Scroll;\n}(_parchment2.default.Scroll);\n\nScroll.blotName = 'scroll';\nScroll.className = 'ql-editor';\nScroll.tagName = 'DIV';\nScroll.defaultChild = 'block';\nScroll.allowedChildren = [_block2.default, _block.BlockEmbed, _container2.default];\n\nexports.default = Scroll;\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.SHORTKEY = exports.default = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(11);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _op = __webpack_require__(20);\n\nvar _op2 = _interopRequireDefault(_op);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:keyboard');\n\nvar SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey';\n\nvar Keyboard = function (_Module) {\n  _inherits(Keyboard, _Module);\n\n  _createClass(Keyboard, null, [{\n    key: 'match',\n    value: function match(evt, binding) {\n      binding = normalize(binding);\n      if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function (key) {\n        return !!binding[key] !== evt[key] && binding[key] !== null;\n      })) {\n        return false;\n      }\n      return binding.key === (evt.which || evt.keyCode);\n    }\n  }]);\n\n  function Keyboard(quill, options) {\n    _classCallCheck(this, Keyboard);\n\n    var _this = _possibleConstructorReturn(this, (Keyboard.__proto__ || Object.getPrototypeOf(Keyboard)).call(this, quill, options));\n\n    _this.bindings = {};\n    Object.keys(_this.options.bindings).forEach(function (name) {\n      if (name === 'list autofill' && quill.scroll.whitelist != null && !quill.scroll.whitelist['list']) {\n        return;\n      }\n      if (_this.options.bindings[name]) {\n        _this.addBinding(_this.options.bindings[name]);\n      }\n    });\n    _this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter);\n    _this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function () {});\n    if (/Firefox/i.test(navigator.userAgent)) {\n      // Need to handle delete and backspace for Firefox in the general case #1171\n      _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace);\n      _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete);\n    } else {\n      _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace);\n      _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete);\n    }\n    _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange);\n    _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange);\n    _this.addBinding({ key: Keyboard.keys.BACKSPACE, altKey: null, ctrlKey: null, metaKey: null, shiftKey: null }, { collapsed: true, offset: 0 }, handleBackspace);\n    _this.listen();\n    return _this;\n  }\n\n  _createClass(Keyboard, [{\n    key: 'addBinding',\n    value: function addBinding(key) {\n      var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n      var handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      var binding = normalize(key);\n      if (binding == null || binding.key == null) {\n        return debug.warn('Attempted to add invalid keyboard binding', binding);\n      }\n      if (typeof context === 'function') {\n        context = { handler: context };\n      }\n      if (typeof handler === 'function') {\n        handler = { handler: handler };\n      }\n      binding = (0, _extend2.default)(binding, context, handler);\n      this.bindings[binding.key] = this.bindings[binding.key] || [];\n      this.bindings[binding.key].push(binding);\n    }\n  }, {\n    key: 'listen',\n    value: function listen() {\n      var _this2 = this;\n\n      this.quill.root.addEventListener('keydown', function (evt) {\n        if (evt.defaultPrevented) return;\n        var which = evt.which || evt.keyCode;\n        var bindings = (_this2.bindings[which] || []).filter(function (binding) {\n          return Keyboard.match(evt, binding);\n        });\n        if (bindings.length === 0) return;\n        var range = _this2.quill.getSelection();\n        if (range == null || !_this2.quill.hasFocus()) return;\n\n        var _quill$getLine = _this2.quill.getLine(range.index),\n            _quill$getLine2 = _slicedToArray(_quill$getLine, 2),\n            line = _quill$getLine2[0],\n            offset = _quill$getLine2[1];\n\n        var _quill$getLeaf = _this2.quill.getLeaf(range.index),\n            _quill$getLeaf2 = _slicedToArray(_quill$getLeaf, 2),\n            leafStart = _quill$getLeaf2[0],\n            offsetStart = _quill$getLeaf2[1];\n\n        var _ref = range.length === 0 ? [leafStart, offsetStart] : _this2.quill.getLeaf(range.index + range.length),\n            _ref2 = _slicedToArray(_ref, 2),\n            leafEnd = _ref2[0],\n            offsetEnd = _ref2[1];\n\n        var prefixText = leafStart instanceof _parchment2.default.Text ? leafStart.value().slice(0, offsetStart) : '';\n        var suffixText = leafEnd instanceof _parchment2.default.Text ? leafEnd.value().slice(offsetEnd) : '';\n        var curContext = {\n          collapsed: range.length === 0,\n          empty: range.length === 0 && line.length() <= 1,\n          format: _this2.quill.getFormat(range),\n          offset: offset,\n          prefix: prefixText,\n          suffix: suffixText\n        };\n        var prevented = bindings.some(function (binding) {\n          if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false;\n          if (binding.empty != null && binding.empty !== curContext.empty) return false;\n          if (binding.offset != null && binding.offset !== curContext.offset) return false;\n          if (Array.isArray(binding.format)) {\n            // any format is present\n            if (binding.format.every(function (name) {\n              return curContext.format[name] == null;\n            })) {\n              return false;\n            }\n          } else if (_typeof(binding.format) === 'object') {\n            // all formats must match\n            if (!Object.keys(binding.format).every(function (name) {\n              if (binding.format[name] === true) return curContext.format[name] != null;\n              if (binding.format[name] === false) return curContext.format[name] == null;\n              return (0, _deepEqual2.default)(binding.format[name], curContext.format[name]);\n            })) {\n              return false;\n            }\n          }\n          if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false;\n          if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false;\n          return binding.handler.call(_this2, range, curContext) !== true;\n        });\n        if (prevented) {\n          evt.preventDefault();\n        }\n      });\n    }\n  }]);\n\n  return Keyboard;\n}(_module2.default);\n\nKeyboard.keys = {\n  BACKSPACE: 8,\n  TAB: 9,\n  ENTER: 13,\n  ESCAPE: 27,\n  LEFT: 37,\n  UP: 38,\n  RIGHT: 39,\n  DOWN: 40,\n  DELETE: 46\n};\n\nKeyboard.DEFAULTS = {\n  bindings: {\n    'bold': makeFormatHandler('bold'),\n    'italic': makeFormatHandler('italic'),\n    'underline': makeFormatHandler('underline'),\n    'indent': {\n      // highlight tab or tab at beginning of list, indent or blockquote\n      key: Keyboard.keys.TAB,\n      format: ['blockquote', 'indent', 'list'],\n      handler: function handler(range, context) {\n        if (context.collapsed && context.offset !== 0) return true;\n        this.quill.format('indent', '+1', _quill2.default.sources.USER);\n      }\n    },\n    'outdent': {\n      key: Keyboard.keys.TAB,\n      shiftKey: true,\n      format: ['blockquote', 'indent', 'list'],\n      // highlight tab or tab at beginning of list, indent or blockquote\n      handler: function handler(range, context) {\n        if (context.collapsed && context.offset !== 0) return true;\n        this.quill.format('indent', '-1', _quill2.default.sources.USER);\n      }\n    },\n    'outdent backspace': {\n      key: Keyboard.keys.BACKSPACE,\n      collapsed: true,\n      shiftKey: null,\n      metaKey: null,\n      ctrlKey: null,\n      altKey: null,\n      format: ['indent', 'list'],\n      offset: 0,\n      handler: function handler(range, context) {\n        if (context.format.indent != null) {\n          this.quill.format('indent', '-1', _quill2.default.sources.USER);\n        } else if (context.format.list != null) {\n          this.quill.format('list', false, _quill2.default.sources.USER);\n        }\n      }\n    },\n    'indent code-block': makeCodeBlockHandler(true),\n    'outdent code-block': makeCodeBlockHandler(false),\n    'remove tab': {\n      key: Keyboard.keys.TAB,\n      shiftKey: true,\n      collapsed: true,\n      prefix: /\\t$/,\n      handler: function handler(range) {\n        this.quill.deleteText(range.index - 1, 1, _quill2.default.sources.USER);\n      }\n    },\n    'tab': {\n      key: Keyboard.keys.TAB,\n      handler: function handler(range) {\n        this.quill.history.cutoff();\n        var delta = new _quillDelta2.default().retain(range.index).delete(range.length).insert('\\t');\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.history.cutoff();\n        this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n      }\n    },\n    'list empty enter': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: ['list'],\n      empty: true,\n      handler: function handler(range, context) {\n        this.quill.format('list', false, _quill2.default.sources.USER);\n        if (context.format.indent) {\n          this.quill.format('indent', false, _quill2.default.sources.USER);\n        }\n      }\n    },\n    'checklist enter': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: { list: 'checked' },\n      handler: function handler(range) {\n        var _quill$getLine3 = this.quill.getLine(range.index),\n            _quill$getLine4 = _slicedToArray(_quill$getLine3, 2),\n            line = _quill$getLine4[0],\n            offset = _quill$getLine4[1];\n\n        var formats = (0, _extend2.default)({}, line.formats(), { list: 'checked' });\n        var delta = new _quillDelta2.default().retain(range.index).insert('\\n', formats).retain(line.length() - offset - 1).retain(1, { list: 'unchecked' });\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n        this.quill.scrollIntoView();\n      }\n    },\n    'header enter': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: ['header'],\n      suffix: /^$/,\n      handler: function handler(range, context) {\n        var _quill$getLine5 = this.quill.getLine(range.index),\n            _quill$getLine6 = _slicedToArray(_quill$getLine5, 2),\n            line = _quill$getLine6[0],\n            offset = _quill$getLine6[1];\n\n        var delta = new _quillDelta2.default().retain(range.index).insert('\\n', context.format).retain(line.length() - offset - 1).retain(1, { header: null });\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n        this.quill.scrollIntoView();\n      }\n    },\n    'list autofill': {\n      key: ' ',\n      collapsed: true,\n      format: { list: false },\n      prefix: /^\\s*?(\\d+\\.|-|\\*|\\[ ?\\]|\\[x\\])$/,\n      handler: function handler(range, context) {\n        var length = context.prefix.length;\n\n        var _quill$getLine7 = this.quill.getLine(range.index),\n            _quill$getLine8 = _slicedToArray(_quill$getLine7, 2),\n            line = _quill$getLine8[0],\n            offset = _quill$getLine8[1];\n\n        if (offset > length) return true;\n        var value = void 0;\n        switch (context.prefix.trim()) {\n          case '[]':case '[ ]':\n            value = 'unchecked';\n            break;\n          case '[x]':\n            value = 'checked';\n            break;\n          case '-':case '*':\n            value = 'bullet';\n            break;\n          default:\n            value = 'ordered';\n        }\n        this.quill.insertText(range.index, ' ', _quill2.default.sources.USER);\n        this.quill.history.cutoff();\n        var delta = new _quillDelta2.default().retain(range.index - offset).delete(length + 1).retain(line.length() - 2 - offset).retain(1, { list: value });\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.history.cutoff();\n        this.quill.setSelection(range.index - length, _quill2.default.sources.SILENT);\n      }\n    },\n    'code exit': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: ['code-block'],\n      prefix: /\\n\\n$/,\n      suffix: /^\\s+$/,\n      handler: function handler(range) {\n        var _quill$getLine9 = this.quill.getLine(range.index),\n            _quill$getLine10 = _slicedToArray(_quill$getLine9, 2),\n            line = _quill$getLine10[0],\n            offset = _quill$getLine10[1];\n\n        var delta = new _quillDelta2.default().retain(range.index + line.length() - offset - 2).retain(1, { 'code-block': null }).delete(1);\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n      }\n    },\n    'embed left': makeEmbedArrowHandler(Keyboard.keys.LEFT, false),\n    'embed left shift': makeEmbedArrowHandler(Keyboard.keys.LEFT, true),\n    'embed right': makeEmbedArrowHandler(Keyboard.keys.RIGHT, false),\n    'embed right shift': makeEmbedArrowHandler(Keyboard.keys.RIGHT, true)\n  }\n};\n\nfunction makeEmbedArrowHandler(key, shiftKey) {\n  var _ref3;\n\n  var where = key === Keyboard.keys.LEFT ? 'prefix' : 'suffix';\n  return _ref3 = {\n    key: key,\n    shiftKey: shiftKey,\n    altKey: null\n  }, _defineProperty(_ref3, where, /^$/), _defineProperty(_ref3, 'handler', function handler(range) {\n    var index = range.index;\n    if (key === Keyboard.keys.RIGHT) {\n      index += range.length + 1;\n    }\n\n    var _quill$getLeaf3 = this.quill.getLeaf(index),\n        _quill$getLeaf4 = _slicedToArray(_quill$getLeaf3, 1),\n        leaf = _quill$getLeaf4[0];\n\n    if (!(leaf instanceof _parchment2.default.Embed)) return true;\n    if (key === Keyboard.keys.LEFT) {\n      if (shiftKey) {\n        this.quill.setSelection(range.index - 1, range.length + 1, _quill2.default.sources.USER);\n      } else {\n        this.quill.setSelection(range.index - 1, _quill2.default.sources.USER);\n      }\n    } else {\n      if (shiftKey) {\n        this.quill.setSelection(range.index, range.length + 1, _quill2.default.sources.USER);\n      } else {\n        this.quill.setSelection(range.index + range.length + 1, _quill2.default.sources.USER);\n      }\n    }\n    return false;\n  }), _ref3;\n}\n\nfunction handleBackspace(range, context) {\n  if (range.index === 0 || this.quill.getLength() <= 1) return;\n\n  var _quill$getLine11 = this.quill.getLine(range.index),\n      _quill$getLine12 = _slicedToArray(_quill$getLine11, 1),\n      line = _quill$getLine12[0];\n\n  var formats = {};\n  if (context.offset === 0) {\n    var _quill$getLine13 = this.quill.getLine(range.index - 1),\n        _quill$getLine14 = _slicedToArray(_quill$getLine13, 1),\n        prev = _quill$getLine14[0];\n\n    if (prev != null && prev.length() > 1) {\n      var curFormats = line.formats();\n      var prevFormats = this.quill.getFormat(range.index - 1, 1);\n      formats = _op2.default.attributes.diff(curFormats, prevFormats) || {};\n    }\n  }\n  // Check for astral symbols\n  var length = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]$/.test(context.prefix) ? 2 : 1;\n  this.quill.deleteText(range.index - length, length, _quill2.default.sources.USER);\n  if (Object.keys(formats).length > 0) {\n    this.quill.formatLine(range.index - length, length, formats, _quill2.default.sources.USER);\n  }\n  this.quill.focus();\n}\n\nfunction handleDelete(range, context) {\n  // Check for astral symbols\n  var length = /^[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/.test(context.suffix) ? 2 : 1;\n  if (range.index >= this.quill.getLength() - length) return;\n  var formats = {},\n      nextLength = 0;\n\n  var _quill$getLine15 = this.quill.getLine(range.index),\n      _quill$getLine16 = _slicedToArray(_quill$getLine15, 1),\n      line = _quill$getLine16[0];\n\n  if (context.offset >= line.length() - 1) {\n    var _quill$getLine17 = this.quill.getLine(range.index + 1),\n        _quill$getLine18 = _slicedToArray(_quill$getLine17, 1),\n        next = _quill$getLine18[0];\n\n    if (next) {\n      var curFormats = line.formats();\n      var nextFormats = this.quill.getFormat(range.index, 1);\n      formats = _op2.default.attributes.diff(curFormats, nextFormats) || {};\n      nextLength = next.length();\n    }\n  }\n  this.quill.deleteText(range.index, length, _quill2.default.sources.USER);\n  if (Object.keys(formats).length > 0) {\n    this.quill.formatLine(range.index + nextLength - 1, length, formats, _quill2.default.sources.USER);\n  }\n}\n\nfunction handleDeleteRange(range) {\n  var lines = this.quill.getLines(range);\n  var formats = {};\n  if (lines.length > 1) {\n    var firstFormats = lines[0].formats();\n    var lastFormats = lines[lines.length - 1].formats();\n    formats = _op2.default.attributes.diff(lastFormats, firstFormats) || {};\n  }\n  this.quill.deleteText(range, _quill2.default.sources.USER);\n  if (Object.keys(formats).length > 0) {\n    this.quill.formatLine(range.index, 1, formats, _quill2.default.sources.USER);\n  }\n  this.quill.setSelection(range.index, _quill2.default.sources.SILENT);\n  this.quill.focus();\n}\n\nfunction handleEnter(range, context) {\n  var _this3 = this;\n\n  if (range.length > 0) {\n    this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change\n  }\n  var lineFormats = Object.keys(context.format).reduce(function (lineFormats, format) {\n    if (_parchment2.default.query(format, _parchment2.default.Scope.BLOCK) && !Array.isArray(context.format[format])) {\n      lineFormats[format] = context.format[format];\n    }\n    return lineFormats;\n  }, {});\n  this.quill.insertText(range.index, '\\n', lineFormats, _quill2.default.sources.USER);\n  // Earlier scroll.deleteAt might have messed up our selection,\n  // so insertText's built in selection preservation is not reliable\n  this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n  this.quill.focus();\n  Object.keys(context.format).forEach(function (name) {\n    if (lineFormats[name] != null) return;\n    if (Array.isArray(context.format[name])) return;\n    if (name === 'link') return;\n    _this3.quill.format(name, context.format[name], _quill2.default.sources.USER);\n  });\n}\n\nfunction makeCodeBlockHandler(indent) {\n  return {\n    key: Keyboard.keys.TAB,\n    shiftKey: !indent,\n    format: { 'code-block': true },\n    handler: function handler(range) {\n      var CodeBlock = _parchment2.default.query('code-block');\n      var index = range.index,\n          length = range.length;\n\n      var _quill$scroll$descend = this.quill.scroll.descendant(CodeBlock, index),\n          _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2),\n          block = _quill$scroll$descend2[0],\n          offset = _quill$scroll$descend2[1];\n\n      if (block == null) return;\n      var scrollIndex = this.quill.getIndex(block);\n      var start = block.newlineIndex(offset, true) + 1;\n      var end = block.newlineIndex(scrollIndex + offset + length);\n      var lines = block.domNode.textContent.slice(start, end).split('\\n');\n      offset = 0;\n      lines.forEach(function (line, i) {\n        if (indent) {\n          block.insertAt(start + offset, CodeBlock.TAB);\n          offset += CodeBlock.TAB.length;\n          if (i === 0) {\n            index += CodeBlock.TAB.length;\n          } else {\n            length += CodeBlock.TAB.length;\n          }\n        } else if (line.startsWith(CodeBlock.TAB)) {\n          block.deleteAt(start + offset, CodeBlock.TAB.length);\n          offset -= CodeBlock.TAB.length;\n          if (i === 0) {\n            index -= CodeBlock.TAB.length;\n          } else {\n            length -= CodeBlock.TAB.length;\n          }\n        }\n        offset += line.length + 1;\n      });\n      this.quill.update(_quill2.default.sources.USER);\n      this.quill.setSelection(index, length, _quill2.default.sources.SILENT);\n    }\n  };\n}\n\nfunction makeFormatHandler(format) {\n  return {\n    key: format[0].toUpperCase(),\n    shortKey: true,\n    handler: function handler(range, context) {\n      this.quill.format(format, !context.format[format], _quill2.default.sources.USER);\n    }\n  };\n}\n\nfunction normalize(binding) {\n  if (typeof binding === 'string' || typeof binding === 'number') {\n    return normalize({ key: binding });\n  }\n  if ((typeof binding === 'undefined' ? 'undefined' : _typeof(binding)) === 'object') {\n    binding = (0, _clone2.default)(binding, false);\n  }\n  if (typeof binding.key === 'string') {\n    if (Keyboard.keys[binding.key.toUpperCase()] != null) {\n      binding.key = Keyboard.keys[binding.key.toUpperCase()];\n    } else if (binding.key.length === 1) {\n      binding.key = binding.key.toUpperCase().charCodeAt(0);\n    } else {\n      return null;\n    }\n  }\n  if (binding.shortKey) {\n    binding[SHORTKEY] = binding.shortKey;\n    delete binding.shortKey;\n  }\n  return binding;\n}\n\nexports.default = Keyboard;\nexports.SHORTKEY = SHORTKEY;\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Cursor = function (_Parchment$Embed) {\n  _inherits(Cursor, _Parchment$Embed);\n\n  _createClass(Cursor, null, [{\n    key: 'value',\n    value: function value() {\n      return undefined;\n    }\n  }]);\n\n  function Cursor(domNode, selection) {\n    _classCallCheck(this, Cursor);\n\n    var _this = _possibleConstructorReturn(this, (Cursor.__proto__ || Object.getPrototypeOf(Cursor)).call(this, domNode));\n\n    _this.selection = selection;\n    _this.textNode = document.createTextNode(Cursor.CONTENTS);\n    _this.domNode.appendChild(_this.textNode);\n    _this._length = 0;\n    return _this;\n  }\n\n  _createClass(Cursor, [{\n    key: 'detach',\n    value: function detach() {\n      // super.detach() will also clear domNode.__blot\n      if (this.parent != null) this.parent.removeChild(this);\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      if (this._length !== 0) {\n        return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'format', this).call(this, name, value);\n      }\n      var target = this,\n          index = 0;\n      while (target != null && target.statics.scope !== _parchment2.default.Scope.BLOCK_BLOT) {\n        index += target.offset(target.parent);\n        target = target.parent;\n      }\n      if (target != null) {\n        this._length = Cursor.CONTENTS.length;\n        target.optimize();\n        target.formatAt(index, Cursor.CONTENTS.length, name, value);\n        this._length = 0;\n      }\n    }\n  }, {\n    key: 'index',\n    value: function index(node, offset) {\n      if (node === this.textNode) return 0;\n      return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'index', this).call(this, node, offset);\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      return this._length;\n    }\n  }, {\n    key: 'position',\n    value: function position() {\n      return [this.textNode, this.textNode.data.length];\n    }\n  }, {\n    key: 'remove',\n    value: function remove() {\n      _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'remove', this).call(this);\n      this.parent = null;\n    }\n  }, {\n    key: 'restore',\n    value: function restore() {\n      if (this.selection.composing || this.parent == null) return;\n      var textNode = this.textNode;\n      var range = this.selection.getNativeRange();\n      var restoreText = void 0,\n          start = void 0,\n          end = void 0;\n      if (range != null && range.start.node === textNode && range.end.node === textNode) {\n        var _ref = [textNode, range.start.offset, range.end.offset];\n        restoreText = _ref[0];\n        start = _ref[1];\n        end = _ref[2];\n      }\n      // Link format will insert text outside of anchor tag\n      while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {\n        this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);\n      }\n      if (this.textNode.data !== Cursor.CONTENTS) {\n        var text = this.textNode.data.split(Cursor.CONTENTS).join('');\n        if (this.next instanceof _text2.default) {\n          restoreText = this.next.domNode;\n          this.next.insertAt(0, text);\n          this.textNode.data = Cursor.CONTENTS;\n        } else {\n          this.textNode.data = text;\n          this.parent.insertBefore(_parchment2.default.create(this.textNode), this);\n          this.textNode = document.createTextNode(Cursor.CONTENTS);\n          this.domNode.appendChild(this.textNode);\n        }\n      }\n      this.remove();\n      if (start != null) {\n        var _map = [start, end].map(function (offset) {\n          return Math.max(0, Math.min(restoreText.data.length, offset - 1));\n        });\n\n        var _map2 = _slicedToArray(_map, 2);\n\n        start = _map2[0];\n        end = _map2[1];\n\n        return {\n          startNode: restoreText,\n          startOffset: start,\n          endNode: restoreText,\n          endOffset: end\n        };\n      }\n    }\n  }, {\n    key: 'update',\n    value: function update(mutations, context) {\n      var _this2 = this;\n\n      if (mutations.some(function (mutation) {\n        return mutation.type === 'characterData' && mutation.target === _this2.textNode;\n      })) {\n        var range = this.restore();\n        if (range) context.range = range;\n      }\n    }\n  }, {\n    key: 'value',\n    value: function value() {\n      return '';\n    }\n  }]);\n\n  return Cursor;\n}(_parchment2.default.Embed);\n\nCursor.blotName = 'cursor';\nCursor.className = 'ql-cursor';\nCursor.tagName = 'span';\nCursor.CONTENTS = '\\uFEFF'; // Zero width no break space\n\n\nexports.default = Cursor;\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Container = function (_Parchment$Container) {\n  _inherits(Container, _Parchment$Container);\n\n  function Container() {\n    _classCallCheck(this, Container);\n\n    return _possibleConstructorReturn(this, (Container.__proto__ || Object.getPrototypeOf(Container)).apply(this, arguments));\n  }\n\n  return Container;\n}(_parchment2.default.Container);\n\nContainer.allowedChildren = [_block2.default, _block.BlockEmbed, Container];\n\nexports.default = Container;\n\n/***/ }),\n/* 26 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.ColorStyle = exports.ColorClass = exports.ColorAttributor = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ColorAttributor = function (_Parchment$Attributor) {\n  _inherits(ColorAttributor, _Parchment$Attributor);\n\n  function ColorAttributor() {\n    _classCallCheck(this, ColorAttributor);\n\n    return _possibleConstructorReturn(this, (ColorAttributor.__proto__ || Object.getPrototypeOf(ColorAttributor)).apply(this, arguments));\n  }\n\n  _createClass(ColorAttributor, [{\n    key: 'value',\n    value: function value(domNode) {\n      var value = _get(ColorAttributor.prototype.__proto__ || Object.getPrototypeOf(ColorAttributor.prototype), 'value', this).call(this, domNode);\n      if (!value.startsWith('rgb(')) return value;\n      value = value.replace(/^[^\\d]+/, '').replace(/[^\\d]+$/, '');\n      return '#' + value.split(',').map(function (component) {\n        return ('00' + parseInt(component).toString(16)).slice(-2);\n      }).join('');\n    }\n  }]);\n\n  return ColorAttributor;\n}(_parchment2.default.Attributor.Style);\n\nvar ColorClass = new _parchment2.default.Attributor.Class('color', 'ql-color', {\n  scope: _parchment2.default.Scope.INLINE\n});\nvar ColorStyle = new ColorAttributor('color', 'color', {\n  scope: _parchment2.default.Scope.INLINE\n});\n\nexports.ColorAttributor = ColorAttributor;\nexports.ColorClass = ColorClass;\nexports.ColorStyle = ColorStyle;\n\n/***/ }),\n/* 27 */,\n/* 28 */,\n/* 29 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _container = __webpack_require__(25);\n\nvar _container2 = _interopRequireDefault(_container);\n\nvar _cursor = __webpack_require__(24);\n\nvar _cursor2 = _interopRequireDefault(_cursor);\n\nvar _embed = __webpack_require__(35);\n\nvar _embed2 = _interopRequireDefault(_embed);\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _scroll = __webpack_require__(22);\n\nvar _scroll2 = _interopRequireDefault(_scroll);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nvar _clipboard = __webpack_require__(55);\n\nvar _clipboard2 = _interopRequireDefault(_clipboard);\n\nvar _history = __webpack_require__(42);\n\nvar _history2 = _interopRequireDefault(_history);\n\nvar _keyboard = __webpack_require__(23);\n\nvar _keyboard2 = _interopRequireDefault(_keyboard);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_quill2.default.register({\n  'blots/block': _block2.default,\n  'blots/block/embed': _block.BlockEmbed,\n  'blots/break': _break2.default,\n  'blots/container': _container2.default,\n  'blots/cursor': _cursor2.default,\n  'blots/embed': _embed2.default,\n  'blots/inline': _inline2.default,\n  'blots/scroll': _scroll2.default,\n  'blots/text': _text2.default,\n\n  'modules/clipboard': _clipboard2.default,\n  'modules/history': _history2.default,\n  'modules/keyboard': _keyboard2.default\n});\n\n_parchment2.default.register(_block2.default, _break2.default, _cursor2.default, _inline2.default, _scroll2.default, _text2.default);\n\nexports.default = _quill2.default;\n\n/***/ }),\n/* 30 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = __webpack_require__(1);\nvar ShadowBlot = /** @class */ (function () {\n    function ShadowBlot(domNode) {\n        this.domNode = domNode;\n        // @ts-ignore\n        this.domNode[Registry.DATA_KEY] = { blot: this };\n    }\n    Object.defineProperty(ShadowBlot.prototype, \"statics\", {\n        // Hack for accessing inherited static methods\n        get: function () {\n            return this.constructor;\n        },\n        enumerable: true,\n        configurable: true\n    });\n    ShadowBlot.create = function (value) {\n        if (this.tagName == null) {\n            throw new Registry.ParchmentError('Blot definition missing tagName');\n        }\n        var node;\n        if (Array.isArray(this.tagName)) {\n            if (typeof value === 'string') {\n                value = value.toUpperCase();\n                if (parseInt(value).toString() === value) {\n                    value = parseInt(value);\n                }\n            }\n            if (typeof value === 'number') {\n                node = document.createElement(this.tagName[value - 1]);\n            }\n            else if (this.tagName.indexOf(value) > -1) {\n                node = document.createElement(value);\n            }\n            else {\n                node = document.createElement(this.tagName[0]);\n            }\n        }\n        else {\n            node = document.createElement(this.tagName);\n        }\n        if (this.className) {\n            node.classList.add(this.className);\n        }\n        return node;\n    };\n    ShadowBlot.prototype.attach = function () {\n        if (this.parent != null) {\n            this.scroll = this.parent.scroll;\n        }\n    };\n    ShadowBlot.prototype.clone = function () {\n        var domNode = this.domNode.cloneNode(false);\n        return Registry.create(domNode);\n    };\n    ShadowBlot.prototype.detach = function () {\n        if (this.parent != null)\n            this.parent.removeChild(this);\n        // @ts-ignore\n        delete this.domNode[Registry.DATA_KEY];\n    };\n    ShadowBlot.prototype.deleteAt = function (index, length) {\n        var blot = this.isolate(index, length);\n        blot.remove();\n    };\n    ShadowBlot.prototype.formatAt = function (index, length, name, value) {\n        var blot = this.isolate(index, length);\n        if (Registry.query(name, Registry.Scope.BLOT) != null && value) {\n            blot.wrap(name, value);\n        }\n        else if (Registry.query(name, Registry.Scope.ATTRIBUTE) != null) {\n            var parent = Registry.create(this.statics.scope);\n            blot.wrap(parent);\n            parent.format(name, value);\n        }\n    };\n    ShadowBlot.prototype.insertAt = function (index, value, def) {\n        var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n        var ref = this.split(index);\n        this.parent.insertBefore(blot, ref);\n    };\n    ShadowBlot.prototype.insertInto = function (parentBlot, refBlot) {\n        if (refBlot === void 0) { refBlot = null; }\n        if (this.parent != null) {\n            this.parent.children.remove(this);\n        }\n        var refDomNode = null;\n        parentBlot.children.insertBefore(this, refBlot);\n        if (refBlot != null) {\n            refDomNode = refBlot.domNode;\n        }\n        if (this.domNode.parentNode != parentBlot.domNode ||\n            this.domNode.nextSibling != refDomNode) {\n            parentBlot.domNode.insertBefore(this.domNode, refDomNode);\n        }\n        this.parent = parentBlot;\n        this.attach();\n    };\n    ShadowBlot.prototype.isolate = function (index, length) {\n        var target = this.split(index);\n        target.split(length);\n        return target;\n    };\n    ShadowBlot.prototype.length = function () {\n        return 1;\n    };\n    ShadowBlot.prototype.offset = function (root) {\n        if (root === void 0) { root = this.parent; }\n        if (this.parent == null || this == root)\n            return 0;\n        return this.parent.children.offset(this) + this.parent.offset(root);\n    };\n    ShadowBlot.prototype.optimize = function (context) {\n        // TODO clean up once we use WeakMap\n        // @ts-ignore\n        if (this.domNode[Registry.DATA_KEY] != null) {\n            // @ts-ignore\n            delete this.domNode[Registry.DATA_KEY].mutations;\n        }\n    };\n    ShadowBlot.prototype.remove = function () {\n        if (this.domNode.parentNode != null) {\n            this.domNode.parentNode.removeChild(this.domNode);\n        }\n        this.detach();\n    };\n    ShadowBlot.prototype.replace = function (target) {\n        if (target.parent == null)\n            return;\n        target.parent.insertBefore(this, target.next);\n        target.remove();\n    };\n    ShadowBlot.prototype.replaceWith = function (name, value) {\n        var replacement = typeof name === 'string' ? Registry.create(name, value) : name;\n        replacement.replace(this);\n        return replacement;\n    };\n    ShadowBlot.prototype.split = function (index, force) {\n        return index === 0 ? this : this.next;\n    };\n    ShadowBlot.prototype.update = function (mutations, context) {\n        // Nothing to do by default\n    };\n    ShadowBlot.prototype.wrap = function (name, value) {\n        var wrapper = typeof name === 'string' ? Registry.create(name, value) : name;\n        if (this.parent != null) {\n            this.parent.insertBefore(wrapper, this.next);\n        }\n        wrapper.appendChild(this);\n        return wrapper;\n    };\n    ShadowBlot.blotName = 'abstract';\n    return ShadowBlot;\n}());\nexports.default = ShadowBlot;\n\n\n/***/ }),\n/* 31 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nvar class_1 = __webpack_require__(32);\nvar style_1 = __webpack_require__(33);\nvar Registry = __webpack_require__(1);\nvar AttributorStore = /** @class */ (function () {\n    function AttributorStore(domNode) {\n        this.attributes = {};\n        this.domNode = domNode;\n        this.build();\n    }\n    AttributorStore.prototype.attribute = function (attribute, value) {\n        // verb\n        if (value) {\n            if (attribute.add(this.domNode, value)) {\n                if (attribute.value(this.domNode) != null) {\n                    this.attributes[attribute.attrName] = attribute;\n                }\n                else {\n                    delete this.attributes[attribute.attrName];\n                }\n            }\n        }\n        else {\n            attribute.remove(this.domNode);\n            delete this.attributes[attribute.attrName];\n        }\n    };\n    AttributorStore.prototype.build = function () {\n        var _this = this;\n        this.attributes = {};\n        var attributes = attributor_1.default.keys(this.domNode);\n        var classes = class_1.default.keys(this.domNode);\n        var styles = style_1.default.keys(this.domNode);\n        attributes\n            .concat(classes)\n            .concat(styles)\n            .forEach(function (name) {\n            var attr = Registry.query(name, Registry.Scope.ATTRIBUTE);\n            if (attr instanceof attributor_1.default) {\n                _this.attributes[attr.attrName] = attr;\n            }\n        });\n    };\n    AttributorStore.prototype.copy = function (target) {\n        var _this = this;\n        Object.keys(this.attributes).forEach(function (key) {\n            var value = _this.attributes[key].value(_this.domNode);\n            target.format(key, value);\n        });\n    };\n    AttributorStore.prototype.move = function (target) {\n        var _this = this;\n        this.copy(target);\n        Object.keys(this.attributes).forEach(function (key) {\n            _this.attributes[key].remove(_this.domNode);\n        });\n        this.attributes = {};\n    };\n    AttributorStore.prototype.values = function () {\n        var _this = this;\n        return Object.keys(this.attributes).reduce(function (attributes, name) {\n            attributes[name] = _this.attributes[name].value(_this.domNode);\n            return attributes;\n        }, {});\n    };\n    return AttributorStore;\n}());\nexports.default = AttributorStore;\n\n\n/***/ }),\n/* 32 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nfunction match(node, prefix) {\n    var className = node.getAttribute('class') || '';\n    return className.split(/\\s+/).filter(function (name) {\n        return name.indexOf(prefix + \"-\") === 0;\n    });\n}\nvar ClassAttributor = /** @class */ (function (_super) {\n    __extends(ClassAttributor, _super);\n    function ClassAttributor() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    ClassAttributor.keys = function (node) {\n        return (node.getAttribute('class') || '').split(/\\s+/).map(function (name) {\n            return name\n                .split('-')\n                .slice(0, -1)\n                .join('-');\n        });\n    };\n    ClassAttributor.prototype.add = function (node, value) {\n        if (!this.canAdd(node, value))\n            return false;\n        this.remove(node);\n        node.classList.add(this.keyName + \"-\" + value);\n        return true;\n    };\n    ClassAttributor.prototype.remove = function (node) {\n        var matches = match(node, this.keyName);\n        matches.forEach(function (name) {\n            node.classList.remove(name);\n        });\n        if (node.classList.length === 0) {\n            node.removeAttribute('class');\n        }\n    };\n    ClassAttributor.prototype.value = function (node) {\n        var result = match(node, this.keyName)[0] || '';\n        var value = result.slice(this.keyName.length + 1); // +1 for hyphen\n        return this.canAdd(node, value) ? value : '';\n    };\n    return ClassAttributor;\n}(attributor_1.default));\nexports.default = ClassAttributor;\n\n\n/***/ }),\n/* 33 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nfunction camelize(name) {\n    var parts = name.split('-');\n    var rest = parts\n        .slice(1)\n        .map(function (part) {\n        return part[0].toUpperCase() + part.slice(1);\n    })\n        .join('');\n    return parts[0] + rest;\n}\nvar StyleAttributor = /** @class */ (function (_super) {\n    __extends(StyleAttributor, _super);\n    function StyleAttributor() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    StyleAttributor.keys = function (node) {\n        return (node.getAttribute('style') || '').split(';').map(function (value) {\n            var arr = value.split(':');\n            return arr[0].trim();\n        });\n    };\n    StyleAttributor.prototype.add = function (node, value) {\n        if (!this.canAdd(node, value))\n            return false;\n        // @ts-ignore\n        node.style[camelize(this.keyName)] = value;\n        return true;\n    };\n    StyleAttributor.prototype.remove = function (node) {\n        // @ts-ignore\n        node.style[camelize(this.keyName)] = '';\n        if (!node.getAttribute('style')) {\n            node.removeAttribute('style');\n        }\n    };\n    StyleAttributor.prototype.value = function (node) {\n        // @ts-ignore\n        var value = node.style[camelize(this.keyName)];\n        return this.canAdd(node, value) ? value : '';\n    };\n    return StyleAttributor;\n}(attributor_1.default));\nexports.default = StyleAttributor;\n\n\n/***/ }),\n/* 34 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Theme = function () {\n  function Theme(quill, options) {\n    _classCallCheck(this, Theme);\n\n    this.quill = quill;\n    this.options = options;\n    this.modules = {};\n  }\n\n  _createClass(Theme, [{\n    key: 'init',\n    value: function init() {\n      var _this = this;\n\n      Object.keys(this.options.modules).forEach(function (name) {\n        if (_this.modules[name] == null) {\n          _this.addModule(name);\n        }\n      });\n    }\n  }, {\n    key: 'addModule',\n    value: function addModule(name) {\n      var moduleClass = this.quill.constructor.import('modules/' + name);\n      this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {});\n      return this.modules[name];\n    }\n  }]);\n\n  return Theme;\n}();\n\nTheme.DEFAULTS = {\n  modules: {}\n};\nTheme.themes = {\n  'default': Theme\n};\n\nexports.default = Theme;\n\n/***/ }),\n/* 35 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar GUARD_TEXT = '\\uFEFF';\n\nvar Embed = function (_Parchment$Embed) {\n  _inherits(Embed, _Parchment$Embed);\n\n  function Embed(node) {\n    _classCallCheck(this, Embed);\n\n    var _this = _possibleConstructorReturn(this, (Embed.__proto__ || Object.getPrototypeOf(Embed)).call(this, node));\n\n    _this.contentNode = document.createElement('span');\n    _this.contentNode.setAttribute('contenteditable', false);\n    [].slice.call(_this.domNode.childNodes).forEach(function (childNode) {\n      _this.contentNode.appendChild(childNode);\n    });\n    _this.leftGuard = document.createTextNode(GUARD_TEXT);\n    _this.rightGuard = document.createTextNode(GUARD_TEXT);\n    _this.domNode.appendChild(_this.leftGuard);\n    _this.domNode.appendChild(_this.contentNode);\n    _this.domNode.appendChild(_this.rightGuard);\n    return _this;\n  }\n\n  _createClass(Embed, [{\n    key: 'index',\n    value: function index(node, offset) {\n      if (node === this.leftGuard) return 0;\n      if (node === this.rightGuard) return 1;\n      return _get(Embed.prototype.__proto__ || Object.getPrototypeOf(Embed.prototype), 'index', this).call(this, node, offset);\n    }\n  }, {\n    key: 'restore',\n    value: function restore(node) {\n      var range = void 0,\n          textNode = void 0;\n      var text = node.data.split(GUARD_TEXT).join('');\n      if (node === this.leftGuard) {\n        if (this.prev instanceof _text2.default) {\n          var prevLength = this.prev.length();\n          this.prev.insertAt(prevLength, text);\n          range = {\n            startNode: this.prev.domNode,\n            startOffset: prevLength + text.length\n          };\n        } else {\n          textNode = document.createTextNode(text);\n          this.parent.insertBefore(_parchment2.default.create(textNode), this);\n          range = {\n            startNode: textNode,\n            startOffset: text.length\n          };\n        }\n      } else if (node === this.rightGuard) {\n        if (this.next instanceof _text2.default) {\n          this.next.insertAt(0, text);\n          range = {\n            startNode: this.next.domNode,\n            startOffset: text.length\n          };\n        } else {\n          textNode = document.createTextNode(text);\n          this.parent.insertBefore(_parchment2.default.create(textNode), this.next);\n          range = {\n            startNode: textNode,\n            startOffset: text.length\n          };\n        }\n      }\n      node.data = GUARD_TEXT;\n      return range;\n    }\n  }, {\n    key: 'update',\n    value: function update(mutations, context) {\n      var _this2 = this;\n\n      mutations.forEach(function (mutation) {\n        if (mutation.type === 'characterData' && (mutation.target === _this2.leftGuard || mutation.target === _this2.rightGuard)) {\n          var range = _this2.restore(mutation.target);\n          if (range) context.range = range;\n        }\n      });\n    }\n  }]);\n\n  return Embed;\n}(_parchment2.default.Embed);\n\nexports.default = Embed;\n\n/***/ }),\n/* 36 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.AlignStyle = exports.AlignClass = exports.AlignAttribute = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar config = {\n  scope: _parchment2.default.Scope.BLOCK,\n  whitelist: ['right', 'center', 'justify']\n};\n\nvar AlignAttribute = new _parchment2.default.Attributor.Attribute('align', 'align', config);\nvar AlignClass = new _parchment2.default.Attributor.Class('align', 'ql-align', config);\nvar AlignStyle = new _parchment2.default.Attributor.Style('align', 'text-align', config);\n\nexports.AlignAttribute = AlignAttribute;\nexports.AlignClass = AlignClass;\nexports.AlignStyle = AlignStyle;\n\n/***/ }),\n/* 37 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.BackgroundStyle = exports.BackgroundClass = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _color = __webpack_require__(26);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar BackgroundClass = new _parchment2.default.Attributor.Class('background', 'ql-bg', {\n  scope: _parchment2.default.Scope.INLINE\n});\nvar BackgroundStyle = new _color.ColorAttributor('background', 'background-color', {\n  scope: _parchment2.default.Scope.INLINE\n});\n\nexports.BackgroundClass = BackgroundClass;\nexports.BackgroundStyle = BackgroundStyle;\n\n/***/ }),\n/* 38 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.DirectionStyle = exports.DirectionClass = exports.DirectionAttribute = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar config = {\n  scope: _parchment2.default.Scope.BLOCK,\n  whitelist: ['rtl']\n};\n\nvar DirectionAttribute = new _parchment2.default.Attributor.Attribute('direction', 'dir', config);\nvar DirectionClass = new _parchment2.default.Attributor.Class('direction', 'ql-direction', config);\nvar DirectionStyle = new _parchment2.default.Attributor.Style('direction', 'direction', config);\n\nexports.DirectionAttribute = DirectionAttribute;\nexports.DirectionClass = DirectionClass;\nexports.DirectionStyle = DirectionStyle;\n\n/***/ }),\n/* 39 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.FontClass = exports.FontStyle = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar config = {\n  scope: _parchment2.default.Scope.INLINE,\n  whitelist: ['serif', 'monospace']\n};\n\nvar FontClass = new _parchment2.default.Attributor.Class('font', 'ql-font', config);\n\nvar FontStyleAttributor = function (_Parchment$Attributor) {\n  _inherits(FontStyleAttributor, _Parchment$Attributor);\n\n  function FontStyleAttributor() {\n    _classCallCheck(this, FontStyleAttributor);\n\n    return _possibleConstructorReturn(this, (FontStyleAttributor.__proto__ || Object.getPrototypeOf(FontStyleAttributor)).apply(this, arguments));\n  }\n\n  _createClass(FontStyleAttributor, [{\n    key: 'value',\n    value: function value(node) {\n      return _get(FontStyleAttributor.prototype.__proto__ || Object.getPrototypeOf(FontStyleAttributor.prototype), 'value', this).call(this, node).replace(/[\"']/g, '');\n    }\n  }]);\n\n  return FontStyleAttributor;\n}(_parchment2.default.Attributor.Style);\n\nvar FontStyle = new FontStyleAttributor('font', 'font-family', config);\n\nexports.FontStyle = FontStyle;\nexports.FontClass = FontClass;\n\n/***/ }),\n/* 40 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.SizeStyle = exports.SizeClass = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar SizeClass = new _parchment2.default.Attributor.Class('size', 'ql-size', {\n  scope: _parchment2.default.Scope.INLINE,\n  whitelist: ['small', 'large', 'huge']\n});\nvar SizeStyle = new _parchment2.default.Attributor.Style('size', 'font-size', {\n  scope: _parchment2.default.Scope.INLINE,\n  whitelist: ['10px', '18px', '32px']\n});\n\nexports.SizeClass = SizeClass;\nexports.SizeStyle = SizeStyle;\n\n/***/ }),\n/* 41 */,\n/* 42 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.getLastChangeIndex = exports.default = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar History = function (_Module) {\n  _inherits(History, _Module);\n\n  function History(quill, options) {\n    _classCallCheck(this, History);\n\n    var _this = _possibleConstructorReturn(this, (History.__proto__ || Object.getPrototypeOf(History)).call(this, quill, options));\n\n    _this.lastRecorded = 0;\n    _this.ignoreChange = false;\n    _this.clear();\n    _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (eventName, delta, oldDelta, source) {\n      if (eventName !== _quill2.default.events.TEXT_CHANGE || _this.ignoreChange) return;\n      if (!_this.options.userOnly || source === _quill2.default.sources.USER) {\n        _this.record(delta, oldDelta);\n      } else {\n        _this.transform(delta);\n      }\n    });\n    _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, _this.undo.bind(_this));\n    _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, _this.redo.bind(_this));\n    if (/Win/i.test(navigator.platform)) {\n      _this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, _this.redo.bind(_this));\n    }\n    return _this;\n  }\n\n  _createClass(History, [{\n    key: 'change',\n    value: function change(source, dest) {\n      if (this.stack[source].length === 0) return;\n      var delta = this.stack[source].pop();\n      this.stack[dest].push(delta);\n      this.lastRecorded = 0;\n      this.ignoreChange = true;\n      this.quill.updateContents(delta[source], _quill2.default.sources.USER);\n      this.ignoreChange = false;\n      var index = getLastChangeIndex(delta[source]);\n      this.quill.setSelection(index);\n    }\n  }, {\n    key: 'clear',\n    value: function clear() {\n      this.stack = { undo: [], redo: [] };\n    }\n  }, {\n    key: 'cutoff',\n    value: function cutoff() {\n      this.lastRecorded = 0;\n    }\n  }, {\n    key: 'record',\n    value: function record(changeDelta, oldDelta) {\n      if (changeDelta.ops.length === 0) return;\n      this.stack.redo = [];\n      var undoDelta = this.quill.getContents().diff(oldDelta);\n      var timestamp = Date.now();\n      if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) {\n        var delta = this.stack.undo.pop();\n        undoDelta = undoDelta.compose(delta.undo);\n        changeDelta = delta.redo.compose(changeDelta);\n      } else {\n        this.lastRecorded = timestamp;\n      }\n      this.stack.undo.push({\n        redo: changeDelta,\n        undo: undoDelta\n      });\n      if (this.stack.undo.length > this.options.maxStack) {\n        this.stack.undo.shift();\n      }\n    }\n  }, {\n    key: 'redo',\n    value: function redo() {\n      this.change('redo', 'undo');\n    }\n  }, {\n    key: 'transform',\n    value: function transform(delta) {\n      this.stack.undo.forEach(function (change) {\n        change.undo = delta.transform(change.undo, true);\n        change.redo = delta.transform(change.redo, true);\n      });\n      this.stack.redo.forEach(function (change) {\n        change.undo = delta.transform(change.undo, true);\n        change.redo = delta.transform(change.redo, true);\n      });\n    }\n  }, {\n    key: 'undo',\n    value: function undo() {\n      this.change('undo', 'redo');\n    }\n  }]);\n\n  return History;\n}(_module2.default);\n\nHistory.DEFAULTS = {\n  delay: 1000,\n  maxStack: 100,\n  userOnly: false\n};\n\nfunction endsWithNewlineChange(delta) {\n  var lastOp = delta.ops[delta.ops.length - 1];\n  if (lastOp == null) return false;\n  if (lastOp.insert != null) {\n    return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\\n');\n  }\n  if (lastOp.attributes != null) {\n    return Object.keys(lastOp.attributes).some(function (attr) {\n      return _parchment2.default.query(attr, _parchment2.default.Scope.BLOCK) != null;\n    });\n  }\n  return false;\n}\n\nfunction getLastChangeIndex(delta) {\n  var deleteLength = delta.reduce(function (length, op) {\n    length += op.delete || 0;\n    return length;\n  }, 0);\n  var changeIndex = delta.length() - deleteLength;\n  if (endsWithNewlineChange(delta)) {\n    changeIndex -= 1;\n  }\n  return changeIndex;\n}\n\nexports.default = History;\nexports.getLastChangeIndex = getLastChangeIndex;\n\n/***/ }),\n/* 43 */,\n/* 44 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar LinkedList = /** @class */ (function () {\n    function LinkedList() {\n        this.head = this.tail = null;\n        this.length = 0;\n    }\n    LinkedList.prototype.append = function () {\n        var nodes = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            nodes[_i] = arguments[_i];\n        }\n        this.insertBefore(nodes[0], null);\n        if (nodes.length > 1) {\n            this.append.apply(this, nodes.slice(1));\n        }\n    };\n    LinkedList.prototype.contains = function (node) {\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            if (cur === node)\n                return true;\n        }\n        return false;\n    };\n    LinkedList.prototype.insertBefore = function (node, refNode) {\n        if (!node)\n            return;\n        node.next = refNode;\n        if (refNode != null) {\n            node.prev = refNode.prev;\n            if (refNode.prev != null) {\n                refNode.prev.next = node;\n            }\n            refNode.prev = node;\n            if (refNode === this.head) {\n                this.head = node;\n            }\n        }\n        else if (this.tail != null) {\n            this.tail.next = node;\n            node.prev = this.tail;\n            this.tail = node;\n        }\n        else {\n            node.prev = null;\n            this.head = this.tail = node;\n        }\n        this.length += 1;\n    };\n    LinkedList.prototype.offset = function (target) {\n        var index = 0, cur = this.head;\n        while (cur != null) {\n            if (cur === target)\n                return index;\n            index += cur.length();\n            cur = cur.next;\n        }\n        return -1;\n    };\n    LinkedList.prototype.remove = function (node) {\n        if (!this.contains(node))\n            return;\n        if (node.prev != null)\n            node.prev.next = node.next;\n        if (node.next != null)\n            node.next.prev = node.prev;\n        if (node === this.head)\n            this.head = node.next;\n        if (node === this.tail)\n            this.tail = node.prev;\n        this.length -= 1;\n    };\n    LinkedList.prototype.iterator = function (curNode) {\n        if (curNode === void 0) { curNode = this.head; }\n        // TODO use yield when we can\n        return function () {\n            var ret = curNode;\n            if (curNode != null)\n                curNode = curNode.next;\n            return ret;\n        };\n    };\n    LinkedList.prototype.find = function (index, inclusive) {\n        if (inclusive === void 0) { inclusive = false; }\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            var length = cur.length();\n            if (index < length ||\n                (inclusive && index === length && (cur.next == null || cur.next.length() !== 0))) {\n                return [cur, index];\n            }\n            index -= length;\n        }\n        return [null, 0];\n    };\n    LinkedList.prototype.forEach = function (callback) {\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            callback(cur);\n        }\n    };\n    LinkedList.prototype.forEachAt = function (index, length, callback) {\n        if (length <= 0)\n            return;\n        var _a = this.find(index), startNode = _a[0], offset = _a[1];\n        var cur, curIndex = index - offset, next = this.iterator(startNode);\n        while ((cur = next()) && curIndex < index + length) {\n            var curLength = cur.length();\n            if (index > curIndex) {\n                callback(cur, index - curIndex, Math.min(length, curIndex + curLength - index));\n            }\n            else {\n                callback(cur, 0, Math.min(curLength, index + length - curIndex));\n            }\n            curIndex += curLength;\n        }\n    };\n    LinkedList.prototype.map = function (callback) {\n        return this.reduce(function (memo, cur) {\n            memo.push(callback(cur));\n            return memo;\n        }, []);\n    };\n    LinkedList.prototype.reduce = function (callback, memo) {\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            memo = callback(memo, cur);\n        }\n        return memo;\n    };\n    return LinkedList;\n}());\nexports.default = LinkedList;\n\n\n/***/ }),\n/* 45 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = __webpack_require__(17);\nvar Registry = __webpack_require__(1);\nvar OBSERVER_CONFIG = {\n    attributes: true,\n    characterData: true,\n    characterDataOldValue: true,\n    childList: true,\n    subtree: true,\n};\nvar MAX_OPTIMIZE_ITERATIONS = 100;\nvar ScrollBlot = /** @class */ (function (_super) {\n    __extends(ScrollBlot, _super);\n    function ScrollBlot(node) {\n        var _this = _super.call(this, node) || this;\n        _this.scroll = _this;\n        _this.observer = new MutationObserver(function (mutations) {\n            _this.update(mutations);\n        });\n        _this.observer.observe(_this.domNode, OBSERVER_CONFIG);\n        _this.attach();\n        return _this;\n    }\n    ScrollBlot.prototype.detach = function () {\n        _super.prototype.detach.call(this);\n        this.observer.disconnect();\n    };\n    ScrollBlot.prototype.deleteAt = function (index, length) {\n        this.update();\n        if (index === 0 && length === this.length()) {\n            this.children.forEach(function (child) {\n                child.remove();\n            });\n        }\n        else {\n            _super.prototype.deleteAt.call(this, index, length);\n        }\n    };\n    ScrollBlot.prototype.formatAt = function (index, length, name, value) {\n        this.update();\n        _super.prototype.formatAt.call(this, index, length, name, value);\n    };\n    ScrollBlot.prototype.insertAt = function (index, value, def) {\n        this.update();\n        _super.prototype.insertAt.call(this, index, value, def);\n    };\n    ScrollBlot.prototype.optimize = function (mutations, context) {\n        var _this = this;\n        if (mutations === void 0) { mutations = []; }\n        if (context === void 0) { context = {}; }\n        _super.prototype.optimize.call(this, context);\n        // We must modify mutations directly, cannot make copy and then modify\n        var records = [].slice.call(this.observer.takeRecords());\n        // Array.push currently seems to be implemented by a non-tail recursive function\n        // so we cannot just mutations.push.apply(mutations, this.observer.takeRecords());\n        while (records.length > 0)\n            mutations.push(records.pop());\n        // TODO use WeakMap\n        var mark = function (blot, markParent) {\n            if (markParent === void 0) { markParent = true; }\n            if (blot == null || blot === _this)\n                return;\n            if (blot.domNode.parentNode == null)\n                return;\n            // @ts-ignore\n            if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations = [];\n            }\n            if (markParent)\n                mark(blot.parent);\n        };\n        var optimize = function (blot) {\n            // Post-order traversal\n            if (\n            // @ts-ignore\n            blot.domNode[Registry.DATA_KEY] == null ||\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations == null) {\n                return;\n            }\n            if (blot instanceof container_1.default) {\n                blot.children.forEach(optimize);\n            }\n            blot.optimize(context);\n        };\n        var remaining = mutations;\n        for (var i = 0; remaining.length > 0; i += 1) {\n            if (i >= MAX_OPTIMIZE_ITERATIONS) {\n                throw new Error('[Parchment] Maximum optimize iterations reached');\n            }\n            remaining.forEach(function (mutation) {\n                var blot = Registry.find(mutation.target, true);\n                if (blot == null)\n                    return;\n                if (blot.domNode === mutation.target) {\n                    if (mutation.type === 'childList') {\n                        mark(Registry.find(mutation.previousSibling, false));\n                        [].forEach.call(mutation.addedNodes, function (node) {\n                            var child = Registry.find(node, false);\n                            mark(child, false);\n                            if (child instanceof container_1.default) {\n                                child.children.forEach(function (grandChild) {\n                                    mark(grandChild, false);\n                                });\n                            }\n                        });\n                    }\n                    else if (mutation.type === 'attributes') {\n                        mark(blot.prev);\n                    }\n                }\n                mark(blot);\n            });\n            this.children.forEach(optimize);\n            remaining = [].slice.call(this.observer.takeRecords());\n            records = remaining.slice();\n            while (records.length > 0)\n                mutations.push(records.pop());\n        }\n    };\n    ScrollBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        if (context === void 0) { context = {}; }\n        mutations = mutations || this.observer.takeRecords();\n        // TODO use WeakMap\n        mutations\n            .map(function (mutation) {\n            var blot = Registry.find(mutation.target, true);\n            if (blot == null)\n                return null;\n            // @ts-ignore\n            if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations = [mutation];\n                return blot;\n            }\n            else {\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations.push(mutation);\n                return null;\n            }\n        })\n            .forEach(function (blot) {\n            if (blot == null ||\n                blot === _this ||\n                //@ts-ignore\n                blot.domNode[Registry.DATA_KEY] == null)\n                return;\n            // @ts-ignore\n            blot.update(blot.domNode[Registry.DATA_KEY].mutations || [], context);\n        });\n        // @ts-ignore\n        if (this.domNode[Registry.DATA_KEY].mutations != null) {\n            // @ts-ignore\n            _super.prototype.update.call(this, this.domNode[Registry.DATA_KEY].mutations, context);\n        }\n        this.optimize(mutations, context);\n    };\n    ScrollBlot.blotName = 'scroll';\n    ScrollBlot.defaultChild = 'block';\n    ScrollBlot.scope = Registry.Scope.BLOCK_BLOT;\n    ScrollBlot.tagName = 'DIV';\n    return ScrollBlot;\n}(container_1.default));\nexports.default = ScrollBlot;\n\n\n/***/ }),\n/* 46 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = __webpack_require__(18);\nvar Registry = __webpack_require__(1);\n// Shallow object comparison\nfunction isEqual(obj1, obj2) {\n    if (Object.keys(obj1).length !== Object.keys(obj2).length)\n        return false;\n    // @ts-ignore\n    for (var prop in obj1) {\n        // @ts-ignore\n        if (obj1[prop] !== obj2[prop])\n            return false;\n    }\n    return true;\n}\nvar InlineBlot = /** @class */ (function (_super) {\n    __extends(InlineBlot, _super);\n    function InlineBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    InlineBlot.formats = function (domNode) {\n        if (domNode.tagName === InlineBlot.tagName)\n            return undefined;\n        return _super.formats.call(this, domNode);\n    };\n    InlineBlot.prototype.format = function (name, value) {\n        var _this = this;\n        if (name === this.statics.blotName && !value) {\n            this.children.forEach(function (child) {\n                if (!(child instanceof format_1.default)) {\n                    child = child.wrap(InlineBlot.blotName, true);\n                }\n                _this.attributes.copy(child);\n            });\n            this.unwrap();\n        }\n        else {\n            _super.prototype.format.call(this, name, value);\n        }\n    };\n    InlineBlot.prototype.formatAt = function (index, length, name, value) {\n        if (this.formats()[name] != null || Registry.query(name, Registry.Scope.ATTRIBUTE)) {\n            var blot = this.isolate(index, length);\n            blot.format(name, value);\n        }\n        else {\n            _super.prototype.formatAt.call(this, index, length, name, value);\n        }\n    };\n    InlineBlot.prototype.optimize = function (context) {\n        _super.prototype.optimize.call(this, context);\n        var formats = this.formats();\n        if (Object.keys(formats).length === 0) {\n            return this.unwrap(); // unformatted span\n        }\n        var next = this.next;\n        if (next instanceof InlineBlot && next.prev === this && isEqual(formats, next.formats())) {\n            next.moveChildren(this);\n            next.remove();\n        }\n    };\n    InlineBlot.blotName = 'inline';\n    InlineBlot.scope = Registry.Scope.INLINE_BLOT;\n    InlineBlot.tagName = 'SPAN';\n    return InlineBlot;\n}(format_1.default));\nexports.default = InlineBlot;\n\n\n/***/ }),\n/* 47 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = __webpack_require__(18);\nvar Registry = __webpack_require__(1);\nvar BlockBlot = /** @class */ (function (_super) {\n    __extends(BlockBlot, _super);\n    function BlockBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    BlockBlot.formats = function (domNode) {\n        var tagName = Registry.query(BlockBlot.blotName).tagName;\n        if (domNode.tagName === tagName)\n            return undefined;\n        return _super.formats.call(this, domNode);\n    };\n    BlockBlot.prototype.format = function (name, value) {\n        if (Registry.query(name, Registry.Scope.BLOCK) == null) {\n            return;\n        }\n        else if (name === this.statics.blotName && !value) {\n            this.replaceWith(BlockBlot.blotName);\n        }\n        else {\n            _super.prototype.format.call(this, name, value);\n        }\n    };\n    BlockBlot.prototype.formatAt = function (index, length, name, value) {\n        if (Registry.query(name, Registry.Scope.BLOCK) != null) {\n            this.format(name, value);\n        }\n        else {\n            _super.prototype.formatAt.call(this, index, length, name, value);\n        }\n    };\n    BlockBlot.prototype.insertAt = function (index, value, def) {\n        if (def == null || Registry.query(value, Registry.Scope.INLINE) != null) {\n            // Insert text or inline\n            _super.prototype.insertAt.call(this, index, value, def);\n        }\n        else {\n            var after = this.split(index);\n            var blot = Registry.create(value, def);\n            after.parent.insertBefore(blot, after);\n        }\n    };\n    BlockBlot.prototype.update = function (mutations, context) {\n        if (navigator.userAgent.match(/Trident/)) {\n            this.build();\n        }\n        else {\n            _super.prototype.update.call(this, mutations, context);\n        }\n    };\n    BlockBlot.blotName = 'block';\n    BlockBlot.scope = Registry.Scope.BLOCK_BLOT;\n    BlockBlot.tagName = 'P';\n    return BlockBlot;\n}(format_1.default));\nexports.default = BlockBlot;\n\n\n/***/ }),\n/* 48 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = __webpack_require__(19);\nvar EmbedBlot = /** @class */ (function (_super) {\n    __extends(EmbedBlot, _super);\n    function EmbedBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    EmbedBlot.formats = function (domNode) {\n        return undefined;\n    };\n    EmbedBlot.prototype.format = function (name, value) {\n        // super.formatAt wraps, which is what we want in general,\n        // but this allows subclasses to overwrite for formats\n        // that just apply to particular embeds\n        _super.prototype.formatAt.call(this, 0, this.length(), name, value);\n    };\n    EmbedBlot.prototype.formatAt = function (index, length, name, value) {\n        if (index === 0 && length === this.length()) {\n            this.format(name, value);\n        }\n        else {\n            _super.prototype.formatAt.call(this, index, length, name, value);\n        }\n    };\n    EmbedBlot.prototype.formats = function () {\n        return this.statics.formats(this.domNode);\n    };\n    return EmbedBlot;\n}(leaf_1.default));\nexports.default = EmbedBlot;\n\n\n/***/ }),\n/* 49 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = __webpack_require__(19);\nvar Registry = __webpack_require__(1);\nvar TextBlot = /** @class */ (function (_super) {\n    __extends(TextBlot, _super);\n    function TextBlot(node) {\n        var _this = _super.call(this, node) || this;\n        _this.text = _this.statics.value(_this.domNode);\n        return _this;\n    }\n    TextBlot.create = function (value) {\n        return document.createTextNode(value);\n    };\n    TextBlot.value = function (domNode) {\n        var text = domNode.data;\n        // @ts-ignore\n        if (text['normalize'])\n            text = text['normalize']();\n        return text;\n    };\n    TextBlot.prototype.deleteAt = function (index, length) {\n        this.domNode.data = this.text = this.text.slice(0, index) + this.text.slice(index + length);\n    };\n    TextBlot.prototype.index = function (node, offset) {\n        if (this.domNode === node) {\n            return offset;\n        }\n        return -1;\n    };\n    TextBlot.prototype.insertAt = function (index, value, def) {\n        if (def == null) {\n            this.text = this.text.slice(0, index) + value + this.text.slice(index);\n            this.domNode.data = this.text;\n        }\n        else {\n            _super.prototype.insertAt.call(this, index, value, def);\n        }\n    };\n    TextBlot.prototype.length = function () {\n        return this.text.length;\n    };\n    TextBlot.prototype.optimize = function (context) {\n        _super.prototype.optimize.call(this, context);\n        this.text = this.statics.value(this.domNode);\n        if (this.text.length === 0) {\n            this.remove();\n        }\n        else if (this.next instanceof TextBlot && this.next.prev === this) {\n            this.insertAt(this.length(), this.next.value());\n            this.next.remove();\n        }\n    };\n    TextBlot.prototype.position = function (index, inclusive) {\n        if (inclusive === void 0) { inclusive = false; }\n        return [this.domNode, index];\n    };\n    TextBlot.prototype.split = function (index, force) {\n        if (force === void 0) { force = false; }\n        if (!force) {\n            if (index === 0)\n                return this;\n            if (index === this.length())\n                return this.next;\n        }\n        var after = Registry.create(this.domNode.splitText(index));\n        this.parent.insertBefore(after, this.next);\n        this.text = this.statics.value(this.domNode);\n        return after;\n    };\n    TextBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        if (mutations.some(function (mutation) {\n            return mutation.type === 'characterData' && mutation.target === _this.domNode;\n        })) {\n            this.text = this.statics.value(this.domNode);\n        }\n    };\n    TextBlot.prototype.value = function () {\n        return this.text;\n    };\n    TextBlot.blotName = 'text';\n    TextBlot.scope = Registry.Scope.INLINE_BLOT;\n    return TextBlot;\n}(leaf_1.default));\nexports.default = TextBlot;\n\n\n/***/ }),\n/* 50 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar elem = document.createElement('div');\nelem.classList.toggle('test-class', false);\nif (elem.classList.contains('test-class')) {\n  var _toggle = DOMTokenList.prototype.toggle;\n  DOMTokenList.prototype.toggle = function (token, force) {\n    if (arguments.length > 1 && !this.contains(token) === !force) {\n      return force;\n    } else {\n      return _toggle.call(this, token);\n    }\n  };\n}\n\nif (!String.prototype.startsWith) {\n  String.prototype.startsWith = function (searchString, position) {\n    position = position || 0;\n    return this.substr(position, searchString.length) === searchString;\n  };\n}\n\nif (!String.prototype.endsWith) {\n  String.prototype.endsWith = function (searchString, position) {\n    var subjectString = this.toString();\n    if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {\n      position = subjectString.length;\n    }\n    position -= searchString.length;\n    var lastIndex = subjectString.indexOf(searchString, position);\n    return lastIndex !== -1 && lastIndex === position;\n  };\n}\n\nif (!Array.prototype.find) {\n  Object.defineProperty(Array.prototype, \"find\", {\n    value: function value(predicate) {\n      if (this === null) {\n        throw new TypeError('Array.prototype.find called on null or undefined');\n      }\n      if (typeof predicate !== 'function') {\n        throw new TypeError('predicate must be a function');\n      }\n      var list = Object(this);\n      var length = list.length >>> 0;\n      var thisArg = arguments[1];\n      var value;\n\n      for (var i = 0; i < length; i++) {\n        value = list[i];\n        if (predicate.call(thisArg, value, i, list)) {\n          return value;\n        }\n      }\n      return undefined;\n    }\n  });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  // Disable resizing in Firefox\n  document.execCommand(\"enableObjectResizing\", false, false);\n  // Disable automatic linkifying in IE11\n  document.execCommand(\"autoUrlDetect\", false, false);\n});\n\n/***/ }),\n/* 51 */\n/***/ (function(module, exports) {\n\n/**\n * This library modifies the diff-patch-match library by Neil Fraser\n * by removing the patch and match functionality and certain advanced\n * options in the diff function. The original license is as follows:\n *\n * ===\n *\n * Diff Match and Patch\n *\n * Copyright 2006 Google Inc.\n * http://code.google.com/p/google-diff-match-patch/\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n/**\n * The data structure representing a diff is an array of tuples:\n * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]\n * which means: delete 'Hello', add 'Goodbye' and keep ' world.'\n */\nvar DIFF_DELETE = -1;\nvar DIFF_INSERT = 1;\nvar DIFF_EQUAL = 0;\n\n\n/**\n * Find the differences between two texts.  Simplifies the problem by stripping\n * any common prefix or suffix off the texts before diffing.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {Int} cursor_pos Expected edit position in text1 (optional)\n * @return {Array} Array of diff tuples.\n */\nfunction diff_main(text1, text2, cursor_pos) {\n  // Check for equality (speedup).\n  if (text1 == text2) {\n    if (text1) {\n      return [[DIFF_EQUAL, text1]];\n    }\n    return [];\n  }\n\n  // Check cursor_pos within bounds\n  if (cursor_pos < 0 || text1.length < cursor_pos) {\n    cursor_pos = null;\n  }\n\n  // Trim off common prefix (speedup).\n  var commonlength = diff_commonPrefix(text1, text2);\n  var commonprefix = text1.substring(0, commonlength);\n  text1 = text1.substring(commonlength);\n  text2 = text2.substring(commonlength);\n\n  // Trim off common suffix (speedup).\n  commonlength = diff_commonSuffix(text1, text2);\n  var commonsuffix = text1.substring(text1.length - commonlength);\n  text1 = text1.substring(0, text1.length - commonlength);\n  text2 = text2.substring(0, text2.length - commonlength);\n\n  // Compute the diff on the middle block.\n  var diffs = diff_compute_(text1, text2);\n\n  // Restore the prefix and suffix.\n  if (commonprefix) {\n    diffs.unshift([DIFF_EQUAL, commonprefix]);\n  }\n  if (commonsuffix) {\n    diffs.push([DIFF_EQUAL, commonsuffix]);\n  }\n  diff_cleanupMerge(diffs);\n  if (cursor_pos != null) {\n    diffs = fix_cursor(diffs, cursor_pos);\n  }\n  diffs = fix_emoji(diffs);\n  return diffs;\n};\n\n\n/**\n * Find the differences between two texts.  Assumes that the texts do not\n * have any common prefix or suffix.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_compute_(text1, text2) {\n  var diffs;\n\n  if (!text1) {\n    // Just add some text (speedup).\n    return [[DIFF_INSERT, text2]];\n  }\n\n  if (!text2) {\n    // Just delete some text (speedup).\n    return [[DIFF_DELETE, text1]];\n  }\n\n  var longtext = text1.length > text2.length ? text1 : text2;\n  var shorttext = text1.length > text2.length ? text2 : text1;\n  var i = longtext.indexOf(shorttext);\n  if (i != -1) {\n    // Shorter text is inside the longer text (speedup).\n    diffs = [[DIFF_INSERT, longtext.substring(0, i)],\n             [DIFF_EQUAL, shorttext],\n             [DIFF_INSERT, longtext.substring(i + shorttext.length)]];\n    // Swap insertions for deletions if diff is reversed.\n    if (text1.length > text2.length) {\n      diffs[0][0] = diffs[2][0] = DIFF_DELETE;\n    }\n    return diffs;\n  }\n\n  if (shorttext.length == 1) {\n    // Single character string.\n    // After the previous speedup, the character can't be an equality.\n    return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n  }\n\n  // Check to see if the problem can be split in two.\n  var hm = diff_halfMatch_(text1, text2);\n  if (hm) {\n    // A half-match was found, sort out the return data.\n    var text1_a = hm[0];\n    var text1_b = hm[1];\n    var text2_a = hm[2];\n    var text2_b = hm[3];\n    var mid_common = hm[4];\n    // Send both pairs off for separate processing.\n    var diffs_a = diff_main(text1_a, text2_a);\n    var diffs_b = diff_main(text1_b, text2_b);\n    // Merge the results.\n    return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b);\n  }\n\n  return diff_bisect_(text1, text2);\n};\n\n\n/**\n * Find the 'middle snake' of a diff, split the problem in two\n * and return the recursively constructed diff.\n * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n * @private\n */\nfunction diff_bisect_(text1, text2) {\n  // Cache the text lengths to prevent multiple calls.\n  var text1_length = text1.length;\n  var text2_length = text2.length;\n  var max_d = Math.ceil((text1_length + text2_length) / 2);\n  var v_offset = max_d;\n  var v_length = 2 * max_d;\n  var v1 = new Array(v_length);\n  var v2 = new Array(v_length);\n  // Setting all elements to -1 is faster in Chrome & Firefox than mixing\n  // integers and undefined.\n  for (var x = 0; x < v_length; x++) {\n    v1[x] = -1;\n    v2[x] = -1;\n  }\n  v1[v_offset + 1] = 0;\n  v2[v_offset + 1] = 0;\n  var delta = text1_length - text2_length;\n  // If the total number of characters is odd, then the front path will collide\n  // with the reverse path.\n  var front = (delta % 2 != 0);\n  // Offsets for start and end of k loop.\n  // Prevents mapping of space beyond the grid.\n  var k1start = 0;\n  var k1end = 0;\n  var k2start = 0;\n  var k2end = 0;\n  for (var d = 0; d < max_d; d++) {\n    // Walk the front path one step.\n    for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n      var k1_offset = v_offset + k1;\n      var x1;\n      if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n        x1 = v1[k1_offset + 1];\n      } else {\n        x1 = v1[k1_offset - 1] + 1;\n      }\n      var y1 = x1 - k1;\n      while (x1 < text1_length && y1 < text2_length &&\n             text1.charAt(x1) == text2.charAt(y1)) {\n        x1++;\n        y1++;\n      }\n      v1[k1_offset] = x1;\n      if (x1 > text1_length) {\n        // Ran off the right of the graph.\n        k1end += 2;\n      } else if (y1 > text2_length) {\n        // Ran off the bottom of the graph.\n        k1start += 2;\n      } else if (front) {\n        var k2_offset = v_offset + delta - k1;\n        if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n          // Mirror x2 onto top-left coordinate system.\n          var x2 = text1_length - v2[k2_offset];\n          if (x1 >= x2) {\n            // Overlap detected.\n            return diff_bisectSplit_(text1, text2, x1, y1);\n          }\n        }\n      }\n    }\n\n    // Walk the reverse path one step.\n    for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n      var k2_offset = v_offset + k2;\n      var x2;\n      if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n        x2 = v2[k2_offset + 1];\n      } else {\n        x2 = v2[k2_offset - 1] + 1;\n      }\n      var y2 = x2 - k2;\n      while (x2 < text1_length && y2 < text2_length &&\n             text1.charAt(text1_length - x2 - 1) ==\n             text2.charAt(text2_length - y2 - 1)) {\n        x2++;\n        y2++;\n      }\n      v2[k2_offset] = x2;\n      if (x2 > text1_length) {\n        // Ran off the left of the graph.\n        k2end += 2;\n      } else if (y2 > text2_length) {\n        // Ran off the top of the graph.\n        k2start += 2;\n      } else if (!front) {\n        var k1_offset = v_offset + delta - k2;\n        if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n          var x1 = v1[k1_offset];\n          var y1 = v_offset + x1 - k1_offset;\n          // Mirror x2 onto top-left coordinate system.\n          x2 = text1_length - x2;\n          if (x1 >= x2) {\n            // Overlap detected.\n            return diff_bisectSplit_(text1, text2, x1, y1);\n          }\n        }\n      }\n    }\n  }\n  // Diff took too long and hit the deadline or\n  // number of diffs equals number of characters, no commonality at all.\n  return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n};\n\n\n/**\n * Given the location of the 'middle snake', split the diff in two parts\n * and recurse.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} x Index of split point in text1.\n * @param {number} y Index of split point in text2.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_bisectSplit_(text1, text2, x, y) {\n  var text1a = text1.substring(0, x);\n  var text2a = text2.substring(0, y);\n  var text1b = text1.substring(x);\n  var text2b = text2.substring(y);\n\n  // Compute both diffs serially.\n  var diffs = diff_main(text1a, text2a);\n  var diffsb = diff_main(text1b, text2b);\n\n  return diffs.concat(diffsb);\n};\n\n\n/**\n * Determine the common prefix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the start of each\n *     string.\n */\nfunction diff_commonPrefix(text1, text2) {\n  // Quick check for common null cases.\n  if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {\n    return 0;\n  }\n  // Binary search.\n  // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n  var pointermin = 0;\n  var pointermax = Math.min(text1.length, text2.length);\n  var pointermid = pointermax;\n  var pointerstart = 0;\n  while (pointermin < pointermid) {\n    if (text1.substring(pointerstart, pointermid) ==\n        text2.substring(pointerstart, pointermid)) {\n      pointermin = pointermid;\n      pointerstart = pointermin;\n    } else {\n      pointermax = pointermid;\n    }\n    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n  }\n  return pointermid;\n};\n\n\n/**\n * Determine the common suffix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the end of each string.\n */\nfunction diff_commonSuffix(text1, text2) {\n  // Quick check for common null cases.\n  if (!text1 || !text2 ||\n      text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {\n    return 0;\n  }\n  // Binary search.\n  // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n  var pointermin = 0;\n  var pointermax = Math.min(text1.length, text2.length);\n  var pointermid = pointermax;\n  var pointerend = 0;\n  while (pointermin < pointermid) {\n    if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==\n        text2.substring(text2.length - pointermid, text2.length - pointerend)) {\n      pointermin = pointermid;\n      pointerend = pointermin;\n    } else {\n      pointermax = pointermid;\n    }\n    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n  }\n  return pointermid;\n};\n\n\n/**\n * Do the two texts share a substring which is at least half the length of the\n * longer text?\n * This speedup can produce non-minimal diffs.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {Array.<string>} Five element Array, containing the prefix of\n *     text1, the suffix of text1, the prefix of text2, the suffix of\n *     text2 and the common middle.  Or null if there was no match.\n */\nfunction diff_halfMatch_(text1, text2) {\n  var longtext = text1.length > text2.length ? text1 : text2;\n  var shorttext = text1.length > text2.length ? text2 : text1;\n  if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {\n    return null;  // Pointless.\n  }\n\n  /**\n   * Does a substring of shorttext exist within longtext such that the substring\n   * is at least half the length of longtext?\n   * Closure, but does not reference any external variables.\n   * @param {string} longtext Longer string.\n   * @param {string} shorttext Shorter string.\n   * @param {number} i Start index of quarter length substring within longtext.\n   * @return {Array.<string>} Five element Array, containing the prefix of\n   *     longtext, the suffix of longtext, the prefix of shorttext, the suffix\n   *     of shorttext and the common middle.  Or null if there was no match.\n   * @private\n   */\n  function diff_halfMatchI_(longtext, shorttext, i) {\n    // Start with a 1/4 length substring at position i as a seed.\n    var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));\n    var j = -1;\n    var best_common = '';\n    var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;\n    while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n      var prefixLength = diff_commonPrefix(longtext.substring(i),\n                                           shorttext.substring(j));\n      var suffixLength = diff_commonSuffix(longtext.substring(0, i),\n                                           shorttext.substring(0, j));\n      if (best_common.length < suffixLength + prefixLength) {\n        best_common = shorttext.substring(j - suffixLength, j) +\n            shorttext.substring(j, j + prefixLength);\n        best_longtext_a = longtext.substring(0, i - suffixLength);\n        best_longtext_b = longtext.substring(i + prefixLength);\n        best_shorttext_a = shorttext.substring(0, j - suffixLength);\n        best_shorttext_b = shorttext.substring(j + prefixLength);\n      }\n    }\n    if (best_common.length * 2 >= longtext.length) {\n      return [best_longtext_a, best_longtext_b,\n              best_shorttext_a, best_shorttext_b, best_common];\n    } else {\n      return null;\n    }\n  }\n\n  // First check if the second quarter is the seed for a half-match.\n  var hm1 = diff_halfMatchI_(longtext, shorttext,\n                             Math.ceil(longtext.length / 4));\n  // Check again based on the third quarter.\n  var hm2 = diff_halfMatchI_(longtext, shorttext,\n                             Math.ceil(longtext.length / 2));\n  var hm;\n  if (!hm1 && !hm2) {\n    return null;\n  } else if (!hm2) {\n    hm = hm1;\n  } else if (!hm1) {\n    hm = hm2;\n  } else {\n    // Both matched.  Select the longest.\n    hm = hm1[4].length > hm2[4].length ? hm1 : hm2;\n  }\n\n  // A half-match was found, sort out the return data.\n  var text1_a, text1_b, text2_a, text2_b;\n  if (text1.length > text2.length) {\n    text1_a = hm[0];\n    text1_b = hm[1];\n    text2_a = hm[2];\n    text2_b = hm[3];\n  } else {\n    text2_a = hm[0];\n    text2_b = hm[1];\n    text1_a = hm[2];\n    text1_b = hm[3];\n  }\n  var mid_common = hm[4];\n  return [text1_a, text1_b, text2_a, text2_b, mid_common];\n};\n\n\n/**\n * Reorder and merge like edit sections.  Merge equalities.\n * Any edit section can move as long as it doesn't cross an equality.\n * @param {Array} diffs Array of diff tuples.\n */\nfunction diff_cleanupMerge(diffs) {\n  diffs.push([DIFF_EQUAL, '']);  // Add a dummy entry at the end.\n  var pointer = 0;\n  var count_delete = 0;\n  var count_insert = 0;\n  var text_delete = '';\n  var text_insert = '';\n  var commonlength;\n  while (pointer < diffs.length) {\n    switch (diffs[pointer][0]) {\n      case DIFF_INSERT:\n        count_insert++;\n        text_insert += diffs[pointer][1];\n        pointer++;\n        break;\n      case DIFF_DELETE:\n        count_delete++;\n        text_delete += diffs[pointer][1];\n        pointer++;\n        break;\n      case DIFF_EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete + count_insert > 1) {\n          if (count_delete !== 0 && count_insert !== 0) {\n            // Factor out any common prefixies.\n            commonlength = diff_commonPrefix(text_insert, text_delete);\n            if (commonlength !== 0) {\n              if ((pointer - count_delete - count_insert) > 0 &&\n                  diffs[pointer - count_delete - count_insert - 1][0] ==\n                  DIFF_EQUAL) {\n                diffs[pointer - count_delete - count_insert - 1][1] +=\n                    text_insert.substring(0, commonlength);\n              } else {\n                diffs.splice(0, 0, [DIFF_EQUAL,\n                                    text_insert.substring(0, commonlength)]);\n                pointer++;\n              }\n              text_insert = text_insert.substring(commonlength);\n              text_delete = text_delete.substring(commonlength);\n            }\n            // Factor out any common suffixies.\n            commonlength = diff_commonSuffix(text_insert, text_delete);\n            if (commonlength !== 0) {\n              diffs[pointer][1] = text_insert.substring(text_insert.length -\n                  commonlength) + diffs[pointer][1];\n              text_insert = text_insert.substring(0, text_insert.length -\n                  commonlength);\n              text_delete = text_delete.substring(0, text_delete.length -\n                  commonlength);\n            }\n          }\n          // Delete the offending records and add the merged ones.\n          if (count_delete === 0) {\n            diffs.splice(pointer - count_insert,\n                count_delete + count_insert, [DIFF_INSERT, text_insert]);\n          } else if (count_insert === 0) {\n            diffs.splice(pointer - count_delete,\n                count_delete + count_insert, [DIFF_DELETE, text_delete]);\n          } else {\n            diffs.splice(pointer - count_delete - count_insert,\n                count_delete + count_insert, [DIFF_DELETE, text_delete],\n                [DIFF_INSERT, text_insert]);\n          }\n          pointer = pointer - count_delete - count_insert +\n                    (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1;\n        } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {\n          // Merge this equality with the previous one.\n          diffs[pointer - 1][1] += diffs[pointer][1];\n          diffs.splice(pointer, 1);\n        } else {\n          pointer++;\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = '';\n        text_insert = '';\n        break;\n    }\n  }\n  if (diffs[diffs.length - 1][1] === '') {\n    diffs.pop();  // Remove the dummy entry at the end.\n  }\n\n  // Second pass: look for single edits surrounded on both sides by equalities\n  // which can be shifted sideways to eliminate an equality.\n  // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n  var changes = false;\n  pointer = 1;\n  // Intentionally ignore the first and last element (don't need checking).\n  while (pointer < diffs.length - 1) {\n    if (diffs[pointer - 1][0] == DIFF_EQUAL &&\n        diffs[pointer + 1][0] == DIFF_EQUAL) {\n      // This is a single edit surrounded by equalities.\n      if (diffs[pointer][1].substring(diffs[pointer][1].length -\n          diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) {\n        // Shift the edit over the previous equality.\n        diffs[pointer][1] = diffs[pointer - 1][1] +\n            diffs[pointer][1].substring(0, diffs[pointer][1].length -\n                                        diffs[pointer - 1][1].length);\n        diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];\n        diffs.splice(pointer - 1, 1);\n        changes = true;\n      } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) ==\n          diffs[pointer + 1][1]) {\n        // Shift the edit over the next equality.\n        diffs[pointer - 1][1] += diffs[pointer + 1][1];\n        diffs[pointer][1] =\n            diffs[pointer][1].substring(diffs[pointer + 1][1].length) +\n            diffs[pointer + 1][1];\n        diffs.splice(pointer + 1, 1);\n        changes = true;\n      }\n    }\n    pointer++;\n  }\n  // If shifts were made, the diff needs reordering and another shift sweep.\n  if (changes) {\n    diff_cleanupMerge(diffs);\n  }\n};\n\n\nvar diff = diff_main;\ndiff.INSERT = DIFF_INSERT;\ndiff.DELETE = DIFF_DELETE;\ndiff.EQUAL = DIFF_EQUAL;\n\nmodule.exports = diff;\n\n/*\n * Modify a diff such that the cursor position points to the start of a change:\n * E.g.\n *   cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1)\n *     => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]]\n *   cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2)\n *     => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]]\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} A tuple [cursor location in the modified diff, modified diff]\n */\nfunction cursor_normalize_diff (diffs, cursor_pos) {\n  if (cursor_pos === 0) {\n    return [DIFF_EQUAL, diffs];\n  }\n  for (var current_pos = 0, i = 0; i < diffs.length; i++) {\n    var d = diffs[i];\n    if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) {\n      var next_pos = current_pos + d[1].length;\n      if (cursor_pos === next_pos) {\n        return [i + 1, diffs];\n      } else if (cursor_pos < next_pos) {\n        // copy to prevent side effects\n        diffs = diffs.slice();\n        // split d into two diff changes\n        var split_pos = cursor_pos - current_pos;\n        var d_left = [d[0], d[1].slice(0, split_pos)];\n        var d_right = [d[0], d[1].slice(split_pos)];\n        diffs.splice(i, 1, d_left, d_right);\n        return [i + 1, diffs];\n      } else {\n        current_pos = next_pos;\n      }\n    }\n  }\n  throw new Error('cursor_pos is out of bounds!')\n}\n\n/*\n * Modify a diff such that the edit position is \"shifted\" to the proposed edit location (cursor_position).\n *\n * Case 1)\n *   Check if a naive shift is possible:\n *     [0, X], [ 1, Y] -> [ 1, Y], [0, X]    (if X + Y === Y + X)\n *     [0, X], [-1, Y] -> [-1, Y], [0, X]    (if X + Y === Y + X) - holds same result\n * Case 2)\n *   Check if the following shifts are possible:\n *     [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix']\n *     [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix']\n *         ^            ^\n *         d          d_next\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} Array of diff tuples\n */\nfunction fix_cursor (diffs, cursor_pos) {\n  var norm = cursor_normalize_diff(diffs, cursor_pos);\n  var ndiffs = norm[1];\n  var cursor_pointer = norm[0];\n  var d = ndiffs[cursor_pointer];\n  var d_next = ndiffs[cursor_pointer + 1];\n\n  if (d == null) {\n    // Text was deleted from end of original string,\n    // cursor is now out of bounds in new string\n    return diffs;\n  } else if (d[0] !== DIFF_EQUAL) {\n    // A modification happened at the cursor location.\n    // This is the expected outcome, so we can return the original diff.\n    return diffs;\n  } else {\n    if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) {\n      // Case 1)\n      // It is possible to perform a naive shift\n      ndiffs.splice(cursor_pointer, 2, d_next, d)\n      return merge_tuples(ndiffs, cursor_pointer, 2)\n    } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) {\n      // Case 2)\n      // d[1] is a prefix of d_next[1]\n      // We can assume that d_next[0] !== 0, since d[0] === 0\n      // Shift edit locations..\n      ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]);\n      var suffix = d_next[1].slice(d[1].length);\n      if (suffix.length > 0) {\n        ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]);\n      }\n      return merge_tuples(ndiffs, cursor_pointer, 3)\n    } else {\n      // Not possible to perform any modification\n      return diffs;\n    }\n  }\n}\n\n/*\n * Check diff did not split surrogate pairs.\n * Ex. [0, '\\uD83D'], [-1, '\\uDC36'], [1, '\\uDC2F'] -> [-1, '\\uD83D\\uDC36'], [1, '\\uD83D\\uDC2F']\n *     '\\uD83D\\uDC36' === '🐶', '\\uD83D\\uDC2F' === '🐯'\n *\n * @param {Array} diffs Array of diff tuples\n * @return {Array} Array of diff tuples\n */\nfunction fix_emoji (diffs) {\n  var compact = false;\n  var starts_with_pair_end = function(str) {\n    return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF;\n  }\n  var ends_with_pair_start = function(str) {\n    return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF;\n  }\n  for (var i = 2; i < diffs.length; i += 1) {\n    if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) &&\n        diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) &&\n        diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) {\n      compact = true;\n\n      diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1];\n      diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1];\n\n      diffs[i-2][1] = diffs[i-2][1].slice(0, -1);\n    }\n  }\n  if (!compact) {\n    return diffs;\n  }\n  var fixed_diffs = [];\n  for (var i = 0; i < diffs.length; i += 1) {\n    if (diffs[i][1].length > 0) {\n      fixed_diffs.push(diffs[i]);\n    }\n  }\n  return fixed_diffs;\n}\n\n/*\n * Try to merge tuples with their neigbors in a given range.\n * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab']\n *\n * @param {Array} diffs Array of diff tuples.\n * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]).\n * @param {Int} length Number of consecutive elements to check.\n * @return {Array} Array of merged diff tuples.\n */\nfunction merge_tuples (diffs, start, length) {\n  // Check from (start-1) to (start+length).\n  for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) {\n    if (i + 1 < diffs.length) {\n      var left_d = diffs[i];\n      var right_d = diffs[i+1];\n      if (left_d[0] === right_d[1]) {\n        diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]);\n      }\n    }\n  }\n  return diffs;\n}\n\n\n/***/ }),\n/* 52 */\n/***/ (function(module, exports) {\n\nexports = module.exports = typeof Object.keys === 'function'\n  ? Object.keys : shim;\n\nexports.shim = shim;\nfunction shim (obj) {\n  var keys = [];\n  for (var key in obj) keys.push(key);\n  return keys;\n}\n\n\n/***/ }),\n/* 53 */\n/***/ (function(module, exports) {\n\nvar supportsArgumentsClass = (function(){\n  return Object.prototype.toString.call(arguments)\n})() == '[object Arguments]';\n\nexports = module.exports = supportsArgumentsClass ? supported : unsupported;\n\nexports.supported = supported;\nfunction supported(object) {\n  return Object.prototype.toString.call(object) == '[object Arguments]';\n};\n\nexports.unsupported = unsupported;\nfunction unsupported(object){\n  return object &&\n    typeof object == 'object' &&\n    typeof object.length == 'number' &&\n    Object.prototype.hasOwnProperty.call(object, 'callee') &&\n    !Object.prototype.propertyIsEnumerable.call(object, 'callee') ||\n    false;\n};\n\n\n/***/ }),\n/* 54 */\n/***/ (function(module, exports) {\n\n'use strict';\n\nvar has = Object.prototype.hasOwnProperty\n  , prefix = '~';\n\n/**\n * Constructor to create a storage for our `EE` objects.\n * An `Events` instance is a plain object whose properties are event names.\n *\n * @constructor\n * @api private\n */\nfunction Events() {}\n\n//\n// We try to not inherit from `Object.prototype`. In some engines creating an\n// instance in this way is faster than calling `Object.create(null)` directly.\n// If `Object.create(null)` is not supported we prefix the event names with a\n// character to make sure that the built-in object properties are not\n// overridden or used as an attack vector.\n//\nif (Object.create) {\n  Events.prototype = Object.create(null);\n\n  //\n  // This hack is needed because the `__proto__` property is still inherited in\n  // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n  //\n  if (!new Events().__proto__) prefix = false;\n}\n\n/**\n * Representation of a single event listener.\n *\n * @param {Function} fn The listener function.\n * @param {Mixed} context The context to invoke the listener with.\n * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n * @constructor\n * @api private\n */\nfunction EE(fn, context, once) {\n  this.fn = fn;\n  this.context = context;\n  this.once = once || false;\n}\n\n/**\n * Minimal `EventEmitter` interface that is molded against the Node.js\n * `EventEmitter` interface.\n *\n * @constructor\n * @api public\n */\nfunction EventEmitter() {\n  this._events = new Events();\n  this._eventsCount = 0;\n}\n\n/**\n * Return an array listing the events for which the emitter has registered\n * listeners.\n *\n * @returns {Array}\n * @api public\n */\nEventEmitter.prototype.eventNames = function eventNames() {\n  var names = []\n    , events\n    , name;\n\n  if (this._eventsCount === 0) return names;\n\n  for (name in (events = this._events)) {\n    if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n  }\n\n  if (Object.getOwnPropertySymbols) {\n    return names.concat(Object.getOwnPropertySymbols(events));\n  }\n\n  return names;\n};\n\n/**\n * Return the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Boolean} exists Only check if there are listeners.\n * @returns {Array|Boolean}\n * @api public\n */\nEventEmitter.prototype.listeners = function listeners(event, exists) {\n  var evt = prefix ? prefix + event : event\n    , available = this._events[evt];\n\n  if (exists) return !!available;\n  if (!available) return [];\n  if (available.fn) return [available.fn];\n\n  for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {\n    ee[i] = available[i].fn;\n  }\n\n  return ee;\n};\n\n/**\n * Calls each of the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @returns {Boolean} `true` if the event had listeners, else `false`.\n * @api public\n */\nEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n  var evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) return false;\n\n  var listeners = this._events[evt]\n    , len = arguments.length\n    , args\n    , i;\n\n  if (listeners.fn) {\n    if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n    switch (len) {\n      case 1: return listeners.fn.call(listeners.context), true;\n      case 2: return listeners.fn.call(listeners.context, a1), true;\n      case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n      case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n      case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n      case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n    }\n\n    for (i = 1, args = new Array(len -1); i < len; i++) {\n      args[i - 1] = arguments[i];\n    }\n\n    listeners.fn.apply(listeners.context, args);\n  } else {\n    var length = listeners.length\n      , j;\n\n    for (i = 0; i < length; i++) {\n      if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n      switch (len) {\n        case 1: listeners[i].fn.call(listeners[i].context); break;\n        case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n        case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n        case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n        default:\n          if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n            args[j - 1] = arguments[j];\n          }\n\n          listeners[i].fn.apply(listeners[i].context, args);\n      }\n    }\n  }\n\n  return true;\n};\n\n/**\n * Add a listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.on = function on(event, fn, context) {\n  var listener = new EE(fn, context || this)\n    , evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n  else if (!this._events[evt].fn) this._events[evt].push(listener);\n  else this._events[evt] = [this._events[evt], listener];\n\n  return this;\n};\n\n/**\n * Add a one-time listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.once = function once(event, fn, context) {\n  var listener = new EE(fn, context || this, true)\n    , evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n  else if (!this._events[evt].fn) this._events[evt].push(listener);\n  else this._events[evt] = [this._events[evt], listener];\n\n  return this;\n};\n\n/**\n * Remove the listeners of a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn Only remove the listeners that match this function.\n * @param {Mixed} context Only remove the listeners that have this context.\n * @param {Boolean} once Only remove one-time listeners.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n  var evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) return this;\n  if (!fn) {\n    if (--this._eventsCount === 0) this._events = new Events();\n    else delete this._events[evt];\n    return this;\n  }\n\n  var listeners = this._events[evt];\n\n  if (listeners.fn) {\n    if (\n         listeners.fn === fn\n      && (!once || listeners.once)\n      && (!context || listeners.context === context)\n    ) {\n      if (--this._eventsCount === 0) this._events = new Events();\n      else delete this._events[evt];\n    }\n  } else {\n    for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n      if (\n           listeners[i].fn !== fn\n        || (once && !listeners[i].once)\n        || (context && listeners[i].context !== context)\n      ) {\n        events.push(listeners[i]);\n      }\n    }\n\n    //\n    // Reset the array, or remove it completely if we have no more listeners.\n    //\n    if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n    else if (--this._eventsCount === 0) this._events = new Events();\n    else delete this._events[evt];\n  }\n\n  return this;\n};\n\n/**\n * Remove all listeners, or those of the specified event.\n *\n * @param {String|Symbol} [event] The event name.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n  var evt;\n\n  if (event) {\n    evt = prefix ? prefix + event : event;\n    if (this._events[evt]) {\n      if (--this._eventsCount === 0) this._events = new Events();\n      else delete this._events[evt];\n    }\n  } else {\n    this._events = new Events();\n    this._eventsCount = 0;\n  }\n\n  return this;\n};\n\n//\n// Alias methods names because people roll like that.\n//\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\nEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n//\n// This function doesn't apply anymore.\n//\nEventEmitter.prototype.setMaxListeners = function setMaxListeners() {\n  return this;\n};\n\n//\n// Expose the prefix.\n//\nEventEmitter.prefixed = prefix;\n\n//\n// Allow `EventEmitter` to be imported as module namespace.\n//\nEventEmitter.EventEmitter = EventEmitter;\n\n//\n// Expose the module.\n//\nif ('undefined' !== typeof module) {\n  module.exports = EventEmitter;\n}\n\n\n/***/ }),\n/* 55 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.matchText = exports.matchSpacing = exports.matchNewline = exports.matchBlot = exports.matchAttributor = exports.default = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _extend2 = __webpack_require__(3);\n\nvar _extend3 = _interopRequireDefault(_extend2);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _align = __webpack_require__(36);\n\nvar _background = __webpack_require__(37);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _color = __webpack_require__(26);\n\nvar _direction = __webpack_require__(38);\n\nvar _font = __webpack_require__(39);\n\nvar _size = __webpack_require__(40);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:clipboard');\n\nvar DOM_KEY = '__ql-matcher';\n\nvar CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]];\n\nvar ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) {\n  memo[attr.keyName] = attr;\n  return memo;\n}, {});\n\nvar STYLE_ATTRIBUTORS = [_align.AlignStyle, _background.BackgroundStyle, _color.ColorStyle, _direction.DirectionStyle, _font.FontStyle, _size.SizeStyle].reduce(function (memo, attr) {\n  memo[attr.keyName] = attr;\n  return memo;\n}, {});\n\nvar Clipboard = function (_Module) {\n  _inherits(Clipboard, _Module);\n\n  function Clipboard(quill, options) {\n    _classCallCheck(this, Clipboard);\n\n    var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options));\n\n    _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this));\n    _this.container = _this.quill.addContainer('ql-clipboard');\n    _this.container.setAttribute('contenteditable', true);\n    _this.container.setAttribute('tabindex', -1);\n    _this.matchers = [];\n    CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) {\n      var _ref2 = _slicedToArray(_ref, 2),\n          selector = _ref2[0],\n          matcher = _ref2[1];\n\n      if (!options.matchVisual && matcher === matchSpacing) return;\n      _this.addMatcher(selector, matcher);\n    });\n    return _this;\n  }\n\n  _createClass(Clipboard, [{\n    key: 'addMatcher',\n    value: function addMatcher(selector, matcher) {\n      this.matchers.push([selector, matcher]);\n    }\n  }, {\n    key: 'convert',\n    value: function convert(html) {\n      if (typeof html === 'string') {\n        this.container.innerHTML = html.replace(/\\>\\r?\\n +\\</g, '><'); // Remove spaces between tags\n        return this.convert();\n      }\n      var formats = this.quill.getFormat(this.quill.selection.savedRange.index);\n      if (formats[_code2.default.blotName]) {\n        var text = this.container.innerText;\n        this.container.innerHTML = '';\n        return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName]));\n      }\n\n      var _prepareMatching = this.prepareMatching(),\n          _prepareMatching2 = _slicedToArray(_prepareMatching, 2),\n          elementMatchers = _prepareMatching2[0],\n          textMatchers = _prepareMatching2[1];\n\n      var delta = traverse(this.container, elementMatchers, textMatchers);\n      // Remove trailing newline\n      if (deltaEndsWith(delta, '\\n') && delta.ops[delta.ops.length - 1].attributes == null) {\n        delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1));\n      }\n      debug.log('convert', this.container.innerHTML, delta);\n      this.container.innerHTML = '';\n      return delta;\n    }\n  }, {\n    key: 'dangerouslyPasteHTML',\n    value: function dangerouslyPasteHTML(index, html) {\n      var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _quill2.default.sources.API;\n\n      if (typeof index === 'string') {\n        this.quill.setContents(this.convert(index), html);\n        this.quill.setSelection(0, _quill2.default.sources.SILENT);\n      } else {\n        var paste = this.convert(html);\n        this.quill.updateContents(new _quillDelta2.default().retain(index).concat(paste), source);\n        this.quill.setSelection(index + paste.length(), _quill2.default.sources.SILENT);\n      }\n    }\n  }, {\n    key: 'onPaste',\n    value: function onPaste(e) {\n      var _this2 = this;\n\n      if (e.defaultPrevented || !this.quill.isEnabled()) return;\n      var range = this.quill.getSelection();\n      var delta = new _quillDelta2.default().retain(range.index);\n      var scrollTop = this.quill.scrollingContainer.scrollTop;\n      this.container.focus();\n      this.quill.selection.update(_quill2.default.sources.SILENT);\n      setTimeout(function () {\n        delta = delta.concat(_this2.convert()).delete(range.length);\n        _this2.quill.updateContents(delta, _quill2.default.sources.USER);\n        // range.length contributes to delta.length()\n        _this2.quill.setSelection(delta.length() - range.length, _quill2.default.sources.SILENT);\n        _this2.quill.scrollingContainer.scrollTop = scrollTop;\n        _this2.quill.focus();\n      }, 1);\n    }\n  }, {\n    key: 'prepareMatching',\n    value: function prepareMatching() {\n      var _this3 = this;\n\n      var elementMatchers = [],\n          textMatchers = [];\n      this.matchers.forEach(function (pair) {\n        var _pair = _slicedToArray(pair, 2),\n            selector = _pair[0],\n            matcher = _pair[1];\n\n        switch (selector) {\n          case Node.TEXT_NODE:\n            textMatchers.push(matcher);\n            break;\n          case Node.ELEMENT_NODE:\n            elementMatchers.push(matcher);\n            break;\n          default:\n            [].forEach.call(_this3.container.querySelectorAll(selector), function (node) {\n              // TODO use weakmap\n              node[DOM_KEY] = node[DOM_KEY] || [];\n              node[DOM_KEY].push(matcher);\n            });\n            break;\n        }\n      });\n      return [elementMatchers, textMatchers];\n    }\n  }]);\n\n  return Clipboard;\n}(_module2.default);\n\nClipboard.DEFAULTS = {\n  matchers: [],\n  matchVisual: true\n};\n\nfunction applyFormat(delta, format, value) {\n  if ((typeof format === 'undefined' ? 'undefined' : _typeof(format)) === 'object') {\n    return Object.keys(format).reduce(function (delta, key) {\n      return applyFormat(delta, key, format[key]);\n    }, delta);\n  } else {\n    return delta.reduce(function (delta, op) {\n      if (op.attributes && op.attributes[format]) {\n        return delta.push(op);\n      } else {\n        return delta.insert(op.insert, (0, _extend3.default)({}, _defineProperty({}, format, value), op.attributes));\n      }\n    }, new _quillDelta2.default());\n  }\n}\n\nfunction computeStyle(node) {\n  if (node.nodeType !== Node.ELEMENT_NODE) return {};\n  var DOM_KEY = '__ql-computed-style';\n  return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));\n}\n\nfunction deltaEndsWith(delta, text) {\n  var endText = \"\";\n  for (var i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {\n    var op = delta.ops[i];\n    if (typeof op.insert !== 'string') break;\n    endText = op.insert + endText;\n  }\n  return endText.slice(-1 * text.length) === text;\n}\n\nfunction isLine(node) {\n  if (node.childNodes.length === 0) return false; // Exclude embed blocks\n  var style = computeStyle(node);\n  return ['block', 'list-item'].indexOf(style.display) > -1;\n}\n\nfunction traverse(node, elementMatchers, textMatchers) {\n  // Post-order\n  if (node.nodeType === node.TEXT_NODE) {\n    return textMatchers.reduce(function (delta, matcher) {\n      return matcher(node, delta);\n    }, new _quillDelta2.default());\n  } else if (node.nodeType === node.ELEMENT_NODE) {\n    return [].reduce.call(node.childNodes || [], function (delta, childNode) {\n      var childrenDelta = traverse(childNode, elementMatchers, textMatchers);\n      if (childNode.nodeType === node.ELEMENT_NODE) {\n        childrenDelta = elementMatchers.reduce(function (childrenDelta, matcher) {\n          return matcher(childNode, childrenDelta);\n        }, childrenDelta);\n        childrenDelta = (childNode[DOM_KEY] || []).reduce(function (childrenDelta, matcher) {\n          return matcher(childNode, childrenDelta);\n        }, childrenDelta);\n      }\n      return delta.concat(childrenDelta);\n    }, new _quillDelta2.default());\n  } else {\n    return new _quillDelta2.default();\n  }\n}\n\nfunction matchAlias(format, node, delta) {\n  return applyFormat(delta, format, true);\n}\n\nfunction matchAttributor(node, delta) {\n  var attributes = _parchment2.default.Attributor.Attribute.keys(node);\n  var classes = _parchment2.default.Attributor.Class.keys(node);\n  var styles = _parchment2.default.Attributor.Style.keys(node);\n  var formats = {};\n  attributes.concat(classes).concat(styles).forEach(function (name) {\n    var attr = _parchment2.default.query(name, _parchment2.default.Scope.ATTRIBUTE);\n    if (attr != null) {\n      formats[attr.attrName] = attr.value(node);\n      if (formats[attr.attrName]) return;\n    }\n    attr = ATTRIBUTE_ATTRIBUTORS[name];\n    if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n      formats[attr.attrName] = attr.value(node) || undefined;\n    }\n    attr = STYLE_ATTRIBUTORS[name];\n    if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n      attr = STYLE_ATTRIBUTORS[name];\n      formats[attr.attrName] = attr.value(node) || undefined;\n    }\n  });\n  if (Object.keys(formats).length > 0) {\n    delta = applyFormat(delta, formats);\n  }\n  return delta;\n}\n\nfunction matchBlot(node, delta) {\n  var match = _parchment2.default.query(node);\n  if (match == null) return delta;\n  if (match.prototype instanceof _parchment2.default.Embed) {\n    var embed = {};\n    var value = match.value(node);\n    if (value != null) {\n      embed[match.blotName] = value;\n      delta = new _quillDelta2.default().insert(embed, match.formats(node));\n    }\n  } else if (typeof match.formats === 'function') {\n    delta = applyFormat(delta, match.blotName, match.formats(node));\n  }\n  return delta;\n}\n\nfunction matchBreak(node, delta) {\n  if (!deltaEndsWith(delta, '\\n')) {\n    delta.insert('\\n');\n  }\n  return delta;\n}\n\nfunction matchIgnore() {\n  return new _quillDelta2.default();\n}\n\nfunction matchIndent(node, delta) {\n  var match = _parchment2.default.query(node);\n  if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\\n')) {\n    return delta;\n  }\n  var indent = -1,\n      parent = node.parentNode;\n  while (!parent.classList.contains('ql-clipboard')) {\n    if ((_parchment2.default.query(parent) || {}).blotName === 'list') {\n      indent += 1;\n    }\n    parent = parent.parentNode;\n  }\n  if (indent <= 0) return delta;\n  return delta.compose(new _quillDelta2.default().retain(delta.length() - 1).retain(1, { indent: indent }));\n}\n\nfunction matchNewline(node, delta) {\n  if (!deltaEndsWith(delta, '\\n')) {\n    if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) {\n      delta.insert('\\n');\n    }\n  }\n  return delta;\n}\n\nfunction matchSpacing(node, delta) {\n  if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\\n\\n')) {\n    var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);\n    if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) {\n      delta.insert('\\n');\n    }\n  }\n  return delta;\n}\n\nfunction matchStyles(node, delta) {\n  var formats = {};\n  var style = node.style || {};\n  if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {\n    formats.italic = true;\n  }\n  if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) {\n    formats.bold = true;\n  }\n  if (Object.keys(formats).length > 0) {\n    delta = applyFormat(delta, formats);\n  }\n  if (parseFloat(style.textIndent || 0) > 0) {\n    // Could be 0.5in\n    delta = new _quillDelta2.default().insert('\\t').concat(delta);\n  }\n  return delta;\n}\n\nfunction matchText(node, delta) {\n  var text = node.data;\n  // Word represents empty line with <o:p>&nbsp;</o:p>\n  if (node.parentNode.tagName === 'O:P') {\n    return delta.insert(text.trim());\n  }\n  if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) {\n    return delta;\n  }\n  if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {\n    // eslint-disable-next-line func-style\n    var replacer = function replacer(collapse, match) {\n      match = match.replace(/[^\\u00a0]/g, ''); // \\u00a0 is nbsp;\n      return match.length < 1 && collapse ? ' ' : match;\n    };\n    text = text.replace(/\\r\\n/g, ' ').replace(/\\n/g, ' ');\n    text = text.replace(/\\s\\s+/g, replacer.bind(replacer, true)); // collapse whitespace\n    if (node.previousSibling == null && isLine(node.parentNode) || node.previousSibling != null && isLine(node.previousSibling)) {\n      text = text.replace(/^\\s+/, replacer.bind(replacer, false));\n    }\n    if (node.nextSibling == null && isLine(node.parentNode) || node.nextSibling != null && isLine(node.nextSibling)) {\n      text = text.replace(/\\s+$/, replacer.bind(replacer, false));\n    }\n  }\n  return delta.insert(text);\n}\n\nexports.default = Clipboard;\nexports.matchAttributor = matchAttributor;\nexports.matchBlot = matchBlot;\nexports.matchNewline = matchNewline;\nexports.matchSpacing = matchSpacing;\nexports.matchText = matchText;\n\n/***/ }),\n/* 56 */,\n/* 57 */,\n/* 58 */,\n/* 59 */,\n/* 60 */,\n/* 61 */,\n/* 62 */,\n/* 63 */,\n/* 64 */,\n/* 65 */,\n/* 66 */,\n/* 67 */,\n/* 68 */,\n/* 69 */,\n/* 70 */,\n/* 71 */,\n/* 72 */,\n/* 73 */,\n/* 74 */,\n/* 75 */,\n/* 76 */,\n/* 77 */,\n/* 78 */,\n/* 79 */,\n/* 80 */,\n/* 81 */,\n/* 82 */,\n/* 83 */,\n/* 84 */,\n/* 85 */,\n/* 86 */,\n/* 87 */,\n/* 88 */,\n/* 89 */,\n/* 90 */,\n/* 91 */,\n/* 92 */,\n/* 93 */,\n/* 94 */,\n/* 95 */,\n/* 96 */,\n/* 97 */,\n/* 98 */,\n/* 99 */,\n/* 100 */,\n/* 101 */,\n/* 102 */,\n/* 103 */,\n/* 104 */,\n/* 105 */,\n/* 106 */,\n/* 107 */,\n/* 108 */,\n/* 109 */,\n/* 110 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = __webpack_require__(29);\n\n\n/***/ })\n/******/ ])[\"default\"];\n});"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/quill/quill.js",
    "content": "/*!\n * Quill Editor v1.3.7\n * https://quilljs.com/\n * Copyright (c) 2014, Jason Chen\n * Copyright (c) 2013, salesforce.com\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Quill\"] = factory();\n\telse\n\t\troot[\"Quill\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 109);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = __webpack_require__(17);\nvar format_1 = __webpack_require__(18);\nvar leaf_1 = __webpack_require__(19);\nvar scroll_1 = __webpack_require__(45);\nvar inline_1 = __webpack_require__(46);\nvar block_1 = __webpack_require__(47);\nvar embed_1 = __webpack_require__(48);\nvar text_1 = __webpack_require__(49);\nvar attributor_1 = __webpack_require__(12);\nvar class_1 = __webpack_require__(32);\nvar style_1 = __webpack_require__(33);\nvar store_1 = __webpack_require__(31);\nvar Registry = __webpack_require__(1);\nvar Parchment = {\n    Scope: Registry.Scope,\n    create: Registry.create,\n    find: Registry.find,\n    query: Registry.query,\n    register: Registry.register,\n    Container: container_1.default,\n    Format: format_1.default,\n    Leaf: leaf_1.default,\n    Embed: embed_1.default,\n    Scroll: scroll_1.default,\n    Block: block_1.default,\n    Inline: inline_1.default,\n    Text: text_1.default,\n    Attributor: {\n        Attribute: attributor_1.default,\n        Class: class_1.default,\n        Style: style_1.default,\n        Store: store_1.default,\n    },\n};\nexports.default = Parchment;\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar ParchmentError = /** @class */ (function (_super) {\n    __extends(ParchmentError, _super);\n    function ParchmentError(message) {\n        var _this = this;\n        message = '[Parchment] ' + message;\n        _this = _super.call(this, message) || this;\n        _this.message = message;\n        _this.name = _this.constructor.name;\n        return _this;\n    }\n    return ParchmentError;\n}(Error));\nexports.ParchmentError = ParchmentError;\nvar attributes = {};\nvar classes = {};\nvar tags = {};\nvar types = {};\nexports.DATA_KEY = '__blot';\nvar Scope;\n(function (Scope) {\n    Scope[Scope[\"TYPE\"] = 3] = \"TYPE\";\n    Scope[Scope[\"LEVEL\"] = 12] = \"LEVEL\";\n    Scope[Scope[\"ATTRIBUTE\"] = 13] = \"ATTRIBUTE\";\n    Scope[Scope[\"BLOT\"] = 14] = \"BLOT\";\n    Scope[Scope[\"INLINE\"] = 7] = \"INLINE\";\n    Scope[Scope[\"BLOCK\"] = 11] = \"BLOCK\";\n    Scope[Scope[\"BLOCK_BLOT\"] = 10] = \"BLOCK_BLOT\";\n    Scope[Scope[\"INLINE_BLOT\"] = 6] = \"INLINE_BLOT\";\n    Scope[Scope[\"BLOCK_ATTRIBUTE\"] = 9] = \"BLOCK_ATTRIBUTE\";\n    Scope[Scope[\"INLINE_ATTRIBUTE\"] = 5] = \"INLINE_ATTRIBUTE\";\n    Scope[Scope[\"ANY\"] = 15] = \"ANY\";\n})(Scope = exports.Scope || (exports.Scope = {}));\nfunction create(input, value) {\n    var match = query(input);\n    if (match == null) {\n        throw new ParchmentError(\"Unable to create \" + input + \" blot\");\n    }\n    var BlotClass = match;\n    var node = \n    // @ts-ignore\n    input instanceof Node || input['nodeType'] === Node.TEXT_NODE ? input : BlotClass.create(value);\n    return new BlotClass(node, value);\n}\nexports.create = create;\nfunction find(node, bubble) {\n    if (bubble === void 0) { bubble = false; }\n    if (node == null)\n        return null;\n    // @ts-ignore\n    if (node[exports.DATA_KEY] != null)\n        return node[exports.DATA_KEY].blot;\n    if (bubble)\n        return find(node.parentNode, bubble);\n    return null;\n}\nexports.find = find;\nfunction query(query, scope) {\n    if (scope === void 0) { scope = Scope.ANY; }\n    var match;\n    if (typeof query === 'string') {\n        match = types[query] || attributes[query];\n        // @ts-ignore\n    }\n    else if (query instanceof Text || query['nodeType'] === Node.TEXT_NODE) {\n        match = types['text'];\n    }\n    else if (typeof query === 'number') {\n        if (query & Scope.LEVEL & Scope.BLOCK) {\n            match = types['block'];\n        }\n        else if (query & Scope.LEVEL & Scope.INLINE) {\n            match = types['inline'];\n        }\n    }\n    else if (query instanceof HTMLElement) {\n        var names = (query.getAttribute('class') || '').split(/\\s+/);\n        for (var i in names) {\n            match = classes[names[i]];\n            if (match)\n                break;\n        }\n        match = match || tags[query.tagName];\n    }\n    if (match == null)\n        return null;\n    // @ts-ignore\n    if (scope & Scope.LEVEL & match.scope && scope & Scope.TYPE & match.scope)\n        return match;\n    return null;\n}\nexports.query = query;\nfunction register() {\n    var Definitions = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        Definitions[_i] = arguments[_i];\n    }\n    if (Definitions.length > 1) {\n        return Definitions.map(function (d) {\n            return register(d);\n        });\n    }\n    var Definition = Definitions[0];\n    if (typeof Definition.blotName !== 'string' && typeof Definition.attrName !== 'string') {\n        throw new ParchmentError('Invalid definition');\n    }\n    else if (Definition.blotName === 'abstract') {\n        throw new ParchmentError('Cannot register abstract class');\n    }\n    types[Definition.blotName || Definition.attrName] = Definition;\n    if (typeof Definition.keyName === 'string') {\n        attributes[Definition.keyName] = Definition;\n    }\n    else {\n        if (Definition.className != null) {\n            classes[Definition.className] = Definition;\n        }\n        if (Definition.tagName != null) {\n            if (Array.isArray(Definition.tagName)) {\n                Definition.tagName = Definition.tagName.map(function (tagName) {\n                    return tagName.toUpperCase();\n                });\n            }\n            else {\n                Definition.tagName = Definition.tagName.toUpperCase();\n            }\n            var tagNames = Array.isArray(Definition.tagName) ? Definition.tagName : [Definition.tagName];\n            tagNames.forEach(function (tag) {\n                if (tags[tag] == null || Definition.className == null) {\n                    tags[tag] = Definition;\n                }\n            });\n        }\n    }\n    return Definition;\n}\nexports.register = register;\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar diff = __webpack_require__(51);\nvar equal = __webpack_require__(11);\nvar extend = __webpack_require__(3);\nvar op = __webpack_require__(20);\n\n\nvar NULL_CHARACTER = String.fromCharCode(0);  // Placeholder char for embed in diff()\n\n\nvar Delta = function (ops) {\n  // Assume we are given a well formed ops\n  if (Array.isArray(ops)) {\n    this.ops = ops;\n  } else if (ops != null && Array.isArray(ops.ops)) {\n    this.ops = ops.ops;\n  } else {\n    this.ops = [];\n  }\n};\n\n\nDelta.prototype.insert = function (text, attributes) {\n  var newOp = {};\n  if (text.length === 0) return this;\n  newOp.insert = text;\n  if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n    newOp.attributes = attributes;\n  }\n  return this.push(newOp);\n};\n\nDelta.prototype['delete'] = function (length) {\n  if (length <= 0) return this;\n  return this.push({ 'delete': length });\n};\n\nDelta.prototype.retain = function (length, attributes) {\n  if (length <= 0) return this;\n  var newOp = { retain: length };\n  if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n    newOp.attributes = attributes;\n  }\n  return this.push(newOp);\n};\n\nDelta.prototype.push = function (newOp) {\n  var index = this.ops.length;\n  var lastOp = this.ops[index - 1];\n  newOp = extend(true, {}, newOp);\n  if (typeof lastOp === 'object') {\n    if (typeof newOp['delete'] === 'number' && typeof lastOp['delete'] === 'number') {\n      this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] };\n      return this;\n    }\n    // Since it does not matter if we insert before or after deleting at the same index,\n    // always prefer to insert first\n    if (typeof lastOp['delete'] === 'number' && newOp.insert != null) {\n      index -= 1;\n      lastOp = this.ops[index - 1];\n      if (typeof lastOp !== 'object') {\n        this.ops.unshift(newOp);\n        return this;\n      }\n    }\n    if (equal(newOp.attributes, lastOp.attributes)) {\n      if (typeof newOp.insert === 'string' && typeof lastOp.insert === 'string') {\n        this.ops[index - 1] = { insert: lastOp.insert + newOp.insert };\n        if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n        return this;\n      } else if (typeof newOp.retain === 'number' && typeof lastOp.retain === 'number') {\n        this.ops[index - 1] = { retain: lastOp.retain + newOp.retain };\n        if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n        return this;\n      }\n    }\n  }\n  if (index === this.ops.length) {\n    this.ops.push(newOp);\n  } else {\n    this.ops.splice(index, 0, newOp);\n  }\n  return this;\n};\n\nDelta.prototype.chop = function () {\n  var lastOp = this.ops[this.ops.length - 1];\n  if (lastOp && lastOp.retain && !lastOp.attributes) {\n    this.ops.pop();\n  }\n  return this;\n};\n\nDelta.prototype.filter = function (predicate) {\n  return this.ops.filter(predicate);\n};\n\nDelta.prototype.forEach = function (predicate) {\n  this.ops.forEach(predicate);\n};\n\nDelta.prototype.map = function (predicate) {\n  return this.ops.map(predicate);\n};\n\nDelta.prototype.partition = function (predicate) {\n  var passed = [], failed = [];\n  this.forEach(function(op) {\n    var target = predicate(op) ? passed : failed;\n    target.push(op);\n  });\n  return [passed, failed];\n};\n\nDelta.prototype.reduce = function (predicate, initial) {\n  return this.ops.reduce(predicate, initial);\n};\n\nDelta.prototype.changeLength = function () {\n  return this.reduce(function (length, elem) {\n    if (elem.insert) {\n      return length + op.length(elem);\n    } else if (elem.delete) {\n      return length - elem.delete;\n    }\n    return length;\n  }, 0);\n};\n\nDelta.prototype.length = function () {\n  return this.reduce(function (length, elem) {\n    return length + op.length(elem);\n  }, 0);\n};\n\nDelta.prototype.slice = function (start, end) {\n  start = start || 0;\n  if (typeof end !== 'number') end = Infinity;\n  var ops = [];\n  var iter = op.iterator(this.ops);\n  var index = 0;\n  while (index < end && iter.hasNext()) {\n    var nextOp;\n    if (index < start) {\n      nextOp = iter.next(start - index);\n    } else {\n      nextOp = iter.next(end - index);\n      ops.push(nextOp);\n    }\n    index += op.length(nextOp);\n  }\n  return new Delta(ops);\n};\n\n\nDelta.prototype.compose = function (other) {\n  var thisIter = op.iterator(this.ops);\n  var otherIter = op.iterator(other.ops);\n  var ops = [];\n  var firstOther = otherIter.peek();\n  if (firstOther != null && typeof firstOther.retain === 'number' && firstOther.attributes == null) {\n    var firstLeft = firstOther.retain;\n    while (thisIter.peekType() === 'insert' && thisIter.peekLength() <= firstLeft) {\n      firstLeft -= thisIter.peekLength();\n      ops.push(thisIter.next());\n    }\n    if (firstOther.retain - firstLeft > 0) {\n      otherIter.next(firstOther.retain - firstLeft);\n    }\n  }\n  var delta = new Delta(ops);\n  while (thisIter.hasNext() || otherIter.hasNext()) {\n    if (otherIter.peekType() === 'insert') {\n      delta.push(otherIter.next());\n    } else if (thisIter.peekType() === 'delete') {\n      delta.push(thisIter.next());\n    } else {\n      var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n      var thisOp = thisIter.next(length);\n      var otherOp = otherIter.next(length);\n      if (typeof otherOp.retain === 'number') {\n        var newOp = {};\n        if (typeof thisOp.retain === 'number') {\n          newOp.retain = length;\n        } else {\n          newOp.insert = thisOp.insert;\n        }\n        // Preserve null when composing with a retain, otherwise remove it for inserts\n        var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number');\n        if (attributes) newOp.attributes = attributes;\n        delta.push(newOp);\n\n        // Optimization if rest of other is just retain\n        if (!otherIter.hasNext() && equal(delta.ops[delta.ops.length - 1], newOp)) {\n          var rest = new Delta(thisIter.rest());\n          return delta.concat(rest).chop();\n        }\n\n      // Other op should be delete, we could be an insert or retain\n      // Insert + delete cancels out\n      } else if (typeof otherOp['delete'] === 'number' && typeof thisOp.retain === 'number') {\n        delta.push(otherOp);\n      }\n    }\n  }\n  return delta.chop();\n};\n\nDelta.prototype.concat = function (other) {\n  var delta = new Delta(this.ops.slice());\n  if (other.ops.length > 0) {\n    delta.push(other.ops[0]);\n    delta.ops = delta.ops.concat(other.ops.slice(1));\n  }\n  return delta;\n};\n\nDelta.prototype.diff = function (other, index) {\n  if (this.ops === other.ops) {\n    return new Delta();\n  }\n  var strings = [this, other].map(function (delta) {\n    return delta.map(function (op) {\n      if (op.insert != null) {\n        return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER;\n      }\n      var prep = (delta === other) ? 'on' : 'with';\n      throw new Error('diff() called ' + prep + ' non-document');\n    }).join('');\n  });\n  var delta = new Delta();\n  var diffResult = diff(strings[0], strings[1], index);\n  var thisIter = op.iterator(this.ops);\n  var otherIter = op.iterator(other.ops);\n  diffResult.forEach(function (component) {\n    var length = component[1].length;\n    while (length > 0) {\n      var opLength = 0;\n      switch (component[0]) {\n        case diff.INSERT:\n          opLength = Math.min(otherIter.peekLength(), length);\n          delta.push(otherIter.next(opLength));\n          break;\n        case diff.DELETE:\n          opLength = Math.min(length, thisIter.peekLength());\n          thisIter.next(opLength);\n          delta['delete'](opLength);\n          break;\n        case diff.EQUAL:\n          opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length);\n          var thisOp = thisIter.next(opLength);\n          var otherOp = otherIter.next(opLength);\n          if (equal(thisOp.insert, otherOp.insert)) {\n            delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes));\n          } else {\n            delta.push(otherOp)['delete'](opLength);\n          }\n          break;\n      }\n      length -= opLength;\n    }\n  });\n  return delta.chop();\n};\n\nDelta.prototype.eachLine = function (predicate, newline) {\n  newline = newline || '\\n';\n  var iter = op.iterator(this.ops);\n  var line = new Delta();\n  var i = 0;\n  while (iter.hasNext()) {\n    if (iter.peekType() !== 'insert') return;\n    var thisOp = iter.peek();\n    var start = op.length(thisOp) - iter.peekLength();\n    var index = typeof thisOp.insert === 'string' ?\n      thisOp.insert.indexOf(newline, start) - start : -1;\n    if (index < 0) {\n      line.push(iter.next());\n    } else if (index > 0) {\n      line.push(iter.next(index));\n    } else {\n      if (predicate(line, iter.next(1).attributes || {}, i) === false) {\n        return;\n      }\n      i += 1;\n      line = new Delta();\n    }\n  }\n  if (line.length() > 0) {\n    predicate(line, {}, i);\n  }\n};\n\nDelta.prototype.transform = function (other, priority) {\n  priority = !!priority;\n  if (typeof other === 'number') {\n    return this.transformPosition(other, priority);\n  }\n  var thisIter = op.iterator(this.ops);\n  var otherIter = op.iterator(other.ops);\n  var delta = new Delta();\n  while (thisIter.hasNext() || otherIter.hasNext()) {\n    if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) {\n      delta.retain(op.length(thisIter.next()));\n    } else if (otherIter.peekType() === 'insert') {\n      delta.push(otherIter.next());\n    } else {\n      var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n      var thisOp = thisIter.next(length);\n      var otherOp = otherIter.next(length);\n      if (thisOp['delete']) {\n        // Our delete either makes their delete redundant or removes their retain\n        continue;\n      } else if (otherOp['delete']) {\n        delta.push(otherOp);\n      } else {\n        // We retain either their retain or insert\n        delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority));\n      }\n    }\n  }\n  return delta.chop();\n};\n\nDelta.prototype.transformPosition = function (index, priority) {\n  priority = !!priority;\n  var thisIter = op.iterator(this.ops);\n  var offset = 0;\n  while (thisIter.hasNext() && offset <= index) {\n    var length = thisIter.peekLength();\n    var nextType = thisIter.peekType();\n    thisIter.next();\n    if (nextType === 'delete') {\n      index -= Math.min(length, index - offset);\n      continue;\n    } else if (nextType === 'insert' && (offset < index || !priority)) {\n      index += length;\n    }\n    offset += length;\n  }\n  return index;\n};\n\n\nmodule.exports = Delta;\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports) {\n\n'use strict';\n\nvar hasOwn = Object.prototype.hasOwnProperty;\nvar toStr = Object.prototype.toString;\nvar defineProperty = Object.defineProperty;\nvar gOPD = Object.getOwnPropertyDescriptor;\n\nvar isArray = function isArray(arr) {\n\tif (typeof Array.isArray === 'function') {\n\t\treturn Array.isArray(arr);\n\t}\n\n\treturn toStr.call(arr) === '[object Array]';\n};\n\nvar isPlainObject = function isPlainObject(obj) {\n\tif (!obj || toStr.call(obj) !== '[object Object]') {\n\t\treturn false;\n\t}\n\n\tvar hasOwnConstructor = hasOwn.call(obj, 'constructor');\n\tvar hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');\n\t// Not own constructor property must be Object\n\tif (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {\n\t\treturn false;\n\t}\n\n\t// Own properties are enumerated firstly, so to speed up,\n\t// if last one is own, then all properties are own.\n\tvar key;\n\tfor (key in obj) { /**/ }\n\n\treturn typeof key === 'undefined' || hasOwn.call(obj, key);\n};\n\n// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target\nvar setProperty = function setProperty(target, options) {\n\tif (defineProperty && options.name === '__proto__') {\n\t\tdefineProperty(target, options.name, {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\t\t\tvalue: options.newValue,\n\t\t\twritable: true\n\t\t});\n\t} else {\n\t\ttarget[options.name] = options.newValue;\n\t}\n};\n\n// Return undefined instead of __proto__ if '__proto__' is not an own property\nvar getProperty = function getProperty(obj, name) {\n\tif (name === '__proto__') {\n\t\tif (!hasOwn.call(obj, name)) {\n\t\t\treturn void 0;\n\t\t} else if (gOPD) {\n\t\t\t// In early versions of node, obj['__proto__'] is buggy when obj has\n\t\t\t// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.\n\t\t\treturn gOPD(obj, name).value;\n\t\t}\n\t}\n\n\treturn obj[name];\n};\n\nmodule.exports = function extend() {\n\tvar options, name, src, copy, copyIsArray, clone;\n\tvar target = arguments[0];\n\tvar i = 1;\n\tvar length = arguments.length;\n\tvar deep = false;\n\n\t// Handle a deep copy situation\n\tif (typeof target === 'boolean') {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\tif (target == null || (typeof target !== 'object' && typeof target !== 'function')) {\n\t\ttarget = {};\n\t}\n\n\tfor (; i < length; ++i) {\n\t\toptions = arguments[i];\n\t\t// Only deal with non-null/undefined values\n\t\tif (options != null) {\n\t\t\t// Extend the base object\n\t\t\tfor (name in options) {\n\t\t\t\tsrc = getProperty(target, name);\n\t\t\t\tcopy = getProperty(options, name);\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif (target !== copy) {\n\t\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\t\tif (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {\n\t\t\t\t\t\tif (copyIsArray) {\n\t\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\t\tclone = src && isArray(src) ? src : [];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tclone = src && isPlainObject(src) ? src : {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\t\tsetProperty(target, { name: name, newValue: extend(deep, clone, copy) });\n\n\t\t\t\t\t// Don't bring in undefined values\n\t\t\t\t\t} else if (typeof copy !== 'undefined') {\n\t\t\t\t\t\tsetProperty(target, { name: name, newValue: copy });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.BlockEmbed = exports.bubbleFormats = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar NEWLINE_LENGTH = 1;\n\nvar BlockEmbed = function (_Parchment$Embed) {\n  _inherits(BlockEmbed, _Parchment$Embed);\n\n  function BlockEmbed() {\n    _classCallCheck(this, BlockEmbed);\n\n    return _possibleConstructorReturn(this, (BlockEmbed.__proto__ || Object.getPrototypeOf(BlockEmbed)).apply(this, arguments));\n  }\n\n  _createClass(BlockEmbed, [{\n    key: 'attach',\n    value: function attach() {\n      _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'attach', this).call(this);\n      this.attributes = new _parchment2.default.Attributor.Store(this.domNode);\n    }\n  }, {\n    key: 'delta',\n    value: function delta() {\n      return new _quillDelta2.default().insert(this.value(), (0, _extend2.default)(this.formats(), this.attributes.values()));\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      var attribute = _parchment2.default.query(name, _parchment2.default.Scope.BLOCK_ATTRIBUTE);\n      if (attribute != null) {\n        this.attributes.attribute(attribute, value);\n      }\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      this.format(name, value);\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (typeof value === 'string' && value.endsWith('\\n')) {\n        var block = _parchment2.default.create(Block.blotName);\n        this.parent.insertBefore(block, index === 0 ? this : this.next);\n        block.insertAt(0, value.slice(0, -1));\n      } else {\n        _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'insertAt', this).call(this, index, value, def);\n      }\n    }\n  }]);\n\n  return BlockEmbed;\n}(_parchment2.default.Embed);\n\nBlockEmbed.scope = _parchment2.default.Scope.BLOCK_BLOT;\n// It is important for cursor behavior BlockEmbeds use tags that are block level elements\n\n\nvar Block = function (_Parchment$Block) {\n  _inherits(Block, _Parchment$Block);\n\n  function Block(domNode) {\n    _classCallCheck(this, Block);\n\n    var _this2 = _possibleConstructorReturn(this, (Block.__proto__ || Object.getPrototypeOf(Block)).call(this, domNode));\n\n    _this2.cache = {};\n    return _this2;\n  }\n\n  _createClass(Block, [{\n    key: 'delta',\n    value: function delta() {\n      if (this.cache.delta == null) {\n        this.cache.delta = this.descendants(_parchment2.default.Leaf).reduce(function (delta, leaf) {\n          if (leaf.length() === 0) {\n            return delta;\n          } else {\n            return delta.insert(leaf.value(), bubbleFormats(leaf));\n          }\n        }, new _quillDelta2.default()).insert('\\n', bubbleFormats(this));\n      }\n      return this.cache.delta;\n    }\n  }, {\n    key: 'deleteAt',\n    value: function deleteAt(index, length) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'deleteAt', this).call(this, index, length);\n      this.cache = {};\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      if (length <= 0) return;\n      if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) {\n        if (index + length === this.length()) {\n          this.format(name, value);\n        }\n      } else {\n        _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'formatAt', this).call(this, index, Math.min(length, this.length() - index - 1), name, value);\n      }\n      this.cache = {};\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (def != null) return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, index, value, def);\n      if (value.length === 0) return;\n      var lines = value.split('\\n');\n      var text = lines.shift();\n      if (text.length > 0) {\n        if (index < this.length() - 1 || this.children.tail == null) {\n          _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, Math.min(index, this.length() - 1), text);\n        } else {\n          this.children.tail.insertAt(this.children.tail.length(), text);\n        }\n        this.cache = {};\n      }\n      var block = this;\n      lines.reduce(function (index, line) {\n        block = block.split(index, true);\n        block.insertAt(0, line);\n        return line.length;\n      }, index + text.length);\n    }\n  }, {\n    key: 'insertBefore',\n    value: function insertBefore(blot, ref) {\n      var head = this.children.head;\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertBefore', this).call(this, blot, ref);\n      if (head instanceof _break2.default) {\n        head.remove();\n      }\n      this.cache = {};\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      if (this.cache.length == null) {\n        this.cache.length = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'length', this).call(this) + NEWLINE_LENGTH;\n      }\n      return this.cache.length;\n    }\n  }, {\n    key: 'moveChildren',\n    value: function moveChildren(target, ref) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'moveChildren', this).call(this, target, ref);\n      this.cache = {};\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize(context) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'optimize', this).call(this, context);\n      this.cache = {};\n    }\n  }, {\n    key: 'path',\n    value: function path(index) {\n      return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'path', this).call(this, index, true);\n    }\n  }, {\n    key: 'removeChild',\n    value: function removeChild(child) {\n      _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'removeChild', this).call(this, child);\n      this.cache = {};\n    }\n  }, {\n    key: 'split',\n    value: function split(index) {\n      var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n      if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {\n        var clone = this.clone();\n        if (index === 0) {\n          this.parent.insertBefore(clone, this);\n          return this;\n        } else {\n          this.parent.insertBefore(clone, this.next);\n          return clone;\n        }\n      } else {\n        var next = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'split', this).call(this, index, force);\n        this.cache = {};\n        return next;\n      }\n    }\n  }]);\n\n  return Block;\n}(_parchment2.default.Block);\n\nBlock.blotName = 'block';\nBlock.tagName = 'P';\nBlock.defaultChild = 'break';\nBlock.allowedChildren = [_inline2.default, _parchment2.default.Embed, _text2.default];\n\nfunction bubbleFormats(blot) {\n  var formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n  if (blot == null) return formats;\n  if (typeof blot.formats === 'function') {\n    formats = (0, _extend2.default)(formats, blot.formats());\n  }\n  if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {\n    return formats;\n  }\n  return bubbleFormats(blot.parent, formats);\n}\n\nexports.bubbleFormats = bubbleFormats;\nexports.BlockEmbed = BlockEmbed;\nexports.default = Block;\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.overload = exports.expandConfig = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\n__webpack_require__(50);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _editor = __webpack_require__(14);\n\nvar _editor2 = _interopRequireDefault(_editor);\n\nvar _emitter3 = __webpack_require__(8);\n\nvar _emitter4 = _interopRequireDefault(_emitter3);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _selection = __webpack_require__(15);\n\nvar _selection2 = _interopRequireDefault(_selection);\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _theme = __webpack_require__(34);\n\nvar _theme2 = _interopRequireDefault(_theme);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar debug = (0, _logger2.default)('quill');\n\nvar Quill = function () {\n  _createClass(Quill, null, [{\n    key: 'debug',\n    value: function debug(limit) {\n      if (limit === true) {\n        limit = 'log';\n      }\n      _logger2.default.level(limit);\n    }\n  }, {\n    key: 'find',\n    value: function find(node) {\n      return node.__quill || _parchment2.default.find(node);\n    }\n  }, {\n    key: 'import',\n    value: function _import(name) {\n      if (this.imports[name] == null) {\n        debug.error('Cannot import ' + name + '. Are you sure it was registered?');\n      }\n      return this.imports[name];\n    }\n  }, {\n    key: 'register',\n    value: function register(path, target) {\n      var _this = this;\n\n      var overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n      if (typeof path !== 'string') {\n        var name = path.attrName || path.blotName;\n        if (typeof name === 'string') {\n          // register(Blot | Attributor, overwrite)\n          this.register('formats/' + name, path, target);\n        } else {\n          Object.keys(path).forEach(function (key) {\n            _this.register(key, path[key], target);\n          });\n        }\n      } else {\n        if (this.imports[path] != null && !overwrite) {\n          debug.warn('Overwriting ' + path + ' with', target);\n        }\n        this.imports[path] = target;\n        if ((path.startsWith('blots/') || path.startsWith('formats/')) && target.blotName !== 'abstract') {\n          _parchment2.default.register(target);\n        } else if (path.startsWith('modules') && typeof target.register === 'function') {\n          target.register();\n        }\n      }\n    }\n  }]);\n\n  function Quill(container) {\n    var _this2 = this;\n\n    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n    _classCallCheck(this, Quill);\n\n    this.options = expandConfig(container, options);\n    this.container = this.options.container;\n    if (this.container == null) {\n      return debug.error('Invalid Quill container', container);\n    }\n    if (this.options.debug) {\n      Quill.debug(this.options.debug);\n    }\n    var html = this.container.innerHTML.trim();\n    this.container.classList.add('ql-container');\n    this.container.innerHTML = '';\n    this.container.__quill = this;\n    this.root = this.addContainer('ql-editor');\n    this.root.classList.add('ql-blank');\n    this.root.setAttribute('data-gramm', false);\n    this.scrollingContainer = this.options.scrollingContainer || this.root;\n    this.emitter = new _emitter4.default();\n    this.scroll = _parchment2.default.create(this.root, {\n      emitter: this.emitter,\n      whitelist: this.options.formats\n    });\n    this.editor = new _editor2.default(this.scroll);\n    this.selection = new _selection2.default(this.scroll, this.emitter);\n    this.theme = new this.options.theme(this, this.options);\n    this.keyboard = this.theme.addModule('keyboard');\n    this.clipboard = this.theme.addModule('clipboard');\n    this.history = this.theme.addModule('history');\n    this.theme.init();\n    this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type) {\n      if (type === _emitter4.default.events.TEXT_CHANGE) {\n        _this2.root.classList.toggle('ql-blank', _this2.editor.isBlank());\n      }\n    });\n    this.emitter.on(_emitter4.default.events.SCROLL_UPDATE, function (source, mutations) {\n      var range = _this2.selection.lastRange;\n      var index = range && range.length === 0 ? range.index : undefined;\n      modify.call(_this2, function () {\n        return _this2.editor.update(null, mutations, index);\n      }, source);\n    });\n    var contents = this.clipboard.convert('<div class=\\'ql-editor\\' style=\"white-space: normal;\">' + html + '<p><br></p></div>');\n    this.setContents(contents);\n    this.history.clear();\n    if (this.options.placeholder) {\n      this.root.setAttribute('data-placeholder', this.options.placeholder);\n    }\n    if (this.options.readOnly) {\n      this.disable();\n    }\n  }\n\n  _createClass(Quill, [{\n    key: 'addContainer',\n    value: function addContainer(container) {\n      var refNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n\n      if (typeof container === 'string') {\n        var className = container;\n        container = document.createElement('div');\n        container.classList.add(className);\n      }\n      this.container.insertBefore(container, refNode);\n      return container;\n    }\n  }, {\n    key: 'blur',\n    value: function blur() {\n      this.selection.setRange(null);\n    }\n  }, {\n    key: 'deleteText',\n    value: function deleteText(index, length, source) {\n      var _this3 = this;\n\n      var _overload = overload(index, length, source);\n\n      var _overload2 = _slicedToArray(_overload, 4);\n\n      index = _overload2[0];\n      length = _overload2[1];\n      source = _overload2[3];\n\n      return modify.call(this, function () {\n        return _this3.editor.deleteText(index, length);\n      }, source, index, -1 * length);\n    }\n  }, {\n    key: 'disable',\n    value: function disable() {\n      this.enable(false);\n    }\n  }, {\n    key: 'enable',\n    value: function enable() {\n      var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n\n      this.scroll.enable(enabled);\n      this.container.classList.toggle('ql-disabled', !enabled);\n    }\n  }, {\n    key: 'focus',\n    value: function focus() {\n      var scrollTop = this.scrollingContainer.scrollTop;\n      this.selection.focus();\n      this.scrollingContainer.scrollTop = scrollTop;\n      this.scrollIntoView();\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      var _this4 = this;\n\n      var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API;\n\n      return modify.call(this, function () {\n        var range = _this4.getSelection(true);\n        var change = new _quillDelta2.default();\n        if (range == null) {\n          return change;\n        } else if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) {\n          change = _this4.editor.formatLine(range.index, range.length, _defineProperty({}, name, value));\n        } else if (range.length === 0) {\n          _this4.selection.format(name, value);\n          return change;\n        } else {\n          change = _this4.editor.formatText(range.index, range.length, _defineProperty({}, name, value));\n        }\n        _this4.setSelection(range, _emitter4.default.sources.SILENT);\n        return change;\n      }, source);\n    }\n  }, {\n    key: 'formatLine',\n    value: function formatLine(index, length, name, value, source) {\n      var _this5 = this;\n\n      var formats = void 0;\n\n      var _overload3 = overload(index, length, name, value, source);\n\n      var _overload4 = _slicedToArray(_overload3, 4);\n\n      index = _overload4[0];\n      length = _overload4[1];\n      formats = _overload4[2];\n      source = _overload4[3];\n\n      return modify.call(this, function () {\n        return _this5.editor.formatLine(index, length, formats);\n      }, source, index, 0);\n    }\n  }, {\n    key: 'formatText',\n    value: function formatText(index, length, name, value, source) {\n      var _this6 = this;\n\n      var formats = void 0;\n\n      var _overload5 = overload(index, length, name, value, source);\n\n      var _overload6 = _slicedToArray(_overload5, 4);\n\n      index = _overload6[0];\n      length = _overload6[1];\n      formats = _overload6[2];\n      source = _overload6[3];\n\n      return modify.call(this, function () {\n        return _this6.editor.formatText(index, length, formats);\n      }, source, index, 0);\n    }\n  }, {\n    key: 'getBounds',\n    value: function getBounds(index) {\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      var bounds = void 0;\n      if (typeof index === 'number') {\n        bounds = this.selection.getBounds(index, length);\n      } else {\n        bounds = this.selection.getBounds(index.index, index.length);\n      }\n      var containerBounds = this.container.getBoundingClientRect();\n      return {\n        bottom: bounds.bottom - containerBounds.top,\n        height: bounds.height,\n        left: bounds.left - containerBounds.left,\n        right: bounds.right - containerBounds.left,\n        top: bounds.top - containerBounds.top,\n        width: bounds.width\n      };\n    }\n  }, {\n    key: 'getContents',\n    value: function getContents() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index;\n\n      var _overload7 = overload(index, length);\n\n      var _overload8 = _slicedToArray(_overload7, 2);\n\n      index = _overload8[0];\n      length = _overload8[1];\n\n      return this.editor.getContents(index, length);\n    }\n  }, {\n    key: 'getFormat',\n    value: function getFormat() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getSelection(true);\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      if (typeof index === 'number') {\n        return this.editor.getFormat(index, length);\n      } else {\n        return this.editor.getFormat(index.index, index.length);\n      }\n    }\n  }, {\n    key: 'getIndex',\n    value: function getIndex(blot) {\n      return blot.offset(this.scroll);\n    }\n  }, {\n    key: 'getLength',\n    value: function getLength() {\n      return this.scroll.length();\n    }\n  }, {\n    key: 'getLeaf',\n    value: function getLeaf(index) {\n      return this.scroll.leaf(index);\n    }\n  }, {\n    key: 'getLine',\n    value: function getLine(index) {\n      return this.scroll.line(index);\n    }\n  }, {\n    key: 'getLines',\n    value: function getLines() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;\n\n      if (typeof index !== 'number') {\n        return this.scroll.lines(index.index, index.length);\n      } else {\n        return this.scroll.lines(index, length);\n      }\n    }\n  }, {\n    key: 'getModule',\n    value: function getModule(name) {\n      return this.theme.modules[name];\n    }\n  }, {\n    key: 'getSelection',\n    value: function getSelection() {\n      var focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n      if (focus) this.focus();\n      this.update(); // Make sure we access getRange with editor in consistent state\n      return this.selection.getRange()[0];\n    }\n  }, {\n    key: 'getText',\n    value: function getText() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index;\n\n      var _overload9 = overload(index, length);\n\n      var _overload10 = _slicedToArray(_overload9, 2);\n\n      index = _overload10[0];\n      length = _overload10[1];\n\n      return this.editor.getText(index, length);\n    }\n  }, {\n    key: 'hasFocus',\n    value: function hasFocus() {\n      return this.selection.hasFocus();\n    }\n  }, {\n    key: 'insertEmbed',\n    value: function insertEmbed(index, embed, value) {\n      var _this7 = this;\n\n      var source = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Quill.sources.API;\n\n      return modify.call(this, function () {\n        return _this7.editor.insertEmbed(index, embed, value);\n      }, source, index);\n    }\n  }, {\n    key: 'insertText',\n    value: function insertText(index, text, name, value, source) {\n      var _this8 = this;\n\n      var formats = void 0;\n\n      var _overload11 = overload(index, 0, name, value, source);\n\n      var _overload12 = _slicedToArray(_overload11, 4);\n\n      index = _overload12[0];\n      formats = _overload12[2];\n      source = _overload12[3];\n\n      return modify.call(this, function () {\n        return _this8.editor.insertText(index, text, formats);\n      }, source, index, text.length);\n    }\n  }, {\n    key: 'isEnabled',\n    value: function isEnabled() {\n      return !this.container.classList.contains('ql-disabled');\n    }\n  }, {\n    key: 'off',\n    value: function off() {\n      return this.emitter.off.apply(this.emitter, arguments);\n    }\n  }, {\n    key: 'on',\n    value: function on() {\n      return this.emitter.on.apply(this.emitter, arguments);\n    }\n  }, {\n    key: 'once',\n    value: function once() {\n      return this.emitter.once.apply(this.emitter, arguments);\n    }\n  }, {\n    key: 'pasteHTML',\n    value: function pasteHTML(index, html, source) {\n      this.clipboard.dangerouslyPasteHTML(index, html, source);\n    }\n  }, {\n    key: 'removeFormat',\n    value: function removeFormat(index, length, source) {\n      var _this9 = this;\n\n      var _overload13 = overload(index, length, source);\n\n      var _overload14 = _slicedToArray(_overload13, 4);\n\n      index = _overload14[0];\n      length = _overload14[1];\n      source = _overload14[3];\n\n      return modify.call(this, function () {\n        return _this9.editor.removeFormat(index, length);\n      }, source, index);\n    }\n  }, {\n    key: 'scrollIntoView',\n    value: function scrollIntoView() {\n      this.selection.scrollIntoView(this.scrollingContainer);\n    }\n  }, {\n    key: 'setContents',\n    value: function setContents(delta) {\n      var _this10 = this;\n\n      var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n      return modify.call(this, function () {\n        delta = new _quillDelta2.default(delta);\n        var length = _this10.getLength();\n        var deleted = _this10.editor.deleteText(0, length);\n        var applied = _this10.editor.applyDelta(delta);\n        var lastOp = applied.ops[applied.ops.length - 1];\n        if (lastOp != null && typeof lastOp.insert === 'string' && lastOp.insert[lastOp.insert.length - 1] === '\\n') {\n          _this10.editor.deleteText(_this10.getLength() - 1, 1);\n          applied.delete(1);\n        }\n        var ret = deleted.compose(applied);\n        return ret;\n      }, source);\n    }\n  }, {\n    key: 'setSelection',\n    value: function setSelection(index, length, source) {\n      if (index == null) {\n        this.selection.setRange(null, length || Quill.sources.API);\n      } else {\n        var _overload15 = overload(index, length, source);\n\n        var _overload16 = _slicedToArray(_overload15, 4);\n\n        index = _overload16[0];\n        length = _overload16[1];\n        source = _overload16[3];\n\n        this.selection.setRange(new _selection.Range(index, length), source);\n        if (source !== _emitter4.default.sources.SILENT) {\n          this.selection.scrollIntoView(this.scrollingContainer);\n        }\n      }\n    }\n  }, {\n    key: 'setText',\n    value: function setText(text) {\n      var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n      var delta = new _quillDelta2.default().insert(text);\n      return this.setContents(delta, source);\n    }\n  }, {\n    key: 'update',\n    value: function update() {\n      var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER;\n\n      var change = this.scroll.update(source); // Will update selection before selection.update() does if text changes\n      this.selection.update(source);\n      return change;\n    }\n  }, {\n    key: 'updateContents',\n    value: function updateContents(delta) {\n      var _this11 = this;\n\n      var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n      return modify.call(this, function () {\n        delta = new _quillDelta2.default(delta);\n        return _this11.editor.applyDelta(delta, source);\n      }, source, true);\n    }\n  }]);\n\n  return Quill;\n}();\n\nQuill.DEFAULTS = {\n  bounds: null,\n  formats: null,\n  modules: {},\n  placeholder: '',\n  readOnly: false,\n  scrollingContainer: null,\n  strict: true,\n  theme: 'default'\n};\nQuill.events = _emitter4.default.events;\nQuill.sources = _emitter4.default.sources;\n// eslint-disable-next-line no-undef\nQuill.version =  false ? 'dev' : \"1.3.7\";\n\nQuill.imports = {\n  'delta': _quillDelta2.default,\n  'parchment': _parchment2.default,\n  'core/module': _module2.default,\n  'core/theme': _theme2.default\n};\n\nfunction expandConfig(container, userConfig) {\n  userConfig = (0, _extend2.default)(true, {\n    container: container,\n    modules: {\n      clipboard: true,\n      keyboard: true,\n      history: true\n    }\n  }, userConfig);\n  if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) {\n    userConfig.theme = _theme2.default;\n  } else {\n    userConfig.theme = Quill.import('themes/' + userConfig.theme);\n    if (userConfig.theme == null) {\n      throw new Error('Invalid theme ' + userConfig.theme + '. Did you register it?');\n    }\n  }\n  var themeConfig = (0, _extend2.default)(true, {}, userConfig.theme.DEFAULTS);\n  [themeConfig, userConfig].forEach(function (config) {\n    config.modules = config.modules || {};\n    Object.keys(config.modules).forEach(function (module) {\n      if (config.modules[module] === true) {\n        config.modules[module] = {};\n      }\n    });\n  });\n  var moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules));\n  var moduleConfig = moduleNames.reduce(function (config, name) {\n    var moduleClass = Quill.import('modules/' + name);\n    if (moduleClass == null) {\n      debug.error('Cannot load ' + name + ' module. Are you sure you registered it?');\n    } else {\n      config[name] = moduleClass.DEFAULTS || {};\n    }\n    return config;\n  }, {});\n  // Special case toolbar shorthand\n  if (userConfig.modules != null && userConfig.modules.toolbar && userConfig.modules.toolbar.constructor !== Object) {\n    userConfig.modules.toolbar = {\n      container: userConfig.modules.toolbar\n    };\n  }\n  userConfig = (0, _extend2.default)(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig);\n  ['bounds', 'container', 'scrollingContainer'].forEach(function (key) {\n    if (typeof userConfig[key] === 'string') {\n      userConfig[key] = document.querySelector(userConfig[key]);\n    }\n  });\n  userConfig.modules = Object.keys(userConfig.modules).reduce(function (config, name) {\n    if (userConfig.modules[name]) {\n      config[name] = userConfig.modules[name];\n    }\n    return config;\n  }, {});\n  return userConfig;\n}\n\n// Handle selection preservation and TEXT_CHANGE emission\n// common to modification APIs\nfunction modify(modifier, source, index, shift) {\n  if (this.options.strict && !this.isEnabled() && source === _emitter4.default.sources.USER) {\n    return new _quillDelta2.default();\n  }\n  var range = index == null ? null : this.getSelection();\n  var oldDelta = this.editor.delta;\n  var change = modifier();\n  if (range != null) {\n    if (index === true) index = range.index;\n    if (shift == null) {\n      range = shiftRange(range, change, source);\n    } else if (shift !== 0) {\n      range = shiftRange(range, index, shift, source);\n    }\n    this.setSelection(range, _emitter4.default.sources.SILENT);\n  }\n  if (change.length() > 0) {\n    var _emitter;\n\n    var args = [_emitter4.default.events.TEXT_CHANGE, change, oldDelta, source];\n    (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args));\n    if (source !== _emitter4.default.sources.SILENT) {\n      var _emitter2;\n\n      (_emitter2 = this.emitter).emit.apply(_emitter2, args);\n    }\n  }\n  return change;\n}\n\nfunction overload(index, length, name, value, source) {\n  var formats = {};\n  if (typeof index.index === 'number' && typeof index.length === 'number') {\n    // Allow for throwaway end (used by insertText/insertEmbed)\n    if (typeof length !== 'number') {\n      source = value, value = name, name = length, length = index.length, index = index.index;\n    } else {\n      length = index.length, index = index.index;\n    }\n  } else if (typeof length !== 'number') {\n    source = value, value = name, name = length, length = 0;\n  }\n  // Handle format being object, two format name/value strings or excluded\n  if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') {\n    formats = name;\n    source = value;\n  } else if (typeof name === 'string') {\n    if (value != null) {\n      formats[name] = value;\n    } else {\n      source = name;\n    }\n  }\n  // Handle optional source\n  source = source || _emitter4.default.sources.API;\n  return [index, length, formats, source];\n}\n\nfunction shiftRange(range, index, length, source) {\n  if (range == null) return null;\n  var start = void 0,\n      end = void 0;\n  if (index instanceof _quillDelta2.default) {\n    var _map = [range.index, range.index + range.length].map(function (pos) {\n      return index.transformPosition(pos, source !== _emitter4.default.sources.USER);\n    });\n\n    var _map2 = _slicedToArray(_map, 2);\n\n    start = _map2[0];\n    end = _map2[1];\n  } else {\n    var _map3 = [range.index, range.index + range.length].map(function (pos) {\n      if (pos < index || pos === index && source === _emitter4.default.sources.USER) return pos;\n      if (length >= 0) {\n        return pos + length;\n      } else {\n        return Math.max(index, pos + length);\n      }\n    });\n\n    var _map4 = _slicedToArray(_map3, 2);\n\n    start = _map4[0];\n    end = _map4[1];\n  }\n  return new _selection.Range(start, end - start);\n}\n\nexports.expandConfig = expandConfig;\nexports.overload = overload;\nexports.default = Quill;\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Inline = function (_Parchment$Inline) {\n  _inherits(Inline, _Parchment$Inline);\n\n  function Inline() {\n    _classCallCheck(this, Inline);\n\n    return _possibleConstructorReturn(this, (Inline.__proto__ || Object.getPrototypeOf(Inline)).apply(this, arguments));\n  }\n\n  _createClass(Inline, [{\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      if (Inline.compare(this.statics.blotName, name) < 0 && _parchment2.default.query(name, _parchment2.default.Scope.BLOT)) {\n        var blot = this.isolate(index, length);\n        if (value) {\n          blot.wrap(name, value);\n        }\n      } else {\n        _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'formatAt', this).call(this, index, length, name, value);\n      }\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize(context) {\n      _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'optimize', this).call(this, context);\n      if (this.parent instanceof Inline && Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) {\n        var parent = this.parent.isolate(this.offset(), this.length());\n        this.moveChildren(parent);\n        parent.wrap(this);\n      }\n    }\n  }], [{\n    key: 'compare',\n    value: function compare(self, other) {\n      var selfIndex = Inline.order.indexOf(self);\n      var otherIndex = Inline.order.indexOf(other);\n      if (selfIndex >= 0 || otherIndex >= 0) {\n        return selfIndex - otherIndex;\n      } else if (self === other) {\n        return 0;\n      } else if (self < other) {\n        return -1;\n      } else {\n        return 1;\n      }\n    }\n  }]);\n\n  return Inline;\n}(_parchment2.default.Inline);\n\nInline.allowedChildren = [Inline, _parchment2.default.Embed, _text2.default];\n// Lower index means deeper in the DOM tree, since not found (-1) is for embeds\nInline.order = ['cursor', 'inline', // Must be lower\n'underline', 'strike', 'italic', 'bold', 'script', 'link', 'code' // Must be higher\n];\n\nexports.default = Inline;\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar TextBlot = function (_Parchment$Text) {\n  _inherits(TextBlot, _Parchment$Text);\n\n  function TextBlot() {\n    _classCallCheck(this, TextBlot);\n\n    return _possibleConstructorReturn(this, (TextBlot.__proto__ || Object.getPrototypeOf(TextBlot)).apply(this, arguments));\n  }\n\n  return TextBlot;\n}(_parchment2.default.Text);\n\nexports.default = TextBlot;\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _eventemitter = __webpack_require__(54);\n\nvar _eventemitter2 = _interopRequireDefault(_eventemitter);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:events');\n\nvar EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click'];\n\nEVENTS.forEach(function (eventName) {\n  document.addEventListener(eventName, function () {\n    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {\n      args[_key] = arguments[_key];\n    }\n\n    [].slice.call(document.querySelectorAll('.ql-container')).forEach(function (node) {\n      // TODO use WeakMap\n      if (node.__quill && node.__quill.emitter) {\n        var _node$__quill$emitter;\n\n        (_node$__quill$emitter = node.__quill.emitter).handleDOM.apply(_node$__quill$emitter, args);\n      }\n    });\n  });\n});\n\nvar Emitter = function (_EventEmitter) {\n  _inherits(Emitter, _EventEmitter);\n\n  function Emitter() {\n    _classCallCheck(this, Emitter);\n\n    var _this = _possibleConstructorReturn(this, (Emitter.__proto__ || Object.getPrototypeOf(Emitter)).call(this));\n\n    _this.listeners = {};\n    _this.on('error', debug.error);\n    return _this;\n  }\n\n  _createClass(Emitter, [{\n    key: 'emit',\n    value: function emit() {\n      debug.log.apply(debug, arguments);\n      _get(Emitter.prototype.__proto__ || Object.getPrototypeOf(Emitter.prototype), 'emit', this).apply(this, arguments);\n    }\n  }, {\n    key: 'handleDOM',\n    value: function handleDOM(event) {\n      for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n        args[_key2 - 1] = arguments[_key2];\n      }\n\n      (this.listeners[event.type] || []).forEach(function (_ref) {\n        var node = _ref.node,\n            handler = _ref.handler;\n\n        if (event.target === node || node.contains(event.target)) {\n          handler.apply(undefined, [event].concat(args));\n        }\n      });\n    }\n  }, {\n    key: 'listenDOM',\n    value: function listenDOM(eventName, node, handler) {\n      if (!this.listeners[eventName]) {\n        this.listeners[eventName] = [];\n      }\n      this.listeners[eventName].push({ node: node, handler: handler });\n    }\n  }]);\n\n  return Emitter;\n}(_eventemitter2.default);\n\nEmitter.events = {\n  EDITOR_CHANGE: 'editor-change',\n  SCROLL_BEFORE_UPDATE: 'scroll-before-update',\n  SCROLL_OPTIMIZE: 'scroll-optimize',\n  SCROLL_UPDATE: 'scroll-update',\n  SELECTION_CHANGE: 'selection-change',\n  TEXT_CHANGE: 'text-change'\n};\nEmitter.sources = {\n  API: 'api',\n  SILENT: 'silent',\n  USER: 'user'\n};\n\nexports.default = Emitter;\n\n/***/ }),\n/* 9 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Module = function Module(quill) {\n  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n  _classCallCheck(this, Module);\n\n  this.quill = quill;\n  this.options = options;\n};\n\nModule.DEFAULTS = {};\n\nexports.default = Module;\n\n/***/ }),\n/* 10 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nvar levels = ['error', 'warn', 'log', 'info'];\nvar level = 'warn';\n\nfunction debug(method) {\n  if (levels.indexOf(method) <= levels.indexOf(level)) {\n    var _console;\n\n    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n      args[_key - 1] = arguments[_key];\n    }\n\n    (_console = console)[method].apply(_console, args); // eslint-disable-line no-console\n  }\n}\n\nfunction namespace(ns) {\n  return levels.reduce(function (logger, method) {\n    logger[method] = debug.bind(console, method, ns);\n    return logger;\n  }, {});\n}\n\ndebug.level = namespace.level = function (newLevel) {\n  level = newLevel;\n};\n\nexports.default = namespace;\n\n/***/ }),\n/* 11 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar pSlice = Array.prototype.slice;\nvar objectKeys = __webpack_require__(52);\nvar isArguments = __webpack_require__(53);\n\nvar deepEqual = module.exports = function (actual, expected, opts) {\n  if (!opts) opts = {};\n  // 7.1. All identical values are equivalent, as determined by ===.\n  if (actual === expected) {\n    return true;\n\n  } else if (actual instanceof Date && expected instanceof Date) {\n    return actual.getTime() === expected.getTime();\n\n  // 7.3. Other pairs that do not both pass typeof value == 'object',\n  // equivalence is determined by ==.\n  } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') {\n    return opts.strict ? actual === expected : actual == expected;\n\n  // 7.4. For all other Object pairs, including Array objects, equivalence is\n  // determined by having the same number of owned properties (as verified\n  // with Object.prototype.hasOwnProperty.call), the same set of keys\n  // (although not necessarily the same order), equivalent values for every\n  // corresponding key, and an identical 'prototype' property. Note: this\n  // accounts for both named and indexed properties on Arrays.\n  } else {\n    return objEquiv(actual, expected, opts);\n  }\n}\n\nfunction isUndefinedOrNull(value) {\n  return value === null || value === undefined;\n}\n\nfunction isBuffer (x) {\n  if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;\n  if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {\n    return false;\n  }\n  if (x.length > 0 && typeof x[0] !== 'number') return false;\n  return true;\n}\n\nfunction objEquiv(a, b, opts) {\n  var i, key;\n  if (isUndefinedOrNull(a) || isUndefinedOrNull(b))\n    return false;\n  // an identical 'prototype' property.\n  if (a.prototype !== b.prototype) return false;\n  //~~~I've managed to break Object.keys through screwy arguments passing.\n  //   Converting to array solves the problem.\n  if (isArguments(a)) {\n    if (!isArguments(b)) {\n      return false;\n    }\n    a = pSlice.call(a);\n    b = pSlice.call(b);\n    return deepEqual(a, b, opts);\n  }\n  if (isBuffer(a)) {\n    if (!isBuffer(b)) {\n      return false;\n    }\n    if (a.length !== b.length) return false;\n    for (i = 0; i < a.length; i++) {\n      if (a[i] !== b[i]) return false;\n    }\n    return true;\n  }\n  try {\n    var ka = objectKeys(a),\n        kb = objectKeys(b);\n  } catch (e) {//happens when one is a string literal and the other isn't\n    return false;\n  }\n  // having the same number of owned properties (keys incorporates\n  // hasOwnProperty)\n  if (ka.length != kb.length)\n    return false;\n  //the same set of keys (although not necessarily the same order),\n  ka.sort();\n  kb.sort();\n  //~~~cheap key test\n  for (i = ka.length - 1; i >= 0; i--) {\n    if (ka[i] != kb[i])\n      return false;\n  }\n  //equivalent values for every corresponding key, and\n  //~~~possibly expensive deep test\n  for (i = ka.length - 1; i >= 0; i--) {\n    key = ka[i];\n    if (!deepEqual(a[key], b[key], opts)) return false;\n  }\n  return typeof a === typeof b;\n}\n\n\n/***/ }),\n/* 12 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = __webpack_require__(1);\nvar Attributor = /** @class */ (function () {\n    function Attributor(attrName, keyName, options) {\n        if (options === void 0) { options = {}; }\n        this.attrName = attrName;\n        this.keyName = keyName;\n        var attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE;\n        if (options.scope != null) {\n            // Ignore type bits, force attribute bit\n            this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit;\n        }\n        else {\n            this.scope = Registry.Scope.ATTRIBUTE;\n        }\n        if (options.whitelist != null)\n            this.whitelist = options.whitelist;\n    }\n    Attributor.keys = function (node) {\n        return [].map.call(node.attributes, function (item) {\n            return item.name;\n        });\n    };\n    Attributor.prototype.add = function (node, value) {\n        if (!this.canAdd(node, value))\n            return false;\n        node.setAttribute(this.keyName, value);\n        return true;\n    };\n    Attributor.prototype.canAdd = function (node, value) {\n        var match = Registry.query(node, Registry.Scope.BLOT & (this.scope | Registry.Scope.TYPE));\n        if (match == null)\n            return false;\n        if (this.whitelist == null)\n            return true;\n        if (typeof value === 'string') {\n            return this.whitelist.indexOf(value.replace(/[\"']/g, '')) > -1;\n        }\n        else {\n            return this.whitelist.indexOf(value) > -1;\n        }\n    };\n    Attributor.prototype.remove = function (node) {\n        node.removeAttribute(this.keyName);\n    };\n    Attributor.prototype.value = function (node) {\n        var value = node.getAttribute(this.keyName);\n        if (this.canAdd(node, value) && value) {\n            return value;\n        }\n        return '';\n    };\n    return Attributor;\n}());\nexports.default = Attributor;\n\n\n/***/ }),\n/* 13 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.Code = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Code = function (_Inline) {\n  _inherits(Code, _Inline);\n\n  function Code() {\n    _classCallCheck(this, Code);\n\n    return _possibleConstructorReturn(this, (Code.__proto__ || Object.getPrototypeOf(Code)).apply(this, arguments));\n  }\n\n  return Code;\n}(_inline2.default);\n\nCode.blotName = 'code';\nCode.tagName = 'CODE';\n\nvar CodeBlock = function (_Block) {\n  _inherits(CodeBlock, _Block);\n\n  function CodeBlock() {\n    _classCallCheck(this, CodeBlock);\n\n    return _possibleConstructorReturn(this, (CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock)).apply(this, arguments));\n  }\n\n  _createClass(CodeBlock, [{\n    key: 'delta',\n    value: function delta() {\n      var _this3 = this;\n\n      var text = this.domNode.textContent;\n      if (text.endsWith('\\n')) {\n        // Should always be true\n        text = text.slice(0, -1);\n      }\n      return text.split('\\n').reduce(function (delta, frag) {\n        return delta.insert(frag).insert('\\n', _this3.formats());\n      }, new _quillDelta2.default());\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      if (name === this.statics.blotName && value) return;\n\n      var _descendant = this.descendant(_text2.default, this.length() - 1),\n          _descendant2 = _slicedToArray(_descendant, 1),\n          text = _descendant2[0];\n\n      if (text != null) {\n        text.deleteAt(text.length() - 1, 1);\n      }\n      _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'format', this).call(this, name, value);\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, name, value) {\n      if (length === 0) return;\n      if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK) == null || name === this.statics.blotName && value === this.statics.formats(this.domNode)) {\n        return;\n      }\n      var nextNewline = this.newlineIndex(index);\n      if (nextNewline < 0 || nextNewline >= index + length) return;\n      var prevNewline = this.newlineIndex(index, true) + 1;\n      var isolateLength = nextNewline - prevNewline + 1;\n      var blot = this.isolate(prevNewline, isolateLength);\n      var next = blot.next;\n      blot.format(name, value);\n      if (next instanceof CodeBlock) {\n        next.formatAt(0, index - prevNewline + length - isolateLength, name, value);\n      }\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (def != null) return;\n\n      var _descendant3 = this.descendant(_text2.default, index),\n          _descendant4 = _slicedToArray(_descendant3, 2),\n          text = _descendant4[0],\n          offset = _descendant4[1];\n\n      text.insertAt(offset, value);\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      var length = this.domNode.textContent.length;\n      if (!this.domNode.textContent.endsWith('\\n')) {\n        return length + 1;\n      }\n      return length;\n    }\n  }, {\n    key: 'newlineIndex',\n    value: function newlineIndex(searchIndex) {\n      var reverse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n      if (!reverse) {\n        var offset = this.domNode.textContent.slice(searchIndex).indexOf('\\n');\n        return offset > -1 ? searchIndex + offset : -1;\n      } else {\n        return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\\n');\n      }\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize(context) {\n      if (!this.domNode.textContent.endsWith('\\n')) {\n        this.appendChild(_parchment2.default.create('text', '\\n'));\n      }\n      _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'optimize', this).call(this, context);\n      var next = this.next;\n      if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) {\n        next.optimize(context);\n        next.moveChildren(this);\n        next.remove();\n      }\n    }\n  }, {\n    key: 'replace',\n    value: function replace(target) {\n      _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'replace', this).call(this, target);\n      [].slice.call(this.domNode.querySelectorAll('*')).forEach(function (node) {\n        var blot = _parchment2.default.find(node);\n        if (blot == null) {\n          node.parentNode.removeChild(node);\n        } else if (blot instanceof _parchment2.default.Embed) {\n          blot.remove();\n        } else {\n          blot.unwrap();\n        }\n      });\n    }\n  }], [{\n    key: 'create',\n    value: function create(value) {\n      var domNode = _get(CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock), 'create', this).call(this, value);\n      domNode.setAttribute('spellcheck', false);\n      return domNode;\n    }\n  }, {\n    key: 'formats',\n    value: function formats() {\n      return true;\n    }\n  }]);\n\n  return CodeBlock;\n}(_block2.default);\n\nCodeBlock.blotName = 'code-block';\nCodeBlock.tagName = 'PRE';\nCodeBlock.TAB = '  ';\n\nexports.Code = Code;\nexports.default = CodeBlock;\n\n/***/ }),\n/* 14 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _op = __webpack_require__(20);\n\nvar _op2 = _interopRequireDefault(_op);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _cursor = __webpack_require__(24);\n\nvar _cursor2 = _interopRequireDefault(_cursor);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(11);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar ASCII = /^[ -~]*$/;\n\nvar Editor = function () {\n  function Editor(scroll) {\n    _classCallCheck(this, Editor);\n\n    this.scroll = scroll;\n    this.delta = this.getDelta();\n  }\n\n  _createClass(Editor, [{\n    key: 'applyDelta',\n    value: function applyDelta(delta) {\n      var _this = this;\n\n      var consumeNextNewline = false;\n      this.scroll.update();\n      var scrollLength = this.scroll.length();\n      this.scroll.batchStart();\n      delta = normalizeDelta(delta);\n      delta.reduce(function (index, op) {\n        var length = op.retain || op.delete || op.insert.length || 1;\n        var attributes = op.attributes || {};\n        if (op.insert != null) {\n          if (typeof op.insert === 'string') {\n            var text = op.insert;\n            if (text.endsWith('\\n') && consumeNextNewline) {\n              consumeNextNewline = false;\n              text = text.slice(0, -1);\n            }\n            if (index >= scrollLength && !text.endsWith('\\n')) {\n              consumeNextNewline = true;\n            }\n            _this.scroll.insertAt(index, text);\n\n            var _scroll$line = _this.scroll.line(index),\n                _scroll$line2 = _slicedToArray(_scroll$line, 2),\n                line = _scroll$line2[0],\n                offset = _scroll$line2[1];\n\n            var formats = (0, _extend2.default)({}, (0, _block.bubbleFormats)(line));\n            if (line instanceof _block2.default) {\n              var _line$descendant = line.descendant(_parchment2.default.Leaf, offset),\n                  _line$descendant2 = _slicedToArray(_line$descendant, 1),\n                  leaf = _line$descendant2[0];\n\n              formats = (0, _extend2.default)(formats, (0, _block.bubbleFormats)(leaf));\n            }\n            attributes = _op2.default.attributes.diff(formats, attributes) || {};\n          } else if (_typeof(op.insert) === 'object') {\n            var key = Object.keys(op.insert)[0]; // There should only be one key\n            if (key == null) return index;\n            _this.scroll.insertAt(index, key, op.insert[key]);\n          }\n          scrollLength += length;\n        }\n        Object.keys(attributes).forEach(function (name) {\n          _this.scroll.formatAt(index, length, name, attributes[name]);\n        });\n        return index + length;\n      }, 0);\n      delta.reduce(function (index, op) {\n        if (typeof op.delete === 'number') {\n          _this.scroll.deleteAt(index, op.delete);\n          return index;\n        }\n        return index + (op.retain || op.insert.length || 1);\n      }, 0);\n      this.scroll.batchEnd();\n      return this.update(delta);\n    }\n  }, {\n    key: 'deleteText',\n    value: function deleteText(index, length) {\n      this.scroll.deleteAt(index, length);\n      return this.update(new _quillDelta2.default().retain(index).delete(length));\n    }\n  }, {\n    key: 'formatLine',\n    value: function formatLine(index, length) {\n      var _this2 = this;\n\n      var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      this.scroll.update();\n      Object.keys(formats).forEach(function (format) {\n        if (_this2.scroll.whitelist != null && !_this2.scroll.whitelist[format]) return;\n        var lines = _this2.scroll.lines(index, Math.max(length, 1));\n        var lengthRemaining = length;\n        lines.forEach(function (line) {\n          var lineLength = line.length();\n          if (!(line instanceof _code2.default)) {\n            line.format(format, formats[format]);\n          } else {\n            var codeIndex = index - line.offset(_this2.scroll);\n            var codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1;\n            line.formatAt(codeIndex, codeLength, format, formats[format]);\n          }\n          lengthRemaining -= lineLength;\n        });\n      });\n      this.scroll.optimize();\n      return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats)));\n    }\n  }, {\n    key: 'formatText',\n    value: function formatText(index, length) {\n      var _this3 = this;\n\n      var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      Object.keys(formats).forEach(function (format) {\n        _this3.scroll.formatAt(index, length, format, formats[format]);\n      });\n      return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats)));\n    }\n  }, {\n    key: 'getContents',\n    value: function getContents(index, length) {\n      return this.delta.slice(index, index + length);\n    }\n  }, {\n    key: 'getDelta',\n    value: function getDelta() {\n      return this.scroll.lines().reduce(function (delta, line) {\n        return delta.concat(line.delta());\n      }, new _quillDelta2.default());\n    }\n  }, {\n    key: 'getFormat',\n    value: function getFormat(index) {\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      var lines = [],\n          leaves = [];\n      if (length === 0) {\n        this.scroll.path(index).forEach(function (path) {\n          var _path = _slicedToArray(path, 1),\n              blot = _path[0];\n\n          if (blot instanceof _block2.default) {\n            lines.push(blot);\n          } else if (blot instanceof _parchment2.default.Leaf) {\n            leaves.push(blot);\n          }\n        });\n      } else {\n        lines = this.scroll.lines(index, length);\n        leaves = this.scroll.descendants(_parchment2.default.Leaf, index, length);\n      }\n      var formatsArr = [lines, leaves].map(function (blots) {\n        if (blots.length === 0) return {};\n        var formats = (0, _block.bubbleFormats)(blots.shift());\n        while (Object.keys(formats).length > 0) {\n          var blot = blots.shift();\n          if (blot == null) return formats;\n          formats = combineFormats((0, _block.bubbleFormats)(blot), formats);\n        }\n        return formats;\n      });\n      return _extend2.default.apply(_extend2.default, formatsArr);\n    }\n  }, {\n    key: 'getText',\n    value: function getText(index, length) {\n      return this.getContents(index, length).filter(function (op) {\n        return typeof op.insert === 'string';\n      }).map(function (op) {\n        return op.insert;\n      }).join('');\n    }\n  }, {\n    key: 'insertEmbed',\n    value: function insertEmbed(index, embed, value) {\n      this.scroll.insertAt(index, embed, value);\n      return this.update(new _quillDelta2.default().retain(index).insert(_defineProperty({}, embed, value)));\n    }\n  }, {\n    key: 'insertText',\n    value: function insertText(index, text) {\n      var _this4 = this;\n\n      var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      text = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n      this.scroll.insertAt(index, text);\n      Object.keys(formats).forEach(function (format) {\n        _this4.scroll.formatAt(index, text.length, format, formats[format]);\n      });\n      return this.update(new _quillDelta2.default().retain(index).insert(text, (0, _clone2.default)(formats)));\n    }\n  }, {\n    key: 'isBlank',\n    value: function isBlank() {\n      if (this.scroll.children.length == 0) return true;\n      if (this.scroll.children.length > 1) return false;\n      var block = this.scroll.children.head;\n      if (block.statics.blotName !== _block2.default.blotName) return false;\n      if (block.children.length > 1) return false;\n      return block.children.head instanceof _break2.default;\n    }\n  }, {\n    key: 'removeFormat',\n    value: function removeFormat(index, length) {\n      var text = this.getText(index, length);\n\n      var _scroll$line3 = this.scroll.line(index + length),\n          _scroll$line4 = _slicedToArray(_scroll$line3, 2),\n          line = _scroll$line4[0],\n          offset = _scroll$line4[1];\n\n      var suffixLength = 0,\n          suffix = new _quillDelta2.default();\n      if (line != null) {\n        if (!(line instanceof _code2.default)) {\n          suffixLength = line.length() - offset;\n        } else {\n          suffixLength = line.newlineIndex(offset) - offset + 1;\n        }\n        suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\\n');\n      }\n      var contents = this.getContents(index, length + suffixLength);\n      var diff = contents.diff(new _quillDelta2.default().insert(text).concat(suffix));\n      var delta = new _quillDelta2.default().retain(index).concat(diff);\n      return this.applyDelta(delta);\n    }\n  }, {\n    key: 'update',\n    value: function update(change) {\n      var mutations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];\n      var cursorIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n\n      var oldDelta = this.delta;\n      if (mutations.length === 1 && mutations[0].type === 'characterData' && mutations[0].target.data.match(ASCII) && _parchment2.default.find(mutations[0].target)) {\n        // Optimization for character changes\n        var textBlot = _parchment2.default.find(mutations[0].target);\n        var formats = (0, _block.bubbleFormats)(textBlot);\n        var index = textBlot.offset(this.scroll);\n        var oldValue = mutations[0].oldValue.replace(_cursor2.default.CONTENTS, '');\n        var oldText = new _quillDelta2.default().insert(oldValue);\n        var newText = new _quillDelta2.default().insert(textBlot.value());\n        var diffDelta = new _quillDelta2.default().retain(index).concat(oldText.diff(newText, cursorIndex));\n        change = diffDelta.reduce(function (delta, op) {\n          if (op.insert) {\n            return delta.insert(op.insert, formats);\n          } else {\n            return delta.push(op);\n          }\n        }, new _quillDelta2.default());\n        this.delta = oldDelta.compose(change);\n      } else {\n        this.delta = this.getDelta();\n        if (!change || !(0, _deepEqual2.default)(oldDelta.compose(change), this.delta)) {\n          change = oldDelta.diff(this.delta, cursorIndex);\n        }\n      }\n      return change;\n    }\n  }]);\n\n  return Editor;\n}();\n\nfunction combineFormats(formats, combined) {\n  return Object.keys(combined).reduce(function (merged, name) {\n    if (formats[name] == null) return merged;\n    if (combined[name] === formats[name]) {\n      merged[name] = combined[name];\n    } else if (Array.isArray(combined[name])) {\n      if (combined[name].indexOf(formats[name]) < 0) {\n        merged[name] = combined[name].concat([formats[name]]);\n      }\n    } else {\n      merged[name] = [combined[name], formats[name]];\n    }\n    return merged;\n  }, {});\n}\n\nfunction normalizeDelta(delta) {\n  return delta.reduce(function (delta, op) {\n    if (op.insert === 1) {\n      var attributes = (0, _clone2.default)(op.attributes);\n      delete attributes['image'];\n      return delta.insert({ image: op.attributes.image }, attributes);\n    }\n    if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) {\n      op = (0, _clone2.default)(op);\n      if (op.attributes.list) {\n        op.attributes.list = 'ordered';\n      } else {\n        op.attributes.list = 'bullet';\n        delete op.attributes.bullet;\n      }\n    }\n    if (typeof op.insert === 'string') {\n      var text = op.insert.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n      return delta.insert(text, op.attributes);\n    }\n    return delta.push(op);\n  }, new _quillDelta2.default());\n}\n\nexports.default = Editor;\n\n/***/ }),\n/* 15 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.Range = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(11);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _emitter3 = __webpack_require__(8);\n\nvar _emitter4 = _interopRequireDefault(_emitter3);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar debug = (0, _logger2.default)('quill:selection');\n\nvar Range = function Range(index) {\n  var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n  _classCallCheck(this, Range);\n\n  this.index = index;\n  this.length = length;\n};\n\nvar Selection = function () {\n  function Selection(scroll, emitter) {\n    var _this = this;\n\n    _classCallCheck(this, Selection);\n\n    this.emitter = emitter;\n    this.scroll = scroll;\n    this.composing = false;\n    this.mouseDown = false;\n    this.root = this.scroll.domNode;\n    this.cursor = _parchment2.default.create('cursor', this);\n    // savedRange is last non-null range\n    this.lastRange = this.savedRange = new Range(0, 0);\n    this.handleComposition();\n    this.handleDragging();\n    this.emitter.listenDOM('selectionchange', document, function () {\n      if (!_this.mouseDown) {\n        setTimeout(_this.update.bind(_this, _emitter4.default.sources.USER), 1);\n      }\n    });\n    this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type, delta) {\n      if (type === _emitter4.default.events.TEXT_CHANGE && delta.length() > 0) {\n        _this.update(_emitter4.default.sources.SILENT);\n      }\n    });\n    this.emitter.on(_emitter4.default.events.SCROLL_BEFORE_UPDATE, function () {\n      if (!_this.hasFocus()) return;\n      var native = _this.getNativeRange();\n      if (native == null) return;\n      if (native.start.node === _this.cursor.textNode) return; // cursor.restore() will handle\n      // TODO unclear if this has negative side effects\n      _this.emitter.once(_emitter4.default.events.SCROLL_UPDATE, function () {\n        try {\n          _this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset);\n        } catch (ignored) {}\n      });\n    });\n    this.emitter.on(_emitter4.default.events.SCROLL_OPTIMIZE, function (mutations, context) {\n      if (context.range) {\n        var _context$range = context.range,\n            startNode = _context$range.startNode,\n            startOffset = _context$range.startOffset,\n            endNode = _context$range.endNode,\n            endOffset = _context$range.endOffset;\n\n        _this.setNativeRange(startNode, startOffset, endNode, endOffset);\n      }\n    });\n    this.update(_emitter4.default.sources.SILENT);\n  }\n\n  _createClass(Selection, [{\n    key: 'handleComposition',\n    value: function handleComposition() {\n      var _this2 = this;\n\n      this.root.addEventListener('compositionstart', function () {\n        _this2.composing = true;\n      });\n      this.root.addEventListener('compositionend', function () {\n        _this2.composing = false;\n        if (_this2.cursor.parent) {\n          var range = _this2.cursor.restore();\n          if (!range) return;\n          setTimeout(function () {\n            _this2.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset);\n          }, 1);\n        }\n      });\n    }\n  }, {\n    key: 'handleDragging',\n    value: function handleDragging() {\n      var _this3 = this;\n\n      this.emitter.listenDOM('mousedown', document.body, function () {\n        _this3.mouseDown = true;\n      });\n      this.emitter.listenDOM('mouseup', document.body, function () {\n        _this3.mouseDown = false;\n        _this3.update(_emitter4.default.sources.USER);\n      });\n    }\n  }, {\n    key: 'focus',\n    value: function focus() {\n      if (this.hasFocus()) return;\n      this.root.focus();\n      this.setRange(this.savedRange);\n    }\n  }, {\n    key: 'format',\n    value: function format(_format, value) {\n      if (this.scroll.whitelist != null && !this.scroll.whitelist[_format]) return;\n      this.scroll.update();\n      var nativeRange = this.getNativeRange();\n      if (nativeRange == null || !nativeRange.native.collapsed || _parchment2.default.query(_format, _parchment2.default.Scope.BLOCK)) return;\n      if (nativeRange.start.node !== this.cursor.textNode) {\n        var blot = _parchment2.default.find(nativeRange.start.node, false);\n        if (blot == null) return;\n        // TODO Give blot ability to not split\n        if (blot instanceof _parchment2.default.Leaf) {\n          var after = blot.split(nativeRange.start.offset);\n          blot.parent.insertBefore(this.cursor, after);\n        } else {\n          blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen\n        }\n        this.cursor.attach();\n      }\n      this.cursor.format(_format, value);\n      this.scroll.optimize();\n      this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length);\n      this.update();\n    }\n  }, {\n    key: 'getBounds',\n    value: function getBounds(index) {\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n      var scrollLength = this.scroll.length();\n      index = Math.min(index, scrollLength - 1);\n      length = Math.min(index + length, scrollLength - 1) - index;\n      var node = void 0,\n          _scroll$leaf = this.scroll.leaf(index),\n          _scroll$leaf2 = _slicedToArray(_scroll$leaf, 2),\n          leaf = _scroll$leaf2[0],\n          offset = _scroll$leaf2[1];\n      if (leaf == null) return null;\n\n      var _leaf$position = leaf.position(offset, true);\n\n      var _leaf$position2 = _slicedToArray(_leaf$position, 2);\n\n      node = _leaf$position2[0];\n      offset = _leaf$position2[1];\n\n      var range = document.createRange();\n      if (length > 0) {\n        range.setStart(node, offset);\n\n        var _scroll$leaf3 = this.scroll.leaf(index + length);\n\n        var _scroll$leaf4 = _slicedToArray(_scroll$leaf3, 2);\n\n        leaf = _scroll$leaf4[0];\n        offset = _scroll$leaf4[1];\n\n        if (leaf == null) return null;\n\n        var _leaf$position3 = leaf.position(offset, true);\n\n        var _leaf$position4 = _slicedToArray(_leaf$position3, 2);\n\n        node = _leaf$position4[0];\n        offset = _leaf$position4[1];\n\n        range.setEnd(node, offset);\n        return range.getBoundingClientRect();\n      } else {\n        var side = 'left';\n        var rect = void 0;\n        if (node instanceof Text) {\n          if (offset < node.data.length) {\n            range.setStart(node, offset);\n            range.setEnd(node, offset + 1);\n          } else {\n            range.setStart(node, offset - 1);\n            range.setEnd(node, offset);\n            side = 'right';\n          }\n          rect = range.getBoundingClientRect();\n        } else {\n          rect = leaf.domNode.getBoundingClientRect();\n          if (offset > 0) side = 'right';\n        }\n        return {\n          bottom: rect.top + rect.height,\n          height: rect.height,\n          left: rect[side],\n          right: rect[side],\n          top: rect.top,\n          width: 0\n        };\n      }\n    }\n  }, {\n    key: 'getNativeRange',\n    value: function getNativeRange() {\n      var selection = document.getSelection();\n      if (selection == null || selection.rangeCount <= 0) return null;\n      var nativeRange = selection.getRangeAt(0);\n      if (nativeRange == null) return null;\n      var range = this.normalizeNative(nativeRange);\n      debug.info('getNativeRange', range);\n      return range;\n    }\n  }, {\n    key: 'getRange',\n    value: function getRange() {\n      var normalized = this.getNativeRange();\n      if (normalized == null) return [null, null];\n      var range = this.normalizedToRange(normalized);\n      return [range, normalized];\n    }\n  }, {\n    key: 'hasFocus',\n    value: function hasFocus() {\n      return document.activeElement === this.root;\n    }\n  }, {\n    key: 'normalizedToRange',\n    value: function normalizedToRange(range) {\n      var _this4 = this;\n\n      var positions = [[range.start.node, range.start.offset]];\n      if (!range.native.collapsed) {\n        positions.push([range.end.node, range.end.offset]);\n      }\n      var indexes = positions.map(function (position) {\n        var _position = _slicedToArray(position, 2),\n            node = _position[0],\n            offset = _position[1];\n\n        var blot = _parchment2.default.find(node, true);\n        var index = blot.offset(_this4.scroll);\n        if (offset === 0) {\n          return index;\n        } else if (blot instanceof _parchment2.default.Container) {\n          return index + blot.length();\n        } else {\n          return index + blot.index(node, offset);\n        }\n      });\n      var end = Math.min(Math.max.apply(Math, _toConsumableArray(indexes)), this.scroll.length() - 1);\n      var start = Math.min.apply(Math, [end].concat(_toConsumableArray(indexes)));\n      return new Range(start, end - start);\n    }\n  }, {\n    key: 'normalizeNative',\n    value: function normalizeNative(nativeRange) {\n      if (!contains(this.root, nativeRange.startContainer) || !nativeRange.collapsed && !contains(this.root, nativeRange.endContainer)) {\n        return null;\n      }\n      var range = {\n        start: { node: nativeRange.startContainer, offset: nativeRange.startOffset },\n        end: { node: nativeRange.endContainer, offset: nativeRange.endOffset },\n        native: nativeRange\n      };\n      [range.start, range.end].forEach(function (position) {\n        var node = position.node,\n            offset = position.offset;\n        while (!(node instanceof Text) && node.childNodes.length > 0) {\n          if (node.childNodes.length > offset) {\n            node = node.childNodes[offset];\n            offset = 0;\n          } else if (node.childNodes.length === offset) {\n            node = node.lastChild;\n            offset = node instanceof Text ? node.data.length : node.childNodes.length + 1;\n          } else {\n            break;\n          }\n        }\n        position.node = node, position.offset = offset;\n      });\n      return range;\n    }\n  }, {\n    key: 'rangeToNative',\n    value: function rangeToNative(range) {\n      var _this5 = this;\n\n      var indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length];\n      var args = [];\n      var scrollLength = this.scroll.length();\n      indexes.forEach(function (index, i) {\n        index = Math.min(scrollLength - 1, index);\n        var node = void 0,\n            _scroll$leaf5 = _this5.scroll.leaf(index),\n            _scroll$leaf6 = _slicedToArray(_scroll$leaf5, 2),\n            leaf = _scroll$leaf6[0],\n            offset = _scroll$leaf6[1];\n        var _leaf$position5 = leaf.position(offset, i !== 0);\n\n        var _leaf$position6 = _slicedToArray(_leaf$position5, 2);\n\n        node = _leaf$position6[0];\n        offset = _leaf$position6[1];\n\n        args.push(node, offset);\n      });\n      if (args.length < 2) {\n        args = args.concat(args);\n      }\n      return args;\n    }\n  }, {\n    key: 'scrollIntoView',\n    value: function scrollIntoView(scrollingContainer) {\n      var range = this.lastRange;\n      if (range == null) return;\n      var bounds = this.getBounds(range.index, range.length);\n      if (bounds == null) return;\n      var limit = this.scroll.length() - 1;\n\n      var _scroll$line = this.scroll.line(Math.min(range.index, limit)),\n          _scroll$line2 = _slicedToArray(_scroll$line, 1),\n          first = _scroll$line2[0];\n\n      var last = first;\n      if (range.length > 0) {\n        var _scroll$line3 = this.scroll.line(Math.min(range.index + range.length, limit));\n\n        var _scroll$line4 = _slicedToArray(_scroll$line3, 1);\n\n        last = _scroll$line4[0];\n      }\n      if (first == null || last == null) return;\n      var scrollBounds = scrollingContainer.getBoundingClientRect();\n      if (bounds.top < scrollBounds.top) {\n        scrollingContainer.scrollTop -= scrollBounds.top - bounds.top;\n      } else if (bounds.bottom > scrollBounds.bottom) {\n        scrollingContainer.scrollTop += bounds.bottom - scrollBounds.bottom;\n      }\n    }\n  }, {\n    key: 'setNativeRange',\n    value: function setNativeRange(startNode, startOffset) {\n      var endNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : startNode;\n      var endOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : startOffset;\n      var force = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n\n      debug.info('setNativeRange', startNode, startOffset, endNode, endOffset);\n      if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) {\n        return;\n      }\n      var selection = document.getSelection();\n      if (selection == null) return;\n      if (startNode != null) {\n        if (!this.hasFocus()) this.root.focus();\n        var native = (this.getNativeRange() || {}).native;\n        if (native == null || force || startNode !== native.startContainer || startOffset !== native.startOffset || endNode !== native.endContainer || endOffset !== native.endOffset) {\n\n          if (startNode.tagName == \"BR\") {\n            startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode);\n            startNode = startNode.parentNode;\n          }\n          if (endNode.tagName == \"BR\") {\n            endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode);\n            endNode = endNode.parentNode;\n          }\n          var range = document.createRange();\n          range.setStart(startNode, startOffset);\n          range.setEnd(endNode, endOffset);\n          selection.removeAllRanges();\n          selection.addRange(range);\n        }\n      } else {\n        selection.removeAllRanges();\n        this.root.blur();\n        document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs)\n      }\n    }\n  }, {\n    key: 'setRange',\n    value: function setRange(range) {\n      var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n      var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API;\n\n      if (typeof force === 'string') {\n        source = force;\n        force = false;\n      }\n      debug.info('setRange', range);\n      if (range != null) {\n        var args = this.rangeToNative(range);\n        this.setNativeRange.apply(this, _toConsumableArray(args).concat([force]));\n      } else {\n        this.setNativeRange(null);\n      }\n      this.update(source);\n    }\n  }, {\n    key: 'update',\n    value: function update() {\n      var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER;\n\n      var oldRange = this.lastRange;\n\n      var _getRange = this.getRange(),\n          _getRange2 = _slicedToArray(_getRange, 2),\n          lastRange = _getRange2[0],\n          nativeRange = _getRange2[1];\n\n      this.lastRange = lastRange;\n      if (this.lastRange != null) {\n        this.savedRange = this.lastRange;\n      }\n      if (!(0, _deepEqual2.default)(oldRange, this.lastRange)) {\n        var _emitter;\n\n        if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) {\n          this.cursor.restore();\n        }\n        var args = [_emitter4.default.events.SELECTION_CHANGE, (0, _clone2.default)(this.lastRange), (0, _clone2.default)(oldRange), source];\n        (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args));\n        if (source !== _emitter4.default.sources.SILENT) {\n          var _emitter2;\n\n          (_emitter2 = this.emitter).emit.apply(_emitter2, args);\n        }\n      }\n    }\n  }]);\n\n  return Selection;\n}();\n\nfunction contains(parent, descendant) {\n  try {\n    // Firefox inserts inaccessible nodes around video elements\n    descendant.parentNode;\n  } catch (e) {\n    return false;\n  }\n  // IE11 has bug with Text nodes\n  // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect\n  if (descendant instanceof Text) {\n    descendant = descendant.parentNode;\n  }\n  return parent.contains(descendant);\n}\n\nexports.Range = Range;\nexports.default = Selection;\n\n/***/ }),\n/* 16 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Break = function (_Parchment$Embed) {\n  _inherits(Break, _Parchment$Embed);\n\n  function Break() {\n    _classCallCheck(this, Break);\n\n    return _possibleConstructorReturn(this, (Break.__proto__ || Object.getPrototypeOf(Break)).apply(this, arguments));\n  }\n\n  _createClass(Break, [{\n    key: 'insertInto',\n    value: function insertInto(parent, ref) {\n      if (parent.children.length === 0) {\n        _get(Break.prototype.__proto__ || Object.getPrototypeOf(Break.prototype), 'insertInto', this).call(this, parent, ref);\n      } else {\n        this.remove();\n      }\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      return 0;\n    }\n  }, {\n    key: 'value',\n    value: function value() {\n      return '';\n    }\n  }], [{\n    key: 'value',\n    value: function value() {\n      return undefined;\n    }\n  }]);\n\n  return Break;\n}(_parchment2.default.Embed);\n\nBreak.blotName = 'break';\nBreak.tagName = 'BR';\n\nexports.default = Break;\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar linked_list_1 = __webpack_require__(44);\nvar shadow_1 = __webpack_require__(30);\nvar Registry = __webpack_require__(1);\nvar ContainerBlot = /** @class */ (function (_super) {\n    __extends(ContainerBlot, _super);\n    function ContainerBlot(domNode) {\n        var _this = _super.call(this, domNode) || this;\n        _this.build();\n        return _this;\n    }\n    ContainerBlot.prototype.appendChild = function (other) {\n        this.insertBefore(other);\n    };\n    ContainerBlot.prototype.attach = function () {\n        _super.prototype.attach.call(this);\n        this.children.forEach(function (child) {\n            child.attach();\n        });\n    };\n    ContainerBlot.prototype.build = function () {\n        var _this = this;\n        this.children = new linked_list_1.default();\n        // Need to be reversed for if DOM nodes already in order\n        [].slice\n            .call(this.domNode.childNodes)\n            .reverse()\n            .forEach(function (node) {\n            try {\n                var child = makeBlot(node);\n                _this.insertBefore(child, _this.children.head || undefined);\n            }\n            catch (err) {\n                if (err instanceof Registry.ParchmentError)\n                    return;\n                else\n                    throw err;\n            }\n        });\n    };\n    ContainerBlot.prototype.deleteAt = function (index, length) {\n        if (index === 0 && length === this.length()) {\n            return this.remove();\n        }\n        this.children.forEachAt(index, length, function (child, offset, length) {\n            child.deleteAt(offset, length);\n        });\n    };\n    ContainerBlot.prototype.descendant = function (criteria, index) {\n        var _a = this.children.find(index), child = _a[0], offset = _a[1];\n        if ((criteria.blotName == null && criteria(child)) ||\n            (criteria.blotName != null && child instanceof criteria)) {\n            return [child, offset];\n        }\n        else if (child instanceof ContainerBlot) {\n            return child.descendant(criteria, offset);\n        }\n        else {\n            return [null, -1];\n        }\n    };\n    ContainerBlot.prototype.descendants = function (criteria, index, length) {\n        if (index === void 0) { index = 0; }\n        if (length === void 0) { length = Number.MAX_VALUE; }\n        var descendants = [];\n        var lengthLeft = length;\n        this.children.forEachAt(index, length, function (child, index, length) {\n            if ((criteria.blotName == null && criteria(child)) ||\n                (criteria.blotName != null && child instanceof criteria)) {\n                descendants.push(child);\n            }\n            if (child instanceof ContainerBlot) {\n                descendants = descendants.concat(child.descendants(criteria, index, lengthLeft));\n            }\n            lengthLeft -= length;\n        });\n        return descendants;\n    };\n    ContainerBlot.prototype.detach = function () {\n        this.children.forEach(function (child) {\n            child.detach();\n        });\n        _super.prototype.detach.call(this);\n    };\n    ContainerBlot.prototype.formatAt = function (index, length, name, value) {\n        this.children.forEachAt(index, length, function (child, offset, length) {\n            child.formatAt(offset, length, name, value);\n        });\n    };\n    ContainerBlot.prototype.insertAt = function (index, value, def) {\n        var _a = this.children.find(index), child = _a[0], offset = _a[1];\n        if (child) {\n            child.insertAt(offset, value, def);\n        }\n        else {\n            var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n            this.appendChild(blot);\n        }\n    };\n    ContainerBlot.prototype.insertBefore = function (childBlot, refBlot) {\n        if (this.statics.allowedChildren != null &&\n            !this.statics.allowedChildren.some(function (child) {\n                return childBlot instanceof child;\n            })) {\n            throw new Registry.ParchmentError(\"Cannot insert \" + childBlot.statics.blotName + \" into \" + this.statics.blotName);\n        }\n        childBlot.insertInto(this, refBlot);\n    };\n    ContainerBlot.prototype.length = function () {\n        return this.children.reduce(function (memo, child) {\n            return memo + child.length();\n        }, 0);\n    };\n    ContainerBlot.prototype.moveChildren = function (targetParent, refNode) {\n        this.children.forEach(function (child) {\n            targetParent.insertBefore(child, refNode);\n        });\n    };\n    ContainerBlot.prototype.optimize = function (context) {\n        _super.prototype.optimize.call(this, context);\n        if (this.children.length === 0) {\n            if (this.statics.defaultChild != null) {\n                var child = Registry.create(this.statics.defaultChild);\n                this.appendChild(child);\n                child.optimize(context);\n            }\n            else {\n                this.remove();\n            }\n        }\n    };\n    ContainerBlot.prototype.path = function (index, inclusive) {\n        if (inclusive === void 0) { inclusive = false; }\n        var _a = this.children.find(index, inclusive), child = _a[0], offset = _a[1];\n        var position = [[this, index]];\n        if (child instanceof ContainerBlot) {\n            return position.concat(child.path(offset, inclusive));\n        }\n        else if (child != null) {\n            position.push([child, offset]);\n        }\n        return position;\n    };\n    ContainerBlot.prototype.removeChild = function (child) {\n        this.children.remove(child);\n    };\n    ContainerBlot.prototype.replace = function (target) {\n        if (target instanceof ContainerBlot) {\n            target.moveChildren(this);\n        }\n        _super.prototype.replace.call(this, target);\n    };\n    ContainerBlot.prototype.split = function (index, force) {\n        if (force === void 0) { force = false; }\n        if (!force) {\n            if (index === 0)\n                return this;\n            if (index === this.length())\n                return this.next;\n        }\n        var after = this.clone();\n        this.parent.insertBefore(after, this.next);\n        this.children.forEachAt(index, this.length(), function (child, offset, length) {\n            child = child.split(offset, force);\n            after.appendChild(child);\n        });\n        return after;\n    };\n    ContainerBlot.prototype.unwrap = function () {\n        this.moveChildren(this.parent, this.next);\n        this.remove();\n    };\n    ContainerBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        var addedNodes = [];\n        var removedNodes = [];\n        mutations.forEach(function (mutation) {\n            if (mutation.target === _this.domNode && mutation.type === 'childList') {\n                addedNodes.push.apply(addedNodes, mutation.addedNodes);\n                removedNodes.push.apply(removedNodes, mutation.removedNodes);\n            }\n        });\n        removedNodes.forEach(function (node) {\n            // Check node has actually been removed\n            // One exception is Chrome does not immediately remove IFRAMEs\n            // from DOM but MutationRecord is correct in its reported removal\n            if (node.parentNode != null &&\n                // @ts-ignore\n                node.tagName !== 'IFRAME' &&\n                document.body.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n                return;\n            }\n            var blot = Registry.find(node);\n            if (blot == null)\n                return;\n            if (blot.domNode.parentNode == null || blot.domNode.parentNode === _this.domNode) {\n                blot.detach();\n            }\n        });\n        addedNodes\n            .filter(function (node) {\n            return node.parentNode == _this.domNode;\n        })\n            .sort(function (a, b) {\n            if (a === b)\n                return 0;\n            if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) {\n                return 1;\n            }\n            return -1;\n        })\n            .forEach(function (node) {\n            var refBlot = null;\n            if (node.nextSibling != null) {\n                refBlot = Registry.find(node.nextSibling);\n            }\n            var blot = makeBlot(node);\n            if (blot.next != refBlot || blot.next == null) {\n                if (blot.parent != null) {\n                    blot.parent.removeChild(_this);\n                }\n                _this.insertBefore(blot, refBlot || undefined);\n            }\n        });\n    };\n    return ContainerBlot;\n}(shadow_1.default));\nfunction makeBlot(node) {\n    var blot = Registry.find(node);\n    if (blot == null) {\n        try {\n            blot = Registry.create(node);\n        }\n        catch (e) {\n            blot = Registry.create(Registry.Scope.INLINE);\n            [].slice.call(node.childNodes).forEach(function (child) {\n                // @ts-ignore\n                blot.domNode.appendChild(child);\n            });\n            if (node.parentNode) {\n                node.parentNode.replaceChild(blot.domNode, node);\n            }\n            blot.attach();\n        }\n    }\n    return blot;\n}\nexports.default = ContainerBlot;\n\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nvar store_1 = __webpack_require__(31);\nvar container_1 = __webpack_require__(17);\nvar Registry = __webpack_require__(1);\nvar FormatBlot = /** @class */ (function (_super) {\n    __extends(FormatBlot, _super);\n    function FormatBlot(domNode) {\n        var _this = _super.call(this, domNode) || this;\n        _this.attributes = new store_1.default(_this.domNode);\n        return _this;\n    }\n    FormatBlot.formats = function (domNode) {\n        if (typeof this.tagName === 'string') {\n            return true;\n        }\n        else if (Array.isArray(this.tagName)) {\n            return domNode.tagName.toLowerCase();\n        }\n        return undefined;\n    };\n    FormatBlot.prototype.format = function (name, value) {\n        var format = Registry.query(name);\n        if (format instanceof attributor_1.default) {\n            this.attributes.attribute(format, value);\n        }\n        else if (value) {\n            if (format != null && (name !== this.statics.blotName || this.formats()[name] !== value)) {\n                this.replaceWith(name, value);\n            }\n        }\n    };\n    FormatBlot.prototype.formats = function () {\n        var formats = this.attributes.values();\n        var format = this.statics.formats(this.domNode);\n        if (format != null) {\n            formats[this.statics.blotName] = format;\n        }\n        return formats;\n    };\n    FormatBlot.prototype.replaceWith = function (name, value) {\n        var replacement = _super.prototype.replaceWith.call(this, name, value);\n        this.attributes.copy(replacement);\n        return replacement;\n    };\n    FormatBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        _super.prototype.update.call(this, mutations, context);\n        if (mutations.some(function (mutation) {\n            return mutation.target === _this.domNode && mutation.type === 'attributes';\n        })) {\n            this.attributes.build();\n        }\n    };\n    FormatBlot.prototype.wrap = function (name, value) {\n        var wrapper = _super.prototype.wrap.call(this, name, value);\n        if (wrapper instanceof FormatBlot && wrapper.statics.scope === this.statics.scope) {\n            this.attributes.move(wrapper);\n        }\n        return wrapper;\n    };\n    return FormatBlot;\n}(container_1.default));\nexports.default = FormatBlot;\n\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar shadow_1 = __webpack_require__(30);\nvar Registry = __webpack_require__(1);\nvar LeafBlot = /** @class */ (function (_super) {\n    __extends(LeafBlot, _super);\n    function LeafBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    LeafBlot.value = function (domNode) {\n        return true;\n    };\n    LeafBlot.prototype.index = function (node, offset) {\n        if (this.domNode === node ||\n            this.domNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n            return Math.min(offset, 1);\n        }\n        return -1;\n    };\n    LeafBlot.prototype.position = function (index, inclusive) {\n        var offset = [].indexOf.call(this.parent.domNode.childNodes, this.domNode);\n        if (index > 0)\n            offset += 1;\n        return [this.parent.domNode, offset];\n    };\n    LeafBlot.prototype.value = function () {\n        var _a;\n        return _a = {}, _a[this.statics.blotName] = this.statics.value(this.domNode) || true, _a;\n    };\n    LeafBlot.scope = Registry.Scope.INLINE_BLOT;\n    return LeafBlot;\n}(shadow_1.default));\nexports.default = LeafBlot;\n\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar equal = __webpack_require__(11);\nvar extend = __webpack_require__(3);\n\n\nvar lib = {\n  attributes: {\n    compose: function (a, b, keepNull) {\n      if (typeof a !== 'object') a = {};\n      if (typeof b !== 'object') b = {};\n      var attributes = extend(true, {}, b);\n      if (!keepNull) {\n        attributes = Object.keys(attributes).reduce(function (copy, key) {\n          if (attributes[key] != null) {\n            copy[key] = attributes[key];\n          }\n          return copy;\n        }, {});\n      }\n      for (var key in a) {\n        if (a[key] !== undefined && b[key] === undefined) {\n          attributes[key] = a[key];\n        }\n      }\n      return Object.keys(attributes).length > 0 ? attributes : undefined;\n    },\n\n    diff: function(a, b) {\n      if (typeof a !== 'object') a = {};\n      if (typeof b !== 'object') b = {};\n      var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) {\n        if (!equal(a[key], b[key])) {\n          attributes[key] = b[key] === undefined ? null : b[key];\n        }\n        return attributes;\n      }, {});\n      return Object.keys(attributes).length > 0 ? attributes : undefined;\n    },\n\n    transform: function (a, b, priority) {\n      if (typeof a !== 'object') return b;\n      if (typeof b !== 'object') return undefined;\n      if (!priority) return b;  // b simply overwrites us without priority\n      var attributes = Object.keys(b).reduce(function (attributes, key) {\n        if (a[key] === undefined) attributes[key] = b[key];  // null is a valid value\n        return attributes;\n      }, {});\n      return Object.keys(attributes).length > 0 ? attributes : undefined;\n    }\n  },\n\n  iterator: function (ops) {\n    return new Iterator(ops);\n  },\n\n  length: function (op) {\n    if (typeof op['delete'] === 'number') {\n      return op['delete'];\n    } else if (typeof op.retain === 'number') {\n      return op.retain;\n    } else {\n      return typeof op.insert === 'string' ? op.insert.length : 1;\n    }\n  }\n};\n\n\nfunction Iterator(ops) {\n  this.ops = ops;\n  this.index = 0;\n  this.offset = 0;\n};\n\nIterator.prototype.hasNext = function () {\n  return this.peekLength() < Infinity;\n};\n\nIterator.prototype.next = function (length) {\n  if (!length) length = Infinity;\n  var nextOp = this.ops[this.index];\n  if (nextOp) {\n    var offset = this.offset;\n    var opLength = lib.length(nextOp)\n    if (length >= opLength - offset) {\n      length = opLength - offset;\n      this.index += 1;\n      this.offset = 0;\n    } else {\n      this.offset += length;\n    }\n    if (typeof nextOp['delete'] === 'number') {\n      return { 'delete': length };\n    } else {\n      var retOp = {};\n      if (nextOp.attributes) {\n        retOp.attributes = nextOp.attributes;\n      }\n      if (typeof nextOp.retain === 'number') {\n        retOp.retain = length;\n      } else if (typeof nextOp.insert === 'string') {\n        retOp.insert = nextOp.insert.substr(offset, length);\n      } else {\n        // offset should === 0, length should === 1\n        retOp.insert = nextOp.insert;\n      }\n      return retOp;\n    }\n  } else {\n    return { retain: Infinity };\n  }\n};\n\nIterator.prototype.peek = function () {\n  return this.ops[this.index];\n};\n\nIterator.prototype.peekLength = function () {\n  if (this.ops[this.index]) {\n    // Should never return 0 if our index is being managed correctly\n    return lib.length(this.ops[this.index]) - this.offset;\n  } else {\n    return Infinity;\n  }\n};\n\nIterator.prototype.peekType = function () {\n  if (this.ops[this.index]) {\n    if (typeof this.ops[this.index]['delete'] === 'number') {\n      return 'delete';\n    } else if (typeof this.ops[this.index].retain === 'number') {\n      return 'retain';\n    } else {\n      return 'insert';\n    }\n  }\n  return 'retain';\n};\n\nIterator.prototype.rest = function () {\n  if (!this.hasNext()) {\n    return [];\n  } else if (this.offset === 0) {\n    return this.ops.slice(this.index);\n  } else {\n    var offset = this.offset;\n    var index = this.index;\n    var next = this.next();\n    var rest = this.ops.slice(this.index);\n    this.offset = offset;\n    this.index = index;\n    return [next].concat(rest);\n  }\n};\n\n\nmodule.exports = lib;\n\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports) {\n\nvar clone = (function() {\n'use strict';\n\nfunction _instanceof(obj, type) {\n  return type != null && obj instanceof type;\n}\n\nvar nativeMap;\ntry {\n  nativeMap = Map;\n} catch(_) {\n  // maybe a reference error because no `Map`. Give it a dummy value that no\n  // value will ever be an instanceof.\n  nativeMap = function() {};\n}\n\nvar nativeSet;\ntry {\n  nativeSet = Set;\n} catch(_) {\n  nativeSet = function() {};\n}\n\nvar nativePromise;\ntry {\n  nativePromise = Promise;\n} catch(_) {\n  nativePromise = function() {};\n}\n\n/**\n * Clones (copies) an Object using deep copying.\n *\n * This function supports circular references by default, but if you are certain\n * there are no circular references in your object, you can save some CPU time\n * by calling clone(obj, false).\n *\n * Caution: if `circular` is false and `parent` contains circular references,\n * your program may enter an infinite loop and crash.\n *\n * @param `parent` - the object to be cloned\n * @param `circular` - set to true if the object to be cloned may contain\n *    circular references. (optional - true by default)\n * @param `depth` - set to a number if the object is only to be cloned to\n *    a particular depth. (optional - defaults to Infinity)\n * @param `prototype` - sets the prototype to be used when cloning an object.\n *    (optional - defaults to parent prototype).\n * @param `includeNonEnumerable` - set to true if the non-enumerable properties\n *    should be cloned as well. Non-enumerable properties on the prototype\n *    chain will be ignored. (optional - false by default)\n*/\nfunction clone(parent, circular, depth, prototype, includeNonEnumerable) {\n  if (typeof circular === 'object') {\n    depth = circular.depth;\n    prototype = circular.prototype;\n    includeNonEnumerable = circular.includeNonEnumerable;\n    circular = circular.circular;\n  }\n  // maintain two arrays for circular references, where corresponding parents\n  // and children have the same index\n  var allParents = [];\n  var allChildren = [];\n\n  var useBuffer = typeof Buffer != 'undefined';\n\n  if (typeof circular == 'undefined')\n    circular = true;\n\n  if (typeof depth == 'undefined')\n    depth = Infinity;\n\n  // recurse this function so we don't reset allParents and allChildren\n  function _clone(parent, depth) {\n    // cloning null always returns null\n    if (parent === null)\n      return null;\n\n    if (depth === 0)\n      return parent;\n\n    var child;\n    var proto;\n    if (typeof parent != 'object') {\n      return parent;\n    }\n\n    if (_instanceof(parent, nativeMap)) {\n      child = new nativeMap();\n    } else if (_instanceof(parent, nativeSet)) {\n      child = new nativeSet();\n    } else if (_instanceof(parent, nativePromise)) {\n      child = new nativePromise(function (resolve, reject) {\n        parent.then(function(value) {\n          resolve(_clone(value, depth - 1));\n        }, function(err) {\n          reject(_clone(err, depth - 1));\n        });\n      });\n    } else if (clone.__isArray(parent)) {\n      child = [];\n    } else if (clone.__isRegExp(parent)) {\n      child = new RegExp(parent.source, __getRegExpFlags(parent));\n      if (parent.lastIndex) child.lastIndex = parent.lastIndex;\n    } else if (clone.__isDate(parent)) {\n      child = new Date(parent.getTime());\n    } else if (useBuffer && Buffer.isBuffer(parent)) {\n      if (Buffer.allocUnsafe) {\n        // Node.js >= 4.5.0\n        child = Buffer.allocUnsafe(parent.length);\n      } else {\n        // Older Node.js versions\n        child = new Buffer(parent.length);\n      }\n      parent.copy(child);\n      return child;\n    } else if (_instanceof(parent, Error)) {\n      child = Object.create(parent);\n    } else {\n      if (typeof prototype == 'undefined') {\n        proto = Object.getPrototypeOf(parent);\n        child = Object.create(proto);\n      }\n      else {\n        child = Object.create(prototype);\n        proto = prototype;\n      }\n    }\n\n    if (circular) {\n      var index = allParents.indexOf(parent);\n\n      if (index != -1) {\n        return allChildren[index];\n      }\n      allParents.push(parent);\n      allChildren.push(child);\n    }\n\n    if (_instanceof(parent, nativeMap)) {\n      parent.forEach(function(value, key) {\n        var keyChild = _clone(key, depth - 1);\n        var valueChild = _clone(value, depth - 1);\n        child.set(keyChild, valueChild);\n      });\n    }\n    if (_instanceof(parent, nativeSet)) {\n      parent.forEach(function(value) {\n        var entryChild = _clone(value, depth - 1);\n        child.add(entryChild);\n      });\n    }\n\n    for (var i in parent) {\n      var attrs;\n      if (proto) {\n        attrs = Object.getOwnPropertyDescriptor(proto, i);\n      }\n\n      if (attrs && attrs.set == null) {\n        continue;\n      }\n      child[i] = _clone(parent[i], depth - 1);\n    }\n\n    if (Object.getOwnPropertySymbols) {\n      var symbols = Object.getOwnPropertySymbols(parent);\n      for (var i = 0; i < symbols.length; i++) {\n        // Don't need to worry about cloning a symbol because it is a primitive,\n        // like a number or string.\n        var symbol = symbols[i];\n        var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);\n        if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {\n          continue;\n        }\n        child[symbol] = _clone(parent[symbol], depth - 1);\n        if (!descriptor.enumerable) {\n          Object.defineProperty(child, symbol, {\n            enumerable: false\n          });\n        }\n      }\n    }\n\n    if (includeNonEnumerable) {\n      var allPropertyNames = Object.getOwnPropertyNames(parent);\n      for (var i = 0; i < allPropertyNames.length; i++) {\n        var propertyName = allPropertyNames[i];\n        var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);\n        if (descriptor && descriptor.enumerable) {\n          continue;\n        }\n        child[propertyName] = _clone(parent[propertyName], depth - 1);\n        Object.defineProperty(child, propertyName, {\n          enumerable: false\n        });\n      }\n    }\n\n    return child;\n  }\n\n  return _clone(parent, depth);\n}\n\n/**\n * Simple flat clone using prototype, accepts only objects, usefull for property\n * override on FLAT configuration object (no nested props).\n *\n * USE WITH CAUTION! This may not behave as you wish if you do not know how this\n * works.\n */\nclone.clonePrototype = function clonePrototype(parent) {\n  if (parent === null)\n    return null;\n\n  var c = function () {};\n  c.prototype = parent;\n  return new c();\n};\n\n// private utility functions\n\nfunction __objToStr(o) {\n  return Object.prototype.toString.call(o);\n}\nclone.__objToStr = __objToStr;\n\nfunction __isDate(o) {\n  return typeof o === 'object' && __objToStr(o) === '[object Date]';\n}\nclone.__isDate = __isDate;\n\nfunction __isArray(o) {\n  return typeof o === 'object' && __objToStr(o) === '[object Array]';\n}\nclone.__isArray = __isArray;\n\nfunction __isRegExp(o) {\n  return typeof o === 'object' && __objToStr(o) === '[object RegExp]';\n}\nclone.__isRegExp = __isRegExp;\n\nfunction __getRegExpFlags(re) {\n  var flags = '';\n  if (re.global) flags += 'g';\n  if (re.ignoreCase) flags += 'i';\n  if (re.multiline) flags += 'm';\n  return flags;\n}\nclone.__getRegExpFlags = __getRegExpFlags;\n\nreturn clone;\n})();\n\nif (typeof module === 'object' && module.exports) {\n  module.exports = clone;\n}\n\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _emitter = __webpack_require__(8);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _container = __webpack_require__(25);\n\nvar _container2 = _interopRequireDefault(_container);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nfunction isLine(blot) {\n  return blot instanceof _block2.default || blot instanceof _block.BlockEmbed;\n}\n\nvar Scroll = function (_Parchment$Scroll) {\n  _inherits(Scroll, _Parchment$Scroll);\n\n  function Scroll(domNode, config) {\n    _classCallCheck(this, Scroll);\n\n    var _this = _possibleConstructorReturn(this, (Scroll.__proto__ || Object.getPrototypeOf(Scroll)).call(this, domNode));\n\n    _this.emitter = config.emitter;\n    if (Array.isArray(config.whitelist)) {\n      _this.whitelist = config.whitelist.reduce(function (whitelist, format) {\n        whitelist[format] = true;\n        return whitelist;\n      }, {});\n    }\n    // Some reason fixes composition issues with character languages in Windows/Chrome, Safari\n    _this.domNode.addEventListener('DOMNodeInserted', function () {});\n    _this.optimize();\n    _this.enable();\n    return _this;\n  }\n\n  _createClass(Scroll, [{\n    key: 'batchStart',\n    value: function batchStart() {\n      this.batch = true;\n    }\n  }, {\n    key: 'batchEnd',\n    value: function batchEnd() {\n      this.batch = false;\n      this.optimize();\n    }\n  }, {\n    key: 'deleteAt',\n    value: function deleteAt(index, length) {\n      var _line = this.line(index),\n          _line2 = _slicedToArray(_line, 2),\n          first = _line2[0],\n          offset = _line2[1];\n\n      var _line3 = this.line(index + length),\n          _line4 = _slicedToArray(_line3, 1),\n          last = _line4[0];\n\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'deleteAt', this).call(this, index, length);\n      if (last != null && first !== last && offset > 0) {\n        if (first instanceof _block.BlockEmbed || last instanceof _block.BlockEmbed) {\n          this.optimize();\n          return;\n        }\n        if (first instanceof _code2.default) {\n          var newlineIndex = first.newlineIndex(first.length(), true);\n          if (newlineIndex > -1) {\n            first = first.split(newlineIndex + 1);\n            if (first === last) {\n              this.optimize();\n              return;\n            }\n          }\n        } else if (last instanceof _code2.default) {\n          var _newlineIndex = last.newlineIndex(0);\n          if (_newlineIndex > -1) {\n            last.split(_newlineIndex + 1);\n          }\n        }\n        var ref = last.children.head instanceof _break2.default ? null : last.children.head;\n        first.moveChildren(last, ref);\n        first.remove();\n      }\n      this.optimize();\n    }\n  }, {\n    key: 'enable',\n    value: function enable() {\n      var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n\n      this.domNode.setAttribute('contenteditable', enabled);\n    }\n  }, {\n    key: 'formatAt',\n    value: function formatAt(index, length, format, value) {\n      if (this.whitelist != null && !this.whitelist[format]) return;\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'formatAt', this).call(this, index, length, format, value);\n      this.optimize();\n    }\n  }, {\n    key: 'insertAt',\n    value: function insertAt(index, value, def) {\n      if (def != null && this.whitelist != null && !this.whitelist[value]) return;\n      if (index >= this.length()) {\n        if (def == null || _parchment2.default.query(value, _parchment2.default.Scope.BLOCK) == null) {\n          var blot = _parchment2.default.create(this.statics.defaultChild);\n          this.appendChild(blot);\n          if (def == null && value.endsWith('\\n')) {\n            value = value.slice(0, -1);\n          }\n          blot.insertAt(0, value, def);\n        } else {\n          var embed = _parchment2.default.create(value, def);\n          this.appendChild(embed);\n        }\n      } else {\n        _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertAt', this).call(this, index, value, def);\n      }\n      this.optimize();\n    }\n  }, {\n    key: 'insertBefore',\n    value: function insertBefore(blot, ref) {\n      if (blot.statics.scope === _parchment2.default.Scope.INLINE_BLOT) {\n        var wrapper = _parchment2.default.create(this.statics.defaultChild);\n        wrapper.appendChild(blot);\n        blot = wrapper;\n      }\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertBefore', this).call(this, blot, ref);\n    }\n  }, {\n    key: 'leaf',\n    value: function leaf(index) {\n      return this.path(index).pop() || [null, -1];\n    }\n  }, {\n    key: 'line',\n    value: function line(index) {\n      if (index === this.length()) {\n        return this.line(index - 1);\n      }\n      return this.descendant(isLine, index);\n    }\n  }, {\n    key: 'lines',\n    value: function lines() {\n      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n      var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;\n\n      var getLines = function getLines(blot, index, length) {\n        var lines = [],\n            lengthLeft = length;\n        blot.children.forEachAt(index, length, function (child, index, length) {\n          if (isLine(child)) {\n            lines.push(child);\n          } else if (child instanceof _parchment2.default.Container) {\n            lines = lines.concat(getLines(child, index, lengthLeft));\n          }\n          lengthLeft -= length;\n        });\n        return lines;\n      };\n      return getLines(this, index, length);\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize() {\n      var mutations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n      var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n      if (this.batch === true) return;\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'optimize', this).call(this, mutations, context);\n      if (mutations.length > 0) {\n        this.emitter.emit(_emitter2.default.events.SCROLL_OPTIMIZE, mutations, context);\n      }\n    }\n  }, {\n    key: 'path',\n    value: function path(index) {\n      return _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'path', this).call(this, index).slice(1); // Exclude self\n    }\n  }, {\n    key: 'update',\n    value: function update(mutations) {\n      if (this.batch === true) return;\n      var source = _emitter2.default.sources.USER;\n      if (typeof mutations === 'string') {\n        source = mutations;\n      }\n      if (!Array.isArray(mutations)) {\n        mutations = this.observer.takeRecords();\n      }\n      if (mutations.length > 0) {\n        this.emitter.emit(_emitter2.default.events.SCROLL_BEFORE_UPDATE, source, mutations);\n      }\n      _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'update', this).call(this, mutations.concat([])); // pass copy\n      if (mutations.length > 0) {\n        this.emitter.emit(_emitter2.default.events.SCROLL_UPDATE, source, mutations);\n      }\n    }\n  }]);\n\n  return Scroll;\n}(_parchment2.default.Scroll);\n\nScroll.blotName = 'scroll';\nScroll.className = 'ql-editor';\nScroll.tagName = 'DIV';\nScroll.defaultChild = 'block';\nScroll.allowedChildren = [_block2.default, _block.BlockEmbed, _container2.default];\n\nexports.default = Scroll;\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.SHORTKEY = exports.default = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(11);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _op = __webpack_require__(20);\n\nvar _op2 = _interopRequireDefault(_op);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:keyboard');\n\nvar SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey';\n\nvar Keyboard = function (_Module) {\n  _inherits(Keyboard, _Module);\n\n  _createClass(Keyboard, null, [{\n    key: 'match',\n    value: function match(evt, binding) {\n      binding = normalize(binding);\n      if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function (key) {\n        return !!binding[key] !== evt[key] && binding[key] !== null;\n      })) {\n        return false;\n      }\n      return binding.key === (evt.which || evt.keyCode);\n    }\n  }]);\n\n  function Keyboard(quill, options) {\n    _classCallCheck(this, Keyboard);\n\n    var _this = _possibleConstructorReturn(this, (Keyboard.__proto__ || Object.getPrototypeOf(Keyboard)).call(this, quill, options));\n\n    _this.bindings = {};\n    Object.keys(_this.options.bindings).forEach(function (name) {\n      if (name === 'list autofill' && quill.scroll.whitelist != null && !quill.scroll.whitelist['list']) {\n        return;\n      }\n      if (_this.options.bindings[name]) {\n        _this.addBinding(_this.options.bindings[name]);\n      }\n    });\n    _this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter);\n    _this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function () {});\n    if (/Firefox/i.test(navigator.userAgent)) {\n      // Need to handle delete and backspace for Firefox in the general case #1171\n      _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace);\n      _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete);\n    } else {\n      _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace);\n      _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete);\n    }\n    _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange);\n    _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange);\n    _this.addBinding({ key: Keyboard.keys.BACKSPACE, altKey: null, ctrlKey: null, metaKey: null, shiftKey: null }, { collapsed: true, offset: 0 }, handleBackspace);\n    _this.listen();\n    return _this;\n  }\n\n  _createClass(Keyboard, [{\n    key: 'addBinding',\n    value: function addBinding(key) {\n      var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n      var handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n      var binding = normalize(key);\n      if (binding == null || binding.key == null) {\n        return debug.warn('Attempted to add invalid keyboard binding', binding);\n      }\n      if (typeof context === 'function') {\n        context = { handler: context };\n      }\n      if (typeof handler === 'function') {\n        handler = { handler: handler };\n      }\n      binding = (0, _extend2.default)(binding, context, handler);\n      this.bindings[binding.key] = this.bindings[binding.key] || [];\n      this.bindings[binding.key].push(binding);\n    }\n  }, {\n    key: 'listen',\n    value: function listen() {\n      var _this2 = this;\n\n      this.quill.root.addEventListener('keydown', function (evt) {\n        if (evt.defaultPrevented) return;\n        var which = evt.which || evt.keyCode;\n        var bindings = (_this2.bindings[which] || []).filter(function (binding) {\n          return Keyboard.match(evt, binding);\n        });\n        if (bindings.length === 0) return;\n        var range = _this2.quill.getSelection();\n        if (range == null || !_this2.quill.hasFocus()) return;\n\n        var _quill$getLine = _this2.quill.getLine(range.index),\n            _quill$getLine2 = _slicedToArray(_quill$getLine, 2),\n            line = _quill$getLine2[0],\n            offset = _quill$getLine2[1];\n\n        var _quill$getLeaf = _this2.quill.getLeaf(range.index),\n            _quill$getLeaf2 = _slicedToArray(_quill$getLeaf, 2),\n            leafStart = _quill$getLeaf2[0],\n            offsetStart = _quill$getLeaf2[1];\n\n        var _ref = range.length === 0 ? [leafStart, offsetStart] : _this2.quill.getLeaf(range.index + range.length),\n            _ref2 = _slicedToArray(_ref, 2),\n            leafEnd = _ref2[0],\n            offsetEnd = _ref2[1];\n\n        var prefixText = leafStart instanceof _parchment2.default.Text ? leafStart.value().slice(0, offsetStart) : '';\n        var suffixText = leafEnd instanceof _parchment2.default.Text ? leafEnd.value().slice(offsetEnd) : '';\n        var curContext = {\n          collapsed: range.length === 0,\n          empty: range.length === 0 && line.length() <= 1,\n          format: _this2.quill.getFormat(range),\n          offset: offset,\n          prefix: prefixText,\n          suffix: suffixText\n        };\n        var prevented = bindings.some(function (binding) {\n          if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false;\n          if (binding.empty != null && binding.empty !== curContext.empty) return false;\n          if (binding.offset != null && binding.offset !== curContext.offset) return false;\n          if (Array.isArray(binding.format)) {\n            // any format is present\n            if (binding.format.every(function (name) {\n              return curContext.format[name] == null;\n            })) {\n              return false;\n            }\n          } else if (_typeof(binding.format) === 'object') {\n            // all formats must match\n            if (!Object.keys(binding.format).every(function (name) {\n              if (binding.format[name] === true) return curContext.format[name] != null;\n              if (binding.format[name] === false) return curContext.format[name] == null;\n              return (0, _deepEqual2.default)(binding.format[name], curContext.format[name]);\n            })) {\n              return false;\n            }\n          }\n          if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false;\n          if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false;\n          return binding.handler.call(_this2, range, curContext) !== true;\n        });\n        if (prevented) {\n          evt.preventDefault();\n        }\n      });\n    }\n  }]);\n\n  return Keyboard;\n}(_module2.default);\n\nKeyboard.keys = {\n  BACKSPACE: 8,\n  TAB: 9,\n  ENTER: 13,\n  ESCAPE: 27,\n  LEFT: 37,\n  UP: 38,\n  RIGHT: 39,\n  DOWN: 40,\n  DELETE: 46\n};\n\nKeyboard.DEFAULTS = {\n  bindings: {\n    'bold': makeFormatHandler('bold'),\n    'italic': makeFormatHandler('italic'),\n    'underline': makeFormatHandler('underline'),\n    'indent': {\n      // highlight tab or tab at beginning of list, indent or blockquote\n      key: Keyboard.keys.TAB,\n      format: ['blockquote', 'indent', 'list'],\n      handler: function handler(range, context) {\n        if (context.collapsed && context.offset !== 0) return true;\n        this.quill.format('indent', '+1', _quill2.default.sources.USER);\n      }\n    },\n    'outdent': {\n      key: Keyboard.keys.TAB,\n      shiftKey: true,\n      format: ['blockquote', 'indent', 'list'],\n      // highlight tab or tab at beginning of list, indent or blockquote\n      handler: function handler(range, context) {\n        if (context.collapsed && context.offset !== 0) return true;\n        this.quill.format('indent', '-1', _quill2.default.sources.USER);\n      }\n    },\n    'outdent backspace': {\n      key: Keyboard.keys.BACKSPACE,\n      collapsed: true,\n      shiftKey: null,\n      metaKey: null,\n      ctrlKey: null,\n      altKey: null,\n      format: ['indent', 'list'],\n      offset: 0,\n      handler: function handler(range, context) {\n        if (context.format.indent != null) {\n          this.quill.format('indent', '-1', _quill2.default.sources.USER);\n        } else if (context.format.list != null) {\n          this.quill.format('list', false, _quill2.default.sources.USER);\n        }\n      }\n    },\n    'indent code-block': makeCodeBlockHandler(true),\n    'outdent code-block': makeCodeBlockHandler(false),\n    'remove tab': {\n      key: Keyboard.keys.TAB,\n      shiftKey: true,\n      collapsed: true,\n      prefix: /\\t$/,\n      handler: function handler(range) {\n        this.quill.deleteText(range.index - 1, 1, _quill2.default.sources.USER);\n      }\n    },\n    'tab': {\n      key: Keyboard.keys.TAB,\n      handler: function handler(range) {\n        this.quill.history.cutoff();\n        var delta = new _quillDelta2.default().retain(range.index).delete(range.length).insert('\\t');\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.history.cutoff();\n        this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n      }\n    },\n    'list empty enter': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: ['list'],\n      empty: true,\n      handler: function handler(range, context) {\n        this.quill.format('list', false, _quill2.default.sources.USER);\n        if (context.format.indent) {\n          this.quill.format('indent', false, _quill2.default.sources.USER);\n        }\n      }\n    },\n    'checklist enter': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: { list: 'checked' },\n      handler: function handler(range) {\n        var _quill$getLine3 = this.quill.getLine(range.index),\n            _quill$getLine4 = _slicedToArray(_quill$getLine3, 2),\n            line = _quill$getLine4[0],\n            offset = _quill$getLine4[1];\n\n        var formats = (0, _extend2.default)({}, line.formats(), { list: 'checked' });\n        var delta = new _quillDelta2.default().retain(range.index).insert('\\n', formats).retain(line.length() - offset - 1).retain(1, { list: 'unchecked' });\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n        this.quill.scrollIntoView();\n      }\n    },\n    'header enter': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: ['header'],\n      suffix: /^$/,\n      handler: function handler(range, context) {\n        var _quill$getLine5 = this.quill.getLine(range.index),\n            _quill$getLine6 = _slicedToArray(_quill$getLine5, 2),\n            line = _quill$getLine6[0],\n            offset = _quill$getLine6[1];\n\n        var delta = new _quillDelta2.default().retain(range.index).insert('\\n', context.format).retain(line.length() - offset - 1).retain(1, { header: null });\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n        this.quill.scrollIntoView();\n      }\n    },\n    'list autofill': {\n      key: ' ',\n      collapsed: true,\n      format: { list: false },\n      prefix: /^\\s*?(\\d+\\.|-|\\*|\\[ ?\\]|\\[x\\])$/,\n      handler: function handler(range, context) {\n        var length = context.prefix.length;\n\n        var _quill$getLine7 = this.quill.getLine(range.index),\n            _quill$getLine8 = _slicedToArray(_quill$getLine7, 2),\n            line = _quill$getLine8[0],\n            offset = _quill$getLine8[1];\n\n        if (offset > length) return true;\n        var value = void 0;\n        switch (context.prefix.trim()) {\n          case '[]':case '[ ]':\n            value = 'unchecked';\n            break;\n          case '[x]':\n            value = 'checked';\n            break;\n          case '-':case '*':\n            value = 'bullet';\n            break;\n          default:\n            value = 'ordered';\n        }\n        this.quill.insertText(range.index, ' ', _quill2.default.sources.USER);\n        this.quill.history.cutoff();\n        var delta = new _quillDelta2.default().retain(range.index - offset).delete(length + 1).retain(line.length() - 2 - offset).retain(1, { list: value });\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n        this.quill.history.cutoff();\n        this.quill.setSelection(range.index - length, _quill2.default.sources.SILENT);\n      }\n    },\n    'code exit': {\n      key: Keyboard.keys.ENTER,\n      collapsed: true,\n      format: ['code-block'],\n      prefix: /\\n\\n$/,\n      suffix: /^\\s+$/,\n      handler: function handler(range) {\n        var _quill$getLine9 = this.quill.getLine(range.index),\n            _quill$getLine10 = _slicedToArray(_quill$getLine9, 2),\n            line = _quill$getLine10[0],\n            offset = _quill$getLine10[1];\n\n        var delta = new _quillDelta2.default().retain(range.index + line.length() - offset - 2).retain(1, { 'code-block': null }).delete(1);\n        this.quill.updateContents(delta, _quill2.default.sources.USER);\n      }\n    },\n    'embed left': makeEmbedArrowHandler(Keyboard.keys.LEFT, false),\n    'embed left shift': makeEmbedArrowHandler(Keyboard.keys.LEFT, true),\n    'embed right': makeEmbedArrowHandler(Keyboard.keys.RIGHT, false),\n    'embed right shift': makeEmbedArrowHandler(Keyboard.keys.RIGHT, true)\n  }\n};\n\nfunction makeEmbedArrowHandler(key, shiftKey) {\n  var _ref3;\n\n  var where = key === Keyboard.keys.LEFT ? 'prefix' : 'suffix';\n  return _ref3 = {\n    key: key,\n    shiftKey: shiftKey,\n    altKey: null\n  }, _defineProperty(_ref3, where, /^$/), _defineProperty(_ref3, 'handler', function handler(range) {\n    var index = range.index;\n    if (key === Keyboard.keys.RIGHT) {\n      index += range.length + 1;\n    }\n\n    var _quill$getLeaf3 = this.quill.getLeaf(index),\n        _quill$getLeaf4 = _slicedToArray(_quill$getLeaf3, 1),\n        leaf = _quill$getLeaf4[0];\n\n    if (!(leaf instanceof _parchment2.default.Embed)) return true;\n    if (key === Keyboard.keys.LEFT) {\n      if (shiftKey) {\n        this.quill.setSelection(range.index - 1, range.length + 1, _quill2.default.sources.USER);\n      } else {\n        this.quill.setSelection(range.index - 1, _quill2.default.sources.USER);\n      }\n    } else {\n      if (shiftKey) {\n        this.quill.setSelection(range.index, range.length + 1, _quill2.default.sources.USER);\n      } else {\n        this.quill.setSelection(range.index + range.length + 1, _quill2.default.sources.USER);\n      }\n    }\n    return false;\n  }), _ref3;\n}\n\nfunction handleBackspace(range, context) {\n  if (range.index === 0 || this.quill.getLength() <= 1) return;\n\n  var _quill$getLine11 = this.quill.getLine(range.index),\n      _quill$getLine12 = _slicedToArray(_quill$getLine11, 1),\n      line = _quill$getLine12[0];\n\n  var formats = {};\n  if (context.offset === 0) {\n    var _quill$getLine13 = this.quill.getLine(range.index - 1),\n        _quill$getLine14 = _slicedToArray(_quill$getLine13, 1),\n        prev = _quill$getLine14[0];\n\n    if (prev != null && prev.length() > 1) {\n      var curFormats = line.formats();\n      var prevFormats = this.quill.getFormat(range.index - 1, 1);\n      formats = _op2.default.attributes.diff(curFormats, prevFormats) || {};\n    }\n  }\n  // Check for astral symbols\n  var length = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]$/.test(context.prefix) ? 2 : 1;\n  this.quill.deleteText(range.index - length, length, _quill2.default.sources.USER);\n  if (Object.keys(formats).length > 0) {\n    this.quill.formatLine(range.index - length, length, formats, _quill2.default.sources.USER);\n  }\n  this.quill.focus();\n}\n\nfunction handleDelete(range, context) {\n  // Check for astral symbols\n  var length = /^[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/.test(context.suffix) ? 2 : 1;\n  if (range.index >= this.quill.getLength() - length) return;\n  var formats = {},\n      nextLength = 0;\n\n  var _quill$getLine15 = this.quill.getLine(range.index),\n      _quill$getLine16 = _slicedToArray(_quill$getLine15, 1),\n      line = _quill$getLine16[0];\n\n  if (context.offset >= line.length() - 1) {\n    var _quill$getLine17 = this.quill.getLine(range.index + 1),\n        _quill$getLine18 = _slicedToArray(_quill$getLine17, 1),\n        next = _quill$getLine18[0];\n\n    if (next) {\n      var curFormats = line.formats();\n      var nextFormats = this.quill.getFormat(range.index, 1);\n      formats = _op2.default.attributes.diff(curFormats, nextFormats) || {};\n      nextLength = next.length();\n    }\n  }\n  this.quill.deleteText(range.index, length, _quill2.default.sources.USER);\n  if (Object.keys(formats).length > 0) {\n    this.quill.formatLine(range.index + nextLength - 1, length, formats, _quill2.default.sources.USER);\n  }\n}\n\nfunction handleDeleteRange(range) {\n  var lines = this.quill.getLines(range);\n  var formats = {};\n  if (lines.length > 1) {\n    var firstFormats = lines[0].formats();\n    var lastFormats = lines[lines.length - 1].formats();\n    formats = _op2.default.attributes.diff(lastFormats, firstFormats) || {};\n  }\n  this.quill.deleteText(range, _quill2.default.sources.USER);\n  if (Object.keys(formats).length > 0) {\n    this.quill.formatLine(range.index, 1, formats, _quill2.default.sources.USER);\n  }\n  this.quill.setSelection(range.index, _quill2.default.sources.SILENT);\n  this.quill.focus();\n}\n\nfunction handleEnter(range, context) {\n  var _this3 = this;\n\n  if (range.length > 0) {\n    this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change\n  }\n  var lineFormats = Object.keys(context.format).reduce(function (lineFormats, format) {\n    if (_parchment2.default.query(format, _parchment2.default.Scope.BLOCK) && !Array.isArray(context.format[format])) {\n      lineFormats[format] = context.format[format];\n    }\n    return lineFormats;\n  }, {});\n  this.quill.insertText(range.index, '\\n', lineFormats, _quill2.default.sources.USER);\n  // Earlier scroll.deleteAt might have messed up our selection,\n  // so insertText's built in selection preservation is not reliable\n  this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n  this.quill.focus();\n  Object.keys(context.format).forEach(function (name) {\n    if (lineFormats[name] != null) return;\n    if (Array.isArray(context.format[name])) return;\n    if (name === 'link') return;\n    _this3.quill.format(name, context.format[name], _quill2.default.sources.USER);\n  });\n}\n\nfunction makeCodeBlockHandler(indent) {\n  return {\n    key: Keyboard.keys.TAB,\n    shiftKey: !indent,\n    format: { 'code-block': true },\n    handler: function handler(range) {\n      var CodeBlock = _parchment2.default.query('code-block');\n      var index = range.index,\n          length = range.length;\n\n      var _quill$scroll$descend = this.quill.scroll.descendant(CodeBlock, index),\n          _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2),\n          block = _quill$scroll$descend2[0],\n          offset = _quill$scroll$descend2[1];\n\n      if (block == null) return;\n      var scrollIndex = this.quill.getIndex(block);\n      var start = block.newlineIndex(offset, true) + 1;\n      var end = block.newlineIndex(scrollIndex + offset + length);\n      var lines = block.domNode.textContent.slice(start, end).split('\\n');\n      offset = 0;\n      lines.forEach(function (line, i) {\n        if (indent) {\n          block.insertAt(start + offset, CodeBlock.TAB);\n          offset += CodeBlock.TAB.length;\n          if (i === 0) {\n            index += CodeBlock.TAB.length;\n          } else {\n            length += CodeBlock.TAB.length;\n          }\n        } else if (line.startsWith(CodeBlock.TAB)) {\n          block.deleteAt(start + offset, CodeBlock.TAB.length);\n          offset -= CodeBlock.TAB.length;\n          if (i === 0) {\n            index -= CodeBlock.TAB.length;\n          } else {\n            length -= CodeBlock.TAB.length;\n          }\n        }\n        offset += line.length + 1;\n      });\n      this.quill.update(_quill2.default.sources.USER);\n      this.quill.setSelection(index, length, _quill2.default.sources.SILENT);\n    }\n  };\n}\n\nfunction makeFormatHandler(format) {\n  return {\n    key: format[0].toUpperCase(),\n    shortKey: true,\n    handler: function handler(range, context) {\n      this.quill.format(format, !context.format[format], _quill2.default.sources.USER);\n    }\n  };\n}\n\nfunction normalize(binding) {\n  if (typeof binding === 'string' || typeof binding === 'number') {\n    return normalize({ key: binding });\n  }\n  if ((typeof binding === 'undefined' ? 'undefined' : _typeof(binding)) === 'object') {\n    binding = (0, _clone2.default)(binding, false);\n  }\n  if (typeof binding.key === 'string') {\n    if (Keyboard.keys[binding.key.toUpperCase()] != null) {\n      binding.key = Keyboard.keys[binding.key.toUpperCase()];\n    } else if (binding.key.length === 1) {\n      binding.key = binding.key.toUpperCase().charCodeAt(0);\n    } else {\n      return null;\n    }\n  }\n  if (binding.shortKey) {\n    binding[SHORTKEY] = binding.shortKey;\n    delete binding.shortKey;\n  }\n  return binding;\n}\n\nexports.default = Keyboard;\nexports.SHORTKEY = SHORTKEY;\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Cursor = function (_Parchment$Embed) {\n  _inherits(Cursor, _Parchment$Embed);\n\n  _createClass(Cursor, null, [{\n    key: 'value',\n    value: function value() {\n      return undefined;\n    }\n  }]);\n\n  function Cursor(domNode, selection) {\n    _classCallCheck(this, Cursor);\n\n    var _this = _possibleConstructorReturn(this, (Cursor.__proto__ || Object.getPrototypeOf(Cursor)).call(this, domNode));\n\n    _this.selection = selection;\n    _this.textNode = document.createTextNode(Cursor.CONTENTS);\n    _this.domNode.appendChild(_this.textNode);\n    _this._length = 0;\n    return _this;\n  }\n\n  _createClass(Cursor, [{\n    key: 'detach',\n    value: function detach() {\n      // super.detach() will also clear domNode.__blot\n      if (this.parent != null) this.parent.removeChild(this);\n    }\n  }, {\n    key: 'format',\n    value: function format(name, value) {\n      if (this._length !== 0) {\n        return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'format', this).call(this, name, value);\n      }\n      var target = this,\n          index = 0;\n      while (target != null && target.statics.scope !== _parchment2.default.Scope.BLOCK_BLOT) {\n        index += target.offset(target.parent);\n        target = target.parent;\n      }\n      if (target != null) {\n        this._length = Cursor.CONTENTS.length;\n        target.optimize();\n        target.formatAt(index, Cursor.CONTENTS.length, name, value);\n        this._length = 0;\n      }\n    }\n  }, {\n    key: 'index',\n    value: function index(node, offset) {\n      if (node === this.textNode) return 0;\n      return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'index', this).call(this, node, offset);\n    }\n  }, {\n    key: 'length',\n    value: function length() {\n      return this._length;\n    }\n  }, {\n    key: 'position',\n    value: function position() {\n      return [this.textNode, this.textNode.data.length];\n    }\n  }, {\n    key: 'remove',\n    value: function remove() {\n      _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'remove', this).call(this);\n      this.parent = null;\n    }\n  }, {\n    key: 'restore',\n    value: function restore() {\n      if (this.selection.composing || this.parent == null) return;\n      var textNode = this.textNode;\n      var range = this.selection.getNativeRange();\n      var restoreText = void 0,\n          start = void 0,\n          end = void 0;\n      if (range != null && range.start.node === textNode && range.end.node === textNode) {\n        var _ref = [textNode, range.start.offset, range.end.offset];\n        restoreText = _ref[0];\n        start = _ref[1];\n        end = _ref[2];\n      }\n      // Link format will insert text outside of anchor tag\n      while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {\n        this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);\n      }\n      if (this.textNode.data !== Cursor.CONTENTS) {\n        var text = this.textNode.data.split(Cursor.CONTENTS).join('');\n        if (this.next instanceof _text2.default) {\n          restoreText = this.next.domNode;\n          this.next.insertAt(0, text);\n          this.textNode.data = Cursor.CONTENTS;\n        } else {\n          this.textNode.data = text;\n          this.parent.insertBefore(_parchment2.default.create(this.textNode), this);\n          this.textNode = document.createTextNode(Cursor.CONTENTS);\n          this.domNode.appendChild(this.textNode);\n        }\n      }\n      this.remove();\n      if (start != null) {\n        var _map = [start, end].map(function (offset) {\n          return Math.max(0, Math.min(restoreText.data.length, offset - 1));\n        });\n\n        var _map2 = _slicedToArray(_map, 2);\n\n        start = _map2[0];\n        end = _map2[1];\n\n        return {\n          startNode: restoreText,\n          startOffset: start,\n          endNode: restoreText,\n          endOffset: end\n        };\n      }\n    }\n  }, {\n    key: 'update',\n    value: function update(mutations, context) {\n      var _this2 = this;\n\n      if (mutations.some(function (mutation) {\n        return mutation.type === 'characterData' && mutation.target === _this2.textNode;\n      })) {\n        var range = this.restore();\n        if (range) context.range = range;\n      }\n    }\n  }, {\n    key: 'value',\n    value: function value() {\n      return '';\n    }\n  }]);\n\n  return Cursor;\n}(_parchment2.default.Embed);\n\nCursor.blotName = 'cursor';\nCursor.className = 'ql-cursor';\nCursor.tagName = 'span';\nCursor.CONTENTS = '\\uFEFF'; // Zero width no break space\n\n\nexports.default = Cursor;\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Container = function (_Parchment$Container) {\n  _inherits(Container, _Parchment$Container);\n\n  function Container() {\n    _classCallCheck(this, Container);\n\n    return _possibleConstructorReturn(this, (Container.__proto__ || Object.getPrototypeOf(Container)).apply(this, arguments));\n  }\n\n  return Container;\n}(_parchment2.default.Container);\n\nContainer.allowedChildren = [_block2.default, _block.BlockEmbed, Container];\n\nexports.default = Container;\n\n/***/ }),\n/* 26 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.ColorStyle = exports.ColorClass = exports.ColorAttributor = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ColorAttributor = function (_Parchment$Attributor) {\n  _inherits(ColorAttributor, _Parchment$Attributor);\n\n  function ColorAttributor() {\n    _classCallCheck(this, ColorAttributor);\n\n    return _possibleConstructorReturn(this, (ColorAttributor.__proto__ || Object.getPrototypeOf(ColorAttributor)).apply(this, arguments));\n  }\n\n  _createClass(ColorAttributor, [{\n    key: 'value',\n    value: function value(domNode) {\n      var value = _get(ColorAttributor.prototype.__proto__ || Object.getPrototypeOf(ColorAttributor.prototype), 'value', this).call(this, domNode);\n      if (!value.startsWith('rgb(')) return value;\n      value = value.replace(/^[^\\d]+/, '').replace(/[^\\d]+$/, '');\n      return '#' + value.split(',').map(function (component) {\n        return ('00' + parseInt(component).toString(16)).slice(-2);\n      }).join('');\n    }\n  }]);\n\n  return ColorAttributor;\n}(_parchment2.default.Attributor.Style);\n\nvar ColorClass = new _parchment2.default.Attributor.Class('color', 'ql-color', {\n  scope: _parchment2.default.Scope.INLINE\n});\nvar ColorStyle = new ColorAttributor('color', 'color', {\n  scope: _parchment2.default.Scope.INLINE\n});\n\nexports.ColorAttributor = ColorAttributor;\nexports.ColorClass = ColorClass;\nexports.ColorStyle = ColorStyle;\n\n/***/ }),\n/* 27 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.sanitize = exports.default = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Link = function (_Inline) {\n  _inherits(Link, _Inline);\n\n  function Link() {\n    _classCallCheck(this, Link);\n\n    return _possibleConstructorReturn(this, (Link.__proto__ || Object.getPrototypeOf(Link)).apply(this, arguments));\n  }\n\n  _createClass(Link, [{\n    key: 'format',\n    value: function format(name, value) {\n      if (name !== this.statics.blotName || !value) return _get(Link.prototype.__proto__ || Object.getPrototypeOf(Link.prototype), 'format', this).call(this, name, value);\n      value = this.constructor.sanitize(value);\n      this.domNode.setAttribute('href', value);\n    }\n  }], [{\n    key: 'create',\n    value: function create(value) {\n      var node = _get(Link.__proto__ || Object.getPrototypeOf(Link), 'create', this).call(this, value);\n      value = this.sanitize(value);\n      node.setAttribute('href', value);\n      node.setAttribute('rel', 'noopener noreferrer');\n      node.setAttribute('target', '_blank');\n      return node;\n    }\n  }, {\n    key: 'formats',\n    value: function formats(domNode) {\n      return domNode.getAttribute('href');\n    }\n  }, {\n    key: 'sanitize',\n    value: function sanitize(url) {\n      return _sanitize(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL;\n    }\n  }]);\n\n  return Link;\n}(_inline2.default);\n\nLink.blotName = 'link';\nLink.tagName = 'A';\nLink.SANITIZED_URL = 'about:blank';\nLink.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel'];\n\nfunction _sanitize(url, protocols) {\n  var anchor = document.createElement('a');\n  anchor.href = url;\n  var protocol = anchor.href.slice(0, anchor.href.indexOf(':'));\n  return protocols.indexOf(protocol) > -1;\n}\n\nexports.default = Link;\nexports.sanitize = _sanitize;\n\n/***/ }),\n/* 28 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _keyboard = __webpack_require__(23);\n\nvar _keyboard2 = _interopRequireDefault(_keyboard);\n\nvar _dropdown = __webpack_require__(107);\n\nvar _dropdown2 = _interopRequireDefault(_dropdown);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar optionsCounter = 0;\n\nfunction toggleAriaAttribute(element, attribute) {\n  element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true'));\n}\n\nvar Picker = function () {\n  function Picker(select) {\n    var _this = this;\n\n    _classCallCheck(this, Picker);\n\n    this.select = select;\n    this.container = document.createElement('span');\n    this.buildPicker();\n    this.select.style.display = 'none';\n    this.select.parentNode.insertBefore(this.container, this.select);\n\n    this.label.addEventListener('mousedown', function () {\n      _this.togglePicker();\n    });\n    this.label.addEventListener('keydown', function (event) {\n      switch (event.keyCode) {\n        // Allows the \"Enter\" key to open the picker\n        case _keyboard2.default.keys.ENTER:\n          _this.togglePicker();\n          break;\n\n        // Allows the \"Escape\" key to close the picker\n        case _keyboard2.default.keys.ESCAPE:\n          _this.escape();\n          event.preventDefault();\n          break;\n        default:\n      }\n    });\n    this.select.addEventListener('change', this.update.bind(this));\n  }\n\n  _createClass(Picker, [{\n    key: 'togglePicker',\n    value: function togglePicker() {\n      this.container.classList.toggle('ql-expanded');\n      // Toggle aria-expanded and aria-hidden to make the picker accessible\n      toggleAriaAttribute(this.label, 'aria-expanded');\n      toggleAriaAttribute(this.options, 'aria-hidden');\n    }\n  }, {\n    key: 'buildItem',\n    value: function buildItem(option) {\n      var _this2 = this;\n\n      var item = document.createElement('span');\n      item.tabIndex = '0';\n      item.setAttribute('role', 'button');\n\n      item.classList.add('ql-picker-item');\n      if (option.hasAttribute('value')) {\n        item.setAttribute('data-value', option.getAttribute('value'));\n      }\n      if (option.textContent) {\n        item.setAttribute('data-label', option.textContent);\n      }\n      item.addEventListener('click', function () {\n        _this2.selectItem(item, true);\n      });\n      item.addEventListener('keydown', function (event) {\n        switch (event.keyCode) {\n          // Allows the \"Enter\" key to select an item\n          case _keyboard2.default.keys.ENTER:\n            _this2.selectItem(item, true);\n            event.preventDefault();\n            break;\n\n          // Allows the \"Escape\" key to close the picker\n          case _keyboard2.default.keys.ESCAPE:\n            _this2.escape();\n            event.preventDefault();\n            break;\n          default:\n        }\n      });\n\n      return item;\n    }\n  }, {\n    key: 'buildLabel',\n    value: function buildLabel() {\n      var label = document.createElement('span');\n      label.classList.add('ql-picker-label');\n      label.innerHTML = _dropdown2.default;\n      label.tabIndex = '0';\n      label.setAttribute('role', 'button');\n      label.setAttribute('aria-expanded', 'false');\n      this.container.appendChild(label);\n      return label;\n    }\n  }, {\n    key: 'buildOptions',\n    value: function buildOptions() {\n      var _this3 = this;\n\n      var options = document.createElement('span');\n      options.classList.add('ql-picker-options');\n\n      // Don't want screen readers to read this until options are visible\n      options.setAttribute('aria-hidden', 'true');\n      options.tabIndex = '-1';\n\n      // Need a unique id for aria-controls\n      options.id = 'ql-picker-options-' + optionsCounter;\n      optionsCounter += 1;\n      this.label.setAttribute('aria-controls', options.id);\n\n      this.options = options;\n\n      [].slice.call(this.select.options).forEach(function (option) {\n        var item = _this3.buildItem(option);\n        options.appendChild(item);\n        if (option.selected === true) {\n          _this3.selectItem(item);\n        }\n      });\n      this.container.appendChild(options);\n    }\n  }, {\n    key: 'buildPicker',\n    value: function buildPicker() {\n      var _this4 = this;\n\n      [].slice.call(this.select.attributes).forEach(function (item) {\n        _this4.container.setAttribute(item.name, item.value);\n      });\n      this.container.classList.add('ql-picker');\n      this.label = this.buildLabel();\n      this.buildOptions();\n    }\n  }, {\n    key: 'escape',\n    value: function escape() {\n      var _this5 = this;\n\n      // Close menu and return focus to trigger label\n      this.close();\n      // Need setTimeout for accessibility to ensure that the browser executes\n      // focus on the next process thread and after any DOM content changes\n      setTimeout(function () {\n        return _this5.label.focus();\n      }, 1);\n    }\n  }, {\n    key: 'close',\n    value: function close() {\n      this.container.classList.remove('ql-expanded');\n      this.label.setAttribute('aria-expanded', 'false');\n      this.options.setAttribute('aria-hidden', 'true');\n    }\n  }, {\n    key: 'selectItem',\n    value: function selectItem(item) {\n      var trigger = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n      var selected = this.container.querySelector('.ql-selected');\n      if (item === selected) return;\n      if (selected != null) {\n        selected.classList.remove('ql-selected');\n      }\n      if (item == null) return;\n      item.classList.add('ql-selected');\n      this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item);\n      if (item.hasAttribute('data-value')) {\n        this.label.setAttribute('data-value', item.getAttribute('data-value'));\n      } else {\n        this.label.removeAttribute('data-value');\n      }\n      if (item.hasAttribute('data-label')) {\n        this.label.setAttribute('data-label', item.getAttribute('data-label'));\n      } else {\n        this.label.removeAttribute('data-label');\n      }\n      if (trigger) {\n        if (typeof Event === 'function') {\n          this.select.dispatchEvent(new Event('change'));\n        } else if ((typeof Event === 'undefined' ? 'undefined' : _typeof(Event)) === 'object') {\n          // IE11\n          var event = document.createEvent('Event');\n          event.initEvent('change', true, true);\n          this.select.dispatchEvent(event);\n        }\n        this.close();\n      }\n    }\n  }, {\n    key: 'update',\n    value: function update() {\n      var option = void 0;\n      if (this.select.selectedIndex > -1) {\n        var item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex];\n        option = this.select.options[this.select.selectedIndex];\n        this.selectItem(item);\n      } else {\n        this.selectItem(null);\n      }\n      var isActive = option != null && option !== this.select.querySelector('option[selected]');\n      this.label.classList.toggle('ql-active', isActive);\n    }\n  }]);\n\n  return Picker;\n}();\n\nexports.default = Picker;\n\n/***/ }),\n/* 29 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(16);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _container = __webpack_require__(25);\n\nvar _container2 = _interopRequireDefault(_container);\n\nvar _cursor = __webpack_require__(24);\n\nvar _cursor2 = _interopRequireDefault(_cursor);\n\nvar _embed = __webpack_require__(35);\n\nvar _embed2 = _interopRequireDefault(_embed);\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _scroll = __webpack_require__(22);\n\nvar _scroll2 = _interopRequireDefault(_scroll);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nvar _clipboard = __webpack_require__(55);\n\nvar _clipboard2 = _interopRequireDefault(_clipboard);\n\nvar _history = __webpack_require__(42);\n\nvar _history2 = _interopRequireDefault(_history);\n\nvar _keyboard = __webpack_require__(23);\n\nvar _keyboard2 = _interopRequireDefault(_keyboard);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_quill2.default.register({\n  'blots/block': _block2.default,\n  'blots/block/embed': _block.BlockEmbed,\n  'blots/break': _break2.default,\n  'blots/container': _container2.default,\n  'blots/cursor': _cursor2.default,\n  'blots/embed': _embed2.default,\n  'blots/inline': _inline2.default,\n  'blots/scroll': _scroll2.default,\n  'blots/text': _text2.default,\n\n  'modules/clipboard': _clipboard2.default,\n  'modules/history': _history2.default,\n  'modules/keyboard': _keyboard2.default\n});\n\n_parchment2.default.register(_block2.default, _break2.default, _cursor2.default, _inline2.default, _scroll2.default, _text2.default);\n\nexports.default = _quill2.default;\n\n/***/ }),\n/* 30 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = __webpack_require__(1);\nvar ShadowBlot = /** @class */ (function () {\n    function ShadowBlot(domNode) {\n        this.domNode = domNode;\n        // @ts-ignore\n        this.domNode[Registry.DATA_KEY] = { blot: this };\n    }\n    Object.defineProperty(ShadowBlot.prototype, \"statics\", {\n        // Hack for accessing inherited static methods\n        get: function () {\n            return this.constructor;\n        },\n        enumerable: true,\n        configurable: true\n    });\n    ShadowBlot.create = function (value) {\n        if (this.tagName == null) {\n            throw new Registry.ParchmentError('Blot definition missing tagName');\n        }\n        var node;\n        if (Array.isArray(this.tagName)) {\n            if (typeof value === 'string') {\n                value = value.toUpperCase();\n                if (parseInt(value).toString() === value) {\n                    value = parseInt(value);\n                }\n            }\n            if (typeof value === 'number') {\n                node = document.createElement(this.tagName[value - 1]);\n            }\n            else if (this.tagName.indexOf(value) > -1) {\n                node = document.createElement(value);\n            }\n            else {\n                node = document.createElement(this.tagName[0]);\n            }\n        }\n        else {\n            node = document.createElement(this.tagName);\n        }\n        if (this.className) {\n            node.classList.add(this.className);\n        }\n        return node;\n    };\n    ShadowBlot.prototype.attach = function () {\n        if (this.parent != null) {\n            this.scroll = this.parent.scroll;\n        }\n    };\n    ShadowBlot.prototype.clone = function () {\n        var domNode = this.domNode.cloneNode(false);\n        return Registry.create(domNode);\n    };\n    ShadowBlot.prototype.detach = function () {\n        if (this.parent != null)\n            this.parent.removeChild(this);\n        // @ts-ignore\n        delete this.domNode[Registry.DATA_KEY];\n    };\n    ShadowBlot.prototype.deleteAt = function (index, length) {\n        var blot = this.isolate(index, length);\n        blot.remove();\n    };\n    ShadowBlot.prototype.formatAt = function (index, length, name, value) {\n        var blot = this.isolate(index, length);\n        if (Registry.query(name, Registry.Scope.BLOT) != null && value) {\n            blot.wrap(name, value);\n        }\n        else if (Registry.query(name, Registry.Scope.ATTRIBUTE) != null) {\n            var parent = Registry.create(this.statics.scope);\n            blot.wrap(parent);\n            parent.format(name, value);\n        }\n    };\n    ShadowBlot.prototype.insertAt = function (index, value, def) {\n        var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n        var ref = this.split(index);\n        this.parent.insertBefore(blot, ref);\n    };\n    ShadowBlot.prototype.insertInto = function (parentBlot, refBlot) {\n        if (refBlot === void 0) { refBlot = null; }\n        if (this.parent != null) {\n            this.parent.children.remove(this);\n        }\n        var refDomNode = null;\n        parentBlot.children.insertBefore(this, refBlot);\n        if (refBlot != null) {\n            refDomNode = refBlot.domNode;\n        }\n        if (this.domNode.parentNode != parentBlot.domNode ||\n            this.domNode.nextSibling != refDomNode) {\n            parentBlot.domNode.insertBefore(this.domNode, refDomNode);\n        }\n        this.parent = parentBlot;\n        this.attach();\n    };\n    ShadowBlot.prototype.isolate = function (index, length) {\n        var target = this.split(index);\n        target.split(length);\n        return target;\n    };\n    ShadowBlot.prototype.length = function () {\n        return 1;\n    };\n    ShadowBlot.prototype.offset = function (root) {\n        if (root === void 0) { root = this.parent; }\n        if (this.parent == null || this == root)\n            return 0;\n        return this.parent.children.offset(this) + this.parent.offset(root);\n    };\n    ShadowBlot.prototype.optimize = function (context) {\n        // TODO clean up once we use WeakMap\n        // @ts-ignore\n        if (this.domNode[Registry.DATA_KEY] != null) {\n            // @ts-ignore\n            delete this.domNode[Registry.DATA_KEY].mutations;\n        }\n    };\n    ShadowBlot.prototype.remove = function () {\n        if (this.domNode.parentNode != null) {\n            this.domNode.parentNode.removeChild(this.domNode);\n        }\n        this.detach();\n    };\n    ShadowBlot.prototype.replace = function (target) {\n        if (target.parent == null)\n            return;\n        target.parent.insertBefore(this, target.next);\n        target.remove();\n    };\n    ShadowBlot.prototype.replaceWith = function (name, value) {\n        var replacement = typeof name === 'string' ? Registry.create(name, value) : name;\n        replacement.replace(this);\n        return replacement;\n    };\n    ShadowBlot.prototype.split = function (index, force) {\n        return index === 0 ? this : this.next;\n    };\n    ShadowBlot.prototype.update = function (mutations, context) {\n        // Nothing to do by default\n    };\n    ShadowBlot.prototype.wrap = function (name, value) {\n        var wrapper = typeof name === 'string' ? Registry.create(name, value) : name;\n        if (this.parent != null) {\n            this.parent.insertBefore(wrapper, this.next);\n        }\n        wrapper.appendChild(this);\n        return wrapper;\n    };\n    ShadowBlot.blotName = 'abstract';\n    return ShadowBlot;\n}());\nexports.default = ShadowBlot;\n\n\n/***/ }),\n/* 31 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nvar class_1 = __webpack_require__(32);\nvar style_1 = __webpack_require__(33);\nvar Registry = __webpack_require__(1);\nvar AttributorStore = /** @class */ (function () {\n    function AttributorStore(domNode) {\n        this.attributes = {};\n        this.domNode = domNode;\n        this.build();\n    }\n    AttributorStore.prototype.attribute = function (attribute, value) {\n        // verb\n        if (value) {\n            if (attribute.add(this.domNode, value)) {\n                if (attribute.value(this.domNode) != null) {\n                    this.attributes[attribute.attrName] = attribute;\n                }\n                else {\n                    delete this.attributes[attribute.attrName];\n                }\n            }\n        }\n        else {\n            attribute.remove(this.domNode);\n            delete this.attributes[attribute.attrName];\n        }\n    };\n    AttributorStore.prototype.build = function () {\n        var _this = this;\n        this.attributes = {};\n        var attributes = attributor_1.default.keys(this.domNode);\n        var classes = class_1.default.keys(this.domNode);\n        var styles = style_1.default.keys(this.domNode);\n        attributes\n            .concat(classes)\n            .concat(styles)\n            .forEach(function (name) {\n            var attr = Registry.query(name, Registry.Scope.ATTRIBUTE);\n            if (attr instanceof attributor_1.default) {\n                _this.attributes[attr.attrName] = attr;\n            }\n        });\n    };\n    AttributorStore.prototype.copy = function (target) {\n        var _this = this;\n        Object.keys(this.attributes).forEach(function (key) {\n            var value = _this.attributes[key].value(_this.domNode);\n            target.format(key, value);\n        });\n    };\n    AttributorStore.prototype.move = function (target) {\n        var _this = this;\n        this.copy(target);\n        Object.keys(this.attributes).forEach(function (key) {\n            _this.attributes[key].remove(_this.domNode);\n        });\n        this.attributes = {};\n    };\n    AttributorStore.prototype.values = function () {\n        var _this = this;\n        return Object.keys(this.attributes).reduce(function (attributes, name) {\n            attributes[name] = _this.attributes[name].value(_this.domNode);\n            return attributes;\n        }, {});\n    };\n    return AttributorStore;\n}());\nexports.default = AttributorStore;\n\n\n/***/ }),\n/* 32 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nfunction match(node, prefix) {\n    var className = node.getAttribute('class') || '';\n    return className.split(/\\s+/).filter(function (name) {\n        return name.indexOf(prefix + \"-\") === 0;\n    });\n}\nvar ClassAttributor = /** @class */ (function (_super) {\n    __extends(ClassAttributor, _super);\n    function ClassAttributor() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    ClassAttributor.keys = function (node) {\n        return (node.getAttribute('class') || '').split(/\\s+/).map(function (name) {\n            return name\n                .split('-')\n                .slice(0, -1)\n                .join('-');\n        });\n    };\n    ClassAttributor.prototype.add = function (node, value) {\n        if (!this.canAdd(node, value))\n            return false;\n        this.remove(node);\n        node.classList.add(this.keyName + \"-\" + value);\n        return true;\n    };\n    ClassAttributor.prototype.remove = function (node) {\n        var matches = match(node, this.keyName);\n        matches.forEach(function (name) {\n            node.classList.remove(name);\n        });\n        if (node.classList.length === 0) {\n            node.removeAttribute('class');\n        }\n    };\n    ClassAttributor.prototype.value = function (node) {\n        var result = match(node, this.keyName)[0] || '';\n        var value = result.slice(this.keyName.length + 1); // +1 for hyphen\n        return this.canAdd(node, value) ? value : '';\n    };\n    return ClassAttributor;\n}(attributor_1.default));\nexports.default = ClassAttributor;\n\n\n/***/ }),\n/* 33 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(12);\nfunction camelize(name) {\n    var parts = name.split('-');\n    var rest = parts\n        .slice(1)\n        .map(function (part) {\n        return part[0].toUpperCase() + part.slice(1);\n    })\n        .join('');\n    return parts[0] + rest;\n}\nvar StyleAttributor = /** @class */ (function (_super) {\n    __extends(StyleAttributor, _super);\n    function StyleAttributor() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    StyleAttributor.keys = function (node) {\n        return (node.getAttribute('style') || '').split(';').map(function (value) {\n            var arr = value.split(':');\n            return arr[0].trim();\n        });\n    };\n    StyleAttributor.prototype.add = function (node, value) {\n        if (!this.canAdd(node, value))\n            return false;\n        // @ts-ignore\n        node.style[camelize(this.keyName)] = value;\n        return true;\n    };\n    StyleAttributor.prototype.remove = function (node) {\n        // @ts-ignore\n        node.style[camelize(this.keyName)] = '';\n        if (!node.getAttribute('style')) {\n            node.removeAttribute('style');\n        }\n    };\n    StyleAttributor.prototype.value = function (node) {\n        // @ts-ignore\n        var value = node.style[camelize(this.keyName)];\n        return this.canAdd(node, value) ? value : '';\n    };\n    return StyleAttributor;\n}(attributor_1.default));\nexports.default = StyleAttributor;\n\n\n/***/ }),\n/* 34 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Theme = function () {\n  function Theme(quill, options) {\n    _classCallCheck(this, Theme);\n\n    this.quill = quill;\n    this.options = options;\n    this.modules = {};\n  }\n\n  _createClass(Theme, [{\n    key: 'init',\n    value: function init() {\n      var _this = this;\n\n      Object.keys(this.options.modules).forEach(function (name) {\n        if (_this.modules[name] == null) {\n          _this.addModule(name);\n        }\n      });\n    }\n  }, {\n    key: 'addModule',\n    value: function addModule(name) {\n      var moduleClass = this.quill.constructor.import('modules/' + name);\n      this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {});\n      return this.modules[name];\n    }\n  }]);\n\n  return Theme;\n}();\n\nTheme.DEFAULTS = {\n  modules: {}\n};\nTheme.themes = {\n  'default': Theme\n};\n\nexports.default = Theme;\n\n/***/ }),\n/* 35 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _text = __webpack_require__(7);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar GUARD_TEXT = '\\uFEFF';\n\nvar Embed = function (_Parchment$Embed) {\n  _inherits(Embed, _Parchment$Embed);\n\n  function Embed(node) {\n    _classCallCheck(this, Embed);\n\n    var _this = _possibleConstructorReturn(this, (Embed.__proto__ || Object.getPrototypeOf(Embed)).call(this, node));\n\n    _this.contentNode = document.createElement('span');\n    _this.contentNode.setAttribute('contenteditable', false);\n    [].slice.call(_this.domNode.childNodes).forEach(function (childNode) {\n      _this.contentNode.appendChild(childNode);\n    });\n    _this.leftGuard = document.createTextNode(GUARD_TEXT);\n    _this.rightGuard = document.createTextNode(GUARD_TEXT);\n    _this.domNode.appendChild(_this.leftGuard);\n    _this.domNode.appendChild(_this.contentNode);\n    _this.domNode.appendChild(_this.rightGuard);\n    return _this;\n  }\n\n  _createClass(Embed, [{\n    key: 'index',\n    value: function index(node, offset) {\n      if (node === this.leftGuard) return 0;\n      if (node === this.rightGuard) return 1;\n      return _get(Embed.prototype.__proto__ || Object.getPrototypeOf(Embed.prototype), 'index', this).call(this, node, offset);\n    }\n  }, {\n    key: 'restore',\n    value: function restore(node) {\n      var range = void 0,\n          textNode = void 0;\n      var text = node.data.split(GUARD_TEXT).join('');\n      if (node === this.leftGuard) {\n        if (this.prev instanceof _text2.default) {\n          var prevLength = this.prev.length();\n          this.prev.insertAt(prevLength, text);\n          range = {\n            startNode: this.prev.domNode,\n            startOffset: prevLength + text.length\n          };\n        } else {\n          textNode = document.createTextNode(text);\n          this.parent.insertBefore(_parchment2.default.create(textNode), this);\n          range = {\n            startNode: textNode,\n            startOffset: text.length\n          };\n        }\n      } else if (node === this.rightGuard) {\n        if (this.next instanceof _text2.default) {\n          this.next.insertAt(0, text);\n          range = {\n            startNode: this.next.domNode,\n            startOffset: text.length\n          };\n        } else {\n          textNode = document.createTextNode(text);\n          this.parent.insertBefore(_parchment2.default.create(textNode), this.next);\n          range = {\n            startNode: textNode,\n            startOffset: text.length\n          };\n        }\n      }\n      node.data = GUARD_TEXT;\n      return range;\n    }\n  }, {\n    key: 'update',\n    value: function update(mutations, context) {\n      var _this2 = this;\n\n      mutations.forEach(function (mutation) {\n        if (mutation.type === 'characterData' && (mutation.target === _this2.leftGuard || mutation.target === _this2.rightGuard)) {\n          var range = _this2.restore(mutation.target);\n          if (range) context.range = range;\n        }\n      });\n    }\n  }]);\n\n  return Embed;\n}(_parchment2.default.Embed);\n\nexports.default = Embed;\n\n/***/ }),\n/* 36 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.AlignStyle = exports.AlignClass = exports.AlignAttribute = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar config = {\n  scope: _parchment2.default.Scope.BLOCK,\n  whitelist: ['right', 'center', 'justify']\n};\n\nvar AlignAttribute = new _parchment2.default.Attributor.Attribute('align', 'align', config);\nvar AlignClass = new _parchment2.default.Attributor.Class('align', 'ql-align', config);\nvar AlignStyle = new _parchment2.default.Attributor.Style('align', 'text-align', config);\n\nexports.AlignAttribute = AlignAttribute;\nexports.AlignClass = AlignClass;\nexports.AlignStyle = AlignStyle;\n\n/***/ }),\n/* 37 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.BackgroundStyle = exports.BackgroundClass = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _color = __webpack_require__(26);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar BackgroundClass = new _parchment2.default.Attributor.Class('background', 'ql-bg', {\n  scope: _parchment2.default.Scope.INLINE\n});\nvar BackgroundStyle = new _color.ColorAttributor('background', 'background-color', {\n  scope: _parchment2.default.Scope.INLINE\n});\n\nexports.BackgroundClass = BackgroundClass;\nexports.BackgroundStyle = BackgroundStyle;\n\n/***/ }),\n/* 38 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.DirectionStyle = exports.DirectionClass = exports.DirectionAttribute = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar config = {\n  scope: _parchment2.default.Scope.BLOCK,\n  whitelist: ['rtl']\n};\n\nvar DirectionAttribute = new _parchment2.default.Attributor.Attribute('direction', 'dir', config);\nvar DirectionClass = new _parchment2.default.Attributor.Class('direction', 'ql-direction', config);\nvar DirectionStyle = new _parchment2.default.Attributor.Style('direction', 'direction', config);\n\nexports.DirectionAttribute = DirectionAttribute;\nexports.DirectionClass = DirectionClass;\nexports.DirectionStyle = DirectionStyle;\n\n/***/ }),\n/* 39 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.FontClass = exports.FontStyle = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar config = {\n  scope: _parchment2.default.Scope.INLINE,\n  whitelist: ['serif', 'monospace']\n};\n\nvar FontClass = new _parchment2.default.Attributor.Class('font', 'ql-font', config);\n\nvar FontStyleAttributor = function (_Parchment$Attributor) {\n  _inherits(FontStyleAttributor, _Parchment$Attributor);\n\n  function FontStyleAttributor() {\n    _classCallCheck(this, FontStyleAttributor);\n\n    return _possibleConstructorReturn(this, (FontStyleAttributor.__proto__ || Object.getPrototypeOf(FontStyleAttributor)).apply(this, arguments));\n  }\n\n  _createClass(FontStyleAttributor, [{\n    key: 'value',\n    value: function value(node) {\n      return _get(FontStyleAttributor.prototype.__proto__ || Object.getPrototypeOf(FontStyleAttributor.prototype), 'value', this).call(this, node).replace(/[\"']/g, '');\n    }\n  }]);\n\n  return FontStyleAttributor;\n}(_parchment2.default.Attributor.Style);\n\nvar FontStyle = new FontStyleAttributor('font', 'font-family', config);\n\nexports.FontStyle = FontStyle;\nexports.FontClass = FontClass;\n\n/***/ }),\n/* 40 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.SizeStyle = exports.SizeClass = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar SizeClass = new _parchment2.default.Attributor.Class('size', 'ql-size', {\n  scope: _parchment2.default.Scope.INLINE,\n  whitelist: ['small', 'large', 'huge']\n});\nvar SizeStyle = new _parchment2.default.Attributor.Style('size', 'font-size', {\n  scope: _parchment2.default.Scope.INLINE,\n  whitelist: ['10px', '18px', '32px']\n});\n\nexports.SizeClass = SizeClass;\nexports.SizeStyle = SizeStyle;\n\n/***/ }),\n/* 41 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = {\n  'align': {\n    '': __webpack_require__(76),\n    'center': __webpack_require__(77),\n    'right': __webpack_require__(78),\n    'justify': __webpack_require__(79)\n  },\n  'background': __webpack_require__(80),\n  'blockquote': __webpack_require__(81),\n  'bold': __webpack_require__(82),\n  'clean': __webpack_require__(83),\n  'code': __webpack_require__(58),\n  'code-block': __webpack_require__(58),\n  'color': __webpack_require__(84),\n  'direction': {\n    '': __webpack_require__(85),\n    'rtl': __webpack_require__(86)\n  },\n  'float': {\n    'center': __webpack_require__(87),\n    'full': __webpack_require__(88),\n    'left': __webpack_require__(89),\n    'right': __webpack_require__(90)\n  },\n  'formula': __webpack_require__(91),\n  'header': {\n    '1': __webpack_require__(92),\n    '2': __webpack_require__(93)\n  },\n  'italic': __webpack_require__(94),\n  'image': __webpack_require__(95),\n  'indent': {\n    '+1': __webpack_require__(96),\n    '-1': __webpack_require__(97)\n  },\n  'link': __webpack_require__(98),\n  'list': {\n    'ordered': __webpack_require__(99),\n    'bullet': __webpack_require__(100),\n    'check': __webpack_require__(101)\n  },\n  'script': {\n    'sub': __webpack_require__(102),\n    'super': __webpack_require__(103)\n  },\n  'strike': __webpack_require__(104),\n  'underline': __webpack_require__(105),\n  'video': __webpack_require__(106)\n};\n\n/***/ }),\n/* 42 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.getLastChangeIndex = exports.default = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar History = function (_Module) {\n  _inherits(History, _Module);\n\n  function History(quill, options) {\n    _classCallCheck(this, History);\n\n    var _this = _possibleConstructorReturn(this, (History.__proto__ || Object.getPrototypeOf(History)).call(this, quill, options));\n\n    _this.lastRecorded = 0;\n    _this.ignoreChange = false;\n    _this.clear();\n    _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (eventName, delta, oldDelta, source) {\n      if (eventName !== _quill2.default.events.TEXT_CHANGE || _this.ignoreChange) return;\n      if (!_this.options.userOnly || source === _quill2.default.sources.USER) {\n        _this.record(delta, oldDelta);\n      } else {\n        _this.transform(delta);\n      }\n    });\n    _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, _this.undo.bind(_this));\n    _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, _this.redo.bind(_this));\n    if (/Win/i.test(navigator.platform)) {\n      _this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, _this.redo.bind(_this));\n    }\n    return _this;\n  }\n\n  _createClass(History, [{\n    key: 'change',\n    value: function change(source, dest) {\n      if (this.stack[source].length === 0) return;\n      var delta = this.stack[source].pop();\n      this.stack[dest].push(delta);\n      this.lastRecorded = 0;\n      this.ignoreChange = true;\n      this.quill.updateContents(delta[source], _quill2.default.sources.USER);\n      this.ignoreChange = false;\n      var index = getLastChangeIndex(delta[source]);\n      this.quill.setSelection(index);\n    }\n  }, {\n    key: 'clear',\n    value: function clear() {\n      this.stack = { undo: [], redo: [] };\n    }\n  }, {\n    key: 'cutoff',\n    value: function cutoff() {\n      this.lastRecorded = 0;\n    }\n  }, {\n    key: 'record',\n    value: function record(changeDelta, oldDelta) {\n      if (changeDelta.ops.length === 0) return;\n      this.stack.redo = [];\n      var undoDelta = this.quill.getContents().diff(oldDelta);\n      var timestamp = Date.now();\n      if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) {\n        var delta = this.stack.undo.pop();\n        undoDelta = undoDelta.compose(delta.undo);\n        changeDelta = delta.redo.compose(changeDelta);\n      } else {\n        this.lastRecorded = timestamp;\n      }\n      this.stack.undo.push({\n        redo: changeDelta,\n        undo: undoDelta\n      });\n      if (this.stack.undo.length > this.options.maxStack) {\n        this.stack.undo.shift();\n      }\n    }\n  }, {\n    key: 'redo',\n    value: function redo() {\n      this.change('redo', 'undo');\n    }\n  }, {\n    key: 'transform',\n    value: function transform(delta) {\n      this.stack.undo.forEach(function (change) {\n        change.undo = delta.transform(change.undo, true);\n        change.redo = delta.transform(change.redo, true);\n      });\n      this.stack.redo.forEach(function (change) {\n        change.undo = delta.transform(change.undo, true);\n        change.redo = delta.transform(change.redo, true);\n      });\n    }\n  }, {\n    key: 'undo',\n    value: function undo() {\n      this.change('undo', 'redo');\n    }\n  }]);\n\n  return History;\n}(_module2.default);\n\nHistory.DEFAULTS = {\n  delay: 1000,\n  maxStack: 100,\n  userOnly: false\n};\n\nfunction endsWithNewlineChange(delta) {\n  var lastOp = delta.ops[delta.ops.length - 1];\n  if (lastOp == null) return false;\n  if (lastOp.insert != null) {\n    return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\\n');\n  }\n  if (lastOp.attributes != null) {\n    return Object.keys(lastOp.attributes).some(function (attr) {\n      return _parchment2.default.query(attr, _parchment2.default.Scope.BLOCK) != null;\n    });\n  }\n  return false;\n}\n\nfunction getLastChangeIndex(delta) {\n  var deleteLength = delta.reduce(function (length, op) {\n    length += op.delete || 0;\n    return length;\n  }, 0);\n  var changeIndex = delta.length() - deleteLength;\n  if (endsWithNewlineChange(delta)) {\n    changeIndex -= 1;\n  }\n  return changeIndex;\n}\n\nexports.default = History;\nexports.getLastChangeIndex = getLastChangeIndex;\n\n/***/ }),\n/* 43 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.BaseTooltip = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _emitter = __webpack_require__(8);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _keyboard = __webpack_require__(23);\n\nvar _keyboard2 = _interopRequireDefault(_keyboard);\n\nvar _theme = __webpack_require__(34);\n\nvar _theme2 = _interopRequireDefault(_theme);\n\nvar _colorPicker = __webpack_require__(59);\n\nvar _colorPicker2 = _interopRequireDefault(_colorPicker);\n\nvar _iconPicker = __webpack_require__(60);\n\nvar _iconPicker2 = _interopRequireDefault(_iconPicker);\n\nvar _picker = __webpack_require__(28);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nvar _tooltip = __webpack_require__(61);\n\nvar _tooltip2 = _interopRequireDefault(_tooltip);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ALIGNS = [false, 'center', 'right', 'justify'];\n\nvar COLORS = [\"#000000\", \"#e60000\", \"#ff9900\", \"#ffff00\", \"#008a00\", \"#0066cc\", \"#9933ff\", \"#ffffff\", \"#facccc\", \"#ffebcc\", \"#ffffcc\", \"#cce8cc\", \"#cce0f5\", \"#ebd6ff\", \"#bbbbbb\", \"#f06666\", \"#ffc266\", \"#ffff66\", \"#66b966\", \"#66a3e0\", \"#c285ff\", \"#888888\", \"#a10000\", \"#b26b00\", \"#b2b200\", \"#006100\", \"#0047b2\", \"#6b24b2\", \"#444444\", \"#5c0000\", \"#663d00\", \"#666600\", \"#003700\", \"#002966\", \"#3d1466\"];\n\nvar FONTS = [false, 'serif', 'monospace'];\n\nvar HEADERS = ['1', '2', '3', false];\n\nvar SIZES = ['small', false, 'large', 'huge'];\n\nvar BaseTheme = function (_Theme) {\n  _inherits(BaseTheme, _Theme);\n\n  function BaseTheme(quill, options) {\n    _classCallCheck(this, BaseTheme);\n\n    var _this = _possibleConstructorReturn(this, (BaseTheme.__proto__ || Object.getPrototypeOf(BaseTheme)).call(this, quill, options));\n\n    var listener = function listener(e) {\n      if (!document.body.contains(quill.root)) {\n        return document.body.removeEventListener('click', listener);\n      }\n      if (_this.tooltip != null && !_this.tooltip.root.contains(e.target) && document.activeElement !== _this.tooltip.textbox && !_this.quill.hasFocus()) {\n        _this.tooltip.hide();\n      }\n      if (_this.pickers != null) {\n        _this.pickers.forEach(function (picker) {\n          if (!picker.container.contains(e.target)) {\n            picker.close();\n          }\n        });\n      }\n    };\n    quill.emitter.listenDOM('click', document.body, listener);\n    return _this;\n  }\n\n  _createClass(BaseTheme, [{\n    key: 'addModule',\n    value: function addModule(name) {\n      var module = _get(BaseTheme.prototype.__proto__ || Object.getPrototypeOf(BaseTheme.prototype), 'addModule', this).call(this, name);\n      if (name === 'toolbar') {\n        this.extendToolbar(module);\n      }\n      return module;\n    }\n  }, {\n    key: 'buildButtons',\n    value: function buildButtons(buttons, icons) {\n      buttons.forEach(function (button) {\n        var className = button.getAttribute('class') || '';\n        className.split(/\\s+/).forEach(function (name) {\n          if (!name.startsWith('ql-')) return;\n          name = name.slice('ql-'.length);\n          if (icons[name] == null) return;\n          if (name === 'direction') {\n            button.innerHTML = icons[name][''] + icons[name]['rtl'];\n          } else if (typeof icons[name] === 'string') {\n            button.innerHTML = icons[name];\n          } else {\n            var value = button.value || '';\n            if (value != null && icons[name][value]) {\n              button.innerHTML = icons[name][value];\n            }\n          }\n        });\n      });\n    }\n  }, {\n    key: 'buildPickers',\n    value: function buildPickers(selects, icons) {\n      var _this2 = this;\n\n      this.pickers = selects.map(function (select) {\n        if (select.classList.contains('ql-align')) {\n          if (select.querySelector('option') == null) {\n            fillSelect(select, ALIGNS);\n          }\n          return new _iconPicker2.default(select, icons.align);\n        } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {\n          var format = select.classList.contains('ql-background') ? 'background' : 'color';\n          if (select.querySelector('option') == null) {\n            fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');\n          }\n          return new _colorPicker2.default(select, icons[format]);\n        } else {\n          if (select.querySelector('option') == null) {\n            if (select.classList.contains('ql-font')) {\n              fillSelect(select, FONTS);\n            } else if (select.classList.contains('ql-header')) {\n              fillSelect(select, HEADERS);\n            } else if (select.classList.contains('ql-size')) {\n              fillSelect(select, SIZES);\n            }\n          }\n          return new _picker2.default(select);\n        }\n      });\n      var update = function update() {\n        _this2.pickers.forEach(function (picker) {\n          picker.update();\n        });\n      };\n      this.quill.on(_emitter2.default.events.EDITOR_CHANGE, update);\n    }\n  }]);\n\n  return BaseTheme;\n}(_theme2.default);\n\nBaseTheme.DEFAULTS = (0, _extend2.default)(true, {}, _theme2.default.DEFAULTS, {\n  modules: {\n    toolbar: {\n      handlers: {\n        formula: function formula() {\n          this.quill.theme.tooltip.edit('formula');\n        },\n        image: function image() {\n          var _this3 = this;\n\n          var fileInput = this.container.querySelector('input.ql-image[type=file]');\n          if (fileInput == null) {\n            fileInput = document.createElement('input');\n            fileInput.setAttribute('type', 'file');\n            fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');\n            fileInput.classList.add('ql-image');\n            fileInput.addEventListener('change', function () {\n              if (fileInput.files != null && fileInput.files[0] != null) {\n                var reader = new FileReader();\n                reader.onload = function (e) {\n                  var range = _this3.quill.getSelection(true);\n                  _this3.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert({ image: e.target.result }), _emitter2.default.sources.USER);\n                  _this3.quill.setSelection(range.index + 1, _emitter2.default.sources.SILENT);\n                  fileInput.value = \"\";\n                };\n                reader.readAsDataURL(fileInput.files[0]);\n              }\n            });\n            this.container.appendChild(fileInput);\n          }\n          fileInput.click();\n        },\n        video: function video() {\n          this.quill.theme.tooltip.edit('video');\n        }\n      }\n    }\n  }\n});\n\nvar BaseTooltip = function (_Tooltip) {\n  _inherits(BaseTooltip, _Tooltip);\n\n  function BaseTooltip(quill, boundsContainer) {\n    _classCallCheck(this, BaseTooltip);\n\n    var _this4 = _possibleConstructorReturn(this, (BaseTooltip.__proto__ || Object.getPrototypeOf(BaseTooltip)).call(this, quill, boundsContainer));\n\n    _this4.textbox = _this4.root.querySelector('input[type=\"text\"]');\n    _this4.listen();\n    return _this4;\n  }\n\n  _createClass(BaseTooltip, [{\n    key: 'listen',\n    value: function listen() {\n      var _this5 = this;\n\n      this.textbox.addEventListener('keydown', function (event) {\n        if (_keyboard2.default.match(event, 'enter')) {\n          _this5.save();\n          event.preventDefault();\n        } else if (_keyboard2.default.match(event, 'escape')) {\n          _this5.cancel();\n          event.preventDefault();\n        }\n      });\n    }\n  }, {\n    key: 'cancel',\n    value: function cancel() {\n      this.hide();\n    }\n  }, {\n    key: 'edit',\n    value: function edit() {\n      var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'link';\n      var preview = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n\n      this.root.classList.remove('ql-hidden');\n      this.root.classList.add('ql-editing');\n      if (preview != null) {\n        this.textbox.value = preview;\n      } else if (mode !== this.root.getAttribute('data-mode')) {\n        this.textbox.value = '';\n      }\n      this.position(this.quill.getBounds(this.quill.selection.savedRange));\n      this.textbox.select();\n      this.textbox.setAttribute('placeholder', this.textbox.getAttribute('data-' + mode) || '');\n      this.root.setAttribute('data-mode', mode);\n    }\n  }, {\n    key: 'restoreFocus',\n    value: function restoreFocus() {\n      var scrollTop = this.quill.scrollingContainer.scrollTop;\n      this.quill.focus();\n      this.quill.scrollingContainer.scrollTop = scrollTop;\n    }\n  }, {\n    key: 'save',\n    value: function save() {\n      var value = this.textbox.value;\n      switch (this.root.getAttribute('data-mode')) {\n        case 'link':\n          {\n            var scrollTop = this.quill.root.scrollTop;\n            if (this.linkRange) {\n              this.quill.formatText(this.linkRange, 'link', value, _emitter2.default.sources.USER);\n              delete this.linkRange;\n            } else {\n              this.restoreFocus();\n              this.quill.format('link', value, _emitter2.default.sources.USER);\n            }\n            this.quill.root.scrollTop = scrollTop;\n            break;\n          }\n        case 'video':\n          {\n            value = extractVideoUrl(value);\n          } // eslint-disable-next-line no-fallthrough\n        case 'formula':\n          {\n            if (!value) break;\n            var range = this.quill.getSelection(true);\n            if (range != null) {\n              var index = range.index + range.length;\n              this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, _emitter2.default.sources.USER);\n              if (this.root.getAttribute('data-mode') === 'formula') {\n                this.quill.insertText(index + 1, ' ', _emitter2.default.sources.USER);\n              }\n              this.quill.setSelection(index + 2, _emitter2.default.sources.USER);\n            }\n            break;\n          }\n        default:\n      }\n      this.textbox.value = '';\n      this.hide();\n    }\n  }]);\n\n  return BaseTooltip;\n}(_tooltip2.default);\n\nfunction extractVideoUrl(url) {\n  var match = url.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtube\\.com\\/watch.*v=([a-zA-Z0-9_-]+)/) || url.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtu\\.be\\/([a-zA-Z0-9_-]+)/);\n  if (match) {\n    return (match[1] || 'https') + '://www.youtube.com/embed/' + match[2] + '?showinfo=0';\n  }\n  if (match = url.match(/^(?:(https?):\\/\\/)?(?:www\\.)?vimeo\\.com\\/(\\d+)/)) {\n    // eslint-disable-line no-cond-assign\n    return (match[1] || 'https') + '://player.vimeo.com/video/' + match[2] + '/';\n  }\n  return url;\n}\n\nfunction fillSelect(select, values) {\n  var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n  values.forEach(function (value) {\n    var option = document.createElement('option');\n    if (value === defaultValue) {\n      option.setAttribute('selected', 'selected');\n    } else {\n      option.setAttribute('value', value);\n    }\n    select.appendChild(option);\n  });\n}\n\nexports.BaseTooltip = BaseTooltip;\nexports.default = BaseTheme;\n\n/***/ }),\n/* 44 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar LinkedList = /** @class */ (function () {\n    function LinkedList() {\n        this.head = this.tail = null;\n        this.length = 0;\n    }\n    LinkedList.prototype.append = function () {\n        var nodes = [];\n        for (var _i = 0; _i < arguments.length; _i++) {\n            nodes[_i] = arguments[_i];\n        }\n        this.insertBefore(nodes[0], null);\n        if (nodes.length > 1) {\n            this.append.apply(this, nodes.slice(1));\n        }\n    };\n    LinkedList.prototype.contains = function (node) {\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            if (cur === node)\n                return true;\n        }\n        return false;\n    };\n    LinkedList.prototype.insertBefore = function (node, refNode) {\n        if (!node)\n            return;\n        node.next = refNode;\n        if (refNode != null) {\n            node.prev = refNode.prev;\n            if (refNode.prev != null) {\n                refNode.prev.next = node;\n            }\n            refNode.prev = node;\n            if (refNode === this.head) {\n                this.head = node;\n            }\n        }\n        else if (this.tail != null) {\n            this.tail.next = node;\n            node.prev = this.tail;\n            this.tail = node;\n        }\n        else {\n            node.prev = null;\n            this.head = this.tail = node;\n        }\n        this.length += 1;\n    };\n    LinkedList.prototype.offset = function (target) {\n        var index = 0, cur = this.head;\n        while (cur != null) {\n            if (cur === target)\n                return index;\n            index += cur.length();\n            cur = cur.next;\n        }\n        return -1;\n    };\n    LinkedList.prototype.remove = function (node) {\n        if (!this.contains(node))\n            return;\n        if (node.prev != null)\n            node.prev.next = node.next;\n        if (node.next != null)\n            node.next.prev = node.prev;\n        if (node === this.head)\n            this.head = node.next;\n        if (node === this.tail)\n            this.tail = node.prev;\n        this.length -= 1;\n    };\n    LinkedList.prototype.iterator = function (curNode) {\n        if (curNode === void 0) { curNode = this.head; }\n        // TODO use yield when we can\n        return function () {\n            var ret = curNode;\n            if (curNode != null)\n                curNode = curNode.next;\n            return ret;\n        };\n    };\n    LinkedList.prototype.find = function (index, inclusive) {\n        if (inclusive === void 0) { inclusive = false; }\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            var length = cur.length();\n            if (index < length ||\n                (inclusive && index === length && (cur.next == null || cur.next.length() !== 0))) {\n                return [cur, index];\n            }\n            index -= length;\n        }\n        return [null, 0];\n    };\n    LinkedList.prototype.forEach = function (callback) {\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            callback(cur);\n        }\n    };\n    LinkedList.prototype.forEachAt = function (index, length, callback) {\n        if (length <= 0)\n            return;\n        var _a = this.find(index), startNode = _a[0], offset = _a[1];\n        var cur, curIndex = index - offset, next = this.iterator(startNode);\n        while ((cur = next()) && curIndex < index + length) {\n            var curLength = cur.length();\n            if (index > curIndex) {\n                callback(cur, index - curIndex, Math.min(length, curIndex + curLength - index));\n            }\n            else {\n                callback(cur, 0, Math.min(curLength, index + length - curIndex));\n            }\n            curIndex += curLength;\n        }\n    };\n    LinkedList.prototype.map = function (callback) {\n        return this.reduce(function (memo, cur) {\n            memo.push(callback(cur));\n            return memo;\n        }, []);\n    };\n    LinkedList.prototype.reduce = function (callback, memo) {\n        var cur, next = this.iterator();\n        while ((cur = next())) {\n            memo = callback(memo, cur);\n        }\n        return memo;\n    };\n    return LinkedList;\n}());\nexports.default = LinkedList;\n\n\n/***/ }),\n/* 45 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = __webpack_require__(17);\nvar Registry = __webpack_require__(1);\nvar OBSERVER_CONFIG = {\n    attributes: true,\n    characterData: true,\n    characterDataOldValue: true,\n    childList: true,\n    subtree: true,\n};\nvar MAX_OPTIMIZE_ITERATIONS = 100;\nvar ScrollBlot = /** @class */ (function (_super) {\n    __extends(ScrollBlot, _super);\n    function ScrollBlot(node) {\n        var _this = _super.call(this, node) || this;\n        _this.scroll = _this;\n        _this.observer = new MutationObserver(function (mutations) {\n            _this.update(mutations);\n        });\n        _this.observer.observe(_this.domNode, OBSERVER_CONFIG);\n        _this.attach();\n        return _this;\n    }\n    ScrollBlot.prototype.detach = function () {\n        _super.prototype.detach.call(this);\n        this.observer.disconnect();\n    };\n    ScrollBlot.prototype.deleteAt = function (index, length) {\n        this.update();\n        if (index === 0 && length === this.length()) {\n            this.children.forEach(function (child) {\n                child.remove();\n            });\n        }\n        else {\n            _super.prototype.deleteAt.call(this, index, length);\n        }\n    };\n    ScrollBlot.prototype.formatAt = function (index, length, name, value) {\n        this.update();\n        _super.prototype.formatAt.call(this, index, length, name, value);\n    };\n    ScrollBlot.prototype.insertAt = function (index, value, def) {\n        this.update();\n        _super.prototype.insertAt.call(this, index, value, def);\n    };\n    ScrollBlot.prototype.optimize = function (mutations, context) {\n        var _this = this;\n        if (mutations === void 0) { mutations = []; }\n        if (context === void 0) { context = {}; }\n        _super.prototype.optimize.call(this, context);\n        // We must modify mutations directly, cannot make copy and then modify\n        var records = [].slice.call(this.observer.takeRecords());\n        // Array.push currently seems to be implemented by a non-tail recursive function\n        // so we cannot just mutations.push.apply(mutations, this.observer.takeRecords());\n        while (records.length > 0)\n            mutations.push(records.pop());\n        // TODO use WeakMap\n        var mark = function (blot, markParent) {\n            if (markParent === void 0) { markParent = true; }\n            if (blot == null || blot === _this)\n                return;\n            if (blot.domNode.parentNode == null)\n                return;\n            // @ts-ignore\n            if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations = [];\n            }\n            if (markParent)\n                mark(blot.parent);\n        };\n        var optimize = function (blot) {\n            // Post-order traversal\n            if (\n            // @ts-ignore\n            blot.domNode[Registry.DATA_KEY] == null ||\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations == null) {\n                return;\n            }\n            if (blot instanceof container_1.default) {\n                blot.children.forEach(optimize);\n            }\n            blot.optimize(context);\n        };\n        var remaining = mutations;\n        for (var i = 0; remaining.length > 0; i += 1) {\n            if (i >= MAX_OPTIMIZE_ITERATIONS) {\n                throw new Error('[Parchment] Maximum optimize iterations reached');\n            }\n            remaining.forEach(function (mutation) {\n                var blot = Registry.find(mutation.target, true);\n                if (blot == null)\n                    return;\n                if (blot.domNode === mutation.target) {\n                    if (mutation.type === 'childList') {\n                        mark(Registry.find(mutation.previousSibling, false));\n                        [].forEach.call(mutation.addedNodes, function (node) {\n                            var child = Registry.find(node, false);\n                            mark(child, false);\n                            if (child instanceof container_1.default) {\n                                child.children.forEach(function (grandChild) {\n                                    mark(grandChild, false);\n                                });\n                            }\n                        });\n                    }\n                    else if (mutation.type === 'attributes') {\n                        mark(blot.prev);\n                    }\n                }\n                mark(blot);\n            });\n            this.children.forEach(optimize);\n            remaining = [].slice.call(this.observer.takeRecords());\n            records = remaining.slice();\n            while (records.length > 0)\n                mutations.push(records.pop());\n        }\n    };\n    ScrollBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        if (context === void 0) { context = {}; }\n        mutations = mutations || this.observer.takeRecords();\n        // TODO use WeakMap\n        mutations\n            .map(function (mutation) {\n            var blot = Registry.find(mutation.target, true);\n            if (blot == null)\n                return null;\n            // @ts-ignore\n            if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations = [mutation];\n                return blot;\n            }\n            else {\n                // @ts-ignore\n                blot.domNode[Registry.DATA_KEY].mutations.push(mutation);\n                return null;\n            }\n        })\n            .forEach(function (blot) {\n            if (blot == null ||\n                blot === _this ||\n                //@ts-ignore\n                blot.domNode[Registry.DATA_KEY] == null)\n                return;\n            // @ts-ignore\n            blot.update(blot.domNode[Registry.DATA_KEY].mutations || [], context);\n        });\n        // @ts-ignore\n        if (this.domNode[Registry.DATA_KEY].mutations != null) {\n            // @ts-ignore\n            _super.prototype.update.call(this, this.domNode[Registry.DATA_KEY].mutations, context);\n        }\n        this.optimize(mutations, context);\n    };\n    ScrollBlot.blotName = 'scroll';\n    ScrollBlot.defaultChild = 'block';\n    ScrollBlot.scope = Registry.Scope.BLOCK_BLOT;\n    ScrollBlot.tagName = 'DIV';\n    return ScrollBlot;\n}(container_1.default));\nexports.default = ScrollBlot;\n\n\n/***/ }),\n/* 46 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = __webpack_require__(18);\nvar Registry = __webpack_require__(1);\n// Shallow object comparison\nfunction isEqual(obj1, obj2) {\n    if (Object.keys(obj1).length !== Object.keys(obj2).length)\n        return false;\n    // @ts-ignore\n    for (var prop in obj1) {\n        // @ts-ignore\n        if (obj1[prop] !== obj2[prop])\n            return false;\n    }\n    return true;\n}\nvar InlineBlot = /** @class */ (function (_super) {\n    __extends(InlineBlot, _super);\n    function InlineBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    InlineBlot.formats = function (domNode) {\n        if (domNode.tagName === InlineBlot.tagName)\n            return undefined;\n        return _super.formats.call(this, domNode);\n    };\n    InlineBlot.prototype.format = function (name, value) {\n        var _this = this;\n        if (name === this.statics.blotName && !value) {\n            this.children.forEach(function (child) {\n                if (!(child instanceof format_1.default)) {\n                    child = child.wrap(InlineBlot.blotName, true);\n                }\n                _this.attributes.copy(child);\n            });\n            this.unwrap();\n        }\n        else {\n            _super.prototype.format.call(this, name, value);\n        }\n    };\n    InlineBlot.prototype.formatAt = function (index, length, name, value) {\n        if (this.formats()[name] != null || Registry.query(name, Registry.Scope.ATTRIBUTE)) {\n            var blot = this.isolate(index, length);\n            blot.format(name, value);\n        }\n        else {\n            _super.prototype.formatAt.call(this, index, length, name, value);\n        }\n    };\n    InlineBlot.prototype.optimize = function (context) {\n        _super.prototype.optimize.call(this, context);\n        var formats = this.formats();\n        if (Object.keys(formats).length === 0) {\n            return this.unwrap(); // unformatted span\n        }\n        var next = this.next;\n        if (next instanceof InlineBlot && next.prev === this && isEqual(formats, next.formats())) {\n            next.moveChildren(this);\n            next.remove();\n        }\n    };\n    InlineBlot.blotName = 'inline';\n    InlineBlot.scope = Registry.Scope.INLINE_BLOT;\n    InlineBlot.tagName = 'SPAN';\n    return InlineBlot;\n}(format_1.default));\nexports.default = InlineBlot;\n\n\n/***/ }),\n/* 47 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = __webpack_require__(18);\nvar Registry = __webpack_require__(1);\nvar BlockBlot = /** @class */ (function (_super) {\n    __extends(BlockBlot, _super);\n    function BlockBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    BlockBlot.formats = function (domNode) {\n        var tagName = Registry.query(BlockBlot.blotName).tagName;\n        if (domNode.tagName === tagName)\n            return undefined;\n        return _super.formats.call(this, domNode);\n    };\n    BlockBlot.prototype.format = function (name, value) {\n        if (Registry.query(name, Registry.Scope.BLOCK) == null) {\n            return;\n        }\n        else if (name === this.statics.blotName && !value) {\n            this.replaceWith(BlockBlot.blotName);\n        }\n        else {\n            _super.prototype.format.call(this, name, value);\n        }\n    };\n    BlockBlot.prototype.formatAt = function (index, length, name, value) {\n        if (Registry.query(name, Registry.Scope.BLOCK) != null) {\n            this.format(name, value);\n        }\n        else {\n            _super.prototype.formatAt.call(this, index, length, name, value);\n        }\n    };\n    BlockBlot.prototype.insertAt = function (index, value, def) {\n        if (def == null || Registry.query(value, Registry.Scope.INLINE) != null) {\n            // Insert text or inline\n            _super.prototype.insertAt.call(this, index, value, def);\n        }\n        else {\n            var after = this.split(index);\n            var blot = Registry.create(value, def);\n            after.parent.insertBefore(blot, after);\n        }\n    };\n    BlockBlot.prototype.update = function (mutations, context) {\n        if (navigator.userAgent.match(/Trident/)) {\n            this.build();\n        }\n        else {\n            _super.prototype.update.call(this, mutations, context);\n        }\n    };\n    BlockBlot.blotName = 'block';\n    BlockBlot.scope = Registry.Scope.BLOCK_BLOT;\n    BlockBlot.tagName = 'P';\n    return BlockBlot;\n}(format_1.default));\nexports.default = BlockBlot;\n\n\n/***/ }),\n/* 48 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = __webpack_require__(19);\nvar EmbedBlot = /** @class */ (function (_super) {\n    __extends(EmbedBlot, _super);\n    function EmbedBlot() {\n        return _super !== null && _super.apply(this, arguments) || this;\n    }\n    EmbedBlot.formats = function (domNode) {\n        return undefined;\n    };\n    EmbedBlot.prototype.format = function (name, value) {\n        // super.formatAt wraps, which is what we want in general,\n        // but this allows subclasses to overwrite for formats\n        // that just apply to particular embeds\n        _super.prototype.formatAt.call(this, 0, this.length(), name, value);\n    };\n    EmbedBlot.prototype.formatAt = function (index, length, name, value) {\n        if (index === 0 && length === this.length()) {\n            this.format(name, value);\n        }\n        else {\n            _super.prototype.formatAt.call(this, index, length, name, value);\n        }\n    };\n    EmbedBlot.prototype.formats = function () {\n        return this.statics.formats(this.domNode);\n    };\n    return EmbedBlot;\n}(leaf_1.default));\nexports.default = EmbedBlot;\n\n\n/***/ }),\n/* 49 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n    var extendStatics = Object.setPrototypeOf ||\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n    return function (d, b) {\n        extendStatics(d, b);\n        function __() { this.constructor = d; }\n        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n    };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = __webpack_require__(19);\nvar Registry = __webpack_require__(1);\nvar TextBlot = /** @class */ (function (_super) {\n    __extends(TextBlot, _super);\n    function TextBlot(node) {\n        var _this = _super.call(this, node) || this;\n        _this.text = _this.statics.value(_this.domNode);\n        return _this;\n    }\n    TextBlot.create = function (value) {\n        return document.createTextNode(value);\n    };\n    TextBlot.value = function (domNode) {\n        var text = domNode.data;\n        // @ts-ignore\n        if (text['normalize'])\n            text = text['normalize']();\n        return text;\n    };\n    TextBlot.prototype.deleteAt = function (index, length) {\n        this.domNode.data = this.text = this.text.slice(0, index) + this.text.slice(index + length);\n    };\n    TextBlot.prototype.index = function (node, offset) {\n        if (this.domNode === node) {\n            return offset;\n        }\n        return -1;\n    };\n    TextBlot.prototype.insertAt = function (index, value, def) {\n        if (def == null) {\n            this.text = this.text.slice(0, index) + value + this.text.slice(index);\n            this.domNode.data = this.text;\n        }\n        else {\n            _super.prototype.insertAt.call(this, index, value, def);\n        }\n    };\n    TextBlot.prototype.length = function () {\n        return this.text.length;\n    };\n    TextBlot.prototype.optimize = function (context) {\n        _super.prototype.optimize.call(this, context);\n        this.text = this.statics.value(this.domNode);\n        if (this.text.length === 0) {\n            this.remove();\n        }\n        else if (this.next instanceof TextBlot && this.next.prev === this) {\n            this.insertAt(this.length(), this.next.value());\n            this.next.remove();\n        }\n    };\n    TextBlot.prototype.position = function (index, inclusive) {\n        if (inclusive === void 0) { inclusive = false; }\n        return [this.domNode, index];\n    };\n    TextBlot.prototype.split = function (index, force) {\n        if (force === void 0) { force = false; }\n        if (!force) {\n            if (index === 0)\n                return this;\n            if (index === this.length())\n                return this.next;\n        }\n        var after = Registry.create(this.domNode.splitText(index));\n        this.parent.insertBefore(after, this.next);\n        this.text = this.statics.value(this.domNode);\n        return after;\n    };\n    TextBlot.prototype.update = function (mutations, context) {\n        var _this = this;\n        if (mutations.some(function (mutation) {\n            return mutation.type === 'characterData' && mutation.target === _this.domNode;\n        })) {\n            this.text = this.statics.value(this.domNode);\n        }\n    };\n    TextBlot.prototype.value = function () {\n        return this.text;\n    };\n    TextBlot.blotName = 'text';\n    TextBlot.scope = Registry.Scope.INLINE_BLOT;\n    return TextBlot;\n}(leaf_1.default));\nexports.default = TextBlot;\n\n\n/***/ }),\n/* 50 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar elem = document.createElement('div');\nelem.classList.toggle('test-class', false);\nif (elem.classList.contains('test-class')) {\n  var _toggle = DOMTokenList.prototype.toggle;\n  DOMTokenList.prototype.toggle = function (token, force) {\n    if (arguments.length > 1 && !this.contains(token) === !force) {\n      return force;\n    } else {\n      return _toggle.call(this, token);\n    }\n  };\n}\n\nif (!String.prototype.startsWith) {\n  String.prototype.startsWith = function (searchString, position) {\n    position = position || 0;\n    return this.substr(position, searchString.length) === searchString;\n  };\n}\n\nif (!String.prototype.endsWith) {\n  String.prototype.endsWith = function (searchString, position) {\n    var subjectString = this.toString();\n    if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {\n      position = subjectString.length;\n    }\n    position -= searchString.length;\n    var lastIndex = subjectString.indexOf(searchString, position);\n    return lastIndex !== -1 && lastIndex === position;\n  };\n}\n\nif (!Array.prototype.find) {\n  Object.defineProperty(Array.prototype, \"find\", {\n    value: function value(predicate) {\n      if (this === null) {\n        throw new TypeError('Array.prototype.find called on null or undefined');\n      }\n      if (typeof predicate !== 'function') {\n        throw new TypeError('predicate must be a function');\n      }\n      var list = Object(this);\n      var length = list.length >>> 0;\n      var thisArg = arguments[1];\n      var value;\n\n      for (var i = 0; i < length; i++) {\n        value = list[i];\n        if (predicate.call(thisArg, value, i, list)) {\n          return value;\n        }\n      }\n      return undefined;\n    }\n  });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  // Disable resizing in Firefox\n  document.execCommand(\"enableObjectResizing\", false, false);\n  // Disable automatic linkifying in IE11\n  document.execCommand(\"autoUrlDetect\", false, false);\n});\n\n/***/ }),\n/* 51 */\n/***/ (function(module, exports) {\n\n/**\n * This library modifies the diff-patch-match library by Neil Fraser\n * by removing the patch and match functionality and certain advanced\n * options in the diff function. The original license is as follows:\n *\n * ===\n *\n * Diff Match and Patch\n *\n * Copyright 2006 Google Inc.\n * http://code.google.com/p/google-diff-match-patch/\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n/**\n * The data structure representing a diff is an array of tuples:\n * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]\n * which means: delete 'Hello', add 'Goodbye' and keep ' world.'\n */\nvar DIFF_DELETE = -1;\nvar DIFF_INSERT = 1;\nvar DIFF_EQUAL = 0;\n\n\n/**\n * Find the differences between two texts.  Simplifies the problem by stripping\n * any common prefix or suffix off the texts before diffing.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {Int} cursor_pos Expected edit position in text1 (optional)\n * @return {Array} Array of diff tuples.\n */\nfunction diff_main(text1, text2, cursor_pos) {\n  // Check for equality (speedup).\n  if (text1 == text2) {\n    if (text1) {\n      return [[DIFF_EQUAL, text1]];\n    }\n    return [];\n  }\n\n  // Check cursor_pos within bounds\n  if (cursor_pos < 0 || text1.length < cursor_pos) {\n    cursor_pos = null;\n  }\n\n  // Trim off common prefix (speedup).\n  var commonlength = diff_commonPrefix(text1, text2);\n  var commonprefix = text1.substring(0, commonlength);\n  text1 = text1.substring(commonlength);\n  text2 = text2.substring(commonlength);\n\n  // Trim off common suffix (speedup).\n  commonlength = diff_commonSuffix(text1, text2);\n  var commonsuffix = text1.substring(text1.length - commonlength);\n  text1 = text1.substring(0, text1.length - commonlength);\n  text2 = text2.substring(0, text2.length - commonlength);\n\n  // Compute the diff on the middle block.\n  var diffs = diff_compute_(text1, text2);\n\n  // Restore the prefix and suffix.\n  if (commonprefix) {\n    diffs.unshift([DIFF_EQUAL, commonprefix]);\n  }\n  if (commonsuffix) {\n    diffs.push([DIFF_EQUAL, commonsuffix]);\n  }\n  diff_cleanupMerge(diffs);\n  if (cursor_pos != null) {\n    diffs = fix_cursor(diffs, cursor_pos);\n  }\n  diffs = fix_emoji(diffs);\n  return diffs;\n};\n\n\n/**\n * Find the differences between two texts.  Assumes that the texts do not\n * have any common prefix or suffix.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_compute_(text1, text2) {\n  var diffs;\n\n  if (!text1) {\n    // Just add some text (speedup).\n    return [[DIFF_INSERT, text2]];\n  }\n\n  if (!text2) {\n    // Just delete some text (speedup).\n    return [[DIFF_DELETE, text1]];\n  }\n\n  var longtext = text1.length > text2.length ? text1 : text2;\n  var shorttext = text1.length > text2.length ? text2 : text1;\n  var i = longtext.indexOf(shorttext);\n  if (i != -1) {\n    // Shorter text is inside the longer text (speedup).\n    diffs = [[DIFF_INSERT, longtext.substring(0, i)],\n             [DIFF_EQUAL, shorttext],\n             [DIFF_INSERT, longtext.substring(i + shorttext.length)]];\n    // Swap insertions for deletions if diff is reversed.\n    if (text1.length > text2.length) {\n      diffs[0][0] = diffs[2][0] = DIFF_DELETE;\n    }\n    return diffs;\n  }\n\n  if (shorttext.length == 1) {\n    // Single character string.\n    // After the previous speedup, the character can't be an equality.\n    return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n  }\n\n  // Check to see if the problem can be split in two.\n  var hm = diff_halfMatch_(text1, text2);\n  if (hm) {\n    // A half-match was found, sort out the return data.\n    var text1_a = hm[0];\n    var text1_b = hm[1];\n    var text2_a = hm[2];\n    var text2_b = hm[3];\n    var mid_common = hm[4];\n    // Send both pairs off for separate processing.\n    var diffs_a = diff_main(text1_a, text2_a);\n    var diffs_b = diff_main(text1_b, text2_b);\n    // Merge the results.\n    return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b);\n  }\n\n  return diff_bisect_(text1, text2);\n};\n\n\n/**\n * Find the 'middle snake' of a diff, split the problem in two\n * and return the recursively constructed diff.\n * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n * @private\n */\nfunction diff_bisect_(text1, text2) {\n  // Cache the text lengths to prevent multiple calls.\n  var text1_length = text1.length;\n  var text2_length = text2.length;\n  var max_d = Math.ceil((text1_length + text2_length) / 2);\n  var v_offset = max_d;\n  var v_length = 2 * max_d;\n  var v1 = new Array(v_length);\n  var v2 = new Array(v_length);\n  // Setting all elements to -1 is faster in Chrome & Firefox than mixing\n  // integers and undefined.\n  for (var x = 0; x < v_length; x++) {\n    v1[x] = -1;\n    v2[x] = -1;\n  }\n  v1[v_offset + 1] = 0;\n  v2[v_offset + 1] = 0;\n  var delta = text1_length - text2_length;\n  // If the total number of characters is odd, then the front path will collide\n  // with the reverse path.\n  var front = (delta % 2 != 0);\n  // Offsets for start and end of k loop.\n  // Prevents mapping of space beyond the grid.\n  var k1start = 0;\n  var k1end = 0;\n  var k2start = 0;\n  var k2end = 0;\n  for (var d = 0; d < max_d; d++) {\n    // Walk the front path one step.\n    for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n      var k1_offset = v_offset + k1;\n      var x1;\n      if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n        x1 = v1[k1_offset + 1];\n      } else {\n        x1 = v1[k1_offset - 1] + 1;\n      }\n      var y1 = x1 - k1;\n      while (x1 < text1_length && y1 < text2_length &&\n             text1.charAt(x1) == text2.charAt(y1)) {\n        x1++;\n        y1++;\n      }\n      v1[k1_offset] = x1;\n      if (x1 > text1_length) {\n        // Ran off the right of the graph.\n        k1end += 2;\n      } else if (y1 > text2_length) {\n        // Ran off the bottom of the graph.\n        k1start += 2;\n      } else if (front) {\n        var k2_offset = v_offset + delta - k1;\n        if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n          // Mirror x2 onto top-left coordinate system.\n          var x2 = text1_length - v2[k2_offset];\n          if (x1 >= x2) {\n            // Overlap detected.\n            return diff_bisectSplit_(text1, text2, x1, y1);\n          }\n        }\n      }\n    }\n\n    // Walk the reverse path one step.\n    for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n      var k2_offset = v_offset + k2;\n      var x2;\n      if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n        x2 = v2[k2_offset + 1];\n      } else {\n        x2 = v2[k2_offset - 1] + 1;\n      }\n      var y2 = x2 - k2;\n      while (x2 < text1_length && y2 < text2_length &&\n             text1.charAt(text1_length - x2 - 1) ==\n             text2.charAt(text2_length - y2 - 1)) {\n        x2++;\n        y2++;\n      }\n      v2[k2_offset] = x2;\n      if (x2 > text1_length) {\n        // Ran off the left of the graph.\n        k2end += 2;\n      } else if (y2 > text2_length) {\n        // Ran off the top of the graph.\n        k2start += 2;\n      } else if (!front) {\n        var k1_offset = v_offset + delta - k2;\n        if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n          var x1 = v1[k1_offset];\n          var y1 = v_offset + x1 - k1_offset;\n          // Mirror x2 onto top-left coordinate system.\n          x2 = text1_length - x2;\n          if (x1 >= x2) {\n            // Overlap detected.\n            return diff_bisectSplit_(text1, text2, x1, y1);\n          }\n        }\n      }\n    }\n  }\n  // Diff took too long and hit the deadline or\n  // number of diffs equals number of characters, no commonality at all.\n  return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n};\n\n\n/**\n * Given the location of the 'middle snake', split the diff in two parts\n * and recurse.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} x Index of split point in text1.\n * @param {number} y Index of split point in text2.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_bisectSplit_(text1, text2, x, y) {\n  var text1a = text1.substring(0, x);\n  var text2a = text2.substring(0, y);\n  var text1b = text1.substring(x);\n  var text2b = text2.substring(y);\n\n  // Compute both diffs serially.\n  var diffs = diff_main(text1a, text2a);\n  var diffsb = diff_main(text1b, text2b);\n\n  return diffs.concat(diffsb);\n};\n\n\n/**\n * Determine the common prefix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the start of each\n *     string.\n */\nfunction diff_commonPrefix(text1, text2) {\n  // Quick check for common null cases.\n  if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {\n    return 0;\n  }\n  // Binary search.\n  // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n  var pointermin = 0;\n  var pointermax = Math.min(text1.length, text2.length);\n  var pointermid = pointermax;\n  var pointerstart = 0;\n  while (pointermin < pointermid) {\n    if (text1.substring(pointerstart, pointermid) ==\n        text2.substring(pointerstart, pointermid)) {\n      pointermin = pointermid;\n      pointerstart = pointermin;\n    } else {\n      pointermax = pointermid;\n    }\n    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n  }\n  return pointermid;\n};\n\n\n/**\n * Determine the common suffix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the end of each string.\n */\nfunction diff_commonSuffix(text1, text2) {\n  // Quick check for common null cases.\n  if (!text1 || !text2 ||\n      text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {\n    return 0;\n  }\n  // Binary search.\n  // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n  var pointermin = 0;\n  var pointermax = Math.min(text1.length, text2.length);\n  var pointermid = pointermax;\n  var pointerend = 0;\n  while (pointermin < pointermid) {\n    if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==\n        text2.substring(text2.length - pointermid, text2.length - pointerend)) {\n      pointermin = pointermid;\n      pointerend = pointermin;\n    } else {\n      pointermax = pointermid;\n    }\n    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n  }\n  return pointermid;\n};\n\n\n/**\n * Do the two texts share a substring which is at least half the length of the\n * longer text?\n * This speedup can produce non-minimal diffs.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {Array.<string>} Five element Array, containing the prefix of\n *     text1, the suffix of text1, the prefix of text2, the suffix of\n *     text2 and the common middle.  Or null if there was no match.\n */\nfunction diff_halfMatch_(text1, text2) {\n  var longtext = text1.length > text2.length ? text1 : text2;\n  var shorttext = text1.length > text2.length ? text2 : text1;\n  if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {\n    return null;  // Pointless.\n  }\n\n  /**\n   * Does a substring of shorttext exist within longtext such that the substring\n   * is at least half the length of longtext?\n   * Closure, but does not reference any external variables.\n   * @param {string} longtext Longer string.\n   * @param {string} shorttext Shorter string.\n   * @param {number} i Start index of quarter length substring within longtext.\n   * @return {Array.<string>} Five element Array, containing the prefix of\n   *     longtext, the suffix of longtext, the prefix of shorttext, the suffix\n   *     of shorttext and the common middle.  Or null if there was no match.\n   * @private\n   */\n  function diff_halfMatchI_(longtext, shorttext, i) {\n    // Start with a 1/4 length substring at position i as a seed.\n    var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));\n    var j = -1;\n    var best_common = '';\n    var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;\n    while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n      var prefixLength = diff_commonPrefix(longtext.substring(i),\n                                           shorttext.substring(j));\n      var suffixLength = diff_commonSuffix(longtext.substring(0, i),\n                                           shorttext.substring(0, j));\n      if (best_common.length < suffixLength + prefixLength) {\n        best_common = shorttext.substring(j - suffixLength, j) +\n            shorttext.substring(j, j + prefixLength);\n        best_longtext_a = longtext.substring(0, i - suffixLength);\n        best_longtext_b = longtext.substring(i + prefixLength);\n        best_shorttext_a = shorttext.substring(0, j - suffixLength);\n        best_shorttext_b = shorttext.substring(j + prefixLength);\n      }\n    }\n    if (best_common.length * 2 >= longtext.length) {\n      return [best_longtext_a, best_longtext_b,\n              best_shorttext_a, best_shorttext_b, best_common];\n    } else {\n      return null;\n    }\n  }\n\n  // First check if the second quarter is the seed for a half-match.\n  var hm1 = diff_halfMatchI_(longtext, shorttext,\n                             Math.ceil(longtext.length / 4));\n  // Check again based on the third quarter.\n  var hm2 = diff_halfMatchI_(longtext, shorttext,\n                             Math.ceil(longtext.length / 2));\n  var hm;\n  if (!hm1 && !hm2) {\n    return null;\n  } else if (!hm2) {\n    hm = hm1;\n  } else if (!hm1) {\n    hm = hm2;\n  } else {\n    // Both matched.  Select the longest.\n    hm = hm1[4].length > hm2[4].length ? hm1 : hm2;\n  }\n\n  // A half-match was found, sort out the return data.\n  var text1_a, text1_b, text2_a, text2_b;\n  if (text1.length > text2.length) {\n    text1_a = hm[0];\n    text1_b = hm[1];\n    text2_a = hm[2];\n    text2_b = hm[3];\n  } else {\n    text2_a = hm[0];\n    text2_b = hm[1];\n    text1_a = hm[2];\n    text1_b = hm[3];\n  }\n  var mid_common = hm[4];\n  return [text1_a, text1_b, text2_a, text2_b, mid_common];\n};\n\n\n/**\n * Reorder and merge like edit sections.  Merge equalities.\n * Any edit section can move as long as it doesn't cross an equality.\n * @param {Array} diffs Array of diff tuples.\n */\nfunction diff_cleanupMerge(diffs) {\n  diffs.push([DIFF_EQUAL, '']);  // Add a dummy entry at the end.\n  var pointer = 0;\n  var count_delete = 0;\n  var count_insert = 0;\n  var text_delete = '';\n  var text_insert = '';\n  var commonlength;\n  while (pointer < diffs.length) {\n    switch (diffs[pointer][0]) {\n      case DIFF_INSERT:\n        count_insert++;\n        text_insert += diffs[pointer][1];\n        pointer++;\n        break;\n      case DIFF_DELETE:\n        count_delete++;\n        text_delete += diffs[pointer][1];\n        pointer++;\n        break;\n      case DIFF_EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete + count_insert > 1) {\n          if (count_delete !== 0 && count_insert !== 0) {\n            // Factor out any common prefixies.\n            commonlength = diff_commonPrefix(text_insert, text_delete);\n            if (commonlength !== 0) {\n              if ((pointer - count_delete - count_insert) > 0 &&\n                  diffs[pointer - count_delete - count_insert - 1][0] ==\n                  DIFF_EQUAL) {\n                diffs[pointer - count_delete - count_insert - 1][1] +=\n                    text_insert.substring(0, commonlength);\n              } else {\n                diffs.splice(0, 0, [DIFF_EQUAL,\n                                    text_insert.substring(0, commonlength)]);\n                pointer++;\n              }\n              text_insert = text_insert.substring(commonlength);\n              text_delete = text_delete.substring(commonlength);\n            }\n            // Factor out any common suffixies.\n            commonlength = diff_commonSuffix(text_insert, text_delete);\n            if (commonlength !== 0) {\n              diffs[pointer][1] = text_insert.substring(text_insert.length -\n                  commonlength) + diffs[pointer][1];\n              text_insert = text_insert.substring(0, text_insert.length -\n                  commonlength);\n              text_delete = text_delete.substring(0, text_delete.length -\n                  commonlength);\n            }\n          }\n          // Delete the offending records and add the merged ones.\n          if (count_delete === 0) {\n            diffs.splice(pointer - count_insert,\n                count_delete + count_insert, [DIFF_INSERT, text_insert]);\n          } else if (count_insert === 0) {\n            diffs.splice(pointer - count_delete,\n                count_delete + count_insert, [DIFF_DELETE, text_delete]);\n          } else {\n            diffs.splice(pointer - count_delete - count_insert,\n                count_delete + count_insert, [DIFF_DELETE, text_delete],\n                [DIFF_INSERT, text_insert]);\n          }\n          pointer = pointer - count_delete - count_insert +\n                    (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1;\n        } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {\n          // Merge this equality with the previous one.\n          diffs[pointer - 1][1] += diffs[pointer][1];\n          diffs.splice(pointer, 1);\n        } else {\n          pointer++;\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = '';\n        text_insert = '';\n        break;\n    }\n  }\n  if (diffs[diffs.length - 1][1] === '') {\n    diffs.pop();  // Remove the dummy entry at the end.\n  }\n\n  // Second pass: look for single edits surrounded on both sides by equalities\n  // which can be shifted sideways to eliminate an equality.\n  // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n  var changes = false;\n  pointer = 1;\n  // Intentionally ignore the first and last element (don't need checking).\n  while (pointer < diffs.length - 1) {\n    if (diffs[pointer - 1][0] == DIFF_EQUAL &&\n        diffs[pointer + 1][0] == DIFF_EQUAL) {\n      // This is a single edit surrounded by equalities.\n      if (diffs[pointer][1].substring(diffs[pointer][1].length -\n          diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) {\n        // Shift the edit over the previous equality.\n        diffs[pointer][1] = diffs[pointer - 1][1] +\n            diffs[pointer][1].substring(0, diffs[pointer][1].length -\n                                        diffs[pointer - 1][1].length);\n        diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];\n        diffs.splice(pointer - 1, 1);\n        changes = true;\n      } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) ==\n          diffs[pointer + 1][1]) {\n        // Shift the edit over the next equality.\n        diffs[pointer - 1][1] += diffs[pointer + 1][1];\n        diffs[pointer][1] =\n            diffs[pointer][1].substring(diffs[pointer + 1][1].length) +\n            diffs[pointer + 1][1];\n        diffs.splice(pointer + 1, 1);\n        changes = true;\n      }\n    }\n    pointer++;\n  }\n  // If shifts were made, the diff needs reordering and another shift sweep.\n  if (changes) {\n    diff_cleanupMerge(diffs);\n  }\n};\n\n\nvar diff = diff_main;\ndiff.INSERT = DIFF_INSERT;\ndiff.DELETE = DIFF_DELETE;\ndiff.EQUAL = DIFF_EQUAL;\n\nmodule.exports = diff;\n\n/*\n * Modify a diff such that the cursor position points to the start of a change:\n * E.g.\n *   cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1)\n *     => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]]\n *   cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2)\n *     => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]]\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} A tuple [cursor location in the modified diff, modified diff]\n */\nfunction cursor_normalize_diff (diffs, cursor_pos) {\n  if (cursor_pos === 0) {\n    return [DIFF_EQUAL, diffs];\n  }\n  for (var current_pos = 0, i = 0; i < diffs.length; i++) {\n    var d = diffs[i];\n    if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) {\n      var next_pos = current_pos + d[1].length;\n      if (cursor_pos === next_pos) {\n        return [i + 1, diffs];\n      } else if (cursor_pos < next_pos) {\n        // copy to prevent side effects\n        diffs = diffs.slice();\n        // split d into two diff changes\n        var split_pos = cursor_pos - current_pos;\n        var d_left = [d[0], d[1].slice(0, split_pos)];\n        var d_right = [d[0], d[1].slice(split_pos)];\n        diffs.splice(i, 1, d_left, d_right);\n        return [i + 1, diffs];\n      } else {\n        current_pos = next_pos;\n      }\n    }\n  }\n  throw new Error('cursor_pos is out of bounds!')\n}\n\n/*\n * Modify a diff such that the edit position is \"shifted\" to the proposed edit location (cursor_position).\n *\n * Case 1)\n *   Check if a naive shift is possible:\n *     [0, X], [ 1, Y] -> [ 1, Y], [0, X]    (if X + Y === Y + X)\n *     [0, X], [-1, Y] -> [-1, Y], [0, X]    (if X + Y === Y + X) - holds same result\n * Case 2)\n *   Check if the following shifts are possible:\n *     [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix']\n *     [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix']\n *         ^            ^\n *         d          d_next\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} Array of diff tuples\n */\nfunction fix_cursor (diffs, cursor_pos) {\n  var norm = cursor_normalize_diff(diffs, cursor_pos);\n  var ndiffs = norm[1];\n  var cursor_pointer = norm[0];\n  var d = ndiffs[cursor_pointer];\n  var d_next = ndiffs[cursor_pointer + 1];\n\n  if (d == null) {\n    // Text was deleted from end of original string,\n    // cursor is now out of bounds in new string\n    return diffs;\n  } else if (d[0] !== DIFF_EQUAL) {\n    // A modification happened at the cursor location.\n    // This is the expected outcome, so we can return the original diff.\n    return diffs;\n  } else {\n    if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) {\n      // Case 1)\n      // It is possible to perform a naive shift\n      ndiffs.splice(cursor_pointer, 2, d_next, d)\n      return merge_tuples(ndiffs, cursor_pointer, 2)\n    } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) {\n      // Case 2)\n      // d[1] is a prefix of d_next[1]\n      // We can assume that d_next[0] !== 0, since d[0] === 0\n      // Shift edit locations..\n      ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]);\n      var suffix = d_next[1].slice(d[1].length);\n      if (suffix.length > 0) {\n        ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]);\n      }\n      return merge_tuples(ndiffs, cursor_pointer, 3)\n    } else {\n      // Not possible to perform any modification\n      return diffs;\n    }\n  }\n}\n\n/*\n * Check diff did not split surrogate pairs.\n * Ex. [0, '\\uD83D'], [-1, '\\uDC36'], [1, '\\uDC2F'] -> [-1, '\\uD83D\\uDC36'], [1, '\\uD83D\\uDC2F']\n *     '\\uD83D\\uDC36' === '🐶', '\\uD83D\\uDC2F' === '🐯'\n *\n * @param {Array} diffs Array of diff tuples\n * @return {Array} Array of diff tuples\n */\nfunction fix_emoji (diffs) {\n  var compact = false;\n  var starts_with_pair_end = function(str) {\n    return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF;\n  }\n  var ends_with_pair_start = function(str) {\n    return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF;\n  }\n  for (var i = 2; i < diffs.length; i += 1) {\n    if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) &&\n        diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) &&\n        diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) {\n      compact = true;\n\n      diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1];\n      diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1];\n\n      diffs[i-2][1] = diffs[i-2][1].slice(0, -1);\n    }\n  }\n  if (!compact) {\n    return diffs;\n  }\n  var fixed_diffs = [];\n  for (var i = 0; i < diffs.length; i += 1) {\n    if (diffs[i][1].length > 0) {\n      fixed_diffs.push(diffs[i]);\n    }\n  }\n  return fixed_diffs;\n}\n\n/*\n * Try to merge tuples with their neigbors in a given range.\n * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab']\n *\n * @param {Array} diffs Array of diff tuples.\n * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]).\n * @param {Int} length Number of consecutive elements to check.\n * @return {Array} Array of merged diff tuples.\n */\nfunction merge_tuples (diffs, start, length) {\n  // Check from (start-1) to (start+length).\n  for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) {\n    if (i + 1 < diffs.length) {\n      var left_d = diffs[i];\n      var right_d = diffs[i+1];\n      if (left_d[0] === right_d[1]) {\n        diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]);\n      }\n    }\n  }\n  return diffs;\n}\n\n\n/***/ }),\n/* 52 */\n/***/ (function(module, exports) {\n\nexports = module.exports = typeof Object.keys === 'function'\n  ? Object.keys : shim;\n\nexports.shim = shim;\nfunction shim (obj) {\n  var keys = [];\n  for (var key in obj) keys.push(key);\n  return keys;\n}\n\n\n/***/ }),\n/* 53 */\n/***/ (function(module, exports) {\n\nvar supportsArgumentsClass = (function(){\n  return Object.prototype.toString.call(arguments)\n})() == '[object Arguments]';\n\nexports = module.exports = supportsArgumentsClass ? supported : unsupported;\n\nexports.supported = supported;\nfunction supported(object) {\n  return Object.prototype.toString.call(object) == '[object Arguments]';\n};\n\nexports.unsupported = unsupported;\nfunction unsupported(object){\n  return object &&\n    typeof object == 'object' &&\n    typeof object.length == 'number' &&\n    Object.prototype.hasOwnProperty.call(object, 'callee') &&\n    !Object.prototype.propertyIsEnumerable.call(object, 'callee') ||\n    false;\n};\n\n\n/***/ }),\n/* 54 */\n/***/ (function(module, exports) {\n\n'use strict';\n\nvar has = Object.prototype.hasOwnProperty\n  , prefix = '~';\n\n/**\n * Constructor to create a storage for our `EE` objects.\n * An `Events` instance is a plain object whose properties are event names.\n *\n * @constructor\n * @api private\n */\nfunction Events() {}\n\n//\n// We try to not inherit from `Object.prototype`. In some engines creating an\n// instance in this way is faster than calling `Object.create(null)` directly.\n// If `Object.create(null)` is not supported we prefix the event names with a\n// character to make sure that the built-in object properties are not\n// overridden or used as an attack vector.\n//\nif (Object.create) {\n  Events.prototype = Object.create(null);\n\n  //\n  // This hack is needed because the `__proto__` property is still inherited in\n  // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n  //\n  if (!new Events().__proto__) prefix = false;\n}\n\n/**\n * Representation of a single event listener.\n *\n * @param {Function} fn The listener function.\n * @param {Mixed} context The context to invoke the listener with.\n * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n * @constructor\n * @api private\n */\nfunction EE(fn, context, once) {\n  this.fn = fn;\n  this.context = context;\n  this.once = once || false;\n}\n\n/**\n * Minimal `EventEmitter` interface that is molded against the Node.js\n * `EventEmitter` interface.\n *\n * @constructor\n * @api public\n */\nfunction EventEmitter() {\n  this._events = new Events();\n  this._eventsCount = 0;\n}\n\n/**\n * Return an array listing the events for which the emitter has registered\n * listeners.\n *\n * @returns {Array}\n * @api public\n */\nEventEmitter.prototype.eventNames = function eventNames() {\n  var names = []\n    , events\n    , name;\n\n  if (this._eventsCount === 0) return names;\n\n  for (name in (events = this._events)) {\n    if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n  }\n\n  if (Object.getOwnPropertySymbols) {\n    return names.concat(Object.getOwnPropertySymbols(events));\n  }\n\n  return names;\n};\n\n/**\n * Return the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Boolean} exists Only check if there are listeners.\n * @returns {Array|Boolean}\n * @api public\n */\nEventEmitter.prototype.listeners = function listeners(event, exists) {\n  var evt = prefix ? prefix + event : event\n    , available = this._events[evt];\n\n  if (exists) return !!available;\n  if (!available) return [];\n  if (available.fn) return [available.fn];\n\n  for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {\n    ee[i] = available[i].fn;\n  }\n\n  return ee;\n};\n\n/**\n * Calls each of the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @returns {Boolean} `true` if the event had listeners, else `false`.\n * @api public\n */\nEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n  var evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) return false;\n\n  var listeners = this._events[evt]\n    , len = arguments.length\n    , args\n    , i;\n\n  if (listeners.fn) {\n    if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n    switch (len) {\n      case 1: return listeners.fn.call(listeners.context), true;\n      case 2: return listeners.fn.call(listeners.context, a1), true;\n      case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n      case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n      case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n      case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n    }\n\n    for (i = 1, args = new Array(len -1); i < len; i++) {\n      args[i - 1] = arguments[i];\n    }\n\n    listeners.fn.apply(listeners.context, args);\n  } else {\n    var length = listeners.length\n      , j;\n\n    for (i = 0; i < length; i++) {\n      if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n      switch (len) {\n        case 1: listeners[i].fn.call(listeners[i].context); break;\n        case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n        case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n        case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n        default:\n          if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n            args[j - 1] = arguments[j];\n          }\n\n          listeners[i].fn.apply(listeners[i].context, args);\n      }\n    }\n  }\n\n  return true;\n};\n\n/**\n * Add a listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.on = function on(event, fn, context) {\n  var listener = new EE(fn, context || this)\n    , evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n  else if (!this._events[evt].fn) this._events[evt].push(listener);\n  else this._events[evt] = [this._events[evt], listener];\n\n  return this;\n};\n\n/**\n * Add a one-time listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.once = function once(event, fn, context) {\n  var listener = new EE(fn, context || this, true)\n    , evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n  else if (!this._events[evt].fn) this._events[evt].push(listener);\n  else this._events[evt] = [this._events[evt], listener];\n\n  return this;\n};\n\n/**\n * Remove the listeners of a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn Only remove the listeners that match this function.\n * @param {Mixed} context Only remove the listeners that have this context.\n * @param {Boolean} once Only remove one-time listeners.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n  var evt = prefix ? prefix + event : event;\n\n  if (!this._events[evt]) return this;\n  if (!fn) {\n    if (--this._eventsCount === 0) this._events = new Events();\n    else delete this._events[evt];\n    return this;\n  }\n\n  var listeners = this._events[evt];\n\n  if (listeners.fn) {\n    if (\n         listeners.fn === fn\n      && (!once || listeners.once)\n      && (!context || listeners.context === context)\n    ) {\n      if (--this._eventsCount === 0) this._events = new Events();\n      else delete this._events[evt];\n    }\n  } else {\n    for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n      if (\n           listeners[i].fn !== fn\n        || (once && !listeners[i].once)\n        || (context && listeners[i].context !== context)\n      ) {\n        events.push(listeners[i]);\n      }\n    }\n\n    //\n    // Reset the array, or remove it completely if we have no more listeners.\n    //\n    if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n    else if (--this._eventsCount === 0) this._events = new Events();\n    else delete this._events[evt];\n  }\n\n  return this;\n};\n\n/**\n * Remove all listeners, or those of the specified event.\n *\n * @param {String|Symbol} [event] The event name.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n  var evt;\n\n  if (event) {\n    evt = prefix ? prefix + event : event;\n    if (this._events[evt]) {\n      if (--this._eventsCount === 0) this._events = new Events();\n      else delete this._events[evt];\n    }\n  } else {\n    this._events = new Events();\n    this._eventsCount = 0;\n  }\n\n  return this;\n};\n\n//\n// Alias methods names because people roll like that.\n//\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\nEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n//\n// This function doesn't apply anymore.\n//\nEventEmitter.prototype.setMaxListeners = function setMaxListeners() {\n  return this;\n};\n\n//\n// Expose the prefix.\n//\nEventEmitter.prefixed = prefix;\n\n//\n// Allow `EventEmitter` to be imported as module namespace.\n//\nEventEmitter.EventEmitter = EventEmitter;\n\n//\n// Expose the module.\n//\nif ('undefined' !== typeof module) {\n  module.exports = EventEmitter;\n}\n\n\n/***/ }),\n/* 55 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.matchText = exports.matchSpacing = exports.matchNewline = exports.matchBlot = exports.matchAttributor = exports.default = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _extend2 = __webpack_require__(3);\n\nvar _extend3 = _interopRequireDefault(_extend2);\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _align = __webpack_require__(36);\n\nvar _background = __webpack_require__(37);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _color = __webpack_require__(26);\n\nvar _direction = __webpack_require__(38);\n\nvar _font = __webpack_require__(39);\n\nvar _size = __webpack_require__(40);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:clipboard');\n\nvar DOM_KEY = '__ql-matcher';\n\nvar CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]];\n\nvar ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) {\n  memo[attr.keyName] = attr;\n  return memo;\n}, {});\n\nvar STYLE_ATTRIBUTORS = [_align.AlignStyle, _background.BackgroundStyle, _color.ColorStyle, _direction.DirectionStyle, _font.FontStyle, _size.SizeStyle].reduce(function (memo, attr) {\n  memo[attr.keyName] = attr;\n  return memo;\n}, {});\n\nvar Clipboard = function (_Module) {\n  _inherits(Clipboard, _Module);\n\n  function Clipboard(quill, options) {\n    _classCallCheck(this, Clipboard);\n\n    var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options));\n\n    _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this));\n    _this.container = _this.quill.addContainer('ql-clipboard');\n    _this.container.setAttribute('contenteditable', true);\n    _this.container.setAttribute('tabindex', -1);\n    _this.matchers = [];\n    CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) {\n      var _ref2 = _slicedToArray(_ref, 2),\n          selector = _ref2[0],\n          matcher = _ref2[1];\n\n      if (!options.matchVisual && matcher === matchSpacing) return;\n      _this.addMatcher(selector, matcher);\n    });\n    return _this;\n  }\n\n  _createClass(Clipboard, [{\n    key: 'addMatcher',\n    value: function addMatcher(selector, matcher) {\n      this.matchers.push([selector, matcher]);\n    }\n  }, {\n    key: 'convert',\n    value: function convert(html) {\n      if (typeof html === 'string') {\n        this.container.innerHTML = html.replace(/\\>\\r?\\n +\\</g, '><'); // Remove spaces between tags\n        return this.convert();\n      }\n      var formats = this.quill.getFormat(this.quill.selection.savedRange.index);\n      if (formats[_code2.default.blotName]) {\n        var text = this.container.innerText;\n        this.container.innerHTML = '';\n        return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName]));\n      }\n\n      var _prepareMatching = this.prepareMatching(),\n          _prepareMatching2 = _slicedToArray(_prepareMatching, 2),\n          elementMatchers = _prepareMatching2[0],\n          textMatchers = _prepareMatching2[1];\n\n      var delta = traverse(this.container, elementMatchers, textMatchers);\n      // Remove trailing newline\n      if (deltaEndsWith(delta, '\\n') && delta.ops[delta.ops.length - 1].attributes == null) {\n        delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1));\n      }\n      debug.log('convert', this.container.innerHTML, delta);\n      this.container.innerHTML = '';\n      return delta;\n    }\n  }, {\n    key: 'dangerouslyPasteHTML',\n    value: function dangerouslyPasteHTML(index, html) {\n      var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _quill2.default.sources.API;\n\n      if (typeof index === 'string') {\n        this.quill.setContents(this.convert(index), html);\n        this.quill.setSelection(0, _quill2.default.sources.SILENT);\n      } else {\n        var paste = this.convert(html);\n        this.quill.updateContents(new _quillDelta2.default().retain(index).concat(paste), source);\n        this.quill.setSelection(index + paste.length(), _quill2.default.sources.SILENT);\n      }\n    }\n  }, {\n    key: 'onPaste',\n    value: function onPaste(e) {\n      var _this2 = this;\n\n      if (e.defaultPrevented || !this.quill.isEnabled()) return;\n      var range = this.quill.getSelection();\n      var delta = new _quillDelta2.default().retain(range.index);\n      var scrollTop = this.quill.scrollingContainer.scrollTop;\n      this.container.focus();\n      this.quill.selection.update(_quill2.default.sources.SILENT);\n      setTimeout(function () {\n        delta = delta.concat(_this2.convert()).delete(range.length);\n        _this2.quill.updateContents(delta, _quill2.default.sources.USER);\n        // range.length contributes to delta.length()\n        _this2.quill.setSelection(delta.length() - range.length, _quill2.default.sources.SILENT);\n        _this2.quill.scrollingContainer.scrollTop = scrollTop;\n        _this2.quill.focus();\n      }, 1);\n    }\n  }, {\n    key: 'prepareMatching',\n    value: function prepareMatching() {\n      var _this3 = this;\n\n      var elementMatchers = [],\n          textMatchers = [];\n      this.matchers.forEach(function (pair) {\n        var _pair = _slicedToArray(pair, 2),\n            selector = _pair[0],\n            matcher = _pair[1];\n\n        switch (selector) {\n          case Node.TEXT_NODE:\n            textMatchers.push(matcher);\n            break;\n          case Node.ELEMENT_NODE:\n            elementMatchers.push(matcher);\n            break;\n          default:\n            [].forEach.call(_this3.container.querySelectorAll(selector), function (node) {\n              // TODO use weakmap\n              node[DOM_KEY] = node[DOM_KEY] || [];\n              node[DOM_KEY].push(matcher);\n            });\n            break;\n        }\n      });\n      return [elementMatchers, textMatchers];\n    }\n  }]);\n\n  return Clipboard;\n}(_module2.default);\n\nClipboard.DEFAULTS = {\n  matchers: [],\n  matchVisual: true\n};\n\nfunction applyFormat(delta, format, value) {\n  if ((typeof format === 'undefined' ? 'undefined' : _typeof(format)) === 'object') {\n    return Object.keys(format).reduce(function (delta, key) {\n      return applyFormat(delta, key, format[key]);\n    }, delta);\n  } else {\n    return delta.reduce(function (delta, op) {\n      if (op.attributes && op.attributes[format]) {\n        return delta.push(op);\n      } else {\n        return delta.insert(op.insert, (0, _extend3.default)({}, _defineProperty({}, format, value), op.attributes));\n      }\n    }, new _quillDelta2.default());\n  }\n}\n\nfunction computeStyle(node) {\n  if (node.nodeType !== Node.ELEMENT_NODE) return {};\n  var DOM_KEY = '__ql-computed-style';\n  return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));\n}\n\nfunction deltaEndsWith(delta, text) {\n  var endText = \"\";\n  for (var i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {\n    var op = delta.ops[i];\n    if (typeof op.insert !== 'string') break;\n    endText = op.insert + endText;\n  }\n  return endText.slice(-1 * text.length) === text;\n}\n\nfunction isLine(node) {\n  if (node.childNodes.length === 0) return false; // Exclude embed blocks\n  var style = computeStyle(node);\n  return ['block', 'list-item'].indexOf(style.display) > -1;\n}\n\nfunction traverse(node, elementMatchers, textMatchers) {\n  // Post-order\n  if (node.nodeType === node.TEXT_NODE) {\n    return textMatchers.reduce(function (delta, matcher) {\n      return matcher(node, delta);\n    }, new _quillDelta2.default());\n  } else if (node.nodeType === node.ELEMENT_NODE) {\n    return [].reduce.call(node.childNodes || [], function (delta, childNode) {\n      var childrenDelta = traverse(childNode, elementMatchers, textMatchers);\n      if (childNode.nodeType === node.ELEMENT_NODE) {\n        childrenDelta = elementMatchers.reduce(function (childrenDelta, matcher) {\n          return matcher(childNode, childrenDelta);\n        }, childrenDelta);\n        childrenDelta = (childNode[DOM_KEY] || []).reduce(function (childrenDelta, matcher) {\n          return matcher(childNode, childrenDelta);\n        }, childrenDelta);\n      }\n      return delta.concat(childrenDelta);\n    }, new _quillDelta2.default());\n  } else {\n    return new _quillDelta2.default();\n  }\n}\n\nfunction matchAlias(format, node, delta) {\n  return applyFormat(delta, format, true);\n}\n\nfunction matchAttributor(node, delta) {\n  var attributes = _parchment2.default.Attributor.Attribute.keys(node);\n  var classes = _parchment2.default.Attributor.Class.keys(node);\n  var styles = _parchment2.default.Attributor.Style.keys(node);\n  var formats = {};\n  attributes.concat(classes).concat(styles).forEach(function (name) {\n    var attr = _parchment2.default.query(name, _parchment2.default.Scope.ATTRIBUTE);\n    if (attr != null) {\n      formats[attr.attrName] = attr.value(node);\n      if (formats[attr.attrName]) return;\n    }\n    attr = ATTRIBUTE_ATTRIBUTORS[name];\n    if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n      formats[attr.attrName] = attr.value(node) || undefined;\n    }\n    attr = STYLE_ATTRIBUTORS[name];\n    if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n      attr = STYLE_ATTRIBUTORS[name];\n      formats[attr.attrName] = attr.value(node) || undefined;\n    }\n  });\n  if (Object.keys(formats).length > 0) {\n    delta = applyFormat(delta, formats);\n  }\n  return delta;\n}\n\nfunction matchBlot(node, delta) {\n  var match = _parchment2.default.query(node);\n  if (match == null) return delta;\n  if (match.prototype instanceof _parchment2.default.Embed) {\n    var embed = {};\n    var value = match.value(node);\n    if (value != null) {\n      embed[match.blotName] = value;\n      delta = new _quillDelta2.default().insert(embed, match.formats(node));\n    }\n  } else if (typeof match.formats === 'function') {\n    delta = applyFormat(delta, match.blotName, match.formats(node));\n  }\n  return delta;\n}\n\nfunction matchBreak(node, delta) {\n  if (!deltaEndsWith(delta, '\\n')) {\n    delta.insert('\\n');\n  }\n  return delta;\n}\n\nfunction matchIgnore() {\n  return new _quillDelta2.default();\n}\n\nfunction matchIndent(node, delta) {\n  var match = _parchment2.default.query(node);\n  if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\\n')) {\n    return delta;\n  }\n  var indent = -1,\n      parent = node.parentNode;\n  while (!parent.classList.contains('ql-clipboard')) {\n    if ((_parchment2.default.query(parent) || {}).blotName === 'list') {\n      indent += 1;\n    }\n    parent = parent.parentNode;\n  }\n  if (indent <= 0) return delta;\n  return delta.compose(new _quillDelta2.default().retain(delta.length() - 1).retain(1, { indent: indent }));\n}\n\nfunction matchNewline(node, delta) {\n  if (!deltaEndsWith(delta, '\\n')) {\n    if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) {\n      delta.insert('\\n');\n    }\n  }\n  return delta;\n}\n\nfunction matchSpacing(node, delta) {\n  if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\\n\\n')) {\n    var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);\n    if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) {\n      delta.insert('\\n');\n    }\n  }\n  return delta;\n}\n\nfunction matchStyles(node, delta) {\n  var formats = {};\n  var style = node.style || {};\n  if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {\n    formats.italic = true;\n  }\n  if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) {\n    formats.bold = true;\n  }\n  if (Object.keys(formats).length > 0) {\n    delta = applyFormat(delta, formats);\n  }\n  if (parseFloat(style.textIndent || 0) > 0) {\n    // Could be 0.5in\n    delta = new _quillDelta2.default().insert('\\t').concat(delta);\n  }\n  return delta;\n}\n\nfunction matchText(node, delta) {\n  var text = node.data;\n  // Word represents empty line with <o:p>&nbsp;</o:p>\n  if (node.parentNode.tagName === 'O:P') {\n    return delta.insert(text.trim());\n  }\n  if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) {\n    return delta;\n  }\n  if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {\n    // eslint-disable-next-line func-style\n    var replacer = function replacer(collapse, match) {\n      match = match.replace(/[^\\u00a0]/g, ''); // \\u00a0 is nbsp;\n      return match.length < 1 && collapse ? ' ' : match;\n    };\n    text = text.replace(/\\r\\n/g, ' ').replace(/\\n/g, ' ');\n    text = text.replace(/\\s\\s+/g, replacer.bind(replacer, true)); // collapse whitespace\n    if (node.previousSibling == null && isLine(node.parentNode) || node.previousSibling != null && isLine(node.previousSibling)) {\n      text = text.replace(/^\\s+/, replacer.bind(replacer, false));\n    }\n    if (node.nextSibling == null && isLine(node.parentNode) || node.nextSibling != null && isLine(node.nextSibling)) {\n      text = text.replace(/\\s+$/, replacer.bind(replacer, false));\n    }\n  }\n  return delta.insert(text);\n}\n\nexports.default = Clipboard;\nexports.matchAttributor = matchAttributor;\nexports.matchBlot = matchBlot;\nexports.matchNewline = matchNewline;\nexports.matchSpacing = matchSpacing;\nexports.matchText = matchText;\n\n/***/ }),\n/* 56 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Bold = function (_Inline) {\n  _inherits(Bold, _Inline);\n\n  function Bold() {\n    _classCallCheck(this, Bold);\n\n    return _possibleConstructorReturn(this, (Bold.__proto__ || Object.getPrototypeOf(Bold)).apply(this, arguments));\n  }\n\n  _createClass(Bold, [{\n    key: 'optimize',\n    value: function optimize(context) {\n      _get(Bold.prototype.__proto__ || Object.getPrototypeOf(Bold.prototype), 'optimize', this).call(this, context);\n      if (this.domNode.tagName !== this.statics.tagName[0]) {\n        this.replaceWith(this.statics.blotName);\n      }\n    }\n  }], [{\n    key: 'create',\n    value: function create() {\n      return _get(Bold.__proto__ || Object.getPrototypeOf(Bold), 'create', this).call(this);\n    }\n  }, {\n    key: 'formats',\n    value: function formats() {\n      return true;\n    }\n  }]);\n\n  return Bold;\n}(_inline2.default);\n\nBold.blotName = 'bold';\nBold.tagName = ['STRONG', 'B'];\n\nexports.default = Bold;\n\n/***/ }),\n/* 57 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.addControls = exports.default = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _quillDelta = __webpack_require__(2);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:toolbar');\n\nvar Toolbar = function (_Module) {\n  _inherits(Toolbar, _Module);\n\n  function Toolbar(quill, options) {\n    _classCallCheck(this, Toolbar);\n\n    var _this = _possibleConstructorReturn(this, (Toolbar.__proto__ || Object.getPrototypeOf(Toolbar)).call(this, quill, options));\n\n    if (Array.isArray(_this.options.container)) {\n      var container = document.createElement('div');\n      addControls(container, _this.options.container);\n      quill.container.parentNode.insertBefore(container, quill.container);\n      _this.container = container;\n    } else if (typeof _this.options.container === 'string') {\n      _this.container = document.querySelector(_this.options.container);\n    } else {\n      _this.container = _this.options.container;\n    }\n    if (!(_this.container instanceof HTMLElement)) {\n      var _ret;\n\n      return _ret = debug.error('Container required for toolbar', _this.options), _possibleConstructorReturn(_this, _ret);\n    }\n    _this.container.classList.add('ql-toolbar');\n    _this.controls = [];\n    _this.handlers = {};\n    Object.keys(_this.options.handlers).forEach(function (format) {\n      _this.addHandler(format, _this.options.handlers[format]);\n    });\n    [].forEach.call(_this.container.querySelectorAll('button, select'), function (input) {\n      _this.attach(input);\n    });\n    _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (type, range) {\n      if (type === _quill2.default.events.SELECTION_CHANGE) {\n        _this.update(range);\n      }\n    });\n    _this.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () {\n      var _this$quill$selection = _this.quill.selection.getRange(),\n          _this$quill$selection2 = _slicedToArray(_this$quill$selection, 1),\n          range = _this$quill$selection2[0]; // quill.getSelection triggers update\n\n\n      _this.update(range);\n    });\n    return _this;\n  }\n\n  _createClass(Toolbar, [{\n    key: 'addHandler',\n    value: function addHandler(format, handler) {\n      this.handlers[format] = handler;\n    }\n  }, {\n    key: 'attach',\n    value: function attach(input) {\n      var _this2 = this;\n\n      var format = [].find.call(input.classList, function (className) {\n        return className.indexOf('ql-') === 0;\n      });\n      if (!format) return;\n      format = format.slice('ql-'.length);\n      if (input.tagName === 'BUTTON') {\n        input.setAttribute('type', 'button');\n      }\n      if (this.handlers[format] == null) {\n        if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) {\n          debug.warn('ignoring attaching to disabled format', format, input);\n          return;\n        }\n        if (_parchment2.default.query(format) == null) {\n          debug.warn('ignoring attaching to nonexistent format', format, input);\n          return;\n        }\n      }\n      var eventName = input.tagName === 'SELECT' ? 'change' : 'click';\n      input.addEventListener(eventName, function (e) {\n        var value = void 0;\n        if (input.tagName === 'SELECT') {\n          if (input.selectedIndex < 0) return;\n          var selected = input.options[input.selectedIndex];\n          if (selected.hasAttribute('selected')) {\n            value = false;\n          } else {\n            value = selected.value || false;\n          }\n        } else {\n          if (input.classList.contains('ql-active')) {\n            value = false;\n          } else {\n            value = input.value || !input.hasAttribute('value');\n          }\n          e.preventDefault();\n        }\n        _this2.quill.focus();\n\n        var _quill$selection$getR = _this2.quill.selection.getRange(),\n            _quill$selection$getR2 = _slicedToArray(_quill$selection$getR, 1),\n            range = _quill$selection$getR2[0];\n\n        if (_this2.handlers[format] != null) {\n          _this2.handlers[format].call(_this2, value);\n        } else if (_parchment2.default.query(format).prototype instanceof _parchment2.default.Embed) {\n          value = prompt('Enter ' + format);\n          if (!value) return;\n          _this2.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert(_defineProperty({}, format, value)), _quill2.default.sources.USER);\n        } else {\n          _this2.quill.format(format, value, _quill2.default.sources.USER);\n        }\n        _this2.update(range);\n      });\n      // TODO use weakmap\n      this.controls.push([format, input]);\n    }\n  }, {\n    key: 'update',\n    value: function update(range) {\n      var formats = range == null ? {} : this.quill.getFormat(range);\n      this.controls.forEach(function (pair) {\n        var _pair = _slicedToArray(pair, 2),\n            format = _pair[0],\n            input = _pair[1];\n\n        if (input.tagName === 'SELECT') {\n          var option = void 0;\n          if (range == null) {\n            option = null;\n          } else if (formats[format] == null) {\n            option = input.querySelector('option[selected]');\n          } else if (!Array.isArray(formats[format])) {\n            var value = formats[format];\n            if (typeof value === 'string') {\n              value = value.replace(/\\\"/g, '\\\\\"');\n            }\n            option = input.querySelector('option[value=\"' + value + '\"]');\n          }\n          if (option == null) {\n            input.value = ''; // TODO make configurable?\n            input.selectedIndex = -1;\n          } else {\n            option.selected = true;\n          }\n        } else {\n          if (range == null) {\n            input.classList.remove('ql-active');\n          } else if (input.hasAttribute('value')) {\n            // both being null should match (default values)\n            // '1' should match with 1 (headers)\n            var isActive = formats[format] === input.getAttribute('value') || formats[format] != null && formats[format].toString() === input.getAttribute('value') || formats[format] == null && !input.getAttribute('value');\n            input.classList.toggle('ql-active', isActive);\n          } else {\n            input.classList.toggle('ql-active', formats[format] != null);\n          }\n        }\n      });\n    }\n  }]);\n\n  return Toolbar;\n}(_module2.default);\n\nToolbar.DEFAULTS = {};\n\nfunction addButton(container, format, value) {\n  var input = document.createElement('button');\n  input.setAttribute('type', 'button');\n  input.classList.add('ql-' + format);\n  if (value != null) {\n    input.value = value;\n  }\n  container.appendChild(input);\n}\n\nfunction addControls(container, groups) {\n  if (!Array.isArray(groups[0])) {\n    groups = [groups];\n  }\n  groups.forEach(function (controls) {\n    var group = document.createElement('span');\n    group.classList.add('ql-formats');\n    controls.forEach(function (control) {\n      if (typeof control === 'string') {\n        addButton(group, control);\n      } else {\n        var format = Object.keys(control)[0];\n        var value = control[format];\n        if (Array.isArray(value)) {\n          addSelect(group, format, value);\n        } else {\n          addButton(group, format, value);\n        }\n      }\n    });\n    container.appendChild(group);\n  });\n}\n\nfunction addSelect(container, format, values) {\n  var input = document.createElement('select');\n  input.classList.add('ql-' + format);\n  values.forEach(function (value) {\n    var option = document.createElement('option');\n    if (value !== false) {\n      option.setAttribute('value', value);\n    } else {\n      option.setAttribute('selected', 'selected');\n    }\n    input.appendChild(option);\n  });\n  container.appendChild(input);\n}\n\nToolbar.DEFAULTS = {\n  container: null,\n  handlers: {\n    clean: function clean() {\n      var _this3 = this;\n\n      var range = this.quill.getSelection();\n      if (range == null) return;\n      if (range.length == 0) {\n        var formats = this.quill.getFormat();\n        Object.keys(formats).forEach(function (name) {\n          // Clean functionality in existing apps only clean inline formats\n          if (_parchment2.default.query(name, _parchment2.default.Scope.INLINE) != null) {\n            _this3.quill.format(name, false);\n          }\n        });\n      } else {\n        this.quill.removeFormat(range, _quill2.default.sources.USER);\n      }\n    },\n    direction: function direction(value) {\n      var align = this.quill.getFormat()['align'];\n      if (value === 'rtl' && align == null) {\n        this.quill.format('align', 'right', _quill2.default.sources.USER);\n      } else if (!value && align === 'right') {\n        this.quill.format('align', false, _quill2.default.sources.USER);\n      }\n      this.quill.format('direction', value, _quill2.default.sources.USER);\n    },\n    indent: function indent(value) {\n      var range = this.quill.getSelection();\n      var formats = this.quill.getFormat(range);\n      var indent = parseInt(formats.indent || 0);\n      if (value === '+1' || value === '-1') {\n        var modifier = value === '+1' ? 1 : -1;\n        if (formats.direction === 'rtl') modifier *= -1;\n        this.quill.format('indent', indent + modifier, _quill2.default.sources.USER);\n      }\n    },\n    link: function link(value) {\n      if (value === true) {\n        value = prompt('Enter link URL:');\n      }\n      this.quill.format('link', value, _quill2.default.sources.USER);\n    },\n    list: function list(value) {\n      var range = this.quill.getSelection();\n      var formats = this.quill.getFormat(range);\n      if (value === 'check') {\n        if (formats['list'] === 'checked' || formats['list'] === 'unchecked') {\n          this.quill.format('list', false, _quill2.default.sources.USER);\n        } else {\n          this.quill.format('list', 'unchecked', _quill2.default.sources.USER);\n        }\n      } else {\n        this.quill.format('list', value, _quill2.default.sources.USER);\n      }\n    }\n  }\n};\n\nexports.default = Toolbar;\nexports.addControls = addControls;\n\n/***/ }),\n/* 58 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <polyline class=\\\"ql-even ql-stroke\\\" points=\\\"5 7 3 9 5 11\\\"></polyline> <polyline class=\\\"ql-even ql-stroke\\\" points=\\\"13 7 15 9 13 11\\\"></polyline> <line class=ql-stroke x1=10 x2=8 y1=5 y2=13></line> </svg>\";\n\n/***/ }),\n/* 59 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _picker = __webpack_require__(28);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ColorPicker = function (_Picker) {\n  _inherits(ColorPicker, _Picker);\n\n  function ColorPicker(select, label) {\n    _classCallCheck(this, ColorPicker);\n\n    var _this = _possibleConstructorReturn(this, (ColorPicker.__proto__ || Object.getPrototypeOf(ColorPicker)).call(this, select));\n\n    _this.label.innerHTML = label;\n    _this.container.classList.add('ql-color-picker');\n    [].slice.call(_this.container.querySelectorAll('.ql-picker-item'), 0, 7).forEach(function (item) {\n      item.classList.add('ql-primary');\n    });\n    return _this;\n  }\n\n  _createClass(ColorPicker, [{\n    key: 'buildItem',\n    value: function buildItem(option) {\n      var item = _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'buildItem', this).call(this, option);\n      item.style.backgroundColor = option.getAttribute('value') || '';\n      return item;\n    }\n  }, {\n    key: 'selectItem',\n    value: function selectItem(item, trigger) {\n      _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'selectItem', this).call(this, item, trigger);\n      var colorLabel = this.label.querySelector('.ql-color-label');\n      var value = item ? item.getAttribute('data-value') || '' : '';\n      if (colorLabel) {\n        if (colorLabel.tagName === 'line') {\n          colorLabel.style.stroke = value;\n        } else {\n          colorLabel.style.fill = value;\n        }\n      }\n    }\n  }]);\n\n  return ColorPicker;\n}(_picker2.default);\n\nexports.default = ColorPicker;\n\n/***/ }),\n/* 60 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _picker = __webpack_require__(28);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar IconPicker = function (_Picker) {\n  _inherits(IconPicker, _Picker);\n\n  function IconPicker(select, icons) {\n    _classCallCheck(this, IconPicker);\n\n    var _this = _possibleConstructorReturn(this, (IconPicker.__proto__ || Object.getPrototypeOf(IconPicker)).call(this, select));\n\n    _this.container.classList.add('ql-icon-picker');\n    [].forEach.call(_this.container.querySelectorAll('.ql-picker-item'), function (item) {\n      item.innerHTML = icons[item.getAttribute('data-value') || ''];\n    });\n    _this.defaultItem = _this.container.querySelector('.ql-selected');\n    _this.selectItem(_this.defaultItem);\n    return _this;\n  }\n\n  _createClass(IconPicker, [{\n    key: 'selectItem',\n    value: function selectItem(item, trigger) {\n      _get(IconPicker.prototype.__proto__ || Object.getPrototypeOf(IconPicker.prototype), 'selectItem', this).call(this, item, trigger);\n      item = item || this.defaultItem;\n      this.label.innerHTML = item.innerHTML;\n    }\n  }]);\n\n  return IconPicker;\n}(_picker2.default);\n\nexports.default = IconPicker;\n\n/***/ }),\n/* 61 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Tooltip = function () {\n  function Tooltip(quill, boundsContainer) {\n    var _this = this;\n\n    _classCallCheck(this, Tooltip);\n\n    this.quill = quill;\n    this.boundsContainer = boundsContainer || document.body;\n    this.root = quill.addContainer('ql-tooltip');\n    this.root.innerHTML = this.constructor.TEMPLATE;\n    if (this.quill.root === this.quill.scrollingContainer) {\n      this.quill.root.addEventListener('scroll', function () {\n        _this.root.style.marginTop = -1 * _this.quill.root.scrollTop + 'px';\n      });\n    }\n    this.hide();\n  }\n\n  _createClass(Tooltip, [{\n    key: 'hide',\n    value: function hide() {\n      this.root.classList.add('ql-hidden');\n    }\n  }, {\n    key: 'position',\n    value: function position(reference) {\n      var left = reference.left + reference.width / 2 - this.root.offsetWidth / 2;\n      // root.scrollTop should be 0 if scrollContainer !== root\n      var top = reference.bottom + this.quill.root.scrollTop;\n      this.root.style.left = left + 'px';\n      this.root.style.top = top + 'px';\n      this.root.classList.remove('ql-flip');\n      var containerBounds = this.boundsContainer.getBoundingClientRect();\n      var rootBounds = this.root.getBoundingClientRect();\n      var shift = 0;\n      if (rootBounds.right > containerBounds.right) {\n        shift = containerBounds.right - rootBounds.right;\n        this.root.style.left = left + shift + 'px';\n      }\n      if (rootBounds.left < containerBounds.left) {\n        shift = containerBounds.left - rootBounds.left;\n        this.root.style.left = left + shift + 'px';\n      }\n      if (rootBounds.bottom > containerBounds.bottom) {\n        var height = rootBounds.bottom - rootBounds.top;\n        var verticalShift = reference.bottom - reference.top + height;\n        this.root.style.top = top - verticalShift + 'px';\n        this.root.classList.add('ql-flip');\n      }\n      return shift;\n    }\n  }, {\n    key: 'show',\n    value: function show() {\n      this.root.classList.remove('ql-editing');\n      this.root.classList.remove('ql-hidden');\n    }\n  }]);\n\n  return Tooltip;\n}();\n\nexports.default = Tooltip;\n\n/***/ }),\n/* 62 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _emitter = __webpack_require__(8);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _base = __webpack_require__(43);\n\nvar _base2 = _interopRequireDefault(_base);\n\nvar _link = __webpack_require__(27);\n\nvar _link2 = _interopRequireDefault(_link);\n\nvar _selection = __webpack_require__(15);\n\nvar _icons = __webpack_require__(41);\n\nvar _icons2 = _interopRequireDefault(_icons);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar TOOLBAR_CONFIG = [[{ header: ['1', '2', '3', false] }], ['bold', 'italic', 'underline', 'link'], [{ list: 'ordered' }, { list: 'bullet' }], ['clean']];\n\nvar SnowTheme = function (_BaseTheme) {\n  _inherits(SnowTheme, _BaseTheme);\n\n  function SnowTheme(quill, options) {\n    _classCallCheck(this, SnowTheme);\n\n    if (options.modules.toolbar != null && options.modules.toolbar.container == null) {\n      options.modules.toolbar.container = TOOLBAR_CONFIG;\n    }\n\n    var _this = _possibleConstructorReturn(this, (SnowTheme.__proto__ || Object.getPrototypeOf(SnowTheme)).call(this, quill, options));\n\n    _this.quill.container.classList.add('ql-snow');\n    return _this;\n  }\n\n  _createClass(SnowTheme, [{\n    key: 'extendToolbar',\n    value: function extendToolbar(toolbar) {\n      toolbar.container.classList.add('ql-snow');\n      this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default);\n      this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default);\n      this.tooltip = new SnowTooltip(this.quill, this.options.bounds);\n      if (toolbar.container.querySelector('.ql-link')) {\n        this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function (range, context) {\n          toolbar.handlers['link'].call(toolbar, !context.format.link);\n        });\n      }\n    }\n  }]);\n\n  return SnowTheme;\n}(_base2.default);\n\nSnowTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, {\n  modules: {\n    toolbar: {\n      handlers: {\n        link: function link(value) {\n          if (value) {\n            var range = this.quill.getSelection();\n            if (range == null || range.length == 0) return;\n            var preview = this.quill.getText(range);\n            if (/^\\S+@\\S+\\.\\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {\n              preview = 'mailto:' + preview;\n            }\n            var tooltip = this.quill.theme.tooltip;\n            tooltip.edit('link', preview);\n          } else {\n            this.quill.format('link', false);\n          }\n        }\n      }\n    }\n  }\n});\n\nvar SnowTooltip = function (_BaseTooltip) {\n  _inherits(SnowTooltip, _BaseTooltip);\n\n  function SnowTooltip(quill, bounds) {\n    _classCallCheck(this, SnowTooltip);\n\n    var _this2 = _possibleConstructorReturn(this, (SnowTooltip.__proto__ || Object.getPrototypeOf(SnowTooltip)).call(this, quill, bounds));\n\n    _this2.preview = _this2.root.querySelector('a.ql-preview');\n    return _this2;\n  }\n\n  _createClass(SnowTooltip, [{\n    key: 'listen',\n    value: function listen() {\n      var _this3 = this;\n\n      _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'listen', this).call(this);\n      this.root.querySelector('a.ql-action').addEventListener('click', function (event) {\n        if (_this3.root.classList.contains('ql-editing')) {\n          _this3.save();\n        } else {\n          _this3.edit('link', _this3.preview.textContent);\n        }\n        event.preventDefault();\n      });\n      this.root.querySelector('a.ql-remove').addEventListener('click', function (event) {\n        if (_this3.linkRange != null) {\n          var range = _this3.linkRange;\n          _this3.restoreFocus();\n          _this3.quill.formatText(range, 'link', false, _emitter2.default.sources.USER);\n          delete _this3.linkRange;\n        }\n        event.preventDefault();\n        _this3.hide();\n      });\n      this.quill.on(_emitter2.default.events.SELECTION_CHANGE, function (range, oldRange, source) {\n        if (range == null) return;\n        if (range.length === 0 && source === _emitter2.default.sources.USER) {\n          var _quill$scroll$descend = _this3.quill.scroll.descendant(_link2.default, range.index),\n              _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2),\n              link = _quill$scroll$descend2[0],\n              offset = _quill$scroll$descend2[1];\n\n          if (link != null) {\n            _this3.linkRange = new _selection.Range(range.index - offset, link.length());\n            var preview = _link2.default.formats(link.domNode);\n            _this3.preview.textContent = preview;\n            _this3.preview.setAttribute('href', preview);\n            _this3.show();\n            _this3.position(_this3.quill.getBounds(_this3.linkRange));\n            return;\n          }\n        } else {\n          delete _this3.linkRange;\n        }\n        _this3.hide();\n      });\n    }\n  }, {\n    key: 'show',\n    value: function show() {\n      _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'show', this).call(this);\n      this.root.removeAttribute('data-mode');\n    }\n  }]);\n\n  return SnowTooltip;\n}(_base.BaseTooltip);\n\nSnowTooltip.TEMPLATE = ['<a class=\"ql-preview\" rel=\"noopener noreferrer\" target=\"_blank\" href=\"about:blank\"></a>', '<input type=\"text\" data-formula=\"e=mc^2\" data-link=\"https://quilljs.com\" data-video=\"Embed URL\">', '<a class=\"ql-action\"></a>', '<a class=\"ql-remove\"></a>'].join('');\n\nexports.default = SnowTheme;\n\n/***/ }),\n/* 63 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _core = __webpack_require__(29);\n\nvar _core2 = _interopRequireDefault(_core);\n\nvar _align = __webpack_require__(36);\n\nvar _direction = __webpack_require__(38);\n\nvar _indent = __webpack_require__(64);\n\nvar _blockquote = __webpack_require__(65);\n\nvar _blockquote2 = _interopRequireDefault(_blockquote);\n\nvar _header = __webpack_require__(66);\n\nvar _header2 = _interopRequireDefault(_header);\n\nvar _list = __webpack_require__(67);\n\nvar _list2 = _interopRequireDefault(_list);\n\nvar _background = __webpack_require__(37);\n\nvar _color = __webpack_require__(26);\n\nvar _font = __webpack_require__(39);\n\nvar _size = __webpack_require__(40);\n\nvar _bold = __webpack_require__(56);\n\nvar _bold2 = _interopRequireDefault(_bold);\n\nvar _italic = __webpack_require__(68);\n\nvar _italic2 = _interopRequireDefault(_italic);\n\nvar _link = __webpack_require__(27);\n\nvar _link2 = _interopRequireDefault(_link);\n\nvar _script = __webpack_require__(69);\n\nvar _script2 = _interopRequireDefault(_script);\n\nvar _strike = __webpack_require__(70);\n\nvar _strike2 = _interopRequireDefault(_strike);\n\nvar _underline = __webpack_require__(71);\n\nvar _underline2 = _interopRequireDefault(_underline);\n\nvar _image = __webpack_require__(72);\n\nvar _image2 = _interopRequireDefault(_image);\n\nvar _video = __webpack_require__(73);\n\nvar _video2 = _interopRequireDefault(_video);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _formula = __webpack_require__(74);\n\nvar _formula2 = _interopRequireDefault(_formula);\n\nvar _syntax = __webpack_require__(75);\n\nvar _syntax2 = _interopRequireDefault(_syntax);\n\nvar _toolbar = __webpack_require__(57);\n\nvar _toolbar2 = _interopRequireDefault(_toolbar);\n\nvar _icons = __webpack_require__(41);\n\nvar _icons2 = _interopRequireDefault(_icons);\n\nvar _picker = __webpack_require__(28);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nvar _colorPicker = __webpack_require__(59);\n\nvar _colorPicker2 = _interopRequireDefault(_colorPicker);\n\nvar _iconPicker = __webpack_require__(60);\n\nvar _iconPicker2 = _interopRequireDefault(_iconPicker);\n\nvar _tooltip = __webpack_require__(61);\n\nvar _tooltip2 = _interopRequireDefault(_tooltip);\n\nvar _bubble = __webpack_require__(108);\n\nvar _bubble2 = _interopRequireDefault(_bubble);\n\nvar _snow = __webpack_require__(62);\n\nvar _snow2 = _interopRequireDefault(_snow);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_core2.default.register({\n  'attributors/attribute/direction': _direction.DirectionAttribute,\n\n  'attributors/class/align': _align.AlignClass,\n  'attributors/class/background': _background.BackgroundClass,\n  'attributors/class/color': _color.ColorClass,\n  'attributors/class/direction': _direction.DirectionClass,\n  'attributors/class/font': _font.FontClass,\n  'attributors/class/size': _size.SizeClass,\n\n  'attributors/style/align': _align.AlignStyle,\n  'attributors/style/background': _background.BackgroundStyle,\n  'attributors/style/color': _color.ColorStyle,\n  'attributors/style/direction': _direction.DirectionStyle,\n  'attributors/style/font': _font.FontStyle,\n  'attributors/style/size': _size.SizeStyle\n}, true);\n\n_core2.default.register({\n  'formats/align': _align.AlignClass,\n  'formats/direction': _direction.DirectionClass,\n  'formats/indent': _indent.IndentClass,\n\n  'formats/background': _background.BackgroundStyle,\n  'formats/color': _color.ColorStyle,\n  'formats/font': _font.FontClass,\n  'formats/size': _size.SizeClass,\n\n  'formats/blockquote': _blockquote2.default,\n  'formats/code-block': _code2.default,\n  'formats/header': _header2.default,\n  'formats/list': _list2.default,\n\n  'formats/bold': _bold2.default,\n  'formats/code': _code.Code,\n  'formats/italic': _italic2.default,\n  'formats/link': _link2.default,\n  'formats/script': _script2.default,\n  'formats/strike': _strike2.default,\n  'formats/underline': _underline2.default,\n\n  'formats/image': _image2.default,\n  'formats/video': _video2.default,\n\n  'formats/list/item': _list.ListItem,\n\n  'modules/formula': _formula2.default,\n  'modules/syntax': _syntax2.default,\n  'modules/toolbar': _toolbar2.default,\n\n  'themes/bubble': _bubble2.default,\n  'themes/snow': _snow2.default,\n\n  'ui/icons': _icons2.default,\n  'ui/picker': _picker2.default,\n  'ui/icon-picker': _iconPicker2.default,\n  'ui/color-picker': _colorPicker2.default,\n  'ui/tooltip': _tooltip2.default\n}, true);\n\nexports.default = _core2.default;\n\n/***/ }),\n/* 64 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.IndentClass = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar IdentAttributor = function (_Parchment$Attributor) {\n  _inherits(IdentAttributor, _Parchment$Attributor);\n\n  function IdentAttributor() {\n    _classCallCheck(this, IdentAttributor);\n\n    return _possibleConstructorReturn(this, (IdentAttributor.__proto__ || Object.getPrototypeOf(IdentAttributor)).apply(this, arguments));\n  }\n\n  _createClass(IdentAttributor, [{\n    key: 'add',\n    value: function add(node, value) {\n      if (value === '+1' || value === '-1') {\n        var indent = this.value(node) || 0;\n        value = value === '+1' ? indent + 1 : indent - 1;\n      }\n      if (value === 0) {\n        this.remove(node);\n        return true;\n      } else {\n        return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'add', this).call(this, node, value);\n      }\n    }\n  }, {\n    key: 'canAdd',\n    value: function canAdd(node, value) {\n      return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, value) || _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, parseInt(value));\n    }\n  }, {\n    key: 'value',\n    value: function value(node) {\n      return parseInt(_get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'value', this).call(this, node)) || undefined; // Don't return NaN\n    }\n  }]);\n\n  return IdentAttributor;\n}(_parchment2.default.Attributor.Class);\n\nvar IndentClass = new IdentAttributor('indent', 'ql-indent', {\n  scope: _parchment2.default.Scope.BLOCK,\n  whitelist: [1, 2, 3, 4, 5, 6, 7, 8]\n});\n\nexports.IndentClass = IndentClass;\n\n/***/ }),\n/* 65 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Blockquote = function (_Block) {\n  _inherits(Blockquote, _Block);\n\n  function Blockquote() {\n    _classCallCheck(this, Blockquote);\n\n    return _possibleConstructorReturn(this, (Blockquote.__proto__ || Object.getPrototypeOf(Blockquote)).apply(this, arguments));\n  }\n\n  return Blockquote;\n}(_block2.default);\n\nBlockquote.blotName = 'blockquote';\nBlockquote.tagName = 'blockquote';\n\nexports.default = Blockquote;\n\n/***/ }),\n/* 66 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Header = function (_Block) {\n  _inherits(Header, _Block);\n\n  function Header() {\n    _classCallCheck(this, Header);\n\n    return _possibleConstructorReturn(this, (Header.__proto__ || Object.getPrototypeOf(Header)).apply(this, arguments));\n  }\n\n  _createClass(Header, null, [{\n    key: 'formats',\n    value: function formats(domNode) {\n      return this.tagName.indexOf(domNode.tagName) + 1;\n    }\n  }]);\n\n  return Header;\n}(_block2.default);\n\nHeader.blotName = 'header';\nHeader.tagName = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];\n\nexports.default = Header;\n\n/***/ }),\n/* 67 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.ListItem = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(4);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _container = __webpack_require__(25);\n\nvar _container2 = _interopRequireDefault(_container);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ListItem = function (_Block) {\n  _inherits(ListItem, _Block);\n\n  function ListItem() {\n    _classCallCheck(this, ListItem);\n\n    return _possibleConstructorReturn(this, (ListItem.__proto__ || Object.getPrototypeOf(ListItem)).apply(this, arguments));\n  }\n\n  _createClass(ListItem, [{\n    key: 'format',\n    value: function format(name, value) {\n      if (name === List.blotName && !value) {\n        this.replaceWith(_parchment2.default.create(this.statics.scope));\n      } else {\n        _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'format', this).call(this, name, value);\n      }\n    }\n  }, {\n    key: 'remove',\n    value: function remove() {\n      if (this.prev == null && this.next == null) {\n        this.parent.remove();\n      } else {\n        _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'remove', this).call(this);\n      }\n    }\n  }, {\n    key: 'replaceWith',\n    value: function replaceWith(name, value) {\n      this.parent.isolate(this.offset(this.parent), this.length());\n      if (name === this.parent.statics.blotName) {\n        this.parent.replaceWith(name, value);\n        return this;\n      } else {\n        this.parent.unwrap();\n        return _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'replaceWith', this).call(this, name, value);\n      }\n    }\n  }], [{\n    key: 'formats',\n    value: function formats(domNode) {\n      return domNode.tagName === this.tagName ? undefined : _get(ListItem.__proto__ || Object.getPrototypeOf(ListItem), 'formats', this).call(this, domNode);\n    }\n  }]);\n\n  return ListItem;\n}(_block2.default);\n\nListItem.blotName = 'list-item';\nListItem.tagName = 'LI';\n\nvar List = function (_Container) {\n  _inherits(List, _Container);\n\n  _createClass(List, null, [{\n    key: 'create',\n    value: function create(value) {\n      var tagName = value === 'ordered' ? 'OL' : 'UL';\n      var node = _get(List.__proto__ || Object.getPrototypeOf(List), 'create', this).call(this, tagName);\n      if (value === 'checked' || value === 'unchecked') {\n        node.setAttribute('data-checked', value === 'checked');\n      }\n      return node;\n    }\n  }, {\n    key: 'formats',\n    value: function formats(domNode) {\n      if (domNode.tagName === 'OL') return 'ordered';\n      if (domNode.tagName === 'UL') {\n        if (domNode.hasAttribute('data-checked')) {\n          return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked';\n        } else {\n          return 'bullet';\n        }\n      }\n      return undefined;\n    }\n  }]);\n\n  function List(domNode) {\n    _classCallCheck(this, List);\n\n    var _this2 = _possibleConstructorReturn(this, (List.__proto__ || Object.getPrototypeOf(List)).call(this, domNode));\n\n    var listEventHandler = function listEventHandler(e) {\n      if (e.target.parentNode !== domNode) return;\n      var format = _this2.statics.formats(domNode);\n      var blot = _parchment2.default.find(e.target);\n      if (format === 'checked') {\n        blot.format('list', 'unchecked');\n      } else if (format === 'unchecked') {\n        blot.format('list', 'checked');\n      }\n    };\n\n    domNode.addEventListener('touchstart', listEventHandler);\n    domNode.addEventListener('mousedown', listEventHandler);\n    return _this2;\n  }\n\n  _createClass(List, [{\n    key: 'format',\n    value: function format(name, value) {\n      if (this.children.length > 0) {\n        this.children.tail.format(name, value);\n      }\n    }\n  }, {\n    key: 'formats',\n    value: function formats() {\n      // We don't inherit from FormatBlot\n      return _defineProperty({}, this.statics.blotName, this.statics.formats(this.domNode));\n    }\n  }, {\n    key: 'insertBefore',\n    value: function insertBefore(blot, ref) {\n      if (blot instanceof ListItem) {\n        _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'insertBefore', this).call(this, blot, ref);\n      } else {\n        var index = ref == null ? this.length() : ref.offset(this);\n        var after = this.split(index);\n        after.parent.insertBefore(blot, after);\n      }\n    }\n  }, {\n    key: 'optimize',\n    value: function optimize(context) {\n      _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'optimize', this).call(this, context);\n      var next = this.next;\n      if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && next.domNode.tagName === this.domNode.tagName && next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) {\n        next.moveChildren(this);\n        next.remove();\n      }\n    }\n  }, {\n    key: 'replace',\n    value: function replace(target) {\n      if (target.statics.blotName !== this.statics.blotName) {\n        var item = _parchment2.default.create(this.statics.defaultChild);\n        target.moveChildren(item);\n        this.appendChild(item);\n      }\n      _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'replace', this).call(this, target);\n    }\n  }]);\n\n  return List;\n}(_container2.default);\n\nList.blotName = 'list';\nList.scope = _parchment2.default.Scope.BLOCK_BLOT;\nList.tagName = ['OL', 'UL'];\nList.defaultChild = 'list-item';\nList.allowedChildren = [ListItem];\n\nexports.ListItem = ListItem;\nexports.default = List;\n\n/***/ }),\n/* 68 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _bold = __webpack_require__(56);\n\nvar _bold2 = _interopRequireDefault(_bold);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Italic = function (_Bold) {\n  _inherits(Italic, _Bold);\n\n  function Italic() {\n    _classCallCheck(this, Italic);\n\n    return _possibleConstructorReturn(this, (Italic.__proto__ || Object.getPrototypeOf(Italic)).apply(this, arguments));\n  }\n\n  return Italic;\n}(_bold2.default);\n\nItalic.blotName = 'italic';\nItalic.tagName = ['EM', 'I'];\n\nexports.default = Italic;\n\n/***/ }),\n/* 69 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Script = function (_Inline) {\n  _inherits(Script, _Inline);\n\n  function Script() {\n    _classCallCheck(this, Script);\n\n    return _possibleConstructorReturn(this, (Script.__proto__ || Object.getPrototypeOf(Script)).apply(this, arguments));\n  }\n\n  _createClass(Script, null, [{\n    key: 'create',\n    value: function create(value) {\n      if (value === 'super') {\n        return document.createElement('sup');\n      } else if (value === 'sub') {\n        return document.createElement('sub');\n      } else {\n        return _get(Script.__proto__ || Object.getPrototypeOf(Script), 'create', this).call(this, value);\n      }\n    }\n  }, {\n    key: 'formats',\n    value: function formats(domNode) {\n      if (domNode.tagName === 'SUB') return 'sub';\n      if (domNode.tagName === 'SUP') return 'super';\n      return undefined;\n    }\n  }]);\n\n  return Script;\n}(_inline2.default);\n\nScript.blotName = 'script';\nScript.tagName = ['SUB', 'SUP'];\n\nexports.default = Script;\n\n/***/ }),\n/* 70 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Strike = function (_Inline) {\n  _inherits(Strike, _Inline);\n\n  function Strike() {\n    _classCallCheck(this, Strike);\n\n    return _possibleConstructorReturn(this, (Strike.__proto__ || Object.getPrototypeOf(Strike)).apply(this, arguments));\n  }\n\n  return Strike;\n}(_inline2.default);\n\nStrike.blotName = 'strike';\nStrike.tagName = 'S';\n\nexports.default = Strike;\n\n/***/ }),\n/* 71 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _inline = __webpack_require__(6);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Underline = function (_Inline) {\n  _inherits(Underline, _Inline);\n\n  function Underline() {\n    _classCallCheck(this, Underline);\n\n    return _possibleConstructorReturn(this, (Underline.__proto__ || Object.getPrototypeOf(Underline)).apply(this, arguments));\n  }\n\n  return Underline;\n}(_inline2.default);\n\nUnderline.blotName = 'underline';\nUnderline.tagName = 'U';\n\nexports.default = Underline;\n\n/***/ }),\n/* 72 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _link = __webpack_require__(27);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ATTRIBUTES = ['alt', 'height', 'width'];\n\nvar Image = function (_Parchment$Embed) {\n  _inherits(Image, _Parchment$Embed);\n\n  function Image() {\n    _classCallCheck(this, Image);\n\n    return _possibleConstructorReturn(this, (Image.__proto__ || Object.getPrototypeOf(Image)).apply(this, arguments));\n  }\n\n  _createClass(Image, [{\n    key: 'format',\n    value: function format(name, value) {\n      if (ATTRIBUTES.indexOf(name) > -1) {\n        if (value) {\n          this.domNode.setAttribute(name, value);\n        } else {\n          this.domNode.removeAttribute(name);\n        }\n      } else {\n        _get(Image.prototype.__proto__ || Object.getPrototypeOf(Image.prototype), 'format', this).call(this, name, value);\n      }\n    }\n  }], [{\n    key: 'create',\n    value: function create(value) {\n      var node = _get(Image.__proto__ || Object.getPrototypeOf(Image), 'create', this).call(this, value);\n      if (typeof value === 'string') {\n        node.setAttribute('src', this.sanitize(value));\n      }\n      return node;\n    }\n  }, {\n    key: 'formats',\n    value: function formats(domNode) {\n      return ATTRIBUTES.reduce(function (formats, attribute) {\n        if (domNode.hasAttribute(attribute)) {\n          formats[attribute] = domNode.getAttribute(attribute);\n        }\n        return formats;\n      }, {});\n    }\n  }, {\n    key: 'match',\n    value: function match(url) {\n      return (/\\.(jpe?g|gif|png)$/.test(url) || /^data:image\\/.+;base64/.test(url)\n      );\n    }\n  }, {\n    key: 'sanitize',\n    value: function sanitize(url) {\n      return (0, _link.sanitize)(url, ['http', 'https', 'data']) ? url : '//:0';\n    }\n  }, {\n    key: 'value',\n    value: function value(domNode) {\n      return domNode.getAttribute('src');\n    }\n  }]);\n\n  return Image;\n}(_parchment2.default.Embed);\n\nImage.blotName = 'image';\nImage.tagName = 'IMG';\n\nexports.default = Image;\n\n/***/ }),\n/* 73 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _block = __webpack_require__(4);\n\nvar _link = __webpack_require__(27);\n\nvar _link2 = _interopRequireDefault(_link);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ATTRIBUTES = ['height', 'width'];\n\nvar Video = function (_BlockEmbed) {\n  _inherits(Video, _BlockEmbed);\n\n  function Video() {\n    _classCallCheck(this, Video);\n\n    return _possibleConstructorReturn(this, (Video.__proto__ || Object.getPrototypeOf(Video)).apply(this, arguments));\n  }\n\n  _createClass(Video, [{\n    key: 'format',\n    value: function format(name, value) {\n      if (ATTRIBUTES.indexOf(name) > -1) {\n        if (value) {\n          this.domNode.setAttribute(name, value);\n        } else {\n          this.domNode.removeAttribute(name);\n        }\n      } else {\n        _get(Video.prototype.__proto__ || Object.getPrototypeOf(Video.prototype), 'format', this).call(this, name, value);\n      }\n    }\n  }], [{\n    key: 'create',\n    value: function create(value) {\n      var node = _get(Video.__proto__ || Object.getPrototypeOf(Video), 'create', this).call(this, value);\n      node.setAttribute('frameborder', '0');\n      node.setAttribute('allowfullscreen', true);\n      node.setAttribute('src', this.sanitize(value));\n      return node;\n    }\n  }, {\n    key: 'formats',\n    value: function formats(domNode) {\n      return ATTRIBUTES.reduce(function (formats, attribute) {\n        if (domNode.hasAttribute(attribute)) {\n          formats[attribute] = domNode.getAttribute(attribute);\n        }\n        return formats;\n      }, {});\n    }\n  }, {\n    key: 'sanitize',\n    value: function sanitize(url) {\n      return _link2.default.sanitize(url);\n    }\n  }, {\n    key: 'value',\n    value: function value(domNode) {\n      return domNode.getAttribute('src');\n    }\n  }]);\n\n  return Video;\n}(_block.BlockEmbed);\n\nVideo.blotName = 'video';\nVideo.className = 'ql-video';\nVideo.tagName = 'IFRAME';\n\nexports.default = Video;\n\n/***/ }),\n/* 74 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.FormulaBlot = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _embed = __webpack_require__(35);\n\nvar _embed2 = _interopRequireDefault(_embed);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar FormulaBlot = function (_Embed) {\n  _inherits(FormulaBlot, _Embed);\n\n  function FormulaBlot() {\n    _classCallCheck(this, FormulaBlot);\n\n    return _possibleConstructorReturn(this, (FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot)).apply(this, arguments));\n  }\n\n  _createClass(FormulaBlot, null, [{\n    key: 'create',\n    value: function create(value) {\n      var node = _get(FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot), 'create', this).call(this, value);\n      if (typeof value === 'string') {\n        window.katex.render(value, node, {\n          throwOnError: false,\n          errorColor: '#f00'\n        });\n        node.setAttribute('data-value', value);\n      }\n      return node;\n    }\n  }, {\n    key: 'value',\n    value: function value(domNode) {\n      return domNode.getAttribute('data-value');\n    }\n  }]);\n\n  return FormulaBlot;\n}(_embed2.default);\n\nFormulaBlot.blotName = 'formula';\nFormulaBlot.className = 'ql-formula';\nFormulaBlot.tagName = 'SPAN';\n\nvar Formula = function (_Module) {\n  _inherits(Formula, _Module);\n\n  _createClass(Formula, null, [{\n    key: 'register',\n    value: function register() {\n      _quill2.default.register(FormulaBlot, true);\n    }\n  }]);\n\n  function Formula() {\n    _classCallCheck(this, Formula);\n\n    var _this2 = _possibleConstructorReturn(this, (Formula.__proto__ || Object.getPrototypeOf(Formula)).call(this));\n\n    if (window.katex == null) {\n      throw new Error('Formula module requires KaTeX.');\n    }\n    return _this2;\n  }\n\n  return Formula;\n}(_module2.default);\n\nexports.FormulaBlot = FormulaBlot;\nexports.default = Formula;\n\n/***/ }),\n/* 75 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.CodeToken = exports.CodeBlock = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(5);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _module = __webpack_require__(9);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar SyntaxCodeBlock = function (_CodeBlock) {\n  _inherits(SyntaxCodeBlock, _CodeBlock);\n\n  function SyntaxCodeBlock() {\n    _classCallCheck(this, SyntaxCodeBlock);\n\n    return _possibleConstructorReturn(this, (SyntaxCodeBlock.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock)).apply(this, arguments));\n  }\n\n  _createClass(SyntaxCodeBlock, [{\n    key: 'replaceWith',\n    value: function replaceWith(block) {\n      this.domNode.textContent = this.domNode.textContent;\n      this.attach();\n      _get(SyntaxCodeBlock.prototype.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock.prototype), 'replaceWith', this).call(this, block);\n    }\n  }, {\n    key: 'highlight',\n    value: function highlight(_highlight) {\n      var text = this.domNode.textContent;\n      if (this.cachedText !== text) {\n        if (text.trim().length > 0 || this.cachedText == null) {\n          this.domNode.innerHTML = _highlight(text);\n          this.domNode.normalize();\n          this.attach();\n        }\n        this.cachedText = text;\n      }\n    }\n  }]);\n\n  return SyntaxCodeBlock;\n}(_code2.default);\n\nSyntaxCodeBlock.className = 'ql-syntax';\n\nvar CodeToken = new _parchment2.default.Attributor.Class('token', 'hljs', {\n  scope: _parchment2.default.Scope.INLINE\n});\n\nvar Syntax = function (_Module) {\n  _inherits(Syntax, _Module);\n\n  _createClass(Syntax, null, [{\n    key: 'register',\n    value: function register() {\n      _quill2.default.register(CodeToken, true);\n      _quill2.default.register(SyntaxCodeBlock, true);\n    }\n  }]);\n\n  function Syntax(quill, options) {\n    _classCallCheck(this, Syntax);\n\n    var _this2 = _possibleConstructorReturn(this, (Syntax.__proto__ || Object.getPrototypeOf(Syntax)).call(this, quill, options));\n\n    if (typeof _this2.options.highlight !== 'function') {\n      throw new Error('Syntax module requires highlight.js. Please include the library on the page before Quill.');\n    }\n    var timer = null;\n    _this2.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () {\n      clearTimeout(timer);\n      timer = setTimeout(function () {\n        _this2.highlight();\n        timer = null;\n      }, _this2.options.interval);\n    });\n    _this2.highlight();\n    return _this2;\n  }\n\n  _createClass(Syntax, [{\n    key: 'highlight',\n    value: function highlight() {\n      var _this3 = this;\n\n      if (this.quill.selection.composing) return;\n      this.quill.update(_quill2.default.sources.USER);\n      var range = this.quill.getSelection();\n      this.quill.scroll.descendants(SyntaxCodeBlock).forEach(function (code) {\n        code.highlight(_this3.options.highlight);\n      });\n      this.quill.update(_quill2.default.sources.SILENT);\n      if (range != null) {\n        this.quill.setSelection(range, _quill2.default.sources.SILENT);\n      }\n    }\n  }]);\n\n  return Syntax;\n}(_module2.default);\n\nSyntax.DEFAULTS = {\n  highlight: function () {\n    if (window.hljs == null) return null;\n    return function (text) {\n      var result = window.hljs.highlightAuto(text);\n      return result.value;\n    };\n  }(),\n  interval: 1000\n};\n\nexports.CodeBlock = SyntaxCodeBlock;\nexports.CodeToken = CodeToken;\nexports.default = Syntax;\n\n/***/ }),\n/* 76 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=3 x2=15 y1=9 y2=9></line> <line class=ql-stroke x1=3 x2=13 y1=14 y2=14></line> <line class=ql-stroke x1=3 x2=9 y1=4 y2=4></line> </svg>\";\n\n/***/ }),\n/* 77 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=15 x2=3 y1=9 y2=9></line> <line class=ql-stroke x1=14 x2=4 y1=14 y2=14></line> <line class=ql-stroke x1=12 x2=6 y1=4 y2=4></line> </svg>\";\n\n/***/ }),\n/* 78 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=15 x2=3 y1=9 y2=9></line> <line class=ql-stroke x1=15 x2=5 y1=14 y2=14></line> <line class=ql-stroke x1=15 x2=9 y1=4 y2=4></line> </svg>\";\n\n/***/ }),\n/* 79 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=15 x2=3 y1=9 y2=9></line> <line class=ql-stroke x1=15 x2=3 y1=14 y2=14></line> <line class=ql-stroke x1=15 x2=3 y1=4 y2=4></line> </svg>\";\n\n/***/ }),\n/* 80 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <g class=\\\"ql-fill ql-color-label\\\"> <polygon points=\\\"6 6.868 6 6 5 6 5 7 5.942 7 6 6.868\\\"></polygon> <rect height=1 width=1 x=4 y=4></rect> <polygon points=\\\"6.817 5 6 5 6 6 6.38 6 6.817 5\\\"></polygon> <rect height=1 width=1 x=2 y=6></rect> <rect height=1 width=1 x=3 y=5></rect> <rect height=1 width=1 x=4 y=7></rect> <polygon points=\\\"4 11.439 4 11 3 11 3 12 3.755 12 4 11.439\\\"></polygon> <rect height=1 width=1 x=2 y=12></rect> <rect height=1 width=1 x=2 y=9></rect> <rect height=1 width=1 x=2 y=15></rect> <polygon points=\\\"4.63 10 4 10 4 11 4.192 11 4.63 10\\\"></polygon> <rect height=1 width=1 x=3 y=8></rect> <path d=M10.832,4.2L11,4.582V4H10.708A1.948,1.948,0,0,1,10.832,4.2Z></path> <path d=M7,4.582L7.168,4.2A1.929,1.929,0,0,1,7.292,4H7V4.582Z></path> <path d=M8,13H7.683l-0.351.8a1.933,1.933,0,0,1-.124.2H8V13Z></path> <rect height=1 width=1 x=12 y=2></rect> <rect height=1 width=1 x=11 y=3></rect> <path d=M9,3H8V3.282A1.985,1.985,0,0,1,9,3Z></path> <rect height=1 width=1 x=2 y=3></rect> <rect height=1 width=1 x=6 y=2></rect> <rect height=1 width=1 x=3 y=2></rect> <rect height=1 width=1 x=5 y=3></rect> <rect height=1 width=1 x=9 y=2></rect> <rect height=1 width=1 x=15 y=14></rect> <polygon points=\\\"13.447 10.174 13.469 10.225 13.472 10.232 13.808 11 14 11 14 10 13.37 10 13.447 10.174\\\"></polygon> <rect height=1 width=1 x=13 y=7></rect> <rect height=1 width=1 x=15 y=5></rect> <rect height=1 width=1 x=14 y=6></rect> <rect height=1 width=1 x=15 y=8></rect> <rect height=1 width=1 x=14 y=9></rect> <path d=M3.775,14H3v1H4V14.314A1.97,1.97,0,0,1,3.775,14Z></path> <rect height=1 width=1 x=14 y=3></rect> <polygon points=\\\"12 6.868 12 6 11.62 6 12 6.868\\\"></polygon> <rect height=1 width=1 x=15 y=2></rect> <rect height=1 width=1 x=12 y=5></rect> <rect height=1 width=1 x=13 y=4></rect> <polygon points=\\\"12.933 9 13 9 13 8 12.495 8 12.933 9\\\"></polygon> <rect height=1 width=1 x=9 y=14></rect> <rect height=1 width=1 x=8 y=15></rect> <path d=M6,14.926V15H7V14.316A1.993,1.993,0,0,1,6,14.926Z></path> <rect height=1 width=1 x=5 y=15></rect> <path d=M10.668,13.8L10.317,13H10v1h0.792A1.947,1.947,0,0,1,10.668,13.8Z></path> <rect height=1 width=1 x=11 y=15></rect> <path d=M14.332,12.2a1.99,1.99,0,0,1,.166.8H15V12H14.245Z></path> <rect height=1 width=1 x=14 y=15></rect> <rect height=1 width=1 x=15 y=11></rect> </g> <polyline class=ql-stroke points=\\\"5.5 13 9 5 12.5 13\\\"></polyline> <line class=ql-stroke x1=11.63 x2=6.38 y1=11 y2=11></line> </svg>\";\n\n/***/ }),\n/* 81 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <rect class=\\\"ql-fill ql-stroke\\\" height=3 width=3 x=4 y=5></rect> <rect class=\\\"ql-fill ql-stroke\\\" height=3 width=3 x=11 y=5></rect> <path class=\\\"ql-even ql-fill ql-stroke\\\" d=M7,8c0,4.031-3,5-3,5></path> <path class=\\\"ql-even ql-fill ql-stroke\\\" d=M14,8c0,4.031-3,5-3,5></path> </svg>\";\n\n/***/ }),\n/* 82 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-stroke d=M5,4H9.5A2.5,2.5,0,0,1,12,6.5v0A2.5,2.5,0,0,1,9.5,9H5A0,0,0,0,1,5,9V4A0,0,0,0,1,5,4Z></path> <path class=ql-stroke d=M5,9h5.5A2.5,2.5,0,0,1,13,11.5v0A2.5,2.5,0,0,1,10.5,14H5a0,0,0,0,1,0,0V9A0,0,0,0,1,5,9Z></path> </svg>\";\n\n/***/ }),\n/* 83 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg class=\\\"\\\" viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=5 x2=13 y1=3 y2=3></line> <line class=ql-stroke x1=6 x2=9.35 y1=12 y2=3></line> <line class=ql-stroke x1=11 x2=15 y1=11 y2=15></line> <line class=ql-stroke x1=15 x2=11 y1=11 y2=15></line> <rect class=ql-fill height=1 rx=0.5 ry=0.5 width=7 x=2 y=14></rect> </svg>\";\n\n/***/ }),\n/* 84 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=\\\"ql-color-label ql-stroke ql-transparent\\\" x1=3 x2=15 y1=15 y2=15></line> <polyline class=ql-stroke points=\\\"5.5 11 9 3 12.5 11\\\"></polyline> <line class=ql-stroke x1=11.63 x2=6.38 y1=9 y2=9></line> </svg>\";\n\n/***/ }),\n/* 85 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <polygon class=\\\"ql-stroke ql-fill\\\" points=\\\"3 11 5 9 3 7 3 11\\\"></polygon> <line class=\\\"ql-stroke ql-fill\\\" x1=15 x2=11 y1=4 y2=4></line> <path class=ql-fill d=M11,3a3,3,0,0,0,0,6h1V3H11Z></path> <rect class=ql-fill height=11 width=1 x=11 y=4></rect> <rect class=ql-fill height=11 width=1 x=13 y=4></rect> </svg>\";\n\n/***/ }),\n/* 86 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <polygon class=\\\"ql-stroke ql-fill\\\" points=\\\"15 12 13 10 15 8 15 12\\\"></polygon> <line class=\\\"ql-stroke ql-fill\\\" x1=9 x2=5 y1=4 y2=4></line> <path class=ql-fill d=M5,3A3,3,0,0,0,5,9H6V3H5Z></path> <rect class=ql-fill height=11 width=1 x=5 y=4></rect> <rect class=ql-fill height=11 width=1 x=7 y=4></rect> </svg>\";\n\n/***/ }),\n/* 87 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M14,16H4a1,1,0,0,1,0-2H14A1,1,0,0,1,14,16Z /> <path class=ql-fill d=M14,4H4A1,1,0,0,1,4,2H14A1,1,0,0,1,14,4Z /> <rect class=ql-fill x=3 y=6 width=12 height=6 rx=1 ry=1 /> </svg>\";\n\n/***/ }),\n/* 88 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M13,16H5a1,1,0,0,1,0-2h8A1,1,0,0,1,13,16Z /> <path class=ql-fill d=M13,4H5A1,1,0,0,1,5,2h8A1,1,0,0,1,13,4Z /> <rect class=ql-fill x=2 y=6 width=14 height=6 rx=1 ry=1 /> </svg>\";\n\n/***/ }),\n/* 89 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M15,8H13a1,1,0,0,1,0-2h2A1,1,0,0,1,15,8Z /> <path class=ql-fill d=M15,12H13a1,1,0,0,1,0-2h2A1,1,0,0,1,15,12Z /> <path class=ql-fill d=M15,16H5a1,1,0,0,1,0-2H15A1,1,0,0,1,15,16Z /> <path class=ql-fill d=M15,4H5A1,1,0,0,1,5,2H15A1,1,0,0,1,15,4Z /> <rect class=ql-fill x=2 y=6 width=8 height=6 rx=1 ry=1 /> </svg>\";\n\n/***/ }),\n/* 90 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M5,8H3A1,1,0,0,1,3,6H5A1,1,0,0,1,5,8Z /> <path class=ql-fill d=M5,12H3a1,1,0,0,1,0-2H5A1,1,0,0,1,5,12Z /> <path class=ql-fill d=M13,16H3a1,1,0,0,1,0-2H13A1,1,0,0,1,13,16Z /> <path class=ql-fill d=M13,4H3A1,1,0,0,1,3,2H13A1,1,0,0,1,13,4Z /> <rect class=ql-fill x=8 y=6 width=8 height=6 rx=1 ry=1 transform=\\\"translate(24 18) rotate(-180)\\\"/> </svg>\";\n\n/***/ }),\n/* 91 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M11.759,2.482a2.561,2.561,0,0,0-3.53.607A7.656,7.656,0,0,0,6.8,6.2C6.109,9.188,5.275,14.677,4.15,14.927a1.545,1.545,0,0,0-1.3-.933A0.922,0.922,0,0,0,2,15.036S1.954,16,4.119,16s3.091-2.691,3.7-5.553c0.177-.826.36-1.726,0.554-2.6L8.775,6.2c0.381-1.421.807-2.521,1.306-2.676a1.014,1.014,0,0,0,1.02.56A0.966,0.966,0,0,0,11.759,2.482Z></path> <rect class=ql-fill height=1.6 rx=0.8 ry=0.8 width=5 x=5.15 y=6.2></rect> <path class=ql-fill d=M13.663,12.027a1.662,1.662,0,0,1,.266-0.276q0.193,0.069.456,0.138a2.1,2.1,0,0,0,.535.069,1.075,1.075,0,0,0,.767-0.3,1.044,1.044,0,0,0,.314-0.8,0.84,0.84,0,0,0-.238-0.619,0.8,0.8,0,0,0-.594-0.239,1.154,1.154,0,0,0-.781.3,4.607,4.607,0,0,0-.781,1q-0.091.15-.218,0.346l-0.246.38c-0.068-.288-0.137-0.582-0.212-0.885-0.459-1.847-2.494-.984-2.941-0.8-0.482.2-.353,0.647-0.094,0.529a0.869,0.869,0,0,1,1.281.585c0.217,0.751.377,1.436,0.527,2.038a5.688,5.688,0,0,1-.362.467,2.69,2.69,0,0,1-.264.271q-0.221-.08-0.471-0.147a2.029,2.029,0,0,0-.522-0.066,1.079,1.079,0,0,0-.768.3A1.058,1.058,0,0,0,9,15.131a0.82,0.82,0,0,0,.832.852,1.134,1.134,0,0,0,.787-0.3,5.11,5.11,0,0,0,.776-0.993q0.141-.219.215-0.34c0.046-.076.122-0.194,0.223-0.346a2.786,2.786,0,0,0,.918,1.726,2.582,2.582,0,0,0,2.376-.185c0.317-.181.212-0.565,0-0.494A0.807,0.807,0,0,1,14.176,15a5.159,5.159,0,0,1-.913-2.446l0,0Q13.487,12.24,13.663,12.027Z></path> </svg>\";\n\n/***/ }),\n/* 92 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewBox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M10,4V14a1,1,0,0,1-2,0V10H3v4a1,1,0,0,1-2,0V4A1,1,0,0,1,3,4V8H8V4a1,1,0,0,1,2,0Zm6.06787,9.209H14.98975V7.59863a.54085.54085,0,0,0-.605-.60547h-.62744a1.01119,1.01119,0,0,0-.748.29688L11.645,8.56641a.5435.5435,0,0,0-.022.8584l.28613.30762a.53861.53861,0,0,0,.84717.0332l.09912-.08789a1.2137,1.2137,0,0,0,.2417-.35254h.02246s-.01123.30859-.01123.60547V13.209H12.041a.54085.54085,0,0,0-.605.60547v.43945a.54085.54085,0,0,0,.605.60547h4.02686a.54085.54085,0,0,0,.605-.60547v-.43945A.54085.54085,0,0,0,16.06787,13.209Z /> </svg>\";\n\n/***/ }),\n/* 93 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewBox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M16.73975,13.81445v.43945a.54085.54085,0,0,1-.605.60547H11.855a.58392.58392,0,0,1-.64893-.60547V14.0127c0-2.90527,3.39941-3.42187,3.39941-4.55469a.77675.77675,0,0,0-.84717-.78125,1.17684,1.17684,0,0,0-.83594.38477c-.2749.26367-.561.374-.85791.13184l-.4292-.34082c-.30811-.24219-.38525-.51758-.1543-.81445a2.97155,2.97155,0,0,1,2.45361-1.17676,2.45393,2.45393,0,0,1,2.68408,2.40918c0,2.45312-3.1792,2.92676-3.27832,3.93848h2.79443A.54085.54085,0,0,1,16.73975,13.81445ZM9,3A.99974.99974,0,0,0,8,4V8H3V4A1,1,0,0,0,1,4V14a1,1,0,0,0,2,0V10H8v4a1,1,0,0,0,2,0V4A.99974.99974,0,0,0,9,3Z /> </svg>\";\n\n/***/ }),\n/* 94 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=7 x2=13 y1=4 y2=4></line> <line class=ql-stroke x1=5 x2=11 y1=14 y2=14></line> <line class=ql-stroke x1=8 x2=10 y1=14 y2=4></line> </svg>\";\n\n/***/ }),\n/* 95 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <rect class=ql-stroke height=10 width=12 x=3 y=4></rect> <circle class=ql-fill cx=6 cy=7 r=1></circle> <polyline class=\\\"ql-even ql-fill\\\" points=\\\"5 12 5 11 7 9 8 10 11 7 13 9 13 12 5 12\\\"></polyline> </svg>\";\n\n/***/ }),\n/* 96 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=3 x2=15 y1=14 y2=14></line> <line class=ql-stroke x1=3 x2=15 y1=4 y2=4></line> <line class=ql-stroke x1=9 x2=15 y1=9 y2=9></line> <polyline class=\\\"ql-fill ql-stroke\\\" points=\\\"3 7 3 11 5 9 3 7\\\"></polyline> </svg>\";\n\n/***/ }),\n/* 97 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=3 x2=15 y1=14 y2=14></line> <line class=ql-stroke x1=3 x2=15 y1=4 y2=4></line> <line class=ql-stroke x1=9 x2=15 y1=9 y2=9></line> <polyline class=ql-stroke points=\\\"5 7 5 11 3 9 5 7\\\"></polyline> </svg>\";\n\n/***/ }),\n/* 98 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=7 x2=11 y1=7 y2=11></line> <path class=\\\"ql-even ql-stroke\\\" d=M8.9,4.577a3.476,3.476,0,0,1,.36,4.679A3.476,3.476,0,0,1,4.577,8.9C3.185,7.5,2.035,6.4,4.217,4.217S7.5,3.185,8.9,4.577Z></path> <path class=\\\"ql-even ql-stroke\\\" d=M13.423,9.1a3.476,3.476,0,0,0-4.679-.36,3.476,3.476,0,0,0,.36,4.679c1.392,1.392,2.5,2.542,4.679.36S14.815,10.5,13.423,9.1Z></path> </svg>\";\n\n/***/ }),\n/* 99 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=7 x2=15 y1=4 y2=4></line> <line class=ql-stroke x1=7 x2=15 y1=9 y2=9></line> <line class=ql-stroke x1=7 x2=15 y1=14 y2=14></line> <line class=\\\"ql-stroke ql-thin\\\" x1=2.5 x2=4.5 y1=5.5 y2=5.5></line> <path class=ql-fill d=M3.5,6A0.5,0.5,0,0,1,3,5.5V3.085l-0.276.138A0.5,0.5,0,0,1,2.053,3c-0.124-.247-0.023-0.324.224-0.447l1-.5A0.5,0.5,0,0,1,4,2.5v3A0.5,0.5,0,0,1,3.5,6Z></path> <path class=\\\"ql-stroke ql-thin\\\" d=M4.5,10.5h-2c0-.234,1.85-1.076,1.85-2.234A0.959,0.959,0,0,0,2.5,8.156></path> <path class=\\\"ql-stroke ql-thin\\\" d=M2.5,14.846a0.959,0.959,0,0,0,1.85-.109A0.7,0.7,0,0,0,3.75,14a0.688,0.688,0,0,0,.6-0.736,0.959,0.959,0,0,0-1.85-.109></path> </svg>\";\n\n/***/ }),\n/* 100 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=6 x2=15 y1=4 y2=4></line> <line class=ql-stroke x1=6 x2=15 y1=9 y2=9></line> <line class=ql-stroke x1=6 x2=15 y1=14 y2=14></line> <line class=ql-stroke x1=3 x2=3 y1=4 y2=4></line> <line class=ql-stroke x1=3 x2=3 y1=9 y2=9></line> <line class=ql-stroke x1=3 x2=3 y1=14 y2=14></line> </svg>\";\n\n/***/ }),\n/* 101 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg class=\\\"\\\" viewbox=\\\"0 0 18 18\\\"> <line class=ql-stroke x1=9 x2=15 y1=4 y2=4></line> <polyline class=ql-stroke points=\\\"3 4 4 5 6 3\\\"></polyline> <line class=ql-stroke x1=9 x2=15 y1=14 y2=14></line> <polyline class=ql-stroke points=\\\"3 14 4 15 6 13\\\"></polyline> <line class=ql-stroke x1=9 x2=15 y1=9 y2=9></line> <polyline class=ql-stroke points=\\\"3 9 4 10 6 8\\\"></polyline> </svg>\";\n\n/***/ }),\n/* 102 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M15.5,15H13.861a3.858,3.858,0,0,0,1.914-2.975,1.8,1.8,0,0,0-1.6-1.751A1.921,1.921,0,0,0,12.021,11.7a0.50013,0.50013,0,1,0,.957.291h0a0.914,0.914,0,0,1,1.053-.725,0.81,0.81,0,0,1,.744.762c0,1.076-1.16971,1.86982-1.93971,2.43082A1.45639,1.45639,0,0,0,12,15.5a0.5,0.5,0,0,0,.5.5h3A0.5,0.5,0,0,0,15.5,15Z /> <path class=ql-fill d=M9.65,5.241a1,1,0,0,0-1.409.108L6,7.964,3.759,5.349A1,1,0,0,0,2.192,6.59178Q2.21541,6.6213,2.241,6.649L4.684,9.5,2.241,12.35A1,1,0,0,0,3.71,13.70722q0.02557-.02768.049-0.05722L6,11.036,8.241,13.65a1,1,0,1,0,1.567-1.24277Q9.78459,12.3777,9.759,12.35L7.316,9.5,9.759,6.651A1,1,0,0,0,9.65,5.241Z /> </svg>\";\n\n/***/ }),\n/* 103 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-fill d=M15.5,7H13.861a4.015,4.015,0,0,0,1.914-2.975,1.8,1.8,0,0,0-1.6-1.751A1.922,1.922,0,0,0,12.021,3.7a0.5,0.5,0,1,0,.957.291,0.917,0.917,0,0,1,1.053-.725,0.81,0.81,0,0,1,.744.762c0,1.077-1.164,1.925-1.934,2.486A1.423,1.423,0,0,0,12,7.5a0.5,0.5,0,0,0,.5.5h3A0.5,0.5,0,0,0,15.5,7Z /> <path class=ql-fill d=M9.651,5.241a1,1,0,0,0-1.41.108L6,7.964,3.759,5.349a1,1,0,1,0-1.519,1.3L4.683,9.5,2.241,12.35a1,1,0,1,0,1.519,1.3L6,11.036,8.241,13.65a1,1,0,0,0,1.519-1.3L7.317,9.5,9.759,6.651A1,1,0,0,0,9.651,5.241Z /> </svg>\";\n\n/***/ }),\n/* 104 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <line class=\\\"ql-stroke ql-thin\\\" x1=15.5 x2=2.5 y1=8.5 y2=9.5></line> <path class=ql-fill d=M9.007,8C6.542,7.791,6,7.519,6,6.5,6,5.792,7.283,5,9,5c1.571,0,2.765.679,2.969,1.309a1,1,0,0,0,1.9-.617C13.356,4.106,11.354,3,9,3,6.2,3,4,4.538,4,6.5a3.2,3.2,0,0,0,.5,1.843Z></path> <path class=ql-fill d=M8.984,10C11.457,10.208,12,10.479,12,11.5c0,0.708-1.283,1.5-3,1.5-1.571,0-2.765-.679-2.969-1.309a1,1,0,1,0-1.9.617C4.644,13.894,6.646,15,9,15c2.8,0,5-1.538,5-3.5a3.2,3.2,0,0,0-.5-1.843Z></path> </svg>\";\n\n/***/ }),\n/* 105 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <path class=ql-stroke d=M5,3V9a4.012,4.012,0,0,0,4,4H9a4.012,4.012,0,0,0,4-4V3></path> <rect class=ql-fill height=1 rx=0.5 ry=0.5 width=12 x=3 y=15></rect> </svg>\";\n\n/***/ }),\n/* 106 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <rect class=ql-stroke height=12 width=12 x=3 y=3></rect> <rect class=ql-fill height=12 width=1 x=5 y=3></rect> <rect class=ql-fill height=12 width=1 x=12 y=3></rect> <rect class=ql-fill height=2 width=8 x=5 y=8></rect> <rect class=ql-fill height=1 width=3 x=3 y=5></rect> <rect class=ql-fill height=1 width=3 x=3 y=7></rect> <rect class=ql-fill height=1 width=3 x=3 y=10></rect> <rect class=ql-fill height=1 width=3 x=3 y=12></rect> <rect class=ql-fill height=1 width=3 x=12 y=5></rect> <rect class=ql-fill height=1 width=3 x=12 y=7></rect> <rect class=ql-fill height=1 width=3 x=12 y=10></rect> <rect class=ql-fill height=1 width=3 x=12 y=12></rect> </svg>\";\n\n/***/ }),\n/* 107 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"<svg viewbox=\\\"0 0 18 18\\\"> <polygon class=ql-stroke points=\\\"7 11 9 13 11 11 7 11\\\"></polygon> <polygon class=ql-stroke points=\\\"7 7 9 5 11 7 7 7\\\"></polygon> </svg>\";\n\n/***/ }),\n/* 108 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.BubbleTooltip = undefined;\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _extend = __webpack_require__(3);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _emitter = __webpack_require__(8);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _base = __webpack_require__(43);\n\nvar _base2 = _interopRequireDefault(_base);\n\nvar _selection = __webpack_require__(15);\n\nvar _icons = __webpack_require__(41);\n\nvar _icons2 = _interopRequireDefault(_icons);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar TOOLBAR_CONFIG = [['bold', 'italic', 'link'], [{ header: 1 }, { header: 2 }, 'blockquote']];\n\nvar BubbleTheme = function (_BaseTheme) {\n  _inherits(BubbleTheme, _BaseTheme);\n\n  function BubbleTheme(quill, options) {\n    _classCallCheck(this, BubbleTheme);\n\n    if (options.modules.toolbar != null && options.modules.toolbar.container == null) {\n      options.modules.toolbar.container = TOOLBAR_CONFIG;\n    }\n\n    var _this = _possibleConstructorReturn(this, (BubbleTheme.__proto__ || Object.getPrototypeOf(BubbleTheme)).call(this, quill, options));\n\n    _this.quill.container.classList.add('ql-bubble');\n    return _this;\n  }\n\n  _createClass(BubbleTheme, [{\n    key: 'extendToolbar',\n    value: function extendToolbar(toolbar) {\n      this.tooltip = new BubbleTooltip(this.quill, this.options.bounds);\n      this.tooltip.root.appendChild(toolbar.container);\n      this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default);\n      this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default);\n    }\n  }]);\n\n  return BubbleTheme;\n}(_base2.default);\n\nBubbleTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, {\n  modules: {\n    toolbar: {\n      handlers: {\n        link: function link(value) {\n          if (!value) {\n            this.quill.format('link', false);\n          } else {\n            this.quill.theme.tooltip.edit();\n          }\n        }\n      }\n    }\n  }\n});\n\nvar BubbleTooltip = function (_BaseTooltip) {\n  _inherits(BubbleTooltip, _BaseTooltip);\n\n  function BubbleTooltip(quill, bounds) {\n    _classCallCheck(this, BubbleTooltip);\n\n    var _this2 = _possibleConstructorReturn(this, (BubbleTooltip.__proto__ || Object.getPrototypeOf(BubbleTooltip)).call(this, quill, bounds));\n\n    _this2.quill.on(_emitter2.default.events.EDITOR_CHANGE, function (type, range, oldRange, source) {\n      if (type !== _emitter2.default.events.SELECTION_CHANGE) return;\n      if (range != null && range.length > 0 && source === _emitter2.default.sources.USER) {\n        _this2.show();\n        // Lock our width so we will expand beyond our offsetParent boundaries\n        _this2.root.style.left = '0px';\n        _this2.root.style.width = '';\n        _this2.root.style.width = _this2.root.offsetWidth + 'px';\n        var lines = _this2.quill.getLines(range.index, range.length);\n        if (lines.length === 1) {\n          _this2.position(_this2.quill.getBounds(range));\n        } else {\n          var lastLine = lines[lines.length - 1];\n          var index = _this2.quill.getIndex(lastLine);\n          var length = Math.min(lastLine.length() - 1, range.index + range.length - index);\n          var _bounds = _this2.quill.getBounds(new _selection.Range(index, length));\n          _this2.position(_bounds);\n        }\n      } else if (document.activeElement !== _this2.textbox && _this2.quill.hasFocus()) {\n        _this2.hide();\n      }\n    });\n    return _this2;\n  }\n\n  _createClass(BubbleTooltip, [{\n    key: 'listen',\n    value: function listen() {\n      var _this3 = this;\n\n      _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'listen', this).call(this);\n      this.root.querySelector('.ql-close').addEventListener('click', function () {\n        _this3.root.classList.remove('ql-editing');\n      });\n      this.quill.on(_emitter2.default.events.SCROLL_OPTIMIZE, function () {\n        // Let selection be restored by toolbar handlers before repositioning\n        setTimeout(function () {\n          if (_this3.root.classList.contains('ql-hidden')) return;\n          var range = _this3.quill.getSelection();\n          if (range != null) {\n            _this3.position(_this3.quill.getBounds(range));\n          }\n        }, 1);\n      });\n    }\n  }, {\n    key: 'cancel',\n    value: function cancel() {\n      this.show();\n    }\n  }, {\n    key: 'position',\n    value: function position(reference) {\n      var shift = _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'position', this).call(this, reference);\n      var arrow = this.root.querySelector('.ql-tooltip-arrow');\n      arrow.style.marginLeft = '';\n      if (shift === 0) return shift;\n      arrow.style.marginLeft = -1 * shift - arrow.offsetWidth / 2 + 'px';\n    }\n  }]);\n\n  return BubbleTooltip;\n}(_base.BaseTooltip);\n\nBubbleTooltip.TEMPLATE = ['<span class=\"ql-tooltip-arrow\"></span>', '<div class=\"ql-tooltip-editor\">', '<input type=\"text\" data-formula=\"e=mc^2\" data-link=\"https://quilljs.com\" data-video=\"Embed URL\">', '<a class=\"ql-close\"></a>', '</div>'].join('');\n\nexports.BubbleTooltip = BubbleTooltip;\nexports.default = BubbleTheme;\n\n/***/ }),\n/* 109 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = __webpack_require__(63);\n\n\n/***/ })\n/******/ ])[\"default\"];\n});"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/quill/quill.snow.css",
    "content": "/*!\n * Quill Editor v1.3.7\n * https://quilljs.com/\n * Copyright (c) 2014, Jason Chen\n * Copyright (c) 2013, salesforce.com\n */\n.ql-container {\n  box-sizing: border-box;\n  font-family: Helvetica, Arial, sans-serif;\n  font-size: 13px;\n  height: 100%;\n  margin: 0px;\n  position: relative;\n}\n.ql-container.ql-disabled .ql-tooltip {\n  visibility: hidden;\n}\n.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {\n  pointer-events: none;\n}\n.ql-clipboard {\n  left: -100000px;\n  height: 1px;\n  overflow-y: hidden;\n  position: absolute;\n  top: 50%;\n}\n.ql-clipboard p {\n  margin: 0;\n  padding: 0;\n}\n.ql-editor {\n  box-sizing: border-box;\n  line-height: 1.42;\n  height: 100%;\n  outline: none;\n  overflow-y: auto;\n  padding: 12px 15px;\n  tab-size: 4;\n  -moz-tab-size: 4;\n  text-align: left;\n  white-space: pre-wrap;\n  word-wrap: break-word;\n}\n.ql-editor > * {\n  cursor: text;\n}\n.ql-editor p,\n.ql-editor ol,\n.ql-editor ul,\n.ql-editor pre,\n.ql-editor blockquote,\n.ql-editor h1,\n.ql-editor h2,\n.ql-editor h3,\n.ql-editor h4,\n.ql-editor h5,\n.ql-editor h6 {\n  margin: 0;\n  padding: 0;\n  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol,\n.ql-editor ul {\n  padding-left: 1.5em;\n}\n.ql-editor ol > li,\n.ql-editor ul > li {\n  list-style-type: none;\n}\n.ql-editor ul > li::before {\n  content: '\\2022';\n}\n.ql-editor ul[data-checked=true],\n.ql-editor ul[data-checked=false] {\n  pointer-events: none;\n}\n.ql-editor ul[data-checked=true] > li *,\n.ql-editor ul[data-checked=false] > li * {\n  pointer-events: all;\n}\n.ql-editor ul[data-checked=true] > li::before,\n.ql-editor ul[data-checked=false] > li::before {\n  color: #777;\n  cursor: pointer;\n  pointer-events: all;\n}\n.ql-editor ul[data-checked=true] > li::before {\n  content: '\\2611';\n}\n.ql-editor ul[data-checked=false] > li::before {\n  content: '\\2610';\n}\n.ql-editor li::before {\n  display: inline-block;\n  white-space: nowrap;\n  width: 1.2em;\n}\n.ql-editor li:not(.ql-direction-rtl)::before {\n  margin-left: -1.5em;\n  margin-right: 0.3em;\n  text-align: right;\n}\n.ql-editor li.ql-direction-rtl::before {\n  margin-left: 0.3em;\n  margin-right: -1.5em;\n}\n.ql-editor ol li:not(.ql-direction-rtl),\n.ql-editor ul li:not(.ql-direction-rtl) {\n  padding-left: 1.5em;\n}\n.ql-editor ol li.ql-direction-rtl,\n.ql-editor ul li.ql-direction-rtl {\n  padding-right: 1.5em;\n}\n.ql-editor ol li {\n  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n  counter-increment: list-0;\n}\n.ql-editor ol li:before {\n  content: counter(list-0, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-1 {\n  counter-increment: list-1;\n}\n.ql-editor ol li.ql-indent-1:before {\n  content: counter(list-1, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-1 {\n  counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-2 {\n  counter-increment: list-2;\n}\n.ql-editor ol li.ql-indent-2:before {\n  content: counter(list-2, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-2 {\n  counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-3 {\n  counter-increment: list-3;\n}\n.ql-editor ol li.ql-indent-3:before {\n  content: counter(list-3, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-3 {\n  counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-4 {\n  counter-increment: list-4;\n}\n.ql-editor ol li.ql-indent-4:before {\n  content: counter(list-4, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-4 {\n  counter-reset: list-5 list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-5 {\n  counter-increment: list-5;\n}\n.ql-editor ol li.ql-indent-5:before {\n  content: counter(list-5, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-5 {\n  counter-reset: list-6 list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-6 {\n  counter-increment: list-6;\n}\n.ql-editor ol li.ql-indent-6:before {\n  content: counter(list-6, decimal) '. ';\n}\n.ql-editor ol li.ql-indent-6 {\n  counter-reset: list-7 list-8 list-9;\n}\n.ql-editor ol li.ql-indent-7 {\n  counter-increment: list-7;\n}\n.ql-editor ol li.ql-indent-7:before {\n  content: counter(list-7, lower-alpha) '. ';\n}\n.ql-editor ol li.ql-indent-7 {\n  counter-reset: list-8 list-9;\n}\n.ql-editor ol li.ql-indent-8 {\n  counter-increment: list-8;\n}\n.ql-editor ol li.ql-indent-8:before {\n  content: counter(list-8, lower-roman) '. ';\n}\n.ql-editor ol li.ql-indent-8 {\n  counter-reset: list-9;\n}\n.ql-editor ol li.ql-indent-9 {\n  counter-increment: list-9;\n}\n.ql-editor ol li.ql-indent-9:before {\n  content: counter(list-9, decimal) '. ';\n}\n.ql-editor .ql-indent-1:not(.ql-direction-rtl) {\n  padding-left: 3em;\n}\n.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {\n  padding-left: 4.5em;\n}\n.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {\n  padding-right: 3em;\n}\n.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {\n  padding-right: 4.5em;\n}\n.ql-editor .ql-indent-2:not(.ql-direction-rtl) {\n  padding-left: 6em;\n}\n.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {\n  padding-left: 7.5em;\n}\n.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {\n  padding-right: 6em;\n}\n.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {\n  padding-right: 7.5em;\n}\n.ql-editor .ql-indent-3:not(.ql-direction-rtl) {\n  padding-left: 9em;\n}\n.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {\n  padding-left: 10.5em;\n}\n.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {\n  padding-right: 9em;\n}\n.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {\n  padding-right: 10.5em;\n}\n.ql-editor .ql-indent-4:not(.ql-direction-rtl) {\n  padding-left: 12em;\n}\n.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {\n  padding-left: 13.5em;\n}\n.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {\n  padding-right: 12em;\n}\n.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {\n  padding-right: 13.5em;\n}\n.ql-editor .ql-indent-5:not(.ql-direction-rtl) {\n  padding-left: 15em;\n}\n.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {\n  padding-left: 16.5em;\n}\n.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {\n  padding-right: 15em;\n}\n.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {\n  padding-right: 16.5em;\n}\n.ql-editor .ql-indent-6:not(.ql-direction-rtl) {\n  padding-left: 18em;\n}\n.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {\n  padding-left: 19.5em;\n}\n.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {\n  padding-right: 18em;\n}\n.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {\n  padding-right: 19.5em;\n}\n.ql-editor .ql-indent-7:not(.ql-direction-rtl) {\n  padding-left: 21em;\n}\n.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {\n  padding-left: 22.5em;\n}\n.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {\n  padding-right: 21em;\n}\n.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {\n  padding-right: 22.5em;\n}\n.ql-editor .ql-indent-8:not(.ql-direction-rtl) {\n  padding-left: 24em;\n}\n.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {\n  padding-left: 25.5em;\n}\n.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {\n  padding-right: 24em;\n}\n.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {\n  padding-right: 25.5em;\n}\n.ql-editor .ql-indent-9:not(.ql-direction-rtl) {\n  padding-left: 27em;\n}\n.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {\n  padding-left: 28.5em;\n}\n.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {\n  padding-right: 27em;\n}\n.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {\n  padding-right: 28.5em;\n}\n.ql-editor .ql-video {\n  display: block;\n  max-width: 100%;\n}\n.ql-editor .ql-video.ql-align-center {\n  margin: 0 auto;\n}\n.ql-editor .ql-video.ql-align-right {\n  margin: 0 0 0 auto;\n}\n.ql-editor .ql-bg-black {\n  background-color: #000;\n}\n.ql-editor .ql-bg-red {\n  background-color: #e60000;\n}\n.ql-editor .ql-bg-orange {\n  background-color: #f90;\n}\n.ql-editor .ql-bg-yellow {\n  background-color: #ff0;\n}\n.ql-editor .ql-bg-green {\n  background-color: #008a00;\n}\n.ql-editor .ql-bg-blue {\n  background-color: #06c;\n}\n.ql-editor .ql-bg-purple {\n  background-color: #93f;\n}\n.ql-editor .ql-color-white {\n  color: #fff;\n}\n.ql-editor .ql-color-red {\n  color: #e60000;\n}\n.ql-editor .ql-color-orange {\n  color: #f90;\n}\n.ql-editor .ql-color-yellow {\n  color: #ff0;\n}\n.ql-editor .ql-color-green {\n  color: #008a00;\n}\n.ql-editor .ql-color-blue {\n  color: #06c;\n}\n.ql-editor .ql-color-purple {\n  color: #93f;\n}\n.ql-editor .ql-font-serif {\n  font-family: Georgia, Times New Roman, serif;\n}\n.ql-editor .ql-font-monospace {\n  font-family: Monaco, Courier New, monospace;\n}\n.ql-editor .ql-size-small {\n  font-size: 0.75em;\n}\n.ql-editor .ql-size-large {\n  font-size: 1.5em;\n}\n.ql-editor .ql-size-huge {\n  font-size: 2.5em;\n}\n.ql-editor .ql-direction-rtl {\n  direction: rtl;\n  text-align: inherit;\n}\n.ql-editor .ql-align-center {\n  text-align: center;\n}\n.ql-editor .ql-align-justify {\n  text-align: justify;\n}\n.ql-editor .ql-align-right {\n  text-align: right;\n}\n.ql-editor.ql-blank::before {\n  color: rgba(0,0,0,0.6);\n  content: attr(data-placeholder);\n  font-style: italic;\n  left: 15px;\n  pointer-events: none;\n  position: absolute;\n  right: 15px;\n}\n.ql-snow.ql-toolbar:after,\n.ql-snow .ql-toolbar:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n.ql-snow.ql-toolbar button,\n.ql-snow .ql-toolbar button {\n  background: none;\n  border: none;\n  cursor: pointer;\n  display: inline-block;\n  float: left;\n  height: 24px;\n  padding: 3px 5px;\n  width: 28px;\n}\n.ql-snow.ql-toolbar button svg,\n.ql-snow .ql-toolbar button svg {\n  float: left;\n  height: 100%;\n}\n.ql-snow.ql-toolbar button:active:hover,\n.ql-snow .ql-toolbar button:active:hover {\n  outline: none;\n}\n.ql-snow.ql-toolbar input.ql-image[type=file],\n.ql-snow .ql-toolbar input.ql-image[type=file] {\n  display: none;\n}\n.ql-snow.ql-toolbar button:hover,\n.ql-snow .ql-toolbar button:hover,\n.ql-snow.ql-toolbar button:focus,\n.ql-snow .ql-toolbar button:focus,\n.ql-snow.ql-toolbar button.ql-active,\n.ql-snow .ql-toolbar button.ql-active,\n.ql-snow.ql-toolbar .ql-picker-label:hover,\n.ql-snow .ql-toolbar .ql-picker-label:hover,\n.ql-snow.ql-toolbar .ql-picker-label.ql-active,\n.ql-snow .ql-toolbar .ql-picker-label.ql-active,\n.ql-snow.ql-toolbar .ql-picker-item:hover,\n.ql-snow .ql-toolbar .ql-picker-item:hover,\n.ql-snow.ql-toolbar .ql-picker-item.ql-selected,\n.ql-snow .ql-toolbar .ql-picker-item.ql-selected {\n  color: #06c;\n}\n.ql-snow.ql-toolbar button:hover .ql-fill,\n.ql-snow .ql-toolbar button:hover .ql-fill,\n.ql-snow.ql-toolbar button:focus .ql-fill,\n.ql-snow .ql-toolbar button:focus .ql-fill,\n.ql-snow.ql-toolbar button.ql-active .ql-fill,\n.ql-snow .ql-toolbar button.ql-active .ql-fill,\n.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,\n.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,\n.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,\n.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,\n.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,\n.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,\n.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,\n.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,\n.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,\n.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,\n.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,\n.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,\n.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,\n.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,\n.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,\n.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,\n.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,\n.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,\n.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,\n.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,\n.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,\n.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {\n  fill: #06c;\n}\n.ql-snow.ql-toolbar button:hover .ql-stroke,\n.ql-snow .ql-toolbar button:hover .ql-stroke,\n.ql-snow.ql-toolbar button:focus .ql-stroke,\n.ql-snow .ql-toolbar button:focus .ql-stroke,\n.ql-snow.ql-toolbar button.ql-active .ql-stroke,\n.ql-snow .ql-toolbar button.ql-active .ql-stroke,\n.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,\n.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,\n.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,\n.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,\n.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,\n.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,\n.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,\n.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,\n.ql-snow.ql-toolbar button:hover .ql-stroke-miter,\n.ql-snow .ql-toolbar button:hover .ql-stroke-miter,\n.ql-snow.ql-toolbar button:focus .ql-stroke-miter,\n.ql-snow .ql-toolbar button:focus .ql-stroke-miter,\n.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,\n.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,\n.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,\n.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,\n.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,\n.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,\n.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,\n.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,\n.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,\n.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {\n  stroke: #06c;\n}\n@media (pointer: coarse) {\n  .ql-snow.ql-toolbar button:hover:not(.ql-active),\n  .ql-snow .ql-toolbar button:hover:not(.ql-active) {\n    color: #444;\n  }\n  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,\n  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,\n  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,\n  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {\n    fill: #444;\n  }\n  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,\n  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,\n  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,\n  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {\n    stroke: #444;\n  }\n}\n.ql-snow {\n  box-sizing: border-box;\n}\n.ql-snow * {\n  box-sizing: border-box;\n}\n.ql-snow .ql-hidden {\n  display: none;\n}\n.ql-snow .ql-out-bottom,\n.ql-snow .ql-out-top {\n  visibility: hidden;\n}\n.ql-snow .ql-tooltip {\n  position: absolute;\n  transform: translateY(10px);\n}\n.ql-snow .ql-tooltip a {\n  cursor: pointer;\n  text-decoration: none;\n}\n.ql-snow .ql-tooltip.ql-flip {\n  transform: translateY(-10px);\n}\n.ql-snow .ql-formats {\n  display: inline-block;\n  vertical-align: middle;\n}\n.ql-snow .ql-formats:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n.ql-snow .ql-stroke {\n  fill: none;\n  stroke: #444;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  stroke-width: 2;\n}\n.ql-snow .ql-stroke-miter {\n  fill: none;\n  stroke: #444;\n  stroke-miterlimit: 10;\n  stroke-width: 2;\n}\n.ql-snow .ql-fill,\n.ql-snow .ql-stroke.ql-fill {\n  fill: #444;\n}\n.ql-snow .ql-empty {\n  fill: none;\n}\n.ql-snow .ql-even {\n  fill-rule: evenodd;\n}\n.ql-snow .ql-thin,\n.ql-snow .ql-stroke.ql-thin {\n  stroke-width: 1;\n}\n.ql-snow .ql-transparent {\n  opacity: 0.4;\n}\n.ql-snow .ql-direction svg:last-child {\n  display: none;\n}\n.ql-snow .ql-direction.ql-active svg:last-child {\n  display: inline;\n}\n.ql-snow .ql-direction.ql-active svg:first-child {\n  display: none;\n}\n.ql-snow .ql-editor h1 {\n  font-size: 2em;\n}\n.ql-snow .ql-editor h2 {\n  font-size: 1.5em;\n}\n.ql-snow .ql-editor h3 {\n  font-size: 1.17em;\n}\n.ql-snow .ql-editor h4 {\n  font-size: 1em;\n}\n.ql-snow .ql-editor h5 {\n  font-size: 0.83em;\n}\n.ql-snow .ql-editor h6 {\n  font-size: 0.67em;\n}\n.ql-snow .ql-editor a {\n  text-decoration: underline;\n}\n.ql-snow .ql-editor blockquote {\n  border-left: 4px solid #ccc;\n  margin-bottom: 5px;\n  margin-top: 5px;\n  padding-left: 16px;\n}\n.ql-snow .ql-editor code,\n.ql-snow .ql-editor pre {\n  background-color: #f0f0f0;\n  border-radius: 3px;\n}\n.ql-snow .ql-editor pre {\n  white-space: pre-wrap;\n  margin-bottom: 5px;\n  margin-top: 5px;\n  padding: 5px 10px;\n}\n.ql-snow .ql-editor code {\n  font-size: 85%;\n  padding: 2px 4px;\n}\n.ql-snow .ql-editor pre.ql-syntax {\n  background-color: #23241f;\n  color: #f8f8f2;\n  overflow: visible;\n}\n.ql-snow .ql-editor img {\n  max-width: 100%;\n}\n.ql-snow .ql-picker {\n  color: #444;\n  display: inline-block;\n  float: left;\n  font-size: 14px;\n  font-weight: 500;\n  height: 24px;\n  position: relative;\n  vertical-align: middle;\n}\n.ql-snow .ql-picker-label {\n  cursor: pointer;\n  display: inline-block;\n  height: 100%;\n  padding-left: 8px;\n  padding-right: 2px;\n  position: relative;\n  width: 100%;\n}\n.ql-snow .ql-picker-label::before {\n  display: inline-block;\n  line-height: 22px;\n}\n.ql-snow .ql-picker-options {\n  background-color: #fff;\n  display: none;\n  min-width: 100%;\n  padding: 4px 8px;\n  position: absolute;\n  white-space: nowrap;\n}\n.ql-snow .ql-picker-options .ql-picker-item {\n  cursor: pointer;\n  display: block;\n  padding-bottom: 5px;\n  padding-top: 5px;\n}\n.ql-snow .ql-picker.ql-expanded .ql-picker-label {\n  color: #ccc;\n  z-index: 2;\n}\n.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {\n  fill: #ccc;\n}\n.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {\n  stroke: #ccc;\n}\n.ql-snow .ql-picker.ql-expanded .ql-picker-options {\n  display: block;\n  margin-top: -1px;\n  top: 100%;\n  z-index: 1;\n}\n.ql-snow .ql-color-picker,\n.ql-snow .ql-icon-picker {\n  width: 28px;\n}\n.ql-snow .ql-color-picker .ql-picker-label,\n.ql-snow .ql-icon-picker .ql-picker-label {\n  padding: 2px 4px;\n}\n.ql-snow .ql-color-picker .ql-picker-label svg,\n.ql-snow .ql-icon-picker .ql-picker-label svg {\n  right: 4px;\n}\n.ql-snow .ql-icon-picker .ql-picker-options {\n  padding: 4px 0px;\n}\n.ql-snow .ql-icon-picker .ql-picker-item {\n  height: 24px;\n  width: 24px;\n  padding: 2px 4px;\n}\n.ql-snow .ql-color-picker .ql-picker-options {\n  padding: 3px 5px;\n  width: 152px;\n}\n.ql-snow .ql-color-picker .ql-picker-item {\n  border: 1px solid transparent;\n  float: left;\n  height: 16px;\n  margin: 2px;\n  padding: 0px;\n  width: 16px;\n}\n.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {\n  position: absolute;\n  margin-top: -9px;\n  right: 0;\n  top: 50%;\n  width: 18px;\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {\n  content: attr(data-label);\n}\n.ql-snow .ql-picker.ql-header {\n  width: 98px;\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item::before {\n  content: 'Normal';\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"1\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"1\"]::before {\n  content: 'Heading 1';\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"2\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"2\"]::before {\n  content: 'Heading 2';\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"3\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"3\"]::before {\n  content: 'Heading 3';\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"4\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"4\"]::before {\n  content: 'Heading 4';\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"5\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"5\"]::before {\n  content: 'Heading 5';\n}\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value=\"6\"]::before,\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"6\"]::before {\n  content: 'Heading 6';\n}\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"1\"]::before {\n  font-size: 2em;\n}\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"2\"]::before {\n  font-size: 1.5em;\n}\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"3\"]::before {\n  font-size: 1.17em;\n}\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"4\"]::before {\n  font-size: 1em;\n}\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"5\"]::before {\n  font-size: 0.83em;\n}\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value=\"6\"]::before {\n  font-size: 0.67em;\n}\n.ql-snow .ql-picker.ql-font {\n  width: 108px;\n}\n.ql-snow .ql-picker.ql-font .ql-picker-label::before,\n.ql-snow .ql-picker.ql-font .ql-picker-item::before {\n  content: 'Sans Serif';\n}\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {\n  content: 'Serif';\n}\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {\n  content: 'Monospace';\n}\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {\n  font-family: Georgia, Times New Roman, serif;\n}\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {\n  font-family: Monaco, Courier New, monospace;\n}\n.ql-snow .ql-picker.ql-size {\n  width: 98px;\n}\n.ql-snow .ql-picker.ql-size .ql-picker-label::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item::before {\n  content: 'Normal';\n}\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {\n  content: 'Small';\n}\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {\n  content: 'Large';\n}\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {\n  content: 'Huge';\n}\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {\n  font-size: 10px;\n}\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {\n  font-size: 18px;\n}\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {\n  font-size: 32px;\n}\n.ql-snow .ql-color-picker.ql-background .ql-picker-item {\n  background-color: #fff;\n}\n.ql-snow .ql-color-picker.ql-color .ql-picker-item {\n  background-color: #000;\n}\n.ql-toolbar.ql-snow {\n  border: 1px solid #ccc;\n  box-sizing: border-box;\n  font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;\n  padding: 8px;\n}\n.ql-toolbar.ql-snow .ql-formats {\n  margin-right: 15px;\n}\n.ql-toolbar.ql-snow .ql-picker-label {\n  border: 1px solid transparent;\n}\n.ql-toolbar.ql-snow .ql-picker-options {\n  border: 1px solid transparent;\n  box-shadow: rgba(0,0,0,0.2) 0 2px 8px;\n}\n.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {\n  border-color: #ccc;\n}\n.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {\n  border-color: #ccc;\n}\n.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,\n.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover {\n  border-color: #000;\n}\n.ql-toolbar.ql-snow + .ql-container.ql-snow {\n  border-top: 0px;\n}\n.ql-snow .ql-tooltip {\n  background-color: #fff;\n  border: 1px solid #ccc;\n  box-shadow: 0px 0px 5px #ddd;\n  color: #444;\n  padding: 5px 12px;\n  white-space: nowrap;\n}\n.ql-snow .ql-tooltip::before {\n  content: \"Visit URL:\";\n  line-height: 26px;\n  margin-right: 8px;\n}\n.ql-snow .ql-tooltip input[type=text] {\n  display: none;\n  border: 1px solid #ccc;\n  font-size: 13px;\n  height: 26px;\n  margin: 0px;\n  padding: 3px 5px;\n  width: 170px;\n}\n.ql-snow .ql-tooltip a.ql-preview {\n  display: inline-block;\n  max-width: 200px;\n  overflow-x: hidden;\n  text-overflow: ellipsis;\n  vertical-align: top;\n}\n.ql-snow .ql-tooltip a.ql-action::after {\n  border-right: 1px solid #ccc;\n  content: 'Edit';\n  margin-left: 16px;\n  padding-right: 8px;\n}\n.ql-snow .ql-tooltip a.ql-remove::before {\n  content: 'Remove';\n  margin-left: 8px;\n}\n.ql-snow .ql-tooltip a {\n  line-height: 26px;\n}\n.ql-snow .ql-tooltip.ql-editing a.ql-preview,\n.ql-snow .ql-tooltip.ql-editing a.ql-remove {\n  display: none;\n}\n.ql-snow .ql-tooltip.ql-editing input[type=text] {\n  display: inline-block;\n}\n.ql-snow .ql-tooltip.ql-editing a.ql-action::after {\n  border-right: 0px;\n  content: 'Save';\n  padding-right: 0px;\n}\n.ql-snow .ql-tooltip[data-mode=link]::before {\n  content: \"Enter link:\";\n}\n.ql-snow .ql-tooltip[data-mode=formula]::before {\n  content: \"Enter formula:\";\n}\n.ql-snow .ql-tooltip[data-mode=video]::before {\n  content: \"Enter video:\";\n}\n.ql-snow a {\n  color: #06c;\n}\n.ql-container.ql-snow {\n  border: 1px solid #ccc;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/remixicon/remixicon.css",
    "content": "/*\n* Remix Icon v2.5.0\n* https://remixicon.com\n* https://github.com/Remix-Design/RemixIcon\n*\n* Copyright RemixIcon.com\n* Released under the Apache License Version 2.0\n*\n* Date: 2020-05-23\n*/\n@font-face {\n  font-family: \"remixicon\";\n  src: url('remixicon.eot?t=1590207869815'); /* IE9*/\n  src: url('remixicon.eot?t=1590207869815#iefix') format('embedded-opentype'), /* IE6-IE8 */\n  url(\"remixicon.woff2?t=1590207869815\") format(\"woff2\"),\n  url(\"remixicon.woff?t=1590207869815\") format(\"woff\"),\n  url('remixicon.ttf?t=1590207869815') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/\n  url('remixicon.svg?t=1590207869815#remixicon') format('svg'); /* iOS 4.1- */\n  font-display: swap;\n}\n\n[class^=\"ri-\"], [class*=\" ri-\"] {\n  font-family: 'remixicon' !important;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.ri-lg { font-size: 1.3333em; line-height: 0.75em; vertical-align: -.0667em; }\n.ri-xl { font-size: 1.5em; line-height: 0.6666em; vertical-align: -.075em; }\n.ri-xxs { font-size: .5em; }\n.ri-xs { font-size: .75em; }\n.ri-sm { font-size: .875em }\n.ri-1x { font-size: 1em; }\n.ri-2x { font-size: 2em; }\n.ri-3x { font-size: 3em; }\n.ri-4x { font-size: 4em; }\n.ri-5x { font-size: 5em; }\n.ri-6x { font-size: 6em; }\n.ri-7x { font-size: 7em; }\n.ri-8x { font-size: 8em; }\n.ri-9x { font-size: 9em; }\n.ri-10x { font-size: 10em; }\n.ri-fw { text-align: center; width: 1.25em; }\n\n.ri-24-hours-fill:before { content: \"\\ea01\"; }\n.ri-24-hours-line:before { content: \"\\ea02\"; }\n.ri-4k-fill:before { content: \"\\ea03\"; }\n.ri-4k-line:before { content: \"\\ea04\"; }\n.ri-a-b:before { content: \"\\ea05\"; }\n.ri-account-box-fill:before { content: \"\\ea06\"; }\n.ri-account-box-line:before { content: \"\\ea07\"; }\n.ri-account-circle-fill:before { content: \"\\ea08\"; }\n.ri-account-circle-line:before { content: \"\\ea09\"; }\n.ri-account-pin-box-fill:before { content: \"\\ea0a\"; }\n.ri-account-pin-box-line:before { content: \"\\ea0b\"; }\n.ri-account-pin-circle-fill:before { content: \"\\ea0c\"; }\n.ri-account-pin-circle-line:before { content: \"\\ea0d\"; }\n.ri-add-box-fill:before { content: \"\\ea0e\"; }\n.ri-add-box-line:before { content: \"\\ea0f\"; }\n.ri-add-circle-fill:before { content: \"\\ea10\"; }\n.ri-add-circle-line:before { content: \"\\ea11\"; }\n.ri-add-fill:before { content: \"\\ea12\"; }\n.ri-add-line:before { content: \"\\ea13\"; }\n.ri-admin-fill:before { content: \"\\ea14\"; }\n.ri-admin-line:before { content: \"\\ea15\"; }\n.ri-advertisement-fill:before { content: \"\\ea16\"; }\n.ri-advertisement-line:before { content: \"\\ea17\"; }\n.ri-airplay-fill:before { content: \"\\ea18\"; }\n.ri-airplay-line:before { content: \"\\ea19\"; }\n.ri-alarm-fill:before { content: \"\\ea1a\"; }\n.ri-alarm-line:before { content: \"\\ea1b\"; }\n.ri-alarm-warning-fill:before { content: \"\\ea1c\"; }\n.ri-alarm-warning-line:before { content: \"\\ea1d\"; }\n.ri-album-fill:before { content: \"\\ea1e\"; }\n.ri-album-line:before { content: \"\\ea1f\"; }\n.ri-alert-fill:before { content: \"\\ea20\"; }\n.ri-alert-line:before { content: \"\\ea21\"; }\n.ri-aliens-fill:before { content: \"\\ea22\"; }\n.ri-aliens-line:before { content: \"\\ea23\"; }\n.ri-align-bottom:before { content: \"\\ea24\"; }\n.ri-align-center:before { content: \"\\ea25\"; }\n.ri-align-justify:before { content: \"\\ea26\"; }\n.ri-align-left:before { content: \"\\ea27\"; }\n.ri-align-right:before { content: \"\\ea28\"; }\n.ri-align-top:before { content: \"\\ea29\"; }\n.ri-align-vertically:before { content: \"\\ea2a\"; }\n.ri-alipay-fill:before { content: \"\\ea2b\"; }\n.ri-alipay-line:before { content: \"\\ea2c\"; }\n.ri-amazon-fill:before { content: \"\\ea2d\"; }\n.ri-amazon-line:before { content: \"\\ea2e\"; }\n.ri-anchor-fill:before { content: \"\\ea2f\"; }\n.ri-anchor-line:before { content: \"\\ea30\"; }\n.ri-ancient-gate-fill:before { content: \"\\ea31\"; }\n.ri-ancient-gate-line:before { content: \"\\ea32\"; }\n.ri-ancient-pavilion-fill:before { content: \"\\ea33\"; }\n.ri-ancient-pavilion-line:before { content: \"\\ea34\"; }\n.ri-android-fill:before { content: \"\\ea35\"; }\n.ri-android-line:before { content: \"\\ea36\"; }\n.ri-angularjs-fill:before { content: \"\\ea37\"; }\n.ri-angularjs-line:before { content: \"\\ea38\"; }\n.ri-anticlockwise-2-fill:before { content: \"\\ea39\"; }\n.ri-anticlockwise-2-line:before { content: \"\\ea3a\"; }\n.ri-anticlockwise-fill:before { content: \"\\ea3b\"; }\n.ri-anticlockwise-line:before { content: \"\\ea3c\"; }\n.ri-app-store-fill:before { content: \"\\ea3d\"; }\n.ri-app-store-line:before { content: \"\\ea3e\"; }\n.ri-apple-fill:before { content: \"\\ea3f\"; }\n.ri-apple-line:before { content: \"\\ea40\"; }\n.ri-apps-2-fill:before { content: \"\\ea41\"; }\n.ri-apps-2-line:before { content: \"\\ea42\"; }\n.ri-apps-fill:before { content: \"\\ea43\"; }\n.ri-apps-line:before { content: \"\\ea44\"; }\n.ri-archive-drawer-fill:before { content: \"\\ea45\"; }\n.ri-archive-drawer-line:before { content: \"\\ea46\"; }\n.ri-archive-fill:before { content: \"\\ea47\"; }\n.ri-archive-line:before { content: \"\\ea48\"; }\n.ri-arrow-down-circle-fill:before { content: \"\\ea49\"; }\n.ri-arrow-down-circle-line:before { content: \"\\ea4a\"; }\n.ri-arrow-down-fill:before { content: \"\\ea4b\"; }\n.ri-arrow-down-line:before { content: \"\\ea4c\"; }\n.ri-arrow-down-s-fill:before { content: \"\\ea4d\"; }\n.ri-arrow-down-s-line:before { content: \"\\ea4e\"; }\n.ri-arrow-drop-down-fill:before { content: \"\\ea4f\"; }\n.ri-arrow-drop-down-line:before { content: \"\\ea50\"; }\n.ri-arrow-drop-left-fill:before { content: \"\\ea51\"; }\n.ri-arrow-drop-left-line:before { content: \"\\ea52\"; }\n.ri-arrow-drop-right-fill:before { content: \"\\ea53\"; }\n.ri-arrow-drop-right-line:before { content: \"\\ea54\"; }\n.ri-arrow-drop-up-fill:before { content: \"\\ea55\"; }\n.ri-arrow-drop-up-line:before { content: \"\\ea56\"; }\n.ri-arrow-go-back-fill:before { content: \"\\ea57\"; }\n.ri-arrow-go-back-line:before { content: \"\\ea58\"; }\n.ri-arrow-go-forward-fill:before { content: \"\\ea59\"; }\n.ri-arrow-go-forward-line:before { content: \"\\ea5a\"; }\n.ri-arrow-left-circle-fill:before { content: \"\\ea5b\"; }\n.ri-arrow-left-circle-line:before { content: \"\\ea5c\"; }\n.ri-arrow-left-down-fill:before { content: \"\\ea5d\"; }\n.ri-arrow-left-down-line:before { content: \"\\ea5e\"; }\n.ri-arrow-left-fill:before { content: \"\\ea5f\"; }\n.ri-arrow-left-line:before { content: \"\\ea60\"; }\n.ri-arrow-left-right-fill:before { content: \"\\ea61\"; }\n.ri-arrow-left-right-line:before { content: \"\\ea62\"; }\n.ri-arrow-left-s-fill:before { content: \"\\ea63\"; }\n.ri-arrow-left-s-line:before { content: \"\\ea64\"; }\n.ri-arrow-left-up-fill:before { content: \"\\ea65\"; }\n.ri-arrow-left-up-line:before { content: \"\\ea66\"; }\n.ri-arrow-right-circle-fill:before { content: \"\\ea67\"; }\n.ri-arrow-right-circle-line:before { content: \"\\ea68\"; }\n.ri-arrow-right-down-fill:before { content: \"\\ea69\"; }\n.ri-arrow-right-down-line:before { content: \"\\ea6a\"; }\n.ri-arrow-right-fill:before { content: \"\\ea6b\"; }\n.ri-arrow-right-line:before { content: \"\\ea6c\"; }\n.ri-arrow-right-s-fill:before { content: \"\\ea6d\"; }\n.ri-arrow-right-s-line:before { content: \"\\ea6e\"; }\n.ri-arrow-right-up-fill:before { content: \"\\ea6f\"; }\n.ri-arrow-right-up-line:before { content: \"\\ea70\"; }\n.ri-arrow-up-circle-fill:before { content: \"\\ea71\"; }\n.ri-arrow-up-circle-line:before { content: \"\\ea72\"; }\n.ri-arrow-up-down-fill:before { content: \"\\ea73\"; }\n.ri-arrow-up-down-line:before { content: \"\\ea74\"; }\n.ri-arrow-up-fill:before { content: \"\\ea75\"; }\n.ri-arrow-up-line:before { content: \"\\ea76\"; }\n.ri-arrow-up-s-fill:before { content: \"\\ea77\"; }\n.ri-arrow-up-s-line:before { content: \"\\ea78\"; }\n.ri-artboard-2-fill:before { content: \"\\ea79\"; }\n.ri-artboard-2-line:before { content: \"\\ea7a\"; }\n.ri-artboard-fill:before { content: \"\\ea7b\"; }\n.ri-artboard-line:before { content: \"\\ea7c\"; }\n.ri-article-fill:before { content: \"\\ea7d\"; }\n.ri-article-line:before { content: \"\\ea7e\"; }\n.ri-aspect-ratio-fill:before { content: \"\\ea7f\"; }\n.ri-aspect-ratio-line:before { content: \"\\ea80\"; }\n.ri-asterisk:before { content: \"\\ea81\"; }\n.ri-at-fill:before { content: \"\\ea82\"; }\n.ri-at-line:before { content: \"\\ea83\"; }\n.ri-attachment-2:before { content: \"\\ea84\"; }\n.ri-attachment-fill:before { content: \"\\ea85\"; }\n.ri-attachment-line:before { content: \"\\ea86\"; }\n.ri-auction-fill:before { content: \"\\ea87\"; }\n.ri-auction-line:before { content: \"\\ea88\"; }\n.ri-award-fill:before { content: \"\\ea89\"; }\n.ri-award-line:before { content: \"\\ea8a\"; }\n.ri-baidu-fill:before { content: \"\\ea8b\"; }\n.ri-baidu-line:before { content: \"\\ea8c\"; }\n.ri-ball-pen-fill:before { content: \"\\ea8d\"; }\n.ri-ball-pen-line:before { content: \"\\ea8e\"; }\n.ri-bank-card-2-fill:before { content: \"\\ea8f\"; }\n.ri-bank-card-2-line:before { content: \"\\ea90\"; }\n.ri-bank-card-fill:before { content: \"\\ea91\"; }\n.ri-bank-card-line:before { content: \"\\ea92\"; }\n.ri-bank-fill:before { content: \"\\ea93\"; }\n.ri-bank-line:before { content: \"\\ea94\"; }\n.ri-bar-chart-2-fill:before { content: \"\\ea95\"; }\n.ri-bar-chart-2-line:before { content: \"\\ea96\"; }\n.ri-bar-chart-box-fill:before { content: \"\\ea97\"; }\n.ri-bar-chart-box-line:before { content: \"\\ea98\"; }\n.ri-bar-chart-fill:before { content: \"\\ea99\"; }\n.ri-bar-chart-grouped-fill:before { content: \"\\ea9a\"; }\n.ri-bar-chart-grouped-line:before { content: \"\\ea9b\"; }\n.ri-bar-chart-horizontal-fill:before { content: \"\\ea9c\"; }\n.ri-bar-chart-horizontal-line:before { content: \"\\ea9d\"; }\n.ri-bar-chart-line:before { content: \"\\ea9e\"; }\n.ri-barcode-box-fill:before { content: \"\\ea9f\"; }\n.ri-barcode-box-line:before { content: \"\\eaa0\"; }\n.ri-barcode-fill:before { content: \"\\eaa1\"; }\n.ri-barcode-line:before { content: \"\\eaa2\"; }\n.ri-barricade-fill:before { content: \"\\eaa3\"; }\n.ri-barricade-line:before { content: \"\\eaa4\"; }\n.ri-base-station-fill:before { content: \"\\eaa5\"; }\n.ri-base-station-line:before { content: \"\\eaa6\"; }\n.ri-basketball-fill:before { content: \"\\eaa7\"; }\n.ri-basketball-line:before { content: \"\\eaa8\"; }\n.ri-battery-2-charge-fill:before { content: \"\\eaa9\"; }\n.ri-battery-2-charge-line:before { content: \"\\eaaa\"; }\n.ri-battery-2-fill:before { content: \"\\eaab\"; }\n.ri-battery-2-line:before { content: \"\\eaac\"; }\n.ri-battery-charge-fill:before { content: \"\\eaad\"; }\n.ri-battery-charge-line:before { content: \"\\eaae\"; }\n.ri-battery-fill:before { content: \"\\eaaf\"; }\n.ri-battery-line:before { content: \"\\eab0\"; }\n.ri-battery-low-fill:before { content: \"\\eab1\"; }\n.ri-battery-low-line:before { content: \"\\eab2\"; }\n.ri-battery-saver-fill:before { content: \"\\eab3\"; }\n.ri-battery-saver-line:before { content: \"\\eab4\"; }\n.ri-battery-share-fill:before { content: \"\\eab5\"; }\n.ri-battery-share-line:before { content: \"\\eab6\"; }\n.ri-bear-smile-fill:before { content: \"\\eab7\"; }\n.ri-bear-smile-line:before { content: \"\\eab8\"; }\n.ri-behance-fill:before { content: \"\\eab9\"; }\n.ri-behance-line:before { content: \"\\eaba\"; }\n.ri-bell-fill:before { content: \"\\eabb\"; }\n.ri-bell-line:before { content: \"\\eabc\"; }\n.ri-bike-fill:before { content: \"\\eabd\"; }\n.ri-bike-line:before { content: \"\\eabe\"; }\n.ri-bilibili-fill:before { content: \"\\eabf\"; }\n.ri-bilibili-line:before { content: \"\\eac0\"; }\n.ri-bill-fill:before { content: \"\\eac1\"; }\n.ri-bill-line:before { content: \"\\eac2\"; }\n.ri-billiards-fill:before { content: \"\\eac3\"; }\n.ri-billiards-line:before { content: \"\\eac4\"; }\n.ri-bit-coin-fill:before { content: \"\\eac5\"; }\n.ri-bit-coin-line:before { content: \"\\eac6\"; }\n.ri-blaze-fill:before { content: \"\\eac7\"; }\n.ri-blaze-line:before { content: \"\\eac8\"; }\n.ri-bluetooth-connect-fill:before { content: \"\\eac9\"; }\n.ri-bluetooth-connect-line:before { content: \"\\eaca\"; }\n.ri-bluetooth-fill:before { content: \"\\eacb\"; }\n.ri-bluetooth-line:before { content: \"\\eacc\"; }\n.ri-blur-off-fill:before { content: \"\\eacd\"; }\n.ri-blur-off-line:before { content: \"\\eace\"; }\n.ri-body-scan-fill:before { content: \"\\eacf\"; }\n.ri-body-scan-line:before { content: \"\\ead0\"; }\n.ri-bold:before { content: \"\\ead1\"; }\n.ri-book-2-fill:before { content: \"\\ead2\"; }\n.ri-book-2-line:before { content: \"\\ead3\"; }\n.ri-book-3-fill:before { content: \"\\ead4\"; }\n.ri-book-3-line:before { content: \"\\ead5\"; }\n.ri-book-fill:before { content: \"\\ead6\"; }\n.ri-book-line:before { content: \"\\ead7\"; }\n.ri-book-mark-fill:before { content: \"\\ead8\"; }\n.ri-book-mark-line:before { content: \"\\ead9\"; }\n.ri-book-open-fill:before { content: \"\\eada\"; }\n.ri-book-open-line:before { content: \"\\eadb\"; }\n.ri-book-read-fill:before { content: \"\\eadc\"; }\n.ri-book-read-line:before { content: \"\\eadd\"; }\n.ri-booklet-fill:before { content: \"\\eade\"; }\n.ri-booklet-line:before { content: \"\\eadf\"; }\n.ri-bookmark-2-fill:before { content: \"\\eae0\"; }\n.ri-bookmark-2-line:before { content: \"\\eae1\"; }\n.ri-bookmark-3-fill:before { content: \"\\eae2\"; }\n.ri-bookmark-3-line:before { content: \"\\eae3\"; }\n.ri-bookmark-fill:before { content: \"\\eae4\"; }\n.ri-bookmark-line:before { content: \"\\eae5\"; }\n.ri-boxing-fill:before { content: \"\\eae6\"; }\n.ri-boxing-line:before { content: \"\\eae7\"; }\n.ri-braces-fill:before { content: \"\\eae8\"; }\n.ri-braces-line:before { content: \"\\eae9\"; }\n.ri-brackets-fill:before { content: \"\\eaea\"; }\n.ri-brackets-line:before { content: \"\\eaeb\"; }\n.ri-briefcase-2-fill:before { content: \"\\eaec\"; }\n.ri-briefcase-2-line:before { content: \"\\eaed\"; }\n.ri-briefcase-3-fill:before { content: \"\\eaee\"; }\n.ri-briefcase-3-line:before { content: \"\\eaef\"; }\n.ri-briefcase-4-fill:before { content: \"\\eaf0\"; }\n.ri-briefcase-4-line:before { content: \"\\eaf1\"; }\n.ri-briefcase-5-fill:before { content: \"\\eaf2\"; }\n.ri-briefcase-5-line:before { content: \"\\eaf3\"; }\n.ri-briefcase-fill:before { content: \"\\eaf4\"; }\n.ri-briefcase-line:before { content: \"\\eaf5\"; }\n.ri-bring-forward:before { content: \"\\eaf6\"; }\n.ri-bring-to-front:before { content: \"\\eaf7\"; }\n.ri-broadcast-fill:before { content: \"\\eaf8\"; }\n.ri-broadcast-line:before { content: \"\\eaf9\"; }\n.ri-brush-2-fill:before { content: \"\\eafa\"; }\n.ri-brush-2-line:before { content: \"\\eafb\"; }\n.ri-brush-3-fill:before { content: \"\\eafc\"; }\n.ri-brush-3-line:before { content: \"\\eafd\"; }\n.ri-brush-4-fill:before { content: \"\\eafe\"; }\n.ri-brush-4-line:before { content: \"\\eaff\"; }\n.ri-brush-fill:before { content: \"\\eb00\"; }\n.ri-brush-line:before { content: \"\\eb01\"; }\n.ri-bubble-chart-fill:before { content: \"\\eb02\"; }\n.ri-bubble-chart-line:before { content: \"\\eb03\"; }\n.ri-bug-2-fill:before { content: \"\\eb04\"; }\n.ri-bug-2-line:before { content: \"\\eb05\"; }\n.ri-bug-fill:before { content: \"\\eb06\"; }\n.ri-bug-line:before { content: \"\\eb07\"; }\n.ri-building-2-fill:before { content: \"\\eb08\"; }\n.ri-building-2-line:before { content: \"\\eb09\"; }\n.ri-building-3-fill:before { content: \"\\eb0a\"; }\n.ri-building-3-line:before { content: \"\\eb0b\"; }\n.ri-building-4-fill:before { content: \"\\eb0c\"; }\n.ri-building-4-line:before { content: \"\\eb0d\"; }\n.ri-building-fill:before { content: \"\\eb0e\"; }\n.ri-building-line:before { content: \"\\eb0f\"; }\n.ri-bus-2-fill:before { content: \"\\eb10\"; }\n.ri-bus-2-line:before { content: \"\\eb11\"; }\n.ri-bus-fill:before { content: \"\\eb12\"; }\n.ri-bus-line:before { content: \"\\eb13\"; }\n.ri-bus-wifi-fill:before { content: \"\\eb14\"; }\n.ri-bus-wifi-line:before { content: \"\\eb15\"; }\n.ri-cactus-fill:before { content: \"\\eb16\"; }\n.ri-cactus-line:before { content: \"\\eb17\"; }\n.ri-cake-2-fill:before { content: \"\\eb18\"; }\n.ri-cake-2-line:before { content: \"\\eb19\"; }\n.ri-cake-3-fill:before { content: \"\\eb1a\"; }\n.ri-cake-3-line:before { content: \"\\eb1b\"; }\n.ri-cake-fill:before { content: \"\\eb1c\"; }\n.ri-cake-line:before { content: \"\\eb1d\"; }\n.ri-calculator-fill:before { content: \"\\eb1e\"; }\n.ri-calculator-line:before { content: \"\\eb1f\"; }\n.ri-calendar-2-fill:before { content: \"\\eb20\"; }\n.ri-calendar-2-line:before { content: \"\\eb21\"; }\n.ri-calendar-check-fill:before { content: \"\\eb22\"; }\n.ri-calendar-check-line:before { content: \"\\eb23\"; }\n.ri-calendar-event-fill:before { content: \"\\eb24\"; }\n.ri-calendar-event-line:before { content: \"\\eb25\"; }\n.ri-calendar-fill:before { content: \"\\eb26\"; }\n.ri-calendar-line:before { content: \"\\eb27\"; }\n.ri-calendar-todo-fill:before { content: \"\\eb28\"; }\n.ri-calendar-todo-line:before { content: \"\\eb29\"; }\n.ri-camera-2-fill:before { content: \"\\eb2a\"; }\n.ri-camera-2-line:before { content: \"\\eb2b\"; }\n.ri-camera-3-fill:before { content: \"\\eb2c\"; }\n.ri-camera-3-line:before { content: \"\\eb2d\"; }\n.ri-camera-fill:before { content: \"\\eb2e\"; }\n.ri-camera-lens-fill:before { content: \"\\eb2f\"; }\n.ri-camera-lens-line:before { content: \"\\eb30\"; }\n.ri-camera-line:before { content: \"\\eb31\"; }\n.ri-camera-off-fill:before { content: \"\\eb32\"; }\n.ri-camera-off-line:before { content: \"\\eb33\"; }\n.ri-camera-switch-fill:before { content: \"\\eb34\"; }\n.ri-camera-switch-line:before { content: \"\\eb35\"; }\n.ri-capsule-fill:before { content: \"\\eb36\"; }\n.ri-capsule-line:before { content: \"\\eb37\"; }\n.ri-car-fill:before { content: \"\\eb38\"; }\n.ri-car-line:before { content: \"\\eb39\"; }\n.ri-car-washing-fill:before { content: \"\\eb3a\"; }\n.ri-car-washing-line:before { content: \"\\eb3b\"; }\n.ri-caravan-fill:before { content: \"\\eb3c\"; }\n.ri-caravan-line:before { content: \"\\eb3d\"; }\n.ri-cast-fill:before { content: \"\\eb3e\"; }\n.ri-cast-line:before { content: \"\\eb3f\"; }\n.ri-cellphone-fill:before { content: \"\\eb40\"; }\n.ri-cellphone-line:before { content: \"\\eb41\"; }\n.ri-celsius-fill:before { content: \"\\eb42\"; }\n.ri-celsius-line:before { content: \"\\eb43\"; }\n.ri-centos-fill:before { content: \"\\eb44\"; }\n.ri-centos-line:before { content: \"\\eb45\"; }\n.ri-character-recognition-fill:before { content: \"\\eb46\"; }\n.ri-character-recognition-line:before { content: \"\\eb47\"; }\n.ri-charging-pile-2-fill:before { content: \"\\eb48\"; }\n.ri-charging-pile-2-line:before { content: \"\\eb49\"; }\n.ri-charging-pile-fill:before { content: \"\\eb4a\"; }\n.ri-charging-pile-line:before { content: \"\\eb4b\"; }\n.ri-chat-1-fill:before { content: \"\\eb4c\"; }\n.ri-chat-1-line:before { content: \"\\eb4d\"; }\n.ri-chat-2-fill:before { content: \"\\eb4e\"; }\n.ri-chat-2-line:before { content: \"\\eb4f\"; }\n.ri-chat-3-fill:before { content: \"\\eb50\"; }\n.ri-chat-3-line:before { content: \"\\eb51\"; }\n.ri-chat-4-fill:before { content: \"\\eb52\"; }\n.ri-chat-4-line:before { content: \"\\eb53\"; }\n.ri-chat-check-fill:before { content: \"\\eb54\"; }\n.ri-chat-check-line:before { content: \"\\eb55\"; }\n.ri-chat-delete-fill:before { content: \"\\eb56\"; }\n.ri-chat-delete-line:before { content: \"\\eb57\"; }\n.ri-chat-download-fill:before { content: \"\\eb58\"; }\n.ri-chat-download-line:before { content: \"\\eb59\"; }\n.ri-chat-follow-up-fill:before { content: \"\\eb5a\"; }\n.ri-chat-follow-up-line:before { content: \"\\eb5b\"; }\n.ri-chat-forward-fill:before { content: \"\\eb5c\"; }\n.ri-chat-forward-line:before { content: \"\\eb5d\"; }\n.ri-chat-heart-fill:before { content: \"\\eb5e\"; }\n.ri-chat-heart-line:before { content: \"\\eb5f\"; }\n.ri-chat-history-fill:before { content: \"\\eb60\"; }\n.ri-chat-history-line:before { content: \"\\eb61\"; }\n.ri-chat-new-fill:before { content: \"\\eb62\"; }\n.ri-chat-new-line:before { content: \"\\eb63\"; }\n.ri-chat-off-fill:before { content: \"\\eb64\"; }\n.ri-chat-off-line:before { content: \"\\eb65\"; }\n.ri-chat-poll-fill:before { content: \"\\eb66\"; }\n.ri-chat-poll-line:before { content: \"\\eb67\"; }\n.ri-chat-private-fill:before { content: \"\\eb68\"; }\n.ri-chat-private-line:before { content: \"\\eb69\"; }\n.ri-chat-quote-fill:before { content: \"\\eb6a\"; }\n.ri-chat-quote-line:before { content: \"\\eb6b\"; }\n.ri-chat-settings-fill:before { content: \"\\eb6c\"; }\n.ri-chat-settings-line:before { content: \"\\eb6d\"; }\n.ri-chat-smile-2-fill:before { content: \"\\eb6e\"; }\n.ri-chat-smile-2-line:before { content: \"\\eb6f\"; }\n.ri-chat-smile-3-fill:before { content: \"\\eb70\"; }\n.ri-chat-smile-3-line:before { content: \"\\eb71\"; }\n.ri-chat-smile-fill:before { content: \"\\eb72\"; }\n.ri-chat-smile-line:before { content: \"\\eb73\"; }\n.ri-chat-upload-fill:before { content: \"\\eb74\"; }\n.ri-chat-upload-line:before { content: \"\\eb75\"; }\n.ri-chat-voice-fill:before { content: \"\\eb76\"; }\n.ri-chat-voice-line:before { content: \"\\eb77\"; }\n.ri-check-double-fill:before { content: \"\\eb78\"; }\n.ri-check-double-line:before { content: \"\\eb79\"; }\n.ri-check-fill:before { content: \"\\eb7a\"; }\n.ri-check-line:before { content: \"\\eb7b\"; }\n.ri-checkbox-blank-circle-fill:before { content: \"\\eb7c\"; }\n.ri-checkbox-blank-circle-line:before { content: \"\\eb7d\"; }\n.ri-checkbox-blank-fill:before { content: \"\\eb7e\"; }\n.ri-checkbox-blank-line:before { content: \"\\eb7f\"; }\n.ri-checkbox-circle-fill:before { content: \"\\eb80\"; }\n.ri-checkbox-circle-line:before { content: \"\\eb81\"; }\n.ri-checkbox-fill:before { content: \"\\eb82\"; }\n.ri-checkbox-indeterminate-fill:before { content: \"\\eb83\"; }\n.ri-checkbox-indeterminate-line:before { content: \"\\eb84\"; }\n.ri-checkbox-line:before { content: \"\\eb85\"; }\n.ri-checkbox-multiple-blank-fill:before { content: \"\\eb86\"; }\n.ri-checkbox-multiple-blank-line:before { content: \"\\eb87\"; }\n.ri-checkbox-multiple-fill:before { content: \"\\eb88\"; }\n.ri-checkbox-multiple-line:before { content: \"\\eb89\"; }\n.ri-china-railway-fill:before { content: \"\\eb8a\"; }\n.ri-china-railway-line:before { content: \"\\eb8b\"; }\n.ri-chrome-fill:before { content: \"\\eb8c\"; }\n.ri-chrome-line:before { content: \"\\eb8d\"; }\n.ri-clapperboard-fill:before { content: \"\\eb8e\"; }\n.ri-clapperboard-line:before { content: \"\\eb8f\"; }\n.ri-clipboard-fill:before { content: \"\\eb90\"; }\n.ri-clipboard-line:before { content: \"\\eb91\"; }\n.ri-clockwise-2-fill:before { content: \"\\eb92\"; }\n.ri-clockwise-2-line:before { content: \"\\eb93\"; }\n.ri-clockwise-fill:before { content: \"\\eb94\"; }\n.ri-clockwise-line:before { content: \"\\eb95\"; }\n.ri-close-circle-fill:before { content: \"\\eb96\"; }\n.ri-close-circle-line:before { content: \"\\eb97\"; }\n.ri-close-fill:before { content: \"\\eb98\"; }\n.ri-close-line:before { content: \"\\eb99\"; }\n.ri-closed-captioning-fill:before { content: \"\\eb9a\"; }\n.ri-closed-captioning-line:before { content: \"\\eb9b\"; }\n.ri-cloud-fill:before { content: \"\\eb9c\"; }\n.ri-cloud-line:before { content: \"\\eb9d\"; }\n.ri-cloud-off-fill:before { content: \"\\eb9e\"; }\n.ri-cloud-off-line:before { content: \"\\eb9f\"; }\n.ri-cloud-windy-fill:before { content: \"\\eba0\"; }\n.ri-cloud-windy-line:before { content: \"\\eba1\"; }\n.ri-cloudy-2-fill:before { content: \"\\eba2\"; }\n.ri-cloudy-2-line:before { content: \"\\eba3\"; }\n.ri-cloudy-fill:before { content: \"\\eba4\"; }\n.ri-cloudy-line:before { content: \"\\eba5\"; }\n.ri-code-box-fill:before { content: \"\\eba6\"; }\n.ri-code-box-line:before { content: \"\\eba7\"; }\n.ri-code-fill:before { content: \"\\eba8\"; }\n.ri-code-line:before { content: \"\\eba9\"; }\n.ri-code-s-fill:before { content: \"\\ebaa\"; }\n.ri-code-s-line:before { content: \"\\ebab\"; }\n.ri-code-s-slash-fill:before { content: \"\\ebac\"; }\n.ri-code-s-slash-line:before { content: \"\\ebad\"; }\n.ri-code-view:before { content: \"\\ebae\"; }\n.ri-codepen-fill:before { content: \"\\ebaf\"; }\n.ri-codepen-line:before { content: \"\\ebb0\"; }\n.ri-coin-fill:before { content: \"\\ebb1\"; }\n.ri-coin-line:before { content: \"\\ebb2\"; }\n.ri-coins-fill:before { content: \"\\ebb3\"; }\n.ri-coins-line:before { content: \"\\ebb4\"; }\n.ri-collage-fill:before { content: \"\\ebb5\"; }\n.ri-collage-line:before { content: \"\\ebb6\"; }\n.ri-command-fill:before { content: \"\\ebb7\"; }\n.ri-command-line:before { content: \"\\ebb8\"; }\n.ri-community-fill:before { content: \"\\ebb9\"; }\n.ri-community-line:before { content: \"\\ebba\"; }\n.ri-compass-2-fill:before { content: \"\\ebbb\"; }\n.ri-compass-2-line:before { content: \"\\ebbc\"; }\n.ri-compass-3-fill:before { content: \"\\ebbd\"; }\n.ri-compass-3-line:before { content: \"\\ebbe\"; }\n.ri-compass-4-fill:before { content: \"\\ebbf\"; }\n.ri-compass-4-line:before { content: \"\\ebc0\"; }\n.ri-compass-discover-fill:before { content: \"\\ebc1\"; }\n.ri-compass-discover-line:before { content: \"\\ebc2\"; }\n.ri-compass-fill:before { content: \"\\ebc3\"; }\n.ri-compass-line:before { content: \"\\ebc4\"; }\n.ri-compasses-2-fill:before { content: \"\\ebc5\"; }\n.ri-compasses-2-line:before { content: \"\\ebc6\"; }\n.ri-compasses-fill:before { content: \"\\ebc7\"; }\n.ri-compasses-line:before { content: \"\\ebc8\"; }\n.ri-computer-fill:before { content: \"\\ebc9\"; }\n.ri-computer-line:before { content: \"\\ebca\"; }\n.ri-contacts-book-2-fill:before { content: \"\\ebcb\"; }\n.ri-contacts-book-2-line:before { content: \"\\ebcc\"; }\n.ri-contacts-book-fill:before { content: \"\\ebcd\"; }\n.ri-contacts-book-line:before { content: \"\\ebce\"; }\n.ri-contacts-book-upload-fill:before { content: \"\\ebcf\"; }\n.ri-contacts-book-upload-line:before { content: \"\\ebd0\"; }\n.ri-contacts-fill:before { content: \"\\ebd1\"; }\n.ri-contacts-line:before { content: \"\\ebd2\"; }\n.ri-contrast-2-fill:before { content: \"\\ebd3\"; }\n.ri-contrast-2-line:before { content: \"\\ebd4\"; }\n.ri-contrast-drop-2-fill:before { content: \"\\ebd5\"; }\n.ri-contrast-drop-2-line:before { content: \"\\ebd6\"; }\n.ri-contrast-drop-fill:before { content: \"\\ebd7\"; }\n.ri-contrast-drop-line:before { content: \"\\ebd8\"; }\n.ri-contrast-fill:before { content: \"\\ebd9\"; }\n.ri-contrast-line:before { content: \"\\ebda\"; }\n.ri-copper-coin-fill:before { content: \"\\ebdb\"; }\n.ri-copper-coin-line:before { content: \"\\ebdc\"; }\n.ri-copper-diamond-fill:before { content: \"\\ebdd\"; }\n.ri-copper-diamond-line:before { content: \"\\ebde\"; }\n.ri-copyleft-fill:before { content: \"\\ebdf\"; }\n.ri-copyleft-line:before { content: \"\\ebe0\"; }\n.ri-copyright-fill:before { content: \"\\ebe1\"; }\n.ri-copyright-line:before { content: \"\\ebe2\"; }\n.ri-coreos-fill:before { content: \"\\ebe3\"; }\n.ri-coreos-line:before { content: \"\\ebe4\"; }\n.ri-coupon-2-fill:before { content: \"\\ebe5\"; }\n.ri-coupon-2-line:before { content: \"\\ebe6\"; }\n.ri-coupon-3-fill:before { content: \"\\ebe7\"; }\n.ri-coupon-3-line:before { content: \"\\ebe8\"; }\n.ri-coupon-4-fill:before { content: \"\\ebe9\"; }\n.ri-coupon-4-line:before { content: \"\\ebea\"; }\n.ri-coupon-5-fill:before { content: \"\\ebeb\"; }\n.ri-coupon-5-line:before { content: \"\\ebec\"; }\n.ri-coupon-fill:before { content: \"\\ebed\"; }\n.ri-coupon-line:before { content: \"\\ebee\"; }\n.ri-cpu-fill:before { content: \"\\ebef\"; }\n.ri-cpu-line:before { content: \"\\ebf0\"; }\n.ri-creative-commons-by-fill:before { content: \"\\ebf1\"; }\n.ri-creative-commons-by-line:before { content: \"\\ebf2\"; }\n.ri-creative-commons-fill:before { content: \"\\ebf3\"; }\n.ri-creative-commons-line:before { content: \"\\ebf4\"; }\n.ri-creative-commons-nc-fill:before { content: \"\\ebf5\"; }\n.ri-creative-commons-nc-line:before { content: \"\\ebf6\"; }\n.ri-creative-commons-nd-fill:before { content: \"\\ebf7\"; }\n.ri-creative-commons-nd-line:before { content: \"\\ebf8\"; }\n.ri-creative-commons-sa-fill:before { content: \"\\ebf9\"; }\n.ri-creative-commons-sa-line:before { content: \"\\ebfa\"; }\n.ri-creative-commons-zero-fill:before { content: \"\\ebfb\"; }\n.ri-creative-commons-zero-line:before { content: \"\\ebfc\"; }\n.ri-criminal-fill:before { content: \"\\ebfd\"; }\n.ri-criminal-line:before { content: \"\\ebfe\"; }\n.ri-crop-2-fill:before { content: \"\\ebff\"; }\n.ri-crop-2-line:before { content: \"\\ec00\"; }\n.ri-crop-fill:before { content: \"\\ec01\"; }\n.ri-crop-line:before { content: \"\\ec02\"; }\n.ri-css3-fill:before { content: \"\\ec03\"; }\n.ri-css3-line:before { content: \"\\ec04\"; }\n.ri-cup-fill:before { content: \"\\ec05\"; }\n.ri-cup-line:before { content: \"\\ec06\"; }\n.ri-currency-fill:before { content: \"\\ec07\"; }\n.ri-currency-line:before { content: \"\\ec08\"; }\n.ri-cursor-fill:before { content: \"\\ec09\"; }\n.ri-cursor-line:before { content: \"\\ec0a\"; }\n.ri-customer-service-2-fill:before { content: \"\\ec0b\"; }\n.ri-customer-service-2-line:before { content: \"\\ec0c\"; }\n.ri-customer-service-fill:before { content: \"\\ec0d\"; }\n.ri-customer-service-line:before { content: \"\\ec0e\"; }\n.ri-dashboard-2-fill:before { content: \"\\ec0f\"; }\n.ri-dashboard-2-line:before { content: \"\\ec10\"; }\n.ri-dashboard-3-fill:before { content: \"\\ec11\"; }\n.ri-dashboard-3-line:before { content: \"\\ec12\"; }\n.ri-dashboard-fill:before { content: \"\\ec13\"; }\n.ri-dashboard-line:before { content: \"\\ec14\"; }\n.ri-database-2-fill:before { content: \"\\ec15\"; }\n.ri-database-2-line:before { content: \"\\ec16\"; }\n.ri-database-fill:before { content: \"\\ec17\"; }\n.ri-database-line:before { content: \"\\ec18\"; }\n.ri-delete-back-2-fill:before { content: \"\\ec19\"; }\n.ri-delete-back-2-line:before { content: \"\\ec1a\"; }\n.ri-delete-back-fill:before { content: \"\\ec1b\"; }\n.ri-delete-back-line:before { content: \"\\ec1c\"; }\n.ri-delete-bin-2-fill:before { content: \"\\ec1d\"; }\n.ri-delete-bin-2-line:before { content: \"\\ec1e\"; }\n.ri-delete-bin-3-fill:before { content: \"\\ec1f\"; }\n.ri-delete-bin-3-line:before { content: \"\\ec20\"; }\n.ri-delete-bin-4-fill:before { content: \"\\ec21\"; }\n.ri-delete-bin-4-line:before { content: \"\\ec22\"; }\n.ri-delete-bin-5-fill:before { content: \"\\ec23\"; }\n.ri-delete-bin-5-line:before { content: \"\\ec24\"; }\n.ri-delete-bin-6-fill:before { content: \"\\ec25\"; }\n.ri-delete-bin-6-line:before { content: \"\\ec26\"; }\n.ri-delete-bin-7-fill:before { content: \"\\ec27\"; }\n.ri-delete-bin-7-line:before { content: \"\\ec28\"; }\n.ri-delete-bin-fill:before { content: \"\\ec29\"; }\n.ri-delete-bin-line:before { content: \"\\ec2a\"; }\n.ri-delete-column:before { content: \"\\ec2b\"; }\n.ri-delete-row:before { content: \"\\ec2c\"; }\n.ri-device-fill:before { content: \"\\ec2d\"; }\n.ri-device-line:before { content: \"\\ec2e\"; }\n.ri-device-recover-fill:before { content: \"\\ec2f\"; }\n.ri-device-recover-line:before { content: \"\\ec30\"; }\n.ri-dingding-fill:before { content: \"\\ec31\"; }\n.ri-dingding-line:before { content: \"\\ec32\"; }\n.ri-direction-fill:before { content: \"\\ec33\"; }\n.ri-direction-line:before { content: \"\\ec34\"; }\n.ri-disc-fill:before { content: \"\\ec35\"; }\n.ri-disc-line:before { content: \"\\ec36\"; }\n.ri-discord-fill:before { content: \"\\ec37\"; }\n.ri-discord-line:before { content: \"\\ec38\"; }\n.ri-discuss-fill:before { content: \"\\ec39\"; }\n.ri-discuss-line:before { content: \"\\ec3a\"; }\n.ri-dislike-fill:before { content: \"\\ec3b\"; }\n.ri-dislike-line:before { content: \"\\ec3c\"; }\n.ri-disqus-fill:before { content: \"\\ec3d\"; }\n.ri-disqus-line:before { content: \"\\ec3e\"; }\n.ri-divide-fill:before { content: \"\\ec3f\"; }\n.ri-divide-line:before { content: \"\\ec40\"; }\n.ri-donut-chart-fill:before { content: \"\\ec41\"; }\n.ri-donut-chart-line:before { content: \"\\ec42\"; }\n.ri-door-closed-fill:before { content: \"\\ec43\"; }\n.ri-door-closed-line:before { content: \"\\ec44\"; }\n.ri-door-fill:before { content: \"\\ec45\"; }\n.ri-door-line:before { content: \"\\ec46\"; }\n.ri-door-lock-box-fill:before { content: \"\\ec47\"; }\n.ri-door-lock-box-line:before { content: \"\\ec48\"; }\n.ri-door-lock-fill:before { content: \"\\ec49\"; }\n.ri-door-lock-line:before { content: \"\\ec4a\"; }\n.ri-door-open-fill:before { content: \"\\ec4b\"; }\n.ri-door-open-line:before { content: \"\\ec4c\"; }\n.ri-dossier-fill:before { content: \"\\ec4d\"; }\n.ri-dossier-line:before { content: \"\\ec4e\"; }\n.ri-douban-fill:before { content: \"\\ec4f\"; }\n.ri-douban-line:before { content: \"\\ec50\"; }\n.ri-double-quotes-l:before { content: \"\\ec51\"; }\n.ri-double-quotes-r:before { content: \"\\ec52\"; }\n.ri-download-2-fill:before { content: \"\\ec53\"; }\n.ri-download-2-line:before { content: \"\\ec54\"; }\n.ri-download-cloud-2-fill:before { content: \"\\ec55\"; }\n.ri-download-cloud-2-line:before { content: \"\\ec56\"; }\n.ri-download-cloud-fill:before { content: \"\\ec57\"; }\n.ri-download-cloud-line:before { content: \"\\ec58\"; }\n.ri-download-fill:before { content: \"\\ec59\"; }\n.ri-download-line:before { content: \"\\ec5a\"; }\n.ri-draft-fill:before { content: \"\\ec5b\"; }\n.ri-draft-line:before { content: \"\\ec5c\"; }\n.ri-drag-drop-fill:before { content: \"\\ec5d\"; }\n.ri-drag-drop-line:before { content: \"\\ec5e\"; }\n.ri-drag-move-2-fill:before { content: \"\\ec5f\"; }\n.ri-drag-move-2-line:before { content: \"\\ec60\"; }\n.ri-drag-move-fill:before { content: \"\\ec61\"; }\n.ri-drag-move-line:before { content: \"\\ec62\"; }\n.ri-dribbble-fill:before { content: \"\\ec63\"; }\n.ri-dribbble-line:before { content: \"\\ec64\"; }\n.ri-drive-fill:before { content: \"\\ec65\"; }\n.ri-drive-line:before { content: \"\\ec66\"; }\n.ri-drizzle-fill:before { content: \"\\ec67\"; }\n.ri-drizzle-line:before { content: \"\\ec68\"; }\n.ri-drop-fill:before { content: \"\\ec69\"; }\n.ri-drop-line:before { content: \"\\ec6a\"; }\n.ri-dropbox-fill:before { content: \"\\ec6b\"; }\n.ri-dropbox-line:before { content: \"\\ec6c\"; }\n.ri-dual-sim-1-fill:before { content: \"\\ec6d\"; }\n.ri-dual-sim-1-line:before { content: \"\\ec6e\"; }\n.ri-dual-sim-2-fill:before { content: \"\\ec6f\"; }\n.ri-dual-sim-2-line:before { content: \"\\ec70\"; }\n.ri-dv-fill:before { content: \"\\ec71\"; }\n.ri-dv-line:before { content: \"\\ec72\"; }\n.ri-dvd-fill:before { content: \"\\ec73\"; }\n.ri-dvd-line:before { content: \"\\ec74\"; }\n.ri-e-bike-2-fill:before { content: \"\\ec75\"; }\n.ri-e-bike-2-line:before { content: \"\\ec76\"; }\n.ri-e-bike-fill:before { content: \"\\ec77\"; }\n.ri-e-bike-line:before { content: \"\\ec78\"; }\n.ri-earth-fill:before { content: \"\\ec79\"; }\n.ri-earth-line:before { content: \"\\ec7a\"; }\n.ri-earthquake-fill:before { content: \"\\ec7b\"; }\n.ri-earthquake-line:before { content: \"\\ec7c\"; }\n.ri-edge-fill:before { content: \"\\ec7d\"; }\n.ri-edge-line:before { content: \"\\ec7e\"; }\n.ri-edit-2-fill:before { content: \"\\ec7f\"; }\n.ri-edit-2-line:before { content: \"\\ec80\"; }\n.ri-edit-box-fill:before { content: \"\\ec81\"; }\n.ri-edit-box-line:before { content: \"\\ec82\"; }\n.ri-edit-circle-fill:before { content: \"\\ec83\"; }\n.ri-edit-circle-line:before { content: \"\\ec84\"; }\n.ri-edit-fill:before { content: \"\\ec85\"; }\n.ri-edit-line:before { content: \"\\ec86\"; }\n.ri-eject-fill:before { content: \"\\ec87\"; }\n.ri-eject-line:before { content: \"\\ec88\"; }\n.ri-emotion-2-fill:before { content: \"\\ec89\"; }\n.ri-emotion-2-line:before { content: \"\\ec8a\"; }\n.ri-emotion-fill:before { content: \"\\ec8b\"; }\n.ri-emotion-happy-fill:before { content: \"\\ec8c\"; }\n.ri-emotion-happy-line:before { content: \"\\ec8d\"; }\n.ri-emotion-laugh-fill:before { content: \"\\ec8e\"; }\n.ri-emotion-laugh-line:before { content: \"\\ec8f\"; }\n.ri-emotion-line:before { content: \"\\ec90\"; }\n.ri-emotion-normal-fill:before { content: \"\\ec91\"; }\n.ri-emotion-normal-line:before { content: \"\\ec92\"; }\n.ri-emotion-sad-fill:before { content: \"\\ec93\"; }\n.ri-emotion-sad-line:before { content: \"\\ec94\"; }\n.ri-emotion-unhappy-fill:before { content: \"\\ec95\"; }\n.ri-emotion-unhappy-line:before { content: \"\\ec96\"; }\n.ri-empathize-fill:before { content: \"\\ec97\"; }\n.ri-empathize-line:before { content: \"\\ec98\"; }\n.ri-emphasis-cn:before { content: \"\\ec99\"; }\n.ri-emphasis:before { content: \"\\ec9a\"; }\n.ri-english-input:before { content: \"\\ec9b\"; }\n.ri-equalizer-fill:before { content: \"\\ec9c\"; }\n.ri-equalizer-line:before { content: \"\\ec9d\"; }\n.ri-eraser-fill:before { content: \"\\ec9e\"; }\n.ri-eraser-line:before { content: \"\\ec9f\"; }\n.ri-error-warning-fill:before { content: \"\\eca0\"; }\n.ri-error-warning-line:before { content: \"\\eca1\"; }\n.ri-evernote-fill:before { content: \"\\eca2\"; }\n.ri-evernote-line:before { content: \"\\eca3\"; }\n.ri-exchange-box-fill:before { content: \"\\eca4\"; }\n.ri-exchange-box-line:before { content: \"\\eca5\"; }\n.ri-exchange-cny-fill:before { content: \"\\eca6\"; }\n.ri-exchange-cny-line:before { content: \"\\eca7\"; }\n.ri-exchange-dollar-fill:before { content: \"\\eca8\"; }\n.ri-exchange-dollar-line:before { content: \"\\eca9\"; }\n.ri-exchange-fill:before { content: \"\\ecaa\"; }\n.ri-exchange-funds-fill:before { content: \"\\ecab\"; }\n.ri-exchange-funds-line:before { content: \"\\ecac\"; }\n.ri-exchange-line:before { content: \"\\ecad\"; }\n.ri-external-link-fill:before { content: \"\\ecae\"; }\n.ri-external-link-line:before { content: \"\\ecaf\"; }\n.ri-eye-2-fill:before { content: \"\\ecb0\"; }\n.ri-eye-2-line:before { content: \"\\ecb1\"; }\n.ri-eye-close-fill:before { content: \"\\ecb2\"; }\n.ri-eye-close-line:before { content: \"\\ecb3\"; }\n.ri-eye-fill:before { content: \"\\ecb4\"; }\n.ri-eye-line:before { content: \"\\ecb5\"; }\n.ri-eye-off-fill:before { content: \"\\ecb6\"; }\n.ri-eye-off-line:before { content: \"\\ecb7\"; }\n.ri-facebook-box-fill:before { content: \"\\ecb8\"; }\n.ri-facebook-box-line:before { content: \"\\ecb9\"; }\n.ri-facebook-circle-fill:before { content: \"\\ecba\"; }\n.ri-facebook-circle-line:before { content: \"\\ecbb\"; }\n.ri-facebook-fill:before { content: \"\\ecbc\"; }\n.ri-facebook-line:before { content: \"\\ecbd\"; }\n.ri-fahrenheit-fill:before { content: \"\\ecbe\"; }\n.ri-fahrenheit-line:before { content: \"\\ecbf\"; }\n.ri-feedback-fill:before { content: \"\\ecc0\"; }\n.ri-feedback-line:before { content: \"\\ecc1\"; }\n.ri-file-2-fill:before { content: \"\\ecc2\"; }\n.ri-file-2-line:before { content: \"\\ecc3\"; }\n.ri-file-3-fill:before { content: \"\\ecc4\"; }\n.ri-file-3-line:before { content: \"\\ecc5\"; }\n.ri-file-4-fill:before { content: \"\\ecc6\"; }\n.ri-file-4-line:before { content: \"\\ecc7\"; }\n.ri-file-add-fill:before { content: \"\\ecc8\"; }\n.ri-file-add-line:before { content: \"\\ecc9\"; }\n.ri-file-chart-2-fill:before { content: \"\\ecca\"; }\n.ri-file-chart-2-line:before { content: \"\\eccb\"; }\n.ri-file-chart-fill:before { content: \"\\eccc\"; }\n.ri-file-chart-line:before { content: \"\\eccd\"; }\n.ri-file-cloud-fill:before { content: \"\\ecce\"; }\n.ri-file-cloud-line:before { content: \"\\eccf\"; }\n.ri-file-code-fill:before { content: \"\\ecd0\"; }\n.ri-file-code-line:before { content: \"\\ecd1\"; }\n.ri-file-copy-2-fill:before { content: \"\\ecd2\"; }\n.ri-file-copy-2-line:before { content: \"\\ecd3\"; }\n.ri-file-copy-fill:before { content: \"\\ecd4\"; }\n.ri-file-copy-line:before { content: \"\\ecd5\"; }\n.ri-file-damage-fill:before { content: \"\\ecd6\"; }\n.ri-file-damage-line:before { content: \"\\ecd7\"; }\n.ri-file-download-fill:before { content: \"\\ecd8\"; }\n.ri-file-download-line:before { content: \"\\ecd9\"; }\n.ri-file-edit-fill:before { content: \"\\ecda\"; }\n.ri-file-edit-line:before { content: \"\\ecdb\"; }\n.ri-file-excel-2-fill:before { content: \"\\ecdc\"; }\n.ri-file-excel-2-line:before { content: \"\\ecdd\"; }\n.ri-file-excel-fill:before { content: \"\\ecde\"; }\n.ri-file-excel-line:before { content: \"\\ecdf\"; }\n.ri-file-fill:before { content: \"\\ece0\"; }\n.ri-file-forbid-fill:before { content: \"\\ece1\"; }\n.ri-file-forbid-line:before { content: \"\\ece2\"; }\n.ri-file-gif-fill:before { content: \"\\ece3\"; }\n.ri-file-gif-line:before { content: \"\\ece4\"; }\n.ri-file-history-fill:before { content: \"\\ece5\"; }\n.ri-file-history-line:before { content: \"\\ece6\"; }\n.ri-file-hwp-fill:before { content: \"\\ece7\"; }\n.ri-file-hwp-line:before { content: \"\\ece8\"; }\n.ri-file-info-fill:before { content: \"\\ece9\"; }\n.ri-file-info-line:before { content: \"\\ecea\"; }\n.ri-file-line:before { content: \"\\eceb\"; }\n.ri-file-list-2-fill:before { content: \"\\ecec\"; }\n.ri-file-list-2-line:before { content: \"\\eced\"; }\n.ri-file-list-3-fill:before { content: \"\\ecee\"; }\n.ri-file-list-3-line:before { content: \"\\ecef\"; }\n.ri-file-list-fill:before { content: \"\\ecf0\"; }\n.ri-file-list-line:before { content: \"\\ecf1\"; }\n.ri-file-lock-fill:before { content: \"\\ecf2\"; }\n.ri-file-lock-line:before { content: \"\\ecf3\"; }\n.ri-file-mark-fill:before { content: \"\\ecf4\"; }\n.ri-file-mark-line:before { content: \"\\ecf5\"; }\n.ri-file-music-fill:before { content: \"\\ecf6\"; }\n.ri-file-music-line:before { content: \"\\ecf7\"; }\n.ri-file-paper-2-fill:before { content: \"\\ecf8\"; }\n.ri-file-paper-2-line:before { content: \"\\ecf9\"; }\n.ri-file-paper-fill:before { content: \"\\ecfa\"; }\n.ri-file-paper-line:before { content: \"\\ecfb\"; }\n.ri-file-pdf-fill:before { content: \"\\ecfc\"; }\n.ri-file-pdf-line:before { content: \"\\ecfd\"; }\n.ri-file-ppt-2-fill:before { content: \"\\ecfe\"; }\n.ri-file-ppt-2-line:before { content: \"\\ecff\"; }\n.ri-file-ppt-fill:before { content: \"\\ed00\"; }\n.ri-file-ppt-line:before { content: \"\\ed01\"; }\n.ri-file-reduce-fill:before { content: \"\\ed02\"; }\n.ri-file-reduce-line:before { content: \"\\ed03\"; }\n.ri-file-search-fill:before { content: \"\\ed04\"; }\n.ri-file-search-line:before { content: \"\\ed05\"; }\n.ri-file-settings-fill:before { content: \"\\ed06\"; }\n.ri-file-settings-line:before { content: \"\\ed07\"; }\n.ri-file-shield-2-fill:before { content: \"\\ed08\"; }\n.ri-file-shield-2-line:before { content: \"\\ed09\"; }\n.ri-file-shield-fill:before { content: \"\\ed0a\"; }\n.ri-file-shield-line:before { content: \"\\ed0b\"; }\n.ri-file-shred-fill:before { content: \"\\ed0c\"; }\n.ri-file-shred-line:before { content: \"\\ed0d\"; }\n.ri-file-text-fill:before { content: \"\\ed0e\"; }\n.ri-file-text-line:before { content: \"\\ed0f\"; }\n.ri-file-transfer-fill:before { content: \"\\ed10\"; }\n.ri-file-transfer-line:before { content: \"\\ed11\"; }\n.ri-file-unknow-fill:before { content: \"\\ed12\"; }\n.ri-file-unknow-line:before { content: \"\\ed13\"; }\n.ri-file-upload-fill:before { content: \"\\ed14\"; }\n.ri-file-upload-line:before { content: \"\\ed15\"; }\n.ri-file-user-fill:before { content: \"\\ed16\"; }\n.ri-file-user-line:before { content: \"\\ed17\"; }\n.ri-file-warning-fill:before { content: \"\\ed18\"; }\n.ri-file-warning-line:before { content: \"\\ed19\"; }\n.ri-file-word-2-fill:before { content: \"\\ed1a\"; }\n.ri-file-word-2-line:before { content: \"\\ed1b\"; }\n.ri-file-word-fill:before { content: \"\\ed1c\"; }\n.ri-file-word-line:before { content: \"\\ed1d\"; }\n.ri-file-zip-fill:before { content: \"\\ed1e\"; }\n.ri-file-zip-line:before { content: \"\\ed1f\"; }\n.ri-film-fill:before { content: \"\\ed20\"; }\n.ri-film-line:before { content: \"\\ed21\"; }\n.ri-filter-2-fill:before { content: \"\\ed22\"; }\n.ri-filter-2-line:before { content: \"\\ed23\"; }\n.ri-filter-3-fill:before { content: \"\\ed24\"; }\n.ri-filter-3-line:before { content: \"\\ed25\"; }\n.ri-filter-fill:before { content: \"\\ed26\"; }\n.ri-filter-line:before { content: \"\\ed27\"; }\n.ri-filter-off-fill:before { content: \"\\ed28\"; }\n.ri-filter-off-line:before { content: \"\\ed29\"; }\n.ri-find-replace-fill:before { content: \"\\ed2a\"; }\n.ri-find-replace-line:before { content: \"\\ed2b\"; }\n.ri-finder-fill:before { content: \"\\ed2c\"; }\n.ri-finder-line:before { content: \"\\ed2d\"; }\n.ri-fingerprint-2-fill:before { content: \"\\ed2e\"; }\n.ri-fingerprint-2-line:before { content: \"\\ed2f\"; }\n.ri-fingerprint-fill:before { content: \"\\ed30\"; }\n.ri-fingerprint-line:before { content: \"\\ed31\"; }\n.ri-fire-fill:before { content: \"\\ed32\"; }\n.ri-fire-line:before { content: \"\\ed33\"; }\n.ri-firefox-fill:before { content: \"\\ed34\"; }\n.ri-firefox-line:before { content: \"\\ed35\"; }\n.ri-first-aid-kit-fill:before { content: \"\\ed36\"; }\n.ri-first-aid-kit-line:before { content: \"\\ed37\"; }\n.ri-flag-2-fill:before { content: \"\\ed38\"; }\n.ri-flag-2-line:before { content: \"\\ed39\"; }\n.ri-flag-fill:before { content: \"\\ed3a\"; }\n.ri-flag-line:before { content: \"\\ed3b\"; }\n.ri-flashlight-fill:before { content: \"\\ed3c\"; }\n.ri-flashlight-line:before { content: \"\\ed3d\"; }\n.ri-flask-fill:before { content: \"\\ed3e\"; }\n.ri-flask-line:before { content: \"\\ed3f\"; }\n.ri-flight-land-fill:before { content: \"\\ed40\"; }\n.ri-flight-land-line:before { content: \"\\ed41\"; }\n.ri-flight-takeoff-fill:before { content: \"\\ed42\"; }\n.ri-flight-takeoff-line:before { content: \"\\ed43\"; }\n.ri-flood-fill:before { content: \"\\ed44\"; }\n.ri-flood-line:before { content: \"\\ed45\"; }\n.ri-flow-chart:before { content: \"\\ed46\"; }\n.ri-flutter-fill:before { content: \"\\ed47\"; }\n.ri-flutter-line:before { content: \"\\ed48\"; }\n.ri-focus-2-fill:before { content: \"\\ed49\"; }\n.ri-focus-2-line:before { content: \"\\ed4a\"; }\n.ri-focus-3-fill:before { content: \"\\ed4b\"; }\n.ri-focus-3-line:before { content: \"\\ed4c\"; }\n.ri-focus-fill:before { content: \"\\ed4d\"; }\n.ri-focus-line:before { content: \"\\ed4e\"; }\n.ri-foggy-fill:before { content: \"\\ed4f\"; }\n.ri-foggy-line:before { content: \"\\ed50\"; }\n.ri-folder-2-fill:before { content: \"\\ed51\"; }\n.ri-folder-2-line:before { content: \"\\ed52\"; }\n.ri-folder-3-fill:before { content: \"\\ed53\"; }\n.ri-folder-3-line:before { content: \"\\ed54\"; }\n.ri-folder-4-fill:before { content: \"\\ed55\"; }\n.ri-folder-4-line:before { content: \"\\ed56\"; }\n.ri-folder-5-fill:before { content: \"\\ed57\"; }\n.ri-folder-5-line:before { content: \"\\ed58\"; }\n.ri-folder-add-fill:before { content: \"\\ed59\"; }\n.ri-folder-add-line:before { content: \"\\ed5a\"; }\n.ri-folder-chart-2-fill:before { content: \"\\ed5b\"; }\n.ri-folder-chart-2-line:before { content: \"\\ed5c\"; }\n.ri-folder-chart-fill:before { content: \"\\ed5d\"; }\n.ri-folder-chart-line:before { content: \"\\ed5e\"; }\n.ri-folder-download-fill:before { content: \"\\ed5f\"; }\n.ri-folder-download-line:before { content: \"\\ed60\"; }\n.ri-folder-fill:before { content: \"\\ed61\"; }\n.ri-folder-forbid-fill:before { content: \"\\ed62\"; }\n.ri-folder-forbid-line:before { content: \"\\ed63\"; }\n.ri-folder-history-fill:before { content: \"\\ed64\"; }\n.ri-folder-history-line:before { content: \"\\ed65\"; }\n.ri-folder-info-fill:before { content: \"\\ed66\"; }\n.ri-folder-info-line:before { content: \"\\ed67\"; }\n.ri-folder-keyhole-fill:before { content: \"\\ed68\"; }\n.ri-folder-keyhole-line:before { content: \"\\ed69\"; }\n.ri-folder-line:before { content: \"\\ed6a\"; }\n.ri-folder-lock-fill:before { content: \"\\ed6b\"; }\n.ri-folder-lock-line:before { content: \"\\ed6c\"; }\n.ri-folder-music-fill:before { content: \"\\ed6d\"; }\n.ri-folder-music-line:before { content: \"\\ed6e\"; }\n.ri-folder-open-fill:before { content: \"\\ed6f\"; }\n.ri-folder-open-line:before { content: \"\\ed70\"; }\n.ri-folder-received-fill:before { content: \"\\ed71\"; }\n.ri-folder-received-line:before { content: \"\\ed72\"; }\n.ri-folder-reduce-fill:before { content: \"\\ed73\"; }\n.ri-folder-reduce-line:before { content: \"\\ed74\"; }\n.ri-folder-settings-fill:before { content: \"\\ed75\"; }\n.ri-folder-settings-line:before { content: \"\\ed76\"; }\n.ri-folder-shared-fill:before { content: \"\\ed77\"; }\n.ri-folder-shared-line:before { content: \"\\ed78\"; }\n.ri-folder-shield-2-fill:before { content: \"\\ed79\"; }\n.ri-folder-shield-2-line:before { content: \"\\ed7a\"; }\n.ri-folder-shield-fill:before { content: \"\\ed7b\"; }\n.ri-folder-shield-line:before { content: \"\\ed7c\"; }\n.ri-folder-transfer-fill:before { content: \"\\ed7d\"; }\n.ri-folder-transfer-line:before { content: \"\\ed7e\"; }\n.ri-folder-unknow-fill:before { content: \"\\ed7f\"; }\n.ri-folder-unknow-line:before { content: \"\\ed80\"; }\n.ri-folder-upload-fill:before { content: \"\\ed81\"; }\n.ri-folder-upload-line:before { content: \"\\ed82\"; }\n.ri-folder-user-fill:before { content: \"\\ed83\"; }\n.ri-folder-user-line:before { content: \"\\ed84\"; }\n.ri-folder-warning-fill:before { content: \"\\ed85\"; }\n.ri-folder-warning-line:before { content: \"\\ed86\"; }\n.ri-folder-zip-fill:before { content: \"\\ed87\"; }\n.ri-folder-zip-line:before { content: \"\\ed88\"; }\n.ri-folders-fill:before { content: \"\\ed89\"; }\n.ri-folders-line:before { content: \"\\ed8a\"; }\n.ri-font-color:before { content: \"\\ed8b\"; }\n.ri-font-size-2:before { content: \"\\ed8c\"; }\n.ri-font-size:before { content: \"\\ed8d\"; }\n.ri-football-fill:before { content: \"\\ed8e\"; }\n.ri-football-line:before { content: \"\\ed8f\"; }\n.ri-footprint-fill:before { content: \"\\ed90\"; }\n.ri-footprint-line:before { content: \"\\ed91\"; }\n.ri-forbid-2-fill:before { content: \"\\ed92\"; }\n.ri-forbid-2-line:before { content: \"\\ed93\"; }\n.ri-forbid-fill:before { content: \"\\ed94\"; }\n.ri-forbid-line:before { content: \"\\ed95\"; }\n.ri-format-clear:before { content: \"\\ed96\"; }\n.ri-fridge-fill:before { content: \"\\ed97\"; }\n.ri-fridge-line:before { content: \"\\ed98\"; }\n.ri-fullscreen-exit-fill:before { content: \"\\ed99\"; }\n.ri-fullscreen-exit-line:before { content: \"\\ed9a\"; }\n.ri-fullscreen-fill:before { content: \"\\ed9b\"; }\n.ri-fullscreen-line:before { content: \"\\ed9c\"; }\n.ri-function-fill:before { content: \"\\ed9d\"; }\n.ri-function-line:before { content: \"\\ed9e\"; }\n.ri-functions:before { content: \"\\ed9f\"; }\n.ri-funds-box-fill:before { content: \"\\eda0\"; }\n.ri-funds-box-line:before { content: \"\\eda1\"; }\n.ri-funds-fill:before { content: \"\\eda2\"; }\n.ri-funds-line:before { content: \"\\eda3\"; }\n.ri-gallery-fill:before { content: \"\\eda4\"; }\n.ri-gallery-line:before { content: \"\\eda5\"; }\n.ri-gallery-upload-fill:before { content: \"\\eda6\"; }\n.ri-gallery-upload-line:before { content: \"\\eda7\"; }\n.ri-game-fill:before { content: \"\\eda8\"; }\n.ri-game-line:before { content: \"\\eda9\"; }\n.ri-gamepad-fill:before { content: \"\\edaa\"; }\n.ri-gamepad-line:before { content: \"\\edab\"; }\n.ri-gas-station-fill:before { content: \"\\edac\"; }\n.ri-gas-station-line:before { content: \"\\edad\"; }\n.ri-gatsby-fill:before { content: \"\\edae\"; }\n.ri-gatsby-line:before { content: \"\\edaf\"; }\n.ri-genderless-fill:before { content: \"\\edb0\"; }\n.ri-genderless-line:before { content: \"\\edb1\"; }\n.ri-ghost-2-fill:before { content: \"\\edb2\"; }\n.ri-ghost-2-line:before { content: \"\\edb3\"; }\n.ri-ghost-fill:before { content: \"\\edb4\"; }\n.ri-ghost-line:before { content: \"\\edb5\"; }\n.ri-ghost-smile-fill:before { content: \"\\edb6\"; }\n.ri-ghost-smile-line:before { content: \"\\edb7\"; }\n.ri-gift-2-fill:before { content: \"\\edb8\"; }\n.ri-gift-2-line:before { content: \"\\edb9\"; }\n.ri-gift-fill:before { content: \"\\edba\"; }\n.ri-gift-line:before { content: \"\\edbb\"; }\n.ri-git-branch-fill:before { content: \"\\edbc\"; }\n.ri-git-branch-line:before { content: \"\\edbd\"; }\n.ri-git-commit-fill:before { content: \"\\edbe\"; }\n.ri-git-commit-line:before { content: \"\\edbf\"; }\n.ri-git-merge-fill:before { content: \"\\edc0\"; }\n.ri-git-merge-line:before { content: \"\\edc1\"; }\n.ri-git-pull-request-fill:before { content: \"\\edc2\"; }\n.ri-git-pull-request-line:before { content: \"\\edc3\"; }\n.ri-git-repository-commits-fill:before { content: \"\\edc4\"; }\n.ri-git-repository-commits-line:before { content: \"\\edc5\"; }\n.ri-git-repository-fill:before { content: \"\\edc6\"; }\n.ri-git-repository-line:before { content: \"\\edc7\"; }\n.ri-git-repository-private-fill:before { content: \"\\edc8\"; }\n.ri-git-repository-private-line:before { content: \"\\edc9\"; }\n.ri-github-fill:before { content: \"\\edca\"; }\n.ri-github-line:before { content: \"\\edcb\"; }\n.ri-gitlab-fill:before { content: \"\\edcc\"; }\n.ri-gitlab-line:before { content: \"\\edcd\"; }\n.ri-global-fill:before { content: \"\\edce\"; }\n.ri-global-line:before { content: \"\\edcf\"; }\n.ri-globe-fill:before { content: \"\\edd0\"; }\n.ri-globe-line:before { content: \"\\edd1\"; }\n.ri-goblet-fill:before { content: \"\\edd2\"; }\n.ri-goblet-line:before { content: \"\\edd3\"; }\n.ri-google-fill:before { content: \"\\edd4\"; }\n.ri-google-line:before { content: \"\\edd5\"; }\n.ri-google-play-fill:before { content: \"\\edd6\"; }\n.ri-google-play-line:before { content: \"\\edd7\"; }\n.ri-government-fill:before { content: \"\\edd8\"; }\n.ri-government-line:before { content: \"\\edd9\"; }\n.ri-gps-fill:before { content: \"\\edda\"; }\n.ri-gps-line:before { content: \"\\eddb\"; }\n.ri-gradienter-fill:before { content: \"\\eddc\"; }\n.ri-gradienter-line:before { content: \"\\eddd\"; }\n.ri-grid-fill:before { content: \"\\edde\"; }\n.ri-grid-line:before { content: \"\\eddf\"; }\n.ri-group-2-fill:before { content: \"\\ede0\"; }\n.ri-group-2-line:before { content: \"\\ede1\"; }\n.ri-group-fill:before { content: \"\\ede2\"; }\n.ri-group-line:before { content: \"\\ede3\"; }\n.ri-guide-fill:before { content: \"\\ede4\"; }\n.ri-guide-line:before { content: \"\\ede5\"; }\n.ri-h-1:before { content: \"\\ede6\"; }\n.ri-h-2:before { content: \"\\ede7\"; }\n.ri-h-3:before { content: \"\\ede8\"; }\n.ri-h-4:before { content: \"\\ede9\"; }\n.ri-h-5:before { content: \"\\edea\"; }\n.ri-h-6:before { content: \"\\edeb\"; }\n.ri-hail-fill:before { content: \"\\edec\"; }\n.ri-hail-line:before { content: \"\\eded\"; }\n.ri-hammer-fill:before { content: \"\\edee\"; }\n.ri-hammer-line:before { content: \"\\edef\"; }\n.ri-hand-coin-fill:before { content: \"\\edf0\"; }\n.ri-hand-coin-line:before { content: \"\\edf1\"; }\n.ri-hand-heart-fill:before { content: \"\\edf2\"; }\n.ri-hand-heart-line:before { content: \"\\edf3\"; }\n.ri-hand-sanitizer-fill:before { content: \"\\edf4\"; }\n.ri-hand-sanitizer-line:before { content: \"\\edf5\"; }\n.ri-handbag-fill:before { content: \"\\edf6\"; }\n.ri-handbag-line:before { content: \"\\edf7\"; }\n.ri-hard-drive-2-fill:before { content: \"\\edf8\"; }\n.ri-hard-drive-2-line:before { content: \"\\edf9\"; }\n.ri-hard-drive-fill:before { content: \"\\edfa\"; }\n.ri-hard-drive-line:before { content: \"\\edfb\"; }\n.ri-hashtag:before { content: \"\\edfc\"; }\n.ri-haze-2-fill:before { content: \"\\edfd\"; }\n.ri-haze-2-line:before { content: \"\\edfe\"; }\n.ri-haze-fill:before { content: \"\\edff\"; }\n.ri-haze-line:before { content: \"\\ee00\"; }\n.ri-hd-fill:before { content: \"\\ee01\"; }\n.ri-hd-line:before { content: \"\\ee02\"; }\n.ri-heading:before { content: \"\\ee03\"; }\n.ri-headphone-fill:before { content: \"\\ee04\"; }\n.ri-headphone-line:before { content: \"\\ee05\"; }\n.ri-health-book-fill:before { content: \"\\ee06\"; }\n.ri-health-book-line:before { content: \"\\ee07\"; }\n.ri-heart-2-fill:before { content: \"\\ee08\"; }\n.ri-heart-2-line:before { content: \"\\ee09\"; }\n.ri-heart-3-fill:before { content: \"\\ee0a\"; }\n.ri-heart-3-line:before { content: \"\\ee0b\"; }\n.ri-heart-add-fill:before { content: \"\\ee0c\"; }\n.ri-heart-add-line:before { content: \"\\ee0d\"; }\n.ri-heart-fill:before { content: \"\\ee0e\"; }\n.ri-heart-line:before { content: \"\\ee0f\"; }\n.ri-heart-pulse-fill:before { content: \"\\ee10\"; }\n.ri-heart-pulse-line:before { content: \"\\ee11\"; }\n.ri-hearts-fill:before { content: \"\\ee12\"; }\n.ri-hearts-line:before { content: \"\\ee13\"; }\n.ri-heavy-showers-fill:before { content: \"\\ee14\"; }\n.ri-heavy-showers-line:before { content: \"\\ee15\"; }\n.ri-history-fill:before { content: \"\\ee16\"; }\n.ri-history-line:before { content: \"\\ee17\"; }\n.ri-home-2-fill:before { content: \"\\ee18\"; }\n.ri-home-2-line:before { content: \"\\ee19\"; }\n.ri-home-3-fill:before { content: \"\\ee1a\"; }\n.ri-home-3-line:before { content: \"\\ee1b\"; }\n.ri-home-4-fill:before { content: \"\\ee1c\"; }\n.ri-home-4-line:before { content: \"\\ee1d\"; }\n.ri-home-5-fill:before { content: \"\\ee1e\"; }\n.ri-home-5-line:before { content: \"\\ee1f\"; }\n.ri-home-6-fill:before { content: \"\\ee20\"; }\n.ri-home-6-line:before { content: \"\\ee21\"; }\n.ri-home-7-fill:before { content: \"\\ee22\"; }\n.ri-home-7-line:before { content: \"\\ee23\"; }\n.ri-home-8-fill:before { content: \"\\ee24\"; }\n.ri-home-8-line:before { content: \"\\ee25\"; }\n.ri-home-fill:before { content: \"\\ee26\"; }\n.ri-home-gear-fill:before { content: \"\\ee27\"; }\n.ri-home-gear-line:before { content: \"\\ee28\"; }\n.ri-home-heart-fill:before { content: \"\\ee29\"; }\n.ri-home-heart-line:before { content: \"\\ee2a\"; }\n.ri-home-line:before { content: \"\\ee2b\"; }\n.ri-home-smile-2-fill:before { content: \"\\ee2c\"; }\n.ri-home-smile-2-line:before { content: \"\\ee2d\"; }\n.ri-home-smile-fill:before { content: \"\\ee2e\"; }\n.ri-home-smile-line:before { content: \"\\ee2f\"; }\n.ri-home-wifi-fill:before { content: \"\\ee30\"; }\n.ri-home-wifi-line:before { content: \"\\ee31\"; }\n.ri-honor-of-kings-fill:before { content: \"\\ee32\"; }\n.ri-honor-of-kings-line:before { content: \"\\ee33\"; }\n.ri-honour-fill:before { content: \"\\ee34\"; }\n.ri-honour-line:before { content: \"\\ee35\"; }\n.ri-hospital-fill:before { content: \"\\ee36\"; }\n.ri-hospital-line:before { content: \"\\ee37\"; }\n.ri-hotel-bed-fill:before { content: \"\\ee38\"; }\n.ri-hotel-bed-line:before { content: \"\\ee39\"; }\n.ri-hotel-fill:before { content: \"\\ee3a\"; }\n.ri-hotel-line:before { content: \"\\ee3b\"; }\n.ri-hotspot-fill:before { content: \"\\ee3c\"; }\n.ri-hotspot-line:before { content: \"\\ee3d\"; }\n.ri-hq-fill:before { content: \"\\ee3e\"; }\n.ri-hq-line:before { content: \"\\ee3f\"; }\n.ri-html5-fill:before { content: \"\\ee40\"; }\n.ri-html5-line:before { content: \"\\ee41\"; }\n.ri-ie-fill:before { content: \"\\ee42\"; }\n.ri-ie-line:before { content: \"\\ee43\"; }\n.ri-image-2-fill:before { content: \"\\ee44\"; }\n.ri-image-2-line:before { content: \"\\ee45\"; }\n.ri-image-add-fill:before { content: \"\\ee46\"; }\n.ri-image-add-line:before { content: \"\\ee47\"; }\n.ri-image-edit-fill:before { content: \"\\ee48\"; }\n.ri-image-edit-line:before { content: \"\\ee49\"; }\n.ri-image-fill:before { content: \"\\ee4a\"; }\n.ri-image-line:before { content: \"\\ee4b\"; }\n.ri-inbox-archive-fill:before { content: \"\\ee4c\"; }\n.ri-inbox-archive-line:before { content: \"\\ee4d\"; }\n.ri-inbox-fill:before { content: \"\\ee4e\"; }\n.ri-inbox-line:before { content: \"\\ee4f\"; }\n.ri-inbox-unarchive-fill:before { content: \"\\ee50\"; }\n.ri-inbox-unarchive-line:before { content: \"\\ee51\"; }\n.ri-increase-decrease-fill:before { content: \"\\ee52\"; }\n.ri-increase-decrease-line:before { content: \"\\ee53\"; }\n.ri-indent-decrease:before { content: \"\\ee54\"; }\n.ri-indent-increase:before { content: \"\\ee55\"; }\n.ri-indeterminate-circle-fill:before { content: \"\\ee56\"; }\n.ri-indeterminate-circle-line:before { content: \"\\ee57\"; }\n.ri-information-fill:before { content: \"\\ee58\"; }\n.ri-information-line:before { content: \"\\ee59\"; }\n.ri-infrared-thermometer-fill:before { content: \"\\ee5a\"; }\n.ri-infrared-thermometer-line:before { content: \"\\ee5b\"; }\n.ri-ink-bottle-fill:before { content: \"\\ee5c\"; }\n.ri-ink-bottle-line:before { content: \"\\ee5d\"; }\n.ri-input-cursor-move:before { content: \"\\ee5e\"; }\n.ri-input-method-fill:before { content: \"\\ee5f\"; }\n.ri-input-method-line:before { content: \"\\ee60\"; }\n.ri-insert-column-left:before { content: \"\\ee61\"; }\n.ri-insert-column-right:before { content: \"\\ee62\"; }\n.ri-insert-row-bottom:before { content: \"\\ee63\"; }\n.ri-insert-row-top:before { content: \"\\ee64\"; }\n.ri-instagram-fill:before { content: \"\\ee65\"; }\n.ri-instagram-line:before { content: \"\\ee66\"; }\n.ri-install-fill:before { content: \"\\ee67\"; }\n.ri-install-line:before { content: \"\\ee68\"; }\n.ri-invision-fill:before { content: \"\\ee69\"; }\n.ri-invision-line:before { content: \"\\ee6a\"; }\n.ri-italic:before { content: \"\\ee6b\"; }\n.ri-kakao-talk-fill:before { content: \"\\ee6c\"; }\n.ri-kakao-talk-line:before { content: \"\\ee6d\"; }\n.ri-key-2-fill:before { content: \"\\ee6e\"; }\n.ri-key-2-line:before { content: \"\\ee6f\"; }\n.ri-key-fill:before { content: \"\\ee70\"; }\n.ri-key-line:before { content: \"\\ee71\"; }\n.ri-keyboard-box-fill:before { content: \"\\ee72\"; }\n.ri-keyboard-box-line:before { content: \"\\ee73\"; }\n.ri-keyboard-fill:before { content: \"\\ee74\"; }\n.ri-keyboard-line:before { content: \"\\ee75\"; }\n.ri-keynote-fill:before { content: \"\\ee76\"; }\n.ri-keynote-line:before { content: \"\\ee77\"; }\n.ri-knife-blood-fill:before { content: \"\\ee78\"; }\n.ri-knife-blood-line:before { content: \"\\ee79\"; }\n.ri-knife-fill:before { content: \"\\ee7a\"; }\n.ri-knife-line:before { content: \"\\ee7b\"; }\n.ri-landscape-fill:before { content: \"\\ee7c\"; }\n.ri-landscape-line:before { content: \"\\ee7d\"; }\n.ri-layout-2-fill:before { content: \"\\ee7e\"; }\n.ri-layout-2-line:before { content: \"\\ee7f\"; }\n.ri-layout-3-fill:before { content: \"\\ee80\"; }\n.ri-layout-3-line:before { content: \"\\ee81\"; }\n.ri-layout-4-fill:before { content: \"\\ee82\"; }\n.ri-layout-4-line:before { content: \"\\ee83\"; }\n.ri-layout-5-fill:before { content: \"\\ee84\"; }\n.ri-layout-5-line:before { content: \"\\ee85\"; }\n.ri-layout-6-fill:before { content: \"\\ee86\"; }\n.ri-layout-6-line:before { content: \"\\ee87\"; }\n.ri-layout-bottom-2-fill:before { content: \"\\ee88\"; }\n.ri-layout-bottom-2-line:before { content: \"\\ee89\"; }\n.ri-layout-bottom-fill:before { content: \"\\ee8a\"; }\n.ri-layout-bottom-line:before { content: \"\\ee8b\"; }\n.ri-layout-column-fill:before { content: \"\\ee8c\"; }\n.ri-layout-column-line:before { content: \"\\ee8d\"; }\n.ri-layout-fill:before { content: \"\\ee8e\"; }\n.ri-layout-grid-fill:before { content: \"\\ee8f\"; }\n.ri-layout-grid-line:before { content: \"\\ee90\"; }\n.ri-layout-left-2-fill:before { content: \"\\ee91\"; }\n.ri-layout-left-2-line:before { content: \"\\ee92\"; }\n.ri-layout-left-fill:before { content: \"\\ee93\"; }\n.ri-layout-left-line:before { content: \"\\ee94\"; }\n.ri-layout-line:before { content: \"\\ee95\"; }\n.ri-layout-masonry-fill:before { content: \"\\ee96\"; }\n.ri-layout-masonry-line:before { content: \"\\ee97\"; }\n.ri-layout-right-2-fill:before { content: \"\\ee98\"; }\n.ri-layout-right-2-line:before { content: \"\\ee99\"; }\n.ri-layout-right-fill:before { content: \"\\ee9a\"; }\n.ri-layout-right-line:before { content: \"\\ee9b\"; }\n.ri-layout-row-fill:before { content: \"\\ee9c\"; }\n.ri-layout-row-line:before { content: \"\\ee9d\"; }\n.ri-layout-top-2-fill:before { content: \"\\ee9e\"; }\n.ri-layout-top-2-line:before { content: \"\\ee9f\"; }\n.ri-layout-top-fill:before { content: \"\\eea0\"; }\n.ri-layout-top-line:before { content: \"\\eea1\"; }\n.ri-leaf-fill:before { content: \"\\eea2\"; }\n.ri-leaf-line:before { content: \"\\eea3\"; }\n.ri-lifebuoy-fill:before { content: \"\\eea4\"; }\n.ri-lifebuoy-line:before { content: \"\\eea5\"; }\n.ri-lightbulb-fill:before { content: \"\\eea6\"; }\n.ri-lightbulb-flash-fill:before { content: \"\\eea7\"; }\n.ri-lightbulb-flash-line:before { content: \"\\eea8\"; }\n.ri-lightbulb-line:before { content: \"\\eea9\"; }\n.ri-line-chart-fill:before { content: \"\\eeaa\"; }\n.ri-line-chart-line:before { content: \"\\eeab\"; }\n.ri-line-fill:before { content: \"\\eeac\"; }\n.ri-line-height:before { content: \"\\eead\"; }\n.ri-line-line:before { content: \"\\eeae\"; }\n.ri-link-m:before { content: \"\\eeaf\"; }\n.ri-link-unlink-m:before { content: \"\\eeb0\"; }\n.ri-link-unlink:before { content: \"\\eeb1\"; }\n.ri-link:before { content: \"\\eeb2\"; }\n.ri-linkedin-box-fill:before { content: \"\\eeb3\"; }\n.ri-linkedin-box-line:before { content: \"\\eeb4\"; }\n.ri-linkedin-fill:before { content: \"\\eeb5\"; }\n.ri-linkedin-line:before { content: \"\\eeb6\"; }\n.ri-links-fill:before { content: \"\\eeb7\"; }\n.ri-links-line:before { content: \"\\eeb8\"; }\n.ri-list-check-2:before { content: \"\\eeb9\"; }\n.ri-list-check:before { content: \"\\eeba\"; }\n.ri-list-ordered:before { content: \"\\eebb\"; }\n.ri-list-settings-fill:before { content: \"\\eebc\"; }\n.ri-list-settings-line:before { content: \"\\eebd\"; }\n.ri-list-unordered:before { content: \"\\eebe\"; }\n.ri-live-fill:before { content: \"\\eebf\"; }\n.ri-live-line:before { content: \"\\eec0\"; }\n.ri-loader-2-fill:before { content: \"\\eec1\"; }\n.ri-loader-2-line:before { content: \"\\eec2\"; }\n.ri-loader-3-fill:before { content: \"\\eec3\"; }\n.ri-loader-3-line:before { content: \"\\eec4\"; }\n.ri-loader-4-fill:before { content: \"\\eec5\"; }\n.ri-loader-4-line:before { content: \"\\eec6\"; }\n.ri-loader-5-fill:before { content: \"\\eec7\"; }\n.ri-loader-5-line:before { content: \"\\eec8\"; }\n.ri-loader-fill:before { content: \"\\eec9\"; }\n.ri-loader-line:before { content: \"\\eeca\"; }\n.ri-lock-2-fill:before { content: \"\\eecb\"; }\n.ri-lock-2-line:before { content: \"\\eecc\"; }\n.ri-lock-fill:before { content: \"\\eecd\"; }\n.ri-lock-line:before { content: \"\\eece\"; }\n.ri-lock-password-fill:before { content: \"\\eecf\"; }\n.ri-lock-password-line:before { content: \"\\eed0\"; }\n.ri-lock-unlock-fill:before { content: \"\\eed1\"; }\n.ri-lock-unlock-line:before { content: \"\\eed2\"; }\n.ri-login-box-fill:before { content: \"\\eed3\"; }\n.ri-login-box-line:before { content: \"\\eed4\"; }\n.ri-login-circle-fill:before { content: \"\\eed5\"; }\n.ri-login-circle-line:before { content: \"\\eed6\"; }\n.ri-logout-box-fill:before { content: \"\\eed7\"; }\n.ri-logout-box-line:before { content: \"\\eed8\"; }\n.ri-logout-box-r-fill:before { content: \"\\eed9\"; }\n.ri-logout-box-r-line:before { content: \"\\eeda\"; }\n.ri-logout-circle-fill:before { content: \"\\eedb\"; }\n.ri-logout-circle-line:before { content: \"\\eedc\"; }\n.ri-logout-circle-r-fill:before { content: \"\\eedd\"; }\n.ri-logout-circle-r-line:before { content: \"\\eede\"; }\n.ri-luggage-cart-fill:before { content: \"\\eedf\"; }\n.ri-luggage-cart-line:before { content: \"\\eee0\"; }\n.ri-luggage-deposit-fill:before { content: \"\\eee1\"; }\n.ri-luggage-deposit-line:before { content: \"\\eee2\"; }\n.ri-lungs-fill:before { content: \"\\eee3\"; }\n.ri-lungs-line:before { content: \"\\eee4\"; }\n.ri-mac-fill:before { content: \"\\eee5\"; }\n.ri-mac-line:before { content: \"\\eee6\"; }\n.ri-macbook-fill:before { content: \"\\eee7\"; }\n.ri-macbook-line:before { content: \"\\eee8\"; }\n.ri-magic-fill:before { content: \"\\eee9\"; }\n.ri-magic-line:before { content: \"\\eeea\"; }\n.ri-mail-add-fill:before { content: \"\\eeeb\"; }\n.ri-mail-add-line:before { content: \"\\eeec\"; }\n.ri-mail-check-fill:before { content: \"\\eeed\"; }\n.ri-mail-check-line:before { content: \"\\eeee\"; }\n.ri-mail-close-fill:before { content: \"\\eeef\"; }\n.ri-mail-close-line:before { content: \"\\eef0\"; }\n.ri-mail-download-fill:before { content: \"\\eef1\"; }\n.ri-mail-download-line:before { content: \"\\eef2\"; }\n.ri-mail-fill:before { content: \"\\eef3\"; }\n.ri-mail-forbid-fill:before { content: \"\\eef4\"; }\n.ri-mail-forbid-line:before { content: \"\\eef5\"; }\n.ri-mail-line:before { content: \"\\eef6\"; }\n.ri-mail-lock-fill:before { content: \"\\eef7\"; }\n.ri-mail-lock-line:before { content: \"\\eef8\"; }\n.ri-mail-open-fill:before { content: \"\\eef9\"; }\n.ri-mail-open-line:before { content: \"\\eefa\"; }\n.ri-mail-send-fill:before { content: \"\\eefb\"; }\n.ri-mail-send-line:before { content: \"\\eefc\"; }\n.ri-mail-settings-fill:before { content: \"\\eefd\"; }\n.ri-mail-settings-line:before { content: \"\\eefe\"; }\n.ri-mail-star-fill:before { content: \"\\eeff\"; }\n.ri-mail-star-line:before { content: \"\\ef00\"; }\n.ri-mail-unread-fill:before { content: \"\\ef01\"; }\n.ri-mail-unread-line:before { content: \"\\ef02\"; }\n.ri-mail-volume-fill:before { content: \"\\ef03\"; }\n.ri-mail-volume-line:before { content: \"\\ef04\"; }\n.ri-map-2-fill:before { content: \"\\ef05\"; }\n.ri-map-2-line:before { content: \"\\ef06\"; }\n.ri-map-fill:before { content: \"\\ef07\"; }\n.ri-map-line:before { content: \"\\ef08\"; }\n.ri-map-pin-2-fill:before { content: \"\\ef09\"; }\n.ri-map-pin-2-line:before { content: \"\\ef0a\"; }\n.ri-map-pin-3-fill:before { content: \"\\ef0b\"; }\n.ri-map-pin-3-line:before { content: \"\\ef0c\"; }\n.ri-map-pin-4-fill:before { content: \"\\ef0d\"; }\n.ri-map-pin-4-line:before { content: \"\\ef0e\"; }\n.ri-map-pin-5-fill:before { content: \"\\ef0f\"; }\n.ri-map-pin-5-line:before { content: \"\\ef10\"; }\n.ri-map-pin-add-fill:before { content: \"\\ef11\"; }\n.ri-map-pin-add-line:before { content: \"\\ef12\"; }\n.ri-map-pin-fill:before { content: \"\\ef13\"; }\n.ri-map-pin-line:before { content: \"\\ef14\"; }\n.ri-map-pin-range-fill:before { content: \"\\ef15\"; }\n.ri-map-pin-range-line:before { content: \"\\ef16\"; }\n.ri-map-pin-time-fill:before { content: \"\\ef17\"; }\n.ri-map-pin-time-line:before { content: \"\\ef18\"; }\n.ri-map-pin-user-fill:before { content: \"\\ef19\"; }\n.ri-map-pin-user-line:before { content: \"\\ef1a\"; }\n.ri-mark-pen-fill:before { content: \"\\ef1b\"; }\n.ri-mark-pen-line:before { content: \"\\ef1c\"; }\n.ri-markdown-fill:before { content: \"\\ef1d\"; }\n.ri-markdown-line:before { content: \"\\ef1e\"; }\n.ri-markup-fill:before { content: \"\\ef1f\"; }\n.ri-markup-line:before { content: \"\\ef20\"; }\n.ri-mastercard-fill:before { content: \"\\ef21\"; }\n.ri-mastercard-line:before { content: \"\\ef22\"; }\n.ri-mastodon-fill:before { content: \"\\ef23\"; }\n.ri-mastodon-line:before { content: \"\\ef24\"; }\n.ri-medal-2-fill:before { content: \"\\ef25\"; }\n.ri-medal-2-line:before { content: \"\\ef26\"; }\n.ri-medal-fill:before { content: \"\\ef27\"; }\n.ri-medal-line:before { content: \"\\ef28\"; }\n.ri-medicine-bottle-fill:before { content: \"\\ef29\"; }\n.ri-medicine-bottle-line:before { content: \"\\ef2a\"; }\n.ri-medium-fill:before { content: \"\\ef2b\"; }\n.ri-medium-line:before { content: \"\\ef2c\"; }\n.ri-men-fill:before { content: \"\\ef2d\"; }\n.ri-men-line:before { content: \"\\ef2e\"; }\n.ri-mental-health-fill:before { content: \"\\ef2f\"; }\n.ri-mental-health-line:before { content: \"\\ef30\"; }\n.ri-menu-2-fill:before { content: \"\\ef31\"; }\n.ri-menu-2-line:before { content: \"\\ef32\"; }\n.ri-menu-3-fill:before { content: \"\\ef33\"; }\n.ri-menu-3-line:before { content: \"\\ef34\"; }\n.ri-menu-4-fill:before { content: \"\\ef35\"; }\n.ri-menu-4-line:before { content: \"\\ef36\"; }\n.ri-menu-5-fill:before { content: \"\\ef37\"; }\n.ri-menu-5-line:before { content: \"\\ef38\"; }\n.ri-menu-add-fill:before { content: \"\\ef39\"; }\n.ri-menu-add-line:before { content: \"\\ef3a\"; }\n.ri-menu-fill:before { content: \"\\ef3b\"; }\n.ri-menu-fold-fill:before { content: \"\\ef3c\"; }\n.ri-menu-fold-line:before { content: \"\\ef3d\"; }\n.ri-menu-line:before { content: \"\\ef3e\"; }\n.ri-menu-unfold-fill:before { content: \"\\ef3f\"; }\n.ri-menu-unfold-line:before { content: \"\\ef40\"; }\n.ri-merge-cells-horizontal:before { content: \"\\ef41\"; }\n.ri-merge-cells-vertical:before { content: \"\\ef42\"; }\n.ri-message-2-fill:before { content: \"\\ef43\"; }\n.ri-message-2-line:before { content: \"\\ef44\"; }\n.ri-message-3-fill:before { content: \"\\ef45\"; }\n.ri-message-3-line:before { content: \"\\ef46\"; }\n.ri-message-fill:before { content: \"\\ef47\"; }\n.ri-message-line:before { content: \"\\ef48\"; }\n.ri-messenger-fill:before { content: \"\\ef49\"; }\n.ri-messenger-line:before { content: \"\\ef4a\"; }\n.ri-meteor-fill:before { content: \"\\ef4b\"; }\n.ri-meteor-line:before { content: \"\\ef4c\"; }\n.ri-mic-2-fill:before { content: \"\\ef4d\"; }\n.ri-mic-2-line:before { content: \"\\ef4e\"; }\n.ri-mic-fill:before { content: \"\\ef4f\"; }\n.ri-mic-line:before { content: \"\\ef50\"; }\n.ri-mic-off-fill:before { content: \"\\ef51\"; }\n.ri-mic-off-line:before { content: \"\\ef52\"; }\n.ri-mickey-fill:before { content: \"\\ef53\"; }\n.ri-mickey-line:before { content: \"\\ef54\"; }\n.ri-microscope-fill:before { content: \"\\ef55\"; }\n.ri-microscope-line:before { content: \"\\ef56\"; }\n.ri-microsoft-fill:before { content: \"\\ef57\"; }\n.ri-microsoft-line:before { content: \"\\ef58\"; }\n.ri-mind-map:before { content: \"\\ef59\"; }\n.ri-mini-program-fill:before { content: \"\\ef5a\"; }\n.ri-mini-program-line:before { content: \"\\ef5b\"; }\n.ri-mist-fill:before { content: \"\\ef5c\"; }\n.ri-mist-line:before { content: \"\\ef5d\"; }\n.ri-money-cny-box-fill:before { content: \"\\ef5e\"; }\n.ri-money-cny-box-line:before { content: \"\\ef5f\"; }\n.ri-money-cny-circle-fill:before { content: \"\\ef60\"; }\n.ri-money-cny-circle-line:before { content: \"\\ef61\"; }\n.ri-money-dollar-box-fill:before { content: \"\\ef62\"; }\n.ri-money-dollar-box-line:before { content: \"\\ef63\"; }\n.ri-money-dollar-circle-fill:before { content: \"\\ef64\"; }\n.ri-money-dollar-circle-line:before { content: \"\\ef65\"; }\n.ri-money-euro-box-fill:before { content: \"\\ef66\"; }\n.ri-money-euro-box-line:before { content: \"\\ef67\"; }\n.ri-money-euro-circle-fill:before { content: \"\\ef68\"; }\n.ri-money-euro-circle-line:before { content: \"\\ef69\"; }\n.ri-money-pound-box-fill:before { content: \"\\ef6a\"; }\n.ri-money-pound-box-line:before { content: \"\\ef6b\"; }\n.ri-money-pound-circle-fill:before { content: \"\\ef6c\"; }\n.ri-money-pound-circle-line:before { content: \"\\ef6d\"; }\n.ri-moon-clear-fill:before { content: \"\\ef6e\"; }\n.ri-moon-clear-line:before { content: \"\\ef6f\"; }\n.ri-moon-cloudy-fill:before { content: \"\\ef70\"; }\n.ri-moon-cloudy-line:before { content: \"\\ef71\"; }\n.ri-moon-fill:before { content: \"\\ef72\"; }\n.ri-moon-foggy-fill:before { content: \"\\ef73\"; }\n.ri-moon-foggy-line:before { content: \"\\ef74\"; }\n.ri-moon-line:before { content: \"\\ef75\"; }\n.ri-more-2-fill:before { content: \"\\ef76\"; }\n.ri-more-2-line:before { content: \"\\ef77\"; }\n.ri-more-fill:before { content: \"\\ef78\"; }\n.ri-more-line:before { content: \"\\ef79\"; }\n.ri-motorbike-fill:before { content: \"\\ef7a\"; }\n.ri-motorbike-line:before { content: \"\\ef7b\"; }\n.ri-mouse-fill:before { content: \"\\ef7c\"; }\n.ri-mouse-line:before { content: \"\\ef7d\"; }\n.ri-movie-2-fill:before { content: \"\\ef7e\"; }\n.ri-movie-2-line:before { content: \"\\ef7f\"; }\n.ri-movie-fill:before { content: \"\\ef80\"; }\n.ri-movie-line:before { content: \"\\ef81\"; }\n.ri-music-2-fill:before { content: \"\\ef82\"; }\n.ri-music-2-line:before { content: \"\\ef83\"; }\n.ri-music-fill:before { content: \"\\ef84\"; }\n.ri-music-line:before { content: \"\\ef85\"; }\n.ri-mv-fill:before { content: \"\\ef86\"; }\n.ri-mv-line:before { content: \"\\ef87\"; }\n.ri-navigation-fill:before { content: \"\\ef88\"; }\n.ri-navigation-line:before { content: \"\\ef89\"; }\n.ri-netease-cloud-music-fill:before { content: \"\\ef8a\"; }\n.ri-netease-cloud-music-line:before { content: \"\\ef8b\"; }\n.ri-netflix-fill:before { content: \"\\ef8c\"; }\n.ri-netflix-line:before { content: \"\\ef8d\"; }\n.ri-newspaper-fill:before { content: \"\\ef8e\"; }\n.ri-newspaper-line:before { content: \"\\ef8f\"; }\n.ri-node-tree:before { content: \"\\ef90\"; }\n.ri-notification-2-fill:before { content: \"\\ef91\"; }\n.ri-notification-2-line:before { content: \"\\ef92\"; }\n.ri-notification-3-fill:before { content: \"\\ef93\"; }\n.ri-notification-3-line:before { content: \"\\ef94\"; }\n.ri-notification-4-fill:before { content: \"\\ef95\"; }\n.ri-notification-4-line:before { content: \"\\ef96\"; }\n.ri-notification-badge-fill:before { content: \"\\ef97\"; }\n.ri-notification-badge-line:before { content: \"\\ef98\"; }\n.ri-notification-fill:before { content: \"\\ef99\"; }\n.ri-notification-line:before { content: \"\\ef9a\"; }\n.ri-notification-off-fill:before { content: \"\\ef9b\"; }\n.ri-notification-off-line:before { content: \"\\ef9c\"; }\n.ri-npmjs-fill:before { content: \"\\ef9d\"; }\n.ri-npmjs-line:before { content: \"\\ef9e\"; }\n.ri-number-0:before { content: \"\\ef9f\"; }\n.ri-number-1:before { content: \"\\efa0\"; }\n.ri-number-2:before { content: \"\\efa1\"; }\n.ri-number-3:before { content: \"\\efa2\"; }\n.ri-number-4:before { content: \"\\efa3\"; }\n.ri-number-5:before { content: \"\\efa4\"; }\n.ri-number-6:before { content: \"\\efa5\"; }\n.ri-number-7:before { content: \"\\efa6\"; }\n.ri-number-8:before { content: \"\\efa7\"; }\n.ri-number-9:before { content: \"\\efa8\"; }\n.ri-numbers-fill:before { content: \"\\efa9\"; }\n.ri-numbers-line:before { content: \"\\efaa\"; }\n.ri-nurse-fill:before { content: \"\\efab\"; }\n.ri-nurse-line:before { content: \"\\efac\"; }\n.ri-oil-fill:before { content: \"\\efad\"; }\n.ri-oil-line:before { content: \"\\efae\"; }\n.ri-omega:before { content: \"\\efaf\"; }\n.ri-open-arm-fill:before { content: \"\\efb0\"; }\n.ri-open-arm-line:before { content: \"\\efb1\"; }\n.ri-open-source-fill:before { content: \"\\efb2\"; }\n.ri-open-source-line:before { content: \"\\efb3\"; }\n.ri-opera-fill:before { content: \"\\efb4\"; }\n.ri-opera-line:before { content: \"\\efb5\"; }\n.ri-order-play-fill:before { content: \"\\efb6\"; }\n.ri-order-play-line:before { content: \"\\efb7\"; }\n.ri-organization-chart:before { content: \"\\efb8\"; }\n.ri-outlet-2-fill:before { content: \"\\efb9\"; }\n.ri-outlet-2-line:before { content: \"\\efba\"; }\n.ri-outlet-fill:before { content: \"\\efbb\"; }\n.ri-outlet-line:before { content: \"\\efbc\"; }\n.ri-page-separator:before { content: \"\\efbd\"; }\n.ri-pages-fill:before { content: \"\\efbe\"; }\n.ri-pages-line:before { content: \"\\efbf\"; }\n.ri-paint-brush-fill:before { content: \"\\efc0\"; }\n.ri-paint-brush-line:before { content: \"\\efc1\"; }\n.ri-paint-fill:before { content: \"\\efc2\"; }\n.ri-paint-line:before { content: \"\\efc3\"; }\n.ri-palette-fill:before { content: \"\\efc4\"; }\n.ri-palette-line:before { content: \"\\efc5\"; }\n.ri-pantone-fill:before { content: \"\\efc6\"; }\n.ri-pantone-line:before { content: \"\\efc7\"; }\n.ri-paragraph:before { content: \"\\efc8\"; }\n.ri-parent-fill:before { content: \"\\efc9\"; }\n.ri-parent-line:before { content: \"\\efca\"; }\n.ri-parentheses-fill:before { content: \"\\efcb\"; }\n.ri-parentheses-line:before { content: \"\\efcc\"; }\n.ri-parking-box-fill:before { content: \"\\efcd\"; }\n.ri-parking-box-line:before { content: \"\\efce\"; }\n.ri-parking-fill:before { content: \"\\efcf\"; }\n.ri-parking-line:before { content: \"\\efd0\"; }\n.ri-passport-fill:before { content: \"\\efd1\"; }\n.ri-passport-line:before { content: \"\\efd2\"; }\n.ri-patreon-fill:before { content: \"\\efd3\"; }\n.ri-patreon-line:before { content: \"\\efd4\"; }\n.ri-pause-circle-fill:before { content: \"\\efd5\"; }\n.ri-pause-circle-line:before { content: \"\\efd6\"; }\n.ri-pause-fill:before { content: \"\\efd7\"; }\n.ri-pause-line:before { content: \"\\efd8\"; }\n.ri-pause-mini-fill:before { content: \"\\efd9\"; }\n.ri-pause-mini-line:before { content: \"\\efda\"; }\n.ri-paypal-fill:before { content: \"\\efdb\"; }\n.ri-paypal-line:before { content: \"\\efdc\"; }\n.ri-pen-nib-fill:before { content: \"\\efdd\"; }\n.ri-pen-nib-line:before { content: \"\\efde\"; }\n.ri-pencil-fill:before { content: \"\\efdf\"; }\n.ri-pencil-line:before { content: \"\\efe0\"; }\n.ri-pencil-ruler-2-fill:before { content: \"\\efe1\"; }\n.ri-pencil-ruler-2-line:before { content: \"\\efe2\"; }\n.ri-pencil-ruler-fill:before { content: \"\\efe3\"; }\n.ri-pencil-ruler-line:before { content: \"\\efe4\"; }\n.ri-percent-fill:before { content: \"\\efe5\"; }\n.ri-percent-line:before { content: \"\\efe6\"; }\n.ri-phone-camera-fill:before { content: \"\\efe7\"; }\n.ri-phone-camera-line:before { content: \"\\efe8\"; }\n.ri-phone-fill:before { content: \"\\efe9\"; }\n.ri-phone-find-fill:before { content: \"\\efea\"; }\n.ri-phone-find-line:before { content: \"\\efeb\"; }\n.ri-phone-line:before { content: \"\\efec\"; }\n.ri-phone-lock-fill:before { content: \"\\efed\"; }\n.ri-phone-lock-line:before { content: \"\\efee\"; }\n.ri-picture-in-picture-2-fill:before { content: \"\\efef\"; }\n.ri-picture-in-picture-2-line:before { content: \"\\eff0\"; }\n.ri-picture-in-picture-exit-fill:before { content: \"\\eff1\"; }\n.ri-picture-in-picture-exit-line:before { content: \"\\eff2\"; }\n.ri-picture-in-picture-fill:before { content: \"\\eff3\"; }\n.ri-picture-in-picture-line:before { content: \"\\eff4\"; }\n.ri-pie-chart-2-fill:before { content: \"\\eff5\"; }\n.ri-pie-chart-2-line:before { content: \"\\eff6\"; }\n.ri-pie-chart-box-fill:before { content: \"\\eff7\"; }\n.ri-pie-chart-box-line:before { content: \"\\eff8\"; }\n.ri-pie-chart-fill:before { content: \"\\eff9\"; }\n.ri-pie-chart-line:before { content: \"\\effa\"; }\n.ri-pin-distance-fill:before { content: \"\\effb\"; }\n.ri-pin-distance-line:before { content: \"\\effc\"; }\n.ri-ping-pong-fill:before { content: \"\\effd\"; }\n.ri-ping-pong-line:before { content: \"\\effe\"; }\n.ri-pinterest-fill:before { content: \"\\efff\"; }\n.ri-pinterest-line:before { content: \"\\f000\"; }\n.ri-pinyin-input:before { content: \"\\f001\"; }\n.ri-pixelfed-fill:before { content: \"\\f002\"; }\n.ri-pixelfed-line:before { content: \"\\f003\"; }\n.ri-plane-fill:before { content: \"\\f004\"; }\n.ri-plane-line:before { content: \"\\f005\"; }\n.ri-plant-fill:before { content: \"\\f006\"; }\n.ri-plant-line:before { content: \"\\f007\"; }\n.ri-play-circle-fill:before { content: \"\\f008\"; }\n.ri-play-circle-line:before { content: \"\\f009\"; }\n.ri-play-fill:before { content: \"\\f00a\"; }\n.ri-play-line:before { content: \"\\f00b\"; }\n.ri-play-list-2-fill:before { content: \"\\f00c\"; }\n.ri-play-list-2-line:before { content: \"\\f00d\"; }\n.ri-play-list-add-fill:before { content: \"\\f00e\"; }\n.ri-play-list-add-line:before { content: \"\\f00f\"; }\n.ri-play-list-fill:before { content: \"\\f010\"; }\n.ri-play-list-line:before { content: \"\\f011\"; }\n.ri-play-mini-fill:before { content: \"\\f012\"; }\n.ri-play-mini-line:before { content: \"\\f013\"; }\n.ri-playstation-fill:before { content: \"\\f014\"; }\n.ri-playstation-line:before { content: \"\\f015\"; }\n.ri-plug-2-fill:before { content: \"\\f016\"; }\n.ri-plug-2-line:before { content: \"\\f017\"; }\n.ri-plug-fill:before { content: \"\\f018\"; }\n.ri-plug-line:before { content: \"\\f019\"; }\n.ri-polaroid-2-fill:before { content: \"\\f01a\"; }\n.ri-polaroid-2-line:before { content: \"\\f01b\"; }\n.ri-polaroid-fill:before { content: \"\\f01c\"; }\n.ri-polaroid-line:before { content: \"\\f01d\"; }\n.ri-police-car-fill:before { content: \"\\f01e\"; }\n.ri-police-car-line:before { content: \"\\f01f\"; }\n.ri-price-tag-2-fill:before { content: \"\\f020\"; }\n.ri-price-tag-2-line:before { content: \"\\f021\"; }\n.ri-price-tag-3-fill:before { content: \"\\f022\"; }\n.ri-price-tag-3-line:before { content: \"\\f023\"; }\n.ri-price-tag-fill:before { content: \"\\f024\"; }\n.ri-price-tag-line:before { content: \"\\f025\"; }\n.ri-printer-cloud-fill:before { content: \"\\f026\"; }\n.ri-printer-cloud-line:before { content: \"\\f027\"; }\n.ri-printer-fill:before { content: \"\\f028\"; }\n.ri-printer-line:before { content: \"\\f029\"; }\n.ri-product-hunt-fill:before { content: \"\\f02a\"; }\n.ri-product-hunt-line:before { content: \"\\f02b\"; }\n.ri-profile-fill:before { content: \"\\f02c\"; }\n.ri-profile-line:before { content: \"\\f02d\"; }\n.ri-projector-2-fill:before { content: \"\\f02e\"; }\n.ri-projector-2-line:before { content: \"\\f02f\"; }\n.ri-projector-fill:before { content: \"\\f030\"; }\n.ri-projector-line:before { content: \"\\f031\"; }\n.ri-psychotherapy-fill:before { content: \"\\f032\"; }\n.ri-psychotherapy-line:before { content: \"\\f033\"; }\n.ri-pulse-fill:before { content: \"\\f034\"; }\n.ri-pulse-line:before { content: \"\\f035\"; }\n.ri-pushpin-2-fill:before { content: \"\\f036\"; }\n.ri-pushpin-2-line:before { content: \"\\f037\"; }\n.ri-pushpin-fill:before { content: \"\\f038\"; }\n.ri-pushpin-line:before { content: \"\\f039\"; }\n.ri-qq-fill:before { content: \"\\f03a\"; }\n.ri-qq-line:before { content: \"\\f03b\"; }\n.ri-qr-code-fill:before { content: \"\\f03c\"; }\n.ri-qr-code-line:before { content: \"\\f03d\"; }\n.ri-qr-scan-2-fill:before { content: \"\\f03e\"; }\n.ri-qr-scan-2-line:before { content: \"\\f03f\"; }\n.ri-qr-scan-fill:before { content: \"\\f040\"; }\n.ri-qr-scan-line:before { content: \"\\f041\"; }\n.ri-question-answer-fill:before { content: \"\\f042\"; }\n.ri-question-answer-line:before { content: \"\\f043\"; }\n.ri-question-fill:before { content: \"\\f044\"; }\n.ri-question-line:before { content: \"\\f045\"; }\n.ri-question-mark:before { content: \"\\f046\"; }\n.ri-questionnaire-fill:before { content: \"\\f047\"; }\n.ri-questionnaire-line:before { content: \"\\f048\"; }\n.ri-quill-pen-fill:before { content: \"\\f049\"; }\n.ri-quill-pen-line:before { content: \"\\f04a\"; }\n.ri-radar-fill:before { content: \"\\f04b\"; }\n.ri-radar-line:before { content: \"\\f04c\"; }\n.ri-radio-2-fill:before { content: \"\\f04d\"; }\n.ri-radio-2-line:before { content: \"\\f04e\"; }\n.ri-radio-button-fill:before { content: \"\\f04f\"; }\n.ri-radio-button-line:before { content: \"\\f050\"; }\n.ri-radio-fill:before { content: \"\\f051\"; }\n.ri-radio-line:before { content: \"\\f052\"; }\n.ri-rainbow-fill:before { content: \"\\f053\"; }\n.ri-rainbow-line:before { content: \"\\f054\"; }\n.ri-rainy-fill:before { content: \"\\f055\"; }\n.ri-rainy-line:before { content: \"\\f056\"; }\n.ri-reactjs-fill:before { content: \"\\f057\"; }\n.ri-reactjs-line:before { content: \"\\f058\"; }\n.ri-record-circle-fill:before { content: \"\\f059\"; }\n.ri-record-circle-line:before { content: \"\\f05a\"; }\n.ri-record-mail-fill:before { content: \"\\f05b\"; }\n.ri-record-mail-line:before { content: \"\\f05c\"; }\n.ri-recycle-fill:before { content: \"\\f05d\"; }\n.ri-recycle-line:before { content: \"\\f05e\"; }\n.ri-red-packet-fill:before { content: \"\\f05f\"; }\n.ri-red-packet-line:before { content: \"\\f060\"; }\n.ri-reddit-fill:before { content: \"\\f061\"; }\n.ri-reddit-line:before { content: \"\\f062\"; }\n.ri-refresh-fill:before { content: \"\\f063\"; }\n.ri-refresh-line:before { content: \"\\f064\"; }\n.ri-refund-2-fill:before { content: \"\\f065\"; }\n.ri-refund-2-line:before { content: \"\\f066\"; }\n.ri-refund-fill:before { content: \"\\f067\"; }\n.ri-refund-line:before { content: \"\\f068\"; }\n.ri-registered-fill:before { content: \"\\f069\"; }\n.ri-registered-line:before { content: \"\\f06a\"; }\n.ri-remixicon-fill:before { content: \"\\f06b\"; }\n.ri-remixicon-line:before { content: \"\\f06c\"; }\n.ri-remote-control-2-fill:before { content: \"\\f06d\"; }\n.ri-remote-control-2-line:before { content: \"\\f06e\"; }\n.ri-remote-control-fill:before { content: \"\\f06f\"; }\n.ri-remote-control-line:before { content: \"\\f070\"; }\n.ri-repeat-2-fill:before { content: \"\\f071\"; }\n.ri-repeat-2-line:before { content: \"\\f072\"; }\n.ri-repeat-fill:before { content: \"\\f073\"; }\n.ri-repeat-line:before { content: \"\\f074\"; }\n.ri-repeat-one-fill:before { content: \"\\f075\"; }\n.ri-repeat-one-line:before { content: \"\\f076\"; }\n.ri-reply-all-fill:before { content: \"\\f077\"; }\n.ri-reply-all-line:before { content: \"\\f078\"; }\n.ri-reply-fill:before { content: \"\\f079\"; }\n.ri-reply-line:before { content: \"\\f07a\"; }\n.ri-reserved-fill:before { content: \"\\f07b\"; }\n.ri-reserved-line:before { content: \"\\f07c\"; }\n.ri-rest-time-fill:before { content: \"\\f07d\"; }\n.ri-rest-time-line:before { content: \"\\f07e\"; }\n.ri-restart-fill:before { content: \"\\f07f\"; }\n.ri-restart-line:before { content: \"\\f080\"; }\n.ri-restaurant-2-fill:before { content: \"\\f081\"; }\n.ri-restaurant-2-line:before { content: \"\\f082\"; }\n.ri-restaurant-fill:before { content: \"\\f083\"; }\n.ri-restaurant-line:before { content: \"\\f084\"; }\n.ri-rewind-fill:before { content: \"\\f085\"; }\n.ri-rewind-line:before { content: \"\\f086\"; }\n.ri-rewind-mini-fill:before { content: \"\\f087\"; }\n.ri-rewind-mini-line:before { content: \"\\f088\"; }\n.ri-rhythm-fill:before { content: \"\\f089\"; }\n.ri-rhythm-line:before { content: \"\\f08a\"; }\n.ri-riding-fill:before { content: \"\\f08b\"; }\n.ri-riding-line:before { content: \"\\f08c\"; }\n.ri-road-map-fill:before { content: \"\\f08d\"; }\n.ri-road-map-line:before { content: \"\\f08e\"; }\n.ri-roadster-fill:before { content: \"\\f08f\"; }\n.ri-roadster-line:before { content: \"\\f090\"; }\n.ri-robot-fill:before { content: \"\\f091\"; }\n.ri-robot-line:before { content: \"\\f092\"; }\n.ri-rocket-2-fill:before { content: \"\\f093\"; }\n.ri-rocket-2-line:before { content: \"\\f094\"; }\n.ri-rocket-fill:before { content: \"\\f095\"; }\n.ri-rocket-line:before { content: \"\\f096\"; }\n.ri-rotate-lock-fill:before { content: \"\\f097\"; }\n.ri-rotate-lock-line:before { content: \"\\f098\"; }\n.ri-rounded-corner:before { content: \"\\f099\"; }\n.ri-route-fill:before { content: \"\\f09a\"; }\n.ri-route-line:before { content: \"\\f09b\"; }\n.ri-router-fill:before { content: \"\\f09c\"; }\n.ri-router-line:before { content: \"\\f09d\"; }\n.ri-rss-fill:before { content: \"\\f09e\"; }\n.ri-rss-line:before { content: \"\\f09f\"; }\n.ri-ruler-2-fill:before { content: \"\\f0a0\"; }\n.ri-ruler-2-line:before { content: \"\\f0a1\"; }\n.ri-ruler-fill:before { content: \"\\f0a2\"; }\n.ri-ruler-line:before { content: \"\\f0a3\"; }\n.ri-run-fill:before { content: \"\\f0a4\"; }\n.ri-run-line:before { content: \"\\f0a5\"; }\n.ri-safari-fill:before { content: \"\\f0a6\"; }\n.ri-safari-line:before { content: \"\\f0a7\"; }\n.ri-safe-2-fill:before { content: \"\\f0a8\"; }\n.ri-safe-2-line:before { content: \"\\f0a9\"; }\n.ri-safe-fill:before { content: \"\\f0aa\"; }\n.ri-safe-line:before { content: \"\\f0ab\"; }\n.ri-sailboat-fill:before { content: \"\\f0ac\"; }\n.ri-sailboat-line:before { content: \"\\f0ad\"; }\n.ri-save-2-fill:before { content: \"\\f0ae\"; }\n.ri-save-2-line:before { content: \"\\f0af\"; }\n.ri-save-3-fill:before { content: \"\\f0b0\"; }\n.ri-save-3-line:before { content: \"\\f0b1\"; }\n.ri-save-fill:before { content: \"\\f0b2\"; }\n.ri-save-line:before { content: \"\\f0b3\"; }\n.ri-scales-2-fill:before { content: \"\\f0b4\"; }\n.ri-scales-2-line:before { content: \"\\f0b5\"; }\n.ri-scales-3-fill:before { content: \"\\f0b6\"; }\n.ri-scales-3-line:before { content: \"\\f0b7\"; }\n.ri-scales-fill:before { content: \"\\f0b8\"; }\n.ri-scales-line:before { content: \"\\f0b9\"; }\n.ri-scan-2-fill:before { content: \"\\f0ba\"; }\n.ri-scan-2-line:before { content: \"\\f0bb\"; }\n.ri-scan-fill:before { content: \"\\f0bc\"; }\n.ri-scan-line:before { content: \"\\f0bd\"; }\n.ri-scissors-2-fill:before { content: \"\\f0be\"; }\n.ri-scissors-2-line:before { content: \"\\f0bf\"; }\n.ri-scissors-cut-fill:before { content: \"\\f0c0\"; }\n.ri-scissors-cut-line:before { content: \"\\f0c1\"; }\n.ri-scissors-fill:before { content: \"\\f0c2\"; }\n.ri-scissors-line:before { content: \"\\f0c3\"; }\n.ri-screenshot-2-fill:before { content: \"\\f0c4\"; }\n.ri-screenshot-2-line:before { content: \"\\f0c5\"; }\n.ri-screenshot-fill:before { content: \"\\f0c6\"; }\n.ri-screenshot-line:before { content: \"\\f0c7\"; }\n.ri-sd-card-fill:before { content: \"\\f0c8\"; }\n.ri-sd-card-line:before { content: \"\\f0c9\"; }\n.ri-sd-card-mini-fill:before { content: \"\\f0ca\"; }\n.ri-sd-card-mini-line:before { content: \"\\f0cb\"; }\n.ri-search-2-fill:before { content: \"\\f0cc\"; }\n.ri-search-2-line:before { content: \"\\f0cd\"; }\n.ri-search-eye-fill:before { content: \"\\f0ce\"; }\n.ri-search-eye-line:before { content: \"\\f0cf\"; }\n.ri-search-fill:before { content: \"\\f0d0\"; }\n.ri-search-line:before { content: \"\\f0d1\"; }\n.ri-secure-payment-fill:before { content: \"\\f0d2\"; }\n.ri-secure-payment-line:before { content: \"\\f0d3\"; }\n.ri-seedling-fill:before { content: \"\\f0d4\"; }\n.ri-seedling-line:before { content: \"\\f0d5\"; }\n.ri-send-backward:before { content: \"\\f0d6\"; }\n.ri-send-plane-2-fill:before { content: \"\\f0d7\"; }\n.ri-send-plane-2-line:before { content: \"\\f0d8\"; }\n.ri-send-plane-fill:before { content: \"\\f0d9\"; }\n.ri-send-plane-line:before { content: \"\\f0da\"; }\n.ri-send-to-back:before { content: \"\\f0db\"; }\n.ri-sensor-fill:before { content: \"\\f0dc\"; }\n.ri-sensor-line:before { content: \"\\f0dd\"; }\n.ri-separator:before { content: \"\\f0de\"; }\n.ri-server-fill:before { content: \"\\f0df\"; }\n.ri-server-line:before { content: \"\\f0e0\"; }\n.ri-service-fill:before { content: \"\\f0e1\"; }\n.ri-service-line:before { content: \"\\f0e2\"; }\n.ri-settings-2-fill:before { content: \"\\f0e3\"; }\n.ri-settings-2-line:before { content: \"\\f0e4\"; }\n.ri-settings-3-fill:before { content: \"\\f0e5\"; }\n.ri-settings-3-line:before { content: \"\\f0e6\"; }\n.ri-settings-4-fill:before { content: \"\\f0e7\"; }\n.ri-settings-4-line:before { content: \"\\f0e8\"; }\n.ri-settings-5-fill:before { content: \"\\f0e9\"; }\n.ri-settings-5-line:before { content: \"\\f0ea\"; }\n.ri-settings-6-fill:before { content: \"\\f0eb\"; }\n.ri-settings-6-line:before { content: \"\\f0ec\"; }\n.ri-settings-fill:before { content: \"\\f0ed\"; }\n.ri-settings-line:before { content: \"\\f0ee\"; }\n.ri-shape-2-fill:before { content: \"\\f0ef\"; }\n.ri-shape-2-line:before { content: \"\\f0f0\"; }\n.ri-shape-fill:before { content: \"\\f0f1\"; }\n.ri-shape-line:before { content: \"\\f0f2\"; }\n.ri-share-box-fill:before { content: \"\\f0f3\"; }\n.ri-share-box-line:before { content: \"\\f0f4\"; }\n.ri-share-circle-fill:before { content: \"\\f0f5\"; }\n.ri-share-circle-line:before { content: \"\\f0f6\"; }\n.ri-share-fill:before { content: \"\\f0f7\"; }\n.ri-share-forward-2-fill:before { content: \"\\f0f8\"; }\n.ri-share-forward-2-line:before { content: \"\\f0f9\"; }\n.ri-share-forward-box-fill:before { content: \"\\f0fa\"; }\n.ri-share-forward-box-line:before { content: \"\\f0fb\"; }\n.ri-share-forward-fill:before { content: \"\\f0fc\"; }\n.ri-share-forward-line:before { content: \"\\f0fd\"; }\n.ri-share-line:before { content: \"\\f0fe\"; }\n.ri-shield-check-fill:before { content: \"\\f0ff\"; }\n.ri-shield-check-line:before { content: \"\\f100\"; }\n.ri-shield-cross-fill:before { content: \"\\f101\"; }\n.ri-shield-cross-line:before { content: \"\\f102\"; }\n.ri-shield-fill:before { content: \"\\f103\"; }\n.ri-shield-flash-fill:before { content: \"\\f104\"; }\n.ri-shield-flash-line:before { content: \"\\f105\"; }\n.ri-shield-keyhole-fill:before { content: \"\\f106\"; }\n.ri-shield-keyhole-line:before { content: \"\\f107\"; }\n.ri-shield-line:before { content: \"\\f108\"; }\n.ri-shield-star-fill:before { content: \"\\f109\"; }\n.ri-shield-star-line:before { content: \"\\f10a\"; }\n.ri-shield-user-fill:before { content: \"\\f10b\"; }\n.ri-shield-user-line:before { content: \"\\f10c\"; }\n.ri-ship-2-fill:before { content: \"\\f10d\"; }\n.ri-ship-2-line:before { content: \"\\f10e\"; }\n.ri-ship-fill:before { content: \"\\f10f\"; }\n.ri-ship-line:before { content: \"\\f110\"; }\n.ri-shirt-fill:before { content: \"\\f111\"; }\n.ri-shirt-line:before { content: \"\\f112\"; }\n.ri-shopping-bag-2-fill:before { content: \"\\f113\"; }\n.ri-shopping-bag-2-line:before { content: \"\\f114\"; }\n.ri-shopping-bag-3-fill:before { content: \"\\f115\"; }\n.ri-shopping-bag-3-line:before { content: \"\\f116\"; }\n.ri-shopping-bag-fill:before { content: \"\\f117\"; }\n.ri-shopping-bag-line:before { content: \"\\f118\"; }\n.ri-shopping-basket-2-fill:before { content: \"\\f119\"; }\n.ri-shopping-basket-2-line:before { content: \"\\f11a\"; }\n.ri-shopping-basket-fill:before { content: \"\\f11b\"; }\n.ri-shopping-basket-line:before { content: \"\\f11c\"; }\n.ri-shopping-cart-2-fill:before { content: \"\\f11d\"; }\n.ri-shopping-cart-2-line:before { content: \"\\f11e\"; }\n.ri-shopping-cart-fill:before { content: \"\\f11f\"; }\n.ri-shopping-cart-line:before { content: \"\\f120\"; }\n.ri-showers-fill:before { content: \"\\f121\"; }\n.ri-showers-line:before { content: \"\\f122\"; }\n.ri-shuffle-fill:before { content: \"\\f123\"; }\n.ri-shuffle-line:before { content: \"\\f124\"; }\n.ri-shut-down-fill:before { content: \"\\f125\"; }\n.ri-shut-down-line:before { content: \"\\f126\"; }\n.ri-side-bar-fill:before { content: \"\\f127\"; }\n.ri-side-bar-line:before { content: \"\\f128\"; }\n.ri-signal-tower-fill:before { content: \"\\f129\"; }\n.ri-signal-tower-line:before { content: \"\\f12a\"; }\n.ri-signal-wifi-1-fill:before { content: \"\\f12b\"; }\n.ri-signal-wifi-1-line:before { content: \"\\f12c\"; }\n.ri-signal-wifi-2-fill:before { content: \"\\f12d\"; }\n.ri-signal-wifi-2-line:before { content: \"\\f12e\"; }\n.ri-signal-wifi-3-fill:before { content: \"\\f12f\"; }\n.ri-signal-wifi-3-line:before { content: \"\\f130\"; }\n.ri-signal-wifi-error-fill:before { content: \"\\f131\"; }\n.ri-signal-wifi-error-line:before { content: \"\\f132\"; }\n.ri-signal-wifi-fill:before { content: \"\\f133\"; }\n.ri-signal-wifi-line:before { content: \"\\f134\"; }\n.ri-signal-wifi-off-fill:before { content: \"\\f135\"; }\n.ri-signal-wifi-off-line:before { content: \"\\f136\"; }\n.ri-sim-card-2-fill:before { content: \"\\f137\"; }\n.ri-sim-card-2-line:before { content: \"\\f138\"; }\n.ri-sim-card-fill:before { content: \"\\f139\"; }\n.ri-sim-card-line:before { content: \"\\f13a\"; }\n.ri-single-quotes-l:before { content: \"\\f13b\"; }\n.ri-single-quotes-r:before { content: \"\\f13c\"; }\n.ri-sip-fill:before { content: \"\\f13d\"; }\n.ri-sip-line:before { content: \"\\f13e\"; }\n.ri-skip-back-fill:before { content: \"\\f13f\"; }\n.ri-skip-back-line:before { content: \"\\f140\"; }\n.ri-skip-back-mini-fill:before { content: \"\\f141\"; }\n.ri-skip-back-mini-line:before { content: \"\\f142\"; }\n.ri-skip-forward-fill:before { content: \"\\f143\"; }\n.ri-skip-forward-line:before { content: \"\\f144\"; }\n.ri-skip-forward-mini-fill:before { content: \"\\f145\"; }\n.ri-skip-forward-mini-line:before { content: \"\\f146\"; }\n.ri-skull-2-fill:before { content: \"\\f147\"; }\n.ri-skull-2-line:before { content: \"\\f148\"; }\n.ri-skull-fill:before { content: \"\\f149\"; }\n.ri-skull-line:before { content: \"\\f14a\"; }\n.ri-skype-fill:before { content: \"\\f14b\"; }\n.ri-skype-line:before { content: \"\\f14c\"; }\n.ri-slack-fill:before { content: \"\\f14d\"; }\n.ri-slack-line:before { content: \"\\f14e\"; }\n.ri-slice-fill:before { content: \"\\f14f\"; }\n.ri-slice-line:before { content: \"\\f150\"; }\n.ri-slideshow-2-fill:before { content: \"\\f151\"; }\n.ri-slideshow-2-line:before { content: \"\\f152\"; }\n.ri-slideshow-3-fill:before { content: \"\\f153\"; }\n.ri-slideshow-3-line:before { content: \"\\f154\"; }\n.ri-slideshow-4-fill:before { content: \"\\f155\"; }\n.ri-slideshow-4-line:before { content: \"\\f156\"; }\n.ri-slideshow-fill:before { content: \"\\f157\"; }\n.ri-slideshow-line:before { content: \"\\f158\"; }\n.ri-smartphone-fill:before { content: \"\\f159\"; }\n.ri-smartphone-line:before { content: \"\\f15a\"; }\n.ri-snapchat-fill:before { content: \"\\f15b\"; }\n.ri-snapchat-line:before { content: \"\\f15c\"; }\n.ri-snowy-fill:before { content: \"\\f15d\"; }\n.ri-snowy-line:before { content: \"\\f15e\"; }\n.ri-sort-asc:before { content: \"\\f15f\"; }\n.ri-sort-desc:before { content: \"\\f160\"; }\n.ri-sound-module-fill:before { content: \"\\f161\"; }\n.ri-sound-module-line:before { content: \"\\f162\"; }\n.ri-soundcloud-fill:before { content: \"\\f163\"; }\n.ri-soundcloud-line:before { content: \"\\f164\"; }\n.ri-space-ship-fill:before { content: \"\\f165\"; }\n.ri-space-ship-line:before { content: \"\\f166\"; }\n.ri-space:before { content: \"\\f167\"; }\n.ri-spam-2-fill:before { content: \"\\f168\"; }\n.ri-spam-2-line:before { content: \"\\f169\"; }\n.ri-spam-3-fill:before { content: \"\\f16a\"; }\n.ri-spam-3-line:before { content: \"\\f16b\"; }\n.ri-spam-fill:before { content: \"\\f16c\"; }\n.ri-spam-line:before { content: \"\\f16d\"; }\n.ri-speaker-2-fill:before { content: \"\\f16e\"; }\n.ri-speaker-2-line:before { content: \"\\f16f\"; }\n.ri-speaker-3-fill:before { content: \"\\f170\"; }\n.ri-speaker-3-line:before { content: \"\\f171\"; }\n.ri-speaker-fill:before { content: \"\\f172\"; }\n.ri-speaker-line:before { content: \"\\f173\"; }\n.ri-spectrum-fill:before { content: \"\\f174\"; }\n.ri-spectrum-line:before { content: \"\\f175\"; }\n.ri-speed-fill:before { content: \"\\f176\"; }\n.ri-speed-line:before { content: \"\\f177\"; }\n.ri-speed-mini-fill:before { content: \"\\f178\"; }\n.ri-speed-mini-line:before { content: \"\\f179\"; }\n.ri-split-cells-horizontal:before { content: \"\\f17a\"; }\n.ri-split-cells-vertical:before { content: \"\\f17b\"; }\n.ri-spotify-fill:before { content: \"\\f17c\"; }\n.ri-spotify-line:before { content: \"\\f17d\"; }\n.ri-spy-fill:before { content: \"\\f17e\"; }\n.ri-spy-line:before { content: \"\\f17f\"; }\n.ri-stack-fill:before { content: \"\\f180\"; }\n.ri-stack-line:before { content: \"\\f181\"; }\n.ri-stack-overflow-fill:before { content: \"\\f182\"; }\n.ri-stack-overflow-line:before { content: \"\\f183\"; }\n.ri-stackshare-fill:before { content: \"\\f184\"; }\n.ri-stackshare-line:before { content: \"\\f185\"; }\n.ri-star-fill:before { content: \"\\f186\"; }\n.ri-star-half-fill:before { content: \"\\f187\"; }\n.ri-star-half-line:before { content: \"\\f188\"; }\n.ri-star-half-s-fill:before { content: \"\\f189\"; }\n.ri-star-half-s-line:before { content: \"\\f18a\"; }\n.ri-star-line:before { content: \"\\f18b\"; }\n.ri-star-s-fill:before { content: \"\\f18c\"; }\n.ri-star-s-line:before { content: \"\\f18d\"; }\n.ri-star-smile-fill:before { content: \"\\f18e\"; }\n.ri-star-smile-line:before { content: \"\\f18f\"; }\n.ri-steam-fill:before { content: \"\\f190\"; }\n.ri-steam-line:before { content: \"\\f191\"; }\n.ri-steering-2-fill:before { content: \"\\f192\"; }\n.ri-steering-2-line:before { content: \"\\f193\"; }\n.ri-steering-fill:before { content: \"\\f194\"; }\n.ri-steering-line:before { content: \"\\f195\"; }\n.ri-stethoscope-fill:before { content: \"\\f196\"; }\n.ri-stethoscope-line:before { content: \"\\f197\"; }\n.ri-sticky-note-2-fill:before { content: \"\\f198\"; }\n.ri-sticky-note-2-line:before { content: \"\\f199\"; }\n.ri-sticky-note-fill:before { content: \"\\f19a\"; }\n.ri-sticky-note-line:before { content: \"\\f19b\"; }\n.ri-stock-fill:before { content: \"\\f19c\"; }\n.ri-stock-line:before { content: \"\\f19d\"; }\n.ri-stop-circle-fill:before { content: \"\\f19e\"; }\n.ri-stop-circle-line:before { content: \"\\f19f\"; }\n.ri-stop-fill:before { content: \"\\f1a0\"; }\n.ri-stop-line:before { content: \"\\f1a1\"; }\n.ri-stop-mini-fill:before { content: \"\\f1a2\"; }\n.ri-stop-mini-line:before { content: \"\\f1a3\"; }\n.ri-store-2-fill:before { content: \"\\f1a4\"; }\n.ri-store-2-line:before { content: \"\\f1a5\"; }\n.ri-store-3-fill:before { content: \"\\f1a6\"; }\n.ri-store-3-line:before { content: \"\\f1a7\"; }\n.ri-store-fill:before { content: \"\\f1a8\"; }\n.ri-store-line:before { content: \"\\f1a9\"; }\n.ri-strikethrough-2:before { content: \"\\f1aa\"; }\n.ri-strikethrough:before { content: \"\\f1ab\"; }\n.ri-subscript-2:before { content: \"\\f1ac\"; }\n.ri-subscript:before { content: \"\\f1ad\"; }\n.ri-subtract-fill:before { content: \"\\f1ae\"; }\n.ri-subtract-line:before { content: \"\\f1af\"; }\n.ri-subway-fill:before { content: \"\\f1b0\"; }\n.ri-subway-line:before { content: \"\\f1b1\"; }\n.ri-subway-wifi-fill:before { content: \"\\f1b2\"; }\n.ri-subway-wifi-line:before { content: \"\\f1b3\"; }\n.ri-suitcase-2-fill:before { content: \"\\f1b4\"; }\n.ri-suitcase-2-line:before { content: \"\\f1b5\"; }\n.ri-suitcase-3-fill:before { content: \"\\f1b6\"; }\n.ri-suitcase-3-line:before { content: \"\\f1b7\"; }\n.ri-suitcase-fill:before { content: \"\\f1b8\"; }\n.ri-suitcase-line:before { content: \"\\f1b9\"; }\n.ri-sun-cloudy-fill:before { content: \"\\f1ba\"; }\n.ri-sun-cloudy-line:before { content: \"\\f1bb\"; }\n.ri-sun-fill:before { content: \"\\f1bc\"; }\n.ri-sun-foggy-fill:before { content: \"\\f1bd\"; }\n.ri-sun-foggy-line:before { content: \"\\f1be\"; }\n.ri-sun-line:before { content: \"\\f1bf\"; }\n.ri-superscript-2:before { content: \"\\f1c0\"; }\n.ri-superscript:before { content: \"\\f1c1\"; }\n.ri-surgical-mask-fill:before { content: \"\\f1c2\"; }\n.ri-surgical-mask-line:before { content: \"\\f1c3\"; }\n.ri-surround-sound-fill:before { content: \"\\f1c4\"; }\n.ri-surround-sound-line:before { content: \"\\f1c5\"; }\n.ri-survey-fill:before { content: \"\\f1c6\"; }\n.ri-survey-line:before { content: \"\\f1c7\"; }\n.ri-swap-box-fill:before { content: \"\\f1c8\"; }\n.ri-swap-box-line:before { content: \"\\f1c9\"; }\n.ri-swap-fill:before { content: \"\\f1ca\"; }\n.ri-swap-line:before { content: \"\\f1cb\"; }\n.ri-switch-fill:before { content: \"\\f1cc\"; }\n.ri-switch-line:before { content: \"\\f1cd\"; }\n.ri-sword-fill:before { content: \"\\f1ce\"; }\n.ri-sword-line:before { content: \"\\f1cf\"; }\n.ri-syringe-fill:before { content: \"\\f1d0\"; }\n.ri-syringe-line:before { content: \"\\f1d1\"; }\n.ri-t-box-fill:before { content: \"\\f1d2\"; }\n.ri-t-box-line:before { content: \"\\f1d3\"; }\n.ri-t-shirt-2-fill:before { content: \"\\f1d4\"; }\n.ri-t-shirt-2-line:before { content: \"\\f1d5\"; }\n.ri-t-shirt-air-fill:before { content: \"\\f1d6\"; }\n.ri-t-shirt-air-line:before { content: \"\\f1d7\"; }\n.ri-t-shirt-fill:before { content: \"\\f1d8\"; }\n.ri-t-shirt-line:before { content: \"\\f1d9\"; }\n.ri-table-2:before { content: \"\\f1da\"; }\n.ri-table-alt-fill:before { content: \"\\f1db\"; }\n.ri-table-alt-line:before { content: \"\\f1dc\"; }\n.ri-table-fill:before { content: \"\\f1dd\"; }\n.ri-table-line:before { content: \"\\f1de\"; }\n.ri-tablet-fill:before { content: \"\\f1df\"; }\n.ri-tablet-line:before { content: \"\\f1e0\"; }\n.ri-takeaway-fill:before { content: \"\\f1e1\"; }\n.ri-takeaway-line:before { content: \"\\f1e2\"; }\n.ri-taobao-fill:before { content: \"\\f1e3\"; }\n.ri-taobao-line:before { content: \"\\f1e4\"; }\n.ri-tape-fill:before { content: \"\\f1e5\"; }\n.ri-tape-line:before { content: \"\\f1e6\"; }\n.ri-task-fill:before { content: \"\\f1e7\"; }\n.ri-task-line:before { content: \"\\f1e8\"; }\n.ri-taxi-fill:before { content: \"\\f1e9\"; }\n.ri-taxi-line:before { content: \"\\f1ea\"; }\n.ri-taxi-wifi-fill:before { content: \"\\f1eb\"; }\n.ri-taxi-wifi-line:before { content: \"\\f1ec\"; }\n.ri-team-fill:before { content: \"\\f1ed\"; }\n.ri-team-line:before { content: \"\\f1ee\"; }\n.ri-telegram-fill:before { content: \"\\f1ef\"; }\n.ri-telegram-line:before { content: \"\\f1f0\"; }\n.ri-temp-cold-fill:before { content: \"\\f1f1\"; }\n.ri-temp-cold-line:before { content: \"\\f1f2\"; }\n.ri-temp-hot-fill:before { content: \"\\f1f3\"; }\n.ri-temp-hot-line:before { content: \"\\f1f4\"; }\n.ri-terminal-box-fill:before { content: \"\\f1f5\"; }\n.ri-terminal-box-line:before { content: \"\\f1f6\"; }\n.ri-terminal-fill:before { content: \"\\f1f7\"; }\n.ri-terminal-line:before { content: \"\\f1f8\"; }\n.ri-terminal-window-fill:before { content: \"\\f1f9\"; }\n.ri-terminal-window-line:before { content: \"\\f1fa\"; }\n.ri-test-tube-fill:before { content: \"\\f1fb\"; }\n.ri-test-tube-line:before { content: \"\\f1fc\"; }\n.ri-text-direction-l:before { content: \"\\f1fd\"; }\n.ri-text-direction-r:before { content: \"\\f1fe\"; }\n.ri-text-spacing:before { content: \"\\f1ff\"; }\n.ri-text-wrap:before { content: \"\\f200\"; }\n.ri-text:before { content: \"\\f201\"; }\n.ri-thermometer-fill:before { content: \"\\f202\"; }\n.ri-thermometer-line:before { content: \"\\f203\"; }\n.ri-thumb-down-fill:before { content: \"\\f204\"; }\n.ri-thumb-down-line:before { content: \"\\f205\"; }\n.ri-thumb-up-fill:before { content: \"\\f206\"; }\n.ri-thumb-up-line:before { content: \"\\f207\"; }\n.ri-thunderstorms-fill:before { content: \"\\f208\"; }\n.ri-thunderstorms-line:before { content: \"\\f209\"; }\n.ri-ticket-2-fill:before { content: \"\\f20a\"; }\n.ri-ticket-2-line:before { content: \"\\f20b\"; }\n.ri-ticket-fill:before { content: \"\\f20c\"; }\n.ri-ticket-line:before { content: \"\\f20d\"; }\n.ri-time-fill:before { content: \"\\f20e\"; }\n.ri-time-line:before { content: \"\\f20f\"; }\n.ri-timer-2-fill:before { content: \"\\f210\"; }\n.ri-timer-2-line:before { content: \"\\f211\"; }\n.ri-timer-fill:before { content: \"\\f212\"; }\n.ri-timer-flash-fill:before { content: \"\\f213\"; }\n.ri-timer-flash-line:before { content: \"\\f214\"; }\n.ri-timer-line:before { content: \"\\f215\"; }\n.ri-todo-fill:before { content: \"\\f216\"; }\n.ri-todo-line:before { content: \"\\f217\"; }\n.ri-toggle-fill:before { content: \"\\f218\"; }\n.ri-toggle-line:before { content: \"\\f219\"; }\n.ri-tools-fill:before { content: \"\\f21a\"; }\n.ri-tools-line:before { content: \"\\f21b\"; }\n.ri-tornado-fill:before { content: \"\\f21c\"; }\n.ri-tornado-line:before { content: \"\\f21d\"; }\n.ri-trademark-fill:before { content: \"\\f21e\"; }\n.ri-trademark-line:before { content: \"\\f21f\"; }\n.ri-traffic-light-fill:before { content: \"\\f220\"; }\n.ri-traffic-light-line:before { content: \"\\f221\"; }\n.ri-train-fill:before { content: \"\\f222\"; }\n.ri-train-line:before { content: \"\\f223\"; }\n.ri-train-wifi-fill:before { content: \"\\f224\"; }\n.ri-train-wifi-line:before { content: \"\\f225\"; }\n.ri-translate-2:before { content: \"\\f226\"; }\n.ri-translate:before { content: \"\\f227\"; }\n.ri-travesti-fill:before { content: \"\\f228\"; }\n.ri-travesti-line:before { content: \"\\f229\"; }\n.ri-treasure-map-fill:before { content: \"\\f22a\"; }\n.ri-treasure-map-line:before { content: \"\\f22b\"; }\n.ri-trello-fill:before { content: \"\\f22c\"; }\n.ri-trello-line:before { content: \"\\f22d\"; }\n.ri-trophy-fill:before { content: \"\\f22e\"; }\n.ri-trophy-line:before { content: \"\\f22f\"; }\n.ri-truck-fill:before { content: \"\\f230\"; }\n.ri-truck-line:before { content: \"\\f231\"; }\n.ri-tumblr-fill:before { content: \"\\f232\"; }\n.ri-tumblr-line:before { content: \"\\f233\"; }\n.ri-tv-2-fill:before { content: \"\\f234\"; }\n.ri-tv-2-line:before { content: \"\\f235\"; }\n.ri-tv-fill:before { content: \"\\f236\"; }\n.ri-tv-line:before { content: \"\\f237\"; }\n.ri-twitch-fill:before { content: \"\\f238\"; }\n.ri-twitch-line:before { content: \"\\f239\"; }\n.ri-twitter-fill:before { content: \"\\f23a\"; }\n.ri-twitter-line:before { content: \"\\f23b\"; }\n.ri-typhoon-fill:before { content: \"\\f23c\"; }\n.ri-typhoon-line:before { content: \"\\f23d\"; }\n.ri-u-disk-fill:before { content: \"\\f23e\"; }\n.ri-u-disk-line:before { content: \"\\f23f\"; }\n.ri-ubuntu-fill:before { content: \"\\f240\"; }\n.ri-ubuntu-line:before { content: \"\\f241\"; }\n.ri-umbrella-fill:before { content: \"\\f242\"; }\n.ri-umbrella-line:before { content: \"\\f243\"; }\n.ri-underline:before { content: \"\\f244\"; }\n.ri-uninstall-fill:before { content: \"\\f245\"; }\n.ri-uninstall-line:before { content: \"\\f246\"; }\n.ri-unsplash-fill:before { content: \"\\f247\"; }\n.ri-unsplash-line:before { content: \"\\f248\"; }\n.ri-upload-2-fill:before { content: \"\\f249\"; }\n.ri-upload-2-line:before { content: \"\\f24a\"; }\n.ri-upload-cloud-2-fill:before { content: \"\\f24b\"; }\n.ri-upload-cloud-2-line:before { content: \"\\f24c\"; }\n.ri-upload-cloud-fill:before { content: \"\\f24d\"; }\n.ri-upload-cloud-line:before { content: \"\\f24e\"; }\n.ri-upload-fill:before { content: \"\\f24f\"; }\n.ri-upload-line:before { content: \"\\f250\"; }\n.ri-usb-fill:before { content: \"\\f251\"; }\n.ri-usb-line:before { content: \"\\f252\"; }\n.ri-user-2-fill:before { content: \"\\f253\"; }\n.ri-user-2-line:before { content: \"\\f254\"; }\n.ri-user-3-fill:before { content: \"\\f255\"; }\n.ri-user-3-line:before { content: \"\\f256\"; }\n.ri-user-4-fill:before { content: \"\\f257\"; }\n.ri-user-4-line:before { content: \"\\f258\"; }\n.ri-user-5-fill:before { content: \"\\f259\"; }\n.ri-user-5-line:before { content: \"\\f25a\"; }\n.ri-user-6-fill:before { content: \"\\f25b\"; }\n.ri-user-6-line:before { content: \"\\f25c\"; }\n.ri-user-add-fill:before { content: \"\\f25d\"; }\n.ri-user-add-line:before { content: \"\\f25e\"; }\n.ri-user-fill:before { content: \"\\f25f\"; }\n.ri-user-follow-fill:before { content: \"\\f260\"; }\n.ri-user-follow-line:before { content: \"\\f261\"; }\n.ri-user-heart-fill:before { content: \"\\f262\"; }\n.ri-user-heart-line:before { content: \"\\f263\"; }\n.ri-user-line:before { content: \"\\f264\"; }\n.ri-user-location-fill:before { content: \"\\f265\"; }\n.ri-user-location-line:before { content: \"\\f266\"; }\n.ri-user-received-2-fill:before { content: \"\\f267\"; }\n.ri-user-received-2-line:before { content: \"\\f268\"; }\n.ri-user-received-fill:before { content: \"\\f269\"; }\n.ri-user-received-line:before { content: \"\\f26a\"; }\n.ri-user-search-fill:before { content: \"\\f26b\"; }\n.ri-user-search-line:before { content: \"\\f26c\"; }\n.ri-user-settings-fill:before { content: \"\\f26d\"; }\n.ri-user-settings-line:before { content: \"\\f26e\"; }\n.ri-user-shared-2-fill:before { content: \"\\f26f\"; }\n.ri-user-shared-2-line:before { content: \"\\f270\"; }\n.ri-user-shared-fill:before { content: \"\\f271\"; }\n.ri-user-shared-line:before { content: \"\\f272\"; }\n.ri-user-smile-fill:before { content: \"\\f273\"; }\n.ri-user-smile-line:before { content: \"\\f274\"; }\n.ri-user-star-fill:before { content: \"\\f275\"; }\n.ri-user-star-line:before { content: \"\\f276\"; }\n.ri-user-unfollow-fill:before { content: \"\\f277\"; }\n.ri-user-unfollow-line:before { content: \"\\f278\"; }\n.ri-user-voice-fill:before { content: \"\\f279\"; }\n.ri-user-voice-line:before { content: \"\\f27a\"; }\n.ri-video-add-fill:before { content: \"\\f27b\"; }\n.ri-video-add-line:before { content: \"\\f27c\"; }\n.ri-video-chat-fill:before { content: \"\\f27d\"; }\n.ri-video-chat-line:before { content: \"\\f27e\"; }\n.ri-video-download-fill:before { content: \"\\f27f\"; }\n.ri-video-download-line:before { content: \"\\f280\"; }\n.ri-video-fill:before { content: \"\\f281\"; }\n.ri-video-line:before { content: \"\\f282\"; }\n.ri-video-upload-fill:before { content: \"\\f283\"; }\n.ri-video-upload-line:before { content: \"\\f284\"; }\n.ri-vidicon-2-fill:before { content: \"\\f285\"; }\n.ri-vidicon-2-line:before { content: \"\\f286\"; }\n.ri-vidicon-fill:before { content: \"\\f287\"; }\n.ri-vidicon-line:before { content: \"\\f288\"; }\n.ri-vimeo-fill:before { content: \"\\f289\"; }\n.ri-vimeo-line:before { content: \"\\f28a\"; }\n.ri-vip-crown-2-fill:before { content: \"\\f28b\"; }\n.ri-vip-crown-2-line:before { content: \"\\f28c\"; }\n.ri-vip-crown-fill:before { content: \"\\f28d\"; }\n.ri-vip-crown-line:before { content: \"\\f28e\"; }\n.ri-vip-diamond-fill:before { content: \"\\f28f\"; }\n.ri-vip-diamond-line:before { content: \"\\f290\"; }\n.ri-vip-fill:before { content: \"\\f291\"; }\n.ri-vip-line:before { content: \"\\f292\"; }\n.ri-virus-fill:before { content: \"\\f293\"; }\n.ri-virus-line:before { content: \"\\f294\"; }\n.ri-visa-fill:before { content: \"\\f295\"; }\n.ri-visa-line:before { content: \"\\f296\"; }\n.ri-voice-recognition-fill:before { content: \"\\f297\"; }\n.ri-voice-recognition-line:before { content: \"\\f298\"; }\n.ri-voiceprint-fill:before { content: \"\\f299\"; }\n.ri-voiceprint-line:before { content: \"\\f29a\"; }\n.ri-volume-down-fill:before { content: \"\\f29b\"; }\n.ri-volume-down-line:before { content: \"\\f29c\"; }\n.ri-volume-mute-fill:before { content: \"\\f29d\"; }\n.ri-volume-mute-line:before { content: \"\\f29e\"; }\n.ri-volume-off-vibrate-fill:before { content: \"\\f29f\"; }\n.ri-volume-off-vibrate-line:before { content: \"\\f2a0\"; }\n.ri-volume-up-fill:before { content: \"\\f2a1\"; }\n.ri-volume-up-line:before { content: \"\\f2a2\"; }\n.ri-volume-vibrate-fill:before { content: \"\\f2a3\"; }\n.ri-volume-vibrate-line:before { content: \"\\f2a4\"; }\n.ri-vuejs-fill:before { content: \"\\f2a5\"; }\n.ri-vuejs-line:before { content: \"\\f2a6\"; }\n.ri-walk-fill:before { content: \"\\f2a7\"; }\n.ri-walk-line:before { content: \"\\f2a8\"; }\n.ri-wallet-2-fill:before { content: \"\\f2a9\"; }\n.ri-wallet-2-line:before { content: \"\\f2aa\"; }\n.ri-wallet-3-fill:before { content: \"\\f2ab\"; }\n.ri-wallet-3-line:before { content: \"\\f2ac\"; }\n.ri-wallet-fill:before { content: \"\\f2ad\"; }\n.ri-wallet-line:before { content: \"\\f2ae\"; }\n.ri-water-flash-fill:before { content: \"\\f2af\"; }\n.ri-water-flash-line:before { content: \"\\f2b0\"; }\n.ri-webcam-fill:before { content: \"\\f2b1\"; }\n.ri-webcam-line:before { content: \"\\f2b2\"; }\n.ri-wechat-2-fill:before { content: \"\\f2b3\"; }\n.ri-wechat-2-line:before { content: \"\\f2b4\"; }\n.ri-wechat-fill:before { content: \"\\f2b5\"; }\n.ri-wechat-line:before { content: \"\\f2b6\"; }\n.ri-wechat-pay-fill:before { content: \"\\f2b7\"; }\n.ri-wechat-pay-line:before { content: \"\\f2b8\"; }\n.ri-weibo-fill:before { content: \"\\f2b9\"; }\n.ri-weibo-line:before { content: \"\\f2ba\"; }\n.ri-whatsapp-fill:before { content: \"\\f2bb\"; }\n.ri-whatsapp-line:before { content: \"\\f2bc\"; }\n.ri-wheelchair-fill:before { content: \"\\f2bd\"; }\n.ri-wheelchair-line:before { content: \"\\f2be\"; }\n.ri-wifi-fill:before { content: \"\\f2bf\"; }\n.ri-wifi-line:before { content: \"\\f2c0\"; }\n.ri-wifi-off-fill:before { content: \"\\f2c1\"; }\n.ri-wifi-off-line:before { content: \"\\f2c2\"; }\n.ri-window-2-fill:before { content: \"\\f2c3\"; }\n.ri-window-2-line:before { content: \"\\f2c4\"; }\n.ri-window-fill:before { content: \"\\f2c5\"; }\n.ri-window-line:before { content: \"\\f2c6\"; }\n.ri-windows-fill:before { content: \"\\f2c7\"; }\n.ri-windows-line:before { content: \"\\f2c8\"; }\n.ri-windy-fill:before { content: \"\\f2c9\"; }\n.ri-windy-line:before { content: \"\\f2ca\"; }\n.ri-wireless-charging-fill:before { content: \"\\f2cb\"; }\n.ri-wireless-charging-line:before { content: \"\\f2cc\"; }\n.ri-women-fill:before { content: \"\\f2cd\"; }\n.ri-women-line:before { content: \"\\f2ce\"; }\n.ri-wubi-input:before { content: \"\\f2cf\"; }\n.ri-xbox-fill:before { content: \"\\f2d0\"; }\n.ri-xbox-line:before { content: \"\\f2d1\"; }\n.ri-xing-fill:before { content: \"\\f2d2\"; }\n.ri-xing-line:before { content: \"\\f2d3\"; }\n.ri-youtube-fill:before { content: \"\\f2d4\"; }\n.ri-youtube-line:before { content: \"\\f2d5\"; }\n.ri-zcool-fill:before { content: \"\\f2d6\"; }\n.ri-zcool-line:before { content: \"\\f2d7\"; }\n.ri-zhihu-fill:before { content: \"\\f2d8\"; }\n.ri-zhihu-line:before { content: \"\\f2d9\"; }\n.ri-zoom-in-fill:before { content: \"\\f2da\"; }\n.ri-zoom-in-line:before { content: \"\\f2db\"; }\n.ri-zoom-out-fill:before { content: \"\\f2dc\"; }\n.ri-zoom-out-line:before { content: \"\\f2dd\"; }\n.ri-zzz-fill:before { content: \"\\f2de\"; }\n.ri-zzz-line:before { content: \"\\f2df\"; }\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/remixicon/remixicon.less",
    "content": "/*\n* Remix Icon v2.5.0\n* https://remixicon.com\n* https://github.com/Remix-Design/RemixIcon\n*\n* Copyright RemixIcon.com\n* Released under the Apache License Version 2.0\n*\n* Date: 2020-05-23\n*/\n@font-face {\n  font-family: \"remixicon\";\n  src: url('remixicon.eot?t=1590207869815'); /* IE9*/\n  src: url('remixicon.eot?t=1590207869815#iefix') format('embedded-opentype'), /* IE6-IE8 */\n  url(\"remixicon.woff2?t=1590207869815\") format(\"woff2\"),\n  url(\"remixicon.woff?t=1590207869815\") format(\"woff\"),\n  url('remixicon.ttf?t=1590207869815') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/\n  url('remixicon.svg?t=1590207869815#remixicon') format('svg'); /* iOS 4.1- */\n  font-display: swap;\n}\n\n[class^=\"ri-\"], [class*=\" ri-\"] {\n  font-family: 'remixicon' !important;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.ri-lg { font-size: 1.3333em; line-height: 0.75em; vertical-align: -.0667em; }\n.ri-xl { font-size: 1.5em; line-height: 0.6666em; vertical-align: -.075em; }\n.ri-xxs { font-size: .5em; }\n.ri-xs { font-size: .75em; }\n.ri-sm { font-size: .875em }\n.ri-1x { font-size: 1em; }\n.ri-2x { font-size: 2em; }\n.ri-3x { font-size: 3em; }\n.ri-4x { font-size: 4em; }\n.ri-5x { font-size: 5em; }\n.ri-6x { font-size: 6em; }\n.ri-7x { font-size: 7em; }\n.ri-8x { font-size: 8em; }\n.ri-9x { font-size: 9em; }\n.ri-10x { font-size: 10em; }\n.ri-fw { text-align: center; width: 1.25em; }\n\n:global {\n.ri-24-hours-fill:before { content: \"\\ea01\"; }\n.ri-24-hours-line:before { content: \"\\ea02\"; }\n.ri-4k-fill:before { content: \"\\ea03\"; }\n.ri-4k-line:before { content: \"\\ea04\"; }\n.ri-a-b:before { content: \"\\ea05\"; }\n.ri-account-box-fill:before { content: \"\\ea06\"; }\n.ri-account-box-line:before { content: \"\\ea07\"; }\n.ri-account-circle-fill:before { content: \"\\ea08\"; }\n.ri-account-circle-line:before { content: \"\\ea09\"; }\n.ri-account-pin-box-fill:before { content: \"\\ea0a\"; }\n.ri-account-pin-box-line:before { content: \"\\ea0b\"; }\n.ri-account-pin-circle-fill:before { content: \"\\ea0c\"; }\n.ri-account-pin-circle-line:before { content: \"\\ea0d\"; }\n.ri-add-box-fill:before { content: \"\\ea0e\"; }\n.ri-add-box-line:before { content: \"\\ea0f\"; }\n.ri-add-circle-fill:before { content: \"\\ea10\"; }\n.ri-add-circle-line:before { content: \"\\ea11\"; }\n.ri-add-fill:before { content: \"\\ea12\"; }\n.ri-add-line:before { content: \"\\ea13\"; }\n.ri-admin-fill:before { content: \"\\ea14\"; }\n.ri-admin-line:before { content: \"\\ea15\"; }\n.ri-advertisement-fill:before { content: \"\\ea16\"; }\n.ri-advertisement-line:before { content: \"\\ea17\"; }\n.ri-airplay-fill:before { content: \"\\ea18\"; }\n.ri-airplay-line:before { content: \"\\ea19\"; }\n.ri-alarm-fill:before { content: \"\\ea1a\"; }\n.ri-alarm-line:before { content: \"\\ea1b\"; }\n.ri-alarm-warning-fill:before { content: \"\\ea1c\"; }\n.ri-alarm-warning-line:before { content: \"\\ea1d\"; }\n.ri-album-fill:before { content: \"\\ea1e\"; }\n.ri-album-line:before { content: \"\\ea1f\"; }\n.ri-alert-fill:before { content: \"\\ea20\"; }\n.ri-alert-line:before { content: \"\\ea21\"; }\n.ri-aliens-fill:before { content: \"\\ea22\"; }\n.ri-aliens-line:before { content: \"\\ea23\"; }\n.ri-align-bottom:before { content: \"\\ea24\"; }\n.ri-align-center:before { content: \"\\ea25\"; }\n.ri-align-justify:before { content: \"\\ea26\"; }\n.ri-align-left:before { content: \"\\ea27\"; }\n.ri-align-right:before { content: \"\\ea28\"; }\n.ri-align-top:before { content: \"\\ea29\"; }\n.ri-align-vertically:before { content: \"\\ea2a\"; }\n.ri-alipay-fill:before { content: \"\\ea2b\"; }\n.ri-alipay-line:before { content: \"\\ea2c\"; }\n.ri-amazon-fill:before { content: \"\\ea2d\"; }\n.ri-amazon-line:before { content: \"\\ea2e\"; }\n.ri-anchor-fill:before { content: \"\\ea2f\"; }\n.ri-anchor-line:before { content: \"\\ea30\"; }\n.ri-ancient-gate-fill:before { content: \"\\ea31\"; }\n.ri-ancient-gate-line:before { content: \"\\ea32\"; }\n.ri-ancient-pavilion-fill:before { content: \"\\ea33\"; }\n.ri-ancient-pavilion-line:before { content: \"\\ea34\"; }\n.ri-android-fill:before { content: \"\\ea35\"; }\n.ri-android-line:before { content: \"\\ea36\"; }\n.ri-angularjs-fill:before { content: \"\\ea37\"; }\n.ri-angularjs-line:before { content: \"\\ea38\"; }\n.ri-anticlockwise-2-fill:before { content: \"\\ea39\"; }\n.ri-anticlockwise-2-line:before { content: \"\\ea3a\"; }\n.ri-anticlockwise-fill:before { content: \"\\ea3b\"; }\n.ri-anticlockwise-line:before { content: \"\\ea3c\"; }\n.ri-app-store-fill:before { content: \"\\ea3d\"; }\n.ri-app-store-line:before { content: \"\\ea3e\"; }\n.ri-apple-fill:before { content: \"\\ea3f\"; }\n.ri-apple-line:before { content: \"\\ea40\"; }\n.ri-apps-2-fill:before { content: \"\\ea41\"; }\n.ri-apps-2-line:before { content: \"\\ea42\"; }\n.ri-apps-fill:before { content: \"\\ea43\"; }\n.ri-apps-line:before { content: \"\\ea44\"; }\n.ri-archive-drawer-fill:before { content: \"\\ea45\"; }\n.ri-archive-drawer-line:before { content: \"\\ea46\"; }\n.ri-archive-fill:before { content: \"\\ea47\"; }\n.ri-archive-line:before { content: \"\\ea48\"; }\n.ri-arrow-down-circle-fill:before { content: \"\\ea49\"; }\n.ri-arrow-down-circle-line:before { content: \"\\ea4a\"; }\n.ri-arrow-down-fill:before { content: \"\\ea4b\"; }\n.ri-arrow-down-line:before { content: \"\\ea4c\"; }\n.ri-arrow-down-s-fill:before { content: \"\\ea4d\"; }\n.ri-arrow-down-s-line:before { content: \"\\ea4e\"; }\n.ri-arrow-drop-down-fill:before { content: \"\\ea4f\"; }\n.ri-arrow-drop-down-line:before { content: \"\\ea50\"; }\n.ri-arrow-drop-left-fill:before { content: \"\\ea51\"; }\n.ri-arrow-drop-left-line:before { content: \"\\ea52\"; }\n.ri-arrow-drop-right-fill:before { content: \"\\ea53\"; }\n.ri-arrow-drop-right-line:before { content: \"\\ea54\"; }\n.ri-arrow-drop-up-fill:before { content: \"\\ea55\"; }\n.ri-arrow-drop-up-line:before { content: \"\\ea56\"; }\n.ri-arrow-go-back-fill:before { content: \"\\ea57\"; }\n.ri-arrow-go-back-line:before { content: \"\\ea58\"; }\n.ri-arrow-go-forward-fill:before { content: \"\\ea59\"; }\n.ri-arrow-go-forward-line:before { content: \"\\ea5a\"; }\n.ri-arrow-left-circle-fill:before { content: \"\\ea5b\"; }\n.ri-arrow-left-circle-line:before { content: \"\\ea5c\"; }\n.ri-arrow-left-down-fill:before { content: \"\\ea5d\"; }\n.ri-arrow-left-down-line:before { content: \"\\ea5e\"; }\n.ri-arrow-left-fill:before { content: \"\\ea5f\"; }\n.ri-arrow-left-line:before { content: \"\\ea60\"; }\n.ri-arrow-left-right-fill:before { content: \"\\ea61\"; }\n.ri-arrow-left-right-line:before { content: \"\\ea62\"; }\n.ri-arrow-left-s-fill:before { content: \"\\ea63\"; }\n.ri-arrow-left-s-line:before { content: \"\\ea64\"; }\n.ri-arrow-left-up-fill:before { content: \"\\ea65\"; }\n.ri-arrow-left-up-line:before { content: \"\\ea66\"; }\n.ri-arrow-right-circle-fill:before { content: \"\\ea67\"; }\n.ri-arrow-right-circle-line:before { content: \"\\ea68\"; }\n.ri-arrow-right-down-fill:before { content: \"\\ea69\"; }\n.ri-arrow-right-down-line:before { content: \"\\ea6a\"; }\n.ri-arrow-right-fill:before { content: \"\\ea6b\"; }\n.ri-arrow-right-line:before { content: \"\\ea6c\"; }\n.ri-arrow-right-s-fill:before { content: \"\\ea6d\"; }\n.ri-arrow-right-s-line:before { content: \"\\ea6e\"; }\n.ri-arrow-right-up-fill:before { content: \"\\ea6f\"; }\n.ri-arrow-right-up-line:before { content: \"\\ea70\"; }\n.ri-arrow-up-circle-fill:before { content: \"\\ea71\"; }\n.ri-arrow-up-circle-line:before { content: \"\\ea72\"; }\n.ri-arrow-up-down-fill:before { content: \"\\ea73\"; }\n.ri-arrow-up-down-line:before { content: \"\\ea74\"; }\n.ri-arrow-up-fill:before { content: \"\\ea75\"; }\n.ri-arrow-up-line:before { content: \"\\ea76\"; }\n.ri-arrow-up-s-fill:before { content: \"\\ea77\"; }\n.ri-arrow-up-s-line:before { content: \"\\ea78\"; }\n.ri-artboard-2-fill:before { content: \"\\ea79\"; }\n.ri-artboard-2-line:before { content: \"\\ea7a\"; }\n.ri-artboard-fill:before { content: \"\\ea7b\"; }\n.ri-artboard-line:before { content: \"\\ea7c\"; }\n.ri-article-fill:before { content: \"\\ea7d\"; }\n.ri-article-line:before { content: \"\\ea7e\"; }\n.ri-aspect-ratio-fill:before { content: \"\\ea7f\"; }\n.ri-aspect-ratio-line:before { content: \"\\ea80\"; }\n.ri-asterisk:before { content: \"\\ea81\"; }\n.ri-at-fill:before { content: \"\\ea82\"; }\n.ri-at-line:before { content: \"\\ea83\"; }\n.ri-attachment-2:before { content: \"\\ea84\"; }\n.ri-attachment-fill:before { content: \"\\ea85\"; }\n.ri-attachment-line:before { content: \"\\ea86\"; }\n.ri-auction-fill:before { content: \"\\ea87\"; }\n.ri-auction-line:before { content: \"\\ea88\"; }\n.ri-award-fill:before { content: \"\\ea89\"; }\n.ri-award-line:before { content: \"\\ea8a\"; }\n.ri-baidu-fill:before { content: \"\\ea8b\"; }\n.ri-baidu-line:before { content: \"\\ea8c\"; }\n.ri-ball-pen-fill:before { content: \"\\ea8d\"; }\n.ri-ball-pen-line:before { content: \"\\ea8e\"; }\n.ri-bank-card-2-fill:before { content: \"\\ea8f\"; }\n.ri-bank-card-2-line:before { content: \"\\ea90\"; }\n.ri-bank-card-fill:before { content: \"\\ea91\"; }\n.ri-bank-card-line:before { content: \"\\ea92\"; }\n.ri-bank-fill:before { content: \"\\ea93\"; }\n.ri-bank-line:before { content: \"\\ea94\"; }\n.ri-bar-chart-2-fill:before { content: \"\\ea95\"; }\n.ri-bar-chart-2-line:before { content: \"\\ea96\"; }\n.ri-bar-chart-box-fill:before { content: \"\\ea97\"; }\n.ri-bar-chart-box-line:before { content: \"\\ea98\"; }\n.ri-bar-chart-fill:before { content: \"\\ea99\"; }\n.ri-bar-chart-grouped-fill:before { content: \"\\ea9a\"; }\n.ri-bar-chart-grouped-line:before { content: \"\\ea9b\"; }\n.ri-bar-chart-horizontal-fill:before { content: \"\\ea9c\"; }\n.ri-bar-chart-horizontal-line:before { content: \"\\ea9d\"; }\n.ri-bar-chart-line:before { content: \"\\ea9e\"; }\n.ri-barcode-box-fill:before { content: \"\\ea9f\"; }\n.ri-barcode-box-line:before { content: \"\\eaa0\"; }\n.ri-barcode-fill:before { content: \"\\eaa1\"; }\n.ri-barcode-line:before { content: \"\\eaa2\"; }\n.ri-barricade-fill:before { content: \"\\eaa3\"; }\n.ri-barricade-line:before { content: \"\\eaa4\"; }\n.ri-base-station-fill:before { content: \"\\eaa5\"; }\n.ri-base-station-line:before { content: \"\\eaa6\"; }\n.ri-basketball-fill:before { content: \"\\eaa7\"; }\n.ri-basketball-line:before { content: \"\\eaa8\"; }\n.ri-battery-2-charge-fill:before { content: \"\\eaa9\"; }\n.ri-battery-2-charge-line:before { content: \"\\eaaa\"; }\n.ri-battery-2-fill:before { content: \"\\eaab\"; }\n.ri-battery-2-line:before { content: \"\\eaac\"; }\n.ri-battery-charge-fill:before { content: \"\\eaad\"; }\n.ri-battery-charge-line:before { content: \"\\eaae\"; }\n.ri-battery-fill:before { content: \"\\eaaf\"; }\n.ri-battery-line:before { content: \"\\eab0\"; }\n.ri-battery-low-fill:before { content: \"\\eab1\"; }\n.ri-battery-low-line:before { content: \"\\eab2\"; }\n.ri-battery-saver-fill:before { content: \"\\eab3\"; }\n.ri-battery-saver-line:before { content: \"\\eab4\"; }\n.ri-battery-share-fill:before { content: \"\\eab5\"; }\n.ri-battery-share-line:before { content: \"\\eab6\"; }\n.ri-bear-smile-fill:before { content: \"\\eab7\"; }\n.ri-bear-smile-line:before { content: \"\\eab8\"; }\n.ri-behance-fill:before { content: \"\\eab9\"; }\n.ri-behance-line:before { content: \"\\eaba\"; }\n.ri-bell-fill:before { content: \"\\eabb\"; }\n.ri-bell-line:before { content: \"\\eabc\"; }\n.ri-bike-fill:before { content: \"\\eabd\"; }\n.ri-bike-line:before { content: \"\\eabe\"; }\n.ri-bilibili-fill:before { content: \"\\eabf\"; }\n.ri-bilibili-line:before { content: \"\\eac0\"; }\n.ri-bill-fill:before { content: \"\\eac1\"; }\n.ri-bill-line:before { content: \"\\eac2\"; }\n.ri-billiards-fill:before { content: \"\\eac3\"; }\n.ri-billiards-line:before { content: \"\\eac4\"; }\n.ri-bit-coin-fill:before { content: \"\\eac5\"; }\n.ri-bit-coin-line:before { content: \"\\eac6\"; }\n.ri-blaze-fill:before { content: \"\\eac7\"; }\n.ri-blaze-line:before { content: \"\\eac8\"; }\n.ri-bluetooth-connect-fill:before { content: \"\\eac9\"; }\n.ri-bluetooth-connect-line:before { content: \"\\eaca\"; }\n.ri-bluetooth-fill:before { content: \"\\eacb\"; }\n.ri-bluetooth-line:before { content: \"\\eacc\"; }\n.ri-blur-off-fill:before { content: \"\\eacd\"; }\n.ri-blur-off-line:before { content: \"\\eace\"; }\n.ri-body-scan-fill:before { content: \"\\eacf\"; }\n.ri-body-scan-line:before { content: \"\\ead0\"; }\n.ri-bold:before { content: \"\\ead1\"; }\n.ri-book-2-fill:before { content: \"\\ead2\"; }\n.ri-book-2-line:before { content: \"\\ead3\"; }\n.ri-book-3-fill:before { content: \"\\ead4\"; }\n.ri-book-3-line:before { content: \"\\ead5\"; }\n.ri-book-fill:before { content: \"\\ead6\"; }\n.ri-book-line:before { content: \"\\ead7\"; }\n.ri-book-mark-fill:before { content: \"\\ead8\"; }\n.ri-book-mark-line:before { content: \"\\ead9\"; }\n.ri-book-open-fill:before { content: \"\\eada\"; }\n.ri-book-open-line:before { content: \"\\eadb\"; }\n.ri-book-read-fill:before { content: \"\\eadc\"; }\n.ri-book-read-line:before { content: \"\\eadd\"; }\n.ri-booklet-fill:before { content: \"\\eade\"; }\n.ri-booklet-line:before { content: \"\\eadf\"; }\n.ri-bookmark-2-fill:before { content: \"\\eae0\"; }\n.ri-bookmark-2-line:before { content: \"\\eae1\"; }\n.ri-bookmark-3-fill:before { content: \"\\eae2\"; }\n.ri-bookmark-3-line:before { content: \"\\eae3\"; }\n.ri-bookmark-fill:before { content: \"\\eae4\"; }\n.ri-bookmark-line:before { content: \"\\eae5\"; }\n.ri-boxing-fill:before { content: \"\\eae6\"; }\n.ri-boxing-line:before { content: \"\\eae7\"; }\n.ri-braces-fill:before { content: \"\\eae8\"; }\n.ri-braces-line:before { content: \"\\eae9\"; }\n.ri-brackets-fill:before { content: \"\\eaea\"; }\n.ri-brackets-line:before { content: \"\\eaeb\"; }\n.ri-briefcase-2-fill:before { content: \"\\eaec\"; }\n.ri-briefcase-2-line:before { content: \"\\eaed\"; }\n.ri-briefcase-3-fill:before { content: \"\\eaee\"; }\n.ri-briefcase-3-line:before { content: \"\\eaef\"; }\n.ri-briefcase-4-fill:before { content: \"\\eaf0\"; }\n.ri-briefcase-4-line:before { content: \"\\eaf1\"; }\n.ri-briefcase-5-fill:before { content: \"\\eaf2\"; }\n.ri-briefcase-5-line:before { content: \"\\eaf3\"; }\n.ri-briefcase-fill:before { content: \"\\eaf4\"; }\n.ri-briefcase-line:before { content: \"\\eaf5\"; }\n.ri-bring-forward:before { content: \"\\eaf6\"; }\n.ri-bring-to-front:before { content: \"\\eaf7\"; }\n.ri-broadcast-fill:before { content: \"\\eaf8\"; }\n.ri-broadcast-line:before { content: \"\\eaf9\"; }\n.ri-brush-2-fill:before { content: \"\\eafa\"; }\n.ri-brush-2-line:before { content: \"\\eafb\"; }\n.ri-brush-3-fill:before { content: \"\\eafc\"; }\n.ri-brush-3-line:before { content: \"\\eafd\"; }\n.ri-brush-4-fill:before { content: \"\\eafe\"; }\n.ri-brush-4-line:before { content: \"\\eaff\"; }\n.ri-brush-fill:before { content: \"\\eb00\"; }\n.ri-brush-line:before { content: \"\\eb01\"; }\n.ri-bubble-chart-fill:before { content: \"\\eb02\"; }\n.ri-bubble-chart-line:before { content: \"\\eb03\"; }\n.ri-bug-2-fill:before { content: \"\\eb04\"; }\n.ri-bug-2-line:before { content: \"\\eb05\"; }\n.ri-bug-fill:before { content: \"\\eb06\"; }\n.ri-bug-line:before { content: \"\\eb07\"; }\n.ri-building-2-fill:before { content: \"\\eb08\"; }\n.ri-building-2-line:before { content: \"\\eb09\"; }\n.ri-building-3-fill:before { content: \"\\eb0a\"; }\n.ri-building-3-line:before { content: \"\\eb0b\"; }\n.ri-building-4-fill:before { content: \"\\eb0c\"; }\n.ri-building-4-line:before { content: \"\\eb0d\"; }\n.ri-building-fill:before { content: \"\\eb0e\"; }\n.ri-building-line:before { content: \"\\eb0f\"; }\n.ri-bus-2-fill:before { content: \"\\eb10\"; }\n.ri-bus-2-line:before { content: \"\\eb11\"; }\n.ri-bus-fill:before { content: \"\\eb12\"; }\n.ri-bus-line:before { content: \"\\eb13\"; }\n.ri-bus-wifi-fill:before { content: \"\\eb14\"; }\n.ri-bus-wifi-line:before { content: \"\\eb15\"; }\n.ri-cactus-fill:before { content: \"\\eb16\"; }\n.ri-cactus-line:before { content: \"\\eb17\"; }\n.ri-cake-2-fill:before { content: \"\\eb18\"; }\n.ri-cake-2-line:before { content: \"\\eb19\"; }\n.ri-cake-3-fill:before { content: \"\\eb1a\"; }\n.ri-cake-3-line:before { content: \"\\eb1b\"; }\n.ri-cake-fill:before { content: \"\\eb1c\"; }\n.ri-cake-line:before { content: \"\\eb1d\"; }\n.ri-calculator-fill:before { content: \"\\eb1e\"; }\n.ri-calculator-line:before { content: \"\\eb1f\"; }\n.ri-calendar-2-fill:before { content: \"\\eb20\"; }\n.ri-calendar-2-line:before { content: \"\\eb21\"; }\n.ri-calendar-check-fill:before { content: \"\\eb22\"; }\n.ri-calendar-check-line:before { content: \"\\eb23\"; }\n.ri-calendar-event-fill:before { content: \"\\eb24\"; }\n.ri-calendar-event-line:before { content: \"\\eb25\"; }\n.ri-calendar-fill:before { content: \"\\eb26\"; }\n.ri-calendar-line:before { content: \"\\eb27\"; }\n.ri-calendar-todo-fill:before { content: \"\\eb28\"; }\n.ri-calendar-todo-line:before { content: \"\\eb29\"; }\n.ri-camera-2-fill:before { content: \"\\eb2a\"; }\n.ri-camera-2-line:before { content: \"\\eb2b\"; }\n.ri-camera-3-fill:before { content: \"\\eb2c\"; }\n.ri-camera-3-line:before { content: \"\\eb2d\"; }\n.ri-camera-fill:before { content: \"\\eb2e\"; }\n.ri-camera-lens-fill:before { content: \"\\eb2f\"; }\n.ri-camera-lens-line:before { content: \"\\eb30\"; }\n.ri-camera-line:before { content: \"\\eb31\"; }\n.ri-camera-off-fill:before { content: \"\\eb32\"; }\n.ri-camera-off-line:before { content: \"\\eb33\"; }\n.ri-camera-switch-fill:before { content: \"\\eb34\"; }\n.ri-camera-switch-line:before { content: \"\\eb35\"; }\n.ri-capsule-fill:before { content: \"\\eb36\"; }\n.ri-capsule-line:before { content: \"\\eb37\"; }\n.ri-car-fill:before { content: \"\\eb38\"; }\n.ri-car-line:before { content: \"\\eb39\"; }\n.ri-car-washing-fill:before { content: \"\\eb3a\"; }\n.ri-car-washing-line:before { content: \"\\eb3b\"; }\n.ri-caravan-fill:before { content: \"\\eb3c\"; }\n.ri-caravan-line:before { content: \"\\eb3d\"; }\n.ri-cast-fill:before { content: \"\\eb3e\"; }\n.ri-cast-line:before { content: \"\\eb3f\"; }\n.ri-cellphone-fill:before { content: \"\\eb40\"; }\n.ri-cellphone-line:before { content: \"\\eb41\"; }\n.ri-celsius-fill:before { content: \"\\eb42\"; }\n.ri-celsius-line:before { content: \"\\eb43\"; }\n.ri-centos-fill:before { content: \"\\eb44\"; }\n.ri-centos-line:before { content: \"\\eb45\"; }\n.ri-character-recognition-fill:before { content: \"\\eb46\"; }\n.ri-character-recognition-line:before { content: \"\\eb47\"; }\n.ri-charging-pile-2-fill:before { content: \"\\eb48\"; }\n.ri-charging-pile-2-line:before { content: \"\\eb49\"; }\n.ri-charging-pile-fill:before { content: \"\\eb4a\"; }\n.ri-charging-pile-line:before { content: \"\\eb4b\"; }\n.ri-chat-1-fill:before { content: \"\\eb4c\"; }\n.ri-chat-1-line:before { content: \"\\eb4d\"; }\n.ri-chat-2-fill:before { content: \"\\eb4e\"; }\n.ri-chat-2-line:before { content: \"\\eb4f\"; }\n.ri-chat-3-fill:before { content: \"\\eb50\"; }\n.ri-chat-3-line:before { content: \"\\eb51\"; }\n.ri-chat-4-fill:before { content: \"\\eb52\"; }\n.ri-chat-4-line:before { content: \"\\eb53\"; }\n.ri-chat-check-fill:before { content: \"\\eb54\"; }\n.ri-chat-check-line:before { content: \"\\eb55\"; }\n.ri-chat-delete-fill:before { content: \"\\eb56\"; }\n.ri-chat-delete-line:before { content: \"\\eb57\"; }\n.ri-chat-download-fill:before { content: \"\\eb58\"; }\n.ri-chat-download-line:before { content: \"\\eb59\"; }\n.ri-chat-follow-up-fill:before { content: \"\\eb5a\"; }\n.ri-chat-follow-up-line:before { content: \"\\eb5b\"; }\n.ri-chat-forward-fill:before { content: \"\\eb5c\"; }\n.ri-chat-forward-line:before { content: \"\\eb5d\"; }\n.ri-chat-heart-fill:before { content: \"\\eb5e\"; }\n.ri-chat-heart-line:before { content: \"\\eb5f\"; }\n.ri-chat-history-fill:before { content: \"\\eb60\"; }\n.ri-chat-history-line:before { content: \"\\eb61\"; }\n.ri-chat-new-fill:before { content: \"\\eb62\"; }\n.ri-chat-new-line:before { content: \"\\eb63\"; }\n.ri-chat-off-fill:before { content: \"\\eb64\"; }\n.ri-chat-off-line:before { content: \"\\eb65\"; }\n.ri-chat-poll-fill:before { content: \"\\eb66\"; }\n.ri-chat-poll-line:before { content: \"\\eb67\"; }\n.ri-chat-private-fill:before { content: \"\\eb68\"; }\n.ri-chat-private-line:before { content: \"\\eb69\"; }\n.ri-chat-quote-fill:before { content: \"\\eb6a\"; }\n.ri-chat-quote-line:before { content: \"\\eb6b\"; }\n.ri-chat-settings-fill:before { content: \"\\eb6c\"; }\n.ri-chat-settings-line:before { content: \"\\eb6d\"; }\n.ri-chat-smile-2-fill:before { content: \"\\eb6e\"; }\n.ri-chat-smile-2-line:before { content: \"\\eb6f\"; }\n.ri-chat-smile-3-fill:before { content: \"\\eb70\"; }\n.ri-chat-smile-3-line:before { content: \"\\eb71\"; }\n.ri-chat-smile-fill:before { content: \"\\eb72\"; }\n.ri-chat-smile-line:before { content: \"\\eb73\"; }\n.ri-chat-upload-fill:before { content: \"\\eb74\"; }\n.ri-chat-upload-line:before { content: \"\\eb75\"; }\n.ri-chat-voice-fill:before { content: \"\\eb76\"; }\n.ri-chat-voice-line:before { content: \"\\eb77\"; }\n.ri-check-double-fill:before { content: \"\\eb78\"; }\n.ri-check-double-line:before { content: \"\\eb79\"; }\n.ri-check-fill:before { content: \"\\eb7a\"; }\n.ri-check-line:before { content: \"\\eb7b\"; }\n.ri-checkbox-blank-circle-fill:before { content: \"\\eb7c\"; }\n.ri-checkbox-blank-circle-line:before { content: \"\\eb7d\"; }\n.ri-checkbox-blank-fill:before { content: \"\\eb7e\"; }\n.ri-checkbox-blank-line:before { content: \"\\eb7f\"; }\n.ri-checkbox-circle-fill:before { content: \"\\eb80\"; }\n.ri-checkbox-circle-line:before { content: \"\\eb81\"; }\n.ri-checkbox-fill:before { content: \"\\eb82\"; }\n.ri-checkbox-indeterminate-fill:before { content: \"\\eb83\"; }\n.ri-checkbox-indeterminate-line:before { content: \"\\eb84\"; }\n.ri-checkbox-line:before { content: \"\\eb85\"; }\n.ri-checkbox-multiple-blank-fill:before { content: \"\\eb86\"; }\n.ri-checkbox-multiple-blank-line:before { content: \"\\eb87\"; }\n.ri-checkbox-multiple-fill:before { content: \"\\eb88\"; }\n.ri-checkbox-multiple-line:before { content: \"\\eb89\"; }\n.ri-china-railway-fill:before { content: \"\\eb8a\"; }\n.ri-china-railway-line:before { content: \"\\eb8b\"; }\n.ri-chrome-fill:before { content: \"\\eb8c\"; }\n.ri-chrome-line:before { content: \"\\eb8d\"; }\n.ri-clapperboard-fill:before { content: \"\\eb8e\"; }\n.ri-clapperboard-line:before { content: \"\\eb8f\"; }\n.ri-clipboard-fill:before { content: \"\\eb90\"; }\n.ri-clipboard-line:before { content: \"\\eb91\"; }\n.ri-clockwise-2-fill:before { content: \"\\eb92\"; }\n.ri-clockwise-2-line:before { content: \"\\eb93\"; }\n.ri-clockwise-fill:before { content: \"\\eb94\"; }\n.ri-clockwise-line:before { content: \"\\eb95\"; }\n.ri-close-circle-fill:before { content: \"\\eb96\"; }\n.ri-close-circle-line:before { content: \"\\eb97\"; }\n.ri-close-fill:before { content: \"\\eb98\"; }\n.ri-close-line:before { content: \"\\eb99\"; }\n.ri-closed-captioning-fill:before { content: \"\\eb9a\"; }\n.ri-closed-captioning-line:before { content: \"\\eb9b\"; }\n.ri-cloud-fill:before { content: \"\\eb9c\"; }\n.ri-cloud-line:before { content: \"\\eb9d\"; }\n.ri-cloud-off-fill:before { content: \"\\eb9e\"; }\n.ri-cloud-off-line:before { content: \"\\eb9f\"; }\n.ri-cloud-windy-fill:before { content: \"\\eba0\"; }\n.ri-cloud-windy-line:before { content: \"\\eba1\"; }\n.ri-cloudy-2-fill:before { content: \"\\eba2\"; }\n.ri-cloudy-2-line:before { content: \"\\eba3\"; }\n.ri-cloudy-fill:before { content: \"\\eba4\"; }\n.ri-cloudy-line:before { content: \"\\eba5\"; }\n.ri-code-box-fill:before { content: \"\\eba6\"; }\n.ri-code-box-line:before { content: \"\\eba7\"; }\n.ri-code-fill:before { content: \"\\eba8\"; }\n.ri-code-line:before { content: \"\\eba9\"; }\n.ri-code-s-fill:before { content: \"\\ebaa\"; }\n.ri-code-s-line:before { content: \"\\ebab\"; }\n.ri-code-s-slash-fill:before { content: \"\\ebac\"; }\n.ri-code-s-slash-line:before { content: \"\\ebad\"; }\n.ri-code-view:before { content: \"\\ebae\"; }\n.ri-codepen-fill:before { content: \"\\ebaf\"; }\n.ri-codepen-line:before { content: \"\\ebb0\"; }\n.ri-coin-fill:before { content: \"\\ebb1\"; }\n.ri-coin-line:before { content: \"\\ebb2\"; }\n.ri-coins-fill:before { content: \"\\ebb3\"; }\n.ri-coins-line:before { content: \"\\ebb4\"; }\n.ri-collage-fill:before { content: \"\\ebb5\"; }\n.ri-collage-line:before { content: \"\\ebb6\"; }\n.ri-command-fill:before { content: \"\\ebb7\"; }\n.ri-command-line:before { content: \"\\ebb8\"; }\n.ri-community-fill:before { content: \"\\ebb9\"; }\n.ri-community-line:before { content: \"\\ebba\"; }\n.ri-compass-2-fill:before { content: \"\\ebbb\"; }\n.ri-compass-2-line:before { content: \"\\ebbc\"; }\n.ri-compass-3-fill:before { content: \"\\ebbd\"; }\n.ri-compass-3-line:before { content: \"\\ebbe\"; }\n.ri-compass-4-fill:before { content: \"\\ebbf\"; }\n.ri-compass-4-line:before { content: \"\\ebc0\"; }\n.ri-compass-discover-fill:before { content: \"\\ebc1\"; }\n.ri-compass-discover-line:before { content: \"\\ebc2\"; }\n.ri-compass-fill:before { content: \"\\ebc3\"; }\n.ri-compass-line:before { content: \"\\ebc4\"; }\n.ri-compasses-2-fill:before { content: \"\\ebc5\"; }\n.ri-compasses-2-line:before { content: \"\\ebc6\"; }\n.ri-compasses-fill:before { content: \"\\ebc7\"; }\n.ri-compasses-line:before { content: \"\\ebc8\"; }\n.ri-computer-fill:before { content: \"\\ebc9\"; }\n.ri-computer-line:before { content: \"\\ebca\"; }\n.ri-contacts-book-2-fill:before { content: \"\\ebcb\"; }\n.ri-contacts-book-2-line:before { content: \"\\ebcc\"; }\n.ri-contacts-book-fill:before { content: \"\\ebcd\"; }\n.ri-contacts-book-line:before { content: \"\\ebce\"; }\n.ri-contacts-book-upload-fill:before { content: \"\\ebcf\"; }\n.ri-contacts-book-upload-line:before { content: \"\\ebd0\"; }\n.ri-contacts-fill:before { content: \"\\ebd1\"; }\n.ri-contacts-line:before { content: \"\\ebd2\"; }\n.ri-contrast-2-fill:before { content: \"\\ebd3\"; }\n.ri-contrast-2-line:before { content: \"\\ebd4\"; }\n.ri-contrast-drop-2-fill:before { content: \"\\ebd5\"; }\n.ri-contrast-drop-2-line:before { content: \"\\ebd6\"; }\n.ri-contrast-drop-fill:before { content: \"\\ebd7\"; }\n.ri-contrast-drop-line:before { content: \"\\ebd8\"; }\n.ri-contrast-fill:before { content: \"\\ebd9\"; }\n.ri-contrast-line:before { content: \"\\ebda\"; }\n.ri-copper-coin-fill:before { content: \"\\ebdb\"; }\n.ri-copper-coin-line:before { content: \"\\ebdc\"; }\n.ri-copper-diamond-fill:before { content: \"\\ebdd\"; }\n.ri-copper-diamond-line:before { content: \"\\ebde\"; }\n.ri-copyleft-fill:before { content: \"\\ebdf\"; }\n.ri-copyleft-line:before { content: \"\\ebe0\"; }\n.ri-copyright-fill:before { content: \"\\ebe1\"; }\n.ri-copyright-line:before { content: \"\\ebe2\"; }\n.ri-coreos-fill:before { content: \"\\ebe3\"; }\n.ri-coreos-line:before { content: \"\\ebe4\"; }\n.ri-coupon-2-fill:before { content: \"\\ebe5\"; }\n.ri-coupon-2-line:before { content: \"\\ebe6\"; }\n.ri-coupon-3-fill:before { content: \"\\ebe7\"; }\n.ri-coupon-3-line:before { content: \"\\ebe8\"; }\n.ri-coupon-4-fill:before { content: \"\\ebe9\"; }\n.ri-coupon-4-line:before { content: \"\\ebea\"; }\n.ri-coupon-5-fill:before { content: \"\\ebeb\"; }\n.ri-coupon-5-line:before { content: \"\\ebec\"; }\n.ri-coupon-fill:before { content: \"\\ebed\"; }\n.ri-coupon-line:before { content: \"\\ebee\"; }\n.ri-cpu-fill:before { content: \"\\ebef\"; }\n.ri-cpu-line:before { content: \"\\ebf0\"; }\n.ri-creative-commons-by-fill:before { content: \"\\ebf1\"; }\n.ri-creative-commons-by-line:before { content: \"\\ebf2\"; }\n.ri-creative-commons-fill:before { content: \"\\ebf3\"; }\n.ri-creative-commons-line:before { content: \"\\ebf4\"; }\n.ri-creative-commons-nc-fill:before { content: \"\\ebf5\"; }\n.ri-creative-commons-nc-line:before { content: \"\\ebf6\"; }\n.ri-creative-commons-nd-fill:before { content: \"\\ebf7\"; }\n.ri-creative-commons-nd-line:before { content: \"\\ebf8\"; }\n.ri-creative-commons-sa-fill:before { content: \"\\ebf9\"; }\n.ri-creative-commons-sa-line:before { content: \"\\ebfa\"; }\n.ri-creative-commons-zero-fill:before { content: \"\\ebfb\"; }\n.ri-creative-commons-zero-line:before { content: \"\\ebfc\"; }\n.ri-criminal-fill:before { content: \"\\ebfd\"; }\n.ri-criminal-line:before { content: \"\\ebfe\"; }\n.ri-crop-2-fill:before { content: \"\\ebff\"; }\n.ri-crop-2-line:before { content: \"\\ec00\"; }\n.ri-crop-fill:before { content: \"\\ec01\"; }\n.ri-crop-line:before { content: \"\\ec02\"; }\n.ri-css3-fill:before { content: \"\\ec03\"; }\n.ri-css3-line:before { content: \"\\ec04\"; }\n.ri-cup-fill:before { content: \"\\ec05\"; }\n.ri-cup-line:before { content: \"\\ec06\"; }\n.ri-currency-fill:before { content: \"\\ec07\"; }\n.ri-currency-line:before { content: \"\\ec08\"; }\n.ri-cursor-fill:before { content: \"\\ec09\"; }\n.ri-cursor-line:before { content: \"\\ec0a\"; }\n.ri-customer-service-2-fill:before { content: \"\\ec0b\"; }\n.ri-customer-service-2-line:before { content: \"\\ec0c\"; }\n.ri-customer-service-fill:before { content: \"\\ec0d\"; }\n.ri-customer-service-line:before { content: \"\\ec0e\"; }\n.ri-dashboard-2-fill:before { content: \"\\ec0f\"; }\n.ri-dashboard-2-line:before { content: \"\\ec10\"; }\n.ri-dashboard-3-fill:before { content: \"\\ec11\"; }\n.ri-dashboard-3-line:before { content: \"\\ec12\"; }\n.ri-dashboard-fill:before { content: \"\\ec13\"; }\n.ri-dashboard-line:before { content: \"\\ec14\"; }\n.ri-database-2-fill:before { content: \"\\ec15\"; }\n.ri-database-2-line:before { content: \"\\ec16\"; }\n.ri-database-fill:before { content: \"\\ec17\"; }\n.ri-database-line:before { content: \"\\ec18\"; }\n.ri-delete-back-2-fill:before { content: \"\\ec19\"; }\n.ri-delete-back-2-line:before { content: \"\\ec1a\"; }\n.ri-delete-back-fill:before { content: \"\\ec1b\"; }\n.ri-delete-back-line:before { content: \"\\ec1c\"; }\n.ri-delete-bin-2-fill:before { content: \"\\ec1d\"; }\n.ri-delete-bin-2-line:before { content: \"\\ec1e\"; }\n.ri-delete-bin-3-fill:before { content: \"\\ec1f\"; }\n.ri-delete-bin-3-line:before { content: \"\\ec20\"; }\n.ri-delete-bin-4-fill:before { content: \"\\ec21\"; }\n.ri-delete-bin-4-line:before { content: \"\\ec22\"; }\n.ri-delete-bin-5-fill:before { content: \"\\ec23\"; }\n.ri-delete-bin-5-line:before { content: \"\\ec24\"; }\n.ri-delete-bin-6-fill:before { content: \"\\ec25\"; }\n.ri-delete-bin-6-line:before { content: \"\\ec26\"; }\n.ri-delete-bin-7-fill:before { content: \"\\ec27\"; }\n.ri-delete-bin-7-line:before { content: \"\\ec28\"; }\n.ri-delete-bin-fill:before { content: \"\\ec29\"; }\n.ri-delete-bin-line:before { content: \"\\ec2a\"; }\n.ri-delete-column:before { content: \"\\ec2b\"; }\n.ri-delete-row:before { content: \"\\ec2c\"; }\n.ri-device-fill:before { content: \"\\ec2d\"; }\n.ri-device-line:before { content: \"\\ec2e\"; }\n.ri-device-recover-fill:before { content: \"\\ec2f\"; }\n.ri-device-recover-line:before { content: \"\\ec30\"; }\n.ri-dingding-fill:before { content: \"\\ec31\"; }\n.ri-dingding-line:before { content: \"\\ec32\"; }\n.ri-direction-fill:before { content: \"\\ec33\"; }\n.ri-direction-line:before { content: \"\\ec34\"; }\n.ri-disc-fill:before { content: \"\\ec35\"; }\n.ri-disc-line:before { content: \"\\ec36\"; }\n.ri-discord-fill:before { content: \"\\ec37\"; }\n.ri-discord-line:before { content: \"\\ec38\"; }\n.ri-discuss-fill:before { content: \"\\ec39\"; }\n.ri-discuss-line:before { content: \"\\ec3a\"; }\n.ri-dislike-fill:before { content: \"\\ec3b\"; }\n.ri-dislike-line:before { content: \"\\ec3c\"; }\n.ri-disqus-fill:before { content: \"\\ec3d\"; }\n.ri-disqus-line:before { content: \"\\ec3e\"; }\n.ri-divide-fill:before { content: \"\\ec3f\"; }\n.ri-divide-line:before { content: \"\\ec40\"; }\n.ri-donut-chart-fill:before { content: \"\\ec41\"; }\n.ri-donut-chart-line:before { content: \"\\ec42\"; }\n.ri-door-closed-fill:before { content: \"\\ec43\"; }\n.ri-door-closed-line:before { content: \"\\ec44\"; }\n.ri-door-fill:before { content: \"\\ec45\"; }\n.ri-door-line:before { content: \"\\ec46\"; }\n.ri-door-lock-box-fill:before { content: \"\\ec47\"; }\n.ri-door-lock-box-line:before { content: \"\\ec48\"; }\n.ri-door-lock-fill:before { content: \"\\ec49\"; }\n.ri-door-lock-line:before { content: \"\\ec4a\"; }\n.ri-door-open-fill:before { content: \"\\ec4b\"; }\n.ri-door-open-line:before { content: \"\\ec4c\"; }\n.ri-dossier-fill:before { content: \"\\ec4d\"; }\n.ri-dossier-line:before { content: \"\\ec4e\"; }\n.ri-douban-fill:before { content: \"\\ec4f\"; }\n.ri-douban-line:before { content: \"\\ec50\"; }\n.ri-double-quotes-l:before { content: \"\\ec51\"; }\n.ri-double-quotes-r:before { content: \"\\ec52\"; }\n.ri-download-2-fill:before { content: \"\\ec53\"; }\n.ri-download-2-line:before { content: \"\\ec54\"; }\n.ri-download-cloud-2-fill:before { content: \"\\ec55\"; }\n.ri-download-cloud-2-line:before { content: \"\\ec56\"; }\n.ri-download-cloud-fill:before { content: \"\\ec57\"; }\n.ri-download-cloud-line:before { content: \"\\ec58\"; }\n.ri-download-fill:before { content: \"\\ec59\"; }\n.ri-download-line:before { content: \"\\ec5a\"; }\n.ri-draft-fill:before { content: \"\\ec5b\"; }\n.ri-draft-line:before { content: \"\\ec5c\"; }\n.ri-drag-drop-fill:before { content: \"\\ec5d\"; }\n.ri-drag-drop-line:before { content: \"\\ec5e\"; }\n.ri-drag-move-2-fill:before { content: \"\\ec5f\"; }\n.ri-drag-move-2-line:before { content: \"\\ec60\"; }\n.ri-drag-move-fill:before { content: \"\\ec61\"; }\n.ri-drag-move-line:before { content: \"\\ec62\"; }\n.ri-dribbble-fill:before { content: \"\\ec63\"; }\n.ri-dribbble-line:before { content: \"\\ec64\"; }\n.ri-drive-fill:before { content: \"\\ec65\"; }\n.ri-drive-line:before { content: \"\\ec66\"; }\n.ri-drizzle-fill:before { content: \"\\ec67\"; }\n.ri-drizzle-line:before { content: \"\\ec68\"; }\n.ri-drop-fill:before { content: \"\\ec69\"; }\n.ri-drop-line:before { content: \"\\ec6a\"; }\n.ri-dropbox-fill:before { content: \"\\ec6b\"; }\n.ri-dropbox-line:before { content: \"\\ec6c\"; }\n.ri-dual-sim-1-fill:before { content: \"\\ec6d\"; }\n.ri-dual-sim-1-line:before { content: \"\\ec6e\"; }\n.ri-dual-sim-2-fill:before { content: \"\\ec6f\"; }\n.ri-dual-sim-2-line:before { content: \"\\ec70\"; }\n.ri-dv-fill:before { content: \"\\ec71\"; }\n.ri-dv-line:before { content: \"\\ec72\"; }\n.ri-dvd-fill:before { content: \"\\ec73\"; }\n.ri-dvd-line:before { content: \"\\ec74\"; }\n.ri-e-bike-2-fill:before { content: \"\\ec75\"; }\n.ri-e-bike-2-line:before { content: \"\\ec76\"; }\n.ri-e-bike-fill:before { content: \"\\ec77\"; }\n.ri-e-bike-line:before { content: \"\\ec78\"; }\n.ri-earth-fill:before { content: \"\\ec79\"; }\n.ri-earth-line:before { content: \"\\ec7a\"; }\n.ri-earthquake-fill:before { content: \"\\ec7b\"; }\n.ri-earthquake-line:before { content: \"\\ec7c\"; }\n.ri-edge-fill:before { content: \"\\ec7d\"; }\n.ri-edge-line:before { content: \"\\ec7e\"; }\n.ri-edit-2-fill:before { content: \"\\ec7f\"; }\n.ri-edit-2-line:before { content: \"\\ec80\"; }\n.ri-edit-box-fill:before { content: \"\\ec81\"; }\n.ri-edit-box-line:before { content: \"\\ec82\"; }\n.ri-edit-circle-fill:before { content: \"\\ec83\"; }\n.ri-edit-circle-line:before { content: \"\\ec84\"; }\n.ri-edit-fill:before { content: \"\\ec85\"; }\n.ri-edit-line:before { content: \"\\ec86\"; }\n.ri-eject-fill:before { content: \"\\ec87\"; }\n.ri-eject-line:before { content: \"\\ec88\"; }\n.ri-emotion-2-fill:before { content: \"\\ec89\"; }\n.ri-emotion-2-line:before { content: \"\\ec8a\"; }\n.ri-emotion-fill:before { content: \"\\ec8b\"; }\n.ri-emotion-happy-fill:before { content: \"\\ec8c\"; }\n.ri-emotion-happy-line:before { content: \"\\ec8d\"; }\n.ri-emotion-laugh-fill:before { content: \"\\ec8e\"; }\n.ri-emotion-laugh-line:before { content: \"\\ec8f\"; }\n.ri-emotion-line:before { content: \"\\ec90\"; }\n.ri-emotion-normal-fill:before { content: \"\\ec91\"; }\n.ri-emotion-normal-line:before { content: \"\\ec92\"; }\n.ri-emotion-sad-fill:before { content: \"\\ec93\"; }\n.ri-emotion-sad-line:before { content: \"\\ec94\"; }\n.ri-emotion-unhappy-fill:before { content: \"\\ec95\"; }\n.ri-emotion-unhappy-line:before { content: \"\\ec96\"; }\n.ri-empathize-fill:before { content: \"\\ec97\"; }\n.ri-empathize-line:before { content: \"\\ec98\"; }\n.ri-emphasis-cn:before { content: \"\\ec99\"; }\n.ri-emphasis:before { content: \"\\ec9a\"; }\n.ri-english-input:before { content: \"\\ec9b\"; }\n.ri-equalizer-fill:before { content: \"\\ec9c\"; }\n.ri-equalizer-line:before { content: \"\\ec9d\"; }\n.ri-eraser-fill:before { content: \"\\ec9e\"; }\n.ri-eraser-line:before { content: \"\\ec9f\"; }\n.ri-error-warning-fill:before { content: \"\\eca0\"; }\n.ri-error-warning-line:before { content: \"\\eca1\"; }\n.ri-evernote-fill:before { content: \"\\eca2\"; }\n.ri-evernote-line:before { content: \"\\eca3\"; }\n.ri-exchange-box-fill:before { content: \"\\eca4\"; }\n.ri-exchange-box-line:before { content: \"\\eca5\"; }\n.ri-exchange-cny-fill:before { content: \"\\eca6\"; }\n.ri-exchange-cny-line:before { content: \"\\eca7\"; }\n.ri-exchange-dollar-fill:before { content: \"\\eca8\"; }\n.ri-exchange-dollar-line:before { content: \"\\eca9\"; }\n.ri-exchange-fill:before { content: \"\\ecaa\"; }\n.ri-exchange-funds-fill:before { content: \"\\ecab\"; }\n.ri-exchange-funds-line:before { content: \"\\ecac\"; }\n.ri-exchange-line:before { content: \"\\ecad\"; }\n.ri-external-link-fill:before { content: \"\\ecae\"; }\n.ri-external-link-line:before { content: \"\\ecaf\"; }\n.ri-eye-2-fill:before { content: \"\\ecb0\"; }\n.ri-eye-2-line:before { content: \"\\ecb1\"; }\n.ri-eye-close-fill:before { content: \"\\ecb2\"; }\n.ri-eye-close-line:before { content: \"\\ecb3\"; }\n.ri-eye-fill:before { content: \"\\ecb4\"; }\n.ri-eye-line:before { content: \"\\ecb5\"; }\n.ri-eye-off-fill:before { content: \"\\ecb6\"; }\n.ri-eye-off-line:before { content: \"\\ecb7\"; }\n.ri-facebook-box-fill:before { content: \"\\ecb8\"; }\n.ri-facebook-box-line:before { content: \"\\ecb9\"; }\n.ri-facebook-circle-fill:before { content: \"\\ecba\"; }\n.ri-facebook-circle-line:before { content: \"\\ecbb\"; }\n.ri-facebook-fill:before { content: \"\\ecbc\"; }\n.ri-facebook-line:before { content: \"\\ecbd\"; }\n.ri-fahrenheit-fill:before { content: \"\\ecbe\"; }\n.ri-fahrenheit-line:before { content: \"\\ecbf\"; }\n.ri-feedback-fill:before { content: \"\\ecc0\"; }\n.ri-feedback-line:before { content: \"\\ecc1\"; }\n.ri-file-2-fill:before { content: \"\\ecc2\"; }\n.ri-file-2-line:before { content: \"\\ecc3\"; }\n.ri-file-3-fill:before { content: \"\\ecc4\"; }\n.ri-file-3-line:before { content: \"\\ecc5\"; }\n.ri-file-4-fill:before { content: \"\\ecc6\"; }\n.ri-file-4-line:before { content: \"\\ecc7\"; }\n.ri-file-add-fill:before { content: \"\\ecc8\"; }\n.ri-file-add-line:before { content: \"\\ecc9\"; }\n.ri-file-chart-2-fill:before { content: \"\\ecca\"; }\n.ri-file-chart-2-line:before { content: \"\\eccb\"; }\n.ri-file-chart-fill:before { content: \"\\eccc\"; }\n.ri-file-chart-line:before { content: \"\\eccd\"; }\n.ri-file-cloud-fill:before { content: \"\\ecce\"; }\n.ri-file-cloud-line:before { content: \"\\eccf\"; }\n.ri-file-code-fill:before { content: \"\\ecd0\"; }\n.ri-file-code-line:before { content: \"\\ecd1\"; }\n.ri-file-copy-2-fill:before { content: \"\\ecd2\"; }\n.ri-file-copy-2-line:before { content: \"\\ecd3\"; }\n.ri-file-copy-fill:before { content: \"\\ecd4\"; }\n.ri-file-copy-line:before { content: \"\\ecd5\"; }\n.ri-file-damage-fill:before { content: \"\\ecd6\"; }\n.ri-file-damage-line:before { content: \"\\ecd7\"; }\n.ri-file-download-fill:before { content: \"\\ecd8\"; }\n.ri-file-download-line:before { content: \"\\ecd9\"; }\n.ri-file-edit-fill:before { content: \"\\ecda\"; }\n.ri-file-edit-line:before { content: \"\\ecdb\"; }\n.ri-file-excel-2-fill:before { content: \"\\ecdc\"; }\n.ri-file-excel-2-line:before { content: \"\\ecdd\"; }\n.ri-file-excel-fill:before { content: \"\\ecde\"; }\n.ri-file-excel-line:before { content: \"\\ecdf\"; }\n.ri-file-fill:before { content: \"\\ece0\"; }\n.ri-file-forbid-fill:before { content: \"\\ece1\"; }\n.ri-file-forbid-line:before { content: \"\\ece2\"; }\n.ri-file-gif-fill:before { content: \"\\ece3\"; }\n.ri-file-gif-line:before { content: \"\\ece4\"; }\n.ri-file-history-fill:before { content: \"\\ece5\"; }\n.ri-file-history-line:before { content: \"\\ece6\"; }\n.ri-file-hwp-fill:before { content: \"\\ece7\"; }\n.ri-file-hwp-line:before { content: \"\\ece8\"; }\n.ri-file-info-fill:before { content: \"\\ece9\"; }\n.ri-file-info-line:before { content: \"\\ecea\"; }\n.ri-file-line:before { content: \"\\eceb\"; }\n.ri-file-list-2-fill:before { content: \"\\ecec\"; }\n.ri-file-list-2-line:before { content: \"\\eced\"; }\n.ri-file-list-3-fill:before { content: \"\\ecee\"; }\n.ri-file-list-3-line:before { content: \"\\ecef\"; }\n.ri-file-list-fill:before { content: \"\\ecf0\"; }\n.ri-file-list-line:before { content: \"\\ecf1\"; }\n.ri-file-lock-fill:before { content: \"\\ecf2\"; }\n.ri-file-lock-line:before { content: \"\\ecf3\"; }\n.ri-file-mark-fill:before { content: \"\\ecf4\"; }\n.ri-file-mark-line:before { content: \"\\ecf5\"; }\n.ri-file-music-fill:before { content: \"\\ecf6\"; }\n.ri-file-music-line:before { content: \"\\ecf7\"; }\n.ri-file-paper-2-fill:before { content: \"\\ecf8\"; }\n.ri-file-paper-2-line:before { content: \"\\ecf9\"; }\n.ri-file-paper-fill:before { content: \"\\ecfa\"; }\n.ri-file-paper-line:before { content: \"\\ecfb\"; }\n.ri-file-pdf-fill:before { content: \"\\ecfc\"; }\n.ri-file-pdf-line:before { content: \"\\ecfd\"; }\n.ri-file-ppt-2-fill:before { content: \"\\ecfe\"; }\n.ri-file-ppt-2-line:before { content: \"\\ecff\"; }\n.ri-file-ppt-fill:before { content: \"\\ed00\"; }\n.ri-file-ppt-line:before { content: \"\\ed01\"; }\n.ri-file-reduce-fill:before { content: \"\\ed02\"; }\n.ri-file-reduce-line:before { content: \"\\ed03\"; }\n.ri-file-search-fill:before { content: \"\\ed04\"; }\n.ri-file-search-line:before { content: \"\\ed05\"; }\n.ri-file-settings-fill:before { content: \"\\ed06\"; }\n.ri-file-settings-line:before { content: \"\\ed07\"; }\n.ri-file-shield-2-fill:before { content: \"\\ed08\"; }\n.ri-file-shield-2-line:before { content: \"\\ed09\"; }\n.ri-file-shield-fill:before { content: \"\\ed0a\"; }\n.ri-file-shield-line:before { content: \"\\ed0b\"; }\n.ri-file-shred-fill:before { content: \"\\ed0c\"; }\n.ri-file-shred-line:before { content: \"\\ed0d\"; }\n.ri-file-text-fill:before { content: \"\\ed0e\"; }\n.ri-file-text-line:before { content: \"\\ed0f\"; }\n.ri-file-transfer-fill:before { content: \"\\ed10\"; }\n.ri-file-transfer-line:before { content: \"\\ed11\"; }\n.ri-file-unknow-fill:before { content: \"\\ed12\"; }\n.ri-file-unknow-line:before { content: \"\\ed13\"; }\n.ri-file-upload-fill:before { content: \"\\ed14\"; }\n.ri-file-upload-line:before { content: \"\\ed15\"; }\n.ri-file-user-fill:before { content: \"\\ed16\"; }\n.ri-file-user-line:before { content: \"\\ed17\"; }\n.ri-file-warning-fill:before { content: \"\\ed18\"; }\n.ri-file-warning-line:before { content: \"\\ed19\"; }\n.ri-file-word-2-fill:before { content: \"\\ed1a\"; }\n.ri-file-word-2-line:before { content: \"\\ed1b\"; }\n.ri-file-word-fill:before { content: \"\\ed1c\"; }\n.ri-file-word-line:before { content: \"\\ed1d\"; }\n.ri-file-zip-fill:before { content: \"\\ed1e\"; }\n.ri-file-zip-line:before { content: \"\\ed1f\"; }\n.ri-film-fill:before { content: \"\\ed20\"; }\n.ri-film-line:before { content: \"\\ed21\"; }\n.ri-filter-2-fill:before { content: \"\\ed22\"; }\n.ri-filter-2-line:before { content: \"\\ed23\"; }\n.ri-filter-3-fill:before { content: \"\\ed24\"; }\n.ri-filter-3-line:before { content: \"\\ed25\"; }\n.ri-filter-fill:before { content: \"\\ed26\"; }\n.ri-filter-line:before { content: \"\\ed27\"; }\n.ri-filter-off-fill:before { content: \"\\ed28\"; }\n.ri-filter-off-line:before { content: \"\\ed29\"; }\n.ri-find-replace-fill:before { content: \"\\ed2a\"; }\n.ri-find-replace-line:before { content: \"\\ed2b\"; }\n.ri-finder-fill:before { content: \"\\ed2c\"; }\n.ri-finder-line:before { content: \"\\ed2d\"; }\n.ri-fingerprint-2-fill:before { content: \"\\ed2e\"; }\n.ri-fingerprint-2-line:before { content: \"\\ed2f\"; }\n.ri-fingerprint-fill:before { content: \"\\ed30\"; }\n.ri-fingerprint-line:before { content: \"\\ed31\"; }\n.ri-fire-fill:before { content: \"\\ed32\"; }\n.ri-fire-line:before { content: \"\\ed33\"; }\n.ri-firefox-fill:before { content: \"\\ed34\"; }\n.ri-firefox-line:before { content: \"\\ed35\"; }\n.ri-first-aid-kit-fill:before { content: \"\\ed36\"; }\n.ri-first-aid-kit-line:before { content: \"\\ed37\"; }\n.ri-flag-2-fill:before { content: \"\\ed38\"; }\n.ri-flag-2-line:before { content: \"\\ed39\"; }\n.ri-flag-fill:before { content: \"\\ed3a\"; }\n.ri-flag-line:before { content: \"\\ed3b\"; }\n.ri-flashlight-fill:before { content: \"\\ed3c\"; }\n.ri-flashlight-line:before { content: \"\\ed3d\"; }\n.ri-flask-fill:before { content: \"\\ed3e\"; }\n.ri-flask-line:before { content: \"\\ed3f\"; }\n.ri-flight-land-fill:before { content: \"\\ed40\"; }\n.ri-flight-land-line:before { content: \"\\ed41\"; }\n.ri-flight-takeoff-fill:before { content: \"\\ed42\"; }\n.ri-flight-takeoff-line:before { content: \"\\ed43\"; }\n.ri-flood-fill:before { content: \"\\ed44\"; }\n.ri-flood-line:before { content: \"\\ed45\"; }\n.ri-flow-chart:before { content: \"\\ed46\"; }\n.ri-flutter-fill:before { content: \"\\ed47\"; }\n.ri-flutter-line:before { content: \"\\ed48\"; }\n.ri-focus-2-fill:before { content: \"\\ed49\"; }\n.ri-focus-2-line:before { content: \"\\ed4a\"; }\n.ri-focus-3-fill:before { content: \"\\ed4b\"; }\n.ri-focus-3-line:before { content: \"\\ed4c\"; }\n.ri-focus-fill:before { content: \"\\ed4d\"; }\n.ri-focus-line:before { content: \"\\ed4e\"; }\n.ri-foggy-fill:before { content: \"\\ed4f\"; }\n.ri-foggy-line:before { content: \"\\ed50\"; }\n.ri-folder-2-fill:before { content: \"\\ed51\"; }\n.ri-folder-2-line:before { content: \"\\ed52\"; }\n.ri-folder-3-fill:before { content: \"\\ed53\"; }\n.ri-folder-3-line:before { content: \"\\ed54\"; }\n.ri-folder-4-fill:before { content: \"\\ed55\"; }\n.ri-folder-4-line:before { content: \"\\ed56\"; }\n.ri-folder-5-fill:before { content: \"\\ed57\"; }\n.ri-folder-5-line:before { content: \"\\ed58\"; }\n.ri-folder-add-fill:before { content: \"\\ed59\"; }\n.ri-folder-add-line:before { content: \"\\ed5a\"; }\n.ri-folder-chart-2-fill:before { content: \"\\ed5b\"; }\n.ri-folder-chart-2-line:before { content: \"\\ed5c\"; }\n.ri-folder-chart-fill:before { content: \"\\ed5d\"; }\n.ri-folder-chart-line:before { content: \"\\ed5e\"; }\n.ri-folder-download-fill:before { content: \"\\ed5f\"; }\n.ri-folder-download-line:before { content: \"\\ed60\"; }\n.ri-folder-fill:before { content: \"\\ed61\"; }\n.ri-folder-forbid-fill:before { content: \"\\ed62\"; }\n.ri-folder-forbid-line:before { content: \"\\ed63\"; }\n.ri-folder-history-fill:before { content: \"\\ed64\"; }\n.ri-folder-history-line:before { content: \"\\ed65\"; }\n.ri-folder-info-fill:before { content: \"\\ed66\"; }\n.ri-folder-info-line:before { content: \"\\ed67\"; }\n.ri-folder-keyhole-fill:before { content: \"\\ed68\"; }\n.ri-folder-keyhole-line:before { content: \"\\ed69\"; }\n.ri-folder-line:before { content: \"\\ed6a\"; }\n.ri-folder-lock-fill:before { content: \"\\ed6b\"; }\n.ri-folder-lock-line:before { content: \"\\ed6c\"; }\n.ri-folder-music-fill:before { content: \"\\ed6d\"; }\n.ri-folder-music-line:before { content: \"\\ed6e\"; }\n.ri-folder-open-fill:before { content: \"\\ed6f\"; }\n.ri-folder-open-line:before { content: \"\\ed70\"; }\n.ri-folder-received-fill:before { content: \"\\ed71\"; }\n.ri-folder-received-line:before { content: \"\\ed72\"; }\n.ri-folder-reduce-fill:before { content: \"\\ed73\"; }\n.ri-folder-reduce-line:before { content: \"\\ed74\"; }\n.ri-folder-settings-fill:before { content: \"\\ed75\"; }\n.ri-folder-settings-line:before { content: \"\\ed76\"; }\n.ri-folder-shared-fill:before { content: \"\\ed77\"; }\n.ri-folder-shared-line:before { content: \"\\ed78\"; }\n.ri-folder-shield-2-fill:before { content: \"\\ed79\"; }\n.ri-folder-shield-2-line:before { content: \"\\ed7a\"; }\n.ri-folder-shield-fill:before { content: \"\\ed7b\"; }\n.ri-folder-shield-line:before { content: \"\\ed7c\"; }\n.ri-folder-transfer-fill:before { content: \"\\ed7d\"; }\n.ri-folder-transfer-line:before { content: \"\\ed7e\"; }\n.ri-folder-unknow-fill:before { content: \"\\ed7f\"; }\n.ri-folder-unknow-line:before { content: \"\\ed80\"; }\n.ri-folder-upload-fill:before { content: \"\\ed81\"; }\n.ri-folder-upload-line:before { content: \"\\ed82\"; }\n.ri-folder-user-fill:before { content: \"\\ed83\"; }\n.ri-folder-user-line:before { content: \"\\ed84\"; }\n.ri-folder-warning-fill:before { content: \"\\ed85\"; }\n.ri-folder-warning-line:before { content: \"\\ed86\"; }\n.ri-folder-zip-fill:before { content: \"\\ed87\"; }\n.ri-folder-zip-line:before { content: \"\\ed88\"; }\n.ri-folders-fill:before { content: \"\\ed89\"; }\n.ri-folders-line:before { content: \"\\ed8a\"; }\n.ri-font-color:before { content: \"\\ed8b\"; }\n.ri-font-size-2:before { content: \"\\ed8c\"; }\n.ri-font-size:before { content: \"\\ed8d\"; }\n.ri-football-fill:before { content: \"\\ed8e\"; }\n.ri-football-line:before { content: \"\\ed8f\"; }\n.ri-footprint-fill:before { content: \"\\ed90\"; }\n.ri-footprint-line:before { content: \"\\ed91\"; }\n.ri-forbid-2-fill:before { content: \"\\ed92\"; }\n.ri-forbid-2-line:before { content: \"\\ed93\"; }\n.ri-forbid-fill:before { content: \"\\ed94\"; }\n.ri-forbid-line:before { content: \"\\ed95\"; }\n.ri-format-clear:before { content: \"\\ed96\"; }\n.ri-fridge-fill:before { content: \"\\ed97\"; }\n.ri-fridge-line:before { content: \"\\ed98\"; }\n.ri-fullscreen-exit-fill:before { content: \"\\ed99\"; }\n.ri-fullscreen-exit-line:before { content: \"\\ed9a\"; }\n.ri-fullscreen-fill:before { content: \"\\ed9b\"; }\n.ri-fullscreen-line:before { content: \"\\ed9c\"; }\n.ri-function-fill:before { content: \"\\ed9d\"; }\n.ri-function-line:before { content: \"\\ed9e\"; }\n.ri-functions:before { content: \"\\ed9f\"; }\n.ri-funds-box-fill:before { content: \"\\eda0\"; }\n.ri-funds-box-line:before { content: \"\\eda1\"; }\n.ri-funds-fill:before { content: \"\\eda2\"; }\n.ri-funds-line:before { content: \"\\eda3\"; }\n.ri-gallery-fill:before { content: \"\\eda4\"; }\n.ri-gallery-line:before { content: \"\\eda5\"; }\n.ri-gallery-upload-fill:before { content: \"\\eda6\"; }\n.ri-gallery-upload-line:before { content: \"\\eda7\"; }\n.ri-game-fill:before { content: \"\\eda8\"; }\n.ri-game-line:before { content: \"\\eda9\"; }\n.ri-gamepad-fill:before { content: \"\\edaa\"; }\n.ri-gamepad-line:before { content: \"\\edab\"; }\n.ri-gas-station-fill:before { content: \"\\edac\"; }\n.ri-gas-station-line:before { content: \"\\edad\"; }\n.ri-gatsby-fill:before { content: \"\\edae\"; }\n.ri-gatsby-line:before { content: \"\\edaf\"; }\n.ri-genderless-fill:before { content: \"\\edb0\"; }\n.ri-genderless-line:before { content: \"\\edb1\"; }\n.ri-ghost-2-fill:before { content: \"\\edb2\"; }\n.ri-ghost-2-line:before { content: \"\\edb3\"; }\n.ri-ghost-fill:before { content: \"\\edb4\"; }\n.ri-ghost-line:before { content: \"\\edb5\"; }\n.ri-ghost-smile-fill:before { content: \"\\edb6\"; }\n.ri-ghost-smile-line:before { content: \"\\edb7\"; }\n.ri-gift-2-fill:before { content: \"\\edb8\"; }\n.ri-gift-2-line:before { content: \"\\edb9\"; }\n.ri-gift-fill:before { content: \"\\edba\"; }\n.ri-gift-line:before { content: \"\\edbb\"; }\n.ri-git-branch-fill:before { content: \"\\edbc\"; }\n.ri-git-branch-line:before { content: \"\\edbd\"; }\n.ri-git-commit-fill:before { content: \"\\edbe\"; }\n.ri-git-commit-line:before { content: \"\\edbf\"; }\n.ri-git-merge-fill:before { content: \"\\edc0\"; }\n.ri-git-merge-line:before { content: \"\\edc1\"; }\n.ri-git-pull-request-fill:before { content: \"\\edc2\"; }\n.ri-git-pull-request-line:before { content: \"\\edc3\"; }\n.ri-git-repository-commits-fill:before { content: \"\\edc4\"; }\n.ri-git-repository-commits-line:before { content: \"\\edc5\"; }\n.ri-git-repository-fill:before { content: \"\\edc6\"; }\n.ri-git-repository-line:before { content: \"\\edc7\"; }\n.ri-git-repository-private-fill:before { content: \"\\edc8\"; }\n.ri-git-repository-private-line:before { content: \"\\edc9\"; }\n.ri-github-fill:before { content: \"\\edca\"; }\n.ri-github-line:before { content: \"\\edcb\"; }\n.ri-gitlab-fill:before { content: \"\\edcc\"; }\n.ri-gitlab-line:before { content: \"\\edcd\"; }\n.ri-global-fill:before { content: \"\\edce\"; }\n.ri-global-line:before { content: \"\\edcf\"; }\n.ri-globe-fill:before { content: \"\\edd0\"; }\n.ri-globe-line:before { content: \"\\edd1\"; }\n.ri-goblet-fill:before { content: \"\\edd2\"; }\n.ri-goblet-line:before { content: \"\\edd3\"; }\n.ri-google-fill:before { content: \"\\edd4\"; }\n.ri-google-line:before { content: \"\\edd5\"; }\n.ri-google-play-fill:before { content: \"\\edd6\"; }\n.ri-google-play-line:before { content: \"\\edd7\"; }\n.ri-government-fill:before { content: \"\\edd8\"; }\n.ri-government-line:before { content: \"\\edd9\"; }\n.ri-gps-fill:before { content: \"\\edda\"; }\n.ri-gps-line:before { content: \"\\eddb\"; }\n.ri-gradienter-fill:before { content: \"\\eddc\"; }\n.ri-gradienter-line:before { content: \"\\eddd\"; }\n.ri-grid-fill:before { content: \"\\edde\"; }\n.ri-grid-line:before { content: \"\\eddf\"; }\n.ri-group-2-fill:before { content: \"\\ede0\"; }\n.ri-group-2-line:before { content: \"\\ede1\"; }\n.ri-group-fill:before { content: \"\\ede2\"; }\n.ri-group-line:before { content: \"\\ede3\"; }\n.ri-guide-fill:before { content: \"\\ede4\"; }\n.ri-guide-line:before { content: \"\\ede5\"; }\n.ri-h-1:before { content: \"\\ede6\"; }\n.ri-h-2:before { content: \"\\ede7\"; }\n.ri-h-3:before { content: \"\\ede8\"; }\n.ri-h-4:before { content: \"\\ede9\"; }\n.ri-h-5:before { content: \"\\edea\"; }\n.ri-h-6:before { content: \"\\edeb\"; }\n.ri-hail-fill:before { content: \"\\edec\"; }\n.ri-hail-line:before { content: \"\\eded\"; }\n.ri-hammer-fill:before { content: \"\\edee\"; }\n.ri-hammer-line:before { content: \"\\edef\"; }\n.ri-hand-coin-fill:before { content: \"\\edf0\"; }\n.ri-hand-coin-line:before { content: \"\\edf1\"; }\n.ri-hand-heart-fill:before { content: \"\\edf2\"; }\n.ri-hand-heart-line:before { content: \"\\edf3\"; }\n.ri-hand-sanitizer-fill:before { content: \"\\edf4\"; }\n.ri-hand-sanitizer-line:before { content: \"\\edf5\"; }\n.ri-handbag-fill:before { content: \"\\edf6\"; }\n.ri-handbag-line:before { content: \"\\edf7\"; }\n.ri-hard-drive-2-fill:before { content: \"\\edf8\"; }\n.ri-hard-drive-2-line:before { content: \"\\edf9\"; }\n.ri-hard-drive-fill:before { content: \"\\edfa\"; }\n.ri-hard-drive-line:before { content: \"\\edfb\"; }\n.ri-hashtag:before { content: \"\\edfc\"; }\n.ri-haze-2-fill:before { content: \"\\edfd\"; }\n.ri-haze-2-line:before { content: \"\\edfe\"; }\n.ri-haze-fill:before { content: \"\\edff\"; }\n.ri-haze-line:before { content: \"\\ee00\"; }\n.ri-hd-fill:before { content: \"\\ee01\"; }\n.ri-hd-line:before { content: \"\\ee02\"; }\n.ri-heading:before { content: \"\\ee03\"; }\n.ri-headphone-fill:before { content: \"\\ee04\"; }\n.ri-headphone-line:before { content: \"\\ee05\"; }\n.ri-health-book-fill:before { content: \"\\ee06\"; }\n.ri-health-book-line:before { content: \"\\ee07\"; }\n.ri-heart-2-fill:before { content: \"\\ee08\"; }\n.ri-heart-2-line:before { content: \"\\ee09\"; }\n.ri-heart-3-fill:before { content: \"\\ee0a\"; }\n.ri-heart-3-line:before { content: \"\\ee0b\"; }\n.ri-heart-add-fill:before { content: \"\\ee0c\"; }\n.ri-heart-add-line:before { content: \"\\ee0d\"; }\n.ri-heart-fill:before { content: \"\\ee0e\"; }\n.ri-heart-line:before { content: \"\\ee0f\"; }\n.ri-heart-pulse-fill:before { content: \"\\ee10\"; }\n.ri-heart-pulse-line:before { content: \"\\ee11\"; }\n.ri-hearts-fill:before { content: \"\\ee12\"; }\n.ri-hearts-line:before { content: \"\\ee13\"; }\n.ri-heavy-showers-fill:before { content: \"\\ee14\"; }\n.ri-heavy-showers-line:before { content: \"\\ee15\"; }\n.ri-history-fill:before { content: \"\\ee16\"; }\n.ri-history-line:before { content: \"\\ee17\"; }\n.ri-home-2-fill:before { content: \"\\ee18\"; }\n.ri-home-2-line:before { content: \"\\ee19\"; }\n.ri-home-3-fill:before { content: \"\\ee1a\"; }\n.ri-home-3-line:before { content: \"\\ee1b\"; }\n.ri-home-4-fill:before { content: \"\\ee1c\"; }\n.ri-home-4-line:before { content: \"\\ee1d\"; }\n.ri-home-5-fill:before { content: \"\\ee1e\"; }\n.ri-home-5-line:before { content: \"\\ee1f\"; }\n.ri-home-6-fill:before { content: \"\\ee20\"; }\n.ri-home-6-line:before { content: \"\\ee21\"; }\n.ri-home-7-fill:before { content: \"\\ee22\"; }\n.ri-home-7-line:before { content: \"\\ee23\"; }\n.ri-home-8-fill:before { content: \"\\ee24\"; }\n.ri-home-8-line:before { content: \"\\ee25\"; }\n.ri-home-fill:before { content: \"\\ee26\"; }\n.ri-home-gear-fill:before { content: \"\\ee27\"; }\n.ri-home-gear-line:before { content: \"\\ee28\"; }\n.ri-home-heart-fill:before { content: \"\\ee29\"; }\n.ri-home-heart-line:before { content: \"\\ee2a\"; }\n.ri-home-line:before { content: \"\\ee2b\"; }\n.ri-home-smile-2-fill:before { content: \"\\ee2c\"; }\n.ri-home-smile-2-line:before { content: \"\\ee2d\"; }\n.ri-home-smile-fill:before { content: \"\\ee2e\"; }\n.ri-home-smile-line:before { content: \"\\ee2f\"; }\n.ri-home-wifi-fill:before { content: \"\\ee30\"; }\n.ri-home-wifi-line:before { content: \"\\ee31\"; }\n.ri-honor-of-kings-fill:before { content: \"\\ee32\"; }\n.ri-honor-of-kings-line:before { content: \"\\ee33\"; }\n.ri-honour-fill:before { content: \"\\ee34\"; }\n.ri-honour-line:before { content: \"\\ee35\"; }\n.ri-hospital-fill:before { content: \"\\ee36\"; }\n.ri-hospital-line:before { content: \"\\ee37\"; }\n.ri-hotel-bed-fill:before { content: \"\\ee38\"; }\n.ri-hotel-bed-line:before { content: \"\\ee39\"; }\n.ri-hotel-fill:before { content: \"\\ee3a\"; }\n.ri-hotel-line:before { content: \"\\ee3b\"; }\n.ri-hotspot-fill:before { content: \"\\ee3c\"; }\n.ri-hotspot-line:before { content: \"\\ee3d\"; }\n.ri-hq-fill:before { content: \"\\ee3e\"; }\n.ri-hq-line:before { content: \"\\ee3f\"; }\n.ri-html5-fill:before { content: \"\\ee40\"; }\n.ri-html5-line:before { content: \"\\ee41\"; }\n.ri-ie-fill:before { content: \"\\ee42\"; }\n.ri-ie-line:before { content: \"\\ee43\"; }\n.ri-image-2-fill:before { content: \"\\ee44\"; }\n.ri-image-2-line:before { content: \"\\ee45\"; }\n.ri-image-add-fill:before { content: \"\\ee46\"; }\n.ri-image-add-line:before { content: \"\\ee47\"; }\n.ri-image-edit-fill:before { content: \"\\ee48\"; }\n.ri-image-edit-line:before { content: \"\\ee49\"; }\n.ri-image-fill:before { content: \"\\ee4a\"; }\n.ri-image-line:before { content: \"\\ee4b\"; }\n.ri-inbox-archive-fill:before { content: \"\\ee4c\"; }\n.ri-inbox-archive-line:before { content: \"\\ee4d\"; }\n.ri-inbox-fill:before { content: \"\\ee4e\"; }\n.ri-inbox-line:before { content: \"\\ee4f\"; }\n.ri-inbox-unarchive-fill:before { content: \"\\ee50\"; }\n.ri-inbox-unarchive-line:before { content: \"\\ee51\"; }\n.ri-increase-decrease-fill:before { content: \"\\ee52\"; }\n.ri-increase-decrease-line:before { content: \"\\ee53\"; }\n.ri-indent-decrease:before { content: \"\\ee54\"; }\n.ri-indent-increase:before { content: \"\\ee55\"; }\n.ri-indeterminate-circle-fill:before { content: \"\\ee56\"; }\n.ri-indeterminate-circle-line:before { content: \"\\ee57\"; }\n.ri-information-fill:before { content: \"\\ee58\"; }\n.ri-information-line:before { content: \"\\ee59\"; }\n.ri-infrared-thermometer-fill:before { content: \"\\ee5a\"; }\n.ri-infrared-thermometer-line:before { content: \"\\ee5b\"; }\n.ri-ink-bottle-fill:before { content: \"\\ee5c\"; }\n.ri-ink-bottle-line:before { content: \"\\ee5d\"; }\n.ri-input-cursor-move:before { content: \"\\ee5e\"; }\n.ri-input-method-fill:before { content: \"\\ee5f\"; }\n.ri-input-method-line:before { content: \"\\ee60\"; }\n.ri-insert-column-left:before { content: \"\\ee61\"; }\n.ri-insert-column-right:before { content: \"\\ee62\"; }\n.ri-insert-row-bottom:before { content: \"\\ee63\"; }\n.ri-insert-row-top:before { content: \"\\ee64\"; }\n.ri-instagram-fill:before { content: \"\\ee65\"; }\n.ri-instagram-line:before { content: \"\\ee66\"; }\n.ri-install-fill:before { content: \"\\ee67\"; }\n.ri-install-line:before { content: \"\\ee68\"; }\n.ri-invision-fill:before { content: \"\\ee69\"; }\n.ri-invision-line:before { content: \"\\ee6a\"; }\n.ri-italic:before { content: \"\\ee6b\"; }\n.ri-kakao-talk-fill:before { content: \"\\ee6c\"; }\n.ri-kakao-talk-line:before { content: \"\\ee6d\"; }\n.ri-key-2-fill:before { content: \"\\ee6e\"; }\n.ri-key-2-line:before { content: \"\\ee6f\"; }\n.ri-key-fill:before { content: \"\\ee70\"; }\n.ri-key-line:before { content: \"\\ee71\"; }\n.ri-keyboard-box-fill:before { content: \"\\ee72\"; }\n.ri-keyboard-box-line:before { content: \"\\ee73\"; }\n.ri-keyboard-fill:before { content: \"\\ee74\"; }\n.ri-keyboard-line:before { content: \"\\ee75\"; }\n.ri-keynote-fill:before { content: \"\\ee76\"; }\n.ri-keynote-line:before { content: \"\\ee77\"; }\n.ri-knife-blood-fill:before { content: \"\\ee78\"; }\n.ri-knife-blood-line:before { content: \"\\ee79\"; }\n.ri-knife-fill:before { content: \"\\ee7a\"; }\n.ri-knife-line:before { content: \"\\ee7b\"; }\n.ri-landscape-fill:before { content: \"\\ee7c\"; }\n.ri-landscape-line:before { content: \"\\ee7d\"; }\n.ri-layout-2-fill:before { content: \"\\ee7e\"; }\n.ri-layout-2-line:before { content: \"\\ee7f\"; }\n.ri-layout-3-fill:before { content: \"\\ee80\"; }\n.ri-layout-3-line:before { content: \"\\ee81\"; }\n.ri-layout-4-fill:before { content: \"\\ee82\"; }\n.ri-layout-4-line:before { content: \"\\ee83\"; }\n.ri-layout-5-fill:before { content: \"\\ee84\"; }\n.ri-layout-5-line:before { content: \"\\ee85\"; }\n.ri-layout-6-fill:before { content: \"\\ee86\"; }\n.ri-layout-6-line:before { content: \"\\ee87\"; }\n.ri-layout-bottom-2-fill:before { content: \"\\ee88\"; }\n.ri-layout-bottom-2-line:before { content: \"\\ee89\"; }\n.ri-layout-bottom-fill:before { content: \"\\ee8a\"; }\n.ri-layout-bottom-line:before { content: \"\\ee8b\"; }\n.ri-layout-column-fill:before { content: \"\\ee8c\"; }\n.ri-layout-column-line:before { content: \"\\ee8d\"; }\n.ri-layout-fill:before { content: \"\\ee8e\"; }\n.ri-layout-grid-fill:before { content: \"\\ee8f\"; }\n.ri-layout-grid-line:before { content: \"\\ee90\"; }\n.ri-layout-left-2-fill:before { content: \"\\ee91\"; }\n.ri-layout-left-2-line:before { content: \"\\ee92\"; }\n.ri-layout-left-fill:before { content: \"\\ee93\"; }\n.ri-layout-left-line:before { content: \"\\ee94\"; }\n.ri-layout-line:before { content: \"\\ee95\"; }\n.ri-layout-masonry-fill:before { content: \"\\ee96\"; }\n.ri-layout-masonry-line:before { content: \"\\ee97\"; }\n.ri-layout-right-2-fill:before { content: \"\\ee98\"; }\n.ri-layout-right-2-line:before { content: \"\\ee99\"; }\n.ri-layout-right-fill:before { content: \"\\ee9a\"; }\n.ri-layout-right-line:before { content: \"\\ee9b\"; }\n.ri-layout-row-fill:before { content: \"\\ee9c\"; }\n.ri-layout-row-line:before { content: \"\\ee9d\"; }\n.ri-layout-top-2-fill:before { content: \"\\ee9e\"; }\n.ri-layout-top-2-line:before { content: \"\\ee9f\"; }\n.ri-layout-top-fill:before { content: \"\\eea0\"; }\n.ri-layout-top-line:before { content: \"\\eea1\"; }\n.ri-leaf-fill:before { content: \"\\eea2\"; }\n.ri-leaf-line:before { content: \"\\eea3\"; }\n.ri-lifebuoy-fill:before { content: \"\\eea4\"; }\n.ri-lifebuoy-line:before { content: \"\\eea5\"; }\n.ri-lightbulb-fill:before { content: \"\\eea6\"; }\n.ri-lightbulb-flash-fill:before { content: \"\\eea7\"; }\n.ri-lightbulb-flash-line:before { content: \"\\eea8\"; }\n.ri-lightbulb-line:before { content: \"\\eea9\"; }\n.ri-line-chart-fill:before { content: \"\\eeaa\"; }\n.ri-line-chart-line:before { content: \"\\eeab\"; }\n.ri-line-fill:before { content: \"\\eeac\"; }\n.ri-line-height:before { content: \"\\eead\"; }\n.ri-line-line:before { content: \"\\eeae\"; }\n.ri-link-m:before { content: \"\\eeaf\"; }\n.ri-link-unlink-m:before { content: \"\\eeb0\"; }\n.ri-link-unlink:before { content: \"\\eeb1\"; }\n.ri-link:before { content: \"\\eeb2\"; }\n.ri-linkedin-box-fill:before { content: \"\\eeb3\"; }\n.ri-linkedin-box-line:before { content: \"\\eeb4\"; }\n.ri-linkedin-fill:before { content: \"\\eeb5\"; }\n.ri-linkedin-line:before { content: \"\\eeb6\"; }\n.ri-links-fill:before { content: \"\\eeb7\"; }\n.ri-links-line:before { content: \"\\eeb8\"; }\n.ri-list-check-2:before { content: \"\\eeb9\"; }\n.ri-list-check:before { content: \"\\eeba\"; }\n.ri-list-ordered:before { content: \"\\eebb\"; }\n.ri-list-settings-fill:before { content: \"\\eebc\"; }\n.ri-list-settings-line:before { content: \"\\eebd\"; }\n.ri-list-unordered:before { content: \"\\eebe\"; }\n.ri-live-fill:before { content: \"\\eebf\"; }\n.ri-live-line:before { content: \"\\eec0\"; }\n.ri-loader-2-fill:before { content: \"\\eec1\"; }\n.ri-loader-2-line:before { content: \"\\eec2\"; }\n.ri-loader-3-fill:before { content: \"\\eec3\"; }\n.ri-loader-3-line:before { content: \"\\eec4\"; }\n.ri-loader-4-fill:before { content: \"\\eec5\"; }\n.ri-loader-4-line:before { content: \"\\eec6\"; }\n.ri-loader-5-fill:before { content: \"\\eec7\"; }\n.ri-loader-5-line:before { content: \"\\eec8\"; }\n.ri-loader-fill:before { content: \"\\eec9\"; }\n.ri-loader-line:before { content: \"\\eeca\"; }\n.ri-lock-2-fill:before { content: \"\\eecb\"; }\n.ri-lock-2-line:before { content: \"\\eecc\"; }\n.ri-lock-fill:before { content: \"\\eecd\"; }\n.ri-lock-line:before { content: \"\\eece\"; }\n.ri-lock-password-fill:before { content: \"\\eecf\"; }\n.ri-lock-password-line:before { content: \"\\eed0\"; }\n.ri-lock-unlock-fill:before { content: \"\\eed1\"; }\n.ri-lock-unlock-line:before { content: \"\\eed2\"; }\n.ri-login-box-fill:before { content: \"\\eed3\"; }\n.ri-login-box-line:before { content: \"\\eed4\"; }\n.ri-login-circle-fill:before { content: \"\\eed5\"; }\n.ri-login-circle-line:before { content: \"\\eed6\"; }\n.ri-logout-box-fill:before { content: \"\\eed7\"; }\n.ri-logout-box-line:before { content: \"\\eed8\"; }\n.ri-logout-box-r-fill:before { content: \"\\eed9\"; }\n.ri-logout-box-r-line:before { content: \"\\eeda\"; }\n.ri-logout-circle-fill:before { content: \"\\eedb\"; }\n.ri-logout-circle-line:before { content: \"\\eedc\"; }\n.ri-logout-circle-r-fill:before { content: \"\\eedd\"; }\n.ri-logout-circle-r-line:before { content: \"\\eede\"; }\n.ri-luggage-cart-fill:before { content: \"\\eedf\"; }\n.ri-luggage-cart-line:before { content: \"\\eee0\"; }\n.ri-luggage-deposit-fill:before { content: \"\\eee1\"; }\n.ri-luggage-deposit-line:before { content: \"\\eee2\"; }\n.ri-lungs-fill:before { content: \"\\eee3\"; }\n.ri-lungs-line:before { content: \"\\eee4\"; }\n.ri-mac-fill:before { content: \"\\eee5\"; }\n.ri-mac-line:before { content: \"\\eee6\"; }\n.ri-macbook-fill:before { content: \"\\eee7\"; }\n.ri-macbook-line:before { content: \"\\eee8\"; }\n.ri-magic-fill:before { content: \"\\eee9\"; }\n.ri-magic-line:before { content: \"\\eeea\"; }\n.ri-mail-add-fill:before { content: \"\\eeeb\"; }\n.ri-mail-add-line:before { content: \"\\eeec\"; }\n.ri-mail-check-fill:before { content: \"\\eeed\"; }\n.ri-mail-check-line:before { content: \"\\eeee\"; }\n.ri-mail-close-fill:before { content: \"\\eeef\"; }\n.ri-mail-close-line:before { content: \"\\eef0\"; }\n.ri-mail-download-fill:before { content: \"\\eef1\"; }\n.ri-mail-download-line:before { content: \"\\eef2\"; }\n.ri-mail-fill:before { content: \"\\eef3\"; }\n.ri-mail-forbid-fill:before { content: \"\\eef4\"; }\n.ri-mail-forbid-line:before { content: \"\\eef5\"; }\n.ri-mail-line:before { content: \"\\eef6\"; }\n.ri-mail-lock-fill:before { content: \"\\eef7\"; }\n.ri-mail-lock-line:before { content: \"\\eef8\"; }\n.ri-mail-open-fill:before { content: \"\\eef9\"; }\n.ri-mail-open-line:before { content: \"\\eefa\"; }\n.ri-mail-send-fill:before { content: \"\\eefb\"; }\n.ri-mail-send-line:before { content: \"\\eefc\"; }\n.ri-mail-settings-fill:before { content: \"\\eefd\"; }\n.ri-mail-settings-line:before { content: \"\\eefe\"; }\n.ri-mail-star-fill:before { content: \"\\eeff\"; }\n.ri-mail-star-line:before { content: \"\\ef00\"; }\n.ri-mail-unread-fill:before { content: \"\\ef01\"; }\n.ri-mail-unread-line:before { content: \"\\ef02\"; }\n.ri-mail-volume-fill:before { content: \"\\ef03\"; }\n.ri-mail-volume-line:before { content: \"\\ef04\"; }\n.ri-map-2-fill:before { content: \"\\ef05\"; }\n.ri-map-2-line:before { content: \"\\ef06\"; }\n.ri-map-fill:before { content: \"\\ef07\"; }\n.ri-map-line:before { content: \"\\ef08\"; }\n.ri-map-pin-2-fill:before { content: \"\\ef09\"; }\n.ri-map-pin-2-line:before { content: \"\\ef0a\"; }\n.ri-map-pin-3-fill:before { content: \"\\ef0b\"; }\n.ri-map-pin-3-line:before { content: \"\\ef0c\"; }\n.ri-map-pin-4-fill:before { content: \"\\ef0d\"; }\n.ri-map-pin-4-line:before { content: \"\\ef0e\"; }\n.ri-map-pin-5-fill:before { content: \"\\ef0f\"; }\n.ri-map-pin-5-line:before { content: \"\\ef10\"; }\n.ri-map-pin-add-fill:before { content: \"\\ef11\"; }\n.ri-map-pin-add-line:before { content: \"\\ef12\"; }\n.ri-map-pin-fill:before { content: \"\\ef13\"; }\n.ri-map-pin-line:before { content: \"\\ef14\"; }\n.ri-map-pin-range-fill:before { content: \"\\ef15\"; }\n.ri-map-pin-range-line:before { content: \"\\ef16\"; }\n.ri-map-pin-time-fill:before { content: \"\\ef17\"; }\n.ri-map-pin-time-line:before { content: \"\\ef18\"; }\n.ri-map-pin-user-fill:before { content: \"\\ef19\"; }\n.ri-map-pin-user-line:before { content: \"\\ef1a\"; }\n.ri-mark-pen-fill:before { content: \"\\ef1b\"; }\n.ri-mark-pen-line:before { content: \"\\ef1c\"; }\n.ri-markdown-fill:before { content: \"\\ef1d\"; }\n.ri-markdown-line:before { content: \"\\ef1e\"; }\n.ri-markup-fill:before { content: \"\\ef1f\"; }\n.ri-markup-line:before { content: \"\\ef20\"; }\n.ri-mastercard-fill:before { content: \"\\ef21\"; }\n.ri-mastercard-line:before { content: \"\\ef22\"; }\n.ri-mastodon-fill:before { content: \"\\ef23\"; }\n.ri-mastodon-line:before { content: \"\\ef24\"; }\n.ri-medal-2-fill:before { content: \"\\ef25\"; }\n.ri-medal-2-line:before { content: \"\\ef26\"; }\n.ri-medal-fill:before { content: \"\\ef27\"; }\n.ri-medal-line:before { content: \"\\ef28\"; }\n.ri-medicine-bottle-fill:before { content: \"\\ef29\"; }\n.ri-medicine-bottle-line:before { content: \"\\ef2a\"; }\n.ri-medium-fill:before { content: \"\\ef2b\"; }\n.ri-medium-line:before { content: \"\\ef2c\"; }\n.ri-men-fill:before { content: \"\\ef2d\"; }\n.ri-men-line:before { content: \"\\ef2e\"; }\n.ri-mental-health-fill:before { content: \"\\ef2f\"; }\n.ri-mental-health-line:before { content: \"\\ef30\"; }\n.ri-menu-2-fill:before { content: \"\\ef31\"; }\n.ri-menu-2-line:before { content: \"\\ef32\"; }\n.ri-menu-3-fill:before { content: \"\\ef33\"; }\n.ri-menu-3-line:before { content: \"\\ef34\"; }\n.ri-menu-4-fill:before { content: \"\\ef35\"; }\n.ri-menu-4-line:before { content: \"\\ef36\"; }\n.ri-menu-5-fill:before { content: \"\\ef37\"; }\n.ri-menu-5-line:before { content: \"\\ef38\"; }\n.ri-menu-add-fill:before { content: \"\\ef39\"; }\n.ri-menu-add-line:before { content: \"\\ef3a\"; }\n.ri-menu-fill:before { content: \"\\ef3b\"; }\n.ri-menu-fold-fill:before { content: \"\\ef3c\"; }\n.ri-menu-fold-line:before { content: \"\\ef3d\"; }\n.ri-menu-line:before { content: \"\\ef3e\"; }\n.ri-menu-unfold-fill:before { content: \"\\ef3f\"; }\n.ri-menu-unfold-line:before { content: \"\\ef40\"; }\n.ri-merge-cells-horizontal:before { content: \"\\ef41\"; }\n.ri-merge-cells-vertical:before { content: \"\\ef42\"; }\n.ri-message-2-fill:before { content: \"\\ef43\"; }\n.ri-message-2-line:before { content: \"\\ef44\"; }\n.ri-message-3-fill:before { content: \"\\ef45\"; }\n.ri-message-3-line:before { content: \"\\ef46\"; }\n.ri-message-fill:before { content: \"\\ef47\"; }\n.ri-message-line:before { content: \"\\ef48\"; }\n.ri-messenger-fill:before { content: \"\\ef49\"; }\n.ri-messenger-line:before { content: \"\\ef4a\"; }\n.ri-meteor-fill:before { content: \"\\ef4b\"; }\n.ri-meteor-line:before { content: \"\\ef4c\"; }\n.ri-mic-2-fill:before { content: \"\\ef4d\"; }\n.ri-mic-2-line:before { content: \"\\ef4e\"; }\n.ri-mic-fill:before { content: \"\\ef4f\"; }\n.ri-mic-line:before { content: \"\\ef50\"; }\n.ri-mic-off-fill:before { content: \"\\ef51\"; }\n.ri-mic-off-line:before { content: \"\\ef52\"; }\n.ri-mickey-fill:before { content: \"\\ef53\"; }\n.ri-mickey-line:before { content: \"\\ef54\"; }\n.ri-microscope-fill:before { content: \"\\ef55\"; }\n.ri-microscope-line:before { content: \"\\ef56\"; }\n.ri-microsoft-fill:before { content: \"\\ef57\"; }\n.ri-microsoft-line:before { content: \"\\ef58\"; }\n.ri-mind-map:before { content: \"\\ef59\"; }\n.ri-mini-program-fill:before { content: \"\\ef5a\"; }\n.ri-mini-program-line:before { content: \"\\ef5b\"; }\n.ri-mist-fill:before { content: \"\\ef5c\"; }\n.ri-mist-line:before { content: \"\\ef5d\"; }\n.ri-money-cny-box-fill:before { content: \"\\ef5e\"; }\n.ri-money-cny-box-line:before { content: \"\\ef5f\"; }\n.ri-money-cny-circle-fill:before { content: \"\\ef60\"; }\n.ri-money-cny-circle-line:before { content: \"\\ef61\"; }\n.ri-money-dollar-box-fill:before { content: \"\\ef62\"; }\n.ri-money-dollar-box-line:before { content: \"\\ef63\"; }\n.ri-money-dollar-circle-fill:before { content: \"\\ef64\"; }\n.ri-money-dollar-circle-line:before { content: \"\\ef65\"; }\n.ri-money-euro-box-fill:before { content: \"\\ef66\"; }\n.ri-money-euro-box-line:before { content: \"\\ef67\"; }\n.ri-money-euro-circle-fill:before { content: \"\\ef68\"; }\n.ri-money-euro-circle-line:before { content: \"\\ef69\"; }\n.ri-money-pound-box-fill:before { content: \"\\ef6a\"; }\n.ri-money-pound-box-line:before { content: \"\\ef6b\"; }\n.ri-money-pound-circle-fill:before { content: \"\\ef6c\"; }\n.ri-money-pound-circle-line:before { content: \"\\ef6d\"; }\n.ri-moon-clear-fill:before { content: \"\\ef6e\"; }\n.ri-moon-clear-line:before { content: \"\\ef6f\"; }\n.ri-moon-cloudy-fill:before { content: \"\\ef70\"; }\n.ri-moon-cloudy-line:before { content: \"\\ef71\"; }\n.ri-moon-fill:before { content: \"\\ef72\"; }\n.ri-moon-foggy-fill:before { content: \"\\ef73\"; }\n.ri-moon-foggy-line:before { content: \"\\ef74\"; }\n.ri-moon-line:before { content: \"\\ef75\"; }\n.ri-more-2-fill:before { content: \"\\ef76\"; }\n.ri-more-2-line:before { content: \"\\ef77\"; }\n.ri-more-fill:before { content: \"\\ef78\"; }\n.ri-more-line:before { content: \"\\ef79\"; }\n.ri-motorbike-fill:before { content: \"\\ef7a\"; }\n.ri-motorbike-line:before { content: \"\\ef7b\"; }\n.ri-mouse-fill:before { content: \"\\ef7c\"; }\n.ri-mouse-line:before { content: \"\\ef7d\"; }\n.ri-movie-2-fill:before { content: \"\\ef7e\"; }\n.ri-movie-2-line:before { content: \"\\ef7f\"; }\n.ri-movie-fill:before { content: \"\\ef80\"; }\n.ri-movie-line:before { content: \"\\ef81\"; }\n.ri-music-2-fill:before { content: \"\\ef82\"; }\n.ri-music-2-line:before { content: \"\\ef83\"; }\n.ri-music-fill:before { content: \"\\ef84\"; }\n.ri-music-line:before { content: \"\\ef85\"; }\n.ri-mv-fill:before { content: \"\\ef86\"; }\n.ri-mv-line:before { content: \"\\ef87\"; }\n.ri-navigation-fill:before { content: \"\\ef88\"; }\n.ri-navigation-line:before { content: \"\\ef89\"; }\n.ri-netease-cloud-music-fill:before { content: \"\\ef8a\"; }\n.ri-netease-cloud-music-line:before { content: \"\\ef8b\"; }\n.ri-netflix-fill:before { content: \"\\ef8c\"; }\n.ri-netflix-line:before { content: \"\\ef8d\"; }\n.ri-newspaper-fill:before { content: \"\\ef8e\"; }\n.ri-newspaper-line:before { content: \"\\ef8f\"; }\n.ri-node-tree:before { content: \"\\ef90\"; }\n.ri-notification-2-fill:before { content: \"\\ef91\"; }\n.ri-notification-2-line:before { content: \"\\ef92\"; }\n.ri-notification-3-fill:before { content: \"\\ef93\"; }\n.ri-notification-3-line:before { content: \"\\ef94\"; }\n.ri-notification-4-fill:before { content: \"\\ef95\"; }\n.ri-notification-4-line:before { content: \"\\ef96\"; }\n.ri-notification-badge-fill:before { content: \"\\ef97\"; }\n.ri-notification-badge-line:before { content: \"\\ef98\"; }\n.ri-notification-fill:before { content: \"\\ef99\"; }\n.ri-notification-line:before { content: \"\\ef9a\"; }\n.ri-notification-off-fill:before { content: \"\\ef9b\"; }\n.ri-notification-off-line:before { content: \"\\ef9c\"; }\n.ri-npmjs-fill:before { content: \"\\ef9d\"; }\n.ri-npmjs-line:before { content: \"\\ef9e\"; }\n.ri-number-0:before { content: \"\\ef9f\"; }\n.ri-number-1:before { content: \"\\efa0\"; }\n.ri-number-2:before { content: \"\\efa1\"; }\n.ri-number-3:before { content: \"\\efa2\"; }\n.ri-number-4:before { content: \"\\efa3\"; }\n.ri-number-5:before { content: \"\\efa4\"; }\n.ri-number-6:before { content: \"\\efa5\"; }\n.ri-number-7:before { content: \"\\efa6\"; }\n.ri-number-8:before { content: \"\\efa7\"; }\n.ri-number-9:before { content: \"\\efa8\"; }\n.ri-numbers-fill:before { content: \"\\efa9\"; }\n.ri-numbers-line:before { content: \"\\efaa\"; }\n.ri-nurse-fill:before { content: \"\\efab\"; }\n.ri-nurse-line:before { content: \"\\efac\"; }\n.ri-oil-fill:before { content: \"\\efad\"; }\n.ri-oil-line:before { content: \"\\efae\"; }\n.ri-omega:before { content: \"\\efaf\"; }\n.ri-open-arm-fill:before { content: \"\\efb0\"; }\n.ri-open-arm-line:before { content: \"\\efb1\"; }\n.ri-open-source-fill:before { content: \"\\efb2\"; }\n.ri-open-source-line:before { content: \"\\efb3\"; }\n.ri-opera-fill:before { content: \"\\efb4\"; }\n.ri-opera-line:before { content: \"\\efb5\"; }\n.ri-order-play-fill:before { content: \"\\efb6\"; }\n.ri-order-play-line:before { content: \"\\efb7\"; }\n.ri-organization-chart:before { content: \"\\efb8\"; }\n.ri-outlet-2-fill:before { content: \"\\efb9\"; }\n.ri-outlet-2-line:before { content: \"\\efba\"; }\n.ri-outlet-fill:before { content: \"\\efbb\"; }\n.ri-outlet-line:before { content: \"\\efbc\"; }\n.ri-page-separator:before { content: \"\\efbd\"; }\n.ri-pages-fill:before { content: \"\\efbe\"; }\n.ri-pages-line:before { content: \"\\efbf\"; }\n.ri-paint-brush-fill:before { content: \"\\efc0\"; }\n.ri-paint-brush-line:before { content: \"\\efc1\"; }\n.ri-paint-fill:before { content: \"\\efc2\"; }\n.ri-paint-line:before { content: \"\\efc3\"; }\n.ri-palette-fill:before { content: \"\\efc4\"; }\n.ri-palette-line:before { content: \"\\efc5\"; }\n.ri-pantone-fill:before { content: \"\\efc6\"; }\n.ri-pantone-line:before { content: \"\\efc7\"; }\n.ri-paragraph:before { content: \"\\efc8\"; }\n.ri-parent-fill:before { content: \"\\efc9\"; }\n.ri-parent-line:before { content: \"\\efca\"; }\n.ri-parentheses-fill:before { content: \"\\efcb\"; }\n.ri-parentheses-line:before { content: \"\\efcc\"; }\n.ri-parking-box-fill:before { content: \"\\efcd\"; }\n.ri-parking-box-line:before { content: \"\\efce\"; }\n.ri-parking-fill:before { content: \"\\efcf\"; }\n.ri-parking-line:before { content: \"\\efd0\"; }\n.ri-passport-fill:before { content: \"\\efd1\"; }\n.ri-passport-line:before { content: \"\\efd2\"; }\n.ri-patreon-fill:before { content: \"\\efd3\"; }\n.ri-patreon-line:before { content: \"\\efd4\"; }\n.ri-pause-circle-fill:before { content: \"\\efd5\"; }\n.ri-pause-circle-line:before { content: \"\\efd6\"; }\n.ri-pause-fill:before { content: \"\\efd7\"; }\n.ri-pause-line:before { content: \"\\efd8\"; }\n.ri-pause-mini-fill:before { content: \"\\efd9\"; }\n.ri-pause-mini-line:before { content: \"\\efda\"; }\n.ri-paypal-fill:before { content: \"\\efdb\"; }\n.ri-paypal-line:before { content: \"\\efdc\"; }\n.ri-pen-nib-fill:before { content: \"\\efdd\"; }\n.ri-pen-nib-line:before { content: \"\\efde\"; }\n.ri-pencil-fill:before { content: \"\\efdf\"; }\n.ri-pencil-line:before { content: \"\\efe0\"; }\n.ri-pencil-ruler-2-fill:before { content: \"\\efe1\"; }\n.ri-pencil-ruler-2-line:before { content: \"\\efe2\"; }\n.ri-pencil-ruler-fill:before { content: \"\\efe3\"; }\n.ri-pencil-ruler-line:before { content: \"\\efe4\"; }\n.ri-percent-fill:before { content: \"\\efe5\"; }\n.ri-percent-line:before { content: \"\\efe6\"; }\n.ri-phone-camera-fill:before { content: \"\\efe7\"; }\n.ri-phone-camera-line:before { content: \"\\efe8\"; }\n.ri-phone-fill:before { content: \"\\efe9\"; }\n.ri-phone-find-fill:before { content: \"\\efea\"; }\n.ri-phone-find-line:before { content: \"\\efeb\"; }\n.ri-phone-line:before { content: \"\\efec\"; }\n.ri-phone-lock-fill:before { content: \"\\efed\"; }\n.ri-phone-lock-line:before { content: \"\\efee\"; }\n.ri-picture-in-picture-2-fill:before { content: \"\\efef\"; }\n.ri-picture-in-picture-2-line:before { content: \"\\eff0\"; }\n.ri-picture-in-picture-exit-fill:before { content: \"\\eff1\"; }\n.ri-picture-in-picture-exit-line:before { content: \"\\eff2\"; }\n.ri-picture-in-picture-fill:before { content: \"\\eff3\"; }\n.ri-picture-in-picture-line:before { content: \"\\eff4\"; }\n.ri-pie-chart-2-fill:before { content: \"\\eff5\"; }\n.ri-pie-chart-2-line:before { content: \"\\eff6\"; }\n.ri-pie-chart-box-fill:before { content: \"\\eff7\"; }\n.ri-pie-chart-box-line:before { content: \"\\eff8\"; }\n.ri-pie-chart-fill:before { content: \"\\eff9\"; }\n.ri-pie-chart-line:before { content: \"\\effa\"; }\n.ri-pin-distance-fill:before { content: \"\\effb\"; }\n.ri-pin-distance-line:before { content: \"\\effc\"; }\n.ri-ping-pong-fill:before { content: \"\\effd\"; }\n.ri-ping-pong-line:before { content: \"\\effe\"; }\n.ri-pinterest-fill:before { content: \"\\efff\"; }\n.ri-pinterest-line:before { content: \"\\f000\"; }\n.ri-pinyin-input:before { content: \"\\f001\"; }\n.ri-pixelfed-fill:before { content: \"\\f002\"; }\n.ri-pixelfed-line:before { content: \"\\f003\"; }\n.ri-plane-fill:before { content: \"\\f004\"; }\n.ri-plane-line:before { content: \"\\f005\"; }\n.ri-plant-fill:before { content: \"\\f006\"; }\n.ri-plant-line:before { content: \"\\f007\"; }\n.ri-play-circle-fill:before { content: \"\\f008\"; }\n.ri-play-circle-line:before { content: \"\\f009\"; }\n.ri-play-fill:before { content: \"\\f00a\"; }\n.ri-play-line:before { content: \"\\f00b\"; }\n.ri-play-list-2-fill:before { content: \"\\f00c\"; }\n.ri-play-list-2-line:before { content: \"\\f00d\"; }\n.ri-play-list-add-fill:before { content: \"\\f00e\"; }\n.ri-play-list-add-line:before { content: \"\\f00f\"; }\n.ri-play-list-fill:before { content: \"\\f010\"; }\n.ri-play-list-line:before { content: \"\\f011\"; }\n.ri-play-mini-fill:before { content: \"\\f012\"; }\n.ri-play-mini-line:before { content: \"\\f013\"; }\n.ri-playstation-fill:before { content: \"\\f014\"; }\n.ri-playstation-line:before { content: \"\\f015\"; }\n.ri-plug-2-fill:before { content: \"\\f016\"; }\n.ri-plug-2-line:before { content: \"\\f017\"; }\n.ri-plug-fill:before { content: \"\\f018\"; }\n.ri-plug-line:before { content: \"\\f019\"; }\n.ri-polaroid-2-fill:before { content: \"\\f01a\"; }\n.ri-polaroid-2-line:before { content: \"\\f01b\"; }\n.ri-polaroid-fill:before { content: \"\\f01c\"; }\n.ri-polaroid-line:before { content: \"\\f01d\"; }\n.ri-police-car-fill:before { content: \"\\f01e\"; }\n.ri-police-car-line:before { content: \"\\f01f\"; }\n.ri-price-tag-2-fill:before { content: \"\\f020\"; }\n.ri-price-tag-2-line:before { content: \"\\f021\"; }\n.ri-price-tag-3-fill:before { content: \"\\f022\"; }\n.ri-price-tag-3-line:before { content: \"\\f023\"; }\n.ri-price-tag-fill:before { content: \"\\f024\"; }\n.ri-price-tag-line:before { content: \"\\f025\"; }\n.ri-printer-cloud-fill:before { content: \"\\f026\"; }\n.ri-printer-cloud-line:before { content: \"\\f027\"; }\n.ri-printer-fill:before { content: \"\\f028\"; }\n.ri-printer-line:before { content: \"\\f029\"; }\n.ri-product-hunt-fill:before { content: \"\\f02a\"; }\n.ri-product-hunt-line:before { content: \"\\f02b\"; }\n.ri-profile-fill:before { content: \"\\f02c\"; }\n.ri-profile-line:before { content: \"\\f02d\"; }\n.ri-projector-2-fill:before { content: \"\\f02e\"; }\n.ri-projector-2-line:before { content: \"\\f02f\"; }\n.ri-projector-fill:before { content: \"\\f030\"; }\n.ri-projector-line:before { content: \"\\f031\"; }\n.ri-psychotherapy-fill:before { content: \"\\f032\"; }\n.ri-psychotherapy-line:before { content: \"\\f033\"; }\n.ri-pulse-fill:before { content: \"\\f034\"; }\n.ri-pulse-line:before { content: \"\\f035\"; }\n.ri-pushpin-2-fill:before { content: \"\\f036\"; }\n.ri-pushpin-2-line:before { content: \"\\f037\"; }\n.ri-pushpin-fill:before { content: \"\\f038\"; }\n.ri-pushpin-line:before { content: \"\\f039\"; }\n.ri-qq-fill:before { content: \"\\f03a\"; }\n.ri-qq-line:before { content: \"\\f03b\"; }\n.ri-qr-code-fill:before { content: \"\\f03c\"; }\n.ri-qr-code-line:before { content: \"\\f03d\"; }\n.ri-qr-scan-2-fill:before { content: \"\\f03e\"; }\n.ri-qr-scan-2-line:before { content: \"\\f03f\"; }\n.ri-qr-scan-fill:before { content: \"\\f040\"; }\n.ri-qr-scan-line:before { content: \"\\f041\"; }\n.ri-question-answer-fill:before { content: \"\\f042\"; }\n.ri-question-answer-line:before { content: \"\\f043\"; }\n.ri-question-fill:before { content: \"\\f044\"; }\n.ri-question-line:before { content: \"\\f045\"; }\n.ri-question-mark:before { content: \"\\f046\"; }\n.ri-questionnaire-fill:before { content: \"\\f047\"; }\n.ri-questionnaire-line:before { content: \"\\f048\"; }\n.ri-quill-pen-fill:before { content: \"\\f049\"; }\n.ri-quill-pen-line:before { content: \"\\f04a\"; }\n.ri-radar-fill:before { content: \"\\f04b\"; }\n.ri-radar-line:before { content: \"\\f04c\"; }\n.ri-radio-2-fill:before { content: \"\\f04d\"; }\n.ri-radio-2-line:before { content: \"\\f04e\"; }\n.ri-radio-button-fill:before { content: \"\\f04f\"; }\n.ri-radio-button-line:before { content: \"\\f050\"; }\n.ri-radio-fill:before { content: \"\\f051\"; }\n.ri-radio-line:before { content: \"\\f052\"; }\n.ri-rainbow-fill:before { content: \"\\f053\"; }\n.ri-rainbow-line:before { content: \"\\f054\"; }\n.ri-rainy-fill:before { content: \"\\f055\"; }\n.ri-rainy-line:before { content: \"\\f056\"; }\n.ri-reactjs-fill:before { content: \"\\f057\"; }\n.ri-reactjs-line:before { content: \"\\f058\"; }\n.ri-record-circle-fill:before { content: \"\\f059\"; }\n.ri-record-circle-line:before { content: \"\\f05a\"; }\n.ri-record-mail-fill:before { content: \"\\f05b\"; }\n.ri-record-mail-line:before { content: \"\\f05c\"; }\n.ri-recycle-fill:before { content: \"\\f05d\"; }\n.ri-recycle-line:before { content: \"\\f05e\"; }\n.ri-red-packet-fill:before { content: \"\\f05f\"; }\n.ri-red-packet-line:before { content: \"\\f060\"; }\n.ri-reddit-fill:before { content: \"\\f061\"; }\n.ri-reddit-line:before { content: \"\\f062\"; }\n.ri-refresh-fill:before { content: \"\\f063\"; }\n.ri-refresh-line:before { content: \"\\f064\"; }\n.ri-refund-2-fill:before { content: \"\\f065\"; }\n.ri-refund-2-line:before { content: \"\\f066\"; }\n.ri-refund-fill:before { content: \"\\f067\"; }\n.ri-refund-line:before { content: \"\\f068\"; }\n.ri-registered-fill:before { content: \"\\f069\"; }\n.ri-registered-line:before { content: \"\\f06a\"; }\n.ri-remixicon-fill:before { content: \"\\f06b\"; }\n.ri-remixicon-line:before { content: \"\\f06c\"; }\n.ri-remote-control-2-fill:before { content: \"\\f06d\"; }\n.ri-remote-control-2-line:before { content: \"\\f06e\"; }\n.ri-remote-control-fill:before { content: \"\\f06f\"; }\n.ri-remote-control-line:before { content: \"\\f070\"; }\n.ri-repeat-2-fill:before { content: \"\\f071\"; }\n.ri-repeat-2-line:before { content: \"\\f072\"; }\n.ri-repeat-fill:before { content: \"\\f073\"; }\n.ri-repeat-line:before { content: \"\\f074\"; }\n.ri-repeat-one-fill:before { content: \"\\f075\"; }\n.ri-repeat-one-line:before { content: \"\\f076\"; }\n.ri-reply-all-fill:before { content: \"\\f077\"; }\n.ri-reply-all-line:before { content: \"\\f078\"; }\n.ri-reply-fill:before { content: \"\\f079\"; }\n.ri-reply-line:before { content: \"\\f07a\"; }\n.ri-reserved-fill:before { content: \"\\f07b\"; }\n.ri-reserved-line:before { content: \"\\f07c\"; }\n.ri-rest-time-fill:before { content: \"\\f07d\"; }\n.ri-rest-time-line:before { content: \"\\f07e\"; }\n.ri-restart-fill:before { content: \"\\f07f\"; }\n.ri-restart-line:before { content: \"\\f080\"; }\n.ri-restaurant-2-fill:before { content: \"\\f081\"; }\n.ri-restaurant-2-line:before { content: \"\\f082\"; }\n.ri-restaurant-fill:before { content: \"\\f083\"; }\n.ri-restaurant-line:before { content: \"\\f084\"; }\n.ri-rewind-fill:before { content: \"\\f085\"; }\n.ri-rewind-line:before { content: \"\\f086\"; }\n.ri-rewind-mini-fill:before { content: \"\\f087\"; }\n.ri-rewind-mini-line:before { content: \"\\f088\"; }\n.ri-rhythm-fill:before { content: \"\\f089\"; }\n.ri-rhythm-line:before { content: \"\\f08a\"; }\n.ri-riding-fill:before { content: \"\\f08b\"; }\n.ri-riding-line:before { content: \"\\f08c\"; }\n.ri-road-map-fill:before { content: \"\\f08d\"; }\n.ri-road-map-line:before { content: \"\\f08e\"; }\n.ri-roadster-fill:before { content: \"\\f08f\"; }\n.ri-roadster-line:before { content: \"\\f090\"; }\n.ri-robot-fill:before { content: \"\\f091\"; }\n.ri-robot-line:before { content: \"\\f092\"; }\n.ri-rocket-2-fill:before { content: \"\\f093\"; }\n.ri-rocket-2-line:before { content: \"\\f094\"; }\n.ri-rocket-fill:before { content: \"\\f095\"; }\n.ri-rocket-line:before { content: \"\\f096\"; }\n.ri-rotate-lock-fill:before { content: \"\\f097\"; }\n.ri-rotate-lock-line:before { content: \"\\f098\"; }\n.ri-rounded-corner:before { content: \"\\f099\"; }\n.ri-route-fill:before { content: \"\\f09a\"; }\n.ri-route-line:before { content: \"\\f09b\"; }\n.ri-router-fill:before { content: \"\\f09c\"; }\n.ri-router-line:before { content: \"\\f09d\"; }\n.ri-rss-fill:before { content: \"\\f09e\"; }\n.ri-rss-line:before { content: \"\\f09f\"; }\n.ri-ruler-2-fill:before { content: \"\\f0a0\"; }\n.ri-ruler-2-line:before { content: \"\\f0a1\"; }\n.ri-ruler-fill:before { content: \"\\f0a2\"; }\n.ri-ruler-line:before { content: \"\\f0a3\"; }\n.ri-run-fill:before { content: \"\\f0a4\"; }\n.ri-run-line:before { content: \"\\f0a5\"; }\n.ri-safari-fill:before { content: \"\\f0a6\"; }\n.ri-safari-line:before { content: \"\\f0a7\"; }\n.ri-safe-2-fill:before { content: \"\\f0a8\"; }\n.ri-safe-2-line:before { content: \"\\f0a9\"; }\n.ri-safe-fill:before { content: \"\\f0aa\"; }\n.ri-safe-line:before { content: \"\\f0ab\"; }\n.ri-sailboat-fill:before { content: \"\\f0ac\"; }\n.ri-sailboat-line:before { content: \"\\f0ad\"; }\n.ri-save-2-fill:before { content: \"\\f0ae\"; }\n.ri-save-2-line:before { content: \"\\f0af\"; }\n.ri-save-3-fill:before { content: \"\\f0b0\"; }\n.ri-save-3-line:before { content: \"\\f0b1\"; }\n.ri-save-fill:before { content: \"\\f0b2\"; }\n.ri-save-line:before { content: \"\\f0b3\"; }\n.ri-scales-2-fill:before { content: \"\\f0b4\"; }\n.ri-scales-2-line:before { content: \"\\f0b5\"; }\n.ri-scales-3-fill:before { content: \"\\f0b6\"; }\n.ri-scales-3-line:before { content: \"\\f0b7\"; }\n.ri-scales-fill:before { content: \"\\f0b8\"; }\n.ri-scales-line:before { content: \"\\f0b9\"; }\n.ri-scan-2-fill:before { content: \"\\f0ba\"; }\n.ri-scan-2-line:before { content: \"\\f0bb\"; }\n.ri-scan-fill:before { content: \"\\f0bc\"; }\n.ri-scan-line:before { content: \"\\f0bd\"; }\n.ri-scissors-2-fill:before { content: \"\\f0be\"; }\n.ri-scissors-2-line:before { content: \"\\f0bf\"; }\n.ri-scissors-cut-fill:before { content: \"\\f0c0\"; }\n.ri-scissors-cut-line:before { content: \"\\f0c1\"; }\n.ri-scissors-fill:before { content: \"\\f0c2\"; }\n.ri-scissors-line:before { content: \"\\f0c3\"; }\n.ri-screenshot-2-fill:before { content: \"\\f0c4\"; }\n.ri-screenshot-2-line:before { content: \"\\f0c5\"; }\n.ri-screenshot-fill:before { content: \"\\f0c6\"; }\n.ri-screenshot-line:before { content: \"\\f0c7\"; }\n.ri-sd-card-fill:before { content: \"\\f0c8\"; }\n.ri-sd-card-line:before { content: \"\\f0c9\"; }\n.ri-sd-card-mini-fill:before { content: \"\\f0ca\"; }\n.ri-sd-card-mini-line:before { content: \"\\f0cb\"; }\n.ri-search-2-fill:before { content: \"\\f0cc\"; }\n.ri-search-2-line:before { content: \"\\f0cd\"; }\n.ri-search-eye-fill:before { content: \"\\f0ce\"; }\n.ri-search-eye-line:before { content: \"\\f0cf\"; }\n.ri-search-fill:before { content: \"\\f0d0\"; }\n.ri-search-line:before { content: \"\\f0d1\"; }\n.ri-secure-payment-fill:before { content: \"\\f0d2\"; }\n.ri-secure-payment-line:before { content: \"\\f0d3\"; }\n.ri-seedling-fill:before { content: \"\\f0d4\"; }\n.ri-seedling-line:before { content: \"\\f0d5\"; }\n.ri-send-backward:before { content: \"\\f0d6\"; }\n.ri-send-plane-2-fill:before { content: \"\\f0d7\"; }\n.ri-send-plane-2-line:before { content: \"\\f0d8\"; }\n.ri-send-plane-fill:before { content: \"\\f0d9\"; }\n.ri-send-plane-line:before { content: \"\\f0da\"; }\n.ri-send-to-back:before { content: \"\\f0db\"; }\n.ri-sensor-fill:before { content: \"\\f0dc\"; }\n.ri-sensor-line:before { content: \"\\f0dd\"; }\n.ri-separator:before { content: \"\\f0de\"; }\n.ri-server-fill:before { content: \"\\f0df\"; }\n.ri-server-line:before { content: \"\\f0e0\"; }\n.ri-service-fill:before { content: \"\\f0e1\"; }\n.ri-service-line:before { content: \"\\f0e2\"; }\n.ri-settings-2-fill:before { content: \"\\f0e3\"; }\n.ri-settings-2-line:before { content: \"\\f0e4\"; }\n.ri-settings-3-fill:before { content: \"\\f0e5\"; }\n.ri-settings-3-line:before { content: \"\\f0e6\"; }\n.ri-settings-4-fill:before { content: \"\\f0e7\"; }\n.ri-settings-4-line:before { content: \"\\f0e8\"; }\n.ri-settings-5-fill:before { content: \"\\f0e9\"; }\n.ri-settings-5-line:before { content: \"\\f0ea\"; }\n.ri-settings-6-fill:before { content: \"\\f0eb\"; }\n.ri-settings-6-line:before { content: \"\\f0ec\"; }\n.ri-settings-fill:before { content: \"\\f0ed\"; }\n.ri-settings-line:before { content: \"\\f0ee\"; }\n.ri-shape-2-fill:before { content: \"\\f0ef\"; }\n.ri-shape-2-line:before { content: \"\\f0f0\"; }\n.ri-shape-fill:before { content: \"\\f0f1\"; }\n.ri-shape-line:before { content: \"\\f0f2\"; }\n.ri-share-box-fill:before { content: \"\\f0f3\"; }\n.ri-share-box-line:before { content: \"\\f0f4\"; }\n.ri-share-circle-fill:before { content: \"\\f0f5\"; }\n.ri-share-circle-line:before { content: \"\\f0f6\"; }\n.ri-share-fill:before { content: \"\\f0f7\"; }\n.ri-share-forward-2-fill:before { content: \"\\f0f8\"; }\n.ri-share-forward-2-line:before { content: \"\\f0f9\"; }\n.ri-share-forward-box-fill:before { content: \"\\f0fa\"; }\n.ri-share-forward-box-line:before { content: \"\\f0fb\"; }\n.ri-share-forward-fill:before { content: \"\\f0fc\"; }\n.ri-share-forward-line:before { content: \"\\f0fd\"; }\n.ri-share-line:before { content: \"\\f0fe\"; }\n.ri-shield-check-fill:before { content: \"\\f0ff\"; }\n.ri-shield-check-line:before { content: \"\\f100\"; }\n.ri-shield-cross-fill:before { content: \"\\f101\"; }\n.ri-shield-cross-line:before { content: \"\\f102\"; }\n.ri-shield-fill:before { content: \"\\f103\"; }\n.ri-shield-flash-fill:before { content: \"\\f104\"; }\n.ri-shield-flash-line:before { content: \"\\f105\"; }\n.ri-shield-keyhole-fill:before { content: \"\\f106\"; }\n.ri-shield-keyhole-line:before { content: \"\\f107\"; }\n.ri-shield-line:before { content: \"\\f108\"; }\n.ri-shield-star-fill:before { content: \"\\f109\"; }\n.ri-shield-star-line:before { content: \"\\f10a\"; }\n.ri-shield-user-fill:before { content: \"\\f10b\"; }\n.ri-shield-user-line:before { content: \"\\f10c\"; }\n.ri-ship-2-fill:before { content: \"\\f10d\"; }\n.ri-ship-2-line:before { content: \"\\f10e\"; }\n.ri-ship-fill:before { content: \"\\f10f\"; }\n.ri-ship-line:before { content: \"\\f110\"; }\n.ri-shirt-fill:before { content: \"\\f111\"; }\n.ri-shirt-line:before { content: \"\\f112\"; }\n.ri-shopping-bag-2-fill:before { content: \"\\f113\"; }\n.ri-shopping-bag-2-line:before { content: \"\\f114\"; }\n.ri-shopping-bag-3-fill:before { content: \"\\f115\"; }\n.ri-shopping-bag-3-line:before { content: \"\\f116\"; }\n.ri-shopping-bag-fill:before { content: \"\\f117\"; }\n.ri-shopping-bag-line:before { content: \"\\f118\"; }\n.ri-shopping-basket-2-fill:before { content: \"\\f119\"; }\n.ri-shopping-basket-2-line:before { content: \"\\f11a\"; }\n.ri-shopping-basket-fill:before { content: \"\\f11b\"; }\n.ri-shopping-basket-line:before { content: \"\\f11c\"; }\n.ri-shopping-cart-2-fill:before { content: \"\\f11d\"; }\n.ri-shopping-cart-2-line:before { content: \"\\f11e\"; }\n.ri-shopping-cart-fill:before { content: \"\\f11f\"; }\n.ri-shopping-cart-line:before { content: \"\\f120\"; }\n.ri-showers-fill:before { content: \"\\f121\"; }\n.ri-showers-line:before { content: \"\\f122\"; }\n.ri-shuffle-fill:before { content: \"\\f123\"; }\n.ri-shuffle-line:before { content: \"\\f124\"; }\n.ri-shut-down-fill:before { content: \"\\f125\"; }\n.ri-shut-down-line:before { content: \"\\f126\"; }\n.ri-side-bar-fill:before { content: \"\\f127\"; }\n.ri-side-bar-line:before { content: \"\\f128\"; }\n.ri-signal-tower-fill:before { content: \"\\f129\"; }\n.ri-signal-tower-line:before { content: \"\\f12a\"; }\n.ri-signal-wifi-1-fill:before { content: \"\\f12b\"; }\n.ri-signal-wifi-1-line:before { content: \"\\f12c\"; }\n.ri-signal-wifi-2-fill:before { content: \"\\f12d\"; }\n.ri-signal-wifi-2-line:before { content: \"\\f12e\"; }\n.ri-signal-wifi-3-fill:before { content: \"\\f12f\"; }\n.ri-signal-wifi-3-line:before { content: \"\\f130\"; }\n.ri-signal-wifi-error-fill:before { content: \"\\f131\"; }\n.ri-signal-wifi-error-line:before { content: \"\\f132\"; }\n.ri-signal-wifi-fill:before { content: \"\\f133\"; }\n.ri-signal-wifi-line:before { content: \"\\f134\"; }\n.ri-signal-wifi-off-fill:before { content: \"\\f135\"; }\n.ri-signal-wifi-off-line:before { content: \"\\f136\"; }\n.ri-sim-card-2-fill:before { content: \"\\f137\"; }\n.ri-sim-card-2-line:before { content: \"\\f138\"; }\n.ri-sim-card-fill:before { content: \"\\f139\"; }\n.ri-sim-card-line:before { content: \"\\f13a\"; }\n.ri-single-quotes-l:before { content: \"\\f13b\"; }\n.ri-single-quotes-r:before { content: \"\\f13c\"; }\n.ri-sip-fill:before { content: \"\\f13d\"; }\n.ri-sip-line:before { content: \"\\f13e\"; }\n.ri-skip-back-fill:before { content: \"\\f13f\"; }\n.ri-skip-back-line:before { content: \"\\f140\"; }\n.ri-skip-back-mini-fill:before { content: \"\\f141\"; }\n.ri-skip-back-mini-line:before { content: \"\\f142\"; }\n.ri-skip-forward-fill:before { content: \"\\f143\"; }\n.ri-skip-forward-line:before { content: \"\\f144\"; }\n.ri-skip-forward-mini-fill:before { content: \"\\f145\"; }\n.ri-skip-forward-mini-line:before { content: \"\\f146\"; }\n.ri-skull-2-fill:before { content: \"\\f147\"; }\n.ri-skull-2-line:before { content: \"\\f148\"; }\n.ri-skull-fill:before { content: \"\\f149\"; }\n.ri-skull-line:before { content: \"\\f14a\"; }\n.ri-skype-fill:before { content: \"\\f14b\"; }\n.ri-skype-line:before { content: \"\\f14c\"; }\n.ri-slack-fill:before { content: \"\\f14d\"; }\n.ri-slack-line:before { content: \"\\f14e\"; }\n.ri-slice-fill:before { content: \"\\f14f\"; }\n.ri-slice-line:before { content: \"\\f150\"; }\n.ri-slideshow-2-fill:before { content: \"\\f151\"; }\n.ri-slideshow-2-line:before { content: \"\\f152\"; }\n.ri-slideshow-3-fill:before { content: \"\\f153\"; }\n.ri-slideshow-3-line:before { content: \"\\f154\"; }\n.ri-slideshow-4-fill:before { content: \"\\f155\"; }\n.ri-slideshow-4-line:before { content: \"\\f156\"; }\n.ri-slideshow-fill:before { content: \"\\f157\"; }\n.ri-slideshow-line:before { content: \"\\f158\"; }\n.ri-smartphone-fill:before { content: \"\\f159\"; }\n.ri-smartphone-line:before { content: \"\\f15a\"; }\n.ri-snapchat-fill:before { content: \"\\f15b\"; }\n.ri-snapchat-line:before { content: \"\\f15c\"; }\n.ri-snowy-fill:before { content: \"\\f15d\"; }\n.ri-snowy-line:before { content: \"\\f15e\"; }\n.ri-sort-asc:before { content: \"\\f15f\"; }\n.ri-sort-desc:before { content: \"\\f160\"; }\n.ri-sound-module-fill:before { content: \"\\f161\"; }\n.ri-sound-module-line:before { content: \"\\f162\"; }\n.ri-soundcloud-fill:before { content: \"\\f163\"; }\n.ri-soundcloud-line:before { content: \"\\f164\"; }\n.ri-space-ship-fill:before { content: \"\\f165\"; }\n.ri-space-ship-line:before { content: \"\\f166\"; }\n.ri-space:before { content: \"\\f167\"; }\n.ri-spam-2-fill:before { content: \"\\f168\"; }\n.ri-spam-2-line:before { content: \"\\f169\"; }\n.ri-spam-3-fill:before { content: \"\\f16a\"; }\n.ri-spam-3-line:before { content: \"\\f16b\"; }\n.ri-spam-fill:before { content: \"\\f16c\"; }\n.ri-spam-line:before { content: \"\\f16d\"; }\n.ri-speaker-2-fill:before { content: \"\\f16e\"; }\n.ri-speaker-2-line:before { content: \"\\f16f\"; }\n.ri-speaker-3-fill:before { content: \"\\f170\"; }\n.ri-speaker-3-line:before { content: \"\\f171\"; }\n.ri-speaker-fill:before { content: \"\\f172\"; }\n.ri-speaker-line:before { content: \"\\f173\"; }\n.ri-spectrum-fill:before { content: \"\\f174\"; }\n.ri-spectrum-line:before { content: \"\\f175\"; }\n.ri-speed-fill:before { content: \"\\f176\"; }\n.ri-speed-line:before { content: \"\\f177\"; }\n.ri-speed-mini-fill:before { content: \"\\f178\"; }\n.ri-speed-mini-line:before { content: \"\\f179\"; }\n.ri-split-cells-horizontal:before { content: \"\\f17a\"; }\n.ri-split-cells-vertical:before { content: \"\\f17b\"; }\n.ri-spotify-fill:before { content: \"\\f17c\"; }\n.ri-spotify-line:before { content: \"\\f17d\"; }\n.ri-spy-fill:before { content: \"\\f17e\"; }\n.ri-spy-line:before { content: \"\\f17f\"; }\n.ri-stack-fill:before { content: \"\\f180\"; }\n.ri-stack-line:before { content: \"\\f181\"; }\n.ri-stack-overflow-fill:before { content: \"\\f182\"; }\n.ri-stack-overflow-line:before { content: \"\\f183\"; }\n.ri-stackshare-fill:before { content: \"\\f184\"; }\n.ri-stackshare-line:before { content: \"\\f185\"; }\n.ri-star-fill:before { content: \"\\f186\"; }\n.ri-star-half-fill:before { content: \"\\f187\"; }\n.ri-star-half-line:before { content: \"\\f188\"; }\n.ri-star-half-s-fill:before { content: \"\\f189\"; }\n.ri-star-half-s-line:before { content: \"\\f18a\"; }\n.ri-star-line:before { content: \"\\f18b\"; }\n.ri-star-s-fill:before { content: \"\\f18c\"; }\n.ri-star-s-line:before { content: \"\\f18d\"; }\n.ri-star-smile-fill:before { content: \"\\f18e\"; }\n.ri-star-smile-line:before { content: \"\\f18f\"; }\n.ri-steam-fill:before { content: \"\\f190\"; }\n.ri-steam-line:before { content: \"\\f191\"; }\n.ri-steering-2-fill:before { content: \"\\f192\"; }\n.ri-steering-2-line:before { content: \"\\f193\"; }\n.ri-steering-fill:before { content: \"\\f194\"; }\n.ri-steering-line:before { content: \"\\f195\"; }\n.ri-stethoscope-fill:before { content: \"\\f196\"; }\n.ri-stethoscope-line:before { content: \"\\f197\"; }\n.ri-sticky-note-2-fill:before { content: \"\\f198\"; }\n.ri-sticky-note-2-line:before { content: \"\\f199\"; }\n.ri-sticky-note-fill:before { content: \"\\f19a\"; }\n.ri-sticky-note-line:before { content: \"\\f19b\"; }\n.ri-stock-fill:before { content: \"\\f19c\"; }\n.ri-stock-line:before { content: \"\\f19d\"; }\n.ri-stop-circle-fill:before { content: \"\\f19e\"; }\n.ri-stop-circle-line:before { content: \"\\f19f\"; }\n.ri-stop-fill:before { content: \"\\f1a0\"; }\n.ri-stop-line:before { content: \"\\f1a1\"; }\n.ri-stop-mini-fill:before { content: \"\\f1a2\"; }\n.ri-stop-mini-line:before { content: \"\\f1a3\"; }\n.ri-store-2-fill:before { content: \"\\f1a4\"; }\n.ri-store-2-line:before { content: \"\\f1a5\"; }\n.ri-store-3-fill:before { content: \"\\f1a6\"; }\n.ri-store-3-line:before { content: \"\\f1a7\"; }\n.ri-store-fill:before { content: \"\\f1a8\"; }\n.ri-store-line:before { content: \"\\f1a9\"; }\n.ri-strikethrough-2:before { content: \"\\f1aa\"; }\n.ri-strikethrough:before { content: \"\\f1ab\"; }\n.ri-subscript-2:before { content: \"\\f1ac\"; }\n.ri-subscript:before { content: \"\\f1ad\"; }\n.ri-subtract-fill:before { content: \"\\f1ae\"; }\n.ri-subtract-line:before { content: \"\\f1af\"; }\n.ri-subway-fill:before { content: \"\\f1b0\"; }\n.ri-subway-line:before { content: \"\\f1b1\"; }\n.ri-subway-wifi-fill:before { content: \"\\f1b2\"; }\n.ri-subway-wifi-line:before { content: \"\\f1b3\"; }\n.ri-suitcase-2-fill:before { content: \"\\f1b4\"; }\n.ri-suitcase-2-line:before { content: \"\\f1b5\"; }\n.ri-suitcase-3-fill:before { content: \"\\f1b6\"; }\n.ri-suitcase-3-line:before { content: \"\\f1b7\"; }\n.ri-suitcase-fill:before { content: \"\\f1b8\"; }\n.ri-suitcase-line:before { content: \"\\f1b9\"; }\n.ri-sun-cloudy-fill:before { content: \"\\f1ba\"; }\n.ri-sun-cloudy-line:before { content: \"\\f1bb\"; }\n.ri-sun-fill:before { content: \"\\f1bc\"; }\n.ri-sun-foggy-fill:before { content: \"\\f1bd\"; }\n.ri-sun-foggy-line:before { content: \"\\f1be\"; }\n.ri-sun-line:before { content: \"\\f1bf\"; }\n.ri-superscript-2:before { content: \"\\f1c0\"; }\n.ri-superscript:before { content: \"\\f1c1\"; }\n.ri-surgical-mask-fill:before { content: \"\\f1c2\"; }\n.ri-surgical-mask-line:before { content: \"\\f1c3\"; }\n.ri-surround-sound-fill:before { content: \"\\f1c4\"; }\n.ri-surround-sound-line:before { content: \"\\f1c5\"; }\n.ri-survey-fill:before { content: \"\\f1c6\"; }\n.ri-survey-line:before { content: \"\\f1c7\"; }\n.ri-swap-box-fill:before { content: \"\\f1c8\"; }\n.ri-swap-box-line:before { content: \"\\f1c9\"; }\n.ri-swap-fill:before { content: \"\\f1ca\"; }\n.ri-swap-line:before { content: \"\\f1cb\"; }\n.ri-switch-fill:before { content: \"\\f1cc\"; }\n.ri-switch-line:before { content: \"\\f1cd\"; }\n.ri-sword-fill:before { content: \"\\f1ce\"; }\n.ri-sword-line:before { content: \"\\f1cf\"; }\n.ri-syringe-fill:before { content: \"\\f1d0\"; }\n.ri-syringe-line:before { content: \"\\f1d1\"; }\n.ri-t-box-fill:before { content: \"\\f1d2\"; }\n.ri-t-box-line:before { content: \"\\f1d3\"; }\n.ri-t-shirt-2-fill:before { content: \"\\f1d4\"; }\n.ri-t-shirt-2-line:before { content: \"\\f1d5\"; }\n.ri-t-shirt-air-fill:before { content: \"\\f1d6\"; }\n.ri-t-shirt-air-line:before { content: \"\\f1d7\"; }\n.ri-t-shirt-fill:before { content: \"\\f1d8\"; }\n.ri-t-shirt-line:before { content: \"\\f1d9\"; }\n.ri-table-2:before { content: \"\\f1da\"; }\n.ri-table-alt-fill:before { content: \"\\f1db\"; }\n.ri-table-alt-line:before { content: \"\\f1dc\"; }\n.ri-table-fill:before { content: \"\\f1dd\"; }\n.ri-table-line:before { content: \"\\f1de\"; }\n.ri-tablet-fill:before { content: \"\\f1df\"; }\n.ri-tablet-line:before { content: \"\\f1e0\"; }\n.ri-takeaway-fill:before { content: \"\\f1e1\"; }\n.ri-takeaway-line:before { content: \"\\f1e2\"; }\n.ri-taobao-fill:before { content: \"\\f1e3\"; }\n.ri-taobao-line:before { content: \"\\f1e4\"; }\n.ri-tape-fill:before { content: \"\\f1e5\"; }\n.ri-tape-line:before { content: \"\\f1e6\"; }\n.ri-task-fill:before { content: \"\\f1e7\"; }\n.ri-task-line:before { content: \"\\f1e8\"; }\n.ri-taxi-fill:before { content: \"\\f1e9\"; }\n.ri-taxi-line:before { content: \"\\f1ea\"; }\n.ri-taxi-wifi-fill:before { content: \"\\f1eb\"; }\n.ri-taxi-wifi-line:before { content: \"\\f1ec\"; }\n.ri-team-fill:before { content: \"\\f1ed\"; }\n.ri-team-line:before { content: \"\\f1ee\"; }\n.ri-telegram-fill:before { content: \"\\f1ef\"; }\n.ri-telegram-line:before { content: \"\\f1f0\"; }\n.ri-temp-cold-fill:before { content: \"\\f1f1\"; }\n.ri-temp-cold-line:before { content: \"\\f1f2\"; }\n.ri-temp-hot-fill:before { content: \"\\f1f3\"; }\n.ri-temp-hot-line:before { content: \"\\f1f4\"; }\n.ri-terminal-box-fill:before { content: \"\\f1f5\"; }\n.ri-terminal-box-line:before { content: \"\\f1f6\"; }\n.ri-terminal-fill:before { content: \"\\f1f7\"; }\n.ri-terminal-line:before { content: \"\\f1f8\"; }\n.ri-terminal-window-fill:before { content: \"\\f1f9\"; }\n.ri-terminal-window-line:before { content: \"\\f1fa\"; }\n.ri-test-tube-fill:before { content: \"\\f1fb\"; }\n.ri-test-tube-line:before { content: \"\\f1fc\"; }\n.ri-text-direction-l:before { content: \"\\f1fd\"; }\n.ri-text-direction-r:before { content: \"\\f1fe\"; }\n.ri-text-spacing:before { content: \"\\f1ff\"; }\n.ri-text-wrap:before { content: \"\\f200\"; }\n.ri-text:before { content: \"\\f201\"; }\n.ri-thermometer-fill:before { content: \"\\f202\"; }\n.ri-thermometer-line:before { content: \"\\f203\"; }\n.ri-thumb-down-fill:before { content: \"\\f204\"; }\n.ri-thumb-down-line:before { content: \"\\f205\"; }\n.ri-thumb-up-fill:before { content: \"\\f206\"; }\n.ri-thumb-up-line:before { content: \"\\f207\"; }\n.ri-thunderstorms-fill:before { content: \"\\f208\"; }\n.ri-thunderstorms-line:before { content: \"\\f209\"; }\n.ri-ticket-2-fill:before { content: \"\\f20a\"; }\n.ri-ticket-2-line:before { content: \"\\f20b\"; }\n.ri-ticket-fill:before { content: \"\\f20c\"; }\n.ri-ticket-line:before { content: \"\\f20d\"; }\n.ri-time-fill:before { content: \"\\f20e\"; }\n.ri-time-line:before { content: \"\\f20f\"; }\n.ri-timer-2-fill:before { content: \"\\f210\"; }\n.ri-timer-2-line:before { content: \"\\f211\"; }\n.ri-timer-fill:before { content: \"\\f212\"; }\n.ri-timer-flash-fill:before { content: \"\\f213\"; }\n.ri-timer-flash-line:before { content: \"\\f214\"; }\n.ri-timer-line:before { content: \"\\f215\"; }\n.ri-todo-fill:before { content: \"\\f216\"; }\n.ri-todo-line:before { content: \"\\f217\"; }\n.ri-toggle-fill:before { content: \"\\f218\"; }\n.ri-toggle-line:before { content: \"\\f219\"; }\n.ri-tools-fill:before { content: \"\\f21a\"; }\n.ri-tools-line:before { content: \"\\f21b\"; }\n.ri-tornado-fill:before { content: \"\\f21c\"; }\n.ri-tornado-line:before { content: \"\\f21d\"; }\n.ri-trademark-fill:before { content: \"\\f21e\"; }\n.ri-trademark-line:before { content: \"\\f21f\"; }\n.ri-traffic-light-fill:before { content: \"\\f220\"; }\n.ri-traffic-light-line:before { content: \"\\f221\"; }\n.ri-train-fill:before { content: \"\\f222\"; }\n.ri-train-line:before { content: \"\\f223\"; }\n.ri-train-wifi-fill:before { content: \"\\f224\"; }\n.ri-train-wifi-line:before { content: \"\\f225\"; }\n.ri-translate-2:before { content: \"\\f226\"; }\n.ri-translate:before { content: \"\\f227\"; }\n.ri-travesti-fill:before { content: \"\\f228\"; }\n.ri-travesti-line:before { content: \"\\f229\"; }\n.ri-treasure-map-fill:before { content: \"\\f22a\"; }\n.ri-treasure-map-line:before { content: \"\\f22b\"; }\n.ri-trello-fill:before { content: \"\\f22c\"; }\n.ri-trello-line:before { content: \"\\f22d\"; }\n.ri-trophy-fill:before { content: \"\\f22e\"; }\n.ri-trophy-line:before { content: \"\\f22f\"; }\n.ri-truck-fill:before { content: \"\\f230\"; }\n.ri-truck-line:before { content: \"\\f231\"; }\n.ri-tumblr-fill:before { content: \"\\f232\"; }\n.ri-tumblr-line:before { content: \"\\f233\"; }\n.ri-tv-2-fill:before { content: \"\\f234\"; }\n.ri-tv-2-line:before { content: \"\\f235\"; }\n.ri-tv-fill:before { content: \"\\f236\"; }\n.ri-tv-line:before { content: \"\\f237\"; }\n.ri-twitch-fill:before { content: \"\\f238\"; }\n.ri-twitch-line:before { content: \"\\f239\"; }\n.ri-twitter-fill:before { content: \"\\f23a\"; }\n.ri-twitter-line:before { content: \"\\f23b\"; }\n.ri-typhoon-fill:before { content: \"\\f23c\"; }\n.ri-typhoon-line:before { content: \"\\f23d\"; }\n.ri-u-disk-fill:before { content: \"\\f23e\"; }\n.ri-u-disk-line:before { content: \"\\f23f\"; }\n.ri-ubuntu-fill:before { content: \"\\f240\"; }\n.ri-ubuntu-line:before { content: \"\\f241\"; }\n.ri-umbrella-fill:before { content: \"\\f242\"; }\n.ri-umbrella-line:before { content: \"\\f243\"; }\n.ri-underline:before { content: \"\\f244\"; }\n.ri-uninstall-fill:before { content: \"\\f245\"; }\n.ri-uninstall-line:before { content: \"\\f246\"; }\n.ri-unsplash-fill:before { content: \"\\f247\"; }\n.ri-unsplash-line:before { content: \"\\f248\"; }\n.ri-upload-2-fill:before { content: \"\\f249\"; }\n.ri-upload-2-line:before { content: \"\\f24a\"; }\n.ri-upload-cloud-2-fill:before { content: \"\\f24b\"; }\n.ri-upload-cloud-2-line:before { content: \"\\f24c\"; }\n.ri-upload-cloud-fill:before { content: \"\\f24d\"; }\n.ri-upload-cloud-line:before { content: \"\\f24e\"; }\n.ri-upload-fill:before { content: \"\\f24f\"; }\n.ri-upload-line:before { content: \"\\f250\"; }\n.ri-usb-fill:before { content: \"\\f251\"; }\n.ri-usb-line:before { content: \"\\f252\"; }\n.ri-user-2-fill:before { content: \"\\f253\"; }\n.ri-user-2-line:before { content: \"\\f254\"; }\n.ri-user-3-fill:before { content: \"\\f255\"; }\n.ri-user-3-line:before { content: \"\\f256\"; }\n.ri-user-4-fill:before { content: \"\\f257\"; }\n.ri-user-4-line:before { content: \"\\f258\"; }\n.ri-user-5-fill:before { content: \"\\f259\"; }\n.ri-user-5-line:before { content: \"\\f25a\"; }\n.ri-user-6-fill:before { content: \"\\f25b\"; }\n.ri-user-6-line:before { content: \"\\f25c\"; }\n.ri-user-add-fill:before { content: \"\\f25d\"; }\n.ri-user-add-line:before { content: \"\\f25e\"; }\n.ri-user-fill:before { content: \"\\f25f\"; }\n.ri-user-follow-fill:before { content: \"\\f260\"; }\n.ri-user-follow-line:before { content: \"\\f261\"; }\n.ri-user-heart-fill:before { content: \"\\f262\"; }\n.ri-user-heart-line:before { content: \"\\f263\"; }\n.ri-user-line:before { content: \"\\f264\"; }\n.ri-user-location-fill:before { content: \"\\f265\"; }\n.ri-user-location-line:before { content: \"\\f266\"; }\n.ri-user-received-2-fill:before { content: \"\\f267\"; }\n.ri-user-received-2-line:before { content: \"\\f268\"; }\n.ri-user-received-fill:before { content: \"\\f269\"; }\n.ri-user-received-line:before { content: \"\\f26a\"; }\n.ri-user-search-fill:before { content: \"\\f26b\"; }\n.ri-user-search-line:before { content: \"\\f26c\"; }\n.ri-user-settings-fill:before { content: \"\\f26d\"; }\n.ri-user-settings-line:before { content: \"\\f26e\"; }\n.ri-user-shared-2-fill:before { content: \"\\f26f\"; }\n.ri-user-shared-2-line:before { content: \"\\f270\"; }\n.ri-user-shared-fill:before { content: \"\\f271\"; }\n.ri-user-shared-line:before { content: \"\\f272\"; }\n.ri-user-smile-fill:before { content: \"\\f273\"; }\n.ri-user-smile-line:before { content: \"\\f274\"; }\n.ri-user-star-fill:before { content: \"\\f275\"; }\n.ri-user-star-line:before { content: \"\\f276\"; }\n.ri-user-unfollow-fill:before { content: \"\\f277\"; }\n.ri-user-unfollow-line:before { content: \"\\f278\"; }\n.ri-user-voice-fill:before { content: \"\\f279\"; }\n.ri-user-voice-line:before { content: \"\\f27a\"; }\n.ri-video-add-fill:before { content: \"\\f27b\"; }\n.ri-video-add-line:before { content: \"\\f27c\"; }\n.ri-video-chat-fill:before { content: \"\\f27d\"; }\n.ri-video-chat-line:before { content: \"\\f27e\"; }\n.ri-video-download-fill:before { content: \"\\f27f\"; }\n.ri-video-download-line:before { content: \"\\f280\"; }\n.ri-video-fill:before { content: \"\\f281\"; }\n.ri-video-line:before { content: \"\\f282\"; }\n.ri-video-upload-fill:before { content: \"\\f283\"; }\n.ri-video-upload-line:before { content: \"\\f284\"; }\n.ri-vidicon-2-fill:before { content: \"\\f285\"; }\n.ri-vidicon-2-line:before { content: \"\\f286\"; }\n.ri-vidicon-fill:before { content: \"\\f287\"; }\n.ri-vidicon-line:before { content: \"\\f288\"; }\n.ri-vimeo-fill:before { content: \"\\f289\"; }\n.ri-vimeo-line:before { content: \"\\f28a\"; }\n.ri-vip-crown-2-fill:before { content: \"\\f28b\"; }\n.ri-vip-crown-2-line:before { content: \"\\f28c\"; }\n.ri-vip-crown-fill:before { content: \"\\f28d\"; }\n.ri-vip-crown-line:before { content: \"\\f28e\"; }\n.ri-vip-diamond-fill:before { content: \"\\f28f\"; }\n.ri-vip-diamond-line:before { content: \"\\f290\"; }\n.ri-vip-fill:before { content: \"\\f291\"; }\n.ri-vip-line:before { content: \"\\f292\"; }\n.ri-virus-fill:before { content: \"\\f293\"; }\n.ri-virus-line:before { content: \"\\f294\"; }\n.ri-visa-fill:before { content: \"\\f295\"; }\n.ri-visa-line:before { content: \"\\f296\"; }\n.ri-voice-recognition-fill:before { content: \"\\f297\"; }\n.ri-voice-recognition-line:before { content: \"\\f298\"; }\n.ri-voiceprint-fill:before { content: \"\\f299\"; }\n.ri-voiceprint-line:before { content: \"\\f29a\"; }\n.ri-volume-down-fill:before { content: \"\\f29b\"; }\n.ri-volume-down-line:before { content: \"\\f29c\"; }\n.ri-volume-mute-fill:before { content: \"\\f29d\"; }\n.ri-volume-mute-line:before { content: \"\\f29e\"; }\n.ri-volume-off-vibrate-fill:before { content: \"\\f29f\"; }\n.ri-volume-off-vibrate-line:before { content: \"\\f2a0\"; }\n.ri-volume-up-fill:before { content: \"\\f2a1\"; }\n.ri-volume-up-line:before { content: \"\\f2a2\"; }\n.ri-volume-vibrate-fill:before { content: \"\\f2a3\"; }\n.ri-volume-vibrate-line:before { content: \"\\f2a4\"; }\n.ri-vuejs-fill:before { content: \"\\f2a5\"; }\n.ri-vuejs-line:before { content: \"\\f2a6\"; }\n.ri-walk-fill:before { content: \"\\f2a7\"; }\n.ri-walk-line:before { content: \"\\f2a8\"; }\n.ri-wallet-2-fill:before { content: \"\\f2a9\"; }\n.ri-wallet-2-line:before { content: \"\\f2aa\"; }\n.ri-wallet-3-fill:before { content: \"\\f2ab\"; }\n.ri-wallet-3-line:before { content: \"\\f2ac\"; }\n.ri-wallet-fill:before { content: \"\\f2ad\"; }\n.ri-wallet-line:before { content: \"\\f2ae\"; }\n.ri-water-flash-fill:before { content: \"\\f2af\"; }\n.ri-water-flash-line:before { content: \"\\f2b0\"; }\n.ri-webcam-fill:before { content: \"\\f2b1\"; }\n.ri-webcam-line:before { content: \"\\f2b2\"; }\n.ri-wechat-2-fill:before { content: \"\\f2b3\"; }\n.ri-wechat-2-line:before { content: \"\\f2b4\"; }\n.ri-wechat-fill:before { content: \"\\f2b5\"; }\n.ri-wechat-line:before { content: \"\\f2b6\"; }\n.ri-wechat-pay-fill:before { content: \"\\f2b7\"; }\n.ri-wechat-pay-line:before { content: \"\\f2b8\"; }\n.ri-weibo-fill:before { content: \"\\f2b9\"; }\n.ri-weibo-line:before { content: \"\\f2ba\"; }\n.ri-whatsapp-fill:before { content: \"\\f2bb\"; }\n.ri-whatsapp-line:before { content: \"\\f2bc\"; }\n.ri-wheelchair-fill:before { content: \"\\f2bd\"; }\n.ri-wheelchair-line:before { content: \"\\f2be\"; }\n.ri-wifi-fill:before { content: \"\\f2bf\"; }\n.ri-wifi-line:before { content: \"\\f2c0\"; }\n.ri-wifi-off-fill:before { content: \"\\f2c1\"; }\n.ri-wifi-off-line:before { content: \"\\f2c2\"; }\n.ri-window-2-fill:before { content: \"\\f2c3\"; }\n.ri-window-2-line:before { content: \"\\f2c4\"; }\n.ri-window-fill:before { content: \"\\f2c5\"; }\n.ri-window-line:before { content: \"\\f2c6\"; }\n.ri-windows-fill:before { content: \"\\f2c7\"; }\n.ri-windows-line:before { content: \"\\f2c8\"; }\n.ri-windy-fill:before { content: \"\\f2c9\"; }\n.ri-windy-line:before { content: \"\\f2ca\"; }\n.ri-wireless-charging-fill:before { content: \"\\f2cb\"; }\n.ri-wireless-charging-line:before { content: \"\\f2cc\"; }\n.ri-women-fill:before { content: \"\\f2cd\"; }\n.ri-women-line:before { content: \"\\f2ce\"; }\n.ri-wubi-input:before { content: \"\\f2cf\"; }\n.ri-xbox-fill:before { content: \"\\f2d0\"; }\n.ri-xbox-line:before { content: \"\\f2d1\"; }\n.ri-xing-fill:before { content: \"\\f2d2\"; }\n.ri-xing-line:before { content: \"\\f2d3\"; }\n.ri-youtube-fill:before { content: \"\\f2d4\"; }\n.ri-youtube-line:before { content: \"\\f2d5\"; }\n.ri-zcool-fill:before { content: \"\\f2d6\"; }\n.ri-zcool-line:before { content: \"\\f2d7\"; }\n.ri-zhihu-fill:before { content: \"\\f2d8\"; }\n.ri-zhihu-line:before { content: \"\\f2d9\"; }\n.ri-zoom-in-fill:before { content: \"\\f2da\"; }\n.ri-zoom-in-line:before { content: \"\\f2db\"; }\n.ri-zoom-out-fill:before { content: \"\\f2dc\"; }\n.ri-zoom-out-line:before { content: \"\\f2dd\"; }\n.ri-zzz-fill:before { content: \"\\f2de\"; }\n.ri-zzz-line:before { content: \"\\f2df\"; }\n\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/simple-datatables/simple-datatables.js",
    "content": "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.simpleDatatables = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n(function (global){(function (){\n\"use strict\";const t=t=>\"[object Object]\"===Object.prototype.toString.call(t),e=e=>{let s=!1;try{s=JSON.parse(e)}catch(t){return!1}return!(null===s||!Array.isArray(s)&&!t(s))&&s},s=(t,e)=>{const s=document.createElement(t);if(e&&\"object\"==typeof e)for(const t in e)\"html\"===t?s.innerHTML=e[t]:s.setAttribute(t,e[t]);return s},i=t=>[\"#text\",\"#comment\"].includes(t.nodeName)?t.data:t.childNodes?t.childNodes.map((t=>i(t))).join(\"\"):\"\",n=function(t){return t.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\")},a=function(t,e){let s=0,i=0;for(;s<t+1;){e[i].hidden||(s+=1),i+=1}return i-1},o=function(t,e){let s=t,i=0;for(;i<t;){e[t].hidden&&(s-=1),i++}return s};function r(t,e,s){var i;return\"#text\"===t.nodeName?i=s.document.createTextNode(t.data):\"#comment\"===t.nodeName?i=s.document.createComment(t.data):(e?i=s.document.createElementNS(\"http://www.w3.org/2000/svg\",t.nodeName):\"svg\"===t.nodeName.toLowerCase()?(i=s.document.createElementNS(\"http://www.w3.org/2000/svg\",\"svg\"),e=!0):i=s.document.createElement(t.nodeName),t.attributes&&Object.entries(t.attributes).forEach((function(t){var e=t[0],s=t[1];return i.setAttribute(e,s)})),t.childNodes&&t.childNodes.forEach((function(t){return i.appendChild(r(t,e,s))})),s.valueDiffing&&(t.value&&(i instanceof HTMLButtonElement||i instanceof HTMLDataElement||i instanceof HTMLInputElement||i instanceof HTMLLIElement||i instanceof HTMLMeterElement||i instanceof HTMLOptionElement||i instanceof HTMLProgressElement||i instanceof HTMLParamElement)&&(i.value=t.value),t.checked&&i instanceof HTMLInputElement&&(i.checked=t.checked),t.selected&&i instanceof HTMLOptionElement&&(i.selected=t.selected))),i}var l=function(t,e){for(e=e.slice();e.length>0;){var s=e.splice(0,1)[0];t=t.childNodes[s]}return t};function d(t,e,s){var i,n,a,o=e[s._const.action],d=e[s._const.route];[s._const.addElement,s._const.addTextElement].includes(o)||(i=l(t,d));var c={diff:e,node:i};if(s.preDiffApply(c))return!0;switch(o){case s._const.addAttribute:if(!(i&&i instanceof Element))return!1;i.setAttribute(e[s._const.name],e[s._const.value]);break;case s._const.modifyAttribute:if(!(i&&i instanceof Element))return!1;i.setAttribute(e[s._const.name],e[s._const.newValue]),i instanceof HTMLInputElement&&\"value\"===e[s._const.name]&&(i.value=e[s._const.newValue]);break;case s._const.removeAttribute:if(!(i&&i instanceof Element))return!1;i.removeAttribute(e[s._const.name]);break;case s._const.modifyTextElement:if(!(i&&i instanceof Text))return!1;s.textDiff(i,i.data,e[s._const.oldValue],e[s._const.newValue]);break;case s._const.modifyValue:if(!i||void 0===i.value)return!1;i.value=e[s._const.newValue];break;case s._const.modifyComment:if(!(i&&i instanceof Comment))return!1;s.textDiff(i,i.data,e[s._const.oldValue],e[s._const.newValue]);break;case s._const.modifyChecked:if(!i||void 0===i.checked)return!1;i.checked=e[s._const.newValue];break;case s._const.modifySelected:if(!i||void 0===i.selected)return!1;i.selected=e[s._const.newValue];break;case s._const.replaceElement:i.parentNode.replaceChild(r(e[s._const.newValue],\"svg\"===e[s._const.newValue].nodeName.toLowerCase(),s),i);break;case s._const.relocateGroup:Array.apply(void 0,new Array(e[s._const.groupLength])).map((function(){return i.removeChild(i.childNodes[e[s._const.from]])})).forEach((function(t,n){0===n&&(a=i.childNodes[e[s._const.to]]),i.insertBefore(t,a||null)}));break;case s._const.removeElement:i.parentNode.removeChild(i);break;case s._const.addElement:var h=(u=d.slice()).splice(u.length-1,1)[0];if(!((i=l(t,u))instanceof Element))return!1;i.insertBefore(r(e[s._const.element],\"http://www.w3.org/2000/svg\"===i.namespaceURI,s),i.childNodes[h]||null);break;case s._const.removeTextElement:if(!i||3!==i.nodeType)return!1;i.parentNode.removeChild(i);break;case s._const.addTextElement:var u;if(h=(u=d.slice()).splice(u.length-1,1)[0],n=s.document.createTextNode(e[s._const.value]),!(i=l(t,u)).childNodes)return!1;i.insertBefore(n,i.childNodes[h]||null);break;default:console.log(\"unknown action\")}return s.postDiffApply({diff:c.diff,node:c.node,newNode:n}),!0}function c(t,e,s){var i=t[e];t[e]=t[s],t[s]=i}var h=function(t){var e=[];return e.push(t.nodeName),\"#text\"!==t.nodeName&&\"#comment\"!==t.nodeName&&t.attributes&&(t.attributes.class&&e.push(\"\".concat(t.nodeName,\".\").concat(t.attributes.class.replace(/ /g,\".\"))),t.attributes.id&&e.push(\"\".concat(t.nodeName,\"#\").concat(t.attributes.id))),e},u=function(t){var e={},s={};return t.forEach((function(t){h(t).forEach((function(t){var i=t in e;i||t in s?i&&(delete e[t],s[t]=!0):e[t]=!0}))})),e},p=function(t,e){var s=u(t),i=u(e),n={};return Object.keys(s).forEach((function(t){i[t]&&(n[t]=!0)})),n},f=function(t){return delete t.outerDone,delete t.innerDone,delete t.valueDone,!t.childNodes||t.childNodes.every(f)},m=function(t){if(Object.prototype.hasOwnProperty.call(t,\"data\"))return{nodeName:\"#text\"===t.nodeName?\"#text\":\"#comment\",data:t.data};var e={nodeName:t.nodeName};return Object.prototype.hasOwnProperty.call(t,\"attributes\")&&(e.attributes=t.attributes),Object.prototype.hasOwnProperty.call(t,\"checked\")&&(e.checked=t.checked),Object.prototype.hasOwnProperty.call(t,\"value\")&&(e.value=t.value),Object.prototype.hasOwnProperty.call(t,\"selected\")&&(e.selected=t.selected),Object.prototype.hasOwnProperty.call(t,\"childNodes\")&&(e.childNodes=t.childNodes.map((function(t){return m(t)}))),e},g=function(t,e){if(![\"nodeName\",\"value\",\"checked\",\"selected\",\"data\"].every((function(s){return t[s]===e[s]})))return!1;if(Object.prototype.hasOwnProperty.call(t,\"data\"))return!0;if(Boolean(t.attributes)!==Boolean(e.attributes))return!1;if(Boolean(t.childNodes)!==Boolean(e.childNodes))return!1;if(t.attributes){var s=Object.keys(t.attributes),i=Object.keys(e.attributes);if(s.length!==i.length)return!1;if(!s.every((function(s){return t.attributes[s]===e.attributes[s]})))return!1}if(t.childNodes){if(t.childNodes.length!==e.childNodes.length)return!1;if(!t.childNodes.every((function(t,s){return g(t,e.childNodes[s])})))return!1}return!0},b=function(t,e,s,i,n){if(void 0===n&&(n=!1),!t||!e)return!1;if(t.nodeName!==e.nodeName)return!1;if([\"#text\",\"#comment\"].includes(t.nodeName))return!!n||t.data===e.data;if(t.nodeName in s)return!0;if(t.attributes&&e.attributes){if(t.attributes.id){if(t.attributes.id!==e.attributes.id)return!1;if(\"\".concat(t.nodeName,\"#\").concat(t.attributes.id)in s)return!0}if(t.attributes.class&&t.attributes.class===e.attributes.class&&\"\".concat(t.nodeName,\".\").concat(t.attributes.class.replace(/ /g,\".\"))in s)return!0}if(i)return!0;var a=t.childNodes?t.childNodes.slice().reverse():[],o=e.childNodes?e.childNodes.slice().reverse():[];if(a.length!==o.length)return!1;if(n)return a.every((function(t,e){return t.nodeName===o[e].nodeName}));var r=p(a,o);return a.every((function(t,e){return b(t,o[e],r,!0,!0)}))},v=function(t,e){return Array.apply(void 0,new Array(t)).map((function(){return e}))},_=function(t,e){for(var s=t.childNodes?t.childNodes:[],i=e.childNodes?e.childNodes:[],n=v(s.length,!1),a=v(i.length,!1),o=[],r=function(){return arguments[1]},l=!1,d=function(){var t=function(t,e,s,i){var n=0,a=[],o=t.length,r=e.length,l=Array.apply(void 0,new Array(o+1)).map((function(){return[]})),d=p(t,e),c=o===r;c&&t.some((function(t,s){var i=h(t),n=h(e[s]);return i.length!==n.length?(c=!1,!0):(i.some((function(t,e){if(t!==n[e])return c=!1,!0})),!c||void 0)}));for(var u=0;u<o;u++)for(var f=t[u],m=0;m<r;m++){var g=e[m];s[u]||i[m]||!b(f,g,d,c)?l[u+1][m+1]=0:(l[u+1][m+1]=l[u][m]?l[u][m]+1:1,l[u+1][m+1]>=n&&(n=l[u+1][m+1],a=[u+1,m+1]))}return 0!==n&&{oldValue:a[0]-n,newValue:a[1]-n,length:n}}(s,i,n,a);t?(o.push(t),Array.apply(void 0,new Array(t.length)).map(r).forEach((function(e){return function(t,e,s,i){t[s.oldValue+i]=!0,e[s.newValue+i]=!0}(n,a,t,e)}))):l=!0};!l;)d();return t.subsets=o,t.subsetsAge=100,o},w=function(){function t(){this.list=[]}return t.prototype.add=function(t){var e;(e=this.list).push.apply(e,t)},t.prototype.forEach=function(t){this.list.forEach((function(e){return t(e)}))},t}(),y=function(){function t(t){void 0===t&&(t={});var e=this;Object.entries(t).forEach((function(t){var s=t[0],i=t[1];return e[s]=i}))}return t.prototype.toString=function(){return JSON.stringify(this)},t.prototype.setValue=function(t,e){return this[t]=e,this},t}();function x(t,e){var s,i,n=t;for(e=e.slice();e.length>0;)i=e.splice(0,1)[0],s=n,n=n.childNodes?n.childNodes[i]:void 0;return{node:n,parentNode:s,nodeIndex:i}}function N(t,e,s){return e.forEach((function(e){!function(t,e,s){var i,n,a,o;if(![s._const.addElement,s._const.addTextElement].includes(e[s._const.action])){var r=x(t,e[s._const.route]);n=r.node,a=r.parentNode,o=r.nodeIndex}var l,d,c=[],h={diff:e,node:n};if(s.preVirtualDiffApply(h))return!0;switch(e[s._const.action]){case s._const.addAttribute:n.attributes||(n.attributes={}),n.attributes[e[s._const.name]]=e[s._const.value],\"checked\"===e[s._const.name]?n.checked=!0:\"selected\"===e[s._const.name]?n.selected=!0:\"INPUT\"===n.nodeName&&\"value\"===e[s._const.name]&&(n.value=e[s._const.value]);break;case s._const.modifyAttribute:n.attributes[e[s._const.name]]=e[s._const.newValue];break;case s._const.removeAttribute:delete n.attributes[e[s._const.name]],0===Object.keys(n.attributes).length&&delete n.attributes,\"checked\"===e[s._const.name]?n.checked=!1:\"selected\"===e[s._const.name]?delete n.selected:\"INPUT\"===n.nodeName&&\"value\"===e[s._const.name]&&delete n.value;break;case s._const.modifyTextElement:n.data=e[s._const.newValue];break;case s._const.modifyValue:n.value=e[s._const.newValue];break;case s._const.modifyComment:n.data=e[s._const.newValue];break;case s._const.modifyChecked:n.checked=e[s._const.newValue];break;case s._const.modifySelected:n.selected=e[s._const.newValue];break;case s._const.replaceElement:l=e[s._const.newValue],a.childNodes[o]=l;break;case s._const.relocateGroup:n.childNodes.splice(e[s._const.from],e[s._const.groupLength]).reverse().forEach((function(t){return n.childNodes.splice(e[s._const.to],0,t)})),n.subsets&&n.subsets.forEach((function(t){if(e[s._const.from]<e[s._const.to]&&t.oldValue<=e[s._const.to]&&t.oldValue>e[s._const.from])t.oldValue-=e[s._const.groupLength],(i=t.oldValue+t.length-e[s._const.to])>0&&(c.push({oldValue:e[s._const.to]+e[s._const.groupLength],newValue:t.newValue+t.length-i,length:i}),t.length-=i);else if(e[s._const.from]>e[s._const.to]&&t.oldValue>e[s._const.to]&&t.oldValue<e[s._const.from]){var i;t.oldValue+=e[s._const.groupLength],(i=t.oldValue+t.length-e[s._const.to])>0&&(c.push({oldValue:e[s._const.to]+e[s._const.groupLength],newValue:t.newValue+t.length-i,length:i}),t.length-=i)}else t.oldValue===e[s._const.from]&&(t.oldValue=e[s._const.to])}));break;case s._const.removeElement:a.childNodes.splice(o,1),a.subsets&&a.subsets.forEach((function(t){t.oldValue>o?t.oldValue-=1:t.oldValue===o?t.delete=!0:t.oldValue<o&&t.oldValue+t.length>o&&(t.oldValue+t.length-1===o?t.length--:(c.push({newValue:t.newValue+o-t.oldValue,oldValue:o,length:t.length-o+t.oldValue-1}),t.length=o-t.oldValue))})),n=a;break;case s._const.addElement:var u=(d=e[s._const.route].slice()).splice(d.length-1,1)[0];n=null===(i=x(t,d))||void 0===i?void 0:i.node,l=e[s._const.element],n.childNodes||(n.childNodes=[]),u>=n.childNodes.length?n.childNodes.push(l):n.childNodes.splice(u,0,l),n.subsets&&n.subsets.forEach((function(t){if(t.oldValue>=u)t.oldValue+=1;else if(t.oldValue<u&&t.oldValue+t.length>u){var e=t.oldValue+t.length-u;c.push({newValue:t.newValue+t.length-e,oldValue:u+1,length:e}),t.length-=e}}));break;case s._const.removeTextElement:a.childNodes.splice(o,1),\"TEXTAREA\"===a.nodeName&&delete a.value,a.subsets&&a.subsets.forEach((function(t){t.oldValue>o?t.oldValue-=1:t.oldValue===o?t.delete=!0:t.oldValue<o&&t.oldValue+t.length>o&&(t.oldValue+t.length-1===o?t.length--:(c.push({newValue:t.newValue+o-t.oldValue,oldValue:o,length:t.length-o+t.oldValue-1}),t.length=o-t.oldValue))})),n=a;break;case s._const.addTextElement:var p=(d=e[s._const.route].slice()).splice(d.length-1,1)[0];(l={}).nodeName=\"#text\",l.data=e[s._const.value],(n=x(t,d).node).childNodes||(n.childNodes=[]),p>=n.childNodes.length?n.childNodes.push(l):n.childNodes.splice(p,0,l),\"TEXTAREA\"===n.nodeName&&(n.value=e[s._const.newValue]),n.subsets&&n.subsets.forEach((function(t){if(t.oldValue>=p&&(t.oldValue+=1),t.oldValue<p&&t.oldValue+t.length>p){var e=t.oldValue+t.length-p;c.push({newValue:t.newValue+t.length-e,oldValue:p+1,length:e}),t.length-=e}}));break;default:console.log(\"unknown action\")}n.subsets&&(n.subsets=n.subsets.filter((function(t){return!t.delete&&t.oldValue!==t.newValue})),c.length&&(n.subsets=n.subsets.concat(c))),s.postVirtualDiffApply({node:h.node,diff:h.diff,newNode:l})}(t,e,s)})),!0}function D(t,e){void 0===e&&(e={});var s={nodeName:t.nodeName};return t instanceof Text||t instanceof Comment?s.data=t.data:(t.attributes&&t.attributes.length>0&&(s.attributes={},Array.prototype.slice.call(t.attributes).forEach((function(t){return s.attributes[t.name]=t.value}))),t instanceof HTMLTextAreaElement?s.value=t.value:t.childNodes&&t.childNodes.length>0&&(s.childNodes=[],Array.prototype.slice.call(t.childNodes).forEach((function(t){return s.childNodes.push(D(t,e))}))),e.valueDiffing&&(t instanceof HTMLInputElement&&[\"radio\",\"checkbox\"].includes(t.type.toLowerCase())&&void 0!==t.checked?s.checked=t.checked:(t instanceof HTMLButtonElement||t instanceof HTMLDataElement||t instanceof HTMLInputElement||t instanceof HTMLLIElement||t instanceof HTMLMeterElement||t instanceof HTMLOptionElement||t instanceof HTMLProgressElement||t instanceof HTMLParamElement)&&(s.value=t.value),t instanceof HTMLOptionElement&&(s.selected=t.selected))),s}var M=/<\\s*\\/*[a-zA-Z:_][a-zA-Z0-9:_\\-.]*\\s*(?:\"[^\"]*\"['\"]*|'[^']*'['\"]*|[^'\"/>])*\\/*\\s*>|<!--(?:.|\\n|\\r)*?-->/g,E=Object.create?Object.create(null):{},O=/\\s([^'\"/\\s><]+?)[\\s/>]|([^\\s=]+)=\\s?(\".*?\"|'.*?')/g;function V(t){return t.replace(/&lt;/g,\"<\").replace(/&gt;/g,\">\").replace(/&amp;/g,\"&\")}var $={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,menuItem:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},C=function(t){var e={nodeName:\"\",attributes:{}},s=!1,i=t.match(/<\\/?([^\\s]+?)[/\\s>]/);if(i&&(e.nodeName=i[1].toUpperCase(),($[i[1]]||\"/\"===t.charAt(t.length-2))&&(s=!0),e.nodeName.startsWith(\"!--\"))){var n=t.indexOf(\"--\\x3e\");return{type:\"comment\",node:{nodeName:\"#comment\",data:-1!==n?t.slice(4,n):\"\"},voidElement:s}}for(var a=new RegExp(O),o=null,r=!1;!r;)if(null===(o=a.exec(t)))r=!0;else if(o[0].trim())if(o[1]){var l=o[1].trim(),d=[l,\"\"];l.indexOf(\"=\")>-1&&(d=l.split(\"=\")),e.attributes[d[0]]=d[1],a.lastIndex--}else o[2]&&(e.attributes[o[2]]=o[3].trim().substring(1,o[3].length-1));return{type:\"tag\",node:e,voidElement:s}},L=function(t,e){void 0===e&&(e={components:E});var s,i=[],n=-1,a=[],o=!1;if(0!==t.indexOf(\"<\")){var r=t.indexOf(\"<\");i.push({nodeName:\"#text\",data:-1===r?t:t.substring(0,r)})}return t.replace(M,(function(r,l){if(o){if(r!==\"</\".concat(s.node.nodeName,\">\"))return\"\";o=!1}var d=\"/\"!==r.charAt(1),c=r.startsWith(\"\\x3c!--\"),h=l+r.length,u=t.charAt(h);if(c){var p=C(r).node;if(n<0)return i.push(p),\"\";var f=a[n];return f&&p.nodeName&&(f.node.childNodes||(f.node.childNodes=[]),f.node.childNodes.push(p)),\"\"}if(d){s=C(r),n++,\"tag\"===s.type&&e.components[s.node.nodeName]&&(s.type=\"component\",o=!0),s.voidElement||o||!u||\"<\"===u||(s.node.childNodes||(s.node.childNodes=[]),s.node.childNodes.push({nodeName:\"#text\",data:V(t.slice(h,t.indexOf(\"<\",h)))})),0===n&&s.node.nodeName&&i.push(s.node);var m=a[n-1];m&&s.node.nodeName&&(m.node.childNodes||(m.node.childNodes=[]),m.node.childNodes.push(s.node)),a[n]=s}if((!d||s.voidElement)&&(n>-1&&(s.voidElement||s.node.nodeName===r.slice(2,-1).toUpperCase())&&--n>-1&&(s=a[n]),!o&&\"<\"!==u&&u)){var g=-1===n?i:a[n].node.childNodes||[],b=t.indexOf(\"<\",h),v=V(t.slice(h,-1===b?void 0:b));g.push({nodeName:\"#text\",data:v})}return\"\"})),i[0]},S=function(){function t(t,e,s){this.options=s,this.t1=\"undefined\"!=typeof Element&&t instanceof Element?D(t,this.options):\"string\"==typeof t?L(t,this.options):JSON.parse(JSON.stringify(t)),this.t2=\"undefined\"!=typeof Element&&e instanceof Element?D(e,this.options):\"string\"==typeof e?L(e,this.options):JSON.parse(JSON.stringify(e)),this.diffcount=0,this.foundAll=!1,this.debug&&(this.t1Orig=\"undefined\"!=typeof Element&&t instanceof Element?D(t,this.options):\"string\"==typeof t?L(t,this.options):JSON.parse(JSON.stringify(t)),this.t2Orig=\"undefined\"!=typeof Element&&e instanceof Element?D(e,this.options):\"string\"==typeof e?L(e,this.options):JSON.parse(JSON.stringify(e))),this.tracker=new w}return t.prototype.init=function(){return this.findDiffs(this.t1,this.t2)},t.prototype.findDiffs=function(t,e){var s;do{if(this.options.debug&&(this.diffcount+=1,this.diffcount>this.options.diffcap))throw new Error(\"surpassed diffcap:\".concat(JSON.stringify(this.t1Orig),\" -> \").concat(JSON.stringify(this.t2Orig)));0===(s=this.findNextDiff(t,e,[])).length&&(g(t,e)||(this.foundAll?console.error(\"Could not find remaining diffs!\"):(this.foundAll=!0,f(t),s=this.findNextDiff(t,e,[])))),s.length>0&&(this.foundAll=!1,this.tracker.add(s),N(t,s,this.options))}while(s.length>0);return this.tracker.list},t.prototype.findNextDiff=function(t,e,s){var i,n;if(this.options.maxDepth&&s.length>this.options.maxDepth)return[];if(!t.outerDone){if(i=this.findOuterDiff(t,e,s),this.options.filterOuterDiff&&(n=this.options.filterOuterDiff(t,e,i))&&(i=n),i.length>0)return t.outerDone=!0,i;t.outerDone=!0}if(Object.prototype.hasOwnProperty.call(t,\"data\"))return[];if(!t.innerDone){if((i=this.findInnerDiff(t,e,s)).length>0)return i;t.innerDone=!0}if(this.options.valueDiffing&&!t.valueDone){if((i=this.findValueDiff(t,e,s)).length>0)return t.valueDone=!0,i;t.valueDone=!0}return[]},t.prototype.findOuterDiff=function(t,e,s){var i,n,a,o,r,l,d=[];if(t.nodeName!==e.nodeName){if(!s.length)throw new Error(\"Top level nodes have to be of the same kind.\");return[(new y).setValue(this.options._const.action,this.options._const.replaceElement).setValue(this.options._const.oldValue,m(t)).setValue(this.options._const.newValue,m(e)).setValue(this.options._const.route,s)]}if(s.length&&this.options.diffcap<Math.abs((t.childNodes||[]).length-(e.childNodes||[]).length))return[(new y).setValue(this.options._const.action,this.options._const.replaceElement).setValue(this.options._const.oldValue,m(t)).setValue(this.options._const.newValue,m(e)).setValue(this.options._const.route,s)];if(Object.prototype.hasOwnProperty.call(t,\"data\")&&t.data!==e.data)return\"#text\"===t.nodeName?[(new y).setValue(this.options._const.action,this.options._const.modifyTextElement).setValue(this.options._const.route,s).setValue(this.options._const.oldValue,t.data).setValue(this.options._const.newValue,e.data)]:[(new y).setValue(this.options._const.action,this.options._const.modifyComment).setValue(this.options._const.route,s).setValue(this.options._const.oldValue,t.data).setValue(this.options._const.newValue,e.data)];for(n=t.attributes?Object.keys(t.attributes).sort():[],a=e.attributes?Object.keys(e.attributes).sort():[],o=n.length,l=0;l<o;l++)i=n[l],-1===(r=a.indexOf(i))?d.push((new y).setValue(this.options._const.action,this.options._const.removeAttribute).setValue(this.options._const.route,s).setValue(this.options._const.name,i).setValue(this.options._const.value,t.attributes[i])):(a.splice(r,1),t.attributes[i]!==e.attributes[i]&&d.push((new y).setValue(this.options._const.action,this.options._const.modifyAttribute).setValue(this.options._const.route,s).setValue(this.options._const.name,i).setValue(this.options._const.oldValue,t.attributes[i]).setValue(this.options._const.newValue,e.attributes[i])));for(o=a.length,l=0;l<o;l++)i=a[l],d.push((new y).setValue(this.options._const.action,this.options._const.addAttribute).setValue(this.options._const.route,s).setValue(this.options._const.name,i).setValue(this.options._const.value,e.attributes[i]));return d},t.prototype.findInnerDiff=function(t,e,s){var i=t.childNodes?t.childNodes.slice():[],n=e.childNodes?e.childNodes.slice():[],a=Math.max(i.length,n.length),o=Math.abs(i.length-n.length),r=[],l=0;if(!this.options.maxChildCount||a<this.options.maxChildCount){var d=Boolean(t.subsets&&t.subsetsAge--),c=d?t.subsets:t.childNodes&&e.childNodes?_(t,e):[];if(c.length>0&&(r=this.attemptGroupRelocation(t,e,c,s,d)).length>0)return r}for(var h=0;h<a;h+=1){var u=i[h],p=n[h];if(o&&(u&&!p?\"#text\"===u.nodeName?(r.push((new y).setValue(this.options._const.action,this.options._const.removeTextElement).setValue(this.options._const.route,s.concat(l)).setValue(this.options._const.value,u.data)),l-=1):(r.push((new y).setValue(this.options._const.action,this.options._const.removeElement).setValue(this.options._const.route,s.concat(l)).setValue(this.options._const.element,m(u))),l-=1):p&&!u&&(\"#text\"===p.nodeName?r.push((new y).setValue(this.options._const.action,this.options._const.addTextElement).setValue(this.options._const.route,s.concat(l)).setValue(this.options._const.value,p.data)):r.push((new y).setValue(this.options._const.action,this.options._const.addElement).setValue(this.options._const.route,s.concat(l)).setValue(this.options._const.element,m(p))))),u&&p)if(!this.options.maxChildCount||a<this.options.maxChildCount)r=r.concat(this.findNextDiff(u,p,s.concat(l)));else if(!g(u,p))if(i.length>n.length)\"#text\"===u.nodeName?r.push((new y).setValue(this.options._const.action,this.options._const.removeTextElement).setValue(this.options._const.route,s.concat(l)).setValue(this.options._const.value,u.data)):r.push((new y).setValue(this.options._const.action,this.options._const.removeElement).setValue(this.options._const.element,m(u)).setValue(this.options._const.route,s.concat(l))),i.splice(h,1),h-=1,l-=1,o-=1;else if(i.length<n.length){var f=m(p);r=r.concat([(new y).setValue(this.options._const.action,this.options._const.addElement).setValue(this.options._const.element,f).setValue(this.options._const.route,s.concat(l))]),i.splice(h,0,f),o-=1}else r=r.concat([(new y).setValue(this.options._const.action,this.options._const.replaceElement).setValue(this.options._const.oldValue,m(u)).setValue(this.options._const.newValue,m(p)).setValue(this.options._const.route,s.concat(l))]);l+=1}return t.innerDone=!0,r},t.prototype.attemptGroupRelocation=function(t,e,s,i,n){for(var a,o,r,l,d,c,h=function(t,e,s){var i=t.childNodes?v(t.childNodes.length,!0):[],n=e.childNodes?v(e.childNodes.length,!0):[],a=0;return s.forEach((function(t){for(var e=t.oldValue+t.length,s=t.newValue+t.length,o=t.oldValue;o<e;o+=1)i[o]=a;for(o=t.newValue;o<s;o+=1)n[o]=a;a+=1})),{gaps1:i,gaps2:n}}(t,e,s),u=h.gaps1,p=h.gaps2,f=Math.min(u.length,p.length),g=[],_=0,w=0;_<f;w+=1,_+=1)if(!n||!0!==u[_]&&!0!==p[_])if(!0===u[_])if(\"#text\"===(l=t.childNodes[w]).nodeName)if(\"#text\"===e.childNodes[_].nodeName){if(l.data!==e.childNodes[_].data){for(c=w;t.childNodes.length>c+1&&\"#text\"===t.childNodes[c+1].nodeName;)if(c+=1,e.childNodes[_].data===t.childNodes[c].data){d=!0;break}if(!d)return g.push((new y).setValue(this.options._const.action,this.options._const.modifyTextElement).setValue(this.options._const.route,i.concat(_)).setValue(this.options._const.oldValue,l.data).setValue(this.options._const.newValue,e.childNodes[_].data)),g}}else g.push((new y).setValue(this.options._const.action,this.options._const.removeTextElement).setValue(this.options._const.route,i.concat(_)).setValue(this.options._const.value,l.data)),u.splice(_,1),f=Math.min(u.length,p.length),_-=1;else g.push((new y).setValue(this.options._const.action,this.options._const.removeElement).setValue(this.options._const.route,i.concat(_)).setValue(this.options._const.element,m(l))),u.splice(_,1),f=Math.min(u.length,p.length),_-=1;else if(!0===p[_])\"#text\"===(l=e.childNodes[_]).nodeName?(g.push((new y).setValue(this.options._const.action,this.options._const.addTextElement).setValue(this.options._const.route,i.concat(_)).setValue(this.options._const.value,l.data)),u.splice(_,0,!0),f=Math.min(u.length,p.length),w-=1):(g.push((new y).setValue(this.options._const.action,this.options._const.addElement).setValue(this.options._const.route,i.concat(_)).setValue(this.options._const.element,m(l))),u.splice(_,0,!0),f=Math.min(u.length,p.length),w-=1);else if(u[_]!==p[_]){if(g.length>0)return g;if(r=s[u[_]],(o=Math.min(r.newValue,t.childNodes.length-r.length))!==r.oldValue){a=!1;for(var x=0;x<r.length;x+=1)b(t.childNodes[o+x],t.childNodes[r.oldValue+x],{},!1,!0)||(a=!0);if(a)return[(new y).setValue(this.options._const.action,this.options._const.relocateGroup).setValue(this.options._const.groupLength,r.length).setValue(this.options._const.from,r.oldValue).setValue(this.options._const.to,o).setValue(this.options._const.route,i)]}}return g},t.prototype.findValueDiff=function(t,e,s){var i=[];return t.selected!==e.selected&&i.push((new y).setValue(this.options._const.action,this.options._const.modifySelected).setValue(this.options._const.oldValue,t.selected).setValue(this.options._const.newValue,e.selected).setValue(this.options._const.route,s)),(t.value||e.value)&&t.value!==e.value&&\"OPTION\"!==t.nodeName&&i.push((new y).setValue(this.options._const.action,this.options._const.modifyValue).setValue(this.options._const.oldValue,t.value||\"\").setValue(this.options._const.newValue,e.value||\"\").setValue(this.options._const.route,s)),t.checked!==e.checked&&i.push((new y).setValue(this.options._const.action,this.options._const.modifyChecked).setValue(this.options._const.oldValue,t.checked).setValue(this.options._const.newValue,e.checked).setValue(this.options._const.route,s)),i},t}(),k={debug:!1,diffcap:10,maxDepth:!1,maxChildCount:50,valueDiffing:!0,textDiff:function(t,e,s,i){t.data=i},preVirtualDiffApply:function(){},postVirtualDiffApply:function(){},preDiffApply:function(){},postDiffApply:function(){},filterOuterDiff:null,compress:!1,_const:!1,document:!(\"undefined\"==typeof window||!window.document)&&window.document,components:[]},T=function(){function t(t){if(void 0===t&&(t={}),Object.entries(k).forEach((function(e){var s=e[0],i=e[1];Object.prototype.hasOwnProperty.call(t,s)||(t[s]=i)})),!t._const){var e=[\"addAttribute\",\"modifyAttribute\",\"removeAttribute\",\"modifyTextElement\",\"relocateGroup\",\"removeElement\",\"addElement\",\"removeTextElement\",\"addTextElement\",\"replaceElement\",\"modifyValue\",\"modifyChecked\",\"modifySelected\",\"modifyComment\",\"action\",\"route\",\"oldValue\",\"newValue\",\"element\",\"group\",\"groupLength\",\"from\",\"to\",\"name\",\"value\",\"data\",\"attributes\",\"nodeName\",\"childNodes\",\"checked\",\"selected\"],s={};t.compress?e.forEach((function(t,e){return s[t]=e})):e.forEach((function(t){return s[t]=t})),t._const=s}this.options=t}return t.prototype.apply=function(t,e){return function(t,e,s){return e.every((function(e){return d(t,e,s)}))}(t,e,this.options)},t.prototype.undo=function(t,e){return function(t,e,s){(e=e.slice()).reverse(),e.forEach((function(e){!function(t,e,s){switch(e[s._const.action]){case s._const.addAttribute:e[s._const.action]=s._const.removeAttribute,d(t,e,s);break;case s._const.modifyAttribute:c(e,s._const.oldValue,s._const.newValue),d(t,e,s);break;case s._const.removeAttribute:e[s._const.action]=s._const.addAttribute,d(t,e,s);break;case s._const.modifyTextElement:case s._const.modifyValue:case s._const.modifyComment:case s._const.modifyChecked:case s._const.modifySelected:case s._const.replaceElement:c(e,s._const.oldValue,s._const.newValue),d(t,e,s);break;case s._const.relocateGroup:c(e,s._const.from,s._const.to),d(t,e,s);break;case s._const.removeElement:e[s._const.action]=s._const.addElement,d(t,e,s);break;case s._const.addElement:e[s._const.action]=s._const.removeElement,d(t,e,s);break;case s._const.removeTextElement:e[s._const.action]=s._const.addTextElement,d(t,e,s);break;case s._const.addTextElement:e[s._const.action]=s._const.removeTextElement,d(t,e,s);break;default:console.log(\"unknown action\")}}(t,e,s)}))}(t,e,this.options)},t.prototype.diff=function(t,e){return new S(t,e,this.options).init()},t}();const A=(t,e,s,{classes:i,format:n,hiddenHeader:a,sortable:o,scrollY:r,type:l},{noColumnWidths:d,unhideHeader:c})=>({nodeName:\"TR\",childNodes:t.map(((t,h)=>{const u=e[h]||{type:l,format:n,sortable:!0,searchable:!0};if(u.hidden)return;const p={};if(u.sortable&&o&&(!r.length||c)&&(u.filter?p[\"data-filterable\"]=\"true\":p[\"data-sortable\"]=\"true\"),u.headerClass&&(p.class=u.headerClass),s.sort&&s.sort.column===h){const t=\"asc\"===s.sort.dir?i.ascending:i.descending;p.class=p.class?`${p.class} ${t}`:t,p[\"aria-sort\"]=\"asc\"===s.sort.dir?\"ascending\":\"descending\"}else s.filters[h]&&(p.class=p.class?`${p.class} ${i.filterActive}`:i.filterActive);let f=\"\";s.widths[h]&&!d&&(f+=`width: ${s.widths[h]}%;`),r.length&&!c&&(f+=\"padding-bottom: 0;padding-top: 0;border: 0;\"),f.length&&(p.style=f),u.headerClass&&(p.class=u.headerClass);const m=\"html\"===t.type?t.data:[{nodeName:\"#text\",data:t.text??String(t.data)}];return{nodeName:\"TH\",attributes:p,childNodes:!a&&!r.length||c?u.sortable&&o?[{nodeName:\"a\",attributes:{href:\"#\",class:u.filter?i.filter:i.sorter},childNodes:m}]:m:[{nodeName:\"#text\",data:\"\"}]}})).filter((t=>t))}),P=(t,e,s,i,n,a,{classes:o,hiddenHeader:r,header:l,footer:d,format:c,sortable:h,scrollY:u,type:p,rowRender:f,tabIndex:m},{noColumnWidths:g,unhideHeader:b,renderHeader:v})=>{const _={nodeName:\"TABLE\",attributes:{...t},childNodes:[{nodeName:\"TBODY\",childNodes:s.map((({row:t,index:e})=>{const s={nodeName:\"TR\",attributes:{\"data-index\":String(e)},childNodes:t.map(((t,s)=>{const a=i[s]||{type:p,format:c,sortable:!0,searchable:!0};if(a.hidden)return;const o=\"html\"===a.type?{nodeName:\"TD\",childNodes:t.data}:{nodeName:\"TD\",childNodes:[{nodeName:\"#text\",data:t.text??String(t.data)}]};if(l||d||!n.widths[s]||g||(o.attributes={style:`width: ${n.widths[s]}%;`}),a.cellClass&&(o.attributes||(o.attributes={}),o.attributes.class=a.cellClass),a.render){const i=a.render(t.data,o,e,s);if(i){if(\"string\"!=typeof i)return i;{const t=L(`<td>${i}</td>`);1===t.childNodes.length&&[\"#text\",\"#comment\"].includes(t.childNodes[0].nodeName)?o.childNodes[0].data=i:o.childNodes=t.childNodes}}}return o})).filter((t=>t))};if(e===a&&(s.attributes.class=o.cursor),f){const i=f(t,s,e);if(i){if(\"string\"!=typeof i)return i;{const t=L(`<tr>${i}</tr>`);!t.childNodes||1===t.childNodes.length&&[\"#text\",\"#comment\"].includes(t.childNodes[0].nodeName)?s.childNodes[0].data=i:s.childNodes=t.childNodes}}}return s}))}]};if(_.attributes.class=_.attributes.class?`${_.attributes.class} ${o.table}`:o.table,l||d||v){const t=A(e,i,n,{classes:o,hiddenHeader:r,sortable:h,scrollY:u},{noColumnWidths:g,unhideHeader:b});if(l||v){const e={nodeName:\"THEAD\",childNodes:[t]};!u.length&&!r||b||(e.attributes={style:\"height: 0px;\"}),_.childNodes.unshift(e)}if(d){const e={nodeName:\"TFOOT\",childNodes:[l?structuredClone(t):t]};!u.length&&!r||b||(e.attributes={style:\"height: 0px;\"}),_.childNodes.push(e)}}return!1!==m&&(_.attributes.tabindex=String(m)),_};\"undefined\"!=typeof globalThis?globalThis:\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self&&self;var H={},R={get exports(){return H},set exports(t){H=t}};R.exports=function(){var t=1e3,e=6e4,s=36e5,i=\"millisecond\",n=\"second\",a=\"minute\",o=\"hour\",r=\"day\",l=\"week\",d=\"month\",c=\"quarter\",h=\"year\",u=\"date\",p=\"Invalid Date\",f=/^(\\d{4})[-/]?(\\d{1,2})?[-/]?(\\d{0,2})[Tt\\s]*(\\d{1,2})?:?(\\d{1,2})?:?(\\d{1,2})?[.:]?(\\d+)?$/,m=/\\[([^\\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,g={name:\"en\",weekdays:\"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\"),months:\"January_February_March_April_May_June_July_August_September_October_November_December\".split(\"_\"),ordinal:function(t){var e=[\"th\",\"st\",\"nd\",\"rd\"],s=t%100;return\"[\"+t+(e[(s-20)%10]||e[s]||e[0])+\"]\"}},b=function(t,e,s){var i=String(t);return!i||i.length>=e?t:\"\"+Array(e+1-i.length).join(s)+t},v={s:b,z:function(t){var e=-t.utcOffset(),s=Math.abs(e),i=Math.floor(s/60),n=s%60;return(e<=0?\"+\":\"-\")+b(i,2,\"0\")+\":\"+b(n,2,\"0\")},m:function t(e,s){if(e.date()<s.date())return-t(s,e);var i=12*(s.year()-e.year())+(s.month()-e.month()),n=e.clone().add(i,d),a=s-n<0,o=e.clone().add(i+(a?-1:1),d);return+(-(i+(s-n)/(a?n-o:o-n))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return{M:d,y:h,w:l,d:r,D:u,h:o,m:a,s:n,ms:i,Q:c}[t]||String(t||\"\").toLowerCase().replace(/s$/,\"\")},u:function(t){return void 0===t}},_=\"en\",w={};w[_]=g;var y=function(t){return t instanceof M},x=function t(e,s,i){var n;if(!e)return _;if(\"string\"==typeof e){var a=e.toLowerCase();w[a]&&(n=a),s&&(w[a]=s,n=a);var o=e.split(\"-\");if(!n&&o.length>1)return t(o[0])}else{var r=e.name;w[r]=e,n=r}return!i&&n&&(_=n),n||!i&&_},N=function(t,e){if(y(t))return t.clone();var s=\"object\"==typeof e?e:{};return s.date=t,s.args=arguments,new M(s)},D=v;D.l=x,D.i=y,D.w=function(t,e){return N(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var M=function(){function g(t){this.$L=x(t.locale,null,!0),this.parse(t)}var b=g.prototype;return b.parse=function(t){this.$d=function(t){var e=t.date,s=t.utc;if(null===e)return new Date(NaN);if(D.u(e))return new Date;if(e instanceof Date)return new Date(e);if(\"string\"==typeof e&&!/Z$/i.test(e)){var i=e.match(f);if(i){var n=i[2]-1||0,a=(i[7]||\"0\").substring(0,3);return s?new Date(Date.UTC(i[1],n,i[3]||1,i[4]||0,i[5]||0,i[6]||0,a)):new Date(i[1],n,i[3]||1,i[4]||0,i[5]||0,i[6]||0,a)}}return new Date(e)}(t),this.$x=t.x||{},this.init()},b.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},b.$utils=function(){return D},b.isValid=function(){return!(this.$d.toString()===p)},b.isSame=function(t,e){var s=N(t);return this.startOf(e)<=s&&s<=this.endOf(e)},b.isAfter=function(t,e){return N(t)<this.startOf(e)},b.isBefore=function(t,e){return this.endOf(e)<N(t)},b.$g=function(t,e,s){return D.u(t)?this[e]:this.set(s,t)},b.unix=function(){return Math.floor(this.valueOf()/1e3)},b.valueOf=function(){return this.$d.getTime()},b.startOf=function(t,e){var s=this,i=!!D.u(e)||e,c=D.p(t),p=function(t,e){var n=D.w(s.$u?Date.UTC(s.$y,e,t):new Date(s.$y,e,t),s);return i?n:n.endOf(r)},f=function(t,e){return D.w(s.toDate()[t].apply(s.toDate(\"s\"),(i?[0,0,0,0]:[23,59,59,999]).slice(e)),s)},m=this.$W,g=this.$M,b=this.$D,v=\"set\"+(this.$u?\"UTC\":\"\");switch(c){case h:return i?p(1,0):p(31,11);case d:return i?p(1,g):p(0,g+1);case l:var _=this.$locale().weekStart||0,w=(m<_?m+7:m)-_;return p(i?b-w:b+(6-w),g);case r:case u:return f(v+\"Hours\",0);case o:return f(v+\"Minutes\",1);case a:return f(v+\"Seconds\",2);case n:return f(v+\"Milliseconds\",3);default:return this.clone()}},b.endOf=function(t){return this.startOf(t,!1)},b.$set=function(t,e){var s,l=D.p(t),c=\"set\"+(this.$u?\"UTC\":\"\"),p=(s={},s[r]=c+\"Date\",s[u]=c+\"Date\",s[d]=c+\"Month\",s[h]=c+\"FullYear\",s[o]=c+\"Hours\",s[a]=c+\"Minutes\",s[n]=c+\"Seconds\",s[i]=c+\"Milliseconds\",s)[l],f=l===r?this.$D+(e-this.$W):e;if(l===d||l===h){var m=this.clone().set(u,1);m.$d[p](f),m.init(),this.$d=m.set(u,Math.min(this.$D,m.daysInMonth())).$d}else p&&this.$d[p](f);return this.init(),this},b.set=function(t,e){return this.clone().$set(t,e)},b.get=function(t){return this[D.p(t)]()},b.add=function(i,c){var u,p=this;i=Number(i);var f=D.p(c),m=function(t){var e=N(p);return D.w(e.date(e.date()+Math.round(t*i)),p)};if(f===d)return this.set(d,this.$M+i);if(f===h)return this.set(h,this.$y+i);if(f===r)return m(1);if(f===l)return m(7);var g=(u={},u[a]=e,u[o]=s,u[n]=t,u)[f]||1,b=this.$d.getTime()+i*g;return D.w(b,this)},b.subtract=function(t,e){return this.add(-1*t,e)},b.format=function(t){var e=this,s=this.$locale();if(!this.isValid())return s.invalidDate||p;var i=t||\"YYYY-MM-DDTHH:mm:ssZ\",n=D.z(this),a=this.$H,o=this.$m,r=this.$M,l=s.weekdays,d=s.months,c=function(t,s,n,a){return t&&(t[s]||t(e,i))||n[s].slice(0,a)},h=function(t){return D.s(a%12||12,t,\"0\")},u=s.meridiem||function(t,e,s){var i=t<12?\"AM\":\"PM\";return s?i.toLowerCase():i},f={YY:String(this.$y).slice(-2),YYYY:this.$y,M:r+1,MM:D.s(r+1,2,\"0\"),MMM:c(s.monthsShort,r,d,3),MMMM:c(d,r),D:this.$D,DD:D.s(this.$D,2,\"0\"),d:String(this.$W),dd:c(s.weekdaysMin,this.$W,l,2),ddd:c(s.weekdaysShort,this.$W,l,3),dddd:l[this.$W],H:String(a),HH:D.s(a,2,\"0\"),h:h(1),hh:h(2),a:u(a,o,!0),A:u(a,o,!1),m:String(o),mm:D.s(o,2,\"0\"),s:String(this.$s),ss:D.s(this.$s,2,\"0\"),SSS:D.s(this.$ms,3,\"0\"),Z:n};return i.replace(m,(function(t,e){return e||f[t]||n.replace(\":\",\"\")}))},b.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},b.diff=function(i,u,p){var f,m=D.p(u),g=N(i),b=(g.utcOffset()-this.utcOffset())*e,v=this-g,_=D.m(this,g);return _=(f={},f[h]=_/12,f[d]=_,f[c]=_/3,f[l]=(v-b)/6048e5,f[r]=(v-b)/864e5,f[o]=v/s,f[a]=v/e,f[n]=v/t,f)[m]||v,p?_:D.a(_)},b.daysInMonth=function(){return this.endOf(d).$D},b.$locale=function(){return w[this.$L]},b.locale=function(t,e){if(!t)return this.$L;var s=this.clone(),i=x(t,e,!0);return i&&(s.$L=i),s},b.clone=function(){return D.w(this.$d,this)},b.toDate=function(){return new Date(this.valueOf())},b.toJSON=function(){return this.isValid()?this.toISOString():null},b.toISOString=function(){return this.$d.toISOString()},b.toString=function(){return this.$d.toUTCString()},g}(),E=M.prototype;return N.prototype=E,[[\"$ms\",i],[\"$s\",n],[\"$m\",a],[\"$H\",o],[\"$W\",r],[\"$M\",d],[\"$y\",h],[\"$D\",u]].forEach((function(t){E[t[1]]=function(e){return this.$g(e,t[0],t[1])}})),N.extend=function(t,e){return t.$i||(t(e,M,N),t.$i=!0),N},N.locale=x,N.isDayjs=y,N.unix=function(t){return N(1e3*t)},N.en=w[_],N.Ls=w,N.p={},N}();var I=H,Y={},j={get exports(){return Y},set exports(t){Y=t}};j.exports=function(){var t={LTS:\"h:mm:ss A\",LT:\"h:mm A\",L:\"MM/DD/YYYY\",LL:\"MMMM D, YYYY\",LLL:\"MMMM D, YYYY h:mm A\",LLLL:\"dddd, MMMM D, YYYY h:mm A\"},e=/(\\[[^[]*\\])|([-_:/.,()\\s]+)|(A|a|YYYY|YY?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g,s=/\\d\\d/,i=/\\d\\d?/,n=/\\d*[^-_:/,()\\s\\d]+/,a={},o=function(t){return(t=+t)+(t>68?1900:2e3)},r=function(t){return function(e){this[t]=+e}},l=[/[+-]\\d\\d:?(\\d\\d)?|Z/,function(t){(this.zone||(this.zone={})).offset=function(t){if(!t)return 0;if(\"Z\"===t)return 0;var e=t.match(/([+-]|\\d\\d)/g),s=60*e[1]+(+e[2]||0);return 0===s?0:\"+\"===e[0]?-s:s}(t)}],d=function(t){var e=a[t];return e&&(e.indexOf?e:e.s.concat(e.f))},c=function(t,e){var s,i=a.meridiem;if(i){for(var n=1;n<=24;n+=1)if(t.indexOf(i(n,0,e))>-1){s=n>12;break}}else s=t===(e?\"pm\":\"PM\");return s},h={A:[n,function(t){this.afternoon=c(t,!1)}],a:[n,function(t){this.afternoon=c(t,!0)}],S:[/\\d/,function(t){this.milliseconds=100*+t}],SS:[s,function(t){this.milliseconds=10*+t}],SSS:[/\\d{3}/,function(t){this.milliseconds=+t}],s:[i,r(\"seconds\")],ss:[i,r(\"seconds\")],m:[i,r(\"minutes\")],mm:[i,r(\"minutes\")],H:[i,r(\"hours\")],h:[i,r(\"hours\")],HH:[i,r(\"hours\")],hh:[i,r(\"hours\")],D:[i,r(\"day\")],DD:[s,r(\"day\")],Do:[n,function(t){var e=a.ordinal,s=t.match(/\\d+/);if(this.day=s[0],e)for(var i=1;i<=31;i+=1)e(i).replace(/\\[|\\]/g,\"\")===t&&(this.day=i)}],M:[i,r(\"month\")],MM:[s,r(\"month\")],MMM:[n,function(t){var e=d(\"months\"),s=(d(\"monthsShort\")||e.map((function(t){return t.slice(0,3)}))).indexOf(t)+1;if(s<1)throw new Error;this.month=s%12||s}],MMMM:[n,function(t){var e=d(\"months\").indexOf(t)+1;if(e<1)throw new Error;this.month=e%12||e}],Y:[/[+-]?\\d+/,r(\"year\")],YY:[s,function(t){this.year=o(t)}],YYYY:[/\\d{4}/,r(\"year\")],Z:l,ZZ:l};function u(s){var i,n;i=s,n=a&&a.formats;for(var o=(s=i.replace(/(\\[[^\\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(e,s,i){var a=i&&i.toUpperCase();return s||n[i]||t[i]||n[a].replace(/(\\[[^\\]]+])|(MMMM|MM|DD|dddd)/g,(function(t,e,s){return e||s.slice(1)}))}))).match(e),r=o.length,l=0;l<r;l+=1){var d=o[l],c=h[d],u=c&&c[0],p=c&&c[1];o[l]=p?{regex:u,parser:p}:d.replace(/^\\[|\\]$/g,\"\")}return function(t){for(var e={},s=0,i=0;s<r;s+=1){var n=o[s];if(\"string\"==typeof n)i+=n.length;else{var a=n.regex,l=n.parser,d=t.slice(i),c=a.exec(d)[0];l.call(e,c),t=t.replace(c,\"\")}}return function(t){var e=t.afternoon;if(void 0!==e){var s=t.hours;e?s<12&&(t.hours+=12):12===s&&(t.hours=0),delete t.afternoon}}(e),e}}return function(t,e,s){s.p.customParseFormat=!0,t&&t.parseTwoDigitYear&&(o=t.parseTwoDigitYear);var i=e.prototype,n=i.parse;i.parse=function(t){var e=t.date,i=t.utc,o=t.args;this.$u=i;var r=o[1];if(\"string\"==typeof r){var l=!0===o[2],d=!0===o[3],c=l||d,h=o[2];d&&(h=o[2]),a=this.$locale(),!l&&h&&(a=s.Ls[h]),this.$d=function(t,e,s){try{if([\"x\",\"X\"].indexOf(e)>-1)return new Date((\"X\"===e?1e3:1)*t);var i=u(e)(t),n=i.year,a=i.month,o=i.day,r=i.hours,l=i.minutes,d=i.seconds,c=i.milliseconds,h=i.zone,p=new Date,f=o||(n||a?1:p.getDate()),m=n||p.getFullYear(),g=0;n&&!a||(g=a>0?a-1:p.getMonth());var b=r||0,v=l||0,_=d||0,w=c||0;return h?new Date(Date.UTC(m,g,f,b,v,_,w+60*h.offset*1e3)):s?new Date(Date.UTC(m,g,f,b,v,_,w)):new Date(m,g,f,b,v,_,w)}catch(t){return new Date(\"\")}}(e,r,i),this.init(),h&&!0!==h&&(this.$L=this.locale(h).$L),c&&e!=this.format(r)&&(this.$d=new Date(\"\")),a={}}else if(r instanceof Array)for(var p=r.length,f=1;f<=p;f+=1){o[1]=r[f-1];var m=s.apply(this,o);if(m.isValid()){this.$d=m.$d,this.$L=m.$L,this.init();break}f===p&&(this.$d=new Date(\"\"))}else n.call(this,t)}}}();var q=Y;I.extend(q);const B=(t,e)=>{let s;if(e)switch(e){case\"ISO_8601\":s=t;break;case\"RFC_2822\":s=I(t.slice(5),\"DD MMM YYYY HH:mm:ss ZZ\").unix();break;case\"MYSQL\":s=I(t,\"YYYY-MM-DD hh:mm:ss\").unix();break;case\"UNIX\":s=I(t).unix();break;default:s=I(t,e,!0).valueOf()}return s},F=(t,e)=>{if(t?.constructor===Object&&Object.prototype.hasOwnProperty.call(t,\"data\")&&!Object.keys(t).find((t=>![\"text\",\"order\",\"data\"].includes(t))))return t;const s={data:t};switch(e.type){case\"string\":\"string\"!=typeof t&&(s.text=String(s.data),s.order=s.text);break;case\"date\":e.format&&(s.order=B(String(s.data),e.format));break;case\"number\":s.text=String(s.data),s.data=parseInt(s.data,10);break;case\"html\":{const t=Array.isArray(s.data)?{nodeName:\"TD\",childNodes:s.data}:L(`<td>${String(s.data)}</td>`);s.data=t.childNodes||[];const e=i(t);s.text=e,s.order=e;break}case\"boolean\":\"string\"==typeof s.data&&(s.data=s.data.toLowerCase().trim()),s.data=![\"false\",!1,null,void 0,0].includes(s.data),s.order=s.data?1:0,s.text=String(s.data);break;case\"other\":s.text=\"\",s.order=0;break;default:s.text=JSON.stringify(s.data)}return s},z=t=>{if(t instanceof Object&&t.constructor===Object&&t.hasOwnProperty(\"data\")&&(\"string\"==typeof t.text||\"string\"==typeof t.data))return t;const e={data:t};if(\"string\"==typeof t){if(t.length){const s=L(`<th>${t}</th>`);if(s.childNodes&&(1!==s.childNodes.length||\"#text\"!==s.childNodes[0].nodeName)){e.data=s.childNodes,e.type=\"html\";const t=i(s);e.text=t}}}else[null,void 0].includes(t)?e.text=\"\":e.text=JSON.stringify(t);return e},U=(t,e,s,n,a)=>{const o={data:[],headings:[]};t.headings?o.headings=t.headings.map((t=>z(t))):e?.tHead?o.headings=Array.from(e.tHead.querySelectorAll(\"th\")).map(((t,e)=>{const o=(t=>{const e=D(t,{valueDiffing:!1});let s;return s=!e.childNodes||1===e.childNodes.length&&\"#text\"===e.childNodes[0].nodeName?{data:t.innerText,type:\"string\"}:{data:e.childNodes,type:\"html\",text:i(e)},s})(t);s[e]||(s[e]={type:n,format:a,searchable:!0,sortable:!0});const r=s[e];return\"false\"!==t.dataset.sortable?.trim().toLowerCase()&&\"false\"!==t.dataset.sort?.trim().toLowerCase()||(r.sortable=!1),\"false\"===t.dataset.searchable?.trim().toLowerCase()&&(r.searchable=!1),\"true\"!==t.dataset.hidden?.trim().toLowerCase()&&\"true\"!==t.getAttribute(\"hidden\")?.trim().toLowerCase()||(r.hidden=!0),[\"number\",\"string\",\"html\",\"date\",\"boolean\",\"other\"].includes(t.dataset.type)&&(r.type=t.dataset.type,\"date\"===r.type&&t.dataset.format&&(r.format=t.dataset.format)),o})):t.data?.length?o.headings=t.data[0].map((t=>z(\"\"))):e?.tBodies.length&&(o.headings=Array.from(e.tBodies[0].rows[0].cells).map((t=>z(\"\"))));for(let t=0;t<o.headings.length;t++)s[t]||(s[t]={type:n,format:a,sortable:!0,searchable:!0});if(t.data?o.data=t.data.map((t=>t.map(((t,e)=>F(t,s[e]))))):e?.tBodies?.length&&(o.data=Array.from(e.tBodies[0].rows).map((t=>Array.from(t.cells).map(((t,e)=>{const i=t.dataset.content?F(t.dataset.content,s[e]):((t,e)=>{let s;switch(e.type){case\"string\":s={data:t.innerText};break;case\"date\":{const i=t.innerText;s={data:i,order:B(i,e.format)};break}case\"number\":s={data:parseInt(t.innerText,10),text:t.innerText};break;case\"boolean\":{const e=![\"false\",\"0\",\"null\",\"undefined\"].includes(t.innerText.toLowerCase().trim());s={data:e,order:e?1:0,text:e?\"1\":\"0\"};break}default:s={data:D(t,{valueDiffing:!1}).childNodes||[],text:t.innerText,order:t.innerText}}return s})(t,s[e]);return t.dataset.order&&(i.order=isNaN(parseFloat(t.dataset.order))?t.dataset.order:parseFloat(t.dataset.order)),i}))))),o.data.length&&o.data[0].length!==o.headings.length)throw new Error(\"Data heading length mismatch.\");return o};class J{constructor(t){this.dt=t,this.cursor=!1}setCursor(t=!1){if(t===this.cursor)return;const e=this.cursor;if(this.cursor=t,this.dt._renderTable(),!1!==t&&this.dt.options.scrollY){const t=this.dt.dom.querySelector(`tr.${this.dt.options.classes.cursor}`);t&&t.scrollIntoView({block:\"nearest\"})}this.dt.emit(\"datatable.cursormove\",this.cursor,e)}add(t){const e=t.map(((t,e)=>{const s=this.dt.columns.settings[e];return F(t,s)}));this.dt.data.data.push(e),this.dt.data.data.length&&(this.dt.hasRows=!0),this.dt.update(!0)}remove(t){if(!Array.isArray(t))return this.remove([t]);this.dt.data.data=this.dt.data.data.filter(((e,s)=>!t.includes(s))),this.dt.data.data.length||(this.dt.hasRows=!1),this.dt.update(!0)}findRowIndex(t,e){return this.dt.data.data.findIndex((s=>(s[t].text??String(s[t].data)).toLowerCase().includes(String(e).toLowerCase())))}findRow(t,e){const s=this.findRowIndex(t,e);if(s<0)return{index:-1,row:null,cols:[]};const i=this.dt.data.data[s],n=i.map((t=>t.data));return{index:s,row:i,cols:n}}updateRow(t,e){const s=e.map(((t,e)=>{const s=this.dt.columns.settings[e];return F(t,s)}));this.dt.data.data.splice(t,1,s),this.dt.update(!0)}}class W{constructor(t){this.dt=t,this.init()}init(){[this.settings,this._state]=((t=[],e,s)=>{let i=[],n=!1;const a=[];return t.forEach((t=>{(Array.isArray(t.select)?t.select:[t.select]).forEach((o=>{i[o]||(i[o]={type:t.type||e,sortable:!0,searchable:!0});const r=i[o];t.render&&(r.render=t.render),t.format?r.format=t.format:\"date\"===t.type&&(r.format=s),t.cellClass&&(r.cellClass=t.cellClass),t.headerClass&&(r.headerClass=t.headerClass),t.locale&&(r.locale=t.locale),!1===t.sortable?r.sortable=!1:(t.numeric&&(r.numeric=t.numeric),t.caseFirst&&(r.caseFirst=t.caseFirst)),!1===t.searchable?r.searchable=!1:t.sensitivity&&(r.sensitivity=t.sensitivity),(r.searchable||r.sortable)&&t.ignorePunctuation&&(r.ignorePunctuation=t.ignorePunctuation),t.hidden&&(r.hidden=!0),t.filter&&(r.filter=t.filter),t.sortSequence&&(r.sortSequence=t.sortSequence),t.sort&&(t.filter?a[o]=t.sort:n={column:o,dir:t.sort})}))})),i=i.map((t=>t||{type:e,format:\"date\"===e?s:void 0,sortable:!0,searchable:!0})),[i,{filters:a,sort:n,widths:[]}]})(this.dt.options.columns,this.dt.options.type,this.dt.options.format)}swap(t){if(2===t.length){const e=this.dt.data.headings.map(((t,e)=>e)),s=t[0],i=t[1],n=e[i];return e[i]=e[s],e[s]=n,this.order(e)}}order(t){this.dt.data.headings=t.map((t=>this.dt.data.headings[t])),this.dt.data.data=this.dt.data.data.map((e=>t.map((t=>e[t])))),this.settings=t.map((t=>this.settings[t])),this.dt.update()}hide(t){t.length&&(t.forEach((t=>{this.settings[t]||(this.settings[t]={type:\"string\"});this.settings[t].hidden=!0})),this.dt.update())}show(t){t.length&&(t.forEach((t=>{this.settings[t]||(this.settings[t]={type:\"string\",sortable:!0});delete this.settings[t].hidden})),this.dt.update())}visible(t){return void 0===t&&(t=[...Array(this.dt.data.headings.length).keys()]),Array.isArray(t)?t.map((t=>!this.settings[t]?.hidden)):!this.settings[t]?.hidden}add(t){const e=this.dt.data.headings.length;if(this.dt.data.headings=this.dt.data.headings.concat([z(t.heading)]),this.dt.data.data=this.dt.data.data.map(((e,s)=>e.concat([F(t.data[s],t)]))),this.settings[e]={type:t.type||\"string\",sortable:!0,searchable:!0},t.type||t.format||t.sortable||t.render||t.filter){const s=this.settings[e];t.render&&(s.render=t.render),t.format&&(s.format=t.format),t.cellClass&&(s.cellClass=t.cellClass),t.headerClass&&(s.headerClass=t.headerClass),t.locale&&(s.locale=t.locale),!1===t.sortable?s.sortable=!1:(t.numeric&&(s.numeric=t.numeric),t.caseFirst&&(s.caseFirst=t.caseFirst)),!1===t.searchable?s.searchable=!1:t.sensitivity&&(s.sensitivity=t.sensitivity),(s.searchable||s.sortable)&&t.ignorePunctuation&&(s.ignorePunctuation=t.ignorePunctuation),t.hidden&&(s.hidden=!0),t.filter&&(s.filter=t.filter),t.sortSequence&&(s.sortSequence=t.sortSequence)}this.dt.update(!0)}remove(t){if(!Array.isArray(t))return this.remove([t]);this.dt.data.headings=this.dt.data.headings.filter(((e,s)=>!t.includes(s))),this.dt.data.data=this.dt.data.data.map((e=>e.filter(((e,s)=>!t.includes(s))))),this.dt.update(!0)}filter(t,e=!1){if(!this.settings[t]?.filter?.length)return;const s=this._state.filters[t];let i;if(s){let e=!1;i=this.settings[t].filter.find((t=>!!e||(t===s&&(e=!0),!1)))}else{const e=this.settings[t].filter;i=e?e[0]:void 0}i?this._state.filters[t]=i:s&&(this._state.filters[t]=void 0),this.dt._currentPage=1,this.dt.update(),e||this.dt.emit(\"datatable.filter\",t,i)}sort(t,e,s=!1){const i=this.settings[t];if(s||this.dt.emit(\"datatable.sorting\",t,e),!e){const t=!!this._state.sort&&this._state.sort?.dir,s=i?.sortSequence||[\"asc\",\"desc\"];if(t){const i=s.indexOf(t);e=-1===i?\"asc\":i===s.length-1?s[0]:s[i+1]}else e=s.length?s[0]:\"asc\"}const n=!![\"string\",\"html\"].includes(i.type)&&new Intl.Collator(i.locale||this.dt.options.locale,{usage:\"sort\",numeric:i.numeric||this.dt.options.numeric,caseFirst:i.caseFirst||this.dt.options.caseFirst,ignorePunctuation:i.ignorePunctuation||this.dt.options.ignorePunctuation});this.dt.data.data.sort(((s,i)=>{let a=s[t].order||s[t].data,o=i[t].order||i[t].data;if(\"desc\"===e){const t=a;a=o,o=t}return n?n.compare(String(a),String(o)):a<o?-1:a>o?1:0})),this._state.sort={column:t,dir:e},this.dt._searchQueries.length?(this.dt.multiSearch(this.dt._searchQueries),this.dt.emit(\"datatable.sort\",t,e)):s||(this.dt._currentPage=1,this.dt.update(),this.dt.emit(\"datatable.sort\",t,e))}_measureWidths(){const t=this.dt.data.headings.filter(((t,e)=>!this.settings[e]?.hidden));if((this.dt.options.scrollY.length||this.dt.options.fixedColumns)&&t?.length){this._state.widths=[];const t={noPaging:!0};if(this.dt.options.header||this.dt.options.footer){this.dt.options.scrollY.length&&(t.unhideHeader=!0),this.dt.headerDOM&&this.dt.headerDOM.parentElement.removeChild(this.dt.headerDOM),t.noColumnWidths=!0,this.dt._renderTable(t);const e=Array.from(this.dt.dom.querySelector(\"thead, tfoot\")?.firstElementChild?.querySelectorAll(\"th\")||[]);let s=0;const i=this.dt.data.headings.map(((t,i)=>{if(this.settings[i]?.hidden)return 0;const n=e[s].offsetWidth;return s+=1,n})),n=i.reduce(((t,e)=>t+e),0);this._state.widths=i.map((t=>t/n*100))}else{t.renderHeader=!0,this.dt._renderTable(t);const e=Array.from(this.dt.dom.querySelector(\"thead, tfoot\")?.firstElementChild?.querySelectorAll(\"th\")||[]);let s=0;const i=this.dt.data.headings.map(((t,i)=>{if(this.settings[i]?.hidden)return 0;const n=e[s].offsetWidth;return s+=1,n})),n=i.reduce(((t,e)=>t+e),0);this._state.widths=i.map((t=>t/n*100))}this.dt._renderTable()}}}const Q={sortable:!0,locale:\"en\",numeric:!0,caseFirst:\"false\",searchable:!0,sensitivity:\"base\",ignorePunctuation:!0,destroyable:!0,data:{},type:\"html\",format:\"YYYY-MM-DD\",columns:[],paging:!0,perPage:10,perPageSelect:[5,10,15,20,25],nextPrev:!0,firstLast:!1,prevText:\"‹\",nextText:\"›\",firstText:\"«\",lastText:\"»\",ellipsisText:\"…\",truncatePager:!0,pagerDelta:2,scrollY:\"\",fixedColumns:!0,fixedHeight:!1,footer:!1,header:!0,hiddenHeader:!1,rowNavigation:!1,tabIndex:!1,pagerRender:!1,rowRender:!1,tableRender:!1,labels:{placeholder:\"Search...\",searchTitle:\"Search within table\",perPage:\"entries per page\",noRows:\"No entries found\",noResults:\"No results match your search query\",info:\"Showing {start} to {end} of {rows} entries\"},template:(t,e)=>`<div class='${t.classes.top}'>\\n    ${t.paging&&t.perPageSelect?`<div class='${t.classes.dropdown}'>\\n            <label>\\n                <select class='${t.classes.selector}'></select> ${t.labels.perPage}\\n            </label>\\n        </div>`:\"\"}\\n    ${t.searchable?`<div class='${t.classes.search}'>\\n            <input class='${t.classes.input}' placeholder='${t.labels.placeholder}' type='search' title='${t.labels.searchTitle}'${e.id?` aria-controls=\"${e.id}\"`:\"\"}>\\n        </div>`:\"\"}\\n</div>\\n<div class='${t.classes.container}'${t.scrollY.length?` style='height: ${t.scrollY}; overflow-Y: auto;'`:\"\"}></div>\\n<div class='${t.classes.bottom}'>\\n    ${t.paging?`<div class='${t.classes.info}'></div>`:\"\"}\\n    <nav class='${t.classes.pagination}'></nav>\\n</div>`,classes:{active:\"datatable-active\",ascending:\"datatable-ascending\",bottom:\"datatable-bottom\",container:\"datatable-container\",cursor:\"datatable-cursor\",descending:\"datatable-descending\",disabled:\"datatable-disabled\",dropdown:\"datatable-dropdown\",ellipsis:\"datatable-ellipsis\",filter:\"datatable-filter\",filterActive:\"datatable-filter-active\",empty:\"datatable-empty\",headercontainer:\"datatable-headercontainer\",hidden:\"datatable-hidden\",info:\"datatable-info\",input:\"datatable-input\",loading:\"datatable-loading\",pagination:\"datatable-pagination\",paginationList:\"datatable-pagination-list\",paginationListItem:\"datatable-pagination-list-item\",paginationListItemLink:\"datatable-pagination-list-item-link\",search:\"datatable-search\",selector:\"datatable-selector\",sorter:\"datatable-sorter\",table:\"datatable-table\",top:\"datatable-top\",wrapper:\"datatable-wrapper\"}},Z=(t,e,s,i={})=>({nodeName:\"LI\",attributes:{class:i.active&&!i.hidden?`${s.classes.paginationListItem} ${s.classes.active}`:i.hidden?`${s.classes.paginationListItem} ${s.classes.hidden} ${s.classes.disabled}`:s.classes.paginationListItem},childNodes:[{nodeName:\"A\",attributes:{\"data-page\":String(t),class:s.classes.paginationListItemLink},childNodes:[{nodeName:\"#text\",data:e}]}]}),X=(t,e,s,i,n)=>{let a=[];if(n.firstLast&&a.push(Z(1,n.firstText,n)),n.nextPrev){const e=t?1:s-1;a.push(Z(e,n.prevText,n,{hidden:t}))}let o=[...Array(i).keys()].map((t=>Z(t+1,String(t+1),n,{active:t===s-1})));if(n.truncatePager&&(o=((t,e,s,i)=>{const n=i.pagerDelta,a=i.classes,o=i.ellipsisText,r=2*n;let l=e-n,d=e+n;e<4-n+r?d=3+r:e>s-(3-n+r)&&(l=s-(2+r));const c=[];for(let e=1;e<=s;e++)if(1==e||e==s||e>=l&&e<=d){const s=t[e-1];c.push(s)}let h;const u=[];return c.forEach((e=>{const s=parseInt(e.childNodes[0].attributes[\"data-page\"],10);if(h){const e=parseInt(h.childNodes[0].attributes[\"data-page\"],10);if(s-e==2)u.push(t[e]);else if(s-e!=1){const t={nodeName:\"LI\",attributes:{class:`${a.paginationListItem} ${a.ellipsis} ${a.disabled}`},childNodes:[{nodeName:\"A\",attributes:{class:a.paginationListItemLink},childNodes:[{nodeName:\"#text\",data:o}]}]};u.push(t)}}u.push(e),h=e})),u})(o,s,i,n)),a=a.concat(o),n.nextPrev){const t=e?i:s+1;a.push(Z(t,n.nextText,n,{hidden:e}))}n.firstLast&&a.push(Z(i,n.lastText,n));return{nodeName:\"UL\",attributes:{class:n.classes.paginationList},childNodes:o.length>1?a:[]}};const G={classes:{row:\"datatable-editor-row\",form:\"datatable-editor-form\",item:\"datatable-editor-item\",menu:\"datatable-editor-menu\",save:\"datatable-editor-save\",block:\"datatable-editor-block\",close:\"datatable-editor-close\",inner:\"datatable-editor-inner\",input:\"datatable-editor-input\",label:\"datatable-editor-label\",modal:\"datatable-editor-modal\",action:\"datatable-editor-action\",header:\"datatable-editor-header\",wrapper:\"datatable-editor-wrapper\",editable:\"datatable-editor-editable\",container:\"datatable-editor-container\",separator:\"datatable-editor-separator\"},labels:{editCell:\"Edit Cell\",editRow:\"Edit Row\",removeRow:\"Remove Row\",reallyRemove:\"Are you sure?\"},inline:!0,hiddenColumns:!1,contextMenu:!0,clickEvent:\"dblclick\",excludeColumns:[],menuItems:[{text:t=>t.options.labels.editCell,action:(t,e)=>{if(!(t.event.target instanceof Element))return;const s=t.event.target.closest(\"td\");return t.editCell(s)}},{text:t=>t.options.labels.editRow,action:(t,e)=>{if(!(t.event.target instanceof Element))return;const s=t.event.target.closest(\"tr\");return t.editRow(s)}},{separator:!0},{text:t=>t.options.labels.removeRow,action:(t,e)=>{if(t.event.target instanceof Element&&confirm(t.options.labels.reallyRemove)){const e=t.event.target.closest(\"tr\");t.removeRow(e)}}}]};class K{constructor(t,e={}){this.dt=t,this.options={...G,...e}}init(){this.initialized||(this.dt.wrapperDOM.classList.add(this.options.classes.editable),this.options.inline&&(this.originalRowRender=this.dt.options.rowRender,this.dt.options.rowRender=(t,e,s)=>{let i=this.rowRender(t,e,s);return this.originalRowRender&&(i=this.originalRowRender(t,i,s)),i}),this.options.contextMenu&&(this.containerDOM=s(\"div\",{id:this.options.classes.container}),this.wrapperDOM=s(\"div\",{class:this.options.classes.wrapper}),this.menuDOM=s(\"ul\",{class:this.options.classes.menu}),this.options.menuItems&&this.options.menuItems.length&&this.options.menuItems.forEach((t=>{const e=s(\"li\",{class:t.separator?this.options.classes.separator:this.options.classes.item});if(!t.separator){const i=s(\"a\",{class:this.options.classes.action,href:t.url||\"#\",html:\"function\"==typeof t.text?t.text(this):t.text});e.appendChild(i),t.action&&\"function\"==typeof t.action&&i.addEventListener(\"click\",(e=>{e.preventDefault(),t.action(this,e)}))}this.menuDOM.appendChild(e)})),this.wrapperDOM.appendChild(this.menuDOM),this.containerDOM.appendChild(this.wrapperDOM),this.update()),this.data={},this.closed=!0,this.editing=!1,this.editingRow=!1,this.editingCell=!1,this.bindEvents(),setTimeout((()=>{this.initialized=!0,this.dt.emit(\"editable.init\")}),10))}bindEvents(){this.events={context:this.context.bind(this),update:this.update.bind(this),dismiss:this.dismiss.bind(this),keydown:this.keydown.bind(this),click:this.click.bind(this)},this.dt.dom.addEventListener(this.options.clickEvent,this.events.click),document.addEventListener(\"click\",this.events.dismiss),document.addEventListener(\"keydown\",this.events.keydown),this.options.contextMenu&&(this.dt.dom.addEventListener(\"contextmenu\",this.events.context),this.events.reset=function(t,e=300){let s;return(...i)=>{clearTimeout(s),s=window.setTimeout((()=>t()),e)}}((()=>this.events.update()),50),window.addEventListener(\"resize\",this.events.reset),window.addEventListener(\"scroll\",this.events.reset))}context(t){const e=t.target;if(!(e instanceof Element))return;this.event=t;const s=e.closest(\"tbody td\");if(this.options.contextMenu&&!this.disabled&&s){t.preventDefault();let e=t.pageX,s=t.pageY;e>this.limits.x&&(e-=this.rect.width),s>this.limits.y&&(s-=this.rect.height),this.wrapperDOM.style.top=`${s}px`,this.wrapperDOM.style.left=`${e}px`,this.openMenu(),this.update()}}click(t){const e=t.target;if(e instanceof Element)if(this.editing&&this.data&&this.editingCell){const t=this.modalDOM?this.modalDOM.querySelector(`input.${this.options.classes.input}[type=text]`):this.dt.wrapperDOM.querySelector(`input.${this.options.classes.input}[type=text]`);this.saveCell(t.value)}else if(!this.editing){const s=e.closest(\"tbody td\");s&&(this.editCell(s),t.preventDefault())}}keydown(t){if(this.modalDOM){if(\"Escape\"===t.key)this.closeModal();else if(\"Enter\"===t.key)if(this.editingCell){const t=this.modalDOM.querySelector(`input.${this.options.classes.input}[type=text]`);this.saveCell(t.value)}else{const t=Array.from(this.modalDOM.querySelectorAll(`input.${this.options.classes.input}[type=text]`));this.saveRow(t.map((t=>t.value.trim())),this.data.row)}}else if(this.editing&&this.data)if(\"Enter\"===t.key){if(this.editingCell){const t=this.dt.wrapperDOM.querySelector(`input.${this.options.classes.input}[type=text]`);this.saveCell(t.value)}else if(this.editingRow){const t=Array.from(this.dt.wrapperDOM.querySelectorAll(`input.${this.options.classes.input}[type=text]`));this.saveRow(t.map((t=>t.value.trim())),this.data.row)}}else\"Escape\"===t.key&&this.saveCell(this.data.content)}editCell(t){const e=a(t.cellIndex,this.dt.columns.settings);if(this.options.excludeColumns.includes(e))return void this.closeMenu();const s=parseInt(t.parentElement.dataset.index,10),i=this.dt.data.data[s][e];this.data={cell:i,rowIndex:s,columnIndex:e,content:i.text||String(i.data)},this.editing=!0,this.editingCell=!0,this.options.inline?this.dt.update():this.editCellModal(),this.closeMenu()}editCellModal(){const t=this.data.cell,e=this.data.columnIndex,i=this.dt.data.headings[e].text||String(this.dt.data.headings[e].data),a=[`<div class='${this.options.classes.inner}'>`,`<div class='${this.options.classes.header}'>`,\"<h4>Editing cell</h4>\",`<button class='${this.options.classes.close}' type='button' data-editor-close>×</button>`,\" </div>\",`<div class='${this.options.classes.block}'>`,`<form class='${this.options.classes.form}'>`,`<div class='${this.options.classes.row}'>`,`<label class='${this.options.classes.label}'>${n(i)}</label>`,`<input class='${this.options.classes.input}' value='${n(t.text||String(t.data)||\"\")}' type='text'>`,\"</div>\",`<div class='${this.options.classes.row}'>`,`<button class='${this.options.classes.save}' type='button' data-editor-save>Save</button>`,\"</div>\",\"</form>\",\"</div>\",\"</div>\"].join(\"\"),o=s(\"div\",{class:this.options.classes.modal,html:a});this.modalDOM=o,this.openModal();const r=o.querySelector(`input.${this.options.classes.input}[type=text]`);r.focus(),r.selectionStart=r.selectionEnd=r.value.length,o.addEventListener(\"click\",(t=>{const e=t.target;e instanceof Element&&(e.hasAttribute(\"data-editor-close\")?this.closeModal():e.hasAttribute(\"data-editor-save\")&&this.saveCell(r.value))}))}saveCell(t){const e=this.data.content,s=this.dt.columns.settings[this.data.columnIndex].type||this.dt.options.type,i=t.trim();let n;if(\"number\"===s)n={data:parseFloat(i)};else if(\"boolean\"===s)n=[\"\",\"false\",\"0\"].includes(i)?{data:!1,text:\"false\",order:0}:{data:!0,text:\"true\",order:1};else if(\"html\"===s)n={data:[{nodeName:\"#text\",data:t}],text:t,order:t};else if(\"string\"===s)n={data:t};else if(\"date\"===s){const e=this.dt.columns.settings[this.data.columnIndex].format||this.dt.options.format;n={data:t,order:B(String(t),e)}}else n={data:t};this.dt.data.data[this.data.rowIndex][this.data.columnIndex]=n,this.closeModal();const a=this.data.rowIndex,o=this.data.columnIndex;this.data={},this.dt.update(!0),this.editing=!1,this.editingCell=!1,this.dt.emit(\"editable.save.cell\",t,e,a,o)}editRow(t){if(!t||\"TR\"!==t.nodeName||this.editing)return;const e=parseInt(t.dataset.index,10),s=this.dt.data.data[e];this.data={row:s,rowIndex:e},this.editing=!0,this.editingRow=!0,this.options.inline?this.dt.update():this.editRowModal(),this.closeMenu()}editRowModal(){const t=this.data.row,e=[`<div class='${this.options.classes.inner}'>`,`<div class='${this.options.classes.header}'>`,\"<h4>Editing row</h4>\",`<button class='${this.options.classes.close}' type='button' data-editor-close>×</button>`,\" </div>\",`<div class='${this.options.classes.block}'>`,`<form class='${this.options.classes.form}'>`,`<div class='${this.options.classes.row}'>`,`<button class='${this.options.classes.save}' type='button' data-editor-save>Save</button>`,\"</div>\",\"</form>\",\"</div>\",\"</div>\"].join(\"\"),i=s(\"div\",{class:this.options.classes.modal,html:e}),a=i.firstElementChild;if(!a)return;const o=a.lastElementChild?.firstElementChild;if(!o)return;t.forEach(((t,e)=>{const i=this.dt.columns.settings[e];if((!i.hidden||i.hidden&&this.options.hiddenColumns)&&!this.options.excludeColumns.includes(e)){const i=this.dt.data.headings[e].text||String(this.dt.data.headings[e].data);o.insertBefore(s(\"div\",{class:this.options.classes.row,html:[`<div class='${this.options.classes.row}'>`,`<label class='${this.options.classes.label}'>${n(i)}</label>`,`<input class='${this.options.classes.input}' value='${n(t.text||String(t.data)||\"\")}' type='text'>`,\"</div>\"].join(\"\")}),o.lastElementChild)}})),this.modalDOM=i,this.openModal();const r=Array.from(o.querySelectorAll(`input.${this.options.classes.input}[type=text]`));r.pop(),i.addEventListener(\"click\",(t=>{const e=t.target;e instanceof Element&&(e.hasAttribute(\"data-editor-close\")?this.closeModal():e.hasAttribute(\"data-editor-save\")&&this.saveRow(r.map((t=>t.value.trim())),this.data.row))}))}saveRow(t,e){const s=e.map((t=>t.text??String(t.data)));this.dt.data.data[this.data.rowIndex]=this.dt.data.data[this.data.rowIndex].map(((e,s)=>{if(this.dt.columns.settings[s].hidden||this.options.excludeColumns.includes(s))return e;const i=this.dt.columns.settings[s].type||this.dt.options.type,n=t[o(s,this.dt.columns.settings)],a=n.trim();let r;if(\"number\"===i)r={data:parseFloat(a)};else if(\"boolean\"===i)r=[\"\",\"false\",\"0\"].includes(a)?{data:!1,text:\"false\",order:0}:{data:!0,text:\"true\",order:1};else if(\"html\"===i)r={data:[{nodeName:\"#text\",data:n}],text:n,order:n};else if(\"string\"===i)r={data:n};else if(\"date\"===i){const t=this.dt.columns.settings[s].format||this.dt.options.format;r={data:n,order:B(String(n),t)}}else r={data:n};return r})),this.data={},this.dt.update(!0),this.closeModal(),this.dt.emit(\"editable.save.row\",t,s,e)}openModal(){this.modalDOM&&document.body.appendChild(this.modalDOM)}closeModal(){this.editing&&this.modalDOM&&(document.body.removeChild(this.modalDOM),this.modalDOM=this.editing=this.editingRow=this.editingCell=!1)}removeRow(t){if(!t||\"TR\"!==t.nodeName||this.editing)return;const e=parseInt(t.dataset.index,10);this.dt.rows.remove(e),this.closeMenu()}update(){const t=window.scrollX||window.pageXOffset,e=window.scrollY||window.pageYOffset;this.rect=this.wrapperDOM.getBoundingClientRect(),this.limits={x:window.innerWidth+t-this.rect.width,y:window.innerHeight+e-this.rect.height}}dismiss(t){const e=t.target;if(!(e instanceof Element))return;let s=!0;this.options.contextMenu&&(s=!this.wrapperDOM.contains(e),this.editing&&(s=!this.wrapperDOM.contains(e)&&!e.matches(`input.${this.options.classes.input}[type=text]`))),s&&(this.editingCell&&this.saveCell(this.data.content),this.closeMenu())}openMenu(){if(this.editing&&this.data&&this.editingCell){const t=this.modalDOM?this.modalDOM.querySelector(`input.${this.options.classes.input}[type=text]`):this.dt.wrapperDOM.querySelector(`input.${this.options.classes.input}[type=text]`);this.saveCell(t.value)}this.options.contextMenu&&(document.body.appendChild(this.containerDOM),this.closed=!1,this.dt.emit(\"editable.context.open\"))}closeMenu(){this.options.contextMenu&&!this.closed&&(this.closed=!0,document.body.removeChild(this.containerDOM),this.dt.emit(\"editable.context.close\"))}destroy(){this.dt.dom.removeEventListener(this.options.clickEvent,this.events.click),this.dt.dom.removeEventListener(\"contextmenu\",this.events.context),document.removeEventListener(\"click\",this.events.dismiss),document.removeEventListener(\"keydown\",this.events.keydown),window.removeEventListener(\"resize\",this.events.reset),window.removeEventListener(\"scroll\",this.events.reset),document.body.contains(this.containerDOM)&&document.body.removeChild(this.containerDOM),this.options.inline&&(this.dt.options.rowRender=this.originalRowRender),this.initialized=!1}rowRender(t,e,s){if(!this.data||this.data.rowIndex!==s)return e;if(this.editingCell){e.childNodes[o(this.data.columnIndex,this.dt.columns.settings)].childNodes=[{nodeName:\"INPUT\",attributes:{type:\"text\",value:this.data.content,class:this.options.classes.input}}]}else e.childNodes.forEach(((s,i)=>{const o=a(i,this.dt.columns.settings),r=t[o];if(!this.options.excludeColumns.includes(o)){e.childNodes[i].childNodes=[{nodeName:\"INPUT\",attributes:{type:\"text\",value:n(r.text||String(r.data)||\"\"),class:this.options.classes.input}}]}}));return e}}exports.DataTable=class{constructor(t,e={}){const s=\"string\"==typeof t?document.querySelector(t):t;s instanceof HTMLTableElement?this.dom=s:(this.dom=document.createElement(\"table\"),s.appendChild(this.dom));const i={...Q.labels,...e.labels},n={...Q.classes,...e.classes};this.options={...Q,...e,labels:i,classes:n},this._initialInnerHTML=this.options.destroyable?this.dom.innerHTML:\"\",this.options.tabIndex?this.dom.tabIndex=this.options.tabIndex:this.options.rowNavigation&&-1===this.dom.tabIndex&&(this.dom.tabIndex=0),this._listeners={onResize:()=>this._onResize()},this._dd=new T({valueDiffing:!1}),this.initialized=!1,this._events={},this._currentPage=0,this.onFirstPage=!0,this.hasHeadings=!1,this.hasRows=!1,this._searchQueries=[],this.init()}init(){if(this.initialized||this.dom.classList.contains(this.options.classes.table))return!1;this._virtualDOM=D(this.dom,{valueDiffing:!1}),this._tableAttributes={...this._virtualDOM.attributes},this.rows=new J(this),this.columns=new W(this),this.data=U(this.options.data,this.dom,this.columns.settings,this.options.type,this.options.format),this._render(),setTimeout((()=>{this.emit(\"datatable.init\"),this.initialized=!0}),10)}_render(){this.wrapperDOM=s(\"div\",{class:`${this.options.classes.wrapper} ${this.options.classes.loading}`}),this.wrapperDOM.innerHTML=this.options.template(this.options,this.dom);const t=this.wrapperDOM.querySelector(`select.${this.options.classes.selector}`);t&&this.options.paging&&this.options.perPageSelect?this.options.perPageSelect.forEach((e=>{const[s,i]=Array.isArray(e)?[e[0],e[1]]:[String(e),e],n=i===this.options.perPage,a=new Option(s,String(i),n,n);t.appendChild(a)})):t&&t.parentElement.removeChild(t),this.containerDOM=this.wrapperDOM.querySelector(`.${this.options.classes.container}`),this._pagerDOMs=[],Array.from(this.wrapperDOM.querySelectorAll(`.${this.options.classes.pagination}`)).forEach((t=>{t instanceof HTMLElement&&(t.innerHTML=`<ul class=\"${this.options.classes.paginationList}\"></ul>`,this._pagerDOMs.push(t.firstElementChild))})),this._virtualPagerDOM={nodeName:\"UL\",attributes:{class:this.options.classes.paginationList}},this._label=this.wrapperDOM.querySelector(`.${this.options.classes.info}`),this.dom.parentElement.replaceChild(this.wrapperDOM,this.dom),this.containerDOM.appendChild(this.dom),this._rect=this.dom.getBoundingClientRect(),this._fixHeight(),this.options.header||this.wrapperDOM.classList.add(\"no-header\"),this.options.footer||this.wrapperDOM.classList.add(\"no-footer\"),this.options.sortable&&this.wrapperDOM.classList.add(\"sortable\"),this.options.searchable&&this.wrapperDOM.classList.add(\"searchable\"),this.options.fixedHeight&&this.wrapperDOM.classList.add(\"fixed-height\"),this.options.fixedColumns&&this.wrapperDOM.classList.add(\"fixed-columns\"),this._bindEvents(),this.columns._state.sort&&this.columns.sort(this.columns._state.sort.column,this.columns._state.sort.dir,!0),this.update(!0)}_renderTable(t={}){let e=P(this._tableAttributes,this.data.headings,(this.options.paging||this._searchQueries.length)&&this._currentPage&&this.pages.length&&!t.noPaging?this.pages[this._currentPage-1]:this.data.data.map(((t,e)=>({row:t,index:e}))),this.columns.settings,this.columns._state,this.rows.cursor,this.options,t);if(this.options.tableRender){const t=this.options.tableRender(this.data,e,\"main\");t&&(e=t)}const s=this._dd.diff(this._virtualDOM,e);this._dd.apply(this.dom,s),this._virtualDOM=e}_renderPage(t=!1){this.hasRows&&this.totalPages?(this._currentPage>this.totalPages&&(this._currentPage=1),this._renderTable(),this.onFirstPage=1===this._currentPage,this.onLastPage=this._currentPage===this.lastPage):this.setMessage(this.options.labels.noRows);let e,s=0,i=0,n=0;if(this.totalPages&&(s=this._currentPage-1,i=s*this.options.perPage,n=i+this.pages[s].length,i+=1,e=this._searchQueries.length?this._searchData.length:this.data.data.length),this._label&&this.options.labels.info.length){const t=this.options.labels.info.replace(\"{start}\",String(i)).replace(\"{end}\",String(n)).replace(\"{page}\",String(this._currentPage)).replace(\"{pages}\",String(this.totalPages)).replace(\"{rows}\",String(e));this._label.innerHTML=e?t:\"\"}if(1==this._currentPage&&this._fixHeight(),this.options.rowNavigation&&this._currentPage&&(!this.rows.cursor||!this.pages[this._currentPage-1].find((t=>t.index===this.rows.cursor)))){const e=this.pages[this._currentPage-1];e.length&&(t?this.rows.setCursor(e[e.length-1].index):this.rows.setCursor(e[0].index))}}_renderPagers(){if(!this.options.paging)return;let t=X(this.onFirstPage,this.onLastPage,this._currentPage,this.totalPages,this.options);if(this.options.pagerRender){const e=this.options.pagerRender([this.onFirstPage,this.onLastPage,this._currentPage,this.totalPages],t);e&&(t=e)}const e=this._dd.diff(this._virtualPagerDOM,t);this._pagerDOMs.forEach((t=>{this._dd.apply(t,e)})),this._virtualPagerDOM=t}_renderSeparateHeader(){const t=this.dom.parentElement;this.headerDOM||(this.headerDOM=document.createElement(\"div\"),this._virtualHeaderDOM={nodeName:\"DIV\"}),t.parentElement.insertBefore(this.headerDOM,t);let e={nodeName:\"TABLE\",attributes:{class:this.options.classes.table},childNodes:[{nodeName:\"THEAD\",childNodes:[A(this.data.headings,this.columns.settings,this.columns._state,this.options,{unhideHeader:!0})]}]};if(this.options.tableRender){const t=this.options.tableRender(this.data,e,\"header\");t&&(e=t)}const s={nodeName:\"DIV\",attributes:{class:this.options.classes.headercontainer},childNodes:[e]},i=this._dd.diff(this._virtualHeaderDOM,s);this._dd.apply(this.headerDOM,i),this._virtualHeaderDOM=s;const n=this.headerDOM.firstElementChild.clientWidth-this.dom.clientWidth;if(n){const t=structuredClone(this._virtualHeaderDOM);t.attributes.style=`padding-right: ${n}px;`;const e=this._dd.diff(this._virtualHeaderDOM,t);this._dd.apply(this.headerDOM,e),this._virtualHeaderDOM=t}t.scrollHeight>t.clientHeight&&(t.style.overflowY=\"scroll\")}_bindEvents(){if(this.options.perPageSelect){const t=this.wrapperDOM.querySelector(`select.${this.options.classes.selector}`);t&&t instanceof HTMLSelectElement&&t.addEventListener(\"change\",(()=>{this.options.perPage=parseInt(t.value,10),this.update(),this._fixHeight(),this.emit(\"datatable.perpage\",this.options.perPage)}),!1)}this.options.searchable&&this.wrapperDOM.addEventListener(\"keyup\",(t=>{const e=t.target;if(!(e instanceof HTMLInputElement&&e.matches(`.${this.options.classes.input}`)))return;t.preventDefault();const s=Array.from(this.wrapperDOM.querySelectorAll(`.${this.options.classes.input}`)).filter((t=>t.value.length)).map((t=>t.dataset.columns?{term:t.value,columns:JSON.parse(t.dataset.columns)}:{term:t.value,columns:void 0}));if(1===s.length){const t=s[0];this.search(t.term,t.columns)}else this.multiSearch(s)})),this.wrapperDOM.addEventListener(\"click\",(t=>{const e=t.target.closest(\"a\");if(e)if(e.hasAttribute(\"data-page\"))this.page(parseInt(e.getAttribute(\"data-page\"),10)),t.preventDefault();else if(e.classList.contains(this.options.classes.sorter)){const s=Array.from(e.parentElement.parentElement.children).indexOf(e.parentElement),i=a(s,this.columns.settings);this.columns.sort(i),t.preventDefault()}else if(e.classList.contains(this.options.classes.filter)){const s=Array.from(e.parentElement.parentElement.children).indexOf(e.parentElement),i=a(s,this.columns.settings);this.columns.filter(i),t.preventDefault()}}),!1),this.options.rowNavigation?(this.dom.addEventListener(\"keydown\",(t=>{if(\"ArrowUp\"===t.key){let e;t.preventDefault(),t.stopPropagation(),this.pages[this._currentPage-1].find((t=>t.index===this.rows.cursor||(e=t,!1))),e?this.rows.setCursor(e.index):this.onFirstPage||this.page(this._currentPage-1,!0)}else if(\"ArrowDown\"===t.key){let e;t.preventDefault(),t.stopPropagation();const s=this.pages[this._currentPage-1].find((t=>!!e||(t.index===this.rows.cursor&&(e=!0),!1)));s?this.rows.setCursor(s.index):this.onLastPage||this.page(this._currentPage+1)}else[\"Enter\",\" \"].includes(t.key)&&this.emit(\"datatable.selectrow\",this.rows.cursor,t)})),this.dom.addEventListener(\"mousedown\",(t=>{const e=t.target;if(e instanceof Element&&this.dom.matches(\":focus\")){const s=Array.from(this.dom.querySelectorAll(\"body tr\")).find((t=>t.contains(e)));s&&s instanceof HTMLElement&&this.emit(\"datatable.selectrow\",parseInt(s.dataset.index,10),t)}}))):this.dom.addEventListener(\"mousedown\",(t=>{const e=t.target;if(!(e instanceof Element))return;const s=Array.from(this.dom.querySelectorAll(\"body tr\")).find((t=>t.contains(e)));s&&s instanceof HTMLElement&&this.emit(\"datatable.selectrow\",parseInt(s.dataset.index,10),t)})),window.addEventListener(\"resize\",this._listeners.onResize)}_onResize(){this._rect=this.containerDOM.getBoundingClientRect(),this._rect.width&&this.update(!0)}destroy(){this.options.destroyable&&(this.dom.innerHTML=this._initialInnerHTML,this.dom.classList.remove(this.options.classes.table),this.wrapperDOM.parentElement&&this.wrapperDOM.parentElement.replaceChild(this.dom,this.wrapperDOM),this.initialized=!1,window.removeEventListener(\"resize\",this._listeners.onResize))}update(t=!1){t&&(this.columns._measureWidths(),this.hasRows=Boolean(this.data.data.length),this.hasHeadings=Boolean(this.data.headings.length)),this.wrapperDOM.classList.remove(this.options.classes.empty),this._paginate(),this._renderPage(),this._renderPagers(),this.options.scrollY.length&&this._renderSeparateHeader(),this.emit(\"datatable.update\")}_paginate(){let t=this.data.data.map(((t,e)=>({row:t,index:e})));return this._searchQueries.length&&(t=[],this._searchData.forEach((e=>t.push({index:e,row:this.data.data[e]})))),this.columns._state.filters.length&&this.columns._state.filters.forEach(((e,s)=>{e&&(t=t.filter((t=>\"function\"==typeof e?e(t.row[s].data):(t.row[s].text??t.row[s].data)===e)))})),this.options.paging&&this.options.perPage>0?this.pages=t.map(((e,s)=>s%this.options.perPage==0?t.slice(s,s+this.options.perPage):null)).filter((t=>t)):this.pages=[t],this.totalPages=this.lastPage=this.pages.length,this._currentPage||(this._currentPage=1),this.totalPages}_fixHeight(){this.options.fixedHeight&&(this.containerDOM.style.height=null,this._rect=this.containerDOM.getBoundingClientRect(),this.containerDOM.style.height=`${this._rect.height}px`)}search(t,e){if(!t.length)return this._currentPage=1,this._searchQueries=[],this._searchData=[],this.update(),this.emit(\"datatable.search\",\"\",[]),this.wrapperDOM.classList.remove(\"search-results\"),!1;this.multiSearch([{term:t,columns:e||void 0}]),this.emit(\"datatable.search\",t,this._searchData)}multiSearch(t){if(!this.hasRows)return!1;if(this._currentPage=1,this._searchQueries=t,this._searchData=[],!(t=t.filter((t=>t.term.length))).length)return this.update(),this.emit(\"datatable.multisearch\",t,this._searchData),this.wrapperDOM.classList.remove(\"search-results\"),!1;const e=t.map((t=>this.columns.settings.map(((e,s)=>{if(e.hidden||!e.searchable||t.columns&&!t.columns.includes(s))return!1;let i=t.term;const n=e.sensitivity||this.options.sensitivity;[\"base\",\"accent\"].includes(n)&&(i=i.toLowerCase()),[\"base\",\"case\"].includes(n)&&(i=i.normalize(\"NFD\").replace(/\\p{Diacritic}/gu,\"\"));return(e.ignorePunctuation||this.options.ignorePunctuation)&&(i=i.replace(/[.,/#!$%^&*;:{}=-_`~()]/g,\"\")),i}))));this.data.data.forEach(((t,s)=>{const i=t.map(((t,e)=>{let s=(t.text||String(t.data)).trim();if(s.length){const t=this.columns.settings[e],i=t.sensitivity||this.options.sensitivity;[\"base\",\"accent\"].includes(i)&&(s=s.toLowerCase()),[\"base\",\"case\"].includes(i)&&(s=s.normalize(\"NFD\").replace(/\\p{Diacritic}/gu,\"\"));(t.ignorePunctuation||this.options.ignorePunctuation)&&(s=s.replace(/[.,/#!$%^&*;:{}=-_`~()]/g,\"\"))}return s}));e.every((t=>t.find(((t,e)=>!!t&&t.split(\" \").find((t=>i[e].includes(t)))))))&&this._searchData.push(s)})),this.wrapperDOM.classList.add(\"search-results\"),this._searchData.length?this.update():(this.wrapperDOM.classList.remove(\"search-results\"),this.setMessage(this.options.labels.noResults)),this.emit(\"datatable.multisearch\",t,this._searchData)}page(t,e=!1){return t!==this._currentPage&&(isNaN(t)||(this._currentPage=t),!(t>this.pages.length||t<0)&&(this._renderPage(e),this._renderPagers(),void this.emit(\"datatable.page\",t)))}insert(e){let s=[];if(Array.isArray(e)){const t=this.data.headings.map((t=>t.text??String(t.data)));e.forEach(((e,i)=>{const n=[];Object.entries(e).forEach((([e,s])=>{const a=t.indexOf(e);a>-1?n[a]=F(s,this.columns.settings[a]):this.hasHeadings||this.hasRows||0!==i||(n[t.length]=F(s,this.columns.settings[t.length]),t.push(e),this.data.headings.push(z(e)))})),s.push(n)}))}else t(e)&&(!e.headings||this.hasHeadings||this.hasRows?e.data&&Array.isArray(e.data)&&(s=e.data.map((t=>t.map(((t,e)=>F(t,this.columns.settings[e])))))):this.data=U(e,void 0,this.columns.settings,this.options.type,this.options.format));s.length&&s.forEach((t=>this.data.data.push(t))),this.hasHeadings=Boolean(this.data.headings.length),this.columns._state.sort&&this.columns.sort(this.columns._state.sort.column,this.columns._state.sort.dir,!0),this.update(!0)}refresh(){this.options.searchable&&(Array.from(this.wrapperDOM.querySelectorAll(`.${this.options.classes.input}`)).forEach((t=>{t.value=\"\"})),this._searchQueries=[]),this._currentPage=1,this.onFirstPage=!0,this.update(!0),this.emit(\"datatable.refresh\")}print(){const t=s(\"table\");let e=P(this._tableAttributes,this.data.headings,this.data.data.map(((t,e)=>({row:t,index:e}))),this.columns.settings,this.columns._state,!1,this.options,{noColumnWidths:!0,unhideHeader:!0});if(this.options.tableRender){const t=this.options.tableRender(this.data,e,\"print\");t&&(e=t)}const i=this._dd.diff({nodeName:\"TABLE\"},e);this._dd.apply(t,i);const n=window.open();n.document.body.appendChild(t),n.print()}setMessage(t){const e=this.data.headings.filter(((t,e)=>!this.columns.settings[e]?.hidden)).length||1;this.wrapperDOM.classList.add(this.options.classes.empty),this._label&&(this._label.innerHTML=\"\"),this.totalPages=0,this._renderPagers();let s={nodeName:\"TABLE\",attributes:{class:this.options.classes.table},childNodes:[{nodeName:\"THEAD\",childNodes:[A(this.data.headings,this.columns.settings,this.columns._state,this.options,{})]},{nodeName:\"TBODY\",childNodes:[{nodeName:\"TR\",childNodes:[{nodeName:\"TD\",attributes:{class:this.options.classes.empty,colspan:String(e)},childNodes:[{nodeName:\"#text\",data:t}]}]}]}]};if(this.options.tableRender){const t=this.options.tableRender(this.data,s,\"message\");t&&(s=t)}const i=this._dd.diff(this._virtualDOM,s);this._dd.apply(this.dom,i),this._virtualDOM=s}on(t,e){this._events[t]=this._events[t]||[],this._events[t].push(e)}off(t,e){t in this._events!=!1&&this._events[t].splice(this._events[t].indexOf(e),1)}emit(t,...e){if(t in this._events!=!1)for(let s=0;s<this._events[t].length;s++)this._events[t][s](...e)}},exports.convertCSV=function(e){let s;if(!t(e))return!1;const i={lineDelimiter:\"\\n\",columnDelimiter:\",\",removeDoubleQuotes:!1,...e};if(i.data.length){s={data:[]};const t=i.data.split(i.lineDelimiter);if(t.length&&(i.headings&&(s.headings=t[0].split(i.columnDelimiter),i.removeDoubleQuotes&&(s.headings=s.headings.map((t=>t.trim().replace(/(^\"|\"$)/g,\"\")))),t.shift()),t.forEach(((t,e)=>{s.data[e]=[];const n=t.split(i.columnDelimiter);n.length&&n.forEach((t=>{i.removeDoubleQuotes&&(t=t.trim().replace(/(^\"|\"$)/g,\"\")),s.data[e].push(t)}))}))),s)return s}return!1},exports.convertJSON=function(s){let i;if(!t(s))return!1;const n={data:\"\",...s};if(n.data.length||t(n.data)){const t=!!e(n.data)&&JSON.parse(n.data);if(t?(i={headings:[],data:[]},t.forEach(((t,e)=>{i.data[e]=[],Object.entries(t).forEach((([t,s])=>{i.headings.includes(t)||i.headings.push(t),i.data[e].push(s)}))}))):console.warn(\"That's not valid JSON!\"),i)return i}return!1},exports.createElement=s,exports.exportCSV=function(e,s={}){if(!e.hasHeadings&&!e.hasRows)return!1;if(!t(s))return!1;const i={download:!0,skipColumn:[],lineDelimiter:\"\\n\",columnDelimiter:\",\",...s},n=t=>!i.skipColumn.includes(t)&&!e.columns.settings[t]?.hidden;let a=[];const o=e.data.headings.filter(((t,e)=>n(e))).map((t=>t.text??t.data));if(a[0]=o,i.selection)if(Array.isArray(i.selection))for(let t=0;t<i.selection.length;t++)a=a.concat(e.pages[i.selection[t]-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.text??t.data)))));else a=a.concat(e.pages[i.selection-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.text??t.data)))));else a=a.concat(e.data.data.map((t=>t.filter(((t,e)=>n(e))).map((t=>t.text??t.data)))));if(a.length){let t=\"\";if(a.forEach((e=>{e.forEach((e=>{\"string\"==typeof e&&(e=(e=(e=(e=(e=e.trim()).replace(/\\s{2,}/g,\" \")).replace(/\\n/g,\"  \")).replace(/\"/g,'\"\"')).replace(/#/g,\"%23\")).includes(\",\")&&(e=`\"${e}\"`),t+=e+i.columnDelimiter})),t=t.trim().substring(0,t.length-1),t+=i.lineDelimiter})),t=t.trim().substring(0,t.length-1),i.download){const e=document.createElement(\"a\");e.href=encodeURI(`data:text/csv;charset=utf-8,${t}`),e.download=`${i.filename||\"datatable_export\"}.csv`,document.body.appendChild(e),e.click(),document.body.removeChild(e)}return t}return!1},exports.exportJSON=function(e,s={}){if(!e.hasHeadings&&!e.hasRows)return!1;if(!t(s))return!1;const i={download:!0,skipColumn:[],replacer:null,space:4,...s},n=t=>!i.skipColumn.includes(t)&&!e.columns.settings[t]?.hidden;let a=[];if(i.selection)if(Array.isArray(i.selection))for(let t=0;t<i.selection.length;t++)a=a.concat(e.pages[i.selection[t]-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.data)))));else a=a.concat(e.pages[i.selection-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.data)))));else a=a.concat(e.data.data.map((t=>t.filter(((t,e)=>n(e))).map((t=>t.data)))));const o=e.data.headings.filter(((t,e)=>n(e))).map((t=>t.text??String(t.data)));if(a.length){const t=[];a.forEach(((e,s)=>{t[s]=t[s]||{},e.forEach(((e,i)=>{t[s][o[i]]=e}))}));const e=JSON.stringify(t,i.replacer,i.space);if(i.download){const t=new Blob([e],{type:\"data:application/json;charset=utf-8\"}),s=URL.createObjectURL(t),n=document.createElement(\"a\");n.href=s,n.download=`${i.filename||\"datatable_export\"}.json`,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(s)}return e}return!1},exports.exportSQL=function(e,s={}){if(!e.hasHeadings&&!e.hasRows)return!1;if(!t(s))return!1;const i={download:!0,skipColumn:[],tableName:\"myTable\",...s},n=t=>!i.skipColumn.includes(t)&&!e.columns.settings[t]?.hidden;let a=[];if(i.selection)if(Array.isArray(i.selection))for(let t=0;t<i.selection.length;t++)a=a.concat(e.pages[i.selection[t]-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.text??t.data)))));else a=a.concat(e.pages[i.selection-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.text??t.data)))));else a=a.concat(e.data.data.map((t=>t.filter(((t,e)=>n(e))).map((t=>t.data)))));const o=e.data.headings.filter(((t,e)=>n(e))).map((t=>t.text??String(t.data)));if(a.length){let t=`INSERT INTO \\`${i.tableName}\\` (`;if(o.forEach((e=>{t+=`\\`${e}\\`,`})),t=t.trim().substring(0,t.length-1),t+=\") VALUES \",a.forEach((e=>{t+=\"(\",e.forEach((e=>{t+=\"string\"==typeof e?`\"${e}\",`:`${e},`})),t=t.trim().substring(0,t.length-1),t+=\"),\"})),t=t.trim().substring(0,t.length-1),t+=\";\",i.download&&(t=`data:application/sql;charset=utf-8,${t}`),i.download){const e=document.createElement(\"a\");e.href=encodeURI(t),e.download=`${i.filename||\"datatable_export\"}.sql`,document.body.appendChild(e),e.click(),document.body.removeChild(e)}return t}return!1},exports.exportTXT=function(e,s={}){if(!e.hasHeadings&&!e.hasRows)return!1;if(!t(s))return!1;const i={download:!0,skipColumn:[],lineDelimiter:\"\\n\",columnDelimiter:\",\",...s},n=t=>!i.skipColumn.includes(t)&&!e.columns.settings[t]?.hidden;let a=[];const o=e.data.headings.filter(((t,e)=>n(e))).map((t=>t.text??t.data));if(a[0]=o,i.selection)if(Array.isArray(i.selection))for(let t=0;t<i.selection.length;t++)a=a.concat(e.pages[i.selection[t]-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.data)))));else a=a.concat(e.pages[i.selection-1].map((t=>t.row.filter(((t,e)=>n(e))).map((t=>t.data)))));else a=a.concat(e.data.data.map((t=>t.filter(((t,e)=>n(e))).map((t=>t.data)))));if(a.length){let t=\"\";if(a.forEach((e=>{e.forEach((e=>{\"string\"==typeof e&&(e=(e=(e=(e=(e=e.trim()).replace(/\\s{2,}/g,\" \")).replace(/\\n/g,\"  \")).replace(/\"/g,'\"\"')).replace(/#/g,\"%23\")).includes(\",\")&&(e=`\"${e}\"`),t+=e+i.columnDelimiter})),t=t.trim().substring(0,t.length-1),t+=i.lineDelimiter})),t=t.trim().substring(0,t.length-1),i.download&&(t=`data:text/csv;charset=utf-8,${t}`),i.download){const e=document.createElement(\"a\");e.href=encodeURI(t),e.download=`${i.filename||\"datatable_export\"}.txt`,document.body.appendChild(e),e.click(),document.body.removeChild(e)}return t}return!1},exports.isJson=e,exports.isObject=t,exports.makeEditable=function(t,e={}){const s=new K(t,e);return t.initialized?s.init():t.on(\"datatable.init\",(()=>s.init())),s};\n\n\n}).call(this)}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? window : {})\n},{}]},{},[1])(1)\n});\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/simple-datatables/style.css",
    "content": ".datatable-wrapper.no-header .datatable-container {\n\tborder-top: 1px solid #d9d9d9;\n}\n\n.datatable-wrapper.no-footer .datatable-container {\n\tborder-bottom: 1px solid #d9d9d9;\n}\n\n.datatable-top,\n.datatable-bottom {\n\tpadding: 8px 10px;\n}\n\n.datatable-top > nav:first-child,\n.datatable-top > div:first-child,\n.datatable-bottom > nav:first-child,\n.datatable-bottom > div:first-child {\n\tfloat: left;\n}\n\n.datatable-top > nav:last-child,\n.datatable-top > div:last-child,\n.datatable-bottom > nav:last-child,\n.datatable-bottom > div:last-child {\n\tfloat: right;\n}\n\n.datatable-selector {\n\tpadding: 6px;\n}\n\n.datatable-input {\n\tpadding: 6px 12px;\n}\n\n.datatable-info {\n\tmargin: 7px 0;\n}\n\n/* PAGER */\n.datatable-pagination ul {\n\tmargin: 0;\n\tpadding-left: 0;\n}\n\n.datatable-pagination li {\n\tlist-style: none;\n\tfloat: left;\n}\n\n.datatable-pagination li.datatable-hidden {\n    visibility: hidden;\n}\n\n.datatable-pagination a {\n\tborder: 1px solid transparent;\n\tfloat: left;\n\tmargin-left: 2px;\n\tpadding: 6px 12px;\n\tposition: relative;\n\ttext-decoration: none;\n\tcolor: #333;\n    cursor: pointer;\n}\n\n.datatable-pagination a:hover {\n\tbackground-color: #d9d9d9;\n}\n\n.datatable-pagination .datatable-active a,\n.datatable-pagination .datatable-active a:focus,\n.datatable-pagination .datatable-active a:hover {\n\tbackground-color: #d9d9d9;\n\tcursor: default;\n}\n\n.datatable-pagination .datatable-ellipsis a,\n.datatable-pagination .datatable-disabled a,\n.datatable-pagination .datatable-disabled a:focus,\n.datatable-pagination .datatable-disabled a:hover {\n    pointer-events: none;\n    cursor: default;\n}\n\n.datatable-pagination .datatable-disabled a,\n.datatable-pagination .datatable-disabled a:focus,\n.datatable-pagination .datatable-disabled a:hover {\n\tcursor: not-allowed;\n\topacity: 0.4;\n}\n\n.datatable-pagination .datatable-pagination a {\n\tfont-weight: bold;\n}\n\n/* TABLE */\n.datatable-table {\n\tmax-width: 100%;\n\twidth: 100%;\n\tborder-spacing: 0;\n\tborder-collapse: separate;\n}\n\n.datatable-table > tbody > tr > td,\n.datatable-table > tbody > tr > th,\n.datatable-table > tfoot > tr > td,\n.datatable-table > tfoot > tr > th,\n.datatable-table > thead > tr > td,\n.datatable-table > thead > tr > th {\n\tvertical-align: top;\n\tpadding: 8px 10px;\n}\n\n.datatable-table > thead > tr > th {\n\tvertical-align: bottom;\n\ttext-align: left;\n\tborder-bottom: 1px solid #d9d9d9;\n}\n\n.datatable-table > tfoot > tr > th {\n\tvertical-align: bottom;\n\ttext-align: left;\n\tborder-top: 1px solid #d9d9d9;\n}\n\n.datatable-table th {\n\tvertical-align: bottom;\n\ttext-align: left;\n}\n\n.datatable-table th a {\n\ttext-decoration: none;\n\tcolor: inherit;\n}\n\n.datatable-sorter, .datatable-filter {\n\tdisplay: inline-block;\n\theight: 100%;\n\tposition: relative;\n\twidth: 100%;\n}\n\n.datatable-sorter::before,\n.datatable-sorter::after {\n\tcontent: \"\";\n\theight: 0;\n\twidth: 0;\n\tposition: absolute;\n\tright: 4px;\n\tborder-left: 4px solid transparent;\n\tborder-right: 4px solid transparent;\n\topacity: 0.2;\n}\n\n\n.datatable-sorter::before {\n\tborder-top: 4px solid #000;\n\tbottom: 0px;\n}\n\n.datatable-sorter::after {\n\tborder-bottom: 4px solid #000;\n\tborder-top: 4px solid transparent;\n\ttop: 0px;\n}\n\n.datatable-ascending .datatable-sorter::after,\n.datatable-descending .datatable-sorter::before,\n.datatable-ascending .datatable-filter::after,\n.datatable-descending .datatable-filter::before {\n\topacity: 0.6;\n}\n\n.datatable-filter::before {\n    content: \"\";\n    position: absolute;\n    right: 4px;\n    opacity: 0.2;\n    width: 0;\n    height: 0;\n    border-left: 7px solid transparent;\n    border-right: 7px solid transparent;\n\tborder-radius: 50%;\n    border-top: 10px solid #000;\n    top: 25%;\n}\n\n.datatable-filter-active .datatable-filter::before {\n    opacity: 0.6;\n}\n\n.datatable-empty {\n\ttext-align: center;\n}\n\n.datatable-top::after, .datatable-bottom::after {\n\tclear: both;\n\tcontent: \" \";\n\tdisplay: table;\n}\n\ntable.datatable-table:focus tr.datatable-cursor > td:first-child {\n\tborder-left: 3px blue solid;\n}\n\ntable.datatable-table:focus {\n\toutline: solid 1px black;\n    outline-offset: -1px;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## 6.3.2 - 2023-02-22\n\n### Fixed\n- Removed a workaround for ensuring stylesheets are loaded in an outdated version of webkit. #TINY-9433\n\n## 6.3.1 - 2022-12-06\n\n### Fixed\n- HTML in messages for the `WindowManager.alert` and `WindowManager.confirm` APIs were not properly sanitized. #TINY-3548\n\n## 6.3.0 - 2022-11-23\n\n### Added\n- New `expand` function added to `tinymce.selection` which expands the selection around the nearest word. #TINY-9001\n- New `expand` function added to `tinymce.dom.RangeUtils` to return a new range expanded around the nearest word. #TINY-9001\n- New `color_map_background` and `color_map_foreground` options which set the base colors used in the `backcolor` and `forecolor` toolbar buttons and menu items. #TINY-9184\n- Added optional `storageKey` property to `colorinput` component and `colorswatch` fancy menu item. #TINY-9184\n- New `addView` function added to `editor.ui.registry` which makes it possible to register custom editor views. #TINY-9210\n- New `ToggleView` command which makes it possible to hide or show registered custom views. #TINY-9210\n- New `color_default_foreground` and `color_default_background` options to set the initial default color for the `forecolor` and `backcolor` toolbar buttons and menu items. #TINY-9183\n- New `getTransparentElements` function added to `tinymce.html.Schema` to return a map object of transparent HTML elements. #TINY-9172\n- Added `ToggleToolbarDrawer` event to subscribe to toolbar’s opening and closing. #TINY-9271\n\n### Changed\n- Transparent elements, like anchors, are now allowed in the root of the editor body if they contain blocks. #TINY-9172\n- Colorswatch keyboard navigation now starts on currently selected color if present in the colorswatch. #TINY-9283\n- `setContent` is now allowed to accept any custom keys and values as a second options argument. #TINY-9143\n\n### Improved\n- Transparent elements, like anchors, can now contain block elements. #TINY-9172\n- Colorswatch now displays a checkmark for selected color. #TINY-9283\n- Color picker dialog now starts on the appropriate color for the cursor position. #TINY-9213\n\n### Fixed\n- Parsing media content would cause a memory leak, which for example occurred when using the `getContent` API. #TINY-9186\n- Dragging a noneditable element toward the bottom edge would cause the page to scroll up. #TINY-9025\n- Range expanding capabilities would behave inconsistently depending on where the cursor was placed. #TINY-9029\n- Compilation errors were thrown when using TypeScript 4.8. #TINY-9161\n- Line separator scrolling in floating toolbars. #TINY-8948\n- A double bottom border appeared on inline mode editor for the `tinymce-5` skin. #TINY-9108\n- The editor header showed up even with no menubar and toolbar configured. #TINY-8819\n- Inline text pattern no longer triggers if it matches only the end but not the start. #TINY-8947\n- Matches of inline text patterns that are similar are now managed correctly. #TINY-8949\n- Using `editor.selection.getContent({ format: 'text' })` or `editor.getContent({ format: 'text' })` would sometimes deselect selected radio buttons. #TINY-9213\n- The context toolbar prevented the user from placing the cursor at the edges of the editor. #TINY-8890\n- The Quick Insert context toolbar provided by the `quickbars` plugin showed when the cursor was in a fake block caret. #TINY-9190\n- The `editor.selection.getRng()` API was not returning a proper range on hidden editors in Firefox. #TINY-9259\n- The `editor.selection.getBookmark()` API was not returning a proper bookmark on hidden editors in Firefox. #TINY-9259\n- Dragging a noneditable element before or after another noneditable element now works correctly. #TINY-9253\n- The restored selection after a redo or undo action was not scrolled into view. #TINY-9222\n- A newline could not be inserted when the selection was restored from a bookmark after an inline element with a `contenteditable=\"false\"` attribute. #TINY-9194\n- The global `tinymce.dom.styleSheetLoader` was not affected by the `content_css_cors` option. #TINY-6037\n- The caret was moved to the previous line when a text pattern executed a `mceInsertContent` command on Enter key when running on Firefox. #TINY-9193\n\n## 6.2.0 - 2022-09-08\n\n### Added\n- New `text_patterns_lookup` option to provide additional text patterns dynamically. #TINY-8778\n- New promotion element has been added to the default UI. It can be disabled using the new `promotion` option. #TINY-8840\n- New `format_noneditable_selector` option to specify the `contenteditable=\"false\"` elements that can be wrapped in a format. #TINY-8905\n- Added `allow` as a valid attribute for the `iframe` element in the editor schema. #TINY-8939\n- New `search` field in the `MenuButton` that shows a search field at the top of the menu, and refetches items when the search field updates. #TINY-8952\n\n### Improved\n- The formatter can now apply a format to a `contenteditable=\"false\"` element by wrapping it. Configurable using the `format_noneditable_selector` option. #TINY-8905\n- The autocompleter now supports a multiple character trigger using the new `trigger` configuration. #TINY-8887\n- The formatter now applies some inline formats, such as color and font size, to list item elements when the entire item content is selected. #TINY-8961\n- The installed and available plugin lists in the Help dialog are now sorted alphabetically. #TINY-9019\n- Alignment can now be applied to more types of embedded media elements. #TINY-8687\n\n### Changed\n- The `@menubar-row-separator-color` oxide variable no longer affects the divider between the Menubar and Toolbar. It only controls the color of the separator lines drawn in multiline Menubars. #TINY-8632\n- The `@toolbar-separator-color` oxide variable now affects the color of the separator between the Menubar and Toolbar only. #TINY-8632\n- Available Premium plugins, which are listed by name in the Help dialog, are no longer translated. #TINY-9019\n\n### Fixed\n- The Autolink plugin did not work when text nodes in the content were fragmented. #TINY-3723\n- Fixed multiple incorrect types on public APIs found while enabling TypeScript strict mode. #TINY-8806\n- The number of blank lines returned from `editor.getContent({format: 'text'})` differed between browsers. #TINY-8579\n- The editor focused via the `auto_focus` option was not scrolled into the viewport. #TINY-8785\n- Adding spaces immediately after a `contenteditable=\"false\"` block did not work properly in some circumstances. #TINY-8814\n- Elements with only `data-*` custom attributes were sometimes removed when they should not be removed. #TINY-8755\n- Selecting a figure with `class=\"image\"` incorrectly highlighted the link toolbar button. #TINY-8832\n- Specifying a single, non-default list style for the `advlist_bullet_styles` and `advlist_number_styles` options was not respected. #TINY-8721\n- Fixed multiple issues that occurred when formatting `contenteditable` elements. #TINY-8905\n- Spaces could be incorrectly added to `urlinput` dialog components (commonly but not exclusively presented in the *Insert/Edit Link* dialog) in certain cases. #TINY-8775\n- The text patterns logic threw an error when there were fragmented text nodes in a paragraph. #TINY-8779\n- Dragging a `contentEditable=false` element towards a document’s edge did not cause scrolling. #TINY-8874\n- Parsing large documents no longer throws a `Maximum call stack size exceeded` exception. #TINY-6945\n- DomParser filter matching was not checked between filters, which could lead to an exception in the parser. #TINY-8888\n- `contenteditable=\"false\"` lists can no longer be toggled; and `contenteditable=\"true\"` list elements within these lists can no longer be indented, split into another list element, or appended to the previous list element by deletion. #TINY-8920\n- Removed extra bottom padding in the context toolbar of the `tinymce-5` skin. #TINY-8980\n- Fixed a regression where pressing **Enter** added or deleted content outside the selection. #TINY-9101\n- Fixed a bug where pressing **Enter** deleted selected `contenteditable=\"false\"` `<pre>` elements. #TINY-9101\n- The `editor.insertContent()` API did not respect the `no_events` argument. #TINY-9140\n\n### Deprecated\n- The autocompleter configuration property, `ch`, has been deprecated. It will be removed in the next major release. Use the `trigger` property instead. #TINY-8887\n\n## 6.1.2 - 2022-07-29\n\n### Fixed\n- Reverted the undo level fix in the `autolink` plugin as it caused duplicated content in some edge cases. #TINY-8936\n\n## 6.1.1 - 2022-07-27\n\n### Fixed\n- Invalid special elements were not cleaned up correctly during sanitization. #TINY-8780\n- An exception was thrown when deleting all content if the start or end of the document had a `contenteditable=\"false\"` element. #TINY-8877\n- When a sidebar was opened using the `sidebar_show` option, its associated toolbar button was not highlighted. #TINY-8873\n- When converting a URL to a link, the `autolink` plugin did not fire an `ExecCommand` event, nor did it create an undo level. #TINY-8896\n- Worked around a Firefox bug which resulted in cookies not being available inside the editor content. #TINY-8916\n- `<pre>` content pasted into a `<pre>` block that had inline styles or was `noneditable` now merges correctly with the surrounding content. #TINY-8860\n- After a `codesample` was pasted, the insertion point was placed incorrectly. #TINY-8861\n\n## 6.1.0 - 2022-06-29\n\n### Added\n- New `sidebar_show` option to show the specified sidebar on initialization. #TINY-8710\n- New `newline_behavior` option controls what happens when the Return or Enter key is pressed or the `mceInsertNewLine` command is used. #TINY-8458\n- New `iframe_template_callback` option in the Media plugin. Patch provided by Namstel. #TINY-8684\n- New `transparent` property for `iframe` dialog component. #TINY-8534\n- New `removeAttributeFilter` and `removeNodeFilter` functions added to the DomParser and DOM Serializer APIs. #TINY-7847\n- New `dispatchChange` function added to the UndoManager API to fire the change with current editor status as level and current undoManager layer as lastLevel. #TINY-8641\n\n### Improved\n- Clearer focus states for buttons while navigating with a keyboard. #TINY-8557\n- Support annotating certain block elements directly when using the editor's Annotation API. #TINY-8698\n- The `mceLink` command can now take the value `{ dialog: true }` to always open the link dialog. #TINY-8057\n- All help dialog links to `https://www.tiny.cloud` now include `rel=\"noopener\"` to avoid potential security issues. #TINY-8834\n\n### Changed\n- The `end_container_on_empty_block` option can now take a string of blocks, allowing the exiting of a blockquote element by pressing Enter or Return twice. #TINY-6559\n- The default value for `end_container_on_empty_block` option has been changed to `'blockquote'`. #TINY-6559\n- Link menu and toolbar buttons now always execute the `mceLink` command. #TINY-8057\n- Toggling fullscreen mode when using the Fullscreen plugin now also fires the `ResizeEditor` event. #TINY-8701\n- Getting the editor's text content now returns newlines instead of an empty string if more than one empty paragraph exists. #TINY-8578\n- Custom elements are now treated as non-empty elements by the schema. #TINY-4784\n- The autocompleter's menu HTML element is now positioned instead of the wrapper. #TINY-6476\n- Choice menu items will now use the `'menuitemradio'` aria role to better reflect that only a single item can be active. #TINY-8602\n\n### Fixed\n- Some Template plugin option values were not escaped properly when doing replacement lookups with Regular Expressions. #TINY-7433\n- Copy events were not dispatched in readonly mode. #TINY-6800\n- `<pre>` tags were not preserved when copying and pasting. #TINY-7719\n- The URL detection used for autolink and smart paste did not work if a path segment contained valid characters such as `!` and `:`. #TINY-8069\n- In some cases pressing the Backspace or Delete key would incorrectly step into tables rather than remain outside. #TINY-8592\n- Links opened when Alt+Enter or Option+Return was typed even when `preventDefault()` was called on the keydown event. #TINY-8661\n- Inconsistent visual behavior between choosing Edit -> Select All and typing Ctrl+A or Cmd+A when a document contained an image. #TINY-4550\n- Ctrl+Shift+Home/End or Cmd+Shift+Up-arrow/Down-arrow did not expand the selection to a `contenteditable=\"false\"` element if the element was at the beginning or end of a document. #TINY-7795\n- Triple-clicking did not select a paragraph in Google Chrome in some circumstances. #TINY-8215\n- Images were not showing as selected when selected along with other content. #TINY-5947\n- Selection direction was not stored or restored when getting or setting selection bookmarks. #TINY-8599\n- When text within an inline boundary element was selected and the right-arrow key was pressed, the insertion point incorrectly moved to the left. #TINY-8601\n- In some versions of Safari, the `editor.selection.isForward()` API could throw an exception due to an invalid selection. #TINY-8686\n- The selection is no longer incorrectly moved inside a comment by the `editor.selection.normalize()` API. #TINY-7817\n- The `InsertParagraph` or `mceInsertNewLine` commands did not delete the current selection like the native command does. #TINY-8606\n- The `InsertLineBreak` command did not replace selected content. #TINY-8458\n- If selected content straddled a parent and nested list, cutting the selection did not always set the list style to `'none'` on the parent list. #TINY-8078\n- Delete operations could behave incorrectly if the selection contains a `contenteditable=\"false\"` element located at the edge of content. #TINY-8729\n- Spaces were not added correctly on some browsers when the insertion point was immediately before or after a `contenteditable=\"false\"` block element. #TINY-8588\n- Images that used a Data URI were corrupted when the data wasn't base64 encoded. #TINY-8337\n- `uploadImages` no longer triggers two change events if there is a removal of images on upload. #TINY-8641\n- Preview and Insert Template dialogs now display the correct content background color when using dark skins. #TINY-8534\n- Dialogs no longer exceed window height on smaller screens. #TINY-8146\n- UI components, such as dialogs, would in some cases cause the Esc keyup event to incorrectly trigger inside the editor. #TINY-7005\n- Fixed incorrect word breaks in menus when the menu presented with a scrollbar. #TINY-8572\n- Notifications did not properly reposition when toggling fullscreen mode. #TINY-8701\n- Text alignments, such as flush left and centered, could not be applied to `<pre>` elements. #TINY-7715\n- Indenting or outdenting list items inside a block element that was inside another list item did not work. #TINY-7209\n- Changing the list type of a list within another block element altered the parent element that contained that list. #TINY-8068\n- Pasting columns in tables could, in some circumstances, result in an invalid table. #TINY-8040\n- Copying columns in tables could sometimes result in an invalid copy. #TINY-8040\n- Changing table properties with the `table_style_by_css` option set to `false` would sometimes reset the table width. #TINY-8758\n- Custom elements added to otherwise blank lines were removed during serialization. #TINY-4784\n- The editor's autocompleter was not triggered at the start of nested list items. #TINY-8759\n- Some function types in the TreeWalker API missed that it could return `undefined`. #TINY-8592\n- Nuget packages for .NET and .NET Core are now configured to copy TinyMCE into `/wwwroot/lib/` when TinyMCE is installed into a project. #TINY-8611\n\n## 6.0.3 - 2022-05-25\n\n### Fixed\n- Could not remove values when multiple cells were selected with the cell properties dialog. #TINY-8625\n- Could not remove values when multiple rows were selected with the row properties dialog. #TINY-8625\n- Empty lines that were formatted in a ranged selection using the `format_empty_lines` option were not kept in the serialized content. #TINY-8639\n- The `s` element was missing from the default schema text inline elements. #TINY-8639\n- Some text inline elements specified via the schema were not removed when empty by default. #TINY-8639\n\n## 6.0.2 - 2022-04-27\n\n### Fixed\n- Some media elements wouldn't update when changing the source URL. #TINY-8660\n- Inline toolbars flickered when switching between editors. #TINY-8594\n- Multiple inline toolbars were shown if focused too quickly. #TINY-8503\n- Added background and additional spacing for the text labeled buttons in the toolbar to improve visual clarity. #TINY-8617\n- Toolbar split buttons with text used an incorrect width on touch devices. #TINY-8647\n\n## 6.0.1 - 2022-03-23\n\n### Fixed\n- Fixed the dev ZIP missing the required `bin` scripts to build from the source. #TINY-8542\n- Fixed a regression whereby text patterns couldn't be updated at runtime. #TINY-8540\n- Fixed an issue where tables with colgroups could be copied incorrectly in some cases. #TINY-8568\n- Naked buttons better adapt to various background colors, improved text contrast in notifications. #TINY-8533\n- The autocompleter would not fire the `AutocompleterStart` event nor close the menu in some cases. #TINY-8552\n- It wasn't possible to select text right after an inline noneditable element. #TINY-8567\n- Fixed a double border showing for the `tinymce-5` skin when using `toolbar_location: 'bottom'`. #TINY-8564\n- Clipboard content was not generated correctly when cutting and copying `contenteditable=\"false\"` elements. #TINY-8563\n- Fixed the box-shadow getting clipped in autocompletor popups. #TINY-8573\n- The `buttonType` property did not work for dialog footer buttons. #TINY-8582\n- Fix contrast ratio for error messages. #TINY-8586\n\n## 6.0.0 - 2022-03-03\n\n### Added\n- New `editor.options` API to replace the old `editor.settings` and `editor.getParam` APIs. #TINY-8206\n- New `editor.annotator.removeAll` API to remove all annotations by name. #TINY-8195\n- New `Resource.unload` API to make it possible to unload resources. #TINY-8431\n- New `FakeClipboard` API on the `tinymce` global. #TINY-8353\n- New `dispatch()` function to replace the now deprecated `fire()` function in various APIs. #TINY-8102\n- New `AutocompleterStart`, `AutocompleterUpdate` and `AutocompleterEnd` events. #TINY-8279\n- New `mceAutocompleterClose`, `mceAutocompleterReload` commands. #TINY-8279\n- New `mceInsertTableDialog` command to open the insert table dialog. #TINY-8273\n- New `slider` dialog component. #TINY-8304\n- New `imagepreview` dialog component, allowing preview and zoom of any image URL. #TINY-8333\n- New `buttonType` property on dialog button components, supporting `toolbar` style in addition to `primary` and `secondary`. #TINY-8304\n- The `tabindex` attribute is now copied from the target element to the iframe. #TINY-8315\n\n### Improved\n- New default theme styling for TinyMCE 6 facelift with old skin available as `tinymce-5` and `tinymce-5-dark`. #TINY-8373\n- The default height of editor has been increased from `200px` to `400px` to improve the usability of the editor. #TINY-6860\n- The upload results returned from the `editor.uploadImages()` API now includes a `removed` flag, reflecting if the image was removed after a failed upload. #TINY-7735\n- The `ScriptLoader`, `StyleSheetLoader`, `AddOnManager`, `PluginManager` and `ThemeManager` APIs will now return a `Promise` when loading resources instead of using callbacks. #TINY-8325\n- A `ThemeLoadError` event is now fired if the theme fails to load. #TINY-8325\n- The `BeforeSetContent` event will now include the actual serialized content when passing in an `AstNode` to the `editor.setContent` API. #TINY-7996\n- Improved support for placing the caret before or after noneditable elements within the editor. #TINY-8169\n- Calls to `editor.selection.setRng` now update the caret position bookmark used when focus is returned to the editor. #TINY-8450\n- The `emoticon` plugin dialog, toolbar and menu item has been updated to use the more accurate `Emojis` term. #TINY-7631\n- The dialog `redial` API will now only rerender the changed components instead of the whole dialog. #TINY-8334\n- The dialog API `setData` method now uses a deep merge algorithm to support partial nested objects. #TINY-8333\n- The dialog spec `initialData` type is now `Partial<T>` to match the underlying implementation details. #TINY-8334\n- Notifications no longer require a timeout to disable the close button. #TINY-6679\n- The editor theme is now fetched in parallel with the icons, language pack and plugins. #TINY-8453\n\n### Changed\n- TinyMCE is now MIT licensed. #TINY-2316\n- Moved the `paste` plugin's functionality to TinyMCE core. #TINY-8310\n- The `paste_data_images` option now defaults to `true`. #TINY-8310\n- Moved the `noneditable` plugin to TinyMCE core. #TINY-8311\n- Renamed the `noneditable_noneditable_class` option to `noneditable_class`. #TINY-8311\n- Renamed the `noneditable_editable_class` option to `editable_class`. #TINY-8311\n- Moved the `textpattern` plugin to TinyMCE core. #TINY-8312\n- Renamed the `textpattern_patterns` option to `text_patterns`. #TINY-8312\n- Moved the `hr` plugin's functionality to TinyMCE core. #TINY-8313\n- Moved the `print` plugin's functionality to TinyMCE core. #TINY-8314\n- Moved non-UI table functionality to core. #TINY-8273\n- The `DomParser` API no longer uses a custom parser internally and instead uses the native `DOMParser` API. #TINY-4627\n- The `editor.getContent()` API can provide custom content by preventing and overriding `content` in the `BeforeGetContent` event. This makes it consistent with the `editor.selection.getContent()` API. #TINY-8018\n- The `editor.setContent()` API can now be prevented using the `BeforeSetContent` event. This makes it consistent with the `editor.selection.setContent()` API. #TINY-8018\n- Add-ons such as plugins and themes are no longer constructed using the `new` operator. #TINY-8256\n- A number of APIs that were not proper classes, are no longer constructed using the `new` operator. #TINY-8322\n- The Editor commands APIs will no longer fallback to executing the browsers native command functionality. #TINY-7829\n- The Editor query command APIs will now return `false` or an empty string on removed editors. #TINY-7829\n- The `mceAddEditor` and `mceToggleEditor` commands now take an object as their value to specify the id and editor options. #TINY-8138\n- The `mceInsertTable` command can no longer open the insert table dialog. Use the `mceInsertTableDialog` command instead. #TINY-8273\n- The `plugins` option now returns a `string` array instead of a space separated string. #TINY-8455\n- The `media` plugin no longer treats `iframe`, `video`, `audio` or `object` elements as \"special\" and will validate the contents against the schema. #TINY-8382\n- The `images_upload_handler` option is no longer passed a `success` or `failure` callback and instead requires a `Promise` to be returned with the upload result. #TINY-8325\n- The `tinymce.settings` global property is no longer set upon initialization. #TINY-7359\n- The `change` event is no longer fired on first modification. #TINY-6920\n- The `GetContent` event will now always pass a `string` for the `content` property. #TINY-7996\n- Changed the default tag for the strikethrough format to the `s` tag when using a html 5 schema. #TINY-8262\n- The `strike` tag is automatically converted to the `s` tag when using a html 5 schema. #TINY-8262\n- Aligning a table to the left or right will now use margin styling instead of float styling. #TINY-6558\n- The `:` control character has been changed to `~` for the schema `valid_elements` and `extended_valid_elements` options. #TINY-6726\n- The `primary` property on dialog buttons has been deprecated. Use the new `buttonType` property instead. #TINY-8304\n- Changed the default statusbar element path delimiter from `»` to `›`. #TINY-8372\n- Replaced the `Powered by Tiny` branding text with the Tiny logo. #TINY-8371\n- The default minimum height of editor has been changed to 100px to prevent the UI disappearing while resizing. #TINY-6860\n- RGB colors are no longer converted to hex values when parsing or serializing content. #TINY-8163\n- Replaced the `isDisabled()` function with an `isEnabled()` function for various APIs. #TINY-8101\n- Replaced the `enable()` and `disable()` functions with a `setEnabled(state)` function in various APIs. #TINY-8101\n- Replaced the `disabled` property with an `enabled` property in various APIs. #TINY-8101\n- Replaced the `disable(name)` and `enable(name)` functions with a `setEnabled(name, state)` function in the Dialog APIs. #TINY-8101\n- Renamed the `tinymce.Env.os.isOSX` API to `tinymce.Env.os.isMacOS`. #TINY-8175\n- Renamed the `tinymce.Env.browser.isChrome` API to `tinymce.Env.browser.isChromium` to better reflect its functionality. #TINY-8300\n- Renamed the `getShortEndedElements` Schema API to `getVoidElements`. #TINY-8344\n- Renamed the `font_formats` option to `font_family_formats`. #TINY-8328\n- Renamed the `fontselect` toolbar button and `fontformats` menu item to `fontfamily`. #TINY-8328\n- Renamed the `fontsize_formats` option to `font_size_formats`. #TINY-8328\n- Renamed the `fontsizeselect` toolbar button and `fontsizes` menu item to `fontsize`. #TINY-8328\n- Renamed the `formatselect` toolbar button and `blockformats` menu item to `blocks`. #TINY-8328\n- Renamed the `styleselect` toolbar button and `formats` menu item to `styles`. #TINY-8328\n- Renamed the `lineheight_formats` option to `line_height_formats`. #TINY-8328\n- Renamed the `getWhiteSpaceElements()` function to `getWhitespaceElements()` in the `Schema` API. #TINY-8102\n- Renamed the `mceInsertClipboardContent` command `content` property to `html` to better reflect what data is passed. #TINY-8310\n- Renamed the `default_link_target` option to `link_default_target` for both `link` and `autolink` plugins. #TINY-4603\n- Renamed the `rel_list` option to `link_rel_list` for the `link` plugin. #TINY-4603\n- Renamed the `target_list` option to `link_target_list` for the `link` plugin. #TINY-4603\n- The default value for the `link_default_protocol` option has been changed to `https` instead of `http`. #TINY-7824\n- The default value for the `element_format` option has been changed to `html`. #TINY-8263\n- The default value for the `schema` option has been changed to `html5`. #TINY-8261\n- The default value for the `table_style_by_css` option has been changed to `true`. #TINY-8259\n- The default value for the `table_use_colgroups` option has been changed to `true`. #TINY-8259\n\n### Fixed\n- The object returned from the `editor.fire()` API was incorrect if the editor had been removed. #TINY-8018\n- The `editor.selection.getContent()` API did not respect the `no_events` argument. #TINY-8018\n- The `editor.annotator.remove` API did not keep selection when removing the annotation. #TINY-8195\n- The `GetContent` event was not fired when getting `tree` or `text` formats using the `editor.selection.getContent()` API. #TINY-8018\n- The `beforeinput` and `input` events would sometimes not fire as expected when deleting content. #TINY-8168 #TINY-8329\n- The `table` plugin would sometimes not correctly handle headers in the `tfoot` section. #TINY-8104\n- The `silver` theme UI was incorrectly rendered before plugins had initialized. #TINY-8288\n- The aria labels for the color picker dialog were not translated. #TINY-8381\n- Fixed sub-menu items not read by screen readers. Patch contributed by westonkd. #TINY-8417\n- Dialog labels and other text-based UI properties did not escape HTML markup. #TINY-7524\n- Anchor elements would render incorrectly when using the `allow_html_in_named_anchor` option. #TINY-3799\n- The `AstNode` HTML serializer did not serialize `pre` or `textarea` elements correctly when they contained newlines. #TINY-8446\n- Fixed sub-menu items not read by screen readers. Patch contributed by westonkd. #TINY-8417\n- The Home or End keys would move out of a editable element contained within a noneditable element. #TINY-8201\n- Dialogs could not be opened in inline mode before the editor had been rendered. #TINY-8397\n- Clicking on menu items could cause an unexpected console warning if the `onAction` function caused the menu to close. #TINY-8513\n- Fixed various color and contrast issues for the dark skins. #TINY-8527\n\n### Removed\n- Removed support for Microsoft Internet Explorer 11. #TINY-8194 #TINY-8241\n- Removed support for Microsoft Word from the opensource paste functionality. #TINY-7493\n- Removed support for the `plugins` option allowing a mixture of a string array and of space separated strings. #TINY-8399\n- Removed support for the deprecated `false` value for the `forced_root_block` option. #TINY-8260\n- Removed the jQuery integration. #TINY-4519\n- Removed the `imagetools` plugin, which is now classified as a Premium plugin. #TINY-8209\n- Removed the `imagetools` dialog component. #TINY-8333\n- Removed the `toc` plugin, which is now classified as a Premium plugin. #TINY-8250\n- Removed the `tabfocus` plugin. #TINY-8315\n- Removed the `textpattern` plugin's API as part of moving it to core. #TINY-8312\n- Removed the `table` plugin's API. #TINY-8273\n- Removed the callback for the `EditorUpload` API. #TINY-8325\n- Removed the legacy browser detection properties from the `Env` API. #TINY-8162\n- Removed the `filterNode` method from the `DomParser` API. #TINY-8249\n- Removed the `SaxParser` API. #TINY-8218\n- Removed the `tinymce.utils.Promise` API. #TINY-8241\n- Removed the `toHex` function for the `DOMUtils` and `Styles` APIs. #TINY-8163\n- Removed the `execCommand` handler function from the plugin and theme interfaces. #TINY-7829\n- Removed the `editor.settings` property as it has been replaced by the new Options API. #TINY-8236\n- Removed the `shortEnded` and `fixed` properties on `tinymce.html.Node` class. #TINY-8205\n- Removed the `mceInsertRawHTML` command. #TINY-8214\n- Removed the style field from the `image` plugin dialog advanced tab. #TINY-3422\n- Removed the `paste_filter_drop` option as native drag and drop handling is no longer supported. #TINY-8511\n- Removed the legacy `mobile` theme. #TINY-7832\n- Removed the deprecated `$`, `Class`, `DomQuery` and `Sizzle` APIs. #TINY-4520 #TINY-8326\n- Removed the deprecated `Color`, `JSON`, `JSONP` and `JSONRequest`. #TINY-8162\n- Removed the deprecated `XHR` API. #TINY-8164\n- Removed the deprecated `setIconStroke` Split Toolbar Button API. #TINY-8162\n- Removed the deprecated `editors` property from `EditorManager`. #TINY-8162\n- Removed the deprecated `execCallback` and `setMode` APIs from `Editor`. #TINY-8162\n- Removed the deprecated `addComponents` and `dependencies` APIs from `AddOnManager`. #TINY-8162\n- Removed the deprecated `clearInterval`, `clearTimeout`, `debounce`, `requestAnimationFrame`, `setInterval`, `setTimeout` and `throttle` APIs from `Delay`. #TINY-8162\n- Removed the deprecated `Schema` options. #TINY-7821\n- Removed the deprecated `file_browser_callback_types`, `force_hex_style_colors` and `images_dataimg_filter` options. #TINY-7823\n- Removed the deprecated `filepicker_validator_handler`, `force_p_newlines`, `gecko_spellcheck`, `tab_focus`, `table_responsive_width` and `toolbar_drawer` options. #TINY-7820\n- Removed the deprecated `media_scripts` option in the `media` plugin. #TINY-8421\n- Removed the deprecated `editor_deselector`, `editor_selector`, `elements`, `mode` and `types` legacy TinyMCE init options. #TINY-7822\n- Removed the deprecated `content_editable_state` and `padd_empty_with_br` options. #TINY-8400\n- Removed the deprecated `autoresize_on_init` option from the `autoresize` plugin. #TINY-8400\n- Removed the deprecated `fullpage`, `spellchecker`, `bbcode`, `legacyoutput`, `colorpicker`, `contextmenu` and `textcolor` plugins. #TINY-8192\n- Removed the undocumented `editor.editorCommands.hasCustomCommand` API. #TINY-7829\n- Removed the undocumented `mceResetDesignMode`, `mceRepaint` and `mceBeginUndoLevel` commands. #TINY-7829\n\n### Deprecated\n- The dialog button component's `primary` property has been deprecated and will be removed in the next major release. Use the new `buttonType` property instead. #TINY-8304\n- The `fire()` function of `tinymce.Editor`, `tinymce.dom.EventUtils`, `tinymce.dom.DOMUtils`, `tinymce.util.Observable` and `tinymce.util.EventDispatcher` has been deprecated and will be removed in the next major release. Use the `dispatch()` function instead. #TINY-8102\n- The `content` property on the `SetContent` event has been deprecated and will be removed in the next major release. #TINY-8457\n- The return value of the `editor.setContent` API has been deprecated and will be removed in the next major release. #TINY-8457\n\n## 5.10.3 - 2022-02-09\n\n### Fixed\n- Alignment would sometimes be removed on parent elements when changing alignment on certain inline nodes, such as images. #TINY-8308\n- The `fullscreen` plugin would reset the scroll position when exiting fullscreen mode. #TINY-8418\n\n## 5.10.2 - 2021-11-17\n\n### Fixed\n- Internal selectors were appearing in the style list when using the `importcss` plugin. #TINY-8238\n\n## 5.10.1 - 2021-11-03\n\n### Fixed\n- The iframe aria help text was not read by some screen readers. #TINY-8171\n- Clicking the `forecolor` or `backcolor` toolbar buttons would do nothing until selecting a color. #TINY-7836\n- Crop functionality did not work in the `imagetools` plugin when the editor was rendered in a shadow root. #TINY-6387\n- Fixed an exception thrown on Safari when closing the `searchreplace` plugin dialog. #TINY-8166\n- The `autolink` plugin did not convert URLs to links when starting with a bracket. #TINY-8091\n- The `autolink` plugin incorrectly created nested links in some cases. #TINY-8091\n- Tables could have an incorrect height set on rows when rendered outside of the editor. #TINY-7699\n- In certain circumstances, the table of contents plugin would incorrectly add an extra empty list item. #TINY-4636\n- The insert table grid menu displayed an incorrect size when re-opening the grid. #TINY-6532\n- The word count plugin was treating the zero width space character (`&#8203;`) as a word. #TINY-7484\n\n## 5.10.0 - 2021-10-11\n\n### Added\n- Added a new `URI.isDomSafe(uri)` API to check if a URI is considered safe to be inserted into the DOM. #TINY-7998\n- Added the `ESC` key code constant to the `VK` API. #TINY-7917\n- Added a new `deprecation_warnings` setting for turning off deprecation console warning messages. #TINY-8049\n\n### Improved\n- The `element` argument of the `editor.selection.scrollIntoView()` API is now optional, and if it is not provided the current selection will be scrolled into view. #TINY-7291\n\n### Changed\n- The deprecated `scope` attribute is no longer added to `td` cells when converting a row to a header row. #TINY-7731\n- The number of `col` elements is normalized to match the number of columns in a table after a table action. #TINY-8011\n\n### Fixed\n- Fixed a regression that caused block wrapper formats to apply and remove incorrectly when using a collapsed selection with multiple words. #TINY-8036\n- Resizing table columns in some scenarios would resize the column to an incorrect position. #TINY-7731\n- Inserting a table where the parent element had padding would cause the table width to be incorrect. #TINY-7991\n- The resize backdrop element did not have the `data-mce-bogus=\"all\"` attribute set to prevent it being included in output. #TINY-7854\n- Resize handles appeared on top of dialogs and menus when using an inline editor. #TINY-3263\n- Fixed the `autoresize` plugin incorrectly scrolling to the top of the editor content in some cases when changing content. #TINY-7291\n- Fixed the `editor.selection.scrollIntoView()` type signature, as it incorrectly required an `Element` instead of `HTMLElement`. #TINY-7291\n- Table cells that were both row and column headers did not retain the correct state when converting back to a regular row or column. #TINY-7709\n- Clicking beside a non-editable element could cause the editor to incorrectly scroll to the top of the content. #TINY-7062\n- Clicking in a table cell, with a non-editable element in an adjacent cell, incorrectly caused the non-editable element to be selected. #TINY-7736\n- Split toolbar buttons incorrectly had nested `tabindex=\"-1\"` attributes. #TINY-7879\n- Fixed notifications rendering in the wrong place initially and when the page was scrolled. #TINY-7894\n- Fixed an exception getting thrown when the number of `col` elements didn't match the number of columns in a table. #TINY-7041 #TINY-8011\n- The table selection state could become incorrect after selecting a noneditable table cell. #TINY-8053\n- As of Mozilla Firefox 91, toggling fullscreen mode with `toolbar_sticky` enabled would cause the toolbar to disappear. #TINY-7873\n- Fixed URLs not cleaned correctly in some cases in the `link` and `image` plugins. #TINY-7998\n- Fixed the `image` and `media` toolbar buttons incorrectly appearing to be in an inactive state in some cases. #TINY-3463\n- Fixed the `editor.selection.selectorChanged` API not firing if the selector matched the current selection when registered in some cases. #TINY-3463\n- Inserting content into a `contenteditable=\"true\"` element that was contained within a `contenteditable=\"false\"` element would move the selection to an incorrect location. #TINY-7842\n- Dragging and dropping `contenteditable=\"false\"` elements could result in the element being placed in an unexpected location. #TINY-7917\n- Pressing the Escape key would not cancel a drag action that started on a `contenteditable=\"false\"` element within the editor. #TINY-7917\n- `video` and `audio` elements were unable to be played when the `media` plugin live embeds were enabled in some cases. #TINY-7674\n- Pasting images would throw an exception if the clipboard `items` were not files (for example, screenshots taken from gnome-software). Patch contributed by cedric-anne. #TINY-8079\n\n### Deprecated\n- Several APIs have been deprecated. See the release notes section for information. #TINY-8023 #TINY-8063\n- Several Editor settings have been deprecated. See the release notes section for information. #TINY-8086\n- The Table of Contents and Image Tools plugins will be classified as Premium plugins in the next major release. #TINY-8087\n- Word support in the `paste` plugin has been deprecated and will be removed in the next major release. #TINY-8087\n\n## 5.9.2 - 2021-09-08\n\n### Fixed\n- Fixed an exception getting thrown when disabling events and setting content. #TINY-7956\n- Delete operations could behave incorrectly if the selection crossed a table boundary. #TINY-7596\n\n## 5.9.1 - 2021-08-27\n\n### Fixed\n- Published TinyMCE types failed to compile in strict mode. #TINY-7915\n- The `TableModified` event sometimes didn't fire when performing certain table actions. #TINY-7916\n\n## 5.9.0 - 2021-08-26\n\n### Added\n- Added a new `mceFocus` command that focuses the editor. Equivalent to using `editor.focus()`. #TINY-7373\n- Added a new `mceTableToggleClass` command which toggles the provided class on the currently selected table. #TINY-7476\n- Added a new `mceTableCellToggleClass` command which toggles the provided class on the currently selected table cells. #TINY-7476\n- Added a new `tablecellvalign` toolbar button and menu item for vertical table cell alignment. #TINY-7477\n- Added a new `tablecellborderwidth` toolbar button and menu item to change table cell border width. #TINY-7478\n- Added a new `tablecellborderstyle` toolbar button and menu item to change table cell border style. #TINY-7478\n- Added a new `tablecaption` toolbar button and menu item to toggle captions on tables. #TINY-7479\n- Added a new `mceTableToggleCaption` command that toggles captions on a selected table. #TINY-7479\n- Added a new `tablerowheader` toolbar button and menu item to toggle the header state of row cells. #TINY-7478\n- Added a new `tablecolheader` toolbar button and menu item to toggle the header state of column cells. #TINY-7482\n- Added a new `tablecellbordercolor` toolbar button and menu item to select table cell border colors, with an accompanying setting `table_border_color_map` to customize the available values. #TINY-7480\n- Added a new `tablecellbackgroundcolor` toolbar button and menu item to select table cell background colors, with an accompanying setting `table_background_color_map` to customize the available values. #TINY-7480\n- Added a new `language` menu item and toolbar button to add `lang` attributes to content, with an accompanying `content_langs` setting to specify the languages available. #TINY-6149\n- A new `lang` format is now available that can be used with `editor.formatter`, or applied with the `Lang` editor command. #TINY-6149\n- Added a new `language` icon for the `language` toolbar button. #TINY-7670\n- Added a new `table-row-numbering` icon. #TINY-7327\n- Added new plugin commands: `mceEmoticons` (Emoticons), `mceWordCount` (Word Count), and `mceTemplate` (Template). #TINY-7619\n- Added a new `iframe_aria_text` setting to set the iframe title attribute. #TINY-1264\n- Added a new DomParser `Node.children()` API to return all the children of a `Node`. #TINY-7756\n\n### Improved\n- Sticky toolbars can now be offset from the top of the page using the new `toolbar_sticky_offset` setting. #TINY-7337\n- Fancy menu items now accept an `initData` property to allow custom initialization data. #TINY-7480\n- Improved the load time of the `fullpage` plugin by using the existing editor schema rather than creating a new one. #TINY-6504\n- Improved the performance when UI components are rendered. #TINY-7572\n- The context toolbar no longer unnecessarily repositions to the top of large elements when scrolling. #TINY-7545\n- The context toolbar will now move out of the way when it overlaps with the selection, such as in table cells. #TINY-7192\n- The context toolbar now uses a short animation when transitioning between different locations. #TINY-7740\n- `Env.browser` now uses the User-Agent Client Hints API where it is available. #TINY-7785\n- Icons with a `-rtl` suffix in their name will now automatically be used when the UI is rendered in right-to-left mode. #TINY-7782\n- The `formatter.match` API now accepts an optional `similar` parameter to check if the format partially matches. #TINY-7712\n- The `formatter.formatChanged` API now supports providing format variables when listening for changes. #TINY-7713\n- The formatter will now fire `FormatApply` and `FormatRemove` events for the relevant actions. #TINY-7713\n- The `autolink` plugin link detection now permits custom protocols. #TINY-7714\n- The `autolink` plugin valid link detection has been improved. #TINY-7714\n\n### Changed\n- Changed the load order so content CSS is loaded before the editor is populated with content. #TINY-7249\n- Changed the `emoticons`, `wordcount`, `code`, `codesample`, and `template` plugins to open dialogs using commands. #TINY-7619\n- The context toolbar will no longer show an arrow when it overlaps the content, such as in table cells. #TINY-7665\n- The context toolbar will no longer overlap the statusbar for toolbars using `node` or `selection` positions. #TINY-7666\n\n### Fixed\n- The `editor.fire` API was incorrectly mutating the original `args` provided. #TINY-3254\n- Unbinding an event handler did not take effect immediately while the event was firing. #TINY-7436\n- Binding an event handler incorrectly took effect immediately while the event was firing. #TINY-7436\n- Unbinding a native event handler inside the `remove` event caused an exception that blocked editor removal. #TINY-7730\n- The `SetContent` event contained the incorrect `content` when using the `editor.selection.setContent()` API. #TINY-3254\n- The editor content could be edited after calling `setProgressState(true)` in iframe mode. #TINY-7373\n- Tabbing out of the editor after calling `setProgressState(true)` behaved inconsistently in iframe mode. #TINY-7373\n- Flash of unstyled content while loading the editor because the content CSS was loaded after the editor content was rendered. #TINY-7249\n- Partially transparent RGBA values provided in the `color_map` setting were given the wrong hex value. #TINY-7163\n- HTML comments with mismatched quotes were parsed incorrectly under certain circumstances. #TINY-7589\n- The editor could crash when inserting certain HTML content. #TINY-7756\n- Inserting certain HTML content into the editor could result in invalid HTML once parsed. #TINY-7756\n- Links in notification text did not show the correct mouse pointer. #TINY-7661\n- Using the Tab key to navigate into the editor on Microsoft Internet Explorer 11 would incorrectly focus the toolbar. #TINY-3707\n- The editor selection could be placed in an incorrect location when undoing or redoing changes in a document containing `contenteditable=\"false\"` elements. #TINY-7663\n- Menus and context menus were not closed when clicking into a different editor. #TINY-7399\n- Context menus on Android were not displayed when more than one HTML element was selected. #TINY-7688\n- Disabled nested menu items could still be opened. #TINY-7700\n- The nested menu item chevron icon was not fading when the menu item was disabled. #TINY-7700\n- `imagetools` buttons were incorrectly enabled for remote images without `imagetools_proxy` set. #TINY-7772\n- Only table content would be deleted when partially selecting a table and content outside the table. #TINY-6044\n- The table cell selection handling was incorrect in some cases when dealing with nested tables. #TINY-6298\n- Removing a table row or column could result in the cursor getting placed in an invalid location. #TINY-7695\n- Pressing the Tab key to navigate through table cells did not skip noneditable cells. #TINY-7705\n- Clicking on a noneditable table cell did not show a visual selection like other noneditable elements. #TINY-7724\n- Some table operations would incorrectly cause table row attributes and styles to be lost. #TINY-6666\n- The selection was incorrectly lost when using the `mceTableCellType` and `mceTableRowType` commands. #TINY-6666\n- The `mceTableRowType` was reversing the order of the rows when converting multiple header rows back to body rows. #TINY-6666\n- The table dialog did not always respect the `table_style_with_css` option. #TINY-4926\n- Pasting into a table with multiple cells selected could cause the content to be pasted in the wrong location. #TINY-7485\n- The `TableModified` event was not fired when pasting cells into a table. #TINY-6939\n- The table paste column before and after icons were not flipped in RTL mode. #TINY-7851\n- Fixed table corruption when deleting a `contenteditable=\"false\"` cell. #TINY-7891\n- The `dir` attribute was being incorrectly applied to list items. #TINY-4589\n- Applying selector formats would sometimes not apply the format correctly to elements in a list. #TINY-7393\n- For formats that specify an attribute or style that should be removed, the formatter `match` API incorrectly returned `false`. #TINY-6149\n- The type signature on the `formatter.matchNode` API had the wrong return type (was `boolean` but should have been `Formatter | undefined`). #TINY-6149\n- The `formatter.formatChanged` API would ignore the `similar` parameter if another callback had already been registered for the same format. #TINY-7713\n- The `formatter.formatChanged` API would sometimes not run the callback the first time the format was removed. #TINY-7713\n- Base64 encoded images with spaces or line breaks in the data URI were not displayed correctly. Patch contributed by RoboBurned.\n\n### Deprecated\n- The `bbcode`, `fullpage`, `legacyoutput`, and `spellchecker` plugins have been deprecated and marked for removal in the next major release. #TINY-7260\n\n## 5.8.2 - 2021-06-23\n\n### Fixed\n- Fixed an issue when pasting cells from tables containing `colgroup`s into tables without `colgroup`s. #TINY-6675\n- Fixed an issue that could cause an invalid toolbar button state when multiple inline editors were on a single page. #TINY-6297\n\n## 5.8.1 - 2021-05-20\n\n### Fixed\n- An unexpected exception was thrown when switching to readonly mode and adjusting the editor width. #TINY-6383\n- Content could be lost when the `pagebreak_split_block` setting was enabled. #TINY-3388\n- The `list-style-type: none;` style on nested list items was incorrectly removed when clearing formatting. #TINY-6264\n- URLs were not always detected when pasting over a selection. Patch contributed by jwcooper. #TINY-6997\n- Properties on the `OpenNotification` event were incorrectly namespaced. #TINY-7486\n\n## 5.8.0 - 2021-05-06\n\n### Added\n- Added the `PAGE_UP` and `PAGE_DOWN` key code constants to the `VK` API. #TINY-4612\n- The editor resize handle can now be controlled using the keyboard. #TINY-4823\n- Added a new `fixed_toolbar_container_target` setting which renders the toolbar in the specified `HTMLElement`. Patch contributed by pvrobays.\n\n### Improved\n- The `inline_boundaries` feature now supports the `home`, `end`, `pageup`, and `pagedown` keys. #TINY-4612\n- Updated the `formatter.matchFormat` API to support matching formats with variables in the `classes` property. #TINY-7227\n- Added HTML5 `audio` and `video` elements to the default alignment formats. #TINY-6633\n- Added support for alpha list numbering to the list properties dialog. #TINY-6891\n\n### Changed\n- Updated the `image` dialog to display the class list dropdown as full-width if the caption checkbox is not present. #TINY-6400\n- Renamed the \"H Align\" and \"V Align\" input labels in the Table Cell Properties dialog to \"Horizontal align\" and \"Vertical align\" respectively. #TINY-7285\n\n### Deprecated\n- The undocumented `setIconStroke` Split Toolbar Button API has been deprecated and will be removed in a future release. #TINY-3551\n\n### Fixed\n- Fixed a bug where it wasn't possible to align nested list items. #TINY-6567\n- The RGB fields in the color picker dialog were not staying in sync with the color palette and hue slider. #TINY-6952\n- The color preview box in the color picker dialog was not correctly displaying the saturation and value of the chosen color. #TINY-6952\n- The color picker dialog will now show an alert if it is submitted with an invalid hex color code. #TINY-2814\n- Fixed a bug where the `TableModified` event was not fired when adding a table row with the Tab key. #TINY-7006\n- Added missing `images_file_types` setting to the exported TypeScript types. #GH-6607\n- Fixed a bug where lists pasted from Word with Roman numeral markers were not displayed correctly. Patch contributed by aautio. #GH-6620\n- The `editor.insertContent` API was incorrectly handling nested `span` elements with matching styles. #TINY-6263\n- The HTML5 `small` element could not be removed when clearing text formatting. #TINY-6633\n- The Oxide button text transform variable was incorrectly using `capitalize` instead of `none`. Patch contributed by dakur. #GH-6341\n- Fix dialog button text that was using title-style capitalization. #TINY-6816\n- Table plugin could perform operations on tables containing the inline editor. #TINY-6625\n- Fixed Tab key navigation inside table cells with a ranged selection. #TINY-6638\n- The foreground and background toolbar button color indicator is no longer blurry. #TINY-3551\n- Fixed a regression in the `tinymce.create()` API that caused issues when multiple objects were created. #TINY-7358\n- Fixed the `LineHeight` command causing the `change` event to be fired inconsistently. #TINY-7048\n\n## 5.7.1 - 2021-03-17\n\n### Fixed\n- Fixed the `help` dialog incorrectly linking to the changelog of TinyMCE 4 instead of TinyMCE 5. #TINY-7031\n- Fixed a bug where error messages were displayed incorrectly in the image dialog. #TINY-7099\n- Fixed an issue where URLs were not correctly filtered in some cases. #TINY-7025\n- Fixed a bug where context menu items with names that contained uppercase characters were not displayed. #TINY-7072\n- Fixed context menu items lacking support for the `disabled` and `shortcut` properties. #TINY-7073\n- Fixed a regression where the width and height were incorrectly set when embedding content using the `media` dialog. #TINY-7074\n\n## 5.7.0 - 2021-02-10\n\n### Added\n- Added IPv6 address support to the URI API. Patch contributed by dev7355608. #GH-4409\n- Added new `structure` and `style` properties to the `TableModified` event to indicate what kinds of modifications were made. #TINY-6643\n- Added `video` and `audio` live embed support for the `media` plugin. #TINY-6229\n- Added the ability to resize `video` and `iframe` media elements. #TINY-6229\n- Added a new `font_css` setting for adding fonts to both the editor and the parent document. #TINY-6199\n- Added a new `ImageUploader` API to simplify uploading image data to the configured `images_upload_url` or `images_upload_handler`. #TINY-4601\n- Added an Oxide variable to define the container background color in fullscreen mode. #TINY-6903\n- Added Oxide variables for setting the toolbar background colors for inline and sticky toolbars. #TINY-6009\n- Added a new `AfterProgressState` event that is fired after `editor.setProgressState` calls complete. #TINY-6686\n- Added support for `table_column_resizing` when inserting or deleting columns. #TINY-6711\n\n### Changed\n- Changed table and table column copy behavior to retain an appropriate width when pasted. #TINY-6664\n- Changed the `lists` plugin to apply list styles to all text blocks within a selection. #TINY-3755\n- Changed the `advlist` plugin to log a console error message when the `list` plugin isn't enabled. #TINY-6585\n- Changed the z-index of the `setProgressState(true)` throbber so it does not hide notifications. #TINY-6686\n- Changed the type signature for `editor.selection.getRng()` incorrectly returning `null`. #TINY-6843\n- Changed some `SaxParser` regular expressions to improve performance. #TINY-6823\n- Changed `editor.setProgressState(true)` to close any open popups. #TINY-6686\n\n### Fixed\n- Fixed `codesample` highlighting performance issues for some languages. #TINY-6996\n- Fixed an issue where cell widths were lost when merging table cells. #TINY-6901\n- Fixed `col` elements incorrectly transformed to `th` elements when converting columns to header columns. #TINY-6715\n- Fixed a number of table operations not working when selecting 2 table cells on Mozilla Firefox. #TINY-3897\n- Fixed a memory leak by backporting an upstream Sizzle fix. #TINY-6859\n- Fixed table `width` style was removed when copying. #TINY-6664\n- Fixed focus lost while typing in the `charmap` or `emoticons` dialogs when the editor is rendered in a shadow root. #TINY-6904\n- Fixed corruption of base64 URLs used in style attributes when parsing HTML. #TINY-6828\n- Fixed the order of CSS precedence of `content_style` and `content_css` in the `preview` and `template` plugins. `content_style` now has precedence. #TINY-6529\n- Fixed an issue where the image dialog tried to calculate image dimensions for an empty image URL. #TINY-6611\n- Fixed an issue where `scope` attributes on table cells would not change as expected when merging or unmerging cells. #TINY-6486\n- Fixed the plugin documentation links in the `help` plugin. #DOC-703\n- Fixed events bound using `DOMUtils` not returning the correct result for `isDefaultPrevented` in some cases. #TINY-6834\n- Fixed the \"Dropped file type is not supported\" notification incorrectly showing when using an inline editor. #TINY-6834\n- Fixed an issue with external styles bleeding into TinyMCE. #TINY-6735\n- Fixed an issue where parsing malformed comments could cause an infinite loop. #TINY-6864\n- Fixed incorrect return types on `editor.selection.moveToBookmark`. #TINY-6504\n- Fixed the type signature for `editor.selection.setCursorLocation()` incorrectly allowing a node with no `offset`. #TINY-6843\n- Fixed incorrect behavior when editor is destroyed while loading stylesheets. #INT-2282\n- Fixed figure elements incorrectly splitting from a valid parent element when editing the image within. #TINY-6592\n- Fixed inserting multiple rows or columns in a table cloning from the incorrect source row or column. #TINY-6906\n- Fixed an issue where new lines were not scrolled into view when pressing Shift+Enter or Shift+Return. #TINY-6964\n- Fixed an issue where list elements would not be removed when outdenting using the Enter or Return key. #TINY-5974\n- Fixed an issue where file extensions with uppercase characters were treated as invalid. #TINY-6940\n- Fixed dialog block messages were not passed through TinyMCE's translation system. #TINY-6971\n\n## 5.6.2 - 2020-12-08\n\n### Fixed\n- Fixed a UI rendering regression when the document body is using `display: flex`. #TINY-6783\n\n## 5.6.1 - 2020-11-25\n\n### Fixed\n- Fixed the `mceTableRowType` and `mceTableCellType` commands were not firing the `newCell` event. #TINY-6692\n- Fixed the HTML5 `s` element was not recognized when editing or clearing text formatting. #TINY-6681\n- Fixed an issue where copying and pasting table columns resulted in invalid HTML when using colgroups. #TINY-6684\n- Fixed an issue where the toolbar would render with the wrong width for inline editors in some situations. #TINY-6683\n\n## 5.6.0 - 2020-11-18\n\n### Added\n- Added new `BeforeOpenNotification` and `OpenNotification` events which allow internal notifications to be captured and modified before display. #TINY-6528\n- Added support for `block` and `unblock` methods on inline dialogs. #TINY-6487\n- Added new `TableModified` event which is fired whenever changes are made to a table. #TINY-6629\n- Added new `images_file_types` setting to determine which image file formats will be automatically processed into `img` tags on paste when using the `paste` plugin. #TINY-6306\n- Added support for `images_file_types` setting in the image file uploader to determine which image file extensions are valid for upload. #TINY-6224\n- Added new `format_empty_lines` setting to control if empty lines are formatted in a ranged selection. #TINY-6483\n- Added template support to the `autocompleter` for customizing the autocompleter items. #TINY-6505\n- Added new user interface `enable`, `disable`, and `isDisabled` methods. #TINY-6397\n- Added new `closest` formatter API to get the closest matching selection format from a set of formats. #TINY-6479\n- Added new `emojiimages` emoticons database that uses the twemoji CDN by default. #TINY-6021\n- Added new `emoticons_database` setting to configure which emoji database to use. #TINY-6021\n- Added new `name` field to the `style_formats` setting object to enable specifying a name for the format. #TINY-4239\n\n### Changed\n- Changed `readonly` mode to allow hyperlinks to be clickable. #TINY-6248\n\n### Fixed\n- Fixed the `change` event not firing after a successful image upload. #TINY-6586\n- Fixed the type signature for the `entity_encoding` setting not accepting delimited lists. #TINY-6648\n- Fixed layout issues when empty `tr` elements were incorrectly removed from tables. #TINY-4679\n- Fixed image file extensions lost when uploading an image with an alternative extension, such as `.jfif`. #TINY-6622\n- Fixed a security issue where URLs in attributes weren't correctly sanitized. #TINY-6518\n- Fixed `DOMUtils.getParents` incorrectly including the shadow root in the array of elements returned. #TINY-6540\n- Fixed an issue where the root document could be scrolled while an editor dialog was open inside a shadow root. #TINY-6363\n- Fixed `getContent` with text format returning a new line when the editor is empty. #TINY-6281\n- Fixed table column and row resizers not respecting the `data-mce-resize` attribute. #TINY-6600\n- Fixed inserting a table via the `mceInsertTable` command incorrectly creating 2 undo levels. #TINY-6656\n- Fixed nested tables with `colgroup` elements incorrectly always resizing the inner table. #TINY-6623\n- Fixed the `visualchars` plugin causing the editor to steal focus when initialized. #TINY-6282\n- Fixed `fullpage` plugin altering text content in `editor.getContent()`. #TINY-6541\n- Fixed `fullscreen` plugin not working correctly with multiple editors and shadow DOM. #TINY-6280\n- Fixed font size keywords such as `medium` not displaying correctly in font size menus. #TINY-6291\n- Fixed an issue where some attributes in table cells were not copied over to new rows or columns. #TINY-6485\n- Fixed incorrectly removing formatting on adjacent spaces when removing formatting on a ranged selection. #TINY-6268\n- Fixed the `Cut` menu item not working in the latest version of Mozilla Firefox. #TINY-6615\n- Fixed some incorrect types in the new TypeScript declaration file. #TINY-6413\n- Fixed a regression where a fake offscreen selection element was incorrectly created for the editor root node. #TINY-6555\n- Fixed an issue where menus would incorrectly collapse in small containers. #TINY-3321\n- Fixed an issue where only one table column at a time could be converted to a header. #TINY-6326\n- Fixed some minor memory leaks that prevented garbage collection for editor instances. #TINY-6570\n- Fixed resizing a `responsive` table not working when using the column resize handles. #TINY-6601\n- Fixed incorrectly calculating table `col` widths when resizing responsive tables. #TINY-6646\n- Fixed an issue where spaces were not preserved in pre-blocks when getting text content. #TINY-6448\n- Fixed a regression that caused the selection to be difficult to see in tables with backgrounds. #TINY-6495\n- Fixed content pasted multiple times in the editor when using Microsoft Internet Explorer 11. Patch contributed by mattford. #GH-4905\n\n## 5.5.1 - 2020-10-01\n\n### Fixed\n- Fixed pressing the down key near the end of a document incorrectly raising an exception. #TINY-6471\n- Fixed incorrect Typescript types for the `Tools` API. #TINY-6475\n\n## 5.5.0 - 2020-09-29\n\n### Added\n- Added a TypeScript declaration file to the bundle output for TinyMCE core. #TINY-3785\n- Added new `table_column_resizing` setting to control how table columns are resized when using the resize bars. #TINY-6001\n- Added the ability to remove images on a failed upload using the `images_upload_handler` failure callback. #TINY-6011\n- Added `hasPlugin` function to the editor API to determine if a plugin exists or not. #TINY-766\n- Added new `ToggleToolbarDrawer` command and query state handler to allow the toolbar drawer to be programmatically toggled and the toggle state to be checked. #TINY-6032\n- Added the ability to use `colgroup` elements in tables. #TINY-6050\n- Added a new setting `table_use_colgroups` for toggling whether colgroups are used in new tables. #TINY-6050\n- Added the ability to delete and navigate HTML media elements without the `media` plugin. #TINY-4211\n- Added `fullscreen_native` setting to the `fullscreen` plugin to enable use of the entire monitor. #TINY-6284\n- Added table related oxide variables to the Style API for more granular control over table cell selection appearance. #TINY-6311\n- Added new `toolbar_persist` setting to control the visibility of the inline toolbar. #TINY-4847\n- Added new APIs to allow for programmatic control of the inline toolbar visibility. #TINY-4847\n- Added the `origin` property to the `ObjectResized` and `ObjectResizeStart` events, to specify which handle the resize was performed on. #TINY-6242\n- Added new StyleSheetLoader `unload` and `unloadAll` APIs to allow loaded stylesheets to be removed. #TINY-3926\n- Added the `LineHeight` query command and action to the editor. #TINY-4843\n- Added the `lineheight` toolbar and menu items, and added `lineheight` to the default format menu. #TINY-4843\n- Added a new `contextmenu_avoid_overlap` setting to allow context menus to avoid overlapping matched nodes. #TINY-6036\n- Added new listbox dialog UI component for rendering a dropdown that allows nested options. #TINY-2236\n- Added back the ability to use nested items in the `image_class_list`, `link_class_list`, `link_list`, `table_class_list`, `table_cell_class_list`, and `table_row_class_list` settings. #TINY-2236\n\n### Changed\n- Changed how CSS manipulates table cells when selecting multiple cells to achieve a semi-transparent selection. #TINY-6311\n- Changed the `target` property on fired events to use the native event target. The original target for an open shadow root can be obtained using `event.getComposedPath()`. #TINY-6128\n- Changed the editor to clean-up loaded CSS stylesheets when all editors using the stylesheet have been removed. #TINY-3926\n- Changed `imagetools` context menu icon for accessing the `image` dialog to use the `image` icon. #TINY-4141\n- Changed the `editor.insertContent()` and `editor.selection.setContent()` APIs to retain leading and trailing whitespace. #TINY-5966\n- Changed the `table` plugin `Column` menu to include the cut, copy and paste column menu items. #TINY-6374\n- Changed the default table styles in the content CSS files to better support the styling options available in the `table` dialog. #TINY-6179\n\n### Deprecated\n- Deprecated the `Env.experimentalShadowDom` flag. #TINY-6128\n\n### Fixed\n- Fixed tables with no borders displaying with the default border styles in the `preview` dialog. #TINY-6179\n- Fixed loss of whitespace when inserting content after a non-breaking space. #TINY-5966\n- Fixed the `event.getComposedPath()` function throwing an exception for events fired from the editor. #TINY-6128\n- Fixed notifications not appearing when the editor is within a ShadowRoot. #TINY-6354\n- Fixed focus issues with inline dialogs when the editor is within a ShadowRoot. #TINY-6360\n- Fixed the `template` plugin previews missing some content styles. #TINY-6115\n- Fixed the `media` plugin not saving the alternative source url in some situations. #TINY-4113\n- Fixed an issue where column resizing using the resize bars was inconsistent between fixed and relative table widths. #TINY-6001\n- Fixed an issue where dragging and dropping within a table would select table cells. #TINY-5950\n- Fixed up and down keyboard navigation not working for inline `contenteditable=\"false\"` elements. #TINY-6226\n- Fixed dialog not retrieving `close` icon from icon pack. #TINY-6445\n- Fixed the `unlink` toolbar button not working when selecting multiple links. #TINY-4867\n- Fixed the `link` dialog not showing the \"Text to display\" field in some valid cases. #TINY-5205\n- Fixed the `DOMUtils.split()` API incorrectly removing some content. #TINY-6294\n- Fixed pressing the escape key not focusing the editor when using multiple toolbars. #TINY-6230\n- Fixed the `dirty` flag not being correctly set during an `AddUndo` event. #TINY-4707\n- Fixed `editor.selection.setCursorLocation` incorrectly placing the cursor outside `pre` elements in some circumstances. #TINY-4058\n- Fixed an exception being thrown when pressing the enter key inside pre elements while `br_in_pre` setting is false. #TINY-4058\n\n## 5.4.2 - 2020-08-17\n\n### Fixed\n- Fixed the editor not resizing when resizing the browser window in fullscreen mode. #TINY-3511\n- Fixed clicking on notifications causing inline editors to hide. #TINY-6058\n- Fixed an issue where link URLs could not be deleted or edited in the link dialog in some cases. #TINY-4706\n- Fixed a regression where setting the `anchor_top` or `anchor_bottom` options to `false` was not working. #TINY-6256\n- Fixed the `anchor` plugin not supporting the `allow_html_in_named_anchor` option. #TINY-6236\n- Fixed an exception thrown when removing inline formats that contained additional styles or classes. #TINY-6288\n- Fixed an exception thrown when positioning the context toolbar on Internet Explorer 11 in some edge cases. #TINY-6271\n- Fixed inline formats not removed when more than one `removeformat` format rule existed. #TINY-6216\n- Fixed an issue where spaces were sometimes removed when removing formating on nearby text. #TINY-6251\n- Fixed the list toolbar buttons not showing as active when a list is selected. #TINY-6286\n- Fixed an issue where the UI would sometimes not be shown or hidden when calling the show or hide API methods on the editor. #TINY-6048\n- Fixed the list type style not retained when copying list items. #TINY-6289\n- Fixed the Paste plugin converting tabs in plain text to a single space character. A `paste_tab_spaces` option has been included for setting the number of spaces used to replace a tab character. #TINY-6237\n\n## 5.4.1 - 2020-07-08\n\n### Fixed\n- Fixed the Search and Replace plugin incorrectly including zero-width caret characters in search results. #TINY-4599\n- Fixed dragging and dropping unsupported files navigating the browser away from the editor. #TINY-6027\n- Fixed undo levels not created on browser handled drop or paste events. #TINY-6027\n- Fixed content in an iframe element parsing as DOM elements instead of text content. #TINY-5943\n- Fixed Oxide checklist styles not showing when printing. #TINY-5139\n- Fixed bug with `scope` attribute not being added to the cells of header rows. #TINY-6206\n\n## 5.4.0 - 2020-06-30\n\n### Added\n- Added keyboard navigation support to menus and toolbars when the editor is in a ShadowRoot. #TINY-6152\n- Added the ability for menus to be clicked when the editor is in an open shadow root. #TINY-6091\n- Added the `Editor.ui.styleSheetLoader` API for loading stylesheets within the Document or ShadowRoot containing the editor UI. #TINY-6089\n- Added the `StyleSheetLoader` module to the public API. #TINY-6100\n- Added Oxide variables for styling the `select` element and headings in dialog content. #TINY-6070\n- Added icons for `table` column and row cut, copy, and paste toolbar buttons. #TINY-6062\n- Added all `table` menu items to the UI registry, so they can be used by name in other menus. #TINY-4866\n- Added new `mceTableApplyCellStyle` command to the `table` plugin. #TINY-6004\n- Added new `table` cut, copy, and paste column editor commands and menu items. #TINY-6006\n- Added font related Oxide variables for secondary buttons, allowing for custom styling. #TINY-6061\n- Added new `table_header_type` setting to control how table header rows are structured. #TINY-6007\n- Added new `table_sizing_mode` setting to replace the `table_responsive_width` setting, which has now been deprecated. #TINY-6051\n- Added new `mceTableSizingMode` command for changing the sizing mode of a table. #TINY-6000\n- Added new `mceTableRowType`, `mceTableColType`, and `mceTableCellType` commands and value queries. #TINY-6150\n\n### Changed\n- Changed `advlist` toolbar buttons to only show a dropdown list if there is more than one option. #TINY-3194\n- Changed `mceInsertTable` command and `insertTable` API method to take optional header rows and columns arguments. #TINY-6012\n- Changed stylesheet loading, so that UI skin stylesheets can load in a ShadowRoot if required. #TINY-6089\n- Changed the DOM location of menus so that they display correctly when the editor is in a ShadowRoot. #TINY-6093\n- Changed the table plugin to correctly detect all valid header row structures. #TINY-6007\n\n### Fixed\n- Fixed tables with no defined width being converted to a `fixed` width table when modifying the table. #TINY-6051\n- Fixed the `autosave` `isEmpty` API incorrectly detecting non-empty content as empty. #TINY-5953\n- Fixed table `Paste row after` and `Paste row before` menu items not disabled when nothing was available to paste. #TINY-6006\n- Fixed a selection performance issue with large tables on Microsoft Internet Explorer and Edge. #TINY-6057\n- Fixed filters for screening commands from the undo stack to be case-insensitive. #TINY-5946\n- Fixed `fullscreen` plugin now removes all classes when the editor is closed. #TINY-4048\n- Fixed handling of mixed-case icon identifiers (names) for UI elements. #TINY-3854\n- Fixed leading and trailing spaces lost when using `editor.selection.getContent({ format: 'text' })`. #TINY-5986\n- Fixed an issue where changing the URL with the quicklink toolbar caused unexpected undo behavior. #TINY-5952\n- Fixed an issue where removing formatting within a table cell would cause Internet Explorer 11 to scroll to the end of the table. #TINY-6049\n- Fixed an issue where the `allow_html_data_urls` setting was not correctly applied. #TINY-5951\n- Fixed the `autolink` feature so that it no longer treats a string with multiple \"@\" characters as an email address. #TINY-4773\n- Fixed an issue where removing the editor would leave unexpected attributes on the target element. #TINY-4001\n- Fixed the `link` plugin now suggest `mailto:` when the text contains an '@' and no slashes (`/`). #TINY-5941\n- Fixed the `valid_children` check of custom elements now allows a wider range of characters in names. #TINY-5971\n\n## 5.3.2 - 2020-06-10\n\n### Fixed\n- Fixed a regression introduced in 5.3.0, where `images_dataimg_filter` was no-longer called. #TINY-6086\n\n## 5.3.1 - 2020-05-27\n\n### Fixed\n- Fixed the image upload error alert also incorrectly closing the image dialog. #TINY-6020\n- Fixed editor content scrolling incorrectly on focus in Firefox by reverting default content CSS html and body heights added in 5.3.0. #TINY-6019\n\n## 5.3.0 - 2020-05-21\n\n### Added\n- Added html and body height styles to the default oxide content CSS. #TINY-5978\n- Added `uploadUri` and `blobInfo` to the data returned by `editor.uploadImages()`. #TINY-4579\n- Added a new function to the `BlobCache` API to lookup a blob based on the base64 data and mime type. #TINY-5988\n- Added the ability to search and replace within a selection. #TINY-4549\n- Added the ability to set the list start position for ordered lists and added new `lists` context menu item. #TINY-3915\n- Added `icon` as an optional config option to the toggle menu item API. #TINY-3345\n- Added `auto` mode for `toolbar_location` which positions the toolbar and menu bar at the bottom if there is no space at the top. #TINY-3161\n\n### Changed\n- Changed the default `toolbar_location` to `auto`. #TINY-3161\n- Changed toggle menu items and choice menu items to have a dedicated icon with the checkmark displayed on the far right side of the menu item. #TINY-3345\n- Changed the `link`, `image`, and `paste` plugins to use Promises to reduce the bundle size. #TINY-4710\n- Changed the default icons to be lazy loaded during initialization. #TINY-4729\n- Changed the parsing of content so base64 encoded urls are converted to blob urls. #TINY-4727\n- Changed context toolbars so they concatenate when more than one is suitable for the current selection. #TINY-4495\n- Changed inline style element formats (strong, b, em, i, u, strike) to convert to a span on format removal if a `style` or `class` attribute is present. #TINY-4741\n\n### Fixed\n- Fixed the `selection.setContent()` API not running parser filters. #TINY-4002\n- Fixed formats incorrectly applied or removed when table cells were selected. #TINY-4709\n- Fixed the `quickimage` button not restricting the file types to images. #TINY-4715\n- Fixed search and replace ignoring text in nested contenteditable elements. #TINY-5967\n- Fixed resize handlers displaying in the wrong location sometimes for remote images. #TINY-4732\n- Fixed table picker breaking in Firefox on low zoom levels. #TINY-4728\n- Fixed issue with loading or pasting contents with large base64 encoded images on Safari. #TINY-4715\n- Fixed supplementary special characters being truncated when inserted into the editor. Patch contributed by mlitwin. #TINY-4791\n- Fixed toolbar buttons not set to disabled when the editor is in readonly mode. #TINY-4592\n- Fixed the editor selection incorrectly changing when removing caret format containers. #TINY-3438\n- Fixed bug where title, width, and height would be set to empty string values when updating an image and removing those attributes using the image dialog. #TINY-4786\n- Fixed `ObjectResized` event firing when an object wasn't resized. #TINY-4161\n- Fixed `ObjectResized` and `ObjectResizeStart` events incorrectly fired when adding or removing table rows and columns. #TINY-4829\n- Fixed the placeholder not hiding when pasting content into the editor. #TINY-4828\n- Fixed an issue where the editor would fail to load if local storage was disabled. #TINY-5935\n- Fixed an issue where an uploaded image would reuse a cached image with a different mime type. #TINY-5988\n- Fixed bug where toolbars and dialogs would not show if the body element was replaced (e.g. with Turbolinks). Patch contributed by spohlenz. #GH-5653\n- Fixed an issue where multiple formats would be removed when removing a single format at the end of lines or on empty lines. #TINY-1170\n- Fixed zero-width spaces incorrectly included in the `wordcount` plugin character count. #TINY-5991\n- Fixed a regression introduced in 5.2.0 whereby the desktop `toolbar_mode` setting would incorrectly override the mobile default setting. #TINY-5998\n- Fixed an issue where deleting all content in a single cell table would delete the entire table. #TINY-1044\n\n## 5.2.2 - 2020-04-23\n\n### Fixed\n- Fixed an issue where anchors could not be inserted on empty lines. #TINY-2788\n- Fixed text decorations (underline, strikethrough) not consistently inheriting the text color. #TINY-4757\n- Fixed `format` menu alignment buttons inconsistently applying to images. #TINY-4057\n- Fixed the floating toolbar drawer height collapsing when the editor is rendered in modal dialogs or floating containers. #TINY-4837\n- Fixed `media` embed content not processing safely in some cases. #TINY-4857\n\n## 5.2.1 - 2020-03-25\n\n### Fixed\n- Fixed the \"is decorative\" checkbox in the image dialog clearing after certain dialog events. #FOAM-11\n- Fixed possible uncaught exception when a `style` attribute is removed using a content filter on `setContent`. #TINY-4742\n- Fixed the table selection not functioning correctly in Microsoft Edge 44 or higher. #TINY-3862\n- Fixed the table resize handles not functioning correctly in Microsoft Edge 44 or higher. #TINY-4160\n- Fixed the floating toolbar drawer disconnecting from the toolbar when adding content in inline mode. #TINY-4725 #TINY-4765\n- Fixed `readonly` mode not returning the appropriate boolean value. #TINY-3948\n- Fixed the `forced_root_block_attrs` setting not applying attributes to new blocks consistently. #TINY-4564\n- Fixed the editor incorrectly stealing focus during initialization in Microsoft Internet Explorer. #TINY-4697\n- Fixed dialogs stealing focus when opening an alert or confirm dialog using an `onAction` callback. #TINY-4014\n- Fixed inline dialogs incorrectly closing when clicking on an opened alert or confirm dialog. #TINY-4012\n- Fixed the context toolbar overlapping the menu bar and toolbar. #TINY-4586\n- Fixed notification and inline dialog positioning issues when using `toolbar_location: 'bottom'`. #TINY-4586\n- Fixed the `colorinput` popup appearing offscreen on mobile devices. #TINY-4711\n- Fixed special characters not being found when searching by \"whole words only\". #TINY-4522\n- Fixed an issue where dragging images could cause them to be duplicated. #TINY-4195\n- Fixed context toolbars activating without the editor having focus. #TINY-4754\n- Fixed an issue where removing the background color of text did not always work. #TINY-4770\n- Fixed an issue where new rows and columns in a table did not retain the style of the previous row or column. #TINY-4788\n\n## 5.2.0 - 2020-02-13\n\n### Added\n- Added the ability to apply formats to spaces. #TINY-4200\n- Added new `toolbar_location` setting to allow for positioning the menu and toolbar at the bottom of the editor. #TINY-4210\n- Added new `toolbar_groups` setting to allow a custom floating toolbar group to be added to the toolbar when using `floating` toolbar mode. #TINY-4229\n- Added new `link_default_protocol` setting to `link` and `autolink` plugin to allow a protocol to be used by default. #TINY-3328\n- Added new `placeholder` setting to allow a placeholder to be shown when the editor is empty. #TINY-3917\n- Added new `tinymce.dom.TextSeeker` API to allow searching text across different DOM nodes. #TINY-4200\n- Added a drop shadow below the toolbar while in sticky mode and introduced Oxide variables to customize it when creating a custom skin. #TINY-4343\n- Added `quickbars_image_toolbar` setting to allow for the image quickbar to be turned off. #TINY-4398\n- Added iframe and img `loading` attribute to the default schema. Patch contributed by ataylor32. #GH-5112\n- Added new `getNodeFilters`/`getAttributeFilters` functions to the `editor.serializer` instance. #TINY-4344\n- Added new `a11y_advanced_options` setting to allow additional accessibility options to be added. #FOAM-11\n- Added new accessibility options and behaviours to the image dialog using `a11y_advanced_options`. #FOAM-11\n- Added the ability to use the window `PrismJS` instance for the `codesample` plugin instead of the bundled version to allow for styling custom languages. #TINY-4504\n- Added error message events that fire when a resource loading error occurs. #TINY-4509\n\n### Changed\n- Changed the default schema to disallow `onchange` for select elements. #TINY-4614\n- Changed default `toolbar_mode` value from false to `wrap`. The value false has been deprecated. #TINY-4617\n- Changed `toolbar_drawer` setting to `toolbar_mode`. `toolbar_drawer` has been deprecated. #TINY-4416\n- Changed iframe mode to set selection on content init if selection doesn't exist. #TINY-4139\n- Changed table related icons to align them with the visual style of the other icons. #TINY-4341\n- Changed and improved the visual appearance of the color input field. #TINY-2917\n- Changed fake caret container to use `forced_root_block` when possible. #TINY-4190\n- Changed the `requireLangPack` API to wait until the plugin has been loaded before loading the language pack. #TINY-3716\n- Changed the formatter so `style_formats` are registered before the initial content is loaded into the editor. #TINY-4238\n- Changed media plugin to use https protocol for media urls by default. #TINY-4577\n- Changed the parser to treat CDATA nodes as bogus HTML comments to match the HTML parsing spec. A new `preserve_cdata` setting has been added to preserve CDATA nodes if required. #TINY-4625\n\n### Fixed\n- Fixed incorrect parsing of malformed/bogus HTML comments. #TINY-4625\n- Fixed `quickbars` selection toolbar appearing on non-editable elements. #TINY-4359\n- Fixed bug with alignment toolbar buttons sometimes not changing state correctly. #TINY-4139\n- Fixed the `codesample` toolbar button not toggling when selecting code samples other than HTML. #TINY-4504\n- Fixed content incorrectly scrolling to the top or bottom when pressing enter if when the content was already in view. #TINY-4162\n- Fixed `scrollIntoView` potentially hiding elements behind the toolbar. #TINY-4162\n- Fixed editor not respecting the `resize_img_proportional` setting due to legacy code. #TINY-4236\n- Fixed flickering floating toolbar drawer in inline mode. #TINY-4210\n- Fixed an issue where the template plugin dialog would be indefinitely blocked on a failed template load. #TINY-2766\n- Fixed the `mscontrolselect` event not being unbound on IE/Edge. #TINY-4196\n- Fixed Confirm dialog footer buttons so only the \"Yes\" button is highlighted. #TINY-4310\n- Fixed `file_picker_callback` functionality for Image, Link and Media plugins. #TINY-4163\n- Fixed issue where floating toolbar drawer sometimes would break if the editor is resized while the drawer is open. #TINY-4439\n- Fixed incorrect `external_plugins` loading error message. #TINY-4503\n- Fixed resize handler was not hidden for ARIA purposes. Patch contributed by Parent5446. #GH-5195\n- Fixed an issue where content could be lost if a misspelled word was selected and spellchecking was disabled. #TINY-3899\n- Fixed validation errors in the CSS where certain properties had the wrong default value. #TINY-4491\n- Fixed an issue where forced root block attributes were not applied when removing a list. #TINY-4272\n- Fixed an issue where the element path isn't being cleared when there are no parents. #TINY-4412\n- Fixed an issue where width and height in svg icons containing `rect` elements were overridden by the CSS reset. #TINY-4408\n- Fixed an issue where uploading images with `images_reuse_filename` enabled and that included a query parameter would generate an invalid URL. #TINY-4638\n- Fixed the `closeButton` property not working when opening notifications. #TINY-4674\n- Fixed keyboard flicker when opening a context menu on mobile. #TINY-4540\n- Fixed issue where plus icon svg contained strokes. #TINY-4681\n\n## 5.1.6 - 2020-01-28\n\n### Fixed\n- Fixed `readonly` mode not blocking all clicked links. #TINY-4572\n- Fixed legacy font sizes being calculated inconsistently for the `FontSize` query command value. #TINY-4555\n- Fixed changing a tables row from `Header` to `Body` incorrectly moving the row to the bottom of the table. #TINY-4593\n- Fixed the context menu not showing in certain cases with hybrid devices. #TINY-4569\n- Fixed the context menu opening in the wrong location when the target is the editor body. #TINY-4568\n- Fixed the `image` plugin not respecting the `automatic_uploads` setting when uploading local images. #TINY-4287\n- Fixed security issue related to parsing HTML comments and CDATA. #TINY-4544\n\n## 5.1.5 - 2019-12-19\n\n### Fixed\n- Fixed the UI not working with hybrid devices that accept both touch and mouse events. #TNY-4521\n- Fixed the `charmap` dialog initially focusing the first tab of the dialog instead of the search input field. #TINY-4342\n- Fixed an exception being raised when inserting content if the caret was directly before or after a `contenteditable=\"false\"` element. #TINY-4528\n- Fixed a bug with pasting image URLs when paste as text is enabled. #TINY-4523\n\n## 5.1.4 - 2019-12-11\n\n### Fixed\n- Fixed dialog contents disappearing when clicking a checkbox for right-to-left languages. #TINY-4518\n- Fixed the `legacyoutput` plugin registering legacy formats after editor initialization, causing legacy content to be stripped on the initial load. #TINY-4447\n- Fixed search and replace not cycling through results when searching using special characters. #TINY-4506\n- Fixed the `visualchars` plugin converting HTML-like text to DOM elements in certain cases. #TINY-4507\n- Fixed an issue with the `paste` plugin not sanitizing content in some cases. #TINY-4510\n- Fixed HTML comments incorrectly being parsed in certain cases. #TINY-4511\n\n## 5.1.3 - 2019-12-04\n\n### Fixed\n- Fixed sticky toolbar not undocking when fullscreen mode is activated. #TINY-4390\n- Fixed the \"Current Window\" target not applying when updating links using the link dialog. #TINY-4063\n- Fixed disabled menu items not highlighting when focused. #TINY-4339\n- Fixed touch events passing through dialog collection items to the content underneath on Android devices. #TINY-4431\n- Fixed keyboard navigation of the Help dialog's Keyboard Navigation tab. #TINY-4391\n- Fixed search and replace dialog disappearing when finding offscreen matches on iOS devices. #TINY-4350\n- Fixed performance issues where sticky toolbar was jumping while scrolling on slower browsers. #TINY-4475\n\n## 5.1.2 - 2019-11-19\n\n### Fixed\n- Fixed desktop touch devices using `mobile` configuration overrides. #TINY-4345\n- Fixed unable to disable the new scrolling toolbar feature. #TINY-4345\n- Fixed touch events passing through any pop-up items to the content underneath on Android devices. #TINY-4367\n- Fixed the table selector handles throwing JavaScript exceptions for non-table selections. #TINY-4338\n- Fixed `cut` operations not removing selected content on Android devices when the `paste` plugin is enabled. #TINY-4362\n- Fixed inline toolbar not constrained to the window width by default. #TINY-4314\n- Fixed context toolbar split button chevrons pointing right when they should be pointing down. #TINY-4257\n- Fixed unable to access the dialog footer in tabbed dialogs on small screens. #TINY-4360\n- Fixed mobile table selectors were hard to select with touch by increasing the size. #TINY-4366\n- Fixed mobile table selectors moving when moving outside the editor. #TINY-4366\n- Fixed inline toolbars collapsing when using sliding toolbars. #TINY-4389\n- Fixed block textpatterns not treating NBSPs as spaces. #TINY-4378\n- Fixed backspace not merging blocks when the last element in the preceding block was a `contenteditable=\"false\"` element. #TINY-4235\n- Fixed toolbar buttons that only contain text labels overlapping on mobile devices. #TINY-4395\n- Fixed quickbars quickimage picker not working on mobile. #TINY-4377\n- Fixed fullscreen not resizing in an iOS WKWebView component. #TINY-4413\n\n## 5.1.1 - 2019-10-28\n\n### Fixed\n- Fixed font formats containing spaces being wrapped in `&quot;` entities instead of single quotes. #TINY-4275\n- Fixed alert and confirm dialogs losing focus when clicked. #TINY-4248\n- Fixed clicking outside a modal dialog focusing on the document body. #TINY-4249\n- Fixed the context toolbar not hiding when scrolled out of view. #TINY-4265\n\n## 5.1.0 - 2019-10-17\n\n### Added\n- Added touch selector handles for table selections on touch devices. #TINY-4097\n- Added border width field to Table Cell dialog. #TINY-4028\n- Added touch event listener to media plugin to make embeds playable. #TINY-4093\n- Added oxide styling options to notifications and tweaked the default variables. #TINY-4153\n- Added additional padding to split button chevrons on touch devices, to make them easier to interact with. #TINY-4223\n- Added new platform detection functions to `Env` and deprecated older detection properties. #TINY-4184\n- Added `inputMode` config field to specify inputmode attribute of `input` dialog components. #TINY-4062\n- Added new `inputMode` property to relevant plugins/dialogs. #TINY-4102\n- Added new `toolbar_sticky` setting to allow the iframe menubar/toolbar to stick to the top of the window when scrolling. #TINY-3982\n\n### Changed\n- Changed default setting for `toolbar_drawer` to `floating`. #TINY-3634\n- Changed mobile phones to use the `silver` theme by default. #TINY-3634\n- Changed some editor settings to default to `false` on touch devices:\n  - `menubar`(phones only). #TINY-4077\n  - `table_grid`. #TINY-4075\n  - `resize`. #TINY-4157\n  - `object_resizing`. #TINY-4157\n- Changed toolbars and context toolbars to sidescroll on mobile. #TINY-3894 #TINY-4107\n- Changed context menus to render as horizontal menus on touch devices. #TINY-4107\n- Changed the editor to use the `VisualViewport` API of the browser where possible. #TINY-4078\n- Changed visualblocks toolbar button icon and renamed `paragraph` icon to `visualchars`. #TINY-4074\n- Changed Oxide default for `@toolbar-button-chevron-color` to follow toolbar button icon color. #TINY-4153\n- Changed the `urlinput` dialog component to use the `url` type attribute. #TINY-4102\n\n### Fixed\n- Fixed Safari desktop visual viewport fires resize on fullscreen breaking the restore function. #TINY-3976\n- Fixed scroll issues on mobile devices. #TINY-3976\n- Fixed context toolbar unable to refresh position on iOS12. #TINY-4107\n- Fixed ctrl+left click not opening links on readonly mode and the preview dialog. #TINY-4138\n- Fixed Slider UI component not firing `onChange` event on touch devices. #TINY-4092\n- Fixed notifications overlapping instead of stacking. #TINY-3478\n- Fixed inline dialogs positioning incorrectly when the page is scrolled. #TINY-4018\n- Fixed inline dialogs and menus not repositioning when resizing. #TINY-3227\n- Fixed inline toolbar incorrectly stretching to the full width when a width value was provided. #TINY-4066\n- Fixed menu chevrons color to follow the menu text color. #TINY-4153\n- Fixed table menu selection grid from staying black when using dark skins, now follows border color. #TINY-4153\n- Fixed Oxide using the wrong text color variable for menubar button focused state. #TINY-4146\n- Fixed the autoresize plugin not keeping the selection in view when resizing. #TINY-4094\n- Fixed textpattern plugin throwing exceptions when using `forced_root_block: false`. #TINY-4172\n- Fixed missing CSS fill styles for toolbar button icon active state. #TINY-4147\n- Fixed an issue where the editor selection could end up inside a short ended element (such as `br`). #TINY-3999\n- Fixed browser selection being lost in inline mode when opening split dropdowns. #TINY-4197\n- Fixed backspace throwing an exception when using `forced_root_block: false`. #TINY-4099\n- Fixed floating toolbar drawer expanding outside the bounds of the editor. #TINY-3941\n- Fixed the autocompleter not activating immediately after a `br` or `contenteditable=false` element. #TINY-4194\n- Fixed an issue where the autocompleter would incorrectly close on IE 11 in certain edge cases. #TINY-4205\n\n## 5.0.16 - 2019-09-24\n\n### Added\n- Added new `referrer_policy` setting to add the `referrerpolicy` attribute when loading scripts or stylesheets. #TINY-3978\n- Added a slight background color to dialog tab links when focused to aid keyboard navigation. #TINY-3877\n\n### Fixed\n- Fixed media poster value not updating on change. #TINY-4013\n- Fixed openlink was not registered as a toolbar button. #TINY-4024\n- Fixed failing to initialize if a script tag was used inside a SVG. #TINY-4087\n- Fixed double top border showing on toolbar without menubar when toolbar_drawer is enabled. #TINY-4118\n- Fixed unable to drag inline dialogs to the bottom of the screen when scrolled. #TINY-4154\n- Fixed notifications appearing on top of the toolbar when scrolled in inline mode. #TINY-4159\n- Fixed notifications displaying incorrectly on IE 11. #TINY-4169\n\n## 5.0.15 - 2019-09-02\n\n### Added\n- Added a dark `content_css` skin to go with the dark UI skin. #TINY-3743\n\n### Changed\n- Changed the enabled state on toolbar buttons so they don't get the hover effect. #TINY-3974\n\n### Fixed\n- Fixed missing CSS active state on toolbar buttons. #TINY-3966\n- Fixed `onChange` callback not firing for the colorinput dialog component. #TINY-3968\n- Fixed context toolbars not showing in fullscreen mode. #TINY-4023\n\n## 5.0.14 - 2019-08-19\n\n### Added\n- Added an API to reload the autocompleter menu with additional fetch metadata #MENTIONS-17\n\n### Fixed\n- Fixed missing toolbar button border styling options. #TINY-3965\n- Fixed image upload progress notification closing before the upload is complete. #TINY-3963\n- Fixed inline dialogs not closing on escape when no dialog component is in focus. #TINY-3936\n- Fixed plugins not being filtered when defaulting to mobile on phones. #TINY-3537\n- Fixed toolbar more drawer showing the content behind it when transitioning between opened and closed states. #TINY-3878\n- Fixed focus not returning to the dialog after pressing the \"Replace all\" button in the search and replace dialog. #TINY-3961\n\n### Removed\n- Removed Oxide variable `@menubar-select-disabled-border-color` and replaced it with `@menubar-select-disabled-border`. #TINY-3965\n\n## 5.0.13 - 2019-08-06\n\n### Changed\n- Changed modal dialogs to prevent dragging by default and added new `draggable_modal` setting to restore dragging. #TINY-3873\n- Changed the nonbreaking plugin to insert nbsp characters wrapped in spans to aid in filtering. This can be disabled using the `nonbreaking_wrap` setting. #TINY-3647\n- Changed backspace behaviour in lists to outdent nested list items when the cursor is at the start of the list item. #TINY-3651\n\n### Fixed\n- Fixed sidebar growing beyond editor bounds in IE 11. #TINY-3937\n- Fixed issue with being unable to keyboard navigate disabled toolbar buttons. #TINY-3350\n- Fixed issues with backspace and delete in nested contenteditable true and false elements. #TINY-3868\n- Fixed issue with losing keyboard navigation in dialogs due to disabled buttons. #TINY-3914\n- Fixed `MouseEvent.mozPressure is deprecated` warning in Firefox. #TINY-3919\n- Fixed `default_link_target` not being respected when `target_list` is disabled. #TINY-3757\n- Fixed mobile plugin filter to only apply to the mobile theme, rather than all mobile platforms. #TINY-3405\n- Fixed focus switching to another editor during mode changes. #TINY-3852\n- Fixed an exception being thrown when clicking on an uninitialized inline editor. #TINY-3925\n- Fixed unable to keyboard navigate to dialog menu buttons. #TINY-3933\n- Fixed dialogs being able to be dragged outside the window viewport. #TINY-3787\n- Fixed inline dialogs appearing above modal dialogs. #TINY-3932\n\n## 5.0.12 - 2019-07-18\n\n### Added\n- Added ability to utilize UI dialog panels inside other panels. #TINY-3305\n- Added help dialog tab explaining keyboard navigation of the editor. #TINY-3603\n\n### Changed\n- Changed the \"Find and Replace\" design to an inline dialog. #TINY-3054\n\n### Fixed\n- Fixed issue where autolink spacebar event was not being fired on Edge. #TINY-3891\n- Fixed table selection missing the background color. #TINY-3892\n- Fixed removing shortcuts not working for function keys. #TINY-3871\n- Fixed non-descriptive UI component type names. #TINY-3349\n- Fixed UI registry components rendering as the wrong type when manually specifying a different type. #TINY-3385\n- Fixed an issue where dialog checkbox, input, selectbox, textarea and urlinput components couldn't be disabled. #TINY-3708\n- Fixed the context toolbar not using viable screen space in inline/distraction free mode. #TINY-3717\n- Fixed the context toolbar overlapping the toolbar in various conditions. #TINY-3205\n- Fixed IE11 edge case where items were being inserted into the wrong location. #TINY-3884\n\n## 5.0.11 - 2019-07-04\n\n### Fixed\n- Fixed packaging errors caused by a rollup treeshaking bug (https://github.com/rollup/rollup/issues/2970). #TINY-3866\n- Fixed the customeditor component not able to get data from the dialog api. #TINY-3866\n- Fixed collection component tooltips not being translated. #TINY-3855\n\n## 5.0.10 - 2019-07-02\n\n### Added\n- Added support for all HTML color formats in `color_map` setting. #TINY-3837\n\n### Changed\n- Changed backspace key handling to outdent content in appropriate circumstances. #TINY-3685\n- Changed default palette for forecolor and backcolor to include some lighter colors suitable for highlights. #TINY-2865\n- Changed the search and replace plugin to cycle through results. #TINY-3800\n\n### Fixed\n- Fixed inconsistent types causing some properties to be unable to be used in dialog components. #TINY-3778\n- Fixed an issue in the Oxide skin where dialog content like outlines and shadows were clipped because of overflow hidden. #TINY-3566\n- Fixed the search and replace plugin not resetting state when changing the search query. #TINY-3800\n- Fixed backspace in lists not creating an undo level. #TINY-3814\n- Fixed the editor to cancel loading in quirks mode where the UI is not supported. #TINY-3391\n- Fixed applying fonts not working when the name contained spaces and numbers. #TINY-3801\n- Fixed so that initial content is retained when initializing on list items. #TINY-3796\n- Fixed inefficient font name and font size current value lookup during rendering. #TINY-3813\n- Fixed mobile font copied into the wrong folder for the oxide-dark skin. #TINY-3816\n- Fixed an issue where resizing the width of tables would produce inaccurate results. #TINY-3827\n- Fixed a memory leak in the Silver theme. #TINY-3797\n- Fixed alert and confirm dialogs using incorrect markup causing inconsistent padding. #TINY-3835\n- Fixed an issue in the Table plugin with `table_responsive_width` not enforcing units when resizing. #TINY-3790\n- Fixed leading, trailing and sequential spaces being lost when pasting plain text. #TINY-3726\n- Fixed exception being thrown when creating relative URIs. #TINY-3851\n- Fixed focus is no longer set to the editor content during mode changes unless the editor already had focus. #TINY-3852\n\n## 5.0.9 - 2019-06-26\n\n### Fixed\n- Fixed print plugin not working in Firefox. #TINY-3834\n\n## 5.0.8 - 2019-06-18\n\n### Added\n- Added back support for multiple toolbars. #TINY-2195\n- Added support for .m4a files to the media plugin. #TINY-3750\n- Added new base_url and suffix editor init options. #TINY-3681\n\n### Fixed\n- Fixed incorrect padding for select boxes with visible values. #TINY-3780\n- Fixed selection incorrectly changing when programmatically setting selection on contenteditable false elements. #TINY-3766\n- Fixed sidebar background being transparent. #TINY-3727\n- Fixed the build to remove duplicate iife wrappers. #TINY-3689\n- Fixed bogus autocompleter span appearing in content when the autocompleter menu is shown. #TINY-3752\n- Fixed toolbar font size select not working with legacyoutput plugin. #TINY-2921\n- Fixed the legacyoutput plugin incorrectly aligning images. #TINY-3660\n- Fixed remove color not working when using the legacyoutput plugin. #TINY-3756\n- Fixed the font size menu applying incorrect sizes when using the legacyoutput plugin. #TINY-3773\n- Fixed scrollIntoView not working when the parent window was out of view. #TINY-3663\n- Fixed the print plugin printing from the wrong window in IE11. #TINY-3762\n- Fixed content CSS loaded over CORS not loading in the preview plugin with content_css_cors enabled. #TINY-3769\n- Fixed the link plugin missing the default \"None\" option for link list. #TINY-3738\n- Fixed small dot visible with menubar and toolbar disabled in inline mode. #TINY-3623\n- Fixed space key properly inserts a nbsp before/after block elements. #TINY-3745\n- Fixed native context menu not showing with images in IE11. #TINY-3392\n- Fixed inconsistent browser context menu image selection. #TINY-3789\n\n## 5.0.7 - 2019-06-05\n\n### Added\n- Added new toolbar button and menu item for inserting tables via dialog. #TINY-3636\n- Added new API for adding/removing/changing tabs in the Help dialog. #TINY-3535\n- Added highlighting of matched text in autocompleter items. #TINY-3687\n- Added the ability for autocompleters to work with matches that include spaces. #TINY-3704\n- Added new `imagetools_fetch_image` callback to allow custom implementations for cors loading of images. #TINY-3658\n- Added `'http'` and `https` options to `link_assume_external_targets` to prepend `http://` or `https://` prefixes when URL does not contain a protocol prefix. Patch contributed by francoisfreitag. #GH-4335\n\n### Changed\n- Changed annotations navigation to work the same as inline boundaries. #TINY-3396\n- Changed tabpanel API by adding a `name` field and changing relevant methods to use it. #TINY-3535\n\n### Fixed\n- Fixed text color not updating all color buttons when choosing a color. #TINY-3602\n- Fixed the autocompleter not working with fragmented text. #TINY-3459\n- Fixed the autosave plugin no longer overwrites window.onbeforeunload. #TINY-3688\n- Fixed infinite loop in the paste plugin when IE11 takes a long time to process paste events. Patch contributed by lRawd. #GH-4987\n- Fixed image handle locations when using `fixed_toolbar_container`. Patch contributed by t00. #GH-4966\n- Fixed the autoresize plugin not firing `ResizeEditor` events. #TINY-3587\n- Fixed editor in fullscreen mode not extending to the bottom of the screen. #TINY-3701\n- Fixed list removal when pressing backspace after the start of the list item. #TINY-3697\n- Fixed autocomplete not triggering from compositionend events. #TINY-3711\n- Fixed `file_picker_callback` could not set the caption field on the insert image dialog. #TINY-3172\n- Fixed the autocompleter menu showing up after a selection had been made. #TINY-3718\n- Fixed an exception being thrown when a file or number input has focus during initialization. Patch contributed by t00. #GH-2194\n\n## 5.0.6 - 2019-05-22\n\n### Added\n- Added `icons_url` editor settings to enable icon packs to be loaded from a custom url. #TINY-3585\n- Added `image_uploadtab` editor setting to control the visibility of the upload tab in the image dialog. #TINY-3606\n- Added new api endpoints to the wordcount plugin and improved character count logic. #TINY-3578\n\n### Changed\n- Changed plugin, language and icon loading errors to log in the console instead of a notification. #TINY-3585\n\n### Fixed\n- Fixed the textpattern plugin not working with fragmented text. #TINY-3089\n- Fixed various toolbar drawer accessibility issues and added an animation. #TINY-3554\n- Fixed issues with selection and ui components when toggling readonly mode. #TINY-3592\n- Fixed so readonly mode works with inline editors. #TINY-3592\n- Fixed docked inline toolbar positioning when scrolled. #TINY-3621\n- Fixed initial value not being set on bespoke select in quickbars and toolbar drawer. #TINY-3591\n- Fixed so that nbsp entities aren't trimmed in white-space: pre-line elements. #TINY-3642\n- Fixed `mceInsertLink` command inserting spaces instead of url encoded characters. #GH-4990\n- Fixed text content floating on top of dialogs in IE11. #TINY-3640\n\n## 5.0.5 - 2019-05-09\n\n### Added\n- Added menu items to match the forecolor/backcolor toolbar buttons. #TINY-2878\n- Added default directionality based on the configured language. #TINY-2621\n- Added styles, icons and tests for rtl mode. #TINY-2621\n\n### Fixed\n- Fixed autoresize not working with floating elements or when media elements finished loading. #TINY-3545\n- Fixed incorrect vertical caret positioning in IE 11. #TINY-3188\n- Fixed submenu anchoring hiding overflowed content. #TINY-3564\n\n### Removed\n- Removed unused and hidden validation icons to avoid displaying phantom tooltips. #TINY-2329\n\n## 5.0.4 - 2019-04-23\n\n### Added\n- Added back URL dialog functionality, which is now available via `editor.windowManager.openUrl()`. #TINY-3382\n- Added the missing throbber functionality when calling `editor.setProgressState(true)`. #TINY-3453\n- Added function to reset the editor content and undo/dirty state via `editor.resetContent()`. #TINY-3435\n- Added the ability to set menu buttons as active. #TINY-3274\n- Added `editor.mode` API, featuring a custom editor mode API. #TINY-3406\n- Added better styling to floating toolbar drawer. #TINY-3479\n- Added the new premium plugins to the Help dialog plugins tab. #TINY-3496\n- Added the linkchecker context menu items to the default configuration. #TINY-3543\n\n### Fixed\n- Fixed image context menu items showing on placeholder images. #TINY-3280\n- Fixed dialog labels and text color contrast within notifications/alert banners to satisfy WCAG 4.5:1 contrast ratio for accessibility. #TINY-3351\n- Fixed selectbox and colorpicker items not being translated. #TINY-3546\n- Fixed toolbar drawer sliding mode to correctly focus the editor when tabbing via keyboard navigation. #TINY-3533\n- Fixed positioning of the styleselect menu in iOS while using the mobile theme. #TINY-3505\n- Fixed the menubutton `onSetup` callback to be correctly executed when rendering the menu buttons. #TINY-3547\n- Fixed `default_link_target` setting to be correctly utilized when creating a link. #TINY-3508\n- Fixed colorpicker floating marginally outside its container. #TINY-3026\n- Fixed disabled menu items displaying as active when hovered. #TINY-3027\n\n### Removed\n- Removed redundant mobile wrapper. #TINY-3480\n\n## 5.0.3 - 2019-03-19\n\n### Changed\n- Changed empty nested-menu items within the style formats menu to be disabled or hidden if the value of `style_formats_autohide` is `true`. #TINY-3310\n- Changed the entire phrase 'Powered by Tiny' in the status bar to be a link instead of just the word 'Tiny'. #TINY-3366\n- Changed `formatselect`, `styleselect` and `align` menus to use the `mceToggleFormat` command internally. #TINY-3428\n\n### Fixed\n- Fixed toolbar keyboard navigation to work as expected when `toolbar_drawer` is configured. #TINY-3432\n- Fixed text direction buttons to display the correct pressed state in selections that have no explicit `dir` property. #TINY-3138\n- Fixed the mobile editor to clean up properly when removed. #TINY-3445\n- Fixed quickbar toolbars to add an empty box to the screen when it is set to `false`. #TINY-3439\n- Fixed an issue where pressing the **Delete/Backspace** key at the edge of tables was creating incorrect selections. #TINY-3371\n- Fixed an issue where dialog collection items (emoticon and special character dialogs) couldn't be selected with touch devices. #TINY-3444\n- Fixed a type error introduced in TinyMCE version 5.0.2 when calling `editor.getContent()` with nested bookmarks. #TINY-3400\n- Fixed an issue that prevented default icons from being overridden. #TINY-3449\n- Fixed an issue where **Home/End** keys wouldn't move the caret correctly before or after `contenteditable=false` inline elements. #TINY-2995\n- Fixed styles to be preserved in IE 11 when editing via the `fullpage` plugin. #TINY-3464\n- Fixed the `link` plugin context toolbar missing the open link button. #TINY-3461\n- Fixed inconsistent dialog component spacing. #TINY-3436\n\n## 5.0.2 - 2019-03-05\n\n### Added\n- Added presentation and document presets to `htmlpanel` dialog component. #TINY-2694\n- Added missing fixed_toolbar_container setting has been reimplemented in the Silver theme. #TINY-2712\n- Added a new toolbar setting `toolbar_drawer` that moves toolbar groups which overflow the editor width into either a `sliding` or `floating` toolbar section. #TINY-2874\n\n### Changed\n- Updated the build process to include package lock files in the dev distribution archive. #TINY-2870\n\n### Fixed\n- Fixed inline dialogs did not have aria attributes. #TINY-2694\n- Fixed default icons are now available in the UI registry, allowing use outside of toolbar buttons. #TINY-3307\n- Fixed a memory leak related to select toolbar items. #TINY-2874\n- Fixed a memory leak due to format changed listeners that were never unbound. #TINY-3191\n- Fixed an issue where content may have been lost when using permanent bookmarks. #TINY-3400\n- Fixed the quicklink toolbar button not rendering in the quickbars plugin. #TINY-3125\n- Fixed an issue where menus were generating invalid HTML in some cases. #TINY-3323\n- Fixed an issue that could cause the mobile theme to show a blank white screen when the editor was inside an `overflow:hidden` element. #TINY-3407\n- Fixed mobile theme using a transparent background and not taking up the full width on iOS. #TINY-3414\n- Fixed the template plugin dialog missing the description field. #TINY-3337\n- Fixed input dialog components using an invalid default type attribute. #TINY-3424\n- Fixed an issue where backspace/delete keys after/before pagebreak elements wouldn't move the caret. #TINY-3097\n- Fixed an issue in the table plugin where menu items and toolbar buttons weren't showing correctly based on the selection. #TINY-3423\n- Fixed inconsistent button focus styles in Firefox. #TINY-3377\n- Fixed the resize icon floating left when all status bar elements were disabled. #TINY-3340\n- Fixed the resize handle to not show in fullscreen mode. #TINY-3404\n\n## 5.0.1 - 2019-02-21\n\n### Added\n- Added H1-H6 toggle button registration to the silver theme. #TINY-3070\n- Added code sample toolbar button will now toggle on when the cursor is in a code section. #TINY-3040\n- Added new settings to the emoticons plugin to allow additional emoticons to be added. #TINY-3088\n\n### Fixed\n- Fixed an issue where adding links to images would replace the image with text. #TINY-3356\n- Fixed an issue where the inline editor could use fractional pixels for positioning. #TINY-3202\n- Fixed an issue where uploading non-image files in the Image Plugin upload tab threw an error. #TINY-3244\n- Fixed an issue in the media plugin that was causing the source url and height/width to be lost in certain circumstances. #TINY-2858\n- Fixed an issue with the Context Toolbar not being removed when clicking outside of the editor. #TINY-2804\n- Fixed an issue where clicking 'Remove link' wouldn't remove the link in certain circumstances. #TINY-3199\n- Fixed an issue where the media plugin would fail when parsing dialog data. #TINY-3218\n- Fixed an issue where retrieving the selected content as text didn't create newlines. #TINY-3197\n- Fixed incorrect keyboard shortcuts in the Help dialog for Windows. #TINY-3292\n- Fixed an issue where JSON serialization could produce invalid JSON. #TINY-3281\n- Fixed production CSS including references to source maps. #TINY-3920\n- Fixed development CSS was not included in the development zip. #TINY-3920\n- Fixed the autocompleter matches predicate not matching on the start of words by default. #TINY-3306\n- Fixed an issue where the page could be scrolled with modal dialogs open. #TINY-2252\n- Fixed an issue where autocomplete menus would show an icon margin when no items had icons. #TINY-3329\n- Fixed an issue in the quickbars plugin where images incorrectly showed the text selection toolbar. #TINY-3338\n- Fixed an issue that caused the inline editor to fail to render when the target element already had focus. #TINY-3353\n\n### Removed\n- Removed paste as text notification banner and paste_plaintext_inform setting. #POW-102\n\n## 5.0.0 - 2019-02-04\n\nFull documentation for the version 5 features and changes is available at https://www.tiny.cloud/docs/tinymce/5/release-notes/release-notes50/\n\n### Added\n- Added links and registered names with * to denote premium plugins in Plugins tab of Help dialog. #TINY-3223\n\n### Changed\n- Changed Tiny 5 mobile skin to look more uniform with desktop. #TINY-2650\n- Blacklisted table, th and td as inline editor target. #TINY-717\n\n### Fixed\n- Fixed an issue where tab panel heights weren't sizing properly on smaller screens and weren't updating on resize. #TINY-3242\n- Fixed image tools not having any padding between the label and slider. #TINY-3220\n- Fixed context toolbar toggle buttons not showing the correct state. #TINY-3022\n- Fixed missing separators in the spellchecker context menu between the suggestions and actions. #TINY-3217\n- Fixed notification icon positioning in alert banners. #TINY-2196\n- Fixed a typo in the word count plugin name. #TINY-3062\n- Fixed charmap and emoticons dialogs not having a primary button. #TINY-3233\n- Fixed an issue where resizing wouldn't work correctly depending on the box-sizing model. #TINY-3278\n\n## 5.0.0-rc-2 - 2019-01-22\n\n### Added\n- Added screen reader accessibility for sidebar and statusbar. #TINY-2699\n\n### Changed\n- Changed formatting menus so they are registered and made the align toolbar button use an icon instead of text. #TINY-2880\n- Changed checkboxes to use a boolean for its state, instead of a string. #TINY-2848\n- Updated the textpattern plugin to properly support nested patterns and to allow running a command with a value for a pattern with a start and an end. #TINY-2991\n- Updated Emoticons and Charmap dialogs to be screen reader accessible. #TINY-2693\n\n### Fixed\n- Fixed the link dialog such that it will now retain class attributes when updating links. #TINY-2825\n- Fixed \"Find and replace\" not showing in the \"Edit\" menu by default. #TINY-3061\n- Fixed dropdown buttons missing the 'type' attribute, which could cause forms to be incorrectly submitted. #TINY-2826\n- Fixed emoticon and charmap search not returning expected results in certain cases. #TINY-3084\n- Fixed blank rel_list values throwing an exception in the link plugin. #TINY-3149\n\n### Removed\n- Removed unnecessary 'flex' and unused 'colspan' properties from the new dialog APIs. #TINY-2973\n\n## 5.0.0-rc-1 - 2019-01-08\n\n### Added\n- Added editor settings functionality to specify title attributes for toolbar groups. #TINY-2690\n- Added icons instead of button text to improve Search and Replace dialog footer appearance. #TINY-2654\n- Added `tox-dialog__table` instead of `mce-table-striped` class to enhance Help dialog appearance. #TINY-2360\n- Added title attribute to iframes so, screen readers can announce iframe labels. #TINY-2692\n- Added a wordcount menu item, that defaults to appearing in the tools menu. #TINY-2877\n\n### Changed\n- Updated the font select dropdown logic to try to detect the system font stack and show \"System Font\" as the font name. #TINY-2710\n- Updated the autocompleter to only show when it has matched items. #TINY-2350\n- Updated SizeInput labels to \"Height\" and \"Width\" instead of Dimensions. #TINY-2833\n- Updated the build process to minify and generate ASCII only output for the emoticons database. #TINY-2744\n\n### Fixed\n- Fixed readonly mode not fully disabling editing content. #TINY-2287\n- Fixed accessibility issues with the font select, font size, style select and format select toolbar dropdowns. #TINY-2713\n- Fixed accessibility issues with split dropdowns. #TINY-2697\n- Fixed the legacyoutput plugin to be compatible with TinyMCE 5.0. #TINY-2301\n- Fixed icons not showing correctly in the autocompleter popup. #TINY-3029\n- Fixed an issue where preview wouldn't show anything in Edge under certain circumstances. #TINY-3035\n- Fixed the height being incorrectly calculated for the autoresize plugin. #TINY-2807\n\n## 5.0.0-beta-1 - 2018-11-30\n\n### Added\n- Added a new `addNestedMenuItem()` UI registry function and changed all nested menu items to use the new registry functions. #TINY-2230\n- Added title attribute to color swatch colors. #TINY-2669\n- Added anchorbar component to anchor inline toolbar dialogs to instead of the toolbar. #TINY-2040\n- Added support for toolbar<n> and toolbar array config options to be squashed into a single toolbar and not create multiple toolbars. #TINY-2195\n- Added error handling for when forced_root_block config option is set to true. #TINY-2261\n- Added functionality for the removed_menuitems config option. #TINY-2184\n- Added the ability to use a string to reference menu items in menu buttons and submenu items. #TINY-2253\n\n### Changed\n- Changed the name of the \"inlite\" plugin to \"quickbars\". #TINY-2831\n- Changed the background color icon to highlight background icon. #TINY-2258\n- Changed Help dialog to be accessible to screen readers. #TINY-2687\n- Changed the color swatch to save selected custom colors to local storage for use across sessions. #TINY-2722\n- Changed `WindowManager` API - methods `getParams`, `setParams` and `getWindows`, and the legacy `windows` property, have been removed. `alert` and `confirm` dialogs are no longer tracked in the window list. #TINY-2603\n\n### Fixed\n- Fixed an inline mode issue where the save plugin upon saving can cause content loss. #TINY-2659\n- Fixed an issue in IE 11 where calling selection.getContent() would return an empty string when the editor didn't have focus. #TINY-2325\n\n### Removed\n- Removed compat3x plugin. #TINY-2815\n\n## 5.0.0-preview-4 - 2018-11-12\n\n### Added\n- Added width and height placeholder text to image and media dialog dimensions input. #AP-296\n- Added the ability to keyboard navigate through menus, toolbars, sidebar and the status bar sequentially. #AP-381\n- Added translation capability back to the editor's UI. #AP-282\n- Added `label` component type for dialogs to group components under a label.\n\n### Changed\n- Changed the editor resize handle so that it should be disabled when the autoresize plugin is turned on. #AP-424\n- Changed UI text for microcopy improvements. #TINY-2281\n\n### Fixed\n- Fixed distraction free plugin. #AP-470\n- Fixed contents of the input field being selected on focus instead of just recieving an outline highlight. #AP-464\n- Fixed styling issues with dialogs and menus in IE 11. #AP-456\n- Fixed custom style format control not honoring custom formats. #AP-393\n- Fixed context menu not appearing when clicking an image with a caption. #AP-382\n- Fixed directionality of UI when using an RTL language. #AP-423\n- Fixed page responsiveness with multiple inline editors. #AP-430\n- Fixed empty toolbar groups appearing through invalid configuration of the `toolbar` property. #AP-450\n- Fixed text not being retained when updating links through the link dialog. #AP-293\n- Fixed edit image context menu, context toolbar and toolbar items being incorrectly enabled when selecting invalid images. #AP-323\n- Fixed emoji type ahead being shown when typing URLs. #AP-366\n- Fixed toolbar configuration properties incorrectly expecting string arrays instead of strings. #AP-342\n- Fixed the block formatting toolbar item not showing a \"Formatting\" title when there is no selection. #AP-321\n- Fixed clicking disabled toolbar buttons hiding the toolbar in inline mode. #AP-380\n- Fixed `EditorResize` event not being fired upon editor resize. #AP-327\n- Fixed tables losing styles when updating through the dialog. #AP-368\n- Fixed context toolbar positioning to be more consistent near the edges of the editor. #AP-318\n- Fixed table of contents plugin now works with v5 toolbar APIs correctly. #AP-347\n- Fixed the `link_context_toolbar` configuration not disabling the context toolbar. #AP-458\n- Fixed the link context toolbar showing incorrect relative links. #AP-435\n- Fixed the alignment of the icon in alert banner dialog components. #TINY-2220\n- Fixed the visual blocks and visual char menu options not displaying their toggled state. #TINY-2238\n- Fixed the editor not displaying as fullscreen when toggled. #TINY-2237\n\n### Removed\n- Removed the tox-custom-editor class that was added to the wrapping element of codemirror. #TINY-2211\n\n## 5.0.0-preview-3 - 2018-10-18\n\n### Changed\n- Changed editor layout to use modern CSS properties over manually calculating dimensions. #AP-324\n- Changed `autoresize_min_height` and `autoresize_max_height` configurations to `min_height` and `max_height`. #AP-324\n- Changed `Whole word` label in Search and Replace dialog to `Find whole words only`. #AP-387\n\n### Fixed\n- Fixed bugs with editor width jumping when resizing and the iframe not resizing to smaller than 150px in height. #AP-324\n- Fixed mobile theme bug that prevented the editor from loading. #AP-404\n- Fixed long toolbar groups extending outside of the editor instead of wrapping.\n- Fixed dialog titles so they are now proper case. #AP-384\n- Fixed color picker default to be #000000 instead of #ff00ff. #AP-216\n- Fixed \"match case\" option on the Find and Replace dialog is no longer selected by default. #AP-298\n- Fixed vertical alignment of toolbar icons. #DES-134\n- Fixed toolbar icons not appearing on IE11. #DES-133\n\n## 5.0.0-preview-2 - 2018-10-10\n\n### Added\n- Added swatch is now shown for colorinput fields, instead of the colorpicker directly. #AP-328\n- Added fontformats and fontsizes menu items. #AP-390\n\n### Changed\n- Changed configuration of color options has been simplified to `color_map`, `color_cols`, and `custom_colors`. #AP-328\n- Changed `height` configuration to apply to the editor frame (including menubar, toolbar, status bar) instead of the content area. #AP-324\n\n### Fixed\n- Fixed styleselect not updating the displayed item as the cursor moved. #AP-388\n- Fixed preview iframe not expanding to the dialog size. #AP-252\n- Fixed 'meta' shortcuts not translated into platform-specific text. #AP-270\n- Fixed tabbed dialogs (Charmap and Emoticons) shrinking when no search results returned.\n- Fixed a bug where alert banner icons were not retrieved from icon pack. #AP-330\n- Fixed component styles to flex so they fill large dialogs. #AP-252\n- Fixed editor flashing unstyled during load (still in progress). #AP-349\n\n### Removed\n- Removed `colorpicker` plugin, it is now in the theme. #AP-328\n- Removed `textcolor` plugin, it is now in the theme. #AP-328\n\n## 5.0.0-preview-1 - 2018-10-01\n\nDeveloper preview 1.\n\nInitial list of features and changes is available at https://www.tiny.cloud/docs/tinymce/5/release-notes/release-notes50/.\n\n## 4.9.11 - 2020-07-13\n\n### Fixed\n- Fixed the `selection.setContent()` API not running parser filters. #TINY-4002\n- Fixed content in an iframe element parsing as DOM elements instead of text content. #TINY-5943\n- Fixed up and down keyboard navigation not working for inline `contenteditable=\"false\"` elements. #TINY-6226\n\n## 4.9.10 - 2020-04-23\n\n### Fixed\n- Fixed an issue where the editor selection could end up inside a short ended element (eg br). #TINY-3999\n- Fixed a security issue related to CDATA sanitization during parsing. #TINY-4669\n- Fixed `media` embed content not processing safely in some cases. #TINY-4857\n\n## 4.9.9 - 2020-03-25\n\n### Fixed\n- Fixed the table selection not functioning correctly in Microsoft Edge 44 or higher. #TINY-3862\n- Fixed the table resize handles not functioning correctly in Microsoft Edge 44 or higher. #TINY-4160\n- Fixed the `forced_root_block_attrs` setting not applying attributes to new blocks consistently. #TINY-4564\n- Fixed the editor failing to initialize if a script tag was used inside an SVG. #TINY-4087\n\n## 4.9.8 - 2020-01-28\n\n### Fixed\n- Fixed the `mobile` theme failing to load due to a bundling issue. #TINY-4613\n- Fixed security issue related to parsing HTML comments and CDATA. #TINY-4544\n\n## 4.9.7 - 2019-12-19\n\n### Fixed\n- Fixed the `visualchars` plugin converting HTML-like text to DOM elements in certain cases. #TINY-4507\n- Fixed an issue with the `paste` plugin not sanitizing content in some cases. #TINY-4510\n- Fixed HTML comments incorrectly being parsed in certain cases. #TINY-4511\n\n## 4.9.6 - 2019-09-02\n\n### Fixed\n- Fixed image browse button sometimes displaying the browse window twice. #TINY-3959\n\n## 4.9.5 - 2019-07-02\n\n### Changed\n- Changed annotations navigation to work the same as inline boundaries. #TINY-3396\n\n### Fixed\n- Fixed the print plugin printing from the wrong window in IE11. #TINY-3762\n- Fixed an exception being thrown when a file or number input has focus during initialization. Patch contributed by t00. #GH-2194\n- Fixed positioning of the styleselect menu in iOS while using the mobile theme. #TINY-3505\n- Fixed native context menu not showing with images in IE11. #TINY-3392\n- Fixed selection incorrectly changing when programmatically setting selection on contenteditable false elements. #TINY-3766\n- Fixed image browse button not working on touch devices. #TINY-3751\n- Fixed so that nbsp entities aren't trimmed in white-space: pre-line elements. #TINY-3642\n- Fixed space key properly inserts a nbsp before/after block elements. #TINY-3745\n- Fixed infinite loop in the paste plugin when IE11 takes a long time to process paste events. Patch contributed by lRawd. #GH-4987\n\n## 4.9.4 - 2019-03-20\n\n### Fixed\n- Fixed an issue where **Home/End** keys wouldn't move the caret correctly before or after `contenteditable=false` inline elements. #TINY-2995\n- Fixed an issue where content may have been lost when using permanent bookmarks. #TINY-3400\n- Fixed the mobile editor to clean up properly when removed. #TINY-3445\n- Fixed an issue where retrieving the selected content as text didn't create newlines. #TINY-3197\n- Fixed an issue where typing space between images would cause issues with nbsp not being inserted. #TINY-3346\n\n## 4.9.3 - 2019-01-31\n\n### Added\n- Added a visualchars_default_state setting to the Visualchars Plugin. Patch contributed by mat3e.\n\n### Fixed\n- Fixed a bug where scrolling on a page with more than one editor would cause a ResizeWindow event to fire. #TINY-3247\n- Fixed a bug where if a plugin threw an error during initialisation the whole editor would fail to load. #TINY-3243\n- Fixed a bug where getContent would include bogus elements when valid_elements setting was set up in a specific way. #TINY-3213\n- Fixed a bug where only a few function key names could be used when creating keyboard shortcuts. #TINY-3146\n- Fixed a bug where it wasn't possible to enter spaces into an editor after pressing shift+enter. #TINY-3099\n- Fixed a bug where no caret would be rendered after backspacing to a contenteditable false element. #TINY-2998\n- Fixed a bug where deletion to/from indented lists would leave list fragments in the editor. #TINY-2981\n\n## 4.9.2 - 2018-12-17\n\n### Fixed\n- Fixed a bug with pressing the space key on IE 11 would result in nbsp characters being inserted between words at the end of a block. #TINY-2996\n- Fixed a bug where character composition using quote and space on US International keyboards would produce a space instead of a quote. #TINY-2999\n- Fixed a bug where remove format wouldn't remove the inner most inline element in some situations. #TINY-2982\n- Fixed a bug where outdenting an list item would affect attributes on other list items within the same list. #TINY-2971\n- Fixed a bug where the DomParser filters wouldn't be applied for elements created when parsing invalid html. #TINY-2978\n- Fixed a bug where setProgressState wouldn't automatically close floating ui elements like menus. #TINY-2896\n- Fixed a bug where it wasn't possible to navigate out of a figcaption element using the arrow keys. #TINY-2894\n- Fixed a bug where enter key before an image inside a link would remove the image. #TINY-2780\n\n## 4.9.1 - 2018-12-04\n\n### Added\n- Added functionality to insert html to the replacement feature of the Textpattern Plugin. #TINY-2839\n\n### Fixed\n- Fixed a bug where `editor.selection.getContent({format: 'text'})` didn't work as expected in IE11 on an unfocused editor. #TINY-2862\n- Fixed a bug in the Textpattern Plugin where the editor would get an incorrect selection after inserting a text pattern on Safari. #TINY-2838\n- Fixed a bug where the space bar didn't work correctly in editors with the forced_root_block setting set to false. #TINY-2816\n\n## 4.9.0 - 2018-11-27\n\n### Added\n- Added a replace feature to the Textpattern Plugin. #TINY-1908\n- Added functionality to the Lists Plugin that improves the indentation logic. #TINY-1790\n\n### Fixed\n- Fixed a bug where it wasn't possible to delete/backspace when the caret was between a contentEditable=false element and a BR. #TINY-2372\n- Fixed a bug where copying table cells without a text selection would fail to copy anything. #TINY-1789\n- Implemented missing `autosave_restore_when_empty` functionality in the Autosave Plugin. Patch contributed by gzzo. #GH-4447\n- Reduced insertion of unnecessary nonbreaking spaces in the editor. #TINY-1879\n\n## 4.8.5 - 2018-10-30\n\n### Added\n- Added a content_css_cors setting to the editor that adds the crossorigin=\"anonymous\" attribute to link tags added by the StyleSheetLoader. #TINY-1909\n\n### Fixed\n- Fixed a bug where trying to remove formatting with a collapsed selection range would throw an exception. #GH-4636\n- Fixed a bug in the image plugin that caused updating figures to split contenteditable elements. #GH-4563\n- Fixed a bug that was causing incorrect viewport calculations for fixed position UI elements. #TINY-1897\n- Fixed a bug where inline formatting would cause the delete key to do nothing. #TINY-1900\n\n## 4.8.4 - 2018-10-23\n\n### Added\n- Added support for the HTML5 `main` element. #TINY-1877\n\n### Changed\n- Changed the keyboard shortcut to move focus to contextual toolbars to Ctrl+F9. #TINY-1812\n\n### Fixed\n- Fixed a bug where content css could not be loaded from another domain. #TINY-1891\n- Fixed a bug on FireFox where the cursor would get stuck between two contenteditable false inline elements located inside of the same block element divided by a BR. #TINY-1878\n- Fixed a bug with the insertContent method where nonbreaking spaces would be inserted incorrectly. #TINY-1868\n- Fixed a bug where the toolbar of the inline editor would not be visible in some scenarios. #TINY-1862\n- Fixed a bug where removing the editor while more than one notification was open would throw an error. #TINY-1845\n- Fixed a bug where the menubutton would be rendered on top of the menu if the viewport didn't have enough height. #TINY-1678\n- Fixed a bug with the annotations api where annotating collapsed selections caused problems. #TBS-2449\n- Fixed a bug where wbr elements were being transformed into whitespace when using the Paste Plugin's paste as text setting. #GH-4638\n- Fixed a bug where the Search and Replace didn't replace spaces correctly. #GH-4632\n- Fixed a bug with sublist items not persisting selection. #GH-4628\n- Fixed a bug with mceInsertRawHTML command not working as expected. #GH-4625\n\n## 4.8.3 - 2018-09-13\n\n### Fixed\n- Fixed a bug where the Wordcount Plugin didn't correctly count words within tables on IE11. #TINY-1770\n- Fixed a bug where it wasn't possible to move the caret out of a table on IE11 and Firefox. #TINY-1682\n- Fixed a bug where merging empty blocks didn't work as expected, sometimes causing content to be deleted. #TINY-1781\n- Fixed a bug where the Textcolor Plugin didn't show the correct current color. #TINY-1810\n- Fixed a bug where clear formatting with a collapsed selection would sometimes clear formatting from more content than expected. #TINY-1813 #TINY-1821\n- Fixed a bug with the Table Plugin where it wasn't possible to keyboard navigate to the caption. #TINY-1818\n\n## 4.8.2 - 2018-08-09\n\n### Changed\n- Moved annotator from \"experimental\" to \"annotator\" object on editor. #TBS-2398\n- Improved the multiclick normalization across browsers. #TINY-1788\n\n### Fixed\n- Fixed a bug where running getSelectedBlocks with a collapsed selection between block elements would produce incorrect results. #TINY-1787\n- Fixed a bug where the ScriptLoaders loadScript method would not work as expected in FireFox when loaded on the same page as a ShadowDOM polyfill. #TINY-1786\n- Removed reference to ShadowDOM event.path as Blink based browsers now support event.composedPath. #TINY-1785\n- Fixed a bug where a reference to localStorage would throw an \"access denied\" error in IE11 with strict security settings. #TINY-1782\n- Fixed a bug where pasting using the toolbar button on an inline editor in IE11 would cause a looping behaviour. #TINY-1768\n\n## 4.8.1 - 2018-07-26\n\n### Fixed\n- Fixed a bug where the content of inline editors was being cleaned on every call of `editor.save()`. #TINY-1783\n- Fixed a bug where the arrow of the Inlite Theme toolbar was being rendered incorrectly in RTL mode. #TINY-1776\n- Fixed a bug with the Paste Plugin where pasting after inline contenteditable false elements moved the caret to the end of the line. #TINY-1758\n\n## 4.8.0 - 2018-06-27\n\n### Added\n- Added new \"experimental\" object in editor, with initial Annotator API. #TBS-2374\n\n### Fixed\n- Fixed a bug where deleting paragraphs inside of table cells would delete the whole table cell. #TINY-1759\n- Fixed a bug in the Table Plugin where removing row height set on the row properties dialog did not update the table. #TINY-1730\n- Fixed a bug with the font select toolbar item didn't update correctly. #TINY-1683\n- Fixed a bug where all bogus elements would not be deleted when removing an inline editor. #TINY-1669\n\n## 4.7.13 - 2018-05-16\n\n### Added\n- Added missing code menu item from the default menu config. #TINY-1648\n- Added new align button for combining the separate align buttons into a menu button. #TINY-1652\n\n### Fixed\n- Fixed a bug where Edge 17 wouldn't be able to select images or tables. #TINY-1679\n- Fixed issue where whitespace wasn't preserved when the editor was initialized on pre elements. #TINY-1649\n- Fixed a bug with the fontselect dropdowns throwing an error if the editor was hidden in Firefox. #TINY-1664\n- Fixed a bug where it wasn't possible to merge table cells on IE 11. #TINY-1671\n- Fixed a bug where textcolor wasn't applying properly on IE 11 in some situations. #TINY-1663\n- Fixed a bug where the justifyfull command state wasn't working correctly. #TINY-1677\n- Fixed a bug where the styles wasn't updated correctly when resizing some tables. #TINY-1668\n\n## 4.7.12 - 2018-05-03\n\n### Added\n- Added an option to filter out image svg data urls.\n- Added support for html5 details and summary elements.\n\n### Changed\n- Changed so the mce-abs-layout-item css rule targets html instead of body. Patch contributed by nazar-pc.\n\n### Fixed\n- Fixed a bug where the \"read\" step on the mobile theme was still present on android mobile browsers.\n- Fixed a bug where all images in the editor document would reload on any editor change.\n- Fixed a bug with the Table Plugin where ObjectResized event wasn't being triggered on column resize.\n- Fixed so the selection is set to the first suitable caret position after editor.setContent called.\n- Fixed so links with xlink:href attributes are filtered correctly to prevent XSS.\n- Fixed a bug on IE11 where pasting content into an inline editor initialized on a heading element would create new editable elements.\n- Fixed a bug where readonly mode would not work as expected when the editor contained contentEditable=true elements.\n- Fixed a bug where the Link Plugin would throw an error when used together with the webcomponents polyfill. Patch contributed by 4esnog.\n- Fixed a bug where the \"Powered by TinyMCE\" branding link would break on XHTML pages. Patch contributed by tistre.\n- Fixed a bug where the same id would be used in the blobcache for all pasted images. Patch contributed by thorn0.\n\n## 4.7.11 - 2018-04-11\n\n### Added\n- Added a new imagetools_credentials_hosts option to the Imagetools Plugin.\n\n### Fixed\n- Fixed a bug where toggling a list containing empty LIs would throw an error. Patch contributed by bradleyke.\n- Fixed a bug where applying block styles to a text with the caret at the end of the paragraph would select all text in the paragraph.\n- Fixed a bug where toggling on the Spellchecker Plugin would trigger isDirty on the editor.\n- Fixed a bug where it was possible to enter content into selection bookmark spans.\n- Fixed a bug where if a non paragraph block was configured in forced_root_block the editor.getContent method would return incorrect values with an empty editor.\n- Fixed a bug where dropdown menu panels stayed open and fixed in position when dragging dialog windows.\n- Fixed a bug where it wasn't possible to extend table cells with the space button in Safari.\n- Fixed a bug where the setupeditor event would thrown an error when using the Compat3x Plugin.\n- Fixed a bug where an error was thrown in FontInfo when called on a detached element.\n\n## 4.7.10 - 2018-04-03\n\n### Added\n- Added normalization of triple clicks across browsers in the editor.\n- Added a `hasFocus` method to the editor that checks if the editor has focus.\n- Added correct icon to the Nonbreaking Plugin menu item.\n\n### Fixed\n- Fixed so the `getContent`/`setContent` methods work even if the editor is not initialized.\n- Fixed a bug with the Media Plugin where query strings were being stripped from youtube links.\n- Fixed a bug where image styles were changed/removed when opening and closing the Image Plugin dialog.\n- Fixed a bug in the Table Plugin where some table cell styles were not correctly added to the content html.\n- Fixed a bug in the Spellchecker Plugin where it wasn't possible to change the spellchecker language.\n- Fixed so the the unlink action in the Link Plugin has a menu item and can be added to the contextmenu.\n- Fixed a bug where it wasn't possible to keyboard navigate to the start of an inline element on a new line within the same block element.\n- Fixed a bug with the Text Color Plugin where if used with an inline editor located at the bottom of the screen the colorpicker could appear off screen.\n- Fixed a bug with the UndoManager where undo levels were being added for nbzwsp characters.\n- Fixed a bug with the Table Plugin where the caret would sometimes be lost when keyboard navigating up through a table.\n- Fixed a bug where FontInfo.getFontFamily would throw an error when called on a removed editor.\n- Fixed a bug in Firefox where undo levels were not being added correctly for some specific operations.\n- Fixed a bug where initializing an inline editor inside of a table would make the whole table resizeable.\n- Fixed a bug where the fake cursor that appears next to tables on Firefox was positioned incorrectly when switching to fullscreen.\n- Fixed a bug where zwsp's weren't trimmed from the output from `editor.getContent({ format: 'text' })`.\n- Fixed a bug where the fontsizeselect/fontselect toolbar items showed the body info rather than the first possible caret position info on init.\n- Fixed a bug where it wasn't possible to select all content if the editor only contained an inline boundary element.\n- Fixed a bug where `content_css` urls with query strings wasn't working.\n- Fixed a bug in the Table Plugin where some table row styles were removed when changing other styles in the row properties dialog.\n\n### Removed\n- Removed the \"read\" step from the mobile theme.\n\n## 4.7.9 - 2018-02-27\n\n### Fixed\n- Fixed a bug where the editor target element didn't get the correct style when removing the editor.\n\n## 4.7.8 - 2018-02-26\n\n### Fixed\n- Fixed an issue with the Help Plugin where the menuitem name wasn't lowercase.\n- Fixed an issue on MacOS where text and bold text did not have the same line-height in the autocomplete dropdown in the Link Plugin dialog.\n- Fixed a bug where the \"paste as text\" option in the Paste Plugin didn't work.\n- Fixed a bug where dialog list boxes didn't get positioned correctly in documents with scroll.\n- Fixed a bug where the Inlite Theme didn't use the Table Plugin api to insert correct tables.\n- Fixed a bug where the Inlite Theme panel didn't hide on blur in a correct way.\n- Fixed a bug where placing the cursor before a table in Firefox would scroll to the bottom of the table.\n- Fixed a bug where selecting partial text in table cells with rowspans and deleting would produce faulty tables.\n- Fixed a bug where the Preview Plugin didn't work on Safari due to sandbox security.\n- Fixed a bug where table cell selection using the keyboard threw an error.\n- Fixed so the font size and font family doesn't toggle the text but only sets the selected format on the selected text.\n- Fixed so the built-in spellchecking on Chrome and Safari creates an undo level when replacing words.\n\n## 4.7.7 - 2018-02-19\n\n### Added\n- Added a border style selector to the advanced tab of the Image Plugin.\n- Added better controls for default table inserted by the Table Plugin.\n- Added new `table_responsive_width` option to the Table Plugin that controls whether to use pixel or percentage widths.\n\n### Fixed\n- Fixed a bug where the Link Plugin text didn't update when a URL was pasted using the context menu.\n- Fixed a bug with the Spellchecker Plugin where using \"Add to dictionary\" in the context menu threw an error.\n- Fixed a bug in the Media Plugin where the preview node for iframes got default width and height attributes that interfered with width/height styles.\n- Fixed a bug where backslashes were being added to some font family names in Firefox in the fontselect toolbar item.\n- Fixed a bug where errors would be thrown when trying to remove an editor that had not yet been fully initialized.\n- Fixed a bug where the Imagetools Plugin didn't update the images atomically.\n- Fixed a bug where the Fullscreen Plugin was throwing errors when being used on an inline editor.\n- Fixed a bug where drop down menus weren't positioned correctly in inline editors on scroll.\n- Fixed a bug with a semicolon missing at the end of the bundled javascript files.\n- Fixed a bug in the Table Plugin with cursor navigation inside of tables where the cursor would sometimes jump into an incorrect table cells.\n- Fixed a bug where indenting a table that is a list item using the \"Increase indent\" button would create a nested table.\n- Fixed a bug where text nodes containing only whitespace were being wrapped by paragraph elements.\n- Fixed a bug where whitespace was being inserted after br tags inside of paragraph tags.\n- Fixed a bug where converting an indented paragraph to a list item would cause the list item to have extra padding.\n- Fixed a bug where Copy/Paste in an editor with a lot of content would cause the editor to scroll to the top of the content in IE11.\n- Fixed a bug with a memory leak in the DragHelper. Path contributed by ben-mckernan.\n- Fixed a bug where the advanced tab in the Media Plugin was being shown even if it didn't contain anything. Patch contributed by gabrieeel.\n- Fixed an outdated eventname in the EventUtils. Patch contributed by nazar-pc.\n- Fixed an issue where the Json.parse function would throw an error when being used on a page with strict CSP settings.\n- Fixed so you can place the curser before and after table elements within the editor in Firefox and Edge/IE.\n\n## 4.7.6 - 2018-01-29\n\n### Fixed\n- Fixed a bug in the jquery integration where it threw an error saying that \"global is not defined\".\n- Fixed a bug where deleting a table cell whose previous sibling was set to contenteditable false would create a corrupted table.\n- Fixed a bug where highlighting text in an unfocused editor did not work correctly in IE11/Edge.\n- Fixed a bug where the table resize handles were not being repositioned when activating the Fullscreen Plugin.\n- Fixed a bug where the Imagetools Plugin dialog didn't honor editor RTL settings.\n- Fixed a bug where block elements weren't being merged correctly if you deleted from after a contenteditable false element to the beginning of another block element.\n- Fixed a bug where TinyMCE didn't work with module loaders like webpack.\n\n## 4.7.5 - 2018-01-22\n\n### Fixed\n- Fixed bug with the Codesample Plugin where it wasn't possible to edit codesamples when the editor was in inline mode.\n- Fixed bug where focusing on the status bar broke the keyboard navigation functionality.\n- Fixed bug where an error would be thrown on Edge by the Table Plugin when pasting using the PowerPaste Plugin.\n- Fixed bug in the Table Plugin where selecting row border style from the dropdown menu in advanced row properties would throw an error.\n- Fixed bug with icons being rendered incorrectly on Chrome on Mac OS.\n- Fixed bug in the Textcolor Plugin where the font color and background color buttons wouldn't trigger an ExecCommand event.\n- Fixed bug in the Link Plugin where the url field wasn't forced LTR.\n- Fixed bug where the Nonbreaking Plugin incorrectly inserted spaces into tables.\n- Fixed bug with the inline theme where the toolbar wasn't repositioned on window resize.\n\n## 4.7.4 - 2017-12-05\n\n### Fixed\n- Fixed bug in the Nonbreaking Plugin where the nonbreaking_force_tab setting was being ignored.\n- Fixed bug in the Table Plugin where changing row height incorrectly converted column widths to pixels.\n- Fixed bug in the Table Plugin on Edge and IE11 where resizing the last column after resizing the table would cause invalid column heights.\n- Fixed bug in the Table Plugin where keyboard navigation was not normalized between browsers.\n- Fixed bug in the Table Plugin where the colorpicker button would show even without defining the colorpicker_callback.\n- Fixed bug in the Table Plugin where it wasn't possible to set the cell background color.\n- Fixed bug where Firefox would throw an error when intialising an editor on an element that is hidden or not yet added to the DOM.\n- Fixed bug where Firefox would throw an error when intialising an editor inside of a hidden iframe.\n\n## 4.7.3 - 2017-11-23\n\n### Added\n- Added functionality to open the Codesample Plugin dialog when double clicking on a codesample. Patch contributed by dakuzen.\n\n### Fixed\n- Fixed bug where undo/redo didn't work correctly with some formats and caret positions.\n- Fixed bug where the color picker didn't show up in Table Plugin dialogs.\n- Fixed bug where it wasn't possible to change the width of a table through the Table Plugin dialog.\n- Fixed bug where the Charmap Plugin couldn't insert some special characters.\n- Fixed bug where editing a newly inserted link would not actually edit the link but insert a new link next to it.\n- Fixed bug where deleting all content in a table cell made it impossible to place the caret into it.\n- Fixed bug where the vertical alignment field in the Table Plugin cell properties dialog didn't do anything.\n- Fixed bug where an image with a caption showed two sets of resize handles in IE11.\n- Fixed bug where pressing the enter button inside of an h1 with contenteditable set to true would sometimes produce a p tag.\n- Fixed bug with backspace not working as expected before a noneditable element.\n- Fixed bug where operating on tables with invalid rowspans would cause an error to be thrown.\n- Fixed so a real base64 representation of the image is available on the blobInfo that the images_upload_handler gets called with.\n- Fixed so the image upload tab is available when the images_upload_handler is defined (and not only when the images_upload_url is defined).\n\n## 4.7.2 - 2017-11-07\n\n### Added\n- Added newly rewritten Table Plugin.\n- Added support for attributes with colon in valid_elements and addValidElements.\n- Added support for dailymotion short url in the Media Plugin. Patch contributed by maat8.\n- Added support for converting to half pt when converting font size from px to pt. Patch contributed by danny6514.\n- Added support for location hash to the Autosave plugin to make it work better with SPAs using hash routing.\n- Added support for merging table cells when pasting a table into another table.\n\n### Changed\n- Changed so the language packs are only loaded once. Patch contributed by 0xor1.\n- Simplified the css for inline boundaries selection by switching to an attribute selector.\n\n### Fixed\n- Fixed bug where an error would be thrown on editor initialization if the window.getSelection() returned null.\n- Fixed bug where holding down control or alt keys made the keyboard navigation inside an inline boundary not work as expected.\n- Fixed bug where applying formats in IE11 produced extra, empty paragraphs in the editor.\n- Fixed bug where the Word Count Plugin didn't count some mathematical operators correctly.\n- Fixed bug where removing an inline editor removed the element that the editor had been initialized on.\n- Fixed bug where setting the selection to the end of an editable container caused some formatting problems.\n- Fixed bug where an error would be thrown sometimes when an editor was removed because of the selection bookmark was being stored asynchronously.\n- Fixed a bug where an editor initialized on an empty list did not contain any valid cursor positions.\n- Fixed a bug with the Context Menu Plugin and webkit browsers on Mac where right-clicking inside a table would produce an incorrect selection.\n- Fixed bug where the Image Plugin constrain proportions setting wasn't working as expected.\n- Fixed bug where deleting the last character in a span with decorations produced an incorrect element when typing.\n- Fixed bug where focusing on inline editors made the toolbar flicker when moving between elements quickly.\n- Fixed bug where the selection would be stored incorrectly in inline editors when the mouseup event was fired outside the editor body.\n- Fixed bug where toggling bold at the end of an inline boundary would toggle off the whole word.\n- Fixed bug where setting the skin to false would not stop the loading of some skin css files.\n- Fixed bug in mobile theme where pinch-to-zoom would break after exiting the editor.\n- Fixed bug where sublists of a fully selected list would not be switched correctly when changing list style.\n- Fixed bug where inserting media by source would break the UndoManager.\n- Fixed bug where inserting some content into the editor with a specific selection would replace some content incorrectly.\n- Fixed bug where selecting all content with ctrl+a in IE11 caused problems with untoggling some formatting.\n- Fixed bug where the Search and Replace Plugin left some marker spans in the editor when undoing and redoing after replacing some content.\n- Fixed bug where the editor would not get a scrollbar when using the Fullscreen and Autoresize plugins together.\n- Fixed bug where the font selector would stop working correctly after selecting fonts three times.\n- Fixed so pressing the enter key inside of an inline boundary inserts a br after the inline boundary element.\n- Fixed a bug where it wasn't possible to use tab navigation inside of a table that was inside of a list.\n- Fixed bug where end_container_on_empty_block would incorrectly remove elements.\n- Fixed bug where content_styles weren't added to the Preview Plugin iframe.\n- Fixed so the beforeSetContent/beforeGetContent events are preventable.\n- Fixed bug where changing height value in Table Plugin advanced tab didn't do anything.\n- Fixed bug where it wasn't possible to remove formatting from content in beginning of table cell.\n\n## 4.7.1 - 2017-10-09\n\n### Fixed\n- Fixed bug where theme set to false on an inline editor produced an extra div element after the target element.\n- Fixed bug where the editor drag icon was misaligned with the branding set to false.\n- Fixed bug where doubled menu items were not being removed as expected with the removed_menuitems setting.\n- Fixed bug where the Table of contents plugin threw an error when initialized.\n- Fixed bug where it wasn't possible to add inline formats to text selected right to left.\n- Fixed bug where the paste from plain text mode did not work as expected.\n- Fixed so the style previews do not set color and background color when selected.\n- Fixed bug where the Autolink plugin didn't work as expected with some formats applied on an empty editor.\n- Fixed bug where the Textpattern plugin were throwing errors on some patterns.\n- Fixed bug where the Save plugin saved all editors instead of only the active editor. Patch contributed by dannoe.\n\n## 4.7.0 - 2017-10-03\n\n### Added\n- Added new mobile ui that is specifically designed for mobile devices.\n\n### Changed\n- Updated the default skin to be more modern and white since white is preferred by most implementations.\n- Restructured the default menus to be more similar to common office suites like Google Docs.\n\n### Fixed\n- Fixed so theme can be set to false on both inline and iframe editor modes.\n- Fixed bug where inline editor would add/remove the visualblocks css multiple times.\n- Fixed bug where selection wouldn't be properly restored when editor lost focus and commands where invoked.\n- Fixed bug where toc plugin would generate id:s for headers even though a toc wasn't inserted into the content.\n- Fixed bug where is wasn't possible to drag/drop contents within the editor if paste_data_images where set to true.\n- Fixed bug where getParam and close in WindowManager would get the first opened window instead of the last opened window.\n- Fixed bug where delete would delete between cells inside a table in Firefox.\n\n## 4.6.7 - 2017-09-18\n\n### Added\n- Added some missing translations to Image, Link and Help plugins.\n\n### Fixed\n- Fixed bug where paste wasn't working in IOS.\n- Fixed bug where the Word Count Plugin didn't count some mathematical operators correctly.\n- Fixed bug where inserting a list in a table caused the cell to expand in height.\n- Fixed bug where pressing enter in a list located inside of a table deleted list items instead of inserting new list item.\n- Fixed bug where copy and pasting table cells produced inconsistent results.\n- Fixed bug where initializing an editor with an ID of 'length' would throw an exception.\n- Fixed bug where it was possible to split a non merged table cell.\n- Fixed bug where copy and pasting a list with a very specific selection into another list would produce a nested list.\n- Fixed bug where copy and pasting ordered lists sometimes produced unordered lists.\n- Fixed bug where padded elements inside other elements would be treated as empty.\n- Fixed so you can resize images inside a figure element.\n- Fixed bug where an inline TinyMCE editor initialized on a table did not set selection on load in Chrome.\n- Fixed the positioning of the inlite toolbar when the target element wasn't big enough to fit the toolbar.\n\n## 4.6.6 - 2017-08-30\n\n### Fixed\n- Fixed so that notifications wrap long text content instead of bleeding outside the notification element.\n- Fixed so the content_style css is added after the skin and custom stylesheets.\n- Fixed bug where it wasn't possible to remove a table with the Cut button.\n- Fixed bug where the center format wasn't getting the same font size as the other formats in the format preview.\n- Fixed bug where the wordcount plugin wasn't counting hyphenated words correctly.\n- Fixed bug where all content pasted into the editor was added to the end of the editor.\n- Fixed bug where enter keydown on list item selection only deleted content and didn't create a new line.\n- Fixed bug where destroying the editor while the content css was still loading caused error notifications on Firefox.\n- Fixed bug where undoing cut operation in IE11 left some unwanted html in the editor content.\n- Fixed bug where enter keydown would throw an error in IE11.\n- Fixed bug where duplicate instances of an editor were added to the editors array when using the createEditor API.\n- Fixed bug where the formatter applied formats on the wrong content when spellchecker was activated.\n- Fixed bug where switching formats would reset font size on child nodes.\n- Fixed bug where the table caption element weren't always the first descendant to the table tag.\n- Fixed bug where pasting some content into the editor on chrome some newlines were removed.\n- Fixed bug where it wasn't possible to remove a list if a list item was a table element.\n- Fixed bug where copy/pasting partial selections of tables wouldn't produce a proper table.\n- Fixed bug where the searchreplace plugin could not find consecutive spaces.\n- Fixed bug where background color wasn't applied correctly on some partially selected contents.\n\n## 4.6.5 - 2017-08-02\n\n### Added\n- Added new inline_boundaries_selector that allows you to specify the elements that should have boundaries.\n- Added new local upload feature this allows the user to upload images directly from the image dialog.\n- Added a new api for providing meta data for plugins. It will show up in the help dialog if it's provided.\n\n### Fixed\n- Fixed so that the notifications created by the notification manager are more screen reader accessible.\n- Fixed bug where changing the list format on multiple selected lists didn't change all of the lists.\n- Fixed bug where the nonbreaking plugin would insert multiple undo levels when pressing the tab key.\n- Fixed bug where delete/backspace wouldn't render a caret when all editor contents where deleted.\n- Fixed bug where delete/backspace wouldn't render a caret if the deleted element was a single contentEditable false element.\n- Fixed bug where the wordcount plugin wouldn't count words correctly if word where typed after applying a style format.\n- Fixed bug where the wordcount plugin would count mathematical formulas as multiple words for example 1+1=2.\n- Fixed bug where formatting of triple clicked blocks on Chrome/Safari would result in styles being added outside the visual selection.\n- Fixed bug where paste would add the contents to the end of the editor area when inline mode was used.\n- Fixed bug where toggling off bold formatting on text entered in a new paragraph would add an extra line break.\n- Fixed bug where autolink plugin would only produce a link on every other consecutive link on Firefox.\n- Fixed bug where it wasn't possible to select all contents if the content only had one pre element.\n- Fixed bug where sizzle would produce lagging behavior on some sites due to repaints caused by feature detection.\n- Fixed bug where toggling off inline formats wouldn't include the space on selected contents with leading or trailing spaces.\n- Fixed bug where the cut operation in UI wouldn't work in Chrome.\n- Fixed bug where some legacy editor initialization logic would throw exceptions about editor settings not being defined.\n- Fixed bug where it wasn't possible to apply text color to links if they where part of a non collapsed selection.\n- Fixed bug where an exception would be thrown if the user selected a video element and then moved the focus outside the editor.\n- Fixed bug where list operations didn't work if there where block elements inside the list items.\n- Fixed bug where applying block formats to lists wrapped in block elements would apply to all elements in that wrapped block.\n\n## 4.6.4 - 2017-06-13\n\n### Fixed\n- Fixed bug where the editor would move the caret when clicking on the scrollbar next to a content editable false block.\n- Fixed bug where the text color select dropdowns wasn't placed correctly when they didn't fit the width of the screen.\n- Fixed bug where the default editor line height wasn't working for mixed font size contents.\n- Fixed bug where the content css files for inline editors were loaded multiple times for multiple editor instances.\n- Fixed bug where the initial value of the font size/font family dropdowns wasn't displayed.\n- Fixed bug where the I18n api was not supporting arrays as the translation replacement values.\n- Fixed bug where chrome would display \"The given range isn't in document.\" errors for invalid ranges passed to setRng.\n- Fixed bug where the compat3x plugin wasn't working since the global tinymce references wasn't resolved correctly.\n- Fixed bug where the preview plugin wasn't encoding the base url passed into the iframe contents producing a xss bug.\n- Fixed bug where the dom parser/serializer wasn't handling some special elements like noframes, title and xmp.\n- Fixed bug where the dom parser/serializer wasn't handling cdata sections with comments inside.\n- Fixed bug where the editor would scroll to the top of the editable area if a dialog was closed in inline mode.\n- Fixed bug where the link dialog would not display the right rel value if rel_list was configured.\n- Fixed bug where the context menu would select images on some platforms but not others.\n- Fixed bug where the filenames of images were not retained on dragged and drop into the editor from the desktop.\n- Fixed bug where the paste plugin would misrepresent newlines when pasting plain text and having forced_root_block configured.\n- Fixed so that the error messages for the imagetools plugin is more human readable.\n- Fixed so the internal validate setting for the parser/serializer can't be set from editor initialization settings.\n\n## 4.6.3 - 2017-05-30\n\n### Fixed\n- Fixed bug where the arrow keys didn't work correctly when navigating on nested inline boundary elements.\n- Fixed bug where delete/backspace didn't work correctly on nested inline boundary elements.\n- Fixed bug where image editing didn't work on subsequent edits of the same image.\n- Fixed bug where charmap descriptions wouldn't properly wrap if they exceeded the width of the box.\n- Fixed bug where the default image upload handler only accepted 200 as a valid http status code.\n- Fixed so rel on target=_blank links gets forced with only noopener instead of both noopener and noreferrer.\n\n## 4.6.2 - 2017-05-23\n\n### Fixed\n- Fixed bug where the SaxParser would run out of memory on very large documents.\n- Fixed bug with formatting like font size wasn't applied to del elements.\n- Fixed bug where various api calls would be throwing exceptions if they where invoked on a removed editor instance.\n- Fixed bug where the branding position would be incorrect if the editor was inside a hidden tab and then later showed.\n- Fixed bug where the color levels feature in the imagetools dialog wasn't working properly.\n- Fixed bug where imagetools dialog wouldn't pre-load images from CORS domains, before trying to prepare them for editing.\n- Fixed bug where the tab key would move the caret to the next table cell if being pressed inside a list inside a table.\n- Fixed bug where the cut/copy operations would loose parent context like the current format etc.\n- Fixed bug with format preview not working on invalid elements excluded by valid_elements.\n- Fixed bug where blocks would be merged in incorrect order on backspace/delete.\n- Fixed bug where zero length text nodes would cause issues with the undo logic if there where iframes present.\n- Fixed bug where the font size/family select lists would throw errors if the first node was a comment.\n- Fixed bug with csp having to allow local script evaluation since it was used to detect global scope.\n- Fixed bug where CSP required a relaxed option for javascript: URLs in unsupported legacy browsers.\n- Fixed bug where a fake caret would be rendered for td with the contenteditable=false.\n- Fixed bug where typing would be blocked on IE 11 when within a nested contenteditable=true/false structure.\n\n## 4.6.1 - 2017-05-10\n\n### Added\n- Added configuration option to list plugin to disable tab indentation.\n\n### Fixed\n- Fixed bug where format change on very specific content could cause the selection to change.\n- Fixed bug where TinyMCE could not be lazyloaded through jquery integration.\n- Fixed bug where entities in style attributes weren't decoded correctly on paste in webkit.\n- Fixed bug where fontsize_formats option had been renamed incorrectly.\n- Fixed bug with broken backspace/delete behaviour between contenteditable=false blocks.\n- Fixed bug where it wasn't possible to backspace to the previous line with the inline boundaries functionality turned on.\n- Fixed bug where is wasn't possible to move caret left and right around a linked image with the inline boundaries functionality turned on.\n- Fixed bug where pressing enter after/before hr element threw exception. Patch contributed bradleyke.\n- Fixed so the CSS in the visualblocks plugin doesn't overwrite background color. Patch contributed by Christian Rank.\n- Fixed bug where multibyte characters weren't encoded correctly. Patch contributed by James Tarkenton.\n- Fixed bug where shift-click to select within contenteditable=true fields wasn't working.\n\n## 4.6.0 - 2017-05-04\n\n### Added\n- Added an inline boundary caret position feature that makes it easier to type at the beginning/end of links/code elements.\n- Added a help plugin that adds a button and a dialog showing the editor shortcuts and loaded plugins.\n- Added an inline_boundaries option that allows you to disable the inline boundary feature if it's not desired.\n- Added a new ScrollIntoView event that allows you to override the default scroll to element behavior.\n- Added role and aria- attributes as valid elements in the default valid elements config.\n- Added new internal flag for PastePreProcess/PastePostProcess this is useful to know if the paste was coming from an external source.\n- Added new ignore function to UndoManager this works similar to transact except that it doesn't add an undo level by default.\n\n### Fixed\n- Fixed so that urls gets retained for images when being edited. This url is then passed on to the upload handler.\n- Fixed so that the editors would be initialized on readyState interactive instead of complete.\n- Fixed so that the init event of the editor gets fired once all contentCSS files have been properly loaded.\n- Fixed so that width/height of the editor gets taken from the textarea element if it's explicitly specified in styles.\n- Fixed so that keep_styles set to false no longer clones class/style from the previous paragraph on enter.\n- Fixed so that the default line-height is 1.2em to avoid zwnbsp characters from producing text rendering glitches on Windows.\n- Fixed so that loading errors of content css gets presented by a notification message.\n- Fixed so figure image elements can be linked when selected this wraps the figure image in a anchor element.\n- Fixed bug where it wasn't possible to copy/paste rows with colspans by using the table copy/paste feature.\n- Fixed bug where the protect setting wasn't properly applied to header/footer parts when using the fullpage plugin.\n- Fixed bug where custom formats that specified upper case element names where not applied correctly.\n- Fixed bug where some screen readers weren't reading buttons due to an aria specific fix for IE 8.\n- Fixed bug where cut wasn't working correctly on iOS due to it's clipboard API not working correctly.\n- Fixed bug where Edge would paste div elements instead of paragraphs when pasting plain text.\n- Fixed bug where the textpattern plugin wasn't dealing with trailing punctuations correctly.\n- Fixed bug where image editing would some times change the image format from jpg to png.\n- Fixed bug where some UI elements could be inserted into the toolbar even if they where not registered.\n- Fixed bug where it was possible to click the TD instead of the character in the character map and that caused an exception.\n- Fixed bug where the font size/font family dropdowns would sometimes show an incorrect value due to css not being loaded in time.\n- Fixed bug with the media plugin inserting undefined instead of retaining size when media_dimensions was set to false.\n- Fixed bug with deleting images when forced_root_blocks where set to false.\n- Fixed bug where input focus wasn't properly handled on nested content editable elements.\n- Fixed bug where Chrome/Firefox would throw an exception when selecting images due to recent change of setBaseAndExtent support.\n- Fixed bug where malformed blobs would throw exceptions now they are simply ignored.\n- Fixed bug where backspace/delete wouldn't work properly in some cases where all contents was selected in WebKit.\n- Fixed bug with Angular producing errors since it was expecting events objects to be patched with their custom properties.\n- Fixed bug where the formatter would apply formatting to spellchecker errors now all bogus elements are excluded.\n- Fixed bug with backspace/delete inside table caption elements wouldn't behave properly on IE 11.\n- Fixed bug where typing after a contenteditable false inline element could move the caret to the end of that element.\n- Fixed bug where backspace before/after contenteditable false blocks wouldn't properly remove the right element.\n- Fixed bug where backspace before/after contenteditable false inline elements wouldn't properly empty the current block element.\n- Fixed bug where vertical caret navigation with a custom line-height would sometimes match incorrect positions.\n- Fixed bug with paste on Edge where character encoding wasn't handled properly due to a browser bug.\n- Fixed bug with paste on Edge where extra fragment data was inserted into the contents when pasting.\n- Fixed bug with pasting contents when having a whole block element selected on WebKit could cause WebKit spans to appear.\n- Fixed bug where the visualchars plugin wasn't working correctly showing invisible nbsp characters.\n- Fixed bug where browsers would hang if you tried to load some malformed html contents.\n- Fixed bug where the init call promise wouldn't resolve if the specified selector didn't find any matching elements.\n- Fixed bug where the Schema isValidChild function was case sensitive.\n\n### Removed\n- Dropped support for IE 8-10 due to market share and lack of support from Microsoft. See tinymce docs for details.\n\n## 4.5.3 - 2017-02-01\n\n### Added\n- Added keyboard navigation for menu buttons when the menu is in focus.\n- Added api to the list plugin for setting custom classes/attributes on lists.\n- Added validation for the anchor plugin input field according to W3C id naming specifications.\n\n### Fixed\n- Fixed bug where media placeholders were removed after resize with the forced_root_block setting set to false.\n- Fixed bug where deleting selections with similar sibling nodes sometimes deleted the whole document.\n- Fixed bug with inlite theme where several toolbars would appear scrolling when more than one instance of the editor was in use.\n- Fixed bug where the editor would throw error with the fontselect plugin on hidden editor instances in Firefox.\n- Fixed bug where the background color would not stretch to the font size.\n- Fixed bug where font size would be removed when changing background color.\n- Fixed bug where the undomanager trimmed away whitespace between nodes on undo/redo.\n- Fixed bug where media_dimensions=false in media plugin caused the editor to throw an error.\n- Fixed bug where IE was producing font/u elements within links on paste.\n- Fixed bug where some button tooltips were broken when compat3x was in use.\n- Fixed bug where backspace/delete/typeover would remove the caption element.\n- Fixed bug where powerspell failed to function when compat3x was enabled.\n- Fixed bug where it wasn't possible to apply sub/sup on text with large font size.\n- Fixed bug where pre tags with spaces weren't treated as content.\n- Fixed bug where Meta+A would select the entire document instead of all contents in nested ce=true elements.\n\n## 4.5.2 - 2017-01-04\n\n### Fixed\n- Added missing keyboard shortcut description for the underline menu item in the format menu.\n- Fixed bug where external blob urls wasn't properly handled by editor upload logic. Patch contributed by David Oviedo.\n- Fixed bug where urls wasn't treated as a single word by the wordcount plugin.\n- Fixed bug where nbsp characters wasn't treated as word delimiters by the wordcount plugin.\n- Fixed bug where editor instance wasn't properly passed to the format preview logic. Patch contributed by NullQuery.\n- Fixed bug where the fake caret wasn't hidden when you moved selection to a cE=false element.\n- Fixed bug where it wasn't possible to edit existing code sample blocks.\n- Fixed bug where it wasn't possible to delete editor contents if the selection included an empty block.\n- Fixed bug where the formatter wasn't expanding words on some international characters. Patch contributed by Martin Larochelle.\n- Fixed bug where the open link feature wasn't working correctly on IE 11.\n- Fixed bug where enter before/after a cE=false block wouldn't properly padd the paragraph with an br element.\n- Fixed so font size and font family select boxes always displays a value by using the runtime style as a fallback.\n- Fixed so missing plugins will be logged to console as warnings rather than halting the initialization of the editor.\n- Fixed so splitbuttons become normal buttons in advlist plugin if styles are empty. Patch contributed by René Schleusner.\n- Fixed so you can multi insert rows/cols by selecting table cells and using insert rows/columns.\n\n## 4.5.1 - 2016-12-07\n\n### Fixed\n- Fixed bug where the lists plugin wouldn't initialize without the advlist plugins if served from cdn.\n- Fixed bug where selectors with \"*\" would cause the style format preview to throw an error.\n- Fixed bug with toggling lists off on lists with empty list items would throw an error.\n- Fixed bug where editing images would produce non existing blob uris.\n- Fixed bug where the offscreen toc selection would be treated as the real toc element.\n- Fixed bug where the aria level attribute for element path would have an incorrect start index.\n- Fixed bug where the offscreen selection of cE=false that where very wide would be shown onscreen. Patch contributed by Steven Bufton.\n- Fixed so the default_link_target gets applied to links created by the autolink plugin.\n- Fixed so that the name attribute gets removed by the anchor plugin if editing anchors.\n\n## 4.5.0 - 2016-11-23\n\n### Added\n- Added new toc plugin allows you to insert table of contents based on editor headings.\n- Added new auto complete menu to all url fields. Adds history, link to anchors etc.\n- Added new sidebar api that allows you to add custom sidebar panels and buttons to toggle these.\n- Added new insert menu button that allows you to have multiple insert functions under the same menu button.\n- Added new open link feature to ctrl+click, alt+enter and context menu.\n- Added new media_embed_handler option to allow the media plugin to be populated with custom embeds.\n- Added new support for editing transparent images using the image tools dialog.\n- Added new images_reuse_filename option to allow filenames of images to be retained for upload.\n- Added new security feature where links with target=\"_blank\" will by default get rel=\"noopener noreferrer\".\n- Added new allow_unsafe_link_target to allow you to opt-out of the target=\"_blank\" security feature.\n- Added new style_formats_autohide option to automatically hide styles based on context.\n- Added new codesample_content_css option to specify where the code sample prism css is loaded from.\n- Added new support for Japanese/Chinese word count following the unicode standards on this.\n- Added new fragmented undo levels this dramatically reduces flicker on contents with iframes.\n- Added new live previews for complex elements like table or lists.\n\n### Fixed\n- Fixed bug where it wasn't possible to properly tab between controls in a dialog with a disabled form item control.\n- Fixed bug where firefox would generate a rectangle on elements produced after/before a cE=false elements.\n- Fixed bug with advlist plugin not switching list element format properly in some edge cases.\n- Fixed bug where col/rowspans wasn't correctly computed by the table plugin in some cases.\n- Fixed bug where the table plugin would thrown an error if object_resizing was disabled.\n- Fixed bug where some invalid markup would cause issues when running in XHTML mode. Patch contributed by Charles Bourasseau.\n- Fixed bug where the fullscreen class wouldn't be removed properly when closing dialogs.\n- Fixed bug where the PastePlainTextToggle event wasn't fired by the paste plugin when the state changed.\n- Fixed bug where table the row type wasn't properly updated in table row dialog. Patch contributed by Matthias Balmer.\n- Fixed bug where select all and cut wouldn't place caret focus back to the editor in WebKit. Patch contributed by Daniel Jalkut.\n- Fixed bug where applying cell/row properties to multiple cells/rows would reset other unchanged properties.\n- Fixed bug where some elements in the schema would have redundant/incorrect children.\n- Fixed bug where selector and target options would cause issues if used together.\n- Fixed bug where drag/drop of images from desktop on chrome would thrown an error.\n- Fixed bug where cut on WebKit/Blink wouldn't add an undo level.\n- Fixed bug where IE 11 would scroll to the cE=false elements when they where selected.\n- Fixed bug where keys like F5 wouldn't work when a cE=false element was selected.\n- Fixed bug where the undo manager wouldn't stop the typing state when commands where executed.\n- Fixed bug where unlink on wrapped links wouldn't work properly.\n- Fixed bug with drag/drop of images on WebKit where the image would be deleted form the source editor.\n- Fixed bug where the visual characters mode would be disabled when contents was extracted from the editor.\n- Fixed bug where some browsers would toggle of formats applied to the caret when clicking in the editor toolbar.\n- Fixed bug where the custom theme function wasn't working correctly.\n- Fixed bug where image option for custom buttons required you to have icon specified as well.\n- Fixed bug where the context menu and contextual toolbars would be visible at the same time and sometimes overlapping.\n- Fixed bug where the noneditable plugin would double wrap elements when using the noneditable_regexp option.\n- Fixed bug where tables would get padding instead of margin when you used the indent button.\n- Fixed bug where the charmap plugin wouldn't properly insert non breaking spaces.\n- Fixed bug where the color previews in color input boxes wasn't properly updated.\n- Fixed bug where the list items of previous lists wasn't merged in the right order.\n- Fixed bug where it wasn't possible to drag/drop inline-block cE=false elements on IE 11.\n- Fixed bug where some table cell merges would produce incorrect rowspan/colspan.\n- Fixed so the font size of the editor defaults to 14px instead of 11px this can be overridden by custom css.\n- Fixed so wordcount is debounced to reduce cpu hogging on larger texts.\n- Fixed so tinymce global gets properly exported as a module when used with some module bundlers.\n- Fixed so it's possible to specify what css properties you want to preview on specific formats.\n- Fixed so anchors are contentEditable=false while within the editor.\n- Fixed so selected contents gets wrapped in a inline code element by the codesample plugin.\n- Fixed so conditional comments gets properly stripped independent of case. Patch contributed by Georgii Dolzhykov.\n- Fixed so some escaped css sequences gets properly handled. Patch contributed by Georgii Dolzhykov.\n- Fixed so notifications with the same message doesn't get displayed at the same time.\n- Fixed so F10 can be used as an alternative key to focus to the toolbar.\n- Fixed various api documentation issues and typos.\n\n### Removed\n- Removed layer plugin since it wasn't really ported from 3.x and there doesn't seem to be much use for it.\n- Removed moxieplayer.swf from the media plugin since it wasn't used by the media plugin.\n- Removed format state from the advlist plugin to be more consistent with common word processors.\n\n## 4.4.3 - 2016-09-01\n\n### Fixed\n- Fixed bug where copy would produce an exception on Chrome.\n- Fixed bug where deleting lists on IE 11 would merge in correct text nodes.\n- Fixed bug where deleting partial lists with indentation wouldn't cause proper normalization.\n\n## 4.4.2 - 2016-08-25\n\n### Added\n- Added new importcss_exclusive option to disable unique selectors per group.\n- Added new group specific selector_converter option to importcss plugin.\n- Added new codesample_languages option to apply custom languages to codesample plugin.\n- Added new codesample_dialog_width/codesample_dialog_height options.\n\n### Fixed\n- Fixed bug where fullscreen button had an incorrect keyboard shortcut.\n- Fixed bug where backspace/delete wouldn't work correctly from a block to a cE=false element.\n- Fixed bug where smartpaste wasn't detecting links with special characters in them like tilde.\n- Fixed bug where the editor wouldn't get proper focus if you clicked on a cE=false element.\n- Fixed bug where it wasn't possible to copy/paste table rows that had merged cells.\n- Fixed bug where merging cells could some times produce invalid col/rowspan attibute values.\n- Fixed bug where getBody would sometimes thrown an exception now it just returns null if the iframe is clobbered.\n- Fixed bug where drag/drop of cE=false element wasn't properly constrained to viewport.\n- Fixed bug where contextmenu on Mac would collapse any selection to a caret.\n- Fixed bug where rtl mode wasn't rendered properly when loading a language pack with the rtl flag.\n- Fixed bug where Kamer word bounderies would be stripped from contents.\n- Fixed bug where lists would sometimes render two dots or numbers on the same line.\n- Fixed bug where the skin_url wasn't used by the inlite theme.\n- Fixed so data attributes are ignored when comparing formats in the formatter.\n- Fixed so it's possible to disable inline toolbars in the inlite theme.\n- Fixed so template dialog gets resized if it doesn't fit the window viewport.\n\n## 4.4.1 - 2016-07-26\n\n### Added\n- Added smart_paste option to paste plugin to allow disabling the paste behavior if needed.\n\n### Fixed\n- Fixed bug where png urls wasn't properly detected by the smart paste logic.\n- Fixed bug where the element path wasn't working properly when multiple editor instances where used.\n- Fixed bug with creating lists out of multiple paragraphs would just create one list item instead of multiple.\n- Fixed bug where scroll position wasn't properly handled by the inlite theme to place the toolbar properly.\n- Fixed bug where multiple instances of the editor using the inlite theme didn't render the toolbar properly.\n- Fixed bug where the shortcut label for fullscreen mode didn't match the actual shortcut key.\n- Fixed bug where it wasn't possible to select cE=false blocks using touch devices on for example iOS.\n- Fixed bug where it was possible to select the child image within a cE=false on IE 11.\n- Fixed so inserts of html containing lists doesn't merge with any existing lists unless it's a paste operation.\n\n## 4.4.0 - 2016-06-30\n\n### Added\n- Added new inlite theme this is a more lightweight inline UI.\n- Added smarter paste logic that auto detects urls in the clipboard and inserts images/links based on that.\n- Added a better image resize algorithm for better image quality in the imagetools plugin.\n\n### Fixed\n- Fixed bug where it wasn't possible to drag/dropping cE=false elements on FF.\n- Fixed bug where backspace/delete before/after a cE=false block would produce a new paragraph.\n- Fixed bug where list style type css property wasn't preserved when indenting lists.\n- Fixed bug where merging of lists where done even if the list style type was different.\n- Fixed bug where the image_dataimg_filter function wasn't used when pasting images.\n- Fixed bug where nested editable within a non editable element would cause scroll on focus in Chrome.\n- Fixed so invalid targets for inline mode is blocked on initialization. We only support elements that can have children.\n\n## 4.3.13 - 2016-06-08\n\n### Added\n- Added characters with a diacritical mark to charmap plugin. Patch contributed by Dominik Schilling.\n- Added better error handling if the image proxy service would produce errors.\n\n### Fixed\n- Fixed issue with pasting list items into list items would produce nested list rather than a merged list.\n- Fixed bug where table selection could get stuck in selection mode for inline editors.\n- Fixed bug where it was possible to place the caret inside the resize grid elements.\n- Fixed bug where it wasn't possible to place in elements horizontally adjacent cE=false blocks.\n- Fixed bug where multiple notifications wouldn't be properly placed on screen.\n- Fixed bug where multiple editor instance of the same id could be produces in some specific integrations.\n\n## 4.3.12 - 2016-05-10\n\n### Fixed\n- Fixed bug where focus calls couldn't be made inside the editors PostRender event handler.\n- Fixed bug where some translations wouldn't work as expected due to a bug in editor.translate.\n- Fixed bug where the node change event could fire with a node out side the root of the editor.\n- Fixed bug where Chrome wouldn't properly present the keyboard paste clipboard details when paste was clicked.\n- Fixed bug where merged cells in tables couldn't be selected from right to left.\n- Fixed bug where insert row wouldn't properly update a merged cells rowspan property.\n- Fixed bug where the color input boxes preview field wasn't properly set on initialization.\n- Fixed bug where IME composition inside table cells wouldn't work as expected on IE 11.\n- Fixed so all shadow dom support is under and experimental flag due to flaky browser support.\n\n## 4.3.11 - 2016-04-25\n\n### Fixed\n- Fixed bug where it wasn't possible to insert empty blocks though the API unless they where padded.\n- Fixed bug where you couldn't type the Euro character on Windows.\n- Fixed bug where backspace/delete from a cE=false element to a text block didn't work properly.\n- Fixed bug where the text color default grid would render incorrectly.\n- Fixed bug where the codesample plugin wouldn't load the css in the editor for multiple editors.\n- Fixed so the codesample plugin textarea gets focused by default.\n\n## 4.3.10 - 2016-04-12\n\n### Fixed\n- Fixed bug where the key \"y\" on WebKit couldn't be entered due to conflict with keycode for F10 on keypress.\n\n## 4.3.9 - 2016-04-12\n\n### Added\n- Added support for focusing the contextual toolbars using keyboard.\n- Added keyboard support for slider UI controls. You can no increase/decrease using arrow keys.\n- Added url pattern matching for Dailymotion to media plugin. Patch contributed by Bertrand Darbon.\n- Added body_class to template plugin preview. Patch contributed by Milen Petrinski.\n- Added options to better override textcolor pickers with custom colors. Patch contributed by Xavier Boubert.\n- Added visual arrows to inline contextual toolbars so that they point to the element being active.\n\n### Changed\n- Changed the Meta+Shift+F shortcut to Ctrl+Shift+F since Czech, Slovak, Polish languages used the first one for input.\n\n### Fixed\n- Fixed so toolbars for tables or other larger elements get better positioned below the scrollable viewport.\n- Fixed bug where it was possible to click links inside cE=false blocks.\n- Fixed bug where event targets wasn't properly handled in Safari Technical Preview.\n- Fixed bug where drag/drop text in FF 45 would make the editor caret invisible.\n- Fixed bug where the remove state wasn't properly set on editor instances when detected as clobbered.\n- Fixed bug where offscreen selection of some cE=false elements would render onscreen. Patch contributed by Steven Bufton\n- Fixed bug where enter would clone styles out side the root on editors inside a span. Patch contributed by ChristophKaser.\n- Fixed bug where drag/drop of images into the editor didn't work correctly in FF.\n- Fixed so the first item in panels for the imagetools dialog gets proper keyboard focus.\n\n## 4.3.8 - 2016-03-15\n\n### Fixed\n- Fixed bug where inserting HR at the end of a block element would produce an extra empty block.\n- Fixed bug where links would be clickable when readonly mode was enabled.\n- Fixed bug where the formatter would normalize to the wrong node on very specific content.\n- Fixed bug where some nested list items couldn't be indented properly.\n- Fixed bug where links where clickable in the preview dialog.\n- Fixed so the alt attribute doesn't get padded with an empty value by default.\n- Fixed so nested alignment works more correctly. You will now alter the alignment to the closest block parent.\n\n## 4.3.7 - 2016-03-02\n\n### Fixed\n- Fixed bug where incorrect icons would be rendered for imagetools edit and color levels.\n- Fixed bug where navigation using arrow keys inside a SelectBox didn't move up/down.\n- Fixed bug where the visualblocks plugin would render borders round internal UI elements.\n\n## 4.3.6 - 2016-03-01\n\n### Added\n- Added new paste_remember_plaintext_info option to allow a global disable of the plain text mode notification.\n- Added new PastePlainTextToggle event that fires when plain text mode toggles on/off.\n\n### Fixed\n- Fixed bug where it wasn't possible to select media elements since the drag logic would snap it to mouse cursor.\n- Fixed bug where it was hard to place the caret inside nested cE=true elements when the outer cE=false element was focused.\n- Fixed bug where editors wouldn't properly initialize if both selector and mode where used.\n- Fixed bug where IME input inside table cells would switch the IME off.\n- Fixed bug where selection inside the first table cell would cause the whole table cell to get selected.\n- Fixed bug where error handling of images being uploaded wouldn't properly handle faulty statuses.\n- Fixed bug where inserting contents before a HR would cause an exception to be thrown.\n- Fixed bug where copy/paste of Excel data would be inserted as an image.\n- Fixed caret position issues with copy/paste of inline block cE=false elements.\n- Fixed issues with various menu item focus bugs in Chrome. Where the focused menu bar item wasn't properly blurred.\n- Fixed so the notifications have a solid background since it would be hard to read if there where text under it.\n- Fixed so notifications gets animated similar to the ones used by dialogs.\n- Fixed so larger images that gets pasted is handled better.\n- Fixed so the window close button is more uniform on various platform and also increased it's hit area.\n\n## 4.3.5 - 2016-02-11\n\nNpm version bump due to package not being fully updated.\n\n## 4.3.4 - 2016-02-11\n\n### Added\n- Added new OpenWindow/CloseWindow events that gets fired when windows open/close.\n- Added new NewCell/NewRow events that gets fired when table cells/rows are created.\n- Added new Promise return value to tinymce.init makes it easier to handle initialization.\n\n### Fixed\n- Fixed various bugs with drag/drop of contentEditable:false elements.\n- Fixed bug where deleting of very specific nested list items would result in an odd list.\n- Fixed bug where lists would get merged with adjacent lists outside the editable inline root.\n- Fixed bug where MS Edge would crash when closing a dialog then clicking a menu item.\n- Fixed bug where table cell selection would add undo levels.\n- Fixed bug where table cell selection wasn't removed when inline editor where removed.\n- Fixed bug where table cell selection wouldn't work properly on nested tables.\n- Fixed bug where table merge menu would be available when merging between thead and tbody.\n- Fixed bug where table row/column resize wouldn't get properly removed when the editor was removed.\n- Fixed bug where Chrome would scroll to the editor if there where a empty hash value in document url.\n- Fixed bug where the cache suffix wouldn't work correctly with the importcss plugin.\n- Fixed bug where selection wouldn't work properly on MS Edge on Windows Phone 10.\n- Fixed so adjacent pre blocks gets joined into one pre block since that seems like the user intent.\n- Fixed so events gets properly dispatched in shadow dom. Patch provided by Nazar Mokrynskyi.\n\n### Removed\n- Removed the jQuery version the jQuery plugin is now moved into the main package.\n- Removed jscs from build process since eslint can now handle code style checking.\n\n## 4.3.3 - 2016-01-14\n\n### Added\n- Added new table_resize_bars configuration setting.  This setting allows you to disable the table resize bars.\n- Added new beforeInitialize event to tinymce.util.XHR lets you modify XHR properties before open. Patch contributed by Brent Clintel.\n- Added new autolink_pattern setting to autolink plugin. Enables you to override the default autolink formats. Patch contributed by Ben Tiedt.\n- Added new charmap option that lets you override the default charmap of the charmap plugin.\n- Added new charmap_append option that lets you add new characters to the default charmap of the charmap plugin.\n- Added new insertCustomChar event that gets fired when a character is inserted by the charmap plugin.\n\n### Fixed\n- Fixed bug where table cells started with a superfluous &nbsp; in IE10+.\n- Fixed bug where table plugin would retain all BR tags when cells were merged.\n- Fixed bug where media plugin would strip underscores from youtube urls.\n- Fixed bug where IME input would fail on IE 11 if you typed within a table.\n- Fixed bug where double click selection of a word would remove the space before the word on insert contents.\n- Fixed bug where table plugin would produce exceptions when hovering tables with invalid structure.\n- Fixed bug where fullscreen wouldn't scroll back to it's original position when untoggled.\n- Fixed so the template plugins templates setting can be a function that gets a callback that can provide templates.\n\n## 4.3.2 - 2015-12-14\n\n### Fixed\n- Fixed bug where the resize bars for table cells were not affected by the object_resizing property.\n- Fixed bug where the contextual table toolbar would appear incorrectly if TinyMCE was initialized inline inside a table.\n- Fixed bug where resizing table cells did not fire a node change event or add an undo level.\n- Fixed bug where double click selection of text on IE 11 wouldn't work properly.\n- Fixed bug where codesample plugin would incorrectly produce br elements inside code elements.\n- Fixed bug where media plugin would strip dashes from youtube urls.\n- Fixed bug where it was possible to move the caret into the table resize bars.\n- Fixed bug where drag/drop into a cE=false element was possible on IE.\n\n## 4.3.1 - 2015-11-30\n\n### Fixed\n- Fixed so it's possible to disable the table inline toolbar by setting it to false or an empty string.\n- Fixed bug where it wasn't possible to resize some tables using the drag handles.\n- Fixed bug where unique id:s would clash for multiple editor instances and cE=false selections.\n- Fixed bug where the same plugin could be initialized multiple times.\n- Fixed bug where the table inline toolbars would be displayed at the same time as the image toolbars.\n- Fixed bug where the table selection rect wouldn't be removed when selecting another control element.\n\n## 4.3.0 - 2015-11-23\n\n### Added\n- Added new table column/row resize support. Makes it a lot more easy to resize the columns/rows in a table.\n- Added new table inline toolbar. Makes it easier to for example add new rows or columns to a table.\n- Added new notification API. Lets you display floating notifications to the end user.\n- Added new codesample plugin that lets you insert syntax highlighted pre elements into the editor.\n- Added new image_caption to images. Lets you create images with captions using a HTML5 figure/figcaption elements.\n- Added new live previews of embeded videos. Lets you play the video right inside the editor.\n- Added new setDirty method and \"dirty\" event to the editor. Makes it easier to track the dirty state change.\n- Added new setMode method to Editor instances that lets you dynamically switch between design/readonly.\n- Added new core support for contentEditable=false elements within the editor overrides the browsers broken behavior.\n\n### Changed\n- Rewrote the noneditable plugin to use the new contentEditable false core logic.\n\n### Fixed\n- Fixed so the dirty state doesn't set to false automatically when the undo index is set to 0.\n- Fixed the Selection.placeCaretAt so it works better on IE when the coordinate is between paragraphs.\n- Fixed bug where data-mce-bogus=\"all\" element contents where counted by the word count plugin.\n- Fixed bug where contentEditable=false elements would be indented by the indent buttons.\n- Fixed bug where images within contentEditable=false would be selected in WebKit on mouse click.\n- Fixed bug in DOMUntils split method where the replacement parameter wouldn't work on specific cases.\n- Fixed bug where the importcss plugin would import classes from the skin content css file.\n- Fixed so all button variants have a wrapping span for it's text to make it easier to skin.\n- Fixed so it's easier to exit pre block using the arrow keys.\n- Fixed bug where listboxes with fix widths didn't render correctly.\n\n## 4.2.8 - 2015-11-13\n\n### Fixed\n- Fixed bug where it was possible to delete tables as the inline root element if all columns where selected.\n- Fixed bug where the UI buttons active state wasn't properly updated due to recent refactoring of that logic.\n\n## 4.2.7 - 2015-10-27\n\n### Fixed\n- Fixed bug where backspace/delete would remove all formats on the last paragraph character in WebKit/Blink.\n- Fixed bug where backspace within a inline format element with a bogus caret container would move the caret.\n- Fixed bug where backspace/delete on selected table cells wouldn't add an undo level.\n- Fixed bug where script tags embedded within the editor could sometimes get a mce- prefix prepended to them\n- Fixed bug where validate: false option could produce an error to be thrown from the Serialization step.\n- Fixed bug where inline editing of a table as the root element could let the user delete that table.\n- Fixed bug where inline editing of a table as the root element wouldn't properly handle enter key.\n- Fixed bug where inline editing of a table as the root element would normalize the selection incorrectly.\n- Fixed bug where inline editing of a list as the root element could let the user delete that list.\n- Fixed bug where inline editing of a list as the root element could let the user split that list.\n- Fixed bug where resize handles would be rendered on editable root elements such as table.\n\n## 4.2.6 - 2015-09-28\n\n### Added\n- Added capability to set request headers when using XHRs.\n- Added capability to upload local images automatically default delay is set to 30 seconds after editing images.\n- Added commands ids mceEditImage, mceAchor and mceMedia to be avaiable from execCommand.\n- Added Edge browser to saucelabs grunt task. Patch contributed by John-David Dalton.\n\n### Fixed\n- Fixed bug where blob uris not produced by tinymce would produce HTML invalid markup.\n- Fixed bug where selection of contents of a nearly empty editor in Edge would sometimes fail.\n- Fixed bug where color styles woudln't be retained on copy/paste in Blink/Webkit.\n- Fixed bug where the table plugin would throw an error when inserting rows after a child table.\n- Fixed bug where the template plugin wouldn't handle functions as variable replacements.\n- Fixed bug where undo/redo sometimes wouldn't work properly when applying formatting collapsed ranges.\n- Fixed bug where shift+delete wouldn't do a cut operation on Blink/WebKit.\n- Fixed bug where cut action wouldn't properly store the before selection bookmark for the undo level.\n- Fixed bug where backspace in side an empty list element on IE would loose editor focus.\n- Fixed bug where the save plugin wouldn't enable the buttons when a change occurred.\n- Fixed bug where Edge wouldn't initialize the editor if a document.domain was specified.\n- Fixed bug where enter key before nested images would sometimes not properly expand the previous block.\n- Fixed bug where the inline toolbars wouldn't get properly hidden when blurring the editor instance.\n- Fixed bug where Edge would paste Chinese characters on some Windows 10 installations.\n- Fixed bug where IME would loose focus on IE 11 due to the double trailing br bug fix.\n- Fixed bug where the proxy url in imagetools was incorrect. Patch contributed by Wong Ho Wang.\n\n## 4.2.5 - 2015-08-31\n\n### Added\n- Added fullscreen capability to embedded youtube and vimeo videos.\n\n### Fixed\n- Fixed bug where the uploadImages call didn't work on IE 10.\n- Fixed bug where image place holders would be uploaded by uploadImages call.\n- Fixed bug where images marked with bogus would be uploaded by the uploadImages call.\n- Fixed bug where multiple calls to uploadImages would result in decreased performance.\n- Fixed bug where pagebreaks were editable to imagetools patch contributed by Rasmus Wallin.\n- Fixed bug where the element path could cause too much recursion exception.\n- Fixed bug for domains containing \".min\". Patch contributed by Loïc Février.\n- Fixed so validation of external links to accept a number after www. Patch contributed by Victor Carvalho.\n- Fixed so the charmap is exposed though execCommand. Patch contributed by Matthew Will.\n- Fixed so that the image uploads are concurrent for improved performance.\n- Fixed various grammar problems in inline documentation. Patches provided by nikolas.\n\n## 4.2.4 - 2015-08-17\n\n### Added\n- Added picture as a valid element to the HTML 5 schema. Patch contributed by Adam Taylor.\n\n### Fixed\n- Fixed bug where contents would be duplicated on drag/drop within the same editor.\n- Fixed bug where floating/alignment of images on Edge wouldn't work properly.\n- Fixed bug where it wasn't possible to drag images on IE 11.\n- Fixed bug where image selection on Edge would sometimes fail.\n- Fixed bug where contextual toolbars icons wasn't rendered properly when using the toolbar_items_size.\n- Fixed bug where searchreplace dialog doesn't get prefilled with the selected text.\n- Fixed bug where fragmented matches wouldn't get properly replaced by the searchreplace plugin.\n- Fixed bug where enter key wouldn't place the caret if was after a trailing space within an inline element.\n- Fixed bug where the autolink plugin could produce multiple links for the same text on Gecko.\n- Fixed bug where EditorUpload could sometimes throw an exception if the blob wasn't found.\n- Fixed xss issues with media plugin not properly filtering out some script attributes.\n\n## 4.2.3 - 2015-07-30\n\n### Fixed\n- Fixed bug where image selection wasn't possible on Edge due to incompatible setBaseAndExtend API.\n- Fixed bug where image blobs urls where not properly destroyed by the imagetools plugin.\n- Fixed bug where keyboard shortcuts wasn't working correctly on IE 8.\n- Fixed skin issue where the borders of panels where not visible on IE 8.\n\n## 4.2.2 - 2015-07-22\n\n### Fixed\n- Fixed bug where float panels were not being hidden on inline editor blur when fixed_toolbar_container config option was in use.\n- Fixed bug where combobox states wasn't properly updated if contents where updated without keyboard.\n- Fixed bug where pasting into textbox or combobox would move the caret to the end of text.\n- Fixed bug where removal of bogus span elements before block elements would remove whitespace between nodes.\n- Fixed bug where repositioning of inline toolbars where async and producing errors if the editor was removed from DOM to early. Patch by iseulde.\n- Fixed bug where element path wasn't working correctly. Patch contributed by iseulde.\n- Fixed bug where menus wasn't rendered correctly when custom images where added to a menu. Patch contributed by Naim Hammadi.\n\n## 4.2.1 - 2015-06-29\n\n### Fixed\n- Fixed bug where back/forward buttons in the browser would render blob images as broken images.\n- Fixed bug where Firefox would throw regexp to big error when replacing huge base64 chunks.\n- Fixed bug rendering issues with resize and context toolbars not being placed properly until next animation frame.\n- Fixed bug where the rendering of the image while cropping would some times not be centered correctly.\n- Fixed bug where listbox items with submenus would me selected as active.\n- Fixed bug where context menu where throwing an error when rendering.\n- Fixed bug where resize both option wasn't working due to resent addClass API change. Patch contributed by Jogai.\n- Fixed bug where a hideAll call for container rendered inline toolbars would throw an error.\n- Fixed bug where onclick event handler on combobox could cause issues if element.id was a function by some polluting libraries.\n- Fixed bug where listboxes wouldn't get proper selected sub menu item when using link_list or image_list.\n- Fixed so the UI controls are as wide as 4.1.x to avoid wrapping controls in toolbars.\n- Fixed so the imagetools dialog is adaptive for smaller screen sizes.\n\n## 4.2.0 - 2015-06-25\n\n### Added\n- Added new flat default skin to make the UI more modern.\n- Added new imagetools plugin, lets you crop/resize and apply filters to images.\n- Added new contextual toolbars support to the API lets you add floating toolbars for specific CSS selectors.\n- Added new promise feature fill as tinymce.util.Promise.\n- Added new built in image upload feature lets you upload any base64 encoded image within the editor as files.\n\n### Fixed\n- Fixed bug where resize handles would appear in the right position in the wrong editor when switching between resizable content in different inline editors.\n- Fixed bug where tables would not be inserted in inline mode due to previous float panel fix.\n- Fixed bug where floating panels would remain open when focus was lost on inline editors.\n- Fixed bug where cut command on Chrome would thrown a browser security exception.\n- Fixed bug where IE 11 sometimes would report an incorrect size for images in the image dialog.\n- Fixed bug where it wasn't possible to remove inline formatting at the end of block elements.\n- Fixed bug where it wasn't possible to delete table cell contents when cell selection was vertical.\n- Fixed bug where table cell wasn't emptied from block elements if delete/backspace where pressed in empty cell.\n- Fixed bug where cmd+shift+arrow didn't work correctly on Firefox mac when selecting to start/end of line.\n- Fixed bug where removal of bogus elements would sometimes remove whitespace between nodes.\n- Fixed bug where the resize handles wasn't updated when the main window was resized.\n- Fixed so script elements gets removed by default to prevent possible XSS issues in default config implementations.\n- Fixed so the UI doesn't need manual reflows when using non native layout managers.\n- Fixed so base64 encoded images doesn't slow down the editor on modern browsers while editing.\n- Fixed so all UI elements uses touch events to improve mobile device support.\n- Removed the touch click quirks patch for iOS since it did more harm than good.\n- Removed the non proportional resize handles since. Unproportional resize can still be done by holding the shift key.\n\n## 4.1.10 - 2015-05-05\n\n### Fixed\n- Fixed bug where plugins loaded with compat3x would sometimes throw errors when loading using the jQuery version.\n- Fixed bug where extra empty paragraphs would get deleted in WebKit/Blink due to recent Quriks fix.\n- Fixed bug where the editor wouldn't work properly on IE 12 due to some required browser sniffing.\n- Fixed bug where formatting shortcut keys where interfering with Mac OS X screenshot keys.\n- Fixed bug where the caret wouldn't move to the next/previous line boundary on Cmd+Left/Right on Gecko.\n- Fixed bug where it wasn't possible to remove formats from very specific nested contents.\n- Fixed bug where undo levels wasn't produced when typing letters using the shift or alt+ctrl modifiers.\n- Fixed bug where the dirty state wasn't properly updated when typing using the shift or alt+ctrl modifiers.\n- Fixed bug where an error would be thrown if an autofocused editor was destroyed quickly after its initialization. Patch provided by thorn0.\n- Fixed issue with dirty state not being properly updated on redo operation.\n- Fixed issue with entity decoder not handling incorrectly written numeric entities.\n- Fixed issue where some PI element values wouldn't be properly encoded.\n\n## 4.1.9 - 2015-03-10\n\n### Fixed\n- Fixed bug where indentation wouldn't work properly for non list elements.\n- Fixed bug with image plugin not pulling the image dimensions out correctly if a custom document_base_url was used.\n- Fixed bug where ctrl+alt+[1-9] would conflict with the AltGr+[1-9] on Windows. New shortcuts is ctrl+shift+[1-9].\n- Fixed bug with removing formatting on nodes in inline mode would sometimes include nodes outside the editor body.\n- Fixed bug where extra nbsp:s would be inserted when you replaced a word surrounded by spaces using insertContent.\n- Fixed bug with pasting from Google Docs would produce extra strong elements and line feeds.\n\n## 4.1.8 - 2015-03-05\n\n### Added\n- Added new html5 sizes attribute to img elements used together with srcset.\n- Added new elementpath option that makes it possible to disable the element path but keep the statusbar.\n- Added new option table_style_by_css for the table plugin to set table styling with css rather than table attributes.\n- Added new link_assume_external_targets option to prompt the user to prepend http:// prefix if the supplied link does not contain a protocol prefix.\n- Added new image_prepend_url option to allow a custom base path/url to be added to images.\n- Added new table_appearance_options option to make it possible to disable some options.\n- Added new image_title option to make it possible to alter the title of the image, disabled by default.\n\n### Fixed\n- Fixed bug where selection starting from out side of the body wouldn't produce a proper selection range on IE 11.\n- Fixed bug where pressing enter twice before a table moves the cursor in the table and causes a javascript error.\n- Fixed bug where advanced image styles were not respected.\n- Fixed bug where the less common Shift+Delete didn't produce a proper cut operation on WebKit browsers.\n- Fixed bug where image/media size constrain logic would produce NaN when handling non number values.\n- Fixed bug where internal classes where removed by the removeformat command.\n- Fixed bug with creating links table cell contents with a specific selection would throw a exceptions on WebKit/Blink.\n- Fixed bug where valid_classes option didn't work as expected according to docs. Patch provided by thorn0.\n- Fixed bug where jQuery plugin would patch the internal methods multiple times. Patch provided by Drew Martin.\n- Fixed bug where backspace key wouldn't delete the current selection of newly formatted content.\n- Fixed bug where type over of inline formatting elements wouldn't properly keep the format on WebKit/Blink.\n- Fixed bug where selection needed to be properly normalized on modern IE versions.\n- Fixed bug where Command+Backspace didn't properly delete the whole line of text but the previous word.\n- Fixed bug where UI active states wheren't properly updated on IE if you placed caret within the current range.\n- Fixed bug where delete/backspace on WebKit/Blink would remove span elements created by the user.\n- Fixed bug where delete/backspace would produce incorrect results when deleting between two text blocks with br elements.\n- Fixed bug where captions where removed when pasting from MS Office.\n- Fixed bug where lists plugin wouldn't properly remove fully selected nested lists.\n- Fixed bug where the ttf font used for icons would throw an warning message on Gecko on Mac OS X.\n- Fixed a bug where applying a color to text did not update the undo/redo history.\n- Fixed so shy entities gets displayed when using the visualchars plugin.\n- Fixed so removeformat removes ins/del by default since these might be used for strikethough.\n- Fixed so multiple language packs can be loaded and added to the global I18n data structure.\n- Fixed so transparent color selection gets treated as a normal color selection. Patch contributed by Alexander Hofbauer.\n- Fixed so it's possible to disable autoresize_overflow_padding, autoresize_bottom_margin options by setting them to false.\n- Fixed so the charmap plugin shows the description of the character in the dialog. Patch contributed by Jelle Hissink.\n- Removed address from the default list of block formats since it tends to be missused.\n- Fixed so the pre block format is called preformatted to make it more verbose.\n- Fixed so it's possible to context scope translation strings this isn't needed most of the time.\n- Fixed so the max length of the width/height input fields of the media dialog is 5 instead of 3.\n- Fixed so drag/dropped contents gets properly processed by paste plugin since it's basically a paste. Patch contributed by Greg Fairbanks.\n- Fixed so shortcut keys for headers is ctrl+alt+[1-9] instead of ctrl+[1-9] since these are for switching tabs in the browsers.\n- Fixed so \"u\" doesn't get converted into a span element by the legacy input filter. Since this is now a valid HTML5 element.\n- Fixed font families in order to provide appropriate web-safe fonts.\n\n## 4.1.7 - 2014-11-27\n\n### Added\n- Added HTML5 schema support for srcset, source and picture. Patch contributed by mattheu.\n- Added new cache_suffix setting to enable cache busting by producing unique urls.\n- Added new paste_convert_word_fake_lists option to enable users to disable the fake lists convert logic.\n\n### Fixed\n- Fixed so advlist style changes adds undo levels for each change.\n- Fixed bug where WebKit would sometimes produce an exception when the autolink plugin where looking for URLs.\n- Fixed bug where IE 7 wouldn't be rendered properly due to aggressive css compression.\n- Fixed bug where DomQuery wouldn't accept window as constructor element.\n- Fixed bug where the color picker in 3.x dialogs wouldn't work properly. Patch contributed by Callidior.\n- Fixed bug where the image plugin wouldn't respect the document_base_url.\n- Fixed bug where the jQuery plugin would fail to append to elements named array prototype names.\n\n## 4.1.6 - 2014-10-08\n\n### Changed\n- Replaced jake with grunt since it is more mainstream and has better plugin support.\n\n### Fixed\n- Fixed bug with clicking on the scrollbar of the iframe would cause a JS error to be thrown.\n- Fixed bug where null would produce an exception if you passed it to selection.setRng.\n- Fixed bug where Ctrl/Cmd+Tab would indent the current list item if you switched tabs in the browser.\n- Fixed bug where pasting empty cells from Excel would result in a broken table.\n- Fixed bug where it wasn't possible to switch back to default list style type.\n- Fixed issue where the select all quirk fix would fire for other modifiers than Ctrl/Cmd combinations.\n\n\n## 4.1.5 - 2014-09-09\n\n### Fixed\n- Fixed bug where sometimes the resize rectangles wouldn't properly render on images on WebKit/Blink.\n- Fixed bug in list plugin where delete/backspace would merge empty LI elements in lists incorrectly.\n- Fixed bug where empty list elements would result in empty LI elements without it's parent container.\n- Fixed bug where backspace in empty caret formatted element could produce an type error exception of Gecko.\n- Fixed bug where lists pasted from word with a custom start index above 9 wouldn't be properly handled.\n- Fixed bug where tabfocus plugin would tab out of the editor instance even if the default action was prevented.\n- Fixed bug where tabfocus wouldn't tab properly to other adjacent editor instances.\n- Fixed bug where the DOMUtils setStyles wouldn't properly removed or update the data-mce-style attribute.\n- Fixed bug where dialog select boxes would be placed incorrectly if document.body wasn't statically positioned.\n- Fixed bug where pasting would sometimes scroll to the top of page if the user was using the autoresize plugin.\n- Fixed bug where caret wouldn't be properly rendered by Chrome when clicking on the iframes documentElement.\n- Fixed so custom images for menubutton/splitbutton can be provided. Patch contributed by Naim Hammadi.\n- Fixed so the default action of windows closing can be prevented by blocking the default action of the close event.\n- Fixed so nodeChange and focus of the editor isn't automatically performed when opening sub dialogs.\n\n## 4.1.4 - 2014-08-21\n\n### Added\n- Added new media_filter_html option to media plugin that blocks any conditional comments, scripts etc within a video element.\n- Added new content_security_policy option allows you to set custom policy for iframe contents. Patch contributed by Francois Chagnon.\n\n### Fixed\n- Fixed bug where activate/deactivate events wasn't firing properly when switching between editors.\n- Fixed bug where placing the caret on iOS was difficult due to a WebKit bug with touch events.\n- Fixed bug where the resize helper wouldn't render properly on older IE versions.\n- Fixed bug where resizing images inside tables on older IE versions would sometimes fail depending mouse position.\n- Fixed bug where editor.insertContent would produce an exception when inserting select/option elements.\n- Fixed bug where extra empty paragraphs would be produced if block elements where inserted inside span elements.\n- Fixed bug where the spellchecker menu item wouldn't be properly checked if spell checking was started before it was rendered.\n- Fixed bug where the DomQuery filter function wouldn't remove non elements from collection.\n- Fixed bug where document with custom document.domain wouldn't properly render the editor.\n- Fixed bug where IE 8 would throw exception when trying to enter invalid color values into colorboxes.\n- Fixed bug where undo manager could incorrectly add an extra undo level when custom resize handles was removed.\n- Fixed bug where it wouldn't be possible to alter cell properties properly on table cells on IE 8.\n- Fixed so the color picker button in table dialog isn't shown unless you include the colorpicker plugin or add your own custom color picker.\n- Fixed so activate/deactivate events fire when windowManager opens a window since.\n- Fixed so the table advtab options isn't separated by an underscore to normalize naming with image_advtab option.\n- Fixed so the table cell dialog has proper padding when the advanced tab in disabled.\n\n## 4.1.3 - 2014-07-29\n\n### Added\n- Added event binding logic to tinymce.util.XHR making it possible to override headers and settings before any request is made.\n\n### Fixed\n- Fixed bug where drag events wasn't fireing properly on older IE versions since the event handlers where bound to document.\n- Fixed bug where drag/dropping contents within the editor on IE would force the contents into plain text mode even if it was internal content.\n- Fixed bug where IE 7 wouldn't open menus properly due to a resize bug in the browser auto closing them immediately.\n- Fixed bug where the DOMUtils getPos logic wouldn't produce a valid coordinate inside the body if the body was positioned non static.\n- Fixed bug where the element path and format state wasn't properly updated if you had the wordcount plugin enabled.\n- Fixed bug where a comment at the beginning of source would produce an exception in the formatter logic.\n- Fixed bug where setAttrib/getAttrib on null would throw exception together with any hooked attributes like style.\n- Fixed bug where table sizes wasn't properly retained when copy/pasting on WebKit/Blink.\n- Fixed bug where WebKit/Blink would produce colors in RGB format instead of the forced HEX format when deleting contents.\n- Fixed bug where the width attribute wasn't updated on tables if you changed the size inside the table dialog.\n- Fixed bug where control selection wasn't properly handled when the caret was placed directly after an image.\n- Fixed bug where selecting the contents of table cells using the selection.select method wouldn't place the caret properly.\n- Fixed bug where the selection state for images wasn't removed when placing the caret right after an image on WebKit/Blink.\n- Fixed bug where all events wasn't properly unbound when and editor instance was removed or destroyed by some external innerHTML call.\n- Fixed bug where it wasn't possible or very hard to select images on iOS when the onscreen keyboard was visible.\n- Fixed so auto_focus can take a boolean argument this will auto focus the last initialized editor might be useful for single inits.\n- Fixed so word auto detect lists logic works better for faked lists that doesn't have specific markup.\n- Fixed so nodeChange gets fired on mouseup as it used to before 4.1.1 we optimized that event to fire less often.\n\n### Removed\n- Removed the finish menu item from spellchecker menu since it's redundant you can stop spellchecking by toggling menu item or button.\n\n## 4.1.2 - 2014-07-15\n\n### Added\n- Added offset/grep to DomQuery class works basically the same as it's jQuery equivalent.\n\n### Fixed\n- Fixed bug where backspace/delete or setContent with an empty string would remove header data when using the fullpage plugin.\n- Fixed bug where tinymce.remove with a selector not matching any editors would remove all editors.\n- Fixed bug where resizing of the editor didn't work since the theme was calling setStyles instead of setStyle.\n- Fixed bug where IE 7 would fail to append html fragments to iframe document when using DomQuery.\n- Fixed bug where the getStyle DOMUtils method would produce an exception if it was called with null as it's element.\n- Fixed bug where the paste plugin would remove the element if the none of the paste_webkit_styles rules matched the current style.\n- Fixed bug where contextmenu table items wouldn't work properly on IE since it would some times fire an incorrect selection change.\n- Fixed bug where the padding/border values wasn't used in the size calculation for the body size when using autoresize. Patch contributed by Matt Whelan.\n- Fixed bug where conditional word comments wouldn't be properly removed when pasting plain text.\n- Fixed bug where resizing would sometime fail on IE 11 when the mouseup occurred inside the resizable element.\n- Fixed so the iframe gets initialized without any inline event handlers for better CSP support. Patch contributed by Matt Whelan.\n- Fixed so the tinymce.dom.Sizzle is the latest version of sizzle this resolves the document context bug.\n\n## 4.1.1 - 2014-07-08\n\n### Fixed\n- Fixed bug where pasting plain text on some WebKit versions would result in an empty line.\n- Fixed bug where resizing images inside tables on IE 11 wouldn't work properly.\n- Fixed bug where IE 11 would sometimes throw \"Invalid argument\" exception when editor contents was set to an empty string.\n- Fixed bug where document.activeElement would throw exceptions on IE 9 when that element was hidden or removed from dom.\n- Fixed bug where WebKit/Blink sometimes produced br elements with the Apple-interchange-newline class.\n- Fixed bug where table cell selection wasn't properly removed when copy/pasting table cells.\n- Fixed bug where pasting nested list items from Word wouldn't produce proper semantic nested lists.\n- Fixed bug where right clicking using the contextmenu plugin on WebKit/Blink on Mac OS X would select the target current word or line.\n- Fixed bug where it wasn't possible to alter table cell properties on IE 8 using the context menu.\n- Fixed bug where the resize helper wouldn't be correctly positioned on older IE versions.\n- Fixed bug where fullpage plugin would produce an error if you didn't specify a doctype encoding.\n- Fixed bug where anchor plugin would get the name/id of the current element even if it wasn't anchor element.\n- Fixed bug where visual aids for tables wouldn't be properly disabled when changing the border size.\n- Fixed bug where some control selection events wasn't properly fired on older IE versions.\n- Fixed bug where table cell selection on older IE versions would prevent resizing of images.\n- Fixed bug with paste_data_images paste option not working properly on modern IE versions.\n- Fixed bug where custom elements with underscores in the name wasn't properly parsed/serialized.\n- Fixed bug where applying inline formats to nested list elements would produce an incorrect formatting result.\n- Fixed so it's possible to hide items from elements path by using preventDefault/stopPropagation.\n- Fixed so inline mode toolbar gets rendered right aligned if the editable element positioned to the documents right edge.\n- Fixed so empty inline elements inside empty block elements doesn't get removed if configured to be kept intact.\n- Fixed so DomQuery parentsUntil/prevUntil/nextUntil supports selectors/elements/filters etc.\n- Fixed so legacyoutput plugin overrides fontselect and fontsizeselect controls and handles font elements properly.\n\n## 4.1.0 - 2014-06-18\n\n### Added\n- Added new file_picker_callback option to replace the old file_browser_callback the latter will still work though.\n- Added new custom colors to textcolor plugin will be displayed if a color picker is provided also shows the latest colors.\n- Added new color_picker_callback option to enable you to add custom color pickers to the editor.\n- Added new advanced tabs to table/cell/row dialogs to enable you to select colors for border/background.\n- Added new colorpicker plugin that lets you select colors from a hsv color picker.\n- Added new tinymce.util.Color class to handle color parsing and converting.\n- Added new colorpicker UI widget element lets you add a hsv color picker to any form/window.\n- Added new textpattern plugin that allows you to use markdown like text patterns to format contents.\n- Added new resize helper element that shows the current width & height while resizing.\n- Added new \"once\" method to Editor and EventDispatcher enables since callback execution events.\n- Added new jQuery like class under tinymce.dom.DomQuery it's exposed on editor instances (editor.$) and globally under (tinymce.$).\n\n### Fixed\n- Fixed so the default resize method for images are proportional shift/ctrl can be used to make an unproportional size.\n- Fixed bug where the image_dimensions option of the image plugin would cause exceptions when it tried to update the size.\n- Fixed bug where table cell dialog class field wasn't properly updated when editing an a table cell with an existing class.\n- Fixed bug where Safari on Mac would produce webkit-fake-url for pasted images so these are now removed.\n- Fixed bug where the nodeChange event would get fired before the selection was changed when clicking inside the current selection range.\n- Fixed bug where valid_classes option would cause exception when it removed internal prefixed classes like mce-item-.\n- Fixed bug where backspace would cause navigation in IE 8 on an inline element and after a caret formatting was applied.\n- Fixed so placeholder images produced by the media plugin gets selected when inserted/edited.\n- Fixed so it's possible to drag in images when the paste_data_images option is enabled. Might be useful for mail clients.\n- Fixed so images doesn't get a width/height applied if the image_dimensions option is set to false useful for responsive contents.\n- Fixed so it's possible to pass in an optional arguments object for the nodeChanged function to be passed to all nodechange event listeners.\n- Fixed bug where media plugin embed code didn't update correctly.\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/README.md",
    "content": "# TinyMCE\n\nThe world's #1 open source rich text editor.\n\nUsed and trusted by millions of developers, TinyMCE is the world’s most customizable, scalable, and flexible rich text editor. We’ve helped launch the likes of Atlassian, Medium, Evernote (and lots more that we can’t tell you), by empowering them to create exceptional content and experiences for their users.\n\nWith more than 350M+ downloads every year, we’re also one of the most trusted enterprise-grade open source HTML editors on the internet. There’s currently more than 100M+ products worldwide, powered by Tiny. As a high powered WYSIWYG editor, TinyMCE is built to scale, designed to innovate, and thrives on delivering results to difficult edge-cases.\n\nYou can access a [full featured demo of TinyMCE](https://www.tiny.cloud/docs/tinymce/6/premium-full-featured/) in the docs on the TinyMCE website.\n\n<p align=\"center\">\n  <img alt=\"Screenshot of the TinyMCE Editor\" src=\"https://www.tiny.cloud/storage/github-readme-images/tinymce-editor-6x.png\"\\>\n</p>\n\n## Get started with TinyMCE\n\nGetting started with the TinyMCE rich text editor is easy, and for simple configurations can be done in less than 5 minutes.\n\n[TinyMCE Cloud Deployment Quick Start Guide](https://www.tiny.cloud/docs/tinymce/6/cloud-quick-start/)\n\n[TinyMCE Self-hosted Deployment Guide](https://www.tiny.cloud/docs/tinymce/6/npm-projects/)\n\nTinyMCE provides a range of configuration options that allow you to integrate it into your application. Start customizing with a [basic setup](https://www.tiny.cloud/docs/tinymce/6/basic-setup/).\n\nConfigure it for one of three modes of editing:\n\n- [TinyMCE classic editing mode](https://www.tiny.cloud/docs/tinymce/6/use-tinymce-classic/).\n- [TinyMCE inline editing mode](https://www.tiny.cloud/docs/tinymce/6/use-tinymce-inline/).\n- [TinyMCE distraction-free editing mode](https://www.tiny.cloud/docs/tinymce/6/use-tinymce-distraction-free/).\n\n## Features\n\n### Integration\n\nTinyMCE is easily integrated into your projects with the help of components such as:\n\n- [tinymce-react](https://github.com/tinymce/tinymce-react)\n- [tinymce-vue](https://github.com/tinymce/tinymce-vue)\n- [tinymce-angular](https://github.com/tinymce/tinymce-angular)\n\nWith over 29 integrations, and 400+ APIs, see the TinyMCE docs for a full list of editor [integrations](https://www.tiny.cloud/docs/tinymce/6/integrations/).\n\n### Customization\n\nIt is easy to [configure the UI](https://www.tiny.cloud/docs/tinymce/6/customize-ui/) of your rich text editor to match the design of your site, product or application. Due to its flexibility, you can [configure the editor](https://www.tiny.cloud/docs/tinymce/6/basic-setup/) with as much or as little functionality as you like, depending on your requirements.\n\nWith [50+ powerful plugins available](https://www.tiny.cloud/tinymce/features/), and content editable as the basis of TinyMCE, adding additional functionality is as simple as including a single line of code.\n\nRealizing the full power of most plugins requires only a few lines more.\n\n### Extensibility\n\nSometimes your editor requirements can be quite unique, and you need the freedom and flexibility to innovate. Thanks to TinyMCE being open source, you can view the source code and develop your own extensions for custom functionality to meet your own requirements.\n\nThe TinyMCE [API](https://www.tiny.cloud/docs/tinymce/6/apis/tinymce.root/) is exposed to make it easier for you to write custom functionality that fits within the existing framework of TinyMCE [UI components](https://www.tiny.cloud/docs/tinymce/6/custom-ui-components/).\n\n### Extended Features and Support\n\nFor the professional software teams that require more in-depth efficiency, compliance or collaborative features built to enterprise-grade standards, please [get in touch with our team](https://www.tiny.cloud/contact/).\n\nTiny also offers dedicated SLAs and support for professional development teams.\n\n## Compiling and contributing\n\nIn 2019 the decision was made to transition our codebase to a monorepo. For information on compiling and contributing, see: [contribution guidelines](https://github.com/tinymce/tinymce/blob/master/CONTRIBUTING.md).\n\nAs an open source product, we encourage and support the active development of our software.\n\n## Want more information?\n\nVisit the [TinyMCE website](https://tiny.cloud/) and check out the [TinyMCE documentation](https://www.tiny.cloud/docs/).\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/bower.json",
    "content": "{\n\t\"name\": \"tinymce\",\n\t\"description\": \"Web based JavaScript HTML WYSIWYG editor control.\",\n\t\"license\": \"MIT\",\n\t\"keywords\": [\n\t\t\"wysiwyg\",\n\t\t\"tinymce\",\n\t\t\"richtext\",\n\t\t\"javascript\",\n\t\t\"html\",\n\t\t\"text\",\n\t\t\"rich editor\",\n\t\t\"rich text editor\",\n\t\t\"rte\",\n\t\t\"rich text\",\n\t\t\"contenteditable\",\n\t\t\"editing\"\n\t],\n\t\"homepage\": \"https://www.tiny.cloud/\",\n\t\"ignore\": [\n\t\t\"README.md\",\n\t\t\"composer.json\",\n\t\t\"package.json\",\n\t\t\".npmignore\",\n\t\t\"CHANGELOG.md\"\n\t]\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/composer.json",
    "content": "{\n\t\"name\": \"tinymce/tinymce\",\n\t\"version\": \"6.3.2\",\n\t\"description\": \"Web based JavaScript HTML WYSIWYG editor control.\",\n\t\"license\": [\n\t\t\"MIT-only\"\n\t],\n\t\"keywords\": [\n\t\t\"wysiwyg\",\n\t\t\"tinymce\",\n\t\t\"richtext\",\n\t\t\"javascript\",\n\t\t\"html\",\n\t\t\"text\",\n\t\t\"rich editor\",\n\t\t\"rich text editor\",\n\t\t\"rte\",\n\t\t\"rich text\",\n\t\t\"contenteditable\",\n\t\t\"editing\"\n\t],\n\t\"homepage\": \"https://www.tiny.cloud/\",\n\t\"type\": \"component\",\n\t\"extra\": {\n\t\t\"component\": {\n\t\t\t\"scripts\": [\n\t\t\t\t\"tinymce.js\",\n\t\t\t\t\"plugins/*/plugin.js\",\n\t\t\t\t\"themes/*/theme.js\",\n\t\t\t\t\"models/*/model.js\",\n\t\t\t\t\"icons/*/icons.js\"\n\t\t\t],\n\t\t\t\"files\": [\n\t\t\t\t\"tinymce.min.js\",\n\t\t\t\t\"plugins/*/plugin.min.js\",\n\t\t\t\t\"themes/*/theme.min.js\",\n\t\t\t\t\"models/*/model.min.js\",\n\t\t\t\t\"skins/**\",\n\t\t\t\t\"icons/*/icons.min.js\"\n\t\t\t]\n\t\t}\n\t},\n\t\"archive\": {\n\t\t\"exclude\": [\n\t\t\t\"README.md\",\n\t\t\t\"bower.js\",\n\t\t\t\"package.json\",\n\t\t\t\".npmignore\",\n\t\t\t\"CHANGELOG.md\"\n\t\t]\n\t}\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/icons/default/icons.js",
    "content": "tinymce.IconManager.add('default', {\n  icons: {\n    'accessibility-check': '<svg width=\"24\" height=\"24\"><path d=\"M12 2a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2c0-1.1.9-2 2-2Zm8 7h-5v12c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-5c0-.6-.4-1-1-1a1 1 0 0 0-1 1v5c0 .6-.4 1-1 1a1 1 0 0 1-1-1V9H4a1 1 0 1 1 0-2h16c.6 0 1 .4 1 1s-.4 1-1 1Z\" fill-rule=\"nonzero\"/></svg>',\n    'action-next': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M5.7 7.3a1 1 0 0 0-1.4 1.4l7.7 7.7 7.7-7.7a1 1 0 1 0-1.4-1.4L12 13.6 5.7 7.3Z\"/></svg>',\n    'action-prev': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M18.3 15.7a1 1 0 0 0 1.4-1.4L12 6.6l-7.7 7.7a1 1 0 0 0 1.4 1.4L12 9.4l6.3 6.3Z\"/></svg>',\n    'addtag': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M15 5a2 2 0 0 1 1.6.8L21 12l-4.4 6.2a2 2 0 0 1-1.6.8h-3v-2h3l3.5-5L15 7H5v3H3V7c0-1.1.9-2 2-2h10Z\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6 12a1 1 0 0 0-1 1v2H3a1 1 0 1 0 0 2h2v2a1 1 0 1 0 2 0v-2h2a1 1 0 1 0 0-2H7v-2c0-.6-.4-1-1-1Z\"/></svg>',\n    'align-center': '<svg width=\"24\" height=\"24\"><path d=\"M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm3 4h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 1 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm-3-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z\" fill-rule=\"evenodd\"/></svg>',\n    'align-justify': '<svg width=\"24\" height=\"24\"><path d=\"M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z\" fill-rule=\"evenodd\"/></svg>',\n    'align-left': '<svg width=\"24\" height=\"24\"><path d=\"M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Zm0-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z\" fill-rule=\"evenodd\"/></svg>',\n    'align-none': '<svg width=\"24\" height=\"24\"><path d=\"M14.2 5 13 7H5a1 1 0 1 1 0-2h9.2Zm4 0h.8a1 1 0 0 1 0 2h-2l1.2-2Zm-6.4 4-1.2 2H5a1 1 0 0 1 0-2h6.8Zm4 0H19a1 1 0 0 1 0 2h-4.4l1.2-2Zm-6.4 4-1.2 2H5a1 1 0 0 1 0-2h4.4Zm4 0H19a1 1 0 0 1 0 2h-6.8l1.2-2ZM7 17l-1.2 2H5a1 1 0 0 1 0-2h2Zm4 0h8a1 1 0 0 1 0 2H9.8l1.2-2Zm5.2-13.5 1.3.7-9.7 16.3-1.3-.7 9.7-16.3Z\" fill-rule=\"evenodd\"/></svg>',\n    'align-right': '<svg width=\"24\" height=\"24\"><path d=\"M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm6 4h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm-6-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z\" fill-rule=\"evenodd\"/></svg>',\n    'arrow-left': '<svg width=\"24\" height=\"24\"><path d=\"m5.6 13 12 6a1 1 0 0 0 1.4-1V6a1 1 0 0 0-1.4-.9l-12 6a1 1 0 0 0 0 1.8Z\" fill-rule=\"evenodd\"/></svg>',\n    'arrow-right': '<svg width=\"24\" height=\"24\"><path d=\"m18.5 13-12 6A1 1 0 0 1 5 18V6a1 1 0 0 1 1.4-.9l12 6a1 1 0 0 1 0 1.8Z\" fill-rule=\"evenodd\"/></svg>',\n    'bold': '<svg width=\"24\" height=\"24\"><path d=\"M7.8 19c-.3 0-.5 0-.6-.2l-.2-.5V5.7c0-.2 0-.4.2-.5l.6-.2h5c1.5 0 2.7.3 3.5 1 .7.6 1.1 1.4 1.1 2.5a3 3 0 0 1-.6 1.9c-.4.6-1 1-1.6 1.2.4.1.9.3 1.3.6s.8.7 1 1.2c.4.4.5 1 .5 1.6 0 1.3-.4 2.3-1.3 3-.8.7-2.1 1-3.8 1H7.8Zm5-8.3c.6 0 1.2-.1 1.6-.5.4-.3.6-.7.6-1.3 0-1.1-.8-1.7-2.3-1.7H9.3v3.5h3.4Zm.5 6c.7 0 1.3-.1 1.7-.4.4-.4.6-.9.6-1.5s-.2-1-.7-1.4c-.4-.3-1-.4-2-.4H9.4v3.8h4Z\" fill-rule=\"evenodd\"/></svg>',\n    'bookmark': '<svg width=\"24\" height=\"24\"><path d=\"M6 4v17l6-4 6 4V4c0-.6-.4-1-1-1H7a1 1 0 0 0-1 1Z\" fill-rule=\"nonzero\"/></svg>',\n    'border-style': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><rect width=\"18\" height=\"2\" x=\"3\" y=\"6\" rx=\"1\"/><rect width=\"2.8\" height=\"2\" x=\"3\" y=\"16\" rx=\"1\"/><rect width=\"2.8\" height=\"2\" x=\"6.8\" y=\"16\" rx=\"1\"/><rect width=\"2.8\" height=\"2\" x=\"10.6\" y=\"16\" rx=\"1\"/><rect width=\"2.8\" height=\"2\" x=\"14.4\" y=\"16\" rx=\"1\"/><rect width=\"2.8\" height=\"2\" x=\"18.2\" y=\"16\" rx=\"1\"/><rect width=\"8\" height=\"2\" x=\"3\" y=\"11\" rx=\"1\"/><rect width=\"8\" height=\"2\" x=\"13\" y=\"11\" rx=\"1\"/></g></svg>',\n    'border-width': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><rect width=\"18\" height=\"5\" x=\"3\" y=\"5\" rx=\"1\"/><rect width=\"18\" height=\"3.5\" x=\"3\" y=\"11.5\" rx=\"1\"/><rect width=\"18\" height=\"2\" x=\"3\" y=\"17\" rx=\"1\"/></g></svg>',\n    'brightness': '<svg width=\"24\" height=\"24\"><path d=\"M12 17c.3 0 .5.1.7.3.2.2.3.4.3.7v1c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7v-1c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3Zm0-10a1 1 0 0 1-.7-.3A1 1 0 0 1 11 6V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7v1c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3Zm7 4c.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-1a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h1ZM7 12c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3H5a1 1 0 0 1-.7-.3A1 1 0 0 1 4 12c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h1c.3 0 .5.1.7.3.2.2.3.4.3.7Zm10 3.5.7.8c.2.1.3.4.3.6 0 .3-.1.6-.3.8a1 1 0 0 1-.8.3 1 1 0 0 1-.6-.3l-.8-.7a1 1 0 0 1-.3-.8c0-.2.1-.5.3-.7a1 1 0 0 1 1.4 0Zm-10-7-.7-.8a1 1 0 0 1-.3-.6c0-.3.1-.6.3-.8.2-.2.5-.3.8-.3.2 0 .5.1.7.3l.7.7c.2.2.3.5.3.8 0 .2-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.8-.3Zm10 0a1 1 0 0 1-.8.3 1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.6.3-.8l.8-.7c.1-.2.4-.3.6-.3.3 0 .6.1.8.3.2.2.3.5.3.8 0 .2-.1.5-.3.7l-.7.7Zm-10 7c.2-.2.5-.3.8-.3.2 0 .5.1.7.3a1 1 0 0 1 0 1.4l-.8.8a1 1 0 0 1-.6.3 1 1 0 0 1-.8-.3 1 1 0 0 1-.3-.8c0-.2.1-.5.3-.6l.7-.8ZM12 8a4 4 0 0 1 3.7 2.4 4 4 0 0 1 0 3.2A4 4 0 0 1 12 16a4 4 0 0 1-3.7-2.4 4 4 0 0 1 0-3.2A4 4 0 0 1 12 8Zm0 6.5c.7 0 1.3-.2 1.8-.7.5-.5.7-1.1.7-1.8s-.2-1.3-.7-1.8c-.5-.5-1.1-.7-1.8-.7s-1.3.2-1.8.7c-.5.5-.7 1.1-.7 1.8s.2 1.3.7 1.8c.5.5 1.1.7 1.8.7Z\" fill-rule=\"evenodd\"/></svg>',\n    'browse': '<svg width=\"24\" height=\"24\"><path d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-4v-2h4V8H5v10h4v2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9.4-2.3 2.3a1 1 0 1 1-1.4-1.4l4-4a1 1 0 0 1 1.4 0l4 4a1 1 0 0 1-1.4 1.4L13 13.4V20a1 1 0 0 1-2 0v-6.6Z\" fill-rule=\"nonzero\"/></svg>',\n    'cancel': '<svg width=\"24\" height=\"24\"><path d=\"M12 4.6a7.4 7.4 0 1 1 0 14.8 7.4 7.4 0 0 1 0-14.8ZM12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Zm0 8L14.8 8l1 1.1-2.7 2.8 2.7 2.7-1.1 1.1-2.7-2.7-2.7 2.7-1-1.1 2.6-2.7-2.7-2.7 1-1.1 2.8 2.7Z\" fill-rule=\"nonzero\"/></svg>',\n    'cell-background-color': '<svg width=\"24\" height=\"24\"><path d=\"m15.7 2 1.6 1.6-2.7 2.6 5.9 5.8c.7.7.7 1.7 0 2.4l-6.3 6.1a1.7 1.7 0 0 1-2.4 0l-6.3-6.1c-.7-.7-.7-1.7 0-2.4L15.7 2ZM18 12l-4.5-4L9 12h9ZM4 16s2 2.4 2 3.8C6 21 5.1 22 4 22s-2-1-2-2.2C2 18.4 4 16 4 16Z\"/></svg>',\n    'cell-border-color': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path fill-rule=\"nonzero\" d=\"M5 13v5h2v2H5a2 2 0 0 1-2-2v-5h2zm8-7V4h6a2 2 0 0 1 2 2h-8z\" opacity=\".2\"/><path fill-rule=\"nonzero\" d=\"M13 4v2H5v7H3V6c0-1.1.9-2 2-2h8zm-2.6 14.1.1-.1.1.1.2.3.2.2.2.2c.4.6.8 1.2.8 1.7 0 .8-.7 1.5-1.5 1.5S9 21.3 9 20.5c0-.5.4-1.1.8-1.7l.2-.2.2-.2.2-.3z\"/><path d=\"m13 11-2 2H5v-2h6V6h2z\"/><path fill-rule=\"nonzero\" d=\"m18.4 8 1 1-1.8 1.9 4 4c.5.4.5 1.1 0 1.6l-4.3 4.2a1.2 1.2 0 0 1-1.6 0l-4.4-4.2c-.4-.5-.4-1.2 0-1.7l7-6.8Zm1.6 7-3-3-3 3h6Z\"/></g></svg>',\n    'change-case': '<svg width=\"24\" height=\"24\"><path d=\"M18.4 18.2v-.6c-.5.8-1.3 1.2-2.4 1.2-2.2 0-3.3-1.6-3.3-4.8 0-3.1 1-4.7 3.3-4.7 1.1 0 1.8.3 2.4 1.1v-.6c0-.5.4-.8.8-.8s.8.3.8.8v8.4c0 .5-.4.8-.8.8a.8.8 0 0 1-.8-.8zm-2-7.4c-1.3 0-1.8.9-1.8 3.2 0 2.4.5 3.3 1.7 3.3 1.3 0 1.8-.9 1.8-3.2 0-2.4-.5-3.3-1.7-3.3zM10 15.7H5.5l-.8 2.6a1 1 0 0 1-1 .7h-.2a.7.7 0 0 1-.7-1l4-12a1 1 0 0 1 2 0l4 12a.7.7 0 0 1-.8 1h-.2a1 1 0 0 1-1-.7l-.8-2.6zm-.3-1.5-2-6.5-1.9 6.5h3.9z\" fill-rule=\"evenodd\"/></svg>',\n    'character-count': '<svg width=\"24\" height=\"24\"><path d=\"M4 11.5h16v1H4v-1Zm4.8-6.8V10H7.7V5.8h-1v-1h2ZM11 8.3V9h2v1h-3V7.7l2-1v-.9h-2v-1h3v2.4l-2 1Zm6.3-3.4V10h-3.1V9h2.1V8h-2.1V6.8h2.1v-1h-2.1v-1h3.1ZM5.8 16.4c0-.5.2-.8.5-1 .2-.2.6-.3 1.2-.3l.8.1c.2 0 .4.2.5.3l.4.4v2.8l.2.3H8.2V18.7l-.6.3H7c-.4 0-.7 0-1-.2a1 1 0 0 1-.3-.9c0-.3 0-.6.3-.8.3-.2.7-.4 1.2-.4l.6-.2h.3v-.2l-.1-.2a.8.8 0 0 0-.5-.1 1 1 0 0 0-.4 0l-.3.4h-1Zm2.3.8h-.2l-.2.1-.4.1a1 1 0 0 0-.4.2l-.2.2.1.3.5.1h.4l.4-.4v-.6Zm2-3.4h1.2v1.7l.5-.3h.5c.5 0 .9.1 1.2.5.3.4.5.8.5 1.4 0 .6-.2 1.1-.5 1.5-.3.4-.7.6-1.3.6l-.6-.1-.4-.4v.4h-1.1v-5.4Zm1.1 3.3c0 .3 0 .6.2.8a.7.7 0 0 0 1.2 0l.2-.8c0-.4 0-.6-.2-.8a.7.7 0 0 0-.6-.3l-.6.3-.2.8Zm6.1-.5c0-.2 0-.3-.2-.4a.8.8 0 0 0-.5-.2c-.3 0-.5.1-.6.3l-.2.9c0 .3 0 .6.2.8.1.2.3.3.6.3.2 0 .4 0 .5-.2l.2-.4h1.1c0 .5-.3.8-.6 1.1a2 2 0 0 1-1.3.4c-.5 0-1-.2-1.3-.6a2 2 0 0 1-.5-1.4c0-.6.1-1.1.5-1.5.3-.4.8-.5 1.4-.5.5 0 1 0 1.2.3.4.3.5.7.5 1.2h-1v-.1Z\" fill-rule=\"evenodd\"/></svg>',\n    'checklist-rtl': '<svg width=\"24\" height=\"24\"><path d=\"M5 17h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2zm14.2 11c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 20c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 14c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 8c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8z\" fill-rule=\"evenodd\"/></svg>',\n    'checklist': '<svg width=\"24\" height=\"24\"><path d=\"M11 17h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2h-8a1 1 0 0 1 0-2ZM7.2 16c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 20c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 14c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 8c-.2.3-.7.4-1 0L3.8 6.9a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Z\" fill-rule=\"evenodd\"/></svg>',\n    'checkmark': '<svg width=\"24\" height=\"24\"><path d=\"M18.2 5.4a1 1 0 0 1 1.6 1.2l-8 12a1 1 0 0 1-1.5.1l-5-5a1 1 0 1 1 1.4-1.4l4.1 4.1 7.4-11Z\" fill-rule=\"nonzero\"/></svg>',\n    'chevron-down': '<svg width=\"10\" height=\"10\"><path d=\"M8.7 2.2c.3-.3.8-.3 1 0 .4.4.4.9 0 1.2L5.7 7.8c-.3.3-.9.3-1.2 0L.2 3.4a.8.8 0 0 1 0-1.2c.3-.3.8-.3 1.1 0L5 6l3.7-3.8Z\" fill-rule=\"nonzero\"/></svg>',\n    'chevron-left': '<svg width=\"10\" height=\"10\"><path d=\"M7.8 1.3 4 5l3.8 3.7c.3.3.3.8 0 1-.4.4-.9.4-1.2 0L2.2 5.7a.8.8 0 0 1 0-1.2L6.6.2C7 0 7.4 0 7.8.2c.3.3.3.8 0 1.1Z\" fill-rule=\"nonzero\"/></svg>',\n    'chevron-right': '<svg width=\"10\" height=\"10\"><path d=\"M2.2 1.3a.8.8 0 0 1 0-1c.4-.4.9-.4 1.2 0l4.4 4.1c.3.4.3.9 0 1.2L3.4 9.8c-.3.3-.8.3-1.2 0a.8.8 0 0 1 0-1.1L6 5 2.2 1.3Z\" fill-rule=\"nonzero\"/></svg>',\n    'chevron-up': '<svg width=\"10\" height=\"10\"><path d=\"M8.7 7.8 5 4 1.3 7.8c-.3.3-.8.3-1 0a.8.8 0 0 1 0-1.2l4.1-4.4c.3-.3.9-.3 1.2 0l4.2 4.4c.3.3.3.9 0 1.2-.3.3-.8.3-1.1 0Z\" fill-rule=\"nonzero\"/></svg>',\n    'close': '<svg width=\"24\" height=\"24\"><path d=\"M17.3 8.2 13.4 12l3.9 3.8a1 1 0 0 1-1.5 1.5L12 13.4l-3.8 3.9a1 1 0 0 1-1.5-1.5l3.9-3.8-3.9-3.8a1 1 0 0 1 1.5-1.5l3.8 3.9 3.8-3.9a1 1 0 0 1 1.5 1.5Z\" fill-rule=\"evenodd\"/></svg>',\n    'code-sample': '<svg width=\"24\" height=\"26\"><path d=\"M7.1 11a2.8 2.8 0 0 1-.8 2 2.8 2.8 0 0 1 .8 2v1.7c0 .3.1.6.4.8.2.3.5.4.8.4.3 0 .4.2.4.4v.8c0 .2-.1.4-.4.4-.7 0-1.4-.3-2-.8-.5-.6-.8-1.3-.8-2V15c0-.3-.1-.6-.4-.8-.2-.3-.5-.4-.8-.4a.4.4 0 0 1-.4-.4v-.8c0-.2.2-.4.4-.4.3 0 .6-.1.8-.4.3-.2.4-.5.4-.8V9.3c0-.7.3-1.4.8-2 .6-.5 1.3-.8 2-.8.3 0 .4.2.4.4v.8c0 .2-.1.4-.4.4-.3 0-.6.1-.8.4-.3.2-.4.5-.4.8V11Zm9.8 0V9.3c0-.3-.1-.6-.4-.8-.2-.3-.5-.4-.8-.4a.4.4 0 0 1-.4-.4V7c0-.2.1-.4.4-.4.7 0 1.4.3 2 .8.5.6.8 1.3.8 2V11c0 .3.1.6.4.8.2.3.5.4.8.4.2 0 .4.2.4.4v.8c0 .2-.2.4-.4.4-.3 0-.6.1-.8.4-.3.2-.4.5-.4.8v1.7c0 .7-.3 1.4-.8 2-.6.5-1.3.8-2 .8a.4.4 0 0 1-.4-.4v-.8c0-.2.1-.4.4-.4.3 0 .6-.1.8-.4.3-.2.4-.5.4-.8V15a2.8 2.8 0 0 1 .8-2 2.8 2.8 0 0 1-.8-2Zm-3.3-.4c0 .4-.1.8-.5 1.1-.3.3-.7.5-1.1.5-.4 0-.8-.2-1.1-.5-.4-.3-.5-.7-.5-1.1 0-.5.1-.9.5-1.2.3-.3.7-.4 1.1-.4.4 0 .8.1 1.1.4.4.3.5.7.5 1.2ZM12 13c.4 0 .8.1 1.1.5.4.3.5.7.5 1.1 0 1-.1 1.6-.5 2a3 3 0 0 1-1.1 1c-.4.3-.8.4-1.1.4a.5.5 0 0 1-.5-.5V17a3 3 0 0 0 1-.2l.6-.6c-.6 0-1-.2-1.3-.5-.2-.3-.3-.7-.3-1 0-.5.1-1 .5-1.2.3-.4.7-.5 1.1-.5Z\" fill-rule=\"evenodd\"/></svg>',\n    'color-levels': '<svg width=\"24\" height=\"24\"><path d=\"M17.5 11.4A9 9 0 0 1 18 14c0 .5 0 1-.2 1.4 0 .4-.3.9-.5 1.3a6.2 6.2 0 0 1-3.7 3 5.7 5.7 0 0 1-3.2 0A5.9 5.9 0 0 1 7.6 18a6.2 6.2 0 0 1-1.4-2.6 6.7 6.7 0 0 1 0-2.8c0-.4.1-.9.3-1.3a13.6 13.6 0 0 1 2.3-4A20 20 0 0 1 12 4a26.4 26.4 0 0 1 3.2 3.4 18.2 18.2 0 0 1 2.3 4Zm-2 4.5c.4-.7.5-1.4.5-2a7.3 7.3 0 0 0-1-3.2c.2.6.2 1.2.2 1.9a4.5 4.5 0 0 1-1.3 3 5.3 5.3 0 0 1-2.3 1.5 4.9 4.9 0 0 1-2 .1 4.3 4.3 0 0 0 2.4.8 4 4 0 0 0 2-.6 4 4 0 0 0 1.5-1.5Z\" fill-rule=\"evenodd\"/></svg>',\n    'color-picker': '<svg width=\"24\" height=\"24\"><path d=\"M12 3a9 9 0 0 0 0 18 1.5 1.5 0 0 0 1.1-2.5c-.2-.3-.4-.6-.4-1 0-.8.7-1.5 1.5-1.5H16a5 5 0 0 0 5-5c0-4.4-4-8-9-8Zm-5.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm3-4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm3 4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Z\" fill-rule=\"nonzero\"/></svg>',\n    'color-swatch-remove-color': '<svg width=\"24\" height=\"24\"><path stroke=\"#000\" stroke-width=\"2\" d=\"M21 3 3 21\" fill-rule=\"evenodd\"/></svg>',\n    'color-swatch': '<svg width=\"24\" height=\"24\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"1\" fill-rule=\"evenodd\"/></svg>',\n    'comment-add': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><path d=\"m9 19 3-2h7c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H5a1 1 0 0 0-1 1v10c0 .6.4 1 1 1h4v2Zm-2 4v-4H5a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-6.4L7 23Z\"/><path d=\"M13 10h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0v-2H9a1 1 0 0 1 0-2h2V8a1 1 0 0 1 2 0v2Z\"/></g></svg>',\n    'comment': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"m9 19 3-2h7c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H5a1 1 0 0 0-1 1v10c0 .6.4 1 1 1h4v2Zm-2 4v-4H5a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-6.4L7 23Z\"/></svg>',\n    'contrast': '<svg width=\"24\" height=\"24\"><path d=\"M12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Zm-6 8a6 6 0 0 0 6 6V6a6 6 0 0 0-6 6Z\" fill-rule=\"evenodd\"/></svg>',\n    'copy': '<svg width=\"24\" height=\"24\"><path d=\"M16 3H6a2 2 0 0 0-2 2v11h2V5h10V3Zm1 4a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2V9c0-1.2.9-2 2-2h7Zm0 12V9h-7v10h7Z\" fill-rule=\"nonzero\"/></svg>',\n    'crop': '<svg width=\"24\" height=\"24\"><path d=\"M17 8v7h2c.6 0 1 .4 1 1s-.4 1-1 1h-2v2c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-2H7V9H5a1 1 0 1 1 0-2h2V5c0-.6.4-1 1-1s1 .4 1 1v2h7l3-3 1 1-3 3ZM9 9v5l5-5H9Zm1 6h5v-5l-5 5Z\" fill-rule=\"evenodd\"/></svg>',\n    'cut-column': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M7.2 4.5c.9 0 1.6.4 2.2 1A3.7 3.7 0 0 1 10.5 8v.5l1 1 4-4 1-.5a3.3 3.3 0 0 1 2 0c.4 0 .7.3 1 .5L17 8h4v13h-6V10l-1.5 1.5.5.5v4l-2.5-2.5-1 1v.5c0 .4 0 .8-.3 1.2-.2.5-.4.9-.8 1.2-.6.7-1.3 1-2.2 1-.8.2-1.5 0-2-.6l-.5-.8-.2-1c0-.4 0-.8.3-1.2A3.9 3.9 0 0 1 7 12.7c.5-.2 1-.3 1.5-.2l1-1-1-1c-.5 0-1 0-1.5-.2-.5-.1-1-.4-1.4-.9-.4-.3-.6-.7-.8-1.2L4.5 7c0-.4 0-.7.2-1 0-.3.3-.6.5-.8.5-.5 1.2-.8 2-.7Zm12.3 5h-3v10h3v-10ZM8 13.8h-.3l-.4.2a2.8 2.8 0 0 0-.7.4v.1a2.8 2.8 0 0 0-.6.8l-.1.4v.7l.2.5.5.2h.7a2.6 2.6 0 0 0 .8-.3 2.4 2.4 0 0 0 .7-.7 2.5 2.5 0 0 0 .3-.8 1.5 1.5 0 0 0 0-.8 1 1 0 0 0-.2-.4 1 1 0 0 0-.5-.2H8Zm3.5-3.7c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4ZM7 5.8h-.4a1 1 0 0 0-.5.3 1 1 0 0 0-.2.5v.7a2.5 2.5 0 0 0 .3.8l.2.3h.1l.4.4.4.2.4.1h.7L9 9l.2-.4a1.6 1.6 0 0 0 0-.8 2.6 2.6 0 0 0-.3-.8A2.5 2.5 0 0 0 7.7 6l-.4-.1H7Z\"/></svg>',\n    'cut-row': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M22 3v5H9l3 3 2-2h4l-4 4 1 1h.5c.4 0 .8 0 1.2.3.5.2.9.4 1.2.8.7.6 1 1.3 1 2.2.2.8 0 1.5-.6 2l-.8.5-1 .2c-.4 0-.8 0-1.2-.3a3.9 3.9 0 0 1-2.1-2.2c-.2-.5-.3-1-.2-1.5l-1-1-1 1c0 .5 0 1-.2 1.5-.1.5-.4 1-.9 1.4-.3.4-.7.6-1.2.8l-1.2.3c-.4 0-.7 0-1-.2-.3 0-.6-.3-.8-.5-.5-.5-.8-1.2-.7-2 0-.9.4-1.6 1-2.2A3.7 3.7 0 0 1 8.6 14H9l1-1-4-4-.5-1a3.3 3.3 0 0 1 0-2c0-.4.3-.7.5-1l2 2V3h14ZM8.5 15.3h-.3a2.6 2.6 0 0 0-.8.4 2.5 2.5 0 0 0-.9 1.1l-.1.4v.7l.2.5.5.2h.7a2.5 2.5 0 0 0 .8-.3L9 18V18l.4-.4.2-.4.1-.4v-.7a1 1 0 0 0-.2-.5 1 1 0 0 0-.4-.2h-.5Zm7 0H15a1 1 0 0 0-.4.3 1 1 0 0 0-.2.5 1.5 1.5 0 0 0 0 .7v.4a2.8 2.8 0 0 0 .5.7h.1a2.8 2.8 0 0 0 .8.6l.4.1h.7l.5-.2.2-.5v-.7a2.6 2.6 0 0 0-.3-.8 2.4 2.4 0 0 0-.7-.7 2.5 2.5 0 0 0-.8-.3h-.3ZM12 11.6c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4Zm8.5-7.1h-11v2h11v-2Z\"/></svg>',\n    'cut': '<svg width=\"24\" height=\"24\"><path d=\"M18 15c.6.7 1 1.4 1 2.3 0 .8-.2 1.5-.7 2l-.8.5-1 .2c-.4 0-.8 0-1.2-.3a3.9 3.9 0 0 1-2.1-2.2c-.2-.5-.3-1-.2-1.5l-1-1-1 1c0 .5 0 1-.2 1.5-.1.5-.4 1-.9 1.4-.3.4-.7.6-1.2.8l-1.2.3c-.4 0-.7 0-1-.2-.3 0-.6-.3-.8-.5-.5-.5-.8-1.2-.7-2 0-.9.4-1.6 1-2.2A3.7 3.7 0 0 1 8.6 14H9l1-1-4-4-.5-1a3.3 3.3 0 0 1 0-2c0-.4.3-.7.5-1l6 6 6-6 .5 1a3.3 3.3 0 0 1 0 2c0 .4-.3.7-.5 1l-4 4 1 1h.5c.4 0 .8 0 1.2.3.5.2.9.4 1.2.8Zm-8.5 2.2.1-.4v-.7a1 1 0 0 0-.2-.5 1 1 0 0 0-.4-.2 1.6 1.6 0 0 0-.8 0 2.6 2.6 0 0 0-.8.3 2.5 2.5 0 0 0-.9 1.1l-.1.4v.7l.2.5.5.2h.7a2.5 2.5 0 0 0 .8-.3 2.8 2.8 0 0 0 1-1Zm2.5-2.8c.4 0 .7-.1 1-.4.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4Zm5.4 4 .2-.5v-.7a2.6 2.6 0 0 0-.3-.8 2.4 2.4 0 0 0-.7-.7 2.5 2.5 0 0 0-.8-.3 1.5 1.5 0 0 0-.8 0 1 1 0 0 0-.4.2 1 1 0 0 0-.2.5 1.5 1.5 0 0 0 0 .7v.4l.3.4.3.4a2.8 2.8 0 0 0 .8.5l.4.1h.7l.5-.2Z\" fill-rule=\"evenodd\"/></svg>',\n    'document-properties': '<svg width=\"24\" height=\"24\"><path d=\"M14.4 3H7a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V7.6L14.4 3ZM17 19H7V5h6v4h4v10Z\" fill-rule=\"nonzero\"/></svg>',\n    'drag': '<svg width=\"24\" height=\"24\"><path d=\"M13 5h2v2h-2V5Zm0 4h2v2h-2V9ZM9 9h2v2H9V9Zm4 4h2v2h-2v-2Zm-4 0h2v2H9v-2Zm0 4h2v2H9v-2Zm4 0h2v2h-2v-2ZM9 5h2v2H9V5Z\" fill-rule=\"evenodd\"/></svg>',\n    'duplicate-column': '<svg width=\"24\" height=\"24\"><path d=\"M17 6v16h-7V6h7Zm-2 2h-3v12h3V8Zm-2-6v2H8v15H6V2h7Z\"/></svg>',\n    'duplicate-row': '<svg width=\"24\" height=\"24\"><path d=\"M22 11v7H6v-7h16Zm-2 2H8v3h12v-3Zm-1-6v2H4v5H2V7h17Z\"/></svg>',\n    'duplicate': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><path d=\"M16 3v2H6v11H4V5c0-1.1.9-2 2-2h10Zm3 8h-2V9h-7v10h9a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2V9c0-1.2.9-2 2-2h7a2 2 0 0 1 2 2v2Z\"/><path d=\"M17 14h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1h-1a1 1 0 0 1 0-2h1v-1a1 1 0 0 1 2 0v1Z\"/></g></svg>',\n    'edit-block': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"m19.8 8.8-9.4 9.4c-.2.2-.5.4-.9.4l-5.4 1.2 1.2-5.4.5-.8 9.4-9.4c.7-.7 1.8-.7 2.5 0l2.1 2.1c.7.7.7 1.8 0 2.5Zm-2-.2 1-.9v-.3l-2.2-2.2a.3.3 0 0 0-.3 0l-1 1L18 8.5Zm-1 1-2.5-2.4-6 6 2.5 2.5 6-6Zm-7 7.1-2.6-2.4-.3.3-.1.2-.7 3 3.1-.6h.1l.4-.5Z\"/></svg>',\n    'edit-image': '<svg width=\"24\" height=\"24\"><path d=\"M18 16h2V7a2 2 0 0 0-2-2H7v2h11v9ZM6 17h15a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1H6a2 2 0 0 1-2-2V7H3a1 1 0 1 1 0-2h1V4a1 1 0 1 1 2 0v13Zm3-5.3 1.3 2 3-4.7 3.7 6H7l2-3.3Z\" fill-rule=\"nonzero\"/></svg>',\n    'embed-page': '<svg width=\"24\" height=\"24\"><path d=\"M19 6V5H5v14h2A13 13 0 0 1 19 6Zm0 1.4c-.8.8-1.6 2.4-2.2 4.6H19V7.4Zm0 5.6h-2.4c-.4 1.8-.6 3.8-.6 6h3v-6Zm-4 6c0-2.2.2-4.2.6-6H13c-.7 1.8-1.1 3.8-1.1 6h3Zm-4 0c0-2.2.4-4.2 1-6H9.6A12 12 0 0 0 8 19h3ZM4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm11.8 9c.4-1.9 1-3.4 1.8-4.5a9.2 9.2 0 0 0-4 4.5h2.2Zm-3.4 0a12 12 0 0 1 2.8-4 12 12 0 0 0-5 4h2.2Z\" fill-rule=\"nonzero\"/></svg>',\n    'embed': '<svg width=\"24\" height=\"24\"><path d=\"M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5H5Zm4.8 2.6 5.6 4a.5.5 0 0 1 0 .8l-5.6 4A.5.5 0 0 1 9 16V8a.5.5 0 0 1 .8-.4Z\" fill-rule=\"nonzero\"/></svg>',\n    'emoji': '<svg width=\"24\" height=\"24\"><path d=\"M9 11c.6 0 1-.4 1-1s-.4-1-1-1a1 1 0 0 0-1 1c0 .6.4 1 1 1Zm6 0c.6 0 1-.4 1-1s-.4-1-1-1a1 1 0 0 0-1 1c0 .6.4 1 1 1Zm-3 5.5c2.1 0 4-1.5 4.4-3.5H7.6c.5 2 2.3 3.5 4.4 3.5ZM12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 14.5a6.5 6.5 0 1 1 0-13 6.5 6.5 0 0 1 0 13Z\" fill-rule=\"nonzero\"/></svg>',\n    'export': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><path d=\"M14.4 3 18 7v1h-5V5H7v14h9a1 1 0 0 1 2 0c0 1-.8 2-1.9 2H7c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2h7.5Z\"/><path d=\"M18.1 12c.5 0 .9.4.9 1 0 .5-.3 1-.8 1h-7.3c-.5 0-.9-.4-.9-1 0-.5.3-1 .8-1h7.3Z\"/><path d=\"M16.4 9.2a1 1 0 0 1 1.4.2l2.4 3.6-2.4 3.6a1 1 0 0 1-1.7-1v-.2l1.7-2.4-1.6-2.4a1 1 0 0 1 .2-1.4Z\"/></g></svg>',\n    'fill': '<svg width=\"24\" height=\"26\"><path d=\"m16.6 12-9-9-1.4 1.4 2.4 2.4-5.2 5.1c-.5.6-.5 1.6 0 2.2L9 19.6a1.5 1.5 0 0 0 2.2 0l5.5-5.5c.5-.6.5-1.6 0-2.2ZM5.2 13 10 8.2l4.8 4.8H5.2ZM19 14.5s-2 2.2-2 3.5c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.3-2-3.5-2-3.5Z\" fill-rule=\"nonzero\"/></svg>',\n    'flip-horizontally': '<svg width=\"24\" height=\"24\"><path d=\"M14 19h2v-2h-2v2Zm4-8h2V9h-2v2ZM4 7v10c0 1.1.9 2 2 2h3v-2H6V7h3V5H6a2 2 0 0 0-2 2Zm14-2v2h2a2 2 0 0 0-2-2Zm-7 16h2V3h-2v18Zm7-6h2v-2h-2v2Zm-4-8h2V5h-2v2Zm4 12a2 2 0 0 0 2-2h-2v2Z\" fill-rule=\"nonzero\"/></svg>',\n    'flip-vertically': '<svg width=\"24\" height=\"24\"><path d=\"M5 14v2h2v-2H5Zm8 4v2h2v-2h-2Zm4-14H7a2 2 0 0 0-2 2v3h2V6h10v3h2V6a2 2 0 0 0-2-2Zm2 14h-2v2a2 2 0 0 0 2-2ZM3 11v2h18v-2H3Zm6 7v2h2v-2H9Zm8-4v2h2v-2h-2ZM5 18c0 1.1.9 2 2 2v-2H5Z\" fill-rule=\"nonzero\"/></svg>',\n    'footnote': '<svg width=\"24\" height=\"24\"><path d=\"M19 13c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2h14Z\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M19 4v6h-1V5h-1.5V4h2.6Z\"/><path d=\"M12 18c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2h7ZM14 8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2h9Z\"/></svg>',\n    'format-painter': '<svg width=\"24\" height=\"24\"><path d=\"M18 5V4c0-.5-.4-1-1-1H5a1 1 0 0 0-1 1v4c0 .6.5 1 1 1h12c.6 0 1-.4 1-1V7h1v4H9v9c0 .6.4 1 1 1h2c.6 0 1-.4 1-1v-7h8V5h-3Z\" fill-rule=\"nonzero\"/></svg>',\n    'format': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M17 5a1 1 0 0 1 0 2h-4v11a1 1 0 0 1-2 0V7H7a1 1 0 1 1 0-2h10Z\"/></svg>',\n    'fullscreen': '<svg width=\"24\" height=\"24\"><path d=\"m15.3 10-1.2-1.3 2.9-3h-2.3a.9.9 0 1 1 0-1.7H19c.5 0 .9.4.9.9v4.4a.9.9 0 1 1-1.8 0V7l-2.9 3Zm0 4 3 3v-2.3a.9.9 0 1 1 1.7 0V19c0 .5-.4.9-.9.9h-4.4a.9.9 0 1 1 0-1.8H17l-3-2.9 1.3-1.2ZM10 15.4l-2.9 3h2.3a.9.9 0 1 1 0 1.7H5a.9.9 0 0 1-.9-.9v-4.4a.9.9 0 1 1 1.8 0V17l2.9-3 1.2 1.3ZM8.7 10 5.7 7v2.3a.9.9 0 0 1-1.7 0V5c0-.5.4-.9.9-.9h4.4a.9.9 0 0 1 0 1.8H7l3 2.9-1.3 1.2Z\" fill-rule=\"nonzero\"/></svg>',\n    'gallery': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"m5 15.7 2.3-2.2c.3-.3.7-.3 1 0L11 16l5.1-5c.3-.4.8-.4 1 0l2 1.9V8H5v7.7ZM5 18V19h3l1.8-1.9-2-2L5 17.9Zm14-3-2.5-2.4-6.4 6.5H19v-4ZM4 6h16c.6 0 1 .4 1 1v13c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V7c0-.6.4-1 1-1Zm6 7a2 2 0 1 1 0-4 2 2 0 0 1 0 4ZM4.5 4h15a.5.5 0 1 1 0 1h-15a.5.5 0 0 1 0-1Zm2-2h11a.5.5 0 1 1 0 1h-11a.5.5 0 0 1 0-1Z\"/></svg>',\n    'gamma': '<svg width=\"24\" height=\"24\"><path d=\"M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5H5Zm6.5 11.8V14L9.2 8.7a5.1 5.1 0 0 0-.4-.8l-.1-.2H8v-1l.3-.1.3-.1h.7a1 1 0 0 1 .6.5l.1.3a8.5 8.5 0 0 1 .3.6l1.9 4.6 2-5.2a1 1 0 0 1 1-.6.5.5 0 0 1 .5.6L13 14v2.8a.7.7 0 0 1-1.4 0Z\" fill-rule=\"nonzero\"/></svg>',\n    'help': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path d=\"M12 5.5a6.5 6.5 0 0 0-6 9 6.3 6.3 0 0 0 1.4 2l1 1a6.3 6.3 0 0 0 3.6 1 6.5 6.5 0 0 0 6-9 6.3 6.3 0 0 0-1.4-2l-1-1a6.3 6.3 0 0 0-3.6-1ZM12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Z\"/><path d=\"M9.6 9.7a.7.7 0 0 1-.7-.8c0-1.1 1.5-1.8 3.2-1.8 1.8 0 3.2.8 3.2 2.4 0 1.4-.4 2.1-1.5 2.8-.2 0-.3.1-.3.2a2 2 0 0 0-.8.8.8.8 0 0 1-1.4-.6c.3-.7.8-1 1.3-1.5l.4-.2c.7-.4.8-.6.8-1.5 0-.5-.6-.9-1.7-.9-.5 0-1 .1-1.4.3-.2 0-.3.1-.3.2v-.2c0 .4-.4.8-.8.8Z\" fill-rule=\"nonzero\"/><circle cx=\"12\" cy=\"16\" r=\"1\"/></g></svg>',\n    'highlight-bg-color': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path id=\"tox-icon-highlight-bg-color__color\" d=\"M3 18h18v3H3z\"/><path fill-rule=\"nonzero\" d=\"M7.7 16.7H3l3.3-3.3-.7-.8L10.2 8l4 4.1-4 4.2c-.2.2-.6.2-.8 0l-.6-.7-1.1 1.1zm5-7.5L11 7.4l3-2.9a2 2 0 0 1 2.6 0L18 6c.7.7.7 2 0 2.7l-2.9 2.9-1.8-1.8-.5-.6\"/></g></svg>',\n    'home': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z\"/></svg>',\n    'horizontal-rule': '<svg width=\"24\" height=\"24\"><path d=\"M4 11h16v2H4z\" fill-rule=\"evenodd\"/></svg>',\n    'image-options': '<svg width=\"24\" height=\"24\"><path d=\"M6 10a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm12 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm-6 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Z\" fill-rule=\"nonzero\"/></svg>',\n    'image': '<svg width=\"24\" height=\"24\"><path d=\"m5 15.7 3.3-3.2c.3-.3.7-.3 1 0L12 15l4.1-4c.3-.4.8-.4 1 0l2 1.9V5H5v10.7ZM5 18V19h3l2.8-2.9-2-2L5 17.9Zm14-3-2.5-2.4-6.4 6.5H19v-4ZM4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm6 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z\" fill-rule=\"nonzero\"/></svg>',\n    'indent': '<svg width=\"24\" height=\"24\"><path d=\"M7 5h12c.6 0 1 .4 1 1s-.4 1-1 1H7a1 1 0 1 1 0-2Zm5 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm0 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm-5 4h12a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2Zm-2.6-3.8L6.2 12l-1.8-1.2a1 1 0 0 1 1.2-1.6l3 2a1 1 0 0 1 0 1.6l-3 2a1 1 0 1 1-1.2-1.6Z\" fill-rule=\"evenodd\"/></svg>',\n    'info': '<svg width=\"24\" height=\"24\"><path d=\"M12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Zm-1 3v2h2V7h-2Zm3 10v-1h-1v-5h-3v1h1v4h-1v1h4Z\" fill-rule=\"evenodd\"/></svg>',\n    'insert-character': '<svg width=\"24\" height=\"24\"><path d=\"M15 18h4l1-2v4h-6v-3.3l1.4-1a6 6 0 0 0 1.8-2.9 6.3 6.3 0 0 0-.1-4.1 5.8 5.8 0 0 0-3-3.2c-.6-.3-1.3-.5-2.1-.5a5.1 5.1 0 0 0-3.9 1.8 6.3 6.3 0 0 0-1.3 6 6.2 6.2 0 0 0 1.8 3l1.4.9V20H4v-4l1 2h4v-.5l-2-1L5.4 15A6.5 6.5 0 0 1 4 11c0-1 .2-1.9.6-2.7A7 7 0 0 1 6.3 6C7.1 5.4 8 5 9 4.5c1-.3 2-.5 3.1-.5a8.8 8.8 0 0 1 5.7 2 7 7 0 0 1 1.7 2.3 6 6 0 0 1 .2 4.8c-.2.7-.6 1.3-1 1.9a7.6 7.6 0 0 1-3.6 2.5v.5Z\" fill-rule=\"evenodd\"/></svg>',\n    'insert-time': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><path d=\"M12 19a7 7 0 1 0 0-14 7 7 0 0 0 0 14Zm0 2a9 9 0 1 1 0-18 9 9 0 0 1 0 18Z\"/><path d=\"M16 12h-3V7c0-.6-.4-1-1-1a1 1 0 0 0-1 1v7h5c.6 0 1-.4 1-1s-.4-1-1-1Z\"/></g></svg>',\n    'invert': '<svg width=\"24\" height=\"24\"><path d=\"M18 19.3 16.5 18a5.8 5.8 0 0 1-3.1 1.9 6.1 6.1 0 0 1-5.5-1.6A5.8 5.8 0 0 1 6 14v-.3l.1-1.2A13.9 13.9 0 0 1 7.7 9l-3-3 .7-.8 2.8 2.9 9 8.9 1.5 1.6-.7.6Zm0-5.5v.3l-.1 1.1-.4 1-1.2-1.2a4.3 4.3 0 0 0 .2-1v-.2c0-.4 0-.8-.2-1.3l-.5-1.4a14.8 14.8 0 0 0-3-4.2L12 6a26.1 26.1 0 0 0-2.2 2.5l-1-1a20.9 20.9 0 0 1 2.9-3.3L12 4l1 .8a22.2 22.2 0 0 1 4 5.4c.6 1.2 1 2.4 1 3.6Z\" fill-rule=\"evenodd\"/></svg>',\n    'italic': '<svg width=\"24\" height=\"24\"><path d=\"m16.7 4.7-.1.9h-.3c-.6 0-1 0-1.4.3-.3.3-.4.6-.5 1.1l-2.1 9.8v.6c0 .5.4.8 1.4.8h.2l-.2.8H8l.2-.8h.2c1.1 0 1.8-.5 2-1.5l2-9.8.1-.5c0-.6-.4-.8-1.4-.8h-.3l.2-.9h5.8Z\" fill-rule=\"evenodd\"/></svg>',\n    'language': '<svg width=\"24\" height=\"24\"><path d=\"M12 3a9 9 0 1 1 0 18 9 9 0 0 1 0-18Zm4.3 13.3c-.5 1-1.2 2-2 2.9a7.5 7.5 0 0 0 3.2-2.1l-.2-.2a6 6 0 0 0-1-.6Zm-8.6 0c-.5.2-.9.5-1.2.8.9 1 2 1.7 3.2 2a10 10 0 0 1-2-2.8Zm3.6-.8c-.8 0-1.6.1-2.2.3.5 1 1.2 1.9 2.1 2.7Zm1.5 0v3c.9-.8 1.6-1.7 2.1-2.7-.6-.2-1.4-.3-2.1-.3Zm-6-2.7H4.5c.2 1 .5 2.1 1 3h.3l1.3-1a10 10 0 0 1-.3-2Zm12.7 0h-2.3c0 .7-.1 1.4-.3 2l1.6 1.1c.5-1 .9-2 1-3.1Zm-3.8 0h-3V14c1 0 2 .1 2.7.4.2-.5.3-1 .3-1.6Zm-4.4 0h-3l.3 1.6c.8-.3 1.7-.4 2.7-.4v-1.3Zm-5.5-5c-.7 1-1.1 2.2-1.3 3.5h2.3c0-1 .2-1.8.5-2.6l-1.5-1Zm2.9 1.4v.1c-.2.6-.4 1.3-.4 2h3V9.4c-1 0-1.8-.1-2.6-.3Zm6.6 0h-.1l-2.4.3v1.8h3l-.5-2.1Zm3-1.4-.3.1-1.3.8c.3.8.5 1.6.5 2.6h2.3a7.5 7.5 0 0 0-1.3-3.5Zm-9 0 2 .2V5.5a9 9 0 0 0-2 2.2Zm3.5-2.3V8c.6 0 1.3 0 1.9-.2a9 9 0 0 0-2-2.3Zm-3-.7h-.1c-1.1.4-2.1 1-3 1.8l1.2.7a10 10 0 0 1 1.9-2.5Zm4.4 0 .1.1a10 10 0 0 1 1.8 2.4l1.1-.7a7.5 7.5 0 0 0-3-1.8Z\"/></svg>',\n    'line-height': '<svg width=\"24\" height=\"24\"><path d=\"M21 5a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zM7 3.6l3.7 3.7a1 1 0 0 1-1.3 1.5h-.1L8 7.3v9.2l1.3-1.3a1 1 0 0 1 1.3 0h.1c.4.4.4 1 0 1.3v.1L7 20.4l-3.7-3.7a1 1 0 0 1 1.3-1.5h.1L6 16.7V7.4L4.7 8.7a1 1 0 0 1-1.3 0h-.1a1 1 0 0 1 0-1.3v-.1L7 3.6z\"/></svg>',\n    'line': '<svg width=\"24\" height=\"24\"><path d=\"m15 9-8 8H4v-3l8-8 3 3Zm1-1-3-3 1-1h1c-.2 0 0 0 0 0l2 2s0 .2 0 0v1l-1 1ZM4 18h16v2H4v-2Z\" fill-rule=\"evenodd\"/></svg>',\n    'link': '<svg width=\"24\" height=\"24\"><path d=\"M6.2 12.3a1 1 0 0 1 1.4 1.4l-2 2a2 2 0 1 0 2.6 2.8l4.8-4.8a1 1 0 0 0 0-1.4 1 1 0 1 1 1.4-1.3 2.9 2.9 0 0 1 0 4L9.6 20a3.9 3.9 0 0 1-5.5-5.5l2-2Zm11.6-.6a1 1 0 0 1-1.4-1.4l2-2a2 2 0 1 0-2.6-2.8L11 10.3a1 1 0 0 0 0 1.4A1 1 0 1 1 9.6 13a2.9 2.9 0 0 1 0-4L14.4 4a3.9 3.9 0 0 1 5.5 5.5l-2 2Z\" fill-rule=\"nonzero\"/></svg>',\n    'list-bull-circle': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path d=\"M11 16a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6ZM11 26a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6ZM11 36a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6Z\" fill-rule=\"nonzero\"/><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/></g></svg>',\n    'list-bull-default': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><circle cx=\"11\" cy=\"14\" r=\"3\"/><circle cx=\"11\" cy=\"24\" r=\"3\"/><circle cx=\"11\" cy=\"34\" r=\"3\"/><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/></g></svg>',\n    'list-bull-square': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path d=\"M8 11h6v6H8zM8 21h6v6H8zM8 31h6v6H8z\"/><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/></g></svg>',\n    'list-num-default-rtl': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z\"/><path d=\"M37.4 17v-4.8h-.1l-1.5 1v-1.1l1.6-1.1h1.2v6zM33.3 17.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7zm1.7 5.7c0-1.2 1-2 2.2-2 1.3 0 2.1.8 2.1 1.8 0 .7-.3 1.2-1.3 2.2l-1.2 1v.2h2.6v1h-4.3v-.9l2-1.9c.8-.8 1-1.1 1-1.5 0-.5-.4-.8-1-.8-.5 0-.9.3-.9.9H35zm-1.7 4.3c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7zm3.2 7.3v-1h.7c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7s-1 .3-1 .8H35c0-1.1 1-1.8 2.2-1.8 1.2 0 2.1.6 2.1 1.6 0 .7-.4 1.2-1 1.3v.1c.7.1 1.3.7 1.3 1.4 0 1-1 1.9-2.4 1.9-1.3 0-2.2-.8-2.3-2h1.2c0 .6.5 1 1.1 1 .6 0 1-.4 1-1 0-.5-.3-.8-1-.8h-.7zm-3.3 2.7c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7z\"/></g></svg>',\n    'list-num-default': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/><path d=\"M10 17v-4.8l-1.5 1v-1.1l1.6-1h1.2V17h-1.2Zm3.6.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm-5 5.7c0-1.2.8-2 2.1-2s2.1.8 2.1 1.8c0 .7-.3 1.2-1.4 2.2l-1.1 1v.2h2.6v1H8.6v-.9l2-1.9c.8-.8 1-1.1 1-1.5 0-.5-.4-.8-1-.8-.5 0-.9.3-.9.9H8.5Zm6.3 4.3c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM10 34.4v-1h.7c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7s-1 .3-1 .8H8.6c0-1.1 1-1.8 2.2-1.8 1.3 0 2.1.6 2.1 1.6 0 .7-.4 1.2-1 1.3v.1c.8.1 1.3.7 1.3 1.4 0 1-1 1.9-2.4 1.9-1.3 0-2.2-.8-2.3-2h1.2c0 .6.5 1 1.1 1 .7 0 1-.4 1-1 0-.5-.3-.8-1-.8h-.7Zm4.7 2.7c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7Z\"/></g></svg>',\n    'list-num-lower-alpha-rtl': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z\"/><path d=\"M36.5 16c-.9 0-1.5-.5-1.5-1.3s.6-1.3 1.8-1.4h1v-.4c0-.4-.2-.6-.7-.6-.4 0-.7.1-.8.4h-1.1c0-.8.8-1.4 2-1.4S39 12 39 13V16h-1.2v-.6c-.3.4-.8.7-1.4.7Zm.4-.8c.6 0 1-.4 1-.9V14h-1c-.5.1-.7.3-.7.6 0 .4.3.6.7.6ZM33.1 16.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7ZM37.7 26c-.7 0-1.2-.2-1.5-.7v.7H35v-6.3h1.2v2.5c.3-.5.8-.9 1.5-.9 1.1 0 1.8 1 1.8 2.4 0 1.5-.7 2.4-1.8 2.4Zm-.5-3.6c-.6 0-1 .5-1 1.3s.4 1.4 1 1.4c.7 0 1-.6 1-1.4 0-.8-.3-1.3-1-1.3ZM33.2 26.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7zm6 7h-1c-.1-.5-.4-.8-1-.8s-1 .5-1 1.4c0 1 .4 1.4 1 1.4.5 0 .9-.2 1-.7h1c0 1-.8 1.7-2 1.7-1.4 0-2.2-.9-2.2-2.4s.8-2.4 2.2-2.4c1.2 0 2 .7 2 1.7zm-6.1 3c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7z\"/></g></svg>',\n    'list-num-lower-alpha': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/><path d=\"M10.3 15.2c.5 0 1-.4 1-.9V14h-1c-.5.1-.8.3-.8.6 0 .4.3.6.8.6Zm-.4.9c-1 0-1.5-.6-1.5-1.4 0-.8.6-1.3 1.7-1.4h1.1v-.4c0-.4-.2-.6-.7-.6-.5 0-.8.1-.9.4h-1c0-.8.8-1.4 2-1.4 1.1 0 1.8.6 1.8 1.6V16h-1.1v-.6h-.1c-.2.4-.7.7-1.3.7Zm4.6 0c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-3.2 10c-.6 0-1.2-.3-1.4-.8v.7H8.5v-6.3H10v2.5c.3-.5.8-.9 1.4-.9 1.2 0 1.9 1 1.9 2.4 0 1.5-.7 2.4-1.9 2.4Zm-.4-3.7c-.7 0-1 .5-1 1.3s.3 1.4 1 1.4c.6 0 1-.6 1-1.4 0-.8-.4-1.3-1-1.3Zm4 3.7c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-2.2 7h-1.2c0-.5-.4-.8-.9-.8-.6 0-1 .5-1 1.4 0 1 .4 1.4 1 1.4.5 0 .8-.2 1-.7h1c0 1-.8 1.7-2 1.7-1.4 0-2.2-.9-2.2-2.4s.8-2.4 2.2-2.4c1.2 0 2 .7 2 1.7Zm1.8 3c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z\"/></g></svg>',\n    'list-num-lower-greek-rtl': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z\"/><path d=\"M37.4 16c-1.2 0-2-.8-2-2.3 0-1.5.8-2.4 2-2.4.6 0 1 .4 1.3 1v-.9H40v3.2c0 .4.1.5.4.5h.2v.9h-.6c-.6 0-1-.2-1-.7h-.2c-.2.4-.7.8-1.3.8Zm.3-1c.6 0 1-.5 1-1.3s-.4-1.3-1-1.3-1 .5-1 1.3.4 1.4 1 1.4ZM33.3 16.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM36 21.9c0-1.5.8-2.3 2.1-2.3 1.2 0 2 .6 2 1.6 0 .6-.3 1-.9 1.3.9.3 1.3.8 1.3 1.7 0 1.2-.7 1.9-1.8 1.9-.6 0-1.1-.3-1.4-.8v2.2H36V22Zm1.8 1.2v-1h.3c.5 0 .9-.2.9-.7 0-.5-.3-.8-.9-.8-.5 0-.8.3-.8 1v2.2c0 .8.4 1.3 1 1.3s1-.4 1-1-.4-1-1.2-1h-.3ZM33.3 26.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM37.1 34.6 34.8 30h1.4l1.7 3.5 1.7-3.5h1.1l-2.2 4.6v.1c.5.8.7 1.4.7 1.8 0 .4-.2.8-.4 1-.2.2-.6.3-1 .3-.9 0-1.3-.4-1.3-1.2 0-.5.2-1 .5-1.7l.1-.2Zm.7 1a2 2 0 0 0-.4.9c0 .3.1.4.4.4.3 0 .4-.1.4-.4 0-.2-.1-.6-.4-1ZM33.3 36.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z\"/></g></svg>',\n    'list-num-lower-greek': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/><path d=\"M10.5 15c.7 0 1-.5 1-1.3s-.3-1.3-1-1.3c-.5 0-.9.5-.9 1.3s.4 1.4 1 1.4Zm-.3 1c-1.1 0-1.8-.8-1.8-2.3 0-1.5.7-2.4 1.8-2.4.7 0 1.1.4 1.3 1h.1v-.9h1.2v3.2c0 .4.1.5.4.5h.2v.9h-.6c-.6 0-1-.2-1.1-.7h-.1c-.2.4-.7.8-1.4.8Zm5 .1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7Zm-4.9 7v-1h.3c.6 0 1-.2 1-.7 0-.5-.4-.8-1-.8-.5 0-.8.3-.8 1v2.2c0 .8.4 1.3 1.1 1.3.6 0 1-.4 1-1s-.5-1-1.3-1h-.3ZM8.6 22c0-1.5.7-2.3 2-2.3 1.2 0 2 .6 2 1.6 0 .6-.3 1-.8 1.3.8.3 1.3.8 1.3 1.7 0 1.2-.8 1.9-1.9 1.9-.6 0-1.1-.3-1.3-.8v2.2H8.5V22Zm6.2 4.2c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm-4.5 8.5L8 30h1.4l1.7 3.5 1.7-3.5h1.1l-2.2 4.6v.1c.5.8.7 1.4.7 1.8 0 .4-.1.8-.4 1-.2.2-.6.3-1 .3-.9 0-1.3-.4-1.3-1.2 0-.5.2-1 .5-1.7l.1-.2Zm.7 1a2 2 0 0 0-.4.9c0 .3.1.4.4.4.3 0 .4-.1.4-.4 0-.2-.1-.6-.4-1Zm4.5.5c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z\"/></g></svg>',\n    'list-num-lower-roman-rtl': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z\"/><path d=\"M32.9 16v-1.2h-1.3V16H33Zm0 10v-1.2h-1.3V26H33Zm0 10v-1.2h-1.3V36H33Z\"/><path fill-rule=\"nonzero\" d=\"M36 21h-1.5v5H36zM36 31h-1.5v5H36zM39 21h-1.5v5H39zM39 31h-1.5v5H39zM42 31h-1.5v5H42zM36 11h-1.5v5H36zM36 19h-1.5v1H36zM36 29h-1.5v1H36zM39 19h-1.5v1H39zM39 29h-1.5v1H39zM42 29h-1.5v1H42zM36 9h-1.5v1H36z\"/></g></svg>',\n    'list-num-lower-roman': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/><path d=\"M15.1 16v-1.2h1.3V16H15Zm0 10v-1.2h1.3V26H15Zm0 10v-1.2h1.3V36H15Z\"/><path fill-rule=\"nonzero\" d=\"M12 21h1.5v5H12zM12 31h1.5v5H12zM9 21h1.5v5H9zM9 31h1.5v5H9zM6 31h1.5v5H6zM12 11h1.5v5H12zM12 19h1.5v1H12zM12 29h1.5v1H12zM9 19h1.5v1H9zM9 29h1.5v1H9zM6 29h1.5v1H6zM12 9h1.5v1H12z\"/></g></svg>',\n    'list-num-upper-alpha-rtl': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z\"/><path d=\"m39.3 17-.5-1.4h-2l-.5 1.4H35l2-6h1.6l2 6h-1.3Zm-1.6-4.7-.7 2.3h1.6l-.8-2.3ZM33.4 17c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm4.7 9.9h-2.7v-6H38c1.2 0 1.9.6 1.9 1.5 0 .6-.5 1.2-1 1.3.7.1 1.3.7 1.3 1.5 0 1-.8 1.7-2 1.7Zm-1.4-5v1.5h1c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7h-1Zm0 4h1.1c.7 0 1.1-.3 1.1-.8 0-.6-.4-.9-1.1-.9h-1.1V26ZM33 27.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm4.9 10c-1.8 0-2.8-1.1-2.8-3.1s1-3.1 2.8-3.1c1.4 0 2.5.9 2.6 2.2h-1.3c0-.7-.6-1.1-1.3-1.1-1 0-1.6.7-1.6 2s.6 2 1.6 2c.7 0 1.2-.4 1.4-1h1.2c-.1 1.3-1.2 2.2-2.6 2.2Zm-4.5 0c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z\"/></g></svg>',\n    'list-num-upper-alpha': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/><path d=\"m12.6 17-.5-1.4h-2L9.5 17H8.3l2-6H12l2 6h-1.3ZM11 12.3l-.7 2.3h1.6l-.8-2.3Zm4.7 4.8c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7ZM11.4 27H8.7v-6h2.6c1.2 0 1.9.6 1.9 1.5 0 .6-.5 1.2-1 1.3.7.1 1.3.7 1.3 1.5 0 1-.8 1.7-2 1.7ZM10 22v1.5h1c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7h-1Zm0 4H11c.7 0 1.1-.3 1.1-.8 0-.6-.4-.9-1.1-.9H10V26Zm5.4 1.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-4.1 10c-1.8 0-2.8-1.1-2.8-3.1s1-3.1 2.8-3.1c1.4 0 2.5.9 2.6 2.2h-1.3c0-.7-.6-1.1-1.3-1.1-1 0-1.6.7-1.6 2s.6 2 1.6 2c.7 0 1.2-.4 1.4-1h1.2c-.1 1.3-1.2 2.2-2.6 2.2Zm4.5 0c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z\"/></g></svg>',\n    'list-num-upper-roman-rtl': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z\"/><path d=\"M31.6 17v-1.2H33V17h-1.3Zm0 10v-1.2H33V27h-1.3Zm0 10v-1.2H33V37h-1.3Z\"/><path fill-rule=\"nonzero\" d=\"M34.5 20H36v7h-1.5zM34.5 30H36v7h-1.5zM37.5 20H39v7h-1.5zM37.5 30H39v7h-1.5zM40.5 30H42v7h-1.5zM34.5 10H36v7h-1.5z\"/></g></svg>',\n    'list-num-upper-roman': '<svg width=\"48\" height=\"48\"><g fill-rule=\"evenodd\"><path opacity=\".2\" d=\"M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z\"/><path d=\"M15.1 17v-1.2h1.3V17H15Zm0 10v-1.2h1.3V27H15Zm0 10v-1.2h1.3V37H15Z\"/><path fill-rule=\"nonzero\" d=\"M12 20h1.5v7H12zM12 30h1.5v7H12zM9 20h1.5v7H9zM9 30h1.5v7H9zM6 30h1.5v7H6zM12 10h1.5v7H12z\"/></g></svg>',\n    'lock': '<svg width=\"24\" height=\"24\"><path d=\"M16.3 11c.2 0 .3 0 .5.2l.2.6v7.4c0 .3 0 .4-.2.6l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6v-7.4c0-.3 0-.4.2-.6l.5-.2H8V8c0-.8.3-1.5.9-2.1.6-.6 1.3-.9 2.1-.9h2c.8 0 1.5.3 2.1.9.6.6.9 1.3.9 2.1v3h.3ZM10 8v3h4V8a1 1 0 0 0-.3-.7A1 1 0 0 0 13 7h-2a1 1 0 0 0-.7.3 1 1 0 0 0-.3.7Z\" fill-rule=\"evenodd\"/></svg>',\n    'ltr': '<svg width=\"24\" height=\"24\"><path d=\"M11 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 7.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L11 5ZM4.4 16.2 6.2 15l-1.8-1.2a1 1 0 0 1 1.2-1.6l3 2a1 1 0 0 1 0 1.6l-3 2a1 1 0 1 1-1.2-1.6Z\" fill-rule=\"evenodd\"/></svg>',\n    'more-drawer': '<svg width=\"24\" height=\"24\"><path d=\"M6 10a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm12 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm-6 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Z\" fill-rule=\"nonzero\"/></svg>',\n    'new-document': '<svg width=\"24\" height=\"24\"><path d=\"M14.4 3H7a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V7.6L14.4 3ZM17 19H7V5h6v4h4v10Z\" fill-rule=\"nonzero\"/></svg>',\n    'new-tab': '<svg width=\"24\" height=\"24\"><path d=\"m15 13 2-2v8H5V7h8l-2 2H7v8h8v-4Zm4-8v5.5l-2-2-5.6 5.5H10v-1.4L15.5 7l-2-2H19Z\" fill-rule=\"evenodd\"/></svg>',\n    'non-breaking': '<svg width=\"24\" height=\"24\"><path d=\"M11 11H8a1 1 0 1 1 0-2h3V6c0-.6.4-1 1-1s1 .4 1 1v3h3c.6 0 1 .4 1 1s-.4 1-1 1h-3v3c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-3Zm10 4v5H3v-5c0-.6.4-1 1-1s1 .4 1 1v3h14v-3c0-.6.4-1 1-1s1 .4 1 1Z\" fill-rule=\"evenodd\"/></svg>',\n    'notice': '<svg width=\"24\" height=\"24\"><path d=\"M15.5 4 20 8.5v7L15.5 20h-7L4 15.5v-7L8.5 4h7ZM13 17v-2h-2v2h2Zm0-4V7h-2v6h2Z\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"/></svg>',\n    'ordered-list-rtl': '<svg width=\"24\" height=\"24\"><path d=\"M6 17h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2Zm13-1v3.5a.5.5 0 1 1-1 0V5h-.5a.5.5 0 1 1 0-1H19Zm-1 8.8.2.2h1.3a.5.5 0 1 1 0 1h-1.6a1 1 0 0 1-.9-1V13c0-.4.3-.8.6-1l1.2-.4.2-.3a.2.2 0 0 0-.2-.2h-1.3a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h1.6c.5 0 .9.4.9 1v.1c0 .4-.3.8-.6 1l-1.2.4-.2.3Zm2 4.2v2c0 .6-.4 1-1 1h-1.5a.5.5 0 0 1 0-1h1.2a.3.3 0 1 0 0-.6h-1.3a.4.4 0 1 1 0-.8h1.3a.3.3 0 0 0 0-.6h-1.2a.5.5 0 1 1 0-1H19c.6 0 1 .4 1 1Z\" fill-rule=\"evenodd\"/></svg>',\n    'ordered-list': '<svg width=\"24\" height=\"24\"><path d=\"M10 17h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 1 1 0-2ZM6 4v3.5c0 .3-.2.5-.5.5a.5.5 0 0 1-.5-.5V5h-.5a.5.5 0 0 1 0-1H6Zm-1 8.8.2.2h1.3c.3 0 .5.2.5.5s-.2.5-.5.5H4.9a1 1 0 0 1-.9-1V13c0-.4.3-.8.6-1l1.2-.4.2-.3a.2.2 0 0 0-.2-.2H4.5a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h1.6c.5 0 .9.4.9 1v.1c0 .4-.3.8-.6 1l-1.2.4-.2.3ZM7 17v2c0 .6-.4 1-1 1H4.5a.5.5 0 0 1 0-1h1.2c.2 0 .3-.1.3-.3 0-.2-.1-.3-.3-.3H4.4a.4.4 0 1 1 0-.8h1.3c.2 0 .3-.1.3-.3 0-.2-.1-.3-.3-.3H4.5a.5.5 0 1 1 0-1H6c.6 0 1 .4 1 1Z\" fill-rule=\"evenodd\"/></svg>',\n    'orientation': '<svg width=\"24\" height=\"24\"><path d=\"M7.3 6.4 1 13l6.4 6.5 6.5-6.5-6.5-6.5ZM3.7 13l3.6-3.7L11 13l-3.7 3.7-3.6-3.7ZM12 6l2.8 2.7c.3.3.3.8 0 1-.3.4-.9.4-1.2 0L9.2 5.7a.8.8 0 0 1 0-1.2L13.6.2c.3-.3.9-.3 1.2 0 .3.3.3.8 0 1.1L12 4h1a9 9 0 1 1-4.3 16.9l1.5-1.5A7 7 0 1 0 13 6h-1Z\" fill-rule=\"nonzero\"/></svg>',\n    'outdent': '<svg width=\"24\" height=\"24\"><path d=\"M7 5h12c.6 0 1 .4 1 1s-.4 1-1 1H7a1 1 0 1 1 0-2Zm5 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm0 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm-5 4h12a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2Zm1.6-3.8a1 1 0 0 1-1.2 1.6l-3-2a1 1 0 0 1 0-1.6l3-2a1 1 0 0 1 1.2 1.6L6.8 12l1.8 1.2Z\" fill-rule=\"evenodd\"/></svg>',\n    'page-break': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path d=\"M5 11c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h1c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm4 0c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h1c.6 0 1 .4 1 1s-.4 1-1 1h-1a1 1 0 0 1 0-2Zm4 0c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2ZM7 3v5h10V3c0-.6.4-1 1-1s1 .4 1 1v7H5V3c0-.6.4-1 1-1s1 .4 1 1ZM6 22a1 1 0 0 1-1-1v-7h14v7c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-5H7v5c0 .6-.4 1-1 1Z\"/></g></svg>',\n    'paragraph': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M10 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 6.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L10 5Z\"/></svg>',\n    'paste-column-after': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V7h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h7v2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm8 7v12h-6V8h6Zm-1.5 1.5h-3v9h3v-9ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z\"/></svg>',\n    'paste-column-before': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V18c0 1-.8 2-1.9 2H11v-2h7V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v2H4V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm-2 7v12H4V8h6ZM8.5 9.5h-3v9h3v-9ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z\"/></svg>',\n    'paste-row-after': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V11h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h14c0 1-.8 2-1.9 2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm10 11v5H8v-5h14Zm-1.5 1.5h-11v2h11v-2ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z\"/></svg>',\n    'paste-row-before': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" d=\"M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V7h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h12v-4h2v4c0 1-.8 2-1.9 2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm10 7v5H8V8h14Zm-1.5 1.5h-11v2h11v-2ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z\"/></svg>',\n    'paste-text': '<svg width=\"24\" height=\"24\"><path d=\"M18 9V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h3V9h9ZM9 20H6a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.2A3 3 0 0 1 12 1a3 3 0 0 1 2.8 2H18a2 2 0 0 1 2 2v4h1v12H9v-1Zm1.5-9.5v9h9v-9h-9ZM12 3a1 1 0 0 0-1 1c0 .5.4 1 1 1s1-.5 1-1-.4-1-1-1Zm0 9h6v2h-.5l-.5-1h-1v4h.8v1h-3.6v-1h.8v-4h-1l-.5 1H12v-2Z\" fill-rule=\"nonzero\"/></svg>',\n    'paste': '<svg width=\"24\" height=\"24\"><path d=\"M18 9V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h3V9h9ZM9 20H6a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.2A3 3 0 0 1 12 1a3 3 0 0 1 2.8 2H18a2 2 0 0 1 2 2v4h1v12H9v-1Zm1.5-9.5v9h9v-9h-9ZM12 3a1 1 0 0 0-1 1c0 .5.4 1 1 1s1-.5 1-1-.4-1-1-1Z\" fill-rule=\"nonzero\"/></svg>',\n    'permanent-pen': '<svg width=\"24\" height=\"24\"><path d=\"M10.5 17.5 8 20H3v-3l3.5-3.5a2 2 0 0 1 0-3L14 3l1 1-7.3 7.3a1 1 0 0 0 0 1.4l3.6 3.6c.4.4 1 .4 1.4 0L20 9l1 1-7.6 7.6a2 2 0 0 1-2.8 0l-.1-.1Z\" fill-rule=\"nonzero\"/></svg>',\n    'plus': '<svg width=\"24\" height=\"24\"><path d=\"M12 4c.5 0 1 .4 1 .9V11h6a1 1 0 0 1 .1 2H13v6a1 1 0 0 1-2 .1V13H5a1 1 0 0 1-.1-2H11V5c0-.6.4-1 1-1Z\"/></svg>',\n    'preferences': '<svg width=\"24\" height=\"24\"><path d=\"m20.1 13.5-1.9.2a5.8 5.8 0 0 1-.6 1.5l1.2 1.5c.4.4.3 1 0 1.4l-.7.7a1 1 0 0 1-1.4 0l-1.5-1.2a6.2 6.2 0 0 1-1.5.6l-.2 1.9c0 .5-.5.9-1 .9h-1a1 1 0 0 1-1-.9l-.2-1.9a5.8 5.8 0 0 1-1.5-.6l-1.5 1.2a1 1 0 0 1-1.4 0l-.7-.7a1 1 0 0 1 0-1.4l1.2-1.5a6.2 6.2 0 0 1-.6-1.5l-1.9-.2a1 1 0 0 1-.9-1v-1c0-.5.4-1 .9-1l1.9-.2a5.8 5.8 0 0 1 .6-1.5L5.2 7.3a1 1 0 0 1 0-1.4l.7-.7a1 1 0 0 1 1.4 0l1.5 1.2a6.2 6.2 0 0 1 1.5-.6l.2-1.9c0-.5.5-.9 1-.9h1c.5 0 1 .4 1 .9l.2 1.9a5.8 5.8 0 0 1 1.5.6l1.5-1.2a1 1 0 0 1 1.4 0l.7.7c.3.4.4 1 0 1.4l-1.2 1.5a6.2 6.2 0 0 1 .6 1.5l1.9.2c.5 0 .9.5.9 1v1c0 .5-.4 1-.9 1ZM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z\" fill-rule=\"evenodd\"/></svg>',\n    'preview': '<svg width=\"24\" height=\"24\"><path d=\"M3.5 12.5c.5.8 1.1 1.6 1.8 2.3 2 2 4.2 3.2 6.7 3.2s4.7-1.2 6.7-3.2a16.2 16.2 0 0 0 2.1-2.8 15.7 15.7 0 0 0-2.1-2.8c-2-2-4.2-3.2-6.7-3.2a9.3 9.3 0 0 0-6.7 3.2A16.2 16.2 0 0 0 3.2 12c0 .2.2.3.3.5Zm-2.4-1 .7-1.2L4 7.8C6.2 5.4 8.9 4 12 4c3 0 5.8 1.4 8.1 3.8a18.2 18.2 0 0 1 2.8 3.7v1l-.7 1.2-2.1 2.5c-2.3 2.4-5 3.8-8.1 3.8-3 0-5.8-1.4-8.1-3.8a18.2 18.2 0 0 1-2.8-3.7 1 1 0 0 1 0-1Zm12-3.3a2 2 0 1 0 2.7 2.6 4 4 0 1 1-2.6-2.6Z\" fill-rule=\"nonzero\"/></svg>',\n    'print': '<svg width=\"24\" height=\"24\"><path d=\"M18 8H6a3 3 0 0 0-3 3v6h2v3h14v-3h2v-6a3 3 0 0 0-3-3Zm-1 10H7v-4h10v4Zm.5-5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5Zm.5-8H6v2h12V5Z\" fill-rule=\"nonzero\"/></svg>',\n    'quote': '<svg width=\"24\" height=\"24\"><path d=\"M7.5 17h.9c.4 0 .7-.2.9-.6L11 13V8c0-.6-.4-1-1-1H6a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h2l-1.3 2.7a1 1 0 0 0 .8 1.3Zm8 0h.9c.4 0 .7-.2.9-.6L19 13V8c0-.6-.4-1-1-1h-4a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h2l-1.3 2.7a1 1 0 0 0 .8 1.3Z\" fill-rule=\"nonzero\"/></svg>',\n    'redo': '<svg width=\"24\" height=\"24\"><path d=\"M17.6 10H12c-2.8 0-4.4 1.4-4.9 3.5-.4 2 .3 4 1.4 4.6a1 1 0 1 1-1 1.8c-2-1.2-2.9-4.1-2.3-6.8.6-3 3-5.1 6.8-5.1h5.6l-3.3-3.3a1 1 0 1 1 1.4-1.4l5 5a1 1 0 0 1 0 1.4l-5 5a1 1 0 0 1-1.4-1.4l3.3-3.3Z\" fill-rule=\"nonzero\"/></svg>',\n    'reload': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><path d=\"m5 22.1-1.2-4.7v-.2a1 1 0 0 1 1-1l5 .4a1 1 0 1 1-.2 2l-2.2-.2a7.8 7.8 0 0 0 8.4.2 7.5 7.5 0 0 0 3.5-6.4 1 1 0 1 1 2 0 9.5 9.5 0 0 1-4.5 8 9.9 9.9 0 0 1-10.2 0l.4 1.4a1 1 0 1 1-2 .5ZM13.6 7.4c0-.5.5-1 1-.9l2.8.2a8 8 0 0 0-9.5-1 7.5 7.5 0 0 0-3.6 7 1 1 0 0 1-2 0 9.5 9.5 0 0 1 4.5-8.6 10 10 0 0 1 10.9.3l-.3-1a1 1 0 0 1 2-.5l1.1 4.8a1 1 0 0 1-1 1.2l-5-.4a1 1 0 0 1-.9-1Z\"/></g></svg>',\n    'remove-formatting': '<svg width=\"24\" height=\"24\"><path d=\"M13.2 6a1 1 0 0 1 0 .2l-2.6 10a1 1 0 0 1-1 .8h-.2a.8.8 0 0 1-.8-1l2.6-10H8a1 1 0 1 1 0-2h9a1 1 0 0 1 0 2h-3.8ZM5 18h7a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Zm13 1.5L16.5 18 15 19.5a.7.7 0 0 1-1-1l1.5-1.5-1.5-1.5a.7.7 0 0 1 1-1l1.5 1.5 1.5-1.5a.7.7 0 0 1 1 1L17.5 17l1.5 1.5a.7.7 0 0 1-1 1Z\" fill-rule=\"evenodd\"/></svg>',\n    'remove': '<svg width=\"24\" height=\"24\"><path d=\"M16 7h3a1 1 0 0 1 0 2h-1v9a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V9H5a1 1 0 1 1 0-2h3V6a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v1Zm-2 0V6c0-.6-.4-1-1-1h-2a1 1 0 0 0-1 1v1h4Zm2 2H8v9c0 .6.4 1 1 1h6c.6 0 1-.4 1-1V9Zm-7 3a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0v-4Zm4 0a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0v-4Z\" fill-rule=\"nonzero\"/></svg>',\n    'resize-handle': '<svg width=\"10\" height=\"10\"><g fill-rule=\"nonzero\"><path d=\"M8.1 1.1A.5.5 0 1 1 9 2l-7 7A.5.5 0 1 1 1 8l7-7ZM8.1 5.1A.5.5 0 1 1 9 6l-3 3A.5.5 0 1 1 5 8l3-3Z\"/></g></svg>',\n    'resize': '<svg width=\"24\" height=\"24\"><path d=\"M4 5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h6c.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7a1 1 0 0 1-.7.3H7.4L18 16.6V13c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7v6c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-6a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h3.6L6 7.4V11c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.7-.3A1 1 0 0 1 4 11V5Z\" fill-rule=\"evenodd\"/></svg>',\n    'restore-draft': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path d=\"M17 13c0 .6-.4 1-1 1h-4V8c0-.6.4-1 1-1s1 .4 1 1v4h2c.6 0 1 .4 1 1Z\"/><path d=\"M4.7 10H9a1 1 0 0 1 0 2H3a1 1 0 0 1-1-1V5a1 1 0 1 1 2 0v3l2.5-2.4a9.2 9.2 0 0 1 10.8-1.5A9 9 0 0 1 13.4 21c-2.4.1-4.7-.7-6.5-2.2a1 1 0 1 1 1.3-1.5 7.2 7.2 0 0 0 11.6-3.7 7 7 0 0 0-3.5-7.7A7.2 7.2 0 0 0 8 7L4.7 10Z\" fill-rule=\"nonzero\"/></g></svg>',\n    'rotate-left': '<svg width=\"24\" height=\"24\"><path d=\"M4.7 10H9a1 1 0 0 1 0 2H3a1 1 0 0 1-1-1V5a1 1 0 1 1 2 0v3l2.5-2.4a9.2 9.2 0 0 1 10.8-1.5A9 9 0 0 1 13.4 21c-2.4.1-4.7-.7-6.5-2.2a1 1 0 1 1 1.3-1.5 7.2 7.2 0 0 0 11.6-3.7 7 7 0 0 0-3.5-7.7A7.2 7.2 0 0 0 8 7L4.7 10Z\" fill-rule=\"nonzero\"/></svg>',\n    'rotate-right': '<svg width=\"24\" height=\"24\"><path d=\"M20 8V5a1 1 0 0 1 2 0v6c0 .6-.4 1-1 1h-6a1 1 0 0 1 0-2h4.3L16 7A7.2 7.2 0 0 0 7.7 6a7 7 0 0 0 3 13.1c1.9.1 3.7-.5 5-1.7a1 1 0 0 1 1.4 1.5A9.2 9.2 0 0 1 2.2 14c-.9-3.9 1-8 4.5-9.9 3.5-1.9 8-1.3 10.8 1.5L20 8Z\" fill-rule=\"nonzero\"/></svg>',\n    'rtl': '<svg width=\"24\" height=\"24\"><path d=\"M8 5h8v2h-2v12h-2V7h-2v12H8v-7c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 4.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L8 5Zm12 11.2a1 1 0 1 1-1 1.6l-3-2a1 1 0 0 1 0-1.6l3-2a1 1 0 1 1 1 1.6L18.4 15l1.8 1.2Z\" fill-rule=\"evenodd\"/></svg>',\n    'save': '<svg width=\"24\" height=\"24\"><path d=\"M5 16h14a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2c0-1.1.9-2 2-2Zm0 2v2h14v-2H5Zm10 0h2v2h-2v-2Zm-4-6.4L8.7 9.3a1 1 0 1 0-1.4 1.4l4 4c.4.4 1 .4 1.4 0l4-4a1 1 0 1 0-1.4-1.4L13 11.6V4a1 1 0 0 0-2 0v7.6Z\" fill-rule=\"nonzero\"/></svg>',\n    'search': '<svg width=\"24\" height=\"24\"><path d=\"M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Z\" fill-rule=\"nonzero\"/></svg>',\n    'select-all': '<svg width=\"24\" height=\"24\"><path d=\"M3 5h2V3a2 2 0 0 0-2 2Zm0 8h2v-2H3v2Zm4 8h2v-2H7v2ZM3 9h2V7H3v2Zm10-6h-2v2h2V3Zm6 0v2h2a2 2 0 0 0-2-2ZM5 21v-2H3c0 1.1.9 2 2 2Zm-2-4h2v-2H3v2ZM9 3H7v2h2V3Zm2 18h2v-2h-2v2Zm8-8h2v-2h-2v2Zm0 8a2 2 0 0 0 2-2h-2v2Zm0-12h2V7h-2v2Zm0 8h2v-2h-2v2Zm-4 4h2v-2h-2v2Zm0-16h2V3h-2v2ZM7 17h10V7H7v10Zm2-8h6v6H9V9Z\" fill-rule=\"nonzero\"/></svg>',\n    'selected': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M6 4h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2Zm3.6 10.9L7 12.3a.7.7 0 0 0-1 1L9.6 17 18 8.6a.7.7 0 0 0 0-1 .7.7 0 0 0-1 0l-7.4 7.3Z\"/></svg>',\n    'settings': '<svg width=\"24\" height=\"24\"><path d=\"M11 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8v.3c0 .2 0 .3-.2.5l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V8H5a1 1 0 1 1 0-2h2v-.3c0-.2 0-.3.2-.5l.5-.2h2.5c.3 0 .4 0 .6.2l.2.5V6ZM8 8h2V6H8v2Zm9 2.8v.2h2c.6 0 1 .4 1 1s-.4 1-1 1h-2v.3c0 .2 0 .3-.2.5l-.6.2h-2.4c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V13H5a1 1 0 0 1 0-2h8v-.3c0-.2 0-.3.2-.5l.6-.2h2.4c.3 0 .4 0 .6.2l.2.6ZM14 13h2v-2h-2v2Zm-3 2.8v.2h8c.6 0 1 .4 1 1s-.4 1-1 1h-8v.3c0 .2 0 .3-.2.5l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V18H5a1 1 0 0 1 0-2h2v-.3c0-.2 0-.3.2-.5l.5-.2h2.5c.3 0 .4 0 .6.2l.2.6ZM8 18h2v-2H8v2Z\" fill-rule=\"evenodd\"/></svg>',\n    'sharpen': '<svg width=\"24\" height=\"24\"><path d=\"m16 6 4 4-8 9-8-9 4-4h8Zm-4 10.2 5.5-6.2-.1-.1H12v-.3h5.1l-.2-.2H12V9h4.6l-.2-.2H12v-.3h4.1l-.2-.2H12V8h3.6l-.2-.2H8.7L6.5 10l.1.1H12v.3H6.9l.2.2H12v.3H7.3l.2.2H12v.3H7.7l.3.2h4v.3H8.2l.2.2H12v.3H8.6l.3.2H12v.3H9l.3.2H12v.3H9.5l.2.2H12v.3h-2l.2.2H12v.3h-1.6l.2.2H12v.3h-1.1l.2.2h.9v.3h-.7l.2.2h.5v.3h-.3l.3.2Z\" fill-rule=\"evenodd\"/></svg>',\n    'sourcecode': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><path d=\"M9.8 15.7c.3.3.3.8 0 1-.3.4-.9.4-1.2 0l-4.4-4.1a.8.8 0 0 1 0-1.2l4.4-4.2c.3-.3.9-.3 1.2 0 .3.3.3.8 0 1.1L6 12l3.8 3.7ZM14.2 15.7c-.3.3-.3.8 0 1 .4.4.9.4 1.2 0l4.4-4.1c.3-.3.3-.9 0-1.2l-4.4-4.2a.8.8 0 0 0-1.2 0c-.3.3-.3.8 0 1.1L18 12l-3.8 3.7Z\"/></g></svg>',\n    'spell-check': '<svg width=\"24\" height=\"24\"><path d=\"M6 8v3H5V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h2c.3 0 .5.1.7.3.2.2.3.4.3.7v6H8V8H6Zm0-3v2h2V5H6Zm13 0h-3v5h3v1h-3a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h3v1Zm-5 1.5-.1.7c-.1.2-.3.3-.6.3.3 0 .5.1.6.3l.1.7V10c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-3V4h3c.3 0 .5.1.7.3.2.2.3.4.3.7v1.5ZM13 10V8h-2v2h2Zm0-3V5h-2v2h2Zm3 5 1 1-6.5 7L7 15.5l1.3-1 2.2 2.2L16 12Z\" fill-rule=\"evenodd\"/></svg>',\n    'strike-through': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path d=\"M15.6 8.5c-.5-.7-1-1.1-1.3-1.3-.6-.4-1.3-.6-2-.6-2.7 0-2.8 1.7-2.8 2.1 0 1.6 1.8 2 3.2 2.3 4.4.9 4.6 2.8 4.6 3.9 0 1.4-.7 4.1-5 4.1A6.2 6.2 0 0 1 7 16.4l1.5-1.1c.4.6 1.6 2 3.7 2 1.6 0 2.5-.4 3-1.2.4-.8.3-2-.8-2.6-.7-.4-1.6-.7-2.9-1-1-.2-3.9-.8-3.9-3.6C7.6 6 10.3 5 12.4 5c2.9 0 4.2 1.6 4.7 2.4l-1.5 1.1Z\"/><path d=\"M5 11h14a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z\" fill-rule=\"nonzero\"/></g></svg>',\n    'subscript': '<svg width=\"24\" height=\"24\"><path d=\"m10.4 10 4.6 4.6-1.4 1.4L9 11.4 4.4 16 3 14.6 7.6 10 3 5.4 4.4 4 9 8.6 13.6 4 15 5.4 10.4 10ZM21 19h-5v-1l1-.8 1.7-1.6c.3-.4.5-.8.5-1.2 0-.3 0-.6-.2-.7-.2-.2-.5-.3-.9-.3a2 2 0 0 0-.8.2l-.7.3-.4-1.1 1-.6 1.2-.2c.8 0 1.4.3 1.8.7.4.4.6.9.6 1.5s-.2 1.1-.5 1.6a8 8 0 0 1-1.3 1.3l-.6.6h2.6V19Z\" fill-rule=\"nonzero\"/></svg>',\n    'superscript': '<svg width=\"24\" height=\"24\"><path d=\"M15 9.4 10.4 14l4.6 4.6-1.4 1.4L9 15.4 4.4 20 3 18.6 7.6 14 3 9.4 4.4 8 9 12.6 13.6 8 15 9.4Zm5.9 1.6h-5v-1l1-.8 1.7-1.6c.3-.5.5-.9.5-1.3 0-.3 0-.5-.2-.7-.2-.2-.5-.3-.9-.3l-.8.2-.7.4-.4-1.2c.2-.2.5-.4 1-.5.3-.2.8-.2 1.2-.2.8 0 1.4.2 1.8.6.4.4.6 1 .6 1.6 0 .5-.2 1-.5 1.5l-1.3 1.4-.6.5h2.6V11Z\" fill-rule=\"nonzero\"/></svg>',\n    'table-caption': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><rect width=\"12\" height=\"2\" x=\"3\" y=\"4\" rx=\"1\"/><path d=\"M19 8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2h14ZM5 15v3h6v-3H5Zm14 0h-6v3h6v-3Zm0-5h-6v3h6v-3ZM5 13h6v-3H5v3Z\"/></g></svg>',\n    'table-cell-classes': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path fill-rule=\"nonzero\" d=\"M13 4v9H3V6c0-1.1.9-2 2-2h8Zm-2 2H5v5h6V6Z\"/><path fill-rule=\"nonzero\" d=\"M13 4h6a2 2 0 0 1 2 2v7h-8v-2h6V6h-6V4Z\" opacity=\".2\"/><path d=\"m18 20-2.6 1.6.7-3-2.4-2 3.1-.2 1.2-2.9 1.2 2.9 3.1.2-2.4 2 .7 3z\"/><path fill-rule=\"nonzero\" d=\"M3 13v5c0 1.1.9 2 2 2h8v-7h-2v5H5v-5H3Z\" opacity=\".2\"/></g></svg>',\n    'table-cell-properties': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9H5v5h6v-5Zm8 0h-6v5h6v-5Zm-8-7H5v5h6V6Z\"/></svg>',\n    'table-cell-select-all': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v12h14V6Z\"/><path d=\"M13 6v5h6v2h-6v5h-2v-5H5v-2h6V6h2Z\" opacity=\".2\"/></g></svg>',\n    'table-cell-select-inner': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v12h14V6Z\" opacity=\".2\"/><path d=\"M13 6v5h6v2h-6v5h-2v-5H5v-2h6V6h2Z\"/></g></svg>',\n    'table-classes': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v7h-8v7H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9H5v5h6v-5Zm8-7h-6v5h6V6Zm-8 0H5v5h6V6Z\"/><path d=\"m18 20-2.6 1.6.7-3-2.4-2 3.1-.2 1.2-2.9 1.2 2.9 3.1.2-2.4 2 .7 3z\"/></g></svg>',\n    'table-delete-column': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-4 4h-2V6h-2v2H9V6H5v12h4v-2h2v2h2v-2h2v2h4V6h-4v2Zm.3.5 1 1.2-3 2.3 3 2.3-1 1.2L12 13l-3.3 2.6-1-1.2 3-2.3-3-2.3 1-1.2L12 11l3.3-2.5Z\"/></svg>',\n    'table-delete-row': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v3h2.5v2H5v2h2.5v2H5v3h14v-3h-2.5v-2H19v-2h-2.5V9H19V6Zm-4.7 1.8 1.2 1L13 12l2.6 3.3-1.2 1-2.3-3-2.3 3-1.2-1L11 12 8.5 8.7l1.2-1 2.3 3 2.3-3Z\"/></svg>',\n    'table-delete-table': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><path d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 6v12h14V6H5Z\"/><path d=\"m14.4 8.6 1.1 1-2.4 2.4 2.4 2.4-1.1 1.1-2.4-2.4-2.4 2.4-1-1.1 2.3-2.4-2.3-2.4 1-1 2.4 2.3z\"/></g></svg>',\n    'table-insert-column-after': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M20 4c.6 0 1 .4 1 1v2a1 1 0 0 1-2 0V6h-8v12h8v-1a1 1 0 0 1 2 0v2c0 .5-.4 1-.9 1H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h15ZM9 13H5v5h4v-5Zm7-5c.5 0 1 .4 1 .9V11h2a1 1 0 0 1 .1 2H17v2a1 1 0 0 1-2 .1V13h-2a1 1 0 0 1-.1-2H15V9c0-.6.4-1 1-1ZM9 6H5v5h4V6Z\"/></svg>',\n    'table-insert-column-before': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a1 1 0 0 1-1-1v-2a1 1 0 0 1 2 0v1h8V6H5v1a1 1 0 1 1-2 0V5c0-.6.4-1 1-1h15Zm0 9h-4v5h4v-5ZM8 8c.5 0 1 .4 1 .9V11h2a1 1 0 0 1 .1 2H9v2a1 1 0 0 1-2 .1V13H5a1 1 0 0 1-.1-2H7V9c0-.6.4-1 1-1Zm11-2h-4v5h4V6Z\"/></svg>',\n    'table-insert-row-above': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M6 4a1 1 0 1 1 0 2H5v6h14V6h-1a1 1 0 0 1 0-2h2c.6 0 1 .4 1 1v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5c0-.6.4-1 1-1h2Zm5 10H5v4h6v-4Zm8 0h-6v4h6v-4ZM12 3c.5 0 1 .4 1 .9V6h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 .1V8H9a1 1 0 0 1 0-2h2V4c0-.6.4-1 1-1Z\"/></svg>',\n    'table-insert-row-after': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M12 13c.5 0 1 .4 1 .9V16h2a1 1 0 0 1 .1 2H13v2a1 1 0 0 1-2 .1V18H9a1 1 0 0 1-.1-2H11v-2c0-.6.4-1 1-1Zm6 7a1 1 0 0 1 0-2h1v-6H5v6h1a1 1 0 0 1 0 2H4a1 1 0 0 1-1-1V6c0-1.1.9-2 2-2h14a2 2 0 0 1 2 2v13c0 .5-.4 1-.9 1H18ZM11 6H5v4h6V6Zm8 0h-6v4h6V6Z\"/></svg>',\n    'table-left-header': '<svg width=\"24\" height=\"24\"><path d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 9h-4v5h4v-5Zm-6 0H9v5h4v-5Zm0-7H9v5h4V6Zm6 0h-4v5h4V6Z\"/></svg>',\n    'table-merge-cells': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 15.5V18h3v-2.5H5Zm14-5h-9V18h9v-7.5ZM19 6h-4v2.5h4V6ZM8 6H5v2.5h3V6Zm5 0h-3v2.5h3V6Zm-8 7.5h3v-3H5v3Z\"/></svg>',\n    'table-row-numbering-rtl': '<svg width=\"24\" height=\"24\"><path d=\"M6 4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6Zm0 12h8v3H6v-3Zm11 0c.6 0 1 .4 1 1v1a1 1 0 0 1-2 0v-1c0-.6.4-1 1-1ZM6 11h8v3H6v-3Zm11 0c.6 0 1 .4 1 1v1a1 1 0 0 1-2 0v-1c0-.6.4-1 1-1ZM6 6h8v3H6V6Zm11 0c.6 0 1 .4 1 1v1a1 1 0 1 1-2 0V7c0-.6.4-1 1-1Z\"/></svg>',\n    'table-row-numbering': '<svg width=\"24\" height=\"24\"><path d=\"M18 4a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h12Zm0 12h-8v3h8v-3ZM7 16a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1c0-.6-.4-1-1-1Zm11-5h-8v3h8v-3ZM7 11a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1c0-.6-.4-1-1-1Zm11-5h-8v3h8V6ZM7 6a1 1 0 0 0-1 1v1a1 1 0 1 0 2 0V7c0-.6-.4-1-1-1Z\"/></svg>',\n    'table-row-properties': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 15v3h6v-3H5Zm14 0h-6v3h6v-3Zm0-9h-6v3h6V6ZM5 9h6V6H5v3Z\"/></svg>',\n    'table-split-cells': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM8 15.5H5V18h3v-2.5Zm11-5h-9V18h9v-7.5Zm-2.5 1 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2-2-2 1-1 2 2 2-2Zm-8.5-1H5v3h3v-3ZM19 6h-4v2.5h4V6ZM8 6H5v2.5h3V6Zm5 0h-3v2.5h3V6Z\"/></svg>',\n    'table-top-header': '<svg width=\"24\" height=\"24\"><path d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 11H5v3h6v-3Zm8 0h-6v3h6v-3Zm0-5h-6v3h6v-3ZM5 13h6v-3H5v3Z\"/></svg>',\n    'table': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 14v4h6v-4H5Zm14 0h-6v4h6v-4Zm0-6h-6v4h6V8ZM5 12h6V8H5v4Z\"/></svg>',\n    'template': '<svg width=\"24\" height=\"24\"><path d=\"M19 19v-1H5v1h14ZM9 16v-4a5 5 0 1 1 6 0v4h4a2 2 0 0 1 2 2v3H3v-3c0-1.1.9-2 2-2h4Zm4 0v-5l.8-.6a3 3 0 1 0-3.6 0l.8.6v5h2Z\" fill-rule=\"nonzero\"/></svg>',\n    'temporary-placeholder': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path d=\"M9 7.6V6h2.5V4.5a.5.5 0 1 1 1 0V6H15v1.6a8 8 0 1 1-6 0Zm-2.6 5.3a.5.5 0 0 0 .3.6c.3 0 .6 0 .6-.3l.1-.2a5 5 0 0 1 3.3-2.8c.3-.1.4-.4.4-.6-.1-.3-.4-.5-.6-.4a6 6 0 0 0-4.1 3.7Z\"/><circle cx=\"14\" cy=\"4\" r=\"1\"/><circle cx=\"12\" cy=\"2\" r=\"1\"/><circle cx=\"10\" cy=\"4\" r=\"1\"/></g></svg>',\n    'text-color': '<svg width=\"24\" height=\"24\"><g fill-rule=\"evenodd\"><path id=\"tox-icon-text-color__color\" d=\"M3 18h18v3H3z\"/><path d=\"M8.7 16h-.8a.5.5 0 0 1-.5-.6l2.7-9c.1-.3.3-.4.5-.4h2.8c.2 0 .4.1.5.4l2.7 9a.5.5 0 0 1-.5.6h-.8a.5.5 0 0 1-.4-.4l-.7-2.2c0-.3-.3-.4-.5-.4h-3.4c-.2 0-.4.1-.5.4l-.7 2.2c0 .3-.2.4-.4.4Zm2.6-7.6-.6 2a.5.5 0 0 0 .5.6h1.6a.5.5 0 0 0 .5-.6l-.6-2c0-.3-.3-.4-.5-.4h-.4c-.2 0-.4.1-.5.4Z\"/></g></svg>',\n    'toc': '<svg width=\"24\" height=\"24\"><path d=\"M5 5c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 1 1 0-2Zm3 0h11c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 1 1 0-2Zm-3 8c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h11c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm0-4c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 1 1 0-2Zm3 0h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm-3 8c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Z\" fill-rule=\"evenodd\"/></svg>',\n    'translate': '<svg width=\"24\" height=\"24\"><path d=\"m12.7 14.3-.3.7-.4.7-2.2-2.2-3.1 3c-.3.4-.8.4-1 0a.7.7 0 0 1 0-1l3.1-3A12.4 12.4 0 0 1 6.7 9H8a10.1 10.1 0 0 0 1.7 2.4c.5-.5 1-1.1 1.4-1.8l.9-2H4.7a.7.7 0 1 1 0-1.5h4.4v-.7c0-.4.3-.8.7-.8.4 0 .7.4.7.8v.7H15c.4 0 .8.3.8.7 0 .4-.4.8-.8.8h-1.4a12.3 12.3 0 0 1-1 2.4 13.5 13.5 0 0 1-1.7 2.3l1.9 1.8Zm4.3-3 2.7 7.3a.5.5 0 0 1-.4.7 1 1 0 0 1-1-.7l-.6-1.5h-3.4l-.6 1.5a1 1 0 0 1-1 .7.5.5 0 0 1-.4-.7l2.7-7.4a1 1 0 0 1 2 0Zm-2.2 4.4h2.4L16 12.5l-1.2 3.2Z\" fill-rule=\"evenodd\"/></svg>',\n    'typography': '<svg width=\"24\" height=\"24\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M17 5a1 1 0 1 1 0 2h-4v11a1 1 0 1 1-2 0V7H7a1 1 0 0 1 0-2h10Z\"/><path d=\"m17.5 14 .8-1.7 1.7-.8-1.7-.8-.8-1.7-.8 1.7-1.7.8 1.7.8.8 1.7ZM7 14l1 2 2 1-2 1-1 2-1-2-2-1 2-1 1-2Z\"/></svg>',\n    'underline': '<svg width=\"24\" height=\"24\"><path d=\"M16 5c.6 0 1 .4 1 1v5.5a4 4 0 0 1-.4 1.8l-1 1.4a5.3 5.3 0 0 1-5.5 1 5 5 0 0 1-1.6-1c-.5-.4-.8-.9-1.1-1.4a4 4 0 0 1-.4-1.8V6c0-.6.4-1 1-1s1 .4 1 1v5.5c0 .3 0 .6.2 1l.6.7a3.3 3.3 0 0 0 2.2.8 3.4 3.4 0 0 0 2.2-.8c.3-.2.4-.5.6-.8l.2-.9V6c0-.6.4-1 1-1ZM8 17h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Z\" fill-rule=\"evenodd\"/></svg>',\n    'undo': '<svg width=\"24\" height=\"24\"><path d=\"M6.4 8H12c3.7 0 6.2 2 6.8 5.1.6 2.7-.4 5.6-2.3 6.8a1 1 0 0 1-1-1.8c1.1-.6 1.8-2.7 1.4-4.6-.5-2.1-2.1-3.5-4.9-3.5H6.4l3.3 3.3a1 1 0 1 1-1.4 1.4l-5-5a1 1 0 0 1 0-1.4l5-5a1 1 0 0 1 1.4 1.4L6.4 8Z\" fill-rule=\"nonzero\"/></svg>',\n    'unlink': '<svg width=\"24\" height=\"24\"><path d=\"M6.2 12.3a1 1 0 0 1 1.4 1.4l-2 2a2 2 0 1 0 2.6 2.8l4.8-4.8a1 1 0 0 0 0-1.4 1 1 0 1 1 1.4-1.3 2.9 2.9 0 0 1 0 4L9.6 20a3.9 3.9 0 0 1-5.5-5.5l2-2Zm11.6-.6a1 1 0 0 1-1.4-1.4l2.1-2a2 2 0 1 0-2.7-2.8L11 10.3a1 1 0 0 0 0 1.4A1 1 0 1 1 9.6 13a2.9 2.9 0 0 1 0-4L14.4 4a3.9 3.9 0 0 1 5.5 5.5l-2 2ZM7.6 6.3a.8.8 0 0 1-1 1.1L3.3 4.2a.7.7 0 1 1 1-1l3.2 3.1ZM5.1 8.6a.8.8 0 0 1 0 1.5H3a.8.8 0 0 1 0-1.5H5Zm5-3.5a.8.8 0 0 1-1.5 0V3a.8.8 0 0 1 1.5 0V5Zm6 11.8a.8.8 0 0 1 1-1l3.2 3.2a.8.8 0 0 1-1 1L16 17Zm-2.2 2a.8.8 0 0 1 1.5 0V21a.8.8 0 0 1-1.5 0V19Zm5-3.5a.7.7 0 1 1 0-1.5H21a.8.8 0 0 1 0 1.5H19Z\" fill-rule=\"nonzero\"/></svg>',\n    'unlock': '<svg width=\"24\" height=\"24\"><path d=\"M16 5c.8 0 1.5.3 2.1.9.6.6.9 1.3.9 2.1v3h-2V8a1 1 0 0 0-.3-.7A1 1 0 0 0 16 7h-2a1 1 0 0 0-.7.3 1 1 0 0 0-.3.7v3h.3c.2 0 .3 0 .5.2l.2.6v7.4c0 .3 0 .4-.2.6l-.6.2H4.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6v-7.4c0-.3 0-.4.2-.6l.5-.2H11V8c0-.8.3-1.5.9-2.1.6-.6 1.3-.9 2.1-.9h2Z\" fill-rule=\"evenodd\"/></svg>',\n    'unordered-list': '<svg width=\"24\" height=\"24\"><path d=\"M11 5h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2ZM4.5 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Zm0 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Zm0 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Z\" fill-rule=\"evenodd\"/></svg>',\n    'unselected': '<svg width=\"24\" height=\"24\"><path fill-rule=\"nonzero\" d=\"M6 4h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2Zm0 1a1 1 0 0 0-1 1v12c0 .6.4 1 1 1h12c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H6Z\"/></svg>',\n    'upload': '<svg width=\"24\" height=\"24\"><path d=\"M18 19v-2a1 1 0 0 1 2 0v3c0 .6-.4 1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 2 0v2h12ZM11 6.4 8.7 8.7a1 1 0 0 1-1.4-1.4l4-4a1 1 0 0 1 1.4 0l4 4a1 1 0 1 1-1.4 1.4L13 6.4V16a1 1 0 0 1-2 0V6.4Z\" fill-rule=\"nonzero\"/></svg>',\n    'user': '<svg width=\"24\" height=\"24\"><path d=\"M12 24a12 12 0 1 1 0-24 12 12 0 0 1 0 24Zm-8.7-5.3a11 11 0 0 0 17.4 0C19.4 16.3 14.6 15 12 15c-2.6 0-7.4 1.3-8.7 3.7ZM12 13c2.2 0 4-2 4-4.5S14.2 4 12 4 8 6 8 8.5 9.8 13 12 13Z\" fill-rule=\"nonzero\"/></svg>',\n    'vertical-align': '<svg width=\"24\" height=\"24\"><g fill-rule=\"nonzero\"><rect width=\"18\" height=\"2\" x=\"3\" y=\"11\" rx=\"1\"/><path d=\"M12 2c.6 0 1 .4 1 1v4l2-1.3a1 1 0 0 1 1.2 1.5l-.1.1-4.1 3-4-3a1 1 0 0 1 1-1.7l2 1.5V3c0-.6.4-1 1-1zm0 11.8 4 2.9a1 1 0 0 1-1 1.7l-2-1.5V21c0 .5-.4 1-.9 1H12a1 1 0 0 1-1-1v-4l-2 1.3a1 1 0 0 1-1.2-.1l-.1-.1a1 1 0 0 1 .1-1.3l.1-.1 4.1-3z\"/></g></svg>',\n    'visualblocks': '<svg width=\"24\" height=\"24\"><path d=\"M9 19v2H7v-2h2Zm-4 0v2a2 2 0 0 1-2-2h2Zm8 0v2h-2v-2h2Zm8 0a2 2 0 0 1-2 2v-2h2Zm-4 0v2h-2v-2h2ZM15 7a1 1 0 0 1 0 2v7a1 1 0 0 1-2 0V9h-1v7a1 1 0 0 1-2 0v-4a2.5 2.5 0 0 1-.2-5H15ZM5 15v2H3v-2h2Zm16 0v2h-2v-2h2ZM5 11v2H3v-2h2Zm16 0v2h-2v-2h2ZM5 7v2H3V7h2Zm16 0v2h-2V7h2ZM5 3v2H3c0-1.1.9-2 2-2Zm8 0v2h-2V3h2Zm6 0a2 2 0 0 1 2 2h-2V3ZM9 3v2H7V3h2Zm8 0v2h-2V3h2Z\" fill-rule=\"evenodd\"/></svg>',\n    'visualchars': '<svg width=\"24\" height=\"24\"><path d=\"M10 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 6.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L10 5Z\" fill-rule=\"evenodd\"/></svg>',\n    'warning': '<svg width=\"24\" height=\"24\"><path d=\"M19.8 18.3c.2.5.3.9 0 1.2-.1.3-.5.5-1 .5H5.2c-.5 0-.9-.2-1-.5-.3-.3-.2-.7 0-1.2L11 4.7l.5-.5.5-.2c.2 0 .3 0 .5.2.2 0 .3.3.5.5l6.8 13.6ZM12 18c.3 0 .5-.1.7-.3.2-.2.3-.4.3-.7a1 1 0 0 0-.3-.7 1 1 0 0 0-.7-.3 1 1 0 0 0-.7.3 1 1 0 0 0-.3.7c0 .3.1.5.3.7.2.2.4.3.7.3Zm.7-3 .3-4a1 1 0 0 0-.3-.7 1 1 0 0 0-.7-.3 1 1 0 0 0-.7.3 1 1 0 0 0-.3.7l.3 4h1.4Z\" fill-rule=\"evenodd\"/></svg>',\n    'zoom-in': '<svg width=\"24\" height=\"24\"><path d=\"M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Zm-1-9a1 1 0 0 1 2 0v6a1 1 0 0 1-2 0V8Zm-2 4a1 1 0 0 1 0-2h6a1 1 0 0 1 0 2H8Z\" fill-rule=\"nonzero\"/></svg>',\n    'zoom-out': '<svg width=\"24\" height=\"24\"><path d=\"M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Zm-3-5a1 1 0 0 1 0-2h6a1 1 0 0 1 0 2H8Z\" fill-rule=\"nonzero\"/></svg>',\n  }\n});"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/icons/default/index.js",
    "content": "// Exports the \"default\" icons for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/icons/default')\n//   ES2015:\n//     import 'tinymce/icons/default'\nrequire('./icons.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/license.txt",
    "content": "MIT License\n\nCopyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/models/dom/index.js",
    "content": "// Exports the \"dom\" model for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/models/dom')\n//   ES2015:\n//     import 'tinymce/models/dom'\nrequire('./model.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/models/dom/model.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.ModelManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq$2 = t => a => t === a;\n    const isString = isType$1('string');\n    const isObject = isType$1('object');\n    const isArray = isType$1('array');\n    const isNull = eq$2(null);\n    const isBoolean = isSimpleType('boolean');\n    const isUndefined = eq$2(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n\n    const noop = () => {\n    };\n    const compose = (fa, fb) => {\n      return (...args) => {\n        return fa(fb.apply(null, args));\n      };\n    };\n    const compose1 = (fbc, fab) => a => fbc(fab(a));\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const identity = x => {\n      return x;\n    };\n    const tripleEquals = (a, b) => {\n      return a === b;\n    };\n    function curry(fn, ...initialArgs) {\n      return (...restArgs) => {\n        const all = initialArgs.concat(restArgs);\n        return fn.apply(null, all);\n      };\n    }\n    const not = f => t => !f(t);\n    const die = msg => {\n      return () => {\n        throw new Error(msg);\n      };\n    };\n    const apply = f => {\n      return f();\n    };\n    const never = constant(false);\n    const always = constant(true);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativeSlice = Array.prototype.slice;\n    const nativeIndexOf = Array.prototype.indexOf;\n    const nativePush = Array.prototype.push;\n    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);\n    const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1;\n    const exists = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const range$1 = (num, f) => {\n      const r = [];\n      for (let i = 0; i < num; i++) {\n        r.push(f(i));\n      }\n      return r;\n    };\n    const map$1 = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$2 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const eachr = (xs, f) => {\n      for (let i = xs.length - 1; i >= 0; i--) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const partition = (xs, pred) => {\n      const pass = [];\n      const fail = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        const arr = pred(x, i) ? pass : fail;\n        arr.push(x);\n      }\n      return {\n        pass,\n        fail\n      };\n    };\n    const filter$2 = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n    const foldr = (xs, f, acc) => {\n      eachr(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const foldl = (xs, f, acc) => {\n      each$2(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find$1 = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n    const findIndex = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(i);\n        }\n      }\n      return Optional.none();\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind$2 = (xs, f) => flatten(map$1(xs, f));\n    const forall = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        const x = xs[i];\n        if (pred(x, i) !== true) {\n          return false;\n        }\n      }\n      return true;\n    };\n    const reverse = xs => {\n      const r = nativeSlice.call(xs, 0);\n      r.reverse();\n      return r;\n    };\n    const mapToObject = (xs, f) => {\n      const r = {};\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        r[String(x)] = f(x, i);\n      }\n      return r;\n    };\n    const sort$1 = (xs, comparator) => {\n      const copy = nativeSlice.call(xs, 0);\n      copy.sort(comparator);\n      return copy;\n    };\n    const get$d = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get$d(xs, 0);\n    const last$2 = xs => get$d(xs, xs.length - 1);\n    const findMap = (arr, f) => {\n      for (let i = 0; i < arr.length; i++) {\n        const r = f(arr[i], i);\n        if (r.isSome()) {\n          return r;\n        }\n      }\n      return Optional.none();\n    };\n\n    const keys = Object.keys;\n    const hasOwnProperty = Object.hasOwnProperty;\n    const each$1 = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const map = (obj, f) => {\n      return tupleMap(obj, (x, i) => ({\n        k: i,\n        v: f(x, i)\n      }));\n    };\n    const tupleMap = (obj, f) => {\n      const r = {};\n      each$1(obj, (x, i) => {\n        const tuple = f(x, i);\n        r[tuple.k] = tuple.v;\n      });\n      return r;\n    };\n    const objAcc = r => (x, i) => {\n      r[i] = x;\n    };\n    const internalFilter = (obj, pred, onTrue, onFalse) => {\n      each$1(obj, (x, i) => {\n        (pred(x, i) ? onTrue : onFalse)(x, i);\n      });\n    };\n    const filter$1 = (obj, pred) => {\n      const t = {};\n      internalFilter(obj, pred, objAcc(t), noop);\n      return t;\n    };\n    const mapToArray = (obj, f) => {\n      const r = [];\n      each$1(obj, (value, name) => {\n        r.push(f(value, name));\n      });\n      return r;\n    };\n    const values = obj => {\n      return mapToArray(obj, identity);\n    };\n    const get$c = (obj, key) => {\n      return has$1(obj, key) ? Optional.from(obj[key]) : Optional.none();\n    };\n    const has$1 = (obj, key) => hasOwnProperty.call(obj, key);\n    const hasNonNullableKey = (obj, key) => has$1(obj, key) && obj[key] !== undefined && obj[key] !== null;\n    const isEmpty = r => {\n      for (const x in r) {\n        if (hasOwnProperty.call(r, x)) {\n          return false;\n        }\n      }\n      return true;\n    };\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const COMMENT = 8;\n    const DOCUMENT = 9;\n    const DOCUMENT_FRAGMENT = 11;\n    const ELEMENT = 1;\n    const TEXT = 3;\n\n    const name = element => {\n      const r = element.dom.nodeName;\n      return r.toLowerCase();\n    };\n    const type = element => element.dom.nodeType;\n    const isType = t => element => type(element) === t;\n    const isComment = element => type(element) === COMMENT || name(element) === '#comment';\n    const isElement = isType(ELEMENT);\n    const isText = isType(TEXT);\n    const isDocument = isType(DOCUMENT);\n    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);\n    const isTag = tag => e => isElement(e) && name(e) === tag;\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set$2 = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const setAll$1 = (element, attrs) => {\n      const dom = element.dom;\n      each$1(attrs, (v, k) => {\n        rawSet(dom, k, v);\n      });\n    };\n    const setOptions = (element, attrs) => {\n      each$1(attrs, (v, k) => {\n        v.fold(() => {\n          remove$7(element, k);\n        }, value => {\n          rawSet(element.dom, k, value);\n        });\n      });\n    };\n    const get$b = (element, key) => {\n      const v = element.dom.getAttribute(key);\n      return v === null ? undefined : v;\n    };\n    const getOpt = (element, key) => Optional.from(get$b(element, key));\n    const remove$7 = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n    const clone$2 = element => foldl(element.dom.attributes, (acc, attr) => {\n      acc[attr.name] = attr.value;\n      return acc;\n    }, {});\n\n    const fromHtml$1 = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom$1(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom$1(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom$1(node);\n    };\n    const fromDom$1 = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint$1 = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);\n    const SugarElement = {\n      fromHtml: fromHtml$1,\n      fromTag,\n      fromText,\n      fromDom: fromDom$1,\n      fromPoint: fromPoint$1\n    };\n\n    const is$2 = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;\n    const all$1 = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? [] : map$1(base.querySelectorAll(selector), SugarElement.fromDom);\n    };\n    const one = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);\n    };\n\n    const eq$1 = (e1, e2) => e1.dom === e2.dom;\n    const contains$1 = (e1, e2) => {\n      const d1 = e1.dom;\n      const d2 = e2.dom;\n      return d1 === d2 ? false : d1.contains(d2);\n    };\n    const is$1 = is$2;\n\n    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);\n    const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);\n    const documentElement = element => SugarElement.fromDom(documentOrOwner(element).dom.documentElement);\n    const defaultView = element => SugarElement.fromDom(documentOrOwner(element).dom.defaultView);\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);\n    const parents = (element, isRoot) => {\n      const stop = isFunction(isRoot) ? isRoot : never;\n      let dom = element.dom;\n      const ret = [];\n      while (dom.parentNode !== null && dom.parentNode !== undefined) {\n        const rawParent = dom.parentNode;\n        const p = SugarElement.fromDom(rawParent);\n        ret.push(p);\n        if (stop(p) === true) {\n          break;\n        } else {\n          dom = rawParent;\n        }\n      }\n      return ret;\n    };\n    const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);\n    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);\n    const children$2 = element => map$1(element.dom.childNodes, SugarElement.fromDom);\n    const child$2 = (element, index) => {\n      const cs = element.dom.childNodes;\n      return Optional.from(cs[index]).map(SugarElement.fromDom);\n    };\n    const firstChild = element => child$2(element, 0);\n\n    const before$3 = (marker, element) => {\n      const parent$1 = parent(marker);\n      parent$1.each(v => {\n        v.dom.insertBefore(element.dom, marker.dom);\n      });\n    };\n    const after$5 = (marker, element) => {\n      const sibling = nextSibling(marker);\n      sibling.fold(() => {\n        const parent$1 = parent(marker);\n        parent$1.each(v => {\n          append$1(v, element);\n        });\n      }, v => {\n        before$3(v, element);\n      });\n    };\n    const prepend = (parent, element) => {\n      const firstChild$1 = firstChild(parent);\n      firstChild$1.fold(() => {\n        append$1(parent, element);\n      }, v => {\n        parent.dom.insertBefore(element.dom, v.dom);\n      });\n    };\n    const append$1 = (parent, element) => {\n      parent.dom.appendChild(element.dom);\n    };\n    const appendAt = (parent, element, index) => {\n      child$2(parent, index).fold(() => {\n        append$1(parent, element);\n      }, v => {\n        before$3(v, element);\n      });\n    };\n    const wrap = (element, wrapper) => {\n      before$3(element, wrapper);\n      append$1(wrapper, element);\n    };\n\n    const after$4 = (marker, elements) => {\n      each$2(elements, (x, i) => {\n        const e = i === 0 ? marker : elements[i - 1];\n        after$5(e, x);\n      });\n    };\n    const append = (parent, elements) => {\n      each$2(elements, x => {\n        append$1(parent, x);\n      });\n    };\n\n    const empty = element => {\n      element.dom.textContent = '';\n      each$2(children$2(element), rogue => {\n        remove$6(rogue);\n      });\n    };\n    const remove$6 = element => {\n      const dom = element.dom;\n      if (dom.parentNode !== null) {\n        dom.parentNode.removeChild(dom);\n      }\n    };\n    const unwrap = wrapper => {\n      const children = children$2(wrapper);\n      if (children.length > 0) {\n        after$4(wrapper, children);\n      }\n      remove$6(wrapper);\n    };\n\n    const clone$1 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));\n    const shallow = original => clone$1(original, false);\n    const deep = original => clone$1(original, true);\n    const shallowAs = (original, tag) => {\n      const nu = SugarElement.fromTag(tag);\n      const attributes = clone$2(original);\n      setAll$1(nu, attributes);\n      return nu;\n    };\n    const copy$2 = (original, tag) => {\n      const nu = shallowAs(original, tag);\n      const cloneChildren = children$2(deep(original));\n      append(nu, cloneChildren);\n      return nu;\n    };\n    const mutate$1 = (original, tag) => {\n      const nu = shallowAs(original, tag);\n      after$5(original, nu);\n      const children = children$2(original);\n      append(nu, children);\n      remove$6(original);\n      return nu;\n    };\n\n    const validSectionList = [\n      'tfoot',\n      'thead',\n      'tbody',\n      'colgroup'\n    ];\n    const isValidSection = parentName => contains$2(validSectionList, parentName);\n    const grid = (rows, columns) => ({\n      rows,\n      columns\n    });\n    const address = (row, column) => ({\n      row,\n      column\n    });\n    const detail = (element, rowspan, colspan) => ({\n      element,\n      rowspan,\n      colspan\n    });\n    const detailnew = (element, rowspan, colspan, isNew) => ({\n      element,\n      rowspan,\n      colspan,\n      isNew\n    });\n    const extended = (element, rowspan, colspan, row, column, isLocked) => ({\n      element,\n      rowspan,\n      colspan,\n      row,\n      column,\n      isLocked\n    });\n    const rowdetail = (element, cells, section) => ({\n      element,\n      cells,\n      section\n    });\n    const rowdetailnew = (element, cells, section, isNew) => ({\n      element,\n      cells,\n      section,\n      isNew\n    });\n    const elementnew = (element, isNew, isLocked) => ({\n      element,\n      isNew,\n      isLocked\n    });\n    const rowcells = (element, cells, section, isNew) => ({\n      element,\n      cells,\n      section,\n      isNew\n    });\n    const bounds = (startRow, startCol, finishRow, finishCol) => ({\n      startRow,\n      startCol,\n      finishRow,\n      finishCol\n    });\n    const columnext = (element, colspan, column) => ({\n      element,\n      colspan,\n      column\n    });\n    const colgroup = (element, columns) => ({\n      element,\n      columns\n    });\n\n    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);\n    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);\n    const isSupported$1 = constant(supported);\n    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;\n    const getShadowRoot = e => {\n      const r = getRootNode(e);\n      return isShadowRoot(r) ? Optional.some(r) : Optional.none();\n    };\n    const getShadowHost = e => SugarElement.fromDom(e.dom.host);\n    const getOriginalEventTarget = event => {\n      if (isSupported$1() && isNonNullable(event.target)) {\n        const el = SugarElement.fromDom(event.target);\n        if (isElement(el) && isOpenShadowHost(el)) {\n          if (event.composed && event.composedPath) {\n            const composedPath = event.composedPath();\n            if (composedPath) {\n              return head(composedPath);\n            }\n          }\n        }\n      }\n      return Optional.from(event.target);\n    };\n    const isOpenShadowHost = element => isNonNullable(element.dom.shadowRoot);\n\n    const inBody = element => {\n      const dom = isText(element) ? element.dom.parentNode : element.dom;\n      if (dom === undefined || dom === null || dom.ownerDocument === null) {\n        return false;\n      }\n      const doc = dom.ownerDocument;\n      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));\n    };\n    const body$1 = () => getBody$1(SugarElement.fromDom(document));\n    const getBody$1 = doc => {\n      const b = doc.dom.body;\n      if (b === null || b === undefined) {\n        throw new Error('Body is not available yet');\n      }\n      return SugarElement.fromDom(b);\n    };\n\n    const ancestors$4 = (scope, predicate, isRoot) => filter$2(parents(scope, isRoot), predicate);\n    const children$1 = (scope, predicate) => filter$2(children$2(scope), predicate);\n    const descendants$1 = (scope, predicate) => {\n      let result = [];\n      each$2(children$2(scope), x => {\n        if (predicate(x)) {\n          result = result.concat([x]);\n        }\n        result = result.concat(descendants$1(x, predicate));\n      });\n      return result;\n    };\n\n    const ancestors$3 = (scope, selector, isRoot) => ancestors$4(scope, e => is$2(e, selector), isRoot);\n    const children = (scope, selector) => children$1(scope, e => is$2(e, selector));\n    const descendants = (scope, selector) => all$1(selector, scope);\n\n    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {\n      if (is(scope, a)) {\n        return Optional.some(scope);\n      } else if (isFunction(isRoot) && isRoot(scope)) {\n        return Optional.none();\n      } else {\n        return ancestor(scope, a, isRoot);\n      }\n    };\n\n    const ancestor$2 = (scope, predicate, isRoot) => {\n      let element = scope.dom;\n      const stop = isFunction(isRoot) ? isRoot : never;\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        if (predicate(el)) {\n          return Optional.some(el);\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const closest$2 = (scope, predicate, isRoot) => {\n      const is = (s, test) => test(s);\n      return ClosestOrAncestor(is, ancestor$2, scope, predicate, isRoot);\n    };\n    const child$1 = (scope, predicate) => {\n      const pred = node => predicate(SugarElement.fromDom(node));\n      const result = find$1(scope.dom.childNodes, pred);\n      return result.map(SugarElement.fromDom);\n    };\n    const descendant$1 = (scope, predicate) => {\n      const descend = node => {\n        for (let i = 0; i < node.childNodes.length; i++) {\n          const child = SugarElement.fromDom(node.childNodes[i]);\n          if (predicate(child)) {\n            return Optional.some(child);\n          }\n          const res = descend(node.childNodes[i]);\n          if (res.isSome()) {\n            return res;\n          }\n        }\n        return Optional.none();\n      };\n      return descend(scope.dom);\n    };\n\n    const ancestor$1 = (scope, selector, isRoot) => ancestor$2(scope, e => is$2(e, selector), isRoot);\n    const child = (scope, selector) => child$1(scope, e => is$2(e, selector));\n    const descendant = (scope, selector) => one(selector, scope);\n    const closest$1 = (scope, selector, isRoot) => {\n      const is = (element, selector) => is$2(element, selector);\n      return ClosestOrAncestor(is, ancestor$1, scope, selector, isRoot);\n    };\n\n    const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));\n    const cat = arr => {\n      const r = [];\n      const push = x => {\n        r.push(x);\n      };\n      for (let i = 0; i < arr.length; i++) {\n        arr[i].each(push);\n      }\n      return r;\n    };\n    const bindFrom = (a, f) => a !== undefined && a !== null ? f(a) : Optional.none();\n    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();\n\n    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;\n    const contains = (str, substr, start = 0, end) => {\n      const idx = str.indexOf(substr, start);\n      if (idx !== -1) {\n        return isUndefined(end) ? true : idx + substr.length <= end;\n      } else {\n        return false;\n      }\n    };\n    const startsWith = (str, prefix) => {\n      return checkRange(str, prefix, 0);\n    };\n    const endsWith = (str, suffix) => {\n      return checkRange(str, suffix, str.length - suffix.length);\n    };\n    const blank = r => s => s.replace(r, '');\n    const trim = blank(/^\\s+|\\s+$/g);\n    const isNotEmpty = s => s.length > 0;\n    const toFloat = value => {\n      const num = parseFloat(value);\n      return isNaN(num) ? Optional.none() : Optional.some(num);\n    };\n\n    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);\n\n    const internalSet = (dom, property, value) => {\n      if (!isString(value)) {\n        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);\n        throw new Error('CSS value must be a string: ' + value);\n      }\n      if (isSupported(dom)) {\n        dom.style.setProperty(property, value);\n      }\n    };\n    const internalRemove = (dom, property) => {\n      if (isSupported(dom)) {\n        dom.style.removeProperty(property);\n      }\n    };\n    const set$1 = (element, property, value) => {\n      const dom = element.dom;\n      internalSet(dom, property, value);\n    };\n    const setAll = (element, css) => {\n      const dom = element.dom;\n      each$1(css, (v, k) => {\n        internalSet(dom, k, v);\n      });\n    };\n    const get$a = (element, property) => {\n      const dom = element.dom;\n      const styles = window.getComputedStyle(dom);\n      const r = styles.getPropertyValue(property);\n      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;\n    };\n    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';\n    const getRaw$2 = (element, property) => {\n      const dom = element.dom;\n      const raw = getUnsafeProperty(dom, property);\n      return Optional.from(raw).filter(r => r.length > 0);\n    };\n    const remove$5 = (element, property) => {\n      const dom = element.dom;\n      internalRemove(dom, property);\n      if (is(getOpt(element, 'style').map(trim), '')) {\n        remove$7(element, 'style');\n      }\n    };\n    const copy$1 = (source, target) => {\n      const sourceDom = source.dom;\n      const targetDom = target.dom;\n      if (isSupported(sourceDom) && isSupported(targetDom)) {\n        targetDom.style.cssText = sourceDom.style.cssText;\n      }\n    };\n\n    const getAttrValue = (cell, name, fallback = 0) => getOpt(cell, name).map(value => parseInt(value, 10)).getOr(fallback);\n    const getSpan = (cell, type) => getAttrValue(cell, type, 1);\n    const hasColspan = cellOrCol => {\n      if (isTag('col')(cellOrCol)) {\n        return getAttrValue(cellOrCol, 'span', 1) > 1;\n      } else {\n        return getSpan(cellOrCol, 'colspan') > 1;\n      }\n    };\n    const hasRowspan = cell => getSpan(cell, 'rowspan') > 1;\n    const getCssValue = (element, property) => parseInt(get$a(element, property), 10);\n    const minWidth = constant(10);\n    const minHeight = constant(10);\n\n    const firstLayer = (scope, selector) => {\n      return filterFirstLayer(scope, selector, always);\n    };\n    const filterFirstLayer = (scope, selector, predicate) => {\n      return bind$2(children$2(scope), x => {\n        if (is$2(x, selector)) {\n          return predicate(x) ? [x] : [];\n        } else {\n          return filterFirstLayer(x, selector, predicate);\n        }\n      });\n    };\n\n    const lookup = (tags, element, isRoot = never) => {\n      if (isRoot(element)) {\n        return Optional.none();\n      }\n      if (contains$2(tags, name(element))) {\n        return Optional.some(element);\n      }\n      const isRootOrUpperTable = elm => is$2(elm, 'table') || isRoot(elm);\n      return ancestor$1(element, tags.join(','), isRootOrUpperTable);\n    };\n    const cell = (element, isRoot) => lookup([\n      'td',\n      'th'\n    ], element, isRoot);\n    const cells$1 = ancestor => firstLayer(ancestor, 'th,td');\n    const columns$1 = ancestor => {\n      if (is$2(ancestor, 'colgroup')) {\n        return children(ancestor, 'col');\n      } else {\n        return bind$2(columnGroups(ancestor), columnGroup => children(columnGroup, 'col'));\n      }\n    };\n    const table = (element, isRoot) => closest$1(element, 'table', isRoot);\n    const rows$1 = ancestor => firstLayer(ancestor, 'tr');\n    const columnGroups = ancestor => table(ancestor).fold(constant([]), table => children(table, 'colgroup'));\n\n    const fromRowsOrColGroups = (elems, getSection) => map$1(elems, row => {\n      if (name(row) === 'colgroup') {\n        const cells = map$1(columns$1(row), column => {\n          const colspan = getAttrValue(column, 'span', 1);\n          return detail(column, 1, colspan);\n        });\n        return rowdetail(row, cells, 'colgroup');\n      } else {\n        const cells = map$1(cells$1(row), cell => {\n          const rowspan = getAttrValue(cell, 'rowspan', 1);\n          const colspan = getAttrValue(cell, 'colspan', 1);\n          return detail(cell, rowspan, colspan);\n        });\n        return rowdetail(row, cells, getSection(row));\n      }\n    });\n    const getParentSection = group => parent(group).map(parent => {\n      const parentName = name(parent);\n      return isValidSection(parentName) ? parentName : 'tbody';\n    }).getOr('tbody');\n    const fromTable$1 = table => {\n      const rows = rows$1(table);\n      const columnGroups$1 = columnGroups(table);\n      const elems = [\n        ...columnGroups$1,\n        ...rows\n      ];\n      return fromRowsOrColGroups(elems, getParentSection);\n    };\n    const fromPastedRows = (elems, section) => fromRowsOrColGroups(elems, () => section);\n\n    const cached = f => {\n      let called = false;\n      let r;\n      return (...args) => {\n        if (!called) {\n          called = true;\n          r = f.apply(null, args);\n        }\n        return r;\n      };\n    };\n\n    const DeviceType = (os, browser, userAgent, mediaMatch) => {\n      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;\n      const isiPhone = os.isiOS() && !isiPad;\n      const isMobile = os.isiOS() || os.isAndroid();\n      const isTouch = isMobile || mediaMatch('(pointer:coarse)');\n      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');\n      const isPhone = isiPhone || isMobile && !isTablet;\n      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;\n      const isDesktop = !isPhone && !isTablet && !iOSwebview;\n      return {\n        isiPad: constant(isiPad),\n        isiPhone: constant(isiPhone),\n        isTablet: constant(isTablet),\n        isPhone: constant(isPhone),\n        isTouch: constant(isTouch),\n        isAndroid: os.isAndroid,\n        isiOS: os.isiOS,\n        isWebView: constant(iOSwebview),\n        isDesktop: constant(isDesktop)\n      };\n    };\n\n    const firstMatch = (regexes, s) => {\n      for (let i = 0; i < regexes.length; i++) {\n        const x = regexes[i];\n        if (x.test(s)) {\n          return x;\n        }\n      }\n      return undefined;\n    };\n    const find = (regexes, agent) => {\n      const r = firstMatch(regexes, agent);\n      if (!r) {\n        return {\n          major: 0,\n          minor: 0\n        };\n      }\n      const group = i => {\n        return Number(agent.replace(r, '$' + i));\n      };\n      return nu$2(group(1), group(2));\n    };\n    const detect$5 = (versionRegexes, agent) => {\n      const cleanedAgent = String(agent).toLowerCase();\n      if (versionRegexes.length === 0) {\n        return unknown$2();\n      }\n      return find(versionRegexes, cleanedAgent);\n    };\n    const unknown$2 = () => {\n      return nu$2(0, 0);\n    };\n    const nu$2 = (major, minor) => {\n      return {\n        major,\n        minor\n      };\n    };\n    const Version = {\n      nu: nu$2,\n      detect: detect$5,\n      unknown: unknown$2\n    };\n\n    const detectBrowser$1 = (browsers, userAgentData) => {\n      return findMap(userAgentData.brands, uaBrand => {\n        const lcBrand = uaBrand.brand.toLowerCase();\n        return find$1(browsers, browser => {\n          var _a;\n          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());\n        }).map(info => ({\n          current: info.name,\n          version: Version.nu(parseInt(uaBrand.version, 10), 0)\n        }));\n      });\n    };\n\n    const detect$4 = (candidates, userAgent) => {\n      const agent = String(userAgent).toLowerCase();\n      return find$1(candidates, candidate => {\n        return candidate.search(agent);\n      });\n    };\n    const detectBrowser = (browsers, userAgent) => {\n      return detect$4(browsers, userAgent).map(browser => {\n        const version = Version.detect(browser.versionRegexes, userAgent);\n        return {\n          current: browser.name,\n          version\n        };\n      });\n    };\n    const detectOs = (oses, userAgent) => {\n      return detect$4(oses, userAgent).map(os => {\n        const version = Version.detect(os.versionRegexes, userAgent);\n        return {\n          current: os.name,\n          version\n        };\n      });\n    };\n\n    const normalVersionRegex = /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/;\n    const checkContains = target => {\n      return uastring => {\n        return contains(uastring, target);\n      };\n    };\n    const browsers = [\n      {\n        name: 'Edge',\n        versionRegexes: [/.*?edge\\/ ?([0-9]+)\\.([0-9]+)$/],\n        search: uastring => {\n          return contains(uastring, 'edge/') && contains(uastring, 'chrome') && contains(uastring, 'safari') && contains(uastring, 'applewebkit');\n        }\n      },\n      {\n        name: 'Chromium',\n        brand: 'Chromium',\n        versionRegexes: [\n          /.*?chrome\\/([0-9]+)\\.([0-9]+).*/,\n          normalVersionRegex\n        ],\n        search: uastring => {\n          return contains(uastring, 'chrome') && !contains(uastring, 'chromeframe');\n        }\n      },\n      {\n        name: 'IE',\n        versionRegexes: [\n          /.*?msie\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*?rv:([0-9]+)\\.([0-9]+).*/\n        ],\n        search: uastring => {\n          return contains(uastring, 'msie') || contains(uastring, 'trident');\n        }\n      },\n      {\n        name: 'Opera',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?opera\\/([0-9]+)\\.([0-9]+).*/\n        ],\n        search: checkContains('opera')\n      },\n      {\n        name: 'Firefox',\n        versionRegexes: [/.*?firefox\\/\\ ?([0-9]+)\\.([0-9]+).*/],\n        search: checkContains('firefox')\n      },\n      {\n        name: 'Safari',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?cpu os ([0-9]+)_([0-9]+).*/\n        ],\n        search: uastring => {\n          return (contains(uastring, 'safari') || contains(uastring, 'mobile/')) && contains(uastring, 'applewebkit');\n        }\n      }\n    ];\n    const oses = [\n      {\n        name: 'Windows',\n        search: checkContains('win'),\n        versionRegexes: [/.*?windows\\ nt\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'iOS',\n        search: uastring => {\n          return contains(uastring, 'iphone') || contains(uastring, 'ipad');\n        },\n        versionRegexes: [\n          /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*cpu os ([0-9]+)_([0-9]+).*/,\n          /.*cpu iphone os ([0-9]+)_([0-9]+).*/\n        ]\n      },\n      {\n        name: 'Android',\n        search: checkContains('android'),\n        versionRegexes: [/.*?android\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'macOS',\n        search: checkContains('mac os x'),\n        versionRegexes: [/.*?mac\\ os\\ x\\ ?([0-9]+)_([0-9]+).*/]\n      },\n      {\n        name: 'Linux',\n        search: checkContains('linux'),\n        versionRegexes: []\n      },\n      {\n        name: 'Solaris',\n        search: checkContains('sunos'),\n        versionRegexes: []\n      },\n      {\n        name: 'FreeBSD',\n        search: checkContains('freebsd'),\n        versionRegexes: []\n      },\n      {\n        name: 'ChromeOS',\n        search: checkContains('cros'),\n        versionRegexes: [/.*?chrome\\/([0-9]+)\\.([0-9]+).*/]\n      }\n    ];\n    const PlatformInfo = {\n      browsers: constant(browsers),\n      oses: constant(oses)\n    };\n\n    const edge = 'Edge';\n    const chromium = 'Chromium';\n    const ie = 'IE';\n    const opera = 'Opera';\n    const firefox = 'Firefox';\n    const safari = 'Safari';\n    const unknown$1 = () => {\n      return nu$1({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu$1 = info => {\n      const current = info.current;\n      const version = info.version;\n      const isBrowser = name => () => current === name;\n      return {\n        current,\n        version,\n        isEdge: isBrowser(edge),\n        isChromium: isBrowser(chromium),\n        isIE: isBrowser(ie),\n        isOpera: isBrowser(opera),\n        isFirefox: isBrowser(firefox),\n        isSafari: isBrowser(safari)\n      };\n    };\n    const Browser = {\n      unknown: unknown$1,\n      nu: nu$1,\n      edge: constant(edge),\n      chromium: constant(chromium),\n      ie: constant(ie),\n      opera: constant(opera),\n      firefox: constant(firefox),\n      safari: constant(safari)\n    };\n\n    const windows = 'Windows';\n    const ios = 'iOS';\n    const android = 'Android';\n    const linux = 'Linux';\n    const macos = 'macOS';\n    const solaris = 'Solaris';\n    const freebsd = 'FreeBSD';\n    const chromeos = 'ChromeOS';\n    const unknown = () => {\n      return nu({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu = info => {\n      const current = info.current;\n      const version = info.version;\n      const isOS = name => () => current === name;\n      return {\n        current,\n        version,\n        isWindows: isOS(windows),\n        isiOS: isOS(ios),\n        isAndroid: isOS(android),\n        isMacOS: isOS(macos),\n        isLinux: isOS(linux),\n        isSolaris: isOS(solaris),\n        isFreeBSD: isOS(freebsd),\n        isChromeOS: isOS(chromeos)\n      };\n    };\n    const OperatingSystem = {\n      unknown,\n      nu,\n      windows: constant(windows),\n      ios: constant(ios),\n      android: constant(android),\n      linux: constant(linux),\n      macos: constant(macos),\n      solaris: constant(solaris),\n      freebsd: constant(freebsd),\n      chromeos: constant(chromeos)\n    };\n\n    const detect$3 = (userAgent, userAgentDataOpt, mediaMatch) => {\n      const browsers = PlatformInfo.browsers();\n      const oses = PlatformInfo.oses();\n      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);\n      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);\n      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);\n      return {\n        browser,\n        os,\n        deviceType\n      };\n    };\n    const PlatformDetection = { detect: detect$3 };\n\n    const mediaMatch = query => window.matchMedia(query).matches;\n    let platform = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));\n    const detect$2 = () => platform();\n\n    const Dimension = (name, getOffset) => {\n      const set = (element, h) => {\n        if (!isNumber(h) && !h.match(/^[0-9]+$/)) {\n          throw new Error(name + '.set accepts only positive integer values. Value was ' + h);\n        }\n        const dom = element.dom;\n        if (isSupported(dom)) {\n          dom.style[name] = h + 'px';\n        }\n      };\n      const get = element => {\n        const r = getOffset(element);\n        if (r <= 0 || r === null) {\n          const css = get$a(element, name);\n          return parseFloat(css) || 0;\n        }\n        return r;\n      };\n      const getOuter = get;\n      const aggregate = (element, properties) => foldl(properties, (acc, property) => {\n        const val = get$a(element, property);\n        const value = val === undefined ? 0 : parseInt(val, 10);\n        return isNaN(value) ? acc : acc + value;\n      }, 0);\n      const max = (element, value, properties) => {\n        const cumulativeInclusions = aggregate(element, properties);\n        const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;\n        return absoluteMax;\n      };\n      return {\n        set,\n        get,\n        getOuter,\n        aggregate,\n        max\n      };\n    };\n\n    const toNumber = (px, fallback) => toFloat(px).getOr(fallback);\n    const getProp = (element, name, fallback) => toNumber(get$a(element, name), fallback);\n    const calcContentBoxSize = (element, size, upper, lower) => {\n      const paddingUpper = getProp(element, `padding-${ upper }`, 0);\n      const paddingLower = getProp(element, `padding-${ lower }`, 0);\n      const borderUpper = getProp(element, `border-${ upper }-width`, 0);\n      const borderLower = getProp(element, `border-${ lower }-width`, 0);\n      return size - paddingUpper - paddingLower - borderUpper - borderLower;\n    };\n    const getCalculatedWidth = (element, boxSizing) => {\n      const dom = element.dom;\n      const width = dom.getBoundingClientRect().width || dom.offsetWidth;\n      return boxSizing === 'border-box' ? width : calcContentBoxSize(element, width, 'left', 'right');\n    };\n    const getHeight$1 = element => getProp(element, 'height', element.dom.offsetHeight);\n    const getWidth = element => getProp(element, 'width', element.dom.offsetWidth);\n    const getInnerWidth = element => getCalculatedWidth(element, 'content-box');\n\n    const api$2 = Dimension('width', element => element.dom.offsetWidth);\n    const get$9 = element => api$2.get(element);\n    const getOuter$2 = element => api$2.getOuter(element);\n    const getInner = getInnerWidth;\n    const getRuntime$1 = getWidth;\n\n    const addCells = (gridRow, index, cells) => {\n      const existingCells = gridRow.cells;\n      const before = existingCells.slice(0, index);\n      const after = existingCells.slice(index);\n      const newCells = before.concat(cells).concat(after);\n      return setCells(gridRow, newCells);\n    };\n    const addCell = (gridRow, index, cell) => addCells(gridRow, index, [cell]);\n    const mutateCell = (gridRow, index, cell) => {\n      const cells = gridRow.cells;\n      cells[index] = cell;\n    };\n    const setCells = (gridRow, cells) => rowcells(gridRow.element, cells, gridRow.section, gridRow.isNew);\n    const mapCells = (gridRow, f) => {\n      const cells = gridRow.cells;\n      const r = map$1(cells, f);\n      return rowcells(gridRow.element, r, gridRow.section, gridRow.isNew);\n    };\n    const getCell = (gridRow, index) => gridRow.cells[index];\n    const getCellElement = (gridRow, index) => getCell(gridRow, index).element;\n    const cellLength = gridRow => gridRow.cells.length;\n    const extractGridDetails = grid => {\n      const result = partition(grid, row => row.section === 'colgroup');\n      return {\n        rows: result.fail,\n        cols: result.pass\n      };\n    };\n    const clone = (gridRow, cloneRow, cloneCell) => {\n      const newCells = map$1(gridRow.cells, cloneCell);\n      return rowcells(cloneRow(gridRow.element), newCells, gridRow.section, true);\n    };\n\n    const LOCKED_COL_ATTR = 'data-snooker-locked-cols';\n    const getLockedColumnsFromTable = table => getOpt(table, LOCKED_COL_ATTR).bind(lockedColStr => Optional.from(lockedColStr.match(/\\d+/g))).map(lockedCols => mapToObject(lockedCols, always));\n    const getLockedColumnsFromGrid = grid => {\n      const locked = foldl(extractGridDetails(grid).rows, (acc, row) => {\n        each$2(row.cells, (cell, idx) => {\n          if (cell.isLocked) {\n            acc[idx] = true;\n          }\n        });\n        return acc;\n      }, {});\n      const lockedArr = mapToArray(locked, (_val, key) => parseInt(key, 10));\n      return sort$1(lockedArr);\n    };\n\n    const key = (row, column) => {\n      return row + ',' + column;\n    };\n    const getAt = (warehouse, row, column) => Optional.from(warehouse.access[key(row, column)]);\n    const findItem = (warehouse, item, comparator) => {\n      const filtered = filterItems(warehouse, detail => {\n        return comparator(item, detail.element);\n      });\n      return filtered.length > 0 ? Optional.some(filtered[0]) : Optional.none();\n    };\n    const filterItems = (warehouse, predicate) => {\n      const all = bind$2(warehouse.all, r => {\n        return r.cells;\n      });\n      return filter$2(all, predicate);\n    };\n    const generateColumns = rowData => {\n      const columnsGroup = {};\n      let index = 0;\n      each$2(rowData.cells, column => {\n        const colspan = column.colspan;\n        range$1(colspan, columnIndex => {\n          const colIndex = index + columnIndex;\n          columnsGroup[colIndex] = columnext(column.element, colspan, colIndex);\n        });\n        index += colspan;\n      });\n      return columnsGroup;\n    };\n    const generate$1 = list => {\n      const access = {};\n      const cells = [];\n      const tableOpt = head(list).map(rowData => rowData.element).bind(table);\n      const lockedColumns = tableOpt.bind(getLockedColumnsFromTable).getOr({});\n      let maxRows = 0;\n      let maxColumns = 0;\n      let rowCount = 0;\n      const {\n        pass: colgroupRows,\n        fail: rows\n      } = partition(list, rowData => rowData.section === 'colgroup');\n      each$2(rows, rowData => {\n        const currentRow = [];\n        each$2(rowData.cells, rowCell => {\n          let start = 0;\n          while (access[key(rowCount, start)] !== undefined) {\n            start++;\n          }\n          const isLocked = hasNonNullableKey(lockedColumns, start.toString());\n          const current = extended(rowCell.element, rowCell.rowspan, rowCell.colspan, rowCount, start, isLocked);\n          for (let occupiedColumnPosition = 0; occupiedColumnPosition < rowCell.colspan; occupiedColumnPosition++) {\n            for (let occupiedRowPosition = 0; occupiedRowPosition < rowCell.rowspan; occupiedRowPosition++) {\n              const rowPosition = rowCount + occupiedRowPosition;\n              const columnPosition = start + occupiedColumnPosition;\n              const newpos = key(rowPosition, columnPosition);\n              access[newpos] = current;\n              maxColumns = Math.max(maxColumns, columnPosition + 1);\n            }\n          }\n          currentRow.push(current);\n        });\n        maxRows++;\n        cells.push(rowdetail(rowData.element, currentRow, rowData.section));\n        rowCount++;\n      });\n      const {columns, colgroups} = last$2(colgroupRows).map(rowData => {\n        const columns = generateColumns(rowData);\n        const colgroup$1 = colgroup(rowData.element, values(columns));\n        return {\n          colgroups: [colgroup$1],\n          columns\n        };\n      }).getOrThunk(() => ({\n        colgroups: [],\n        columns: {}\n      }));\n      const grid$1 = grid(maxRows, maxColumns);\n      return {\n        grid: grid$1,\n        access,\n        all: cells,\n        columns,\n        colgroups\n      };\n    };\n    const fromTable = table => {\n      const list = fromTable$1(table);\n      return generate$1(list);\n    };\n    const justCells = warehouse => bind$2(warehouse.all, w => w.cells);\n    const justColumns = warehouse => values(warehouse.columns);\n    const hasColumns = warehouse => keys(warehouse.columns).length > 0;\n    const getColumnAt = (warehouse, columnIndex) => Optional.from(warehouse.columns[columnIndex]);\n    const Warehouse = {\n      fromTable,\n      generate: generate$1,\n      getAt,\n      findItem,\n      filterItems,\n      justCells,\n      justColumns,\n      hasColumns,\n      getColumnAt\n    };\n\n    const columns = (warehouse, isValidCell = always) => {\n      const grid = warehouse.grid;\n      const cols = range$1(grid.columns, identity);\n      const rowsArr = range$1(grid.rows, identity);\n      return map$1(cols, col => {\n        const getBlock = () => bind$2(rowsArr, r => Warehouse.getAt(warehouse, r, col).filter(detail => detail.column === col).toArray());\n        const isValid = detail => detail.colspan === 1 && isValidCell(detail.element);\n        const getFallback = () => Warehouse.getAt(warehouse, 0, col);\n        return decide(getBlock, isValid, getFallback);\n      });\n    };\n    const decide = (getBlock, isValid, getFallback) => {\n      const inBlock = getBlock();\n      const validInBlock = find$1(inBlock, isValid);\n      const detailOption = validInBlock.orThunk(() => Optional.from(inBlock[0]).orThunk(getFallback));\n      return detailOption.map(detail => detail.element);\n    };\n    const rows = warehouse => {\n      const grid = warehouse.grid;\n      const rowsArr = range$1(grid.rows, identity);\n      const cols = range$1(grid.columns, identity);\n      return map$1(rowsArr, row => {\n        const getBlock = () => bind$2(cols, c => Warehouse.getAt(warehouse, row, c).filter(detail => detail.row === row).fold(constant([]), detail => [detail]));\n        const isSingle = detail => detail.rowspan === 1;\n        const getFallback = () => Warehouse.getAt(warehouse, row, 0);\n        return decide(getBlock, isSingle, getFallback);\n      });\n    };\n\n    const deduce = (xs, index) => {\n      if (index < 0 || index >= xs.length - 1) {\n        return Optional.none();\n      }\n      const current = xs[index].fold(() => {\n        const rest = reverse(xs.slice(0, index));\n        return findMap(rest, (a, i) => a.map(aa => ({\n          value: aa,\n          delta: i + 1\n        })));\n      }, c => Optional.some({\n        value: c,\n        delta: 0\n      }));\n      const next = xs[index + 1].fold(() => {\n        const rest = xs.slice(index + 1);\n        return findMap(rest, (a, i) => a.map(aa => ({\n          value: aa,\n          delta: i + 1\n        })));\n      }, n => Optional.some({\n        value: n,\n        delta: 1\n      }));\n      return current.bind(c => next.map(n => {\n        const extras = n.delta + c.delta;\n        return Math.abs(n.value - c.value) / extras;\n      }));\n    };\n\n    const onDirection = (isLtr, isRtl) => element => getDirection(element) === 'rtl' ? isRtl : isLtr;\n    const getDirection = element => get$a(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';\n\n    const api$1 = Dimension('height', element => {\n      const dom = element.dom;\n      return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;\n    });\n    const get$8 = element => api$1.get(element);\n    const getOuter$1 = element => api$1.getOuter(element);\n    const getRuntime = getHeight$1;\n\n    const r = (left, top) => {\n      const translate = (x, y) => r(left + x, top + y);\n      return {\n        left,\n        top,\n        translate\n      };\n    };\n    const SugarPosition = r;\n\n    const boxPosition = dom => {\n      const box = dom.getBoundingClientRect();\n      return SugarPosition(box.left, box.top);\n    };\n    const firstDefinedOrZero = (a, b) => {\n      if (a !== undefined) {\n        return a;\n      } else {\n        return b !== undefined ? b : 0;\n      }\n    };\n    const absolute = element => {\n      const doc = element.dom.ownerDocument;\n      const body = doc.body;\n      const win = doc.defaultView;\n      const html = doc.documentElement;\n      if (body === element.dom) {\n        return SugarPosition(body.offsetLeft, body.offsetTop);\n      }\n      const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop);\n      const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft);\n      const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);\n      const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);\n      return viewport(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);\n    };\n    const viewport = element => {\n      const dom = element.dom;\n      const doc = dom.ownerDocument;\n      const body = doc.body;\n      if (body === dom) {\n        return SugarPosition(body.offsetLeft, body.offsetTop);\n      }\n      if (!inBody(element)) {\n        return SugarPosition(0, 0);\n      }\n      return boxPosition(dom);\n    };\n\n    const rowInfo = (row, y) => ({\n      row,\n      y\n    });\n    const colInfo = (col, x) => ({\n      col,\n      x\n    });\n    const rtlEdge = cell => {\n      const pos = absolute(cell);\n      return pos.left + getOuter$2(cell);\n    };\n    const ltrEdge = cell => {\n      return absolute(cell).left;\n    };\n    const getLeftEdge = (index, cell) => {\n      return colInfo(index, ltrEdge(cell));\n    };\n    const getRightEdge = (index, cell) => {\n      return colInfo(index, rtlEdge(cell));\n    };\n    const getTop$1 = cell => {\n      return absolute(cell).top;\n    };\n    const getTopEdge = (index, cell) => {\n      return rowInfo(index, getTop$1(cell));\n    };\n    const getBottomEdge = (index, cell) => {\n      return rowInfo(index, getTop$1(cell) + getOuter$1(cell));\n    };\n    const findPositions = (getInnerEdge, getOuterEdge, array) => {\n      if (array.length === 0) {\n        return [];\n      }\n      const lines = map$1(array.slice(1), (cellOption, index) => {\n        return cellOption.map(cell => {\n          return getInnerEdge(index, cell);\n        });\n      });\n      const lastLine = array[array.length - 1].map(cell => {\n        return getOuterEdge(array.length - 1, cell);\n      });\n      return lines.concat([lastLine]);\n    };\n    const negate = step => {\n      return -step;\n    };\n    const height = {\n      delta: identity,\n      positions: optElements => findPositions(getTopEdge, getBottomEdge, optElements),\n      edge: getTop$1\n    };\n    const ltr$1 = {\n      delta: identity,\n      edge: ltrEdge,\n      positions: optElements => findPositions(getLeftEdge, getRightEdge, optElements)\n    };\n    const rtl$1 = {\n      delta: negate,\n      edge: rtlEdge,\n      positions: optElements => findPositions(getRightEdge, getLeftEdge, optElements)\n    };\n    const detect$1 = onDirection(ltr$1, rtl$1);\n    const width = {\n      delta: (amount, table) => detect$1(table).delta(amount, table),\n      positions: (cols, table) => detect$1(table).positions(cols, table),\n      edge: cell => detect$1(cell).edge(cell)\n    };\n\n    const units = {\n      unsupportedLength: [\n        'em',\n        'ex',\n        'cap',\n        'ch',\n        'ic',\n        'rem',\n        'lh',\n        'rlh',\n        'vw',\n        'vh',\n        'vi',\n        'vb',\n        'vmin',\n        'vmax',\n        'cm',\n        'mm',\n        'Q',\n        'in',\n        'pc',\n        'pt',\n        'px'\n      ],\n      fixed: [\n        'px',\n        'pt'\n      ],\n      relative: ['%'],\n      empty: ['']\n    };\n    const pattern = (() => {\n      const decimalDigits = '[0-9]+';\n      const signedInteger = '[+-]?' + decimalDigits;\n      const exponentPart = '[eE]' + signedInteger;\n      const dot = '\\\\.';\n      const opt = input => `(?:${ input })?`;\n      const unsignedDecimalLiteral = [\n        'Infinity',\n        decimalDigits + dot + opt(decimalDigits) + opt(exponentPart),\n        dot + decimalDigits + opt(exponentPart),\n        decimalDigits + opt(exponentPart)\n      ].join('|');\n      const float = `[+-]?(?:${ unsignedDecimalLiteral })`;\n      return new RegExp(`^(${ float })(.*)$`);\n    })();\n    const isUnit = (unit, accepted) => exists(accepted, acc => exists(units[acc], check => unit === check));\n    const parse = (input, accepted) => {\n      const match = Optional.from(pattern.exec(input));\n      return match.bind(array => {\n        const value = Number(array[1]);\n        const unitRaw = array[2];\n        if (isUnit(unitRaw, accepted)) {\n          return Optional.some({\n            value,\n            unit: unitRaw\n          });\n        } else {\n          return Optional.none();\n        }\n      });\n    };\n\n    const rPercentageBasedSizeRegex = /(\\d+(\\.\\d+)?)%/;\n    const rPixelBasedSizeRegex = /(\\d+(\\.\\d+)?)px|em/;\n    const isCol$2 = isTag('col');\n    const getPercentSize = (elm, outerGetter, innerGetter) => {\n      const relativeParent = parentElement(elm).getOrThunk(() => getBody$1(owner(elm)));\n      return outerGetter(elm) / innerGetter(relativeParent) * 100;\n    };\n    const setPixelWidth = (cell, amount) => {\n      set$1(cell, 'width', amount + 'px');\n    };\n    const setPercentageWidth = (cell, amount) => {\n      set$1(cell, 'width', amount + '%');\n    };\n    const setHeight = (cell, amount) => {\n      set$1(cell, 'height', amount + 'px');\n    };\n    const getHeightValue = cell => getRuntime(cell) + 'px';\n    const convert = (cell, number, getter, setter) => {\n      const newSize = table(cell).map(table => {\n        const total = getter(table);\n        return Math.floor(number / 100 * total);\n      }).getOr(number);\n      setter(cell, newSize);\n      return newSize;\n    };\n    const normalizePixelSize = (value, cell, getter, setter) => {\n      const number = parseFloat(value);\n      return endsWith(value, '%') && name(cell) !== 'table' ? convert(cell, number, getter, setter) : number;\n    };\n    const getTotalHeight = cell => {\n      const value = getHeightValue(cell);\n      if (!value) {\n        return get$8(cell);\n      }\n      return normalizePixelSize(value, cell, get$8, setHeight);\n    };\n    const get$7 = (cell, type, f) => {\n      const v = f(cell);\n      const span = getSpan(cell, type);\n      return v / span;\n    };\n    const getRaw$1 = (element, prop) => {\n      return getRaw$2(element, prop).orThunk(() => {\n        return getOpt(element, prop).map(val => val + 'px');\n      });\n    };\n    const getRawWidth$1 = element => getRaw$1(element, 'width');\n    const getRawHeight = element => getRaw$1(element, 'height');\n    const getPercentageWidth = cell => getPercentSize(cell, get$9, getInner);\n    const getPixelWidth$1 = cell => isCol$2(cell) ? get$9(cell) : getRuntime$1(cell);\n    const getHeight = cell => {\n      return get$7(cell, 'rowspan', getTotalHeight);\n    };\n    const getGenericWidth = cell => {\n      const width = getRawWidth$1(cell);\n      return width.bind(w => parse(w, [\n        'fixed',\n        'relative',\n        'empty'\n      ]));\n    };\n    const setGenericWidth = (cell, amount, unit) => {\n      set$1(cell, 'width', amount + unit);\n    };\n    const getPixelTableWidth = table => get$9(table) + 'px';\n    const getPercentTableWidth = table => getPercentSize(table, get$9, getInner) + '%';\n    const isPercentSizing$1 = table => getRawWidth$1(table).exists(size => rPercentageBasedSizeRegex.test(size));\n    const isPixelSizing$1 = table => getRawWidth$1(table).exists(size => rPixelBasedSizeRegex.test(size));\n    const isNoneSizing$1 = table => getRawWidth$1(table).isNone();\n    const percentageBasedSizeRegex = constant(rPercentageBasedSizeRegex);\n\n    const isCol$1 = isTag('col');\n    const getRawW = cell => {\n      return getRawWidth$1(cell).getOrThunk(() => getPixelWidth$1(cell) + 'px');\n    };\n    const getRawH = cell => {\n      return getRawHeight(cell).getOrThunk(() => getHeight(cell) + 'px');\n    };\n    const justCols = warehouse => map$1(Warehouse.justColumns(warehouse), column => Optional.from(column.element));\n    const isValidColumn = cell => {\n      const browser = detect$2().browser;\n      const supportsColWidths = browser.isChromium() || browser.isFirefox();\n      return isCol$1(cell) ? supportsColWidths : true;\n    };\n    const getDimension = (cellOpt, index, backups, filter, getter, fallback) => cellOpt.filter(filter).fold(() => fallback(deduce(backups, index)), cell => getter(cell));\n    const getWidthFrom = (warehouse, table, getWidth, fallback) => {\n      const columnCells = columns(warehouse);\n      const columns$1 = Warehouse.hasColumns(warehouse) ? justCols(warehouse) : columnCells;\n      const backups = [Optional.some(width.edge(table))].concat(map$1(width.positions(columnCells, table), pos => pos.map(p => p.x)));\n      const colFilter = not(hasColspan);\n      return map$1(columns$1, (cellOption, c) => {\n        return getDimension(cellOption, c, backups, colFilter, column => {\n          if (isValidColumn(column)) {\n            return getWidth(column);\n          } else {\n            const cell = bindFrom(columnCells[c], identity);\n            return getDimension(cell, c, backups, colFilter, cell => fallback(Optional.some(get$9(cell))), fallback);\n          }\n        }, fallback);\n      });\n    };\n    const getDeduced = deduced => {\n      return deduced.map(d => {\n        return d + 'px';\n      }).getOr('');\n    };\n    const getRawWidths = (warehouse, table) => {\n      return getWidthFrom(warehouse, table, getRawW, getDeduced);\n    };\n    const getPercentageWidths = (warehouse, table, tableSize) => {\n      return getWidthFrom(warehouse, table, getPercentageWidth, deduced => {\n        return deduced.fold(() => {\n          return tableSize.minCellWidth();\n        }, cellWidth => {\n          return cellWidth / tableSize.pixelWidth() * 100;\n        });\n      });\n    };\n    const getPixelWidths = (warehouse, table, tableSize) => {\n      return getWidthFrom(warehouse, table, getPixelWidth$1, deduced => {\n        return deduced.getOrThunk(tableSize.minCellWidth);\n      });\n    };\n    const getHeightFrom = (warehouse, table, direction, getHeight, fallback) => {\n      const rows$1 = rows(warehouse);\n      const backups = [Optional.some(direction.edge(table))].concat(map$1(direction.positions(rows$1, table), pos => pos.map(p => p.y)));\n      return map$1(rows$1, (cellOption, c) => {\n        return getDimension(cellOption, c, backups, not(hasRowspan), getHeight, fallback);\n      });\n    };\n    const getPixelHeights = (warehouse, table, direction) => {\n      return getHeightFrom(warehouse, table, direction, getHeight, deduced => {\n        return deduced.getOrThunk(minHeight);\n      });\n    };\n    const getRawHeights = (warehouse, table, direction) => {\n      return getHeightFrom(warehouse, table, direction, getRawH, getDeduced);\n    };\n\n    const widthLookup = (table, getter) => () => {\n      if (inBody(table)) {\n        return getter(table);\n      } else {\n        return parseFloat(getRaw$2(table, 'width').getOr('0'));\n      }\n    };\n    const noneSize = table => {\n      const getWidth = widthLookup(table, get$9);\n      const zero = constant(0);\n      const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize);\n      return {\n        width: getWidth,\n        pixelWidth: getWidth,\n        getWidths,\n        getCellDelta: zero,\n        singleColumnWidth: constant([0]),\n        minCellWidth: zero,\n        setElementWidth: noop,\n        adjustTableWidth: noop,\n        isRelative: true,\n        label: 'none'\n      };\n    };\n    const percentageSize = table => {\n      const getFloatWidth = widthLookup(table, elem => parseFloat(getPercentTableWidth(elem)));\n      const getWidth = widthLookup(table, get$9);\n      const getCellDelta = delta => delta / getWidth() * 100;\n      const singleColumnWidth = (w, _delta) => [100 - w];\n      const minCellWidth = () => minWidth() / getWidth() * 100;\n      const adjustTableWidth = delta => {\n        const currentWidth = getFloatWidth();\n        const change = delta / 100 * currentWidth;\n        const newWidth = currentWidth + change;\n        setPercentageWidth(table, newWidth);\n      };\n      const getWidths = (warehouse, tableSize) => getPercentageWidths(warehouse, table, tableSize);\n      return {\n        width: getFloatWidth,\n        pixelWidth: getWidth,\n        getWidths,\n        getCellDelta,\n        singleColumnWidth,\n        minCellWidth,\n        setElementWidth: setPercentageWidth,\n        adjustTableWidth,\n        isRelative: true,\n        label: 'percent'\n      };\n    };\n    const pixelSize = table => {\n      const getWidth = widthLookup(table, get$9);\n      const getCellDelta = identity;\n      const singleColumnWidth = (w, delta) => {\n        const newNext = Math.max(minWidth(), w + delta);\n        return [newNext - w];\n      };\n      const adjustTableWidth = delta => {\n        const newWidth = getWidth() + delta;\n        setPixelWidth(table, newWidth);\n      };\n      const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize);\n      return {\n        width: getWidth,\n        pixelWidth: getWidth,\n        getWidths,\n        getCellDelta,\n        singleColumnWidth,\n        minCellWidth: minWidth,\n        setElementWidth: setPixelWidth,\n        adjustTableWidth,\n        isRelative: false,\n        label: 'pixel'\n      };\n    };\n    const chooseSize = (element, width) => {\n      const percentMatch = percentageBasedSizeRegex().exec(width);\n      if (percentMatch !== null) {\n        return percentageSize(element);\n      } else {\n        return pixelSize(element);\n      }\n    };\n    const getTableSize = table => {\n      const width = getRawWidth$1(table);\n      return width.fold(() => noneSize(table), w => chooseSize(table, w));\n    };\n    const TableSize = {\n      getTableSize,\n      pixelSize,\n      percentageSize,\n      noneSize\n    };\n\n    const statsStruct = (minRow, minCol, maxRow, maxCol, allCells, selectedCells) => ({\n      minRow,\n      minCol,\n      maxRow,\n      maxCol,\n      allCells,\n      selectedCells\n    });\n    const findSelectedStats = (house, isSelected) => {\n      const totalColumns = house.grid.columns;\n      const totalRows = house.grid.rows;\n      let minRow = totalRows;\n      let minCol = totalColumns;\n      let maxRow = 0;\n      let maxCol = 0;\n      const allCells = [];\n      const selectedCells = [];\n      each$1(house.access, detail => {\n        allCells.push(detail);\n        if (isSelected(detail)) {\n          selectedCells.push(detail);\n          const startRow = detail.row;\n          const endRow = startRow + detail.rowspan - 1;\n          const startCol = detail.column;\n          const endCol = startCol + detail.colspan - 1;\n          if (startRow < minRow) {\n            minRow = startRow;\n          } else if (endRow > maxRow) {\n            maxRow = endRow;\n          }\n          if (startCol < minCol) {\n            minCol = startCol;\n          } else if (endCol > maxCol) {\n            maxCol = endCol;\n          }\n        }\n      });\n      return statsStruct(minRow, minCol, maxRow, maxCol, allCells, selectedCells);\n    };\n    const makeCell = (list, seenSelected, rowIndex) => {\n      const row = list[rowIndex].element;\n      const td = SugarElement.fromTag('td');\n      append$1(td, SugarElement.fromTag('br'));\n      const f = seenSelected ? append$1 : prepend;\n      f(row, td);\n    };\n    const fillInGaps = (list, house, stats, isSelected) => {\n      const rows = filter$2(list, row => row.section !== 'colgroup');\n      const totalColumns = house.grid.columns;\n      const totalRows = house.grid.rows;\n      for (let i = 0; i < totalRows; i++) {\n        let seenSelected = false;\n        for (let j = 0; j < totalColumns; j++) {\n          if (!(i < stats.minRow || i > stats.maxRow || j < stats.minCol || j > stats.maxCol)) {\n            const needCell = Warehouse.getAt(house, i, j).filter(isSelected).isNone();\n            if (needCell) {\n              makeCell(rows, seenSelected, i);\n            } else {\n              seenSelected = true;\n            }\n          }\n        }\n      }\n    };\n    const clean = (replica, stats, house, widthDelta) => {\n      each$1(house.columns, col => {\n        if (col.column < stats.minCol || col.column > stats.maxCol) {\n          remove$6(col.element);\n        }\n      });\n      const emptyRows = filter$2(firstLayer(replica, 'tr'), row => row.dom.childElementCount === 0);\n      each$2(emptyRows, remove$6);\n      if (stats.minCol === stats.maxCol || stats.minRow === stats.maxRow) {\n        each$2(firstLayer(replica, 'th,td'), cell => {\n          remove$7(cell, 'rowspan');\n          remove$7(cell, 'colspan');\n        });\n      }\n      remove$7(replica, LOCKED_COL_ATTR);\n      remove$7(replica, 'data-snooker-col-series');\n      const tableSize = TableSize.getTableSize(replica);\n      tableSize.adjustTableWidth(widthDelta);\n    };\n    const getTableWidthDelta = (table, warehouse, tableSize, stats) => {\n      if (stats.minCol === 0 && warehouse.grid.columns === stats.maxCol + 1) {\n        return 0;\n      }\n      const colWidths = getPixelWidths(warehouse, table, tableSize);\n      const allColsWidth = foldl(colWidths, (acc, width) => acc + width, 0);\n      const selectedColsWidth = foldl(colWidths.slice(stats.minCol, stats.maxCol + 1), (acc, width) => acc + width, 0);\n      const newWidth = selectedColsWidth / allColsWidth * tableSize.pixelWidth();\n      const delta = newWidth - tableSize.pixelWidth();\n      return tableSize.getCellDelta(delta);\n    };\n    const extract$1 = (table, selectedSelector) => {\n      const isSelected = detail => is$2(detail.element, selectedSelector);\n      const replica = deep(table);\n      const list = fromTable$1(replica);\n      const tableSize = TableSize.getTableSize(table);\n      const replicaHouse = Warehouse.generate(list);\n      const replicaStats = findSelectedStats(replicaHouse, isSelected);\n      const selector = 'th:not(' + selectedSelector + ')' + ',td:not(' + selectedSelector + ')';\n      const unselectedCells = filterFirstLayer(replica, 'th,td', cell => is$2(cell, selector));\n      each$2(unselectedCells, remove$6);\n      fillInGaps(list, replicaHouse, replicaStats, isSelected);\n      const house = Warehouse.fromTable(table);\n      const widthDelta = getTableWidthDelta(table, house, tableSize, replicaStats);\n      clean(replica, replicaStats, replicaHouse, widthDelta);\n      return replica;\n    };\n\n    const nbsp = '\\xA0';\n\n    const NodeValue = (is, name) => {\n      const get = element => {\n        if (!is(element)) {\n          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');\n        }\n        return getOption(element).getOr('');\n      };\n      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();\n      const set = (element, value) => {\n        if (!is(element)) {\n          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');\n        }\n        element.dom.nodeValue = value;\n      };\n      return {\n        get,\n        getOption,\n        set\n      };\n    };\n\n    const api = NodeValue(isText, 'text');\n    const get$6 = element => api.get(element);\n    const getOption = element => api.getOption(element);\n    const set = (element, value) => api.set(element, value);\n\n    const getEnd = element => name(element) === 'img' ? 1 : getOption(element).fold(() => children$2(element).length, v => v.length);\n    const isTextNodeWithCursorPosition = el => getOption(el).filter(text => text.trim().length !== 0 || text.indexOf(nbsp) > -1).isSome();\n    const elementsWithCursorPosition = [\n      'img',\n      'br'\n    ];\n    const isCursorPosition = elem => {\n      const hasCursorPosition = isTextNodeWithCursorPosition(elem);\n      return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem));\n    };\n\n    const first = element => descendant$1(element, isCursorPosition);\n    const last$1 = element => descendantRtl(element, isCursorPosition);\n    const descendantRtl = (scope, predicate) => {\n      const descend = element => {\n        const children = children$2(element);\n        for (let i = children.length - 1; i >= 0; i--) {\n          const child = children[i];\n          if (predicate(child)) {\n            return Optional.some(child);\n          }\n          const res = descend(child);\n          if (res.isSome()) {\n            return res;\n          }\n        }\n        return Optional.none();\n      };\n      return descend(scope);\n    };\n\n    const transferableAttributes = {\n      scope: [\n        'row',\n        'col'\n      ]\n    };\n    const createCell = doc => () => {\n      const td = SugarElement.fromTag('td', doc.dom);\n      append$1(td, SugarElement.fromTag('br', doc.dom));\n      return td;\n    };\n    const createCol = doc => () => {\n      return SugarElement.fromTag('col', doc.dom);\n    };\n    const createColgroup = doc => () => {\n      return SugarElement.fromTag('colgroup', doc.dom);\n    };\n    const createRow$1 = doc => () => {\n      return SugarElement.fromTag('tr', doc.dom);\n    };\n    const replace$1 = (cell, tag, attrs) => {\n      const replica = copy$2(cell, tag);\n      each$1(attrs, (v, k) => {\n        if (v === null) {\n          remove$7(replica, k);\n        } else {\n          set$2(replica, k, v);\n        }\n      });\n      return replica;\n    };\n    const pasteReplace = cell => {\n      return cell;\n    };\n    const cloneFormats = (oldCell, newCell, formats) => {\n      const first$1 = first(oldCell);\n      return first$1.map(firstText => {\n        const formatSelector = formats.join(',');\n        const parents = ancestors$3(firstText, formatSelector, element => {\n          return eq$1(element, oldCell);\n        });\n        return foldr(parents, (last, parent) => {\n          const clonedFormat = shallow(parent);\n          remove$7(clonedFormat, 'contenteditable');\n          append$1(last, clonedFormat);\n          return clonedFormat;\n        }, newCell);\n      }).getOr(newCell);\n    };\n    const cloneAppropriateAttributes = (original, clone) => {\n      each$1(transferableAttributes, (validAttributes, attributeName) => getOpt(original, attributeName).filter(attribute => contains$2(validAttributes, attribute)).each(attribute => set$2(clone, attributeName, attribute)));\n    };\n    const cellOperations = (mutate, doc, formatsToClone) => {\n      const cloneCss = (prev, clone) => {\n        copy$1(prev.element, clone);\n        remove$5(clone, 'height');\n        if (prev.colspan !== 1) {\n          remove$5(clone, 'width');\n        }\n      };\n      const newCell = prev => {\n        const td = SugarElement.fromTag(name(prev.element), doc.dom);\n        const formats = formatsToClone.getOr([\n          'strong',\n          'em',\n          'b',\n          'i',\n          'span',\n          'font',\n          'h1',\n          'h2',\n          'h3',\n          'h4',\n          'h5',\n          'h6',\n          'p',\n          'div'\n        ]);\n        const lastNode = formats.length > 0 ? cloneFormats(prev.element, td, formats) : td;\n        append$1(lastNode, SugarElement.fromTag('br'));\n        cloneCss(prev, td);\n        cloneAppropriateAttributes(prev.element, td);\n        mutate(prev.element, td);\n        return td;\n      };\n      const newCol = prev => {\n        const col = SugarElement.fromTag(name(prev.element), doc.dom);\n        cloneCss(prev, col);\n        mutate(prev.element, col);\n        return col;\n      };\n      return {\n        col: newCol,\n        colgroup: createColgroup(doc),\n        row: createRow$1(doc),\n        cell: newCell,\n        replace: replace$1,\n        colGap: createCol(doc),\n        gap: createCell(doc)\n      };\n    };\n    const paste$1 = doc => {\n      return {\n        col: createCol(doc),\n        colgroup: createColgroup(doc),\n        row: createRow$1(doc),\n        cell: createCell(doc),\n        replace: pasteReplace,\n        colGap: createCol(doc),\n        gap: createCell(doc)\n      };\n    };\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      return children$2(SugarElement.fromDom(div));\n    };\n    const fromDom = nodes => map$1(nodes, SugarElement.fromDom);\n\n    const getBody = editor => SugarElement.fromDom(editor.getBody());\n    const getIsRoot = editor => element => eq$1(element, getBody(editor));\n    const removeDataStyle = table => {\n      remove$7(table, 'data-mce-style');\n      const removeStyleAttribute = element => remove$7(element, 'data-mce-style');\n      each$2(cells$1(table), removeStyleAttribute);\n      each$2(columns$1(table), removeStyleAttribute);\n      each$2(rows$1(table), removeStyleAttribute);\n    };\n    const getSelectionStart = editor => SugarElement.fromDom(editor.selection.getStart());\n    const getPixelWidth = elm => elm.getBoundingClientRect().width;\n    const getPixelHeight = elm => elm.getBoundingClientRect().height;\n    const getRawWidth = (editor, elm) => {\n      const raw = editor.dom.getStyle(elm, 'width') || editor.dom.getAttrib(elm, 'width');\n      return Optional.from(raw).filter(isNotEmpty);\n    };\n    const isPercentage$1 = value => /^(\\d+(\\.\\d+)?)%$/.test(value);\n    const isPixel = value => /^(\\d+(\\.\\d+)?)px$/.test(value);\n\n    const inSelection = (bounds, detail) => {\n      const leftEdge = detail.column;\n      const rightEdge = detail.column + detail.colspan - 1;\n      const topEdge = detail.row;\n      const bottomEdge = detail.row + detail.rowspan - 1;\n      return leftEdge <= bounds.finishCol && rightEdge >= bounds.startCol && (topEdge <= bounds.finishRow && bottomEdge >= bounds.startRow);\n    };\n    const isWithin = (bounds, detail) => {\n      return detail.column >= bounds.startCol && detail.column + detail.colspan - 1 <= bounds.finishCol && detail.row >= bounds.startRow && detail.row + detail.rowspan - 1 <= bounds.finishRow;\n    };\n    const isRectangular = (warehouse, bounds) => {\n      let isRect = true;\n      const detailIsWithin = curry(isWithin, bounds);\n      for (let i = bounds.startRow; i <= bounds.finishRow; i++) {\n        for (let j = bounds.startCol; j <= bounds.finishCol; j++) {\n          isRect = isRect && Warehouse.getAt(warehouse, i, j).exists(detailIsWithin);\n        }\n      }\n      return isRect ? Optional.some(bounds) : Optional.none();\n    };\n\n    const getBounds = (detailA, detailB) => {\n      return bounds(Math.min(detailA.row, detailB.row), Math.min(detailA.column, detailB.column), Math.max(detailA.row + detailA.rowspan - 1, detailB.row + detailB.rowspan - 1), Math.max(detailA.column + detailA.colspan - 1, detailB.column + detailB.colspan - 1));\n    };\n    const getAnyBox = (warehouse, startCell, finishCell) => {\n      const startCoords = Warehouse.findItem(warehouse, startCell, eq$1);\n      const finishCoords = Warehouse.findItem(warehouse, finishCell, eq$1);\n      return startCoords.bind(sc => {\n        return finishCoords.map(fc => {\n          return getBounds(sc, fc);\n        });\n      });\n    };\n    const getBox$1 = (warehouse, startCell, finishCell) => {\n      return getAnyBox(warehouse, startCell, finishCell).bind(bounds => {\n        return isRectangular(warehouse, bounds);\n      });\n    };\n\n    const moveBy$1 = (warehouse, cell, row, column) => {\n      return Warehouse.findItem(warehouse, cell, eq$1).bind(detail => {\n        const startRow = row > 0 ? detail.row + detail.rowspan - 1 : detail.row;\n        const startCol = column > 0 ? detail.column + detail.colspan - 1 : detail.column;\n        const dest = Warehouse.getAt(warehouse, startRow + row, startCol + column);\n        return dest.map(d => {\n          return d.element;\n        });\n      });\n    };\n    const intercepts$1 = (warehouse, start, finish) => {\n      return getAnyBox(warehouse, start, finish).map(bounds => {\n        const inside = Warehouse.filterItems(warehouse, curry(inSelection, bounds));\n        return map$1(inside, detail => {\n          return detail.element;\n        });\n      });\n    };\n    const parentCell = (warehouse, innerCell) => {\n      const isContainedBy = (c1, c2) => {\n        return contains$1(c2, c1);\n      };\n      return Warehouse.findItem(warehouse, innerCell, isContainedBy).map(detail => {\n        return detail.element;\n      });\n    };\n\n    const moveBy = (cell, deltaRow, deltaColumn) => {\n      return table(cell).bind(table => {\n        const warehouse = getWarehouse(table);\n        return moveBy$1(warehouse, cell, deltaRow, deltaColumn);\n      });\n    };\n    const intercepts = (table, first, last) => {\n      const warehouse = getWarehouse(table);\n      return intercepts$1(warehouse, first, last);\n    };\n    const nestedIntercepts = (table, first, firstTable, last, lastTable) => {\n      const warehouse = getWarehouse(table);\n      const optStartCell = eq$1(table, firstTable) ? Optional.some(first) : parentCell(warehouse, first);\n      const optLastCell = eq$1(table, lastTable) ? Optional.some(last) : parentCell(warehouse, last);\n      return optStartCell.bind(startCell => optLastCell.bind(lastCell => intercepts$1(warehouse, startCell, lastCell)));\n    };\n    const getBox = (table, first, last) => {\n      const warehouse = getWarehouse(table);\n      return getBox$1(warehouse, first, last);\n    };\n    const getWarehouse = Warehouse.fromTable;\n\n    var TagBoundaries = [\n      'body',\n      'p',\n      'div',\n      'article',\n      'aside',\n      'figcaption',\n      'figure',\n      'footer',\n      'header',\n      'nav',\n      'section',\n      'ol',\n      'ul',\n      'li',\n      'table',\n      'thead',\n      'tbody',\n      'tfoot',\n      'caption',\n      'tr',\n      'td',\n      'th',\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'blockquote',\n      'pre',\n      'address'\n    ];\n\n    var DomUniverse = () => {\n      const clone = element => {\n        return SugarElement.fromDom(element.dom.cloneNode(false));\n      };\n      const document = element => documentOrOwner(element).dom;\n      const isBoundary = element => {\n        if (!isElement(element)) {\n          return false;\n        }\n        if (name(element) === 'body') {\n          return true;\n        }\n        return contains$2(TagBoundaries, name(element));\n      };\n      const isEmptyTag = element => {\n        if (!isElement(element)) {\n          return false;\n        }\n        return contains$2([\n          'br',\n          'img',\n          'hr',\n          'input'\n        ], name(element));\n      };\n      const isNonEditable = element => isElement(element) && get$b(element, 'contenteditable') === 'false';\n      const comparePosition = (element, other) => {\n        return element.dom.compareDocumentPosition(other.dom);\n      };\n      const copyAttributesTo = (source, destination) => {\n        const as = clone$2(source);\n        setAll$1(destination, as);\n      };\n      const isSpecial = element => {\n        const tag = name(element);\n        return contains$2([\n          'script',\n          'noscript',\n          'iframe',\n          'noframes',\n          'noembed',\n          'title',\n          'style',\n          'textarea',\n          'xmp'\n        ], tag);\n      };\n      const getLanguage = element => isElement(element) ? getOpt(element, 'lang') : Optional.none();\n      return {\n        up: constant({\n          selector: ancestor$1,\n          closest: closest$1,\n          predicate: ancestor$2,\n          all: parents\n        }),\n        down: constant({\n          selector: descendants,\n          predicate: descendants$1\n        }),\n        styles: constant({\n          get: get$a,\n          getRaw: getRaw$2,\n          set: set$1,\n          remove: remove$5\n        }),\n        attrs: constant({\n          get: get$b,\n          set: set$2,\n          remove: remove$7,\n          copyTo: copyAttributesTo\n        }),\n        insert: constant({\n          before: before$3,\n          after: after$5,\n          afterAll: after$4,\n          append: append$1,\n          appendAll: append,\n          prepend: prepend,\n          wrap: wrap\n        }),\n        remove: constant({\n          unwrap: unwrap,\n          remove: remove$6\n        }),\n        create: constant({\n          nu: SugarElement.fromTag,\n          clone,\n          text: SugarElement.fromText\n        }),\n        query: constant({\n          comparePosition,\n          prevSibling: prevSibling,\n          nextSibling: nextSibling\n        }),\n        property: constant({\n          children: children$2,\n          name: name,\n          parent: parent,\n          document,\n          isText: isText,\n          isComment: isComment,\n          isElement: isElement,\n          isSpecial,\n          getLanguage,\n          getText: get$6,\n          setText: set,\n          isBoundary,\n          isEmptyTag,\n          isNonEditable\n        }),\n        eq: eq$1,\n        is: is$1\n      };\n    };\n\n    const all = (universe, look, elements, f) => {\n      const head = elements[0];\n      const tail = elements.slice(1);\n      return f(universe, look, head, tail);\n    };\n    const oneAll = (universe, look, elements) => {\n      return elements.length > 0 ? all(universe, look, elements, unsafeOne) : Optional.none();\n    };\n    const unsafeOne = (universe, look, head, tail) => {\n      const start = look(universe, head);\n      return foldr(tail, (b, a) => {\n        const current = look(universe, a);\n        return commonElement(universe, b, current);\n      }, start);\n    };\n    const commonElement = (universe, start, end) => {\n      return start.bind(s => {\n        return end.filter(curry(universe.eq, s));\n      });\n    };\n\n    const eq = (universe, item) => {\n      return curry(universe.eq, item);\n    };\n    const ancestors$2 = (universe, start, end, isRoot = never) => {\n      const ps1 = [start].concat(universe.up().all(start));\n      const ps2 = [end].concat(universe.up().all(end));\n      const prune = path => {\n        const index = findIndex(path, isRoot);\n        return index.fold(() => {\n          return path;\n        }, ind => {\n          return path.slice(0, ind + 1);\n        });\n      };\n      const pruned1 = prune(ps1);\n      const pruned2 = prune(ps2);\n      const shared = find$1(pruned1, x => {\n        return exists(pruned2, eq(universe, x));\n      });\n      return {\n        firstpath: pruned1,\n        secondpath: pruned2,\n        shared\n      };\n    };\n\n    const sharedOne$1 = oneAll;\n    const ancestors$1 = ancestors$2;\n\n    const universe$3 = DomUniverse();\n    const sharedOne = (look, elements) => {\n      return sharedOne$1(universe$3, (_universe, element) => {\n        return look(element);\n      }, elements);\n    };\n    const ancestors = (start, finish, isRoot) => {\n      return ancestors$1(universe$3, start, finish, isRoot);\n    };\n\n    const lookupTable = container => {\n      return ancestor$1(container, 'table');\n    };\n    const identify = (start, finish, isRoot) => {\n      const getIsRoot = rootTable => {\n        return element => {\n          return isRoot !== undefined && isRoot(element) || eq$1(element, rootTable);\n        };\n      };\n      if (eq$1(start, finish)) {\n        return Optional.some({\n          boxes: Optional.some([start]),\n          start,\n          finish\n        });\n      } else {\n        return lookupTable(start).bind(startTable => {\n          return lookupTable(finish).bind(finishTable => {\n            if (eq$1(startTable, finishTable)) {\n              return Optional.some({\n                boxes: intercepts(startTable, start, finish),\n                start,\n                finish\n              });\n            } else if (contains$1(startTable, finishTable)) {\n              const ancestorCells = ancestors$3(finish, 'td,th', getIsRoot(startTable));\n              const finishCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : finish;\n              return Optional.some({\n                boxes: nestedIntercepts(startTable, start, startTable, finish, finishTable),\n                start,\n                finish: finishCell\n              });\n            } else if (contains$1(finishTable, startTable)) {\n              const ancestorCells = ancestors$3(start, 'td,th', getIsRoot(finishTable));\n              const startCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : start;\n              return Optional.some({\n                boxes: nestedIntercepts(finishTable, start, startTable, finish, finishTable),\n                start,\n                finish: startCell\n              });\n            } else {\n              return ancestors(start, finish).shared.bind(lca => {\n                return closest$1(lca, 'table', isRoot).bind(lcaTable => {\n                  const finishAncestorCells = ancestors$3(finish, 'td,th', getIsRoot(lcaTable));\n                  const finishCell = finishAncestorCells.length > 0 ? finishAncestorCells[finishAncestorCells.length - 1] : finish;\n                  const startAncestorCells = ancestors$3(start, 'td,th', getIsRoot(lcaTable));\n                  const startCell = startAncestorCells.length > 0 ? startAncestorCells[startAncestorCells.length - 1] : start;\n                  return Optional.some({\n                    boxes: nestedIntercepts(lcaTable, start, startTable, finish, finishTable),\n                    start: startCell,\n                    finish: finishCell\n                  });\n                });\n              });\n            }\n          });\n        });\n      }\n    };\n    const retrieve$1 = (container, selector) => {\n      const sels = descendants(container, selector);\n      return sels.length > 0 ? Optional.some(sels) : Optional.none();\n    };\n    const getLast = (boxes, lastSelectedSelector) => {\n      return find$1(boxes, box => {\n        return is$2(box, lastSelectedSelector);\n      });\n    };\n    const getEdges = (container, firstSelectedSelector, lastSelectedSelector) => {\n      return descendant(container, firstSelectedSelector).bind(first => {\n        return descendant(container, lastSelectedSelector).bind(last => {\n          return sharedOne(lookupTable, [\n            first,\n            last\n          ]).map(table => {\n            return {\n              first,\n              last,\n              table\n            };\n          });\n        });\n      });\n    };\n    const expandTo = (finish, firstSelectedSelector) => {\n      return ancestor$1(finish, 'table').bind(table => {\n        return descendant(table, firstSelectedSelector).bind(start => {\n          return identify(start, finish).bind(identified => {\n            return identified.boxes.map(boxes => {\n              return {\n                boxes,\n                start: identified.start,\n                finish: identified.finish\n              };\n            });\n          });\n        });\n      });\n    };\n    const shiftSelection = (boxes, deltaRow, deltaColumn, firstSelectedSelector, lastSelectedSelector) => {\n      return getLast(boxes, lastSelectedSelector).bind(last => {\n        return moveBy(last, deltaRow, deltaColumn).bind(finish => {\n          return expandTo(finish, firstSelectedSelector);\n        });\n      });\n    };\n\n    const retrieve = (container, selector) => {\n      return retrieve$1(container, selector);\n    };\n    const retrieveBox = (container, firstSelectedSelector, lastSelectedSelector) => {\n      return getEdges(container, firstSelectedSelector, lastSelectedSelector).bind(edges => {\n        const isRoot = ancestor => {\n          return eq$1(container, ancestor);\n        };\n        const sectionSelector = 'thead,tfoot,tbody,table';\n        const firstAncestor = ancestor$1(edges.first, sectionSelector, isRoot);\n        const lastAncestor = ancestor$1(edges.last, sectionSelector, isRoot);\n        return firstAncestor.bind(fA => {\n          return lastAncestor.bind(lA => {\n            return eq$1(fA, lA) ? getBox(edges.table, edges.first, edges.last) : Optional.none();\n          });\n        });\n      });\n    };\n\n    const selection = identity;\n    const unmergable = selectedCells => {\n      const hasSpan = (elem, type) => getOpt(elem, type).exists(span => parseInt(span, 10) > 1);\n      const hasRowOrColSpan = elem => hasSpan(elem, 'rowspan') || hasSpan(elem, 'colspan');\n      return selectedCells.length > 0 && forall(selectedCells, hasRowOrColSpan) ? Optional.some(selectedCells) : Optional.none();\n    };\n    const mergable = (table, selectedCells, ephemera) => {\n      if (selectedCells.length <= 1) {\n        return Optional.none();\n      } else {\n        return retrieveBox(table, ephemera.firstSelectedSelector, ephemera.lastSelectedSelector).map(bounds => ({\n          bounds,\n          cells: selectedCells\n        }));\n      }\n    };\n\n    const strSelected = 'data-mce-selected';\n    const strSelectedSelector = 'td[' + strSelected + '],th[' + strSelected + ']';\n    const strAttributeSelector = '[' + strSelected + ']';\n    const strFirstSelected = 'data-mce-first-selected';\n    const strFirstSelectedSelector = 'td[' + strFirstSelected + '],th[' + strFirstSelected + ']';\n    const strLastSelected = 'data-mce-last-selected';\n    const strLastSelectedSelector = 'td[' + strLastSelected + '],th[' + strLastSelected + ']';\n    const attributeSelector = strAttributeSelector;\n    const ephemera = {\n      selected: strSelected,\n      selectedSelector: strSelectedSelector,\n      firstSelected: strFirstSelected,\n      firstSelectedSelector: strFirstSelectedSelector,\n      lastSelected: strLastSelected,\n      lastSelectedSelector: strLastSelectedSelector\n    };\n\n    const forMenu = (selectedCells, table, cell) => ({\n      element: cell,\n      mergable: mergable(table, selectedCells, ephemera),\n      unmergable: unmergable(selectedCells),\n      selection: selection(selectedCells)\n    });\n    const paste = (element, clipboard, generators) => ({\n      element,\n      clipboard,\n      generators\n    });\n    const pasteRows = (selectedCells, _cell, clipboard, generators) => ({\n      selection: selection(selectedCells),\n      clipboard,\n      generators\n    });\n\n    const getSelectionCellFallback = element => table(element).bind(table => retrieve(table, ephemera.firstSelectedSelector)).fold(constant(element), cells => cells[0]);\n    const getSelectionFromSelector = selector => (initCell, isRoot) => {\n      const cellName = name(initCell);\n      const cell = cellName === 'col' || cellName === 'colgroup' ? getSelectionCellFallback(initCell) : initCell;\n      return closest$1(cell, selector, isRoot);\n    };\n    const getSelectionCellOrCaption = getSelectionFromSelector('th,td,caption');\n    const getSelectionCell = getSelectionFromSelector('th,td');\n    const getCellsFromSelection = editor => fromDom(editor.model.table.getSelectedCells());\n    const getCellsFromFakeSelection = editor => filter$2(getCellsFromSelection(editor), cell => is$2(cell, ephemera.selectedSelector));\n\n    const extractSelected = cells => {\n      return table(cells[0]).map(table => {\n        const replica = extract$1(table, attributeSelector);\n        removeDataStyle(replica);\n        return [replica];\n      });\n    };\n    const serializeElements = (editor, elements) => map$1(elements, elm => editor.selection.serializer.serialize(elm.dom, {})).join('');\n    const getTextContent = elements => map$1(elements, element => element.dom.innerText).join('');\n    const registerEvents = (editor, actions) => {\n      editor.on('BeforeGetContent', e => {\n        const multiCellContext = cells => {\n          e.preventDefault();\n          extractSelected(cells).each(elements => {\n            e.content = e.format === 'text' ? getTextContent(elements) : serializeElements(editor, elements);\n          });\n        };\n        if (e.selection === true) {\n          const cells = getCellsFromFakeSelection(editor);\n          if (cells.length >= 1) {\n            multiCellContext(cells);\n          }\n        }\n      });\n      editor.on('BeforeSetContent', e => {\n        if (e.selection === true && e.paste === true) {\n          const selectedCells = getCellsFromSelection(editor);\n          head(selectedCells).each(cell => {\n            table(cell).each(table => {\n              const elements = filter$2(fromHtml(e.content), content => {\n                return name(content) !== 'meta';\n              });\n              const isTable = isTag('table');\n              if (elements.length === 1 && isTable(elements[0])) {\n                e.preventDefault();\n                const doc = SugarElement.fromDom(editor.getDoc());\n                const generators = paste$1(doc);\n                const targets = paste(cell, elements[0], generators);\n                actions.pasteCells(table, targets).each(() => {\n                  editor.focus();\n                });\n              }\n            });\n          });\n        }\n      });\n    };\n\n    const point = (element, offset) => ({\n      element,\n      offset\n    });\n\n    const scan$1 = (universe, element, direction) => {\n      if (universe.property().isText(element) && universe.property().getText(element).trim().length === 0 || universe.property().isComment(element)) {\n        return direction(element).bind(elem => {\n          return scan$1(universe, elem, direction).orThunk(() => {\n            return Optional.some(elem);\n          });\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n    const toEnd = (universe, element) => {\n      if (universe.property().isText(element)) {\n        return universe.property().getText(element).length;\n      }\n      const children = universe.property().children(element);\n      return children.length;\n    };\n    const freefallRtl$2 = (universe, element) => {\n      const candidate = scan$1(universe, element, universe.query().prevSibling).getOr(element);\n      if (universe.property().isText(candidate)) {\n        return point(candidate, toEnd(universe, candidate));\n      }\n      const children = universe.property().children(candidate);\n      return children.length > 0 ? freefallRtl$2(universe, children[children.length - 1]) : point(candidate, toEnd(universe, candidate));\n    };\n\n    const freefallRtl$1 = freefallRtl$2;\n\n    const universe$2 = DomUniverse();\n    const freefallRtl = element => {\n      return freefallRtl$1(universe$2, element);\n    };\n\n    const halve = (main, other) => {\n      if (!hasColspan(main)) {\n        const width = getGenericWidth(main);\n        width.each(w => {\n          const newWidth = w.value / 2;\n          setGenericWidth(main, newWidth, w.unit);\n          setGenericWidth(other, newWidth, w.unit);\n        });\n      }\n    };\n\n    const zero = array => map$1(array, constant(0));\n    const surround = (sizes, startIndex, endIndex, results, f) => f(sizes.slice(0, startIndex)).concat(results).concat(f(sizes.slice(endIndex)));\n    const clampDeltaHelper = predicate => (sizes, index, delta, minCellSize) => {\n      if (!predicate(delta)) {\n        return delta;\n      } else {\n        const newSize = Math.max(minCellSize, sizes[index] - Math.abs(delta));\n        const diff = Math.abs(newSize - sizes[index]);\n        return delta >= 0 ? diff : -diff;\n      }\n    };\n    const clampNegativeDelta = clampDeltaHelper(delta => delta < 0);\n    const clampDelta = clampDeltaHelper(always);\n    const resizeTable = () => {\n      const calcFixedDeltas = (sizes, index, next, delta, minCellSize) => {\n        const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize);\n        return surround(sizes, index, next + 1, [\n          clampedDelta,\n          0\n        ], zero);\n      };\n      const calcRelativeDeltas = (sizes, index, delta, minCellSize) => {\n        const ratio = (100 + delta) / 100;\n        const newThis = Math.max(minCellSize, (sizes[index] + delta) / ratio);\n        return map$1(sizes, (size, idx) => {\n          const newSize = idx === index ? newThis : size / ratio;\n          return newSize - size;\n        });\n      };\n      const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize, isRelative) => {\n        if (isRelative) {\n          return calcRelativeDeltas(sizes, index, delta, minCellSize);\n        } else {\n          return calcFixedDeltas(sizes, index, next, delta, minCellSize);\n        }\n      };\n      const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize, isRelative) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize, isRelative);\n      const resizeTable = (resizer, delta) => resizer(delta);\n      const calcRightEdgeDeltas = (sizes, _prev, index, delta, minCellSize, isRelative) => {\n        if (isRelative) {\n          return calcRelativeDeltas(sizes, index, delta, minCellSize);\n        } else {\n          const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize);\n          return zero(sizes.slice(0, index)).concat([clampedDelta]);\n        }\n      };\n      const calcRedestributedWidths = (sizes, totalWidth, pixelDelta, isRelative) => {\n        if (isRelative) {\n          const tableWidth = totalWidth + pixelDelta;\n          const ratio = tableWidth / totalWidth;\n          const newSizes = map$1(sizes, size => size / ratio);\n          return {\n            delta: ratio * 100 - 100,\n            newSizes\n          };\n        } else {\n          return {\n            delta: pixelDelta,\n            newSizes: sizes\n          };\n        }\n      };\n      return {\n        resizeTable,\n        clampTableDelta: clampNegativeDelta,\n        calcLeftEdgeDeltas,\n        calcMiddleDeltas,\n        calcRightEdgeDeltas,\n        calcRedestributedWidths\n      };\n    };\n    const preserveTable = () => {\n      const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize) => {\n        const idx = delta >= 0 ? next : index;\n        const clampedDelta = clampDelta(sizes, idx, delta, minCellSize);\n        return surround(sizes, index, next + 1, [\n          clampedDelta,\n          -clampedDelta\n        ], zero);\n      };\n      const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize);\n      const resizeTable = (resizer, delta, isLastColumn) => {\n        if (isLastColumn) {\n          resizer(delta);\n        }\n      };\n      const calcRightEdgeDeltas = (sizes, _prev, _index, delta, _minCellSize, isRelative) => {\n        if (isRelative) {\n          return zero(sizes);\n        } else {\n          const diff = delta / sizes.length;\n          return map$1(sizes, constant(diff));\n        }\n      };\n      const clampTableDelta = (sizes, index, delta, minCellSize, isLastColumn) => {\n        if (isLastColumn) {\n          if (delta >= 0) {\n            return delta;\n          } else {\n            const maxDelta = foldl(sizes, (a, b) => a + b - minCellSize, 0);\n            return Math.max(-maxDelta, delta);\n          }\n        } else {\n          return clampNegativeDelta(sizes, index, delta, minCellSize);\n        }\n      };\n      const calcRedestributedWidths = (sizes, _totalWidth, _pixelDelta, _isRelative) => ({\n        delta: 0,\n        newSizes: sizes\n      });\n      return {\n        resizeTable,\n        clampTableDelta,\n        calcLeftEdgeDeltas,\n        calcMiddleDeltas,\n        calcRightEdgeDeltas,\n        calcRedestributedWidths\n      };\n    };\n\n    const getGridSize = table => {\n      const warehouse = Warehouse.fromTable(table);\n      return warehouse.grid;\n    };\n\n    const isHeaderCell = isTag('th');\n    const isHeaderCells = cells => forall(cells, cell => isHeaderCell(cell.element));\n    const getRowHeaderType = (isHeaderRow, isHeaderCells) => {\n      if (isHeaderRow && isHeaderCells) {\n        return 'sectionCells';\n      } else if (isHeaderRow) {\n        return 'section';\n      } else {\n        return 'cells';\n      }\n    };\n    const getRowType = row => {\n      const isHeaderRow = row.section === 'thead';\n      const isHeaderCells = is(findCommonCellType(row.cells), 'th');\n      if (row.section === 'tfoot') {\n        return { type: 'footer' };\n      } else if (isHeaderRow || isHeaderCells) {\n        return {\n          type: 'header',\n          subType: getRowHeaderType(isHeaderRow, isHeaderCells)\n        };\n      } else {\n        return { type: 'body' };\n      }\n    };\n    const findCommonCellType = cells => {\n      const headerCells = filter$2(cells, cell => isHeaderCell(cell.element));\n      if (headerCells.length === 0) {\n        return Optional.some('td');\n      } else if (headerCells.length === cells.length) {\n        return Optional.some('th');\n      } else {\n        return Optional.none();\n      }\n    };\n    const findCommonRowType = rows => {\n      const rowTypes = map$1(rows, row => getRowType(row).type);\n      const hasHeader = contains$2(rowTypes, 'header');\n      const hasFooter = contains$2(rowTypes, 'footer');\n      if (!hasHeader && !hasFooter) {\n        return Optional.some('body');\n      } else {\n        const hasBody = contains$2(rowTypes, 'body');\n        if (hasHeader && !hasBody && !hasFooter) {\n          return Optional.some('header');\n        } else if (!hasHeader && !hasBody && hasFooter) {\n          return Optional.some('footer');\n        } else {\n          return Optional.none();\n        }\n      }\n    };\n    const findTableRowHeaderType = warehouse => findMap(warehouse.all, row => {\n      const rowType = getRowType(row);\n      return rowType.type === 'header' ? Optional.from(rowType.subType) : Optional.none();\n    });\n\n    const transformCell = (cell, comparator, substitution) => elementnew(substitution(cell.element, comparator), true, cell.isLocked);\n    const transformRow = (row, section) => row.section !== section ? rowcells(row.element, row.cells, section, row.isNew) : row;\n    const section = () => ({\n      transformRow,\n      transformCell: (cell, comparator, substitution) => {\n        const newCell = substitution(cell.element, comparator);\n        const fixedCell = name(newCell) !== 'td' ? mutate$1(newCell, 'td') : newCell;\n        return elementnew(fixedCell, cell.isNew, cell.isLocked);\n      }\n    });\n    const sectionCells = () => ({\n      transformRow,\n      transformCell\n    });\n    const cells = () => ({\n      transformRow: (row, section) => {\n        const newSection = section === 'thead' ? 'tbody' : section;\n        return transformRow(row, newSection);\n      },\n      transformCell\n    });\n    const fallback = () => ({\n      transformRow: identity,\n      transformCell\n    });\n    const getTableSectionType = (table, fallback) => {\n      const warehouse = Warehouse.fromTable(table);\n      const type = findTableRowHeaderType(warehouse).getOr(fallback);\n      switch (type) {\n      case 'section':\n        return section();\n      case 'sectionCells':\n        return sectionCells();\n      case 'cells':\n        return cells();\n      }\n    };\n    const TableSection = {\n      getTableSectionType,\n      section,\n      sectionCells,\n      cells,\n      fallback\n    };\n\n    const closest = target => closest$1(target, '[contenteditable]');\n    const isEditable$1 = (element, assumeEditable = false) => {\n      if (inBody(element)) {\n        return element.dom.isContentEditable;\n      } else {\n        return closest(element).fold(constant(assumeEditable), editable => getRaw(editable) === 'true');\n      }\n    };\n    const getRaw = element => element.dom.contentEditable;\n\n    const setIfNot = (element, property, value, ignore) => {\n      if (value === ignore) {\n        remove$7(element, property);\n      } else {\n        set$2(element, property, value);\n      }\n    };\n    const insert$1 = (table, selector, element) => {\n      last$2(children(table, selector)).fold(() => prepend(table, element), child => after$5(child, element));\n    };\n    const generateSection = (table, sectionName) => {\n      const section = child(table, sectionName).getOrThunk(() => {\n        const newSection = SugarElement.fromTag(sectionName, owner(table).dom);\n        if (sectionName === 'thead') {\n          insert$1(table, 'caption,colgroup', newSection);\n        } else if (sectionName === 'colgroup') {\n          insert$1(table, 'caption', newSection);\n        } else {\n          append$1(table, newSection);\n        }\n        return newSection;\n      });\n      empty(section);\n      return section;\n    };\n    const render$1 = (table, grid) => {\n      const newRows = [];\n      const newCells = [];\n      const syncRows = gridSection => map$1(gridSection, row => {\n        if (row.isNew) {\n          newRows.push(row.element);\n        }\n        const tr = row.element;\n        empty(tr);\n        each$2(row.cells, cell => {\n          if (cell.isNew) {\n            newCells.push(cell.element);\n          }\n          setIfNot(cell.element, 'colspan', cell.colspan, 1);\n          setIfNot(cell.element, 'rowspan', cell.rowspan, 1);\n          append$1(tr, cell.element);\n        });\n        return tr;\n      });\n      const syncColGroup = gridSection => bind$2(gridSection, colGroup => map$1(colGroup.cells, col => {\n        setIfNot(col.element, 'span', col.colspan, 1);\n        return col.element;\n      }));\n      const renderSection = (gridSection, sectionName) => {\n        const section = generateSection(table, sectionName);\n        const sync = sectionName === 'colgroup' ? syncColGroup : syncRows;\n        const sectionElems = sync(gridSection);\n        append(section, sectionElems);\n      };\n      const removeSection = sectionName => {\n        child(table, sectionName).each(remove$6);\n      };\n      const renderOrRemoveSection = (gridSection, sectionName) => {\n        if (gridSection.length > 0) {\n          renderSection(gridSection, sectionName);\n        } else {\n          removeSection(sectionName);\n        }\n      };\n      const headSection = [];\n      const bodySection = [];\n      const footSection = [];\n      const columnGroupsSection = [];\n      each$2(grid, row => {\n        switch (row.section) {\n        case 'thead':\n          headSection.push(row);\n          break;\n        case 'tbody':\n          bodySection.push(row);\n          break;\n        case 'tfoot':\n          footSection.push(row);\n          break;\n        case 'colgroup':\n          columnGroupsSection.push(row);\n          break;\n        }\n      });\n      renderOrRemoveSection(columnGroupsSection, 'colgroup');\n      renderOrRemoveSection(headSection, 'thead');\n      renderOrRemoveSection(bodySection, 'tbody');\n      renderOrRemoveSection(footSection, 'tfoot');\n      return {\n        newRows,\n        newCells\n      };\n    };\n    const copy = grid => map$1(grid, row => {\n      const tr = shallow(row.element);\n      each$2(row.cells, cell => {\n        const clonedCell = deep(cell.element);\n        setIfNot(clonedCell, 'colspan', cell.colspan, 1);\n        setIfNot(clonedCell, 'rowspan', cell.rowspan, 1);\n        append$1(tr, clonedCell);\n      });\n      return tr;\n    });\n\n    const getColumn = (grid, index) => {\n      return map$1(grid, row => {\n        return getCell(row, index);\n      });\n    };\n    const getRow = (grid, index) => {\n      return grid[index];\n    };\n    const findDiff = (xs, comp) => {\n      if (xs.length === 0) {\n        return 0;\n      }\n      const first = xs[0];\n      const index = findIndex(xs, x => {\n        return !comp(first.element, x.element);\n      });\n      return index.getOr(xs.length);\n    };\n    const subgrid = (grid, row, column, comparator) => {\n      const gridRow = getRow(grid, row);\n      const isColRow = gridRow.section === 'colgroup';\n      const colspan = findDiff(gridRow.cells.slice(column), comparator);\n      const rowspan = isColRow ? 1 : findDiff(getColumn(grid.slice(row), column), comparator);\n      return {\n        colspan,\n        rowspan\n      };\n    };\n\n    const toDetails = (grid, comparator) => {\n      const seen = map$1(grid, row => map$1(row.cells, never));\n      const updateSeen = (rowIndex, columnIndex, rowspan, colspan) => {\n        for (let row = rowIndex; row < rowIndex + rowspan; row++) {\n          for (let column = columnIndex; column < columnIndex + colspan; column++) {\n            seen[row][column] = true;\n          }\n        }\n      };\n      return map$1(grid, (row, rowIndex) => {\n        const details = bind$2(row.cells, (cell, columnIndex) => {\n          if (seen[rowIndex][columnIndex] === false) {\n            const result = subgrid(grid, rowIndex, columnIndex, comparator);\n            updateSeen(rowIndex, columnIndex, result.rowspan, result.colspan);\n            return [detailnew(cell.element, result.rowspan, result.colspan, cell.isNew)];\n          } else {\n            return [];\n          }\n        });\n        return rowdetailnew(row.element, details, row.section, row.isNew);\n      });\n    };\n    const toGrid = (warehouse, generators, isNew) => {\n      const grid = [];\n      each$2(warehouse.colgroups, colgroup => {\n        const colgroupCols = [];\n        for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) {\n          const element = Warehouse.getColumnAt(warehouse, columnIndex).map(column => elementnew(column.element, isNew, false)).getOrThunk(() => elementnew(generators.colGap(), true, false));\n          colgroupCols.push(element);\n        }\n        grid.push(rowcells(colgroup.element, colgroupCols, 'colgroup', isNew));\n      });\n      for (let rowIndex = 0; rowIndex < warehouse.grid.rows; rowIndex++) {\n        const rowCells = [];\n        for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) {\n          const element = Warehouse.getAt(warehouse, rowIndex, columnIndex).map(item => elementnew(item.element, isNew, item.isLocked)).getOrThunk(() => elementnew(generators.gap(), true, false));\n          rowCells.push(element);\n        }\n        const rowDetail = warehouse.all[rowIndex];\n        const row = rowcells(rowDetail.element, rowCells, rowDetail.section, isNew);\n        grid.push(row);\n      }\n      return grid;\n    };\n\n    const fromWarehouse = (warehouse, generators) => toGrid(warehouse, generators, false);\n    const toDetailList = grid => toDetails(grid, eq$1);\n    const findInWarehouse = (warehouse, element) => findMap(warehouse.all, r => find$1(r.cells, e => eq$1(element, e.element)));\n    const extractCells = (warehouse, target, predicate) => {\n      const details = map$1(target.selection, cell$1 => {\n        return cell(cell$1).bind(lc => findInWarehouse(warehouse, lc)).filter(predicate);\n      });\n      const cells = cat(details);\n      return someIf(cells.length > 0, cells);\n    };\n    const run = (operation, extract, adjustment, postAction, genWrappers) => (table, target, generators, behaviours) => {\n      const warehouse = Warehouse.fromTable(table);\n      const tableSection = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.section).getOrThunk(TableSection.fallback);\n      const output = extract(warehouse, target).map(info => {\n        const model = fromWarehouse(warehouse, generators);\n        const result = operation(model, info, eq$1, genWrappers(generators), tableSection);\n        const lockedColumns = getLockedColumnsFromGrid(result.grid);\n        const grid = toDetailList(result.grid);\n        return {\n          info,\n          grid,\n          cursor: result.cursor,\n          lockedColumns\n        };\n      });\n      return output.bind(out => {\n        const newElements = render$1(table, out.grid);\n        const tableSizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.sizing).getOrThunk(() => TableSize.getTableSize(table));\n        const resizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.resize).getOrThunk(preserveTable);\n        adjustment(table, out.grid, out.info, {\n          sizing: tableSizing,\n          resize: resizing,\n          section: tableSection\n        });\n        postAction(table);\n        remove$7(table, LOCKED_COL_ATTR);\n        if (out.lockedColumns.length > 0) {\n          set$2(table, LOCKED_COL_ATTR, out.lockedColumns.join(','));\n        }\n        return Optional.some({\n          cursor: out.cursor,\n          newRows: newElements.newRows,\n          newCells: newElements.newCells\n        });\n      });\n    };\n    const onPaste = (warehouse, target) => cell(target.element).bind(cell => findInWarehouse(warehouse, cell).map(details => {\n      const value = {\n        ...details,\n        generators: target.generators,\n        clipboard: target.clipboard\n      };\n      return value;\n    }));\n    const onPasteByEditor = (warehouse, target) => extractCells(warehouse, target, always).map(cells => ({\n      cells,\n      generators: target.generators,\n      clipboard: target.clipboard\n    }));\n    const onMergable = (_warehouse, target) => target.mergable;\n    const onUnmergable = (_warehouse, target) => target.unmergable;\n    const onCells = (warehouse, target) => extractCells(warehouse, target, always);\n    const onUnlockedCells = (warehouse, target) => extractCells(warehouse, target, detail => !detail.isLocked);\n    const isUnlockedTableCell = (warehouse, cell) => findInWarehouse(warehouse, cell).exists(detail => !detail.isLocked);\n    const allUnlocked = (warehouse, cells) => forall(cells, cell => isUnlockedTableCell(warehouse, cell));\n    const onUnlockedMergable = (warehouse, target) => onMergable(warehouse, target).filter(mergeable => allUnlocked(warehouse, mergeable.cells));\n    const onUnlockedUnmergable = (warehouse, target) => onUnmergable(warehouse, target).filter(cells => allUnlocked(warehouse, cells));\n\n    const merge$2 = (grid, bounds, comparator, substitution) => {\n      const rows = extractGridDetails(grid).rows;\n      if (rows.length === 0) {\n        return grid;\n      }\n      for (let i = bounds.startRow; i <= bounds.finishRow; i++) {\n        for (let j = bounds.startCol; j <= bounds.finishCol; j++) {\n          const row = rows[i];\n          const isLocked = getCell(row, j).isLocked;\n          mutateCell(row, j, elementnew(substitution(), false, isLocked));\n        }\n      }\n      return grid;\n    };\n    const unmerge = (grid, target, comparator, substitution) => {\n      const rows = extractGridDetails(grid).rows;\n      let first = true;\n      for (let i = 0; i < rows.length; i++) {\n        for (let j = 0; j < cellLength(rows[0]); j++) {\n          const row = rows[i];\n          const currentCell = getCell(row, j);\n          const currentCellElm = currentCell.element;\n          const isToReplace = comparator(currentCellElm, target);\n          if (isToReplace && !first) {\n            mutateCell(row, j, elementnew(substitution(), true, currentCell.isLocked));\n          } else if (isToReplace) {\n            first = false;\n          }\n        }\n      }\n      return grid;\n    };\n    const uniqueCells = (row, comparator) => {\n      return foldl(row, (rest, cell) => {\n        return exists(rest, currentCell => {\n          return comparator(currentCell.element, cell.element);\n        }) ? rest : rest.concat([cell]);\n      }, []);\n    };\n    const splitCols = (grid, index, comparator, substitution) => {\n      if (index > 0 && index < grid[0].cells.length) {\n        each$2(grid, row => {\n          const prevCell = row.cells[index - 1];\n          let offset = 0;\n          const substitute = substitution();\n          while (row.cells.length > index + offset && comparator(prevCell.element, row.cells[index + offset].element)) {\n            mutateCell(row, index + offset, elementnew(substitute, true, row.cells[index + offset].isLocked));\n            offset++;\n          }\n        });\n      }\n      return grid;\n    };\n    const splitRows = (grid, index, comparator, substitution) => {\n      const rows = extractGridDetails(grid).rows;\n      if (index > 0 && index < rows.length) {\n        const rowPrevCells = rows[index - 1].cells;\n        const cells = uniqueCells(rowPrevCells, comparator);\n        each$2(cells, cell => {\n          let replacement = Optional.none();\n          for (let i = index; i < rows.length; i++) {\n            for (let j = 0; j < cellLength(rows[0]); j++) {\n              const row = rows[i];\n              const current = getCell(row, j);\n              const isToReplace = comparator(current.element, cell.element);\n              if (isToReplace) {\n                if (replacement.isNone()) {\n                  replacement = Optional.some(substitution());\n                }\n                replacement.each(sub => {\n                  mutateCell(row, j, elementnew(sub, true, current.isLocked));\n                });\n              }\n            }\n          }\n        });\n      }\n      return grid;\n    };\n\n    const value$1 = value => {\n      const applyHelper = fn => fn(value);\n      const constHelper = constant(value);\n      const outputHelper = () => output;\n      const output = {\n        tag: true,\n        inner: value,\n        fold: (_onError, onValue) => onValue(value),\n        isValue: always,\n        isError: never,\n        map: mapper => Result.value(mapper(value)),\n        mapError: outputHelper,\n        bind: applyHelper,\n        exists: applyHelper,\n        forall: applyHelper,\n        getOr: constHelper,\n        or: outputHelper,\n        getOrThunk: constHelper,\n        orThunk: outputHelper,\n        getOrDie: constHelper,\n        each: fn => {\n          fn(value);\n        },\n        toOptional: () => Optional.some(value)\n      };\n      return output;\n    };\n    const error = error => {\n      const outputHelper = () => output;\n      const output = {\n        tag: false,\n        inner: error,\n        fold: (onError, _onValue) => onError(error),\n        isValue: never,\n        isError: always,\n        map: outputHelper,\n        mapError: mapper => Result.error(mapper(error)),\n        bind: outputHelper,\n        exists: never,\n        forall: always,\n        getOr: identity,\n        or: identity,\n        getOrThunk: apply,\n        orThunk: apply,\n        getOrDie: die(String(error)),\n        each: noop,\n        toOptional: Optional.none\n      };\n      return output;\n    };\n    const fromOption = (optional, err) => optional.fold(() => error(err), value$1);\n    const Result = {\n      value: value$1,\n      error,\n      fromOption\n    };\n\n    const measure = (startAddress, gridA, gridB) => {\n      if (startAddress.row >= gridA.length || startAddress.column > cellLength(gridA[0])) {\n        return Result.error('invalid start address out of table bounds, row: ' + startAddress.row + ', column: ' + startAddress.column);\n      }\n      const rowRemainder = gridA.slice(startAddress.row);\n      const colRemainder = rowRemainder[0].cells.slice(startAddress.column);\n      const colRequired = cellLength(gridB[0]);\n      const rowRequired = gridB.length;\n      return Result.value({\n        rowDelta: rowRemainder.length - rowRequired,\n        colDelta: colRemainder.length - colRequired\n      });\n    };\n    const measureWidth = (gridA, gridB) => {\n      const colLengthA = cellLength(gridA[0]);\n      const colLengthB = cellLength(gridB[0]);\n      return {\n        rowDelta: 0,\n        colDelta: colLengthA - colLengthB\n      };\n    };\n    const measureHeight = (gridA, gridB) => {\n      const rowLengthA = gridA.length;\n      const rowLengthB = gridB.length;\n      return {\n        rowDelta: rowLengthA - rowLengthB,\n        colDelta: 0\n      };\n    };\n    const generateElements = (amount, row, generators, isLocked) => {\n      const generator = row.section === 'colgroup' ? generators.col : generators.cell;\n      return range$1(amount, idx => elementnew(generator(), true, isLocked(idx)));\n    };\n    const rowFill = (grid, amount, generators, lockedColumns) => {\n      const exampleRow = grid[grid.length - 1];\n      return grid.concat(range$1(amount, () => {\n        const generator = exampleRow.section === 'colgroup' ? generators.colgroup : generators.row;\n        const row = clone(exampleRow, generator, identity);\n        const elements = generateElements(row.cells.length, row, generators, idx => has$1(lockedColumns, idx.toString()));\n        return setCells(row, elements);\n      }));\n    };\n    const colFill = (grid, amount, generators, startIndex) => map$1(grid, row => {\n      const newChildren = generateElements(amount, row, generators, never);\n      return addCells(row, startIndex, newChildren);\n    });\n    const lockedColFill = (grid, generators, lockedColumns) => map$1(grid, row => {\n      return foldl(lockedColumns, (acc, colNum) => {\n        const newChild = generateElements(1, row, generators, always)[0];\n        return addCell(acc, colNum, newChild);\n      }, row);\n    });\n    const tailor = (gridA, delta, generators) => {\n      const fillCols = delta.colDelta < 0 ? colFill : identity;\n      const fillRows = delta.rowDelta < 0 ? rowFill : identity;\n      const lockedColumns = getLockedColumnsFromGrid(gridA);\n      const gridWidth = cellLength(gridA[0]);\n      const isLastColLocked = exists(lockedColumns, locked => locked === gridWidth - 1);\n      const modifiedCols = fillCols(gridA, Math.abs(delta.colDelta), generators, isLastColLocked ? gridWidth - 1 : gridWidth);\n      const newLockedColumns = getLockedColumnsFromGrid(modifiedCols);\n      return fillRows(modifiedCols, Math.abs(delta.rowDelta), generators, mapToObject(newLockedColumns, always));\n    };\n\n    const isSpanning = (grid, row, col, comparator) => {\n      const candidate = getCell(grid[row], col);\n      const matching = curry(comparator, candidate.element);\n      const currentRow = grid[row];\n      return grid.length > 1 && cellLength(currentRow) > 1 && (col > 0 && matching(getCellElement(currentRow, col - 1)) || col < currentRow.cells.length - 1 && matching(getCellElement(currentRow, col + 1)) || row > 0 && matching(getCellElement(grid[row - 1], col)) || row < grid.length - 1 && matching(getCellElement(grid[row + 1], col)));\n    };\n    const mergeTables = (startAddress, gridA, gridBRows, generator, comparator, lockedColumns) => {\n      const startRow = startAddress.row;\n      const startCol = startAddress.column;\n      const mergeHeight = gridBRows.length;\n      const mergeWidth = cellLength(gridBRows[0]);\n      const endRow = startRow + mergeHeight;\n      const endCol = startCol + mergeWidth + lockedColumns.length;\n      const lockedColumnObj = mapToObject(lockedColumns, always);\n      for (let r = startRow; r < endRow; r++) {\n        let skippedCol = 0;\n        for (let c = startCol; c < endCol; c++) {\n          if (lockedColumnObj[c]) {\n            skippedCol++;\n            continue;\n          }\n          if (isSpanning(gridA, r, c, comparator)) {\n            unmerge(gridA, getCellElement(gridA[r], c), comparator, generator.cell);\n          }\n          const gridBColIndex = c - startCol - skippedCol;\n          const newCell = getCell(gridBRows[r - startRow], gridBColIndex);\n          const newCellElm = newCell.element;\n          const replacement = generator.replace(newCellElm);\n          mutateCell(gridA[r], c, elementnew(replacement, true, newCell.isLocked));\n        }\n      }\n      return gridA;\n    };\n    const getValidStartAddress = (currentStartAddress, grid, lockedColumns) => {\n      const gridColLength = cellLength(grid[0]);\n      const adjustedRowAddress = extractGridDetails(grid).cols.length + currentStartAddress.row;\n      const possibleColAddresses = range$1(gridColLength - currentStartAddress.column, num => num + currentStartAddress.column);\n      const validColAddress = find$1(possibleColAddresses, num => forall(lockedColumns, col => col !== num)).getOr(gridColLength - 1);\n      return {\n        row: adjustedRowAddress,\n        column: validColAddress\n      };\n    };\n    const getLockedColumnsWithinBounds = (startAddress, rows, lockedColumns) => filter$2(lockedColumns, colNum => colNum >= startAddress.column && colNum <= cellLength(rows[0]) + startAddress.column);\n    const merge$1 = (startAddress, gridA, gridB, generator, comparator) => {\n      const lockedColumns = getLockedColumnsFromGrid(gridA);\n      const validStartAddress = getValidStartAddress(startAddress, gridA, lockedColumns);\n      const gridBRows = extractGridDetails(gridB).rows;\n      const lockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, lockedColumns);\n      const result = measure(validStartAddress, gridA, gridBRows);\n      return result.map(diff => {\n        const delta = {\n          ...diff,\n          colDelta: diff.colDelta - lockedColumnsWithinBounds.length\n        };\n        const fittedGrid = tailor(gridA, delta, generator);\n        const newLockedColumns = getLockedColumnsFromGrid(fittedGrid);\n        const newLockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, newLockedColumns);\n        return mergeTables(validStartAddress, fittedGrid, gridBRows, generator, comparator, newLockedColumnsWithinBounds);\n      });\n    };\n    const insertCols = (index, gridA, gridB, generator, comparator) => {\n      splitCols(gridA, index, comparator, generator.cell);\n      const delta = measureHeight(gridB, gridA);\n      const fittedNewGrid = tailor(gridB, delta, generator);\n      const secondDelta = measureHeight(gridA, fittedNewGrid);\n      const fittedOldGrid = tailor(gridA, secondDelta, generator);\n      return map$1(fittedOldGrid, (gridRow, i) => {\n        return addCells(gridRow, index, fittedNewGrid[i].cells);\n      });\n    };\n    const insertRows = (index, gridA, gridB, generator, comparator) => {\n      splitRows(gridA, index, comparator, generator.cell);\n      const locked = getLockedColumnsFromGrid(gridA);\n      const diff = measureWidth(gridA, gridB);\n      const delta = {\n        ...diff,\n        colDelta: diff.colDelta - locked.length\n      };\n      const fittedOldGrid = tailor(gridA, delta, generator);\n      const {\n        cols: oldCols,\n        rows: oldRows\n      } = extractGridDetails(fittedOldGrid);\n      const newLocked = getLockedColumnsFromGrid(fittedOldGrid);\n      const secondDiff = measureWidth(gridB, gridA);\n      const secondDelta = {\n        ...secondDiff,\n        colDelta: secondDiff.colDelta + newLocked.length\n      };\n      const fittedGridB = lockedColFill(gridB, generator, newLocked);\n      const fittedNewGrid = tailor(fittedGridB, secondDelta, generator);\n      return [\n        ...oldCols,\n        ...oldRows.slice(0, index),\n        ...fittedNewGrid,\n        ...oldRows.slice(index, oldRows.length)\n      ];\n    };\n\n    const cloneRow = (row, cloneCell, comparator, substitution) => clone(row, elem => substitution(elem, comparator), cloneCell);\n    const insertRowAt = (grid, index, example, comparator, substitution) => {\n      const {rows, cols} = extractGridDetails(grid);\n      const before = rows.slice(0, index);\n      const after = rows.slice(index);\n      const newRow = cloneRow(rows[example], (ex, c) => {\n        const withinSpan = index > 0 && index < rows.length && comparator(getCellElement(rows[index - 1], c), getCellElement(rows[index], c));\n        const ret = withinSpan ? getCell(rows[index], c) : elementnew(substitution(ex.element, comparator), true, ex.isLocked);\n        return ret;\n      }, comparator, substitution);\n      return [\n        ...cols,\n        ...before,\n        newRow,\n        ...after\n      ];\n    };\n    const getElementFor = (row, column, section, withinSpan, example, comparator, substitution) => {\n      if (section === 'colgroup' || !withinSpan) {\n        const cell = getCell(row, example);\n        return elementnew(substitution(cell.element, comparator), true, false);\n      } else {\n        return getCell(row, column);\n      }\n    };\n    const insertColumnAt = (grid, index, example, comparator, substitution) => map$1(grid, row => {\n      const withinSpan = index > 0 && index < cellLength(row) && comparator(getCellElement(row, index - 1), getCellElement(row, index));\n      const sub = getElementFor(row, index, row.section, withinSpan, example, comparator, substitution);\n      return addCell(row, index, sub);\n    });\n    const deleteColumnsAt = (grid, columns) => bind$2(grid, row => {\n      const existingCells = row.cells;\n      const cells = foldr(columns, (acc, column) => column >= 0 && column < acc.length ? acc.slice(0, column).concat(acc.slice(column + 1)) : acc, existingCells);\n      return cells.length > 0 ? [rowcells(row.element, cells, row.section, row.isNew)] : [];\n    });\n    const deleteRowsAt = (grid, start, finish) => {\n      const {rows, cols} = extractGridDetails(grid);\n      return [\n        ...cols,\n        ...rows.slice(0, start),\n        ...rows.slice(finish + 1)\n      ];\n    };\n\n    const notInStartRow = (grid, rowIndex, colIndex, comparator) => getCellElement(grid[rowIndex], colIndex) !== undefined && (rowIndex > 0 && comparator(getCellElement(grid[rowIndex - 1], colIndex), getCellElement(grid[rowIndex], colIndex)));\n    const notInStartColumn = (row, index, comparator) => index > 0 && comparator(getCellElement(row, index - 1), getCellElement(row, index));\n    const isDuplicatedCell = (grid, rowIndex, colIndex, comparator) => notInStartRow(grid, rowIndex, colIndex, comparator) || notInStartColumn(grid[rowIndex], colIndex, comparator);\n    const rowReplacerPredicate = (targetRow, columnHeaders) => {\n      const entireTableIsHeader = forall(columnHeaders, identity) && isHeaderCells(targetRow.cells);\n      return entireTableIsHeader ? always : (cell, _rowIndex, colIndex) => {\n        const type = name(cell.element);\n        return !(type === 'th' && columnHeaders[colIndex]);\n      };\n    };\n    const columnReplacePredicate = (targetColumn, rowHeaders) => {\n      const entireTableIsHeader = forall(rowHeaders, identity) && isHeaderCells(targetColumn);\n      return entireTableIsHeader ? always : (cell, rowIndex, _colIndex) => {\n        const type = name(cell.element);\n        return !(type === 'th' && rowHeaders[rowIndex]);\n      };\n    };\n    const determineScope = (applyScope, cell, newScope, isInHeader) => {\n      const hasSpan = scope => scope === 'row' ? hasRowspan(cell) : hasColspan(cell);\n      const getScope = scope => hasSpan(scope) ? `${ scope }group` : scope;\n      if (applyScope) {\n        return isHeaderCell(cell) ? getScope(newScope) : null;\n      } else if (isInHeader && isHeaderCell(cell)) {\n        const oppositeScope = newScope === 'row' ? 'col' : 'row';\n        return getScope(oppositeScope);\n      } else {\n        return null;\n      }\n    };\n    const rowScopeGenerator = (applyScope, columnHeaders) => (cell, rowIndex, columnIndex) => Optional.some(determineScope(applyScope, cell.element, 'col', columnHeaders[columnIndex]));\n    const columnScopeGenerator = (applyScope, rowHeaders) => (cell, rowIndex) => Optional.some(determineScope(applyScope, cell.element, 'row', rowHeaders[rowIndex]));\n    const replace = (cell, comparator, substitute) => elementnew(substitute(cell.element, comparator), true, cell.isLocked);\n    const replaceIn = (grid, targets, comparator, substitute, replacer, genScope, shouldReplace) => {\n      const isTarget = cell => {\n        return exists(targets, target => {\n          return comparator(cell.element, target.element);\n        });\n      };\n      return map$1(grid, (row, rowIndex) => {\n        return mapCells(row, (cell, colIndex) => {\n          if (isTarget(cell)) {\n            const newCell = shouldReplace(cell, rowIndex, colIndex) ? replacer(cell, comparator, substitute) : cell;\n            genScope(newCell, rowIndex, colIndex).each(scope => {\n              setOptions(newCell.element, { scope: Optional.from(scope) });\n            });\n            return newCell;\n          } else {\n            return cell;\n          }\n        });\n      });\n    };\n    const getColumnCells = (rows, columnIndex, comparator) => bind$2(rows, (row, i) => {\n      return isDuplicatedCell(rows, i, columnIndex, comparator) ? [] : [getCell(row, columnIndex)];\n    });\n    const getRowCells = (rows, rowIndex, comparator) => {\n      const targetRow = rows[rowIndex];\n      return bind$2(targetRow.cells, (item, i) => {\n        return isDuplicatedCell(rows, rowIndex, i, comparator) ? [] : [item];\n      });\n    };\n    const replaceColumns = (grid, indexes, applyScope, comparator, substitution) => {\n      const rows = extractGridDetails(grid).rows;\n      const targets = bind$2(indexes, index => getColumnCells(rows, index, comparator));\n      const rowHeaders = map$1(rows, row => isHeaderCells(row.cells));\n      const shouldReplaceCell = columnReplacePredicate(targets, rowHeaders);\n      const scopeGenerator = columnScopeGenerator(applyScope, rowHeaders);\n      return replaceIn(grid, targets, comparator, substitution, replace, scopeGenerator, shouldReplaceCell);\n    };\n    const replaceRows = (grid, indexes, section, applyScope, comparator, substitution, tableSection) => {\n      const {cols, rows} = extractGridDetails(grid);\n      const targetRow = rows[indexes[0]];\n      const targets = bind$2(indexes, index => getRowCells(rows, index, comparator));\n      const columnHeaders = map$1(targetRow.cells, (_cell, index) => isHeaderCells(getColumnCells(rows, index, comparator)));\n      const newRows = [...rows];\n      each$2(indexes, index => {\n        newRows[index] = tableSection.transformRow(rows[index], section);\n      });\n      const newGrid = [\n        ...cols,\n        ...newRows\n      ];\n      const shouldReplaceCell = rowReplacerPredicate(targetRow, columnHeaders);\n      const scopeGenerator = rowScopeGenerator(applyScope, columnHeaders);\n      return replaceIn(newGrid, targets, comparator, substitution, tableSection.transformCell, scopeGenerator, shouldReplaceCell);\n    };\n    const replaceCells = (grid, details, comparator, substitution) => {\n      const rows = extractGridDetails(grid).rows;\n      const targetCells = map$1(details, detail => getCell(rows[detail.row], detail.column));\n      return replaceIn(grid, targetCells, comparator, substitution, replace, Optional.none, always);\n    };\n\n    const generate = cases => {\n      if (!isArray(cases)) {\n        throw new Error('cases must be an array');\n      }\n      if (cases.length === 0) {\n        throw new Error('there must be at least one case');\n      }\n      const constructors = [];\n      const adt = {};\n      each$2(cases, (acase, count) => {\n        const keys$1 = keys(acase);\n        if (keys$1.length !== 1) {\n          throw new Error('one and only one name per case');\n        }\n        const key = keys$1[0];\n        const value = acase[key];\n        if (adt[key] !== undefined) {\n          throw new Error('duplicate key detected:' + key);\n        } else if (key === 'cata') {\n          throw new Error('cannot have a case named cata (sorry)');\n        } else if (!isArray(value)) {\n          throw new Error('case arguments must be an array');\n        }\n        constructors.push(key);\n        adt[key] = (...args) => {\n          const argLength = args.length;\n          if (argLength !== value.length) {\n            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);\n          }\n          const match = branches => {\n            const branchKeys = keys(branches);\n            if (constructors.length !== branchKeys.length) {\n              throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\\nActual: ' + branchKeys.join(','));\n            }\n            const allReqd = forall(constructors, reqKey => {\n              return contains$2(branchKeys, reqKey);\n            });\n            if (!allReqd) {\n              throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\\nRequired: ' + constructors.join(', '));\n            }\n            return branches[key].apply(null, args);\n          };\n          return {\n            fold: (...foldArgs) => {\n              if (foldArgs.length !== cases.length) {\n                throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + foldArgs.length);\n              }\n              const target = foldArgs[count];\n              return target.apply(null, args);\n            },\n            match,\n            log: label => {\n              console.log(label, {\n                constructors,\n                constructor: key,\n                params: args\n              });\n            }\n          };\n        };\n      });\n      return adt;\n    };\n    const Adt = { generate };\n\n    const adt$6 = Adt.generate([\n      { none: [] },\n      { only: ['index'] },\n      {\n        left: [\n          'index',\n          'next'\n        ]\n      },\n      {\n        middle: [\n          'prev',\n          'index',\n          'next'\n        ]\n      },\n      {\n        right: [\n          'prev',\n          'index'\n        ]\n      }\n    ]);\n    const ColumnContext = { ...adt$6 };\n\n    const neighbours = (input, index) => {\n      if (input.length === 0) {\n        return ColumnContext.none();\n      }\n      if (input.length === 1) {\n        return ColumnContext.only(0);\n      }\n      if (index === 0) {\n        return ColumnContext.left(0, 1);\n      }\n      if (index === input.length - 1) {\n        return ColumnContext.right(index - 1, index);\n      }\n      if (index > 0 && index < input.length - 1) {\n        return ColumnContext.middle(index - 1, index, index + 1);\n      }\n      return ColumnContext.none();\n    };\n    const determine = (input, column, step, tableSize, resize) => {\n      const result = input.slice(0);\n      const context = neighbours(input, column);\n      const onNone = constant(map$1(result, constant(0)));\n      const onOnly = index => tableSize.singleColumnWidth(result[index], step);\n      const onLeft = (index, next) => resize.calcLeftEdgeDeltas(result, index, next, step, tableSize.minCellWidth(), tableSize.isRelative);\n      const onMiddle = (prev, index, next) => resize.calcMiddleDeltas(result, prev, index, next, step, tableSize.minCellWidth(), tableSize.isRelative);\n      const onRight = (prev, index) => resize.calcRightEdgeDeltas(result, prev, index, step, tableSize.minCellWidth(), tableSize.isRelative);\n      return context.fold(onNone, onOnly, onLeft, onMiddle, onRight);\n    };\n\n    const total = (start, end, measures) => {\n      let r = 0;\n      for (let i = start; i < end; i++) {\n        r += measures[i] !== undefined ? measures[i] : 0;\n      }\n      return r;\n    };\n    const recalculateWidthForCells = (warehouse, widths) => {\n      const all = Warehouse.justCells(warehouse);\n      return map$1(all, cell => {\n        const width = total(cell.column, cell.column + cell.colspan, widths);\n        return {\n          element: cell.element,\n          width,\n          colspan: cell.colspan\n        };\n      });\n    };\n    const recalculateWidthForColumns = (warehouse, widths) => {\n      const groups = Warehouse.justColumns(warehouse);\n      return map$1(groups, (column, index) => ({\n        element: column.element,\n        width: widths[index],\n        colspan: column.colspan\n      }));\n    };\n    const recalculateHeightForCells = (warehouse, heights) => {\n      const all = Warehouse.justCells(warehouse);\n      return map$1(all, cell => {\n        const height = total(cell.row, cell.row + cell.rowspan, heights);\n        return {\n          element: cell.element,\n          height,\n          rowspan: cell.rowspan\n        };\n      });\n    };\n    const matchRowHeight = (warehouse, heights) => {\n      return map$1(warehouse.all, (row, i) => {\n        return {\n          element: row.element,\n          height: heights[i]\n        };\n      });\n    };\n\n    const sumUp = newSize => foldr(newSize, (b, a) => b + a, 0);\n    const recalculate = (warehouse, widths) => {\n      if (Warehouse.hasColumns(warehouse)) {\n        return recalculateWidthForColumns(warehouse, widths);\n      } else {\n        return recalculateWidthForCells(warehouse, widths);\n      }\n    };\n    const recalculateAndApply = (warehouse, widths, tableSize) => {\n      const newSizes = recalculate(warehouse, widths);\n      each$2(newSizes, cell => {\n        tableSize.setElementWidth(cell.element, cell.width);\n      });\n    };\n    const adjustWidth = (table, delta, index, resizing, tableSize) => {\n      const warehouse = Warehouse.fromTable(table);\n      const step = tableSize.getCellDelta(delta);\n      const widths = tableSize.getWidths(warehouse, tableSize);\n      const isLastColumn = index === warehouse.grid.columns - 1;\n      const clampedStep = resizing.clampTableDelta(widths, index, step, tableSize.minCellWidth(), isLastColumn);\n      const deltas = determine(widths, index, clampedStep, tableSize, resizing);\n      const newWidths = map$1(deltas, (dx, i) => dx + widths[i]);\n      recalculateAndApply(warehouse, newWidths, tableSize);\n      resizing.resizeTable(tableSize.adjustTableWidth, clampedStep, isLastColumn);\n    };\n    const adjustHeight = (table, delta, index, direction) => {\n      const warehouse = Warehouse.fromTable(table);\n      const heights = getPixelHeights(warehouse, table, direction);\n      const newHeights = map$1(heights, (dy, i) => index === i ? Math.max(delta + dy, minHeight()) : dy);\n      const newCellSizes = recalculateHeightForCells(warehouse, newHeights);\n      const newRowSizes = matchRowHeight(warehouse, newHeights);\n      each$2(newRowSizes, row => {\n        setHeight(row.element, row.height);\n      });\n      each$2(newCellSizes, cell => {\n        setHeight(cell.element, cell.height);\n      });\n      const total = sumUp(newHeights);\n      setHeight(table, total);\n    };\n    const adjustAndRedistributeWidths$1 = (_table, list, details, tableSize, resizeBehaviour) => {\n      const warehouse = Warehouse.generate(list);\n      const sizes = tableSize.getWidths(warehouse, tableSize);\n      const tablePixelWidth = tableSize.pixelWidth();\n      const {newSizes, delta} = resizeBehaviour.calcRedestributedWidths(sizes, tablePixelWidth, details.pixelDelta, tableSize.isRelative);\n      recalculateAndApply(warehouse, newSizes, tableSize);\n      tableSize.adjustTableWidth(delta);\n    };\n    const adjustWidthTo = (_table, list, _info, tableSize) => {\n      const warehouse = Warehouse.generate(list);\n      const widths = tableSize.getWidths(warehouse, tableSize);\n      recalculateAndApply(warehouse, widths, tableSize);\n    };\n\n    const uniqueColumns = details => {\n      const uniqueCheck = (rest, detail) => {\n        const columnExists = exists(rest, currentDetail => currentDetail.column === detail.column);\n        return columnExists ? rest : rest.concat([detail]);\n      };\n      return foldl(details, uniqueCheck, []).sort((detailA, detailB) => detailA.column - detailB.column);\n    };\n\n    const isCol = isTag('col');\n    const isColgroup = isTag('colgroup');\n    const isRow$1 = element => name(element) === 'tr' || isColgroup(element);\n    const elementToData = element => {\n      const colspan = getAttrValue(element, 'colspan', 1);\n      const rowspan = getAttrValue(element, 'rowspan', 1);\n      return {\n        element,\n        colspan,\n        rowspan\n      };\n    };\n    const modification = (generators, toData = elementToData) => {\n      const nuCell = data => isCol(data.element) ? generators.col(data) : generators.cell(data);\n      const nuRow = data => isColgroup(data.element) ? generators.colgroup(data) : generators.row(data);\n      const add = element => {\n        if (isRow$1(element)) {\n          return nuRow({ element });\n        } else {\n          const cell = element;\n          const replacement = nuCell(toData(cell));\n          recent = Optional.some({\n            item: cell,\n            replacement\n          });\n          return replacement;\n        }\n      };\n      let recent = Optional.none();\n      const getOrInit = (element, comparator) => {\n        return recent.fold(() => {\n          return add(element);\n        }, p => {\n          return comparator(element, p.item) ? p.replacement : add(element);\n        });\n      };\n      return { getOrInit };\n    };\n    const transform$1 = tag => {\n      return generators => {\n        const list = [];\n        const find = (element, comparator) => {\n          return find$1(list, x => {\n            return comparator(x.item, element);\n          });\n        };\n        const makeNew = element => {\n          const attrs = tag === 'td' ? { scope: null } : {};\n          const cell = generators.replace(element, tag, attrs);\n          list.push({\n            item: element,\n            sub: cell\n          });\n          return cell;\n        };\n        const replaceOrInit = (element, comparator) => {\n          if (isRow$1(element) || isCol(element)) {\n            return element;\n          } else {\n            const cell = element;\n            return find(cell, comparator).fold(() => {\n              return makeNew(cell);\n            }, p => {\n              return comparator(element, p.item) ? p.sub : makeNew(cell);\n            });\n          }\n        };\n        return { replaceOrInit };\n      };\n    };\n    const getScopeAttribute = cell => getOpt(cell, 'scope').map(attribute => attribute.substr(0, 3));\n    const merging = generators => {\n      const unmerge = cell => {\n        const scope = getScopeAttribute(cell);\n        scope.each(attribute => set$2(cell, 'scope', attribute));\n        return () => {\n          const raw = generators.cell({\n            element: cell,\n            colspan: 1,\n            rowspan: 1\n          });\n          remove$5(raw, 'width');\n          remove$5(cell, 'width');\n          scope.each(attribute => set$2(raw, 'scope', attribute));\n          return raw;\n        };\n      };\n      const merge = cells => {\n        const getScopeProperty = () => {\n          const stringAttributes = cat(map$1(cells, getScopeAttribute));\n          if (stringAttributes.length === 0) {\n            return Optional.none();\n          } else {\n            const baseScope = stringAttributes[0];\n            const scopes = [\n              'row',\n              'col'\n            ];\n            const isMixed = exists(stringAttributes, attribute => {\n              return attribute !== baseScope && contains$2(scopes, attribute);\n            });\n            return isMixed ? Optional.none() : Optional.from(baseScope);\n          }\n        };\n        remove$5(cells[0], 'width');\n        getScopeProperty().fold(() => remove$7(cells[0], 'scope'), attribute => set$2(cells[0], 'scope', attribute + 'group'));\n        return constant(cells[0]);\n      };\n      return {\n        unmerge,\n        merge\n      };\n    };\n    const Generators = {\n      modification,\n      transform: transform$1,\n      merging\n    };\n\n    const blockList = [\n      'body',\n      'p',\n      'div',\n      'article',\n      'aside',\n      'figcaption',\n      'figure',\n      'footer',\n      'header',\n      'nav',\n      'section',\n      'ol',\n      'ul',\n      'table',\n      'thead',\n      'tfoot',\n      'tbody',\n      'caption',\n      'tr',\n      'td',\n      'th',\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'blockquote',\n      'pre',\n      'address'\n    ];\n    const isList$1 = (universe, item) => {\n      const tagName = universe.property().name(item);\n      return contains$2([\n        'ol',\n        'ul'\n      ], tagName);\n    };\n    const isBlock$1 = (universe, item) => {\n      const tagName = universe.property().name(item);\n      return contains$2(blockList, tagName);\n    };\n    const isEmptyTag$1 = (universe, item) => {\n      return contains$2([\n        'br',\n        'img',\n        'hr',\n        'input'\n      ], universe.property().name(item));\n    };\n\n    const universe$1 = DomUniverse();\n    const isBlock = element => {\n      return isBlock$1(universe$1, element);\n    };\n    const isList = element => {\n      return isList$1(universe$1, element);\n    };\n    const isEmptyTag = element => {\n      return isEmptyTag$1(universe$1, element);\n    };\n\n    const merge = cells => {\n      const isBr = isTag('br');\n      const advancedBr = children => {\n        return forall(children, c => {\n          return isBr(c) || isText(c) && get$6(c).trim().length === 0;\n        });\n      };\n      const isListItem = el => {\n        return name(el) === 'li' || ancestor$2(el, isList).isSome();\n      };\n      const siblingIsBlock = el => {\n        return nextSibling(el).map(rightSibling => {\n          if (isBlock(rightSibling)) {\n            return true;\n          }\n          if (isEmptyTag(rightSibling)) {\n            return name(rightSibling) === 'img' ? false : true;\n          }\n          return false;\n        }).getOr(false);\n      };\n      const markCell = cell => {\n        return last$1(cell).bind(rightEdge => {\n          const rightSiblingIsBlock = siblingIsBlock(rightEdge);\n          return parent(rightEdge).map(parent => {\n            return rightSiblingIsBlock === true || isListItem(parent) || isBr(rightEdge) || isBlock(parent) && !eq$1(cell, parent) ? [] : [SugarElement.fromTag('br')];\n          });\n        }).getOr([]);\n      };\n      const markContent = () => {\n        const content = bind$2(cells, cell => {\n          const children = children$2(cell);\n          return advancedBr(children) ? [] : children.concat(markCell(cell));\n        });\n        return content.length === 0 ? [SugarElement.fromTag('br')] : content;\n      };\n      const contents = markContent();\n      empty(cells[0]);\n      append(cells[0], contents);\n    };\n\n    const isEditable = elem => isEditable$1(elem, true);\n    const prune = table => {\n      const cells = cells$1(table);\n      if (cells.length === 0) {\n        remove$6(table);\n      }\n    };\n    const outcome = (grid, cursor) => ({\n      grid,\n      cursor\n    });\n    const findEditableCursorPosition = rows => findMap(rows, row => findMap(row.cells, cell => {\n      const elem = cell.element;\n      return someIf(isEditable(elem), elem);\n    }));\n    const elementFromGrid = (grid, row, column) => {\n      var _a, _b;\n      const rows = extractGridDetails(grid).rows;\n      return Optional.from((_b = (_a = rows[row]) === null || _a === void 0 ? void 0 : _a.cells[column]) === null || _b === void 0 ? void 0 : _b.element).filter(isEditable).orThunk(() => findEditableCursorPosition(rows));\n    };\n    const bundle = (grid, row, column) => {\n      const cursorElement = elementFromGrid(grid, row, column);\n      return outcome(grid, cursorElement);\n    };\n    const uniqueRows = details => {\n      const rowCompilation = (rest, detail) => {\n        const rowExists = exists(rest, currentDetail => currentDetail.row === detail.row);\n        return rowExists ? rest : rest.concat([detail]);\n      };\n      return foldl(details, rowCompilation, []).sort((detailA, detailB) => detailA.row - detailB.row);\n    };\n    const opInsertRowsBefore = (grid, details, comparator, genWrappers) => {\n      const targetIndex = details[0].row;\n      const rows = uniqueRows(details);\n      const newGrid = foldr(rows, (acc, row) => {\n        const newG = insertRowAt(acc.grid, targetIndex, row.row + acc.delta, comparator, genWrappers.getOrInit);\n        return {\n          grid: newG,\n          delta: acc.delta + 1\n        };\n      }, {\n        grid,\n        delta: 0\n      }).grid;\n      return bundle(newGrid, targetIndex, details[0].column);\n    };\n    const opInsertRowsAfter = (grid, details, comparator, genWrappers) => {\n      const rows = uniqueRows(details);\n      const target = rows[rows.length - 1];\n      const targetIndex = target.row + target.rowspan;\n      const newGrid = foldr(rows, (newG, row) => {\n        return insertRowAt(newG, targetIndex, row.row, comparator, genWrappers.getOrInit);\n      }, grid);\n      return bundle(newGrid, targetIndex, details[0].column);\n    };\n    const opInsertColumnsBefore = (grid, extractDetail, comparator, genWrappers) => {\n      const details = extractDetail.details;\n      const columns = uniqueColumns(details);\n      const targetIndex = columns[0].column;\n      const newGrid = foldr(columns, (acc, col) => {\n        const newG = insertColumnAt(acc.grid, targetIndex, col.column + acc.delta, comparator, genWrappers.getOrInit);\n        return {\n          grid: newG,\n          delta: acc.delta + 1\n        };\n      }, {\n        grid,\n        delta: 0\n      }).grid;\n      return bundle(newGrid, details[0].row, targetIndex);\n    };\n    const opInsertColumnsAfter = (grid, extractDetail, comparator, genWrappers) => {\n      const details = extractDetail.details;\n      const target = details[details.length - 1];\n      const targetIndex = target.column + target.colspan;\n      const columns = uniqueColumns(details);\n      const newGrid = foldr(columns, (newG, col) => {\n        return insertColumnAt(newG, targetIndex, col.column, comparator, genWrappers.getOrInit);\n      }, grid);\n      return bundle(newGrid, details[0].row, targetIndex);\n    };\n    const opMakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => {\n      const columns = uniqueColumns(details);\n      const columnIndexes = map$1(columns, detail => detail.column);\n      const newGrid = replaceColumns(initialGrid, columnIndexes, true, comparator, genWrappers.replaceOrInit);\n      return bundle(newGrid, details[0].row, details[0].column);\n    };\n    const opMakeCellsHeader = (initialGrid, details, comparator, genWrappers) => {\n      const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit);\n      return bundle(newGrid, details[0].row, details[0].column);\n    };\n    const opUnmakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => {\n      const columns = uniqueColumns(details);\n      const columnIndexes = map$1(columns, detail => detail.column);\n      const newGrid = replaceColumns(initialGrid, columnIndexes, false, comparator, genWrappers.replaceOrInit);\n      return bundle(newGrid, details[0].row, details[0].column);\n    };\n    const opUnmakeCellsHeader = (initialGrid, details, comparator, genWrappers) => {\n      const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit);\n      return bundle(newGrid, details[0].row, details[0].column);\n    };\n    const makeRowsSection = (section, applyScope) => (initialGrid, details, comparator, genWrappers, tableSection) => {\n      const rows = uniqueRows(details);\n      const rowIndexes = map$1(rows, detail => detail.row);\n      const newGrid = replaceRows(initialGrid, rowIndexes, section, applyScope, comparator, genWrappers.replaceOrInit, tableSection);\n      return bundle(newGrid, details[0].row, details[0].column);\n    };\n    const opMakeRowsHeader = makeRowsSection('thead', true);\n    const opMakeRowsBody = makeRowsSection('tbody', false);\n    const opMakeRowsFooter = makeRowsSection('tfoot', false);\n    const opEraseColumns = (grid, extractDetail, _comparator, _genWrappers) => {\n      const columns = uniqueColumns(extractDetail.details);\n      const newGrid = deleteColumnsAt(grid, map$1(columns, column => column.column));\n      const maxColIndex = newGrid.length > 0 ? newGrid[0].cells.length - 1 : 0;\n      return bundle(newGrid, columns[0].row, Math.min(columns[0].column, maxColIndex));\n    };\n    const opEraseRows = (grid, details, _comparator, _genWrappers) => {\n      const rows = uniqueRows(details);\n      const newGrid = deleteRowsAt(grid, rows[0].row, rows[rows.length - 1].row);\n      const maxRowIndex = newGrid.length > 0 ? newGrid.length - 1 : 0;\n      return bundle(newGrid, Math.min(details[0].row, maxRowIndex), details[0].column);\n    };\n    const opMergeCells = (grid, mergable, comparator, genWrappers) => {\n      const cells = mergable.cells;\n      merge(cells);\n      const newGrid = merge$2(grid, mergable.bounds, comparator, genWrappers.merge(cells));\n      return outcome(newGrid, Optional.from(cells[0]));\n    };\n    const opUnmergeCells = (grid, unmergable, comparator, genWrappers) => {\n      const unmerge$1 = (b, cell) => unmerge(b, cell, comparator, genWrappers.unmerge(cell));\n      const newGrid = foldr(unmergable, unmerge$1, grid);\n      return outcome(newGrid, Optional.from(unmergable[0]));\n    };\n    const opPasteCells = (grid, pasteDetails, comparator, _genWrappers) => {\n      const gridify = (table, generators) => {\n        const wh = Warehouse.fromTable(table);\n        return toGrid(wh, generators, true);\n      };\n      const gridB = gridify(pasteDetails.clipboard, pasteDetails.generators);\n      const startAddress = address(pasteDetails.row, pasteDetails.column);\n      const mergedGrid = merge$1(startAddress, grid, gridB, pasteDetails.generators, comparator);\n      return mergedGrid.fold(() => outcome(grid, Optional.some(pasteDetails.element)), newGrid => {\n        return bundle(newGrid, pasteDetails.row, pasteDetails.column);\n      });\n    };\n    const gridifyRows = (rows, generators, context) => {\n      const pasteDetails = fromPastedRows(rows, context.section);\n      const wh = Warehouse.generate(pasteDetails);\n      return toGrid(wh, generators, true);\n    };\n    const opPasteColsBefore = (grid, pasteDetails, comparator, _genWrappers) => {\n      const rows = extractGridDetails(grid).rows;\n      const index = pasteDetails.cells[0].column;\n      const context = rows[pasteDetails.cells[0].row];\n      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);\n      const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator);\n      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);\n    };\n    const opPasteColsAfter = (grid, pasteDetails, comparator, _genWrappers) => {\n      const rows = extractGridDetails(grid).rows;\n      const index = pasteDetails.cells[pasteDetails.cells.length - 1].column + pasteDetails.cells[pasteDetails.cells.length - 1].colspan;\n      const context = rows[pasteDetails.cells[0].row];\n      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);\n      const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator);\n      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);\n    };\n    const opPasteRowsBefore = (grid, pasteDetails, comparator, _genWrappers) => {\n      const rows = extractGridDetails(grid).rows;\n      const index = pasteDetails.cells[0].row;\n      const context = rows[index];\n      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);\n      const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator);\n      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);\n    };\n    const opPasteRowsAfter = (grid, pasteDetails, comparator, _genWrappers) => {\n      const rows = extractGridDetails(grid).rows;\n      const index = pasteDetails.cells[pasteDetails.cells.length - 1].row + pasteDetails.cells[pasteDetails.cells.length - 1].rowspan;\n      const context = rows[pasteDetails.cells[0].row];\n      const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);\n      const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator);\n      return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);\n    };\n    const opGetColumnsType = (table, target) => {\n      const house = Warehouse.fromTable(table);\n      const details = onCells(house, target);\n      return details.bind(selectedCells => {\n        const lastSelectedCell = selectedCells[selectedCells.length - 1];\n        const minColRange = selectedCells[0].column;\n        const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan;\n        const selectedColumnCells = flatten(map$1(house.all, row => filter$2(row.cells, cell => cell.column >= minColRange && cell.column < maxColRange)));\n        return findCommonCellType(selectedColumnCells);\n      }).getOr('');\n    };\n    const opGetCellsType = (table, target) => {\n      const house = Warehouse.fromTable(table);\n      const details = onCells(house, target);\n      return details.bind(findCommonCellType).getOr('');\n    };\n    const opGetRowsType = (table, target) => {\n      const house = Warehouse.fromTable(table);\n      const details = onCells(house, target);\n      return details.bind(selectedCells => {\n        const lastSelectedCell = selectedCells[selectedCells.length - 1];\n        const minRowRange = selectedCells[0].row;\n        const maxRowRange = lastSelectedCell.row + lastSelectedCell.rowspan;\n        const selectedRows = house.all.slice(minRowRange, maxRowRange);\n        return findCommonRowType(selectedRows);\n      }).getOr('');\n    };\n    const resize = (table, list, details, behaviours) => adjustWidthTo(table, list, details, behaviours.sizing);\n    const adjustAndRedistributeWidths = (table, list, details, behaviours) => adjustAndRedistributeWidths$1(table, list, details, behaviours.sizing, behaviours.resize);\n    const firstColumnIsLocked = (_warehouse, details) => exists(details, detail => detail.column === 0 && detail.isLocked);\n    const lastColumnIsLocked = (warehouse, details) => exists(details, detail => detail.column + detail.colspan >= warehouse.grid.columns && detail.isLocked);\n    const getColumnsWidth = (warehouse, details) => {\n      const columns$1 = columns(warehouse);\n      const uniqueCols = uniqueColumns(details);\n      return foldl(uniqueCols, (acc, detail) => {\n        const column = columns$1[detail.column];\n        const colWidth = column.map(getOuter$2).getOr(0);\n        return acc + colWidth;\n      }, 0);\n    };\n    const insertColumnsExtractor = before => (warehouse, target) => onCells(warehouse, target).filter(details => {\n      const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked;\n      return !checkLocked(warehouse, details);\n    }).map(details => ({\n      details,\n      pixelDelta: getColumnsWidth(warehouse, details)\n    }));\n    const eraseColumnsExtractor = (warehouse, target) => onUnlockedCells(warehouse, target).map(details => ({\n      details,\n      pixelDelta: -getColumnsWidth(warehouse, details)\n    }));\n    const pasteColumnsExtractor = before => (warehouse, target) => onPasteByEditor(warehouse, target).filter(details => {\n      const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked;\n      return !checkLocked(warehouse, details.cells);\n    });\n    const headerCellGenerator = Generators.transform('th');\n    const bodyCellGenerator = Generators.transform('td');\n    const insertRowsBefore = run(opInsertRowsBefore, onCells, noop, noop, Generators.modification);\n    const insertRowsAfter = run(opInsertRowsAfter, onCells, noop, noop, Generators.modification);\n    const insertColumnsBefore = run(opInsertColumnsBefore, insertColumnsExtractor(true), adjustAndRedistributeWidths, noop, Generators.modification);\n    const insertColumnsAfter = run(opInsertColumnsAfter, insertColumnsExtractor(false), adjustAndRedistributeWidths, noop, Generators.modification);\n    const eraseColumns = run(opEraseColumns, eraseColumnsExtractor, adjustAndRedistributeWidths, prune, Generators.modification);\n    const eraseRows = run(opEraseRows, onCells, noop, prune, Generators.modification);\n    const makeColumnsHeader = run(opMakeColumnsHeader, onUnlockedCells, noop, noop, headerCellGenerator);\n    const unmakeColumnsHeader = run(opUnmakeColumnsHeader, onUnlockedCells, noop, noop, bodyCellGenerator);\n    const makeRowsHeader = run(opMakeRowsHeader, onUnlockedCells, noop, noop, headerCellGenerator);\n    const makeRowsBody = run(opMakeRowsBody, onUnlockedCells, noop, noop, bodyCellGenerator);\n    const makeRowsFooter = run(opMakeRowsFooter, onUnlockedCells, noop, noop, bodyCellGenerator);\n    const makeCellsHeader = run(opMakeCellsHeader, onUnlockedCells, noop, noop, headerCellGenerator);\n    const unmakeCellsHeader = run(opUnmakeCellsHeader, onUnlockedCells, noop, noop, bodyCellGenerator);\n    const mergeCells = run(opMergeCells, onUnlockedMergable, resize, noop, Generators.merging);\n    const unmergeCells = run(opUnmergeCells, onUnlockedUnmergable, resize, noop, Generators.merging);\n    const pasteCells = run(opPasteCells, onPaste, resize, noop, Generators.modification);\n    const pasteColsBefore = run(opPasteColsBefore, pasteColumnsExtractor(true), noop, noop, Generators.modification);\n    const pasteColsAfter = run(opPasteColsAfter, pasteColumnsExtractor(false), noop, noop, Generators.modification);\n    const pasteRowsBefore = run(opPasteRowsBefore, onPasteByEditor, noop, noop, Generators.modification);\n    const pasteRowsAfter = run(opPasteRowsAfter, onPasteByEditor, noop, noop, Generators.modification);\n    const getColumnsType = opGetColumnsType;\n    const getCellsType = opGetCellsType;\n    const getRowsType = opGetRowsType;\n\n    const fireNewRow = (editor, row) => editor.dispatch('NewRow', { node: row });\n    const fireNewCell = (editor, cell) => editor.dispatch('NewCell', { node: cell });\n    const fireTableModified = (editor, table, data) => {\n      editor.dispatch('TableModified', {\n        ...data,\n        table\n      });\n    };\n    const fireTableSelectionChange = (editor, cells, start, finish, otherCells) => {\n      editor.dispatch('TableSelectionChange', {\n        cells,\n        start,\n        finish,\n        otherCells\n      });\n    };\n    const fireTableSelectionClear = editor => {\n      editor.dispatch('TableSelectionClear');\n    };\n    const fireObjectResizeStart = (editor, target, width, height, origin) => {\n      editor.dispatch('ObjectResizeStart', {\n        target,\n        width,\n        height,\n        origin\n      });\n    };\n    const fireObjectResized = (editor, target, width, height, origin) => {\n      editor.dispatch('ObjectResized', {\n        target,\n        width,\n        height,\n        origin\n      });\n    };\n    const styleModified = {\n      structure: false,\n      style: true\n    };\n    const structureModified = {\n      structure: true,\n      style: false\n    };\n    const styleAndStructureModified = {\n      structure: true,\n      style: true\n    };\n\n    const option = name => editor => editor.options.get(name);\n    const defaultWidth = '100%';\n    const getPixelForcedWidth = editor => {\n      var _a;\n      const dom = editor.dom;\n      const parentBlock = (_a = dom.getParent(editor.selection.getStart(), dom.isBlock)) !== null && _a !== void 0 ? _a : editor.getBody();\n      return getInner(SugarElement.fromDom(parentBlock)) + 'px';\n    };\n    const determineDefaultTableStyles = (editor, defaultStyles) => {\n      if (isTableResponsiveForced(editor) || !shouldStyleWithCss(editor)) {\n        return defaultStyles;\n      } else if (isTablePixelsForced(editor)) {\n        return {\n          ...defaultStyles,\n          width: getPixelForcedWidth(editor)\n        };\n      } else {\n        return {\n          ...defaultStyles,\n          width: defaultWidth\n        };\n      }\n    };\n    const determineDefaultTableAttributes = (editor, defaultAttributes) => {\n      if (isTableResponsiveForced(editor) || shouldStyleWithCss(editor)) {\n        return defaultAttributes;\n      } else if (isTablePixelsForced(editor)) {\n        return {\n          ...defaultAttributes,\n          width: getPixelForcedWidth(editor)\n        };\n      } else {\n        return {\n          ...defaultAttributes,\n          width: defaultWidth\n        };\n      }\n    };\n    const register = editor => {\n      const registerOption = editor.options.register;\n      registerOption('table_clone_elements', { processor: 'string[]' });\n      registerOption('table_use_colgroups', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('table_header_type', {\n        processor: value => {\n          const valid = contains$2([\n            'section',\n            'cells',\n            'sectionCells',\n            'auto'\n          ], value);\n          return valid ? {\n            value,\n            valid\n          } : {\n            valid: false,\n            message: 'Must be one of: section, cells, sectionCells or auto.'\n          };\n        },\n        default: 'section'\n      });\n      registerOption('table_sizing_mode', {\n        processor: 'string',\n        default: 'auto'\n      });\n      registerOption('table_default_attributes', {\n        processor: 'object',\n        default: { border: '1' }\n      });\n      registerOption('table_default_styles', {\n        processor: 'object',\n        default: { 'border-collapse': 'collapse' }\n      });\n      registerOption('table_column_resizing', {\n        processor: value => {\n          const valid = contains$2([\n            'preservetable',\n            'resizetable'\n          ], value);\n          return valid ? {\n            value,\n            valid\n          } : {\n            valid: false,\n            message: 'Must be preservetable, or resizetable.'\n          };\n        },\n        default: 'preservetable'\n      });\n      registerOption('table_resize_bars', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('table_style_by_css', {\n        processor: 'boolean',\n        default: true\n      });\n    };\n    const getTableCloneElements = editor => {\n      return Optional.from(editor.options.get('table_clone_elements'));\n    };\n    const hasTableObjectResizing = editor => {\n      const objectResizing = editor.options.get('object_resizing');\n      return contains$2(objectResizing.split(','), 'table');\n    };\n    const getTableHeaderType = option('table_header_type');\n    const getTableColumnResizingBehaviour = option('table_column_resizing');\n    const isPreserveTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === 'preservetable';\n    const isResizeTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === 'resizetable';\n    const getTableSizingMode = option('table_sizing_mode');\n    const isTablePercentagesForced = editor => getTableSizingMode(editor) === 'relative';\n    const isTablePixelsForced = editor => getTableSizingMode(editor) === 'fixed';\n    const isTableResponsiveForced = editor => getTableSizingMode(editor) === 'responsive';\n    const hasTableResizeBars = option('table_resize_bars');\n    const shouldStyleWithCss = option('table_style_by_css');\n    const getTableDefaultAttributes = editor => {\n      const options = editor.options;\n      const defaultAttributes = options.get('table_default_attributes');\n      return options.isSet('table_default_attributes') ? defaultAttributes : determineDefaultTableAttributes(editor, defaultAttributes);\n    };\n    const getTableDefaultStyles = editor => {\n      const options = editor.options;\n      const defaultStyles = options.get('table_default_styles');\n      return options.isSet('table_default_styles') ? defaultStyles : determineDefaultTableStyles(editor, defaultStyles);\n    };\n    const tableUseColumnGroup = option('table_use_colgroups');\n\n    const get$5 = (editor, table) => {\n      if (isTablePercentagesForced(editor)) {\n        return TableSize.percentageSize(table);\n      } else if (isTablePixelsForced(editor)) {\n        return TableSize.pixelSize(table);\n      } else {\n        return TableSize.getTableSize(table);\n      }\n    };\n\n    const TableActions = (editor, resizeHandler, cellSelectionHandler) => {\n      const isTableBody = editor => name(getBody(editor)) === 'table';\n      const lastRowGuard = table => !isTableBody(editor) || getGridSize(table).rows > 1;\n      const lastColumnGuard = table => !isTableBody(editor) || getGridSize(table).columns > 1;\n      const cloneFormats = getTableCloneElements(editor);\n      const colMutationOp = isResizeTableColumnResizing(editor) ? noop : halve;\n      const getTableSectionType = table => {\n        switch (getTableHeaderType(editor)) {\n        case 'section':\n          return TableSection.section();\n        case 'sectionCells':\n          return TableSection.sectionCells();\n        case 'cells':\n          return TableSection.cells();\n        default:\n          return TableSection.getTableSectionType(table, 'section');\n        }\n      };\n      const setSelectionFromAction = (table, result) => result.cursor.fold(() => {\n        const cells = cells$1(table);\n        return head(cells).filter(inBody).map(firstCell => {\n          cellSelectionHandler.clearSelectedCells(table.dom);\n          const rng = editor.dom.createRng();\n          rng.selectNode(firstCell.dom);\n          editor.selection.setRng(rng);\n          set$2(firstCell, 'data-mce-selected', '1');\n          return rng;\n        });\n      }, cell => {\n        const des = freefallRtl(cell);\n        const rng = editor.dom.createRng();\n        rng.setStart(des.element.dom, des.offset);\n        rng.setEnd(des.element.dom, des.offset);\n        editor.selection.setRng(rng);\n        cellSelectionHandler.clearSelectedCells(table.dom);\n        return Optional.some(rng);\n      });\n      const execute = (operation, guard, mutate, effect) => (table, target, noEvents = false) => {\n        removeDataStyle(table);\n        const doc = SugarElement.fromDom(editor.getDoc());\n        const generators = cellOperations(mutate, doc, cloneFormats);\n        const behaviours = {\n          sizing: get$5(editor, table),\n          resize: isResizeTableColumnResizing(editor) ? resizeTable() : preserveTable(),\n          section: getTableSectionType(table)\n        };\n        return guard(table) ? operation(table, target, generators, behaviours).bind(result => {\n          resizeHandler.refresh(table.dom);\n          each$2(result.newRows, row => {\n            fireNewRow(editor, row.dom);\n          });\n          each$2(result.newCells, cell => {\n            fireNewCell(editor, cell.dom);\n          });\n          const range = setSelectionFromAction(table, result);\n          if (inBody(table)) {\n            removeDataStyle(table);\n            if (!noEvents) {\n              fireTableModified(editor, table.dom, effect);\n            }\n          }\n          return range.map(rng => ({\n            rng,\n            effect\n          }));\n        }) : Optional.none();\n      };\n      const deleteRow = execute(eraseRows, lastRowGuard, noop, structureModified);\n      const deleteColumn = execute(eraseColumns, lastColumnGuard, noop, structureModified);\n      const insertRowsBefore$1 = execute(insertRowsBefore, always, noop, structureModified);\n      const insertRowsAfter$1 = execute(insertRowsAfter, always, noop, structureModified);\n      const insertColumnsBefore$1 = execute(insertColumnsBefore, always, colMutationOp, structureModified);\n      const insertColumnsAfter$1 = execute(insertColumnsAfter, always, colMutationOp, structureModified);\n      const mergeCells$1 = execute(mergeCells, always, noop, structureModified);\n      const unmergeCells$1 = execute(unmergeCells, always, noop, structureModified);\n      const pasteColsBefore$1 = execute(pasteColsBefore, always, noop, structureModified);\n      const pasteColsAfter$1 = execute(pasteColsAfter, always, noop, structureModified);\n      const pasteRowsBefore$1 = execute(pasteRowsBefore, always, noop, structureModified);\n      const pasteRowsAfter$1 = execute(pasteRowsAfter, always, noop, structureModified);\n      const pasteCells$1 = execute(pasteCells, always, noop, styleAndStructureModified);\n      const makeCellsHeader$1 = execute(makeCellsHeader, always, noop, structureModified);\n      const unmakeCellsHeader$1 = execute(unmakeCellsHeader, always, noop, structureModified);\n      const makeColumnsHeader$1 = execute(makeColumnsHeader, always, noop, structureModified);\n      const unmakeColumnsHeader$1 = execute(unmakeColumnsHeader, always, noop, structureModified);\n      const makeRowsHeader$1 = execute(makeRowsHeader, always, noop, structureModified);\n      const makeRowsBody$1 = execute(makeRowsBody, always, noop, structureModified);\n      const makeRowsFooter$1 = execute(makeRowsFooter, always, noop, structureModified);\n      const getTableCellType = getCellsType;\n      const getTableColType = getColumnsType;\n      const getTableRowType = getRowsType;\n      return {\n        deleteRow,\n        deleteColumn,\n        insertRowsBefore: insertRowsBefore$1,\n        insertRowsAfter: insertRowsAfter$1,\n        insertColumnsBefore: insertColumnsBefore$1,\n        insertColumnsAfter: insertColumnsAfter$1,\n        mergeCells: mergeCells$1,\n        unmergeCells: unmergeCells$1,\n        pasteColsBefore: pasteColsBefore$1,\n        pasteColsAfter: pasteColsAfter$1,\n        pasteRowsBefore: pasteRowsBefore$1,\n        pasteRowsAfter: pasteRowsAfter$1,\n        pasteCells: pasteCells$1,\n        makeCellsHeader: makeCellsHeader$1,\n        unmakeCellsHeader: unmakeCellsHeader$1,\n        makeColumnsHeader: makeColumnsHeader$1,\n        unmakeColumnsHeader: unmakeColumnsHeader$1,\n        makeRowsHeader: makeRowsHeader$1,\n        makeRowsBody: makeRowsBody$1,\n        makeRowsFooter: makeRowsFooter$1,\n        getTableRowType,\n        getTableCellType,\n        getTableColType\n      };\n    };\n\n    const constrainSpan = (element, property, value) => {\n      const currentColspan = getAttrValue(element, property, 1);\n      if (value === 1 || currentColspan <= 1) {\n        remove$7(element, property);\n      } else {\n        set$2(element, property, Math.min(value, currentColspan));\n      }\n    };\n    const isColInRange = (minColRange, maxColRange) => cell => {\n      const endCol = cell.column + cell.colspan - 1;\n      const startCol = cell.column;\n      return endCol >= minColRange && startCol < maxColRange;\n    };\n    const generateColGroup = (house, minColRange, maxColRange) => {\n      if (Warehouse.hasColumns(house)) {\n        const colsToCopy = filter$2(Warehouse.justColumns(house), isColInRange(minColRange, maxColRange));\n        const copiedCols = map$1(colsToCopy, c => {\n          const clonedCol = deep(c.element);\n          constrainSpan(clonedCol, 'span', maxColRange - minColRange);\n          return clonedCol;\n        });\n        const fakeColgroup = SugarElement.fromTag('colgroup');\n        append(fakeColgroup, copiedCols);\n        return [fakeColgroup];\n      } else {\n        return [];\n      }\n    };\n    const generateRows = (house, minColRange, maxColRange) => map$1(house.all, row => {\n      const cellsToCopy = filter$2(row.cells, isColInRange(minColRange, maxColRange));\n      const copiedCells = map$1(cellsToCopy, cell => {\n        const clonedCell = deep(cell.element);\n        constrainSpan(clonedCell, 'colspan', maxColRange - minColRange);\n        return clonedCell;\n      });\n      const fakeTR = SugarElement.fromTag('tr');\n      append(fakeTR, copiedCells);\n      return fakeTR;\n    });\n    const copyCols = (table, target) => {\n      const house = Warehouse.fromTable(table);\n      const details = onUnlockedCells(house, target);\n      return details.map(selectedCells => {\n        const lastSelectedCell = selectedCells[selectedCells.length - 1];\n        const minColRange = selectedCells[0].column;\n        const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan;\n        const fakeColGroups = generateColGroup(house, minColRange, maxColRange);\n        const fakeRows = generateRows(house, minColRange, maxColRange);\n        return [\n          ...fakeColGroups,\n          ...fakeRows\n        ];\n      });\n    };\n\n    const copyRows = (table, target, generators) => {\n      const warehouse = Warehouse.fromTable(table);\n      const details = onCells(warehouse, target);\n      return details.bind(selectedCells => {\n        const grid = toGrid(warehouse, generators, false);\n        const rows = extractGridDetails(grid).rows;\n        const slicedGrid = rows.slice(selectedCells[0].row, selectedCells[selectedCells.length - 1].row + selectedCells[selectedCells.length - 1].rowspan);\n        const filteredGrid = bind$2(slicedGrid, row => {\n          const newCells = filter$2(row.cells, cell => !cell.isLocked);\n          return newCells.length > 0 ? [{\n              ...row,\n              cells: newCells\n            }] : [];\n        });\n        const slicedDetails = toDetailList(filteredGrid);\n        return someIf(slicedDetails.length > 0, slicedDetails);\n      }).map(slicedDetails => copy(slicedDetails));\n    };\n\n    const adt$5 = Adt.generate([\n      { invalid: ['raw'] },\n      { pixels: ['value'] },\n      { percent: ['value'] }\n    ]);\n    const validateFor = (suffix, type, value) => {\n      const rawAmount = value.substring(0, value.length - suffix.length);\n      const amount = parseFloat(rawAmount);\n      return rawAmount === amount.toString() ? type(amount) : adt$5.invalid(value);\n    };\n    const from = value => {\n      if (endsWith(value, '%')) {\n        return validateFor('%', adt$5.percent, value);\n      }\n      if (endsWith(value, 'px')) {\n        return validateFor('px', adt$5.pixels, value);\n      }\n      return adt$5.invalid(value);\n    };\n    const Size = {\n      ...adt$5,\n      from\n    };\n\n    const redistributeToPercent = (widths, totalWidth) => {\n      return map$1(widths, w => {\n        const colType = Size.from(w);\n        return colType.fold(() => {\n          return w;\n        }, px => {\n          const ratio = px / totalWidth * 100;\n          return ratio + '%';\n        }, pc => {\n          return pc + '%';\n        });\n      });\n    };\n    const redistributeToPx = (widths, totalWidth, newTotalWidth) => {\n      const scale = newTotalWidth / totalWidth;\n      return map$1(widths, w => {\n        const colType = Size.from(w);\n        return colType.fold(() => {\n          return w;\n        }, px => {\n          return px * scale + 'px';\n        }, pc => {\n          return pc / 100 * newTotalWidth + 'px';\n        });\n      });\n    };\n    const redistributeEmpty = (newWidthType, columns) => {\n      const f = newWidthType.fold(() => constant(''), pixels => {\n        const num = pixels / columns;\n        return constant(num + 'px');\n      }, () => {\n        const num = 100 / columns;\n        return constant(num + '%');\n      });\n      return range$1(columns, f);\n    };\n    const redistributeValues = (newWidthType, widths, totalWidth) => {\n      return newWidthType.fold(() => {\n        return widths;\n      }, px => {\n        return redistributeToPx(widths, totalWidth, px);\n      }, _pc => {\n        return redistributeToPercent(widths, totalWidth);\n      });\n    };\n    const redistribute$1 = (widths, totalWidth, newWidth) => {\n      const newType = Size.from(newWidth);\n      const floats = forall(widths, s => {\n        return s === '0px';\n      }) ? redistributeEmpty(newType, widths.length) : redistributeValues(newType, widths, totalWidth);\n      return normalize(floats);\n    };\n    const sum = (values, fallback) => {\n      if (values.length === 0) {\n        return fallback;\n      }\n      return foldr(values, (rest, v) => {\n        return Size.from(v).fold(constant(0), identity, identity) + rest;\n      }, 0);\n    };\n    const roundDown = (num, unit) => {\n      const floored = Math.floor(num);\n      return {\n        value: floored + unit,\n        remainder: num - floored\n      };\n    };\n    const add$3 = (value, amount) => {\n      return Size.from(value).fold(constant(value), px => {\n        return px + amount + 'px';\n      }, pc => {\n        return pc + amount + '%';\n      });\n    };\n    const normalize = values => {\n      if (values.length === 0) {\n        return values;\n      }\n      const scan = foldr(values, (rest, value) => {\n        const info = Size.from(value).fold(() => ({\n          value,\n          remainder: 0\n        }), num => roundDown(num, 'px'), num => ({\n          value: num + '%',\n          remainder: 0\n        }));\n        return {\n          output: [info.value].concat(rest.output),\n          remainder: rest.remainder + info.remainder\n        };\n      }, {\n        output: [],\n        remainder: 0\n      });\n      const r = scan.output;\n      return r.slice(0, r.length - 1).concat([add$3(r[r.length - 1], Math.round(scan.remainder))]);\n    };\n    const validate = Size.from;\n\n    const redistributeToW = (newWidths, cells, unit) => {\n      each$2(cells, cell => {\n        const widths = newWidths.slice(cell.column, cell.colspan + cell.column);\n        const w = sum(widths, minWidth());\n        set$1(cell.element, 'width', w + unit);\n      });\n    };\n    const redistributeToColumns = (newWidths, columns, unit) => {\n      each$2(columns, (column, index) => {\n        const width = sum([newWidths[index]], minWidth());\n        set$1(column.element, 'width', width + unit);\n      });\n    };\n    const redistributeToH = (newHeights, rows, cells, unit) => {\n      each$2(cells, cell => {\n        const heights = newHeights.slice(cell.row, cell.rowspan + cell.row);\n        const h = sum(heights, minHeight());\n        set$1(cell.element, 'height', h + unit);\n      });\n      each$2(rows, (row, i) => {\n        set$1(row.element, 'height', newHeights[i]);\n      });\n    };\n    const getUnit = newSize => {\n      return validate(newSize).fold(constant('px'), constant('px'), constant('%'));\n    };\n    const redistribute = (table, optWidth, optHeight) => {\n      const warehouse = Warehouse.fromTable(table);\n      const rows = warehouse.all;\n      const cells = Warehouse.justCells(warehouse);\n      const columns = Warehouse.justColumns(warehouse);\n      optWidth.each(newWidth => {\n        const widthUnit = getUnit(newWidth);\n        const totalWidth = get$9(table);\n        const oldWidths = getRawWidths(warehouse, table);\n        const nuWidths = redistribute$1(oldWidths, totalWidth, newWidth);\n        if (Warehouse.hasColumns(warehouse)) {\n          redistributeToColumns(nuWidths, columns, widthUnit);\n        } else {\n          redistributeToW(nuWidths, cells, widthUnit);\n        }\n        set$1(table, 'width', newWidth);\n      });\n      optHeight.each(newHeight => {\n        const hUnit = getUnit(newHeight);\n        const totalHeight = get$8(table);\n        const oldHeights = getRawHeights(warehouse, table, height);\n        const nuHeights = redistribute$1(oldHeights, totalHeight, newHeight);\n        redistributeToH(nuHeights, rows, cells, hUnit);\n        set$1(table, 'height', newHeight);\n      });\n    };\n    const isPercentSizing = isPercentSizing$1;\n    const isPixelSizing = isPixelSizing$1;\n    const isNoneSizing = isNoneSizing$1;\n\n    const cleanupLegacyAttributes = element => {\n      remove$7(element, 'width');\n    };\n    const convertToPercentSize = table => {\n      const newWidth = getPercentTableWidth(table);\n      redistribute(table, Optional.some(newWidth), Optional.none());\n      cleanupLegacyAttributes(table);\n    };\n    const convertToPixelSize = table => {\n      const newWidth = getPixelTableWidth(table);\n      redistribute(table, Optional.some(newWidth), Optional.none());\n      cleanupLegacyAttributes(table);\n    };\n    const convertToNoneSize = table => {\n      remove$5(table, 'width');\n      const columns = columns$1(table);\n      const rowElements = columns.length > 0 ? columns : cells$1(table);\n      each$2(rowElements, cell => {\n        remove$5(cell, 'width');\n        cleanupLegacyAttributes(cell);\n      });\n      cleanupLegacyAttributes(table);\n    };\n\n    const DefaultRenderOptions = {\n      styles: {\n        'border-collapse': 'collapse',\n        'width': '100%'\n      },\n      attributes: { border: '1' },\n      colGroups: false\n    };\n    const tableHeaderCell = () => SugarElement.fromTag('th');\n    const tableCell = () => SugarElement.fromTag('td');\n    const tableColumn = () => SugarElement.fromTag('col');\n    const createRow = (columns, rowHeaders, columnHeaders, rowIndex) => {\n      const tr = SugarElement.fromTag('tr');\n      for (let j = 0; j < columns; j++) {\n        const td = rowIndex < rowHeaders || j < columnHeaders ? tableHeaderCell() : tableCell();\n        if (j < columnHeaders) {\n          set$2(td, 'scope', 'row');\n        }\n        if (rowIndex < rowHeaders) {\n          set$2(td, 'scope', 'col');\n        }\n        append$1(td, SugarElement.fromTag('br'));\n        append$1(tr, td);\n      }\n      return tr;\n    };\n    const createGroupRow = columns => {\n      const columnGroup = SugarElement.fromTag('colgroup');\n      range$1(columns, () => append$1(columnGroup, tableColumn()));\n      return columnGroup;\n    };\n    const createRows = (rows, columns, rowHeaders, columnHeaders) => range$1(rows, r => createRow(columns, rowHeaders, columnHeaders, r));\n    const render = (rows, columns, rowHeaders, columnHeaders, headerType, renderOpts = DefaultRenderOptions) => {\n      const table = SugarElement.fromTag('table');\n      const rowHeadersGoInThead = headerType !== 'cells';\n      setAll(table, renderOpts.styles);\n      setAll$1(table, renderOpts.attributes);\n      if (renderOpts.colGroups) {\n        append$1(table, createGroupRow(columns));\n      }\n      const actualRowHeaders = Math.min(rows, rowHeaders);\n      if (rowHeadersGoInThead && rowHeaders > 0) {\n        const thead = SugarElement.fromTag('thead');\n        append$1(table, thead);\n        const theadRowHeaders = headerType === 'sectionCells' ? actualRowHeaders : 0;\n        const theadRows = createRows(rowHeaders, columns, theadRowHeaders, columnHeaders);\n        append(thead, theadRows);\n      }\n      const tbody = SugarElement.fromTag('tbody');\n      append$1(table, tbody);\n      const numRows = rowHeadersGoInThead ? rows - actualRowHeaders : rows;\n      const numRowHeaders = rowHeadersGoInThead ? 0 : rowHeaders;\n      const tbodyRows = createRows(numRows, columns, numRowHeaders, columnHeaders);\n      append(tbody, tbodyRows);\n      return table;\n    };\n\n    const get$4 = element => element.dom.innerHTML;\n    const getOuter = element => {\n      const container = SugarElement.fromTag('div');\n      const clone = SugarElement.fromDom(element.dom.cloneNode(true));\n      append$1(container, clone);\n      return get$4(container);\n    };\n\n    const placeCaretInCell = (editor, cell) => {\n      editor.selection.select(cell.dom, true);\n      editor.selection.collapse(true);\n    };\n    const selectFirstCellInTable = (editor, tableElm) => {\n      descendant(tableElm, 'td,th').each(curry(placeCaretInCell, editor));\n    };\n    const fireEvents = (editor, table) => {\n      each$2(descendants(table, 'tr'), row => {\n        fireNewRow(editor, row.dom);\n        each$2(descendants(row, 'th,td'), cell => {\n          fireNewCell(editor, cell.dom);\n        });\n      });\n    };\n    const isPercentage = width => isString(width) && width.indexOf('%') !== -1;\n    const insert = (editor, columns, rows, colHeaders, rowHeaders) => {\n      const defaultStyles = getTableDefaultStyles(editor);\n      const options = {\n        styles: defaultStyles,\n        attributes: getTableDefaultAttributes(editor),\n        colGroups: tableUseColumnGroup(editor)\n      };\n      editor.undoManager.ignore(() => {\n        const table = render(rows, columns, rowHeaders, colHeaders, getTableHeaderType(editor), options);\n        set$2(table, 'data-mce-id', '__mce');\n        const html = getOuter(table);\n        editor.insertContent(html);\n        editor.addVisual();\n      });\n      return descendant(getBody(editor), 'table[data-mce-id=\"__mce\"]').map(table => {\n        if (isTablePixelsForced(editor)) {\n          convertToPixelSize(table);\n        } else if (isTableResponsiveForced(editor)) {\n          convertToNoneSize(table);\n        } else if (isTablePercentagesForced(editor) || isPercentage(defaultStyles.width)) {\n          convertToPercentSize(table);\n        }\n        removeDataStyle(table);\n        remove$7(table, 'data-mce-id');\n        fireEvents(editor, table);\n        selectFirstCellInTable(editor, table);\n        return table.dom;\n      }).getOrNull();\n    };\n    const insertTable = (editor, rows, columns, options = {}) => {\n      const checkInput = val => isNumber(val) && val > 0;\n      if (checkInput(rows) && checkInput(columns)) {\n        const headerRows = options.headerRows || 0;\n        const headerColumns = options.headerColumns || 0;\n        return insert(editor, columns, rows, headerColumns, headerRows);\n      } else {\n        console.error('Invalid values for mceInsertTable - rows and columns values are required to insert a table.');\n        return null;\n      }\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.FakeClipboard');\n\n    const tableTypeBase = 'x-tinymce/dom-table-';\n    const tableTypeRow = tableTypeBase + 'rows';\n    const tableTypeColumn = tableTypeBase + 'columns';\n    const setData = items => {\n      const fakeClipboardItem = global.FakeClipboardItem(items);\n      global.write([fakeClipboardItem]);\n    };\n    const getData = type => {\n      var _a;\n      const items = (_a = global.read()) !== null && _a !== void 0 ? _a : [];\n      return findMap(items, item => Optional.from(item.getType(type)));\n    };\n    const clearData = type => {\n      if (getData(type).isSome()) {\n        global.clear();\n      }\n    };\n    const setRows = rowsOpt => {\n      rowsOpt.fold(clearRows, rows => setData({ [tableTypeRow]: rows }));\n    };\n    const getRows = () => getData(tableTypeRow);\n    const clearRows = () => clearData(tableTypeRow);\n    const setColumns = columnsOpt => {\n      columnsOpt.fold(clearColumns, columns => setData({ [tableTypeColumn]: columns }));\n    };\n    const getColumns = () => getData(tableTypeColumn);\n    const clearColumns = () => clearData(tableTypeColumn);\n\n    const getSelectionStartCellOrCaption = editor => getSelectionCellOrCaption(getSelectionStart(editor), getIsRoot(editor));\n    const getSelectionStartCell = editor => getSelectionCell(getSelectionStart(editor), getIsRoot(editor));\n    const registerCommands = (editor, actions) => {\n      const isRoot = getIsRoot(editor);\n      const eraseTable = () => getSelectionStartCellOrCaption(editor).each(cellOrCaption => {\n        table(cellOrCaption, isRoot).filter(not(isRoot)).each(table => {\n          const cursor = SugarElement.fromText('');\n          after$5(table, cursor);\n          remove$6(table);\n          if (editor.dom.isEmpty(editor.getBody())) {\n            editor.setContent('');\n            editor.selection.setCursorLocation();\n          } else {\n            const rng = editor.dom.createRng();\n            rng.setStart(cursor.dom, 0);\n            rng.setEnd(cursor.dom, 0);\n            editor.selection.setRng(rng);\n            editor.nodeChanged();\n          }\n        });\n      });\n      const setSizingMode = sizing => getSelectionStartCellOrCaption(editor).each(cellOrCaption => {\n        const isForcedSizing = isTableResponsiveForced(editor) || isTablePixelsForced(editor) || isTablePercentagesForced(editor);\n        if (!isForcedSizing) {\n          table(cellOrCaption, isRoot).each(table => {\n            if (sizing === 'relative' && !isPercentSizing(table)) {\n              convertToPercentSize(table);\n            } else if (sizing === 'fixed' && !isPixelSizing(table)) {\n              convertToPixelSize(table);\n            } else if (sizing === 'responsive' && !isNoneSizing(table)) {\n              convertToNoneSize(table);\n            }\n            removeDataStyle(table);\n            fireTableModified(editor, table.dom, structureModified);\n          });\n        }\n      });\n      const getTableFromCell = cell => table(cell, isRoot);\n      const performActionOnSelection = action => getSelectionStartCell(editor).bind(cell => getTableFromCell(cell).map(table => action(table, cell)));\n      const toggleTableClass = (_ui, clazz) => {\n        performActionOnSelection(table => {\n          editor.formatter.toggle('tableclass', { value: clazz }, table.dom);\n          fireTableModified(editor, table.dom, styleModified);\n        });\n      };\n      const toggleTableCellClass = (_ui, clazz) => {\n        performActionOnSelection(table => {\n          const selectedCells = getCellsFromSelection(editor);\n          const allHaveClass = forall(selectedCells, cell => editor.formatter.match('tablecellclass', { value: clazz }, cell.dom));\n          const formatterAction = allHaveClass ? editor.formatter.remove : editor.formatter.apply;\n          each$2(selectedCells, cell => formatterAction('tablecellclass', { value: clazz }, cell.dom));\n          fireTableModified(editor, table.dom, styleModified);\n        });\n      };\n      const toggleCaption = () => {\n        getSelectionStartCellOrCaption(editor).each(cellOrCaption => {\n          table(cellOrCaption, isRoot).each(table => {\n            child(table, 'caption').fold(() => {\n              const caption = SugarElement.fromTag('caption');\n              append$1(caption, SugarElement.fromText('Caption'));\n              appendAt(table, caption, 0);\n              editor.selection.setCursorLocation(caption.dom, 0);\n            }, caption => {\n              if (isTag('caption')(cellOrCaption)) {\n                one('td', table).each(td => editor.selection.setCursorLocation(td.dom, 0));\n              }\n              remove$6(caption);\n            });\n            fireTableModified(editor, table.dom, structureModified);\n          });\n        });\n      };\n      const postExecute = _data => {\n        editor.focus();\n      };\n      const actOnSelection = (execute, noEvents = false) => performActionOnSelection((table, startCell) => {\n        const targets = forMenu(getCellsFromSelection(editor), table, startCell);\n        execute(table, targets, noEvents).each(postExecute);\n      });\n      const copyRowSelection = () => performActionOnSelection((table, startCell) => {\n        const targets = forMenu(getCellsFromSelection(editor), table, startCell);\n        const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), Optional.none());\n        return copyRows(table, targets, generators);\n      });\n      const copyColSelection = () => performActionOnSelection((table, startCell) => {\n        const targets = forMenu(getCellsFromSelection(editor), table, startCell);\n        return copyCols(table, targets);\n      });\n      const pasteOnSelection = (execute, getRows) => getRows().each(rows => {\n        const clonedRows = map$1(rows, row => deep(row));\n        performActionOnSelection((table, startCell) => {\n          const generators = paste$1(SugarElement.fromDom(editor.getDoc()));\n          const targets = pasteRows(getCellsFromSelection(editor), startCell, clonedRows, generators);\n          execute(table, targets).each(postExecute);\n        });\n      });\n      const actOnType = getAction => (_ui, args) => get$c(args, 'type').each(type => {\n        actOnSelection(getAction(type), args.no_events);\n      });\n      each$1({\n        mceTableSplitCells: () => actOnSelection(actions.unmergeCells),\n        mceTableMergeCells: () => actOnSelection(actions.mergeCells),\n        mceTableInsertRowBefore: () => actOnSelection(actions.insertRowsBefore),\n        mceTableInsertRowAfter: () => actOnSelection(actions.insertRowsAfter),\n        mceTableInsertColBefore: () => actOnSelection(actions.insertColumnsBefore),\n        mceTableInsertColAfter: () => actOnSelection(actions.insertColumnsAfter),\n        mceTableDeleteCol: () => actOnSelection(actions.deleteColumn),\n        mceTableDeleteRow: () => actOnSelection(actions.deleteRow),\n        mceTableCutCol: () => copyColSelection().each(selection => {\n          setColumns(selection);\n          actOnSelection(actions.deleteColumn);\n        }),\n        mceTableCutRow: () => copyRowSelection().each(selection => {\n          setRows(selection);\n          actOnSelection(actions.deleteRow);\n        }),\n        mceTableCopyCol: () => copyColSelection().each(selection => setColumns(selection)),\n        mceTableCopyRow: () => copyRowSelection().each(selection => setRows(selection)),\n        mceTablePasteColBefore: () => pasteOnSelection(actions.pasteColsBefore, getColumns),\n        mceTablePasteColAfter: () => pasteOnSelection(actions.pasteColsAfter, getColumns),\n        mceTablePasteRowBefore: () => pasteOnSelection(actions.pasteRowsBefore, getRows),\n        mceTablePasteRowAfter: () => pasteOnSelection(actions.pasteRowsAfter, getRows),\n        mceTableDelete: eraseTable,\n        mceTableCellToggleClass: toggleTableCellClass,\n        mceTableToggleClass: toggleTableClass,\n        mceTableToggleCaption: toggleCaption,\n        mceTableSizingMode: (_ui, sizing) => setSizingMode(sizing),\n        mceTableCellType: actOnType(type => type === 'th' ? actions.makeCellsHeader : actions.unmakeCellsHeader),\n        mceTableColType: actOnType(type => type === 'th' ? actions.makeColumnsHeader : actions.unmakeColumnsHeader),\n        mceTableRowType: actOnType(type => {\n          switch (type) {\n          case 'header':\n            return actions.makeRowsHeader;\n          case 'footer':\n            return actions.makeRowsFooter;\n          default:\n            return actions.makeRowsBody;\n          }\n        })\n      }, (func, name) => editor.addCommand(name, func));\n      editor.addCommand('mceInsertTable', (_ui, args) => {\n        insertTable(editor, args.rows, args.columns, args.options);\n      });\n      editor.addCommand('mceTableApplyCellStyle', (_ui, args) => {\n        const getFormatName = style => 'tablecell' + style.toLowerCase().replace('-', '');\n        if (!isObject(args)) {\n          return;\n        }\n        const cells = getCellsFromSelection(editor);\n        if (cells.length === 0) {\n          return;\n        }\n        const validArgs = filter$1(args, (value, style) => editor.formatter.has(getFormatName(style)) && isString(value));\n        if (isEmpty(validArgs)) {\n          return;\n        }\n        each$1(validArgs, (value, style) => {\n          const formatName = getFormatName(style);\n          each$2(cells, cell => {\n            if (value === '') {\n              editor.formatter.remove(formatName, { value: null }, cell.dom, true);\n            } else {\n              editor.formatter.apply(formatName, { value }, cell.dom);\n            }\n          });\n        });\n        getTableFromCell(cells[0]).each(table => fireTableModified(editor, table.dom, styleModified));\n      });\n    };\n\n    const registerQueryCommands = (editor, actions) => {\n      const isRoot = getIsRoot(editor);\n      const lookupOnSelection = action => getSelectionCell(getSelectionStart(editor)).bind(cell => table(cell, isRoot).map(table => {\n        const targets = forMenu(getCellsFromSelection(editor), table, cell);\n        return action(table, targets);\n      })).getOr('');\n      each$1({\n        mceTableRowType: () => lookupOnSelection(actions.getTableRowType),\n        mceTableCellType: () => lookupOnSelection(actions.getTableCellType),\n        mceTableColType: () => lookupOnSelection(actions.getTableColType)\n      }, (func, name) => editor.addQueryValueHandler(name, func));\n    };\n\n    const adt$4 = Adt.generate([\n      { before: ['element'] },\n      {\n        on: [\n          'element',\n          'offset'\n        ]\n      },\n      { after: ['element'] }\n    ]);\n    const cata$1 = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);\n    const getStart$1 = situ => situ.fold(identity, identity, identity);\n    const before$2 = adt$4.before;\n    const on = adt$4.on;\n    const after$3 = adt$4.after;\n    const Situ = {\n      before: before$2,\n      on,\n      after: after$3,\n      cata: cata$1,\n      getStart: getStart$1\n    };\n\n    const create$4 = (selection, kill) => ({\n      selection,\n      kill\n    });\n    const Response = { create: create$4 };\n\n    const selectNode = (win, element) => {\n      const rng = win.document.createRange();\n      rng.selectNode(element.dom);\n      return rng;\n    };\n    const selectNodeContents = (win, element) => {\n      const rng = win.document.createRange();\n      selectNodeContentsUsing(rng, element);\n      return rng;\n    };\n    const selectNodeContentsUsing = (rng, element) => rng.selectNodeContents(element.dom);\n    const setStart = (rng, situ) => {\n      situ.fold(e => {\n        rng.setStartBefore(e.dom);\n      }, (e, o) => {\n        rng.setStart(e.dom, o);\n      }, e => {\n        rng.setStartAfter(e.dom);\n      });\n    };\n    const setFinish = (rng, situ) => {\n      situ.fold(e => {\n        rng.setEndBefore(e.dom);\n      }, (e, o) => {\n        rng.setEnd(e.dom, o);\n      }, e => {\n        rng.setEndAfter(e.dom);\n      });\n    };\n    const relativeToNative = (win, startSitu, finishSitu) => {\n      const range = win.document.createRange();\n      setStart(range, startSitu);\n      setFinish(range, finishSitu);\n      return range;\n    };\n    const exactToNative = (win, start, soffset, finish, foffset) => {\n      const rng = win.document.createRange();\n      rng.setStart(start.dom, soffset);\n      rng.setEnd(finish.dom, foffset);\n      return rng;\n    };\n    const toRect = rect => ({\n      left: rect.left,\n      top: rect.top,\n      right: rect.right,\n      bottom: rect.bottom,\n      width: rect.width,\n      height: rect.height\n    });\n    const getFirstRect$1 = rng => {\n      const rects = rng.getClientRects();\n      const rect = rects.length > 0 ? rects[0] : rng.getBoundingClientRect();\n      return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();\n    };\n\n    const adt$3 = Adt.generate([\n      {\n        ltr: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      },\n      {\n        rtl: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      }\n    ]);\n    const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);\n    const getRanges = (win, selection) => selection.match({\n      domRange: rng => {\n        return {\n          ltr: constant(rng),\n          rtl: Optional.none\n        };\n      },\n      relative: (startSitu, finishSitu) => {\n        return {\n          ltr: cached(() => relativeToNative(win, startSitu, finishSitu)),\n          rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu)))\n        };\n      },\n      exact: (start, soffset, finish, foffset) => {\n        return {\n          ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),\n          rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, soffset)))\n        };\n      }\n    });\n    const doDiagnose = (win, ranges) => {\n      const rng = ranges.ltr();\n      if (rng.collapsed) {\n        const reversed = ranges.rtl().filter(rev => rev.collapsed === false);\n        return reversed.map(rev => adt$3.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)).getOrThunk(() => fromRange(win, adt$3.ltr, rng));\n      } else {\n        return fromRange(win, adt$3.ltr, rng);\n      }\n    };\n    const diagnose = (win, selection) => {\n      const ranges = getRanges(win, selection);\n      return doDiagnose(win, ranges);\n    };\n    const asLtrRange = (win, selection) => {\n      const diagnosis = diagnose(win, selection);\n      return diagnosis.match({\n        ltr: (start, soffset, finish, foffset) => {\n          const rng = win.document.createRange();\n          rng.setStart(start.dom, soffset);\n          rng.setEnd(finish.dom, foffset);\n          return rng;\n        },\n        rtl: (start, soffset, finish, foffset) => {\n          const rng = win.document.createRange();\n          rng.setStart(finish.dom, foffset);\n          rng.setEnd(start.dom, soffset);\n          return rng;\n        }\n      });\n    };\n    adt$3.ltr;\n    adt$3.rtl;\n\n    const create$3 = (start, soffset, finish, foffset) => ({\n      start,\n      soffset,\n      finish,\n      foffset\n    });\n    const SimRange = { create: create$3 };\n\n    const create$2 = (start, soffset, finish, foffset) => {\n      return {\n        start: Situ.on(start, soffset),\n        finish: Situ.on(finish, foffset)\n      };\n    };\n    const Situs = { create: create$2 };\n\n    const convertToRange = (win, selection) => {\n      const rng = asLtrRange(win, selection);\n      return SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset);\n    };\n    const makeSitus = Situs.create;\n\n    const sync = (container, isRoot, start, soffset, finish, foffset, selectRange) => {\n      if (!(eq$1(start, finish) && soffset === foffset)) {\n        return closest$1(start, 'td,th', isRoot).bind(s => {\n          return closest$1(finish, 'td,th', isRoot).bind(f => {\n            return detect(container, isRoot, s, f, selectRange);\n          });\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n    const detect = (container, isRoot, start, finish, selectRange) => {\n      if (!eq$1(start, finish)) {\n        return identify(start, finish, isRoot).bind(cellSel => {\n          const boxes = cellSel.boxes.getOr([]);\n          if (boxes.length > 1) {\n            selectRange(container, boxes, cellSel.start, cellSel.finish);\n            return Optional.some(Response.create(Optional.some(makeSitus(start, 0, start, getEnd(start))), true));\n          } else {\n            return Optional.none();\n          }\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n    const update = (rows, columns, container, selected, annotations) => {\n      const updateSelection = newSels => {\n        annotations.clearBeforeUpdate(container);\n        annotations.selectRange(container, newSels.boxes, newSels.start, newSels.finish);\n        return newSels.boxes;\n      };\n      return shiftSelection(selected, rows, columns, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(updateSelection);\n    };\n\n    const traverse = (item, mode) => ({\n      item,\n      mode\n    });\n    const backtrack = (universe, item, _direction, transition = sidestep) => {\n      return universe.property().parent(item).map(p => {\n        return traverse(p, transition);\n      });\n    };\n    const sidestep = (universe, item, direction, transition = advance) => {\n      return direction.sibling(universe, item).map(p => {\n        return traverse(p, transition);\n      });\n    };\n    const advance = (universe, item, direction, transition = advance) => {\n      const children = universe.property().children(item);\n      const result = direction.first(children);\n      return result.map(r => {\n        return traverse(r, transition);\n      });\n    };\n    const successors = [\n      {\n        current: backtrack,\n        next: sidestep,\n        fallback: Optional.none()\n      },\n      {\n        current: sidestep,\n        next: advance,\n        fallback: Optional.some(backtrack)\n      },\n      {\n        current: advance,\n        next: advance,\n        fallback: Optional.some(sidestep)\n      }\n    ];\n    const go = (universe, item, mode, direction, rules = successors) => {\n      const ruleOpt = find$1(rules, succ => {\n        return succ.current === mode;\n      });\n      return ruleOpt.bind(rule => {\n        return rule.current(universe, item, direction, rule.next).orThunk(() => {\n          return rule.fallback.bind(fb => {\n            return go(universe, item, fb, direction);\n          });\n        });\n      });\n    };\n\n    const left$1 = () => {\n      const sibling = (universe, item) => {\n        return universe.query().prevSibling(item);\n      };\n      const first = children => {\n        return children.length > 0 ? Optional.some(children[children.length - 1]) : Optional.none();\n      };\n      return {\n        sibling,\n        first\n      };\n    };\n    const right$1 = () => {\n      const sibling = (universe, item) => {\n        return universe.query().nextSibling(item);\n      };\n      const first = children => {\n        return children.length > 0 ? Optional.some(children[0]) : Optional.none();\n      };\n      return {\n        sibling,\n        first\n      };\n    };\n    const Walkers = {\n      left: left$1,\n      right: right$1\n    };\n\n    const hone = (universe, item, predicate, mode, direction, isRoot) => {\n      const next = go(universe, item, mode, direction);\n      return next.bind(n => {\n        if (isRoot(n.item)) {\n          return Optional.none();\n        } else {\n          return predicate(n.item) ? Optional.some(n.item) : hone(universe, n.item, predicate, n.mode, direction, isRoot);\n        }\n      });\n    };\n    const left = (universe, item, predicate, isRoot) => {\n      return hone(universe, item, predicate, sidestep, Walkers.left(), isRoot);\n    };\n    const right = (universe, item, predicate, isRoot) => {\n      return hone(universe, item, predicate, sidestep, Walkers.right(), isRoot);\n    };\n\n    const isLeaf = universe => element => universe.property().children(element).length === 0;\n    const before$1 = (universe, item, isRoot) => {\n      return seekLeft$1(universe, item, isLeaf(universe), isRoot);\n    };\n    const after$2 = (universe, item, isRoot) => {\n      return seekRight$1(universe, item, isLeaf(universe), isRoot);\n    };\n    const seekLeft$1 = left;\n    const seekRight$1 = right;\n\n    const universe = DomUniverse();\n    const before = (element, isRoot) => {\n      return before$1(universe, element, isRoot);\n    };\n    const after$1 = (element, isRoot) => {\n      return after$2(universe, element, isRoot);\n    };\n    const seekLeft = (element, predicate, isRoot) => {\n      return seekLeft$1(universe, element, predicate, isRoot);\n    };\n    const seekRight = (element, predicate, isRoot) => {\n      return seekRight$1(universe, element, predicate, isRoot);\n    };\n\n    const ancestor = (scope, predicate, isRoot) => ancestor$2(scope, predicate, isRoot).isSome();\n\n    const adt$2 = Adt.generate([\n      { none: ['message'] },\n      { success: [] },\n      { failedUp: ['cell'] },\n      { failedDown: ['cell'] }\n    ]);\n    const isOverlapping = (bridge, before, after) => {\n      const beforeBounds = bridge.getRect(before);\n      const afterBounds = bridge.getRect(after);\n      return afterBounds.right > beforeBounds.left && afterBounds.left < beforeBounds.right;\n    };\n    const isRow = elem => {\n      return closest$1(elem, 'tr');\n    };\n    const verify = (bridge, before, beforeOffset, after, afterOffset, failure, isRoot) => {\n      return closest$1(after, 'td,th', isRoot).bind(afterCell => {\n        return closest$1(before, 'td,th', isRoot).map(beforeCell => {\n          if (!eq$1(afterCell, beforeCell)) {\n            return sharedOne(isRow, [\n              afterCell,\n              beforeCell\n            ]).fold(() => {\n              return isOverlapping(bridge, beforeCell, afterCell) ? adt$2.success() : failure(beforeCell);\n            }, _sharedRow => {\n              return failure(beforeCell);\n            });\n          } else {\n            return eq$1(after, afterCell) && getEnd(afterCell) === afterOffset ? failure(beforeCell) : adt$2.none('in same cell');\n          }\n        });\n      }).getOr(adt$2.none('default'));\n    };\n    const cata = (subject, onNone, onSuccess, onFailedUp, onFailedDown) => {\n      return subject.fold(onNone, onSuccess, onFailedUp, onFailedDown);\n    };\n    const BeforeAfter = {\n      ...adt$2,\n      verify,\n      cata\n    };\n\n    const inParent = (parent, children, element, index) => ({\n      parent,\n      children,\n      element,\n      index\n    });\n    const indexInParent = element => parent(element).bind(parent => {\n      const children = children$2(parent);\n      return indexOf(children, element).map(index => inParent(parent, children, element, index));\n    });\n    const indexOf = (elements, element) => findIndex(elements, curry(eq$1, element));\n\n    const isBr = isTag('br');\n    const gatherer = (cand, gather, isRoot) => {\n      return gather(cand, isRoot).bind(target => {\n        return isText(target) && get$6(target).trim().length === 0 ? gatherer(target, gather, isRoot) : Optional.some(target);\n      });\n    };\n    const handleBr = (isRoot, element, direction) => {\n      return direction.traverse(element).orThunk(() => {\n        return gatherer(element, direction.gather, isRoot);\n      }).map(direction.relative);\n    };\n    const findBr = (element, offset) => {\n      return child$2(element, offset).filter(isBr).orThunk(() => {\n        return child$2(element, offset - 1).filter(isBr);\n      });\n    };\n    const handleParent = (isRoot, element, offset, direction) => {\n      return findBr(element, offset).bind(br => {\n        return direction.traverse(br).fold(() => {\n          return gatherer(br, direction.gather, isRoot).map(direction.relative);\n        }, adjacent => {\n          return indexInParent(adjacent).map(info => {\n            return Situ.on(info.parent, info.index);\n          });\n        });\n      });\n    };\n    const tryBr = (isRoot, element, offset, direction) => {\n      const target = isBr(element) ? handleBr(isRoot, element, direction) : handleParent(isRoot, element, offset, direction);\n      return target.map(tgt => {\n        return {\n          start: tgt,\n          finish: tgt\n        };\n      });\n    };\n    const process = analysis => {\n      return BeforeAfter.cata(analysis, _message => {\n        return Optional.none();\n      }, () => {\n        return Optional.none();\n      }, cell => {\n        return Optional.some(point(cell, 0));\n      }, cell => {\n        return Optional.some(point(cell, getEnd(cell)));\n      });\n    };\n\n    const moveDown = (caret, amount) => {\n      return {\n        left: caret.left,\n        top: caret.top + amount,\n        right: caret.right,\n        bottom: caret.bottom + amount\n      };\n    };\n    const moveUp = (caret, amount) => {\n      return {\n        left: caret.left,\n        top: caret.top - amount,\n        right: caret.right,\n        bottom: caret.bottom - amount\n      };\n    };\n    const translate = (caret, xDelta, yDelta) => {\n      return {\n        left: caret.left + xDelta,\n        top: caret.top + yDelta,\n        right: caret.right + xDelta,\n        bottom: caret.bottom + yDelta\n      };\n    };\n    const getTop = caret => {\n      return caret.top;\n    };\n    const getBottom = caret => {\n      return caret.bottom;\n    };\n\n    const getPartialBox = (bridge, element, offset) => {\n      if (offset >= 0 && offset < getEnd(element)) {\n        return bridge.getRangedRect(element, offset, element, offset + 1);\n      } else if (offset > 0) {\n        return bridge.getRangedRect(element, offset - 1, element, offset);\n      }\n      return Optional.none();\n    };\n    const toCaret = rect => ({\n      left: rect.left,\n      top: rect.top,\n      right: rect.right,\n      bottom: rect.bottom\n    });\n    const getElemBox = (bridge, element) => {\n      return Optional.some(bridge.getRect(element));\n    };\n    const getBoxAt = (bridge, element, offset) => {\n      if (isElement(element)) {\n        return getElemBox(bridge, element).map(toCaret);\n      } else if (isText(element)) {\n        return getPartialBox(bridge, element, offset).map(toCaret);\n      } else {\n        return Optional.none();\n      }\n    };\n    const getEntireBox = (bridge, element) => {\n      if (isElement(element)) {\n        return getElemBox(bridge, element).map(toCaret);\n      } else if (isText(element)) {\n        return bridge.getRangedRect(element, 0, element, getEnd(element)).map(toCaret);\n      } else {\n        return Optional.none();\n      }\n    };\n\n    const JUMP_SIZE = 5;\n    const NUM_RETRIES = 100;\n    const adt$1 = Adt.generate([\n      { none: [] },\n      { retry: ['caret'] }\n    ]);\n    const isOutside = (caret, box) => {\n      return caret.left < box.left || Math.abs(box.right - caret.left) < 1 || caret.left > box.right;\n    };\n    const inOutsideBlock = (bridge, element, caret) => {\n      return closest$2(element, isBlock).fold(never, cell => {\n        return getEntireBox(bridge, cell).exists(box => {\n          return isOutside(caret, box);\n        });\n      });\n    };\n    const adjustDown = (bridge, element, guessBox, original, caret) => {\n      const lowerCaret = moveDown(caret, JUMP_SIZE);\n      if (Math.abs(guessBox.bottom - original.bottom) < 1) {\n        return adt$1.retry(lowerCaret);\n      } else if (guessBox.top > caret.bottom) {\n        return adt$1.retry(lowerCaret);\n      } else if (guessBox.top === caret.bottom) {\n        return adt$1.retry(moveDown(caret, 1));\n      } else {\n        return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(lowerCaret, JUMP_SIZE, 0)) : adt$1.none();\n      }\n    };\n    const adjustUp = (bridge, element, guessBox, original, caret) => {\n      const higherCaret = moveUp(caret, JUMP_SIZE);\n      if (Math.abs(guessBox.top - original.top) < 1) {\n        return adt$1.retry(higherCaret);\n      } else if (guessBox.bottom < caret.top) {\n        return adt$1.retry(higherCaret);\n      } else if (guessBox.bottom === caret.top) {\n        return adt$1.retry(moveUp(caret, 1));\n      } else {\n        return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(higherCaret, JUMP_SIZE, 0)) : adt$1.none();\n      }\n    };\n    const upMovement = {\n      point: getTop,\n      adjuster: adjustUp,\n      move: moveUp,\n      gather: before\n    };\n    const downMovement = {\n      point: getBottom,\n      adjuster: adjustDown,\n      move: moveDown,\n      gather: after$1\n    };\n    const isAtTable = (bridge, x, y) => {\n      return bridge.elementFromPoint(x, y).filter(elm => {\n        return name(elm) === 'table';\n      }).isSome();\n    };\n    const adjustForTable = (bridge, movement, original, caret, numRetries) => {\n      return adjustTil(bridge, movement, original, movement.move(caret, JUMP_SIZE), numRetries);\n    };\n    const adjustTil = (bridge, movement, original, caret, numRetries) => {\n      if (numRetries === 0) {\n        return Optional.some(caret);\n      }\n      if (isAtTable(bridge, caret.left, movement.point(caret))) {\n        return adjustForTable(bridge, movement, original, caret, numRetries - 1);\n      }\n      return bridge.situsFromPoint(caret.left, movement.point(caret)).bind(guess => {\n        return guess.start.fold(Optional.none, element => {\n          return getEntireBox(bridge, element).bind(guessBox => {\n            return movement.adjuster(bridge, element, guessBox, original, caret).fold(Optional.none, newCaret => {\n              return adjustTil(bridge, movement, original, newCaret, numRetries - 1);\n            });\n          }).orThunk(() => {\n            return Optional.some(caret);\n          });\n        }, Optional.none);\n      });\n    };\n    const checkScroll = (movement, adjusted, bridge) => {\n      if (movement.point(adjusted) > bridge.getInnerHeight()) {\n        return Optional.some(movement.point(adjusted) - bridge.getInnerHeight());\n      } else if (movement.point(adjusted) < 0) {\n        return Optional.some(-movement.point(adjusted));\n      } else {\n        return Optional.none();\n      }\n    };\n    const retry = (movement, bridge, caret) => {\n      const moved = movement.move(caret, JUMP_SIZE);\n      const adjusted = adjustTil(bridge, movement, caret, moved, NUM_RETRIES).getOr(moved);\n      return checkScroll(movement, adjusted, bridge).fold(() => {\n        return bridge.situsFromPoint(adjusted.left, movement.point(adjusted));\n      }, delta => {\n        bridge.scrollBy(0, delta);\n        return bridge.situsFromPoint(adjusted.left, movement.point(adjusted) - delta);\n      });\n    };\n    const Retries = {\n      tryUp: curry(retry, upMovement),\n      tryDown: curry(retry, downMovement),\n      getJumpSize: constant(JUMP_SIZE)\n    };\n\n    const MAX_RETRIES = 20;\n    const findSpot = (bridge, isRoot, direction) => {\n      return bridge.getSelection().bind(sel => {\n        return tryBr(isRoot, sel.finish, sel.foffset, direction).fold(() => {\n          return Optional.some(point(sel.finish, sel.foffset));\n        }, brNeighbour => {\n          const range = bridge.fromSitus(brNeighbour);\n          const analysis = BeforeAfter.verify(bridge, sel.finish, sel.foffset, range.finish, range.foffset, direction.failure, isRoot);\n          return process(analysis);\n        });\n      });\n    };\n    const scan = (bridge, isRoot, element, offset, direction, numRetries) => {\n      if (numRetries === 0) {\n        return Optional.none();\n      }\n      return tryCursor(bridge, isRoot, element, offset, direction).bind(situs => {\n        const range = bridge.fromSitus(situs);\n        const analysis = BeforeAfter.verify(bridge, element, offset, range.finish, range.foffset, direction.failure, isRoot);\n        return BeforeAfter.cata(analysis, () => {\n          return Optional.none();\n        }, () => {\n          return Optional.some(situs);\n        }, cell => {\n          if (eq$1(element, cell) && offset === 0) {\n            return tryAgain(bridge, element, offset, moveUp, direction);\n          } else {\n            return scan(bridge, isRoot, cell, 0, direction, numRetries - 1);\n          }\n        }, cell => {\n          if (eq$1(element, cell) && offset === getEnd(cell)) {\n            return tryAgain(bridge, element, offset, moveDown, direction);\n          } else {\n            return scan(bridge, isRoot, cell, getEnd(cell), direction, numRetries - 1);\n          }\n        });\n      });\n    };\n    const tryAgain = (bridge, element, offset, move, direction) => {\n      return getBoxAt(bridge, element, offset).bind(box => {\n        return tryAt(bridge, direction, move(box, Retries.getJumpSize()));\n      });\n    };\n    const tryAt = (bridge, direction, box) => {\n      const browser = detect$2().browser;\n      if (browser.isChromium() || browser.isSafari() || browser.isFirefox()) {\n        return direction.retry(bridge, box);\n      } else {\n        return Optional.none();\n      }\n    };\n    const tryCursor = (bridge, isRoot, element, offset, direction) => {\n      return getBoxAt(bridge, element, offset).bind(box => {\n        return tryAt(bridge, direction, box);\n      });\n    };\n    const handle$1 = (bridge, isRoot, direction) => {\n      return findSpot(bridge, isRoot, direction).bind(spot => {\n        return scan(bridge, isRoot, spot.element, spot.offset, direction, MAX_RETRIES).map(bridge.fromSitus);\n      });\n    };\n\n    const inSameTable = (elem, table) => {\n      return ancestor(elem, e => {\n        return parent(e).exists(p => {\n          return eq$1(p, table);\n        });\n      });\n    };\n    const simulate = (bridge, isRoot, direction, initial, anchor) => {\n      return closest$1(initial, 'td,th', isRoot).bind(start => {\n        return closest$1(start, 'table', isRoot).bind(table => {\n          if (!inSameTable(anchor, table)) {\n            return Optional.none();\n          }\n          return handle$1(bridge, isRoot, direction).bind(range => {\n            return closest$1(range.finish, 'td,th', isRoot).map(finish => {\n              return {\n                start,\n                finish,\n                range\n              };\n            });\n          });\n        });\n      });\n    };\n    const navigate = (bridge, isRoot, direction, initial, anchor, precheck) => {\n      return precheck(initial, isRoot).orThunk(() => {\n        return simulate(bridge, isRoot, direction, initial, anchor).map(info => {\n          const range = info.range;\n          return Response.create(Optional.some(makeSitus(range.start, range.soffset, range.finish, range.foffset)), true);\n        });\n      });\n    };\n    const firstUpCheck = (initial, isRoot) => {\n      return closest$1(initial, 'tr', isRoot).bind(startRow => {\n        return closest$1(startRow, 'table', isRoot).bind(table => {\n          const rows = descendants(table, 'tr');\n          if (eq$1(startRow, rows[0])) {\n            return seekLeft(table, element => {\n              return last$1(element).isSome();\n            }, isRoot).map(last => {\n              const lastOffset = getEnd(last);\n              return Response.create(Optional.some(makeSitus(last, lastOffset, last, lastOffset)), true);\n            });\n          } else {\n            return Optional.none();\n          }\n        });\n      });\n    };\n    const lastDownCheck = (initial, isRoot) => {\n      return closest$1(initial, 'tr', isRoot).bind(startRow => {\n        return closest$1(startRow, 'table', isRoot).bind(table => {\n          const rows = descendants(table, 'tr');\n          if (eq$1(startRow, rows[rows.length - 1])) {\n            return seekRight(table, element => {\n              return first(element).isSome();\n            }, isRoot).map(first => {\n              return Response.create(Optional.some(makeSitus(first, 0, first, 0)), true);\n            });\n          } else {\n            return Optional.none();\n          }\n        });\n      });\n    };\n    const select = (bridge, container, isRoot, direction, initial, anchor, selectRange) => {\n      return simulate(bridge, isRoot, direction, initial, anchor).bind(info => {\n        return detect(container, isRoot, info.start, info.finish, selectRange);\n      });\n    };\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const singleton = doRevoke => {\n      const subject = Cell(Optional.none());\n      const revoke = () => subject.get().each(doRevoke);\n      const clear = () => {\n        revoke();\n        subject.set(Optional.none());\n      };\n      const isSet = () => subject.get().isSome();\n      const get = () => subject.get();\n      const set = s => {\n        revoke();\n        subject.set(Optional.some(s));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const value = () => {\n      const subject = singleton(noop);\n      const on = f => subject.get().each(f);\n      return {\n        ...subject,\n        on\n      };\n    };\n\n    const findCell = (target, isRoot) => closest$1(target, 'td,th', isRoot);\n    const MouseSelection = (bridge, container, isRoot, annotations) => {\n      const cursor = value();\n      const clearstate = cursor.clear;\n      const applySelection = event => {\n        cursor.on(start => {\n          annotations.clearBeforeUpdate(container);\n          findCell(event.target, isRoot).each(finish => {\n            identify(start, finish, isRoot).each(cellSel => {\n              const boxes = cellSel.boxes.getOr([]);\n              if (boxes.length === 1) {\n                const singleCell = boxes[0];\n                const isNonEditableCell = getRaw(singleCell) === 'false';\n                const isCellClosestContentEditable = is(closest(event.target), singleCell, eq$1);\n                if (isNonEditableCell && isCellClosestContentEditable) {\n                  annotations.selectRange(container, boxes, singleCell, singleCell);\n                  bridge.selectContents(singleCell);\n                }\n              } else if (boxes.length > 1) {\n                annotations.selectRange(container, boxes, cellSel.start, cellSel.finish);\n                bridge.selectContents(finish);\n              }\n            });\n          });\n        });\n      };\n      const mousedown = event => {\n        annotations.clear(container);\n        findCell(event.target, isRoot).each(cursor.set);\n      };\n      const mouseover = event => {\n        applySelection(event);\n      };\n      const mouseup = event => {\n        applySelection(event);\n        clearstate();\n      };\n      return {\n        clearstate,\n        mousedown,\n        mouseover,\n        mouseup\n      };\n    };\n\n    const down = {\n      traverse: nextSibling,\n      gather: after$1,\n      relative: Situ.before,\n      retry: Retries.tryDown,\n      failure: BeforeAfter.failedDown\n    };\n    const up = {\n      traverse: prevSibling,\n      gather: before,\n      relative: Situ.before,\n      retry: Retries.tryUp,\n      failure: BeforeAfter.failedUp\n    };\n\n    const isKey = key => {\n      return keycode => {\n        return keycode === key;\n      };\n    };\n    const isUp = isKey(38);\n    const isDown = isKey(40);\n    const isNavigation = keycode => {\n      return keycode >= 37 && keycode <= 40;\n    };\n    const ltr = {\n      isBackward: isKey(37),\n      isForward: isKey(39)\n    };\n    const rtl = {\n      isBackward: isKey(39),\n      isForward: isKey(37)\n    };\n\n    const get$3 = _DOC => {\n      const doc = _DOC !== undefined ? _DOC.dom : document;\n      const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;\n      const y = doc.body.scrollTop || doc.documentElement.scrollTop;\n      return SugarPosition(x, y);\n    };\n    const by = (x, y, _DOC) => {\n      const doc = _DOC !== undefined ? _DOC.dom : document;\n      const win = doc.defaultView;\n      if (win) {\n        win.scrollBy(x, y);\n      }\n    };\n\n    const adt = Adt.generate([\n      { domRange: ['rng'] },\n      {\n        relative: [\n          'startSitu',\n          'finishSitu'\n        ]\n      },\n      {\n        exact: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      }\n    ]);\n    const exactFromRange = simRange => adt.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);\n    const getStart = selection => selection.match({\n      domRange: rng => SugarElement.fromDom(rng.startContainer),\n      relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),\n      exact: (start, _soffset, _finish, _foffset) => start\n    });\n    const domRange = adt.domRange;\n    const relative = adt.relative;\n    const exact = adt.exact;\n    const getWin = selection => {\n      const start = getStart(selection);\n      return defaultView(start);\n    };\n    const range = SimRange.create;\n    const SimSelection = {\n      domRange,\n      relative,\n      exact,\n      exactFromRange,\n      getWin,\n      range\n    };\n\n    const caretPositionFromPoint = (doc, x, y) => {\n      var _a, _b;\n      return Optional.from((_b = (_a = doc.dom).caretPositionFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)).bind(pos => {\n        if (pos.offsetNode === null) {\n          return Optional.none();\n        }\n        const r = doc.dom.createRange();\n        r.setStart(pos.offsetNode, pos.offset);\n        r.collapse();\n        return Optional.some(r);\n      });\n    };\n    const caretRangeFromPoint = (doc, x, y) => {\n      var _a, _b;\n      return Optional.from((_b = (_a = doc.dom).caretRangeFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y));\n    };\n    const availableSearch = (() => {\n      if (document.caretPositionFromPoint) {\n        return caretPositionFromPoint;\n      } else if (document.caretRangeFromPoint) {\n        return caretRangeFromPoint;\n      } else {\n        return Optional.none;\n      }\n    })();\n    const fromPoint = (win, x, y) => {\n      const doc = SugarElement.fromDom(win.document);\n      return availableSearch(doc, x, y).map(rng => SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));\n    };\n\n    const beforeSpecial = (element, offset) => {\n      const name$1 = name(element);\n      if ('input' === name$1) {\n        return Situ.after(element);\n      } else if (!contains$2([\n          'br',\n          'img'\n        ], name$1)) {\n        return Situ.on(element, offset);\n      } else {\n        return offset === 0 ? Situ.before(element) : Situ.after(element);\n      }\n    };\n    const preprocessRelative = (startSitu, finishSitu) => {\n      const start = startSitu.fold(Situ.before, beforeSpecial, Situ.after);\n      const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after);\n      return SimSelection.relative(start, finish);\n    };\n    const preprocessExact = (start, soffset, finish, foffset) => {\n      const startSitu = beforeSpecial(start, soffset);\n      const finishSitu = beforeSpecial(finish, foffset);\n      return SimSelection.relative(startSitu, finishSitu);\n    };\n\n    const makeRange = (start, soffset, finish, foffset) => {\n      const doc = owner(start);\n      const rng = doc.dom.createRange();\n      rng.setStart(start.dom, soffset);\n      rng.setEnd(finish.dom, foffset);\n      return rng;\n    };\n    const after = (start, soffset, finish, foffset) => {\n      const r = makeRange(start, soffset, finish, foffset);\n      const same = eq$1(start, finish) && soffset === foffset;\n      return r.collapsed && !same;\n    };\n\n    const getNativeSelection = win => Optional.from(win.getSelection());\n    const doSetNativeRange = (win, rng) => {\n      getNativeSelection(win).each(selection => {\n        selection.removeAllRanges();\n        selection.addRange(rng);\n      });\n    };\n    const doSetRange = (win, start, soffset, finish, foffset) => {\n      const rng = exactToNative(win, start, soffset, finish, foffset);\n      doSetNativeRange(win, rng);\n    };\n    const setLegacyRtlRange = (win, selection, start, soffset, finish, foffset) => {\n      selection.collapse(start.dom, soffset);\n      selection.extend(finish.dom, foffset);\n    };\n    const setRangeFromRelative = (win, relative) => diagnose(win, relative).match({\n      ltr: (start, soffset, finish, foffset) => {\n        doSetRange(win, start, soffset, finish, foffset);\n      },\n      rtl: (start, soffset, finish, foffset) => {\n        getNativeSelection(win).each(selection => {\n          if (selection.setBaseAndExtent) {\n            selection.setBaseAndExtent(start.dom, soffset, finish.dom, foffset);\n          } else if (selection.extend) {\n            try {\n              setLegacyRtlRange(win, selection, start, soffset, finish, foffset);\n            } catch (e) {\n              doSetRange(win, finish, foffset, start, soffset);\n            }\n          } else {\n            doSetRange(win, finish, foffset, start, soffset);\n          }\n        });\n      }\n    });\n    const setExact = (win, start, soffset, finish, foffset) => {\n      const relative = preprocessExact(start, soffset, finish, foffset);\n      setRangeFromRelative(win, relative);\n    };\n    const setRelative = (win, startSitu, finishSitu) => {\n      const relative = preprocessRelative(startSitu, finishSitu);\n      setRangeFromRelative(win, relative);\n    };\n    const readRange = selection => {\n      if (selection.rangeCount > 0) {\n        const firstRng = selection.getRangeAt(0);\n        const lastRng = selection.getRangeAt(selection.rangeCount - 1);\n        return Optional.some(SimRange.create(SugarElement.fromDom(firstRng.startContainer), firstRng.startOffset, SugarElement.fromDom(lastRng.endContainer), lastRng.endOffset));\n      } else {\n        return Optional.none();\n      }\n    };\n    const doGetExact = selection => {\n      if (selection.anchorNode === null || selection.focusNode === null) {\n        return readRange(selection);\n      } else {\n        const anchor = SugarElement.fromDom(selection.anchorNode);\n        const focus = SugarElement.fromDom(selection.focusNode);\n        return after(anchor, selection.anchorOffset, focus, selection.focusOffset) ? Optional.some(SimRange.create(anchor, selection.anchorOffset, focus, selection.focusOffset)) : readRange(selection);\n      }\n    };\n    const setToElement = (win, element, selectNodeContents$1 = true) => {\n      const rngGetter = selectNodeContents$1 ? selectNodeContents : selectNode;\n      const rng = rngGetter(win, element);\n      doSetNativeRange(win, rng);\n    };\n    const getExact = win => getNativeSelection(win).filter(sel => sel.rangeCount > 0).bind(doGetExact);\n    const get$2 = win => getExact(win).map(range => SimSelection.exact(range.start, range.soffset, range.finish, range.foffset));\n    const getFirstRect = (win, selection) => {\n      const rng = asLtrRange(win, selection);\n      return getFirstRect$1(rng);\n    };\n    const getAtPoint = (win, x, y) => fromPoint(win, x, y);\n    const clear = win => {\n      getNativeSelection(win).each(selection => selection.removeAllRanges());\n    };\n\n    const WindowBridge = win => {\n      const elementFromPoint = (x, y) => {\n        return SugarElement.fromPoint(SugarElement.fromDom(win.document), x, y);\n      };\n      const getRect = element => {\n        return element.dom.getBoundingClientRect();\n      };\n      const getRangedRect = (start, soffset, finish, foffset) => {\n        const sel = SimSelection.exact(start, soffset, finish, foffset);\n        return getFirstRect(win, sel);\n      };\n      const getSelection = () => {\n        return get$2(win).map(exactAdt => {\n          return convertToRange(win, exactAdt);\n        });\n      };\n      const fromSitus = situs => {\n        const relative = SimSelection.relative(situs.start, situs.finish);\n        return convertToRange(win, relative);\n      };\n      const situsFromPoint = (x, y) => {\n        return getAtPoint(win, x, y).map(exact => {\n          return Situs.create(exact.start, exact.soffset, exact.finish, exact.foffset);\n        });\n      };\n      const clearSelection = () => {\n        clear(win);\n      };\n      const collapseSelection = (toStart = false) => {\n        get$2(win).each(sel => sel.fold(rng => rng.collapse(toStart), (startSitu, finishSitu) => {\n          const situ = toStart ? startSitu : finishSitu;\n          setRelative(win, situ, situ);\n        }, (start, soffset, finish, foffset) => {\n          const node = toStart ? start : finish;\n          const offset = toStart ? soffset : foffset;\n          setExact(win, node, offset, node, offset);\n        }));\n      };\n      const selectNode = element => {\n        setToElement(win, element, false);\n      };\n      const selectContents = element => {\n        setToElement(win, element);\n      };\n      const setSelection = sel => {\n        setExact(win, sel.start, sel.soffset, sel.finish, sel.foffset);\n      };\n      const setRelativeSelection = (start, finish) => {\n        setRelative(win, start, finish);\n      };\n      const getInnerHeight = () => {\n        return win.innerHeight;\n      };\n      const getScrollY = () => {\n        const pos = get$3(SugarElement.fromDom(win.document));\n        return pos.top;\n      };\n      const scrollBy = (x, y) => {\n        by(x, y, SugarElement.fromDom(win.document));\n      };\n      return {\n        elementFromPoint,\n        getRect,\n        getRangedRect,\n        getSelection,\n        fromSitus,\n        situsFromPoint,\n        clearSelection,\n        collapseSelection,\n        setSelection,\n        setRelativeSelection,\n        selectNode,\n        selectContents,\n        getInnerHeight,\n        getScrollY,\n        scrollBy\n      };\n    };\n\n    const rc = (rows, cols) => ({\n      rows,\n      cols\n    });\n    const mouse = (win, container, isRoot, annotations) => {\n      const bridge = WindowBridge(win);\n      const handlers = MouseSelection(bridge, container, isRoot, annotations);\n      return {\n        clearstate: handlers.clearstate,\n        mousedown: handlers.mousedown,\n        mouseover: handlers.mouseover,\n        mouseup: handlers.mouseup\n      };\n    };\n    const keyboard = (win, container, isRoot, annotations) => {\n      const bridge = WindowBridge(win);\n      const clearToNavigate = () => {\n        annotations.clear(container);\n        return Optional.none();\n      };\n      const keydown = (event, start, soffset, finish, foffset, direction) => {\n        const realEvent = event.raw;\n        const keycode = realEvent.which;\n        const shiftKey = realEvent.shiftKey === true;\n        const handler = retrieve$1(container, annotations.selectedSelector).fold(() => {\n          if (isNavigation(keycode) && !shiftKey) {\n            annotations.clearBeforeUpdate(container);\n          }\n          if (isDown(keycode) && shiftKey) {\n            return curry(select, bridge, container, isRoot, down, finish, start, annotations.selectRange);\n          } else if (isUp(keycode) && shiftKey) {\n            return curry(select, bridge, container, isRoot, up, finish, start, annotations.selectRange);\n          } else if (isDown(keycode)) {\n            return curry(navigate, bridge, isRoot, down, finish, start, lastDownCheck);\n          } else if (isUp(keycode)) {\n            return curry(navigate, bridge, isRoot, up, finish, start, firstUpCheck);\n          } else {\n            return Optional.none;\n          }\n        }, selected => {\n          const update$1 = attempts => {\n            return () => {\n              const navigation = findMap(attempts, delta => {\n                return update(delta.rows, delta.cols, container, selected, annotations);\n              });\n              return navigation.fold(() => {\n                return getEdges(container, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(edges => {\n                  const relative = isDown(keycode) || direction.isForward(keycode) ? Situ.after : Situ.before;\n                  bridge.setRelativeSelection(Situ.on(edges.first, 0), relative(edges.table));\n                  annotations.clear(container);\n                  return Response.create(Optional.none(), true);\n                });\n              }, _ => {\n                return Optional.some(Response.create(Optional.none(), true));\n              });\n            };\n          };\n          if (isDown(keycode) && shiftKey) {\n            return update$1([rc(+1, 0)]);\n          } else if (isUp(keycode) && shiftKey) {\n            return update$1([rc(-1, 0)]);\n          } else if (direction.isBackward(keycode) && shiftKey) {\n            return update$1([\n              rc(0, -1),\n              rc(-1, 0)\n            ]);\n          } else if (direction.isForward(keycode) && shiftKey) {\n            return update$1([\n              rc(0, +1),\n              rc(+1, 0)\n            ]);\n          } else if (isNavigation(keycode) && !shiftKey) {\n            return clearToNavigate;\n          } else {\n            return Optional.none;\n          }\n        });\n        return handler();\n      };\n      const keyup = (event, start, soffset, finish, foffset) => {\n        return retrieve$1(container, annotations.selectedSelector).fold(() => {\n          const realEvent = event.raw;\n          const keycode = realEvent.which;\n          const shiftKey = realEvent.shiftKey === true;\n          if (!shiftKey) {\n            return Optional.none();\n          }\n          if (isNavigation(keycode)) {\n            return sync(container, isRoot, start, soffset, finish, foffset, annotations.selectRange);\n          } else {\n            return Optional.none();\n          }\n        }, Optional.none);\n      };\n      return {\n        keydown,\n        keyup\n      };\n    };\n    const external = (win, container, isRoot, annotations) => {\n      const bridge = WindowBridge(win);\n      return (start, finish) => {\n        annotations.clearBeforeUpdate(container);\n        identify(start, finish, isRoot).each(cellSel => {\n          const boxes = cellSel.boxes.getOr([]);\n          annotations.selectRange(container, boxes, cellSel.start, cellSel.finish);\n          bridge.selectContents(finish);\n          bridge.collapseSelection();\n        });\n      };\n    };\n\n    const read = (element, attr) => {\n      const value = get$b(element, attr);\n      return value === undefined || value === '' ? [] : value.split(' ');\n    };\n    const add$2 = (element, attr, id) => {\n      const old = read(element, attr);\n      const nu = old.concat([id]);\n      set$2(element, attr, nu.join(' '));\n      return true;\n    };\n    const remove$4 = (element, attr, id) => {\n      const nu = filter$2(read(element, attr), v => v !== id);\n      if (nu.length > 0) {\n        set$2(element, attr, nu.join(' '));\n      } else {\n        remove$7(element, attr);\n      }\n      return false;\n    };\n\n    const supports = element => element.dom.classList !== undefined;\n    const get$1 = element => read(element, 'class');\n    const add$1 = (element, clazz) => add$2(element, 'class', clazz);\n    const remove$3 = (element, clazz) => remove$4(element, 'class', clazz);\n\n    const add = (element, clazz) => {\n      if (supports(element)) {\n        element.dom.classList.add(clazz);\n      } else {\n        add$1(element, clazz);\n      }\n    };\n    const cleanClass = element => {\n      const classList = supports(element) ? element.dom.classList : get$1(element);\n      if (classList.length === 0) {\n        remove$7(element, 'class');\n      }\n    };\n    const remove$2 = (element, clazz) => {\n      if (supports(element)) {\n        const classList = element.dom.classList;\n        classList.remove(clazz);\n      } else {\n        remove$3(element, clazz);\n      }\n      cleanClass(element);\n    };\n    const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);\n\n    const remove$1 = (element, classes) => {\n      each$2(classes, x => {\n        remove$2(element, x);\n      });\n    };\n\n    const addClass = clazz => element => {\n      add(element, clazz);\n    };\n    const removeClasses = classes => element => {\n      remove$1(element, classes);\n    };\n\n    const byClass = ephemera => {\n      const addSelectionClass = addClass(ephemera.selected);\n      const removeSelectionClasses = removeClasses([\n        ephemera.selected,\n        ephemera.lastSelected,\n        ephemera.firstSelected\n      ]);\n      const clear = container => {\n        const sels = descendants(container, ephemera.selectedSelector);\n        each$2(sels, removeSelectionClasses);\n      };\n      const selectRange = (container, cells, start, finish) => {\n        clear(container);\n        each$2(cells, addSelectionClass);\n        add(start, ephemera.firstSelected);\n        add(finish, ephemera.lastSelected);\n      };\n      return {\n        clearBeforeUpdate: clear,\n        clear,\n        selectRange,\n        selectedSelector: ephemera.selectedSelector,\n        firstSelectedSelector: ephemera.firstSelectedSelector,\n        lastSelectedSelector: ephemera.lastSelectedSelector\n      };\n    };\n    const byAttr = (ephemera, onSelection, onClear) => {\n      const removeSelectionAttributes = element => {\n        remove$7(element, ephemera.selected);\n        remove$7(element, ephemera.firstSelected);\n        remove$7(element, ephemera.lastSelected);\n      };\n      const addSelectionAttribute = element => {\n        set$2(element, ephemera.selected, '1');\n      };\n      const clear = container => {\n        clearBeforeUpdate(container);\n        onClear();\n      };\n      const clearBeforeUpdate = container => {\n        const sels = descendants(container, `${ ephemera.selectedSelector },${ ephemera.firstSelectedSelector },${ ephemera.lastSelectedSelector }`);\n        each$2(sels, removeSelectionAttributes);\n      };\n      const selectRange = (container, cells, start, finish) => {\n        clear(container);\n        each$2(cells, addSelectionAttribute);\n        set$2(start, ephemera.firstSelected, '1');\n        set$2(finish, ephemera.lastSelected, '1');\n        onSelection(cells, start, finish);\n      };\n      return {\n        clearBeforeUpdate,\n        clear,\n        selectRange,\n        selectedSelector: ephemera.selectedSelector,\n        firstSelectedSelector: ephemera.firstSelectedSelector,\n        lastSelectedSelector: ephemera.lastSelectedSelector\n      };\n    };\n    const SelectionAnnotation = {\n      byClass,\n      byAttr\n    };\n\n    const fold = (subject, onNone, onMultiple, onSingle) => {\n      switch (subject.tag) {\n      case 'none':\n        return onNone();\n      case 'single':\n        return onSingle(subject.element);\n      case 'multiple':\n        return onMultiple(subject.elements);\n      }\n    };\n    const none = () => ({ tag: 'none' });\n    const multiple = elements => ({\n      tag: 'multiple',\n      elements\n    });\n    const single = element => ({\n      tag: 'single',\n      element\n    });\n\n    const Selections = (lazyRoot, getStart, selectedSelector) => {\n      const get = () => retrieve(lazyRoot(), selectedSelector).fold(() => getStart().fold(none, single), multiple);\n      return { get };\n    };\n\n    const getUpOrLeftCells = (grid, selectedCells) => {\n      const upGrid = grid.slice(0, selectedCells[selectedCells.length - 1].row + 1);\n      const upDetails = toDetailList(upGrid);\n      return bind$2(upDetails, detail => {\n        const slicedCells = detail.cells.slice(0, selectedCells[selectedCells.length - 1].column + 1);\n        return map$1(slicedCells, cell => cell.element);\n      });\n    };\n    const getDownOrRightCells = (grid, selectedCells) => {\n      const downGrid = grid.slice(selectedCells[0].row + selectedCells[0].rowspan - 1, grid.length);\n      const downDetails = toDetailList(downGrid);\n      return bind$2(downDetails, detail => {\n        const slicedCells = detail.cells.slice(selectedCells[0].column + selectedCells[0].colspan - 1, detail.cells.length);\n        return map$1(slicedCells, cell => cell.element);\n      });\n    };\n    const getOtherCells = (table, target, generators) => {\n      const warehouse = Warehouse.fromTable(table);\n      const details = onCells(warehouse, target);\n      return details.map(selectedCells => {\n        const grid = toGrid(warehouse, generators, false);\n        const {rows} = extractGridDetails(grid);\n        const upOrLeftCells = getUpOrLeftCells(rows, selectedCells);\n        const downOrRightCells = getDownOrRightCells(rows, selectedCells);\n        return {\n          upOrLeftCells,\n          downOrRightCells\n        };\n      });\n    };\n\n    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({\n      target,\n      x,\n      y,\n      stop,\n      prevent,\n      kill,\n      raw\n    });\n    const fromRawEvent$1 = rawEvent => {\n      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));\n      const stop = () => rawEvent.stopPropagation();\n      const prevent = () => rawEvent.preventDefault();\n      const kill = compose(prevent, stop);\n      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);\n    };\n    const handle = (filter, handler) => rawEvent => {\n      if (filter(rawEvent)) {\n        handler(fromRawEvent$1(rawEvent));\n      }\n    };\n    const binder = (element, event, filter, handler, useCapture) => {\n      const wrapped = handle(filter, handler);\n      element.dom.addEventListener(event, wrapped, useCapture);\n      return { unbind: curry(unbind, element, event, wrapped, useCapture) };\n    };\n    const bind$1 = (element, event, filter, handler) => binder(element, event, filter, handler, false);\n    const unbind = (element, event, handler, useCapture) => {\n      element.dom.removeEventListener(event, handler, useCapture);\n    };\n\n    const filter = always;\n    const bind = (element, event, handler) => bind$1(element, event, filter, handler);\n    const fromRawEvent = fromRawEvent$1;\n\n    const hasInternalTarget = e => !has(SugarElement.fromDom(e.target), 'ephox-snooker-resizer-bar');\n    const TableCellSelectionHandler = (editor, resizeHandler) => {\n      const cellSelection = Selections(() => SugarElement.fromDom(editor.getBody()), () => getSelectionCell(getSelectionStart(editor), getIsRoot(editor)), ephemera.selectedSelector);\n      const onSelection = (cells, start, finish) => {\n        const tableOpt = table(start);\n        tableOpt.each(table => {\n          const cloneFormats = getTableCloneElements(editor);\n          const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), cloneFormats);\n          const selectedCells = getCellsFromSelection(editor);\n          const otherCells = getOtherCells(table, { selection: selectedCells }, generators);\n          fireTableSelectionChange(editor, cells, start, finish, otherCells);\n        });\n      };\n      const onClear = () => fireTableSelectionClear(editor);\n      const annotations = SelectionAnnotation.byAttr(ephemera, onSelection, onClear);\n      editor.on('init', _e => {\n        const win = editor.getWin();\n        const body = getBody(editor);\n        const isRoot = getIsRoot(editor);\n        const syncSelection = () => {\n          const sel = editor.selection;\n          const start = SugarElement.fromDom(sel.getStart());\n          const end = SugarElement.fromDom(sel.getEnd());\n          const shared = sharedOne(table, [\n            start,\n            end\n          ]);\n          shared.fold(() => annotations.clear(body), noop);\n        };\n        const mouseHandlers = mouse(win, body, isRoot, annotations);\n        const keyHandlers = keyboard(win, body, isRoot, annotations);\n        const external$1 = external(win, body, isRoot, annotations);\n        const hasShiftKey = event => event.raw.shiftKey === true;\n        editor.on('TableSelectorChange', e => external$1(e.start, e.finish));\n        const handleResponse = (event, response) => {\n          if (!hasShiftKey(event)) {\n            return;\n          }\n          if (response.kill) {\n            event.kill();\n          }\n          response.selection.each(ns => {\n            const relative = SimSelection.relative(ns.start, ns.finish);\n            const rng = asLtrRange(win, relative);\n            editor.selection.setRng(rng);\n          });\n        };\n        const keyup = event => {\n          const wrappedEvent = fromRawEvent(event);\n          if (wrappedEvent.raw.shiftKey && isNavigation(wrappedEvent.raw.which)) {\n            const rng = editor.selection.getRng();\n            const start = SugarElement.fromDom(rng.startContainer);\n            const end = SugarElement.fromDom(rng.endContainer);\n            keyHandlers.keyup(wrappedEvent, start, rng.startOffset, end, rng.endOffset).each(response => {\n              handleResponse(wrappedEvent, response);\n            });\n          }\n        };\n        const keydown = event => {\n          const wrappedEvent = fromRawEvent(event);\n          resizeHandler.hide();\n          const rng = editor.selection.getRng();\n          const start = SugarElement.fromDom(rng.startContainer);\n          const end = SugarElement.fromDom(rng.endContainer);\n          const direction = onDirection(ltr, rtl)(SugarElement.fromDom(editor.selection.getStart()));\n          keyHandlers.keydown(wrappedEvent, start, rng.startOffset, end, rng.endOffset, direction).each(response => {\n            handleResponse(wrappedEvent, response);\n          });\n          resizeHandler.show();\n        };\n        const isLeftMouse = raw => raw.button === 0;\n        const isLeftButtonPressed = raw => {\n          if (raw.buttons === undefined) {\n            return true;\n          }\n          return (raw.buttons & 1) !== 0;\n        };\n        const dragStart = _e => {\n          mouseHandlers.clearstate();\n        };\n        const mouseDown = e => {\n          if (isLeftMouse(e) && hasInternalTarget(e)) {\n            mouseHandlers.mousedown(fromRawEvent(e));\n          }\n        };\n        const mouseOver = e => {\n          if (isLeftButtonPressed(e) && hasInternalTarget(e)) {\n            mouseHandlers.mouseover(fromRawEvent(e));\n          }\n        };\n        const mouseUp = e => {\n          if (isLeftMouse(e) && hasInternalTarget(e)) {\n            mouseHandlers.mouseup(fromRawEvent(e));\n          }\n        };\n        const getDoubleTap = () => {\n          const lastTarget = Cell(SugarElement.fromDom(body));\n          const lastTimeStamp = Cell(0);\n          const touchEnd = t => {\n            const target = SugarElement.fromDom(t.target);\n            if (isTag('td')(target) || isTag('th')(target)) {\n              const lT = lastTarget.get();\n              const lTS = lastTimeStamp.get();\n              if (eq$1(lT, target) && t.timeStamp - lTS < 300) {\n                t.preventDefault();\n                external$1(target, target);\n              }\n            }\n            lastTarget.set(target);\n            lastTimeStamp.set(t.timeStamp);\n          };\n          return { touchEnd };\n        };\n        const doubleTap = getDoubleTap();\n        editor.on('dragstart', dragStart);\n        editor.on('mousedown', mouseDown);\n        editor.on('mouseover', mouseOver);\n        editor.on('mouseup', mouseUp);\n        editor.on('touchend', doubleTap.touchEnd);\n        editor.on('keyup', keyup);\n        editor.on('keydown', keydown);\n        editor.on('NodeChange', syncSelection);\n      });\n      editor.on('PreInit', () => {\n        editor.serializer.addTempAttr(ephemera.firstSelected);\n        editor.serializer.addTempAttr(ephemera.lastSelected);\n      });\n      const clearSelectedCells = container => annotations.clear(SugarElement.fromDom(container));\n      const getSelectedCells = () => fold(cellSelection.get(), constant([]), cells => {\n        return map$1(cells, cell => cell.dom);\n      }, cell => [cell.dom]);\n      return {\n        getSelectedCells,\n        clearSelectedCells\n      };\n    };\n\n    const Event = fields => {\n      let handlers = [];\n      const bind = handler => {\n        if (handler === undefined) {\n          throw new Error('Event bind error: undefined handler');\n        }\n        handlers.push(handler);\n      };\n      const unbind = handler => {\n        handlers = filter$2(handlers, h => {\n          return h !== handler;\n        });\n      };\n      const trigger = (...args) => {\n        const event = {};\n        each$2(fields, (name, i) => {\n          event[name] = args[i];\n        });\n        each$2(handlers, handler => {\n          handler(event);\n        });\n      };\n      return {\n        bind,\n        unbind,\n        trigger\n      };\n    };\n\n    const create$1 = typeDefs => {\n      const registry = map(typeDefs, event => {\n        return {\n          bind: event.bind,\n          unbind: event.unbind\n        };\n      });\n      const trigger = map(typeDefs, event => {\n        return event.trigger;\n      });\n      return {\n        registry,\n        trigger\n      };\n    };\n\n    const last = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        cancel();\n        timer = setTimeout(() => {\n          timer = null;\n          fn.apply(null, args);\n        }, rate);\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    const sort = arr => {\n      return arr.slice(0).sort();\n    };\n    const reqMessage = (required, keys) => {\n      throw new Error('All required keys (' + sort(required).join(', ') + ') were not specified. Specified keys were: ' + sort(keys).join(', ') + '.');\n    };\n    const unsuppMessage = unsupported => {\n      throw new Error('Unsupported keys for object: ' + sort(unsupported).join(', '));\n    };\n    const validateStrArr = (label, array) => {\n      if (!isArray(array)) {\n        throw new Error('The ' + label + ' fields must be an array. Was: ' + array + '.');\n      }\n      each$2(array, a => {\n        if (!isString(a)) {\n          throw new Error('The value ' + a + ' in the ' + label + ' fields was not a string.');\n        }\n      });\n    };\n    const invalidTypeMessage = (incorrect, type) => {\n      throw new Error('All values need to be of type: ' + type + '. Keys (' + sort(incorrect).join(', ') + ') were not.');\n    };\n    const checkDupes = everything => {\n      const sorted = sort(everything);\n      const dupe = find$1(sorted, (s, i) => {\n        return i < sorted.length - 1 && s === sorted[i + 1];\n      });\n      dupe.each(d => {\n        throw new Error('The field: ' + d + ' occurs more than once in the combined fields: [' + sorted.join(', ') + '].');\n      });\n    };\n\n    const base = (handleUnsupported, required) => {\n      return baseWith(handleUnsupported, required, {\n        validate: isFunction,\n        label: 'function'\n      });\n    };\n    const baseWith = (handleUnsupported, required, pred) => {\n      if (required.length === 0) {\n        throw new Error('You must specify at least one required field.');\n      }\n      validateStrArr('required', required);\n      checkDupes(required);\n      return obj => {\n        const keys$1 = keys(obj);\n        const allReqd = forall(required, req => {\n          return contains$2(keys$1, req);\n        });\n        if (!allReqd) {\n          reqMessage(required, keys$1);\n        }\n        handleUnsupported(required, keys$1);\n        const invalidKeys = filter$2(required, key => {\n          return !pred.validate(obj[key], key);\n        });\n        if (invalidKeys.length > 0) {\n          invalidTypeMessage(invalidKeys, pred.label);\n        }\n        return obj;\n      };\n    };\n    const handleExact = (required, keys) => {\n      const unsupported = filter$2(keys, key => {\n        return !contains$2(required, key);\n      });\n      if (unsupported.length > 0) {\n        unsuppMessage(unsupported);\n      }\n    };\n    const exactly = required => base(handleExact, required);\n\n    const DragMode = exactly([\n      'compare',\n      'extract',\n      'mutate',\n      'sink'\n    ]);\n    const DragSink = exactly([\n      'element',\n      'start',\n      'stop',\n      'destroy'\n    ]);\n    const DragApi = exactly([\n      'forceDrop',\n      'drop',\n      'move',\n      'delayDrop'\n    ]);\n\n    const InDrag = () => {\n      let previous = Optional.none();\n      const reset = () => {\n        previous = Optional.none();\n      };\n      const update = (mode, nu) => {\n        const result = previous.map(old => {\n          return mode.compare(old, nu);\n        });\n        previous = Optional.some(nu);\n        return result;\n      };\n      const onEvent = (event, mode) => {\n        const dataOption = mode.extract(event);\n        dataOption.each(data => {\n          const offset = update(mode, data);\n          offset.each(d => {\n            events.trigger.move(d);\n          });\n        });\n      };\n      const events = create$1({ move: Event(['info']) });\n      return {\n        onEvent,\n        reset,\n        events: events.registry\n      };\n    };\n\n    const NoDrag = () => {\n      const events = create$1({ move: Event(['info']) });\n      return {\n        onEvent: noop,\n        reset: noop,\n        events: events.registry\n      };\n    };\n\n    const Movement = () => {\n      const noDragState = NoDrag();\n      const inDragState = InDrag();\n      let dragState = noDragState;\n      const on = () => {\n        dragState.reset();\n        dragState = inDragState;\n      };\n      const off = () => {\n        dragState.reset();\n        dragState = noDragState;\n      };\n      const onEvent = (event, mode) => {\n        dragState.onEvent(event, mode);\n      };\n      const isOn = () => {\n        return dragState === inDragState;\n      };\n      return {\n        on,\n        off,\n        isOn,\n        onEvent,\n        events: inDragState.events\n      };\n    };\n\n    const setup = (mutation, mode, settings) => {\n      let active = false;\n      const events = create$1({\n        start: Event([]),\n        stop: Event([])\n      });\n      const movement = Movement();\n      const drop = () => {\n        sink.stop();\n        if (movement.isOn()) {\n          movement.off();\n          events.trigger.stop();\n        }\n      };\n      const throttledDrop = last(drop, 200);\n      const go = parent => {\n        sink.start(parent);\n        movement.on();\n        events.trigger.start();\n      };\n      const mousemove = event => {\n        throttledDrop.cancel();\n        movement.onEvent(event, mode);\n      };\n      movement.events.move.bind(event => {\n        mode.mutate(mutation, event.info);\n      });\n      const on = () => {\n        active = true;\n      };\n      const off = () => {\n        active = false;\n      };\n      const runIfActive = f => {\n        return (...args) => {\n          if (active) {\n            f.apply(null, args);\n          }\n        };\n      };\n      const sink = mode.sink(DragApi({\n        forceDrop: drop,\n        drop: runIfActive(drop),\n        move: runIfActive(mousemove),\n        delayDrop: runIfActive(throttledDrop.throttle)\n      }), settings);\n      const destroy = () => {\n        sink.destroy();\n      };\n      return {\n        element: sink.element,\n        go,\n        on,\n        off,\n        destroy,\n        events: events.registry\n      };\n    };\n\n    const css = namespace => {\n      const dashNamespace = namespace.replace(/\\./g, '-');\n      const resolve = str => {\n        return dashNamespace + '-' + str;\n      };\n      return { resolve };\n    };\n\n    const styles$1 = css('ephox-dragster');\n    const resolve$1 = styles$1.resolve;\n\n    const Blocker = options => {\n      const settings = {\n        layerClass: resolve$1('blocker'),\n        ...options\n      };\n      const div = SugarElement.fromTag('div');\n      set$2(div, 'role', 'presentation');\n      setAll(div, {\n        position: 'fixed',\n        left: '0px',\n        top: '0px',\n        width: '100%',\n        height: '100%'\n      });\n      add(div, resolve$1('blocker'));\n      add(div, settings.layerClass);\n      const element = constant(div);\n      const destroy = () => {\n        remove$6(div);\n      };\n      return {\n        element,\n        destroy\n      };\n    };\n\n    const compare = (old, nu) => {\n      return SugarPosition(nu.left - old.left, nu.top - old.top);\n    };\n    const extract = event => {\n      return Optional.some(SugarPosition(event.x, event.y));\n    };\n    const mutate = (mutation, info) => {\n      mutation.mutate(info.left, info.top);\n    };\n    const sink = (dragApi, settings) => {\n      const blocker = Blocker(settings);\n      const mdown = bind(blocker.element(), 'mousedown', dragApi.forceDrop);\n      const mup = bind(blocker.element(), 'mouseup', dragApi.drop);\n      const mmove = bind(blocker.element(), 'mousemove', dragApi.move);\n      const mout = bind(blocker.element(), 'mouseout', dragApi.delayDrop);\n      const destroy = () => {\n        blocker.destroy();\n        mup.unbind();\n        mmove.unbind();\n        mout.unbind();\n        mdown.unbind();\n      };\n      const start = parent => {\n        append$1(parent, blocker.element());\n      };\n      const stop = () => {\n        remove$6(blocker.element());\n      };\n      return DragSink({\n        element: blocker.element,\n        start,\n        stop,\n        destroy\n      });\n    };\n    var MouseDrag = DragMode({\n      compare,\n      extract,\n      sink,\n      mutate\n    });\n\n    const transform = (mutation, settings = {}) => {\n      var _a;\n      const mode = (_a = settings.mode) !== null && _a !== void 0 ? _a : MouseDrag;\n      return setup(mutation, mode, settings);\n    };\n\n    const styles = css('ephox-snooker');\n    const resolve = styles.resolve;\n\n    const Mutation = () => {\n      const events = create$1({\n        drag: Event([\n          'xDelta',\n          'yDelta'\n        ])\n      });\n      const mutate = (x, y) => {\n        events.trigger.drag(x, y);\n      };\n      return {\n        mutate,\n        events: events.registry\n      };\n    };\n\n    const BarMutation = () => {\n      const events = create$1({\n        drag: Event([\n          'xDelta',\n          'yDelta',\n          'target'\n        ])\n      });\n      let target = Optional.none();\n      const delegate = Mutation();\n      delegate.events.drag.bind(event => {\n        target.each(t => {\n          events.trigger.drag(event.xDelta, event.yDelta, t);\n        });\n      });\n      const assign = t => {\n        target = Optional.some(t);\n      };\n      const get = () => {\n        return target;\n      };\n      return {\n        assign,\n        get,\n        mutate: delegate.mutate,\n        events: events.registry\n      };\n    };\n\n    const col = (column, x, y, w, h) => {\n      const bar = SugarElement.fromTag('div');\n      setAll(bar, {\n        position: 'absolute',\n        left: x - w / 2 + 'px',\n        top: y + 'px',\n        height: h + 'px',\n        width: w + 'px'\n      });\n      setAll$1(bar, {\n        'data-column': column,\n        'role': 'presentation'\n      });\n      return bar;\n    };\n    const row = (r, x, y, w, h) => {\n      const bar = SugarElement.fromTag('div');\n      setAll(bar, {\n        position: 'absolute',\n        left: x + 'px',\n        top: y - h / 2 + 'px',\n        height: h + 'px',\n        width: w + 'px'\n      });\n      setAll$1(bar, {\n        'data-row': r,\n        'role': 'presentation'\n      });\n      return bar;\n    };\n\n    const resizeBar = resolve('resizer-bar');\n    const resizeRowBar = resolve('resizer-rows');\n    const resizeColBar = resolve('resizer-cols');\n    const BAR_THICKNESS = 7;\n    const resizableRows = (warehouse, isResizable) => bind$2(warehouse.all, (row, i) => isResizable(row.element) ? [i] : []);\n    const resizableColumns = (warehouse, isResizable) => {\n      const resizableCols = [];\n      range$1(warehouse.grid.columns, index => {\n        const colElmOpt = Warehouse.getColumnAt(warehouse, index).map(col => col.element);\n        if (colElmOpt.forall(isResizable)) {\n          resizableCols.push(index);\n        }\n      });\n      return filter$2(resizableCols, colIndex => {\n        const columnCells = Warehouse.filterItems(warehouse, cell => cell.column === colIndex);\n        return forall(columnCells, cell => isResizable(cell.element));\n      });\n    };\n    const destroy = wire => {\n      const previous = descendants(wire.parent(), '.' + resizeBar);\n      each$2(previous, remove$6);\n    };\n    const drawBar = (wire, positions, create) => {\n      const origin = wire.origin();\n      each$2(positions, cpOption => {\n        cpOption.each(cp => {\n          const bar = create(origin, cp);\n          add(bar, resizeBar);\n          append$1(wire.parent(), bar);\n        });\n      });\n    };\n    const refreshCol = (wire, colPositions, position, tableHeight) => {\n      drawBar(wire, colPositions, (origin, cp) => {\n        const colBar = col(cp.col, cp.x - origin.left, position.top - origin.top, BAR_THICKNESS, tableHeight);\n        add(colBar, resizeColBar);\n        return colBar;\n      });\n    };\n    const refreshRow = (wire, rowPositions, position, tableWidth) => {\n      drawBar(wire, rowPositions, (origin, cp) => {\n        const rowBar = row(cp.row, position.left - origin.left, cp.y - origin.top, tableWidth, BAR_THICKNESS);\n        add(rowBar, resizeRowBar);\n        return rowBar;\n      });\n    };\n    const refreshGrid = (warhouse, wire, table, rows, cols) => {\n      const position = absolute(table);\n      const isResizable = wire.isResizable;\n      const rowPositions = rows.length > 0 ? height.positions(rows, table) : [];\n      const resizableRowBars = rowPositions.length > 0 ? resizableRows(warhouse, isResizable) : [];\n      const resizableRowPositions = filter$2(rowPositions, (_pos, i) => exists(resizableRowBars, barIndex => i === barIndex));\n      refreshRow(wire, resizableRowPositions, position, getOuter$2(table));\n      const colPositions = cols.length > 0 ? width.positions(cols, table) : [];\n      const resizableColBars = colPositions.length > 0 ? resizableColumns(warhouse, isResizable) : [];\n      const resizableColPositions = filter$2(colPositions, (_pos, i) => exists(resizableColBars, barIndex => i === barIndex));\n      refreshCol(wire, resizableColPositions, position, getOuter$1(table));\n    };\n    const refresh = (wire, table) => {\n      destroy(wire);\n      if (wire.isResizable(table)) {\n        const warehouse = Warehouse.fromTable(table);\n        const rows$1 = rows(warehouse);\n        const cols = columns(warehouse);\n        refreshGrid(warehouse, wire, table, rows$1, cols);\n      }\n    };\n    const each = (wire, f) => {\n      const bars = descendants(wire.parent(), '.' + resizeBar);\n      each$2(bars, f);\n    };\n    const hide = wire => {\n      each(wire, bar => {\n        set$1(bar, 'display', 'none');\n      });\n    };\n    const show = wire => {\n      each(wire, bar => {\n        set$1(bar, 'display', 'block');\n      });\n    };\n    const isRowBar = element => {\n      return has(element, resizeRowBar);\n    };\n    const isColBar = element => {\n      return has(element, resizeColBar);\n    };\n\n    const resizeBarDragging = resolve('resizer-bar-dragging');\n    const BarManager = wire => {\n      const mutation = BarMutation();\n      const resizing = transform(mutation, {});\n      let hoverTable = Optional.none();\n      const getResizer = (element, type) => {\n        return Optional.from(get$b(element, type));\n      };\n      mutation.events.drag.bind(event => {\n        getResizer(event.target, 'data-row').each(_dataRow => {\n          const currentRow = getCssValue(event.target, 'top');\n          set$1(event.target, 'top', currentRow + event.yDelta + 'px');\n        });\n        getResizer(event.target, 'data-column').each(_dataCol => {\n          const currentCol = getCssValue(event.target, 'left');\n          set$1(event.target, 'left', currentCol + event.xDelta + 'px');\n        });\n      });\n      const getDelta = (target, dir) => {\n        const newX = getCssValue(target, dir);\n        const oldX = getAttrValue(target, 'data-initial-' + dir, 0);\n        return newX - oldX;\n      };\n      resizing.events.stop.bind(() => {\n        mutation.get().each(target => {\n          hoverTable.each(table => {\n            getResizer(target, 'data-row').each(row => {\n              const delta = getDelta(target, 'top');\n              remove$7(target, 'data-initial-top');\n              events.trigger.adjustHeight(table, delta, parseInt(row, 10));\n            });\n            getResizer(target, 'data-column').each(column => {\n              const delta = getDelta(target, 'left');\n              remove$7(target, 'data-initial-left');\n              events.trigger.adjustWidth(table, delta, parseInt(column, 10));\n            });\n            refresh(wire, table);\n          });\n        });\n      });\n      const handler = (target, dir) => {\n        events.trigger.startAdjust();\n        mutation.assign(target);\n        set$2(target, 'data-initial-' + dir, getCssValue(target, dir));\n        add(target, resizeBarDragging);\n        set$1(target, 'opacity', '0.2');\n        resizing.go(wire.parent());\n      };\n      const mousedown = bind(wire.parent(), 'mousedown', event => {\n        if (isRowBar(event.target)) {\n          handler(event.target, 'top');\n        }\n        if (isColBar(event.target)) {\n          handler(event.target, 'left');\n        }\n      });\n      const isRoot = e => {\n        return eq$1(e, wire.view());\n      };\n      const findClosestEditableTable = target => closest$1(target, 'table', isRoot).filter(isEditable$1);\n      const mouseover = bind(wire.view(), 'mouseover', event => {\n        findClosestEditableTable(event.target).fold(() => {\n          if (inBody(event.target)) {\n            destroy(wire);\n          }\n        }, table => {\n          hoverTable = Optional.some(table);\n          refresh(wire, table);\n        });\n      });\n      const destroy$1 = () => {\n        mousedown.unbind();\n        mouseover.unbind();\n        resizing.destroy();\n        destroy(wire);\n      };\n      const refresh$1 = tbl => {\n        refresh(wire, tbl);\n      };\n      const events = create$1({\n        adjustHeight: Event([\n          'table',\n          'delta',\n          'row'\n        ]),\n        adjustWidth: Event([\n          'table',\n          'delta',\n          'column'\n        ]),\n        startAdjust: Event([])\n      });\n      return {\n        destroy: destroy$1,\n        refresh: refresh$1,\n        on: resizing.on,\n        off: resizing.off,\n        hideBars: curry(hide, wire),\n        showBars: curry(show, wire),\n        events: events.registry\n      };\n    };\n\n    const create = (wire, resizing, lazySizing) => {\n      const hdirection = height;\n      const vdirection = width;\n      const manager = BarManager(wire);\n      const events = create$1({\n        beforeResize: Event([\n          'table',\n          'type'\n        ]),\n        afterResize: Event([\n          'table',\n          'type'\n        ]),\n        startDrag: Event([])\n      });\n      manager.events.adjustHeight.bind(event => {\n        const table = event.table;\n        events.trigger.beforeResize(table, 'row');\n        const delta = hdirection.delta(event.delta, table);\n        adjustHeight(table, delta, event.row, hdirection);\n        events.trigger.afterResize(table, 'row');\n      });\n      manager.events.startAdjust.bind(_event => {\n        events.trigger.startDrag();\n      });\n      manager.events.adjustWidth.bind(event => {\n        const table = event.table;\n        events.trigger.beforeResize(table, 'col');\n        const delta = vdirection.delta(event.delta, table);\n        const tableSize = lazySizing(table);\n        adjustWidth(table, delta, event.column, resizing, tableSize);\n        events.trigger.afterResize(table, 'col');\n      });\n      return {\n        on: manager.on,\n        off: manager.off,\n        refreshBars: manager.refresh,\n        hideBars: manager.hideBars,\n        showBars: manager.showBars,\n        destroy: manager.destroy,\n        events: events.registry\n      };\n    };\n    const TableResize = { create };\n\n    const only = (element, isResizable) => {\n      const parent = isDocument(element) ? documentElement(element) : element;\n      return {\n        parent: constant(parent),\n        view: constant(element),\n        origin: constant(SugarPosition(0, 0)),\n        isResizable\n      };\n    };\n    const detached = (editable, chrome, isResizable) => {\n      const origin = () => absolute(chrome);\n      return {\n        parent: constant(chrome),\n        view: constant(editable),\n        origin,\n        isResizable\n      };\n    };\n    const body = (editable, chrome, isResizable) => {\n      return {\n        parent: constant(chrome),\n        view: constant(editable),\n        origin: constant(SugarPosition(0, 0)),\n        isResizable\n      };\n    };\n    const ResizeWire = {\n      only,\n      detached,\n      body\n    };\n\n    const createContainer = () => {\n      const container = SugarElement.fromTag('div');\n      setAll(container, {\n        position: 'static',\n        height: '0',\n        width: '0',\n        padding: '0',\n        margin: '0',\n        border: '0'\n      });\n      append$1(body$1(), container);\n      return container;\n    };\n    const get = (editor, isResizable) => {\n      return editor.inline ? ResizeWire.body(SugarElement.fromDom(editor.getBody()), createContainer(), isResizable) : ResizeWire.only(SugarElement.fromDom(editor.getDoc()), isResizable);\n    };\n    const remove = (editor, wire) => {\n      if (editor.inline) {\n        remove$6(wire.parent());\n      }\n    };\n\n    const isTable = node => isNonNullable(node) && node.nodeName === 'TABLE';\n    const barResizerPrefix = 'bar-';\n    const isResizable = elm => get$b(elm, 'data-mce-resize') !== 'false';\n    const syncPixels = table => {\n      const warehouse = Warehouse.fromTable(table);\n      if (!Warehouse.hasColumns(warehouse)) {\n        each$2(cells$1(table), cell => {\n          const computedWidth = get$a(cell, 'width');\n          set$1(cell, 'width', computedWidth);\n          remove$7(cell, 'width');\n        });\n      }\n    };\n    const TableResizeHandler = editor => {\n      const selectionRng = value();\n      const tableResize = value();\n      const resizeWire = value();\n      let startW;\n      let startRawW;\n      const lazySizing = table => get$5(editor, table);\n      const lazyResizingBehaviour = () => isPreserveTableColumnResizing(editor) ? preserveTable() : resizeTable();\n      const getNumColumns = table => getGridSize(table).columns;\n      const afterCornerResize = (table, origin, width) => {\n        const isRightEdgeResize = endsWith(origin, 'e');\n        if (startRawW === '') {\n          convertToPercentSize(table);\n        }\n        if (width !== startW && startRawW !== '') {\n          set$1(table, 'width', startRawW);\n          const resizing = lazyResizingBehaviour();\n          const tableSize = lazySizing(table);\n          const col = isPreserveTableColumnResizing(editor) || isRightEdgeResize ? getNumColumns(table) - 1 : 0;\n          adjustWidth(table, width - startW, col, resizing, tableSize);\n        } else if (isPercentage$1(startRawW)) {\n          const percentW = parseFloat(startRawW.replace('%', ''));\n          const targetPercentW = width * percentW / startW;\n          set$1(table, 'width', targetPercentW + '%');\n        }\n        if (isPixel(startRawW)) {\n          syncPixels(table);\n        }\n      };\n      const destroy = () => {\n        tableResize.on(sz => {\n          sz.destroy();\n        });\n        resizeWire.on(w => {\n          remove(editor, w);\n        });\n      };\n      editor.on('init', () => {\n        const rawWire = get(editor, isResizable);\n        resizeWire.set(rawWire);\n        if (hasTableObjectResizing(editor) && hasTableResizeBars(editor)) {\n          const resizing = lazyResizingBehaviour();\n          const sz = TableResize.create(rawWire, resizing, lazySizing);\n          sz.on();\n          sz.events.startDrag.bind(_event => {\n            selectionRng.set(editor.selection.getRng());\n          });\n          sz.events.beforeResize.bind(event => {\n            const rawTable = event.table.dom;\n            fireObjectResizeStart(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type);\n          });\n          sz.events.afterResize.bind(event => {\n            const table = event.table;\n            const rawTable = table.dom;\n            removeDataStyle(table);\n            selectionRng.on(rng => {\n              editor.selection.setRng(rng);\n              editor.focus();\n            });\n            fireObjectResized(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type);\n            editor.undoManager.add();\n          });\n          tableResize.set(sz);\n        }\n      });\n      editor.on('ObjectResizeStart', e => {\n        const targetElm = e.target;\n        if (isTable(targetElm)) {\n          const table = SugarElement.fromDom(targetElm);\n          each$2(editor.dom.select('.mce-clonedresizable'), clone => {\n            editor.dom.addClass(clone, 'mce-' + getTableColumnResizingBehaviour(editor) + '-columns');\n          });\n          if (!isPixelSizing(table) && isTablePixelsForced(editor)) {\n            convertToPixelSize(table);\n          } else if (!isPercentSizing(table) && isTablePercentagesForced(editor)) {\n            convertToPercentSize(table);\n          }\n          if (isNoneSizing(table) && startsWith(e.origin, barResizerPrefix)) {\n            convertToPercentSize(table);\n          }\n          startW = e.width;\n          startRawW = isTableResponsiveForced(editor) ? '' : getRawWidth(editor, targetElm).getOr('');\n        }\n      });\n      editor.on('ObjectResized', e => {\n        const targetElm = e.target;\n        if (isTable(targetElm)) {\n          const table = SugarElement.fromDom(targetElm);\n          const origin = e.origin;\n          if (startsWith(origin, 'corner-')) {\n            afterCornerResize(table, origin, e.width);\n          }\n          removeDataStyle(table);\n          fireTableModified(editor, table.dom, styleModified);\n        }\n      });\n      editor.on('SwitchMode', () => {\n        tableResize.on(resize => {\n          if (editor.mode.isReadOnly()) {\n            resize.hideBars();\n          } else {\n            resize.showBars();\n          }\n        });\n      });\n      editor.on('remove', () => {\n        destroy();\n      });\n      const refresh = table => {\n        tableResize.on(resize => resize.refreshBars(SugarElement.fromDom(table)));\n      };\n      const hide = () => {\n        tableResize.on(resize => resize.hideBars());\n      };\n      const show = () => {\n        tableResize.on(resize => resize.showBars());\n      };\n      return {\n        refresh,\n        hide,\n        show\n      };\n    };\n\n    const setupTable = editor => {\n      register(editor);\n      const resizeHandler = TableResizeHandler(editor);\n      const cellSelectionHandler = TableCellSelectionHandler(editor, resizeHandler);\n      const actions = TableActions(editor, resizeHandler, cellSelectionHandler);\n      registerCommands(editor, actions);\n      registerQueryCommands(editor, actions);\n      registerEvents(editor, actions);\n      return {\n        getSelectedCells: cellSelectionHandler.getSelectedCells,\n        clearSelectedCells: cellSelectionHandler.clearSelectedCells\n      };\n    };\n\n    const DomModel = editor => {\n      const table = setupTable(editor);\n      return { table };\n    };\n    var Model = () => {\n      global$1.add('dom', DomModel);\n    };\n\n    Model();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/package.json",
    "content": "{\n\t\"name\": \"tinymce\",\n\t\"version\": \"6.3.2\",\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/tinymce/tinymce.git\",\n\t\t\"directory\": \"modules/tinymce\"\n\t},\n\t\"description\": \"Web based JavaScript HTML WYSIWYG editor control.\",\n\t\"author\": \"Ephox Corporation DBA Tiny Technologies, Inc\",\n\t\"main\": \"tinymce.js\",\n\t\"types\": \"tinymce.d.ts\",\n\t\"license\": \"MIT\",\n\t\"keywords\": [\n\t\t\"wysiwyg\",\n\t\t\"tinymce\",\n\t\t\"richtext\",\n\t\t\"javascript\",\n\t\t\"html\",\n\t\t\"text\",\n\t\t\"rich editor\",\n\t\t\"rich text editor\",\n\t\t\"rte\",\n\t\t\"rich text\",\n\t\t\"contenteditable\",\n\t\t\"editing\"\n\t],\n\t\"homepage\": \"https://www.tiny.cloud/\",\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/tinymce/tinymce/issues\"\n\t}\n}"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/advlist/index.js",
    "content": "// Exports the \"advlist\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/advlist')\n//   ES2015:\n//     import 'tinymce/plugins/advlist'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/advlist/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const applyListFormat = (editor, listName, styleValue) => {\n      const cmd = listName === 'UL' ? 'InsertUnorderedList' : 'InsertOrderedList';\n      editor.execCommand(cmd, false, styleValue === false ? null : { 'list-style-type': styleValue });\n    };\n\n    const register$2 = editor => {\n      editor.addCommand('ApplyUnorderedListStyle', (ui, value) => {\n        applyListFormat(editor, 'UL', value['list-style-type']);\n      });\n      editor.addCommand('ApplyOrderedListStyle', (ui, value) => {\n        applyListFormat(editor, 'OL', value['list-style-type']);\n      });\n    };\n\n    const option = name => editor => editor.options.get(name);\n    const register$1 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('advlist_number_styles', {\n        processor: 'string[]',\n        default: 'default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman'.split(',')\n      });\n      registerOption('advlist_bullet_styles', {\n        processor: 'string[]',\n        default: 'default,circle,square'.split(',')\n      });\n    };\n    const getNumberStyles = option('advlist_number_styles');\n    const getBulletStyles = option('advlist_bullet_styles');\n\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const isChildOfBody = (editor, elm) => {\n      return editor.dom.isChildOf(elm, editor.getBody());\n    };\n    const isTableCellNode = node => {\n      return isNonNullable(node) && /^(TH|TD)$/.test(node.nodeName);\n    };\n    const isListNode = editor => node => {\n      return isNonNullable(node) && /^(OL|UL|DL)$/.test(node.nodeName) && isChildOfBody(editor, node);\n    };\n    const getSelectedStyleType = editor => {\n      const listElm = editor.dom.getParent(editor.selection.getNode(), 'ol,ul');\n      const style = editor.dom.getStyle(listElm, 'listStyleType');\n      return Optional.from(style);\n    };\n    const isWithinNonEditable = (editor, element) => element !== null && editor.dom.getContentEditableParent(element) === 'false';\n    const isWithinNonEditableList = (editor, element) => {\n      const parentList = editor.dom.getParent(element, 'ol,ul,dl');\n      return isWithinNonEditable(editor, parentList);\n    };\n\n    const findIndex = (list, predicate) => {\n      for (let index = 0; index < list.length; index++) {\n        const element = list[index];\n        if (predicate(element)) {\n          return index;\n        }\n      }\n      return -1;\n    };\n    const styleValueToText = styleValue => {\n      return styleValue.replace(/\\-/g, ' ').replace(/\\b\\w/g, chr => {\n        return chr.toUpperCase();\n      });\n    };\n    const normalizeStyleValue = styleValue => isNullable(styleValue) || styleValue === 'default' ? '' : styleValue;\n    const isWithinList = (editor, e, nodeName) => {\n      const tableCellIndex = findIndex(e.parents, isTableCellNode);\n      const parents = tableCellIndex !== -1 ? e.parents.slice(0, tableCellIndex) : e.parents;\n      const lists = global.grep(parents, isListNode(editor));\n      return lists.length > 0 && lists[0].nodeName === nodeName;\n    };\n    const makeSetupHandler = (editor, nodeName) => api => {\n      const nodeChangeHandler = e => {\n        api.setActive(isWithinList(editor, e, nodeName));\n        api.setEnabled(!isWithinNonEditableList(editor, e.element));\n      };\n      editor.on('NodeChange', nodeChangeHandler);\n      return () => editor.off('NodeChange', nodeChangeHandler);\n    };\n    const addSplitButton = (editor, id, tooltip, cmd, nodeName, styles) => {\n      editor.ui.registry.addSplitButton(id, {\n        tooltip,\n        icon: nodeName === 'OL' ? 'ordered-list' : 'unordered-list',\n        presets: 'listpreview',\n        columns: 3,\n        fetch: callback => {\n          const items = global.map(styles, styleValue => {\n            const iconStyle = nodeName === 'OL' ? 'num' : 'bull';\n            const iconName = styleValue === 'disc' || styleValue === 'decimal' ? 'default' : styleValue;\n            const itemValue = normalizeStyleValue(styleValue);\n            const displayText = styleValueToText(styleValue);\n            return {\n              type: 'choiceitem',\n              value: itemValue,\n              icon: 'list-' + iconStyle + '-' + iconName,\n              text: displayText\n            };\n          });\n          callback(items);\n        },\n        onAction: () => editor.execCommand(cmd),\n        onItemAction: (_splitButtonApi, value) => {\n          applyListFormat(editor, nodeName, value);\n        },\n        select: value => {\n          const listStyleType = getSelectedStyleType(editor);\n          return listStyleType.map(listStyle => value === listStyle).getOr(false);\n        },\n        onSetup: makeSetupHandler(editor, nodeName)\n      });\n    };\n    const addButton = (editor, id, tooltip, cmd, nodeName, styleValue) => {\n      editor.ui.registry.addToggleButton(id, {\n        active: false,\n        tooltip,\n        icon: nodeName === 'OL' ? 'ordered-list' : 'unordered-list',\n        onSetup: makeSetupHandler(editor, nodeName),\n        onAction: () => editor.queryCommandState(cmd) || styleValue === '' ? editor.execCommand(cmd) : applyListFormat(editor, nodeName, styleValue)\n      });\n    };\n    const addControl = (editor, id, tooltip, cmd, nodeName, styles) => {\n      if (styles.length > 1) {\n        addSplitButton(editor, id, tooltip, cmd, nodeName, styles);\n      } else {\n        addButton(editor, id, tooltip, cmd, nodeName, normalizeStyleValue(styles[0]));\n      }\n    };\n    const register = editor => {\n      addControl(editor, 'numlist', 'Numbered list', 'InsertOrderedList', 'OL', getNumberStyles(editor));\n      addControl(editor, 'bullist', 'Bullet list', 'InsertUnorderedList', 'UL', getBulletStyles(editor));\n    };\n\n    var Plugin = () => {\n      global$1.add('advlist', editor => {\n        if (editor.hasPlugin('lists')) {\n          register$1(editor);\n          register(editor);\n          register$2(editor);\n        } else {\n          console.error('Please use the Lists plugin together with the Advanced List plugin.');\n        }\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/anchor/index.js",
    "content": "// Exports the \"anchor\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/anchor')\n//   ES2015:\n//     import 'tinymce/plugins/anchor'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/anchor/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.dom.RangeUtils');\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('allow_html_in_named_anchor', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const allowHtmlInNamedAnchor = option('allow_html_in_named_anchor');\n\n    const namedAnchorSelector = 'a:not([href])';\n    const isEmptyString = str => !str;\n    const getIdFromAnchor = elm => {\n      const id = elm.getAttribute('id') || elm.getAttribute('name');\n      return id || '';\n    };\n    const isAnchor = elm => elm.nodeName.toLowerCase() === 'a';\n    const isNamedAnchor = elm => isAnchor(elm) && !elm.getAttribute('href') && getIdFromAnchor(elm) !== '';\n    const isEmptyNamedAnchor = elm => isNamedAnchor(elm) && !elm.firstChild;\n\n    const removeEmptyNamedAnchorsInSelection = editor => {\n      const dom = editor.dom;\n      global$1(dom).walk(editor.selection.getRng(), nodes => {\n        global.each(nodes, node => {\n          if (isEmptyNamedAnchor(node)) {\n            dom.remove(node, false);\n          }\n        });\n      });\n    };\n    const isValidId = id => /^[A-Za-z][A-Za-z0-9\\-:._]*$/.test(id);\n    const getNamedAnchor = editor => editor.dom.getParent(editor.selection.getStart(), namedAnchorSelector);\n    const getId = editor => {\n      const anchor = getNamedAnchor(editor);\n      if (anchor) {\n        return getIdFromAnchor(anchor);\n      } else {\n        return '';\n      }\n    };\n    const createAnchor = (editor, id) => {\n      editor.undoManager.transact(() => {\n        if (!allowHtmlInNamedAnchor(editor)) {\n          editor.selection.collapse(true);\n        }\n        if (editor.selection.isCollapsed()) {\n          editor.insertContent(editor.dom.createHTML('a', { id }));\n        } else {\n          removeEmptyNamedAnchorsInSelection(editor);\n          editor.formatter.remove('namedAnchor', undefined, undefined, true);\n          editor.formatter.apply('namedAnchor', { value: id });\n          editor.addVisual();\n        }\n      });\n    };\n    const updateAnchor = (editor, id, anchorElement) => {\n      anchorElement.removeAttribute('name');\n      anchorElement.id = id;\n      editor.addVisual();\n      editor.undoManager.add();\n    };\n    const insert = (editor, id) => {\n      const anchor = getNamedAnchor(editor);\n      if (anchor) {\n        updateAnchor(editor, id, anchor);\n      } else {\n        createAnchor(editor, id);\n      }\n      editor.focus();\n    };\n\n    const insertAnchor = (editor, newId) => {\n      if (!isValidId(newId)) {\n        editor.windowManager.alert('ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.');\n        return false;\n      } else {\n        insert(editor, newId);\n        return true;\n      }\n    };\n    const open = editor => {\n      const currentId = getId(editor);\n      editor.windowManager.open({\n        title: 'Anchor',\n        size: 'normal',\n        body: {\n          type: 'panel',\n          items: [{\n              name: 'id',\n              type: 'input',\n              label: 'ID',\n              placeholder: 'example'\n            }]\n        },\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData: { id: currentId },\n        onSubmit: api => {\n          if (insertAnchor(editor, api.getData().id)) {\n            api.close();\n          }\n        }\n      });\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mceAnchor', () => {\n        open(editor);\n      });\n    };\n\n    const isNamedAnchorNode = node => isEmptyString(node.attr('href')) && !isEmptyString(node.attr('id') || node.attr('name'));\n    const isEmptyNamedAnchorNode = node => isNamedAnchorNode(node) && !node.firstChild;\n    const setContentEditable = state => nodes => {\n      for (let i = 0; i < nodes.length; i++) {\n        const node = nodes[i];\n        if (isEmptyNamedAnchorNode(node)) {\n          node.attr('contenteditable', state);\n        }\n      }\n    };\n    const setup = editor => {\n      editor.on('PreInit', () => {\n        editor.parser.addNodeFilter('a', setContentEditable('false'));\n        editor.serializer.addNodeFilter('a', setContentEditable(null));\n      });\n    };\n\n    const registerFormats = editor => {\n      editor.formatter.register('namedAnchor', {\n        inline: 'a',\n        selector: namedAnchorSelector,\n        remove: 'all',\n        split: true,\n        deep: true,\n        attributes: { id: '%value' },\n        onmatch: (node, _fmt, _itemName) => {\n          return isNamedAnchor(node);\n        }\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceAnchor');\n      editor.ui.registry.addToggleButton('anchor', {\n        icon: 'bookmark',\n        tooltip: 'Anchor',\n        onAction,\n        onSetup: buttonApi => editor.selection.selectorChangedWithUnbind('a:not([href])', buttonApi.setActive).unbind\n      });\n      editor.ui.registry.addMenuItem('anchor', {\n        icon: 'bookmark',\n        text: 'Anchor...',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$2.add('anchor', editor => {\n        register$2(editor);\n        setup(editor);\n        register$1(editor);\n        register(editor);\n        editor.on('PreInit', () => {\n          registerFormats(editor);\n        });\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/autolink/index.js",
    "content": "// Exports the \"autolink\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/autolink')\n//   ES2015:\n//     import 'tinymce/plugins/autolink'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/autolink/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n  'use strict';\n\n  var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n  const link = () => /(?:[A-Za-z][A-Za-z\\d.+-]{0,14}:\\/\\/(?:[-.~*+=!&;:'%@?^${}(),\\w]+@)?|www\\.|[-;:&=+$,.\\w]+@)[A-Za-z\\d-]+(?:\\.[A-Za-z\\d-]+)*(?::\\d+)?(?:\\/(?:[-.~*+=!;:'%@$(),\\/\\w]*[-~*+=%@$()\\/\\w])?)?(?:\\?(?:[-.~*+=!&;:'%@?^${}(),\\/\\w]+))?(?:#(?:[-.~*+=!&;:'%@?^${}(),\\/\\w]+))?/g;\n\n  const option = name => editor => editor.options.get(name);\n  const register = editor => {\n    const registerOption = editor.options.register;\n    registerOption('autolink_pattern', {\n      processor: 'regexp',\n      default: new RegExp('^' + link().source + '$', 'i')\n    });\n    registerOption('link_default_target', { processor: 'string' });\n    registerOption('link_default_protocol', {\n      processor: 'string',\n      default: 'https'\n    });\n  };\n  const getAutoLinkPattern = option('autolink_pattern');\n  const getDefaultLinkTarget = option('link_default_target');\n  const getDefaultLinkProtocol = option('link_default_protocol');\n  const allowUnsafeLinkTarget = option('allow_unsafe_link_target');\n\n  const hasProto = (v, constructor, predicate) => {\n    var _a;\n    if (predicate(v, constructor.prototype)) {\n      return true;\n    } else {\n      return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n    }\n  };\n  const typeOf = x => {\n    const t = typeof x;\n    if (x === null) {\n      return 'null';\n    } else if (t === 'object' && Array.isArray(x)) {\n      return 'array';\n    } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n      return 'string';\n    } else {\n      return t;\n    }\n  };\n  const isType = type => value => typeOf(value) === type;\n  const eq = t => a => t === a;\n  const isString = isType('string');\n  const isUndefined = eq(undefined);\n  const isNullable = a => a === null || a === undefined;\n  const isNonNullable = a => !isNullable(a);\n\n  const not = f => t => !f(t);\n\n  const hasOwnProperty = Object.hasOwnProperty;\n  const has = (obj, key) => hasOwnProperty.call(obj, key);\n\n  const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;\n  const contains = (str, substr, start = 0, end) => {\n    const idx = str.indexOf(substr, start);\n    if (idx !== -1) {\n      return isUndefined(end) ? true : idx + substr.length <= end;\n    } else {\n      return false;\n    }\n  };\n  const startsWith = (str, prefix) => {\n    return checkRange(str, prefix, 0);\n  };\n\n  const zeroWidth = '\\uFEFF';\n  const isZwsp = char => char === zeroWidth;\n  const removeZwsp = s => s.replace(/\\uFEFF/g, '');\n\n  var global = tinymce.util.Tools.resolve('tinymce.dom.TextSeeker');\n\n  const isTextNode = node => node.nodeType === 3;\n  const isElement = node => node.nodeType === 1;\n  const isBracketOrSpace = char => /^[(\\[{ \\u00a0]$/.test(char);\n  const hasProtocol = url => /^([A-Za-z][A-Za-z\\d.+-]*:\\/\\/)|mailto:/.test(url);\n  const isPunctuation = char => /[?!,.;:]/.test(char);\n  const findChar = (text, index, predicate) => {\n    for (let i = index - 1; i >= 0; i--) {\n      const char = text.charAt(i);\n      if (!isZwsp(char) && predicate(char)) {\n        return i;\n      }\n    }\n    return -1;\n  };\n  const freefallRtl = (container, offset) => {\n    let tempNode = container;\n    let tempOffset = offset;\n    while (isElement(tempNode) && tempNode.childNodes[tempOffset]) {\n      tempNode = tempNode.childNodes[tempOffset];\n      tempOffset = isTextNode(tempNode) ? tempNode.data.length : tempNode.childNodes.length;\n    }\n    return {\n      container: tempNode,\n      offset: tempOffset\n    };\n  };\n\n  const parseCurrentLine = (editor, offset) => {\n    var _a;\n    const voidElements = editor.schema.getVoidElements();\n    const autoLinkPattern = getAutoLinkPattern(editor);\n    const {dom, selection} = editor;\n    if (dom.getParent(selection.getNode(), 'a[href]') !== null) {\n      return null;\n    }\n    const rng = selection.getRng();\n    const textSeeker = global(dom, node => {\n      return dom.isBlock(node) || has(voidElements, node.nodeName.toLowerCase()) || dom.getContentEditable(node) === 'false';\n    });\n    const {\n      container: endContainer,\n      offset: endOffset\n    } = freefallRtl(rng.endContainer, rng.endOffset);\n    const root = (_a = dom.getParent(endContainer, dom.isBlock)) !== null && _a !== void 0 ? _a : dom.getRoot();\n    const endSpot = textSeeker.backwards(endContainer, endOffset + offset, (node, offset) => {\n      const text = node.data;\n      const idx = findChar(text, offset, not(isBracketOrSpace));\n      return idx === -1 || isPunctuation(text[idx]) ? idx : idx + 1;\n    }, root);\n    if (!endSpot) {\n      return null;\n    }\n    let lastTextNode = endSpot.container;\n    const startSpot = textSeeker.backwards(endSpot.container, endSpot.offset, (node, offset) => {\n      lastTextNode = node;\n      const idx = findChar(node.data, offset, isBracketOrSpace);\n      return idx === -1 ? idx : idx + 1;\n    }, root);\n    const newRng = dom.createRng();\n    if (!startSpot) {\n      newRng.setStart(lastTextNode, 0);\n    } else {\n      newRng.setStart(startSpot.container, startSpot.offset);\n    }\n    newRng.setEnd(endSpot.container, endSpot.offset);\n    const rngText = removeZwsp(newRng.toString());\n    const matches = rngText.match(autoLinkPattern);\n    if (matches) {\n      let url = matches[0];\n      if (startsWith(url, 'www.')) {\n        const protocol = getDefaultLinkProtocol(editor);\n        url = protocol + '://' + url;\n      } else if (contains(url, '@') && !hasProtocol(url)) {\n        url = 'mailto:' + url;\n      }\n      return {\n        rng: newRng,\n        url\n      };\n    } else {\n      return null;\n    }\n  };\n  const convertToLink = (editor, result) => {\n    const {dom, selection} = editor;\n    const {rng, url} = result;\n    const bookmark = selection.getBookmark();\n    selection.setRng(rng);\n    const command = 'createlink';\n    const args = {\n      command,\n      ui: false,\n      value: url\n    };\n    const beforeExecEvent = editor.dispatch('BeforeExecCommand', args);\n    if (!beforeExecEvent.isDefaultPrevented()) {\n      editor.getDoc().execCommand(command, false, url);\n      editor.dispatch('ExecCommand', args);\n      const defaultLinkTarget = getDefaultLinkTarget(editor);\n      if (isString(defaultLinkTarget)) {\n        const anchor = selection.getNode();\n        dom.setAttrib(anchor, 'target', defaultLinkTarget);\n        if (defaultLinkTarget === '_blank' && !allowUnsafeLinkTarget(editor)) {\n          dom.setAttrib(anchor, 'rel', 'noopener');\n        }\n      }\n    }\n    selection.moveToBookmark(bookmark);\n    editor.nodeChanged();\n  };\n  const handleSpacebar = editor => {\n    const result = parseCurrentLine(editor, -1);\n    if (isNonNullable(result)) {\n      convertToLink(editor, result);\n    }\n  };\n  const handleBracket = handleSpacebar;\n  const handleEnter = editor => {\n    const result = parseCurrentLine(editor, 0);\n    if (isNonNullable(result)) {\n      convertToLink(editor, result);\n    }\n  };\n  const setup = editor => {\n    editor.on('keydown', e => {\n      if (e.keyCode === 13 && !e.isDefaultPrevented()) {\n        handleEnter(editor);\n      }\n    });\n    editor.on('keyup', e => {\n      if (e.keyCode === 32) {\n        handleSpacebar(editor);\n      } else if (e.keyCode === 48 && e.shiftKey || e.keyCode === 221) {\n        handleBracket(editor);\n      }\n    });\n  };\n\n  var Plugin = () => {\n    global$1.add('autolink', editor => {\n      register(editor);\n      setup(editor);\n    });\n  };\n\n  Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/autoresize/index.js",
    "content": "// Exports the \"autoresize\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/autoresize')\n//   ES2015:\n//     import 'tinymce/plugins/autoresize'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/autoresize/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    var global = tinymce.util.Tools.resolve('tinymce.Env');\n\n    const fireResizeEditor = editor => editor.dispatch('ResizeEditor');\n\n    const option = name => editor => editor.options.get(name);\n    const register$1 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('autoresize_overflow_padding', {\n        processor: 'number',\n        default: 1\n      });\n      registerOption('autoresize_bottom_margin', {\n        processor: 'number',\n        default: 50\n      });\n    };\n    const getMinHeight = option('min_height');\n    const getMaxHeight = option('max_height');\n    const getAutoResizeOverflowPadding = option('autoresize_overflow_padding');\n    const getAutoResizeBottomMargin = option('autoresize_bottom_margin');\n\n    const isFullscreen = editor => editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen();\n    const toggleScrolling = (editor, state) => {\n      const body = editor.getBody();\n      if (body) {\n        body.style.overflowY = state ? '' : 'hidden';\n        if (!state) {\n          body.scrollTop = 0;\n        }\n      }\n    };\n    const parseCssValueToInt = (dom, elm, name, computed) => {\n      var _a;\n      const value = parseInt((_a = dom.getStyle(elm, name, computed)) !== null && _a !== void 0 ? _a : '', 10);\n      return isNaN(value) ? 0 : value;\n    };\n    const shouldScrollIntoView = trigger => {\n      if ((trigger === null || trigger === void 0 ? void 0 : trigger.type.toLowerCase()) === 'setcontent') {\n        const setContentEvent = trigger;\n        return setContentEvent.selection === true || setContentEvent.paste === true;\n      } else {\n        return false;\n      }\n    };\n    const resize = (editor, oldSize, trigger) => {\n      var _a;\n      const dom = editor.dom;\n      const doc = editor.getDoc();\n      if (!doc) {\n        return;\n      }\n      if (isFullscreen(editor)) {\n        toggleScrolling(editor, true);\n        return;\n      }\n      const docEle = doc.documentElement;\n      const resizeBottomMargin = getAutoResizeBottomMargin(editor);\n      const minHeight = (_a = getMinHeight(editor)) !== null && _a !== void 0 ? _a : editor.getElement().offsetHeight;\n      let resizeHeight = minHeight;\n      const marginTop = parseCssValueToInt(dom, docEle, 'margin-top', true);\n      const marginBottom = parseCssValueToInt(dom, docEle, 'margin-bottom', true);\n      let contentHeight = docEle.offsetHeight + marginTop + marginBottom + resizeBottomMargin;\n      if (contentHeight < 0) {\n        contentHeight = 0;\n      }\n      const containerHeight = editor.getContainer().offsetHeight;\n      const contentAreaHeight = editor.getContentAreaContainer().offsetHeight;\n      const chromeHeight = containerHeight - contentAreaHeight;\n      if (contentHeight + chromeHeight > minHeight) {\n        resizeHeight = contentHeight + chromeHeight;\n      }\n      const maxHeight = getMaxHeight(editor);\n      if (maxHeight && resizeHeight > maxHeight) {\n        resizeHeight = maxHeight;\n        toggleScrolling(editor, true);\n      } else {\n        toggleScrolling(editor, false);\n      }\n      if (resizeHeight !== oldSize.get()) {\n        const deltaSize = resizeHeight - oldSize.get();\n        dom.setStyle(editor.getContainer(), 'height', resizeHeight + 'px');\n        oldSize.set(resizeHeight);\n        fireResizeEditor(editor);\n        if (global.browser.isSafari() && (global.os.isMacOS() || global.os.isiOS())) {\n          const win = editor.getWin();\n          win.scrollTo(win.pageXOffset, win.pageYOffset);\n        }\n        if (editor.hasFocus() && shouldScrollIntoView(trigger)) {\n          editor.selection.scrollIntoView();\n        }\n        if ((global.browser.isSafari() || global.browser.isChromium()) && deltaSize < 0) {\n          resize(editor, oldSize, trigger);\n        }\n      }\n    };\n    const setup = (editor, oldSize) => {\n      editor.on('init', () => {\n        const overflowPadding = getAutoResizeOverflowPadding(editor);\n        const dom = editor.dom;\n        dom.setStyles(editor.getDoc().documentElement, { height: 'auto' });\n        dom.setStyles(editor.getBody(), {\n          'paddingLeft': overflowPadding,\n          'paddingRight': overflowPadding,\n          'min-height': 0\n        });\n      });\n      editor.on('NodeChange SetContent keyup FullscreenStateChanged ResizeContent', e => {\n        resize(editor, oldSize, e);\n      });\n    };\n\n    const register = (editor, oldSize) => {\n      editor.addCommand('mceAutoResize', () => {\n        resize(editor, oldSize);\n      });\n    };\n\n    var Plugin = () => {\n      global$1.add('autoresize', editor => {\n        register$1(editor);\n        if (!editor.options.isSet('resize')) {\n          editor.options.set('resize', false);\n        }\n        if (!editor.inline) {\n          const oldSize = Cell(0);\n          register(editor, oldSize);\n          setup(editor, oldSize);\n        }\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/autosave/index.js",
    "content": "// Exports the \"autosave\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/autosave')\n//   ES2015:\n//     import 'tinymce/plugins/autosave'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/autosave/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const eq = t => a => t === a;\n    const isString = isType('string');\n    const isUndefined = eq(undefined);\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.util.Delay');\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.util.LocalStorage');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const fireRestoreDraft = editor => editor.dispatch('RestoreDraft');\n    const fireStoreDraft = editor => editor.dispatch('StoreDraft');\n    const fireRemoveDraft = editor => editor.dispatch('RemoveDraft');\n\n    const parse = timeString => {\n      const multiples = {\n        s: 1000,\n        m: 60000\n      };\n      const parsedTime = /^(\\d+)([ms]?)$/.exec(timeString);\n      return (parsedTime && parsedTime[2] ? multiples[parsedTime[2]] : 1) * parseInt(timeString, 10);\n    };\n\n    const option = name => editor => editor.options.get(name);\n    const register$1 = editor => {\n      const registerOption = editor.options.register;\n      const timeProcessor = value => {\n        const valid = isString(value);\n        if (valid) {\n          return {\n            value: parse(value),\n            valid\n          };\n        } else {\n          return {\n            valid: false,\n            message: 'Must be a string.'\n          };\n        }\n      };\n      registerOption('autosave_ask_before_unload', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('autosave_prefix', {\n        processor: 'string',\n        default: 'tinymce-autosave-{path}{query}{hash}-{id}-'\n      });\n      registerOption('autosave_restore_when_empty', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('autosave_interval', {\n        processor: timeProcessor,\n        default: '30s'\n      });\n      registerOption('autosave_retention', {\n        processor: timeProcessor,\n        default: '20m'\n      });\n    };\n    const shouldAskBeforeUnload = option('autosave_ask_before_unload');\n    const shouldRestoreWhenEmpty = option('autosave_restore_when_empty');\n    const getAutoSaveInterval = option('autosave_interval');\n    const getAutoSaveRetention = option('autosave_retention');\n    const getAutoSavePrefix = editor => {\n      const location = document.location;\n      return editor.options.get('autosave_prefix').replace(/{path}/g, location.pathname).replace(/{query}/g, location.search).replace(/{hash}/g, location.hash).replace(/{id}/g, editor.id);\n    };\n\n    const isEmpty = (editor, html) => {\n      if (isUndefined(html)) {\n        return editor.dom.isEmpty(editor.getBody());\n      } else {\n        const trimmedHtml = global$1.trim(html);\n        if (trimmedHtml === '') {\n          return true;\n        } else {\n          const fragment = new DOMParser().parseFromString(trimmedHtml, 'text/html');\n          return editor.dom.isEmpty(fragment);\n        }\n      }\n    };\n    const hasDraft = editor => {\n      var _a;\n      const time = parseInt((_a = global$2.getItem(getAutoSavePrefix(editor) + 'time')) !== null && _a !== void 0 ? _a : '0', 10) || 0;\n      if (new Date().getTime() - time > getAutoSaveRetention(editor)) {\n        removeDraft(editor, false);\n        return false;\n      }\n      return true;\n    };\n    const removeDraft = (editor, fire) => {\n      const prefix = getAutoSavePrefix(editor);\n      global$2.removeItem(prefix + 'draft');\n      global$2.removeItem(prefix + 'time');\n      if (fire !== false) {\n        fireRemoveDraft(editor);\n      }\n    };\n    const storeDraft = editor => {\n      const prefix = getAutoSavePrefix(editor);\n      if (!isEmpty(editor) && editor.isDirty()) {\n        global$2.setItem(prefix + 'draft', editor.getContent({\n          format: 'raw',\n          no_events: true\n        }));\n        global$2.setItem(prefix + 'time', new Date().getTime().toString());\n        fireStoreDraft(editor);\n      }\n    };\n    const restoreDraft = editor => {\n      var _a;\n      const prefix = getAutoSavePrefix(editor);\n      if (hasDraft(editor)) {\n        editor.setContent((_a = global$2.getItem(prefix + 'draft')) !== null && _a !== void 0 ? _a : '', { format: 'raw' });\n        fireRestoreDraft(editor);\n      }\n    };\n    const startStoreDraft = editor => {\n      const interval = getAutoSaveInterval(editor);\n      global$3.setEditorInterval(editor, () => {\n        storeDraft(editor);\n      }, interval);\n    };\n    const restoreLastDraft = editor => {\n      editor.undoManager.transact(() => {\n        restoreDraft(editor);\n        removeDraft(editor);\n      });\n      editor.focus();\n    };\n\n    const get = editor => ({\n      hasDraft: () => hasDraft(editor),\n      storeDraft: () => storeDraft(editor),\n      restoreDraft: () => restoreDraft(editor),\n      removeDraft: fire => removeDraft(editor, fire),\n      isEmpty: html => isEmpty(editor, html)\n    });\n\n    var global = tinymce.util.Tools.resolve('tinymce.EditorManager');\n\n    const setup = editor => {\n      editor.editorManager.on('BeforeUnload', e => {\n        let msg;\n        global$1.each(global.get(), editor => {\n          if (editor.plugins.autosave) {\n            editor.plugins.autosave.storeDraft();\n          }\n          if (!msg && editor.isDirty() && shouldAskBeforeUnload(editor)) {\n            msg = editor.translate('You have unsaved changes are you sure you want to navigate away?');\n          }\n        });\n        if (msg) {\n          e.preventDefault();\n          e.returnValue = msg;\n        }\n      });\n    };\n\n    const makeSetupHandler = editor => api => {\n      api.setEnabled(hasDraft(editor));\n      const editorEventCallback = () => api.setEnabled(hasDraft(editor));\n      editor.on('StoreDraft RestoreDraft RemoveDraft', editorEventCallback);\n      return () => editor.off('StoreDraft RestoreDraft RemoveDraft', editorEventCallback);\n    };\n    const register = editor => {\n      startStoreDraft(editor);\n      const onAction = () => {\n        restoreLastDraft(editor);\n      };\n      editor.ui.registry.addButton('restoredraft', {\n        tooltip: 'Restore last draft',\n        icon: 'restore-draft',\n        onAction,\n        onSetup: makeSetupHandler(editor)\n      });\n      editor.ui.registry.addMenuItem('restoredraft', {\n        text: 'Restore last draft',\n        icon: 'restore-draft',\n        onAction,\n        onSetup: makeSetupHandler(editor)\n      });\n    };\n\n    var Plugin = () => {\n      global$4.add('autosave', editor => {\n        register$1(editor);\n        setup(editor);\n        register(editor);\n        editor.on('init', () => {\n          if (shouldRestoreWhenEmpty(editor) && editor.dom.isEmpty(editor.getBody())) {\n            restoreDraft(editor);\n          }\n        });\n        return get(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/charmap/index.js",
    "content": "// Exports the \"charmap\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/charmap')\n//   ES2015:\n//     import 'tinymce/plugins/charmap'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/charmap/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const fireInsertCustomChar = (editor, chr) => {\n      return editor.dispatch('insertCustomChar', { chr });\n    };\n\n    const insertChar = (editor, chr) => {\n      const evtChr = fireInsertCustomChar(editor, chr).chr;\n      editor.execCommand('mceInsertContent', false, evtChr);\n    };\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq = t => a => t === a;\n    const isArray$1 = isType('array');\n    const isNull = eq(null);\n    const isUndefined = eq(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const never = constant(false);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativePush = Array.prototype.push;\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray$1(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind = (xs, f) => flatten(map(xs, f));\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      const charMapProcessor = value => isFunction(value) || isArray$1(value);\n      registerOption('charmap', { processor: charMapProcessor });\n      registerOption('charmap_append', { processor: charMapProcessor });\n    };\n    const getCharMap$1 = option('charmap');\n    const getCharMapAppend = option('charmap_append');\n\n    const isArray = global.isArray;\n    const UserDefined = 'User Defined';\n    const getDefaultCharMap = () => {\n      return [\n        {\n          name: 'Currency',\n          characters: [\n            [\n              36,\n              'dollar sign'\n            ],\n            [\n              162,\n              'cent sign'\n            ],\n            [\n              8364,\n              'euro sign'\n            ],\n            [\n              163,\n              'pound sign'\n            ],\n            [\n              165,\n              'yen sign'\n            ],\n            [\n              164,\n              'currency sign'\n            ],\n            [\n              8352,\n              'euro-currency sign'\n            ],\n            [\n              8353,\n              'colon sign'\n            ],\n            [\n              8354,\n              'cruzeiro sign'\n            ],\n            [\n              8355,\n              'french franc sign'\n            ],\n            [\n              8356,\n              'lira sign'\n            ],\n            [\n              8357,\n              'mill sign'\n            ],\n            [\n              8358,\n              'naira sign'\n            ],\n            [\n              8359,\n              'peseta sign'\n            ],\n            [\n              8360,\n              'rupee sign'\n            ],\n            [\n              8361,\n              'won sign'\n            ],\n            [\n              8362,\n              'new sheqel sign'\n            ],\n            [\n              8363,\n              'dong sign'\n            ],\n            [\n              8365,\n              'kip sign'\n            ],\n            [\n              8366,\n              'tugrik sign'\n            ],\n            [\n              8367,\n              'drachma sign'\n            ],\n            [\n              8368,\n              'german penny symbol'\n            ],\n            [\n              8369,\n              'peso sign'\n            ],\n            [\n              8370,\n              'guarani sign'\n            ],\n            [\n              8371,\n              'austral sign'\n            ],\n            [\n              8372,\n              'hryvnia sign'\n            ],\n            [\n              8373,\n              'cedi sign'\n            ],\n            [\n              8374,\n              'livre tournois sign'\n            ],\n            [\n              8375,\n              'spesmilo sign'\n            ],\n            [\n              8376,\n              'tenge sign'\n            ],\n            [\n              8377,\n              'indian rupee sign'\n            ],\n            [\n              8378,\n              'turkish lira sign'\n            ],\n            [\n              8379,\n              'nordic mark sign'\n            ],\n            [\n              8380,\n              'manat sign'\n            ],\n            [\n              8381,\n              'ruble sign'\n            ],\n            [\n              20870,\n              'yen character'\n            ],\n            [\n              20803,\n              'yuan character'\n            ],\n            [\n              22291,\n              'yuan character, in hong kong and taiwan'\n            ],\n            [\n              22278,\n              'yen/yuan character variant one'\n            ]\n          ]\n        },\n        {\n          name: 'Text',\n          characters: [\n            [\n              169,\n              'copyright sign'\n            ],\n            [\n              174,\n              'registered sign'\n            ],\n            [\n              8482,\n              'trade mark sign'\n            ],\n            [\n              8240,\n              'per mille sign'\n            ],\n            [\n              181,\n              'micro sign'\n            ],\n            [\n              183,\n              'middle dot'\n            ],\n            [\n              8226,\n              'bullet'\n            ],\n            [\n              8230,\n              'three dot leader'\n            ],\n            [\n              8242,\n              'minutes / feet'\n            ],\n            [\n              8243,\n              'seconds / inches'\n            ],\n            [\n              167,\n              'section sign'\n            ],\n            [\n              182,\n              'paragraph sign'\n            ],\n            [\n              223,\n              'sharp s / ess-zed'\n            ]\n          ]\n        },\n        {\n          name: 'Quotations',\n          characters: [\n            [\n              8249,\n              'single left-pointing angle quotation mark'\n            ],\n            [\n              8250,\n              'single right-pointing angle quotation mark'\n            ],\n            [\n              171,\n              'left pointing guillemet'\n            ],\n            [\n              187,\n              'right pointing guillemet'\n            ],\n            [\n              8216,\n              'left single quotation mark'\n            ],\n            [\n              8217,\n              'right single quotation mark'\n            ],\n            [\n              8220,\n              'left double quotation mark'\n            ],\n            [\n              8221,\n              'right double quotation mark'\n            ],\n            [\n              8218,\n              'single low-9 quotation mark'\n            ],\n            [\n              8222,\n              'double low-9 quotation mark'\n            ],\n            [\n              60,\n              'less-than sign'\n            ],\n            [\n              62,\n              'greater-than sign'\n            ],\n            [\n              8804,\n              'less-than or equal to'\n            ],\n            [\n              8805,\n              'greater-than or equal to'\n            ],\n            [\n              8211,\n              'en dash'\n            ],\n            [\n              8212,\n              'em dash'\n            ],\n            [\n              175,\n              'macron'\n            ],\n            [\n              8254,\n              'overline'\n            ],\n            [\n              164,\n              'currency sign'\n            ],\n            [\n              166,\n              'broken bar'\n            ],\n            [\n              168,\n              'diaeresis'\n            ],\n            [\n              161,\n              'inverted exclamation mark'\n            ],\n            [\n              191,\n              'turned question mark'\n            ],\n            [\n              710,\n              'circumflex accent'\n            ],\n            [\n              732,\n              'small tilde'\n            ],\n            [\n              176,\n              'degree sign'\n            ],\n            [\n              8722,\n              'minus sign'\n            ],\n            [\n              177,\n              'plus-minus sign'\n            ],\n            [\n              247,\n              'division sign'\n            ],\n            [\n              8260,\n              'fraction slash'\n            ],\n            [\n              215,\n              'multiplication sign'\n            ],\n            [\n              185,\n              'superscript one'\n            ],\n            [\n              178,\n              'superscript two'\n            ],\n            [\n              179,\n              'superscript three'\n            ],\n            [\n              188,\n              'fraction one quarter'\n            ],\n            [\n              189,\n              'fraction one half'\n            ],\n            [\n              190,\n              'fraction three quarters'\n            ]\n          ]\n        },\n        {\n          name: 'Mathematical',\n          characters: [\n            [\n              402,\n              'function / florin'\n            ],\n            [\n              8747,\n              'integral'\n            ],\n            [\n              8721,\n              'n-ary sumation'\n            ],\n            [\n              8734,\n              'infinity'\n            ],\n            [\n              8730,\n              'square root'\n            ],\n            [\n              8764,\n              'similar to'\n            ],\n            [\n              8773,\n              'approximately equal to'\n            ],\n            [\n              8776,\n              'almost equal to'\n            ],\n            [\n              8800,\n              'not equal to'\n            ],\n            [\n              8801,\n              'identical to'\n            ],\n            [\n              8712,\n              'element of'\n            ],\n            [\n              8713,\n              'not an element of'\n            ],\n            [\n              8715,\n              'contains as member'\n            ],\n            [\n              8719,\n              'n-ary product'\n            ],\n            [\n              8743,\n              'logical and'\n            ],\n            [\n              8744,\n              'logical or'\n            ],\n            [\n              172,\n              'not sign'\n            ],\n            [\n              8745,\n              'intersection'\n            ],\n            [\n              8746,\n              'union'\n            ],\n            [\n              8706,\n              'partial differential'\n            ],\n            [\n              8704,\n              'for all'\n            ],\n            [\n              8707,\n              'there exists'\n            ],\n            [\n              8709,\n              'diameter'\n            ],\n            [\n              8711,\n              'backward difference'\n            ],\n            [\n              8727,\n              'asterisk operator'\n            ],\n            [\n              8733,\n              'proportional to'\n            ],\n            [\n              8736,\n              'angle'\n            ]\n          ]\n        },\n        {\n          name: 'Extended Latin',\n          characters: [\n            [\n              192,\n              'A - grave'\n            ],\n            [\n              193,\n              'A - acute'\n            ],\n            [\n              194,\n              'A - circumflex'\n            ],\n            [\n              195,\n              'A - tilde'\n            ],\n            [\n              196,\n              'A - diaeresis'\n            ],\n            [\n              197,\n              'A - ring above'\n            ],\n            [\n              256,\n              'A - macron'\n            ],\n            [\n              198,\n              'ligature AE'\n            ],\n            [\n              199,\n              'C - cedilla'\n            ],\n            [\n              200,\n              'E - grave'\n            ],\n            [\n              201,\n              'E - acute'\n            ],\n            [\n              202,\n              'E - circumflex'\n            ],\n            [\n              203,\n              'E - diaeresis'\n            ],\n            [\n              274,\n              'E - macron'\n            ],\n            [\n              204,\n              'I - grave'\n            ],\n            [\n              205,\n              'I - acute'\n            ],\n            [\n              206,\n              'I - circumflex'\n            ],\n            [\n              207,\n              'I - diaeresis'\n            ],\n            [\n              298,\n              'I - macron'\n            ],\n            [\n              208,\n              'ETH'\n            ],\n            [\n              209,\n              'N - tilde'\n            ],\n            [\n              210,\n              'O - grave'\n            ],\n            [\n              211,\n              'O - acute'\n            ],\n            [\n              212,\n              'O - circumflex'\n            ],\n            [\n              213,\n              'O - tilde'\n            ],\n            [\n              214,\n              'O - diaeresis'\n            ],\n            [\n              216,\n              'O - slash'\n            ],\n            [\n              332,\n              'O - macron'\n            ],\n            [\n              338,\n              'ligature OE'\n            ],\n            [\n              352,\n              'S - caron'\n            ],\n            [\n              217,\n              'U - grave'\n            ],\n            [\n              218,\n              'U - acute'\n            ],\n            [\n              219,\n              'U - circumflex'\n            ],\n            [\n              220,\n              'U - diaeresis'\n            ],\n            [\n              362,\n              'U - macron'\n            ],\n            [\n              221,\n              'Y - acute'\n            ],\n            [\n              376,\n              'Y - diaeresis'\n            ],\n            [\n              562,\n              'Y - macron'\n            ],\n            [\n              222,\n              'THORN'\n            ],\n            [\n              224,\n              'a - grave'\n            ],\n            [\n              225,\n              'a - acute'\n            ],\n            [\n              226,\n              'a - circumflex'\n            ],\n            [\n              227,\n              'a - tilde'\n            ],\n            [\n              228,\n              'a - diaeresis'\n            ],\n            [\n              229,\n              'a - ring above'\n            ],\n            [\n              257,\n              'a - macron'\n            ],\n            [\n              230,\n              'ligature ae'\n            ],\n            [\n              231,\n              'c - cedilla'\n            ],\n            [\n              232,\n              'e - grave'\n            ],\n            [\n              233,\n              'e - acute'\n            ],\n            [\n              234,\n              'e - circumflex'\n            ],\n            [\n              235,\n              'e - diaeresis'\n            ],\n            [\n              275,\n              'e - macron'\n            ],\n            [\n              236,\n              'i - grave'\n            ],\n            [\n              237,\n              'i - acute'\n            ],\n            [\n              238,\n              'i - circumflex'\n            ],\n            [\n              239,\n              'i - diaeresis'\n            ],\n            [\n              299,\n              'i - macron'\n            ],\n            [\n              240,\n              'eth'\n            ],\n            [\n              241,\n              'n - tilde'\n            ],\n            [\n              242,\n              'o - grave'\n            ],\n            [\n              243,\n              'o - acute'\n            ],\n            [\n              244,\n              'o - circumflex'\n            ],\n            [\n              245,\n              'o - tilde'\n            ],\n            [\n              246,\n              'o - diaeresis'\n            ],\n            [\n              248,\n              'o slash'\n            ],\n            [\n              333,\n              'o macron'\n            ],\n            [\n              339,\n              'ligature oe'\n            ],\n            [\n              353,\n              's - caron'\n            ],\n            [\n              249,\n              'u - grave'\n            ],\n            [\n              250,\n              'u - acute'\n            ],\n            [\n              251,\n              'u - circumflex'\n            ],\n            [\n              252,\n              'u - diaeresis'\n            ],\n            [\n              363,\n              'u - macron'\n            ],\n            [\n              253,\n              'y - acute'\n            ],\n            [\n              254,\n              'thorn'\n            ],\n            [\n              255,\n              'y - diaeresis'\n            ],\n            [\n              563,\n              'y - macron'\n            ],\n            [\n              913,\n              'Alpha'\n            ],\n            [\n              914,\n              'Beta'\n            ],\n            [\n              915,\n              'Gamma'\n            ],\n            [\n              916,\n              'Delta'\n            ],\n            [\n              917,\n              'Epsilon'\n            ],\n            [\n              918,\n              'Zeta'\n            ],\n            [\n              919,\n              'Eta'\n            ],\n            [\n              920,\n              'Theta'\n            ],\n            [\n              921,\n              'Iota'\n            ],\n            [\n              922,\n              'Kappa'\n            ],\n            [\n              923,\n              'Lambda'\n            ],\n            [\n              924,\n              'Mu'\n            ],\n            [\n              925,\n              'Nu'\n            ],\n            [\n              926,\n              'Xi'\n            ],\n            [\n              927,\n              'Omicron'\n            ],\n            [\n              928,\n              'Pi'\n            ],\n            [\n              929,\n              'Rho'\n            ],\n            [\n              931,\n              'Sigma'\n            ],\n            [\n              932,\n              'Tau'\n            ],\n            [\n              933,\n              'Upsilon'\n            ],\n            [\n              934,\n              'Phi'\n            ],\n            [\n              935,\n              'Chi'\n            ],\n            [\n              936,\n              'Psi'\n            ],\n            [\n              937,\n              'Omega'\n            ],\n            [\n              945,\n              'alpha'\n            ],\n            [\n              946,\n              'beta'\n            ],\n            [\n              947,\n              'gamma'\n            ],\n            [\n              948,\n              'delta'\n            ],\n            [\n              949,\n              'epsilon'\n            ],\n            [\n              950,\n              'zeta'\n            ],\n            [\n              951,\n              'eta'\n            ],\n            [\n              952,\n              'theta'\n            ],\n            [\n              953,\n              'iota'\n            ],\n            [\n              954,\n              'kappa'\n            ],\n            [\n              955,\n              'lambda'\n            ],\n            [\n              956,\n              'mu'\n            ],\n            [\n              957,\n              'nu'\n            ],\n            [\n              958,\n              'xi'\n            ],\n            [\n              959,\n              'omicron'\n            ],\n            [\n              960,\n              'pi'\n            ],\n            [\n              961,\n              'rho'\n            ],\n            [\n              962,\n              'final sigma'\n            ],\n            [\n              963,\n              'sigma'\n            ],\n            [\n              964,\n              'tau'\n            ],\n            [\n              965,\n              'upsilon'\n            ],\n            [\n              966,\n              'phi'\n            ],\n            [\n              967,\n              'chi'\n            ],\n            [\n              968,\n              'psi'\n            ],\n            [\n              969,\n              'omega'\n            ]\n          ]\n        },\n        {\n          name: 'Symbols',\n          characters: [\n            [\n              8501,\n              'alef symbol'\n            ],\n            [\n              982,\n              'pi symbol'\n            ],\n            [\n              8476,\n              'real part symbol'\n            ],\n            [\n              978,\n              'upsilon - hook symbol'\n            ],\n            [\n              8472,\n              'Weierstrass p'\n            ],\n            [\n              8465,\n              'imaginary part'\n            ]\n          ]\n        },\n        {\n          name: 'Arrows',\n          characters: [\n            [\n              8592,\n              'leftwards arrow'\n            ],\n            [\n              8593,\n              'upwards arrow'\n            ],\n            [\n              8594,\n              'rightwards arrow'\n            ],\n            [\n              8595,\n              'downwards arrow'\n            ],\n            [\n              8596,\n              'left right arrow'\n            ],\n            [\n              8629,\n              'carriage return'\n            ],\n            [\n              8656,\n              'leftwards double arrow'\n            ],\n            [\n              8657,\n              'upwards double arrow'\n            ],\n            [\n              8658,\n              'rightwards double arrow'\n            ],\n            [\n              8659,\n              'downwards double arrow'\n            ],\n            [\n              8660,\n              'left right double arrow'\n            ],\n            [\n              8756,\n              'therefore'\n            ],\n            [\n              8834,\n              'subset of'\n            ],\n            [\n              8835,\n              'superset of'\n            ],\n            [\n              8836,\n              'not a subset of'\n            ],\n            [\n              8838,\n              'subset of or equal to'\n            ],\n            [\n              8839,\n              'superset of or equal to'\n            ],\n            [\n              8853,\n              'circled plus'\n            ],\n            [\n              8855,\n              'circled times'\n            ],\n            [\n              8869,\n              'perpendicular'\n            ],\n            [\n              8901,\n              'dot operator'\n            ],\n            [\n              8968,\n              'left ceiling'\n            ],\n            [\n              8969,\n              'right ceiling'\n            ],\n            [\n              8970,\n              'left floor'\n            ],\n            [\n              8971,\n              'right floor'\n            ],\n            [\n              9001,\n              'left-pointing angle bracket'\n            ],\n            [\n              9002,\n              'right-pointing angle bracket'\n            ],\n            [\n              9674,\n              'lozenge'\n            ],\n            [\n              9824,\n              'black spade suit'\n            ],\n            [\n              9827,\n              'black club suit'\n            ],\n            [\n              9829,\n              'black heart suit'\n            ],\n            [\n              9830,\n              'black diamond suit'\n            ],\n            [\n              8194,\n              'en space'\n            ],\n            [\n              8195,\n              'em space'\n            ],\n            [\n              8201,\n              'thin space'\n            ],\n            [\n              8204,\n              'zero width non-joiner'\n            ],\n            [\n              8205,\n              'zero width joiner'\n            ],\n            [\n              8206,\n              'left-to-right mark'\n            ],\n            [\n              8207,\n              'right-to-left mark'\n            ]\n          ]\n        }\n      ];\n    };\n    const charmapFilter = charmap => {\n      return global.grep(charmap, item => {\n        return isArray(item) && item.length === 2;\n      });\n    };\n    const getCharsFromOption = optionValue => {\n      if (isArray(optionValue)) {\n        return charmapFilter(optionValue);\n      }\n      if (typeof optionValue === 'function') {\n        return optionValue();\n      }\n      return [];\n    };\n    const extendCharMap = (editor, charmap) => {\n      const userCharMap = getCharMap$1(editor);\n      if (userCharMap) {\n        charmap = [{\n            name: UserDefined,\n            characters: getCharsFromOption(userCharMap)\n          }];\n      }\n      const userCharMapAppend = getCharMapAppend(editor);\n      if (userCharMapAppend) {\n        const userDefinedGroup = global.grep(charmap, cg => cg.name === UserDefined);\n        if (userDefinedGroup.length) {\n          userDefinedGroup[0].characters = [\n            ...userDefinedGroup[0].characters,\n            ...getCharsFromOption(userCharMapAppend)\n          ];\n          return charmap;\n        }\n        return charmap.concat({\n          name: UserDefined,\n          characters: getCharsFromOption(userCharMapAppend)\n        });\n      }\n      return charmap;\n    };\n    const getCharMap = editor => {\n      const groups = extendCharMap(editor, getDefaultCharMap());\n      return groups.length > 1 ? [{\n          name: 'All',\n          characters: bind(groups, g => g.characters)\n        }].concat(groups) : groups;\n    };\n\n    const get = editor => {\n      const getCharMap$1 = () => {\n        return getCharMap(editor);\n      };\n      const insertChar$1 = chr => {\n        insertChar(editor, chr);\n      };\n      return {\n        getCharMap: getCharMap$1,\n        insertChar: insertChar$1\n      };\n    };\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const last = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        cancel();\n        timer = setTimeout(() => {\n          timer = null;\n          fn.apply(null, args);\n        }, rate);\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    const contains = (str, substr, start = 0, end) => {\n      const idx = str.indexOf(substr, start);\n      if (idx !== -1) {\n        return isUndefined(end) ? true : idx + substr.length <= end;\n      } else {\n        return false;\n      }\n    };\n    const fromCodePoint = String.fromCodePoint;\n\n    const charMatches = (charCode, name, lowerCasePattern) => {\n      if (contains(fromCodePoint(charCode).toLowerCase(), lowerCasePattern)) {\n        return true;\n      } else {\n        return contains(name.toLowerCase(), lowerCasePattern) || contains(name.toLowerCase().replace(/\\s+/g, ''), lowerCasePattern);\n      }\n    };\n    const scan = (group, pattern) => {\n      const matches = [];\n      const lowerCasePattern = pattern.toLowerCase();\n      each(group.characters, g => {\n        if (charMatches(g[0], g[1], lowerCasePattern)) {\n          matches.push(g);\n        }\n      });\n      return map(matches, m => ({\n        text: m[1],\n        value: fromCodePoint(m[0]),\n        icon: fromCodePoint(m[0])\n      }));\n    };\n\n    const patternName = 'pattern';\n    const open = (editor, charMap) => {\n      const makeGroupItems = () => [\n        {\n          label: 'Search',\n          type: 'input',\n          name: patternName\n        },\n        {\n          type: 'collection',\n          name: 'results'\n        }\n      ];\n      const makeTabs = () => map(charMap, charGroup => ({\n        title: charGroup.name,\n        name: charGroup.name,\n        items: makeGroupItems()\n      }));\n      const makePanel = () => ({\n        type: 'panel',\n        items: makeGroupItems()\n      });\n      const makeTabPanel = () => ({\n        type: 'tabpanel',\n        tabs: makeTabs()\n      });\n      const currentTab = charMap.length === 1 ? Cell(UserDefined) : Cell('All');\n      const scanAndSet = (dialogApi, pattern) => {\n        find(charMap, group => group.name === currentTab.get()).each(f => {\n          const items = scan(f, pattern);\n          dialogApi.setData({ results: items });\n        });\n      };\n      const SEARCH_DELAY = 40;\n      const updateFilter = last(dialogApi => {\n        const pattern = dialogApi.getData().pattern;\n        scanAndSet(dialogApi, pattern);\n      }, SEARCH_DELAY);\n      const body = charMap.length === 1 ? makePanel() : makeTabPanel();\n      const initialData = {\n        pattern: '',\n        results: scan(charMap[0], '')\n      };\n      const bridgeSpec = {\n        title: 'Special Character',\n        size: 'normal',\n        body,\n        buttons: [{\n            type: 'cancel',\n            name: 'close',\n            text: 'Close',\n            primary: true\n          }],\n        initialData,\n        onAction: (api, details) => {\n          if (details.name === 'results') {\n            insertChar(editor, details.value);\n            api.close();\n          }\n        },\n        onTabChange: (dialogApi, details) => {\n          currentTab.set(details.newTabName);\n          updateFilter.throttle(dialogApi);\n        },\n        onChange: (dialogApi, changeData) => {\n          if (changeData.name === patternName) {\n            updateFilter.throttle(dialogApi);\n          }\n        }\n      };\n      const dialogApi = editor.windowManager.open(bridgeSpec);\n      dialogApi.focus(patternName);\n    };\n\n    const register$1 = (editor, charMap) => {\n      editor.addCommand('mceShowCharmap', () => {\n        open(editor, charMap);\n      });\n    };\n\n    const init = (editor, all) => {\n      editor.ui.registry.addAutocompleter('charmap', {\n        trigger: ':',\n        columns: 'auto',\n        minChars: 2,\n        fetch: (pattern, _maxResults) => new Promise((resolve, _reject) => {\n          resolve(scan(all, pattern));\n        }),\n        onAction: (autocompleteApi, rng, value) => {\n          editor.selection.setRng(rng);\n          editor.insertContent(value);\n          autocompleteApi.hide();\n        }\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceShowCharmap');\n      editor.ui.registry.addButton('charmap', {\n        icon: 'insert-character',\n        tooltip: 'Special character',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('charmap', {\n        icon: 'insert-character',\n        text: 'Special character...',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$1.add('charmap', editor => {\n        register$2(editor);\n        const charMap = getCharMap(editor);\n        register$1(editor, charMap);\n        register(editor);\n        init(editor, charMap[0]);\n        return get(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/code/index.js",
    "content": "// Exports the \"code\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/code')\n//   ES2015:\n//     import 'tinymce/plugins/code'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/code/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const setContent = (editor, html) => {\n      editor.focus();\n      editor.undoManager.transact(() => {\n        editor.setContent(html);\n      });\n      editor.selection.setCursorLocation();\n      editor.nodeChanged();\n    };\n    const getContent = editor => {\n      return editor.getContent({ source_view: true });\n    };\n\n    const open = editor => {\n      const editorContent = getContent(editor);\n      editor.windowManager.open({\n        title: 'Source Code',\n        size: 'large',\n        body: {\n          type: 'panel',\n          items: [{\n              type: 'textarea',\n              name: 'code'\n            }]\n        },\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData: { code: editorContent },\n        onSubmit: api => {\n          setContent(editor, api.getData().code);\n          api.close();\n        }\n      });\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mceCodeEditor', () => {\n        open(editor);\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceCodeEditor');\n      editor.ui.registry.addButton('code', {\n        icon: 'sourcecode',\n        tooltip: 'Source code',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('code', {\n        icon: 'sourcecode',\n        text: 'Source code',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global.add('code', editor => {\n        register$1(editor);\n        register(editor);\n        return {};\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/codesample/index.js",
    "content": "// Exports the \"codesample\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/codesample')\n//   ES2015:\n//     import 'tinymce/plugins/codesample'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/codesample/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const get$1 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get$1(xs, 0);\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    const Global = typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const prismjs = function (global, module, exports) {\n      const oldprism = window.Prism;\n      window.Prism = { manual: true };\n      var _self = typeof window !== 'undefined' ? window : typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope ? self : {};\n      var Prism = function (_self) {\n        var lang = /(?:^|\\s)lang(?:uage)?-([\\w-]+)(?=\\s|$)/i;\n        var uniqueId = 0;\n        var plainTextGrammar = {};\n        var _ = {\n          manual: _self.Prism && _self.Prism.manual,\n          disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,\n          util: {\n            encode: function encode(tokens) {\n              if (tokens instanceof Token) {\n                return new Token(tokens.type, encode(tokens.content), tokens.alias);\n              } else if (Array.isArray(tokens)) {\n                return tokens.map(encode);\n              } else {\n                return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\\u00a0/g, ' ');\n              }\n            },\n            type: function (o) {\n              return Object.prototype.toString.call(o).slice(8, -1);\n            },\n            objId: function (obj) {\n              if (!obj['__id']) {\n                Object.defineProperty(obj, '__id', { value: ++uniqueId });\n              }\n              return obj['__id'];\n            },\n            clone: function deepClone(o, visited) {\n              visited = visited || {};\n              var clone;\n              var id;\n              switch (_.util.type(o)) {\n              case 'Object':\n                id = _.util.objId(o);\n                if (visited[id]) {\n                  return visited[id];\n                }\n                clone = {};\n                visited[id] = clone;\n                for (var key in o) {\n                  if (o.hasOwnProperty(key)) {\n                    clone[key] = deepClone(o[key], visited);\n                  }\n                }\n                return clone;\n              case 'Array':\n                id = _.util.objId(o);\n                if (visited[id]) {\n                  return visited[id];\n                }\n                clone = [];\n                visited[id] = clone;\n                o.forEach(function (v, i) {\n                  clone[i] = deepClone(v, visited);\n                });\n                return clone;\n              default:\n                return o;\n              }\n            },\n            getLanguage: function (element) {\n              while (element) {\n                var m = lang.exec(element.className);\n                if (m) {\n                  return m[1].toLowerCase();\n                }\n                element = element.parentElement;\n              }\n              return 'none';\n            },\n            setLanguage: function (element, language) {\n              element.className = element.className.replace(RegExp(lang, 'gi'), '');\n              element.classList.add('language-' + language);\n            },\n            currentScript: function () {\n              if (typeof document === 'undefined') {\n                return null;\n              }\n              if ('currentScript' in document && 1 < 2) {\n                return document.currentScript;\n              }\n              try {\n                throw new Error();\n              } catch (err) {\n                var src = (/at [^(\\r\\n]*\\((.*):[^:]+:[^:]+\\)$/i.exec(err.stack) || [])[1];\n                if (src) {\n                  var scripts = document.getElementsByTagName('script');\n                  for (var i in scripts) {\n                    if (scripts[i].src == src) {\n                      return scripts[i];\n                    }\n                  }\n                }\n                return null;\n              }\n            },\n            isActive: function (element, className, defaultActivation) {\n              var no = 'no-' + className;\n              while (element) {\n                var classList = element.classList;\n                if (classList.contains(className)) {\n                  return true;\n                }\n                if (classList.contains(no)) {\n                  return false;\n                }\n                element = element.parentElement;\n              }\n              return !!defaultActivation;\n            }\n          },\n          languages: {\n            plain: plainTextGrammar,\n            plaintext: plainTextGrammar,\n            text: plainTextGrammar,\n            txt: plainTextGrammar,\n            extend: function (id, redef) {\n              var lang = _.util.clone(_.languages[id]);\n              for (var key in redef) {\n                lang[key] = redef[key];\n              }\n              return lang;\n            },\n            insertBefore: function (inside, before, insert, root) {\n              root = root || _.languages;\n              var grammar = root[inside];\n              var ret = {};\n              for (var token in grammar) {\n                if (grammar.hasOwnProperty(token)) {\n                  if (token == before) {\n                    for (var newToken in insert) {\n                      if (insert.hasOwnProperty(newToken)) {\n                        ret[newToken] = insert[newToken];\n                      }\n                    }\n                  }\n                  if (!insert.hasOwnProperty(token)) {\n                    ret[token] = grammar[token];\n                  }\n                }\n              }\n              var old = root[inside];\n              root[inside] = ret;\n              _.languages.DFS(_.languages, function (key, value) {\n                if (value === old && key != inside) {\n                  this[key] = ret;\n                }\n              });\n              return ret;\n            },\n            DFS: function DFS(o, callback, type, visited) {\n              visited = visited || {};\n              var objId = _.util.objId;\n              for (var i in o) {\n                if (o.hasOwnProperty(i)) {\n                  callback.call(o, i, o[i], type || i);\n                  var property = o[i];\n                  var propertyType = _.util.type(property);\n                  if (propertyType === 'Object' && !visited[objId(property)]) {\n                    visited[objId(property)] = true;\n                    DFS(property, callback, null, visited);\n                  } else if (propertyType === 'Array' && !visited[objId(property)]) {\n                    visited[objId(property)] = true;\n                    DFS(property, callback, i, visited);\n                  }\n                }\n              }\n            }\n          },\n          plugins: {},\n          highlightAll: function (async, callback) {\n            _.highlightAllUnder(document, async, callback);\n          },\n          highlightAllUnder: function (container, async, callback) {\n            var env = {\n              callback: callback,\n              container: container,\n              selector: 'code[class*=\"language-\"], [class*=\"language-\"] code, code[class*=\"lang-\"], [class*=\"lang-\"] code'\n            };\n            _.hooks.run('before-highlightall', env);\n            env.elements = Array.prototype.slice.apply(env.container.querySelectorAll(env.selector));\n            _.hooks.run('before-all-elements-highlight', env);\n            for (var i = 0, element; element = env.elements[i++];) {\n              _.highlightElement(element, async === true, env.callback);\n            }\n          },\n          highlightElement: function (element, async, callback) {\n            var language = _.util.getLanguage(element);\n            var grammar = _.languages[language];\n            _.util.setLanguage(element, language);\n            var parent = element.parentElement;\n            if (parent && parent.nodeName.toLowerCase() === 'pre') {\n              _.util.setLanguage(parent, language);\n            }\n            var code = element.textContent;\n            var env = {\n              element: element,\n              language: language,\n              grammar: grammar,\n              code: code\n            };\n            function insertHighlightedCode(highlightedCode) {\n              env.highlightedCode = highlightedCode;\n              _.hooks.run('before-insert', env);\n              env.element.innerHTML = env.highlightedCode;\n              _.hooks.run('after-highlight', env);\n              _.hooks.run('complete', env);\n              callback && callback.call(env.element);\n            }\n            _.hooks.run('before-sanity-check', env);\n            parent = env.element.parentElement;\n            if (parent && parent.nodeName.toLowerCase() === 'pre' && !parent.hasAttribute('tabindex')) {\n              parent.setAttribute('tabindex', '0');\n            }\n            if (!env.code) {\n              _.hooks.run('complete', env);\n              callback && callback.call(env.element);\n              return;\n            }\n            _.hooks.run('before-highlight', env);\n            if (!env.grammar) {\n              insertHighlightedCode(_.util.encode(env.code));\n              return;\n            }\n            if (async && _self.Worker) {\n              var worker = new Worker(_.filename);\n              worker.onmessage = function (evt) {\n                insertHighlightedCode(evt.data);\n              };\n              worker.postMessage(JSON.stringify({\n                language: env.language,\n                code: env.code,\n                immediateClose: true\n              }));\n            } else {\n              insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));\n            }\n          },\n          highlight: function (text, grammar, language) {\n            var env = {\n              code: text,\n              grammar: grammar,\n              language: language\n            };\n            _.hooks.run('before-tokenize', env);\n            if (!env.grammar) {\n              throw new Error('The language \"' + env.language + '\" has no grammar.');\n            }\n            env.tokens = _.tokenize(env.code, env.grammar);\n            _.hooks.run('after-tokenize', env);\n            return Token.stringify(_.util.encode(env.tokens), env.language);\n          },\n          tokenize: function (text, grammar) {\n            var rest = grammar.rest;\n            if (rest) {\n              for (var token in rest) {\n                grammar[token] = rest[token];\n              }\n              delete grammar.rest;\n            }\n            var tokenList = new LinkedList();\n            addAfter(tokenList, tokenList.head, text);\n            matchGrammar(text, tokenList, grammar, tokenList.head, 0);\n            return toArray(tokenList);\n          },\n          hooks: {\n            all: {},\n            add: function (name, callback) {\n              var hooks = _.hooks.all;\n              hooks[name] = hooks[name] || [];\n              hooks[name].push(callback);\n            },\n            run: function (name, env) {\n              var callbacks = _.hooks.all[name];\n              if (!callbacks || !callbacks.length) {\n                return;\n              }\n              for (var i = 0, callback; callback = callbacks[i++];) {\n                callback(env);\n              }\n            }\n          },\n          Token: Token\n        };\n        _self.Prism = _;\n        function Token(type, content, alias, matchedStr) {\n          this.type = type;\n          this.content = content;\n          this.alias = alias;\n          this.length = (matchedStr || '').length | 0;\n        }\n        Token.stringify = function stringify(o, language) {\n          if (typeof o == 'string') {\n            return o;\n          }\n          if (Array.isArray(o)) {\n            var s = '';\n            o.forEach(function (e) {\n              s += stringify(e, language);\n            });\n            return s;\n          }\n          var env = {\n            type: o.type,\n            content: stringify(o.content, language),\n            tag: 'span',\n            classes: [\n              'token',\n              o.type\n            ],\n            attributes: {},\n            language: language\n          };\n          var aliases = o.alias;\n          if (aliases) {\n            if (Array.isArray(aliases)) {\n              Array.prototype.push.apply(env.classes, aliases);\n            } else {\n              env.classes.push(aliases);\n            }\n          }\n          _.hooks.run('wrap', env);\n          var attributes = '';\n          for (var name in env.attributes) {\n            attributes += ' ' + name + '=\"' + (env.attributes[name] || '').replace(/\"/g, '&quot;') + '\"';\n          }\n          return '<' + env.tag + ' class=\"' + env.classes.join(' ') + '\"' + attributes + '>' + env.content + '</' + env.tag + '>';\n        };\n        function matchPattern(pattern, pos, text, lookbehind) {\n          pattern.lastIndex = pos;\n          var match = pattern.exec(text);\n          if (match && lookbehind && match[1]) {\n            var lookbehindLength = match[1].length;\n            match.index += lookbehindLength;\n            match[0] = match[0].slice(lookbehindLength);\n          }\n          return match;\n        }\n        function matchGrammar(text, tokenList, grammar, startNode, startPos, rematch) {\n          for (var token in grammar) {\n            if (!grammar.hasOwnProperty(token) || !grammar[token]) {\n              continue;\n            }\n            var patterns = grammar[token];\n            patterns = Array.isArray(patterns) ? patterns : [patterns];\n            for (var j = 0; j < patterns.length; ++j) {\n              if (rematch && rematch.cause == token + ',' + j) {\n                return;\n              }\n              var patternObj = patterns[j];\n              var inside = patternObj.inside;\n              var lookbehind = !!patternObj.lookbehind;\n              var greedy = !!patternObj.greedy;\n              var alias = patternObj.alias;\n              if (greedy && !patternObj.pattern.global) {\n                var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0];\n                patternObj.pattern = RegExp(patternObj.pattern.source, flags + 'g');\n              }\n              var pattern = patternObj.pattern || patternObj;\n              for (var currentNode = startNode.next, pos = startPos; currentNode !== tokenList.tail; pos += currentNode.value.length, currentNode = currentNode.next) {\n                if (rematch && pos >= rematch.reach) {\n                  break;\n                }\n                var str = currentNode.value;\n                if (tokenList.length > text.length) {\n                  return;\n                }\n                if (str instanceof Token) {\n                  continue;\n                }\n                var removeCount = 1;\n                var match;\n                if (greedy) {\n                  match = matchPattern(pattern, pos, text, lookbehind);\n                  if (!match || match.index >= text.length) {\n                    break;\n                  }\n                  var from = match.index;\n                  var to = match.index + match[0].length;\n                  var p = pos;\n                  p += currentNode.value.length;\n                  while (from >= p) {\n                    currentNode = currentNode.next;\n                    p += currentNode.value.length;\n                  }\n                  p -= currentNode.value.length;\n                  pos = p;\n                  if (currentNode.value instanceof Token) {\n                    continue;\n                  }\n                  for (var k = currentNode; k !== tokenList.tail && (p < to || typeof k.value === 'string'); k = k.next) {\n                    removeCount++;\n                    p += k.value.length;\n                  }\n                  removeCount--;\n                  str = text.slice(pos, p);\n                  match.index -= pos;\n                } else {\n                  match = matchPattern(pattern, 0, str, lookbehind);\n                  if (!match) {\n                    continue;\n                  }\n                }\n                var from = match.index;\n                var matchStr = match[0];\n                var before = str.slice(0, from);\n                var after = str.slice(from + matchStr.length);\n                var reach = pos + str.length;\n                if (rematch && reach > rematch.reach) {\n                  rematch.reach = reach;\n                }\n                var removeFrom = currentNode.prev;\n                if (before) {\n                  removeFrom = addAfter(tokenList, removeFrom, before);\n                  pos += before.length;\n                }\n                removeRange(tokenList, removeFrom, removeCount);\n                var wrapped = new Token(token, inside ? _.tokenize(matchStr, inside) : matchStr, alias, matchStr);\n                currentNode = addAfter(tokenList, removeFrom, wrapped);\n                if (after) {\n                  addAfter(tokenList, currentNode, after);\n                }\n                if (removeCount > 1) {\n                  var nestedRematch = {\n                    cause: token + ',' + j,\n                    reach: reach\n                  };\n                  matchGrammar(text, tokenList, grammar, currentNode.prev, pos, nestedRematch);\n                  if (rematch && nestedRematch.reach > rematch.reach) {\n                    rematch.reach = nestedRematch.reach;\n                  }\n                }\n              }\n            }\n          }\n        }\n        function LinkedList() {\n          var head = {\n            value: null,\n            prev: null,\n            next: null\n          };\n          var tail = {\n            value: null,\n            prev: head,\n            next: null\n          };\n          head.next = tail;\n          this.head = head;\n          this.tail = tail;\n          this.length = 0;\n        }\n        function addAfter(list, node, value) {\n          var next = node.next;\n          var newNode = {\n            value: value,\n            prev: node,\n            next: next\n          };\n          node.next = newNode;\n          next.prev = newNode;\n          list.length++;\n          return newNode;\n        }\n        function removeRange(list, node, count) {\n          var next = node.next;\n          for (var i = 0; i < count && next !== list.tail; i++) {\n            next = next.next;\n          }\n          node.next = next;\n          next.prev = node;\n          list.length -= i;\n        }\n        function toArray(list) {\n          var array = [];\n          var node = list.head.next;\n          while (node !== list.tail) {\n            array.push(node.value);\n            node = node.next;\n          }\n          return array;\n        }\n        if (!_self.document) {\n          if (!_self.addEventListener) {\n            return _;\n          }\n          if (!_.disableWorkerMessageHandler) {\n            _self.addEventListener('message', function (evt) {\n              var message = JSON.parse(evt.data);\n              var lang = message.language;\n              var code = message.code;\n              var immediateClose = message.immediateClose;\n              _self.postMessage(_.highlight(code, _.languages[lang], lang));\n              if (immediateClose) {\n                _self.close();\n              }\n            }, false);\n          }\n          return _;\n        }\n        var script = _.util.currentScript();\n        if (script) {\n          _.filename = script.src;\n          if (script.hasAttribute('data-manual')) {\n            _.manual = true;\n          }\n        }\n        function highlightAutomaticallyCallback() {\n          if (!_.manual) {\n            _.highlightAll();\n          }\n        }\n        if (!_.manual) {\n          var readyState = document.readyState;\n          if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) {\n            document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback);\n          } else {\n            if (window.requestAnimationFrame) {\n              window.requestAnimationFrame(highlightAutomaticallyCallback);\n            } else {\n              window.setTimeout(highlightAutomaticallyCallback, 16);\n            }\n          }\n        }\n        return _;\n      }(_self);\n      if (typeof module !== 'undefined' && module.exports) {\n        module.exports = Prism;\n      }\n      if (typeof global !== 'undefined') {\n        global.Prism = Prism;\n      }\n      Prism.languages.clike = {\n        'comment': [\n          {\n            pattern: /(^|[^\\\\])\\/\\*[\\s\\S]*?(?:\\*\\/|$)/,\n            lookbehind: true,\n            greedy: true\n          },\n          {\n            pattern: /(^|[^\\\\:])\\/\\/.*/,\n            lookbehind: true,\n            greedy: true\n          }\n        ],\n        'string': {\n          pattern: /([\"'])(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\1)[^\\\\\\r\\n])*\\1/,\n          greedy: true\n        },\n        'class-name': {\n          pattern: /(\\b(?:class|extends|implements|instanceof|interface|new|trait)\\s+|\\bcatch\\s+\\()[\\w.\\\\]+/i,\n          lookbehind: true,\n          inside: { 'punctuation': /[.\\\\]/ }\n        },\n        'keyword': /\\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\\b/,\n        'boolean': /\\b(?:false|true)\\b/,\n        'function': /\\b\\w+(?=\\()/,\n        'number': /\\b0x[\\da-f]+\\b|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:e[+-]?\\d+)?/i,\n        'operator': /[<>]=?|[!=]=?=?|--?|\\+\\+?|&&?|\\|\\|?|[?*/~^%]/,\n        'punctuation': /[{}[\\];(),.:]/\n      };\n      (function (Prism) {\n        function getPlaceholder(language, index) {\n          return '___' + language.toUpperCase() + index + '___';\n        }\n        Object.defineProperties(Prism.languages['markup-templating'] = {}, {\n          buildPlaceholders: {\n            value: function (env, language, placeholderPattern, replaceFilter) {\n              if (env.language !== language) {\n                return;\n              }\n              var tokenStack = env.tokenStack = [];\n              env.code = env.code.replace(placeholderPattern, function (match) {\n                if (typeof replaceFilter === 'function' && !replaceFilter(match)) {\n                  return match;\n                }\n                var i = tokenStack.length;\n                var placeholder;\n                while (env.code.indexOf(placeholder = getPlaceholder(language, i)) !== -1) {\n                  ++i;\n                }\n                tokenStack[i] = match;\n                return placeholder;\n              });\n              env.grammar = Prism.languages.markup;\n            }\n          },\n          tokenizePlaceholders: {\n            value: function (env, language) {\n              if (env.language !== language || !env.tokenStack) {\n                return;\n              }\n              env.grammar = Prism.languages[language];\n              var j = 0;\n              var keys = Object.keys(env.tokenStack);\n              function walkTokens(tokens) {\n                for (var i = 0; i < tokens.length; i++) {\n                  if (j >= keys.length) {\n                    break;\n                  }\n                  var token = tokens[i];\n                  if (typeof token === 'string' || token.content && typeof token.content === 'string') {\n                    var k = keys[j];\n                    var t = env.tokenStack[k];\n                    var s = typeof token === 'string' ? token : token.content;\n                    var placeholder = getPlaceholder(language, k);\n                    var index = s.indexOf(placeholder);\n                    if (index > -1) {\n                      ++j;\n                      var before = s.substring(0, index);\n                      var middle = new Prism.Token(language, Prism.tokenize(t, env.grammar), 'language-' + language, t);\n                      var after = s.substring(index + placeholder.length);\n                      var replacement = [];\n                      if (before) {\n                        replacement.push.apply(replacement, walkTokens([before]));\n                      }\n                      replacement.push(middle);\n                      if (after) {\n                        replacement.push.apply(replacement, walkTokens([after]));\n                      }\n                      if (typeof token === 'string') {\n                        tokens.splice.apply(tokens, [\n                          i,\n                          1\n                        ].concat(replacement));\n                      } else {\n                        token.content = replacement;\n                      }\n                    }\n                  } else if (token.content) {\n                    walkTokens(token.content);\n                  }\n                }\n                return tokens;\n              }\n              walkTokens(env.tokens);\n            }\n          }\n        });\n      }(Prism));\n      Prism.languages.c = Prism.languages.extend('clike', {\n        'comment': {\n          pattern: /\\/\\/(?:[^\\r\\n\\\\]|\\\\(?:\\r\\n?|\\n|(?![\\r\\n])))*|\\/\\*[\\s\\S]*?(?:\\*\\/|$)/,\n          greedy: true\n        },\n        'string': {\n          pattern: /\"(?:\\\\(?:\\r\\n|[\\s\\S])|[^\"\\\\\\r\\n])*\"/,\n          greedy: true\n        },\n        'class-name': {\n          pattern: /(\\b(?:enum|struct)\\s+(?:__attribute__\\s*\\(\\([\\s\\S]*?\\)\\)\\s*)?)\\w+|\\b[a-z]\\w*_t\\b/,\n          lookbehind: true\n        },\n        'keyword': /\\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\\b/,\n        'function': /\\b[a-z_]\\w*(?=\\s*\\()/i,\n        'number': /(?:\\b0x(?:[\\da-f]+(?:\\.[\\da-f]*)?|\\.[\\da-f]+)(?:p[+-]?\\d+)?|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:e[+-]?\\d+)?)[ful]{0,4}/i,\n        'operator': />>=?|<<=?|->|([-+&|:])\\1|[?:~]|[-+*/%&|^!=<>]=?/\n      });\n      Prism.languages.insertBefore('c', 'string', {\n        'char': {\n          pattern: /'(?:\\\\(?:\\r\\n|[\\s\\S])|[^'\\\\\\r\\n]){0,32}'/,\n          greedy: true\n        }\n      });\n      Prism.languages.insertBefore('c', 'string', {\n        'macro': {\n          pattern: /(^[\\t ]*)#\\s*[a-z](?:[^\\r\\n\\\\/]|\\/(?!\\*)|\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/|\\\\(?:\\r\\n|[\\s\\S]))*/im,\n          lookbehind: true,\n          greedy: true,\n          alias: 'property',\n          inside: {\n            'string': [\n              {\n                pattern: /^(#\\s*include\\s*)<[^>]+>/,\n                lookbehind: true\n              },\n              Prism.languages.c['string']\n            ],\n            'char': Prism.languages.c['char'],\n            'comment': Prism.languages.c['comment'],\n            'macro-name': [\n              {\n                pattern: /(^#\\s*define\\s+)\\w+\\b(?!\\()/i,\n                lookbehind: true\n              },\n              {\n                pattern: /(^#\\s*define\\s+)\\w+\\b(?=\\()/i,\n                lookbehind: true,\n                alias: 'function'\n              }\n            ],\n            'directive': {\n              pattern: /^(#\\s*)[a-z]+/,\n              lookbehind: true,\n              alias: 'keyword'\n            },\n            'directive-hash': /^#/,\n            'punctuation': /##|\\\\(?=[\\r\\n])/,\n            'expression': {\n              pattern: /\\S[\\s\\S]*/,\n              inside: Prism.languages.c\n            }\n          }\n        }\n      });\n      Prism.languages.insertBefore('c', 'function', { 'constant': /\\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\\b/ });\n      delete Prism.languages.c['boolean'];\n      (function (Prism) {\n        var keyword = /\\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\\b/;\n        var modName = /\\b(?!<keyword>)\\w+(?:\\s*\\.\\s*\\w+)*\\b/.source.replace(/<keyword>/g, function () {\n          return keyword.source;\n        });\n        Prism.languages.cpp = Prism.languages.extend('c', {\n          'class-name': [\n            {\n              pattern: RegExp(/(\\b(?:class|concept|enum|struct|typename)\\s+)(?!<keyword>)\\w+/.source.replace(/<keyword>/g, function () {\n                return keyword.source;\n              })),\n              lookbehind: true\n            },\n            /\\b[A-Z]\\w*(?=\\s*::\\s*\\w+\\s*\\()/,\n            /\\b[A-Z_]\\w*(?=\\s*::\\s*~\\w+\\s*\\()/i,\n            /\\b\\w+(?=\\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\\s*::\\s*\\w+\\s*\\()/\n          ],\n          'keyword': keyword,\n          'number': {\n            pattern: /(?:\\b0b[01']+|\\b0x(?:[\\da-f']+(?:\\.[\\da-f']*)?|\\.[\\da-f']+)(?:p[+-]?[\\d']+)?|(?:\\b[\\d']+(?:\\.[\\d']*)?|\\B\\.[\\d']+)(?:e[+-]?[\\d']+)?)[ful]{0,4}/i,\n            greedy: true\n          },\n          'operator': />>=?|<<=?|->|--|\\+\\+|&&|\\|\\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\\b/,\n          'boolean': /\\b(?:false|true)\\b/\n        });\n        Prism.languages.insertBefore('cpp', 'string', {\n          'module': {\n            pattern: RegExp(/(\\b(?:import|module)\\s+)/.source + '(?:' + /\"(?:\\\\(?:\\r\\n|[\\s\\S])|[^\"\\\\\\r\\n])*\"|<[^<>\\r\\n]*>/.source + '|' + /<mod-name>(?:\\s*:\\s*<mod-name>)?|:\\s*<mod-name>/.source.replace(/<mod-name>/g, function () {\n              return modName;\n            }) + ')'),\n            lookbehind: true,\n            greedy: true,\n            inside: {\n              'string': /^[<\"][\\s\\S]+/,\n              'operator': /:/,\n              'punctuation': /\\./\n            }\n          },\n          'raw-string': {\n            pattern: /R\"([^()\\\\ ]{0,16})\\([\\s\\S]*?\\)\\1\"/,\n            alias: 'string',\n            greedy: true\n          }\n        });\n        Prism.languages.insertBefore('cpp', 'keyword', {\n          'generic-function': {\n            pattern: /\\b(?!operator\\b)[a-z_]\\w*\\s*<(?:[^<>]|<[^<>]*>)*>(?=\\s*\\()/i,\n            inside: {\n              'function': /^\\w+/,\n              'generic': {\n                pattern: /<[\\s\\S]+/,\n                alias: 'class-name',\n                inside: Prism.languages.cpp\n              }\n            }\n          }\n        });\n        Prism.languages.insertBefore('cpp', 'operator', {\n          'double-colon': {\n            pattern: /::/,\n            alias: 'punctuation'\n          }\n        });\n        Prism.languages.insertBefore('cpp', 'class-name', {\n          'base-clause': {\n            pattern: /(\\b(?:class|struct)\\s+\\w+\\s*:\\s*)[^;{}\"'\\s]+(?:\\s+[^;{}\"'\\s]+)*(?=\\s*[;{])/,\n            lookbehind: true,\n            greedy: true,\n            inside: Prism.languages.extend('cpp', {})\n          }\n        });\n        Prism.languages.insertBefore('inside', 'double-colon', { 'class-name': /\\b[a-z_]\\w*\\b(?!\\s*::)/i }, Prism.languages.cpp['base-clause']);\n      }(Prism));\n      (function (Prism) {\n        function replace(pattern, replacements) {\n          return pattern.replace(/<<(\\d+)>>/g, function (m, index) {\n            return '(?:' + replacements[+index] + ')';\n          });\n        }\n        function re(pattern, replacements, flags) {\n          return RegExp(replace(pattern, replacements), flags || '');\n        }\n        function nested(pattern, depthLog2) {\n          for (var i = 0; i < depthLog2; i++) {\n            pattern = pattern.replace(/<<self>>/g, function () {\n              return '(?:' + pattern + ')';\n            });\n          }\n          return pattern.replace(/<<self>>/g, '[^\\\\s\\\\S]');\n        }\n        var keywordKinds = {\n          type: 'bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void',\n          typeDeclaration: 'class enum interface record struct',\n          contextual: 'add alias and ascending async await by descending from(?=\\\\s*(?:\\\\w|$)) get global group into init(?=\\\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\\\s*{)',\n          other: 'abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield'\n        };\n        function keywordsToPattern(words) {\n          return '\\\\b(?:' + words.trim().replace(/ /g, '|') + ')\\\\b';\n        }\n        var typeDeclarationKeywords = keywordsToPattern(keywordKinds.typeDeclaration);\n        var keywords = RegExp(keywordsToPattern(keywordKinds.type + ' ' + keywordKinds.typeDeclaration + ' ' + keywordKinds.contextual + ' ' + keywordKinds.other));\n        var nonTypeKeywords = keywordsToPattern(keywordKinds.typeDeclaration + ' ' + keywordKinds.contextual + ' ' + keywordKinds.other);\n        var nonContextualKeywords = keywordsToPattern(keywordKinds.type + ' ' + keywordKinds.typeDeclaration + ' ' + keywordKinds.other);\n        var generic = nested(/<(?:[^<>;=+\\-*/%&|^]|<<self>>)*>/.source, 2);\n        var nestedRound = nested(/\\((?:[^()]|<<self>>)*\\)/.source, 2);\n        var name = /@?\\b[A-Za-z_]\\w*\\b/.source;\n        var genericName = replace(/<<0>>(?:\\s*<<1>>)?/.source, [\n          name,\n          generic\n        ]);\n        var identifier = replace(/(?!<<0>>)<<1>>(?:\\s*\\.\\s*<<1>>)*/.source, [\n          nonTypeKeywords,\n          genericName\n        ]);\n        var array = /\\[\\s*(?:,\\s*)*\\]/.source;\n        var typeExpressionWithoutTuple = replace(/<<0>>(?:\\s*(?:\\?\\s*)?<<1>>)*(?:\\s*\\?)?/.source, [\n          identifier,\n          array\n        ]);\n        var tupleElement = replace(/[^,()<>[\\];=+\\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source, [\n          generic,\n          nestedRound,\n          array\n        ]);\n        var tuple = replace(/\\(<<0>>+(?:,<<0>>+)+\\)/.source, [tupleElement]);\n        var typeExpression = replace(/(?:<<0>>|<<1>>)(?:\\s*(?:\\?\\s*)?<<2>>)*(?:\\s*\\?)?/.source, [\n          tuple,\n          identifier,\n          array\n        ]);\n        var typeInside = {\n          'keyword': keywords,\n          'punctuation': /[<>()?,.:[\\]]/\n        };\n        var character = /'(?:[^\\r\\n'\\\\]|\\\\.|\\\\[Uux][\\da-fA-F]{1,8})'/.source;\n        var regularString = /\"(?:\\\\.|[^\\\\\"\\r\\n])*\"/.source;\n        var verbatimString = /@\"(?:\"\"|\\\\[\\s\\S]|[^\\\\\"])*\"(?!\")/.source;\n        Prism.languages.csharp = Prism.languages.extend('clike', {\n          'string': [\n            {\n              pattern: re(/(^|[^$\\\\])<<0>>/.source, [verbatimString]),\n              lookbehind: true,\n              greedy: true\n            },\n            {\n              pattern: re(/(^|[^@$\\\\])<<0>>/.source, [regularString]),\n              lookbehind: true,\n              greedy: true\n            }\n          ],\n          'class-name': [\n            {\n              pattern: re(/(\\busing\\s+static\\s+)<<0>>(?=\\s*;)/.source, [identifier]),\n              lookbehind: true,\n              inside: typeInside\n            },\n            {\n              pattern: re(/(\\busing\\s+<<0>>\\s*=\\s*)<<1>>(?=\\s*;)/.source, [\n                name,\n                typeExpression\n              ]),\n              lookbehind: true,\n              inside: typeInside\n            },\n            {\n              pattern: re(/(\\busing\\s+)<<0>>(?=\\s*=)/.source, [name]),\n              lookbehind: true\n            },\n            {\n              pattern: re(/(\\b<<0>>\\s+)<<1>>/.source, [\n                typeDeclarationKeywords,\n                genericName\n              ]),\n              lookbehind: true,\n              inside: typeInside\n            },\n            {\n              pattern: re(/(\\bcatch\\s*\\(\\s*)<<0>>/.source, [identifier]),\n              lookbehind: true,\n              inside: typeInside\n            },\n            {\n              pattern: re(/(\\bwhere\\s+)<<0>>/.source, [name]),\n              lookbehind: true\n            },\n            {\n              pattern: re(/(\\b(?:is(?:\\s+not)?|as)\\s+)<<0>>/.source, [typeExpressionWithoutTuple]),\n              lookbehind: true,\n              inside: typeInside\n            },\n            {\n              pattern: re(/\\b<<0>>(?=\\s+(?!<<1>>|with\\s*\\{)<<2>>(?:\\s*[=,;:{)\\]]|\\s+(?:in|when)\\b))/.source, [\n                typeExpression,\n                nonContextualKeywords,\n                name\n              ]),\n              inside: typeInside\n            }\n          ],\n          'keyword': keywords,\n          'number': /(?:\\b0(?:x[\\da-f_]*[\\da-f]|b[01_]*[01])|(?:\\B\\.\\d+(?:_+\\d+)*|\\b\\d+(?:_+\\d+)*(?:\\.\\d+(?:_+\\d+)*)?)(?:e[-+]?\\d+(?:_+\\d+)*)?)(?:[dflmu]|lu|ul)?\\b/i,\n          'operator': />>=?|<<=?|[-=]>|([-+&|])\\1|~|\\?\\?=?|[-+*/%&|^!=<>]=?/,\n          'punctuation': /\\?\\.?|::|[{}[\\];(),.:]/\n        });\n        Prism.languages.insertBefore('csharp', 'number', {\n          'range': {\n            pattern: /\\.\\./,\n            alias: 'operator'\n          }\n        });\n        Prism.languages.insertBefore('csharp', 'punctuation', {\n          'named-parameter': {\n            pattern: re(/([(,]\\s*)<<0>>(?=\\s*:)/.source, [name]),\n            lookbehind: true,\n            alias: 'punctuation'\n          }\n        });\n        Prism.languages.insertBefore('csharp', 'class-name', {\n          'namespace': {\n            pattern: re(/(\\b(?:namespace|using)\\s+)<<0>>(?:\\s*\\.\\s*<<0>>)*(?=\\s*[;{])/.source, [name]),\n            lookbehind: true,\n            inside: { 'punctuation': /\\./ }\n          },\n          'type-expression': {\n            pattern: re(/(\\b(?:default|sizeof|typeof)\\s*\\(\\s*(?!\\s))(?:[^()\\s]|\\s(?!\\s)|<<0>>)*(?=\\s*\\))/.source, [nestedRound]),\n            lookbehind: true,\n            alias: 'class-name',\n            inside: typeInside\n          },\n          'return-type': {\n            pattern: re(/<<0>>(?=\\s+(?:<<1>>\\s*(?:=>|[({]|\\.\\s*this\\s*\\[)|this\\s*\\[))/.source, [\n              typeExpression,\n              identifier\n            ]),\n            inside: typeInside,\n            alias: 'class-name'\n          },\n          'constructor-invocation': {\n            pattern: re(/(\\bnew\\s+)<<0>>(?=\\s*[[({])/.source, [typeExpression]),\n            lookbehind: true,\n            inside: typeInside,\n            alias: 'class-name'\n          },\n          'generic-method': {\n            pattern: re(/<<0>>\\s*<<1>>(?=\\s*\\()/.source, [\n              name,\n              generic\n            ]),\n            inside: {\n              'function': re(/^<<0>>/.source, [name]),\n              'generic': {\n                pattern: RegExp(generic),\n                alias: 'class-name',\n                inside: typeInside\n              }\n            }\n          },\n          'type-list': {\n            pattern: re(/\\b((?:<<0>>\\s+<<1>>|record\\s+<<1>>\\s*<<5>>|where\\s+<<2>>)\\s*:\\s*)(?:<<3>>|<<4>>|<<1>>\\s*<<5>>|<<6>>)(?:\\s*,\\s*(?:<<3>>|<<4>>|<<6>>))*(?=\\s*(?:where|[{;]|=>|$))/.source, [\n              typeDeclarationKeywords,\n              genericName,\n              name,\n              typeExpression,\n              keywords.source,\n              nestedRound,\n              /\\bnew\\s*\\(\\s*\\)/.source\n            ]),\n            lookbehind: true,\n            inside: {\n              'record-arguments': {\n                pattern: re(/(^(?!new\\s*\\()<<0>>\\s*)<<1>>/.source, [\n                  genericName,\n                  nestedRound\n                ]),\n                lookbehind: true,\n                greedy: true,\n                inside: Prism.languages.csharp\n              },\n              'keyword': keywords,\n              'class-name': {\n                pattern: RegExp(typeExpression),\n                greedy: true,\n                inside: typeInside\n              },\n              'punctuation': /[,()]/\n            }\n          },\n          'preprocessor': {\n            pattern: /(^[\\t ]*)#.*/m,\n            lookbehind: true,\n            alias: 'property',\n            inside: {\n              'directive': {\n                pattern: /(#)\\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\\b/,\n                lookbehind: true,\n                alias: 'keyword'\n              }\n            }\n          }\n        });\n        var regularStringOrCharacter = regularString + '|' + character;\n        var regularStringCharacterOrComment = replace(/\\/(?![*/])|\\/\\/[^\\r\\n]*[\\r\\n]|\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/|<<0>>/.source, [regularStringOrCharacter]);\n        var roundExpression = nested(replace(/[^\"'/()]|<<0>>|\\(<<self>>*\\)/.source, [regularStringCharacterOrComment]), 2);\n        var attrTarget = /\\b(?:assembly|event|field|method|module|param|property|return|type)\\b/.source;\n        var attr = replace(/<<0>>(?:\\s*\\(<<1>>*\\))?/.source, [\n          identifier,\n          roundExpression\n        ]);\n        Prism.languages.insertBefore('csharp', 'class-name', {\n          'attribute': {\n            pattern: re(/((?:^|[^\\s\\w>)?])\\s*\\[\\s*)(?:<<0>>\\s*:\\s*)?<<1>>(?:\\s*,\\s*<<1>>)*(?=\\s*\\])/.source, [\n              attrTarget,\n              attr\n            ]),\n            lookbehind: true,\n            greedy: true,\n            inside: {\n              'target': {\n                pattern: re(/^<<0>>(?=\\s*:)/.source, [attrTarget]),\n                alias: 'keyword'\n              },\n              'attribute-arguments': {\n                pattern: re(/\\(<<0>>*\\)/.source, [roundExpression]),\n                inside: Prism.languages.csharp\n              },\n              'class-name': {\n                pattern: RegExp(identifier),\n                inside: { 'punctuation': /\\./ }\n              },\n              'punctuation': /[:,]/\n            }\n          }\n        });\n        var formatString = /:[^}\\r\\n]+/.source;\n        var mInterpolationRound = nested(replace(/[^\"'/()]|<<0>>|\\(<<self>>*\\)/.source, [regularStringCharacterOrComment]), 2);\n        var mInterpolation = replace(/\\{(?!\\{)(?:(?![}:])<<0>>)*<<1>>?\\}/.source, [\n          mInterpolationRound,\n          formatString\n        ]);\n        var sInterpolationRound = nested(replace(/[^\"'/()]|\\/(?!\\*)|\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/|<<0>>|\\(<<self>>*\\)/.source, [regularStringOrCharacter]), 2);\n        var sInterpolation = replace(/\\{(?!\\{)(?:(?![}:])<<0>>)*<<1>>?\\}/.source, [\n          sInterpolationRound,\n          formatString\n        ]);\n        function createInterpolationInside(interpolation, interpolationRound) {\n          return {\n            'interpolation': {\n              pattern: re(/((?:^|[^{])(?:\\{\\{)*)<<0>>/.source, [interpolation]),\n              lookbehind: true,\n              inside: {\n                'format-string': {\n                  pattern: re(/(^\\{(?:(?![}:])<<0>>)*)<<1>>(?=\\}$)/.source, [\n                    interpolationRound,\n                    formatString\n                  ]),\n                  lookbehind: true,\n                  inside: { 'punctuation': /^:/ }\n                },\n                'punctuation': /^\\{|\\}$/,\n                'expression': {\n                  pattern: /[\\s\\S]+/,\n                  alias: 'language-csharp',\n                  inside: Prism.languages.csharp\n                }\n              }\n            },\n            'string': /[\\s\\S]+/\n          };\n        }\n        Prism.languages.insertBefore('csharp', 'string', {\n          'interpolation-string': [\n            {\n              pattern: re(/(^|[^\\\\])(?:\\$@|@\\$)\"(?:\"\"|\\\\[\\s\\S]|\\{\\{|<<0>>|[^\\\\{\"])*\"/.source, [mInterpolation]),\n              lookbehind: true,\n              greedy: true,\n              inside: createInterpolationInside(mInterpolation, mInterpolationRound)\n            },\n            {\n              pattern: re(/(^|[^@\\\\])\\$\"(?:\\\\.|\\{\\{|<<0>>|[^\\\\\"{])*\"/.source, [sInterpolation]),\n              lookbehind: true,\n              greedy: true,\n              inside: createInterpolationInside(sInterpolation, sInterpolationRound)\n            }\n          ],\n          'char': {\n            pattern: RegExp(character),\n            greedy: true\n          }\n        });\n        Prism.languages.dotnet = Prism.languages.cs = Prism.languages.csharp;\n      }(Prism));\n      (function (Prism) {\n        var string = /(?:\"(?:\\\\(?:\\r\\n|[\\s\\S])|[^\"\\\\\\r\\n])*\"|'(?:\\\\(?:\\r\\n|[\\s\\S])|[^'\\\\\\r\\n])*')/;\n        Prism.languages.css = {\n          'comment': /\\/\\*[\\s\\S]*?\\*\\//,\n          'atrule': {\n            pattern: /@[\\w-](?:[^;{\\s]|\\s+(?![\\s{]))*(?:;|(?=\\s*\\{))/,\n            inside: {\n              'rule': /^@[\\w-]+/,\n              'selector-function-argument': {\n                pattern: /(\\bselector\\s*\\(\\s*(?![\\s)]))(?:[^()\\s]|\\s+(?![\\s)])|\\((?:[^()]|\\([^()]*\\))*\\))+(?=\\s*\\))/,\n                lookbehind: true,\n                alias: 'selector'\n              },\n              'keyword': {\n                pattern: /(^|[^\\w-])(?:and|not|only|or)(?![\\w-])/,\n                lookbehind: true\n              }\n            }\n          },\n          'url': {\n            pattern: RegExp('\\\\burl\\\\((?:' + string.source + '|' + /(?:[^\\\\\\r\\n()\"']|\\\\[\\s\\S])*/.source + ')\\\\)', 'i'),\n            greedy: true,\n            inside: {\n              'function': /^url/i,\n              'punctuation': /^\\(|\\)$/,\n              'string': {\n                pattern: RegExp('^' + string.source + '$'),\n                alias: 'url'\n              }\n            }\n          },\n          'selector': {\n            pattern: RegExp('(^|[{}\\\\s])[^{}\\\\s](?:[^{};\"\\'\\\\s]|\\\\s+(?![\\\\s{])|' + string.source + ')*(?=\\\\s*\\\\{)'),\n            lookbehind: true\n          },\n          'string': {\n            pattern: string,\n            greedy: true\n          },\n          'property': {\n            pattern: /(^|[^-\\w\\xA0-\\uFFFF])(?!\\s)[-_a-z\\xA0-\\uFFFF](?:(?!\\s)[-\\w\\xA0-\\uFFFF])*(?=\\s*:)/i,\n            lookbehind: true\n          },\n          'important': /!important\\b/i,\n          'function': {\n            pattern: /(^|[^-a-z0-9])[-a-z0-9]+(?=\\()/i,\n            lookbehind: true\n          },\n          'punctuation': /[(){};:,]/\n        };\n        Prism.languages.css['atrule'].inside.rest = Prism.languages.css;\n        var markup = Prism.languages.markup;\n        if (markup) {\n          markup.tag.addInlined('style', 'css');\n          markup.tag.addAttribute('style', 'css');\n        }\n      }(Prism));\n      (function (Prism) {\n        var keywords = /\\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\\s*[(){}[\\]<>=%~.:,;?+\\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\\b/;\n        var classNamePrefix = /(?:[a-z]\\w*\\s*\\.\\s*)*(?:[A-Z]\\w*\\s*\\.\\s*)*/.source;\n        var className = {\n          pattern: RegExp(/(^|[^\\w.])/.source + classNamePrefix + /[A-Z](?:[\\d_A-Z]*[a-z]\\w*)?\\b/.source),\n          lookbehind: true,\n          inside: {\n            'namespace': {\n              pattern: /^[a-z]\\w*(?:\\s*\\.\\s*[a-z]\\w*)*(?:\\s*\\.)?/,\n              inside: { 'punctuation': /\\./ }\n            },\n            'punctuation': /\\./\n          }\n        };\n        Prism.languages.java = Prism.languages.extend('clike', {\n          'string': {\n            pattern: /(^|[^\\\\])\"(?:\\\\.|[^\"\\\\\\r\\n])*\"/,\n            lookbehind: true,\n            greedy: true\n          },\n          'class-name': [\n            className,\n            {\n              pattern: RegExp(/(^|[^\\w.])/.source + classNamePrefix + /[A-Z]\\w*(?=\\s+\\w+\\s*[;,=()]|\\s*(?:\\[[\\s,]*\\]\\s*)?::\\s*new\\b)/.source),\n              lookbehind: true,\n              inside: className.inside\n            },\n            {\n              pattern: RegExp(/(\\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\\s+)/.source + classNamePrefix + /[A-Z]\\w*\\b/.source),\n              lookbehind: true,\n              inside: className.inside\n            }\n          ],\n          'keyword': keywords,\n          'function': [\n            Prism.languages.clike.function,\n            {\n              pattern: /(::\\s*)[a-z_]\\w*/,\n              lookbehind: true\n            }\n          ],\n          'number': /\\b0b[01][01_]*L?\\b|\\b0x(?:\\.[\\da-f_p+-]+|[\\da-f_]+(?:\\.[\\da-f_p+-]+)?)\\b|(?:\\b\\d[\\d_]*(?:\\.[\\d_]*)?|\\B\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[dfl]?/i,\n          'operator': {\n            pattern: /(^|[^.])(?:<<=?|>>>?=?|->|--|\\+\\+|&&|\\|\\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,\n            lookbehind: true\n          }\n        });\n        Prism.languages.insertBefore('java', 'string', {\n          'triple-quoted-string': {\n            pattern: /\"\"\"[ \\t]*[\\r\\n](?:(?:\"|\"\")?(?:\\\\.|[^\"\\\\]))*\"\"\"/,\n            greedy: true,\n            alias: 'string'\n          },\n          'char': {\n            pattern: /'(?:\\\\.|[^'\\\\\\r\\n]){1,6}'/,\n            greedy: true\n          }\n        });\n        Prism.languages.insertBefore('java', 'class-name', {\n          'annotation': {\n            pattern: /(^|[^.])@\\w+(?:\\s*\\.\\s*\\w+)*/,\n            lookbehind: true,\n            alias: 'punctuation'\n          },\n          'generics': {\n            pattern: /<(?:[\\w\\s,.?]|&(?!&)|<(?:[\\w\\s,.?]|&(?!&)|<(?:[\\w\\s,.?]|&(?!&)|<(?:[\\w\\s,.?]|&(?!&))*>)*>)*>)*>/,\n            inside: {\n              'class-name': className,\n              'keyword': keywords,\n              'punctuation': /[<>(),.:]/,\n              'operator': /[?&|]/\n            }\n          },\n          'import': [\n            {\n              pattern: RegExp(/(\\bimport\\s+)/.source + classNamePrefix + /(?:[A-Z]\\w*|\\*)(?=\\s*;)/.source),\n              lookbehind: true,\n              inside: {\n                'namespace': className.inside.namespace,\n                'punctuation': /\\./,\n                'operator': /\\*/,\n                'class-name': /\\w+/\n              }\n            },\n            {\n              pattern: RegExp(/(\\bimport\\s+static\\s+)/.source + classNamePrefix + /(?:\\w+|\\*)(?=\\s*;)/.source),\n              lookbehind: true,\n              alias: 'static',\n              inside: {\n                'namespace': className.inside.namespace,\n                'static': /\\b\\w+$/,\n                'punctuation': /\\./,\n                'operator': /\\*/,\n                'class-name': /\\w+/\n              }\n            }\n          ],\n          'namespace': {\n            pattern: RegExp(/(\\b(?:exports|import(?:\\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\\s+)(?!<keyword>)[a-z]\\w*(?:\\.[a-z]\\w*)*\\.?/.source.replace(/<keyword>/g, function () {\n              return keywords.source;\n            })),\n            lookbehind: true,\n            inside: { 'punctuation': /\\./ }\n          }\n        });\n      }(Prism));\n      Prism.languages.javascript = Prism.languages.extend('clike', {\n        'class-name': [\n          Prism.languages.clike['class-name'],\n          {\n            pattern: /(^|[^$\\w\\xA0-\\uFFFF])(?!\\s)[_$A-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\.(?:constructor|prototype))/,\n            lookbehind: true\n          }\n        ],\n        'keyword': [\n          {\n            pattern: /((?:^|\\})\\s*)catch\\b/,\n            lookbehind: true\n          },\n          {\n            pattern: /(^|[^.]|\\.\\.\\.\\s*)\\b(?:as|assert(?=\\s*\\{)|async(?=\\s*(?:function\\b|\\(|[$\\w\\xA0-\\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\\s*(?:\\{|$))|for|from(?=\\s*(?:['\"]|$))|function|(?:get|set)(?=\\s*(?:[#\\[$\\w\\xA0-\\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\\b/,\n            lookbehind: true\n          }\n        ],\n        'function': /#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*(?:\\.\\s*(?:apply|bind|call)\\s*)?\\()/,\n        'number': {\n          pattern: RegExp(/(^|[^\\w$])/.source + '(?:' + (/NaN|Infinity/.source + '|' + /0[bB][01]+(?:_[01]+)*n?/.source + '|' + /0[oO][0-7]+(?:_[0-7]+)*n?/.source + '|' + /0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?/.source + '|' + /\\d+(?:_\\d+)*n/.source + '|' + /(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?/.source) + ')' + /(?![\\w$])/.source),\n          lookbehind: true\n        },\n        'operator': /--|\\+\\+|\\*\\*=?|=>|&&=?|\\|\\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\\.{3}|\\?\\?=?|\\?\\.?|[~:]/\n      });\n      Prism.languages.javascript['class-name'][0].pattern = /(\\b(?:class|extends|implements|instanceof|interface|new)\\s+)[\\w.\\\\]+/;\n      Prism.languages.insertBefore('javascript', 'keyword', {\n        'regex': {\n          pattern: RegExp(/((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/.source + /\\//.source + '(?:' + /(?:\\[(?:[^\\]\\\\\\r\\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\\r\\n])+\\/[dgimyus]{0,7}/.source + '|' + /(?:\\[(?:[^[\\]\\\\\\r\\n]|\\\\.|\\[(?:[^[\\]\\\\\\r\\n]|\\\\.|\\[(?:[^[\\]\\\\\\r\\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\\r\\n])+\\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source + ')' + /(?=(?:\\s|\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/)*(?:$|[\\r\\n,.;:})\\]]|\\/\\/))/.source),\n          lookbehind: true,\n          greedy: true,\n          inside: {\n            'regex-source': {\n              pattern: /^(\\/)[\\s\\S]+(?=\\/[a-z]*$)/,\n              lookbehind: true,\n              alias: 'language-regex',\n              inside: Prism.languages.regex\n            },\n            'regex-delimiter': /^\\/|\\/$/,\n            'regex-flags': /^[a-z]+$/\n          }\n        },\n        'function-variable': {\n          pattern: /#?(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*[=:]\\s*(?:async\\s*)?(?:\\bfunction\\b|(?:\\((?:[^()]|\\([^()]*\\))*\\)|(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*)\\s*=>))/,\n          alias: 'function'\n        },\n        'parameter': [\n          {\n            pattern: /(function(?:\\s+(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*)?\\s*\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\))/,\n            lookbehind: true,\n            inside: Prism.languages.javascript\n          },\n          {\n            pattern: /(^|[^$\\w\\xA0-\\uFFFF])(?!\\s)[_$a-z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*=>)/i,\n            lookbehind: true,\n            inside: Prism.languages.javascript\n          },\n          {\n            pattern: /(\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\)\\s*=>)/,\n            lookbehind: true,\n            inside: Prism.languages.javascript\n          },\n          {\n            pattern: /((?:\\b|\\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\\w\\xA0-\\uFFFF]))(?:(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*\\s*)\\(\\s*|\\]\\s*\\(\\s*)(?!\\s)(?:[^()\\s]|\\s+(?![\\s)])|\\([^()]*\\))+(?=\\s*\\)\\s*\\{)/,\n            lookbehind: true,\n            inside: Prism.languages.javascript\n          }\n        ],\n        'constant': /\\b[A-Z](?:[A-Z_]|\\dx?)*\\b/\n      });\n      Prism.languages.insertBefore('javascript', 'string', {\n        'hashbang': {\n          pattern: /^#!.*/,\n          greedy: true,\n          alias: 'comment'\n        },\n        'template-string': {\n          pattern: /`(?:\\\\[\\s\\S]|\\$\\{(?:[^{}]|\\{(?:[^{}]|\\{[^}]*\\})*\\})+\\}|(?!\\$\\{)[^\\\\`])*`/,\n          greedy: true,\n          inside: {\n            'template-punctuation': {\n              pattern: /^`|`$/,\n              alias: 'string'\n            },\n            'interpolation': {\n              pattern: /((?:^|[^\\\\])(?:\\\\{2})*)\\$\\{(?:[^{}]|\\{(?:[^{}]|\\{[^}]*\\})*\\})+\\}/,\n              lookbehind: true,\n              inside: {\n                'interpolation-punctuation': {\n                  pattern: /^\\$\\{|\\}$/,\n                  alias: 'punctuation'\n                },\n                rest: Prism.languages.javascript\n              }\n            },\n            'string': /[\\s\\S]+/\n          }\n        },\n        'string-property': {\n          pattern: /((?:^|[,{])[ \\t]*)([\"'])(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\2)[^\\\\\\r\\n])*\\2(?=\\s*:)/m,\n          lookbehind: true,\n          greedy: true,\n          alias: 'property'\n        }\n      });\n      Prism.languages.insertBefore('javascript', 'operator', {\n        'literal-property': {\n          pattern: /((?:^|[,{])[ \\t]*)(?!\\s)[_$a-zA-Z\\xA0-\\uFFFF](?:(?!\\s)[$\\w\\xA0-\\uFFFF])*(?=\\s*:)/m,\n          lookbehind: true,\n          alias: 'property'\n        }\n      });\n      if (Prism.languages.markup) {\n        Prism.languages.markup.tag.addInlined('script', 'javascript');\n        Prism.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source, 'javascript');\n      }\n      Prism.languages.js = Prism.languages.javascript;\n      Prism.languages.markup = {\n        'comment': {\n          pattern: /<!--(?:(?!<!--)[\\s\\S])*?-->/,\n          greedy: true\n        },\n        'prolog': {\n          pattern: /<\\?[\\s\\S]+?\\?>/,\n          greedy: true\n        },\n        'doctype': {\n          pattern: /<!DOCTYPE(?:[^>\"'[\\]]|\"[^\"]*\"|'[^']*')+(?:\\[(?:[^<\"'\\]]|\"[^\"]*\"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\\]\\s*)?>/i,\n          greedy: true,\n          inside: {\n            'internal-subset': {\n              pattern: /(^[^\\[]*\\[)[\\s\\S]+(?=\\]>$)/,\n              lookbehind: true,\n              greedy: true,\n              inside: null\n            },\n            'string': {\n              pattern: /\"[^\"]*\"|'[^']*'/,\n              greedy: true\n            },\n            'punctuation': /^<!|>$|[[\\]]/,\n            'doctype-tag': /^DOCTYPE/i,\n            'name': /[^\\s<>'\"]+/\n          }\n        },\n        'cdata': {\n          pattern: /<!\\[CDATA\\[[\\s\\S]*?\\]\\]>/i,\n          greedy: true\n        },\n        'tag': {\n          pattern: /<\\/?(?!\\d)[^\\s>\\/=$<%]+(?:\\s(?:\\s*[^\\s>\\/=]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))|(?=[\\s/>])))+)?\\s*\\/?>/,\n          greedy: true,\n          inside: {\n            'tag': {\n              pattern: /^<\\/?[^\\s>\\/]+/,\n              inside: {\n                'punctuation': /^<\\/?/,\n                'namespace': /^[^\\s>\\/:]+:/\n              }\n            },\n            'special-attr': [],\n            'attr-value': {\n              pattern: /=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+)/,\n              inside: {\n                'punctuation': [\n                  {\n                    pattern: /^=/,\n                    alias: 'attr-equals'\n                  },\n                  /\"|'/\n                ]\n              }\n            },\n            'punctuation': /\\/?>/,\n            'attr-name': {\n              pattern: /[^\\s>\\/]+/,\n              inside: { 'namespace': /^[^\\s>\\/:]+:/ }\n            }\n          }\n        },\n        'entity': [\n          {\n            pattern: /&[\\da-z]{1,8};/i,\n            alias: 'named-entity'\n          },\n          /&#x?[\\da-f]{1,8};/i\n        ]\n      };\n      Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] = Prism.languages.markup['entity'];\n      Prism.languages.markup['doctype'].inside['internal-subset'].inside = Prism.languages.markup;\n      Prism.hooks.add('wrap', function (env) {\n        if (env.type === 'entity') {\n          env.attributes['title'] = env.content.replace(/&amp;/, '&');\n        }\n      });\n      Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {\n        value: function addInlined(tagName, lang) {\n          var includedCdataInside = {};\n          includedCdataInside['language-' + lang] = {\n            pattern: /(^<!\\[CDATA\\[)[\\s\\S]+?(?=\\]\\]>$)/i,\n            lookbehind: true,\n            inside: Prism.languages[lang]\n          };\n          includedCdataInside['cdata'] = /^<!\\[CDATA\\[|\\]\\]>$/i;\n          var inside = {\n            'included-cdata': {\n              pattern: /<!\\[CDATA\\[[\\s\\S]*?\\]\\]>/i,\n              inside: includedCdataInside\n            }\n          };\n          inside['language-' + lang] = {\n            pattern: /[\\s\\S]+/,\n            inside: Prism.languages[lang]\n          };\n          var def = {};\n          def[tagName] = {\n            pattern: RegExp(/(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[\\s\\S])*?(?=<\\/__>)/.source.replace(/__/g, function () {\n              return tagName;\n            }), 'i'),\n            lookbehind: true,\n            greedy: true,\n            inside: inside\n          };\n          Prism.languages.insertBefore('markup', 'cdata', def);\n        }\n      });\n      Object.defineProperty(Prism.languages.markup.tag, 'addAttribute', {\n        value: function (attrName, lang) {\n          Prism.languages.markup.tag.inside['special-attr'].push({\n            pattern: RegExp(/(^|[\"'\\s])/.source + '(?:' + attrName + ')' + /\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))/.source, 'i'),\n            lookbehind: true,\n            inside: {\n              'attr-name': /^[^\\s=]+/,\n              'attr-value': {\n                pattern: /=[\\s\\S]+/,\n                inside: {\n                  'value': {\n                    pattern: /(^=\\s*([\"']|(?![\"'])))\\S[\\s\\S]*(?=\\2$)/,\n                    lookbehind: true,\n                    alias: [\n                      lang,\n                      'language-' + lang\n                    ],\n                    inside: Prism.languages[lang]\n                  },\n                  'punctuation': [\n                    {\n                      pattern: /^=/,\n                      alias: 'attr-equals'\n                    },\n                    /\"|'/\n                  ]\n                }\n              }\n            }\n          });\n        }\n      });\n      Prism.languages.html = Prism.languages.markup;\n      Prism.languages.mathml = Prism.languages.markup;\n      Prism.languages.svg = Prism.languages.markup;\n      Prism.languages.xml = Prism.languages.extend('markup', {});\n      Prism.languages.ssml = Prism.languages.xml;\n      Prism.languages.atom = Prism.languages.xml;\n      Prism.languages.rss = Prism.languages.xml;\n      (function (Prism) {\n        var comment = /\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*|#(?!\\[).*/;\n        var constant = [\n          {\n            pattern: /\\b(?:false|true)\\b/i,\n            alias: 'boolean'\n          },\n          {\n            pattern: /(::\\s*)\\b[a-z_]\\w*\\b(?!\\s*\\()/i,\n            greedy: true,\n            lookbehind: true\n          },\n          {\n            pattern: /(\\b(?:case|const)\\s+)\\b[a-z_]\\w*(?=\\s*[;=])/i,\n            greedy: true,\n            lookbehind: true\n          },\n          /\\b(?:null)\\b/i,\n          /\\b[A-Z_][A-Z0-9_]*\\b(?!\\s*\\()/\n        ];\n        var number = /\\b0b[01]+(?:_[01]+)*\\b|\\b0o[0-7]+(?:_[0-7]+)*\\b|\\b0x[\\da-f]+(?:_[\\da-f]+)*\\b|(?:\\b\\d+(?:_\\d+)*\\.?(?:\\d+(?:_\\d+)*)?|\\B\\.\\d+)(?:e[+-]?\\d+)?/i;\n        var operator = /<?=>|\\?\\?=?|\\.{3}|\\??->|[!=]=?=?|::|\\*\\*=?|--|\\+\\+|&&|\\|\\||<<|>>|[?~]|[/^|%*&<>.+-]=?/;\n        var punctuation = /[{}\\[\\](),:;]/;\n        Prism.languages.php = {\n          'delimiter': {\n            pattern: /\\?>$|^<\\?(?:php(?=\\s)|=)?/i,\n            alias: 'important'\n          },\n          'comment': comment,\n          'variable': /\\$+(?:\\w+\\b|(?=\\{))/,\n          'package': {\n            pattern: /(namespace\\s+|use\\s+(?:function\\s+)?)(?:\\\\?\\b[a-z_]\\w*)+\\b(?!\\\\)/i,\n            lookbehind: true,\n            inside: { 'punctuation': /\\\\/ }\n          },\n          'class-name-definition': {\n            pattern: /(\\b(?:class|enum|interface|trait)\\s+)\\b[a-z_]\\w*(?!\\\\)\\b/i,\n            lookbehind: true,\n            alias: 'class-name'\n          },\n          'function-definition': {\n            pattern: /(\\bfunction\\s+)[a-z_]\\w*(?=\\s*\\()/i,\n            lookbehind: true,\n            alias: 'function'\n          },\n          'keyword': [\n            {\n              pattern: /(\\(\\s*)\\b(?:array|bool|boolean|float|int|integer|object|string)\\b(?=\\s*\\))/i,\n              alias: 'type-casting',\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /([(,?]\\s*)\\b(?:array(?!\\s*\\()|bool|callable|(?:false|null)(?=\\s*\\|)|float|int|iterable|mixed|object|self|static|string)\\b(?=\\s*\\$)/i,\n              alias: 'type-hint',\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /(\\)\\s*:\\s*(?:\\?\\s*)?)\\b(?:array(?!\\s*\\()|bool|callable|(?:false|null)(?=\\s*\\|)|float|int|iterable|mixed|never|object|self|static|string|void)\\b/i,\n              alias: 'return-type',\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /\\b(?:array(?!\\s*\\()|bool|float|int|iterable|mixed|object|string|void)\\b/i,\n              alias: 'type-declaration',\n              greedy: true\n            },\n            {\n              pattern: /(\\|\\s*)(?:false|null)\\b|\\b(?:false|null)(?=\\s*\\|)/i,\n              alias: 'type-declaration',\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /\\b(?:parent|self|static)(?=\\s*::)/i,\n              alias: 'static-context',\n              greedy: true\n            },\n            {\n              pattern: /(\\byield\\s+)from\\b/i,\n              lookbehind: true\n            },\n            /\\bclass\\b/i,\n            {\n              pattern: /((?:^|[^\\s>:]|(?:^|[^-])>|(?:^|[^:]):)\\s*)\\b(?:abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|never|new|or|parent|print|private|protected|public|readonly|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield|__halt_compiler)\\b/i,\n              lookbehind: true\n            }\n          ],\n          'argument-name': {\n            pattern: /([(,]\\s*)\\b[a-z_]\\w*(?=\\s*:(?!:))/i,\n            lookbehind: true\n          },\n          'class-name': [\n            {\n              pattern: /(\\b(?:extends|implements|instanceof|new(?!\\s+self|\\s+static))\\s+|\\bcatch\\s*\\()\\b[a-z_]\\w*(?!\\\\)\\b/i,\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /(\\|\\s*)\\b[a-z_]\\w*(?!\\\\)\\b/i,\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /\\b[a-z_]\\w*(?!\\\\)\\b(?=\\s*\\|)/i,\n              greedy: true\n            },\n            {\n              pattern: /(\\|\\s*)(?:\\\\?\\b[a-z_]\\w*)+\\b/i,\n              alias: 'class-name-fully-qualified',\n              greedy: true,\n              lookbehind: true,\n              inside: { 'punctuation': /\\\\/ }\n            },\n            {\n              pattern: /(?:\\\\?\\b[a-z_]\\w*)+\\b(?=\\s*\\|)/i,\n              alias: 'class-name-fully-qualified',\n              greedy: true,\n              inside: { 'punctuation': /\\\\/ }\n            },\n            {\n              pattern: /(\\b(?:extends|implements|instanceof|new(?!\\s+self\\b|\\s+static\\b))\\s+|\\bcatch\\s*\\()(?:\\\\?\\b[a-z_]\\w*)+\\b(?!\\\\)/i,\n              alias: 'class-name-fully-qualified',\n              greedy: true,\n              lookbehind: true,\n              inside: { 'punctuation': /\\\\/ }\n            },\n            {\n              pattern: /\\b[a-z_]\\w*(?=\\s*\\$)/i,\n              alias: 'type-declaration',\n              greedy: true\n            },\n            {\n              pattern: /(?:\\\\?\\b[a-z_]\\w*)+(?=\\s*\\$)/i,\n              alias: [\n                'class-name-fully-qualified',\n                'type-declaration'\n              ],\n              greedy: true,\n              inside: { 'punctuation': /\\\\/ }\n            },\n            {\n              pattern: /\\b[a-z_]\\w*(?=\\s*::)/i,\n              alias: 'static-context',\n              greedy: true\n            },\n            {\n              pattern: /(?:\\\\?\\b[a-z_]\\w*)+(?=\\s*::)/i,\n              alias: [\n                'class-name-fully-qualified',\n                'static-context'\n              ],\n              greedy: true,\n              inside: { 'punctuation': /\\\\/ }\n            },\n            {\n              pattern: /([(,?]\\s*)[a-z_]\\w*(?=\\s*\\$)/i,\n              alias: 'type-hint',\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /([(,?]\\s*)(?:\\\\?\\b[a-z_]\\w*)+(?=\\s*\\$)/i,\n              alias: [\n                'class-name-fully-qualified',\n                'type-hint'\n              ],\n              greedy: true,\n              lookbehind: true,\n              inside: { 'punctuation': /\\\\/ }\n            },\n            {\n              pattern: /(\\)\\s*:\\s*(?:\\?\\s*)?)\\b[a-z_]\\w*(?!\\\\)\\b/i,\n              alias: 'return-type',\n              greedy: true,\n              lookbehind: true\n            },\n            {\n              pattern: /(\\)\\s*:\\s*(?:\\?\\s*)?)(?:\\\\?\\b[a-z_]\\w*)+\\b(?!\\\\)/i,\n              alias: [\n                'class-name-fully-qualified',\n                'return-type'\n              ],\n              greedy: true,\n              lookbehind: true,\n              inside: { 'punctuation': /\\\\/ }\n            }\n          ],\n          'constant': constant,\n          'function': {\n            pattern: /(^|[^\\\\\\w])\\\\?[a-z_](?:[\\w\\\\]*\\w)?(?=\\s*\\()/i,\n            lookbehind: true,\n            inside: { 'punctuation': /\\\\/ }\n          },\n          'property': {\n            pattern: /(->\\s*)\\w+/,\n            lookbehind: true\n          },\n          'number': number,\n          'operator': operator,\n          'punctuation': punctuation\n        };\n        var string_interpolation = {\n          pattern: /\\{\\$(?:\\{(?:\\{[^{}]+\\}|[^{}]+)\\}|[^{}])+\\}|(^|[^\\\\{])\\$+(?:\\w+(?:\\[[^\\r\\n\\[\\]]+\\]|->\\w+)?)/,\n          lookbehind: true,\n          inside: Prism.languages.php\n        };\n        var string = [\n          {\n            pattern: /<<<'([^']+)'[\\r\\n](?:.*[\\r\\n])*?\\1;/,\n            alias: 'nowdoc-string',\n            greedy: true,\n            inside: {\n              'delimiter': {\n                pattern: /^<<<'[^']+'|[a-z_]\\w*;$/i,\n                alias: 'symbol',\n                inside: { 'punctuation': /^<<<'?|[';]$/ }\n              }\n            }\n          },\n          {\n            pattern: /<<<(?:\"([^\"]+)\"[\\r\\n](?:.*[\\r\\n])*?\\1;|([a-z_]\\w*)[\\r\\n](?:.*[\\r\\n])*?\\2;)/i,\n            alias: 'heredoc-string',\n            greedy: true,\n            inside: {\n              'delimiter': {\n                pattern: /^<<<(?:\"[^\"]+\"|[a-z_]\\w*)|[a-z_]\\w*;$/i,\n                alias: 'symbol',\n                inside: { 'punctuation': /^<<<\"?|[\";]$/ }\n              },\n              'interpolation': string_interpolation\n            }\n          },\n          {\n            pattern: /`(?:\\\\[\\s\\S]|[^\\\\`])*`/,\n            alias: 'backtick-quoted-string',\n            greedy: true\n          },\n          {\n            pattern: /'(?:\\\\[\\s\\S]|[^\\\\'])*'/,\n            alias: 'single-quoted-string',\n            greedy: true\n          },\n          {\n            pattern: /\"(?:\\\\[\\s\\S]|[^\\\\\"])*\"/,\n            alias: 'double-quoted-string',\n            greedy: true,\n            inside: { 'interpolation': string_interpolation }\n          }\n        ];\n        Prism.languages.insertBefore('php', 'variable', {\n          'string': string,\n          'attribute': {\n            pattern: /#\\[(?:[^\"'\\/#]|\\/(?![*/])|\\/\\/.*$|#(?!\\[).*$|\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/|\"(?:\\\\[\\s\\S]|[^\\\\\"])*\"|'(?:\\\\[\\s\\S]|[^\\\\'])*')+\\](?=\\s*[a-z$#])/im,\n            greedy: true,\n            inside: {\n              'attribute-content': {\n                pattern: /^(#\\[)[\\s\\S]+(?=\\]$)/,\n                lookbehind: true,\n                inside: {\n                  'comment': comment,\n                  'string': string,\n                  'attribute-class-name': [\n                    {\n                      pattern: /([^:]|^)\\b[a-z_]\\w*(?!\\\\)\\b/i,\n                      alias: 'class-name',\n                      greedy: true,\n                      lookbehind: true\n                    },\n                    {\n                      pattern: /([^:]|^)(?:\\\\?\\b[a-z_]\\w*)+/i,\n                      alias: [\n                        'class-name',\n                        'class-name-fully-qualified'\n                      ],\n                      greedy: true,\n                      lookbehind: true,\n                      inside: { 'punctuation': /\\\\/ }\n                    }\n                  ],\n                  'constant': constant,\n                  'number': number,\n                  'operator': operator,\n                  'punctuation': punctuation\n                }\n              },\n              'delimiter': {\n                pattern: /^#\\[|\\]$/,\n                alias: 'punctuation'\n              }\n            }\n          }\n        });\n        Prism.hooks.add('before-tokenize', function (env) {\n          if (!/<\\?/.test(env.code)) {\n            return;\n          }\n          var phpPattern = /<\\?(?:[^\"'/#]|\\/(?![*/])|(\"|')(?:\\\\[\\s\\S]|(?!\\1)[^\\\\])*\\1|(?:\\/\\/|#(?!\\[))(?:[^?\\n\\r]|\\?(?!>))*(?=$|\\?>|[\\r\\n])|#\\[|\\/\\*(?:[^*]|\\*(?!\\/))*(?:\\*\\/|$))*?(?:\\?>|$)/g;\n          Prism.languages['markup-templating'].buildPlaceholders(env, 'php', phpPattern);\n        });\n        Prism.hooks.add('after-tokenize', function (env) {\n          Prism.languages['markup-templating'].tokenizePlaceholders(env, 'php');\n        });\n      }(Prism));\n      Prism.languages.python = {\n        'comment': {\n          pattern: /(^|[^\\\\])#.*/,\n          lookbehind: true,\n          greedy: true\n        },\n        'string-interpolation': {\n          pattern: /(?:f|fr|rf)(?:(\"\"\"|''')[\\s\\S]*?\\1|(\"|')(?:\\\\.|(?!\\2)[^\\\\\\r\\n])*\\2)/i,\n          greedy: true,\n          inside: {\n            'interpolation': {\n              pattern: /((?:^|[^{])(?:\\{\\{)*)\\{(?!\\{)(?:[^{}]|\\{(?!\\{)(?:[^{}]|\\{(?!\\{)(?:[^{}])+\\})+\\})+\\}/,\n              lookbehind: true,\n              inside: {\n                'format-spec': {\n                  pattern: /(:)[^:(){}]+(?=\\}$)/,\n                  lookbehind: true\n                },\n                'conversion-option': {\n                  pattern: /![sra](?=[:}]$)/,\n                  alias: 'punctuation'\n                },\n                rest: null\n              }\n            },\n            'string': /[\\s\\S]+/\n          }\n        },\n        'triple-quoted-string': {\n          pattern: /(?:[rub]|br|rb)?(\"\"\"|''')[\\s\\S]*?\\1/i,\n          greedy: true,\n          alias: 'string'\n        },\n        'string': {\n          pattern: /(?:[rub]|br|rb)?(\"|')(?:\\\\.|(?!\\1)[^\\\\\\r\\n])*\\1/i,\n          greedy: true\n        },\n        'function': {\n          pattern: /((?:^|\\s)def[ \\t]+)[a-zA-Z_]\\w*(?=\\s*\\()/g,\n          lookbehind: true\n        },\n        'class-name': {\n          pattern: /(\\bclass\\s+)\\w+/i,\n          lookbehind: true\n        },\n        'decorator': {\n          pattern: /(^[\\t ]*)@\\w+(?:\\.\\w+)*/m,\n          lookbehind: true,\n          alias: [\n            'annotation',\n            'punctuation'\n          ],\n          inside: { 'punctuation': /\\./ }\n        },\n        'keyword': /\\b(?:_(?=\\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\\b/,\n        'builtin': /\\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\\b/,\n        'boolean': /\\b(?:False|None|True)\\b/,\n        'number': /\\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\\b|(?:\\b\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\B\\.\\d+(?:_\\d+)*)(?:e[+-]?\\d+(?:_\\d+)*)?j?(?!\\w)/i,\n        'operator': /[-+%=]=?|!=|:=|\\*\\*?=?|\\/\\/?=?|<[<=>]?|>[=>]?|[&|^~]/,\n        'punctuation': /[{}[\\];(),.:]/\n      };\n      Prism.languages.python['string-interpolation'].inside['interpolation'].inside.rest = Prism.languages.python;\n      Prism.languages.py = Prism.languages.python;\n      (function (Prism) {\n        Prism.languages.ruby = Prism.languages.extend('clike', {\n          'comment': {\n            pattern: /#.*|^=begin\\s[\\s\\S]*?^=end/m,\n            greedy: true\n          },\n          'class-name': {\n            pattern: /(\\b(?:class|module)\\s+|\\bcatch\\s+\\()[\\w.\\\\]+|\\b[A-Z_]\\w*(?=\\s*\\.\\s*new\\b)/,\n            lookbehind: true,\n            inside: { 'punctuation': /[.\\\\]/ }\n          },\n          'keyword': /\\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\\b/,\n          'operator': /\\.{2,3}|&\\.|===|<?=>|[!=]?~|(?:&&|\\|\\||<<|>>|\\*\\*|[+\\-*/%<>!^&|=])=?|[?:]/,\n          'punctuation': /[(){}[\\].,;]/\n        });\n        Prism.languages.insertBefore('ruby', 'operator', {\n          'double-colon': {\n            pattern: /::/,\n            alias: 'punctuation'\n          }\n        });\n        var interpolation = {\n          pattern: /((?:^|[^\\\\])(?:\\\\{2})*)#\\{(?:[^{}]|\\{[^{}]*\\})*\\}/,\n          lookbehind: true,\n          inside: {\n            'content': {\n              pattern: /^(#\\{)[\\s\\S]+(?=\\}$)/,\n              lookbehind: true,\n              inside: Prism.languages.ruby\n            },\n            'delimiter': {\n              pattern: /^#\\{|\\}$/,\n              alias: 'punctuation'\n            }\n          }\n        };\n        delete Prism.languages.ruby.function;\n        var percentExpression = '(?:' + [\n          /([^a-zA-Z0-9\\s{(\\[<=])(?:(?!\\1)[^\\\\]|\\\\[\\s\\S])*\\1/.source,\n          /\\((?:[^()\\\\]|\\\\[\\s\\S]|\\((?:[^()\\\\]|\\\\[\\s\\S])*\\))*\\)/.source,\n          /\\{(?:[^{}\\\\]|\\\\[\\s\\S]|\\{(?:[^{}\\\\]|\\\\[\\s\\S])*\\})*\\}/.source,\n          /\\[(?:[^\\[\\]\\\\]|\\\\[\\s\\S]|\\[(?:[^\\[\\]\\\\]|\\\\[\\s\\S])*\\])*\\]/.source,\n          /<(?:[^<>\\\\]|\\\\[\\s\\S]|<(?:[^<>\\\\]|\\\\[\\s\\S])*>)*>/.source\n        ].join('|') + ')';\n        var symbolName = /(?:\"(?:\\\\.|[^\"\\\\\\r\\n])*\"|(?:\\b[a-zA-Z_]\\w*|[^\\s\\0-\\x7F]+)[?!]?|\\$.)/.source;\n        Prism.languages.insertBefore('ruby', 'keyword', {\n          'regex-literal': [\n            {\n              pattern: RegExp(/%r/.source + percentExpression + /[egimnosux]{0,6}/.source),\n              greedy: true,\n              inside: {\n                'interpolation': interpolation,\n                'regex': /[\\s\\S]+/\n              }\n            },\n            {\n              pattern: /(^|[^/])\\/(?!\\/)(?:\\[[^\\r\\n\\]]+\\]|\\\\.|[^[/\\\\\\r\\n])+\\/[egimnosux]{0,6}(?=\\s*(?:$|[\\r\\n,.;})#]))/,\n              lookbehind: true,\n              greedy: true,\n              inside: {\n                'interpolation': interpolation,\n                'regex': /[\\s\\S]+/\n              }\n            }\n          ],\n          'variable': /[@$]+[a-zA-Z_]\\w*(?:[?!]|\\b)/,\n          'symbol': [\n            {\n              pattern: RegExp(/(^|[^:]):/.source + symbolName),\n              lookbehind: true,\n              greedy: true\n            },\n            {\n              pattern: RegExp(/([\\r\\n{(,][ \\t]*)/.source + symbolName + /(?=:(?!:))/.source),\n              lookbehind: true,\n              greedy: true\n            }\n          ],\n          'method-definition': {\n            pattern: /(\\bdef\\s+)\\w+(?:\\s*\\.\\s*\\w+)?/,\n            lookbehind: true,\n            inside: {\n              'function': /\\b\\w+$/,\n              'keyword': /^self\\b/,\n              'class-name': /^\\w+/,\n              'punctuation': /\\./\n            }\n          }\n        });\n        Prism.languages.insertBefore('ruby', 'string', {\n          'string-literal': [\n            {\n              pattern: RegExp(/%[qQiIwWs]?/.source + percentExpression),\n              greedy: true,\n              inside: {\n                'interpolation': interpolation,\n                'string': /[\\s\\S]+/\n              }\n            },\n            {\n              pattern: /(\"|')(?:#\\{[^}]+\\}|#(?!\\{)|\\\\(?:\\r\\n|[\\s\\S])|(?!\\1)[^\\\\#\\r\\n])*\\1/,\n              greedy: true,\n              inside: {\n                'interpolation': interpolation,\n                'string': /[\\s\\S]+/\n              }\n            },\n            {\n              pattern: /<<[-~]?([a-z_]\\w*)[\\r\\n](?:.*[\\r\\n])*?[\\t ]*\\1/i,\n              alias: 'heredoc-string',\n              greedy: true,\n              inside: {\n                'delimiter': {\n                  pattern: /^<<[-~]?[a-z_]\\w*|\\b[a-z_]\\w*$/i,\n                  inside: {\n                    'symbol': /\\b\\w+/,\n                    'punctuation': /^<<[-~]?/\n                  }\n                },\n                'interpolation': interpolation,\n                'string': /[\\s\\S]+/\n              }\n            },\n            {\n              pattern: /<<[-~]?'([a-z_]\\w*)'[\\r\\n](?:.*[\\r\\n])*?[\\t ]*\\1/i,\n              alias: 'heredoc-string',\n              greedy: true,\n              inside: {\n                'delimiter': {\n                  pattern: /^<<[-~]?'[a-z_]\\w*'|\\b[a-z_]\\w*$/i,\n                  inside: {\n                    'symbol': /\\b\\w+/,\n                    'punctuation': /^<<[-~]?'|'$/\n                  }\n                },\n                'string': /[\\s\\S]+/\n              }\n            }\n          ],\n          'command-literal': [\n            {\n              pattern: RegExp(/%x/.source + percentExpression),\n              greedy: true,\n              inside: {\n                'interpolation': interpolation,\n                'command': {\n                  pattern: /[\\s\\S]+/,\n                  alias: 'string'\n                }\n              }\n            },\n            {\n              pattern: /`(?:#\\{[^}]+\\}|#(?!\\{)|\\\\(?:\\r\\n|[\\s\\S])|[^\\\\`#\\r\\n])*`/,\n              greedy: true,\n              inside: {\n                'interpolation': interpolation,\n                'command': {\n                  pattern: /[\\s\\S]+/,\n                  alias: 'string'\n                }\n              }\n            }\n          ]\n        });\n        delete Prism.languages.ruby.string;\n        Prism.languages.insertBefore('ruby', 'number', {\n          'builtin': /\\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\\b/,\n          'constant': /\\b[A-Z][A-Z0-9_]*(?:[?!]|\\b)/\n        });\n        Prism.languages.rb = Prism.languages.ruby;\n      }(Prism));\n      window.Prism = oldprism;\n      return Prism;\n    }(undefined, undefined);\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('codesample_languages', { processor: 'object[]' });\n      registerOption('codesample_global_prismjs', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const getLanguages$1 = option('codesample_languages');\n    const useGlobalPrismJS = option('codesample_global_prismjs');\n\n    const get = editor => Global.Prism && useGlobalPrismJS(editor) ? Global.Prism : prismjs;\n\n    const isCodeSample = elm => {\n      return isNonNullable(elm) && elm.nodeName === 'PRE' && elm.className.indexOf('language-') !== -1;\n    };\n\n    const getSelectedCodeSample = editor => {\n      const node = editor.selection ? editor.selection.getNode() : null;\n      return isCodeSample(node) ? Optional.some(node) : Optional.none();\n    };\n    const insertCodeSample = (editor, language, code) => {\n      const dom = editor.dom;\n      editor.undoManager.transact(() => {\n        const node = getSelectedCodeSample(editor);\n        code = global$1.DOM.encode(code);\n        return node.fold(() => {\n          editor.insertContent('<pre id=\"__new\" class=\"language-' + language + '\">' + code + '</pre>');\n          const newPre = dom.select('#__new')[0];\n          dom.setAttrib(newPre, 'id', null);\n          editor.selection.select(newPre);\n        }, n => {\n          dom.setAttrib(n, 'class', 'language-' + language);\n          n.innerHTML = code;\n          get(editor).highlightElement(n);\n          editor.selection.select(n);\n        });\n      });\n    };\n    const getCurrentCode = editor => {\n      const node = getSelectedCodeSample(editor);\n      return node.bind(n => Optional.from(n.textContent)).getOr('');\n    };\n\n    const getLanguages = editor => {\n      const defaultLanguages = [\n        {\n          text: 'HTML/XML',\n          value: 'markup'\n        },\n        {\n          text: 'JavaScript',\n          value: 'javascript'\n        },\n        {\n          text: 'CSS',\n          value: 'css'\n        },\n        {\n          text: 'PHP',\n          value: 'php'\n        },\n        {\n          text: 'Ruby',\n          value: 'ruby'\n        },\n        {\n          text: 'Python',\n          value: 'python'\n        },\n        {\n          text: 'Java',\n          value: 'java'\n        },\n        {\n          text: 'C',\n          value: 'c'\n        },\n        {\n          text: 'C#',\n          value: 'csharp'\n        },\n        {\n          text: 'C++',\n          value: 'cpp'\n        }\n      ];\n      const customLanguages = getLanguages$1(editor);\n      return customLanguages ? customLanguages : defaultLanguages;\n    };\n    const getCurrentLanguage = (editor, fallback) => {\n      const node = getSelectedCodeSample(editor);\n      return node.fold(() => fallback, n => {\n        const matches = n.className.match(/language-(\\w+)/);\n        return matches ? matches[1] : fallback;\n      });\n    };\n\n    const open = editor => {\n      const languages = getLanguages(editor);\n      const defaultLanguage = head(languages).fold(constant(''), l => l.value);\n      const currentLanguage = getCurrentLanguage(editor, defaultLanguage);\n      const currentCode = getCurrentCode(editor);\n      editor.windowManager.open({\n        title: 'Insert/Edit Code Sample',\n        size: 'large',\n        body: {\n          type: 'panel',\n          items: [\n            {\n              type: 'selectbox',\n              name: 'language',\n              label: 'Language',\n              items: languages\n            },\n            {\n              type: 'textarea',\n              name: 'code',\n              label: 'Code view'\n            }\n          ]\n        },\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData: {\n          language: currentLanguage,\n          code: currentCode\n        },\n        onSubmit: api => {\n          const data = api.getData();\n          insertCodeSample(editor, data.language, data.code);\n          api.close();\n        }\n      });\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('codesample', () => {\n        const node = editor.selection.getNode();\n        if (editor.selection.isCollapsed() || isCodeSample(node)) {\n          open(editor);\n        } else {\n          editor.formatter.toggle('code');\n        }\n      });\n    };\n\n    const blank = r => s => s.replace(r, '');\n    const trim = blank(/^\\s+|\\s+$/g);\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const setup = editor => {\n      editor.on('PreProcess', e => {\n        const dom = editor.dom;\n        const pres = dom.select('pre[contenteditable=false]', e.node);\n        global.each(global.grep(pres, isCodeSample), elm => {\n          const code = elm.textContent;\n          dom.setAttrib(elm, 'class', trim(dom.getAttrib(elm, 'class')));\n          dom.setAttrib(elm, 'contentEditable', null);\n          dom.setAttrib(elm, 'data-mce-highlighted', null);\n          let child;\n          while (child = elm.firstChild) {\n            elm.removeChild(child);\n          }\n          const codeElm = dom.add(elm, 'code');\n          codeElm.textContent = code;\n        });\n      });\n      editor.on('SetContent', () => {\n        const dom = editor.dom;\n        const unprocessedCodeSamples = global.grep(dom.select('pre'), elm => {\n          return isCodeSample(elm) && dom.getAttrib(elm, 'data-mce-highlighted') !== 'true';\n        });\n        if (unprocessedCodeSamples.length) {\n          editor.undoManager.transact(() => {\n            global.each(unprocessedCodeSamples, elm => {\n              var _a;\n              global.each(dom.select('br', elm), elm => {\n                dom.replace(editor.getDoc().createTextNode('\\n'), elm);\n              });\n              elm.innerHTML = dom.encode((_a = elm.textContent) !== null && _a !== void 0 ? _a : '');\n              get(editor).highlightElement(elm);\n              dom.setAttrib(elm, 'data-mce-highlighted', true);\n              elm.className = trim(elm.className);\n            });\n          });\n        }\n      });\n      editor.on('PreInit', () => {\n        editor.parser.addNodeFilter('pre', nodes => {\n          var _a;\n          for (let i = 0, l = nodes.length; i < l; i++) {\n            const node = nodes[i];\n            const isCodeSample = ((_a = node.attr('class')) !== null && _a !== void 0 ? _a : '').indexOf('language-') !== -1;\n            if (isCodeSample) {\n              node.attr('contenteditable', 'false');\n              node.attr('data-mce-highlighted', 'false');\n            }\n          }\n        });\n      });\n    };\n\n    const isCodeSampleSelection = editor => {\n      const node = editor.selection.getStart();\n      return editor.dom.is(node, 'pre[class*=\"language-\"]');\n    };\n    const register = editor => {\n      const onAction = () => editor.execCommand('codesample');\n      editor.ui.registry.addToggleButton('codesample', {\n        icon: 'code-sample',\n        tooltip: 'Insert/edit code sample',\n        onAction,\n        onSetup: api => {\n          const nodeChangeHandler = () => {\n            api.setActive(isCodeSampleSelection(editor));\n          };\n          editor.on('NodeChange', nodeChangeHandler);\n          return () => editor.off('NodeChange', nodeChangeHandler);\n        }\n      });\n      editor.ui.registry.addMenuItem('codesample', {\n        text: 'Code sample...',\n        icon: 'code-sample',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$2.add('codesample', editor => {\n        register$2(editor);\n        setup(editor);\n        register(editor);\n        register$1(editor);\n        editor.on('dblclick', ev => {\n          if (isCodeSample(ev.target)) {\n            open(editor);\n          }\n        });\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/directionality/index.js",
    "content": "// Exports the \"directionality\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/directionality')\n//   ES2015:\n//     import 'tinymce/plugins/directionality'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/directionality/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const isString = isType$1('string');\n    const isBoolean = isSimpleType('boolean');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n\n    const compose1 = (fbc, fab) => a => fbc(fab(a));\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const never = constant(false);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const filter = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n\n    const DOCUMENT = 9;\n    const DOCUMENT_FRAGMENT = 11;\n    const ELEMENT = 1;\n    const TEXT = 3;\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom(node);\n    };\n    const fromDom = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom,\n      fromPoint\n    };\n\n    const is = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const name = element => {\n      const r = element.dom.nodeName;\n      return r.toLowerCase();\n    };\n    const type = element => element.dom.nodeType;\n    const isType = t => element => type(element) === t;\n    const isElement = isType(ELEMENT);\n    const isText = isType(TEXT);\n    const isDocument = isType(DOCUMENT);\n    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);\n    const isTag = tag => e => isElement(e) && name(e) === tag;\n\n    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);\n    const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const children$2 = element => map(element.dom.childNodes, SugarElement.fromDom);\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const remove = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n\n    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);\n    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);\n    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;\n    const getShadowRoot = e => {\n      const r = getRootNode(e);\n      return isShadowRoot(r) ? Optional.some(r) : Optional.none();\n    };\n    const getShadowHost = e => SugarElement.fromDom(e.dom.host);\n\n    const inBody = element => {\n      const dom = isText(element) ? element.dom.parentNode : element.dom;\n      if (dom === undefined || dom === null || dom.ownerDocument === null) {\n        return false;\n      }\n      const doc = dom.ownerDocument;\n      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));\n    };\n\n    const ancestor$1 = (scope, predicate, isRoot) => {\n      let element = scope.dom;\n      const stop = isFunction(isRoot) ? isRoot : never;\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        if (predicate(el)) {\n          return Optional.some(el);\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n\n    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);\n\n    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);\n\n    const get = (element, property) => {\n      const dom = element.dom;\n      const styles = window.getComputedStyle(dom);\n      const r = styles.getPropertyValue(property);\n      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;\n    };\n    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';\n\n    const getDirection = element => get(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';\n\n    const children$1 = (scope, predicate) => filter(children$2(scope), predicate);\n\n    const children = (scope, selector) => children$1(scope, e => is(e, selector));\n\n    const getParentElement = element => parent(element).filter(isElement);\n    const getNormalizedBlock = (element, isListItem) => {\n      const normalizedElement = isListItem ? ancestor(element, 'ol,ul') : Optional.some(element);\n      return normalizedElement.getOr(element);\n    };\n    const isListItem = isTag('li');\n    const setDir = (editor, dir) => {\n      const selectedBlocks = editor.selection.getSelectedBlocks();\n      if (selectedBlocks.length > 0) {\n        each(selectedBlocks, block => {\n          const blockElement = SugarElement.fromDom(block);\n          const isBlockElementListItem = isListItem(blockElement);\n          const normalizedBlock = getNormalizedBlock(blockElement, isBlockElementListItem);\n          const normalizedBlockParent = getParentElement(normalizedBlock);\n          normalizedBlockParent.each(parent => {\n            const parentDirection = getDirection(parent);\n            if (parentDirection !== dir) {\n              set(normalizedBlock, 'dir', dir);\n            } else if (getDirection(normalizedBlock) !== dir) {\n              remove(normalizedBlock, 'dir');\n            }\n            if (isBlockElementListItem) {\n              const listItems = children(normalizedBlock, 'li[dir]');\n              each(listItems, listItem => remove(listItem, 'dir'));\n            }\n          });\n        });\n        editor.nodeChanged();\n      }\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mceDirectionLTR', () => {\n        setDir(editor, 'ltr');\n      });\n      editor.addCommand('mceDirectionRTL', () => {\n        setDir(editor, 'rtl');\n      });\n    };\n\n    const getNodeChangeHandler = (editor, dir) => api => {\n      const nodeChangeHandler = e => {\n        const element = SugarElement.fromDom(e.element);\n        api.setActive(getDirection(element) === dir);\n      };\n      editor.on('NodeChange', nodeChangeHandler);\n      return () => editor.off('NodeChange', nodeChangeHandler);\n    };\n    const register = editor => {\n      editor.ui.registry.addToggleButton('ltr', {\n        tooltip: 'Left to right',\n        icon: 'ltr',\n        onAction: () => editor.execCommand('mceDirectionLTR'),\n        onSetup: getNodeChangeHandler(editor, 'ltr')\n      });\n      editor.ui.registry.addToggleButton('rtl', {\n        tooltip: 'Right to left',\n        icon: 'rtl',\n        onAction: () => editor.execCommand('mceDirectionRTL'),\n        onSetup: getNodeChangeHandler(editor, 'rtl')\n      });\n    };\n\n    var Plugin = () => {\n      global.add('directionality', editor => {\n        register$1(editor);\n        register(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/emoticons/index.js",
    "content": "// Exports the \"emoticons\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/emoticons')\n//   ES2015:\n//     import 'tinymce/plugins/emoticons'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/emoticons/js/emojiimages.js",
    "content": "window.tinymce.Resource.add(\"tinymce.plugins.emoticons\",{100:{keywords:[\"score\",\"perfect\",\"numbers\",\"century\",\"exam\",\"quiz\",\"test\",\"pass\",\"hundred\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💯\" src=\"1f4af.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},1234:{keywords:[\"numbers\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔢\" src=\"1f522.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},grinning:{keywords:[\"face\",\"smile\",\"happy\",\"joy\",\":D\",\"grin\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😀\" src=\"1f600.png\"/>',fitzpatrick_scale:false,category:\"people\"},grimacing:{keywords:[\"face\",\"grimace\",\"teeth\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😬\" src=\"1f62c.png\"/>',fitzpatrick_scale:false,category:\"people\"},grin:{keywords:[\"face\",\"happy\",\"smile\",\"joy\",\"kawaii\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😁\" src=\"1f601.png\"/>',fitzpatrick_scale:false,category:\"people\"},joy:{keywords:[\"face\",\"cry\",\"tears\",\"weep\",\"happy\",\"happytears\",\"haha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😂\" src=\"1f602.png\"/>',fitzpatrick_scale:false,category:\"people\"},rofl:{keywords:[\"face\",\"rolling\",\"floor\",\"laughing\",\"lol\",\"haha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤣\" src=\"1f923.png\"/>',fitzpatrick_scale:false,category:\"people\"},partying:{keywords:[\"face\",\"celebration\",\"woohoo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥳\" src=\"1f973.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiley:{keywords:[\"face\",\"happy\",\"joy\",\"haha\",\":D\",\":)\",\"smile\",\"funny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😃\" src=\"1f603.png\"/>',fitzpatrick_scale:false,category:\"people\"},smile:{keywords:[\"face\",\"happy\",\"joy\",\"funny\",\"haha\",\"laugh\",\"like\",\":D\",\":)\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😄\" src=\"1f604.png\"/>',fitzpatrick_scale:false,category:\"people\"},sweat_smile:{keywords:[\"face\",\"hot\",\"happy\",\"laugh\",\"sweat\",\"smile\",\"relief\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😅\" src=\"1f605.png\"/>',fitzpatrick_scale:false,category:\"people\"},laughing:{keywords:[\"happy\",\"joy\",\"lol\",\"satisfied\",\"haha\",\"face\",\"glad\",\"XD\",\"laugh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😆\" src=\"1f606.png\"/>',fitzpatrick_scale:false,category:\"people\"},innocent:{keywords:[\"face\",\"angel\",\"heaven\",\"halo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😇\" src=\"1f607.png\"/>',fitzpatrick_scale:false,category:\"people\"},wink:{keywords:[\"face\",\"happy\",\"mischievous\",\"secret\",\";)\",\"smile\",\"eye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😉\" src=\"1f609.png\"/>',fitzpatrick_scale:false,category:\"people\"},blush:{keywords:[\"face\",\"smile\",\"happy\",\"flushed\",\"crush\",\"embarrassed\",\"shy\",\"joy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😊\" src=\"1f60a.png\"/>',fitzpatrick_scale:false,category:\"people\"},slightly_smiling_face:{keywords:[\"face\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙂\" src=\"1f642.png\"/>',fitzpatrick_scale:false,category:\"people\"},upside_down_face:{keywords:[\"face\",\"flipped\",\"silly\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙃\" src=\"1f643.png\"/>',fitzpatrick_scale:false,category:\"people\"},relaxed:{keywords:[\"face\",\"blush\",\"massage\",\"happiness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☺️\" src=\"263a.png\"/>',fitzpatrick_scale:false,category:\"people\"},yum:{keywords:[\"happy\",\"joy\",\"tongue\",\"smile\",\"face\",\"silly\",\"yummy\",\"nom\",\"delicious\",\"savouring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😋\" src=\"1f60b.png\"/>',fitzpatrick_scale:false,category:\"people\"},relieved:{keywords:[\"face\",\"relaxed\",\"phew\",\"massage\",\"happiness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😌\" src=\"1f60c.png\"/>',fitzpatrick_scale:false,category:\"people\"},heart_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😍\" src=\"1f60d.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiling_face_with_three_hearts:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"hearts\",\"adore\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥰\" src=\"1f970.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_heart:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😘\" src=\"1f618.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing:{keywords:[\"love\",\"like\",\"face\",\"3\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😗\" src=\"1f617.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_smiling_eyes:{keywords:[\"face\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😙\" src=\"1f619.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_closed_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😚\" src=\"1f61a.png\"/>',fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_winking_eye:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"wink\",\"tongue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😜\" src=\"1f61c.png\"/>',fitzpatrick_scale:false,category:\"people\"},zany:{keywords:[\"face\",\"goofy\",\"crazy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤪\" src=\"1f92a.png\"/>',fitzpatrick_scale:false,category:\"people\"},raised_eyebrow:{keywords:[\"face\",\"distrust\",\"scepticism\",\"disapproval\",\"disbelief\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤨\" src=\"1f928.png\"/>',fitzpatrick_scale:false,category:\"people\"},monocle:{keywords:[\"face\",\"stuffy\",\"wealthy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧐\" src=\"1f9d0.png\"/>',fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_closed_eyes:{keywords:[\"face\",\"prank\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😝\" src=\"1f61d.png\"/>',fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😛\" src=\"1f61b.png\"/>',fitzpatrick_scale:false,category:\"people\"},money_mouth_face:{keywords:[\"face\",\"rich\",\"dollar\",\"money\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤑\" src=\"1f911.png\"/>',fitzpatrick_scale:false,category:\"people\"},nerd_face:{keywords:[\"face\",\"nerdy\",\"geek\",\"dork\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤓\" src=\"1f913.png\"/>',fitzpatrick_scale:false,category:\"people\"},sunglasses:{keywords:[\"face\",\"cool\",\"smile\",\"summer\",\"beach\",\"sunglass\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😎\" src=\"1f60e.png\"/>',fitzpatrick_scale:false,category:\"people\"},star_struck:{keywords:[\"face\",\"smile\",\"starry\",\"eyes\",\"grinning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤩\" src=\"1f929.png\"/>',fitzpatrick_scale:false,category:\"people\"},clown_face:{keywords:[\"face\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤡\" src=\"1f921.png\"/>',fitzpatrick_scale:false,category:\"people\"},cowboy_hat_face:{keywords:[\"face\",\"cowgirl\",\"hat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤠\" src=\"1f920.png\"/>',fitzpatrick_scale:false,category:\"people\"},hugs:{keywords:[\"face\",\"smile\",\"hug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤗\" src=\"1f917.png\"/>',fitzpatrick_scale:false,category:\"people\"},smirk:{keywords:[\"face\",\"smile\",\"mean\",\"prank\",\"smug\",\"sarcasm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😏\" src=\"1f60f.png\"/>',fitzpatrick_scale:false,category:\"people\"},no_mouth:{keywords:[\"face\",\"hellokitty\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😶\" src=\"1f636.png\"/>',fitzpatrick_scale:false,category:\"people\"},neutral_face:{keywords:[\"indifference\",\"meh\",\":|\",\"neutral\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😐\" src=\"1f610.png\"/>',fitzpatrick_scale:false,category:\"people\"},expressionless:{keywords:[\"face\",\"indifferent\",\"-_-\",\"meh\",\"deadpan\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😑\" src=\"1f611.png\"/>',fitzpatrick_scale:false,category:\"people\"},unamused:{keywords:[\"indifference\",\"bored\",\"straight face\",\"serious\",\"sarcasm\",\"unimpressed\",\"skeptical\",\"dubious\",\"side_eye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😒\" src=\"1f612.png\"/>',fitzpatrick_scale:false,category:\"people\"},roll_eyes:{keywords:[\"face\",\"eyeroll\",\"frustrated\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙄\" src=\"1f644.png\"/>',fitzpatrick_scale:false,category:\"people\"},thinking:{keywords:[\"face\",\"hmmm\",\"think\",\"consider\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤔\" src=\"1f914.png\"/>',fitzpatrick_scale:false,category:\"people\"},lying_face:{keywords:[\"face\",\"lie\",\"pinocchio\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤥\" src=\"1f925.png\"/>',fitzpatrick_scale:false,category:\"people\"},hand_over_mouth:{keywords:[\"face\",\"whoops\",\"shock\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤭\" src=\"1f92d.png\"/>',fitzpatrick_scale:false,category:\"people\"},shushing:{keywords:[\"face\",\"quiet\",\"shhh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤫\" src=\"1f92b.png\"/>',fitzpatrick_scale:false,category:\"people\"},symbols_over_mouth:{keywords:[\"face\",\"swearing\",\"cursing\",\"cussing\",\"profanity\",\"expletive\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤬\" src=\"1f92c.png\"/>',fitzpatrick_scale:false,category:\"people\"},exploding_head:{keywords:[\"face\",\"shocked\",\"mind\",\"blown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤯\" src=\"1f92f.png\"/>',fitzpatrick_scale:false,category:\"people\"},flushed:{keywords:[\"face\",\"blush\",\"shy\",\"flattered\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😳\" src=\"1f633.png\"/>',fitzpatrick_scale:false,category:\"people\"},disappointed:{keywords:[\"face\",\"sad\",\"upset\",\"depressed\",\":(\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😞\" src=\"1f61e.png\"/>',fitzpatrick_scale:false,category:\"people\"},worried:{keywords:[\"face\",\"concern\",\"nervous\",\":(\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😟\" src=\"1f61f.png\"/>',fitzpatrick_scale:false,category:\"people\"},angry:{keywords:[\"mad\",\"face\",\"annoyed\",\"frustrated\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😠\" src=\"1f620.png\"/>',fitzpatrick_scale:false,category:\"people\"},rage:{keywords:[\"angry\",\"mad\",\"hate\",\"despise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😡\" src=\"1f621.png\"/>',fitzpatrick_scale:false,category:\"people\"},pensive:{keywords:[\"face\",\"sad\",\"depressed\",\"upset\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😔\" src=\"1f614.png\"/>',fitzpatrick_scale:false,category:\"people\"},confused:{keywords:[\"face\",\"indifference\",\"huh\",\"weird\",\"hmmm\",\":/\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😕\" src=\"1f615.png\"/>',fitzpatrick_scale:false,category:\"people\"},slightly_frowning_face:{keywords:[\"face\",\"frowning\",\"disappointed\",\"sad\",\"upset\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙁\" src=\"1f641.png\"/>',fitzpatrick_scale:false,category:\"people\"},frowning_face:{keywords:[\"face\",\"sad\",\"upset\",\"frown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☹\" src=\"2639.png\"/>',fitzpatrick_scale:false,category:\"people\"},persevere:{keywords:[\"face\",\"sick\",\"no\",\"upset\",\"oops\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😣\" src=\"1f623.png\"/>',fitzpatrick_scale:false,category:\"people\"},confounded:{keywords:[\"face\",\"confused\",\"sick\",\"unwell\",\"oops\",\":S\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😖\" src=\"1f616.png\"/>',fitzpatrick_scale:false,category:\"people\"},tired_face:{keywords:[\"sick\",\"whine\",\"upset\",\"frustrated\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😫\" src=\"1f62b.png\"/>',fitzpatrick_scale:false,category:\"people\"},weary:{keywords:[\"face\",\"tired\",\"sleepy\",\"sad\",\"frustrated\",\"upset\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😩\" src=\"1f629.png\"/>',fitzpatrick_scale:false,category:\"people\"},pleading:{keywords:[\"face\",\"begging\",\"mercy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥺\" src=\"1f97a.png\"/>',fitzpatrick_scale:false,category:\"people\"},triumph:{keywords:[\"face\",\"gas\",\"phew\",\"proud\",\"pride\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😤\" src=\"1f624.png\"/>',fitzpatrick_scale:false,category:\"people\"},open_mouth:{keywords:[\"face\",\"surprise\",\"impressed\",\"wow\",\"whoa\",\":O\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😮\" src=\"1f62e.png\"/>',fitzpatrick_scale:false,category:\"people\"},scream:{keywords:[\"face\",\"munch\",\"scared\",\"omg\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😱\" src=\"1f631.png\"/>',fitzpatrick_scale:false,category:\"people\"},fearful:{keywords:[\"face\",\"scared\",\"terrified\",\"nervous\",\"oops\",\"huh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😨\" src=\"1f628.png\"/>',fitzpatrick_scale:false,category:\"people\"},cold_sweat:{keywords:[\"face\",\"nervous\",\"sweat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😰\" src=\"1f630.png\"/>',fitzpatrick_scale:false,category:\"people\"},hushed:{keywords:[\"face\",\"woo\",\"shh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😯\" src=\"1f62f.png\"/>',fitzpatrick_scale:false,category:\"people\"},frowning:{keywords:[\"face\",\"aw\",\"what\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😦\" src=\"1f626.png\"/>',fitzpatrick_scale:false,category:\"people\"},anguished:{keywords:[\"face\",\"stunned\",\"nervous\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😧\" src=\"1f627.png\"/>',fitzpatrick_scale:false,category:\"people\"},cry:{keywords:[\"face\",\"tears\",\"sad\",\"depressed\",\"upset\",\":'(\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😢\" src=\"1f622.png\"/>',fitzpatrick_scale:false,category:\"people\"},disappointed_relieved:{keywords:[\"face\",\"phew\",\"sweat\",\"nervous\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😥\" src=\"1f625.png\"/>',fitzpatrick_scale:false,category:\"people\"},drooling_face:{keywords:[\"face\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤤\" src=\"1f924.png\"/>',fitzpatrick_scale:false,category:\"people\"},sleepy:{keywords:[\"face\",\"tired\",\"rest\",\"nap\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😪\" src=\"1f62a.png\"/>',fitzpatrick_scale:false,category:\"people\"},sweat:{keywords:[\"face\",\"hot\",\"sad\",\"tired\",\"exercise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😓\" src=\"1f613.png\"/>',fitzpatrick_scale:false,category:\"people\"},hot:{keywords:[\"face\",\"feverish\",\"heat\",\"red\",\"sweating\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥵\" src=\"1f975.png\"/>',fitzpatrick_scale:false,category:\"people\"},cold:{keywords:[\"face\",\"blue\",\"freezing\",\"frozen\",\"frostbite\",\"icicles\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥶\" src=\"1f976.png\"/>',fitzpatrick_scale:false,category:\"people\"},sob:{keywords:[\"face\",\"cry\",\"tears\",\"sad\",\"upset\",\"depressed\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😭\" src=\"1f62d.png\"/>',fitzpatrick_scale:false,category:\"people\"},dizzy_face:{keywords:[\"spent\",\"unconscious\",\"xox\",\"dizzy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😵\" src=\"1f635.png\"/>',fitzpatrick_scale:false,category:\"people\"},astonished:{keywords:[\"face\",\"xox\",\"surprised\",\"poisoned\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😲\" src=\"1f632.png\"/>',fitzpatrick_scale:false,category:\"people\"},zipper_mouth_face:{keywords:[\"face\",\"sealed\",\"zipper\",\"secret\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤐\" src=\"1f910.png\"/>',fitzpatrick_scale:false,category:\"people\"},nauseated_face:{keywords:[\"face\",\"vomit\",\"gross\",\"green\",\"sick\",\"throw up\",\"ill\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤢\" src=\"1f922.png\"/>',fitzpatrick_scale:false,category:\"people\"},sneezing_face:{keywords:[\"face\",\"gesundheit\",\"sneeze\",\"sick\",\"allergy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤧\" src=\"1f927.png\"/>',fitzpatrick_scale:false,category:\"people\"},vomiting:{keywords:[\"face\",\"sick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤮\" src=\"1f92e.png\"/>',fitzpatrick_scale:false,category:\"people\"},mask:{keywords:[\"face\",\"sick\",\"ill\",\"disease\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😷\" src=\"1f637.png\"/>',fitzpatrick_scale:false,category:\"people\"},face_with_thermometer:{keywords:[\"sick\",\"temperature\",\"thermometer\",\"cold\",\"fever\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤒\" src=\"1f912.png\"/>',fitzpatrick_scale:false,category:\"people\"},face_with_head_bandage:{keywords:[\"injured\",\"clumsy\",\"bandage\",\"hurt\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤕\" src=\"1f915.png\"/>',fitzpatrick_scale:false,category:\"people\"},woozy:{keywords:[\"face\",\"dizzy\",\"intoxicated\",\"tipsy\",\"wavy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥴\" src=\"1f974.png\"/>',fitzpatrick_scale:false,category:\"people\"},sleeping:{keywords:[\"face\",\"tired\",\"sleepy\",\"night\",\"zzz\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😴\" src=\"1f634.png\"/>',fitzpatrick_scale:false,category:\"people\"},zzz:{keywords:[\"sleepy\",\"tired\",\"dream\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💤\" src=\"1f4a4.png\"/>',fitzpatrick_scale:false,category:\"people\"},poop:{keywords:[\"hankey\",\"shitface\",\"fail\",\"turd\",\"shit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💩\" src=\"1f4a9.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiling_imp:{keywords:[\"devil\",\"horns\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😈\" src=\"1f608.png\"/>',fitzpatrick_scale:false,category:\"people\"},imp:{keywords:[\"devil\",\"angry\",\"horns\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👿\" src=\"1f47f.png\"/>',fitzpatrick_scale:false,category:\"people\"},japanese_ogre:{keywords:[\"monster\",\"red\",\"mask\",\"halloween\",\"scary\",\"creepy\",\"devil\",\"demon\",\"japanese\",\"ogre\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👹\" src=\"1f479.png\"/>',fitzpatrick_scale:false,category:\"people\"},japanese_goblin:{keywords:[\"red\",\"evil\",\"mask\",\"monster\",\"scary\",\"creepy\",\"japanese\",\"goblin\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👺\" src=\"1f47a.png\"/>',fitzpatrick_scale:false,category:\"people\"},skull:{keywords:[\"dead\",\"skeleton\",\"creepy\",\"death\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💀\" src=\"1f480.png\"/>',fitzpatrick_scale:false,category:\"people\"},ghost:{keywords:[\"halloween\",\"spooky\",\"scary\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👻\" src=\"1f47b.png\"/>',fitzpatrick_scale:false,category:\"people\"},alien:{keywords:[\"UFO\",\"paul\",\"weird\",\"outer_space\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👽\" src=\"1f47d.png\"/>',fitzpatrick_scale:false,category:\"people\"},robot:{keywords:[\"computer\",\"machine\",\"bot\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤖\" src=\"1f916.png\"/>',fitzpatrick_scale:false,category:\"people\"},smiley_cat:{keywords:[\"animal\",\"cats\",\"happy\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😺\" src=\"1f63a.png\"/>',fitzpatrick_scale:false,category:\"people\"},smile_cat:{keywords:[\"animal\",\"cats\",\"smile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😸\" src=\"1f638.png\"/>',fitzpatrick_scale:false,category:\"people\"},joy_cat:{keywords:[\"animal\",\"cats\",\"haha\",\"happy\",\"tears\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😹\" src=\"1f639.png\"/>',fitzpatrick_scale:false,category:\"people\"},heart_eyes_cat:{keywords:[\"animal\",\"love\",\"like\",\"affection\",\"cats\",\"valentines\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😻\" src=\"1f63b.png\"/>',fitzpatrick_scale:false,category:\"people\"},smirk_cat:{keywords:[\"animal\",\"cats\",\"smirk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😼\" src=\"1f63c.png\"/>',fitzpatrick_scale:false,category:\"people\"},kissing_cat:{keywords:[\"animal\",\"cats\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😽\" src=\"1f63d.png\"/>',fitzpatrick_scale:false,category:\"people\"},scream_cat:{keywords:[\"animal\",\"cats\",\"munch\",\"scared\",\"scream\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙀\" src=\"1f640.png\"/>',fitzpatrick_scale:false,category:\"people\"},crying_cat_face:{keywords:[\"animal\",\"tears\",\"weep\",\"sad\",\"cats\",\"upset\",\"cry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😿\" src=\"1f63f.png\"/>',fitzpatrick_scale:false,category:\"people\"},pouting_cat:{keywords:[\"animal\",\"cats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"😾\" src=\"1f63e.png\"/>',fitzpatrick_scale:false,category:\"people\"},palms_up:{keywords:[\"hands\",\"gesture\",\"cupped\",\"prayer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤲\" src=\"1f932.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_hands:{keywords:[\"gesture\",\"hooray\",\"yea\",\"celebration\",\"hands\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙌\" src=\"1f64c.png\"/>',fitzpatrick_scale:true,category:\"people\"},clap:{keywords:[\"hands\",\"praise\",\"applause\",\"congrats\",\"yay\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👏\" src=\"1f44f.png\"/>',fitzpatrick_scale:true,category:\"people\"},wave:{keywords:[\"hands\",\"gesture\",\"goodbye\",\"solong\",\"farewell\",\"hello\",\"hi\",\"palm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👋\" src=\"1f44b.png\"/>',fitzpatrick_scale:true,category:\"people\"},call_me_hand:{keywords:[\"hands\",\"gesture\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤙\" src=\"1f919.png\"/>',fitzpatrick_scale:true,category:\"people\"},\"+1\":{keywords:[\"thumbsup\",\"yes\",\"awesome\",\"good\",\"agree\",\"accept\",\"cool\",\"hand\",\"like\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👍\" src=\"1f44d.png\"/>',fitzpatrick_scale:true,category:\"people\"},\"-1\":{keywords:[\"thumbsdown\",\"no\",\"dislike\",\"hand\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👎\" src=\"1f44e.png\"/>',fitzpatrick_scale:true,category:\"people\"},facepunch:{keywords:[\"angry\",\"violence\",\"fist\",\"hit\",\"attack\",\"hand\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👊\" src=\"1f44a.png\"/>',fitzpatrick_scale:true,category:\"people\"},fist:{keywords:[\"fingers\",\"hand\",\"grasp\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✊\" src=\"270a.png\"/>',fitzpatrick_scale:true,category:\"people\"},fist_left:{keywords:[\"hand\",\"fistbump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤛\" src=\"1f91b.png\"/>',fitzpatrick_scale:true,category:\"people\"},fist_right:{keywords:[\"hand\",\"fistbump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤜\" src=\"1f91c.png\"/>',fitzpatrick_scale:true,category:\"people\"},v:{keywords:[\"fingers\",\"ohyeah\",\"hand\",\"peace\",\"victory\",\"two\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✌\" src=\"270c.png\"/>',fitzpatrick_scale:true,category:\"people\"},ok_hand:{keywords:[\"fingers\",\"limbs\",\"perfect\",\"ok\",\"okay\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👌\" src=\"1f44c.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_hand:{keywords:[\"fingers\",\"stop\",\"highfive\",\"palm\",\"ban\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✋\" src=\"270b.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_back_of_hand:{keywords:[\"fingers\",\"raised\",\"backhand\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤚\" src=\"1f91a.png\"/>',fitzpatrick_scale:true,category:\"people\"},open_hands:{keywords:[\"fingers\",\"butterfly\",\"hands\",\"open\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👐\" src=\"1f450.png\"/>',fitzpatrick_scale:true,category:\"people\"},muscle:{keywords:[\"arm\",\"flex\",\"hand\",\"summer\",\"strong\",\"biceps\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💪\" src=\"1f4aa.png\"/>',fitzpatrick_scale:true,category:\"people\"},pray:{keywords:[\"please\",\"hope\",\"wish\",\"namaste\",\"highfive\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙏\" src=\"1f64f.png\"/>',fitzpatrick_scale:true,category:\"people\"},foot:{keywords:[\"kick\",\"stomp\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦶\" src=\"1f9b6.png\"/>',fitzpatrick_scale:true,category:\"people\"},leg:{keywords:[\"kick\",\"limb\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦵\" src=\"1f9b5.png\"/>',fitzpatrick_scale:true,category:\"people\"},handshake:{keywords:[\"agreement\",\"shake\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤝\" src=\"1f91d.png\"/>',fitzpatrick_scale:false,category:\"people\"},point_up:{keywords:[\"hand\",\"fingers\",\"direction\",\"up\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☝\" src=\"261d.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_up_2:{keywords:[\"fingers\",\"hand\",\"direction\",\"up\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👆\" src=\"1f446.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_down:{keywords:[\"fingers\",\"hand\",\"direction\",\"down\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👇\" src=\"1f447.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_left:{keywords:[\"direction\",\"fingers\",\"hand\",\"left\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👈\" src=\"1f448.png\"/>',fitzpatrick_scale:true,category:\"people\"},point_right:{keywords:[\"fingers\",\"hand\",\"direction\",\"right\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👉\" src=\"1f449.png\"/>',fitzpatrick_scale:true,category:\"people\"},fu:{keywords:[\"hand\",\"fingers\",\"rude\",\"middle\",\"flipping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖕\" src=\"1f595.png\"/>',fitzpatrick_scale:true,category:\"people\"},raised_hand_with_fingers_splayed:{keywords:[\"hand\",\"fingers\",\"palm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖐\" src=\"1f590.png\"/>',fitzpatrick_scale:true,category:\"people\"},love_you:{keywords:[\"hand\",\"fingers\",\"gesture\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤟\" src=\"1f91f.png\"/>',fitzpatrick_scale:true,category:\"people\"},metal:{keywords:[\"hand\",\"fingers\",\"evil_eye\",\"sign_of_horns\",\"rock_on\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤘\" src=\"1f918.png\"/>',fitzpatrick_scale:true,category:\"people\"},crossed_fingers:{keywords:[\"good\",\"lucky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤞\" src=\"1f91e.png\"/>',fitzpatrick_scale:true,category:\"people\"},vulcan_salute:{keywords:[\"hand\",\"fingers\",\"spock\",\"star trek\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖖\" src=\"1f596.png\"/>',fitzpatrick_scale:true,category:\"people\"},writing_hand:{keywords:[\"lower_left_ballpoint_pen\",\"stationery\",\"write\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✍\" src=\"270d.png\"/>',fitzpatrick_scale:true,category:\"people\"},selfie:{keywords:[\"camera\",\"phone\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤳\" src=\"1f933.png\"/>',fitzpatrick_scale:true,category:\"people\"},nail_care:{keywords:[\"beauty\",\"manicure\",\"finger\",\"fashion\",\"nail\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💅\" src=\"1f485.png\"/>',fitzpatrick_scale:true,category:\"people\"},lips:{keywords:[\"mouth\",\"kiss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👄\" src=\"1f444.png\"/>',fitzpatrick_scale:false,category:\"people\"},tooth:{keywords:[\"teeth\",\"dentist\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦷\" src=\"1f9b7.png\"/>',fitzpatrick_scale:false,category:\"people\"},tongue:{keywords:[\"mouth\",\"playful\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👅\" src=\"1f445.png\"/>',fitzpatrick_scale:false,category:\"people\"},ear:{keywords:[\"face\",\"hear\",\"sound\",\"listen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👂\" src=\"1f442.png\"/>',fitzpatrick_scale:true,category:\"people\"},nose:{keywords:[\"smell\",\"sniff\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👃\" src=\"1f443.png\"/>',fitzpatrick_scale:true,category:\"people\"},eye:{keywords:[\"face\",\"look\",\"see\",\"watch\",\"stare\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👁\" src=\"1f441.png\"/>',fitzpatrick_scale:false,category:\"people\"},eyes:{keywords:[\"look\",\"watch\",\"stalk\",\"peek\",\"see\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👀\" src=\"1f440.png\"/>',fitzpatrick_scale:false,category:\"people\"},brain:{keywords:[\"smart\",\"intelligent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧠\" src=\"1f9e0.png\"/>',fitzpatrick_scale:false,category:\"people\"},bust_in_silhouette:{keywords:[\"user\",\"person\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👤\" src=\"1f464.png\"/>',fitzpatrick_scale:false,category:\"people\"},busts_in_silhouette:{keywords:[\"user\",\"person\",\"human\",\"group\",\"team\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👥\" src=\"1f465.png\"/>',fitzpatrick_scale:false,category:\"people\"},speaking_head:{keywords:[\"user\",\"person\",\"human\",\"sing\",\"say\",\"talk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗣\" src=\"1f5e3.png\"/>',fitzpatrick_scale:false,category:\"people\"},baby:{keywords:[\"child\",\"boy\",\"girl\",\"toddler\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👶\" src=\"1f476.png\"/>',fitzpatrick_scale:true,category:\"people\"},child:{keywords:[\"gender-neutral\",\"young\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧒\" src=\"1f9d2.png\"/>',fitzpatrick_scale:true,category:\"people\"},boy:{keywords:[\"man\",\"male\",\"guy\",\"teenager\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👦\" src=\"1f466.png\"/>',fitzpatrick_scale:true,category:\"people\"},girl:{keywords:[\"female\",\"woman\",\"teenager\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👧\" src=\"1f467.png\"/>',fitzpatrick_scale:true,category:\"people\"},adult:{keywords:[\"gender-neutral\",\"person\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧑\" src=\"1f9d1.png\"/>',fitzpatrick_scale:true,category:\"people\"},man:{keywords:[\"mustache\",\"father\",\"dad\",\"guy\",\"classy\",\"sir\",\"moustache\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨\" src=\"1f468.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman:{keywords:[\"female\",\"girls\",\"lady\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩\" src=\"1f469.png\"/>',fitzpatrick_scale:true,category:\"people\"},blonde_woman:{keywords:[\"woman\",\"female\",\"girl\",\"blonde\",\"person\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👱‍♀️\" src=\"1f471-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},blonde_man:{keywords:[\"man\",\"male\",\"boy\",\"blonde\",\"guy\",\"person\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👱\" src=\"1f471.png\"/>',fitzpatrick_scale:true,category:\"people\"},bearded_person:{keywords:[\"person\",\"bewhiskered\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧔\" src=\"1f9d4.png\"/>',fitzpatrick_scale:true,category:\"people\"},older_adult:{keywords:[\"human\",\"elder\",\"senior\",\"gender-neutral\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧓\" src=\"1f9d3.png\"/>',fitzpatrick_scale:true,category:\"people\"},older_man:{keywords:[\"human\",\"male\",\"men\",\"old\",\"elder\",\"senior\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👴\" src=\"1f474.png\"/>',fitzpatrick_scale:true,category:\"people\"},older_woman:{keywords:[\"human\",\"female\",\"women\",\"lady\",\"old\",\"elder\",\"senior\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👵\" src=\"1f475.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_with_gua_pi_mao:{keywords:[\"male\",\"boy\",\"chinese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👲\" src=\"1f472.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_with_headscarf:{keywords:[\"female\",\"hijab\",\"mantilla\",\"tichel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧕\" src=\"1f9d5.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_with_turban:{keywords:[\"female\",\"indian\",\"hinduism\",\"arabs\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👳‍♀️\" src=\"1f473-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_with_turban:{keywords:[\"male\",\"indian\",\"hinduism\",\"arabs\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👳\" src=\"1f473.png\"/>',fitzpatrick_scale:true,category:\"people\"},policewoman:{keywords:[\"woman\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👮‍♀️\" src=\"1f46e-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},policeman:{keywords:[\"man\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👮\" src=\"1f46e.png\"/>',fitzpatrick_scale:true,category:\"people\"},construction_worker_woman:{keywords:[\"female\",\"human\",\"wip\",\"build\",\"construction\",\"worker\",\"labor\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👷‍♀️\" src=\"1f477-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},construction_worker_man:{keywords:[\"male\",\"human\",\"wip\",\"guy\",\"build\",\"construction\",\"worker\",\"labor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👷\" src=\"1f477.png\"/>',fitzpatrick_scale:true,category:\"people\"},guardswoman:{keywords:[\"uk\",\"gb\",\"british\",\"female\",\"royal\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💂‍♀️\" src=\"1f482-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},guardsman:{keywords:[\"uk\",\"gb\",\"british\",\"male\",\"guy\",\"royal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💂\" src=\"1f482.png\"/>',fitzpatrick_scale:true,category:\"people\"},female_detective:{keywords:[\"human\",\"spy\",\"detective\",\"female\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕵️‍♀️\" src=\"1f575-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},male_detective:{keywords:[\"human\",\"spy\",\"detective\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕵\" src=\"1f575.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍⚕️\" src=\"1f469-200d-2695-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍⚕️\" src=\"1f468-200d-2695-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_farmer:{keywords:[\"rancher\",\"gardener\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🌾\" src=\"1f469-200d-1f33e.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_farmer:{keywords:[\"rancher\",\"gardener\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🌾\" src=\"1f468-200d-1f33e.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_cook:{keywords:[\"chef\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🍳\" src=\"1f469-200d-1f373.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_cook:{keywords:[\"chef\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🍳\" src=\"1f468-200d-1f373.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_student:{keywords:[\"graduate\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🎓\" src=\"1f469-200d-1f393.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_student:{keywords:[\"graduate\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🎓\" src=\"1f468-200d-1f393.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_singer:{keywords:[\"rockstar\",\"entertainer\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🎤\" src=\"1f469-200d-1f3a4.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_singer:{keywords:[\"rockstar\",\"entertainer\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🎤\" src=\"1f468-200d-1f3a4.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_teacher:{keywords:[\"instructor\",\"professor\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🏫\" src=\"1f469-200d-1f3eb.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_teacher:{keywords:[\"instructor\",\"professor\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🏫\" src=\"1f468-200d-1f3eb.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_factory_worker:{keywords:[\"assembly\",\"industrial\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🏭\" src=\"1f469-200d-1f3ed.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_factory_worker:{keywords:[\"assembly\",\"industrial\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🏭\" src=\"1f468-200d-1f3ed.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"woman\",\"human\",\"laptop\",\"computer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍💻\" src=\"1f469-200d-1f4bb.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"man\",\"human\",\"laptop\",\"computer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍💻\" src=\"1f468-200d-1f4bb.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_office_worker:{keywords:[\"business\",\"manager\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍💼\" src=\"1f469-200d-1f4bc.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_office_worker:{keywords:[\"business\",\"manager\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍💼\" src=\"1f468-200d-1f4bc.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_mechanic:{keywords:[\"plumber\",\"woman\",\"human\",\"wrench\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🔧\" src=\"1f469-200d-1f527.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_mechanic:{keywords:[\"plumber\",\"man\",\"human\",\"wrench\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🔧\" src=\"1f468-200d-1f527.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🔬\" src=\"1f469-200d-1f52c.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🔬\" src=\"1f468-200d-1f52c.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_artist:{keywords:[\"painter\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🎨\" src=\"1f469-200d-1f3a8.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_artist:{keywords:[\"painter\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🎨\" src=\"1f468-200d-1f3a8.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_firefighter:{keywords:[\"fireman\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🚒\" src=\"1f469-200d-1f692.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_firefighter:{keywords:[\"fireman\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🚒\" src=\"1f468-200d-1f692.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_pilot:{keywords:[\"aviator\",\"plane\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍✈️\" src=\"1f469-200d-2708-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_pilot:{keywords:[\"aviator\",\"plane\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍✈️\" src=\"1f468-200d-2708-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_astronaut:{keywords:[\"space\",\"rocket\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍🚀\" src=\"1f469-200d-1f680.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_astronaut:{keywords:[\"space\",\"rocket\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍🚀\" src=\"1f468-200d-1f680.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_judge:{keywords:[\"justice\",\"court\",\"woman\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍⚖️\" src=\"1f469-200d-2696-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_judge:{keywords:[\"justice\",\"court\",\"man\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍⚖️\" src=\"1f468-200d-2696-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_superhero:{keywords:[\"woman\",\"female\",\"good\",\"heroine\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦸‍♀️\" src=\"1f9b8-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_superhero:{keywords:[\"man\",\"male\",\"good\",\"hero\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦸‍♂️\" src=\"1f9b8-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_supervillain:{keywords:[\"woman\",\"female\",\"evil\",\"bad\",\"criminal\",\"heroine\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦹‍♀️\" src=\"1f9b9-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_supervillain:{keywords:[\"man\",\"male\",\"evil\",\"bad\",\"criminal\",\"hero\",\"superpowers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦹‍♂️\" src=\"1f9b9-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},mrs_claus:{keywords:[\"woman\",\"female\",\"xmas\",\"mother christmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤶\" src=\"1f936.png\"/>',fitzpatrick_scale:true,category:\"people\"},santa:{keywords:[\"festival\",\"man\",\"male\",\"xmas\",\"father christmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎅\" src=\"1f385.png\"/>',fitzpatrick_scale:true,category:\"people\"},sorceress:{keywords:[\"woman\",\"female\",\"mage\",\"witch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧙‍♀️\" src=\"1f9d9-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},wizard:{keywords:[\"man\",\"male\",\"mage\",\"sorcerer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧙‍♂️\" src=\"1f9d9-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_elf:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧝‍♀️\" src=\"1f9dd-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_elf:{keywords:[\"man\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧝‍♂️\" src=\"1f9dd-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_vampire:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧛‍♀️\" src=\"1f9db-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_vampire:{keywords:[\"man\",\"male\",\"dracula\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧛‍♂️\" src=\"1f9db-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_zombie:{keywords:[\"woman\",\"female\",\"undead\",\"walking dead\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧟‍♀️\" src=\"1f9df-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},man_zombie:{keywords:[\"man\",\"male\",\"dracula\",\"undead\",\"walking dead\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧟‍♂️\" src=\"1f9df-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},woman_genie:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧞‍♀️\" src=\"1f9de-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},man_genie:{keywords:[\"man\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧞‍♂️\" src=\"1f9de-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},mermaid:{keywords:[\"woman\",\"female\",\"merwoman\",\"ariel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧜‍♀️\" src=\"1f9dc-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},merman:{keywords:[\"man\",\"male\",\"triton\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧜‍♂️\" src=\"1f9dc-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_fairy:{keywords:[\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧚‍♀️\" src=\"1f9da-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_fairy:{keywords:[\"man\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧚‍♂️\" src=\"1f9da-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},angel:{keywords:[\"heaven\",\"wings\",\"halo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👼\" src=\"1f47c.png\"/>',fitzpatrick_scale:true,category:\"people\"},pregnant_woman:{keywords:[\"baby\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤰\" src=\"1f930.png\"/>',fitzpatrick_scale:true,category:\"people\"},breastfeeding:{keywords:[\"nursing\",\"baby\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤱\" src=\"1f931.png\"/>',fitzpatrick_scale:true,category:\"people\"},princess:{keywords:[\"girl\",\"woman\",\"female\",\"blond\",\"crown\",\"royal\",\"queen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👸\" src=\"1f478.png\"/>',fitzpatrick_scale:true,category:\"people\"},prince:{keywords:[\"boy\",\"man\",\"male\",\"crown\",\"royal\",\"king\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤴\" src=\"1f934.png\"/>',fitzpatrick_scale:true,category:\"people\"},bride_with_veil:{keywords:[\"couple\",\"marriage\",\"wedding\",\"woman\",\"bride\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👰\" src=\"1f470.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_in_tuxedo:{keywords:[\"couple\",\"marriage\",\"wedding\",\"groom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤵\" src=\"1f935.png\"/>',fitzpatrick_scale:true,category:\"people\"},running_woman:{keywords:[\"woman\",\"walking\",\"exercise\",\"race\",\"running\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏃‍♀️\" src=\"1f3c3-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},running_man:{keywords:[\"man\",\"walking\",\"exercise\",\"race\",\"running\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏃\" src=\"1f3c3.png\"/>',fitzpatrick_scale:true,category:\"people\"},walking_woman:{keywords:[\"human\",\"feet\",\"steps\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚶‍♀️\" src=\"1f6b6-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},walking_man:{keywords:[\"human\",\"feet\",\"steps\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚶\" src=\"1f6b6.png\"/>',fitzpatrick_scale:true,category:\"people\"},dancer:{keywords:[\"female\",\"girl\",\"woman\",\"fun\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💃\" src=\"1f483.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_dancing:{keywords:[\"male\",\"boy\",\"fun\",\"dancer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕺\" src=\"1f57a.png\"/>',fitzpatrick_scale:true,category:\"people\"},dancing_women:{keywords:[\"female\",\"bunny\",\"women\",\"girls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👯\" src=\"1f46f.png\"/>',fitzpatrick_scale:false,category:\"people\"},dancing_men:{keywords:[\"male\",\"bunny\",\"men\",\"boys\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👯‍♂️\" src=\"1f46f-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"people\"},couple:{keywords:[\"pair\",\"people\",\"human\",\"love\",\"date\",\"dating\",\"like\",\"affection\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👫\" src=\"1f46b.png\"/>',fitzpatrick_scale:false,category:\"people\"},two_men_holding_hands:{keywords:[\"pair\",\"couple\",\"love\",\"like\",\"bromance\",\"friendship\",\"people\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👬\" src=\"1f46c.png\"/>',fitzpatrick_scale:false,category:\"people\"},two_women_holding_hands:{keywords:[\"pair\",\"friendship\",\"couple\",\"love\",\"like\",\"female\",\"people\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👭\" src=\"1f46d.png\"/>',fitzpatrick_scale:false,category:\"people\"},bowing_woman:{keywords:[\"woman\",\"female\",\"girl\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙇‍♀️\" src=\"1f647-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},bowing_man:{keywords:[\"man\",\"male\",\"boy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙇\" src=\"1f647.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_facepalming:{keywords:[\"man\",\"male\",\"boy\",\"disbelief\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤦‍♂️\" src=\"1f926-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_facepalming:{keywords:[\"woman\",\"female\",\"girl\",\"disbelief\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤦‍♀️\" src=\"1f926-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_shrugging:{keywords:[\"woman\",\"female\",\"girl\",\"confused\",\"indifferent\",\"doubt\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤷\" src=\"1f937.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_shrugging:{keywords:[\"man\",\"male\",\"boy\",\"confused\",\"indifferent\",\"doubt\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤷‍♂️\" src=\"1f937-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},tipping_hand_woman:{keywords:[\"female\",\"girl\",\"woman\",\"human\",\"information\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💁\" src=\"1f481.png\"/>',fitzpatrick_scale:true,category:\"people\"},tipping_hand_man:{keywords:[\"male\",\"boy\",\"man\",\"human\",\"information\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💁‍♂️\" src=\"1f481-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},no_good_woman:{keywords:[\"female\",\"girl\",\"woman\",\"nope\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙅\" src=\"1f645.png\"/>',fitzpatrick_scale:true,category:\"people\"},no_good_man:{keywords:[\"male\",\"boy\",\"man\",\"nope\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙅‍♂️\" src=\"1f645-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},ok_woman:{keywords:[\"women\",\"girl\",\"female\",\"pink\",\"human\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙆\" src=\"1f646.png\"/>',fitzpatrick_scale:true,category:\"people\"},ok_man:{keywords:[\"men\",\"boy\",\"male\",\"blue\",\"human\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙆‍♂️\" src=\"1f646-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},raising_hand_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙋\" src=\"1f64b.png\"/>',fitzpatrick_scale:true,category:\"people\"},raising_hand_man:{keywords:[\"male\",\"boy\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙋‍♂️\" src=\"1f64b-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},pouting_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙎\" src=\"1f64e.png\"/>',fitzpatrick_scale:true,category:\"people\"},pouting_man:{keywords:[\"male\",\"boy\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙎‍♂️\" src=\"1f64e-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},frowning_woman:{keywords:[\"female\",\"girl\",\"woman\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙍\" src=\"1f64d.png\"/>',fitzpatrick_scale:true,category:\"people\"},frowning_man:{keywords:[\"male\",\"boy\",\"man\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙍‍♂️\" src=\"1f64d-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},haircut_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💇\" src=\"1f487.png\"/>',fitzpatrick_scale:true,category:\"people\"},haircut_man:{keywords:[\"male\",\"boy\",\"man\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💇‍♂️\" src=\"1f487-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},massage_woman:{keywords:[\"female\",\"girl\",\"woman\",\"head\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💆\" src=\"1f486.png\"/>',fitzpatrick_scale:true,category:\"people\"},massage_man:{keywords:[\"male\",\"boy\",\"man\",\"head\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💆‍♂️\" src=\"1f486-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},woman_in_steamy_room:{keywords:[\"female\",\"woman\",\"spa\",\"steamroom\",\"sauna\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧖‍♀️\" src=\"1f9d6-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},man_in_steamy_room:{keywords:[\"male\",\"man\",\"spa\",\"steamroom\",\"sauna\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧖‍♂️\" src=\"1f9d6-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"people\"},couple_with_heart_woman_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💑\" src=\"1f491.png\"/>',fitzpatrick_scale:false,category:\"people\"},couple_with_heart_woman_woman:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍❤️‍👩\" src=\"1f469-200d-2764-fe0f-200d-1f469.png\"/>',fitzpatrick_scale:false,category:\"people\"},couple_with_heart_man_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍❤️‍👨\" src=\"1f468-200d-2764-fe0f-200d-1f468.png\"/>',fitzpatrick_scale:false,category:\"people\"},couplekiss_man_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💏\" src=\"1f48f.png\"/>',fitzpatrick_scale:false,category:\"people\"},couplekiss_woman_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍❤️‍💋‍👩\" src=\"1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png\"/>',fitzpatrick_scale:false,category:\"people\"},couplekiss_man_man:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍❤️‍💋‍👨\" src=\"1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy:{keywords:[\"home\",\"parents\",\"child\",\"mom\",\"dad\",\"father\",\"mother\",\"people\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👪\" src=\"1f46a.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👧\" src=\"1f468-200d-1f469-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👧‍👦\" src=\"1f468-200d-1f469-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👦‍👦\" src=\"1f468-200d-1f469-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👩‍👧‍👧\" src=\"1f468-200d-1f469-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👦\" src=\"1f469-200d-1f469-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👧\" src=\"1f469-200d-1f469-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👧‍👦\" src=\"1f469-200d-1f469-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👦‍👦\" src=\"1f469-200d-1f469-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👩‍👧‍👧\" src=\"1f469-200d-1f469-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👦\" src=\"1f468-200d-1f468-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👧\" src=\"1f468-200d-1f468-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👧‍👦\" src=\"1f468-200d-1f468-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👦‍👦\" src=\"1f468-200d-1f468-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👨‍👧‍👧\" src=\"1f468-200d-1f468-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👦\" src=\"1f469-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👧\" src=\"1f469-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👧‍👦\" src=\"1f469-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👦‍👦\" src=\"1f469-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_woman_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👩‍👧‍👧\" src=\"1f469-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👦\" src=\"1f468-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👧\" src=\"1f468-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👧‍👦\" src=\"1f468-200d-1f467-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👦‍👦\" src=\"1f468-200d-1f466-200d-1f466.png\"/>',fitzpatrick_scale:false,category:\"people\"},family_man_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👨‍👧‍👧\" src=\"1f468-200d-1f467-200d-1f467.png\"/>',fitzpatrick_scale:false,category:\"people\"},yarn:{keywords:[\"ball\",\"crochet\",\"knit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧶\" src=\"1f9f6.png\"/>',fitzpatrick_scale:false,category:\"people\"},thread:{keywords:[\"needle\",\"sewing\",\"spool\",\"string\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧵\" src=\"1f9f5.png\"/>',fitzpatrick_scale:false,category:\"people\"},coat:{keywords:[\"jacket\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧥\" src=\"1f9e5.png\"/>',fitzpatrick_scale:false,category:\"people\"},labcoat:{keywords:[\"doctor\",\"experiment\",\"scientist\",\"chemist\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥼\" src=\"1f97c.png\"/>',fitzpatrick_scale:false,category:\"people\"},womans_clothes:{keywords:[\"fashion\",\"shopping_bags\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👚\" src=\"1f45a.png\"/>',fitzpatrick_scale:false,category:\"people\"},tshirt:{keywords:[\"fashion\",\"cloth\",\"casual\",\"shirt\",\"tee\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👕\" src=\"1f455.png\"/>',fitzpatrick_scale:false,category:\"people\"},jeans:{keywords:[\"fashion\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👖\" src=\"1f456.png\"/>',fitzpatrick_scale:false,category:\"people\"},necktie:{keywords:[\"shirt\",\"suitup\",\"formal\",\"fashion\",\"cloth\",\"business\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👔\" src=\"1f454.png\"/>',fitzpatrick_scale:false,category:\"people\"},dress:{keywords:[\"clothes\",\"fashion\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👗\" src=\"1f457.png\"/>',fitzpatrick_scale:false,category:\"people\"},bikini:{keywords:[\"swimming\",\"female\",\"woman\",\"girl\",\"fashion\",\"beach\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👙\" src=\"1f459.png\"/>',fitzpatrick_scale:false,category:\"people\"},kimono:{keywords:[\"dress\",\"fashion\",\"women\",\"female\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👘\" src=\"1f458.png\"/>',fitzpatrick_scale:false,category:\"people\"},lipstick:{keywords:[\"female\",\"girl\",\"fashion\",\"woman\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💄\" src=\"1f484.png\"/>',fitzpatrick_scale:false,category:\"people\"},kiss:{keywords:[\"face\",\"lips\",\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💋\" src=\"1f48b.png\"/>',fitzpatrick_scale:false,category:\"people\"},footprints:{keywords:[\"feet\",\"tracking\",\"walking\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👣\" src=\"1f463.png\"/>',fitzpatrick_scale:false,category:\"people\"},flat_shoe:{keywords:[\"ballet\",\"slip-on\",\"slipper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥿\" src=\"1f97f.png\"/>',fitzpatrick_scale:false,category:\"people\"},high_heel:{keywords:[\"fashion\",\"shoes\",\"female\",\"pumps\",\"stiletto\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👠\" src=\"1f460.png\"/>',fitzpatrick_scale:false,category:\"people\"},sandal:{keywords:[\"shoes\",\"fashion\",\"flip flops\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👡\" src=\"1f461.png\"/>',fitzpatrick_scale:false,category:\"people\"},boot:{keywords:[\"shoes\",\"fashion\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👢\" src=\"1f462.png\"/>',fitzpatrick_scale:false,category:\"people\"},mans_shoe:{keywords:[\"fashion\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👞\" src=\"1f45e.png\"/>',fitzpatrick_scale:false,category:\"people\"},athletic_shoe:{keywords:[\"shoes\",\"sports\",\"sneakers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👟\" src=\"1f45f.png\"/>',fitzpatrick_scale:false,category:\"people\"},hiking_boot:{keywords:[\"backpacking\",\"camping\",\"hiking\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥾\" src=\"1f97e.png\"/>',fitzpatrick_scale:false,category:\"people\"},socks:{keywords:[\"stockings\",\"clothes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧦\" src=\"1f9e6.png\"/>',fitzpatrick_scale:false,category:\"people\"},gloves:{keywords:[\"hands\",\"winter\",\"clothes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧤\" src=\"1f9e4.png\"/>',fitzpatrick_scale:false,category:\"people\"},scarf:{keywords:[\"neck\",\"winter\",\"clothes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧣\" src=\"1f9e3.png\"/>',fitzpatrick_scale:false,category:\"people\"},womans_hat:{keywords:[\"fashion\",\"accessories\",\"female\",\"lady\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👒\" src=\"1f452.png\"/>',fitzpatrick_scale:false,category:\"people\"},tophat:{keywords:[\"magic\",\"gentleman\",\"classy\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎩\" src=\"1f3a9.png\"/>',fitzpatrick_scale:false,category:\"people\"},billed_hat:{keywords:[\"cap\",\"baseball\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧢\" src=\"1f9e2.png\"/>',fitzpatrick_scale:false,category:\"people\"},rescue_worker_helmet:{keywords:[\"construction\",\"build\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛑\" src=\"26d1.png\"/>',fitzpatrick_scale:false,category:\"people\"},mortar_board:{keywords:[\"school\",\"college\",\"degree\",\"university\",\"graduation\",\"cap\",\"hat\",\"legal\",\"learn\",\"education\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎓\" src=\"1f393.png\"/>',fitzpatrick_scale:false,category:\"people\"},crown:{keywords:[\"king\",\"kod\",\"leader\",\"royalty\",\"lord\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👑\" src=\"1f451.png\"/>',fitzpatrick_scale:false,category:\"people\"},school_satchel:{keywords:[\"student\",\"education\",\"bag\",\"backpack\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎒\" src=\"1f392.png\"/>',fitzpatrick_scale:false,category:\"people\"},luggage:{keywords:[\"packing\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧳\" src=\"1f9f3.png\"/>',fitzpatrick_scale:false,category:\"people\"},pouch:{keywords:[\"bag\",\"accessories\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👝\" src=\"1f45d.png\"/>',fitzpatrick_scale:false,category:\"people\"},purse:{keywords:[\"fashion\",\"accessories\",\"money\",\"sales\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👛\" src=\"1f45b.png\"/>',fitzpatrick_scale:false,category:\"people\"},handbag:{keywords:[\"fashion\",\"accessory\",\"accessories\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👜\" src=\"1f45c.png\"/>',fitzpatrick_scale:false,category:\"people\"},briefcase:{keywords:[\"business\",\"documents\",\"work\",\"law\",\"legal\",\"job\",\"career\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💼\" src=\"1f4bc.png\"/>',fitzpatrick_scale:false,category:\"people\"},eyeglasses:{keywords:[\"fashion\",\"accessories\",\"eyesight\",\"nerdy\",\"dork\",\"geek\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👓\" src=\"1f453.png\"/>',fitzpatrick_scale:false,category:\"people\"},dark_sunglasses:{keywords:[\"face\",\"cool\",\"accessories\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕶\" src=\"1f576.png\"/>',fitzpatrick_scale:false,category:\"people\"},goggles:{keywords:[\"eyes\",\"protection\",\"safety\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥽\" src=\"1f97d.png\"/>',fitzpatrick_scale:false,category:\"people\"},ring:{keywords:[\"wedding\",\"propose\",\"marriage\",\"valentines\",\"diamond\",\"fashion\",\"jewelry\",\"gem\",\"engagement\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💍\" src=\"1f48d.png\"/>',fitzpatrick_scale:false,category:\"people\"},closed_umbrella:{keywords:[\"weather\",\"rain\",\"drizzle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌂\" src=\"1f302.png\"/>',fitzpatrick_scale:false,category:\"people\"},dog:{keywords:[\"animal\",\"friend\",\"nature\",\"woof\",\"puppy\",\"pet\",\"faithful\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐶\" src=\"1f436.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cat:{keywords:[\"animal\",\"meow\",\"nature\",\"pet\",\"kitten\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐱\" src=\"1f431.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse:{keywords:[\"animal\",\"nature\",\"cheese_wedge\",\"rodent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐭\" src=\"1f42d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hamster:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐹\" src=\"1f439.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit:{keywords:[\"animal\",\"nature\",\"pet\",\"spring\",\"magic\",\"bunny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐰\" src=\"1f430.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fox_face:{keywords:[\"animal\",\"nature\",\"face\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦊\" src=\"1f98a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bear:{keywords:[\"animal\",\"nature\",\"wild\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐻\" src=\"1f43b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},panda_face:{keywords:[\"animal\",\"nature\",\"panda\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐼\" src=\"1f43c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},koala:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐨\" src=\"1f428.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger:{keywords:[\"animal\",\"cat\",\"danger\",\"wild\",\"nature\",\"roar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐯\" src=\"1f42f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},lion:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦁\" src=\"1f981.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cow:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐮\" src=\"1f42e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},pig:{keywords:[\"animal\",\"oink\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐷\" src=\"1f437.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},pig_nose:{keywords:[\"animal\",\"oink\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐽\" src=\"1f43d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},frog:{keywords:[\"animal\",\"nature\",\"croak\",\"toad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐸\" src=\"1f438.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},squid:{keywords:[\"animal\",\"nature\",\"ocean\",\"sea\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦑\" src=\"1f991.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},octopus:{keywords:[\"animal\",\"creature\",\"ocean\",\"sea\",\"nature\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐙\" src=\"1f419.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shrimp:{keywords:[\"animal\",\"ocean\",\"nature\",\"seafood\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦐\" src=\"1f990.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey_face:{keywords:[\"animal\",\"nature\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐵\" src=\"1f435.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},gorilla:{keywords:[\"animal\",\"nature\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦍\" src=\"1f98d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},see_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"haha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙈\" src=\"1f648.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hear_no_evil:{keywords:[\"animal\",\"monkey\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙉\" src=\"1f649.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},speak_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"omg\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🙊\" src=\"1f64a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey:{keywords:[\"animal\",\"nature\",\"banana\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐒\" src=\"1f412.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},chicken:{keywords:[\"animal\",\"cluck\",\"nature\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐔\" src=\"1f414.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},penguin:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐧\" src=\"1f427.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bird:{keywords:[\"animal\",\"nature\",\"fly\",\"tweet\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐦\" src=\"1f426.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},baby_chick:{keywords:[\"animal\",\"chicken\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐤\" src=\"1f424.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hatching_chick:{keywords:[\"animal\",\"chicken\",\"egg\",\"born\",\"baby\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐣\" src=\"1f423.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hatched_chick:{keywords:[\"animal\",\"chicken\",\"baby\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐥\" src=\"1f425.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},duck:{keywords:[\"animal\",\"nature\",\"bird\",\"mallard\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦆\" src=\"1f986.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},eagle:{keywords:[\"animal\",\"nature\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦅\" src=\"1f985.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},owl:{keywords:[\"animal\",\"nature\",\"bird\",\"hoot\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦉\" src=\"1f989.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bat:{keywords:[\"animal\",\"nature\",\"blind\",\"vampire\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦇\" src=\"1f987.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},wolf:{keywords:[\"animal\",\"nature\",\"wild\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐺\" src=\"1f43a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},boar:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐗\" src=\"1f417.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},horse:{keywords:[\"animal\",\"brown\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐴\" src=\"1f434.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},unicorn:{keywords:[\"animal\",\"nature\",\"mystical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦄\" src=\"1f984.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},honeybee:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\",\"spring\",\"honey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐝\" src=\"1f41d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bug:{keywords:[\"animal\",\"insect\",\"nature\",\"worm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐛\" src=\"1f41b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},butterfly:{keywords:[\"animal\",\"insect\",\"nature\",\"caterpillar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦋\" src=\"1f98b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snail:{keywords:[\"slow\",\"animal\",\"shell\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐌\" src=\"1f40c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},beetle:{keywords:[\"animal\",\"insect\",\"nature\",\"ladybug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐞\" src=\"1f41e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ant:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐜\" src=\"1f41c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},grasshopper:{keywords:[\"animal\",\"cricket\",\"chirp\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦗\" src=\"1f997.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},spider:{keywords:[\"animal\",\"arachnid\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕷\" src=\"1f577.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},scorpion:{keywords:[\"animal\",\"arachnid\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦂\" src=\"1f982.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},crab:{keywords:[\"animal\",\"crustacean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦀\" src=\"1f980.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snake:{keywords:[\"animal\",\"evil\",\"nature\",\"hiss\",\"python\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐍\" src=\"1f40d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},lizard:{keywords:[\"animal\",\"nature\",\"reptile\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦎\" src=\"1f98e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},\"t-rex\":{keywords:[\"animal\",\"nature\",\"dinosaur\",\"tyrannosaurus\",\"extinct\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦖\" src=\"1f996.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sauropod:{keywords:[\"animal\",\"nature\",\"dinosaur\",\"brachiosaurus\",\"brontosaurus\",\"diplodocus\",\"extinct\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦕\" src=\"1f995.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},turtle:{keywords:[\"animal\",\"slow\",\"nature\",\"tortoise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐢\" src=\"1f422.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tropical_fish:{keywords:[\"animal\",\"swim\",\"ocean\",\"beach\",\"nemo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐠\" src=\"1f420.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fish:{keywords:[\"animal\",\"food\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐟\" src=\"1f41f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},blowfish:{keywords:[\"animal\",\"nature\",\"food\",\"sea\",\"ocean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐡\" src=\"1f421.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dolphin:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"flipper\",\"fins\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐬\" src=\"1f42c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shark:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"jaws\",\"fins\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦈\" src=\"1f988.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},whale:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐳\" src=\"1f433.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},whale2:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐋\" src=\"1f40b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},crocodile:{keywords:[\"animal\",\"nature\",\"reptile\",\"lizard\",\"alligator\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐊\" src=\"1f40a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},leopard:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐆\" src=\"1f406.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},zebra:{keywords:[\"animal\",\"nature\",\"stripes\",\"safari\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦓\" src=\"1f993.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger2:{keywords:[\"animal\",\"nature\",\"roar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐅\" src=\"1f405.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},water_buffalo:{keywords:[\"animal\",\"nature\",\"ox\",\"cow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐃\" src=\"1f403.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ox:{keywords:[\"animal\",\"cow\",\"beef\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐂\" src=\"1f402.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cow2:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐄\" src=\"1f404.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},deer:{keywords:[\"animal\",\"nature\",\"horns\",\"venison\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦌\" src=\"1f98c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dromedary_camel:{keywords:[\"animal\",\"hot\",\"desert\",\"hump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐪\" src=\"1f42a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},camel:{keywords:[\"animal\",\"nature\",\"hot\",\"desert\",\"hump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐫\" src=\"1f42b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},giraffe:{keywords:[\"animal\",\"nature\",\"spots\",\"safari\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦒\" src=\"1f992.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},elephant:{keywords:[\"animal\",\"nature\",\"nose\",\"th\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐘\" src=\"1f418.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rhinoceros:{keywords:[\"animal\",\"nature\",\"horn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦏\" src=\"1f98f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},goat:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐐\" src=\"1f410.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ram:{keywords:[\"animal\",\"sheep\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐏\" src=\"1f40f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sheep:{keywords:[\"animal\",\"nature\",\"wool\",\"shipit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐑\" src=\"1f411.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},racehorse:{keywords:[\"animal\",\"gamble\",\"luck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐎\" src=\"1f40e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},pig2:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐖\" src=\"1f416.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rat:{keywords:[\"animal\",\"mouse\",\"rodent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐀\" src=\"1f400.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse2:{keywords:[\"animal\",\"nature\",\"rodent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐁\" src=\"1f401.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rooster:{keywords:[\"animal\",\"nature\",\"chicken\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐓\" src=\"1f413.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},turkey:{keywords:[\"animal\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦃\" src=\"1f983.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dove:{keywords:[\"animal\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕊\" src=\"1f54a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dog2:{keywords:[\"animal\",\"nature\",\"friend\",\"doge\",\"pet\",\"faithful\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐕\" src=\"1f415.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},poodle:{keywords:[\"dog\",\"animal\",\"101\",\"nature\",\"pet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐩\" src=\"1f429.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cat2:{keywords:[\"animal\",\"meow\",\"pet\",\"cats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐈\" src=\"1f408.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit2:{keywords:[\"animal\",\"nature\",\"pet\",\"magic\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐇\" src=\"1f407.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},chipmunk:{keywords:[\"animal\",\"nature\",\"rodent\",\"squirrel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐿\" src=\"1f43f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hedgehog:{keywords:[\"animal\",\"nature\",\"spiny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦔\" src=\"1f994.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},raccoon:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦝\" src=\"1f99d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},llama:{keywords:[\"animal\",\"nature\",\"alpaca\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦙\" src=\"1f999.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hippopotamus:{keywords:[\"animal\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦛\" src=\"1f99b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},kangaroo:{keywords:[\"animal\",\"nature\",\"australia\",\"joey\",\"hop\",\"marsupial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦘\" src=\"1f998.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},badger:{keywords:[\"animal\",\"nature\",\"honey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦡\" src=\"1f9a1.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},swan:{keywords:[\"animal\",\"nature\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦢\" src=\"1f9a2.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},peacock:{keywords:[\"animal\",\"nature\",\"peahen\",\"bird\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦚\" src=\"1f99a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},parrot:{keywords:[\"animal\",\"nature\",\"bird\",\"pirate\",\"talk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦜\" src=\"1f99c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},lobster:{keywords:[\"animal\",\"nature\",\"bisque\",\"claws\",\"seafood\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦞\" src=\"1f99e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mosquito:{keywords:[\"animal\",\"nature\",\"insect\",\"malaria\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦟\" src=\"1f99f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},paw_prints:{keywords:[\"animal\",\"tracking\",\"footprints\",\"dog\",\"cat\",\"pet\",\"feet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐾\" src=\"1f43e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐉\" src=\"1f409.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon_face:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐲\" src=\"1f432.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cactus:{keywords:[\"vegetable\",\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌵\" src=\"1f335.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},christmas_tree:{keywords:[\"festival\",\"vacation\",\"december\",\"xmas\",\"celebration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎄\" src=\"1f384.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},evergreen_tree:{keywords:[\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌲\" src=\"1f332.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},deciduous_tree:{keywords:[\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌳\" src=\"1f333.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},palm_tree:{keywords:[\"plant\",\"vegetable\",\"nature\",\"summer\",\"beach\",\"mojito\",\"tropical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌴\" src=\"1f334.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},seedling:{keywords:[\"plant\",\"nature\",\"grass\",\"lawn\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌱\" src=\"1f331.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},herb:{keywords:[\"vegetable\",\"plant\",\"medicine\",\"weed\",\"grass\",\"lawn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌿\" src=\"1f33f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shamrock:{keywords:[\"vegetable\",\"plant\",\"nature\",\"irish\",\"clover\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☘\" src=\"2618.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},four_leaf_clover:{keywords:[\"vegetable\",\"plant\",\"nature\",\"lucky\",\"irish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍀\" src=\"1f340.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bamboo:{keywords:[\"plant\",\"nature\",\"vegetable\",\"panda\",\"pine_decoration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎍\" src=\"1f38d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tanabata_tree:{keywords:[\"plant\",\"nature\",\"branch\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎋\" src=\"1f38b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},leaves:{keywords:[\"nature\",\"plant\",\"tree\",\"vegetable\",\"grass\",\"lawn\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍃\" src=\"1f343.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fallen_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"leaves\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍂\" src=\"1f342.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},maple_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"ca\",\"fall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍁\" src=\"1f341.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ear_of_rice:{keywords:[\"nature\",\"plant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌾\" src=\"1f33e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},hibiscus:{keywords:[\"plant\",\"vegetable\",\"flowers\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌺\" src=\"1f33a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sunflower:{keywords:[\"nature\",\"plant\",\"fall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌻\" src=\"1f33b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},rose:{keywords:[\"flowers\",\"valentines\",\"love\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌹\" src=\"1f339.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},wilted_flower:{keywords:[\"plant\",\"nature\",\"flower\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥀\" src=\"1f940.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tulip:{keywords:[\"flowers\",\"plant\",\"nature\",\"summer\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌷\" src=\"1f337.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},blossom:{keywords:[\"nature\",\"flowers\",\"yellow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌼\" src=\"1f33c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cherry_blossom:{keywords:[\"nature\",\"plant\",\"spring\",\"flower\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌸\" src=\"1f338.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},bouquet:{keywords:[\"flowers\",\"nature\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💐\" src=\"1f490.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},mushroom:{keywords:[\"plant\",\"vegetable\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍄\" src=\"1f344.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},chestnut:{keywords:[\"food\",\"squirrel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌰\" src=\"1f330.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},jack_o_lantern:{keywords:[\"halloween\",\"light\",\"pumpkin\",\"creepy\",\"fall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎃\" src=\"1f383.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},shell:{keywords:[\"nature\",\"sea\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🐚\" src=\"1f41a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},spider_web:{keywords:[\"animal\",\"insect\",\"arachnid\",\"silk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕸\" src=\"1f578.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_americas:{keywords:[\"globe\",\"world\",\"USA\",\"international\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌎\" src=\"1f30e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_africa:{keywords:[\"globe\",\"world\",\"international\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌍\" src=\"1f30d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_asia:{keywords:[\"globe\",\"world\",\"east\",\"international\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌏\" src=\"1f30f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon:{keywords:[\"nature\",\"yellow\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌕\" src=\"1f315.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_gibbous_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\",\"waxing_gibbous_moon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌖\" src=\"1f316.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌗\" src=\"1f317.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌘\" src=\"1f318.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌑\" src=\"1f311.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌒\" src=\"1f312.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌓\" src=\"1f313.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_gibbous_moon:{keywords:[\"nature\",\"night\",\"sky\",\"gray\",\"twilight\",\"planet\",\"space\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌔\" src=\"1f314.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌚\" src=\"1f31a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌝\" src=\"1f31d.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌛\" src=\"1f31b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌜\" src=\"1f31c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_with_face:{keywords:[\"nature\",\"morning\",\"sky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌞\" src=\"1f31e.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},crescent_moon:{keywords:[\"night\",\"sleep\",\"sky\",\"evening\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌙\" src=\"1f319.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},star:{keywords:[\"night\",\"yellow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⭐\" src=\"2b50.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},star2:{keywords:[\"night\",\"sparkle\",\"awesome\",\"good\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌟\" src=\"1f31f.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dizzy:{keywords:[\"star\",\"sparkle\",\"shoot\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💫\" src=\"1f4ab.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sparkles:{keywords:[\"stars\",\"shine\",\"shiny\",\"cool\",\"awesome\",\"good\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✨\" src=\"2728.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},comet:{keywords:[\"space\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☄\" src=\"2604.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sunny:{keywords:[\"weather\",\"nature\",\"brightness\",\"summer\",\"beach\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☀️\" src=\"2600.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_small_cloud:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌤\" src=\"1f324.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},partly_sunny:{keywords:[\"weather\",\"nature\",\"cloudy\",\"morning\",\"fall\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛅\" src=\"26c5.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_large_cloud:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌥\" src=\"1f325.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_rain_cloud:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌦\" src=\"1f326.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud:{keywords:[\"weather\",\"sky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☁️\" src=\"2601.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_rain:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌧\" src=\"1f327.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning_and_rain:{keywords:[\"weather\",\"lightning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛈\" src=\"26c8.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning:{keywords:[\"weather\",\"thunder\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌩\" src=\"1f329.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},zap:{keywords:[\"thunder\",\"weather\",\"lightning bolt\",\"fast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚡\" src=\"26a1.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fire:{keywords:[\"hot\",\"cook\",\"flame\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔥\" src=\"1f525.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},boom:{keywords:[\"bomb\",\"explode\",\"explosion\",\"collision\",\"blown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💥\" src=\"1f4a5.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snowflake:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❄️\" src=\"2744.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_snow:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌨\" src=\"1f328.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\",\"without_snow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛄\" src=\"26c4.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman_with_snow:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☃\" src=\"2603.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},wind_face:{keywords:[\"gust\",\"air\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌬\" src=\"1f32c.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},dash:{keywords:[\"wind\",\"air\",\"fast\",\"shoo\",\"fart\",\"smoke\",\"puff\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💨\" src=\"1f4a8.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},tornado:{keywords:[\"weather\",\"cyclone\",\"twister\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌪\" src=\"1f32a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},fog:{keywords:[\"weather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌫\" src=\"1f32b.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},open_umbrella:{keywords:[\"weather\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☂\" src=\"2602.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},umbrella:{keywords:[\"rainy\",\"weather\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☔\" src=\"2614.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},droplet:{keywords:[\"water\",\"drip\",\"faucet\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💧\" src=\"1f4a7.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},sweat_drops:{keywords:[\"water\",\"drip\",\"oops\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💦\" src=\"1f4a6.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},ocean:{keywords:[\"sea\",\"water\",\"wave\",\"nature\",\"tsunami\",\"disaster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌊\" src=\"1f30a.png\"/>',fitzpatrick_scale:false,category:\"animals_and_nature\"},green_apple:{keywords:[\"fruit\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍏\" src=\"1f34f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},apple:{keywords:[\"fruit\",\"mac\",\"school\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍎\" src=\"1f34e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pear:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍐\" src=\"1f350.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tangerine:{keywords:[\"food\",\"fruit\",\"nature\",\"orange\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍊\" src=\"1f34a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},lemon:{keywords:[\"fruit\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍋\" src=\"1f34b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},banana:{keywords:[\"fruit\",\"food\",\"monkey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍌\" src=\"1f34c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},watermelon:{keywords:[\"fruit\",\"food\",\"picnic\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍉\" src=\"1f349.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},grapes:{keywords:[\"fruit\",\"food\",\"wine\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍇\" src=\"1f347.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},strawberry:{keywords:[\"fruit\",\"food\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍓\" src=\"1f353.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},melon:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍈\" src=\"1f348.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cherries:{keywords:[\"food\",\"fruit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍒\" src=\"1f352.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},peach:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍑\" src=\"1f351.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pineapple:{keywords:[\"fruit\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍍\" src=\"1f34d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},coconut:{keywords:[\"fruit\",\"nature\",\"food\",\"palm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥥\" src=\"1f965.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},kiwi_fruit:{keywords:[\"fruit\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥝\" src=\"1f95d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},mango:{keywords:[\"fruit\",\"food\",\"tropical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥭\" src=\"1f96d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},avocado:{keywords:[\"fruit\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥑\" src=\"1f951.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},broccoli:{keywords:[\"fruit\",\"food\",\"vegetable\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥦\" src=\"1f966.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tomato:{keywords:[\"fruit\",\"vegetable\",\"nature\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍅\" src=\"1f345.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},eggplant:{keywords:[\"vegetable\",\"nature\",\"food\",\"aubergine\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍆\" src=\"1f346.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cucumber:{keywords:[\"fruit\",\"food\",\"pickle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥒\" src=\"1f952.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},carrot:{keywords:[\"vegetable\",\"food\",\"orange\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥕\" src=\"1f955.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},hot_pepper:{keywords:[\"food\",\"spicy\",\"chilli\",\"chili\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌶\" src=\"1f336.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},potato:{keywords:[\"food\",\"tuber\",\"vegatable\",\"starch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥔\" src=\"1f954.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},corn:{keywords:[\"food\",\"vegetable\",\"plant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌽\" src=\"1f33d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},leafy_greens:{keywords:[\"food\",\"vegetable\",\"plant\",\"bok choy\",\"cabbage\",\"kale\",\"lettuce\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥬\" src=\"1f96c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sweet_potato:{keywords:[\"food\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍠\" src=\"1f360.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},peanuts:{keywords:[\"food\",\"nut\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥜\" src=\"1f95c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},honey_pot:{keywords:[\"bees\",\"sweet\",\"kitchen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍯\" src=\"1f36f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},croissant:{keywords:[\"food\",\"bread\",\"french\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥐\" src=\"1f950.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bread:{keywords:[\"food\",\"wheat\",\"breakfast\",\"toast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍞\" src=\"1f35e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},baguette_bread:{keywords:[\"food\",\"bread\",\"french\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥖\" src=\"1f956.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bagel:{keywords:[\"food\",\"bread\",\"bakery\",\"schmear\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥯\" src=\"1f96f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pretzel:{keywords:[\"food\",\"bread\",\"twisted\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥨\" src=\"1f968.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cheese:{keywords:[\"food\",\"chadder\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧀\" src=\"1f9c0.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},egg:{keywords:[\"food\",\"chicken\",\"breakfast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥚\" src=\"1f95a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bacon:{keywords:[\"food\",\"breakfast\",\"pork\",\"pig\",\"meat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥓\" src=\"1f953.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},steak:{keywords:[\"food\",\"cow\",\"meat\",\"cut\",\"chop\",\"lambchop\",\"porkchop\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥩\" src=\"1f969.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pancakes:{keywords:[\"food\",\"breakfast\",\"flapjacks\",\"hotcakes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥞\" src=\"1f95e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},poultry_leg:{keywords:[\"food\",\"meat\",\"drumstick\",\"bird\",\"chicken\",\"turkey\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍗\" src=\"1f357.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},meat_on_bone:{keywords:[\"good\",\"food\",\"drumstick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍖\" src=\"1f356.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bone:{keywords:[\"skeleton\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦴\" src=\"1f9b4.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fried_shrimp:{keywords:[\"food\",\"animal\",\"appetizer\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍤\" src=\"1f364.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fried_egg:{keywords:[\"food\",\"breakfast\",\"kitchen\",\"egg\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍳\" src=\"1f373.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},hamburger:{keywords:[\"meat\",\"fast food\",\"beef\",\"cheeseburger\",\"mcdonalds\",\"burger king\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍔\" src=\"1f354.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fries:{keywords:[\"chips\",\"snack\",\"fast food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍟\" src=\"1f35f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},stuffed_flatbread:{keywords:[\"food\",\"flatbread\",\"stuffed\",\"gyro\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥙\" src=\"1f959.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},hotdog:{keywords:[\"food\",\"frankfurter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌭\" src=\"1f32d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pizza:{keywords:[\"food\",\"party\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍕\" src=\"1f355.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sandwich:{keywords:[\"food\",\"lunch\",\"bread\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥪\" src=\"1f96a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},canned_food:{keywords:[\"food\",\"soup\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥫\" src=\"1f96b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},spaghetti:{keywords:[\"food\",\"italian\",\"noodle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍝\" src=\"1f35d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},taco:{keywords:[\"food\",\"mexican\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌮\" src=\"1f32e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},burrito:{keywords:[\"food\",\"mexican\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌯\" src=\"1f32f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},green_salad:{keywords:[\"food\",\"healthy\",\"lettuce\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥗\" src=\"1f957.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},shallow_pan_of_food:{keywords:[\"food\",\"cooking\",\"casserole\",\"paella\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥘\" src=\"1f958.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},ramen:{keywords:[\"food\",\"japanese\",\"noodle\",\"chopsticks\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍜\" src=\"1f35c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},stew:{keywords:[\"food\",\"meat\",\"soup\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍲\" src=\"1f372.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fish_cake:{keywords:[\"food\",\"japan\",\"sea\",\"beach\",\"narutomaki\",\"pink\",\"swirl\",\"kamaboko\",\"surimi\",\"ramen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍥\" src=\"1f365.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fortune_cookie:{keywords:[\"food\",\"prophecy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥠\" src=\"1f960.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sushi:{keywords:[\"food\",\"fish\",\"japanese\",\"rice\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍣\" src=\"1f363.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bento:{keywords:[\"food\",\"japanese\",\"box\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍱\" src=\"1f371.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},curry:{keywords:[\"food\",\"spicy\",\"hot\",\"indian\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍛\" src=\"1f35b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},rice_ball:{keywords:[\"food\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍙\" src=\"1f359.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},rice:{keywords:[\"food\",\"china\",\"asian\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍚\" src=\"1f35a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},rice_cracker:{keywords:[\"food\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍘\" src=\"1f358.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},oden:{keywords:[\"food\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍢\" src=\"1f362.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},dango:{keywords:[\"food\",\"dessert\",\"sweet\",\"japanese\",\"barbecue\",\"meat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍡\" src=\"1f361.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},shaved_ice:{keywords:[\"hot\",\"dessert\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍧\" src=\"1f367.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},ice_cream:{keywords:[\"food\",\"hot\",\"dessert\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍨\" src=\"1f368.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},icecream:{keywords:[\"food\",\"hot\",\"dessert\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍦\" src=\"1f366.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},pie:{keywords:[\"food\",\"dessert\",\"pastry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥧\" src=\"1f967.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cake:{keywords:[\"food\",\"dessert\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍰\" src=\"1f370.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cupcake:{keywords:[\"food\",\"dessert\",\"bakery\",\"sweet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧁\" src=\"1f9c1.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},moon_cake:{keywords:[\"food\",\"autumn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥮\" src=\"1f96e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},birthday:{keywords:[\"food\",\"dessert\",\"cake\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎂\" src=\"1f382.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},custard:{keywords:[\"dessert\",\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍮\" src=\"1f36e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},candy:{keywords:[\"snack\",\"dessert\",\"sweet\",\"lolly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍬\" src=\"1f36c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},lollipop:{keywords:[\"food\",\"snack\",\"candy\",\"sweet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍭\" src=\"1f36d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},chocolate_bar:{keywords:[\"food\",\"snack\",\"dessert\",\"sweet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍫\" src=\"1f36b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},popcorn:{keywords:[\"food\",\"movie theater\",\"films\",\"snack\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍿\" src=\"1f37f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},dumpling:{keywords:[\"food\",\"empanada\",\"pierogi\",\"potsticker\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥟\" src=\"1f95f.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},doughnut:{keywords:[\"food\",\"dessert\",\"snack\",\"sweet\",\"donut\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍩\" src=\"1f369.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cookie:{keywords:[\"food\",\"snack\",\"oreo\",\"chocolate\",\"sweet\",\"dessert\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍪\" src=\"1f36a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},milk_glass:{keywords:[\"beverage\",\"drink\",\"cow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥛\" src=\"1f95b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},beer:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍺\" src=\"1f37a.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},beers:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍻\" src=\"1f37b.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},clinking_glasses:{keywords:[\"beverage\",\"drink\",\"party\",\"alcohol\",\"celebrate\",\"cheers\",\"wine\",\"champagne\",\"toast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥂\" src=\"1f942.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},wine_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍷\" src=\"1f377.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tumbler_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"liquor\",\"booze\",\"bourbon\",\"scotch\",\"whisky\",\"glass\",\"shot\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥃\" src=\"1f943.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cocktail:{keywords:[\"drink\",\"drunk\",\"alcohol\",\"beverage\",\"booze\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍸\" src=\"1f378.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tropical_drink:{keywords:[\"beverage\",\"cocktail\",\"summer\",\"beach\",\"alcohol\",\"booze\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍹\" src=\"1f379.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},champagne:{keywords:[\"drink\",\"wine\",\"bottle\",\"celebration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍾\" src=\"1f37e.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},sake:{keywords:[\"wine\",\"drink\",\"drunk\",\"beverage\",\"japanese\",\"alcohol\",\"booze\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍶\" src=\"1f376.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},tea:{keywords:[\"drink\",\"bowl\",\"breakfast\",\"green\",\"british\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍵\" src=\"1f375.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},cup_with_straw:{keywords:[\"drink\",\"soda\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥤\" src=\"1f964.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},coffee:{keywords:[\"beverage\",\"caffeine\",\"latte\",\"espresso\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☕\" src=\"2615.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},baby_bottle:{keywords:[\"food\",\"container\",\"milk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍼\" src=\"1f37c.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},salt:{keywords:[\"condiment\",\"shaker\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧂\" src=\"1f9c2.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},spoon:{keywords:[\"cutlery\",\"kitchen\",\"tableware\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥄\" src=\"1f944.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},fork_and_knife:{keywords:[\"cutlery\",\"kitchen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍴\" src=\"1f374.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},plate_with_cutlery:{keywords:[\"food\",\"eat\",\"meal\",\"lunch\",\"dinner\",\"restaurant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🍽\" src=\"1f37d.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},bowl_with_spoon:{keywords:[\"food\",\"breakfast\",\"cereal\",\"oatmeal\",\"porridge\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥣\" src=\"1f963.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},takeout_box:{keywords:[\"food\",\"leftovers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥡\" src=\"1f961.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},chopsticks:{keywords:[\"food\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥢\" src=\"1f962.png\"/>',fitzpatrick_scale:false,category:\"food_and_drink\"},soccer:{keywords:[\"sports\",\"football\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚽\" src=\"26bd.png\"/>',fitzpatrick_scale:false,category:\"activity\"},basketball:{keywords:[\"sports\",\"balls\",\"NBA\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏀\" src=\"1f3c0.png\"/>',fitzpatrick_scale:false,category:\"activity\"},football:{keywords:[\"sports\",\"balls\",\"NFL\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏈\" src=\"1f3c8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},baseball:{keywords:[\"sports\",\"balls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚾\" src=\"26be.png\"/>',fitzpatrick_scale:false,category:\"activity\"},softball:{keywords:[\"sports\",\"balls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥎\" src=\"1f94e.png\"/>',fitzpatrick_scale:false,category:\"activity\"},tennis:{keywords:[\"sports\",\"balls\",\"green\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎾\" src=\"1f3be.png\"/>',fitzpatrick_scale:false,category:\"activity\"},volleyball:{keywords:[\"sports\",\"balls\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏐\" src=\"1f3d0.png\"/>',fitzpatrick_scale:false,category:\"activity\"},rugby_football:{keywords:[\"sports\",\"team\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏉\" src=\"1f3c9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},flying_disc:{keywords:[\"sports\",\"frisbee\",\"ultimate\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥏\" src=\"1f94f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"8ball\":{keywords:[\"pool\",\"hobby\",\"game\",\"luck\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎱\" src=\"1f3b1.png\"/>',fitzpatrick_scale:false,category:\"activity\"},golf:{keywords:[\"sports\",\"business\",\"flag\",\"hole\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛳\" src=\"26f3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},golfing_woman:{keywords:[\"sports\",\"business\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏌️‍♀️\" src=\"1f3cc-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},golfing_man:{keywords:[\"sports\",\"business\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏌\" src=\"1f3cc.png\"/>',fitzpatrick_scale:true,category:\"activity\"},ping_pong:{keywords:[\"sports\",\"pingpong\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏓\" src=\"1f3d3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},badminton:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏸\" src=\"1f3f8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},goal_net:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥅\" src=\"1f945.png\"/>',fitzpatrick_scale:false,category:\"activity\"},ice_hockey:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏒\" src=\"1f3d2.png\"/>',fitzpatrick_scale:false,category:\"activity\"},field_hockey:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏑\" src=\"1f3d1.png\"/>',fitzpatrick_scale:false,category:\"activity\"},lacrosse:{keywords:[\"sports\",\"ball\",\"stick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥍\" src=\"1f94d.png\"/>',fitzpatrick_scale:false,category:\"activity\"},cricket:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏏\" src=\"1f3cf.png\"/>',fitzpatrick_scale:false,category:\"activity\"},ski:{keywords:[\"sports\",\"winter\",\"cold\",\"snow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎿\" src=\"1f3bf.png\"/>',fitzpatrick_scale:false,category:\"activity\"},skier:{keywords:[\"sports\",\"winter\",\"snow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛷\" src=\"26f7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},snowboarder:{keywords:[\"sports\",\"winter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏂\" src=\"1f3c2.png\"/>',fitzpatrick_scale:true,category:\"activity\"},person_fencing:{keywords:[\"sports\",\"fencing\",\"sword\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤺\" src=\"1f93a.png\"/>',fitzpatrick_scale:false,category:\"activity\"},women_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤼‍♀️\" src=\"1f93c-200d-2640-fe0f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},men_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤼‍♂️\" src=\"1f93c-200d-2642-fe0f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},woman_cartwheeling:{keywords:[\"gymnastics\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤸‍♀️\" src=\"1f938-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_cartwheeling:{keywords:[\"gymnastics\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤸‍♂️\" src=\"1f938-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},woman_playing_handball:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤾‍♀️\" src=\"1f93e-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_playing_handball:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤾‍♂️\" src=\"1f93e-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},ice_skate:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛸\" src=\"26f8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},curling_stone:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥌\" src=\"1f94c.png\"/>',fitzpatrick_scale:false,category:\"activity\"},skateboard:{keywords:[\"board\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛹\" src=\"1f6f9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},sled:{keywords:[\"sleigh\",\"luge\",\"toboggan\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛷\" src=\"1f6f7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},bow_and_arrow:{keywords:[\"sports\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏹\" src=\"1f3f9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},fishing_pole_and_fish:{keywords:[\"food\",\"hobby\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎣\" src=\"1f3a3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},boxing_glove:{keywords:[\"sports\",\"fighting\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥊\" src=\"1f94a.png\"/>',fitzpatrick_scale:false,category:\"activity\"},martial_arts_uniform:{keywords:[\"judo\",\"karate\",\"taekwondo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥋\" src=\"1f94b.png\"/>',fitzpatrick_scale:false,category:\"activity\"},rowing_woman:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚣‍♀️\" src=\"1f6a3-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},rowing_man:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚣\" src=\"1f6a3.png\"/>',fitzpatrick_scale:true,category:\"activity\"},climbing_woman:{keywords:[\"sports\",\"hobby\",\"woman\",\"female\",\"rock\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧗‍♀️\" src=\"1f9d7-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},climbing_man:{keywords:[\"sports\",\"hobby\",\"man\",\"male\",\"rock\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧗‍♂️\" src=\"1f9d7-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},swimming_woman:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏊‍♀️\" src=\"1f3ca-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},swimming_man:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏊\" src=\"1f3ca.png\"/>',fitzpatrick_scale:true,category:\"activity\"},woman_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤽‍♀️\" src=\"1f93d-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤽‍♂️\" src=\"1f93d-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},woman_in_lotus_position:{keywords:[\"woman\",\"female\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧘‍♀️\" src=\"1f9d8-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_in_lotus_position:{keywords:[\"man\",\"male\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧘‍♂️\" src=\"1f9d8-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},surfing_woman:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏄‍♀️\" src=\"1f3c4-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},surfing_man:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏄\" src=\"1f3c4.png\"/>',fitzpatrick_scale:true,category:\"activity\"},bath:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛀\" src=\"1f6c0.png\"/>',fitzpatrick_scale:true,category:\"activity\"},basketball_woman:{keywords:[\"sports\",\"human\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛹️‍♀️\" src=\"26f9-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},basketball_man:{keywords:[\"sports\",\"human\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛹\" src=\"26f9.png\"/>',fitzpatrick_scale:true,category:\"activity\"},weight_lifting_woman:{keywords:[\"sports\",\"training\",\"exercise\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏋️‍♀️\" src=\"1f3cb-fe0f-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},weight_lifting_man:{keywords:[\"sports\",\"training\",\"exercise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏋\" src=\"1f3cb.png\"/>',fitzpatrick_scale:true,category:\"activity\"},biking_woman:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚴‍♀️\" src=\"1f6b4-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},biking_man:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚴\" src=\"1f6b4.png\"/>',fitzpatrick_scale:true,category:\"activity\"},mountain_biking_woman:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\",\"woman\",\"female\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚵‍♀️\" src=\"1f6b5-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},mountain_biking_man:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚵\" src=\"1f6b5.png\"/>',fitzpatrick_scale:true,category:\"activity\"},horse_racing:{keywords:[\"animal\",\"betting\",\"competition\",\"gambling\",\"luck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏇\" src=\"1f3c7.png\"/>',fitzpatrick_scale:true,category:\"activity\"},business_suit_levitating:{keywords:[\"suit\",\"business\",\"levitate\",\"hover\",\"jump\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕴\" src=\"1f574.png\"/>',fitzpatrick_scale:true,category:\"activity\"},trophy:{keywords:[\"win\",\"award\",\"contest\",\"place\",\"ftw\",\"ceremony\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏆\" src=\"1f3c6.png\"/>',fitzpatrick_scale:false,category:\"activity\"},running_shirt_with_sash:{keywords:[\"play\",\"pageant\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎽\" src=\"1f3bd.png\"/>',fitzpatrick_scale:false,category:\"activity\"},medal_sports:{keywords:[\"award\",\"winning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏅\" src=\"1f3c5.png\"/>',fitzpatrick_scale:false,category:\"activity\"},medal_military:{keywords:[\"award\",\"winning\",\"army\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎖\" src=\"1f396.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"1st_place_medal\":{keywords:[\"award\",\"winning\",\"first\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥇\" src=\"1f947.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"2nd_place_medal\":{keywords:[\"award\",\"second\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥈\" src=\"1f948.png\"/>',fitzpatrick_scale:false,category:\"activity\"},\"3rd_place_medal\":{keywords:[\"award\",\"third\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥉\" src=\"1f949.png\"/>',fitzpatrick_scale:false,category:\"activity\"},reminder_ribbon:{keywords:[\"sports\",\"cause\",\"support\",\"awareness\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎗\" src=\"1f397.png\"/>',fitzpatrick_scale:false,category:\"activity\"},rosette:{keywords:[\"flower\",\"decoration\",\"military\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏵\" src=\"1f3f5.png\"/>',fitzpatrick_scale:false,category:\"activity\"},ticket:{keywords:[\"event\",\"concert\",\"pass\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎫\" src=\"1f3ab.png\"/>',fitzpatrick_scale:false,category:\"activity\"},tickets:{keywords:[\"sports\",\"concert\",\"entrance\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎟\" src=\"1f39f.png\"/>',fitzpatrick_scale:false,category:\"activity\"},performing_arts:{keywords:[\"acting\",\"theater\",\"drama\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎭\" src=\"1f3ad.png\"/>',fitzpatrick_scale:false,category:\"activity\"},art:{keywords:[\"design\",\"paint\",\"draw\",\"colors\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎨\" src=\"1f3a8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},circus_tent:{keywords:[\"festival\",\"carnival\",\"party\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎪\" src=\"1f3aa.png\"/>',fitzpatrick_scale:false,category:\"activity\"},woman_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤹‍♀️\" src=\"1f939-200d-2640-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},man_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🤹‍♂️\" src=\"1f939-200d-2642-fe0f.png\"/>',fitzpatrick_scale:true,category:\"activity\"},microphone:{keywords:[\"sound\",\"music\",\"PA\",\"sing\",\"talkshow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎤\" src=\"1f3a4.png\"/>',fitzpatrick_scale:false,category:\"activity\"},headphones:{keywords:[\"music\",\"score\",\"gadgets\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎧\" src=\"1f3a7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},musical_score:{keywords:[\"treble\",\"clef\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎼\" src=\"1f3bc.png\"/>',fitzpatrick_scale:false,category:\"activity\"},musical_keyboard:{keywords:[\"piano\",\"instrument\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎹\" src=\"1f3b9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},drum:{keywords:[\"music\",\"instrument\",\"drumsticks\",\"snare\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🥁\" src=\"1f941.png\"/>',fitzpatrick_scale:false,category:\"activity\"},saxophone:{keywords:[\"music\",\"instrument\",\"jazz\",\"blues\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎷\" src=\"1f3b7.png\"/>',fitzpatrick_scale:false,category:\"activity\"},trumpet:{keywords:[\"music\",\"brass\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎺\" src=\"1f3ba.png\"/>',fitzpatrick_scale:false,category:\"activity\"},guitar:{keywords:[\"music\",\"instrument\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎸\" src=\"1f3b8.png\"/>',fitzpatrick_scale:false,category:\"activity\"},violin:{keywords:[\"music\",\"instrument\",\"orchestra\",\"symphony\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎻\" src=\"1f3bb.png\"/>',fitzpatrick_scale:false,category:\"activity\"},clapper:{keywords:[\"movie\",\"film\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎬\" src=\"1f3ac.png\"/>',fitzpatrick_scale:false,category:\"activity\"},video_game:{keywords:[\"play\",\"console\",\"PS4\",\"controller\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎮\" src=\"1f3ae.png\"/>',fitzpatrick_scale:false,category:\"activity\"},space_invader:{keywords:[\"game\",\"arcade\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"👾\" src=\"1f47e.png\"/>',fitzpatrick_scale:false,category:\"activity\"},dart:{keywords:[\"game\",\"play\",\"bar\",\"target\",\"bullseye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎯\" src=\"1f3af.png\"/>',fitzpatrick_scale:false,category:\"activity\"},game_die:{keywords:[\"dice\",\"random\",\"tabletop\",\"play\",\"luck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎲\" src=\"1f3b2.png\"/>',fitzpatrick_scale:false,category:\"activity\"},chess_pawn:{keywords:[\"expendable\"],char:\"♟\",fitzpatrick_scale:false,category:\"activity\"},slot_machine:{keywords:[\"bet\",\"gamble\",\"vegas\",\"fruit machine\",\"luck\",\"casino\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎰\" src=\"1f3b0.png\"/>',fitzpatrick_scale:false,category:\"activity\"},jigsaw:{keywords:[\"interlocking\",\"puzzle\",\"piece\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧩\" src=\"1f9e9.png\"/>',fitzpatrick_scale:false,category:\"activity\"},bowling:{keywords:[\"sports\",\"fun\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎳\" src=\"1f3b3.png\"/>',fitzpatrick_scale:false,category:\"activity\"},red_car:{keywords:[\"red\",\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚗\" src=\"1f697.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},taxi:{keywords:[\"uber\",\"vehicle\",\"cars\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚕\" src=\"1f695.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},blue_car:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚙\" src=\"1f699.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bus:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚌\" src=\"1f68c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},trolleybus:{keywords:[\"bart\",\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚎\" src=\"1f68e.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},racing_car:{keywords:[\"sports\",\"race\",\"fast\",\"formula\",\"f1\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏎\" src=\"1f3ce.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},police_car:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"law\",\"legal\",\"enforcement\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚓\" src=\"1f693.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ambulance:{keywords:[\"health\",\"911\",\"hospital\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚑\" src=\"1f691.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fire_engine:{keywords:[\"transportation\",\"cars\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚒\" src=\"1f692.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},minibus:{keywords:[\"vehicle\",\"car\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚐\" src=\"1f690.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},truck:{keywords:[\"cars\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚚\" src=\"1f69a.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},articulated_lorry:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"express\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚛\" src=\"1f69b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tractor:{keywords:[\"vehicle\",\"car\",\"farming\",\"agriculture\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚜\" src=\"1f69c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},kick_scooter:{keywords:[\"vehicle\",\"kick\",\"razor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛴\" src=\"1f6f4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motorcycle:{keywords:[\"race\",\"sports\",\"fast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏍\" src=\"1f3cd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bike:{keywords:[\"sports\",\"bicycle\",\"exercise\",\"hipster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚲\" src=\"1f6b2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motor_scooter:{keywords:[\"vehicle\",\"vespa\",\"sasha\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛵\" src=\"1f6f5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rotating_light:{keywords:[\"police\",\"ambulance\",\"911\",\"emergency\",\"alert\",\"error\",\"pinged\",\"law\",\"legal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚨\" src=\"1f6a8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_police_car:{keywords:[\"vehicle\",\"law\",\"legal\",\"enforcement\",\"911\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚔\" src=\"1f694.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_bus:{keywords:[\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚍\" src=\"1f68d.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_automobile:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚘\" src=\"1f698.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_taxi:{keywords:[\"vehicle\",\"cars\",\"uber\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚖\" src=\"1f696.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},aerial_tramway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚡\" src=\"1f6a1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_cableway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚠\" src=\"1f6a0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},suspension_railway:{keywords:[\"vehicle\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚟\" src=\"1f69f.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},railway_car:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚃\" src=\"1f683.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},train:{keywords:[\"transportation\",\"vehicle\",\"carriage\",\"public\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚋\" src=\"1f68b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},monorail:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚝\" src=\"1f69d.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_side:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚄\" src=\"1f684.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_front:{keywords:[\"transportation\",\"vehicle\",\"speed\",\"fast\",\"public\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚅\" src=\"1f685.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},light_rail:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚈\" src=\"1f688.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_railway:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚞\" src=\"1f69e.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},steam_locomotive:{keywords:[\"transportation\",\"vehicle\",\"train\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚂\" src=\"1f682.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},train2:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚆\" src=\"1f686.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},metro:{keywords:[\"transportation\",\"blue-square\",\"mrt\",\"underground\",\"tube\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚇\" src=\"1f687.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tram:{keywords:[\"transportation\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚊\" src=\"1f68a.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},station:{keywords:[\"transportation\",\"vehicle\",\"public\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚉\" src=\"1f689.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},flying_saucer:{keywords:[\"transportation\",\"vehicle\",\"ufo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛸\" src=\"1f6f8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},helicopter:{keywords:[\"transportation\",\"vehicle\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚁\" src=\"1f681.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},small_airplane:{keywords:[\"flight\",\"transportation\",\"fly\",\"vehicle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛩\" src=\"1f6e9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},airplane:{keywords:[\"vehicle\",\"transportation\",\"flight\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✈️\" src=\"2708.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},flight_departure:{keywords:[\"airport\",\"flight\",\"landing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛫\" src=\"1f6eb.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},flight_arrival:{keywords:[\"airport\",\"flight\",\"boarding\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛬\" src=\"1f6ec.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sailboat:{keywords:[\"ship\",\"summer\",\"transportation\",\"water\",\"sailing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛵\" src=\"26f5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motor_boat:{keywords:[\"ship\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛥\" src=\"1f6e5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},speedboat:{keywords:[\"ship\",\"transportation\",\"vehicle\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚤\" src=\"1f6a4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ferry:{keywords:[\"boat\",\"ship\",\"yacht\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛴\" src=\"26f4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},passenger_ship:{keywords:[\"yacht\",\"cruise\",\"ferry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛳\" src=\"1f6f3.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rocket:{keywords:[\"launch\",\"ship\",\"staffmode\",\"NASA\",\"outer space\",\"outer_space\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚀\" src=\"1f680.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},artificial_satellite:{keywords:[\"communication\",\"gps\",\"orbit\",\"spaceflight\",\"NASA\",\"ISS\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛰\" src=\"1f6f0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},seat:{keywords:[\"sit\",\"airplane\",\"transport\",\"bus\",\"flight\",\"fly\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💺\" src=\"1f4ba.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},canoe:{keywords:[\"boat\",\"paddle\",\"water\",\"ship\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛶\" src=\"1f6f6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},anchor:{keywords:[\"ship\",\"ferry\",\"sea\",\"boat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚓\" src=\"2693.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},construction:{keywords:[\"wip\",\"progress\",\"caution\",\"warning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚧\" src=\"1f6a7.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fuelpump:{keywords:[\"gas station\",\"petroleum\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛽\" src=\"26fd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},busstop:{keywords:[\"transportation\",\"wait\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚏\" src=\"1f68f.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},vertical_traffic_light:{keywords:[\"transportation\",\"driving\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚦\" src=\"1f6a6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},traffic_light:{keywords:[\"transportation\",\"signal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚥\" src=\"1f6a5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},checkered_flag:{keywords:[\"contest\",\"finishline\",\"race\",\"gokart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏁\" src=\"1f3c1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ship:{keywords:[\"transportation\",\"titanic\",\"deploy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚢\" src=\"1f6a2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},ferris_wheel:{keywords:[\"photo\",\"carnival\",\"londoneye\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎡\" src=\"1f3a1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},roller_coaster:{keywords:[\"carnival\",\"playground\",\"photo\",\"fun\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎢\" src=\"1f3a2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},carousel_horse:{keywords:[\"photo\",\"carnival\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎠\" src=\"1f3a0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},building_construction:{keywords:[\"wip\",\"working\",\"progress\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏗\" src=\"1f3d7.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},foggy:{keywords:[\"photo\",\"mountain\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌁\" src=\"1f301.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tokyo_tower:{keywords:[\"photo\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗼\" src=\"1f5fc.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},factory:{keywords:[\"building\",\"industry\",\"pollution\",\"smoke\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏭\" src=\"1f3ed.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fountain:{keywords:[\"photo\",\"summer\",\"water\",\"fresh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛲\" src=\"26f2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rice_scene:{keywords:[\"photo\",\"japan\",\"asia\",\"tsukimi\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎑\" src=\"1f391.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain:{keywords:[\"photo\",\"nature\",\"environment\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛰\" src=\"26f0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_snow:{keywords:[\"photo\",\"nature\",\"environment\",\"winter\",\"cold\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏔\" src=\"1f3d4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mount_fuji:{keywords:[\"photo\",\"mountain\",\"nature\",\"japanese\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗻\" src=\"1f5fb.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},volcano:{keywords:[\"photo\",\"nature\",\"disaster\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌋\" src=\"1f30b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},japan:{keywords:[\"nation\",\"country\",\"japanese\",\"asia\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗾\" src=\"1f5fe.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},camping:{keywords:[\"photo\",\"outdoors\",\"tent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏕\" src=\"1f3d5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},tent:{keywords:[\"photo\",\"camping\",\"outdoors\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛺\" src=\"26fa.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},national_park:{keywords:[\"photo\",\"environment\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏞\" src=\"1f3de.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},motorway:{keywords:[\"road\",\"cupertino\",\"interstate\",\"highway\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛣\" src=\"1f6e3.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},railway_track:{keywords:[\"train\",\"transportation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛤\" src=\"1f6e4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise:{keywords:[\"morning\",\"view\",\"vacation\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌅\" src=\"1f305.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise_over_mountains:{keywords:[\"view\",\"vacation\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌄\" src=\"1f304.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},desert:{keywords:[\"photo\",\"warm\",\"saharah\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏜\" src=\"1f3dc.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},beach_umbrella:{keywords:[\"weather\",\"summer\",\"sunny\",\"sand\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏖\" src=\"1f3d6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},desert_island:{keywords:[\"photo\",\"tropical\",\"mojito\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏝\" src=\"1f3dd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunrise:{keywords:[\"photo\",\"good morning\",\"dawn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌇\" src=\"1f307.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunset:{keywords:[\"photo\",\"evening\",\"sky\",\"buildings\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌆\" src=\"1f306.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},cityscape:{keywords:[\"photo\",\"night life\",\"urban\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏙\" src=\"1f3d9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},night_with_stars:{keywords:[\"evening\",\"city\",\"downtown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌃\" src=\"1f303.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bridge_at_night:{keywords:[\"photo\",\"sanfrancisco\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌉\" src=\"1f309.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},milky_way:{keywords:[\"photo\",\"space\",\"stars\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌌\" src=\"1f30c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},stars:{keywords:[\"night\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌠\" src=\"1f320.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},sparkler:{keywords:[\"stars\",\"night\",\"shine\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎇\" src=\"1f387.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},fireworks:{keywords:[\"photo\",\"festival\",\"carnival\",\"congratulations\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎆\" src=\"1f386.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},rainbow:{keywords:[\"nature\",\"happy\",\"unicorn_face\",\"photo\",\"sky\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌈\" src=\"1f308.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},houses:{keywords:[\"buildings\",\"photo\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏘\" src=\"1f3d8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},european_castle:{keywords:[\"building\",\"royalty\",\"history\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏰\" src=\"1f3f0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},japanese_castle:{keywords:[\"photo\",\"building\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏯\" src=\"1f3ef.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},stadium:{keywords:[\"photo\",\"place\",\"sports\",\"concert\",\"venue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏟\" src=\"1f3df.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},statue_of_liberty:{keywords:[\"american\",\"newyork\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗽\" src=\"1f5fd.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},house:{keywords:[\"building\",\"home\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏠\" src=\"1f3e0.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},house_with_garden:{keywords:[\"home\",\"plant\",\"nature\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏡\" src=\"1f3e1.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},derelict_house:{keywords:[\"abandon\",\"evict\",\"broken\",\"building\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏚\" src=\"1f3da.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},office:{keywords:[\"building\",\"bureau\",\"work\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏢\" src=\"1f3e2.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},department_store:{keywords:[\"building\",\"shopping\",\"mall\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏬\" src=\"1f3ec.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},post_office:{keywords:[\"building\",\"envelope\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏣\" src=\"1f3e3.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},european_post_office:{keywords:[\"building\",\"email\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏤\" src=\"1f3e4.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},hospital:{keywords:[\"building\",\"health\",\"surgery\",\"doctor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏥\" src=\"1f3e5.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},bank:{keywords:[\"building\",\"money\",\"sales\",\"cash\",\"business\",\"enterprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏦\" src=\"1f3e6.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},hotel:{keywords:[\"building\",\"accomodation\",\"checkin\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏨\" src=\"1f3e8.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},convenience_store:{keywords:[\"building\",\"shopping\",\"groceries\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏪\" src=\"1f3ea.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},school:{keywords:[\"building\",\"student\",\"education\",\"learn\",\"teach\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏫\" src=\"1f3eb.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},love_hotel:{keywords:[\"like\",\"affection\",\"dating\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏩\" src=\"1f3e9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},wedding:{keywords:[\"love\",\"like\",\"affection\",\"couple\",\"marriage\",\"bride\",\"groom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💒\" src=\"1f492.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},classical_building:{keywords:[\"art\",\"culture\",\"history\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏛\" src=\"1f3db.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},church:{keywords:[\"building\",\"religion\",\"christ\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛪\" src=\"26ea.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},mosque:{keywords:[\"islam\",\"worship\",\"minaret\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕌\" src=\"1f54c.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},synagogue:{keywords:[\"judaism\",\"worship\",\"temple\",\"jewish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕍\" src=\"1f54d.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},kaaba:{keywords:[\"mecca\",\"mosque\",\"islam\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕋\" src=\"1f54b.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},shinto_shrine:{keywords:[\"temple\",\"japan\",\"kyoto\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛩\" src=\"26e9.png\"/>',fitzpatrick_scale:false,category:\"travel_and_places\"},watch:{keywords:[\"time\",\"accessories\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⌚\" src=\"231a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},iphone:{keywords:[\"technology\",\"apple\",\"gadgets\",\"dial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📱\" src=\"1f4f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},calling:{keywords:[\"iphone\",\"incoming\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📲\" src=\"1f4f2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},computer:{keywords:[\"technology\",\"laptop\",\"screen\",\"display\",\"monitor\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💻\" src=\"1f4bb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},keyboard:{keywords:[\"technology\",\"computer\",\"type\",\"input\",\"text\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⌨\" src=\"2328.png\"/>',fitzpatrick_scale:false,category:\"objects\"},desktop_computer:{keywords:[\"technology\",\"computing\",\"screen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖥\" src=\"1f5a5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},printer:{keywords:[\"paper\",\"ink\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖨\" src=\"1f5a8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},computer_mouse:{keywords:[\"click\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖱\" src=\"1f5b1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},trackball:{keywords:[\"technology\",\"trackpad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖲\" src=\"1f5b2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},joystick:{keywords:[\"game\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕹\" src=\"1f579.png\"/>',fitzpatrick_scale:false,category:\"objects\"},clamp:{keywords:[\"tool\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗜\" src=\"1f5dc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},minidisc:{keywords:[\"technology\",\"record\",\"data\",\"disk\",\"90s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💽\" src=\"1f4bd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},floppy_disk:{keywords:[\"oldschool\",\"technology\",\"save\",\"90s\",\"80s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💾\" src=\"1f4be.png\"/>',fitzpatrick_scale:false,category:\"objects\"},cd:{keywords:[\"technology\",\"dvd\",\"disk\",\"disc\",\"90s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💿\" src=\"1f4bf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dvd:{keywords:[\"cd\",\"disk\",\"disc\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📀\" src=\"1f4c0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},vhs:{keywords:[\"record\",\"video\",\"oldschool\",\"90s\",\"80s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📼\" src=\"1f4fc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},camera:{keywords:[\"gadgets\",\"photography\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📷\" src=\"1f4f7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},camera_flash:{keywords:[\"photography\",\"gadgets\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📸\" src=\"1f4f8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},video_camera:{keywords:[\"film\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📹\" src=\"1f4f9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},movie_camera:{keywords:[\"film\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎥\" src=\"1f3a5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},film_projector:{keywords:[\"video\",\"tape\",\"record\",\"movie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📽\" src=\"1f4fd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},film_strip:{keywords:[\"movie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎞\" src=\"1f39e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},telephone_receiver:{keywords:[\"technology\",\"communication\",\"dial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📞\" src=\"1f4de.png\"/>',fitzpatrick_scale:false,category:\"objects\"},phone:{keywords:[\"technology\",\"communication\",\"dial\",\"telephone\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☎️\" src=\"260e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pager:{keywords:[\"bbcall\",\"oldschool\",\"90s\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📟\" src=\"1f4df.png\"/>',fitzpatrick_scale:false,category:\"objects\"},fax:{keywords:[\"communication\",\"technology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📠\" src=\"1f4e0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},tv:{keywords:[\"technology\",\"program\",\"oldschool\",\"show\",\"television\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📺\" src=\"1f4fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},radio:{keywords:[\"communication\",\"music\",\"podcast\",\"program\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📻\" src=\"1f4fb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},studio_microphone:{keywords:[\"sing\",\"recording\",\"artist\",\"talkshow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎙\" src=\"1f399.png\"/>',fitzpatrick_scale:false,category:\"objects\"},level_slider:{keywords:[\"scale\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎚\" src=\"1f39a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},control_knobs:{keywords:[\"dial\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎛\" src=\"1f39b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},compass:{keywords:[\"magnetic\",\"navigation\",\"orienteering\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧭\" src=\"1f9ed.png\"/>',fitzpatrick_scale:false,category:\"objects\"},stopwatch:{keywords:[\"time\",\"deadline\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏱\" src=\"23f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},timer_clock:{keywords:[\"alarm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏲\" src=\"23f2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},alarm_clock:{keywords:[\"time\",\"wake\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏰\" src=\"23f0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mantelpiece_clock:{keywords:[\"time\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕰\" src=\"1f570.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hourglass_flowing_sand:{keywords:[\"oldschool\",\"time\",\"countdown\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏳\" src=\"23f3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hourglass:{keywords:[\"time\",\"clock\",\"oldschool\",\"limit\",\"exam\",\"quiz\",\"test\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⌛\" src=\"231b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},satellite:{keywords:[\"communication\",\"future\",\"radio\",\"space\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📡\" src=\"1f4e1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},battery:{keywords:[\"power\",\"energy\",\"sustain\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔋\" src=\"1f50b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},electric_plug:{keywords:[\"charger\",\"power\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔌\" src=\"1f50c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bulb:{keywords:[\"light\",\"electricity\",\"idea\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💡\" src=\"1f4a1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},flashlight:{keywords:[\"dark\",\"camping\",\"sight\",\"night\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔦\" src=\"1f526.png\"/>',fitzpatrick_scale:false,category:\"objects\"},candle:{keywords:[\"fire\",\"wax\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕯\" src=\"1f56f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},fire_extinguisher:{keywords:[\"quench\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧯\" src=\"1f9ef.png\"/>',fitzpatrick_scale:false,category:\"objects\"},wastebasket:{keywords:[\"bin\",\"trash\",\"rubbish\",\"garbage\",\"toss\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗑\" src=\"1f5d1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},oil_drum:{keywords:[\"barrell\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛢\" src=\"1f6e2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},money_with_wings:{keywords:[\"dollar\",\"bills\",\"payment\",\"sale\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💸\" src=\"1f4b8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dollar:{keywords:[\"money\",\"sales\",\"bill\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💵\" src=\"1f4b5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},yen:{keywords:[\"money\",\"sales\",\"japanese\",\"dollar\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💴\" src=\"1f4b4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},euro:{keywords:[\"money\",\"sales\",\"dollar\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💶\" src=\"1f4b6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pound:{keywords:[\"british\",\"sterling\",\"money\",\"sales\",\"bills\",\"uk\",\"england\",\"currency\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💷\" src=\"1f4b7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},moneybag:{keywords:[\"dollar\",\"payment\",\"coins\",\"sale\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💰\" src=\"1f4b0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},credit_card:{keywords:[\"money\",\"sales\",\"dollar\",\"bill\",\"payment\",\"shopping\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💳\" src=\"1f4b3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gem:{keywords:[\"blue\",\"ruby\",\"diamond\",\"jewelry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💎\" src=\"1f48e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},balance_scale:{keywords:[\"law\",\"fairness\",\"weight\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚖\" src=\"2696.png\"/>',fitzpatrick_scale:false,category:\"objects\"},toolbox:{keywords:[\"tools\",\"diy\",\"fix\",\"maintainer\",\"mechanic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧰\" src=\"1f9f0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},wrench:{keywords:[\"tools\",\"diy\",\"ikea\",\"fix\",\"maintainer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔧\" src=\"1f527.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hammer:{keywords:[\"tools\",\"build\",\"create\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔨\" src=\"1f528.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hammer_and_pick:{keywords:[\"tools\",\"build\",\"create\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚒\" src=\"2692.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hammer_and_wrench:{keywords:[\"tools\",\"build\",\"create\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛠\" src=\"1f6e0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pick:{keywords:[\"tools\",\"dig\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛏\" src=\"26cf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},nut_and_bolt:{keywords:[\"handy\",\"tools\",\"fix\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔩\" src=\"1f529.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gear:{keywords:[\"cog\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚙\" src=\"2699.png\"/>',fitzpatrick_scale:false,category:\"objects\"},brick:{keywords:[\"bricks\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧱\" src=\"1f9f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},chains:{keywords:[\"lock\",\"arrest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛓\" src=\"26d3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},magnet:{keywords:[\"attraction\",\"magnetic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧲\" src=\"1f9f2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gun:{keywords:[\"violence\",\"weapon\",\"pistol\",\"revolver\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔫\" src=\"1f52b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bomb:{keywords:[\"boom\",\"explode\",\"explosion\",\"terrorism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💣\" src=\"1f4a3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},firecracker:{keywords:[\"dynamite\",\"boom\",\"explode\",\"explosion\",\"explosive\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧨\" src=\"1f9e8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hocho:{keywords:[\"knife\",\"blade\",\"cutlery\",\"kitchen\",\"weapon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔪\" src=\"1f52a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dagger:{keywords:[\"weapon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗡\" src=\"1f5e1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crossed_swords:{keywords:[\"weapon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚔\" src=\"2694.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shield:{keywords:[\"protection\",\"security\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛡\" src=\"1f6e1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},smoking:{keywords:[\"kills\",\"tobacco\",\"cigarette\",\"joint\",\"smoke\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚬\" src=\"1f6ac.png\"/>',fitzpatrick_scale:false,category:\"objects\"},skull_and_crossbones:{keywords:[\"poison\",\"danger\",\"deadly\",\"scary\",\"death\",\"pirate\",\"evil\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☠\" src=\"2620.png\"/>',fitzpatrick_scale:false,category:\"objects\"},coffin:{keywords:[\"vampire\",\"dead\",\"die\",\"death\",\"rip\",\"graveyard\",\"cemetery\",\"casket\",\"funeral\",\"box\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚰\" src=\"26b0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},funeral_urn:{keywords:[\"dead\",\"die\",\"death\",\"rip\",\"ashes\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚱\" src=\"26b1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},amphora:{keywords:[\"vase\",\"jar\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏺\" src=\"1f3fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crystal_ball:{keywords:[\"disco\",\"party\",\"magic\",\"circus\",\"fortune_teller\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔮\" src=\"1f52e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},prayer_beads:{keywords:[\"dhikr\",\"religious\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📿\" src=\"1f4ff.png\"/>',fitzpatrick_scale:false,category:\"objects\"},nazar_amulet:{keywords:[\"bead\",\"charm\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧿\" src=\"1f9ff.png\"/>',fitzpatrick_scale:false,category:\"objects\"},barber:{keywords:[\"hair\",\"salon\",\"style\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💈\" src=\"1f488.png\"/>',fitzpatrick_scale:false,category:\"objects\"},alembic:{keywords:[\"distilling\",\"science\",\"experiment\",\"chemistry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚗\" src=\"2697.png\"/>',fitzpatrick_scale:false,category:\"objects\"},telescope:{keywords:[\"stars\",\"space\",\"zoom\",\"science\",\"astronomy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔭\" src=\"1f52d.png\"/>',fitzpatrick_scale:false,category:\"objects\"},microscope:{keywords:[\"laboratory\",\"experiment\",\"zoomin\",\"science\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔬\" src=\"1f52c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},hole:{keywords:[\"embarrassing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕳\" src=\"1f573.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pill:{keywords:[\"health\",\"medicine\",\"doctor\",\"pharmacy\",\"drug\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💊\" src=\"1f48a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},syringe:{keywords:[\"health\",\"hospital\",\"drugs\",\"blood\",\"medicine\",\"needle\",\"doctor\",\"nurse\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💉\" src=\"1f489.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dna:{keywords:[\"biologist\",\"genetics\",\"life\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧬\" src=\"1f9ec.png\"/>',fitzpatrick_scale:false,category:\"objects\"},microbe:{keywords:[\"amoeba\",\"bacteria\",\"germs\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🦠\" src=\"1f9a0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},petri_dish:{keywords:[\"bacteria\",\"biology\",\"culture\",\"lab\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧫\" src=\"1f9eb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},test_tube:{keywords:[\"chemistry\",\"experiment\",\"lab\",\"science\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧪\" src=\"1f9ea.png\"/>',fitzpatrick_scale:false,category:\"objects\"},thermometer:{keywords:[\"weather\",\"temperature\",\"hot\",\"cold\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌡\" src=\"1f321.png\"/>',fitzpatrick_scale:false,category:\"objects\"},broom:{keywords:[\"cleaning\",\"sweeping\",\"witch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧹\" src=\"1f9f9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},basket:{keywords:[\"laundry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧺\" src=\"1f9fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},toilet_paper:{keywords:[\"roll\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧻\" src=\"1f9fb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},label:{keywords:[\"sale\",\"tag\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏷\" src=\"1f3f7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bookmark:{keywords:[\"favorite\",\"label\",\"save\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔖\" src=\"1f516.png\"/>',fitzpatrick_scale:false,category:\"objects\"},toilet:{keywords:[\"restroom\",\"wc\",\"washroom\",\"bathroom\",\"potty\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚽\" src=\"1f6bd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shower:{keywords:[\"clean\",\"water\",\"bathroom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚿\" src=\"1f6bf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bathtub:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛁\" src=\"1f6c1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},soap:{keywords:[\"bar\",\"bathing\",\"cleaning\",\"lather\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧼\" src=\"1f9fc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},sponge:{keywords:[\"absorbing\",\"cleaning\",\"porous\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧽\" src=\"1f9fd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},lotion_bottle:{keywords:[\"moisturizer\",\"sunscreen\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧴\" src=\"1f9f4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},key:{keywords:[\"lock\",\"door\",\"password\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔑\" src=\"1f511.png\"/>',fitzpatrick_scale:false,category:\"objects\"},old_key:{keywords:[\"lock\",\"door\",\"password\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗝\" src=\"1f5dd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},couch_and_lamp:{keywords:[\"read\",\"chill\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛋\" src=\"1f6cb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},sleeping_bed:{keywords:[\"bed\",\"rest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛌\" src=\"1f6cc.png\"/>',fitzpatrick_scale:true,category:\"objects\"},bed:{keywords:[\"sleep\",\"rest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛏\" src=\"1f6cf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},door:{keywords:[\"house\",\"entry\",\"exit\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚪\" src=\"1f6aa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bellhop_bell:{keywords:[\"service\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛎\" src=\"1f6ce.png\"/>',fitzpatrick_scale:false,category:\"objects\"},teddy_bear:{keywords:[\"plush\",\"stuffed\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧸\" src=\"1f9f8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},framed_picture:{keywords:[\"photography\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖼\" src=\"1f5bc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},world_map:{keywords:[\"location\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗺\" src=\"1f5fa.png\"/>',fitzpatrick_scale:false,category:\"objects\"},parasol_on_ground:{keywords:[\"weather\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛱\" src=\"26f1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},moyai:{keywords:[\"rock\",\"easter island\",\"moai\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗿\" src=\"1f5ff.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shopping:{keywords:[\"mall\",\"buy\",\"purchase\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛍\" src=\"1f6cd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},shopping_cart:{keywords:[\"trolley\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛒\" src=\"1f6d2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},balloon:{keywords:[\"party\",\"celebration\",\"birthday\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎈\" src=\"1f388.png\"/>',fitzpatrick_scale:false,category:\"objects\"},flags:{keywords:[\"fish\",\"japanese\",\"koinobori\",\"carp\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎏\" src=\"1f38f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},ribbon:{keywords:[\"decoration\",\"pink\",\"girl\",\"bowtie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎀\" src=\"1f380.png\"/>',fitzpatrick_scale:false,category:\"objects\"},gift:{keywords:[\"present\",\"birthday\",\"christmas\",\"xmas\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎁\" src=\"1f381.png\"/>',fitzpatrick_scale:false,category:\"objects\"},confetti_ball:{keywords:[\"festival\",\"party\",\"birthday\",\"circus\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎊\" src=\"1f38a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},tada:{keywords:[\"party\",\"congratulations\",\"birthday\",\"magic\",\"circus\",\"celebration\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎉\" src=\"1f389.png\"/>',fitzpatrick_scale:false,category:\"objects\"},dolls:{keywords:[\"japanese\",\"toy\",\"kimono\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎎\" src=\"1f38e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},wind_chime:{keywords:[\"nature\",\"ding\",\"spring\",\"bell\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎐\" src=\"1f390.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crossed_flags:{keywords:[\"japanese\",\"nation\",\"country\",\"border\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎌\" src=\"1f38c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},izakaya_lantern:{keywords:[\"light\",\"paper\",\"halloween\",\"spooky\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏮\" src=\"1f3ee.png\"/>',fitzpatrick_scale:false,category:\"objects\"},red_envelope:{keywords:[\"gift\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧧\" src=\"1f9e7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},email:{keywords:[\"letter\",\"postal\",\"inbox\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✉️\" src=\"2709.png\"/>',fitzpatrick_scale:false,category:\"objects\"},envelope_with_arrow:{keywords:[\"email\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📩\" src=\"1f4e9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},incoming_envelope:{keywords:[\"email\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📨\" src=\"1f4e8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},\"e-mail\":{keywords:[\"communication\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📧\" src=\"1f4e7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},love_letter:{keywords:[\"email\",\"like\",\"affection\",\"envelope\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💌\" src=\"1f48c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},postbox:{keywords:[\"email\",\"letter\",\"envelope\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📮\" src=\"1f4ee.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox_closed:{keywords:[\"email\",\"communication\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📪\" src=\"1f4ea.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox:{keywords:[\"email\",\"inbox\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📫\" src=\"1f4eb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox_with_mail:{keywords:[\"email\",\"inbox\",\"communication\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📬\" src=\"1f4ec.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mailbox_with_no_mail:{keywords:[\"email\",\"inbox\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📭\" src=\"1f4ed.png\"/>',fitzpatrick_scale:false,category:\"objects\"},package:{keywords:[\"mail\",\"gift\",\"cardboard\",\"box\",\"moving\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📦\" src=\"1f4e6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},postal_horn:{keywords:[\"instrument\",\"music\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📯\" src=\"1f4ef.png\"/>',fitzpatrick_scale:false,category:\"objects\"},inbox_tray:{keywords:[\"email\",\"documents\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📥\" src=\"1f4e5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},outbox_tray:{keywords:[\"inbox\",\"email\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📤\" src=\"1f4e4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},scroll:{keywords:[\"documents\",\"ancient\",\"history\",\"paper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📜\" src=\"1f4dc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},page_with_curl:{keywords:[\"documents\",\"office\",\"paper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📃\" src=\"1f4c3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bookmark_tabs:{keywords:[\"favorite\",\"save\",\"order\",\"tidy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📑\" src=\"1f4d1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},receipt:{keywords:[\"accounting\",\"expenses\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧾\" src=\"1f9fe.png\"/>',fitzpatrick_scale:false,category:\"objects\"},bar_chart:{keywords:[\"graph\",\"presentation\",\"stats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📊\" src=\"1f4ca.png\"/>',fitzpatrick_scale:false,category:\"objects\"},chart_with_upwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recovery\",\"business\",\"economics\",\"money\",\"sales\",\"good\",\"success\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📈\" src=\"1f4c8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},chart_with_downwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recession\",\"business\",\"economics\",\"money\",\"sales\",\"bad\",\"failure\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📉\" src=\"1f4c9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},page_facing_up:{keywords:[\"documents\",\"office\",\"paper\",\"information\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📄\" src=\"1f4c4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},date:{keywords:[\"calendar\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📅\" src=\"1f4c5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},calendar:{keywords:[\"schedule\",\"date\",\"planning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📆\" src=\"1f4c6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},spiral_calendar:{keywords:[\"date\",\"schedule\",\"planning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗓\" src=\"1f5d3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},card_index:{keywords:[\"business\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📇\" src=\"1f4c7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},card_file_box:{keywords:[\"business\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗃\" src=\"1f5c3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},ballot_box:{keywords:[\"election\",\"vote\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗳\" src=\"1f5f3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},file_cabinet:{keywords:[\"filing\",\"organizing\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗄\" src=\"1f5c4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},clipboard:{keywords:[\"stationery\",\"documents\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📋\" src=\"1f4cb.png\"/>',fitzpatrick_scale:false,category:\"objects\"},spiral_notepad:{keywords:[\"memo\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗒\" src=\"1f5d2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},file_folder:{keywords:[\"documents\",\"business\",\"office\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📁\" src=\"1f4c1.png\"/>',fitzpatrick_scale:false,category:\"objects\"},open_file_folder:{keywords:[\"documents\",\"load\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📂\" src=\"1f4c2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},card_index_dividers:{keywords:[\"organizing\",\"business\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗂\" src=\"1f5c2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},newspaper_roll:{keywords:[\"press\",\"headline\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗞\" src=\"1f5de.png\"/>',fitzpatrick_scale:false,category:\"objects\"},newspaper:{keywords:[\"press\",\"headline\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📰\" src=\"1f4f0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},notebook:{keywords:[\"stationery\",\"record\",\"notes\",\"paper\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📓\" src=\"1f4d3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},closed_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"learn\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📕\" src=\"1f4d5.png\"/>',fitzpatrick_scale:false,category:\"objects\"},green_book:{keywords:[\"read\",\"library\",\"knowledge\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📗\" src=\"1f4d7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},blue_book:{keywords:[\"read\",\"library\",\"knowledge\",\"learn\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📘\" src=\"1f4d8.png\"/>',fitzpatrick_scale:false,category:\"objects\"},orange_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📙\" src=\"1f4d9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},notebook_with_decorative_cover:{keywords:[\"classroom\",\"notes\",\"record\",\"paper\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📔\" src=\"1f4d4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},ledger:{keywords:[\"notes\",\"paper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📒\" src=\"1f4d2.png\"/>',fitzpatrick_scale:false,category:\"objects\"},books:{keywords:[\"literature\",\"library\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📚\" src=\"1f4da.png\"/>',fitzpatrick_scale:false,category:\"objects\"},open_book:{keywords:[\"book\",\"read\",\"library\",\"knowledge\",\"literature\",\"learn\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📖\" src=\"1f4d6.png\"/>',fitzpatrick_scale:false,category:\"objects\"},safety_pin:{keywords:[\"diaper\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧷\" src=\"1f9f7.png\"/>',fitzpatrick_scale:false,category:\"objects\"},link:{keywords:[\"rings\",\"url\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔗\" src=\"1f517.png\"/>',fitzpatrick_scale:false,category:\"objects\"},paperclip:{keywords:[\"documents\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📎\" src=\"1f4ce.png\"/>',fitzpatrick_scale:false,category:\"objects\"},paperclips:{keywords:[\"documents\",\"stationery\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖇\" src=\"1f587.png\"/>',fitzpatrick_scale:false,category:\"objects\"},scissors:{keywords:[\"stationery\",\"cut\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✂️\" src=\"2702.png\"/>',fitzpatrick_scale:false,category:\"objects\"},triangular_ruler:{keywords:[\"stationery\",\"math\",\"architect\",\"sketch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📐\" src=\"1f4d0.png\"/>',fitzpatrick_scale:false,category:\"objects\"},straight_ruler:{keywords:[\"stationery\",\"calculate\",\"length\",\"math\",\"school\",\"drawing\",\"architect\",\"sketch\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📏\" src=\"1f4cf.png\"/>',fitzpatrick_scale:false,category:\"objects\"},abacus:{keywords:[\"calculation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧮\" src=\"1f9ee.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pushpin:{keywords:[\"stationery\",\"mark\",\"here\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📌\" src=\"1f4cc.png\"/>',fitzpatrick_scale:false,category:\"objects\"},round_pushpin:{keywords:[\"stationery\",\"location\",\"map\",\"here\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📍\" src=\"1f4cd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},triangular_flag_on_post:{keywords:[\"mark\",\"milestone\",\"place\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚩\" src=\"1f6a9.png\"/>',fitzpatrick_scale:false,category:\"objects\"},white_flag:{keywords:[\"losing\",\"loser\",\"lost\",\"surrender\",\"give up\",\"fail\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏳\" src=\"1f3f3.png\"/>',fitzpatrick_scale:false,category:\"objects\"},black_flag:{keywords:[\"pirate\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴\" src=\"1f3f4.png\"/>',fitzpatrick_scale:false,category:\"objects\"},rainbow_flag:{keywords:[\"flag\",\"rainbow\",\"pride\",\"gay\",\"lgbt\",\"glbt\",\"queer\",\"homosexual\",\"lesbian\",\"bisexual\",\"transgender\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏳️‍🌈\" src=\"1f3f3-fe0f-200d-1f308.png\"/>',fitzpatrick_scale:false,category:\"objects\"},closed_lock_with_key:{keywords:[\"security\",\"privacy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔐\" src=\"1f510.png\"/>',fitzpatrick_scale:false,category:\"objects\"},lock:{keywords:[\"security\",\"password\",\"padlock\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔒\" src=\"1f512.png\"/>',fitzpatrick_scale:false,category:\"objects\"},unlock:{keywords:[\"privacy\",\"security\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔓\" src=\"1f513.png\"/>',fitzpatrick_scale:false,category:\"objects\"},lock_with_ink_pen:{keywords:[\"security\",\"secret\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔏\" src=\"1f50f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖊\" src=\"1f58a.png\"/>',fitzpatrick_scale:false,category:\"objects\"},fountain_pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖋\" src=\"1f58b.png\"/>',fitzpatrick_scale:false,category:\"objects\"},black_nib:{keywords:[\"pen\",\"stationery\",\"writing\",\"write\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✒️\" src=\"2712.png\"/>',fitzpatrick_scale:false,category:\"objects\"},memo:{keywords:[\"write\",\"documents\",\"stationery\",\"pencil\",\"paper\",\"writing\",\"legal\",\"exam\",\"quiz\",\"test\",\"study\",\"compose\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📝\" src=\"1f4dd.png\"/>',fitzpatrick_scale:false,category:\"objects\"},pencil2:{keywords:[\"stationery\",\"write\",\"paper\",\"writing\",\"school\",\"study\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✏️\" src=\"270f.png\"/>',fitzpatrick_scale:false,category:\"objects\"},crayon:{keywords:[\"drawing\",\"creativity\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖍\" src=\"1f58d.png\"/>',fitzpatrick_scale:false,category:\"objects\"},paintbrush:{keywords:[\"drawing\",\"creativity\",\"art\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖌\" src=\"1f58c.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mag:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔍\" src=\"1f50d.png\"/>',fitzpatrick_scale:false,category:\"objects\"},mag_right:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔎\" src=\"1f50e.png\"/>',fitzpatrick_scale:false,category:\"objects\"},heart:{keywords:[\"love\",\"like\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❤️\" src=\"2764.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},orange_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🧡\" src=\"1f9e1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},yellow_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💛\" src=\"1f49b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},green_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💚\" src=\"1f49a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},blue_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💙\" src=\"1f499.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},purple_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💜\" src=\"1f49c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_heart:{keywords:[\"evil\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🖤\" src=\"1f5a4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},broken_heart:{keywords:[\"sad\",\"sorry\",\"break\",\"heart\",\"heartbreak\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💔\" src=\"1f494.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_heart_exclamation:{keywords:[\"decoration\",\"love\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❣\" src=\"2763.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},two_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💕\" src=\"1f495.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},revolving_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💞\" src=\"1f49e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heartbeat:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"pink\",\"heart\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💓\" src=\"1f493.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heartpulse:{keywords:[\"like\",\"love\",\"affection\",\"valentines\",\"pink\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💗\" src=\"1f497.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sparkling_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💖\" src=\"1f496.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cupid:{keywords:[\"love\",\"like\",\"heart\",\"affection\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💘\" src=\"1f498.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},gift_heart:{keywords:[\"love\",\"valentines\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💝\" src=\"1f49d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heart_decoration:{keywords:[\"purple-square\",\"love\",\"like\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💟\" src=\"1f49f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},peace_symbol:{keywords:[\"hippie\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☮\" src=\"262e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},latin_cross:{keywords:[\"christianity\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✝\" src=\"271d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},star_and_crescent:{keywords:[\"islam\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☪\" src=\"262a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},om:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕉\" src=\"1f549.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wheel_of_dharma:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☸\" src=\"2638.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},star_of_david:{keywords:[\"judaism\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✡\" src=\"2721.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},six_pointed_star:{keywords:[\"purple-square\",\"religion\",\"jewish\",\"hexagram\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔯\" src=\"1f52f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},menorah:{keywords:[\"hanukkah\",\"candles\",\"jewish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕎\" src=\"1f54e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},yin_yang:{keywords:[\"balance\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☯\" src=\"262f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},orthodox_cross:{keywords:[\"suppedaneum\",\"religion\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☦\" src=\"2626.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},place_of_worship:{keywords:[\"religion\",\"church\",\"temple\",\"prayer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛐\" src=\"1f6d0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ophiuchus:{keywords:[\"sign\",\"purple-square\",\"constellation\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛎\" src=\"26ce.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},aries:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♈\" src=\"2648.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},taurus:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♉\" src=\"2649.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},gemini:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♊\" src=\"264a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cancer:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♋\" src=\"264b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},leo:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♌\" src=\"264c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},virgo:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♍\" src=\"264d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},libra:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♎\" src=\"264e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},scorpius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\",\"scorpio\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♏\" src=\"264f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sagittarius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♐\" src=\"2650.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},capricorn:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♑\" src=\"2651.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},aquarius:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♒\" src=\"2652.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},pisces:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♓\" src=\"2653.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},id:{keywords:[\"purple-square\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆔\" src=\"1f194.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},atom_symbol:{keywords:[\"science\",\"physics\",\"chemistry\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚛\" src=\"269b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7a7a:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"empty\",\"sky\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈳\" src=\"1f233.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u5272:{keywords:[\"cut\",\"divide\",\"chinese\",\"kanji\",\"pink-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈹\" src=\"1f239.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},radioactive:{keywords:[\"nuclear\",\"danger\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☢\" src=\"2622.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},biohazard:{keywords:[\"danger\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☣\" src=\"2623.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mobile_phone_off:{keywords:[\"mute\",\"orange-square\",\"silence\",\"quiet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📴\" src=\"1f4f4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},vibration_mode:{keywords:[\"orange-square\",\"phone\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📳\" src=\"1f4f3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6709:{keywords:[\"orange-square\",\"chinese\",\"have\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈶\" src=\"1f236.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7121:{keywords:[\"nothing\",\"chinese\",\"kanji\",\"japanese\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈚\" src=\"1f21a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7533:{keywords:[\"chinese\",\"japanese\",\"kanji\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈸\" src=\"1f238.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u55b6:{keywords:[\"japanese\",\"opening hours\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈺\" src=\"1f23a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6708:{keywords:[\"chinese\",\"month\",\"moon\",\"japanese\",\"orange-square\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈷️\" src=\"1f237.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eight_pointed_black_star:{keywords:[\"orange-square\",\"shape\",\"polygon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✴️\" src=\"2734.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},vs:{keywords:[\"words\",\"orange-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆚\" src=\"1f19a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},accept:{keywords:[\"ok\",\"good\",\"chinese\",\"kanji\",\"agree\",\"yes\",\"orange-circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🉑\" src=\"1f251.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_flower:{keywords:[\"japanese\",\"spring\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💮\" src=\"1f4ae.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ideograph_advantage:{keywords:[\"chinese\",\"kanji\",\"obtain\",\"get\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🉐\" src=\"1f250.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},secret:{keywords:[\"privacy\",\"chinese\",\"sshh\",\"kanji\",\"red-circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"㊙️\" src=\"3299.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},congratulations:{keywords:[\"chinese\",\"kanji\",\"japanese\",\"red-circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"㊗️\" src=\"3297.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u5408:{keywords:[\"japanese\",\"chinese\",\"join\",\"kanji\",\"red-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈴\" src=\"1f234.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6e80:{keywords:[\"full\",\"chinese\",\"japanese\",\"red-square\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈵\" src=\"1f235.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u7981:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"forbidden\",\"limit\",\"restricted\",\"red-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈲\" src=\"1f232.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},a:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅰️\" src=\"1f170.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},b:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅱️\" src=\"1f171.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ab:{keywords:[\"red-square\",\"alphabet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆎\" src=\"1f18e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cl:{keywords:[\"alphabet\",\"words\",\"red-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆑\" src=\"1f191.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},o2:{keywords:[\"alphabet\",\"red-square\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅾️\" src=\"1f17e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sos:{keywords:[\"help\",\"red-square\",\"words\",\"emergency\",\"911\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆘\" src=\"1f198.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_entry:{keywords:[\"limit\",\"security\",\"privacy\",\"bad\",\"denied\",\"stop\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⛔\" src=\"26d4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},name_badge:{keywords:[\"fire\",\"forbid\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📛\" src=\"1f4db.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_entry_sign:{keywords:[\"forbid\",\"stop\",\"limit\",\"denied\",\"disallow\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚫\" src=\"1f6ab.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},x:{keywords:[\"no\",\"delete\",\"remove\",\"cancel\",\"red\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❌\" src=\"274c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},o:{keywords:[\"circle\",\"round\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⭕\" src=\"2b55.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},stop_sign:{keywords:[\"stop\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛑\" src=\"1f6d1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},anger:{keywords:[\"angry\",\"mad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💢\" src=\"1f4a2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},hotsprings:{keywords:[\"bath\",\"warm\",\"relax\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♨️\" src=\"2668.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_pedestrians:{keywords:[\"rules\",\"crossing\",\"walking\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚷\" src=\"1f6b7.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},do_not_litter:{keywords:[\"trash\",\"bin\",\"garbage\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚯\" src=\"1f6af.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_bicycles:{keywords:[\"cyclist\",\"prohibited\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚳\" src=\"1f6b3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},\"non-potable_water\":{keywords:[\"drink\",\"faucet\",\"tap\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚱\" src=\"1f6b1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},underage:{keywords:[\"18\",\"drink\",\"pub\",\"night\",\"minor\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔞\" src=\"1f51e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_mobile_phones:{keywords:[\"iphone\",\"mute\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📵\" src=\"1f4f5.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},exclamation:{keywords:[\"heavy_exclamation_mark\",\"danger\",\"surprise\",\"punctuation\",\"wow\",\"warning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❗\" src=\"2757.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},grey_exclamation:{keywords:[\"surprise\",\"punctuation\",\"gray\",\"wow\",\"warning\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❕\" src=\"2755.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},question:{keywords:[\"doubt\",\"confused\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❓\" src=\"2753.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},grey_question:{keywords:[\"doubts\",\"gray\",\"huh\",\"confused\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❔\" src=\"2754.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},bangbang:{keywords:[\"exclamation\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"‼️\" src=\"203c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},interrobang:{keywords:[\"wat\",\"punctuation\",\"surprise\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⁉️\" src=\"2049.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},low_brightness:{keywords:[\"sun\",\"afternoon\",\"warm\",\"summer\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔅\" src=\"1f505.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},high_brightness:{keywords:[\"sun\",\"light\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔆\" src=\"1f506.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},trident:{keywords:[\"weapon\",\"spear\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔱\" src=\"1f531.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},fleur_de_lis:{keywords:[\"decorative\",\"scout\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚜\" src=\"269c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},part_alternation_mark:{keywords:[\"graph\",\"presentation\",\"stats\",\"business\",\"economics\",\"bad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"〽️\" src=\"303d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},warning:{keywords:[\"exclamation\",\"wip\",\"alert\",\"error\",\"problem\",\"issue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚠️\" src=\"26a0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},children_crossing:{keywords:[\"school\",\"warning\",\"danger\",\"sign\",\"driving\",\"yellow-diamond\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚸\" src=\"1f6b8.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},beginner:{keywords:[\"badge\",\"shield\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔰\" src=\"1f530.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},recycle:{keywords:[\"arrow\",\"environment\",\"garbage\",\"trash\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♻️\" src=\"267b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},u6307:{keywords:[\"chinese\",\"point\",\"green-square\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈯\" src=\"1f22f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},chart:{keywords:[\"green-square\",\"graph\",\"presentation\",\"stats\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💹\" src=\"1f4b9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sparkle:{keywords:[\"stars\",\"green-square\",\"awesome\",\"good\",\"fireworks\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❇️\" src=\"2747.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eight_spoked_asterisk:{keywords:[\"star\",\"sparkle\",\"green-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✳️\" src=\"2733.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},negative_squared_cross_mark:{keywords:[\"x\",\"green-square\",\"no\",\"deny\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"❎\" src=\"274e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_check_mark:{keywords:[\"green-square\",\"ok\",\"agree\",\"vote\",\"election\",\"answer\",\"tick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✅\" src=\"2705.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},diamond_shape_with_a_dot_inside:{keywords:[\"jewel\",\"blue\",\"gem\",\"crystal\",\"fancy\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💠\" src=\"1f4a0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cyclone:{keywords:[\"weather\",\"swirl\",\"blue\",\"cloud\",\"vortex\",\"spiral\",\"whirlpool\",\"spin\",\"tornado\",\"hurricane\",\"typhoon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌀\" src=\"1f300.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},loop:{keywords:[\"tape\",\"cassette\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➿\" src=\"27bf.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},globe_with_meridians:{keywords:[\"earth\",\"international\",\"world\",\"internet\",\"interweb\",\"i18n\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🌐\" src=\"1f310.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},m:{keywords:[\"alphabet\",\"blue-circle\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"Ⓜ️\" src=\"24c2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},atm:{keywords:[\"money\",\"sales\",\"cash\",\"blue-square\",\"payment\",\"bank\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏧\" src=\"1f3e7.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sa:{keywords:[\"japanese\",\"blue-square\",\"katakana\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈂️\" src=\"1f202.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},passport_control:{keywords:[\"custom\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛂\" src=\"1f6c2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},customs:{keywords:[\"passport\",\"border\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛃\" src=\"1f6c3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},baggage_claim:{keywords:[\"blue-square\",\"airport\",\"transport\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛄\" src=\"1f6c4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},left_luggage:{keywords:[\"blue-square\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🛅\" src=\"1f6c5.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wheelchair:{keywords:[\"blue-square\",\"disabled\",\"a11y\",\"accessibility\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♿\" src=\"267f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_smoking:{keywords:[\"cigarette\",\"blue-square\",\"smell\",\"smoke\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚭\" src=\"1f6ad.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wc:{keywords:[\"toilet\",\"restroom\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚾\" src=\"1f6be.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},parking:{keywords:[\"cars\",\"blue-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🅿️\" src=\"1f17f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},potable_water:{keywords:[\"blue-square\",\"liquid\",\"restroom\",\"cleaning\",\"faucet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚰\" src=\"1f6b0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mens:{keywords:[\"toilet\",\"restroom\",\"wc\",\"blue-square\",\"gender\",\"male\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚹\" src=\"1f6b9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},womens:{keywords:[\"purple-square\",\"woman\",\"female\",\"toilet\",\"loo\",\"restroom\",\"gender\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚺\" src=\"1f6ba.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},baby_symbol:{keywords:[\"orange-square\",\"child\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚼\" src=\"1f6bc.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},restroom:{keywords:[\"blue-square\",\"toilet\",\"refresh\",\"wc\",\"gender\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚻\" src=\"1f6bb.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},put_litter_in_its_place:{keywords:[\"blue-square\",\"sign\",\"human\",\"info\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🚮\" src=\"1f6ae.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cinema:{keywords:[\"blue-square\",\"record\",\"film\",\"movie\",\"curtain\",\"stage\",\"theater\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎦\" src=\"1f3a6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},signal_strength:{keywords:[\"blue-square\",\"reception\",\"phone\",\"internet\",\"connection\",\"wifi\",\"bluetooth\",\"bars\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📶\" src=\"1f4f6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},koko:{keywords:[\"blue-square\",\"here\",\"katakana\",\"japanese\",\"destination\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🈁\" src=\"1f201.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ng:{keywords:[\"blue-square\",\"words\",\"shape\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆖\" src=\"1f196.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ok:{keywords:[\"good\",\"agree\",\"yes\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆗\" src=\"1f197.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},up:{keywords:[\"blue-square\",\"above\",\"high\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆙\" src=\"1f199.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},cool:{keywords:[\"words\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆒\" src=\"1f192.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},new:{keywords:[\"blue-square\",\"words\",\"start\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆕\" src=\"1f195.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},free:{keywords:[\"blue-square\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🆓\" src=\"1f193.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},zero:{keywords:[\"0\",\"numbers\",\"blue-square\",\"null\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"0️⃣\" src=\"30-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},one:{keywords:[\"blue-square\",\"numbers\",\"1\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"1️⃣\" src=\"31-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},two:{keywords:[\"numbers\",\"2\",\"prime\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"2️⃣\" src=\"32-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},three:{keywords:[\"3\",\"numbers\",\"prime\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"3️⃣\" src=\"33-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},four:{keywords:[\"4\",\"numbers\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"4️⃣\" src=\"34-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},five:{keywords:[\"5\",\"numbers\",\"blue-square\",\"prime\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"5️⃣\" src=\"35-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},six:{keywords:[\"6\",\"numbers\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"6️⃣\" src=\"36-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},seven:{keywords:[\"7\",\"numbers\",\"blue-square\",\"prime\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"7️⃣\" src=\"37-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eight:{keywords:[\"8\",\"blue-square\",\"numbers\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"8️⃣\" src=\"38-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},nine:{keywords:[\"blue-square\",\"numbers\",\"9\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"9️⃣\" src=\"39-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},keycap_ten:{keywords:[\"numbers\",\"10\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔟\" src=\"1f51f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},asterisk:{keywords:[\"star\",\"keycap\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"*⃣\" src=\"2a-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},eject_button:{keywords:[\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏏️\" src=\"23cf.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_forward:{keywords:[\"blue-square\",\"right\",\"direction\",\"play\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"▶️\" src=\"25b6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},pause_button:{keywords:[\"pause\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏸\" src=\"23f8.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},next_track_button:{keywords:[\"forward\",\"next\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏭\" src=\"23ed.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},stop_button:{keywords:[\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏹\" src=\"23f9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},record_button:{keywords:[\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏺\" src=\"23fa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},play_or_pause_button:{keywords:[\"blue-square\",\"play\",\"pause\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏯\" src=\"23ef.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},previous_track_button:{keywords:[\"backward\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏮\" src=\"23ee.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},fast_forward:{keywords:[\"blue-square\",\"play\",\"speed\",\"continue\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏩\" src=\"23e9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},rewind:{keywords:[\"play\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏪\" src=\"23ea.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},twisted_rightwards_arrows:{keywords:[\"blue-square\",\"shuffle\",\"music\",\"random\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔀\" src=\"1f500.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},repeat:{keywords:[\"loop\",\"record\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔁\" src=\"1f501.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},repeat_one:{keywords:[\"blue-square\",\"loop\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔂\" src=\"1f502.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_backward:{keywords:[\"blue-square\",\"left\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◀️\" src=\"25c0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_up_small:{keywords:[\"blue-square\",\"triangle\",\"direction\",\"point\",\"forward\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔼\" src=\"1f53c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_down_small:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔽\" src=\"1f53d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_double_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏫\" src=\"23eb.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_double_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⏬\" src=\"23ec.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_right:{keywords:[\"blue-square\",\"next\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➡️\" src=\"27a1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_left:{keywords:[\"blue-square\",\"previous\",\"back\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬅️\" src=\"2b05.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_up:{keywords:[\"blue-square\",\"continue\",\"top\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬆️\" src=\"2b06.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬇️\" src=\"2b07.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_right:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northeast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↗️\" src=\"2197.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_right:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southeast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↘️\" src=\"2198.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_left:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southwest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↙️\" src=\"2199.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_left:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northwest\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↖️\" src=\"2196.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_up_down:{keywords:[\"blue-square\",\"direction\",\"way\",\"vertical\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↕️\" src=\"2195.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},left_right_arrow:{keywords:[\"shape\",\"direction\",\"horizontal\",\"sideways\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↔️\" src=\"2194.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrows_counterclockwise:{keywords:[\"blue-square\",\"sync\",\"cycle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔄\" src=\"1f504.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_right_hook:{keywords:[\"blue-square\",\"return\",\"rotate\",\"direction\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↪️\" src=\"21aa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},leftwards_arrow_with_hook:{keywords:[\"back\",\"return\",\"blue-square\",\"undo\",\"enter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"↩️\" src=\"21a9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⤴️\" src=\"2934.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⤵️\" src=\"2935.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},hash:{keywords:[\"symbol\",\"blue-square\",\"twitter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"#️⃣\" src=\"23-20e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},information_source:{keywords:[\"blue-square\",\"alphabet\",\"letter\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"ℹ️\" src=\"2139.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},abc:{keywords:[\"blue-square\",\"alphabet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔤\" src=\"1f524.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},abcd:{keywords:[\"blue-square\",\"alphabet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔡\" src=\"1f521.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},capital_abcd:{keywords:[\"alphabet\",\"words\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔠\" src=\"1f520.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},symbols:{keywords:[\"blue-square\",\"music\",\"note\",\"ampersand\",\"percent\",\"glyphs\",\"characters\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔣\" src=\"1f523.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},musical_note:{keywords:[\"score\",\"tone\",\"sound\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎵\" src=\"1f3b5.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},notes:{keywords:[\"music\",\"score\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎶\" src=\"1f3b6.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},wavy_dash:{keywords:[\"draw\",\"line\",\"moustache\",\"mustache\",\"squiggle\",\"scribble\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"〰️\" src=\"3030.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},curly_loop:{keywords:[\"scribble\",\"draw\",\"shape\",\"squiggle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➰\" src=\"27b0.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_check_mark:{keywords:[\"ok\",\"nike\",\"answer\",\"yes\",\"tick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✔️\" src=\"2714.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},arrows_clockwise:{keywords:[\"sync\",\"cycle\",\"round\",\"repeat\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔃\" src=\"1f503.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_plus_sign:{keywords:[\"math\",\"calculation\",\"addition\",\"more\",\"increase\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➕\" src=\"2795.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_minus_sign:{keywords:[\"math\",\"calculation\",\"subtract\",\"less\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➖\" src=\"2796.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_division_sign:{keywords:[\"divide\",\"math\",\"calculation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"➗\" src=\"2797.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_multiplication_x:{keywords:[\"math\",\"calculation\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"✖️\" src=\"2716.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},infinity:{keywords:[\"forever\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♾\" src=\"267e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},heavy_dollar_sign:{keywords:[\"money\",\"sales\",\"payment\",\"currency\",\"buck\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💲\" src=\"1f4b2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},currency_exchange:{keywords:[\"money\",\"sales\",\"dollar\",\"travel\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💱\" src=\"1f4b1.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},copyright:{keywords:[\"ip\",\"license\",\"circle\",\"law\",\"legal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"©️\" src=\"a9.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},registered:{keywords:[\"alphabet\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"®️\" src=\"ae.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},tm:{keywords:[\"trademark\",\"brand\",\"law\",\"legal\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"™️\" src=\"2122.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},end:{keywords:[\"words\",\"arrow\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔚\" src=\"1f51a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},back:{keywords:[\"arrow\",\"words\",\"return\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔙\" src=\"1f519.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},on:{keywords:[\"arrow\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔛\" src=\"1f51b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},top:{keywords:[\"words\",\"blue-square\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔝\" src=\"1f51d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},soon:{keywords:[\"arrow\",\"words\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔜\" src=\"1f51c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},ballot_box_with_check:{keywords:[\"ok\",\"agree\",\"confirm\",\"black-square\",\"vote\",\"election\",\"yes\",\"tick\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"☑️\" src=\"2611.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},radio_button:{keywords:[\"input\",\"old\",\"music\",\"circle\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔘\" src=\"1f518.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_circle:{keywords:[\"shape\",\"round\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚪\" src=\"26aa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_circle:{keywords:[\"shape\",\"button\",\"round\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⚫\" src=\"26ab.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},red_circle:{keywords:[\"shape\",\"error\",\"danger\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔴\" src=\"1f534.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},large_blue_circle:{keywords:[\"shape\",\"icon\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔵\" src=\"1f535.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔸\" src=\"1f538.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔹\" src=\"1f539.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},large_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔶\" src=\"1f536.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},large_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔷\" src=\"1f537.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle:{keywords:[\"shape\",\"direction\",\"up\",\"top\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔺\" src=\"1f53a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_small_square:{keywords:[\"shape\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"▪️\" src=\"25aa.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_small_square:{keywords:[\"shape\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"▫️\" src=\"25ab.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_large_square:{keywords:[\"shape\",\"icon\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬛\" src=\"2b1b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_large_square:{keywords:[\"shape\",\"icon\",\"stone\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"⬜\" src=\"2b1c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle_down:{keywords:[\"shape\",\"direction\",\"bottom\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔻\" src=\"1f53b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_medium_square:{keywords:[\"shape\",\"button\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◼️\" src=\"25fc.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_medium_square:{keywords:[\"shape\",\"stone\",\"icon\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◻️\" src=\"25fb.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_medium_small_square:{keywords:[\"icon\",\"shape\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◾\" src=\"25fe.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_medium_small_square:{keywords:[\"shape\",\"stone\",\"icon\",\"button\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"◽\" src=\"25fd.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_square_button:{keywords:[\"shape\",\"input\",\"frame\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔲\" src=\"1f532.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},white_square_button:{keywords:[\"shape\",\"input\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔳\" src=\"1f533.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},speaker:{keywords:[\"sound\",\"volume\",\"silence\",\"broadcast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔈\" src=\"1f508.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},sound:{keywords:[\"volume\",\"speaker\",\"broadcast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔉\" src=\"1f509.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},loud_sound:{keywords:[\"volume\",\"noise\",\"noisy\",\"speaker\",\"broadcast\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔊\" src=\"1f50a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mute:{keywords:[\"sound\",\"volume\",\"silence\",\"quiet\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔇\" src=\"1f507.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mega:{keywords:[\"sound\",\"speaker\",\"volume\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📣\" src=\"1f4e3.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},loudspeaker:{keywords:[\"volume\",\"sound\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"📢\" src=\"1f4e2.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},bell:{keywords:[\"sound\",\"notification\",\"christmas\",\"xmas\",\"chime\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔔\" src=\"1f514.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},no_bell:{keywords:[\"sound\",\"volume\",\"mute\",\"quiet\",\"silent\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🔕\" src=\"1f515.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},black_joker:{keywords:[\"poker\",\"cards\",\"game\",\"play\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🃏\" src=\"1f0cf.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},mahjong:{keywords:[\"game\",\"play\",\"chinese\",\"kanji\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🀄\" src=\"1f004.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},spades:{keywords:[\"poker\",\"cards\",\"suits\",\"magic\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♠️\" src=\"2660.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clubs:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♣️\" src=\"2663.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},hearts:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♥️\" src=\"2665.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},diamonds:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"♦️\" src=\"2666.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},flower_playing_cards:{keywords:[\"game\",\"sunset\",\"red\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🎴\" src=\"1f3b4.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},thought_balloon:{keywords:[\"bubble\",\"cloud\",\"speech\",\"thinking\",\"dream\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💭\" src=\"1f4ad.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},right_anger_bubble:{keywords:[\"caption\",\"speech\",\"thinking\",\"mad\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗯\" src=\"1f5ef.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},speech_balloon:{keywords:[\"bubble\",\"words\",\"message\",\"talk\",\"chatting\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"💬\" src=\"1f4ac.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},left_speech_bubble:{keywords:[\"words\",\"message\",\"talk\",\"chatting\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🗨\" src=\"1f5e8.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕐\" src=\"1f550.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock2:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕑\" src=\"1f551.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock3:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕒\" src=\"1f552.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock4:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕓\" src=\"1f553.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock5:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕔\" src=\"1f554.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock6:{keywords:[\"time\",\"late\",\"early\",\"schedule\",\"dawn\",\"dusk\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕕\" src=\"1f555.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock7:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕖\" src=\"1f556.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock8:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕗\" src=\"1f557.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock9:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕘\" src=\"1f558.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock10:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕙\" src=\"1f559.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock11:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕚\" src=\"1f55a.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock12:{keywords:[\"time\",\"noon\",\"midnight\",\"midday\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕛\" src=\"1f55b.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕜\" src=\"1f55c.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕝\" src=\"1f55d.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock330:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕞\" src=\"1f55e.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock430:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕟\" src=\"1f55f.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock530:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕠\" src=\"1f560.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock630:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕡\" src=\"1f561.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock730:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕢\" src=\"1f562.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock830:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕣\" src=\"1f563.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock930:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕤\" src=\"1f564.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1030:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕥\" src=\"1f565.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕦\" src=\"1f566.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},clock1230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🕧\" src=\"1f567.png\"/>',fitzpatrick_scale:false,category:\"symbols\"},afghanistan:{keywords:[\"af\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇫\" src=\"1f1e6-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},aland_islands:{keywords:[\"Åland\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇽\" src=\"1f1e6-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},albania:{keywords:[\"al\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇱\" src=\"1f1e6-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},algeria:{keywords:[\"dz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇿\" src=\"1f1e9-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},american_samoa:{keywords:[\"american\",\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇸\" src=\"1f1e6-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},andorra:{keywords:[\"ad\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇩\" src=\"1f1e6-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},angola:{keywords:[\"ao\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇴\" src=\"1f1e6-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},anguilla:{keywords:[\"ai\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇮\" src=\"1f1e6-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},antarctica:{keywords:[\"aq\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇶\" src=\"1f1e6-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},antigua_barbuda:{keywords:[\"antigua\",\"barbuda\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇬\" src=\"1f1e6-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},argentina:{keywords:[\"ar\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇷\" src=\"1f1e6-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},armenia:{keywords:[\"am\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇲\" src=\"1f1e6-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},aruba:{keywords:[\"aw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇼\" src=\"1f1e6-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},australia:{keywords:[\"au\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇺\" src=\"1f1e6-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},austria:{keywords:[\"at\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇹\" src=\"1f1e6-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},azerbaijan:{keywords:[\"az\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇿\" src=\"1f1e6-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bahamas:{keywords:[\"bs\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇸\" src=\"1f1e7-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bahrain:{keywords:[\"bh\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇭\" src=\"1f1e7-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bangladesh:{keywords:[\"bd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇩\" src=\"1f1e7-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},barbados:{keywords:[\"bb\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇧\" src=\"1f1e7-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},belarus:{keywords:[\"by\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇾\" src=\"1f1e7-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},belgium:{keywords:[\"be\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇪\" src=\"1f1e7-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},belize:{keywords:[\"bz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇿\" src=\"1f1e7-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},benin:{keywords:[\"bj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇯\" src=\"1f1e7-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bermuda:{keywords:[\"bm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇲\" src=\"1f1e7-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bhutan:{keywords:[\"bt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇹\" src=\"1f1e7-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bolivia:{keywords:[\"bo\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇴\" src=\"1f1e7-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},caribbean_netherlands:{keywords:[\"bonaire\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇶\" src=\"1f1e7-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bosnia_herzegovina:{keywords:[\"bosnia\",\"herzegovina\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇦\" src=\"1f1e7-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},botswana:{keywords:[\"bw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇼\" src=\"1f1e7-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},brazil:{keywords:[\"br\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇷\" src=\"1f1e7-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},british_indian_ocean_territory:{keywords:[\"british\",\"indian\",\"ocean\",\"territory\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇴\" src=\"1f1ee-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},british_virgin_islands:{keywords:[\"british\",\"virgin\",\"islands\",\"bvi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇬\" src=\"1f1fb-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},brunei:{keywords:[\"bn\",\"darussalam\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇳\" src=\"1f1e7-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},bulgaria:{keywords:[\"bg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇬\" src=\"1f1e7-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},burkina_faso:{keywords:[\"burkina\",\"faso\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇫\" src=\"1f1e7-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},burundi:{keywords:[\"bi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇮\" src=\"1f1e7-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cape_verde:{keywords:[\"cabo\",\"verde\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇻\" src=\"1f1e8-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cambodia:{keywords:[\"kh\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇭\" src=\"1f1f0-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cameroon:{keywords:[\"cm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇲\" src=\"1f1e8-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},canada:{keywords:[\"ca\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇦\" src=\"1f1e8-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},canary_islands:{keywords:[\"canary\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇨\" src=\"1f1ee-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cayman_islands:{keywords:[\"cayman\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇾\" src=\"1f1f0-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},central_african_republic:{keywords:[\"central\",\"african\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇫\" src=\"1f1e8-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},chad:{keywords:[\"td\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇩\" src=\"1f1f9-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},chile:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇱\" src=\"1f1e8-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cn:{keywords:[\"china\",\"chinese\",\"prc\",\"flag\",\"country\",\"nation\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇳\" src=\"1f1e8-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},christmas_island:{keywords:[\"christmas\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇽\" src=\"1f1e8-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cocos_islands:{keywords:[\"cocos\",\"keeling\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇨\" src=\"1f1e8-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},colombia:{keywords:[\"co\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇴\" src=\"1f1e8-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},comoros:{keywords:[\"km\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇲\" src=\"1f1f0-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},congo_brazzaville:{keywords:[\"congo\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇬\" src=\"1f1e8-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},congo_kinshasa:{keywords:[\"congo\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇩\" src=\"1f1e8-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cook_islands:{keywords:[\"cook\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇰\" src=\"1f1e8-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},costa_rica:{keywords:[\"costa\",\"rica\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇷\" src=\"1f1e8-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},croatia:{keywords:[\"hr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇷\" src=\"1f1ed-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cuba:{keywords:[\"cu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇺\" src=\"1f1e8-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},curacao:{keywords:[\"curaçao\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇼\" src=\"1f1e8-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cyprus:{keywords:[\"cy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇾\" src=\"1f1e8-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},czech_republic:{keywords:[\"cz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇿\" src=\"1f1e8-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},denmark:{keywords:[\"dk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇰\" src=\"1f1e9-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},djibouti:{keywords:[\"dj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇯\" src=\"1f1e9-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},dominica:{keywords:[\"dm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇲\" src=\"1f1e9-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},dominican_republic:{keywords:[\"dominican\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇴\" src=\"1f1e9-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ecuador:{keywords:[\"ec\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇨\" src=\"1f1ea-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},egypt:{keywords:[\"eg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇬\" src=\"1f1ea-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},el_salvador:{keywords:[\"el\",\"salvador\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇻\" src=\"1f1f8-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},equatorial_guinea:{keywords:[\"equatorial\",\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇶\" src=\"1f1ec-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},eritrea:{keywords:[\"er\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇷\" src=\"1f1ea-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},estonia:{keywords:[\"ee\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇪\" src=\"1f1ea-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ethiopia:{keywords:[\"et\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇹\" src=\"1f1ea-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},eu:{keywords:[\"european\",\"union\",\"flag\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇺\" src=\"1f1ea-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},falkland_islands:{keywords:[\"falkland\",\"islands\",\"malvinas\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇰\" src=\"1f1eb-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},faroe_islands:{keywords:[\"faroe\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇴\" src=\"1f1eb-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},fiji:{keywords:[\"fj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇯\" src=\"1f1eb-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},finland:{keywords:[\"fi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇮\" src=\"1f1eb-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},fr:{keywords:[\"banner\",\"flag\",\"nation\",\"france\",\"french\",\"country\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇷\" src=\"1f1eb-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},french_guiana:{keywords:[\"french\",\"guiana\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇫\" src=\"1f1ec-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},french_polynesia:{keywords:[\"french\",\"polynesia\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇫\" src=\"1f1f5-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},french_southern_territories:{keywords:[\"french\",\"southern\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇫\" src=\"1f1f9-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},gabon:{keywords:[\"ga\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇦\" src=\"1f1ec-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},gambia:{keywords:[\"gm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇲\" src=\"1f1ec-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},georgia:{keywords:[\"ge\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇪\" src=\"1f1ec-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},de:{keywords:[\"german\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇩🇪\" src=\"1f1e9-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ghana:{keywords:[\"gh\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇭\" src=\"1f1ec-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},gibraltar:{keywords:[\"gi\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇮\" src=\"1f1ec-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},greece:{keywords:[\"gr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇷\" src=\"1f1ec-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},greenland:{keywords:[\"gl\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇱\" src=\"1f1ec-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},grenada:{keywords:[\"gd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇩\" src=\"1f1ec-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guadeloupe:{keywords:[\"gp\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇵\" src=\"1f1ec-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guam:{keywords:[\"gu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇺\" src=\"1f1ec-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guatemala:{keywords:[\"gt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇹\" src=\"1f1ec-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guernsey:{keywords:[\"gg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇬\" src=\"1f1ec-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guinea:{keywords:[\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇳\" src=\"1f1ec-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guinea_bissau:{keywords:[\"gw\",\"bissau\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇼\" src=\"1f1ec-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},guyana:{keywords:[\"gy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇾\" src=\"1f1ec-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},haiti:{keywords:[\"ht\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇹\" src=\"1f1ed-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},honduras:{keywords:[\"hn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇳\" src=\"1f1ed-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},hong_kong:{keywords:[\"hong\",\"kong\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇰\" src=\"1f1ed-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},hungary:{keywords:[\"hu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇭🇺\" src=\"1f1ed-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},iceland:{keywords:[\"is\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇸\" src=\"1f1ee-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},india:{keywords:[\"in\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇳\" src=\"1f1ee-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},indonesia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇩\" src=\"1f1ee-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},iran:{keywords:[\"iran,\",\"islamic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇷\" src=\"1f1ee-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},iraq:{keywords:[\"iq\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇶\" src=\"1f1ee-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ireland:{keywords:[\"ie\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇪\" src=\"1f1ee-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},isle_of_man:{keywords:[\"isle\",\"man\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇲\" src=\"1f1ee-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},israel:{keywords:[\"il\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇱\" src=\"1f1ee-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},it:{keywords:[\"italy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇮🇹\" src=\"1f1ee-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},cote_divoire:{keywords:[\"ivory\",\"coast\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇮\" src=\"1f1e8-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jamaica:{keywords:[\"jm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇲\" src=\"1f1ef-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jp:{keywords:[\"japanese\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇵\" src=\"1f1ef-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jersey:{keywords:[\"je\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇪\" src=\"1f1ef-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},jordan:{keywords:[\"jo\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇯🇴\" src=\"1f1ef-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kazakhstan:{keywords:[\"kz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇿\" src=\"1f1f0-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kenya:{keywords:[\"ke\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇪\" src=\"1f1f0-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kiribati:{keywords:[\"ki\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇮\" src=\"1f1f0-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kosovo:{keywords:[\"xk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇽🇰\" src=\"1f1fd-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kuwait:{keywords:[\"kw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇼\" src=\"1f1f0-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kyrgyzstan:{keywords:[\"kg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇬\" src=\"1f1f0-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},laos:{keywords:[\"lao\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇦\" src=\"1f1f1-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},latvia:{keywords:[\"lv\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇻\" src=\"1f1f1-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},lebanon:{keywords:[\"lb\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇧\" src=\"1f1f1-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},lesotho:{keywords:[\"ls\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇸\" src=\"1f1f1-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},liberia:{keywords:[\"lr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇷\" src=\"1f1f1-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},libya:{keywords:[\"ly\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇾\" src=\"1f1f1-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},liechtenstein:{keywords:[\"li\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇮\" src=\"1f1f1-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},lithuania:{keywords:[\"lt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇹\" src=\"1f1f1-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},luxembourg:{keywords:[\"lu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇺\" src=\"1f1f1-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},macau:{keywords:[\"macao\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇴\" src=\"1f1f2-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},macedonia:{keywords:[\"macedonia,\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇰\" src=\"1f1f2-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},madagascar:{keywords:[\"mg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇬\" src=\"1f1f2-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},malawi:{keywords:[\"mw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇼\" src=\"1f1f2-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},malaysia:{keywords:[\"my\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇾\" src=\"1f1f2-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},maldives:{keywords:[\"mv\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇻\" src=\"1f1f2-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mali:{keywords:[\"ml\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇱\" src=\"1f1f2-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},malta:{keywords:[\"mt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇹\" src=\"1f1f2-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},marshall_islands:{keywords:[\"marshall\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇭\" src=\"1f1f2-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},martinique:{keywords:[\"mq\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇶\" src=\"1f1f2-1f1f6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mauritania:{keywords:[\"mr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇷\" src=\"1f1f2-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mauritius:{keywords:[\"mu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇺\" src=\"1f1f2-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mayotte:{keywords:[\"yt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇾🇹\" src=\"1f1fe-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mexico:{keywords:[\"mx\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇽\" src=\"1f1f2-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},micronesia:{keywords:[\"micronesia,\",\"federated\",\"states\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇫🇲\" src=\"1f1eb-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},moldova:{keywords:[\"moldova,\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇩\" src=\"1f1f2-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},monaco:{keywords:[\"mc\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇨\" src=\"1f1f2-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mongolia:{keywords:[\"mn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇳\" src=\"1f1f2-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},montenegro:{keywords:[\"me\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇪\" src=\"1f1f2-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},montserrat:{keywords:[\"ms\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇸\" src=\"1f1f2-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},morocco:{keywords:[\"ma\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇦\" src=\"1f1f2-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},mozambique:{keywords:[\"mz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇿\" src=\"1f1f2-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},myanmar:{keywords:[\"mm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇲\" src=\"1f1f2-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},namibia:{keywords:[\"na\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇦\" src=\"1f1f3-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nauru:{keywords:[\"nr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇷\" src=\"1f1f3-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nepal:{keywords:[\"np\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇵\" src=\"1f1f3-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},netherlands:{keywords:[\"nl\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇱\" src=\"1f1f3-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},new_caledonia:{keywords:[\"new\",\"caledonia\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇨\" src=\"1f1f3-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},new_zealand:{keywords:[\"new\",\"zealand\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇿\" src=\"1f1f3-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nicaragua:{keywords:[\"ni\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇮\" src=\"1f1f3-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},niger:{keywords:[\"ne\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇪\" src=\"1f1f3-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},nigeria:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇬\" src=\"1f1f3-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},niue:{keywords:[\"nu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇺\" src=\"1f1f3-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},norfolk_island:{keywords:[\"norfolk\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇫\" src=\"1f1f3-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},northern_mariana_islands:{keywords:[\"northern\",\"mariana\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇲🇵\" src=\"1f1f2-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},north_korea:{keywords:[\"north\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇵\" src=\"1f1f0-1f1f5.png\"/>',fitzpatrick_scale:false,category:\"flags\"},norway:{keywords:[\"no\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇳🇴\" src=\"1f1f3-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},oman:{keywords:[\"om_symbol\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇴🇲\" src=\"1f1f4-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},pakistan:{keywords:[\"pk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇰\" src=\"1f1f5-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},palau:{keywords:[\"pw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇼\" src=\"1f1f5-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},palestinian_territories:{keywords:[\"palestine\",\"palestinian\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇸\" src=\"1f1f5-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},panama:{keywords:[\"pa\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇦\" src=\"1f1f5-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},papua_new_guinea:{keywords:[\"papua\",\"new\",\"guinea\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇬\" src=\"1f1f5-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},paraguay:{keywords:[\"py\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇾\" src=\"1f1f5-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},peru:{keywords:[\"pe\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇪\" src=\"1f1f5-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},philippines:{keywords:[\"ph\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇭\" src=\"1f1f5-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},pitcairn_islands:{keywords:[\"pitcairn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇳\" src=\"1f1f5-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},poland:{keywords:[\"pl\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇱\" src=\"1f1f5-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},portugal:{keywords:[\"pt\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇹\" src=\"1f1f5-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},puerto_rico:{keywords:[\"puerto\",\"rico\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇷\" src=\"1f1f5-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},qatar:{keywords:[\"qa\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇶🇦\" src=\"1f1f6-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},reunion:{keywords:[\"réunion\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇪\" src=\"1f1f7-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},romania:{keywords:[\"ro\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇴\" src=\"1f1f7-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ru:{keywords:[\"russian\",\"federation\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇺\" src=\"1f1f7-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},rwanda:{keywords:[\"rw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇼\" src=\"1f1f7-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_barthelemy:{keywords:[\"saint\",\"barthélemy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇧🇱\" src=\"1f1e7-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_helena:{keywords:[\"saint\",\"helena\",\"ascension\",\"tristan\",\"cunha\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇭\" src=\"1f1f8-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_kitts_nevis:{keywords:[\"saint\",\"kitts\",\"nevis\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇳\" src=\"1f1f0-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_lucia:{keywords:[\"saint\",\"lucia\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇨\" src=\"1f1f1-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_pierre_miquelon:{keywords:[\"saint\",\"pierre\",\"miquelon\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇵🇲\" src=\"1f1f5-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},st_vincent_grenadines:{keywords:[\"saint\",\"vincent\",\"grenadines\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇨\" src=\"1f1fb-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},samoa:{keywords:[\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇼🇸\" src=\"1f1fc-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},san_marino:{keywords:[\"san\",\"marino\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇲\" src=\"1f1f8-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sao_tome_principe:{keywords:[\"sao\",\"tome\",\"principe\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇹\" src=\"1f1f8-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},saudi_arabia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇦\" src=\"1f1f8-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},senegal:{keywords:[\"sn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇳\" src=\"1f1f8-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},serbia:{keywords:[\"rs\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇷🇸\" src=\"1f1f7-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},seychelles:{keywords:[\"sc\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇨\" src=\"1f1f8-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sierra_leone:{keywords:[\"sierra\",\"leone\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇱\" src=\"1f1f8-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},singapore:{keywords:[\"sg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇬\" src=\"1f1f8-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sint_maarten:{keywords:[\"sint\",\"maarten\",\"dutch\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇽\" src=\"1f1f8-1f1fd.png\"/>',fitzpatrick_scale:false,category:\"flags\"},slovakia:{keywords:[\"sk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇰\" src=\"1f1f8-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},slovenia:{keywords:[\"si\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇮\" src=\"1f1f8-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},solomon_islands:{keywords:[\"solomon\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇧\" src=\"1f1f8-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},somalia:{keywords:[\"so\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇴\" src=\"1f1f8-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},south_africa:{keywords:[\"south\",\"africa\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇿🇦\" src=\"1f1ff-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},south_georgia_south_sandwich_islands:{keywords:[\"south\",\"georgia\",\"sandwich\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇸\" src=\"1f1ec-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},kr:{keywords:[\"south\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇰🇷\" src=\"1f1f0-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},south_sudan:{keywords:[\"south\",\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇸\" src=\"1f1f8-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},es:{keywords:[\"spain\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇸\" src=\"1f1ea-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sri_lanka:{keywords:[\"sri\",\"lanka\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇱🇰\" src=\"1f1f1-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sudan:{keywords:[\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇩\" src=\"1f1f8-1f1e9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},suriname:{keywords:[\"sr\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇷\" src=\"1f1f8-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},swaziland:{keywords:[\"sz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇿\" src=\"1f1f8-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},sweden:{keywords:[\"se\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇪\" src=\"1f1f8-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},switzerland:{keywords:[\"ch\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇨🇭\" src=\"1f1e8-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},syria:{keywords:[\"syrian\",\"arab\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇸🇾\" src=\"1f1f8-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},taiwan:{keywords:[\"tw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇼\" src=\"1f1f9-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tajikistan:{keywords:[\"tj\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇯\" src=\"1f1f9-1f1ef.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tanzania:{keywords:[\"tanzania,\",\"united\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇿\" src=\"1f1f9-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},thailand:{keywords:[\"th\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇭\" src=\"1f1f9-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},timor_leste:{keywords:[\"timor\",\"leste\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇱\" src=\"1f1f9-1f1f1.png\"/>',fitzpatrick_scale:false,category:\"flags\"},togo:{keywords:[\"tg\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇬\" src=\"1f1f9-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tokelau:{keywords:[\"tk\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇰\" src=\"1f1f9-1f1f0.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tonga:{keywords:[\"to\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇴\" src=\"1f1f9-1f1f4.png\"/>',fitzpatrick_scale:false,category:\"flags\"},trinidad_tobago:{keywords:[\"trinidad\",\"tobago\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇹\" src=\"1f1f9-1f1f9.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tunisia:{keywords:[\"tn\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇳\" src=\"1f1f9-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tr:{keywords:[\"turkey\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇷\" src=\"1f1f9-1f1f7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},turkmenistan:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇲\" src=\"1f1f9-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},turks_caicos_islands:{keywords:[\"turks\",\"caicos\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇨\" src=\"1f1f9-1f1e8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},tuvalu:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇹🇻\" src=\"1f1f9-1f1fb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uganda:{keywords:[\"ug\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇬\" src=\"1f1fa-1f1ec.png\"/>',fitzpatrick_scale:false,category:\"flags\"},ukraine:{keywords:[\"ua\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇦\" src=\"1f1fa-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},united_arab_emirates:{keywords:[\"united\",\"arab\",\"emirates\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇦🇪\" src=\"1f1e6-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uk:{keywords:[\"united\",\"kingdom\",\"great\",\"britain\",\"northern\",\"ireland\",\"flag\",\"nation\",\"country\",\"banner\",\"british\",\"UK\",\"english\",\"england\",\"union jack\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇬🇧\" src=\"1f1ec-1f1e7.png\"/>',fitzpatrick_scale:false,category:\"flags\"},england:{keywords:[\"flag\",\"english\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴󠁧󠁢󠁥󠁮󠁧󠁿\" src=\"1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png\"/>',fitzpatrick_scale:false,category:\"flags\"},scotland:{keywords:[\"flag\",\"scottish\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴󠁧󠁢󠁳󠁣󠁴󠁿\" src=\"1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png\"/>',fitzpatrick_scale:false,category:\"flags\"},wales:{keywords:[\"flag\",\"welsh\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴󠁧󠁢󠁷󠁬󠁳󠁿\" src=\"1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png\"/>',fitzpatrick_scale:false,category:\"flags\"},us:{keywords:[\"united\",\"states\",\"america\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇸\" src=\"1f1fa-1f1f8.png\"/>',fitzpatrick_scale:false,category:\"flags\"},us_virgin_islands:{keywords:[\"virgin\",\"islands\",\"us\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇮\" src=\"1f1fb-1f1ee.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uruguay:{keywords:[\"uy\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇾\" src=\"1f1fa-1f1fe.png\"/>',fitzpatrick_scale:false,category:\"flags\"},uzbekistan:{keywords:[\"uz\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇿\" src=\"1f1fa-1f1ff.png\"/>',fitzpatrick_scale:false,category:\"flags\"},vanuatu:{keywords:[\"vu\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇺\" src=\"1f1fb-1f1fa.png\"/>',fitzpatrick_scale:false,category:\"flags\"},vatican_city:{keywords:[\"vatican\",\"city\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇦\" src=\"1f1fb-1f1e6.png\"/>',fitzpatrick_scale:false,category:\"flags\"},venezuela:{keywords:[\"ve\",\"bolivarian\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇪\" src=\"1f1fb-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},vietnam:{keywords:[\"viet\",\"nam\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇻🇳\" src=\"1f1fb-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},wallis_futuna:{keywords:[\"wallis\",\"futuna\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇼🇫\" src=\"1f1fc-1f1eb.png\"/>',fitzpatrick_scale:false,category:\"flags\"},western_sahara:{keywords:[\"western\",\"sahara\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇪🇭\" src=\"1f1ea-1f1ed.png\"/>',fitzpatrick_scale:false,category:\"flags\"},yemen:{keywords:[\"ye\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇾🇪\" src=\"1f1fe-1f1ea.png\"/>',fitzpatrick_scale:false,category:\"flags\"},zambia:{keywords:[\"zm\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇿🇲\" src=\"1f1ff-1f1f2.png\"/>',fitzpatrick_scale:false,category:\"flags\"},zimbabwe:{keywords:[\"zw\",\"flag\",\"nation\",\"country\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇿🇼\" src=\"1f1ff-1f1fc.png\"/>',fitzpatrick_scale:false,category:\"flags\"},united_nations:{keywords:[\"un\",\"flag\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🇺🇳\" src=\"1f1fa-1f1f3.png\"/>',fitzpatrick_scale:false,category:\"flags\"},pirate_flag:{keywords:[\"skull\",\"crossbones\",\"flag\",\"banner\"],char:'<img data-emoticon=\"true\" style=\"width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em\" draggable=\"false\" alt=\"🏴‍☠️\" src=\"1f3f4-200d-2620-fe0f.png\"/>',fitzpatrick_scale:false,category:\"flags\"}});"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/emoticons/js/emojis.js",
    "content": "window.tinymce.Resource.add(\"tinymce.plugins.emoticons\",{grinning:{keywords:[\"face\",\"smile\",\"happy\",\"joy\",\":D\",\"grin\"],char:\"😀\",fitzpatrick_scale:false,category:\"people\"},grimacing:{keywords:[\"face\",\"grimace\",\"teeth\"],char:\"😬\",fitzpatrick_scale:false,category:\"people\"},grin:{keywords:[\"face\",\"happy\",\"smile\",\"joy\",\"kawaii\"],char:\"😁\",fitzpatrick_scale:false,category:\"people\"},joy:{keywords:[\"face\",\"cry\",\"tears\",\"weep\",\"happy\",\"happytears\",\"haha\"],char:\"😂\",fitzpatrick_scale:false,category:\"people\"},rofl:{keywords:[\"face\",\"rolling\",\"floor\",\"laughing\",\"lol\",\"haha\"],char:\"🤣\",fitzpatrick_scale:false,category:\"people\"},partying:{keywords:[\"face\",\"celebration\",\"woohoo\"],char:\"🥳\",fitzpatrick_scale:false,category:\"people\"},smiley:{keywords:[\"face\",\"happy\",\"joy\",\"haha\",\":D\",\":)\",\"smile\",\"funny\"],char:\"😃\",fitzpatrick_scale:false,category:\"people\"},smile:{keywords:[\"face\",\"happy\",\"joy\",\"funny\",\"haha\",\"laugh\",\"like\",\":D\",\":)\"],char:\"😄\",fitzpatrick_scale:false,category:\"people\"},sweat_smile:{keywords:[\"face\",\"hot\",\"happy\",\"laugh\",\"sweat\",\"smile\",\"relief\"],char:\"😅\",fitzpatrick_scale:false,category:\"people\"},laughing:{keywords:[\"happy\",\"joy\",\"lol\",\"satisfied\",\"haha\",\"face\",\"glad\",\"XD\",\"laugh\"],char:\"😆\",fitzpatrick_scale:false,category:\"people\"},innocent:{keywords:[\"face\",\"angel\",\"heaven\",\"halo\"],char:\"😇\",fitzpatrick_scale:false,category:\"people\"},wink:{keywords:[\"face\",\"happy\",\"mischievous\",\"secret\",\";)\",\"smile\",\"eye\"],char:\"😉\",fitzpatrick_scale:false,category:\"people\"},blush:{keywords:[\"face\",\"smile\",\"happy\",\"flushed\",\"crush\",\"embarrassed\",\"shy\",\"joy\"],char:\"😊\",fitzpatrick_scale:false,category:\"people\"},slightly_smiling_face:{keywords:[\"face\",\"smile\"],char:\"🙂\",fitzpatrick_scale:false,category:\"people\"},upside_down_face:{keywords:[\"face\",\"flipped\",\"silly\",\"smile\"],char:\"🙃\",fitzpatrick_scale:false,category:\"people\"},relaxed:{keywords:[\"face\",\"blush\",\"massage\",\"happiness\"],char:\"☺️\",fitzpatrick_scale:false,category:\"people\"},yum:{keywords:[\"happy\",\"joy\",\"tongue\",\"smile\",\"face\",\"silly\",\"yummy\",\"nom\",\"delicious\",\"savouring\"],char:\"😋\",fitzpatrick_scale:false,category:\"people\"},relieved:{keywords:[\"face\",\"relaxed\",\"phew\",\"massage\",\"happiness\"],char:\"😌\",fitzpatrick_scale:false,category:\"people\"},heart_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"heart\"],char:\"😍\",fitzpatrick_scale:false,category:\"people\"},smiling_face_with_three_hearts:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"crush\",\"hearts\",\"adore\"],char:\"🥰\",fitzpatrick_scale:false,category:\"people\"},kissing_heart:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😘\",fitzpatrick_scale:false,category:\"people\"},kissing:{keywords:[\"love\",\"like\",\"face\",\"3\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😗\",fitzpatrick_scale:false,category:\"people\"},kissing_smiling_eyes:{keywords:[\"face\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😙\",fitzpatrick_scale:false,category:\"people\"},kissing_closed_eyes:{keywords:[\"face\",\"love\",\"like\",\"affection\",\"valentines\",\"infatuation\",\"kiss\"],char:\"😚\",fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_winking_eye:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"wink\",\"tongue\"],char:\"😜\",fitzpatrick_scale:false,category:\"people\"},zany:{keywords:[\"face\",\"goofy\",\"crazy\"],char:\"🤪\",fitzpatrick_scale:false,category:\"people\"},raised_eyebrow:{keywords:[\"face\",\"distrust\",\"scepticism\",\"disapproval\",\"disbelief\",\"surprise\"],char:\"🤨\",fitzpatrick_scale:false,category:\"people\"},monocle:{keywords:[\"face\",\"stuffy\",\"wealthy\"],char:\"🧐\",fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue_closed_eyes:{keywords:[\"face\",\"prank\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:\"😝\",fitzpatrick_scale:false,category:\"people\"},stuck_out_tongue:{keywords:[\"face\",\"prank\",\"childish\",\"playful\",\"mischievous\",\"smile\",\"tongue\"],char:\"😛\",fitzpatrick_scale:false,category:\"people\"},money_mouth_face:{keywords:[\"face\",\"rich\",\"dollar\",\"money\"],char:\"🤑\",fitzpatrick_scale:false,category:\"people\"},nerd_face:{keywords:[\"face\",\"nerdy\",\"geek\",\"dork\"],char:\"🤓\",fitzpatrick_scale:false,category:\"people\"},sunglasses:{keywords:[\"face\",\"cool\",\"smile\",\"summer\",\"beach\",\"sunglass\"],char:\"😎\",fitzpatrick_scale:false,category:\"people\"},star_struck:{keywords:[\"face\",\"smile\",\"starry\",\"eyes\",\"grinning\"],char:\"🤩\",fitzpatrick_scale:false,category:\"people\"},clown_face:{keywords:[\"face\"],char:\"🤡\",fitzpatrick_scale:false,category:\"people\"},cowboy_hat_face:{keywords:[\"face\",\"cowgirl\",\"hat\"],char:\"🤠\",fitzpatrick_scale:false,category:\"people\"},hugs:{keywords:[\"face\",\"smile\",\"hug\"],char:\"🤗\",fitzpatrick_scale:false,category:\"people\"},smirk:{keywords:[\"face\",\"smile\",\"mean\",\"prank\",\"smug\",\"sarcasm\"],char:\"😏\",fitzpatrick_scale:false,category:\"people\"},no_mouth:{keywords:[\"face\",\"hellokitty\"],char:\"😶\",fitzpatrick_scale:false,category:\"people\"},neutral_face:{keywords:[\"indifference\",\"meh\",\":|\",\"neutral\"],char:\"😐\",fitzpatrick_scale:false,category:\"people\"},expressionless:{keywords:[\"face\",\"indifferent\",\"-_-\",\"meh\",\"deadpan\"],char:\"😑\",fitzpatrick_scale:false,category:\"people\"},unamused:{keywords:[\"indifference\",\"bored\",\"straight face\",\"serious\",\"sarcasm\",\"unimpressed\",\"skeptical\",\"dubious\",\"side_eye\"],char:\"😒\",fitzpatrick_scale:false,category:\"people\"},roll_eyes:{keywords:[\"face\",\"eyeroll\",\"frustrated\"],char:\"🙄\",fitzpatrick_scale:false,category:\"people\"},thinking:{keywords:[\"face\",\"hmmm\",\"think\",\"consider\"],char:\"🤔\",fitzpatrick_scale:false,category:\"people\"},lying_face:{keywords:[\"face\",\"lie\",\"pinocchio\"],char:\"🤥\",fitzpatrick_scale:false,category:\"people\"},hand_over_mouth:{keywords:[\"face\",\"whoops\",\"shock\",\"surprise\"],char:\"🤭\",fitzpatrick_scale:false,category:\"people\"},shushing:{keywords:[\"face\",\"quiet\",\"shhh\"],char:\"🤫\",fitzpatrick_scale:false,category:\"people\"},symbols_over_mouth:{keywords:[\"face\",\"swearing\",\"cursing\",\"cussing\",\"profanity\",\"expletive\"],char:\"🤬\",fitzpatrick_scale:false,category:\"people\"},exploding_head:{keywords:[\"face\",\"shocked\",\"mind\",\"blown\"],char:\"🤯\",fitzpatrick_scale:false,category:\"people\"},flushed:{keywords:[\"face\",\"blush\",\"shy\",\"flattered\"],char:\"😳\",fitzpatrick_scale:false,category:\"people\"},disappointed:{keywords:[\"face\",\"sad\",\"upset\",\"depressed\",\":(\"],char:\"😞\",fitzpatrick_scale:false,category:\"people\"},worried:{keywords:[\"face\",\"concern\",\"nervous\",\":(\"],char:\"😟\",fitzpatrick_scale:false,category:\"people\"},angry:{keywords:[\"mad\",\"face\",\"annoyed\",\"frustrated\"],char:\"😠\",fitzpatrick_scale:false,category:\"people\"},rage:{keywords:[\"angry\",\"mad\",\"hate\",\"despise\"],char:\"😡\",fitzpatrick_scale:false,category:\"people\"},pensive:{keywords:[\"face\",\"sad\",\"depressed\",\"upset\"],char:\"😔\",fitzpatrick_scale:false,category:\"people\"},confused:{keywords:[\"face\",\"indifference\",\"huh\",\"weird\",\"hmmm\",\":/\"],char:\"😕\",fitzpatrick_scale:false,category:\"people\"},slightly_frowning_face:{keywords:[\"face\",\"frowning\",\"disappointed\",\"sad\",\"upset\"],char:\"🙁\",fitzpatrick_scale:false,category:\"people\"},frowning_face:{keywords:[\"face\",\"sad\",\"upset\",\"frown\"],char:\"☹\",fitzpatrick_scale:false,category:\"people\"},persevere:{keywords:[\"face\",\"sick\",\"no\",\"upset\",\"oops\"],char:\"😣\",fitzpatrick_scale:false,category:\"people\"},confounded:{keywords:[\"face\",\"confused\",\"sick\",\"unwell\",\"oops\",\":S\"],char:\"😖\",fitzpatrick_scale:false,category:\"people\"},tired_face:{keywords:[\"sick\",\"whine\",\"upset\",\"frustrated\"],char:\"😫\",fitzpatrick_scale:false,category:\"people\"},weary:{keywords:[\"face\",\"tired\",\"sleepy\",\"sad\",\"frustrated\",\"upset\"],char:\"😩\",fitzpatrick_scale:false,category:\"people\"},pleading:{keywords:[\"face\",\"begging\",\"mercy\"],char:\"🥺\",fitzpatrick_scale:false,category:\"people\"},triumph:{keywords:[\"face\",\"gas\",\"phew\",\"proud\",\"pride\"],char:\"😤\",fitzpatrick_scale:false,category:\"people\"},open_mouth:{keywords:[\"face\",\"surprise\",\"impressed\",\"wow\",\"whoa\",\":O\"],char:\"😮\",fitzpatrick_scale:false,category:\"people\"},scream:{keywords:[\"face\",\"munch\",\"scared\",\"omg\"],char:\"😱\",fitzpatrick_scale:false,category:\"people\"},fearful:{keywords:[\"face\",\"scared\",\"terrified\",\"nervous\",\"oops\",\"huh\"],char:\"😨\",fitzpatrick_scale:false,category:\"people\"},cold_sweat:{keywords:[\"face\",\"nervous\",\"sweat\"],char:\"😰\",fitzpatrick_scale:false,category:\"people\"},hushed:{keywords:[\"face\",\"woo\",\"shh\"],char:\"😯\",fitzpatrick_scale:false,category:\"people\"},frowning:{keywords:[\"face\",\"aw\",\"what\"],char:\"😦\",fitzpatrick_scale:false,category:\"people\"},anguished:{keywords:[\"face\",\"stunned\",\"nervous\"],char:\"😧\",fitzpatrick_scale:false,category:\"people\"},cry:{keywords:[\"face\",\"tears\",\"sad\",\"depressed\",\"upset\",\":'(\"],char:\"😢\",fitzpatrick_scale:false,category:\"people\"},disappointed_relieved:{keywords:[\"face\",\"phew\",\"sweat\",\"nervous\"],char:\"😥\",fitzpatrick_scale:false,category:\"people\"},drooling_face:{keywords:[\"face\"],char:\"🤤\",fitzpatrick_scale:false,category:\"people\"},sleepy:{keywords:[\"face\",\"tired\",\"rest\",\"nap\"],char:\"😪\",fitzpatrick_scale:false,category:\"people\"},sweat:{keywords:[\"face\",\"hot\",\"sad\",\"tired\",\"exercise\"],char:\"😓\",fitzpatrick_scale:false,category:\"people\"},hot:{keywords:[\"face\",\"feverish\",\"heat\",\"red\",\"sweating\"],char:\"🥵\",fitzpatrick_scale:false,category:\"people\"},cold:{keywords:[\"face\",\"blue\",\"freezing\",\"frozen\",\"frostbite\",\"icicles\"],char:\"🥶\",fitzpatrick_scale:false,category:\"people\"},sob:{keywords:[\"face\",\"cry\",\"tears\",\"sad\",\"upset\",\"depressed\"],char:\"😭\",fitzpatrick_scale:false,category:\"people\"},dizzy_face:{keywords:[\"spent\",\"unconscious\",\"xox\",\"dizzy\"],char:\"😵\",fitzpatrick_scale:false,category:\"people\"},astonished:{keywords:[\"face\",\"xox\",\"surprised\",\"poisoned\"],char:\"😲\",fitzpatrick_scale:false,category:\"people\"},zipper_mouth_face:{keywords:[\"face\",\"sealed\",\"zipper\",\"secret\"],char:\"🤐\",fitzpatrick_scale:false,category:\"people\"},nauseated_face:{keywords:[\"face\",\"vomit\",\"gross\",\"green\",\"sick\",\"throw up\",\"ill\"],char:\"🤢\",fitzpatrick_scale:false,category:\"people\"},sneezing_face:{keywords:[\"face\",\"gesundheit\",\"sneeze\",\"sick\",\"allergy\"],char:\"🤧\",fitzpatrick_scale:false,category:\"people\"},vomiting:{keywords:[\"face\",\"sick\"],char:\"🤮\",fitzpatrick_scale:false,category:\"people\"},mask:{keywords:[\"face\",\"sick\",\"ill\",\"disease\"],char:\"😷\",fitzpatrick_scale:false,category:\"people\"},face_with_thermometer:{keywords:[\"sick\",\"temperature\",\"thermometer\",\"cold\",\"fever\"],char:\"🤒\",fitzpatrick_scale:false,category:\"people\"},face_with_head_bandage:{keywords:[\"injured\",\"clumsy\",\"bandage\",\"hurt\"],char:\"🤕\",fitzpatrick_scale:false,category:\"people\"},woozy:{keywords:[\"face\",\"dizzy\",\"intoxicated\",\"tipsy\",\"wavy\"],char:\"🥴\",fitzpatrick_scale:false,category:\"people\"},sleeping:{keywords:[\"face\",\"tired\",\"sleepy\",\"night\",\"zzz\"],char:\"😴\",fitzpatrick_scale:false,category:\"people\"},zzz:{keywords:[\"sleepy\",\"tired\",\"dream\"],char:\"💤\",fitzpatrick_scale:false,category:\"people\"},poop:{keywords:[\"hankey\",\"shitface\",\"fail\",\"turd\",\"shit\"],char:\"💩\",fitzpatrick_scale:false,category:\"people\"},smiling_imp:{keywords:[\"devil\",\"horns\"],char:\"😈\",fitzpatrick_scale:false,category:\"people\"},imp:{keywords:[\"devil\",\"angry\",\"horns\"],char:\"👿\",fitzpatrick_scale:false,category:\"people\"},japanese_ogre:{keywords:[\"monster\",\"red\",\"mask\",\"halloween\",\"scary\",\"creepy\",\"devil\",\"demon\",\"japanese\",\"ogre\"],char:\"👹\",fitzpatrick_scale:false,category:\"people\"},japanese_goblin:{keywords:[\"red\",\"evil\",\"mask\",\"monster\",\"scary\",\"creepy\",\"japanese\",\"goblin\"],char:\"👺\",fitzpatrick_scale:false,category:\"people\"},skull:{keywords:[\"dead\",\"skeleton\",\"creepy\",\"death\"],char:\"💀\",fitzpatrick_scale:false,category:\"people\"},ghost:{keywords:[\"halloween\",\"spooky\",\"scary\"],char:\"👻\",fitzpatrick_scale:false,category:\"people\"},alien:{keywords:[\"UFO\",\"paul\",\"weird\",\"outer_space\"],char:\"👽\",fitzpatrick_scale:false,category:\"people\"},robot:{keywords:[\"computer\",\"machine\",\"bot\"],char:\"🤖\",fitzpatrick_scale:false,category:\"people\"},smiley_cat:{keywords:[\"animal\",\"cats\",\"happy\",\"smile\"],char:\"😺\",fitzpatrick_scale:false,category:\"people\"},smile_cat:{keywords:[\"animal\",\"cats\",\"smile\"],char:\"😸\",fitzpatrick_scale:false,category:\"people\"},joy_cat:{keywords:[\"animal\",\"cats\",\"haha\",\"happy\",\"tears\"],char:\"😹\",fitzpatrick_scale:false,category:\"people\"},heart_eyes_cat:{keywords:[\"animal\",\"love\",\"like\",\"affection\",\"cats\",\"valentines\",\"heart\"],char:\"😻\",fitzpatrick_scale:false,category:\"people\"},smirk_cat:{keywords:[\"animal\",\"cats\",\"smirk\"],char:\"😼\",fitzpatrick_scale:false,category:\"people\"},kissing_cat:{keywords:[\"animal\",\"cats\",\"kiss\"],char:\"😽\",fitzpatrick_scale:false,category:\"people\"},scream_cat:{keywords:[\"animal\",\"cats\",\"munch\",\"scared\",\"scream\"],char:\"🙀\",fitzpatrick_scale:false,category:\"people\"},crying_cat_face:{keywords:[\"animal\",\"tears\",\"weep\",\"sad\",\"cats\",\"upset\",\"cry\"],char:\"😿\",fitzpatrick_scale:false,category:\"people\"},pouting_cat:{keywords:[\"animal\",\"cats\"],char:\"😾\",fitzpatrick_scale:false,category:\"people\"},palms_up:{keywords:[\"hands\",\"gesture\",\"cupped\",\"prayer\"],char:\"🤲\",fitzpatrick_scale:true,category:\"people\"},raised_hands:{keywords:[\"gesture\",\"hooray\",\"yea\",\"celebration\",\"hands\"],char:\"🙌\",fitzpatrick_scale:true,category:\"people\"},clap:{keywords:[\"hands\",\"praise\",\"applause\",\"congrats\",\"yay\"],char:\"👏\",fitzpatrick_scale:true,category:\"people\"},wave:{keywords:[\"hands\",\"gesture\",\"goodbye\",\"solong\",\"farewell\",\"hello\",\"hi\",\"palm\"],char:\"👋\",fitzpatrick_scale:true,category:\"people\"},call_me_hand:{keywords:[\"hands\",\"gesture\"],char:\"🤙\",fitzpatrick_scale:true,category:\"people\"},\"+1\":{keywords:[\"thumbsup\",\"yes\",\"awesome\",\"good\",\"agree\",\"accept\",\"cool\",\"hand\",\"like\"],char:\"👍\",fitzpatrick_scale:true,category:\"people\"},\"-1\":{keywords:[\"thumbsdown\",\"no\",\"dislike\",\"hand\"],char:\"👎\",fitzpatrick_scale:true,category:\"people\"},facepunch:{keywords:[\"angry\",\"violence\",\"fist\",\"hit\",\"attack\",\"hand\"],char:\"👊\",fitzpatrick_scale:true,category:\"people\"},fist:{keywords:[\"fingers\",\"hand\",\"grasp\"],char:\"✊\",fitzpatrick_scale:true,category:\"people\"},fist_left:{keywords:[\"hand\",\"fistbump\"],char:\"🤛\",fitzpatrick_scale:true,category:\"people\"},fist_right:{keywords:[\"hand\",\"fistbump\"],char:\"🤜\",fitzpatrick_scale:true,category:\"people\"},v:{keywords:[\"fingers\",\"ohyeah\",\"hand\",\"peace\",\"victory\",\"two\"],char:\"✌\",fitzpatrick_scale:true,category:\"people\"},ok_hand:{keywords:[\"fingers\",\"limbs\",\"perfect\",\"ok\",\"okay\"],char:\"👌\",fitzpatrick_scale:true,category:\"people\"},raised_hand:{keywords:[\"fingers\",\"stop\",\"highfive\",\"palm\",\"ban\"],char:\"✋\",fitzpatrick_scale:true,category:\"people\"},raised_back_of_hand:{keywords:[\"fingers\",\"raised\",\"backhand\"],char:\"🤚\",fitzpatrick_scale:true,category:\"people\"},open_hands:{keywords:[\"fingers\",\"butterfly\",\"hands\",\"open\"],char:\"👐\",fitzpatrick_scale:true,category:\"people\"},muscle:{keywords:[\"arm\",\"flex\",\"hand\",\"summer\",\"strong\",\"biceps\"],char:\"💪\",fitzpatrick_scale:true,category:\"people\"},pray:{keywords:[\"please\",\"hope\",\"wish\",\"namaste\",\"highfive\"],char:\"🙏\",fitzpatrick_scale:true,category:\"people\"},foot:{keywords:[\"kick\",\"stomp\"],char:\"🦶\",fitzpatrick_scale:true,category:\"people\"},leg:{keywords:[\"kick\",\"limb\"],char:\"🦵\",fitzpatrick_scale:true,category:\"people\"},handshake:{keywords:[\"agreement\",\"shake\"],char:\"🤝\",fitzpatrick_scale:false,category:\"people\"},point_up:{keywords:[\"hand\",\"fingers\",\"direction\",\"up\"],char:\"☝\",fitzpatrick_scale:true,category:\"people\"},point_up_2:{keywords:[\"fingers\",\"hand\",\"direction\",\"up\"],char:\"👆\",fitzpatrick_scale:true,category:\"people\"},point_down:{keywords:[\"fingers\",\"hand\",\"direction\",\"down\"],char:\"👇\",fitzpatrick_scale:true,category:\"people\"},point_left:{keywords:[\"direction\",\"fingers\",\"hand\",\"left\"],char:\"👈\",fitzpatrick_scale:true,category:\"people\"},point_right:{keywords:[\"fingers\",\"hand\",\"direction\",\"right\"],char:\"👉\",fitzpatrick_scale:true,category:\"people\"},fu:{keywords:[\"hand\",\"fingers\",\"rude\",\"middle\",\"flipping\"],char:\"🖕\",fitzpatrick_scale:true,category:\"people\"},raised_hand_with_fingers_splayed:{keywords:[\"hand\",\"fingers\",\"palm\"],char:\"🖐\",fitzpatrick_scale:true,category:\"people\"},love_you:{keywords:[\"hand\",\"fingers\",\"gesture\"],char:\"🤟\",fitzpatrick_scale:true,category:\"people\"},metal:{keywords:[\"hand\",\"fingers\",\"evil_eye\",\"sign_of_horns\",\"rock_on\"],char:\"🤘\",fitzpatrick_scale:true,category:\"people\"},crossed_fingers:{keywords:[\"good\",\"lucky\"],char:\"🤞\",fitzpatrick_scale:true,category:\"people\"},vulcan_salute:{keywords:[\"hand\",\"fingers\",\"spock\",\"star trek\"],char:\"🖖\",fitzpatrick_scale:true,category:\"people\"},writing_hand:{keywords:[\"lower_left_ballpoint_pen\",\"stationery\",\"write\",\"compose\"],char:\"✍\",fitzpatrick_scale:true,category:\"people\"},selfie:{keywords:[\"camera\",\"phone\"],char:\"🤳\",fitzpatrick_scale:true,category:\"people\"},nail_care:{keywords:[\"beauty\",\"manicure\",\"finger\",\"fashion\",\"nail\"],char:\"💅\",fitzpatrick_scale:true,category:\"people\"},lips:{keywords:[\"mouth\",\"kiss\"],char:\"👄\",fitzpatrick_scale:false,category:\"people\"},tooth:{keywords:[\"teeth\",\"dentist\"],char:\"🦷\",fitzpatrick_scale:false,category:\"people\"},tongue:{keywords:[\"mouth\",\"playful\"],char:\"👅\",fitzpatrick_scale:false,category:\"people\"},ear:{keywords:[\"face\",\"hear\",\"sound\",\"listen\"],char:\"👂\",fitzpatrick_scale:true,category:\"people\"},nose:{keywords:[\"smell\",\"sniff\"],char:\"👃\",fitzpatrick_scale:true,category:\"people\"},eye:{keywords:[\"face\",\"look\",\"see\",\"watch\",\"stare\"],char:\"👁\",fitzpatrick_scale:false,category:\"people\"},eyes:{keywords:[\"look\",\"watch\",\"stalk\",\"peek\",\"see\"],char:\"👀\",fitzpatrick_scale:false,category:\"people\"},brain:{keywords:[\"smart\",\"intelligent\"],char:\"🧠\",fitzpatrick_scale:false,category:\"people\"},bust_in_silhouette:{keywords:[\"user\",\"person\",\"human\"],char:\"👤\",fitzpatrick_scale:false,category:\"people\"},busts_in_silhouette:{keywords:[\"user\",\"person\",\"human\",\"group\",\"team\"],char:\"👥\",fitzpatrick_scale:false,category:\"people\"},speaking_head:{keywords:[\"user\",\"person\",\"human\",\"sing\",\"say\",\"talk\"],char:\"🗣\",fitzpatrick_scale:false,category:\"people\"},baby:{keywords:[\"child\",\"boy\",\"girl\",\"toddler\"],char:\"👶\",fitzpatrick_scale:true,category:\"people\"},child:{keywords:[\"gender-neutral\",\"young\"],char:\"🧒\",fitzpatrick_scale:true,category:\"people\"},boy:{keywords:[\"man\",\"male\",\"guy\",\"teenager\"],char:\"👦\",fitzpatrick_scale:true,category:\"people\"},girl:{keywords:[\"female\",\"woman\",\"teenager\"],char:\"👧\",fitzpatrick_scale:true,category:\"people\"},adult:{keywords:[\"gender-neutral\",\"person\"],char:\"🧑\",fitzpatrick_scale:true,category:\"people\"},man:{keywords:[\"mustache\",\"father\",\"dad\",\"guy\",\"classy\",\"sir\",\"moustache\"],char:\"👨\",fitzpatrick_scale:true,category:\"people\"},woman:{keywords:[\"female\",\"girls\",\"lady\"],char:\"👩\",fitzpatrick_scale:true,category:\"people\"},blonde_woman:{keywords:[\"woman\",\"female\",\"girl\",\"blonde\",\"person\"],char:\"👱‍♀️\",fitzpatrick_scale:true,category:\"people\"},blonde_man:{keywords:[\"man\",\"male\",\"boy\",\"blonde\",\"guy\",\"person\"],char:\"👱\",fitzpatrick_scale:true,category:\"people\"},bearded_person:{keywords:[\"person\",\"bewhiskered\"],char:\"🧔\",fitzpatrick_scale:true,category:\"people\"},older_adult:{keywords:[\"human\",\"elder\",\"senior\",\"gender-neutral\"],char:\"🧓\",fitzpatrick_scale:true,category:\"people\"},older_man:{keywords:[\"human\",\"male\",\"men\",\"old\",\"elder\",\"senior\"],char:\"👴\",fitzpatrick_scale:true,category:\"people\"},older_woman:{keywords:[\"human\",\"female\",\"women\",\"lady\",\"old\",\"elder\",\"senior\"],char:\"👵\",fitzpatrick_scale:true,category:\"people\"},man_with_gua_pi_mao:{keywords:[\"male\",\"boy\",\"chinese\"],char:\"👲\",fitzpatrick_scale:true,category:\"people\"},woman_with_headscarf:{keywords:[\"female\",\"hijab\",\"mantilla\",\"tichel\"],char:\"🧕\",fitzpatrick_scale:true,category:\"people\"},woman_with_turban:{keywords:[\"female\",\"indian\",\"hinduism\",\"arabs\",\"woman\"],char:\"👳‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_with_turban:{keywords:[\"male\",\"indian\",\"hinduism\",\"arabs\"],char:\"👳\",fitzpatrick_scale:true,category:\"people\"},policewoman:{keywords:[\"woman\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\",\"female\"],char:\"👮‍♀️\",fitzpatrick_scale:true,category:\"people\"},policeman:{keywords:[\"man\",\"police\",\"law\",\"legal\",\"enforcement\",\"arrest\",\"911\"],char:\"👮\",fitzpatrick_scale:true,category:\"people\"},construction_worker_woman:{keywords:[\"female\",\"human\",\"wip\",\"build\",\"construction\",\"worker\",\"labor\",\"woman\"],char:\"👷‍♀️\",fitzpatrick_scale:true,category:\"people\"},construction_worker_man:{keywords:[\"male\",\"human\",\"wip\",\"guy\",\"build\",\"construction\",\"worker\",\"labor\"],char:\"👷\",fitzpatrick_scale:true,category:\"people\"},guardswoman:{keywords:[\"uk\",\"gb\",\"british\",\"female\",\"royal\",\"woman\"],char:\"💂‍♀️\",fitzpatrick_scale:true,category:\"people\"},guardsman:{keywords:[\"uk\",\"gb\",\"british\",\"male\",\"guy\",\"royal\"],char:\"💂\",fitzpatrick_scale:true,category:\"people\"},female_detective:{keywords:[\"human\",\"spy\",\"detective\",\"female\",\"woman\"],char:\"🕵️‍♀️\",fitzpatrick_scale:true,category:\"people\"},male_detective:{keywords:[\"human\",\"spy\",\"detective\"],char:\"🕵\",fitzpatrick_scale:true,category:\"people\"},woman_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"woman\",\"human\"],char:\"👩‍⚕️\",fitzpatrick_scale:true,category:\"people\"},man_health_worker:{keywords:[\"doctor\",\"nurse\",\"therapist\",\"healthcare\",\"man\",\"human\"],char:\"👨‍⚕️\",fitzpatrick_scale:true,category:\"people\"},woman_farmer:{keywords:[\"rancher\",\"gardener\",\"woman\",\"human\"],char:\"👩‍🌾\",fitzpatrick_scale:true,category:\"people\"},man_farmer:{keywords:[\"rancher\",\"gardener\",\"man\",\"human\"],char:\"👨‍🌾\",fitzpatrick_scale:true,category:\"people\"},woman_cook:{keywords:[\"chef\",\"woman\",\"human\"],char:\"👩‍🍳\",fitzpatrick_scale:true,category:\"people\"},man_cook:{keywords:[\"chef\",\"man\",\"human\"],char:\"👨‍🍳\",fitzpatrick_scale:true,category:\"people\"},woman_student:{keywords:[\"graduate\",\"woman\",\"human\"],char:\"👩‍🎓\",fitzpatrick_scale:true,category:\"people\"},man_student:{keywords:[\"graduate\",\"man\",\"human\"],char:\"👨‍🎓\",fitzpatrick_scale:true,category:\"people\"},woman_singer:{keywords:[\"rockstar\",\"entertainer\",\"woman\",\"human\"],char:\"👩‍🎤\",fitzpatrick_scale:true,category:\"people\"},man_singer:{keywords:[\"rockstar\",\"entertainer\",\"man\",\"human\"],char:\"👨‍🎤\",fitzpatrick_scale:true,category:\"people\"},woman_teacher:{keywords:[\"instructor\",\"professor\",\"woman\",\"human\"],char:\"👩‍🏫\",fitzpatrick_scale:true,category:\"people\"},man_teacher:{keywords:[\"instructor\",\"professor\",\"man\",\"human\"],char:\"👨‍🏫\",fitzpatrick_scale:true,category:\"people\"},woman_factory_worker:{keywords:[\"assembly\",\"industrial\",\"woman\",\"human\"],char:\"👩‍🏭\",fitzpatrick_scale:true,category:\"people\"},man_factory_worker:{keywords:[\"assembly\",\"industrial\",\"man\",\"human\"],char:\"👨‍🏭\",fitzpatrick_scale:true,category:\"people\"},woman_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"woman\",\"human\",\"laptop\",\"computer\"],char:\"👩‍💻\",fitzpatrick_scale:true,category:\"people\"},man_technologist:{keywords:[\"coder\",\"developer\",\"engineer\",\"programmer\",\"software\",\"man\",\"human\",\"laptop\",\"computer\"],char:\"👨‍💻\",fitzpatrick_scale:true,category:\"people\"},woman_office_worker:{keywords:[\"business\",\"manager\",\"woman\",\"human\"],char:\"👩‍💼\",fitzpatrick_scale:true,category:\"people\"},man_office_worker:{keywords:[\"business\",\"manager\",\"man\",\"human\"],char:\"👨‍💼\",fitzpatrick_scale:true,category:\"people\"},woman_mechanic:{keywords:[\"plumber\",\"woman\",\"human\",\"wrench\"],char:\"👩‍🔧\",fitzpatrick_scale:true,category:\"people\"},man_mechanic:{keywords:[\"plumber\",\"man\",\"human\",\"wrench\"],char:\"👨‍🔧\",fitzpatrick_scale:true,category:\"people\"},woman_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"woman\",\"human\"],char:\"👩‍🔬\",fitzpatrick_scale:true,category:\"people\"},man_scientist:{keywords:[\"biologist\",\"chemist\",\"engineer\",\"physicist\",\"man\",\"human\"],char:\"👨‍🔬\",fitzpatrick_scale:true,category:\"people\"},woman_artist:{keywords:[\"painter\",\"woman\",\"human\"],char:\"👩‍🎨\",fitzpatrick_scale:true,category:\"people\"},man_artist:{keywords:[\"painter\",\"man\",\"human\"],char:\"👨‍🎨\",fitzpatrick_scale:true,category:\"people\"},woman_firefighter:{keywords:[\"fireman\",\"woman\",\"human\"],char:\"👩‍🚒\",fitzpatrick_scale:true,category:\"people\"},man_firefighter:{keywords:[\"fireman\",\"man\",\"human\"],char:\"👨‍🚒\",fitzpatrick_scale:true,category:\"people\"},woman_pilot:{keywords:[\"aviator\",\"plane\",\"woman\",\"human\"],char:\"👩‍✈️\",fitzpatrick_scale:true,category:\"people\"},man_pilot:{keywords:[\"aviator\",\"plane\",\"man\",\"human\"],char:\"👨‍✈️\",fitzpatrick_scale:true,category:\"people\"},woman_astronaut:{keywords:[\"space\",\"rocket\",\"woman\",\"human\"],char:\"👩‍🚀\",fitzpatrick_scale:true,category:\"people\"},man_astronaut:{keywords:[\"space\",\"rocket\",\"man\",\"human\"],char:\"👨‍🚀\",fitzpatrick_scale:true,category:\"people\"},woman_judge:{keywords:[\"justice\",\"court\",\"woman\",\"human\"],char:\"👩‍⚖️\",fitzpatrick_scale:true,category:\"people\"},man_judge:{keywords:[\"justice\",\"court\",\"man\",\"human\"],char:\"👨‍⚖️\",fitzpatrick_scale:true,category:\"people\"},woman_superhero:{keywords:[\"woman\",\"female\",\"good\",\"heroine\",\"superpowers\"],char:\"🦸‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_superhero:{keywords:[\"man\",\"male\",\"good\",\"hero\",\"superpowers\"],char:\"🦸‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_supervillain:{keywords:[\"woman\",\"female\",\"evil\",\"bad\",\"criminal\",\"heroine\",\"superpowers\"],char:\"🦹‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_supervillain:{keywords:[\"man\",\"male\",\"evil\",\"bad\",\"criminal\",\"hero\",\"superpowers\"],char:\"🦹‍♂️\",fitzpatrick_scale:true,category:\"people\"},mrs_claus:{keywords:[\"woman\",\"female\",\"xmas\",\"mother christmas\"],char:\"🤶\",fitzpatrick_scale:true,category:\"people\"},santa:{keywords:[\"festival\",\"man\",\"male\",\"xmas\",\"father christmas\"],char:\"🎅\",fitzpatrick_scale:true,category:\"people\"},sorceress:{keywords:[\"woman\",\"female\",\"mage\",\"witch\"],char:\"🧙‍♀️\",fitzpatrick_scale:true,category:\"people\"},wizard:{keywords:[\"man\",\"male\",\"mage\",\"sorcerer\"],char:\"🧙‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_elf:{keywords:[\"woman\",\"female\"],char:\"🧝‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_elf:{keywords:[\"man\",\"male\"],char:\"🧝‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_vampire:{keywords:[\"woman\",\"female\"],char:\"🧛‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_vampire:{keywords:[\"man\",\"male\",\"dracula\"],char:\"🧛‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_zombie:{keywords:[\"woman\",\"female\",\"undead\",\"walking dead\"],char:\"🧟‍♀️\",fitzpatrick_scale:false,category:\"people\"},man_zombie:{keywords:[\"man\",\"male\",\"dracula\",\"undead\",\"walking dead\"],char:\"🧟‍♂️\",fitzpatrick_scale:false,category:\"people\"},woman_genie:{keywords:[\"woman\",\"female\"],char:\"🧞‍♀️\",fitzpatrick_scale:false,category:\"people\"},man_genie:{keywords:[\"man\",\"male\"],char:\"🧞‍♂️\",fitzpatrick_scale:false,category:\"people\"},mermaid:{keywords:[\"woman\",\"female\",\"merwoman\",\"ariel\"],char:\"🧜‍♀️\",fitzpatrick_scale:true,category:\"people\"},merman:{keywords:[\"man\",\"male\",\"triton\"],char:\"🧜‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_fairy:{keywords:[\"woman\",\"female\"],char:\"🧚‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_fairy:{keywords:[\"man\",\"male\"],char:\"🧚‍♂️\",fitzpatrick_scale:true,category:\"people\"},angel:{keywords:[\"heaven\",\"wings\",\"halo\"],char:\"👼\",fitzpatrick_scale:true,category:\"people\"},pregnant_woman:{keywords:[\"baby\"],char:\"🤰\",fitzpatrick_scale:true,category:\"people\"},breastfeeding:{keywords:[\"nursing\",\"baby\"],char:\"🤱\",fitzpatrick_scale:true,category:\"people\"},princess:{keywords:[\"girl\",\"woman\",\"female\",\"blond\",\"crown\",\"royal\",\"queen\"],char:\"👸\",fitzpatrick_scale:true,category:\"people\"},prince:{keywords:[\"boy\",\"man\",\"male\",\"crown\",\"royal\",\"king\"],char:\"🤴\",fitzpatrick_scale:true,category:\"people\"},bride_with_veil:{keywords:[\"couple\",\"marriage\",\"wedding\",\"woman\",\"bride\"],char:\"👰\",fitzpatrick_scale:true,category:\"people\"},man_in_tuxedo:{keywords:[\"couple\",\"marriage\",\"wedding\",\"groom\"],char:\"🤵\",fitzpatrick_scale:true,category:\"people\"},running_woman:{keywords:[\"woman\",\"walking\",\"exercise\",\"race\",\"running\",\"female\"],char:\"🏃‍♀️\",fitzpatrick_scale:true,category:\"people\"},running_man:{keywords:[\"man\",\"walking\",\"exercise\",\"race\",\"running\"],char:\"🏃\",fitzpatrick_scale:true,category:\"people\"},walking_woman:{keywords:[\"human\",\"feet\",\"steps\",\"woman\",\"female\"],char:\"🚶‍♀️\",fitzpatrick_scale:true,category:\"people\"},walking_man:{keywords:[\"human\",\"feet\",\"steps\"],char:\"🚶\",fitzpatrick_scale:true,category:\"people\"},dancer:{keywords:[\"female\",\"girl\",\"woman\",\"fun\"],char:\"💃\",fitzpatrick_scale:true,category:\"people\"},man_dancing:{keywords:[\"male\",\"boy\",\"fun\",\"dancer\"],char:\"🕺\",fitzpatrick_scale:true,category:\"people\"},dancing_women:{keywords:[\"female\",\"bunny\",\"women\",\"girls\"],char:\"👯\",fitzpatrick_scale:false,category:\"people\"},dancing_men:{keywords:[\"male\",\"bunny\",\"men\",\"boys\"],char:\"👯‍♂️\",fitzpatrick_scale:false,category:\"people\"},couple:{keywords:[\"pair\",\"people\",\"human\",\"love\",\"date\",\"dating\",\"like\",\"affection\",\"valentines\",\"marriage\"],char:\"👫\",fitzpatrick_scale:false,category:\"people\"},two_men_holding_hands:{keywords:[\"pair\",\"couple\",\"love\",\"like\",\"bromance\",\"friendship\",\"people\",\"human\"],char:\"👬\",fitzpatrick_scale:false,category:\"people\"},two_women_holding_hands:{keywords:[\"pair\",\"friendship\",\"couple\",\"love\",\"like\",\"female\",\"people\",\"human\"],char:\"👭\",fitzpatrick_scale:false,category:\"people\"},bowing_woman:{keywords:[\"woman\",\"female\",\"girl\"],char:\"🙇‍♀️\",fitzpatrick_scale:true,category:\"people\"},bowing_man:{keywords:[\"man\",\"male\",\"boy\"],char:\"🙇\",fitzpatrick_scale:true,category:\"people\"},man_facepalming:{keywords:[\"man\",\"male\",\"boy\",\"disbelief\"],char:\"🤦‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_facepalming:{keywords:[\"woman\",\"female\",\"girl\",\"disbelief\"],char:\"🤦‍♀️\",fitzpatrick_scale:true,category:\"people\"},woman_shrugging:{keywords:[\"woman\",\"female\",\"girl\",\"confused\",\"indifferent\",\"doubt\"],char:\"🤷\",fitzpatrick_scale:true,category:\"people\"},man_shrugging:{keywords:[\"man\",\"male\",\"boy\",\"confused\",\"indifferent\",\"doubt\"],char:\"🤷‍♂️\",fitzpatrick_scale:true,category:\"people\"},tipping_hand_woman:{keywords:[\"female\",\"girl\",\"woman\",\"human\",\"information\"],char:\"💁\",fitzpatrick_scale:true,category:\"people\"},tipping_hand_man:{keywords:[\"male\",\"boy\",\"man\",\"human\",\"information\"],char:\"💁‍♂️\",fitzpatrick_scale:true,category:\"people\"},no_good_woman:{keywords:[\"female\",\"girl\",\"woman\",\"nope\"],char:\"🙅\",fitzpatrick_scale:true,category:\"people\"},no_good_man:{keywords:[\"male\",\"boy\",\"man\",\"nope\"],char:\"🙅‍♂️\",fitzpatrick_scale:true,category:\"people\"},ok_woman:{keywords:[\"women\",\"girl\",\"female\",\"pink\",\"human\",\"woman\"],char:\"🙆\",fitzpatrick_scale:true,category:\"people\"},ok_man:{keywords:[\"men\",\"boy\",\"male\",\"blue\",\"human\",\"man\"],char:\"🙆‍♂️\",fitzpatrick_scale:true,category:\"people\"},raising_hand_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:\"🙋\",fitzpatrick_scale:true,category:\"people\"},raising_hand_man:{keywords:[\"male\",\"boy\",\"man\"],char:\"🙋‍♂️\",fitzpatrick_scale:true,category:\"people\"},pouting_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:\"🙎\",fitzpatrick_scale:true,category:\"people\"},pouting_man:{keywords:[\"male\",\"boy\",\"man\"],char:\"🙎‍♂️\",fitzpatrick_scale:true,category:\"people\"},frowning_woman:{keywords:[\"female\",\"girl\",\"woman\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:\"🙍\",fitzpatrick_scale:true,category:\"people\"},frowning_man:{keywords:[\"male\",\"boy\",\"man\",\"sad\",\"depressed\",\"discouraged\",\"unhappy\"],char:\"🙍‍♂️\",fitzpatrick_scale:true,category:\"people\"},haircut_woman:{keywords:[\"female\",\"girl\",\"woman\"],char:\"💇\",fitzpatrick_scale:true,category:\"people\"},haircut_man:{keywords:[\"male\",\"boy\",\"man\"],char:\"💇‍♂️\",fitzpatrick_scale:true,category:\"people\"},massage_woman:{keywords:[\"female\",\"girl\",\"woman\",\"head\"],char:\"💆\",fitzpatrick_scale:true,category:\"people\"},massage_man:{keywords:[\"male\",\"boy\",\"man\",\"head\"],char:\"💆‍♂️\",fitzpatrick_scale:true,category:\"people\"},woman_in_steamy_room:{keywords:[\"female\",\"woman\",\"spa\",\"steamroom\",\"sauna\"],char:\"🧖‍♀️\",fitzpatrick_scale:true,category:\"people\"},man_in_steamy_room:{keywords:[\"male\",\"man\",\"spa\",\"steamroom\",\"sauna\"],char:\"🧖‍♂️\",fitzpatrick_scale:true,category:\"people\"},couple_with_heart_woman_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:\"💑\",fitzpatrick_scale:false,category:\"people\"},couple_with_heart_woman_woman:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:\"👩‍❤️‍👩\",fitzpatrick_scale:false,category:\"people\"},couple_with_heart_man_man:{keywords:[\"pair\",\"love\",\"like\",\"affection\",\"human\",\"dating\",\"valentines\",\"marriage\"],char:\"👨‍❤️‍👨\",fitzpatrick_scale:false,category:\"people\"},couplekiss_man_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:\"💏\",fitzpatrick_scale:false,category:\"people\"},couplekiss_woman_woman:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:\"👩‍❤️‍💋‍👩\",fitzpatrick_scale:false,category:\"people\"},couplekiss_man_man:{keywords:[\"pair\",\"valentines\",\"love\",\"like\",\"dating\",\"marriage\"],char:\"👨‍❤️‍💋‍👨\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy:{keywords:[\"home\",\"parents\",\"child\",\"mom\",\"dad\",\"father\",\"mother\",\"people\",\"human\"],char:\"👪\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"child\"],char:\"👨‍👩‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👩‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👩‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👩‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_woman_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👩‍👩‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_man_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_man_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_man_boy_boy:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_man_girl_girl:{keywords:[\"home\",\"parents\",\"people\",\"human\",\"children\"],char:\"👨‍👨‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👩‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👩‍👧\",fitzpatrick_scale:false,category:\"people\"},family_woman_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👩‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👩‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_woman_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👩‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👨‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"child\"],char:\"👨‍👧\",fitzpatrick_scale:false,category:\"people\"},family_man_girl_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👨‍👧‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_boy_boy:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👨‍👦‍👦\",fitzpatrick_scale:false,category:\"people\"},family_man_girl_girl:{keywords:[\"home\",\"parent\",\"people\",\"human\",\"children\"],char:\"👨‍👧‍👧\",fitzpatrick_scale:false,category:\"people\"},yarn:{keywords:[\"ball\",\"crochet\",\"knit\"],char:\"🧶\",fitzpatrick_scale:false,category:\"people\"},thread:{keywords:[\"needle\",\"sewing\",\"spool\",\"string\"],char:\"🧵\",fitzpatrick_scale:false,category:\"people\"},coat:{keywords:[\"jacket\"],char:\"🧥\",fitzpatrick_scale:false,category:\"people\"},labcoat:{keywords:[\"doctor\",\"experiment\",\"scientist\",\"chemist\"],char:\"🥼\",fitzpatrick_scale:false,category:\"people\"},womans_clothes:{keywords:[\"fashion\",\"shopping_bags\",\"female\"],char:\"👚\",fitzpatrick_scale:false,category:\"people\"},tshirt:{keywords:[\"fashion\",\"cloth\",\"casual\",\"shirt\",\"tee\"],char:\"👕\",fitzpatrick_scale:false,category:\"people\"},jeans:{keywords:[\"fashion\",\"shopping\"],char:\"👖\",fitzpatrick_scale:false,category:\"people\"},necktie:{keywords:[\"shirt\",\"suitup\",\"formal\",\"fashion\",\"cloth\",\"business\"],char:\"👔\",fitzpatrick_scale:false,category:\"people\"},dress:{keywords:[\"clothes\",\"fashion\",\"shopping\"],char:\"👗\",fitzpatrick_scale:false,category:\"people\"},bikini:{keywords:[\"swimming\",\"female\",\"woman\",\"girl\",\"fashion\",\"beach\",\"summer\"],char:\"👙\",fitzpatrick_scale:false,category:\"people\"},kimono:{keywords:[\"dress\",\"fashion\",\"women\",\"female\",\"japanese\"],char:\"👘\",fitzpatrick_scale:false,category:\"people\"},lipstick:{keywords:[\"female\",\"girl\",\"fashion\",\"woman\"],char:\"💄\",fitzpatrick_scale:false,category:\"people\"},kiss:{keywords:[\"face\",\"lips\",\"love\",\"like\",\"affection\",\"valentines\"],char:\"💋\",fitzpatrick_scale:false,category:\"people\"},footprints:{keywords:[\"feet\",\"tracking\",\"walking\",\"beach\"],char:\"👣\",fitzpatrick_scale:false,category:\"people\"},flat_shoe:{keywords:[\"ballet\",\"slip-on\",\"slipper\"],char:\"🥿\",fitzpatrick_scale:false,category:\"people\"},high_heel:{keywords:[\"fashion\",\"shoes\",\"female\",\"pumps\",\"stiletto\"],char:\"👠\",fitzpatrick_scale:false,category:\"people\"},sandal:{keywords:[\"shoes\",\"fashion\",\"flip flops\"],char:\"👡\",fitzpatrick_scale:false,category:\"people\"},boot:{keywords:[\"shoes\",\"fashion\"],char:\"👢\",fitzpatrick_scale:false,category:\"people\"},mans_shoe:{keywords:[\"fashion\",\"male\"],char:\"👞\",fitzpatrick_scale:false,category:\"people\"},athletic_shoe:{keywords:[\"shoes\",\"sports\",\"sneakers\"],char:\"👟\",fitzpatrick_scale:false,category:\"people\"},hiking_boot:{keywords:[\"backpacking\",\"camping\",\"hiking\"],char:\"🥾\",fitzpatrick_scale:false,category:\"people\"},socks:{keywords:[\"stockings\",\"clothes\"],char:\"🧦\",fitzpatrick_scale:false,category:\"people\"},gloves:{keywords:[\"hands\",\"winter\",\"clothes\"],char:\"🧤\",fitzpatrick_scale:false,category:\"people\"},scarf:{keywords:[\"neck\",\"winter\",\"clothes\"],char:\"🧣\",fitzpatrick_scale:false,category:\"people\"},womans_hat:{keywords:[\"fashion\",\"accessories\",\"female\",\"lady\",\"spring\"],char:\"👒\",fitzpatrick_scale:false,category:\"people\"},tophat:{keywords:[\"magic\",\"gentleman\",\"classy\",\"circus\"],char:\"🎩\",fitzpatrick_scale:false,category:\"people\"},billed_hat:{keywords:[\"cap\",\"baseball\"],char:\"🧢\",fitzpatrick_scale:false,category:\"people\"},rescue_worker_helmet:{keywords:[\"construction\",\"build\"],char:\"⛑\",fitzpatrick_scale:false,category:\"people\"},mortar_board:{keywords:[\"school\",\"college\",\"degree\",\"university\",\"graduation\",\"cap\",\"hat\",\"legal\",\"learn\",\"education\"],char:\"🎓\",fitzpatrick_scale:false,category:\"people\"},crown:{keywords:[\"king\",\"kod\",\"leader\",\"royalty\",\"lord\"],char:\"👑\",fitzpatrick_scale:false,category:\"people\"},school_satchel:{keywords:[\"student\",\"education\",\"bag\",\"backpack\"],char:\"🎒\",fitzpatrick_scale:false,category:\"people\"},luggage:{keywords:[\"packing\",\"travel\"],char:\"🧳\",fitzpatrick_scale:false,category:\"people\"},pouch:{keywords:[\"bag\",\"accessories\",\"shopping\"],char:\"👝\",fitzpatrick_scale:false,category:\"people\"},purse:{keywords:[\"fashion\",\"accessories\",\"money\",\"sales\",\"shopping\"],char:\"👛\",fitzpatrick_scale:false,category:\"people\"},handbag:{keywords:[\"fashion\",\"accessory\",\"accessories\",\"shopping\"],char:\"👜\",fitzpatrick_scale:false,category:\"people\"},briefcase:{keywords:[\"business\",\"documents\",\"work\",\"law\",\"legal\",\"job\",\"career\"],char:\"💼\",fitzpatrick_scale:false,category:\"people\"},eyeglasses:{keywords:[\"fashion\",\"accessories\",\"eyesight\",\"nerdy\",\"dork\",\"geek\"],char:\"👓\",fitzpatrick_scale:false,category:\"people\"},dark_sunglasses:{keywords:[\"face\",\"cool\",\"accessories\"],char:\"🕶\",fitzpatrick_scale:false,category:\"people\"},goggles:{keywords:[\"eyes\",\"protection\",\"safety\"],char:\"🥽\",fitzpatrick_scale:false,category:\"people\"},ring:{keywords:[\"wedding\",\"propose\",\"marriage\",\"valentines\",\"diamond\",\"fashion\",\"jewelry\",\"gem\",\"engagement\"],char:\"💍\",fitzpatrick_scale:false,category:\"people\"},closed_umbrella:{keywords:[\"weather\",\"rain\",\"drizzle\"],char:\"🌂\",fitzpatrick_scale:false,category:\"people\"},dog:{keywords:[\"animal\",\"friend\",\"nature\",\"woof\",\"puppy\",\"pet\",\"faithful\"],char:\"🐶\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cat:{keywords:[\"animal\",\"meow\",\"nature\",\"pet\",\"kitten\"],char:\"🐱\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse:{keywords:[\"animal\",\"nature\",\"cheese_wedge\",\"rodent\"],char:\"🐭\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hamster:{keywords:[\"animal\",\"nature\"],char:\"🐹\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit:{keywords:[\"animal\",\"nature\",\"pet\",\"spring\",\"magic\",\"bunny\"],char:\"🐰\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fox_face:{keywords:[\"animal\",\"nature\",\"face\"],char:\"🦊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bear:{keywords:[\"animal\",\"nature\",\"wild\"],char:\"🐻\",fitzpatrick_scale:false,category:\"animals_and_nature\"},panda_face:{keywords:[\"animal\",\"nature\",\"panda\"],char:\"🐼\",fitzpatrick_scale:false,category:\"animals_and_nature\"},koala:{keywords:[\"animal\",\"nature\"],char:\"🐨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger:{keywords:[\"animal\",\"cat\",\"danger\",\"wild\",\"nature\",\"roar\"],char:\"🐯\",fitzpatrick_scale:false,category:\"animals_and_nature\"},lion:{keywords:[\"animal\",\"nature\"],char:\"🦁\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cow:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:\"🐮\",fitzpatrick_scale:false,category:\"animals_and_nature\"},pig:{keywords:[\"animal\",\"oink\",\"nature\"],char:\"🐷\",fitzpatrick_scale:false,category:\"animals_and_nature\"},pig_nose:{keywords:[\"animal\",\"oink\"],char:\"🐽\",fitzpatrick_scale:false,category:\"animals_and_nature\"},frog:{keywords:[\"animal\",\"nature\",\"croak\",\"toad\"],char:\"🐸\",fitzpatrick_scale:false,category:\"animals_and_nature\"},squid:{keywords:[\"animal\",\"nature\",\"ocean\",\"sea\"],char:\"🦑\",fitzpatrick_scale:false,category:\"animals_and_nature\"},octopus:{keywords:[\"animal\",\"creature\",\"ocean\",\"sea\",\"nature\",\"beach\"],char:\"🐙\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shrimp:{keywords:[\"animal\",\"ocean\",\"nature\",\"seafood\"],char:\"🦐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey_face:{keywords:[\"animal\",\"nature\",\"circus\"],char:\"🐵\",fitzpatrick_scale:false,category:\"animals_and_nature\"},gorilla:{keywords:[\"animal\",\"nature\",\"circus\"],char:\"🦍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},see_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"haha\"],char:\"🙈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hear_no_evil:{keywords:[\"animal\",\"monkey\",\"nature\"],char:\"🙉\",fitzpatrick_scale:false,category:\"animals_and_nature\"},speak_no_evil:{keywords:[\"monkey\",\"animal\",\"nature\",\"omg\"],char:\"🙊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},monkey:{keywords:[\"animal\",\"nature\",\"banana\",\"circus\"],char:\"🐒\",fitzpatrick_scale:false,category:\"animals_and_nature\"},chicken:{keywords:[\"animal\",\"cluck\",\"nature\",\"bird\"],char:\"🐔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},penguin:{keywords:[\"animal\",\"nature\"],char:\"🐧\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bird:{keywords:[\"animal\",\"nature\",\"fly\",\"tweet\",\"spring\"],char:\"🐦\",fitzpatrick_scale:false,category:\"animals_and_nature\"},baby_chick:{keywords:[\"animal\",\"chicken\",\"bird\"],char:\"🐤\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hatching_chick:{keywords:[\"animal\",\"chicken\",\"egg\",\"born\",\"baby\",\"bird\"],char:\"🐣\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hatched_chick:{keywords:[\"animal\",\"chicken\",\"baby\",\"bird\"],char:\"🐥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},duck:{keywords:[\"animal\",\"nature\",\"bird\",\"mallard\"],char:\"🦆\",fitzpatrick_scale:false,category:\"animals_and_nature\"},eagle:{keywords:[\"animal\",\"nature\",\"bird\"],char:\"🦅\",fitzpatrick_scale:false,category:\"animals_and_nature\"},owl:{keywords:[\"animal\",\"nature\",\"bird\",\"hoot\"],char:\"🦉\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bat:{keywords:[\"animal\",\"nature\",\"blind\",\"vampire\"],char:\"🦇\",fitzpatrick_scale:false,category:\"animals_and_nature\"},wolf:{keywords:[\"animal\",\"nature\",\"wild\"],char:\"🐺\",fitzpatrick_scale:false,category:\"animals_and_nature\"},boar:{keywords:[\"animal\",\"nature\"],char:\"🐗\",fitzpatrick_scale:false,category:\"animals_and_nature\"},horse:{keywords:[\"animal\",\"brown\",\"nature\"],char:\"🐴\",fitzpatrick_scale:false,category:\"animals_and_nature\"},unicorn:{keywords:[\"animal\",\"nature\",\"mystical\"],char:\"🦄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},honeybee:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\",\"spring\",\"honey\"],char:\"🐝\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bug:{keywords:[\"animal\",\"insect\",\"nature\",\"worm\"],char:\"🐛\",fitzpatrick_scale:false,category:\"animals_and_nature\"},butterfly:{keywords:[\"animal\",\"insect\",\"nature\",\"caterpillar\"],char:\"🦋\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snail:{keywords:[\"slow\",\"animal\",\"shell\"],char:\"🐌\",fitzpatrick_scale:false,category:\"animals_and_nature\"},beetle:{keywords:[\"animal\",\"insect\",\"nature\",\"ladybug\"],char:\"🐞\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ant:{keywords:[\"animal\",\"insect\",\"nature\",\"bug\"],char:\"🐜\",fitzpatrick_scale:false,category:\"animals_and_nature\"},grasshopper:{keywords:[\"animal\",\"cricket\",\"chirp\"],char:\"🦗\",fitzpatrick_scale:false,category:\"animals_and_nature\"},spider:{keywords:[\"animal\",\"arachnid\"],char:\"🕷\",fitzpatrick_scale:false,category:\"animals_and_nature\"},scorpion:{keywords:[\"animal\",\"arachnid\"],char:\"🦂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},crab:{keywords:[\"animal\",\"crustacean\"],char:\"🦀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snake:{keywords:[\"animal\",\"evil\",\"nature\",\"hiss\",\"python\"],char:\"🐍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},lizard:{keywords:[\"animal\",\"nature\",\"reptile\"],char:\"🦎\",fitzpatrick_scale:false,category:\"animals_and_nature\"},\"t-rex\":{keywords:[\"animal\",\"nature\",\"dinosaur\",\"tyrannosaurus\",\"extinct\"],char:\"🦖\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sauropod:{keywords:[\"animal\",\"nature\",\"dinosaur\",\"brachiosaurus\",\"brontosaurus\",\"diplodocus\",\"extinct\"],char:\"🦕\",fitzpatrick_scale:false,category:\"animals_and_nature\"},turtle:{keywords:[\"animal\",\"slow\",\"nature\",\"tortoise\"],char:\"🐢\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tropical_fish:{keywords:[\"animal\",\"swim\",\"ocean\",\"beach\",\"nemo\"],char:\"🐠\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fish:{keywords:[\"animal\",\"food\",\"nature\"],char:\"🐟\",fitzpatrick_scale:false,category:\"animals_and_nature\"},blowfish:{keywords:[\"animal\",\"nature\",\"food\",\"sea\",\"ocean\"],char:\"🐡\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dolphin:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"flipper\",\"fins\",\"beach\"],char:\"🐬\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shark:{keywords:[\"animal\",\"nature\",\"fish\",\"sea\",\"ocean\",\"jaws\",\"fins\",\"beach\"],char:\"🦈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},whale:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:\"🐳\",fitzpatrick_scale:false,category:\"animals_and_nature\"},whale2:{keywords:[\"animal\",\"nature\",\"sea\",\"ocean\"],char:\"🐋\",fitzpatrick_scale:false,category:\"animals_and_nature\"},crocodile:{keywords:[\"animal\",\"nature\",\"reptile\",\"lizard\",\"alligator\"],char:\"🐊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},leopard:{keywords:[\"animal\",\"nature\"],char:\"🐆\",fitzpatrick_scale:false,category:\"animals_and_nature\"},zebra:{keywords:[\"animal\",\"nature\",\"stripes\",\"safari\"],char:\"🦓\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tiger2:{keywords:[\"animal\",\"nature\",\"roar\"],char:\"🐅\",fitzpatrick_scale:false,category:\"animals_and_nature\"},water_buffalo:{keywords:[\"animal\",\"nature\",\"ox\",\"cow\"],char:\"🐃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ox:{keywords:[\"animal\",\"cow\",\"beef\"],char:\"🐂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cow2:{keywords:[\"beef\",\"ox\",\"animal\",\"nature\",\"moo\",\"milk\"],char:\"🐄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},deer:{keywords:[\"animal\",\"nature\",\"horns\",\"venison\"],char:\"🦌\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dromedary_camel:{keywords:[\"animal\",\"hot\",\"desert\",\"hump\"],char:\"🐪\",fitzpatrick_scale:false,category:\"animals_and_nature\"},camel:{keywords:[\"animal\",\"nature\",\"hot\",\"desert\",\"hump\"],char:\"🐫\",fitzpatrick_scale:false,category:\"animals_and_nature\"},giraffe:{keywords:[\"animal\",\"nature\",\"spots\",\"safari\"],char:\"🦒\",fitzpatrick_scale:false,category:\"animals_and_nature\"},elephant:{keywords:[\"animal\",\"nature\",\"nose\",\"th\",\"circus\"],char:\"🐘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rhinoceros:{keywords:[\"animal\",\"nature\",\"horn\"],char:\"🦏\",fitzpatrick_scale:false,category:\"animals_and_nature\"},goat:{keywords:[\"animal\",\"nature\"],char:\"🐐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ram:{keywords:[\"animal\",\"sheep\",\"nature\"],char:\"🐏\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sheep:{keywords:[\"animal\",\"nature\",\"wool\",\"shipit\"],char:\"🐑\",fitzpatrick_scale:false,category:\"animals_and_nature\"},racehorse:{keywords:[\"animal\",\"gamble\",\"luck\"],char:\"🐎\",fitzpatrick_scale:false,category:\"animals_and_nature\"},pig2:{keywords:[\"animal\",\"nature\"],char:\"🐖\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rat:{keywords:[\"animal\",\"mouse\",\"rodent\"],char:\"🐀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mouse2:{keywords:[\"animal\",\"nature\",\"rodent\"],char:\"🐁\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rooster:{keywords:[\"animal\",\"nature\",\"chicken\"],char:\"🐓\",fitzpatrick_scale:false,category:\"animals_and_nature\"},turkey:{keywords:[\"animal\",\"bird\"],char:\"🦃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dove:{keywords:[\"animal\",\"bird\"],char:\"🕊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dog2:{keywords:[\"animal\",\"nature\",\"friend\",\"doge\",\"pet\",\"faithful\"],char:\"🐕\",fitzpatrick_scale:false,category:\"animals_and_nature\"},poodle:{keywords:[\"dog\",\"animal\",\"101\",\"nature\",\"pet\"],char:\"🐩\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cat2:{keywords:[\"animal\",\"meow\",\"pet\",\"cats\"],char:\"🐈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rabbit2:{keywords:[\"animal\",\"nature\",\"pet\",\"magic\",\"spring\"],char:\"🐇\",fitzpatrick_scale:false,category:\"animals_and_nature\"},chipmunk:{keywords:[\"animal\",\"nature\",\"rodent\",\"squirrel\"],char:\"🐿\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hedgehog:{keywords:[\"animal\",\"nature\",\"spiny\"],char:\"🦔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},raccoon:{keywords:[\"animal\",\"nature\"],char:\"🦝\",fitzpatrick_scale:false,category:\"animals_and_nature\"},llama:{keywords:[\"animal\",\"nature\",\"alpaca\"],char:\"🦙\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hippopotamus:{keywords:[\"animal\",\"nature\"],char:\"🦛\",fitzpatrick_scale:false,category:\"animals_and_nature\"},kangaroo:{keywords:[\"animal\",\"nature\",\"australia\",\"joey\",\"hop\",\"marsupial\"],char:\"🦘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},badger:{keywords:[\"animal\",\"nature\",\"honey\"],char:\"🦡\",fitzpatrick_scale:false,category:\"animals_and_nature\"},swan:{keywords:[\"animal\",\"nature\",\"bird\"],char:\"🦢\",fitzpatrick_scale:false,category:\"animals_and_nature\"},peacock:{keywords:[\"animal\",\"nature\",\"peahen\",\"bird\"],char:\"🦚\",fitzpatrick_scale:false,category:\"animals_and_nature\"},parrot:{keywords:[\"animal\",\"nature\",\"bird\",\"pirate\",\"talk\"],char:\"🦜\",fitzpatrick_scale:false,category:\"animals_and_nature\"},lobster:{keywords:[\"animal\",\"nature\",\"bisque\",\"claws\",\"seafood\"],char:\"🦞\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mosquito:{keywords:[\"animal\",\"nature\",\"insect\",\"malaria\"],char:\"🦟\",fitzpatrick_scale:false,category:\"animals_and_nature\"},paw_prints:{keywords:[\"animal\",\"tracking\",\"footprints\",\"dog\",\"cat\",\"pet\",\"feet\"],char:\"🐾\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:\"🐉\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dragon_face:{keywords:[\"animal\",\"myth\",\"nature\",\"chinese\",\"green\"],char:\"🐲\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cactus:{keywords:[\"vegetable\",\"plant\",\"nature\"],char:\"🌵\",fitzpatrick_scale:false,category:\"animals_and_nature\"},christmas_tree:{keywords:[\"festival\",\"vacation\",\"december\",\"xmas\",\"celebration\"],char:\"🎄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},evergreen_tree:{keywords:[\"plant\",\"nature\"],char:\"🌲\",fitzpatrick_scale:false,category:\"animals_and_nature\"},deciduous_tree:{keywords:[\"plant\",\"nature\"],char:\"🌳\",fitzpatrick_scale:false,category:\"animals_and_nature\"},palm_tree:{keywords:[\"plant\",\"vegetable\",\"nature\",\"summer\",\"beach\",\"mojito\",\"tropical\"],char:\"🌴\",fitzpatrick_scale:false,category:\"animals_and_nature\"},seedling:{keywords:[\"plant\",\"nature\",\"grass\",\"lawn\",\"spring\"],char:\"🌱\",fitzpatrick_scale:false,category:\"animals_and_nature\"},herb:{keywords:[\"vegetable\",\"plant\",\"medicine\",\"weed\",\"grass\",\"lawn\"],char:\"🌿\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shamrock:{keywords:[\"vegetable\",\"plant\",\"nature\",\"irish\",\"clover\"],char:\"☘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},four_leaf_clover:{keywords:[\"vegetable\",\"plant\",\"nature\",\"lucky\",\"irish\"],char:\"🍀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bamboo:{keywords:[\"plant\",\"nature\",\"vegetable\",\"panda\",\"pine_decoration\"],char:\"🎍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tanabata_tree:{keywords:[\"plant\",\"nature\",\"branch\",\"summer\"],char:\"🎋\",fitzpatrick_scale:false,category:\"animals_and_nature\"},leaves:{keywords:[\"nature\",\"plant\",\"tree\",\"vegetable\",\"grass\",\"lawn\",\"spring\"],char:\"🍃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fallen_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"leaves\"],char:\"🍂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},maple_leaf:{keywords:[\"nature\",\"plant\",\"vegetable\",\"ca\",\"fall\"],char:\"🍁\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ear_of_rice:{keywords:[\"nature\",\"plant\"],char:\"🌾\",fitzpatrick_scale:false,category:\"animals_and_nature\"},hibiscus:{keywords:[\"plant\",\"vegetable\",\"flowers\",\"beach\"],char:\"🌺\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sunflower:{keywords:[\"nature\",\"plant\",\"fall\"],char:\"🌻\",fitzpatrick_scale:false,category:\"animals_and_nature\"},rose:{keywords:[\"flowers\",\"valentines\",\"love\",\"spring\"],char:\"🌹\",fitzpatrick_scale:false,category:\"animals_and_nature\"},wilted_flower:{keywords:[\"plant\",\"nature\",\"flower\"],char:\"🥀\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tulip:{keywords:[\"flowers\",\"plant\",\"nature\",\"summer\",\"spring\"],char:\"🌷\",fitzpatrick_scale:false,category:\"animals_and_nature\"},blossom:{keywords:[\"nature\",\"flowers\",\"yellow\"],char:\"🌼\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cherry_blossom:{keywords:[\"nature\",\"plant\",\"spring\",\"flower\"],char:\"🌸\",fitzpatrick_scale:false,category:\"animals_and_nature\"},bouquet:{keywords:[\"flowers\",\"nature\",\"spring\"],char:\"💐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},mushroom:{keywords:[\"plant\",\"vegetable\"],char:\"🍄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},chestnut:{keywords:[\"food\",\"squirrel\"],char:\"🌰\",fitzpatrick_scale:false,category:\"animals_and_nature\"},jack_o_lantern:{keywords:[\"halloween\",\"light\",\"pumpkin\",\"creepy\",\"fall\"],char:\"🎃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},shell:{keywords:[\"nature\",\"sea\",\"beach\"],char:\"🐚\",fitzpatrick_scale:false,category:\"animals_and_nature\"},spider_web:{keywords:[\"animal\",\"insect\",\"arachnid\",\"silk\"],char:\"🕸\",fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_americas:{keywords:[\"globe\",\"world\",\"USA\",\"international\"],char:\"🌎\",fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_africa:{keywords:[\"globe\",\"world\",\"international\"],char:\"🌍\",fitzpatrick_scale:false,category:\"animals_and_nature\"},earth_asia:{keywords:[\"globe\",\"world\",\"east\",\"international\"],char:\"🌏\",fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon:{keywords:[\"nature\",\"yellow\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌕\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_gibbous_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\",\"waxing_gibbous_moon\"],char:\"🌖\",fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌗\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waning_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌘\",fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌑\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_crescent_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌒\",fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌓\",fitzpatrick_scale:false,category:\"animals_and_nature\"},waxing_gibbous_moon:{keywords:[\"nature\",\"night\",\"sky\",\"gray\",\"twilight\",\"planet\",\"space\",\"evening\",\"sleep\"],char:\"🌔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},new_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌚\",fitzpatrick_scale:false,category:\"animals_and_nature\"},full_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌝\",fitzpatrick_scale:false,category:\"animals_and_nature\"},first_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌛\",fitzpatrick_scale:false,category:\"animals_and_nature\"},last_quarter_moon_with_face:{keywords:[\"nature\",\"twilight\",\"planet\",\"space\",\"night\",\"evening\",\"sleep\"],char:\"🌜\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_with_face:{keywords:[\"nature\",\"morning\",\"sky\"],char:\"🌞\",fitzpatrick_scale:false,category:\"animals_and_nature\"},crescent_moon:{keywords:[\"night\",\"sleep\",\"sky\",\"evening\",\"magic\"],char:\"🌙\",fitzpatrick_scale:false,category:\"animals_and_nature\"},star:{keywords:[\"night\",\"yellow\"],char:\"⭐\",fitzpatrick_scale:false,category:\"animals_and_nature\"},star2:{keywords:[\"night\",\"sparkle\",\"awesome\",\"good\",\"magic\"],char:\"🌟\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dizzy:{keywords:[\"star\",\"sparkle\",\"shoot\",\"magic\"],char:\"💫\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sparkles:{keywords:[\"stars\",\"shine\",\"shiny\",\"cool\",\"awesome\",\"good\",\"magic\"],char:\"✨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},comet:{keywords:[\"space\"],char:\"☄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sunny:{keywords:[\"weather\",\"nature\",\"brightness\",\"summer\",\"beach\",\"spring\"],char:\"☀️\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_small_cloud:{keywords:[\"weather\"],char:\"🌤\",fitzpatrick_scale:false,category:\"animals_and_nature\"},partly_sunny:{keywords:[\"weather\",\"nature\",\"cloudy\",\"morning\",\"fall\",\"spring\"],char:\"⛅\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_large_cloud:{keywords:[\"weather\"],char:\"🌥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sun_behind_rain_cloud:{keywords:[\"weather\"],char:\"🌦\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud:{keywords:[\"weather\",\"sky\"],char:\"☁️\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_rain:{keywords:[\"weather\"],char:\"🌧\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning_and_rain:{keywords:[\"weather\",\"lightning\"],char:\"⛈\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_lightning:{keywords:[\"weather\",\"thunder\"],char:\"🌩\",fitzpatrick_scale:false,category:\"animals_and_nature\"},zap:{keywords:[\"thunder\",\"weather\",\"lightning bolt\",\"fast\"],char:\"⚡\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fire:{keywords:[\"hot\",\"cook\",\"flame\"],char:\"🔥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},boom:{keywords:[\"bomb\",\"explode\",\"explosion\",\"collision\",\"blown\"],char:\"💥\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snowflake:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\"],char:\"❄️\",fitzpatrick_scale:false,category:\"animals_and_nature\"},cloud_with_snow:{keywords:[\"weather\"],char:\"🌨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\",\"without_snow\"],char:\"⛄\",fitzpatrick_scale:false,category:\"animals_and_nature\"},snowman_with_snow:{keywords:[\"winter\",\"season\",\"cold\",\"weather\",\"christmas\",\"xmas\",\"frozen\"],char:\"☃\",fitzpatrick_scale:false,category:\"animals_and_nature\"},wind_face:{keywords:[\"gust\",\"air\"],char:\"🌬\",fitzpatrick_scale:false,category:\"animals_and_nature\"},dash:{keywords:[\"wind\",\"air\",\"fast\",\"shoo\",\"fart\",\"smoke\",\"puff\"],char:\"💨\",fitzpatrick_scale:false,category:\"animals_and_nature\"},tornado:{keywords:[\"weather\",\"cyclone\",\"twister\"],char:\"🌪\",fitzpatrick_scale:false,category:\"animals_and_nature\"},fog:{keywords:[\"weather\"],char:\"🌫\",fitzpatrick_scale:false,category:\"animals_and_nature\"},open_umbrella:{keywords:[\"weather\",\"spring\"],char:\"☂\",fitzpatrick_scale:false,category:\"animals_and_nature\"},umbrella:{keywords:[\"rainy\",\"weather\",\"spring\"],char:\"☔\",fitzpatrick_scale:false,category:\"animals_and_nature\"},droplet:{keywords:[\"water\",\"drip\",\"faucet\",\"spring\"],char:\"💧\",fitzpatrick_scale:false,category:\"animals_and_nature\"},sweat_drops:{keywords:[\"water\",\"drip\",\"oops\"],char:\"💦\",fitzpatrick_scale:false,category:\"animals_and_nature\"},ocean:{keywords:[\"sea\",\"water\",\"wave\",\"nature\",\"tsunami\",\"disaster\"],char:\"🌊\",fitzpatrick_scale:false,category:\"animals_and_nature\"},green_apple:{keywords:[\"fruit\",\"nature\"],char:\"🍏\",fitzpatrick_scale:false,category:\"food_and_drink\"},apple:{keywords:[\"fruit\",\"mac\",\"school\"],char:\"🍎\",fitzpatrick_scale:false,category:\"food_and_drink\"},pear:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍐\",fitzpatrick_scale:false,category:\"food_and_drink\"},tangerine:{keywords:[\"food\",\"fruit\",\"nature\",\"orange\"],char:\"🍊\",fitzpatrick_scale:false,category:\"food_and_drink\"},lemon:{keywords:[\"fruit\",\"nature\"],char:\"🍋\",fitzpatrick_scale:false,category:\"food_and_drink\"},banana:{keywords:[\"fruit\",\"food\",\"monkey\"],char:\"🍌\",fitzpatrick_scale:false,category:\"food_and_drink\"},watermelon:{keywords:[\"fruit\",\"food\",\"picnic\",\"summer\"],char:\"🍉\",fitzpatrick_scale:false,category:\"food_and_drink\"},grapes:{keywords:[\"fruit\",\"food\",\"wine\"],char:\"🍇\",fitzpatrick_scale:false,category:\"food_and_drink\"},strawberry:{keywords:[\"fruit\",\"food\",\"nature\"],char:\"🍓\",fitzpatrick_scale:false,category:\"food_and_drink\"},melon:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍈\",fitzpatrick_scale:false,category:\"food_and_drink\"},cherries:{keywords:[\"food\",\"fruit\"],char:\"🍒\",fitzpatrick_scale:false,category:\"food_and_drink\"},peach:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍑\",fitzpatrick_scale:false,category:\"food_and_drink\"},pineapple:{keywords:[\"fruit\",\"nature\",\"food\"],char:\"🍍\",fitzpatrick_scale:false,category:\"food_and_drink\"},coconut:{keywords:[\"fruit\",\"nature\",\"food\",\"palm\"],char:\"🥥\",fitzpatrick_scale:false,category:\"food_and_drink\"},kiwi_fruit:{keywords:[\"fruit\",\"food\"],char:\"🥝\",fitzpatrick_scale:false,category:\"food_and_drink\"},mango:{keywords:[\"fruit\",\"food\",\"tropical\"],char:\"🥭\",fitzpatrick_scale:false,category:\"food_and_drink\"},avocado:{keywords:[\"fruit\",\"food\"],char:\"🥑\",fitzpatrick_scale:false,category:\"food_and_drink\"},broccoli:{keywords:[\"fruit\",\"food\",\"vegetable\"],char:\"🥦\",fitzpatrick_scale:false,category:\"food_and_drink\"},tomato:{keywords:[\"fruit\",\"vegetable\",\"nature\",\"food\"],char:\"🍅\",fitzpatrick_scale:false,category:\"food_and_drink\"},eggplant:{keywords:[\"vegetable\",\"nature\",\"food\",\"aubergine\"],char:\"🍆\",fitzpatrick_scale:false,category:\"food_and_drink\"},cucumber:{keywords:[\"fruit\",\"food\",\"pickle\"],char:\"🥒\",fitzpatrick_scale:false,category:\"food_and_drink\"},carrot:{keywords:[\"vegetable\",\"food\",\"orange\"],char:\"🥕\",fitzpatrick_scale:false,category:\"food_and_drink\"},hot_pepper:{keywords:[\"food\",\"spicy\",\"chilli\",\"chili\"],char:\"🌶\",fitzpatrick_scale:false,category:\"food_and_drink\"},potato:{keywords:[\"food\",\"tuber\",\"vegatable\",\"starch\"],char:\"🥔\",fitzpatrick_scale:false,category:\"food_and_drink\"},corn:{keywords:[\"food\",\"vegetable\",\"plant\"],char:\"🌽\",fitzpatrick_scale:false,category:\"food_and_drink\"},leafy_greens:{keywords:[\"food\",\"vegetable\",\"plant\",\"bok choy\",\"cabbage\",\"kale\",\"lettuce\"],char:\"🥬\",fitzpatrick_scale:false,category:\"food_and_drink\"},sweet_potato:{keywords:[\"food\",\"nature\"],char:\"🍠\",fitzpatrick_scale:false,category:\"food_and_drink\"},peanuts:{keywords:[\"food\",\"nut\"],char:\"🥜\",fitzpatrick_scale:false,category:\"food_and_drink\"},honey_pot:{keywords:[\"bees\",\"sweet\",\"kitchen\"],char:\"🍯\",fitzpatrick_scale:false,category:\"food_and_drink\"},croissant:{keywords:[\"food\",\"bread\",\"french\"],char:\"🥐\",fitzpatrick_scale:false,category:\"food_and_drink\"},bread:{keywords:[\"food\",\"wheat\",\"breakfast\",\"toast\"],char:\"🍞\",fitzpatrick_scale:false,category:\"food_and_drink\"},baguette_bread:{keywords:[\"food\",\"bread\",\"french\"],char:\"🥖\",fitzpatrick_scale:false,category:\"food_and_drink\"},bagel:{keywords:[\"food\",\"bread\",\"bakery\",\"schmear\"],char:\"🥯\",fitzpatrick_scale:false,category:\"food_and_drink\"},pretzel:{keywords:[\"food\",\"bread\",\"twisted\"],char:\"🥨\",fitzpatrick_scale:false,category:\"food_and_drink\"},cheese:{keywords:[\"food\",\"chadder\"],char:\"🧀\",fitzpatrick_scale:false,category:\"food_and_drink\"},egg:{keywords:[\"food\",\"chicken\",\"breakfast\"],char:\"🥚\",fitzpatrick_scale:false,category:\"food_and_drink\"},bacon:{keywords:[\"food\",\"breakfast\",\"pork\",\"pig\",\"meat\"],char:\"🥓\",fitzpatrick_scale:false,category:\"food_and_drink\"},steak:{keywords:[\"food\",\"cow\",\"meat\",\"cut\",\"chop\",\"lambchop\",\"porkchop\"],char:\"🥩\",fitzpatrick_scale:false,category:\"food_and_drink\"},pancakes:{keywords:[\"food\",\"breakfast\",\"flapjacks\",\"hotcakes\"],char:\"🥞\",fitzpatrick_scale:false,category:\"food_and_drink\"},poultry_leg:{keywords:[\"food\",\"meat\",\"drumstick\",\"bird\",\"chicken\",\"turkey\"],char:\"🍗\",fitzpatrick_scale:false,category:\"food_and_drink\"},meat_on_bone:{keywords:[\"good\",\"food\",\"drumstick\"],char:\"🍖\",fitzpatrick_scale:false,category:\"food_and_drink\"},bone:{keywords:[\"skeleton\"],char:\"🦴\",fitzpatrick_scale:false,category:\"food_and_drink\"},fried_shrimp:{keywords:[\"food\",\"animal\",\"appetizer\",\"summer\"],char:\"🍤\",fitzpatrick_scale:false,category:\"food_and_drink\"},fried_egg:{keywords:[\"food\",\"breakfast\",\"kitchen\",\"egg\"],char:\"🍳\",fitzpatrick_scale:false,category:\"food_and_drink\"},hamburger:{keywords:[\"meat\",\"fast food\",\"beef\",\"cheeseburger\",\"mcdonalds\",\"burger king\"],char:\"🍔\",fitzpatrick_scale:false,category:\"food_and_drink\"},fries:{keywords:[\"chips\",\"snack\",\"fast food\"],char:\"🍟\",fitzpatrick_scale:false,category:\"food_and_drink\"},stuffed_flatbread:{keywords:[\"food\",\"flatbread\",\"stuffed\",\"gyro\"],char:\"🥙\",fitzpatrick_scale:false,category:\"food_and_drink\"},hotdog:{keywords:[\"food\",\"frankfurter\"],char:\"🌭\",fitzpatrick_scale:false,category:\"food_and_drink\"},pizza:{keywords:[\"food\",\"party\"],char:\"🍕\",fitzpatrick_scale:false,category:\"food_and_drink\"},sandwich:{keywords:[\"food\",\"lunch\",\"bread\"],char:\"🥪\",fitzpatrick_scale:false,category:\"food_and_drink\"},canned_food:{keywords:[\"food\",\"soup\"],char:\"🥫\",fitzpatrick_scale:false,category:\"food_and_drink\"},spaghetti:{keywords:[\"food\",\"italian\",\"noodle\"],char:\"🍝\",fitzpatrick_scale:false,category:\"food_and_drink\"},taco:{keywords:[\"food\",\"mexican\"],char:\"🌮\",fitzpatrick_scale:false,category:\"food_and_drink\"},burrito:{keywords:[\"food\",\"mexican\"],char:\"🌯\",fitzpatrick_scale:false,category:\"food_and_drink\"},green_salad:{keywords:[\"food\",\"healthy\",\"lettuce\"],char:\"🥗\",fitzpatrick_scale:false,category:\"food_and_drink\"},shallow_pan_of_food:{keywords:[\"food\",\"cooking\",\"casserole\",\"paella\"],char:\"🥘\",fitzpatrick_scale:false,category:\"food_and_drink\"},ramen:{keywords:[\"food\",\"japanese\",\"noodle\",\"chopsticks\"],char:\"🍜\",fitzpatrick_scale:false,category:\"food_and_drink\"},stew:{keywords:[\"food\",\"meat\",\"soup\"],char:\"🍲\",fitzpatrick_scale:false,category:\"food_and_drink\"},fish_cake:{keywords:[\"food\",\"japan\",\"sea\",\"beach\",\"narutomaki\",\"pink\",\"swirl\",\"kamaboko\",\"surimi\",\"ramen\"],char:\"🍥\",fitzpatrick_scale:false,category:\"food_and_drink\"},fortune_cookie:{keywords:[\"food\",\"prophecy\"],char:\"🥠\",fitzpatrick_scale:false,category:\"food_and_drink\"},sushi:{keywords:[\"food\",\"fish\",\"japanese\",\"rice\"],char:\"🍣\",fitzpatrick_scale:false,category:\"food_and_drink\"},bento:{keywords:[\"food\",\"japanese\",\"box\"],char:\"🍱\",fitzpatrick_scale:false,category:\"food_and_drink\"},curry:{keywords:[\"food\",\"spicy\",\"hot\",\"indian\"],char:\"🍛\",fitzpatrick_scale:false,category:\"food_and_drink\"},rice_ball:{keywords:[\"food\",\"japanese\"],char:\"🍙\",fitzpatrick_scale:false,category:\"food_and_drink\"},rice:{keywords:[\"food\",\"china\",\"asian\"],char:\"🍚\",fitzpatrick_scale:false,category:\"food_and_drink\"},rice_cracker:{keywords:[\"food\",\"japanese\"],char:\"🍘\",fitzpatrick_scale:false,category:\"food_and_drink\"},oden:{keywords:[\"food\",\"japanese\"],char:\"🍢\",fitzpatrick_scale:false,category:\"food_and_drink\"},dango:{keywords:[\"food\",\"dessert\",\"sweet\",\"japanese\",\"barbecue\",\"meat\"],char:\"🍡\",fitzpatrick_scale:false,category:\"food_and_drink\"},shaved_ice:{keywords:[\"hot\",\"dessert\",\"summer\"],char:\"🍧\",fitzpatrick_scale:false,category:\"food_and_drink\"},ice_cream:{keywords:[\"food\",\"hot\",\"dessert\"],char:\"🍨\",fitzpatrick_scale:false,category:\"food_and_drink\"},icecream:{keywords:[\"food\",\"hot\",\"dessert\",\"summer\"],char:\"🍦\",fitzpatrick_scale:false,category:\"food_and_drink\"},pie:{keywords:[\"food\",\"dessert\",\"pastry\"],char:\"🥧\",fitzpatrick_scale:false,category:\"food_and_drink\"},cake:{keywords:[\"food\",\"dessert\"],char:\"🍰\",fitzpatrick_scale:false,category:\"food_and_drink\"},cupcake:{keywords:[\"food\",\"dessert\",\"bakery\",\"sweet\"],char:\"🧁\",fitzpatrick_scale:false,category:\"food_and_drink\"},moon_cake:{keywords:[\"food\",\"autumn\"],char:\"🥮\",fitzpatrick_scale:false,category:\"food_and_drink\"},birthday:{keywords:[\"food\",\"dessert\",\"cake\"],char:\"🎂\",fitzpatrick_scale:false,category:\"food_and_drink\"},custard:{keywords:[\"dessert\",\"food\"],char:\"🍮\",fitzpatrick_scale:false,category:\"food_and_drink\"},candy:{keywords:[\"snack\",\"dessert\",\"sweet\",\"lolly\"],char:\"🍬\",fitzpatrick_scale:false,category:\"food_and_drink\"},lollipop:{keywords:[\"food\",\"snack\",\"candy\",\"sweet\"],char:\"🍭\",fitzpatrick_scale:false,category:\"food_and_drink\"},chocolate_bar:{keywords:[\"food\",\"snack\",\"dessert\",\"sweet\"],char:\"🍫\",fitzpatrick_scale:false,category:\"food_and_drink\"},popcorn:{keywords:[\"food\",\"movie theater\",\"films\",\"snack\"],char:\"🍿\",fitzpatrick_scale:false,category:\"food_and_drink\"},dumpling:{keywords:[\"food\",\"empanada\",\"pierogi\",\"potsticker\"],char:\"🥟\",fitzpatrick_scale:false,category:\"food_and_drink\"},doughnut:{keywords:[\"food\",\"dessert\",\"snack\",\"sweet\",\"donut\"],char:\"🍩\",fitzpatrick_scale:false,category:\"food_and_drink\"},cookie:{keywords:[\"food\",\"snack\",\"oreo\",\"chocolate\",\"sweet\",\"dessert\"],char:\"🍪\",fitzpatrick_scale:false,category:\"food_and_drink\"},milk_glass:{keywords:[\"beverage\",\"drink\",\"cow\"],char:\"🥛\",fitzpatrick_scale:false,category:\"food_and_drink\"},beer:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:\"🍺\",fitzpatrick_scale:false,category:\"food_and_drink\"},beers:{keywords:[\"relax\",\"beverage\",\"drink\",\"drunk\",\"party\",\"pub\",\"summer\",\"alcohol\",\"booze\"],char:\"🍻\",fitzpatrick_scale:false,category:\"food_and_drink\"},clinking_glasses:{keywords:[\"beverage\",\"drink\",\"party\",\"alcohol\",\"celebrate\",\"cheers\",\"wine\",\"champagne\",\"toast\"],char:\"🥂\",fitzpatrick_scale:false,category:\"food_and_drink\"},wine_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"booze\"],char:\"🍷\",fitzpatrick_scale:false,category:\"food_and_drink\"},tumbler_glass:{keywords:[\"drink\",\"beverage\",\"drunk\",\"alcohol\",\"liquor\",\"booze\",\"bourbon\",\"scotch\",\"whisky\",\"glass\",\"shot\"],char:\"🥃\",fitzpatrick_scale:false,category:\"food_and_drink\"},cocktail:{keywords:[\"drink\",\"drunk\",\"alcohol\",\"beverage\",\"booze\",\"mojito\"],char:\"🍸\",fitzpatrick_scale:false,category:\"food_and_drink\"},tropical_drink:{keywords:[\"beverage\",\"cocktail\",\"summer\",\"beach\",\"alcohol\",\"booze\",\"mojito\"],char:\"🍹\",fitzpatrick_scale:false,category:\"food_and_drink\"},champagne:{keywords:[\"drink\",\"wine\",\"bottle\",\"celebration\"],char:\"🍾\",fitzpatrick_scale:false,category:\"food_and_drink\"},sake:{keywords:[\"wine\",\"drink\",\"drunk\",\"beverage\",\"japanese\",\"alcohol\",\"booze\"],char:\"🍶\",fitzpatrick_scale:false,category:\"food_and_drink\"},tea:{keywords:[\"drink\",\"bowl\",\"breakfast\",\"green\",\"british\"],char:\"🍵\",fitzpatrick_scale:false,category:\"food_and_drink\"},cup_with_straw:{keywords:[\"drink\",\"soda\"],char:\"🥤\",fitzpatrick_scale:false,category:\"food_and_drink\"},coffee:{keywords:[\"beverage\",\"caffeine\",\"latte\",\"espresso\"],char:\"☕\",fitzpatrick_scale:false,category:\"food_and_drink\"},baby_bottle:{keywords:[\"food\",\"container\",\"milk\"],char:\"🍼\",fitzpatrick_scale:false,category:\"food_and_drink\"},salt:{keywords:[\"condiment\",\"shaker\"],char:\"🧂\",fitzpatrick_scale:false,category:\"food_and_drink\"},spoon:{keywords:[\"cutlery\",\"kitchen\",\"tableware\"],char:\"🥄\",fitzpatrick_scale:false,category:\"food_and_drink\"},fork_and_knife:{keywords:[\"cutlery\",\"kitchen\"],char:\"🍴\",fitzpatrick_scale:false,category:\"food_and_drink\"},plate_with_cutlery:{keywords:[\"food\",\"eat\",\"meal\",\"lunch\",\"dinner\",\"restaurant\"],char:\"🍽\",fitzpatrick_scale:false,category:\"food_and_drink\"},bowl_with_spoon:{keywords:[\"food\",\"breakfast\",\"cereal\",\"oatmeal\",\"porridge\"],char:\"🥣\",fitzpatrick_scale:false,category:\"food_and_drink\"},takeout_box:{keywords:[\"food\",\"leftovers\"],char:\"🥡\",fitzpatrick_scale:false,category:\"food_and_drink\"},chopsticks:{keywords:[\"food\"],char:\"🥢\",fitzpatrick_scale:false,category:\"food_and_drink\"},soccer:{keywords:[\"sports\",\"football\"],char:\"⚽\",fitzpatrick_scale:false,category:\"activity\"},basketball:{keywords:[\"sports\",\"balls\",\"NBA\"],char:\"🏀\",fitzpatrick_scale:false,category:\"activity\"},football:{keywords:[\"sports\",\"balls\",\"NFL\"],char:\"🏈\",fitzpatrick_scale:false,category:\"activity\"},baseball:{keywords:[\"sports\",\"balls\"],char:\"⚾\",fitzpatrick_scale:false,category:\"activity\"},softball:{keywords:[\"sports\",\"balls\"],char:\"🥎\",fitzpatrick_scale:false,category:\"activity\"},tennis:{keywords:[\"sports\",\"balls\",\"green\"],char:\"🎾\",fitzpatrick_scale:false,category:\"activity\"},volleyball:{keywords:[\"sports\",\"balls\"],char:\"🏐\",fitzpatrick_scale:false,category:\"activity\"},rugby_football:{keywords:[\"sports\",\"team\"],char:\"🏉\",fitzpatrick_scale:false,category:\"activity\"},flying_disc:{keywords:[\"sports\",\"frisbee\",\"ultimate\"],char:\"🥏\",fitzpatrick_scale:false,category:\"activity\"},\"8ball\":{keywords:[\"pool\",\"hobby\",\"game\",\"luck\",\"magic\"],char:\"🎱\",fitzpatrick_scale:false,category:\"activity\"},golf:{keywords:[\"sports\",\"business\",\"flag\",\"hole\",\"summer\"],char:\"⛳\",fitzpatrick_scale:false,category:\"activity\"},golfing_woman:{keywords:[\"sports\",\"business\",\"woman\",\"female\"],char:\"🏌️‍♀️\",fitzpatrick_scale:false,category:\"activity\"},golfing_man:{keywords:[\"sports\",\"business\"],char:\"🏌\",fitzpatrick_scale:true,category:\"activity\"},ping_pong:{keywords:[\"sports\",\"pingpong\"],char:\"🏓\",fitzpatrick_scale:false,category:\"activity\"},badminton:{keywords:[\"sports\"],char:\"🏸\",fitzpatrick_scale:false,category:\"activity\"},goal_net:{keywords:[\"sports\"],char:\"🥅\",fitzpatrick_scale:false,category:\"activity\"},ice_hockey:{keywords:[\"sports\"],char:\"🏒\",fitzpatrick_scale:false,category:\"activity\"},field_hockey:{keywords:[\"sports\"],char:\"🏑\",fitzpatrick_scale:false,category:\"activity\"},lacrosse:{keywords:[\"sports\",\"ball\",\"stick\"],char:\"🥍\",fitzpatrick_scale:false,category:\"activity\"},cricket:{keywords:[\"sports\"],char:\"🏏\",fitzpatrick_scale:false,category:\"activity\"},ski:{keywords:[\"sports\",\"winter\",\"cold\",\"snow\"],char:\"🎿\",fitzpatrick_scale:false,category:\"activity\"},skier:{keywords:[\"sports\",\"winter\",\"snow\"],char:\"⛷\",fitzpatrick_scale:false,category:\"activity\"},snowboarder:{keywords:[\"sports\",\"winter\"],char:\"🏂\",fitzpatrick_scale:true,category:\"activity\"},person_fencing:{keywords:[\"sports\",\"fencing\",\"sword\"],char:\"🤺\",fitzpatrick_scale:false,category:\"activity\"},women_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:\"🤼‍♀️\",fitzpatrick_scale:false,category:\"activity\"},men_wrestling:{keywords:[\"sports\",\"wrestlers\"],char:\"🤼‍♂️\",fitzpatrick_scale:false,category:\"activity\"},woman_cartwheeling:{keywords:[\"gymnastics\"],char:\"🤸‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_cartwheeling:{keywords:[\"gymnastics\"],char:\"🤸‍♂️\",fitzpatrick_scale:true,category:\"activity\"},woman_playing_handball:{keywords:[\"sports\"],char:\"🤾‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_playing_handball:{keywords:[\"sports\"],char:\"🤾‍♂️\",fitzpatrick_scale:true,category:\"activity\"},ice_skate:{keywords:[\"sports\"],char:\"⛸\",fitzpatrick_scale:false,category:\"activity\"},curling_stone:{keywords:[\"sports\"],char:\"🥌\",fitzpatrick_scale:false,category:\"activity\"},skateboard:{keywords:[\"board\"],char:\"🛹\",fitzpatrick_scale:false,category:\"activity\"},sled:{keywords:[\"sleigh\",\"luge\",\"toboggan\"],char:\"🛷\",fitzpatrick_scale:false,category:\"activity\"},bow_and_arrow:{keywords:[\"sports\"],char:\"🏹\",fitzpatrick_scale:false,category:\"activity\"},fishing_pole_and_fish:{keywords:[\"food\",\"hobby\",\"summer\"],char:\"🎣\",fitzpatrick_scale:false,category:\"activity\"},boxing_glove:{keywords:[\"sports\",\"fighting\"],char:\"🥊\",fitzpatrick_scale:false,category:\"activity\"},martial_arts_uniform:{keywords:[\"judo\",\"karate\",\"taekwondo\"],char:\"🥋\",fitzpatrick_scale:false,category:\"activity\"},rowing_woman:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\",\"woman\",\"female\"],char:\"🚣‍♀️\",fitzpatrick_scale:true,category:\"activity\"},rowing_man:{keywords:[\"sports\",\"hobby\",\"water\",\"ship\"],char:\"🚣\",fitzpatrick_scale:true,category:\"activity\"},climbing_woman:{keywords:[\"sports\",\"hobby\",\"woman\",\"female\",\"rock\"],char:\"🧗‍♀️\",fitzpatrick_scale:true,category:\"activity\"},climbing_man:{keywords:[\"sports\",\"hobby\",\"man\",\"male\",\"rock\"],char:\"🧗‍♂️\",fitzpatrick_scale:true,category:\"activity\"},swimming_woman:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\",\"woman\",\"female\"],char:\"🏊‍♀️\",fitzpatrick_scale:true,category:\"activity\"},swimming_man:{keywords:[\"sports\",\"exercise\",\"human\",\"athlete\",\"water\",\"summer\"],char:\"🏊\",fitzpatrick_scale:true,category:\"activity\"},woman_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:\"🤽‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_playing_water_polo:{keywords:[\"sports\",\"pool\"],char:\"🤽‍♂️\",fitzpatrick_scale:true,category:\"activity\"},woman_in_lotus_position:{keywords:[\"woman\",\"female\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:\"🧘‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_in_lotus_position:{keywords:[\"man\",\"male\",\"meditation\",\"yoga\",\"serenity\",\"zen\",\"mindfulness\"],char:\"🧘‍♂️\",fitzpatrick_scale:true,category:\"activity\"},surfing_woman:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\",\"woman\",\"female\"],char:\"🏄‍♀️\",fitzpatrick_scale:true,category:\"activity\"},surfing_man:{keywords:[\"sports\",\"ocean\",\"sea\",\"summer\",\"beach\"],char:\"🏄\",fitzpatrick_scale:true,category:\"activity\"},bath:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:\"🛀\",fitzpatrick_scale:true,category:\"activity\"},basketball_woman:{keywords:[\"sports\",\"human\",\"woman\",\"female\"],char:\"⛹️‍♀️\",fitzpatrick_scale:true,category:\"activity\"},basketball_man:{keywords:[\"sports\",\"human\"],char:\"⛹\",fitzpatrick_scale:true,category:\"activity\"},weight_lifting_woman:{keywords:[\"sports\",\"training\",\"exercise\",\"woman\",\"female\"],char:\"🏋️‍♀️\",fitzpatrick_scale:true,category:\"activity\"},weight_lifting_man:{keywords:[\"sports\",\"training\",\"exercise\"],char:\"🏋\",fitzpatrick_scale:true,category:\"activity\"},biking_woman:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\",\"woman\",\"female\"],char:\"🚴‍♀️\",fitzpatrick_scale:true,category:\"activity\"},biking_man:{keywords:[\"sports\",\"bike\",\"exercise\",\"hipster\"],char:\"🚴\",fitzpatrick_scale:true,category:\"activity\"},mountain_biking_woman:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\",\"woman\",\"female\"],char:\"🚵‍♀️\",fitzpatrick_scale:true,category:\"activity\"},mountain_biking_man:{keywords:[\"transportation\",\"sports\",\"human\",\"race\",\"bike\"],char:\"🚵\",fitzpatrick_scale:true,category:\"activity\"},horse_racing:{keywords:[\"animal\",\"betting\",\"competition\",\"gambling\",\"luck\"],char:\"🏇\",fitzpatrick_scale:true,category:\"activity\"},business_suit_levitating:{keywords:[\"suit\",\"business\",\"levitate\",\"hover\",\"jump\"],char:\"🕴\",fitzpatrick_scale:true,category:\"activity\"},trophy:{keywords:[\"win\",\"award\",\"contest\",\"place\",\"ftw\",\"ceremony\"],char:\"🏆\",fitzpatrick_scale:false,category:\"activity\"},running_shirt_with_sash:{keywords:[\"play\",\"pageant\"],char:\"🎽\",fitzpatrick_scale:false,category:\"activity\"},medal_sports:{keywords:[\"award\",\"winning\"],char:\"🏅\",fitzpatrick_scale:false,category:\"activity\"},medal_military:{keywords:[\"award\",\"winning\",\"army\"],char:\"🎖\",fitzpatrick_scale:false,category:\"activity\"},\"1st_place_medal\":{keywords:[\"award\",\"winning\",\"first\"],char:\"🥇\",fitzpatrick_scale:false,category:\"activity\"},\"2nd_place_medal\":{keywords:[\"award\",\"second\"],char:\"🥈\",fitzpatrick_scale:false,category:\"activity\"},\"3rd_place_medal\":{keywords:[\"award\",\"third\"],char:\"🥉\",fitzpatrick_scale:false,category:\"activity\"},reminder_ribbon:{keywords:[\"sports\",\"cause\",\"support\",\"awareness\"],char:\"🎗\",fitzpatrick_scale:false,category:\"activity\"},rosette:{keywords:[\"flower\",\"decoration\",\"military\"],char:\"🏵\",fitzpatrick_scale:false,category:\"activity\"},ticket:{keywords:[\"event\",\"concert\",\"pass\"],char:\"🎫\",fitzpatrick_scale:false,category:\"activity\"},tickets:{keywords:[\"sports\",\"concert\",\"entrance\"],char:\"🎟\",fitzpatrick_scale:false,category:\"activity\"},performing_arts:{keywords:[\"acting\",\"theater\",\"drama\"],char:\"🎭\",fitzpatrick_scale:false,category:\"activity\"},art:{keywords:[\"design\",\"paint\",\"draw\",\"colors\"],char:\"🎨\",fitzpatrick_scale:false,category:\"activity\"},circus_tent:{keywords:[\"festival\",\"carnival\",\"party\"],char:\"🎪\",fitzpatrick_scale:false,category:\"activity\"},woman_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:\"🤹‍♀️\",fitzpatrick_scale:true,category:\"activity\"},man_juggling:{keywords:[\"juggle\",\"balance\",\"skill\",\"multitask\"],char:\"🤹‍♂️\",fitzpatrick_scale:true,category:\"activity\"},microphone:{keywords:[\"sound\",\"music\",\"PA\",\"sing\",\"talkshow\"],char:\"🎤\",fitzpatrick_scale:false,category:\"activity\"},headphones:{keywords:[\"music\",\"score\",\"gadgets\"],char:\"🎧\",fitzpatrick_scale:false,category:\"activity\"},musical_score:{keywords:[\"treble\",\"clef\",\"compose\"],char:\"🎼\",fitzpatrick_scale:false,category:\"activity\"},musical_keyboard:{keywords:[\"piano\",\"instrument\",\"compose\"],char:\"🎹\",fitzpatrick_scale:false,category:\"activity\"},drum:{keywords:[\"music\",\"instrument\",\"drumsticks\",\"snare\"],char:\"🥁\",fitzpatrick_scale:false,category:\"activity\"},saxophone:{keywords:[\"music\",\"instrument\",\"jazz\",\"blues\"],char:\"🎷\",fitzpatrick_scale:false,category:\"activity\"},trumpet:{keywords:[\"music\",\"brass\"],char:\"🎺\",fitzpatrick_scale:false,category:\"activity\"},guitar:{keywords:[\"music\",\"instrument\"],char:\"🎸\",fitzpatrick_scale:false,category:\"activity\"},violin:{keywords:[\"music\",\"instrument\",\"orchestra\",\"symphony\"],char:\"🎻\",fitzpatrick_scale:false,category:\"activity\"},clapper:{keywords:[\"movie\",\"film\",\"record\"],char:\"🎬\",fitzpatrick_scale:false,category:\"activity\"},video_game:{keywords:[\"play\",\"console\",\"PS4\",\"controller\"],char:\"🎮\",fitzpatrick_scale:false,category:\"activity\"},space_invader:{keywords:[\"game\",\"arcade\",\"play\"],char:\"👾\",fitzpatrick_scale:false,category:\"activity\"},dart:{keywords:[\"game\",\"play\",\"bar\",\"target\",\"bullseye\"],char:\"🎯\",fitzpatrick_scale:false,category:\"activity\"},game_die:{keywords:[\"dice\",\"random\",\"tabletop\",\"play\",\"luck\"],char:\"🎲\",fitzpatrick_scale:false,category:\"activity\"},chess_pawn:{keywords:[\"expendable\"],char:\"♟\",fitzpatrick_scale:false,category:\"activity\"},slot_machine:{keywords:[\"bet\",\"gamble\",\"vegas\",\"fruit machine\",\"luck\",\"casino\"],char:\"🎰\",fitzpatrick_scale:false,category:\"activity\"},jigsaw:{keywords:[\"interlocking\",\"puzzle\",\"piece\"],char:\"🧩\",fitzpatrick_scale:false,category:\"activity\"},bowling:{keywords:[\"sports\",\"fun\",\"play\"],char:\"🎳\",fitzpatrick_scale:false,category:\"activity\"},red_car:{keywords:[\"red\",\"transportation\",\"vehicle\"],char:\"🚗\",fitzpatrick_scale:false,category:\"travel_and_places\"},taxi:{keywords:[\"uber\",\"vehicle\",\"cars\",\"transportation\"],char:\"🚕\",fitzpatrick_scale:false,category:\"travel_and_places\"},blue_car:{keywords:[\"transportation\",\"vehicle\"],char:\"🚙\",fitzpatrick_scale:false,category:\"travel_and_places\"},bus:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:\"🚌\",fitzpatrick_scale:false,category:\"travel_and_places\"},trolleybus:{keywords:[\"bart\",\"transportation\",\"vehicle\"],char:\"🚎\",fitzpatrick_scale:false,category:\"travel_and_places\"},racing_car:{keywords:[\"sports\",\"race\",\"fast\",\"formula\",\"f1\"],char:\"🏎\",fitzpatrick_scale:false,category:\"travel_and_places\"},police_car:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"law\",\"legal\",\"enforcement\"],char:\"🚓\",fitzpatrick_scale:false,category:\"travel_and_places\"},ambulance:{keywords:[\"health\",\"911\",\"hospital\"],char:\"🚑\",fitzpatrick_scale:false,category:\"travel_and_places\"},fire_engine:{keywords:[\"transportation\",\"cars\",\"vehicle\"],char:\"🚒\",fitzpatrick_scale:false,category:\"travel_and_places\"},minibus:{keywords:[\"vehicle\",\"car\",\"transportation\"],char:\"🚐\",fitzpatrick_scale:false,category:\"travel_and_places\"},truck:{keywords:[\"cars\",\"transportation\"],char:\"🚚\",fitzpatrick_scale:false,category:\"travel_and_places\"},articulated_lorry:{keywords:[\"vehicle\",\"cars\",\"transportation\",\"express\"],char:\"🚛\",fitzpatrick_scale:false,category:\"travel_and_places\"},tractor:{keywords:[\"vehicle\",\"car\",\"farming\",\"agriculture\"],char:\"🚜\",fitzpatrick_scale:false,category:\"travel_and_places\"},kick_scooter:{keywords:[\"vehicle\",\"kick\",\"razor\"],char:\"🛴\",fitzpatrick_scale:false,category:\"travel_and_places\"},motorcycle:{keywords:[\"race\",\"sports\",\"fast\"],char:\"🏍\",fitzpatrick_scale:false,category:\"travel_and_places\"},bike:{keywords:[\"sports\",\"bicycle\",\"exercise\",\"hipster\"],char:\"🚲\",fitzpatrick_scale:false,category:\"travel_and_places\"},motor_scooter:{keywords:[\"vehicle\",\"vespa\",\"sasha\"],char:\"🛵\",fitzpatrick_scale:false,category:\"travel_and_places\"},rotating_light:{keywords:[\"police\",\"ambulance\",\"911\",\"emergency\",\"alert\",\"error\",\"pinged\",\"law\",\"legal\"],char:\"🚨\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_police_car:{keywords:[\"vehicle\",\"law\",\"legal\",\"enforcement\",\"911\"],char:\"🚔\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_bus:{keywords:[\"vehicle\",\"transportation\"],char:\"🚍\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_automobile:{keywords:[\"car\",\"vehicle\",\"transportation\"],char:\"🚘\",fitzpatrick_scale:false,category:\"travel_and_places\"},oncoming_taxi:{keywords:[\"vehicle\",\"cars\",\"uber\"],char:\"🚖\",fitzpatrick_scale:false,category:\"travel_and_places\"},aerial_tramway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:\"🚡\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_cableway:{keywords:[\"transportation\",\"vehicle\",\"ski\"],char:\"🚠\",fitzpatrick_scale:false,category:\"travel_and_places\"},suspension_railway:{keywords:[\"vehicle\",\"transportation\"],char:\"🚟\",fitzpatrick_scale:false,category:\"travel_and_places\"},railway_car:{keywords:[\"transportation\",\"vehicle\"],char:\"🚃\",fitzpatrick_scale:false,category:\"travel_and_places\"},train:{keywords:[\"transportation\",\"vehicle\",\"carriage\",\"public\",\"travel\"],char:\"🚋\",fitzpatrick_scale:false,category:\"travel_and_places\"},monorail:{keywords:[\"transportation\",\"vehicle\"],char:\"🚝\",fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_side:{keywords:[\"transportation\",\"vehicle\"],char:\"🚄\",fitzpatrick_scale:false,category:\"travel_and_places\"},bullettrain_front:{keywords:[\"transportation\",\"vehicle\",\"speed\",\"fast\",\"public\",\"travel\"],char:\"🚅\",fitzpatrick_scale:false,category:\"travel_and_places\"},light_rail:{keywords:[\"transportation\",\"vehicle\"],char:\"🚈\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_railway:{keywords:[\"transportation\",\"vehicle\"],char:\"🚞\",fitzpatrick_scale:false,category:\"travel_and_places\"},steam_locomotive:{keywords:[\"transportation\",\"vehicle\",\"train\"],char:\"🚂\",fitzpatrick_scale:false,category:\"travel_and_places\"},train2:{keywords:[\"transportation\",\"vehicle\"],char:\"🚆\",fitzpatrick_scale:false,category:\"travel_and_places\"},metro:{keywords:[\"transportation\",\"blue-square\",\"mrt\",\"underground\",\"tube\"],char:\"🚇\",fitzpatrick_scale:false,category:\"travel_and_places\"},tram:{keywords:[\"transportation\",\"vehicle\"],char:\"🚊\",fitzpatrick_scale:false,category:\"travel_and_places\"},station:{keywords:[\"transportation\",\"vehicle\",\"public\"],char:\"🚉\",fitzpatrick_scale:false,category:\"travel_and_places\"},flying_saucer:{keywords:[\"transportation\",\"vehicle\",\"ufo\"],char:\"🛸\",fitzpatrick_scale:false,category:\"travel_and_places\"},helicopter:{keywords:[\"transportation\",\"vehicle\",\"fly\"],char:\"🚁\",fitzpatrick_scale:false,category:\"travel_and_places\"},small_airplane:{keywords:[\"flight\",\"transportation\",\"fly\",\"vehicle\"],char:\"🛩\",fitzpatrick_scale:false,category:\"travel_and_places\"},airplane:{keywords:[\"vehicle\",\"transportation\",\"flight\",\"fly\"],char:\"✈️\",fitzpatrick_scale:false,category:\"travel_and_places\"},flight_departure:{keywords:[\"airport\",\"flight\",\"landing\"],char:\"🛫\",fitzpatrick_scale:false,category:\"travel_and_places\"},flight_arrival:{keywords:[\"airport\",\"flight\",\"boarding\"],char:\"🛬\",fitzpatrick_scale:false,category:\"travel_and_places\"},sailboat:{keywords:[\"ship\",\"summer\",\"transportation\",\"water\",\"sailing\"],char:\"⛵\",fitzpatrick_scale:false,category:\"travel_and_places\"},motor_boat:{keywords:[\"ship\"],char:\"🛥\",fitzpatrick_scale:false,category:\"travel_and_places\"},speedboat:{keywords:[\"ship\",\"transportation\",\"vehicle\",\"summer\"],char:\"🚤\",fitzpatrick_scale:false,category:\"travel_and_places\"},ferry:{keywords:[\"boat\",\"ship\",\"yacht\"],char:\"⛴\",fitzpatrick_scale:false,category:\"travel_and_places\"},passenger_ship:{keywords:[\"yacht\",\"cruise\",\"ferry\"],char:\"🛳\",fitzpatrick_scale:false,category:\"travel_and_places\"},rocket:{keywords:[\"launch\",\"ship\",\"staffmode\",\"NASA\",\"outer space\",\"outer_space\",\"fly\"],char:\"🚀\",fitzpatrick_scale:false,category:\"travel_and_places\"},artificial_satellite:{keywords:[\"communication\",\"gps\",\"orbit\",\"spaceflight\",\"NASA\",\"ISS\"],char:\"🛰\",fitzpatrick_scale:false,category:\"travel_and_places\"},seat:{keywords:[\"sit\",\"airplane\",\"transport\",\"bus\",\"flight\",\"fly\"],char:\"💺\",fitzpatrick_scale:false,category:\"travel_and_places\"},canoe:{keywords:[\"boat\",\"paddle\",\"water\",\"ship\"],char:\"🛶\",fitzpatrick_scale:false,category:\"travel_and_places\"},anchor:{keywords:[\"ship\",\"ferry\",\"sea\",\"boat\"],char:\"⚓\",fitzpatrick_scale:false,category:\"travel_and_places\"},construction:{keywords:[\"wip\",\"progress\",\"caution\",\"warning\"],char:\"🚧\",fitzpatrick_scale:false,category:\"travel_and_places\"},fuelpump:{keywords:[\"gas station\",\"petroleum\"],char:\"⛽\",fitzpatrick_scale:false,category:\"travel_and_places\"},busstop:{keywords:[\"transportation\",\"wait\"],char:\"🚏\",fitzpatrick_scale:false,category:\"travel_and_places\"},vertical_traffic_light:{keywords:[\"transportation\",\"driving\"],char:\"🚦\",fitzpatrick_scale:false,category:\"travel_and_places\"},traffic_light:{keywords:[\"transportation\",\"signal\"],char:\"🚥\",fitzpatrick_scale:false,category:\"travel_and_places\"},checkered_flag:{keywords:[\"contest\",\"finishline\",\"race\",\"gokart\"],char:\"🏁\",fitzpatrick_scale:false,category:\"travel_and_places\"},ship:{keywords:[\"transportation\",\"titanic\",\"deploy\"],char:\"🚢\",fitzpatrick_scale:false,category:\"travel_and_places\"},ferris_wheel:{keywords:[\"photo\",\"carnival\",\"londoneye\"],char:\"🎡\",fitzpatrick_scale:false,category:\"travel_and_places\"},roller_coaster:{keywords:[\"carnival\",\"playground\",\"photo\",\"fun\"],char:\"🎢\",fitzpatrick_scale:false,category:\"travel_and_places\"},carousel_horse:{keywords:[\"photo\",\"carnival\"],char:\"🎠\",fitzpatrick_scale:false,category:\"travel_and_places\"},building_construction:{keywords:[\"wip\",\"working\",\"progress\"],char:\"🏗\",fitzpatrick_scale:false,category:\"travel_and_places\"},foggy:{keywords:[\"photo\",\"mountain\"],char:\"🌁\",fitzpatrick_scale:false,category:\"travel_and_places\"},tokyo_tower:{keywords:[\"photo\",\"japanese\"],char:\"🗼\",fitzpatrick_scale:false,category:\"travel_and_places\"},factory:{keywords:[\"building\",\"industry\",\"pollution\",\"smoke\"],char:\"🏭\",fitzpatrick_scale:false,category:\"travel_and_places\"},fountain:{keywords:[\"photo\",\"summer\",\"water\",\"fresh\"],char:\"⛲\",fitzpatrick_scale:false,category:\"travel_and_places\"},rice_scene:{keywords:[\"photo\",\"japan\",\"asia\",\"tsukimi\"],char:\"🎑\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain:{keywords:[\"photo\",\"nature\",\"environment\"],char:\"⛰\",fitzpatrick_scale:false,category:\"travel_and_places\"},mountain_snow:{keywords:[\"photo\",\"nature\",\"environment\",\"winter\",\"cold\"],char:\"🏔\",fitzpatrick_scale:false,category:\"travel_and_places\"},mount_fuji:{keywords:[\"photo\",\"mountain\",\"nature\",\"japanese\"],char:\"🗻\",fitzpatrick_scale:false,category:\"travel_and_places\"},volcano:{keywords:[\"photo\",\"nature\",\"disaster\"],char:\"🌋\",fitzpatrick_scale:false,category:\"travel_and_places\"},japan:{keywords:[\"nation\",\"country\",\"japanese\",\"asia\"],char:\"🗾\",fitzpatrick_scale:false,category:\"travel_and_places\"},camping:{keywords:[\"photo\",\"outdoors\",\"tent\"],char:\"🏕\",fitzpatrick_scale:false,category:\"travel_and_places\"},tent:{keywords:[\"photo\",\"camping\",\"outdoors\"],char:\"⛺\",fitzpatrick_scale:false,category:\"travel_and_places\"},national_park:{keywords:[\"photo\",\"environment\",\"nature\"],char:\"🏞\",fitzpatrick_scale:false,category:\"travel_and_places\"},motorway:{keywords:[\"road\",\"cupertino\",\"interstate\",\"highway\"],char:\"🛣\",fitzpatrick_scale:false,category:\"travel_and_places\"},railway_track:{keywords:[\"train\",\"transportation\"],char:\"🛤\",fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise:{keywords:[\"morning\",\"view\",\"vacation\",\"photo\"],char:\"🌅\",fitzpatrick_scale:false,category:\"travel_and_places\"},sunrise_over_mountains:{keywords:[\"view\",\"vacation\",\"photo\"],char:\"🌄\",fitzpatrick_scale:false,category:\"travel_and_places\"},desert:{keywords:[\"photo\",\"warm\",\"saharah\"],char:\"🏜\",fitzpatrick_scale:false,category:\"travel_and_places\"},beach_umbrella:{keywords:[\"weather\",\"summer\",\"sunny\",\"sand\",\"mojito\"],char:\"🏖\",fitzpatrick_scale:false,category:\"travel_and_places\"},desert_island:{keywords:[\"photo\",\"tropical\",\"mojito\"],char:\"🏝\",fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunrise:{keywords:[\"photo\",\"good morning\",\"dawn\"],char:\"🌇\",fitzpatrick_scale:false,category:\"travel_and_places\"},city_sunset:{keywords:[\"photo\",\"evening\",\"sky\",\"buildings\"],char:\"🌆\",fitzpatrick_scale:false,category:\"travel_and_places\"},cityscape:{keywords:[\"photo\",\"night life\",\"urban\"],char:\"🏙\",fitzpatrick_scale:false,category:\"travel_and_places\"},night_with_stars:{keywords:[\"evening\",\"city\",\"downtown\"],char:\"🌃\",fitzpatrick_scale:false,category:\"travel_and_places\"},bridge_at_night:{keywords:[\"photo\",\"sanfrancisco\"],char:\"🌉\",fitzpatrick_scale:false,category:\"travel_and_places\"},milky_way:{keywords:[\"photo\",\"space\",\"stars\"],char:\"🌌\",fitzpatrick_scale:false,category:\"travel_and_places\"},stars:{keywords:[\"night\",\"photo\"],char:\"🌠\",fitzpatrick_scale:false,category:\"travel_and_places\"},sparkler:{keywords:[\"stars\",\"night\",\"shine\"],char:\"🎇\",fitzpatrick_scale:false,category:\"travel_and_places\"},fireworks:{keywords:[\"photo\",\"festival\",\"carnival\",\"congratulations\"],char:\"🎆\",fitzpatrick_scale:false,category:\"travel_and_places\"},rainbow:{keywords:[\"nature\",\"happy\",\"unicorn_face\",\"photo\",\"sky\",\"spring\"],char:\"🌈\",fitzpatrick_scale:false,category:\"travel_and_places\"},houses:{keywords:[\"buildings\",\"photo\"],char:\"🏘\",fitzpatrick_scale:false,category:\"travel_and_places\"},european_castle:{keywords:[\"building\",\"royalty\",\"history\"],char:\"🏰\",fitzpatrick_scale:false,category:\"travel_and_places\"},japanese_castle:{keywords:[\"photo\",\"building\"],char:\"🏯\",fitzpatrick_scale:false,category:\"travel_and_places\"},stadium:{keywords:[\"photo\",\"place\",\"sports\",\"concert\",\"venue\"],char:\"🏟\",fitzpatrick_scale:false,category:\"travel_and_places\"},statue_of_liberty:{keywords:[\"american\",\"newyork\"],char:\"🗽\",fitzpatrick_scale:false,category:\"travel_and_places\"},house:{keywords:[\"building\",\"home\"],char:\"🏠\",fitzpatrick_scale:false,category:\"travel_and_places\"},house_with_garden:{keywords:[\"home\",\"plant\",\"nature\"],char:\"🏡\",fitzpatrick_scale:false,category:\"travel_and_places\"},derelict_house:{keywords:[\"abandon\",\"evict\",\"broken\",\"building\"],char:\"🏚\",fitzpatrick_scale:false,category:\"travel_and_places\"},office:{keywords:[\"building\",\"bureau\",\"work\"],char:\"🏢\",fitzpatrick_scale:false,category:\"travel_and_places\"},department_store:{keywords:[\"building\",\"shopping\",\"mall\"],char:\"🏬\",fitzpatrick_scale:false,category:\"travel_and_places\"},post_office:{keywords:[\"building\",\"envelope\",\"communication\"],char:\"🏣\",fitzpatrick_scale:false,category:\"travel_and_places\"},european_post_office:{keywords:[\"building\",\"email\"],char:\"🏤\",fitzpatrick_scale:false,category:\"travel_and_places\"},hospital:{keywords:[\"building\",\"health\",\"surgery\",\"doctor\"],char:\"🏥\",fitzpatrick_scale:false,category:\"travel_and_places\"},bank:{keywords:[\"building\",\"money\",\"sales\",\"cash\",\"business\",\"enterprise\"],char:\"🏦\",fitzpatrick_scale:false,category:\"travel_and_places\"},hotel:{keywords:[\"building\",\"accomodation\",\"checkin\"],char:\"🏨\",fitzpatrick_scale:false,category:\"travel_and_places\"},convenience_store:{keywords:[\"building\",\"shopping\",\"groceries\"],char:\"🏪\",fitzpatrick_scale:false,category:\"travel_and_places\"},school:{keywords:[\"building\",\"student\",\"education\",\"learn\",\"teach\"],char:\"🏫\",fitzpatrick_scale:false,category:\"travel_and_places\"},love_hotel:{keywords:[\"like\",\"affection\",\"dating\"],char:\"🏩\",fitzpatrick_scale:false,category:\"travel_and_places\"},wedding:{keywords:[\"love\",\"like\",\"affection\",\"couple\",\"marriage\",\"bride\",\"groom\"],char:\"💒\",fitzpatrick_scale:false,category:\"travel_and_places\"},classical_building:{keywords:[\"art\",\"culture\",\"history\"],char:\"🏛\",fitzpatrick_scale:false,category:\"travel_and_places\"},church:{keywords:[\"building\",\"religion\",\"christ\"],char:\"⛪\",fitzpatrick_scale:false,category:\"travel_and_places\"},mosque:{keywords:[\"islam\",\"worship\",\"minaret\"],char:\"🕌\",fitzpatrick_scale:false,category:\"travel_and_places\"},synagogue:{keywords:[\"judaism\",\"worship\",\"temple\",\"jewish\"],char:\"🕍\",fitzpatrick_scale:false,category:\"travel_and_places\"},kaaba:{keywords:[\"mecca\",\"mosque\",\"islam\"],char:\"🕋\",fitzpatrick_scale:false,category:\"travel_and_places\"},shinto_shrine:{keywords:[\"temple\",\"japan\",\"kyoto\"],char:\"⛩\",fitzpatrick_scale:false,category:\"travel_and_places\"},watch:{keywords:[\"time\",\"accessories\"],char:\"⌚\",fitzpatrick_scale:false,category:\"objects\"},iphone:{keywords:[\"technology\",\"apple\",\"gadgets\",\"dial\"],char:\"📱\",fitzpatrick_scale:false,category:\"objects\"},calling:{keywords:[\"iphone\",\"incoming\"],char:\"📲\",fitzpatrick_scale:false,category:\"objects\"},computer:{keywords:[\"technology\",\"laptop\",\"screen\",\"display\",\"monitor\"],char:\"💻\",fitzpatrick_scale:false,category:\"objects\"},keyboard:{keywords:[\"technology\",\"computer\",\"type\",\"input\",\"text\"],char:\"⌨\",fitzpatrick_scale:false,category:\"objects\"},desktop_computer:{keywords:[\"technology\",\"computing\",\"screen\"],char:\"🖥\",fitzpatrick_scale:false,category:\"objects\"},printer:{keywords:[\"paper\",\"ink\"],char:\"🖨\",fitzpatrick_scale:false,category:\"objects\"},computer_mouse:{keywords:[\"click\"],char:\"🖱\",fitzpatrick_scale:false,category:\"objects\"},trackball:{keywords:[\"technology\",\"trackpad\"],char:\"🖲\",fitzpatrick_scale:false,category:\"objects\"},joystick:{keywords:[\"game\",\"play\"],char:\"🕹\",fitzpatrick_scale:false,category:\"objects\"},clamp:{keywords:[\"tool\"],char:\"🗜\",fitzpatrick_scale:false,category:\"objects\"},minidisc:{keywords:[\"technology\",\"record\",\"data\",\"disk\",\"90s\"],char:\"💽\",fitzpatrick_scale:false,category:\"objects\"},floppy_disk:{keywords:[\"oldschool\",\"technology\",\"save\",\"90s\",\"80s\"],char:\"💾\",fitzpatrick_scale:false,category:\"objects\"},cd:{keywords:[\"technology\",\"dvd\",\"disk\",\"disc\",\"90s\"],char:\"💿\",fitzpatrick_scale:false,category:\"objects\"},dvd:{keywords:[\"cd\",\"disk\",\"disc\"],char:\"📀\",fitzpatrick_scale:false,category:\"objects\"},vhs:{keywords:[\"record\",\"video\",\"oldschool\",\"90s\",\"80s\"],char:\"📼\",fitzpatrick_scale:false,category:\"objects\"},camera:{keywords:[\"gadgets\",\"photography\"],char:\"📷\",fitzpatrick_scale:false,category:\"objects\"},camera_flash:{keywords:[\"photography\",\"gadgets\"],char:\"📸\",fitzpatrick_scale:false,category:\"objects\"},video_camera:{keywords:[\"film\",\"record\"],char:\"📹\",fitzpatrick_scale:false,category:\"objects\"},movie_camera:{keywords:[\"film\",\"record\"],char:\"🎥\",fitzpatrick_scale:false,category:\"objects\"},film_projector:{keywords:[\"video\",\"tape\",\"record\",\"movie\"],char:\"📽\",fitzpatrick_scale:false,category:\"objects\"},film_strip:{keywords:[\"movie\"],char:\"🎞\",fitzpatrick_scale:false,category:\"objects\"},telephone_receiver:{keywords:[\"technology\",\"communication\",\"dial\"],char:\"📞\",fitzpatrick_scale:false,category:\"objects\"},phone:{keywords:[\"technology\",\"communication\",\"dial\",\"telephone\"],char:\"☎️\",fitzpatrick_scale:false,category:\"objects\"},pager:{keywords:[\"bbcall\",\"oldschool\",\"90s\"],char:\"📟\",fitzpatrick_scale:false,category:\"objects\"},fax:{keywords:[\"communication\",\"technology\"],char:\"📠\",fitzpatrick_scale:false,category:\"objects\"},tv:{keywords:[\"technology\",\"program\",\"oldschool\",\"show\",\"television\"],char:\"📺\",fitzpatrick_scale:false,category:\"objects\"},radio:{keywords:[\"communication\",\"music\",\"podcast\",\"program\"],char:\"📻\",fitzpatrick_scale:false,category:\"objects\"},studio_microphone:{keywords:[\"sing\",\"recording\",\"artist\",\"talkshow\"],char:\"🎙\",fitzpatrick_scale:false,category:\"objects\"},level_slider:{keywords:[\"scale\"],char:\"🎚\",fitzpatrick_scale:false,category:\"objects\"},control_knobs:{keywords:[\"dial\"],char:\"🎛\",fitzpatrick_scale:false,category:\"objects\"},compass:{keywords:[\"magnetic\",\"navigation\",\"orienteering\"],char:\"🧭\",fitzpatrick_scale:false,category:\"objects\"},stopwatch:{keywords:[\"time\",\"deadline\"],char:\"⏱\",fitzpatrick_scale:false,category:\"objects\"},timer_clock:{keywords:[\"alarm\"],char:\"⏲\",fitzpatrick_scale:false,category:\"objects\"},alarm_clock:{keywords:[\"time\",\"wake\"],char:\"⏰\",fitzpatrick_scale:false,category:\"objects\"},mantelpiece_clock:{keywords:[\"time\"],char:\"🕰\",fitzpatrick_scale:false,category:\"objects\"},hourglass_flowing_sand:{keywords:[\"oldschool\",\"time\",\"countdown\"],char:\"⏳\",fitzpatrick_scale:false,category:\"objects\"},hourglass:{keywords:[\"time\",\"clock\",\"oldschool\",\"limit\",\"exam\",\"quiz\",\"test\"],char:\"⌛\",fitzpatrick_scale:false,category:\"objects\"},satellite:{keywords:[\"communication\",\"future\",\"radio\",\"space\"],char:\"📡\",fitzpatrick_scale:false,category:\"objects\"},battery:{keywords:[\"power\",\"energy\",\"sustain\"],char:\"🔋\",fitzpatrick_scale:false,category:\"objects\"},electric_plug:{keywords:[\"charger\",\"power\"],char:\"🔌\",fitzpatrick_scale:false,category:\"objects\"},bulb:{keywords:[\"light\",\"electricity\",\"idea\"],char:\"💡\",fitzpatrick_scale:false,category:\"objects\"},flashlight:{keywords:[\"dark\",\"camping\",\"sight\",\"night\"],char:\"🔦\",fitzpatrick_scale:false,category:\"objects\"},candle:{keywords:[\"fire\",\"wax\"],char:\"🕯\",fitzpatrick_scale:false,category:\"objects\"},fire_extinguisher:{keywords:[\"quench\"],char:\"🧯\",fitzpatrick_scale:false,category:\"objects\"},wastebasket:{keywords:[\"bin\",\"trash\",\"rubbish\",\"garbage\",\"toss\"],char:\"🗑\",fitzpatrick_scale:false,category:\"objects\"},oil_drum:{keywords:[\"barrell\"],char:\"🛢\",fitzpatrick_scale:false,category:\"objects\"},money_with_wings:{keywords:[\"dollar\",\"bills\",\"payment\",\"sale\"],char:\"💸\",fitzpatrick_scale:false,category:\"objects\"},dollar:{keywords:[\"money\",\"sales\",\"bill\",\"currency\"],char:\"💵\",fitzpatrick_scale:false,category:\"objects\"},yen:{keywords:[\"money\",\"sales\",\"japanese\",\"dollar\",\"currency\"],char:\"💴\",fitzpatrick_scale:false,category:\"objects\"},euro:{keywords:[\"money\",\"sales\",\"dollar\",\"currency\"],char:\"💶\",fitzpatrick_scale:false,category:\"objects\"},pound:{keywords:[\"british\",\"sterling\",\"money\",\"sales\",\"bills\",\"uk\",\"england\",\"currency\"],char:\"💷\",fitzpatrick_scale:false,category:\"objects\"},moneybag:{keywords:[\"dollar\",\"payment\",\"coins\",\"sale\"],char:\"💰\",fitzpatrick_scale:false,category:\"objects\"},credit_card:{keywords:[\"money\",\"sales\",\"dollar\",\"bill\",\"payment\",\"shopping\"],char:\"💳\",fitzpatrick_scale:false,category:\"objects\"},gem:{keywords:[\"blue\",\"ruby\",\"diamond\",\"jewelry\"],char:\"💎\",fitzpatrick_scale:false,category:\"objects\"},balance_scale:{keywords:[\"law\",\"fairness\",\"weight\"],char:\"⚖\",fitzpatrick_scale:false,category:\"objects\"},toolbox:{keywords:[\"tools\",\"diy\",\"fix\",\"maintainer\",\"mechanic\"],char:\"🧰\",fitzpatrick_scale:false,category:\"objects\"},wrench:{keywords:[\"tools\",\"diy\",\"ikea\",\"fix\",\"maintainer\"],char:\"🔧\",fitzpatrick_scale:false,category:\"objects\"},hammer:{keywords:[\"tools\",\"build\",\"create\"],char:\"🔨\",fitzpatrick_scale:false,category:\"objects\"},hammer_and_pick:{keywords:[\"tools\",\"build\",\"create\"],char:\"⚒\",fitzpatrick_scale:false,category:\"objects\"},hammer_and_wrench:{keywords:[\"tools\",\"build\",\"create\"],char:\"🛠\",fitzpatrick_scale:false,category:\"objects\"},pick:{keywords:[\"tools\",\"dig\"],char:\"⛏\",fitzpatrick_scale:false,category:\"objects\"},nut_and_bolt:{keywords:[\"handy\",\"tools\",\"fix\"],char:\"🔩\",fitzpatrick_scale:false,category:\"objects\"},gear:{keywords:[\"cog\"],char:\"⚙\",fitzpatrick_scale:false,category:\"objects\"},brick:{keywords:[\"bricks\"],char:\"🧱\",fitzpatrick_scale:false,category:\"objects\"},chains:{keywords:[\"lock\",\"arrest\"],char:\"⛓\",fitzpatrick_scale:false,category:\"objects\"},magnet:{keywords:[\"attraction\",\"magnetic\"],char:\"🧲\",fitzpatrick_scale:false,category:\"objects\"},gun:{keywords:[\"violence\",\"weapon\",\"pistol\",\"revolver\"],char:\"🔫\",fitzpatrick_scale:false,category:\"objects\"},bomb:{keywords:[\"boom\",\"explode\",\"explosion\",\"terrorism\"],char:\"💣\",fitzpatrick_scale:false,category:\"objects\"},firecracker:{keywords:[\"dynamite\",\"boom\",\"explode\",\"explosion\",\"explosive\"],char:\"🧨\",fitzpatrick_scale:false,category:\"objects\"},hocho:{keywords:[\"knife\",\"blade\",\"cutlery\",\"kitchen\",\"weapon\"],char:\"🔪\",fitzpatrick_scale:false,category:\"objects\"},dagger:{keywords:[\"weapon\"],char:\"🗡\",fitzpatrick_scale:false,category:\"objects\"},crossed_swords:{keywords:[\"weapon\"],char:\"⚔\",fitzpatrick_scale:false,category:\"objects\"},shield:{keywords:[\"protection\",\"security\"],char:\"🛡\",fitzpatrick_scale:false,category:\"objects\"},smoking:{keywords:[\"kills\",\"tobacco\",\"cigarette\",\"joint\",\"smoke\"],char:\"🚬\",fitzpatrick_scale:false,category:\"objects\"},skull_and_crossbones:{keywords:[\"poison\",\"danger\",\"deadly\",\"scary\",\"death\",\"pirate\",\"evil\"],char:\"☠\",fitzpatrick_scale:false,category:\"objects\"},coffin:{keywords:[\"vampire\",\"dead\",\"die\",\"death\",\"rip\",\"graveyard\",\"cemetery\",\"casket\",\"funeral\",\"box\"],char:\"⚰\",fitzpatrick_scale:false,category:\"objects\"},funeral_urn:{keywords:[\"dead\",\"die\",\"death\",\"rip\",\"ashes\"],char:\"⚱\",fitzpatrick_scale:false,category:\"objects\"},amphora:{keywords:[\"vase\",\"jar\"],char:\"🏺\",fitzpatrick_scale:false,category:\"objects\"},crystal_ball:{keywords:[\"disco\",\"party\",\"magic\",\"circus\",\"fortune_teller\"],char:\"🔮\",fitzpatrick_scale:false,category:\"objects\"},prayer_beads:{keywords:[\"dhikr\",\"religious\"],char:\"📿\",fitzpatrick_scale:false,category:\"objects\"},nazar_amulet:{keywords:[\"bead\",\"charm\"],char:\"🧿\",fitzpatrick_scale:false,category:\"objects\"},barber:{keywords:[\"hair\",\"salon\",\"style\"],char:\"💈\",fitzpatrick_scale:false,category:\"objects\"},alembic:{keywords:[\"distilling\",\"science\",\"experiment\",\"chemistry\"],char:\"⚗\",fitzpatrick_scale:false,category:\"objects\"},telescope:{keywords:[\"stars\",\"space\",\"zoom\",\"science\",\"astronomy\"],char:\"🔭\",fitzpatrick_scale:false,category:\"objects\"},microscope:{keywords:[\"laboratory\",\"experiment\",\"zoomin\",\"science\",\"study\"],char:\"🔬\",fitzpatrick_scale:false,category:\"objects\"},hole:{keywords:[\"embarrassing\"],char:\"🕳\",fitzpatrick_scale:false,category:\"objects\"},pill:{keywords:[\"health\",\"medicine\",\"doctor\",\"pharmacy\",\"drug\"],char:\"💊\",fitzpatrick_scale:false,category:\"objects\"},syringe:{keywords:[\"health\",\"hospital\",\"drugs\",\"blood\",\"medicine\",\"needle\",\"doctor\",\"nurse\"],char:\"💉\",fitzpatrick_scale:false,category:\"objects\"},dna:{keywords:[\"biologist\",\"genetics\",\"life\"],char:\"🧬\",fitzpatrick_scale:false,category:\"objects\"},microbe:{keywords:[\"amoeba\",\"bacteria\",\"germs\"],char:\"🦠\",fitzpatrick_scale:false,category:\"objects\"},petri_dish:{keywords:[\"bacteria\",\"biology\",\"culture\",\"lab\"],char:\"🧫\",fitzpatrick_scale:false,category:\"objects\"},test_tube:{keywords:[\"chemistry\",\"experiment\",\"lab\",\"science\"],char:\"🧪\",fitzpatrick_scale:false,category:\"objects\"},thermometer:{keywords:[\"weather\",\"temperature\",\"hot\",\"cold\"],char:\"🌡\",fitzpatrick_scale:false,category:\"objects\"},broom:{keywords:[\"cleaning\",\"sweeping\",\"witch\"],char:\"🧹\",fitzpatrick_scale:false,category:\"objects\"},basket:{keywords:[\"laundry\"],char:\"🧺\",fitzpatrick_scale:false,category:\"objects\"},toilet_paper:{keywords:[\"roll\"],char:\"🧻\",fitzpatrick_scale:false,category:\"objects\"},label:{keywords:[\"sale\",\"tag\"],char:\"🏷\",fitzpatrick_scale:false,category:\"objects\"},bookmark:{keywords:[\"favorite\",\"label\",\"save\"],char:\"🔖\",fitzpatrick_scale:false,category:\"objects\"},toilet:{keywords:[\"restroom\",\"wc\",\"washroom\",\"bathroom\",\"potty\"],char:\"🚽\",fitzpatrick_scale:false,category:\"objects\"},shower:{keywords:[\"clean\",\"water\",\"bathroom\"],char:\"🚿\",fitzpatrick_scale:false,category:\"objects\"},bathtub:{keywords:[\"clean\",\"shower\",\"bathroom\"],char:\"🛁\",fitzpatrick_scale:false,category:\"objects\"},soap:{keywords:[\"bar\",\"bathing\",\"cleaning\",\"lather\"],char:\"🧼\",fitzpatrick_scale:false,category:\"objects\"},sponge:{keywords:[\"absorbing\",\"cleaning\",\"porous\"],char:\"🧽\",fitzpatrick_scale:false,category:\"objects\"},lotion_bottle:{keywords:[\"moisturizer\",\"sunscreen\"],char:\"🧴\",fitzpatrick_scale:false,category:\"objects\"},key:{keywords:[\"lock\",\"door\",\"password\"],char:\"🔑\",fitzpatrick_scale:false,category:\"objects\"},old_key:{keywords:[\"lock\",\"door\",\"password\"],char:\"🗝\",fitzpatrick_scale:false,category:\"objects\"},couch_and_lamp:{keywords:[\"read\",\"chill\"],char:\"🛋\",fitzpatrick_scale:false,category:\"objects\"},sleeping_bed:{keywords:[\"bed\",\"rest\"],char:\"🛌\",fitzpatrick_scale:true,category:\"objects\"},bed:{keywords:[\"sleep\",\"rest\"],char:\"🛏\",fitzpatrick_scale:false,category:\"objects\"},door:{keywords:[\"house\",\"entry\",\"exit\"],char:\"🚪\",fitzpatrick_scale:false,category:\"objects\"},bellhop_bell:{keywords:[\"service\"],char:\"🛎\",fitzpatrick_scale:false,category:\"objects\"},teddy_bear:{keywords:[\"plush\",\"stuffed\"],char:\"🧸\",fitzpatrick_scale:false,category:\"objects\"},framed_picture:{keywords:[\"photography\"],char:\"🖼\",fitzpatrick_scale:false,category:\"objects\"},world_map:{keywords:[\"location\",\"direction\"],char:\"🗺\",fitzpatrick_scale:false,category:\"objects\"},parasol_on_ground:{keywords:[\"weather\",\"summer\"],char:\"⛱\",fitzpatrick_scale:false,category:\"objects\"},moyai:{keywords:[\"rock\",\"easter island\",\"moai\"],char:\"🗿\",fitzpatrick_scale:false,category:\"objects\"},shopping:{keywords:[\"mall\",\"buy\",\"purchase\"],char:\"🛍\",fitzpatrick_scale:false,category:\"objects\"},shopping_cart:{keywords:[\"trolley\"],char:\"🛒\",fitzpatrick_scale:false,category:\"objects\"},balloon:{keywords:[\"party\",\"celebration\",\"birthday\",\"circus\"],char:\"🎈\",fitzpatrick_scale:false,category:\"objects\"},flags:{keywords:[\"fish\",\"japanese\",\"koinobori\",\"carp\",\"banner\"],char:\"🎏\",fitzpatrick_scale:false,category:\"objects\"},ribbon:{keywords:[\"decoration\",\"pink\",\"girl\",\"bowtie\"],char:\"🎀\",fitzpatrick_scale:false,category:\"objects\"},gift:{keywords:[\"present\",\"birthday\",\"christmas\",\"xmas\"],char:\"🎁\",fitzpatrick_scale:false,category:\"objects\"},confetti_ball:{keywords:[\"festival\",\"party\",\"birthday\",\"circus\"],char:\"🎊\",fitzpatrick_scale:false,category:\"objects\"},tada:{keywords:[\"party\",\"congratulations\",\"birthday\",\"magic\",\"circus\",\"celebration\"],char:\"🎉\",fitzpatrick_scale:false,category:\"objects\"},dolls:{keywords:[\"japanese\",\"toy\",\"kimono\"],char:\"🎎\",fitzpatrick_scale:false,category:\"objects\"},wind_chime:{keywords:[\"nature\",\"ding\",\"spring\",\"bell\"],char:\"🎐\",fitzpatrick_scale:false,category:\"objects\"},crossed_flags:{keywords:[\"japanese\",\"nation\",\"country\",\"border\"],char:\"🎌\",fitzpatrick_scale:false,category:\"objects\"},izakaya_lantern:{keywords:[\"light\",\"paper\",\"halloween\",\"spooky\"],char:\"🏮\",fitzpatrick_scale:false,category:\"objects\"},red_envelope:{keywords:[\"gift\"],char:\"🧧\",fitzpatrick_scale:false,category:\"objects\"},email:{keywords:[\"letter\",\"postal\",\"inbox\",\"communication\"],char:\"✉️\",fitzpatrick_scale:false,category:\"objects\"},envelope_with_arrow:{keywords:[\"email\",\"communication\"],char:\"📩\",fitzpatrick_scale:false,category:\"objects\"},incoming_envelope:{keywords:[\"email\",\"inbox\"],char:\"📨\",fitzpatrick_scale:false,category:\"objects\"},\"e-mail\":{keywords:[\"communication\",\"inbox\"],char:\"📧\",fitzpatrick_scale:false,category:\"objects\"},love_letter:{keywords:[\"email\",\"like\",\"affection\",\"envelope\",\"valentines\"],char:\"💌\",fitzpatrick_scale:false,category:\"objects\"},postbox:{keywords:[\"email\",\"letter\",\"envelope\"],char:\"📮\",fitzpatrick_scale:false,category:\"objects\"},mailbox_closed:{keywords:[\"email\",\"communication\",\"inbox\"],char:\"📪\",fitzpatrick_scale:false,category:\"objects\"},mailbox:{keywords:[\"email\",\"inbox\",\"communication\"],char:\"📫\",fitzpatrick_scale:false,category:\"objects\"},mailbox_with_mail:{keywords:[\"email\",\"inbox\",\"communication\"],char:\"📬\",fitzpatrick_scale:false,category:\"objects\"},mailbox_with_no_mail:{keywords:[\"email\",\"inbox\"],char:\"📭\",fitzpatrick_scale:false,category:\"objects\"},package:{keywords:[\"mail\",\"gift\",\"cardboard\",\"box\",\"moving\"],char:\"📦\",fitzpatrick_scale:false,category:\"objects\"},postal_horn:{keywords:[\"instrument\",\"music\"],char:\"📯\",fitzpatrick_scale:false,category:\"objects\"},inbox_tray:{keywords:[\"email\",\"documents\"],char:\"📥\",fitzpatrick_scale:false,category:\"objects\"},outbox_tray:{keywords:[\"inbox\",\"email\"],char:\"📤\",fitzpatrick_scale:false,category:\"objects\"},scroll:{keywords:[\"documents\",\"ancient\",\"history\",\"paper\"],char:\"📜\",fitzpatrick_scale:false,category:\"objects\"},page_with_curl:{keywords:[\"documents\",\"office\",\"paper\"],char:\"📃\",fitzpatrick_scale:false,category:\"objects\"},bookmark_tabs:{keywords:[\"favorite\",\"save\",\"order\",\"tidy\"],char:\"📑\",fitzpatrick_scale:false,category:\"objects\"},receipt:{keywords:[\"accounting\",\"expenses\"],char:\"🧾\",fitzpatrick_scale:false,category:\"objects\"},bar_chart:{keywords:[\"graph\",\"presentation\",\"stats\"],char:\"📊\",fitzpatrick_scale:false,category:\"objects\"},chart_with_upwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recovery\",\"business\",\"economics\",\"money\",\"sales\",\"good\",\"success\"],char:\"📈\",fitzpatrick_scale:false,category:\"objects\"},chart_with_downwards_trend:{keywords:[\"graph\",\"presentation\",\"stats\",\"recession\",\"business\",\"economics\",\"money\",\"sales\",\"bad\",\"failure\"],char:\"📉\",fitzpatrick_scale:false,category:\"objects\"},page_facing_up:{keywords:[\"documents\",\"office\",\"paper\",\"information\"],char:\"📄\",fitzpatrick_scale:false,category:\"objects\"},date:{keywords:[\"calendar\",\"schedule\"],char:\"📅\",fitzpatrick_scale:false,category:\"objects\"},calendar:{keywords:[\"schedule\",\"date\",\"planning\"],char:\"📆\",fitzpatrick_scale:false,category:\"objects\"},spiral_calendar:{keywords:[\"date\",\"schedule\",\"planning\"],char:\"🗓\",fitzpatrick_scale:false,category:\"objects\"},card_index:{keywords:[\"business\",\"stationery\"],char:\"📇\",fitzpatrick_scale:false,category:\"objects\"},card_file_box:{keywords:[\"business\",\"stationery\"],char:\"🗃\",fitzpatrick_scale:false,category:\"objects\"},ballot_box:{keywords:[\"election\",\"vote\"],char:\"🗳\",fitzpatrick_scale:false,category:\"objects\"},file_cabinet:{keywords:[\"filing\",\"organizing\"],char:\"🗄\",fitzpatrick_scale:false,category:\"objects\"},clipboard:{keywords:[\"stationery\",\"documents\"],char:\"📋\",fitzpatrick_scale:false,category:\"objects\"},spiral_notepad:{keywords:[\"memo\",\"stationery\"],char:\"🗒\",fitzpatrick_scale:false,category:\"objects\"},file_folder:{keywords:[\"documents\",\"business\",\"office\"],char:\"📁\",fitzpatrick_scale:false,category:\"objects\"},open_file_folder:{keywords:[\"documents\",\"load\"],char:\"📂\",fitzpatrick_scale:false,category:\"objects\"},card_index_dividers:{keywords:[\"organizing\",\"business\",\"stationery\"],char:\"🗂\",fitzpatrick_scale:false,category:\"objects\"},newspaper_roll:{keywords:[\"press\",\"headline\"],char:\"🗞\",fitzpatrick_scale:false,category:\"objects\"},newspaper:{keywords:[\"press\",\"headline\"],char:\"📰\",fitzpatrick_scale:false,category:\"objects\"},notebook:{keywords:[\"stationery\",\"record\",\"notes\",\"paper\",\"study\"],char:\"📓\",fitzpatrick_scale:false,category:\"objects\"},closed_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"learn\"],char:\"📕\",fitzpatrick_scale:false,category:\"objects\"},green_book:{keywords:[\"read\",\"library\",\"knowledge\",\"study\"],char:\"📗\",fitzpatrick_scale:false,category:\"objects\"},blue_book:{keywords:[\"read\",\"library\",\"knowledge\",\"learn\",\"study\"],char:\"📘\",fitzpatrick_scale:false,category:\"objects\"},orange_book:{keywords:[\"read\",\"library\",\"knowledge\",\"textbook\",\"study\"],char:\"📙\",fitzpatrick_scale:false,category:\"objects\"},notebook_with_decorative_cover:{keywords:[\"classroom\",\"notes\",\"record\",\"paper\",\"study\"],char:\"📔\",fitzpatrick_scale:false,category:\"objects\"},ledger:{keywords:[\"notes\",\"paper\"],char:\"📒\",fitzpatrick_scale:false,category:\"objects\"},books:{keywords:[\"literature\",\"library\",\"study\"],char:\"📚\",fitzpatrick_scale:false,category:\"objects\"},open_book:{keywords:[\"book\",\"read\",\"library\",\"knowledge\",\"literature\",\"learn\",\"study\"],char:\"📖\",fitzpatrick_scale:false,category:\"objects\"},safety_pin:{keywords:[\"diaper\"],char:\"🧷\",fitzpatrick_scale:false,category:\"objects\"},link:{keywords:[\"rings\",\"url\"],char:\"🔗\",fitzpatrick_scale:false,category:\"objects\"},paperclip:{keywords:[\"documents\",\"stationery\"],char:\"📎\",fitzpatrick_scale:false,category:\"objects\"},paperclips:{keywords:[\"documents\",\"stationery\"],char:\"🖇\",fitzpatrick_scale:false,category:\"objects\"},scissors:{keywords:[\"stationery\",\"cut\"],char:\"✂️\",fitzpatrick_scale:false,category:\"objects\"},triangular_ruler:{keywords:[\"stationery\",\"math\",\"architect\",\"sketch\"],char:\"📐\",fitzpatrick_scale:false,category:\"objects\"},straight_ruler:{keywords:[\"stationery\",\"calculate\",\"length\",\"math\",\"school\",\"drawing\",\"architect\",\"sketch\"],char:\"📏\",fitzpatrick_scale:false,category:\"objects\"},abacus:{keywords:[\"calculation\"],char:\"🧮\",fitzpatrick_scale:false,category:\"objects\"},pushpin:{keywords:[\"stationery\",\"mark\",\"here\"],char:\"📌\",fitzpatrick_scale:false,category:\"objects\"},round_pushpin:{keywords:[\"stationery\",\"location\",\"map\",\"here\"],char:\"📍\",fitzpatrick_scale:false,category:\"objects\"},triangular_flag_on_post:{keywords:[\"mark\",\"milestone\",\"place\"],char:\"🚩\",fitzpatrick_scale:false,category:\"objects\"},white_flag:{keywords:[\"losing\",\"loser\",\"lost\",\"surrender\",\"give up\",\"fail\"],char:\"🏳\",fitzpatrick_scale:false,category:\"objects\"},black_flag:{keywords:[\"pirate\"],char:\"🏴\",fitzpatrick_scale:false,category:\"objects\"},rainbow_flag:{keywords:[\"flag\",\"rainbow\",\"pride\",\"gay\",\"lgbt\",\"glbt\",\"queer\",\"homosexual\",\"lesbian\",\"bisexual\",\"transgender\"],char:\"🏳️‍🌈\",fitzpatrick_scale:false,category:\"objects\"},closed_lock_with_key:{keywords:[\"security\",\"privacy\"],char:\"🔐\",fitzpatrick_scale:false,category:\"objects\"},lock:{keywords:[\"security\",\"password\",\"padlock\"],char:\"🔒\",fitzpatrick_scale:false,category:\"objects\"},unlock:{keywords:[\"privacy\",\"security\"],char:\"🔓\",fitzpatrick_scale:false,category:\"objects\"},lock_with_ink_pen:{keywords:[\"security\",\"secret\"],char:\"🔏\",fitzpatrick_scale:false,category:\"objects\"},pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:\"🖊\",fitzpatrick_scale:false,category:\"objects\"},fountain_pen:{keywords:[\"stationery\",\"writing\",\"write\"],char:\"🖋\",fitzpatrick_scale:false,category:\"objects\"},black_nib:{keywords:[\"pen\",\"stationery\",\"writing\",\"write\"],char:\"✒️\",fitzpatrick_scale:false,category:\"objects\"},memo:{keywords:[\"write\",\"documents\",\"stationery\",\"pencil\",\"paper\",\"writing\",\"legal\",\"exam\",\"quiz\",\"test\",\"study\",\"compose\"],char:\"📝\",fitzpatrick_scale:false,category:\"objects\"},pencil2:{keywords:[\"stationery\",\"write\",\"paper\",\"writing\",\"school\",\"study\"],char:\"✏️\",fitzpatrick_scale:false,category:\"objects\"},crayon:{keywords:[\"drawing\",\"creativity\"],char:\"🖍\",fitzpatrick_scale:false,category:\"objects\"},paintbrush:{keywords:[\"drawing\",\"creativity\",\"art\"],char:\"🖌\",fitzpatrick_scale:false,category:\"objects\"},mag:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:\"🔍\",fitzpatrick_scale:false,category:\"objects\"},mag_right:{keywords:[\"search\",\"zoom\",\"find\",\"detective\"],char:\"🔎\",fitzpatrick_scale:false,category:\"objects\"},heart:{keywords:[\"love\",\"like\",\"valentines\"],char:\"❤️\",fitzpatrick_scale:false,category:\"symbols\"},orange_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"🧡\",fitzpatrick_scale:false,category:\"symbols\"},yellow_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💛\",fitzpatrick_scale:false,category:\"symbols\"},green_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💚\",fitzpatrick_scale:false,category:\"symbols\"},blue_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💙\",fitzpatrick_scale:false,category:\"symbols\"},purple_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💜\",fitzpatrick_scale:false,category:\"symbols\"},black_heart:{keywords:[\"evil\"],char:\"🖤\",fitzpatrick_scale:false,category:\"symbols\"},broken_heart:{keywords:[\"sad\",\"sorry\",\"break\",\"heart\",\"heartbreak\"],char:\"💔\",fitzpatrick_scale:false,category:\"symbols\"},heavy_heart_exclamation:{keywords:[\"decoration\",\"love\"],char:\"❣\",fitzpatrick_scale:false,category:\"symbols\"},two_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"heart\"],char:\"💕\",fitzpatrick_scale:false,category:\"symbols\"},revolving_hearts:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💞\",fitzpatrick_scale:false,category:\"symbols\"},heartbeat:{keywords:[\"love\",\"like\",\"affection\",\"valentines\",\"pink\",\"heart\"],char:\"💓\",fitzpatrick_scale:false,category:\"symbols\"},heartpulse:{keywords:[\"like\",\"love\",\"affection\",\"valentines\",\"pink\"],char:\"💗\",fitzpatrick_scale:false,category:\"symbols\"},sparkling_heart:{keywords:[\"love\",\"like\",\"affection\",\"valentines\"],char:\"💖\",fitzpatrick_scale:false,category:\"symbols\"},cupid:{keywords:[\"love\",\"like\",\"heart\",\"affection\",\"valentines\"],char:\"💘\",fitzpatrick_scale:false,category:\"symbols\"},gift_heart:{keywords:[\"love\",\"valentines\"],char:\"💝\",fitzpatrick_scale:false,category:\"symbols\"},heart_decoration:{keywords:[\"purple-square\",\"love\",\"like\"],char:\"💟\",fitzpatrick_scale:false,category:\"symbols\"},peace_symbol:{keywords:[\"hippie\"],char:\"☮\",fitzpatrick_scale:false,category:\"symbols\"},latin_cross:{keywords:[\"christianity\"],char:\"✝\",fitzpatrick_scale:false,category:\"symbols\"},star_and_crescent:{keywords:[\"islam\"],char:\"☪\",fitzpatrick_scale:false,category:\"symbols\"},om:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:\"🕉\",fitzpatrick_scale:false,category:\"symbols\"},wheel_of_dharma:{keywords:[\"hinduism\",\"buddhism\",\"sikhism\",\"jainism\"],char:\"☸\",fitzpatrick_scale:false,category:\"symbols\"},star_of_david:{keywords:[\"judaism\"],char:\"✡\",fitzpatrick_scale:false,category:\"symbols\"},six_pointed_star:{keywords:[\"purple-square\",\"religion\",\"jewish\",\"hexagram\"],char:\"🔯\",fitzpatrick_scale:false,category:\"symbols\"},menorah:{keywords:[\"hanukkah\",\"candles\",\"jewish\"],char:\"🕎\",fitzpatrick_scale:false,category:\"symbols\"},yin_yang:{keywords:[\"balance\"],char:\"☯\",fitzpatrick_scale:false,category:\"symbols\"},orthodox_cross:{keywords:[\"suppedaneum\",\"religion\"],char:\"☦\",fitzpatrick_scale:false,category:\"symbols\"},place_of_worship:{keywords:[\"religion\",\"church\",\"temple\",\"prayer\"],char:\"🛐\",fitzpatrick_scale:false,category:\"symbols\"},ophiuchus:{keywords:[\"sign\",\"purple-square\",\"constellation\",\"astrology\"],char:\"⛎\",fitzpatrick_scale:false,category:\"symbols\"},aries:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♈\",fitzpatrick_scale:false,category:\"symbols\"},taurus:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:\"♉\",fitzpatrick_scale:false,category:\"symbols\"},gemini:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♊\",fitzpatrick_scale:false,category:\"symbols\"},cancer:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♋\",fitzpatrick_scale:false,category:\"symbols\"},leo:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♌\",fitzpatrick_scale:false,category:\"symbols\"},virgo:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♍\",fitzpatrick_scale:false,category:\"symbols\"},libra:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♎\",fitzpatrick_scale:false,category:\"symbols\"},scorpius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\",\"scorpio\"],char:\"♏\",fitzpatrick_scale:false,category:\"symbols\"},sagittarius:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♐\",fitzpatrick_scale:false,category:\"symbols\"},capricorn:{keywords:[\"sign\",\"zodiac\",\"purple-square\",\"astrology\"],char:\"♑\",fitzpatrick_scale:false,category:\"symbols\"},aquarius:{keywords:[\"sign\",\"purple-square\",\"zodiac\",\"astrology\"],char:\"♒\",fitzpatrick_scale:false,category:\"symbols\"},pisces:{keywords:[\"purple-square\",\"sign\",\"zodiac\",\"astrology\"],char:\"♓\",fitzpatrick_scale:false,category:\"symbols\"},id:{keywords:[\"purple-square\",\"words\"],char:\"🆔\",fitzpatrick_scale:false,category:\"symbols\"},atom_symbol:{keywords:[\"science\",\"physics\",\"chemistry\"],char:\"⚛\",fitzpatrick_scale:false,category:\"symbols\"},u7a7a:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"empty\",\"sky\",\"blue-square\"],char:\"🈳\",fitzpatrick_scale:false,category:\"symbols\"},u5272:{keywords:[\"cut\",\"divide\",\"chinese\",\"kanji\",\"pink-square\"],char:\"🈹\",fitzpatrick_scale:false,category:\"symbols\"},radioactive:{keywords:[\"nuclear\",\"danger\"],char:\"☢\",fitzpatrick_scale:false,category:\"symbols\"},biohazard:{keywords:[\"danger\"],char:\"☣\",fitzpatrick_scale:false,category:\"symbols\"},mobile_phone_off:{keywords:[\"mute\",\"orange-square\",\"silence\",\"quiet\"],char:\"📴\",fitzpatrick_scale:false,category:\"symbols\"},vibration_mode:{keywords:[\"orange-square\",\"phone\"],char:\"📳\",fitzpatrick_scale:false,category:\"symbols\"},u6709:{keywords:[\"orange-square\",\"chinese\",\"have\",\"kanji\"],char:\"🈶\",fitzpatrick_scale:false,category:\"symbols\"},u7121:{keywords:[\"nothing\",\"chinese\",\"kanji\",\"japanese\",\"orange-square\"],char:\"🈚\",fitzpatrick_scale:false,category:\"symbols\"},u7533:{keywords:[\"chinese\",\"japanese\",\"kanji\",\"orange-square\"],char:\"🈸\",fitzpatrick_scale:false,category:\"symbols\"},u55b6:{keywords:[\"japanese\",\"opening hours\",\"orange-square\"],char:\"🈺\",fitzpatrick_scale:false,category:\"symbols\"},u6708:{keywords:[\"chinese\",\"month\",\"moon\",\"japanese\",\"orange-square\",\"kanji\"],char:\"🈷️\",fitzpatrick_scale:false,category:\"symbols\"},eight_pointed_black_star:{keywords:[\"orange-square\",\"shape\",\"polygon\"],char:\"✴️\",fitzpatrick_scale:false,category:\"symbols\"},vs:{keywords:[\"words\",\"orange-square\"],char:\"🆚\",fitzpatrick_scale:false,category:\"symbols\"},accept:{keywords:[\"ok\",\"good\",\"chinese\",\"kanji\",\"agree\",\"yes\",\"orange-circle\"],char:\"🉑\",fitzpatrick_scale:false,category:\"symbols\"},white_flower:{keywords:[\"japanese\",\"spring\"],char:\"💮\",fitzpatrick_scale:false,category:\"symbols\"},ideograph_advantage:{keywords:[\"chinese\",\"kanji\",\"obtain\",\"get\",\"circle\"],char:\"🉐\",fitzpatrick_scale:false,category:\"symbols\"},secret:{keywords:[\"privacy\",\"chinese\",\"sshh\",\"kanji\",\"red-circle\"],char:\"㊙️\",fitzpatrick_scale:false,category:\"symbols\"},congratulations:{keywords:[\"chinese\",\"kanji\",\"japanese\",\"red-circle\"],char:\"㊗️\",fitzpatrick_scale:false,category:\"symbols\"},u5408:{keywords:[\"japanese\",\"chinese\",\"join\",\"kanji\",\"red-square\"],char:\"🈴\",fitzpatrick_scale:false,category:\"symbols\"},u6e80:{keywords:[\"full\",\"chinese\",\"japanese\",\"red-square\",\"kanji\"],char:\"🈵\",fitzpatrick_scale:false,category:\"symbols\"},u7981:{keywords:[\"kanji\",\"japanese\",\"chinese\",\"forbidden\",\"limit\",\"restricted\",\"red-square\"],char:\"🈲\",fitzpatrick_scale:false,category:\"symbols\"},a:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:\"🅰️\",fitzpatrick_scale:false,category:\"symbols\"},b:{keywords:[\"red-square\",\"alphabet\",\"letter\"],char:\"🅱️\",fitzpatrick_scale:false,category:\"symbols\"},ab:{keywords:[\"red-square\",\"alphabet\"],char:\"🆎\",fitzpatrick_scale:false,category:\"symbols\"},cl:{keywords:[\"alphabet\",\"words\",\"red-square\"],char:\"🆑\",fitzpatrick_scale:false,category:\"symbols\"},o2:{keywords:[\"alphabet\",\"red-square\",\"letter\"],char:\"🅾️\",fitzpatrick_scale:false,category:\"symbols\"},sos:{keywords:[\"help\",\"red-square\",\"words\",\"emergency\",\"911\"],char:\"🆘\",fitzpatrick_scale:false,category:\"symbols\"},no_entry:{keywords:[\"limit\",\"security\",\"privacy\",\"bad\",\"denied\",\"stop\",\"circle\"],char:\"⛔\",fitzpatrick_scale:false,category:\"symbols\"},name_badge:{keywords:[\"fire\",\"forbid\"],char:\"📛\",fitzpatrick_scale:false,category:\"symbols\"},no_entry_sign:{keywords:[\"forbid\",\"stop\",\"limit\",\"denied\",\"disallow\",\"circle\"],char:\"🚫\",fitzpatrick_scale:false,category:\"symbols\"},x:{keywords:[\"no\",\"delete\",\"remove\",\"cancel\",\"red\"],char:\"❌\",fitzpatrick_scale:false,category:\"symbols\"},o:{keywords:[\"circle\",\"round\"],char:\"⭕\",fitzpatrick_scale:false,category:\"symbols\"},stop_sign:{keywords:[\"stop\"],char:\"🛑\",fitzpatrick_scale:false,category:\"symbols\"},anger:{keywords:[\"angry\",\"mad\"],char:\"💢\",fitzpatrick_scale:false,category:\"symbols\"},hotsprings:{keywords:[\"bath\",\"warm\",\"relax\"],char:\"♨️\",fitzpatrick_scale:false,category:\"symbols\"},no_pedestrians:{keywords:[\"rules\",\"crossing\",\"walking\",\"circle\"],char:\"🚷\",fitzpatrick_scale:false,category:\"symbols\"},do_not_litter:{keywords:[\"trash\",\"bin\",\"garbage\",\"circle\"],char:\"🚯\",fitzpatrick_scale:false,category:\"symbols\"},no_bicycles:{keywords:[\"cyclist\",\"prohibited\",\"circle\"],char:\"🚳\",fitzpatrick_scale:false,category:\"symbols\"},\"non-potable_water\":{keywords:[\"drink\",\"faucet\",\"tap\",\"circle\"],char:\"🚱\",fitzpatrick_scale:false,category:\"symbols\"},underage:{keywords:[\"18\",\"drink\",\"pub\",\"night\",\"minor\",\"circle\"],char:\"🔞\",fitzpatrick_scale:false,category:\"symbols\"},no_mobile_phones:{keywords:[\"iphone\",\"mute\",\"circle\"],char:\"📵\",fitzpatrick_scale:false,category:\"symbols\"},exclamation:{keywords:[\"heavy_exclamation_mark\",\"danger\",\"surprise\",\"punctuation\",\"wow\",\"warning\"],char:\"❗\",fitzpatrick_scale:false,category:\"symbols\"},grey_exclamation:{keywords:[\"surprise\",\"punctuation\",\"gray\",\"wow\",\"warning\"],char:\"❕\",fitzpatrick_scale:false,category:\"symbols\"},question:{keywords:[\"doubt\",\"confused\"],char:\"❓\",fitzpatrick_scale:false,category:\"symbols\"},grey_question:{keywords:[\"doubts\",\"gray\",\"huh\",\"confused\"],char:\"❔\",fitzpatrick_scale:false,category:\"symbols\"},bangbang:{keywords:[\"exclamation\",\"surprise\"],char:\"‼️\",fitzpatrick_scale:false,category:\"symbols\"},interrobang:{keywords:[\"wat\",\"punctuation\",\"surprise\"],char:\"⁉️\",fitzpatrick_scale:false,category:\"symbols\"},100:{keywords:[\"score\",\"perfect\",\"numbers\",\"century\",\"exam\",\"quiz\",\"test\",\"pass\",\"hundred\"],char:\"💯\",fitzpatrick_scale:false,category:\"symbols\"},low_brightness:{keywords:[\"sun\",\"afternoon\",\"warm\",\"summer\"],char:\"🔅\",fitzpatrick_scale:false,category:\"symbols\"},high_brightness:{keywords:[\"sun\",\"light\"],char:\"🔆\",fitzpatrick_scale:false,category:\"symbols\"},trident:{keywords:[\"weapon\",\"spear\"],char:\"🔱\",fitzpatrick_scale:false,category:\"symbols\"},fleur_de_lis:{keywords:[\"decorative\",\"scout\"],char:\"⚜\",fitzpatrick_scale:false,category:\"symbols\"},part_alternation_mark:{keywords:[\"graph\",\"presentation\",\"stats\",\"business\",\"economics\",\"bad\"],char:\"〽️\",fitzpatrick_scale:false,category:\"symbols\"},warning:{keywords:[\"exclamation\",\"wip\",\"alert\",\"error\",\"problem\",\"issue\"],char:\"⚠️\",fitzpatrick_scale:false,category:\"symbols\"},children_crossing:{keywords:[\"school\",\"warning\",\"danger\",\"sign\",\"driving\",\"yellow-diamond\"],char:\"🚸\",fitzpatrick_scale:false,category:\"symbols\"},beginner:{keywords:[\"badge\",\"shield\"],char:\"🔰\",fitzpatrick_scale:false,category:\"symbols\"},recycle:{keywords:[\"arrow\",\"environment\",\"garbage\",\"trash\"],char:\"♻️\",fitzpatrick_scale:false,category:\"symbols\"},u6307:{keywords:[\"chinese\",\"point\",\"green-square\",\"kanji\"],char:\"🈯\",fitzpatrick_scale:false,category:\"symbols\"},chart:{keywords:[\"green-square\",\"graph\",\"presentation\",\"stats\"],char:\"💹\",fitzpatrick_scale:false,category:\"symbols\"},sparkle:{keywords:[\"stars\",\"green-square\",\"awesome\",\"good\",\"fireworks\"],char:\"❇️\",fitzpatrick_scale:false,category:\"symbols\"},eight_spoked_asterisk:{keywords:[\"star\",\"sparkle\",\"green-square\"],char:\"✳️\",fitzpatrick_scale:false,category:\"symbols\"},negative_squared_cross_mark:{keywords:[\"x\",\"green-square\",\"no\",\"deny\"],char:\"❎\",fitzpatrick_scale:false,category:\"symbols\"},white_check_mark:{keywords:[\"green-square\",\"ok\",\"agree\",\"vote\",\"election\",\"answer\",\"tick\"],char:\"✅\",fitzpatrick_scale:false,category:\"symbols\"},diamond_shape_with_a_dot_inside:{keywords:[\"jewel\",\"blue\",\"gem\",\"crystal\",\"fancy\"],char:\"💠\",fitzpatrick_scale:false,category:\"symbols\"},cyclone:{keywords:[\"weather\",\"swirl\",\"blue\",\"cloud\",\"vortex\",\"spiral\",\"whirlpool\",\"spin\",\"tornado\",\"hurricane\",\"typhoon\"],char:\"🌀\",fitzpatrick_scale:false,category:\"symbols\"},loop:{keywords:[\"tape\",\"cassette\"],char:\"➿\",fitzpatrick_scale:false,category:\"symbols\"},globe_with_meridians:{keywords:[\"earth\",\"international\",\"world\",\"internet\",\"interweb\",\"i18n\"],char:\"🌐\",fitzpatrick_scale:false,category:\"symbols\"},m:{keywords:[\"alphabet\",\"blue-circle\",\"letter\"],char:\"Ⓜ️\",fitzpatrick_scale:false,category:\"symbols\"},atm:{keywords:[\"money\",\"sales\",\"cash\",\"blue-square\",\"payment\",\"bank\"],char:\"🏧\",fitzpatrick_scale:false,category:\"symbols\"},sa:{keywords:[\"japanese\",\"blue-square\",\"katakana\"],char:\"🈂️\",fitzpatrick_scale:false,category:\"symbols\"},passport_control:{keywords:[\"custom\",\"blue-square\"],char:\"🛂\",fitzpatrick_scale:false,category:\"symbols\"},customs:{keywords:[\"passport\",\"border\",\"blue-square\"],char:\"🛃\",fitzpatrick_scale:false,category:\"symbols\"},baggage_claim:{keywords:[\"blue-square\",\"airport\",\"transport\"],char:\"🛄\",fitzpatrick_scale:false,category:\"symbols\"},left_luggage:{keywords:[\"blue-square\",\"travel\"],char:\"🛅\",fitzpatrick_scale:false,category:\"symbols\"},wheelchair:{keywords:[\"blue-square\",\"disabled\",\"a11y\",\"accessibility\"],char:\"♿\",fitzpatrick_scale:false,category:\"symbols\"},no_smoking:{keywords:[\"cigarette\",\"blue-square\",\"smell\",\"smoke\"],char:\"🚭\",fitzpatrick_scale:false,category:\"symbols\"},wc:{keywords:[\"toilet\",\"restroom\",\"blue-square\"],char:\"🚾\",fitzpatrick_scale:false,category:\"symbols\"},parking:{keywords:[\"cars\",\"blue-square\",\"alphabet\",\"letter\"],char:\"🅿️\",fitzpatrick_scale:false,category:\"symbols\"},potable_water:{keywords:[\"blue-square\",\"liquid\",\"restroom\",\"cleaning\",\"faucet\"],char:\"🚰\",fitzpatrick_scale:false,category:\"symbols\"},mens:{keywords:[\"toilet\",\"restroom\",\"wc\",\"blue-square\",\"gender\",\"male\"],char:\"🚹\",fitzpatrick_scale:false,category:\"symbols\"},womens:{keywords:[\"purple-square\",\"woman\",\"female\",\"toilet\",\"loo\",\"restroom\",\"gender\"],char:\"🚺\",fitzpatrick_scale:false,category:\"symbols\"},baby_symbol:{keywords:[\"orange-square\",\"child\"],char:\"🚼\",fitzpatrick_scale:false,category:\"symbols\"},restroom:{keywords:[\"blue-square\",\"toilet\",\"refresh\",\"wc\",\"gender\"],char:\"🚻\",fitzpatrick_scale:false,category:\"symbols\"},put_litter_in_its_place:{keywords:[\"blue-square\",\"sign\",\"human\",\"info\"],char:\"🚮\",fitzpatrick_scale:false,category:\"symbols\"},cinema:{keywords:[\"blue-square\",\"record\",\"film\",\"movie\",\"curtain\",\"stage\",\"theater\"],char:\"🎦\",fitzpatrick_scale:false,category:\"symbols\"},signal_strength:{keywords:[\"blue-square\",\"reception\",\"phone\",\"internet\",\"connection\",\"wifi\",\"bluetooth\",\"bars\"],char:\"📶\",fitzpatrick_scale:false,category:\"symbols\"},koko:{keywords:[\"blue-square\",\"here\",\"katakana\",\"japanese\",\"destination\"],char:\"🈁\",fitzpatrick_scale:false,category:\"symbols\"},ng:{keywords:[\"blue-square\",\"words\",\"shape\",\"icon\"],char:\"🆖\",fitzpatrick_scale:false,category:\"symbols\"},ok:{keywords:[\"good\",\"agree\",\"yes\",\"blue-square\"],char:\"🆗\",fitzpatrick_scale:false,category:\"symbols\"},up:{keywords:[\"blue-square\",\"above\",\"high\"],char:\"🆙\",fitzpatrick_scale:false,category:\"symbols\"},cool:{keywords:[\"words\",\"blue-square\"],char:\"🆒\",fitzpatrick_scale:false,category:\"symbols\"},new:{keywords:[\"blue-square\",\"words\",\"start\"],char:\"🆕\",fitzpatrick_scale:false,category:\"symbols\"},free:{keywords:[\"blue-square\",\"words\"],char:\"🆓\",fitzpatrick_scale:false,category:\"symbols\"},zero:{keywords:[\"0\",\"numbers\",\"blue-square\",\"null\"],char:\"0️⃣\",fitzpatrick_scale:false,category:\"symbols\"},one:{keywords:[\"blue-square\",\"numbers\",\"1\"],char:\"1️⃣\",fitzpatrick_scale:false,category:\"symbols\"},two:{keywords:[\"numbers\",\"2\",\"prime\",\"blue-square\"],char:\"2️⃣\",fitzpatrick_scale:false,category:\"symbols\"},three:{keywords:[\"3\",\"numbers\",\"prime\",\"blue-square\"],char:\"3️⃣\",fitzpatrick_scale:false,category:\"symbols\"},four:{keywords:[\"4\",\"numbers\",\"blue-square\"],char:\"4️⃣\",fitzpatrick_scale:false,category:\"symbols\"},five:{keywords:[\"5\",\"numbers\",\"blue-square\",\"prime\"],char:\"5️⃣\",fitzpatrick_scale:false,category:\"symbols\"},six:{keywords:[\"6\",\"numbers\",\"blue-square\"],char:\"6️⃣\",fitzpatrick_scale:false,category:\"symbols\"},seven:{keywords:[\"7\",\"numbers\",\"blue-square\",\"prime\"],char:\"7️⃣\",fitzpatrick_scale:false,category:\"symbols\"},eight:{keywords:[\"8\",\"blue-square\",\"numbers\"],char:\"8️⃣\",fitzpatrick_scale:false,category:\"symbols\"},nine:{keywords:[\"blue-square\",\"numbers\",\"9\"],char:\"9️⃣\",fitzpatrick_scale:false,category:\"symbols\"},keycap_ten:{keywords:[\"numbers\",\"10\",\"blue-square\"],char:\"🔟\",fitzpatrick_scale:false,category:\"symbols\"},asterisk:{keywords:[\"star\",\"keycap\"],char:\"*⃣\",fitzpatrick_scale:false,category:\"symbols\"},1234:{keywords:[\"numbers\",\"blue-square\"],char:\"🔢\",fitzpatrick_scale:false,category:\"symbols\"},eject_button:{keywords:[\"blue-square\"],char:\"⏏️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_forward:{keywords:[\"blue-square\",\"right\",\"direction\",\"play\"],char:\"▶️\",fitzpatrick_scale:false,category:\"symbols\"},pause_button:{keywords:[\"pause\",\"blue-square\"],char:\"⏸\",fitzpatrick_scale:false,category:\"symbols\"},next_track_button:{keywords:[\"forward\",\"next\",\"blue-square\"],char:\"⏭\",fitzpatrick_scale:false,category:\"symbols\"},stop_button:{keywords:[\"blue-square\"],char:\"⏹\",fitzpatrick_scale:false,category:\"symbols\"},record_button:{keywords:[\"blue-square\"],char:\"⏺\",fitzpatrick_scale:false,category:\"symbols\"},play_or_pause_button:{keywords:[\"blue-square\",\"play\",\"pause\"],char:\"⏯\",fitzpatrick_scale:false,category:\"symbols\"},previous_track_button:{keywords:[\"backward\"],char:\"⏮\",fitzpatrick_scale:false,category:\"symbols\"},fast_forward:{keywords:[\"blue-square\",\"play\",\"speed\",\"continue\"],char:\"⏩\",fitzpatrick_scale:false,category:\"symbols\"},rewind:{keywords:[\"play\",\"blue-square\"],char:\"⏪\",fitzpatrick_scale:false,category:\"symbols\"},twisted_rightwards_arrows:{keywords:[\"blue-square\",\"shuffle\",\"music\",\"random\"],char:\"🔀\",fitzpatrick_scale:false,category:\"symbols\"},repeat:{keywords:[\"loop\",\"record\"],char:\"🔁\",fitzpatrick_scale:false,category:\"symbols\"},repeat_one:{keywords:[\"blue-square\",\"loop\"],char:\"🔂\",fitzpatrick_scale:false,category:\"symbols\"},arrow_backward:{keywords:[\"blue-square\",\"left\",\"direction\"],char:\"◀️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_up_small:{keywords:[\"blue-square\",\"triangle\",\"direction\",\"point\",\"forward\",\"top\"],char:\"🔼\",fitzpatrick_scale:false,category:\"symbols\"},arrow_down_small:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"🔽\",fitzpatrick_scale:false,category:\"symbols\"},arrow_double_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:\"⏫\",fitzpatrick_scale:false,category:\"symbols\"},arrow_double_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"⏬\",fitzpatrick_scale:false,category:\"symbols\"},arrow_right:{keywords:[\"blue-square\",\"next\"],char:\"➡️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_left:{keywords:[\"blue-square\",\"previous\",\"back\"],char:\"⬅️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_up:{keywords:[\"blue-square\",\"continue\",\"top\",\"direction\"],char:\"⬆️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"⬇️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_right:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northeast\"],char:\"↗️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_right:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southeast\"],char:\"↘️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_lower_left:{keywords:[\"blue-square\",\"direction\",\"diagonal\",\"southwest\"],char:\"↙️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_upper_left:{keywords:[\"blue-square\",\"point\",\"direction\",\"diagonal\",\"northwest\"],char:\"↖️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_up_down:{keywords:[\"blue-square\",\"direction\",\"way\",\"vertical\"],char:\"↕️\",fitzpatrick_scale:false,category:\"symbols\"},left_right_arrow:{keywords:[\"shape\",\"direction\",\"horizontal\",\"sideways\"],char:\"↔️\",fitzpatrick_scale:false,category:\"symbols\"},arrows_counterclockwise:{keywords:[\"blue-square\",\"sync\",\"cycle\"],char:\"🔄\",fitzpatrick_scale:false,category:\"symbols\"},arrow_right_hook:{keywords:[\"blue-square\",\"return\",\"rotate\",\"direction\"],char:\"↪️\",fitzpatrick_scale:false,category:\"symbols\"},leftwards_arrow_with_hook:{keywords:[\"back\",\"return\",\"blue-square\",\"undo\",\"enter\"],char:\"↩️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_up:{keywords:[\"blue-square\",\"direction\",\"top\"],char:\"⤴️\",fitzpatrick_scale:false,category:\"symbols\"},arrow_heading_down:{keywords:[\"blue-square\",\"direction\",\"bottom\"],char:\"⤵️\",fitzpatrick_scale:false,category:\"symbols\"},hash:{keywords:[\"symbol\",\"blue-square\",\"twitter\"],char:\"#️⃣\",fitzpatrick_scale:false,category:\"symbols\"},information_source:{keywords:[\"blue-square\",\"alphabet\",\"letter\"],char:\"ℹ️\",fitzpatrick_scale:false,category:\"symbols\"},abc:{keywords:[\"blue-square\",\"alphabet\"],char:\"🔤\",fitzpatrick_scale:false,category:\"symbols\"},abcd:{keywords:[\"blue-square\",\"alphabet\"],char:\"🔡\",fitzpatrick_scale:false,category:\"symbols\"},capital_abcd:{keywords:[\"alphabet\",\"words\",\"blue-square\"],char:\"🔠\",fitzpatrick_scale:false,category:\"symbols\"},symbols:{keywords:[\"blue-square\",\"music\",\"note\",\"ampersand\",\"percent\",\"glyphs\",\"characters\"],char:\"🔣\",fitzpatrick_scale:false,category:\"symbols\"},musical_note:{keywords:[\"score\",\"tone\",\"sound\"],char:\"🎵\",fitzpatrick_scale:false,category:\"symbols\"},notes:{keywords:[\"music\",\"score\"],char:\"🎶\",fitzpatrick_scale:false,category:\"symbols\"},wavy_dash:{keywords:[\"draw\",\"line\",\"moustache\",\"mustache\",\"squiggle\",\"scribble\"],char:\"〰️\",fitzpatrick_scale:false,category:\"symbols\"},curly_loop:{keywords:[\"scribble\",\"draw\",\"shape\",\"squiggle\"],char:\"➰\",fitzpatrick_scale:false,category:\"symbols\"},heavy_check_mark:{keywords:[\"ok\",\"nike\",\"answer\",\"yes\",\"tick\"],char:\"✔️\",fitzpatrick_scale:false,category:\"symbols\"},arrows_clockwise:{keywords:[\"sync\",\"cycle\",\"round\",\"repeat\"],char:\"🔃\",fitzpatrick_scale:false,category:\"symbols\"},heavy_plus_sign:{keywords:[\"math\",\"calculation\",\"addition\",\"more\",\"increase\"],char:\"➕\",fitzpatrick_scale:false,category:\"symbols\"},heavy_minus_sign:{keywords:[\"math\",\"calculation\",\"subtract\",\"less\"],char:\"➖\",fitzpatrick_scale:false,category:\"symbols\"},heavy_division_sign:{keywords:[\"divide\",\"math\",\"calculation\"],char:\"➗\",fitzpatrick_scale:false,category:\"symbols\"},heavy_multiplication_x:{keywords:[\"math\",\"calculation\"],char:\"✖️\",fitzpatrick_scale:false,category:\"symbols\"},infinity:{keywords:[\"forever\"],char:\"♾\",fitzpatrick_scale:false,category:\"symbols\"},heavy_dollar_sign:{keywords:[\"money\",\"sales\",\"payment\",\"currency\",\"buck\"],char:\"💲\",fitzpatrick_scale:false,category:\"symbols\"},currency_exchange:{keywords:[\"money\",\"sales\",\"dollar\",\"travel\"],char:\"💱\",fitzpatrick_scale:false,category:\"symbols\"},copyright:{keywords:[\"ip\",\"license\",\"circle\",\"law\",\"legal\"],char:\"©️\",fitzpatrick_scale:false,category:\"symbols\"},registered:{keywords:[\"alphabet\",\"circle\"],char:\"®️\",fitzpatrick_scale:false,category:\"symbols\"},tm:{keywords:[\"trademark\",\"brand\",\"law\",\"legal\"],char:\"™️\",fitzpatrick_scale:false,category:\"symbols\"},end:{keywords:[\"words\",\"arrow\"],char:\"🔚\",fitzpatrick_scale:false,category:\"symbols\"},back:{keywords:[\"arrow\",\"words\",\"return\"],char:\"🔙\",fitzpatrick_scale:false,category:\"symbols\"},on:{keywords:[\"arrow\",\"words\"],char:\"🔛\",fitzpatrick_scale:false,category:\"symbols\"},top:{keywords:[\"words\",\"blue-square\"],char:\"🔝\",fitzpatrick_scale:false,category:\"symbols\"},soon:{keywords:[\"arrow\",\"words\"],char:\"🔜\",fitzpatrick_scale:false,category:\"symbols\"},ballot_box_with_check:{keywords:[\"ok\",\"agree\",\"confirm\",\"black-square\",\"vote\",\"election\",\"yes\",\"tick\"],char:\"☑️\",fitzpatrick_scale:false,category:\"symbols\"},radio_button:{keywords:[\"input\",\"old\",\"music\",\"circle\"],char:\"🔘\",fitzpatrick_scale:false,category:\"symbols\"},white_circle:{keywords:[\"shape\",\"round\"],char:\"⚪\",fitzpatrick_scale:false,category:\"symbols\"},black_circle:{keywords:[\"shape\",\"button\",\"round\"],char:\"⚫\",fitzpatrick_scale:false,category:\"symbols\"},red_circle:{keywords:[\"shape\",\"error\",\"danger\"],char:\"🔴\",fitzpatrick_scale:false,category:\"symbols\"},large_blue_circle:{keywords:[\"shape\",\"icon\",\"button\"],char:\"🔵\",fitzpatrick_scale:false,category:\"symbols\"},small_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔸\",fitzpatrick_scale:false,category:\"symbols\"},small_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔹\",fitzpatrick_scale:false,category:\"symbols\"},large_orange_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔶\",fitzpatrick_scale:false,category:\"symbols\"},large_blue_diamond:{keywords:[\"shape\",\"jewel\",\"gem\"],char:\"🔷\",fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle:{keywords:[\"shape\",\"direction\",\"up\",\"top\"],char:\"🔺\",fitzpatrick_scale:false,category:\"symbols\"},black_small_square:{keywords:[\"shape\",\"icon\"],char:\"▪️\",fitzpatrick_scale:false,category:\"symbols\"},white_small_square:{keywords:[\"shape\",\"icon\"],char:\"▫️\",fitzpatrick_scale:false,category:\"symbols\"},black_large_square:{keywords:[\"shape\",\"icon\",\"button\"],char:\"⬛\",fitzpatrick_scale:false,category:\"symbols\"},white_large_square:{keywords:[\"shape\",\"icon\",\"stone\",\"button\"],char:\"⬜\",fitzpatrick_scale:false,category:\"symbols\"},small_red_triangle_down:{keywords:[\"shape\",\"direction\",\"bottom\"],char:\"🔻\",fitzpatrick_scale:false,category:\"symbols\"},black_medium_square:{keywords:[\"shape\",\"button\",\"icon\"],char:\"◼️\",fitzpatrick_scale:false,category:\"symbols\"},white_medium_square:{keywords:[\"shape\",\"stone\",\"icon\"],char:\"◻️\",fitzpatrick_scale:false,category:\"symbols\"},black_medium_small_square:{keywords:[\"icon\",\"shape\",\"button\"],char:\"◾\",fitzpatrick_scale:false,category:\"symbols\"},white_medium_small_square:{keywords:[\"shape\",\"stone\",\"icon\",\"button\"],char:\"◽\",fitzpatrick_scale:false,category:\"symbols\"},black_square_button:{keywords:[\"shape\",\"input\",\"frame\"],char:\"🔲\",fitzpatrick_scale:false,category:\"symbols\"},white_square_button:{keywords:[\"shape\",\"input\"],char:\"🔳\",fitzpatrick_scale:false,category:\"symbols\"},speaker:{keywords:[\"sound\",\"volume\",\"silence\",\"broadcast\"],char:\"🔈\",fitzpatrick_scale:false,category:\"symbols\"},sound:{keywords:[\"volume\",\"speaker\",\"broadcast\"],char:\"🔉\",fitzpatrick_scale:false,category:\"symbols\"},loud_sound:{keywords:[\"volume\",\"noise\",\"noisy\",\"speaker\",\"broadcast\"],char:\"🔊\",fitzpatrick_scale:false,category:\"symbols\"},mute:{keywords:[\"sound\",\"volume\",\"silence\",\"quiet\"],char:\"🔇\",fitzpatrick_scale:false,category:\"symbols\"},mega:{keywords:[\"sound\",\"speaker\",\"volume\"],char:\"📣\",fitzpatrick_scale:false,category:\"symbols\"},loudspeaker:{keywords:[\"volume\",\"sound\"],char:\"📢\",fitzpatrick_scale:false,category:\"symbols\"},bell:{keywords:[\"sound\",\"notification\",\"christmas\",\"xmas\",\"chime\"],char:\"🔔\",fitzpatrick_scale:false,category:\"symbols\"},no_bell:{keywords:[\"sound\",\"volume\",\"mute\",\"quiet\",\"silent\"],char:\"🔕\",fitzpatrick_scale:false,category:\"symbols\"},black_joker:{keywords:[\"poker\",\"cards\",\"game\",\"play\",\"magic\"],char:\"🃏\",fitzpatrick_scale:false,category:\"symbols\"},mahjong:{keywords:[\"game\",\"play\",\"chinese\",\"kanji\"],char:\"🀄\",fitzpatrick_scale:false,category:\"symbols\"},spades:{keywords:[\"poker\",\"cards\",\"suits\",\"magic\"],char:\"♠️\",fitzpatrick_scale:false,category:\"symbols\"},clubs:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:\"♣️\",fitzpatrick_scale:false,category:\"symbols\"},hearts:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:\"♥️\",fitzpatrick_scale:false,category:\"symbols\"},diamonds:{keywords:[\"poker\",\"cards\",\"magic\",\"suits\"],char:\"♦️\",fitzpatrick_scale:false,category:\"symbols\"},flower_playing_cards:{keywords:[\"game\",\"sunset\",\"red\"],char:\"🎴\",fitzpatrick_scale:false,category:\"symbols\"},thought_balloon:{keywords:[\"bubble\",\"cloud\",\"speech\",\"thinking\",\"dream\"],char:\"💭\",fitzpatrick_scale:false,category:\"symbols\"},right_anger_bubble:{keywords:[\"caption\",\"speech\",\"thinking\",\"mad\"],char:\"🗯\",fitzpatrick_scale:false,category:\"symbols\"},speech_balloon:{keywords:[\"bubble\",\"words\",\"message\",\"talk\",\"chatting\"],char:\"💬\",fitzpatrick_scale:false,category:\"symbols\"},left_speech_bubble:{keywords:[\"words\",\"message\",\"talk\",\"chatting\"],char:\"🗨\",fitzpatrick_scale:false,category:\"symbols\"},clock1:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕐\",fitzpatrick_scale:false,category:\"symbols\"},clock2:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕑\",fitzpatrick_scale:false,category:\"symbols\"},clock3:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕒\",fitzpatrick_scale:false,category:\"symbols\"},clock4:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕓\",fitzpatrick_scale:false,category:\"symbols\"},clock5:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕔\",fitzpatrick_scale:false,category:\"symbols\"},clock6:{keywords:[\"time\",\"late\",\"early\",\"schedule\",\"dawn\",\"dusk\"],char:\"🕕\",fitzpatrick_scale:false,category:\"symbols\"},clock7:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕖\",fitzpatrick_scale:false,category:\"symbols\"},clock8:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕗\",fitzpatrick_scale:false,category:\"symbols\"},clock9:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕘\",fitzpatrick_scale:false,category:\"symbols\"},clock10:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕙\",fitzpatrick_scale:false,category:\"symbols\"},clock11:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕚\",fitzpatrick_scale:false,category:\"symbols\"},clock12:{keywords:[\"time\",\"noon\",\"midnight\",\"midday\",\"late\",\"early\",\"schedule\"],char:\"🕛\",fitzpatrick_scale:false,category:\"symbols\"},clock130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕜\",fitzpatrick_scale:false,category:\"symbols\"},clock230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕝\",fitzpatrick_scale:false,category:\"symbols\"},clock330:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕞\",fitzpatrick_scale:false,category:\"symbols\"},clock430:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕟\",fitzpatrick_scale:false,category:\"symbols\"},clock530:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕠\",fitzpatrick_scale:false,category:\"symbols\"},clock630:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕡\",fitzpatrick_scale:false,category:\"symbols\"},clock730:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕢\",fitzpatrick_scale:false,category:\"symbols\"},clock830:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕣\",fitzpatrick_scale:false,category:\"symbols\"},clock930:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕤\",fitzpatrick_scale:false,category:\"symbols\"},clock1030:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕥\",fitzpatrick_scale:false,category:\"symbols\"},clock1130:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕦\",fitzpatrick_scale:false,category:\"symbols\"},clock1230:{keywords:[\"time\",\"late\",\"early\",\"schedule\"],char:\"🕧\",fitzpatrick_scale:false,category:\"symbols\"},afghanistan:{keywords:[\"af\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇫\",fitzpatrick_scale:false,category:\"flags\"},aland_islands:{keywords:[\"Åland\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇽\",fitzpatrick_scale:false,category:\"flags\"},albania:{keywords:[\"al\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇱\",fitzpatrick_scale:false,category:\"flags\"},algeria:{keywords:[\"dz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇿\",fitzpatrick_scale:false,category:\"flags\"},american_samoa:{keywords:[\"american\",\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇸\",fitzpatrick_scale:false,category:\"flags\"},andorra:{keywords:[\"ad\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇩\",fitzpatrick_scale:false,category:\"flags\"},angola:{keywords:[\"ao\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇴\",fitzpatrick_scale:false,category:\"flags\"},anguilla:{keywords:[\"ai\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇮\",fitzpatrick_scale:false,category:\"flags\"},antarctica:{keywords:[\"aq\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇶\",fitzpatrick_scale:false,category:\"flags\"},antigua_barbuda:{keywords:[\"antigua\",\"barbuda\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇬\",fitzpatrick_scale:false,category:\"flags\"},argentina:{keywords:[\"ar\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇷\",fitzpatrick_scale:false,category:\"flags\"},armenia:{keywords:[\"am\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇲\",fitzpatrick_scale:false,category:\"flags\"},aruba:{keywords:[\"aw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇼\",fitzpatrick_scale:false,category:\"flags\"},australia:{keywords:[\"au\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇺\",fitzpatrick_scale:false,category:\"flags\"},austria:{keywords:[\"at\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇹\",fitzpatrick_scale:false,category:\"flags\"},azerbaijan:{keywords:[\"az\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇿\",fitzpatrick_scale:false,category:\"flags\"},bahamas:{keywords:[\"bs\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇸\",fitzpatrick_scale:false,category:\"flags\"},bahrain:{keywords:[\"bh\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇭\",fitzpatrick_scale:false,category:\"flags\"},bangladesh:{keywords:[\"bd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇩\",fitzpatrick_scale:false,category:\"flags\"},barbados:{keywords:[\"bb\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇧\",fitzpatrick_scale:false,category:\"flags\"},belarus:{keywords:[\"by\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇾\",fitzpatrick_scale:false,category:\"flags\"},belgium:{keywords:[\"be\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇪\",fitzpatrick_scale:false,category:\"flags\"},belize:{keywords:[\"bz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇿\",fitzpatrick_scale:false,category:\"flags\"},benin:{keywords:[\"bj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇯\",fitzpatrick_scale:false,category:\"flags\"},bermuda:{keywords:[\"bm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇲\",fitzpatrick_scale:false,category:\"flags\"},bhutan:{keywords:[\"bt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇹\",fitzpatrick_scale:false,category:\"flags\"},bolivia:{keywords:[\"bo\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇴\",fitzpatrick_scale:false,category:\"flags\"},caribbean_netherlands:{keywords:[\"bonaire\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇶\",fitzpatrick_scale:false,category:\"flags\"},bosnia_herzegovina:{keywords:[\"bosnia\",\"herzegovina\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇦\",fitzpatrick_scale:false,category:\"flags\"},botswana:{keywords:[\"bw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇼\",fitzpatrick_scale:false,category:\"flags\"},brazil:{keywords:[\"br\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇷\",fitzpatrick_scale:false,category:\"flags\"},british_indian_ocean_territory:{keywords:[\"british\",\"indian\",\"ocean\",\"territory\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇴\",fitzpatrick_scale:false,category:\"flags\"},british_virgin_islands:{keywords:[\"british\",\"virgin\",\"islands\",\"bvi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇬\",fitzpatrick_scale:false,category:\"flags\"},brunei:{keywords:[\"bn\",\"darussalam\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇳\",fitzpatrick_scale:false,category:\"flags\"},bulgaria:{keywords:[\"bg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇬\",fitzpatrick_scale:false,category:\"flags\"},burkina_faso:{keywords:[\"burkina\",\"faso\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇫\",fitzpatrick_scale:false,category:\"flags\"},burundi:{keywords:[\"bi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇮\",fitzpatrick_scale:false,category:\"flags\"},cape_verde:{keywords:[\"cabo\",\"verde\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇻\",fitzpatrick_scale:false,category:\"flags\"},cambodia:{keywords:[\"kh\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇭\",fitzpatrick_scale:false,category:\"flags\"},cameroon:{keywords:[\"cm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇲\",fitzpatrick_scale:false,category:\"flags\"},canada:{keywords:[\"ca\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇦\",fitzpatrick_scale:false,category:\"flags\"},canary_islands:{keywords:[\"canary\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇨\",fitzpatrick_scale:false,category:\"flags\"},cayman_islands:{keywords:[\"cayman\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇾\",fitzpatrick_scale:false,category:\"flags\"},central_african_republic:{keywords:[\"central\",\"african\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇫\",fitzpatrick_scale:false,category:\"flags\"},chad:{keywords:[\"td\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇩\",fitzpatrick_scale:false,category:\"flags\"},chile:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇱\",fitzpatrick_scale:false,category:\"flags\"},cn:{keywords:[\"china\",\"chinese\",\"prc\",\"flag\",\"country\",\"nation\",\"banner\"],char:\"🇨🇳\",fitzpatrick_scale:false,category:\"flags\"},christmas_island:{keywords:[\"christmas\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇽\",fitzpatrick_scale:false,category:\"flags\"},cocos_islands:{keywords:[\"cocos\",\"keeling\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇨\",fitzpatrick_scale:false,category:\"flags\"},colombia:{keywords:[\"co\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇴\",fitzpatrick_scale:false,category:\"flags\"},comoros:{keywords:[\"km\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇲\",fitzpatrick_scale:false,category:\"flags\"},congo_brazzaville:{keywords:[\"congo\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇬\",fitzpatrick_scale:false,category:\"flags\"},congo_kinshasa:{keywords:[\"congo\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇩\",fitzpatrick_scale:false,category:\"flags\"},cook_islands:{keywords:[\"cook\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇰\",fitzpatrick_scale:false,category:\"flags\"},costa_rica:{keywords:[\"costa\",\"rica\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇷\",fitzpatrick_scale:false,category:\"flags\"},croatia:{keywords:[\"hr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇷\",fitzpatrick_scale:false,category:\"flags\"},cuba:{keywords:[\"cu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇺\",fitzpatrick_scale:false,category:\"flags\"},curacao:{keywords:[\"curaçao\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇼\",fitzpatrick_scale:false,category:\"flags\"},cyprus:{keywords:[\"cy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇾\",fitzpatrick_scale:false,category:\"flags\"},czech_republic:{keywords:[\"cz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇿\",fitzpatrick_scale:false,category:\"flags\"},denmark:{keywords:[\"dk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇰\",fitzpatrick_scale:false,category:\"flags\"},djibouti:{keywords:[\"dj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇯\",fitzpatrick_scale:false,category:\"flags\"},dominica:{keywords:[\"dm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇲\",fitzpatrick_scale:false,category:\"flags\"},dominican_republic:{keywords:[\"dominican\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇩🇴\",fitzpatrick_scale:false,category:\"flags\"},ecuador:{keywords:[\"ec\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇨\",fitzpatrick_scale:false,category:\"flags\"},egypt:{keywords:[\"eg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇬\",fitzpatrick_scale:false,category:\"flags\"},el_salvador:{keywords:[\"el\",\"salvador\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇻\",fitzpatrick_scale:false,category:\"flags\"},equatorial_guinea:{keywords:[\"equatorial\",\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇶\",fitzpatrick_scale:false,category:\"flags\"},eritrea:{keywords:[\"er\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇷\",fitzpatrick_scale:false,category:\"flags\"},estonia:{keywords:[\"ee\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇪\",fitzpatrick_scale:false,category:\"flags\"},ethiopia:{keywords:[\"et\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇹\",fitzpatrick_scale:false,category:\"flags\"},eu:{keywords:[\"european\",\"union\",\"flag\",\"banner\"],char:\"🇪🇺\",fitzpatrick_scale:false,category:\"flags\"},falkland_islands:{keywords:[\"falkland\",\"islands\",\"malvinas\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇰\",fitzpatrick_scale:false,category:\"flags\"},faroe_islands:{keywords:[\"faroe\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇴\",fitzpatrick_scale:false,category:\"flags\"},fiji:{keywords:[\"fj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇯\",fitzpatrick_scale:false,category:\"flags\"},finland:{keywords:[\"fi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇮\",fitzpatrick_scale:false,category:\"flags\"},fr:{keywords:[\"banner\",\"flag\",\"nation\",\"france\",\"french\",\"country\"],char:\"🇫🇷\",fitzpatrick_scale:false,category:\"flags\"},french_guiana:{keywords:[\"french\",\"guiana\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇫\",fitzpatrick_scale:false,category:\"flags\"},french_polynesia:{keywords:[\"french\",\"polynesia\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇫\",fitzpatrick_scale:false,category:\"flags\"},french_southern_territories:{keywords:[\"french\",\"southern\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇫\",fitzpatrick_scale:false,category:\"flags\"},gabon:{keywords:[\"ga\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇦\",fitzpatrick_scale:false,category:\"flags\"},gambia:{keywords:[\"gm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇲\",fitzpatrick_scale:false,category:\"flags\"},georgia:{keywords:[\"ge\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇪\",fitzpatrick_scale:false,category:\"flags\"},de:{keywords:[\"german\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇩🇪\",fitzpatrick_scale:false,category:\"flags\"},ghana:{keywords:[\"gh\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇭\",fitzpatrick_scale:false,category:\"flags\"},gibraltar:{keywords:[\"gi\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇮\",fitzpatrick_scale:false,category:\"flags\"},greece:{keywords:[\"gr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇷\",fitzpatrick_scale:false,category:\"flags\"},greenland:{keywords:[\"gl\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇱\",fitzpatrick_scale:false,category:\"flags\"},grenada:{keywords:[\"gd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇩\",fitzpatrick_scale:false,category:\"flags\"},guadeloupe:{keywords:[\"gp\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇵\",fitzpatrick_scale:false,category:\"flags\"},guam:{keywords:[\"gu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇺\",fitzpatrick_scale:false,category:\"flags\"},guatemala:{keywords:[\"gt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇹\",fitzpatrick_scale:false,category:\"flags\"},guernsey:{keywords:[\"gg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇬\",fitzpatrick_scale:false,category:\"flags\"},guinea:{keywords:[\"gn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇳\",fitzpatrick_scale:false,category:\"flags\"},guinea_bissau:{keywords:[\"gw\",\"bissau\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇼\",fitzpatrick_scale:false,category:\"flags\"},guyana:{keywords:[\"gy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇾\",fitzpatrick_scale:false,category:\"flags\"},haiti:{keywords:[\"ht\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇹\",fitzpatrick_scale:false,category:\"flags\"},honduras:{keywords:[\"hn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇳\",fitzpatrick_scale:false,category:\"flags\"},hong_kong:{keywords:[\"hong\",\"kong\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇰\",fitzpatrick_scale:false,category:\"flags\"},hungary:{keywords:[\"hu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇭🇺\",fitzpatrick_scale:false,category:\"flags\"},iceland:{keywords:[\"is\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇸\",fitzpatrick_scale:false,category:\"flags\"},india:{keywords:[\"in\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇳\",fitzpatrick_scale:false,category:\"flags\"},indonesia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇩\",fitzpatrick_scale:false,category:\"flags\"},iran:{keywords:[\"iran,\",\"islamic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇷\",fitzpatrick_scale:false,category:\"flags\"},iraq:{keywords:[\"iq\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇶\",fitzpatrick_scale:false,category:\"flags\"},ireland:{keywords:[\"ie\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇪\",fitzpatrick_scale:false,category:\"flags\"},isle_of_man:{keywords:[\"isle\",\"man\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇲\",fitzpatrick_scale:false,category:\"flags\"},israel:{keywords:[\"il\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇱\",fitzpatrick_scale:false,category:\"flags\"},it:{keywords:[\"italy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇮🇹\",fitzpatrick_scale:false,category:\"flags\"},cote_divoire:{keywords:[\"ivory\",\"coast\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇮\",fitzpatrick_scale:false,category:\"flags\"},jamaica:{keywords:[\"jm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇯🇲\",fitzpatrick_scale:false,category:\"flags\"},jp:{keywords:[\"japanese\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇯🇵\",fitzpatrick_scale:false,category:\"flags\"},jersey:{keywords:[\"je\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇯🇪\",fitzpatrick_scale:false,category:\"flags\"},jordan:{keywords:[\"jo\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇯🇴\",fitzpatrick_scale:false,category:\"flags\"},kazakhstan:{keywords:[\"kz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇿\",fitzpatrick_scale:false,category:\"flags\"},kenya:{keywords:[\"ke\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇪\",fitzpatrick_scale:false,category:\"flags\"},kiribati:{keywords:[\"ki\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇮\",fitzpatrick_scale:false,category:\"flags\"},kosovo:{keywords:[\"xk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇽🇰\",fitzpatrick_scale:false,category:\"flags\"},kuwait:{keywords:[\"kw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇼\",fitzpatrick_scale:false,category:\"flags\"},kyrgyzstan:{keywords:[\"kg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇬\",fitzpatrick_scale:false,category:\"flags\"},laos:{keywords:[\"lao\",\"democratic\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇦\",fitzpatrick_scale:false,category:\"flags\"},latvia:{keywords:[\"lv\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇻\",fitzpatrick_scale:false,category:\"flags\"},lebanon:{keywords:[\"lb\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇧\",fitzpatrick_scale:false,category:\"flags\"},lesotho:{keywords:[\"ls\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇸\",fitzpatrick_scale:false,category:\"flags\"},liberia:{keywords:[\"lr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇷\",fitzpatrick_scale:false,category:\"flags\"},libya:{keywords:[\"ly\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇾\",fitzpatrick_scale:false,category:\"flags\"},liechtenstein:{keywords:[\"li\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇮\",fitzpatrick_scale:false,category:\"flags\"},lithuania:{keywords:[\"lt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇹\",fitzpatrick_scale:false,category:\"flags\"},luxembourg:{keywords:[\"lu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇺\",fitzpatrick_scale:false,category:\"flags\"},macau:{keywords:[\"macao\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇴\",fitzpatrick_scale:false,category:\"flags\"},macedonia:{keywords:[\"macedonia,\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇰\",fitzpatrick_scale:false,category:\"flags\"},madagascar:{keywords:[\"mg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇬\",fitzpatrick_scale:false,category:\"flags\"},malawi:{keywords:[\"mw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇼\",fitzpatrick_scale:false,category:\"flags\"},malaysia:{keywords:[\"my\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇾\",fitzpatrick_scale:false,category:\"flags\"},maldives:{keywords:[\"mv\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇻\",fitzpatrick_scale:false,category:\"flags\"},mali:{keywords:[\"ml\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇱\",fitzpatrick_scale:false,category:\"flags\"},malta:{keywords:[\"mt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇹\",fitzpatrick_scale:false,category:\"flags\"},marshall_islands:{keywords:[\"marshall\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇭\",fitzpatrick_scale:false,category:\"flags\"},martinique:{keywords:[\"mq\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇶\",fitzpatrick_scale:false,category:\"flags\"},mauritania:{keywords:[\"mr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇷\",fitzpatrick_scale:false,category:\"flags\"},mauritius:{keywords:[\"mu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇺\",fitzpatrick_scale:false,category:\"flags\"},mayotte:{keywords:[\"yt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇾🇹\",fitzpatrick_scale:false,category:\"flags\"},mexico:{keywords:[\"mx\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇽\",fitzpatrick_scale:false,category:\"flags\"},micronesia:{keywords:[\"micronesia,\",\"federated\",\"states\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇫🇲\",fitzpatrick_scale:false,category:\"flags\"},moldova:{keywords:[\"moldova,\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇩\",fitzpatrick_scale:false,category:\"flags\"},monaco:{keywords:[\"mc\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇨\",fitzpatrick_scale:false,category:\"flags\"},mongolia:{keywords:[\"mn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇳\",fitzpatrick_scale:false,category:\"flags\"},montenegro:{keywords:[\"me\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇪\",fitzpatrick_scale:false,category:\"flags\"},montserrat:{keywords:[\"ms\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇸\",fitzpatrick_scale:false,category:\"flags\"},morocco:{keywords:[\"ma\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇦\",fitzpatrick_scale:false,category:\"flags\"},mozambique:{keywords:[\"mz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇿\",fitzpatrick_scale:false,category:\"flags\"},myanmar:{keywords:[\"mm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇲\",fitzpatrick_scale:false,category:\"flags\"},namibia:{keywords:[\"na\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇦\",fitzpatrick_scale:false,category:\"flags\"},nauru:{keywords:[\"nr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇷\",fitzpatrick_scale:false,category:\"flags\"},nepal:{keywords:[\"np\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇵\",fitzpatrick_scale:false,category:\"flags\"},netherlands:{keywords:[\"nl\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇱\",fitzpatrick_scale:false,category:\"flags\"},new_caledonia:{keywords:[\"new\",\"caledonia\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇨\",fitzpatrick_scale:false,category:\"flags\"},new_zealand:{keywords:[\"new\",\"zealand\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇿\",fitzpatrick_scale:false,category:\"flags\"},nicaragua:{keywords:[\"ni\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇮\",fitzpatrick_scale:false,category:\"flags\"},niger:{keywords:[\"ne\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇪\",fitzpatrick_scale:false,category:\"flags\"},nigeria:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇬\",fitzpatrick_scale:false,category:\"flags\"},niue:{keywords:[\"nu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇺\",fitzpatrick_scale:false,category:\"flags\"},norfolk_island:{keywords:[\"norfolk\",\"island\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇫\",fitzpatrick_scale:false,category:\"flags\"},northern_mariana_islands:{keywords:[\"northern\",\"mariana\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇲🇵\",fitzpatrick_scale:false,category:\"flags\"},north_korea:{keywords:[\"north\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇰🇵\",fitzpatrick_scale:false,category:\"flags\"},norway:{keywords:[\"no\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇳🇴\",fitzpatrick_scale:false,category:\"flags\"},oman:{keywords:[\"om_symbol\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇴🇲\",fitzpatrick_scale:false,category:\"flags\"},pakistan:{keywords:[\"pk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇰\",fitzpatrick_scale:false,category:\"flags\"},palau:{keywords:[\"pw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇼\",fitzpatrick_scale:false,category:\"flags\"},palestinian_territories:{keywords:[\"palestine\",\"palestinian\",\"territories\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇸\",fitzpatrick_scale:false,category:\"flags\"},panama:{keywords:[\"pa\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇦\",fitzpatrick_scale:false,category:\"flags\"},papua_new_guinea:{keywords:[\"papua\",\"new\",\"guinea\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇬\",fitzpatrick_scale:false,category:\"flags\"},paraguay:{keywords:[\"py\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇾\",fitzpatrick_scale:false,category:\"flags\"},peru:{keywords:[\"pe\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇪\",fitzpatrick_scale:false,category:\"flags\"},philippines:{keywords:[\"ph\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇭\",fitzpatrick_scale:false,category:\"flags\"},pitcairn_islands:{keywords:[\"pitcairn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇳\",fitzpatrick_scale:false,category:\"flags\"},poland:{keywords:[\"pl\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇱\",fitzpatrick_scale:false,category:\"flags\"},portugal:{keywords:[\"pt\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇹\",fitzpatrick_scale:false,category:\"flags\"},puerto_rico:{keywords:[\"puerto\",\"rico\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇷\",fitzpatrick_scale:false,category:\"flags\"},qatar:{keywords:[\"qa\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇶🇦\",fitzpatrick_scale:false,category:\"flags\"},reunion:{keywords:[\"réunion\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇪\",fitzpatrick_scale:false,category:\"flags\"},romania:{keywords:[\"ro\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇴\",fitzpatrick_scale:false,category:\"flags\"},ru:{keywords:[\"russian\",\"federation\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇺\",fitzpatrick_scale:false,category:\"flags\"},rwanda:{keywords:[\"rw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇼\",fitzpatrick_scale:false,category:\"flags\"},st_barthelemy:{keywords:[\"saint\",\"barthélemy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇧🇱\",fitzpatrick_scale:false,category:\"flags\"},st_helena:{keywords:[\"saint\",\"helena\",\"ascension\",\"tristan\",\"cunha\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇭\",fitzpatrick_scale:false,category:\"flags\"},st_kitts_nevis:{keywords:[\"saint\",\"kitts\",\"nevis\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇰🇳\",fitzpatrick_scale:false,category:\"flags\"},st_lucia:{keywords:[\"saint\",\"lucia\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇨\",fitzpatrick_scale:false,category:\"flags\"},st_pierre_miquelon:{keywords:[\"saint\",\"pierre\",\"miquelon\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇵🇲\",fitzpatrick_scale:false,category:\"flags\"},st_vincent_grenadines:{keywords:[\"saint\",\"vincent\",\"grenadines\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇨\",fitzpatrick_scale:false,category:\"flags\"},samoa:{keywords:[\"ws\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇼🇸\",fitzpatrick_scale:false,category:\"flags\"},san_marino:{keywords:[\"san\",\"marino\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇲\",fitzpatrick_scale:false,category:\"flags\"},sao_tome_principe:{keywords:[\"sao\",\"tome\",\"principe\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇹\",fitzpatrick_scale:false,category:\"flags\"},saudi_arabia:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇦\",fitzpatrick_scale:false,category:\"flags\"},senegal:{keywords:[\"sn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇳\",fitzpatrick_scale:false,category:\"flags\"},serbia:{keywords:[\"rs\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇷🇸\",fitzpatrick_scale:false,category:\"flags\"},seychelles:{keywords:[\"sc\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇨\",fitzpatrick_scale:false,category:\"flags\"},sierra_leone:{keywords:[\"sierra\",\"leone\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇱\",fitzpatrick_scale:false,category:\"flags\"},singapore:{keywords:[\"sg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇬\",fitzpatrick_scale:false,category:\"flags\"},sint_maarten:{keywords:[\"sint\",\"maarten\",\"dutch\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇽\",fitzpatrick_scale:false,category:\"flags\"},slovakia:{keywords:[\"sk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇰\",fitzpatrick_scale:false,category:\"flags\"},slovenia:{keywords:[\"si\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇮\",fitzpatrick_scale:false,category:\"flags\"},solomon_islands:{keywords:[\"solomon\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇧\",fitzpatrick_scale:false,category:\"flags\"},somalia:{keywords:[\"so\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇴\",fitzpatrick_scale:false,category:\"flags\"},south_africa:{keywords:[\"south\",\"africa\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇿🇦\",fitzpatrick_scale:false,category:\"flags\"},south_georgia_south_sandwich_islands:{keywords:[\"south\",\"georgia\",\"sandwich\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇬🇸\",fitzpatrick_scale:false,category:\"flags\"},kr:{keywords:[\"south\",\"korea\",\"nation\",\"flag\",\"country\",\"banner\"],char:\"🇰🇷\",fitzpatrick_scale:false,category:\"flags\"},south_sudan:{keywords:[\"south\",\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇸\",fitzpatrick_scale:false,category:\"flags\"},es:{keywords:[\"spain\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇸\",fitzpatrick_scale:false,category:\"flags\"},sri_lanka:{keywords:[\"sri\",\"lanka\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇱🇰\",fitzpatrick_scale:false,category:\"flags\"},sudan:{keywords:[\"sd\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇩\",fitzpatrick_scale:false,category:\"flags\"},suriname:{keywords:[\"sr\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇷\",fitzpatrick_scale:false,category:\"flags\"},swaziland:{keywords:[\"sz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇿\",fitzpatrick_scale:false,category:\"flags\"},sweden:{keywords:[\"se\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇪\",fitzpatrick_scale:false,category:\"flags\"},switzerland:{keywords:[\"ch\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇨🇭\",fitzpatrick_scale:false,category:\"flags\"},syria:{keywords:[\"syrian\",\"arab\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇸🇾\",fitzpatrick_scale:false,category:\"flags\"},taiwan:{keywords:[\"tw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇼\",fitzpatrick_scale:false,category:\"flags\"},tajikistan:{keywords:[\"tj\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇯\",fitzpatrick_scale:false,category:\"flags\"},tanzania:{keywords:[\"tanzania,\",\"united\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇿\",fitzpatrick_scale:false,category:\"flags\"},thailand:{keywords:[\"th\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇭\",fitzpatrick_scale:false,category:\"flags\"},timor_leste:{keywords:[\"timor\",\"leste\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇱\",fitzpatrick_scale:false,category:\"flags\"},togo:{keywords:[\"tg\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇬\",fitzpatrick_scale:false,category:\"flags\"},tokelau:{keywords:[\"tk\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇰\",fitzpatrick_scale:false,category:\"flags\"},tonga:{keywords:[\"to\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇴\",fitzpatrick_scale:false,category:\"flags\"},trinidad_tobago:{keywords:[\"trinidad\",\"tobago\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇹\",fitzpatrick_scale:false,category:\"flags\"},tunisia:{keywords:[\"tn\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇳\",fitzpatrick_scale:false,category:\"flags\"},tr:{keywords:[\"turkey\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇷\",fitzpatrick_scale:false,category:\"flags\"},turkmenistan:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇲\",fitzpatrick_scale:false,category:\"flags\"},turks_caicos_islands:{keywords:[\"turks\",\"caicos\",\"islands\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇨\",fitzpatrick_scale:false,category:\"flags\"},tuvalu:{keywords:[\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇹🇻\",fitzpatrick_scale:false,category:\"flags\"},uganda:{keywords:[\"ug\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇬\",fitzpatrick_scale:false,category:\"flags\"},ukraine:{keywords:[\"ua\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇦\",fitzpatrick_scale:false,category:\"flags\"},united_arab_emirates:{keywords:[\"united\",\"arab\",\"emirates\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇦🇪\",fitzpatrick_scale:false,category:\"flags\"},uk:{keywords:[\"united\",\"kingdom\",\"great\",\"britain\",\"northern\",\"ireland\",\"flag\",\"nation\",\"country\",\"banner\",\"british\",\"UK\",\"english\",\"england\",\"union jack\"],char:\"🇬🇧\",fitzpatrick_scale:false,category:\"flags\"},england:{keywords:[\"flag\",\"english\"],char:\"🏴󠁧󠁢󠁥󠁮󠁧󠁿\",fitzpatrick_scale:false,category:\"flags\"},scotland:{keywords:[\"flag\",\"scottish\"],char:\"🏴󠁧󠁢󠁳󠁣󠁴󠁿\",fitzpatrick_scale:false,category:\"flags\"},wales:{keywords:[\"flag\",\"welsh\"],char:\"🏴󠁧󠁢󠁷󠁬󠁳󠁿\",fitzpatrick_scale:false,category:\"flags\"},us:{keywords:[\"united\",\"states\",\"america\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇸\",fitzpatrick_scale:false,category:\"flags\"},us_virgin_islands:{keywords:[\"virgin\",\"islands\",\"us\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇮\",fitzpatrick_scale:false,category:\"flags\"},uruguay:{keywords:[\"uy\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇾\",fitzpatrick_scale:false,category:\"flags\"},uzbekistan:{keywords:[\"uz\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇺🇿\",fitzpatrick_scale:false,category:\"flags\"},vanuatu:{keywords:[\"vu\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇺\",fitzpatrick_scale:false,category:\"flags\"},vatican_city:{keywords:[\"vatican\",\"city\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇦\",fitzpatrick_scale:false,category:\"flags\"},venezuela:{keywords:[\"ve\",\"bolivarian\",\"republic\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇪\",fitzpatrick_scale:false,category:\"flags\"},vietnam:{keywords:[\"viet\",\"nam\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇻🇳\",fitzpatrick_scale:false,category:\"flags\"},wallis_futuna:{keywords:[\"wallis\",\"futuna\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇼🇫\",fitzpatrick_scale:false,category:\"flags\"},western_sahara:{keywords:[\"western\",\"sahara\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇪🇭\",fitzpatrick_scale:false,category:\"flags\"},yemen:{keywords:[\"ye\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇾🇪\",fitzpatrick_scale:false,category:\"flags\"},zambia:{keywords:[\"zm\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇿🇲\",fitzpatrick_scale:false,category:\"flags\"},zimbabwe:{keywords:[\"zw\",\"flag\",\"nation\",\"country\",\"banner\"],char:\"🇿🇼\",fitzpatrick_scale:false,category:\"flags\"},united_nations:{keywords:[\"un\",\"flag\",\"banner\"],char:\"🇺🇳\",fitzpatrick_scale:false,category:\"flags\"},pirate_flag:{keywords:[\"skull\",\"crossbones\",\"flag\",\"banner\"],char:\"🏴‍☠️\",fitzpatrick_scale:false,category:\"flags\"}});"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/emoticons/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const eq = t => a => t === a;\n    const isNull = eq(null);\n    const isUndefined = eq(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n\n    const noop = () => {\n    };\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const never = constant(false);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const exists = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const map$1 = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$1 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const last = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        cancel();\n        timer = setTimeout(() => {\n          timer = null;\n          fn.apply(null, args);\n        }, rate);\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    const insertEmoticon = (editor, ch) => {\n      editor.insertContent(ch);\n    };\n\n    const keys = Object.keys;\n    const hasOwnProperty = Object.hasOwnProperty;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const map = (obj, f) => {\n      return tupleMap(obj, (x, i) => ({\n        k: i,\n        v: f(x, i)\n      }));\n    };\n    const tupleMap = (obj, f) => {\n      const r = {};\n      each(obj, (x, i) => {\n        const tuple = f(x, i);\n        r[tuple.k] = tuple.v;\n      });\n      return r;\n    };\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n\n    const shallow = (old, nu) => {\n      return nu;\n    };\n    const baseMerge = merger => {\n      return (...objects) => {\n        if (objects.length === 0) {\n          throw new Error(`Can't merge zero objects`);\n        }\n        const ret = {};\n        for (let j = 0; j < objects.length; j++) {\n          const curObject = objects[j];\n          for (const key in curObject) {\n            if (has(curObject, key)) {\n              ret[key] = merger(ret[key], curObject[key]);\n            }\n          }\n        }\n        return ret;\n      };\n    };\n    const merge = baseMerge(shallow);\n\n    const singleton = doRevoke => {\n      const subject = Cell(Optional.none());\n      const revoke = () => subject.get().each(doRevoke);\n      const clear = () => {\n        revoke();\n        subject.set(Optional.none());\n      };\n      const isSet = () => subject.get().isSome();\n      const get = () => subject.get();\n      const set = s => {\n        revoke();\n        subject.set(Optional.some(s));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const value = () => {\n      const subject = singleton(noop);\n      const on = f => subject.get().each(f);\n      return {\n        ...subject,\n        on\n      };\n    };\n\n    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;\n    const contains = (str, substr, start = 0, end) => {\n      const idx = str.indexOf(substr, start);\n      if (idx !== -1) {\n        return isUndefined(end) ? true : idx + substr.length <= end;\n      } else {\n        return false;\n      }\n    };\n    const startsWith = (str, prefix) => {\n      return checkRange(str, prefix, 0);\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.Resource');\n\n    const DEFAULT_ID = 'tinymce.plugins.emoticons';\n    const option = name => editor => editor.options.get(name);\n    const register$2 = (editor, pluginUrl) => {\n      const registerOption = editor.options.register;\n      registerOption('emoticons_database', {\n        processor: 'string',\n        default: 'emojis'\n      });\n      registerOption('emoticons_database_url', {\n        processor: 'string',\n        default: `${ pluginUrl }/js/${ getEmojiDatabase(editor) }${ editor.suffix }.js`\n      });\n      registerOption('emoticons_database_id', {\n        processor: 'string',\n        default: DEFAULT_ID\n      });\n      registerOption('emoticons_append', {\n        processor: 'object',\n        default: {}\n      });\n      registerOption('emoticons_images_url', {\n        processor: 'string',\n        default: 'https://twemoji.maxcdn.com/v/13.0.1/72x72/'\n      });\n    };\n    const getEmojiDatabase = option('emoticons_database');\n    const getEmojiDatabaseUrl = option('emoticons_database_url');\n    const getEmojiDatabaseId = option('emoticons_database_id');\n    const getAppendedEmoji = option('emoticons_append');\n    const getEmojiImageUrl = option('emoticons_images_url');\n\n    const ALL_CATEGORY = 'All';\n    const categoryNameMap = {\n      symbols: 'Symbols',\n      people: 'People',\n      animals_and_nature: 'Animals and Nature',\n      food_and_drink: 'Food and Drink',\n      activity: 'Activity',\n      travel_and_places: 'Travel and Places',\n      objects: 'Objects',\n      flags: 'Flags',\n      user: 'User Defined'\n    };\n    const translateCategory = (categories, name) => has(categories, name) ? categories[name] : name;\n    const getUserDefinedEmoji = editor => {\n      const userDefinedEmoticons = getAppendedEmoji(editor);\n      return map(userDefinedEmoticons, value => ({\n        keywords: [],\n        category: 'user',\n        ...value\n      }));\n    };\n    const initDatabase = (editor, databaseUrl, databaseId) => {\n      const categories = value();\n      const all = value();\n      const emojiImagesUrl = getEmojiImageUrl(editor);\n      const getEmoji = lib => {\n        if (startsWith(lib.char, '<img')) {\n          return lib.char.replace(/src=\"([^\"]+)\"/, (match, url) => `src=\"${ emojiImagesUrl }${ url }\"`);\n        } else {\n          return lib.char;\n        }\n      };\n      const processEmojis = emojis => {\n        const cats = {};\n        const everything = [];\n        each(emojis, (lib, title) => {\n          const entry = {\n            title,\n            keywords: lib.keywords,\n            char: getEmoji(lib),\n            category: translateCategory(categoryNameMap, lib.category)\n          };\n          const current = cats[entry.category] !== undefined ? cats[entry.category] : [];\n          cats[entry.category] = current.concat([entry]);\n          everything.push(entry);\n        });\n        categories.set(cats);\n        all.set(everything);\n      };\n      editor.on('init', () => {\n        global.load(databaseId, databaseUrl).then(emojis => {\n          const userEmojis = getUserDefinedEmoji(editor);\n          processEmojis(merge(emojis, userEmojis));\n        }, err => {\n          console.log(`Failed to load emojis: ${ err }`);\n          categories.set({});\n          all.set([]);\n        });\n      });\n      const listCategory = category => {\n        if (category === ALL_CATEGORY) {\n          return listAll();\n        }\n        return categories.get().bind(cats => Optional.from(cats[category])).getOr([]);\n      };\n      const listAll = () => all.get().getOr([]);\n      const listCategories = () => [ALL_CATEGORY].concat(keys(categories.get().getOr({})));\n      const waitForLoad = () => {\n        if (hasLoaded()) {\n          return Promise.resolve(true);\n        } else {\n          return new Promise((resolve, reject) => {\n            let numRetries = 15;\n            const interval = setInterval(() => {\n              if (hasLoaded()) {\n                clearInterval(interval);\n                resolve(true);\n              } else {\n                numRetries--;\n                if (numRetries < 0) {\n                  console.log('Could not load emojis from url: ' + databaseUrl);\n                  clearInterval(interval);\n                  reject(false);\n                }\n              }\n            }, 100);\n          });\n        }\n      };\n      const hasLoaded = () => categories.isSet() && all.isSet();\n      return {\n        listCategories,\n        hasLoaded,\n        waitForLoad,\n        listAll,\n        listCategory\n      };\n    };\n\n    const emojiMatches = (emoji, lowerCasePattern) => contains(emoji.title.toLowerCase(), lowerCasePattern) || exists(emoji.keywords, k => contains(k.toLowerCase(), lowerCasePattern));\n    const emojisFrom = (list, pattern, maxResults) => {\n      const matches = [];\n      const lowerCasePattern = pattern.toLowerCase();\n      const reachedLimit = maxResults.fold(() => never, max => size => size >= max);\n      for (let i = 0; i < list.length; i++) {\n        if (pattern.length === 0 || emojiMatches(list[i], lowerCasePattern)) {\n          matches.push({\n            value: list[i].char,\n            text: list[i].title,\n            icon: list[i].char\n          });\n          if (reachedLimit(matches.length)) {\n            break;\n          }\n        }\n      }\n      return matches;\n    };\n\n    const patternName = 'pattern';\n    const open = (editor, database) => {\n      const initialState = {\n        pattern: '',\n        results: emojisFrom(database.listAll(), '', Optional.some(300))\n      };\n      const currentTab = Cell(ALL_CATEGORY);\n      const scan = dialogApi => {\n        const dialogData = dialogApi.getData();\n        const category = currentTab.get();\n        const candidates = database.listCategory(category);\n        const results = emojisFrom(candidates, dialogData[patternName], category === ALL_CATEGORY ? Optional.some(300) : Optional.none());\n        dialogApi.setData({ results });\n      };\n      const updateFilter = last(dialogApi => {\n        scan(dialogApi);\n      }, 200);\n      const searchField = {\n        label: 'Search',\n        type: 'input',\n        name: patternName\n      };\n      const resultsField = {\n        type: 'collection',\n        name: 'results'\n      };\n      const getInitialState = () => {\n        const body = {\n          type: 'tabpanel',\n          tabs: map$1(database.listCategories(), cat => ({\n            title: cat,\n            name: cat,\n            items: [\n              searchField,\n              resultsField\n            ]\n          }))\n        };\n        return {\n          title: 'Emojis',\n          size: 'normal',\n          body,\n          initialData: initialState,\n          onTabChange: (dialogApi, details) => {\n            currentTab.set(details.newTabName);\n            updateFilter.throttle(dialogApi);\n          },\n          onChange: updateFilter.throttle,\n          onAction: (dialogApi, actionData) => {\n            if (actionData.name === 'results') {\n              insertEmoticon(editor, actionData.value);\n              dialogApi.close();\n            }\n          },\n          buttons: [{\n              type: 'cancel',\n              text: 'Close',\n              primary: true\n            }]\n        };\n      };\n      const dialogApi = editor.windowManager.open(getInitialState());\n      dialogApi.focus(patternName);\n      if (!database.hasLoaded()) {\n        dialogApi.block('Loading emojis...');\n        database.waitForLoad().then(() => {\n          dialogApi.redial(getInitialState());\n          updateFilter.throttle(dialogApi);\n          dialogApi.focus(patternName);\n          dialogApi.unblock();\n        }).catch(_err => {\n          dialogApi.redial({\n            title: 'Emojis',\n            body: {\n              type: 'panel',\n              items: [{\n                  type: 'alertbanner',\n                  level: 'error',\n                  icon: 'warning',\n                  text: 'Could not load emojis'\n                }]\n            },\n            buttons: [{\n                type: 'cancel',\n                text: 'Close',\n                primary: true\n              }],\n            initialData: {\n              pattern: '',\n              results: []\n            }\n          });\n          dialogApi.focus(patternName);\n          dialogApi.unblock();\n        });\n      }\n    };\n\n    const register$1 = (editor, database) => {\n      editor.addCommand('mceEmoticons', () => open(editor, database));\n    };\n\n    const setup = editor => {\n      editor.on('PreInit', () => {\n        editor.parser.addAttributeFilter('data-emoticon', nodes => {\n          each$1(nodes, node => {\n            node.attr('data-mce-resize', 'false');\n            node.attr('data-mce-placeholder', '1');\n          });\n        });\n      });\n    };\n\n    const init = (editor, database) => {\n      editor.ui.registry.addAutocompleter('emoticons', {\n        trigger: ':',\n        columns: 'auto',\n        minChars: 2,\n        fetch: (pattern, maxResults) => database.waitForLoad().then(() => {\n          const candidates = database.listAll();\n          return emojisFrom(candidates, pattern, Optional.some(maxResults));\n        }),\n        onAction: (autocompleteApi, rng, value) => {\n          editor.selection.setRng(rng);\n          editor.insertContent(value);\n          autocompleteApi.hide();\n        }\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceEmoticons');\n      editor.ui.registry.addButton('emoticons', {\n        tooltip: 'Emojis',\n        icon: 'emoji',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('emoticons', {\n        text: 'Emojis...',\n        icon: 'emoji',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$1.add('emoticons', (editor, pluginUrl) => {\n        register$2(editor, pluginUrl);\n        const databaseUrl = getEmojiDatabaseUrl(editor);\n        const databaseId = getEmojiDatabaseId(editor);\n        const database = initDatabase(editor, databaseUrl, databaseId);\n        register$1(editor, database);\n        register(editor);\n        init(editor, database);\n        setup(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/fullscreen/index.js",
    "content": "// Exports the \"fullscreen\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/fullscreen')\n//   ES2015:\n//     import 'tinymce/plugins/fullscreen'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/fullscreen/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const get$5 = fullscreenState => ({ isFullscreen: () => fullscreenState.get() !== null });\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq$1 = t => a => t === a;\n    const isString = isType$1('string');\n    const isArray = isType$1('array');\n    const isNull = eq$1(null);\n    const isBoolean = isSimpleType('boolean');\n    const isUndefined = eq$1(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n\n    const noop = () => {\n    };\n    const compose = (fa, fb) => {\n      return (...args) => {\n        return fa(fb.apply(null, args));\n      };\n    };\n    const compose1 = (fbc, fab) => a => fbc(fab(a));\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    function curry(fn, ...initialArgs) {\n      return (...restArgs) => {\n        const all = initialArgs.concat(restArgs);\n        return fn.apply(null, all);\n      };\n    }\n    const never = constant(false);\n    const always = constant(true);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const singleton = doRevoke => {\n      const subject = Cell(Optional.none());\n      const revoke = () => subject.get().each(doRevoke);\n      const clear = () => {\n        revoke();\n        subject.set(Optional.none());\n      };\n      const isSet = () => subject.get().isSome();\n      const get = () => subject.get();\n      const set = s => {\n        revoke();\n        subject.set(Optional.some(s));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const unbindable = () => singleton(s => s.unbind());\n    const value = () => {\n      const subject = singleton(noop);\n      const on = f => subject.get().each(f);\n      return {\n        ...subject,\n        on\n      };\n    };\n\n    const first = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        if (isNull(timer)) {\n          timer = setTimeout(() => {\n            timer = null;\n            fn.apply(null, args);\n          }, rate);\n        }\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    const nativePush = Array.prototype.push;\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$1 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const filter$1 = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find$1 = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind$3 = (xs, f) => flatten(map(xs, f));\n    const get$4 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get$4(xs, 0);\n    const findMap = (arr, f) => {\n      for (let i = 0; i < arr.length; i++) {\n        const r = f(arr[i], i);\n        if (r.isSome()) {\n          return r;\n        }\n      }\n      return Optional.none();\n    };\n\n    const keys = Object.keys;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n\n    const contains = (str, substr, start = 0, end) => {\n      const idx = str.indexOf(substr, start);\n      if (idx !== -1) {\n        return isUndefined(end) ? true : idx + substr.length <= end;\n      } else {\n        return false;\n      }\n    };\n\n    const isSupported$1 = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom(node);\n    };\n    const fromDom = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom,\n      fromPoint\n    };\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const DOCUMENT = 9;\n    const DOCUMENT_FRAGMENT = 11;\n    const ELEMENT = 1;\n    const TEXT = 3;\n\n    const type = element => element.dom.nodeType;\n    const isType = t => element => type(element) === t;\n    const isElement = isType(ELEMENT);\n    const isText = isType(TEXT);\n    const isDocument = isType(DOCUMENT);\n    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);\n\n    const is = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;\n    const all$1 = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);\n    };\n\n    const eq = (e1, e2) => e1.dom === e2.dom;\n\n    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);\n    const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const parents = (element, isRoot) => {\n      const stop = isFunction(isRoot) ? isRoot : never;\n      let dom = element.dom;\n      const ret = [];\n      while (dom.parentNode !== null && dom.parentNode !== undefined) {\n        const rawParent = dom.parentNode;\n        const p = SugarElement.fromDom(rawParent);\n        ret.push(p);\n        if (stop(p) === true) {\n          break;\n        } else {\n          dom = rawParent;\n        }\n      }\n      return ret;\n    };\n    const siblings$2 = element => {\n      const filterSelf = elements => filter$1(elements, x => !eq(element, x));\n      return parent(element).map(children).map(filterSelf).getOr([]);\n    };\n    const children = element => map(element.dom.childNodes, SugarElement.fromDom);\n\n    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);\n    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);\n    const isSupported = constant(supported);\n    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;\n    const getShadowRoot = e => {\n      const r = getRootNode(e);\n      return isShadowRoot(r) ? Optional.some(r) : Optional.none();\n    };\n    const getShadowHost = e => SugarElement.fromDom(e.dom.host);\n    const getOriginalEventTarget = event => {\n      if (isSupported() && isNonNullable(event.target)) {\n        const el = SugarElement.fromDom(event.target);\n        if (isElement(el) && isOpenShadowHost(el)) {\n          if (event.composed && event.composedPath) {\n            const composedPath = event.composedPath();\n            if (composedPath) {\n              return head(composedPath);\n            }\n          }\n        }\n      }\n      return Optional.from(event.target);\n    };\n    const isOpenShadowHost = element => isNonNullable(element.dom.shadowRoot);\n\n    const inBody = element => {\n      const dom = isText(element) ? element.dom.parentNode : element.dom;\n      if (dom === undefined || dom === null || dom.ownerDocument === null) {\n        return false;\n      }\n      const doc = dom.ownerDocument;\n      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));\n    };\n    const getBody = doc => {\n      const b = doc.dom.body;\n      if (b === null || b === undefined) {\n        throw new Error('Body is not available yet');\n      }\n      return SugarElement.fromDom(b);\n    };\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const get$3 = (element, key) => {\n      const v = element.dom.getAttribute(key);\n      return v === null ? undefined : v;\n    };\n    const remove = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n\n    const internalSet = (dom, property, value) => {\n      if (!isString(value)) {\n        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);\n        throw new Error('CSS value must be a string: ' + value);\n      }\n      if (isSupported$1(dom)) {\n        dom.style.setProperty(property, value);\n      }\n    };\n    const setAll = (element, css) => {\n      const dom = element.dom;\n      each(css, (v, k) => {\n        internalSet(dom, k, v);\n      });\n    };\n    const get$2 = (element, property) => {\n      const dom = element.dom;\n      const styles = window.getComputedStyle(dom);\n      const r = styles.getPropertyValue(property);\n      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;\n    };\n    const getUnsafeProperty = (dom, property) => isSupported$1(dom) ? dom.style.getPropertyValue(property) : '';\n\n    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({\n      target,\n      x,\n      y,\n      stop,\n      prevent,\n      kill,\n      raw\n    });\n    const fromRawEvent = rawEvent => {\n      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));\n      const stop = () => rawEvent.stopPropagation();\n      const prevent = () => rawEvent.preventDefault();\n      const kill = compose(prevent, stop);\n      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);\n    };\n    const handle = (filter, handler) => rawEvent => {\n      if (filter(rawEvent)) {\n        handler(fromRawEvent(rawEvent));\n      }\n    };\n    const binder = (element, event, filter, handler, useCapture) => {\n      const wrapped = handle(filter, handler);\n      element.dom.addEventListener(event, wrapped, useCapture);\n      return { unbind: curry(unbind, element, event, wrapped, useCapture) };\n    };\n    const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false);\n    const unbind = (element, event, handler, useCapture) => {\n      element.dom.removeEventListener(event, handler, useCapture);\n    };\n\n    const filter = always;\n    const bind$1 = (element, event, handler) => bind$2(element, event, filter, handler);\n\n    const cached = f => {\n      let called = false;\n      let r;\n      return (...args) => {\n        if (!called) {\n          called = true;\n          r = f.apply(null, args);\n        }\n        return r;\n      };\n    };\n\n    const DeviceType = (os, browser, userAgent, mediaMatch) => {\n      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;\n      const isiPhone = os.isiOS() && !isiPad;\n      const isMobile = os.isiOS() || os.isAndroid();\n      const isTouch = isMobile || mediaMatch('(pointer:coarse)');\n      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');\n      const isPhone = isiPhone || isMobile && !isTablet;\n      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;\n      const isDesktop = !isPhone && !isTablet && !iOSwebview;\n      return {\n        isiPad: constant(isiPad),\n        isiPhone: constant(isiPhone),\n        isTablet: constant(isTablet),\n        isPhone: constant(isPhone),\n        isTouch: constant(isTouch),\n        isAndroid: os.isAndroid,\n        isiOS: os.isiOS,\n        isWebView: constant(iOSwebview),\n        isDesktop: constant(isDesktop)\n      };\n    };\n\n    const firstMatch = (regexes, s) => {\n      for (let i = 0; i < regexes.length; i++) {\n        const x = regexes[i];\n        if (x.test(s)) {\n          return x;\n        }\n      }\n      return undefined;\n    };\n    const find = (regexes, agent) => {\n      const r = firstMatch(regexes, agent);\n      if (!r) {\n        return {\n          major: 0,\n          minor: 0\n        };\n      }\n      const group = i => {\n        return Number(agent.replace(r, '$' + i));\n      };\n      return nu$2(group(1), group(2));\n    };\n    const detect$3 = (versionRegexes, agent) => {\n      const cleanedAgent = String(agent).toLowerCase();\n      if (versionRegexes.length === 0) {\n        return unknown$2();\n      }\n      return find(versionRegexes, cleanedAgent);\n    };\n    const unknown$2 = () => {\n      return nu$2(0, 0);\n    };\n    const nu$2 = (major, minor) => {\n      return {\n        major,\n        minor\n      };\n    };\n    const Version = {\n      nu: nu$2,\n      detect: detect$3,\n      unknown: unknown$2\n    };\n\n    const detectBrowser$1 = (browsers, userAgentData) => {\n      return findMap(userAgentData.brands, uaBrand => {\n        const lcBrand = uaBrand.brand.toLowerCase();\n        return find$1(browsers, browser => {\n          var _a;\n          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());\n        }).map(info => ({\n          current: info.name,\n          version: Version.nu(parseInt(uaBrand.version, 10), 0)\n        }));\n      });\n    };\n\n    const detect$2 = (candidates, userAgent) => {\n      const agent = String(userAgent).toLowerCase();\n      return find$1(candidates, candidate => {\n        return candidate.search(agent);\n      });\n    };\n    const detectBrowser = (browsers, userAgent) => {\n      return detect$2(browsers, userAgent).map(browser => {\n        const version = Version.detect(browser.versionRegexes, userAgent);\n        return {\n          current: browser.name,\n          version\n        };\n      });\n    };\n    const detectOs = (oses, userAgent) => {\n      return detect$2(oses, userAgent).map(os => {\n        const version = Version.detect(os.versionRegexes, userAgent);\n        return {\n          current: os.name,\n          version\n        };\n      });\n    };\n\n    const normalVersionRegex = /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/;\n    const checkContains = target => {\n      return uastring => {\n        return contains(uastring, target);\n      };\n    };\n    const browsers = [\n      {\n        name: 'Edge',\n        versionRegexes: [/.*?edge\\/ ?([0-9]+)\\.([0-9]+)$/],\n        search: uastring => {\n          return contains(uastring, 'edge/') && contains(uastring, 'chrome') && contains(uastring, 'safari') && contains(uastring, 'applewebkit');\n        }\n      },\n      {\n        name: 'Chromium',\n        brand: 'Chromium',\n        versionRegexes: [\n          /.*?chrome\\/([0-9]+)\\.([0-9]+).*/,\n          normalVersionRegex\n        ],\n        search: uastring => {\n          return contains(uastring, 'chrome') && !contains(uastring, 'chromeframe');\n        }\n      },\n      {\n        name: 'IE',\n        versionRegexes: [\n          /.*?msie\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*?rv:([0-9]+)\\.([0-9]+).*/\n        ],\n        search: uastring => {\n          return contains(uastring, 'msie') || contains(uastring, 'trident');\n        }\n      },\n      {\n        name: 'Opera',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?opera\\/([0-9]+)\\.([0-9]+).*/\n        ],\n        search: checkContains('opera')\n      },\n      {\n        name: 'Firefox',\n        versionRegexes: [/.*?firefox\\/\\ ?([0-9]+)\\.([0-9]+).*/],\n        search: checkContains('firefox')\n      },\n      {\n        name: 'Safari',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?cpu os ([0-9]+)_([0-9]+).*/\n        ],\n        search: uastring => {\n          return (contains(uastring, 'safari') || contains(uastring, 'mobile/')) && contains(uastring, 'applewebkit');\n        }\n      }\n    ];\n    const oses = [\n      {\n        name: 'Windows',\n        search: checkContains('win'),\n        versionRegexes: [/.*?windows\\ nt\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'iOS',\n        search: uastring => {\n          return contains(uastring, 'iphone') || contains(uastring, 'ipad');\n        },\n        versionRegexes: [\n          /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*cpu os ([0-9]+)_([0-9]+).*/,\n          /.*cpu iphone os ([0-9]+)_([0-9]+).*/\n        ]\n      },\n      {\n        name: 'Android',\n        search: checkContains('android'),\n        versionRegexes: [/.*?android\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'macOS',\n        search: checkContains('mac os x'),\n        versionRegexes: [/.*?mac\\ os\\ x\\ ?([0-9]+)_([0-9]+).*/]\n      },\n      {\n        name: 'Linux',\n        search: checkContains('linux'),\n        versionRegexes: []\n      },\n      {\n        name: 'Solaris',\n        search: checkContains('sunos'),\n        versionRegexes: []\n      },\n      {\n        name: 'FreeBSD',\n        search: checkContains('freebsd'),\n        versionRegexes: []\n      },\n      {\n        name: 'ChromeOS',\n        search: checkContains('cros'),\n        versionRegexes: [/.*?chrome\\/([0-9]+)\\.([0-9]+).*/]\n      }\n    ];\n    const PlatformInfo = {\n      browsers: constant(browsers),\n      oses: constant(oses)\n    };\n\n    const edge = 'Edge';\n    const chromium = 'Chromium';\n    const ie = 'IE';\n    const opera = 'Opera';\n    const firefox = 'Firefox';\n    const safari = 'Safari';\n    const unknown$1 = () => {\n      return nu$1({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu$1 = info => {\n      const current = info.current;\n      const version = info.version;\n      const isBrowser = name => () => current === name;\n      return {\n        current,\n        version,\n        isEdge: isBrowser(edge),\n        isChromium: isBrowser(chromium),\n        isIE: isBrowser(ie),\n        isOpera: isBrowser(opera),\n        isFirefox: isBrowser(firefox),\n        isSafari: isBrowser(safari)\n      };\n    };\n    const Browser = {\n      unknown: unknown$1,\n      nu: nu$1,\n      edge: constant(edge),\n      chromium: constant(chromium),\n      ie: constant(ie),\n      opera: constant(opera),\n      firefox: constant(firefox),\n      safari: constant(safari)\n    };\n\n    const windows = 'Windows';\n    const ios = 'iOS';\n    const android = 'Android';\n    const linux = 'Linux';\n    const macos = 'macOS';\n    const solaris = 'Solaris';\n    const freebsd = 'FreeBSD';\n    const chromeos = 'ChromeOS';\n    const unknown = () => {\n      return nu({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu = info => {\n      const current = info.current;\n      const version = info.version;\n      const isOS = name => () => current === name;\n      return {\n        current,\n        version,\n        isWindows: isOS(windows),\n        isiOS: isOS(ios),\n        isAndroid: isOS(android),\n        isMacOS: isOS(macos),\n        isLinux: isOS(linux),\n        isSolaris: isOS(solaris),\n        isFreeBSD: isOS(freebsd),\n        isChromeOS: isOS(chromeos)\n      };\n    };\n    const OperatingSystem = {\n      unknown,\n      nu,\n      windows: constant(windows),\n      ios: constant(ios),\n      android: constant(android),\n      linux: constant(linux),\n      macos: constant(macos),\n      solaris: constant(solaris),\n      freebsd: constant(freebsd),\n      chromeos: constant(chromeos)\n    };\n\n    const detect$1 = (userAgent, userAgentDataOpt, mediaMatch) => {\n      const browsers = PlatformInfo.browsers();\n      const oses = PlatformInfo.oses();\n      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);\n      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);\n      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);\n      return {\n        browser,\n        os,\n        deviceType\n      };\n    };\n    const PlatformDetection = { detect: detect$1 };\n\n    const mediaMatch = query => window.matchMedia(query).matches;\n    let platform = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));\n    const detect = () => platform();\n\n    const r = (left, top) => {\n      const translate = (x, y) => r(left + x, top + y);\n      return {\n        left,\n        top,\n        translate\n      };\n    };\n    const SugarPosition = r;\n\n    const get$1 = _DOC => {\n      const doc = _DOC !== undefined ? _DOC.dom : document;\n      const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;\n      const y = doc.body.scrollTop || doc.documentElement.scrollTop;\n      return SugarPosition(x, y);\n    };\n\n    const get = _win => {\n      const win = _win === undefined ? window : _win;\n      if (detect().browser.isFirefox()) {\n        return Optional.none();\n      } else {\n        return Optional.from(win.visualViewport);\n      }\n    };\n    const bounds = (x, y, width, height) => ({\n      x,\n      y,\n      width,\n      height,\n      right: x + width,\n      bottom: y + height\n    });\n    const getBounds = _win => {\n      const win = _win === undefined ? window : _win;\n      const doc = win.document;\n      const scroll = get$1(SugarElement.fromDom(doc));\n      return get(win).fold(() => {\n        const html = win.document.documentElement;\n        const width = html.clientWidth;\n        const height = html.clientHeight;\n        return bounds(scroll.left, scroll.top, width, height);\n      }, visualViewport => bounds(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height));\n    };\n    const bind = (name, callback, _win) => get(_win).map(visualViewport => {\n      const handler = e => callback(fromRawEvent(e));\n      visualViewport.addEventListener(name, handler);\n      return { unbind: () => visualViewport.removeEventListener(name, handler) };\n    }).getOrThunk(() => ({ unbind: noop }));\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    var global = tinymce.util.Tools.resolve('tinymce.Env');\n\n    const fireFullscreenStateChanged = (editor, state) => {\n      editor.dispatch('FullscreenStateChanged', { state });\n      editor.dispatch('ResizeEditor');\n    };\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('fullscreen_native', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const getFullscreenNative = option('fullscreen_native');\n\n    const getFullscreenRoot = editor => {\n      const elem = SugarElement.fromDom(editor.getElement());\n      return getShadowRoot(elem).map(getShadowHost).getOrThunk(() => getBody(owner(elem)));\n    };\n    const getFullscreenElement = root => {\n      if (root.fullscreenElement !== undefined) {\n        return root.fullscreenElement;\n      } else if (root.msFullscreenElement !== undefined) {\n        return root.msFullscreenElement;\n      } else if (root.webkitFullscreenElement !== undefined) {\n        return root.webkitFullscreenElement;\n      } else {\n        return null;\n      }\n    };\n    const getFullscreenchangeEventName = () => {\n      if (document.fullscreenElement !== undefined) {\n        return 'fullscreenchange';\n      } else if (document.msFullscreenElement !== undefined) {\n        return 'MSFullscreenChange';\n      } else if (document.webkitFullscreenElement !== undefined) {\n        return 'webkitfullscreenchange';\n      } else {\n        return 'fullscreenchange';\n      }\n    };\n    const requestFullscreen = sugarElem => {\n      const elem = sugarElem.dom;\n      if (elem.requestFullscreen) {\n        elem.requestFullscreen();\n      } else if (elem.msRequestFullscreen) {\n        elem.msRequestFullscreen();\n      } else if (elem.webkitRequestFullScreen) {\n        elem.webkitRequestFullScreen();\n      }\n    };\n    const exitFullscreen = sugarDoc => {\n      const doc = sugarDoc.dom;\n      if (doc.exitFullscreen) {\n        doc.exitFullscreen();\n      } else if (doc.msExitFullscreen) {\n        doc.msExitFullscreen();\n      } else if (doc.webkitCancelFullScreen) {\n        doc.webkitCancelFullScreen();\n      }\n    };\n    const isFullscreenElement = elem => elem.dom === getFullscreenElement(owner(elem).dom);\n\n    const ancestors$1 = (scope, predicate, isRoot) => filter$1(parents(scope, isRoot), predicate);\n    const siblings$1 = (scope, predicate) => filter$1(siblings$2(scope), predicate);\n\n    const all = selector => all$1(selector);\n    const ancestors = (scope, selector, isRoot) => ancestors$1(scope, e => is(e, selector), isRoot);\n    const siblings = (scope, selector) => siblings$1(scope, e => is(e, selector));\n\n    const attr = 'data-ephox-mobile-fullscreen-style';\n    const siblingStyles = 'display:none!important;';\n    const ancestorPosition = 'position:absolute!important;';\n    const ancestorStyles = 'top:0!important;left:0!important;margin:0!important;padding:0!important;width:100%!important;height:100%!important;overflow:visible!important;';\n    const bgFallback = 'background-color:rgb(255,255,255)!important;';\n    const isAndroid = global.os.isAndroid();\n    const matchColor = editorBody => {\n      const color = get$2(editorBody, 'background-color');\n      return color !== undefined && color !== '' ? 'background-color:' + color + '!important' : bgFallback;\n    };\n    const clobberStyles = (dom, container, editorBody) => {\n      const gatherSiblings = element => {\n        return siblings(element, '*:not(.tox-silver-sink)');\n      };\n      const clobber = clobberStyle => element => {\n        const styles = get$3(element, 'style');\n        const backup = styles === undefined ? 'no-styles' : styles.trim();\n        if (backup === clobberStyle) {\n          return;\n        } else {\n          set(element, attr, backup);\n          setAll(element, dom.parseStyle(clobberStyle));\n        }\n      };\n      const ancestors$1 = ancestors(container, '*');\n      const siblings$1 = bind$3(ancestors$1, gatherSiblings);\n      const bgColor = matchColor(editorBody);\n      each$1(siblings$1, clobber(siblingStyles));\n      each$1(ancestors$1, clobber(ancestorPosition + ancestorStyles + bgColor));\n      const containerStyles = isAndroid === true ? '' : ancestorPosition;\n      clobber(containerStyles + ancestorStyles + bgColor)(container);\n    };\n    const restoreStyles = dom => {\n      const clobberedEls = all('[' + attr + ']');\n      each$1(clobberedEls, element => {\n        const restore = get$3(element, attr);\n        if (restore && restore !== 'no-styles') {\n          setAll(element, dom.parseStyle(restore));\n        } else {\n          remove(element, 'style');\n        }\n        remove(element, attr);\n      });\n    };\n\n    const DOM = global$1.DOM;\n    const getScrollPos = () => getBounds(window);\n    const setScrollPos = pos => window.scrollTo(pos.x, pos.y);\n    const viewportUpdate = get().fold(() => ({\n      bind: noop,\n      unbind: noop\n    }), visualViewport => {\n      const editorContainer = value();\n      const resizeBinder = unbindable();\n      const scrollBinder = unbindable();\n      const refreshScroll = () => {\n        document.body.scrollTop = 0;\n        document.documentElement.scrollTop = 0;\n      };\n      const refreshVisualViewport = () => {\n        window.requestAnimationFrame(() => {\n          editorContainer.on(container => setAll(container, {\n            top: visualViewport.offsetTop + 'px',\n            left: visualViewport.offsetLeft + 'px',\n            height: visualViewport.height + 'px',\n            width: visualViewport.width + 'px'\n          }));\n        });\n      };\n      const update = first(() => {\n        refreshScroll();\n        refreshVisualViewport();\n      }, 50);\n      const bind$1 = element => {\n        editorContainer.set(element);\n        update.throttle();\n        resizeBinder.set(bind('resize', update.throttle));\n        scrollBinder.set(bind('scroll', update.throttle));\n      };\n      const unbind = () => {\n        editorContainer.on(() => {\n          resizeBinder.clear();\n          scrollBinder.clear();\n        });\n        editorContainer.clear();\n      };\n      return {\n        bind: bind$1,\n        unbind\n      };\n    });\n    const toggleFullscreen = (editor, fullscreenState) => {\n      const body = document.body;\n      const documentElement = document.documentElement;\n      const editorContainer = editor.getContainer();\n      const editorContainerS = SugarElement.fromDom(editorContainer);\n      const fullscreenRoot = getFullscreenRoot(editor);\n      const fullscreenInfo = fullscreenState.get();\n      const editorBody = SugarElement.fromDom(editor.getBody());\n      const isTouch = global.deviceType.isTouch();\n      const editorContainerStyle = editorContainer.style;\n      const iframe = editor.iframeElement;\n      const iframeStyle = iframe === null || iframe === void 0 ? void 0 : iframe.style;\n      const handleClasses = handler => {\n        handler(body, 'tox-fullscreen');\n        handler(documentElement, 'tox-fullscreen');\n        handler(editorContainer, 'tox-fullscreen');\n        getShadowRoot(editorContainerS).map(root => getShadowHost(root).dom).each(host => {\n          handler(host, 'tox-fullscreen');\n          handler(host, 'tox-shadowhost');\n        });\n      };\n      const cleanup = () => {\n        if (isTouch) {\n          restoreStyles(editor.dom);\n        }\n        handleClasses(DOM.removeClass);\n        viewportUpdate.unbind();\n        Optional.from(fullscreenState.get()).each(info => info.fullscreenChangeHandler.unbind());\n      };\n      if (!fullscreenInfo) {\n        const fullscreenChangeHandler = bind$1(owner(fullscreenRoot), getFullscreenchangeEventName(), _evt => {\n          if (getFullscreenNative(editor)) {\n            if (!isFullscreenElement(fullscreenRoot) && fullscreenState.get() !== null) {\n              toggleFullscreen(editor, fullscreenState);\n            }\n          }\n        });\n        const newFullScreenInfo = {\n          scrollPos: getScrollPos(),\n          containerWidth: editorContainerStyle.width,\n          containerHeight: editorContainerStyle.height,\n          containerTop: editorContainerStyle.top,\n          containerLeft: editorContainerStyle.left,\n          iframeWidth: iframeStyle.width,\n          iframeHeight: iframeStyle.height,\n          fullscreenChangeHandler\n        };\n        if (isTouch) {\n          clobberStyles(editor.dom, editorContainerS, editorBody);\n        }\n        iframeStyle.width = iframeStyle.height = '100%';\n        editorContainerStyle.width = editorContainerStyle.height = '';\n        handleClasses(DOM.addClass);\n        viewportUpdate.bind(editorContainerS);\n        editor.on('remove', cleanup);\n        fullscreenState.set(newFullScreenInfo);\n        if (getFullscreenNative(editor)) {\n          requestFullscreen(fullscreenRoot);\n        }\n        fireFullscreenStateChanged(editor, true);\n      } else {\n        fullscreenInfo.fullscreenChangeHandler.unbind();\n        if (getFullscreenNative(editor) && isFullscreenElement(fullscreenRoot)) {\n          exitFullscreen(owner(fullscreenRoot));\n        }\n        iframeStyle.width = fullscreenInfo.iframeWidth;\n        iframeStyle.height = fullscreenInfo.iframeHeight;\n        editorContainerStyle.width = fullscreenInfo.containerWidth;\n        editorContainerStyle.height = fullscreenInfo.containerHeight;\n        editorContainerStyle.top = fullscreenInfo.containerTop;\n        editorContainerStyle.left = fullscreenInfo.containerLeft;\n        cleanup();\n        setScrollPos(fullscreenInfo.scrollPos);\n        fullscreenState.set(null);\n        fireFullscreenStateChanged(editor, false);\n        editor.off('remove', cleanup);\n      }\n    };\n\n    const register$1 = (editor, fullscreenState) => {\n      editor.addCommand('mceFullScreen', () => {\n        toggleFullscreen(editor, fullscreenState);\n      });\n    };\n\n    const makeSetupHandler = (editor, fullscreenState) => api => {\n      api.setActive(fullscreenState.get() !== null);\n      const editorEventCallback = e => api.setActive(e.state);\n      editor.on('FullscreenStateChanged', editorEventCallback);\n      return () => editor.off('FullscreenStateChanged', editorEventCallback);\n    };\n    const register = (editor, fullscreenState) => {\n      const onAction = () => editor.execCommand('mceFullScreen');\n      editor.ui.registry.addToggleMenuItem('fullscreen', {\n        text: 'Fullscreen',\n        icon: 'fullscreen',\n        shortcut: 'Meta+Shift+F',\n        onAction,\n        onSetup: makeSetupHandler(editor, fullscreenState)\n      });\n      editor.ui.registry.addToggleButton('fullscreen', {\n        tooltip: 'Fullscreen',\n        icon: 'fullscreen',\n        onAction,\n        onSetup: makeSetupHandler(editor, fullscreenState)\n      });\n    };\n\n    var Plugin = () => {\n      global$2.add('fullscreen', editor => {\n        const fullscreenState = Cell(null);\n        if (editor.inline) {\n          return get$5(fullscreenState);\n        }\n        register$2(editor);\n        register$1(editor, fullscreenState);\n        register(editor, fullscreenState);\n        editor.addShortcut('Meta+Shift+F', '', 'mceFullScreen');\n        return get$5(fullscreenState);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/help/index.js",
    "content": "// Exports the \"help\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/help')\n//   ES2015:\n//     import 'tinymce/plugins/help'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/help/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    let unique = 0;\n    const generate = prefix => {\n      const date = new Date();\n      const time = date.getTime();\n      const random = Math.floor(Math.random() * 1000000000);\n      unique++;\n      return prefix + '_' + random + unique + String(time);\n    };\n\n    const get$1 = customTabs => {\n      const addTab = spec => {\n        var _a;\n        const name = (_a = spec.name) !== null && _a !== void 0 ? _a : generate('tab-name');\n        const currentCustomTabs = customTabs.get();\n        currentCustomTabs[name] = spec;\n        customTabs.set(currentCustomTabs);\n      };\n      return { addTab };\n    };\n\n    const register$2 = (editor, dialogOpener) => {\n      editor.addCommand('mceHelp', dialogOpener);\n    };\n\n    const option = name => editor => editor.options.get(name);\n    const register$1 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('help_tabs', { processor: 'array' });\n    };\n    const getHelpTabs = option('help_tabs');\n    const getForcedPlugins = option('forced_plugins');\n\n    const register = (editor, dialogOpener) => {\n      editor.ui.registry.addButton('help', {\n        icon: 'help',\n        tooltip: 'Help',\n        onAction: dialogOpener\n      });\n      editor.ui.registry.addMenuItem('help', {\n        text: 'Help',\n        icon: 'help',\n        shortcut: 'Alt+0',\n        onAction: dialogOpener\n      });\n    };\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq = t => a => t === a;\n    const isString = isType('string');\n    const isUndefined = eq(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const never = constant(false);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativeSlice = Array.prototype.slice;\n    const nativeIndexOf = Array.prototype.indexOf;\n    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);\n    const contains = (xs, x) => rawIndexOf(xs, x) > -1;\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const filter = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n    const sort = (xs, comparator) => {\n      const copy = nativeSlice.call(xs, 0);\n      copy.sort(comparator);\n      return copy;\n    };\n\n    const keys = Object.keys;\n    const hasOwnProperty = Object.hasOwnProperty;\n    const get = (obj, key) => {\n      return has(obj, key) ? Optional.from(obj[key]) : Optional.none();\n    };\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n\n    const cat = arr => {\n      const r = [];\n      const push = x => {\n        r.push(x);\n      };\n      for (let i = 0; i < arr.length; i++) {\n        arr[i].each(push);\n      }\n      return r;\n    };\n\n    const description = `<h1>Editor UI keyboard navigation</h1>\n\n<h2>Activating keyboard navigation</h2>\n\n<p>The sections of the outer UI of the editor - the menubar, toolbar, sidebar and footer - are all keyboard navigable. As such, there are multiple ways to activate keyboard navigation:</p>\n<ul>\n  <li>Focus the menubar: Alt + F9 (Windows) or &#x2325;F9 (MacOS)</li>\n  <li>Focus the toolbar: Alt + F10 (Windows) or &#x2325;F10 (MacOS)</li>\n  <li>Focus the footer: Alt + F11 (Windows) or &#x2325;F11 (MacOS)</li>\n</ul>\n\n<p>Focusing the menubar or toolbar will start keyboard navigation at the first item in the menubar or toolbar, which will be highlighted with a gray background. Focusing the footer will start keyboard navigation at the first item in the element path, which will be highlighted with an underline. </p>\n\n<h2>Moving between UI sections</h2>\n\n<p>When keyboard navigation is active, pressing tab will move the focus to the next major section of the UI, where applicable. These sections are:</p>\n<ul>\n  <li>the menubar</li>\n  <li>each group of the toolbar </li>\n  <li>the sidebar</li>\n  <li>the element path in the footer </li>\n  <li>the wordcount toggle button in the footer </li>\n  <li>the branding link in the footer </li>\n  <li>the editor resize handle in the footer</li>\n</ul>\n\n<p>Pressing shift + tab will move backwards through the same sections, except when moving from the footer to the toolbar. Focusing the element path then pressing shift + tab will move focus to the first toolbar group, not the last.</p>\n\n<h2>Moving within UI sections</h2>\n\n<p>Keyboard navigation within UI sections can usually be achieved using the left and right arrow keys. This includes:</p>\n<ul>\n  <li>moving between menus in the menubar</li>\n  <li>moving between buttons in a toolbar group</li>\n  <li>moving between items in the element path</li>\n</ul>\n\n<p>In all these UI sections, keyboard navigation will cycle within the section. For example, focusing the last button in a toolbar group then pressing right arrow will move focus to the first item in the same toolbar group. </p>\n\n<h1>Executing buttons</h1>\n\n<p>To execute a button, navigate the selection to the desired button and hit space or enter.</p>\n\n<h1>Opening, navigating and closing menus</h1>\n\n<p>When focusing a menubar button or a toolbar button with a menu, pressing space, enter or down arrow will open the menu. When the menu opens the first item will be selected. To move up or down the menu, press the up or down arrow key respectively. This is the same for submenus, which can also be opened and closed using the left and right arrow keys.</p>\n\n<p>To close any active menu, hit the escape key. When a menu is closed the selection will be restored to its previous selection. This also works for closing submenus.</p>\n\n<h1>Context toolbars and menus</h1>\n\n<p>To focus an open context toolbar such as the table context toolbar, press Ctrl + F9 (Windows) or &#x2303;F9 (MacOS).</p>\n\n<p>Context toolbar navigation is the same as toolbar navigation, and context menu navigation is the same as standard menu navigation.</p>\n\n<h1>Dialog navigation</h1>\n\n<p>There are two types of dialog UIs in TinyMCE: tabbed dialogs and non-tabbed dialogs.</p>\n\n<p>When a non-tabbed dialog is opened, the first interactive component in the dialog will be focused. Users can navigate between interactive components by pressing tab. This includes any footer buttons. Navigation will cycle back to the first dialog component if tab is pressed while focusing the last component in the dialog. Pressing shift + tab will navigate backwards.</p>\n\n<p>When a tabbed dialog is opened, the first button in the tab menu is focused. Pressing tab will navigate to the first interactive component in that tab, and will cycle through the tab\\u2019s components, the footer buttons, then back to the tab button. To switch to another tab, focus the tab button for the current tab, then use the arrow keys to cycle through the tab buttons.</p>`;\n    const tab$3 = () => {\n      const body = {\n        type: 'htmlpanel',\n        presets: 'document',\n        html: description\n      };\n      return {\n        name: 'keyboardnav',\n        title: 'Keyboard Navigation',\n        items: [body]\n      };\n    };\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.Env');\n\n    const convertText = source => {\n      const isMac = global$2.os.isMacOS() || global$2.os.isiOS();\n      const mac = {\n        alt: '&#x2325;',\n        ctrl: '&#x2303;',\n        shift: '&#x21E7;',\n        meta: '&#x2318;',\n        access: '&#x2303;&#x2325;'\n      };\n      const other = {\n        meta: 'Ctrl ',\n        access: 'Shift + Alt '\n      };\n      const replace = isMac ? mac : other;\n      const shortcut = source.split('+');\n      const updated = map(shortcut, segment => {\n        const search = segment.toLowerCase().trim();\n        return has(replace, search) ? replace[search] : segment;\n      });\n      return isMac ? updated.join('').replace(/\\s/, '') : updated.join('+');\n    };\n\n    const shortcuts = [\n      {\n        shortcuts: ['Meta + B'],\n        action: 'Bold'\n      },\n      {\n        shortcuts: ['Meta + I'],\n        action: 'Italic'\n      },\n      {\n        shortcuts: ['Meta + U'],\n        action: 'Underline'\n      },\n      {\n        shortcuts: ['Meta + A'],\n        action: 'Select all'\n      },\n      {\n        shortcuts: [\n          'Meta + Y',\n          'Meta + Shift + Z'\n        ],\n        action: 'Redo'\n      },\n      {\n        shortcuts: ['Meta + Z'],\n        action: 'Undo'\n      },\n      {\n        shortcuts: ['Access + 1'],\n        action: 'Heading 1'\n      },\n      {\n        shortcuts: ['Access + 2'],\n        action: 'Heading 2'\n      },\n      {\n        shortcuts: ['Access + 3'],\n        action: 'Heading 3'\n      },\n      {\n        shortcuts: ['Access + 4'],\n        action: 'Heading 4'\n      },\n      {\n        shortcuts: ['Access + 5'],\n        action: 'Heading 5'\n      },\n      {\n        shortcuts: ['Access + 6'],\n        action: 'Heading 6'\n      },\n      {\n        shortcuts: ['Access + 7'],\n        action: 'Paragraph'\n      },\n      {\n        shortcuts: ['Access + 8'],\n        action: 'Div'\n      },\n      {\n        shortcuts: ['Access + 9'],\n        action: 'Address'\n      },\n      {\n        shortcuts: ['Alt + 0'],\n        action: 'Open help dialog'\n      },\n      {\n        shortcuts: ['Alt + F9'],\n        action: 'Focus to menubar'\n      },\n      {\n        shortcuts: ['Alt + F10'],\n        action: 'Focus to toolbar'\n      },\n      {\n        shortcuts: ['Alt + F11'],\n        action: 'Focus to element path'\n      },\n      {\n        shortcuts: ['Ctrl + F9'],\n        action: 'Focus to contextual toolbar'\n      },\n      {\n        shortcuts: ['Shift + Enter'],\n        action: 'Open popup menu for split buttons'\n      },\n      {\n        shortcuts: ['Meta + K'],\n        action: 'Insert link (if link plugin activated)'\n      },\n      {\n        shortcuts: ['Meta + S'],\n        action: 'Save (if save plugin activated)'\n      },\n      {\n        shortcuts: ['Meta + F'],\n        action: 'Find (if searchreplace plugin activated)'\n      },\n      {\n        shortcuts: ['Meta + Shift + F'],\n        action: 'Switch to or from fullscreen mode'\n      }\n    ];\n\n    const tab$2 = () => {\n      const shortcutList = map(shortcuts, shortcut => {\n        const shortcutText = map(shortcut.shortcuts, convertText).join(' or ');\n        return [\n          shortcut.action,\n          shortcutText\n        ];\n      });\n      const tablePanel = {\n        type: 'table',\n        header: [\n          'Action',\n          'Shortcut'\n        ],\n        cells: shortcutList\n      };\n      return {\n        name: 'shortcuts',\n        title: 'Handy Shortcuts',\n        items: [tablePanel]\n      };\n    };\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.I18n');\n\n    const urls = map([\n      {\n        key: 'advlist',\n        name: 'Advanced List'\n      },\n      {\n        key: 'anchor',\n        name: 'Anchor'\n      },\n      {\n        key: 'autolink',\n        name: 'Autolink'\n      },\n      {\n        key: 'autoresize',\n        name: 'Autoresize'\n      },\n      {\n        key: 'autosave',\n        name: 'Autosave'\n      },\n      {\n        key: 'charmap',\n        name: 'Character Map'\n      },\n      {\n        key: 'code',\n        name: 'Code'\n      },\n      {\n        key: 'codesample',\n        name: 'Code Sample'\n      },\n      {\n        key: 'colorpicker',\n        name: 'Color Picker'\n      },\n      {\n        key: 'directionality',\n        name: 'Directionality'\n      },\n      {\n        key: 'emoticons',\n        name: 'Emoticons'\n      },\n      {\n        key: 'fullscreen',\n        name: 'Full Screen'\n      },\n      {\n        key: 'help',\n        name: 'Help'\n      },\n      {\n        key: 'image',\n        name: 'Image'\n      },\n      {\n        key: 'importcss',\n        name: 'Import CSS'\n      },\n      {\n        key: 'insertdatetime',\n        name: 'Insert Date/Time'\n      },\n      {\n        key: 'link',\n        name: 'Link'\n      },\n      {\n        key: 'lists',\n        name: 'Lists'\n      },\n      {\n        key: 'media',\n        name: 'Media'\n      },\n      {\n        key: 'nonbreaking',\n        name: 'Nonbreaking'\n      },\n      {\n        key: 'pagebreak',\n        name: 'Page Break'\n      },\n      {\n        key: 'preview',\n        name: 'Preview'\n      },\n      {\n        key: 'quickbars',\n        name: 'Quick Toolbars'\n      },\n      {\n        key: 'save',\n        name: 'Save'\n      },\n      {\n        key: 'searchreplace',\n        name: 'Search and Replace'\n      },\n      {\n        key: 'table',\n        name: 'Table'\n      },\n      {\n        key: 'template',\n        name: 'Template'\n      },\n      {\n        key: 'textcolor',\n        name: 'Text Color'\n      },\n      {\n        key: 'visualblocks',\n        name: 'Visual Blocks'\n      },\n      {\n        key: 'visualchars',\n        name: 'Visual Characters'\n      },\n      {\n        key: 'wordcount',\n        name: 'Word Count'\n      },\n      {\n        key: 'a11ychecker',\n        name: 'Accessibility Checker',\n        type: 'premium'\n      },\n      {\n        key: 'advcode',\n        name: 'Advanced Code Editor',\n        type: 'premium'\n      },\n      {\n        key: 'advtable',\n        name: 'Advanced Tables',\n        type: 'premium'\n      },\n      {\n        key: 'casechange',\n        name: 'Case Change',\n        type: 'premium'\n      },\n      {\n        key: 'checklist',\n        name: 'Checklist',\n        type: 'premium'\n      },\n      {\n        key: 'editimage',\n        name: 'Enhanced Image Editing',\n        type: 'premium'\n      },\n      {\n        key: 'footnotes',\n        name: 'Footnotes',\n        type: 'premium'\n      },\n      {\n        key: 'typography',\n        name: 'Advanced Typography',\n        type: 'premium'\n      },\n      {\n        key: 'mediaembed',\n        name: 'Enhanced Media Embed',\n        type: 'premium',\n        slug: 'introduction-to-mediaembed'\n      },\n      {\n        key: 'export',\n        name: 'Export',\n        type: 'premium'\n      },\n      {\n        key: 'formatpainter',\n        name: 'Format Painter',\n        type: 'premium'\n      },\n      {\n        key: 'inlinecss',\n        name: 'Inline CSS',\n        type: 'premium'\n      },\n      {\n        key: 'linkchecker',\n        name: 'Link Checker',\n        type: 'premium'\n      },\n      {\n        key: 'mentions',\n        name: 'Mentions',\n        type: 'premium'\n      },\n      {\n        key: 'mergetags',\n        name: 'Merge Tags',\n        type: 'premium'\n      },\n      {\n        key: 'pageembed',\n        name: 'Page Embed',\n        type: 'premium'\n      },\n      {\n        key: 'permanentpen',\n        name: 'Permanent Pen',\n        type: 'premium'\n      },\n      {\n        key: 'powerpaste',\n        name: 'PowerPaste',\n        type: 'premium',\n        slug: 'introduction-to-powerpaste'\n      },\n      {\n        key: 'rtc',\n        name: 'Real-Time Collaboration',\n        type: 'premium',\n        slug: 'rtc-introduction'\n      },\n      {\n        key: 'tinymcespellchecker',\n        name: 'Spell Checker Pro',\n        type: 'premium',\n        slug: 'introduction-to-tiny-spellchecker'\n      },\n      {\n        key: 'autocorrect',\n        name: 'Spelling Autocorrect',\n        type: 'premium'\n      },\n      {\n        key: 'tableofcontents',\n        name: 'Table of Contents',\n        type: 'premium'\n      },\n      {\n        key: 'tinycomments',\n        name: 'Tiny Comments',\n        type: 'premium',\n        slug: 'introduction-to-tiny-comments'\n      },\n      {\n        key: 'tinydrive',\n        name: 'Tiny Drive',\n        type: 'premium',\n        slug: 'tinydrive-introduction'\n      }\n    ], item => ({\n      ...item,\n      type: item.type || 'opensource',\n      slug: item.slug || item.key\n    }));\n\n    const tab$1 = editor => {\n      const availablePlugins = () => {\n        const premiumPlugins = filter(urls, ({type}) => {\n          return type === 'premium';\n        });\n        const sortedPremiumPlugins = sort(map(premiumPlugins, p => p.name), (s1, s2) => s1.localeCompare(s2));\n        const premiumPluginList = map(sortedPremiumPlugins, pluginName => `<li>${ pluginName }</li>`).join('');\n        return '<div data-mce-tabstop=\"1\" tabindex=\"-1\">' + '<p><b>' + global$1.translate('Premium plugins:') + '</b></p>' + '<ul>' + premiumPluginList + '<li class=\"tox-help__more-link\" \"><a href=\"https://www.tiny.cloud/pricing/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce\" rel=\"noopener\" target=\"_blank\">' + global$1.translate('Learn more...') + '</a></li>' + '</ul>' + '</div>';\n      };\n      const makeLink = p => `<a href=\"${ p.url }\" target=\"_blank\" rel=\"noopener\">${ p.name }</a>`;\n      const identifyUnknownPlugin = (editor, key) => {\n        const getMetadata = editor.plugins[key].getMetadata;\n        if (isFunction(getMetadata)) {\n          const metadata = getMetadata();\n          return {\n            name: metadata.name,\n            html: makeLink(metadata)\n          };\n        } else {\n          return {\n            name: key,\n            html: key\n          };\n        }\n      };\n      const getPluginData = (editor, key) => find(urls, x => {\n        return x.key === key;\n      }).fold(() => {\n        return identifyUnknownPlugin(editor, key);\n      }, x => {\n        const name = x.type === 'premium' ? `${ x.name }*` : x.name;\n        const html = makeLink({\n          name,\n          url: `https://www.tiny.cloud/docs/tinymce/6/${ x.slug }/`\n        });\n        return {\n          name,\n          html\n        };\n      });\n      const getPluginKeys = editor => {\n        const keys$1 = keys(editor.plugins);\n        const forcedPlugins = getForcedPlugins(editor);\n        return isUndefined(forcedPlugins) ? keys$1 : filter(keys$1, k => !contains(forcedPlugins, k));\n      };\n      const pluginLister = editor => {\n        const pluginKeys = getPluginKeys(editor);\n        const sortedPluginData = sort(map(pluginKeys, k => getPluginData(editor, k)), (pd1, pd2) => pd1.name.localeCompare(pd2.name));\n        const pluginLis = map(sortedPluginData, key => {\n          return '<li>' + key.html + '</li>';\n        });\n        const count = pluginLis.length;\n        const pluginsString = pluginLis.join('');\n        const html = '<p><b>' + global$1.translate([\n          'Plugins installed ({0}):',\n          count\n        ]) + '</b></p>' + '<ul>' + pluginsString + '</ul>';\n        return html;\n      };\n      const installedPlugins = editor => {\n        if (editor == null) {\n          return '';\n        }\n        return '<div data-mce-tabstop=\"1\" tabindex=\"-1\">' + pluginLister(editor) + '</div>';\n      };\n      const htmlPanel = {\n        type: 'htmlpanel',\n        presets: 'document',\n        html: [\n          installedPlugins(editor),\n          availablePlugins()\n        ].join('')\n      };\n      return {\n        name: 'plugins',\n        title: 'Plugins',\n        items: [htmlPanel]\n      };\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.EditorManager');\n\n    const tab = () => {\n      const getVersion = (major, minor) => major.indexOf('@') === 0 ? 'X.X.X' : major + '.' + minor;\n      const version = getVersion(global.majorVersion, global.minorVersion);\n      const changeLogLink = '<a href=\"https://www.tiny.cloud/docs/tinymce/6/changelog/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce\" rel=\"noopener\" target=\"_blank\">TinyMCE ' + version + '</a>';\n      const htmlPanel = {\n        type: 'htmlpanel',\n        html: '<p>' + global$1.translate([\n          'You are using {0}',\n          changeLogLink\n        ]) + '</p>',\n        presets: 'document'\n      };\n      return {\n        name: 'versions',\n        title: 'Version',\n        items: [htmlPanel]\n      };\n    };\n\n    const parseHelpTabsSetting = (tabsFromSettings, tabs) => {\n      const newTabs = {};\n      const names = map(tabsFromSettings, t => {\n        var _a;\n        if (isString(t)) {\n          if (has(tabs, t)) {\n            newTabs[t] = tabs[t];\n          }\n          return t;\n        } else {\n          const name = (_a = t.name) !== null && _a !== void 0 ? _a : generate('tab-name');\n          newTabs[name] = t;\n          return name;\n        }\n      });\n      return {\n        tabs: newTabs,\n        names\n      };\n    };\n    const getNamesFromTabs = tabs => {\n      const names = keys(tabs);\n      const idx = names.indexOf('versions');\n      if (idx !== -1) {\n        names.splice(idx, 1);\n        names.push('versions');\n      }\n      return {\n        tabs,\n        names\n      };\n    };\n    const parseCustomTabs = (editor, customTabs) => {\n      const shortcuts = tab$2();\n      const nav = tab$3();\n      const plugins = tab$1(editor);\n      const versions = tab();\n      const tabs = {\n        [shortcuts.name]: shortcuts,\n        [nav.name]: nav,\n        [plugins.name]: plugins,\n        [versions.name]: versions,\n        ...customTabs.get()\n      };\n      return Optional.from(getHelpTabs(editor)).fold(() => getNamesFromTabs(tabs), tabsFromSettings => parseHelpTabsSetting(tabsFromSettings, tabs));\n    };\n    const init = (editor, customTabs) => () => {\n      const {tabs, names} = parseCustomTabs(editor, customTabs);\n      const foundTabs = map(names, name => get(tabs, name));\n      const dialogTabs = cat(foundTabs);\n      const body = {\n        type: 'tabpanel',\n        tabs: dialogTabs\n      };\n      editor.windowManager.open({\n        title: 'Help',\n        size: 'medium',\n        body,\n        buttons: [{\n            type: 'cancel',\n            name: 'close',\n            text: 'Close',\n            primary: true\n          }],\n        initialData: {}\n      });\n    };\n\n    var Plugin = () => {\n      global$3.add('help', editor => {\n        const customTabs = Cell({});\n        const api = get$1(customTabs);\n        register$1(editor);\n        const dialogOpener = init(editor, customTabs);\n        register(editor, dialogOpener);\n        register$2(editor, dialogOpener);\n        editor.shortcuts.add('Alt+0', 'Open help dialog', 'mceHelp');\n        return api;\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/image/index.js",
    "content": "// Exports the \"image\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/image')\n//   ES2015:\n//     import 'tinymce/plugins/image'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/image/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const getPrototypeOf = Object.getPrototypeOf;\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq = t => a => t === a;\n    const is = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf(o) === proto);\n    const isString = isType('string');\n    const isObject = isType('object');\n    const isPlainObject = value => is(value, Object);\n    const isArray = isType('array');\n    const isNull = eq(null);\n    const isBoolean = isSimpleType('boolean');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n    const isArrayOf = (value, pred) => {\n      if (isArray(value)) {\n        for (let i = 0, len = value.length; i < len; ++i) {\n          if (!pred(value[i])) {\n            return false;\n          }\n        }\n        return true;\n      }\n      return false;\n    };\n\n    const noop = () => {\n    };\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const keys = Object.keys;\n    const hasOwnProperty = Object.hasOwnProperty;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const objAcc = r => (x, i) => {\n      r[i] = x;\n    };\n    const internalFilter = (obj, pred, onTrue, onFalse) => {\n      each(obj, (x, i) => {\n        (pred(x, i) ? onTrue : onFalse)(x, i);\n      });\n    };\n    const filter = (obj, pred) => {\n      const t = {};\n      internalFilter(obj, pred, objAcc(t), noop);\n      return t;\n    };\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;\n\n    const nativePush = Array.prototype.push;\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const get = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get(xs, 0);\n    const findMap = (arr, f) => {\n      for (let i = 0; i < arr.length; i++) {\n        const r = f(arr[i], i);\n        if (r.isSome()) {\n          return r;\n        }\n      }\n      return Optional.none();\n    };\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const remove = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom(node);\n    };\n    const fromDom = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom,\n      fromPoint\n    };\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI');\n\n    const isNotEmpty = s => s.length > 0;\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('image_dimensions', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('image_advtab', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('image_uploadtab', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('image_prepend_url', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('image_class_list', { processor: 'object[]' });\n      registerOption('image_description', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('image_title', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('image_caption', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('image_list', {\n        processor: value => {\n          const valid = value === false || isString(value) || isArrayOf(value, isObject) || isFunction(value);\n          return valid ? {\n            value,\n            valid\n          } : {\n            valid: false,\n            message: 'Must be false, a string, an array or a function.'\n          };\n        },\n        default: false\n      });\n    };\n    const hasDimensions = option('image_dimensions');\n    const hasAdvTab = option('image_advtab');\n    const hasUploadTab = option('image_uploadtab');\n    const getPrependUrl = option('image_prepend_url');\n    const getClassList = option('image_class_list');\n    const hasDescription = option('image_description');\n    const hasImageTitle = option('image_title');\n    const hasImageCaption = option('image_caption');\n    const getImageList = option('image_list');\n    const showAccessibilityOptions = option('a11y_advanced_options');\n    const isAutomaticUploadsEnabled = option('automatic_uploads');\n    const hasUploadUrl = editor => isNotEmpty(editor.options.get('images_upload_url'));\n    const hasUploadHandler = editor => isNonNullable(editor.options.get('images_upload_handler'));\n\n    const parseIntAndGetMax = (val1, val2) => Math.max(parseInt(val1, 10), parseInt(val2, 10));\n    const getImageSize = url => new Promise(callback => {\n      const img = document.createElement('img');\n      const done = dimensions => {\n        img.onload = img.onerror = null;\n        if (img.parentNode) {\n          img.parentNode.removeChild(img);\n        }\n        callback(dimensions);\n      };\n      img.onload = () => {\n        const width = parseIntAndGetMax(img.width, img.clientWidth);\n        const height = parseIntAndGetMax(img.height, img.clientHeight);\n        const dimensions = {\n          width,\n          height\n        };\n        done(Promise.resolve(dimensions));\n      };\n      img.onerror = () => {\n        done(Promise.reject(`Failed to get image dimensions for: ${ url }`));\n      };\n      const style = img.style;\n      style.visibility = 'hidden';\n      style.position = 'fixed';\n      style.bottom = style.left = '0px';\n      style.width = style.height = 'auto';\n      document.body.appendChild(img);\n      img.src = url;\n    });\n    const removePixelSuffix = value => {\n      if (value) {\n        value = value.replace(/px$/, '');\n      }\n      return value;\n    };\n    const addPixelSuffix = value => {\n      if (value.length > 0 && /^[0-9]+$/.test(value)) {\n        value += 'px';\n      }\n      return value;\n    };\n    const mergeMargins = css => {\n      if (css.margin) {\n        const splitMargin = String(css.margin).split(' ');\n        switch (splitMargin.length) {\n        case 1:\n          css['margin-top'] = css['margin-top'] || splitMargin[0];\n          css['margin-right'] = css['margin-right'] || splitMargin[0];\n          css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];\n          css['margin-left'] = css['margin-left'] || splitMargin[0];\n          break;\n        case 2:\n          css['margin-top'] = css['margin-top'] || splitMargin[0];\n          css['margin-right'] = css['margin-right'] || splitMargin[1];\n          css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];\n          css['margin-left'] = css['margin-left'] || splitMargin[1];\n          break;\n        case 3:\n          css['margin-top'] = css['margin-top'] || splitMargin[0];\n          css['margin-right'] = css['margin-right'] || splitMargin[1];\n          css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];\n          css['margin-left'] = css['margin-left'] || splitMargin[1];\n          break;\n        case 4:\n          css['margin-top'] = css['margin-top'] || splitMargin[0];\n          css['margin-right'] = css['margin-right'] || splitMargin[1];\n          css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];\n          css['margin-left'] = css['margin-left'] || splitMargin[3];\n        }\n        delete css.margin;\n      }\n      return css;\n    };\n    const createImageList = (editor, callback) => {\n      const imageList = getImageList(editor);\n      if (isString(imageList)) {\n        fetch(imageList).then(res => {\n          if (res.ok) {\n            res.json().then(callback);\n          }\n        });\n      } else if (isFunction(imageList)) {\n        imageList(callback);\n      } else {\n        callback(imageList);\n      }\n    };\n    const waitLoadImage = (editor, data, imgElm) => {\n      const selectImage = () => {\n        imgElm.onload = imgElm.onerror = null;\n        if (editor.selection) {\n          editor.selection.select(imgElm);\n          editor.nodeChanged();\n        }\n      };\n      imgElm.onload = () => {\n        if (!data.width && !data.height && hasDimensions(editor)) {\n          editor.dom.setAttribs(imgElm, {\n            width: String(imgElm.clientWidth),\n            height: String(imgElm.clientHeight)\n          });\n        }\n        selectImage();\n      };\n      imgElm.onerror = selectImage;\n    };\n    const blobToDataUri = blob => new Promise((resolve, reject) => {\n      const reader = new FileReader();\n      reader.onload = () => {\n        resolve(reader.result);\n      };\n      reader.onerror = () => {\n        var _a;\n        reject((_a = reader.error) === null || _a === void 0 ? void 0 : _a.message);\n      };\n      reader.readAsDataURL(blob);\n    });\n    const isPlaceholderImage = imgElm => imgElm.nodeName === 'IMG' && (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n    const isSafeImageUrl = (editor, src) => {\n      const getOption = editor.options.get;\n      return global$2.isDomSafe(src, 'img', {\n        allow_html_data_urls: getOption('allow_html_data_urls'),\n        allow_script_urls: getOption('allow_script_urls'),\n        allow_svg_data_urls: getOption('allow_svg_data_urls')\n      });\n    };\n\n    const DOM = global$3.DOM;\n    const getHspace = image => {\n      if (image.style.marginLeft && image.style.marginRight && image.style.marginLeft === image.style.marginRight) {\n        return removePixelSuffix(image.style.marginLeft);\n      } else {\n        return '';\n      }\n    };\n    const getVspace = image => {\n      if (image.style.marginTop && image.style.marginBottom && image.style.marginTop === image.style.marginBottom) {\n        return removePixelSuffix(image.style.marginTop);\n      } else {\n        return '';\n      }\n    };\n    const getBorder = image => {\n      if (image.style.borderWidth) {\n        return removePixelSuffix(image.style.borderWidth);\n      } else {\n        return '';\n      }\n    };\n    const getAttrib = (image, name) => {\n      var _a;\n      if (image.hasAttribute(name)) {\n        return (_a = image.getAttribute(name)) !== null && _a !== void 0 ? _a : '';\n      } else {\n        return '';\n      }\n    };\n    const hasCaption = image => image.parentNode !== null && image.parentNode.nodeName === 'FIGURE';\n    const updateAttrib = (image, name, value) => {\n      if (value === '' || value === null) {\n        image.removeAttribute(name);\n      } else {\n        image.setAttribute(name, value);\n      }\n    };\n    const wrapInFigure = image => {\n      const figureElm = DOM.create('figure', { class: 'image' });\n      DOM.insertAfter(figureElm, image);\n      figureElm.appendChild(image);\n      figureElm.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption'));\n      figureElm.contentEditable = 'false';\n    };\n    const removeFigure = image => {\n      const figureElm = image.parentNode;\n      if (isNonNullable(figureElm)) {\n        DOM.insertAfter(image, figureElm);\n        DOM.remove(figureElm);\n      }\n    };\n    const toggleCaption = image => {\n      if (hasCaption(image)) {\n        removeFigure(image);\n      } else {\n        wrapInFigure(image);\n      }\n    };\n    const normalizeStyle = (image, normalizeCss) => {\n      const attrValue = image.getAttribute('style');\n      const value = normalizeCss(attrValue !== null ? attrValue : '');\n      if (value.length > 0) {\n        image.setAttribute('style', value);\n        image.setAttribute('data-mce-style', value);\n      } else {\n        image.removeAttribute('style');\n      }\n    };\n    const setSize = (name, normalizeCss) => (image, name, value) => {\n      const styles = image.style;\n      if (styles[name]) {\n        styles[name] = addPixelSuffix(value);\n        normalizeStyle(image, normalizeCss);\n      } else {\n        updateAttrib(image, name, value);\n      }\n    };\n    const getSize = (image, name) => {\n      if (image.style[name]) {\n        return removePixelSuffix(image.style[name]);\n      } else {\n        return getAttrib(image, name);\n      }\n    };\n    const setHspace = (image, value) => {\n      const pxValue = addPixelSuffix(value);\n      image.style.marginLeft = pxValue;\n      image.style.marginRight = pxValue;\n    };\n    const setVspace = (image, value) => {\n      const pxValue = addPixelSuffix(value);\n      image.style.marginTop = pxValue;\n      image.style.marginBottom = pxValue;\n    };\n    const setBorder = (image, value) => {\n      const pxValue = addPixelSuffix(value);\n      image.style.borderWidth = pxValue;\n    };\n    const setBorderStyle = (image, value) => {\n      image.style.borderStyle = value;\n    };\n    const getBorderStyle = image => {\n      var _a;\n      return (_a = image.style.borderStyle) !== null && _a !== void 0 ? _a : '';\n    };\n    const isFigure = elm => isNonNullable(elm) && elm.nodeName === 'FIGURE';\n    const isImage = elm => elm.nodeName === 'IMG';\n    const getIsDecorative = image => DOM.getAttrib(image, 'alt').length === 0 && DOM.getAttrib(image, 'role') === 'presentation';\n    const getAlt = image => {\n      if (getIsDecorative(image)) {\n        return '';\n      } else {\n        return getAttrib(image, 'alt');\n      }\n    };\n    const defaultData = () => ({\n      src: '',\n      alt: '',\n      title: '',\n      width: '',\n      height: '',\n      class: '',\n      style: '',\n      caption: false,\n      hspace: '',\n      vspace: '',\n      border: '',\n      borderStyle: '',\n      isDecorative: false\n    });\n    const getStyleValue = (normalizeCss, data) => {\n      var _a;\n      const image = document.createElement('img');\n      updateAttrib(image, 'style', data.style);\n      if (getHspace(image) || data.hspace !== '') {\n        setHspace(image, data.hspace);\n      }\n      if (getVspace(image) || data.vspace !== '') {\n        setVspace(image, data.vspace);\n      }\n      if (getBorder(image) || data.border !== '') {\n        setBorder(image, data.border);\n      }\n      if (getBorderStyle(image) || data.borderStyle !== '') {\n        setBorderStyle(image, data.borderStyle);\n      }\n      return normalizeCss((_a = image.getAttribute('style')) !== null && _a !== void 0 ? _a : '');\n    };\n    const create = (normalizeCss, data) => {\n      const image = document.createElement('img');\n      write(normalizeCss, {\n        ...data,\n        caption: false\n      }, image);\n      setAlt(image, data.alt, data.isDecorative);\n      if (data.caption) {\n        const figure = DOM.create('figure', { class: 'image' });\n        figure.appendChild(image);\n        figure.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption'));\n        figure.contentEditable = 'false';\n        return figure;\n      } else {\n        return image;\n      }\n    };\n    const read = (normalizeCss, image) => ({\n      src: getAttrib(image, 'src'),\n      alt: getAlt(image),\n      title: getAttrib(image, 'title'),\n      width: getSize(image, 'width'),\n      height: getSize(image, 'height'),\n      class: getAttrib(image, 'class'),\n      style: normalizeCss(getAttrib(image, 'style')),\n      caption: hasCaption(image),\n      hspace: getHspace(image),\n      vspace: getVspace(image),\n      border: getBorder(image),\n      borderStyle: getBorderStyle(image),\n      isDecorative: getIsDecorative(image)\n    });\n    const updateProp = (image, oldData, newData, name, set) => {\n      if (newData[name] !== oldData[name]) {\n        set(image, name, String(newData[name]));\n      }\n    };\n    const setAlt = (image, alt, isDecorative) => {\n      if (isDecorative) {\n        DOM.setAttrib(image, 'role', 'presentation');\n        const sugarImage = SugarElement.fromDom(image);\n        set(sugarImage, 'alt', '');\n      } else {\n        if (isNull(alt)) {\n          const sugarImage = SugarElement.fromDom(image);\n          remove(sugarImage, 'alt');\n        } else {\n          const sugarImage = SugarElement.fromDom(image);\n          set(sugarImage, 'alt', alt);\n        }\n        if (DOM.getAttrib(image, 'role') === 'presentation') {\n          DOM.setAttrib(image, 'role', '');\n        }\n      }\n    };\n    const updateAlt = (image, oldData, newData) => {\n      if (newData.alt !== oldData.alt || newData.isDecorative !== oldData.isDecorative) {\n        setAlt(image, newData.alt, newData.isDecorative);\n      }\n    };\n    const normalized = (set, normalizeCss) => (image, name, value) => {\n      set(image, value);\n      normalizeStyle(image, normalizeCss);\n    };\n    const write = (normalizeCss, newData, image) => {\n      const oldData = read(normalizeCss, image);\n      updateProp(image, oldData, newData, 'caption', (image, _name, _value) => toggleCaption(image));\n      updateProp(image, oldData, newData, 'src', updateAttrib);\n      updateProp(image, oldData, newData, 'title', updateAttrib);\n      updateProp(image, oldData, newData, 'width', setSize('width', normalizeCss));\n      updateProp(image, oldData, newData, 'height', setSize('height', normalizeCss));\n      updateProp(image, oldData, newData, 'class', updateAttrib);\n      updateProp(image, oldData, newData, 'style', normalized((image, value) => updateAttrib(image, 'style', value), normalizeCss));\n      updateProp(image, oldData, newData, 'hspace', normalized(setHspace, normalizeCss));\n      updateProp(image, oldData, newData, 'vspace', normalized(setVspace, normalizeCss));\n      updateProp(image, oldData, newData, 'border', normalized(setBorder, normalizeCss));\n      updateProp(image, oldData, newData, 'borderStyle', normalized(setBorderStyle, normalizeCss));\n      updateAlt(image, oldData, newData);\n    };\n\n    const normalizeCss$1 = (editor, cssText) => {\n      const css = editor.dom.styles.parse(cssText);\n      const mergedCss = mergeMargins(css);\n      const compressed = editor.dom.styles.parse(editor.dom.styles.serialize(mergedCss));\n      return editor.dom.styles.serialize(compressed);\n    };\n    const getSelectedImage = editor => {\n      const imgElm = editor.selection.getNode();\n      const figureElm = editor.dom.getParent(imgElm, 'figure.image');\n      if (figureElm) {\n        return editor.dom.select('img', figureElm)[0];\n      }\n      if (imgElm && (imgElm.nodeName !== 'IMG' || isPlaceholderImage(imgElm))) {\n        return null;\n      }\n      return imgElm;\n    };\n    const splitTextBlock = (editor, figure) => {\n      var _a;\n      const dom = editor.dom;\n      const textBlockElements = filter(editor.schema.getTextBlockElements(), (_, parentElm) => !editor.schema.isValidChild(parentElm, 'figure'));\n      const textBlock = dom.getParent(figure.parentNode, node => hasNonNullableKey(textBlockElements, node.nodeName), editor.getBody());\n      if (textBlock) {\n        return (_a = dom.split(textBlock, figure)) !== null && _a !== void 0 ? _a : figure;\n      } else {\n        return figure;\n      }\n    };\n    const readImageDataFromSelection = editor => {\n      const image = getSelectedImage(editor);\n      return image ? read(css => normalizeCss$1(editor, css), image) : defaultData();\n    };\n    const insertImageAtCaret = (editor, data) => {\n      const elm = create(css => normalizeCss$1(editor, css), data);\n      editor.dom.setAttrib(elm, 'data-mce-id', '__mcenew');\n      editor.focus();\n      editor.selection.setContent(elm.outerHTML);\n      const insertedElm = editor.dom.select('*[data-mce-id=\"__mcenew\"]')[0];\n      editor.dom.setAttrib(insertedElm, 'data-mce-id', null);\n      if (isFigure(insertedElm)) {\n        const figure = splitTextBlock(editor, insertedElm);\n        editor.selection.select(figure);\n      } else {\n        editor.selection.select(insertedElm);\n      }\n    };\n    const syncSrcAttr = (editor, image) => {\n      editor.dom.setAttrib(image, 'src', image.getAttribute('src'));\n    };\n    const deleteImage = (editor, image) => {\n      if (image) {\n        const elm = editor.dom.is(image.parentNode, 'figure.image') ? image.parentNode : image;\n        editor.dom.remove(elm);\n        editor.focus();\n        editor.nodeChanged();\n        if (editor.dom.isEmpty(editor.getBody())) {\n          editor.setContent('');\n          editor.selection.setCursorLocation();\n        }\n      }\n    };\n    const writeImageDataToSelection = (editor, data) => {\n      const image = getSelectedImage(editor);\n      if (image) {\n        write(css => normalizeCss$1(editor, css), data, image);\n        syncSrcAttr(editor, image);\n        if (isFigure(image.parentNode)) {\n          const figure = image.parentNode;\n          splitTextBlock(editor, figure);\n          editor.selection.select(image.parentNode);\n        } else {\n          editor.selection.select(image);\n          waitLoadImage(editor, data, image);\n        }\n      }\n    };\n    const sanitizeImageData = (editor, data) => {\n      const src = data.src;\n      return {\n        ...data,\n        src: isSafeImageUrl(editor, src) ? src : ''\n      };\n    };\n    const insertOrUpdateImage = (editor, partialData) => {\n      const image = getSelectedImage(editor);\n      if (image) {\n        const selectedImageData = read(css => normalizeCss$1(editor, css), image);\n        const data = {\n          ...selectedImageData,\n          ...partialData\n        };\n        const sanitizedData = sanitizeImageData(editor, data);\n        if (data.src) {\n          writeImageDataToSelection(editor, sanitizedData);\n        } else {\n          deleteImage(editor, image);\n        }\n      } else if (partialData.src) {\n        insertImageAtCaret(editor, {\n          ...defaultData(),\n          ...partialData\n        });\n      }\n    };\n\n    const deep = (old, nu) => {\n      const bothObjects = isPlainObject(old) && isPlainObject(nu);\n      return bothObjects ? deepMerge(old, nu) : nu;\n    };\n    const baseMerge = merger => {\n      return (...objects) => {\n        if (objects.length === 0) {\n          throw new Error(`Can't merge zero objects`);\n        }\n        const ret = {};\n        for (let j = 0; j < objects.length; j++) {\n          const curObject = objects[j];\n          for (const key in curObject) {\n            if (has(curObject, key)) {\n              ret[key] = merger(ret[key], curObject[key]);\n            }\n          }\n        }\n        return ret;\n      };\n    };\n    const deepMerge = baseMerge(deep);\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.ImageUploader');\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const getValue = item => isString(item.value) ? item.value : '';\n    const getText = item => {\n      if (isString(item.text)) {\n        return item.text;\n      } else if (isString(item.title)) {\n        return item.title;\n      } else {\n        return '';\n      }\n    };\n    const sanitizeList = (list, extractValue) => {\n      const out = [];\n      global.each(list, item => {\n        const text = getText(item);\n        if (item.menu !== undefined) {\n          const items = sanitizeList(item.menu, extractValue);\n          out.push({\n            text,\n            items\n          });\n        } else {\n          const value = extractValue(item);\n          out.push({\n            text,\n            value\n          });\n        }\n      });\n      return out;\n    };\n    const sanitizer = (extractor = getValue) => list => {\n      if (list) {\n        return Optional.from(list).map(list => sanitizeList(list, extractor));\n      } else {\n        return Optional.none();\n      }\n    };\n    const sanitize = list => sanitizer(getValue)(list);\n    const isGroup = item => has(item, 'items');\n    const findEntryDelegate = (list, value) => findMap(list, item => {\n      if (isGroup(item)) {\n        return findEntryDelegate(item.items, value);\n      } else if (item.value === value) {\n        return Optional.some(item);\n      } else {\n        return Optional.none();\n      }\n    });\n    const findEntry = (optList, value) => optList.bind(list => findEntryDelegate(list, value));\n    const ListUtils = {\n      sanitizer,\n      sanitize,\n      findEntry\n    };\n\n    const makeTab$2 = _info => ({\n      title: 'Advanced',\n      name: 'advanced',\n      items: [{\n          type: 'grid',\n          columns: 2,\n          items: [\n            {\n              type: 'input',\n              label: 'Vertical space',\n              name: 'vspace',\n              inputMode: 'numeric'\n            },\n            {\n              type: 'input',\n              label: 'Horizontal space',\n              name: 'hspace',\n              inputMode: 'numeric'\n            },\n            {\n              type: 'input',\n              label: 'Border width',\n              name: 'border',\n              inputMode: 'numeric'\n            },\n            {\n              type: 'listbox',\n              name: 'borderstyle',\n              label: 'Border style',\n              items: [\n                {\n                  text: 'Select...',\n                  value: ''\n                },\n                {\n                  text: 'Solid',\n                  value: 'solid'\n                },\n                {\n                  text: 'Dotted',\n                  value: 'dotted'\n                },\n                {\n                  text: 'Dashed',\n                  value: 'dashed'\n                },\n                {\n                  text: 'Double',\n                  value: 'double'\n                },\n                {\n                  text: 'Groove',\n                  value: 'groove'\n                },\n                {\n                  text: 'Ridge',\n                  value: 'ridge'\n                },\n                {\n                  text: 'Inset',\n                  value: 'inset'\n                },\n                {\n                  text: 'Outset',\n                  value: 'outset'\n                },\n                {\n                  text: 'None',\n                  value: 'none'\n                },\n                {\n                  text: 'Hidden',\n                  value: 'hidden'\n                }\n              ]\n            }\n          ]\n        }]\n    });\n    const AdvTab = { makeTab: makeTab$2 };\n\n    const collect = editor => {\n      const urlListSanitizer = ListUtils.sanitizer(item => editor.convertURL(item.value || item.url || '', 'src'));\n      const futureImageList = new Promise(completer => {\n        createImageList(editor, imageList => {\n          completer(urlListSanitizer(imageList).map(items => flatten([\n            [{\n                text: 'None',\n                value: ''\n              }],\n            items\n          ])));\n        });\n      });\n      const classList = ListUtils.sanitize(getClassList(editor));\n      const hasAdvTab$1 = hasAdvTab(editor);\n      const hasUploadTab$1 = hasUploadTab(editor);\n      const hasUploadUrl$1 = hasUploadUrl(editor);\n      const hasUploadHandler$1 = hasUploadHandler(editor);\n      const image = readImageDataFromSelection(editor);\n      const hasDescription$1 = hasDescription(editor);\n      const hasImageTitle$1 = hasImageTitle(editor);\n      const hasDimensions$1 = hasDimensions(editor);\n      const hasImageCaption$1 = hasImageCaption(editor);\n      const hasAccessibilityOptions = showAccessibilityOptions(editor);\n      const automaticUploads = isAutomaticUploadsEnabled(editor);\n      const prependURL = Optional.some(getPrependUrl(editor)).filter(preUrl => isString(preUrl) && preUrl.length > 0);\n      return futureImageList.then(imageList => ({\n        image,\n        imageList,\n        classList,\n        hasAdvTab: hasAdvTab$1,\n        hasUploadTab: hasUploadTab$1,\n        hasUploadUrl: hasUploadUrl$1,\n        hasUploadHandler: hasUploadHandler$1,\n        hasDescription: hasDescription$1,\n        hasImageTitle: hasImageTitle$1,\n        hasDimensions: hasDimensions$1,\n        hasImageCaption: hasImageCaption$1,\n        prependURL,\n        hasAccessibilityOptions,\n        automaticUploads\n      }));\n    };\n\n    const makeItems = info => {\n      const imageUrl = {\n        name: 'src',\n        type: 'urlinput',\n        filetype: 'image',\n        label: 'Source'\n      };\n      const imageList = info.imageList.map(items => ({\n        name: 'images',\n        type: 'listbox',\n        label: 'Image list',\n        items\n      }));\n      const imageDescription = {\n        name: 'alt',\n        type: 'input',\n        label: 'Alternative description',\n        enabled: !(info.hasAccessibilityOptions && info.image.isDecorative)\n      };\n      const imageTitle = {\n        name: 'title',\n        type: 'input',\n        label: 'Image title'\n      };\n      const imageDimensions = {\n        name: 'dimensions',\n        type: 'sizeinput'\n      };\n      const isDecorative = {\n        type: 'label',\n        label: 'Accessibility',\n        items: [{\n            name: 'isDecorative',\n            type: 'checkbox',\n            label: 'Image is decorative'\n          }]\n      };\n      const classList = info.classList.map(items => ({\n        name: 'classes',\n        type: 'listbox',\n        label: 'Class',\n        items\n      }));\n      const caption = {\n        type: 'label',\n        label: 'Caption',\n        items: [{\n            type: 'checkbox',\n            name: 'caption',\n            label: 'Show caption'\n          }]\n      };\n      const getDialogContainerType = useColumns => useColumns ? {\n        type: 'grid',\n        columns: 2\n      } : { type: 'panel' };\n      return flatten([\n        [imageUrl],\n        imageList.toArray(),\n        info.hasAccessibilityOptions && info.hasDescription ? [isDecorative] : [],\n        info.hasDescription ? [imageDescription] : [],\n        info.hasImageTitle ? [imageTitle] : [],\n        info.hasDimensions ? [imageDimensions] : [],\n        [{\n            ...getDialogContainerType(info.classList.isSome() && info.hasImageCaption),\n            items: flatten([\n              classList.toArray(),\n              info.hasImageCaption ? [caption] : []\n            ])\n          }]\n      ]);\n    };\n    const makeTab$1 = info => ({\n      title: 'General',\n      name: 'general',\n      items: makeItems(info)\n    });\n    const MainTab = {\n      makeTab: makeTab$1,\n      makeItems\n    };\n\n    const makeTab = _info => {\n      const items = [{\n          type: 'dropzone',\n          name: 'fileinput'\n        }];\n      return {\n        title: 'Upload',\n        name: 'upload',\n        items\n      };\n    };\n    const UploadTab = { makeTab };\n\n    const createState = info => ({\n      prevImage: ListUtils.findEntry(info.imageList, info.image.src),\n      prevAlt: info.image.alt,\n      open: true\n    });\n    const fromImageData = image => ({\n      src: {\n        value: image.src,\n        meta: {}\n      },\n      images: image.src,\n      alt: image.alt,\n      title: image.title,\n      dimensions: {\n        width: image.width,\n        height: image.height\n      },\n      classes: image.class,\n      caption: image.caption,\n      style: image.style,\n      vspace: image.vspace,\n      border: image.border,\n      hspace: image.hspace,\n      borderstyle: image.borderStyle,\n      fileinput: [],\n      isDecorative: image.isDecorative\n    });\n    const toImageData = (data, removeEmptyAlt) => ({\n      src: data.src.value,\n      alt: (data.alt === null || data.alt.length === 0) && removeEmptyAlt ? null : data.alt,\n      title: data.title,\n      width: data.dimensions.width,\n      height: data.dimensions.height,\n      class: data.classes,\n      style: data.style,\n      caption: data.caption,\n      hspace: data.hspace,\n      vspace: data.vspace,\n      border: data.border,\n      borderStyle: data.borderstyle,\n      isDecorative: data.isDecorative\n    });\n    const addPrependUrl2 = (info, srcURL) => {\n      if (!/^(?:[a-zA-Z]+:)?\\/\\//.test(srcURL)) {\n        return info.prependURL.bind(prependUrl => {\n          if (srcURL.substring(0, prependUrl.length) !== prependUrl) {\n            return Optional.some(prependUrl + srcURL);\n          }\n          return Optional.none();\n        });\n      }\n      return Optional.none();\n    };\n    const addPrependUrl = (info, api) => {\n      const data = api.getData();\n      addPrependUrl2(info, data.src.value).each(srcURL => {\n        api.setData({\n          src: {\n            value: srcURL,\n            meta: data.src.meta\n          }\n        });\n      });\n    };\n    const formFillFromMeta2 = (info, data, meta) => {\n      if (info.hasDescription && isString(meta.alt)) {\n        data.alt = meta.alt;\n      }\n      if (info.hasAccessibilityOptions) {\n        data.isDecorative = meta.isDecorative || data.isDecorative || false;\n      }\n      if (info.hasImageTitle && isString(meta.title)) {\n        data.title = meta.title;\n      }\n      if (info.hasDimensions) {\n        if (isString(meta.width)) {\n          data.dimensions.width = meta.width;\n        }\n        if (isString(meta.height)) {\n          data.dimensions.height = meta.height;\n        }\n      }\n      if (isString(meta.class)) {\n        ListUtils.findEntry(info.classList, meta.class).each(entry => {\n          data.classes = entry.value;\n        });\n      }\n      if (info.hasImageCaption) {\n        if (isBoolean(meta.caption)) {\n          data.caption = meta.caption;\n        }\n      }\n      if (info.hasAdvTab) {\n        if (isString(meta.style)) {\n          data.style = meta.style;\n        }\n        if (isString(meta.vspace)) {\n          data.vspace = meta.vspace;\n        }\n        if (isString(meta.border)) {\n          data.border = meta.border;\n        }\n        if (isString(meta.hspace)) {\n          data.hspace = meta.hspace;\n        }\n        if (isString(meta.borderstyle)) {\n          data.borderstyle = meta.borderstyle;\n        }\n      }\n    };\n    const formFillFromMeta = (info, api) => {\n      const data = api.getData();\n      const meta = data.src.meta;\n      if (meta !== undefined) {\n        const newData = deepMerge({}, data);\n        formFillFromMeta2(info, newData, meta);\n        api.setData(newData);\n      }\n    };\n    const calculateImageSize = (helpers, info, state, api) => {\n      const data = api.getData();\n      const url = data.src.value;\n      const meta = data.src.meta || {};\n      if (!meta.width && !meta.height && info.hasDimensions) {\n        if (isNotEmpty(url)) {\n          helpers.imageSize(url).then(size => {\n            if (state.open) {\n              api.setData({ dimensions: size });\n            }\n          }).catch(e => console.error(e));\n        } else {\n          api.setData({\n            dimensions: {\n              width: '',\n              height: ''\n            }\n          });\n        }\n      }\n    };\n    const updateImagesDropdown = (info, state, api) => {\n      const data = api.getData();\n      const image = ListUtils.findEntry(info.imageList, data.src.value);\n      state.prevImage = image;\n      api.setData({ images: image.map(entry => entry.value).getOr('') });\n    };\n    const changeSrc = (helpers, info, state, api) => {\n      addPrependUrl(info, api);\n      formFillFromMeta(info, api);\n      calculateImageSize(helpers, info, state, api);\n      updateImagesDropdown(info, state, api);\n    };\n    const changeImages = (helpers, info, state, api) => {\n      const data = api.getData();\n      const image = ListUtils.findEntry(info.imageList, data.images);\n      image.each(img => {\n        const updateAlt = data.alt === '' || state.prevImage.map(image => image.text === data.alt).getOr(false);\n        if (updateAlt) {\n          if (img.value === '') {\n            api.setData({\n              src: img,\n              alt: state.prevAlt\n            });\n          } else {\n            api.setData({\n              src: img,\n              alt: img.text\n            });\n          }\n        } else {\n          api.setData({ src: img });\n        }\n      });\n      state.prevImage = image;\n      changeSrc(helpers, info, state, api);\n    };\n    const changeFileInput = (helpers, info, state, api) => {\n      const data = api.getData();\n      api.block('Uploading image');\n      head(data.fileinput).fold(() => {\n        api.unblock();\n      }, file => {\n        const blobUri = URL.createObjectURL(file);\n        const finalize = () => {\n          api.unblock();\n          URL.revokeObjectURL(blobUri);\n        };\n        const updateSrcAndSwitchTab = url => {\n          api.setData({\n            src: {\n              value: url,\n              meta: {}\n            }\n          });\n          api.showTab('general');\n          changeSrc(helpers, info, state, api);\n        };\n        blobToDataUri(file).then(dataUrl => {\n          const blobInfo = helpers.createBlobCache(file, blobUri, dataUrl);\n          if (info.automaticUploads) {\n            helpers.uploadImage(blobInfo).then(result => {\n              updateSrcAndSwitchTab(result.url);\n              finalize();\n            }).catch(err => {\n              finalize();\n              helpers.alertErr(err);\n            });\n          } else {\n            helpers.addToBlobCache(blobInfo);\n            updateSrcAndSwitchTab(blobInfo.blobUri());\n            api.unblock();\n          }\n        });\n      });\n    };\n    const changeHandler = (helpers, info, state) => (api, evt) => {\n      if (evt.name === 'src') {\n        changeSrc(helpers, info, state, api);\n      } else if (evt.name === 'images') {\n        changeImages(helpers, info, state, api);\n      } else if (evt.name === 'alt') {\n        state.prevAlt = api.getData().alt;\n      } else if (evt.name === 'fileinput') {\n        changeFileInput(helpers, info, state, api);\n      } else if (evt.name === 'isDecorative') {\n        api.setEnabled('alt', !api.getData().isDecorative);\n      }\n    };\n    const closeHandler = state => () => {\n      state.open = false;\n    };\n    const makeDialogBody = info => {\n      if (info.hasAdvTab || info.hasUploadUrl || info.hasUploadHandler) {\n        const tabPanel = {\n          type: 'tabpanel',\n          tabs: flatten([\n            [MainTab.makeTab(info)],\n            info.hasAdvTab ? [AdvTab.makeTab(info)] : [],\n            info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : []\n          ])\n        };\n        return tabPanel;\n      } else {\n        const panel = {\n          type: 'panel',\n          items: MainTab.makeItems(info)\n        };\n        return panel;\n      }\n    };\n    const submitHandler = (editor, info, helpers) => api => {\n      const data = deepMerge(fromImageData(info.image), api.getData());\n      const finalData = {\n        ...data,\n        style: getStyleValue(helpers.normalizeCss, toImageData(data, false))\n      };\n      editor.execCommand('mceUpdateImage', false, toImageData(finalData, info.hasAccessibilityOptions));\n      editor.editorUpload.uploadImagesAuto();\n      api.close();\n    };\n    const imageSize = editor => url => {\n      if (!isSafeImageUrl(editor, url)) {\n        return Promise.resolve({\n          width: '',\n          height: ''\n        });\n      } else {\n        return getImageSize(editor.documentBaseURI.toAbsolute(url)).then(dimensions => ({\n          width: String(dimensions.width),\n          height: String(dimensions.height)\n        }));\n      }\n    };\n    const createBlobCache = editor => (file, blobUri, dataUrl) => {\n      var _a;\n      return editor.editorUpload.blobCache.create({\n        blob: file,\n        blobUri,\n        name: (_a = file.name) === null || _a === void 0 ? void 0 : _a.replace(/\\.[^\\.]+$/, ''),\n        filename: file.name,\n        base64: dataUrl.split(',')[1]\n      });\n    };\n    const addToBlobCache = editor => blobInfo => {\n      editor.editorUpload.blobCache.add(blobInfo);\n    };\n    const alertErr = editor => message => {\n      editor.windowManager.alert(message);\n    };\n    const normalizeCss = editor => cssText => normalizeCss$1(editor, cssText);\n    const parseStyle = editor => cssText => editor.dom.parseStyle(cssText);\n    const serializeStyle = editor => (stylesArg, name) => editor.dom.serializeStyle(stylesArg, name);\n    const uploadImage = editor => blobInfo => global$1(editor).upload([blobInfo], false).then(results => {\n      var _a;\n      if (results.length === 0) {\n        return Promise.reject('Failed to upload image');\n      } else if (results[0].status === false) {\n        return Promise.reject((_a = results[0].error) === null || _a === void 0 ? void 0 : _a.message);\n      } else {\n        return results[0];\n      }\n    });\n    const Dialog = editor => {\n      const helpers = {\n        imageSize: imageSize(editor),\n        addToBlobCache: addToBlobCache(editor),\n        createBlobCache: createBlobCache(editor),\n        alertErr: alertErr(editor),\n        normalizeCss: normalizeCss(editor),\n        parseStyle: parseStyle(editor),\n        serializeStyle: serializeStyle(editor),\n        uploadImage: uploadImage(editor)\n      };\n      const open = () => {\n        collect(editor).then(info => {\n          const state = createState(info);\n          return {\n            title: 'Insert/Edit Image',\n            size: 'normal',\n            body: makeDialogBody(info),\n            buttons: [\n              {\n                type: 'cancel',\n                name: 'cancel',\n                text: 'Cancel'\n              },\n              {\n                type: 'submit',\n                name: 'save',\n                text: 'Save',\n                primary: true\n              }\n            ],\n            initialData: fromImageData(info.image),\n            onSubmit: submitHandler(editor, info, helpers),\n            onChange: changeHandler(helpers, info, state),\n            onClose: closeHandler(state)\n          };\n        }).then(editor.windowManager.open);\n      };\n      return { open };\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mceImage', Dialog(editor).open);\n      editor.addCommand('mceUpdateImage', (_ui, data) => {\n        editor.undoManager.transact(() => insertOrUpdateImage(editor, data));\n      });\n    };\n\n    const hasImageClass = node => {\n      const className = node.attr('class');\n      return isNonNullable(className) && /\\bimage\\b/.test(className);\n    };\n    const toggleContentEditableState = state => nodes => {\n      let i = nodes.length;\n      const toggleContentEditable = node => {\n        node.attr('contenteditable', state ? 'true' : null);\n      };\n      while (i--) {\n        const node = nodes[i];\n        if (hasImageClass(node)) {\n          node.attr('contenteditable', state ? 'false' : null);\n          global.each(node.getAll('figcaption'), toggleContentEditable);\n        }\n      }\n    };\n    const setup = editor => {\n      editor.on('PreInit', () => {\n        editor.parser.addNodeFilter('figure', toggleContentEditableState(true));\n        editor.serializer.addNodeFilter('figure', toggleContentEditableState(false));\n      });\n    };\n\n    const register = editor => {\n      editor.ui.registry.addToggleButton('image', {\n        icon: 'image',\n        tooltip: 'Insert/edit image',\n        onAction: Dialog(editor).open,\n        onSetup: buttonApi => {\n          buttonApi.setActive(isNonNullable(getSelectedImage(editor)));\n          return editor.selection.selectorChangedWithUnbind('img:not([data-mce-object]):not([data-mce-placeholder]),figure.image', buttonApi.setActive).unbind;\n        }\n      });\n      editor.ui.registry.addMenuItem('image', {\n        icon: 'image',\n        text: 'Image...',\n        onAction: Dialog(editor).open\n      });\n      editor.ui.registry.addContextMenu('image', { update: element => isFigure(element) || isImage(element) && !isPlaceholderImage(element) ? ['image'] : [] });\n    };\n\n    var Plugin = () => {\n      global$4.add('image', editor => {\n        register$2(editor);\n        setup(editor);\n        register(editor);\n        register$1(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/importcss/index.js",
    "content": "// Exports the \"importcss\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/importcss')\n//   ES2015:\n//     import 'tinymce/plugins/importcss'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/importcss/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const isString = isType('string');\n    const isObject = isType('object');\n    const isArray = isType('array');\n    const isFunction = isSimpleType('function');\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.EditorManager');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.Env');\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const option = name => editor => editor.options.get(name);\n    const register = editor => {\n      const registerOption = editor.options.register;\n      const filterProcessor = value => isString(value) || isFunction(value) || isObject(value);\n      registerOption('importcss_merge_classes', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('importcss_exclusive', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('importcss_selector_converter', { processor: 'function' });\n      registerOption('importcss_selector_filter', { processor: filterProcessor });\n      registerOption('importcss_file_filter', { processor: filterProcessor });\n      registerOption('importcss_groups', { processor: 'object[]' });\n      registerOption('importcss_append', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const shouldMergeClasses = option('importcss_merge_classes');\n    const shouldImportExclusive = option('importcss_exclusive');\n    const getSelectorConverter = option('importcss_selector_converter');\n    const getSelectorFilter = option('importcss_selector_filter');\n    const getCssGroups = option('importcss_groups');\n    const shouldAppend = option('importcss_append');\n    const getFileFilter = option('importcss_file_filter');\n    const getSkin = option('skin');\n    const getSkinUrl = option('skin_url');\n\n    const nativePush = Array.prototype.push;\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind = (xs, f) => flatten(map(xs, f));\n\n    const generate = () => {\n      const ungroupedOrder = [];\n      const groupOrder = [];\n      const groups = {};\n      const addItemToGroup = (groupTitle, itemInfo) => {\n        if (groups[groupTitle]) {\n          groups[groupTitle].push(itemInfo);\n        } else {\n          groupOrder.push(groupTitle);\n          groups[groupTitle] = [itemInfo];\n        }\n      };\n      const addItem = itemInfo => {\n        ungroupedOrder.push(itemInfo);\n      };\n      const toFormats = () => {\n        const groupItems = bind(groupOrder, g => {\n          const items = groups[g];\n          return items.length === 0 ? [] : [{\n              title: g,\n              items\n            }];\n        });\n        return groupItems.concat(ungroupedOrder);\n      };\n      return {\n        addItemToGroup,\n        addItem,\n        toFormats\n      };\n    };\n\n    const internalEditorStyle = /^\\.(?:ephox|tiny-pageembed|mce)(?:[.-]+\\w+)+$/;\n    const removeCacheSuffix = url => {\n      const cacheSuffix = global$1.cacheSuffix;\n      if (isString(url)) {\n        url = url.replace('?' + cacheSuffix, '').replace('&' + cacheSuffix, '');\n      }\n      return url;\n    };\n    const isSkinContentCss = (editor, href) => {\n      const skin = getSkin(editor);\n      if (skin) {\n        const skinUrlBase = getSkinUrl(editor);\n        const skinUrl = skinUrlBase ? editor.documentBaseURI.toAbsolute(skinUrlBase) : global$2.baseURL + '/skins/ui/' + skin;\n        const contentSkinUrlPart = global$2.baseURL + '/skins/content/';\n        return href === skinUrl + '/content' + (editor.inline ? '.inline' : '') + '.min.css' || href.indexOf(contentSkinUrlPart) !== -1;\n      }\n      return false;\n    };\n    const compileFilter = filter => {\n      if (isString(filter)) {\n        return value => {\n          return value.indexOf(filter) !== -1;\n        };\n      } else if (filter instanceof RegExp) {\n        return value => {\n          return filter.test(value);\n        };\n      }\n      return filter;\n    };\n    const isCssImportRule = rule => rule.styleSheet;\n    const isCssPageRule = rule => rule.selectorText;\n    const getSelectors = (editor, doc, fileFilter) => {\n      const selectors = [];\n      const contentCSSUrls = {};\n      const append = (styleSheet, imported) => {\n        let href = styleSheet.href;\n        let rules;\n        href = removeCacheSuffix(href);\n        if (!href || fileFilter && !fileFilter(href, imported) || isSkinContentCss(editor, href)) {\n          return;\n        }\n        global.each(styleSheet.imports, styleSheet => {\n          append(styleSheet, true);\n        });\n        try {\n          rules = styleSheet.cssRules || styleSheet.rules;\n        } catch (e) {\n        }\n        global.each(rules, cssRule => {\n          if (isCssImportRule(cssRule)) {\n            append(cssRule.styleSheet, true);\n          } else if (isCssPageRule(cssRule)) {\n            global.each(cssRule.selectorText.split(','), selector => {\n              selectors.push(global.trim(selector));\n            });\n          }\n        });\n      };\n      global.each(editor.contentCSS, url => {\n        contentCSSUrls[url] = true;\n      });\n      if (!fileFilter) {\n        fileFilter = (href, imported) => {\n          return imported || contentCSSUrls[href];\n        };\n      }\n      try {\n        global.each(doc.styleSheets, styleSheet => {\n          append(styleSheet);\n        });\n      } catch (e) {\n      }\n      return selectors;\n    };\n    const defaultConvertSelectorToFormat = (editor, selectorText) => {\n      let format = {};\n      const selector = /^(?:([a-z0-9\\-_]+))?(\\.[a-z0-9_\\-\\.]+)$/i.exec(selectorText);\n      if (!selector) {\n        return;\n      }\n      const elementName = selector[1];\n      const classes = selector[2].substr(1).split('.').join(' ');\n      const inlineSelectorElements = global.makeMap('a,img');\n      if (selector[1]) {\n        format = { title: selectorText };\n        if (editor.schema.getTextBlockElements()[elementName]) {\n          format.block = elementName;\n        } else if (editor.schema.getBlockElements()[elementName] || inlineSelectorElements[elementName.toLowerCase()]) {\n          format.selector = elementName;\n        } else {\n          format.inline = elementName;\n        }\n      } else if (selector[2]) {\n        format = {\n          inline: 'span',\n          title: selectorText.substr(1),\n          classes\n        };\n      }\n      if (shouldMergeClasses(editor)) {\n        format.classes = classes;\n      } else {\n        format.attributes = { class: classes };\n      }\n      return format;\n    };\n    const getGroupsBySelector = (groups, selector) => {\n      return global.grep(groups, group => {\n        return !group.filter || group.filter(selector);\n      });\n    };\n    const compileUserDefinedGroups = groups => {\n      return global.map(groups, group => {\n        return global.extend({}, group, {\n          original: group,\n          selectors: {},\n          filter: compileFilter(group.filter)\n        });\n      });\n    };\n    const isExclusiveMode = (editor, group) => {\n      return group === null || shouldImportExclusive(editor);\n    };\n    const isUniqueSelector = (editor, selector, group, globallyUniqueSelectors) => {\n      return !(isExclusiveMode(editor, group) ? selector in globallyUniqueSelectors : selector in group.selectors);\n    };\n    const markUniqueSelector = (editor, selector, group, globallyUniqueSelectors) => {\n      if (isExclusiveMode(editor, group)) {\n        globallyUniqueSelectors[selector] = true;\n      } else {\n        group.selectors[selector] = true;\n      }\n    };\n    const convertSelectorToFormat = (editor, plugin, selector, group) => {\n      let selectorConverter;\n      const converter = getSelectorConverter(editor);\n      if (group && group.selector_converter) {\n        selectorConverter = group.selector_converter;\n      } else if (converter) {\n        selectorConverter = converter;\n      } else {\n        selectorConverter = () => {\n          return defaultConvertSelectorToFormat(editor, selector);\n        };\n      }\n      return selectorConverter.call(plugin, selector, group);\n    };\n    const setup = editor => {\n      editor.on('init', () => {\n        const model = generate();\n        const globallyUniqueSelectors = {};\n        const selectorFilter = compileFilter(getSelectorFilter(editor));\n        const groups = compileUserDefinedGroups(getCssGroups(editor));\n        const processSelector = (selector, group) => {\n          if (isUniqueSelector(editor, selector, group, globallyUniqueSelectors)) {\n            markUniqueSelector(editor, selector, group, globallyUniqueSelectors);\n            const format = convertSelectorToFormat(editor, editor.plugins.importcss, selector, group);\n            if (format) {\n              const formatName = format.name || global$3.DOM.uniqueId();\n              editor.formatter.register(formatName, format);\n              return {\n                title: format.title,\n                format: formatName\n              };\n            }\n          }\n          return null;\n        };\n        global.each(getSelectors(editor, editor.getDoc(), compileFilter(getFileFilter(editor))), selector => {\n          if (!internalEditorStyle.test(selector)) {\n            if (!selectorFilter || selectorFilter(selector)) {\n              const selectorGroups = getGroupsBySelector(groups, selector);\n              if (selectorGroups.length > 0) {\n                global.each(selectorGroups, group => {\n                  const menuItem = processSelector(selector, group);\n                  if (menuItem) {\n                    model.addItemToGroup(group.title, menuItem);\n                  }\n                });\n              } else {\n                const menuItem = processSelector(selector, null);\n                if (menuItem) {\n                  model.addItem(menuItem);\n                }\n              }\n            }\n          }\n        });\n        const items = model.toFormats();\n        editor.dispatch('addStyleModifications', {\n          items,\n          replace: !shouldAppend(editor)\n        });\n      });\n    };\n\n    const get = editor => {\n      const convertSelectorToFormat = selectorText => {\n        return defaultConvertSelectorToFormat(editor, selectorText);\n      };\n      return { convertSelectorToFormat };\n    };\n\n    var Plugin = () => {\n      global$4.add('importcss', editor => {\n        register(editor);\n        setup(editor);\n        return get(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/insertdatetime/index.js",
    "content": "// Exports the \"insertdatetime\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/insertdatetime')\n//   ES2015:\n//     import 'tinymce/plugins/insertdatetime'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/insertdatetime/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('insertdatetime_dateformat', {\n        processor: 'string',\n        default: editor.translate('%Y-%m-%d')\n      });\n      registerOption('insertdatetime_timeformat', {\n        processor: 'string',\n        default: editor.translate('%H:%M:%S')\n      });\n      registerOption('insertdatetime_formats', {\n        processor: 'string[]',\n        default: [\n          '%H:%M:%S',\n          '%Y-%m-%d',\n          '%I:%M:%S %p',\n          '%D'\n        ]\n      });\n      registerOption('insertdatetime_element', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const getDateFormat = option('insertdatetime_dateformat');\n    const getTimeFormat = option('insertdatetime_timeformat');\n    const getFormats = option('insertdatetime_formats');\n    const shouldInsertTimeElement = option('insertdatetime_element');\n    const getDefaultDateTime = editor => {\n      const formats = getFormats(editor);\n      return formats.length > 0 ? formats[0] : getTimeFormat(editor);\n    };\n\n    const daysShort = 'Sun Mon Tue Wed Thu Fri Sat Sun'.split(' ');\n    const daysLong = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday'.split(' ');\n    const monthsShort = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');\n    const monthsLong = 'January February March April May June July August September October November December'.split(' ');\n    const addZeros = (value, len) => {\n      value = '' + value;\n      if (value.length < len) {\n        for (let i = 0; i < len - value.length; i++) {\n          value = '0' + value;\n        }\n      }\n      return value;\n    };\n    const getDateTime = (editor, fmt, date = new Date()) => {\n      fmt = fmt.replace('%D', '%m/%d/%Y');\n      fmt = fmt.replace('%r', '%I:%M:%S %p');\n      fmt = fmt.replace('%Y', '' + date.getFullYear());\n      fmt = fmt.replace('%y', '' + date.getYear());\n      fmt = fmt.replace('%m', addZeros(date.getMonth() + 1, 2));\n      fmt = fmt.replace('%d', addZeros(date.getDate(), 2));\n      fmt = fmt.replace('%H', '' + addZeros(date.getHours(), 2));\n      fmt = fmt.replace('%M', '' + addZeros(date.getMinutes(), 2));\n      fmt = fmt.replace('%S', '' + addZeros(date.getSeconds(), 2));\n      fmt = fmt.replace('%I', '' + ((date.getHours() + 11) % 12 + 1));\n      fmt = fmt.replace('%p', '' + (date.getHours() < 12 ? 'AM' : 'PM'));\n      fmt = fmt.replace('%B', '' + editor.translate(monthsLong[date.getMonth()]));\n      fmt = fmt.replace('%b', '' + editor.translate(monthsShort[date.getMonth()]));\n      fmt = fmt.replace('%A', '' + editor.translate(daysLong[date.getDay()]));\n      fmt = fmt.replace('%a', '' + editor.translate(daysShort[date.getDay()]));\n      fmt = fmt.replace('%%', '%');\n      return fmt;\n    };\n    const updateElement = (editor, timeElm, computerTime, userTime) => {\n      const newTimeElm = editor.dom.create('time', { datetime: computerTime }, userTime);\n      editor.dom.replace(newTimeElm, timeElm);\n      editor.selection.select(newTimeElm, true);\n      editor.selection.collapse(false);\n    };\n    const insertDateTime = (editor, format) => {\n      if (shouldInsertTimeElement(editor)) {\n        const userTime = getDateTime(editor, format);\n        let computerTime;\n        if (/%[HMSIp]/.test(format)) {\n          computerTime = getDateTime(editor, '%Y-%m-%dT%H:%M');\n        } else {\n          computerTime = getDateTime(editor, '%Y-%m-%d');\n        }\n        const timeElm = editor.dom.getParent(editor.selection.getStart(), 'time');\n        if (timeElm) {\n          updateElement(editor, timeElm, computerTime, userTime);\n        } else {\n          editor.insertContent('<time datetime=\"' + computerTime + '\">' + userTime + '</time>');\n        }\n      } else {\n        editor.insertContent(getDateTime(editor, format));\n      }\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mceInsertDate', (_ui, value) => {\n        insertDateTime(editor, value !== null && value !== void 0 ? value : getDateFormat(editor));\n      });\n      editor.addCommand('mceInsertTime', (_ui, value) => {\n        insertDateTime(editor, value !== null && value !== void 0 ? value : getTimeFormat(editor));\n      });\n    };\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const register = editor => {\n      const formats = getFormats(editor);\n      const defaultFormat = Cell(getDefaultDateTime(editor));\n      const insertDateTime = format => editor.execCommand('mceInsertDate', false, format);\n      editor.ui.registry.addSplitButton('insertdatetime', {\n        icon: 'insert-time',\n        tooltip: 'Insert date/time',\n        select: value => value === defaultFormat.get(),\n        fetch: done => {\n          done(global.map(formats, format => ({\n            type: 'choiceitem',\n            text: getDateTime(editor, format),\n            value: format\n          })));\n        },\n        onAction: _api => {\n          insertDateTime(defaultFormat.get());\n        },\n        onItemAction: (_api, value) => {\n          defaultFormat.set(value);\n          insertDateTime(value);\n        }\n      });\n      const makeMenuItemHandler = format => () => {\n        defaultFormat.set(format);\n        insertDateTime(format);\n      };\n      editor.ui.registry.addNestedMenuItem('insertdatetime', {\n        icon: 'insert-time',\n        text: 'Date/time',\n        getSubmenuItems: () => global.map(formats, format => ({\n          type: 'menuitem',\n          text: getDateTime(editor, format),\n          onAction: makeMenuItemHandler(format)\n        }))\n      });\n    };\n\n    var Plugin = () => {\n      global$1.add('insertdatetime', editor => {\n        register$2(editor);\n        register$1(editor);\n        register(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/link/index.js",
    "content": "// Exports the \"link\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/link')\n//   ES2015:\n//     import 'tinymce/plugins/link'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/link/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$5 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq = t => a => t === a;\n    const isString = isType('string');\n    const isObject = isType('object');\n    const isArray = isType('array');\n    const isNull = eq(null);\n    const isBoolean = isSimpleType('boolean');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isArrayOf = (value, pred) => {\n      if (isArray(value)) {\n        for (let i = 0, len = value.length; i < len; ++i) {\n          if (!pred(value[i])) {\n            return false;\n          }\n        }\n        return true;\n      }\n      return false;\n    };\n\n    const noop = () => {\n    };\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const tripleEquals = (a, b) => {\n      return a === b;\n    };\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativeIndexOf = Array.prototype.indexOf;\n    const nativePush = Array.prototype.push;\n    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);\n    const contains = (xs, x) => rawIndexOf(xs, x) > -1;\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$1 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const foldl = (xs, f, acc) => {\n      each$1(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind = (xs, f) => flatten(map(xs, f));\n    const findMap = (arr, f) => {\n      for (let i = 0; i < arr.length; i++) {\n        const r = f(arr[i], i);\n        if (r.isSome()) {\n          return r;\n        }\n      }\n      return Optional.none();\n    };\n\n    const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));\n    const cat = arr => {\n      const r = [];\n      const push = x => {\n        r.push(x);\n      };\n      for (let i = 0; i < arr.length; i++) {\n        arr[i].each(push);\n      }\n      return r;\n    };\n    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();\n\n    const option = name => editor => editor.options.get(name);\n    const register$1 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('link_assume_external_targets', {\n        processor: value => {\n          const valid = isString(value) || isBoolean(value);\n          if (valid) {\n            if (value === true) {\n              return {\n                value: 1,\n                valid\n              };\n            } else if (value === 'http' || value === 'https') {\n              return {\n                value,\n                valid\n              };\n            } else {\n              return {\n                value: 0,\n                valid\n              };\n            }\n          } else {\n            return {\n              valid: false,\n              message: 'Must be a string or a boolean.'\n            };\n          }\n        },\n        default: false\n      });\n      registerOption('link_context_toolbar', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('link_list', { processor: value => isString(value) || isFunction(value) || isArrayOf(value, isObject) });\n      registerOption('link_default_target', { processor: 'string' });\n      registerOption('link_default_protocol', {\n        processor: 'string',\n        default: 'https'\n      });\n      registerOption('link_target_list', {\n        processor: value => isBoolean(value) || isArrayOf(value, isObject),\n        default: true\n      });\n      registerOption('link_rel_list', {\n        processor: 'object[]',\n        default: []\n      });\n      registerOption('link_class_list', {\n        processor: 'object[]',\n        default: []\n      });\n      registerOption('link_title', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('allow_unsafe_link_target', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('link_quicklink', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const assumeExternalTargets = option('link_assume_external_targets');\n    const hasContextToolbar = option('link_context_toolbar');\n    const getLinkList = option('link_list');\n    const getDefaultLinkTarget = option('link_default_target');\n    const getDefaultLinkProtocol = option('link_default_protocol');\n    const getTargetList = option('link_target_list');\n    const getRelList = option('link_rel_list');\n    const getLinkClassList = option('link_class_list');\n    const shouldShowLinkTitle = option('link_title');\n    const allowUnsafeLinkTarget = option('allow_unsafe_link_target');\n    const useQuickLink = option('link_quicklink');\n\n    var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const getValue = item => isString(item.value) ? item.value : '';\n    const getText = item => {\n      if (isString(item.text)) {\n        return item.text;\n      } else if (isString(item.title)) {\n        return item.title;\n      } else {\n        return '';\n      }\n    };\n    const sanitizeList = (list, extractValue) => {\n      const out = [];\n      global$4.each(list, item => {\n        const text = getText(item);\n        if (item.menu !== undefined) {\n          const items = sanitizeList(item.menu, extractValue);\n          out.push({\n            text,\n            items\n          });\n        } else {\n          const value = extractValue(item);\n          out.push({\n            text,\n            value\n          });\n        }\n      });\n      return out;\n    };\n    const sanitizeWith = (extracter = getValue) => list => Optional.from(list).map(list => sanitizeList(list, extracter));\n    const sanitize = list => sanitizeWith(getValue)(list);\n    const createUi = (name, label) => items => ({\n      name,\n      type: 'listbox',\n      label,\n      items\n    });\n    const ListOptions = {\n      sanitize,\n      sanitizeWith,\n      createUi,\n      getValue\n    };\n\n    const keys = Object.keys;\n    const hasOwnProperty = Object.hasOwnProperty;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const objAcc = r => (x, i) => {\n      r[i] = x;\n    };\n    const internalFilter = (obj, pred, onTrue, onFalse) => {\n      each(obj, (x, i) => {\n        (pred(x, i) ? onTrue : onFalse)(x, i);\n      });\n    };\n    const filter = (obj, pred) => {\n      const t = {};\n      internalFilter(obj, pred, objAcc(t), noop);\n      return t;\n    };\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI');\n\n    const isAnchor = elm => isNonNullable(elm) && elm.nodeName.toLowerCase() === 'a';\n    const isLink = elm => isAnchor(elm) && !!getHref(elm);\n    const collectNodesInRange = (rng, predicate) => {\n      if (rng.collapsed) {\n        return [];\n      } else {\n        const contents = rng.cloneContents();\n        const firstChild = contents.firstChild;\n        const walker = new global$3(firstChild, contents);\n        const elements = [];\n        let current = firstChild;\n        do {\n          if (predicate(current)) {\n            elements.push(current);\n          }\n        } while (current = walker.next());\n        return elements;\n      }\n    };\n    const hasProtocol = url => /^\\w+:/i.test(url);\n    const getHref = elm => {\n      var _a, _b;\n      return (_b = (_a = elm.getAttribute('data-mce-href')) !== null && _a !== void 0 ? _a : elm.getAttribute('href')) !== null && _b !== void 0 ? _b : '';\n    };\n    const applyRelTargetRules = (rel, isUnsafe) => {\n      const rules = ['noopener'];\n      const rels = rel ? rel.split(/\\s+/) : [];\n      const toString = rels => global$4.trim(rels.sort().join(' '));\n      const addTargetRules = rels => {\n        rels = removeTargetRules(rels);\n        return rels.length > 0 ? rels.concat(rules) : rules;\n      };\n      const removeTargetRules = rels => rels.filter(val => global$4.inArray(rules, val) === -1);\n      const newRels = isUnsafe ? addTargetRules(rels) : removeTargetRules(rels);\n      return newRels.length > 0 ? toString(newRels) : '';\n    };\n    const trimCaretContainers = text => text.replace(/\\uFEFF/g, '');\n    const getAnchorElement = (editor, selectedElm) => {\n      selectedElm = selectedElm || editor.selection.getNode();\n      if (isImageFigure(selectedElm)) {\n        return Optional.from(editor.dom.select('a[href]', selectedElm)[0]);\n      } else {\n        return Optional.from(editor.dom.getParent(selectedElm, 'a[href]'));\n      }\n    };\n    const isInAnchor = (editor, selectedElm) => getAnchorElement(editor, selectedElm).isSome();\n    const getAnchorText = (selection, anchorElm) => {\n      const text = anchorElm.fold(() => selection.getContent({ format: 'text' }), anchorElm => anchorElm.innerText || anchorElm.textContent || '');\n      return trimCaretContainers(text);\n    };\n    const hasLinks = elements => global$4.grep(elements, isLink).length > 0;\n    const hasLinksInSelection = rng => collectNodesInRange(rng, isLink).length > 0;\n    const isOnlyTextSelected = editor => {\n      const inlineTextElements = editor.schema.getTextInlineElements();\n      const isElement = elm => elm.nodeType === 1 && !isAnchor(elm) && !has(inlineTextElements, elm.nodeName.toLowerCase());\n      const isInBlockAnchor = getAnchorElement(editor).exists(anchor => anchor.hasAttribute('data-mce-block'));\n      if (isInBlockAnchor) {\n        return false;\n      }\n      const rng = editor.selection.getRng();\n      if (!rng.collapsed) {\n        const elements = collectNodesInRange(rng, isElement);\n        return elements.length === 0;\n      } else {\n        return true;\n      }\n    };\n    const isImageFigure = elm => isNonNullable(elm) && elm.nodeName === 'FIGURE' && /\\bimage\\b/i.test(elm.className);\n    const getLinkAttrs = data => {\n      const attrs = [\n        'title',\n        'rel',\n        'class',\n        'target'\n      ];\n      return foldl(attrs, (acc, key) => {\n        data[key].each(value => {\n          acc[key] = value.length > 0 ? value : null;\n        });\n        return acc;\n      }, { href: data.href });\n    };\n    const handleExternalTargets = (href, assumeExternalTargets) => {\n      if ((assumeExternalTargets === 'http' || assumeExternalTargets === 'https') && !hasProtocol(href)) {\n        return assumeExternalTargets + '://' + href;\n      }\n      return href;\n    };\n    const applyLinkOverrides = (editor, linkAttrs) => {\n      const newLinkAttrs = { ...linkAttrs };\n      if (getRelList(editor).length === 0 && !allowUnsafeLinkTarget(editor)) {\n        const newRel = applyRelTargetRules(newLinkAttrs.rel, newLinkAttrs.target === '_blank');\n        newLinkAttrs.rel = newRel ? newRel : null;\n      }\n      if (Optional.from(newLinkAttrs.target).isNone() && getTargetList(editor) === false) {\n        newLinkAttrs.target = getDefaultLinkTarget(editor);\n      }\n      newLinkAttrs.href = handleExternalTargets(newLinkAttrs.href, assumeExternalTargets(editor));\n      return newLinkAttrs;\n    };\n    const updateLink = (editor, anchorElm, text, linkAttrs) => {\n      text.each(text => {\n        if (has(anchorElm, 'innerText')) {\n          anchorElm.innerText = text;\n        } else {\n          anchorElm.textContent = text;\n        }\n      });\n      editor.dom.setAttribs(anchorElm, linkAttrs);\n      editor.selection.select(anchorElm);\n    };\n    const createLink = (editor, selectedElm, text, linkAttrs) => {\n      const dom = editor.dom;\n      if (isImageFigure(selectedElm)) {\n        linkImageFigure(dom, selectedElm, linkAttrs);\n      } else {\n        text.fold(() => {\n          editor.execCommand('mceInsertLink', false, linkAttrs);\n        }, text => {\n          editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(text)));\n        });\n      }\n    };\n    const linkDomMutation = (editor, attachState, data) => {\n      const selectedElm = editor.selection.getNode();\n      const anchorElm = getAnchorElement(editor, selectedElm);\n      const linkAttrs = applyLinkOverrides(editor, getLinkAttrs(data));\n      editor.undoManager.transact(() => {\n        if (data.href === attachState.href) {\n          attachState.attach();\n        }\n        anchorElm.fold(() => {\n          createLink(editor, selectedElm, data.text, linkAttrs);\n        }, elm => {\n          editor.focus();\n          updateLink(editor, elm, data.text, linkAttrs);\n        });\n      });\n    };\n    const unlinkSelection = editor => {\n      const dom = editor.dom, selection = editor.selection;\n      const bookmark = selection.getBookmark();\n      const rng = selection.getRng().cloneRange();\n      const startAnchorElm = dom.getParent(rng.startContainer, 'a[href]', editor.getBody());\n      const endAnchorElm = dom.getParent(rng.endContainer, 'a[href]', editor.getBody());\n      if (startAnchorElm) {\n        rng.setStartBefore(startAnchorElm);\n      }\n      if (endAnchorElm) {\n        rng.setEndAfter(endAnchorElm);\n      }\n      selection.setRng(rng);\n      editor.execCommand('unlink');\n      selection.moveToBookmark(bookmark);\n    };\n    const unlinkDomMutation = editor => {\n      editor.undoManager.transact(() => {\n        const node = editor.selection.getNode();\n        if (isImageFigure(node)) {\n          unlinkImageFigure(editor, node);\n        } else {\n          unlinkSelection(editor);\n        }\n        editor.focus();\n      });\n    };\n    const unwrapOptions = data => {\n      const {\n        class: cls,\n        href,\n        rel,\n        target,\n        text,\n        title\n      } = data;\n      return filter({\n        class: cls.getOrNull(),\n        href,\n        rel: rel.getOrNull(),\n        target: target.getOrNull(),\n        text: text.getOrNull(),\n        title: title.getOrNull()\n      }, (v, _k) => isNull(v) === false);\n    };\n    const sanitizeData = (editor, data) => {\n      const getOption = editor.options.get;\n      const uriOptions = {\n        allow_html_data_urls: getOption('allow_html_data_urls'),\n        allow_script_urls: getOption('allow_script_urls'),\n        allow_svg_data_urls: getOption('allow_svg_data_urls')\n      };\n      const href = data.href;\n      return {\n        ...data,\n        href: global$2.isDomSafe(href, 'a', uriOptions) ? href : ''\n      };\n    };\n    const link = (editor, attachState, data) => {\n      const sanitizedData = sanitizeData(editor, data);\n      editor.hasPlugin('rtc', true) ? editor.execCommand('createlink', false, unwrapOptions(sanitizedData)) : linkDomMutation(editor, attachState, sanitizedData);\n    };\n    const unlink = editor => {\n      editor.hasPlugin('rtc', true) ? editor.execCommand('unlink') : unlinkDomMutation(editor);\n    };\n    const unlinkImageFigure = (editor, fig) => {\n      var _a;\n      const img = editor.dom.select('img', fig)[0];\n      if (img) {\n        const a = editor.dom.getParents(img, 'a[href]', fig)[0];\n        if (a) {\n          (_a = a.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(img, a);\n          editor.dom.remove(a);\n        }\n      }\n    };\n    const linkImageFigure = (dom, fig, attrs) => {\n      var _a;\n      const img = dom.select('img', fig)[0];\n      if (img) {\n        const a = dom.create('a', attrs);\n        (_a = img.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(a, img);\n        a.appendChild(img);\n      }\n    };\n\n    const isListGroup = item => hasNonNullableKey(item, 'items');\n    const findTextByValue = (value, catalog) => findMap(catalog, item => {\n      if (isListGroup(item)) {\n        return findTextByValue(value, item.items);\n      } else {\n        return someIf(item.value === value, item);\n      }\n    });\n    const getDelta = (persistentText, fieldName, catalog, data) => {\n      const value = data[fieldName];\n      const hasPersistentText = persistentText.length > 0;\n      return value !== undefined ? findTextByValue(value, catalog).map(i => ({\n        url: {\n          value: i.value,\n          meta: {\n            text: hasPersistentText ? persistentText : i.text,\n            attach: noop\n          }\n        },\n        text: hasPersistentText ? persistentText : i.text\n      })) : Optional.none();\n    };\n    const findCatalog = (catalogs, fieldName) => {\n      if (fieldName === 'link') {\n        return catalogs.link;\n      } else if (fieldName === 'anchor') {\n        return catalogs.anchor;\n      } else {\n        return Optional.none();\n      }\n    };\n    const init = (initialData, linkCatalog) => {\n      const persistentData = {\n        text: initialData.text,\n        title: initialData.title\n      };\n      const getTitleFromUrlChange = url => {\n        var _a;\n        return someIf(persistentData.title.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.title).getOr(''));\n      };\n      const getTextFromUrlChange = url => {\n        var _a;\n        return someIf(persistentData.text.length <= 0, Optional.from((_a = url.meta) === null || _a === void 0 ? void 0 : _a.text).getOr(url.value));\n      };\n      const onUrlChange = data => {\n        const text = getTextFromUrlChange(data.url);\n        const title = getTitleFromUrlChange(data.url);\n        if (text.isSome() || title.isSome()) {\n          return Optional.some({\n            ...text.map(text => ({ text })).getOr({}),\n            ...title.map(title => ({ title })).getOr({})\n          });\n        } else {\n          return Optional.none();\n        }\n      };\n      const onCatalogChange = (data, change) => {\n        const catalog = findCatalog(linkCatalog, change).getOr([]);\n        return getDelta(persistentData.text, change, catalog, data);\n      };\n      const onChange = (getData, change) => {\n        const name = change.name;\n        if (name === 'url') {\n          return onUrlChange(getData());\n        } else if (contains([\n            'anchor',\n            'link'\n          ], name)) {\n          return onCatalogChange(getData(), name);\n        } else if (name === 'text' || name === 'title') {\n          persistentData[name] = getData()[name];\n          return Optional.none();\n        } else {\n          return Optional.none();\n        }\n      };\n      return { onChange };\n    };\n    const DialogChanges = {\n      init,\n      getDelta\n    };\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Delay');\n\n    const delayedConfirm = (editor, message, callback) => {\n      const rng = editor.selection.getRng();\n      global$1.setEditorTimeout(editor, () => {\n        editor.windowManager.confirm(message, state => {\n          editor.selection.setRng(rng);\n          callback(state);\n        });\n      });\n    };\n    const tryEmailTransform = data => {\n      const url = data.href;\n      const suggestMailTo = url.indexOf('@') > 0 && url.indexOf('/') === -1 && url.indexOf('mailto:') === -1;\n      return suggestMailTo ? Optional.some({\n        message: 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',\n        preprocess: oldData => ({\n          ...oldData,\n          href: 'mailto:' + url\n        })\n      }) : Optional.none();\n    };\n    const tryProtocolTransform = (assumeExternalTargets, defaultLinkProtocol) => data => {\n      const url = data.href;\n      const suggestProtocol = assumeExternalTargets === 1 && !hasProtocol(url) || assumeExternalTargets === 0 && /^\\s*www(\\.|\\d\\.)/i.test(url);\n      return suggestProtocol ? Optional.some({\n        message: `The URL you entered seems to be an external link. Do you want to add the required ${ defaultLinkProtocol }:// prefix?`,\n        preprocess: oldData => ({\n          ...oldData,\n          href: defaultLinkProtocol + '://' + url\n        })\n      }) : Optional.none();\n    };\n    const preprocess = (editor, data) => findMap([\n      tryEmailTransform,\n      tryProtocolTransform(assumeExternalTargets(editor), getDefaultLinkProtocol(editor))\n    ], f => f(data)).fold(() => Promise.resolve(data), transform => new Promise(callback => {\n      delayedConfirm(editor, transform.message, state => {\n        callback(state ? transform.preprocess(data) : data);\n      });\n    }));\n    const DialogConfirms = { preprocess };\n\n    const getAnchors = editor => {\n      const anchorNodes = editor.dom.select('a:not([href])');\n      const anchors = bind(anchorNodes, anchor => {\n        const id = anchor.name || anchor.id;\n        return id ? [{\n            text: id,\n            value: '#' + id\n          }] : [];\n      });\n      return anchors.length > 0 ? Optional.some([{\n          text: 'None',\n          value: ''\n        }].concat(anchors)) : Optional.none();\n    };\n    const AnchorListOptions = { getAnchors };\n\n    const getClasses = editor => {\n      const list = getLinkClassList(editor);\n      if (list.length > 0) {\n        return ListOptions.sanitize(list);\n      }\n      return Optional.none();\n    };\n    const ClassListOptions = { getClasses };\n\n    const parseJson = text => {\n      try {\n        return Optional.some(JSON.parse(text));\n      } catch (err) {\n        return Optional.none();\n      }\n    };\n    const getLinks = editor => {\n      const extractor = item => editor.convertURL(item.value || item.url || '', 'href');\n      const linkList = getLinkList(editor);\n      return new Promise(resolve => {\n        if (isString(linkList)) {\n          fetch(linkList).then(res => res.ok ? res.text().then(parseJson) : Promise.reject()).then(resolve, () => resolve(Optional.none()));\n        } else if (isFunction(linkList)) {\n          linkList(output => resolve(Optional.some(output)));\n        } else {\n          resolve(Optional.from(linkList));\n        }\n      }).then(optItems => optItems.bind(ListOptions.sanitizeWith(extractor)).map(items => {\n        if (items.length > 0) {\n          const noneItem = [{\n              text: 'None',\n              value: ''\n            }];\n          return noneItem.concat(items);\n        } else {\n          return items;\n        }\n      }));\n    };\n    const LinkListOptions = { getLinks };\n\n    const getRels = (editor, initialTarget) => {\n      const list = getRelList(editor);\n      if (list.length > 0) {\n        const isTargetBlank = is(initialTarget, '_blank');\n        const enforceSafe = allowUnsafeLinkTarget(editor) === false;\n        const safeRelExtractor = item => applyRelTargetRules(ListOptions.getValue(item), isTargetBlank);\n        const sanitizer = enforceSafe ? ListOptions.sanitizeWith(safeRelExtractor) : ListOptions.sanitize;\n        return sanitizer(list);\n      }\n      return Optional.none();\n    };\n    const RelOptions = { getRels };\n\n    const fallbacks = [\n      {\n        text: 'Current window',\n        value: ''\n      },\n      {\n        text: 'New window',\n        value: '_blank'\n      }\n    ];\n    const getTargets = editor => {\n      const list = getTargetList(editor);\n      if (isArray(list)) {\n        return ListOptions.sanitize(list).orThunk(() => Optional.some(fallbacks));\n      } else if (list === false) {\n        return Optional.none();\n      }\n      return Optional.some(fallbacks);\n    };\n    const TargetOptions = { getTargets };\n\n    const nonEmptyAttr = (dom, elem, name) => {\n      const val = dom.getAttrib(elem, name);\n      return val !== null && val.length > 0 ? Optional.some(val) : Optional.none();\n    };\n    const extractFromAnchor = (editor, anchor) => {\n      const dom = editor.dom;\n      const onlyText = isOnlyTextSelected(editor);\n      const text = onlyText ? Optional.some(getAnchorText(editor.selection, anchor)) : Optional.none();\n      const url = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'href')));\n      const target = anchor.bind(anchorElm => Optional.from(dom.getAttrib(anchorElm, 'target')));\n      const rel = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'rel'));\n      const linkClass = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'class'));\n      const title = anchor.bind(anchorElm => nonEmptyAttr(dom, anchorElm, 'title'));\n      return {\n        url,\n        text,\n        title,\n        target,\n        rel,\n        linkClass\n      };\n    };\n    const collect = (editor, linkNode) => LinkListOptions.getLinks(editor).then(links => {\n      const anchor = extractFromAnchor(editor, linkNode);\n      return {\n        anchor,\n        catalogs: {\n          targets: TargetOptions.getTargets(editor),\n          rels: RelOptions.getRels(editor, anchor.target),\n          classes: ClassListOptions.getClasses(editor),\n          anchor: AnchorListOptions.getAnchors(editor),\n          link: links\n        },\n        optNode: linkNode,\n        flags: { titleEnabled: shouldShowLinkTitle(editor) }\n      };\n    });\n    const DialogInfo = { collect };\n\n    const handleSubmit = (editor, info) => api => {\n      const data = api.getData();\n      if (!data.url.value) {\n        unlink(editor);\n        api.close();\n        return;\n      }\n      const getChangedValue = key => Optional.from(data[key]).filter(value => !is(info.anchor[key], value));\n      const changedData = {\n        href: data.url.value,\n        text: getChangedValue('text'),\n        target: getChangedValue('target'),\n        rel: getChangedValue('rel'),\n        class: getChangedValue('linkClass'),\n        title: getChangedValue('title')\n      };\n      const attachState = {\n        href: data.url.value,\n        attach: data.url.meta !== undefined && data.url.meta.attach ? data.url.meta.attach : noop\n      };\n      DialogConfirms.preprocess(editor, changedData).then(pData => {\n        link(editor, attachState, pData);\n      });\n      api.close();\n    };\n    const collectData = editor => {\n      const anchorNode = getAnchorElement(editor);\n      return DialogInfo.collect(editor, anchorNode);\n    };\n    const getInitialData = (info, defaultTarget) => {\n      const anchor = info.anchor;\n      const url = anchor.url.getOr('');\n      return {\n        url: {\n          value: url,\n          meta: { original: { value: url } }\n        },\n        text: anchor.text.getOr(''),\n        title: anchor.title.getOr(''),\n        anchor: url,\n        link: url,\n        rel: anchor.rel.getOr(''),\n        target: anchor.target.or(defaultTarget).getOr(''),\n        linkClass: anchor.linkClass.getOr('')\n      };\n    };\n    const makeDialog = (settings, onSubmit, editor) => {\n      const urlInput = [{\n          name: 'url',\n          type: 'urlinput',\n          filetype: 'file',\n          label: 'URL'\n        }];\n      const displayText = settings.anchor.text.map(() => ({\n        name: 'text',\n        type: 'input',\n        label: 'Text to display'\n      })).toArray();\n      const titleText = settings.flags.titleEnabled ? [{\n          name: 'title',\n          type: 'input',\n          label: 'Title'\n        }] : [];\n      const defaultTarget = Optional.from(getDefaultLinkTarget(editor));\n      const initialData = getInitialData(settings, defaultTarget);\n      const catalogs = settings.catalogs;\n      const dialogDelta = DialogChanges.init(initialData, catalogs);\n      const body = {\n        type: 'panel',\n        items: flatten([\n          urlInput,\n          displayText,\n          titleText,\n          cat([\n            catalogs.anchor.map(ListOptions.createUi('anchor', 'Anchors')),\n            catalogs.rels.map(ListOptions.createUi('rel', 'Rel')),\n            catalogs.targets.map(ListOptions.createUi('target', 'Open link in...')),\n            catalogs.link.map(ListOptions.createUi('link', 'Link list')),\n            catalogs.classes.map(ListOptions.createUi('linkClass', 'Class'))\n          ])\n        ])\n      };\n      return {\n        title: 'Insert/Edit Link',\n        size: 'normal',\n        body,\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData,\n        onChange: (api, {name}) => {\n          dialogDelta.onChange(api.getData, { name }).each(newData => {\n            api.setData(newData);\n          });\n        },\n        onSubmit\n      };\n    };\n    const open$1 = editor => {\n      const data = collectData(editor);\n      data.then(info => {\n        const onSubmit = handleSubmit(editor, info);\n        return makeDialog(info, onSubmit, editor);\n      }).then(spec => {\n        editor.windowManager.open(spec);\n      });\n    };\n\n    const register = editor => {\n      editor.addCommand('mceLink', (_ui, value) => {\n        if ((value === null || value === void 0 ? void 0 : value.dialog) === true || !useQuickLink(editor)) {\n          open$1(editor);\n        } else {\n          editor.dispatch('contexttoolbar-show', { toolbarKey: 'quicklink' });\n        }\n      });\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.VK');\n\n    const appendClickRemove = (link, evt) => {\n      document.body.appendChild(link);\n      link.dispatchEvent(evt);\n      document.body.removeChild(link);\n    };\n    const open = url => {\n      const link = document.createElement('a');\n      link.target = '_blank';\n      link.href = url;\n      link.rel = 'noreferrer noopener';\n      const evt = document.createEvent('MouseEvents');\n      evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n      appendClickRemove(link, evt);\n    };\n\n    const getLink = (editor, elm) => editor.dom.getParent(elm, 'a[href]');\n    const getSelectedLink = editor => getLink(editor, editor.selection.getStart());\n    const hasOnlyAltModifier = e => {\n      return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;\n    };\n    const gotoLink = (editor, a) => {\n      if (a) {\n        const href = getHref(a);\n        if (/^#/.test(href)) {\n          const targetEl = editor.dom.select(href);\n          if (targetEl.length) {\n            editor.selection.scrollIntoView(targetEl[0], true);\n          }\n        } else {\n          open(a.href);\n        }\n      }\n    };\n    const openDialog = editor => () => {\n      editor.execCommand('mceLink', false, { dialog: true });\n    };\n    const gotoSelectedLink = editor => () => {\n      gotoLink(editor, getSelectedLink(editor));\n    };\n    const setupGotoLinks = editor => {\n      editor.on('click', e => {\n        const link = getLink(editor, e.target);\n        if (link && global.metaKeyPressed(e)) {\n          e.preventDefault();\n          gotoLink(editor, link);\n        }\n      });\n      editor.on('keydown', e => {\n        if (!e.isDefaultPrevented() && e.keyCode === 13 && hasOnlyAltModifier(e)) {\n          const link = getSelectedLink(editor);\n          if (link) {\n            e.preventDefault();\n            gotoLink(editor, link);\n          }\n        }\n      });\n    };\n    const toggleState = (editor, toggler) => {\n      editor.on('NodeChange', toggler);\n      return () => editor.off('NodeChange', toggler);\n    };\n    const toggleActiveState = editor => api => {\n      const updateState = () => api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));\n      updateState();\n      return toggleState(editor, updateState);\n    };\n    const toggleEnabledState = editor => api => {\n      const updateState = () => api.setEnabled(isInAnchor(editor, editor.selection.getNode()));\n      updateState();\n      return toggleState(editor, updateState);\n    };\n    const toggleUnlinkState = editor => api => {\n      const hasLinks$1 = parents => hasLinks(parents) || hasLinksInSelection(editor.selection.getRng());\n      const parents = editor.dom.getParents(editor.selection.getStart());\n      api.setEnabled(hasLinks$1(parents));\n      return toggleState(editor, e => api.setEnabled(hasLinks$1(e.parents)));\n    };\n\n    const setup = editor => {\n      editor.addShortcut('Meta+K', '', () => {\n        editor.execCommand('mceLink');\n      });\n    };\n\n    const setupButtons = editor => {\n      editor.ui.registry.addToggleButton('link', {\n        icon: 'link',\n        tooltip: 'Insert/edit link',\n        onAction: openDialog(editor),\n        onSetup: toggleActiveState(editor)\n      });\n      editor.ui.registry.addButton('openlink', {\n        icon: 'new-tab',\n        tooltip: 'Open link',\n        onAction: gotoSelectedLink(editor),\n        onSetup: toggleEnabledState(editor)\n      });\n      editor.ui.registry.addButton('unlink', {\n        icon: 'unlink',\n        tooltip: 'Remove link',\n        onAction: () => unlink(editor),\n        onSetup: toggleUnlinkState(editor)\n      });\n    };\n    const setupMenuItems = editor => {\n      editor.ui.registry.addMenuItem('openlink', {\n        text: 'Open link',\n        icon: 'new-tab',\n        onAction: gotoSelectedLink(editor),\n        onSetup: toggleEnabledState(editor)\n      });\n      editor.ui.registry.addMenuItem('link', {\n        icon: 'link',\n        text: 'Link...',\n        shortcut: 'Meta+K',\n        onAction: openDialog(editor)\n      });\n      editor.ui.registry.addMenuItem('unlink', {\n        icon: 'unlink',\n        text: 'Remove link',\n        onAction: () => unlink(editor),\n        onSetup: toggleUnlinkState(editor)\n      });\n    };\n    const setupContextMenu = editor => {\n      const inLink = 'link unlink openlink';\n      const noLink = 'link';\n      editor.ui.registry.addContextMenu('link', { update: element => hasLinks(editor.dom.getParents(element, 'a')) ? inLink : noLink });\n    };\n    const setupContextToolbars = editor => {\n      const collapseSelectionToEnd = editor => {\n        editor.selection.collapse(false);\n      };\n      const onSetupLink = buttonApi => {\n        const node = editor.selection.getNode();\n        buttonApi.setEnabled(isInAnchor(editor, node));\n        return noop;\n      };\n      const getLinkText = value => {\n        const anchor = getAnchorElement(editor);\n        const onlyText = isOnlyTextSelected(editor);\n        if (anchor.isNone() && onlyText) {\n          const text = getAnchorText(editor.selection, anchor);\n          return Optional.some(text.length > 0 ? text : value);\n        } else {\n          return Optional.none();\n        }\n      };\n      editor.ui.registry.addContextForm('quicklink', {\n        launch: {\n          type: 'contextformtogglebutton',\n          icon: 'link',\n          tooltip: 'Link',\n          onSetup: toggleActiveState(editor)\n        },\n        label: 'Link',\n        predicate: node => hasContextToolbar(editor) && isInAnchor(editor, node),\n        initValue: () => {\n          const elm = getAnchorElement(editor);\n          return elm.fold(constant(''), getHref);\n        },\n        commands: [\n          {\n            type: 'contextformtogglebutton',\n            icon: 'link',\n            tooltip: 'Link',\n            primary: true,\n            onSetup: buttonApi => {\n              const node = editor.selection.getNode();\n              buttonApi.setActive(isInAnchor(editor, node));\n              return toggleActiveState(editor)(buttonApi);\n            },\n            onAction: formApi => {\n              const value = formApi.getValue();\n              const text = getLinkText(value);\n              const attachState = {\n                href: value,\n                attach: noop\n              };\n              link(editor, attachState, {\n                href: value,\n                text,\n                title: Optional.none(),\n                rel: Optional.none(),\n                target: Optional.none(),\n                class: Optional.none()\n              });\n              collapseSelectionToEnd(editor);\n              formApi.hide();\n            }\n          },\n          {\n            type: 'contextformbutton',\n            icon: 'unlink',\n            tooltip: 'Remove link',\n            onSetup: onSetupLink,\n            onAction: formApi => {\n              unlink(editor);\n              formApi.hide();\n            }\n          },\n          {\n            type: 'contextformbutton',\n            icon: 'new-tab',\n            tooltip: 'Open link',\n            onSetup: onSetupLink,\n            onAction: formApi => {\n              gotoSelectedLink(editor)();\n              formApi.hide();\n            }\n          }\n        ]\n      });\n    };\n\n    var Plugin = () => {\n      global$5.add('link', editor => {\n        register$1(editor);\n        setupButtons(editor);\n        setupMenuItems(editor);\n        setupContextMenu(editor);\n        setupContextToolbars(editor);\n        setupGotoLinks(editor);\n        register(editor);\n        setup(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/lists/index.js",
    "content": "// Exports the \"lists\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/lists')\n//   ES2015:\n//     import 'tinymce/plugins/lists'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/lists/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$6 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const isString = isType$1('string');\n    const isObject = isType$1('object');\n    const isArray = isType$1('array');\n    const isBoolean = isSimpleType('boolean');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n\n    const noop = () => {\n    };\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const tripleEquals = (a, b) => {\n      return a === b;\n    };\n    const not = f => t => !f(t);\n    const never = constant(false);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativeSlice = Array.prototype.slice;\n    const nativeIndexOf = Array.prototype.indexOf;\n    const nativePush = Array.prototype.push;\n    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);\n    const contains$1 = (xs, x) => rawIndexOf(xs, x) > -1;\n    const exists = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$1 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const filter$1 = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n    const groupBy = (xs, f) => {\n      if (xs.length === 0) {\n        return [];\n      } else {\n        let wasType = f(xs[0]);\n        const r = [];\n        let group = [];\n        for (let i = 0, len = xs.length; i < len; i++) {\n          const x = xs[i];\n          const type = f(x);\n          if (type !== wasType) {\n            r.push(group);\n            group = [];\n          }\n          wasType = type;\n          group.push(x);\n        }\n        if (group.length !== 0) {\n          r.push(group);\n        }\n        return r;\n      }\n    };\n    const foldl = (xs, f, acc) => {\n      each$1(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind = (xs, f) => flatten(map(xs, f));\n    const reverse = xs => {\n      const r = nativeSlice.call(xs, 0);\n      r.reverse();\n      return r;\n    };\n    const get$1 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get$1(xs, 0);\n    const last = xs => get$1(xs, xs.length - 1);\n    const unique = (xs, comparator) => {\n      const r = [];\n      const isDuplicated = isFunction(comparator) ? x => exists(r, i => comparator(i, x)) : x => contains$1(r, x);\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (!isDuplicated(x)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n\n    const is$2 = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));\n    const equals = (lhs, rhs, comparator = tripleEquals) => lift2(lhs, rhs, comparator).getOr(lhs.isNone() && rhs.isNone());\n    const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();\n\n    const ELEMENT = 1;\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom$1(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom$1(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom$1(node);\n    };\n    const fromDom$1 = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom: fromDom$1,\n      fromPoint\n    };\n\n    const is$1 = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n\n    const eq = (e1, e2) => e1.dom === e2.dom;\n    const contains = (e1, e2) => {\n      const d1 = e1.dom;\n      const d2 = e2.dom;\n      return d1 === d2 ? false : d1.contains(d2);\n    };\n    const is = is$1;\n\n    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {\n      if (is(scope, a)) {\n        return Optional.some(scope);\n      } else if (isFunction(isRoot) && isRoot(scope)) {\n        return Optional.none();\n      } else {\n        return ancestor(scope, a, isRoot);\n      }\n    };\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const name = element => {\n      const r = element.dom.nodeName;\n      return r.toLowerCase();\n    };\n    const type = element => element.dom.nodeType;\n    const isType = t => element => type(element) === t;\n    const isElement$1 = isType(ELEMENT);\n    const isTag = tag => e => isElement$1(e) && name(e) === tag;\n\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);\n    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);\n    const children = element => map(element.dom.childNodes, SugarElement.fromDom);\n    const child = (element, index) => {\n      const cs = element.dom.childNodes;\n      return Optional.from(cs[index]).map(SugarElement.fromDom);\n    };\n    const firstChild = element => child(element, 0);\n    const lastChild = element => child(element, element.dom.childNodes.length - 1);\n\n    const ancestor = (scope, predicate, isRoot) => {\n      let element = scope.dom;\n      const stop = isFunction(isRoot) ? isRoot : never;\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        if (predicate(el)) {\n          return Optional.some(el);\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const closest = (scope, predicate, isRoot) => {\n      const is = (s, test) => test(s);\n      return ClosestOrAncestor(is, ancestor, scope, predicate, isRoot);\n    };\n\n    const before$1 = (marker, element) => {\n      const parent$1 = parent(marker);\n      parent$1.each(v => {\n        v.dom.insertBefore(element.dom, marker.dom);\n      });\n    };\n    const after = (marker, element) => {\n      const sibling = nextSibling(marker);\n      sibling.fold(() => {\n        const parent$1 = parent(marker);\n        parent$1.each(v => {\n          append$1(v, element);\n        });\n      }, v => {\n        before$1(v, element);\n      });\n    };\n    const append$1 = (parent, element) => {\n      parent.dom.appendChild(element.dom);\n    };\n\n    const before = (marker, elements) => {\n      each$1(elements, x => {\n        before$1(marker, x);\n      });\n    };\n    const append = (parent, elements) => {\n      each$1(elements, x => {\n        append$1(parent, x);\n      });\n    };\n\n    const empty = element => {\n      element.dom.textContent = '';\n      each$1(children(element), rogue => {\n        remove(rogue);\n      });\n    };\n    const remove = element => {\n      const dom = element.dom;\n      if (dom.parentNode !== null) {\n        dom.parentNode.removeChild(dom);\n      }\n    };\n\n    var global$5 = tinymce.util.Tools.resolve('tinymce.dom.RangeUtils');\n\n    var global$4 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.util.VK');\n\n    const fromDom = nodes => map(nodes, SugarElement.fromDom);\n\n    const keys = Object.keys;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const objAcc = r => (x, i) => {\n      r[i] = x;\n    };\n    const internalFilter = (obj, pred, onTrue, onFalse) => {\n      each(obj, (x, i) => {\n        (pred(x, i) ? onTrue : onFalse)(x, i);\n      });\n    };\n    const filter = (obj, pred) => {\n      const t = {};\n      internalFilter(obj, pred, objAcc(t), noop);\n      return t;\n    };\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const setAll = (element, attrs) => {\n      const dom = element.dom;\n      each(attrs, (v, k) => {\n        rawSet(dom, k, v);\n      });\n    };\n    const clone$1 = element => foldl(element.dom.attributes, (acc, attr) => {\n      acc[attr.name] = attr.value;\n      return acc;\n    }, {});\n\n    const clone = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));\n    const deep = original => clone(original, true);\n    const shallowAs = (original, tag) => {\n      const nu = SugarElement.fromTag(tag);\n      const attributes = clone$1(original);\n      setAll(nu, attributes);\n      return nu;\n    };\n    const mutate = (original, tag) => {\n      const nu = shallowAs(original, tag);\n      after(original, nu);\n      const children$1 = children(original);\n      append(nu, children$1);\n      remove(original);\n      return nu;\n    };\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const matchNodeName = name => node => isNonNullable(node) && node.nodeName.toLowerCase() === name;\n    const matchNodeNames = regex => node => isNonNullable(node) && regex.test(node.nodeName);\n    const isTextNode = node => isNonNullable(node) && node.nodeType === 3;\n    const isElement = node => isNonNullable(node) && node.nodeType === 1;\n    const isListNode = matchNodeNames(/^(OL|UL|DL)$/);\n    const isOlUlNode = matchNodeNames(/^(OL|UL)$/);\n    const isOlNode = matchNodeName('ol');\n    const isListItemNode = matchNodeNames(/^(LI|DT|DD)$/);\n    const isDlItemNode = matchNodeNames(/^(DT|DD)$/);\n    const isTableCellNode = matchNodeNames(/^(TH|TD)$/);\n    const isBr = matchNodeName('br');\n    const isFirstChild = node => {\n      var _a;\n      return ((_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.firstChild) === node;\n    };\n    const isTextBlock = (editor, node) => isNonNullable(node) && node.nodeName in editor.schema.getTextBlockElements();\n    const isBlock = (node, blockElements) => isNonNullable(node) && node.nodeName in blockElements;\n    const isBogusBr = (dom, node) => {\n      if (!isBr(node)) {\n        return false;\n      }\n      return dom.isBlock(node.nextSibling) && !isBr(node.previousSibling);\n    };\n    const isEmpty$1 = (dom, elm, keepBookmarks) => {\n      const empty = dom.isEmpty(elm);\n      if (keepBookmarks && dom.select('span[data-mce-type=bookmark]', elm).length > 0) {\n        return false;\n      }\n      return empty;\n    };\n    const isChildOfBody = (dom, elm) => dom.isChildOf(elm, dom.getRoot());\n\n    const option = name => editor => editor.options.get(name);\n    const register$3 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('lists_indent_on_tab', {\n        processor: 'boolean',\n        default: true\n      });\n    };\n    const shouldIndentOnTab = option('lists_indent_on_tab');\n    const getForcedRootBlock = option('forced_root_block');\n    const getForcedRootBlockAttrs = option('forced_root_block_attrs');\n\n    const createTextBlock = (editor, contentNode) => {\n      const dom = editor.dom;\n      const blockElements = editor.schema.getBlockElements();\n      const fragment = dom.createFragment();\n      const blockName = getForcedRootBlock(editor);\n      const blockAttrs = getForcedRootBlockAttrs(editor);\n      let node;\n      let textBlock;\n      let hasContentNode = false;\n      textBlock = dom.create(blockName, blockAttrs);\n      if (!isBlock(contentNode.firstChild, blockElements)) {\n        fragment.appendChild(textBlock);\n      }\n      while (node = contentNode.firstChild) {\n        const nodeName = node.nodeName;\n        if (!hasContentNode && (nodeName !== 'SPAN' || node.getAttribute('data-mce-type') !== 'bookmark')) {\n          hasContentNode = true;\n        }\n        if (isBlock(node, blockElements)) {\n          fragment.appendChild(node);\n          textBlock = null;\n        } else {\n          if (!textBlock) {\n            textBlock = dom.create(blockName, blockAttrs);\n            fragment.appendChild(textBlock);\n          }\n          textBlock.appendChild(node);\n        }\n      }\n      if (!hasContentNode && textBlock) {\n        textBlock.appendChild(dom.create('br', { 'data-mce-bogus': '1' }));\n      }\n      return fragment;\n    };\n\n    const DOM$2 = global$2.DOM;\n    const splitList = (editor, list, li) => {\n      const removeAndKeepBookmarks = targetNode => {\n        const parent = targetNode.parentNode;\n        if (parent) {\n          global$1.each(bookmarks, node => {\n            parent.insertBefore(node, li.parentNode);\n          });\n        }\n        DOM$2.remove(targetNode);\n      };\n      const bookmarks = DOM$2.select('span[data-mce-type=\"bookmark\"]', list);\n      const newBlock = createTextBlock(editor, li);\n      const tmpRng = DOM$2.createRng();\n      tmpRng.setStartAfter(li);\n      tmpRng.setEndAfter(list);\n      const fragment = tmpRng.extractContents();\n      for (let node = fragment.firstChild; node; node = node.firstChild) {\n        if (node.nodeName === 'LI' && editor.dom.isEmpty(node)) {\n          DOM$2.remove(node);\n          break;\n        }\n      }\n      if (!editor.dom.isEmpty(fragment)) {\n        DOM$2.insertAfter(fragment, list);\n      }\n      DOM$2.insertAfter(newBlock, list);\n      const parent = li.parentElement;\n      if (parent && isEmpty$1(editor.dom, parent)) {\n        removeAndKeepBookmarks(parent);\n      }\n      DOM$2.remove(li);\n      if (isEmpty$1(editor.dom, list)) {\n        DOM$2.remove(list);\n      }\n    };\n\n    const isDescriptionDetail = isTag('dd');\n    const isDescriptionTerm = isTag('dt');\n    const outdentDlItem = (editor, item) => {\n      if (isDescriptionDetail(item)) {\n        mutate(item, 'dt');\n      } else if (isDescriptionTerm(item)) {\n        parentElement(item).each(dl => splitList(editor, dl.dom, item.dom));\n      }\n    };\n    const indentDlItem = item => {\n      if (isDescriptionTerm(item)) {\n        mutate(item, 'dd');\n      }\n    };\n    const dlIndentation = (editor, indentation, dlItems) => {\n      if (indentation === 'Indent') {\n        each$1(dlItems, indentDlItem);\n      } else {\n        each$1(dlItems, item => outdentDlItem(editor, item));\n      }\n    };\n\n    const getNormalizedPoint = (container, offset) => {\n      if (isTextNode(container)) {\n        return {\n          container,\n          offset\n        };\n      }\n      const node = global$5.getNode(container, offset);\n      if (isTextNode(node)) {\n        return {\n          container: node,\n          offset: offset >= container.childNodes.length ? node.data.length : 0\n        };\n      } else if (node.previousSibling && isTextNode(node.previousSibling)) {\n        return {\n          container: node.previousSibling,\n          offset: node.previousSibling.data.length\n        };\n      } else if (node.nextSibling && isTextNode(node.nextSibling)) {\n        return {\n          container: node.nextSibling,\n          offset: 0\n        };\n      }\n      return {\n        container,\n        offset\n      };\n    };\n    const normalizeRange = rng => {\n      const outRng = rng.cloneRange();\n      const rangeStart = getNormalizedPoint(rng.startContainer, rng.startOffset);\n      outRng.setStart(rangeStart.container, rangeStart.offset);\n      const rangeEnd = getNormalizedPoint(rng.endContainer, rng.endOffset);\n      outRng.setEnd(rangeEnd.container, rangeEnd.offset);\n      return outRng;\n    };\n\n    const listNames = [\n      'OL',\n      'UL',\n      'DL'\n    ];\n    const listSelector = listNames.join(',');\n    const getParentList = (editor, node) => {\n      const selectionStart = node || editor.selection.getStart(true);\n      return editor.dom.getParent(selectionStart, listSelector, getClosestListHost(editor, selectionStart));\n    };\n    const isParentListSelected = (parentList, selectedBlocks) => isNonNullable(parentList) && selectedBlocks.length === 1 && selectedBlocks[0] === parentList;\n    const findSubLists = parentList => filter$1(parentList.querySelectorAll(listSelector), isListNode);\n    const getSelectedSubLists = editor => {\n      const parentList = getParentList(editor);\n      const selectedBlocks = editor.selection.getSelectedBlocks();\n      if (isParentListSelected(parentList, selectedBlocks)) {\n        return findSubLists(parentList);\n      } else {\n        return filter$1(selectedBlocks, elm => {\n          return isListNode(elm) && parentList !== elm;\n        });\n      }\n    };\n    const findParentListItemsNodes = (editor, elms) => {\n      const listItemsElms = global$1.map(elms, elm => {\n        const parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListHost(editor, elm));\n        return parentLi ? parentLi : elm;\n      });\n      return unique(listItemsElms);\n    };\n    const getSelectedListItems = editor => {\n      const selectedBlocks = editor.selection.getSelectedBlocks();\n      return filter$1(findParentListItemsNodes(editor, selectedBlocks), isListItemNode);\n    };\n    const getSelectedDlItems = editor => filter$1(getSelectedListItems(editor), isDlItemNode);\n    const getClosestEditingHost = (editor, elm) => {\n      const parentTableCell = editor.dom.getParents(elm, 'TD,TH');\n      return parentTableCell.length > 0 ? parentTableCell[0] : editor.getBody();\n    };\n    const isListHost = (schema, node) => !isListNode(node) && !isListItemNode(node) && exists(listNames, listName => schema.isValidChild(node.nodeName, listName));\n    const getClosestListHost = (editor, elm) => {\n      const parentBlocks = editor.dom.getParents(elm, editor.dom.isBlock);\n      const parentBlock = find(parentBlocks, elm => isListHost(editor.schema, elm));\n      return parentBlock.getOr(editor.getBody());\n    };\n    const findLastParentListNode = (editor, elm) => {\n      const parentLists = editor.dom.getParents(elm, 'ol,ul', getClosestListHost(editor, elm));\n      return last(parentLists);\n    };\n    const getSelectedLists = editor => {\n      const firstList = findLastParentListNode(editor, editor.selection.getStart());\n      const subsequentLists = filter$1(editor.selection.getSelectedBlocks(), isOlUlNode);\n      return firstList.toArray().concat(subsequentLists);\n    };\n    const getSelectedListRoots = editor => {\n      const selectedLists = getSelectedLists(editor);\n      return getUniqueListRoots(editor, selectedLists);\n    };\n    const getUniqueListRoots = (editor, lists) => {\n      const listRoots = map(lists, list => findLastParentListNode(editor, list).getOr(list));\n      return unique(listRoots);\n    };\n\n    const isCustomList = list => /\\btox\\-/.test(list.className);\n    const inList = (parents, listName) => findUntil(parents, isListNode, isTableCellNode).exists(list => list.nodeName === listName && !isCustomList(list));\n    const isWithinNonEditable = (editor, element) => element !== null && editor.dom.getContentEditableParent(element) === 'false';\n    const selectionIsWithinNonEditableList = editor => {\n      const parentList = getParentList(editor);\n      return isWithinNonEditable(editor, parentList);\n    };\n    const isWithinNonEditableList = (editor, element) => {\n      const parentList = editor.dom.getParent(element, 'ol,ul,dl');\n      return isWithinNonEditable(editor, parentList);\n    };\n    const setNodeChangeHandler = (editor, nodeChangeHandler) => {\n      const initialNode = editor.selection.getNode();\n      nodeChangeHandler({\n        parents: editor.dom.getParents(initialNode),\n        element: initialNode\n      });\n      editor.on('NodeChange', nodeChangeHandler);\n      return () => editor.off('NodeChange', nodeChangeHandler);\n    };\n\n    const fromElements = (elements, scope) => {\n      const doc = scope || document;\n      const fragment = doc.createDocumentFragment();\n      each$1(elements, element => {\n        fragment.appendChild(element.dom);\n      });\n      return SugarElement.fromDom(fragment);\n    };\n\n    const fireListEvent = (editor, action, element) => editor.dispatch('ListMutation', {\n      action,\n      element\n    });\n\n    const blank = r => s => s.replace(r, '');\n    const trim = blank(/^\\s+|\\s+$/g);\n    const isNotEmpty = s => s.length > 0;\n    const isEmpty = s => !isNotEmpty(s);\n\n    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);\n\n    const internalSet = (dom, property, value) => {\n      if (!isString(value)) {\n        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);\n        throw new Error('CSS value must be a string: ' + value);\n      }\n      if (isSupported(dom)) {\n        dom.style.setProperty(property, value);\n      }\n    };\n    const set = (element, property, value) => {\n      const dom = element.dom;\n      internalSet(dom, property, value);\n    };\n\n    const joinSegment = (parent, child) => {\n      append$1(parent.item, child.list);\n    };\n    const joinSegments = segments => {\n      for (let i = 1; i < segments.length; i++) {\n        joinSegment(segments[i - 1], segments[i]);\n      }\n    };\n    const appendSegments = (head$1, tail) => {\n      lift2(last(head$1), head(tail), joinSegment);\n    };\n    const createSegment = (scope, listType) => {\n      const segment = {\n        list: SugarElement.fromTag(listType, scope),\n        item: SugarElement.fromTag('li', scope)\n      };\n      append$1(segment.list, segment.item);\n      return segment;\n    };\n    const createSegments = (scope, entry, size) => {\n      const segments = [];\n      for (let i = 0; i < size; i++) {\n        segments.push(createSegment(scope, entry.listType));\n      }\n      return segments;\n    };\n    const populateSegments = (segments, entry) => {\n      for (let i = 0; i < segments.length - 1; i++) {\n        set(segments[i].item, 'list-style-type', 'none');\n      }\n      last(segments).each(segment => {\n        setAll(segment.list, entry.listAttributes);\n        setAll(segment.item, entry.itemAttributes);\n        append(segment.item, entry.content);\n      });\n    };\n    const normalizeSegment = (segment, entry) => {\n      if (name(segment.list) !== entry.listType) {\n        segment.list = mutate(segment.list, entry.listType);\n      }\n      setAll(segment.list, entry.listAttributes);\n    };\n    const createItem = (scope, attr, content) => {\n      const item = SugarElement.fromTag('li', scope);\n      setAll(item, attr);\n      append(item, content);\n      return item;\n    };\n    const appendItem = (segment, item) => {\n      append$1(segment.list, item);\n      segment.item = item;\n    };\n    const writeShallow = (scope, cast, entry) => {\n      const newCast = cast.slice(0, entry.depth);\n      last(newCast).each(segment => {\n        const item = createItem(scope, entry.itemAttributes, entry.content);\n        appendItem(segment, item);\n        normalizeSegment(segment, entry);\n      });\n      return newCast;\n    };\n    const writeDeep = (scope, cast, entry) => {\n      const segments = createSegments(scope, entry, entry.depth - cast.length);\n      joinSegments(segments);\n      populateSegments(segments, entry);\n      appendSegments(cast, segments);\n      return cast.concat(segments);\n    };\n    const composeList = (scope, entries) => {\n      const cast = foldl(entries, (cast, entry) => {\n        return entry.depth > cast.length ? writeDeep(scope, cast, entry) : writeShallow(scope, cast, entry);\n      }, []);\n      return head(cast).map(segment => segment.list);\n    };\n\n    const isList = el => is(el, 'OL,UL');\n    const hasFirstChildList = el => firstChild(el).exists(isList);\n    const hasLastChildList = el => lastChild(el).exists(isList);\n\n    const isIndented = entry => entry.depth > 0;\n    const isSelected = entry => entry.isSelected;\n    const cloneItemContent = li => {\n      const children$1 = children(li);\n      const content = hasLastChildList(li) ? children$1.slice(0, -1) : children$1;\n      return map(content, deep);\n    };\n    const createEntry = (li, depth, isSelected) => parent(li).filter(isElement$1).map(list => ({\n      depth,\n      dirty: false,\n      isSelected,\n      content: cloneItemContent(li),\n      itemAttributes: clone$1(li),\n      listAttributes: clone$1(list),\n      listType: name(list)\n    }));\n\n    const indentEntry = (indentation, entry) => {\n      switch (indentation) {\n      case 'Indent':\n        entry.depth++;\n        break;\n      case 'Outdent':\n        entry.depth--;\n        break;\n      case 'Flatten':\n        entry.depth = 0;\n      }\n      entry.dirty = true;\n    };\n\n    const cloneListProperties = (target, source) => {\n      target.listType = source.listType;\n      target.listAttributes = { ...source.listAttributes };\n    };\n    const cleanListProperties = entry => {\n      entry.listAttributes = filter(entry.listAttributes, (_value, key) => key !== 'start');\n    };\n    const closestSiblingEntry = (entries, start) => {\n      const depth = entries[start].depth;\n      const matches = entry => entry.depth === depth && !entry.dirty;\n      const until = entry => entry.depth < depth;\n      return findUntil(reverse(entries.slice(0, start)), matches, until).orThunk(() => findUntil(entries.slice(start + 1), matches, until));\n    };\n    const normalizeEntries = entries => {\n      each$1(entries, (entry, i) => {\n        closestSiblingEntry(entries, i).fold(() => {\n          if (entry.dirty) {\n            cleanListProperties(entry);\n          }\n        }, matchingEntry => cloneListProperties(entry, matchingEntry));\n      });\n      return entries;\n    };\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const parseItem = (depth, itemSelection, selectionState, item) => firstChild(item).filter(isList).fold(() => {\n      itemSelection.each(selection => {\n        if (eq(selection.start, item)) {\n          selectionState.set(true);\n        }\n      });\n      const currentItemEntry = createEntry(item, depth, selectionState.get());\n      itemSelection.each(selection => {\n        if (eq(selection.end, item)) {\n          selectionState.set(false);\n        }\n      });\n      const childListEntries = lastChild(item).filter(isList).map(list => parseList(depth, itemSelection, selectionState, list)).getOr([]);\n      return currentItemEntry.toArray().concat(childListEntries);\n    }, list => parseList(depth, itemSelection, selectionState, list));\n    const parseList = (depth, itemSelection, selectionState, list) => bind(children(list), element => {\n      const parser = isList(element) ? parseList : parseItem;\n      const newDepth = depth + 1;\n      return parser(newDepth, itemSelection, selectionState, element);\n    });\n    const parseLists = (lists, itemSelection) => {\n      const selectionState = Cell(false);\n      const initialDepth = 0;\n      return map(lists, list => ({\n        sourceList: list,\n        entries: parseList(initialDepth, itemSelection, selectionState, list)\n      }));\n    };\n\n    const outdentedComposer = (editor, entries) => {\n      const normalizedEntries = normalizeEntries(entries);\n      return map(normalizedEntries, entry => {\n        const content = fromElements(entry.content);\n        return SugarElement.fromDom(createTextBlock(editor, content.dom));\n      });\n    };\n    const indentedComposer = (editor, entries) => {\n      const normalizedEntries = normalizeEntries(entries);\n      return composeList(editor.contentDocument, normalizedEntries).toArray();\n    };\n    const composeEntries = (editor, entries) => bind(groupBy(entries, isIndented), entries => {\n      const groupIsIndented = head(entries).exists(isIndented);\n      return groupIsIndented ? indentedComposer(editor, entries) : outdentedComposer(editor, entries);\n    });\n    const indentSelectedEntries = (entries, indentation) => {\n      each$1(filter$1(entries, isSelected), entry => indentEntry(indentation, entry));\n    };\n    const getItemSelection = editor => {\n      const selectedListItems = map(getSelectedListItems(editor), SugarElement.fromDom);\n      return lift2(find(selectedListItems, not(hasFirstChildList)), find(reverse(selectedListItems), not(hasFirstChildList)), (start, end) => ({\n        start,\n        end\n      }));\n    };\n    const listIndentation = (editor, lists, indentation) => {\n      const entrySets = parseLists(lists, getItemSelection(editor));\n      each$1(entrySets, entrySet => {\n        indentSelectedEntries(entrySet.entries, indentation);\n        const composedLists = composeEntries(editor, entrySet.entries);\n        each$1(composedLists, composedList => {\n          fireListEvent(editor, indentation === 'Indent' ? 'IndentList' : 'OutdentList', composedList.dom);\n        });\n        before(entrySet.sourceList, composedLists);\n        remove(entrySet.sourceList);\n      });\n    };\n\n    const selectionIndentation = (editor, indentation) => {\n      const lists = fromDom(getSelectedListRoots(editor));\n      const dlItems = fromDom(getSelectedDlItems(editor));\n      let isHandled = false;\n      if (lists.length || dlItems.length) {\n        const bookmark = editor.selection.getBookmark();\n        listIndentation(editor, lists, indentation);\n        dlIndentation(editor, indentation, dlItems);\n        editor.selection.moveToBookmark(bookmark);\n        editor.selection.setRng(normalizeRange(editor.selection.getRng()));\n        editor.nodeChanged();\n        isHandled = true;\n      }\n      return isHandled;\n    };\n    const handleIndentation = (editor, indentation) => !selectionIsWithinNonEditableList(editor) && selectionIndentation(editor, indentation);\n    const indentListSelection = editor => handleIndentation(editor, 'Indent');\n    const outdentListSelection = editor => handleIndentation(editor, 'Outdent');\n    const flattenListSelection = editor => handleIndentation(editor, 'Flatten');\n\n    var global = tinymce.util.Tools.resolve('tinymce.dom.BookmarkManager');\n\n    const DOM$1 = global$2.DOM;\n    const createBookmark = rng => {\n      const bookmark = {};\n      const setupEndPoint = start => {\n        let container = rng[start ? 'startContainer' : 'endContainer'];\n        let offset = rng[start ? 'startOffset' : 'endOffset'];\n        if (isElement(container)) {\n          const offsetNode = DOM$1.create('span', { 'data-mce-type': 'bookmark' });\n          if (container.hasChildNodes()) {\n            offset = Math.min(offset, container.childNodes.length - 1);\n            if (start) {\n              container.insertBefore(offsetNode, container.childNodes[offset]);\n            } else {\n              DOM$1.insertAfter(offsetNode, container.childNodes[offset]);\n            }\n          } else {\n            container.appendChild(offsetNode);\n          }\n          container = offsetNode;\n          offset = 0;\n        }\n        bookmark[start ? 'startContainer' : 'endContainer'] = container;\n        bookmark[start ? 'startOffset' : 'endOffset'] = offset;\n      };\n      setupEndPoint(true);\n      if (!rng.collapsed) {\n        setupEndPoint();\n      }\n      return bookmark;\n    };\n    const resolveBookmark = bookmark => {\n      const restoreEndPoint = start => {\n        const nodeIndex = container => {\n          var _a;\n          let node = (_a = container.parentNode) === null || _a === void 0 ? void 0 : _a.firstChild;\n          let idx = 0;\n          while (node) {\n            if (node === container) {\n              return idx;\n            }\n            if (!isElement(node) || node.getAttribute('data-mce-type') !== 'bookmark') {\n              idx++;\n            }\n            node = node.nextSibling;\n          }\n          return -1;\n        };\n        let container = bookmark[start ? 'startContainer' : 'endContainer'];\n        let offset = bookmark[start ? 'startOffset' : 'endOffset'];\n        if (!container) {\n          return;\n        }\n        if (isElement(container) && container.parentNode) {\n          const node = container;\n          offset = nodeIndex(container);\n          container = container.parentNode;\n          DOM$1.remove(node);\n          if (!container.hasChildNodes() && DOM$1.isBlock(container)) {\n            container.appendChild(DOM$1.create('br'));\n          }\n        }\n        bookmark[start ? 'startContainer' : 'endContainer'] = container;\n        bookmark[start ? 'startOffset' : 'endOffset'] = offset;\n      };\n      restoreEndPoint(true);\n      restoreEndPoint();\n      const rng = DOM$1.createRng();\n      rng.setStart(bookmark.startContainer, bookmark.startOffset);\n      if (bookmark.endContainer) {\n        rng.setEnd(bookmark.endContainer, bookmark.endOffset);\n      }\n      return normalizeRange(rng);\n    };\n\n    const listToggleActionFromListName = listName => {\n      switch (listName) {\n      case 'UL':\n        return 'ToggleUlList';\n      case 'OL':\n        return 'ToggleOlList';\n      case 'DL':\n        return 'ToggleDLList';\n      }\n    };\n\n    const updateListStyle = (dom, el, detail) => {\n      const type = detail['list-style-type'] ? detail['list-style-type'] : null;\n      dom.setStyle(el, 'list-style-type', type);\n    };\n    const setAttribs = (elm, attrs) => {\n      global$1.each(attrs, (value, key) => {\n        elm.setAttribute(key, value);\n      });\n    };\n    const updateListAttrs = (dom, el, detail) => {\n      setAttribs(el, detail['list-attributes']);\n      global$1.each(dom.select('li', el), li => {\n        setAttribs(li, detail['list-item-attributes']);\n      });\n    };\n    const updateListWithDetails = (dom, el, detail) => {\n      updateListStyle(dom, el, detail);\n      updateListAttrs(dom, el, detail);\n    };\n    const removeStyles = (dom, element, styles) => {\n      global$1.each(styles, style => dom.setStyle(element, style, ''));\n    };\n    const getEndPointNode = (editor, rng, start, root) => {\n      let container = rng[start ? 'startContainer' : 'endContainer'];\n      const offset = rng[start ? 'startOffset' : 'endOffset'];\n      if (isElement(container)) {\n        container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;\n      }\n      if (!start && isBr(container.nextSibling)) {\n        container = container.nextSibling;\n      }\n      while (container.parentNode !== root) {\n        const parent = container.parentNode;\n        if (isTextBlock(editor, container)) {\n          return container;\n        }\n        if (/^(TD|TH)$/.test(parent.nodeName)) {\n          return container;\n        }\n        container = parent;\n      }\n      return container;\n    };\n    const getSelectedTextBlocks = (editor, rng, root) => {\n      const textBlocks = [];\n      const dom = editor.dom;\n      const startNode = getEndPointNode(editor, rng, true, root);\n      const endNode = getEndPointNode(editor, rng, false, root);\n      let block;\n      const siblings = [];\n      for (let node = startNode; node; node = node.nextSibling) {\n        siblings.push(node);\n        if (node === endNode) {\n          break;\n        }\n      }\n      global$1.each(siblings, node => {\n        var _a;\n        if (isTextBlock(editor, node)) {\n          textBlocks.push(node);\n          block = null;\n          return;\n        }\n        if (dom.isBlock(node) || isBr(node)) {\n          if (isBr(node)) {\n            dom.remove(node);\n          }\n          block = null;\n          return;\n        }\n        const nextSibling = node.nextSibling;\n        if (global.isBookmarkNode(node)) {\n          if (isListNode(nextSibling) || isTextBlock(editor, nextSibling) || !nextSibling && node.parentNode === root) {\n            block = null;\n            return;\n          }\n        }\n        if (!block) {\n          block = dom.create('p');\n          (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(block, node);\n          textBlocks.push(block);\n        }\n        block.appendChild(node);\n      });\n      return textBlocks;\n    };\n    const hasCompatibleStyle = (dom, sib, detail) => {\n      const sibStyle = dom.getStyle(sib, 'list-style-type');\n      let detailStyle = detail ? detail['list-style-type'] : '';\n      detailStyle = detailStyle === null ? '' : detailStyle;\n      return sibStyle === detailStyle;\n    };\n    const applyList = (editor, listName, detail) => {\n      const rng = editor.selection.getRng();\n      let listItemName = 'LI';\n      const root = getClosestListHost(editor, editor.selection.getStart(true));\n      const dom = editor.dom;\n      if (dom.getContentEditable(editor.selection.getNode()) === 'false') {\n        return;\n      }\n      listName = listName.toUpperCase();\n      if (listName === 'DL') {\n        listItemName = 'DT';\n      }\n      const bookmark = createBookmark(rng);\n      const selectedTextBlocks = getSelectedTextBlocks(editor, rng, root);\n      global$1.each(selectedTextBlocks, block => {\n        let listBlock;\n        const sibling = block.previousSibling;\n        const parent = block.parentNode;\n        if (!isListItemNode(parent)) {\n          if (sibling && isListNode(sibling) && sibling.nodeName === listName && hasCompatibleStyle(dom, sibling, detail)) {\n            listBlock = sibling;\n            block = dom.rename(block, listItemName);\n            sibling.appendChild(block);\n          } else {\n            listBlock = dom.create(listName);\n            parent.insertBefore(listBlock, block);\n            listBlock.appendChild(block);\n            block = dom.rename(block, listItemName);\n          }\n          removeStyles(dom, block, [\n            'margin',\n            'margin-right',\n            'margin-bottom',\n            'margin-left',\n            'margin-top',\n            'padding',\n            'padding-right',\n            'padding-bottom',\n            'padding-left',\n            'padding-top'\n          ]);\n          updateListWithDetails(dom, listBlock, detail);\n          mergeWithAdjacentLists(editor.dom, listBlock);\n        }\n      });\n      editor.selection.setRng(resolveBookmark(bookmark));\n    };\n    const isValidLists = (list1, list2) => {\n      return isListNode(list1) && list1.nodeName === (list2 === null || list2 === void 0 ? void 0 : list2.nodeName);\n    };\n    const hasSameListStyle = (dom, list1, list2) => {\n      const targetStyle = dom.getStyle(list1, 'list-style-type', true);\n      const style = dom.getStyle(list2, 'list-style-type', true);\n      return targetStyle === style;\n    };\n    const hasSameClasses = (elm1, elm2) => {\n      return elm1.className === elm2.className;\n    };\n    const shouldMerge = (dom, list1, list2) => {\n      return isValidLists(list1, list2) && hasSameListStyle(dom, list1, list2) && hasSameClasses(list1, list2);\n    };\n    const mergeWithAdjacentLists = (dom, listBlock) => {\n      let node;\n      let sibling = listBlock.nextSibling;\n      if (shouldMerge(dom, listBlock, sibling)) {\n        const liSibling = sibling;\n        while (node = liSibling.firstChild) {\n          listBlock.appendChild(node);\n        }\n        dom.remove(liSibling);\n      }\n      sibling = listBlock.previousSibling;\n      if (shouldMerge(dom, listBlock, sibling)) {\n        const liSibling = sibling;\n        while (node = liSibling.lastChild) {\n          listBlock.insertBefore(node, listBlock.firstChild);\n        }\n        dom.remove(liSibling);\n      }\n    };\n    const updateList$1 = (editor, list, listName, detail) => {\n      if (list.nodeName !== listName) {\n        const newList = editor.dom.rename(list, listName);\n        updateListWithDetails(editor.dom, newList, detail);\n        fireListEvent(editor, listToggleActionFromListName(listName), newList);\n      } else {\n        updateListWithDetails(editor.dom, list, detail);\n        fireListEvent(editor, listToggleActionFromListName(listName), list);\n      }\n    };\n    const toggleMultipleLists = (editor, parentList, lists, listName, detail) => {\n      const parentIsList = isListNode(parentList);\n      if (parentIsList && parentList.nodeName === listName && !hasListStyleDetail(detail)) {\n        flattenListSelection(editor);\n      } else {\n        applyList(editor, listName, detail);\n        const bookmark = createBookmark(editor.selection.getRng());\n        const allLists = parentIsList ? [\n          parentList,\n          ...lists\n        ] : lists;\n        global$1.each(allLists, elm => {\n          updateList$1(editor, elm, listName, detail);\n        });\n        editor.selection.setRng(resolveBookmark(bookmark));\n      }\n    };\n    const hasListStyleDetail = detail => {\n      return 'list-style-type' in detail;\n    };\n    const toggleSingleList = (editor, parentList, listName, detail) => {\n      if (parentList === editor.getBody()) {\n        return;\n      }\n      if (parentList) {\n        if (parentList.nodeName === listName && !hasListStyleDetail(detail) && !isCustomList(parentList)) {\n          flattenListSelection(editor);\n        } else {\n          const bookmark = createBookmark(editor.selection.getRng());\n          updateListWithDetails(editor.dom, parentList, detail);\n          const newList = editor.dom.rename(parentList, listName);\n          mergeWithAdjacentLists(editor.dom, newList);\n          editor.selection.setRng(resolveBookmark(bookmark));\n          applyList(editor, listName, detail);\n          fireListEvent(editor, listToggleActionFromListName(listName), newList);\n        }\n      } else {\n        applyList(editor, listName, detail);\n        fireListEvent(editor, listToggleActionFromListName(listName), parentList);\n      }\n    };\n    const toggleList = (editor, listName, _detail) => {\n      const parentList = getParentList(editor);\n      if (isWithinNonEditableList(editor, parentList)) {\n        return;\n      }\n      const selectedSubLists = getSelectedSubLists(editor);\n      const detail = isObject(_detail) ? _detail : {};\n      if (selectedSubLists.length > 0) {\n        toggleMultipleLists(editor, parentList, selectedSubLists, listName, detail);\n      } else {\n        toggleSingleList(editor, parentList, listName, detail);\n      }\n    };\n\n    const DOM = global$2.DOM;\n    const normalizeList = (dom, list) => {\n      const parentNode = list.parentElement;\n      if (parentNode && parentNode.nodeName === 'LI' && parentNode.firstChild === list) {\n        const sibling = parentNode.previousSibling;\n        if (sibling && sibling.nodeName === 'LI') {\n          sibling.appendChild(list);\n          if (isEmpty$1(dom, parentNode)) {\n            DOM.remove(parentNode);\n          }\n        } else {\n          DOM.setStyle(parentNode, 'listStyleType', 'none');\n        }\n      }\n      if (isListNode(parentNode)) {\n        const sibling = parentNode.previousSibling;\n        if (sibling && sibling.nodeName === 'LI') {\n          sibling.appendChild(list);\n        }\n      }\n    };\n    const normalizeLists = (dom, element) => {\n      const lists = global$1.grep(dom.select('ol,ul', element));\n      global$1.each(lists, list => {\n        normalizeList(dom, list);\n      });\n    };\n\n    const findNextCaretContainer = (editor, rng, isForward, root) => {\n      let node = rng.startContainer;\n      const offset = rng.startOffset;\n      if (isTextNode(node) && (isForward ? offset < node.data.length : offset > 0)) {\n        return node;\n      }\n      const nonEmptyBlocks = editor.schema.getNonEmptyElements();\n      if (isElement(node)) {\n        node = global$5.getNode(node, offset);\n      }\n      const walker = new global$4(node, root);\n      if (isForward) {\n        if (isBogusBr(editor.dom, node)) {\n          walker.next();\n        }\n      }\n      const walkFn = isForward ? walker.next.bind(walker) : walker.prev2.bind(walker);\n      while (node = walkFn()) {\n        if (node.nodeName === 'LI' && !node.hasChildNodes()) {\n          return node;\n        }\n        if (nonEmptyBlocks[node.nodeName]) {\n          return node;\n        }\n        if (isTextNode(node) && node.data.length > 0) {\n          return node;\n        }\n      }\n      return null;\n    };\n    const hasOnlyOneBlockChild = (dom, elm) => {\n      const childNodes = elm.childNodes;\n      return childNodes.length === 1 && !isListNode(childNodes[0]) && dom.isBlock(childNodes[0]);\n    };\n    const unwrapSingleBlockChild = (dom, elm) => {\n      if (hasOnlyOneBlockChild(dom, elm)) {\n        dom.remove(elm.firstChild, true);\n      }\n    };\n    const moveChildren = (dom, fromElm, toElm) => {\n      let node;\n      const targetElm = hasOnlyOneBlockChild(dom, toElm) ? toElm.firstChild : toElm;\n      unwrapSingleBlockChild(dom, fromElm);\n      if (!isEmpty$1(dom, fromElm, true)) {\n        while (node = fromElm.firstChild) {\n          targetElm.appendChild(node);\n        }\n      }\n    };\n    const mergeLiElements = (dom, fromElm, toElm) => {\n      let listNode;\n      const ul = fromElm.parentNode;\n      if (!isChildOfBody(dom, fromElm) || !isChildOfBody(dom, toElm)) {\n        return;\n      }\n      if (isListNode(toElm.lastChild)) {\n        listNode = toElm.lastChild;\n      }\n      if (ul === toElm.lastChild) {\n        if (isBr(ul.previousSibling)) {\n          dom.remove(ul.previousSibling);\n        }\n      }\n      const node = toElm.lastChild;\n      if (node && isBr(node) && fromElm.hasChildNodes()) {\n        dom.remove(node);\n      }\n      if (isEmpty$1(dom, toElm, true)) {\n        empty(SugarElement.fromDom(toElm));\n      }\n      moveChildren(dom, fromElm, toElm);\n      if (listNode) {\n        toElm.appendChild(listNode);\n      }\n      const contains$1 = contains(SugarElement.fromDom(toElm), SugarElement.fromDom(fromElm));\n      const nestedLists = contains$1 ? dom.getParents(fromElm, isListNode, toElm) : [];\n      dom.remove(fromElm);\n      each$1(nestedLists, list => {\n        if (isEmpty$1(dom, list) && list !== dom.getRoot()) {\n          dom.remove(list);\n        }\n      });\n    };\n    const mergeIntoEmptyLi = (editor, fromLi, toLi) => {\n      empty(SugarElement.fromDom(toLi));\n      mergeLiElements(editor.dom, fromLi, toLi);\n      editor.selection.setCursorLocation(toLi, 0);\n    };\n    const mergeForward = (editor, rng, fromLi, toLi) => {\n      const dom = editor.dom;\n      if (dom.isEmpty(toLi)) {\n        mergeIntoEmptyLi(editor, fromLi, toLi);\n      } else {\n        const bookmark = createBookmark(rng);\n        mergeLiElements(dom, fromLi, toLi);\n        editor.selection.setRng(resolveBookmark(bookmark));\n      }\n    };\n    const mergeBackward = (editor, rng, fromLi, toLi) => {\n      const bookmark = createBookmark(rng);\n      mergeLiElements(editor.dom, fromLi, toLi);\n      const resolvedBookmark = resolveBookmark(bookmark);\n      editor.selection.setRng(resolvedBookmark);\n    };\n    const backspaceDeleteFromListToListCaret = (editor, isForward) => {\n      const dom = editor.dom, selection = editor.selection;\n      const selectionStartElm = selection.getStart();\n      const root = getClosestEditingHost(editor, selectionStartElm);\n      const li = dom.getParent(selection.getStart(), 'LI', root);\n      if (li) {\n        const ul = li.parentElement;\n        if (ul === editor.getBody() && isEmpty$1(dom, ul)) {\n          return true;\n        }\n        const rng = normalizeRange(selection.getRng());\n        const otherLi = dom.getParent(findNextCaretContainer(editor, rng, isForward, root), 'LI', root);\n        if (otherLi && otherLi !== li) {\n          editor.undoManager.transact(() => {\n            if (isForward) {\n              mergeForward(editor, rng, otherLi, li);\n            } else {\n              if (isFirstChild(li)) {\n                outdentListSelection(editor);\n              } else {\n                mergeBackward(editor, rng, li, otherLi);\n              }\n            }\n          });\n          return true;\n        } else if (!otherLi) {\n          if (!isForward && rng.startOffset === 0 && rng.endOffset === 0) {\n            editor.undoManager.transact(() => {\n              flattenListSelection(editor);\n            });\n            return true;\n          }\n        }\n      }\n      return false;\n    };\n    const removeBlock = (dom, block, root) => {\n      const parentBlock = dom.getParent(block.parentNode, dom.isBlock, root);\n      dom.remove(block);\n      if (parentBlock && dom.isEmpty(parentBlock)) {\n        dom.remove(parentBlock);\n      }\n    };\n    const backspaceDeleteIntoListCaret = (editor, isForward) => {\n      const dom = editor.dom;\n      const selectionStartElm = editor.selection.getStart();\n      const root = getClosestEditingHost(editor, selectionStartElm);\n      const block = dom.getParent(selectionStartElm, dom.isBlock, root);\n      if (block && dom.isEmpty(block)) {\n        const rng = normalizeRange(editor.selection.getRng());\n        const otherLi = dom.getParent(findNextCaretContainer(editor, rng, isForward, root), 'LI', root);\n        if (otherLi) {\n          const findValidElement = element => contains$1([\n            'td',\n            'th',\n            'caption'\n          ], name(element));\n          const findRoot = node => node.dom === root;\n          const otherLiCell = closest(SugarElement.fromDom(otherLi), findValidElement, findRoot);\n          const caretCell = closest(SugarElement.fromDom(rng.startContainer), findValidElement, findRoot);\n          if (!equals(otherLiCell, caretCell, eq)) {\n            return false;\n          }\n          editor.undoManager.transact(() => {\n            removeBlock(dom, block, root);\n            mergeWithAdjacentLists(dom, otherLi.parentNode);\n            editor.selection.select(otherLi, true);\n            editor.selection.collapse(isForward);\n          });\n          return true;\n        }\n      }\n      return false;\n    };\n    const backspaceDeleteCaret = (editor, isForward) => {\n      return backspaceDeleteFromListToListCaret(editor, isForward) || backspaceDeleteIntoListCaret(editor, isForward);\n    };\n    const hasListSelection = editor => {\n      const selectionStartElm = editor.selection.getStart();\n      const root = getClosestEditingHost(editor, selectionStartElm);\n      const startListParent = editor.dom.getParent(selectionStartElm, 'LI,DT,DD', root);\n      return startListParent || getSelectedListItems(editor).length > 0;\n    };\n    const backspaceDeleteRange = editor => {\n      if (hasListSelection(editor)) {\n        editor.undoManager.transact(() => {\n          editor.execCommand('Delete');\n          normalizeLists(editor.dom, editor.getBody());\n        });\n        return true;\n      }\n      return false;\n    };\n    const backspaceDelete = (editor, isForward) => {\n      const selection = editor.selection;\n      return !isWithinNonEditableList(editor, selection.getNode()) && (selection.isCollapsed() ? backspaceDeleteCaret(editor, isForward) : backspaceDeleteRange(editor));\n    };\n    const setup$1 = editor => {\n      editor.on('ExecCommand', e => {\n        const cmd = e.command.toLowerCase();\n        if ((cmd === 'delete' || cmd === 'forwarddelete') && hasListSelection(editor)) {\n          normalizeLists(editor.dom, editor.getBody());\n        }\n      });\n      editor.on('keydown', e => {\n        if (e.keyCode === global$3.BACKSPACE) {\n          if (backspaceDelete(editor, false)) {\n            e.preventDefault();\n          }\n        } else if (e.keyCode === global$3.DELETE) {\n          if (backspaceDelete(editor, true)) {\n            e.preventDefault();\n          }\n        }\n      });\n    };\n\n    const get = editor => ({\n      backspaceDelete: isForward => {\n        backspaceDelete(editor, isForward);\n      }\n    });\n\n    const updateList = (editor, update) => {\n      const parentList = getParentList(editor);\n      if (parentList === null || isWithinNonEditableList(editor, parentList)) {\n        return;\n      }\n      editor.undoManager.transact(() => {\n        if (isObject(update.styles)) {\n          editor.dom.setStyles(parentList, update.styles);\n        }\n        if (isObject(update.attrs)) {\n          each(update.attrs, (v, k) => editor.dom.setAttrib(parentList, k, v));\n        }\n      });\n    };\n\n    const parseAlphabeticBase26 = str => {\n      const chars = reverse(trim(str).split(''));\n      const values = map(chars, (char, i) => {\n        const charValue = char.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0) + 1;\n        return Math.pow(26, i) * charValue;\n      });\n      return foldl(values, (sum, v) => sum + v, 0);\n    };\n    const composeAlphabeticBase26 = value => {\n      value--;\n      if (value < 0) {\n        return '';\n      } else {\n        const remainder = value % 26;\n        const quotient = Math.floor(value / 26);\n        const rest = composeAlphabeticBase26(quotient);\n        const char = String.fromCharCode('A'.charCodeAt(0) + remainder);\n        return rest + char;\n      }\n    };\n    const isUppercase = str => /^[A-Z]+$/.test(str);\n    const isLowercase = str => /^[a-z]+$/.test(str);\n    const isNumeric = str => /^[0-9]+$/.test(str);\n    const deduceListType = start => {\n      if (isNumeric(start)) {\n        return 2;\n      } else if (isUppercase(start)) {\n        return 0;\n      } else if (isLowercase(start)) {\n        return 1;\n      } else if (isEmpty(start)) {\n        return 3;\n      } else {\n        return 4;\n      }\n    };\n    const parseStartValue = start => {\n      switch (deduceListType(start)) {\n      case 2:\n        return Optional.some({\n          listStyleType: Optional.none(),\n          start\n        });\n      case 0:\n        return Optional.some({\n          listStyleType: Optional.some('upper-alpha'),\n          start: parseAlphabeticBase26(start).toString()\n        });\n      case 1:\n        return Optional.some({\n          listStyleType: Optional.some('lower-alpha'),\n          start: parseAlphabeticBase26(start).toString()\n        });\n      case 3:\n        return Optional.some({\n          listStyleType: Optional.none(),\n          start: ''\n        });\n      case 4:\n        return Optional.none();\n      }\n    };\n    const parseDetail = detail => {\n      const start = parseInt(detail.start, 10);\n      if (is$2(detail.listStyleType, 'upper-alpha')) {\n        return composeAlphabeticBase26(start);\n      } else if (is$2(detail.listStyleType, 'lower-alpha')) {\n        return composeAlphabeticBase26(start).toLowerCase();\n      } else {\n        return detail.start;\n      }\n    };\n\n    const open = editor => {\n      const currentList = getParentList(editor);\n      if (!isOlNode(currentList) || isWithinNonEditableList(editor, currentList)) {\n        return;\n      }\n      editor.windowManager.open({\n        title: 'List Properties',\n        body: {\n          type: 'panel',\n          items: [{\n              type: 'input',\n              name: 'start',\n              label: 'Start list at number',\n              inputMode: 'numeric'\n            }]\n        },\n        initialData: {\n          start: parseDetail({\n            start: editor.dom.getAttrib(currentList, 'start', '1'),\n            listStyleType: Optional.from(editor.dom.getStyle(currentList, 'list-style-type'))\n          })\n        },\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        onSubmit: api => {\n          const data = api.getData();\n          parseStartValue(data.start).each(detail => {\n            editor.execCommand('mceListUpdate', false, {\n              attrs: { start: detail.start === '1' ? '' : detail.start },\n              styles: { 'list-style-type': detail.listStyleType.getOr('') }\n            });\n          });\n          api.close();\n        }\n      });\n    };\n\n    const queryListCommandState = (editor, listName) => () => {\n      const parentList = getParentList(editor);\n      return isNonNullable(parentList) && parentList.nodeName === listName;\n    };\n    const registerDialog = editor => {\n      editor.addCommand('mceListProps', () => {\n        open(editor);\n      });\n    };\n    const register$2 = editor => {\n      editor.on('BeforeExecCommand', e => {\n        const cmd = e.command.toLowerCase();\n        if (cmd === 'indent') {\n          indentListSelection(editor);\n        } else if (cmd === 'outdent') {\n          outdentListSelection(editor);\n        }\n      });\n      editor.addCommand('InsertUnorderedList', (ui, detail) => {\n        toggleList(editor, 'UL', detail);\n      });\n      editor.addCommand('InsertOrderedList', (ui, detail) => {\n        toggleList(editor, 'OL', detail);\n      });\n      editor.addCommand('InsertDefinitionList', (ui, detail) => {\n        toggleList(editor, 'DL', detail);\n      });\n      editor.addCommand('RemoveList', () => {\n        flattenListSelection(editor);\n      });\n      registerDialog(editor);\n      editor.addCommand('mceListUpdate', (ui, detail) => {\n        if (isObject(detail)) {\n          updateList(editor, detail);\n        }\n      });\n      editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState(editor, 'UL'));\n      editor.addQueryStateHandler('InsertOrderedList', queryListCommandState(editor, 'OL'));\n      editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState(editor, 'DL'));\n    };\n\n    const setupTabKey = editor => {\n      editor.on('keydown', e => {\n        if (e.keyCode !== global$3.TAB || global$3.metaKeyPressed(e)) {\n          return;\n        }\n        editor.undoManager.transact(() => {\n          if (e.shiftKey ? outdentListSelection(editor) : indentListSelection(editor)) {\n            e.preventDefault();\n          }\n        });\n      });\n    };\n    const setup = editor => {\n      if (shouldIndentOnTab(editor)) {\n        setupTabKey(editor);\n      }\n      setup$1(editor);\n    };\n\n    const setupToggleButtonHandler = (editor, listName) => api => {\n      const toggleButtonHandler = e => {\n        api.setActive(inList(e.parents, listName));\n        api.setEnabled(!isWithinNonEditableList(editor, e.element));\n      };\n      return setNodeChangeHandler(editor, toggleButtonHandler);\n    };\n    const register$1 = editor => {\n      const exec = command => () => editor.execCommand(command);\n      if (!editor.hasPlugin('advlist')) {\n        editor.ui.registry.addToggleButton('numlist', {\n          icon: 'ordered-list',\n          active: false,\n          tooltip: 'Numbered list',\n          onAction: exec('InsertOrderedList'),\n          onSetup: setupToggleButtonHandler(editor, 'OL')\n        });\n        editor.ui.registry.addToggleButton('bullist', {\n          icon: 'unordered-list',\n          active: false,\n          tooltip: 'Bullet list',\n          onAction: exec('InsertUnorderedList'),\n          onSetup: setupToggleButtonHandler(editor, 'UL')\n        });\n      }\n    };\n\n    const setupMenuButtonHandler = (editor, listName) => api => {\n      const menuButtonHandler = e => api.setEnabled(inList(e.parents, listName) && !isWithinNonEditableList(editor, e.element));\n      return setNodeChangeHandler(editor, menuButtonHandler);\n    };\n    const register = editor => {\n      const listProperties = {\n        text: 'List properties...',\n        icon: 'ordered-list',\n        onAction: () => editor.execCommand('mceListProps'),\n        onSetup: setupMenuButtonHandler(editor, 'OL')\n      };\n      editor.ui.registry.addMenuItem('listprops', listProperties);\n      editor.ui.registry.addContextMenu('lists', {\n        update: node => {\n          const parentList = getParentList(editor, node);\n          return isOlNode(parentList) ? ['listprops'] : [];\n        }\n      });\n    };\n\n    var Plugin = () => {\n      global$6.add('lists', editor => {\n        register$3(editor);\n        if (!editor.hasPlugin('rtc', true)) {\n          setup(editor);\n          register$2(editor);\n        } else {\n          registerDialog(editor);\n        }\n        register$1(editor);\n        register(editor);\n        return get(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/media/index.js",
    "content": "// Exports the \"media\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/media')\n//   ES2015:\n//     import 'tinymce/plugins/media'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/media/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$6 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isString = isType('string');\n    const isObject = isType('object');\n    const isArray = isType('array');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativePush = Array.prototype.push;\n    const each$1 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const keys = Object.keys;\n    const hasOwnProperty = Object.hasOwnProperty;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const get$1 = (obj, key) => {\n      return has(obj, key) ? Optional.from(obj[key]) : Optional.none();\n    };\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('audio_template_callback', { processor: 'function' });\n      registerOption('video_template_callback', { processor: 'function' });\n      registerOption('iframe_template_callback', { processor: 'function' });\n      registerOption('media_live_embeds', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('media_filter_html', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('media_url_resolver', { processor: 'function' });\n      registerOption('media_alt_source', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('media_poster', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('media_dimensions', {\n        processor: 'boolean',\n        default: true\n      });\n    };\n    const getAudioTemplateCallback = option('audio_template_callback');\n    const getVideoTemplateCallback = option('video_template_callback');\n    const getIframeTemplateCallback = option('iframe_template_callback');\n    const hasLiveEmbeds = option('media_live_embeds');\n    const shouldFilterHtml = option('media_filter_html');\n    const getUrlResolver = option('media_url_resolver');\n    const hasAltSource = option('media_alt_source');\n    const hasPoster = option('media_poster');\n    const hasDimensions = option('media_dimensions');\n\n    var global$5 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    var global$4 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.html.DomParser');\n\n    const DOM$1 = global$4.DOM;\n    const trimPx = value => value.replace(/px$/, '');\n    const getEphoxEmbedData = node => {\n      const style = node.attr('style');\n      const styles = style ? DOM$1.parseStyle(style) : {};\n      return {\n        type: 'ephox-embed-iri',\n        source: node.attr('data-ephox-embed-iri'),\n        altsource: '',\n        poster: '',\n        width: get$1(styles, 'max-width').map(trimPx).getOr(''),\n        height: get$1(styles, 'max-height').map(trimPx).getOr('')\n      };\n    };\n    const htmlToData = (html, schema) => {\n      let data = {};\n      const parser = global$3({\n        validate: false,\n        forced_root_block: false\n      }, schema);\n      const rootNode = parser.parse(html);\n      for (let node = rootNode; node; node = node.walk()) {\n        if (node.type === 1) {\n          const name = node.name;\n          if (node.attr('data-ephox-embed-iri')) {\n            data = getEphoxEmbedData(node);\n            break;\n          } else {\n            if (!data.source && name === 'param') {\n              data.source = node.attr('movie');\n            }\n            if (name === 'iframe' || name === 'object' || name === 'embed' || name === 'video' || name === 'audio') {\n              if (!data.type) {\n                data.type = name;\n              }\n              data = global$5.extend(node.attributes.map, data);\n            }\n            if (name === 'script') {\n              data = {\n                type: 'script',\n                source: node.attr('src')\n              };\n            }\n            if (name === 'source') {\n              if (!data.source) {\n                data.source = node.attr('src');\n              } else if (!data.altsource) {\n                data.altsource = node.attr('src');\n              }\n            }\n            if (name === 'img' && !data.poster) {\n              data.poster = node.attr('src');\n            }\n          }\n        }\n      }\n      data.source = data.source || data.src || '';\n      data.altsource = data.altsource || '';\n      data.poster = data.poster || '';\n      return data;\n    };\n\n    const guess = url => {\n      var _a;\n      const mimes = {\n        mp3: 'audio/mpeg',\n        m4a: 'audio/x-m4a',\n        wav: 'audio/wav',\n        mp4: 'video/mp4',\n        webm: 'video/webm',\n        ogg: 'video/ogg',\n        swf: 'application/x-shockwave-flash'\n      };\n      const fileEnd = (_a = url.toLowerCase().split('.').pop()) !== null && _a !== void 0 ? _a : '';\n      return get$1(mimes, fileEnd).getOr('');\n    };\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.html.Node');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.html.Serializer');\n\n    const Parser = (schema, settings = {}) => global$3({\n      forced_root_block: false,\n      validate: false,\n      allow_conditional_comments: true,\n      ...settings\n    }, schema);\n\n    const DOM = global$4.DOM;\n    const addPx = value => /^[0-9.]+$/.test(value) ? value + 'px' : value;\n    const updateEphoxEmbed = (data, node) => {\n      const style = node.attr('style');\n      const styleMap = style ? DOM.parseStyle(style) : {};\n      if (isNonNullable(data.width)) {\n        styleMap['max-width'] = addPx(data.width);\n      }\n      if (isNonNullable(data.height)) {\n        styleMap['max-height'] = addPx(data.height);\n      }\n      node.attr('style', DOM.serializeStyle(styleMap));\n    };\n    const sources = [\n      'source',\n      'altsource'\n    ];\n    const updateHtml = (html, data, updateAll, schema) => {\n      let numSources = 0;\n      let sourceCount = 0;\n      const parser = Parser(schema);\n      parser.addNodeFilter('source', nodes => numSources = nodes.length);\n      const rootNode = parser.parse(html);\n      for (let node = rootNode; node; node = node.walk()) {\n        if (node.type === 1) {\n          const name = node.name;\n          if (node.attr('data-ephox-embed-iri')) {\n            updateEphoxEmbed(data, node);\n            break;\n          } else {\n            switch (name) {\n            case 'video':\n            case 'object':\n            case 'embed':\n            case 'img':\n            case 'iframe':\n              if (data.height !== undefined && data.width !== undefined) {\n                node.attr('width', data.width);\n                node.attr('height', data.height);\n              }\n              break;\n            }\n            if (updateAll) {\n              switch (name) {\n              case 'video':\n                node.attr('poster', data.poster);\n                node.attr('src', null);\n                for (let index = numSources; index < 2; index++) {\n                  if (data[sources[index]]) {\n                    const source = new global$2('source', 1);\n                    source.attr('src', data[sources[index]]);\n                    source.attr('type', data[sources[index] + 'mime'] || null);\n                    node.append(source);\n                  }\n                }\n                break;\n              case 'iframe':\n                node.attr('src', data.source);\n                break;\n              case 'object':\n                const hasImage = node.getAll('img').length > 0;\n                if (data.poster && !hasImage) {\n                  node.attr('src', data.poster);\n                  const img = new global$2('img', 1);\n                  img.attr('src', data.poster);\n                  img.attr('width', data.width);\n                  img.attr('height', data.height);\n                  node.append(img);\n                }\n                break;\n              case 'source':\n                if (sourceCount < 2) {\n                  node.attr('src', data[sources[sourceCount]]);\n                  node.attr('type', data[sources[sourceCount] + 'mime'] || null);\n                  if (!data[sources[sourceCount]]) {\n                    node.remove();\n                    continue;\n                  }\n                }\n                sourceCount++;\n                break;\n              case 'img':\n                if (!data.poster) {\n                  node.remove();\n                }\n                break;\n              }\n            }\n          }\n        }\n      }\n      return global$1({}, schema).serialize(rootNode);\n    };\n\n    const urlPatterns = [\n      {\n        regex: /youtu\\.be\\/([\\w\\-_\\?&=.]+)/i,\n        type: 'iframe',\n        w: 560,\n        h: 314,\n        url: 'www.youtube.com/embed/$1',\n        allowFullscreen: true\n      },\n      {\n        regex: /youtube\\.com(.+)v=([^&]+)(&([a-z0-9&=\\-_]+))?/i,\n        type: 'iframe',\n        w: 560,\n        h: 314,\n        url: 'www.youtube.com/embed/$2?$4',\n        allowFullscreen: true\n      },\n      {\n        regex: /youtube.com\\/embed\\/([a-z0-9\\?&=\\-_]+)/i,\n        type: 'iframe',\n        w: 560,\n        h: 314,\n        url: 'www.youtube.com/embed/$1',\n        allowFullscreen: true\n      },\n      {\n        regex: /vimeo\\.com\\/([0-9]+)/,\n        type: 'iframe',\n        w: 425,\n        h: 350,\n        url: 'player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc',\n        allowFullscreen: true\n      },\n      {\n        regex: /vimeo\\.com\\/(.*)\\/([0-9]+)/,\n        type: 'iframe',\n        w: 425,\n        h: 350,\n        url: 'player.vimeo.com/video/$2?title=0&amp;byline=0',\n        allowFullscreen: true\n      },\n      {\n        regex: /maps\\.google\\.([a-z]{2,3})\\/maps\\/(.+)msid=(.+)/,\n        type: 'iframe',\n        w: 425,\n        h: 350,\n        url: 'maps.google.com/maps/ms?msid=$2&output=embed\"',\n        allowFullscreen: false\n      },\n      {\n        regex: /dailymotion\\.com\\/video\\/([^_]+)/,\n        type: 'iframe',\n        w: 480,\n        h: 270,\n        url: 'www.dailymotion.com/embed/video/$1',\n        allowFullscreen: true\n      },\n      {\n        regex: /dai\\.ly\\/([^_]+)/,\n        type: 'iframe',\n        w: 480,\n        h: 270,\n        url: 'www.dailymotion.com/embed/video/$1',\n        allowFullscreen: true\n      }\n    ];\n    const getProtocol = url => {\n      const protocolMatches = url.match(/^(https?:\\/\\/|www\\.)(.+)$/i);\n      if (protocolMatches && protocolMatches.length > 1) {\n        return protocolMatches[1] === 'www.' ? 'https://' : protocolMatches[1];\n      } else {\n        return 'https://';\n      }\n    };\n    const getUrl = (pattern, url) => {\n      const protocol = getProtocol(url);\n      const match = pattern.regex.exec(url);\n      let newUrl = protocol + pattern.url;\n      if (isNonNullable(match)) {\n        for (let i = 0; i < match.length; i++) {\n          newUrl = newUrl.replace('$' + i, () => match[i] ? match[i] : '');\n        }\n      }\n      return newUrl.replace(/\\?$/, '');\n    };\n    const matchPattern = url => {\n      const patterns = urlPatterns.filter(pattern => pattern.regex.test(url));\n      if (patterns.length > 0) {\n        return global$5.extend({}, patterns[0], { url: getUrl(patterns[0], url) });\n      } else {\n        return null;\n      }\n    };\n\n    const getIframeHtml = (data, iframeTemplateCallback) => {\n      if (iframeTemplateCallback) {\n        return iframeTemplateCallback(data);\n      } else {\n        const allowFullscreen = data.allowfullscreen ? ' allowFullscreen=\"1\"' : '';\n        return '<iframe src=\"' + data.source + '\" width=\"' + data.width + '\" height=\"' + data.height + '\"' + allowFullscreen + '></iframe>';\n      }\n    };\n    const getFlashHtml = data => {\n      let html = '<object data=\"' + data.source + '\" width=\"' + data.width + '\" height=\"' + data.height + '\" type=\"application/x-shockwave-flash\">';\n      if (data.poster) {\n        html += '<img src=\"' + data.poster + '\" width=\"' + data.width + '\" height=\"' + data.height + '\" />';\n      }\n      html += '</object>';\n      return html;\n    };\n    const getAudioHtml = (data, audioTemplateCallback) => {\n      if (audioTemplateCallback) {\n        return audioTemplateCallback(data);\n      } else {\n        return '<audio controls=\"controls\" src=\"' + data.source + '\">' + (data.altsource ? '\\n<source src=\"' + data.altsource + '\"' + (data.altsourcemime ? ' type=\"' + data.altsourcemime + '\"' : '') + ' />\\n' : '') + '</audio>';\n      }\n    };\n    const getVideoHtml = (data, videoTemplateCallback) => {\n      if (videoTemplateCallback) {\n        return videoTemplateCallback(data);\n      } else {\n        return '<video width=\"' + data.width + '\" height=\"' + data.height + '\"' + (data.poster ? ' poster=\"' + data.poster + '\"' : '') + ' controls=\"controls\">\\n' + '<source src=\"' + data.source + '\"' + (data.sourcemime ? ' type=\"' + data.sourcemime + '\"' : '') + ' />\\n' + (data.altsource ? '<source src=\"' + data.altsource + '\"' + (data.altsourcemime ? ' type=\"' + data.altsourcemime + '\"' : '') + ' />\\n' : '') + '</video>';\n      }\n    };\n    const getScriptHtml = data => {\n      return '<script src=\"' + data.source + '\"></script>';\n    };\n    const dataToHtml = (editor, dataIn) => {\n      var _a;\n      const data = global$5.extend({}, dataIn);\n      if (!data.source) {\n        global$5.extend(data, htmlToData((_a = data.embed) !== null && _a !== void 0 ? _a : '', editor.schema));\n        if (!data.source) {\n          return '';\n        }\n      }\n      if (!data.altsource) {\n        data.altsource = '';\n      }\n      if (!data.poster) {\n        data.poster = '';\n      }\n      data.source = editor.convertURL(data.source, 'source');\n      data.altsource = editor.convertURL(data.altsource, 'source');\n      data.sourcemime = guess(data.source);\n      data.altsourcemime = guess(data.altsource);\n      data.poster = editor.convertURL(data.poster, 'poster');\n      const pattern = matchPattern(data.source);\n      if (pattern) {\n        data.source = pattern.url;\n        data.type = pattern.type;\n        data.allowfullscreen = pattern.allowFullscreen;\n        data.width = data.width || String(pattern.w);\n        data.height = data.height || String(pattern.h);\n      }\n      if (data.embed) {\n        return updateHtml(data.embed, data, true, editor.schema);\n      } else {\n        const audioTemplateCallback = getAudioTemplateCallback(editor);\n        const videoTemplateCallback = getVideoTemplateCallback(editor);\n        const iframeTemplateCallback = getIframeTemplateCallback(editor);\n        data.width = data.width || '300';\n        data.height = data.height || '150';\n        global$5.each(data, (value, key) => {\n          data[key] = editor.dom.encode('' + value);\n        });\n        if (data.type === 'iframe') {\n          return getIframeHtml(data, iframeTemplateCallback);\n        } else if (data.sourcemime === 'application/x-shockwave-flash') {\n          return getFlashHtml(data);\n        } else if (data.sourcemime.indexOf('audio') !== -1) {\n          return getAudioHtml(data, audioTemplateCallback);\n        } else if (data.type === 'script') {\n          return getScriptHtml(data);\n        } else {\n          return getVideoHtml(data, videoTemplateCallback);\n        }\n      }\n    };\n\n    const isMediaElement = element => element.hasAttribute('data-mce-object') || element.hasAttribute('data-ephox-embed-iri');\n    const setup$2 = editor => {\n      editor.on('click keyup touchend', () => {\n        const selectedNode = editor.selection.getNode();\n        if (selectedNode && editor.dom.hasClass(selectedNode, 'mce-preview-object')) {\n          if (editor.dom.getAttrib(selectedNode, 'data-mce-selected')) {\n            selectedNode.setAttribute('data-mce-selected', '2');\n          }\n        }\n      });\n      editor.on('ObjectSelected', e => {\n        const objectType = e.target.getAttribute('data-mce-object');\n        if (objectType === 'script') {\n          e.preventDefault();\n        }\n      });\n      editor.on('ObjectResized', e => {\n        const target = e.target;\n        if (target.getAttribute('data-mce-object')) {\n          let html = target.getAttribute('data-mce-html');\n          if (html) {\n            html = unescape(html);\n            target.setAttribute('data-mce-html', escape(updateHtml(html, {\n              width: String(e.width),\n              height: String(e.height)\n            }, false, editor.schema)));\n          }\n        }\n      });\n    };\n\n    const cache = {};\n    const embedPromise = (data, dataToHtml, handler) => {\n      return new Promise((res, rej) => {\n        const wrappedResolve = response => {\n          if (response.html) {\n            cache[data.source] = response;\n          }\n          return res({\n            url: data.source,\n            html: response.html ? response.html : dataToHtml(data)\n          });\n        };\n        if (cache[data.source]) {\n          wrappedResolve(cache[data.source]);\n        } else {\n          handler({ url: data.source }, wrappedResolve, rej);\n        }\n      });\n    };\n    const defaultPromise = (data, dataToHtml) => Promise.resolve({\n      html: dataToHtml(data),\n      url: data.source\n    });\n    const loadedData = editor => data => dataToHtml(editor, data);\n    const getEmbedHtml = (editor, data) => {\n      const embedHandler = getUrlResolver(editor);\n      return embedHandler ? embedPromise(data, loadedData(editor), embedHandler) : defaultPromise(data, loadedData(editor));\n    };\n    const isCached = url => has(cache, url);\n\n    const extractMeta = (sourceInput, data) => get$1(data, sourceInput).bind(mainData => get$1(mainData, 'meta'));\n    const getValue = (data, metaData, sourceInput) => prop => {\n      const getFromData = () => get$1(data, prop);\n      const getFromMetaData = () => get$1(metaData, prop);\n      const getNonEmptyValue = c => get$1(c, 'value').bind(v => v.length > 0 ? Optional.some(v) : Optional.none());\n      const getFromValueFirst = () => getFromData().bind(child => isObject(child) ? getNonEmptyValue(child).orThunk(getFromMetaData) : getFromMetaData().orThunk(() => Optional.from(child)));\n      const getFromMetaFirst = () => getFromMetaData().orThunk(() => getFromData().bind(child => isObject(child) ? getNonEmptyValue(child) : Optional.from(child)));\n      return { [prop]: (prop === sourceInput ? getFromValueFirst() : getFromMetaFirst()).getOr('') };\n    };\n    const getDimensions = (data, metaData) => {\n      const dimensions = {};\n      get$1(data, 'dimensions').each(dims => {\n        each$1([\n          'width',\n          'height'\n        ], prop => {\n          get$1(metaData, prop).orThunk(() => get$1(dims, prop)).each(value => dimensions[prop] = value);\n        });\n      });\n      return dimensions;\n    };\n    const unwrap = (data, sourceInput) => {\n      const metaData = sourceInput && sourceInput !== 'dimensions' ? extractMeta(sourceInput, data).getOr({}) : {};\n      const get = getValue(data, metaData, sourceInput);\n      return {\n        ...get('source'),\n        ...get('altsource'),\n        ...get('poster'),\n        ...get('embed'),\n        ...getDimensions(data, metaData)\n      };\n    };\n    const wrap = data => {\n      const wrapped = {\n        ...data,\n        source: { value: get$1(data, 'source').getOr('') },\n        altsource: { value: get$1(data, 'altsource').getOr('') },\n        poster: { value: get$1(data, 'poster').getOr('') }\n      };\n      each$1([\n        'width',\n        'height'\n      ], prop => {\n        get$1(data, prop).each(value => {\n          const dimensions = wrapped.dimensions || {};\n          dimensions[prop] = value;\n          wrapped.dimensions = dimensions;\n        });\n      });\n      return wrapped;\n    };\n    const handleError = editor => error => {\n      const errorMessage = error && error.msg ? 'Media embed handler error: ' + error.msg : 'Media embed handler threw unknown error.';\n      editor.notificationManager.open({\n        type: 'error',\n        text: errorMessage\n      });\n    };\n    const getEditorData = editor => {\n      const element = editor.selection.getNode();\n      const snippet = isMediaElement(element) ? editor.serializer.serialize(element, { selection: true }) : '';\n      return {\n        embed: snippet,\n        ...htmlToData(snippet, editor.schema)\n      };\n    };\n    const addEmbedHtml = (api, editor) => response => {\n      if (isString(response.url) && response.url.trim().length > 0) {\n        const html = response.html;\n        const snippetData = htmlToData(html, editor.schema);\n        const nuData = {\n          ...snippetData,\n          source: response.url,\n          embed: html\n        };\n        api.setData(wrap(nuData));\n      }\n    };\n    const selectPlaceholder = (editor, beforeObjects) => {\n      const afterObjects = editor.dom.select('*[data-mce-object]');\n      for (let i = 0; i < beforeObjects.length; i++) {\n        for (let y = afterObjects.length - 1; y >= 0; y--) {\n          if (beforeObjects[i] === afterObjects[y]) {\n            afterObjects.splice(y, 1);\n          }\n        }\n      }\n      editor.selection.select(afterObjects[0]);\n    };\n    const handleInsert = (editor, html) => {\n      const beforeObjects = editor.dom.select('*[data-mce-object]');\n      editor.insertContent(html);\n      selectPlaceholder(editor, beforeObjects);\n      editor.nodeChanged();\n    };\n    const submitForm = (prevData, newData, editor) => {\n      var _a;\n      newData.embed = updateHtml((_a = newData.embed) !== null && _a !== void 0 ? _a : '', newData, false, editor.schema);\n      if (newData.embed && (prevData.source === newData.source || isCached(newData.source))) {\n        handleInsert(editor, newData.embed);\n      } else {\n        getEmbedHtml(editor, newData).then(response => {\n          handleInsert(editor, response.html);\n        }).catch(handleError(editor));\n      }\n    };\n    const showDialog = editor => {\n      const editorData = getEditorData(editor);\n      const currentData = Cell(editorData);\n      const initialData = wrap(editorData);\n      const handleSource = (prevData, api) => {\n        const serviceData = unwrap(api.getData(), 'source');\n        if (prevData.source !== serviceData.source) {\n          addEmbedHtml(win, editor)({\n            url: serviceData.source,\n            html: ''\n          });\n          getEmbedHtml(editor, serviceData).then(addEmbedHtml(win, editor)).catch(handleError(editor));\n        }\n      };\n      const handleEmbed = api => {\n        var _a;\n        const data = unwrap(api.getData());\n        const dataFromEmbed = htmlToData((_a = data.embed) !== null && _a !== void 0 ? _a : '', editor.schema);\n        api.setData(wrap(dataFromEmbed));\n      };\n      const handleUpdate = (api, sourceInput) => {\n        const data = unwrap(api.getData(), sourceInput);\n        const embed = dataToHtml(editor, data);\n        api.setData(wrap({\n          ...data,\n          embed\n        }));\n      };\n      const mediaInput = [{\n          name: 'source',\n          type: 'urlinput',\n          filetype: 'media',\n          label: 'Source'\n        }];\n      const sizeInput = !hasDimensions(editor) ? [] : [{\n          type: 'sizeinput',\n          name: 'dimensions',\n          label: 'Constrain proportions',\n          constrain: true\n        }];\n      const generalTab = {\n        title: 'General',\n        name: 'general',\n        items: flatten([\n          mediaInput,\n          sizeInput\n        ])\n      };\n      const embedTextarea = {\n        type: 'textarea',\n        name: 'embed',\n        label: 'Paste your embed code below:'\n      };\n      const embedTab = {\n        title: 'Embed',\n        items: [embedTextarea]\n      };\n      const advancedFormItems = [];\n      if (hasAltSource(editor)) {\n        advancedFormItems.push({\n          name: 'altsource',\n          type: 'urlinput',\n          filetype: 'media',\n          label: 'Alternative source URL'\n        });\n      }\n      if (hasPoster(editor)) {\n        advancedFormItems.push({\n          name: 'poster',\n          type: 'urlinput',\n          filetype: 'image',\n          label: 'Media poster (Image URL)'\n        });\n      }\n      const advancedTab = {\n        title: 'Advanced',\n        name: 'advanced',\n        items: advancedFormItems\n      };\n      const tabs = [\n        generalTab,\n        embedTab\n      ];\n      if (advancedFormItems.length > 0) {\n        tabs.push(advancedTab);\n      }\n      const body = {\n        type: 'tabpanel',\n        tabs\n      };\n      const win = editor.windowManager.open({\n        title: 'Insert/Edit Media',\n        size: 'normal',\n        body,\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        onSubmit: api => {\n          const serviceData = unwrap(api.getData());\n          submitForm(currentData.get(), serviceData, editor);\n          api.close();\n        },\n        onChange: (api, detail) => {\n          switch (detail.name) {\n          case 'source':\n            handleSource(currentData.get(), api);\n            break;\n          case 'embed':\n            handleEmbed(api);\n            break;\n          case 'dimensions':\n          case 'altsource':\n          case 'poster':\n            handleUpdate(api, detail.name);\n            break;\n          }\n          currentData.set(unwrap(api.getData()));\n        },\n        initialData\n      });\n    };\n\n    const get = editor => {\n      const showDialog$1 = () => {\n        showDialog(editor);\n      };\n      return { showDialog: showDialog$1 };\n    };\n\n    const register$1 = editor => {\n      const showDialog$1 = () => {\n        showDialog(editor);\n      };\n      editor.addCommand('mceMedia', showDialog$1);\n    };\n\n    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;\n    const startsWith = (str, prefix) => {\n      return checkRange(str, prefix, 0);\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.Env');\n\n    const isLiveEmbedNode = node => {\n      const name = node.name;\n      return name === 'iframe' || name === 'video' || name === 'audio';\n    };\n    const getDimension = (node, styles, dimension, defaultValue = null) => {\n      const value = node.attr(dimension);\n      if (isNonNullable(value)) {\n        return value;\n      } else if (!has(styles, dimension)) {\n        return defaultValue;\n      } else {\n        return null;\n      }\n    };\n    const setDimensions = (node, previewNode, styles) => {\n      const useDefaults = previewNode.name === 'img' || node.name === 'video';\n      const defaultWidth = useDefaults ? '300' : null;\n      const fallbackHeight = node.name === 'audio' ? '30' : '150';\n      const defaultHeight = useDefaults ? fallbackHeight : null;\n      previewNode.attr({\n        width: getDimension(node, styles, 'width', defaultWidth),\n        height: getDimension(node, styles, 'height', defaultHeight)\n      });\n    };\n    const appendNodeContent = (editor, nodeName, previewNode, html) => {\n      const newNode = Parser(editor.schema).parse(html, { context: nodeName });\n      while (newNode.firstChild) {\n        previewNode.append(newNode.firstChild);\n      }\n    };\n    const createPlaceholderNode = (editor, node) => {\n      const name = node.name;\n      const placeHolder = new global$2('img', 1);\n      retainAttributesAndInnerHtml(editor, node, placeHolder);\n      setDimensions(node, placeHolder, {});\n      placeHolder.attr({\n        'style': node.attr('style'),\n        'src': global.transparentSrc,\n        'data-mce-object': name,\n        'class': 'mce-object mce-object-' + name\n      });\n      return placeHolder;\n    };\n    const createPreviewNode = (editor, node) => {\n      var _a;\n      const name = node.name;\n      const previewWrapper = new global$2('span', 1);\n      previewWrapper.attr({\n        'contentEditable': 'false',\n        'style': node.attr('style'),\n        'data-mce-object': name,\n        'class': 'mce-preview-object mce-object-' + name\n      });\n      retainAttributesAndInnerHtml(editor, node, previewWrapper);\n      const styles = editor.dom.parseStyle((_a = node.attr('style')) !== null && _a !== void 0 ? _a : '');\n      const previewNode = new global$2(name, 1);\n      setDimensions(node, previewNode, styles);\n      previewNode.attr({\n        src: node.attr('src'),\n        style: node.attr('style'),\n        class: node.attr('class')\n      });\n      if (name === 'iframe') {\n        previewNode.attr({\n          allowfullscreen: node.attr('allowfullscreen'),\n          frameborder: '0'\n        });\n      } else {\n        const attrs = [\n          'controls',\n          'crossorigin',\n          'currentTime',\n          'loop',\n          'muted',\n          'poster',\n          'preload'\n        ];\n        each$1(attrs, attrName => {\n          previewNode.attr(attrName, node.attr(attrName));\n        });\n        const sanitizedHtml = previewWrapper.attr('data-mce-html');\n        if (isNonNullable(sanitizedHtml)) {\n          appendNodeContent(editor, name, previewNode, unescape(sanitizedHtml));\n        }\n      }\n      const shimNode = new global$2('span', 1);\n      shimNode.attr('class', 'mce-shim');\n      previewWrapper.append(previewNode);\n      previewWrapper.append(shimNode);\n      return previewWrapper;\n    };\n    const retainAttributesAndInnerHtml = (editor, sourceNode, targetNode) => {\n      var _a;\n      const attribs = (_a = sourceNode.attributes) !== null && _a !== void 0 ? _a : [];\n      let ai = attribs.length;\n      while (ai--) {\n        const attrName = attribs[ai].name;\n        let attrValue = attribs[ai].value;\n        if (attrName !== 'width' && attrName !== 'height' && attrName !== 'style' && !startsWith(attrName, 'data-mce-')) {\n          if (attrName === 'data' || attrName === 'src') {\n            attrValue = editor.convertURL(attrValue, attrName);\n          }\n          targetNode.attr('data-mce-p-' + attrName, attrValue);\n        }\n      }\n      const serializer = global$1({ inner: true }, editor.schema);\n      const tempNode = new global$2('div', 1);\n      each$1(sourceNode.children(), child => tempNode.append(child));\n      const innerHtml = serializer.serialize(tempNode);\n      if (innerHtml) {\n        targetNode.attr('data-mce-html', escape(innerHtml));\n        targetNode.empty();\n      }\n    };\n    const isPageEmbedWrapper = node => {\n      const nodeClass = node.attr('class');\n      return isString(nodeClass) && /\\btiny-pageembed\\b/.test(nodeClass);\n    };\n    const isWithinEmbedWrapper = node => {\n      let tempNode = node;\n      while (tempNode = tempNode.parent) {\n        if (tempNode.attr('data-ephox-embed-iri') || isPageEmbedWrapper(tempNode)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const placeHolderConverter = editor => nodes => {\n      let i = nodes.length;\n      let node;\n      while (i--) {\n        node = nodes[i];\n        if (!node.parent) {\n          continue;\n        }\n        if (node.parent.attr('data-mce-object')) {\n          continue;\n        }\n        if (isLiveEmbedNode(node) && hasLiveEmbeds(editor)) {\n          if (!isWithinEmbedWrapper(node)) {\n            node.replace(createPreviewNode(editor, node));\n          }\n        } else {\n          if (!isWithinEmbedWrapper(node)) {\n            node.replace(createPlaceholderNode(editor, node));\n          }\n        }\n      }\n    };\n\n    const parseAndSanitize = (editor, context, html) => {\n      const validate = shouldFilterHtml(editor);\n      return Parser(editor.schema, { validate }).parse(html, { context });\n    };\n\n    const setup$1 = editor => {\n      editor.on('PreInit', () => {\n        const {schema, serializer, parser} = editor;\n        const boolAttrs = schema.getBoolAttrs();\n        each$1('webkitallowfullscreen mozallowfullscreen'.split(' '), name => {\n          boolAttrs[name] = {};\n        });\n        each({ embed: ['wmode'] }, (attrs, name) => {\n          const rule = schema.getElementRule(name);\n          if (rule) {\n            each$1(attrs, attr => {\n              rule.attributes[attr] = {};\n              rule.attributesOrder.push(attr);\n            });\n          }\n        });\n        parser.addNodeFilter('iframe,video,audio,object,embed,script', placeHolderConverter(editor));\n        serializer.addAttributeFilter('data-mce-object', (nodes, name) => {\n          var _a;\n          let i = nodes.length;\n          while (i--) {\n            const node = nodes[i];\n            if (!node.parent) {\n              continue;\n            }\n            const realElmName = node.attr(name);\n            const realElm = new global$2(realElmName, 1);\n            if (realElmName !== 'audio' && realElmName !== 'script') {\n              const className = node.attr('class');\n              if (className && className.indexOf('mce-preview-object') !== -1 && node.firstChild) {\n                realElm.attr({\n                  width: node.firstChild.attr('width'),\n                  height: node.firstChild.attr('height')\n                });\n              } else {\n                realElm.attr({\n                  width: node.attr('width'),\n                  height: node.attr('height')\n                });\n              }\n            }\n            realElm.attr({ style: node.attr('style') });\n            const attribs = (_a = node.attributes) !== null && _a !== void 0 ? _a : [];\n            let ai = attribs.length;\n            while (ai--) {\n              const attrName = attribs[ai].name;\n              if (attrName.indexOf('data-mce-p-') === 0) {\n                realElm.attr(attrName.substr(11), attribs[ai].value);\n              }\n            }\n            if (realElmName === 'script') {\n              realElm.attr('type', 'text/javascript');\n            }\n            const innerHtml = node.attr('data-mce-html');\n            if (innerHtml) {\n              const fragment = parseAndSanitize(editor, realElmName, unescape(innerHtml));\n              each$1(fragment.children(), child => realElm.append(child));\n            }\n            node.replace(realElm);\n          }\n        });\n      });\n      editor.on('SetContent', () => {\n        const dom = editor.dom;\n        each$1(dom.select('span.mce-preview-object'), elm => {\n          if (dom.select('span.mce-shim', elm).length === 0) {\n            dom.add(elm, 'span', { class: 'mce-shim' });\n          }\n        });\n      });\n    };\n\n    const setup = editor => {\n      editor.on('ResolveName', e => {\n        let name;\n        if (e.target.nodeType === 1 && (name = e.target.getAttribute('data-mce-object'))) {\n          e.name = name;\n        }\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceMedia');\n      editor.ui.registry.addToggleButton('media', {\n        tooltip: 'Insert/edit media',\n        icon: 'embed',\n        onAction,\n        onSetup: buttonApi => {\n          const selection = editor.selection;\n          buttonApi.setActive(isMediaElement(selection.getNode()));\n          return selection.selectorChangedWithUnbind('img[data-mce-object],span[data-mce-object],div[data-ephox-embed-iri]', buttonApi.setActive).unbind;\n        }\n      });\n      editor.ui.registry.addMenuItem('media', {\n        icon: 'embed',\n        text: 'Media...',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$6.add('media', editor => {\n        register$2(editor);\n        register$1(editor);\n        register(editor);\n        setup(editor);\n        setup$1(editor);\n        setup$2(editor);\n        return get(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/nonbreaking/index.js",
    "content": "// Exports the \"nonbreaking\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/nonbreaking')\n//   ES2015:\n//     import 'tinymce/plugins/nonbreaking'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/nonbreaking/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const isSimpleType = type => value => typeof value === type;\n    const isBoolean = isSimpleType('boolean');\n    const isNumber = isSimpleType('number');\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('nonbreaking_force_tab', {\n        processor: value => {\n          if (isBoolean(value)) {\n            return {\n              value: value ? 3 : 0,\n              valid: true\n            };\n          } else if (isNumber(value)) {\n            return {\n              value,\n              valid: true\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be a boolean or number.'\n            };\n          }\n        },\n        default: false\n      });\n      registerOption('nonbreaking_wrap', {\n        processor: 'boolean',\n        default: true\n      });\n    };\n    const getKeyboardSpaces = option('nonbreaking_force_tab');\n    const wrapNbsps = option('nonbreaking_wrap');\n\n    const stringRepeat = (string, repeats) => {\n      let str = '';\n      for (let index = 0; index < repeats; index++) {\n        str += string;\n      }\n      return str;\n    };\n    const isVisualCharsEnabled = editor => editor.plugins.visualchars ? editor.plugins.visualchars.isEnabled() : false;\n    const insertNbsp = (editor, times) => {\n      const classes = () => isVisualCharsEnabled(editor) ? 'mce-nbsp-wrap mce-nbsp' : 'mce-nbsp-wrap';\n      const nbspSpan = () => `<span class=\"${ classes() }\" contenteditable=\"false\">${ stringRepeat('&nbsp;', times) }</span>`;\n      const shouldWrap = wrapNbsps(editor);\n      const html = shouldWrap || editor.plugins.visualchars ? nbspSpan() : stringRepeat('&nbsp;', times);\n      editor.undoManager.transact(() => editor.insertContent(html));\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mceNonBreaking', () => {\n        insertNbsp(editor, 1);\n      });\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.VK');\n\n    const setup = editor => {\n      const spaces = getKeyboardSpaces(editor);\n      if (spaces > 0) {\n        editor.on('keydown', e => {\n          if (e.keyCode === global.TAB && !e.isDefaultPrevented()) {\n            if (e.shiftKey) {\n              return;\n            }\n            e.preventDefault();\n            e.stopImmediatePropagation();\n            insertNbsp(editor, spaces);\n          }\n        });\n      }\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceNonBreaking');\n      editor.ui.registry.addButton('nonbreaking', {\n        icon: 'non-breaking',\n        tooltip: 'Nonbreaking space',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('nonbreaking', {\n        icon: 'non-breaking',\n        text: 'Nonbreaking space',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$1.add('nonbreaking', editor => {\n        register$2(editor);\n        register$1(editor);\n        register(editor);\n        setup(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/pagebreak/index.js",
    "content": "// Exports the \"pagebreak\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/pagebreak')\n//   ES2015:\n//     import 'tinymce/plugins/pagebreak'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/pagebreak/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    var global = tinymce.util.Tools.resolve('tinymce.Env');\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('pagebreak_separator', {\n        processor: 'string',\n        default: '<!-- pagebreak -->'\n      });\n      registerOption('pagebreak_split_block', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const getSeparatorHtml = option('pagebreak_separator');\n    const shouldSplitBlock = option('pagebreak_split_block');\n\n    const pageBreakClass = 'mce-pagebreak';\n    const getPlaceholderHtml = shouldSplitBlock => {\n      const html = `<img src=\"${ global.transparentSrc }\" class=\"${ pageBreakClass }\" data-mce-resize=\"false\" data-mce-placeholder />`;\n      return shouldSplitBlock ? `<p>${ html }</p>` : html;\n    };\n    const setup$1 = editor => {\n      const separatorHtml = getSeparatorHtml(editor);\n      const shouldSplitBlock$1 = () => shouldSplitBlock(editor);\n      const pageBreakSeparatorRegExp = new RegExp(separatorHtml.replace(/[\\?\\.\\*\\[\\]\\(\\)\\{\\}\\+\\^\\$\\:]/g, a => {\n        return '\\\\' + a;\n      }), 'gi');\n      editor.on('BeforeSetContent', e => {\n        e.content = e.content.replace(pageBreakSeparatorRegExp, getPlaceholderHtml(shouldSplitBlock$1()));\n      });\n      editor.on('PreInit', () => {\n        editor.serializer.addNodeFilter('img', nodes => {\n          let i = nodes.length, node, className;\n          while (i--) {\n            node = nodes[i];\n            className = node.attr('class');\n            if (className && className.indexOf(pageBreakClass) !== -1) {\n              const parentNode = node.parent;\n              if (parentNode && editor.schema.getBlockElements()[parentNode.name] && shouldSplitBlock$1()) {\n                parentNode.type = 3;\n                parentNode.value = separatorHtml;\n                parentNode.raw = true;\n                node.remove();\n                continue;\n              }\n              node.type = 3;\n              node.value = separatorHtml;\n              node.raw = true;\n            }\n          }\n        });\n      });\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mcePageBreak', () => {\n        editor.insertContent(getPlaceholderHtml(shouldSplitBlock(editor)));\n      });\n    };\n\n    const setup = editor => {\n      editor.on('ResolveName', e => {\n        if (e.target.nodeName === 'IMG' && editor.dom.hasClass(e.target, pageBreakClass)) {\n          e.name = 'pagebreak';\n        }\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mcePageBreak');\n      editor.ui.registry.addButton('pagebreak', {\n        icon: 'page-break',\n        tooltip: 'Page break',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('pagebreak', {\n        text: 'Page break',\n        icon: 'page-break',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$1.add('pagebreak', editor => {\n        register$2(editor);\n        register$1(editor);\n        register(editor);\n        setup$1(editor);\n        setup(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/preview/index.js",
    "content": "// Exports the \"preview\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/preview')\n//   ES2015:\n//     import 'tinymce/plugins/preview'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/preview/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.Env');\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const option = name => editor => editor.options.get(name);\n    const getContentStyle = option('content_style');\n    const shouldUseContentCssCors = option('content_css_cors');\n    const getBodyClass = option('body_class');\n    const getBodyId = option('body_id');\n\n    const getPreviewHtml = editor => {\n      var _a;\n      let headHtml = '';\n      const encode = editor.dom.encode;\n      const contentStyle = (_a = getContentStyle(editor)) !== null && _a !== void 0 ? _a : '';\n      headHtml += '<base href=\"' + encode(editor.documentBaseURI.getURI()) + '\">';\n      const cors = shouldUseContentCssCors(editor) ? ' crossorigin=\"anonymous\"' : '';\n      global.each(editor.contentCSS, url => {\n        headHtml += '<link type=\"text/css\" rel=\"stylesheet\" href=\"' + encode(editor.documentBaseURI.toAbsolute(url)) + '\"' + cors + '>';\n      });\n      if (contentStyle) {\n        headHtml += '<style type=\"text/css\">' + contentStyle + '</style>';\n      }\n      const bodyId = getBodyId(editor);\n      const bodyClass = getBodyClass(editor);\n      const isMetaKeyPressed = global$1.os.isMacOS() || global$1.os.isiOS() ? 'e.metaKey' : 'e.ctrlKey && !e.altKey';\n      const preventClicksOnLinksScript = '<script>' + 'document.addEventListener && document.addEventListener(\"click\", function(e) {' + 'for (var elm = e.target; elm; elm = elm.parentNode) {' + 'if (elm.nodeName === \"A\" && !(' + isMetaKeyPressed + ')) {' + 'e.preventDefault();' + '}' + '}' + '}, false);' + '</script> ';\n      const directionality = editor.getBody().dir;\n      const dirAttr = directionality ? ' dir=\"' + encode(directionality) + '\"' : '';\n      const previewHtml = '<!DOCTYPE html>' + '<html>' + '<head>' + headHtml + '</head>' + '<body id=\"' + encode(bodyId) + '\" class=\"mce-content-body ' + encode(bodyClass) + '\"' + dirAttr + '>' + editor.getContent() + preventClicksOnLinksScript + '</body>' + '</html>';\n      return previewHtml;\n    };\n\n    const open = editor => {\n      const content = getPreviewHtml(editor);\n      const dataApi = editor.windowManager.open({\n        title: 'Preview',\n        size: 'large',\n        body: {\n          type: 'panel',\n          items: [{\n              name: 'preview',\n              type: 'iframe',\n              sandboxed: true,\n              transparent: false\n            }]\n        },\n        buttons: [{\n            type: 'cancel',\n            name: 'close',\n            text: 'Close',\n            primary: true\n          }],\n        initialData: { preview: content }\n      });\n      dataApi.focus('close');\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mcePreview', () => {\n        open(editor);\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mcePreview');\n      editor.ui.registry.addButton('preview', {\n        icon: 'preview',\n        tooltip: 'Preview',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('preview', {\n        icon: 'preview',\n        text: 'Preview',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$2.add('preview', editor => {\n        register$1(editor);\n        register(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/quickbars/index.js",
    "content": "// Exports the \"quickbars\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/quickbars')\n//   ES2015:\n//     import 'tinymce/plugins/quickbars'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/quickbars/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const isString = isType('string');\n    const isBoolean = isSimpleType('boolean');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n\n    const option = name => editor => editor.options.get(name);\n    const register = editor => {\n      const registerOption = editor.options.register;\n      const toolbarProcessor = defaultValue => value => {\n        const valid = isBoolean(value) || isString(value);\n        if (valid) {\n          if (isBoolean(value)) {\n            return {\n              value: value ? defaultValue : '',\n              valid\n            };\n          } else {\n            return {\n              value: value.trim(),\n              valid\n            };\n          }\n        } else {\n          return {\n            valid: false,\n            message: 'Must be a boolean or string.'\n          };\n        }\n      };\n      const defaultSelectionToolbar = 'bold italic | quicklink h2 h3 blockquote';\n      registerOption('quickbars_selection_toolbar', {\n        processor: toolbarProcessor(defaultSelectionToolbar),\n        default: defaultSelectionToolbar\n      });\n      const defaultInsertToolbar = 'quickimage quicktable';\n      registerOption('quickbars_insert_toolbar', {\n        processor: toolbarProcessor(defaultInsertToolbar),\n        default: defaultInsertToolbar\n      });\n      const defaultImageToolbar = 'alignleft aligncenter alignright';\n      registerOption('quickbars_image_toolbar', {\n        processor: toolbarProcessor(defaultImageToolbar),\n        default: defaultImageToolbar\n      });\n    };\n    const getTextSelectionToolbarItems = option('quickbars_selection_toolbar');\n    const getInsertToolbarItems = option('quickbars_insert_toolbar');\n    const getImageToolbarItems = option('quickbars_image_toolbar');\n\n    let unique = 0;\n    const generate = prefix => {\n      const date = new Date();\n      const time = date.getTime();\n      const random = Math.floor(Math.random() * 1000000000);\n      unique++;\n      return prefix + '_' + random + unique + String(time);\n    };\n\n    const insertTable = (editor, columns, rows) => {\n      editor.execCommand('mceInsertTable', false, {\n        rows,\n        columns\n      });\n    };\n    const insertBlob = (editor, base64, blob) => {\n      const blobCache = editor.editorUpload.blobCache;\n      const blobInfo = blobCache.create(generate('mceu'), blob, base64);\n      blobCache.add(blobInfo);\n      editor.insertContent(editor.dom.createHTML('img', { src: blobInfo.blobUri() }));\n    };\n\n    const blobToBase64 = blob => {\n      return new Promise(resolve => {\n        const reader = new FileReader();\n        reader.onloadend = () => {\n          resolve(reader.result.split(',')[1]);\n        };\n        reader.readAsDataURL(blob);\n      });\n    };\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.Env');\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Delay');\n\n    const pickFile = editor => new Promise(resolve => {\n      const fileInput = document.createElement('input');\n      fileInput.type = 'file';\n      fileInput.accept = 'image/*';\n      fileInput.style.position = 'fixed';\n      fileInput.style.left = '0';\n      fileInput.style.top = '0';\n      fileInput.style.opacity = '0.001';\n      document.body.appendChild(fileInput);\n      const changeHandler = e => {\n        resolve(Array.prototype.slice.call(e.target.files));\n      };\n      fileInput.addEventListener('change', changeHandler);\n      const cancelHandler = e => {\n        const cleanup = () => {\n          var _a;\n          resolve([]);\n          (_a = fileInput.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(fileInput);\n        };\n        if (global$1.os.isAndroid() && e.type !== 'remove') {\n          global.setEditorTimeout(editor, cleanup, 0);\n        } else {\n          cleanup();\n        }\n        editor.off('focusin remove', cancelHandler);\n      };\n      editor.on('focusin remove', cancelHandler);\n      fileInput.click();\n    });\n\n    const setupButtons = editor => {\n      editor.ui.registry.addButton('quickimage', {\n        icon: 'image',\n        tooltip: 'Insert image',\n        onAction: () => {\n          pickFile(editor).then(files => {\n            if (files.length > 0) {\n              const blob = files[0];\n              blobToBase64(blob).then(base64 => {\n                insertBlob(editor, base64, blob);\n              });\n            }\n          });\n        }\n      });\n      editor.ui.registry.addButton('quicktable', {\n        icon: 'table',\n        tooltip: 'Insert table',\n        onAction: () => {\n          insertTable(editor, 2, 2);\n        }\n      });\n    };\n\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const never = constant(false);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const ELEMENT = 1;\n\n    const name = element => {\n      const r = element.dom.nodeName;\n      return r.toLowerCase();\n    };\n\n    const has = (element, key) => {\n      const dom = element.dom;\n      return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;\n    };\n\n    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {\n      if (is(scope, a)) {\n        return Optional.some(scope);\n      } else if (isFunction(isRoot) && isRoot(scope)) {\n        return Optional.none();\n      } else {\n        return ancestor(scope, a, isRoot);\n      }\n    };\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom(node);\n    };\n    const fromDom = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom,\n      fromPoint\n    };\n\n    const is = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n\n    const ancestor$1 = (scope, predicate, isRoot) => {\n      let element = scope.dom;\n      const stop = isFunction(isRoot) ? isRoot : never;\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        if (predicate(el)) {\n          return Optional.some(el);\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const closest$2 = (scope, predicate, isRoot) => {\n      const is = (s, test) => test(s);\n      return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);\n    };\n\n    const closest$1 = (scope, predicate, isRoot) => closest$2(scope, predicate, isRoot).isSome();\n\n    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);\n    const closest = (scope, selector, isRoot) => {\n      const is$1 = (element, selector) => is(element, selector);\n      return ClosestOrAncestor(is$1, ancestor, scope, selector, isRoot);\n    };\n\n    const addToEditor$1 = editor => {\n      const insertToolbarItems = getInsertToolbarItems(editor);\n      if (insertToolbarItems.length > 0) {\n        editor.ui.registry.addContextToolbar('quickblock', {\n          predicate: node => {\n            const sugarNode = SugarElement.fromDom(node);\n            const textBlockElementsMap = editor.schema.getTextBlockElements();\n            const isRoot = elem => elem.dom === editor.getBody();\n            return !has(sugarNode, 'data-mce-bogus') && closest(sugarNode, 'table,[data-mce-bogus=\"all\"]', isRoot).fold(() => closest$1(sugarNode, elem => name(elem) in textBlockElementsMap && editor.dom.isEmpty(elem.dom), isRoot), never);\n          },\n          items: insertToolbarItems,\n          position: 'line',\n          scope: 'editor'\n        });\n      }\n    };\n\n    const addToEditor = editor => {\n      const isEditable = node => editor.dom.getContentEditableParent(node) !== 'false';\n      const isImage = node => node.nodeName === 'IMG' || node.nodeName === 'FIGURE' && /image/i.test(node.className);\n      const imageToolbarItems = getImageToolbarItems(editor);\n      if (imageToolbarItems.length > 0) {\n        editor.ui.registry.addContextToolbar('imageselection', {\n          predicate: isImage,\n          items: imageToolbarItems,\n          position: 'node'\n        });\n      }\n      const textToolbarItems = getTextSelectionToolbarItems(editor);\n      if (textToolbarItems.length > 0) {\n        editor.ui.registry.addContextToolbar('textselection', {\n          predicate: node => !isImage(node) && !editor.selection.isCollapsed() && isEditable(node),\n          items: textToolbarItems,\n          position: 'selection',\n          scope: 'editor'\n        });\n      }\n    };\n\n    var Plugin = () => {\n      global$2.add('quickbars', editor => {\n        register(editor);\n        setupButtons(editor);\n        addToEditor$1(editor);\n        addToEditor(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/save/index.js",
    "content": "// Exports the \"save\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/save')\n//   ES2015:\n//     import 'tinymce/plugins/save'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/save/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const isSimpleType = type => value => typeof value === type;\n    const isFunction = isSimpleType('function');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('save_enablewhendirty', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('save_onsavecallback', { processor: 'function' });\n      registerOption('save_oncancelcallback', { processor: 'function' });\n    };\n    const enableWhenDirty = option('save_enablewhendirty');\n    const getOnSaveCallback = option('save_onsavecallback');\n    const getOnCancelCallback = option('save_oncancelcallback');\n\n    const displayErrorMessage = (editor, message) => {\n      editor.notificationManager.open({\n        text: message,\n        type: 'error'\n      });\n    };\n    const save = editor => {\n      const formObj = global$1.DOM.getParent(editor.id, 'form');\n      if (enableWhenDirty(editor) && !editor.isDirty()) {\n        return;\n      }\n      editor.save();\n      const onSaveCallback = getOnSaveCallback(editor);\n      if (isFunction(onSaveCallback)) {\n        onSaveCallback.call(editor, editor);\n        editor.nodeChanged();\n        return;\n      }\n      if (formObj) {\n        editor.setDirty(false);\n        if (!formObj.onsubmit || formObj.onsubmit()) {\n          if (typeof formObj.submit === 'function') {\n            formObj.submit();\n          } else {\n            displayErrorMessage(editor, 'Error: Form submit field collision.');\n          }\n        }\n        editor.nodeChanged();\n      } else {\n        displayErrorMessage(editor, 'Error: No form element found.');\n      }\n    };\n    const cancel = editor => {\n      const h = global.trim(editor.startContent);\n      const onCancelCallback = getOnCancelCallback(editor);\n      if (isFunction(onCancelCallback)) {\n        onCancelCallback.call(editor, editor);\n        return;\n      }\n      editor.resetContent(h);\n    };\n\n    const register$1 = editor => {\n      editor.addCommand('mceSave', () => {\n        save(editor);\n      });\n      editor.addCommand('mceCancel', () => {\n        cancel(editor);\n      });\n    };\n\n    const stateToggle = editor => api => {\n      const handler = () => {\n        api.setEnabled(!enableWhenDirty(editor) || editor.isDirty());\n      };\n      handler();\n      editor.on('NodeChange dirty', handler);\n      return () => editor.off('NodeChange dirty', handler);\n    };\n    const register = editor => {\n      editor.ui.registry.addButton('save', {\n        icon: 'save',\n        tooltip: 'Save',\n        enabled: false,\n        onAction: () => editor.execCommand('mceSave'),\n        onSetup: stateToggle(editor)\n      });\n      editor.ui.registry.addButton('cancel', {\n        icon: 'cancel',\n        tooltip: 'Cancel',\n        enabled: false,\n        onAction: () => editor.execCommand('mceCancel'),\n        onSetup: stateToggle(editor)\n      });\n      editor.addShortcut('Meta+S', '', 'mceSave');\n    };\n\n    var Plugin = () => {\n      global$2.add('save', editor => {\n        register$2(editor);\n        register(editor);\n        register$1(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/searchreplace/index.js",
    "content": "// Exports the \"searchreplace\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/searchreplace')\n//   ES2015:\n//     import 'tinymce/plugins/searchreplace'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/searchreplace/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const isString = isType$1('string');\n    const isArray = isType$1('array');\n    const isBoolean = isSimpleType('boolean');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isNumber = isSimpleType('number');\n\n    const noop = () => {\n    };\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const always = constant(true);\n\n    const punctuationStr = '[!-#%-*,-\\\\/:;?@\\\\[-\\\\]_{}\\xA1\\xAB\\xB7\\xBB\\xBF;\\xB7\\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\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1361-\\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\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u3008\\u3009\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30\\u2E31\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\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]';\n\n    const punctuation$1 = constant(punctuationStr);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const punctuation = punctuation$1;\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.Env');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const nativeSlice = Array.prototype.slice;\n    const nativePush = Array.prototype.push;\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const eachr = (xs, f) => {\n      for (let i = xs.length - 1; i >= 0; i--) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const groupBy = (xs, f) => {\n      if (xs.length === 0) {\n        return [];\n      } else {\n        let wasType = f(xs[0]);\n        const r = [];\n        let group = [];\n        for (let i = 0, len = xs.length; i < len; i++) {\n          const x = xs[i];\n          const type = f(x);\n          if (type !== wasType) {\n            r.push(group);\n            group = [];\n          }\n          wasType = type;\n          group.push(x);\n        }\n        if (group.length !== 0) {\n          r.push(group);\n        }\n        return r;\n      }\n    };\n    const foldl = (xs, f, acc) => {\n      each(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind = (xs, f) => flatten(map(xs, f));\n    const sort = (xs, comparator) => {\n      const copy = nativeSlice.call(xs, 0);\n      copy.sort(comparator);\n      return copy;\n    };\n\n    const hasOwnProperty = Object.hasOwnProperty;\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const DOCUMENT = 9;\n    const DOCUMENT_FRAGMENT = 11;\n    const ELEMENT = 1;\n    const TEXT = 3;\n\n    const type = element => element.dom.nodeType;\n    const isType = t => element => type(element) === t;\n    const isText$1 = isType(TEXT);\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom(node);\n    };\n    const fromDom = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom,\n      fromPoint\n    };\n\n    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;\n    const all = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);\n    };\n\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const children = element => map(element.dom.childNodes, SugarElement.fromDom);\n    const spot = (element, offset) => ({\n      element,\n      offset\n    });\n    const leaf = (element, offset) => {\n      const cs = children(element);\n      return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset);\n    };\n\n    const before = (marker, element) => {\n      const parent$1 = parent(marker);\n      parent$1.each(v => {\n        v.dom.insertBefore(element.dom, marker.dom);\n      });\n    };\n    const append = (parent, element) => {\n      parent.dom.appendChild(element.dom);\n    };\n    const wrap = (element, wrapper) => {\n      before(element, wrapper);\n      append(wrapper, element);\n    };\n\n    const NodeValue = (is, name) => {\n      const get = element => {\n        if (!is(element)) {\n          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');\n        }\n        return getOption(element).getOr('');\n      };\n      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();\n      const set = (element, value) => {\n        if (!is(element)) {\n          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');\n        }\n        element.dom.nodeValue = value;\n      };\n      return {\n        get,\n        getOption,\n        set\n      };\n    };\n\n    const api = NodeValue(isText$1, 'text');\n    const get$1 = element => api.get(element);\n\n    const compareDocumentPosition = (a, b, match) => {\n      return (a.compareDocumentPosition(b) & match) !== 0;\n    };\n    const documentPositionPreceding = (a, b) => {\n      return compareDocumentPosition(a, b, Node.DOCUMENT_POSITION_PRECEDING);\n    };\n\n    const descendants = (scope, selector) => all(selector, scope);\n\n    var global = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');\n\n    const isSimpleBoundary = (dom, node) => dom.isBlock(node) || has(dom.schema.getVoidElements(), node.nodeName);\n    const isContentEditableFalse = (dom, node) => dom.getContentEditable(node) === 'false';\n    const isContentEditableTrueInCef = (dom, node) => dom.getContentEditable(node) === 'true' && node.parentNode && dom.getContentEditableParent(node.parentNode) === 'false';\n    const isHidden = (dom, node) => !dom.isBlock(node) && has(dom.schema.getWhitespaceElements(), node.nodeName);\n    const isBoundary = (dom, node) => isSimpleBoundary(dom, node) || isContentEditableFalse(dom, node) || isHidden(dom, node) || isContentEditableTrueInCef(dom, node);\n    const isText = node => node.nodeType === 3;\n    const nuSection = () => ({\n      sOffset: 0,\n      fOffset: 0,\n      elements: []\n    });\n    const toLeaf = (node, offset) => leaf(SugarElement.fromDom(node), offset);\n    const walk = (dom, walkerFn, startNode, callbacks, endNode, skipStart = true) => {\n      let next = skipStart ? walkerFn(false) : startNode;\n      while (next) {\n        const isCefNode = isContentEditableFalse(dom, next);\n        if (isCefNode || isHidden(dom, next)) {\n          const stopWalking = isCefNode ? callbacks.cef(next) : callbacks.boundary(next);\n          if (stopWalking) {\n            break;\n          } else {\n            next = walkerFn(true);\n            continue;\n          }\n        } else if (isSimpleBoundary(dom, next)) {\n          if (callbacks.boundary(next)) {\n            break;\n          }\n        } else if (isText(next)) {\n          callbacks.text(next);\n        }\n        if (next === endNode) {\n          break;\n        } else {\n          next = walkerFn(false);\n        }\n      }\n    };\n    const collectTextToBoundary = (dom, section, node, rootNode, forwards) => {\n      var _a;\n      if (isBoundary(dom, node)) {\n        return;\n      }\n      const rootBlock = (_a = dom.getParent(rootNode, dom.isBlock)) !== null && _a !== void 0 ? _a : dom.getRoot();\n      const walker = new global(node, rootBlock);\n      const walkerFn = forwards ? walker.next.bind(walker) : walker.prev.bind(walker);\n      walk(dom, walkerFn, node, {\n        boundary: always,\n        cef: always,\n        text: next => {\n          if (forwards) {\n            section.fOffset += next.length;\n          } else {\n            section.sOffset += next.length;\n          }\n          section.elements.push(SugarElement.fromDom(next));\n        }\n      });\n    };\n    const collect = (dom, rootNode, startNode, endNode, callbacks, skipStart = true) => {\n      const walker = new global(startNode, rootNode);\n      const sections = [];\n      let current = nuSection();\n      collectTextToBoundary(dom, current, startNode, rootNode, false);\n      const finishSection = () => {\n        if (current.elements.length > 0) {\n          sections.push(current);\n          current = nuSection();\n        }\n        return false;\n      };\n      walk(dom, walker.next.bind(walker), startNode, {\n        boundary: finishSection,\n        cef: node => {\n          finishSection();\n          if (callbacks) {\n            sections.push(...callbacks.cef(node));\n          }\n          return false;\n        },\n        text: next => {\n          current.elements.push(SugarElement.fromDom(next));\n          if (callbacks) {\n            callbacks.text(next, current);\n          }\n        }\n      }, endNode, skipStart);\n      if (endNode) {\n        collectTextToBoundary(dom, current, endNode, rootNode, true);\n      }\n      finishSection();\n      return sections;\n    };\n    const collectRangeSections = (dom, rng) => {\n      const start = toLeaf(rng.startContainer, rng.startOffset);\n      const startNode = start.element.dom;\n      const end = toLeaf(rng.endContainer, rng.endOffset);\n      const endNode = end.element.dom;\n      return collect(dom, rng.commonAncestorContainer, startNode, endNode, {\n        text: (node, section) => {\n          if (node === endNode) {\n            section.fOffset += node.length - end.offset;\n          } else if (node === startNode) {\n            section.sOffset += start.offset;\n          }\n        },\n        cef: node => {\n          const sections = bind(descendants(SugarElement.fromDom(node), '*[contenteditable=true]'), e => {\n            const ceTrueNode = e.dom;\n            return collect(dom, ceTrueNode, ceTrueNode);\n          });\n          return sort(sections, (a, b) => documentPositionPreceding(a.elements[0].dom, b.elements[0].dom) ? 1 : -1);\n        }\n      }, false);\n    };\n    const fromRng = (dom, rng) => rng.collapsed ? [] : collectRangeSections(dom, rng);\n    const fromNode = (dom, node) => {\n      const rng = dom.createRng();\n      rng.selectNode(node);\n      return fromRng(dom, rng);\n    };\n    const fromNodes = (dom, nodes) => bind(nodes, node => fromNode(dom, node));\n\n    const find$2 = (text, pattern, start = 0, finish = text.length) => {\n      const regex = pattern.regex;\n      regex.lastIndex = start;\n      const results = [];\n      let match;\n      while (match = regex.exec(text)) {\n        const matchedText = match[pattern.matchIndex];\n        const matchStart = match.index + match[0].indexOf(matchedText);\n        const matchFinish = matchStart + matchedText.length;\n        if (matchFinish > finish) {\n          break;\n        }\n        results.push({\n          start: matchStart,\n          finish: matchFinish\n        });\n        regex.lastIndex = matchFinish;\n      }\n      return results;\n    };\n    const extract = (elements, matches) => {\n      const nodePositions = foldl(elements, (acc, element) => {\n        const content = get$1(element);\n        const start = acc.last;\n        const finish = start + content.length;\n        const positions = bind(matches, (match, matchIdx) => {\n          if (match.start < finish && match.finish > start) {\n            return [{\n                element,\n                start: Math.max(start, match.start) - start,\n                finish: Math.min(finish, match.finish) - start,\n                matchId: matchIdx\n              }];\n          } else {\n            return [];\n          }\n        });\n        return {\n          results: acc.results.concat(positions),\n          last: finish\n        };\n      }, {\n        results: [],\n        last: 0\n      }).results;\n      return groupBy(nodePositions, position => position.matchId);\n    };\n\n    const find$1 = (pattern, sections) => bind(sections, section => {\n      const elements = section.elements;\n      const content = map(elements, get$1).join('');\n      const positions = find$2(content, pattern, section.sOffset, content.length - section.fOffset);\n      return extract(elements, positions);\n    });\n    const mark = (matches, replacementNode) => {\n      eachr(matches, (match, idx) => {\n        eachr(match, pos => {\n          const wrapper = SugarElement.fromDom(replacementNode.cloneNode(false));\n          set(wrapper, 'data-mce-index', idx);\n          const textNode = pos.element.dom;\n          if (textNode.length === pos.finish && pos.start === 0) {\n            wrap(pos.element, wrapper);\n          } else {\n            if (textNode.length !== pos.finish) {\n              textNode.splitText(pos.finish);\n            }\n            const matchNode = textNode.splitText(pos.start);\n            wrap(SugarElement.fromDom(matchNode), wrapper);\n          }\n        });\n      });\n    };\n    const findAndMark = (dom, pattern, node, replacementNode) => {\n      const textSections = fromNode(dom, node);\n      const matches = find$1(pattern, textSections);\n      mark(matches, replacementNode);\n      return matches.length;\n    };\n    const findAndMarkInSelection = (dom, pattern, selection, replacementNode) => {\n      const bookmark = selection.getBookmark();\n      const nodes = dom.select('td[data-mce-selected],th[data-mce-selected]');\n      const textSections = nodes.length > 0 ? fromNodes(dom, nodes) : fromRng(dom, selection.getRng());\n      const matches = find$1(pattern, textSections);\n      mark(matches, replacementNode);\n      selection.moveToBookmark(bookmark);\n      return matches.length;\n    };\n\n    const getElmIndex = elm => {\n      return elm.getAttribute('data-mce-index');\n    };\n    const markAllMatches = (editor, currentSearchState, pattern, inSelection) => {\n      const marker = editor.dom.create('span', { 'data-mce-bogus': 1 });\n      marker.className = 'mce-match-marker';\n      const node = editor.getBody();\n      done(editor, currentSearchState, false);\n      if (inSelection) {\n        return findAndMarkInSelection(editor.dom, pattern, editor.selection, marker);\n      } else {\n        return findAndMark(editor.dom, pattern, node, marker);\n      }\n    };\n    const unwrap = node => {\n      var _a;\n      const parentNode = node.parentNode;\n      if (node.firstChild) {\n        parentNode.insertBefore(node.firstChild, node);\n      }\n      (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);\n    };\n    const findSpansByIndex = (editor, index) => {\n      const spans = [];\n      const nodes = global$1.toArray(editor.getBody().getElementsByTagName('span'));\n      if (nodes.length) {\n        for (let i = 0; i < nodes.length; i++) {\n          const nodeIndex = getElmIndex(nodes[i]);\n          if (nodeIndex === null || !nodeIndex.length) {\n            continue;\n          }\n          if (nodeIndex === index.toString()) {\n            spans.push(nodes[i]);\n          }\n        }\n      }\n      return spans;\n    };\n    const moveSelection = (editor, currentSearchState, forward) => {\n      const searchState = currentSearchState.get();\n      let testIndex = searchState.index;\n      const dom = editor.dom;\n      if (forward) {\n        if (testIndex + 1 === searchState.count) {\n          testIndex = 0;\n        } else {\n          testIndex++;\n        }\n      } else {\n        if (testIndex - 1 === -1) {\n          testIndex = searchState.count - 1;\n        } else {\n          testIndex--;\n        }\n      }\n      dom.removeClass(findSpansByIndex(editor, searchState.index), 'mce-match-marker-selected');\n      const spans = findSpansByIndex(editor, testIndex);\n      if (spans.length) {\n        dom.addClass(findSpansByIndex(editor, testIndex), 'mce-match-marker-selected');\n        editor.selection.scrollIntoView(spans[0]);\n        return testIndex;\n      }\n      return -1;\n    };\n    const removeNode = (dom, node) => {\n      const parent = node.parentNode;\n      dom.remove(node);\n      if (parent && dom.isEmpty(parent)) {\n        dom.remove(parent);\n      }\n    };\n    const escapeSearchText = (text, wholeWord) => {\n      const escapedText = text.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, '\\\\$&').replace(/\\s/g, '[^\\\\S\\\\r\\\\n\\\\uFEFF]');\n      const wordRegex = '(' + escapedText + ')';\n      return wholeWord ? `(?:^|\\\\s|${ punctuation() })` + wordRegex + `(?=$|\\\\s|${ punctuation() })` : wordRegex;\n    };\n    const find = (editor, currentSearchState, text, matchCase, wholeWord, inSelection) => {\n      const selection = editor.selection;\n      const escapedText = escapeSearchText(text, wholeWord);\n      const isForwardSelection = selection.isForward();\n      const pattern = {\n        regex: new RegExp(escapedText, matchCase ? 'g' : 'gi'),\n        matchIndex: 1\n      };\n      const count = markAllMatches(editor, currentSearchState, pattern, inSelection);\n      if (global$2.browser.isSafari()) {\n        selection.setRng(selection.getRng(), isForwardSelection);\n      }\n      if (count) {\n        const newIndex = moveSelection(editor, currentSearchState, true);\n        currentSearchState.set({\n          index: newIndex,\n          count,\n          text,\n          matchCase,\n          wholeWord,\n          inSelection\n        });\n      }\n      return count;\n    };\n    const next = (editor, currentSearchState) => {\n      const index = moveSelection(editor, currentSearchState, true);\n      currentSearchState.set({\n        ...currentSearchState.get(),\n        index\n      });\n    };\n    const prev = (editor, currentSearchState) => {\n      const index = moveSelection(editor, currentSearchState, false);\n      currentSearchState.set({\n        ...currentSearchState.get(),\n        index\n      });\n    };\n    const isMatchSpan = node => {\n      const matchIndex = getElmIndex(node);\n      return matchIndex !== null && matchIndex.length > 0;\n    };\n    const replace = (editor, currentSearchState, text, forward, all) => {\n      const searchState = currentSearchState.get();\n      const currentIndex = searchState.index;\n      let currentMatchIndex, nextIndex = currentIndex;\n      forward = forward !== false;\n      const node = editor.getBody();\n      const nodes = global$1.grep(global$1.toArray(node.getElementsByTagName('span')), isMatchSpan);\n      for (let i = 0; i < nodes.length; i++) {\n        const nodeIndex = getElmIndex(nodes[i]);\n        let matchIndex = currentMatchIndex = parseInt(nodeIndex, 10);\n        if (all || matchIndex === searchState.index) {\n          if (text.length) {\n            nodes[i].innerText = text;\n            unwrap(nodes[i]);\n          } else {\n            removeNode(editor.dom, nodes[i]);\n          }\n          while (nodes[++i]) {\n            matchIndex = parseInt(getElmIndex(nodes[i]), 10);\n            if (matchIndex === currentMatchIndex) {\n              removeNode(editor.dom, nodes[i]);\n            } else {\n              i--;\n              break;\n            }\n          }\n          if (forward) {\n            nextIndex--;\n          }\n        } else if (currentMatchIndex > currentIndex) {\n          nodes[i].setAttribute('data-mce-index', String(currentMatchIndex - 1));\n        }\n      }\n      currentSearchState.set({\n        ...searchState,\n        count: all ? 0 : searchState.count - 1,\n        index: nextIndex\n      });\n      if (forward) {\n        next(editor, currentSearchState);\n      } else {\n        prev(editor, currentSearchState);\n      }\n      return !all && currentSearchState.get().count > 0;\n    };\n    const done = (editor, currentSearchState, keepEditorSelection) => {\n      let startContainer;\n      let endContainer;\n      const searchState = currentSearchState.get();\n      const nodes = global$1.toArray(editor.getBody().getElementsByTagName('span'));\n      for (let i = 0; i < nodes.length; i++) {\n        const nodeIndex = getElmIndex(nodes[i]);\n        if (nodeIndex !== null && nodeIndex.length) {\n          if (nodeIndex === searchState.index.toString()) {\n            if (!startContainer) {\n              startContainer = nodes[i].firstChild;\n            }\n            endContainer = nodes[i].firstChild;\n          }\n          unwrap(nodes[i]);\n        }\n      }\n      currentSearchState.set({\n        ...searchState,\n        index: -1,\n        count: 0,\n        text: ''\n      });\n      if (startContainer && endContainer) {\n        const rng = editor.dom.createRng();\n        rng.setStart(startContainer, 0);\n        rng.setEnd(endContainer, endContainer.data.length);\n        if (keepEditorSelection !== false) {\n          editor.selection.setRng(rng);\n        }\n        return rng;\n      } else {\n        return undefined;\n      }\n    };\n    const hasNext = (editor, currentSearchState) => currentSearchState.get().count > 1;\n    const hasPrev = (editor, currentSearchState) => currentSearchState.get().count > 1;\n\n    const get = (editor, currentState) => {\n      const done$1 = keepEditorSelection => {\n        return done(editor, currentState, keepEditorSelection);\n      };\n      const find$1 = (text, matchCase, wholeWord, inSelection = false) => {\n        return find(editor, currentState, text, matchCase, wholeWord, inSelection);\n      };\n      const next$1 = () => {\n        return next(editor, currentState);\n      };\n      const prev$1 = () => {\n        return prev(editor, currentState);\n      };\n      const replace$1 = (text, forward, all) => {\n        return replace(editor, currentState, text, forward, all);\n      };\n      return {\n        done: done$1,\n        find: find$1,\n        next: next$1,\n        prev: prev$1,\n        replace: replace$1\n      };\n    };\n\n    const singleton = doRevoke => {\n      const subject = Cell(Optional.none());\n      const revoke = () => subject.get().each(doRevoke);\n      const clear = () => {\n        revoke();\n        subject.set(Optional.none());\n      };\n      const isSet = () => subject.get().isSome();\n      const get = () => subject.get();\n      const set = s => {\n        revoke();\n        subject.set(Optional.some(s));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const value = () => {\n      const subject = singleton(noop);\n      const on = f => subject.get().each(f);\n      return {\n        ...subject,\n        on\n      };\n    };\n\n    const open = (editor, currentSearchState) => {\n      const dialogApi = value();\n      editor.undoManager.add();\n      const selectedText = global$1.trim(editor.selection.getContent({ format: 'text' }));\n      const updateButtonStates = api => {\n        api.setEnabled('next', hasNext(editor, currentSearchState));\n        api.setEnabled('prev', hasPrev(editor, currentSearchState));\n      };\n      const updateSearchState = api => {\n        const data = api.getData();\n        const current = currentSearchState.get();\n        currentSearchState.set({\n          ...current,\n          matchCase: data.matchcase,\n          wholeWord: data.wholewords,\n          inSelection: data.inselection\n        });\n      };\n      const disableAll = (api, disable) => {\n        const buttons = [\n          'replace',\n          'replaceall',\n          'prev',\n          'next'\n        ];\n        const toggle = name => api.setEnabled(name, !disable);\n        each(buttons, toggle);\n      };\n      const notFoundAlert = api => {\n        editor.windowManager.alert('Could not find the specified string.', () => {\n          api.focus('findtext');\n        });\n      };\n      const focusButtonIfRequired = (api, name) => {\n        if (global$2.browser.isSafari() && global$2.deviceType.isTouch() && (name === 'find' || name === 'replace' || name === 'replaceall')) {\n          api.focus(name);\n        }\n      };\n      const reset = api => {\n        done(editor, currentSearchState, false);\n        disableAll(api, true);\n        updateButtonStates(api);\n      };\n      const doFind = api => {\n        const data = api.getData();\n        const last = currentSearchState.get();\n        if (!data.findtext.length) {\n          reset(api);\n          return;\n        }\n        if (last.text === data.findtext && last.matchCase === data.matchcase && last.wholeWord === data.wholewords) {\n          next(editor, currentSearchState);\n        } else {\n          const count = find(editor, currentSearchState, data.findtext, data.matchcase, data.wholewords, data.inselection);\n          if (count <= 0) {\n            notFoundAlert(api);\n          }\n          disableAll(api, count === 0);\n        }\n        updateButtonStates(api);\n      };\n      const initialState = currentSearchState.get();\n      const initialData = {\n        findtext: selectedText,\n        replacetext: '',\n        wholewords: initialState.wholeWord,\n        matchcase: initialState.matchCase,\n        inselection: initialState.inSelection\n      };\n      const spec = {\n        title: 'Find and Replace',\n        size: 'normal',\n        body: {\n          type: 'panel',\n          items: [\n            {\n              type: 'bar',\n              items: [\n                {\n                  type: 'input',\n                  name: 'findtext',\n                  placeholder: 'Find',\n                  maximized: true,\n                  inputMode: 'search'\n                },\n                {\n                  type: 'button',\n                  name: 'prev',\n                  text: 'Previous',\n                  icon: 'action-prev',\n                  enabled: false,\n                  borderless: true\n                },\n                {\n                  type: 'button',\n                  name: 'next',\n                  text: 'Next',\n                  icon: 'action-next',\n                  enabled: false,\n                  borderless: true\n                }\n              ]\n            },\n            {\n              type: 'input',\n              name: 'replacetext',\n              placeholder: 'Replace with',\n              inputMode: 'search'\n            }\n          ]\n        },\n        buttons: [\n          {\n            type: 'menu',\n            name: 'options',\n            icon: 'preferences',\n            tooltip: 'Preferences',\n            align: 'start',\n            items: [\n              {\n                type: 'togglemenuitem',\n                name: 'matchcase',\n                text: 'Match case'\n              },\n              {\n                type: 'togglemenuitem',\n                name: 'wholewords',\n                text: 'Find whole words only'\n              },\n              {\n                type: 'togglemenuitem',\n                name: 'inselection',\n                text: 'Find in selection'\n              }\n            ]\n          },\n          {\n            type: 'custom',\n            name: 'find',\n            text: 'Find',\n            primary: true\n          },\n          {\n            type: 'custom',\n            name: 'replace',\n            text: 'Replace',\n            enabled: false\n          },\n          {\n            type: 'custom',\n            name: 'replaceall',\n            text: 'Replace all',\n            enabled: false\n          }\n        ],\n        initialData,\n        onChange: (api, details) => {\n          if (details.name === 'findtext' && currentSearchState.get().count > 0) {\n            reset(api);\n          }\n        },\n        onAction: (api, details) => {\n          const data = api.getData();\n          switch (details.name) {\n          case 'find':\n            doFind(api);\n            break;\n          case 'replace':\n            if (!replace(editor, currentSearchState, data.replacetext)) {\n              reset(api);\n            } else {\n              updateButtonStates(api);\n            }\n            break;\n          case 'replaceall':\n            replace(editor, currentSearchState, data.replacetext, true, true);\n            reset(api);\n            break;\n          case 'prev':\n            prev(editor, currentSearchState);\n            updateButtonStates(api);\n            break;\n          case 'next':\n            next(editor, currentSearchState);\n            updateButtonStates(api);\n            break;\n          case 'matchcase':\n          case 'wholewords':\n          case 'inselection':\n            updateSearchState(api);\n            reset(api);\n            break;\n          }\n          focusButtonIfRequired(api, details.name);\n        },\n        onSubmit: api => {\n          doFind(api);\n          focusButtonIfRequired(api, 'find');\n        },\n        onClose: () => {\n          editor.focus();\n          done(editor, currentSearchState);\n          editor.undoManager.add();\n        }\n      };\n      dialogApi.set(editor.windowManager.open(spec, { inline: 'toolbar' }));\n    };\n\n    const register$1 = (editor, currentSearchState) => {\n      editor.addCommand('SearchReplace', () => {\n        open(editor, currentSearchState);\n      });\n    };\n\n    const showDialog = (editor, currentSearchState) => () => {\n      open(editor, currentSearchState);\n    };\n    const register = (editor, currentSearchState) => {\n      editor.ui.registry.addMenuItem('searchreplace', {\n        text: 'Find and replace...',\n        shortcut: 'Meta+F',\n        onAction: showDialog(editor, currentSearchState),\n        icon: 'search'\n      });\n      editor.ui.registry.addButton('searchreplace', {\n        tooltip: 'Find and replace',\n        onAction: showDialog(editor, currentSearchState),\n        icon: 'search'\n      });\n      editor.shortcuts.add('Meta+F', '', showDialog(editor, currentSearchState));\n    };\n\n    var Plugin = () => {\n      global$3.add('searchreplace', editor => {\n        const currentSearchState = Cell({\n          index: -1,\n          count: 0,\n          text: '',\n          matchCase: false,\n          wholeWord: false,\n          inSelection: false\n        });\n        register$1(editor, currentSearchState);\n        register(editor, currentSearchState);\n        return get(editor, currentSearchState);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/table/index.js",
    "content": "// Exports the \"table\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/table')\n//   ES2015:\n//     import 'tinymce/plugins/table'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/table/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq$1 = t => a => t === a;\n    const isString = isType$1('string');\n    const isArray = isType$1('array');\n    const isBoolean = isSimpleType('boolean');\n    const isUndefined = eq$1(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n\n    const noop = () => {\n    };\n    const compose1 = (fbc, fab) => a => fbc(fab(a));\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const identity = x => {\n      return x;\n    };\n    const tripleEquals = (a, b) => {\n      return a === b;\n    };\n    function curry(fn, ...initialArgs) {\n      return (...restArgs) => {\n        const all = initialArgs.concat(restArgs);\n        return fn.apply(null, all);\n      };\n    }\n    const call = f => {\n      f();\n    };\n    const never = constant(false);\n    const always = constant(true);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const keys = Object.keys;\n    const hasOwnProperty = Object.hasOwnProperty;\n    const each$1 = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const objAcc = r => (x, i) => {\n      r[i] = x;\n    };\n    const internalFilter = (obj, pred, onTrue, onFalse) => {\n      each$1(obj, (x, i) => {\n        (pred(x, i) ? onTrue : onFalse)(x, i);\n      });\n    };\n    const filter$1 = (obj, pred) => {\n      const t = {};\n      internalFilter(obj, pred, objAcc(t), noop);\n      return t;\n    };\n    const mapToArray = (obj, f) => {\n      const r = [];\n      each$1(obj, (value, name) => {\n        r.push(f(value, name));\n      });\n      return r;\n    };\n    const values = obj => {\n      return mapToArray(obj, identity);\n    };\n    const size = obj => {\n      return keys(obj).length;\n    };\n    const get$4 = (obj, key) => {\n      return has(obj, key) ? Optional.from(obj[key]) : Optional.none();\n    };\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n    const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null;\n\n    const nativeIndexOf = Array.prototype.indexOf;\n    const nativePush = Array.prototype.push;\n    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);\n    const contains = (xs, x) => rawIndexOf(xs, x) > -1;\n    const exists = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const range = (num, f) => {\n      const r = [];\n      for (let i = 0; i < num; i++) {\n        r.push(f(i));\n      }\n      return r;\n    };\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const eachr = (xs, f) => {\n      for (let i = xs.length - 1; i >= 0; i--) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const partition = (xs, pred) => {\n      const pass = [];\n      const fail = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        const arr = pred(x, i) ? pass : fail;\n        arr.push(x);\n      }\n      return {\n        pass,\n        fail\n      };\n    };\n    const filter = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n    const foldr = (xs, f, acc) => {\n      eachr(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const foldl = (xs, f, acc) => {\n      each(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n    const flatten$1 = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind = (xs, f) => flatten$1(map(xs, f));\n    const forall = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        const x = xs[i];\n        if (pred(x, i) !== true) {\n          return false;\n        }\n      }\n      return true;\n    };\n    const mapToObject = (xs, f) => {\n      const r = {};\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        r[String(x)] = f(x, i);\n      }\n      return r;\n    };\n    const get$3 = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get$3(xs, 0);\n    const last = xs => get$3(xs, xs.length - 1);\n    const findMap = (arr, f) => {\n      for (let i = 0; i < arr.length; i++) {\n        const r = f(arr[i], i);\n        if (r.isSome()) {\n          return r;\n        }\n      }\n      return Optional.none();\n    };\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom$1(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom$1(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom$1(node);\n    };\n    const fromDom$1 = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom: fromDom$1,\n      fromPoint\n    };\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const COMMENT = 8;\n    const DOCUMENT = 9;\n    const DOCUMENT_FRAGMENT = 11;\n    const ELEMENT = 1;\n    const TEXT = 3;\n\n    const name = element => {\n      const r = element.dom.nodeName;\n      return r.toLowerCase();\n    };\n    const type = element => element.dom.nodeType;\n    const isType = t => element => type(element) === t;\n    const isComment = element => type(element) === COMMENT || name(element) === '#comment';\n    const isElement = isType(ELEMENT);\n    const isText = isType(TEXT);\n    const isDocument = isType(DOCUMENT);\n    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);\n    const isTag = tag => e => isElement(e) && name(e) === tag;\n\n    const is$2 = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;\n    const all$1 = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? [] : map(base.querySelectorAll(selector), SugarElement.fromDom);\n    };\n    const one = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);\n    };\n\n    const eq = (e1, e2) => e1.dom === e2.dom;\n    const is$1 = is$2;\n\n    const owner = element => SugarElement.fromDom(element.dom.ownerDocument);\n    const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const parents = (element, isRoot) => {\n      const stop = isFunction(isRoot) ? isRoot : never;\n      let dom = element.dom;\n      const ret = [];\n      while (dom.parentNode !== null && dom.parentNode !== undefined) {\n        const rawParent = dom.parentNode;\n        const p = SugarElement.fromDom(rawParent);\n        ret.push(p);\n        if (stop(p) === true) {\n          break;\n        } else {\n          dom = rawParent;\n        }\n      }\n      return ret;\n    };\n    const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);\n    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);\n    const children$3 = element => map(element.dom.childNodes, SugarElement.fromDom);\n    const child$3 = (element, index) => {\n      const cs = element.dom.childNodes;\n      return Optional.from(cs[index]).map(SugarElement.fromDom);\n    };\n    const firstChild = element => child$3(element, 0);\n\n    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);\n    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);\n    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;\n    const getShadowRoot = e => {\n      const r = getRootNode(e);\n      return isShadowRoot(r) ? Optional.some(r) : Optional.none();\n    };\n    const getShadowHost = e => SugarElement.fromDom(e.dom.host);\n\n    const inBody = element => {\n      const dom = isText(element) ? element.dom.parentNode : element.dom;\n      if (dom === undefined || dom === null || dom.ownerDocument === null) {\n        return false;\n      }\n      const doc = dom.ownerDocument;\n      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));\n    };\n\n    const children$2 = (scope, predicate) => filter(children$3(scope), predicate);\n    const descendants$1 = (scope, predicate) => {\n      let result = [];\n      each(children$3(scope), x => {\n        if (predicate(x)) {\n          result = result.concat([x]);\n        }\n        result = result.concat(descendants$1(x, predicate));\n      });\n      return result;\n    };\n\n    const children$1 = (scope, selector) => children$2(scope, e => is$2(e, selector));\n    const descendants = (scope, selector) => all$1(selector, scope);\n\n    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {\n      if (is(scope, a)) {\n        return Optional.some(scope);\n      } else if (isFunction(isRoot) && isRoot(scope)) {\n        return Optional.none();\n      } else {\n        return ancestor(scope, a, isRoot);\n      }\n    };\n\n    const ancestor$1 = (scope, predicate, isRoot) => {\n      let element = scope.dom;\n      const stop = isFunction(isRoot) ? isRoot : never;\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        if (predicate(el)) {\n          return Optional.some(el);\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const child$2 = (scope, predicate) => {\n      const pred = node => predicate(SugarElement.fromDom(node));\n      const result = find(scope.dom.childNodes, pred);\n      return result.map(SugarElement.fromDom);\n    };\n\n    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is$2(e, selector), isRoot);\n    const child$1 = (scope, selector) => child$2(scope, e => is$2(e, selector));\n    const descendant = (scope, selector) => one(selector, scope);\n    const closest = (scope, selector, isRoot) => {\n      const is = (element, selector) => is$2(element, selector);\n      return ClosestOrAncestor(is, ancestor, scope, selector, isRoot);\n    };\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set$2 = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const setAll = (element, attrs) => {\n      const dom = element.dom;\n      each$1(attrs, (v, k) => {\n        rawSet(dom, k, v);\n      });\n    };\n    const get$2 = (element, key) => {\n      const v = element.dom.getAttribute(key);\n      return v === null ? undefined : v;\n    };\n    const getOpt = (element, key) => Optional.from(get$2(element, key));\n    const remove$2 = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n    const clone = element => foldl(element.dom.attributes, (acc, attr) => {\n      acc[attr.name] = attr.value;\n      return acc;\n    }, {});\n\n    const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));\n    const cat = arr => {\n      const r = [];\n      const push = x => {\n        r.push(x);\n      };\n      for (let i = 0; i < arr.length; i++) {\n        arr[i].each(push);\n      }\n      return r;\n    };\n    const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();\n    const flatten = oot => oot.bind(identity);\n    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();\n\n    const removeFromStart = (str, numChars) => {\n      return str.substring(numChars);\n    };\n\n    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;\n    const removeLeading = (str, prefix) => {\n      return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;\n    };\n    const startsWith = (str, prefix) => {\n      return checkRange(str, prefix, 0);\n    };\n    const blank = r => s => s.replace(r, '');\n    const trim = blank(/^\\s+|\\s+$/g);\n    const isNotEmpty = s => s.length > 0;\n    const isEmpty = s => !isNotEmpty(s);\n    const toInt = (value, radix = 10) => {\n      const num = parseInt(value, radix);\n      return isNaN(num) ? Optional.none() : Optional.some(num);\n    };\n    const toFloat = value => {\n      const num = parseFloat(value);\n      return isNaN(num) ? Optional.none() : Optional.some(num);\n    };\n\n    const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);\n\n    const internalSet = (dom, property, value) => {\n      if (!isString(value)) {\n        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);\n        throw new Error('CSS value must be a string: ' + value);\n      }\n      if (isSupported(dom)) {\n        dom.style.setProperty(property, value);\n      }\n    };\n    const internalRemove = (dom, property) => {\n      if (isSupported(dom)) {\n        dom.style.removeProperty(property);\n      }\n    };\n    const set$1 = (element, property, value) => {\n      const dom = element.dom;\n      internalSet(dom, property, value);\n    };\n    const get$1 = (element, property) => {\n      const dom = element.dom;\n      const styles = window.getComputedStyle(dom);\n      const r = styles.getPropertyValue(property);\n      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;\n    };\n    const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';\n    const getRaw = (element, property) => {\n      const dom = element.dom;\n      const raw = getUnsafeProperty(dom, property);\n      return Optional.from(raw).filter(r => r.length > 0);\n    };\n    const remove$1 = (element, property) => {\n      const dom = element.dom;\n      internalRemove(dom, property);\n      if (is(getOpt(element, 'style').map(trim), '')) {\n        remove$2(element, 'style');\n      }\n    };\n\n    const getAttrValue = (cell, name, fallback = 0) => getOpt(cell, name).map(value => parseInt(value, 10)).getOr(fallback);\n\n    const firstLayer = (scope, selector) => {\n      return filterFirstLayer(scope, selector, always);\n    };\n    const filterFirstLayer = (scope, selector, predicate) => {\n      return bind(children$3(scope), x => {\n        if (is$2(x, selector)) {\n          return predicate(x) ? [x] : [];\n        } else {\n          return filterFirstLayer(x, selector, predicate);\n        }\n      });\n    };\n\n    const validSectionList = [\n      'tfoot',\n      'thead',\n      'tbody',\n      'colgroup'\n    ];\n    const isValidSection = parentName => contains(validSectionList, parentName);\n    const grid = (rows, columns) => ({\n      rows,\n      columns\n    });\n    const detail = (element, rowspan, colspan) => ({\n      element,\n      rowspan,\n      colspan\n    });\n    const extended = (element, rowspan, colspan, row, column, isLocked) => ({\n      element,\n      rowspan,\n      colspan,\n      row,\n      column,\n      isLocked\n    });\n    const rowdetail = (element, cells, section) => ({\n      element,\n      cells,\n      section\n    });\n    const bounds = (startRow, startCol, finishRow, finishCol) => ({\n      startRow,\n      startCol,\n      finishRow,\n      finishCol\n    });\n    const columnext = (element, colspan, column) => ({\n      element,\n      colspan,\n      column\n    });\n    const colgroup = (element, columns) => ({\n      element,\n      columns\n    });\n\n    const lookup = (tags, element, isRoot = never) => {\n      if (isRoot(element)) {\n        return Optional.none();\n      }\n      if (contains(tags, name(element))) {\n        return Optional.some(element);\n      }\n      const isRootOrUpperTable = elm => is$2(elm, 'table') || isRoot(elm);\n      return ancestor(element, tags.join(','), isRootOrUpperTable);\n    };\n    const cell = (element, isRoot) => lookup([\n      'td',\n      'th'\n    ], element, isRoot);\n    const cells = ancestor => firstLayer(ancestor, 'th,td');\n    const columns = ancestor => {\n      if (is$2(ancestor, 'colgroup')) {\n        return children$1(ancestor, 'col');\n      } else {\n        return bind(columnGroups(ancestor), columnGroup => children$1(columnGroup, 'col'));\n      }\n    };\n    const table = (element, isRoot) => closest(element, 'table', isRoot);\n    const rows = ancestor => firstLayer(ancestor, 'tr');\n    const columnGroups = ancestor => table(ancestor).fold(constant([]), table => children$1(table, 'colgroup'));\n\n    const fromRowsOrColGroups = (elems, getSection) => map(elems, row => {\n      if (name(row) === 'colgroup') {\n        const cells = map(columns(row), column => {\n          const colspan = getAttrValue(column, 'span', 1);\n          return detail(column, 1, colspan);\n        });\n        return rowdetail(row, cells, 'colgroup');\n      } else {\n        const cells$1 = map(cells(row), cell => {\n          const rowspan = getAttrValue(cell, 'rowspan', 1);\n          const colspan = getAttrValue(cell, 'colspan', 1);\n          return detail(cell, rowspan, colspan);\n        });\n        return rowdetail(row, cells$1, getSection(row));\n      }\n    });\n    const getParentSection = group => parent(group).map(parent => {\n      const parentName = name(parent);\n      return isValidSection(parentName) ? parentName : 'tbody';\n    }).getOr('tbody');\n    const fromTable$1 = table => {\n      const rows$1 = rows(table);\n      const columnGroups$1 = columnGroups(table);\n      const elems = [\n        ...columnGroups$1,\n        ...rows$1\n      ];\n      return fromRowsOrColGroups(elems, getParentSection);\n    };\n\n    const LOCKED_COL_ATTR = 'data-snooker-locked-cols';\n    const getLockedColumnsFromTable = table => getOpt(table, LOCKED_COL_ATTR).bind(lockedColStr => Optional.from(lockedColStr.match(/\\d+/g))).map(lockedCols => mapToObject(lockedCols, always));\n\n    const key = (row, column) => {\n      return row + ',' + column;\n    };\n    const getAt = (warehouse, row, column) => Optional.from(warehouse.access[key(row, column)]);\n    const findItem = (warehouse, item, comparator) => {\n      const filtered = filterItems(warehouse, detail => {\n        return comparator(item, detail.element);\n      });\n      return filtered.length > 0 ? Optional.some(filtered[0]) : Optional.none();\n    };\n    const filterItems = (warehouse, predicate) => {\n      const all = bind(warehouse.all, r => {\n        return r.cells;\n      });\n      return filter(all, predicate);\n    };\n    const generateColumns = rowData => {\n      const columnsGroup = {};\n      let index = 0;\n      each(rowData.cells, column => {\n        const colspan = column.colspan;\n        range(colspan, columnIndex => {\n          const colIndex = index + columnIndex;\n          columnsGroup[colIndex] = columnext(column.element, colspan, colIndex);\n        });\n        index += colspan;\n      });\n      return columnsGroup;\n    };\n    const generate$1 = list => {\n      const access = {};\n      const cells = [];\n      const tableOpt = head(list).map(rowData => rowData.element).bind(table);\n      const lockedColumns = tableOpt.bind(getLockedColumnsFromTable).getOr({});\n      let maxRows = 0;\n      let maxColumns = 0;\n      let rowCount = 0;\n      const {\n        pass: colgroupRows,\n        fail: rows\n      } = partition(list, rowData => rowData.section === 'colgroup');\n      each(rows, rowData => {\n        const currentRow = [];\n        each(rowData.cells, rowCell => {\n          let start = 0;\n          while (access[key(rowCount, start)] !== undefined) {\n            start++;\n          }\n          const isLocked = hasNonNullableKey(lockedColumns, start.toString());\n          const current = extended(rowCell.element, rowCell.rowspan, rowCell.colspan, rowCount, start, isLocked);\n          for (let occupiedColumnPosition = 0; occupiedColumnPosition < rowCell.colspan; occupiedColumnPosition++) {\n            for (let occupiedRowPosition = 0; occupiedRowPosition < rowCell.rowspan; occupiedRowPosition++) {\n              const rowPosition = rowCount + occupiedRowPosition;\n              const columnPosition = start + occupiedColumnPosition;\n              const newpos = key(rowPosition, columnPosition);\n              access[newpos] = current;\n              maxColumns = Math.max(maxColumns, columnPosition + 1);\n            }\n          }\n          currentRow.push(current);\n        });\n        maxRows++;\n        cells.push(rowdetail(rowData.element, currentRow, rowData.section));\n        rowCount++;\n      });\n      const {columns, colgroups} = last(colgroupRows).map(rowData => {\n        const columns = generateColumns(rowData);\n        const colgroup$1 = colgroup(rowData.element, values(columns));\n        return {\n          colgroups: [colgroup$1],\n          columns\n        };\n      }).getOrThunk(() => ({\n        colgroups: [],\n        columns: {}\n      }));\n      const grid$1 = grid(maxRows, maxColumns);\n      return {\n        grid: grid$1,\n        access,\n        all: cells,\n        columns,\n        colgroups\n      };\n    };\n    const fromTable = table => {\n      const list = fromTable$1(table);\n      return generate$1(list);\n    };\n    const justCells = warehouse => bind(warehouse.all, w => w.cells);\n    const justColumns = warehouse => values(warehouse.columns);\n    const hasColumns = warehouse => keys(warehouse.columns).length > 0;\n    const getColumnAt = (warehouse, columnIndex) => Optional.from(warehouse.columns[columnIndex]);\n    const Warehouse = {\n      fromTable,\n      generate: generate$1,\n      getAt,\n      findItem,\n      filterItems,\n      justCells,\n      justColumns,\n      hasColumns,\n      getColumnAt\n    };\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const getTDTHOverallStyle = (dom, elm, name) => {\n      const cells = dom.select('td,th', elm);\n      let firstChildStyle;\n      for (let i = 0; i < cells.length; i++) {\n        const currentStyle = dom.getStyle(cells[i], name);\n        if (isUndefined(firstChildStyle)) {\n          firstChildStyle = currentStyle;\n        }\n        if (firstChildStyle !== currentStyle) {\n          return '';\n        }\n      }\n      return firstChildStyle;\n    };\n    const setAlign = (editor, elm, name) => {\n      global$2.each('left center right'.split(' '), align => {\n        if (align !== name) {\n          editor.formatter.remove('align' + align, {}, elm);\n        }\n      });\n      if (name) {\n        editor.formatter.apply('align' + name, {}, elm);\n      }\n    };\n    const setVAlign = (editor, elm, name) => {\n      global$2.each('top middle bottom'.split(' '), align => {\n        if (align !== name) {\n          editor.formatter.remove('valign' + align, {}, elm);\n        }\n      });\n      if (name) {\n        editor.formatter.apply('valign' + name, {}, elm);\n      }\n    };\n\n    const fireTableModified = (editor, table, data) => {\n      editor.dispatch('TableModified', {\n        ...data,\n        table\n      });\n    };\n\n    const toNumber = (px, fallback) => toFloat(px).getOr(fallback);\n    const getProp = (element, name, fallback) => toNumber(get$1(element, name), fallback);\n    const calcContentBoxSize = (element, size, upper, lower) => {\n      const paddingUpper = getProp(element, `padding-${ upper }`, 0);\n      const paddingLower = getProp(element, `padding-${ lower }`, 0);\n      const borderUpper = getProp(element, `border-${ upper }-width`, 0);\n      const borderLower = getProp(element, `border-${ lower }-width`, 0);\n      return size - paddingUpper - paddingLower - borderUpper - borderLower;\n    };\n    const getCalculatedWidth = (element, boxSizing) => {\n      const dom = element.dom;\n      const width = dom.getBoundingClientRect().width || dom.offsetWidth;\n      return boxSizing === 'border-box' ? width : calcContentBoxSize(element, width, 'left', 'right');\n    };\n    const getInnerWidth = element => getCalculatedWidth(element, 'content-box');\n\n    const getInner = getInnerWidth;\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.Env');\n\n    const defaultTableToolbar = 'tableprops tabledelete | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol';\n    const defaultCellBorderWidths = range(5, i => {\n      const size = `${ i + 1 }px`;\n      return {\n        title: size,\n        value: size\n      };\n    });\n    const defaultCellBorderStyles = map([\n      'Solid',\n      'Dotted',\n      'Dashed',\n      'Double',\n      'Groove',\n      'Ridge',\n      'Inset',\n      'Outset',\n      'None',\n      'Hidden'\n    ], type => {\n      return {\n        title: type,\n        value: type.toLowerCase()\n      };\n    });\n    const defaultWidth = '100%';\n    const getPixelForcedWidth = editor => {\n      var _a;\n      const dom = editor.dom;\n      const parentBlock = (_a = dom.getParent(editor.selection.getStart(), dom.isBlock)) !== null && _a !== void 0 ? _a : editor.getBody();\n      return getInner(SugarElement.fromDom(parentBlock)) + 'px';\n    };\n    const determineDefaultStyles = (editor, defaultStyles) => {\n      if (isResponsiveForced(editor) || !shouldStyleWithCss(editor)) {\n        return defaultStyles;\n      } else if (isPixelsForced(editor)) {\n        return {\n          ...defaultStyles,\n          width: getPixelForcedWidth(editor)\n        };\n      } else {\n        return {\n          ...defaultStyles,\n          width: defaultWidth\n        };\n      }\n    };\n    const determineDefaultAttributes = (editor, defaultAttributes) => {\n      if (isResponsiveForced(editor) || shouldStyleWithCss(editor)) {\n        return defaultAttributes;\n      } else if (isPixelsForced(editor)) {\n        return {\n          ...defaultAttributes,\n          width: getPixelForcedWidth(editor)\n        };\n      } else {\n        return {\n          ...defaultAttributes,\n          width: defaultWidth\n        };\n      }\n    };\n    const option = name => editor => editor.options.get(name);\n    const register = editor => {\n      const registerOption = editor.options.register;\n      registerOption('table_border_widths', {\n        processor: 'object[]',\n        default: defaultCellBorderWidths\n      });\n      registerOption('table_border_styles', {\n        processor: 'object[]',\n        default: defaultCellBorderStyles\n      });\n      registerOption('table_cell_advtab', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('table_row_advtab', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('table_advtab', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('table_appearance_options', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('table_grid', {\n        processor: 'boolean',\n        default: !global$1.deviceType.isTouch()\n      });\n      registerOption('table_cell_class_list', {\n        processor: 'object[]',\n        default: []\n      });\n      registerOption('table_row_class_list', {\n        processor: 'object[]',\n        default: []\n      });\n      registerOption('table_class_list', {\n        processor: 'object[]',\n        default: []\n      });\n      registerOption('table_toolbar', {\n        processor: 'string',\n        default: defaultTableToolbar\n      });\n      registerOption('table_background_color_map', {\n        processor: 'object[]',\n        default: []\n      });\n      registerOption('table_border_color_map', {\n        processor: 'object[]',\n        default: []\n      });\n    };\n    const getTableSizingMode = option('table_sizing_mode');\n    const getTableBorderWidths = option('table_border_widths');\n    const getTableBorderStyles = option('table_border_styles');\n    const hasAdvancedCellTab = option('table_cell_advtab');\n    const hasAdvancedRowTab = option('table_row_advtab');\n    const hasAdvancedTableTab = option('table_advtab');\n    const hasAppearanceOptions = option('table_appearance_options');\n    const hasTableGrid = option('table_grid');\n    const shouldStyleWithCss = option('table_style_by_css');\n    const getCellClassList = option('table_cell_class_list');\n    const getRowClassList = option('table_row_class_list');\n    const getTableClassList = option('table_class_list');\n    const getToolbar = option('table_toolbar');\n    const getTableBackgroundColorMap = option('table_background_color_map');\n    const getTableBorderColorMap = option('table_border_color_map');\n    const isPixelsForced = editor => getTableSizingMode(editor) === 'fixed';\n    const isResponsiveForced = editor => getTableSizingMode(editor) === 'responsive';\n    const getDefaultStyles = editor => {\n      const options = editor.options;\n      const defaultStyles = options.get('table_default_styles');\n      return options.isSet('table_default_styles') ? defaultStyles : determineDefaultStyles(editor, defaultStyles);\n    };\n    const getDefaultAttributes = editor => {\n      const options = editor.options;\n      const defaultAttributes = options.get('table_default_attributes');\n      return options.isSet('table_default_attributes') ? defaultAttributes : determineDefaultAttributes(editor, defaultAttributes);\n    };\n\n    const getNodeName = elm => elm.nodeName.toLowerCase();\n    const getBody = editor => SugarElement.fromDom(editor.getBody());\n    const getIsRoot = editor => element => eq(element, getBody(editor));\n    const removePxSuffix = size => size ? size.replace(/px$/, '') : '';\n    const addPxSuffix = size => /^\\d+(\\.\\d+)?$/.test(size) ? size + 'px' : size;\n    const getSelectionStart = editor => SugarElement.fromDom(editor.selection.getStart());\n    const getSelectionEnd = editor => SugarElement.fromDom(editor.selection.getEnd());\n\n    const isWithin = (bounds, detail) => {\n      return detail.column >= bounds.startCol && detail.column + detail.colspan - 1 <= bounds.finishCol && detail.row >= bounds.startRow && detail.row + detail.rowspan - 1 <= bounds.finishRow;\n    };\n    const isRectangular = (warehouse, bounds) => {\n      let isRect = true;\n      const detailIsWithin = curry(isWithin, bounds);\n      for (let i = bounds.startRow; i <= bounds.finishRow; i++) {\n        for (let j = bounds.startCol; j <= bounds.finishCol; j++) {\n          isRect = isRect && Warehouse.getAt(warehouse, i, j).exists(detailIsWithin);\n        }\n      }\n      return isRect ? Optional.some(bounds) : Optional.none();\n    };\n\n    const getBounds = (detailA, detailB) => {\n      return bounds(Math.min(detailA.row, detailB.row), Math.min(detailA.column, detailB.column), Math.max(detailA.row + detailA.rowspan - 1, detailB.row + detailB.rowspan - 1), Math.max(detailA.column + detailA.colspan - 1, detailB.column + detailB.colspan - 1));\n    };\n    const getAnyBox = (warehouse, startCell, finishCell) => {\n      const startCoords = Warehouse.findItem(warehouse, startCell, eq);\n      const finishCoords = Warehouse.findItem(warehouse, finishCell, eq);\n      return startCoords.bind(sc => {\n        return finishCoords.map(fc => {\n          return getBounds(sc, fc);\n        });\n      });\n    };\n    const getBox$1 = (warehouse, startCell, finishCell) => {\n      return getAnyBox(warehouse, startCell, finishCell).bind(bounds => {\n        return isRectangular(warehouse, bounds);\n      });\n    };\n\n    const getBox = (table, first, last) => {\n      const warehouse = getWarehouse(table);\n      return getBox$1(warehouse, first, last);\n    };\n    const getWarehouse = Warehouse.fromTable;\n\n    const before = (marker, element) => {\n      const parent$1 = parent(marker);\n      parent$1.each(v => {\n        v.dom.insertBefore(element.dom, marker.dom);\n      });\n    };\n    const after$1 = (marker, element) => {\n      const sibling = nextSibling(marker);\n      sibling.fold(() => {\n        const parent$1 = parent(marker);\n        parent$1.each(v => {\n          append$1(v, element);\n        });\n      }, v => {\n        before(v, element);\n      });\n    };\n    const prepend = (parent, element) => {\n      const firstChild$1 = firstChild(parent);\n      firstChild$1.fold(() => {\n        append$1(parent, element);\n      }, v => {\n        parent.dom.insertBefore(element.dom, v.dom);\n      });\n    };\n    const append$1 = (parent, element) => {\n      parent.dom.appendChild(element.dom);\n    };\n    const wrap = (element, wrapper) => {\n      before(element, wrapper);\n      append$1(wrapper, element);\n    };\n\n    const after = (marker, elements) => {\n      each(elements, (x, i) => {\n        const e = i === 0 ? marker : elements[i - 1];\n        after$1(e, x);\n      });\n    };\n    const append = (parent, elements) => {\n      each(elements, x => {\n        append$1(parent, x);\n      });\n    };\n\n    const remove = element => {\n      const dom = element.dom;\n      if (dom.parentNode !== null) {\n        dom.parentNode.removeChild(dom);\n      }\n    };\n    const unwrap = wrapper => {\n      const children = children$3(wrapper);\n      if (children.length > 0) {\n        after(wrapper, children);\n      }\n      remove(wrapper);\n    };\n\n    const NodeValue = (is, name) => {\n      const get = element => {\n        if (!is(element)) {\n          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');\n        }\n        return getOption(element).getOr('');\n      };\n      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();\n      const set = (element, value) => {\n        if (!is(element)) {\n          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');\n        }\n        element.dom.nodeValue = value;\n      };\n      return {\n        get,\n        getOption,\n        set\n      };\n    };\n\n    const api = NodeValue(isText, 'text');\n    const get = element => api.get(element);\n    const set = (element, value) => api.set(element, value);\n\n    var TagBoundaries = [\n      'body',\n      'p',\n      'div',\n      'article',\n      'aside',\n      'figcaption',\n      'figure',\n      'footer',\n      'header',\n      'nav',\n      'section',\n      'ol',\n      'ul',\n      'li',\n      'table',\n      'thead',\n      'tbody',\n      'tfoot',\n      'caption',\n      'tr',\n      'td',\n      'th',\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'blockquote',\n      'pre',\n      'address'\n    ];\n\n    var DomUniverse = () => {\n      const clone$1 = element => {\n        return SugarElement.fromDom(element.dom.cloneNode(false));\n      };\n      const document = element => documentOrOwner(element).dom;\n      const isBoundary = element => {\n        if (!isElement(element)) {\n          return false;\n        }\n        if (name(element) === 'body') {\n          return true;\n        }\n        return contains(TagBoundaries, name(element));\n      };\n      const isEmptyTag = element => {\n        if (!isElement(element)) {\n          return false;\n        }\n        return contains([\n          'br',\n          'img',\n          'hr',\n          'input'\n        ], name(element));\n      };\n      const isNonEditable = element => isElement(element) && get$2(element, 'contenteditable') === 'false';\n      const comparePosition = (element, other) => {\n        return element.dom.compareDocumentPosition(other.dom);\n      };\n      const copyAttributesTo = (source, destination) => {\n        const as = clone(source);\n        setAll(destination, as);\n      };\n      const isSpecial = element => {\n        const tag = name(element);\n        return contains([\n          'script',\n          'noscript',\n          'iframe',\n          'noframes',\n          'noembed',\n          'title',\n          'style',\n          'textarea',\n          'xmp'\n        ], tag);\n      };\n      const getLanguage = element => isElement(element) ? getOpt(element, 'lang') : Optional.none();\n      return {\n        up: constant({\n          selector: ancestor,\n          closest: closest,\n          predicate: ancestor$1,\n          all: parents\n        }),\n        down: constant({\n          selector: descendants,\n          predicate: descendants$1\n        }),\n        styles: constant({\n          get: get$1,\n          getRaw: getRaw,\n          set: set$1,\n          remove: remove$1\n        }),\n        attrs: constant({\n          get: get$2,\n          set: set$2,\n          remove: remove$2,\n          copyTo: copyAttributesTo\n        }),\n        insert: constant({\n          before: before,\n          after: after$1,\n          afterAll: after,\n          append: append$1,\n          appendAll: append,\n          prepend: prepend,\n          wrap: wrap\n        }),\n        remove: constant({\n          unwrap: unwrap,\n          remove: remove\n        }),\n        create: constant({\n          nu: SugarElement.fromTag,\n          clone: clone$1,\n          text: SugarElement.fromText\n        }),\n        query: constant({\n          comparePosition,\n          prevSibling: prevSibling,\n          nextSibling: nextSibling\n        }),\n        property: constant({\n          children: children$3,\n          name: name,\n          parent: parent,\n          document,\n          isText: isText,\n          isComment: isComment,\n          isElement: isElement,\n          isSpecial,\n          getLanguage,\n          getText: get,\n          setText: set,\n          isBoundary,\n          isEmptyTag,\n          isNonEditable\n        }),\n        eq: eq,\n        is: is$1\n      };\n    };\n\n    const all = (universe, look, elements, f) => {\n      const head = elements[0];\n      const tail = elements.slice(1);\n      return f(universe, look, head, tail);\n    };\n    const oneAll = (universe, look, elements) => {\n      return elements.length > 0 ? all(universe, look, elements, unsafeOne) : Optional.none();\n    };\n    const unsafeOne = (universe, look, head, tail) => {\n      const start = look(universe, head);\n      return foldr(tail, (b, a) => {\n        const current = look(universe, a);\n        return commonElement(universe, b, current);\n      }, start);\n    };\n    const commonElement = (universe, start, end) => {\n      return start.bind(s => {\n        return end.filter(curry(universe.eq, s));\n      });\n    };\n\n    const sharedOne$1 = oneAll;\n\n    const universe = DomUniverse();\n    const sharedOne = (look, elements) => {\n      return sharedOne$1(universe, (_universe, element) => {\n        return look(element);\n      }, elements);\n    };\n\n    const lookupTable = container => {\n      return ancestor(container, 'table');\n    };\n    const retrieve$1 = (container, selector) => {\n      const sels = descendants(container, selector);\n      return sels.length > 0 ? Optional.some(sels) : Optional.none();\n    };\n    const getEdges = (container, firstSelectedSelector, lastSelectedSelector) => {\n      return descendant(container, firstSelectedSelector).bind(first => {\n        return descendant(container, lastSelectedSelector).bind(last => {\n          return sharedOne(lookupTable, [\n            first,\n            last\n          ]).map(table => {\n            return {\n              first,\n              last,\n              table\n            };\n          });\n        });\n      });\n    };\n\n    const retrieve = (container, selector) => {\n      return retrieve$1(container, selector);\n    };\n    const retrieveBox = (container, firstSelectedSelector, lastSelectedSelector) => {\n      return getEdges(container, firstSelectedSelector, lastSelectedSelector).bind(edges => {\n        const isRoot = ancestor => {\n          return eq(container, ancestor);\n        };\n        const sectionSelector = 'thead,tfoot,tbody,table';\n        const firstAncestor = ancestor(edges.first, sectionSelector, isRoot);\n        const lastAncestor = ancestor(edges.last, sectionSelector, isRoot);\n        return firstAncestor.bind(fA => {\n          return lastAncestor.bind(lA => {\n            return eq(fA, lA) ? getBox(edges.table, edges.first, edges.last) : Optional.none();\n          });\n        });\n      });\n    };\n\n    const fromDom = nodes => map(nodes, SugarElement.fromDom);\n\n    const strSelected = 'data-mce-selected';\n    const strSelectedSelector = 'td[' + strSelected + '],th[' + strSelected + ']';\n    const strFirstSelected = 'data-mce-first-selected';\n    const strFirstSelectedSelector = 'td[' + strFirstSelected + '],th[' + strFirstSelected + ']';\n    const strLastSelected = 'data-mce-last-selected';\n    const strLastSelectedSelector = 'td[' + strLastSelected + '],th[' + strLastSelected + ']';\n    const ephemera = {\n      selected: strSelected,\n      selectedSelector: strSelectedSelector,\n      firstSelected: strFirstSelected,\n      firstSelectedSelector: strFirstSelectedSelector,\n      lastSelected: strLastSelected,\n      lastSelectedSelector: strLastSelectedSelector\n    };\n\n    const getSelectionCellFallback = element => table(element).bind(table => retrieve(table, ephemera.firstSelectedSelector)).fold(constant(element), cells => cells[0]);\n    const getSelectionFromSelector = selector => (initCell, isRoot) => {\n      const cellName = name(initCell);\n      const cell = cellName === 'col' || cellName === 'colgroup' ? getSelectionCellFallback(initCell) : initCell;\n      return closest(cell, selector, isRoot);\n    };\n    const getSelectionCellOrCaption = getSelectionFromSelector('th,td,caption');\n    const getSelectionCell = getSelectionFromSelector('th,td');\n    const getCellsFromSelection = editor => fromDom(editor.model.table.getSelectedCells());\n    const getRowsFromSelection = (selected, selector) => {\n      const cellOpt = getSelectionCell(selected);\n      const rowsOpt = cellOpt.bind(cell => table(cell)).map(table => rows(table));\n      return lift2(cellOpt, rowsOpt, (cell, rows) => filter(rows, row => exists(fromDom(row.dom.cells), rowCell => get$2(rowCell, selector) === '1' || eq(rowCell, cell)))).getOr([]);\n    };\n\n    const verticalAlignValues = [\n      {\n        text: 'None',\n        value: ''\n      },\n      {\n        text: 'Top',\n        value: 'top'\n      },\n      {\n        text: 'Middle',\n        value: 'middle'\n      },\n      {\n        text: 'Bottom',\n        value: 'bottom'\n      }\n    ];\n\n    const hexColour = value => ({ value });\n    const shorthandRegex = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;\n    const longformRegex = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i;\n    const isHexString = hex => shorthandRegex.test(hex) || longformRegex.test(hex);\n    const normalizeHex = hex => removeLeading(hex, '#').toUpperCase();\n    const fromString$1 = hex => isHexString(hex) ? Optional.some({ value: normalizeHex(hex) }) : Optional.none();\n    const toHex = component => {\n      const hex = component.toString(16);\n      return (hex.length === 1 ? '0' + hex : hex).toUpperCase();\n    };\n    const fromRgba = rgbaColour => {\n      const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);\n      return hexColour(value);\n    };\n\n    const rgbRegex = /^\\s*rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)\\s*$/i;\n    const rgbaRegex = /^\\s*rgba\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d?(?:\\.\\d+)?)\\s*\\)\\s*$/i;\n    const rgbaColour = (red, green, blue, alpha) => ({\n      red,\n      green,\n      blue,\n      alpha\n    });\n    const fromStringValues = (red, green, blue, alpha) => {\n      const r = parseInt(red, 10);\n      const g = parseInt(green, 10);\n      const b = parseInt(blue, 10);\n      const a = parseFloat(alpha);\n      return rgbaColour(r, g, b, a);\n    };\n    const fromString = rgbaString => {\n      if (rgbaString === 'transparent') {\n        return Optional.some(rgbaColour(0, 0, 0, 0));\n      }\n      const rgbMatch = rgbRegex.exec(rgbaString);\n      if (rgbMatch !== null) {\n        return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], '1'));\n      }\n      const rgbaMatch = rgbaRegex.exec(rgbaString);\n      if (rgbaMatch !== null) {\n        return Optional.some(fromStringValues(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], rgbaMatch[4]));\n      }\n      return Optional.none();\n    };\n\n    const anyToHex = color => fromString$1(color).orThunk(() => fromString(color).map(fromRgba)).getOrThunk(() => {\n      const canvas = document.createElement('canvas');\n      canvas.height = 1;\n      canvas.width = 1;\n      const canvasContext = canvas.getContext('2d');\n      canvasContext.clearRect(0, 0, canvas.width, canvas.height);\n      canvasContext.fillStyle = '#FFFFFF';\n      canvasContext.fillStyle = color;\n      canvasContext.fillRect(0, 0, 1, 1);\n      const rgba = canvasContext.getImageData(0, 0, 1, 1).data;\n      const r = rgba[0];\n      const g = rgba[1];\n      const b = rgba[2];\n      const a = rgba[3];\n      return fromRgba(rgbaColour(r, g, b, a));\n    });\n    const rgbaToHexString = color => fromString(color).map(fromRgba).map(h => '#' + h.value).getOr(color);\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const singleton = doRevoke => {\n      const subject = Cell(Optional.none());\n      const revoke = () => subject.get().each(doRevoke);\n      const clear = () => {\n        revoke();\n        subject.set(Optional.none());\n      };\n      const isSet = () => subject.get().isSome();\n      const get = () => subject.get();\n      const set = s => {\n        revoke();\n        subject.set(Optional.some(s));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const unbindable = () => singleton(s => s.unbind());\n\n    const onSetupToggle = (editor, formatName, formatValue) => {\n      return api => {\n        const boundCallback = unbindable();\n        const isNone = isEmpty(formatValue);\n        const init = () => {\n          const selectedCells = getCellsFromSelection(editor);\n          const checkNode = cell => editor.formatter.match(formatName, { value: formatValue }, cell.dom, isNone);\n          if (isNone) {\n            api.setActive(!exists(selectedCells, checkNode));\n            boundCallback.set(editor.formatter.formatChanged(formatName, match => api.setActive(!match), true));\n          } else {\n            api.setActive(forall(selectedCells, checkNode));\n            boundCallback.set(editor.formatter.formatChanged(formatName, api.setActive, false, { value: formatValue }));\n          }\n        };\n        editor.initialized ? init() : editor.on('init', init);\n        return boundCallback.clear;\n      };\n    };\n    const isListGroup = item => hasNonNullableKey(item, 'menu');\n    const buildListItems = items => map(items, item => {\n      const text = item.text || item.title || '';\n      if (isListGroup(item)) {\n        return {\n          text,\n          items: buildListItems(item.menu)\n        };\n      } else {\n        return {\n          text,\n          value: item.value\n        };\n      }\n    });\n    const buildMenuItems = (editor, items, format, onAction) => map(items, item => {\n      const text = item.text || item.title;\n      if (isListGroup(item)) {\n        return {\n          type: 'nestedmenuitem',\n          text,\n          getSubmenuItems: () => buildMenuItems(editor, item.menu, format, onAction)\n        };\n      } else {\n        return {\n          text,\n          type: 'togglemenuitem',\n          onAction: () => onAction(item.value),\n          onSetup: onSetupToggle(editor, format, item.value)\n        };\n      }\n    });\n    const applyTableCellStyle = (editor, style) => value => {\n      editor.execCommand('mceTableApplyCellStyle', false, { [style]: value });\n    };\n    const filterNoneItem = list => bind(list, item => {\n      if (isListGroup(item)) {\n        return [{\n            ...item,\n            menu: filterNoneItem(item.menu)\n          }];\n      } else {\n        return isNotEmpty(item.value) ? [item] : [];\n      }\n    });\n    const generateMenuItemsCallback = (editor, items, format, onAction) => callback => callback(buildMenuItems(editor, items, format, onAction));\n    const buildColorMenu = (editor, colorList, style) => {\n      const colorMap = map(colorList, entry => ({\n        text: entry.title,\n        value: '#' + anyToHex(entry.value).value,\n        type: 'choiceitem'\n      }));\n      return [{\n          type: 'fancymenuitem',\n          fancytype: 'colorswatch',\n          initData: {\n            colors: colorMap.length > 0 ? colorMap : undefined,\n            allowCustomColors: false\n          },\n          onAction: data => {\n            const value = data.value === 'remove' ? '' : data.value;\n            editor.execCommand('mceTableApplyCellStyle', false, { [style]: value });\n          }\n        }];\n    };\n    const changeRowHeader = editor => () => {\n      const currentType = editor.queryCommandValue('mceTableRowType');\n      const newType = currentType === 'header' ? 'body' : 'header';\n      editor.execCommand('mceTableRowType', false, { type: newType });\n    };\n    const changeColumnHeader = editor => () => {\n      const currentType = editor.queryCommandValue('mceTableColType');\n      const newType = currentType === 'th' ? 'td' : 'th';\n      editor.execCommand('mceTableColType', false, { type: newType });\n    };\n\n    const getClassList$1 = editor => {\n      const classes = buildListItems(getCellClassList(editor));\n      if (classes.length > 0) {\n        return Optional.some({\n          name: 'class',\n          type: 'listbox',\n          label: 'Class',\n          items: classes\n        });\n      }\n      return Optional.none();\n    };\n    const children = [\n      {\n        name: 'width',\n        type: 'input',\n        label: 'Width'\n      },\n      {\n        name: 'height',\n        type: 'input',\n        label: 'Height'\n      },\n      {\n        name: 'celltype',\n        type: 'listbox',\n        label: 'Cell type',\n        items: [\n          {\n            text: 'Cell',\n            value: 'td'\n          },\n          {\n            text: 'Header cell',\n            value: 'th'\n          }\n        ]\n      },\n      {\n        name: 'scope',\n        type: 'listbox',\n        label: 'Scope',\n        items: [\n          {\n            text: 'None',\n            value: ''\n          },\n          {\n            text: 'Row',\n            value: 'row'\n          },\n          {\n            text: 'Column',\n            value: 'col'\n          },\n          {\n            text: 'Row group',\n            value: 'rowgroup'\n          },\n          {\n            text: 'Column group',\n            value: 'colgroup'\n          }\n        ]\n      },\n      {\n        name: 'halign',\n        type: 'listbox',\n        label: 'Horizontal align',\n        items: [\n          {\n            text: 'None',\n            value: ''\n          },\n          {\n            text: 'Left',\n            value: 'left'\n          },\n          {\n            text: 'Center',\n            value: 'center'\n          },\n          {\n            text: 'Right',\n            value: 'right'\n          }\n        ]\n      },\n      {\n        name: 'valign',\n        type: 'listbox',\n        label: 'Vertical align',\n        items: verticalAlignValues\n      }\n    ];\n    const getItems$2 = editor => children.concat(getClassList$1(editor).toArray());\n\n    const getAdvancedTab = (editor, dialogName) => {\n      const emptyBorderStyle = [{\n          text: 'Select...',\n          value: ''\n        }];\n      const advTabItems = [\n        {\n          name: 'borderstyle',\n          type: 'listbox',\n          label: 'Border style',\n          items: emptyBorderStyle.concat(buildListItems(getTableBorderStyles(editor)))\n        },\n        {\n          name: 'bordercolor',\n          type: 'colorinput',\n          label: 'Border color'\n        },\n        {\n          name: 'backgroundcolor',\n          type: 'colorinput',\n          label: 'Background color'\n        }\n      ];\n      const borderWidth = {\n        name: 'borderwidth',\n        type: 'input',\n        label: 'Border width'\n      };\n      const items = dialogName === 'cell' ? [borderWidth].concat(advTabItems) : advTabItems;\n      return {\n        title: 'Advanced',\n        name: 'advanced',\n        items\n      };\n    };\n\n    const normal = (editor, element) => {\n      const dom = editor.dom;\n      const setAttrib = (attr, value) => {\n        dom.setAttrib(element, attr, value);\n      };\n      const setStyle = (prop, value) => {\n        dom.setStyle(element, prop, value);\n      };\n      const setFormat = (formatName, value) => {\n        if (value === '') {\n          editor.formatter.remove(formatName, { value: null }, element, true);\n        } else {\n          editor.formatter.apply(formatName, { value }, element);\n        }\n      };\n      return {\n        setAttrib,\n        setStyle,\n        setFormat\n      };\n    };\n    const DomModifier = { normal };\n\n    const isHeaderCell = isTag('th');\n    const getRowHeaderType = (isHeaderRow, isHeaderCells) => {\n      if (isHeaderRow && isHeaderCells) {\n        return 'sectionCells';\n      } else if (isHeaderRow) {\n        return 'section';\n      } else {\n        return 'cells';\n      }\n    };\n    const getRowType$1 = row => {\n      const isHeaderRow = row.section === 'thead';\n      const isHeaderCells = is(findCommonCellType(row.cells), 'th');\n      if (row.section === 'tfoot') {\n        return { type: 'footer' };\n      } else if (isHeaderRow || isHeaderCells) {\n        return {\n          type: 'header',\n          subType: getRowHeaderType(isHeaderRow, isHeaderCells)\n        };\n      } else {\n        return { type: 'body' };\n      }\n    };\n    const findCommonCellType = cells => {\n      const headerCells = filter(cells, cell => isHeaderCell(cell.element));\n      if (headerCells.length === 0) {\n        return Optional.some('td');\n      } else if (headerCells.length === cells.length) {\n        return Optional.some('th');\n      } else {\n        return Optional.none();\n      }\n    };\n    const findCommonRowType = rows => {\n      const rowTypes = map(rows, row => getRowType$1(row).type);\n      const hasHeader = contains(rowTypes, 'header');\n      const hasFooter = contains(rowTypes, 'footer');\n      if (!hasHeader && !hasFooter) {\n        return Optional.some('body');\n      } else {\n        const hasBody = contains(rowTypes, 'body');\n        if (hasHeader && !hasBody && !hasFooter) {\n          return Optional.some('header');\n        } else if (!hasHeader && !hasBody && hasFooter) {\n          return Optional.some('footer');\n        } else {\n          return Optional.none();\n        }\n      }\n    };\n\n    const cached = f => {\n      let called = false;\n      let r;\n      return (...args) => {\n        if (!called) {\n          called = true;\n          r = f.apply(null, args);\n        }\n        return r;\n      };\n    };\n\n    const findInWarehouse = (warehouse, element) => findMap(warehouse.all, r => find(r.cells, e => eq(element, e.element)));\n    const extractCells = (warehouse, target, predicate) => {\n      const details = map(target.selection, cell$1 => {\n        return cell(cell$1).bind(lc => findInWarehouse(warehouse, lc)).filter(predicate);\n      });\n      const cells = cat(details);\n      return someIf(cells.length > 0, cells);\n    };\n    const onMergable = (_warehouse, target) => target.mergable;\n    const onUnmergable = (_warehouse, target) => target.unmergable;\n    const onCells = (warehouse, target) => extractCells(warehouse, target, always);\n    const isUnlockedTableCell = (warehouse, cell) => findInWarehouse(warehouse, cell).exists(detail => !detail.isLocked);\n    const allUnlocked = (warehouse, cells) => forall(cells, cell => isUnlockedTableCell(warehouse, cell));\n    const onUnlockedMergable = (warehouse, target) => onMergable(warehouse, target).filter(mergeable => allUnlocked(warehouse, mergeable.cells));\n    const onUnlockedUnmergable = (warehouse, target) => onUnmergable(warehouse, target).filter(cells => allUnlocked(warehouse, cells));\n\n    const generate = cases => {\n      if (!isArray(cases)) {\n        throw new Error('cases must be an array');\n      }\n      if (cases.length === 0) {\n        throw new Error('there must be at least one case');\n      }\n      const constructors = [];\n      const adt = {};\n      each(cases, (acase, count) => {\n        const keys$1 = keys(acase);\n        if (keys$1.length !== 1) {\n          throw new Error('one and only one name per case');\n        }\n        const key = keys$1[0];\n        const value = acase[key];\n        if (adt[key] !== undefined) {\n          throw new Error('duplicate key detected:' + key);\n        } else if (key === 'cata') {\n          throw new Error('cannot have a case named cata (sorry)');\n        } else if (!isArray(value)) {\n          throw new Error('case arguments must be an array');\n        }\n        constructors.push(key);\n        adt[key] = (...args) => {\n          const argLength = args.length;\n          if (argLength !== value.length) {\n            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);\n          }\n          const match = branches => {\n            const branchKeys = keys(branches);\n            if (constructors.length !== branchKeys.length) {\n              throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\\nActual: ' + branchKeys.join(','));\n            }\n            const allReqd = forall(constructors, reqKey => {\n              return contains(branchKeys, reqKey);\n            });\n            if (!allReqd) {\n              throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\\nRequired: ' + constructors.join(', '));\n            }\n            return branches[key].apply(null, args);\n          };\n          return {\n            fold: (...foldArgs) => {\n              if (foldArgs.length !== cases.length) {\n                throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + foldArgs.length);\n              }\n              const target = foldArgs[count];\n              return target.apply(null, args);\n            },\n            match,\n            log: label => {\n              console.log(label, {\n                constructors,\n                constructor: key,\n                params: args\n              });\n            }\n          };\n        };\n      });\n      return adt;\n    };\n    const Adt = { generate };\n\n    const adt = Adt.generate([\n      { none: [] },\n      { only: ['index'] },\n      {\n        left: [\n          'index',\n          'next'\n        ]\n      },\n      {\n        middle: [\n          'prev',\n          'index',\n          'next'\n        ]\n      },\n      {\n        right: [\n          'prev',\n          'index'\n        ]\n      }\n    ]);\n    ({ ...adt });\n\n    const opGetRowsType = (table, target) => {\n      const house = Warehouse.fromTable(table);\n      const details = onCells(house, target);\n      return details.bind(selectedCells => {\n        const lastSelectedCell = selectedCells[selectedCells.length - 1];\n        const minRowRange = selectedCells[0].row;\n        const maxRowRange = lastSelectedCell.row + lastSelectedCell.rowspan;\n        const selectedRows = house.all.slice(minRowRange, maxRowRange);\n        return findCommonRowType(selectedRows);\n      }).getOr('');\n    };\n    const getRowsType = opGetRowsType;\n\n    const rgbToHex = value => startsWith(value, 'rgb') ? rgbaToHexString(value) : value;\n    const extractAdvancedStyles = elm => {\n      const element = SugarElement.fromDom(elm);\n      return {\n        borderwidth: getRaw(element, 'border-width').getOr(''),\n        borderstyle: getRaw(element, 'border-style').getOr(''),\n        bordercolor: getRaw(element, 'border-color').map(rgbToHex).getOr(''),\n        backgroundcolor: getRaw(element, 'background-color').map(rgbToHex).getOr('')\n      };\n    };\n    const getSharedValues = data => {\n      const baseData = data[0];\n      const comparisonData = data.slice(1);\n      each(comparisonData, items => {\n        each(keys(baseData), key => {\n          each$1(items, (itemValue, itemKey) => {\n            const comparisonValue = baseData[key];\n            if (comparisonValue !== '' && key === itemKey) {\n              if (comparisonValue !== itemValue) {\n                baseData[key] = '';\n              }\n            }\n          });\n        });\n      });\n      return baseData;\n    };\n    const getAlignment = (formats, formatName, editor, elm) => find(formats, name => !isUndefined(editor.formatter.matchNode(elm, formatName + name))).getOr('');\n    const getHAlignment = curry(getAlignment, [\n      'left',\n      'center',\n      'right'\n    ], 'align');\n    const getVAlignment = curry(getAlignment, [\n      'top',\n      'middle',\n      'bottom'\n    ], 'valign');\n    const extractDataFromSettings = (editor, hasAdvTableTab) => {\n      const style = getDefaultStyles(editor);\n      const attrs = getDefaultAttributes(editor);\n      const extractAdvancedStyleData = () => ({\n        borderstyle: get$4(style, 'border-style').getOr(''),\n        bordercolor: rgbToHex(get$4(style, 'border-color').getOr('')),\n        backgroundcolor: rgbToHex(get$4(style, 'background-color').getOr(''))\n      });\n      const defaultData = {\n        height: '',\n        width: '100%',\n        cellspacing: '',\n        cellpadding: '',\n        caption: false,\n        class: '',\n        align: '',\n        border: ''\n      };\n      const getBorder = () => {\n        const borderWidth = style['border-width'];\n        if (shouldStyleWithCss(editor) && borderWidth) {\n          return { border: borderWidth };\n        }\n        return get$4(attrs, 'border').fold(() => ({}), border => ({ border }));\n      };\n      const advStyle = hasAdvTableTab ? extractAdvancedStyleData() : {};\n      const getCellPaddingCellSpacing = () => {\n        const spacing = get$4(style, 'border-spacing').or(get$4(attrs, 'cellspacing')).fold(() => ({}), cellspacing => ({ cellspacing }));\n        const padding = get$4(style, 'border-padding').or(get$4(attrs, 'cellpadding')).fold(() => ({}), cellpadding => ({ cellpadding }));\n        return {\n          ...spacing,\n          ...padding\n        };\n      };\n      const data = {\n        ...defaultData,\n        ...style,\n        ...attrs,\n        ...advStyle,\n        ...getBorder(),\n        ...getCellPaddingCellSpacing()\n      };\n      return data;\n    };\n    const getRowType = elm => table(SugarElement.fromDom(elm)).map(table => {\n      const target = { selection: fromDom(elm.cells) };\n      return getRowsType(table, target);\n    }).getOr('');\n    const extractDataFromTableElement = (editor, elm, hasAdvTableTab) => {\n      const getBorder = (dom, elm) => {\n        const optBorderWidth = getRaw(SugarElement.fromDom(elm), 'border-width');\n        if (shouldStyleWithCss(editor) && optBorderWidth.isSome()) {\n          return optBorderWidth.getOr('');\n        }\n        return dom.getAttrib(elm, 'border') || getTDTHOverallStyle(editor.dom, elm, 'border-width') || getTDTHOverallStyle(editor.dom, elm, 'border') || '';\n      };\n      const dom = editor.dom;\n      const cellspacing = shouldStyleWithCss(editor) ? dom.getStyle(elm, 'border-spacing') || dom.getAttrib(elm, 'cellspacing') : dom.getAttrib(elm, 'cellspacing') || dom.getStyle(elm, 'border-spacing');\n      const cellpadding = shouldStyleWithCss(editor) ? getTDTHOverallStyle(dom, elm, 'padding') || dom.getAttrib(elm, 'cellpadding') : dom.getAttrib(elm, 'cellpadding') || getTDTHOverallStyle(dom, elm, 'padding');\n      return {\n        width: dom.getStyle(elm, 'width') || dom.getAttrib(elm, 'width'),\n        height: dom.getStyle(elm, 'height') || dom.getAttrib(elm, 'height'),\n        cellspacing: cellspacing !== null && cellspacing !== void 0 ? cellspacing : '',\n        cellpadding: cellpadding !== null && cellpadding !== void 0 ? cellpadding : '',\n        border: getBorder(dom, elm),\n        caption: !!dom.select('caption', elm)[0],\n        class: dom.getAttrib(elm, 'class', ''),\n        align: getHAlignment(editor, elm),\n        ...hasAdvTableTab ? extractAdvancedStyles(elm) : {}\n      };\n    };\n    const extractDataFromRowElement = (editor, elm, hasAdvancedRowTab) => {\n      const dom = editor.dom;\n      return {\n        height: dom.getStyle(elm, 'height') || dom.getAttrib(elm, 'height'),\n        class: dom.getAttrib(elm, 'class', ''),\n        type: getRowType(elm),\n        align: getHAlignment(editor, elm),\n        ...hasAdvancedRowTab ? extractAdvancedStyles(elm) : {}\n      };\n    };\n    const extractDataFromCellElement = (editor, cell, hasAdvancedCellTab, column) => {\n      const dom = editor.dom;\n      const colElm = column.getOr(cell);\n      const getStyle = (element, style) => dom.getStyle(element, style) || dom.getAttrib(element, style);\n      return {\n        width: getStyle(colElm, 'width'),\n        height: getStyle(cell, 'height'),\n        scope: dom.getAttrib(cell, 'scope'),\n        celltype: getNodeName(cell),\n        class: dom.getAttrib(cell, 'class', ''),\n        halign: getHAlignment(editor, cell),\n        valign: getVAlignment(editor, cell),\n        ...hasAdvancedCellTab ? extractAdvancedStyles(cell) : {}\n      };\n    };\n\n    const getSelectedCells = (table, cells) => {\n      const warehouse = Warehouse.fromTable(table);\n      const allCells = Warehouse.justCells(warehouse);\n      const filtered = filter(allCells, cellA => exists(cells, cellB => eq(cellA.element, cellB)));\n      return map(filtered, cell => ({\n        element: cell.element.dom,\n        column: Warehouse.getColumnAt(warehouse, cell.column).map(col => col.element.dom)\n      }));\n    };\n    const updateSimpleProps$1 = (modifier, colModifier, data, shouldUpdate) => {\n      if (shouldUpdate('scope')) {\n        modifier.setAttrib('scope', data.scope);\n      }\n      if (shouldUpdate('class')) {\n        modifier.setAttrib('class', data.class);\n      }\n      if (shouldUpdate('height')) {\n        modifier.setStyle('height', addPxSuffix(data.height));\n      }\n      if (shouldUpdate('width')) {\n        colModifier.setStyle('width', addPxSuffix(data.width));\n      }\n    };\n    const updateAdvancedProps$1 = (modifier, data, shouldUpdate) => {\n      if (shouldUpdate('backgroundcolor')) {\n        modifier.setFormat('tablecellbackgroundcolor', data.backgroundcolor);\n      }\n      if (shouldUpdate('bordercolor')) {\n        modifier.setFormat('tablecellbordercolor', data.bordercolor);\n      }\n      if (shouldUpdate('borderstyle')) {\n        modifier.setFormat('tablecellborderstyle', data.borderstyle);\n      }\n      if (shouldUpdate('borderwidth')) {\n        modifier.setFormat('tablecellborderwidth', addPxSuffix(data.borderwidth));\n      }\n    };\n    const applyStyleData$1 = (editor, cells, data, wasChanged) => {\n      const isSingleCell = cells.length === 1;\n      each(cells, item => {\n        const cellElm = item.element;\n        const shouldOverrideCurrentValue = isSingleCell ? always : wasChanged;\n        const modifier = DomModifier.normal(editor, cellElm);\n        const colModifier = item.column.map(col => DomModifier.normal(editor, col)).getOr(modifier);\n        updateSimpleProps$1(modifier, colModifier, data, shouldOverrideCurrentValue);\n        if (hasAdvancedCellTab(editor)) {\n          updateAdvancedProps$1(modifier, data, shouldOverrideCurrentValue);\n        }\n        if (wasChanged('halign')) {\n          setAlign(editor, cellElm, data.halign);\n        }\n        if (wasChanged('valign')) {\n          setVAlign(editor, cellElm, data.valign);\n        }\n      });\n    };\n    const applyStructureData$1 = (editor, data) => {\n      editor.execCommand('mceTableCellType', false, {\n        type: data.celltype,\n        no_events: true\n      });\n    };\n    const applyCellData = (editor, cells, oldData, data) => {\n      const modifiedData = filter$1(data, (value, key) => oldData[key] !== value);\n      if (size(modifiedData) > 0 && cells.length >= 1) {\n        table(cells[0]).each(table => {\n          const selectedCells = getSelectedCells(table, cells);\n          const styleModified = size(filter$1(modifiedData, (_value, key) => key !== 'scope' && key !== 'celltype')) > 0;\n          const structureModified = has(modifiedData, 'celltype');\n          if (styleModified || has(modifiedData, 'scope')) {\n            applyStyleData$1(editor, selectedCells, data, curry(has, modifiedData));\n          }\n          if (structureModified) {\n            applyStructureData$1(editor, data);\n          }\n          fireTableModified(editor, table.dom, {\n            structure: structureModified,\n            style: styleModified\n          });\n        });\n      }\n    };\n    const onSubmitCellForm = (editor, cells, oldData, api) => {\n      const data = api.getData();\n      api.close();\n      editor.undoManager.transact(() => {\n        applyCellData(editor, cells, oldData, data);\n        editor.focus();\n      });\n    };\n    const getData$1 = (editor, cells) => {\n      const cellsData = table(cells[0]).map(table => map(getSelectedCells(table, cells), item => extractDataFromCellElement(editor, item.element, hasAdvancedCellTab(editor), item.column)));\n      return getSharedValues(cellsData.getOrDie());\n    };\n    const open$2 = editor => {\n      const cells = getCellsFromSelection(editor);\n      if (cells.length === 0) {\n        return;\n      }\n      const data = getData$1(editor, cells);\n      const dialogTabPanel = {\n        type: 'tabpanel',\n        tabs: [\n          {\n            title: 'General',\n            name: 'general',\n            items: getItems$2(editor)\n          },\n          getAdvancedTab(editor, 'cell')\n        ]\n      };\n      const dialogPanel = {\n        type: 'panel',\n        items: [{\n            type: 'grid',\n            columns: 2,\n            items: getItems$2(editor)\n          }]\n      };\n      editor.windowManager.open({\n        title: 'Cell Properties',\n        size: 'normal',\n        body: hasAdvancedCellTab(editor) ? dialogTabPanel : dialogPanel,\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData: data,\n        onSubmit: curry(onSubmitCellForm, editor, cells, data)\n      });\n    };\n\n    const getClassList = editor => {\n      const classes = buildListItems(getRowClassList(editor));\n      if (classes.length > 0) {\n        return Optional.some({\n          name: 'class',\n          type: 'listbox',\n          label: 'Class',\n          items: classes\n        });\n      }\n      return Optional.none();\n    };\n    const formChildren = [\n      {\n        type: 'listbox',\n        name: 'type',\n        label: 'Row type',\n        items: [\n          {\n            text: 'Header',\n            value: 'header'\n          },\n          {\n            text: 'Body',\n            value: 'body'\n          },\n          {\n            text: 'Footer',\n            value: 'footer'\n          }\n        ]\n      },\n      {\n        type: 'listbox',\n        name: 'align',\n        label: 'Alignment',\n        items: [\n          {\n            text: 'None',\n            value: ''\n          },\n          {\n            text: 'Left',\n            value: 'left'\n          },\n          {\n            text: 'Center',\n            value: 'center'\n          },\n          {\n            text: 'Right',\n            value: 'right'\n          }\n        ]\n      },\n      {\n        label: 'Height',\n        name: 'height',\n        type: 'input'\n      }\n    ];\n    const getItems$1 = editor => formChildren.concat(getClassList(editor).toArray());\n\n    const updateSimpleProps = (modifier, data, shouldUpdate) => {\n      if (shouldUpdate('class')) {\n        modifier.setAttrib('class', data.class);\n      }\n      if (shouldUpdate('height')) {\n        modifier.setStyle('height', addPxSuffix(data.height));\n      }\n    };\n    const updateAdvancedProps = (modifier, data, shouldUpdate) => {\n      if (shouldUpdate('backgroundcolor')) {\n        modifier.setStyle('background-color', data.backgroundcolor);\n      }\n      if (shouldUpdate('bordercolor')) {\n        modifier.setStyle('border-color', data.bordercolor);\n      }\n      if (shouldUpdate('borderstyle')) {\n        modifier.setStyle('border-style', data.borderstyle);\n      }\n    };\n    const applyStyleData = (editor, rows, data, wasChanged) => {\n      const isSingleRow = rows.length === 1;\n      const shouldOverrideCurrentValue = isSingleRow ? always : wasChanged;\n      each(rows, rowElm => {\n        const modifier = DomModifier.normal(editor, rowElm);\n        updateSimpleProps(modifier, data, shouldOverrideCurrentValue);\n        if (hasAdvancedRowTab(editor)) {\n          updateAdvancedProps(modifier, data, shouldOverrideCurrentValue);\n        }\n        if (wasChanged('align')) {\n          setAlign(editor, rowElm, data.align);\n        }\n      });\n    };\n    const applyStructureData = (editor, data) => {\n      editor.execCommand('mceTableRowType', false, {\n        type: data.type,\n        no_events: true\n      });\n    };\n    const applyRowData = (editor, rows, oldData, data) => {\n      const modifiedData = filter$1(data, (value, key) => oldData[key] !== value);\n      if (size(modifiedData) > 0) {\n        const typeModified = has(modifiedData, 'type');\n        const styleModified = typeModified ? size(modifiedData) > 1 : true;\n        if (styleModified) {\n          applyStyleData(editor, rows, data, curry(has, modifiedData));\n        }\n        if (typeModified) {\n          applyStructureData(editor, data);\n        }\n        table(SugarElement.fromDom(rows[0])).each(table => fireTableModified(editor, table.dom, {\n          structure: typeModified,\n          style: styleModified\n        }));\n      }\n    };\n    const onSubmitRowForm = (editor, rows, oldData, api) => {\n      const data = api.getData();\n      api.close();\n      editor.undoManager.transact(() => {\n        applyRowData(editor, rows, oldData, data);\n        editor.focus();\n      });\n    };\n    const open$1 = editor => {\n      const rows = getRowsFromSelection(getSelectionStart(editor), ephemera.selected);\n      if (rows.length === 0) {\n        return;\n      }\n      const rowsData = map(rows, rowElm => extractDataFromRowElement(editor, rowElm.dom, hasAdvancedRowTab(editor)));\n      const data = getSharedValues(rowsData);\n      const dialogTabPanel = {\n        type: 'tabpanel',\n        tabs: [\n          {\n            title: 'General',\n            name: 'general',\n            items: getItems$1(editor)\n          },\n          getAdvancedTab(editor, 'row')\n        ]\n      };\n      const dialogPanel = {\n        type: 'panel',\n        items: [{\n            type: 'grid',\n            columns: 2,\n            items: getItems$1(editor)\n          }]\n      };\n      editor.windowManager.open({\n        title: 'Row Properties',\n        size: 'normal',\n        body: hasAdvancedRowTab(editor) ? dialogTabPanel : dialogPanel,\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData: data,\n        onSubmit: curry(onSubmitRowForm, editor, map(rows, r => r.dom), data)\n      });\n    };\n\n    const getItems = (editor, classes, insertNewTable) => {\n      const rowColCountItems = !insertNewTable ? [] : [\n        {\n          type: 'input',\n          name: 'cols',\n          label: 'Cols',\n          inputMode: 'numeric'\n        },\n        {\n          type: 'input',\n          name: 'rows',\n          label: 'Rows',\n          inputMode: 'numeric'\n        }\n      ];\n      const alwaysItems = [\n        {\n          type: 'input',\n          name: 'width',\n          label: 'Width'\n        },\n        {\n          type: 'input',\n          name: 'height',\n          label: 'Height'\n        }\n      ];\n      const appearanceItems = hasAppearanceOptions(editor) ? [\n        {\n          type: 'input',\n          name: 'cellspacing',\n          label: 'Cell spacing',\n          inputMode: 'numeric'\n        },\n        {\n          type: 'input',\n          name: 'cellpadding',\n          label: 'Cell padding',\n          inputMode: 'numeric'\n        },\n        {\n          type: 'input',\n          name: 'border',\n          label: 'Border width'\n        },\n        {\n          type: 'label',\n          label: 'Caption',\n          items: [{\n              type: 'checkbox',\n              name: 'caption',\n              label: 'Show caption'\n            }]\n        }\n      ] : [];\n      const alignmentItem = [{\n          type: 'listbox',\n          name: 'align',\n          label: 'Alignment',\n          items: [\n            {\n              text: 'None',\n              value: ''\n            },\n            {\n              text: 'Left',\n              value: 'left'\n            },\n            {\n              text: 'Center',\n              value: 'center'\n            },\n            {\n              text: 'Right',\n              value: 'right'\n            }\n          ]\n        }];\n      const classListItem = classes.length > 0 ? [{\n          type: 'listbox',\n          name: 'class',\n          label: 'Class',\n          items: classes\n        }] : [];\n      return rowColCountItems.concat(alwaysItems).concat(appearanceItems).concat(alignmentItem).concat(classListItem);\n    };\n\n    const styleTDTH = (dom, elm, name, value) => {\n      if (elm.tagName === 'TD' || elm.tagName === 'TH') {\n        if (isString(name) && isNonNullable(value)) {\n          dom.setStyle(elm, name, value);\n        } else {\n          dom.setStyles(elm, name);\n        }\n      } else {\n        if (elm.children) {\n          for (let i = 0; i < elm.children.length; i++) {\n            styleTDTH(dom, elm.children[i], name, value);\n          }\n        }\n      }\n    };\n    const applyDataToElement = (editor, tableElm, data) => {\n      const dom = editor.dom;\n      const attrs = {};\n      const styles = {};\n      if (!isUndefined(data.class)) {\n        attrs.class = data.class;\n      }\n      styles.height = addPxSuffix(data.height);\n      if (shouldStyleWithCss(editor)) {\n        styles.width = addPxSuffix(data.width);\n      } else if (dom.getAttrib(tableElm, 'width')) {\n        attrs.width = removePxSuffix(data.width);\n      }\n      if (shouldStyleWithCss(editor)) {\n        styles['border-width'] = addPxSuffix(data.border);\n        styles['border-spacing'] = addPxSuffix(data.cellspacing);\n      } else {\n        attrs.border = data.border;\n        attrs.cellpadding = data.cellpadding;\n        attrs.cellspacing = data.cellspacing;\n      }\n      if (shouldStyleWithCss(editor) && tableElm.children) {\n        for (let i = 0; i < tableElm.children.length; i++) {\n          styleTDTH(dom, tableElm.children[i], {\n            'border-width': addPxSuffix(data.border),\n            'padding': addPxSuffix(data.cellpadding)\n          });\n          if (hasAdvancedTableTab(editor)) {\n            styleTDTH(dom, tableElm.children[i], { 'border-color': data.bordercolor });\n          }\n        }\n      }\n      if (hasAdvancedTableTab(editor)) {\n        const advData = data;\n        styles['background-color'] = advData.backgroundcolor;\n        styles['border-color'] = advData.bordercolor;\n        styles['border-style'] = advData.borderstyle;\n      }\n      attrs.style = dom.serializeStyle({\n        ...getDefaultStyles(editor),\n        ...styles\n      });\n      dom.setAttribs(tableElm, {\n        ...getDefaultAttributes(editor),\n        ...attrs\n      });\n    };\n    const onSubmitTableForm = (editor, tableElm, oldData, api) => {\n      const dom = editor.dom;\n      const data = api.getData();\n      const modifiedData = filter$1(data, (value, key) => oldData[key] !== value);\n      api.close();\n      if (data.class === '') {\n        delete data.class;\n      }\n      editor.undoManager.transact(() => {\n        if (!tableElm) {\n          const cols = toInt(data.cols).getOr(1);\n          const rows = toInt(data.rows).getOr(1);\n          editor.execCommand('mceInsertTable', false, {\n            rows,\n            columns: cols\n          });\n          tableElm = getSelectionCell(getSelectionStart(editor), getIsRoot(editor)).bind(cell => table(cell, getIsRoot(editor))).map(table => table.dom).getOrDie();\n        }\n        if (size(modifiedData) > 0) {\n          applyDataToElement(editor, tableElm, data);\n          const captionElm = dom.select('caption', tableElm)[0];\n          if (captionElm && !data.caption || !captionElm && data.caption) {\n            editor.execCommand('mceTableToggleCaption');\n          }\n          setAlign(editor, tableElm, data.align);\n        }\n        editor.focus();\n        editor.addVisual();\n        if (size(modifiedData) > 0) {\n          const captionModified = has(modifiedData, 'caption');\n          const styleModified = captionModified ? size(modifiedData) > 1 : true;\n          fireTableModified(editor, tableElm, {\n            structure: captionModified,\n            style: styleModified\n          });\n        }\n      });\n    };\n    const open = (editor, insertNewTable) => {\n      const dom = editor.dom;\n      let tableElm;\n      let data = extractDataFromSettings(editor, hasAdvancedTableTab(editor));\n      if (insertNewTable) {\n        data.cols = '1';\n        data.rows = '1';\n        if (hasAdvancedTableTab(editor)) {\n          data.borderstyle = '';\n          data.bordercolor = '';\n          data.backgroundcolor = '';\n        }\n      } else {\n        tableElm = dom.getParent(editor.selection.getStart(), 'table', editor.getBody());\n        if (tableElm) {\n          data = extractDataFromTableElement(editor, tableElm, hasAdvancedTableTab(editor));\n        } else {\n          if (hasAdvancedTableTab(editor)) {\n            data.borderstyle = '';\n            data.bordercolor = '';\n            data.backgroundcolor = '';\n          }\n        }\n      }\n      const classes = buildListItems(getTableClassList(editor));\n      if (classes.length > 0) {\n        if (data.class) {\n          data.class = data.class.replace(/\\s*mce\\-item\\-table\\s*/g, '');\n        }\n      }\n      const generalPanel = {\n        type: 'grid',\n        columns: 2,\n        items: getItems(editor, classes, insertNewTable)\n      };\n      const nonAdvancedForm = () => ({\n        type: 'panel',\n        items: [generalPanel]\n      });\n      const advancedForm = () => ({\n        type: 'tabpanel',\n        tabs: [\n          {\n            title: 'General',\n            name: 'general',\n            items: [generalPanel]\n          },\n          getAdvancedTab(editor, 'table')\n        ]\n      });\n      const dialogBody = hasAdvancedTableTab(editor) ? advancedForm() : nonAdvancedForm();\n      editor.windowManager.open({\n        title: 'Table Properties',\n        size: 'normal',\n        body: dialogBody,\n        onSubmit: curry(onSubmitTableForm, editor, tableElm, data),\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData: data\n      });\n    };\n\n    const registerCommands = editor => {\n      each$1({\n        mceTableProps: curry(open, editor, false),\n        mceTableRowProps: curry(open$1, editor),\n        mceTableCellProps: curry(open$2, editor)\n      }, (func, name) => editor.addCommand(name, () => func()));\n      editor.addCommand('mceInsertTableDialog', _ui => {\n        open(editor, true);\n      });\n    };\n\n    const child = (scope, selector) => child$1(scope, selector).isSome();\n\n    const selection = identity;\n    const unmergable = selectedCells => {\n      const hasSpan = (elem, type) => getOpt(elem, type).exists(span => parseInt(span, 10) > 1);\n      const hasRowOrColSpan = elem => hasSpan(elem, 'rowspan') || hasSpan(elem, 'colspan');\n      return selectedCells.length > 0 && forall(selectedCells, hasRowOrColSpan) ? Optional.some(selectedCells) : Optional.none();\n    };\n    const mergable = (table, selectedCells, ephemera) => {\n      if (selectedCells.length <= 1) {\n        return Optional.none();\n      } else {\n        return retrieveBox(table, ephemera.firstSelectedSelector, ephemera.lastSelectedSelector).map(bounds => ({\n          bounds,\n          cells: selectedCells\n        }));\n      }\n    };\n\n    const noMenu = cell => ({\n      element: cell,\n      mergable: Optional.none(),\n      unmergable: Optional.none(),\n      selection: [cell]\n    });\n    const forMenu = (selectedCells, table, cell) => ({\n      element: cell,\n      mergable: mergable(table, selectedCells, ephemera),\n      unmergable: unmergable(selectedCells),\n      selection: selection(selectedCells)\n    });\n\n    const getSelectionTargets = editor => {\n      const targets = Cell(Optional.none());\n      const changeHandlers = Cell([]);\n      let selectionDetails = Optional.none();\n      const isCaption = isTag('caption');\n      const isDisabledForSelection = key => selectionDetails.forall(details => !details[key]);\n      const getStart = () => getSelectionCellOrCaption(getSelectionStart(editor), getIsRoot(editor));\n      const getEnd = () => getSelectionCellOrCaption(getSelectionEnd(editor), getIsRoot(editor));\n      const findTargets = () => getStart().bind(startCellOrCaption => flatten(lift2(table(startCellOrCaption), getEnd().bind(table), (startTable, endTable) => {\n        if (eq(startTable, endTable)) {\n          if (isCaption(startCellOrCaption)) {\n            return Optional.some(noMenu(startCellOrCaption));\n          } else {\n            return Optional.some(forMenu(getCellsFromSelection(editor), startTable, startCellOrCaption));\n          }\n        }\n        return Optional.none();\n      })));\n      const getExtractedDetails = targets => {\n        const tableOpt = table(targets.element);\n        return tableOpt.map(table => {\n          const warehouse = Warehouse.fromTable(table);\n          const selectedCells = onCells(warehouse, targets).getOr([]);\n          const locked = foldl(selectedCells, (acc, cell) => {\n            if (cell.isLocked) {\n              acc.onAny = true;\n              if (cell.column === 0) {\n                acc.onFirst = true;\n              } else if (cell.column + cell.colspan >= warehouse.grid.columns) {\n                acc.onLast = true;\n              }\n            }\n            return acc;\n          }, {\n            onAny: false,\n            onFirst: false,\n            onLast: false\n          });\n          return {\n            mergeable: onUnlockedMergable(warehouse, targets).isSome(),\n            unmergeable: onUnlockedUnmergable(warehouse, targets).isSome(),\n            locked\n          };\n        });\n      };\n      const resetTargets = () => {\n        targets.set(cached(findTargets)());\n        selectionDetails = targets.get().bind(getExtractedDetails);\n        each(changeHandlers.get(), call);\n      };\n      const setupHandler = handler => {\n        handler();\n        changeHandlers.set(changeHandlers.get().concat([handler]));\n        return () => {\n          changeHandlers.set(filter(changeHandlers.get(), h => h !== handler));\n        };\n      };\n      const onSetup = (api, isDisabled) => setupHandler(() => targets.get().fold(() => {\n        api.setEnabled(false);\n      }, targets => {\n        api.setEnabled(!isDisabled(targets));\n      }));\n      const onSetupWithToggle = (api, isDisabled, isActive) => setupHandler(() => targets.get().fold(() => {\n        api.setEnabled(false);\n        api.setActive(false);\n      }, targets => {\n        api.setEnabled(!isDisabled(targets));\n        api.setActive(isActive(targets));\n      }));\n      const isDisabledFromLocked = lockedDisable => selectionDetails.exists(details => details.locked[lockedDisable]);\n      const onSetupTable = api => onSetup(api, _ => false);\n      const onSetupCellOrRow = api => onSetup(api, targets => isCaption(targets.element));\n      const onSetupColumn = lockedDisable => api => onSetup(api, targets => isCaption(targets.element) || isDisabledFromLocked(lockedDisable));\n      const onSetupPasteable = getClipboardData => api => onSetup(api, targets => isCaption(targets.element) || getClipboardData().isNone());\n      const onSetupPasteableColumn = (getClipboardData, lockedDisable) => api => onSetup(api, targets => isCaption(targets.element) || getClipboardData().isNone() || isDisabledFromLocked(lockedDisable));\n      const onSetupMergeable = api => onSetup(api, _targets => isDisabledForSelection('mergeable'));\n      const onSetupUnmergeable = api => onSetup(api, _targets => isDisabledForSelection('unmergeable'));\n      const onSetupTableWithCaption = api => {\n        return onSetupWithToggle(api, never, targets => {\n          const tableOpt = table(targets.element, getIsRoot(editor));\n          return tableOpt.exists(table => child(table, 'caption'));\n        });\n      };\n      const onSetupTableHeaders = (command, headerType) => api => {\n        return onSetupWithToggle(api, targets => isCaption(targets.element), () => editor.queryCommandValue(command) === headerType);\n      };\n      const onSetupTableRowHeaders = onSetupTableHeaders('mceTableRowType', 'header');\n      const onSetupTableColumnHeaders = onSetupTableHeaders('mceTableColType', 'th');\n      editor.on('NodeChange ExecCommand TableSelectorChange', resetTargets);\n      return {\n        onSetupTable,\n        onSetupCellOrRow,\n        onSetupColumn,\n        onSetupPasteable,\n        onSetupPasteableColumn,\n        onSetupMergeable,\n        onSetupUnmergeable,\n        resetTargets,\n        onSetupTableWithCaption,\n        onSetupTableRowHeaders,\n        onSetupTableColumnHeaders,\n        targets: targets.get\n      };\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.FakeClipboard');\n\n    const tableTypeBase = 'x-tinymce/dom-table-';\n    const tableTypeRow = tableTypeBase + 'rows';\n    const tableTypeColumn = tableTypeBase + 'columns';\n    const getData = type => {\n      var _a;\n      const items = (_a = global.read()) !== null && _a !== void 0 ? _a : [];\n      return findMap(items, item => Optional.from(item.getType(type)));\n    };\n    const getRows = () => getData(tableTypeRow);\n    const getColumns = () => getData(tableTypeColumn);\n\n    const addButtons = (editor, selectionTargets) => {\n      editor.ui.registry.addMenuButton('table', {\n        tooltip: 'Table',\n        icon: 'table',\n        fetch: callback => callback('inserttable | cell row column | advtablesort | tableprops deletetable')\n      });\n      const cmd = command => () => editor.execCommand(command);\n      const addButtonIfRegistered = (name, spec) => {\n        if (editor.queryCommandSupported(spec.command)) {\n          editor.ui.registry.addButton(name, {\n            ...spec,\n            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)\n          });\n        }\n      };\n      const addToggleButtonIfRegistered = (name, spec) => {\n        if (editor.queryCommandSupported(spec.command)) {\n          editor.ui.registry.addToggleButton(name, {\n            ...spec,\n            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)\n          });\n        }\n      };\n      addButtonIfRegistered('tableprops', {\n        tooltip: 'Table properties',\n        command: 'mceTableProps',\n        icon: 'table',\n        onSetup: selectionTargets.onSetupTable\n      });\n      addButtonIfRegistered('tabledelete', {\n        tooltip: 'Delete table',\n        command: 'mceTableDelete',\n        icon: 'table-delete-table',\n        onSetup: selectionTargets.onSetupTable\n      });\n      addButtonIfRegistered('tablecellprops', {\n        tooltip: 'Cell properties',\n        command: 'mceTableCellProps',\n        icon: 'table-cell-properties',\n        onSetup: selectionTargets.onSetupCellOrRow\n      });\n      addButtonIfRegistered('tablemergecells', {\n        tooltip: 'Merge cells',\n        command: 'mceTableMergeCells',\n        icon: 'table-merge-cells',\n        onSetup: selectionTargets.onSetupMergeable\n      });\n      addButtonIfRegistered('tablesplitcells', {\n        tooltip: 'Split cell',\n        command: 'mceTableSplitCells',\n        icon: 'table-split-cells',\n        onSetup: selectionTargets.onSetupUnmergeable\n      });\n      addButtonIfRegistered('tableinsertrowbefore', {\n        tooltip: 'Insert row before',\n        command: 'mceTableInsertRowBefore',\n        icon: 'table-insert-row-above',\n        onSetup: selectionTargets.onSetupCellOrRow\n      });\n      addButtonIfRegistered('tableinsertrowafter', {\n        tooltip: 'Insert row after',\n        command: 'mceTableInsertRowAfter',\n        icon: 'table-insert-row-after',\n        onSetup: selectionTargets.onSetupCellOrRow\n      });\n      addButtonIfRegistered('tabledeleterow', {\n        tooltip: 'Delete row',\n        command: 'mceTableDeleteRow',\n        icon: 'table-delete-row',\n        onSetup: selectionTargets.onSetupCellOrRow\n      });\n      addButtonIfRegistered('tablerowprops', {\n        tooltip: 'Row properties',\n        command: 'mceTableRowProps',\n        icon: 'table-row-properties',\n        onSetup: selectionTargets.onSetupCellOrRow\n      });\n      addButtonIfRegistered('tableinsertcolbefore', {\n        tooltip: 'Insert column before',\n        command: 'mceTableInsertColBefore',\n        icon: 'table-insert-column-before',\n        onSetup: selectionTargets.onSetupColumn('onFirst')\n      });\n      addButtonIfRegistered('tableinsertcolafter', {\n        tooltip: 'Insert column after',\n        command: 'mceTableInsertColAfter',\n        icon: 'table-insert-column-after',\n        onSetup: selectionTargets.onSetupColumn('onLast')\n      });\n      addButtonIfRegistered('tabledeletecol', {\n        tooltip: 'Delete column',\n        command: 'mceTableDeleteCol',\n        icon: 'table-delete-column',\n        onSetup: selectionTargets.onSetupColumn('onAny')\n      });\n      addButtonIfRegistered('tablecutrow', {\n        tooltip: 'Cut row',\n        command: 'mceTableCutRow',\n        icon: 'cut-row',\n        onSetup: selectionTargets.onSetupCellOrRow\n      });\n      addButtonIfRegistered('tablecopyrow', {\n        tooltip: 'Copy row',\n        command: 'mceTableCopyRow',\n        icon: 'duplicate-row',\n        onSetup: selectionTargets.onSetupCellOrRow\n      });\n      addButtonIfRegistered('tablepasterowbefore', {\n        tooltip: 'Paste row before',\n        command: 'mceTablePasteRowBefore',\n        icon: 'paste-row-before',\n        onSetup: selectionTargets.onSetupPasteable(getRows)\n      });\n      addButtonIfRegistered('tablepasterowafter', {\n        tooltip: 'Paste row after',\n        command: 'mceTablePasteRowAfter',\n        icon: 'paste-row-after',\n        onSetup: selectionTargets.onSetupPasteable(getRows)\n      });\n      addButtonIfRegistered('tablecutcol', {\n        tooltip: 'Cut column',\n        command: 'mceTableCutCol',\n        icon: 'cut-column',\n        onSetup: selectionTargets.onSetupColumn('onAny')\n      });\n      addButtonIfRegistered('tablecopycol', {\n        tooltip: 'Copy column',\n        command: 'mceTableCopyCol',\n        icon: 'duplicate-column',\n        onSetup: selectionTargets.onSetupColumn('onAny')\n      });\n      addButtonIfRegistered('tablepastecolbefore', {\n        tooltip: 'Paste column before',\n        command: 'mceTablePasteColBefore',\n        icon: 'paste-column-before',\n        onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onFirst')\n      });\n      addButtonIfRegistered('tablepastecolafter', {\n        tooltip: 'Paste column after',\n        command: 'mceTablePasteColAfter',\n        icon: 'paste-column-after',\n        onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onLast')\n      });\n      addButtonIfRegistered('tableinsertdialog', {\n        tooltip: 'Insert table',\n        command: 'mceInsertTableDialog',\n        icon: 'table'\n      });\n      const tableClassList = filterNoneItem(getTableClassList(editor));\n      if (tableClassList.length !== 0 && editor.queryCommandSupported('mceTableToggleClass')) {\n        editor.ui.registry.addMenuButton('tableclass', {\n          icon: 'table-classes',\n          tooltip: 'Table styles',\n          fetch: generateMenuItemsCallback(editor, tableClassList, 'tableclass', value => editor.execCommand('mceTableToggleClass', false, value)),\n          onSetup: selectionTargets.onSetupTable\n        });\n      }\n      const tableCellClassList = filterNoneItem(getCellClassList(editor));\n      if (tableCellClassList.length !== 0 && editor.queryCommandSupported('mceTableCellToggleClass')) {\n        editor.ui.registry.addMenuButton('tablecellclass', {\n          icon: 'table-cell-classes',\n          tooltip: 'Cell styles',\n          fetch: generateMenuItemsCallback(editor, tableCellClassList, 'tablecellclass', value => editor.execCommand('mceTableCellToggleClass', false, value)),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n      }\n      if (editor.queryCommandSupported('mceTableApplyCellStyle')) {\n        editor.ui.registry.addMenuButton('tablecellvalign', {\n          icon: 'vertical-align',\n          tooltip: 'Vertical align',\n          fetch: generateMenuItemsCallback(editor, verticalAlignValues, 'tablecellverticalalign', applyTableCellStyle(editor, 'vertical-align')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addMenuButton('tablecellborderwidth', {\n          icon: 'border-width',\n          tooltip: 'Border width',\n          fetch: generateMenuItemsCallback(editor, getTableBorderWidths(editor), 'tablecellborderwidth', applyTableCellStyle(editor, 'border-width')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addMenuButton('tablecellborderstyle', {\n          icon: 'border-style',\n          tooltip: 'Border style',\n          fetch: generateMenuItemsCallback(editor, getTableBorderStyles(editor), 'tablecellborderstyle', applyTableCellStyle(editor, 'border-style')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addMenuButton('tablecellbackgroundcolor', {\n          icon: 'cell-background-color',\n          tooltip: 'Background color',\n          fetch: callback => callback(buildColorMenu(editor, getTableBackgroundColorMap(editor), 'background-color')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addMenuButton('tablecellbordercolor', {\n          icon: 'cell-border-color',\n          tooltip: 'Border color',\n          fetch: callback => callback(buildColorMenu(editor, getTableBorderColorMap(editor), 'border-color')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n      }\n      addToggleButtonIfRegistered('tablecaption', {\n        tooltip: 'Table caption',\n        icon: 'table-caption',\n        command: 'mceTableToggleCaption',\n        onSetup: selectionTargets.onSetupTableWithCaption\n      });\n      addToggleButtonIfRegistered('tablerowheader', {\n        tooltip: 'Row header',\n        icon: 'table-top-header',\n        command: 'mceTableRowType',\n        onAction: changeRowHeader(editor),\n        onSetup: selectionTargets.onSetupTableRowHeaders\n      });\n      addToggleButtonIfRegistered('tablecolheader', {\n        tooltip: 'Column header',\n        icon: 'table-left-header',\n        command: 'mceTableColType',\n        onAction: changeColumnHeader(editor),\n        onSetup: selectionTargets.onSetupTableColumnHeaders\n      });\n    };\n    const addToolbars = editor => {\n      const isTable = table => editor.dom.is(table, 'table') && editor.getBody().contains(table);\n      const toolbar = getToolbar(editor);\n      if (toolbar.length > 0) {\n        editor.ui.registry.addContextToolbar('table', {\n          predicate: isTable,\n          items: toolbar,\n          scope: 'node',\n          position: 'node'\n        });\n      }\n    };\n\n    const addMenuItems = (editor, selectionTargets) => {\n      const cmd = command => () => editor.execCommand(command);\n      const addMenuIfRegistered = (name, spec) => {\n        if (editor.queryCommandSupported(spec.command)) {\n          editor.ui.registry.addMenuItem(name, {\n            ...spec,\n            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)\n          });\n          return true;\n        } else {\n          return false;\n        }\n      };\n      const addToggleMenuIfRegistered = (name, spec) => {\n        if (editor.queryCommandSupported(spec.command)) {\n          editor.ui.registry.addToggleMenuItem(name, {\n            ...spec,\n            onAction: isFunction(spec.onAction) ? spec.onAction : cmd(spec.command)\n          });\n        }\n      };\n      const insertTableAction = data => {\n        editor.execCommand('mceInsertTable', false, {\n          rows: data.numRows,\n          columns: data.numColumns\n        });\n      };\n      const hasRowMenuItems = [\n        addMenuIfRegistered('tableinsertrowbefore', {\n          text: 'Insert row before',\n          icon: 'table-insert-row-above',\n          command: 'mceTableInsertRowBefore',\n          onSetup: selectionTargets.onSetupCellOrRow\n        }),\n        addMenuIfRegistered('tableinsertrowafter', {\n          text: 'Insert row after',\n          icon: 'table-insert-row-after',\n          command: 'mceTableInsertRowAfter',\n          onSetup: selectionTargets.onSetupCellOrRow\n        }),\n        addMenuIfRegistered('tabledeleterow', {\n          text: 'Delete row',\n          icon: 'table-delete-row',\n          command: 'mceTableDeleteRow',\n          onSetup: selectionTargets.onSetupCellOrRow\n        }),\n        addMenuIfRegistered('tablerowprops', {\n          text: 'Row properties',\n          icon: 'table-row-properties',\n          command: 'mceTableRowProps',\n          onSetup: selectionTargets.onSetupCellOrRow\n        }),\n        addMenuIfRegistered('tablecutrow', {\n          text: 'Cut row',\n          icon: 'cut-row',\n          command: 'mceTableCutRow',\n          onSetup: selectionTargets.onSetupCellOrRow\n        }),\n        addMenuIfRegistered('tablecopyrow', {\n          text: 'Copy row',\n          icon: 'duplicate-row',\n          command: 'mceTableCopyRow',\n          onSetup: selectionTargets.onSetupCellOrRow\n        }),\n        addMenuIfRegistered('tablepasterowbefore', {\n          text: 'Paste row before',\n          icon: 'paste-row-before',\n          command: 'mceTablePasteRowBefore',\n          onSetup: selectionTargets.onSetupPasteable(getRows)\n        }),\n        addMenuIfRegistered('tablepasterowafter', {\n          text: 'Paste row after',\n          icon: 'paste-row-after',\n          command: 'mceTablePasteRowAfter',\n          onSetup: selectionTargets.onSetupPasteable(getRows)\n        })\n      ];\n      const hasColumnMenuItems = [\n        addMenuIfRegistered('tableinsertcolumnbefore', {\n          text: 'Insert column before',\n          icon: 'table-insert-column-before',\n          command: 'mceTableInsertColBefore',\n          onSetup: selectionTargets.onSetupColumn('onFirst')\n        }),\n        addMenuIfRegistered('tableinsertcolumnafter', {\n          text: 'Insert column after',\n          icon: 'table-insert-column-after',\n          command: 'mceTableInsertColAfter',\n          onSetup: selectionTargets.onSetupColumn('onLast')\n        }),\n        addMenuIfRegistered('tabledeletecolumn', {\n          text: 'Delete column',\n          icon: 'table-delete-column',\n          command: 'mceTableDeleteCol',\n          onSetup: selectionTargets.onSetupColumn('onAny')\n        }),\n        addMenuIfRegistered('tablecutcolumn', {\n          text: 'Cut column',\n          icon: 'cut-column',\n          command: 'mceTableCutCol',\n          onSetup: selectionTargets.onSetupColumn('onAny')\n        }),\n        addMenuIfRegistered('tablecopycolumn', {\n          text: 'Copy column',\n          icon: 'duplicate-column',\n          command: 'mceTableCopyCol',\n          onSetup: selectionTargets.onSetupColumn('onAny')\n        }),\n        addMenuIfRegistered('tablepastecolumnbefore', {\n          text: 'Paste column before',\n          icon: 'paste-column-before',\n          command: 'mceTablePasteColBefore',\n          onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onFirst')\n        }),\n        addMenuIfRegistered('tablepastecolumnafter', {\n          text: 'Paste column after',\n          icon: 'paste-column-after',\n          command: 'mceTablePasteColAfter',\n          onSetup: selectionTargets.onSetupPasteableColumn(getColumns, 'onLast')\n        })\n      ];\n      const hasCellMenuItems = [\n        addMenuIfRegistered('tablecellprops', {\n          text: 'Cell properties',\n          icon: 'table-cell-properties',\n          command: 'mceTableCellProps',\n          onSetup: selectionTargets.onSetupCellOrRow\n        }),\n        addMenuIfRegistered('tablemergecells', {\n          text: 'Merge cells',\n          icon: 'table-merge-cells',\n          command: 'mceTableMergeCells',\n          onSetup: selectionTargets.onSetupMergeable\n        }),\n        addMenuIfRegistered('tablesplitcells', {\n          text: 'Split cell',\n          icon: 'table-split-cells',\n          command: 'mceTableSplitCells',\n          onSetup: selectionTargets.onSetupUnmergeable\n        })\n      ];\n      if (!hasTableGrid(editor)) {\n        editor.ui.registry.addMenuItem('inserttable', {\n          text: 'Table',\n          icon: 'table',\n          onAction: cmd('mceInsertTableDialog')\n        });\n      } else {\n        editor.ui.registry.addNestedMenuItem('inserttable', {\n          text: 'Table',\n          icon: 'table',\n          getSubmenuItems: () => [{\n              type: 'fancymenuitem',\n              fancytype: 'inserttable',\n              onAction: insertTableAction\n            }]\n        });\n      }\n      editor.ui.registry.addMenuItem('inserttabledialog', {\n        text: 'Insert table',\n        icon: 'table',\n        onAction: cmd('mceInsertTableDialog')\n      });\n      addMenuIfRegistered('tableprops', {\n        text: 'Table properties',\n        onSetup: selectionTargets.onSetupTable,\n        command: 'mceTableProps'\n      });\n      addMenuIfRegistered('deletetable', {\n        text: 'Delete table',\n        icon: 'table-delete-table',\n        onSetup: selectionTargets.onSetupTable,\n        command: 'mceTableDelete'\n      });\n      if (contains(hasRowMenuItems, true)) {\n        editor.ui.registry.addNestedMenuItem('row', {\n          type: 'nestedmenuitem',\n          text: 'Row',\n          getSubmenuItems: constant('tableinsertrowbefore tableinsertrowafter tabledeleterow tablerowprops | tablecutrow tablecopyrow tablepasterowbefore tablepasterowafter')\n        });\n      }\n      if (contains(hasColumnMenuItems, true)) {\n        editor.ui.registry.addNestedMenuItem('column', {\n          type: 'nestedmenuitem',\n          text: 'Column',\n          getSubmenuItems: constant('tableinsertcolumnbefore tableinsertcolumnafter tabledeletecolumn | tablecutcolumn tablecopycolumn tablepastecolumnbefore tablepastecolumnafter')\n        });\n      }\n      if (contains(hasCellMenuItems, true)) {\n        editor.ui.registry.addNestedMenuItem('cell', {\n          type: 'nestedmenuitem',\n          text: 'Cell',\n          getSubmenuItems: constant('tablecellprops tablemergecells tablesplitcells')\n        });\n      }\n      editor.ui.registry.addContextMenu('table', {\n        update: () => {\n          selectionTargets.resetTargets();\n          return selectionTargets.targets().fold(constant(''), targets => {\n            if (name(targets.element) === 'caption') {\n              return 'tableprops deletetable';\n            } else {\n              return 'cell row column | advtablesort | tableprops deletetable';\n            }\n          });\n        }\n      });\n      const tableClassList = filterNoneItem(getTableClassList(editor));\n      if (tableClassList.length !== 0 && editor.queryCommandSupported('mceTableToggleClass')) {\n        editor.ui.registry.addNestedMenuItem('tableclass', {\n          icon: 'table-classes',\n          text: 'Table styles',\n          getSubmenuItems: () => buildMenuItems(editor, tableClassList, 'tableclass', value => editor.execCommand('mceTableToggleClass', false, value)),\n          onSetup: selectionTargets.onSetupTable\n        });\n      }\n      const tableCellClassList = filterNoneItem(getCellClassList(editor));\n      if (tableCellClassList.length !== 0 && editor.queryCommandSupported('mceTableCellToggleClass')) {\n        editor.ui.registry.addNestedMenuItem('tablecellclass', {\n          icon: 'table-cell-classes',\n          text: 'Cell styles',\n          getSubmenuItems: () => buildMenuItems(editor, tableCellClassList, 'tablecellclass', value => editor.execCommand('mceTableCellToggleClass', false, value)),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n      }\n      if (editor.queryCommandSupported('mceTableApplyCellStyle')) {\n        editor.ui.registry.addNestedMenuItem('tablecellvalign', {\n          icon: 'vertical-align',\n          text: 'Vertical align',\n          getSubmenuItems: () => buildMenuItems(editor, verticalAlignValues, 'tablecellverticalalign', applyTableCellStyle(editor, 'vertical-align')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addNestedMenuItem('tablecellborderwidth', {\n          icon: 'border-width',\n          text: 'Border width',\n          getSubmenuItems: () => buildMenuItems(editor, getTableBorderWidths(editor), 'tablecellborderwidth', applyTableCellStyle(editor, 'border-width')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addNestedMenuItem('tablecellborderstyle', {\n          icon: 'border-style',\n          text: 'Border style',\n          getSubmenuItems: () => buildMenuItems(editor, getTableBorderStyles(editor), 'tablecellborderstyle', applyTableCellStyle(editor, 'border-style')),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addNestedMenuItem('tablecellbackgroundcolor', {\n          icon: 'cell-background-color',\n          text: 'Background color',\n          getSubmenuItems: () => buildColorMenu(editor, getTableBackgroundColorMap(editor), 'background-color'),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n        editor.ui.registry.addNestedMenuItem('tablecellbordercolor', {\n          icon: 'cell-border-color',\n          text: 'Border color',\n          getSubmenuItems: () => buildColorMenu(editor, getTableBorderColorMap(editor), 'border-color'),\n          onSetup: selectionTargets.onSetupCellOrRow\n        });\n      }\n      addToggleMenuIfRegistered('tablecaption', {\n        icon: 'table-caption',\n        text: 'Table caption',\n        command: 'mceTableToggleCaption',\n        onSetup: selectionTargets.onSetupTableWithCaption\n      });\n      addToggleMenuIfRegistered('tablerowheader', {\n        text: 'Row header',\n        icon: 'table-top-header',\n        command: 'mceTableRowType',\n        onAction: changeRowHeader(editor),\n        onSetup: selectionTargets.onSetupTableRowHeaders\n      });\n      addToggleMenuIfRegistered('tablecolheader', {\n        text: 'Column header',\n        icon: 'table-left-header',\n        command: 'mceTableColType',\n        onAction: changeColumnHeader(editor),\n        onSetup: selectionTargets.onSetupTableRowHeaders\n      });\n    };\n\n    const Plugin = editor => {\n      const selectionTargets = getSelectionTargets(editor);\n      register(editor);\n      registerCommands(editor);\n      addMenuItems(editor, selectionTargets);\n      addButtons(editor, selectionTargets);\n      addToolbars(editor);\n    };\n    var Plugin$1 = () => {\n      global$3.add('table', Plugin);\n    };\n\n    Plugin$1();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/template/index.js",
    "content": "// Exports the \"template\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/template')\n//   ES2015:\n//     import 'tinymce/plugins/template'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/template/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const isString = isType('string');\n    const isObject = isType('object');\n    const isArray = isType('array');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isArrayOf = (value, pred) => {\n      if (isArray(value)) {\n        for (let i = 0, len = value.length; i < len; ++i) {\n          if (!pred(value[i])) {\n            return false;\n          }\n        }\n        return true;\n      }\n      return false;\n    };\n\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    function curry(fn, ...initialArgs) {\n      return (...restArgs) => {\n        const all = initialArgs.concat(restArgs);\n        return fn.apply(null, all);\n      };\n    }\n    const never = constant(false);\n\n    const escape = text => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const option = name => editor => editor.options.get(name);\n    const register$2 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('template_cdate_classes', {\n        processor: 'string',\n        default: 'cdate'\n      });\n      registerOption('template_mdate_classes', {\n        processor: 'string',\n        default: 'mdate'\n      });\n      registerOption('template_selected_content_classes', {\n        processor: 'string',\n        default: 'selcontent'\n      });\n      registerOption('template_preview_replace_values', { processor: 'object' });\n      registerOption('template_replace_values', { processor: 'object' });\n      registerOption('templates', {\n        processor: value => isString(value) || isArrayOf(value, isObject) || isFunction(value),\n        default: []\n      });\n      registerOption('template_cdate_format', {\n        processor: 'string',\n        default: editor.translate('%Y-%m-%d')\n      });\n      registerOption('template_mdate_format', {\n        processor: 'string',\n        default: editor.translate('%Y-%m-%d')\n      });\n    };\n    const getCreationDateClasses = option('template_cdate_classes');\n    const getModificationDateClasses = option('template_mdate_classes');\n    const getSelectedContentClasses = option('template_selected_content_classes');\n    const getPreviewReplaceValues = option('template_preview_replace_values');\n    const getTemplateReplaceValues = option('template_replace_values');\n    const getTemplates = option('templates');\n    const getCdateFormat = option('template_cdate_format');\n    const getMdateFormat = option('template_mdate_format');\n    const getContentStyle = option('content_style');\n    const shouldUseContentCssCors = option('content_css_cors');\n    const getBodyClass = option('body_class');\n\n    const addZeros = (value, len) => {\n      value = '' + value;\n      if (value.length < len) {\n        for (let i = 0; i < len - value.length; i++) {\n          value = '0' + value;\n        }\n      }\n      return value;\n    };\n    const getDateTime = (editor, fmt, date = new Date()) => {\n      const daysShort = 'Sun Mon Tue Wed Thu Fri Sat Sun'.split(' ');\n      const daysLong = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday'.split(' ');\n      const monthsShort = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');\n      const monthsLong = 'January February March April May June July August September October November December'.split(' ');\n      fmt = fmt.replace('%D', '%m/%d/%Y');\n      fmt = fmt.replace('%r', '%I:%M:%S %p');\n      fmt = fmt.replace('%Y', '' + date.getFullYear());\n      fmt = fmt.replace('%y', '' + date.getYear());\n      fmt = fmt.replace('%m', addZeros(date.getMonth() + 1, 2));\n      fmt = fmt.replace('%d', addZeros(date.getDate(), 2));\n      fmt = fmt.replace('%H', '' + addZeros(date.getHours(), 2));\n      fmt = fmt.replace('%M', '' + addZeros(date.getMinutes(), 2));\n      fmt = fmt.replace('%S', '' + addZeros(date.getSeconds(), 2));\n      fmt = fmt.replace('%I', '' + ((date.getHours() + 11) % 12 + 1));\n      fmt = fmt.replace('%p', '' + (date.getHours() < 12 ? 'AM' : 'PM'));\n      fmt = fmt.replace('%B', '' + editor.translate(monthsLong[date.getMonth()]));\n      fmt = fmt.replace('%b', '' + editor.translate(monthsShort[date.getMonth()]));\n      fmt = fmt.replace('%A', '' + editor.translate(daysLong[date.getDay()]));\n      fmt = fmt.replace('%a', '' + editor.translate(daysShort[date.getDay()]));\n      fmt = fmt.replace('%%', '%');\n      return fmt;\n    };\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const exists = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n\n    const hasOwnProperty = Object.hasOwnProperty;\n    const get = (obj, key) => {\n      return has(obj, key) ? Optional.from(obj[key]) : Optional.none();\n    };\n    const has = (obj, key) => hasOwnProperty.call(obj, key);\n\n    const entitiesAttr = {\n      '\"': '&quot;',\n      '<': '&lt;',\n      '>': '&gt;',\n      '&': '&amp;',\n      '\\'': '&#039;'\n    };\n    const htmlEscape = html => html.replace(/[\"'<>&]/g, match => get(entitiesAttr, match).getOr(match));\n    const hasAnyClasses = (dom, n, classes) => exists(classes.split(/\\s+/), c => dom.hasClass(n, c));\n\n    const createTemplateList = (editor, callback) => {\n      return () => {\n        const templateList = getTemplates(editor);\n        if (isFunction(templateList)) {\n          templateList(callback);\n        } else if (isString(templateList)) {\n          fetch(templateList).then(res => {\n            if (res.ok) {\n              res.json().then(callback);\n            }\n          });\n        } else {\n          callback(templateList);\n        }\n      };\n    };\n    const replaceTemplateValues = (html, templateValues) => {\n      global$1.each(templateValues, (v, k) => {\n        if (isFunction(v)) {\n          v = v(k);\n        }\n        html = html.replace(new RegExp('\\\\{\\\\$' + escape(k) + '\\\\}', 'g'), v);\n      });\n      return html;\n    };\n    const replaceVals = (editor, scope) => {\n      const dom = editor.dom, vl = getTemplateReplaceValues(editor);\n      global$1.each(dom.select('*', scope), e => {\n        global$1.each(vl, (v, k) => {\n          if (dom.hasClass(e, k)) {\n            if (isFunction(v)) {\n              v(e);\n            }\n          }\n        });\n      });\n    };\n    const insertTemplate = (editor, _ui, html) => {\n      const dom = editor.dom;\n      const sel = editor.selection.getContent();\n      html = replaceTemplateValues(html, getTemplateReplaceValues(editor));\n      let el = dom.create('div', {}, html);\n      const n = dom.select('.mceTmpl', el);\n      if (n && n.length > 0) {\n        el = dom.create('div');\n        el.appendChild(n[0].cloneNode(true));\n      }\n      global$1.each(dom.select('*', el), n => {\n        if (hasAnyClasses(dom, n, getCreationDateClasses(editor))) {\n          n.innerHTML = getDateTime(editor, getCdateFormat(editor));\n        }\n        if (hasAnyClasses(dom, n, getModificationDateClasses(editor))) {\n          n.innerHTML = getDateTime(editor, getMdateFormat(editor));\n        }\n        if (hasAnyClasses(dom, n, getSelectedContentClasses(editor))) {\n          n.innerHTML = sel;\n        }\n      });\n      replaceVals(editor, el);\n      editor.execCommand('mceInsertContent', false, el.innerHTML);\n      editor.addVisual();\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.Env');\n\n    const getPreviewContent = (editor, html) => {\n      var _a;\n      if (html.indexOf('<html>') === -1) {\n        let contentCssEntries = '';\n        const contentStyle = (_a = getContentStyle(editor)) !== null && _a !== void 0 ? _a : '';\n        const cors = shouldUseContentCssCors(editor) ? ' crossorigin=\"anonymous\"' : '';\n        global$1.each(editor.contentCSS, url => {\n          contentCssEntries += '<link type=\"text/css\" rel=\"stylesheet\" href=\"' + editor.documentBaseURI.toAbsolute(url) + '\"' + cors + '>';\n        });\n        if (contentStyle) {\n          contentCssEntries += '<style type=\"text/css\">' + contentStyle + '</style>';\n        }\n        const bodyClass = getBodyClass(editor);\n        const encode = editor.dom.encode;\n        const isMetaKeyPressed = global.os.isMacOS() || global.os.isiOS() ? 'e.metaKey' : 'e.ctrlKey && !e.altKey';\n        const preventClicksOnLinksScript = '<script>' + 'document.addEventListener && document.addEventListener(\"click\", function(e) {' + 'for (var elm = e.target; elm; elm = elm.parentNode) {' + 'if (elm.nodeName === \"A\" && !(' + isMetaKeyPressed + ')) {' + 'e.preventDefault();' + '}' + '}' + '}, false);' + '</script> ';\n        const directionality = editor.getBody().dir;\n        const dirAttr = directionality ? ' dir=\"' + encode(directionality) + '\"' : '';\n        html = '<!DOCTYPE html>' + '<html>' + '<head>' + '<base href=\"' + encode(editor.documentBaseURI.getURI()) + '\">' + contentCssEntries + preventClicksOnLinksScript + '</head>' + '<body class=\"' + encode(bodyClass) + '\"' + dirAttr + '>' + html + '</body>' + '</html>';\n      }\n      return replaceTemplateValues(html, getPreviewReplaceValues(editor));\n    };\n    const open = (editor, templateList) => {\n      const createTemplates = () => {\n        if (!templateList || templateList.length === 0) {\n          const message = editor.translate('No templates defined.');\n          editor.notificationManager.open({\n            text: message,\n            type: 'info'\n          });\n          return Optional.none();\n        }\n        return Optional.from(global$1.map(templateList, (template, index) => {\n          const isUrlTemplate = t => t.url !== undefined;\n          return {\n            selected: index === 0,\n            text: template.title,\n            value: {\n              url: isUrlTemplate(template) ? Optional.from(template.url) : Optional.none(),\n              content: !isUrlTemplate(template) ? Optional.from(template.content) : Optional.none(),\n              description: template.description\n            }\n          };\n        }));\n      };\n      const createSelectBoxItems = templates => map(templates, t => ({\n        text: t.text,\n        value: t.text\n      }));\n      const findTemplate = (templates, templateTitle) => find(templates, t => t.text === templateTitle);\n      const loadFailedAlert = api => {\n        editor.windowManager.alert('Could not load the specified template.', () => api.focus('template'));\n      };\n      const getTemplateContent = t => t.value.url.fold(() => Promise.resolve(t.value.content.getOr('')), url => fetch(url).then(res => res.ok ? res.text() : Promise.reject()));\n      const onChange = (templates, updateDialog) => (api, change) => {\n        if (change.name === 'template') {\n          const newTemplateTitle = api.getData().template;\n          findTemplate(templates, newTemplateTitle).each(t => {\n            api.block('Loading...');\n            getTemplateContent(t).then(previewHtml => {\n              updateDialog(api, t, previewHtml);\n            }).catch(() => {\n              updateDialog(api, t, '');\n              api.setEnabled('save', false);\n              loadFailedAlert(api);\n            });\n          });\n        }\n      };\n      const onSubmit = templates => api => {\n        const data = api.getData();\n        findTemplate(templates, data.template).each(t => {\n          getTemplateContent(t).then(previewHtml => {\n            editor.execCommand('mceInsertTemplate', false, previewHtml);\n            api.close();\n          }).catch(() => {\n            api.setEnabled('save', false);\n            loadFailedAlert(api);\n          });\n        });\n      };\n      const openDialog = templates => {\n        const selectBoxItems = createSelectBoxItems(templates);\n        const buildDialogSpec = (bodyItems, initialData) => ({\n          title: 'Insert Template',\n          size: 'large',\n          body: {\n            type: 'panel',\n            items: bodyItems\n          },\n          initialData,\n          buttons: [\n            {\n              type: 'cancel',\n              name: 'cancel',\n              text: 'Cancel'\n            },\n            {\n              type: 'submit',\n              name: 'save',\n              text: 'Save',\n              primary: true\n            }\n          ],\n          onSubmit: onSubmit(templates),\n          onChange: onChange(templates, updateDialog)\n        });\n        const updateDialog = (dialogApi, template, previewHtml) => {\n          const content = getPreviewContent(editor, previewHtml);\n          const bodyItems = [\n            {\n              type: 'selectbox',\n              name: 'template',\n              label: 'Templates',\n              items: selectBoxItems\n            },\n            {\n              type: 'htmlpanel',\n              html: `<p aria-live=\"polite\">${ htmlEscape(template.value.description) }</p>`\n            },\n            {\n              label: 'Preview',\n              type: 'iframe',\n              name: 'preview',\n              sandboxed: false,\n              transparent: false\n            }\n          ];\n          const initialData = {\n            template: template.text,\n            preview: content\n          };\n          dialogApi.unblock();\n          dialogApi.redial(buildDialogSpec(bodyItems, initialData));\n          dialogApi.focus('template');\n        };\n        const dialogApi = editor.windowManager.open(buildDialogSpec([], {\n          template: '',\n          preview: ''\n        }));\n        dialogApi.block('Loading...');\n        getTemplateContent(templates[0]).then(previewHtml => {\n          updateDialog(dialogApi, templates[0], previewHtml);\n        }).catch(() => {\n          updateDialog(dialogApi, templates[0], '');\n          dialogApi.setEnabled('save', false);\n          loadFailedAlert(dialogApi);\n        });\n      };\n      const optTemplates = createTemplates();\n      optTemplates.each(openDialog);\n    };\n\n    const showDialog = editor => templates => {\n      open(editor, templates);\n    };\n    const register$1 = editor => {\n      editor.addCommand('mceInsertTemplate', curry(insertTemplate, editor));\n      editor.addCommand('mceTemplate', createTemplateList(editor, showDialog(editor)));\n    };\n\n    const setup = editor => {\n      editor.on('PreProcess', o => {\n        const dom = editor.dom, dateFormat = getMdateFormat(editor);\n        global$1.each(dom.select('div', o.node), e => {\n          if (dom.hasClass(e, 'mceTmpl')) {\n            global$1.each(dom.select('*', e), e => {\n              if (hasAnyClasses(dom, e, getModificationDateClasses(editor))) {\n                e.innerHTML = getDateTime(editor, dateFormat);\n              }\n            });\n            replaceVals(editor, e);\n          }\n        });\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceTemplate');\n      editor.ui.registry.addButton('template', {\n        icon: 'template',\n        tooltip: 'Insert template',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('template', {\n        icon: 'template',\n        text: 'Insert template...',\n        onAction\n      });\n    };\n\n    var Plugin = () => {\n      global$2.add('template', editor => {\n        register$2(editor);\n        register(editor);\n        register$1(editor);\n        setup(editor);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/visualblocks/index.js",
    "content": "// Exports the \"visualblocks\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/visualblocks')\n//   ES2015:\n//     import 'tinymce/plugins/visualblocks'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/visualblocks/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const fireVisualBlocks = (editor, state) => {\n      editor.dispatch('VisualBlocks', { state });\n    };\n\n    const toggleVisualBlocks = (editor, pluginUrl, enabledState) => {\n      const dom = editor.dom;\n      dom.toggleClass(editor.getBody(), 'mce-visualblocks');\n      enabledState.set(!enabledState.get());\n      fireVisualBlocks(editor, enabledState.get());\n    };\n\n    const register$2 = (editor, pluginUrl, enabledState) => {\n      editor.addCommand('mceVisualBlocks', () => {\n        toggleVisualBlocks(editor, pluginUrl, enabledState);\n      });\n    };\n\n    const option = name => editor => editor.options.get(name);\n    const register$1 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('visualblocks_default_state', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const isEnabledByDefault = option('visualblocks_default_state');\n\n    const setup = (editor, pluginUrl, enabledState) => {\n      editor.on('PreviewFormats AfterPreviewFormats', e => {\n        if (enabledState.get()) {\n          editor.dom.toggleClass(editor.getBody(), 'mce-visualblocks', e.type === 'afterpreviewformats');\n        }\n      });\n      editor.on('init', () => {\n        if (isEnabledByDefault(editor)) {\n          toggleVisualBlocks(editor, pluginUrl, enabledState);\n        }\n      });\n    };\n\n    const toggleActiveState = (editor, enabledState) => api => {\n      api.setActive(enabledState.get());\n      const editorEventCallback = e => api.setActive(e.state);\n      editor.on('VisualBlocks', editorEventCallback);\n      return () => editor.off('VisualBlocks', editorEventCallback);\n    };\n    const register = (editor, enabledState) => {\n      const onAction = () => editor.execCommand('mceVisualBlocks');\n      editor.ui.registry.addToggleButton('visualblocks', {\n        icon: 'visualblocks',\n        tooltip: 'Show blocks',\n        onAction,\n        onSetup: toggleActiveState(editor, enabledState)\n      });\n      editor.ui.registry.addToggleMenuItem('visualblocks', {\n        text: 'Show blocks',\n        icon: 'visualblocks',\n        onAction,\n        onSetup: toggleActiveState(editor, enabledState)\n      });\n    };\n\n    var Plugin = () => {\n      global.add('visualblocks', (editor, pluginUrl) => {\n        register$1(editor);\n        const enabledState = Cell(false);\n        register$2(editor, pluginUrl, enabledState);\n        register(editor, enabledState);\n        setup(editor, pluginUrl, enabledState);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/visualchars/index.js",
    "content": "// Exports the \"visualchars\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/visualchars')\n//   ES2015:\n//     import 'tinymce/plugins/visualchars'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/visualchars/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const get$2 = toggleState => {\n      const isEnabled = () => {\n        return toggleState.get();\n      };\n      return { isEnabled };\n    };\n\n    const fireVisualChars = (editor, state) => {\n      return editor.dispatch('VisualChars', { state });\n    };\n\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq = t => a => t === a;\n    const isString = isType$1('string');\n    const isNull = eq(null);\n    const isBoolean = isSimpleType('boolean');\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isNumber = isSimpleType('number');\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$1 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const filter = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n\n    const keys = Object.keys;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const TEXT = 3;\n\n    const type = element => element.dom.nodeType;\n    const value = element => element.dom.nodeValue;\n    const isType = t => element => type(element) === t;\n    const isText = isType(TEXT);\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const get$1 = (element, key) => {\n      const v = element.dom.getAttribute(key);\n      return v === null ? undefined : v;\n    };\n    const remove$3 = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n\n    const read = (element, attr) => {\n      const value = get$1(element, attr);\n      return value === undefined || value === '' ? [] : value.split(' ');\n    };\n    const add$2 = (element, attr, id) => {\n      const old = read(element, attr);\n      const nu = old.concat([id]);\n      set(element, attr, nu.join(' '));\n      return true;\n    };\n    const remove$2 = (element, attr, id) => {\n      const nu = filter(read(element, attr), v => v !== id);\n      if (nu.length > 0) {\n        set(element, attr, nu.join(' '));\n      } else {\n        remove$3(element, attr);\n      }\n      return false;\n    };\n\n    const supports = element => element.dom.classList !== undefined;\n    const get = element => read(element, 'class');\n    const add$1 = (element, clazz) => add$2(element, 'class', clazz);\n    const remove$1 = (element, clazz) => remove$2(element, 'class', clazz);\n\n    const add = (element, clazz) => {\n      if (supports(element)) {\n        element.dom.classList.add(clazz);\n      } else {\n        add$1(element, clazz);\n      }\n    };\n    const cleanClass = element => {\n      const classList = supports(element) ? element.dom.classList : get(element);\n      if (classList.length === 0) {\n        remove$3(element, 'class');\n      }\n    };\n    const remove = (element, clazz) => {\n      if (supports(element)) {\n        const classList = element.dom.classList;\n        classList.remove(clazz);\n      } else {\n        remove$1(element, clazz);\n      }\n      cleanClass(element);\n    };\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom(node);\n    };\n    const fromDom = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);\n    const SugarElement = {\n      fromHtml,\n      fromTag,\n      fromText,\n      fromDom,\n      fromPoint\n    };\n\n    const charMap = {\n      '\\xA0': 'nbsp',\n      '\\xAD': 'shy'\n    };\n    const charMapToRegExp = (charMap, global) => {\n      let regExp = '';\n      each(charMap, (_value, key) => {\n        regExp += key;\n      });\n      return new RegExp('[' + regExp + ']', global ? 'g' : '');\n    };\n    const charMapToSelector = charMap => {\n      let selector = '';\n      each(charMap, value => {\n        if (selector) {\n          selector += ',';\n        }\n        selector += 'span.mce-' + value;\n      });\n      return selector;\n    };\n    const regExp = charMapToRegExp(charMap);\n    const regExpGlobal = charMapToRegExp(charMap, true);\n    const selector = charMapToSelector(charMap);\n    const nbspClass = 'mce-nbsp';\n\n    const wrapCharWithSpan = value => '<span data-mce-bogus=\"1\" class=\"mce-' + charMap[value] + '\">' + value + '</span>';\n\n    const isMatch = n => {\n      const value$1 = value(n);\n      return isText(n) && isString(value$1) && regExp.test(value$1);\n    };\n    const filterDescendants = (scope, predicate) => {\n      let result = [];\n      const dom = scope.dom;\n      const children = map(dom.childNodes, SugarElement.fromDom);\n      each$1(children, x => {\n        if (predicate(x)) {\n          result = result.concat([x]);\n        }\n        result = result.concat(filterDescendants(x, predicate));\n      });\n      return result;\n    };\n    const findParentElm = (elm, rootElm) => {\n      while (elm.parentNode) {\n        if (elm.parentNode === rootElm) {\n          return rootElm;\n        }\n        elm = elm.parentNode;\n      }\n      return undefined;\n    };\n    const replaceWithSpans = text => text.replace(regExpGlobal, wrapCharWithSpan);\n\n    const isWrappedNbsp = node => node.nodeName.toLowerCase() === 'span' && node.classList.contains('mce-nbsp-wrap');\n    const show = (editor, rootElm) => {\n      const dom = editor.dom;\n      const nodeList = filterDescendants(SugarElement.fromDom(rootElm), isMatch);\n      each$1(nodeList, n => {\n        var _a;\n        const parent = n.dom.parentNode;\n        if (isWrappedNbsp(parent)) {\n          add(SugarElement.fromDom(parent), nbspClass);\n        } else {\n          const withSpans = replaceWithSpans(dom.encode((_a = value(n)) !== null && _a !== void 0 ? _a : ''));\n          const div = dom.create('div', {}, withSpans);\n          let node;\n          while (node = div.lastChild) {\n            dom.insertAfter(node, n.dom);\n          }\n          editor.dom.remove(n.dom);\n        }\n      });\n    };\n    const hide = (editor, rootElm) => {\n      const nodeList = editor.dom.select(selector, rootElm);\n      each$1(nodeList, node => {\n        if (isWrappedNbsp(node)) {\n          remove(SugarElement.fromDom(node), nbspClass);\n        } else {\n          editor.dom.remove(node, true);\n        }\n      });\n    };\n    const toggle = editor => {\n      const body = editor.getBody();\n      const bookmark = editor.selection.getBookmark();\n      let parentNode = findParentElm(editor.selection.getNode(), body);\n      parentNode = parentNode !== undefined ? parentNode : body;\n      hide(editor, parentNode);\n      show(editor, parentNode);\n      editor.selection.moveToBookmark(bookmark);\n    };\n\n    const applyVisualChars = (editor, toggleState) => {\n      fireVisualChars(editor, toggleState.get());\n      const body = editor.getBody();\n      if (toggleState.get() === true) {\n        show(editor, body);\n      } else {\n        hide(editor, body);\n      }\n    };\n    const toggleVisualChars = (editor, toggleState) => {\n      toggleState.set(!toggleState.get());\n      const bookmark = editor.selection.getBookmark();\n      applyVisualChars(editor, toggleState);\n      editor.selection.moveToBookmark(bookmark);\n    };\n\n    const register$2 = (editor, toggleState) => {\n      editor.addCommand('mceVisualChars', () => {\n        toggleVisualChars(editor, toggleState);\n      });\n    };\n\n    const option = name => editor => editor.options.get(name);\n    const register$1 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('visualchars_default_state', {\n        processor: 'boolean',\n        default: false\n      });\n    };\n    const isEnabledByDefault = option('visualchars_default_state');\n\n    const setup$1 = (editor, toggleState) => {\n      editor.on('init', () => {\n        applyVisualChars(editor, toggleState);\n      });\n    };\n\n    const first = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        if (isNull(timer)) {\n          timer = setTimeout(() => {\n            timer = null;\n            fn.apply(null, args);\n          }, rate);\n        }\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    const setup = (editor, toggleState) => {\n      const debouncedToggle = first(() => {\n        toggle(editor);\n      }, 300);\n      editor.on('keydown', e => {\n        if (toggleState.get() === true) {\n          e.keyCode === 13 ? toggle(editor) : debouncedToggle.throttle();\n        }\n      });\n      editor.on('remove', debouncedToggle.cancel);\n    };\n\n    const toggleActiveState = (editor, enabledStated) => api => {\n      api.setActive(enabledStated.get());\n      const editorEventCallback = e => api.setActive(e.state);\n      editor.on('VisualChars', editorEventCallback);\n      return () => editor.off('VisualChars', editorEventCallback);\n    };\n    const register = (editor, toggleState) => {\n      const onAction = () => editor.execCommand('mceVisualChars');\n      editor.ui.registry.addToggleButton('visualchars', {\n        tooltip: 'Show invisible characters',\n        icon: 'visualchars',\n        onAction,\n        onSetup: toggleActiveState(editor, toggleState)\n      });\n      editor.ui.registry.addToggleMenuItem('visualchars', {\n        text: 'Show invisible characters',\n        icon: 'visualchars',\n        onAction,\n        onSetup: toggleActiveState(editor, toggleState)\n      });\n    };\n\n    var Plugin = () => {\n      global.add('visualchars', editor => {\n        register$1(editor);\n        const toggleState = Cell(isEnabledByDefault(editor));\n        register$2(editor, toggleState);\n        register(editor, toggleState);\n        setup(editor, toggleState);\n        setup$1(editor, toggleState);\n        return get$2(toggleState);\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/wordcount/index.js",
    "content": "// Exports the \"wordcount\" plugin for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/plugins/wordcount')\n//   ES2015:\n//     import 'tinymce/plugins/wordcount'\nrequire('./plugin.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/plugins/wordcount/plugin.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');\n\n    const eq = t => a => t === a;\n    const isNull = eq(null);\n\n    const identity = x => {\n      return x;\n    };\n\n    const zeroWidth = '\\uFEFF';\n    const removeZwsp$1 = s => s.replace(/\\uFEFF/g, '');\n\n    const map = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n\n    const punctuationStr = '[!-#%-*,-\\\\/:;?@\\\\[-\\\\]_{}\\xA1\\xAB\\xB7\\xBB\\xBF;\\xB7\\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\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1361-\\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\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u3008\\u3009\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30\\u2E31\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\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]';\n    const regExps = {\n      aletter: '[A-Za-z\\xaa\\xb5\\xba\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\u02c1\\u02c6-\\u02d1\\u02e0-\\u02e4\\u02ec\\u02ee\\u0370-\\u0374\\u0376\\u0377\\u037a-\\u037d\\u0386\\u0388-\\u038a\\u038c\\u038e-\\u03a1\\u03a3-\\u03f5\\u03f7-\\u0481\\u048a-\\u0527\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05d0-\\u05ea\\u05f0-\\u05F3\\u0620-\\u064a\\u066e\\u066f\\u0671-\\u06d3\\u06d5\\u06e5\\u06e6\\u06ee\\u06ef\\u06fa-\\u06fc\\u06ff\\u0710\\u0712-\\u072f\\u074d-\\u07a5\\u07b1\\u07ca-\\u07ea\\u07f4\\u07f5\\u07fa\\u0800-\\u0815\\u081a\\u0824\\u0828\\u0840-\\u0858\\u0904-\\u0939\\u093d\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097f\\u0985-\\u098c\\u098f\\u0990\\u0993-\\u09a8\\u09aa-\\u09b0\\u09b2\\u09b6-\\u09b9\\u09bd\\u09ce\\u09dc\\u09dd\\u09df-\\u09e1\\u09f0\\u09f1\\u0a05-\\u0a0a\\u0a0f\\u0a10\\u0a13-\\u0a28\\u0a2a-\\u0a30\\u0a32\\u0a33\\u0a35\\u0a36\\u0a38\\u0a39\\u0a59-\\u0a5c\\u0a5e\\u0a72-\\u0a74\\u0a85-\\u0a8d\\u0a8f-\\u0a91\\u0a93-\\u0aa8\\u0aaa-\\u0ab0\\u0ab2\\u0ab3\\u0ab5-\\u0ab9\\u0abd\\u0ad0\\u0ae0\\u0ae1\\u0b05-\\u0b0c\\u0b0f\\u0b10\\u0b13-\\u0b28\\u0b2a-\\u0b30\\u0b32\\u0b33\\u0b35-\\u0b39\\u0b3d\\u0b5c\\u0b5d\\u0b5f-\\u0b61\\u0b71\\u0b83\\u0b85-\\u0b8a\\u0b8e-\\u0b90\\u0b92-\\u0b95\\u0b99\\u0b9a\\u0b9c\\u0b9e\\u0b9f\\u0ba3\\u0ba4\\u0ba8-\\u0baa\\u0bae-\\u0bb9\\u0bd0\\u0c05-\\u0c0c\\u0c0e-\\u0c10\\u0c12-\\u0c28\\u0c2a-\\u0c33\\u0c35-\\u0c39\\u0c3d\\u0c58\\u0c59\\u0c60\\u0c61\\u0c85-\\u0c8c\\u0c8e-\\u0c90\\u0c92-\\u0ca8\\u0caa-\\u0cb3\\u0cb5-\\u0cb9\\u0cbd\\u0cde\\u0ce0\\u0ce1\\u0cf1\\u0cf2\\u0d05-\\u0d0c\\u0d0e-\\u0d10\\u0d12-\\u0d3a\\u0d3d\\u0d4e\\u0d60\\u0d61\\u0d7a-\\u0d7f\\u0d85-\\u0d96\\u0d9a-\\u0db1\\u0db3-\\u0dbb\\u0dbd\\u0dc0-\\u0dc6\\u0f00\\u0f40-\\u0f47\\u0f49-\\u0f6c\\u0f88-\\u0f8c\\u10a0-\\u10c5\\u10d0-\\u10fa\\u10fc\\u1100-\\u1248\\u124a-\\u124d\\u1250-\\u1256\\u1258\\u125a-\\u125d\\u1260-\\u1288\\u128a-\\u128d\\u1290-\\u12b0\\u12b2-\\u12b5\\u12b8-\\u12be\\u12c0\\u12c2-\\u12c5\\u12c8-\\u12d6\\u12d8-\\u1310\\u1312-\\u1315\\u1318-\\u135a\\u1380-\\u138f\\u13a0-\\u13f4\\u1401-\\u166c\\u166f-\\u167f\\u1681-\\u169a\\u16a0-\\u16ea\\u16ee-\\u16f0\\u1700-\\u170c\\u170e-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176c\\u176e-\\u1770\\u1820-\\u1877\\u1880-\\u18a8\\u18aa\\u18b0-\\u18f5\\u1900-\\u191c\\u1a00-\\u1a16\\u1b05-\\u1b33\\u1b45-\\u1b4b\\u1b83-\\u1ba0\\u1bae\\u1baf\\u1bc0-\\u1be5\\u1c00-\\u1c23\\u1c4d-\\u1c4f\\u1c5a-\\u1c7d\\u1ce9-\\u1cec\\u1cee-\\u1cf1\\u1d00-\\u1dbf\\u1e00-\\u1f15\\u1f18-\\u1f1d\\u1f20-\\u1f45\\u1f48-\\u1f4d\\u1f50-\\u1f57\\u1f59\\u1f5b\\u1f5d\\u1f5f-\\u1f7d\\u1f80-\\u1fb4\\u1fb6-\\u1fbc\\u1fbe\\u1fc2-\\u1fc4\\u1fc6-\\u1fcc\\u1fd0-\\u1fd3\\u1fd6-\\u1fdb\\u1fe0-\\u1fec\\u1ff2-\\u1ff4\\u1ff6-\\u1ffc\\u2071\\u207f\\u2090-\\u209c\\u2102\\u2107\\u210a-\\u2113\\u2115\\u2119-\\u211d\\u2124\\u2126\\u2128\\u212a-\\u212d\\u212f-\\u2139\\u213c-\\u213f\\u2145-\\u2149\\u214e\\u2160-\\u2188\\u24B6-\\u24E9\\u2c00-\\u2c2e\\u2c30-\\u2c5e\\u2c60-\\u2ce4\\u2ceb-\\u2cee\\u2d00-\\u2d25\\u2d30-\\u2d65\\u2d6f\\u2d80-\\u2d96\\u2da0-\\u2da6\\u2da8-\\u2dae\\u2db0-\\u2db6\\u2db8-\\u2dbe\\u2dc0-\\u2dc6\\u2dc8-\\u2dce\\u2dd0-\\u2dd6\\u2dd8-\\u2dde\\u2e2f\\u3005\\u303b\\u303c\\u3105-\\u312d\\u3131-\\u318e\\u31a0-\\u31ba\\ua000-\\ua48c\\ua4d0-\\ua4fd\\ua500-\\ua60c\\ua610-\\ua61f\\ua62a\\ua62b\\ua640-\\ua66e\\ua67f-\\ua697\\ua6a0-\\ua6ef\\ua717-\\ua71f\\ua722-\\ua788\\ua78b-\\ua78e\\ua790\\ua791\\ua7a0-\\ua7a9\\ua7fa-\\ua801\\ua803-\\ua805\\ua807-\\ua80a\\ua80c-\\ua822\\ua840-\\ua873\\ua882-\\ua8b3\\ua8f2-\\ua8f7\\ua8fb\\ua90a-\\ua925\\ua930-\\ua946\\ua960-\\ua97c\\ua984-\\ua9b2\\ua9cf\\uaa00-\\uaa28\\uaa40-\\uaa42\\uaa44-\\uaa4b\\uab01-\\uab06\\uab09-\\uab0e\\uab11-\\uab16\\uab20-\\uab26\\uab28-\\uab2e\\uabc0-\\uabe2\\uac00-\\ud7a3\\ud7b0-\\ud7c6\\ud7cb-\\ud7fb\\ufb00-\\ufb06\\ufb13-\\ufb17\\ufb1d\\ufb1f-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40\\ufb41\\ufb43\\ufb44\\ufb46-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb\\ufe70-\\ufe74\\ufe76-\\ufefc\\uff21-\\uff3a\\uff41-\\uff5a\\uffa0-\\uffbe\\uffc2-\\uffc7\\uffca-\\uffcf\\uffd2-\\uffd7\\uffda-\\uffdc]',\n      midnumlet: `[-'\\\\.\\u2018\\u2019\\u2024\\uFE52\\uFF07\\uFF0E]`,\n      midletter: '[:\\xB7\\xB7\\u05F4\\u2027\\uFE13\\uFE55\\uFF1A]',\n      midnum: '[\\xB1+*/,;;\\u0589\\u060C\\u060D\\u066C\\u07F8\\u2044\\uFE10\\uFE14\\uFE50\\uFE54\\uFF0C\\uFF1B]',\n      numeric: '[0-9\\u0660-\\u0669\\u066B\\u06f0-\\u06f9\\u07c0-\\u07c9\\u0966-\\u096f\\u09e6-\\u09ef\\u0a66-\\u0a6f\\u0ae6-\\u0aef\\u0b66-\\u0b6f\\u0be6-\\u0bef\\u0c66-\\u0c6f\\u0ce6-\\u0cef\\u0d66-\\u0d6f\\u0e50-\\u0e59\\u0ed0-\\u0ed9\\u0f20-\\u0f29\\u1040-\\u1049\\u1090-\\u1099\\u17e0-\\u17e9\\u1810-\\u1819\\u1946-\\u194f\\u19d0-\\u19d9\\u1a80-\\u1a89\\u1a90-\\u1a99\\u1b50-\\u1b59\\u1bb0-\\u1bb9\\u1c40-\\u1c49\\u1c50-\\u1c59\\ua620-\\ua629\\ua8d0-\\ua8d9\\ua900-\\ua909\\ua9d0-\\ua9d9\\uaa50-\\uaa59\\uabf0-\\uabf9]',\n      cr: '\\\\r',\n      lf: '\\\\n',\n      newline: '[\\x0B\\f\\x85\\u2028\\u2029]',\n      extend: '[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065f\\u0670\\u06d6-\\u06dc\\u06df-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0859-\\u085b\\u0900-\\u0903\\u093a-\\u093c\\u093e-\\u094f\\u0951-\\u0957\\u0962\\u0963\\u0981-\\u0983\\u09bc\\u09be-\\u09c4\\u09c7\\u09c8\\u09cb-\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01-\\u0a03\\u0a3c\\u0a3e-\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81-\\u0a83\\u0abc\\u0abe-\\u0ac5\\u0ac7-\\u0ac9\\u0acb-\\u0acd\\u0ae2\\u0ae3\\u0b01-\\u0b03\\u0b3c\\u0b3e-\\u0b44\\u0b47\\u0b48\\u0b4b-\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe-\\u0bc2\\u0bc6-\\u0bc8\\u0bca-\\u0bcd\\u0bd7\\u0c01-\\u0c03\\u0c3e-\\u0c44\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0c82\\u0c83\\u0cbc\\u0cbe-\\u0cc4\\u0cc6-\\u0cc8\\u0cca-\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d02\\u0d03\\u0d3e-\\u0d44\\u0d46-\\u0d48\\u0d4a-\\u0d4d\\u0d57\\u0d62\\u0d63\\u0d82\\u0d83\\u0dca\\u0dcf-\\u0dd4\\u0dd6\\u0dd8-\\u0ddf\\u0df2\\u0df3\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f3e\\u0f3f\\u0f71-\\u0f84\\u0f86\\u0f87\\u0f8d-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102b-\\u103e\\u1056-\\u1059\\u105e-\\u1060\\u1062-\\u1064\\u1067-\\u106d\\u1071-\\u1074\\u1082-\\u108d\\u108f\\u109a-\\u109d\\u135d-\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b6-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u192b\\u1930-\\u193b\\u19b0-\\u19c0\\u19c8\\u19c9\\u1a17-\\u1a1b\\u1a55-\\u1a5e\\u1a60-\\u1a7c\\u1a7f\\u1b00-\\u1b04\\u1b34-\\u1b44\\u1b6b-\\u1b73\\u1b80-\\u1b82\\u1ba1-\\u1baa\\u1be6-\\u1bf3\\u1c24-\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce8\\u1ced\\u1cf2\\u1dc0-\\u1de6\\u1dfc-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2d7f\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\uA672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua823-\\ua827\\ua880\\ua881\\ua8b4-\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua953\\ua980-\\ua983\\ua9b3-\\ua9c0\\uaa29-\\uaa36\\uaa43\\uaa4c\\uaa4d\\uaa7b\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe3-\\uabea\\uabec\\uabed\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]',\n      format: '[\\xAD\\u0600-\\u0603\\u06DD\\u070F\\u17b4\\u17b5\\u200E\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u206A-\\u206F\\uFEFF\\uFFF9-\\uFFFB]',\n      katakana: '[\\u3031-\\u3035\\u309B\\u309C\\u30A0-\\u30fa\\u30fc-\\u30ff\\u31f0-\\u31ff\\u32D0-\\u32FE\\u3300-\\u3357\\uff66-\\uff9d]',\n      extendnumlet: '[=_\\u203f\\u2040\\u2054\\ufe33\\ufe34\\ufe4d-\\ufe4f\\uff3f\\u2200-\\u22FF<>]',\n      punctuation: punctuationStr\n    };\n    const characterIndices = {\n      ALETTER: 0,\n      MIDNUMLET: 1,\n      MIDLETTER: 2,\n      MIDNUM: 3,\n      NUMERIC: 4,\n      CR: 5,\n      LF: 6,\n      NEWLINE: 7,\n      EXTEND: 8,\n      FORMAT: 9,\n      KATAKANA: 10,\n      EXTENDNUMLET: 11,\n      AT: 12,\n      OTHER: 13\n    };\n    const SETS$1 = [\n      new RegExp(regExps.aletter),\n      new RegExp(regExps.midnumlet),\n      new RegExp(regExps.midletter),\n      new RegExp(regExps.midnum),\n      new RegExp(regExps.numeric),\n      new RegExp(regExps.cr),\n      new RegExp(regExps.lf),\n      new RegExp(regExps.newline),\n      new RegExp(regExps.extend),\n      new RegExp(regExps.format),\n      new RegExp(regExps.katakana),\n      new RegExp(regExps.extendnumlet),\n      new RegExp('@')\n    ];\n    const EMPTY_STRING$1 = '';\n    const PUNCTUATION$1 = new RegExp('^' + regExps.punctuation + '$');\n    const WHITESPACE$1 = /^\\s+$/;\n\n    const SETS = SETS$1;\n    const OTHER = characterIndices.OTHER;\n    const getType = char => {\n      let type = OTHER;\n      const setsLength = SETS.length;\n      for (let j = 0; j < setsLength; ++j) {\n        const set = SETS[j];\n        if (set && set.test(char)) {\n          type = j;\n          break;\n        }\n      }\n      return type;\n    };\n    const memoize = func => {\n      const cache = {};\n      return char => {\n        if (cache[char]) {\n          return cache[char];\n        } else {\n          const result = func(char);\n          cache[char] = result;\n          return result;\n        }\n      };\n    };\n    const classify = characters => {\n      const memoized = memoize(getType);\n      return map(characters, memoized);\n    };\n\n    const isWordBoundary = (map, index) => {\n      const type = map[index];\n      const nextType = map[index + 1];\n      if (index < 0 || index > map.length - 1 && index !== 0) {\n        return false;\n      }\n      if (type === characterIndices.ALETTER && nextType === characterIndices.ALETTER) {\n        return false;\n      }\n      const nextNextType = map[index + 2];\n      if (type === characterIndices.ALETTER && (nextType === characterIndices.MIDLETTER || nextType === characterIndices.MIDNUMLET || nextType === characterIndices.AT) && nextNextType === characterIndices.ALETTER) {\n        return false;\n      }\n      const prevType = map[index - 1];\n      if ((type === characterIndices.MIDLETTER || type === characterIndices.MIDNUMLET || nextType === characterIndices.AT) && nextType === characterIndices.ALETTER && prevType === characterIndices.ALETTER) {\n        return false;\n      }\n      if ((type === characterIndices.NUMERIC || type === characterIndices.ALETTER) && (nextType === characterIndices.NUMERIC || nextType === characterIndices.ALETTER)) {\n        return false;\n      }\n      if ((type === characterIndices.MIDNUM || type === characterIndices.MIDNUMLET) && nextType === characterIndices.NUMERIC && prevType === characterIndices.NUMERIC) {\n        return false;\n      }\n      if (type === characterIndices.NUMERIC && (nextType === characterIndices.MIDNUM || nextType === characterIndices.MIDNUMLET) && nextNextType === characterIndices.NUMERIC) {\n        return false;\n      }\n      if (type === characterIndices.EXTEND || type === characterIndices.FORMAT || prevType === characterIndices.EXTEND || prevType === characterIndices.FORMAT || nextType === characterIndices.EXTEND || nextType === characterIndices.FORMAT) {\n        return false;\n      }\n      if (type === characterIndices.CR && nextType === characterIndices.LF) {\n        return false;\n      }\n      if (type === characterIndices.NEWLINE || type === characterIndices.CR || type === characterIndices.LF) {\n        return true;\n      }\n      if (nextType === characterIndices.NEWLINE || nextType === characterIndices.CR || nextType === characterIndices.LF) {\n        return true;\n      }\n      if (type === characterIndices.KATAKANA && nextType === characterIndices.KATAKANA) {\n        return false;\n      }\n      if (nextType === characterIndices.EXTENDNUMLET && (type === characterIndices.ALETTER || type === characterIndices.NUMERIC || type === characterIndices.KATAKANA || type === characterIndices.EXTENDNUMLET)) {\n        return false;\n      }\n      if (type === characterIndices.EXTENDNUMLET && (nextType === characterIndices.ALETTER || nextType === characterIndices.NUMERIC || nextType === characterIndices.KATAKANA)) {\n        return false;\n      }\n      if (type === characterIndices.AT) {\n        return false;\n      }\n      return true;\n    };\n\n    const EMPTY_STRING = EMPTY_STRING$1;\n    const WHITESPACE = WHITESPACE$1;\n    const PUNCTUATION = PUNCTUATION$1;\n    const isProtocol = str => str === 'http' || str === 'https';\n    const findWordEnd = (characters, startIndex) => {\n      let i;\n      for (i = startIndex; i < characters.length; i++) {\n        if (WHITESPACE.test(characters[i])) {\n          break;\n        }\n      }\n      return i;\n    };\n    const findUrlEnd = (characters, startIndex) => {\n      const endIndex = findWordEnd(characters, startIndex + 1);\n      const peakedWord = characters.slice(startIndex + 1, endIndex).join(EMPTY_STRING);\n      return peakedWord.substr(0, 3) === '://' ? endIndex : startIndex;\n    };\n    const findWords = (chars, sChars, characterMap, options) => {\n      const words = [];\n      let word = [];\n      for (let i = 0; i < characterMap.length; ++i) {\n        word.push(chars[i]);\n        if (isWordBoundary(characterMap, i)) {\n          const ch = sChars[i];\n          if ((options.includeWhitespace || !WHITESPACE.test(ch)) && (options.includePunctuation || !PUNCTUATION.test(ch))) {\n            const startOfWord = i - word.length + 1;\n            const endOfWord = i + 1;\n            const str = sChars.slice(startOfWord, endOfWord).join(EMPTY_STRING);\n            if (isProtocol(str)) {\n              const endOfUrl = findUrlEnd(sChars, i);\n              const url = chars.slice(endOfWord, endOfUrl);\n              Array.prototype.push.apply(word, url);\n              i = endOfUrl;\n            }\n            words.push(word);\n          }\n          word = [];\n        }\n      }\n      return words;\n    };\n    const getDefaultOptions = () => ({\n      includeWhitespace: false,\n      includePunctuation: false\n    });\n    const getWords$1 = (chars, extract, options) => {\n      options = {\n        ...getDefaultOptions(),\n        ...options\n      };\n      const filteredChars = [];\n      const extractedChars = [];\n      for (let i = 0; i < chars.length; i++) {\n        const ch = extract(chars[i]);\n        if (ch !== zeroWidth) {\n          filteredChars.push(chars[i]);\n          extractedChars.push(ch);\n        }\n      }\n      const characterMap = classify(extractedChars);\n      return findWords(filteredChars, extractedChars, characterMap, options);\n    };\n\n    const getWords = getWords$1;\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');\n\n    const getText = (node, schema) => {\n      const blockElements = schema.getBlockElements();\n      const voidElements = schema.getVoidElements();\n      const isNewline = node => blockElements[node.nodeName] || voidElements[node.nodeName];\n      const textBlocks = [];\n      let txt = '';\n      const treeWalker = new global$1(node, node);\n      let tempNode;\n      while (tempNode = treeWalker.next()) {\n        if (tempNode.nodeType === 3) {\n          txt += removeZwsp$1(tempNode.data);\n        } else if (isNewline(tempNode) && txt.length) {\n          textBlocks.push(txt);\n          txt = '';\n        }\n      }\n      if (txt.length) {\n        textBlocks.push(txt);\n      }\n      return textBlocks;\n    };\n\n    const removeZwsp = text => text.replace(/\\u200B/g, '');\n    const strLen = str => str.replace(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g, '_').length;\n    const countWords = (node, schema) => {\n      const text = removeZwsp(getText(node, schema).join('\\n'));\n      return getWords(text.split(''), identity).length;\n    };\n    const countCharacters = (node, schema) => {\n      const text = getText(node, schema).join('');\n      return strLen(text);\n    };\n    const countCharactersWithoutSpaces = (node, schema) => {\n      const text = getText(node, schema).join('').replace(/\\s/g, '');\n      return strLen(text);\n    };\n\n    const createBodyCounter = (editor, count) => () => count(editor.getBody(), editor.schema);\n    const createSelectionCounter = (editor, count) => () => count(editor.selection.getRng().cloneContents(), editor.schema);\n    const createBodyWordCounter = editor => createBodyCounter(editor, countWords);\n    const get = editor => ({\n      body: {\n        getWordCount: createBodyWordCounter(editor),\n        getCharacterCount: createBodyCounter(editor, countCharacters),\n        getCharacterCountWithoutSpaces: createBodyCounter(editor, countCharactersWithoutSpaces)\n      },\n      selection: {\n        getWordCount: createSelectionCounter(editor, countWords),\n        getCharacterCount: createSelectionCounter(editor, countCharacters),\n        getCharacterCountWithoutSpaces: createSelectionCounter(editor, countCharactersWithoutSpaces)\n      },\n      getCount: createBodyWordCounter(editor)\n    });\n\n    const open = (editor, api) => {\n      editor.windowManager.open({\n        title: 'Word Count',\n        body: {\n          type: 'panel',\n          items: [{\n              type: 'table',\n              header: [\n                'Count',\n                'Document',\n                'Selection'\n              ],\n              cells: [\n                [\n                  'Words',\n                  String(api.body.getWordCount()),\n                  String(api.selection.getWordCount())\n                ],\n                [\n                  'Characters (no spaces)',\n                  String(api.body.getCharacterCountWithoutSpaces()),\n                  String(api.selection.getCharacterCountWithoutSpaces())\n                ],\n                [\n                  'Characters',\n                  String(api.body.getCharacterCount()),\n                  String(api.selection.getCharacterCount())\n                ]\n              ]\n            }]\n        },\n        buttons: [{\n            type: 'cancel',\n            name: 'close',\n            text: 'Close',\n            primary: true\n          }]\n      });\n    };\n\n    const register$1 = (editor, api) => {\n      editor.addCommand('mceWordCount', () => open(editor, api));\n    };\n\n    const first = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        if (isNull(timer)) {\n          timer = setTimeout(() => {\n            timer = null;\n            fn.apply(null, args);\n          }, rate);\n        }\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.Delay');\n\n    const fireWordCountUpdate = (editor, api) => {\n      editor.dispatch('wordCountUpdate', {\n        wordCount: {\n          words: api.body.getWordCount(),\n          characters: api.body.getCharacterCount(),\n          charactersWithoutSpaces: api.body.getCharacterCountWithoutSpaces()\n        }\n      });\n    };\n\n    const updateCount = (editor, api) => {\n      fireWordCountUpdate(editor, api);\n    };\n    const setup = (editor, api, delay) => {\n      const debouncedUpdate = first(() => updateCount(editor, api), delay);\n      editor.on('init', () => {\n        updateCount(editor, api);\n        global.setEditorTimeout(editor, () => {\n          editor.on('SetContent BeforeAddUndo Undo Redo ViewUpdate keyup', debouncedUpdate.throttle);\n        }, 0);\n        editor.on('remove', debouncedUpdate.cancel);\n      });\n    };\n\n    const register = editor => {\n      const onAction = () => editor.execCommand('mceWordCount');\n      editor.ui.registry.addButton('wordcount', {\n        tooltip: 'Word count',\n        icon: 'character-count',\n        onAction\n      });\n      editor.ui.registry.addMenuItem('wordcount', {\n        text: 'Word count',\n        icon: 'character-count',\n        onAction\n      });\n    };\n\n    var Plugin = (delay = 300) => {\n      global$2.add('wordcount', editor => {\n        const api = get(editor);\n        register$1(editor, api);\n        register(editor);\n        setup(editor, api, delay);\n        return api;\n      });\n    };\n\n    Plugin();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/content/dark/content.css",
    "content": "body {\n  background-color: #222f3e;\n  color: #fff;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  line-height: 1.4;\n  margin: 1rem;\n}\na {\n  color: #4099ff;\n}\ntable {\n  border-collapse: collapse;\n}\n/* Apply a default padding if legacy cellpadding attribute is missing */\ntable:not([cellpadding]) th,\ntable:not([cellpadding]) td {\n  padding: 0.4rem;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) td {\n  border-width: 1px;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) td {\n  border-style: solid;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) td {\n  border-color: #6d737b;\n}\nfigure {\n  display: table;\n  margin: 1rem auto;\n}\nfigure figcaption {\n  color: #8a8f97;\n  display: block;\n  margin-top: 0.25rem;\n  text-align: center;\n}\nhr {\n  border-color: #6d737b;\n  border-style: solid;\n  border-width: 1px 0 0 0;\n}\ncode {\n  background-color: #6d737b;\n  border-radius: 3px;\n  padding: 0.1rem 0.2rem;\n}\n.mce-content-body:not([dir=rtl]) blockquote {\n  border-left: 2px solid #6d737b;\n  margin-left: 1.5rem;\n  padding-left: 1rem;\n}\n.mce-content-body[dir=rtl] blockquote {\n  border-right: 2px solid #6d737b;\n  margin-right: 1.5rem;\n  padding-right: 1rem;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/content/default/content.css",
    "content": "body {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  line-height: 1.4;\n  margin: 1rem;\n}\ntable {\n  border-collapse: collapse;\n}\n/* Apply a default padding if legacy cellpadding attribute is missing */\ntable:not([cellpadding]) th,\ntable:not([cellpadding]) td {\n  padding: 0.4rem;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) td {\n  border-width: 1px;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) td {\n  border-style: solid;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) td {\n  border-color: #ccc;\n}\nfigure {\n  display: table;\n  margin: 1rem auto;\n}\nfigure figcaption {\n  color: #999;\n  display: block;\n  margin-top: 0.25rem;\n  text-align: center;\n}\nhr {\n  border-color: #ccc;\n  border-style: solid;\n  border-width: 1px 0 0 0;\n}\ncode {\n  background-color: #e8e8e8;\n  border-radius: 3px;\n  padding: 0.1rem 0.2rem;\n}\n.mce-content-body:not([dir=rtl]) blockquote {\n  border-left: 2px solid #ccc;\n  margin-left: 1.5rem;\n  padding-left: 1rem;\n}\n.mce-content-body[dir=rtl] blockquote {\n  border-right: 2px solid #ccc;\n  margin-right: 1.5rem;\n  padding-right: 1rem;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/content/document/content.css",
    "content": "@media screen {\n  html {\n    background: #f4f4f4;\n    min-height: 100%;\n  }\n}\nbody {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n}\n@media screen {\n  body {\n    background-color: #fff;\n    box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);\n    box-sizing: border-box;\n    margin: 1rem auto 0;\n    max-width: 820px;\n    min-height: calc(100vh - 1rem);\n    padding: 4rem 6rem 6rem 6rem;\n  }\n}\ntable {\n  border-collapse: collapse;\n}\n/* Apply a default padding if legacy cellpadding attribute is missing */\ntable:not([cellpadding]) th,\ntable:not([cellpadding]) td {\n  padding: 0.4rem;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) td {\n  border-width: 1px;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) td {\n  border-style: solid;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) td {\n  border-color: #ccc;\n}\nfigure figcaption {\n  color: #999;\n  margin-top: 0.25rem;\n  text-align: center;\n}\nhr {\n  border-color: #ccc;\n  border-style: solid;\n  border-width: 1px 0 0 0;\n}\n.mce-content-body:not([dir=rtl]) blockquote {\n  border-left: 2px solid #ccc;\n  margin-left: 1.5rem;\n  padding-left: 1rem;\n}\n.mce-content-body[dir=rtl] blockquote {\n  border-right: 2px solid #ccc;\n  margin-right: 1.5rem;\n  padding-right: 1rem;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/content/tinymce-5/content.css",
    "content": "body {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  line-height: 1.4;\n  margin: 1rem;\n}\ntable {\n  border-collapse: collapse;\n}\n/* Apply a default padding if legacy cellpadding attribute is missing */\ntable:not([cellpadding]) th,\ntable:not([cellpadding]) td {\n  padding: 0.4rem;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) td {\n  border-width: 1px;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) td {\n  border-style: solid;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) td {\n  border-color: #ccc;\n}\nfigure {\n  display: table;\n  margin: 1rem auto;\n}\nfigure figcaption {\n  color: #999;\n  display: block;\n  margin-top: 0.25rem;\n  text-align: center;\n}\nhr {\n  border-color: #ccc;\n  border-style: solid;\n  border-width: 1px 0 0 0;\n}\ncode {\n  background-color: #e8e8e8;\n  border-radius: 3px;\n  padding: 0.1rem 0.2rem;\n}\n.mce-content-body:not([dir=rtl]) blockquote {\n  border-left: 2px solid #ccc;\n  margin-left: 1.5rem;\n  padding-left: 1rem;\n}\n.mce-content-body[dir=rtl] blockquote {\n  border-right: 2px solid #ccc;\n  margin-right: 1.5rem;\n  padding-right: 1rem;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/content/tinymce-5-dark/content.css",
    "content": "body {\n  background-color: #2f3742;\n  color: #dfe0e4;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  line-height: 1.4;\n  margin: 1rem;\n}\na {\n  color: #4099ff;\n}\ntable {\n  border-collapse: collapse;\n}\n/* Apply a default padding if legacy cellpadding attribute is missing */\ntable:not([cellpadding]) th,\ntable:not([cellpadding]) td {\n  padding: 0.4rem;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) td {\n  border-width: 1px;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) td {\n  border-style: solid;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) td {\n  border-color: #6d737b;\n}\nfigure {\n  display: table;\n  margin: 1rem auto;\n}\nfigure figcaption {\n  color: #8a8f97;\n  display: block;\n  margin-top: 0.25rem;\n  text-align: center;\n}\nhr {\n  border-color: #6d737b;\n  border-style: solid;\n  border-width: 1px 0 0 0;\n}\ncode {\n  background-color: #6d737b;\n  border-radius: 3px;\n  padding: 0.1rem 0.2rem;\n}\n.mce-content-body:not([dir=rtl]) blockquote {\n  border-left: 2px solid #6d737b;\n  margin-left: 1.5rem;\n  padding-left: 1rem;\n}\n.mce-content-body[dir=rtl] blockquote {\n  border-right: 2px solid #6d737b;\n  margin-right: 1.5rem;\n  padding-right: 1rem;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/content/writer/content.css",
    "content": "body {\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  line-height: 1.4;\n  margin: 1rem auto;\n  max-width: 900px;\n}\ntable {\n  border-collapse: collapse;\n}\n/* Apply a default padding if legacy cellpadding attribute is missing */\ntable:not([cellpadding]) th,\ntable:not([cellpadding]) td {\n  padding: 0.4rem;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-width\"]) td {\n  border-width: 1px;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-style\"]) td {\n  border-style: solid;\n}\n/* Set default table styles if a table has a positive border attribute\n   and no inline css */\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) th,\ntable[border]:not([border=\"0\"]):not([style*=\"border-color\"]) td {\n  border-color: #ccc;\n}\nfigure {\n  display: table;\n  margin: 1rem auto;\n}\nfigure figcaption {\n  color: #999;\n  display: block;\n  margin-top: 0.25rem;\n  text-align: center;\n}\nhr {\n  border-color: #ccc;\n  border-style: solid;\n  border-width: 1px 0 0 0;\n}\ncode {\n  background-color: #e8e8e8;\n  border-radius: 3px;\n  padding: 0.1rem 0.2rem;\n}\n.mce-content-body:not([dir=rtl]) blockquote {\n  border-left: 2px solid #ccc;\n  margin-left: 1.5rem;\n  padding-left: 1rem;\n}\n.mce-content-body[dir=rtl] blockquote {\n  border-right: 2px solid #ccc;\n  margin-right: 1.5rem;\n  padding-right: 1rem;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide/content.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: black;\n  background: none;\n  text-shadow: 0 1px white;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"]::-moz-selection,\npre[class*=\"language-\"] ::-moz-selection,\ncode[class*=\"language-\"]::-moz-selection,\ncode[class*=\"language-\"] ::-moz-selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\npre[class*=\"language-\"]::selection,\npre[class*=\"language-\"] ::selection,\ncode[class*=\"language-\"]::selection,\ncode[class*=\"language-\"] ::selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n@media print {\n  code[class*=\"language-\"],\n  pre[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #f5f2f0;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: slategray;\n}\n.token.punctuation {\n  color: #999;\n}\n.token.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #905;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #690;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #9a6e3a;\n  /* This background color was intended by the author of this theme. */\n  background: hsla(0, 0%, 100%, 0.5);\n}\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #07a;\n}\n.token.function,\n.token.class-name {\n  color: #DD4A68;\n}\n.token.regex,\n.token.important,\n.token.variable {\n  color: #e90;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.1);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #b4d7ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid rgba(180, 215, 255, 0.7);\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: multiply;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #b4d7ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\nbody {\n  font-family: sans-serif;\n}\ntable {\n  border-collapse: collapse;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide/content.inline.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: black;\n  background: none;\n  text-shadow: 0 1px white;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"]::-moz-selection,\npre[class*=\"language-\"] ::-moz-selection,\ncode[class*=\"language-\"]::-moz-selection,\ncode[class*=\"language-\"] ::-moz-selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\npre[class*=\"language-\"]::selection,\npre[class*=\"language-\"] ::selection,\ncode[class*=\"language-\"]::selection,\ncode[class*=\"language-\"] ::selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n@media print {\n  code[class*=\"language-\"],\n  pre[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #f5f2f0;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: slategray;\n}\n.token.punctuation {\n  color: #999;\n}\n.token.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #905;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #690;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #9a6e3a;\n  /* This background color was intended by the author of this theme. */\n  background: hsla(0, 0%, 100%, 0.5);\n}\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #07a;\n}\n.token.function,\n.token.class-name {\n  color: #DD4A68;\n}\n.token.regex,\n.token.important,\n.token.variable {\n  color: #e90;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.1);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #b4d7ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid rgba(180, 215, 255, 0.7);\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: multiply;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #b4d7ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide/skin.css",
    "content": ".tox {\n  box-shadow: none;\n  box-sizing: content-box;\n  color: #222f3e;\n  cursor: auto;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: normal;\n  -webkit-tap-highlight-color: transparent;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  vertical-align: initial;\n  white-space: normal;\n}\n.tox *:not(svg):not(rect) {\n  box-sizing: inherit;\n  color: inherit;\n  cursor: inherit;\n  direction: inherit;\n  font-family: inherit;\n  font-size: inherit;\n  font-style: inherit;\n  font-weight: inherit;\n  line-height: inherit;\n  -webkit-tap-highlight-color: inherit;\n  text-align: inherit;\n  text-decoration: inherit;\n  text-shadow: inherit;\n  text-transform: inherit;\n  vertical-align: inherit;\n  white-space: inherit;\n}\n.tox *:not(svg):not(rect) {\n  /* stylelint-disable-line no-duplicate-selectors */\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  float: none;\n  height: auto;\n  margin: 0;\n  max-width: none;\n  outline: 0;\n  padding: 0;\n  position: static;\n  width: auto;\n}\n.tox:not([dir=rtl]) {\n  direction: ltr;\n  text-align: left;\n}\n.tox[dir=rtl] {\n  direction: rtl;\n  text-align: right;\n}\n.tox-tinymce {\n  border: 2px solid #eeeeee;\n  border-radius: 10px;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  overflow: hidden;\n  position: relative;\n  visibility: inherit !important;\n}\n.tox.tox-tinymce-inline {\n  border: none;\n  box-shadow: none;\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-container {\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-header {\n  background-color: #fff;\n  border: 2px solid #eeeeee;\n  border-radius: 10px;\n  box-shadow: none;\n  overflow: hidden;\n}\n.tox-tinymce-aux {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  z-index: 1300;\n}\n.tox-tinymce *:focus,\n.tox-tinymce-aux *:focus {\n  outline: none;\n}\nbutton::-moz-focus-inner {\n  border: 0;\n}\n.tox[dir=rtl] .tox-icon--flip svg {\n  transform: rotateY(180deg);\n}\n.tox .accessibility-issue__header {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description {\n  align-items: stretch;\n  border-radius: 6px;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .accessibility-issue__description > div {\n  padding-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div .tox-icon svg {\n  display: block;\n}\n.tox .accessibility-issue__repair {\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description {\n  background-color: rgba(0, 101, 216, 0.1);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2 {\n  color: #006ce7;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg {\n  fill: #006ce7;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon {\n  background-color: #006ce7;\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:focus {\n  background-color: #0060ce;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:active {\n  background-color: #0054b4;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description {\n  background-color: rgba(255, 165, 0, 0.08);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2 {\n  color: #8f5d00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg {\n  fill: #8f5d00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon {\n  background-color: #FFE89D;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:focus {\n  background-color: #F2D574;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:active {\n  background-color: #E8C657;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description {\n  background-color: rgba(204, 0, 0, 0.1);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2 {\n  color: #c00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg {\n  fill: #c00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon {\n  background-color: #F2BFBF;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:focus {\n  background-color: #E9A4A4;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:active {\n  background-color: #EE9494;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description {\n  background-color: rgba(120, 171, 70, 0.1);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description > *:last-child {\n  display: none;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2 {\n  color: #527530;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg {\n  fill: #527530;\n}\n.tox .tox-dialog__body-content .accessibility-issue__header .tox-form__group h1,\n.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2 {\n  font-size: 14px;\n  margin-top: 0;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-left: auto;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 4px 4px 8px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-right: auto;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 8px 4px 4px;\n}\n.tox .tox-anchorbar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-bar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-button {\n  background-color: #006ce7;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #006ce7;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  line-height: 24px;\n  margin: 0;\n  outline: none;\n  padding: 4px 16px;\n  position: relative;\n  text-align: center;\n  text-decoration: none;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-button::before {\n  border-radius: 6px;\n  bottom: -1px;\n  box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #006ce7, 0 0 0 3px rgba(0, 108, 231, 0.25);\n  content: '';\n  left: -1px;\n  opacity: 0;\n  pointer-events: none;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n.tox .tox-button[disabled] {\n  background-color: #006ce7;\n  background-image: none;\n  border-color: #006ce7;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-button:focus:not(:disabled) {\n  background-color: #0060ce;\n  background-image: none;\n  border-color: #0060ce;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:focus-visible:not(:disabled)::before {\n  opacity: 1;\n}\n.tox .tox-button:hover:not(:disabled) {\n  background-color: #0060ce;\n  background-image: none;\n  border-color: #0060ce;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:active:not(:disabled) {\n  background-color: #0054b4;\n  background-image: none;\n  border-color: #0054b4;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary {\n  background-color: #f0f0f0;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #f0f0f0;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  color: #222f3e;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  outline: none;\n  padding: 4px 16px;\n  text-decoration: none;\n  text-transform: none;\n}\n.tox .tox-button--secondary[disabled] {\n  background-color: #f0f0f0;\n  background-image: none;\n  border-color: #f0f0f0;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-button--secondary:focus:not(:disabled) {\n  background-color: #e3e3e3;\n  background-image: none;\n  border-color: #e3e3e3;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--secondary:hover:not(:disabled) {\n  background-color: #e3e3e3;\n  background-image: none;\n  border-color: #e3e3e3;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--secondary:active:not(:disabled) {\n  background-color: #d6d6d6;\n  background-image: none;\n  border-color: #d6d6d6;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--icon,\n.tox .tox-button.tox-button--icon,\n.tox .tox-button.tox-button--secondary.tox-button--icon {\n  padding: 4px;\n}\n.tox .tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg {\n  display: block;\n  fill: currentColor;\n}\n.tox .tox-button-link {\n  background: 0;\n  border: none;\n  box-sizing: border-box;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  padding: 0;\n  white-space: nowrap;\n}\n.tox .tox-button-link--sm {\n  font-size: 14px;\n}\n.tox .tox-button--naked {\n  background-color: transparent;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #222f3e;\n}\n.tox .tox-button--naked[disabled] {\n  background-color: rgba(34, 47, 62, 0.12);\n  border-color: transparent;\n  box-shadow: unset;\n  color: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-button--naked:hover:not(:disabled) {\n  background-color: rgba(34, 47, 62, 0.12);\n  border-color: transparent;\n  box-shadow: unset;\n  color: #222f3e;\n}\n.tox .tox-button--naked:focus:not(:disabled) {\n  background-color: rgba(34, 47, 62, 0.12);\n  border-color: transparent;\n  box-shadow: unset;\n  color: #222f3e;\n}\n.tox .tox-button--naked:active:not(:disabled) {\n  background-color: rgba(34, 47, 62, 0.18);\n  border-color: transparent;\n  box-shadow: unset;\n  color: #222f3e;\n}\n.tox .tox-button--naked .tox-icon svg {\n  fill: currentColor;\n}\n.tox .tox-button--naked.tox-button--icon:hover:not(:disabled) {\n  color: #222f3e;\n}\n.tox .tox-checkbox {\n  align-items: center;\n  border-radius: 6px;\n  cursor: pointer;\n  display: flex;\n  height: 36px;\n  min-width: 36px;\n}\n.tox .tox-checkbox__input {\n  /* Hide from view but visible to screen readers */\n  height: 1px;\n  overflow: hidden;\n  position: absolute;\n  top: auto;\n  width: 1px;\n}\n.tox .tox-checkbox__icons {\n  align-items: center;\n  border-radius: 6px;\n  box-shadow: 0 0 0 2px transparent;\n  box-sizing: content-box;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  padding: calc(4px - 1px);\n  width: 24px;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: block;\n  fill: rgba(34, 47, 62, 0.3);\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: none;\n  fill: #006ce7;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: none;\n  fill: #006ce7;\n}\n.tox .tox-checkbox--disabled {\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:focus + .tox-checkbox__icons {\n  border-radius: 6px;\n  box-shadow: inset 0 0 0 1px #006ce7;\n  padding: calc(4px - 1px);\n}\n.tox:not([dir=rtl]) .tox-checkbox__label {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-checkbox__input {\n  left: -10000px;\n}\n.tox:not([dir=rtl]) .tox-bar .tox-checkbox {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__label {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__input {\n  right: -10000px;\n}\n.tox[dir=rtl] .tox-bar .tox-checkbox {\n  margin-right: 4px;\n}\n.tox {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-collection--toolbar .tox-collection__group {\n  display: flex;\n  padding: 0;\n}\n.tox .tox-collection--grid .tox-collection__group {\n  display: flex;\n  flex-wrap: wrap;\n  max-height: 208px;\n  overflow-x: hidden;\n  overflow-y: auto;\n  padding: 0;\n}\n.tox .tox-collection--list .tox-collection__group {\n  border-bottom-width: 0;\n  border-color: #e3e3e3;\n  border-left-width: 0;\n  border-right-width: 0;\n  border-style: solid;\n  border-top-width: 1px;\n  padding: 4px 0;\n}\n.tox .tox-collection--list .tox-collection__group:first-child {\n  border-top-width: 0;\n}\n.tox .tox-collection__group-heading {\n  background-color: #fcfcfc;\n  color: rgba(34, 47, 62, 0.7);\n  cursor: default;\n  font-size: 12px;\n  font-style: normal;\n  font-weight: normal;\n  margin-bottom: 4px;\n  margin-top: -4px;\n  padding: 4px 8px;\n  text-transform: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection__item {\n  align-items: center;\n  border-radius: 3px;\n  color: #222f3e;\n  display: flex;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection--list .tox-collection__item {\n  padding: 4px 8px;\n}\n.tox .tox-collection--toolbar .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--grid .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--list .tox-collection__item--enabled {\n  background-color: #fff;\n  color: #222f3e;\n}\n.tox .tox-collection--list .tox-collection__item--active {\n  background-color: #cce2fa;\n}\n.tox .tox-collection--toolbar .tox-collection__item--enabled {\n  background-color: #a6ccf7;\n  color: #222f3e;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active {\n  background-color: #cce2fa;\n}\n.tox .tox-collection--grid .tox-collection__item--enabled {\n  background-color: #a6ccf7;\n  color: #222f3e;\n}\n.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  background-color: #cce2fa;\n  color: #222f3e;\n}\n.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #222f3e;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #222f3e;\n}\n.tox .tox-collection__item-icon,\n.tox .tox-collection__item-checkmark {\n  align-items: center;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  width: 24px;\n}\n.tox .tox-collection__item-icon svg,\n.tox .tox-collection__item-checkmark svg {\n  fill: currentColor;\n}\n.tox .tox-collection--toolbar-lg .tox-collection__item-icon {\n  height: 48px;\n  width: 48px;\n}\n.tox .tox-collection__item-label {\n  color: currentColor;\n  display: inline-block;\n  flex: 1;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 24px;\n  text-transform: none;\n  word-break: break-all;\n}\n.tox .tox-collection__item-accessory {\n  color: rgba(34, 47, 62, 0.7);\n  display: inline-block;\n  font-size: 14px;\n  height: 24px;\n  line-height: 24px;\n  text-transform: none;\n}\n.tox .tox-collection__item-caret {\n  align-items: center;\n  display: flex;\n  min-height: 24px;\n}\n.tox .tox-collection__item-caret::after {\n  content: '';\n  font-size: 0;\n  min-height: inherit;\n}\n.tox .tox-collection__item-caret svg {\n  fill: #222f3e;\n}\n.tox .tox-collection__item--state-disabled {\n  background-color: transparent;\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg {\n  display: none;\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory + .tox-collection__item-checkmark {\n  display: none;\n}\n.tox .tox-collection--horizontal {\n  background-color: #fff;\n  border: 1px solid #e3e3e3;\n  border-radius: 6px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n  margin-bottom: 0;\n  overflow-x: auto;\n  padding: 0;\n}\n.tox .tox-collection--horizontal .tox-collection__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: nowrap;\n  margin: 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item {\n  height: 28px;\n  margin: 6px 1px 5px 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item-label {\n  white-space: nowrap;\n}\n.tox .tox-collection--horizontal .tox-collection__item-caret {\n  margin-left: 4px;\n}\n.tox .tox-collection__item-container {\n  display: flex;\n}\n.tox .tox-collection__item-container--row {\n  align-items: center;\n  flex: 1 1 auto;\n  flex-direction: row;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-left {\n  margin-right: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-right {\n  justify-content: flex-end;\n  margin-left: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top {\n  align-items: flex-start;\n  margin-bottom: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle {\n  align-items: center;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom {\n  align-items: flex-end;\n  margin-top: auto;\n}\n.tox .tox-collection__item-container--column {\n  align-self: center;\n  flex: 1 1 auto;\n  flex-direction: column;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-left {\n  align-items: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-right {\n  align-items: flex-end;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top {\n  align-self: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle {\n  align-self: center;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom {\n  align-self: flex-end;\n}\n.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-right: 1px solid transparent;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-collection__item-accessory {\n  margin-left: 16px;\n  text-align: right;\n}\n.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret {\n  margin-left: 16px;\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-left: 1px solid transparent;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-collection__item-accessory {\n  margin-right: 16px;\n  text-align: left;\n}\n.tox[dir=rtl] .tox-collection .tox-collection__item-caret {\n  margin-right: 16px;\n  transform: rotateY(180deg);\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret {\n  margin-right: 4px;\n}\n.tox .tox-color-picker-container {\n  display: flex;\n  flex-direction: row;\n  height: 225px;\n  margin: 0;\n}\n.tox .tox-sv-palette {\n  box-sizing: border-box;\n  display: flex;\n  height: 100%;\n}\n.tox .tox-sv-palette-spectrum {\n  height: 100%;\n}\n.tox .tox-sv-palette,\n.tox .tox-sv-palette-spectrum {\n  width: 225px;\n}\n.tox .tox-sv-palette-thumb {\n  background: none;\n  border: 1px solid black;\n  border-radius: 50%;\n  box-sizing: content-box;\n  height: 12px;\n  position: absolute;\n  width: 12px;\n}\n.tox .tox-sv-palette-inner-thumb {\n  border: 1px solid white;\n  border-radius: 50%;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox .tox-hue-slider {\n  box-sizing: border-box;\n  height: 100%;\n  width: 25px;\n}\n.tox .tox-hue-slider-spectrum {\n  background: linear-gradient(to bottom, #f00, #ff0080, #f0f, #8000ff, #00f, #0080ff, #0ff, #00ff80, #0f0, #80ff00, #ff0, #ff8000, #f00);\n  height: 100%;\n  width: 100%;\n}\n.tox .tox-hue-slider,\n.tox .tox-hue-slider-spectrum {\n  width: 20px;\n}\n.tox .tox-hue-slider-thumb {\n  background: white;\n  border: 1px solid black;\n  box-sizing: content-box;\n  height: 4px;\n  width: 100%;\n}\n.tox .tox-rgb-form {\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n}\n.tox .tox-rgb-form div {\n  align-items: center;\n  display: flex;\n  justify-content: space-between;\n  margin-bottom: 5px;\n  width: inherit;\n}\n.tox .tox-rgb-form input {\n  width: 6em;\n}\n.tox .tox-rgb-form input.tox-invalid {\n  /* Need !important to override Chrome's focus styling unfortunately */\n  border: 1px solid red !important;\n}\n.tox .tox-rgb-form .tox-rgba-preview {\n  border: 1px solid black;\n  flex-grow: 2;\n  margin-bottom: 0;\n}\n.tox:not([dir=rtl]) .tox-sv-palette {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider-thumb {\n  margin-left: -1px;\n}\n.tox:not([dir=rtl]) .tox-rgb-form label {\n  margin-right: 0.5em;\n}\n.tox[dir=rtl] .tox-sv-palette {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider-thumb {\n  margin-right: -1px;\n}\n.tox[dir=rtl] .tox-rgb-form label {\n  margin-left: 0.5em;\n}\n.tox .tox-toolbar .tox-swatches,\n.tox .tox-toolbar__primary .tox-swatches,\n.tox .tox-toolbar__overflow .tox-swatches {\n  margin: 5px 0 6px 11px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-swatches-menu {\n  border: 0;\n  margin: -4px -4px;\n}\n.tox .tox-swatches__row {\n  display: flex;\n}\n.tox .tox-swatch {\n  height: 30px;\n  transition: transform 0.15s, box-shadow 0.15s;\n  width: 30px;\n}\n.tox .tox-swatch:hover,\n.tox .tox-swatch:focus {\n  box-shadow: 0 0 0 1px rgba(127, 127, 127, 0.3) inset;\n  transform: scale(0.8);\n}\n.tox .tox-swatch--remove {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n}\n.tox .tox-swatch--remove svg path {\n  stroke: #e74c3c;\n}\n.tox .tox-swatches__picker-btn {\n  align-items: center;\n  background-color: transparent;\n  border: 0;\n  cursor: pointer;\n  display: flex;\n  height: 30px;\n  justify-content: center;\n  outline: none;\n  padding: 0;\n  width: 30px;\n}\n.tox .tox-swatches__picker-btn svg {\n  fill: #222f3e;\n  height: 24px;\n  width: 24px;\n}\n.tox .tox-swatches__picker-btn:hover {\n  background: #cce2fa;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg {\n  display: none;\n  fill: #222f3e;\n  height: 24px;\n  margin: calc((30px - 24px) / 2) calc((30px - 24px) / 2);\n  width: 24px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg path {\n  fill: #fff;\n  paint-order: stroke;\n  stroke: #222f3e;\n  stroke-width: 2px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove)[aria-checked=\"true\"] svg {\n  display: block;\n}\n.tox:not([dir=rtl]) .tox-swatches__picker-btn {\n  margin-left: auto;\n}\n.tox[dir=rtl] .tox-swatches__picker-btn {\n  margin-right: auto;\n}\n.tox .tox-comment-thread {\n  background: #fff;\n  position: relative;\n}\n.tox .tox-comment-thread > *:not(:first-child) {\n  margin-top: 8px;\n}\n.tox .tox-comment {\n  background: #fff;\n  border: 1px solid #eeeeee;\n  border-radius: 6px;\n  box-shadow: 0 4px 8px 0 rgba(34, 47, 62, 0.1);\n  padding: 8px 8px 16px 8px;\n  position: relative;\n}\n.tox .tox-comment__header {\n  align-items: center;\n  color: #222f3e;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .tox-comment__date {\n  color: #222f3e;\n  font-size: 12px;\n  line-height: 18px;\n}\n.tox .tox-comment__body {\n  color: #222f3e;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin-top: 8px;\n  position: relative;\n  text-transform: initial;\n}\n.tox .tox-comment__body textarea {\n  resize: none;\n  white-space: normal;\n  width: 100%;\n}\n.tox .tox-comment__expander {\n  padding-top: 8px;\n}\n.tox .tox-comment__expander p {\n  color: rgba(34, 47, 62, 0.7);\n  font-size: 14px;\n  font-style: normal;\n}\n.tox .tox-comment__body p {\n  margin: 0;\n}\n.tox .tox-comment__buttonspacing {\n  padding-top: 16px;\n  text-align: center;\n}\n.tox .tox-comment-thread__overlay::after {\n  background: #fff;\n  bottom: 0;\n  content: \"\";\n  display: flex;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__reply {\n  display: flex;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 8px;\n}\n.tox .tox-comment__reply > *:first-child {\n  margin-bottom: 8px;\n  width: 100%;\n}\n.tox .tox-comment__edit {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 16px;\n}\n.tox .tox-comment__gradient::after {\n  background: linear-gradient(rgba(255, 255, 255, 0), #fff);\n  bottom: 0;\n  content: \"\";\n  display: block;\n  height: 5em;\n  margin-top: -40px;\n  position: absolute;\n  width: 100%;\n}\n.tox .tox-comment__overlay {\n  background: #fff;\n  bottom: 0;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  text-align: center;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__loading-text {\n  align-items: center;\n  color: #222f3e;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n}\n.tox .tox-comment__loading-text > div {\n  padding-bottom: 16px;\n}\n.tox .tox-comment__overlaytext {\n  bottom: 0;\n  flex-direction: column;\n  font-size: 14px;\n  left: 0;\n  padding: 1em;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 10;\n}\n.tox .tox-comment__overlaytext p {\n  background-color: #fff;\n  box-shadow: 0 0 8px 8px #fff;\n  color: #222f3e;\n  text-align: center;\n}\n.tox .tox-comment__overlaytext div:nth-of-type(2) {\n  font-size: 0.8em;\n}\n.tox .tox-comment__busy-spinner {\n  align-items: center;\n  background-color: #fff;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 20;\n}\n.tox .tox-comment__scroll {\n  display: flex;\n  flex-direction: column;\n  flex-shrink: 1;\n  overflow: auto;\n}\n.tox .tox-conversations {\n  margin: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__edit {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__buttonspacing > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__edit > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__reply > *:last-child {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-comment__edit {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-comment__buttonspacing > *:last-child,\n.tox[dir=rtl] .tox-comment__edit > *:last-child,\n.tox[dir=rtl] .tox-comment__reply > *:last-child {\n  margin-right: 8px;\n}\n.tox .tox-user {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-user__avatar svg {\n  fill: rgba(34, 47, 62, 0.7);\n}\n.tox .tox-user__avatar img {\n  border-radius: 50%;\n  height: 36px;\n  object-fit: cover;\n  vertical-align: middle;\n  width: 36px;\n}\n.tox .tox-user__name {\n  color: #222f3e;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  line-height: 18px;\n  text-transform: none;\n}\n.tox:not([dir=rtl]) .tox-user__avatar svg,\n.tox:not([dir=rtl]) .tox-user__avatar img {\n  margin-right: 8px;\n}\n.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar svg,\n.tox[dir=rtl] .tox-user__avatar img {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar + .tox-user__name {\n  margin-right: 8px;\n}\n.tox .tox-dialog-wrap {\n  align-items: center;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1100;\n}\n.tox .tox-dialog-wrap__backdrop {\n  background-color: rgba(255, 255, 255, 0.75);\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 1;\n}\n.tox .tox-dialog-wrap__backdrop--opaque {\n  background-color: #fff;\n}\n.tox .tox-dialog {\n  background-color: #fff;\n  border-color: #eeeeee;\n  border-radius: 10px;\n  border-style: solid;\n  border-width: 0px;\n  box-shadow: 0 16px 16px -10px rgba(34, 47, 62, 0.15), 0 0 40px 1px rgba(34, 47, 62, 0.15);\n  display: flex;\n  flex-direction: column;\n  max-height: 100%;\n  max-width: 480px;\n  overflow: hidden;\n  position: relative;\n  width: 95vw;\n  z-index: 2;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog {\n    align-self: flex-start;\n    margin: 8px auto;\n    max-height: calc(100vh - 8px * 2);\n    width: calc(100vw - 16px);\n  }\n}\n.tox .tox-dialog-inline {\n  z-index: 1100;\n}\n.tox .tox-dialog__header {\n  align-items: center;\n  background-color: #fff;\n  border-bottom: none;\n  color: #222f3e;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 16px 0 16px;\n  position: relative;\n}\n.tox .tox-dialog__header .tox-button {\n  z-index: 1;\n}\n.tox .tox-dialog__draghandle {\n  cursor: grab;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tox .tox-dialog__draghandle:active {\n  cursor: grabbing;\n}\n.tox .tox-dialog__dismiss {\n  margin-left: auto;\n}\n.tox .tox-dialog__title {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  text-transform: none;\n}\n.tox .tox-dialog__body {\n  color: #222f3e;\n  display: flex;\n  flex: 1;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  min-width: 0;\n  text-align: left;\n  text-transform: none;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body {\n    flex-direction: column;\n  }\n}\n.tox .tox-dialog__body-nav {\n  align-items: flex-start;\n  display: flex;\n  flex-direction: column;\n  padding: 16px 16px;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {\n    flex-direction: row;\n    -webkit-overflow-scrolling: touch;\n    overflow-x: auto;\n    padding-bottom: 0;\n  }\n}\n.tox .tox-dialog__body-nav-item {\n  border-bottom: 2px solid transparent;\n  color: rgba(34, 47, 62, 0.7);\n  display: inline-block;\n  font-size: 14px;\n  line-height: 1.3;\n  margin-bottom: 8px;\n  text-decoration: none;\n  white-space: nowrap;\n}\n.tox .tox-dialog__body-nav-item:focus {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.tox .tox-dialog__body-nav-item--active {\n  border-bottom: 2px solid #006ce7;\n  color: #006ce7;\n}\n.tox .tox-dialog__body-content {\n  box-sizing: border-box;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n  max-height: 650px;\n  overflow: auto;\n  -webkit-overflow-scrolling: touch;\n  padding: 16px 16px;\n}\n.tox .tox-dialog__body-content > * {\n  margin-bottom: 0;\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content > *:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content a {\n  color: #006ce7;\n  cursor: pointer;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:hover,\n.tox .tox-dialog__body-content a:focus {\n  color: #0054b4;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:active {\n  color: #0054b4;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content svg {\n  fill: #222f3e;\n}\n.tox .tox-dialog__body-content ul {\n  display: block;\n  list-style-type: disc;\n  margin-bottom: 16px;\n  margin-inline-end: 0;\n  margin-inline-start: 0;\n  padding-inline-start: 2.5rem;\n}\n.tox .tox-dialog__body-content .tox-form__group h1 {\n  color: #222f3e;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group h2 {\n  color: #222f3e;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group p {\n  margin-bottom: 16px;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:first-child,\n.tox .tox-dialog__body-content .tox-form__group h2:first-child,\n.tox .tox-dialog__body-content .tox-form__group p:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:last-child,\n.tox .tox-dialog__body-content .tox-form__group h2:last-child,\n.tox .tox-dialog__body-content .tox-form__group p:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:only-child,\n.tox .tox-dialog__body-content .tox-form__group h2:only-child,\n.tox .tox-dialog__body-content .tox-form__group p:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog--width-lg {\n  height: 650px;\n  max-width: 1200px;\n}\n.tox .tox-dialog--width-md {\n  max-width: 800px;\n}\n.tox .tox-dialog--width-md .tox-dialog__body-content {\n  overflow: auto;\n}\n.tox .tox-dialog__body-content--centered {\n  text-align: center;\n}\n.tox .tox-dialog__footer {\n  align-items: center;\n  background-color: #fff;\n  border-top: none;\n  display: flex;\n  justify-content: space-between;\n  padding: 8px 16px;\n}\n.tox .tox-dialog__footer-start,\n.tox .tox-dialog__footer-end {\n  display: flex;\n}\n.tox .tox-dialog__busy-spinner {\n  align-items: center;\n  background-color: rgba(255, 255, 255, 0.75);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 3;\n}\n.tox .tox-dialog__table {\n  border-collapse: collapse;\n  width: 100%;\n}\n.tox .tox-dialog__table thead th {\n  font-weight: bold;\n  padding-bottom: 8px;\n}\n.tox .tox-dialog__table tbody tr {\n  border-bottom: 1px solid #eeeeee;\n}\n.tox .tox-dialog__table tbody tr:last-child {\n  border-bottom: none;\n}\n.tox .tox-dialog__table td {\n  padding-bottom: 8px;\n  padding-top: 8px;\n}\n.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {\n  background: #fff;\n}\n.tox .tox-dialog__popups {\n  position: absolute;\n  width: 100%;\n  z-index: 1100;\n}\n.tox .tox-dialog__body-iframe {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-dialog__body-iframe .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox .tox-dialog-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox .tox-dialog-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox .tox-dialog-dock-transition {\n  transition: visibility 0s linear 0.3s, opacity 0.3s ease;\n}\n.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {\n  transition-delay: 0s;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {\n    margin-right: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {\n    margin-left: 8px;\n  }\n}\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-dialog__body {\n  text-align: right;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {\n    margin-left: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {\n    margin-right: 8px;\n  }\n}\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-right: 8px;\n}\nbody.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox .tox-dropzone-container {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dropzone {\n  align-items: center;\n  background: #fff;\n  border: 2px dashed #eeeeee;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  justify-content: center;\n  min-height: 100px;\n  padding: 10px;\n}\n.tox .tox-dropzone p {\n  color: rgba(34, 47, 62, 0.7);\n  margin: 0 0 16px 0;\n}\n.tox .tox-edit-area {\n  display: flex;\n  flex: 1;\n  overflow: hidden;\n  position: relative;\n}\n.tox .tox-edit-area__iframe {\n  background-color: #fff;\n  border: 0;\n  box-sizing: border-box;\n  flex: 1;\n  height: 100%;\n  position: absolute;\n  width: 100%;\n}\n.tox.tox-inline-edit-area {\n  border: 1px dotted #eeeeee;\n}\n.tox .tox-editor-container {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  overflow: hidden;\n}\n.tox .tox-editor-header {\n  display: grid;\n  grid-template-columns: 1fr min-content;\n  z-index: 1;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header {\n  background-color: #fff;\n  border-bottom: none;\n  box-shadow: 0 2px 2px -2px rgba(34, 47, 62, 0.1), 0 8px 8px -4px rgba(34, 47, 62, 0.07);\n  padding: 4px 0;\n  transition: box-shadow 0.5s;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-bottom .tox-editor-header {\n  border-top: 1px solid #e3e3e3;\n  box-shadow: none;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on .tox-editor-header {\n  background-color: #fff;\n  box-shadow: 0 2px 2px -2px rgba(34, 47, 62, 0.2), 0 8px 8px -4px rgba(34, 47, 62, 0.15);\n  padding: 4px 0;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on.tox-tinymce--toolbar-bottom .tox-editor-header {\n  box-shadow: 0 2px 2px -2px rgba(34, 47, 62, 0.2), 0 8px 8px -4px rgba(34, 47, 62, 0.15);\n}\n.tox.tox:not(.tox-tinymce-inline) .tox-editor-header.tox-editor-header--empty {\n  background: none;\n  border: none;\n  box-shadow: none;\n  padding: 0;\n}\n.tox-editor-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox-editor-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox-editor-dock-transition {\n  transition: visibility 0s linear 0.25s, opacity 0.25s ease;\n}\n.tox-editor-dock-transition.tox-editor-dock-fadein {\n  transition-delay: 0s;\n}\n.tox .tox-control-wrap {\n  flex: 1;\n  position: relative;\n}\n.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid {\n  display: none;\n}\n.tox .tox-control-wrap svg {\n  display: block;\n}\n.tox .tox-control-wrap__status-icon-wrap {\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-control-wrap__status-icon-invalid svg {\n  fill: #c00;\n}\n.tox .tox-control-wrap__status-icon-unknown svg {\n  fill: orange;\n}\n.tox .tox-control-wrap__status-icon-valid svg {\n  fill: green;\n}\n.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield {\n  padding-right: 32px;\n}\n.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap {\n  right: 4px;\n}\n.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield {\n  padding-left: 32px;\n}\n.tox[dir=rtl] .tox-control-wrap__status-icon-wrap {\n  left: 4px;\n}\n.tox .tox-autocompleter {\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-menu {\n  box-sizing: border-box;\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-autocompleter-highlight {\n  font-weight: bold;\n}\n.tox .tox-color-input {\n  display: flex;\n  position: relative;\n  z-index: 1;\n}\n.tox .tox-color-input .tox-textfield {\n  z-index: -1;\n}\n.tox .tox-color-input span {\n  border-color: rgba(34, 47, 62, 0.2);\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  height: 24px;\n  position: absolute;\n  top: 6px;\n  width: 24px;\n}\n.tox .tox-color-input span:hover:not([aria-disabled=true]),\n.tox .tox-color-input span:focus:not([aria-disabled=true]) {\n  border-color: #006ce7;\n  cursor: pointer;\n}\n.tox .tox-color-input span::before {\n  background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(-45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%), linear-gradient(-45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%);\n  background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n  background-size: 12px 12px;\n  border: 1px solid #fff;\n  border-radius: 6px;\n  box-sizing: border-box;\n  content: '';\n  height: 24px;\n  left: -1px;\n  position: absolute;\n  top: -1px;\n  width: 24px;\n  z-index: -1;\n}\n.tox .tox-color-input span[aria-disabled=true] {\n  cursor: not-allowed;\n}\n.tox:not([dir=rtl]) .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-color-input .tox-textfield {\n  padding-left: 36px;\n}\n.tox:not([dir=rtl]) .tox-color-input span {\n  left: 6px;\n}\n.tox[dir=\"rtl\"] .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=\"rtl\"] .tox-color-input .tox-textfield {\n  padding-right: 36px;\n}\n.tox[dir=\"rtl\"] .tox-color-input span {\n  right: 6px;\n}\n.tox .tox-label,\n.tox .tox-toolbar-label {\n  color: rgba(34, 47, 62, 0.7);\n  display: block;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  padding: 0 8px 0 0;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-toolbar-label {\n  padding: 0 8px;\n}\n.tox[dir=rtl] .tox-label {\n  padding: 0 0 0 8px;\n}\n.tox .tox-form {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group {\n  box-sizing: border-box;\n  margin-bottom: 4px;\n}\n.tox .tox-form-group--maximize {\n  flex: 1;\n}\n.tox .tox-form__group--error {\n  color: #c00;\n}\n.tox .tox-form__group--collection {\n  display: flex;\n}\n.tox .tox-form__grid {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: space-between;\n}\n.tox .tox-form__grid--2col > .tox-form__group {\n  width: calc(50% - (8px / 2));\n}\n.tox .tox-form__grid--3col > .tox-form__group {\n  width: calc(100% / 3 - (8px / 2));\n}\n.tox .tox-form__grid--4col > .tox-form__group {\n  width: calc(25% - (8px / 2));\n}\n.tox .tox-form__controls-h-stack {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--inline {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--stretched {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group--stretched .tox-textarea {\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox:not([dir=rtl]) .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-lock.tox-locked .tox-lock-icon__unlock,\n.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock {\n  display: none;\n}\n.tox .tox-textfield,\n.tox .tox-toolbar-textfield,\n.tox .tox-listboxfield .tox-listbox--select,\n.tox .tox-textarea {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #fff;\n  border-color: #eeeeee;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #222f3e;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 5.5px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-textfield[disabled],\n.tox .tox-textarea[disabled] {\n  background-color: #f2f2f2;\n  color: rgba(34, 47, 62, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-textfield:focus,\n.tox .tox-listboxfield .tox-listbox--select:focus,\n.tox .tox-textarea:focus {\n  background-color: #fff;\n  border-color: #006ce7;\n  box-shadow: 0 0 0 2px rgba(0, 108, 231, 0.25);\n  outline: none;\n}\n.tox .tox-toolbar-textfield {\n  border-width: 0;\n  margin-bottom: 3px;\n  margin-top: 2px;\n  max-width: 250px;\n}\n.tox .tox-naked-btn {\n  background-color: transparent;\n  border: 0;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #006ce7;\n  cursor: pointer;\n  display: block;\n  margin: 0;\n  padding: 0;\n}\n.tox .tox-naked-btn svg {\n  display: block;\n  fill: #222f3e;\n}\n.tox:not([dir=rtl]) .tox-toolbar-textfield + * {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-toolbar-textfield + * {\n  margin-right: 4px;\n}\n.tox .tox-listboxfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-listboxfield .tox-listbox--select[disabled] {\n  background-color: #f2f2f2;\n  color: rgba(34, 47, 62, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-listbox__select-label {\n  cursor: default;\n  flex: 1;\n  margin: 0 4px;\n}\n.tox .tox-listbox__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-listbox__select-chevron svg {\n  fill: #222f3e;\n}\n.tox .tox-listboxfield .tox-listbox--select {\n  align-items: center;\n  display: flex;\n}\n.tox:not([dir=rtl]) .tox-listboxfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-listboxfield svg {\n  left: 8px;\n}\n.tox .tox-selectfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-selectfield select {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #fff;\n  border-color: #eeeeee;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #222f3e;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 5.5px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-selectfield select[disabled] {\n  background-color: #f2f2f2;\n  color: rgba(34, 47, 62, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-selectfield select::-ms-expand {\n  display: none;\n}\n.tox .tox-selectfield select:focus {\n  background-color: #fff;\n  border-color: #006ce7;\n  box-shadow: 0 0 0 2px rgba(0, 108, 231, 0.25);\n  outline: none;\n}\n.tox .tox-selectfield svg {\n  pointer-events: none;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"0\"],\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"1\"] {\n  padding-right: 24px;\n}\n.tox:not([dir=rtl]) .tox-selectfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-selectfield select[size=\"0\"],\n.tox[dir=rtl] .tox-selectfield select[size=\"1\"] {\n  padding-left: 24px;\n}\n.tox[dir=rtl] .tox-selectfield svg {\n  left: 8px;\n}\n.tox .tox-textarea {\n  -webkit-appearance: textarea;\n     -moz-appearance: textarea;\n          appearance: textarea;\n  white-space: pre-wrap;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n.tox .tox-help__more-link {\n  list-style: none;\n  margin-top: 1em;\n}\n.tox .tox-imagepreview {\n  background-color: #666;\n  height: 380px;\n  overflow: hidden;\n  position: relative;\n  width: 100%;\n}\n.tox .tox-imagepreview.tox-imagepreview__loaded {\n  overflow: auto;\n}\n.tox .tox-imagepreview__container {\n  display: flex;\n  left: 100vw;\n  position: absolute;\n  top: 100vw;\n}\n.tox .tox-imagepreview__image {\n  background: url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==);\n}\n.tox .tox-image-tools .tox-spacer {\n  flex: 1;\n}\n.tox .tox-image-tools .tox-bar {\n  align-items: center;\n  display: flex;\n  height: 60px;\n  justify-content: center;\n}\n.tox .tox-image-tools .tox-imagepreview,\n.tox .tox-image-tools .tox-imagepreview + .tox-bar {\n  margin-top: 8px;\n}\n.tox .tox-image-tools .tox-croprect-block {\n  background: black;\n  filter: alpha(opacity=50);\n  opacity: 0.5;\n  position: absolute;\n  zoom: 1;\n}\n.tox .tox-image-tools .tox-croprect-handle {\n  border: 2px solid white;\n  height: 20px;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 20px;\n}\n.tox .tox-image-tools .tox-croprect-handle-move {\n  border: 0;\n  cursor: move;\n  position: absolute;\n}\n.tox .tox-image-tools .tox-croprect-handle-nw {\n  border-width: 2px 0 0 2px;\n  cursor: nw-resize;\n  left: 100px;\n  margin: -2px 0 0 -2px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-ne {\n  border-width: 2px 2px 0 0;\n  cursor: ne-resize;\n  left: 200px;\n  margin: -2px 0 0 -20px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-sw {\n  border-width: 0 0 2px 2px;\n  cursor: sw-resize;\n  left: 100px;\n  margin: -20px 2px 0 -2px;\n  top: 200px;\n}\n.tox .tox-image-tools .tox-croprect-handle-se {\n  border-width: 0 2px 2px 0;\n  cursor: se-resize;\n  left: 200px;\n  margin: -20px 0 0 -20px;\n  top: 200px;\n}\n.tox .tox-insert-table-picker {\n  display: flex;\n  flex-wrap: wrap;\n  width: 170px;\n}\n.tox .tox-insert-table-picker > div {\n  border-color: #eeeeee;\n  border-style: solid;\n  border-width: 0 1px 1px 0;\n  box-sizing: border-box;\n  height: 17px;\n  width: 17px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker {\n  margin: -4px -4px;\n}\n.tox .tox-insert-table-picker .tox-insert-table-picker__selected {\n  background-color: rgba(0, 108, 231, 0.5);\n  border-color: rgba(0, 108, 231, 0.5);\n}\n.tox .tox-insert-table-picker__label {\n  color: rgba(34, 47, 62, 0.7);\n  display: block;\n  font-size: 14px;\n  padding: 4px;\n  text-align: center;\n  width: 100%;\n}\n.tox:not([dir=rtl]) {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-insert-table-picker > div:nth-child(10n) {\n  border-right: 0;\n}\n.tox[dir=rtl] {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=rtl] .tox-insert-table-picker > div:nth-child(10n+1) {\n  border-right: 0;\n}\n.tox {\n  /* stylelint-disable */\n  /* stylelint-enable */\n}\n.tox .tox-menu {\n  background-color: #fff;\n  border: 1px solid transparent;\n  border-radius: 6px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  display: inline-block;\n  overflow: hidden;\n  vertical-align: top;\n  z-index: 1150;\n}\n.tox .tox-menu.tox-collection.tox-collection--list {\n  padding: 0 4px;\n}\n.tox .tox-menu.tox-collection.tox-collection--toolbar {\n  padding: 8px;\n}\n.tox .tox-menu.tox-collection.tox-collection--grid {\n  padding: 8px;\n}\n@media only screen and (min-width: 768px ) {\n  .tox .tox-menu .tox-collection__item-label {\n    overflow-wrap: break-word;\n    word-break: normal;\n  }\n}\n.tox .tox-menu__label h1,\n.tox .tox-menu__label h2,\n.tox .tox-menu__label h3,\n.tox .tox-menu__label h4,\n.tox .tox-menu__label h5,\n.tox .tox-menu__label h6,\n.tox .tox-menu__label p,\n.tox .tox-menu__label blockquote,\n.tox .tox-menu__label code {\n  margin: 0;\n}\n.tox .tox-menubar {\n  background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;\n  background-color: #fff;\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  grid-column: 1 / -1;\n  grid-row: 1;\n  padding: 0 11px 0 12px;\n}\n.tox .tox-promotion + .tox-menubar {\n  grid-column: 1;\n}\n.tox .tox-promotion {\n  background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;\n  background-color: #fff;\n  grid-column: 2;\n  grid-row: 1;\n  padding-inline-end: 8px;\n  padding-inline-start: 4px;\n  padding-top: 5px;\n}\n.tox .tox-promotion-link {\n  align-items: unsafe center;\n  background-color: #E8F1F8;\n  border-radius: 5px;\n  color: #086BE6;\n  cursor: pointer;\n  display: flex;\n  font-size: 14px;\n  height: 26.6px;\n  padding: 4px 8px;\n  white-space: nowrap;\n}\n.tox .tox-promotion-link:hover {\n  background-color: #B4D7FF;\n}\n.tox .tox-promotion-link:focus {\n  background-color: #D9EDF7;\n}\n/* Deprecated. Remove in next major release */\n.tox .tox-mbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #222f3e;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 28px;\n  justify-content: center;\n  margin: 5px 1px 6px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0 4px;\n  text-transform: none;\n  width: auto;\n}\n.tox .tox-mbtn[disabled] {\n  background-color: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-mbtn:focus:not(:disabled) {\n  background: #cce2fa;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-mbtn--active {\n  background: #a6ccf7;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active) {\n  background: #cce2fa;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-mbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-mbtn[disabled] .tox-mbtn__select-label {\n  cursor: not-allowed;\n}\n.tox .tox-mbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n  display: none;\n}\n.tox .tox-notification {\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: grid;\n  font-size: 14px;\n  font-weight: normal;\n  grid-template-columns: minmax(40px, 1fr) auto minmax(40px, 1fr);\n  margin-top: 4px;\n  opacity: 0;\n  padding: 4px;\n  transition: transform 100ms ease-in, opacity 150ms ease-in;\n}\n.tox .tox-notification p {\n  font-size: 14px;\n  font-weight: normal;\n}\n.tox .tox-notification a {\n  cursor: pointer;\n  text-decoration: underline;\n}\n.tox .tox-notification--in {\n  opacity: 1;\n}\n.tox .tox-notification--success {\n  background-color: #e4eeda;\n  border-color: #d7e6c8;\n  color: #222f3e;\n}\n.tox .tox-notification--success p {\n  color: #222f3e;\n}\n.tox .tox-notification--success a {\n  color: #517342;\n}\n.tox .tox-notification--success svg {\n  fill: #222f3e;\n}\n.tox .tox-notification--error {\n  background-color: #f5cccc;\n  border-color: #f0b3b3;\n  color: #222f3e;\n}\n.tox .tox-notification--error p {\n  color: #222f3e;\n}\n.tox .tox-notification--error a {\n  color: #77181f;\n}\n.tox .tox-notification--error svg {\n  fill: #222f3e;\n}\n.tox .tox-notification--warn,\n.tox .tox-notification--warning {\n  background-color: #fff5cc;\n  border-color: #fff0b3;\n  color: #222f3e;\n}\n.tox .tox-notification--warn p,\n.tox .tox-notification--warning p {\n  color: #222f3e;\n}\n.tox .tox-notification--warn a,\n.tox .tox-notification--warning a {\n  color: #7a6e25;\n}\n.tox .tox-notification--warn svg,\n.tox .tox-notification--warning svg {\n  fill: #222f3e;\n}\n.tox .tox-notification--info {\n  background-color: #d6e7fb;\n  border-color: #c1dbf9;\n  color: #222f3e;\n}\n.tox .tox-notification--info p {\n  color: #222f3e;\n}\n.tox .tox-notification--info a {\n  color: #2a64a6;\n}\n.tox .tox-notification--info svg {\n  fill: #222f3e;\n}\n.tox .tox-notification__body {\n  align-self: center;\n  color: #222f3e;\n  font-size: 14px;\n  grid-column-end: 3;\n  grid-column-start: 2;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  text-align: center;\n  white-space: normal;\n  word-break: break-all;\n  word-break: break-word;\n}\n.tox .tox-notification__body > * {\n  margin: 0;\n}\n.tox .tox-notification__body > * + * {\n  margin-top: 1rem;\n}\n.tox .tox-notification__icon {\n  align-self: center;\n  grid-column-end: 2;\n  grid-column-start: 1;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification__icon svg {\n  display: block;\n}\n.tox .tox-notification__dismiss {\n  align-self: start;\n  grid-column-end: 4;\n  grid-column-start: 3;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification .tox-progress-bar {\n  grid-column-end: 4;\n  grid-column-start: 1;\n  grid-row-end: 3;\n  grid-row-start: 2;\n  justify-self: center;\n}\n.tox .tox-pop {\n  display: inline-block;\n  position: relative;\n}\n.tox .tox-pop--resizing {\n  transition: width 0.1s ease;\n}\n.tox .tox-pop--resizing .tox-toolbar,\n.tox .tox-pop--resizing .tox-toolbar__group {\n  flex-wrap: nowrap;\n}\n.tox .tox-pop--transition {\n  transition: 0.15s ease;\n  transition-property: left, right, top, bottom;\n}\n.tox .tox-pop--transition::before,\n.tox .tox-pop--transition::after {\n  transition: all 0.15s, visibility 0s, opacity 0.075s ease 0.075s;\n}\n.tox .tox-pop__dialog {\n  background-color: #fff;\n  border: 1px solid #eeeeee;\n  border-radius: 6px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  min-width: 0;\n  overflow: hidden;\n}\n.tox .tox-pop__dialog > *:not(.tox-toolbar) {\n  margin: 4px 4px 4px 8px;\n}\n.tox .tox-pop__dialog .tox-toolbar {\n  background-color: transparent;\n  margin-bottom: -1px;\n}\n.tox .tox-pop::before,\n.tox .tox-pop::after {\n  border-style: solid;\n  content: '';\n  display: block;\n  height: 0;\n  opacity: 1;\n  position: absolute;\n  width: 0;\n}\n.tox .tox-pop.tox-pop--inset::before,\n.tox .tox-pop.tox-pop--inset::after {\n  opacity: 0;\n  transition: all 0s 0.15s, visibility 0s, opacity 0.075s ease;\n}\n.tox .tox-pop.tox-pop--bottom::before,\n.tox .tox-pop.tox-pop--bottom::after {\n  left: 50%;\n  top: 100%;\n}\n.tox .tox-pop.tox-pop--bottom::after {\n  border-color: #fff transparent transparent transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: -1px;\n}\n.tox .tox-pop.tox-pop--bottom::before {\n  border-color: #eeeeee transparent transparent transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--top::before,\n.tox .tox-pop.tox-pop--top::after {\n  left: 50%;\n  top: 0;\n  transform: translateY(-100%);\n}\n.tox .tox-pop.tox-pop--top::after {\n  border-color: transparent transparent #fff transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: 1px;\n}\n.tox .tox-pop.tox-pop--top::before {\n  border-color: transparent transparent #eeeeee transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--left::before,\n.tox .tox-pop.tox-pop--left::after {\n  left: 0;\n  top: calc(50% - 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--left::after {\n  border-color: transparent #fff transparent transparent;\n  border-width: 8px;\n  margin-left: -15px;\n}\n.tox .tox-pop.tox-pop--left::before {\n  border-color: transparent #eeeeee transparent transparent;\n  border-width: 10px;\n  margin-left: -19px;\n}\n.tox .tox-pop.tox-pop--right::before,\n.tox .tox-pop.tox-pop--right::after {\n  left: 100%;\n  top: calc(50% + 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--right::after {\n  border-color: transparent transparent transparent #fff;\n  border-width: 8px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--right::before {\n  border-color: transparent transparent transparent #eeeeee;\n  border-width: 10px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--align-left::before,\n.tox .tox-pop.tox-pop--align-left::after {\n  left: 20px;\n}\n.tox .tox-pop.tox-pop--align-right::before,\n.tox .tox-pop.tox-pop--align-right::after {\n  left: calc(100% - 20px);\n}\n.tox .tox-sidebar-wrap {\n  display: flex;\n  flex-direction: row;\n  flex-grow: 1;\n  min-height: 0;\n}\n.tox .tox-sidebar {\n  background-color: #fff;\n  display: flex;\n  flex-direction: row;\n  justify-content: flex-end;\n}\n.tox .tox-sidebar__slider {\n  display: flex;\n  overflow: hidden;\n}\n.tox .tox-sidebar__pane-container {\n  display: flex;\n}\n.tox .tox-sidebar__pane {\n  display: flex;\n}\n.tox .tox-sidebar--sliding-closed {\n  opacity: 0;\n}\n.tox .tox-sidebar--sliding-open {\n  opacity: 1;\n}\n.tox .tox-sidebar--sliding-growing,\n.tox .tox-sidebar--sliding-shrinking {\n  transition: width 0.5s ease, opacity 0.5s ease;\n}\n.tox .tox-selector {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  display: inline-block;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox.tox-platform-touch .tox-selector {\n  height: 12px;\n  width: 12px;\n}\n.tox .tox-slider {\n  align-items: center;\n  display: flex;\n  flex: 1;\n  height: 24px;\n  justify-content: center;\n  position: relative;\n}\n.tox .tox-slider__rail {\n  background-color: transparent;\n  border: 1px solid #eeeeee;\n  border-radius: 6px;\n  height: 10px;\n  min-width: 120px;\n  width: 100%;\n}\n.tox .tox-slider__handle {\n  background-color: #006ce7;\n  border: 2px solid #0054b4;\n  border-radius: 6px;\n  box-shadow: none;\n  height: 24px;\n  left: 50%;\n  position: absolute;\n  top: 50%;\n  transform: translateX(-50%) translateY(-50%);\n  width: 14px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider:not(:first-of-type) {\n  margin-inline-start: 8px;\n}\n.tox .tox-form__controls-h-stack > .tox-form__group + .tox-slider {\n  margin-inline-start: 32px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider + .tox-form__group {\n  margin-inline-start: 32px;\n}\n.tox .tox-source-code {\n  overflow: auto;\n}\n.tox .tox-spinner {\n  display: flex;\n}\n.tox .tox-spinner > div {\n  animation: tam-bouncing-dots 1.5s ease-in-out 0s infinite both;\n  background-color: rgba(34, 47, 62, 0.7);\n  border-radius: 100%;\n  height: 8px;\n  width: 8px;\n}\n.tox .tox-spinner > div:nth-child(1) {\n  animation-delay: -0.32s;\n}\n.tox .tox-spinner > div:nth-child(2) {\n  animation-delay: -0.16s;\n}\n@keyframes tam-bouncing-dots {\n  0%,\n  80%,\n  100% {\n    transform: scale(0);\n  }\n  40% {\n    transform: scale(1);\n  }\n}\n.tox:not([dir=rtl]) .tox-spinner > div:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-spinner > div:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-statusbar {\n  align-items: center;\n  background-color: #fff;\n  border-top: 1px solid #e3e3e3;\n  color: rgba(34, 47, 62, 0.7);\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-weight: normal;\n  height: 25px;\n  overflow: hidden;\n  padding: 0 8px;\n  position: relative;\n  text-transform: none;\n}\n.tox .tox-statusbar__text-container {\n  display: flex;\n  flex: 1 1 auto;\n  justify-content: flex-end;\n  overflow: hidden;\n}\n.tox .tox-statusbar__path {\n  display: flex;\n  flex: 1 1 auto;\n  margin-right: auto;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__path > * {\n  display: inline;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__wordcount {\n  flex: 0 0 auto;\n  margin-left: 1ch;\n}\n.tox .tox-statusbar a,\n.tox .tox-statusbar__path-item,\n.tox .tox-statusbar__wordcount {\n  color: rgba(34, 47, 62, 0.7);\n  text-decoration: none;\n}\n.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) {\n  color: #222f3e;\n  cursor: pointer;\n}\n.tox .tox-statusbar__branding svg {\n  fill: rgba(34, 47, 62, 0.8);\n  height: 1.14em;\n  vertical-align: -0.28em;\n  width: 3.6em;\n}\n.tox .tox-statusbar__branding a:hover:not(:disabled):not([aria-disabled=true]) svg,\n.tox .tox-statusbar__branding a:focus:not(:disabled):not([aria-disabled=true]) svg {\n  fill: #222f3e;\n}\n.tox .tox-statusbar__resize-handle {\n  align-items: flex-end;\n  align-self: stretch;\n  cursor: nwse-resize;\n  display: flex;\n  flex: 0 0 auto;\n  justify-content: flex-end;\n  margin-left: auto;\n  margin-right: -8px;\n  padding-bottom: 3px;\n  padding-left: 1ch;\n  padding-right: 3px;\n}\n.tox .tox-statusbar__resize-handle svg {\n  display: block;\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-statusbar__resize-handle:focus svg {\n  background-color: #dee0e2;\n  border-radius: 1px 1px 5px 1px;\n  box-shadow: 0 0 0 2px #dee0e2;\n}\n.tox:not([dir=rtl]) .tox-statusbar__path > * {\n  margin-right: 4px;\n}\n.tox:not([dir=rtl]) .tox-statusbar__branding {\n  margin-left: 2ch;\n}\n.tox[dir=rtl] .tox-statusbar {\n  flex-direction: row-reverse;\n}\n.tox[dir=rtl] .tox-statusbar__path > * {\n  margin-left: 4px;\n}\n.tox .tox-throbber {\n  z-index: 1299;\n}\n.tox .tox-throbber__busy-spinner {\n  align-items: center;\n  background-color: rgba(255, 255, 255, 0.6);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n.tox .tox-tbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #222f3e;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 28px;\n  justify-content: center;\n  margin: 6px 1px 5px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0;\n  text-transform: none;\n  width: 34px;\n}\n.tox .tox-tbtn svg {\n  display: block;\n  fill: #222f3e;\n}\n.tox .tox-tbtn.tox-tbtn-more {\n  padding-left: 5px;\n  padding-right: 5px;\n  width: inherit;\n}\n.tox .tox-tbtn:focus {\n  background: #cce2fa;\n  border: 0;\n  box-shadow: none;\n}\n.tox .tox-tbtn:hover {\n  background: #cce2fa;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-tbtn:hover svg {\n  fill: #222f3e;\n}\n.tox .tox-tbtn:active {\n  background: #a6ccf7;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-tbtn:active svg {\n  fill: #222f3e;\n}\n.tox .tox-tbtn--disabled,\n.tox .tox-tbtn--disabled:hover,\n.tox .tox-tbtn:disabled,\n.tox .tox-tbtn:disabled:hover {\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-tbtn--disabled svg,\n.tox .tox-tbtn--disabled:hover svg,\n.tox .tox-tbtn:disabled svg,\n.tox .tox-tbtn:disabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-tbtn--enabled,\n.tox .tox-tbtn--enabled:hover {\n  background: #a6ccf7;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-tbtn--enabled > *,\n.tox .tox-tbtn--enabled:hover > * {\n  transform: none;\n}\n.tox .tox-tbtn--enabled svg,\n.tox .tox-tbtn--enabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: #222f3e;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) {\n  color: #222f3e;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg {\n  fill: #222f3e;\n}\n.tox .tox-tbtn:active > * {\n  transform: none;\n}\n.tox .tox-tbtn--md {\n  height: 42px;\n  width: 51px;\n}\n.tox .tox-tbtn--lg {\n  flex-direction: column;\n  height: 56px;\n  width: 68px;\n}\n.tox .tox-tbtn--return {\n  align-self: stretch;\n  height: unset;\n  width: 16px;\n}\n.tox .tox-tbtn--labeled {\n  padding: 0 4px;\n  width: unset;\n}\n.tox .tox-tbtn__vlabel {\n  display: block;\n  font-size: 10px;\n  font-weight: normal;\n  letter-spacing: -0.025em;\n  margin-bottom: 4px;\n  white-space: nowrap;\n}\n.tox .tox-tbtn--select {\n  margin: 6px 1px 5px 0;\n  padding: 0 4px;\n  width: auto;\n}\n.tox .tox-tbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-tbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-tbtn__select-chevron svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-tbtn--bespoke {\n  background: #f7f7f7;\n}\n.tox .tox-tbtn--bespoke + .tox-tbtn--bespoke {\n  margin-inline-start: 4px;\n}\n.tox .tox-tbtn--bespoke .tox-tbtn__select-label {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  width: 7em;\n}\n.tox .tox-split-button {\n  border: 0;\n  border-radius: 3px;\n  box-sizing: border-box;\n  display: flex;\n  margin: 6px 1px 5px 0;\n  overflow: hidden;\n}\n.tox .tox-split-button:hover {\n  box-shadow: 0 0 0 1px #cce2fa inset;\n}\n.tox .tox-split-button:focus {\n  background: #cce2fa;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-split-button > * {\n  border-radius: 0;\n}\n.tox .tox-split-button__chevron {\n  width: 16px;\n}\n.tox .tox-split-button__chevron svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-split-button .tox-tbtn {\n  margin: 0;\n}\n.tox .tox-split-button.tox-tbtn--disabled:hover,\n.tox .tox-split-button.tox-tbtn--disabled:focus,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus {\n  background: transparent;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn--select {\n  padding: 0 0px;\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn:not(.tox-tbtn--select):first-child {\n  width: 30px;\n}\n.tox.tox-platform-touch .tox-split-button__chevron {\n  width: 20px;\n}\n.tox .tox-toolbar-overlord {\n  background-color: #fff;\n}\n.tox .tox-toolbar,\n.tox .tox-toolbar__primary,\n.tox .tox-toolbar__overflow {\n  background-attachment: local;\n  background-color: #fff;\n  background-image: repeating-linear-gradient(#e3e3e3 0px 1px, transparent 1px 39px);\n  background-position: center top 40px;\n  background-repeat: no-repeat;\n  background-size: calc(100% - 11px * 2) calc(100% - 41px);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  padding: 0 0px;\n  transform: perspective(1px);\n}\n.tox .tox-toolbar-overlord > .tox-toolbar,\n.tox .tox-toolbar-overlord > .tox-toolbar__primary,\n.tox .tox-toolbar-overlord > .tox-toolbar__overflow {\n  background-position: center top 0px;\n  background-size: calc(100% - 11px * 2) calc(100% - 0px);\n}\n.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed {\n  height: 0;\n  opacity: 0;\n  padding-bottom: 0;\n  padding-top: 0;\n  visibility: hidden;\n}\n.tox .tox-toolbar__overflow--growing {\n  transition: height 0.3s ease, opacity 0.2s linear 0.1s;\n}\n.tox .tox-toolbar__overflow--shrinking {\n  transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;\n}\n.tox .tox-toolbar-overlord,\n.tox .tox-anchorbar {\n  grid-column: 1 / -1;\n}\n.tox .tox-menubar + .tox-toolbar,\n.tox .tox-menubar + .tox-toolbar-overlord {\n  border-top: 1px solid transparent;\n  margin-top: -1px;\n  padding-bottom: 1px;\n  padding-top: 1px;\n}\n.tox .tox-toolbar--scrolling {\n  flex-wrap: nowrap;\n  overflow-x: auto;\n}\n.tox .tox-pop .tox-toolbar {\n  border-width: 0;\n}\n.tox .tox-toolbar--no-divider {\n  background-image: none;\n}\n.tox .tox-toolbar-overlord .tox-toolbar:not(.tox-toolbar--scrolling):first-child,\n.tox .tox-toolbar-overlord .tox-toolbar__primary {\n  background-position: center top 39px;\n}\n.tox .tox-editor-header > .tox-toolbar--scrolling,\n.tox .tox-toolbar-overlord .tox-toolbar--scrolling:first-child {\n  background-image: none;\n}\n.tox.tox-tinymce-aux .tox-toolbar__overflow {\n  background-color: #fff;\n  background-position: center top 43px;\n  background-size: calc(100% - 8px * 2) calc(100% - 51px);\n  border: none;\n  border-radius: 6px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  overscroll-behavior: none;\n  padding: 4px 0;\n}\n.tox-pop .tox-pop__dialog {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox-pop .tox-pop__dialog .tox-toolbar {\n  background-position: center top 43px;\n  background-size: calc(100% - 11px * 2) calc(100% - 51px);\n  padding: 4px 0;\n}\n.tox .tox-toolbar__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: wrap;\n  margin: 0 0;\n  padding: 0 11px 0 12px;\n}\n.tox .tox-toolbar__group--pull-right {\n  margin-left: auto;\n}\n.tox .tox-toolbar--scrolling .tox-toolbar__group {\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n}\n.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type) {\n  border-right: 1px solid transparent;\n}\n.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type) {\n  border-left: 1px solid transparent;\n}\n.tox .tox-tooltip {\n  display: inline-block;\n  padding: 8px;\n  position: relative;\n}\n.tox .tox-tooltip__body {\n  background-color: #222f3e;\n  border-radius: 6px;\n  box-shadow: 0 2px 4px rgba(34, 47, 62, 0.3);\n  color: rgba(255, 255, 255, 0.75);\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  padding: 4px 8px;\n  text-transform: none;\n}\n.tox .tox-tooltip__arrow {\n  position: absolute;\n}\n.tox .tox-tooltip--down .tox-tooltip__arrow {\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  border-top: 8px solid #222f3e;\n  bottom: 0;\n  left: 50%;\n  position: absolute;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--up .tox-tooltip__arrow {\n  border-bottom: 8px solid #222f3e;\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  left: 50%;\n  position: absolute;\n  top: 0;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--right .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-left: 8px solid #222f3e;\n  border-top: 8px solid transparent;\n  position: absolute;\n  right: 0;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-tooltip--left .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-right: 8px solid #222f3e;\n  border-top: 8px solid transparent;\n  left: 0;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-view-wrap,\n.tox .tox-view-wrap__slot-container {\n  background-color: #fff;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view__header {\n  align-items: center;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 8px 0 8px;\n  position: relative;\n}\n.tox .tox-view__header-start,\n.tox .tox-view__header-end {\n  display: flex;\n}\n.tox .tox-view__pane {\n  height: 100%;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-view__pane_panel {\n  border: 1px solid #eeeeee;\n  border-radius: 6px;\n}\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-start > *,\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-view__header .tox-view__header-start > *,\n.tox[dir=rtl] .tox-view__header .tox-view__header-end > * {\n  margin-right: 8px;\n}\n.tox .tox-well {\n  border: 1px solid #eeeeee;\n  border-radius: 6px;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-well > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-well > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-well > *:only-child {\n  margin: 0;\n}\n.tox .tox-custom-editor {\n  border: 1px solid #eeeeee;\n  border-radius: 6px;\n  display: flex;\n  flex: 1;\n  position: relative;\n}\n/* stylelint-disable */\n.tox {\n  /* stylelint-enable */\n}\n.tox .tox-dialog-loading::before {\n  background-color: rgba(0, 0, 0, 0.5);\n  content: \"\";\n  height: 100%;\n  position: absolute;\n  width: 100%;\n  z-index: 1000;\n}\n.tox .tox-tab {\n  cursor: pointer;\n}\n.tox .tox-dialog__content-js {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-content .tox-collection {\n  display: flex;\n  flex: 1;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide/skin.shadowdom.css",
    "content": "body.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide-dark/content.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%236d737b%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * Dracula Theme originally by Zeno Rocha [@zenorocha]\n * https://draculatheme.com/\n *\n * Ported for PrismJS by Albert Vallverdu [@byverdu]\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: #f8f8f2;\n  background: none;\n  text-shadow: 0 1px rgba(0, 0, 0, 0.3);\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n  border-radius: 0.3em;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #282a36;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: #6272a4;\n}\n.token.punctuation {\n  color: #f8f8f2;\n}\n.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #ff79c6;\n}\n.token.boolean,\n.token.number {\n  color: #bd93f9;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #50fa7b;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string,\n.token.variable {\n  color: #f8f8f2;\n}\n.token.atrule,\n.token.attr-value,\n.token.function,\n.token.class-name {\n  color: #f1fa8c;\n}\n.token.keyword {\n  color: #8be9fd;\n}\n.token.regex,\n.token.important {\n  color: #ffb86c;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.3);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.3);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #4099ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #4099ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #4099ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid transparent;\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: lighten;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #4099ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\nbody {\n  font-family: sans-serif;\n}\ntable {\n  border-collapse: collapse;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide-dark/content.inline.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: black;\n  background: none;\n  text-shadow: 0 1px white;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"]::-moz-selection,\npre[class*=\"language-\"] ::-moz-selection,\ncode[class*=\"language-\"]::-moz-selection,\ncode[class*=\"language-\"] ::-moz-selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\npre[class*=\"language-\"]::selection,\npre[class*=\"language-\"] ::selection,\ncode[class*=\"language-\"]::selection,\ncode[class*=\"language-\"] ::selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n@media print {\n  code[class*=\"language-\"],\n  pre[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #f5f2f0;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: slategray;\n}\n.token.punctuation {\n  color: #999;\n}\n.token.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #905;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #690;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #9a6e3a;\n  /* This background color was intended by the author of this theme. */\n  background: hsla(0, 0%, 100%, 0.5);\n}\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #07a;\n}\n.token.function,\n.token.class-name {\n  color: #DD4A68;\n}\n.token.regex,\n.token.important,\n.token.variable {\n  color: #e90;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.1);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #b4d7ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid rgba(180, 215, 255, 0.7);\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: multiply;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #b4d7ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide-dark/skin.css",
    "content": ".tox {\n  box-shadow: none;\n  box-sizing: content-box;\n  color: #222f3e;\n  cursor: auto;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: normal;\n  -webkit-tap-highlight-color: transparent;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  vertical-align: initial;\n  white-space: normal;\n}\n.tox *:not(svg):not(rect) {\n  box-sizing: inherit;\n  color: inherit;\n  cursor: inherit;\n  direction: inherit;\n  font-family: inherit;\n  font-size: inherit;\n  font-style: inherit;\n  font-weight: inherit;\n  line-height: inherit;\n  -webkit-tap-highlight-color: inherit;\n  text-align: inherit;\n  text-decoration: inherit;\n  text-shadow: inherit;\n  text-transform: inherit;\n  vertical-align: inherit;\n  white-space: inherit;\n}\n.tox *:not(svg):not(rect) {\n  /* stylelint-disable-line no-duplicate-selectors */\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  float: none;\n  height: auto;\n  margin: 0;\n  max-width: none;\n  outline: 0;\n  padding: 0;\n  position: static;\n  width: auto;\n}\n.tox:not([dir=rtl]) {\n  direction: ltr;\n  text-align: left;\n}\n.tox[dir=rtl] {\n  direction: rtl;\n  text-align: right;\n}\n.tox-tinymce {\n  border: 2px solid #161f29;\n  border-radius: 10px;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  overflow: hidden;\n  position: relative;\n  visibility: inherit !important;\n}\n.tox.tox-tinymce-inline {\n  border: none;\n  box-shadow: none;\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-container {\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-header {\n  background-color: #222F3E;\n  border: 2px solid #161f29;\n  border-radius: 10px;\n  box-shadow: none;\n  overflow: hidden;\n}\n.tox-tinymce-aux {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  z-index: 1300;\n}\n.tox-tinymce *:focus,\n.tox-tinymce-aux *:focus {\n  outline: none;\n}\nbutton::-moz-focus-inner {\n  border: 0;\n}\n.tox[dir=rtl] .tox-icon--flip svg {\n  transform: rotateY(180deg);\n}\n.tox .accessibility-issue__header {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description {\n  align-items: stretch;\n  border-radius: 6px;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .accessibility-issue__description > div {\n  padding-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div .tox-icon svg {\n  display: block;\n}\n.tox .accessibility-issue__repair {\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description {\n  background-color: rgba(0, 101, 216, 0.4);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon {\n  background-color: #006ce7;\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:focus {\n  background-color: #0060ce;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:active {\n  background-color: #0054b4;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description {\n  background-color: rgba(255, 165, 0, 0.5);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon {\n  background-color: #FFE89D;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:focus {\n  background-color: #F2D574;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:active {\n  background-color: #E8C657;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description {\n  background-color: rgba(204, 0, 0, 0.5);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon {\n  background-color: #F2BFBF;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:focus {\n  background-color: #E9A4A4;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:active {\n  background-color: #EE9494;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description {\n  background-color: rgba(120, 171, 70, 0.5);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description > *:last-child {\n  display: none;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue__header .tox-form__group h1,\n.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2 {\n  font-size: 14px;\n  margin-top: 0;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-left: auto;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 4px 4px 8px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-right: auto;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 8px 4px 4px;\n}\n.tox .tox-anchorbar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-bar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-button {\n  background-color: #006ce7;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #006ce7;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  line-height: 24px;\n  margin: 0;\n  outline: none;\n  padding: 4px 16px;\n  position: relative;\n  text-align: center;\n  text-decoration: none;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-button::before {\n  border-radius: 6px;\n  bottom: -1px;\n  box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #006ce7, 0 0 0 3px rgba(0, 108, 231, 0.25);\n  content: '';\n  left: -1px;\n  opacity: 0;\n  pointer-events: none;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n.tox .tox-button[disabled] {\n  background-color: #006ce7;\n  background-image: none;\n  border-color: #006ce7;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-button:focus:not(:disabled) {\n  background-color: #0060ce;\n  background-image: none;\n  border-color: #0060ce;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:focus-visible:not(:disabled)::before {\n  opacity: 1;\n}\n.tox .tox-button:hover:not(:disabled) {\n  background-color: #0060ce;\n  background-image: none;\n  border-color: #0060ce;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:active:not(:disabled) {\n  background-color: #0054b4;\n  background-image: none;\n  border-color: #0054b4;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary {\n  background-color: #3d546f;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #3d546f;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  color: #fff;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  outline: none;\n  padding: 4px 16px;\n  text-decoration: none;\n  text-transform: none;\n}\n.tox .tox-button--secondary[disabled] {\n  background-color: #3d546f;\n  background-image: none;\n  border-color: #3d546f;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-button--secondary:focus:not(:disabled) {\n  background-color: #34485f;\n  background-image: none;\n  border-color: #34485f;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary:hover:not(:disabled) {\n  background-color: #34485f;\n  background-image: none;\n  border-color: #34485f;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary:active:not(:disabled) {\n  background-color: #2b3b4e;\n  background-image: none;\n  border-color: #2b3b4e;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--icon,\n.tox .tox-button.tox-button--icon,\n.tox .tox-button.tox-button--secondary.tox-button--icon {\n  padding: 4px;\n}\n.tox .tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg {\n  display: block;\n  fill: currentColor;\n}\n.tox .tox-button-link {\n  background: 0;\n  border: none;\n  box-sizing: border-box;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  padding: 0;\n  white-space: nowrap;\n}\n.tox .tox-button-link--sm {\n  font-size: 14px;\n}\n.tox .tox-button--naked {\n  background-color: transparent;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #fff;\n}\n.tox .tox-button--naked[disabled] {\n  background-color: rgba(255, 255, 255, 0.2);\n  border-color: transparent;\n  box-shadow: unset;\n  color: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-button--naked:hover:not(:disabled) {\n  background-color: rgba(255, 255, 255, 0.2);\n  border-color: transparent;\n  box-shadow: unset;\n  color: #fff;\n}\n.tox .tox-button--naked:focus:not(:disabled) {\n  background-color: rgba(255, 255, 255, 0.2);\n  border-color: transparent;\n  box-shadow: unset;\n  color: #fff;\n}\n.tox .tox-button--naked:active:not(:disabled) {\n  background-color: rgba(255, 255, 255, 0.3);\n  border-color: transparent;\n  box-shadow: unset;\n  color: #fff;\n}\n.tox .tox-button--naked .tox-icon svg {\n  fill: currentColor;\n}\n.tox .tox-button--naked.tox-button--icon:hover:not(:disabled) {\n  color: #fff;\n}\n.tox .tox-checkbox {\n  align-items: center;\n  border-radius: 6px;\n  cursor: pointer;\n  display: flex;\n  height: 36px;\n  min-width: 36px;\n}\n.tox .tox-checkbox__input {\n  /* Hide from view but visible to screen readers */\n  height: 1px;\n  overflow: hidden;\n  position: absolute;\n  top: auto;\n  width: 1px;\n}\n.tox .tox-checkbox__icons {\n  align-items: center;\n  border-radius: 6px;\n  box-shadow: 0 0 0 2px transparent;\n  box-sizing: content-box;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  padding: calc(4px - 1px);\n  width: 24px;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: block;\n  fill: rgba(255, 255, 255, 0.2);\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: none;\n  fill: #006ce7;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: none;\n  fill: #006ce7;\n}\n.tox .tox-checkbox--disabled {\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:focus + .tox-checkbox__icons {\n  border-radius: 6px;\n  box-shadow: inset 0 0 0 1px #006ce7;\n  padding: calc(4px - 1px);\n}\n.tox:not([dir=rtl]) .tox-checkbox__label {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-checkbox__input {\n  left: -10000px;\n}\n.tox:not([dir=rtl]) .tox-bar .tox-checkbox {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__label {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__input {\n  right: -10000px;\n}\n.tox[dir=rtl] .tox-bar .tox-checkbox {\n  margin-right: 4px;\n}\n.tox {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-collection--toolbar .tox-collection__group {\n  display: flex;\n  padding: 0;\n}\n.tox .tox-collection--grid .tox-collection__group {\n  display: flex;\n  flex-wrap: wrap;\n  max-height: 208px;\n  overflow-x: hidden;\n  overflow-y: auto;\n  padding: 0;\n}\n.tox .tox-collection--list .tox-collection__group {\n  border-bottom-width: 0;\n  border-color: rgba(255, 255, 255, 0.15);\n  border-left-width: 0;\n  border-right-width: 0;\n  border-style: solid;\n  border-top-width: 1px;\n  padding: 4px 0;\n}\n.tox .tox-collection--list .tox-collection__group:first-child {\n  border-top-width: 0;\n}\n.tox .tox-collection__group-heading {\n  background-color: rgba(255, 255, 255, 0.15);\n  color: rgba(255, 255, 255, 0.5);\n  cursor: default;\n  font-size: 12px;\n  font-style: normal;\n  font-weight: normal;\n  margin-bottom: 4px;\n  margin-top: -4px;\n  padding: 4px 8px;\n  text-transform: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection__item {\n  align-items: center;\n  border-radius: 3px;\n  color: #fff;\n  display: flex;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection--list .tox-collection__item {\n  padding: 4px 8px;\n}\n.tox .tox-collection--toolbar .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--grid .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--list .tox-collection__item--enabled {\n  background-color: #2b3b4e;\n  color: #fff;\n}\n.tox .tox-collection--list .tox-collection__item--active {\n  background-color: #3389ec;\n}\n.tox .tox-collection--toolbar .tox-collection__item--enabled {\n  background-color: #599fef;\n  color: #fff;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active {\n  background-color: #3389ec;\n}\n.tox .tox-collection--grid .tox-collection__item--enabled {\n  background-color: #599fef;\n  color: #fff;\n}\n.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  background-color: #3389ec;\n  color: #fff;\n}\n.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #fff;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #fff;\n}\n.tox .tox-collection__item-icon,\n.tox .tox-collection__item-checkmark {\n  align-items: center;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  width: 24px;\n}\n.tox .tox-collection__item-icon svg,\n.tox .tox-collection__item-checkmark svg {\n  fill: currentColor;\n}\n.tox .tox-collection--toolbar-lg .tox-collection__item-icon {\n  height: 48px;\n  width: 48px;\n}\n.tox .tox-collection__item-label {\n  color: currentColor;\n  display: inline-block;\n  flex: 1;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 24px;\n  text-transform: none;\n  word-break: break-all;\n}\n.tox .tox-collection__item-accessory {\n  color: rgba(255, 255, 255, 0.5);\n  display: inline-block;\n  font-size: 14px;\n  height: 24px;\n  line-height: 24px;\n  text-transform: none;\n}\n.tox .tox-collection__item-caret {\n  align-items: center;\n  display: flex;\n  min-height: 24px;\n}\n.tox .tox-collection__item-caret::after {\n  content: '';\n  font-size: 0;\n  min-height: inherit;\n}\n.tox .tox-collection__item-caret svg {\n  fill: #fff;\n}\n.tox .tox-collection__item--state-disabled {\n  background-color: transparent;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg {\n  display: none;\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory + .tox-collection__item-checkmark {\n  display: none;\n}\n.tox .tox-collection--horizontal {\n  background-color: #2b3b4e;\n  border: 1px solid rgba(255, 255, 255, 0.15);\n  border-radius: 6px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n  margin-bottom: 0;\n  overflow-x: auto;\n  padding: 0;\n}\n.tox .tox-collection--horizontal .tox-collection__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: nowrap;\n  margin: 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item {\n  height: 28px;\n  margin: 6px 1px 5px 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item-label {\n  white-space: nowrap;\n}\n.tox .tox-collection--horizontal .tox-collection__item-caret {\n  margin-left: 4px;\n}\n.tox .tox-collection__item-container {\n  display: flex;\n}\n.tox .tox-collection__item-container--row {\n  align-items: center;\n  flex: 1 1 auto;\n  flex-direction: row;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-left {\n  margin-right: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-right {\n  justify-content: flex-end;\n  margin-left: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top {\n  align-items: flex-start;\n  margin-bottom: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle {\n  align-items: center;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom {\n  align-items: flex-end;\n  margin-top: auto;\n}\n.tox .tox-collection__item-container--column {\n  align-self: center;\n  flex: 1 1 auto;\n  flex-direction: column;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-left {\n  align-items: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-right {\n  align-items: flex-end;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top {\n  align-self: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle {\n  align-self: center;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom {\n  align-self: flex-end;\n}\n.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-right: 1px solid transparent;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-collection__item-accessory {\n  margin-left: 16px;\n  text-align: right;\n}\n.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret {\n  margin-left: 16px;\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-left: 1px solid transparent;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-collection__item-accessory {\n  margin-right: 16px;\n  text-align: left;\n}\n.tox[dir=rtl] .tox-collection .tox-collection__item-caret {\n  margin-right: 16px;\n  transform: rotateY(180deg);\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret {\n  margin-right: 4px;\n}\n.tox .tox-color-picker-container {\n  display: flex;\n  flex-direction: row;\n  height: 225px;\n  margin: 0;\n}\n.tox .tox-sv-palette {\n  box-sizing: border-box;\n  display: flex;\n  height: 100%;\n}\n.tox .tox-sv-palette-spectrum {\n  height: 100%;\n}\n.tox .tox-sv-palette,\n.tox .tox-sv-palette-spectrum {\n  width: 225px;\n}\n.tox .tox-sv-palette-thumb {\n  background: none;\n  border: 1px solid black;\n  border-radius: 50%;\n  box-sizing: content-box;\n  height: 12px;\n  position: absolute;\n  width: 12px;\n}\n.tox .tox-sv-palette-inner-thumb {\n  border: 1px solid white;\n  border-radius: 50%;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox .tox-hue-slider {\n  box-sizing: border-box;\n  height: 100%;\n  width: 25px;\n}\n.tox .tox-hue-slider-spectrum {\n  background: linear-gradient(to bottom, #f00, #ff0080, #f0f, #8000ff, #00f, #0080ff, #0ff, #00ff80, #0f0, #80ff00, #ff0, #ff8000, #f00);\n  height: 100%;\n  width: 100%;\n}\n.tox .tox-hue-slider,\n.tox .tox-hue-slider-spectrum {\n  width: 20px;\n}\n.tox .tox-hue-slider-thumb {\n  background: white;\n  border: 1px solid black;\n  box-sizing: content-box;\n  height: 4px;\n  width: 100%;\n}\n.tox .tox-rgb-form {\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n}\n.tox .tox-rgb-form div {\n  align-items: center;\n  display: flex;\n  justify-content: space-between;\n  margin-bottom: 5px;\n  width: inherit;\n}\n.tox .tox-rgb-form input {\n  width: 6em;\n}\n.tox .tox-rgb-form input.tox-invalid {\n  /* Need !important to override Chrome's focus styling unfortunately */\n  border: 1px solid red !important;\n}\n.tox .tox-rgb-form .tox-rgba-preview {\n  border: 1px solid black;\n  flex-grow: 2;\n  margin-bottom: 0;\n}\n.tox:not([dir=rtl]) .tox-sv-palette {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider-thumb {\n  margin-left: -1px;\n}\n.tox:not([dir=rtl]) .tox-rgb-form label {\n  margin-right: 0.5em;\n}\n.tox[dir=rtl] .tox-sv-palette {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider-thumb {\n  margin-right: -1px;\n}\n.tox[dir=rtl] .tox-rgb-form label {\n  margin-left: 0.5em;\n}\n.tox .tox-toolbar .tox-swatches,\n.tox .tox-toolbar__primary .tox-swatches,\n.tox .tox-toolbar__overflow .tox-swatches {\n  margin: 5px 0 6px 11px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-swatches-menu {\n  border: 0;\n  margin: -4px -4px;\n}\n.tox .tox-swatches__row {\n  display: flex;\n}\n.tox .tox-swatch {\n  height: 30px;\n  transition: transform 0.15s, box-shadow 0.15s;\n  width: 30px;\n}\n.tox .tox-swatch:hover,\n.tox .tox-swatch:focus {\n  box-shadow: 0 0 0 1px rgba(127, 127, 127, 0.3) inset;\n  transform: scale(0.8);\n}\n.tox .tox-swatch--remove {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n}\n.tox .tox-swatch--remove svg path {\n  stroke: #e74c3c;\n}\n.tox .tox-swatches__picker-btn {\n  align-items: center;\n  background-color: transparent;\n  border: 0;\n  cursor: pointer;\n  display: flex;\n  height: 30px;\n  justify-content: center;\n  outline: none;\n  padding: 0;\n  width: 30px;\n}\n.tox .tox-swatches__picker-btn svg {\n  fill: #fff;\n  height: 24px;\n  width: 24px;\n}\n.tox .tox-swatches__picker-btn:hover {\n  background: #3389ec;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg {\n  display: none;\n  fill: #fff;\n  height: 24px;\n  margin: calc((30px - 24px) / 2) calc((30px - 24px) / 2);\n  width: 24px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg path {\n  fill: #fff;\n  paint-order: stroke;\n  stroke: #222f3e;\n  stroke-width: 2px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove)[aria-checked=\"true\"] svg {\n  display: block;\n}\n.tox:not([dir=rtl]) .tox-swatches__picker-btn {\n  margin-left: auto;\n}\n.tox[dir=rtl] .tox-swatches__picker-btn {\n  margin-right: auto;\n}\n.tox .tox-comment-thread {\n  background: #2b3b4e;\n  position: relative;\n}\n.tox .tox-comment-thread > *:not(:first-child) {\n  margin-top: 8px;\n}\n.tox .tox-comment {\n  background: #2b3b4e;\n  border: 1px solid #161f29;\n  border-radius: 6px;\n  box-shadow: 0 4px 8px 0 rgba(34, 47, 62, 0.1);\n  padding: 8px 8px 16px 8px;\n  position: relative;\n}\n.tox .tox-comment__header {\n  align-items: center;\n  color: #fff;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .tox-comment__date {\n  color: #fff;\n  font-size: 12px;\n  line-height: 18px;\n}\n.tox .tox-comment__body {\n  color: #fff;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin-top: 8px;\n  position: relative;\n  text-transform: initial;\n}\n.tox .tox-comment__body textarea {\n  resize: none;\n  white-space: normal;\n  width: 100%;\n}\n.tox .tox-comment__expander {\n  padding-top: 8px;\n}\n.tox .tox-comment__expander p {\n  color: rgba(255, 255, 255, 0.5);\n  font-size: 14px;\n  font-style: normal;\n}\n.tox .tox-comment__body p {\n  margin: 0;\n}\n.tox .tox-comment__buttonspacing {\n  padding-top: 16px;\n  text-align: center;\n}\n.tox .tox-comment-thread__overlay::after {\n  background: #2b3b4e;\n  bottom: 0;\n  content: \"\";\n  display: flex;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__reply {\n  display: flex;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 8px;\n}\n.tox .tox-comment__reply > *:first-child {\n  margin-bottom: 8px;\n  width: 100%;\n}\n.tox .tox-comment__edit {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 16px;\n}\n.tox .tox-comment__gradient::after {\n  background: linear-gradient(rgba(43, 59, 78, 0), #2b3b4e);\n  bottom: 0;\n  content: \"\";\n  display: block;\n  height: 5em;\n  margin-top: -40px;\n  position: absolute;\n  width: 100%;\n}\n.tox .tox-comment__overlay {\n  background: #2b3b4e;\n  bottom: 0;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  text-align: center;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__loading-text {\n  align-items: center;\n  color: #fff;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n}\n.tox .tox-comment__loading-text > div {\n  padding-bottom: 16px;\n}\n.tox .tox-comment__overlaytext {\n  bottom: 0;\n  flex-direction: column;\n  font-size: 14px;\n  left: 0;\n  padding: 1em;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 10;\n}\n.tox .tox-comment__overlaytext p {\n  background-color: #2b3b4e;\n  box-shadow: 0 0 8px 8px #2b3b4e;\n  color: #fff;\n  text-align: center;\n}\n.tox .tox-comment__overlaytext div:nth-of-type(2) {\n  font-size: 0.8em;\n}\n.tox .tox-comment__busy-spinner {\n  align-items: center;\n  background-color: #2b3b4e;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 20;\n}\n.tox .tox-comment__scroll {\n  display: flex;\n  flex-direction: column;\n  flex-shrink: 1;\n  overflow: auto;\n}\n.tox .tox-conversations {\n  margin: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__edit {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__buttonspacing > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__edit > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__reply > *:last-child {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-comment__edit {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-comment__buttonspacing > *:last-child,\n.tox[dir=rtl] .tox-comment__edit > *:last-child,\n.tox[dir=rtl] .tox-comment__reply > *:last-child {\n  margin-right: 8px;\n}\n.tox .tox-user {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-user__avatar svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-user__avatar img {\n  border-radius: 50%;\n  height: 36px;\n  object-fit: cover;\n  vertical-align: middle;\n  width: 36px;\n}\n.tox .tox-user__name {\n  color: #fff;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  line-height: 18px;\n  text-transform: none;\n}\n.tox:not([dir=rtl]) .tox-user__avatar svg,\n.tox:not([dir=rtl]) .tox-user__avatar img {\n  margin-right: 8px;\n}\n.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar svg,\n.tox[dir=rtl] .tox-user__avatar img {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar + .tox-user__name {\n  margin-right: 8px;\n}\n.tox .tox-dialog-wrap {\n  align-items: center;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1100;\n}\n.tox .tox-dialog-wrap__backdrop {\n  background-color: rgba(34, 47, 62, 0.75);\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 1;\n}\n.tox .tox-dialog-wrap__backdrop--opaque {\n  background-color: #222F3E;\n}\n.tox .tox-dialog {\n  background-color: #2b3b4e;\n  border-color: #161f29;\n  border-radius: 10px;\n  border-style: solid;\n  border-width: 0px;\n  box-shadow: 0 16px 16px -10px rgba(34, 47, 62, 0.15), 0 0 40px 1px rgba(34, 47, 62, 0.15);\n  display: flex;\n  flex-direction: column;\n  max-height: 100%;\n  max-width: 480px;\n  overflow: hidden;\n  position: relative;\n  width: 95vw;\n  z-index: 2;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog {\n    align-self: flex-start;\n    margin: 8px auto;\n    max-height: calc(100vh - 8px * 2);\n    width: calc(100vw - 16px);\n  }\n}\n.tox .tox-dialog-inline {\n  z-index: 1100;\n}\n.tox .tox-dialog__header {\n  align-items: center;\n  background-color: #2b3b4e;\n  border-bottom: none;\n  color: #fff;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 16px 0 16px;\n  position: relative;\n}\n.tox .tox-dialog__header .tox-button {\n  z-index: 1;\n}\n.tox .tox-dialog__draghandle {\n  cursor: grab;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tox .tox-dialog__draghandle:active {\n  cursor: grabbing;\n}\n.tox .tox-dialog__dismiss {\n  margin-left: auto;\n}\n.tox .tox-dialog__title {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  text-transform: none;\n}\n.tox .tox-dialog__body {\n  color: #fff;\n  display: flex;\n  flex: 1;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  min-width: 0;\n  text-align: left;\n  text-transform: none;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body {\n    flex-direction: column;\n  }\n}\n.tox .tox-dialog__body-nav {\n  align-items: flex-start;\n  display: flex;\n  flex-direction: column;\n  padding: 16px 16px;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {\n    flex-direction: row;\n    -webkit-overflow-scrolling: touch;\n    overflow-x: auto;\n    padding-bottom: 0;\n  }\n}\n.tox .tox-dialog__body-nav-item {\n  border-bottom: 2px solid transparent;\n  color: rgba(255, 255, 255, 0.5);\n  display: inline-block;\n  font-size: 14px;\n  line-height: 1.3;\n  margin-bottom: 8px;\n  text-decoration: none;\n  white-space: nowrap;\n}\n.tox .tox-dialog__body-nav-item:focus {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.tox .tox-dialog__body-nav-item--active {\n  border-bottom: 2px solid #006ce7;\n  color: #006ce7;\n}\n.tox .tox-dialog__body-content {\n  box-sizing: border-box;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n  max-height: 650px;\n  overflow: auto;\n  -webkit-overflow-scrolling: touch;\n  padding: 16px 16px;\n}\n.tox .tox-dialog__body-content > * {\n  margin-bottom: 0;\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content > *:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content a {\n  color: #006ce7;\n  cursor: pointer;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:hover,\n.tox .tox-dialog__body-content a:focus {\n  color: #0054b4;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:active {\n  color: #0054b4;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content ul {\n  display: block;\n  list-style-type: disc;\n  margin-bottom: 16px;\n  margin-inline-end: 0;\n  margin-inline-start: 0;\n  padding-inline-start: 2.5rem;\n}\n.tox .tox-dialog__body-content .tox-form__group h1 {\n  color: #fff;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group h2 {\n  color: #fff;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group p {\n  margin-bottom: 16px;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:first-child,\n.tox .tox-dialog__body-content .tox-form__group h2:first-child,\n.tox .tox-dialog__body-content .tox-form__group p:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:last-child,\n.tox .tox-dialog__body-content .tox-form__group h2:last-child,\n.tox .tox-dialog__body-content .tox-form__group p:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:only-child,\n.tox .tox-dialog__body-content .tox-form__group h2:only-child,\n.tox .tox-dialog__body-content .tox-form__group p:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog--width-lg {\n  height: 650px;\n  max-width: 1200px;\n}\n.tox .tox-dialog--width-md {\n  max-width: 800px;\n}\n.tox .tox-dialog--width-md .tox-dialog__body-content {\n  overflow: auto;\n}\n.tox .tox-dialog__body-content--centered {\n  text-align: center;\n}\n.tox .tox-dialog__footer {\n  align-items: center;\n  background-color: #2b3b4e;\n  border-top: none;\n  display: flex;\n  justify-content: space-between;\n  padding: 8px 16px;\n}\n.tox .tox-dialog__footer-start,\n.tox .tox-dialog__footer-end {\n  display: flex;\n}\n.tox .tox-dialog__busy-spinner {\n  align-items: center;\n  background-color: rgba(34, 47, 62, 0.75);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 3;\n}\n.tox .tox-dialog__table {\n  border-collapse: collapse;\n  width: 100%;\n}\n.tox .tox-dialog__table thead th {\n  font-weight: bold;\n  padding-bottom: 8px;\n}\n.tox .tox-dialog__table tbody tr {\n  border-bottom: 1px solid #161f29;\n}\n.tox .tox-dialog__table tbody tr:last-child {\n  border-bottom: none;\n}\n.tox .tox-dialog__table td {\n  padding-bottom: 8px;\n  padding-top: 8px;\n}\n.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {\n  background: #fff;\n}\n.tox .tox-dialog__popups {\n  position: absolute;\n  width: 100%;\n  z-index: 1100;\n}\n.tox .tox-dialog__body-iframe {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-dialog__body-iframe .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox .tox-dialog-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox .tox-dialog-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox .tox-dialog-dock-transition {\n  transition: visibility 0s linear 0.3s, opacity 0.3s ease;\n}\n.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {\n  transition-delay: 0s;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {\n    margin-right: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {\n    margin-left: 8px;\n  }\n}\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-dialog__body {\n  text-align: right;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {\n    margin-left: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {\n    margin-right: 8px;\n  }\n}\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-right: 8px;\n}\nbody.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox .tox-dropzone-container {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dropzone {\n  align-items: center;\n  background: #fff;\n  border: 2px dashed #161f29;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  justify-content: center;\n  min-height: 100px;\n  padding: 10px;\n}\n.tox .tox-dropzone p {\n  color: rgba(255, 255, 255, 0.5);\n  margin: 0 0 16px 0;\n}\n.tox .tox-edit-area {\n  display: flex;\n  flex: 1;\n  overflow: hidden;\n  position: relative;\n}\n.tox .tox-edit-area__iframe {\n  background-color: #fff;\n  border: 0;\n  box-sizing: border-box;\n  flex: 1;\n  height: 100%;\n  position: absolute;\n  width: 100%;\n}\n.tox.tox-inline-edit-area {\n  border: 1px dotted #161f29;\n}\n.tox .tox-editor-container {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  overflow: hidden;\n}\n.tox .tox-editor-header {\n  display: grid;\n  grid-template-columns: 1fr min-content;\n  z-index: 1;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header {\n  background-color: #222F3E;\n  border-bottom: 1px solid rgba(255, 255, 255, 0.15);\n  box-shadow: none;\n  padding: 4px 0;\n  transition: box-shadow 0.5s;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-bottom .tox-editor-header {\n  border-top: 1px solid rgba(255, 255, 255, 0.15);\n  box-shadow: none;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on .tox-editor-header {\n  background-color: #222F3E;\n  box-shadow: none;\n  padding: 4px 0;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on.tox-tinymce--toolbar-bottom .tox-editor-header {\n  box-shadow: none;\n}\n.tox.tox:not(.tox-tinymce-inline) .tox-editor-header.tox-editor-header--empty {\n  background: none;\n  border: none;\n  box-shadow: none;\n  padding: 0;\n}\n.tox-editor-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox-editor-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox-editor-dock-transition {\n  transition: visibility 0s linear 0.25s, opacity 0.25s ease;\n}\n.tox-editor-dock-transition.tox-editor-dock-fadein {\n  transition-delay: 0s;\n}\n.tox .tox-control-wrap {\n  flex: 1;\n  position: relative;\n}\n.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid {\n  display: none;\n}\n.tox .tox-control-wrap svg {\n  display: block;\n}\n.tox .tox-control-wrap__status-icon-wrap {\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-control-wrap__status-icon-invalid svg {\n  fill: #c00;\n}\n.tox .tox-control-wrap__status-icon-unknown svg {\n  fill: orange;\n}\n.tox .tox-control-wrap__status-icon-valid svg {\n  fill: green;\n}\n.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield {\n  padding-right: 32px;\n}\n.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap {\n  right: 4px;\n}\n.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield {\n  padding-left: 32px;\n}\n.tox[dir=rtl] .tox-control-wrap__status-icon-wrap {\n  left: 4px;\n}\n.tox .tox-autocompleter {\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-menu {\n  box-sizing: border-box;\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-autocompleter-highlight {\n  font-weight: bold;\n}\n.tox .tox-color-input {\n  display: flex;\n  position: relative;\n  z-index: 1;\n}\n.tox .tox-color-input .tox-textfield {\n  z-index: -1;\n}\n.tox .tox-color-input span {\n  border-color: rgba(34, 47, 62, 0.2);\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  height: 24px;\n  position: absolute;\n  top: 6px;\n  width: 24px;\n}\n.tox .tox-color-input span:hover:not([aria-disabled=true]),\n.tox .tox-color-input span:focus:not([aria-disabled=true]) {\n  border-color: #006ce7;\n  cursor: pointer;\n}\n.tox .tox-color-input span::before {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.25) 25%, transparent 25%), linear-gradient(-45deg, rgba(255, 255, 255, 0.25) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(255, 255, 255, 0.25) 75%), linear-gradient(-45deg, transparent 75%, rgba(255, 255, 255, 0.25) 75%);\n  background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n  background-size: 12px 12px;\n  border: 1px solid #2b3b4e;\n  border-radius: 6px;\n  box-sizing: border-box;\n  content: '';\n  height: 24px;\n  left: -1px;\n  position: absolute;\n  top: -1px;\n  width: 24px;\n  z-index: -1;\n}\n.tox .tox-color-input span[aria-disabled=true] {\n  cursor: not-allowed;\n}\n.tox:not([dir=rtl]) .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-color-input .tox-textfield {\n  padding-left: 36px;\n}\n.tox:not([dir=rtl]) .tox-color-input span {\n  left: 6px;\n}\n.tox[dir=\"rtl\"] .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=\"rtl\"] .tox-color-input .tox-textfield {\n  padding-right: 36px;\n}\n.tox[dir=\"rtl\"] .tox-color-input span {\n  right: 6px;\n}\n.tox .tox-label,\n.tox .tox-toolbar-label {\n  color: rgba(255, 255, 255, 0.5);\n  display: block;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  padding: 0 8px 0 0;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-toolbar-label {\n  padding: 0 8px;\n}\n.tox[dir=rtl] .tox-label {\n  padding: 0 0 0 8px;\n}\n.tox .tox-form {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group {\n  box-sizing: border-box;\n  margin-bottom: 4px;\n}\n.tox .tox-form-group--maximize {\n  flex: 1;\n}\n.tox .tox-form__group--error {\n  color: #c00;\n}\n.tox .tox-form__group--collection {\n  display: flex;\n}\n.tox .tox-form__grid {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: space-between;\n}\n.tox .tox-form__grid--2col > .tox-form__group {\n  width: calc(50% - (8px / 2));\n}\n.tox .tox-form__grid--3col > .tox-form__group {\n  width: calc(100% / 3 - (8px / 2));\n}\n.tox .tox-form__grid--4col > .tox-form__group {\n  width: calc(25% - (8px / 2));\n}\n.tox .tox-form__controls-h-stack {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--inline {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--stretched {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group--stretched .tox-textarea {\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox:not([dir=rtl]) .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-lock.tox-locked .tox-lock-icon__unlock,\n.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock {\n  display: none;\n}\n.tox .tox-textfield,\n.tox .tox-toolbar-textfield,\n.tox .tox-listboxfield .tox-listbox--select,\n.tox .tox-textarea {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #2b3b4e;\n  border-color: #161f29;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 5.5px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-textfield[disabled],\n.tox .tox-textarea[disabled] {\n  background-color: #222f3e;\n  color: rgba(255, 255, 255, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-textfield:focus,\n.tox .tox-listboxfield .tox-listbox--select:focus,\n.tox .tox-textarea:focus {\n  background-color: #2b3b4e;\n  border-color: #006ce7;\n  box-shadow: 0 0 0 2px rgba(0, 108, 231, 0.25);\n  outline: none;\n}\n.tox .tox-toolbar-textfield {\n  border-width: 0;\n  margin-bottom: 3px;\n  margin-top: 2px;\n  max-width: 250px;\n}\n.tox .tox-naked-btn {\n  background-color: transparent;\n  border: 0;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #006ce7;\n  cursor: pointer;\n  display: block;\n  margin: 0;\n  padding: 0;\n}\n.tox .tox-naked-btn svg {\n  display: block;\n  fill: #fff;\n}\n.tox:not([dir=rtl]) .tox-toolbar-textfield + * {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-toolbar-textfield + * {\n  margin-right: 4px;\n}\n.tox .tox-listboxfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-listboxfield .tox-listbox--select[disabled] {\n  background-color: #19232e;\n  color: rgba(255, 255, 255, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-listbox__select-label {\n  cursor: default;\n  flex: 1;\n  margin: 0 4px;\n}\n.tox .tox-listbox__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-listbox__select-chevron svg {\n  fill: #fff;\n}\n.tox .tox-listboxfield .tox-listbox--select {\n  align-items: center;\n  display: flex;\n}\n.tox:not([dir=rtl]) .tox-listboxfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-listboxfield svg {\n  left: 8px;\n}\n.tox .tox-selectfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-selectfield select {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #2b3b4e;\n  border-color: #161f29;\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 5.5px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-selectfield select[disabled] {\n  background-color: #19232e;\n  color: rgba(255, 255, 255, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-selectfield select::-ms-expand {\n  display: none;\n}\n.tox .tox-selectfield select:focus {\n  background-color: #2b3b4e;\n  border-color: #006ce7;\n  box-shadow: 0 0 0 2px rgba(0, 108, 231, 0.25);\n  outline: none;\n}\n.tox .tox-selectfield svg {\n  pointer-events: none;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"0\"],\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"1\"] {\n  padding-right: 24px;\n}\n.tox:not([dir=rtl]) .tox-selectfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-selectfield select[size=\"0\"],\n.tox[dir=rtl] .tox-selectfield select[size=\"1\"] {\n  padding-left: 24px;\n}\n.tox[dir=rtl] .tox-selectfield svg {\n  left: 8px;\n}\n.tox .tox-textarea {\n  -webkit-appearance: textarea;\n     -moz-appearance: textarea;\n          appearance: textarea;\n  white-space: pre-wrap;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n.tox .tox-help__more-link {\n  list-style: none;\n  margin-top: 1em;\n}\n.tox .tox-imagepreview {\n  background-color: #666;\n  height: 380px;\n  overflow: hidden;\n  position: relative;\n  width: 100%;\n}\n.tox .tox-imagepreview.tox-imagepreview__loaded {\n  overflow: auto;\n}\n.tox .tox-imagepreview__container {\n  display: flex;\n  left: 100vw;\n  position: absolute;\n  top: 100vw;\n}\n.tox .tox-imagepreview__image {\n  background: url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==);\n}\n.tox .tox-image-tools .tox-spacer {\n  flex: 1;\n}\n.tox .tox-image-tools .tox-bar {\n  align-items: center;\n  display: flex;\n  height: 60px;\n  justify-content: center;\n}\n.tox .tox-image-tools .tox-imagepreview,\n.tox .tox-image-tools .tox-imagepreview + .tox-bar {\n  margin-top: 8px;\n}\n.tox .tox-image-tools .tox-croprect-block {\n  background: black;\n  filter: alpha(opacity=50);\n  opacity: 0.5;\n  position: absolute;\n  zoom: 1;\n}\n.tox .tox-image-tools .tox-croprect-handle {\n  border: 2px solid white;\n  height: 20px;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 20px;\n}\n.tox .tox-image-tools .tox-croprect-handle-move {\n  border: 0;\n  cursor: move;\n  position: absolute;\n}\n.tox .tox-image-tools .tox-croprect-handle-nw {\n  border-width: 2px 0 0 2px;\n  cursor: nw-resize;\n  left: 100px;\n  margin: -2px 0 0 -2px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-ne {\n  border-width: 2px 2px 0 0;\n  cursor: ne-resize;\n  left: 200px;\n  margin: -2px 0 0 -20px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-sw {\n  border-width: 0 0 2px 2px;\n  cursor: sw-resize;\n  left: 100px;\n  margin: -20px 2px 0 -2px;\n  top: 200px;\n}\n.tox .tox-image-tools .tox-croprect-handle-se {\n  border-width: 0 2px 2px 0;\n  cursor: se-resize;\n  left: 200px;\n  margin: -20px 0 0 -20px;\n  top: 200px;\n}\n.tox .tox-insert-table-picker {\n  display: flex;\n  flex-wrap: wrap;\n  width: 170px;\n}\n.tox .tox-insert-table-picker > div {\n  border-color: rgba(255, 255, 255, 0.15);\n  border-style: solid;\n  border-width: 0 1px 1px 0;\n  box-sizing: border-box;\n  height: 17px;\n  width: 17px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker {\n  margin: -4px -4px;\n}\n.tox .tox-insert-table-picker .tox-insert-table-picker__selected {\n  background-color: rgba(0, 108, 231, 0.5);\n  border-color: rgba(0, 108, 231, 0.5);\n}\n.tox .tox-insert-table-picker__label {\n  color: #fff;\n  display: block;\n  font-size: 14px;\n  padding: 4px;\n  text-align: center;\n  width: 100%;\n}\n.tox:not([dir=rtl]) {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-insert-table-picker > div:nth-child(10n) {\n  border-right: 0;\n}\n.tox[dir=rtl] {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=rtl] .tox-insert-table-picker > div:nth-child(10n+1) {\n  border-right: 0;\n}\n.tox {\n  /* stylelint-disable */\n  /* stylelint-enable */\n}\n.tox .tox-menu {\n  background-color: #2b3b4e;\n  border: 1px solid rgba(255, 255, 255, 0.15);\n  border-radius: 6px;\n  box-shadow: none;\n  display: inline-block;\n  overflow: hidden;\n  vertical-align: top;\n  z-index: 1150;\n}\n.tox .tox-menu.tox-collection.tox-collection--list {\n  padding: 0 4px;\n}\n.tox .tox-menu.tox-collection.tox-collection--toolbar {\n  padding: 8px;\n}\n.tox .tox-menu.tox-collection.tox-collection--grid {\n  padding: 8px;\n}\n@media only screen and (min-width: 768px ) {\n  .tox .tox-menu .tox-collection__item-label {\n    overflow-wrap: break-word;\n    word-break: normal;\n  }\n}\n.tox .tox-menu__label h1,\n.tox .tox-menu__label h2,\n.tox .tox-menu__label h3,\n.tox .tox-menu__label h4,\n.tox .tox-menu__label h5,\n.tox .tox-menu__label h6,\n.tox .tox-menu__label p,\n.tox .tox-menu__label blockquote,\n.tox .tox-menu__label code {\n  margin: 0;\n}\n.tox .tox-menubar {\n  background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;\n  background-color: #222F3E;\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  grid-column: 1 / -1;\n  grid-row: 1;\n  padding: 0 11px 0 12px;\n}\n.tox .tox-promotion + .tox-menubar {\n  grid-column: 1;\n}\n.tox .tox-promotion {\n  background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;\n  background-color: #222F3E;\n  grid-column: 2;\n  grid-row: 1;\n  padding-inline-end: 8px;\n  padding-inline-start: 4px;\n  padding-top: 5px;\n}\n.tox .tox-promotion-link {\n  align-items: unsafe center;\n  background-color: #E8F1F8;\n  border-radius: 5px;\n  color: #086BE6;\n  cursor: pointer;\n  display: flex;\n  font-size: 14px;\n  height: 26.6px;\n  padding: 4px 8px;\n  white-space: nowrap;\n}\n.tox .tox-promotion-link:hover {\n  background-color: #B4D7FF;\n}\n.tox .tox-promotion-link:focus {\n  background-color: #D9EDF7;\n}\n/* Deprecated. Remove in next major release */\n.tox .tox-mbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #fff;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 28px;\n  justify-content: center;\n  margin: 5px 1px 6px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0 4px;\n  text-transform: none;\n  width: auto;\n}\n.tox .tox-mbtn[disabled] {\n  background-color: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-mbtn:focus:not(:disabled) {\n  background: #3389ec;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-mbtn--active {\n  background: #599fef;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active) {\n  background: #3389ec;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-mbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-mbtn[disabled] .tox-mbtn__select-label {\n  cursor: not-allowed;\n}\n.tox .tox-mbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n  display: none;\n}\n.tox .tox-notification {\n  border-radius: 6px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: grid;\n  font-size: 14px;\n  font-weight: normal;\n  grid-template-columns: minmax(40px, 1fr) auto minmax(40px, 1fr);\n  margin-top: 4px;\n  opacity: 0;\n  padding: 4px;\n  transition: transform 100ms ease-in, opacity 150ms ease-in;\n}\n.tox .tox-notification p {\n  font-size: 14px;\n  font-weight: normal;\n}\n.tox .tox-notification a {\n  cursor: pointer;\n  text-decoration: underline;\n}\n.tox .tox-notification--in {\n  opacity: 1;\n}\n.tox .tox-notification--success {\n  background-color: #334840;\n  border-color: #3c5440;\n  color: #fff;\n}\n.tox .tox-notification--success p {\n  color: #fff;\n}\n.tox .tox-notification--success a {\n  color: #b5d199;\n}\n.tox .tox-notification--success svg {\n  fill: #fff;\n}\n.tox .tox-notification--error {\n  background-color: #442632;\n  border-color: #55212b;\n  color: #fff;\n}\n.tox .tox-notification--error p {\n  color: #fff;\n}\n.tox .tox-notification--error a {\n  color: #e68080;\n}\n.tox .tox-notification--error svg {\n  fill: #fff;\n}\n.tox .tox-notification--warn,\n.tox .tox-notification--warning {\n  background-color: #222F3E;\n  border-color: rgba(255, 255, 255, 0.15);\n  color: #fff0b3;\n}\n.tox .tox-notification--warn p,\n.tox .tox-notification--warning p {\n  color: #fff0b3;\n}\n.tox .tox-notification--warn a,\n.tox .tox-notification--warning a {\n  color: #ffcc00;\n}\n.tox .tox-notification--warn svg,\n.tox .tox-notification--warning svg {\n  fill: #fff0b3;\n}\n.tox .tox-notification--info {\n  background-color: #254161;\n  border-color: #264972;\n  color: #fff;\n}\n.tox .tox-notification--info p {\n  color: #fff;\n}\n.tox .tox-notification--info a {\n  color: #83b7f3;\n}\n.tox .tox-notification--info svg {\n  fill: #fff;\n}\n.tox .tox-notification__body {\n  align-self: center;\n  color: #fff;\n  font-size: 14px;\n  grid-column-end: 3;\n  grid-column-start: 2;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  text-align: center;\n  white-space: normal;\n  word-break: break-all;\n  word-break: break-word;\n}\n.tox .tox-notification__body > * {\n  margin: 0;\n}\n.tox .tox-notification__body > * + * {\n  margin-top: 1rem;\n}\n.tox .tox-notification__icon {\n  align-self: center;\n  grid-column-end: 2;\n  grid-column-start: 1;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification__icon svg {\n  display: block;\n}\n.tox .tox-notification__dismiss {\n  align-self: start;\n  grid-column-end: 4;\n  grid-column-start: 3;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification .tox-progress-bar {\n  grid-column-end: 4;\n  grid-column-start: 1;\n  grid-row-end: 3;\n  grid-row-start: 2;\n  justify-self: center;\n}\n.tox .tox-pop {\n  display: inline-block;\n  position: relative;\n}\n.tox .tox-pop--resizing {\n  transition: width 0.1s ease;\n}\n.tox .tox-pop--resizing .tox-toolbar,\n.tox .tox-pop--resizing .tox-toolbar__group {\n  flex-wrap: nowrap;\n}\n.tox .tox-pop--transition {\n  transition: 0.15s ease;\n  transition-property: left, right, top, bottom;\n}\n.tox .tox-pop--transition::before,\n.tox .tox-pop--transition::after {\n  transition: all 0.15s, visibility 0s, opacity 0.075s ease 0.075s;\n}\n.tox .tox-pop__dialog {\n  background-color: #222F3E;\n  border: 1px solid #161f29;\n  border-radius: 6px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  min-width: 0;\n  overflow: hidden;\n}\n.tox .tox-pop__dialog > *:not(.tox-toolbar) {\n  margin: 4px 4px 4px 8px;\n}\n.tox .tox-pop__dialog .tox-toolbar {\n  background-color: transparent;\n  margin-bottom: -1px;\n}\n.tox .tox-pop::before,\n.tox .tox-pop::after {\n  border-style: solid;\n  content: '';\n  display: block;\n  height: 0;\n  opacity: 1;\n  position: absolute;\n  width: 0;\n}\n.tox .tox-pop.tox-pop--inset::before,\n.tox .tox-pop.tox-pop--inset::after {\n  opacity: 0;\n  transition: all 0s 0.15s, visibility 0s, opacity 0.075s ease;\n}\n.tox .tox-pop.tox-pop--bottom::before,\n.tox .tox-pop.tox-pop--bottom::after {\n  left: 50%;\n  top: 100%;\n}\n.tox .tox-pop.tox-pop--bottom::after {\n  border-color: #222F3E transparent transparent transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: -1px;\n}\n.tox .tox-pop.tox-pop--bottom::before {\n  border-color: #161f29 transparent transparent transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--top::before,\n.tox .tox-pop.tox-pop--top::after {\n  left: 50%;\n  top: 0;\n  transform: translateY(-100%);\n}\n.tox .tox-pop.tox-pop--top::after {\n  border-color: transparent transparent #222F3E transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: 1px;\n}\n.tox .tox-pop.tox-pop--top::before {\n  border-color: transparent transparent #161f29 transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--left::before,\n.tox .tox-pop.tox-pop--left::after {\n  left: 0;\n  top: calc(50% - 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--left::after {\n  border-color: transparent #222F3E transparent transparent;\n  border-width: 8px;\n  margin-left: -15px;\n}\n.tox .tox-pop.tox-pop--left::before {\n  border-color: transparent #161f29 transparent transparent;\n  border-width: 10px;\n  margin-left: -19px;\n}\n.tox .tox-pop.tox-pop--right::before,\n.tox .tox-pop.tox-pop--right::after {\n  left: 100%;\n  top: calc(50% + 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--right::after {\n  border-color: transparent transparent transparent #222F3E;\n  border-width: 8px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--right::before {\n  border-color: transparent transparent transparent #161f29;\n  border-width: 10px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--align-left::before,\n.tox .tox-pop.tox-pop--align-left::after {\n  left: 20px;\n}\n.tox .tox-pop.tox-pop--align-right::before,\n.tox .tox-pop.tox-pop--align-right::after {\n  left: calc(100% - 20px);\n}\n.tox .tox-sidebar-wrap {\n  display: flex;\n  flex-direction: row;\n  flex-grow: 1;\n  min-height: 0;\n}\n.tox .tox-sidebar {\n  background-color: #222F3E;\n  display: flex;\n  flex-direction: row;\n  justify-content: flex-end;\n}\n.tox .tox-sidebar__slider {\n  display: flex;\n  overflow: hidden;\n}\n.tox .tox-sidebar__pane-container {\n  display: flex;\n}\n.tox .tox-sidebar__pane {\n  display: flex;\n}\n.tox .tox-sidebar--sliding-closed {\n  opacity: 0;\n}\n.tox .tox-sidebar--sliding-open {\n  opacity: 1;\n}\n.tox .tox-sidebar--sliding-growing,\n.tox .tox-sidebar--sliding-shrinking {\n  transition: width 0.5s ease, opacity 0.5s ease;\n}\n.tox .tox-selector {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  display: inline-block;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox.tox-platform-touch .tox-selector {\n  height: 12px;\n  width: 12px;\n}\n.tox .tox-slider {\n  align-items: center;\n  display: flex;\n  flex: 1;\n  height: 24px;\n  justify-content: center;\n  position: relative;\n}\n.tox .tox-slider__rail {\n  background-color: transparent;\n  border: 1px solid #161f29;\n  border-radius: 6px;\n  height: 10px;\n  min-width: 120px;\n  width: 100%;\n}\n.tox .tox-slider__handle {\n  background-color: #006ce7;\n  border: 2px solid #0054b4;\n  border-radius: 6px;\n  box-shadow: none;\n  height: 24px;\n  left: 50%;\n  position: absolute;\n  top: 50%;\n  transform: translateX(-50%) translateY(-50%);\n  width: 14px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider:not(:first-of-type) {\n  margin-inline-start: 8px;\n}\n.tox .tox-form__controls-h-stack > .tox-form__group + .tox-slider {\n  margin-inline-start: 32px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider + .tox-form__group {\n  margin-inline-start: 32px;\n}\n.tox .tox-source-code {\n  overflow: auto;\n}\n.tox .tox-spinner {\n  display: flex;\n}\n.tox .tox-spinner > div {\n  animation: tam-bouncing-dots 1.5s ease-in-out 0s infinite both;\n  background-color: rgba(255, 255, 255, 0.5);\n  border-radius: 100%;\n  height: 8px;\n  width: 8px;\n}\n.tox .tox-spinner > div:nth-child(1) {\n  animation-delay: -0.32s;\n}\n.tox .tox-spinner > div:nth-child(2) {\n  animation-delay: -0.16s;\n}\n@keyframes tam-bouncing-dots {\n  0%,\n  80%,\n  100% {\n    transform: scale(0);\n  }\n  40% {\n    transform: scale(1);\n  }\n}\n.tox:not([dir=rtl]) .tox-spinner > div:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-spinner > div:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-statusbar {\n  align-items: center;\n  background-color: #222F3E;\n  border-top: 1px solid rgba(255, 255, 255, 0.15);\n  color: rgba(255, 255, 255, 0.75);\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-weight: normal;\n  height: 25px;\n  overflow: hidden;\n  padding: 0 8px;\n  position: relative;\n  text-transform: none;\n}\n.tox .tox-statusbar__text-container {\n  display: flex;\n  flex: 1 1 auto;\n  justify-content: flex-end;\n  overflow: hidden;\n}\n.tox .tox-statusbar__path {\n  display: flex;\n  flex: 1 1 auto;\n  margin-right: auto;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__path > * {\n  display: inline;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__wordcount {\n  flex: 0 0 auto;\n  margin-left: 1ch;\n}\n.tox .tox-statusbar a,\n.tox .tox-statusbar__path-item,\n.tox .tox-statusbar__wordcount {\n  color: rgba(255, 255, 255, 0.75);\n  text-decoration: none;\n}\n.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) {\n  color: #fff;\n  cursor: pointer;\n}\n.tox .tox-statusbar__branding svg {\n  fill: rgba(255, 255, 255, 0.8);\n  height: 1.14em;\n  vertical-align: -0.28em;\n  width: 3.6em;\n}\n.tox .tox-statusbar__branding a:hover:not(:disabled):not([aria-disabled=true]) svg,\n.tox .tox-statusbar__branding a:focus:not(:disabled):not([aria-disabled=true]) svg {\n  fill: #fff;\n}\n.tox .tox-statusbar__resize-handle {\n  align-items: flex-end;\n  align-self: stretch;\n  cursor: nwse-resize;\n  display: flex;\n  flex: 0 0 auto;\n  justify-content: flex-end;\n  margin-left: auto;\n  margin-right: -8px;\n  padding-bottom: 3px;\n  padding-left: 1ch;\n  padding-right: 3px;\n}\n.tox .tox-statusbar__resize-handle svg {\n  display: block;\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-statusbar__resize-handle:focus svg {\n  background-color: #434e5b;\n  border-radius: 1px 1px 5px 1px;\n  box-shadow: 0 0 0 2px #434e5b;\n}\n.tox:not([dir=rtl]) .tox-statusbar__path > * {\n  margin-right: 4px;\n}\n.tox:not([dir=rtl]) .tox-statusbar__branding {\n  margin-left: 2ch;\n}\n.tox[dir=rtl] .tox-statusbar {\n  flex-direction: row-reverse;\n}\n.tox[dir=rtl] .tox-statusbar__path > * {\n  margin-left: 4px;\n}\n.tox .tox-throbber {\n  z-index: 1299;\n}\n.tox .tox-throbber__busy-spinner {\n  align-items: center;\n  background-color: rgba(34, 47, 62, 0.6);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n.tox .tox-tbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #fff;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 28px;\n  justify-content: center;\n  margin: 6px 1px 5px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0;\n  text-transform: none;\n  width: 34px;\n}\n.tox .tox-tbtn svg {\n  display: block;\n  fill: #fff;\n}\n.tox .tox-tbtn.tox-tbtn-more {\n  padding-left: 5px;\n  padding-right: 5px;\n  width: inherit;\n}\n.tox .tox-tbtn:focus {\n  background: #3389ec;\n  border: 0;\n  box-shadow: none;\n}\n.tox .tox-tbtn:hover {\n  background: #3389ec;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-tbtn:hover svg {\n  fill: #fff;\n}\n.tox .tox-tbtn:active {\n  background: #599fef;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-tbtn:active svg {\n  fill: #fff;\n}\n.tox .tox-tbtn--disabled,\n.tox .tox-tbtn--disabled:hover,\n.tox .tox-tbtn:disabled,\n.tox .tox-tbtn:disabled:hover {\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-tbtn--disabled svg,\n.tox .tox-tbtn--disabled:hover svg,\n.tox .tox-tbtn:disabled svg,\n.tox .tox-tbtn:disabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-tbtn--enabled,\n.tox .tox-tbtn--enabled:hover {\n  background: #599fef;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-tbtn--enabled > *,\n.tox .tox-tbtn--enabled:hover > * {\n  transform: none;\n}\n.tox .tox-tbtn--enabled svg,\n.tox .tox-tbtn--enabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: #fff;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) {\n  color: #fff;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg {\n  fill: #fff;\n}\n.tox .tox-tbtn:active > * {\n  transform: none;\n}\n.tox .tox-tbtn--md {\n  height: 42px;\n  width: 51px;\n}\n.tox .tox-tbtn--lg {\n  flex-direction: column;\n  height: 56px;\n  width: 68px;\n}\n.tox .tox-tbtn--return {\n  align-self: stretch;\n  height: unset;\n  width: 16px;\n}\n.tox .tox-tbtn--labeled {\n  padding: 0 4px;\n  width: unset;\n}\n.tox .tox-tbtn__vlabel {\n  display: block;\n  font-size: 10px;\n  font-weight: normal;\n  letter-spacing: -0.025em;\n  margin-bottom: 4px;\n  white-space: nowrap;\n}\n.tox .tox-tbtn--select {\n  margin: 6px 1px 5px 0;\n  padding: 0 4px;\n  width: auto;\n}\n.tox .tox-tbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-tbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-tbtn__select-chevron svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-tbtn--bespoke {\n  background: #2f4055;\n}\n.tox .tox-tbtn--bespoke + .tox-tbtn--bespoke {\n  margin-inline-start: 4px;\n}\n.tox .tox-tbtn--bespoke .tox-tbtn__select-label {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  width: 7em;\n}\n.tox .tox-split-button {\n  border: 0;\n  border-radius: 3px;\n  box-sizing: border-box;\n  display: flex;\n  margin: 6px 1px 5px 0;\n  overflow: hidden;\n}\n.tox .tox-split-button:hover {\n  box-shadow: 0 0 0 1px #3389ec inset;\n}\n.tox .tox-split-button:focus {\n  background: #3389ec;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-split-button > * {\n  border-radius: 0;\n}\n.tox .tox-split-button__chevron {\n  width: 16px;\n}\n.tox .tox-split-button__chevron svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-split-button .tox-tbtn {\n  margin: 0;\n}\n.tox .tox-split-button.tox-tbtn--disabled:hover,\n.tox .tox-split-button.tox-tbtn--disabled:focus,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus {\n  background: transparent;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn--select {\n  padding: 0 0px;\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn:not(.tox-tbtn--select):first-child {\n  width: 30px;\n}\n.tox.tox-platform-touch .tox-split-button__chevron {\n  width: 20px;\n}\n.tox .tox-toolbar-overlord {\n  background-color: #222F3E;\n}\n.tox .tox-toolbar,\n.tox .tox-toolbar__primary,\n.tox .tox-toolbar__overflow {\n  background-attachment: local;\n  background-color: #222F3E;\n  background-image: repeating-linear-gradient(rgba(255, 255, 255, 0.15) 0px 1px, transparent 1px 39px);\n  background-position: center top 40px;\n  background-repeat: no-repeat;\n  background-size: calc(100% - 11px * 2) calc(100% - 41px);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  padding: 0 0px;\n  transform: perspective(1px);\n}\n.tox .tox-toolbar-overlord > .tox-toolbar,\n.tox .tox-toolbar-overlord > .tox-toolbar__primary,\n.tox .tox-toolbar-overlord > .tox-toolbar__overflow {\n  background-position: center top 0px;\n  background-size: calc(100% - 11px * 2) calc(100% - 0px);\n}\n.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed {\n  height: 0;\n  opacity: 0;\n  padding-bottom: 0;\n  padding-top: 0;\n  visibility: hidden;\n}\n.tox .tox-toolbar__overflow--growing {\n  transition: height 0.3s ease, opacity 0.2s linear 0.1s;\n}\n.tox .tox-toolbar__overflow--shrinking {\n  transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;\n}\n.tox .tox-toolbar-overlord,\n.tox .tox-anchorbar {\n  grid-column: 1 / -1;\n}\n.tox .tox-menubar + .tox-toolbar,\n.tox .tox-menubar + .tox-toolbar-overlord {\n  border-top: 1px solid transparent;\n  margin-top: -1px;\n  padding-bottom: 1px;\n  padding-top: 1px;\n}\n.tox .tox-toolbar--scrolling {\n  flex-wrap: nowrap;\n  overflow-x: auto;\n}\n.tox .tox-pop .tox-toolbar {\n  border-width: 0;\n}\n.tox .tox-toolbar--no-divider {\n  background-image: none;\n}\n.tox .tox-toolbar-overlord .tox-toolbar:not(.tox-toolbar--scrolling):first-child,\n.tox .tox-toolbar-overlord .tox-toolbar__primary {\n  background-position: center top 39px;\n}\n.tox .tox-editor-header > .tox-toolbar--scrolling,\n.tox .tox-toolbar-overlord .tox-toolbar--scrolling:first-child {\n  background-image: none;\n}\n.tox.tox-tinymce-aux .tox-toolbar__overflow {\n  background-color: #222F3E;\n  background-position: center top 43px;\n  background-size: calc(100% - 8px * 2) calc(100% - 51px);\n  border: none;\n  border-radius: 6px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  overscroll-behavior: none;\n  padding: 4px 0;\n}\n.tox-pop .tox-pop__dialog {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox-pop .tox-pop__dialog .tox-toolbar {\n  background-position: center top 43px;\n  background-size: calc(100% - 11px * 2) calc(100% - 51px);\n  padding: 4px 0;\n}\n.tox .tox-toolbar__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: wrap;\n  margin: 0 0;\n  padding: 0 11px 0 12px;\n}\n.tox .tox-toolbar__group--pull-right {\n  margin-left: auto;\n}\n.tox .tox-toolbar--scrolling .tox-toolbar__group {\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n}\n.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type) {\n  border-right: 1px solid transparent;\n}\n.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type) {\n  border-left: 1px solid transparent;\n}\n.tox .tox-tooltip {\n  display: inline-block;\n  padding: 8px;\n  position: relative;\n}\n.tox .tox-tooltip__body {\n  background-color: #3d546f;\n  border-radius: 6px;\n  box-shadow: 0 2px 4px rgba(34, 47, 62, 0.3);\n  color: rgba(255, 255, 255, 0.75);\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  padding: 4px 8px;\n  text-transform: none;\n}\n.tox .tox-tooltip__arrow {\n  position: absolute;\n}\n.tox .tox-tooltip--down .tox-tooltip__arrow {\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  border-top: 8px solid #3d546f;\n  bottom: 0;\n  left: 50%;\n  position: absolute;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--up .tox-tooltip__arrow {\n  border-bottom: 8px solid #3d546f;\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  left: 50%;\n  position: absolute;\n  top: 0;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--right .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-left: 8px solid #3d546f;\n  border-top: 8px solid transparent;\n  position: absolute;\n  right: 0;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-tooltip--left .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-right: 8px solid #3d546f;\n  border-top: 8px solid transparent;\n  left: 0;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-view-wrap,\n.tox .tox-view-wrap__slot-container {\n  background-color: #222F3E;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view__header {\n  align-items: center;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 8px 0 8px;\n  position: relative;\n}\n.tox .tox-view__header-start,\n.tox .tox-view__header-end {\n  display: flex;\n}\n.tox .tox-view__pane {\n  height: 100%;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-view__pane_panel {\n  border: 1px solid #161f29;\n  border-radius: 6px;\n}\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-start > *,\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-view__header .tox-view__header-start > *,\n.tox[dir=rtl] .tox-view__header .tox-view__header-end > * {\n  margin-right: 8px;\n}\n.tox .tox-well {\n  border: 1px solid #161f29;\n  border-radius: 6px;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-well > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-well > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-well > *:only-child {\n  margin: 0;\n}\n.tox .tox-custom-editor {\n  border: 1px solid #161f29;\n  border-radius: 6px;\n  display: flex;\n  flex: 1;\n  position: relative;\n}\n/* stylelint-disable */\n.tox {\n  /* stylelint-enable */\n}\n.tox .tox-dialog-loading::before {\n  background-color: rgba(0, 0, 0, 0.5);\n  content: \"\";\n  height: 100%;\n  position: absolute;\n  width: 100%;\n  z-index: 1000;\n}\n.tox .tox-tab {\n  cursor: pointer;\n}\n.tox .tox-dialog__content-js {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-content .tox-collection {\n  display: flex;\n  flex: 1;\n}\n.tox.tox-tinymce-aux .tox-toolbar__overflow {\n  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.15);\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/oxide-dark/skin.shadowdom.css",
    "content": "body.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5/content.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: black;\n  background: none;\n  text-shadow: 0 1px white;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"]::-moz-selection,\npre[class*=\"language-\"] ::-moz-selection,\ncode[class*=\"language-\"]::-moz-selection,\ncode[class*=\"language-\"] ::-moz-selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\npre[class*=\"language-\"]::selection,\npre[class*=\"language-\"] ::selection,\ncode[class*=\"language-\"]::selection,\ncode[class*=\"language-\"] ::selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n@media print {\n  code[class*=\"language-\"],\n  pre[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #f5f2f0;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: slategray;\n}\n.token.punctuation {\n  color: #999;\n}\n.token.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #905;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #690;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #9a6e3a;\n  /* This background color was intended by the author of this theme. */\n  background: hsla(0, 0%, 100%, 0.5);\n}\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #07a;\n}\n.token.function,\n.token.class-name {\n  color: #DD4A68;\n}\n.token.regex,\n.token.important,\n.token.variable {\n  color: #e90;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.1);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #b4d7ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid rgba(180, 215, 255, 0.7);\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: multiply;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #b4d7ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\nbody {\n  font-family: sans-serif;\n}\ntable {\n  border-collapse: collapse;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5/content.inline.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: black;\n  background: none;\n  text-shadow: 0 1px white;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"]::-moz-selection,\npre[class*=\"language-\"] ::-moz-selection,\ncode[class*=\"language-\"]::-moz-selection,\ncode[class*=\"language-\"] ::-moz-selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\npre[class*=\"language-\"]::selection,\npre[class*=\"language-\"] ::selection,\ncode[class*=\"language-\"]::selection,\ncode[class*=\"language-\"] ::selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n@media print {\n  code[class*=\"language-\"],\n  pre[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #f5f2f0;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: slategray;\n}\n.token.punctuation {\n  color: #999;\n}\n.token.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #905;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #690;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #9a6e3a;\n  /* This background color was intended by the author of this theme. */\n  background: hsla(0, 0%, 100%, 0.5);\n}\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #07a;\n}\n.token.function,\n.token.class-name {\n  color: #DD4A68;\n}\n.token.regex,\n.token.important,\n.token.variable {\n  color: #e90;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.1);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #b4d7ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid rgba(180, 215, 255, 0.7);\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: multiply;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #b4d7ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5/skin.css",
    "content": ".tox {\n  box-shadow: none;\n  box-sizing: content-box;\n  color: #222f3e;\n  cursor: auto;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: normal;\n  -webkit-tap-highlight-color: transparent;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  vertical-align: initial;\n  white-space: normal;\n}\n.tox *:not(svg):not(rect) {\n  box-sizing: inherit;\n  color: inherit;\n  cursor: inherit;\n  direction: inherit;\n  font-family: inherit;\n  font-size: inherit;\n  font-style: inherit;\n  font-weight: inherit;\n  line-height: inherit;\n  -webkit-tap-highlight-color: inherit;\n  text-align: inherit;\n  text-decoration: inherit;\n  text-shadow: inherit;\n  text-transform: inherit;\n  vertical-align: inherit;\n  white-space: inherit;\n}\n.tox *:not(svg):not(rect) {\n  /* stylelint-disable-line no-duplicate-selectors */\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  float: none;\n  height: auto;\n  margin: 0;\n  max-width: none;\n  outline: 0;\n  padding: 0;\n  position: static;\n  width: auto;\n}\n.tox:not([dir=rtl]) {\n  direction: ltr;\n  text-align: left;\n}\n.tox[dir=rtl] {\n  direction: rtl;\n  text-align: right;\n}\n.tox-tinymce {\n  border: 1px solid #cccccc;\n  border-radius: 0;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  overflow: hidden;\n  position: relative;\n  visibility: inherit !important;\n}\n.tox.tox-tinymce-inline {\n  border: none;\n  box-shadow: none;\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-container {\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-header {\n  background-color: #fff;\n  border: 1px solid #cccccc;\n  border-radius: 0;\n  box-shadow: none;\n  overflow: hidden;\n}\n.tox-tinymce-aux {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  z-index: 1300;\n}\n.tox-tinymce *:focus,\n.tox-tinymce-aux *:focus {\n  outline: none;\n}\nbutton::-moz-focus-inner {\n  border: 0;\n}\n.tox[dir=rtl] .tox-icon--flip svg {\n  transform: rotateY(180deg);\n}\n.tox .accessibility-issue__header {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description {\n  align-items: stretch;\n  border-radius: 3px;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .accessibility-issue__description > div {\n  padding-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div .tox-icon svg {\n  display: block;\n}\n.tox .accessibility-issue__repair {\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description {\n  background-color: rgba(30, 113, 170, 0.1);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2 {\n  color: #207ab7;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg {\n  fill: #207ab7;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon {\n  background-color: #207ab7;\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:focus {\n  background-color: #1c6ca1;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:active {\n  background-color: #185d8c;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description {\n  background-color: rgba(255, 165, 0, 0.08);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2 {\n  color: #8f5d00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg {\n  fill: #8f5d00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon {\n  background-color: #FFE89D;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:focus {\n  background-color: #F2D574;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:active {\n  background-color: #E8C657;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description {\n  background-color: rgba(204, 0, 0, 0.1);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2 {\n  color: #c00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg {\n  fill: #c00;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon {\n  background-color: #F2BFBF;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:focus {\n  background-color: #E9A4A4;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:active {\n  background-color: #EE9494;\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description {\n  background-color: rgba(120, 171, 70, 0.1);\n  color: #222f3e;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description > *:last-child {\n  display: none;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2 {\n  color: #527530;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg {\n  fill: #527530;\n}\n.tox .tox-dialog__body-content .accessibility-issue__header .tox-form__group h1,\n.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2 {\n  font-size: 14px;\n  margin-top: 0;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-left: auto;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 4px 4px 8px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-right: auto;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 8px 4px 4px;\n}\n.tox .tox-anchorbar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-bar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-button {\n  background-color: #207ab7;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #207ab7;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  line-height: 24px;\n  margin: 0;\n  outline: none;\n  padding: 4px 16px;\n  position: relative;\n  text-align: center;\n  text-decoration: none;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-button::before {\n  border-radius: 3px;\n  bottom: -1px;\n  box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #207ab7, 0 0 0 3px rgba(32, 122, 183, 0.25);\n  content: '';\n  left: -1px;\n  opacity: 0;\n  pointer-events: none;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n.tox .tox-button[disabled] {\n  background-color: #207ab7;\n  background-image: none;\n  border-color: #207ab7;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-button:focus:not(:disabled) {\n  background-color: #1c6ca1;\n  background-image: none;\n  border-color: #1c6ca1;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:focus-visible:not(:disabled)::before {\n  opacity: 1;\n}\n.tox .tox-button:hover:not(:disabled) {\n  background-color: #1c6ca1;\n  background-image: none;\n  border-color: #1c6ca1;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:active:not(:disabled) {\n  background-color: #185d8c;\n  background-image: none;\n  border-color: #185d8c;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary {\n  background-color: #f0f0f0;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #f0f0f0;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  color: #222f3e;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  outline: none;\n  padding: 4px 16px;\n  text-decoration: none;\n  text-transform: none;\n}\n.tox .tox-button--secondary[disabled] {\n  background-color: #f0f0f0;\n  background-image: none;\n  border-color: #f0f0f0;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-button--secondary:focus:not(:disabled) {\n  background-color: #e3e3e3;\n  background-image: none;\n  border-color: #e3e3e3;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--secondary:hover:not(:disabled) {\n  background-color: #e3e3e3;\n  background-image: none;\n  border-color: #e3e3e3;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--secondary:active:not(:disabled) {\n  background-color: #d6d6d6;\n  background-image: none;\n  border-color: #d6d6d6;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--icon,\n.tox .tox-button.tox-button--icon,\n.tox .tox-button.tox-button--secondary.tox-button--icon {\n  padding: 4px;\n}\n.tox .tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg {\n  display: block;\n  fill: currentColor;\n}\n.tox .tox-button-link {\n  background: 0;\n  border: none;\n  box-sizing: border-box;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  padding: 0;\n  white-space: nowrap;\n}\n.tox .tox-button-link--sm {\n  font-size: 14px;\n}\n.tox .tox-button--naked {\n  background-color: transparent;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #222f3e;\n}\n.tox .tox-button--naked[disabled] {\n  background-color: #f0f0f0;\n  border-color: #f0f0f0;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-button--naked:hover:not(:disabled) {\n  background-color: #e3e3e3;\n  border-color: #e3e3e3;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--naked:focus:not(:disabled) {\n  background-color: #e3e3e3;\n  border-color: #e3e3e3;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--naked:active:not(:disabled) {\n  background-color: #d6d6d6;\n  border-color: #d6d6d6;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-button--naked .tox-icon svg {\n  fill: currentColor;\n}\n.tox .tox-button--naked.tox-button--icon:hover:not(:disabled) {\n  color: #222f3e;\n}\n.tox .tox-checkbox {\n  align-items: center;\n  border-radius: 3px;\n  cursor: pointer;\n  display: flex;\n  height: 36px;\n  min-width: 36px;\n}\n.tox .tox-checkbox__input {\n  /* Hide from view but visible to screen readers */\n  height: 1px;\n  overflow: hidden;\n  position: absolute;\n  top: auto;\n  width: 1px;\n}\n.tox .tox-checkbox__icons {\n  align-items: center;\n  border-radius: 3px;\n  box-shadow: 0 0 0 2px transparent;\n  box-sizing: content-box;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  padding: calc(4px - 1px);\n  width: 24px;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: block;\n  fill: rgba(34, 47, 62, 0.3);\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: none;\n  fill: #207ab7;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: none;\n  fill: #207ab7;\n}\n.tox .tox-checkbox--disabled {\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:focus + .tox-checkbox__icons {\n  border-radius: 3px;\n  box-shadow: inset 0 0 0 1px #207ab7;\n  padding: calc(4px - 1px);\n}\n.tox:not([dir=rtl]) .tox-checkbox__label {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-checkbox__input {\n  left: -10000px;\n}\n.tox:not([dir=rtl]) .tox-bar .tox-checkbox {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__label {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__input {\n  right: -10000px;\n}\n.tox[dir=rtl] .tox-bar .tox-checkbox {\n  margin-right: 4px;\n}\n.tox {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-collection--toolbar .tox-collection__group {\n  display: flex;\n  padding: 0;\n}\n.tox .tox-collection--grid .tox-collection__group {\n  display: flex;\n  flex-wrap: wrap;\n  max-height: 208px;\n  overflow-x: hidden;\n  overflow-y: auto;\n  padding: 0;\n}\n.tox .tox-collection--list .tox-collection__group {\n  border-bottom-width: 0;\n  border-color: #cccccc;\n  border-left-width: 0;\n  border-right-width: 0;\n  border-style: solid;\n  border-top-width: 1px;\n  padding: 4px 0;\n}\n.tox .tox-collection--list .tox-collection__group:first-child {\n  border-top-width: 0;\n}\n.tox .tox-collection__group-heading {\n  background-color: #e6e6e6;\n  color: rgba(34, 47, 62, 0.7);\n  cursor: default;\n  font-size: 12px;\n  font-style: normal;\n  font-weight: normal;\n  margin-bottom: 4px;\n  margin-top: -4px;\n  padding: 4px 8px;\n  text-transform: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection__item {\n  align-items: center;\n  border-radius: 3px;\n  color: #222f3e;\n  display: flex;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection--list .tox-collection__item {\n  padding: 4px 8px;\n}\n.tox .tox-collection--toolbar .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--grid .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--list .tox-collection__item--enabled {\n  background-color: #fff;\n  color: #222f3e;\n}\n.tox .tox-collection--list .tox-collection__item--active {\n  background-color: #dee0e2;\n}\n.tox .tox-collection--toolbar .tox-collection__item--enabled {\n  background-color: #c8cbcf;\n  color: #222f3e;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active {\n  background-color: #dee0e2;\n}\n.tox .tox-collection--grid .tox-collection__item--enabled {\n  background-color: #c8cbcf;\n  color: #222f3e;\n}\n.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  background-color: #dee0e2;\n  color: #222f3e;\n}\n.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #222f3e;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #222f3e;\n}\n.tox .tox-collection__item-icon,\n.tox .tox-collection__item-checkmark {\n  align-items: center;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  width: 24px;\n}\n.tox .tox-collection__item-icon svg,\n.tox .tox-collection__item-checkmark svg {\n  fill: currentColor;\n}\n.tox .tox-collection--toolbar-lg .tox-collection__item-icon {\n  height: 48px;\n  width: 48px;\n}\n.tox .tox-collection__item-label {\n  color: currentColor;\n  display: inline-block;\n  flex: 1;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 24px;\n  text-transform: none;\n  word-break: break-all;\n}\n.tox .tox-collection__item-accessory {\n  color: rgba(34, 47, 62, 0.7);\n  display: inline-block;\n  font-size: 14px;\n  height: 24px;\n  line-height: 24px;\n  text-transform: none;\n}\n.tox .tox-collection__item-caret {\n  align-items: center;\n  display: flex;\n  min-height: 24px;\n}\n.tox .tox-collection__item-caret::after {\n  content: '';\n  font-size: 0;\n  min-height: inherit;\n}\n.tox .tox-collection__item-caret svg {\n  fill: #222f3e;\n}\n.tox .tox-collection__item--state-disabled {\n  background-color: transparent;\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg {\n  display: none;\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory + .tox-collection__item-checkmark {\n  display: none;\n}\n.tox .tox-collection--horizontal {\n  background-color: #fff;\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n  margin-bottom: 0;\n  overflow-x: auto;\n  padding: 0;\n}\n.tox .tox-collection--horizontal .tox-collection__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: nowrap;\n  margin: 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item {\n  height: 34px;\n  margin: 3px 0 2px 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item-label {\n  white-space: nowrap;\n}\n.tox .tox-collection--horizontal .tox-collection__item-caret {\n  margin-left: 4px;\n}\n.tox .tox-collection__item-container {\n  display: flex;\n}\n.tox .tox-collection__item-container--row {\n  align-items: center;\n  flex: 1 1 auto;\n  flex-direction: row;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-left {\n  margin-right: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-right {\n  justify-content: flex-end;\n  margin-left: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top {\n  align-items: flex-start;\n  margin-bottom: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle {\n  align-items: center;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom {\n  align-items: flex-end;\n  margin-top: auto;\n}\n.tox .tox-collection__item-container--column {\n  align-self: center;\n  flex: 1 1 auto;\n  flex-direction: column;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-left {\n  align-items: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-right {\n  align-items: flex-end;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top {\n  align-self: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle {\n  align-self: center;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom {\n  align-self: flex-end;\n}\n.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-right: 1px solid #cccccc;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-collection__item-accessory {\n  margin-left: 16px;\n  text-align: right;\n}\n.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret {\n  margin-left: 16px;\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-left: 1px solid #cccccc;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-collection__item-accessory {\n  margin-right: 16px;\n  text-align: left;\n}\n.tox[dir=rtl] .tox-collection .tox-collection__item-caret {\n  margin-right: 16px;\n  transform: rotateY(180deg);\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret {\n  margin-right: 4px;\n}\n.tox .tox-color-picker-container {\n  display: flex;\n  flex-direction: row;\n  height: 225px;\n  margin: 0;\n}\n.tox .tox-sv-palette {\n  box-sizing: border-box;\n  display: flex;\n  height: 100%;\n}\n.tox .tox-sv-palette-spectrum {\n  height: 100%;\n}\n.tox .tox-sv-palette,\n.tox .tox-sv-palette-spectrum {\n  width: 225px;\n}\n.tox .tox-sv-palette-thumb {\n  background: none;\n  border: 1px solid black;\n  border-radius: 50%;\n  box-sizing: content-box;\n  height: 12px;\n  position: absolute;\n  width: 12px;\n}\n.tox .tox-sv-palette-inner-thumb {\n  border: 1px solid white;\n  border-radius: 50%;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox .tox-hue-slider {\n  box-sizing: border-box;\n  height: 100%;\n  width: 25px;\n}\n.tox .tox-hue-slider-spectrum {\n  background: linear-gradient(to bottom, #f00, #ff0080, #f0f, #8000ff, #00f, #0080ff, #0ff, #00ff80, #0f0, #80ff00, #ff0, #ff8000, #f00);\n  height: 100%;\n  width: 100%;\n}\n.tox .tox-hue-slider,\n.tox .tox-hue-slider-spectrum {\n  width: 20px;\n}\n.tox .tox-hue-slider-thumb {\n  background: white;\n  border: 1px solid black;\n  box-sizing: content-box;\n  height: 4px;\n  width: 100%;\n}\n.tox .tox-rgb-form {\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n}\n.tox .tox-rgb-form div {\n  align-items: center;\n  display: flex;\n  justify-content: space-between;\n  margin-bottom: 5px;\n  width: inherit;\n}\n.tox .tox-rgb-form input {\n  width: 6em;\n}\n.tox .tox-rgb-form input.tox-invalid {\n  /* Need !important to override Chrome's focus styling unfortunately */\n  border: 1px solid red !important;\n}\n.tox .tox-rgb-form .tox-rgba-preview {\n  border: 1px solid black;\n  flex-grow: 2;\n  margin-bottom: 0;\n}\n.tox:not([dir=rtl]) .tox-sv-palette {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider-thumb {\n  margin-left: -1px;\n}\n.tox:not([dir=rtl]) .tox-rgb-form label {\n  margin-right: 0.5em;\n}\n.tox[dir=rtl] .tox-sv-palette {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider-thumb {\n  margin-right: -1px;\n}\n.tox[dir=rtl] .tox-rgb-form label {\n  margin-left: 0.5em;\n}\n.tox .tox-toolbar .tox-swatches,\n.tox .tox-toolbar__primary .tox-swatches,\n.tox .tox-toolbar__overflow .tox-swatches {\n  margin: 2px 0 3px 4px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-swatches-menu {\n  border: 0;\n  margin: -4px 0;\n}\n.tox .tox-swatches__row {\n  display: flex;\n}\n.tox .tox-swatch {\n  height: 30px;\n  transition: transform 0.15s, box-shadow 0.15s;\n  width: 30px;\n}\n.tox .tox-swatch:hover,\n.tox .tox-swatch:focus {\n  box-shadow: 0 0 0 1px rgba(127, 127, 127, 0.3) inset;\n  transform: scale(0.8);\n}\n.tox .tox-swatch--remove {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n}\n.tox .tox-swatch--remove svg path {\n  stroke: #e74c3c;\n}\n.tox .tox-swatches__picker-btn {\n  align-items: center;\n  background-color: transparent;\n  border: 0;\n  cursor: pointer;\n  display: flex;\n  height: 30px;\n  justify-content: center;\n  outline: none;\n  padding: 0;\n  width: 30px;\n}\n.tox .tox-swatches__picker-btn svg {\n  fill: #222f3e;\n  height: 24px;\n  width: 24px;\n}\n.tox .tox-swatches__picker-btn:hover {\n  background: #dee0e2;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg {\n  display: none;\n  fill: #222f3e;\n  height: 24px;\n  margin: calc((30px - 24px) / 2) calc((30px - 24px) / 2);\n  width: 24px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg path {\n  fill: #fff;\n  paint-order: stroke;\n  stroke: #222f3e;\n  stroke-width: 2px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove)[aria-checked=\"true\"] svg {\n  display: block;\n}\n.tox:not([dir=rtl]) .tox-swatches__picker-btn {\n  margin-left: auto;\n}\n.tox[dir=rtl] .tox-swatches__picker-btn {\n  margin-right: auto;\n}\n.tox .tox-comment-thread {\n  background: #fff;\n  position: relative;\n}\n.tox .tox-comment-thread > *:not(:first-child) {\n  margin-top: 8px;\n}\n.tox .tox-comment {\n  background: #fff;\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n  box-shadow: 0 4px 8px 0 rgba(34, 47, 62, 0.1);\n  padding: 8px 8px 16px 8px;\n  position: relative;\n}\n.tox .tox-comment__header {\n  align-items: center;\n  color: #222f3e;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .tox-comment__date {\n  color: #222f3e;\n  font-size: 12px;\n  line-height: 18px;\n}\n.tox .tox-comment__body {\n  color: #222f3e;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin-top: 8px;\n  position: relative;\n  text-transform: initial;\n}\n.tox .tox-comment__body textarea {\n  resize: none;\n  white-space: normal;\n  width: 100%;\n}\n.tox .tox-comment__expander {\n  padding-top: 8px;\n}\n.tox .tox-comment__expander p {\n  color: rgba(34, 47, 62, 0.7);\n  font-size: 14px;\n  font-style: normal;\n}\n.tox .tox-comment__body p {\n  margin: 0;\n}\n.tox .tox-comment__buttonspacing {\n  padding-top: 16px;\n  text-align: center;\n}\n.tox .tox-comment-thread__overlay::after {\n  background: #fff;\n  bottom: 0;\n  content: \"\";\n  display: flex;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__reply {\n  display: flex;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 8px;\n}\n.tox .tox-comment__reply > *:first-child {\n  margin-bottom: 8px;\n  width: 100%;\n}\n.tox .tox-comment__edit {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 16px;\n}\n.tox .tox-comment__gradient::after {\n  background: linear-gradient(rgba(255, 255, 255, 0), #fff);\n  bottom: 0;\n  content: \"\";\n  display: block;\n  height: 5em;\n  margin-top: -40px;\n  position: absolute;\n  width: 100%;\n}\n.tox .tox-comment__overlay {\n  background: #fff;\n  bottom: 0;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  text-align: center;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__loading-text {\n  align-items: center;\n  color: #222f3e;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n}\n.tox .tox-comment__loading-text > div {\n  padding-bottom: 16px;\n}\n.tox .tox-comment__overlaytext {\n  bottom: 0;\n  flex-direction: column;\n  font-size: 14px;\n  left: 0;\n  padding: 1em;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 10;\n}\n.tox .tox-comment__overlaytext p {\n  background-color: #fff;\n  box-shadow: 0 0 8px 8px #fff;\n  color: #222f3e;\n  text-align: center;\n}\n.tox .tox-comment__overlaytext div:nth-of-type(2) {\n  font-size: 0.8em;\n}\n.tox .tox-comment__busy-spinner {\n  align-items: center;\n  background-color: #fff;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 20;\n}\n.tox .tox-comment__scroll {\n  display: flex;\n  flex-direction: column;\n  flex-shrink: 1;\n  overflow: auto;\n}\n.tox .tox-conversations {\n  margin: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__edit {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__buttonspacing > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__edit > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__reply > *:last-child {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-comment__edit {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-comment__buttonspacing > *:last-child,\n.tox[dir=rtl] .tox-comment__edit > *:last-child,\n.tox[dir=rtl] .tox-comment__reply > *:last-child {\n  margin-right: 8px;\n}\n.tox .tox-user {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-user__avatar svg {\n  fill: rgba(34, 47, 62, 0.7);\n}\n.tox .tox-user__avatar img {\n  border-radius: 50%;\n  height: 36px;\n  object-fit: cover;\n  vertical-align: middle;\n  width: 36px;\n}\n.tox .tox-user__name {\n  color: #222f3e;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  line-height: 18px;\n  text-transform: none;\n}\n.tox:not([dir=rtl]) .tox-user__avatar svg,\n.tox:not([dir=rtl]) .tox-user__avatar img {\n  margin-right: 8px;\n}\n.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar svg,\n.tox[dir=rtl] .tox-user__avatar img {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar + .tox-user__name {\n  margin-right: 8px;\n}\n.tox .tox-dialog-wrap {\n  align-items: center;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1100;\n}\n.tox .tox-dialog-wrap__backdrop {\n  background-color: rgba(255, 255, 255, 0.75);\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 1;\n}\n.tox .tox-dialog-wrap__backdrop--opaque {\n  background-color: #fff;\n}\n.tox .tox-dialog {\n  background-color: #fff;\n  border-color: #cccccc;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: 0 16px 16px -10px rgba(34, 47, 62, 0.15), 0 0 40px 1px rgba(34, 47, 62, 0.15);\n  display: flex;\n  flex-direction: column;\n  max-height: 100%;\n  max-width: 480px;\n  overflow: hidden;\n  position: relative;\n  width: 95vw;\n  z-index: 2;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog {\n    align-self: flex-start;\n    margin: 8px auto;\n    max-height: calc(100vh - 8px * 2);\n    width: calc(100vw - 16px);\n  }\n}\n.tox .tox-dialog-inline {\n  z-index: 1100;\n}\n.tox .tox-dialog__header {\n  align-items: center;\n  background-color: #fff;\n  border-bottom: none;\n  color: #222f3e;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 16px 0 16px;\n  position: relative;\n}\n.tox .tox-dialog__header .tox-button {\n  z-index: 1;\n}\n.tox .tox-dialog__draghandle {\n  cursor: grab;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tox .tox-dialog__draghandle:active {\n  cursor: grabbing;\n}\n.tox .tox-dialog__dismiss {\n  margin-left: auto;\n}\n.tox .tox-dialog__title {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  text-transform: none;\n}\n.tox .tox-dialog__body {\n  color: #222f3e;\n  display: flex;\n  flex: 1;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  min-width: 0;\n  text-align: left;\n  text-transform: none;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body {\n    flex-direction: column;\n  }\n}\n.tox .tox-dialog__body-nav {\n  align-items: flex-start;\n  display: flex;\n  flex-direction: column;\n  padding: 16px 16px;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {\n    flex-direction: row;\n    -webkit-overflow-scrolling: touch;\n    overflow-x: auto;\n    padding-bottom: 0;\n  }\n}\n.tox .tox-dialog__body-nav-item {\n  border-bottom: 2px solid transparent;\n  color: rgba(34, 47, 62, 0.7);\n  display: inline-block;\n  font-size: 14px;\n  line-height: 1.3;\n  margin-bottom: 8px;\n  text-decoration: none;\n  white-space: nowrap;\n}\n.tox .tox-dialog__body-nav-item:focus {\n  background-color: rgba(32, 122, 183, 0.1);\n}\n.tox .tox-dialog__body-nav-item--active {\n  border-bottom: 2px solid #207ab7;\n  color: #207ab7;\n}\n.tox .tox-dialog__body-content {\n  box-sizing: border-box;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n  max-height: 650px;\n  overflow: auto;\n  -webkit-overflow-scrolling: touch;\n  padding: 16px 16px;\n}\n.tox .tox-dialog__body-content > * {\n  margin-bottom: 0;\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content > *:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content a {\n  color: #207ab7;\n  cursor: pointer;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:hover,\n.tox .tox-dialog__body-content a:focus {\n  color: #185d8c;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:active {\n  color: #185d8c;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content svg {\n  fill: #222f3e;\n}\n.tox .tox-dialog__body-content ul {\n  display: block;\n  list-style-type: disc;\n  margin-bottom: 16px;\n  margin-inline-end: 0;\n  margin-inline-start: 0;\n  padding-inline-start: 2.5rem;\n}\n.tox .tox-dialog__body-content .tox-form__group h1 {\n  color: #222f3e;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group h2 {\n  color: #222f3e;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group p {\n  margin-bottom: 16px;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:first-child,\n.tox .tox-dialog__body-content .tox-form__group h2:first-child,\n.tox .tox-dialog__body-content .tox-form__group p:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:last-child,\n.tox .tox-dialog__body-content .tox-form__group h2:last-child,\n.tox .tox-dialog__body-content .tox-form__group p:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:only-child,\n.tox .tox-dialog__body-content .tox-form__group h2:only-child,\n.tox .tox-dialog__body-content .tox-form__group p:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog--width-lg {\n  height: 650px;\n  max-width: 1200px;\n}\n.tox .tox-dialog--width-md {\n  max-width: 800px;\n}\n.tox .tox-dialog--width-md .tox-dialog__body-content {\n  overflow: auto;\n}\n.tox .tox-dialog__body-content--centered {\n  text-align: center;\n}\n.tox .tox-dialog__footer {\n  align-items: center;\n  background-color: #fff;\n  border-top: 1px solid #cccccc;\n  display: flex;\n  justify-content: space-between;\n  padding: 8px 16px;\n}\n.tox .tox-dialog__footer-start,\n.tox .tox-dialog__footer-end {\n  display: flex;\n}\n.tox .tox-dialog__busy-spinner {\n  align-items: center;\n  background-color: rgba(255, 255, 255, 0.75);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 3;\n}\n.tox .tox-dialog__table {\n  border-collapse: collapse;\n  width: 100%;\n}\n.tox .tox-dialog__table thead th {\n  font-weight: bold;\n  padding-bottom: 8px;\n}\n.tox .tox-dialog__table tbody tr {\n  border-bottom: 1px solid #cccccc;\n}\n.tox .tox-dialog__table tbody tr:last-child {\n  border-bottom: none;\n}\n.tox .tox-dialog__table td {\n  padding-bottom: 8px;\n  padding-top: 8px;\n}\n.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {\n  background: #fff;\n}\n.tox .tox-dialog__popups {\n  position: absolute;\n  width: 100%;\n  z-index: 1100;\n}\n.tox .tox-dialog__body-iframe {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-dialog__body-iframe .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox .tox-dialog-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox .tox-dialog-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox .tox-dialog-dock-transition {\n  transition: visibility 0s linear 0.3s, opacity 0.3s ease;\n}\n.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {\n  transition-delay: 0s;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {\n    margin-right: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {\n    margin-left: 8px;\n  }\n}\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-dialog__body {\n  text-align: right;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {\n    margin-left: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {\n    margin-right: 8px;\n  }\n}\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-right: 8px;\n}\nbody.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox .tox-dropzone-container {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dropzone {\n  align-items: center;\n  background: #fff;\n  border: 2px dashed #cccccc;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  justify-content: center;\n  min-height: 100px;\n  padding: 10px;\n}\n.tox .tox-dropzone p {\n  color: rgba(34, 47, 62, 0.7);\n  margin: 0 0 16px 0;\n}\n.tox .tox-edit-area {\n  display: flex;\n  flex: 1;\n  overflow: hidden;\n  position: relative;\n}\n.tox .tox-edit-area__iframe {\n  background-color: #fff;\n  border: 0;\n  box-sizing: border-box;\n  flex: 1;\n  height: 100%;\n  position: absolute;\n  width: 100%;\n}\n.tox.tox-inline-edit-area {\n  border: 1px dotted #cccccc;\n}\n.tox .tox-editor-container {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  overflow: hidden;\n}\n.tox .tox-editor-header {\n  display: grid;\n  grid-template-columns: 1fr min-content;\n  z-index: 1;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header {\n  background-color: #fff;\n  border-bottom: none;\n  box-shadow: none;\n  padding: 4px 0;\n  transition: box-shadow 0.5s;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-bottom .tox-editor-header {\n  border-top: 1px solid #cccccc;\n  box-shadow: none;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on .tox-editor-header {\n  background-color: #fff;\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n  padding: 4px 0;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on.tox-tinymce--toolbar-bottom .tox-editor-header {\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n}\n.tox.tox:not(.tox-tinymce-inline) .tox-editor-header.tox-editor-header--empty {\n  background: none;\n  border: none;\n  box-shadow: none;\n  padding: 0;\n}\n.tox-editor-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox-editor-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox-editor-dock-transition {\n  transition: visibility 0s linear 0.25s, opacity 0.25s ease;\n}\n.tox-editor-dock-transition.tox-editor-dock-fadein {\n  transition-delay: 0s;\n}\n.tox .tox-control-wrap {\n  flex: 1;\n  position: relative;\n}\n.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid {\n  display: none;\n}\n.tox .tox-control-wrap svg {\n  display: block;\n}\n.tox .tox-control-wrap__status-icon-wrap {\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-control-wrap__status-icon-invalid svg {\n  fill: #c00;\n}\n.tox .tox-control-wrap__status-icon-unknown svg {\n  fill: orange;\n}\n.tox .tox-control-wrap__status-icon-valid svg {\n  fill: green;\n}\n.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield {\n  padding-right: 32px;\n}\n.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap {\n  right: 4px;\n}\n.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield {\n  padding-left: 32px;\n}\n.tox[dir=rtl] .tox-control-wrap__status-icon-wrap {\n  left: 4px;\n}\n.tox .tox-autocompleter {\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-menu {\n  box-sizing: border-box;\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-autocompleter-highlight {\n  font-weight: bold;\n}\n.tox .tox-color-input {\n  display: flex;\n  position: relative;\n  z-index: 1;\n}\n.tox .tox-color-input .tox-textfield {\n  z-index: -1;\n}\n.tox .tox-color-input span {\n  border-color: rgba(34, 47, 62, 0.2);\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  height: 24px;\n  position: absolute;\n  top: 6px;\n  width: 24px;\n}\n.tox .tox-color-input span:hover:not([aria-disabled=true]),\n.tox .tox-color-input span:focus:not([aria-disabled=true]) {\n  border-color: #207ab7;\n  cursor: pointer;\n}\n.tox .tox-color-input span::before {\n  background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(-45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%), linear-gradient(-45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%);\n  background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n  background-size: 12px 12px;\n  border: 1px solid #fff;\n  border-radius: 3px;\n  box-sizing: border-box;\n  content: '';\n  height: 24px;\n  left: -1px;\n  position: absolute;\n  top: -1px;\n  width: 24px;\n  z-index: -1;\n}\n.tox .tox-color-input span[aria-disabled=true] {\n  cursor: not-allowed;\n}\n.tox:not([dir=rtl]) .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-color-input .tox-textfield {\n  padding-left: 36px;\n}\n.tox:not([dir=rtl]) .tox-color-input span {\n  left: 6px;\n}\n.tox[dir=\"rtl\"] .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=\"rtl\"] .tox-color-input .tox-textfield {\n  padding-right: 36px;\n}\n.tox[dir=\"rtl\"] .tox-color-input span {\n  right: 6px;\n}\n.tox .tox-label,\n.tox .tox-toolbar-label {\n  color: rgba(34, 47, 62, 0.7);\n  display: block;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  padding: 0 8px 0 0;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-toolbar-label {\n  padding: 0 8px;\n}\n.tox[dir=rtl] .tox-label {\n  padding: 0 0 0 8px;\n}\n.tox .tox-form {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group {\n  box-sizing: border-box;\n  margin-bottom: 4px;\n}\n.tox .tox-form-group--maximize {\n  flex: 1;\n}\n.tox .tox-form__group--error {\n  color: #c00;\n}\n.tox .tox-form__group--collection {\n  display: flex;\n}\n.tox .tox-form__grid {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: space-between;\n}\n.tox .tox-form__grid--2col > .tox-form__group {\n  width: calc(50% - (8px / 2));\n}\n.tox .tox-form__grid--3col > .tox-form__group {\n  width: calc(100% / 3 - (8px / 2));\n}\n.tox .tox-form__grid--4col > .tox-form__group {\n  width: calc(25% - (8px / 2));\n}\n.tox .tox-form__controls-h-stack {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--inline {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--stretched {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group--stretched .tox-textarea {\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox:not([dir=rtl]) .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-lock.tox-locked .tox-lock-icon__unlock,\n.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock {\n  display: none;\n}\n.tox .tox-textfield,\n.tox .tox-toolbar-textfield,\n.tox .tox-listboxfield .tox-listbox--select,\n.tox .tox-textarea {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #fff;\n  border-color: #cccccc;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #222f3e;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 4.75px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-textfield[disabled],\n.tox .tox-textarea[disabled] {\n  background-color: #f2f2f2;\n  color: rgba(34, 47, 62, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-textfield:focus,\n.tox .tox-listboxfield .tox-listbox--select:focus,\n.tox .tox-textarea:focus {\n  background-color: #fff;\n  border-color: #207ab7;\n  box-shadow: none;\n  outline: 2px solid rgba(32, 122, 183, 0.25);\n}\n.tox .tox-toolbar-textfield {\n  border-width: 0;\n  margin-bottom: 3px;\n  margin-top: 2px;\n  max-width: 250px;\n}\n.tox .tox-naked-btn {\n  background-color: transparent;\n  border: 0;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #207ab7;\n  cursor: pointer;\n  display: block;\n  margin: 0;\n  padding: 0;\n}\n.tox .tox-naked-btn svg {\n  display: block;\n  fill: #222f3e;\n}\n.tox:not([dir=rtl]) .tox-toolbar-textfield + * {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-toolbar-textfield + * {\n  margin-right: 4px;\n}\n.tox .tox-listboxfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-listboxfield .tox-listbox--select[disabled] {\n  background-color: #f2f2f2;\n  color: rgba(34, 47, 62, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-listbox__select-label {\n  cursor: default;\n  flex: 1;\n  margin: 0 4px;\n}\n.tox .tox-listbox__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-listbox__select-chevron svg {\n  fill: #222f3e;\n}\n.tox .tox-listboxfield .tox-listbox--select {\n  align-items: center;\n  display: flex;\n}\n.tox:not([dir=rtl]) .tox-listboxfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-listboxfield svg {\n  left: 8px;\n}\n.tox .tox-selectfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-selectfield select {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #fff;\n  border-color: #cccccc;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #222f3e;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 4.75px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-selectfield select[disabled] {\n  background-color: #f2f2f2;\n  color: rgba(34, 47, 62, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-selectfield select::-ms-expand {\n  display: none;\n}\n.tox .tox-selectfield select:focus {\n  background-color: #fff;\n  border-color: #207ab7;\n  box-shadow: none;\n  outline: 2px solid rgba(32, 122, 183, 0.25);\n}\n.tox .tox-selectfield svg {\n  pointer-events: none;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"0\"],\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"1\"] {\n  padding-right: 24px;\n}\n.tox:not([dir=rtl]) .tox-selectfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-selectfield select[size=\"0\"],\n.tox[dir=rtl] .tox-selectfield select[size=\"1\"] {\n  padding-left: 24px;\n}\n.tox[dir=rtl] .tox-selectfield svg {\n  left: 8px;\n}\n.tox .tox-textarea {\n  -webkit-appearance: textarea;\n     -moz-appearance: textarea;\n          appearance: textarea;\n  white-space: pre-wrap;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n.tox .tox-help__more-link {\n  list-style: none;\n  margin-top: 1em;\n}\n.tox .tox-imagepreview {\n  background-color: #666;\n  height: 380px;\n  overflow: hidden;\n  position: relative;\n  width: 100%;\n}\n.tox .tox-imagepreview.tox-imagepreview__loaded {\n  overflow: auto;\n}\n.tox .tox-imagepreview__container {\n  display: flex;\n  left: 100vw;\n  position: absolute;\n  top: 100vw;\n}\n.tox .tox-imagepreview__image {\n  background: url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==);\n}\n.tox .tox-image-tools .tox-spacer {\n  flex: 1;\n}\n.tox .tox-image-tools .tox-bar {\n  align-items: center;\n  display: flex;\n  height: 60px;\n  justify-content: center;\n}\n.tox .tox-image-tools .tox-imagepreview,\n.tox .tox-image-tools .tox-imagepreview + .tox-bar {\n  margin-top: 8px;\n}\n.tox .tox-image-tools .tox-croprect-block {\n  background: black;\n  filter: alpha(opacity=50);\n  opacity: 0.5;\n  position: absolute;\n  zoom: 1;\n}\n.tox .tox-image-tools .tox-croprect-handle {\n  border: 2px solid white;\n  height: 20px;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 20px;\n}\n.tox .tox-image-tools .tox-croprect-handle-move {\n  border: 0;\n  cursor: move;\n  position: absolute;\n}\n.tox .tox-image-tools .tox-croprect-handle-nw {\n  border-width: 2px 0 0 2px;\n  cursor: nw-resize;\n  left: 100px;\n  margin: -2px 0 0 -2px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-ne {\n  border-width: 2px 2px 0 0;\n  cursor: ne-resize;\n  left: 200px;\n  margin: -2px 0 0 -20px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-sw {\n  border-width: 0 0 2px 2px;\n  cursor: sw-resize;\n  left: 100px;\n  margin: -20px 2px 0 -2px;\n  top: 200px;\n}\n.tox .tox-image-tools .tox-croprect-handle-se {\n  border-width: 0 2px 2px 0;\n  cursor: se-resize;\n  left: 200px;\n  margin: -20px 0 0 -20px;\n  top: 200px;\n}\n.tox .tox-insert-table-picker {\n  display: flex;\n  flex-wrap: wrap;\n  width: 170px;\n}\n.tox .tox-insert-table-picker > div {\n  border-color: #cccccc;\n  border-style: solid;\n  border-width: 0 1px 1px 0;\n  box-sizing: border-box;\n  height: 17px;\n  width: 17px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker {\n  margin: 0 -4px;\n}\n.tox .tox-insert-table-picker .tox-insert-table-picker__selected {\n  background-color: rgba(32, 122, 183, 0.5);\n  border-color: rgba(32, 122, 183, 0.5);\n}\n.tox .tox-insert-table-picker__label {\n  color: rgba(34, 47, 62, 0.7);\n  display: block;\n  font-size: 14px;\n  padding: 4px;\n  text-align: center;\n  width: 100%;\n}\n.tox:not([dir=rtl]) {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-insert-table-picker > div:nth-child(10n) {\n  border-right: 0;\n}\n.tox[dir=rtl] {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=rtl] .tox-insert-table-picker > div:nth-child(10n+1) {\n  border-right: 0;\n}\n.tox {\n  /* stylelint-disable */\n  /* stylelint-enable */\n}\n.tox .tox-menu {\n  background-color: #fff;\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n  box-shadow: 0 4px 8px 0 rgba(34, 47, 62, 0.1);\n  display: inline-block;\n  overflow: hidden;\n  vertical-align: top;\n  z-index: 1150;\n}\n.tox .tox-menu.tox-collection.tox-collection--list {\n  padding: 0 0;\n}\n.tox .tox-menu.tox-collection.tox-collection--toolbar {\n  padding: 4px;\n}\n.tox .tox-menu.tox-collection.tox-collection--grid {\n  padding: 4px;\n}\n@media only screen and (min-width: 768px ) {\n  .tox .tox-menu .tox-collection__item-label {\n    overflow-wrap: break-word;\n    word-break: normal;\n  }\n}\n.tox .tox-menu__label h1,\n.tox .tox-menu__label h2,\n.tox .tox-menu__label h3,\n.tox .tox-menu__label h4,\n.tox .tox-menu__label h5,\n.tox .tox-menu__label h6,\n.tox .tox-menu__label p,\n.tox .tox-menu__label blockquote,\n.tox .tox-menu__label code {\n  margin: 0;\n}\n.tox .tox-menubar {\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E\") left 0 top 0 #fff;\n  background-color: #fff;\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  grid-column: 1 / -1;\n  grid-row: 1;\n  padding: 0 4px 0 4px;\n}\n.tox .tox-promotion + .tox-menubar {\n  grid-column: 1;\n}\n.tox .tox-promotion {\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E\") left 0 top 0 #fff;\n  background-color: #fff;\n  grid-column: 2;\n  grid-row: 1;\n  padding-inline-end: 8px;\n  padding-inline-start: 4px;\n  padding-top: 5px;\n}\n.tox .tox-promotion-link {\n  align-items: unsafe center;\n  background-color: #E8F1F8;\n  border-radius: 5px;\n  color: #086BE6;\n  cursor: pointer;\n  display: flex;\n  font-size: 14px;\n  height: 26.6px;\n  padding: 4px 8px;\n  white-space: nowrap;\n}\n.tox .tox-promotion-link:hover {\n  background-color: #B4D7FF;\n}\n.tox .tox-promotion-link:focus {\n  background-color: #D9EDF7;\n}\n/* Deprecated. Remove in next major release */\n.tox .tox-mbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #222f3e;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 34px;\n  justify-content: center;\n  margin: 2px 0 3px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0 4px;\n  text-transform: none;\n  width: auto;\n}\n.tox .tox-mbtn[disabled] {\n  background-color: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-mbtn:focus:not(:disabled) {\n  background: #dee0e2;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-mbtn--active {\n  background: #c8cbcf;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active) {\n  background: #dee0e2;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-mbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-mbtn[disabled] .tox-mbtn__select-label {\n  cursor: not-allowed;\n}\n.tox .tox-mbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n  display: none;\n}\n.tox .tox-notification {\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: grid;\n  font-size: 14px;\n  font-weight: normal;\n  grid-template-columns: minmax(40px, 1fr) auto minmax(40px, 1fr);\n  margin-top: 4px;\n  opacity: 0;\n  padding: 4px;\n  transition: transform 100ms ease-in, opacity 150ms ease-in;\n}\n.tox .tox-notification p {\n  font-size: 14px;\n  font-weight: normal;\n}\n.tox .tox-notification a {\n  cursor: pointer;\n  text-decoration: underline;\n}\n.tox .tox-notification--in {\n  opacity: 1;\n}\n.tox .tox-notification--success {\n  background-color: #e4eeda;\n  border-color: #d7e6c8;\n  color: #222f3e;\n}\n.tox .tox-notification--success p {\n  color: #222f3e;\n}\n.tox .tox-notification--success a {\n  color: #517342;\n}\n.tox .tox-notification--success svg {\n  fill: #222f3e;\n}\n.tox .tox-notification--error {\n  background-color: #f5cccc;\n  border-color: #f0b3b3;\n  color: #222f3e;\n}\n.tox .tox-notification--error p {\n  color: #222f3e;\n}\n.tox .tox-notification--error a {\n  color: #77181f;\n}\n.tox .tox-notification--error svg {\n  fill: #222f3e;\n}\n.tox .tox-notification--warn,\n.tox .tox-notification--warning {\n  background-color: #fff5cc;\n  border-color: #fff0b3;\n  color: #222f3e;\n}\n.tox .tox-notification--warn p,\n.tox .tox-notification--warning p {\n  color: #222f3e;\n}\n.tox .tox-notification--warn a,\n.tox .tox-notification--warning a {\n  color: #7a6e25;\n}\n.tox .tox-notification--warn svg,\n.tox .tox-notification--warning svg {\n  fill: #222f3e;\n}\n.tox .tox-notification--info {\n  background-color: #d6e7fb;\n  border-color: #c1dbf9;\n  color: #222f3e;\n}\n.tox .tox-notification--info p {\n  color: #222f3e;\n}\n.tox .tox-notification--info a {\n  color: #2a64a6;\n}\n.tox .tox-notification--info svg {\n  fill: #222f3e;\n}\n.tox .tox-notification__body {\n  align-self: center;\n  color: #222f3e;\n  font-size: 14px;\n  grid-column-end: 3;\n  grid-column-start: 2;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  text-align: center;\n  white-space: normal;\n  word-break: break-all;\n  word-break: break-word;\n}\n.tox .tox-notification__body > * {\n  margin: 0;\n}\n.tox .tox-notification__body > * + * {\n  margin-top: 1rem;\n}\n.tox .tox-notification__icon {\n  align-self: center;\n  grid-column-end: 2;\n  grid-column-start: 1;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification__icon svg {\n  display: block;\n}\n.tox .tox-notification__dismiss {\n  align-self: start;\n  grid-column-end: 4;\n  grid-column-start: 3;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification .tox-progress-bar {\n  grid-column-end: 4;\n  grid-column-start: 1;\n  grid-row-end: 3;\n  grid-row-start: 2;\n  justify-self: center;\n}\n.tox .tox-pop {\n  display: inline-block;\n  position: relative;\n}\n.tox .tox-pop--resizing {\n  transition: width 0.1s ease;\n}\n.tox .tox-pop--resizing .tox-toolbar,\n.tox .tox-pop--resizing .tox-toolbar__group {\n  flex-wrap: nowrap;\n}\n.tox .tox-pop--transition {\n  transition: 0.15s ease;\n  transition-property: left, right, top, bottom;\n}\n.tox .tox-pop--transition::before,\n.tox .tox-pop--transition::after {\n  transition: all 0.15s, visibility 0s, opacity 0.075s ease 0.075s;\n}\n.tox .tox-pop__dialog {\n  background-color: #fff;\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  min-width: 0;\n  overflow: hidden;\n}\n.tox .tox-pop__dialog > *:not(.tox-toolbar) {\n  margin: 4px 4px 4px 8px;\n}\n.tox .tox-pop__dialog .tox-toolbar {\n  background-color: transparent;\n  margin-bottom: -1px;\n}\n.tox .tox-pop::before,\n.tox .tox-pop::after {\n  border-style: solid;\n  content: '';\n  display: block;\n  height: 0;\n  opacity: 1;\n  position: absolute;\n  width: 0;\n}\n.tox .tox-pop.tox-pop--inset::before,\n.tox .tox-pop.tox-pop--inset::after {\n  opacity: 0;\n  transition: all 0s 0.15s, visibility 0s, opacity 0.075s ease;\n}\n.tox .tox-pop.tox-pop--bottom::before,\n.tox .tox-pop.tox-pop--bottom::after {\n  left: 50%;\n  top: 100%;\n}\n.tox .tox-pop.tox-pop--bottom::after {\n  border-color: #fff transparent transparent transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: -1px;\n}\n.tox .tox-pop.tox-pop--bottom::before {\n  border-color: #cccccc transparent transparent transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--top::before,\n.tox .tox-pop.tox-pop--top::after {\n  left: 50%;\n  top: 0;\n  transform: translateY(-100%);\n}\n.tox .tox-pop.tox-pop--top::after {\n  border-color: transparent transparent #fff transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: 1px;\n}\n.tox .tox-pop.tox-pop--top::before {\n  border-color: transparent transparent #cccccc transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--left::before,\n.tox .tox-pop.tox-pop--left::after {\n  left: 0;\n  top: calc(50% - 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--left::after {\n  border-color: transparent #fff transparent transparent;\n  border-width: 8px;\n  margin-left: -15px;\n}\n.tox .tox-pop.tox-pop--left::before {\n  border-color: transparent #cccccc transparent transparent;\n  border-width: 10px;\n  margin-left: -19px;\n}\n.tox .tox-pop.tox-pop--right::before,\n.tox .tox-pop.tox-pop--right::after {\n  left: 100%;\n  top: calc(50% + 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--right::after {\n  border-color: transparent transparent transparent #fff;\n  border-width: 8px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--right::before {\n  border-color: transparent transparent transparent #cccccc;\n  border-width: 10px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--align-left::before,\n.tox .tox-pop.tox-pop--align-left::after {\n  left: 20px;\n}\n.tox .tox-pop.tox-pop--align-right::before,\n.tox .tox-pop.tox-pop--align-right::after {\n  left: calc(100% - 20px);\n}\n.tox .tox-sidebar-wrap {\n  display: flex;\n  flex-direction: row;\n  flex-grow: 1;\n  min-height: 0;\n}\n.tox .tox-sidebar {\n  background-color: #fff;\n  display: flex;\n  flex-direction: row;\n  justify-content: flex-end;\n}\n.tox .tox-sidebar__slider {\n  display: flex;\n  overflow: hidden;\n}\n.tox .tox-sidebar__pane-container {\n  display: flex;\n}\n.tox .tox-sidebar__pane {\n  display: flex;\n}\n.tox .tox-sidebar--sliding-closed {\n  opacity: 0;\n}\n.tox .tox-sidebar--sliding-open {\n  opacity: 1;\n}\n.tox .tox-sidebar--sliding-growing,\n.tox .tox-sidebar--sliding-shrinking {\n  transition: width 0.5s ease, opacity 0.5s ease;\n}\n.tox .tox-selector {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  display: inline-block;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox.tox-platform-touch .tox-selector {\n  height: 12px;\n  width: 12px;\n}\n.tox .tox-slider {\n  align-items: center;\n  display: flex;\n  flex: 1;\n  height: 24px;\n  justify-content: center;\n  position: relative;\n}\n.tox .tox-slider__rail {\n  background-color: transparent;\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n  height: 10px;\n  min-width: 120px;\n  width: 100%;\n}\n.tox .tox-slider__handle {\n  background-color: #207ab7;\n  border: 2px solid #185d8c;\n  border-radius: 3px;\n  box-shadow: none;\n  height: 24px;\n  left: 50%;\n  position: absolute;\n  top: 50%;\n  transform: translateX(-50%) translateY(-50%);\n  width: 14px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider:not(:first-of-type) {\n  margin-inline-start: 8px;\n}\n.tox .tox-form__controls-h-stack > .tox-form__group + .tox-slider {\n  margin-inline-start: 32px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider + .tox-form__group {\n  margin-inline-start: 32px;\n}\n.tox .tox-source-code {\n  overflow: auto;\n}\n.tox .tox-spinner {\n  display: flex;\n}\n.tox .tox-spinner > div {\n  animation: tam-bouncing-dots 1.5s ease-in-out 0s infinite both;\n  background-color: rgba(34, 47, 62, 0.7);\n  border-radius: 100%;\n  height: 8px;\n  width: 8px;\n}\n.tox .tox-spinner > div:nth-child(1) {\n  animation-delay: -0.32s;\n}\n.tox .tox-spinner > div:nth-child(2) {\n  animation-delay: -0.16s;\n}\n@keyframes tam-bouncing-dots {\n  0%,\n  80%,\n  100% {\n    transform: scale(0);\n  }\n  40% {\n    transform: scale(1);\n  }\n}\n.tox:not([dir=rtl]) .tox-spinner > div:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-spinner > div:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-statusbar {\n  align-items: center;\n  background-color: #fff;\n  border-top: 1px solid #cccccc;\n  color: rgba(34, 47, 62, 0.7);\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 12px;\n  font-weight: normal;\n  height: 18px;\n  overflow: hidden;\n  padding: 0 8px;\n  position: relative;\n  text-transform: uppercase;\n}\n.tox .tox-statusbar__text-container {\n  display: flex;\n  flex: 1 1 auto;\n  justify-content: flex-end;\n  overflow: hidden;\n}\n.tox .tox-statusbar__path {\n  display: flex;\n  flex: 1 1 auto;\n  margin-right: auto;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__path > * {\n  display: inline;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__wordcount {\n  flex: 0 0 auto;\n  margin-left: 1ch;\n}\n.tox .tox-statusbar a,\n.tox .tox-statusbar__path-item,\n.tox .tox-statusbar__wordcount {\n  color: rgba(34, 47, 62, 0.7);\n  text-decoration: none;\n}\n.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) {\n  color: #222f3e;\n  cursor: pointer;\n}\n.tox .tox-statusbar__branding svg {\n  fill: rgba(34, 47, 62, 0.8);\n  height: 1.14em;\n  vertical-align: -0.28em;\n  width: 3.6em;\n}\n.tox .tox-statusbar__branding a:hover:not(:disabled):not([aria-disabled=true]) svg,\n.tox .tox-statusbar__branding a:focus:not(:disabled):not([aria-disabled=true]) svg {\n  fill: #222f3e;\n}\n.tox .tox-statusbar__resize-handle {\n  align-items: flex-end;\n  align-self: stretch;\n  cursor: nwse-resize;\n  display: flex;\n  flex: 0 0 auto;\n  justify-content: flex-end;\n  margin-left: auto;\n  margin-right: -8px;\n  padding-bottom: 3px;\n  padding-left: 1ch;\n  padding-right: 3px;\n}\n.tox .tox-statusbar__resize-handle svg {\n  display: block;\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-statusbar__resize-handle:focus svg {\n  background-color: #dee0e2;\n  border-radius: 1px 1px -4px 1px;\n  box-shadow: 0 0 0 2px #dee0e2;\n}\n.tox:not([dir=rtl]) .tox-statusbar__path > * {\n  margin-right: 4px;\n}\n.tox:not([dir=rtl]) .tox-statusbar__branding {\n  margin-left: 2ch;\n}\n.tox[dir=rtl] .tox-statusbar {\n  flex-direction: row-reverse;\n}\n.tox[dir=rtl] .tox-statusbar__path > * {\n  margin-left: 4px;\n}\n.tox .tox-throbber {\n  z-index: 1299;\n}\n.tox .tox-throbber__busy-spinner {\n  align-items: center;\n  background-color: rgba(255, 255, 255, 0.6);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n.tox .tox-tbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #222f3e;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 34px;\n  justify-content: center;\n  margin: 3px 0 2px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0;\n  text-transform: none;\n  width: 34px;\n}\n.tox .tox-tbtn svg {\n  display: block;\n  fill: #222f3e;\n}\n.tox .tox-tbtn.tox-tbtn-more {\n  padding-left: 5px;\n  padding-right: 5px;\n  width: inherit;\n}\n.tox .tox-tbtn:focus {\n  background: #dee0e2;\n  border: 0;\n  box-shadow: none;\n}\n.tox .tox-tbtn:hover {\n  background: #dee0e2;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-tbtn:hover svg {\n  fill: #222f3e;\n}\n.tox .tox-tbtn:active {\n  background: #c8cbcf;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-tbtn:active svg {\n  fill: #222f3e;\n}\n.tox .tox-tbtn--disabled,\n.tox .tox-tbtn--disabled:hover,\n.tox .tox-tbtn:disabled,\n.tox .tox-tbtn:disabled:hover {\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-tbtn--disabled svg,\n.tox .tox-tbtn--disabled:hover svg,\n.tox .tox-tbtn:disabled svg,\n.tox .tox-tbtn:disabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-tbtn--enabled,\n.tox .tox-tbtn--enabled:hover {\n  background: #c8cbcf;\n  border: 0;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-tbtn--enabled > *,\n.tox .tox-tbtn--enabled:hover > * {\n  transform: none;\n}\n.tox .tox-tbtn--enabled svg,\n.tox .tox-tbtn--enabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: #222f3e;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) {\n  color: #222f3e;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg {\n  fill: #222f3e;\n}\n.tox .tox-tbtn:active > * {\n  transform: none;\n}\n.tox .tox-tbtn--md {\n  height: 51px;\n  width: 51px;\n}\n.tox .tox-tbtn--lg {\n  flex-direction: column;\n  height: 68px;\n  width: 68px;\n}\n.tox .tox-tbtn--return {\n  align-self: stretch;\n  height: unset;\n  width: 16px;\n}\n.tox .tox-tbtn--labeled {\n  padding: 0 4px;\n  width: unset;\n}\n.tox .tox-tbtn__vlabel {\n  display: block;\n  font-size: 10px;\n  font-weight: normal;\n  letter-spacing: -0.025em;\n  margin-bottom: 4px;\n  white-space: nowrap;\n}\n.tox .tox-tbtn--select {\n  margin: 3px 0 2px 0;\n  padding: 0 4px;\n  width: auto;\n}\n.tox .tox-tbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-tbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-tbtn__select-chevron svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-tbtn--bespoke {\n  background: transparent;\n}\n.tox .tox-tbtn--bespoke + .tox-tbtn--bespoke {\n  margin-inline-start: 0;\n}\n.tox .tox-tbtn--bespoke .tox-tbtn__select-label {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  width: 7em;\n}\n.tox .tox-split-button {\n  border: 0;\n  border-radius: 3px;\n  box-sizing: border-box;\n  display: flex;\n  margin: 3px 0 2px 0;\n  overflow: hidden;\n}\n.tox .tox-split-button:hover {\n  box-shadow: 0 0 0 1px #dee0e2 inset;\n}\n.tox .tox-split-button:focus {\n  background: #dee0e2;\n  box-shadow: none;\n  color: #222f3e;\n}\n.tox .tox-split-button > * {\n  border-radius: 0;\n}\n.tox .tox-split-button__chevron {\n  width: 16px;\n}\n.tox .tox-split-button__chevron svg {\n  fill: rgba(34, 47, 62, 0.5);\n}\n.tox .tox-split-button .tox-tbtn {\n  margin: 0;\n}\n.tox .tox-split-button.tox-tbtn--disabled:hover,\n.tox .tox-split-button.tox-tbtn--disabled:focus,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus {\n  background: transparent;\n  box-shadow: none;\n  color: rgba(34, 47, 62, 0.5);\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn--select {\n  padding: 0 0px;\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn:not(.tox-tbtn--select):first-child {\n  width: 30px;\n}\n.tox.tox-platform-touch .tox-split-button__chevron {\n  width: 20px;\n}\n.tox .tox-toolbar-overlord {\n  background-color: #fff;\n}\n.tox .tox-toolbar,\n.tox .tox-toolbar__primary,\n.tox .tox-toolbar__overflow {\n  background-attachment: local;\n  background-color: #fff;\n  background-image: repeating-linear-gradient(#cccccc 0px 1px, transparent 1px 39px);\n  background-position: center top 39px;\n  background-repeat: no-repeat;\n  background-size: calc(100% - 4px * 2) calc(100% - 39px);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  padding: 0 0px;\n  transform: perspective(1px);\n}\n.tox .tox-toolbar-overlord > .tox-toolbar,\n.tox .tox-toolbar-overlord > .tox-toolbar__primary,\n.tox .tox-toolbar-overlord > .tox-toolbar__overflow {\n  background-position: center top 0px;\n  background-size: calc(100% - 4px * 2) calc(100% - 0px);\n}\n.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed {\n  height: 0;\n  opacity: 0;\n  padding-bottom: 0;\n  padding-top: 0;\n  visibility: hidden;\n}\n.tox .tox-toolbar__overflow--growing {\n  transition: height 0.3s ease, opacity 0.2s linear 0.1s;\n}\n.tox .tox-toolbar__overflow--shrinking {\n  transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;\n}\n.tox .tox-toolbar-overlord,\n.tox .tox-anchorbar {\n  grid-column: 1 / -1;\n}\n.tox .tox-menubar + .tox-toolbar,\n.tox .tox-menubar + .tox-toolbar-overlord {\n  border-top: 1px solid #cccccc;\n  margin-top: -1px;\n  padding-bottom: 0px;\n  padding-top: 0px;\n}\n.tox .tox-toolbar--scrolling {\n  flex-wrap: nowrap;\n  overflow-x: auto;\n}\n.tox .tox-pop .tox-toolbar {\n  border-width: 0;\n}\n.tox .tox-toolbar--no-divider {\n  background-image: none;\n}\n.tox .tox-toolbar-overlord .tox-toolbar:not(.tox-toolbar--scrolling):first-child,\n.tox .tox-toolbar-overlord .tox-toolbar__primary {\n  background-position: center top 39px;\n}\n.tox .tox-editor-header > .tox-toolbar--scrolling,\n.tox .tox-toolbar-overlord .tox-toolbar--scrolling:first-child {\n  background-image: none;\n}\n.tox.tox-tinymce-aux .tox-toolbar__overflow {\n  background-color: #fff;\n  background-position: center top 43px;\n  background-size: calc(100% - 8px * 2) calc(100% - 51px);\n  border: none;\n  border-radius: 3px;\n  box-shadow: 0 0 2px 0 rgba(34, 47, 62, 0.2), 0 4px 8px 0 rgba(34, 47, 62, 0.15);\n  overscroll-behavior: none;\n  padding: 4px 0;\n}\n.tox-pop .tox-pop__dialog {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox-pop .tox-pop__dialog .tox-toolbar {\n  background-position: center top 43px;\n  background-size: calc(100% - 4px * 2) calc(100% - 51px);\n  padding: 4px 0;\n}\n.tox .tox-toolbar__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: wrap;\n  margin: 0 0;\n  padding: 0 4px 0 4px;\n}\n.tox .tox-toolbar__group--pull-right {\n  margin-left: auto;\n}\n.tox .tox-toolbar--scrolling .tox-toolbar__group {\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n}\n.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type) {\n  border-right: 1px solid #cccccc;\n}\n.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type) {\n  border-left: 1px solid #cccccc;\n}\n.tox .tox-tooltip {\n  display: inline-block;\n  padding: 8px;\n  position: relative;\n}\n.tox .tox-tooltip__body {\n  background-color: #222f3e;\n  border-radius: 3px;\n  box-shadow: 0 2px 4px rgba(34, 47, 62, 0.3);\n  color: rgba(255, 255, 255, 0.75);\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  padding: 4px 8px;\n  text-transform: none;\n}\n.tox .tox-tooltip__arrow {\n  position: absolute;\n}\n.tox .tox-tooltip--down .tox-tooltip__arrow {\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  border-top: 8px solid #222f3e;\n  bottom: 0;\n  left: 50%;\n  position: absolute;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--up .tox-tooltip__arrow {\n  border-bottom: 8px solid #222f3e;\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  left: 50%;\n  position: absolute;\n  top: 0;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--right .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-left: 8px solid #222f3e;\n  border-top: 8px solid transparent;\n  position: absolute;\n  right: 0;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-tooltip--left .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-right: 8px solid #222f3e;\n  border-top: 8px solid transparent;\n  left: 0;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-view-wrap,\n.tox .tox-view-wrap__slot-container {\n  background-color: #fff;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view__header {\n  align-items: center;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 8px 0 8px;\n  position: relative;\n}\n.tox .tox-view__header-start,\n.tox .tox-view__header-end {\n  display: flex;\n}\n.tox .tox-view__pane {\n  height: 100%;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-view__pane_panel {\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n}\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-start > *,\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-view__header .tox-view__header-start > *,\n.tox[dir=rtl] .tox-view__header .tox-view__header-end > * {\n  margin-right: 8px;\n}\n.tox .tox-well {\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-well > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-well > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-well > *:only-child {\n  margin: 0;\n}\n.tox .tox-custom-editor {\n  border: 1px solid #cccccc;\n  border-radius: 3px;\n  display: flex;\n  flex: 1;\n  position: relative;\n}\n/* stylelint-disable */\n.tox {\n  /* stylelint-enable */\n}\n.tox .tox-dialog-loading::before {\n  background-color: rgba(0, 0, 0, 0.5);\n  content: \"\";\n  height: 100%;\n  position: absolute;\n  width: 100%;\n  z-index: 1000;\n}\n.tox .tox-tab {\n  cursor: pointer;\n}\n.tox .tox-dialog__content-js {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-content .tox-collection {\n  display: flex;\n  flex: 1;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header {\n  background-color: none;\n  padding: 0;\n}\n.tox.tox-tinymce--toolbar-bottom .tox-editor-header,\n.tox.tox-tinymce-inline .tox-editor-header {\n  margin-bottom: -1px;\n}\n.tox.tox-tinymce-inline .tox-editor-container {\n  overflow: hidden;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-bottom .tox-editor-header {\n  border-top: none;\n  box-shadow: none;\n}\n.tox.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header {\n  background-color: transparent;\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n  padding: 0;\n}\n.tox.tox.tox-tinymce--toolbar-sticky-on.tox-tinymce--toolbar-bottom .tox-editor-header {\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n}\n.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker {\n  margin: -4px 0;\n}\n.tox .tox-menu.tox-collection.tox-collection--list {\n  padding: 0;\n}\n.tox .tox-pop {\n  box-shadow: none;\n}\n.tox .tox-tbtn,\n.tox .tox-tbtn--select,\n.tox .tox-split-button {\n  margin: 2px 0 3px 0;\n}\n.tox .tox-toolbar,\n.tox .tox-toolbar__primary,\n.tox .tox-toolbar__overflow {\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E\") left 0 top 0px #fff !important;\n}\n.tox .tox-menubar + .tox-toolbar-overlord {\n  border-top: none;\n}\n.tox .tox-menubar + .tox-toolbar,\n.tox .tox-menubar + .tox-toolbar-overlord .tox-toolbar__primary {\n  border-top: 1px solid #cccccc;\n  margin-top: -1px;\n}\n.tox.tox-tinymce-aux .tox-toolbar__overflow {\n  border: 1px solid #cccccc;\n  padding: 0;\n}\n.tox .tox-pop .tox-pop__dialog .tox-toolbar {\n  padding: 0;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {\n  border-top: 1px solid #cccccc;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,\n.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child {\n  border-top: 1px solid #cccccc;\n}\n.tox .tox-toolbar__group {\n  padding: 0 4px 0 4px;\n}\n.tox .tox-collection__item {\n  border-radius: 0;\n  cursor: pointer;\n}\n.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) {\n  color: rgba(34, 47, 62, 0.7);\n  text-decoration: underline;\n}\n.tox .tox-statusbar__branding svg {\n  vertical-align: -0.25em;\n}\n.tox:not([dir=rtl]) .tox-statusbar__branding {\n  margin-left: 1ch;\n}\n.tox .tox-statusbar__resize-handle {\n  padding-bottom: 0;\n  padding-right: 0;\n}\n.tox .tox-button::before {\n  display: none;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5/skin.shadowdom.css",
    "content": "body.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%236d737b%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * Dracula Theme originally by Zeno Rocha [@zenorocha]\n * https://draculatheme.com/\n *\n * Ported for PrismJS by Albert Vallverdu [@byverdu]\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: #f8f8f2;\n  background: none;\n  text-shadow: 0 1px rgba(0, 0, 0, 0.3);\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n  border-radius: 0.3em;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #282a36;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: #6272a4;\n}\n.token.punctuation {\n  color: #f8f8f2;\n}\n.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #ff79c6;\n}\n.token.boolean,\n.token.number {\n  color: #bd93f9;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #50fa7b;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string,\n.token.variable {\n  color: #f8f8f2;\n}\n.token.atrule,\n.token.attr-value,\n.token.function,\n.token.class-name {\n  color: #f1fa8c;\n}\n.token.keyword {\n  color: #8be9fd;\n}\n.token.regex,\n.token.important {\n  color: #ffb86c;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.3);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.3);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #4099ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #4099ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #4099ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #4099ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid transparent;\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: lighten;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #4099ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\nbody {\n  font-family: sans-serif;\n}\ntable {\n  border-collapse: collapse;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5-dark/content.inline.css",
    "content": ".mce-content-body .mce-item-anchor {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n}\n.mce-content-body .mce-item-anchor:empty {\n  cursor: default;\n  display: inline-block;\n  height: 12px !important;\n  padding: 0 2px;\n  -webkit-user-modify: read-only;\n  -moz-user-modify: read-only;\n  -webkit-user-select: all;\n  -moz-user-select: all;\n  user-select: all;\n  width: 8px !important;\n}\n.mce-content-body .mce-item-anchor:not(:empty) {\n  background-position-x: 2px;\n  display: inline-block;\n  padding-left: 12px;\n}\n.mce-content-body .mce-item-anchor[data-mce-selected] {\n  outline-offset: 1px;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"]:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #ffe89d;\n}\n.tox-comments-visible .tox-comment[contenteditable=\"false\"][data-mce-annotation-active=\"true\"]:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] img:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > audio:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] > video:not([data-mce-selected]),\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"] span.mce-preview-object:not([data-mce-selected]) {\n  outline: 3px solid #fed635;\n}\n.tox-comments-visible span.tox-comment:not([data-mce-selected]) {\n  background-color: #ffe89d;\n  outline: none;\n}\n.tox-comments-visible span.tox-comment[data-mce-annotation-active=\"true\"]:not([data-mce-selected=\"inline-boundary\"]) {\n  background-color: #fed635;\n}\n.tox-checklist > li:not(.tox-checklist--hidden) {\n  list-style: none;\n  margin: 0.25em 0;\n}\n.tox-checklist > li:not(.tox-checklist--hidden)::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n  cursor: pointer;\n  height: 1em;\n  margin-left: -1.5em;\n  margin-top: 0.125em;\n  position: absolute;\n  width: 1em;\n}\n.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {\n  content: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A\");\n}\n[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {\n  margin-left: 0;\n  margin-right: -1.5em;\n}\n/* stylelint-disable */\n/* http://prismjs.com/ */\n/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\ncode[class*=\"language-\"],\npre[class*=\"language-\"] {\n  color: black;\n  background: none;\n  text-shadow: 0 1px white;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"]::-moz-selection,\npre[class*=\"language-\"] ::-moz-selection,\ncode[class*=\"language-\"]::-moz-selection,\ncode[class*=\"language-\"] ::-moz-selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\npre[class*=\"language-\"]::selection,\npre[class*=\"language-\"] ::selection,\ncode[class*=\"language-\"]::selection,\ncode[class*=\"language-\"] ::selection {\n  text-shadow: none;\n  background: #b3d4fc;\n}\n@media print {\n  code[class*=\"language-\"],\n  pre[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\n/* Code blocks */\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n}\n:not(pre) > code[class*=\"language-\"],\npre[class*=\"language-\"] {\n  background: #f5f2f0;\n}\n/* Inline code */\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: slategray;\n}\n.token.punctuation {\n  color: #999;\n}\n.token.namespace {\n  opacity: 0.7;\n}\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #905;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #690;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #9a6e3a;\n  /* This background color was intended by the author of this theme. */\n  background: hsla(0, 0%, 100%, 0.5);\n}\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #07a;\n}\n.token.function,\n.token.class-name {\n  color: #DD4A68;\n}\n.token.regex,\n.token.important,\n.token.variable {\n  color: #e90;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/* stylelint-enable */\n.mce-content-body {\n  overflow-wrap: break-word;\n  word-wrap: break-word;\n}\n.mce-content-body .mce-visual-caret {\n  background-color: black;\n  background-color: currentColor;\n  position: absolute;\n}\n.mce-content-body .mce-visual-caret-hidden {\n  display: none;\n}\n.mce-content-body *[data-mce-caret] {\n  left: -1000px;\n  margin: 0;\n  padding: 0;\n  position: absolute;\n  right: auto;\n  top: 0;\n}\n.mce-content-body .mce-offscreen-selection {\n  left: -2000000px;\n  max-width: 1000000px;\n  position: absolute;\n}\n.mce-content-body *[contentEditable=false] {\n  cursor: default;\n}\n.mce-content-body *[contentEditable=true] {\n  cursor: text;\n}\n.tox-cursor-format-painter {\n  cursor: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\"), default;\n}\ndiv.mce-footnotes hr {\n  margin-inline-end: auto;\n  margin-inline-start: 0;\n  width: 25%;\n}\ndiv.mce-footnotes li > a.mce-footnotes-backlink {\n  text-decoration: none;\n}\n@media print {\n  sup.mce-footnote a {\n    color: black;\n    text-decoration: none;\n  }\n  div.mce-footnotes {\n    break-inside: avoid;\n    width: 100%;\n  }\n  div.mce-footnotes li > a.mce-footnotes-backlink {\n    display: none;\n  }\n}\n.mce-content-body figure.align-left {\n  float: left;\n}\n.mce-content-body figure.align-right {\n  float: right;\n}\n.mce-content-body figure.image.align-center {\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\n.mce-preview-object {\n  border: 1px solid gray;\n  display: inline-block;\n  line-height: 0;\n  margin: 0 2px 0 2px;\n  position: relative;\n}\n.mce-preview-object .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-preview-object[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.mce-content-body .mce-mergetag:hover {\n  background-color: rgba(0, 108, 231, 0.1);\n}\n.mce-content-body .mce-mergetag-affix {\n  background-color: rgba(0, 108, 231, 0.1);\n  color: #006ce7;\n}\n.mce-object {\n  background: transparent url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A\") no-repeat center;\n  border: 1px dashed #aaa;\n}\n.mce-pagebreak {\n  border: 1px dashed #aaa;\n  cursor: default;\n  display: block;\n  height: 5px;\n  margin-top: 15px;\n  page-break-before: always;\n  width: 100%;\n}\n@media print {\n  .mce-pagebreak {\n    border: 0;\n  }\n}\n.tiny-pageembed .mce-shim {\n  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tiny-pageembed[data-mce-selected=\"2\"] .mce-shim {\n  display: none;\n}\n.tiny-pageembed {\n  display: inline-block;\n  position: relative;\n}\n.tiny-pageembed--21by9,\n.tiny-pageembed--16by9,\n.tiny-pageembed--4by3,\n.tiny-pageembed--1by1 {\n  display: block;\n  overflow: hidden;\n  padding: 0;\n  position: relative;\n  width: 100%;\n}\n.tiny-pageembed--21by9 {\n  padding-top: 42.857143%;\n}\n.tiny-pageembed--16by9 {\n  padding-top: 56.25%;\n}\n.tiny-pageembed--4by3 {\n  padding-top: 75%;\n}\n.tiny-pageembed--1by1 {\n  padding-top: 100%;\n}\n.tiny-pageembed--21by9 iframe,\n.tiny-pageembed--16by9 iframe,\n.tiny-pageembed--4by3 iframe,\n.tiny-pageembed--1by1 iframe {\n  border: 0;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.mce-content-body[data-mce-placeholder] {\n  position: relative;\n}\n.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  color: rgba(34, 47, 62, 0.7);\n  content: attr(data-mce-placeholder);\n  position: absolute;\n}\n.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {\n  left: 1px;\n}\n.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {\n  right: 1px;\n}\n.mce-content-body div.mce-resizehandle {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n  z-index: 1298;\n}\n.mce-content-body div.mce-resizehandle:hover {\n  background-color: #4099ff;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(1) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(2) {\n  cursor: nesw-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(3) {\n  cursor: nwse-resize;\n}\n.mce-content-body div.mce-resizehandle:nth-of-type(4) {\n  cursor: nesw-resize;\n}\n.mce-content-body .mce-resize-backdrop {\n  z-index: 10000;\n}\n.mce-content-body .mce-clonedresizable {\n  cursor: default;\n  opacity: 0.5;\n  outline: 1px dashed black;\n  position: absolute;\n  z-index: 10001;\n}\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,\n.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {\n  border: 0;\n}\n.mce-content-body .mce-resize-helper {\n  background: #555;\n  background: rgba(0, 0, 0, 0.75);\n  border: 1px;\n  border-radius: 3px;\n  color: white;\n  display: none;\n  font-family: sans-serif;\n  font-size: 12px;\n  line-height: 14px;\n  margin: 5px 10px;\n  padding: 5px;\n  position: absolute;\n  white-space: nowrap;\n  z-index: 10002;\n}\n.tox-rtc-user-selection {\n  position: relative;\n}\n.tox-rtc-user-cursor {\n  bottom: 0;\n  cursor: default;\n  position: absolute;\n  top: 0;\n  width: 2px;\n}\n.tox-rtc-user-cursor::before {\n  background-color: inherit;\n  border-radius: 50%;\n  content: '';\n  display: block;\n  height: 8px;\n  position: absolute;\n  right: -3px;\n  top: -3px;\n  width: 8px;\n}\n.tox-rtc-user-cursor:hover::after {\n  background-color: inherit;\n  border-radius: 100px;\n  box-sizing: border-box;\n  color: #fff;\n  content: attr(data-user);\n  display: block;\n  font-size: 12px;\n  font-weight: bold;\n  left: -5px;\n  min-height: 8px;\n  min-width: 8px;\n  padding: 0 12px;\n  position: absolute;\n  top: -11px;\n  white-space: nowrap;\n  z-index: 1000;\n}\n.tox-rtc-user-selection--1 .tox-rtc-user-cursor {\n  background-color: #2dc26b;\n}\n.tox-rtc-user-selection--2 .tox-rtc-user-cursor {\n  background-color: #e03e2d;\n}\n.tox-rtc-user-selection--3 .tox-rtc-user-cursor {\n  background-color: #f1c40f;\n}\n.tox-rtc-user-selection--4 .tox-rtc-user-cursor {\n  background-color: #3598db;\n}\n.tox-rtc-user-selection--5 .tox-rtc-user-cursor {\n  background-color: #b96ad9;\n}\n.tox-rtc-user-selection--6 .tox-rtc-user-cursor {\n  background-color: #e67e23;\n}\n.tox-rtc-user-selection--7 .tox-rtc-user-cursor {\n  background-color: #aaa69d;\n}\n.tox-rtc-user-selection--8 .tox-rtc-user-cursor {\n  background-color: #f368e0;\n}\n.tox-rtc-remote-image {\n  background: #eaeaea url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\") no-repeat center center;\n  border: 1px solid #ccc;\n  min-height: 240px;\n  min-width: 320px;\n}\n.mce-match-marker {\n  background: #aaa;\n  color: #fff;\n}\n.mce-match-marker-selected {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::-moz-selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-match-marker-selected::selection {\n  background: #39f;\n  color: #fff;\n}\n.mce-content-body img[data-mce-selected],\n.mce-content-body video[data-mce-selected],\n.mce-content-body audio[data-mce-selected],\n.mce-content-body object[data-mce-selected],\n.mce-content-body embed[data-mce-selected],\n.mce-content-body table[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body hr[data-mce-selected] {\n  outline: 3px solid #b4d7ff;\n  outline-offset: 1px;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body *[contentEditable=false][data-mce-selected] {\n  cursor: not-allowed;\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,\n.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {\n  outline: none;\n}\n.mce-content-body *[data-mce-selected=\"inline-boundary\"] {\n  background-color: #b4d7ff;\n}\n.mce-content-body .mce-edit-focus {\n  outline: 3px solid #b4d7ff;\n}\n.mce-content-body td[data-mce-selected],\n.mce-content-body th[data-mce-selected] {\n  position: relative;\n}\n.mce-content-body td[data-mce-selected]::-moz-selection,\n.mce-content-body th[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected]::selection,\n.mce-content-body th[data-mce-selected]::selection {\n  background: none;\n}\n.mce-content-body td[data-mce-selected] *,\n.mce-content-body th[data-mce-selected] * {\n  outline: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.mce-content-body td[data-mce-selected]::after,\n.mce-content-body th[data-mce-selected]::after {\n  background-color: rgba(180, 215, 255, 0.7);\n  border: 1px solid rgba(180, 215, 255, 0.7);\n  bottom: -1px;\n  content: '';\n  left: -1px;\n  mix-blend-mode: multiply;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {\n  .mce-content-body td[data-mce-selected]::after,\n  .mce-content-body th[data-mce-selected]::after {\n    border-color: rgba(0, 84, 180, 0.7);\n  }\n}\n.mce-content-body img[data-mce-selected]::-moz-selection {\n  background: none;\n}\n.mce-content-body img[data-mce-selected]::selection {\n  background: none;\n}\n.ephox-snooker-resizer-bar {\n  background-color: #b4d7ff;\n  opacity: 0;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n.ephox-snooker-resizer-cols {\n  cursor: col-resize;\n}\n.ephox-snooker-resizer-rows {\n  cursor: row-resize;\n}\n.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {\n  opacity: 1;\n}\n.mce-spellchecker-word {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n  height: 2rem;\n}\n.mce-spellchecker-grammar {\n  background-image: url(\"data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A\");\n  background-position: 0 calc(100% + 1px);\n  background-repeat: repeat-x;\n  background-size: auto 6px;\n  cursor: default;\n}\n.mce-toc {\n  border: 1px solid gray;\n}\n.mce-toc h2 {\n  margin: 4px;\n}\n.mce-toc li {\n  list-style-type: none;\n}\n[data-mce-block] {\n  display: block;\n}\ntable[style*=\"border-width: 0px\"],\n.mce-item-table:not([border]),\n.mce-item-table[border=\"0\"],\ntable[style*=\"border-width: 0px\"] td,\n.mce-item-table:not([border]) td,\n.mce-item-table[border=\"0\"] td,\ntable[style*=\"border-width: 0px\"] th,\n.mce-item-table:not([border]) th,\n.mce-item-table[border=\"0\"] th,\ntable[style*=\"border-width: 0px\"] caption,\n.mce-item-table:not([border]) caption,\n.mce-item-table[border=\"0\"] caption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks p,\n.mce-visualblocks h1,\n.mce-visualblocks h2,\n.mce-visualblocks h3,\n.mce-visualblocks h4,\n.mce-visualblocks h5,\n.mce-visualblocks h6,\n.mce-visualblocks div:not([data-mce-bogus]),\n.mce-visualblocks section,\n.mce-visualblocks article,\n.mce-visualblocks blockquote,\n.mce-visualblocks address,\n.mce-visualblocks pre,\n.mce-visualblocks figure,\n.mce-visualblocks figcaption,\n.mce-visualblocks hgroup,\n.mce-visualblocks aside,\n.mce-visualblocks ul,\n.mce-visualblocks ol,\n.mce-visualblocks dl {\n  background-repeat: no-repeat;\n  border: 1px dashed #bbb;\n  margin-left: 3px;\n  padding-top: 10px;\n}\n.mce-visualblocks p {\n  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);\n}\n.mce-visualblocks h1 {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);\n}\n.mce-visualblocks h2 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);\n}\n.mce-visualblocks h3 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);\n}\n.mce-visualblocks h4 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);\n}\n.mce-visualblocks h5 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);\n}\n.mce-visualblocks h6 {\n  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);\n}\n.mce-visualblocks div:not([data-mce-bogus]) {\n  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);\n}\n.mce-visualblocks section {\n  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);\n}\n.mce-visualblocks article {\n  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);\n}\n.mce-visualblocks blockquote {\n  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);\n}\n.mce-visualblocks address {\n  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);\n}\n.mce-visualblocks pre {\n  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);\n}\n.mce-visualblocks figure {\n  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);\n}\n.mce-visualblocks figcaption {\n  border: 1px dashed #bbb;\n}\n.mce-visualblocks hgroup {\n  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);\n}\n.mce-visualblocks aside {\n  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);\n}\n.mce-visualblocks ul {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);\n}\n.mce-visualblocks ol {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);\n}\n.mce-visualblocks dl {\n  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);\n}\n.mce-visualblocks:not([dir=rtl]) p,\n.mce-visualblocks:not([dir=rtl]) h1,\n.mce-visualblocks:not([dir=rtl]) h2,\n.mce-visualblocks:not([dir=rtl]) h3,\n.mce-visualblocks:not([dir=rtl]) h4,\n.mce-visualblocks:not([dir=rtl]) h5,\n.mce-visualblocks:not([dir=rtl]) h6,\n.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),\n.mce-visualblocks:not([dir=rtl]) section,\n.mce-visualblocks:not([dir=rtl]) article,\n.mce-visualblocks:not([dir=rtl]) blockquote,\n.mce-visualblocks:not([dir=rtl]) address,\n.mce-visualblocks:not([dir=rtl]) pre,\n.mce-visualblocks:not([dir=rtl]) figure,\n.mce-visualblocks:not([dir=rtl]) figcaption,\n.mce-visualblocks:not([dir=rtl]) hgroup,\n.mce-visualblocks:not([dir=rtl]) aside,\n.mce-visualblocks:not([dir=rtl]) ul,\n.mce-visualblocks:not([dir=rtl]) ol,\n.mce-visualblocks:not([dir=rtl]) dl {\n  margin-left: 3px;\n}\n.mce-visualblocks[dir=rtl] p,\n.mce-visualblocks[dir=rtl] h1,\n.mce-visualblocks[dir=rtl] h2,\n.mce-visualblocks[dir=rtl] h3,\n.mce-visualblocks[dir=rtl] h4,\n.mce-visualblocks[dir=rtl] h5,\n.mce-visualblocks[dir=rtl] h6,\n.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),\n.mce-visualblocks[dir=rtl] section,\n.mce-visualblocks[dir=rtl] article,\n.mce-visualblocks[dir=rtl] blockquote,\n.mce-visualblocks[dir=rtl] address,\n.mce-visualblocks[dir=rtl] pre,\n.mce-visualblocks[dir=rtl] figure,\n.mce-visualblocks[dir=rtl] figcaption,\n.mce-visualblocks[dir=rtl] hgroup,\n.mce-visualblocks[dir=rtl] aside,\n.mce-visualblocks[dir=rtl] ul,\n.mce-visualblocks[dir=rtl] ol,\n.mce-visualblocks[dir=rtl] dl {\n  background-position-x: right;\n  margin-right: 3px;\n}\n.mce-nbsp,\n.mce-shy {\n  background: #aaa;\n}\n.mce-shy::after {\n  content: '-';\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.css",
    "content": ".tox {\n  box-shadow: none;\n  box-sizing: content-box;\n  color: #2A3746;\n  cursor: auto;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: normal;\n  -webkit-tap-highlight-color: transparent;\n  text-decoration: none;\n  text-shadow: none;\n  text-transform: none;\n  vertical-align: initial;\n  white-space: normal;\n}\n.tox *:not(svg):not(rect) {\n  box-sizing: inherit;\n  color: inherit;\n  cursor: inherit;\n  direction: inherit;\n  font-family: inherit;\n  font-size: inherit;\n  font-style: inherit;\n  font-weight: inherit;\n  line-height: inherit;\n  -webkit-tap-highlight-color: inherit;\n  text-align: inherit;\n  text-decoration: inherit;\n  text-shadow: inherit;\n  text-transform: inherit;\n  vertical-align: inherit;\n  white-space: inherit;\n}\n.tox *:not(svg):not(rect) {\n  /* stylelint-disable-line no-duplicate-selectors */\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  float: none;\n  height: auto;\n  margin: 0;\n  max-width: none;\n  outline: 0;\n  padding: 0;\n  position: static;\n  width: auto;\n}\n.tox:not([dir=rtl]) {\n  direction: ltr;\n  text-align: left;\n}\n.tox[dir=rtl] {\n  direction: rtl;\n  text-align: right;\n}\n.tox-tinymce {\n  border: 1px solid #000000;\n  border-radius: 0;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  overflow: hidden;\n  position: relative;\n  visibility: inherit !important;\n}\n.tox.tox-tinymce-inline {\n  border: none;\n  box-shadow: none;\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-container {\n  overflow: initial;\n}\n.tox.tox-tinymce-inline .tox-editor-header {\n  background-color: #222f3e;\n  border: 1px solid #000000;\n  border-radius: 0;\n  box-shadow: none;\n  overflow: hidden;\n}\n.tox-tinymce-aux {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  z-index: 1300;\n}\n.tox-tinymce *:focus,\n.tox-tinymce-aux *:focus {\n  outline: none;\n}\nbutton::-moz-focus-inner {\n  border: 0;\n}\n.tox[dir=rtl] .tox-icon--flip svg {\n  transform: rotateY(180deg);\n}\n.tox .accessibility-issue__header {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description {\n  align-items: stretch;\n  border-radius: 3px;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .accessibility-issue__description > div {\n  padding-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div {\n  align-items: center;\n  display: flex;\n  margin-bottom: 4px;\n}\n.tox .accessibility-issue__description > div > div .tox-icon svg {\n  display: block;\n}\n.tox .accessibility-issue__repair {\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description {\n  background-color: rgba(30, 113, 170, 0.4);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon {\n  background-color: #207ab7;\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:focus {\n  background-color: #1c6ca1;\n}\n.tox .tox-dialog__body-content .accessibility-issue--info a.tox-button--naked.tox-button--icon:active {\n  background-color: #185d8c;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description {\n  background-color: rgba(255, 165, 0, 0.5);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon {\n  background-color: #FFE89D;\n  color: #2A3746;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:focus {\n  background-color: #F2D574;\n  color: #2A3746;\n}\n.tox .tox-dialog__body-content .accessibility-issue--warn a.tox-button--naked.tox-button--icon:active {\n  background-color: #E8C657;\n  color: #2A3746;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description {\n  background-color: rgba(204, 0, 0, 0.5);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon {\n  background-color: #F2BFBF;\n  color: #2A3746;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:hover,\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:focus {\n  background-color: #E9A4A4;\n  color: #2A3746;\n}\n.tox .tox-dialog__body-content .accessibility-issue--error a.tox-button--naked.tox-button--icon:active {\n  background-color: #EE9494;\n  color: #2A3746;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description {\n  background-color: rgba(120, 171, 70, 0.5);\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description > *:last-child {\n  display: none;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2 {\n  color: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content .accessibility-issue__header .tox-form__group h1,\n.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2 {\n  font-size: 14px;\n  margin-top: 0;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-left: auto;\n}\n.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 4px 4px 8px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {\n  margin-right: auto;\n}\n.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description {\n  padding: 4px 8px 4px 4px;\n}\n.tox .tox-anchorbar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-bar {\n  display: flex;\n  flex: 0 0 auto;\n}\n.tox .tox-button {\n  background-color: #207ab7;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #207ab7;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  line-height: 24px;\n  margin: 0;\n  outline: none;\n  padding: 4px 16px;\n  position: relative;\n  text-align: center;\n  text-decoration: none;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-button::before {\n  border-radius: 3px;\n  bottom: -1px;\n  box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #207ab7, 0 0 0 3px rgba(32, 122, 183, 0.25);\n  content: '';\n  left: -1px;\n  opacity: 0;\n  pointer-events: none;\n  position: absolute;\n  right: -1px;\n  top: -1px;\n}\n.tox .tox-button[disabled] {\n  background-color: #207ab7;\n  background-image: none;\n  border-color: #207ab7;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-button:focus:not(:disabled) {\n  background-color: #1c6ca1;\n  background-image: none;\n  border-color: #1c6ca1;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:focus-visible:not(:disabled)::before {\n  opacity: 1;\n}\n.tox .tox-button:hover:not(:disabled) {\n  background-color: #1c6ca1;\n  background-image: none;\n  border-color: #1c6ca1;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button:active:not(:disabled) {\n  background-color: #185d8c;\n  background-image: none;\n  border-color: #185d8c;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary {\n  background-color: #3d546f;\n  background-image: none;\n  background-position: 0 0;\n  background-repeat: repeat;\n  border-color: #3d546f;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  color: #fff;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  outline: none;\n  padding: 4px 16px;\n  text-decoration: none;\n  text-transform: none;\n}\n.tox .tox-button--secondary[disabled] {\n  background-color: #3d546f;\n  background-image: none;\n  border-color: #3d546f;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-button--secondary:focus:not(:disabled) {\n  background-color: #34485f;\n  background-image: none;\n  border-color: #34485f;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary:hover:not(:disabled) {\n  background-color: #34485f;\n  background-image: none;\n  border-color: #34485f;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--secondary:active:not(:disabled) {\n  background-color: #2b3b4e;\n  background-image: none;\n  border-color: #2b3b4e;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--icon,\n.tox .tox-button.tox-button--icon,\n.tox .tox-button.tox-button--secondary.tox-button--icon {\n  padding: 4px;\n}\n.tox .tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--icon .tox-icon svg,\n.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg {\n  display: block;\n  fill: currentColor;\n}\n.tox .tox-button-link {\n  background: 0;\n  border: none;\n  box-sizing: border-box;\n  cursor: pointer;\n  display: inline-block;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  padding: 0;\n  white-space: nowrap;\n}\n.tox .tox-button-link--sm {\n  font-size: 14px;\n}\n.tox .tox-button--naked {\n  background-color: transparent;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #fff;\n}\n.tox .tox-button--naked[disabled] {\n  background-color: #3d546f;\n  border-color: #3d546f;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-button--naked:hover:not(:disabled) {\n  background-color: #34485f;\n  border-color: #34485f;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--naked:focus:not(:disabled) {\n  background-color: #34485f;\n  border-color: #34485f;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--naked:active:not(:disabled) {\n  background-color: #2b3b4e;\n  border-color: #2b3b4e;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-button--naked .tox-icon svg {\n  fill: currentColor;\n}\n.tox .tox-button--naked.tox-button--icon:hover:not(:disabled) {\n  color: #fff;\n}\n.tox .tox-checkbox {\n  align-items: center;\n  border-radius: 3px;\n  cursor: pointer;\n  display: flex;\n  height: 36px;\n  min-width: 36px;\n}\n.tox .tox-checkbox__input {\n  /* Hide from view but visible to screen readers */\n  height: 1px;\n  overflow: hidden;\n  position: absolute;\n  top: auto;\n  width: 1px;\n}\n.tox .tox-checkbox__icons {\n  align-items: center;\n  border-radius: 3px;\n  box-shadow: 0 0 0 2px transparent;\n  box-sizing: content-box;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  padding: calc(4px - 1px);\n  width: 24px;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: block;\n  fill: rgba(255, 255, 255, 0.2);\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: none;\n  fill: #207ab7;\n}\n.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: none;\n  fill: #207ab7;\n}\n.tox .tox-checkbox--disabled {\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__checked svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {\n  display: none;\n}\n.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {\n  display: block;\n}\n.tox input.tox-checkbox__input:focus + .tox-checkbox__icons {\n  border-radius: 3px;\n  box-shadow: inset 0 0 0 1px #207ab7;\n  padding: calc(4px - 1px);\n}\n.tox:not([dir=rtl]) .tox-checkbox__label {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-checkbox__input {\n  left: -10000px;\n}\n.tox:not([dir=rtl]) .tox-bar .tox-checkbox {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__label {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-checkbox__input {\n  right: -10000px;\n}\n.tox[dir=rtl] .tox-bar .tox-checkbox {\n  margin-right: 4px;\n}\n.tox {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox .tox-collection--toolbar .tox-collection__group {\n  display: flex;\n  padding: 0;\n}\n.tox .tox-collection--grid .tox-collection__group {\n  display: flex;\n  flex-wrap: wrap;\n  max-height: 208px;\n  overflow-x: hidden;\n  overflow-y: auto;\n  padding: 0;\n}\n.tox .tox-collection--list .tox-collection__group {\n  border-bottom-width: 0;\n  border-color: #1a1a1a;\n  border-left-width: 0;\n  border-right-width: 0;\n  border-style: solid;\n  border-top-width: 1px;\n  padding: 4px 0;\n}\n.tox .tox-collection--list .tox-collection__group:first-child {\n  border-top-width: 0;\n}\n.tox .tox-collection__group-heading {\n  background-color: #333333;\n  color: #fff;\n  cursor: default;\n  font-size: 12px;\n  font-style: normal;\n  font-weight: normal;\n  margin-bottom: 4px;\n  margin-top: -4px;\n  padding: 4px 8px;\n  text-transform: none;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection__item {\n  align-items: center;\n  border-radius: 3px;\n  color: #fff;\n  display: flex;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n          user-select: none;\n}\n.tox .tox-collection--list .tox-collection__item {\n  padding: 4px 8px;\n}\n.tox .tox-collection--toolbar .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--grid .tox-collection__item {\n  border-radius: 3px;\n  padding: 4px;\n}\n.tox .tox-collection--list .tox-collection__item--enabled {\n  background-color: #2b3b4e;\n  color: #fff;\n}\n.tox .tox-collection--list .tox-collection__item--active {\n  background-color: #4a5562;\n}\n.tox .tox-collection--toolbar .tox-collection__item--enabled {\n  background-color: #757d87;\n  color: #fff;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active {\n  background-color: #4a5562;\n}\n.tox .tox-collection--grid .tox-collection__item--enabled {\n  background-color: #757d87;\n  color: #fff;\n}\n.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  background-color: #4a5562;\n  color: #fff;\n}\n.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #fff;\n}\n.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled) {\n  color: #fff;\n}\n.tox .tox-collection__item-icon,\n.tox .tox-collection__item-checkmark {\n  align-items: center;\n  display: flex;\n  height: 24px;\n  justify-content: center;\n  width: 24px;\n}\n.tox .tox-collection__item-icon svg,\n.tox .tox-collection__item-checkmark svg {\n  fill: currentColor;\n}\n.tox .tox-collection--toolbar-lg .tox-collection__item-icon {\n  height: 48px;\n  width: 48px;\n}\n.tox .tox-collection__item-label {\n  color: currentColor;\n  display: inline-block;\n  flex: 1;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 24px;\n  text-transform: none;\n  word-break: break-all;\n}\n.tox .tox-collection__item-accessory {\n  color: rgba(255, 255, 255, 0.5);\n  display: inline-block;\n  font-size: 14px;\n  height: 24px;\n  line-height: 24px;\n  text-transform: none;\n}\n.tox .tox-collection__item-caret {\n  align-items: center;\n  display: flex;\n  min-height: 24px;\n}\n.tox .tox-collection__item-caret::after {\n  content: '';\n  font-size: 0;\n  min-height: inherit;\n}\n.tox .tox-collection__item-caret svg {\n  fill: #fff;\n}\n.tox .tox-collection__item--state-disabled {\n  background-color: transparent;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg {\n  display: none;\n}\n.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory + .tox-collection__item-checkmark {\n  display: none;\n}\n.tox .tox-collection--horizontal {\n  background-color: #2b3b4e;\n  border: 1px solid #1a1a1a;\n  border-radius: 3px;\n  box-shadow: 0 0 2px 0 rgba(42, 55, 70, 0.2), 0 4px 8px 0 rgba(42, 55, 70, 0.15);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n  margin-bottom: 0;\n  overflow-x: auto;\n  padding: 0;\n}\n.tox .tox-collection--horizontal .tox-collection__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: nowrap;\n  margin: 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item {\n  height: 34px;\n  margin: 3px 0 2px 0;\n  padding: 0 4px;\n}\n.tox .tox-collection--horizontal .tox-collection__item-label {\n  white-space: nowrap;\n}\n.tox .tox-collection--horizontal .tox-collection__item-caret {\n  margin-left: 4px;\n}\n.tox .tox-collection__item-container {\n  display: flex;\n}\n.tox .tox-collection__item-container--row {\n  align-items: center;\n  flex: 1 1 auto;\n  flex-direction: row;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-left {\n  margin-right: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--align-right {\n  justify-content: flex-end;\n  margin-left: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top {\n  align-items: flex-start;\n  margin-bottom: auto;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle {\n  align-items: center;\n}\n.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom {\n  align-items: flex-end;\n  margin-top: auto;\n}\n.tox .tox-collection__item-container--column {\n  align-self: center;\n  flex: 1 1 auto;\n  flex-direction: column;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-left {\n  align-items: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--align-right {\n  align-items: flex-end;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top {\n  align-self: flex-start;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle {\n  align-self: center;\n}\n.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom {\n  align-self: flex-end;\n}\n.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-right: 1px solid #000000;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-left: 4px;\n}\n.tox:not([dir=rtl]) .tox-collection__item-accessory {\n  margin-left: 16px;\n  text-align: right;\n}\n.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret {\n  margin-left: 16px;\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {\n  border-left: 1px solid #000000;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > *:not(:first-child) {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {\n  margin-right: 4px;\n}\n.tox[dir=rtl] .tox-collection__item-accessory {\n  margin-right: 16px;\n  text-align: left;\n}\n.tox[dir=rtl] .tox-collection .tox-collection__item-caret {\n  margin-right: 16px;\n  transform: rotateY(180deg);\n}\n.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret {\n  margin-right: 4px;\n}\n.tox .tox-color-picker-container {\n  display: flex;\n  flex-direction: row;\n  height: 225px;\n  margin: 0;\n}\n.tox .tox-sv-palette {\n  box-sizing: border-box;\n  display: flex;\n  height: 100%;\n}\n.tox .tox-sv-palette-spectrum {\n  height: 100%;\n}\n.tox .tox-sv-palette,\n.tox .tox-sv-palette-spectrum {\n  width: 225px;\n}\n.tox .tox-sv-palette-thumb {\n  background: none;\n  border: 1px solid black;\n  border-radius: 50%;\n  box-sizing: content-box;\n  height: 12px;\n  position: absolute;\n  width: 12px;\n}\n.tox .tox-sv-palette-inner-thumb {\n  border: 1px solid white;\n  border-radius: 50%;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox .tox-hue-slider {\n  box-sizing: border-box;\n  height: 100%;\n  width: 25px;\n}\n.tox .tox-hue-slider-spectrum {\n  background: linear-gradient(to bottom, #f00, #ff0080, #f0f, #8000ff, #00f, #0080ff, #0ff, #00ff80, #0f0, #80ff00, #ff0, #ff8000, #f00);\n  height: 100%;\n  width: 100%;\n}\n.tox .tox-hue-slider,\n.tox .tox-hue-slider-spectrum {\n  width: 20px;\n}\n.tox .tox-hue-slider-thumb {\n  background: white;\n  border: 1px solid black;\n  box-sizing: content-box;\n  height: 4px;\n  width: 100%;\n}\n.tox .tox-rgb-form {\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n}\n.tox .tox-rgb-form div {\n  align-items: center;\n  display: flex;\n  justify-content: space-between;\n  margin-bottom: 5px;\n  width: inherit;\n}\n.tox .tox-rgb-form input {\n  width: 6em;\n}\n.tox .tox-rgb-form input.tox-invalid {\n  /* Need !important to override Chrome's focus styling unfortunately */\n  border: 1px solid red !important;\n}\n.tox .tox-rgb-form .tox-rgba-preview {\n  border: 1px solid black;\n  flex-grow: 2;\n  margin-bottom: 0;\n}\n.tox:not([dir=rtl]) .tox-sv-palette {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider {\n  margin-right: 15px;\n}\n.tox:not([dir=rtl]) .tox-hue-slider-thumb {\n  margin-left: -1px;\n}\n.tox:not([dir=rtl]) .tox-rgb-form label {\n  margin-right: 0.5em;\n}\n.tox[dir=rtl] .tox-sv-palette {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider {\n  margin-left: 15px;\n}\n.tox[dir=rtl] .tox-hue-slider-thumb {\n  margin-right: -1px;\n}\n.tox[dir=rtl] .tox-rgb-form label {\n  margin-left: 0.5em;\n}\n.tox .tox-toolbar .tox-swatches,\n.tox .tox-toolbar__primary .tox-swatches,\n.tox .tox-toolbar__overflow .tox-swatches {\n  margin: 2px 0 3px 4px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-swatches-menu {\n  border: 0;\n  margin: -4px 0;\n}\n.tox .tox-swatches__row {\n  display: flex;\n}\n.tox .tox-swatch {\n  height: 30px;\n  transition: transform 0.15s, box-shadow 0.15s;\n  width: 30px;\n}\n.tox .tox-swatch:hover,\n.tox .tox-swatch:focus {\n  box-shadow: 0 0 0 1px rgba(127, 127, 127, 0.3) inset;\n  transform: scale(0.8);\n}\n.tox .tox-swatch--remove {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n}\n.tox .tox-swatch--remove svg path {\n  stroke: #e74c3c;\n}\n.tox .tox-swatches__picker-btn {\n  align-items: center;\n  background-color: transparent;\n  border: 0;\n  cursor: pointer;\n  display: flex;\n  height: 30px;\n  justify-content: center;\n  outline: none;\n  padding: 0;\n  width: 30px;\n}\n.tox .tox-swatches__picker-btn svg {\n  fill: #fff;\n  height: 24px;\n  width: 24px;\n}\n.tox .tox-swatches__picker-btn:hover {\n  background: #4a5562;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg {\n  display: none;\n  fill: #fff;\n  height: 24px;\n  margin: calc((30px - 24px) / 2) calc((30px - 24px) / 2);\n  width: 24px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove) svg path {\n  fill: #fff;\n  paint-order: stroke;\n  stroke: #222f3e;\n  stroke-width: 2px;\n}\n.tox div.tox-swatch:not(.tox-swatch--remove)[aria-checked=\"true\"] svg {\n  display: block;\n}\n.tox:not([dir=rtl]) .tox-swatches__picker-btn {\n  margin-left: auto;\n}\n.tox[dir=rtl] .tox-swatches__picker-btn {\n  margin-right: auto;\n}\n.tox .tox-comment-thread {\n  background: #2b3b4e;\n  position: relative;\n}\n.tox .tox-comment-thread > *:not(:first-child) {\n  margin-top: 8px;\n}\n.tox .tox-comment {\n  background: #2b3b4e;\n  border: 1px solid #000000;\n  border-radius: 3px;\n  box-shadow: 0 4px 8px 0 rgba(42, 55, 70, 0.1);\n  padding: 8px 8px 16px 8px;\n  position: relative;\n}\n.tox .tox-comment__header {\n  align-items: center;\n  color: #fff;\n  display: flex;\n  justify-content: space-between;\n}\n.tox .tox-comment__date {\n  color: #fff;\n  font-size: 12px;\n  line-height: 18px;\n}\n.tox .tox-comment__body {\n  color: #fff;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin-top: 8px;\n  position: relative;\n  text-transform: initial;\n}\n.tox .tox-comment__body textarea {\n  resize: none;\n  white-space: normal;\n  width: 100%;\n}\n.tox .tox-comment__expander {\n  padding-top: 8px;\n}\n.tox .tox-comment__expander p {\n  color: rgba(255, 255, 255, 0.5);\n  font-size: 14px;\n  font-style: normal;\n}\n.tox .tox-comment__body p {\n  margin: 0;\n}\n.tox .tox-comment__buttonspacing {\n  padding-top: 16px;\n  text-align: center;\n}\n.tox .tox-comment-thread__overlay::after {\n  background: #2b3b4e;\n  bottom: 0;\n  content: \"\";\n  display: flex;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__reply {\n  display: flex;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 8px;\n}\n.tox .tox-comment__reply > *:first-child {\n  margin-bottom: 8px;\n  width: 100%;\n}\n.tox .tox-comment__edit {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: flex-end;\n  margin-top: 16px;\n}\n.tox .tox-comment__gradient::after {\n  background: linear-gradient(rgba(43, 59, 78, 0), #2b3b4e);\n  bottom: 0;\n  content: \"\";\n  display: block;\n  height: 5em;\n  margin-top: -40px;\n  position: absolute;\n  width: 100%;\n}\n.tox .tox-comment__overlay {\n  background: #2b3b4e;\n  bottom: 0;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  left: 0;\n  opacity: 0.9;\n  position: absolute;\n  right: 0;\n  text-align: center;\n  top: 0;\n  z-index: 5;\n}\n.tox .tox-comment__loading-text {\n  align-items: center;\n  color: #fff;\n  display: flex;\n  flex-direction: column;\n  position: relative;\n}\n.tox .tox-comment__loading-text > div {\n  padding-bottom: 16px;\n}\n.tox .tox-comment__overlaytext {\n  bottom: 0;\n  flex-direction: column;\n  font-size: 14px;\n  left: 0;\n  padding: 1em;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 10;\n}\n.tox .tox-comment__overlaytext p {\n  background-color: #2b3b4e;\n  box-shadow: 0 0 8px 8px #2b3b4e;\n  color: #fff;\n  text-align: center;\n}\n.tox .tox-comment__overlaytext div:nth-of-type(2) {\n  font-size: 0.8em;\n}\n.tox .tox-comment__busy-spinner {\n  align-items: center;\n  background-color: #2b3b4e;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 20;\n}\n.tox .tox-comment__scroll {\n  display: flex;\n  flex-direction: column;\n  flex-shrink: 1;\n  overflow: auto;\n}\n.tox .tox-conversations {\n  margin: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__edit {\n  margin-left: 8px;\n}\n.tox:not([dir=rtl]) .tox-comment__buttonspacing > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__edit > *:last-child,\n.tox:not([dir=rtl]) .tox-comment__reply > *:last-child {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-comment__edit {\n  margin-right: 8px;\n}\n.tox[dir=rtl] .tox-comment__buttonspacing > *:last-child,\n.tox[dir=rtl] .tox-comment__edit > *:last-child,\n.tox[dir=rtl] .tox-comment__reply > *:last-child {\n  margin-right: 8px;\n}\n.tox .tox-user {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-user__avatar svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-user__avatar img {\n  border-radius: 50%;\n  height: 36px;\n  object-fit: cover;\n  vertical-align: middle;\n  width: 36px;\n}\n.tox .tox-user__name {\n  color: #fff;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: bold;\n  line-height: 18px;\n  text-transform: none;\n}\n.tox:not([dir=rtl]) .tox-user__avatar svg,\n.tox:not([dir=rtl]) .tox-user__avatar img {\n  margin-right: 8px;\n}\n.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar svg,\n.tox[dir=rtl] .tox-user__avatar img {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-user__avatar + .tox-user__name {\n  margin-right: 8px;\n}\n.tox .tox-dialog-wrap {\n  align-items: center;\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: fixed;\n  right: 0;\n  top: 0;\n  z-index: 1100;\n}\n.tox .tox-dialog-wrap__backdrop {\n  background-color: rgba(34, 47, 62, 0.75);\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 1;\n}\n.tox .tox-dialog-wrap__backdrop--opaque {\n  background-color: #222f3e;\n}\n.tox .tox-dialog {\n  background-color: #2b3b4e;\n  border-color: #000000;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: 0 16px 16px -10px rgba(42, 55, 70, 0.15), 0 0 40px 1px rgba(42, 55, 70, 0.15);\n  display: flex;\n  flex-direction: column;\n  max-height: 100%;\n  max-width: 480px;\n  overflow: hidden;\n  position: relative;\n  width: 95vw;\n  z-index: 2;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog {\n    align-self: flex-start;\n    margin: 8px auto;\n    max-height: calc(100vh - 8px * 2);\n    width: calc(100vw - 16px);\n  }\n}\n.tox .tox-dialog-inline {\n  z-index: 1100;\n}\n.tox .tox-dialog__header {\n  align-items: center;\n  background-color: #2b3b4e;\n  border-bottom: none;\n  color: #fff;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 16px 0 16px;\n  position: relative;\n}\n.tox .tox-dialog__header .tox-button {\n  z-index: 1;\n}\n.tox .tox-dialog__draghandle {\n  cursor: grab;\n  height: 100%;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.tox .tox-dialog__draghandle:active {\n  cursor: grabbing;\n}\n.tox .tox-dialog__dismiss {\n  margin-left: auto;\n}\n.tox .tox-dialog__title {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  margin: 0;\n  text-transform: none;\n}\n.tox .tox-dialog__body {\n  color: #fff;\n  display: flex;\n  flex: 1;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  min-width: 0;\n  text-align: left;\n  text-transform: none;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body {\n    flex-direction: column;\n  }\n}\n.tox .tox-dialog__body-nav {\n  align-items: flex-start;\n  display: flex;\n  flex-direction: column;\n  padding: 16px 16px;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {\n    flex-direction: row;\n    -webkit-overflow-scrolling: touch;\n    overflow-x: auto;\n    padding-bottom: 0;\n  }\n}\n.tox .tox-dialog__body-nav-item {\n  border-bottom: 2px solid transparent;\n  color: rgba(255, 255, 255, 0.5);\n  display: inline-block;\n  font-size: 14px;\n  line-height: 1.3;\n  margin-bottom: 8px;\n  text-decoration: none;\n  white-space: nowrap;\n}\n.tox .tox-dialog__body-nav-item:focus {\n  background-color: rgba(32, 122, 183, 0.1);\n}\n.tox .tox-dialog__body-nav-item--active {\n  border-bottom: 2px solid #207ab7;\n  color: #207ab7;\n}\n.tox .tox-dialog__body-content {\n  box-sizing: border-box;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n  max-height: 650px;\n  overflow: auto;\n  -webkit-overflow-scrolling: touch;\n  padding: 16px 16px;\n}\n.tox .tox-dialog__body-content > * {\n  margin-bottom: 0;\n  margin-top: 16px;\n}\n.tox .tox-dialog__body-content > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content > *:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content a {\n  color: #207ab7;\n  cursor: pointer;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:hover,\n.tox .tox-dialog__body-content a:focus {\n  color: #185d8c;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content a:active {\n  color: #185d8c;\n  text-decoration: none;\n}\n.tox .tox-dialog__body-content svg {\n  fill: #fff;\n}\n.tox .tox-dialog__body-content ul {\n  display: block;\n  list-style-type: disc;\n  margin-bottom: 16px;\n  margin-inline-end: 0;\n  margin-inline-start: 0;\n  padding-inline-start: 2.5rem;\n}\n.tox .tox-dialog__body-content .tox-form__group h1 {\n  color: #fff;\n  font-size: 20px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group h2 {\n  color: #fff;\n  font-size: 16px;\n  font-style: normal;\n  font-weight: bold;\n  letter-spacing: normal;\n  margin-bottom: 16px;\n  margin-top: 2rem;\n  text-transform: none;\n}\n.tox .tox-dialog__body-content .tox-form__group p {\n  margin-bottom: 16px;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:first-child,\n.tox .tox-dialog__body-content .tox-form__group h2:first-child,\n.tox .tox-dialog__body-content .tox-form__group p:first-child {\n  margin-top: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:last-child,\n.tox .tox-dialog__body-content .tox-form__group h2:last-child,\n.tox .tox-dialog__body-content .tox-form__group p:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-dialog__body-content .tox-form__group h1:only-child,\n.tox .tox-dialog__body-content .tox-form__group h2:only-child,\n.tox .tox-dialog__body-content .tox-form__group p:only-child {\n  margin-bottom: 0;\n  margin-top: 0;\n}\n.tox .tox-dialog--width-lg {\n  height: 650px;\n  max-width: 1200px;\n}\n.tox .tox-dialog--width-md {\n  max-width: 800px;\n}\n.tox .tox-dialog--width-md .tox-dialog__body-content {\n  overflow: auto;\n}\n.tox .tox-dialog__body-content--centered {\n  text-align: center;\n}\n.tox .tox-dialog__footer {\n  align-items: center;\n  background-color: #2b3b4e;\n  border-top: 1px solid #000000;\n  display: flex;\n  justify-content: space-between;\n  padding: 8px 16px;\n}\n.tox .tox-dialog__footer-start,\n.tox .tox-dialog__footer-end {\n  display: flex;\n}\n.tox .tox-dialog__busy-spinner {\n  align-items: center;\n  background-color: rgba(34, 47, 62, 0.75);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 3;\n}\n.tox .tox-dialog__table {\n  border-collapse: collapse;\n  width: 100%;\n}\n.tox .tox-dialog__table thead th {\n  font-weight: bold;\n  padding-bottom: 8px;\n}\n.tox .tox-dialog__table tbody tr {\n  border-bottom: 1px solid #000000;\n}\n.tox .tox-dialog__table tbody tr:last-child {\n  border-bottom: none;\n}\n.tox .tox-dialog__table td {\n  padding-bottom: 8px;\n  padding-top: 8px;\n}\n.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {\n  background: #fff;\n}\n.tox .tox-dialog__popups {\n  position: absolute;\n  width: 100%;\n  z-index: 1100;\n}\n.tox .tox-dialog__body-iframe {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-dialog__body-iframe .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox .tox-dialog-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox .tox-dialog-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox .tox-dialog-dock-transition {\n  transition: visibility 0s linear 0.3s, opacity 0.3s ease;\n}\n.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {\n  transition-delay: 0s;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {\n    margin-right: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {\n    margin-left: 8px;\n  }\n}\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-dialog__body {\n  text-align: right;\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {\n    margin-left: 0;\n  }\n}\n@media only screen and (max-width: 767px ) {\n  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {\n    margin-right: 8px;\n  }\n}\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start > *,\n.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end > * {\n  margin-right: 8px;\n}\nbody.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox .tox-dropzone-container {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dropzone {\n  align-items: center;\n  background: #fff;\n  border: 2px dashed #000000;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  flex-grow: 1;\n  justify-content: center;\n  min-height: 100px;\n  padding: 10px;\n}\n.tox .tox-dropzone p {\n  color: rgba(255, 255, 255, 0.5);\n  margin: 0 0 16px 0;\n}\n.tox .tox-edit-area {\n  display: flex;\n  flex: 1;\n  overflow: hidden;\n  position: relative;\n}\n.tox .tox-edit-area__iframe {\n  background-color: #fff;\n  border: 0;\n  box-sizing: border-box;\n  flex: 1;\n  height: 100%;\n  position: absolute;\n  width: 100%;\n}\n.tox.tox-inline-edit-area {\n  border: 1px dotted #000000;\n}\n.tox .tox-editor-container {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  overflow: hidden;\n}\n.tox .tox-editor-header {\n  display: grid;\n  grid-template-columns: 1fr min-content;\n  z-index: 1;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header {\n  background-color: #222f3e;\n  border-bottom: none;\n  box-shadow: none;\n  padding: 4px 0;\n  transition: box-shadow 0.5s;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-bottom .tox-editor-header {\n  border-top: 1px solid #000000;\n  box-shadow: none;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on .tox-editor-header {\n  background-color: #222f3e;\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n  padding: 4px 0;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on.tox-tinymce--toolbar-bottom .tox-editor-header {\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n}\n.tox.tox:not(.tox-tinymce-inline) .tox-editor-header.tox-editor-header--empty {\n  background: none;\n  border: none;\n  box-shadow: none;\n  padding: 0;\n}\n.tox-editor-dock-fadeout {\n  opacity: 0;\n  visibility: hidden;\n}\n.tox-editor-dock-fadein {\n  opacity: 1;\n  visibility: visible;\n}\n.tox-editor-dock-transition {\n  transition: visibility 0s linear 0.25s, opacity 0.25s ease;\n}\n.tox-editor-dock-transition.tox-editor-dock-fadein {\n  transition-delay: 0s;\n}\n.tox .tox-control-wrap {\n  flex: 1;\n  position: relative;\n}\n.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,\n.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid {\n  display: none;\n}\n.tox .tox-control-wrap svg {\n  display: block;\n}\n.tox .tox-control-wrap__status-icon-wrap {\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-control-wrap__status-icon-invalid svg {\n  fill: #c00;\n}\n.tox .tox-control-wrap__status-icon-unknown svg {\n  fill: orange;\n}\n.tox .tox-control-wrap__status-icon-valid svg {\n  fill: green;\n}\n.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,\n.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield {\n  padding-right: 32px;\n}\n.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap {\n  right: 4px;\n}\n.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,\n.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield {\n  padding-left: 32px;\n}\n.tox[dir=rtl] .tox-control-wrap__status-icon-wrap {\n  left: 4px;\n}\n.tox .tox-autocompleter {\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-menu {\n  box-sizing: border-box;\n  max-width: 25em;\n}\n.tox .tox-autocompleter .tox-autocompleter-highlight {\n  font-weight: bold;\n}\n.tox .tox-color-input {\n  display: flex;\n  position: relative;\n  z-index: 1;\n}\n.tox .tox-color-input .tox-textfield {\n  z-index: -1;\n}\n.tox .tox-color-input span {\n  border-color: rgba(42, 55, 70, 0.2);\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  height: 24px;\n  position: absolute;\n  top: 6px;\n  width: 24px;\n}\n.tox .tox-color-input span:hover:not([aria-disabled=true]),\n.tox .tox-color-input span:focus:not([aria-disabled=true]) {\n  border-color: #207ab7;\n  cursor: pointer;\n}\n.tox .tox-color-input span::before {\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.25) 25%, transparent 25%), linear-gradient(-45deg, rgba(255, 255, 255, 0.25) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(255, 255, 255, 0.25) 75%), linear-gradient(-45deg, transparent 75%, rgba(255, 255, 255, 0.25) 75%);\n  background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n  background-size: 12px 12px;\n  border: 1px solid #2b3b4e;\n  border-radius: 3px;\n  box-sizing: border-box;\n  content: '';\n  height: 24px;\n  left: -1px;\n  position: absolute;\n  top: -1px;\n  width: 24px;\n  z-index: -1;\n}\n.tox .tox-color-input span[aria-disabled=true] {\n  cursor: not-allowed;\n}\n.tox:not([dir=rtl]) .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-color-input .tox-textfield {\n  padding-left: 36px;\n}\n.tox:not([dir=rtl]) .tox-color-input span {\n  left: 6px;\n}\n.tox[dir=\"rtl\"] .tox-color-input {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=\"rtl\"] .tox-color-input .tox-textfield {\n  padding-right: 36px;\n}\n.tox[dir=\"rtl\"] .tox-color-input span {\n  right: 6px;\n}\n.tox .tox-label,\n.tox .tox-toolbar-label {\n  color: rgba(255, 255, 255, 0.5);\n  display: block;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.3;\n  padding: 0 8px 0 0;\n  text-transform: none;\n  white-space: nowrap;\n}\n.tox .tox-toolbar-label {\n  padding: 0 8px;\n}\n.tox[dir=rtl] .tox-label {\n  padding: 0 0 0 8px;\n}\n.tox .tox-form {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group {\n  box-sizing: border-box;\n  margin-bottom: 4px;\n}\n.tox .tox-form-group--maximize {\n  flex: 1;\n}\n.tox .tox-form__group--error {\n  color: #c00;\n}\n.tox .tox-form__group--collection {\n  display: flex;\n}\n.tox .tox-form__grid {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: space-between;\n}\n.tox .tox-form__grid--2col > .tox-form__group {\n  width: calc(50% - (8px / 2));\n}\n.tox .tox-form__grid--3col > .tox-form__group {\n  width: calc(100% / 3 - (8px / 2));\n}\n.tox .tox-form__grid--4col > .tox-form__group {\n  width: calc(25% - (8px / 2));\n}\n.tox .tox-form__controls-h-stack {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--inline {\n  align-items: center;\n  display: flex;\n}\n.tox .tox-form__group--stretched {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-form__group--stretched .tox-textarea {\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-form__group--stretched .tox-navobj :nth-child(2) {\n  flex: 1;\n  height: 100%;\n}\n.tox:not([dir=rtl]) .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-form__controls-h-stack > *:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-lock.tox-locked .tox-lock-icon__unlock,\n.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock {\n  display: none;\n}\n.tox .tox-textfield,\n.tox .tox-toolbar-textfield,\n.tox .tox-listboxfield .tox-listbox--select,\n.tox .tox-textarea {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #2b3b4e;\n  border-color: #000000;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 4.75px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-textfield[disabled],\n.tox .tox-textarea[disabled] {\n  background-color: #222f3e;\n  color: rgba(255, 255, 255, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-textfield:focus,\n.tox .tox-listboxfield .tox-listbox--select:focus,\n.tox .tox-textarea:focus {\n  background-color: #2b3b4e;\n  border-color: #207ab7;\n  box-shadow: none;\n  outline: 2px solid rgba(32, 122, 183, 0.25);\n}\n.tox .tox-toolbar-textfield {\n  border-width: 0;\n  margin-bottom: 3px;\n  margin-top: 2px;\n  max-width: 250px;\n}\n.tox .tox-naked-btn {\n  background-color: transparent;\n  border: 0;\n  border-color: transparent;\n  box-shadow: unset;\n  color: #207ab7;\n  cursor: pointer;\n  display: block;\n  margin: 0;\n  padding: 0;\n}\n.tox .tox-naked-btn svg {\n  display: block;\n  fill: #fff;\n}\n.tox:not([dir=rtl]) .tox-toolbar-textfield + * {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-toolbar-textfield + * {\n  margin-right: 4px;\n}\n.tox .tox-listboxfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-listboxfield .tox-listbox--select[disabled] {\n  background-color: #19232e;\n  color: rgba(255, 255, 255, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-listbox__select-label {\n  cursor: default;\n  flex: 1;\n  margin: 0 4px;\n}\n.tox .tox-listbox__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-listbox__select-chevron svg {\n  fill: #fff;\n}\n.tox .tox-listboxfield .tox-listbox--select {\n  align-items: center;\n  display: flex;\n}\n.tox:not([dir=rtl]) .tox-listboxfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-listboxfield svg {\n  left: 8px;\n}\n.tox .tox-selectfield {\n  cursor: pointer;\n  position: relative;\n}\n.tox .tox-selectfield select {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #2b3b4e;\n  border-color: #000000;\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  color: #fff;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  margin: 0;\n  min-height: 34px;\n  outline: none;\n  padding: 5px 4.75px;\n  resize: none;\n  width: 100%;\n}\n.tox .tox-selectfield select[disabled] {\n  background-color: #19232e;\n  color: rgba(255, 255, 255, 0.85);\n  cursor: not-allowed;\n}\n.tox .tox-selectfield select::-ms-expand {\n  display: none;\n}\n.tox .tox-selectfield select:focus {\n  background-color: #2b3b4e;\n  border-color: #207ab7;\n  box-shadow: none;\n  outline: 2px solid rgba(32, 122, 183, 0.25);\n}\n.tox .tox-selectfield svg {\n  pointer-events: none;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"0\"],\n.tox:not([dir=rtl]) .tox-selectfield select[size=\"1\"] {\n  padding-right: 24px;\n}\n.tox:not([dir=rtl]) .tox-selectfield svg {\n  right: 8px;\n}\n.tox[dir=rtl] .tox-selectfield select[size=\"0\"],\n.tox[dir=rtl] .tox-selectfield select[size=\"1\"] {\n  padding-left: 24px;\n}\n.tox[dir=rtl] .tox-selectfield svg {\n  left: 8px;\n}\n.tox .tox-textarea {\n  -webkit-appearance: textarea;\n     -moz-appearance: textarea;\n          appearance: textarea;\n  white-space: pre-wrap;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n.tox .tox-help__more-link {\n  list-style: none;\n  margin-top: 1em;\n}\n.tox .tox-imagepreview {\n  background-color: #666;\n  height: 380px;\n  overflow: hidden;\n  position: relative;\n  width: 100%;\n}\n.tox .tox-imagepreview.tox-imagepreview__loaded {\n  overflow: auto;\n}\n.tox .tox-imagepreview__container {\n  display: flex;\n  left: 100vw;\n  position: absolute;\n  top: 100vw;\n}\n.tox .tox-imagepreview__image {\n  background: url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==);\n}\n.tox .tox-image-tools .tox-spacer {\n  flex: 1;\n}\n.tox .tox-image-tools .tox-bar {\n  align-items: center;\n  display: flex;\n  height: 60px;\n  justify-content: center;\n}\n.tox .tox-image-tools .tox-imagepreview,\n.tox .tox-image-tools .tox-imagepreview + .tox-bar {\n  margin-top: 8px;\n}\n.tox .tox-image-tools .tox-croprect-block {\n  background: black;\n  filter: alpha(opacity=50);\n  opacity: 0.5;\n  position: absolute;\n  zoom: 1;\n}\n.tox .tox-image-tools .tox-croprect-handle {\n  border: 2px solid white;\n  height: 20px;\n  left: 0;\n  position: absolute;\n  top: 0;\n  width: 20px;\n}\n.tox .tox-image-tools .tox-croprect-handle-move {\n  border: 0;\n  cursor: move;\n  position: absolute;\n}\n.tox .tox-image-tools .tox-croprect-handle-nw {\n  border-width: 2px 0 0 2px;\n  cursor: nw-resize;\n  left: 100px;\n  margin: -2px 0 0 -2px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-ne {\n  border-width: 2px 2px 0 0;\n  cursor: ne-resize;\n  left: 200px;\n  margin: -2px 0 0 -20px;\n  top: 100px;\n}\n.tox .tox-image-tools .tox-croprect-handle-sw {\n  border-width: 0 0 2px 2px;\n  cursor: sw-resize;\n  left: 100px;\n  margin: -20px 2px 0 -2px;\n  top: 200px;\n}\n.tox .tox-image-tools .tox-croprect-handle-se {\n  border-width: 0 2px 2px 0;\n  cursor: se-resize;\n  left: 200px;\n  margin: -20px 0 0 -20px;\n  top: 200px;\n}\n.tox .tox-insert-table-picker {\n  display: flex;\n  flex-wrap: wrap;\n  width: 170px;\n}\n.tox .tox-insert-table-picker > div {\n  border-color: #000000;\n  border-style: solid;\n  border-width: 0 1px 1px 0;\n  box-sizing: border-box;\n  height: 17px;\n  width: 17px;\n}\n.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker {\n  margin: 0 -4px;\n}\n.tox .tox-insert-table-picker .tox-insert-table-picker__selected {\n  background-color: rgba(32, 122, 183, 0.5);\n  border-color: rgba(32, 122, 183, 0.5);\n}\n.tox .tox-insert-table-picker__label {\n  color: #fff;\n  display: block;\n  font-size: 14px;\n  padding: 4px;\n  text-align: center;\n  width: 100%;\n}\n.tox:not([dir=rtl]) {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox:not([dir=rtl]) .tox-insert-table-picker > div:nth-child(10n) {\n  border-right: 0;\n}\n.tox[dir=rtl] {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox[dir=rtl] .tox-insert-table-picker > div:nth-child(10n+1) {\n  border-right: 0;\n}\n.tox {\n  /* stylelint-disable */\n  /* stylelint-enable */\n}\n.tox .tox-menu {\n  background-color: #2b3b4e;\n  border: 1px solid #000000;\n  border-radius: 3px;\n  box-shadow: 0 4px 8px 0 rgba(42, 55, 70, 0.1);\n  display: inline-block;\n  overflow: hidden;\n  vertical-align: top;\n  z-index: 1150;\n}\n.tox .tox-menu.tox-collection.tox-collection--list {\n  padding: 0 0;\n}\n.tox .tox-menu.tox-collection.tox-collection--toolbar {\n  padding: 4px;\n}\n.tox .tox-menu.tox-collection.tox-collection--grid {\n  padding: 4px;\n}\n@media only screen and (min-width: 768px ) {\n  .tox .tox-menu .tox-collection__item-label {\n    overflow-wrap: break-word;\n    word-break: normal;\n  }\n}\n.tox .tox-menu__label h1,\n.tox .tox-menu__label h2,\n.tox .tox-menu__label h3,\n.tox .tox-menu__label h4,\n.tox .tox-menu__label h5,\n.tox .tox-menu__label h6,\n.tox .tox-menu__label p,\n.tox .tox-menu__label blockquote,\n.tox .tox-menu__label code {\n  margin: 0;\n}\n.tox .tox-menubar {\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E\") left 0 top 0 #222f3e;\n  background-color: #222f3e;\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  grid-column: 1 / -1;\n  grid-row: 1;\n  padding: 0 4px 0 4px;\n}\n.tox .tox-promotion + .tox-menubar {\n  grid-column: 1;\n}\n.tox .tox-promotion {\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E\") left 0 top 0 #222f3e;\n  background-color: #222f3e;\n  grid-column: 2;\n  grid-row: 1;\n  padding-inline-end: 8px;\n  padding-inline-start: 4px;\n  padding-top: 5px;\n}\n.tox .tox-promotion-link {\n  align-items: unsafe center;\n  background-color: #E8F1F8;\n  border-radius: 5px;\n  color: #086BE6;\n  cursor: pointer;\n  display: flex;\n  font-size: 14px;\n  height: 26.6px;\n  padding: 4px 8px;\n  white-space: nowrap;\n}\n.tox .tox-promotion-link:hover {\n  background-color: #B4D7FF;\n}\n.tox .tox-promotion-link:focus {\n  background-color: #D9EDF7;\n}\n/* Deprecated. Remove in next major release */\n.tox .tox-mbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #fff;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 34px;\n  justify-content: center;\n  margin: 2px 0 3px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0 4px;\n  text-transform: none;\n  width: auto;\n}\n.tox .tox-mbtn[disabled] {\n  background-color: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-mbtn:focus:not(:disabled) {\n  background: #4a5562;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-mbtn--active {\n  background: #757d87;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active) {\n  background: #4a5562;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-mbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-mbtn[disabled] .tox-mbtn__select-label {\n  cursor: not-allowed;\n}\n.tox .tox-mbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n  display: none;\n}\n.tox .tox-notification {\n  border-radius: 3px;\n  border-style: solid;\n  border-width: 1px;\n  box-shadow: none;\n  box-sizing: border-box;\n  display: grid;\n  font-size: 14px;\n  font-weight: normal;\n  grid-template-columns: minmax(40px, 1fr) auto minmax(40px, 1fr);\n  margin-top: 4px;\n  opacity: 0;\n  padding: 4px;\n  transition: transform 100ms ease-in, opacity 150ms ease-in;\n}\n.tox .tox-notification p {\n  font-size: 14px;\n  font-weight: normal;\n}\n.tox .tox-notification a {\n  cursor: pointer;\n  text-decoration: underline;\n}\n.tox .tox-notification--in {\n  opacity: 1;\n}\n.tox .tox-notification--success {\n  background-color: #334840;\n  border-color: #3c5440;\n  color: #fff;\n}\n.tox .tox-notification--success p {\n  color: #fff;\n}\n.tox .tox-notification--success a {\n  color: #b5d199;\n}\n.tox .tox-notification--success svg {\n  fill: #fff;\n}\n.tox .tox-notification--error {\n  background-color: #442632;\n  border-color: #55212b;\n  color: #fff;\n}\n.tox .tox-notification--error p {\n  color: #fff;\n}\n.tox .tox-notification--error a {\n  color: #e68080;\n}\n.tox .tox-notification--error svg {\n  fill: #fff;\n}\n.tox .tox-notification--warn,\n.tox .tox-notification--warning {\n  background-color: #222f3e;\n  border-color: #000000;\n  color: #fff0b3;\n}\n.tox .tox-notification--warn p,\n.tox .tox-notification--warning p {\n  color: #fff0b3;\n}\n.tox .tox-notification--warn a,\n.tox .tox-notification--warning a {\n  color: #ffcc00;\n}\n.tox .tox-notification--warn svg,\n.tox .tox-notification--warning svg {\n  fill: #fff0b3;\n}\n.tox .tox-notification--info {\n  background-color: #254161;\n  border-color: #264972;\n  color: #fff;\n}\n.tox .tox-notification--info p {\n  color: #fff;\n}\n.tox .tox-notification--info a {\n  color: #83b7f3;\n}\n.tox .tox-notification--info svg {\n  fill: #fff;\n}\n.tox .tox-notification__body {\n  align-self: center;\n  color: #fff;\n  font-size: 14px;\n  grid-column-end: 3;\n  grid-column-start: 2;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  text-align: center;\n  white-space: normal;\n  word-break: break-all;\n  word-break: break-word;\n}\n.tox .tox-notification__body > * {\n  margin: 0;\n}\n.tox .tox-notification__body > * + * {\n  margin-top: 1rem;\n}\n.tox .tox-notification__icon {\n  align-self: center;\n  grid-column-end: 2;\n  grid-column-start: 1;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification__icon svg {\n  display: block;\n}\n.tox .tox-notification__dismiss {\n  align-self: start;\n  grid-column-end: 4;\n  grid-column-start: 3;\n  grid-row-end: 2;\n  grid-row-start: 1;\n  justify-self: end;\n}\n.tox .tox-notification .tox-progress-bar {\n  grid-column-end: 4;\n  grid-column-start: 1;\n  grid-row-end: 3;\n  grid-row-start: 2;\n  justify-self: center;\n}\n.tox .tox-pop {\n  display: inline-block;\n  position: relative;\n}\n.tox .tox-pop--resizing {\n  transition: width 0.1s ease;\n}\n.tox .tox-pop--resizing .tox-toolbar,\n.tox .tox-pop--resizing .tox-toolbar__group {\n  flex-wrap: nowrap;\n}\n.tox .tox-pop--transition {\n  transition: 0.15s ease;\n  transition-property: left, right, top, bottom;\n}\n.tox .tox-pop--transition::before,\n.tox .tox-pop--transition::after {\n  transition: all 0.15s, visibility 0s, opacity 0.075s ease 0.075s;\n}\n.tox .tox-pop__dialog {\n  background-color: #222f3e;\n  border: 1px solid #000000;\n  border-radius: 3px;\n  box-shadow: 0 0 2px 0 rgba(42, 55, 70, 0.2), 0 4px 8px 0 rgba(42, 55, 70, 0.15);\n  min-width: 0;\n  overflow: hidden;\n}\n.tox .tox-pop__dialog > *:not(.tox-toolbar) {\n  margin: 4px 4px 4px 8px;\n}\n.tox .tox-pop__dialog .tox-toolbar {\n  background-color: transparent;\n  margin-bottom: -1px;\n}\n.tox .tox-pop::before,\n.tox .tox-pop::after {\n  border-style: solid;\n  content: '';\n  display: block;\n  height: 0;\n  opacity: 1;\n  position: absolute;\n  width: 0;\n}\n.tox .tox-pop.tox-pop--inset::before,\n.tox .tox-pop.tox-pop--inset::after {\n  opacity: 0;\n  transition: all 0s 0.15s, visibility 0s, opacity 0.075s ease;\n}\n.tox .tox-pop.tox-pop--bottom::before,\n.tox .tox-pop.tox-pop--bottom::after {\n  left: 50%;\n  top: 100%;\n}\n.tox .tox-pop.tox-pop--bottom::after {\n  border-color: #222f3e transparent transparent transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: -1px;\n}\n.tox .tox-pop.tox-pop--bottom::before {\n  border-color: #000000 transparent transparent transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--top::before,\n.tox .tox-pop.tox-pop--top::after {\n  left: 50%;\n  top: 0;\n  transform: translateY(-100%);\n}\n.tox .tox-pop.tox-pop--top::after {\n  border-color: transparent transparent #222f3e transparent;\n  border-width: 8px;\n  margin-left: -8px;\n  margin-top: 1px;\n}\n.tox .tox-pop.tox-pop--top::before {\n  border-color: transparent transparent #000000 transparent;\n  border-width: 9px;\n  margin-left: -9px;\n}\n.tox .tox-pop.tox-pop--left::before,\n.tox .tox-pop.tox-pop--left::after {\n  left: 0;\n  top: calc(50% - 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--left::after {\n  border-color: transparent #222f3e transparent transparent;\n  border-width: 8px;\n  margin-left: -15px;\n}\n.tox .tox-pop.tox-pop--left::before {\n  border-color: transparent #000000 transparent transparent;\n  border-width: 10px;\n  margin-left: -19px;\n}\n.tox .tox-pop.tox-pop--right::before,\n.tox .tox-pop.tox-pop--right::after {\n  left: 100%;\n  top: calc(50% + 1px);\n  transform: translateY(-50%);\n}\n.tox .tox-pop.tox-pop--right::after {\n  border-color: transparent transparent transparent #222f3e;\n  border-width: 8px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--right::before {\n  border-color: transparent transparent transparent #000000;\n  border-width: 10px;\n  margin-left: -1px;\n}\n.tox .tox-pop.tox-pop--align-left::before,\n.tox .tox-pop.tox-pop--align-left::after {\n  left: 20px;\n}\n.tox .tox-pop.tox-pop--align-right::before,\n.tox .tox-pop.tox-pop--align-right::after {\n  left: calc(100% - 20px);\n}\n.tox .tox-sidebar-wrap {\n  display: flex;\n  flex-direction: row;\n  flex-grow: 1;\n  min-height: 0;\n}\n.tox .tox-sidebar {\n  background-color: #222f3e;\n  display: flex;\n  flex-direction: row;\n  justify-content: flex-end;\n}\n.tox .tox-sidebar__slider {\n  display: flex;\n  overflow: hidden;\n}\n.tox .tox-sidebar__pane-container {\n  display: flex;\n}\n.tox .tox-sidebar__pane {\n  display: flex;\n}\n.tox .tox-sidebar--sliding-closed {\n  opacity: 0;\n}\n.tox .tox-sidebar--sliding-open {\n  opacity: 1;\n}\n.tox .tox-sidebar--sliding-growing,\n.tox .tox-sidebar--sliding-shrinking {\n  transition: width 0.5s ease, opacity 0.5s ease;\n}\n.tox .tox-selector {\n  background-color: #4099ff;\n  border-color: #4099ff;\n  border-style: solid;\n  border-width: 1px;\n  box-sizing: border-box;\n  display: inline-block;\n  height: 10px;\n  position: absolute;\n  width: 10px;\n}\n.tox.tox-platform-touch .tox-selector {\n  height: 12px;\n  width: 12px;\n}\n.tox .tox-slider {\n  align-items: center;\n  display: flex;\n  flex: 1;\n  height: 24px;\n  justify-content: center;\n  position: relative;\n}\n.tox .tox-slider__rail {\n  background-color: transparent;\n  border: 1px solid #000000;\n  border-radius: 3px;\n  height: 10px;\n  min-width: 120px;\n  width: 100%;\n}\n.tox .tox-slider__handle {\n  background-color: #207ab7;\n  border: 2px solid #185d8c;\n  border-radius: 3px;\n  box-shadow: none;\n  height: 24px;\n  left: 50%;\n  position: absolute;\n  top: 50%;\n  transform: translateX(-50%) translateY(-50%);\n  width: 14px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider:not(:first-of-type) {\n  margin-inline-start: 8px;\n}\n.tox .tox-form__controls-h-stack > .tox-form__group + .tox-slider {\n  margin-inline-start: 32px;\n}\n.tox .tox-form__controls-h-stack > .tox-slider + .tox-form__group {\n  margin-inline-start: 32px;\n}\n.tox .tox-source-code {\n  overflow: auto;\n}\n.tox .tox-spinner {\n  display: flex;\n}\n.tox .tox-spinner > div {\n  animation: tam-bouncing-dots 1.5s ease-in-out 0s infinite both;\n  background-color: rgba(255, 255, 255, 0.5);\n  border-radius: 100%;\n  height: 8px;\n  width: 8px;\n}\n.tox .tox-spinner > div:nth-child(1) {\n  animation-delay: -0.32s;\n}\n.tox .tox-spinner > div:nth-child(2) {\n  animation-delay: -0.16s;\n}\n@keyframes tam-bouncing-dots {\n  0%,\n  80%,\n  100% {\n    transform: scale(0);\n  }\n  40% {\n    transform: scale(1);\n  }\n}\n.tox:not([dir=rtl]) .tox-spinner > div:not(:first-child) {\n  margin-left: 4px;\n}\n.tox[dir=rtl] .tox-spinner > div:not(:first-child) {\n  margin-right: 4px;\n}\n.tox .tox-statusbar {\n  align-items: center;\n  background-color: #222f3e;\n  border-top: 1px solid #000000;\n  color: #fff;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 12px;\n  font-weight: normal;\n  height: 18px;\n  overflow: hidden;\n  padding: 0 8px;\n  position: relative;\n  text-transform: uppercase;\n}\n.tox .tox-statusbar__text-container {\n  display: flex;\n  flex: 1 1 auto;\n  justify-content: flex-end;\n  overflow: hidden;\n}\n.tox .tox-statusbar__path {\n  display: flex;\n  flex: 1 1 auto;\n  margin-right: auto;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__path > * {\n  display: inline;\n  white-space: nowrap;\n}\n.tox .tox-statusbar__wordcount {\n  flex: 0 0 auto;\n  margin-left: 1ch;\n}\n.tox .tox-statusbar a,\n.tox .tox-statusbar__path-item,\n.tox .tox-statusbar__wordcount {\n  color: #fff;\n  text-decoration: none;\n}\n.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) {\n  color: #fff;\n  cursor: pointer;\n}\n.tox .tox-statusbar__branding svg {\n  fill: rgba(255, 255, 255, 0.8);\n  height: 1.14em;\n  vertical-align: -0.28em;\n  width: 3.6em;\n}\n.tox .tox-statusbar__branding a:hover:not(:disabled):not([aria-disabled=true]) svg,\n.tox .tox-statusbar__branding a:focus:not(:disabled):not([aria-disabled=true]) svg {\n  fill: #fff;\n}\n.tox .tox-statusbar__resize-handle {\n  align-items: flex-end;\n  align-self: stretch;\n  cursor: nwse-resize;\n  display: flex;\n  flex: 0 0 auto;\n  justify-content: flex-end;\n  margin-left: auto;\n  margin-right: -8px;\n  padding-bottom: 3px;\n  padding-left: 1ch;\n  padding-right: 3px;\n}\n.tox .tox-statusbar__resize-handle svg {\n  display: block;\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-statusbar__resize-handle:focus svg {\n  background-color: #4a5562;\n  border-radius: 1px 1px -4px 1px;\n  box-shadow: 0 0 0 2px #4a5562;\n}\n.tox:not([dir=rtl]) .tox-statusbar__path > * {\n  margin-right: 4px;\n}\n.tox:not([dir=rtl]) .tox-statusbar__branding {\n  margin-left: 2ch;\n}\n.tox[dir=rtl] .tox-statusbar {\n  flex-direction: row-reverse;\n}\n.tox[dir=rtl] .tox-statusbar__path > * {\n  margin-left: 4px;\n}\n.tox .tox-throbber {\n  z-index: 1299;\n}\n.tox .tox-throbber__busy-spinner {\n  align-items: center;\n  background-color: rgba(34, 47, 62, 0.6);\n  bottom: 0;\n  display: flex;\n  justify-content: center;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n.tox .tox-tbtn {\n  align-items: center;\n  background: transparent;\n  border: 0;\n  border-radius: 3px;\n  box-shadow: none;\n  color: #fff;\n  display: flex;\n  flex: 0 0 auto;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  height: 34px;\n  justify-content: center;\n  margin: 3px 0 2px 0;\n  outline: none;\n  overflow: hidden;\n  padding: 0;\n  text-transform: none;\n  width: 34px;\n}\n.tox .tox-tbtn svg {\n  display: block;\n  fill: #fff;\n}\n.tox .tox-tbtn.tox-tbtn-more {\n  padding-left: 5px;\n  padding-right: 5px;\n  width: inherit;\n}\n.tox .tox-tbtn:focus {\n  background: #4a5562;\n  border: 0;\n  box-shadow: none;\n}\n.tox .tox-tbtn:hover {\n  background: #4a5562;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-tbtn:hover svg {\n  fill: #fff;\n}\n.tox .tox-tbtn:active {\n  background: #757d87;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-tbtn:active svg {\n  fill: #fff;\n}\n.tox .tox-tbtn--disabled,\n.tox .tox-tbtn--disabled:hover,\n.tox .tox-tbtn:disabled,\n.tox .tox-tbtn:disabled:hover {\n  background: transparent;\n  border: 0;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n  cursor: not-allowed;\n}\n.tox .tox-tbtn--disabled svg,\n.tox .tox-tbtn--disabled:hover svg,\n.tox .tox-tbtn:disabled svg,\n.tox .tox-tbtn:disabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-tbtn--enabled,\n.tox .tox-tbtn--enabled:hover {\n  background: #757d87;\n  border: 0;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-tbtn--enabled > *,\n.tox .tox-tbtn--enabled:hover > * {\n  transform: none;\n}\n.tox .tox-tbtn--enabled svg,\n.tox .tox-tbtn--enabled:hover svg {\n  /* stylelint-disable-line no-descending-specificity */\n  fill: #fff;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) {\n  color: #fff;\n}\n.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg {\n  fill: #fff;\n}\n.tox .tox-tbtn:active > * {\n  transform: none;\n}\n.tox .tox-tbtn--md {\n  height: 51px;\n  width: 51px;\n}\n.tox .tox-tbtn--lg {\n  flex-direction: column;\n  height: 68px;\n  width: 68px;\n}\n.tox .tox-tbtn--return {\n  align-self: stretch;\n  height: unset;\n  width: 16px;\n}\n.tox .tox-tbtn--labeled {\n  padding: 0 4px;\n  width: unset;\n}\n.tox .tox-tbtn__vlabel {\n  display: block;\n  font-size: 10px;\n  font-weight: normal;\n  letter-spacing: -0.025em;\n  margin-bottom: 4px;\n  white-space: nowrap;\n}\n.tox .tox-tbtn--select {\n  margin: 3px 0 2px 0;\n  padding: 0 4px;\n  width: auto;\n}\n.tox .tox-tbtn__select-label {\n  cursor: default;\n  font-weight: normal;\n  margin: 0 4px;\n}\n.tox .tox-tbtn__select-chevron {\n  align-items: center;\n  display: flex;\n  justify-content: center;\n  width: 16px;\n}\n.tox .tox-tbtn__select-chevron svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-tbtn--bespoke {\n  background: transparent;\n}\n.tox .tox-tbtn--bespoke + .tox-tbtn--bespoke {\n  margin-inline-start: 0;\n}\n.tox .tox-tbtn--bespoke .tox-tbtn__select-label {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  width: 7em;\n}\n.tox .tox-split-button {\n  border: 0;\n  border-radius: 3px;\n  box-sizing: border-box;\n  display: flex;\n  margin: 3px 0 2px 0;\n  overflow: hidden;\n}\n.tox .tox-split-button:hover {\n  box-shadow: 0 0 0 1px #4a5562 inset;\n}\n.tox .tox-split-button:focus {\n  background: #4a5562;\n  box-shadow: none;\n  color: #fff;\n}\n.tox .tox-split-button > * {\n  border-radius: 0;\n}\n.tox .tox-split-button__chevron {\n  width: 16px;\n}\n.tox .tox-split-button__chevron svg {\n  fill: rgba(255, 255, 255, 0.5);\n}\n.tox .tox-split-button .tox-tbtn {\n  margin: 0;\n}\n.tox .tox-split-button.tox-tbtn--disabled:hover,\n.tox .tox-split-button.tox-tbtn--disabled:focus,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,\n.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus {\n  background: transparent;\n  box-shadow: none;\n  color: rgba(255, 255, 255, 0.5);\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn--select {\n  padding: 0 0px;\n}\n.tox.tox-platform-touch .tox-split-button .tox-tbtn:not(.tox-tbtn--select):first-child {\n  width: 30px;\n}\n.tox.tox-platform-touch .tox-split-button__chevron {\n  width: 20px;\n}\n.tox .tox-toolbar-overlord {\n  background-color: #222f3e;\n}\n.tox .tox-toolbar,\n.tox .tox-toolbar__primary,\n.tox .tox-toolbar__overflow {\n  background-attachment: local;\n  background-color: #222f3e;\n  background-image: repeating-linear-gradient(#000000 0px 1px, transparent 1px 39px);\n  background-position: center top 39px;\n  background-repeat: no-repeat;\n  background-size: calc(100% - 4px * 2) calc(100% - 39px);\n  display: flex;\n  flex: 0 0 auto;\n  flex-shrink: 0;\n  flex-wrap: wrap;\n  padding: 0 0px;\n  transform: perspective(1px);\n}\n.tox .tox-toolbar-overlord > .tox-toolbar,\n.tox .tox-toolbar-overlord > .tox-toolbar__primary,\n.tox .tox-toolbar-overlord > .tox-toolbar__overflow {\n  background-position: center top 0px;\n  background-size: calc(100% - 4px * 2) calc(100% - 0px);\n}\n.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed {\n  height: 0;\n  opacity: 0;\n  padding-bottom: 0;\n  padding-top: 0;\n  visibility: hidden;\n}\n.tox .tox-toolbar__overflow--growing {\n  transition: height 0.3s ease, opacity 0.2s linear 0.1s;\n}\n.tox .tox-toolbar__overflow--shrinking {\n  transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;\n}\n.tox .tox-toolbar-overlord,\n.tox .tox-anchorbar {\n  grid-column: 1 / -1;\n}\n.tox .tox-menubar + .tox-toolbar,\n.tox .tox-menubar + .tox-toolbar-overlord {\n  border-top: 1px solid #000000;\n  margin-top: -1px;\n  padding-bottom: 0px;\n  padding-top: 0px;\n}\n.tox .tox-toolbar--scrolling {\n  flex-wrap: nowrap;\n  overflow-x: auto;\n}\n.tox .tox-pop .tox-toolbar {\n  border-width: 0;\n}\n.tox .tox-toolbar--no-divider {\n  background-image: none;\n}\n.tox .tox-toolbar-overlord .tox-toolbar:not(.tox-toolbar--scrolling):first-child,\n.tox .tox-toolbar-overlord .tox-toolbar__primary {\n  background-position: center top 39px;\n}\n.tox .tox-editor-header > .tox-toolbar--scrolling,\n.tox .tox-toolbar-overlord .tox-toolbar--scrolling:first-child {\n  background-image: none;\n}\n.tox.tox-tinymce-aux .tox-toolbar__overflow {\n  background-color: #222f3e;\n  background-position: center top 43px;\n  background-size: calc(100% - 8px * 2) calc(100% - 51px);\n  border: none;\n  border-radius: 3px;\n  box-shadow: 0 0 2px 0 rgba(42, 55, 70, 0.2), 0 4px 8px 0 rgba(42, 55, 70, 0.15);\n  overscroll-behavior: none;\n  padding: 4px 0;\n}\n.tox-pop .tox-pop__dialog {\n  /* stylelint-disable-next-line no-descending-specificity */\n}\n.tox-pop .tox-pop__dialog .tox-toolbar {\n  background-position: center top 43px;\n  background-size: calc(100% - 4px * 2) calc(100% - 51px);\n  padding: 4px 0;\n}\n.tox .tox-toolbar__group {\n  align-items: center;\n  display: flex;\n  flex-wrap: wrap;\n  margin: 0 0;\n  padding: 0 4px 0 4px;\n}\n.tox .tox-toolbar__group--pull-right {\n  margin-left: auto;\n}\n.tox .tox-toolbar--scrolling .tox-toolbar__group {\n  flex-shrink: 0;\n  flex-wrap: nowrap;\n}\n.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type) {\n  border-right: 1px solid #000000;\n}\n.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type) {\n  border-left: 1px solid #000000;\n}\n.tox .tox-tooltip {\n  display: inline-block;\n  padding: 8px;\n  position: relative;\n}\n.tox .tox-tooltip__body {\n  background-color: #3d546f;\n  border-radius: 3px;\n  box-shadow: 0 2px 4px rgba(42, 55, 70, 0.3);\n  color: rgba(255, 255, 255, 0.75);\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  padding: 4px 8px;\n  text-transform: none;\n}\n.tox .tox-tooltip__arrow {\n  position: absolute;\n}\n.tox .tox-tooltip--down .tox-tooltip__arrow {\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  border-top: 8px solid #3d546f;\n  bottom: 0;\n  left: 50%;\n  position: absolute;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--up .tox-tooltip__arrow {\n  border-bottom: 8px solid #3d546f;\n  border-left: 8px solid transparent;\n  border-right: 8px solid transparent;\n  left: 50%;\n  position: absolute;\n  top: 0;\n  transform: translateX(-50%);\n}\n.tox .tox-tooltip--right .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-left: 8px solid #3d546f;\n  border-top: 8px solid transparent;\n  position: absolute;\n  right: 0;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-tooltip--left .tox-tooltip__arrow {\n  border-bottom: 8px solid transparent;\n  border-right: 8px solid #3d546f;\n  border-top: 8px solid transparent;\n  left: 0;\n  position: absolute;\n  top: 50%;\n  transform: translateY(-50%);\n}\n.tox .tox-view-wrap,\n.tox .tox-view-wrap__slot-container {\n  background-color: #222f3e;\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n}\n.tox .tox-view__header {\n  align-items: center;\n  display: flex;\n  font-size: 16px;\n  justify-content: space-between;\n  padding: 8px 8px 0 8px;\n  position: relative;\n}\n.tox .tox-view__header-start,\n.tox .tox-view__header-end {\n  display: flex;\n}\n.tox .tox-view__pane {\n  height: 100%;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-view__pane_panel {\n  border: 1px solid #000000;\n  border-radius: 3px;\n}\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-start > *,\n.tox:not([dir=rtl]) .tox-view__header .tox-view__header-end > * {\n  margin-left: 8px;\n}\n.tox[dir=rtl] .tox-view__header .tox-view__header-start > *,\n.tox[dir=rtl] .tox-view__header .tox-view__header-end > * {\n  margin-right: 8px;\n}\n.tox .tox-well {\n  border: 1px solid #000000;\n  border-radius: 3px;\n  padding: 8px;\n  width: 100%;\n}\n.tox .tox-well > *:first-child {\n  margin-top: 0;\n}\n.tox .tox-well > *:last-child {\n  margin-bottom: 0;\n}\n.tox .tox-well > *:only-child {\n  margin: 0;\n}\n.tox .tox-custom-editor {\n  border: 1px solid #000000;\n  border-radius: 3px;\n  display: flex;\n  flex: 1;\n  position: relative;\n}\n/* stylelint-disable */\n.tox {\n  /* stylelint-enable */\n}\n.tox .tox-dialog-loading::before {\n  background-color: rgba(0, 0, 0, 0.5);\n  content: \"\";\n  height: 100%;\n  position: absolute;\n  width: 100%;\n  z-index: 1000;\n}\n.tox .tox-tab {\n  cursor: pointer;\n}\n.tox .tox-dialog__content-js {\n  display: flex;\n  flex: 1;\n}\n.tox .tox-dialog__body-content .tox-collection {\n  display: flex;\n  flex: 1;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header {\n  background-color: none;\n  padding: 0;\n}\n.tox.tox-tinymce--toolbar-bottom .tox-editor-header,\n.tox.tox-tinymce-inline .tox-editor-header {\n  margin-bottom: -1px;\n}\n.tox.tox-tinymce-inline .tox-editor-container {\n  overflow: hidden;\n}\n.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-bottom .tox-editor-header {\n  border-top: none;\n  box-shadow: none;\n}\n.tox.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header {\n  background-color: transparent;\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n  padding: 0;\n}\n.tox.tox.tox-tinymce--toolbar-sticky-on.tox-tinymce--toolbar-bottom .tox-editor-header {\n  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);\n}\n.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker {\n  margin: -4px 0;\n}\n.tox .tox-menu.tox-collection.tox-collection--list {\n  padding: 0;\n}\n.tox .tox-pop {\n  box-shadow: none;\n}\n.tox .tox-tbtn,\n.tox .tox-tbtn--select,\n.tox .tox-split-button {\n  margin: 2px 0 3px 0;\n}\n.tox .tox-toolbar,\n.tox .tox-toolbar__primary,\n.tox .tox-toolbar__overflow {\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E\") left 0 top 0px #222f3e !important;\n}\n.tox .tox-menubar + .tox-toolbar-overlord {\n  border-top: none;\n}\n.tox .tox-menubar + .tox-toolbar,\n.tox .tox-menubar + .tox-toolbar-overlord .tox-toolbar__primary {\n  border-top: 1px solid #000000;\n  margin-top: -1px;\n}\n.tox.tox-tinymce-aux .tox-toolbar__overflow {\n  border: 1px solid #000000;\n  padding: 0;\n}\n.tox .tox-pop .tox-pop__dialog .tox-toolbar {\n  padding: 0;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {\n  border-top: 1px solid #000000;\n}\n.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,\n.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child {\n  border-top: 1px solid #000000;\n}\n.tox .tox-toolbar__group {\n  padding: 0 4px 0 4px;\n}\n.tox .tox-collection__item {\n  border-radius: 0;\n  cursor: pointer;\n}\n.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),\n.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) {\n  color: #fff;\n  text-decoration: underline;\n}\n.tox .tox-statusbar__branding svg {\n  vertical-align: -0.25em;\n}\n.tox:not([dir=rtl]) .tox-statusbar__branding {\n  margin-left: 1ch;\n}\n.tox .tox-statusbar__resize-handle {\n  padding-bottom: 0;\n  padding-right: 0;\n}\n.tox .tox-button::before {\n  display: none;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.css",
    "content": "body.tox-dialog__disable-scroll {\n  overflow: hidden;\n}\n.tox-fullscreen {\n  border: 0;\n  height: 100%;\n  margin: 0;\n  overflow: hidden;\n  overscroll-behavior: none;\n  padding: 0;\n  touch-action: pinch-zoom;\n  width: 100%;\n}\n.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {\n  display: none;\n}\n.tox.tox-tinymce.tox-fullscreen,\n.tox-shadowhost.tox-fullscreen {\n  left: 0;\n  position: fixed;\n  top: 0;\n  z-index: 1200;\n}\n.tox.tox-tinymce.tox-fullscreen {\n  background-color: transparent;\n}\n.tox-fullscreen .tox.tox-tinymce-aux,\n.tox-fullscreen ~ .tox.tox-tinymce-aux {\n  z-index: 1201;\n}\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/themes/silver/index.js",
    "content": "// Exports the \"silver\" theme for usage with module loaders\n// Usage:\n//   CommonJS:\n//     require('tinymce/themes/silver')\n//   ES2015:\n//     import 'tinymce/themes/silver'\nrequire('./theme.js');"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/themes/silver/theme.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    const getPrototypeOf$2 = Object.getPrototypeOf;\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq$1 = t => a => t === a;\n    const is$2 = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf$2(o) === proto);\n    const isString = isType$1('string');\n    const isObject = isType$1('object');\n    const isPlainObject = value => is$2(value, Object);\n    const isArray = isType$1('array');\n    const isNull = eq$1(null);\n    const isBoolean = isSimpleType('boolean');\n    const isUndefined = eq$1(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n    const isArrayOf = (value, pred) => {\n      if (isArray(value)) {\n        for (let i = 0, len = value.length; i < len; ++i) {\n          if (!pred(value[i])) {\n            return false;\n          }\n        }\n        return true;\n      }\n      return false;\n    };\n\n    const noop = () => {\n    };\n    const noarg = f => () => f();\n    const compose = (fa, fb) => {\n      return (...args) => {\n        return fa(fb.apply(null, args));\n      };\n    };\n    const compose1 = (fbc, fab) => a => fbc(fab(a));\n    const constant$1 = value => {\n      return () => {\n        return value;\n      };\n    };\n    const identity = x => {\n      return x;\n    };\n    const tripleEquals = (a, b) => {\n      return a === b;\n    };\n    function curry(fn, ...initialArgs) {\n      return (...restArgs) => {\n        const all = initialArgs.concat(restArgs);\n        return fn.apply(null, all);\n      };\n    }\n    const not = f => t => !f(t);\n    const die = msg => {\n      return () => {\n        throw new Error(msg);\n      };\n    };\n    const apply$1 = f => {\n      return f();\n    };\n    const never = constant$1(false);\n    const always = constant$1(true);\n\n    var global$a = tinymce.util.Tools.resolve('tinymce.ThemeManager');\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativeSlice = Array.prototype.slice;\n    const nativeIndexOf = Array.prototype.indexOf;\n    const nativePush = Array.prototype.push;\n    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);\n    const indexOf = (xs, x) => {\n      const r = rawIndexOf(xs, x);\n      return r === -1 ? Optional.none() : Optional.some(r);\n    };\n    const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1;\n    const exists = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const range$2 = (num, f) => {\n      const r = [];\n      for (let i = 0; i < num; i++) {\n        r.push(f(i));\n      }\n      return r;\n    };\n    const chunk$1 = (array, size) => {\n      const r = [];\n      for (let i = 0; i < array.length; i += size) {\n        const s = nativeSlice.call(array, i, i + size);\n        r.push(s);\n      }\n      return r;\n    };\n    const map$2 = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$1 = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const eachr = (xs, f) => {\n      for (let i = xs.length - 1; i >= 0; i--) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const partition$3 = (xs, pred) => {\n      const pass = [];\n      const fail = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        const arr = pred(x, i) ? pass : fail;\n        arr.push(x);\n      }\n      return {\n        pass,\n        fail\n      };\n    };\n    const filter$2 = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n    const foldr = (xs, f, acc) => {\n      eachr(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const foldl = (xs, f, acc) => {\n      each$1(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const findUntil = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find$5 = (xs, pred) => {\n      return findUntil(xs, pred, never);\n    };\n    const findIndex$1 = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(i);\n        }\n      }\n      return Optional.none();\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind$3 = (xs, f) => flatten(map$2(xs, f));\n    const forall = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        const x = xs[i];\n        if (pred(x, i) !== true) {\n          return false;\n        }\n      }\n      return true;\n    };\n    const reverse = xs => {\n      const r = nativeSlice.call(xs, 0);\n      r.reverse();\n      return r;\n    };\n    const difference = (a1, a2) => filter$2(a1, x => !contains$2(a2, x));\n    const mapToObject = (xs, f) => {\n      const r = {};\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        r[String(x)] = f(x, i);\n      }\n      return r;\n    };\n    const pure$2 = x => [x];\n    const sort = (xs, comparator) => {\n      const copy = nativeSlice.call(xs, 0);\n      copy.sort(comparator);\n      return copy;\n    };\n    const get$h = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get$h(xs, 0);\n    const last$1 = xs => get$h(xs, xs.length - 1);\n    const from = isFunction(Array.from) ? Array.from : x => nativeSlice.call(x);\n    const findMap = (arr, f) => {\n      for (let i = 0; i < arr.length; i++) {\n        const r = f(arr[i], i);\n        if (r.isSome()) {\n          return r;\n        }\n      }\n      return Optional.none();\n    };\n\n    const keys = Object.keys;\n    const hasOwnProperty$1 = Object.hasOwnProperty;\n    const each = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const map$1 = (obj, f) => {\n      return tupleMap(obj, (x, i) => ({\n        k: i,\n        v: f(x, i)\n      }));\n    };\n    const tupleMap = (obj, f) => {\n      const r = {};\n      each(obj, (x, i) => {\n        const tuple = f(x, i);\n        r[tuple.k] = tuple.v;\n      });\n      return r;\n    };\n    const objAcc = r => (x, i) => {\n      r[i] = x;\n    };\n    const internalFilter = (obj, pred, onTrue, onFalse) => {\n      each(obj, (x, i) => {\n        (pred(x, i) ? onTrue : onFalse)(x, i);\n      });\n    };\n    const bifilter = (obj, pred) => {\n      const t = {};\n      const f = {};\n      internalFilter(obj, pred, objAcc(t), objAcc(f));\n      return {\n        t,\n        f\n      };\n    };\n    const filter$1 = (obj, pred) => {\n      const t = {};\n      internalFilter(obj, pred, objAcc(t), noop);\n      return t;\n    };\n    const mapToArray = (obj, f) => {\n      const r = [];\n      each(obj, (value, name) => {\n        r.push(f(value, name));\n      });\n      return r;\n    };\n    const find$4 = (obj, pred) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        if (pred(x, i, obj)) {\n          return Optional.some(x);\n        }\n      }\n      return Optional.none();\n    };\n    const values = obj => {\n      return mapToArray(obj, identity);\n    };\n    const get$g = (obj, key) => {\n      return has$2(obj, key) ? Optional.from(obj[key]) : Optional.none();\n    };\n    const has$2 = (obj, key) => hasOwnProperty$1.call(obj, key);\n    const hasNonNullableKey = (obj, key) => has$2(obj, key) && obj[key] !== undefined && obj[key] !== null;\n\n    const is$1 = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));\n    const equals = (lhs, rhs, comparator = tripleEquals) => lift2(lhs, rhs, comparator).getOr(lhs.isNone() && rhs.isNone());\n    const cat = arr => {\n      const r = [];\n      const push = x => {\n        r.push(x);\n      };\n      for (let i = 0; i < arr.length; i++) {\n        arr[i].each(push);\n      }\n      return r;\n    };\n    const sequence = arr => {\n      const r = [];\n      for (let i = 0; i < arr.length; i++) {\n        const x = arr[i];\n        if (x.isSome()) {\n          r.push(x.getOrDie());\n        } else {\n          return Optional.none();\n        }\n      }\n      return Optional.some(r);\n    };\n    const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();\n    const lift3 = (oa, ob, oc, f) => oa.isSome() && ob.isSome() && oc.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie(), oc.getOrDie())) : Optional.none();\n    const mapFrom = (a, f) => a !== undefined && a !== null ? Optional.some(f(a)) : Optional.none();\n    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();\n\n    const addToEnd = (str, suffix) => {\n      return str + suffix;\n    };\n    const removeFromStart = (str, numChars) => {\n      return str.substring(numChars);\n    };\n\n    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;\n    const removeLeading = (str, prefix) => {\n      return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;\n    };\n    const ensureTrailing = (str, suffix) => {\n      return endsWith(str, suffix) ? str : addToEnd(str, suffix);\n    };\n    const contains$1 = (str, substr, start = 0, end) => {\n      const idx = str.indexOf(substr, start);\n      if (idx !== -1) {\n        return isUndefined(end) ? true : idx + substr.length <= end;\n      } else {\n        return false;\n      }\n    };\n    const startsWith = (str, prefix) => {\n      return checkRange(str, prefix, 0);\n    };\n    const endsWith = (str, suffix) => {\n      return checkRange(str, suffix, str.length - suffix.length);\n    };\n    const blank = r => s => s.replace(r, '');\n    const trim$1 = blank(/^\\s+|\\s+$/g);\n    const isNotEmpty = s => s.length > 0;\n    const isEmpty = s => !isNotEmpty(s);\n\n    const isSupported$1 = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);\n\n    const fromHtml$2 = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom(node);\n    };\n    const fromDom = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);\n    const SugarElement = {\n      fromHtml: fromHtml$2,\n      fromTag,\n      fromText,\n      fromDom,\n      fromPoint\n    };\n\n    const Global = typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const path$1 = (parts, scope) => {\n      let o = scope !== undefined && scope !== null ? scope : Global;\n      for (let i = 0; i < parts.length && o !== undefined && o !== null; ++i) {\n        o = o[parts[i]];\n      }\n      return o;\n    };\n    const resolve = (p, scope) => {\n      const parts = p.split('.');\n      return path$1(parts, scope);\n    };\n\n    const unsafe = (name, scope) => {\n      return resolve(name, scope);\n    };\n    const getOrDie$1 = (name, scope) => {\n      const actual = unsafe(name, scope);\n      if (actual === undefined || actual === null) {\n        throw new Error(name + ' not available on this browser');\n      }\n      return actual;\n    };\n\n    const getPrototypeOf$1 = Object.getPrototypeOf;\n    const sandHTMLElement = scope => {\n      return getOrDie$1('HTMLElement', scope);\n    };\n    const isPrototypeOf = x => {\n      const scope = resolve('ownerDocument.defaultView', x);\n      return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\\w*Element$/.test(getPrototypeOf$1(x).constructor.name));\n    };\n\n    const DOCUMENT = 9;\n    const DOCUMENT_FRAGMENT = 11;\n    const ELEMENT = 1;\n    const TEXT = 3;\n\n    const name$3 = element => {\n      const r = element.dom.nodeName;\n      return r.toLowerCase();\n    };\n    const type$1 = element => element.dom.nodeType;\n    const isType = t => element => type$1(element) === t;\n    const isHTMLElement = element => isElement$1(element) && isPrototypeOf(element.dom);\n    const isElement$1 = isType(ELEMENT);\n    const isText = isType(TEXT);\n    const isDocument = isType(DOCUMENT);\n    const isDocumentFragment = isType(DOCUMENT_FRAGMENT);\n    const isTag = tag => e => isElement$1(e) && name$3(e) === tag;\n\n    const is = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;\n    const all$3 = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? [] : map$2(base.querySelectorAll(selector), SugarElement.fromDom);\n    };\n    const one = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);\n    };\n\n    const eq = (e1, e2) => e1.dom === e2.dom;\n    const contains = (e1, e2) => {\n      const d1 = e1.dom;\n      const d2 = e2.dom;\n      return d1 === d2 ? false : d1.contains(d2);\n    };\n\n    const owner$4 = element => SugarElement.fromDom(element.dom.ownerDocument);\n    const documentOrOwner = dos => isDocument(dos) ? dos : owner$4(dos);\n    const documentElement = element => SugarElement.fromDom(documentOrOwner(element).dom.documentElement);\n    const defaultView = element => SugarElement.fromDom(documentOrOwner(element).dom.defaultView);\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);\n    const offsetParent = element => Optional.from(element.dom.offsetParent).map(SugarElement.fromDom);\n    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);\n    const children = element => map$2(element.dom.childNodes, SugarElement.fromDom);\n    const child$2 = (element, index) => {\n      const cs = element.dom.childNodes;\n      return Optional.from(cs[index]).map(SugarElement.fromDom);\n    };\n    const firstChild = element => child$2(element, 0);\n    const spot = (element, offset) => ({\n      element,\n      offset\n    });\n    const leaf = (element, offset) => {\n      const cs = children(element);\n      return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset);\n    };\n\n    const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);\n    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);\n    const isSupported = constant$1(supported);\n    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;\n    const getContentContainer = dos => isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body);\n    const isInShadowRoot = e => getShadowRoot(e).isSome();\n    const getShadowRoot = e => {\n      const r = getRootNode(e);\n      return isShadowRoot(r) ? Optional.some(r) : Optional.none();\n    };\n    const getShadowHost = e => SugarElement.fromDom(e.dom.host);\n    const getOriginalEventTarget = event => {\n      if (isSupported() && isNonNullable(event.target)) {\n        const el = SugarElement.fromDom(event.target);\n        if (isElement$1(el) && isOpenShadowHost(el)) {\n          if (event.composed && event.composedPath) {\n            const composedPath = event.composedPath();\n            if (composedPath) {\n              return head(composedPath);\n            }\n          }\n        }\n      }\n      return Optional.from(event.target);\n    };\n    const isOpenShadowHost = element => isNonNullable(element.dom.shadowRoot);\n\n    const inBody = element => {\n      const dom = isText(element) ? element.dom.parentNode : element.dom;\n      if (dom === undefined || dom === null || dom.ownerDocument === null) {\n        return false;\n      }\n      const doc = dom.ownerDocument;\n      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));\n    };\n    const body = () => getBody(SugarElement.fromDom(document));\n    const getBody = doc => {\n      const b = doc.dom.body;\n      if (b === null || b === undefined) {\n        throw new Error('Body is not available yet');\n      }\n      return SugarElement.fromDom(b);\n    };\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set$9 = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const setAll$1 = (element, attrs) => {\n      const dom = element.dom;\n      each(attrs, (v, k) => {\n        rawSet(dom, k, v);\n      });\n    };\n    const get$f = (element, key) => {\n      const v = element.dom.getAttribute(key);\n      return v === null ? undefined : v;\n    };\n    const getOpt = (element, key) => Optional.from(get$f(element, key));\n    const has$1 = (element, key) => {\n      const dom = element.dom;\n      return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;\n    };\n    const remove$7 = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n    const clone$2 = element => foldl(element.dom.attributes, (acc, attr) => {\n      acc[attr.name] = attr.value;\n      return acc;\n    }, {});\n\n    const internalSet = (dom, property, value) => {\n      if (!isString(value)) {\n        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);\n        throw new Error('CSS value must be a string: ' + value);\n      }\n      if (isSupported$1(dom)) {\n        dom.style.setProperty(property, value);\n      }\n    };\n    const internalRemove = (dom, property) => {\n      if (isSupported$1(dom)) {\n        dom.style.removeProperty(property);\n      }\n    };\n    const set$8 = (element, property, value) => {\n      const dom = element.dom;\n      internalSet(dom, property, value);\n    };\n    const setAll = (element, css) => {\n      const dom = element.dom;\n      each(css, (v, k) => {\n        internalSet(dom, k, v);\n      });\n    };\n    const setOptions = (element, css) => {\n      const dom = element.dom;\n      each(css, (v, k) => {\n        v.fold(() => {\n          internalRemove(dom, k);\n        }, value => {\n          internalSet(dom, k, value);\n        });\n      });\n    };\n    const get$e = (element, property) => {\n      const dom = element.dom;\n      const styles = window.getComputedStyle(dom);\n      const r = styles.getPropertyValue(property);\n      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;\n    };\n    const getUnsafeProperty = (dom, property) => isSupported$1(dom) ? dom.style.getPropertyValue(property) : '';\n    const getRaw = (element, property) => {\n      const dom = element.dom;\n      const raw = getUnsafeProperty(dom, property);\n      return Optional.from(raw).filter(r => r.length > 0);\n    };\n    const getAllRaw = element => {\n      const css = {};\n      const dom = element.dom;\n      if (isSupported$1(dom)) {\n        for (let i = 0; i < dom.style.length; i++) {\n          const ruleName = dom.style.item(i);\n          css[ruleName] = dom.style[ruleName];\n        }\n      }\n      return css;\n    };\n    const isValidValue = (tag, property, value) => {\n      const element = SugarElement.fromTag(tag);\n      set$8(element, property, value);\n      const style = getRaw(element, property);\n      return style.isSome();\n    };\n    const remove$6 = (element, property) => {\n      const dom = element.dom;\n      internalRemove(dom, property);\n      if (is$1(getOpt(element, 'style').map(trim$1), '')) {\n        remove$7(element, 'style');\n      }\n    };\n    const reflow = e => e.dom.offsetWidth;\n\n    const Dimension = (name, getOffset) => {\n      const set = (element, h) => {\n        if (!isNumber(h) && !h.match(/^[0-9]+$/)) {\n          throw new Error(name + '.set accepts only positive integer values. Value was ' + h);\n        }\n        const dom = element.dom;\n        if (isSupported$1(dom)) {\n          dom.style[name] = h + 'px';\n        }\n      };\n      const get = element => {\n        const r = getOffset(element);\n        if (r <= 0 || r === null) {\n          const css = get$e(element, name);\n          return parseFloat(css) || 0;\n        }\n        return r;\n      };\n      const getOuter = get;\n      const aggregate = (element, properties) => foldl(properties, (acc, property) => {\n        const val = get$e(element, property);\n        const value = val === undefined ? 0 : parseInt(val, 10);\n        return isNaN(value) ? acc : acc + value;\n      }, 0);\n      const max = (element, value, properties) => {\n        const cumulativeInclusions = aggregate(element, properties);\n        const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;\n        return absoluteMax;\n      };\n      return {\n        set,\n        get,\n        getOuter,\n        aggregate,\n        max\n      };\n    };\n\n    const api$2 = Dimension('height', element => {\n      const dom = element.dom;\n      return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;\n    });\n    const get$d = element => api$2.get(element);\n    const getOuter$2 = element => api$2.getOuter(element);\n    const setMax$1 = (element, value) => {\n      const inclusions = [\n        'margin-top',\n        'border-top-width',\n        'padding-top',\n        'padding-bottom',\n        'border-bottom-width',\n        'margin-bottom'\n      ];\n      const absMax = api$2.max(element, value, inclusions);\n      set$8(element, 'max-height', absMax + 'px');\n    };\n\n    const r$1 = (left, top) => {\n      const translate = (x, y) => r$1(left + x, top + y);\n      return {\n        left,\n        top,\n        translate\n      };\n    };\n    const SugarPosition = r$1;\n\n    const boxPosition = dom => {\n      const box = dom.getBoundingClientRect();\n      return SugarPosition(box.left, box.top);\n    };\n    const firstDefinedOrZero = (a, b) => {\n      if (a !== undefined) {\n        return a;\n      } else {\n        return b !== undefined ? b : 0;\n      }\n    };\n    const absolute$3 = element => {\n      const doc = element.dom.ownerDocument;\n      const body = doc.body;\n      const win = doc.defaultView;\n      const html = doc.documentElement;\n      if (body === element.dom) {\n        return SugarPosition(body.offsetLeft, body.offsetTop);\n      }\n      const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop);\n      const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft);\n      const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);\n      const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);\n      return viewport$1(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);\n    };\n    const viewport$1 = element => {\n      const dom = element.dom;\n      const doc = dom.ownerDocument;\n      const body = doc.body;\n      if (body === dom) {\n        return SugarPosition(body.offsetLeft, body.offsetTop);\n      }\n      if (!inBody(element)) {\n        return SugarPosition(0, 0);\n      }\n      return boxPosition(dom);\n    };\n\n    const api$1 = Dimension('width', element => element.dom.offsetWidth);\n    const set$7 = (element, h) => api$1.set(element, h);\n    const get$c = element => api$1.get(element);\n    const getOuter$1 = element => api$1.getOuter(element);\n    const setMax = (element, value) => {\n      const inclusions = [\n        'margin-left',\n        'border-left-width',\n        'padding-left',\n        'padding-right',\n        'border-right-width',\n        'margin-right'\n      ];\n      const absMax = api$1.max(element, value, inclusions);\n      set$8(element, 'max-width', absMax + 'px');\n    };\n\n    const cached = f => {\n      let called = false;\n      let r;\n      return (...args) => {\n        if (!called) {\n          called = true;\n          r = f.apply(null, args);\n        }\n        return r;\n      };\n    };\n\n    const DeviceType = (os, browser, userAgent, mediaMatch) => {\n      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;\n      const isiPhone = os.isiOS() && !isiPad;\n      const isMobile = os.isiOS() || os.isAndroid();\n      const isTouch = isMobile || mediaMatch('(pointer:coarse)');\n      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');\n      const isPhone = isiPhone || isMobile && !isTablet;\n      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;\n      const isDesktop = !isPhone && !isTablet && !iOSwebview;\n      return {\n        isiPad: constant$1(isiPad),\n        isiPhone: constant$1(isiPhone),\n        isTablet: constant$1(isTablet),\n        isPhone: constant$1(isPhone),\n        isTouch: constant$1(isTouch),\n        isAndroid: os.isAndroid,\n        isiOS: os.isiOS,\n        isWebView: constant$1(iOSwebview),\n        isDesktop: constant$1(isDesktop)\n      };\n    };\n\n    const firstMatch = (regexes, s) => {\n      for (let i = 0; i < regexes.length; i++) {\n        const x = regexes[i];\n        if (x.test(s)) {\n          return x;\n        }\n      }\n      return undefined;\n    };\n    const find$3 = (regexes, agent) => {\n      const r = firstMatch(regexes, agent);\n      if (!r) {\n        return {\n          major: 0,\n          minor: 0\n        };\n      }\n      const group = i => {\n        return Number(agent.replace(r, '$' + i));\n      };\n      return nu$d(group(1), group(2));\n    };\n    const detect$4 = (versionRegexes, agent) => {\n      const cleanedAgent = String(agent).toLowerCase();\n      if (versionRegexes.length === 0) {\n        return unknown$3();\n      }\n      return find$3(versionRegexes, cleanedAgent);\n    };\n    const unknown$3 = () => {\n      return nu$d(0, 0);\n    };\n    const nu$d = (major, minor) => {\n      return {\n        major,\n        minor\n      };\n    };\n    const Version = {\n      nu: nu$d,\n      detect: detect$4,\n      unknown: unknown$3\n    };\n\n    const detectBrowser$1 = (browsers, userAgentData) => {\n      return findMap(userAgentData.brands, uaBrand => {\n        const lcBrand = uaBrand.brand.toLowerCase();\n        return find$5(browsers, browser => {\n          var _a;\n          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());\n        }).map(info => ({\n          current: info.name,\n          version: Version.nu(parseInt(uaBrand.version, 10), 0)\n        }));\n      });\n    };\n\n    const detect$3 = (candidates, userAgent) => {\n      const agent = String(userAgent).toLowerCase();\n      return find$5(candidates, candidate => {\n        return candidate.search(agent);\n      });\n    };\n    const detectBrowser = (browsers, userAgent) => {\n      return detect$3(browsers, userAgent).map(browser => {\n        const version = Version.detect(browser.versionRegexes, userAgent);\n        return {\n          current: browser.name,\n          version\n        };\n      });\n    };\n    const detectOs = (oses, userAgent) => {\n      return detect$3(oses, userAgent).map(os => {\n        const version = Version.detect(os.versionRegexes, userAgent);\n        return {\n          current: os.name,\n          version\n        };\n      });\n    };\n\n    const normalVersionRegex = /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/;\n    const checkContains = target => {\n      return uastring => {\n        return contains$1(uastring, target);\n      };\n    };\n    const browsers = [\n      {\n        name: 'Edge',\n        versionRegexes: [/.*?edge\\/ ?([0-9]+)\\.([0-9]+)$/],\n        search: uastring => {\n          return contains$1(uastring, 'edge/') && contains$1(uastring, 'chrome') && contains$1(uastring, 'safari') && contains$1(uastring, 'applewebkit');\n        }\n      },\n      {\n        name: 'Chromium',\n        brand: 'Chromium',\n        versionRegexes: [\n          /.*?chrome\\/([0-9]+)\\.([0-9]+).*/,\n          normalVersionRegex\n        ],\n        search: uastring => {\n          return contains$1(uastring, 'chrome') && !contains$1(uastring, 'chromeframe');\n        }\n      },\n      {\n        name: 'IE',\n        versionRegexes: [\n          /.*?msie\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*?rv:([0-9]+)\\.([0-9]+).*/\n        ],\n        search: uastring => {\n          return contains$1(uastring, 'msie') || contains$1(uastring, 'trident');\n        }\n      },\n      {\n        name: 'Opera',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?opera\\/([0-9]+)\\.([0-9]+).*/\n        ],\n        search: checkContains('opera')\n      },\n      {\n        name: 'Firefox',\n        versionRegexes: [/.*?firefox\\/\\ ?([0-9]+)\\.([0-9]+).*/],\n        search: checkContains('firefox')\n      },\n      {\n        name: 'Safari',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?cpu os ([0-9]+)_([0-9]+).*/\n        ],\n        search: uastring => {\n          return (contains$1(uastring, 'safari') || contains$1(uastring, 'mobile/')) && contains$1(uastring, 'applewebkit');\n        }\n      }\n    ];\n    const oses = [\n      {\n        name: 'Windows',\n        search: checkContains('win'),\n        versionRegexes: [/.*?windows\\ nt\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'iOS',\n        search: uastring => {\n          return contains$1(uastring, 'iphone') || contains$1(uastring, 'ipad');\n        },\n        versionRegexes: [\n          /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*cpu os ([0-9]+)_([0-9]+).*/,\n          /.*cpu iphone os ([0-9]+)_([0-9]+).*/\n        ]\n      },\n      {\n        name: 'Android',\n        search: checkContains('android'),\n        versionRegexes: [/.*?android\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'macOS',\n        search: checkContains('mac os x'),\n        versionRegexes: [/.*?mac\\ os\\ x\\ ?([0-9]+)_([0-9]+).*/]\n      },\n      {\n        name: 'Linux',\n        search: checkContains('linux'),\n        versionRegexes: []\n      },\n      {\n        name: 'Solaris',\n        search: checkContains('sunos'),\n        versionRegexes: []\n      },\n      {\n        name: 'FreeBSD',\n        search: checkContains('freebsd'),\n        versionRegexes: []\n      },\n      {\n        name: 'ChromeOS',\n        search: checkContains('cros'),\n        versionRegexes: [/.*?chrome\\/([0-9]+)\\.([0-9]+).*/]\n      }\n    ];\n    const PlatformInfo = {\n      browsers: constant$1(browsers),\n      oses: constant$1(oses)\n    };\n\n    const edge = 'Edge';\n    const chromium = 'Chromium';\n    const ie = 'IE';\n    const opera = 'Opera';\n    const firefox = 'Firefox';\n    const safari = 'Safari';\n    const unknown$2 = () => {\n      return nu$c({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu$c = info => {\n      const current = info.current;\n      const version = info.version;\n      const isBrowser = name => () => current === name;\n      return {\n        current,\n        version,\n        isEdge: isBrowser(edge),\n        isChromium: isBrowser(chromium),\n        isIE: isBrowser(ie),\n        isOpera: isBrowser(opera),\n        isFirefox: isBrowser(firefox),\n        isSafari: isBrowser(safari)\n      };\n    };\n    const Browser = {\n      unknown: unknown$2,\n      nu: nu$c,\n      edge: constant$1(edge),\n      chromium: constant$1(chromium),\n      ie: constant$1(ie),\n      opera: constant$1(opera),\n      firefox: constant$1(firefox),\n      safari: constant$1(safari)\n    };\n\n    const windows = 'Windows';\n    const ios = 'iOS';\n    const android = 'Android';\n    const linux = 'Linux';\n    const macos = 'macOS';\n    const solaris = 'Solaris';\n    const freebsd = 'FreeBSD';\n    const chromeos = 'ChromeOS';\n    const unknown$1 = () => {\n      return nu$b({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu$b = info => {\n      const current = info.current;\n      const version = info.version;\n      const isOS = name => () => current === name;\n      return {\n        current,\n        version,\n        isWindows: isOS(windows),\n        isiOS: isOS(ios),\n        isAndroid: isOS(android),\n        isMacOS: isOS(macos),\n        isLinux: isOS(linux),\n        isSolaris: isOS(solaris),\n        isFreeBSD: isOS(freebsd),\n        isChromeOS: isOS(chromeos)\n      };\n    };\n    const OperatingSystem = {\n      unknown: unknown$1,\n      nu: nu$b,\n      windows: constant$1(windows),\n      ios: constant$1(ios),\n      android: constant$1(android),\n      linux: constant$1(linux),\n      macos: constant$1(macos),\n      solaris: constant$1(solaris),\n      freebsd: constant$1(freebsd),\n      chromeos: constant$1(chromeos)\n    };\n\n    const detect$2 = (userAgent, userAgentDataOpt, mediaMatch) => {\n      const browsers = PlatformInfo.browsers();\n      const oses = PlatformInfo.oses();\n      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);\n      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);\n      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);\n      return {\n        browser,\n        os,\n        deviceType\n      };\n    };\n    const PlatformDetection = { detect: detect$2 };\n\n    const mediaMatch = query => window.matchMedia(query).matches;\n    let platform = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));\n    const detect$1 = () => platform();\n\n    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({\n      target,\n      x,\n      y,\n      stop,\n      prevent,\n      kill,\n      raw\n    });\n    const fromRawEvent$1 = rawEvent => {\n      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));\n      const stop = () => rawEvent.stopPropagation();\n      const prevent = () => rawEvent.preventDefault();\n      const kill = compose(prevent, stop);\n      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);\n    };\n    const handle = (filter, handler) => rawEvent => {\n      if (filter(rawEvent)) {\n        handler(fromRawEvent$1(rawEvent));\n      }\n    };\n    const binder = (element, event, filter, handler, useCapture) => {\n      const wrapped = handle(filter, handler);\n      element.dom.addEventListener(event, wrapped, useCapture);\n      return { unbind: curry(unbind, element, event, wrapped, useCapture) };\n    };\n    const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false);\n    const capture$1 = (element, event, filter, handler) => binder(element, event, filter, handler, true);\n    const unbind = (element, event, handler, useCapture) => {\n      element.dom.removeEventListener(event, handler, useCapture);\n    };\n\n    const before$1 = (marker, element) => {\n      const parent$1 = parent(marker);\n      parent$1.each(v => {\n        v.dom.insertBefore(element.dom, marker.dom);\n      });\n    };\n    const after$2 = (marker, element) => {\n      const sibling = nextSibling(marker);\n      sibling.fold(() => {\n        const parent$1 = parent(marker);\n        parent$1.each(v => {\n          append$2(v, element);\n        });\n      }, v => {\n        before$1(v, element);\n      });\n    };\n    const prepend$1 = (parent, element) => {\n      const firstChild$1 = firstChild(parent);\n      firstChild$1.fold(() => {\n        append$2(parent, element);\n      }, v => {\n        parent.dom.insertBefore(element.dom, v.dom);\n      });\n    };\n    const append$2 = (parent, element) => {\n      parent.dom.appendChild(element.dom);\n    };\n    const appendAt = (parent, element, index) => {\n      child$2(parent, index).fold(() => {\n        append$2(parent, element);\n      }, v => {\n        before$1(v, element);\n      });\n    };\n\n    const append$1 = (parent, elements) => {\n      each$1(elements, x => {\n        append$2(parent, x);\n      });\n    };\n\n    const empty = element => {\n      element.dom.textContent = '';\n      each$1(children(element), rogue => {\n        remove$5(rogue);\n      });\n    };\n    const remove$5 = element => {\n      const dom = element.dom;\n      if (dom.parentNode !== null) {\n        dom.parentNode.removeChild(dom);\n      }\n    };\n\n    const get$b = _DOC => {\n      const doc = _DOC !== undefined ? _DOC.dom : document;\n      const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;\n      const y = doc.body.scrollTop || doc.documentElement.scrollTop;\n      return SugarPosition(x, y);\n    };\n    const to = (x, y, _DOC) => {\n      const doc = _DOC !== undefined ? _DOC.dom : document;\n      const win = doc.defaultView;\n      if (win) {\n        win.scrollTo(x, y);\n      }\n    };\n\n    const get$a = _win => {\n      const win = _win === undefined ? window : _win;\n      if (detect$1().browser.isFirefox()) {\n        return Optional.none();\n      } else {\n        return Optional.from(win.visualViewport);\n      }\n    };\n    const bounds$1 = (x, y, width, height) => ({\n      x,\n      y,\n      width,\n      height,\n      right: x + width,\n      bottom: y + height\n    });\n    const getBounds$3 = _win => {\n      const win = _win === undefined ? window : _win;\n      const doc = win.document;\n      const scroll = get$b(SugarElement.fromDom(doc));\n      return get$a(win).fold(() => {\n        const html = win.document.documentElement;\n        const width = html.clientWidth;\n        const height = html.clientHeight;\n        return bounds$1(scroll.left, scroll.top, width, height);\n      }, visualViewport => bounds$1(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height));\n    };\n\n    const getDocument = () => SugarElement.fromDom(document);\n\n    const walkUp = (navigation, doc) => {\n      const frame = navigation.view(doc);\n      return frame.fold(constant$1([]), f => {\n        const parent = navigation.owner(f);\n        const rest = walkUp(navigation, parent);\n        return [f].concat(rest);\n      });\n    };\n    const pathTo = (element, navigation) => {\n      const d = navigation.owner(element);\n      const paths = walkUp(navigation, d);\n      return Optional.some(paths);\n    };\n\n    const view = doc => {\n      var _a;\n      const element = doc.dom === document ? Optional.none() : Optional.from((_a = doc.dom.defaultView) === null || _a === void 0 ? void 0 : _a.frameElement);\n      return element.map(SugarElement.fromDom);\n    };\n    const owner$3 = element => owner$4(element);\n\n    var Navigation = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        view: view,\n        owner: owner$3\n    });\n\n    const find$2 = element => {\n      const doc = getDocument();\n      const scroll = get$b(doc);\n      const path = pathTo(element, Navigation);\n      return path.fold(curry(absolute$3, element), frames => {\n        const offset = viewport$1(element);\n        const r = foldr(frames, (b, a) => {\n          const loc = viewport$1(a);\n          return {\n            left: b.left + loc.left,\n            top: b.top + loc.top\n          };\n        }, {\n          left: 0,\n          top: 0\n        });\n        return SugarPosition(r.left + offset.left + scroll.left, r.top + offset.top + scroll.top);\n      });\n    };\n\n    const pointed = (point, width, height) => ({\n      point,\n      width,\n      height\n    });\n    const rect = (x, y, width, height) => ({\n      x,\n      y,\n      width,\n      height\n    });\n    const bounds = (x, y, width, height) => ({\n      x,\n      y,\n      width,\n      height,\n      right: x + width,\n      bottom: y + height\n    });\n    const box$1 = element => {\n      const xy = absolute$3(element);\n      const w = getOuter$1(element);\n      const h = getOuter$2(element);\n      return bounds(xy.left, xy.top, w, h);\n    };\n    const absolute$2 = element => {\n      const position = find$2(element);\n      const width = getOuter$1(element);\n      const height = getOuter$2(element);\n      return bounds(position.left, position.top, width, height);\n    };\n    const win = () => getBounds$3(window);\n\n    const value$4 = value => {\n      const applyHelper = fn => fn(value);\n      const constHelper = constant$1(value);\n      const outputHelper = () => output;\n      const output = {\n        tag: true,\n        inner: value,\n        fold: (_onError, onValue) => onValue(value),\n        isValue: always,\n        isError: never,\n        map: mapper => Result.value(mapper(value)),\n        mapError: outputHelper,\n        bind: applyHelper,\n        exists: applyHelper,\n        forall: applyHelper,\n        getOr: constHelper,\n        or: outputHelper,\n        getOrThunk: constHelper,\n        orThunk: outputHelper,\n        getOrDie: constHelper,\n        each: fn => {\n          fn(value);\n        },\n        toOptional: () => Optional.some(value)\n      };\n      return output;\n    };\n    const error$1 = error => {\n      const outputHelper = () => output;\n      const output = {\n        tag: false,\n        inner: error,\n        fold: (onError, _onValue) => onError(error),\n        isValue: never,\n        isError: always,\n        map: outputHelper,\n        mapError: mapper => Result.error(mapper(error)),\n        bind: outputHelper,\n        exists: never,\n        forall: always,\n        getOr: identity,\n        or: identity,\n        getOrThunk: apply$1,\n        orThunk: apply$1,\n        getOrDie: die(String(error)),\n        each: noop,\n        toOptional: Optional.none\n      };\n      return output;\n    };\n    const fromOption = (optional, err) => optional.fold(() => error$1(err), value$4);\n    const Result = {\n      value: value$4,\n      error: error$1,\n      fromOption\n    };\n\n    var SimpleResultType;\n    (function (SimpleResultType) {\n      SimpleResultType[SimpleResultType['Error'] = 0] = 'Error';\n      SimpleResultType[SimpleResultType['Value'] = 1] = 'Value';\n    }(SimpleResultType || (SimpleResultType = {})));\n    const fold$1 = (res, onError, onValue) => res.stype === SimpleResultType.Error ? onError(res.serror) : onValue(res.svalue);\n    const partition$2 = results => {\n      const values = [];\n      const errors = [];\n      each$1(results, obj => {\n        fold$1(obj, err => errors.push(err), val => values.push(val));\n      });\n      return {\n        values,\n        errors\n      };\n    };\n    const mapError = (res, f) => {\n      if (res.stype === SimpleResultType.Error) {\n        return {\n          stype: SimpleResultType.Error,\n          serror: f(res.serror)\n        };\n      } else {\n        return res;\n      }\n    };\n    const map = (res, f) => {\n      if (res.stype === SimpleResultType.Value) {\n        return {\n          stype: SimpleResultType.Value,\n          svalue: f(res.svalue)\n        };\n      } else {\n        return res;\n      }\n    };\n    const bind$1 = (res, f) => {\n      if (res.stype === SimpleResultType.Value) {\n        return f(res.svalue);\n      } else {\n        return res;\n      }\n    };\n    const bindError = (res, f) => {\n      if (res.stype === SimpleResultType.Error) {\n        return f(res.serror);\n      } else {\n        return res;\n      }\n    };\n    const svalue = v => ({\n      stype: SimpleResultType.Value,\n      svalue: v\n    });\n    const serror = e => ({\n      stype: SimpleResultType.Error,\n      serror: e\n    });\n    const toResult$1 = res => fold$1(res, Result.error, Result.value);\n    const fromResult$1 = res => res.fold(serror, svalue);\n    const SimpleResult = {\n      fromResult: fromResult$1,\n      toResult: toResult$1,\n      svalue,\n      partition: partition$2,\n      serror,\n      bind: bind$1,\n      bindError,\n      map,\n      mapError,\n      fold: fold$1\n    };\n\n    const field$2 = (key, newKey, presence, prop) => ({\n      tag: 'field',\n      key,\n      newKey,\n      presence,\n      prop\n    });\n    const customField$1 = (newKey, instantiator) => ({\n      tag: 'custom',\n      newKey,\n      instantiator\n    });\n    const fold = (value, ifField, ifCustom) => {\n      switch (value.tag) {\n      case 'field':\n        return ifField(value.key, value.newKey, value.presence, value.prop);\n      case 'custom':\n        return ifCustom(value.newKey, value.instantiator);\n      }\n    };\n\n    const shallow$1 = (old, nu) => {\n      return nu;\n    };\n    const deep = (old, nu) => {\n      const bothObjects = isPlainObject(old) && isPlainObject(nu);\n      return bothObjects ? deepMerge(old, nu) : nu;\n    };\n    const baseMerge = merger => {\n      return (...objects) => {\n        if (objects.length === 0) {\n          throw new Error(`Can't merge zero objects`);\n        }\n        const ret = {};\n        for (let j = 0; j < objects.length; j++) {\n          const curObject = objects[j];\n          for (const key in curObject) {\n            if (has$2(curObject, key)) {\n              ret[key] = merger(ret[key], curObject[key]);\n            }\n          }\n        }\n        return ret;\n      };\n    };\n    const deepMerge = baseMerge(deep);\n    const merge$1 = baseMerge(shallow$1);\n\n    const required$2 = () => ({\n      tag: 'required',\n      process: {}\n    });\n    const defaultedThunk = fallbackThunk => ({\n      tag: 'defaultedThunk',\n      process: fallbackThunk\n    });\n    const defaulted$1 = fallback => defaultedThunk(constant$1(fallback));\n    const asOption = () => ({\n      tag: 'option',\n      process: {}\n    });\n    const mergeWithThunk = baseThunk => ({\n      tag: 'mergeWithThunk',\n      process: baseThunk\n    });\n    const mergeWith = base => mergeWithThunk(constant$1(base));\n\n    const mergeValues$1 = (values, base) => values.length > 0 ? SimpleResult.svalue(deepMerge(base, merge$1.apply(undefined, values))) : SimpleResult.svalue(base);\n    const mergeErrors$1 = errors => compose(SimpleResult.serror, flatten)(errors);\n    const consolidateObj = (objects, base) => {\n      const partition = SimpleResult.partition(objects);\n      return partition.errors.length > 0 ? mergeErrors$1(partition.errors) : mergeValues$1(partition.values, base);\n    };\n    const consolidateArr = objects => {\n      const partitions = SimpleResult.partition(objects);\n      return partitions.errors.length > 0 ? mergeErrors$1(partitions.errors) : SimpleResult.svalue(partitions.values);\n    };\n    const ResultCombine = {\n      consolidateObj,\n      consolidateArr\n    };\n\n    const formatObj = input => {\n      return isObject(input) && keys(input).length > 100 ? ' removed due to size' : JSON.stringify(input, null, 2);\n    };\n    const formatErrors = errors => {\n      const es = errors.length > 10 ? errors.slice(0, 10).concat([{\n          path: [],\n          getErrorInfo: constant$1('... (only showing first ten failures)')\n        }]) : errors;\n      return map$2(es, e => {\n        return 'Failed path: (' + e.path.join(' > ') + ')\\n' + e.getErrorInfo();\n      });\n    };\n\n    const nu$a = (path, getErrorInfo) => {\n      return SimpleResult.serror([{\n          path,\n          getErrorInfo\n        }]);\n    };\n    const missingRequired = (path, key, obj) => nu$a(path, () => 'Could not find valid *required* value for \"' + key + '\" in ' + formatObj(obj));\n    const missingKey = (path, key) => nu$a(path, () => 'Choice schema did not contain choice key: \"' + key + '\"');\n    const missingBranch = (path, branches, branch) => nu$a(path, () => 'The chosen schema: \"' + branch + '\" did not exist in branches: ' + formatObj(branches));\n    const unsupportedFields = (path, unsupported) => nu$a(path, () => 'There are unsupported fields: [' + unsupported.join(', ') + '] specified');\n    const custom = (path, err) => nu$a(path, constant$1(err));\n\n    const value$3 = validator => {\n      const extract = (path, val) => {\n        return SimpleResult.bindError(validator(val), err => custom(path, err));\n      };\n      const toString = constant$1('val');\n      return {\n        extract,\n        toString\n      };\n    };\n    const anyValue$1 = value$3(SimpleResult.svalue);\n\n    const requiredAccess = (path, obj, key, bundle) => get$g(obj, key).fold(() => missingRequired(path, key, obj), bundle);\n    const fallbackAccess = (obj, key, fallback, bundle) => {\n      const v = get$g(obj, key).getOrThunk(() => fallback(obj));\n      return bundle(v);\n    };\n    const optionAccess = (obj, key, bundle) => bundle(get$g(obj, key));\n    const optionDefaultedAccess = (obj, key, fallback, bundle) => {\n      const opt = get$g(obj, key).map(val => val === true ? fallback(obj) : val);\n      return bundle(opt);\n    };\n    const extractField = (field, path, obj, key, prop) => {\n      const bundle = av => prop.extract(path.concat([key]), av);\n      const bundleAsOption = optValue => optValue.fold(() => SimpleResult.svalue(Optional.none()), ov => {\n        const result = prop.extract(path.concat([key]), ov);\n        return SimpleResult.map(result, Optional.some);\n      });\n      switch (field.tag) {\n      case 'required':\n        return requiredAccess(path, obj, key, bundle);\n      case 'defaultedThunk':\n        return fallbackAccess(obj, key, field.process, bundle);\n      case 'option':\n        return optionAccess(obj, key, bundleAsOption);\n      case 'defaultedOptionThunk':\n        return optionDefaultedAccess(obj, key, field.process, bundleAsOption);\n      case 'mergeWithThunk': {\n          return fallbackAccess(obj, key, constant$1({}), v => {\n            const result = deepMerge(field.process(obj), v);\n            return bundle(result);\n          });\n        }\n      }\n    };\n    const extractFields = (path, obj, fields) => {\n      const success = {};\n      const errors = [];\n      for (const field of fields) {\n        fold(field, (key, newKey, presence, prop) => {\n          const result = extractField(presence, path, obj, key, prop);\n          SimpleResult.fold(result, err => {\n            errors.push(...err);\n          }, res => {\n            success[newKey] = res;\n          });\n        }, (newKey, instantiator) => {\n          success[newKey] = instantiator(obj);\n        });\n      }\n      return errors.length > 0 ? SimpleResult.serror(errors) : SimpleResult.svalue(success);\n    };\n    const valueThunk = getDelegate => {\n      const extract = (path, val) => getDelegate().extract(path, val);\n      const toString = () => getDelegate().toString();\n      return {\n        extract,\n        toString\n      };\n    };\n    const getSetKeys = obj => keys(filter$1(obj, isNonNullable));\n    const objOfOnly = fields => {\n      const delegate = objOf(fields);\n      const fieldNames = foldr(fields, (acc, value) => {\n        return fold(value, key => deepMerge(acc, { [key]: true }), constant$1(acc));\n      }, {});\n      const extract = (path, o) => {\n        const keys = isBoolean(o) ? [] : getSetKeys(o);\n        const extra = filter$2(keys, k => !hasNonNullableKey(fieldNames, k));\n        return extra.length === 0 ? delegate.extract(path, o) : unsupportedFields(path, extra);\n      };\n      return {\n        extract,\n        toString: delegate.toString\n      };\n    };\n    const objOf = values => {\n      const extract = (path, o) => extractFields(path, o, values);\n      const toString = () => {\n        const fieldStrings = map$2(values, value => fold(value, (key, _okey, _presence, prop) => key + ' -> ' + prop.toString(), (newKey, _instantiator) => 'state(' + newKey + ')'));\n        return 'obj{\\n' + fieldStrings.join('\\n') + '}';\n      };\n      return {\n        extract,\n        toString\n      };\n    };\n    const arrOf = prop => {\n      const extract = (path, array) => {\n        const results = map$2(array, (a, i) => prop.extract(path.concat(['[' + i + ']']), a));\n        return ResultCombine.consolidateArr(results);\n      };\n      const toString = () => 'array(' + prop.toString() + ')';\n      return {\n        extract,\n        toString\n      };\n    };\n    const oneOf = (props, rawF) => {\n      const f = rawF !== undefined ? rawF : identity;\n      const extract = (path, val) => {\n        const errors = [];\n        for (const prop of props) {\n          const res = prop.extract(path, val);\n          if (res.stype === SimpleResultType.Value) {\n            return {\n              stype: SimpleResultType.Value,\n              svalue: f(res.svalue)\n            };\n          }\n          errors.push(res);\n        }\n        return ResultCombine.consolidateArr(errors);\n      };\n      const toString = () => 'oneOf(' + map$2(props, prop => prop.toString()).join(', ') + ')';\n      return {\n        extract,\n        toString\n      };\n    };\n    const setOf$1 = (validator, prop) => {\n      const validateKeys = (path, keys) => arrOf(value$3(validator)).extract(path, keys);\n      const extract = (path, o) => {\n        const keys$1 = keys(o);\n        const validatedKeys = validateKeys(path, keys$1);\n        return SimpleResult.bind(validatedKeys, validKeys => {\n          const schema = map$2(validKeys, vk => {\n            return field$2(vk, vk, required$2(), prop);\n          });\n          return objOf(schema).extract(path, o);\n        });\n      };\n      const toString = () => 'setOf(' + prop.toString() + ')';\n      return {\n        extract,\n        toString\n      };\n    };\n    const thunk = (_desc, processor) => {\n      const getP = cached(processor);\n      const extract = (path, val) => getP().extract(path, val);\n      const toString = () => getP().toString();\n      return {\n        extract,\n        toString\n      };\n    };\n    const arrOfObj = compose(arrOf, objOf);\n\n    const anyValue = constant$1(anyValue$1);\n    const typedValue = (validator, expectedType) => value$3(a => {\n      const actualType = typeof a;\n      return validator(a) ? SimpleResult.svalue(a) : SimpleResult.serror(`Expected type: ${ expectedType } but got: ${ actualType }`);\n    });\n    const number = typedValue(isNumber, 'number');\n    const string = typedValue(isString, 'string');\n    const boolean = typedValue(isBoolean, 'boolean');\n    const functionProcessor = typedValue(isFunction, 'function');\n    const isPostMessageable = val => {\n      if (Object(val) !== val) {\n        return true;\n      }\n      switch ({}.toString.call(val).slice(8, -1)) {\n      case 'Boolean':\n      case 'Number':\n      case 'String':\n      case 'Date':\n      case 'RegExp':\n      case 'Blob':\n      case 'FileList':\n      case 'ImageData':\n      case 'ImageBitmap':\n      case 'ArrayBuffer':\n        return true;\n      case 'Array':\n      case 'Object':\n        return Object.keys(val).every(prop => isPostMessageable(val[prop]));\n      default:\n        return false;\n      }\n    };\n    const postMessageable = value$3(a => {\n      if (isPostMessageable(a)) {\n        return SimpleResult.svalue(a);\n      } else {\n        return SimpleResult.serror('Expected value to be acceptable for sending via postMessage');\n      }\n    });\n\n    const chooseFrom = (path, input, branches, ch) => {\n      const fields = get$g(branches, ch);\n      return fields.fold(() => missingBranch(path, branches, ch), vp => vp.extract(path.concat(['branch: ' + ch]), input));\n    };\n    const choose$2 = (key, branches) => {\n      const extract = (path, input) => {\n        const choice = get$g(input, key);\n        return choice.fold(() => missingKey(path, key), chosen => chooseFrom(path, input, branches, chosen));\n      };\n      const toString = () => 'chooseOn(' + key + '). Possible values: ' + keys(branches);\n      return {\n        extract,\n        toString\n      };\n    };\n\n    const arrOfVal = () => arrOf(anyValue$1);\n    const valueOf = validator => value$3(v => validator(v).fold(SimpleResult.serror, SimpleResult.svalue));\n    const setOf = (validator, prop) => setOf$1(v => SimpleResult.fromResult(validator(v)), prop);\n    const extractValue = (label, prop, obj) => {\n      const res = prop.extract([label], obj);\n      return SimpleResult.mapError(res, errs => ({\n        input: obj,\n        errors: errs\n      }));\n    };\n    const asRaw = (label, prop, obj) => SimpleResult.toResult(extractValue(label, prop, obj));\n    const getOrDie = extraction => {\n      return extraction.fold(errInfo => {\n        throw new Error(formatError(errInfo));\n      }, identity);\n    };\n    const asRawOrDie$1 = (label, prop, obj) => getOrDie(asRaw(label, prop, obj));\n    const formatError = errInfo => {\n      return 'Errors: \\n' + formatErrors(errInfo.errors).join('\\n') + '\\n\\nInput object: ' + formatObj(errInfo.input);\n    };\n    const choose$1 = (key, branches) => choose$2(key, map$1(branches, objOf));\n    const thunkOf = (desc, schema) => thunk(desc, schema);\n\n    const field$1 = field$2;\n    const customField = customField$1;\n    const validateEnum = values => valueOf(value => contains$2(values, value) ? Result.value(value) : Result.error(`Unsupported value: \"${ value }\", choose one of \"${ values.join(', ') }\".`));\n    const required$1 = key => field$1(key, key, required$2(), anyValue());\n    const requiredOf = (key, schema) => field$1(key, key, required$2(), schema);\n    const requiredNumber = key => requiredOf(key, number);\n    const requiredString = key => requiredOf(key, string);\n    const requiredStringEnum = (key, values) => field$1(key, key, required$2(), validateEnum(values));\n    const requiredBoolean = key => requiredOf(key, boolean);\n    const requiredFunction = key => requiredOf(key, functionProcessor);\n    const forbid = (key, message) => field$1(key, key, asOption(), value$3(_v => SimpleResult.serror('The field: ' + key + ' is forbidden. ' + message)));\n    const requiredObjOf = (key, objSchema) => field$1(key, key, required$2(), objOf(objSchema));\n    const requiredArrayOfObj = (key, objFields) => field$1(key, key, required$2(), arrOfObj(objFields));\n    const requiredArrayOf = (key, schema) => field$1(key, key, required$2(), arrOf(schema));\n    const option$3 = key => field$1(key, key, asOption(), anyValue());\n    const optionOf = (key, schema) => field$1(key, key, asOption(), schema);\n    const optionNumber = key => optionOf(key, number);\n    const optionString = key => optionOf(key, string);\n    const optionStringEnum = (key, values) => optionOf(key, validateEnum(values));\n    const optionFunction = key => optionOf(key, functionProcessor);\n    const optionArrayOf = (key, schema) => optionOf(key, arrOf(schema));\n    const optionObjOf = (key, objSchema) => optionOf(key, objOf(objSchema));\n    const optionObjOfOnly = (key, objSchema) => optionOf(key, objOfOnly(objSchema));\n    const defaulted = (key, fallback) => field$1(key, key, defaulted$1(fallback), anyValue());\n    const defaultedOf = (key, fallback, schema) => field$1(key, key, defaulted$1(fallback), schema);\n    const defaultedNumber = (key, fallback) => defaultedOf(key, fallback, number);\n    const defaultedString = (key, fallback) => defaultedOf(key, fallback, string);\n    const defaultedStringEnum = (key, fallback, values) => defaultedOf(key, fallback, validateEnum(values));\n    const defaultedBoolean = (key, fallback) => defaultedOf(key, fallback, boolean);\n    const defaultedFunction = (key, fallback) => defaultedOf(key, fallback, functionProcessor);\n    const defaultedPostMsg = (key, fallback) => defaultedOf(key, fallback, postMessageable);\n    const defaultedArrayOf = (key, fallback, schema) => defaultedOf(key, fallback, arrOf(schema));\n    const defaultedObjOf = (key, fallback, objSchema) => defaultedOf(key, fallback, objOf(objSchema));\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const generate$7 = cases => {\n      if (!isArray(cases)) {\n        throw new Error('cases must be an array');\n      }\n      if (cases.length === 0) {\n        throw new Error('there must be at least one case');\n      }\n      const constructors = [];\n      const adt = {};\n      each$1(cases, (acase, count) => {\n        const keys$1 = keys(acase);\n        if (keys$1.length !== 1) {\n          throw new Error('one and only one name per case');\n        }\n        const key = keys$1[0];\n        const value = acase[key];\n        if (adt[key] !== undefined) {\n          throw new Error('duplicate key detected:' + key);\n        } else if (key === 'cata') {\n          throw new Error('cannot have a case named cata (sorry)');\n        } else if (!isArray(value)) {\n          throw new Error('case arguments must be an array');\n        }\n        constructors.push(key);\n        adt[key] = (...args) => {\n          const argLength = args.length;\n          if (argLength !== value.length) {\n            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);\n          }\n          const match = branches => {\n            const branchKeys = keys(branches);\n            if (constructors.length !== branchKeys.length) {\n              throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\\nActual: ' + branchKeys.join(','));\n            }\n            const allReqd = forall(constructors, reqKey => {\n              return contains$2(branchKeys, reqKey);\n            });\n            if (!allReqd) {\n              throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\\nRequired: ' + constructors.join(', '));\n            }\n            return branches[key].apply(null, args);\n          };\n          return {\n            fold: (...foldArgs) => {\n              if (foldArgs.length !== cases.length) {\n                throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + foldArgs.length);\n              }\n              const target = foldArgs[count];\n              return target.apply(null, args);\n            },\n            match,\n            log: label => {\n              console.log(label, {\n                constructors,\n                constructor: key,\n                params: args\n              });\n            }\n          };\n        };\n      });\n      return adt;\n    };\n    const Adt = { generate: generate$7 };\n\n    Adt.generate([\n      {\n        bothErrors: [\n          'error1',\n          'error2'\n        ]\n      },\n      {\n        firstError: [\n          'error1',\n          'value2'\n        ]\n      },\n      {\n        secondError: [\n          'value1',\n          'error2'\n        ]\n      },\n      {\n        bothValues: [\n          'value1',\n          'value2'\n        ]\n      }\n    ]);\n    const partition$1 = results => {\n      const errors = [];\n      const values = [];\n      each$1(results, result => {\n        result.fold(err => {\n          errors.push(err);\n        }, value => {\n          values.push(value);\n        });\n      });\n      return {\n        errors,\n        values\n      };\n    };\n\n    const exclude$1 = (obj, fields) => {\n      const r = {};\n      each(obj, (v, k) => {\n        if (!contains$2(fields, k)) {\n          r[k] = v;\n        }\n      });\n      return r;\n    };\n\n    const wrap$2 = (key, value) => ({ [key]: value });\n    const wrapAll$1 = keyvalues => {\n      const r = {};\n      each$1(keyvalues, kv => {\n        r[kv.key] = kv.value;\n      });\n      return r;\n    };\n\n    const exclude = (obj, fields) => exclude$1(obj, fields);\n    const wrap$1 = (key, value) => wrap$2(key, value);\n    const wrapAll = keyvalues => wrapAll$1(keyvalues);\n    const mergeValues = (values, base) => {\n      return values.length === 0 ? Result.value(base) : Result.value(deepMerge(base, merge$1.apply(undefined, values)));\n    };\n    const mergeErrors = errors => Result.error(flatten(errors));\n    const consolidate = (objs, base) => {\n      const partitions = partition$1(objs);\n      return partitions.errors.length > 0 ? mergeErrors(partitions.errors) : mergeValues(partitions.values, base);\n    };\n\n    const ensureIsRoot = isRoot => isFunction(isRoot) ? isRoot : never;\n    const ancestor$2 = (scope, transform, isRoot) => {\n      let element = scope.dom;\n      const stop = ensureIsRoot(isRoot);\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        const transformed = transform(el);\n        if (transformed.isSome()) {\n          return transformed;\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const closest$4 = (scope, transform, isRoot) => {\n      const current = transform(scope);\n      const stop = ensureIsRoot(isRoot);\n      return current.orThunk(() => stop(scope) ? Optional.none() : ancestor$2(scope, transform, stop));\n    };\n\n    const isSource = (component, simulatedEvent) => eq(component.element, simulatedEvent.event.target);\n\n    const defaultEventHandler = {\n      can: always,\n      abort: never,\n      run: noop\n    };\n    const nu$9 = parts => {\n      if (!hasNonNullableKey(parts, 'can') && !hasNonNullableKey(parts, 'abort') && !hasNonNullableKey(parts, 'run')) {\n        throw new Error('EventHandler defined by: ' + JSON.stringify(parts, null, 2) + ' does not have can, abort, or run!');\n      }\n      return {\n        ...defaultEventHandler,\n        ...parts\n      };\n    };\n    const all$2 = (handlers, f) => (...args) => foldl(handlers, (acc, handler) => acc && f(handler).apply(undefined, args), true);\n    const any = (handlers, f) => (...args) => foldl(handlers, (acc, handler) => acc || f(handler).apply(undefined, args), false);\n    const read$2 = handler => isFunction(handler) ? {\n      can: always,\n      abort: never,\n      run: handler\n    } : handler;\n    const fuse$1 = handlers => {\n      const can = all$2(handlers, handler => handler.can);\n      const abort = any(handlers, handler => handler.abort);\n      const run = (...args) => {\n        each$1(handlers, handler => {\n          handler.run.apply(undefined, args);\n        });\n      };\n      return {\n        can,\n        abort,\n        run\n      };\n    };\n\n    const constant = constant$1;\n    const touchstart = constant('touchstart');\n    const touchmove = constant('touchmove');\n    const touchend = constant('touchend');\n    const touchcancel = constant('touchcancel');\n    const mousedown = constant('mousedown');\n    const mousemove = constant('mousemove');\n    const mouseout = constant('mouseout');\n    const mouseup = constant('mouseup');\n    const mouseover = constant('mouseover');\n    const focusin = constant('focusin');\n    const focusout = constant('focusout');\n    const keydown = constant('keydown');\n    const keyup = constant('keyup');\n    const input = constant('input');\n    const change = constant('change');\n    const click = constant('click');\n    const transitioncancel = constant('transitioncancel');\n    const transitionend = constant('transitionend');\n    const transitionstart = constant('transitionstart');\n    const selectstart = constant('selectstart');\n\n    const prefixName = name => constant$1('alloy.' + name);\n    const alloy = { tap: prefixName('tap') };\n    const focus$4 = prefixName('focus');\n    const postBlur = prefixName('blur.post');\n    const postPaste = prefixName('paste.post');\n    const receive = prefixName('receive');\n    const execute$5 = prefixName('execute');\n    const focusItem = prefixName('focus.item');\n    const tap = alloy.tap;\n    const longpress = prefixName('longpress');\n    const sandboxClose = prefixName('sandbox.close');\n    const typeaheadCancel = prefixName('typeahead.cancel');\n    const systemInit = prefixName('system.init');\n    const documentTouchmove = prefixName('system.touchmove');\n    const documentTouchend = prefixName('system.touchend');\n    const windowScroll = prefixName('system.scroll');\n    const windowResize = prefixName('system.resize');\n    const attachedToDom = prefixName('system.attached');\n    const detachedFromDom = prefixName('system.detached');\n    const dismissRequested = prefixName('system.dismissRequested');\n    const repositionRequested = prefixName('system.repositionRequested');\n    const focusShifted = prefixName('focusmanager.shifted');\n    const slotVisibility = prefixName('slotcontainer.visibility');\n    const changeTab = prefixName('change.tab');\n    const dismissTab = prefixName('dismiss.tab');\n    const highlight$1 = prefixName('highlight');\n    const dehighlight$1 = prefixName('dehighlight');\n\n    const emit = (component, event) => {\n      dispatchWith(component, component.element, event, {});\n    };\n    const emitWith = (component, event, properties) => {\n      dispatchWith(component, component.element, event, properties);\n    };\n    const emitExecute = component => {\n      emit(component, execute$5());\n    };\n    const dispatch = (component, target, event) => {\n      dispatchWith(component, target, event, {});\n    };\n    const dispatchWith = (component, target, event, properties) => {\n      const data = {\n        target,\n        ...properties\n      };\n      component.getSystem().triggerEvent(event, target, data);\n    };\n    const retargetAndDispatchWith = (component, target, eventName, properties) => {\n      const data = {\n        ...properties,\n        target\n      };\n      component.getSystem().triggerEvent(eventName, target, data);\n    };\n    const dispatchEvent = (component, target, event, simulatedEvent) => {\n      component.getSystem().triggerEvent(event, target, simulatedEvent.event);\n    };\n\n    const derive$2 = configs => wrapAll(configs);\n    const abort = (name, predicate) => {\n      return {\n        key: name,\n        value: nu$9({ abort: predicate })\n      };\n    };\n    const can = (name, predicate) => {\n      return {\n        key: name,\n        value: nu$9({ can: predicate })\n      };\n    };\n    const preventDefault = name => {\n      return {\n        key: name,\n        value: nu$9({\n          run: (component, simulatedEvent) => {\n            simulatedEvent.event.prevent();\n          }\n        })\n      };\n    };\n    const run$1 = (name, handler) => {\n      return {\n        key: name,\n        value: nu$9({ run: handler })\n      };\n    };\n    const runActionExtra = (name, action, extra) => {\n      return {\n        key: name,\n        value: nu$9({\n          run: (component, simulatedEvent) => {\n            action.apply(undefined, [\n              component,\n              simulatedEvent\n            ].concat(extra));\n          }\n        })\n      };\n    };\n    const runOnName = name => {\n      return handler => run$1(name, handler);\n    };\n    const runOnSourceName = name => {\n      return handler => ({\n        key: name,\n        value: nu$9({\n          run: (component, simulatedEvent) => {\n            if (isSource(component, simulatedEvent)) {\n              handler(component, simulatedEvent);\n            }\n          }\n        })\n      });\n    };\n    const redirectToUid = (name, uid) => {\n      return run$1(name, (component, simulatedEvent) => {\n        component.getSystem().getByUid(uid).each(redirectee => {\n          dispatchEvent(redirectee, redirectee.element, name, simulatedEvent);\n        });\n      });\n    };\n    const redirectToPart = (name, detail, partName) => {\n      const uid = detail.partUids[partName];\n      return redirectToUid(name, uid);\n    };\n    const runWithTarget = (name, f) => {\n      return run$1(name, (component, simulatedEvent) => {\n        const ev = simulatedEvent.event;\n        const target = component.getSystem().getByDom(ev.target).getOrThunk(() => {\n          const closest = closest$4(ev.target, el => component.getSystem().getByDom(el).toOptional(), never);\n          return closest.getOr(component);\n        });\n        f(component, target, simulatedEvent);\n      });\n    };\n    const cutter = name => {\n      return run$1(name, (component, simulatedEvent) => {\n        simulatedEvent.cut();\n      });\n    };\n    const stopper = name => {\n      return run$1(name, (component, simulatedEvent) => {\n        simulatedEvent.stop();\n      });\n    };\n    const runOnSource = (name, f) => {\n      return runOnSourceName(name)(f);\n    };\n    const runOnAttached = runOnSourceName(attachedToDom());\n    const runOnDetached = runOnSourceName(detachedFromDom());\n    const runOnInit = runOnSourceName(systemInit());\n    const runOnExecute$1 = runOnName(execute$5());\n\n    const fromHtml$1 = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      return children(SugarElement.fromDom(div));\n    };\n\n    const get$9 = element => element.dom.innerHTML;\n    const set$6 = (element, content) => {\n      const owner = owner$4(element);\n      const docDom = owner.dom;\n      const fragment = SugarElement.fromDom(docDom.createDocumentFragment());\n      const contentElements = fromHtml$1(content, docDom);\n      append$1(fragment, contentElements);\n      empty(element);\n      append$2(element, fragment);\n    };\n    const getOuter = element => {\n      const container = SugarElement.fromTag('div');\n      const clone = SugarElement.fromDom(element.dom.cloneNode(true));\n      append$2(container, clone);\n      return get$9(container);\n    };\n\n    const clone$1 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));\n    const shallow = original => clone$1(original, false);\n\n    const getHtml = element => {\n      if (isShadowRoot(element)) {\n        return '#shadow-root';\n      } else {\n        const clone = shallow(element);\n        return getOuter(clone);\n      }\n    };\n\n    const element = elem => getHtml(elem);\n\n    const isRecursive = (component, originator, target) => eq(originator, component.element) && !eq(originator, target);\n    const events$i = derive$2([can(focus$4(), (component, simulatedEvent) => {\n        const event = simulatedEvent.event;\n        const originator = event.originator;\n        const target = event.target;\n        if (isRecursive(component, originator, target)) {\n          console.warn(focus$4() + ' did not get interpreted by the desired target. ' + '\\nOriginator: ' + element(originator) + '\\nTarget: ' + element(target) + '\\nCheck the ' + focus$4() + ' event handlers');\n          return false;\n        } else {\n          return true;\n        }\n      })]);\n\n    var DefaultEvents = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$i\n    });\n\n    let unique = 0;\n    const generate$6 = prefix => {\n      const date = new Date();\n      const time = date.getTime();\n      const random = Math.floor(Math.random() * 1000000000);\n      unique++;\n      return prefix + '_' + random + unique + String(time);\n    };\n\n    const prefix$1 = constant$1('alloy-id-');\n    const idAttr$1 = constant$1('data-alloy-id');\n\n    const prefix = prefix$1();\n    const idAttr = idAttr$1();\n    const write = (label, elem) => {\n      const id = generate$6(prefix + label);\n      writeOnly(elem, id);\n      return id;\n    };\n    const writeOnly = (elem, uid) => {\n      Object.defineProperty(elem.dom, idAttr, {\n        value: uid,\n        writable: true\n      });\n    };\n    const read$1 = elem => {\n      const id = isElement$1(elem) ? elem.dom[idAttr] : null;\n      return Optional.from(id);\n    };\n    const generate$5 = prefix => generate$6(prefix);\n\n    const make$8 = identity;\n\n    const NoContextApi = getComp => {\n      const getMessage = event => `The component must be in a context to execute: ${ event }` + (getComp ? '\\n' + element(getComp().element) + ' is not in context.' : '');\n      const fail = event => () => {\n        throw new Error(getMessage(event));\n      };\n      const warn = event => () => {\n        console.warn(getMessage(event));\n      };\n      return {\n        debugInfo: constant$1('fake'),\n        triggerEvent: warn('triggerEvent'),\n        triggerFocus: warn('triggerFocus'),\n        triggerEscape: warn('triggerEscape'),\n        broadcast: warn('broadcast'),\n        broadcastOn: warn('broadcastOn'),\n        broadcastEvent: warn('broadcastEvent'),\n        build: fail('build'),\n        buildOrPatch: fail('buildOrPatch'),\n        addToWorld: fail('addToWorld'),\n        removeFromWorld: fail('removeFromWorld'),\n        addToGui: fail('addToGui'),\n        removeFromGui: fail('removeFromGui'),\n        getByUid: fail('getByUid'),\n        getByDom: fail('getByDom'),\n        isConnected: never\n      };\n    };\n    const singleton$1 = NoContextApi();\n\n    const markAsBehaviourApi = (f, apiName, apiFunction) => {\n      const delegate = apiFunction.toString();\n      const endIndex = delegate.indexOf(')') + 1;\n      const openBracketIndex = delegate.indexOf('(');\n      const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\\s*/);\n      f.toFunctionAnnotation = () => ({\n        name: apiName,\n        parameters: cleanParameters(parameters.slice(0, 1).concat(parameters.slice(3)))\n      });\n      return f;\n    };\n    const cleanParameters = parameters => map$2(parameters, p => endsWith(p, '/*') ? p.substring(0, p.length - '/*'.length) : p);\n    const markAsExtraApi = (f, extraName) => {\n      const delegate = f.toString();\n      const endIndex = delegate.indexOf(')') + 1;\n      const openBracketIndex = delegate.indexOf('(');\n      const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\\s*/);\n      f.toFunctionAnnotation = () => ({\n        name: extraName,\n        parameters: cleanParameters(parameters)\n      });\n      return f;\n    };\n    const markAsSketchApi = (f, apiFunction) => {\n      const delegate = apiFunction.toString();\n      const endIndex = delegate.indexOf(')') + 1;\n      const openBracketIndex = delegate.indexOf('(');\n      const parameters = delegate.substring(openBracketIndex + 1, endIndex - 1).split(/,\\s*/);\n      f.toFunctionAnnotation = () => ({\n        name: 'OVERRIDE',\n        parameters: cleanParameters(parameters.slice(1))\n      });\n      return f;\n    };\n\n    const premadeTag = generate$6('alloy-premade');\n    const premade$1 = comp => {\n      Object.defineProperty(comp.element.dom, premadeTag, {\n        value: comp.uid,\n        writable: true\n      });\n      return wrap$1(premadeTag, comp);\n    };\n    const isPremade = element => has$2(element.dom, premadeTag);\n    const getPremade = spec => get$g(spec, premadeTag);\n    const makeApi = f => markAsSketchApi((component, ...rest) => f(component.getApis(), component, ...rest), f);\n\n    const NoState = { init: () => nu$8({ readState: constant$1('No State required') }) };\n    const nu$8 = spec => spec;\n\n    const generateFrom$1 = (spec, all) => {\n      const schema = map$2(all, a => optionObjOf(a.name(), [\n        required$1('config'),\n        defaulted('state', NoState)\n      ]));\n      const validated = asRaw('component.behaviours', objOf(schema), spec.behaviours).fold(errInfo => {\n        throw new Error(formatError(errInfo) + '\\nComplete spec:\\n' + JSON.stringify(spec, null, 2));\n      }, identity);\n      return {\n        list: all,\n        data: map$1(validated, optBlobThunk => {\n          const output = optBlobThunk.map(blob => ({\n            config: blob.config,\n            state: blob.state.init(blob.config)\n          }));\n          return constant$1(output);\n        })\n      };\n    };\n    const getBehaviours$3 = bData => bData.list;\n    const getData$2 = bData => bData.data;\n\n    const byInnerKey = (data, tuple) => {\n      const r = {};\n      each(data, (detail, key) => {\n        each(detail, (value, indexKey) => {\n          const chain = get$g(r, indexKey).getOr([]);\n          r[indexKey] = chain.concat([tuple(key, value)]);\n        });\n      });\n      return r;\n    };\n\n    const nu$7 = s => ({\n      classes: isUndefined(s.classes) ? [] : s.classes,\n      attributes: isUndefined(s.attributes) ? {} : s.attributes,\n      styles: isUndefined(s.styles) ? {} : s.styles\n    });\n    const merge = (defnA, mod) => ({\n      ...defnA,\n      attributes: {\n        ...defnA.attributes,\n        ...mod.attributes\n      },\n      styles: {\n        ...defnA.styles,\n        ...mod.styles\n      },\n      classes: defnA.classes.concat(mod.classes)\n    });\n\n    const combine$2 = (info, baseMod, behaviours, base) => {\n      const modsByBehaviour = { ...baseMod };\n      each$1(behaviours, behaviour => {\n        modsByBehaviour[behaviour.name()] = behaviour.exhibit(info, base);\n      });\n      const byAspect = byInnerKey(modsByBehaviour, (name, modification) => ({\n        name,\n        modification\n      }));\n      const combineObjects = objects => foldr(objects, (b, a) => ({\n        ...a.modification,\n        ...b\n      }), {});\n      const combinedClasses = foldr(byAspect.classes, (b, a) => a.modification.concat(b), []);\n      const combinedAttributes = combineObjects(byAspect.attributes);\n      const combinedStyles = combineObjects(byAspect.styles);\n      return nu$7({\n        classes: combinedClasses,\n        attributes: combinedAttributes,\n        styles: combinedStyles\n      });\n    };\n\n    const sortKeys = (label, keyName, array, order) => {\n      try {\n        const sorted = sort(array, (a, b) => {\n          const aKey = a[keyName];\n          const bKey = b[keyName];\n          const aIndex = order.indexOf(aKey);\n          const bIndex = order.indexOf(bKey);\n          if (aIndex === -1) {\n            throw new Error('The ordering for ' + label + ' does not have an entry for ' + aKey + '.\\nOrder specified: ' + JSON.stringify(order, null, 2));\n          }\n          if (bIndex === -1) {\n            throw new Error('The ordering for ' + label + ' does not have an entry for ' + bKey + '.\\nOrder specified: ' + JSON.stringify(order, null, 2));\n          }\n          if (aIndex < bIndex) {\n            return -1;\n          } else if (bIndex < aIndex) {\n            return 1;\n          } else {\n            return 0;\n          }\n        });\n        return Result.value(sorted);\n      } catch (err) {\n        return Result.error([err]);\n      }\n    };\n\n    const uncurried = (handler, purpose) => ({\n      handler,\n      purpose\n    });\n    const curried = (handler, purpose) => ({\n      cHandler: handler,\n      purpose\n    });\n    const curryArgs = (descHandler, extraArgs) => curried(curry.apply(undefined, [descHandler.handler].concat(extraArgs)), descHandler.purpose);\n    const getCurried = descHandler => descHandler.cHandler;\n\n    const behaviourTuple = (name, handler) => ({\n      name,\n      handler\n    });\n    const nameToHandlers = (behaviours, info) => {\n      const r = {};\n      each$1(behaviours, behaviour => {\n        r[behaviour.name()] = behaviour.handlers(info);\n      });\n      return r;\n    };\n    const groupByEvents = (info, behaviours, base) => {\n      const behaviourEvents = {\n        ...base,\n        ...nameToHandlers(behaviours, info)\n      };\n      return byInnerKey(behaviourEvents, behaviourTuple);\n    };\n    const combine$1 = (info, eventOrder, behaviours, base) => {\n      const byEventName = groupByEvents(info, behaviours, base);\n      return combineGroups(byEventName, eventOrder);\n    };\n    const assemble = rawHandler => {\n      const handler = read$2(rawHandler);\n      return (component, simulatedEvent, ...rest) => {\n        const args = [\n          component,\n          simulatedEvent\n        ].concat(rest);\n        if (handler.abort.apply(undefined, args)) {\n          simulatedEvent.stop();\n        } else if (handler.can.apply(undefined, args)) {\n          handler.run.apply(undefined, args);\n        }\n      };\n    };\n    const missingOrderError = (eventName, tuples) => Result.error(['The event (' + eventName + ') has more than one behaviour that listens to it.\\nWhen this occurs, you must ' + 'specify an event ordering for the behaviours in your spec (e.g. [ \"listing\", \"toggling\" ]).\\nThe behaviours that ' + 'can trigger it are: ' + JSON.stringify(map$2(tuples, c => c.name), null, 2)]);\n    const fuse = (tuples, eventOrder, eventName) => {\n      const order = eventOrder[eventName];\n      if (!order) {\n        return missingOrderError(eventName, tuples);\n      } else {\n        return sortKeys('Event: ' + eventName, 'name', tuples, order).map(sortedTuples => {\n          const handlers = map$2(sortedTuples, tuple => tuple.handler);\n          return fuse$1(handlers);\n        });\n      }\n    };\n    const combineGroups = (byEventName, eventOrder) => {\n      const r = mapToArray(byEventName, (tuples, eventName) => {\n        const combined = tuples.length === 1 ? Result.value(tuples[0].handler) : fuse(tuples, eventOrder, eventName);\n        return combined.map(handler => {\n          const assembled = assemble(handler);\n          const purpose = tuples.length > 1 ? filter$2(eventOrder[eventName], o => exists(tuples, t => t.name === o)).join(' > ') : tuples[0].name;\n          return wrap$1(eventName, uncurried(assembled, purpose));\n        });\n      });\n      return consolidate(r, {});\n    };\n\n    const baseBehaviour = 'alloy.base.behaviour';\n    const schema$z = objOf([\n      field$1('dom', 'dom', required$2(), objOf([\n        required$1('tag'),\n        defaulted('styles', {}),\n        defaulted('classes', []),\n        defaulted('attributes', {}),\n        option$3('value'),\n        option$3('innerHtml')\n      ])),\n      required$1('components'),\n      required$1('uid'),\n      defaulted('events', {}),\n      defaulted('apis', {}),\n      field$1('eventOrder', 'eventOrder', mergeWith({\n        [execute$5()]: [\n          'disabling',\n          baseBehaviour,\n          'toggling',\n          'typeaheadevents'\n        ],\n        [focus$4()]: [\n          baseBehaviour,\n          'focusing',\n          'keying'\n        ],\n        [systemInit()]: [\n          baseBehaviour,\n          'disabling',\n          'toggling',\n          'representing'\n        ],\n        [input()]: [\n          baseBehaviour,\n          'representing',\n          'streaming',\n          'invalidating'\n        ],\n        [detachedFromDom()]: [\n          baseBehaviour,\n          'representing',\n          'item-events',\n          'tooltipping'\n        ],\n        [mousedown()]: [\n          'focusing',\n          baseBehaviour,\n          'item-type-events'\n        ],\n        [touchstart()]: [\n          'focusing',\n          baseBehaviour,\n          'item-type-events'\n        ],\n        [mouseover()]: [\n          'item-type-events',\n          'tooltipping'\n        ],\n        [receive()]: [\n          'receiving',\n          'reflecting',\n          'tooltipping'\n        ]\n      }), anyValue()),\n      option$3('domModification')\n    ]);\n    const toInfo = spec => asRaw('custom.definition', schema$z, spec);\n    const toDefinition = detail => ({\n      ...detail.dom,\n      uid: detail.uid,\n      domChildren: map$2(detail.components, comp => comp.element)\n    });\n    const toModification = detail => detail.domModification.fold(() => nu$7({}), nu$7);\n    const toEvents = info => info.events;\n\n    const read = (element, attr) => {\n      const value = get$f(element, attr);\n      return value === undefined || value === '' ? [] : value.split(' ');\n    };\n    const add$4 = (element, attr, id) => {\n      const old = read(element, attr);\n      const nu = old.concat([id]);\n      set$9(element, attr, nu.join(' '));\n      return true;\n    };\n    const remove$4 = (element, attr, id) => {\n      const nu = filter$2(read(element, attr), v => v !== id);\n      if (nu.length > 0) {\n        set$9(element, attr, nu.join(' '));\n      } else {\n        remove$7(element, attr);\n      }\n      return false;\n    };\n\n    const supports = element => element.dom.classList !== undefined;\n    const get$8 = element => read(element, 'class');\n    const add$3 = (element, clazz) => add$4(element, 'class', clazz);\n    const remove$3 = (element, clazz) => remove$4(element, 'class', clazz);\n\n    const add$2 = (element, clazz) => {\n      if (supports(element)) {\n        element.dom.classList.add(clazz);\n      } else {\n        add$3(element, clazz);\n      }\n    };\n    const cleanClass = element => {\n      const classList = supports(element) ? element.dom.classList : get$8(element);\n      if (classList.length === 0) {\n        remove$7(element, 'class');\n      }\n    };\n    const remove$2 = (element, clazz) => {\n      if (supports(element)) {\n        const classList = element.dom.classList;\n        classList.remove(clazz);\n      } else {\n        remove$3(element, clazz);\n      }\n      cleanClass(element);\n    };\n    const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);\n\n    const add$1 = (element, classes) => {\n      each$1(classes, x => {\n        add$2(element, x);\n      });\n    };\n    const remove$1 = (element, classes) => {\n      each$1(classes, x => {\n        remove$2(element, x);\n      });\n    };\n    const hasAll = (element, classes) => forall(classes, clazz => has(element, clazz));\n    const getNative = element => {\n      const classList = element.dom.classList;\n      const r = new Array(classList.length);\n      for (let i = 0; i < classList.length; i++) {\n        const item = classList.item(i);\n        if (item !== null) {\n          r[i] = item;\n        }\n      }\n      return r;\n    };\n    const get$7 = element => supports(element) ? getNative(element) : get$8(element);\n\n    const get$6 = element => element.dom.value;\n    const set$5 = (element, value) => {\n      if (value === undefined) {\n        throw new Error('Value.set was undefined');\n      }\n      element.dom.value = value;\n    };\n\n    const determineObsoleted = (parent, index, oldObsoleted) => {\n      const newObsoleted = child$2(parent, index);\n      return newObsoleted.map(newObs => {\n        const elemChanged = oldObsoleted.exists(o => !eq(o, newObs));\n        if (elemChanged) {\n          const oldTag = oldObsoleted.map(name$3).getOr('span');\n          const marker = SugarElement.fromTag(oldTag);\n          before$1(newObs, marker);\n          return marker;\n        } else {\n          return newObs;\n        }\n      });\n    };\n    const ensureInDom = (parent, child, obsoleted) => {\n      obsoleted.fold(() => append$2(parent, child), obs => {\n        if (!eq(obs, child)) {\n          before$1(obs, child);\n          remove$5(obs);\n        }\n      });\n    };\n    const patchChildrenWith = (parent, nu, f) => {\n      const builtChildren = map$2(nu, f);\n      const currentChildren = children(parent);\n      each$1(currentChildren.slice(builtChildren.length), remove$5);\n      return builtChildren;\n    };\n    const patchSpecChild = (parent, index, spec, build) => {\n      const oldObsoleted = child$2(parent, index);\n      const childComp = build(spec, oldObsoleted);\n      const obsoleted = determineObsoleted(parent, index, oldObsoleted);\n      ensureInDom(parent, childComp.element, obsoleted);\n      return childComp;\n    };\n    const patchSpecChildren = (parent, specs, build) => patchChildrenWith(parent, specs, (spec, index) => patchSpecChild(parent, index, spec, build));\n    const patchDomChildren = (parent, nodes) => patchChildrenWith(parent, nodes, (node, index) => {\n      const optObsoleted = child$2(parent, index);\n      ensureInDom(parent, node, optObsoleted);\n      return node;\n    });\n\n    const diffKeyValueSet = (newObj, oldObj) => {\n      const newKeys = keys(newObj);\n      const oldKeys = keys(oldObj);\n      const toRemove = difference(oldKeys, newKeys);\n      const toSet = bifilter(newObj, (v, k) => {\n        return !has$2(oldObj, k) || v !== oldObj[k];\n      }).t;\n      return {\n        toRemove,\n        toSet\n      };\n    };\n    const reconcileToDom = (definition, obsoleted) => {\n      const {\n        class: clazz,\n        style,\n        ...existingAttributes\n      } = clone$2(obsoleted);\n      const {\n        toSet: attrsToSet,\n        toRemove: attrsToRemove\n      } = diffKeyValueSet(definition.attributes, existingAttributes);\n      const updateAttrs = () => {\n        each$1(attrsToRemove, a => remove$7(obsoleted, a));\n        setAll$1(obsoleted, attrsToSet);\n      };\n      const existingStyles = getAllRaw(obsoleted);\n      const {\n        toSet: stylesToSet,\n        toRemove: stylesToRemove\n      } = diffKeyValueSet(definition.styles, existingStyles);\n      const updateStyles = () => {\n        each$1(stylesToRemove, s => remove$6(obsoleted, s));\n        setAll(obsoleted, stylesToSet);\n      };\n      const existingClasses = get$7(obsoleted);\n      const classesToRemove = difference(existingClasses, definition.classes);\n      const classesToAdd = difference(definition.classes, existingClasses);\n      const updateClasses = () => {\n        add$1(obsoleted, classesToAdd);\n        remove$1(obsoleted, classesToRemove);\n      };\n      const updateHtml = html => {\n        set$6(obsoleted, html);\n      };\n      const updateChildren = () => {\n        const children = definition.domChildren;\n        patchDomChildren(obsoleted, children);\n      };\n      const updateValue = () => {\n        const valueElement = obsoleted;\n        const value = definition.value.getOrUndefined();\n        if (value !== get$6(valueElement)) {\n          set$5(valueElement, value !== null && value !== void 0 ? value : '');\n        }\n      };\n      updateAttrs();\n      updateClasses();\n      updateStyles();\n      definition.innerHtml.fold(updateChildren, updateHtml);\n      updateValue();\n      return obsoleted;\n    };\n\n    const introduceToDom = definition => {\n      const subject = SugarElement.fromTag(definition.tag);\n      setAll$1(subject, definition.attributes);\n      add$1(subject, definition.classes);\n      setAll(subject, definition.styles);\n      definition.innerHtml.each(html => set$6(subject, html));\n      const children = definition.domChildren;\n      append$1(subject, children);\n      definition.value.each(value => {\n        set$5(subject, value);\n      });\n      return subject;\n    };\n    const attemptPatch = (definition, obsoleted) => {\n      try {\n        const e = reconcileToDom(definition, obsoleted);\n        return Optional.some(e);\n      } catch (err) {\n        return Optional.none();\n      }\n    };\n    const hasMixedChildren = definition => definition.innerHtml.isSome() && definition.domChildren.length > 0;\n    const renderToDom = (definition, optObsoleted) => {\n      const canBePatched = candidate => name$3(candidate) === definition.tag && !hasMixedChildren(definition) && !isPremade(candidate);\n      const elem = optObsoleted.filter(canBePatched).bind(obsoleted => attemptPatch(definition, obsoleted)).getOrThunk(() => introduceToDom(definition));\n      writeOnly(elem, definition.uid);\n      return elem;\n    };\n\n    const getBehaviours$2 = spec => {\n      const behaviours = get$g(spec, 'behaviours').getOr({});\n      return bind$3(keys(behaviours), name => {\n        const behaviour = behaviours[name];\n        return isNonNullable(behaviour) ? [behaviour.me] : [];\n      });\n    };\n    const generateFrom = (spec, all) => generateFrom$1(spec, all);\n    const generate$4 = spec => {\n      const all = getBehaviours$2(spec);\n      return generateFrom(spec, all);\n    };\n\n    const getDomDefinition = (info, bList, bData) => {\n      const definition = toDefinition(info);\n      const infoModification = toModification(info);\n      const baseModification = { 'alloy.base.modification': infoModification };\n      const modification = bList.length > 0 ? combine$2(bData, baseModification, bList, definition) : infoModification;\n      return merge(definition, modification);\n    };\n    const getEvents = (info, bList, bData) => {\n      const baseEvents = { 'alloy.base.behaviour': toEvents(info) };\n      return combine$1(bData, info.eventOrder, bList, baseEvents).getOrDie();\n    };\n    const build$2 = (spec, obsoleted) => {\n      const getMe = () => me;\n      const systemApi = Cell(singleton$1);\n      const info = getOrDie(toInfo(spec));\n      const bBlob = generate$4(spec);\n      const bList = getBehaviours$3(bBlob);\n      const bData = getData$2(bBlob);\n      const modDefinition = getDomDefinition(info, bList, bData);\n      const item = renderToDom(modDefinition, obsoleted);\n      const events = getEvents(info, bList, bData);\n      const subcomponents = Cell(info.components);\n      const connect = newApi => {\n        systemApi.set(newApi);\n      };\n      const disconnect = () => {\n        systemApi.set(NoContextApi(getMe));\n      };\n      const syncComponents = () => {\n        const children$1 = children(item);\n        const subs = bind$3(children$1, child => systemApi.get().getByDom(child).fold(() => [], pure$2));\n        subcomponents.set(subs);\n      };\n      const config = behaviour => {\n        const b = bData;\n        const f = isFunction(b[behaviour.name()]) ? b[behaviour.name()] : () => {\n          throw new Error('Could not find ' + behaviour.name() + ' in ' + JSON.stringify(spec, null, 2));\n        };\n        return f();\n      };\n      const hasConfigured = behaviour => isFunction(bData[behaviour.name()]);\n      const getApis = () => info.apis;\n      const readState = behaviourName => bData[behaviourName]().map(b => b.state.readState()).getOr('not enabled');\n      const me = {\n        uid: spec.uid,\n        getSystem: systemApi.get,\n        config,\n        hasConfigured,\n        spec,\n        readState,\n        getApis,\n        connect,\n        disconnect,\n        element: item,\n        syncComponents,\n        components: subcomponents.get,\n        events\n      };\n      return me;\n    };\n\n    const buildSubcomponents = (spec, obsoleted) => {\n      const components = get$g(spec, 'components').getOr([]);\n      return obsoleted.fold(() => map$2(components, build$1), obs => map$2(components, (c, i) => {\n        return buildOrPatch(c, child$2(obs, i));\n      }));\n    };\n    const buildFromSpec = (userSpec, obsoleted) => {\n      const {\n        events: specEvents,\n        ...spec\n      } = make$8(userSpec);\n      const components = buildSubcomponents(spec, obsoleted);\n      const completeSpec = {\n        ...spec,\n        events: {\n          ...DefaultEvents,\n          ...specEvents\n        },\n        components\n      };\n      return Result.value(build$2(completeSpec, obsoleted));\n    };\n    const text$2 = textContent => {\n      const element = SugarElement.fromText(textContent);\n      return external$1({ element });\n    };\n    const external$1 = spec => {\n      const extSpec = asRawOrDie$1('external.component', objOfOnly([\n        required$1('element'),\n        option$3('uid')\n      ]), spec);\n      const systemApi = Cell(NoContextApi());\n      const connect = newApi => {\n        systemApi.set(newApi);\n      };\n      const disconnect = () => {\n        systemApi.set(NoContextApi(() => me));\n      };\n      const uid = extSpec.uid.getOrThunk(() => generate$5('external'));\n      writeOnly(extSpec.element, uid);\n      const me = {\n        uid,\n        getSystem: systemApi.get,\n        config: Optional.none,\n        hasConfigured: never,\n        connect,\n        disconnect,\n        getApis: () => ({}),\n        element: extSpec.element,\n        spec,\n        readState: constant$1('No state'),\n        syncComponents: noop,\n        components: constant$1([]),\n        events: {}\n      };\n      return premade$1(me);\n    };\n    const uids = generate$5;\n    const isSketchSpec$1 = spec => has$2(spec, 'uid');\n    const buildOrPatch = (spec, obsoleted) => getPremade(spec).getOrThunk(() => {\n      const userSpecWithUid = isSketchSpec$1(spec) ? spec : {\n        uid: uids(''),\n        ...spec\n      };\n      return buildFromSpec(userSpecWithUid, obsoleted).getOrDie();\n    });\n    const build$1 = spec => buildOrPatch(spec, Optional.none());\n    const premade = premade$1;\n\n    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {\n      if (is(scope, a)) {\n        return Optional.some(scope);\n      } else if (isFunction(isRoot) && isRoot(scope)) {\n        return Optional.none();\n      } else {\n        return ancestor(scope, a, isRoot);\n      }\n    };\n\n    const ancestor$1 = (scope, predicate, isRoot) => {\n      let element = scope.dom;\n      const stop = isFunction(isRoot) ? isRoot : never;\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        if (predicate(el)) {\n          return Optional.some(el);\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const closest$3 = (scope, predicate, isRoot) => {\n      const is = (s, test) => test(s);\n      return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);\n    };\n    const child$1 = (scope, predicate) => {\n      const pred = node => predicate(SugarElement.fromDom(node));\n      const result = find$5(scope.dom.childNodes, pred);\n      return result.map(SugarElement.fromDom);\n    };\n    const descendant$1 = (scope, predicate) => {\n      const descend = node => {\n        for (let i = 0; i < node.childNodes.length; i++) {\n          const child = SugarElement.fromDom(node.childNodes[i]);\n          if (predicate(child)) {\n            return Optional.some(child);\n          }\n          const res = descend(node.childNodes[i]);\n          if (res.isSome()) {\n            return res;\n          }\n        }\n        return Optional.none();\n      };\n      return descend(scope.dom);\n    };\n\n    const closest$2 = (scope, predicate, isRoot) => closest$3(scope, predicate, isRoot).isSome();\n\n    const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);\n    const child = (scope, selector) => child$1(scope, e => is(e, selector));\n    const descendant = (scope, selector) => one(selector, scope);\n    const closest$1 = (scope, selector, isRoot) => {\n      const is$1 = (element, selector) => is(element, selector);\n      return ClosestOrAncestor(is$1, ancestor, scope, selector, isRoot);\n    };\n\n    const attribute = 'aria-controls';\n    const find$1 = queryElem => {\n      const dependent = closest$3(queryElem, elem => {\n        if (!isElement$1(elem)) {\n          return false;\n        }\n        const id = get$f(elem, 'id');\n        return id !== undefined && id.indexOf(attribute) > -1;\n      });\n      return dependent.bind(dep => {\n        const id = get$f(dep, 'id');\n        const dos = getRootNode(dep);\n        return descendant(dos, `[${ attribute }=\"${ id }\"]`);\n      });\n    };\n    const manager = () => {\n      const ariaId = generate$6(attribute);\n      const link = elem => {\n        set$9(elem, attribute, ariaId);\n      };\n      const unlink = elem => {\n        remove$7(elem, attribute);\n      };\n      return {\n        id: ariaId,\n        link,\n        unlink\n      };\n    };\n\n    const isAriaPartOf = (component, queryElem) => find$1(queryElem).exists(owner => isPartOf$1(component, owner));\n    const isPartOf$1 = (component, queryElem) => closest$2(queryElem, el => eq(el, component.element), never) || isAriaPartOf(component, queryElem);\n\n    const unknown = 'unknown';\n    var EventConfiguration;\n    (function (EventConfiguration) {\n      EventConfiguration[EventConfiguration['STOP'] = 0] = 'STOP';\n      EventConfiguration[EventConfiguration['NORMAL'] = 1] = 'NORMAL';\n      EventConfiguration[EventConfiguration['LOGGING'] = 2] = 'LOGGING';\n    }(EventConfiguration || (EventConfiguration = {})));\n    const eventConfig = Cell({});\n    const makeEventLogger = (eventName, initialTarget) => {\n      const sequence = [];\n      const startTime = new Date().getTime();\n      return {\n        logEventCut: (_name, target, purpose) => {\n          sequence.push({\n            outcome: 'cut',\n            target,\n            purpose\n          });\n        },\n        logEventStopped: (_name, target, purpose) => {\n          sequence.push({\n            outcome: 'stopped',\n            target,\n            purpose\n          });\n        },\n        logNoParent: (_name, target, purpose) => {\n          sequence.push({\n            outcome: 'no-parent',\n            target,\n            purpose\n          });\n        },\n        logEventNoHandlers: (_name, target) => {\n          sequence.push({\n            outcome: 'no-handlers-left',\n            target\n          });\n        },\n        logEventResponse: (_name, target, purpose) => {\n          sequence.push({\n            outcome: 'response',\n            purpose,\n            target\n          });\n        },\n        write: () => {\n          const finishTime = new Date().getTime();\n          if (contains$2([\n              'mousemove',\n              'mouseover',\n              'mouseout',\n              systemInit()\n            ], eventName)) {\n            return;\n          }\n          console.log(eventName, {\n            event: eventName,\n            time: finishTime - startTime,\n            target: initialTarget.dom,\n            sequence: map$2(sequence, s => {\n              if (!contains$2([\n                  'cut',\n                  'stopped',\n                  'response'\n                ], s.outcome)) {\n                return s.outcome;\n              } else {\n                return '{' + s.purpose + '} ' + s.outcome + ' at (' + element(s.target) + ')';\n              }\n            })\n          });\n        }\n      };\n    };\n    const processEvent = (eventName, initialTarget, f) => {\n      const status = get$g(eventConfig.get(), eventName).orThunk(() => {\n        const patterns = keys(eventConfig.get());\n        return findMap(patterns, p => eventName.indexOf(p) > -1 ? Optional.some(eventConfig.get()[p]) : Optional.none());\n      }).getOr(EventConfiguration.NORMAL);\n      switch (status) {\n      case EventConfiguration.NORMAL:\n        return f(noLogger());\n      case EventConfiguration.LOGGING: {\n          const logger = makeEventLogger(eventName, initialTarget);\n          const output = f(logger);\n          logger.write();\n          return output;\n        }\n      case EventConfiguration.STOP:\n        return true;\n      }\n    };\n    const path = [\n      'alloy/data/Fields',\n      'alloy/debugging/Debugging'\n    ];\n    const getTrace = () => {\n      const err = new Error();\n      if (err.stack !== undefined) {\n        const lines = err.stack.split('\\n');\n        return find$5(lines, line => line.indexOf('alloy') > 0 && !exists(path, p => line.indexOf(p) > -1)).getOr(unknown);\n      } else {\n        return unknown;\n      }\n    };\n    const ignoreEvent = {\n      logEventCut: noop,\n      logEventStopped: noop,\n      logNoParent: noop,\n      logEventNoHandlers: noop,\n      logEventResponse: noop,\n      write: noop\n    };\n    const monitorEvent = (eventName, initialTarget, f) => processEvent(eventName, initialTarget, f);\n    const noLogger = constant$1(ignoreEvent);\n\n    const menuFields = constant$1([\n      required$1('menu'),\n      required$1('selectedMenu')\n    ]);\n    const itemFields = constant$1([\n      required$1('item'),\n      required$1('selectedItem')\n    ]);\n    constant$1(objOf(itemFields().concat(menuFields())));\n    const itemSchema$3 = constant$1(objOf(itemFields()));\n\n    const _initSize = requiredObjOf('initSize', [\n      required$1('numColumns'),\n      required$1('numRows')\n    ]);\n    const itemMarkers = () => requiredOf('markers', itemSchema$3());\n    const tieredMenuMarkers = () => requiredObjOf('markers', [required$1('backgroundMenu')].concat(menuFields()).concat(itemFields()));\n    const markers$1 = required => requiredObjOf('markers', map$2(required, required$1));\n    const onPresenceHandler = (label, fieldName, presence) => {\n      getTrace();\n      return field$1(fieldName, fieldName, presence, valueOf(f => Result.value((...args) => {\n        return f.apply(undefined, args);\n      })));\n    };\n    const onHandler = fieldName => onPresenceHandler('onHandler', fieldName, defaulted$1(noop));\n    const onKeyboardHandler = fieldName => onPresenceHandler('onKeyboardHandler', fieldName, defaulted$1(Optional.none));\n    const onStrictHandler = fieldName => onPresenceHandler('onHandler', fieldName, required$2());\n    const onStrictKeyboardHandler = fieldName => onPresenceHandler('onKeyboardHandler', fieldName, required$2());\n    const output$1 = (name, value) => customField(name, constant$1(value));\n    const snapshot = name => customField(name, identity);\n    const initSize = constant$1(_initSize);\n\n    const nu$6 = (x, y, bubble, direction, placement, boundsRestriction, labelPrefix, alwaysFit = false) => ({\n      x,\n      y,\n      bubble,\n      direction,\n      placement,\n      restriction: boundsRestriction,\n      label: `${ labelPrefix }-${ placement }`,\n      alwaysFit\n    });\n\n    const adt$a = Adt.generate([\n      { southeast: [] },\n      { southwest: [] },\n      { northeast: [] },\n      { northwest: [] },\n      { south: [] },\n      { north: [] },\n      { east: [] },\n      { west: [] }\n    ]);\n    const cata$2 = (subject, southeast, southwest, northeast, northwest, south, north, east, west) => subject.fold(southeast, southwest, northeast, northwest, south, north, east, west);\n    const cataVertical = (subject, south, middle, north) => subject.fold(south, south, north, north, south, north, middle, middle);\n    const cataHorizontal = (subject, east, middle, west) => subject.fold(east, west, east, west, middle, middle, east, west);\n    const southeast$3 = adt$a.southeast;\n    const southwest$3 = adt$a.southwest;\n    const northeast$3 = adt$a.northeast;\n    const northwest$3 = adt$a.northwest;\n    const south$3 = adt$a.south;\n    const north$3 = adt$a.north;\n    const east$3 = adt$a.east;\n    const west$3 = adt$a.west;\n\n    const cycleBy = (value, delta, min, max) => {\n      const r = value + delta;\n      if (r > max) {\n        return min;\n      } else if (r < min) {\n        return max;\n      } else {\n        return r;\n      }\n    };\n    const clamp = (value, min, max) => Math.min(Math.max(value, min), max);\n\n    const getRestriction = (anchor, restriction) => {\n      switch (restriction) {\n      case 1:\n        return anchor.x;\n      case 0:\n        return anchor.x + anchor.width;\n      case 2:\n        return anchor.y;\n      case 3:\n        return anchor.y + anchor.height;\n      }\n    };\n    const boundsRestriction = (anchor, restrictions) => mapToObject([\n      'left',\n      'right',\n      'top',\n      'bottom'\n    ], dir => get$g(restrictions, dir).map(restriction => getRestriction(anchor, restriction)));\n    const adjustBounds = (bounds$1, restriction, bubbleOffset) => {\n      const applyRestriction = (dir, current) => restriction[dir].map(pos => {\n        const isVerticalAxis = dir === 'top' || dir === 'bottom';\n        const offset = isVerticalAxis ? bubbleOffset.top : bubbleOffset.left;\n        const comparator = dir === 'left' || dir === 'top' ? Math.max : Math.min;\n        const newPos = comparator(pos, current) + offset;\n        return isVerticalAxis ? clamp(newPos, bounds$1.y, bounds$1.bottom) : clamp(newPos, bounds$1.x, bounds$1.right);\n      }).getOr(current);\n      const adjustedLeft = applyRestriction('left', bounds$1.x);\n      const adjustedTop = applyRestriction('top', bounds$1.y);\n      const adjustedRight = applyRestriction('right', bounds$1.right);\n      const adjustedBottom = applyRestriction('bottom', bounds$1.bottom);\n      return bounds(adjustedLeft, adjustedTop, adjustedRight - adjustedLeft, adjustedBottom - adjustedTop);\n    };\n\n    const labelPrefix$2 = 'layout';\n    const eastX$1 = anchor => anchor.x;\n    const middleX$1 = (anchor, element) => anchor.x + anchor.width / 2 - element.width / 2;\n    const westX$1 = (anchor, element) => anchor.x + anchor.width - element.width;\n    const northY$2 = (anchor, element) => anchor.y - element.height;\n    const southY$2 = anchor => anchor.y + anchor.height;\n    const centreY$1 = (anchor, element) => anchor.y + anchor.height / 2 - element.height / 2;\n    const eastEdgeX$1 = anchor => anchor.x + anchor.width;\n    const westEdgeX$1 = (anchor, element) => anchor.x - element.width;\n    const southeast$2 = (anchor, element, bubbles) => nu$6(eastX$1(anchor), southY$2(anchor), bubbles.southeast(), southeast$3(), 'southeast', boundsRestriction(anchor, {\n      left: 1,\n      top: 3\n    }), labelPrefix$2);\n    const southwest$2 = (anchor, element, bubbles) => nu$6(westX$1(anchor, element), southY$2(anchor), bubbles.southwest(), southwest$3(), 'southwest', boundsRestriction(anchor, {\n      right: 0,\n      top: 3\n    }), labelPrefix$2);\n    const northeast$2 = (anchor, element, bubbles) => nu$6(eastX$1(anchor), northY$2(anchor, element), bubbles.northeast(), northeast$3(), 'northeast', boundsRestriction(anchor, {\n      left: 1,\n      bottom: 2\n    }), labelPrefix$2);\n    const northwest$2 = (anchor, element, bubbles) => nu$6(westX$1(anchor, element), northY$2(anchor, element), bubbles.northwest(), northwest$3(), 'northwest', boundsRestriction(anchor, {\n      right: 0,\n      bottom: 2\n    }), labelPrefix$2);\n    const north$2 = (anchor, element, bubbles) => nu$6(middleX$1(anchor, element), northY$2(anchor, element), bubbles.north(), north$3(), 'north', boundsRestriction(anchor, { bottom: 2 }), labelPrefix$2);\n    const south$2 = (anchor, element, bubbles) => nu$6(middleX$1(anchor, element), southY$2(anchor), bubbles.south(), south$3(), 'south', boundsRestriction(anchor, { top: 3 }), labelPrefix$2);\n    const east$2 = (anchor, element, bubbles) => nu$6(eastEdgeX$1(anchor), centreY$1(anchor, element), bubbles.east(), east$3(), 'east', boundsRestriction(anchor, { left: 0 }), labelPrefix$2);\n    const west$2 = (anchor, element, bubbles) => nu$6(westEdgeX$1(anchor, element), centreY$1(anchor, element), bubbles.west(), west$3(), 'west', boundsRestriction(anchor, { right: 1 }), labelPrefix$2);\n    const all$1 = () => [\n      southeast$2,\n      southwest$2,\n      northeast$2,\n      northwest$2,\n      south$2,\n      north$2,\n      east$2,\n      west$2\n    ];\n    const allRtl$1 = () => [\n      southwest$2,\n      southeast$2,\n      northwest$2,\n      northeast$2,\n      south$2,\n      north$2,\n      east$2,\n      west$2\n    ];\n    const aboveOrBelow = () => [\n      northeast$2,\n      northwest$2,\n      southeast$2,\n      southwest$2,\n      north$2,\n      south$2\n    ];\n    const aboveOrBelowRtl = () => [\n      northwest$2,\n      northeast$2,\n      southwest$2,\n      southeast$2,\n      north$2,\n      south$2\n    ];\n    const belowOrAbove = () => [\n      southeast$2,\n      southwest$2,\n      northeast$2,\n      northwest$2,\n      south$2,\n      north$2\n    ];\n    const belowOrAboveRtl = () => [\n      southwest$2,\n      southeast$2,\n      northwest$2,\n      northeast$2,\n      south$2,\n      north$2\n    ];\n\n    const chooseChannels = (channels, message) => message.universal ? channels : filter$2(channels, ch => contains$2(message.channels, ch));\n    const events$h = receiveConfig => derive$2([run$1(receive(), (component, message) => {\n        const channelMap = receiveConfig.channels;\n        const channels = keys(channelMap);\n        const receivingData = message;\n        const targetChannels = chooseChannels(channels, receivingData);\n        each$1(targetChannels, ch => {\n          const channelInfo = channelMap[ch];\n          const channelSchema = channelInfo.schema;\n          const data = asRawOrDie$1('channel[' + ch + '] data\\nReceiver: ' + element(component.element), channelSchema, receivingData.data);\n          channelInfo.onReceive(component, data);\n        });\n      })]);\n\n    var ActiveReceiving = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$h\n    });\n\n    var ReceivingSchema = [requiredOf('channels', setOf(Result.value, objOfOnly([\n        onStrictHandler('onReceive'),\n        defaulted('schema', anyValue())\n      ])))];\n\n    const executeEvent = (bConfig, bState, executor) => runOnExecute$1(component => {\n      executor(component, bConfig, bState);\n    });\n    const loadEvent = (bConfig, bState, f) => runOnInit((component, _simulatedEvent) => {\n      f(component, bConfig, bState);\n    });\n    const create$5 = (schema, name, active, apis, extra, state) => {\n      const configSchema = objOfOnly(schema);\n      const schemaSchema = optionObjOf(name, [optionObjOfOnly('config', schema)]);\n      return doCreate(configSchema, schemaSchema, name, active, apis, extra, state);\n    };\n    const createModes$1 = (modes, name, active, apis, extra, state) => {\n      const configSchema = modes;\n      const schemaSchema = optionObjOf(name, [optionOf('config', modes)]);\n      return doCreate(configSchema, schemaSchema, name, active, apis, extra, state);\n    };\n    const wrapApi = (bName, apiFunction, apiName) => {\n      const f = (component, ...rest) => {\n        const args = [component].concat(rest);\n        return component.config({ name: constant$1(bName) }).fold(() => {\n          throw new Error('We could not find any behaviour configuration for: ' + bName + '. Using API: ' + apiName);\n        }, info => {\n          const rest = Array.prototype.slice.call(args, 1);\n          return apiFunction.apply(undefined, [\n            component,\n            info.config,\n            info.state\n          ].concat(rest));\n        });\n      };\n      return markAsBehaviourApi(f, apiName, apiFunction);\n    };\n    const revokeBehaviour = name => ({\n      key: name,\n      value: undefined\n    });\n    const doCreate = (configSchema, schemaSchema, name, active, apis, extra, state) => {\n      const getConfig = info => hasNonNullableKey(info, name) ? info[name]() : Optional.none();\n      const wrappedApis = map$1(apis, (apiF, apiName) => wrapApi(name, apiF, apiName));\n      const wrappedExtra = map$1(extra, (extraF, extraName) => markAsExtraApi(extraF, extraName));\n      const me = {\n        ...wrappedExtra,\n        ...wrappedApis,\n        revoke: curry(revokeBehaviour, name),\n        config: spec => {\n          const prepared = asRawOrDie$1(name + '-config', configSchema, spec);\n          return {\n            key: name,\n            value: {\n              config: prepared,\n              me,\n              configAsRaw: cached(() => asRawOrDie$1(name + '-config', configSchema, spec)),\n              initialConfig: spec,\n              state\n            }\n          };\n        },\n        schema: constant$1(schemaSchema),\n        exhibit: (info, base) => {\n          return lift2(getConfig(info), get$g(active, 'exhibit'), (behaviourInfo, exhibitor) => {\n            return exhibitor(base, behaviourInfo.config, behaviourInfo.state);\n          }).getOrThunk(() => nu$7({}));\n        },\n        name: constant$1(name),\n        handlers: info => {\n          return getConfig(info).map(behaviourInfo => {\n            const getEvents = get$g(active, 'events').getOr(() => ({}));\n            return getEvents(behaviourInfo.config, behaviourInfo.state);\n          }).getOr({});\n        }\n      };\n      return me;\n    };\n\n    const derive$1 = capabilities => wrapAll(capabilities);\n    const simpleSchema = objOfOnly([\n      required$1('fields'),\n      required$1('name'),\n      defaulted('active', {}),\n      defaulted('apis', {}),\n      defaulted('state', NoState),\n      defaulted('extra', {})\n    ]);\n    const create$4 = data => {\n      const value = asRawOrDie$1('Creating behaviour: ' + data.name, simpleSchema, data);\n      return create$5(value.fields, value.name, value.active, value.apis, value.extra, value.state);\n    };\n    const modeSchema = objOfOnly([\n      required$1('branchKey'),\n      required$1('branches'),\n      required$1('name'),\n      defaulted('active', {}),\n      defaulted('apis', {}),\n      defaulted('state', NoState),\n      defaulted('extra', {})\n    ]);\n    const createModes = data => {\n      const value = asRawOrDie$1('Creating behaviour: ' + data.name, modeSchema, data);\n      return createModes$1(choose$1(value.branchKey, value.branches), value.name, value.active, value.apis, value.extra, value.state);\n    };\n    const revoke = constant$1(undefined);\n\n    const Receiving = create$4({\n      fields: ReceivingSchema,\n      name: 'receiving',\n      active: ActiveReceiving\n    });\n\n    const exhibit$6 = (base, posConfig) => nu$7({\n      classes: [],\n      styles: posConfig.useFixed() ? {} : { position: 'relative' }\n    });\n\n    var ActivePosition = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        exhibit: exhibit$6\n    });\n\n    const focus$3 = element => element.dom.focus();\n    const blur$1 = element => element.dom.blur();\n    const hasFocus = element => {\n      const root = getRootNode(element).dom;\n      return element.dom === root.activeElement;\n    };\n    const active$1 = (root = getDocument()) => Optional.from(root.dom.activeElement).map(SugarElement.fromDom);\n    const search = element => active$1(getRootNode(element)).filter(e => element.dom.contains(e.dom));\n\n    const preserve$1 = (f, container) => {\n      const dos = getRootNode(container);\n      const refocus = active$1(dos).bind(focused => {\n        const hasFocus = elem => eq(focused, elem);\n        return hasFocus(container) ? Optional.some(container) : descendant$1(container, hasFocus);\n      });\n      const result = f(container);\n      refocus.each(oldFocus => {\n        active$1(dos).filter(newFocus => eq(newFocus, oldFocus)).fold(() => {\n          focus$3(oldFocus);\n        }, noop);\n      });\n      return result;\n    };\n\n    const NuPositionCss = (position, left, top, right, bottom) => {\n      const toPx = num => num + 'px';\n      return {\n        position,\n        left: left.map(toPx),\n        top: top.map(toPx),\n        right: right.map(toPx),\n        bottom: bottom.map(toPx)\n      };\n    };\n    const toOptions = position => ({\n      ...position,\n      position: Optional.some(position.position)\n    });\n    const applyPositionCss = (element, position) => {\n      setOptions(element, toOptions(position));\n    };\n\n    const adt$9 = Adt.generate([\n      { none: [] },\n      {\n        relative: [\n          'x',\n          'y',\n          'width',\n          'height'\n        ]\n      },\n      {\n        fixed: [\n          'x',\n          'y',\n          'width',\n          'height'\n        ]\n      }\n    ]);\n    const positionWithDirection = (posName, decision, x, y, width, height) => {\n      const decisionRect = decision.rect;\n      const decisionX = decisionRect.x - x;\n      const decisionY = decisionRect.y - y;\n      const decisionWidth = decisionRect.width;\n      const decisionHeight = decisionRect.height;\n      const decisionRight = width - (decisionX + decisionWidth);\n      const decisionBottom = height - (decisionY + decisionHeight);\n      const left = Optional.some(decisionX);\n      const top = Optional.some(decisionY);\n      const right = Optional.some(decisionRight);\n      const bottom = Optional.some(decisionBottom);\n      const none = Optional.none();\n      return cata$2(decision.direction, () => NuPositionCss(posName, left, top, none, none), () => NuPositionCss(posName, none, top, right, none), () => NuPositionCss(posName, left, none, none, bottom), () => NuPositionCss(posName, none, none, right, bottom), () => NuPositionCss(posName, left, top, none, none), () => NuPositionCss(posName, left, none, none, bottom), () => NuPositionCss(posName, left, top, none, none), () => NuPositionCss(posName, none, top, right, none));\n    };\n    const reposition = (origin, decision) => origin.fold(() => {\n      const decisionRect = decision.rect;\n      return NuPositionCss('absolute', Optional.some(decisionRect.x), Optional.some(decisionRect.y), Optional.none(), Optional.none());\n    }, (x, y, width, height) => {\n      return positionWithDirection('absolute', decision, x, y, width, height);\n    }, (x, y, width, height) => {\n      return positionWithDirection('fixed', decision, x, y, width, height);\n    });\n    const toBox = (origin, element) => {\n      const rel = curry(find$2, element);\n      const position = origin.fold(rel, rel, () => {\n        const scroll = get$b();\n        return find$2(element).translate(-scroll.left, -scroll.top);\n      });\n      const width = getOuter$1(element);\n      const height = getOuter$2(element);\n      return bounds(position.left, position.top, width, height);\n    };\n    const viewport = (origin, getBounds) => getBounds.fold(() => origin.fold(win, win, bounds), b => origin.fold(b, b, () => {\n      const bounds$1 = b();\n      const pos = translate$2(origin, bounds$1.x, bounds$1.y);\n      return bounds(pos.left, pos.top, bounds$1.width, bounds$1.height);\n    }));\n    const translate$2 = (origin, x, y) => {\n      const pos = SugarPosition(x, y);\n      const removeScroll = () => {\n        const outerScroll = get$b();\n        return pos.translate(-outerScroll.left, -outerScroll.top);\n      };\n      return origin.fold(constant$1(pos), constant$1(pos), removeScroll);\n    };\n    const cata$1 = (subject, onNone, onRelative, onFixed) => subject.fold(onNone, onRelative, onFixed);\n    adt$9.none;\n    const relative$1 = adt$9.relative;\n    const fixed$1 = adt$9.fixed;\n\n    const anchor = (anchorBox, origin) => ({\n      anchorBox,\n      origin\n    });\n    const box = (anchorBox, origin) => anchor(anchorBox, origin);\n\n    const placementAttribute = 'data-alloy-placement';\n    const setPlacement$1 = (element, placement) => {\n      set$9(element, placementAttribute, placement);\n    };\n    const getPlacement = element => getOpt(element, placementAttribute);\n    const reset$2 = element => remove$7(element, placementAttribute);\n\n    const adt$8 = Adt.generate([\n      { fit: ['reposition'] },\n      {\n        nofit: [\n          'reposition',\n          'visibleW',\n          'visibleH',\n          'isVisible'\n        ]\n      }\n    ]);\n    const determinePosition = (box, bounds) => {\n      const {\n        x: boundsX,\n        y: boundsY,\n        right: boundsRight,\n        bottom: boundsBottom\n      } = bounds;\n      const {x, y, right, bottom, width, height} = box;\n      const xInBounds = x >= boundsX && x <= boundsRight;\n      const yInBounds = y >= boundsY && y <= boundsBottom;\n      const originInBounds = xInBounds && yInBounds;\n      const rightInBounds = right <= boundsRight && right >= boundsX;\n      const bottomInBounds = bottom <= boundsBottom && bottom >= boundsY;\n      const sizeInBounds = rightInBounds && bottomInBounds;\n      const visibleW = Math.min(width, x >= boundsX ? boundsRight - x : right - boundsX);\n      const visibleH = Math.min(height, y >= boundsY ? boundsBottom - y : bottom - boundsY);\n      return {\n        originInBounds,\n        sizeInBounds,\n        visibleW,\n        visibleH\n      };\n    };\n    const calcReposition = (box, bounds$1) => {\n      const {\n        x: boundsX,\n        y: boundsY,\n        right: boundsRight,\n        bottom: boundsBottom\n      } = bounds$1;\n      const {x, y, width, height} = box;\n      const maxX = Math.max(boundsX, boundsRight - width);\n      const maxY = Math.max(boundsY, boundsBottom - height);\n      const restrictedX = clamp(x, boundsX, maxX);\n      const restrictedY = clamp(y, boundsY, maxY);\n      const restrictedWidth = Math.min(restrictedX + width, boundsRight) - restrictedX;\n      const restrictedHeight = Math.min(restrictedY + height, boundsBottom) - restrictedY;\n      return bounds(restrictedX, restrictedY, restrictedWidth, restrictedHeight);\n    };\n    const calcMaxSizes = (direction, box, bounds) => {\n      const upAvailable = constant$1(box.bottom - bounds.y);\n      const downAvailable = constant$1(bounds.bottom - box.y);\n      const maxHeight = cataVertical(direction, downAvailable, downAvailable, upAvailable);\n      const westAvailable = constant$1(box.right - bounds.x);\n      const eastAvailable = constant$1(bounds.right - box.x);\n      const maxWidth = cataHorizontal(direction, eastAvailable, eastAvailable, westAvailable);\n      return {\n        maxWidth,\n        maxHeight\n      };\n    };\n    const attempt = (candidate, width, height, bounds$1) => {\n      const bubble = candidate.bubble;\n      const bubbleOffset = bubble.offset;\n      const adjustedBounds = adjustBounds(bounds$1, candidate.restriction, bubbleOffset);\n      const newX = candidate.x + bubbleOffset.left;\n      const newY = candidate.y + bubbleOffset.top;\n      const box = bounds(newX, newY, width, height);\n      const {originInBounds, sizeInBounds, visibleW, visibleH} = determinePosition(box, adjustedBounds);\n      const fits = originInBounds && sizeInBounds;\n      const fittedBox = fits ? box : calcReposition(box, adjustedBounds);\n      const isPartlyVisible = fittedBox.width > 0 && fittedBox.height > 0;\n      const {maxWidth, maxHeight} = calcMaxSizes(candidate.direction, fittedBox, bounds$1);\n      const reposition = {\n        rect: fittedBox,\n        maxHeight,\n        maxWidth,\n        direction: candidate.direction,\n        placement: candidate.placement,\n        classes: {\n          on: bubble.classesOn,\n          off: bubble.classesOff\n        },\n        layout: candidate.label,\n        testY: newY\n      };\n      return fits || candidate.alwaysFit ? adt$8.fit(reposition) : adt$8.nofit(reposition, visibleW, visibleH, isPartlyVisible);\n    };\n    const attempts = (element, candidates, anchorBox, elementBox, bubbles, bounds) => {\n      const panelWidth = elementBox.width;\n      const panelHeight = elementBox.height;\n      const attemptBestFit = (layout, reposition, visibleW, visibleH, isVisible) => {\n        const next = layout(anchorBox, elementBox, bubbles, element, bounds);\n        const attemptLayout = attempt(next, panelWidth, panelHeight, bounds);\n        return attemptLayout.fold(constant$1(attemptLayout), (newReposition, newVisibleW, newVisibleH, newIsVisible) => {\n          const improved = isVisible === newIsVisible ? newVisibleH > visibleH || newVisibleW > visibleW : !isVisible && newIsVisible;\n          return improved ? attemptLayout : adt$8.nofit(reposition, visibleW, visibleH, isVisible);\n        });\n      };\n      const abc = foldl(candidates, (b, a) => {\n        const bestNext = curry(attemptBestFit, a);\n        return b.fold(constant$1(b), bestNext);\n      }, adt$8.nofit({\n        rect: anchorBox,\n        maxHeight: elementBox.height,\n        maxWidth: elementBox.width,\n        direction: southeast$3(),\n        placement: 'southeast',\n        classes: {\n          on: [],\n          off: []\n        },\n        layout: 'none',\n        testY: anchorBox.y\n      }, -1, -1, false));\n      return abc.fold(identity, identity);\n    };\n\n    const singleton = doRevoke => {\n      const subject = Cell(Optional.none());\n      const revoke = () => subject.get().each(doRevoke);\n      const clear = () => {\n        revoke();\n        subject.set(Optional.none());\n      };\n      const isSet = () => subject.get().isSome();\n      const get = () => subject.get();\n      const set = s => {\n        revoke();\n        subject.set(Optional.some(s));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const destroyable = () => singleton(s => s.destroy());\n    const unbindable = () => singleton(s => s.unbind());\n    const value$2 = () => {\n      const subject = singleton(noop);\n      const on = f => subject.get().each(f);\n      return {\n        ...subject,\n        on\n      };\n    };\n\n    const filter = always;\n    const bind = (element, event, handler) => bind$2(element, event, filter, handler);\n    const capture = (element, event, handler) => capture$1(element, event, filter, handler);\n    const fromRawEvent = fromRawEvent$1;\n\n    const properties = [\n      'top',\n      'bottom',\n      'right',\n      'left'\n    ];\n    const timerAttr = 'data-alloy-transition-timer';\n    const isTransitioning$1 = (element, transition) => hasAll(element, transition.classes);\n    const shouldApplyTransitionCss = (transition, decision, lastPlacement) => {\n      return lastPlacement.exists(placer => {\n        const mode = transition.mode;\n        return mode === 'all' ? true : placer[mode] !== decision[mode];\n      });\n    };\n    const hasChanges = (position, intermediate) => {\n      const round = value => parseFloat(value).toFixed(3);\n      return find$4(intermediate, (value, key) => {\n        const newValue = position[key].map(round);\n        const val = value.map(round);\n        return !equals(newValue, val);\n      }).isSome();\n    };\n    const getTransitionDuration = element => {\n      const get = name => {\n        const style = get$e(element, name);\n        const times = style.split(/\\s*,\\s*/);\n        return filter$2(times, isNotEmpty);\n      };\n      const parse = value => {\n        if (isString(value) && /^[\\d.]+/.test(value)) {\n          const num = parseFloat(value);\n          return endsWith(value, 'ms') ? num : num * 1000;\n        } else {\n          return 0;\n        }\n      };\n      const delay = get('transition-delay');\n      const duration = get('transition-duration');\n      return foldl(duration, (acc, dur, i) => {\n        const time = parse(delay[i]) + parse(dur);\n        return Math.max(acc, time);\n      }, 0);\n    };\n    const setupTransitionListeners = (element, transition) => {\n      const transitionEnd = unbindable();\n      const transitionCancel = unbindable();\n      let timer;\n      const isSourceTransition = e => {\n        var _a;\n        const pseudoElement = (_a = e.raw.pseudoElement) !== null && _a !== void 0 ? _a : '';\n        return eq(e.target, element) && isEmpty(pseudoElement) && contains$2(properties, e.raw.propertyName);\n      };\n      const transitionDone = e => {\n        if (isNullable(e) || isSourceTransition(e)) {\n          transitionEnd.clear();\n          transitionCancel.clear();\n          const type = e === null || e === void 0 ? void 0 : e.raw.type;\n          if (isNullable(type) || type === transitionend()) {\n            clearTimeout(timer);\n            remove$7(element, timerAttr);\n            remove$1(element, transition.classes);\n          }\n        }\n      };\n      const transitionStart = bind(element, transitionstart(), e => {\n        if (isSourceTransition(e)) {\n          transitionStart.unbind();\n          transitionEnd.set(bind(element, transitionend(), transitionDone));\n          transitionCancel.set(bind(element, transitioncancel(), transitionDone));\n        }\n      });\n      const duration = getTransitionDuration(element);\n      requestAnimationFrame(() => {\n        timer = setTimeout(transitionDone, duration + 17);\n        set$9(element, timerAttr, timer);\n      });\n    };\n    const startTransitioning = (element, transition) => {\n      add$1(element, transition.classes);\n      getOpt(element, timerAttr).each(timerId => {\n        clearTimeout(parseInt(timerId, 10));\n        remove$7(element, timerAttr);\n      });\n      setupTransitionListeners(element, transition);\n    };\n    const applyTransitionCss = (element, origin, position, transition, decision, lastPlacement) => {\n      const shouldTransition = shouldApplyTransitionCss(transition, decision, lastPlacement);\n      if (shouldTransition || isTransitioning$1(element, transition)) {\n        set$8(element, 'position', position.position);\n        const rect = toBox(origin, element);\n        const intermediatePosition = reposition(origin, {\n          ...decision,\n          rect\n        });\n        const intermediateCssOptions = mapToObject(properties, prop => intermediatePosition[prop]);\n        if (hasChanges(position, intermediateCssOptions)) {\n          setOptions(element, intermediateCssOptions);\n          if (shouldTransition) {\n            startTransitioning(element, transition);\n          }\n          reflow(element);\n        }\n      } else {\n        remove$1(element, transition.classes);\n      }\n    };\n\n    const elementSize = p => ({\n      width: getOuter$1(p),\n      height: getOuter$2(p)\n    });\n    const layout = (anchorBox, element, bubbles, options) => {\n      remove$6(element, 'max-height');\n      remove$6(element, 'max-width');\n      const elementBox = elementSize(element);\n      return attempts(element, options.preference, anchorBox, elementBox, bubbles, options.bounds);\n    };\n    const setClasses = (element, decision) => {\n      const classInfo = decision.classes;\n      remove$1(element, classInfo.off);\n      add$1(element, classInfo.on);\n    };\n    const setHeight = (element, decision, options) => {\n      const maxHeightFunction = options.maxHeightFunction;\n      maxHeightFunction(element, decision.maxHeight);\n    };\n    const setWidth = (element, decision, options) => {\n      const maxWidthFunction = options.maxWidthFunction;\n      maxWidthFunction(element, decision.maxWidth);\n    };\n    const position$2 = (element, decision, options) => {\n      const positionCss = reposition(options.origin, decision);\n      options.transition.each(transition => {\n        applyTransitionCss(element, options.origin, positionCss, transition, decision, options.lastPlacement);\n      });\n      applyPositionCss(element, positionCss);\n    };\n    const setPlacement = (element, decision) => {\n      setPlacement$1(element, decision.placement);\n    };\n\n    const setMaxHeight = (element, maxHeight) => {\n      setMax$1(element, Math.floor(maxHeight));\n    };\n    const anchored = constant$1((element, available) => {\n      setMaxHeight(element, available);\n      setAll(element, {\n        'overflow-x': 'hidden',\n        'overflow-y': 'auto'\n      });\n    });\n    const expandable$1 = constant$1((element, available) => {\n      setMaxHeight(element, available);\n    });\n\n    const defaultOr = (options, key, dephault) => options[key] === undefined ? dephault : options[key];\n    const simple = (anchor, element, bubble, layouts, lastPlacement, getBounds, overrideOptions, transition) => {\n      const maxHeightFunction = defaultOr(overrideOptions, 'maxHeightFunction', anchored());\n      const maxWidthFunction = defaultOr(overrideOptions, 'maxWidthFunction', noop);\n      const anchorBox = anchor.anchorBox;\n      const origin = anchor.origin;\n      const options = {\n        bounds: viewport(origin, getBounds),\n        origin,\n        preference: layouts,\n        maxHeightFunction,\n        maxWidthFunction,\n        lastPlacement,\n        transition\n      };\n      return go(anchorBox, element, bubble, options);\n    };\n    const go = (anchorBox, element, bubble, options) => {\n      const decision = layout(anchorBox, element, bubble, options);\n      position$2(element, decision, options);\n      setPlacement(element, decision);\n      setClasses(element, decision);\n      setHeight(element, decision, options);\n      setWidth(element, decision, options);\n      return {\n        layout: decision.layout,\n        placement: decision.placement\n      };\n    };\n\n    const allAlignments = [\n      'valignCentre',\n      'alignLeft',\n      'alignRight',\n      'alignCentre',\n      'top',\n      'bottom',\n      'left',\n      'right',\n      'inset'\n    ];\n    const nu$5 = (xOffset, yOffset, classes, insetModifier = 1) => {\n      const insetXOffset = xOffset * insetModifier;\n      const insetYOffset = yOffset * insetModifier;\n      const getClasses = prop => get$g(classes, prop).getOr([]);\n      const make = (xDelta, yDelta, alignmentsOn) => {\n        const alignmentsOff = difference(allAlignments, alignmentsOn);\n        return {\n          offset: SugarPosition(xDelta, yDelta),\n          classesOn: bind$3(alignmentsOn, getClasses),\n          classesOff: bind$3(alignmentsOff, getClasses)\n        };\n      };\n      return {\n        southeast: () => make(-xOffset, yOffset, [\n          'top',\n          'alignLeft'\n        ]),\n        southwest: () => make(xOffset, yOffset, [\n          'top',\n          'alignRight'\n        ]),\n        south: () => make(-xOffset / 2, yOffset, [\n          'top',\n          'alignCentre'\n        ]),\n        northeast: () => make(-xOffset, -yOffset, [\n          'bottom',\n          'alignLeft'\n        ]),\n        northwest: () => make(xOffset, -yOffset, [\n          'bottom',\n          'alignRight'\n        ]),\n        north: () => make(-xOffset / 2, -yOffset, [\n          'bottom',\n          'alignCentre'\n        ]),\n        east: () => make(xOffset, -yOffset / 2, [\n          'valignCentre',\n          'left'\n        ]),\n        west: () => make(-xOffset, -yOffset / 2, [\n          'valignCentre',\n          'right'\n        ]),\n        insetNortheast: () => make(insetXOffset, insetYOffset, [\n          'top',\n          'alignLeft',\n          'inset'\n        ]),\n        insetNorthwest: () => make(-insetXOffset, insetYOffset, [\n          'top',\n          'alignRight',\n          'inset'\n        ]),\n        insetNorth: () => make(-insetXOffset / 2, insetYOffset, [\n          'top',\n          'alignCentre',\n          'inset'\n        ]),\n        insetSoutheast: () => make(insetXOffset, -insetYOffset, [\n          'bottom',\n          'alignLeft',\n          'inset'\n        ]),\n        insetSouthwest: () => make(-insetXOffset, -insetYOffset, [\n          'bottom',\n          'alignRight',\n          'inset'\n        ]),\n        insetSouth: () => make(-insetXOffset / 2, -insetYOffset, [\n          'bottom',\n          'alignCentre',\n          'inset'\n        ]),\n        insetEast: () => make(-insetXOffset, -insetYOffset / 2, [\n          'valignCentre',\n          'right',\n          'inset'\n        ]),\n        insetWest: () => make(insetXOffset, -insetYOffset / 2, [\n          'valignCentre',\n          'left',\n          'inset'\n        ])\n      };\n    };\n    const fallback = () => nu$5(0, 0, {});\n\n    const nu$4 = identity;\n\n    const onDirection = (isLtr, isRtl) => element => getDirection(element) === 'rtl' ? isRtl : isLtr;\n    const getDirection = element => get$e(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';\n\n    var AttributeValue;\n    (function (AttributeValue) {\n      AttributeValue['TopToBottom'] = 'toptobottom';\n      AttributeValue['BottomToTop'] = 'bottomtotop';\n    }(AttributeValue || (AttributeValue = {})));\n    const Attribute = 'data-alloy-vertical-dir';\n    const isBottomToTopDir = el => closest$2(el, current => isElement$1(current) && get$f(current, 'data-alloy-vertical-dir') === AttributeValue.BottomToTop);\n\n    const schema$y = () => optionObjOf('layouts', [\n      required$1('onLtr'),\n      required$1('onRtl'),\n      option$3('onBottomLtr'),\n      option$3('onBottomRtl')\n    ]);\n    const get$5 = (elem, info, defaultLtr, defaultRtl, defaultBottomLtr, defaultBottomRtl, dirElement) => {\n      const isBottomToTop = dirElement.map(isBottomToTopDir).getOr(false);\n      const customLtr = info.layouts.map(ls => ls.onLtr(elem));\n      const customRtl = info.layouts.map(ls => ls.onRtl(elem));\n      const ltr = isBottomToTop ? info.layouts.bind(ls => ls.onBottomLtr.map(f => f(elem))).or(customLtr).getOr(defaultBottomLtr) : customLtr.getOr(defaultLtr);\n      const rtl = isBottomToTop ? info.layouts.bind(ls => ls.onBottomRtl.map(f => f(elem))).or(customRtl).getOr(defaultBottomRtl) : customRtl.getOr(defaultRtl);\n      const f = onDirection(ltr, rtl);\n      return f(elem);\n    };\n\n    const placement$4 = (component, anchorInfo, origin) => {\n      const hotspot = anchorInfo.hotspot;\n      const anchorBox = toBox(origin, hotspot.element);\n      const layouts = get$5(component.element, anchorInfo, belowOrAbove(), belowOrAboveRtl(), aboveOrBelow(), aboveOrBelowRtl(), Optional.some(anchorInfo.hotspot.element));\n      return Optional.some(nu$4({\n        anchorBox,\n        bubble: anchorInfo.bubble.getOr(fallback()),\n        overrides: anchorInfo.overrides,\n        layouts,\n        placer: Optional.none()\n      }));\n    };\n    var HotspotAnchor = [\n      required$1('hotspot'),\n      option$3('bubble'),\n      defaulted('overrides', {}),\n      schema$y(),\n      output$1('placement', placement$4)\n    ];\n\n    const placement$3 = (component, anchorInfo, origin) => {\n      const pos = translate$2(origin, anchorInfo.x, anchorInfo.y);\n      const anchorBox = bounds(pos.left, pos.top, anchorInfo.width, anchorInfo.height);\n      const layouts = get$5(component.element, anchorInfo, all$1(), allRtl$1(), all$1(), allRtl$1(), Optional.none());\n      return Optional.some(nu$4({\n        anchorBox,\n        bubble: anchorInfo.bubble,\n        overrides: anchorInfo.overrides,\n        layouts,\n        placer: Optional.none()\n      }));\n    };\n    var MakeshiftAnchor = [\n      required$1('x'),\n      required$1('y'),\n      defaulted('height', 0),\n      defaulted('width', 0),\n      defaulted('bubble', fallback()),\n      defaulted('overrides', {}),\n      schema$y(),\n      output$1('placement', placement$3)\n    ];\n\n    const adt$7 = Adt.generate([\n      { screen: ['point'] },\n      {\n        absolute: [\n          'point',\n          'scrollLeft',\n          'scrollTop'\n        ]\n      }\n    ]);\n    const toFixed = pos => pos.fold(identity, (point, scrollLeft, scrollTop) => point.translate(-scrollLeft, -scrollTop));\n    const toAbsolute = pos => pos.fold(identity, identity);\n    const sum = points => foldl(points, (b, a) => b.translate(a.left, a.top), SugarPosition(0, 0));\n    const sumAsFixed = positions => {\n      const points = map$2(positions, toFixed);\n      return sum(points);\n    };\n    const sumAsAbsolute = positions => {\n      const points = map$2(positions, toAbsolute);\n      return sum(points);\n    };\n    const screen = adt$7.screen;\n    const absolute$1 = adt$7.absolute;\n\n    const getOffset = (component, origin, anchorInfo) => {\n      const win = defaultView(anchorInfo.root).dom;\n      const hasSameOwner = frame => {\n        const frameOwner = owner$4(frame);\n        const compOwner = owner$4(component.element);\n        return eq(frameOwner, compOwner);\n      };\n      return Optional.from(win.frameElement).map(SugarElement.fromDom).filter(hasSameOwner).map(absolute$3);\n    };\n    const getRootPoint = (component, origin, anchorInfo) => {\n      const doc = owner$4(component.element);\n      const outerScroll = get$b(doc);\n      const offset = getOffset(component, origin, anchorInfo).getOr(outerScroll);\n      return absolute$1(offset, outerScroll.left, outerScroll.top);\n    };\n\n    const getBox = (left, top, width, height) => {\n      const point = screen(SugarPosition(left, top));\n      return Optional.some(pointed(point, width, height));\n    };\n    const calcNewAnchor = (optBox, rootPoint, anchorInfo, origin, elem) => optBox.map(box => {\n      const points = [\n        rootPoint,\n        box.point\n      ];\n      const topLeft = cata$1(origin, () => sumAsAbsolute(points), () => sumAsAbsolute(points), () => sumAsFixed(points));\n      const anchorBox = rect(topLeft.left, topLeft.top, box.width, box.height);\n      const layoutsLtr = anchorInfo.showAbove ? aboveOrBelow() : belowOrAbove();\n      const layoutsRtl = anchorInfo.showAbove ? aboveOrBelowRtl() : belowOrAboveRtl();\n      const layouts = get$5(elem, anchorInfo, layoutsLtr, layoutsRtl, layoutsLtr, layoutsRtl, Optional.none());\n      return nu$4({\n        anchorBox,\n        bubble: anchorInfo.bubble.getOr(fallback()),\n        overrides: anchorInfo.overrides,\n        layouts,\n        placer: Optional.none()\n      });\n    });\n\n    const placement$2 = (component, anchorInfo, origin) => {\n      const rootPoint = getRootPoint(component, origin, anchorInfo);\n      return anchorInfo.node.filter(inBody).bind(target => {\n        const rect = target.dom.getBoundingClientRect();\n        const nodeBox = getBox(rect.left, rect.top, rect.width, rect.height);\n        const elem = anchorInfo.node.getOr(component.element);\n        return calcNewAnchor(nodeBox, rootPoint, anchorInfo, origin, elem);\n      });\n    };\n    var NodeAnchor = [\n      required$1('node'),\n      required$1('root'),\n      option$3('bubble'),\n      schema$y(),\n      defaulted('overrides', {}),\n      defaulted('showAbove', false),\n      output$1('placement', placement$2)\n    ];\n\n    const zeroWidth = '\\uFEFF';\n    const nbsp = '\\xA0';\n\n    const create$3 = (start, soffset, finish, foffset) => ({\n      start,\n      soffset,\n      finish,\n      foffset\n    });\n    const SimRange = { create: create$3 };\n\n    const adt$6 = Adt.generate([\n      { before: ['element'] },\n      {\n        on: [\n          'element',\n          'offset'\n        ]\n      },\n      { after: ['element'] }\n    ]);\n    const cata = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);\n    const getStart$1 = situ => situ.fold(identity, identity, identity);\n    const before = adt$6.before;\n    const on$1 = adt$6.on;\n    const after$1 = adt$6.after;\n    const Situ = {\n      before,\n      on: on$1,\n      after: after$1,\n      cata,\n      getStart: getStart$1\n    };\n\n    const adt$5 = Adt.generate([\n      { domRange: ['rng'] },\n      {\n        relative: [\n          'startSitu',\n          'finishSitu'\n        ]\n      },\n      {\n        exact: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      }\n    ]);\n    const exactFromRange = simRange => adt$5.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);\n    const getStart = selection => selection.match({\n      domRange: rng => SugarElement.fromDom(rng.startContainer),\n      relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),\n      exact: (start, _soffset, _finish, _foffset) => start\n    });\n    const domRange = adt$5.domRange;\n    const relative = adt$5.relative;\n    const exact = adt$5.exact;\n    const getWin = selection => {\n      const start = getStart(selection);\n      return defaultView(start);\n    };\n    const range$1 = SimRange.create;\n    const SimSelection = {\n      domRange,\n      relative,\n      exact,\n      exactFromRange,\n      getWin,\n      range: range$1\n    };\n\n    const setStart = (rng, situ) => {\n      situ.fold(e => {\n        rng.setStartBefore(e.dom);\n      }, (e, o) => {\n        rng.setStart(e.dom, o);\n      }, e => {\n        rng.setStartAfter(e.dom);\n      });\n    };\n    const setFinish = (rng, situ) => {\n      situ.fold(e => {\n        rng.setEndBefore(e.dom);\n      }, (e, o) => {\n        rng.setEnd(e.dom, o);\n      }, e => {\n        rng.setEndAfter(e.dom);\n      });\n    };\n    const relativeToNative = (win, startSitu, finishSitu) => {\n      const range = win.document.createRange();\n      setStart(range, startSitu);\n      setFinish(range, finishSitu);\n      return range;\n    };\n    const exactToNative = (win, start, soffset, finish, foffset) => {\n      const rng = win.document.createRange();\n      rng.setStart(start.dom, soffset);\n      rng.setEnd(finish.dom, foffset);\n      return rng;\n    };\n    const toRect = rect => ({\n      left: rect.left,\n      top: rect.top,\n      right: rect.right,\n      bottom: rect.bottom,\n      width: rect.width,\n      height: rect.height\n    });\n    const getFirstRect$1 = rng => {\n      const rects = rng.getClientRects();\n      const rect = rects.length > 0 ? rects[0] : rng.getBoundingClientRect();\n      return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();\n    };\n    const getBounds$2 = rng => {\n      const rect = rng.getBoundingClientRect();\n      return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();\n    };\n\n    const adt$4 = Adt.generate([\n      {\n        ltr: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      },\n      {\n        rtl: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      }\n    ]);\n    const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);\n    const getRanges = (win, selection) => selection.match({\n      domRange: rng => {\n        return {\n          ltr: constant$1(rng),\n          rtl: Optional.none\n        };\n      },\n      relative: (startSitu, finishSitu) => {\n        return {\n          ltr: cached(() => relativeToNative(win, startSitu, finishSitu)),\n          rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu)))\n        };\n      },\n      exact: (start, soffset, finish, foffset) => {\n        return {\n          ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),\n          rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, soffset)))\n        };\n      }\n    });\n    const doDiagnose = (win, ranges) => {\n      const rng = ranges.ltr();\n      if (rng.collapsed) {\n        const reversed = ranges.rtl().filter(rev => rev.collapsed === false);\n        return reversed.map(rev => adt$4.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)).getOrThunk(() => fromRange(win, adt$4.ltr, rng));\n      } else {\n        return fromRange(win, adt$4.ltr, rng);\n      }\n    };\n    const diagnose = (win, selection) => {\n      const ranges = getRanges(win, selection);\n      return doDiagnose(win, ranges);\n    };\n    const asLtrRange = (win, selection) => {\n      const diagnosis = diagnose(win, selection);\n      return diagnosis.match({\n        ltr: (start, soffset, finish, foffset) => {\n          const rng = win.document.createRange();\n          rng.setStart(start.dom, soffset);\n          rng.setEnd(finish.dom, foffset);\n          return rng;\n        },\n        rtl: (start, soffset, finish, foffset) => {\n          const rng = win.document.createRange();\n          rng.setStart(finish.dom, foffset);\n          rng.setEnd(start.dom, soffset);\n          return rng;\n        }\n      });\n    };\n    adt$4.ltr;\n    adt$4.rtl;\n\n    const descendants = (scope, selector) => all$3(selector, scope);\n\n    const makeRange = (start, soffset, finish, foffset) => {\n      const doc = owner$4(start);\n      const rng = doc.dom.createRange();\n      rng.setStart(start.dom, soffset);\n      rng.setEnd(finish.dom, foffset);\n      return rng;\n    };\n    const after = (start, soffset, finish, foffset) => {\n      const r = makeRange(start, soffset, finish, foffset);\n      const same = eq(start, finish) && soffset === foffset;\n      return r.collapsed && !same;\n    };\n\n    const getNativeSelection = win => Optional.from(win.getSelection());\n    const readRange = selection => {\n      if (selection.rangeCount > 0) {\n        const firstRng = selection.getRangeAt(0);\n        const lastRng = selection.getRangeAt(selection.rangeCount - 1);\n        return Optional.some(SimRange.create(SugarElement.fromDom(firstRng.startContainer), firstRng.startOffset, SugarElement.fromDom(lastRng.endContainer), lastRng.endOffset));\n      } else {\n        return Optional.none();\n      }\n    };\n    const doGetExact = selection => {\n      if (selection.anchorNode === null || selection.focusNode === null) {\n        return readRange(selection);\n      } else {\n        const anchor = SugarElement.fromDom(selection.anchorNode);\n        const focus = SugarElement.fromDom(selection.focusNode);\n        return after(anchor, selection.anchorOffset, focus, selection.focusOffset) ? Optional.some(SimRange.create(anchor, selection.anchorOffset, focus, selection.focusOffset)) : readRange(selection);\n      }\n    };\n    const getExact = win => getNativeSelection(win).filter(sel => sel.rangeCount > 0).bind(doGetExact);\n    const getFirstRect = (win, selection) => {\n      const rng = asLtrRange(win, selection);\n      return getFirstRect$1(rng);\n    };\n    const getBounds$1 = (win, selection) => {\n      const rng = asLtrRange(win, selection);\n      return getBounds$2(rng);\n    };\n\n    const NodeValue = (is, name) => {\n      const get = element => {\n        if (!is(element)) {\n          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');\n        }\n        return getOption(element).getOr('');\n      };\n      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();\n      const set = (element, value) => {\n        if (!is(element)) {\n          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');\n        }\n        element.dom.nodeValue = value;\n      };\n      return {\n        get,\n        getOption,\n        set\n      };\n    };\n\n    const api = NodeValue(isText, 'text');\n    const get$4 = element => api.get(element);\n\n    const point = (element, offset) => ({\n      element,\n      offset\n    });\n    const descendOnce$1 = (element, offset) => {\n      const children$1 = children(element);\n      if (children$1.length === 0) {\n        return point(element, offset);\n      } else if (offset < children$1.length) {\n        return point(children$1[offset], 0);\n      } else {\n        const last = children$1[children$1.length - 1];\n        const len = isText(last) ? get$4(last).length : children(last).length;\n        return point(last, len);\n      }\n    };\n\n    const descendOnce = (element, offset) => isText(element) ? point(element, offset) : descendOnce$1(element, offset);\n    const getAnchorSelection = (win, anchorInfo) => {\n      const getSelection = anchorInfo.getSelection.getOrThunk(() => () => getExact(win));\n      return getSelection().map(sel => {\n        const modStart = descendOnce(sel.start, sel.soffset);\n        const modFinish = descendOnce(sel.finish, sel.foffset);\n        return SimSelection.range(modStart.element, modStart.offset, modFinish.element, modFinish.offset);\n      });\n    };\n    const placement$1 = (component, anchorInfo, origin) => {\n      const win = defaultView(anchorInfo.root).dom;\n      const rootPoint = getRootPoint(component, origin, anchorInfo);\n      const selectionBox = getAnchorSelection(win, anchorInfo).bind(sel => {\n        const optRect = getBounds$1(win, SimSelection.exactFromRange(sel)).orThunk(() => {\n          const x = SugarElement.fromText(zeroWidth);\n          before$1(sel.start, x);\n          const rect = getFirstRect(win, SimSelection.exact(x, 0, x, 1));\n          remove$5(x);\n          return rect;\n        });\n        return optRect.bind(rawRect => getBox(rawRect.left, rawRect.top, rawRect.width, rawRect.height));\n      });\n      const targetElement = getAnchorSelection(win, anchorInfo).bind(sel => isElement$1(sel.start) ? Optional.some(sel.start) : parentElement(sel.start));\n      const elem = targetElement.getOr(component.element);\n      return calcNewAnchor(selectionBox, rootPoint, anchorInfo, origin, elem);\n    };\n    var SelectionAnchor = [\n      option$3('getSelection'),\n      required$1('root'),\n      option$3('bubble'),\n      schema$y(),\n      defaulted('overrides', {}),\n      defaulted('showAbove', false),\n      output$1('placement', placement$1)\n    ];\n\n    const labelPrefix$1 = 'link-layout';\n    const eastX = anchor => anchor.x + anchor.width;\n    const westX = (anchor, element) => anchor.x - element.width;\n    const northY$1 = (anchor, element) => anchor.y - element.height + anchor.height;\n    const southY$1 = anchor => anchor.y;\n    const southeast$1 = (anchor, element, bubbles) => nu$6(eastX(anchor), southY$1(anchor), bubbles.southeast(), southeast$3(), 'southeast', boundsRestriction(anchor, {\n      left: 0,\n      top: 2\n    }), labelPrefix$1);\n    const southwest$1 = (anchor, element, bubbles) => nu$6(westX(anchor, element), southY$1(anchor), bubbles.southwest(), southwest$3(), 'southwest', boundsRestriction(anchor, {\n      right: 1,\n      top: 2\n    }), labelPrefix$1);\n    const northeast$1 = (anchor, element, bubbles) => nu$6(eastX(anchor), northY$1(anchor, element), bubbles.northeast(), northeast$3(), 'northeast', boundsRestriction(anchor, {\n      left: 0,\n      bottom: 3\n    }), labelPrefix$1);\n    const northwest$1 = (anchor, element, bubbles) => nu$6(westX(anchor, element), northY$1(anchor, element), bubbles.northwest(), northwest$3(), 'northwest', boundsRestriction(anchor, {\n      right: 1,\n      bottom: 3\n    }), labelPrefix$1);\n    const all = () => [\n      southeast$1,\n      southwest$1,\n      northeast$1,\n      northwest$1\n    ];\n    const allRtl = () => [\n      southwest$1,\n      southeast$1,\n      northwest$1,\n      northeast$1\n    ];\n\n    const placement = (component, submenuInfo, origin) => {\n      const anchorBox = toBox(origin, submenuInfo.item.element);\n      const layouts = get$5(component.element, submenuInfo, all(), allRtl(), all(), allRtl(), Optional.none());\n      return Optional.some(nu$4({\n        anchorBox,\n        bubble: fallback(),\n        overrides: submenuInfo.overrides,\n        layouts,\n        placer: Optional.none()\n      }));\n    };\n    var SubmenuAnchor = [\n      required$1('item'),\n      schema$y(),\n      defaulted('overrides', {}),\n      output$1('placement', placement)\n    ];\n\n    var AnchorSchema = choose$1('type', {\n      selection: SelectionAnchor,\n      node: NodeAnchor,\n      hotspot: HotspotAnchor,\n      submenu: SubmenuAnchor,\n      makeshift: MakeshiftAnchor\n    });\n\n    const TransitionSchema = [\n      requiredArrayOf('classes', string),\n      defaultedStringEnum('mode', 'all', [\n        'all',\n        'layout',\n        'placement'\n      ])\n    ];\n    const PositionSchema = [\n      defaulted('useFixed', never),\n      option$3('getBounds')\n    ];\n    const PlacementSchema = [\n      requiredOf('anchor', AnchorSchema),\n      optionObjOf('transition', TransitionSchema)\n    ];\n\n    const getFixedOrigin = () => {\n      const html = document.documentElement;\n      return fixed$1(0, 0, html.clientWidth, html.clientHeight);\n    };\n    const getRelativeOrigin = component => {\n      const position = absolute$3(component.element);\n      const bounds = component.element.dom.getBoundingClientRect();\n      return relative$1(position.left, position.top, bounds.width, bounds.height);\n    };\n    const place = (component, origin, anchoring, getBounds, placee, lastPlace, transition) => {\n      const anchor = box(anchoring.anchorBox, origin);\n      return simple(anchor, placee.element, anchoring.bubble, anchoring.layouts, lastPlace, getBounds, anchoring.overrides, transition);\n    };\n    const position$1 = (component, posConfig, posState, placee, placementSpec) => {\n      positionWithin(component, posConfig, posState, placee, placementSpec, Optional.none());\n    };\n    const positionWithin = (component, posConfig, posState, placee, placementSpec, boxElement) => {\n      const boundsBox = boxElement.map(box$1);\n      return positionWithinBounds(component, posConfig, posState, placee, placementSpec, boundsBox);\n    };\n    const positionWithinBounds = (component, posConfig, posState, placee, placementSpec, bounds) => {\n      const placeeDetail = asRawOrDie$1('placement.info', objOf(PlacementSchema), placementSpec);\n      const anchorage = placeeDetail.anchor;\n      const element = placee.element;\n      const placeeState = posState.get(placee.uid);\n      preserve$1(() => {\n        set$8(element, 'position', 'fixed');\n        const oldVisibility = getRaw(element, 'visibility');\n        set$8(element, 'visibility', 'hidden');\n        const origin = posConfig.useFixed() ? getFixedOrigin() : getRelativeOrigin(component);\n        const placer = anchorage.placement;\n        const getBounds = bounds.map(constant$1).or(posConfig.getBounds);\n        placer(component, anchorage, origin).each(anchoring => {\n          const doPlace = anchoring.placer.getOr(place);\n          const newState = doPlace(component, origin, anchoring, getBounds, placee, placeeState, placeeDetail.transition);\n          posState.set(placee.uid, newState);\n        });\n        oldVisibility.fold(() => {\n          remove$6(element, 'visibility');\n        }, vis => {\n          set$8(element, 'visibility', vis);\n        });\n        if (getRaw(element, 'left').isNone() && getRaw(element, 'top').isNone() && getRaw(element, 'right').isNone() && getRaw(element, 'bottom').isNone() && is$1(getRaw(element, 'position'), 'fixed')) {\n          remove$6(element, 'position');\n        }\n      }, element);\n    };\n    const getMode = (component, pConfig, _pState) => pConfig.useFixed() ? 'fixed' : 'absolute';\n    const reset$1 = (component, pConfig, posState, placee) => {\n      const element = placee.element;\n      each$1([\n        'position',\n        'left',\n        'right',\n        'top',\n        'bottom'\n      ], prop => remove$6(element, prop));\n      reset$2(element);\n      posState.clear(placee.uid);\n    };\n\n    var PositionApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        position: position$1,\n        positionWithin: positionWithin,\n        positionWithinBounds: positionWithinBounds,\n        getMode: getMode,\n        reset: reset$1\n    });\n\n    const init$g = () => {\n      let state = {};\n      const set = (id, data) => {\n        state[id] = data;\n      };\n      const get = id => get$g(state, id);\n      const clear = id => {\n        if (isNonNullable(id)) {\n          delete state[id];\n        } else {\n          state = {};\n        }\n      };\n      return nu$8({\n        readState: () => state,\n        clear,\n        set,\n        get\n      });\n    };\n\n    var PositioningState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$g\n    });\n\n    const Positioning = create$4({\n      fields: PositionSchema,\n      name: 'positioning',\n      active: ActivePosition,\n      apis: PositionApis,\n      state: PositioningState\n    });\n\n    const isConnected = comp => comp.getSystem().isConnected();\n    const fireDetaching = component => {\n      emit(component, detachedFromDom());\n      const children = component.components();\n      each$1(children, fireDetaching);\n    };\n    const fireAttaching = component => {\n      const children = component.components();\n      each$1(children, fireAttaching);\n      emit(component, attachedToDom());\n    };\n    const virtualAttach = (parent, child) => {\n      parent.getSystem().addToWorld(child);\n      if (inBody(parent.element)) {\n        fireAttaching(child);\n      }\n    };\n    const virtualDetach = comp => {\n      fireDetaching(comp);\n      comp.getSystem().removeFromWorld(comp);\n    };\n    const attach$1 = (parent, child) => {\n      append$2(parent.element, child.element);\n    };\n    const detachChildren$1 = component => {\n      each$1(component.components(), childComp => remove$5(childComp.element));\n      empty(component.element);\n      component.syncComponents();\n    };\n    const replaceChildren = (component, newSpecs, buildNewChildren) => {\n      const subs = component.components();\n      detachChildren$1(component);\n      const newChildren = buildNewChildren(newSpecs);\n      const deleted = difference(subs, newChildren);\n      each$1(deleted, comp => {\n        fireDetaching(comp);\n        component.getSystem().removeFromWorld(comp);\n      });\n      each$1(newChildren, childComp => {\n        if (!isConnected(childComp)) {\n          component.getSystem().addToWorld(childComp);\n          attach$1(component, childComp);\n          if (inBody(component.element)) {\n            fireAttaching(childComp);\n          }\n        } else {\n          attach$1(component, childComp);\n        }\n      });\n      component.syncComponents();\n    };\n    const virtualReplaceChildren = (component, newSpecs, buildNewChildren) => {\n      const subs = component.components();\n      const existingComps = bind$3(newSpecs, spec => getPremade(spec).toArray());\n      each$1(subs, childComp => {\n        if (!contains$2(existingComps, childComp)) {\n          virtualDetach(childComp);\n        }\n      });\n      const newChildren = buildNewChildren(newSpecs);\n      const deleted = difference(subs, newChildren);\n      each$1(deleted, deletedComp => {\n        if (isConnected(deletedComp)) {\n          virtualDetach(deletedComp);\n        }\n      });\n      each$1(newChildren, childComp => {\n        if (!isConnected(childComp)) {\n          virtualAttach(component, childComp);\n        }\n      });\n      component.syncComponents();\n    };\n\n    const attach = (parent, child) => {\n      attachWith(parent, child, append$2);\n    };\n    const attachWith = (parent, child, insertion) => {\n      parent.getSystem().addToWorld(child);\n      insertion(parent.element, child.element);\n      if (inBody(parent.element)) {\n        fireAttaching(child);\n      }\n      parent.syncComponents();\n    };\n    const doDetach = component => {\n      fireDetaching(component);\n      remove$5(component.element);\n      component.getSystem().removeFromWorld(component);\n    };\n    const detach = component => {\n      const parent$1 = parent(component.element).bind(p => component.getSystem().getByDom(p).toOptional());\n      doDetach(component);\n      parent$1.each(p => {\n        p.syncComponents();\n      });\n    };\n    const detachChildren = component => {\n      const subs = component.components();\n      each$1(subs, doDetach);\n      empty(component.element);\n      component.syncComponents();\n    };\n    const attachSystem = (element, guiSystem) => {\n      attachSystemWith(element, guiSystem, append$2);\n    };\n    const attachSystemAfter = (element, guiSystem) => {\n      attachSystemWith(element, guiSystem, after$2);\n    };\n    const attachSystemWith = (element, guiSystem, inserter) => {\n      inserter(element, guiSystem.element);\n      const children$1 = children(guiSystem.element);\n      each$1(children$1, child => {\n        guiSystem.getByDom(child).each(fireAttaching);\n      });\n    };\n    const detachSystem = guiSystem => {\n      const children$1 = children(guiSystem.element);\n      each$1(children$1, child => {\n        guiSystem.getByDom(child).each(fireDetaching);\n      });\n      remove$5(guiSystem.element);\n    };\n\n    const rebuild = (sandbox, sConfig, sState, data) => {\n      sState.get().each(_data => {\n        detachChildren(sandbox);\n      });\n      const point = sConfig.getAttachPoint(sandbox);\n      attach(point, sandbox);\n      const built = sandbox.getSystem().build(data);\n      attach(sandbox, built);\n      sState.set(built);\n      return built;\n    };\n    const open$1 = (sandbox, sConfig, sState, data) => {\n      const newState = rebuild(sandbox, sConfig, sState, data);\n      sConfig.onOpen(sandbox, newState);\n      return newState;\n    };\n    const setContent = (sandbox, sConfig, sState, data) => sState.get().map(() => rebuild(sandbox, sConfig, sState, data));\n    const openWhileCloaked = (sandbox, sConfig, sState, data, transaction) => {\n      cloak(sandbox, sConfig);\n      open$1(sandbox, sConfig, sState, data);\n      transaction();\n      decloak(sandbox, sConfig);\n    };\n    const close$1 = (sandbox, sConfig, sState) => {\n      sState.get().each(data => {\n        detachChildren(sandbox);\n        detach(sandbox);\n        sConfig.onClose(sandbox, data);\n        sState.clear();\n      });\n    };\n    const isOpen$1 = (_sandbox, _sConfig, sState) => sState.isOpen();\n    const isPartOf = (sandbox, sConfig, sState, queryElem) => isOpen$1(sandbox, sConfig, sState) && sState.get().exists(data => sConfig.isPartOf(sandbox, data, queryElem));\n    const getState$2 = (_sandbox, _sConfig, sState) => sState.get();\n    const store = (sandbox, cssKey, attr, newValue) => {\n      getRaw(sandbox.element, cssKey).fold(() => {\n        remove$7(sandbox.element, attr);\n      }, v => {\n        set$9(sandbox.element, attr, v);\n      });\n      set$8(sandbox.element, cssKey, newValue);\n    };\n    const restore = (sandbox, cssKey, attr) => {\n      getOpt(sandbox.element, attr).fold(() => remove$6(sandbox.element, cssKey), oldValue => set$8(sandbox.element, cssKey, oldValue));\n    };\n    const cloak = (sandbox, sConfig, _sState) => {\n      const sink = sConfig.getAttachPoint(sandbox);\n      set$8(sandbox.element, 'position', Positioning.getMode(sink));\n      store(sandbox, 'visibility', sConfig.cloakVisibilityAttr, 'hidden');\n    };\n    const hasPosition = element => exists([\n      'top',\n      'left',\n      'right',\n      'bottom'\n    ], pos => getRaw(element, pos).isSome());\n    const decloak = (sandbox, sConfig, _sState) => {\n      if (!hasPosition(sandbox.element)) {\n        remove$6(sandbox.element, 'position');\n      }\n      restore(sandbox, 'visibility', sConfig.cloakVisibilityAttr);\n    };\n\n    var SandboxApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        cloak: cloak,\n        decloak: decloak,\n        open: open$1,\n        openWhileCloaked: openWhileCloaked,\n        close: close$1,\n        isOpen: isOpen$1,\n        isPartOf: isPartOf,\n        getState: getState$2,\n        setContent: setContent\n    });\n\n    const events$g = (sandboxConfig, sandboxState) => derive$2([run$1(sandboxClose(), (sandbox, _simulatedEvent) => {\n        close$1(sandbox, sandboxConfig, sandboxState);\n      })]);\n\n    var ActiveSandbox = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$g\n    });\n\n    var SandboxSchema = [\n      onHandler('onOpen'),\n      onHandler('onClose'),\n      required$1('isPartOf'),\n      required$1('getAttachPoint'),\n      defaulted('cloakVisibilityAttr', 'data-precloak-visibility')\n    ];\n\n    const init$f = () => {\n      const contents = value$2();\n      const readState = constant$1('not-implemented');\n      return nu$8({\n        readState,\n        isOpen: contents.isSet,\n        clear: contents.clear,\n        set: contents.set,\n        get: contents.get\n      });\n    };\n\n    var SandboxState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$f\n    });\n\n    const Sandboxing = create$4({\n      fields: SandboxSchema,\n      name: 'sandboxing',\n      active: ActiveSandbox,\n      apis: SandboxApis,\n      state: SandboxState\n    });\n\n    const dismissPopups = constant$1('dismiss.popups');\n    const repositionPopups = constant$1('reposition.popups');\n    const mouseReleased = constant$1('mouse.released');\n\n    const schema$x = objOfOnly([\n      defaulted('isExtraPart', never),\n      optionObjOf('fireEventInstead', [defaulted('event', dismissRequested())])\n    ]);\n    const receivingChannel$1 = rawSpec => {\n      const detail = asRawOrDie$1('Dismissal', schema$x, rawSpec);\n      return {\n        [dismissPopups()]: {\n          schema: objOfOnly([required$1('target')]),\n          onReceive: (sandbox, data) => {\n            if (Sandboxing.isOpen(sandbox)) {\n              const isPart = Sandboxing.isPartOf(sandbox, data.target) || detail.isExtraPart(sandbox, data.target);\n              if (!isPart) {\n                detail.fireEventInstead.fold(() => Sandboxing.close(sandbox), fe => emit(sandbox, fe.event));\n              }\n            }\n          }\n        }\n      };\n    };\n\n    const schema$w = objOfOnly([\n      optionObjOf('fireEventInstead', [defaulted('event', repositionRequested())]),\n      requiredFunction('doReposition')\n    ]);\n    const receivingChannel = rawSpec => {\n      const detail = asRawOrDie$1('Reposition', schema$w, rawSpec);\n      return {\n        [repositionPopups()]: {\n          onReceive: sandbox => {\n            if (Sandboxing.isOpen(sandbox)) {\n              detail.fireEventInstead.fold(() => detail.doReposition(sandbox), fe => emit(sandbox, fe.event));\n            }\n          }\n        }\n      };\n    };\n\n    const onLoad$5 = (component, repConfig, repState) => {\n      repConfig.store.manager.onLoad(component, repConfig, repState);\n    };\n    const onUnload$2 = (component, repConfig, repState) => {\n      repConfig.store.manager.onUnload(component, repConfig, repState);\n    };\n    const setValue$3 = (component, repConfig, repState, data) => {\n      repConfig.store.manager.setValue(component, repConfig, repState, data);\n    };\n    const getValue$3 = (component, repConfig, repState) => repConfig.store.manager.getValue(component, repConfig, repState);\n    const getState$1 = (component, repConfig, repState) => repState;\n\n    var RepresentApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        onLoad: onLoad$5,\n        onUnload: onUnload$2,\n        setValue: setValue$3,\n        getValue: getValue$3,\n        getState: getState$1\n    });\n\n    const events$f = (repConfig, repState) => {\n      const es = repConfig.resetOnDom ? [\n        runOnAttached((comp, _se) => {\n          onLoad$5(comp, repConfig, repState);\n        }),\n        runOnDetached((comp, _se) => {\n          onUnload$2(comp, repConfig, repState);\n        })\n      ] : [loadEvent(repConfig, repState, onLoad$5)];\n      return derive$2(es);\n    };\n\n    var ActiveRepresenting = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$f\n    });\n\n    const memory$1 = () => {\n      const data = Cell(null);\n      const readState = () => ({\n        mode: 'memory',\n        value: data.get()\n      });\n      const isNotSet = () => data.get() === null;\n      const clear = () => {\n        data.set(null);\n      };\n      return nu$8({\n        set: data.set,\n        get: data.get,\n        isNotSet,\n        clear,\n        readState\n      });\n    };\n    const manual = () => {\n      const readState = noop;\n      return nu$8({ readState });\n    };\n    const dataset = () => {\n      const dataByValue = Cell({});\n      const dataByText = Cell({});\n      const readState = () => ({\n        mode: 'dataset',\n        dataByValue: dataByValue.get(),\n        dataByText: dataByText.get()\n      });\n      const clear = () => {\n        dataByValue.set({});\n        dataByText.set({});\n      };\n      const lookup = itemString => get$g(dataByValue.get(), itemString).orThunk(() => get$g(dataByText.get(), itemString));\n      const update = items => {\n        const currentDataByValue = dataByValue.get();\n        const currentDataByText = dataByText.get();\n        const newDataByValue = {};\n        const newDataByText = {};\n        each$1(items, item => {\n          newDataByValue[item.value] = item;\n          get$g(item, 'meta').each(meta => {\n            get$g(meta, 'text').each(text => {\n              newDataByText[text] = item;\n            });\n          });\n        });\n        dataByValue.set({\n          ...currentDataByValue,\n          ...newDataByValue\n        });\n        dataByText.set({\n          ...currentDataByText,\n          ...newDataByText\n        });\n      };\n      return nu$8({\n        readState,\n        lookup,\n        update,\n        clear\n      });\n    };\n    const init$e = spec => spec.store.manager.state(spec);\n\n    var RepresentState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        memory: memory$1,\n        dataset: dataset,\n        manual: manual,\n        init: init$e\n    });\n\n    const setValue$2 = (component, repConfig, repState, data) => {\n      const store = repConfig.store;\n      repState.update([data]);\n      store.setValue(component, data);\n      repConfig.onSetValue(component, data);\n    };\n    const getValue$2 = (component, repConfig, repState) => {\n      const store = repConfig.store;\n      const key = store.getDataKey(component);\n      return repState.lookup(key).getOrThunk(() => store.getFallbackEntry(key));\n    };\n    const onLoad$4 = (component, repConfig, repState) => {\n      const store = repConfig.store;\n      store.initialValue.each(data => {\n        setValue$2(component, repConfig, repState, data);\n      });\n    };\n    const onUnload$1 = (component, repConfig, repState) => {\n      repState.clear();\n    };\n    var DatasetStore = [\n      option$3('initialValue'),\n      required$1('getFallbackEntry'),\n      required$1('getDataKey'),\n      required$1('setValue'),\n      output$1('manager', {\n        setValue: setValue$2,\n        getValue: getValue$2,\n        onLoad: onLoad$4,\n        onUnload: onUnload$1,\n        state: dataset\n      })\n    ];\n\n    const getValue$1 = (component, repConfig, _repState) => repConfig.store.getValue(component);\n    const setValue$1 = (component, repConfig, _repState, data) => {\n      repConfig.store.setValue(component, data);\n      repConfig.onSetValue(component, data);\n    };\n    const onLoad$3 = (component, repConfig, _repState) => {\n      repConfig.store.initialValue.each(data => {\n        repConfig.store.setValue(component, data);\n      });\n    };\n    var ManualStore = [\n      required$1('getValue'),\n      defaulted('setValue', noop),\n      option$3('initialValue'),\n      output$1('manager', {\n        setValue: setValue$1,\n        getValue: getValue$1,\n        onLoad: onLoad$3,\n        onUnload: noop,\n        state: NoState.init\n      })\n    ];\n\n    const setValue = (component, repConfig, repState, data) => {\n      repState.set(data);\n      repConfig.onSetValue(component, data);\n    };\n    const getValue = (component, repConfig, repState) => repState.get();\n    const onLoad$2 = (component, repConfig, repState) => {\n      repConfig.store.initialValue.each(initVal => {\n        if (repState.isNotSet()) {\n          repState.set(initVal);\n        }\n      });\n    };\n    const onUnload = (component, repConfig, repState) => {\n      repState.clear();\n    };\n    var MemoryStore = [\n      option$3('initialValue'),\n      output$1('manager', {\n        setValue,\n        getValue,\n        onLoad: onLoad$2,\n        onUnload,\n        state: memory$1\n      })\n    ];\n\n    var RepresentSchema = [\n      defaultedOf('store', { mode: 'memory' }, choose$1('mode', {\n        memory: MemoryStore,\n        manual: ManualStore,\n        dataset: DatasetStore\n      })),\n      onHandler('onSetValue'),\n      defaulted('resetOnDom', false)\n    ];\n\n    const Representing = create$4({\n      fields: RepresentSchema,\n      name: 'representing',\n      active: ActiveRepresenting,\n      apis: RepresentApis,\n      extra: {\n        setValueFrom: (component, source) => {\n          const value = Representing.getValue(source);\n          Representing.setValue(component, value);\n        }\n      },\n      state: RepresentState\n    });\n\n    const field = (name, forbidden) => defaultedObjOf(name, {}, map$2(forbidden, f => forbid(f.name(), 'Cannot configure ' + f.name() + ' for ' + name)).concat([customField('dump', identity)]));\n    const get$3 = data => data.dump;\n    const augment = (data, original) => ({\n      ...derive$1(original),\n      ...data.dump\n    });\n    const SketchBehaviours = {\n      field,\n      augment,\n      get: get$3\n    };\n\n    const _placeholder = 'placeholder';\n    const adt$3 = Adt.generate([\n      {\n        single: [\n          'required',\n          'valueThunk'\n        ]\n      },\n      {\n        multiple: [\n          'required',\n          'valueThunks'\n        ]\n      }\n    ]);\n    const isSubstituted = spec => has$2(spec, 'uiType');\n    const subPlaceholder = (owner, detail, compSpec, placeholders) => {\n      if (owner.exists(o => o !== compSpec.owner)) {\n        return adt$3.single(true, constant$1(compSpec));\n      }\n      return get$g(placeholders, compSpec.name).fold(() => {\n        throw new Error('Unknown placeholder component: ' + compSpec.name + '\\nKnown: [' + keys(placeholders) + ']\\nNamespace: ' + owner.getOr('none') + '\\nSpec: ' + JSON.stringify(compSpec, null, 2));\n      }, newSpec => newSpec.replace());\n    };\n    const scan = (owner, detail, compSpec, placeholders) => {\n      if (isSubstituted(compSpec) && compSpec.uiType === _placeholder) {\n        return subPlaceholder(owner, detail, compSpec, placeholders);\n      } else {\n        return adt$3.single(false, constant$1(compSpec));\n      }\n    };\n    const substitute = (owner, detail, compSpec, placeholders) => {\n      const base = scan(owner, detail, compSpec, placeholders);\n      return base.fold((req, valueThunk) => {\n        const value = isSubstituted(compSpec) ? valueThunk(detail, compSpec.config, compSpec.validated) : valueThunk(detail);\n        const childSpecs = get$g(value, 'components').getOr([]);\n        const substituted = bind$3(childSpecs, c => substitute(owner, detail, c, placeholders));\n        return [{\n            ...value,\n            components: substituted\n          }];\n      }, (req, valuesThunk) => {\n        if (isSubstituted(compSpec)) {\n          const values = valuesThunk(detail, compSpec.config, compSpec.validated);\n          const preprocessor = compSpec.validated.preprocess.getOr(identity);\n          return preprocessor(values);\n        } else {\n          return valuesThunk(detail);\n        }\n      });\n    };\n    const substituteAll = (owner, detail, components, placeholders) => bind$3(components, c => substitute(owner, detail, c, placeholders));\n    const oneReplace = (label, replacements) => {\n      let called = false;\n      const used = () => called;\n      const replace = () => {\n        if (called) {\n          throw new Error('Trying to use the same placeholder more than once: ' + label);\n        }\n        called = true;\n        return replacements;\n      };\n      const required = () => replacements.fold((req, _) => req, (req, _) => req);\n      return {\n        name: constant$1(label),\n        required,\n        used,\n        replace\n      };\n    };\n    const substitutePlaces = (owner, detail, components, placeholders) => {\n      const ps = map$1(placeholders, (ph, name) => oneReplace(name, ph));\n      const outcome = substituteAll(owner, detail, components, ps);\n      each(ps, p => {\n        if (p.used() === false && p.required()) {\n          throw new Error('Placeholder: ' + p.name() + ' was not found in components list\\nNamespace: ' + owner.getOr('none') + '\\nComponents: ' + JSON.stringify(detail.components, null, 2));\n        }\n      });\n      return outcome;\n    };\n    const single$2 = adt$3.single;\n    const multiple = adt$3.multiple;\n    const placeholder = constant$1(_placeholder);\n\n    const adt$2 = Adt.generate([\n      { required: ['data'] },\n      { external: ['data'] },\n      { optional: ['data'] },\n      { group: ['data'] }\n    ]);\n    const fFactory = defaulted('factory', { sketch: identity });\n    const fSchema = defaulted('schema', []);\n    const fName = required$1('name');\n    const fPname = field$1('pname', 'pname', defaultedThunk(typeSpec => '<alloy.' + generate$6(typeSpec.name) + '>'), anyValue());\n    const fGroupSchema = customField('schema', () => [option$3('preprocess')]);\n    const fDefaults = defaulted('defaults', constant$1({}));\n    const fOverrides = defaulted('overrides', constant$1({}));\n    const requiredSpec = objOf([\n      fFactory,\n      fSchema,\n      fName,\n      fPname,\n      fDefaults,\n      fOverrides\n    ]);\n    const externalSpec = objOf([\n      fFactory,\n      fSchema,\n      fName,\n      fDefaults,\n      fOverrides\n    ]);\n    const optionalSpec = objOf([\n      fFactory,\n      fSchema,\n      fName,\n      fPname,\n      fDefaults,\n      fOverrides\n    ]);\n    const groupSpec = objOf([\n      fFactory,\n      fGroupSchema,\n      fName,\n      required$1('unit'),\n      fPname,\n      fDefaults,\n      fOverrides\n    ]);\n    const asNamedPart = part => {\n      return part.fold(Optional.some, Optional.none, Optional.some, Optional.some);\n    };\n    const name$2 = part => {\n      const get = data => data.name;\n      return part.fold(get, get, get, get);\n    };\n    const asCommon = part => {\n      return part.fold(identity, identity, identity, identity);\n    };\n    const convert = (adtConstructor, partSchema) => spec => {\n      const data = asRawOrDie$1('Converting part type', partSchema, spec);\n      return adtConstructor(data);\n    };\n    const required = convert(adt$2.required, requiredSpec);\n    const external = convert(adt$2.external, externalSpec);\n    const optional = convert(adt$2.optional, optionalSpec);\n    const group = convert(adt$2.group, groupSpec);\n    const original = constant$1('entirety');\n\n    var PartType = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        required: required,\n        external: external,\n        optional: optional,\n        group: group,\n        asNamedPart: asNamedPart,\n        name: name$2,\n        asCommon: asCommon,\n        original: original\n    });\n\n    const combine = (detail, data, partSpec, partValidated) => deepMerge(data.defaults(detail, partSpec, partValidated), partSpec, { uid: detail.partUids[data.name] }, data.overrides(detail, partSpec, partValidated));\n    const subs = (owner, detail, parts) => {\n      const internals = {};\n      const externals = {};\n      each$1(parts, part => {\n        part.fold(data => {\n          internals[data.pname] = single$2(true, (detail, partSpec, partValidated) => data.factory.sketch(combine(detail, data, partSpec, partValidated)));\n        }, data => {\n          const partSpec = detail.parts[data.name];\n          externals[data.name] = constant$1(data.factory.sketch(combine(detail, data, partSpec[original()]), partSpec));\n        }, data => {\n          internals[data.pname] = single$2(false, (detail, partSpec, partValidated) => data.factory.sketch(combine(detail, data, partSpec, partValidated)));\n        }, data => {\n          internals[data.pname] = multiple(true, (detail, _partSpec, _partValidated) => {\n            const units = detail[data.name];\n            return map$2(units, u => data.factory.sketch(deepMerge(data.defaults(detail, u, _partValidated), u, data.overrides(detail, u))));\n          });\n        });\n      });\n      return {\n        internals: constant$1(internals),\n        externals: constant$1(externals)\n      };\n    };\n\n    const generate$3 = (owner, parts) => {\n      const r = {};\n      each$1(parts, part => {\n        asNamedPart(part).each(np => {\n          const g = doGenerateOne(owner, np.pname);\n          r[np.name] = config => {\n            const validated = asRawOrDie$1('Part: ' + np.name + ' in ' + owner, objOf(np.schema), config);\n            return {\n              ...g,\n              config,\n              validated\n            };\n          };\n        });\n      });\n      return r;\n    };\n    const doGenerateOne = (owner, pname) => ({\n      uiType: placeholder(),\n      owner,\n      name: pname\n    });\n    const generateOne$1 = (owner, pname, config) => ({\n      uiType: placeholder(),\n      owner,\n      name: pname,\n      config,\n      validated: {}\n    });\n    const schemas = parts => bind$3(parts, part => part.fold(Optional.none, Optional.some, Optional.none, Optional.none).map(data => requiredObjOf(data.name, data.schema.concat([snapshot(original())]))).toArray());\n    const names = parts => map$2(parts, name$2);\n    const substitutes = (owner, detail, parts) => subs(owner, detail, parts);\n    const components$1 = (owner, detail, internals) => substitutePlaces(Optional.some(owner), detail, detail.components, internals);\n    const getPart = (component, detail, partKey) => {\n      const uid = detail.partUids[partKey];\n      return component.getSystem().getByUid(uid).toOptional();\n    };\n    const getPartOrDie = (component, detail, partKey) => getPart(component, detail, partKey).getOrDie('Could not find part: ' + partKey);\n    const getParts = (component, detail, partKeys) => {\n      const r = {};\n      const uids = detail.partUids;\n      const system = component.getSystem();\n      each$1(partKeys, pk => {\n        r[pk] = constant$1(system.getByUid(uids[pk]));\n      });\n      return r;\n    };\n    const getAllParts = (component, detail) => {\n      const system = component.getSystem();\n      return map$1(detail.partUids, (pUid, _k) => constant$1(system.getByUid(pUid)));\n    };\n    const getAllPartNames = detail => keys(detail.partUids);\n    const getPartsOrDie = (component, detail, partKeys) => {\n      const r = {};\n      const uids = detail.partUids;\n      const system = component.getSystem();\n      each$1(partKeys, pk => {\n        r[pk] = constant$1(system.getByUid(uids[pk]).getOrDie());\n      });\n      return r;\n    };\n    const defaultUids = (baseUid, partTypes) => {\n      const partNames = names(partTypes);\n      return wrapAll(map$2(partNames, pn => ({\n        key: pn,\n        value: baseUid + '-' + pn\n      })));\n    };\n    const defaultUidsSchema = partTypes => field$1('partUids', 'partUids', mergeWithThunk(spec => defaultUids(spec.uid, partTypes)), anyValue());\n\n    var AlloyParts = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        generate: generate$3,\n        generateOne: generateOne$1,\n        schemas: schemas,\n        names: names,\n        substitutes: substitutes,\n        components: components$1,\n        defaultUids: defaultUids,\n        defaultUidsSchema: defaultUidsSchema,\n        getAllParts: getAllParts,\n        getAllPartNames: getAllPartNames,\n        getPart: getPart,\n        getPartOrDie: getPartOrDie,\n        getParts: getParts,\n        getPartsOrDie: getPartsOrDie\n    });\n\n    const base = (partSchemas, partUidsSchemas) => {\n      const ps = partSchemas.length > 0 ? [requiredObjOf('parts', partSchemas)] : [];\n      return ps.concat([\n        required$1('uid'),\n        defaulted('dom', {}),\n        defaulted('components', []),\n        snapshot('originalSpec'),\n        defaulted('debug.sketcher', {})\n      ]).concat(partUidsSchemas);\n    };\n    const asRawOrDie = (label, schema, spec, partSchemas, partUidsSchemas) => {\n      const baseS = base(partSchemas, partUidsSchemas);\n      return asRawOrDie$1(label + ' [SpecSchema]', objOfOnly(baseS.concat(schema)), spec);\n    };\n\n    const single$1 = (owner, schema, factory, spec) => {\n      const specWithUid = supplyUid(spec);\n      const detail = asRawOrDie(owner, schema, specWithUid, [], []);\n      return factory(detail, specWithUid);\n    };\n    const composite$1 = (owner, schema, partTypes, factory, spec) => {\n      const specWithUid = supplyUid(spec);\n      const partSchemas = schemas(partTypes);\n      const partUidsSchema = defaultUidsSchema(partTypes);\n      const detail = asRawOrDie(owner, schema, specWithUid, partSchemas, [partUidsSchema]);\n      const subs = substitutes(owner, detail, partTypes);\n      const components = components$1(owner, detail, subs.internals());\n      return factory(detail, components, specWithUid, subs.externals());\n    };\n    const hasUid = spec => has$2(spec, 'uid');\n    const supplyUid = spec => {\n      return hasUid(spec) ? spec : {\n        ...spec,\n        uid: generate$5('uid')\n      };\n    };\n\n    const isSketchSpec = spec => {\n      return spec.uid !== undefined;\n    };\n    const singleSchema = objOfOnly([\n      required$1('name'),\n      required$1('factory'),\n      required$1('configFields'),\n      defaulted('apis', {}),\n      defaulted('extraApis', {})\n    ]);\n    const compositeSchema = objOfOnly([\n      required$1('name'),\n      required$1('factory'),\n      required$1('configFields'),\n      required$1('partFields'),\n      defaulted('apis', {}),\n      defaulted('extraApis', {})\n    ]);\n    const single = rawConfig => {\n      const config = asRawOrDie$1('Sketcher for ' + rawConfig.name, singleSchema, rawConfig);\n      const sketch = spec => single$1(config.name, config.configFields, config.factory, spec);\n      const apis = map$1(config.apis, makeApi);\n      const extraApis = map$1(config.extraApis, (f, k) => markAsExtraApi(f, k));\n      return {\n        name: config.name,\n        configFields: config.configFields,\n        sketch,\n        ...apis,\n        ...extraApis\n      };\n    };\n    const composite = rawConfig => {\n      const config = asRawOrDie$1('Sketcher for ' + rawConfig.name, compositeSchema, rawConfig);\n      const sketch = spec => composite$1(config.name, config.configFields, config.partFields, config.factory, spec);\n      const parts = generate$3(config.name, config.partFields);\n      const apis = map$1(config.apis, makeApi);\n      const extraApis = map$1(config.extraApis, (f, k) => markAsExtraApi(f, k));\n      return {\n        name: config.name,\n        partFields: config.partFields,\n        configFields: config.configFields,\n        sketch,\n        parts,\n        ...apis,\n        ...extraApis\n      };\n    };\n\n    const inside = target => isTag('input')(target) && get$f(target, 'type') !== 'radio' || isTag('textarea')(target);\n\n    const getCurrent = (component, composeConfig, _composeState) => composeConfig.find(component);\n\n    var ComposeApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        getCurrent: getCurrent\n    });\n\n    const ComposeSchema = [required$1('find')];\n\n    const Composing = create$4({\n      fields: ComposeSchema,\n      name: 'composing',\n      apis: ComposeApis\n    });\n\n    const nativeDisabled = [\n      'input',\n      'button',\n      'textarea',\n      'select'\n    ];\n    const onLoad$1 = (component, disableConfig, disableState) => {\n      const f = disableConfig.disabled() ? disable : enable;\n      f(component, disableConfig);\n    };\n    const hasNative = (component, config) => config.useNative === true && contains$2(nativeDisabled, name$3(component.element));\n    const nativeIsDisabled = component => has$1(component.element, 'disabled');\n    const nativeDisable = component => {\n      set$9(component.element, 'disabled', 'disabled');\n    };\n    const nativeEnable = component => {\n      remove$7(component.element, 'disabled');\n    };\n    const ariaIsDisabled = component => get$f(component.element, 'aria-disabled') === 'true';\n    const ariaDisable = component => {\n      set$9(component.element, 'aria-disabled', 'true');\n    };\n    const ariaEnable = component => {\n      set$9(component.element, 'aria-disabled', 'false');\n    };\n    const disable = (component, disableConfig, _disableState) => {\n      disableConfig.disableClass.each(disableClass => {\n        add$2(component.element, disableClass);\n      });\n      const f = hasNative(component, disableConfig) ? nativeDisable : ariaDisable;\n      f(component);\n      disableConfig.onDisabled(component);\n    };\n    const enable = (component, disableConfig, _disableState) => {\n      disableConfig.disableClass.each(disableClass => {\n        remove$2(component.element, disableClass);\n      });\n      const f = hasNative(component, disableConfig) ? nativeEnable : ariaEnable;\n      f(component);\n      disableConfig.onEnabled(component);\n    };\n    const isDisabled = (component, disableConfig) => hasNative(component, disableConfig) ? nativeIsDisabled(component) : ariaIsDisabled(component);\n    const set$4 = (component, disableConfig, disableState, disabled) => {\n      const f = disabled ? disable : enable;\n      f(component, disableConfig);\n    };\n\n    var DisableApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        enable: enable,\n        disable: disable,\n        isDisabled: isDisabled,\n        onLoad: onLoad$1,\n        set: set$4\n    });\n\n    const exhibit$5 = (base, disableConfig) => nu$7({ classes: disableConfig.disabled() ? disableConfig.disableClass.toArray() : [] });\n    const events$e = (disableConfig, disableState) => derive$2([\n      abort(execute$5(), (component, _simulatedEvent) => isDisabled(component, disableConfig)),\n      loadEvent(disableConfig, disableState, onLoad$1)\n    ]);\n\n    var ActiveDisable = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        exhibit: exhibit$5,\n        events: events$e\n    });\n\n    var DisableSchema = [\n      defaultedFunction('disabled', never),\n      defaulted('useNative', true),\n      option$3('disableClass'),\n      onHandler('onDisabled'),\n      onHandler('onEnabled')\n    ];\n\n    const Disabling = create$4({\n      fields: DisableSchema,\n      name: 'disabling',\n      active: ActiveDisable,\n      apis: DisableApis\n    });\n\n    const dehighlightAllExcept = (component, hConfig, hState, skip) => {\n      const highlighted = descendants(component.element, '.' + hConfig.highlightClass);\n      each$1(highlighted, h => {\n        const shouldSkip = exists(skip, skipComp => eq(skipComp.element, h));\n        if (!shouldSkip) {\n          remove$2(h, hConfig.highlightClass);\n          component.getSystem().getByDom(h).each(target => {\n            hConfig.onDehighlight(component, target);\n            emit(target, dehighlight$1());\n          });\n        }\n      });\n    };\n    const dehighlightAll = (component, hConfig, hState) => dehighlightAllExcept(component, hConfig, hState, []);\n    const dehighlight = (component, hConfig, hState, target) => {\n      if (isHighlighted(component, hConfig, hState, target)) {\n        remove$2(target.element, hConfig.highlightClass);\n        hConfig.onDehighlight(component, target);\n        emit(target, dehighlight$1());\n      }\n    };\n    const highlight = (component, hConfig, hState, target) => {\n      dehighlightAllExcept(component, hConfig, hState, [target]);\n      if (!isHighlighted(component, hConfig, hState, target)) {\n        add$2(target.element, hConfig.highlightClass);\n        hConfig.onHighlight(component, target);\n        emit(target, highlight$1());\n      }\n    };\n    const highlightFirst = (component, hConfig, hState) => {\n      getFirst(component, hConfig).each(firstComp => {\n        highlight(component, hConfig, hState, firstComp);\n      });\n    };\n    const highlightLast = (component, hConfig, hState) => {\n      getLast(component, hConfig).each(lastComp => {\n        highlight(component, hConfig, hState, lastComp);\n      });\n    };\n    const highlightAt = (component, hConfig, hState, index) => {\n      getByIndex(component, hConfig, hState, index).fold(err => {\n        throw err;\n      }, firstComp => {\n        highlight(component, hConfig, hState, firstComp);\n      });\n    };\n    const highlightBy = (component, hConfig, hState, predicate) => {\n      const candidates = getCandidates(component, hConfig);\n      const targetComp = find$5(candidates, predicate);\n      targetComp.each(c => {\n        highlight(component, hConfig, hState, c);\n      });\n    };\n    const isHighlighted = (component, hConfig, hState, queryTarget) => has(queryTarget.element, hConfig.highlightClass);\n    const getHighlighted = (component, hConfig, _hState) => descendant(component.element, '.' + hConfig.highlightClass).bind(e => component.getSystem().getByDom(e).toOptional());\n    const getByIndex = (component, hConfig, hState, index) => {\n      const items = descendants(component.element, '.' + hConfig.itemClass);\n      return Optional.from(items[index]).fold(() => Result.error(new Error('No element found with index ' + index)), component.getSystem().getByDom);\n    };\n    const getFirst = (component, hConfig, _hState) => descendant(component.element, '.' + hConfig.itemClass).bind(e => component.getSystem().getByDom(e).toOptional());\n    const getLast = (component, hConfig, _hState) => {\n      const items = descendants(component.element, '.' + hConfig.itemClass);\n      const last = items.length > 0 ? Optional.some(items[items.length - 1]) : Optional.none();\n      return last.bind(c => component.getSystem().getByDom(c).toOptional());\n    };\n    const getDelta$2 = (component, hConfig, hState, delta) => {\n      const items = descendants(component.element, '.' + hConfig.itemClass);\n      const current = findIndex$1(items, item => has(item, hConfig.highlightClass));\n      return current.bind(selected => {\n        const dest = cycleBy(selected, delta, 0, items.length - 1);\n        return component.getSystem().getByDom(items[dest]).toOptional();\n      });\n    };\n    const getPrevious = (component, hConfig, hState) => getDelta$2(component, hConfig, hState, -1);\n    const getNext = (component, hConfig, hState) => getDelta$2(component, hConfig, hState, +1);\n    const getCandidates = (component, hConfig, _hState) => {\n      const items = descendants(component.element, '.' + hConfig.itemClass);\n      return cat(map$2(items, i => component.getSystem().getByDom(i).toOptional()));\n    };\n\n    var HighlightApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        dehighlightAll: dehighlightAll,\n        dehighlight: dehighlight,\n        highlight: highlight,\n        highlightFirst: highlightFirst,\n        highlightLast: highlightLast,\n        highlightAt: highlightAt,\n        highlightBy: highlightBy,\n        isHighlighted: isHighlighted,\n        getHighlighted: getHighlighted,\n        getFirst: getFirst,\n        getLast: getLast,\n        getPrevious: getPrevious,\n        getNext: getNext,\n        getCandidates: getCandidates\n    });\n\n    var HighlightSchema = [\n      required$1('highlightClass'),\n      required$1('itemClass'),\n      onHandler('onHighlight'),\n      onHandler('onDehighlight')\n    ];\n\n    const Highlighting = create$4({\n      fields: HighlightSchema,\n      name: 'highlighting',\n      apis: HighlightApis\n    });\n\n    const BACKSPACE = [8];\n    const TAB = [9];\n    const ENTER = [13];\n    const ESCAPE = [27];\n    const SPACE = [32];\n    const LEFT = [37];\n    const UP = [38];\n    const RIGHT = [39];\n    const DOWN = [40];\n\n    const cyclePrev = (values, index, predicate) => {\n      const before = reverse(values.slice(0, index));\n      const after = reverse(values.slice(index + 1));\n      return find$5(before.concat(after), predicate);\n    };\n    const tryPrev = (values, index, predicate) => {\n      const before = reverse(values.slice(0, index));\n      return find$5(before, predicate);\n    };\n    const cycleNext = (values, index, predicate) => {\n      const before = values.slice(0, index);\n      const after = values.slice(index + 1);\n      return find$5(after.concat(before), predicate);\n    };\n    const tryNext = (values, index, predicate) => {\n      const after = values.slice(index + 1);\n      return find$5(after, predicate);\n    };\n\n    const inSet = keys => event => {\n      const raw = event.raw;\n      return contains$2(keys, raw.which);\n    };\n    const and = preds => event => forall(preds, pred => pred(event));\n    const isShift = event => {\n      const raw = event.raw;\n      return raw.shiftKey === true;\n    };\n    const isControl = event => {\n      const raw = event.raw;\n      return raw.ctrlKey === true;\n    };\n    const isNotShift = not(isShift);\n\n    const rule = (matches, action) => ({\n      matches,\n      classification: action\n    });\n    const choose = (transitions, event) => {\n      const transition = find$5(transitions, t => t.matches(event));\n      return transition.map(t => t.classification);\n    };\n\n    const reportFocusShifting = (component, prevFocus, newFocus) => {\n      const noChange = prevFocus.exists(p => newFocus.exists(n => eq(n, p)));\n      if (!noChange) {\n        emitWith(component, focusShifted(), {\n          prevFocus,\n          newFocus\n        });\n      }\n    };\n    const dom$2 = () => {\n      const get = component => search(component.element);\n      const set = (component, focusee) => {\n        const prevFocus = get(component);\n        component.getSystem().triggerFocus(focusee, component.element);\n        const newFocus = get(component);\n        reportFocusShifting(component, prevFocus, newFocus);\n      };\n      return {\n        get,\n        set\n      };\n    };\n    const highlights = () => {\n      const get = component => Highlighting.getHighlighted(component).map(item => item.element);\n      const set = (component, element) => {\n        const prevFocus = get(component);\n        component.getSystem().getByDom(element).fold(noop, item => {\n          Highlighting.highlight(component, item);\n        });\n        const newFocus = get(component);\n        reportFocusShifting(component, prevFocus, newFocus);\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    var FocusInsideModes;\n    (function (FocusInsideModes) {\n      FocusInsideModes['OnFocusMode'] = 'onFocus';\n      FocusInsideModes['OnEnterOrSpaceMode'] = 'onEnterOrSpace';\n      FocusInsideModes['OnApiMode'] = 'onApi';\n    }(FocusInsideModes || (FocusInsideModes = {})));\n\n    const typical = (infoSchema, stateInit, getKeydownRules, getKeyupRules, optFocusIn) => {\n      const schema = () => infoSchema.concat([\n        defaulted('focusManager', dom$2()),\n        defaultedOf('focusInside', 'onFocus', valueOf(val => contains$2([\n          'onFocus',\n          'onEnterOrSpace',\n          'onApi'\n        ], val) ? Result.value(val) : Result.error('Invalid value for focusInside'))),\n        output$1('handler', me),\n        output$1('state', stateInit),\n        output$1('sendFocusIn', optFocusIn)\n      ]);\n      const processKey = (component, simulatedEvent, getRules, keyingConfig, keyingState) => {\n        const rules = getRules(component, simulatedEvent, keyingConfig, keyingState);\n        return choose(rules, simulatedEvent.event).bind(rule => rule(component, simulatedEvent, keyingConfig, keyingState));\n      };\n      const toEvents = (keyingConfig, keyingState) => {\n        const onFocusHandler = keyingConfig.focusInside !== FocusInsideModes.OnFocusMode ? Optional.none() : optFocusIn(keyingConfig).map(focusIn => run$1(focus$4(), (component, simulatedEvent) => {\n          focusIn(component, keyingConfig, keyingState);\n          simulatedEvent.stop();\n        }));\n        const tryGoInsideComponent = (component, simulatedEvent) => {\n          const isEnterOrSpace = inSet(SPACE.concat(ENTER))(simulatedEvent.event);\n          if (keyingConfig.focusInside === FocusInsideModes.OnEnterOrSpaceMode && isEnterOrSpace && isSource(component, simulatedEvent)) {\n            optFocusIn(keyingConfig).each(focusIn => {\n              focusIn(component, keyingConfig, keyingState);\n              simulatedEvent.stop();\n            });\n          }\n        };\n        const keyboardEvents = [\n          run$1(keydown(), (component, simulatedEvent) => {\n            processKey(component, simulatedEvent, getKeydownRules, keyingConfig, keyingState).fold(() => {\n              tryGoInsideComponent(component, simulatedEvent);\n            }, _ => {\n              simulatedEvent.stop();\n            });\n          }),\n          run$1(keyup(), (component, simulatedEvent) => {\n            processKey(component, simulatedEvent, getKeyupRules, keyingConfig, keyingState).each(_ => {\n              simulatedEvent.stop();\n            });\n          })\n        ];\n        return derive$2(onFocusHandler.toArray().concat(keyboardEvents));\n      };\n      const me = {\n        schema,\n        processKey,\n        toEvents\n      };\n      return me;\n    };\n\n    const create$2 = cyclicField => {\n      const schema = [\n        option$3('onEscape'),\n        option$3('onEnter'),\n        defaulted('selector', '[data-alloy-tabstop=\"true\"]:not(:disabled)'),\n        defaulted('firstTabstop', 0),\n        defaulted('useTabstopAt', always),\n        option$3('visibilitySelector')\n      ].concat([cyclicField]);\n      const isVisible = (tabbingConfig, element) => {\n        const target = tabbingConfig.visibilitySelector.bind(sel => closest$1(element, sel)).getOr(element);\n        return get$d(target) > 0;\n      };\n      const findInitial = (component, tabbingConfig) => {\n        const tabstops = descendants(component.element, tabbingConfig.selector);\n        const visibles = filter$2(tabstops, elem => isVisible(tabbingConfig, elem));\n        return Optional.from(visibles[tabbingConfig.firstTabstop]);\n      };\n      const findCurrent = (component, tabbingConfig) => tabbingConfig.focusManager.get(component).bind(elem => closest$1(elem, tabbingConfig.selector));\n      const isTabstop = (tabbingConfig, element) => isVisible(tabbingConfig, element) && tabbingConfig.useTabstopAt(element);\n      const focusIn = (component, tabbingConfig, _tabbingState) => {\n        findInitial(component, tabbingConfig).each(target => {\n          tabbingConfig.focusManager.set(component, target);\n        });\n      };\n      const goFromTabstop = (component, tabstops, stopIndex, tabbingConfig, cycle) => cycle(tabstops, stopIndex, elem => isTabstop(tabbingConfig, elem)).fold(() => tabbingConfig.cyclic ? Optional.some(true) : Optional.none(), target => {\n        tabbingConfig.focusManager.set(component, target);\n        return Optional.some(true);\n      });\n      const go = (component, _simulatedEvent, tabbingConfig, cycle) => {\n        const tabstops = descendants(component.element, tabbingConfig.selector);\n        return findCurrent(component, tabbingConfig).bind(tabstop => {\n          const optStopIndex = findIndex$1(tabstops, curry(eq, tabstop));\n          return optStopIndex.bind(stopIndex => goFromTabstop(component, tabstops, stopIndex, tabbingConfig, cycle));\n        });\n      };\n      const goBackwards = (component, simulatedEvent, tabbingConfig) => {\n        const navigate = tabbingConfig.cyclic ? cyclePrev : tryPrev;\n        return go(component, simulatedEvent, tabbingConfig, navigate);\n      };\n      const goForwards = (component, simulatedEvent, tabbingConfig) => {\n        const navigate = tabbingConfig.cyclic ? cycleNext : tryNext;\n        return go(component, simulatedEvent, tabbingConfig, navigate);\n      };\n      const execute = (component, simulatedEvent, tabbingConfig) => tabbingConfig.onEnter.bind(f => f(component, simulatedEvent));\n      const exit = (component, simulatedEvent, tabbingConfig) => tabbingConfig.onEscape.bind(f => f(component, simulatedEvent));\n      const getKeydownRules = constant$1([\n        rule(and([\n          isShift,\n          inSet(TAB)\n        ]), goBackwards),\n        rule(inSet(TAB), goForwards),\n        rule(and([\n          isNotShift,\n          inSet(ENTER)\n        ]), execute)\n      ]);\n      const getKeyupRules = constant$1([rule(inSet(ESCAPE), exit)]);\n      return typical(schema, NoState.init, getKeydownRules, getKeyupRules, () => Optional.some(focusIn));\n    };\n\n    var AcyclicType = create$2(customField('cyclic', never));\n\n    var CyclicType = create$2(customField('cyclic', always));\n\n    const doDefaultExecute = (component, _simulatedEvent, focused) => {\n      dispatch(component, focused, execute$5());\n      return Optional.some(true);\n    };\n    const defaultExecute = (component, simulatedEvent, focused) => {\n      const isComplex = inside(focused) && inSet(SPACE)(simulatedEvent.event);\n      return isComplex ? Optional.none() : doDefaultExecute(component, simulatedEvent, focused);\n    };\n    const stopEventForFirefox = (_component, _simulatedEvent) => Optional.some(true);\n\n    const schema$v = [\n      defaulted('execute', defaultExecute),\n      defaulted('useSpace', false),\n      defaulted('useEnter', true),\n      defaulted('useControlEnter', false),\n      defaulted('useDown', false)\n    ];\n    const execute$4 = (component, simulatedEvent, executeConfig) => executeConfig.execute(component, simulatedEvent, component.element);\n    const getKeydownRules$5 = (component, _simulatedEvent, executeConfig, _executeState) => {\n      const spaceExec = executeConfig.useSpace && !inside(component.element) ? SPACE : [];\n      const enterExec = executeConfig.useEnter ? ENTER : [];\n      const downExec = executeConfig.useDown ? DOWN : [];\n      const execKeys = spaceExec.concat(enterExec).concat(downExec);\n      return [rule(inSet(execKeys), execute$4)].concat(executeConfig.useControlEnter ? [rule(and([\n          isControl,\n          inSet(ENTER)\n        ]), execute$4)] : []);\n    };\n    const getKeyupRules$5 = (component, _simulatedEvent, executeConfig, _executeState) => executeConfig.useSpace && !inside(component.element) ? [rule(inSet(SPACE), stopEventForFirefox)] : [];\n    var ExecutionType = typical(schema$v, NoState.init, getKeydownRules$5, getKeyupRules$5, () => Optional.none());\n\n    const flatgrid$1 = () => {\n      const dimensions = value$2();\n      const setGridSize = (numRows, numColumns) => {\n        dimensions.set({\n          numRows,\n          numColumns\n        });\n      };\n      const getNumRows = () => dimensions.get().map(d => d.numRows);\n      const getNumColumns = () => dimensions.get().map(d => d.numColumns);\n      return nu$8({\n        readState: () => dimensions.get().map(d => ({\n          numRows: String(d.numRows),\n          numColumns: String(d.numColumns)\n        })).getOr({\n          numRows: '?',\n          numColumns: '?'\n        }),\n        setGridSize,\n        getNumRows,\n        getNumColumns\n      });\n    };\n    const init$d = spec => spec.state(spec);\n\n    var KeyingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        flatgrid: flatgrid$1,\n        init: init$d\n    });\n\n    const useH = movement => (component, simulatedEvent, config, state) => {\n      const move = movement(component.element);\n      return use(move, component, simulatedEvent, config, state);\n    };\n    const west$1 = (moveLeft, moveRight) => {\n      const movement = onDirection(moveLeft, moveRight);\n      return useH(movement);\n    };\n    const east$1 = (moveLeft, moveRight) => {\n      const movement = onDirection(moveRight, moveLeft);\n      return useH(movement);\n    };\n    const useV = move => (component, simulatedEvent, config, state) => use(move, component, simulatedEvent, config, state);\n    const use = (move, component, simulatedEvent, config, state) => {\n      const outcome = config.focusManager.get(component).bind(focused => move(component.element, focused, config, state));\n      return outcome.map(newFocus => {\n        config.focusManager.set(component, newFocus);\n        return true;\n      });\n    };\n    const north$1 = useV;\n    const south$1 = useV;\n    const move$1 = useV;\n\n    const isHidden$1 = dom => dom.offsetWidth <= 0 && dom.offsetHeight <= 0;\n    const isVisible = element => !isHidden$1(element.dom);\n\n    const locate = (candidates, predicate) => findIndex$1(candidates, predicate).map(index => ({\n      index,\n      candidates\n    }));\n\n    const locateVisible = (container, current, selector) => {\n      const predicate = x => eq(x, current);\n      const candidates = descendants(container, selector);\n      const visible = filter$2(candidates, isVisible);\n      return locate(visible, predicate);\n    };\n    const findIndex = (elements, target) => findIndex$1(elements, elem => eq(target, elem));\n\n    const withGrid = (values, index, numCols, f) => {\n      const oldRow = Math.floor(index / numCols);\n      const oldColumn = index % numCols;\n      return f(oldRow, oldColumn).bind(address => {\n        const newIndex = address.row * numCols + address.column;\n        return newIndex >= 0 && newIndex < values.length ? Optional.some(values[newIndex]) : Optional.none();\n      });\n    };\n    const cycleHorizontal$1 = (values, index, numRows, numCols, delta) => withGrid(values, index, numCols, (oldRow, oldColumn) => {\n      const onLastRow = oldRow === numRows - 1;\n      const colsInRow = onLastRow ? values.length - oldRow * numCols : numCols;\n      const newColumn = cycleBy(oldColumn, delta, 0, colsInRow - 1);\n      return Optional.some({\n        row: oldRow,\n        column: newColumn\n      });\n    });\n    const cycleVertical$1 = (values, index, numRows, numCols, delta) => withGrid(values, index, numCols, (oldRow, oldColumn) => {\n      const newRow = cycleBy(oldRow, delta, 0, numRows - 1);\n      const onLastRow = newRow === numRows - 1;\n      const colsInRow = onLastRow ? values.length - newRow * numCols : numCols;\n      const newCol = clamp(oldColumn, 0, colsInRow - 1);\n      return Optional.some({\n        row: newRow,\n        column: newCol\n      });\n    });\n    const cycleRight$1 = (values, index, numRows, numCols) => cycleHorizontal$1(values, index, numRows, numCols, +1);\n    const cycleLeft$1 = (values, index, numRows, numCols) => cycleHorizontal$1(values, index, numRows, numCols, -1);\n    const cycleUp$1 = (values, index, numRows, numCols) => cycleVertical$1(values, index, numRows, numCols, -1);\n    const cycleDown$1 = (values, index, numRows, numCols) => cycleVertical$1(values, index, numRows, numCols, +1);\n\n    const schema$u = [\n      required$1('selector'),\n      defaulted('execute', defaultExecute),\n      onKeyboardHandler('onEscape'),\n      defaulted('captureTab', false),\n      initSize()\n    ];\n    const focusIn$3 = (component, gridConfig, _gridState) => {\n      descendant(component.element, gridConfig.selector).each(first => {\n        gridConfig.focusManager.set(component, first);\n      });\n    };\n    const findCurrent$1 = (component, gridConfig) => gridConfig.focusManager.get(component).bind(elem => closest$1(elem, gridConfig.selector));\n    const execute$3 = (component, simulatedEvent, gridConfig, _gridState) => findCurrent$1(component, gridConfig).bind(focused => gridConfig.execute(component, simulatedEvent, focused));\n    const doMove$2 = cycle => (element, focused, gridConfig, gridState) => locateVisible(element, focused, gridConfig.selector).bind(identified => cycle(identified.candidates, identified.index, gridState.getNumRows().getOr(gridConfig.initSize.numRows), gridState.getNumColumns().getOr(gridConfig.initSize.numColumns)));\n    const handleTab = (_component, _simulatedEvent, gridConfig) => gridConfig.captureTab ? Optional.some(true) : Optional.none();\n    const doEscape$1 = (component, simulatedEvent, gridConfig) => gridConfig.onEscape(component, simulatedEvent);\n    const moveLeft$3 = doMove$2(cycleLeft$1);\n    const moveRight$3 = doMove$2(cycleRight$1);\n    const moveNorth$1 = doMove$2(cycleUp$1);\n    const moveSouth$1 = doMove$2(cycleDown$1);\n    const getKeydownRules$4 = constant$1([\n      rule(inSet(LEFT), west$1(moveLeft$3, moveRight$3)),\n      rule(inSet(RIGHT), east$1(moveLeft$3, moveRight$3)),\n      rule(inSet(UP), north$1(moveNorth$1)),\n      rule(inSet(DOWN), south$1(moveSouth$1)),\n      rule(and([\n        isShift,\n        inSet(TAB)\n      ]), handleTab),\n      rule(and([\n        isNotShift,\n        inSet(TAB)\n      ]), handleTab),\n      rule(inSet(SPACE.concat(ENTER)), execute$3)\n    ]);\n    const getKeyupRules$4 = constant$1([\n      rule(inSet(ESCAPE), doEscape$1),\n      rule(inSet(SPACE), stopEventForFirefox)\n    ]);\n    var FlatgridType = typical(schema$u, flatgrid$1, getKeydownRules$4, getKeyupRules$4, () => Optional.some(focusIn$3));\n\n    const horizontal = (container, selector, current, delta) => {\n      const isDisabledButton = candidate => name$3(candidate) === 'button' && get$f(candidate, 'disabled') === 'disabled';\n      const tryCycle = (initial, index, candidates) => {\n        const newIndex = cycleBy(index, delta, 0, candidates.length - 1);\n        if (newIndex === initial) {\n          return Optional.none();\n        } else {\n          return isDisabledButton(candidates[newIndex]) ? tryCycle(initial, newIndex, candidates) : Optional.from(candidates[newIndex]);\n        }\n      };\n      return locateVisible(container, current, selector).bind(identified => {\n        const index = identified.index;\n        const candidates = identified.candidates;\n        return tryCycle(index, index, candidates);\n      });\n    };\n\n    const schema$t = [\n      required$1('selector'),\n      defaulted('getInitial', Optional.none),\n      defaulted('execute', defaultExecute),\n      onKeyboardHandler('onEscape'),\n      defaulted('executeOnMove', false),\n      defaulted('allowVertical', true)\n    ];\n    const findCurrent = (component, flowConfig) => flowConfig.focusManager.get(component).bind(elem => closest$1(elem, flowConfig.selector));\n    const execute$2 = (component, simulatedEvent, flowConfig) => findCurrent(component, flowConfig).bind(focused => flowConfig.execute(component, simulatedEvent, focused));\n    const focusIn$2 = (component, flowConfig, _state) => {\n      flowConfig.getInitial(component).orThunk(() => descendant(component.element, flowConfig.selector)).each(first => {\n        flowConfig.focusManager.set(component, first);\n      });\n    };\n    const moveLeft$2 = (element, focused, info) => horizontal(element, info.selector, focused, -1);\n    const moveRight$2 = (element, focused, info) => horizontal(element, info.selector, focused, +1);\n    const doMove$1 = movement => (component, simulatedEvent, flowConfig, flowState) => movement(component, simulatedEvent, flowConfig, flowState).bind(() => flowConfig.executeOnMove ? execute$2(component, simulatedEvent, flowConfig) : Optional.some(true));\n    const doEscape = (component, simulatedEvent, flowConfig) => flowConfig.onEscape(component, simulatedEvent);\n    const getKeydownRules$3 = (_component, _se, flowConfig, _flowState) => {\n      const westMovers = LEFT.concat(flowConfig.allowVertical ? UP : []);\n      const eastMovers = RIGHT.concat(flowConfig.allowVertical ? DOWN : []);\n      return [\n        rule(inSet(westMovers), doMove$1(west$1(moveLeft$2, moveRight$2))),\n        rule(inSet(eastMovers), doMove$1(east$1(moveLeft$2, moveRight$2))),\n        rule(inSet(ENTER), execute$2),\n        rule(inSet(SPACE), execute$2)\n      ];\n    };\n    const getKeyupRules$3 = constant$1([\n      rule(inSet(SPACE), stopEventForFirefox),\n      rule(inSet(ESCAPE), doEscape)\n    ]);\n    var FlowType = typical(schema$t, NoState.init, getKeydownRules$3, getKeyupRules$3, () => Optional.some(focusIn$2));\n\n    const toCell = (matrix, rowIndex, columnIndex) => Optional.from(matrix[rowIndex]).bind(row => Optional.from(row[columnIndex]).map(cell => ({\n      rowIndex,\n      columnIndex,\n      cell\n    })));\n    const cycleHorizontal = (matrix, rowIndex, startCol, deltaCol) => {\n      const row = matrix[rowIndex];\n      const colsInRow = row.length;\n      const newColIndex = cycleBy(startCol, deltaCol, 0, colsInRow - 1);\n      return toCell(matrix, rowIndex, newColIndex);\n    };\n    const cycleVertical = (matrix, colIndex, startRow, deltaRow) => {\n      const nextRowIndex = cycleBy(startRow, deltaRow, 0, matrix.length - 1);\n      const colsInNextRow = matrix[nextRowIndex].length;\n      const nextColIndex = clamp(colIndex, 0, colsInNextRow - 1);\n      return toCell(matrix, nextRowIndex, nextColIndex);\n    };\n    const moveHorizontal = (matrix, rowIndex, startCol, deltaCol) => {\n      const row = matrix[rowIndex];\n      const colsInRow = row.length;\n      const newColIndex = clamp(startCol + deltaCol, 0, colsInRow - 1);\n      return toCell(matrix, rowIndex, newColIndex);\n    };\n    const moveVertical = (matrix, colIndex, startRow, deltaRow) => {\n      const nextRowIndex = clamp(startRow + deltaRow, 0, matrix.length - 1);\n      const colsInNextRow = matrix[nextRowIndex].length;\n      const nextColIndex = clamp(colIndex, 0, colsInNextRow - 1);\n      return toCell(matrix, nextRowIndex, nextColIndex);\n    };\n    const cycleRight = (matrix, startRow, startCol) => cycleHorizontal(matrix, startRow, startCol, +1);\n    const cycleLeft = (matrix, startRow, startCol) => cycleHorizontal(matrix, startRow, startCol, -1);\n    const cycleUp = (matrix, startRow, startCol) => cycleVertical(matrix, startCol, startRow, -1);\n    const cycleDown = (matrix, startRow, startCol) => cycleVertical(matrix, startCol, startRow, +1);\n    const moveLeft$1 = (matrix, startRow, startCol) => moveHorizontal(matrix, startRow, startCol, -1);\n    const moveRight$1 = (matrix, startRow, startCol) => moveHorizontal(matrix, startRow, startCol, +1);\n    const moveUp$1 = (matrix, startRow, startCol) => moveVertical(matrix, startCol, startRow, -1);\n    const moveDown$1 = (matrix, startRow, startCol) => moveVertical(matrix, startCol, startRow, +1);\n\n    const schema$s = [\n      requiredObjOf('selectors', [\n        required$1('row'),\n        required$1('cell')\n      ]),\n      defaulted('cycles', true),\n      defaulted('previousSelector', Optional.none),\n      defaulted('execute', defaultExecute)\n    ];\n    const focusIn$1 = (component, matrixConfig, _state) => {\n      const focused = matrixConfig.previousSelector(component).orThunk(() => {\n        const selectors = matrixConfig.selectors;\n        return descendant(component.element, selectors.cell);\n      });\n      focused.each(cell => {\n        matrixConfig.focusManager.set(component, cell);\n      });\n    };\n    const execute$1 = (component, simulatedEvent, matrixConfig) => search(component.element).bind(focused => matrixConfig.execute(component, simulatedEvent, focused));\n    const toMatrix = (rows, matrixConfig) => map$2(rows, row => descendants(row, matrixConfig.selectors.cell));\n    const doMove = (ifCycle, ifMove) => (element, focused, matrixConfig) => {\n      const move = matrixConfig.cycles ? ifCycle : ifMove;\n      return closest$1(focused, matrixConfig.selectors.row).bind(inRow => {\n        const cellsInRow = descendants(inRow, matrixConfig.selectors.cell);\n        return findIndex(cellsInRow, focused).bind(colIndex => {\n          const allRows = descendants(element, matrixConfig.selectors.row);\n          return findIndex(allRows, inRow).bind(rowIndex => {\n            const matrix = toMatrix(allRows, matrixConfig);\n            return move(matrix, rowIndex, colIndex).map(next => next.cell);\n          });\n        });\n      });\n    };\n    const moveLeft = doMove(cycleLeft, moveLeft$1);\n    const moveRight = doMove(cycleRight, moveRight$1);\n    const moveNorth = doMove(cycleUp, moveUp$1);\n    const moveSouth = doMove(cycleDown, moveDown$1);\n    const getKeydownRules$2 = constant$1([\n      rule(inSet(LEFT), west$1(moveLeft, moveRight)),\n      rule(inSet(RIGHT), east$1(moveLeft, moveRight)),\n      rule(inSet(UP), north$1(moveNorth)),\n      rule(inSet(DOWN), south$1(moveSouth)),\n      rule(inSet(SPACE.concat(ENTER)), execute$1)\n    ]);\n    const getKeyupRules$2 = constant$1([rule(inSet(SPACE), stopEventForFirefox)]);\n    var MatrixType = typical(schema$s, NoState.init, getKeydownRules$2, getKeyupRules$2, () => Optional.some(focusIn$1));\n\n    const schema$r = [\n      required$1('selector'),\n      defaulted('execute', defaultExecute),\n      defaulted('moveOnTab', false)\n    ];\n    const execute = (component, simulatedEvent, menuConfig) => menuConfig.focusManager.get(component).bind(focused => menuConfig.execute(component, simulatedEvent, focused));\n    const focusIn = (component, menuConfig, _state) => {\n      descendant(component.element, menuConfig.selector).each(first => {\n        menuConfig.focusManager.set(component, first);\n      });\n    };\n    const moveUp = (element, focused, info) => horizontal(element, info.selector, focused, -1);\n    const moveDown = (element, focused, info) => horizontal(element, info.selector, focused, +1);\n    const fireShiftTab = (component, simulatedEvent, menuConfig, menuState) => menuConfig.moveOnTab ? move$1(moveUp)(component, simulatedEvent, menuConfig, menuState) : Optional.none();\n    const fireTab = (component, simulatedEvent, menuConfig, menuState) => menuConfig.moveOnTab ? move$1(moveDown)(component, simulatedEvent, menuConfig, menuState) : Optional.none();\n    const getKeydownRules$1 = constant$1([\n      rule(inSet(UP), move$1(moveUp)),\n      rule(inSet(DOWN), move$1(moveDown)),\n      rule(and([\n        isShift,\n        inSet(TAB)\n      ]), fireShiftTab),\n      rule(and([\n        isNotShift,\n        inSet(TAB)\n      ]), fireTab),\n      rule(inSet(ENTER), execute),\n      rule(inSet(SPACE), execute)\n    ]);\n    const getKeyupRules$1 = constant$1([rule(inSet(SPACE), stopEventForFirefox)]);\n    var MenuType = typical(schema$r, NoState.init, getKeydownRules$1, getKeyupRules$1, () => Optional.some(focusIn));\n\n    const schema$q = [\n      onKeyboardHandler('onSpace'),\n      onKeyboardHandler('onEnter'),\n      onKeyboardHandler('onShiftEnter'),\n      onKeyboardHandler('onLeft'),\n      onKeyboardHandler('onRight'),\n      onKeyboardHandler('onTab'),\n      onKeyboardHandler('onShiftTab'),\n      onKeyboardHandler('onUp'),\n      onKeyboardHandler('onDown'),\n      onKeyboardHandler('onEscape'),\n      defaulted('stopSpaceKeyup', false),\n      option$3('focusIn')\n    ];\n    const getKeydownRules = (component, simulatedEvent, specialInfo) => [\n      rule(inSet(SPACE), specialInfo.onSpace),\n      rule(and([\n        isNotShift,\n        inSet(ENTER)\n      ]), specialInfo.onEnter),\n      rule(and([\n        isShift,\n        inSet(ENTER)\n      ]), specialInfo.onShiftEnter),\n      rule(and([\n        isShift,\n        inSet(TAB)\n      ]), specialInfo.onShiftTab),\n      rule(and([\n        isNotShift,\n        inSet(TAB)\n      ]), specialInfo.onTab),\n      rule(inSet(UP), specialInfo.onUp),\n      rule(inSet(DOWN), specialInfo.onDown),\n      rule(inSet(LEFT), specialInfo.onLeft),\n      rule(inSet(RIGHT), specialInfo.onRight),\n      rule(inSet(SPACE), specialInfo.onSpace)\n    ];\n    const getKeyupRules = (component, simulatedEvent, specialInfo) => [\n      ...specialInfo.stopSpaceKeyup ? [rule(inSet(SPACE), stopEventForFirefox)] : [],\n      rule(inSet(ESCAPE), specialInfo.onEscape)\n    ];\n    var SpecialType = typical(schema$q, NoState.init, getKeydownRules, getKeyupRules, specialInfo => specialInfo.focusIn);\n\n    const acyclic = AcyclicType.schema();\n    const cyclic = CyclicType.schema();\n    const flow = FlowType.schema();\n    const flatgrid = FlatgridType.schema();\n    const matrix = MatrixType.schema();\n    const execution = ExecutionType.schema();\n    const menu = MenuType.schema();\n    const special = SpecialType.schema();\n\n    var KeyboardBranches = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        acyclic: acyclic,\n        cyclic: cyclic,\n        flow: flow,\n        flatgrid: flatgrid,\n        matrix: matrix,\n        execution: execution,\n        menu: menu,\n        special: special\n    });\n\n    const isFlatgridState = keyState => hasNonNullableKey(keyState, 'setGridSize');\n    const Keying = createModes({\n      branchKey: 'mode',\n      branches: KeyboardBranches,\n      name: 'keying',\n      active: {\n        events: (keyingConfig, keyingState) => {\n          const handler = keyingConfig.handler;\n          return handler.toEvents(keyingConfig, keyingState);\n        }\n      },\n      apis: {\n        focusIn: (component, keyConfig, keyState) => {\n          keyConfig.sendFocusIn(keyConfig).fold(() => {\n            component.getSystem().triggerFocus(component.element, component.element);\n          }, sendFocusIn => {\n            sendFocusIn(component, keyConfig, keyState);\n          });\n        },\n        setGridSize: (component, keyConfig, keyState, numRows, numColumns) => {\n          if (!isFlatgridState(keyState)) {\n            console.error('Layout does not support setGridSize');\n          } else {\n            keyState.setGridSize(numRows, numColumns);\n          }\n        }\n      },\n      state: KeyingState\n    });\n\n    const withoutReuse = (parent, data) => {\n      preserve$1(() => {\n        replaceChildren(parent, data, () => map$2(data, parent.getSystem().build));\n      }, parent.element);\n    };\n    const withReuse = (parent, data) => {\n      preserve$1(() => {\n        virtualReplaceChildren(parent, data, () => {\n          return patchSpecChildren(parent.element, data, parent.getSystem().buildOrPatch);\n        });\n      }, parent.element);\n    };\n\n    const virtualReplace = (component, replacee, replaceeIndex, childSpec) => {\n      virtualDetach(replacee);\n      const child = patchSpecChild(component.element, replaceeIndex, childSpec, component.getSystem().buildOrPatch);\n      virtualAttach(component, child);\n      component.syncComponents();\n    };\n    const insert = (component, insertion, childSpec) => {\n      const child = component.getSystem().build(childSpec);\n      attachWith(component, child, insertion);\n    };\n    const replace = (component, replacee, replaceeIndex, childSpec) => {\n      detach(replacee);\n      insert(component, (p, c) => appendAt(p, c, replaceeIndex), childSpec);\n    };\n    const set$3 = (component, replaceConfig, replaceState, data) => {\n      const replacer = replaceConfig.reuseDom ? withReuse : withoutReuse;\n      return replacer(component, data);\n    };\n    const append = (component, replaceConfig, replaceState, appendee) => {\n      insert(component, append$2, appendee);\n    };\n    const prepend = (component, replaceConfig, replaceState, prependee) => {\n      insert(component, prepend$1, prependee);\n    };\n    const remove = (component, replaceConfig, replaceState, removee) => {\n      const children = contents(component);\n      const foundChild = find$5(children, child => eq(removee.element, child.element));\n      foundChild.each(detach);\n    };\n    const contents = (component, _replaceConfig) => component.components();\n    const replaceAt = (component, replaceConfig, replaceState, replaceeIndex, replacer) => {\n      const children = contents(component);\n      return Optional.from(children[replaceeIndex]).map(replacee => {\n        replacer.fold(() => detach(replacee), r => {\n          const replacer = replaceConfig.reuseDom ? virtualReplace : replace;\n          replacer(component, replacee, replaceeIndex, r);\n        });\n        return replacee;\n      });\n    };\n    const replaceBy = (component, replaceConfig, replaceState, replaceePred, replacer) => {\n      const children = contents(component);\n      return findIndex$1(children, replaceePred).bind(replaceeIndex => replaceAt(component, replaceConfig, replaceState, replaceeIndex, replacer));\n    };\n\n    var ReplaceApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        append: append,\n        prepend: prepend,\n        remove: remove,\n        replaceAt: replaceAt,\n        replaceBy: replaceBy,\n        set: set$3,\n        contents: contents\n    });\n\n    const Replacing = create$4({\n      fields: [defaultedBoolean('reuseDom', true)],\n      name: 'replacing',\n      apis: ReplaceApis\n    });\n\n    const events$d = (name, eventHandlers) => {\n      const events = derive$2(eventHandlers);\n      return create$4({\n        fields: [required$1('enabled')],\n        name,\n        active: { events: constant$1(events) }\n      });\n    };\n    const config = (name, eventHandlers) => {\n      const me = events$d(name, eventHandlers);\n      return {\n        key: name,\n        value: {\n          config: {},\n          me,\n          configAsRaw: constant$1({}),\n          initialConfig: {},\n          state: NoState\n        }\n      };\n    };\n\n    const focus$2 = (component, focusConfig) => {\n      if (!focusConfig.ignore) {\n        focus$3(component.element);\n        focusConfig.onFocus(component);\n      }\n    };\n    const blur = (component, focusConfig) => {\n      if (!focusConfig.ignore) {\n        blur$1(component.element);\n      }\n    };\n    const isFocused = component => hasFocus(component.element);\n\n    var FocusApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        focus: focus$2,\n        blur: blur,\n        isFocused: isFocused\n    });\n\n    const exhibit$4 = (base, focusConfig) => {\n      const mod = focusConfig.ignore ? {} : { attributes: { tabindex: '-1' } };\n      return nu$7(mod);\n    };\n    const events$c = focusConfig => derive$2([run$1(focus$4(), (component, simulatedEvent) => {\n        focus$2(component, focusConfig);\n        simulatedEvent.stop();\n      })].concat(focusConfig.stopMousedown ? [run$1(mousedown(), (_, simulatedEvent) => {\n        simulatedEvent.event.prevent();\n      })] : []));\n\n    var ActiveFocus = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        exhibit: exhibit$4,\n        events: events$c\n    });\n\n    var FocusSchema = [\n      onHandler('onFocus'),\n      defaulted('stopMousedown', false),\n      defaulted('ignore', false)\n    ];\n\n    const Focusing = create$4({\n      fields: FocusSchema,\n      name: 'focusing',\n      active: ActiveFocus,\n      apis: FocusApis\n    });\n\n    const SetupBehaviourCellState = initialState => {\n      const init = () => {\n        const cell = Cell(initialState);\n        const get = () => cell.get();\n        const set = newState => cell.set(newState);\n        const clear = () => cell.set(initialState);\n        const readState = () => cell.get();\n        return {\n          get,\n          set,\n          clear,\n          readState\n        };\n      };\n      return { init };\n    };\n\n    const updateAriaState = (component, toggleConfig, toggleState) => {\n      const ariaInfo = toggleConfig.aria;\n      ariaInfo.update(component, ariaInfo, toggleState.get());\n    };\n    const updateClass = (component, toggleConfig, toggleState) => {\n      toggleConfig.toggleClass.each(toggleClass => {\n        if (toggleState.get()) {\n          add$2(component.element, toggleClass);\n        } else {\n          remove$2(component.element, toggleClass);\n        }\n      });\n    };\n    const set$2 = (component, toggleConfig, toggleState, state) => {\n      const initialState = toggleState.get();\n      toggleState.set(state);\n      updateClass(component, toggleConfig, toggleState);\n      updateAriaState(component, toggleConfig, toggleState);\n      if (initialState !== state) {\n        toggleConfig.onToggled(component, state);\n      }\n    };\n    const toggle$2 = (component, toggleConfig, toggleState) => {\n      set$2(component, toggleConfig, toggleState, !toggleState.get());\n    };\n    const on = (component, toggleConfig, toggleState) => {\n      set$2(component, toggleConfig, toggleState, true);\n    };\n    const off = (component, toggleConfig, toggleState) => {\n      set$2(component, toggleConfig, toggleState, false);\n    };\n    const isOn = (component, toggleConfig, toggleState) => toggleState.get();\n    const onLoad = (component, toggleConfig, toggleState) => {\n      set$2(component, toggleConfig, toggleState, toggleConfig.selected);\n    };\n\n    var ToggleApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        onLoad: onLoad,\n        toggle: toggle$2,\n        isOn: isOn,\n        on: on,\n        off: off,\n        set: set$2\n    });\n\n    const exhibit$3 = () => nu$7({});\n    const events$b = (toggleConfig, toggleState) => {\n      const execute = executeEvent(toggleConfig, toggleState, toggle$2);\n      const load = loadEvent(toggleConfig, toggleState, onLoad);\n      return derive$2(flatten([\n        toggleConfig.toggleOnExecute ? [execute] : [],\n        [load]\n      ]));\n    };\n\n    var ActiveToggle = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        exhibit: exhibit$3,\n        events: events$b\n    });\n\n    const updatePressed = (component, ariaInfo, status) => {\n      set$9(component.element, 'aria-pressed', status);\n      if (ariaInfo.syncWithExpanded) {\n        updateExpanded(component, ariaInfo, status);\n      }\n    };\n    const updateSelected = (component, ariaInfo, status) => {\n      set$9(component.element, 'aria-selected', status);\n    };\n    const updateChecked = (component, ariaInfo, status) => {\n      set$9(component.element, 'aria-checked', status);\n    };\n    const updateExpanded = (component, ariaInfo, status) => {\n      set$9(component.element, 'aria-expanded', status);\n    };\n\n    var ToggleSchema = [\n      defaulted('selected', false),\n      option$3('toggleClass'),\n      defaulted('toggleOnExecute', true),\n      onHandler('onToggled'),\n      defaultedOf('aria', { mode: 'none' }, choose$1('mode', {\n        pressed: [\n          defaulted('syncWithExpanded', false),\n          output$1('update', updatePressed)\n        ],\n        checked: [output$1('update', updateChecked)],\n        expanded: [output$1('update', updateExpanded)],\n        selected: [output$1('update', updateSelected)],\n        none: [output$1('update', noop)]\n      }))\n    ];\n\n    const Toggling = create$4({\n      fields: ToggleSchema,\n      name: 'toggling',\n      active: ActiveToggle,\n      apis: ToggleApis,\n      state: SetupBehaviourCellState(false)\n    });\n\n    const pointerEvents = () => {\n      const onClick = (component, simulatedEvent) => {\n        simulatedEvent.stop();\n        emitExecute(component);\n      };\n      return [\n        run$1(click(), onClick),\n        run$1(tap(), onClick),\n        cutter(touchstart()),\n        cutter(mousedown())\n      ];\n    };\n    const events$a = optAction => {\n      const executeHandler = action => runOnExecute$1((component, simulatedEvent) => {\n        action(component);\n        simulatedEvent.stop();\n      });\n      return derive$2(flatten([\n        optAction.map(executeHandler).toArray(),\n        pointerEvents()\n      ]));\n    };\n\n    const hoverEvent = 'alloy.item-hover';\n    const focusEvent = 'alloy.item-focus';\n    const toggledEvent = 'alloy.item-toggled';\n    const onHover = item => {\n      if (search(item.element).isNone() || Focusing.isFocused(item)) {\n        if (!Focusing.isFocused(item)) {\n          Focusing.focus(item);\n        }\n        emitWith(item, hoverEvent, { item });\n      }\n    };\n    const onFocus$1 = item => {\n      emitWith(item, focusEvent, { item });\n    };\n    const onToggled = (item, state) => {\n      emitWith(item, toggledEvent, {\n        item,\n        state\n      });\n    };\n    const hover = constant$1(hoverEvent);\n    const focus$1 = constant$1(focusEvent);\n    const toggled = constant$1(toggledEvent);\n\n    const getItemRole = detail => detail.toggling.map(toggling => toggling.exclusive ? 'menuitemradio' : 'menuitemcheckbox').getOr('menuitem');\n    const getTogglingSpec = tConfig => ({\n      aria: { mode: 'checked' },\n      ...filter$1(tConfig, (_value, name) => name !== 'exclusive'),\n      onToggled: (component, state) => {\n        if (isFunction(tConfig.onToggled)) {\n          tConfig.onToggled(component, state);\n        }\n        onToggled(component, state);\n      }\n    });\n    const builder$2 = detail => ({\n      dom: detail.dom,\n      domModification: {\n        ...detail.domModification,\n        attributes: {\n          'role': getItemRole(detail),\n          ...detail.domModification.attributes,\n          'aria-haspopup': detail.hasSubmenu,\n          ...detail.hasSubmenu ? { 'aria-expanded': false } : {}\n        }\n      },\n      behaviours: SketchBehaviours.augment(detail.itemBehaviours, [\n        detail.toggling.fold(Toggling.revoke, tConfig => Toggling.config(getTogglingSpec(tConfig))),\n        Focusing.config({\n          ignore: detail.ignoreFocus,\n          stopMousedown: detail.ignoreFocus,\n          onFocus: component => {\n            onFocus$1(component);\n          }\n        }),\n        Keying.config({ mode: 'execution' }),\n        Representing.config({\n          store: {\n            mode: 'memory',\n            initialValue: detail.data\n          }\n        }),\n        config('item-type-events', [\n          ...pointerEvents(),\n          run$1(mouseover(), onHover),\n          run$1(focusItem(), Focusing.focus)\n        ])\n      ]),\n      components: detail.components,\n      eventOrder: detail.eventOrder\n    });\n    const schema$p = [\n      required$1('data'),\n      required$1('components'),\n      required$1('dom'),\n      defaulted('hasSubmenu', false),\n      option$3('toggling'),\n      SketchBehaviours.field('itemBehaviours', [\n        Toggling,\n        Focusing,\n        Keying,\n        Representing\n      ]),\n      defaulted('ignoreFocus', false),\n      defaulted('domModification', {}),\n      output$1('builder', builder$2),\n      defaulted('eventOrder', {})\n    ];\n\n    const builder$1 = detail => ({\n      dom: detail.dom,\n      components: detail.components,\n      events: derive$2([stopper(focusItem())])\n    });\n    const schema$o = [\n      required$1('dom'),\n      required$1('components'),\n      output$1('builder', builder$1)\n    ];\n\n    const owner$2 = constant$1('item-widget');\n    const parts$h = constant$1([required({\n        name: 'widget',\n        overrides: detail => {\n          return {\n            behaviours: derive$1([Representing.config({\n                store: {\n                  mode: 'manual',\n                  getValue: _component => {\n                    return detail.data;\n                  },\n                  setValue: noop\n                }\n              })])\n          };\n        }\n      })]);\n\n    const builder = detail => {\n      const subs = substitutes(owner$2(), detail, parts$h());\n      const components = components$1(owner$2(), detail, subs.internals());\n      const focusWidget = component => getPart(component, detail, 'widget').map(widget => {\n        Keying.focusIn(widget);\n        return widget;\n      });\n      const onHorizontalArrow = (component, simulatedEvent) => inside(simulatedEvent.event.target) ? Optional.none() : (() => {\n        if (detail.autofocus) {\n          simulatedEvent.setSource(component.element);\n          return Optional.none();\n        } else {\n          return Optional.none();\n        }\n      })();\n      return {\n        dom: detail.dom,\n        components,\n        domModification: detail.domModification,\n        events: derive$2([\n          runOnExecute$1((component, simulatedEvent) => {\n            focusWidget(component).each(_widget => {\n              simulatedEvent.stop();\n            });\n          }),\n          run$1(mouseover(), onHover),\n          run$1(focusItem(), (component, _simulatedEvent) => {\n            if (detail.autofocus) {\n              focusWidget(component);\n            } else {\n              Focusing.focus(component);\n            }\n          })\n        ]),\n        behaviours: SketchBehaviours.augment(detail.widgetBehaviours, [\n          Representing.config({\n            store: {\n              mode: 'memory',\n              initialValue: detail.data\n            }\n          }),\n          Focusing.config({\n            ignore: detail.ignoreFocus,\n            onFocus: component => {\n              onFocus$1(component);\n            }\n          }),\n          Keying.config({\n            mode: 'special',\n            focusIn: detail.autofocus ? component => {\n              focusWidget(component);\n            } : revoke(),\n            onLeft: onHorizontalArrow,\n            onRight: onHorizontalArrow,\n            onEscape: (component, simulatedEvent) => {\n              if (!Focusing.isFocused(component) && !detail.autofocus) {\n                Focusing.focus(component);\n                return Optional.some(true);\n              } else if (detail.autofocus) {\n                simulatedEvent.setSource(component.element);\n                return Optional.none();\n              } else {\n                return Optional.none();\n              }\n            }\n          })\n        ])\n      };\n    };\n    const schema$n = [\n      required$1('uid'),\n      required$1('data'),\n      required$1('components'),\n      required$1('dom'),\n      defaulted('autofocus', false),\n      defaulted('ignoreFocus', false),\n      SketchBehaviours.field('widgetBehaviours', [\n        Representing,\n        Focusing,\n        Keying\n      ]),\n      defaulted('domModification', {}),\n      defaultUidsSchema(parts$h()),\n      output$1('builder', builder)\n    ];\n\n    const itemSchema$2 = choose$1('type', {\n      widget: schema$n,\n      item: schema$p,\n      separator: schema$o\n    });\n    const configureGrid = (detail, movementInfo) => ({\n      mode: 'flatgrid',\n      selector: '.' + detail.markers.item,\n      initSize: {\n        numColumns: movementInfo.initSize.numColumns,\n        numRows: movementInfo.initSize.numRows\n      },\n      focusManager: detail.focusManager\n    });\n    const configureMatrix = (detail, movementInfo) => ({\n      mode: 'matrix',\n      selectors: {\n        row: movementInfo.rowSelector,\n        cell: '.' + detail.markers.item\n      },\n      previousSelector: movementInfo.previousSelector,\n      focusManager: detail.focusManager\n    });\n    const configureMenu = (detail, movementInfo) => ({\n      mode: 'menu',\n      selector: '.' + detail.markers.item,\n      moveOnTab: movementInfo.moveOnTab,\n      focusManager: detail.focusManager\n    });\n    const parts$g = constant$1([group({\n        factory: {\n          sketch: spec => {\n            const itemInfo = asRawOrDie$1('menu.spec item', itemSchema$2, spec);\n            return itemInfo.builder(itemInfo);\n          }\n        },\n        name: 'items',\n        unit: 'item',\n        defaults: (detail, u) => {\n          return has$2(u, 'uid') ? u : {\n            ...u,\n            uid: generate$5('item')\n          };\n        },\n        overrides: (detail, u) => {\n          return {\n            type: u.type,\n            ignoreFocus: detail.fakeFocus,\n            domModification: { classes: [detail.markers.item] }\n          };\n        }\n      })]);\n    const schema$m = constant$1([\n      required$1('value'),\n      required$1('items'),\n      required$1('dom'),\n      required$1('components'),\n      defaulted('eventOrder', {}),\n      field('menuBehaviours', [\n        Highlighting,\n        Representing,\n        Composing,\n        Keying\n      ]),\n      defaultedOf('movement', {\n        mode: 'menu',\n        moveOnTab: true\n      }, choose$1('mode', {\n        grid: [\n          initSize(),\n          output$1('config', configureGrid)\n        ],\n        matrix: [\n          output$1('config', configureMatrix),\n          required$1('rowSelector'),\n          defaulted('previousSelector', Optional.none)\n        ],\n        menu: [\n          defaulted('moveOnTab', true),\n          output$1('config', configureMenu)\n        ]\n      })),\n      itemMarkers(),\n      defaulted('fakeFocus', false),\n      defaulted('focusManager', dom$2()),\n      onHandler('onHighlight'),\n      onHandler('onDehighlight')\n    ]);\n\n    const focus = constant$1('alloy.menu-focus');\n\n    const deselectOtherRadioItems = (menu, item) => {\n      const checkedRadioItems = descendants(menu.element, '[role=\"menuitemradio\"][aria-checked=\"true\"]');\n      each$1(checkedRadioItems, ele => {\n        if (!eq(ele, item.element)) {\n          menu.getSystem().getByDom(ele).each(c => {\n            Toggling.off(c);\n          });\n        }\n      });\n    };\n    const make$7 = (detail, components, _spec, _externals) => ({\n      uid: detail.uid,\n      dom: detail.dom,\n      markers: detail.markers,\n      behaviours: augment(detail.menuBehaviours, [\n        Highlighting.config({\n          highlightClass: detail.markers.selectedItem,\n          itemClass: detail.markers.item,\n          onHighlight: detail.onHighlight,\n          onDehighlight: detail.onDehighlight\n        }),\n        Representing.config({\n          store: {\n            mode: 'memory',\n            initialValue: detail.value\n          }\n        }),\n        Composing.config({ find: Optional.some }),\n        Keying.config(detail.movement.config(detail, detail.movement))\n      ]),\n      events: derive$2([\n        run$1(focus$1(), (menu, simulatedEvent) => {\n          const event = simulatedEvent.event;\n          menu.getSystem().getByDom(event.target).each(item => {\n            Highlighting.highlight(menu, item);\n            simulatedEvent.stop();\n            emitWith(menu, focus(), {\n              menu,\n              item\n            });\n          });\n        }),\n        run$1(hover(), (menu, simulatedEvent) => {\n          const item = simulatedEvent.event.item;\n          Highlighting.highlight(menu, item);\n        }),\n        run$1(toggled(), (menu, simulatedEvent) => {\n          const {item, state} = simulatedEvent.event;\n          if (state && get$f(item.element, 'role') === 'menuitemradio') {\n            deselectOtherRadioItems(menu, item);\n          }\n        })\n      ]),\n      components,\n      eventOrder: detail.eventOrder,\n      domModification: { attributes: { role: 'menu' } }\n    });\n\n    const Menu = composite({\n      name: 'Menu',\n      configFields: schema$m(),\n      partFields: parts$g(),\n      factory: make$7\n    });\n\n    const transpose$1 = obj => tupleMap(obj, (v, k) => ({\n      k: v,\n      v: k\n    }));\n    const trace = (items, byItem, byMenu, finish) => get$g(byMenu, finish).bind(triggerItem => get$g(items, triggerItem).bind(triggerMenu => {\n      const rest = trace(items, byItem, byMenu, triggerMenu);\n      return Optional.some([triggerMenu].concat(rest));\n    })).getOr([]);\n    const generate$2 = (menus, expansions) => {\n      const items = {};\n      each(menus, (menuItems, menu) => {\n        each$1(menuItems, item => {\n          items[item] = menu;\n        });\n      });\n      const byItem = expansions;\n      const byMenu = transpose$1(expansions);\n      const menuPaths = map$1(byMenu, (_triggerItem, submenu) => [submenu].concat(trace(items, byItem, byMenu, submenu)));\n      return map$1(items, menu => get$g(menuPaths, menu).getOr([menu]));\n    };\n\n    const init$c = () => {\n      const expansions = Cell({});\n      const menus = Cell({});\n      const paths = Cell({});\n      const primary = value$2();\n      const directory = Cell({});\n      const clear = () => {\n        expansions.set({});\n        menus.set({});\n        paths.set({});\n        primary.clear();\n      };\n      const isClear = () => primary.get().isNone();\n      const setMenuBuilt = (menuName, built) => {\n        menus.set({\n          ...menus.get(),\n          [menuName]: {\n            type: 'prepared',\n            menu: built\n          }\n        });\n      };\n      const setContents = (sPrimary, sMenus, sExpansions, dir) => {\n        primary.set(sPrimary);\n        expansions.set(sExpansions);\n        menus.set(sMenus);\n        directory.set(dir);\n        const sPaths = generate$2(dir, sExpansions);\n        paths.set(sPaths);\n      };\n      const getTriggeringItem = menuValue => find$4(expansions.get(), (v, _k) => v === menuValue);\n      const getTriggerData = (menuValue, getItemByValue, path) => getPreparedMenu(menuValue).bind(menu => getTriggeringItem(menuValue).bind(triggeringItemValue => getItemByValue(triggeringItemValue).map(triggeredItem => ({\n        triggeredMenu: menu,\n        triggeringItem: triggeredItem,\n        triggeringPath: path\n      }))));\n      const getTriggeringPath = (itemValue, getItemByValue) => {\n        const extraPath = filter$2(lookupItem(itemValue).toArray(), menuValue => getPreparedMenu(menuValue).isSome());\n        return get$g(paths.get(), itemValue).bind(path => {\n          const revPath = reverse(extraPath.concat(path));\n          const triggers = bind$3(revPath, (menuValue, menuIndex) => getTriggerData(menuValue, getItemByValue, revPath.slice(0, menuIndex + 1)).fold(() => is$1(primary.get(), menuValue) ? [] : [Optional.none()], data => [Optional.some(data)]));\n          return sequence(triggers);\n        });\n      };\n      const expand = itemValue => get$g(expansions.get(), itemValue).map(menu => {\n        const current = get$g(paths.get(), itemValue).getOr([]);\n        return [menu].concat(current);\n      });\n      const collapse = itemValue => get$g(paths.get(), itemValue).bind(path => path.length > 1 ? Optional.some(path.slice(1)) : Optional.none());\n      const refresh = itemValue => get$g(paths.get(), itemValue);\n      const getPreparedMenu = menuValue => lookupMenu(menuValue).bind(extractPreparedMenu);\n      const lookupMenu = menuValue => get$g(menus.get(), menuValue);\n      const lookupItem = itemValue => get$g(expansions.get(), itemValue);\n      const otherMenus = path => {\n        const menuValues = directory.get();\n        return difference(keys(menuValues), path);\n      };\n      const getPrimary = () => primary.get().bind(getPreparedMenu);\n      const getMenus = () => menus.get();\n      return {\n        setMenuBuilt,\n        setContents,\n        expand,\n        refresh,\n        collapse,\n        lookupMenu,\n        lookupItem,\n        otherMenus,\n        getPrimary,\n        getMenus,\n        clear,\n        isClear,\n        getTriggeringPath\n      };\n    };\n    const extractPreparedMenu = prep => prep.type === 'prepared' ? Optional.some(prep.menu) : Optional.none();\n    const LayeredState = {\n      init: init$c,\n      extractPreparedMenu\n    };\n\n    const onMenuItemHighlightedEvent = generate$6('tiered-menu-item-highlight');\n    const onMenuItemDehighlightedEvent = generate$6('tiered-menu-item-dehighlight');\n\n    var HighlightOnOpen;\n    (function (HighlightOnOpen) {\n      HighlightOnOpen[HighlightOnOpen['HighlightMenuAndItem'] = 0] = 'HighlightMenuAndItem';\n      HighlightOnOpen[HighlightOnOpen['HighlightJustMenu'] = 1] = 'HighlightJustMenu';\n      HighlightOnOpen[HighlightOnOpen['HighlightNone'] = 2] = 'HighlightNone';\n    }(HighlightOnOpen || (HighlightOnOpen = {})));\n\n    const make$6 = (detail, _rawUiSpec) => {\n      const submenuParentItems = value$2();\n      const buildMenus = (container, primaryName, menus) => map$1(menus, (spec, name) => {\n        const makeSketch = () => Menu.sketch({\n          ...spec,\n          value: name,\n          markers: detail.markers,\n          fakeFocus: detail.fakeFocus,\n          onHighlight: (menuComp, itemComp) => {\n            const highlightData = {\n              menuComp,\n              itemComp\n            };\n            emitWith(menuComp, onMenuItemHighlightedEvent, highlightData);\n          },\n          onDehighlight: (menuComp, itemComp) => {\n            const dehighlightData = {\n              menuComp,\n              itemComp\n            };\n            emitWith(menuComp, onMenuItemDehighlightedEvent, dehighlightData);\n          },\n          focusManager: detail.fakeFocus ? highlights() : dom$2()\n        });\n        return name === primaryName ? {\n          type: 'prepared',\n          menu: container.getSystem().build(makeSketch())\n        } : {\n          type: 'notbuilt',\n          nbMenu: makeSketch\n        };\n      });\n      const layeredState = LayeredState.init();\n      const setup = container => {\n        const componentMap = buildMenus(container, detail.data.primary, detail.data.menus);\n        const directory = toDirectory();\n        layeredState.setContents(detail.data.primary, componentMap, detail.data.expansions, directory);\n        return layeredState.getPrimary();\n      };\n      const getItemValue = item => Representing.getValue(item).value;\n      const getItemByValue = (_container, menus, itemValue) => findMap(menus, menu => {\n        if (!menu.getSystem().isConnected()) {\n          return Optional.none();\n        }\n        const candidates = Highlighting.getCandidates(menu);\n        return find$5(candidates, c => getItemValue(c) === itemValue);\n      });\n      const toDirectory = _container => map$1(detail.data.menus, (data, _menuName) => bind$3(data.items, item => item.type === 'separator' ? [] : [item.data.value]));\n      const setActiveMenu = Highlighting.highlight;\n      const setActiveMenuAndItem = (container, menu) => {\n        setActiveMenu(container, menu);\n        Highlighting.getHighlighted(menu).orThunk(() => Highlighting.getFirst(menu)).each(item => {\n          if (detail.fakeFocus) {\n            Highlighting.highlight(menu, item);\n          } else {\n            dispatch(container, item.element, focusItem());\n          }\n        });\n      };\n      const getMenus = (state, menuValues) => cat(map$2(menuValues, mv => state.lookupMenu(mv).bind(prep => prep.type === 'prepared' ? Optional.some(prep.menu) : Optional.none())));\n      const closeOthers = (container, state, path) => {\n        const others = getMenus(state, state.otherMenus(path));\n        each$1(others, o => {\n          remove$1(o.element, [detail.markers.backgroundMenu]);\n          if (!detail.stayInDom) {\n            Replacing.remove(container, o);\n          }\n        });\n      };\n      const getSubmenuParents = container => submenuParentItems.get().getOrThunk(() => {\n        const r = {};\n        const items = descendants(container.element, `.${ detail.markers.item }`);\n        const parentItems = filter$2(items, i => get$f(i, 'aria-haspopup') === 'true');\n        each$1(parentItems, i => {\n          container.getSystem().getByDom(i).each(itemComp => {\n            const key = getItemValue(itemComp);\n            r[key] = itemComp;\n          });\n        });\n        submenuParentItems.set(r);\n        return r;\n      });\n      const updateAriaExpansions = (container, path) => {\n        const parentItems = getSubmenuParents(container);\n        each(parentItems, (v, k) => {\n          const expanded = contains$2(path, k);\n          set$9(v.element, 'aria-expanded', expanded);\n        });\n      };\n      const updateMenuPath = (container, state, path) => Optional.from(path[0]).bind(latestMenuName => state.lookupMenu(latestMenuName).bind(menuPrep => {\n        if (menuPrep.type === 'notbuilt') {\n          return Optional.none();\n        } else {\n          const activeMenu = menuPrep.menu;\n          const rest = getMenus(state, path.slice(1));\n          each$1(rest, r => {\n            add$2(r.element, detail.markers.backgroundMenu);\n          });\n          if (!inBody(activeMenu.element)) {\n            Replacing.append(container, premade(activeMenu));\n          }\n          remove$1(activeMenu.element, [detail.markers.backgroundMenu]);\n          setActiveMenuAndItem(container, activeMenu);\n          closeOthers(container, state, path);\n          return Optional.some(activeMenu);\n        }\n      }));\n      let ExpandHighlightDecision;\n      (function (ExpandHighlightDecision) {\n        ExpandHighlightDecision[ExpandHighlightDecision['HighlightSubmenu'] = 0] = 'HighlightSubmenu';\n        ExpandHighlightDecision[ExpandHighlightDecision['HighlightParent'] = 1] = 'HighlightParent';\n      }(ExpandHighlightDecision || (ExpandHighlightDecision = {})));\n      const buildIfRequired = (container, menuName, menuPrep) => {\n        if (menuPrep.type === 'notbuilt') {\n          const menu = container.getSystem().build(menuPrep.nbMenu());\n          layeredState.setMenuBuilt(menuName, menu);\n          return menu;\n        } else {\n          return menuPrep.menu;\n        }\n      };\n      const expandRight = (container, item, decision = ExpandHighlightDecision.HighlightSubmenu) => {\n        if (item.hasConfigured(Disabling) && Disabling.isDisabled(item)) {\n          return Optional.some(item);\n        } else {\n          const value = getItemValue(item);\n          return layeredState.expand(value).bind(path => {\n            updateAriaExpansions(container, path);\n            return Optional.from(path[0]).bind(menuName => layeredState.lookupMenu(menuName).bind(activeMenuPrep => {\n              const activeMenu = buildIfRequired(container, menuName, activeMenuPrep);\n              if (!inBody(activeMenu.element)) {\n                Replacing.append(container, premade(activeMenu));\n              }\n              detail.onOpenSubmenu(container, item, activeMenu, reverse(path));\n              if (decision === ExpandHighlightDecision.HighlightSubmenu) {\n                Highlighting.highlightFirst(activeMenu);\n                return updateMenuPath(container, layeredState, path);\n              } else {\n                Highlighting.dehighlightAll(activeMenu);\n                return Optional.some(item);\n              }\n            }));\n          });\n        }\n      };\n      const collapseLeft = (container, item) => {\n        const value = getItemValue(item);\n        return layeredState.collapse(value).bind(path => {\n          updateAriaExpansions(container, path);\n          return updateMenuPath(container, layeredState, path).map(activeMenu => {\n            detail.onCollapseMenu(container, item, activeMenu);\n            return activeMenu;\n          });\n        });\n      };\n      const updateView = (container, item) => {\n        const value = getItemValue(item);\n        return layeredState.refresh(value).bind(path => {\n          updateAriaExpansions(container, path);\n          return updateMenuPath(container, layeredState, path);\n        });\n      };\n      const onRight = (container, item) => inside(item.element) ? Optional.none() : expandRight(container, item, ExpandHighlightDecision.HighlightSubmenu);\n      const onLeft = (container, item) => inside(item.element) ? Optional.none() : collapseLeft(container, item);\n      const onEscape = (container, item) => collapseLeft(container, item).orThunk(() => detail.onEscape(container, item).map(() => container));\n      const keyOnItem = f => (container, simulatedEvent) => {\n        return closest$1(simulatedEvent.getSource(), `.${ detail.markers.item }`).bind(target => container.getSystem().getByDom(target).toOptional().bind(item => f(container, item).map(always)));\n      };\n      const events = derive$2([\n        run$1(focus(), (tmenu, simulatedEvent) => {\n          const item = simulatedEvent.event.item;\n          layeredState.lookupItem(getItemValue(item)).each(() => {\n            const menu = simulatedEvent.event.menu;\n            Highlighting.highlight(tmenu, menu);\n            const value = getItemValue(simulatedEvent.event.item);\n            layeredState.refresh(value).each(path => closeOthers(tmenu, layeredState, path));\n          });\n        }),\n        runOnExecute$1((component, simulatedEvent) => {\n          const target = simulatedEvent.event.target;\n          component.getSystem().getByDom(target).each(item => {\n            const itemValue = getItemValue(item);\n            if (itemValue.indexOf('collapse-item') === 0) {\n              collapseLeft(component, item);\n            }\n            expandRight(component, item, ExpandHighlightDecision.HighlightSubmenu).fold(() => {\n              detail.onExecute(component, item);\n            }, noop);\n          });\n        }),\n        runOnAttached((container, _simulatedEvent) => {\n          setup(container).each(primary => {\n            Replacing.append(container, premade(primary));\n            detail.onOpenMenu(container, primary);\n            if (detail.highlightOnOpen === HighlightOnOpen.HighlightMenuAndItem) {\n              setActiveMenuAndItem(container, primary);\n            } else if (detail.highlightOnOpen === HighlightOnOpen.HighlightJustMenu) {\n              setActiveMenu(container, primary);\n            }\n          });\n        }),\n        run$1(onMenuItemHighlightedEvent, (tmenuComp, se) => {\n          detail.onHighlightItem(tmenuComp, se.event.menuComp, se.event.itemComp);\n        }),\n        run$1(onMenuItemDehighlightedEvent, (tmenuComp, se) => {\n          detail.onDehighlightItem(tmenuComp, se.event.menuComp, se.event.itemComp);\n        }),\n        ...detail.navigateOnHover ? [run$1(hover(), (tmenu, simulatedEvent) => {\n            const item = simulatedEvent.event.item;\n            updateView(tmenu, item);\n            expandRight(tmenu, item, ExpandHighlightDecision.HighlightParent);\n            detail.onHover(tmenu, item);\n          })] : []\n      ]);\n      const getActiveItem = container => Highlighting.getHighlighted(container).bind(Highlighting.getHighlighted);\n      const collapseMenuApi = container => {\n        getActiveItem(container).each(currentItem => {\n          collapseLeft(container, currentItem);\n        });\n      };\n      const highlightPrimary = container => {\n        layeredState.getPrimary().each(primary => {\n          setActiveMenuAndItem(container, primary);\n        });\n      };\n      const extractMenuFromContainer = container => Optional.from(container.components()[0]).filter(comp => get$f(comp.element, 'role') === 'menu');\n      const repositionMenus = container => {\n        const maybeActivePrimary = layeredState.getPrimary().bind(primary => getActiveItem(container).bind(currentItem => {\n          const itemValue = getItemValue(currentItem);\n          const allMenus = values(layeredState.getMenus());\n          const preparedMenus = cat(map$2(allMenus, LayeredState.extractPreparedMenu));\n          return layeredState.getTriggeringPath(itemValue, v => getItemByValue(container, preparedMenus, v));\n        }).map(triggeringPath => ({\n          primary,\n          triggeringPath\n        })));\n        maybeActivePrimary.fold(() => {\n          extractMenuFromContainer(container).each(primaryMenu => {\n            detail.onRepositionMenu(container, primaryMenu, []);\n          });\n        }, ({primary, triggeringPath}) => {\n          detail.onRepositionMenu(container, primary, triggeringPath);\n        });\n      };\n      const apis = {\n        collapseMenu: collapseMenuApi,\n        highlightPrimary,\n        repositionMenus\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        markers: detail.markers,\n        behaviours: augment(detail.tmenuBehaviours, [\n          Keying.config({\n            mode: 'special',\n            onRight: keyOnItem(onRight),\n            onLeft: keyOnItem(onLeft),\n            onEscape: keyOnItem(onEscape),\n            focusIn: (container, _keyInfo) => {\n              layeredState.getPrimary().each(primary => {\n                dispatch(container, primary.element, focusItem());\n              });\n            }\n          }),\n          Highlighting.config({\n            highlightClass: detail.markers.selectedMenu,\n            itemClass: detail.markers.menu\n          }),\n          Composing.config({\n            find: container => {\n              return Highlighting.getHighlighted(container);\n            }\n          }),\n          Replacing.config({})\n        ]),\n        eventOrder: detail.eventOrder,\n        apis,\n        events\n      };\n    };\n    const collapseItem$1 = constant$1('collapse-item');\n\n    const tieredData = (primary, menus, expansions) => ({\n      primary,\n      menus,\n      expansions\n    });\n    const singleData = (name, menu) => ({\n      primary: name,\n      menus: wrap$1(name, menu),\n      expansions: {}\n    });\n    const collapseItem = text => ({\n      value: generate$6(collapseItem$1()),\n      meta: { text }\n    });\n    const tieredMenu = single({\n      name: 'TieredMenu',\n      configFields: [\n        onStrictKeyboardHandler('onExecute'),\n        onStrictKeyboardHandler('onEscape'),\n        onStrictHandler('onOpenMenu'),\n        onStrictHandler('onOpenSubmenu'),\n        onHandler('onRepositionMenu'),\n        onHandler('onCollapseMenu'),\n        defaulted('highlightOnOpen', HighlightOnOpen.HighlightMenuAndItem),\n        requiredObjOf('data', [\n          required$1('primary'),\n          required$1('menus'),\n          required$1('expansions')\n        ]),\n        defaulted('fakeFocus', false),\n        onHandler('onHighlightItem'),\n        onHandler('onDehighlightItem'),\n        onHandler('onHover'),\n        tieredMenuMarkers(),\n        required$1('dom'),\n        defaulted('navigateOnHover', true),\n        defaulted('stayInDom', false),\n        field('tmenuBehaviours', [\n          Keying,\n          Highlighting,\n          Composing,\n          Replacing\n        ]),\n        defaulted('eventOrder', {})\n      ],\n      apis: {\n        collapseMenu: (apis, tmenu) => {\n          apis.collapseMenu(tmenu);\n        },\n        highlightPrimary: (apis, tmenu) => {\n          apis.highlightPrimary(tmenu);\n        },\n        repositionMenus: (apis, tmenu) => {\n          apis.repositionMenus(tmenu);\n        }\n      },\n      factory: make$6,\n      extraApis: {\n        tieredData,\n        singleData,\n        collapseItem\n      }\n    });\n\n    const makeMenu = (detail, menuSandbox, placementSpec, menuSpec, getBounds) => {\n      const lazySink = () => detail.lazySink(menuSandbox);\n      const layouts = menuSpec.type === 'horizontal' ? {\n        layouts: {\n          onLtr: () => belowOrAbove(),\n          onRtl: () => belowOrAboveRtl()\n        }\n      } : {};\n      const isFirstTierSubmenu = triggeringPaths => triggeringPaths.length === 2;\n      const getSubmenuLayouts = triggeringPaths => isFirstTierSubmenu(triggeringPaths) ? layouts : {};\n      return tieredMenu.sketch({\n        dom: { tag: 'div' },\n        data: menuSpec.data,\n        markers: menuSpec.menu.markers,\n        highlightOnOpen: menuSpec.menu.highlightOnOpen,\n        fakeFocus: menuSpec.menu.fakeFocus,\n        onEscape: () => {\n          Sandboxing.close(menuSandbox);\n          detail.onEscape.map(handler => handler(menuSandbox));\n          return Optional.some(true);\n        },\n        onExecute: () => {\n          return Optional.some(true);\n        },\n        onOpenMenu: (tmenu, menu) => {\n          Positioning.positionWithinBounds(lazySink().getOrDie(), menu, placementSpec, getBounds());\n        },\n        onOpenSubmenu: (tmenu, item, submenu, triggeringPaths) => {\n          const sink = lazySink().getOrDie();\n          Positioning.position(sink, submenu, {\n            anchor: {\n              type: 'submenu',\n              item,\n              ...getSubmenuLayouts(triggeringPaths)\n            }\n          });\n        },\n        onRepositionMenu: (tmenu, primaryMenu, submenuTriggers) => {\n          const sink = lazySink().getOrDie();\n          Positioning.positionWithinBounds(sink, primaryMenu, placementSpec, getBounds());\n          each$1(submenuTriggers, st => {\n            const submenuLayouts = getSubmenuLayouts(st.triggeringPath);\n            Positioning.position(sink, st.triggeredMenu, {\n              anchor: {\n                type: 'submenu',\n                item: st.triggeringItem,\n                ...submenuLayouts\n              }\n            });\n          });\n        }\n      });\n    };\n    const factory$o = (detail, spec) => {\n      const isPartOfRelated = (sandbox, queryElem) => {\n        const related = detail.getRelated(sandbox);\n        return related.exists(rel => isPartOf$1(rel, queryElem));\n      };\n      const setContent = (sandbox, thing) => {\n        Sandboxing.setContent(sandbox, thing);\n      };\n      const showAt = (sandbox, thing, placementSpec) => {\n        showWithin(sandbox, thing, placementSpec, Optional.none());\n      };\n      const showWithin = (sandbox, thing, placementSpec, boxElement) => {\n        showWithinBounds(sandbox, thing, placementSpec, () => boxElement.map(elem => box$1(elem)));\n      };\n      const showWithinBounds = (sandbox, thing, placementSpec, getBounds) => {\n        const sink = detail.lazySink(sandbox).getOrDie();\n        Sandboxing.openWhileCloaked(sandbox, thing, () => Positioning.positionWithinBounds(sink, sandbox, placementSpec, getBounds()));\n        Representing.setValue(sandbox, Optional.some({\n          mode: 'position',\n          config: placementSpec,\n          getBounds\n        }));\n      };\n      const showMenuAt = (sandbox, placementSpec, menuSpec) => {\n        showMenuWithinBounds(sandbox, placementSpec, menuSpec, Optional.none);\n      };\n      const showMenuWithinBounds = (sandbox, placementSpec, menuSpec, getBounds) => {\n        const menu = makeMenu(detail, sandbox, placementSpec, menuSpec, getBounds);\n        Sandboxing.open(sandbox, menu);\n        Representing.setValue(sandbox, Optional.some({\n          mode: 'menu',\n          menu\n        }));\n      };\n      const hide = sandbox => {\n        if (Sandboxing.isOpen(sandbox)) {\n          Representing.setValue(sandbox, Optional.none());\n          Sandboxing.close(sandbox);\n        }\n      };\n      const getContent = sandbox => Sandboxing.getState(sandbox);\n      const reposition = sandbox => {\n        if (Sandboxing.isOpen(sandbox)) {\n          Representing.getValue(sandbox).each(state => {\n            switch (state.mode) {\n            case 'menu':\n              Sandboxing.getState(sandbox).each(tieredMenu.repositionMenus);\n              break;\n            case 'position':\n              const sink = detail.lazySink(sandbox).getOrDie();\n              Positioning.positionWithinBounds(sink, sandbox, state.config, state.getBounds());\n              break;\n            }\n          });\n        }\n      };\n      const apis = {\n        setContent,\n        showAt,\n        showWithin,\n        showWithinBounds,\n        showMenuAt,\n        showMenuWithinBounds,\n        hide,\n        getContent,\n        reposition,\n        isOpen: Sandboxing.isOpen\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        behaviours: augment(detail.inlineBehaviours, [\n          Sandboxing.config({\n            isPartOf: (sandbox, data, queryElem) => {\n              return isPartOf$1(data, queryElem) || isPartOfRelated(sandbox, queryElem);\n            },\n            getAttachPoint: sandbox => {\n              return detail.lazySink(sandbox).getOrDie();\n            },\n            onOpen: sandbox => {\n              detail.onShow(sandbox);\n            },\n            onClose: sandbox => {\n              detail.onHide(sandbox);\n            }\n          }),\n          Representing.config({\n            store: {\n              mode: 'memory',\n              initialValue: Optional.none()\n            }\n          }),\n          Receiving.config({\n            channels: {\n              ...receivingChannel$1({\n                isExtraPart: spec.isExtraPart,\n                ...detail.fireDismissalEventInstead.map(fe => ({ fireEventInstead: { event: fe.event } })).getOr({})\n              }),\n              ...receivingChannel({\n                ...detail.fireRepositionEventInstead.map(fe => ({ fireEventInstead: { event: fe.event } })).getOr({}),\n                doReposition: reposition\n              })\n            }\n          })\n        ]),\n        eventOrder: detail.eventOrder,\n        apis\n      };\n    };\n    const InlineView = single({\n      name: 'InlineView',\n      configFields: [\n        required$1('lazySink'),\n        onHandler('onShow'),\n        onHandler('onHide'),\n        optionFunction('onEscape'),\n        field('inlineBehaviours', [\n          Sandboxing,\n          Representing,\n          Receiving\n        ]),\n        optionObjOf('fireDismissalEventInstead', [defaulted('event', dismissRequested())]),\n        optionObjOf('fireRepositionEventInstead', [defaulted('event', repositionRequested())]),\n        defaulted('getRelated', Optional.none),\n        defaulted('isExtraPart', never),\n        defaulted('eventOrder', Optional.none)\n      ],\n      factory: factory$o,\n      apis: {\n        showAt: (apis, component, anchor, thing) => {\n          apis.showAt(component, anchor, thing);\n        },\n        showWithin: (apis, component, anchor, thing, boxElement) => {\n          apis.showWithin(component, anchor, thing, boxElement);\n        },\n        showWithinBounds: (apis, component, anchor, thing, bounds) => {\n          apis.showWithinBounds(component, anchor, thing, bounds);\n        },\n        showMenuAt: (apis, component, anchor, menuSpec) => {\n          apis.showMenuAt(component, anchor, menuSpec);\n        },\n        showMenuWithinBounds: (apis, component, anchor, menuSpec, bounds) => {\n          apis.showMenuWithinBounds(component, anchor, menuSpec, bounds);\n        },\n        hide: (apis, component) => {\n          apis.hide(component);\n        },\n        isOpen: (apis, component) => apis.isOpen(component),\n        getContent: (apis, component) => apis.getContent(component),\n        setContent: (apis, component, thing) => {\n          apis.setContent(component, thing);\n        },\n        reposition: (apis, component) => {\n          apis.reposition(component);\n        }\n      }\n    });\n\n    var global$9 = tinymce.util.Tools.resolve('tinymce.util.Delay');\n\n    const factory$n = detail => {\n      const events = events$a(detail.action);\n      const tag = detail.dom.tag;\n      const lookupAttr = attr => get$g(detail.dom, 'attributes').bind(attrs => get$g(attrs, attr));\n      const getModAttributes = () => {\n        if (tag === 'button') {\n          const type = lookupAttr('type').getOr('button');\n          const roleAttrs = lookupAttr('role').map(role => ({ role })).getOr({});\n          return {\n            type,\n            ...roleAttrs\n          };\n        } else {\n          const role = lookupAttr('role').getOr('button');\n          return { role };\n        }\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components: detail.components,\n        events,\n        behaviours: SketchBehaviours.augment(detail.buttonBehaviours, [\n          Focusing.config({}),\n          Keying.config({\n            mode: 'execution',\n            useSpace: true,\n            useEnter: true\n          })\n        ]),\n        domModification: { attributes: getModAttributes() },\n        eventOrder: detail.eventOrder\n      };\n    };\n    const Button = single({\n      name: 'Button',\n      factory: factory$n,\n      configFields: [\n        defaulted('uid', undefined),\n        required$1('dom'),\n        defaulted('components', []),\n        SketchBehaviours.field('buttonBehaviours', [\n          Focusing,\n          Keying\n        ]),\n        option$3('action'),\n        option$3('role'),\n        defaulted('eventOrder', {})\n      ]\n    });\n\n    const record = spec => {\n      const uid = isSketchSpec(spec) && hasNonNullableKey(spec, 'uid') ? spec.uid : generate$5('memento');\n      const get = anyInSystem => anyInSystem.getSystem().getByUid(uid).getOrDie();\n      const getOpt = anyInSystem => anyInSystem.getSystem().getByUid(uid).toOptional();\n      const asSpec = () => ({\n        ...spec,\n        uid\n      });\n      return {\n        get,\n        getOpt,\n        asSpec\n      };\n    };\n\n    var global$8 = tinymce.util.Tools.resolve('tinymce.util.I18n');\n\n    const rtlTransform = {\n      'indent': true,\n      'outdent': true,\n      'table-insert-column-after': true,\n      'table-insert-column-before': true,\n      'paste-column-after': true,\n      'paste-column-before': true,\n      'unordered-list': true,\n      'list-bull-circle': true,\n      'list-bull-default': true,\n      'list-bull-square': true\n    };\n    const defaultIconName = 'temporary-placeholder';\n    const defaultIcon = icons => () => get$g(icons, defaultIconName).getOr('!not found!');\n    const getIconName = (name, icons) => {\n      const lcName = name.toLowerCase();\n      if (global$8.isRtl()) {\n        const rtlName = ensureTrailing(lcName, '-rtl');\n        return has$2(icons, rtlName) ? rtlName : lcName;\n      } else {\n        return lcName;\n      }\n    };\n    const lookupIcon = (name, icons) => get$g(icons, getIconName(name, icons));\n    const get$2 = (name, iconProvider) => {\n      const icons = iconProvider();\n      return lookupIcon(name, icons).getOrThunk(defaultIcon(icons));\n    };\n    const getOr = (name, iconProvider, fallbackIcon) => {\n      const icons = iconProvider();\n      return lookupIcon(name, icons).or(fallbackIcon).getOrThunk(defaultIcon(icons));\n    };\n    const needsRtlTransform = iconName => global$8.isRtl() ? has$2(rtlTransform, iconName) : false;\n    const addFocusableBehaviour = () => config('add-focusable', [runOnAttached(comp => {\n        child(comp.element, 'svg').each(svg => set$9(svg, 'focusable', 'false'));\n      })]);\n    const renderIcon$2 = (spec, iconName, icons, fallbackIcon) => {\n      var _a, _b;\n      const rtlIconClasses = needsRtlTransform(iconName) ? ['tox-icon--flip'] : [];\n      const iconHtml = get$g(icons, getIconName(iconName, icons)).or(fallbackIcon).getOrThunk(defaultIcon(icons));\n      return {\n        dom: {\n          tag: spec.tag,\n          attributes: (_a = spec.attributes) !== null && _a !== void 0 ? _a : {},\n          classes: spec.classes.concat(rtlIconClasses),\n          innerHtml: iconHtml\n        },\n        behaviours: derive$1([\n          ...(_b = spec.behaviours) !== null && _b !== void 0 ? _b : [],\n          addFocusableBehaviour()\n        ])\n      };\n    };\n    const render$3 = (iconName, spec, iconProvider, fallbackIcon = Optional.none()) => renderIcon$2(spec, iconName, iconProvider(), fallbackIcon);\n    const renderFirst = (iconNames, spec, iconProvider) => {\n      const icons = iconProvider();\n      const iconName = find$5(iconNames, name => has$2(icons, getIconName(name, icons)));\n      return renderIcon$2(spec, iconName.getOr(defaultIconName), icons, Optional.none());\n    };\n\n    const notificationIconMap = {\n      success: 'checkmark',\n      error: 'warning',\n      err: 'error',\n      warning: 'warning',\n      warn: 'warning',\n      info: 'info'\n    };\n    const factory$m = detail => {\n      const memBannerText = record({\n        dom: {\n          tag: 'p',\n          innerHtml: detail.translationProvider(detail.text)\n        },\n        behaviours: derive$1([Replacing.config({})])\n      });\n      const renderPercentBar = percent => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-bar'],\n          styles: { width: `${ percent }%` }\n        }\n      });\n      const renderPercentText = percent => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-text'],\n          innerHtml: `${ percent }%`\n        }\n      });\n      const memBannerProgress = record({\n        dom: {\n          tag: 'div',\n          classes: detail.progress ? [\n            'tox-progress-bar',\n            'tox-progress-indicator'\n          ] : ['tox-progress-bar']\n        },\n        components: [\n          {\n            dom: {\n              tag: 'div',\n              classes: ['tox-bar-container']\n            },\n            components: [renderPercentBar(0)]\n          },\n          renderPercentText(0)\n        ],\n        behaviours: derive$1([Replacing.config({})])\n      });\n      const updateProgress = (comp, percent) => {\n        if (comp.getSystem().isConnected()) {\n          memBannerProgress.getOpt(comp).each(progress => {\n            Replacing.set(progress, [\n              {\n                dom: {\n                  tag: 'div',\n                  classes: ['tox-bar-container']\n                },\n                components: [renderPercentBar(percent)]\n              },\n              renderPercentText(percent)\n            ]);\n          });\n        }\n      };\n      const updateText = (comp, text) => {\n        if (comp.getSystem().isConnected()) {\n          const banner = memBannerText.get(comp);\n          Replacing.set(banner, [text$2(text)]);\n        }\n      };\n      const apis = {\n        updateProgress,\n        updateText\n      };\n      const iconChoices = flatten([\n        detail.icon.toArray(),\n        detail.level.toArray(),\n        detail.level.bind(level => Optional.from(notificationIconMap[level])).toArray()\n      ]);\n      const memButton = record(Button.sketch({\n        dom: {\n          tag: 'button',\n          classes: [\n            'tox-notification__dismiss',\n            'tox-button',\n            'tox-button--naked',\n            'tox-button--icon'\n          ]\n        },\n        components: [render$3('close', {\n            tag: 'div',\n            classes: ['tox-icon'],\n            attributes: { 'aria-label': detail.translationProvider('Close') }\n          }, detail.iconProvider)],\n        action: comp => {\n          detail.onAction(comp);\n        }\n      }));\n      const notificationIconSpec = renderFirst(iconChoices, {\n        tag: 'div',\n        classes: ['tox-notification__icon']\n      }, detail.iconProvider);\n      const notificationBodySpec = {\n        dom: {\n          tag: 'div',\n          classes: ['tox-notification__body']\n        },\n        components: [memBannerText.asSpec()],\n        behaviours: derive$1([Replacing.config({})])\n      };\n      const components = [\n        notificationIconSpec,\n        notificationBodySpec\n      ];\n      return {\n        uid: detail.uid,\n        dom: {\n          tag: 'div',\n          attributes: { role: 'alert' },\n          classes: detail.level.map(level => [\n            'tox-notification',\n            'tox-notification--in',\n            `tox-notification--${ level }`\n          ]).getOr([\n            'tox-notification',\n            'tox-notification--in'\n          ])\n        },\n        behaviours: derive$1([\n          Focusing.config({}),\n          config('notification-events', [run$1(focusin(), comp => {\n              memButton.getOpt(comp).each(Focusing.focus);\n            })])\n        ]),\n        components: components.concat(detail.progress ? [memBannerProgress.asSpec()] : []).concat(!detail.closeButton ? [] : [memButton.asSpec()]),\n        apis\n      };\n    };\n    const Notification = single({\n      name: 'Notification',\n      factory: factory$m,\n      configFields: [\n        option$3('level'),\n        required$1('progress'),\n        option$3('icon'),\n        required$1('onAction'),\n        required$1('text'),\n        required$1('iconProvider'),\n        required$1('translationProvider'),\n        defaultedBoolean('closeButton', true)\n      ],\n      apis: {\n        updateProgress: (apis, comp, percent) => {\n          apis.updateProgress(comp, percent);\n        },\n        updateText: (apis, comp, text) => {\n          apis.updateText(comp, text);\n        }\n      }\n    });\n\n    var NotificationManagerImpl = (editor, extras, uiMothership) => {\n      const sharedBackstage = extras.backstage.shared;\n      const getBounds = () => {\n        const contentArea = box$1(SugarElement.fromDom(editor.getContentAreaContainer()));\n        const win$1 = win();\n        const x = clamp(win$1.x, contentArea.x, contentArea.right);\n        const y = clamp(win$1.y, contentArea.y, contentArea.bottom);\n        const right = Math.max(contentArea.right, win$1.right);\n        const bottom = Math.max(contentArea.bottom, win$1.bottom);\n        return Optional.some(bounds(x, y, right - x, bottom - y));\n      };\n      const open = (settings, closeCallback) => {\n        const close = () => {\n          closeCallback();\n          InlineView.hide(notificationWrapper);\n        };\n        const notification = build$1(Notification.sketch({\n          text: settings.text,\n          level: contains$2([\n            'success',\n            'error',\n            'warning',\n            'warn',\n            'info'\n          ], settings.type) ? settings.type : undefined,\n          progress: settings.progressBar === true,\n          icon: settings.icon,\n          closeButton: settings.closeButton,\n          onAction: close,\n          iconProvider: sharedBackstage.providers.icons,\n          translationProvider: sharedBackstage.providers.translate\n        }));\n        const notificationWrapper = build$1(InlineView.sketch({\n          dom: {\n            tag: 'div',\n            classes: ['tox-notifications-container']\n          },\n          lazySink: sharedBackstage.getSink,\n          fireDismissalEventInstead: {},\n          ...sharedBackstage.header.isPositionedAtTop() ? {} : { fireRepositionEventInstead: {} }\n        }));\n        uiMothership.add(notificationWrapper);\n        if (isNumber(settings.timeout) && settings.timeout > 0) {\n          global$9.setEditorTimeout(editor, () => {\n            close();\n          }, settings.timeout);\n        }\n        const reposition = () => {\n          const notificationSpec = premade(notification);\n          const anchorOverrides = { maxHeightFunction: expandable$1() };\n          const allNotifications = editor.notificationManager.getNotifications();\n          if (allNotifications[0] === thisNotification) {\n            const anchor = {\n              ...sharedBackstage.anchors.banner(),\n              overrides: anchorOverrides\n            };\n            InlineView.showWithinBounds(notificationWrapper, notificationSpec, { anchor }, getBounds);\n          } else {\n            indexOf(allNotifications, thisNotification).each(idx => {\n              const previousNotification = allNotifications[idx - 1].getEl();\n              const nodeAnchor = {\n                type: 'node',\n                root: body(),\n                node: Optional.some(SugarElement.fromDom(previousNotification)),\n                overrides: anchorOverrides,\n                layouts: {\n                  onRtl: () => [south$2],\n                  onLtr: () => [south$2]\n                }\n              };\n              InlineView.showWithinBounds(notificationWrapper, notificationSpec, { anchor: nodeAnchor }, getBounds);\n            });\n          }\n        };\n        const thisNotification = {\n          close,\n          reposition,\n          text: nuText => {\n            Notification.updateText(notification, nuText);\n          },\n          settings,\n          getEl: () => notification.element.dom,\n          progressBar: {\n            value: percent => {\n              Notification.updateProgress(notification, percent);\n            }\n          }\n        };\n        return thisNotification;\n      };\n      const close = notification => {\n        notification.close();\n      };\n      const getArgs = notification => {\n        return notification.settings;\n      };\n      return {\n        open,\n        close,\n        getArgs\n      };\n    };\n\n    var global$7 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');\n\n    var global$6 = tinymce.util.Tools.resolve('tinymce.EditorManager');\n\n    var global$5 = tinymce.util.Tools.resolve('tinymce.Env');\n\n    var ToolbarMode$1;\n    (function (ToolbarMode) {\n      ToolbarMode['default'] = 'wrap';\n      ToolbarMode['floating'] = 'floating';\n      ToolbarMode['sliding'] = 'sliding';\n      ToolbarMode['scrolling'] = 'scrolling';\n    }(ToolbarMode$1 || (ToolbarMode$1 = {})));\n    var ToolbarLocation$1;\n    (function (ToolbarLocation) {\n      ToolbarLocation['auto'] = 'auto';\n      ToolbarLocation['top'] = 'top';\n      ToolbarLocation['bottom'] = 'bottom';\n    }(ToolbarLocation$1 || (ToolbarLocation$1 = {})));\n    const option$2 = name => editor => editor.options.get(name);\n    const wrapOptional = fn => editor => Optional.from(fn(editor));\n    const register$e = editor => {\n      const isPhone = global$5.deviceType.isPhone();\n      const isMobile = global$5.deviceType.isTablet() || isPhone;\n      const registerOption = editor.options.register;\n      const stringOrFalseProcessor = value => isString(value) || value === false;\n      const stringOrNumberProcessor = value => isString(value) || isNumber(value);\n      registerOption('skin', {\n        processor: value => isString(value) || value === false,\n        default: 'oxide'\n      });\n      registerOption('skin_url', { processor: 'string' });\n      registerOption('height', {\n        processor: stringOrNumberProcessor,\n        default: Math.max(editor.getElement().offsetHeight, 400)\n      });\n      registerOption('width', {\n        processor: stringOrNumberProcessor,\n        default: global$7.DOM.getStyle(editor.getElement(), 'width')\n      });\n      registerOption('min_height', {\n        processor: 'number',\n        default: 100\n      });\n      registerOption('min_width', { processor: 'number' });\n      registerOption('max_height', { processor: 'number' });\n      registerOption('max_width', { processor: 'number' });\n      registerOption('style_formats', { processor: 'object[]' });\n      registerOption('style_formats_merge', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('style_formats_autohide', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('line_height_formats', {\n        processor: 'string',\n        default: '1 1.1 1.2 1.3 1.4 1.5 2'\n      });\n      registerOption('font_family_formats', {\n        processor: 'string',\n        default: 'Andale Mono=andale mono,monospace;' + 'Arial=arial,helvetica,sans-serif;' + 'Arial Black=arial black,sans-serif;' + 'Book Antiqua=book antiqua,palatino,serif;' + 'Comic Sans MS=comic sans ms,sans-serif;' + 'Courier New=courier new,courier,monospace;' + 'Georgia=georgia,palatino,serif;' + 'Helvetica=helvetica,arial,sans-serif;' + 'Impact=impact,sans-serif;' + 'Symbol=symbol;' + 'Tahoma=tahoma,arial,helvetica,sans-serif;' + 'Terminal=terminal,monaco,monospace;' + 'Times New Roman=times new roman,times,serif;' + 'Trebuchet MS=trebuchet ms,geneva,sans-serif;' + 'Verdana=verdana,geneva,sans-serif;' + 'Webdings=webdings;' + 'Wingdings=wingdings,zapf dingbats'\n      });\n      registerOption('font_size_formats', {\n        processor: 'string',\n        default: '8pt 10pt 12pt 14pt 18pt 24pt 36pt'\n      });\n      registerOption('block_formats', {\n        processor: 'string',\n        default: 'Paragraph=p;' + 'Heading 1=h1;' + 'Heading 2=h2;' + 'Heading 3=h3;' + 'Heading 4=h4;' + 'Heading 5=h5;' + 'Heading 6=h6;' + 'Preformatted=pre'\n      });\n      registerOption('content_langs', { processor: 'object[]' });\n      registerOption('removed_menuitems', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('menubar', {\n        processor: value => isString(value) || isBoolean(value),\n        default: !isPhone\n      });\n      registerOption('menu', {\n        processor: 'object',\n        default: {}\n      });\n      registerOption('toolbar', {\n        processor: value => {\n          if (isBoolean(value) || isString(value) || isArray(value)) {\n            return {\n              value,\n              valid: true\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be a boolean, string or array.'\n            };\n          }\n        },\n        default: true\n      });\n      range$2(9, num => {\n        registerOption('toolbar' + (num + 1), { processor: 'string' });\n      });\n      registerOption('toolbar_mode', {\n        processor: 'string',\n        default: isMobile ? 'scrolling' : 'floating'\n      });\n      registerOption('toolbar_groups', {\n        processor: 'object',\n        default: {}\n      });\n      registerOption('toolbar_location', {\n        processor: 'string',\n        default: ToolbarLocation$1.auto\n      });\n      registerOption('toolbar_persist', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('toolbar_sticky', {\n        processor: 'boolean',\n        default: editor.inline\n      });\n      registerOption('toolbar_sticky_offset', {\n        processor: 'number',\n        default: 0\n      });\n      registerOption('fixed_toolbar_container', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('fixed_toolbar_container_target', { processor: 'object' });\n      registerOption('file_picker_callback', { processor: 'function' });\n      registerOption('file_picker_validator_handler', { processor: 'function' });\n      registerOption('file_picker_types', { processor: 'string' });\n      registerOption('typeahead_urls', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('anchor_top', {\n        processor: stringOrFalseProcessor,\n        default: '#top'\n      });\n      registerOption('anchor_bottom', {\n        processor: stringOrFalseProcessor,\n        default: '#bottom'\n      });\n      registerOption('draggable_modal', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('statusbar', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('elementpath', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('branding', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('promotion', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('resize', {\n        processor: value => value === 'both' || isBoolean(value),\n        default: !global$5.deviceType.isTouch()\n      });\n      registerOption('sidebar_show', { processor: 'string' });\n    };\n    const isReadOnly = option$2('readonly');\n    const getHeightOption = option$2('height');\n    const getWidthOption = option$2('width');\n    const getMinWidthOption = wrapOptional(option$2('min_width'));\n    const getMinHeightOption = wrapOptional(option$2('min_height'));\n    const getMaxWidthOption = wrapOptional(option$2('max_width'));\n    const getMaxHeightOption = wrapOptional(option$2('max_height'));\n    const getUserStyleFormats = wrapOptional(option$2('style_formats'));\n    const shouldMergeStyleFormats = option$2('style_formats_merge');\n    const shouldAutoHideStyleFormats = option$2('style_formats_autohide');\n    const getContentLanguages = option$2('content_langs');\n    const getRemovedMenuItems = option$2('removed_menuitems');\n    const getToolbarMode = option$2('toolbar_mode');\n    const getToolbarGroups = option$2('toolbar_groups');\n    const getToolbarLocation = option$2('toolbar_location');\n    const fixedContainerSelector = option$2('fixed_toolbar_container');\n    const fixedToolbarContainerTarget = option$2('fixed_toolbar_container_target');\n    const isToolbarPersist = option$2('toolbar_persist');\n    const getStickyToolbarOffset = option$2('toolbar_sticky_offset');\n    const getMenubar = option$2('menubar');\n    const getToolbar = option$2('toolbar');\n    const getFilePickerCallback = option$2('file_picker_callback');\n    const getFilePickerValidatorHandler = option$2('file_picker_validator_handler');\n    const getFilePickerTypes = option$2('file_picker_types');\n    const useTypeaheadUrls = option$2('typeahead_urls');\n    const getAnchorTop = option$2('anchor_top');\n    const getAnchorBottom = option$2('anchor_bottom');\n    const isDraggableModal$1 = option$2('draggable_modal');\n    const useStatusBar = option$2('statusbar');\n    const useElementPath = option$2('elementpath');\n    const useBranding = option$2('branding');\n    const getResize = option$2('resize');\n    const getPasteAsText = option$2('paste_as_text');\n    const getSidebarShow = option$2('sidebar_show');\n    const promotionEnabled = option$2('promotion');\n    const isSkinDisabled = editor => editor.options.get('skin') === false;\n    const isMenubarEnabled = editor => editor.options.get('menubar') !== false;\n    const getSkinUrl = editor => {\n      const skinUrl = editor.options.get('skin_url');\n      if (isSkinDisabled(editor)) {\n        return skinUrl;\n      } else {\n        if (skinUrl) {\n          return editor.documentBaseURI.toAbsolute(skinUrl);\n        } else {\n          const skin = editor.options.get('skin');\n          return global$6.baseURL + '/skins/ui/' + skin;\n        }\n      }\n    };\n    const getLineHeightFormats = editor => editor.options.get('line_height_formats').split(' ');\n    const isToolbarEnabled = editor => {\n      const toolbar = getToolbar(editor);\n      const isToolbarString = isString(toolbar);\n      const isToolbarObjectArray = isArray(toolbar) && toolbar.length > 0;\n      return !isMultipleToolbars(editor) && (isToolbarObjectArray || isToolbarString || toolbar === true);\n    };\n    const getMultipleToolbarsOption = editor => {\n      const toolbars = range$2(9, num => editor.options.get('toolbar' + (num + 1)));\n      const toolbarArray = filter$2(toolbars, isString);\n      return someIf(toolbarArray.length > 0, toolbarArray);\n    };\n    const isMultipleToolbars = editor => getMultipleToolbarsOption(editor).fold(() => {\n      const toolbar = getToolbar(editor);\n      return isArrayOf(toolbar, isString) && toolbar.length > 0;\n    }, always);\n    const isToolbarLocationBottom = editor => getToolbarLocation(editor) === ToolbarLocation$1.bottom;\n    const fixedContainerTarget = editor => {\n      var _a;\n      if (!editor.inline) {\n        return Optional.none();\n      }\n      const selector = (_a = fixedContainerSelector(editor)) !== null && _a !== void 0 ? _a : '';\n      if (selector.length > 0) {\n        return descendant(body(), selector);\n      }\n      const element = fixedToolbarContainerTarget(editor);\n      if (isNonNullable(element)) {\n        return Optional.some(SugarElement.fromDom(element));\n      }\n      return Optional.none();\n    };\n    const useFixedContainer = editor => editor.inline && fixedContainerTarget(editor).isSome();\n    const getUiContainer = editor => {\n      const fixedContainer = fixedContainerTarget(editor);\n      return fixedContainer.getOrThunk(() => getContentContainer(getRootNode(SugarElement.fromDom(editor.getElement()))));\n    };\n    const isDistractionFree = editor => editor.inline && !isMenubarEnabled(editor) && !isToolbarEnabled(editor) && !isMultipleToolbars(editor);\n    const isStickyToolbar = editor => {\n      const isStickyToolbar = editor.options.get('toolbar_sticky');\n      return (isStickyToolbar || editor.inline) && !useFixedContainer(editor) && !isDistractionFree(editor);\n    };\n    const getMenus = editor => {\n      const menu = editor.options.get('menu');\n      return map$1(menu, menu => ({\n        ...menu,\n        items: menu.items\n      }));\n    };\n\n    var Options = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        get ToolbarMode () { return ToolbarMode$1; },\n        get ToolbarLocation () { return ToolbarLocation$1; },\n        register: register$e,\n        getSkinUrl: getSkinUrl,\n        isReadOnly: isReadOnly,\n        isSkinDisabled: isSkinDisabled,\n        getHeightOption: getHeightOption,\n        getWidthOption: getWidthOption,\n        getMinWidthOption: getMinWidthOption,\n        getMinHeightOption: getMinHeightOption,\n        getMaxWidthOption: getMaxWidthOption,\n        getMaxHeightOption: getMaxHeightOption,\n        getUserStyleFormats: getUserStyleFormats,\n        shouldMergeStyleFormats: shouldMergeStyleFormats,\n        shouldAutoHideStyleFormats: shouldAutoHideStyleFormats,\n        getLineHeightFormats: getLineHeightFormats,\n        getContentLanguages: getContentLanguages,\n        getRemovedMenuItems: getRemovedMenuItems,\n        isMenubarEnabled: isMenubarEnabled,\n        isMultipleToolbars: isMultipleToolbars,\n        isToolbarEnabled: isToolbarEnabled,\n        isToolbarPersist: isToolbarPersist,\n        getMultipleToolbarsOption: getMultipleToolbarsOption,\n        getUiContainer: getUiContainer,\n        useFixedContainer: useFixedContainer,\n        getToolbarMode: getToolbarMode,\n        isDraggableModal: isDraggableModal$1,\n        isDistractionFree: isDistractionFree,\n        isStickyToolbar: isStickyToolbar,\n        getStickyToolbarOffset: getStickyToolbarOffset,\n        getToolbarLocation: getToolbarLocation,\n        isToolbarLocationBottom: isToolbarLocationBottom,\n        getToolbarGroups: getToolbarGroups,\n        getMenus: getMenus,\n        getMenubar: getMenubar,\n        getToolbar: getToolbar,\n        getFilePickerCallback: getFilePickerCallback,\n        getFilePickerTypes: getFilePickerTypes,\n        useTypeaheadUrls: useTypeaheadUrls,\n        getAnchorTop: getAnchorTop,\n        getAnchorBottom: getAnchorBottom,\n        getFilePickerValidatorHandler: getFilePickerValidatorHandler,\n        useStatusBar: useStatusBar,\n        useElementPath: useElementPath,\n        promotionEnabled: promotionEnabled,\n        useBranding: useBranding,\n        getResize: getResize,\n        getPasteAsText: getPasteAsText,\n        getSidebarShow: getSidebarShow\n    });\n\n    const autocompleteSelector = '[data-mce-autocompleter]';\n    const detect = elm => closest$1(elm, autocompleteSelector);\n    const findIn = elm => descendant(elm, autocompleteSelector);\n\n    const setup$e = (api, editor) => {\n      const redirectKeyToItem = (item, e) => {\n        emitWith(item, keydown(), { raw: e });\n      };\n      const getItem = () => api.getMenu().bind(Highlighting.getHighlighted);\n      editor.on('keydown', e => {\n        const keyCode = e.which;\n        if (!api.isActive()) {\n          return;\n        }\n        if (api.isMenuOpen()) {\n          if (keyCode === 13) {\n            getItem().each(emitExecute);\n            e.preventDefault();\n          } else if (keyCode === 40) {\n            getItem().fold(() => {\n              api.getMenu().each(Highlighting.highlightFirst);\n            }, item => {\n              redirectKeyToItem(item, e);\n            });\n            e.preventDefault();\n            e.stopImmediatePropagation();\n          } else if (keyCode === 37 || keyCode === 38 || keyCode === 39) {\n            getItem().each(item => {\n              redirectKeyToItem(item, e);\n              e.preventDefault();\n              e.stopImmediatePropagation();\n            });\n          }\n        } else {\n          if (keyCode === 13 || keyCode === 38 || keyCode === 40) {\n            api.cancelIfNecessary();\n          }\n        }\n      });\n      editor.on('NodeChange', e => {\n        if (api.isActive() && !api.isProcessingAction() && detect(SugarElement.fromDom(e.element)).isNone()) {\n          api.cancelIfNecessary();\n        }\n      });\n    };\n    const AutocompleterEditorEvents = { setup: setup$e };\n\n    var ItemResponse;\n    (function (ItemResponse) {\n      ItemResponse[ItemResponse['CLOSE_ON_EXECUTE'] = 0] = 'CLOSE_ON_EXECUTE';\n      ItemResponse[ItemResponse['BUBBLE_TO_SANDBOX'] = 1] = 'BUBBLE_TO_SANDBOX';\n    }(ItemResponse || (ItemResponse = {})));\n    var ItemResponse$1 = ItemResponse;\n\n    const navClass = 'tox-menu-nav__js';\n    const selectableClass = 'tox-collection__item';\n    const colorClass = 'tox-swatch';\n    const presetClasses = {\n      normal: navClass,\n      color: colorClass\n    };\n    const tickedClass = 'tox-collection__item--enabled';\n    const groupHeadingClass = 'tox-collection__group-heading';\n    const iconClass = 'tox-collection__item-icon';\n    const textClass = 'tox-collection__item-label';\n    const accessoryClass = 'tox-collection__item-accessory';\n    const caretClass = 'tox-collection__item-caret';\n    const checkmarkClass = 'tox-collection__item-checkmark';\n    const activeClass = 'tox-collection__item--active';\n    const containerClass = 'tox-collection__item-container';\n    const containerColumnClass = 'tox-collection__item-container--column';\n    const containerRowClass = 'tox-collection__item-container--row';\n    const containerAlignRightClass = 'tox-collection__item-container--align-right';\n    const containerAlignLeftClass = 'tox-collection__item-container--align-left';\n    const containerValignTopClass = 'tox-collection__item-container--valign-top';\n    const containerValignMiddleClass = 'tox-collection__item-container--valign-middle';\n    const containerValignBottomClass = 'tox-collection__item-container--valign-bottom';\n    const classForPreset = presets => get$g(presetClasses, presets).getOr(navClass);\n\n    const forMenu = presets => {\n      if (presets === 'color') {\n        return 'tox-swatches';\n      } else {\n        return 'tox-menu';\n      }\n    };\n    const classes = presets => ({\n      backgroundMenu: 'tox-background-menu',\n      selectedMenu: 'tox-selected-menu',\n      selectedItem: 'tox-collection__item--active',\n      hasIcons: 'tox-menu--has-icons',\n      menu: forMenu(presets),\n      tieredMenu: 'tox-tiered-menu'\n    });\n\n    const markers = presets => {\n      const menuClasses = classes(presets);\n      return {\n        backgroundMenu: menuClasses.backgroundMenu,\n        selectedMenu: menuClasses.selectedMenu,\n        menu: menuClasses.menu,\n        selectedItem: menuClasses.selectedItem,\n        item: classForPreset(presets)\n      };\n    };\n    const dom$1 = (hasIcons, columns, presets) => {\n      const menuClasses = classes(presets);\n      return {\n        tag: 'div',\n        classes: flatten([\n          [\n            menuClasses.menu,\n            `tox-menu-${ columns }-column`\n          ],\n          hasIcons ? [menuClasses.hasIcons] : []\n        ])\n      };\n    };\n    const components = [Menu.parts.items({})];\n    const part = (hasIcons, columns, presets) => {\n      const menuClasses = classes(presets);\n      const d = {\n        tag: 'div',\n        classes: flatten([[menuClasses.tieredMenu]])\n      };\n      return {\n        dom: d,\n        markers: markers(presets)\n      };\n    };\n\n    const schema$l = constant$1([\n      option$3('data'),\n      defaulted('inputAttributes', {}),\n      defaulted('inputStyles', {}),\n      defaulted('tag', 'input'),\n      defaulted('inputClasses', []),\n      onHandler('onSetValue'),\n      defaulted('styles', {}),\n      defaulted('eventOrder', {}),\n      field('inputBehaviours', [\n        Representing,\n        Focusing\n      ]),\n      defaulted('selectOnFocus', true)\n    ]);\n    const focusBehaviours = detail => derive$1([Focusing.config({\n        onFocus: !detail.selectOnFocus ? noop : component => {\n          const input = component.element;\n          const value = get$6(input);\n          input.dom.setSelectionRange(0, value.length);\n        }\n      })]);\n    const behaviours = detail => ({\n      ...focusBehaviours(detail),\n      ...augment(detail.inputBehaviours, [Representing.config({\n          store: {\n            mode: 'manual',\n            ...detail.data.map(data => ({ initialValue: data })).getOr({}),\n            getValue: input => {\n              return get$6(input.element);\n            },\n            setValue: (input, data) => {\n              const current = get$6(input.element);\n              if (current !== data) {\n                set$5(input.element, data);\n              }\n            }\n          },\n          onSetValue: detail.onSetValue\n        })])\n    });\n    const dom = detail => ({\n      tag: detail.tag,\n      attributes: {\n        type: 'text',\n        ...detail.inputAttributes\n      },\n      styles: detail.inputStyles,\n      classes: detail.inputClasses\n    });\n\n    const factory$l = (detail, _spec) => ({\n      uid: detail.uid,\n      dom: dom(detail),\n      components: [],\n      behaviours: behaviours(detail),\n      eventOrder: detail.eventOrder\n    });\n    const Input = single({\n      name: 'Input',\n      configFields: schema$l(),\n      factory: factory$l\n    });\n\n    const refetchTriggerEvent = generate$6('refetch-trigger-event');\n    const redirectMenuItemInteractionEvent = generate$6('redirect-menu-item-interaction');\n\n    const menuSearcherClass = 'tox-menu__searcher';\n    const findWithinSandbox = sandboxComp => {\n      return descendant(sandboxComp.element, `.${ menuSearcherClass }`).bind(inputElem => sandboxComp.getSystem().getByDom(inputElem).toOptional());\n    };\n    const findWithinMenu = findWithinSandbox;\n    const restoreState = (inputComp, searcherState) => {\n      Representing.setValue(inputComp, searcherState.fetchPattern);\n      inputComp.element.dom.selectionStart = searcherState.selectionStart;\n      inputComp.element.dom.selectionEnd = searcherState.selectionEnd;\n    };\n    const saveState = inputComp => {\n      const fetchPattern = Representing.getValue(inputComp);\n      const selectionStart = inputComp.element.dom.selectionStart;\n      const selectionEnd = inputComp.element.dom.selectionEnd;\n      return {\n        fetchPattern,\n        selectionStart,\n        selectionEnd\n      };\n    };\n    const setActiveDescendant = (inputComp, active) => {\n      getOpt(active.element, 'id').each(id => set$9(inputComp.element, 'aria-activedescendant', id));\n    };\n    const renderMenuSearcher = spec => {\n      const handleByBrowser = (comp, se) => {\n        se.cut();\n        return Optional.none();\n      };\n      const handleByHighlightedItem = (comp, se) => {\n        const eventData = {\n          interactionEvent: se.event,\n          eventType: se.event.raw.type\n        };\n        emitWith(comp, redirectMenuItemInteractionEvent, eventData);\n        return Optional.some(true);\n      };\n      const customSearcherEventsName = 'searcher-events';\n      return {\n        dom: {\n          tag: 'div',\n          classes: [selectableClass]\n        },\n        components: [Input.sketch({\n            inputClasses: [\n              menuSearcherClass,\n              'tox-textfield'\n            ],\n            inputAttributes: {\n              ...spec.placeholder.map(placeholder => ({ placeholder: spec.i18n(placeholder) })).getOr({}),\n              'type': 'search',\n              'aria-autocomplete': 'list'\n            },\n            inputBehaviours: derive$1([\n              config(customSearcherEventsName, [\n                run$1(input(), inputComp => {\n                  emit(inputComp, refetchTriggerEvent);\n                }),\n                run$1(keydown(), (inputComp, se) => {\n                  if (se.event.raw.key === 'Escape') {\n                    se.stop();\n                  }\n                })\n              ]),\n              Keying.config({\n                mode: 'special',\n                onLeft: handleByBrowser,\n                onRight: handleByBrowser,\n                onSpace: handleByBrowser,\n                onEnter: handleByHighlightedItem,\n                onEscape: handleByHighlightedItem,\n                onUp: handleByHighlightedItem,\n                onDown: handleByHighlightedItem\n              })\n            ]),\n            eventOrder: {\n              keydown: [\n                customSearcherEventsName,\n                Keying.name()\n              ]\n            }\n          })]\n      };\n    };\n\n    const searchResultsClass = 'tox-collection--results__js';\n    const augmentWithAria = item => {\n      var _a;\n      if (item.dom) {\n        return {\n          ...item,\n          dom: {\n            ...item.dom,\n            attributes: {\n              ...(_a = item.dom.attributes) !== null && _a !== void 0 ? _a : {},\n              'id': generate$6('aria-item-search-result-id'),\n              'aria-selected': 'false'\n            }\n          }\n        };\n      } else {\n        return item;\n      }\n    };\n\n    const chunk = (rowDom, numColumns) => items => {\n      const chunks = chunk$1(items, numColumns);\n      return map$2(chunks, c => ({\n        dom: rowDom,\n        components: c\n      }));\n    };\n    const forSwatch = columns => ({\n      dom: {\n        tag: 'div',\n        classes: [\n          'tox-menu',\n          'tox-swatches-menu'\n        ]\n      },\n      components: [{\n          dom: {\n            tag: 'div',\n            classes: ['tox-swatches']\n          },\n          components: [Menu.parts.items({\n              preprocess: columns !== 'auto' ? chunk({\n                tag: 'div',\n                classes: ['tox-swatches__row']\n              }, columns) : identity\n            })]\n        }]\n    });\n    const forToolbar = columns => ({\n      dom: {\n        tag: 'div',\n        classes: [\n          'tox-menu',\n          'tox-collection',\n          'tox-collection--toolbar',\n          'tox-collection--toolbar-lg'\n        ]\n      },\n      components: [Menu.parts.items({\n          preprocess: chunk({\n            tag: 'div',\n            classes: ['tox-collection__group']\n          }, columns)\n        })]\n    });\n    const preprocessCollection = (items, isSeparator) => {\n      const allSplits = [];\n      let currentSplit = [];\n      each$1(items, (item, i) => {\n        if (isSeparator(item, i)) {\n          if (currentSplit.length > 0) {\n            allSplits.push(currentSplit);\n          }\n          currentSplit = [];\n          if (has$2(item.dom, 'innerHtml') || item.components && item.components.length > 0) {\n            currentSplit.push(item);\n          }\n        } else {\n          currentSplit.push(item);\n        }\n      });\n      if (currentSplit.length > 0) {\n        allSplits.push(currentSplit);\n      }\n      return map$2(allSplits, s => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-collection__group']\n        },\n        components: s\n      }));\n    };\n    const insertItemsPlaceholder = (columns, initItems, onItem) => {\n      return Menu.parts.items({\n        preprocess: rawItems => {\n          const enrichedItems = map$2(rawItems, onItem);\n          if (columns !== 'auto' && columns > 1) {\n            return chunk({\n              tag: 'div',\n              classes: ['tox-collection__group']\n            }, columns)(enrichedItems);\n          } else {\n            return preprocessCollection(enrichedItems, (_item, i) => initItems[i].type === 'separator');\n          }\n        }\n      });\n    };\n    const forCollection = (columns, initItems, _hasIcons = true) => ({\n      dom: {\n        tag: 'div',\n        classes: [\n          'tox-menu',\n          'tox-collection'\n        ].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid'])\n      },\n      components: [insertItemsPlaceholder(columns, initItems, identity)]\n    });\n    const forCollectionWithSearchResults = (columns, initItems, _hasIcons = true) => {\n      const ariaControlsSearchResults = generate$6('aria-controls-search-results');\n      return {\n        dom: {\n          tag: 'div',\n          classes: [\n            'tox-menu',\n            'tox-collection',\n            searchResultsClass\n          ].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid']),\n          attributes: { id: ariaControlsSearchResults }\n        },\n        components: [insertItemsPlaceholder(columns, initItems, augmentWithAria)]\n      };\n    };\n    const forCollectionWithSearchField = (columns, initItems, searchField) => {\n      const ariaControlsSearchResults = generate$6('aria-controls-search-results');\n      return {\n        dom: {\n          tag: 'div',\n          classes: [\n            'tox-menu',\n            'tox-collection'\n          ].concat(columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid'])\n        },\n        components: [\n          renderMenuSearcher({\n            i18n: global$8.translate,\n            placeholder: searchField.placeholder\n          }),\n          {\n            dom: {\n              tag: 'div',\n              classes: [\n                ...columns === 1 ? ['tox-collection--list'] : ['tox-collection--grid'],\n                searchResultsClass\n              ],\n              attributes: { id: ariaControlsSearchResults }\n            },\n            components: [insertItemsPlaceholder(columns, initItems, augmentWithAria)]\n          }\n        ]\n      };\n    };\n    const forHorizontalCollection = (initItems, _hasIcons = true) => ({\n      dom: {\n        tag: 'div',\n        classes: [\n          'tox-collection',\n          'tox-collection--horizontal'\n        ]\n      },\n      components: [Menu.parts.items({ preprocess: items => preprocessCollection(items, (_item, i) => initItems[i].type === 'separator') })]\n    });\n\n    const menuHasIcons = xs => exists(xs, item => 'icon' in item && item.icon !== undefined);\n    const handleError = error => {\n      console.error(formatError(error));\n      console.log(error);\n      return Optional.none();\n    };\n    const createHorizontalPartialMenuWithAlloyItems = (value, _hasIcons, items, _columns, _menuLayout) => {\n      const structure = forHorizontalCollection(items);\n      return {\n        value,\n        dom: structure.dom,\n        components: structure.components,\n        items\n      };\n    };\n    const createPartialMenuWithAlloyItems = (value, hasIcons, items, columns, menuLayout) => {\n      const getNormalStructure = () => {\n        if (menuLayout.menuType !== 'searchable') {\n          return forCollection(columns, items);\n        } else {\n          return menuLayout.searchMode.searchMode === 'search-with-field' ? forCollectionWithSearchField(columns, items, menuLayout.searchMode) : forCollectionWithSearchResults(columns, items);\n        }\n      };\n      if (menuLayout.menuType === 'color') {\n        const structure = forSwatch(columns);\n        return {\n          value,\n          dom: structure.dom,\n          components: structure.components,\n          items\n        };\n      } else if (menuLayout.menuType === 'normal' && columns === 'auto') {\n        const structure = forCollection(columns, items);\n        return {\n          value,\n          dom: structure.dom,\n          components: structure.components,\n          items\n        };\n      } else if (menuLayout.menuType === 'normal' || menuLayout.menuType === 'searchable') {\n        const structure = getNormalStructure();\n        return {\n          value,\n          dom: structure.dom,\n          components: structure.components,\n          items\n        };\n      } else if (menuLayout.menuType === 'listpreview' && columns !== 'auto') {\n        const structure = forToolbar(columns);\n        return {\n          value,\n          dom: structure.dom,\n          components: structure.components,\n          items\n        };\n      } else {\n        return {\n          value,\n          dom: dom$1(hasIcons, columns, menuLayout.menuType),\n          components: components,\n          items\n        };\n      }\n    };\n\n    const type = requiredString('type');\n    const name$1 = requiredString('name');\n    const label = requiredString('label');\n    const text$1 = requiredString('text');\n    const title = requiredString('title');\n    const icon = requiredString('icon');\n    const value$1 = requiredString('value');\n    const fetch$1 = requiredFunction('fetch');\n    const getSubmenuItems = requiredFunction('getSubmenuItems');\n    const onAction = requiredFunction('onAction');\n    const onItemAction = requiredFunction('onItemAction');\n    const onSetup = defaultedFunction('onSetup', () => noop);\n    const optionalName = optionString('name');\n    const optionalText = optionString('text');\n    const optionalIcon = optionString('icon');\n    const optionalTooltip = optionString('tooltip');\n    const optionalLabel = optionString('label');\n    const optionalShortcut = optionString('shortcut');\n    const optionalSelect = optionFunction('select');\n    const active = defaultedBoolean('active', false);\n    const borderless = defaultedBoolean('borderless', false);\n    const enabled = defaultedBoolean('enabled', true);\n    const primary = defaultedBoolean('primary', false);\n    const defaultedColumns = num => defaulted('columns', num);\n    const defaultedMeta = defaulted('meta', {});\n    const defaultedOnAction = defaultedFunction('onAction', noop);\n    const defaultedType = type => defaultedString('type', type);\n    const generatedName = namePrefix => field$1('name', 'name', defaultedThunk(() => generate$6(`${ namePrefix }-name`)), string);\n    const generatedValue = valuePrefix => field$1('value', 'value', defaultedThunk(() => generate$6(`${ valuePrefix }-value`)), anyValue());\n\n    const separatorMenuItemSchema = objOf([\n      type,\n      optionalText\n    ]);\n    const createSeparatorMenuItem = spec => asRaw('separatormenuitem', separatorMenuItemSchema, spec);\n\n    const autocompleterItemSchema = objOf([\n      defaultedType('autocompleteitem'),\n      active,\n      enabled,\n      defaultedMeta,\n      value$1,\n      optionalText,\n      optionalIcon\n    ]);\n    const createSeparatorItem = spec => asRaw('Autocompleter.Separator', separatorMenuItemSchema, spec);\n    const createAutocompleterItem = spec => asRaw('Autocompleter.Item', autocompleterItemSchema, spec);\n\n    const baseToolbarButtonFields = [\n      enabled,\n      optionalTooltip,\n      optionalIcon,\n      optionalText,\n      onSetup\n    ];\n    const toolbarButtonSchema = objOf([\n      type,\n      onAction\n    ].concat(baseToolbarButtonFields));\n    const createToolbarButton = spec => asRaw('toolbarbutton', toolbarButtonSchema, spec);\n\n    const baseToolbarToggleButtonFields = [active].concat(baseToolbarButtonFields);\n    const toggleButtonSchema = objOf(baseToolbarToggleButtonFields.concat([\n      type,\n      onAction\n    ]));\n    const createToggleButton = spec => asRaw('ToggleButton', toggleButtonSchema, spec);\n\n    const contextBarFields = [\n      defaultedFunction('predicate', never),\n      defaultedStringEnum('scope', 'node', [\n        'node',\n        'editor'\n      ]),\n      defaultedStringEnum('position', 'selection', [\n        'node',\n        'selection',\n        'line'\n      ])\n    ];\n\n    const contextButtonFields = baseToolbarButtonFields.concat([\n      defaultedType('contextformbutton'),\n      primary,\n      onAction,\n      customField('original', identity)\n    ]);\n    const contextToggleButtonFields = baseToolbarToggleButtonFields.concat([\n      defaultedType('contextformbutton'),\n      primary,\n      onAction,\n      customField('original', identity)\n    ]);\n    const launchButtonFields = baseToolbarButtonFields.concat([defaultedType('contextformbutton')]);\n    const launchToggleButtonFields = baseToolbarToggleButtonFields.concat([defaultedType('contextformtogglebutton')]);\n    const toggleOrNormal = choose$1('type', {\n      contextformbutton: contextButtonFields,\n      contextformtogglebutton: contextToggleButtonFields\n    });\n    const contextFormSchema = objOf([\n      defaultedType('contextform'),\n      defaultedFunction('initValue', constant$1('')),\n      optionalLabel,\n      requiredArrayOf('commands', toggleOrNormal),\n      optionOf('launch', choose$1('type', {\n        contextformbutton: launchButtonFields,\n        contextformtogglebutton: launchToggleButtonFields\n      }))\n    ].concat(contextBarFields));\n    const createContextForm = spec => asRaw('ContextForm', contextFormSchema, spec);\n\n    const contextToolbarSchema = objOf([\n      defaultedType('contexttoolbar'),\n      requiredString('items')\n    ].concat(contextBarFields));\n    const createContextToolbar = spec => asRaw('ContextToolbar', contextToolbarSchema, spec);\n\n    const cardImageFields = [\n      type,\n      requiredString('src'),\n      optionString('alt'),\n      defaultedArrayOf('classes', [], string)\n    ];\n    const cardImageSchema = objOf(cardImageFields);\n\n    const cardTextFields = [\n      type,\n      text$1,\n      optionalName,\n      defaultedArrayOf('classes', ['tox-collection__item-label'], string)\n    ];\n    const cardTextSchema = objOf(cardTextFields);\n\n    const itemSchema$1 = valueThunk(() => choose$2('type', {\n      cardimage: cardImageSchema,\n      cardtext: cardTextSchema,\n      cardcontainer: cardContainerSchema\n    }));\n    const cardContainerSchema = objOf([\n      type,\n      defaultedString('direction', 'horizontal'),\n      defaultedString('align', 'left'),\n      defaultedString('valign', 'middle'),\n      requiredArrayOf('items', itemSchema$1)\n    ]);\n\n    const commonMenuItemFields = [\n      enabled,\n      optionalText,\n      optionalShortcut,\n      generatedValue('menuitem'),\n      defaultedMeta\n    ];\n\n    const cardMenuItemSchema = objOf([\n      type,\n      optionalLabel,\n      requiredArrayOf('items', itemSchema$1),\n      onSetup,\n      defaultedOnAction\n    ].concat(commonMenuItemFields));\n    const createCardMenuItem = spec => asRaw('cardmenuitem', cardMenuItemSchema, spec);\n\n    const choiceMenuItemSchema = objOf([\n      type,\n      active,\n      optionalIcon\n    ].concat(commonMenuItemFields));\n    const createChoiceMenuItem = spec => asRaw('choicemenuitem', choiceMenuItemSchema, spec);\n\n    const baseFields = [\n      type,\n      requiredString('fancytype'),\n      defaultedOnAction\n    ];\n    const insertTableFields = [defaulted('initData', {})].concat(baseFields);\n    const colorSwatchFields = [defaultedObjOf('initData', {}, [\n        defaultedBoolean('allowCustomColors', true),\n        defaultedString('storageKey', 'default'),\n        optionArrayOf('colors', anyValue())\n      ])].concat(baseFields);\n    const fancyMenuItemSchema = choose$1('fancytype', {\n      inserttable: insertTableFields,\n      colorswatch: colorSwatchFields\n    });\n    const createFancyMenuItem = spec => asRaw('fancymenuitem', fancyMenuItemSchema, spec);\n\n    const menuItemSchema = objOf([\n      type,\n      onSetup,\n      defaultedOnAction,\n      optionalIcon\n    ].concat(commonMenuItemFields));\n    const createMenuItem = spec => asRaw('menuitem', menuItemSchema, spec);\n\n    const nestedMenuItemSchema = objOf([\n      type,\n      getSubmenuItems,\n      onSetup,\n      optionalIcon\n    ].concat(commonMenuItemFields));\n    const createNestedMenuItem = spec => asRaw('nestedmenuitem', nestedMenuItemSchema, spec);\n\n    const toggleMenuItemSchema = objOf([\n      type,\n      optionalIcon,\n      active,\n      onSetup,\n      onAction\n    ].concat(commonMenuItemFields));\n    const createToggleMenuItem = spec => asRaw('togglemenuitem', toggleMenuItemSchema, spec);\n\n    const detectSize = (comp, margin, selectorClass) => {\n      const descendants$1 = descendants(comp.element, '.' + selectorClass);\n      if (descendants$1.length > 0) {\n        const columnLength = findIndex$1(descendants$1, c => {\n          const thisTop = c.dom.getBoundingClientRect().top;\n          const cTop = descendants$1[0].dom.getBoundingClientRect().top;\n          return Math.abs(thisTop - cTop) > margin;\n        }).getOr(descendants$1.length);\n        return Optional.some({\n          numColumns: columnLength,\n          numRows: Math.ceil(descendants$1.length / columnLength)\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n\n    const namedEvents = (name, handlers) => derive$1([config(name, handlers)]);\n    const unnamedEvents = handlers => namedEvents(generate$6('unnamed-events'), handlers);\n    const SimpleBehaviours = {\n      namedEvents,\n      unnamedEvents\n    };\n\n    const ExclusivityChannel = generate$6('tooltip.exclusive');\n    const ShowTooltipEvent = generate$6('tooltip.show');\n    const HideTooltipEvent = generate$6('tooltip.hide');\n\n    const hideAllExclusive = (component, _tConfig, _tState) => {\n      component.getSystem().broadcastOn([ExclusivityChannel], {});\n    };\n    const setComponents = (component, tConfig, tState, specs) => {\n      tState.getTooltip().each(tooltip => {\n        if (tooltip.getSystem().isConnected()) {\n          Replacing.set(tooltip, specs);\n        }\n      });\n    };\n\n    var TooltippingApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        hideAllExclusive: hideAllExclusive,\n        setComponents: setComponents\n    });\n\n    const events$9 = (tooltipConfig, state) => {\n      const hide = comp => {\n        state.getTooltip().each(p => {\n          detach(p);\n          tooltipConfig.onHide(comp, p);\n          state.clearTooltip();\n        });\n        state.clearTimer();\n      };\n      const show = comp => {\n        if (!state.isShowing()) {\n          hideAllExclusive(comp);\n          const sink = tooltipConfig.lazySink(comp).getOrDie();\n          const popup = comp.getSystem().build({\n            dom: tooltipConfig.tooltipDom,\n            components: tooltipConfig.tooltipComponents,\n            events: derive$2(tooltipConfig.mode === 'normal' ? [\n              run$1(mouseover(), _ => {\n                emit(comp, ShowTooltipEvent);\n              }),\n              run$1(mouseout(), _ => {\n                emit(comp, HideTooltipEvent);\n              })\n            ] : []),\n            behaviours: derive$1([Replacing.config({})])\n          });\n          state.setTooltip(popup);\n          attach(sink, popup);\n          tooltipConfig.onShow(comp, popup);\n          Positioning.position(sink, popup, { anchor: tooltipConfig.anchor(comp) });\n        }\n      };\n      return derive$2(flatten([\n        [\n          run$1(ShowTooltipEvent, comp => {\n            state.resetTimer(() => {\n              show(comp);\n            }, tooltipConfig.delay);\n          }),\n          run$1(HideTooltipEvent, comp => {\n            state.resetTimer(() => {\n              hide(comp);\n            }, tooltipConfig.delay);\n          }),\n          run$1(receive(), (comp, message) => {\n            const receivingData = message;\n            if (!receivingData.universal) {\n              if (contains$2(receivingData.channels, ExclusivityChannel)) {\n                hide(comp);\n              }\n            }\n          }),\n          runOnDetached(comp => {\n            hide(comp);\n          })\n        ],\n        tooltipConfig.mode === 'normal' ? [\n          run$1(focusin(), comp => {\n            emit(comp, ShowTooltipEvent);\n          }),\n          run$1(postBlur(), comp => {\n            emit(comp, HideTooltipEvent);\n          }),\n          run$1(mouseover(), comp => {\n            emit(comp, ShowTooltipEvent);\n          }),\n          run$1(mouseout(), comp => {\n            emit(comp, HideTooltipEvent);\n          })\n        ] : [\n          run$1(highlight$1(), (comp, _se) => {\n            emit(comp, ShowTooltipEvent);\n          }),\n          run$1(dehighlight$1(), comp => {\n            emit(comp, HideTooltipEvent);\n          })\n        ]\n      ]));\n    };\n\n    var ActiveTooltipping = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$9\n    });\n\n    var TooltippingSchema = [\n      required$1('lazySink'),\n      required$1('tooltipDom'),\n      defaulted('exclusive', true),\n      defaulted('tooltipComponents', []),\n      defaulted('delay', 300),\n      defaultedStringEnum('mode', 'normal', [\n        'normal',\n        'follow-highlight'\n      ]),\n      defaulted('anchor', comp => ({\n        type: 'hotspot',\n        hotspot: comp,\n        layouts: {\n          onLtr: constant$1([\n            south$2,\n            north$2,\n            southeast$2,\n            northeast$2,\n            southwest$2,\n            northwest$2\n          ]),\n          onRtl: constant$1([\n            south$2,\n            north$2,\n            southeast$2,\n            northeast$2,\n            southwest$2,\n            northwest$2\n          ])\n        }\n      })),\n      onHandler('onHide'),\n      onHandler('onShow')\n    ];\n\n    const init$b = () => {\n      const timer = value$2();\n      const popup = value$2();\n      const clearTimer = () => {\n        timer.on(clearTimeout);\n      };\n      const resetTimer = (f, delay) => {\n        clearTimer();\n        timer.set(setTimeout(f, delay));\n      };\n      const readState = constant$1('not-implemented');\n      return nu$8({\n        getTooltip: popup.get,\n        isShowing: popup.isSet,\n        setTooltip: popup.set,\n        clearTooltip: popup.clear,\n        clearTimer,\n        resetTimer,\n        readState\n      });\n    };\n\n    var TooltippingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$b\n    });\n\n    const Tooltipping = create$4({\n      fields: TooltippingSchema,\n      name: 'tooltipping',\n      active: ActiveTooltipping,\n      state: TooltippingState,\n      apis: TooltippingApis\n    });\n\n    const escape = text => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n    const ReadOnlyChannel = 'silver.readonly';\n    const ReadOnlyDataSchema = objOf([requiredBoolean('readonly')]);\n    const broadcastReadonly = (uiRefs, readonly) => {\n      const outerContainer = uiRefs.mainUi.outerContainer;\n      const target = outerContainer.element;\n      const motherships = [\n        uiRefs.mainUi.mothership,\n        ...uiRefs.uiMotherships\n      ];\n      if (readonly) {\n        each$1(motherships, m => {\n          m.broadcastOn([dismissPopups()], { target });\n        });\n      }\n      each$1(motherships, m => {\n        m.broadcastOn([ReadOnlyChannel], { readonly });\n      });\n    };\n    const setupReadonlyModeSwitch = (editor, uiRefs) => {\n      editor.on('init', () => {\n        if (editor.mode.isReadOnly()) {\n          broadcastReadonly(uiRefs, true);\n        }\n      });\n      editor.on('SwitchMode', () => broadcastReadonly(uiRefs, editor.mode.isReadOnly()));\n      if (isReadOnly(editor)) {\n        editor.mode.set('readonly');\n      }\n    };\n    const receivingConfig = () => Receiving.config({\n      channels: {\n        [ReadOnlyChannel]: {\n          schema: ReadOnlyDataSchema,\n          onReceive: (comp, data) => {\n            Disabling.set(comp, data.readonly);\n          }\n        }\n      }\n    });\n\n    const item = disabled => Disabling.config({\n      disabled,\n      disableClass: 'tox-collection__item--state-disabled'\n    });\n    const button = disabled => Disabling.config({ disabled });\n    const splitButton = disabled => Disabling.config({\n      disabled,\n      disableClass: 'tox-tbtn--disabled'\n    });\n    const toolbarButton = disabled => Disabling.config({\n      disabled,\n      disableClass: 'tox-tbtn--disabled',\n      useNative: false\n    });\n    const DisablingConfigs = {\n      item,\n      button,\n      splitButton,\n      toolbarButton\n    };\n\n    const runWithApi = (info, comp) => {\n      const api = info.getApi(comp);\n      return f => {\n        f(api);\n      };\n    };\n    const onControlAttached = (info, editorOffCell) => runOnAttached(comp => {\n      const run = runWithApi(info, comp);\n      run(api => {\n        const onDestroy = info.onSetup(api);\n        if (isFunction(onDestroy)) {\n          editorOffCell.set(onDestroy);\n        }\n      });\n    });\n    const onControlDetached = (getApi, editorOffCell) => runOnDetached(comp => runWithApi(getApi, comp)(editorOffCell.get()));\n\n    const onMenuItemExecute = (info, itemResponse) => runOnExecute$1((comp, simulatedEvent) => {\n      runWithApi(info, comp)(info.onAction);\n      if (!info.triggersSubmenu && itemResponse === ItemResponse$1.CLOSE_ON_EXECUTE) {\n        if (comp.getSystem().isConnected()) {\n          emit(comp, sandboxClose());\n        }\n        simulatedEvent.stop();\n      }\n    });\n    const menuItemEventOrder = {\n      [execute$5()]: [\n        'disabling',\n        'alloy.base.behaviour',\n        'toggling',\n        'item-events'\n      ]\n    };\n\n    const componentRenderPipeline = cat;\n    const renderCommonItem = (spec, structure, itemResponse, providersBackstage) => {\n      const editorOffCell = Cell(noop);\n      return {\n        type: 'item',\n        dom: structure.dom,\n        components: componentRenderPipeline(structure.optComponents),\n        data: spec.data,\n        eventOrder: menuItemEventOrder,\n        hasSubmenu: spec.triggersSubmenu,\n        itemBehaviours: derive$1([\n          config('item-events', [\n            onMenuItemExecute(spec, itemResponse),\n            onControlAttached(spec, editorOffCell),\n            onControlDetached(spec, editorOffCell)\n          ]),\n          DisablingConfigs.item(() => !spec.enabled || providersBackstage.isDisabled()),\n          receivingConfig(),\n          Replacing.config({})\n        ].concat(spec.itemBehaviours))\n      };\n    };\n    const buildData = source => ({\n      value: source.value,\n      meta: {\n        text: source.text.getOr(''),\n        ...source.meta\n      }\n    });\n\n    const convertText = source => {\n      const isMac = global$5.os.isMacOS() || global$5.os.isiOS();\n      const mac = {\n        alt: '\\u2325',\n        ctrl: '\\u2303',\n        shift: '\\u21E7',\n        meta: '\\u2318',\n        access: '\\u2303\\u2325'\n      };\n      const other = {\n        meta: 'Ctrl',\n        access: 'Shift+Alt'\n      };\n      const replace = isMac ? mac : other;\n      const shortcut = source.split('+');\n      const updated = map$2(shortcut, segment => {\n        const search = segment.toLowerCase().trim();\n        return has$2(replace, search) ? replace[search] : segment;\n      });\n      return isMac ? updated.join('') : updated.join('+');\n    };\n\n    const renderIcon$1 = (name, icons, classes = [iconClass]) => render$3(name, {\n      tag: 'div',\n      classes\n    }, icons);\n    const renderText = text => ({\n      dom: {\n        tag: 'div',\n        classes: [textClass]\n      },\n      components: [text$2(global$8.translate(text))]\n    });\n    const renderHtml = (html, classes) => ({\n      dom: {\n        tag: 'div',\n        classes,\n        innerHtml: html\n      }\n    });\n    const renderStyledText = (style, text) => ({\n      dom: {\n        tag: 'div',\n        classes: [textClass]\n      },\n      components: [{\n          dom: {\n            tag: style.tag,\n            styles: style.styles\n          },\n          components: [text$2(global$8.translate(text))]\n        }]\n    });\n    const renderShortcut = shortcut => ({\n      dom: {\n        tag: 'div',\n        classes: [accessoryClass]\n      },\n      components: [text$2(convertText(shortcut))]\n    });\n    const renderCheckmark = icons => renderIcon$1('checkmark', icons, [checkmarkClass]);\n    const renderSubmenuCaret = icons => renderIcon$1('chevron-right', icons, [caretClass]);\n    const renderDownwardsCaret = icons => renderIcon$1('chevron-down', icons, [caretClass]);\n    const renderContainer = (container, components) => {\n      const directionClass = container.direction === 'vertical' ? containerColumnClass : containerRowClass;\n      const alignClass = container.align === 'left' ? containerAlignLeftClass : containerAlignRightClass;\n      const getValignClass = () => {\n        switch (container.valign) {\n        case 'top':\n          return containerValignTopClass;\n        case 'middle':\n          return containerValignMiddleClass;\n        case 'bottom':\n          return containerValignBottomClass;\n        }\n      };\n      return {\n        dom: {\n          tag: 'div',\n          classes: [\n            containerClass,\n            directionClass,\n            alignClass,\n            getValignClass()\n          ]\n        },\n        components\n      };\n    };\n    const renderImage = (src, classes, alt) => ({\n      dom: {\n        tag: 'img',\n        classes,\n        attributes: {\n          src,\n          alt: alt.getOr('')\n        }\n      }\n    });\n\n    const renderColorStructure = (item, providerBackstage, fallbackIcon) => {\n      const colorPickerCommand = 'custom';\n      const removeColorCommand = 'remove';\n      const itemText = item.ariaLabel;\n      const itemValue = item.value;\n      const iconSvg = item.iconContent.map(name => getOr(name, providerBackstage.icons, fallbackIcon));\n      const getDom = () => {\n        const common = colorClass;\n        const icon = iconSvg.getOr('');\n        const attributes = itemText.map(text => ({ title: providerBackstage.translate(text) })).getOr({});\n        const baseDom = {\n          tag: 'div',\n          attributes,\n          classes: [common]\n        };\n        if (itemValue === colorPickerCommand) {\n          return {\n            ...baseDom,\n            tag: 'button',\n            classes: [\n              ...baseDom.classes,\n              'tox-swatches__picker-btn'\n            ],\n            innerHtml: icon\n          };\n        } else if (itemValue === removeColorCommand) {\n          return {\n            ...baseDom,\n            classes: [\n              ...baseDom.classes,\n              'tox-swatch--remove'\n            ],\n            innerHtml: icon\n          };\n        } else if (isNonNullable(itemValue)) {\n          return {\n            ...baseDom,\n            attributes: {\n              ...baseDom.attributes,\n              'data-mce-color': itemValue\n            },\n            styles: { 'background-color': itemValue },\n            innerHtml: icon\n          };\n        } else {\n          return baseDom;\n        }\n      };\n      return {\n        dom: getDom(),\n        optComponents: []\n      };\n    };\n    const renderItemDomStructure = ariaLabel => {\n      const domTitle = ariaLabel.map(label => ({ attributes: { title: global$8.translate(label) } })).getOr({});\n      return {\n        tag: 'div',\n        classes: [\n          navClass,\n          selectableClass\n        ],\n        ...domTitle\n      };\n    };\n    const renderNormalItemStructure = (info, providersBackstage, renderIcons, fallbackIcon) => {\n      const iconSpec = {\n        tag: 'div',\n        classes: [iconClass]\n      };\n      const renderIcon = iconName => render$3(iconName, iconSpec, providersBackstage.icons, fallbackIcon);\n      const renderEmptyIcon = () => Optional.some({ dom: iconSpec });\n      const leftIcon = renderIcons ? info.iconContent.map(renderIcon).orThunk(renderEmptyIcon) : Optional.none();\n      const checkmark = info.checkMark;\n      const textRender = Optional.from(info.meta).fold(() => renderText, meta => has$2(meta, 'style') ? curry(renderStyledText, meta.style) : renderText);\n      const content = info.htmlContent.fold(() => info.textContent.map(textRender), html => Optional.some(renderHtml(html, [textClass])));\n      const menuItem = {\n        dom: renderItemDomStructure(info.ariaLabel),\n        optComponents: [\n          leftIcon,\n          content,\n          info.shortcutContent.map(renderShortcut),\n          checkmark,\n          info.caret\n        ]\n      };\n      return menuItem;\n    };\n    const renderItemStructure = (info, providersBackstage, renderIcons, fallbackIcon = Optional.none()) => {\n      if (info.presets === 'color') {\n        return renderColorStructure(info, providersBackstage, fallbackIcon);\n      } else {\n        return renderNormalItemStructure(info, providersBackstage, renderIcons, fallbackIcon);\n      }\n    };\n\n    const tooltipBehaviour = (meta, sharedBackstage) => get$g(meta, 'tooltipWorker').map(tooltipWorker => [Tooltipping.config({\n        lazySink: sharedBackstage.getSink,\n        tooltipDom: {\n          tag: 'div',\n          classes: ['tox-tooltip-worker-container']\n        },\n        tooltipComponents: [],\n        anchor: comp => ({\n          type: 'submenu',\n          item: comp,\n          overrides: { maxHeightFunction: expandable$1 }\n        }),\n        mode: 'follow-highlight',\n        onShow: (component, _tooltip) => {\n          tooltipWorker(elm => {\n            Tooltipping.setComponents(component, [external$1({ element: SugarElement.fromDom(elm) })]);\n          });\n        }\n      })]).getOr([]);\n    const encodeText = text => global$7.DOM.encode(text);\n    const replaceText = (text, matchText) => {\n      const translated = global$8.translate(text);\n      const encoded = encodeText(translated);\n      if (matchText.length > 0) {\n        const escapedMatchRegex = new RegExp(escape(matchText), 'gi');\n        return encoded.replace(escapedMatchRegex, match => `<span class=\"tox-autocompleter-highlight\">${ match }</span>`);\n      } else {\n        return encoded;\n      }\n    };\n    const renderAutocompleteItem = (spec, matchText, useText, presets, onItemValueHandler, itemResponse, sharedBackstage, renderIcons = true) => {\n      const structure = renderItemStructure({\n        presets,\n        textContent: Optional.none(),\n        htmlContent: useText ? spec.text.map(text => replaceText(text, matchText)) : Optional.none(),\n        ariaLabel: spec.text,\n        iconContent: spec.icon,\n        shortcutContent: Optional.none(),\n        checkMark: Optional.none(),\n        caret: Optional.none(),\n        value: spec.value\n      }, sharedBackstage.providers, renderIcons, spec.icon);\n      return renderCommonItem({\n        data: buildData(spec),\n        enabled: spec.enabled,\n        getApi: constant$1({}),\n        onAction: _api => onItemValueHandler(spec.value, spec.meta),\n        onSetup: constant$1(noop),\n        triggersSubmenu: false,\n        itemBehaviours: tooltipBehaviour(spec.meta, sharedBackstage)\n      }, structure, itemResponse, sharedBackstage.providers);\n    };\n\n    const render$2 = (items, extras) => map$2(items, item => {\n      switch (item.type) {\n      case 'cardcontainer':\n        return renderContainer(item, render$2(item.items, extras));\n      case 'cardimage':\n        return renderImage(item.src, item.classes, item.alt);\n      case 'cardtext':\n        const shouldHighlight = item.name.exists(name => contains$2(extras.cardText.highlightOn, name));\n        const matchText = shouldHighlight ? Optional.from(extras.cardText.matchText).getOr('') : '';\n        return renderHtml(replaceText(item.text, matchText), item.classes);\n      }\n    });\n    const renderCardMenuItem = (spec, itemResponse, sharedBackstage, extras) => {\n      const getApi = component => ({\n        isEnabled: () => !Disabling.isDisabled(component),\n        setEnabled: state => {\n          Disabling.set(component, !state);\n          each$1(descendants(component.element, '*'), elm => {\n            component.getSystem().getByDom(elm).each(comp => {\n              if (comp.hasConfigured(Disabling)) {\n                Disabling.set(comp, !state);\n              }\n            });\n          });\n        }\n      });\n      const structure = {\n        dom: renderItemDomStructure(spec.label),\n        optComponents: [Optional.some({\n            dom: {\n              tag: 'div',\n              classes: [\n                containerClass,\n                containerRowClass\n              ]\n            },\n            components: render$2(spec.items, extras)\n          })]\n      };\n      return renderCommonItem({\n        data: buildData({\n          text: Optional.none(),\n          ...spec\n        }),\n        enabled: spec.enabled,\n        getApi,\n        onAction: spec.onAction,\n        onSetup: spec.onSetup,\n        triggersSubmenu: false,\n        itemBehaviours: Optional.from(extras.itemBehaviours).getOr([])\n      }, structure, itemResponse, sharedBackstage.providers);\n    };\n\n    const renderChoiceItem = (spec, useText, presets, onItemValueHandler, isSelected, itemResponse, providersBackstage, renderIcons = true) => {\n      const getApi = component => ({\n        setActive: state => {\n          Toggling.set(component, state);\n        },\n        isActive: () => Toggling.isOn(component),\n        isEnabled: () => !Disabling.isDisabled(component),\n        setEnabled: state => Disabling.set(component, !state)\n      });\n      const structure = renderItemStructure({\n        presets,\n        textContent: useText ? spec.text : Optional.none(),\n        htmlContent: Optional.none(),\n        ariaLabel: spec.text,\n        iconContent: spec.icon,\n        shortcutContent: useText ? spec.shortcut : Optional.none(),\n        checkMark: useText ? Optional.some(renderCheckmark(providersBackstage.icons)) : Optional.none(),\n        caret: Optional.none(),\n        value: spec.value\n      }, providersBackstage, renderIcons);\n      return deepMerge(renderCommonItem({\n        data: buildData(spec),\n        enabled: spec.enabled,\n        getApi,\n        onAction: _api => onItemValueHandler(spec.value),\n        onSetup: api => {\n          api.setActive(isSelected);\n          return noop;\n        },\n        triggersSubmenu: false,\n        itemBehaviours: []\n      }, structure, itemResponse, providersBackstage), {\n        toggling: {\n          toggleClass: tickedClass,\n          toggleOnExecute: false,\n          selected: spec.active,\n          exclusive: true\n        }\n      });\n    };\n\n    const parts$f = generate$3(owner$2(), parts$h());\n\n    const hexColour = value => ({ value });\n    const shorthandRegex = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;\n    const longformRegex = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i;\n    const isHexString = hex => shorthandRegex.test(hex) || longformRegex.test(hex);\n    const normalizeHex = hex => removeLeading(hex, '#').toUpperCase();\n    const fromString$1 = hex => isHexString(hex) ? Optional.some({ value: normalizeHex(hex) }) : Optional.none();\n    const getLongForm = hex => {\n      const hexString = hex.value.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);\n      return { value: hexString };\n    };\n    const extractValues = hex => {\n      const longForm = getLongForm(hex);\n      const splitForm = longformRegex.exec(longForm.value);\n      return splitForm === null ? [\n        'FFFFFF',\n        'FF',\n        'FF',\n        'FF'\n      ] : splitForm;\n    };\n    const toHex = component => {\n      const hex = component.toString(16);\n      return (hex.length === 1 ? '0' + hex : hex).toUpperCase();\n    };\n    const fromRgba = rgbaColour => {\n      const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);\n      return hexColour(value);\n    };\n\n    const min = Math.min;\n    const max = Math.max;\n    const round$1 = Math.round;\n    const rgbRegex = /^\\s*rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)\\s*$/i;\n    const rgbaRegex = /^\\s*rgba\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d?(?:\\.\\d+)?)\\s*\\)\\s*$/i;\n    const rgbaColour = (red, green, blue, alpha) => ({\n      red,\n      green,\n      blue,\n      alpha\n    });\n    const isRgbaComponent = value => {\n      const num = parseInt(value, 10);\n      return num.toString() === value && num >= 0 && num <= 255;\n    };\n    const fromHsv = hsv => {\n      let r;\n      let g;\n      let b;\n      const hue = (hsv.hue || 0) % 360;\n      let saturation = hsv.saturation / 100;\n      let brightness = hsv.value / 100;\n      saturation = max(0, min(saturation, 1));\n      brightness = max(0, min(brightness, 1));\n      if (saturation === 0) {\n        r = g = b = round$1(255 * brightness);\n        return rgbaColour(r, g, b, 1);\n      }\n      const side = hue / 60;\n      const chroma = brightness * saturation;\n      const x = chroma * (1 - Math.abs(side % 2 - 1));\n      const match = brightness - chroma;\n      switch (Math.floor(side)) {\n      case 0:\n        r = chroma;\n        g = x;\n        b = 0;\n        break;\n      case 1:\n        r = x;\n        g = chroma;\n        b = 0;\n        break;\n      case 2:\n        r = 0;\n        g = chroma;\n        b = x;\n        break;\n      case 3:\n        r = 0;\n        g = x;\n        b = chroma;\n        break;\n      case 4:\n        r = x;\n        g = 0;\n        b = chroma;\n        break;\n      case 5:\n        r = chroma;\n        g = 0;\n        b = x;\n        break;\n      default:\n        r = g = b = 0;\n      }\n      r = round$1(255 * (r + match));\n      g = round$1(255 * (g + match));\n      b = round$1(255 * (b + match));\n      return rgbaColour(r, g, b, 1);\n    };\n    const fromHex = hexColour => {\n      const result = extractValues(hexColour);\n      const red = parseInt(result[1], 16);\n      const green = parseInt(result[2], 16);\n      const blue = parseInt(result[3], 16);\n      return rgbaColour(red, green, blue, 1);\n    };\n    const fromStringValues = (red, green, blue, alpha) => {\n      const r = parseInt(red, 10);\n      const g = parseInt(green, 10);\n      const b = parseInt(blue, 10);\n      const a = parseFloat(alpha);\n      return rgbaColour(r, g, b, a);\n    };\n    const fromString = rgbaString => {\n      if (rgbaString === 'transparent') {\n        return Optional.some(rgbaColour(0, 0, 0, 0));\n      }\n      const rgbMatch = rgbRegex.exec(rgbaString);\n      if (rgbMatch !== null) {\n        return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], '1'));\n      }\n      const rgbaMatch = rgbaRegex.exec(rgbaString);\n      if (rgbaMatch !== null) {\n        return Optional.some(fromStringValues(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], rgbaMatch[4]));\n      }\n      return Optional.none();\n    };\n    const toString = rgba => `rgba(${ rgba.red },${ rgba.green },${ rgba.blue },${ rgba.alpha })`;\n    const red = rgbaColour(255, 0, 0, 1);\n\n    const fireSkinLoaded$1 = editor => {\n      editor.dispatch('SkinLoaded');\n    };\n    const fireSkinLoadError$1 = (editor, error) => {\n      editor.dispatch('SkinLoadError', error);\n    };\n    const fireResizeEditor = editor => {\n      editor.dispatch('ResizeEditor');\n    };\n    const fireResizeContent = (editor, e) => {\n      editor.dispatch('ResizeContent', e);\n    };\n    const fireScrollContent = (editor, e) => {\n      editor.dispatch('ScrollContent', e);\n    };\n    const fireTextColorChange = (editor, data) => {\n      editor.dispatch('TextColorChange', data);\n    };\n    const fireAfterProgressState = (editor, state) => {\n      editor.dispatch('AfterProgressState', { state });\n    };\n    const fireResolveName = (editor, node) => editor.dispatch('ResolveName', {\n      name: node.nodeName.toLowerCase(),\n      target: node\n    });\n    const fireToggleToolbarDrawer = (editor, state) => {\n      editor.dispatch('ToggleToolbarDrawer', { state });\n    };\n\n    var global$4 = tinymce.util.Tools.resolve('tinymce.util.LocalStorage');\n\n    const cacheStorage = {};\n    const ColorCache = (storageId, max = 10) => {\n      const storageString = global$4.getItem(storageId);\n      const localstorage = isString(storageString) ? JSON.parse(storageString) : [];\n      const prune = list => {\n        const diff = max - list.length;\n        return diff < 0 ? list.slice(0, max) : list;\n      };\n      const cache = prune(localstorage);\n      const add = key => {\n        indexOf(cache, key).each(remove);\n        cache.unshift(key);\n        if (cache.length > max) {\n          cache.pop();\n        }\n        global$4.setItem(storageId, JSON.stringify(cache));\n      };\n      const remove = idx => {\n        cache.splice(idx, 1);\n      };\n      const state = () => cache.slice(0);\n      return {\n        add,\n        state\n      };\n    };\n    const getCacheForId = id => get$g(cacheStorage, id).getOrThunk(() => {\n      const storageId = `tinymce-custom-colors-${ id }`;\n      const currentData = global$4.getItem(storageId);\n      if (isNullable(currentData)) {\n        const legacyDefault = global$4.getItem('tinymce-custom-colors');\n        global$4.setItem(storageId, isNonNullable(legacyDefault) ? legacyDefault : '[]');\n      }\n      const storage = ColorCache(storageId, 10);\n      cacheStorage[id] = storage;\n      return storage;\n    });\n    const getCurrentColors = id => map$2(getCacheForId(id).state(), color => ({\n      type: 'choiceitem',\n      text: color,\n      icon: 'checkmark',\n      value: color\n    }));\n    const addColor = (id, color) => {\n      getCacheForId(id).add(color);\n    };\n\n    const hsvColour = (hue, saturation, value) => ({\n      hue,\n      saturation,\n      value\n    });\n    const fromRgb = rgbaColour => {\n      let h = 0;\n      let s = 0;\n      let v = 0;\n      const r = rgbaColour.red / 255;\n      const g = rgbaColour.green / 255;\n      const b = rgbaColour.blue / 255;\n      const minRGB = Math.min(r, Math.min(g, b));\n      const maxRGB = Math.max(r, Math.max(g, b));\n      if (minRGB === maxRGB) {\n        v = minRGB;\n        return hsvColour(0, 0, v * 100);\n      }\n      const d = r === minRGB ? g - b : b === minRGB ? r - g : b - r;\n      h = r === minRGB ? 3 : b === minRGB ? 1 : 5;\n      h = 60 * (h - d / (maxRGB - minRGB));\n      s = (maxRGB - minRGB) / maxRGB;\n      v = maxRGB;\n      return hsvColour(Math.round(h), Math.round(s * 100), Math.round(v * 100));\n    };\n\n    const hexToHsv = hex => fromRgb(fromHex(hex));\n    const hsvToHex = hsv => fromRgba(fromHsv(hsv));\n    const anyToHex = color => fromString$1(color).orThunk(() => fromString(color).map(fromRgba)).getOrThunk(() => {\n      const canvas = document.createElement('canvas');\n      canvas.height = 1;\n      canvas.width = 1;\n      const canvasContext = canvas.getContext('2d');\n      canvasContext.clearRect(0, 0, canvas.width, canvas.height);\n      canvasContext.fillStyle = '#FFFFFF';\n      canvasContext.fillStyle = color;\n      canvasContext.fillRect(0, 0, 1, 1);\n      const rgba = canvasContext.getImageData(0, 0, 1, 1).data;\n      const r = rgba[0];\n      const g = rgba[1];\n      const b = rgba[2];\n      const a = rgba[3];\n      return fromRgba(rgbaColour(r, g, b, a));\n    });\n\n    const foregroundId = 'forecolor';\n    const backgroundId = 'hilitecolor';\n    const calcCols = colors => Math.max(5, Math.ceil(Math.sqrt(colors)));\n    const mapColors = colorMap => {\n      const colors = [];\n      for (let i = 0; i < colorMap.length; i += 2) {\n        colors.push({\n          text: colorMap[i + 1],\n          value: '#' + anyToHex(colorMap[i]).value,\n          icon: 'checkmark',\n          type: 'choiceitem'\n        });\n      }\n      return colors;\n    };\n    const option$1 = name => editor => editor.options.get(name);\n    const fallbackColor = '#000000';\n    const register$d = editor => {\n      const registerOption = editor.options.register;\n      const colorProcessor = value => {\n        if (isArrayOf(value, isString)) {\n          return {\n            value: mapColors(value),\n            valid: true\n          };\n        } else {\n          return {\n            valid: false,\n            message: 'Must be an array of strings.'\n          };\n        }\n      };\n      registerOption('color_map', {\n        processor: colorProcessor,\n        default: [\n          '#BFEDD2',\n          'Light Green',\n          '#FBEEB8',\n          'Light Yellow',\n          '#F8CAC6',\n          'Light Red',\n          '#ECCAFA',\n          'Light Purple',\n          '#C2E0F4',\n          'Light Blue',\n          '#2DC26B',\n          'Green',\n          '#F1C40F',\n          'Yellow',\n          '#E03E2D',\n          'Red',\n          '#B96AD9',\n          'Purple',\n          '#3598DB',\n          'Blue',\n          '#169179',\n          'Dark Turquoise',\n          '#E67E23',\n          'Orange',\n          '#BA372A',\n          'Dark Red',\n          '#843FA1',\n          'Dark Purple',\n          '#236FA1',\n          'Dark Blue',\n          '#ECF0F1',\n          'Light Gray',\n          '#CED4D9',\n          'Medium Gray',\n          '#95A5A6',\n          'Gray',\n          '#7E8C8D',\n          'Dark Gray',\n          '#34495E',\n          'Navy Blue',\n          '#000000',\n          'Black',\n          '#ffffff',\n          'White'\n        ]\n      });\n      registerOption('color_map_background', { processor: colorProcessor });\n      registerOption('color_map_foreground', { processor: colorProcessor });\n      registerOption('color_cols', {\n        processor: 'number',\n        default: calcCols(getColors$2(editor, 'default').length)\n      });\n      registerOption('color_cols_foreground', {\n        processor: 'number',\n        default: calcCols(getColors$2(editor, foregroundId).length)\n      });\n      registerOption('color_cols_background', {\n        processor: 'number',\n        default: calcCols(getColors$2(editor, backgroundId).length)\n      });\n      registerOption('custom_colors', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('color_default_foreground', {\n        processor: 'string',\n        default: fallbackColor\n      });\n      registerOption('color_default_background', {\n        processor: 'string',\n        default: fallbackColor\n      });\n    };\n    const getColorCols$1 = (editor, id) => {\n      if (id === foregroundId) {\n        return option$1('color_cols_foreground')(editor);\n      } else if (id === backgroundId) {\n        return option$1('color_cols_background')(editor);\n      } else {\n        return option$1('color_cols')(editor);\n      }\n    };\n    const hasCustomColors$1 = option$1('custom_colors');\n    const getColors$2 = (editor, id) => {\n      if (id === foregroundId && editor.options.isSet('color_map_foreground')) {\n        return option$1('color_map_foreground')(editor);\n      } else if (id === backgroundId && editor.options.isSet('color_map_background')) {\n        return option$1('color_map_background')(editor);\n      } else {\n        return option$1('color_map')(editor);\n      }\n    };\n    const getDefaultForegroundColor = option$1('color_default_foreground');\n    const getDefaultBackgroundColor = option$1('color_default_background');\n\n    const getCurrentColor = (editor, format) => {\n      const cssRgbValue = get$e(SugarElement.fromDom(editor.selection.getStart()), format === 'hilitecolor' ? 'background-color' : 'color');\n      return fromString(cssRgbValue).map(rgba => '#' + fromRgba(rgba).value);\n    };\n    const applyFormat = (editor, format, value) => {\n      editor.undoManager.transact(() => {\n        editor.focus();\n        editor.formatter.apply(format, { value });\n        editor.nodeChanged();\n      });\n    };\n    const removeFormat = (editor, format) => {\n      editor.undoManager.transact(() => {\n        editor.focus();\n        editor.formatter.remove(format, { value: null }, undefined, true);\n        editor.nodeChanged();\n      });\n    };\n    const registerCommands = editor => {\n      editor.addCommand('mceApplyTextcolor', (format, value) => {\n        applyFormat(editor, format, value);\n      });\n      editor.addCommand('mceRemoveTextcolor', format => {\n        removeFormat(editor, format);\n      });\n    };\n    const getAdditionalColors = hasCustom => {\n      const type = 'choiceitem';\n      const remove = {\n        type,\n        text: 'Remove color',\n        icon: 'color-swatch-remove-color',\n        value: 'remove'\n      };\n      const custom = {\n        type,\n        text: 'Custom color',\n        icon: 'color-picker',\n        value: 'custom'\n      };\n      return hasCustom ? [\n        remove,\n        custom\n      ] : [remove];\n    };\n    const applyColor = (editor, format, value, onChoice) => {\n      if (value === 'custom') {\n        const dialog = colorPickerDialog(editor);\n        dialog(colorOpt => {\n          colorOpt.each(color => {\n            addColor(format, color);\n            editor.execCommand('mceApplyTextcolor', format, color);\n            onChoice(color);\n          });\n        }, getCurrentColor(editor, format).getOr(fallbackColor));\n      } else if (value === 'remove') {\n        onChoice('');\n        editor.execCommand('mceRemoveTextcolor', format);\n      } else {\n        onChoice(value);\n        editor.execCommand('mceApplyTextcolor', format, value);\n      }\n    };\n    const getColors$1 = (colors, id, hasCustom) => colors.concat(getCurrentColors(id).concat(getAdditionalColors(hasCustom)));\n    const getFetch$1 = (colors, id, hasCustom) => callback => {\n      callback(getColors$1(colors, id, hasCustom));\n    };\n    const setIconColor = (splitButtonApi, name, newColor) => {\n      const id = name === 'forecolor' ? 'tox-icon-text-color__color' : 'tox-icon-highlight-bg-color__color';\n      splitButtonApi.setIconFill(id, newColor);\n    };\n    const registerTextColorButton = (editor, name, format, tooltip, lastColor) => {\n      editor.ui.registry.addSplitButton(name, {\n        tooltip,\n        presets: 'color',\n        icon: name === 'forecolor' ? 'text-color' : 'highlight-bg-color',\n        select: value => {\n          const optCurrentHex = getCurrentColor(editor, format);\n          return is$1(optCurrentHex, value.toUpperCase());\n        },\n        columns: getColorCols$1(editor, format),\n        fetch: getFetch$1(getColors$2(editor, format), format, hasCustomColors$1(editor)),\n        onAction: _splitButtonApi => {\n          applyColor(editor, format, lastColor.get(), noop);\n        },\n        onItemAction: (_splitButtonApi, value) => {\n          applyColor(editor, format, value, newColor => {\n            lastColor.set(newColor);\n            fireTextColorChange(editor, {\n              name,\n              color: newColor\n            });\n          });\n        },\n        onSetup: splitButtonApi => {\n          setIconColor(splitButtonApi, name, lastColor.get());\n          const handler = e => {\n            if (e.name === name) {\n              setIconColor(splitButtonApi, e.name, e.color);\n            }\n          };\n          editor.on('TextColorChange', handler);\n          return () => {\n            editor.off('TextColorChange', handler);\n          };\n        }\n      });\n    };\n    const registerTextColorMenuItem = (editor, name, format, text) => {\n      editor.ui.registry.addNestedMenuItem(name, {\n        text,\n        icon: name === 'forecolor' ? 'text-color' : 'highlight-bg-color',\n        getSubmenuItems: () => [{\n            type: 'fancymenuitem',\n            fancytype: 'colorswatch',\n            initData: { storageKey: format },\n            onAction: data => {\n              applyColor(editor, format, data.value, noop);\n            }\n          }]\n      });\n    };\n    const colorPickerDialog = editor => (callback, value) => {\n      let isValid = false;\n      const onSubmit = api => {\n        const data = api.getData();\n        const hex = data.colorpicker;\n        if (isValid) {\n          callback(Optional.from(hex));\n          api.close();\n        } else {\n          editor.windowManager.alert(editor.translate([\n            'Invalid hex color code: {0}',\n            hex\n          ]));\n        }\n      };\n      const onAction = (_api, details) => {\n        if (details.name === 'hex-valid') {\n          isValid = details.value;\n        }\n      };\n      const initialData = { colorpicker: value };\n      editor.windowManager.open({\n        title: 'Color Picker',\n        size: 'normal',\n        body: {\n          type: 'panel',\n          items: [{\n              type: 'colorpicker',\n              name: 'colorpicker',\n              label: 'Color'\n            }]\n        },\n        buttons: [\n          {\n            type: 'cancel',\n            name: 'cancel',\n            text: 'Cancel'\n          },\n          {\n            type: 'submit',\n            name: 'save',\n            text: 'Save',\n            primary: true\n          }\n        ],\n        initialData,\n        onAction,\n        onSubmit,\n        onClose: noop,\n        onCancel: () => {\n          callback(Optional.none());\n        }\n      });\n    };\n    const register$c = editor => {\n      registerCommands(editor);\n      const fallbackColorForeground = getDefaultForegroundColor(editor);\n      const fallbackColorBackground = getDefaultBackgroundColor(editor);\n      const lastForeColor = Cell(fallbackColorForeground);\n      const lastBackColor = Cell(fallbackColorBackground);\n      registerTextColorButton(editor, 'forecolor', 'forecolor', 'Text color', lastForeColor);\n      registerTextColorButton(editor, 'backcolor', 'hilitecolor', 'Background color', lastBackColor);\n      registerTextColorMenuItem(editor, 'forecolor', 'forecolor', 'Text color');\n      registerTextColorMenuItem(editor, 'backcolor', 'hilitecolor', 'Background color');\n    };\n\n    const createPartialChoiceMenu = (value, items, onItemValueHandler, columns, presets, itemResponse, select, providersBackstage) => {\n      const hasIcons = menuHasIcons(items);\n      const presetItemTypes = presets !== 'color' ? 'normal' : 'color';\n      const alloyItems = createChoiceItems(items, onItemValueHandler, columns, presetItemTypes, itemResponse, select, providersBackstage);\n      const menuLayout = { menuType: presets };\n      return createPartialMenuWithAlloyItems(value, hasIcons, alloyItems, columns, menuLayout);\n    };\n    const createChoiceItems = (items, onItemValueHandler, columns, itemPresets, itemResponse, select, providersBackstage) => cat(map$2(items, item => {\n      if (item.type === 'choiceitem') {\n        return createChoiceMenuItem(item).fold(handleError, d => Optional.some(renderChoiceItem(d, columns === 1, itemPresets, onItemValueHandler, select(d.value), itemResponse, providersBackstage, menuHasIcons(items))));\n      } else {\n        return Optional.none();\n      }\n    }));\n\n    const deriveMenuMovement = (columns, presets) => {\n      const menuMarkers = markers(presets);\n      if (columns === 1) {\n        return {\n          mode: 'menu',\n          moveOnTab: true\n        };\n      } else if (columns === 'auto') {\n        return {\n          mode: 'grid',\n          selector: '.' + menuMarkers.item,\n          initSize: {\n            numColumns: 1,\n            numRows: 1\n          }\n        };\n      } else {\n        const rowClass = presets === 'color' ? 'tox-swatches__row' : 'tox-collection__group';\n        return {\n          mode: 'matrix',\n          rowSelector: '.' + rowClass,\n          previousSelector: menu => {\n            return presets === 'color' ? descendant(menu.element, '[aria-checked=true]') : Optional.none();\n          }\n        };\n      }\n    };\n    const deriveCollectionMovement = (columns, presets) => {\n      if (columns === 1) {\n        return {\n          mode: 'menu',\n          moveOnTab: false,\n          selector: '.tox-collection__item'\n        };\n      } else if (columns === 'auto') {\n        return {\n          mode: 'flatgrid',\n          selector: '.' + 'tox-collection__item',\n          initSize: {\n            numColumns: 1,\n            numRows: 1\n          }\n        };\n      } else {\n        return {\n          mode: 'matrix',\n          selectors: {\n            row: presets === 'color' ? '.tox-swatches__row' : '.tox-collection__group',\n            cell: presets === 'color' ? `.${ colorClass }` : `.${ selectableClass }`\n          }\n        };\n      }\n    };\n\n    const renderColorSwatchItem = (spec, backstage) => {\n      const items = getColorItems(spec, backstage);\n      const columns = backstage.colorinput.getColorCols(spec.initData.storageKey);\n      const presets = 'color';\n      const menuSpec = createPartialChoiceMenu(generate$6('menu-value'), items, value => {\n        spec.onAction({ value });\n      }, columns, presets, ItemResponse$1.CLOSE_ON_EXECUTE, never, backstage.shared.providers);\n      const widgetSpec = {\n        ...menuSpec,\n        markers: markers(presets),\n        movement: deriveMenuMovement(columns, presets)\n      };\n      return {\n        type: 'widget',\n        data: { value: generate$6('widget-id') },\n        dom: {\n          tag: 'div',\n          classes: ['tox-fancymenuitem']\n        },\n        autofocus: true,\n        components: [parts$f.widget(Menu.sketch(widgetSpec))]\n      };\n    };\n    const getColorItems = (spec, backstage) => {\n      const useCustomColors = spec.initData.allowCustomColors && backstage.colorinput.hasCustomColors();\n      return spec.initData.colors.fold(() => getColors$1(backstage.colorinput.getColors(spec.initData.storageKey), spec.initData.storageKey, useCustomColors), colors => colors.concat(getAdditionalColors(useCustomColors)));\n    };\n\n    const cellOverEvent = generate$6('cell-over');\n    const cellExecuteEvent = generate$6('cell-execute');\n    const makeCell = (row, col, labelId) => {\n      const emitCellOver = c => emitWith(c, cellOverEvent, {\n        row,\n        col\n      });\n      const emitExecute = c => emitWith(c, cellExecuteEvent, {\n        row,\n        col\n      });\n      const onClick = (c, se) => {\n        se.stop();\n        emitExecute(c);\n      };\n      return build$1({\n        dom: {\n          tag: 'div',\n          attributes: {\n            role: 'button',\n            ['aria-labelledby']: labelId\n          }\n        },\n        behaviours: derive$1([\n          config('insert-table-picker-cell', [\n            run$1(mouseover(), Focusing.focus),\n            run$1(execute$5(), emitExecute),\n            run$1(click(), onClick),\n            run$1(tap(), onClick)\n          ]),\n          Toggling.config({\n            toggleClass: 'tox-insert-table-picker__selected',\n            toggleOnExecute: false\n          }),\n          Focusing.config({ onFocus: emitCellOver })\n        ])\n      });\n    };\n    const makeCells = (labelId, numRows, numCols) => {\n      const cells = [];\n      for (let i = 0; i < numRows; i++) {\n        const row = [];\n        for (let j = 0; j < numCols; j++) {\n          row.push(makeCell(i, j, labelId));\n        }\n        cells.push(row);\n      }\n      return cells;\n    };\n    const selectCells = (cells, selectedRow, selectedColumn, numRows, numColumns) => {\n      for (let i = 0; i < numRows; i++) {\n        for (let j = 0; j < numColumns; j++) {\n          Toggling.set(cells[i][j], i <= selectedRow && j <= selectedColumn);\n        }\n      }\n    };\n    const makeComponents = cells => bind$3(cells, cellRow => map$2(cellRow, premade));\n    const makeLabelText = (row, col) => text$2(`${ col }x${ row }`);\n    const renderInsertTableMenuItem = spec => {\n      const numRows = 10;\n      const numColumns = 10;\n      const sizeLabelId = generate$6('size-label');\n      const cells = makeCells(sizeLabelId, numRows, numColumns);\n      const emptyLabelText = makeLabelText(0, 0);\n      const memLabel = record({\n        dom: {\n          tag: 'span',\n          classes: ['tox-insert-table-picker__label'],\n          attributes: { id: sizeLabelId }\n        },\n        components: [emptyLabelText],\n        behaviours: derive$1([Replacing.config({})])\n      });\n      return {\n        type: 'widget',\n        data: { value: generate$6('widget-id') },\n        dom: {\n          tag: 'div',\n          classes: ['tox-fancymenuitem']\n        },\n        autofocus: true,\n        components: [parts$f.widget({\n            dom: {\n              tag: 'div',\n              classes: ['tox-insert-table-picker']\n            },\n            components: makeComponents(cells).concat(memLabel.asSpec()),\n            behaviours: derive$1([\n              config('insert-table-picker', [\n                runOnAttached(c => {\n                  Replacing.set(memLabel.get(c), [emptyLabelText]);\n                }),\n                runWithTarget(cellOverEvent, (c, t, e) => {\n                  const {row, col} = e.event;\n                  selectCells(cells, row, col, numRows, numColumns);\n                  Replacing.set(memLabel.get(c), [makeLabelText(row + 1, col + 1)]);\n                }),\n                runWithTarget(cellExecuteEvent, (c, _, e) => {\n                  const {row, col} = e.event;\n                  spec.onAction({\n                    numRows: row + 1,\n                    numColumns: col + 1\n                  });\n                  emit(c, sandboxClose());\n                })\n              ]),\n              Keying.config({\n                initSize: {\n                  numRows,\n                  numColumns\n                },\n                mode: 'flatgrid',\n                selector: '[role=\"button\"]'\n              })\n            ])\n          })]\n      };\n    };\n\n    const fancyMenuItems = {\n      inserttable: renderInsertTableMenuItem,\n      colorswatch: renderColorSwatchItem\n    };\n    const renderFancyMenuItem = (spec, backstage) => get$g(fancyMenuItems, spec.fancytype).map(render => render(spec, backstage));\n\n    const renderNestedItem = (spec, itemResponse, providersBackstage, renderIcons = true, downwardsCaret = false) => {\n      const caret = downwardsCaret ? renderDownwardsCaret(providersBackstage.icons) : renderSubmenuCaret(providersBackstage.icons);\n      const getApi = component => ({\n        isEnabled: () => !Disabling.isDisabled(component),\n        setEnabled: state => Disabling.set(component, !state)\n      });\n      const structure = renderItemStructure({\n        presets: 'normal',\n        iconContent: spec.icon,\n        textContent: spec.text,\n        htmlContent: Optional.none(),\n        ariaLabel: spec.text,\n        caret: Optional.some(caret),\n        checkMark: Optional.none(),\n        shortcutContent: spec.shortcut\n      }, providersBackstage, renderIcons);\n      return renderCommonItem({\n        data: buildData(spec),\n        getApi,\n        enabled: spec.enabled,\n        onAction: noop,\n        onSetup: spec.onSetup,\n        triggersSubmenu: true,\n        itemBehaviours: []\n      }, structure, itemResponse, providersBackstage);\n    };\n\n    const renderNormalItem = (spec, itemResponse, providersBackstage, renderIcons = true) => {\n      const getApi = component => ({\n        isEnabled: () => !Disabling.isDisabled(component),\n        setEnabled: state => Disabling.set(component, !state)\n      });\n      const structure = renderItemStructure({\n        presets: 'normal',\n        iconContent: spec.icon,\n        textContent: spec.text,\n        htmlContent: Optional.none(),\n        ariaLabel: spec.text,\n        caret: Optional.none(),\n        checkMark: Optional.none(),\n        shortcutContent: spec.shortcut\n      }, providersBackstage, renderIcons);\n      return renderCommonItem({\n        data: buildData(spec),\n        getApi,\n        enabled: spec.enabled,\n        onAction: spec.onAction,\n        onSetup: spec.onSetup,\n        triggersSubmenu: false,\n        itemBehaviours: []\n      }, structure, itemResponse, providersBackstage);\n    };\n\n    const renderSeparatorItem = spec => ({\n      type: 'separator',\n      dom: {\n        tag: 'div',\n        classes: [\n          selectableClass,\n          groupHeadingClass\n        ]\n      },\n      components: spec.text.map(text$2).toArray()\n    });\n\n    const renderToggleMenuItem = (spec, itemResponse, providersBackstage, renderIcons = true) => {\n      const getApi = component => ({\n        setActive: state => {\n          Toggling.set(component, state);\n        },\n        isActive: () => Toggling.isOn(component),\n        isEnabled: () => !Disabling.isDisabled(component),\n        setEnabled: state => Disabling.set(component, !state)\n      });\n      const structure = renderItemStructure({\n        iconContent: spec.icon,\n        textContent: spec.text,\n        htmlContent: Optional.none(),\n        ariaLabel: spec.text,\n        checkMark: Optional.some(renderCheckmark(providersBackstage.icons)),\n        caret: Optional.none(),\n        shortcutContent: spec.shortcut,\n        presets: 'normal',\n        meta: spec.meta\n      }, providersBackstage, renderIcons);\n      return deepMerge(renderCommonItem({\n        data: buildData(spec),\n        enabled: spec.enabled,\n        getApi,\n        onAction: spec.onAction,\n        onSetup: spec.onSetup,\n        triggersSubmenu: false,\n        itemBehaviours: []\n      }, structure, itemResponse, providersBackstage), {\n        toggling: {\n          toggleClass: tickedClass,\n          toggleOnExecute: false,\n          selected: spec.active\n        }\n      });\n    };\n\n    const autocomplete = renderAutocompleteItem;\n    const separator$3 = renderSeparatorItem;\n    const normal = renderNormalItem;\n    const nested = renderNestedItem;\n    const toggle$1 = renderToggleMenuItem;\n    const fancy = renderFancyMenuItem;\n    const card = renderCardMenuItem;\n\n    const getCoupled = (component, coupleConfig, coupleState, name) => coupleState.getOrCreate(component, coupleConfig, name);\n    const getExistingCoupled = (component, coupleConfig, coupleState, name) => coupleState.getExisting(component, coupleConfig, name);\n\n    var CouplingApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        getCoupled: getCoupled,\n        getExistingCoupled: getExistingCoupled\n    });\n\n    var CouplingSchema = [requiredOf('others', setOf(Result.value, anyValue()))];\n\n    const init$a = () => {\n      const coupled = {};\n      const lookupCoupled = (coupleConfig, coupledName) => {\n        const available = keys(coupleConfig.others);\n        if (available.length === 0) {\n          throw new Error('Cannot find any known coupled components');\n        } else {\n          return get$g(coupled, coupledName);\n        }\n      };\n      const getOrCreate = (component, coupleConfig, name) => {\n        return lookupCoupled(coupleConfig, name).getOrThunk(() => {\n          const builder = get$g(coupleConfig.others, name).getOrDie('No information found for coupled component: ' + name);\n          const spec = builder(component);\n          const built = component.getSystem().build(spec);\n          coupled[name] = built;\n          return built;\n        });\n      };\n      const getExisting = (component, coupleConfig, name) => {\n        return lookupCoupled(coupleConfig, name).orThunk(() => {\n          get$g(coupleConfig.others, name).getOrDie('No information found for coupled component: ' + name);\n          return Optional.none();\n        });\n      };\n      const readState = constant$1({});\n      return nu$8({\n        readState,\n        getExisting,\n        getOrCreate\n      });\n    };\n\n    var CouplingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$a\n    });\n\n    const Coupling = create$4({\n      fields: CouplingSchema,\n      name: 'coupling',\n      apis: CouplingApis,\n      state: CouplingState\n    });\n\n    const nu$3 = baseFn => {\n      let data = Optional.none();\n      let callbacks = [];\n      const map = f => nu$3(nCallback => {\n        get(data => {\n          nCallback(f(data));\n        });\n      });\n      const get = nCallback => {\n        if (isReady()) {\n          call(nCallback);\n        } else {\n          callbacks.push(nCallback);\n        }\n      };\n      const set = x => {\n        if (!isReady()) {\n          data = Optional.some(x);\n          run(callbacks);\n          callbacks = [];\n        }\n      };\n      const isReady = () => data.isSome();\n      const run = cbs => {\n        each$1(cbs, call);\n      };\n      const call = cb => {\n        data.each(x => {\n          setTimeout(() => {\n            cb(x);\n          }, 0);\n        });\n      };\n      baseFn(set);\n      return {\n        get,\n        map,\n        isReady\n      };\n    };\n    const pure$1 = a => nu$3(callback => {\n      callback(a);\n    });\n    const LazyValue = {\n      nu: nu$3,\n      pure: pure$1\n    };\n\n    const errorReporter = err => {\n      setTimeout(() => {\n        throw err;\n      }, 0);\n    };\n    const make$5 = run => {\n      const get = callback => {\n        run().then(callback, errorReporter);\n      };\n      const map = fab => {\n        return make$5(() => run().then(fab));\n      };\n      const bind = aFutureB => {\n        return make$5(() => run().then(v => aFutureB(v).toPromise()));\n      };\n      const anonBind = futureB => {\n        return make$5(() => run().then(() => futureB.toPromise()));\n      };\n      const toLazy = () => {\n        return LazyValue.nu(get);\n      };\n      const toCached = () => {\n        let cache = null;\n        return make$5(() => {\n          if (cache === null) {\n            cache = run();\n          }\n          return cache;\n        });\n      };\n      const toPromise = run;\n      return {\n        map,\n        bind,\n        anonBind,\n        toLazy,\n        toCached,\n        toPromise,\n        get\n      };\n    };\n    const nu$2 = baseFn => {\n      return make$5(() => new Promise(baseFn));\n    };\n    const pure = a => {\n      return make$5(() => Promise.resolve(a));\n    };\n    const Future = {\n      nu: nu$2,\n      pure\n    };\n\n    const suffix = constant$1('sink');\n    const partType$1 = constant$1(optional({\n      name: suffix(),\n      overrides: constant$1({\n        dom: { tag: 'div' },\n        behaviours: derive$1([Positioning.config({ useFixed: always })]),\n        events: derive$2([\n          cutter(keydown()),\n          cutter(mousedown()),\n          cutter(click())\n        ])\n      })\n    }));\n\n    const getAnchor = (detail, component) => {\n      const hotspot = detail.getHotspot(component).getOr(component);\n      const type = 'hotspot';\n      const overrides = detail.getAnchorOverrides();\n      return detail.layouts.fold(() => ({\n        type,\n        hotspot,\n        overrides\n      }), layouts => ({\n        type,\n        hotspot,\n        overrides,\n        layouts\n      }));\n    };\n    const fetch = (detail, mapFetch, component) => {\n      const fetcher = detail.fetch;\n      return fetcher(component).map(mapFetch);\n    };\n    const openF = (detail, mapFetch, anchor, component, sandbox, externals, highlightOnOpen) => {\n      const futureData = fetch(detail, mapFetch, component);\n      const getLazySink = getSink(component, detail);\n      return futureData.map(tdata => tdata.bind(data => Optional.from(tieredMenu.sketch({\n        ...externals.menu(),\n        uid: generate$5(''),\n        data,\n        highlightOnOpen,\n        onOpenMenu: (tmenu, menu) => {\n          const sink = getLazySink().getOrDie();\n          Positioning.position(sink, menu, { anchor });\n          Sandboxing.decloak(sandbox);\n        },\n        onOpenSubmenu: (tmenu, item, submenu) => {\n          const sink = getLazySink().getOrDie();\n          Positioning.position(sink, submenu, {\n            anchor: {\n              type: 'submenu',\n              item\n            }\n          });\n          Sandboxing.decloak(sandbox);\n        },\n        onRepositionMenu: (tmenu, primaryMenu, submenuTriggers) => {\n          const sink = getLazySink().getOrDie();\n          Positioning.position(sink, primaryMenu, { anchor });\n          each$1(submenuTriggers, st => {\n            Positioning.position(sink, st.triggeredMenu, {\n              anchor: {\n                type: 'submenu',\n                item: st.triggeringItem\n              }\n            });\n          });\n        },\n        onEscape: () => {\n          Focusing.focus(component);\n          Sandboxing.close(sandbox);\n          return Optional.some(true);\n        }\n      }))));\n    };\n    const open = (detail, mapFetch, hotspot, sandbox, externals, onOpenSync, highlightOnOpen) => {\n      const anchor = getAnchor(detail, hotspot);\n      const processed = openF(detail, mapFetch, anchor, hotspot, sandbox, externals, highlightOnOpen);\n      return processed.map(tdata => {\n        tdata.fold(() => {\n          if (Sandboxing.isOpen(sandbox)) {\n            Sandboxing.close(sandbox);\n          }\n        }, data => {\n          Sandboxing.cloak(sandbox);\n          Sandboxing.open(sandbox, data);\n          onOpenSync(sandbox);\n        });\n        return sandbox;\n      });\n    };\n    const close = (detail, mapFetch, component, sandbox, _externals, _onOpenSync, _highlightOnOpen) => {\n      Sandboxing.close(sandbox);\n      return Future.pure(sandbox);\n    };\n    const togglePopup = (detail, mapFetch, hotspot, externals, onOpenSync, highlightOnOpen) => {\n      const sandbox = Coupling.getCoupled(hotspot, 'sandbox');\n      const showing = Sandboxing.isOpen(sandbox);\n      const action = showing ? close : open;\n      return action(detail, mapFetch, hotspot, sandbox, externals, onOpenSync, highlightOnOpen);\n    };\n    const matchWidth = (hotspot, container, useMinWidth) => {\n      const menu = Composing.getCurrent(container).getOr(container);\n      const buttonWidth = get$c(hotspot.element);\n      if (useMinWidth) {\n        set$8(menu.element, 'min-width', buttonWidth + 'px');\n      } else {\n        set$7(menu.element, buttonWidth);\n      }\n    };\n    const getSink = (anyInSystem, sinkDetail) => anyInSystem.getSystem().getByUid(sinkDetail.uid + '-' + suffix()).map(internalSink => () => Result.value(internalSink)).getOrThunk(() => sinkDetail.lazySink.fold(() => () => Result.error(new Error('No internal sink is specified, nor could an external sink be found')), lazySinkFn => () => lazySinkFn(anyInSystem)));\n    const doRepositionMenus = sandbox => {\n      Sandboxing.getState(sandbox).each(tmenu => {\n        tieredMenu.repositionMenus(tmenu);\n      });\n    };\n    const makeSandbox$1 = (detail, hotspot, extras) => {\n      const ariaControls = manager();\n      const onOpen = (component, menu) => {\n        const anchor = getAnchor(detail, hotspot);\n        ariaControls.link(hotspot.element);\n        if (detail.matchWidth) {\n          matchWidth(anchor.hotspot, menu, detail.useMinWidth);\n        }\n        detail.onOpen(anchor, component, menu);\n        if (extras !== undefined && extras.onOpen !== undefined) {\n          extras.onOpen(component, menu);\n        }\n      };\n      const onClose = (component, menu) => {\n        ariaControls.unlink(hotspot.element);\n        if (extras !== undefined && extras.onClose !== undefined) {\n          extras.onClose(component, menu);\n        }\n      };\n      const lazySink = getSink(hotspot, detail);\n      return {\n        dom: {\n          tag: 'div',\n          classes: detail.sandboxClasses,\n          attributes: {\n            id: ariaControls.id,\n            role: 'listbox'\n          }\n        },\n        behaviours: SketchBehaviours.augment(detail.sandboxBehaviours, [\n          Representing.config({\n            store: {\n              mode: 'memory',\n              initialValue: hotspot\n            }\n          }),\n          Sandboxing.config({\n            onOpen,\n            onClose,\n            isPartOf: (container, data, queryElem) => {\n              return isPartOf$1(data, queryElem) || isPartOf$1(hotspot, queryElem);\n            },\n            getAttachPoint: () => {\n              return lazySink().getOrDie();\n            }\n          }),\n          Composing.config({\n            find: sandbox => {\n              return Sandboxing.getState(sandbox).bind(menu => Composing.getCurrent(menu));\n            }\n          }),\n          Receiving.config({\n            channels: {\n              ...receivingChannel$1({ isExtraPart: never }),\n              ...receivingChannel({ doReposition: doRepositionMenus })\n            }\n          })\n        ])\n      };\n    };\n    const repositionMenus = comp => {\n      const sandbox = Coupling.getCoupled(comp, 'sandbox');\n      doRepositionMenus(sandbox);\n    };\n\n    const sandboxFields = () => [\n      defaulted('sandboxClasses', []),\n      SketchBehaviours.field('sandboxBehaviours', [\n        Composing,\n        Receiving,\n        Sandboxing,\n        Representing\n      ])\n    ];\n\n    const schema$k = constant$1([\n      required$1('dom'),\n      required$1('fetch'),\n      onHandler('onOpen'),\n      onKeyboardHandler('onExecute'),\n      defaulted('getHotspot', Optional.some),\n      defaulted('getAnchorOverrides', constant$1({})),\n      schema$y(),\n      field('dropdownBehaviours', [\n        Toggling,\n        Coupling,\n        Keying,\n        Focusing\n      ]),\n      required$1('toggleClass'),\n      defaulted('eventOrder', {}),\n      option$3('lazySink'),\n      defaulted('matchWidth', false),\n      defaulted('useMinWidth', false),\n      option$3('role')\n    ].concat(sandboxFields()));\n    const parts$e = constant$1([\n      external({\n        schema: [\n          tieredMenuMarkers(),\n          defaulted('fakeFocus', false)\n        ],\n        name: 'menu',\n        defaults: detail => {\n          return { onExecute: detail.onExecute };\n        }\n      }),\n      partType$1()\n    ]);\n\n    const factory$k = (detail, components, _spec, externals) => {\n      const lookupAttr = attr => get$g(detail.dom, 'attributes').bind(attrs => get$g(attrs, attr));\n      const switchToMenu = sandbox => {\n        Sandboxing.getState(sandbox).each(tmenu => {\n          tieredMenu.highlightPrimary(tmenu);\n        });\n      };\n      const togglePopup$1 = (dropdownComp, onOpenSync, highlightOnOpen) => {\n        return togglePopup(detail, identity, dropdownComp, externals, onOpenSync, highlightOnOpen);\n      };\n      const action = component => {\n        const onOpenSync = switchToMenu;\n        togglePopup$1(component, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);\n      };\n      const apis = {\n        expand: comp => {\n          if (!Toggling.isOn(comp)) {\n            togglePopup$1(comp, noop, HighlightOnOpen.HighlightNone).get(noop);\n          }\n        },\n        open: comp => {\n          if (!Toggling.isOn(comp)) {\n            togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).get(noop);\n          }\n        },\n        refetch: comp => {\n          const optSandbox = Coupling.getExistingCoupled(comp, 'sandbox');\n          return optSandbox.fold(() => {\n            return togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).map(noop);\n          }, sandboxComp => {\n            return open(detail, identity, comp, sandboxComp, externals, noop, HighlightOnOpen.HighlightMenuAndItem).map(noop);\n          });\n        },\n        isOpen: Toggling.isOn,\n        close: comp => {\n          if (Toggling.isOn(comp)) {\n            togglePopup$1(comp, noop, HighlightOnOpen.HighlightMenuAndItem).get(noop);\n          }\n        },\n        repositionMenus: comp => {\n          if (Toggling.isOn(comp)) {\n            repositionMenus(comp);\n          }\n        }\n      };\n      const triggerExecute = (comp, _se) => {\n        emitExecute(comp);\n        return Optional.some(true);\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        behaviours: augment(detail.dropdownBehaviours, [\n          Toggling.config({\n            toggleClass: detail.toggleClass,\n            aria: { mode: 'expanded' }\n          }),\n          Coupling.config({\n            others: {\n              sandbox: hotspot => {\n                return makeSandbox$1(detail, hotspot, {\n                  onOpen: () => Toggling.on(hotspot),\n                  onClose: () => Toggling.off(hotspot)\n                });\n              }\n            }\n          }),\n          Keying.config({\n            mode: 'special',\n            onSpace: triggerExecute,\n            onEnter: triggerExecute,\n            onDown: (comp, _se) => {\n              if (Dropdown.isOpen(comp)) {\n                const sandbox = Coupling.getCoupled(comp, 'sandbox');\n                switchToMenu(sandbox);\n              } else {\n                Dropdown.open(comp);\n              }\n              return Optional.some(true);\n            },\n            onEscape: (comp, _se) => {\n              if (Dropdown.isOpen(comp)) {\n                Dropdown.close(comp);\n                return Optional.some(true);\n              } else {\n                return Optional.none();\n              }\n            }\n          }),\n          Focusing.config({})\n        ]),\n        events: events$a(Optional.some(action)),\n        eventOrder: {\n          ...detail.eventOrder,\n          [execute$5()]: [\n            'disabling',\n            'toggling',\n            'alloy.base.behaviour'\n          ]\n        },\n        apis,\n        domModification: {\n          attributes: {\n            'aria-haspopup': 'true',\n            ...detail.role.fold(() => ({}), role => ({ role })),\n            ...detail.dom.tag === 'button' ? { type: lookupAttr('type').getOr('button') } : {}\n          }\n        }\n      };\n    };\n    const Dropdown = composite({\n      name: 'Dropdown',\n      configFields: schema$k(),\n      partFields: parts$e(),\n      factory: factory$k,\n      apis: {\n        open: (apis, comp) => apis.open(comp),\n        refetch: (apis, comp) => apis.refetch(comp),\n        expand: (apis, comp) => apis.expand(comp),\n        close: (apis, comp) => apis.close(comp),\n        isOpen: (apis, comp) => apis.isOpen(comp),\n        repositionMenus: (apis, comp) => apis.repositionMenus(comp)\n      }\n    });\n\n    const identifyMenuLayout = searchMode => {\n      switch (searchMode.searchMode) {\n      case 'no-search': {\n          return { menuType: 'normal' };\n        }\n      default: {\n          return {\n            menuType: 'searchable',\n            searchMode\n          };\n        }\n      }\n    };\n    const handleRefetchTrigger = originalSandboxComp => {\n      const dropdown = Representing.getValue(originalSandboxComp);\n      const optSearcherState = findWithinSandbox(originalSandboxComp).map(saveState);\n      Dropdown.refetch(dropdown).get(() => {\n        const newSandboxComp = Coupling.getCoupled(dropdown, 'sandbox');\n        optSearcherState.each(searcherState => findWithinSandbox(newSandboxComp).each(inputComp => restoreState(inputComp, searcherState)));\n      });\n    };\n    const handleRedirectToMenuItem = (sandboxComp, se) => {\n      getActiveMenuItemFrom(sandboxComp).each(activeItem => {\n        retargetAndDispatchWith(sandboxComp, activeItem.element, se.event.eventType, se.event.interactionEvent);\n      });\n    };\n    const getActiveMenuItemFrom = sandboxComp => {\n      return Sandboxing.getState(sandboxComp).bind(Highlighting.getHighlighted).bind(Highlighting.getHighlighted);\n    };\n    const getSearchResults = activeMenuComp => {\n      return has(activeMenuComp.element, searchResultsClass) ? Optional.some(activeMenuComp.element) : descendant(activeMenuComp.element, '.' + searchResultsClass);\n    };\n    const updateAriaOnHighlight = (tmenuComp, menuComp, itemComp) => {\n      findWithinMenu(tmenuComp).each(inputComp => {\n        setActiveDescendant(inputComp, itemComp);\n        const optActiveResults = getSearchResults(menuComp);\n        optActiveResults.each(resultsElem => {\n          getOpt(resultsElem, 'id').each(controlledId => set$9(inputComp.element, 'aria-controls', controlledId));\n        });\n      });\n      set$9(itemComp.element, 'aria-selected', 'true');\n    };\n    const updateAriaOnDehighlight = (tmenuComp, menuComp, itemComp) => {\n      set$9(itemComp.element, 'aria-selected', 'false');\n    };\n    const focusSearchField = tmenuComp => {\n      findWithinMenu(tmenuComp).each(searcherComp => Focusing.focus(searcherComp));\n    };\n    const getSearchPattern = dropdownComp => {\n      const optSandboxComp = Coupling.getExistingCoupled(dropdownComp, 'sandbox');\n      return optSandboxComp.bind(findWithinSandbox).map(saveState).map(state => state.fetchPattern).getOr('');\n    };\n\n    var FocusMode;\n    (function (FocusMode) {\n      FocusMode[FocusMode['ContentFocus'] = 0] = 'ContentFocus';\n      FocusMode[FocusMode['UiFocus'] = 1] = 'UiFocus';\n    }(FocusMode || (FocusMode = {})));\n    const createMenuItemFromBridge = (item, itemResponse, backstage, menuHasIcons, isHorizontalMenu) => {\n      const providersBackstage = backstage.shared.providers;\n      const parseForHorizontalMenu = menuitem => !isHorizontalMenu ? menuitem : {\n        ...menuitem,\n        shortcut: Optional.none(),\n        icon: menuitem.text.isSome() ? Optional.none() : menuitem.icon\n      };\n      switch (item.type) {\n      case 'menuitem':\n        return createMenuItem(item).fold(handleError, d => Optional.some(normal(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons)));\n      case 'nestedmenuitem':\n        return createNestedMenuItem(item).fold(handleError, d => Optional.some(nested(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons, isHorizontalMenu)));\n      case 'togglemenuitem':\n        return createToggleMenuItem(item).fold(handleError, d => Optional.some(toggle$1(parseForHorizontalMenu(d), itemResponse, providersBackstage, menuHasIcons)));\n      case 'separator':\n        return createSeparatorMenuItem(item).fold(handleError, d => Optional.some(separator$3(d)));\n      case 'fancymenuitem':\n        return createFancyMenuItem(item).fold(handleError, d => fancy(d, backstage));\n      default: {\n          console.error('Unknown item in general menu', item);\n          return Optional.none();\n        }\n      }\n    };\n    const createAutocompleteItems = (items, matchText, onItemValueHandler, columns, itemResponse, sharedBackstage, highlightOn) => {\n      const renderText = columns === 1;\n      const renderIcons = !renderText || menuHasIcons(items);\n      return cat(map$2(items, item => {\n        switch (item.type) {\n        case 'separator':\n          return createSeparatorItem(item).fold(handleError, d => Optional.some(separator$3(d)));\n        case 'cardmenuitem':\n          return createCardMenuItem(item).fold(handleError, d => Optional.some(card({\n            ...d,\n            onAction: api => {\n              d.onAction(api);\n              onItemValueHandler(d.value, d.meta);\n            }\n          }, itemResponse, sharedBackstage, {\n            itemBehaviours: tooltipBehaviour(d.meta, sharedBackstage),\n            cardText: {\n              matchText,\n              highlightOn\n            }\n          })));\n        case 'autocompleteitem':\n        default:\n          return createAutocompleterItem(item).fold(handleError, d => Optional.some(autocomplete(d, matchText, renderText, 'normal', onItemValueHandler, itemResponse, sharedBackstage, renderIcons)));\n        }\n      }));\n    };\n    const createPartialMenu = (value, items, itemResponse, backstage, isHorizontalMenu, searchMode) => {\n      const hasIcons = menuHasIcons(items);\n      const alloyItems = cat(map$2(items, item => {\n        const itemHasIcon = i => isHorizontalMenu ? !has$2(i, 'text') : hasIcons;\n        const createItem = i => createMenuItemFromBridge(i, itemResponse, backstage, itemHasIcon(i), isHorizontalMenu);\n        if (item.type === 'nestedmenuitem' && item.getSubmenuItems().length <= 0) {\n          return createItem({\n            ...item,\n            enabled: false\n          });\n        } else {\n          return createItem(item);\n        }\n      }));\n      const menuLayout = identifyMenuLayout(searchMode);\n      const createPartial = isHorizontalMenu ? createHorizontalPartialMenuWithAlloyItems : createPartialMenuWithAlloyItems;\n      return createPartial(value, hasIcons, alloyItems, 1, menuLayout);\n    };\n    const createTieredDataFrom = partialMenu => tieredMenu.singleData(partialMenu.value, partialMenu);\n    const createInlineMenuFrom = (partialMenu, columns, focusMode, presets) => {\n      const movement = deriveMenuMovement(columns, presets);\n      const menuMarkers = markers(presets);\n      return {\n        data: createTieredDataFrom({\n          ...partialMenu,\n          movement,\n          menuBehaviours: SimpleBehaviours.unnamedEvents(columns !== 'auto' ? [] : [runOnAttached((comp, _se) => {\n              detectSize(comp, 4, menuMarkers.item).each(({numColumns, numRows}) => {\n                Keying.setGridSize(comp, numRows, numColumns);\n              });\n            })])\n        }),\n        menu: {\n          markers: markers(presets),\n          fakeFocus: focusMode === FocusMode.ContentFocus\n        }\n      };\n    };\n\n    const getAutocompleterRange = (dom, initRange) => {\n      return detect(SugarElement.fromDom(initRange.startContainer)).map(elm => {\n        const range = dom.createRng();\n        range.selectNode(elm.dom);\n        return range;\n      });\n    };\n    const register$b = (editor, sharedBackstage) => {\n      const processingAction = Cell(false);\n      const activeState = Cell(false);\n      const autocompleter = build$1(InlineView.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-autocompleter']\n        },\n        components: [],\n        fireDismissalEventInstead: {},\n        inlineBehaviours: derive$1([config('dismissAutocompleter', [run$1(dismissRequested(), () => cancelIfNecessary())])]),\n        lazySink: sharedBackstage.getSink\n      }));\n      const isMenuOpen = () => InlineView.isOpen(autocompleter);\n      const isActive = activeState.get;\n      const hideIfNecessary = () => {\n        if (isMenuOpen()) {\n          InlineView.hide(autocompleter);\n        }\n      };\n      const getMenu = () => InlineView.getContent(autocompleter).bind(tmenu => {\n        return get$h(tmenu.components(), 0);\n      });\n      const cancelIfNecessary = () => editor.execCommand('mceAutocompleterClose');\n      const getCombinedItems = matches => {\n        const columns = findMap(matches, m => Optional.from(m.columns)).getOr(1);\n        return bind$3(matches, match => {\n          const choices = match.items;\n          return createAutocompleteItems(choices, match.matchText, (itemValue, itemMeta) => {\n            const nr = editor.selection.getRng();\n            getAutocompleterRange(editor.dom, nr).each(range => {\n              const autocompleterApi = {\n                hide: () => cancelIfNecessary(),\n                reload: fetchOptions => {\n                  hideIfNecessary();\n                  editor.execCommand('mceAutocompleterReload', false, { fetchOptions });\n                }\n              };\n              processingAction.set(true);\n              match.onAction(autocompleterApi, range, itemValue, itemMeta);\n              processingAction.set(false);\n            });\n          }, columns, ItemResponse$1.BUBBLE_TO_SANDBOX, sharedBackstage, match.highlightOn);\n        });\n      };\n      const display = (lookupData, items) => {\n        findIn(SugarElement.fromDom(editor.getBody())).each(element => {\n          const columns = findMap(lookupData, ld => Optional.from(ld.columns)).getOr(1);\n          InlineView.showMenuAt(autocompleter, {\n            anchor: {\n              type: 'node',\n              root: SugarElement.fromDom(editor.getBody()),\n              node: Optional.from(element)\n            }\n          }, createInlineMenuFrom(createPartialMenuWithAlloyItems('autocompleter-value', true, items, columns, { menuType: 'normal' }), columns, FocusMode.ContentFocus, 'normal'));\n        });\n        getMenu().each(Highlighting.highlightFirst);\n      };\n      const updateDisplay = lookupData => {\n        const combinedItems = getCombinedItems(lookupData);\n        if (combinedItems.length > 0) {\n          display(lookupData, combinedItems);\n        } else {\n          hideIfNecessary();\n        }\n      };\n      editor.on('AutocompleterStart', ({lookupData}) => {\n        activeState.set(true);\n        processingAction.set(false);\n        updateDisplay(lookupData);\n      });\n      editor.on('AutocompleterUpdate', ({lookupData}) => updateDisplay(lookupData));\n      editor.on('AutocompleterEnd', () => {\n        hideIfNecessary();\n        activeState.set(false);\n        processingAction.set(false);\n      });\n      const autocompleterUiApi = {\n        cancelIfNecessary,\n        isMenuOpen,\n        isActive,\n        isProcessingAction: processingAction.get,\n        getMenu\n      };\n      AutocompleterEditorEvents.setup(autocompleterUiApi, editor);\n    };\n    const Autocompleter = { register: register$b };\n\n    const closest = (scope, selector, isRoot) => closest$1(scope, selector, isRoot).isSome();\n\n    const DelayedFunction = (fun, delay) => {\n      let ref = null;\n      const schedule = (...args) => {\n        ref = setTimeout(() => {\n          fun.apply(null, args);\n          ref = null;\n        }, delay);\n      };\n      const cancel = () => {\n        if (ref !== null) {\n          clearTimeout(ref);\n          ref = null;\n        }\n      };\n      return {\n        cancel,\n        schedule\n      };\n    };\n\n    const SIGNIFICANT_MOVE = 5;\n    const LONGPRESS_DELAY = 400;\n    const getTouch = event => {\n      const raw = event.raw;\n      if (raw.touches === undefined || raw.touches.length !== 1) {\n        return Optional.none();\n      }\n      return Optional.some(raw.touches[0]);\n    };\n    const isFarEnough = (touch, data) => {\n      const distX = Math.abs(touch.clientX - data.x);\n      const distY = Math.abs(touch.clientY - data.y);\n      return distX > SIGNIFICANT_MOVE || distY > SIGNIFICANT_MOVE;\n    };\n    const monitor = settings => {\n      const startData = value$2();\n      const longpressFired = Cell(false);\n      const longpress$1 = DelayedFunction(event => {\n        settings.triggerEvent(longpress(), event);\n        longpressFired.set(true);\n      }, LONGPRESS_DELAY);\n      const handleTouchstart = event => {\n        getTouch(event).each(touch => {\n          longpress$1.cancel();\n          const data = {\n            x: touch.clientX,\n            y: touch.clientY,\n            target: event.target\n          };\n          longpress$1.schedule(event);\n          longpressFired.set(false);\n          startData.set(data);\n        });\n        return Optional.none();\n      };\n      const handleTouchmove = event => {\n        longpress$1.cancel();\n        getTouch(event).each(touch => {\n          startData.on(data => {\n            if (isFarEnough(touch, data)) {\n              startData.clear();\n            }\n          });\n        });\n        return Optional.none();\n      };\n      const handleTouchend = event => {\n        longpress$1.cancel();\n        const isSame = data => eq(data.target, event.target);\n        return startData.get().filter(isSame).map(_data => {\n          if (longpressFired.get()) {\n            event.prevent();\n            return false;\n          } else {\n            return settings.triggerEvent(tap(), event);\n          }\n        });\n      };\n      const handlers = wrapAll([\n        {\n          key: touchstart(),\n          value: handleTouchstart\n        },\n        {\n          key: touchmove(),\n          value: handleTouchmove\n        },\n        {\n          key: touchend(),\n          value: handleTouchend\n        }\n      ]);\n      const fireIfReady = (event, type) => get$g(handlers, type).bind(handler => handler(event));\n      return { fireIfReady };\n    };\n\n    const isDangerous = event => {\n      const keyEv = event.raw;\n      return keyEv.which === BACKSPACE[0] && !contains$2([\n        'input',\n        'textarea'\n      ], name$3(event.target)) && !closest(event.target, '[contenteditable=\"true\"]');\n    };\n    const setup$d = (container, rawSettings) => {\n      const settings = {\n        stopBackspace: true,\n        ...rawSettings\n      };\n      const pointerEvents = [\n        'touchstart',\n        'touchmove',\n        'touchend',\n        'touchcancel',\n        'gesturestart',\n        'mousedown',\n        'mouseup',\n        'mouseover',\n        'mousemove',\n        'mouseout',\n        'click'\n      ];\n      const tapEvent = monitor(settings);\n      const simpleEvents = map$2(pointerEvents.concat([\n        'selectstart',\n        'input',\n        'contextmenu',\n        'change',\n        'transitionend',\n        'transitioncancel',\n        'drag',\n        'dragstart',\n        'dragend',\n        'dragenter',\n        'dragleave',\n        'dragover',\n        'drop',\n        'keyup'\n      ]), type => bind(container, type, event => {\n        tapEvent.fireIfReady(event, type).each(tapStopped => {\n          if (tapStopped) {\n            event.kill();\n          }\n        });\n        const stopped = settings.triggerEvent(type, event);\n        if (stopped) {\n          event.kill();\n        }\n      }));\n      const pasteTimeout = value$2();\n      const onPaste = bind(container, 'paste', event => {\n        tapEvent.fireIfReady(event, 'paste').each(tapStopped => {\n          if (tapStopped) {\n            event.kill();\n          }\n        });\n        const stopped = settings.triggerEvent('paste', event);\n        if (stopped) {\n          event.kill();\n        }\n        pasteTimeout.set(setTimeout(() => {\n          settings.triggerEvent(postPaste(), event);\n        }, 0));\n      });\n      const onKeydown = bind(container, 'keydown', event => {\n        const stopped = settings.triggerEvent('keydown', event);\n        if (stopped) {\n          event.kill();\n        } else if (settings.stopBackspace && isDangerous(event)) {\n          event.prevent();\n        }\n      });\n      const onFocusIn = bind(container, 'focusin', event => {\n        const stopped = settings.triggerEvent('focusin', event);\n        if (stopped) {\n          event.kill();\n        }\n      });\n      const focusoutTimeout = value$2();\n      const onFocusOut = bind(container, 'focusout', event => {\n        const stopped = settings.triggerEvent('focusout', event);\n        if (stopped) {\n          event.kill();\n        }\n        focusoutTimeout.set(setTimeout(() => {\n          settings.triggerEvent(postBlur(), event);\n        }, 0));\n      });\n      const unbind = () => {\n        each$1(simpleEvents, e => {\n          e.unbind();\n        });\n        onKeydown.unbind();\n        onFocusIn.unbind();\n        onFocusOut.unbind();\n        onPaste.unbind();\n        pasteTimeout.on(clearTimeout);\n        focusoutTimeout.on(clearTimeout);\n      };\n      return { unbind };\n    };\n\n    const derive = (rawEvent, rawTarget) => {\n      const source = get$g(rawEvent, 'target').getOr(rawTarget);\n      return Cell(source);\n    };\n\n    const fromSource = (event, source) => {\n      const stopper = Cell(false);\n      const cutter = Cell(false);\n      const stop = () => {\n        stopper.set(true);\n      };\n      const cut = () => {\n        cutter.set(true);\n      };\n      return {\n        stop,\n        cut,\n        isStopped: stopper.get,\n        isCut: cutter.get,\n        event,\n        setSource: source.set,\n        getSource: source.get\n      };\n    };\n    const fromExternal = event => {\n      const stopper = Cell(false);\n      const stop = () => {\n        stopper.set(true);\n      };\n      return {\n        stop,\n        cut: noop,\n        isStopped: stopper.get,\n        isCut: never,\n        event,\n        setSource: die('Cannot set source of a broadcasted event'),\n        getSource: die('Cannot get source of a broadcasted event')\n      };\n    };\n\n    const adt$1 = Adt.generate([\n      { stopped: [] },\n      { resume: ['element'] },\n      { complete: [] }\n    ]);\n    const doTriggerHandler = (lookup, eventType, rawEvent, target, source, logger) => {\n      const handler = lookup(eventType, target);\n      const simulatedEvent = fromSource(rawEvent, source);\n      return handler.fold(() => {\n        logger.logEventNoHandlers(eventType, target);\n        return adt$1.complete();\n      }, handlerInfo => {\n        const descHandler = handlerInfo.descHandler;\n        const eventHandler = getCurried(descHandler);\n        eventHandler(simulatedEvent);\n        if (simulatedEvent.isStopped()) {\n          logger.logEventStopped(eventType, handlerInfo.element, descHandler.purpose);\n          return adt$1.stopped();\n        } else if (simulatedEvent.isCut()) {\n          logger.logEventCut(eventType, handlerInfo.element, descHandler.purpose);\n          return adt$1.complete();\n        } else {\n          return parent(handlerInfo.element).fold(() => {\n            logger.logNoParent(eventType, handlerInfo.element, descHandler.purpose);\n            return adt$1.complete();\n          }, parent => {\n            logger.logEventResponse(eventType, handlerInfo.element, descHandler.purpose);\n            return adt$1.resume(parent);\n          });\n        }\n      });\n    };\n    const doTriggerOnUntilStopped = (lookup, eventType, rawEvent, rawTarget, source, logger) => doTriggerHandler(lookup, eventType, rawEvent, rawTarget, source, logger).fold(always, parent => doTriggerOnUntilStopped(lookup, eventType, rawEvent, parent, source, logger), never);\n    const triggerHandler = (lookup, eventType, rawEvent, target, logger) => {\n      const source = derive(rawEvent, target);\n      return doTriggerHandler(lookup, eventType, rawEvent, target, source, logger);\n    };\n    const broadcast = (listeners, rawEvent, _logger) => {\n      const simulatedEvent = fromExternal(rawEvent);\n      each$1(listeners, listener => {\n        const descHandler = listener.descHandler;\n        const handler = getCurried(descHandler);\n        handler(simulatedEvent);\n      });\n      return simulatedEvent.isStopped();\n    };\n    const triggerUntilStopped = (lookup, eventType, rawEvent, logger) => triggerOnUntilStopped(lookup, eventType, rawEvent, rawEvent.target, logger);\n    const triggerOnUntilStopped = (lookup, eventType, rawEvent, rawTarget, logger) => {\n      const source = derive(rawEvent, rawTarget);\n      return doTriggerOnUntilStopped(lookup, eventType, rawEvent, rawTarget, source, logger);\n    };\n\n    const eventHandler = (element, descHandler) => ({\n      element,\n      descHandler\n    });\n    const broadcastHandler = (id, handler) => ({\n      id,\n      descHandler: handler\n    });\n    const EventRegistry = () => {\n      const registry = {};\n      const registerId = (extraArgs, id, events) => {\n        each(events, (v, k) => {\n          const handlers = registry[k] !== undefined ? registry[k] : {};\n          handlers[id] = curryArgs(v, extraArgs);\n          registry[k] = handlers;\n        });\n      };\n      const findHandler = (handlers, elem) => read$1(elem).bind(id => get$g(handlers, id)).map(descHandler => eventHandler(elem, descHandler));\n      const filterByType = type => get$g(registry, type).map(handlers => mapToArray(handlers, (f, id) => broadcastHandler(id, f))).getOr([]);\n      const find = (isAboveRoot, type, target) => get$g(registry, type).bind(handlers => closest$4(target, elem => findHandler(handlers, elem), isAboveRoot));\n      const unregisterId = id => {\n        each(registry, (handlersById, _eventName) => {\n          if (has$2(handlersById, id)) {\n            delete handlersById[id];\n          }\n        });\n      };\n      return {\n        registerId,\n        unregisterId,\n        filterByType,\n        find\n      };\n    };\n\n    const Registry = () => {\n      const events = EventRegistry();\n      const components = {};\n      const readOrTag = component => {\n        const elem = component.element;\n        return read$1(elem).getOrThunk(() => write('uid-', component.element));\n      };\n      const failOnDuplicate = (component, tagId) => {\n        const conflict = components[tagId];\n        if (conflict === component) {\n          unregister(component);\n        } else {\n          throw new Error('The tagId \"' + tagId + '\" is already used by: ' + element(conflict.element) + '\\nCannot use it for: ' + element(component.element) + '\\n' + 'The conflicting element is' + (inBody(conflict.element) ? ' ' : ' not ') + 'already in the DOM');\n        }\n      };\n      const register = component => {\n        const tagId = readOrTag(component);\n        if (hasNonNullableKey(components, tagId)) {\n          failOnDuplicate(component, tagId);\n        }\n        const extraArgs = [component];\n        events.registerId(extraArgs, tagId, component.events);\n        components[tagId] = component;\n      };\n      const unregister = component => {\n        read$1(component.element).each(tagId => {\n          delete components[tagId];\n          events.unregisterId(tagId);\n        });\n      };\n      const filter = type => events.filterByType(type);\n      const find = (isAboveRoot, type, target) => events.find(isAboveRoot, type, target);\n      const getById = id => get$g(components, id);\n      return {\n        find,\n        filter,\n        register,\n        unregister,\n        getById\n      };\n    };\n\n    const factory$j = detail => {\n      const {attributes, ...domWithoutAttributes} = detail.dom;\n      return {\n        uid: detail.uid,\n        dom: {\n          tag: 'div',\n          attributes: {\n            role: 'presentation',\n            ...attributes\n          },\n          ...domWithoutAttributes\n        },\n        components: detail.components,\n        behaviours: get$3(detail.containerBehaviours),\n        events: detail.events,\n        domModification: detail.domModification,\n        eventOrder: detail.eventOrder\n      };\n    };\n    const Container = single({\n      name: 'Container',\n      factory: factory$j,\n      configFields: [\n        defaulted('components', []),\n        field('containerBehaviours', []),\n        defaulted('events', {}),\n        defaulted('domModification', {}),\n        defaulted('eventOrder', {})\n      ]\n    });\n\n    const takeover = root => {\n      const isAboveRoot = el => parent(root.element).fold(always, parent => eq(el, parent));\n      const registry = Registry();\n      const lookup = (eventName, target) => registry.find(isAboveRoot, eventName, target);\n      const domEvents = setup$d(root.element, {\n        triggerEvent: (eventName, event) => {\n          return monitorEvent(eventName, event.target, logger => triggerUntilStopped(lookup, eventName, event, logger));\n        }\n      });\n      const systemApi = {\n        debugInfo: constant$1('real'),\n        triggerEvent: (eventName, target, data) => {\n          monitorEvent(eventName, target, logger => triggerOnUntilStopped(lookup, eventName, data, target, logger));\n        },\n        triggerFocus: (target, originator) => {\n          read$1(target).fold(() => {\n            focus$3(target);\n          }, _alloyId => {\n            monitorEvent(focus$4(), target, logger => {\n              triggerHandler(lookup, focus$4(), {\n                originator,\n                kill: noop,\n                prevent: noop,\n                target\n              }, target, logger);\n              return false;\n            });\n          });\n        },\n        triggerEscape: (comp, simulatedEvent) => {\n          systemApi.triggerEvent('keydown', comp.element, simulatedEvent.event);\n        },\n        getByUid: uid => {\n          return getByUid(uid);\n        },\n        getByDom: elem => {\n          return getByDom(elem);\n        },\n        build: build$1,\n        buildOrPatch: buildOrPatch,\n        addToGui: c => {\n          add(c);\n        },\n        removeFromGui: c => {\n          remove(c);\n        },\n        addToWorld: c => {\n          addToWorld(c);\n        },\n        removeFromWorld: c => {\n          removeFromWorld(c);\n        },\n        broadcast: message => {\n          broadcast$1(message);\n        },\n        broadcastOn: (channels, message) => {\n          broadcastOn(channels, message);\n        },\n        broadcastEvent: (eventName, event) => {\n          broadcastEvent(eventName, event);\n        },\n        isConnected: always\n      };\n      const addToWorld = component => {\n        component.connect(systemApi);\n        if (!isText(component.element)) {\n          registry.register(component);\n          each$1(component.components(), addToWorld);\n          systemApi.triggerEvent(systemInit(), component.element, { target: component.element });\n        }\n      };\n      const removeFromWorld = component => {\n        if (!isText(component.element)) {\n          each$1(component.components(), removeFromWorld);\n          registry.unregister(component);\n        }\n        component.disconnect();\n      };\n      const add = component => {\n        attach(root, component);\n      };\n      const remove = component => {\n        detach(component);\n      };\n      const destroy = () => {\n        domEvents.unbind();\n        remove$5(root.element);\n      };\n      const broadcastData = data => {\n        const receivers = registry.filter(receive());\n        each$1(receivers, receiver => {\n          const descHandler = receiver.descHandler;\n          const handler = getCurried(descHandler);\n          handler(data);\n        });\n      };\n      const broadcast$1 = message => {\n        broadcastData({\n          universal: true,\n          data: message\n        });\n      };\n      const broadcastOn = (channels, message) => {\n        broadcastData({\n          universal: false,\n          channels,\n          data: message\n        });\n      };\n      const broadcastEvent = (eventName, event) => {\n        const listeners = registry.filter(eventName);\n        return broadcast(listeners, event);\n      };\n      const getByUid = uid => registry.getById(uid).fold(() => Result.error(new Error('Could not find component with uid: \"' + uid + '\" in system.')), Result.value);\n      const getByDom = elem => {\n        const uid = read$1(elem).getOr('not found');\n        return getByUid(uid);\n      };\n      addToWorld(root);\n      return {\n        root,\n        element: root.element,\n        destroy,\n        add,\n        remove,\n        getByUid,\n        getByDom,\n        addToWorld,\n        removeFromWorld,\n        broadcast: broadcast$1,\n        broadcastOn,\n        broadcastEvent\n      };\n    };\n\n    const renderBar = (spec, backstage) => ({\n      dom: {\n        tag: 'div',\n        classes: [\n          'tox-bar',\n          'tox-form__controls-h-stack'\n        ]\n      },\n      components: map$2(spec.items, backstage.interpreter)\n    });\n\n    const schema$j = constant$1([\n      defaulted('prefix', 'form-field'),\n      field('fieldBehaviours', [\n        Composing,\n        Representing\n      ])\n    ]);\n    const parts$d = constant$1([\n      optional({\n        schema: [required$1('dom')],\n        name: 'label'\n      }),\n      optional({\n        factory: {\n          sketch: spec => {\n            return {\n              uid: spec.uid,\n              dom: {\n                tag: 'span',\n                styles: { display: 'none' },\n                attributes: { 'aria-hidden': 'true' },\n                innerHtml: spec.text\n              }\n            };\n          }\n        },\n        schema: [required$1('text')],\n        name: 'aria-descriptor'\n      }),\n      required({\n        factory: {\n          sketch: spec => {\n            const excludeFactory = exclude(spec, ['factory']);\n            return spec.factory.sketch(excludeFactory);\n          }\n        },\n        schema: [required$1('factory')],\n        name: 'field'\n      })\n    ]);\n\n    const factory$i = (detail, components, _spec, _externals) => {\n      const behaviours = augment(detail.fieldBehaviours, [\n        Composing.config({\n          find: container => {\n            return getPart(container, detail, 'field');\n          }\n        }),\n        Representing.config({\n          store: {\n            mode: 'manual',\n            getValue: field => {\n              return Composing.getCurrent(field).bind(Representing.getValue);\n            },\n            setValue: (field, value) => {\n              Composing.getCurrent(field).each(current => {\n                Representing.setValue(current, value);\n              });\n            }\n          }\n        })\n      ]);\n      const events = derive$2([runOnAttached((component, _simulatedEvent) => {\n          const ps = getParts(component, detail, [\n            'label',\n            'field',\n            'aria-descriptor'\n          ]);\n          ps.field().each(field => {\n            const id = generate$6(detail.prefix);\n            ps.label().each(label => {\n              set$9(label.element, 'for', id);\n              set$9(field.element, 'id', id);\n            });\n            ps['aria-descriptor']().each(descriptor => {\n              const descriptorId = generate$6(detail.prefix);\n              set$9(descriptor.element, 'id', descriptorId);\n              set$9(field.element, 'aria-describedby', descriptorId);\n            });\n          });\n        })]);\n      const apis = {\n        getField: container => getPart(container, detail, 'field'),\n        getLabel: container => getPart(container, detail, 'label')\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        behaviours,\n        events,\n        apis\n      };\n    };\n    const FormField = composite({\n      name: 'FormField',\n      configFields: schema$j(),\n      partFields: parts$d(),\n      factory: factory$i,\n      apis: {\n        getField: (apis, comp) => apis.getField(comp),\n        getLabel: (apis, comp) => apis.getLabel(comp)\n      }\n    });\n\n    const exhibit$2 = (base, tabConfig) => nu$7({\n      attributes: wrapAll([{\n          key: tabConfig.tabAttr,\n          value: 'true'\n        }])\n    });\n\n    var ActiveTabstopping = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        exhibit: exhibit$2\n    });\n\n    var TabstopSchema = [defaulted('tabAttr', 'data-alloy-tabstop')];\n\n    const Tabstopping = create$4({\n      fields: TabstopSchema,\n      name: 'tabstopping',\n      active: ActiveTabstopping\n    });\n\n    var global$3 = tinymce.util.Tools.resolve('tinymce.html.Entities');\n\n    const renderFormFieldWith = (pLabel, pField, extraClasses, extraBehaviours) => {\n      const spec = renderFormFieldSpecWith(pLabel, pField, extraClasses, extraBehaviours);\n      return FormField.sketch(spec);\n    };\n    const renderFormField = (pLabel, pField) => renderFormFieldWith(pLabel, pField, [], []);\n    const renderFormFieldSpecWith = (pLabel, pField, extraClasses, extraBehaviours) => ({\n      dom: renderFormFieldDomWith(extraClasses),\n      components: pLabel.toArray().concat([pField]),\n      fieldBehaviours: derive$1(extraBehaviours)\n    });\n    const renderFormFieldDom = () => renderFormFieldDomWith([]);\n    const renderFormFieldDomWith = extraClasses => ({\n      tag: 'div',\n      classes: ['tox-form__group'].concat(extraClasses)\n    });\n    const renderLabel$2 = (label, providersBackstage) => FormField.parts.label({\n      dom: {\n        tag: 'label',\n        classes: ['tox-label']\n      },\n      components: [text$2(providersBackstage.translate(label))]\n    });\n\n    const formChangeEvent = generate$6('form-component-change');\n    const formCloseEvent = generate$6('form-close');\n    const formCancelEvent = generate$6('form-cancel');\n    const formActionEvent = generate$6('form-action');\n    const formSubmitEvent = generate$6('form-submit');\n    const formBlockEvent = generate$6('form-block');\n    const formUnblockEvent = generate$6('form-unblock');\n    const formTabChangeEvent = generate$6('form-tabchange');\n    const formResizeEvent = generate$6('form-resize');\n\n    const renderCollection = (spec, providersBackstage, initialData) => {\n      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));\n      const runOnItem = f => (comp, se) => {\n        closest$1(se.event.target, '[data-collection-item-value]').each(target => {\n          f(comp, se, target, get$f(target, 'data-collection-item-value'));\n        });\n      };\n      const setContents = (comp, items) => {\n        const htmlLines = map$2(items, item => {\n          const itemText = global$8.translate(item.text);\n          const textContent = spec.columns === 1 ? `<div class=\"tox-collection__item-label\">${ itemText }</div>` : '';\n          const iconContent = `<div class=\"tox-collection__item-icon\">${ item.icon }</div>`;\n          const mapItemName = {\n            '_': ' ',\n            ' - ': ' ',\n            '-': ' '\n          };\n          const ariaLabel = itemText.replace(/\\_| \\- |\\-/g, match => mapItemName[match]);\n          const disabledClass = providersBackstage.isDisabled() ? ' tox-collection__item--state-disabled' : '';\n          return `<div class=\"tox-collection__item${ disabledClass }\" tabindex=\"-1\" data-collection-item-value=\"${ global$3.encodeAllRaw(item.value) }\" title=\"${ ariaLabel }\" aria-label=\"${ ariaLabel }\">${ iconContent }${ textContent }</div>`;\n        });\n        const chunks = spec.columns !== 'auto' && spec.columns > 1 ? chunk$1(htmlLines, spec.columns) : [htmlLines];\n        const html = map$2(chunks, ch => `<div class=\"tox-collection__group\">${ ch.join('') }</div>`);\n        set$6(comp.element, html.join(''));\n      };\n      const onClick = runOnItem((comp, se, tgt, itemValue) => {\n        se.stop();\n        if (!providersBackstage.isDisabled()) {\n          emitWith(comp, formActionEvent, {\n            name: spec.name,\n            value: itemValue\n          });\n        }\n      });\n      const collectionEvents = [\n        run$1(mouseover(), runOnItem((comp, se, tgt) => {\n          focus$3(tgt);\n        })),\n        run$1(click(), onClick),\n        run$1(tap(), onClick),\n        run$1(focusin(), runOnItem((comp, se, tgt) => {\n          descendant(comp.element, '.' + activeClass).each(currentActive => {\n            remove$2(currentActive, activeClass);\n          });\n          add$2(tgt, activeClass);\n        })),\n        run$1(focusout(), runOnItem(comp => {\n          descendant(comp.element, '.' + activeClass).each(currentActive => {\n            remove$2(currentActive, activeClass);\n          });\n        })),\n        runOnExecute$1(runOnItem((comp, se, tgt, itemValue) => {\n          emitWith(comp, formActionEvent, {\n            name: spec.name,\n            value: itemValue\n          });\n        }))\n      ];\n      const iterCollectionItems = (comp, applyAttributes) => map$2(descendants(comp.element, '.tox-collection__item'), applyAttributes);\n      const pField = FormField.parts.field({\n        dom: {\n          tag: 'div',\n          classes: ['tox-collection'].concat(spec.columns !== 1 ? ['tox-collection--grid'] : ['tox-collection--list'])\n        },\n        components: [],\n        factory: { sketch: identity },\n        behaviours: derive$1([\n          Disabling.config({\n            disabled: providersBackstage.isDisabled,\n            onDisabled: comp => {\n              iterCollectionItems(comp, childElm => {\n                add$2(childElm, 'tox-collection__item--state-disabled');\n                set$9(childElm, 'aria-disabled', true);\n              });\n            },\n            onEnabled: comp => {\n              iterCollectionItems(comp, childElm => {\n                remove$2(childElm, 'tox-collection__item--state-disabled');\n                remove$7(childElm, 'aria-disabled');\n              });\n            }\n          }),\n          receivingConfig(),\n          Replacing.config({}),\n          Representing.config({\n            store: {\n              mode: 'memory',\n              initialValue: initialData.getOr([])\n            },\n            onSetValue: (comp, items) => {\n              setContents(comp, items);\n              if (spec.columns === 'auto') {\n                detectSize(comp, 5, 'tox-collection__item').each(({numRows, numColumns}) => {\n                  Keying.setGridSize(comp, numRows, numColumns);\n                });\n              }\n              emit(comp, formResizeEvent);\n            }\n          }),\n          Tabstopping.config({}),\n          Keying.config(deriveCollectionMovement(spec.columns, 'normal')),\n          config('collection-events', collectionEvents)\n        ]),\n        eventOrder: {\n          [execute$5()]: [\n            'disabling',\n            'alloy.base.behaviour',\n            'collection-events'\n          ]\n        }\n      });\n      const extraClasses = ['tox-form__group--collection'];\n      return renderFormFieldWith(pLabel, pField, extraClasses, []);\n    };\n\n    const ariaElements = [\n      'input',\n      'textarea'\n    ];\n    const isAriaElement = elem => {\n      const name = name$3(elem);\n      return contains$2(ariaElements, name);\n    };\n    const markValid = (component, invalidConfig) => {\n      const elem = invalidConfig.getRoot(component).getOr(component.element);\n      remove$2(elem, invalidConfig.invalidClass);\n      invalidConfig.notify.each(notifyInfo => {\n        if (isAriaElement(component.element)) {\n          set$9(component.element, 'aria-invalid', false);\n        }\n        notifyInfo.getContainer(component).each(container => {\n          set$6(container, notifyInfo.validHtml);\n        });\n        notifyInfo.onValid(component);\n      });\n    };\n    const markInvalid = (component, invalidConfig, invalidState, text) => {\n      const elem = invalidConfig.getRoot(component).getOr(component.element);\n      add$2(elem, invalidConfig.invalidClass);\n      invalidConfig.notify.each(notifyInfo => {\n        if (isAriaElement(component.element)) {\n          set$9(component.element, 'aria-invalid', true);\n        }\n        notifyInfo.getContainer(component).each(container => {\n          set$6(container, text);\n        });\n        notifyInfo.onInvalid(component, text);\n      });\n    };\n    const query = (component, invalidConfig, _invalidState) => invalidConfig.validator.fold(() => Future.pure(Result.value(true)), validatorInfo => validatorInfo.validate(component));\n    const run = (component, invalidConfig, invalidState) => {\n      invalidConfig.notify.each(notifyInfo => {\n        notifyInfo.onValidate(component);\n      });\n      return query(component, invalidConfig).map(valid => {\n        if (component.getSystem().isConnected()) {\n          return valid.fold(err => {\n            markInvalid(component, invalidConfig, invalidState, err);\n            return Result.error(err);\n          }, v => {\n            markValid(component, invalidConfig);\n            return Result.value(v);\n          });\n        } else {\n          return Result.error('No longer in system');\n        }\n      });\n    };\n    const isInvalid = (component, invalidConfig) => {\n      const elem = invalidConfig.getRoot(component).getOr(component.element);\n      return has(elem, invalidConfig.invalidClass);\n    };\n\n    var InvalidateApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        markValid: markValid,\n        markInvalid: markInvalid,\n        query: query,\n        run: run,\n        isInvalid: isInvalid\n    });\n\n    const events$8 = (invalidConfig, invalidState) => invalidConfig.validator.map(validatorInfo => derive$2([run$1(validatorInfo.onEvent, component => {\n        run(component, invalidConfig, invalidState).get(identity);\n      })].concat(validatorInfo.validateOnLoad ? [runOnAttached(component => {\n        run(component, invalidConfig, invalidState).get(noop);\n      })] : []))).getOr({});\n\n    var ActiveInvalidate = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$8\n    });\n\n    var InvalidateSchema = [\n      required$1('invalidClass'),\n      defaulted('getRoot', Optional.none),\n      optionObjOf('notify', [\n        defaulted('aria', 'alert'),\n        defaulted('getContainer', Optional.none),\n        defaulted('validHtml', ''),\n        onHandler('onValid'),\n        onHandler('onInvalid'),\n        onHandler('onValidate')\n      ]),\n      optionObjOf('validator', [\n        required$1('validate'),\n        defaulted('onEvent', 'input'),\n        defaulted('validateOnLoad', true)\n      ])\n    ];\n\n    const Invalidating = create$4({\n      fields: InvalidateSchema,\n      name: 'invalidating',\n      active: ActiveInvalidate,\n      apis: InvalidateApis,\n      extra: {\n        validation: validator => {\n          return component => {\n            const v = Representing.getValue(component);\n            return Future.pure(validator(v));\n          };\n        }\n      }\n    });\n\n    const exhibit$1 = () => nu$7({\n      styles: {\n        '-webkit-user-select': 'none',\n        'user-select': 'none',\n        '-ms-user-select': 'none',\n        '-moz-user-select': '-moz-none'\n      },\n      attributes: { unselectable: 'on' }\n    });\n    const events$7 = () => derive$2([abort(selectstart(), always)]);\n\n    var ActiveUnselecting = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$7,\n        exhibit: exhibit$1\n    });\n\n    const Unselecting = create$4({\n      fields: [],\n      name: 'unselecting',\n      active: ActiveUnselecting\n    });\n\n    const renderPanelButton = (spec, sharedBackstage) => Dropdown.sketch({\n      dom: spec.dom,\n      components: spec.components,\n      toggleClass: 'mce-active',\n      dropdownBehaviours: derive$1([\n        DisablingConfigs.button(sharedBackstage.providers.isDisabled),\n        receivingConfig(),\n        Unselecting.config({}),\n        Tabstopping.config({})\n      ]),\n      layouts: spec.layouts,\n      sandboxClasses: ['tox-dialog__popups'],\n      lazySink: sharedBackstage.getSink,\n      fetch: comp => Future.nu(callback => spec.fetch(callback)).map(items => Optional.from(createTieredDataFrom(deepMerge(createPartialChoiceMenu(generate$6('menu-value'), items, value => {\n        spec.onItemAction(comp, value);\n      }, spec.columns, spec.presets, ItemResponse$1.CLOSE_ON_EXECUTE, never, sharedBackstage.providers), { movement: deriveMenuMovement(spec.columns, spec.presets) })))),\n      parts: { menu: part(false, 1, spec.presets) }\n    });\n\n    const colorInputChangeEvent = generate$6('color-input-change');\n    const colorSwatchChangeEvent = generate$6('color-swatch-change');\n    const colorPickerCancelEvent = generate$6('color-picker-cancel');\n    const renderColorInput = (spec, sharedBackstage, colorInputBackstage, initialData) => {\n      const pField = FormField.parts.field({\n        factory: Input,\n        inputClasses: ['tox-textfield'],\n        data: initialData,\n        onSetValue: c => Invalidating.run(c).get(noop),\n        inputBehaviours: derive$1([\n          Disabling.config({ disabled: sharedBackstage.providers.isDisabled }),\n          receivingConfig(),\n          Tabstopping.config({}),\n          Invalidating.config({\n            invalidClass: 'tox-textbox-field-invalid',\n            getRoot: comp => parentElement(comp.element),\n            notify: {\n              onValid: comp => {\n                const val = Representing.getValue(comp);\n                emitWith(comp, colorInputChangeEvent, { color: val });\n              }\n            },\n            validator: {\n              validateOnLoad: false,\n              validate: input => {\n                const inputValue = Representing.getValue(input);\n                if (inputValue.length === 0) {\n                  return Future.pure(Result.value(true));\n                } else {\n                  const span = SugarElement.fromTag('span');\n                  set$8(span, 'background-color', inputValue);\n                  const res = getRaw(span, 'background-color').fold(() => Result.error('blah'), _ => Result.value(inputValue));\n                  return Future.pure(res);\n                }\n              }\n            }\n          })\n        ]),\n        selectOnFocus: false\n      });\n      const pLabel = spec.label.map(label => renderLabel$2(label, sharedBackstage.providers));\n      const emitSwatchChange = (colorBit, value) => {\n        emitWith(colorBit, colorSwatchChangeEvent, { value });\n      };\n      const onItemAction = (comp, value) => {\n        memColorButton.getOpt(comp).each(colorBit => {\n          if (value === 'custom') {\n            colorInputBackstage.colorPicker(valueOpt => {\n              valueOpt.fold(() => emit(colorBit, colorPickerCancelEvent), value => {\n                emitSwatchChange(colorBit, value);\n                addColor(spec.storageKey, value);\n              });\n            }, '#ffffff');\n          } else if (value === 'remove') {\n            emitSwatchChange(colorBit, '');\n          } else {\n            emitSwatchChange(colorBit, value);\n          }\n        });\n      };\n      const memColorButton = record(renderPanelButton({\n        dom: {\n          tag: 'span',\n          attributes: { 'aria-label': sharedBackstage.providers.translate('Color swatch') }\n        },\n        layouts: {\n          onRtl: () => [\n            southwest$2,\n            southeast$2,\n            south$2\n          ],\n          onLtr: () => [\n            southeast$2,\n            southwest$2,\n            south$2\n          ]\n        },\n        components: [],\n        fetch: getFetch$1(colorInputBackstage.getColors(spec.storageKey), spec.storageKey, colorInputBackstage.hasCustomColors()),\n        columns: colorInputBackstage.getColorCols(spec.storageKey),\n        presets: 'color',\n        onItemAction\n      }, sharedBackstage));\n      return FormField.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-form__group']\n        },\n        components: pLabel.toArray().concat([{\n            dom: {\n              tag: 'div',\n              classes: ['tox-color-input']\n            },\n            components: [\n              pField,\n              memColorButton.asSpec()\n            ]\n          }]),\n        fieldBehaviours: derive$1([config('form-field-events', [\n            run$1(colorInputChangeEvent, (comp, se) => {\n              memColorButton.getOpt(comp).each(colorButton => {\n                set$8(colorButton.element, 'background-color', se.event.color);\n              });\n              emitWith(comp, formChangeEvent, { name: spec.name });\n            }),\n            run$1(colorSwatchChangeEvent, (comp, se) => {\n              FormField.getField(comp).each(field => {\n                Representing.setValue(field, se.event.value);\n                Composing.getCurrent(comp).each(Focusing.focus);\n              });\n            }),\n            run$1(colorPickerCancelEvent, (comp, _se) => {\n              FormField.getField(comp).each(_field => {\n                Composing.getCurrent(comp).each(Focusing.focus);\n              });\n            })\n          ])])\n      });\n    };\n\n    const labelPart = optional({\n      schema: [required$1('dom')],\n      name: 'label'\n    });\n    const edgePart = name => optional({\n      name: '' + name + '-edge',\n      overrides: detail => {\n        const action = detail.model.manager.edgeActions[name];\n        return action.fold(() => ({}), a => ({\n          events: derive$2([\n            runActionExtra(touchstart(), (comp, se, d) => a(comp, d), [detail]),\n            runActionExtra(mousedown(), (comp, se, d) => a(comp, d), [detail]),\n            runActionExtra(mousemove(), (comp, se, det) => {\n              if (det.mouseIsDown.get()) {\n                a(comp, det);\n              }\n            }, [detail])\n          ])\n        }));\n      }\n    });\n    const tlEdgePart = edgePart('top-left');\n    const tedgePart = edgePart('top');\n    const trEdgePart = edgePart('top-right');\n    const redgePart = edgePart('right');\n    const brEdgePart = edgePart('bottom-right');\n    const bedgePart = edgePart('bottom');\n    const blEdgePart = edgePart('bottom-left');\n    const ledgePart = edgePart('left');\n    const thumbPart = required({\n      name: 'thumb',\n      defaults: constant$1({ dom: { styles: { position: 'absolute' } } }),\n      overrides: detail => {\n        return {\n          events: derive$2([\n            redirectToPart(touchstart(), detail, 'spectrum'),\n            redirectToPart(touchmove(), detail, 'spectrum'),\n            redirectToPart(touchend(), detail, 'spectrum'),\n            redirectToPart(mousedown(), detail, 'spectrum'),\n            redirectToPart(mousemove(), detail, 'spectrum'),\n            redirectToPart(mouseup(), detail, 'spectrum')\n          ])\n        };\n      }\n    });\n    const spectrumPart = required({\n      schema: [customField('mouseIsDown', () => Cell(false))],\n      name: 'spectrum',\n      overrides: detail => {\n        const modelDetail = detail.model;\n        const model = modelDetail.manager;\n        const setValueFrom = (component, simulatedEvent) => model.getValueFromEvent(simulatedEvent).map(value => model.setValueFrom(component, detail, value));\n        return {\n          behaviours: derive$1([\n            Keying.config({\n              mode: 'special',\n              onLeft: spectrum => model.onLeft(spectrum, detail),\n              onRight: spectrum => model.onRight(spectrum, detail),\n              onUp: spectrum => model.onUp(spectrum, detail),\n              onDown: spectrum => model.onDown(spectrum, detail)\n            }),\n            Focusing.config({})\n          ]),\n          events: derive$2([\n            run$1(touchstart(), setValueFrom),\n            run$1(touchmove(), setValueFrom),\n            run$1(mousedown(), setValueFrom),\n            run$1(mousemove(), (spectrum, se) => {\n              if (detail.mouseIsDown.get()) {\n                setValueFrom(spectrum, se);\n              }\n            })\n          ])\n        };\n      }\n    });\n    var SliderParts = [\n      labelPart,\n      ledgePart,\n      redgePart,\n      tedgePart,\n      bedgePart,\n      tlEdgePart,\n      trEdgePart,\n      blEdgePart,\n      brEdgePart,\n      thumbPart,\n      spectrumPart\n    ];\n\n    const _sliderChangeEvent = 'slider.change.value';\n    const sliderChangeEvent = constant$1(_sliderChangeEvent);\n    const isTouchEvent$2 = evt => evt.type.indexOf('touch') !== -1;\n    const getEventSource = simulatedEvent => {\n      const evt = simulatedEvent.event.raw;\n      if (isTouchEvent$2(evt)) {\n        const touchEvent = evt;\n        return touchEvent.touches !== undefined && touchEvent.touches.length === 1 ? Optional.some(touchEvent.touches[0]).map(t => SugarPosition(t.clientX, t.clientY)) : Optional.none();\n      } else {\n        const mouseEvent = evt;\n        return mouseEvent.clientX !== undefined ? Optional.some(mouseEvent).map(me => SugarPosition(me.clientX, me.clientY)) : Optional.none();\n      }\n    };\n\n    const t = 'top', r = 'right', b = 'bottom', l = 'left';\n    const minX = detail => detail.model.minX;\n    const minY = detail => detail.model.minY;\n    const min1X = detail => detail.model.minX - 1;\n    const min1Y = detail => detail.model.minY - 1;\n    const maxX = detail => detail.model.maxX;\n    const maxY = detail => detail.model.maxY;\n    const max1X = detail => detail.model.maxX + 1;\n    const max1Y = detail => detail.model.maxY + 1;\n    const range = (detail, max, min) => max(detail) - min(detail);\n    const xRange = detail => range(detail, maxX, minX);\n    const yRange = detail => range(detail, maxY, minY);\n    const halfX = detail => xRange(detail) / 2;\n    const halfY = detail => yRange(detail) / 2;\n    const step = detail => detail.stepSize;\n    const snap = detail => detail.snapToGrid;\n    const snapStart = detail => detail.snapStart;\n    const rounded = detail => detail.rounded;\n    const hasEdge = (detail, edgeName) => detail[edgeName + '-edge'] !== undefined;\n    const hasLEdge = detail => hasEdge(detail, l);\n    const hasREdge = detail => hasEdge(detail, r);\n    const hasTEdge = detail => hasEdge(detail, t);\n    const hasBEdge = detail => hasEdge(detail, b);\n    const currentValue = detail => detail.model.value.get();\n\n    const xyValue = (x, y) => ({\n      x,\n      y\n    });\n    const fireSliderChange$3 = (component, value) => {\n      emitWith(component, sliderChangeEvent(), { value });\n    };\n    const setToTLEdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(min1X(detail), min1Y(detail)));\n    };\n    const setToTEdge = (edge, detail) => {\n      fireSliderChange$3(edge, min1Y(detail));\n    };\n    const setToTEdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(halfX(detail), min1Y(detail)));\n    };\n    const setToTREdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(max1X(detail), min1Y(detail)));\n    };\n    const setToREdge = (edge, detail) => {\n      fireSliderChange$3(edge, max1X(detail));\n    };\n    const setToREdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(max1X(detail), halfY(detail)));\n    };\n    const setToBREdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(max1X(detail), max1Y(detail)));\n    };\n    const setToBEdge = (edge, detail) => {\n      fireSliderChange$3(edge, max1Y(detail));\n    };\n    const setToBEdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(halfX(detail), max1Y(detail)));\n    };\n    const setToBLEdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(min1X(detail), max1Y(detail)));\n    };\n    const setToLEdge = (edge, detail) => {\n      fireSliderChange$3(edge, min1X(detail));\n    };\n    const setToLEdgeXY = (edge, detail) => {\n      fireSliderChange$3(edge, xyValue(min1X(detail), halfY(detail)));\n    };\n\n    const reduceBy = (value, min, max, step) => {\n      if (value < min) {\n        return value;\n      } else if (value > max) {\n        return max;\n      } else if (value === min) {\n        return min - 1;\n      } else {\n        return Math.max(min, value - step);\n      }\n    };\n    const increaseBy = (value, min, max, step) => {\n      if (value > max) {\n        return value;\n      } else if (value < min) {\n        return min;\n      } else if (value === max) {\n        return max + 1;\n      } else {\n        return Math.min(max, value + step);\n      }\n    };\n    const capValue = (value, min, max) => Math.max(min, Math.min(max, value));\n    const snapValueOf = (value, min, max, step, snapStart) => snapStart.fold(() => {\n      const initValue = value - min;\n      const extraValue = Math.round(initValue / step) * step;\n      return capValue(min + extraValue, min - 1, max + 1);\n    }, start => {\n      const remainder = (value - start) % step;\n      const adjustment = Math.round(remainder / step);\n      const rawSteps = Math.floor((value - start) / step);\n      const maxSteps = Math.floor((max - start) / step);\n      const numSteps = Math.min(maxSteps, rawSteps + adjustment);\n      const r = start + numSteps * step;\n      return Math.max(start, r);\n    });\n    const findOffsetOf = (value, min, max) => Math.min(max, Math.max(value, min)) - min;\n    const findValueOf = args => {\n      const {min, max, range, value, step, snap, snapStart, rounded, hasMinEdge, hasMaxEdge, minBound, maxBound, screenRange} = args;\n      const capMin = hasMinEdge ? min - 1 : min;\n      const capMax = hasMaxEdge ? max + 1 : max;\n      if (value < minBound) {\n        return capMin;\n      } else if (value > maxBound) {\n        return capMax;\n      } else {\n        const offset = findOffsetOf(value, minBound, maxBound);\n        const newValue = capValue(offset / screenRange * range + min, capMin, capMax);\n        if (snap && newValue >= min && newValue <= max) {\n          return snapValueOf(newValue, min, max, step, snapStart);\n        } else if (rounded) {\n          return Math.round(newValue);\n        } else {\n          return newValue;\n        }\n      }\n    };\n    const findOffsetOfValue$2 = args => {\n      const {min, max, range, value, hasMinEdge, hasMaxEdge, maxBound, maxOffset, centerMinEdge, centerMaxEdge} = args;\n      if (value < min) {\n        return hasMinEdge ? 0 : centerMinEdge;\n      } else if (value > max) {\n        return hasMaxEdge ? maxBound : centerMaxEdge;\n      } else {\n        return (value - min) / range * maxOffset;\n      }\n    };\n\n    const top = 'top', right = 'right', bottom = 'bottom', left = 'left', width = 'width', height = 'height';\n    const getBounds = component => component.element.dom.getBoundingClientRect();\n    const getBoundsProperty = (bounds, property) => bounds[property];\n    const getMinXBounds = component => {\n      const bounds = getBounds(component);\n      return getBoundsProperty(bounds, left);\n    };\n    const getMaxXBounds = component => {\n      const bounds = getBounds(component);\n      return getBoundsProperty(bounds, right);\n    };\n    const getMinYBounds = component => {\n      const bounds = getBounds(component);\n      return getBoundsProperty(bounds, top);\n    };\n    const getMaxYBounds = component => {\n      const bounds = getBounds(component);\n      return getBoundsProperty(bounds, bottom);\n    };\n    const getXScreenRange = component => {\n      const bounds = getBounds(component);\n      return getBoundsProperty(bounds, width);\n    };\n    const getYScreenRange = component => {\n      const bounds = getBounds(component);\n      return getBoundsProperty(bounds, height);\n    };\n    const getCenterOffsetOf = (componentMinEdge, componentMaxEdge, spectrumMinEdge) => (componentMinEdge + componentMaxEdge) / 2 - spectrumMinEdge;\n    const getXCenterOffSetOf = (component, spectrum) => {\n      const componentBounds = getBounds(component);\n      const spectrumBounds = getBounds(spectrum);\n      const componentMinEdge = getBoundsProperty(componentBounds, left);\n      const componentMaxEdge = getBoundsProperty(componentBounds, right);\n      const spectrumMinEdge = getBoundsProperty(spectrumBounds, left);\n      return getCenterOffsetOf(componentMinEdge, componentMaxEdge, spectrumMinEdge);\n    };\n    const getYCenterOffSetOf = (component, spectrum) => {\n      const componentBounds = getBounds(component);\n      const spectrumBounds = getBounds(spectrum);\n      const componentMinEdge = getBoundsProperty(componentBounds, top);\n      const componentMaxEdge = getBoundsProperty(componentBounds, bottom);\n      const spectrumMinEdge = getBoundsProperty(spectrumBounds, top);\n      return getCenterOffsetOf(componentMinEdge, componentMaxEdge, spectrumMinEdge);\n    };\n\n    const fireSliderChange$2 = (spectrum, value) => {\n      emitWith(spectrum, sliderChangeEvent(), { value });\n    };\n    const findValueOfOffset$1 = (spectrum, detail, left) => {\n      const args = {\n        min: minX(detail),\n        max: maxX(detail),\n        range: xRange(detail),\n        value: left,\n        step: step(detail),\n        snap: snap(detail),\n        snapStart: snapStart(detail),\n        rounded: rounded(detail),\n        hasMinEdge: hasLEdge(detail),\n        hasMaxEdge: hasREdge(detail),\n        minBound: getMinXBounds(spectrum),\n        maxBound: getMaxXBounds(spectrum),\n        screenRange: getXScreenRange(spectrum)\n      };\n      return findValueOf(args);\n    };\n    const setValueFrom$2 = (spectrum, detail, value) => {\n      const xValue = findValueOfOffset$1(spectrum, detail, value);\n      const sliderVal = xValue;\n      fireSliderChange$2(spectrum, sliderVal);\n      return xValue;\n    };\n    const setToMin$2 = (spectrum, detail) => {\n      const min = minX(detail);\n      fireSliderChange$2(spectrum, min);\n    };\n    const setToMax$2 = (spectrum, detail) => {\n      const max = maxX(detail);\n      fireSliderChange$2(spectrum, max);\n    };\n    const moveBy$2 = (direction, spectrum, detail) => {\n      const f = direction > 0 ? increaseBy : reduceBy;\n      const xValue = f(currentValue(detail), minX(detail), maxX(detail), step(detail));\n      fireSliderChange$2(spectrum, xValue);\n      return Optional.some(xValue);\n    };\n    const handleMovement$2 = direction => (spectrum, detail) => moveBy$2(direction, spectrum, detail).map(always);\n    const getValueFromEvent$2 = simulatedEvent => {\n      const pos = getEventSource(simulatedEvent);\n      return pos.map(p => p.left);\n    };\n    const findOffsetOfValue$1 = (spectrum, detail, value, minEdge, maxEdge) => {\n      const minOffset = 0;\n      const maxOffset = getXScreenRange(spectrum);\n      const centerMinEdge = minEdge.bind(edge => Optional.some(getXCenterOffSetOf(edge, spectrum))).getOr(minOffset);\n      const centerMaxEdge = maxEdge.bind(edge => Optional.some(getXCenterOffSetOf(edge, spectrum))).getOr(maxOffset);\n      const args = {\n        min: minX(detail),\n        max: maxX(detail),\n        range: xRange(detail),\n        value,\n        hasMinEdge: hasLEdge(detail),\n        hasMaxEdge: hasREdge(detail),\n        minBound: getMinXBounds(spectrum),\n        minOffset,\n        maxBound: getMaxXBounds(spectrum),\n        maxOffset,\n        centerMinEdge,\n        centerMaxEdge\n      };\n      return findOffsetOfValue$2(args);\n    };\n    const findPositionOfValue$1 = (slider, spectrum, value, minEdge, maxEdge, detail) => {\n      const offset = findOffsetOfValue$1(spectrum, detail, value, minEdge, maxEdge);\n      return getMinXBounds(spectrum) - getMinXBounds(slider) + offset;\n    };\n    const setPositionFromValue$2 = (slider, thumb, detail, edges) => {\n      const value = currentValue(detail);\n      const pos = findPositionOfValue$1(slider, edges.getSpectrum(slider), value, edges.getLeftEdge(slider), edges.getRightEdge(slider), detail);\n      const thumbRadius = get$c(thumb.element) / 2;\n      set$8(thumb.element, 'left', pos - thumbRadius + 'px');\n    };\n    const onLeft$2 = handleMovement$2(-1);\n    const onRight$2 = handleMovement$2(1);\n    const onUp$2 = Optional.none;\n    const onDown$2 = Optional.none;\n    const edgeActions$2 = {\n      'top-left': Optional.none(),\n      'top': Optional.none(),\n      'top-right': Optional.none(),\n      'right': Optional.some(setToREdge),\n      'bottom-right': Optional.none(),\n      'bottom': Optional.none(),\n      'bottom-left': Optional.none(),\n      'left': Optional.some(setToLEdge)\n    };\n\n    var HorizontalModel = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        setValueFrom: setValueFrom$2,\n        setToMin: setToMin$2,\n        setToMax: setToMax$2,\n        findValueOfOffset: findValueOfOffset$1,\n        getValueFromEvent: getValueFromEvent$2,\n        findPositionOfValue: findPositionOfValue$1,\n        setPositionFromValue: setPositionFromValue$2,\n        onLeft: onLeft$2,\n        onRight: onRight$2,\n        onUp: onUp$2,\n        onDown: onDown$2,\n        edgeActions: edgeActions$2\n    });\n\n    const fireSliderChange$1 = (spectrum, value) => {\n      emitWith(spectrum, sliderChangeEvent(), { value });\n    };\n    const findValueOfOffset = (spectrum, detail, top) => {\n      const args = {\n        min: minY(detail),\n        max: maxY(detail),\n        range: yRange(detail),\n        value: top,\n        step: step(detail),\n        snap: snap(detail),\n        snapStart: snapStart(detail),\n        rounded: rounded(detail),\n        hasMinEdge: hasTEdge(detail),\n        hasMaxEdge: hasBEdge(detail),\n        minBound: getMinYBounds(spectrum),\n        maxBound: getMaxYBounds(spectrum),\n        screenRange: getYScreenRange(spectrum)\n      };\n      return findValueOf(args);\n    };\n    const setValueFrom$1 = (spectrum, detail, value) => {\n      const yValue = findValueOfOffset(spectrum, detail, value);\n      const sliderVal = yValue;\n      fireSliderChange$1(spectrum, sliderVal);\n      return yValue;\n    };\n    const setToMin$1 = (spectrum, detail) => {\n      const min = minY(detail);\n      fireSliderChange$1(spectrum, min);\n    };\n    const setToMax$1 = (spectrum, detail) => {\n      const max = maxY(detail);\n      fireSliderChange$1(spectrum, max);\n    };\n    const moveBy$1 = (direction, spectrum, detail) => {\n      const f = direction > 0 ? increaseBy : reduceBy;\n      const yValue = f(currentValue(detail), minY(detail), maxY(detail), step(detail));\n      fireSliderChange$1(spectrum, yValue);\n      return Optional.some(yValue);\n    };\n    const handleMovement$1 = direction => (spectrum, detail) => moveBy$1(direction, spectrum, detail).map(always);\n    const getValueFromEvent$1 = simulatedEvent => {\n      const pos = getEventSource(simulatedEvent);\n      return pos.map(p => {\n        return p.top;\n      });\n    };\n    const findOffsetOfValue = (spectrum, detail, value, minEdge, maxEdge) => {\n      const minOffset = 0;\n      const maxOffset = getYScreenRange(spectrum);\n      const centerMinEdge = minEdge.bind(edge => Optional.some(getYCenterOffSetOf(edge, spectrum))).getOr(minOffset);\n      const centerMaxEdge = maxEdge.bind(edge => Optional.some(getYCenterOffSetOf(edge, spectrum))).getOr(maxOffset);\n      const args = {\n        min: minY(detail),\n        max: maxY(detail),\n        range: yRange(detail),\n        value,\n        hasMinEdge: hasTEdge(detail),\n        hasMaxEdge: hasBEdge(detail),\n        minBound: getMinYBounds(spectrum),\n        minOffset,\n        maxBound: getMaxYBounds(spectrum),\n        maxOffset,\n        centerMinEdge,\n        centerMaxEdge\n      };\n      return findOffsetOfValue$2(args);\n    };\n    const findPositionOfValue = (slider, spectrum, value, minEdge, maxEdge, detail) => {\n      const offset = findOffsetOfValue(spectrum, detail, value, minEdge, maxEdge);\n      return getMinYBounds(spectrum) - getMinYBounds(slider) + offset;\n    };\n    const setPositionFromValue$1 = (slider, thumb, detail, edges) => {\n      const value = currentValue(detail);\n      const pos = findPositionOfValue(slider, edges.getSpectrum(slider), value, edges.getTopEdge(slider), edges.getBottomEdge(slider), detail);\n      const thumbRadius = get$d(thumb.element) / 2;\n      set$8(thumb.element, 'top', pos - thumbRadius + 'px');\n    };\n    const onLeft$1 = Optional.none;\n    const onRight$1 = Optional.none;\n    const onUp$1 = handleMovement$1(-1);\n    const onDown$1 = handleMovement$1(1);\n    const edgeActions$1 = {\n      'top-left': Optional.none(),\n      'top': Optional.some(setToTEdge),\n      'top-right': Optional.none(),\n      'right': Optional.none(),\n      'bottom-right': Optional.none(),\n      'bottom': Optional.some(setToBEdge),\n      'bottom-left': Optional.none(),\n      'left': Optional.none()\n    };\n\n    var VerticalModel = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        setValueFrom: setValueFrom$1,\n        setToMin: setToMin$1,\n        setToMax: setToMax$1,\n        findValueOfOffset: findValueOfOffset,\n        getValueFromEvent: getValueFromEvent$1,\n        findPositionOfValue: findPositionOfValue,\n        setPositionFromValue: setPositionFromValue$1,\n        onLeft: onLeft$1,\n        onRight: onRight$1,\n        onUp: onUp$1,\n        onDown: onDown$1,\n        edgeActions: edgeActions$1\n    });\n\n    const fireSliderChange = (spectrum, value) => {\n      emitWith(spectrum, sliderChangeEvent(), { value });\n    };\n    const sliderValue = (x, y) => ({\n      x,\n      y\n    });\n    const setValueFrom = (spectrum, detail, value) => {\n      const xValue = findValueOfOffset$1(spectrum, detail, value.left);\n      const yValue = findValueOfOffset(spectrum, detail, value.top);\n      const val = sliderValue(xValue, yValue);\n      fireSliderChange(spectrum, val);\n      return val;\n    };\n    const moveBy = (direction, isVerticalMovement, spectrum, detail) => {\n      const f = direction > 0 ? increaseBy : reduceBy;\n      const xValue = isVerticalMovement ? currentValue(detail).x : f(currentValue(detail).x, minX(detail), maxX(detail), step(detail));\n      const yValue = !isVerticalMovement ? currentValue(detail).y : f(currentValue(detail).y, minY(detail), maxY(detail), step(detail));\n      fireSliderChange(spectrum, sliderValue(xValue, yValue));\n      return Optional.some(xValue);\n    };\n    const handleMovement = (direction, isVerticalMovement) => (spectrum, detail) => moveBy(direction, isVerticalMovement, spectrum, detail).map(always);\n    const setToMin = (spectrum, detail) => {\n      const mX = minX(detail);\n      const mY = minY(detail);\n      fireSliderChange(spectrum, sliderValue(mX, mY));\n    };\n    const setToMax = (spectrum, detail) => {\n      const mX = maxX(detail);\n      const mY = maxY(detail);\n      fireSliderChange(spectrum, sliderValue(mX, mY));\n    };\n    const getValueFromEvent = simulatedEvent => getEventSource(simulatedEvent);\n    const setPositionFromValue = (slider, thumb, detail, edges) => {\n      const value = currentValue(detail);\n      const xPos = findPositionOfValue$1(slider, edges.getSpectrum(slider), value.x, edges.getLeftEdge(slider), edges.getRightEdge(slider), detail);\n      const yPos = findPositionOfValue(slider, edges.getSpectrum(slider), value.y, edges.getTopEdge(slider), edges.getBottomEdge(slider), detail);\n      const thumbXRadius = get$c(thumb.element) / 2;\n      const thumbYRadius = get$d(thumb.element) / 2;\n      set$8(thumb.element, 'left', xPos - thumbXRadius + 'px');\n      set$8(thumb.element, 'top', yPos - thumbYRadius + 'px');\n    };\n    const onLeft = handleMovement(-1, false);\n    const onRight = handleMovement(1, false);\n    const onUp = handleMovement(-1, true);\n    const onDown = handleMovement(1, true);\n    const edgeActions = {\n      'top-left': Optional.some(setToTLEdgeXY),\n      'top': Optional.some(setToTEdgeXY),\n      'top-right': Optional.some(setToTREdgeXY),\n      'right': Optional.some(setToREdgeXY),\n      'bottom-right': Optional.some(setToBREdgeXY),\n      'bottom': Optional.some(setToBEdgeXY),\n      'bottom-left': Optional.some(setToBLEdgeXY),\n      'left': Optional.some(setToLEdgeXY)\n    };\n\n    var TwoDModel = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        setValueFrom: setValueFrom,\n        setToMin: setToMin,\n        setToMax: setToMax,\n        getValueFromEvent: getValueFromEvent,\n        setPositionFromValue: setPositionFromValue,\n        onLeft: onLeft,\n        onRight: onRight,\n        onUp: onUp,\n        onDown: onDown,\n        edgeActions: edgeActions\n    });\n\n    const SliderSchema = [\n      defaulted('stepSize', 1),\n      defaulted('onChange', noop),\n      defaulted('onChoose', noop),\n      defaulted('onInit', noop),\n      defaulted('onDragStart', noop),\n      defaulted('onDragEnd', noop),\n      defaulted('snapToGrid', false),\n      defaulted('rounded', true),\n      option$3('snapStart'),\n      requiredOf('model', choose$1('mode', {\n        x: [\n          defaulted('minX', 0),\n          defaulted('maxX', 100),\n          customField('value', spec => Cell(spec.mode.minX)),\n          required$1('getInitialValue'),\n          output$1('manager', HorizontalModel)\n        ],\n        y: [\n          defaulted('minY', 0),\n          defaulted('maxY', 100),\n          customField('value', spec => Cell(spec.mode.minY)),\n          required$1('getInitialValue'),\n          output$1('manager', VerticalModel)\n        ],\n        xy: [\n          defaulted('minX', 0),\n          defaulted('maxX', 100),\n          defaulted('minY', 0),\n          defaulted('maxY', 100),\n          customField('value', spec => Cell({\n            x: spec.mode.minX,\n            y: spec.mode.minY\n          })),\n          required$1('getInitialValue'),\n          output$1('manager', TwoDModel)\n        ]\n      })),\n      field('sliderBehaviours', [\n        Keying,\n        Representing\n      ]),\n      customField('mouseIsDown', () => Cell(false))\n    ];\n\n    const sketch$2 = (detail, components, _spec, _externals) => {\n      const getThumb = component => getPartOrDie(component, detail, 'thumb');\n      const getSpectrum = component => getPartOrDie(component, detail, 'spectrum');\n      const getLeftEdge = component => getPart(component, detail, 'left-edge');\n      const getRightEdge = component => getPart(component, detail, 'right-edge');\n      const getTopEdge = component => getPart(component, detail, 'top-edge');\n      const getBottomEdge = component => getPart(component, detail, 'bottom-edge');\n      const modelDetail = detail.model;\n      const model = modelDetail.manager;\n      const refresh = (slider, thumb) => {\n        model.setPositionFromValue(slider, thumb, detail, {\n          getLeftEdge,\n          getRightEdge,\n          getTopEdge,\n          getBottomEdge,\n          getSpectrum\n        });\n      };\n      const setValue = (slider, newValue) => {\n        modelDetail.value.set(newValue);\n        const thumb = getThumb(slider);\n        refresh(slider, thumb);\n      };\n      const changeValue = (slider, newValue) => {\n        setValue(slider, newValue);\n        const thumb = getThumb(slider);\n        detail.onChange(slider, thumb, newValue);\n        return Optional.some(true);\n      };\n      const resetToMin = slider => {\n        model.setToMin(slider, detail);\n      };\n      const resetToMax = slider => {\n        model.setToMax(slider, detail);\n      };\n      const choose = slider => {\n        const fireOnChoose = () => {\n          getPart(slider, detail, 'thumb').each(thumb => {\n            const value = modelDetail.value.get();\n            detail.onChoose(slider, thumb, value);\n          });\n        };\n        const wasDown = detail.mouseIsDown.get();\n        detail.mouseIsDown.set(false);\n        if (wasDown) {\n          fireOnChoose();\n        }\n      };\n      const onDragStart = (slider, simulatedEvent) => {\n        simulatedEvent.stop();\n        detail.mouseIsDown.set(true);\n        detail.onDragStart(slider, getThumb(slider));\n      };\n      const onDragEnd = (slider, simulatedEvent) => {\n        simulatedEvent.stop();\n        detail.onDragEnd(slider, getThumb(slider));\n        choose(slider);\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        behaviours: augment(detail.sliderBehaviours, [\n          Keying.config({\n            mode: 'special',\n            focusIn: slider => {\n              return getPart(slider, detail, 'spectrum').map(Keying.focusIn).map(always);\n            }\n          }),\n          Representing.config({\n            store: {\n              mode: 'manual',\n              getValue: _ => {\n                return modelDetail.value.get();\n              },\n              setValue\n            }\n          }),\n          Receiving.config({ channels: { [mouseReleased()]: { onReceive: choose } } })\n        ]),\n        events: derive$2([\n          run$1(sliderChangeEvent(), (slider, simulatedEvent) => {\n            changeValue(slider, simulatedEvent.event.value);\n          }),\n          runOnAttached((slider, _simulatedEvent) => {\n            const getInitial = modelDetail.getInitialValue();\n            modelDetail.value.set(getInitial);\n            const thumb = getThumb(slider);\n            refresh(slider, thumb);\n            const spectrum = getSpectrum(slider);\n            detail.onInit(slider, thumb, spectrum, modelDetail.value.get());\n          }),\n          run$1(touchstart(), onDragStart),\n          run$1(touchend(), onDragEnd),\n          run$1(mousedown(), onDragStart),\n          run$1(mouseup(), onDragEnd)\n        ]),\n        apis: {\n          resetToMin,\n          resetToMax,\n          setValue,\n          refresh\n        },\n        domModification: { styles: { position: 'relative' } }\n      };\n    };\n\n    const Slider = composite({\n      name: 'Slider',\n      configFields: SliderSchema,\n      partFields: SliderParts,\n      factory: sketch$2,\n      apis: {\n        setValue: (apis, slider, value) => {\n          apis.setValue(slider, value);\n        },\n        resetToMin: (apis, slider) => {\n          apis.resetToMin(slider);\n        },\n        resetToMax: (apis, slider) => {\n          apis.resetToMax(slider);\n        },\n        refresh: (apis, slider) => {\n          apis.refresh(slider);\n        }\n      }\n    });\n\n    const fieldsUpdate = generate$6('rgb-hex-update');\n    const sliderUpdate = generate$6('slider-update');\n    const paletteUpdate = generate$6('palette-update');\n\n    const sliderFactory = (translate, getClass) => {\n      const spectrum = Slider.parts.spectrum({\n        dom: {\n          tag: 'div',\n          classes: [getClass('hue-slider-spectrum')],\n          attributes: { role: 'presentation' }\n        }\n      });\n      const thumb = Slider.parts.thumb({\n        dom: {\n          tag: 'div',\n          classes: [getClass('hue-slider-thumb')],\n          attributes: { role: 'presentation' }\n        }\n      });\n      return Slider.sketch({\n        dom: {\n          tag: 'div',\n          classes: [getClass('hue-slider')],\n          attributes: { role: 'presentation' }\n        },\n        rounded: false,\n        model: {\n          mode: 'y',\n          getInitialValue: constant$1(0)\n        },\n        components: [\n          spectrum,\n          thumb\n        ],\n        sliderBehaviours: derive$1([Focusing.config({})]),\n        onChange: (slider, _thumb, value) => {\n          emitWith(slider, sliderUpdate, { value });\n        }\n      });\n    };\n\n    const owner$1 = 'form';\n    const schema$i = [field('formBehaviours', [Representing])];\n    const getPartName$1 = name => '<alloy.field.' + name + '>';\n    const sketch$1 = fSpec => {\n      const parts = (() => {\n        const record = [];\n        const field = (name, config) => {\n          record.push(name);\n          return generateOne$1(owner$1, getPartName$1(name), config);\n        };\n        return {\n          field,\n          record: constant$1(record)\n        };\n      })();\n      const spec = fSpec(parts);\n      const partNames = parts.record();\n      const fieldParts = map$2(partNames, n => required({\n        name: n,\n        pname: getPartName$1(n)\n      }));\n      return composite$1(owner$1, schema$i, fieldParts, make$4, spec);\n    };\n    const toResult = (o, e) => o.fold(() => Result.error(e), Result.value);\n    const make$4 = (detail, components) => ({\n      uid: detail.uid,\n      dom: detail.dom,\n      components,\n      behaviours: augment(detail.formBehaviours, [Representing.config({\n          store: {\n            mode: 'manual',\n            getValue: form => {\n              const resPs = getAllParts(form, detail);\n              return map$1(resPs, (resPThunk, pName) => resPThunk().bind(v => {\n                const opt = Composing.getCurrent(v);\n                return toResult(opt, new Error(`Cannot find a current component to extract the value from for form part '${ pName }': ` + element(v.element)));\n              }).map(Representing.getValue));\n            },\n            setValue: (form, values) => {\n              each(values, (newValue, key) => {\n                getPart(form, detail, key).each(wrapper => {\n                  Composing.getCurrent(wrapper).each(field => {\n                    Representing.setValue(field, newValue);\n                  });\n                });\n              });\n            }\n          }\n        })]),\n      apis: {\n        getField: (form, key) => {\n          return getPart(form, detail, key).bind(Composing.getCurrent);\n        }\n      }\n    });\n    const Form = {\n      getField: makeApi((apis, component, key) => apis.getField(component, key)),\n      sketch: sketch$1\n    };\n\n    const validInput = generate$6('valid-input');\n    const invalidInput = generate$6('invalid-input');\n    const validatingInput = generate$6('validating-input');\n    const translatePrefix = 'colorcustom.rgb.';\n    const rgbFormFactory = (translate, getClass, onValidHexx, onInvalidHexx) => {\n      const invalidation = (label, isValid) => Invalidating.config({\n        invalidClass: getClass('invalid'),\n        notify: {\n          onValidate: comp => {\n            emitWith(comp, validatingInput, { type: label });\n          },\n          onValid: comp => {\n            emitWith(comp, validInput, {\n              type: label,\n              value: Representing.getValue(comp)\n            });\n          },\n          onInvalid: comp => {\n            emitWith(comp, invalidInput, {\n              type: label,\n              value: Representing.getValue(comp)\n            });\n          }\n        },\n        validator: {\n          validate: comp => {\n            const value = Representing.getValue(comp);\n            const res = isValid(value) ? Result.value(true) : Result.error(translate('aria.input.invalid'));\n            return Future.pure(res);\n          },\n          validateOnLoad: false\n        }\n      });\n      const renderTextField = (isValid, name, label, description, data) => {\n        const helptext = translate(translatePrefix + 'range');\n        const pLabel = FormField.parts.label({\n          dom: {\n            tag: 'label',\n            attributes: { 'aria-label': description }\n          },\n          components: [text$2(label)]\n        });\n        const pField = FormField.parts.field({\n          data,\n          factory: Input,\n          inputAttributes: {\n            type: 'text',\n            ...name === 'hex' ? { 'aria-live': 'polite' } : {}\n          },\n          inputClasses: [getClass('textfield')],\n          inputBehaviours: derive$1([\n            invalidation(name, isValid),\n            Tabstopping.config({})\n          ]),\n          onSetValue: input => {\n            if (Invalidating.isInvalid(input)) {\n              const run = Invalidating.run(input);\n              run.get(noop);\n            }\n          }\n        });\n        const comps = [\n          pLabel,\n          pField\n        ];\n        const concats = name !== 'hex' ? [FormField.parts['aria-descriptor']({ text: helptext })] : [];\n        const components = comps.concat(concats);\n        return {\n          dom: {\n            tag: 'div',\n            attributes: { role: 'presentation' }\n          },\n          components\n        };\n      };\n      const copyRgbToHex = (form, rgba) => {\n        const hex = fromRgba(rgba);\n        Form.getField(form, 'hex').each(hexField => {\n          if (!Focusing.isFocused(hexField)) {\n            Representing.setValue(form, { hex: hex.value });\n          }\n        });\n        return hex;\n      };\n      const copyRgbToForm = (form, rgb) => {\n        const red = rgb.red;\n        const green = rgb.green;\n        const blue = rgb.blue;\n        Representing.setValue(form, {\n          red,\n          green,\n          blue\n        });\n      };\n      const memPreview = record({\n        dom: {\n          tag: 'div',\n          classes: [getClass('rgba-preview')],\n          styles: { 'background-color': 'white' },\n          attributes: { role: 'presentation' }\n        }\n      });\n      const updatePreview = (anyInSystem, hex) => {\n        memPreview.getOpt(anyInSystem).each(preview => {\n          set$8(preview.element, 'background-color', '#' + hex.value);\n        });\n      };\n      const factory = () => {\n        const state = {\n          red: Cell(Optional.some(255)),\n          green: Cell(Optional.some(255)),\n          blue: Cell(Optional.some(255)),\n          hex: Cell(Optional.some('ffffff'))\n        };\n        const copyHexToRgb = (form, hex) => {\n          const rgb = fromHex(hex);\n          copyRgbToForm(form, rgb);\n          setValueRgb(rgb);\n        };\n        const get = prop => state[prop].get();\n        const set = (prop, value) => {\n          state[prop].set(value);\n        };\n        const getValueRgb = () => get('red').bind(red => get('green').bind(green => get('blue').map(blue => rgbaColour(red, green, blue, 1))));\n        const setValueRgb = rgb => {\n          const red = rgb.red;\n          const green = rgb.green;\n          const blue = rgb.blue;\n          set('red', Optional.some(red));\n          set('green', Optional.some(green));\n          set('blue', Optional.some(blue));\n        };\n        const onInvalidInput = (form, simulatedEvent) => {\n          const data = simulatedEvent.event;\n          if (data.type !== 'hex') {\n            set(data.type, Optional.none());\n          } else {\n            onInvalidHexx(form);\n          }\n        };\n        const onValidHex = (form, value) => {\n          onValidHexx(form);\n          const hex = hexColour(value);\n          set('hex', Optional.some(value));\n          const rgb = fromHex(hex);\n          copyRgbToForm(form, rgb);\n          setValueRgb(rgb);\n          emitWith(form, fieldsUpdate, { hex });\n          updatePreview(form, hex);\n        };\n        const onValidRgb = (form, prop, value) => {\n          const val = parseInt(value, 10);\n          set(prop, Optional.some(val));\n          getValueRgb().each(rgb => {\n            const hex = copyRgbToHex(form, rgb);\n            emitWith(form, fieldsUpdate, { hex });\n            updatePreview(form, hex);\n          });\n        };\n        const isHexInputEvent = data => data.type === 'hex';\n        const onValidInput = (form, simulatedEvent) => {\n          const data = simulatedEvent.event;\n          if (isHexInputEvent(data)) {\n            onValidHex(form, data.value);\n          } else {\n            onValidRgb(form, data.type, data.value);\n          }\n        };\n        const formPartStrings = key => ({\n          label: translate(translatePrefix + key + '.label'),\n          description: translate(translatePrefix + key + '.description')\n        });\n        const redStrings = formPartStrings('red');\n        const greenStrings = formPartStrings('green');\n        const blueStrings = formPartStrings('blue');\n        const hexStrings = formPartStrings('hex');\n        return deepMerge(Form.sketch(parts => ({\n          dom: {\n            tag: 'form',\n            classes: [getClass('rgb-form')],\n            attributes: { 'aria-label': translate('aria.color.picker') }\n          },\n          components: [\n            parts.field('red', FormField.sketch(renderTextField(isRgbaComponent, 'red', redStrings.label, redStrings.description, 255))),\n            parts.field('green', FormField.sketch(renderTextField(isRgbaComponent, 'green', greenStrings.label, greenStrings.description, 255))),\n            parts.field('blue', FormField.sketch(renderTextField(isRgbaComponent, 'blue', blueStrings.label, blueStrings.description, 255))),\n            parts.field('hex', FormField.sketch(renderTextField(isHexString, 'hex', hexStrings.label, hexStrings.description, 'ffffff'))),\n            memPreview.asSpec()\n          ],\n          formBehaviours: derive$1([\n            Invalidating.config({ invalidClass: getClass('form-invalid') }),\n            config('rgb-form-events', [\n              run$1(validInput, onValidInput),\n              run$1(invalidInput, onInvalidInput),\n              run$1(validatingInput, onInvalidInput)\n            ])\n          ])\n        })), {\n          apis: {\n            updateHex: (form, hex) => {\n              Representing.setValue(form, { hex: hex.value });\n              copyHexToRgb(form, hex);\n              updatePreview(form, hex);\n            }\n          }\n        });\n      };\n      const rgbFormSketcher = single({\n        factory,\n        name: 'RgbForm',\n        configFields: [],\n        apis: {\n          updateHex: (apis, form, hex) => {\n            apis.updateHex(form, hex);\n          }\n        },\n        extraApis: {}\n      });\n      return rgbFormSketcher;\n    };\n\n    const paletteFactory = (_translate, getClass) => {\n      const spectrumPart = Slider.parts.spectrum({\n        dom: {\n          tag: 'canvas',\n          attributes: { role: 'presentation' },\n          classes: [getClass('sv-palette-spectrum')]\n        }\n      });\n      const thumbPart = Slider.parts.thumb({\n        dom: {\n          tag: 'div',\n          attributes: { role: 'presentation' },\n          classes: [getClass('sv-palette-thumb')],\n          innerHtml: `<div class=${ getClass('sv-palette-inner-thumb') } role=\"presentation\"></div>`\n        }\n      });\n      const setColour = (canvas, rgba) => {\n        const {width, height} = canvas;\n        const ctx = canvas.getContext('2d');\n        if (ctx === null) {\n          return;\n        }\n        ctx.fillStyle = rgba;\n        ctx.fillRect(0, 0, width, height);\n        const grdWhite = ctx.createLinearGradient(0, 0, width, 0);\n        grdWhite.addColorStop(0, 'rgba(255,255,255,1)');\n        grdWhite.addColorStop(1, 'rgba(255,255,255,0)');\n        ctx.fillStyle = grdWhite;\n        ctx.fillRect(0, 0, width, height);\n        const grdBlack = ctx.createLinearGradient(0, 0, 0, height);\n        grdBlack.addColorStop(0, 'rgba(0,0,0,0)');\n        grdBlack.addColorStop(1, 'rgba(0,0,0,1)');\n        ctx.fillStyle = grdBlack;\n        ctx.fillRect(0, 0, width, height);\n      };\n      const setPaletteHue = (slider, hue) => {\n        const canvas = slider.components()[0].element.dom;\n        const hsv = hsvColour(hue, 100, 100);\n        const rgba = fromHsv(hsv);\n        setColour(canvas, toString(rgba));\n      };\n      const setPaletteThumb = (slider, hex) => {\n        const hsv = fromRgb(fromHex(hex));\n        Slider.setValue(slider, {\n          x: hsv.saturation,\n          y: 100 - hsv.value\n        });\n      };\n      const factory = _detail => {\n        const getInitialValue = constant$1({\n          x: 0,\n          y: 0\n        });\n        const onChange = (slider, _thumb, value) => {\n          emitWith(slider, paletteUpdate, { value });\n        };\n        const onInit = (_slider, _thumb, spectrum, _value) => {\n          setColour(spectrum.element.dom, toString(red));\n        };\n        const sliderBehaviours = derive$1([\n          Composing.config({ find: Optional.some }),\n          Focusing.config({})\n        ]);\n        return Slider.sketch({\n          dom: {\n            tag: 'div',\n            attributes: { role: 'presentation' },\n            classes: [getClass('sv-palette')]\n          },\n          model: {\n            mode: 'xy',\n            getInitialValue\n          },\n          rounded: false,\n          components: [\n            spectrumPart,\n            thumbPart\n          ],\n          onChange,\n          onInit,\n          sliderBehaviours\n        });\n      };\n      const saturationBrightnessPaletteSketcher = single({\n        factory,\n        name: 'SaturationBrightnessPalette',\n        configFields: [],\n        apis: {\n          setHue: (_apis, slider, hue) => {\n            setPaletteHue(slider, hue);\n          },\n          setThumb: (_apis, slider, hex) => {\n            setPaletteThumb(slider, hex);\n          }\n        },\n        extraApis: {}\n      });\n      return saturationBrightnessPaletteSketcher;\n    };\n\n    const makeFactory = (translate, getClass) => {\n      const factory = detail => {\n        const rgbForm = rgbFormFactory(translate, getClass, detail.onValidHex, detail.onInvalidHex);\n        const sbPalette = paletteFactory(translate, getClass);\n        const hueSliderToDegrees = hue => (100 - hue) / 100 * 360;\n        const hueDegreesToSlider = hue => 100 - hue / 360 * 100;\n        const state = {\n          paletteRgba: Cell(red),\n          paletteHue: Cell(0)\n        };\n        const memSlider = record(sliderFactory(translate, getClass));\n        const memPalette = record(sbPalette.sketch({}));\n        const memRgb = record(rgbForm.sketch({}));\n        const updatePalette = (anyInSystem, _hex, hue) => {\n          memPalette.getOpt(anyInSystem).each(palette => {\n            sbPalette.setHue(palette, hue);\n          });\n        };\n        const updateFields = (anyInSystem, hex) => {\n          memRgb.getOpt(anyInSystem).each(form => {\n            rgbForm.updateHex(form, hex);\n          });\n        };\n        const updateSlider = (anyInSystem, _hex, hue) => {\n          memSlider.getOpt(anyInSystem).each(slider => {\n            Slider.setValue(slider, hueDegreesToSlider(hue));\n          });\n        };\n        const updatePaletteThumb = (anyInSystem, hex) => {\n          memPalette.getOpt(anyInSystem).each(palette => {\n            sbPalette.setThumb(palette, hex);\n          });\n        };\n        const updateState = (hex, hue) => {\n          const rgba = fromHex(hex);\n          state.paletteRgba.set(rgba);\n          state.paletteHue.set(hue);\n        };\n        const runUpdates = (anyInSystem, hex, hue, updates) => {\n          updateState(hex, hue);\n          each$1(updates, update => {\n            update(anyInSystem, hex, hue);\n          });\n        };\n        const onPaletteUpdate = () => {\n          const updates = [updateFields];\n          return (form, simulatedEvent) => {\n            const value = simulatedEvent.event.value;\n            const oldHue = state.paletteHue.get();\n            const newHsv = hsvColour(oldHue, value.x, 100 - value.y);\n            const newHex = hsvToHex(newHsv);\n            runUpdates(form, newHex, oldHue, updates);\n          };\n        };\n        const onSliderUpdate = () => {\n          const updates = [\n            updatePalette,\n            updateFields\n          ];\n          return (form, simulatedEvent) => {\n            const hue = hueSliderToDegrees(simulatedEvent.event.value);\n            const oldRgb = state.paletteRgba.get();\n            const oldHsv = fromRgb(oldRgb);\n            const newHsv = hsvColour(hue, oldHsv.saturation, oldHsv.value);\n            const newHex = hsvToHex(newHsv);\n            runUpdates(form, newHex, hue, updates);\n          };\n        };\n        const onFieldsUpdate = () => {\n          const updates = [\n            updatePalette,\n            updateSlider,\n            updatePaletteThumb\n          ];\n          return (form, simulatedEvent) => {\n            const hex = simulatedEvent.event.hex;\n            const hsv = hexToHsv(hex);\n            runUpdates(form, hex, hsv.hue, updates);\n          };\n        };\n        return {\n          uid: detail.uid,\n          dom: detail.dom,\n          components: [\n            memPalette.asSpec(),\n            memSlider.asSpec(),\n            memRgb.asSpec()\n          ],\n          behaviours: derive$1([\n            config('colour-picker-events', [\n              run$1(fieldsUpdate, onFieldsUpdate()),\n              run$1(paletteUpdate, onPaletteUpdate()),\n              run$1(sliderUpdate, onSliderUpdate())\n            ]),\n            Composing.config({ find: comp => memRgb.getOpt(comp) }),\n            Keying.config({ mode: 'acyclic' })\n          ])\n        };\n      };\n      const colourPickerSketcher = single({\n        name: 'ColourPicker',\n        configFields: [\n          required$1('dom'),\n          defaulted('onValidHex', noop),\n          defaulted('onInvalidHex', noop)\n        ],\n        factory\n      });\n      return colourPickerSketcher;\n    };\n\n    const self = () => Composing.config({ find: Optional.some });\n    const memento$1 = mem => Composing.config({ find: mem.getOpt });\n    const childAt = index => Composing.config({ find: comp => child$2(comp.element, index).bind(element => comp.getSystem().getByDom(element).toOptional()) });\n    const ComposingConfigs = {\n      self,\n      memento: memento$1,\n      childAt\n    };\n\n    const processors = objOf([\n      defaulted('preprocess', identity),\n      defaulted('postprocess', identity)\n    ]);\n    const memento = (mem, rawProcessors) => {\n      const ps = asRawOrDie$1('RepresentingConfigs.memento processors', processors, rawProcessors);\n      return Representing.config({\n        store: {\n          mode: 'manual',\n          getValue: comp => {\n            const other = mem.get(comp);\n            const rawValue = Representing.getValue(other);\n            return ps.postprocess(rawValue);\n          },\n          setValue: (comp, rawValue) => {\n            const newValue = ps.preprocess(rawValue);\n            const other = mem.get(comp);\n            Representing.setValue(other, newValue);\n          }\n        }\n      });\n    };\n    const withComp = (optInitialValue, getter, setter) => Representing.config({\n      store: {\n        mode: 'manual',\n        ...optInitialValue.map(initialValue => ({ initialValue })).getOr({}),\n        getValue: getter,\n        setValue: setter\n      }\n    });\n    const withElement = (initialValue, getter, setter) => withComp(initialValue, c => getter(c.element), (c, v) => setter(c.element, v));\n    const domValue = optInitialValue => withElement(optInitialValue, get$6, set$5);\n    const domHtml = optInitialValue => withElement(optInitialValue, get$9, set$6);\n    const memory = initialValue => Representing.config({\n      store: {\n        mode: 'memory',\n        initialValue\n      }\n    });\n    const RepresentingConfigs = {\n      memento,\n      withElement,\n      withComp,\n      domValue,\n      domHtml,\n      memory\n    };\n\n    const english = {\n      'colorcustom.rgb.red.label': 'R',\n      'colorcustom.rgb.red.description': 'Red component',\n      'colorcustom.rgb.green.label': 'G',\n      'colorcustom.rgb.green.description': 'Green component',\n      'colorcustom.rgb.blue.label': 'B',\n      'colorcustom.rgb.blue.description': 'Blue component',\n      'colorcustom.rgb.hex.label': '#',\n      'colorcustom.rgb.hex.description': 'Hex color code',\n      'colorcustom.rgb.range': 'Range 0 to 255',\n      'aria.color.picker': 'Color Picker',\n      'aria.input.invalid': 'Invalid input'\n    };\n    const translate$1 = providerBackstage => key => {\n      return providerBackstage.translate(english[key]);\n    };\n    const renderColorPicker = (_spec, providerBackstage, initialData) => {\n      const getClass = key => 'tox-' + key;\n      const colourPickerFactory = makeFactory(translate$1(providerBackstage), getClass);\n      const onValidHex = form => {\n        emitWith(form, formActionEvent, {\n          name: 'hex-valid',\n          value: true\n        });\n      };\n      const onInvalidHex = form => {\n        emitWith(form, formActionEvent, {\n          name: 'hex-valid',\n          value: false\n        });\n      };\n      const memPicker = record(colourPickerFactory.sketch({\n        dom: {\n          tag: 'div',\n          classes: [getClass('color-picker-container')],\n          attributes: { role: 'presentation' }\n        },\n        onValidHex,\n        onInvalidHex\n      }));\n      return {\n        dom: { tag: 'div' },\n        components: [memPicker.asSpec()],\n        behaviours: derive$1([\n          RepresentingConfigs.withComp(initialData, comp => {\n            const picker = memPicker.get(comp);\n            const optRgbForm = Composing.getCurrent(picker);\n            const optHex = optRgbForm.bind(rgbForm => {\n              const formValues = Representing.getValue(rgbForm);\n              return formValues.hex;\n            });\n            return optHex.map(hex => '#' + hex).getOr('');\n          }, (comp, newValue) => {\n            const pattern = /^#([a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?)/;\n            const valOpt = Optional.from(pattern.exec(newValue)).bind(matches => get$h(matches, 1));\n            const picker = memPicker.get(comp);\n            const optRgbForm = Composing.getCurrent(picker);\n            optRgbForm.fold(() => {\n              console.log('Can not find form');\n            }, rgbForm => {\n              Representing.setValue(rgbForm, { hex: valOpt.getOr('') });\n              Form.getField(rgbForm, 'hex').each(hexField => {\n                emit(hexField, input());\n              });\n            });\n          }),\n          ComposingConfigs.self()\n        ])\n      };\n    };\n\n    var global$2 = tinymce.util.Tools.resolve('tinymce.Resource');\n\n    const isOldCustomEditor = spec => has$2(spec, 'init');\n    const renderCustomEditor = spec => {\n      const editorApi = value$2();\n      const memReplaced = record({ dom: { tag: spec.tag } });\n      const initialValue = value$2();\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-custom-editor']\n        },\n        behaviours: derive$1([\n          config('custom-editor-events', [runOnAttached(component => {\n              memReplaced.getOpt(component).each(ta => {\n                (isOldCustomEditor(spec) ? spec.init(ta.element.dom) : global$2.load(spec.scriptId, spec.scriptUrl).then(init => init(ta.element.dom, spec.settings))).then(ea => {\n                  initialValue.on(cvalue => {\n                    ea.setValue(cvalue);\n                  });\n                  initialValue.clear();\n                  editorApi.set(ea);\n                });\n              });\n            })]),\n          RepresentingConfigs.withComp(Optional.none(), () => editorApi.get().fold(() => initialValue.get().getOr(''), ed => ed.getValue()), (component, value) => {\n            editorApi.get().fold(() => initialValue.set(value), ed => ed.setValue(value));\n          }),\n          ComposingConfigs.self()\n        ]),\n        components: [memReplaced.asSpec()]\n      };\n    };\n\n    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');\n\n    const filterByExtension = (files, providersBackstage) => {\n      const allowedImageFileTypes = global$1.explode(providersBackstage.getOption('images_file_types'));\n      const isFileInAllowedTypes = file => exists(allowedImageFileTypes, type => endsWith(file.name.toLowerCase(), `.${ type.toLowerCase() }`));\n      return filter$2(from(files), isFileInAllowedTypes);\n    };\n    const renderDropZone = (spec, providersBackstage, initialData) => {\n      const stopper = (_, se) => {\n        se.stop();\n      };\n      const sequence = actions => (comp, se) => {\n        each$1(actions, a => {\n          a(comp, se);\n        });\n      };\n      const onDrop = (comp, se) => {\n        var _a;\n        if (!Disabling.isDisabled(comp)) {\n          const transferEvent = se.event.raw;\n          handleFiles(comp, (_a = transferEvent.dataTransfer) === null || _a === void 0 ? void 0 : _a.files);\n        }\n      };\n      const onSelect = (component, simulatedEvent) => {\n        const input = simulatedEvent.event.raw.target;\n        handleFiles(component, input.files);\n      };\n      const handleFiles = (component, files) => {\n        if (files) {\n          Representing.setValue(component, filterByExtension(files, providersBackstage));\n          emitWith(component, formChangeEvent, { name: spec.name });\n        }\n      };\n      const memInput = record({\n        dom: {\n          tag: 'input',\n          attributes: {\n            type: 'file',\n            accept: 'image/*'\n          },\n          styles: { display: 'none' }\n        },\n        behaviours: derive$1([config('input-file-events', [\n            cutter(click()),\n            cutter(tap())\n          ])])\n      });\n      const renderField = s => ({\n        uid: s.uid,\n        dom: {\n          tag: 'div',\n          classes: ['tox-dropzone-container']\n        },\n        behaviours: derive$1([\n          RepresentingConfigs.memory(initialData.getOr([])),\n          ComposingConfigs.self(),\n          Disabling.config({}),\n          Toggling.config({\n            toggleClass: 'dragenter',\n            toggleOnExecute: false\n          }),\n          config('dropzone-events', [\n            run$1('dragenter', sequence([\n              stopper,\n              Toggling.toggle\n            ])),\n            run$1('dragleave', sequence([\n              stopper,\n              Toggling.toggle\n            ])),\n            run$1('dragover', stopper),\n            run$1('drop', sequence([\n              stopper,\n              onDrop\n            ])),\n            run$1(change(), onSelect)\n          ])\n        ]),\n        components: [{\n            dom: {\n              tag: 'div',\n              classes: ['tox-dropzone'],\n              styles: {}\n            },\n            components: [\n              {\n                dom: { tag: 'p' },\n                components: [text$2(providersBackstage.translate('Drop an image here'))]\n              },\n              Button.sketch({\n                dom: {\n                  tag: 'button',\n                  styles: { position: 'relative' },\n                  classes: [\n                    'tox-button',\n                    'tox-button--secondary'\n                  ]\n                },\n                components: [\n                  text$2(providersBackstage.translate('Browse for an image')),\n                  memInput.asSpec()\n                ],\n                action: comp => {\n                  const inputComp = memInput.get(comp);\n                  inputComp.element.dom.click();\n                },\n                buttonBehaviours: derive$1([\n                  Tabstopping.config({}),\n                  DisablingConfigs.button(providersBackstage.isDisabled),\n                  receivingConfig()\n                ])\n              })\n            ]\n          }]\n      });\n      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));\n      const pField = FormField.parts.field({ factory: { sketch: renderField } });\n      return renderFormFieldWith(pLabel, pField, ['tox-form__group--stretched'], []);\n    };\n\n    const renderGrid = (spec, backstage) => ({\n      dom: {\n        tag: 'div',\n        classes: [\n          'tox-form__grid',\n          `tox-form__grid--${ spec.columns }col`\n        ]\n      },\n      components: map$2(spec.items, backstage.interpreter)\n    });\n\n    const beforeObject = generate$6('alloy-fake-before-tabstop');\n    const afterObject = generate$6('alloy-fake-after-tabstop');\n    const craftWithClasses = classes => {\n      return {\n        dom: {\n          tag: 'div',\n          styles: {\n            width: '1px',\n            height: '1px',\n            outline: 'none'\n          },\n          attributes: { tabindex: '0' },\n          classes\n        },\n        behaviours: derive$1([\n          Focusing.config({ ignore: true }),\n          Tabstopping.config({})\n        ])\n      };\n    };\n    const craft = spec => {\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-navobj']\n        },\n        components: [\n          craftWithClasses([beforeObject]),\n          spec,\n          craftWithClasses([afterObject])\n        ],\n        behaviours: derive$1([ComposingConfigs.childAt(1)])\n      };\n    };\n    const triggerTab = (placeholder, shiftKey) => {\n      emitWith(placeholder, keydown(), {\n        raw: {\n          which: 9,\n          shiftKey\n        }\n      });\n    };\n    const onFocus = (container, targetComp) => {\n      const target = targetComp.element;\n      if (has(target, beforeObject)) {\n        triggerTab(container, true);\n      } else if (has(target, afterObject)) {\n        triggerTab(container, false);\n      }\n    };\n    const isPseudoStop = element => {\n      return closest(element, [\n        '.' + beforeObject,\n        '.' + afterObject\n      ].join(','), never);\n    };\n\n    const getDynamicSource = initialData => {\n      const cachedValue = Cell(initialData.getOr(''));\n      return {\n        getValue: _frameComponent => cachedValue.get(),\n        setValue: (frameComponent, html) => {\n          if (cachedValue.get() !== html) {\n            set$9(frameComponent.element, 'srcdoc', html);\n          }\n          cachedValue.set(html);\n        }\n      };\n    };\n    const renderIFrame = (spec, providersBackstage, initialData) => {\n      const isSandbox = spec.sandboxed;\n      const isTransparent = spec.transparent;\n      const baseClass = 'tox-dialog__iframe';\n      const attributes = {\n        ...spec.label.map(title => ({ title })).getOr({}),\n        ...initialData.map(html => ({ srcdoc: html })).getOr({}),\n        ...isSandbox ? { sandbox: 'allow-scripts allow-same-origin' } : {}\n      };\n      const sourcing = getDynamicSource(initialData);\n      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));\n      const factory = newSpec => craft({\n        uid: newSpec.uid,\n        dom: {\n          tag: 'iframe',\n          attributes,\n          classes: isTransparent ? [baseClass] : [\n            baseClass,\n            `${ baseClass }--opaque`\n          ]\n        },\n        behaviours: derive$1([\n          Tabstopping.config({}),\n          Focusing.config({}),\n          RepresentingConfigs.withComp(initialData, sourcing.getValue, sourcing.setValue)\n        ])\n      });\n      const pField = FormField.parts.field({ factory: { sketch: factory } });\n      return renderFormFieldWith(pLabel, pField, ['tox-form__group--stretched'], []);\n    };\n\n    const image = image => new Promise((resolve, reject) => {\n      const loaded = () => {\n        destroy();\n        resolve(image);\n      };\n      const listeners = [\n        bind(image, 'load', loaded),\n        bind(image, 'error', () => {\n          destroy();\n          reject('Unable to load data from image: ' + image.dom.src);\n        })\n      ];\n      const destroy = () => each$1(listeners, l => l.unbind());\n      if (image.dom.complete) {\n        loaded();\n      }\n    });\n\n    const calculateImagePosition = (panelWidth, panelHeight, imageWidth, imageHeight, zoom) => {\n      const width = imageWidth * zoom;\n      const height = imageHeight * zoom;\n      const left = Math.max(0, panelWidth / 2 - width / 2);\n      const top = Math.max(0, panelHeight / 2 - height / 2);\n      return {\n        left: left.toString() + 'px',\n        top: top.toString() + 'px',\n        width: width.toString() + 'px',\n        height: height.toString() + 'px'\n      };\n    };\n    const zoomToFit = (panel, width, height) => {\n      const panelW = get$c(panel);\n      const panelH = get$d(panel);\n      return Math.min(panelW / width, panelH / height, 1);\n    };\n    const renderImagePreview = (spec, initialData) => {\n      const cachedData = Cell(initialData.getOr({ url: '' }));\n      const memImage = record({\n        dom: {\n          tag: 'img',\n          classes: ['tox-imagepreview__image'],\n          attributes: initialData.map(data => ({ src: data.url })).getOr({})\n        }\n      });\n      const memContainer = record({\n        dom: {\n          tag: 'div',\n          classes: ['tox-imagepreview__container'],\n          attributes: { role: 'presentation' }\n        },\n        components: [memImage.asSpec()]\n      });\n      const setValue = (frameComponent, data) => {\n        const translatedData = { url: data.url };\n        data.zoom.each(z => translatedData.zoom = z);\n        data.cachedWidth.each(z => translatedData.cachedWidth = z);\n        data.cachedHeight.each(z => translatedData.cachedHeight = z);\n        cachedData.set(translatedData);\n        const applyFramePositioning = () => {\n          const {cachedWidth, cachedHeight, zoom} = translatedData;\n          if (!isUndefined(cachedWidth) && !isUndefined(cachedHeight)) {\n            if (isUndefined(zoom)) {\n              const z = zoomToFit(frameComponent.element, cachedWidth, cachedHeight);\n              translatedData.zoom = z;\n            }\n            const position = calculateImagePosition(get$c(frameComponent.element), get$d(frameComponent.element), cachedWidth, cachedHeight, translatedData.zoom);\n            memContainer.getOpt(frameComponent).each(container => {\n              setAll(container.element, position);\n            });\n          }\n        };\n        memImage.getOpt(frameComponent).each(imageComponent => {\n          const img = imageComponent.element;\n          if (data.url !== get$f(img, 'src')) {\n            set$9(img, 'src', data.url);\n            remove$2(frameComponent.element, 'tox-imagepreview__loaded');\n          }\n          applyFramePositioning();\n          image(img).then(img => {\n            if (frameComponent.getSystem().isConnected()) {\n              add$2(frameComponent.element, 'tox-imagepreview__loaded');\n              translatedData.cachedWidth = img.dom.naturalWidth;\n              translatedData.cachedHeight = img.dom.naturalHeight;\n              applyFramePositioning();\n            }\n          });\n        });\n      };\n      const styles = {};\n      spec.height.each(h => styles.height = h);\n      const fakeValidatedData = initialData.map(d => ({\n        url: d.url,\n        zoom: Optional.from(d.zoom),\n        cachedWidth: Optional.from(d.cachedWidth),\n        cachedHeight: Optional.from(d.cachedHeight)\n      }));\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-imagepreview'],\n          styles,\n          attributes: { role: 'presentation' }\n        },\n        components: [memContainer.asSpec()],\n        behaviours: derive$1([\n          ComposingConfigs.self(),\n          RepresentingConfigs.withComp(fakeValidatedData, () => cachedData.get(), setValue)\n        ])\n      };\n    };\n\n    const renderLabel$1 = (spec, backstageShared) => {\n      const label = {\n        dom: {\n          tag: 'label',\n          classes: ['tox-label']\n        },\n        components: [text$2(backstageShared.providers.translate(spec.label))]\n      };\n      const comps = map$2(spec.items, backstageShared.interpreter);\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-form__group']\n        },\n        components: [\n          label,\n          ...comps\n        ],\n        behaviours: derive$1([\n          ComposingConfigs.self(),\n          Replacing.config({}),\n          RepresentingConfigs.domHtml(Optional.none()),\n          Keying.config({ mode: 'acyclic' })\n        ])\n      };\n    };\n\n    const internalToolbarButtonExecute = generate$6('toolbar.button.execute');\n    const onToolbarButtonExecute = info => runOnExecute$1((comp, _simulatedEvent) => {\n      runWithApi(info, comp)(itemApi => {\n        emitWith(comp, internalToolbarButtonExecute, { buttonApi: itemApi });\n        info.onAction(itemApi);\n      });\n    });\n    const toolbarButtonEventOrder = {\n      [execute$5()]: [\n        'disabling',\n        'alloy.base.behaviour',\n        'toggling',\n        'toolbar-button-events'\n      ]\n    };\n\n    const renderIcon = (iconName, iconsProvider, behaviours) => render$3(iconName, {\n      tag: 'span',\n      classes: [\n        'tox-icon',\n        'tox-tbtn__icon-wrap'\n      ],\n      behaviours\n    }, iconsProvider);\n    const renderIconFromPack = (iconName, iconsProvider) => renderIcon(iconName, iconsProvider, []);\n    const renderReplaceableIconFromPack = (iconName, iconsProvider) => renderIcon(iconName, iconsProvider, [Replacing.config({})]);\n    const renderLabel = (text, prefix, providersBackstage) => ({\n      dom: {\n        tag: 'span',\n        classes: [`${ prefix }__select-label`]\n      },\n      components: [text$2(providersBackstage.translate(text))],\n      behaviours: derive$1([Replacing.config({})])\n    });\n\n    const updateMenuText = generate$6('update-menu-text');\n    const updateMenuIcon = generate$6('update-menu-icon');\n    const renderCommonDropdown = (spec, prefix, sharedBackstage) => {\n      const editorOffCell = Cell(noop);\n      const optMemDisplayText = spec.text.map(text => record(renderLabel(text, prefix, sharedBackstage.providers)));\n      const optMemDisplayIcon = spec.icon.map(iconName => record(renderReplaceableIconFromPack(iconName, sharedBackstage.providers.icons)));\n      const onLeftOrRightInMenu = (comp, se) => {\n        const dropdown = Representing.getValue(comp);\n        Focusing.focus(dropdown);\n        emitWith(dropdown, 'keydown', { raw: se.event.raw });\n        Dropdown.close(dropdown);\n        return Optional.some(true);\n      };\n      const role = spec.role.fold(() => ({}), role => ({ role }));\n      const tooltipAttributes = spec.tooltip.fold(() => ({}), tooltip => {\n        const translatedTooltip = sharedBackstage.providers.translate(tooltip);\n        return {\n          'title': translatedTooltip,\n          'aria-label': translatedTooltip\n        };\n      });\n      const iconSpec = render$3('chevron-down', {\n        tag: 'div',\n        classes: [`${ prefix }__select-chevron`]\n      }, sharedBackstage.providers.icons);\n      const memDropdown = record(Dropdown.sketch({\n        ...spec.uid ? { uid: spec.uid } : {},\n        ...role,\n        dom: {\n          tag: 'button',\n          classes: [\n            prefix,\n            `${ prefix }--select`\n          ].concat(map$2(spec.classes, c => `${ prefix }--${ c }`)),\n          attributes: { ...tooltipAttributes }\n        },\n        components: componentRenderPipeline([\n          optMemDisplayIcon.map(mem => mem.asSpec()),\n          optMemDisplayText.map(mem => mem.asSpec()),\n          Optional.some(iconSpec)\n        ]),\n        matchWidth: true,\n        useMinWidth: true,\n        onOpen: (anchor, dropdownComp, tmenuComp) => {\n          if (spec.searchable) {\n            focusSearchField(tmenuComp);\n          }\n        },\n        dropdownBehaviours: derive$1([\n          ...spec.dropdownBehaviours,\n          DisablingConfigs.button(() => spec.disabled || sharedBackstage.providers.isDisabled()),\n          receivingConfig(),\n          Unselecting.config({}),\n          Replacing.config({}),\n          config('dropdown-events', [\n            onControlAttached(spec, editorOffCell),\n            onControlDetached(spec, editorOffCell)\n          ]),\n          config('menubutton-update-display-text', [\n            run$1(updateMenuText, (comp, se) => {\n              optMemDisplayText.bind(mem => mem.getOpt(comp)).each(displayText => {\n                Replacing.set(displayText, [text$2(sharedBackstage.providers.translate(se.event.text))]);\n              });\n            }),\n            run$1(updateMenuIcon, (comp, se) => {\n              optMemDisplayIcon.bind(mem => mem.getOpt(comp)).each(displayIcon => {\n                Replacing.set(displayIcon, [renderReplaceableIconFromPack(se.event.icon, sharedBackstage.providers.icons)]);\n              });\n            })\n          ])\n        ]),\n        eventOrder: deepMerge(toolbarButtonEventOrder, {\n          mousedown: [\n            'focusing',\n            'alloy.base.behaviour',\n            'item-type-events',\n            'normal-dropdown-events'\n          ]\n        }),\n        sandboxBehaviours: derive$1([\n          Keying.config({\n            mode: 'special',\n            onLeft: onLeftOrRightInMenu,\n            onRight: onLeftOrRightInMenu\n          }),\n          config('dropdown-sandbox-events', [\n            run$1(refetchTriggerEvent, (originalSandboxComp, se) => {\n              handleRefetchTrigger(originalSandboxComp);\n              se.stop();\n            }),\n            run$1(redirectMenuItemInteractionEvent, (sandboxComp, se) => {\n              handleRedirectToMenuItem(sandboxComp, se);\n              se.stop();\n            })\n          ])\n        ]),\n        lazySink: sharedBackstage.getSink,\n        toggleClass: `${ prefix }--active`,\n        parts: {\n          menu: {\n            ...part(false, spec.columns, spec.presets),\n            fakeFocus: spec.searchable,\n            onHighlightItem: updateAriaOnHighlight,\n            onCollapseMenu: (tmenuComp, itemCompCausingCollapse, nowActiveMenuComp) => {\n              Highlighting.getHighlighted(nowActiveMenuComp).each(itemComp => {\n                updateAriaOnHighlight(tmenuComp, nowActiveMenuComp, itemComp);\n              });\n            },\n            onDehighlightItem: updateAriaOnDehighlight\n          }\n        },\n        fetch: comp => Future.nu(curry(spec.fetch, comp))\n      }));\n      return memDropdown.asSpec();\n    };\n\n    const isMenuItemReference = item => isString(item);\n    const isSeparator$2 = item => item.type === 'separator';\n    const isExpandingMenuItem = item => has$2(item, 'getSubmenuItems');\n    const separator$2 = { type: 'separator' };\n    const unwrapReferences = (items, menuItems) => {\n      const realItems = foldl(items, (acc, item) => {\n        if (isMenuItemReference(item)) {\n          if (item === '') {\n            return acc;\n          } else if (item === '|') {\n            return acc.length > 0 && !isSeparator$2(acc[acc.length - 1]) ? acc.concat([separator$2]) : acc;\n          } else if (has$2(menuItems, item.toLowerCase())) {\n            return acc.concat([menuItems[item.toLowerCase()]]);\n          } else {\n            return acc;\n          }\n        } else {\n          return acc.concat([item]);\n        }\n      }, []);\n      if (realItems.length > 0 && isSeparator$2(realItems[realItems.length - 1])) {\n        realItems.pop();\n      }\n      return realItems;\n    };\n    const getFromExpandingItem = (item, menuItems) => {\n      const submenuItems = item.getSubmenuItems();\n      const rest = expand(submenuItems, menuItems);\n      const newMenus = deepMerge(rest.menus, { [item.value]: rest.items });\n      const newExpansions = deepMerge(rest.expansions, { [item.value]: item.value });\n      return {\n        item,\n        menus: newMenus,\n        expansions: newExpansions\n      };\n    };\n    const generateValueIfRequired = item => {\n      const itemValue = get$g(item, 'value').getOrThunk(() => generate$6('generated-menu-item'));\n      return deepMerge({ value: itemValue }, item);\n    };\n    const expand = (items, menuItems) => {\n      const realItems = unwrapReferences(isString(items) ? items.split(' ') : items, menuItems);\n      return foldr(realItems, (acc, item) => {\n        if (isExpandingMenuItem(item)) {\n          const itemWithValue = generateValueIfRequired(item);\n          const newData = getFromExpandingItem(itemWithValue, menuItems);\n          return {\n            menus: deepMerge(acc.menus, newData.menus),\n            items: [\n              newData.item,\n              ...acc.items\n            ],\n            expansions: deepMerge(acc.expansions, newData.expansions)\n          };\n        } else {\n          return {\n            ...acc,\n            items: [\n              item,\n              ...acc.items\n            ]\n          };\n        }\n      }, {\n        menus: {},\n        expansions: {},\n        items: []\n      });\n    };\n\n    const getSearchModeForField = settings => {\n      return settings.search.fold(() => ({ searchMode: 'no-search' }), searchSettings => ({\n        searchMode: 'search-with-field',\n        placeholder: searchSettings.placeholder\n      }));\n    };\n    const getSearchModeForResults = settings => {\n      return settings.search.fold(() => ({ searchMode: 'no-search' }), _ => ({ searchMode: 'search-with-results' }));\n    };\n    const build = (items, itemResponse, backstage, settings) => {\n      const primary = generate$6('primary-menu');\n      const data = expand(items, backstage.shared.providers.menuItems());\n      if (data.items.length === 0) {\n        return Optional.none();\n      }\n      const mainMenuSearchMode = getSearchModeForField(settings);\n      const mainMenu = createPartialMenu(primary, data.items, itemResponse, backstage, settings.isHorizontalMenu, mainMenuSearchMode);\n      const submenuSearchMode = getSearchModeForResults(settings);\n      const submenus = map$1(data.menus, (menuItems, menuName) => createPartialMenu(menuName, menuItems, itemResponse, backstage, false, submenuSearchMode));\n      const menus = deepMerge(submenus, wrap$1(primary, mainMenu));\n      return Optional.from(tieredMenu.tieredData(primary, menus, data.expansions));\n    };\n\n    const isSingleListItem = item => !has$2(item, 'items');\n    const dataAttribute = 'data-value';\n    const fetchItems = (dropdownComp, name, items, selectedValue) => map$2(items, item => {\n      if (!isSingleListItem(item)) {\n        return {\n          type: 'nestedmenuitem',\n          text: item.text,\n          getSubmenuItems: () => fetchItems(dropdownComp, name, item.items, selectedValue)\n        };\n      } else {\n        return {\n          type: 'togglemenuitem',\n          text: item.text,\n          value: item.value,\n          active: item.value === selectedValue,\n          onAction: () => {\n            Representing.setValue(dropdownComp, item.value);\n            emitWith(dropdownComp, formChangeEvent, { name });\n            Focusing.focus(dropdownComp);\n          }\n        };\n      }\n    });\n    const findItemByValue = (items, value) => findMap(items, item => {\n      if (!isSingleListItem(item)) {\n        return findItemByValue(item.items, value);\n      } else {\n        return someIf(item.value === value, item);\n      }\n    });\n    const renderListBox = (spec, backstage, initialData) => {\n      const providersBackstage = backstage.shared.providers;\n      const initialItem = initialData.bind(value => findItemByValue(spec.items, value)).orThunk(() => head(spec.items).filter(isSingleListItem));\n      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));\n      const pField = FormField.parts.field({\n        dom: {},\n        factory: {\n          sketch: sketchSpec => renderCommonDropdown({\n            uid: sketchSpec.uid,\n            text: initialItem.map(item => item.text),\n            icon: Optional.none(),\n            tooltip: spec.label,\n            role: Optional.none(),\n            fetch: (comp, callback) => {\n              const items = fetchItems(comp, spec.name, spec.items, Representing.getValue(comp));\n              callback(build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {\n                isHorizontalMenu: false,\n                search: Optional.none()\n              }));\n            },\n            onSetup: constant$1(noop),\n            getApi: constant$1({}),\n            columns: 1,\n            presets: 'normal',\n            classes: [],\n            dropdownBehaviours: [\n              Tabstopping.config({}),\n              RepresentingConfigs.withComp(initialItem.map(item => item.value), comp => get$f(comp.element, dataAttribute), (comp, data) => {\n                findItemByValue(spec.items, data).each(item => {\n                  set$9(comp.element, dataAttribute, item.value);\n                  emitWith(comp, updateMenuText, { text: item.text });\n                });\n              })\n            ]\n          }, 'tox-listbox', backstage.shared)\n        }\n      });\n      const listBoxWrap = {\n        dom: {\n          tag: 'div',\n          classes: ['tox-listboxfield']\n        },\n        components: [pField]\n      };\n      return FormField.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-form__group']\n        },\n        components: flatten([\n          pLabel.toArray(),\n          [listBoxWrap]\n        ]),\n        fieldBehaviours: derive$1([Disabling.config({\n            disabled: constant$1(!spec.enabled),\n            onDisabled: comp => {\n              FormField.getField(comp).each(Disabling.disable);\n            },\n            onEnabled: comp => {\n              FormField.getField(comp).each(Disabling.enable);\n            }\n          })])\n      });\n    };\n\n    const renderPanel = (spec, backstage) => ({\n      dom: {\n        tag: 'div',\n        classes: spec.classes\n      },\n      components: map$2(spec.items, backstage.shared.interpreter)\n    });\n\n    const factory$h = (detail, _spec) => {\n      const options = map$2(detail.options, option => ({\n        dom: {\n          tag: 'option',\n          value: option.value,\n          innerHtml: option.text\n        }\n      }));\n      const initialValues = detail.data.map(v => wrap$1('initialValue', v)).getOr({});\n      return {\n        uid: detail.uid,\n        dom: {\n          tag: 'select',\n          classes: detail.selectClasses,\n          attributes: detail.selectAttributes\n        },\n        components: options,\n        behaviours: augment(detail.selectBehaviours, [\n          Focusing.config({}),\n          Representing.config({\n            store: {\n              mode: 'manual',\n              getValue: select => {\n                return get$6(select.element);\n              },\n              setValue: (select, newValue) => {\n                const found = find$5(detail.options, opt => opt.value === newValue);\n                if (found.isSome()) {\n                  set$5(select.element, newValue);\n                }\n              },\n              ...initialValues\n            }\n          })\n        ])\n      };\n    };\n    const HtmlSelect = single({\n      name: 'HtmlSelect',\n      configFields: [\n        required$1('options'),\n        field('selectBehaviours', [\n          Focusing,\n          Representing\n        ]),\n        defaulted('selectClasses', []),\n        defaulted('selectAttributes', {}),\n        option$3('data')\n      ],\n      factory: factory$h\n    });\n\n    const renderSelectBox = (spec, providersBackstage, initialData) => {\n      const translatedOptions = map$2(spec.items, item => ({\n        text: providersBackstage.translate(item.text),\n        value: item.value\n      }));\n      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));\n      const pField = FormField.parts.field({\n        dom: {},\n        ...initialData.map(data => ({ data })).getOr({}),\n        selectAttributes: { size: spec.size },\n        options: translatedOptions,\n        factory: HtmlSelect,\n        selectBehaviours: derive$1([\n          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),\n          Tabstopping.config({}),\n          config('selectbox-change', [run$1(change(), (component, _) => {\n              emitWith(component, formChangeEvent, { name: spec.name });\n            })])\n        ])\n      });\n      const chevron = spec.size > 1 ? Optional.none() : Optional.some(render$3('chevron-down', {\n        tag: 'div',\n        classes: ['tox-selectfield__icon-js']\n      }, providersBackstage.icons));\n      const selectWrap = {\n        dom: {\n          tag: 'div',\n          classes: ['tox-selectfield']\n        },\n        components: flatten([\n          [pField],\n          chevron.toArray()\n        ])\n      };\n      return FormField.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-form__group']\n        },\n        components: flatten([\n          pLabel.toArray(),\n          [selectWrap]\n        ]),\n        fieldBehaviours: derive$1([\n          Disabling.config({\n            disabled: () => !spec.enabled || providersBackstage.isDisabled(),\n            onDisabled: comp => {\n              FormField.getField(comp).each(Disabling.disable);\n            },\n            onEnabled: comp => {\n              FormField.getField(comp).each(Disabling.enable);\n            }\n          }),\n          receivingConfig()\n        ])\n      });\n    };\n\n    const schema$h = constant$1([\n      defaulted('field1Name', 'field1'),\n      defaulted('field2Name', 'field2'),\n      onStrictHandler('onLockedChange'),\n      markers$1(['lockClass']),\n      defaulted('locked', false),\n      SketchBehaviours.field('coupledFieldBehaviours', [\n        Composing,\n        Representing\n      ])\n    ]);\n    const getField = (comp, detail, partName) => getPart(comp, detail, partName).bind(Composing.getCurrent);\n    const coupledPart = (selfName, otherName) => required({\n      factory: FormField,\n      name: selfName,\n      overrides: detail => {\n        return {\n          fieldBehaviours: derive$1([config('coupled-input-behaviour', [run$1(input(), me => {\n                getField(me, detail, otherName).each(other => {\n                  getPart(me, detail, 'lock').each(lock => {\n                    if (Toggling.isOn(lock)) {\n                      detail.onLockedChange(me, other, lock);\n                    }\n                  });\n                });\n              })])])\n        };\n      }\n    });\n    const parts$c = constant$1([\n      coupledPart('field1', 'field2'),\n      coupledPart('field2', 'field1'),\n      required({\n        factory: Button,\n        schema: [required$1('dom')],\n        name: 'lock',\n        overrides: detail => {\n          return {\n            buttonBehaviours: derive$1([Toggling.config({\n                selected: detail.locked,\n                toggleClass: detail.markers.lockClass,\n                aria: { mode: 'pressed' }\n              })])\n          };\n        }\n      })\n    ]);\n\n    const factory$g = (detail, components, _spec, _externals) => ({\n      uid: detail.uid,\n      dom: detail.dom,\n      components,\n      behaviours: SketchBehaviours.augment(detail.coupledFieldBehaviours, [\n        Composing.config({ find: Optional.some }),\n        Representing.config({\n          store: {\n            mode: 'manual',\n            getValue: comp => {\n              const parts = getPartsOrDie(comp, detail, [\n                'field1',\n                'field2'\n              ]);\n              return {\n                [detail.field1Name]: Representing.getValue(parts.field1()),\n                [detail.field2Name]: Representing.getValue(parts.field2())\n              };\n            },\n            setValue: (comp, value) => {\n              const parts = getPartsOrDie(comp, detail, [\n                'field1',\n                'field2'\n              ]);\n              if (hasNonNullableKey(value, detail.field1Name)) {\n                Representing.setValue(parts.field1(), value[detail.field1Name]);\n              }\n              if (hasNonNullableKey(value, detail.field2Name)) {\n                Representing.setValue(parts.field2(), value[detail.field2Name]);\n              }\n            }\n          }\n        })\n      ]),\n      apis: {\n        getField1: component => getPart(component, detail, 'field1'),\n        getField2: component => getPart(component, detail, 'field2'),\n        getLock: component => getPart(component, detail, 'lock')\n      }\n    });\n    const FormCoupledInputs = composite({\n      name: 'FormCoupledInputs',\n      configFields: schema$h(),\n      partFields: parts$c(),\n      factory: factory$g,\n      apis: {\n        getField1: (apis, component) => apis.getField1(component),\n        getField2: (apis, component) => apis.getField2(component),\n        getLock: (apis, component) => apis.getLock(component)\n      }\n    });\n\n    const formatSize = size => {\n      const unitDec = {\n        '': 0,\n        'px': 0,\n        'pt': 1,\n        'mm': 1,\n        'pc': 2,\n        'ex': 2,\n        'em': 2,\n        'ch': 2,\n        'rem': 2,\n        'cm': 3,\n        'in': 4,\n        '%': 4\n      };\n      const maxDecimal = unit => unit in unitDec ? unitDec[unit] : 1;\n      let numText = size.value.toFixed(maxDecimal(size.unit));\n      if (numText.indexOf('.') !== -1) {\n        numText = numText.replace(/\\.?0*$/, '');\n      }\n      return numText + size.unit;\n    };\n    const parseSize = sizeText => {\n      const numPattern = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|%)\\s*$/;\n      const match = numPattern.exec(sizeText);\n      if (match !== null) {\n        const value = parseFloat(match[1]);\n        const unit = match[2];\n        return Result.value({\n          value,\n          unit\n        });\n      } else {\n        return Result.error(sizeText);\n      }\n    };\n    const convertUnit = (size, unit) => {\n      const inInch = {\n        '': 96,\n        'px': 96,\n        'pt': 72,\n        'cm': 2.54,\n        'pc': 12,\n        'mm': 25.4,\n        'in': 1\n      };\n      const supported = u => has$2(inInch, u);\n      if (size.unit === unit) {\n        return Optional.some(size.value);\n      } else if (supported(size.unit) && supported(unit)) {\n        if (inInch[size.unit] === inInch[unit]) {\n          return Optional.some(size.value);\n        } else {\n          return Optional.some(size.value / inInch[size.unit] * inInch[unit]);\n        }\n      } else {\n        return Optional.none();\n      }\n    };\n    const noSizeConversion = _input => Optional.none();\n    const ratioSizeConversion = (scale, unit) => size => convertUnit(size, unit).map(value => ({\n      value: value * scale,\n      unit\n    }));\n    const makeRatioConverter = (currentFieldText, otherFieldText) => {\n      const cValue = parseSize(currentFieldText).toOptional();\n      const oValue = parseSize(otherFieldText).toOptional();\n      return lift2(cValue, oValue, (cSize, oSize) => convertUnit(cSize, oSize.unit).map(val => oSize.value / val).map(r => ratioSizeConversion(r, oSize.unit)).getOr(noSizeConversion)).getOr(noSizeConversion);\n    };\n\n    const renderSizeInput = (spec, providersBackstage) => {\n      let converter = noSizeConversion;\n      const ratioEvent = generate$6('ratio-event');\n      const makeIcon = iconName => render$3(iconName, {\n        tag: 'span',\n        classes: [\n          'tox-icon',\n          'tox-lock-icon__' + iconName\n        ]\n      }, providersBackstage.icons);\n      const pLock = FormCoupledInputs.parts.lock({\n        dom: {\n          tag: 'button',\n          classes: [\n            'tox-lock',\n            'tox-button',\n            'tox-button--naked',\n            'tox-button--icon'\n          ],\n          attributes: { title: providersBackstage.translate(spec.label.getOr('Constrain proportions')) }\n        },\n        components: [\n          makeIcon('lock'),\n          makeIcon('unlock')\n        ],\n        buttonBehaviours: derive$1([\n          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),\n          receivingConfig(),\n          Tabstopping.config({})\n        ])\n      });\n      const formGroup = components => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-form__group']\n        },\n        components\n      });\n      const getFieldPart = isField1 => FormField.parts.field({\n        factory: Input,\n        inputClasses: ['tox-textfield'],\n        inputBehaviours: derive$1([\n          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),\n          receivingConfig(),\n          Tabstopping.config({}),\n          config('size-input-events', [\n            run$1(focusin(), (component, _simulatedEvent) => {\n              emitWith(component, ratioEvent, { isField1 });\n            }),\n            run$1(change(), (component, _simulatedEvent) => {\n              emitWith(component, formChangeEvent, { name: spec.name });\n            })\n          ])\n        ]),\n        selectOnFocus: false\n      });\n      const getLabel = label => ({\n        dom: {\n          tag: 'label',\n          classes: ['tox-label']\n        },\n        components: [text$2(providersBackstage.translate(label))]\n      });\n      const widthField = FormCoupledInputs.parts.field1(formGroup([\n        FormField.parts.label(getLabel('Width')),\n        getFieldPart(true)\n      ]));\n      const heightField = FormCoupledInputs.parts.field2(formGroup([\n        FormField.parts.label(getLabel('Height')),\n        getFieldPart(false)\n      ]));\n      return FormCoupledInputs.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-form__group']\n        },\n        components: [{\n            dom: {\n              tag: 'div',\n              classes: ['tox-form__controls-h-stack']\n            },\n            components: [\n              widthField,\n              heightField,\n              formGroup([\n                getLabel(nbsp),\n                pLock\n              ])\n            ]\n          }],\n        field1Name: 'width',\n        field2Name: 'height',\n        locked: true,\n        markers: { lockClass: 'tox-locked' },\n        onLockedChange: (current, other, _lock) => {\n          parseSize(Representing.getValue(current)).each(size => {\n            converter(size).each(newSize => {\n              Representing.setValue(other, formatSize(newSize));\n            });\n          });\n        },\n        coupledFieldBehaviours: derive$1([\n          Disabling.config({\n            disabled: () => !spec.enabled || providersBackstage.isDisabled(),\n            onDisabled: comp => {\n              FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.disable);\n              FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.disable);\n              FormCoupledInputs.getLock(comp).each(Disabling.disable);\n            },\n            onEnabled: comp => {\n              FormCoupledInputs.getField1(comp).bind(FormField.getField).each(Disabling.enable);\n              FormCoupledInputs.getField2(comp).bind(FormField.getField).each(Disabling.enable);\n              FormCoupledInputs.getLock(comp).each(Disabling.enable);\n            }\n          }),\n          receivingConfig(),\n          config('size-input-events2', [run$1(ratioEvent, (component, simulatedEvent) => {\n              const isField1 = simulatedEvent.event.isField1;\n              const optCurrent = isField1 ? FormCoupledInputs.getField1(component) : FormCoupledInputs.getField2(component);\n              const optOther = isField1 ? FormCoupledInputs.getField2(component) : FormCoupledInputs.getField1(component);\n              const value1 = optCurrent.map(Representing.getValue).getOr('');\n              const value2 = optOther.map(Representing.getValue).getOr('');\n              converter = makeRatioConverter(value1, value2);\n            })])\n        ])\n      });\n    };\n\n    const renderSlider = (spec, providerBackstage, initialData) => {\n      const labelPart = Slider.parts.label({\n        dom: {\n          tag: 'label',\n          classes: ['tox-label']\n        },\n        components: [text$2(providerBackstage.translate(spec.label))]\n      });\n      const spectrum = Slider.parts.spectrum({\n        dom: {\n          tag: 'div',\n          classes: ['tox-slider__rail'],\n          attributes: { role: 'presentation' }\n        }\n      });\n      const thumb = Slider.parts.thumb({\n        dom: {\n          tag: 'div',\n          classes: ['tox-slider__handle'],\n          attributes: { role: 'presentation' }\n        }\n      });\n      return Slider.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-slider'],\n          attributes: { role: 'presentation' }\n        },\n        model: {\n          mode: 'x',\n          minX: spec.min,\n          maxX: spec.max,\n          getInitialValue: constant$1(initialData.getOrThunk(() => (Math.abs(spec.max) - Math.abs(spec.min)) / 2))\n        },\n        components: [\n          labelPart,\n          spectrum,\n          thumb\n        ],\n        sliderBehaviours: derive$1([\n          ComposingConfigs.self(),\n          Focusing.config({})\n        ]),\n        onChoose: (component, thumb, value) => {\n          emitWith(component, formChangeEvent, {\n            name: spec.name,\n            value\n          });\n        }\n      });\n    };\n\n    const renderTable = (spec, providersBackstage) => {\n      const renderTh = text => ({\n        dom: {\n          tag: 'th',\n          innerHtml: providersBackstage.translate(text)\n        }\n      });\n      const renderHeader = header => ({\n        dom: { tag: 'thead' },\n        components: [{\n            dom: { tag: 'tr' },\n            components: map$2(header, renderTh)\n          }]\n      });\n      const renderTd = text => ({\n        dom: {\n          tag: 'td',\n          innerHtml: providersBackstage.translate(text)\n        }\n      });\n      const renderTr = row => ({\n        dom: { tag: 'tr' },\n        components: map$2(row, renderTd)\n      });\n      const renderRows = rows => ({\n        dom: { tag: 'tbody' },\n        components: map$2(rows, renderTr)\n      });\n      return {\n        dom: {\n          tag: 'table',\n          classes: ['tox-dialog__table']\n        },\n        components: [\n          renderHeader(spec.header),\n          renderRows(spec.cells)\n        ],\n        behaviours: derive$1([\n          Tabstopping.config({}),\n          Focusing.config({})\n        ])\n      };\n    };\n\n    const renderTextField = (spec, providersBackstage) => {\n      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));\n      const baseInputBehaviours = [\n        Disabling.config({ disabled: () => spec.disabled || providersBackstage.isDisabled() }),\n        receivingConfig(),\n        Keying.config({\n          mode: 'execution',\n          useEnter: spec.multiline !== true,\n          useControlEnter: spec.multiline === true,\n          execute: comp => {\n            emit(comp, formSubmitEvent);\n            return Optional.some(true);\n          }\n        }),\n        config('textfield-change', [\n          run$1(input(), (component, _) => {\n            emitWith(component, formChangeEvent, { name: spec.name });\n          }),\n          run$1(postPaste(), (component, _) => {\n            emitWith(component, formChangeEvent, { name: spec.name });\n          })\n        ]),\n        Tabstopping.config({})\n      ];\n      const validatingBehaviours = spec.validation.map(vl => Invalidating.config({\n        getRoot: input => {\n          return parentElement(input.element);\n        },\n        invalidClass: 'tox-invalid',\n        validator: {\n          validate: input => {\n            const v = Representing.getValue(input);\n            const result = vl.validator(v);\n            return Future.pure(result === true ? Result.value(v) : Result.error(result));\n          },\n          validateOnLoad: vl.validateOnLoad\n        }\n      })).toArray();\n      const placeholder = spec.placeholder.fold(constant$1({}), p => ({ placeholder: providersBackstage.translate(p) }));\n      const inputMode = spec.inputMode.fold(constant$1({}), mode => ({ inputmode: mode }));\n      const inputAttributes = {\n        ...placeholder,\n        ...inputMode\n      };\n      const pField = FormField.parts.field({\n        tag: spec.multiline === true ? 'textarea' : 'input',\n        ...spec.data.map(data => ({ data })).getOr({}),\n        inputAttributes,\n        inputClasses: [spec.classname],\n        inputBehaviours: derive$1(flatten([\n          baseInputBehaviours,\n          validatingBehaviours\n        ])),\n        selectOnFocus: false,\n        factory: Input\n      });\n      const extraClasses = spec.flex ? ['tox-form__group--stretched'] : [];\n      const extraClasses2 = extraClasses.concat(spec.maximized ? ['tox-form-group--maximize'] : []);\n      const extraBehaviours = [\n        Disabling.config({\n          disabled: () => spec.disabled || providersBackstage.isDisabled(),\n          onDisabled: comp => {\n            FormField.getField(comp).each(Disabling.disable);\n          },\n          onEnabled: comp => {\n            FormField.getField(comp).each(Disabling.enable);\n          }\n        }),\n        receivingConfig()\n      ];\n      return renderFormFieldWith(pLabel, pField, extraClasses2, extraBehaviours);\n    };\n    const renderInput = (spec, providersBackstage, initialData) => renderTextField({\n      name: spec.name,\n      multiline: false,\n      label: spec.label,\n      inputMode: spec.inputMode,\n      placeholder: spec.placeholder,\n      flex: false,\n      disabled: !spec.enabled,\n      classname: 'tox-textfield',\n      validation: Optional.none(),\n      maximized: spec.maximized,\n      data: initialData\n    }, providersBackstage);\n    const renderTextarea = (spec, providersBackstage, initialData) => renderTextField({\n      name: spec.name,\n      multiline: true,\n      label: spec.label,\n      inputMode: Optional.none(),\n      placeholder: spec.placeholder,\n      flex: true,\n      disabled: !spec.enabled,\n      classname: 'tox-textarea',\n      validation: Optional.none(),\n      maximized: spec.maximized,\n      data: initialData\n    }, providersBackstage);\n\n    const events$6 = (streamConfig, streamState) => {\n      const streams = streamConfig.stream.streams;\n      const processor = streams.setup(streamConfig, streamState);\n      return derive$2([\n        run$1(streamConfig.event, processor),\n        runOnDetached(() => streamState.cancel())\n      ].concat(streamConfig.cancelEvent.map(e => [run$1(e, () => streamState.cancel())]).getOr([])));\n    };\n\n    var ActiveStreaming = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$6\n    });\n\n    const first = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        if (isNull(timer)) {\n          timer = setTimeout(() => {\n            timer = null;\n            fn.apply(null, args);\n          }, rate);\n        }\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n    const last = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        cancel();\n        timer = setTimeout(() => {\n          timer = null;\n          fn.apply(null, args);\n        }, rate);\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    const throttle = _config => {\n      const state = Cell(null);\n      const readState = () => ({ timer: state.get() !== null ? 'set' : 'unset' });\n      const setTimer = t => {\n        state.set(t);\n      };\n      const cancel = () => {\n        const t = state.get();\n        if (t !== null) {\n          t.cancel();\n        }\n      };\n      return nu$8({\n        readState,\n        setTimer,\n        cancel\n      });\n    };\n    const init$9 = spec => spec.stream.streams.state(spec);\n\n    var StreamingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        throttle: throttle,\n        init: init$9\n    });\n\n    const setup$c = (streamInfo, streamState) => {\n      const sInfo = streamInfo.stream;\n      const throttler = last(streamInfo.onStream, sInfo.delay);\n      streamState.setTimer(throttler);\n      return (component, simulatedEvent) => {\n        throttler.throttle(component, simulatedEvent);\n        if (sInfo.stopEvent) {\n          simulatedEvent.stop();\n        }\n      };\n    };\n    var StreamingSchema = [\n      requiredOf('stream', choose$1('mode', {\n        throttle: [\n          required$1('delay'),\n          defaulted('stopEvent', true),\n          output$1('streams', {\n            setup: setup$c,\n            state: throttle\n          })\n        ]\n      })),\n      defaulted('event', 'input'),\n      option$3('cancelEvent'),\n      onStrictHandler('onStream')\n    ];\n\n    const Streaming = create$4({\n      fields: StreamingSchema,\n      name: 'streaming',\n      active: ActiveStreaming,\n      state: StreamingState\n    });\n\n    const setValueFromItem = (model, input, item) => {\n      const itemData = Representing.getValue(item);\n      Representing.setValue(input, itemData);\n      setCursorAtEnd(input);\n    };\n    const setSelectionOn = (input, f) => {\n      const el = input.element;\n      const value = get$6(el);\n      const node = el.dom;\n      if (get$f(el, 'type') !== 'number') {\n        f(node, value);\n      }\n    };\n    const setCursorAtEnd = input => {\n      setSelectionOn(input, (node, value) => node.setSelectionRange(value.length, value.length));\n    };\n    const setSelectionToEnd = (input, startOffset) => {\n      setSelectionOn(input, (node, value) => node.setSelectionRange(startOffset, value.length));\n    };\n    const attemptSelectOver = (model, input, item) => {\n      if (!model.selectsOver) {\n        return Optional.none();\n      } else {\n        const currentValue = Representing.getValue(input);\n        const inputDisplay = model.getDisplayText(currentValue);\n        const itemValue = Representing.getValue(item);\n        const itemDisplay = model.getDisplayText(itemValue);\n        return itemDisplay.indexOf(inputDisplay) === 0 ? Optional.some(() => {\n          setValueFromItem(model, input, item);\n          setSelectionToEnd(input, inputDisplay.length);\n        }) : Optional.none();\n      }\n    };\n\n    const itemExecute = constant$1('alloy.typeahead.itemexecute');\n\n    const make$3 = (detail, components, spec, externals) => {\n      const navigateList = (comp, simulatedEvent, highlighter) => {\n        detail.previewing.set(false);\n        const sandbox = Coupling.getCoupled(comp, 'sandbox');\n        if (Sandboxing.isOpen(sandbox)) {\n          Composing.getCurrent(sandbox).each(menu => {\n            Highlighting.getHighlighted(menu).fold(() => {\n              highlighter(menu);\n            }, () => {\n              dispatchEvent(sandbox, menu.element, 'keydown', simulatedEvent);\n            });\n          });\n        } else {\n          const onOpenSync = sandbox => {\n            Composing.getCurrent(sandbox).each(highlighter);\n          };\n          open(detail, mapFetch(comp), comp, sandbox, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);\n        }\n      };\n      const focusBehaviours$1 = focusBehaviours(detail);\n      const mapFetch = comp => tdata => tdata.map(data => {\n        const menus = values(data.menus);\n        const items = bind$3(menus, menu => filter$2(menu.items, item => item.type === 'item'));\n        const repState = Representing.getState(comp);\n        repState.update(map$2(items, item => item.data));\n        return data;\n      });\n      const getActiveMenu = sandboxComp => Composing.getCurrent(sandboxComp);\n      const typeaheadCustomEvents = 'typeaheadevents';\n      const behaviours = [\n        Focusing.config({}),\n        Representing.config({\n          onSetValue: detail.onSetValue,\n          store: {\n            mode: 'dataset',\n            getDataKey: comp => get$6(comp.element),\n            getFallbackEntry: itemString => ({\n              value: itemString,\n              meta: {}\n            }),\n            setValue: (comp, data) => {\n              set$5(comp.element, detail.model.getDisplayText(data));\n            },\n            ...detail.initialData.map(d => wrap$1('initialValue', d)).getOr({})\n          }\n        }),\n        Streaming.config({\n          stream: {\n            mode: 'throttle',\n            delay: detail.responseTime,\n            stopEvent: false\n          },\n          onStream: (component, _simulatedEvent) => {\n            const sandbox = Coupling.getCoupled(component, 'sandbox');\n            const focusInInput = Focusing.isFocused(component);\n            if (focusInInput) {\n              if (get$6(component.element).length >= detail.minChars) {\n                const previousValue = getActiveMenu(sandbox).bind(activeMenu => Highlighting.getHighlighted(activeMenu).map(Representing.getValue));\n                detail.previewing.set(true);\n                const onOpenSync = _sandbox => {\n                  getActiveMenu(sandbox).each(activeMenu => {\n                    previousValue.fold(() => {\n                      if (detail.model.selectsOver) {\n                        Highlighting.highlightFirst(activeMenu);\n                      }\n                    }, pv => {\n                      Highlighting.highlightBy(activeMenu, item => {\n                        const itemData = Representing.getValue(item);\n                        return itemData.value === pv.value;\n                      });\n                      Highlighting.getHighlighted(activeMenu).orThunk(() => {\n                        Highlighting.highlightFirst(activeMenu);\n                        return Optional.none();\n                      });\n                    });\n                  });\n                };\n                open(detail, mapFetch(component), component, sandbox, externals, onOpenSync, HighlightOnOpen.HighlightJustMenu).get(noop);\n              }\n            }\n          },\n          cancelEvent: typeaheadCancel()\n        }),\n        Keying.config({\n          mode: 'special',\n          onDown: (comp, simulatedEvent) => {\n            navigateList(comp, simulatedEvent, Highlighting.highlightFirst);\n            return Optional.some(true);\n          },\n          onEscape: comp => {\n            const sandbox = Coupling.getCoupled(comp, 'sandbox');\n            if (Sandboxing.isOpen(sandbox)) {\n              Sandboxing.close(sandbox);\n              return Optional.some(true);\n            }\n            return Optional.none();\n          },\n          onUp: (comp, simulatedEvent) => {\n            navigateList(comp, simulatedEvent, Highlighting.highlightLast);\n            return Optional.some(true);\n          },\n          onEnter: comp => {\n            const sandbox = Coupling.getCoupled(comp, 'sandbox');\n            const sandboxIsOpen = Sandboxing.isOpen(sandbox);\n            if (sandboxIsOpen && !detail.previewing.get()) {\n              return getActiveMenu(sandbox).bind(activeMenu => Highlighting.getHighlighted(activeMenu)).map(item => {\n                emitWith(comp, itemExecute(), { item });\n                return true;\n              });\n            } else {\n              const currentValue = Representing.getValue(comp);\n              emit(comp, typeaheadCancel());\n              detail.onExecute(sandbox, comp, currentValue);\n              if (sandboxIsOpen) {\n                Sandboxing.close(sandbox);\n              }\n              return Optional.some(true);\n            }\n          }\n        }),\n        Toggling.config({\n          toggleClass: detail.markers.openClass,\n          aria: { mode: 'expanded' }\n        }),\n        Coupling.config({\n          others: {\n            sandbox: hotspot => {\n              return makeSandbox$1(detail, hotspot, {\n                onOpen: () => Toggling.on(hotspot),\n                onClose: () => Toggling.off(hotspot)\n              });\n            }\n          }\n        }),\n        config(typeaheadCustomEvents, [\n          runOnAttached(typeaheadComp => {\n            detail.lazyTypeaheadComp.set(Optional.some(typeaheadComp));\n          }),\n          runOnDetached(_typeaheadComp => {\n            detail.lazyTypeaheadComp.set(Optional.none());\n          }),\n          runOnExecute$1(comp => {\n            const onOpenSync = noop;\n            togglePopup(detail, mapFetch(comp), comp, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);\n          }),\n          run$1(itemExecute(), (comp, se) => {\n            const sandbox = Coupling.getCoupled(comp, 'sandbox');\n            setValueFromItem(detail.model, comp, se.event.item);\n            emit(comp, typeaheadCancel());\n            detail.onItemExecute(comp, sandbox, se.event.item, Representing.getValue(comp));\n            Sandboxing.close(sandbox);\n            setCursorAtEnd(comp);\n          })\n        ].concat(detail.dismissOnBlur ? [run$1(postBlur(), typeahead => {\n            const sandbox = Coupling.getCoupled(typeahead, 'sandbox');\n            if (search(sandbox.element).isNone()) {\n              Sandboxing.close(sandbox);\n            }\n          })] : []))\n      ];\n      const eventOrder = {\n        [detachedFromDom()]: [\n          Representing.name(),\n          Streaming.name(),\n          typeaheadCustomEvents\n        ],\n        ...detail.eventOrder\n      };\n      return {\n        uid: detail.uid,\n        dom: dom(deepMerge(detail, {\n          inputAttributes: {\n            'role': 'combobox',\n            'aria-autocomplete': 'list',\n            'aria-haspopup': 'true'\n          }\n        })),\n        behaviours: {\n          ...focusBehaviours$1,\n          ...augment(detail.typeaheadBehaviours, behaviours)\n        },\n        eventOrder\n      };\n    };\n\n    const schema$g = constant$1([\n      option$3('lazySink'),\n      required$1('fetch'),\n      defaulted('minChars', 5),\n      defaulted('responseTime', 1000),\n      onHandler('onOpen'),\n      defaulted('getHotspot', Optional.some),\n      defaulted('getAnchorOverrides', constant$1({})),\n      defaulted('layouts', Optional.none()),\n      defaulted('eventOrder', {}),\n      defaultedObjOf('model', {}, [\n        defaulted('getDisplayText', itemData => itemData.meta !== undefined && itemData.meta.text !== undefined ? itemData.meta.text : itemData.value),\n        defaulted('selectsOver', true),\n        defaulted('populateFromBrowse', true)\n      ]),\n      onHandler('onSetValue'),\n      onKeyboardHandler('onExecute'),\n      onHandler('onItemExecute'),\n      defaulted('inputClasses', []),\n      defaulted('inputAttributes', {}),\n      defaulted('inputStyles', {}),\n      defaulted('matchWidth', true),\n      defaulted('useMinWidth', false),\n      defaulted('dismissOnBlur', true),\n      markers$1(['openClass']),\n      option$3('initialData'),\n      field('typeaheadBehaviours', [\n        Focusing,\n        Representing,\n        Streaming,\n        Keying,\n        Toggling,\n        Coupling\n      ]),\n      customField('lazyTypeaheadComp', () => Cell(Optional.none)),\n      customField('previewing', () => Cell(true))\n    ].concat(schema$l()).concat(sandboxFields()));\n    const parts$b = constant$1([external({\n        schema: [tieredMenuMarkers()],\n        name: 'menu',\n        overrides: detail => {\n          return {\n            fakeFocus: true,\n            onHighlightItem: (_tmenu, menu, item) => {\n              if (!detail.previewing.get()) {\n                detail.lazyTypeaheadComp.get().each(input => {\n                  if (detail.model.populateFromBrowse) {\n                    setValueFromItem(detail.model, input, item);\n                  }\n                });\n              } else {\n                detail.lazyTypeaheadComp.get().each(input => {\n                  attemptSelectOver(detail.model, input, item).fold(() => {\n                    if (detail.model.selectsOver) {\n                      Highlighting.dehighlight(menu, item);\n                      detail.previewing.set(true);\n                    } else {\n                      detail.previewing.set(false);\n                    }\n                  }, selectOverTextInInput => {\n                    selectOverTextInInput();\n                    detail.previewing.set(false);\n                  });\n                });\n              }\n            },\n            onExecute: (_menu, item) => {\n              return detail.lazyTypeaheadComp.get().map(typeahead => {\n                emitWith(typeahead, itemExecute(), { item });\n                return true;\n              });\n            },\n            onHover: (menu, item) => {\n              detail.previewing.set(false);\n              detail.lazyTypeaheadComp.get().each(input => {\n                if (detail.model.populateFromBrowse) {\n                  setValueFromItem(detail.model, input, item);\n                }\n              });\n            }\n          };\n        }\n      })]);\n\n    const Typeahead = composite({\n      name: 'Typeahead',\n      configFields: schema$g(),\n      partFields: parts$b(),\n      factory: make$3\n    });\n\n    const wrap = delegate => {\n      const toCached = () => {\n        return wrap(delegate.toCached());\n      };\n      const bindFuture = f => {\n        return wrap(delegate.bind(resA => resA.fold(err => Future.pure(Result.error(err)), a => f(a))));\n      };\n      const bindResult = f => {\n        return wrap(delegate.map(resA => resA.bind(f)));\n      };\n      const mapResult = f => {\n        return wrap(delegate.map(resA => resA.map(f)));\n      };\n      const mapError = f => {\n        return wrap(delegate.map(resA => resA.mapError(f)));\n      };\n      const foldResult = (whenError, whenValue) => {\n        return delegate.map(res => res.fold(whenError, whenValue));\n      };\n      const withTimeout = (timeout, errorThunk) => {\n        return wrap(Future.nu(callback => {\n          let timedOut = false;\n          const timer = setTimeout(() => {\n            timedOut = true;\n            callback(Result.error(errorThunk()));\n          }, timeout);\n          delegate.get(result => {\n            if (!timedOut) {\n              clearTimeout(timer);\n              callback(result);\n            }\n          });\n        }));\n      };\n      return {\n        ...delegate,\n        toCached,\n        bindFuture,\n        bindResult,\n        mapResult,\n        mapError,\n        foldResult,\n        withTimeout\n      };\n    };\n    const nu$1 = worker => {\n      return wrap(Future.nu(worker));\n    };\n    const value = value => {\n      return wrap(Future.pure(Result.value(value)));\n    };\n    const error = error => {\n      return wrap(Future.pure(Result.error(error)));\n    };\n    const fromResult = result => {\n      return wrap(Future.pure(result));\n    };\n    const fromFuture = future => {\n      return wrap(future.map(Result.value));\n    };\n    const fromPromise = promise => {\n      return nu$1(completer => {\n        promise.then(value => {\n          completer(Result.value(value));\n        }, error => {\n          completer(Result.error(error));\n        });\n      });\n    };\n    const FutureResult = {\n      nu: nu$1,\n      wrap,\n      pure: value,\n      value,\n      error,\n      fromResult,\n      fromFuture,\n      fromPromise\n    };\n\n    const getMenuButtonApi = component => ({\n      isEnabled: () => !Disabling.isDisabled(component),\n      setEnabled: state => Disabling.set(component, !state),\n      setActive: state => {\n        const elm = component.element;\n        if (state) {\n          add$2(elm, 'tox-tbtn--enabled');\n          set$9(elm, 'aria-pressed', true);\n        } else {\n          remove$2(elm, 'tox-tbtn--enabled');\n          remove$7(elm, 'aria-pressed');\n        }\n      },\n      isActive: () => has(component.element, 'tox-tbtn--enabled')\n    });\n    const renderMenuButton = (spec, prefix, backstage, role) => {\n      return renderCommonDropdown({\n        text: spec.text,\n        icon: spec.icon,\n        tooltip: spec.tooltip,\n        searchable: spec.search.isSome(),\n        role,\n        fetch: (dropdownComp, callback) => {\n          const fetchContext = { pattern: spec.search.isSome() ? getSearchPattern(dropdownComp) : '' };\n          spec.fetch(items => {\n            callback(build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {\n              isHorizontalMenu: false,\n              search: spec.search\n            }));\n          }, fetchContext);\n        },\n        onSetup: spec.onSetup,\n        getApi: getMenuButtonApi,\n        columns: 1,\n        presets: 'normal',\n        classes: [],\n        dropdownBehaviours: [Tabstopping.config({})]\n      }, prefix, backstage.shared);\n    };\n    const getFetch = (items, getButton, backstage) => {\n      const getMenuItemAction = item => api => {\n        const newValue = !api.isActive();\n        api.setActive(newValue);\n        item.storage.set(newValue);\n        backstage.shared.getSink().each(sink => {\n          getButton().getOpt(sink).each(orig => {\n            focus$3(orig.element);\n            emitWith(orig, formActionEvent, {\n              name: item.name,\n              value: item.storage.get()\n            });\n          });\n        });\n      };\n      const getMenuItemSetup = item => api => {\n        api.setActive(item.storage.get());\n      };\n      return success => {\n        success(map$2(items, item => {\n          const text = item.text.fold(() => ({}), text => ({ text }));\n          return {\n            type: item.type,\n            active: false,\n            ...text,\n            onAction: getMenuItemAction(item),\n            onSetup: getMenuItemSetup(item)\n          };\n        }));\n      };\n    };\n\n    const renderCommonSpec = (spec, actionOpt, extraBehaviours = [], dom, components, providersBackstage) => {\n      const action = actionOpt.fold(() => ({}), action => ({ action }));\n      const common = {\n        buttonBehaviours: derive$1([\n          DisablingConfigs.button(() => !spec.enabled || providersBackstage.isDisabled()),\n          receivingConfig(),\n          Tabstopping.config({}),\n          config('button press', [\n            preventDefault('click'),\n            preventDefault('mousedown')\n          ])\n        ].concat(extraBehaviours)),\n        eventOrder: {\n          click: [\n            'button press',\n            'alloy.base.behaviour'\n          ],\n          mousedown: [\n            'button press',\n            'alloy.base.behaviour'\n          ]\n        },\n        ...action\n      };\n      const domFinal = deepMerge(common, { dom });\n      return deepMerge(domFinal, { components });\n    };\n    const renderIconButtonSpec = (spec, action, providersBackstage, extraBehaviours = []) => {\n      const tooltipAttributes = spec.tooltip.map(tooltip => ({\n        'aria-label': providersBackstage.translate(tooltip),\n        'title': providersBackstage.translate(tooltip)\n      })).getOr({});\n      const dom = {\n        tag: 'button',\n        classes: ['tox-tbtn'],\n        attributes: tooltipAttributes\n      };\n      const icon = spec.icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons));\n      const components = componentRenderPipeline([icon]);\n      return renderCommonSpec(spec, action, extraBehaviours, dom, components, providersBackstage);\n    };\n    const calculateClassesFromButtonType = buttonType => {\n      switch (buttonType) {\n      case 'primary':\n        return ['tox-button'];\n      case 'toolbar':\n        return ['tox-tbtn'];\n      case 'secondary':\n      default:\n        return [\n          'tox-button',\n          'tox-button--secondary'\n        ];\n      }\n    };\n    const renderButtonSpec = (spec, action, providersBackstage, extraBehaviours = [], extraClasses = []) => {\n      const translatedText = providersBackstage.translate(spec.text);\n      const icon = spec.icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons));\n      const components = [icon.getOrThunk(() => text$2(translatedText))];\n      const buttonType = spec.buttonType.getOr(!spec.primary && !spec.borderless ? 'secondary' : 'primary');\n      const baseClasses = calculateClassesFromButtonType(buttonType);\n      const classes = [\n        ...baseClasses,\n        ...icon.isSome() ? ['tox-button--icon'] : [],\n        ...spec.borderless ? ['tox-button--naked'] : [],\n        ...extraClasses\n      ];\n      const dom = {\n        tag: 'button',\n        classes,\n        attributes: { title: translatedText }\n      };\n      return renderCommonSpec(spec, action, extraBehaviours, dom, components, providersBackstage);\n    };\n    const renderButton = (spec, action, providersBackstage, extraBehaviours = [], extraClasses = []) => {\n      const buttonSpec = renderButtonSpec(spec, Optional.some(action), providersBackstage, extraBehaviours, extraClasses);\n      return Button.sketch(buttonSpec);\n    };\n    const getAction = (name, buttonType) => comp => {\n      if (buttonType === 'custom') {\n        emitWith(comp, formActionEvent, {\n          name,\n          value: {}\n        });\n      } else if (buttonType === 'submit') {\n        emit(comp, formSubmitEvent);\n      } else if (buttonType === 'cancel') {\n        emit(comp, formCancelEvent);\n      } else {\n        console.error('Unknown button type: ', buttonType);\n      }\n    };\n    const isMenuFooterButtonSpec = (spec, buttonType) => buttonType === 'menu';\n    const isNormalFooterButtonSpec = (spec, buttonType) => buttonType === 'custom' || buttonType === 'cancel' || buttonType === 'submit';\n    const renderFooterButton = (spec, buttonType, backstage) => {\n      if (isMenuFooterButtonSpec(spec, buttonType)) {\n        const getButton = () => memButton;\n        const menuButtonSpec = spec;\n        const fixedSpec = {\n          ...spec,\n          type: 'menubutton',\n          search: Optional.none(),\n          onSetup: api => {\n            api.setEnabled(spec.enabled);\n            return noop;\n          },\n          fetch: getFetch(menuButtonSpec.items, getButton, backstage)\n        };\n        const memButton = record(renderMenuButton(fixedSpec, 'tox-tbtn', backstage, Optional.none()));\n        return memButton.asSpec();\n      } else if (isNormalFooterButtonSpec(spec, buttonType)) {\n        const action = getAction(spec.name, buttonType);\n        const buttonSpec = {\n          ...spec,\n          borderless: false\n        };\n        return renderButton(buttonSpec, action, backstage.shared.providers, []);\n      } else {\n        console.error('Unknown footer button type: ', buttonType);\n        throw new Error('Unknown footer button type');\n      }\n    };\n    const renderDialogButton = (spec, providersBackstage) => {\n      const action = getAction(spec.name, 'custom');\n      return renderFormField(Optional.none(), FormField.parts.field({\n        factory: Button,\n        ...renderButtonSpec(spec, Optional.some(action), providersBackstage, [\n          RepresentingConfigs.memory(''),\n          ComposingConfigs.self()\n        ])\n      }));\n    };\n\n    const separator$1 = { type: 'separator' };\n    const toMenuItem = target => ({\n      type: 'menuitem',\n      value: target.url,\n      text: target.title,\n      meta: { attach: target.attach },\n      onAction: noop\n    });\n    const staticMenuItem = (title, url) => ({\n      type: 'menuitem',\n      value: url,\n      text: title,\n      meta: { attach: undefined },\n      onAction: noop\n    });\n    const toMenuItems = targets => map$2(targets, toMenuItem);\n    const filterLinkTargets = (type, targets) => filter$2(targets, target => target.type === type);\n    const filteredTargets = (type, targets) => toMenuItems(filterLinkTargets(type, targets));\n    const headerTargets = linkInfo => filteredTargets('header', linkInfo.targets);\n    const anchorTargets = linkInfo => filteredTargets('anchor', linkInfo.targets);\n    const anchorTargetTop = linkInfo => Optional.from(linkInfo.anchorTop).map(url => staticMenuItem('<top>', url)).toArray();\n    const anchorTargetBottom = linkInfo => Optional.from(linkInfo.anchorBottom).map(url => staticMenuItem('<bottom>', url)).toArray();\n    const historyTargets = history => map$2(history, url => staticMenuItem(url, url));\n    const joinMenuLists = items => {\n      return foldl(items, (a, b) => {\n        const bothEmpty = a.length === 0 || b.length === 0;\n        return bothEmpty ? a.concat(b) : a.concat(separator$1, b);\n      }, []);\n    };\n    const filterByQuery = (term, menuItems) => {\n      const lowerCaseTerm = term.toLowerCase();\n      return filter$2(menuItems, item => {\n        var _a;\n        const text = item.meta !== undefined && item.meta.text !== undefined ? item.meta.text : item.text;\n        const value = (_a = item.value) !== null && _a !== void 0 ? _a : '';\n        return contains$1(text.toLowerCase(), lowerCaseTerm) || contains$1(value.toLowerCase(), lowerCaseTerm);\n      });\n    };\n\n    const getItems = (fileType, input, urlBackstage) => {\n      const urlInputValue = Representing.getValue(input);\n      const term = urlInputValue.meta.text !== undefined ? urlInputValue.meta.text : urlInputValue.value;\n      const info = urlBackstage.getLinkInformation();\n      return info.fold(() => [], linkInfo => {\n        const history = filterByQuery(term, historyTargets(urlBackstage.getHistory(fileType)));\n        return fileType === 'file' ? joinMenuLists([\n          history,\n          filterByQuery(term, headerTargets(linkInfo)),\n          filterByQuery(term, flatten([\n            anchorTargetTop(linkInfo),\n            anchorTargets(linkInfo),\n            anchorTargetBottom(linkInfo)\n          ]))\n        ]) : history;\n      });\n    };\n    const errorId = generate$6('aria-invalid');\n    const renderUrlInput = (spec, backstage, urlBackstage, initialData) => {\n      const providersBackstage = backstage.shared.providers;\n      const updateHistory = component => {\n        const urlEntry = Representing.getValue(component);\n        urlBackstage.addToHistory(urlEntry.value, spec.filetype);\n      };\n      const typeaheadSpec = {\n        ...initialData.map(initialData => ({ initialData })).getOr({}),\n        dismissOnBlur: true,\n        inputClasses: ['tox-textfield'],\n        sandboxClasses: ['tox-dialog__popups'],\n        inputAttributes: {\n          'aria-errormessage': errorId,\n          'type': 'url'\n        },\n        minChars: 0,\n        responseTime: 0,\n        fetch: input => {\n          const items = getItems(spec.filetype, input, urlBackstage);\n          const tdata = build(items, ItemResponse$1.BUBBLE_TO_SANDBOX, backstage, {\n            isHorizontalMenu: false,\n            search: Optional.none()\n          });\n          return Future.pure(tdata);\n        },\n        getHotspot: comp => memUrlBox.getOpt(comp),\n        onSetValue: (comp, _newValue) => {\n          if (comp.hasConfigured(Invalidating)) {\n            Invalidating.run(comp).get(noop);\n          }\n        },\n        typeaheadBehaviours: derive$1([\n          ...urlBackstage.getValidationHandler().map(handler => Invalidating.config({\n            getRoot: comp => parentElement(comp.element),\n            invalidClass: 'tox-control-wrap--status-invalid',\n            notify: {\n              onInvalid: (comp, err) => {\n                memInvalidIcon.getOpt(comp).each(invalidComp => {\n                  set$9(invalidComp.element, 'title', providersBackstage.translate(err));\n                });\n              }\n            },\n            validator: {\n              validate: input => {\n                const urlEntry = Representing.getValue(input);\n                return FutureResult.nu(completer => {\n                  handler({\n                    type: spec.filetype,\n                    url: urlEntry.value\n                  }, validation => {\n                    if (validation.status === 'invalid') {\n                      const err = Result.error(validation.message);\n                      completer(err);\n                    } else {\n                      const val = Result.value(validation.message);\n                      completer(val);\n                    }\n                  });\n                });\n              },\n              validateOnLoad: false\n            }\n          })).toArray(),\n          Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() }),\n          Tabstopping.config({}),\n          config('urlinput-events', [\n            run$1(input(), comp => {\n              const currentValue = get$6(comp.element);\n              const trimmedValue = currentValue.trim();\n              if (trimmedValue !== currentValue) {\n                set$5(comp.element, trimmedValue);\n              }\n              if (spec.filetype === 'file') {\n                emitWith(comp, formChangeEvent, { name: spec.name });\n              }\n            }),\n            run$1(change(), comp => {\n              emitWith(comp, formChangeEvent, { name: spec.name });\n              updateHistory(comp);\n            }),\n            run$1(postPaste(), comp => {\n              emitWith(comp, formChangeEvent, { name: spec.name });\n              updateHistory(comp);\n            })\n          ])\n        ]),\n        eventOrder: {\n          [input()]: [\n            'streaming',\n            'urlinput-events',\n            'invalidating'\n          ]\n        },\n        model: {\n          getDisplayText: itemData => itemData.value,\n          selectsOver: false,\n          populateFromBrowse: false\n        },\n        markers: { openClass: 'tox-textfield--popup-open' },\n        lazySink: backstage.shared.getSink,\n        parts: { menu: part(false, 1, 'normal') },\n        onExecute: (_menu, component, _entry) => {\n          emitWith(component, formSubmitEvent, {});\n        },\n        onItemExecute: (typeahead, _sandbox, _item, _value) => {\n          updateHistory(typeahead);\n          emitWith(typeahead, formChangeEvent, { name: spec.name });\n        }\n      };\n      const pField = FormField.parts.field({\n        ...typeaheadSpec,\n        factory: Typeahead\n      });\n      const pLabel = spec.label.map(label => renderLabel$2(label, providersBackstage));\n      const makeIcon = (name, errId, icon = name, label = name) => render$3(icon, {\n        tag: 'div',\n        classes: [\n          'tox-icon',\n          'tox-control-wrap__status-icon-' + name\n        ],\n        attributes: {\n          'title': providersBackstage.translate(label),\n          'aria-live': 'polite',\n          ...errId.fold(() => ({}), id => ({ id }))\n        }\n      }, providersBackstage.icons);\n      const memInvalidIcon = record(makeIcon('invalid', Optional.some(errorId), 'warning'));\n      const memStatus = record({\n        dom: {\n          tag: 'div',\n          classes: ['tox-control-wrap__status-icon-wrap']\n        },\n        components: [memInvalidIcon.asSpec()]\n      });\n      const optUrlPicker = urlBackstage.getUrlPicker(spec.filetype);\n      const browseUrlEvent = generate$6('browser.url.event');\n      const memUrlBox = record({\n        dom: {\n          tag: 'div',\n          classes: ['tox-control-wrap']\n        },\n        components: [\n          pField,\n          memStatus.asSpec()\n        ],\n        behaviours: derive$1([Disabling.config({ disabled: () => !spec.enabled || providersBackstage.isDisabled() })])\n      });\n      const memUrlPickerButton = record(renderButton({\n        name: spec.name,\n        icon: Optional.some('browse'),\n        text: spec.label.getOr(''),\n        enabled: spec.enabled,\n        primary: false,\n        buttonType: Optional.none(),\n        borderless: true\n      }, component => emit(component, browseUrlEvent), providersBackstage, [], ['tox-browse-url']));\n      const controlHWrapper = () => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-form__controls-h-stack']\n        },\n        components: flatten([\n          [memUrlBox.asSpec()],\n          optUrlPicker.map(() => memUrlPickerButton.asSpec()).toArray()\n        ])\n      });\n      const openUrlPicker = comp => {\n        Composing.getCurrent(comp).each(field => {\n          const componentData = Representing.getValue(field);\n          const urlData = {\n            fieldname: spec.name,\n            ...componentData\n          };\n          optUrlPicker.each(picker => {\n            picker(urlData).get(chosenData => {\n              Representing.setValue(field, chosenData);\n              emitWith(comp, formChangeEvent, { name: spec.name });\n            });\n          });\n        });\n      };\n      return FormField.sketch({\n        dom: renderFormFieldDom(),\n        components: pLabel.toArray().concat([controlHWrapper()]),\n        fieldBehaviours: derive$1([\n          Disabling.config({\n            disabled: () => !spec.enabled || providersBackstage.isDisabled(),\n            onDisabled: comp => {\n              FormField.getField(comp).each(Disabling.disable);\n              memUrlPickerButton.getOpt(comp).each(Disabling.disable);\n            },\n            onEnabled: comp => {\n              FormField.getField(comp).each(Disabling.enable);\n              memUrlPickerButton.getOpt(comp).each(Disabling.enable);\n            }\n          }),\n          receivingConfig(),\n          config('url-input-events', [run$1(browseUrlEvent, openUrlPicker)])\n        ])\n      });\n    };\n\n    const renderAlertBanner = (spec, providersBackstage) => Container.sketch({\n      dom: {\n        tag: 'div',\n        attributes: { role: 'alert' },\n        classes: [\n          'tox-notification',\n          'tox-notification--in',\n          `tox-notification--${ spec.level }`\n        ]\n      },\n      components: [\n        {\n          dom: {\n            tag: 'div',\n            classes: ['tox-notification__icon']\n          },\n          components: [Button.sketch({\n              dom: {\n                tag: 'button',\n                classes: [\n                  'tox-button',\n                  'tox-button--naked',\n                  'tox-button--icon'\n                ],\n                innerHtml: get$2(spec.icon, providersBackstage.icons),\n                attributes: { title: providersBackstage.translate(spec.iconTooltip) }\n              },\n              action: comp => {\n                emitWith(comp, formActionEvent, {\n                  name: 'alert-banner',\n                  value: spec.url\n                });\n              },\n              buttonBehaviours: derive$1([addFocusableBehaviour()])\n            })]\n        },\n        {\n          dom: {\n            tag: 'div',\n            classes: ['tox-notification__body'],\n            innerHtml: providersBackstage.translate(spec.text)\n          }\n        }\n      ]\n    });\n\n    const set$1 = (element, status) => {\n      element.dom.checked = status;\n    };\n    const get$1 = element => element.dom.checked;\n\n    const renderCheckbox = (spec, providerBackstage, initialData) => {\n      const toggleCheckboxHandler = comp => {\n        comp.element.dom.click();\n        return Optional.some(true);\n      };\n      const pField = FormField.parts.field({\n        factory: { sketch: identity },\n        dom: {\n          tag: 'input',\n          classes: ['tox-checkbox__input'],\n          attributes: { type: 'checkbox' }\n        },\n        behaviours: derive$1([\n          ComposingConfigs.self(),\n          Disabling.config({ disabled: () => !spec.enabled || providerBackstage.isDisabled() }),\n          Tabstopping.config({}),\n          Focusing.config({}),\n          RepresentingConfigs.withElement(initialData, get$1, set$1),\n          Keying.config({\n            mode: 'special',\n            onEnter: toggleCheckboxHandler,\n            onSpace: toggleCheckboxHandler,\n            stopSpaceKeyup: true\n          }),\n          config('checkbox-events', [run$1(change(), (component, _) => {\n              emitWith(component, formChangeEvent, { name: spec.name });\n            })])\n        ])\n      });\n      const pLabel = FormField.parts.label({\n        dom: {\n          tag: 'span',\n          classes: ['tox-checkbox__label']\n        },\n        components: [text$2(providerBackstage.translate(spec.label))],\n        behaviours: derive$1([Unselecting.config({})])\n      });\n      const makeIcon = className => {\n        const iconName = className === 'checked' ? 'selected' : 'unselected';\n        return render$3(iconName, {\n          tag: 'span',\n          classes: [\n            'tox-icon',\n            'tox-checkbox-icon__' + className\n          ]\n        }, providerBackstage.icons);\n      };\n      const memIcons = record({\n        dom: {\n          tag: 'div',\n          classes: ['tox-checkbox__icons']\n        },\n        components: [\n          makeIcon('checked'),\n          makeIcon('unchecked')\n        ]\n      });\n      return FormField.sketch({\n        dom: {\n          tag: 'label',\n          classes: ['tox-checkbox']\n        },\n        components: [\n          pField,\n          memIcons.asSpec(),\n          pLabel\n        ],\n        fieldBehaviours: derive$1([\n          Disabling.config({\n            disabled: () => !spec.enabled || providerBackstage.isDisabled(),\n            disableClass: 'tox-checkbox--disabled',\n            onDisabled: comp => {\n              FormField.getField(comp).each(Disabling.disable);\n            },\n            onEnabled: comp => {\n              FormField.getField(comp).each(Disabling.enable);\n            }\n          }),\n          receivingConfig()\n        ])\n      });\n    };\n\n    const renderHtmlPanel = spec => {\n      if (spec.presets === 'presentation') {\n        return Container.sketch({\n          dom: {\n            tag: 'div',\n            classes: ['tox-form__group'],\n            innerHtml: spec.html\n          }\n        });\n      } else {\n        return Container.sketch({\n          dom: {\n            tag: 'div',\n            classes: ['tox-form__group'],\n            innerHtml: spec.html,\n            attributes: { role: 'document' }\n          },\n          containerBehaviours: derive$1([\n            Tabstopping.config({}),\n            Focusing.config({})\n          ])\n        });\n      }\n    };\n\n    const make$2 = render => {\n      return (parts, spec, dialogData, backstage) => get$g(spec, 'name').fold(() => render(spec, backstage, Optional.none()), fieldName => parts.field(fieldName, render(spec, backstage, get$g(dialogData, fieldName))));\n    };\n    const makeIframe = render => (parts, spec, dialogData, backstage) => {\n      const iframeSpec = deepMerge(spec, { source: 'dynamic' });\n      return make$2(render)(parts, iframeSpec, dialogData, backstage);\n    };\n    const factories = {\n      bar: make$2((spec, backstage) => renderBar(spec, backstage.shared)),\n      collection: make$2((spec, backstage, data) => renderCollection(spec, backstage.shared.providers, data)),\n      alertbanner: make$2((spec, backstage) => renderAlertBanner(spec, backstage.shared.providers)),\n      input: make$2((spec, backstage, data) => renderInput(spec, backstage.shared.providers, data)),\n      textarea: make$2((spec, backstage, data) => renderTextarea(spec, backstage.shared.providers, data)),\n      label: make$2((spec, backstage) => renderLabel$1(spec, backstage.shared)),\n      iframe: makeIframe((spec, backstage, data) => renderIFrame(spec, backstage.shared.providers, data)),\n      button: make$2((spec, backstage) => renderDialogButton(spec, backstage.shared.providers)),\n      checkbox: make$2((spec, backstage, data) => renderCheckbox(spec, backstage.shared.providers, data)),\n      colorinput: make$2((spec, backstage, data) => renderColorInput(spec, backstage.shared, backstage.colorinput, data)),\n      colorpicker: make$2((spec, backstage, data) => renderColorPicker(spec, backstage.shared.providers, data)),\n      dropzone: make$2((spec, backstage, data) => renderDropZone(spec, backstage.shared.providers, data)),\n      grid: make$2((spec, backstage) => renderGrid(spec, backstage.shared)),\n      listbox: make$2((spec, backstage, data) => renderListBox(spec, backstage, data)),\n      selectbox: make$2((spec, backstage, data) => renderSelectBox(spec, backstage.shared.providers, data)),\n      sizeinput: make$2((spec, backstage) => renderSizeInput(spec, backstage.shared.providers)),\n      slider: make$2((spec, backstage, data) => renderSlider(spec, backstage.shared.providers, data)),\n      urlinput: make$2((spec, backstage, data) => renderUrlInput(spec, backstage, backstage.urlinput, data)),\n      customeditor: make$2(renderCustomEditor),\n      htmlpanel: make$2(renderHtmlPanel),\n      imagepreview: make$2((spec, _, data) => renderImagePreview(spec, data)),\n      table: make$2((spec, backstage) => renderTable(spec, backstage.shared.providers)),\n      panel: make$2((spec, backstage) => renderPanel(spec, backstage))\n    };\n    const noFormParts = {\n      field: (_name, spec) => spec,\n      record: constant$1([])\n    };\n    const interpretInForm = (parts, spec, dialogData, oldBackstage) => {\n      const newBackstage = deepMerge(oldBackstage, { shared: { interpreter: childSpec => interpretParts(parts, childSpec, dialogData, newBackstage) } });\n      return interpretParts(parts, spec, dialogData, newBackstage);\n    };\n    const interpretParts = (parts, spec, dialogData, backstage) => get$g(factories, spec.type).fold(() => {\n      console.error(`Unknown factory type \"${ spec.type }\", defaulting to container: `, spec);\n      return spec;\n    }, factory => factory(parts, spec, dialogData, backstage));\n    const interpretWithoutForm = (spec, dialogData, backstage) => interpretParts(noFormParts, spec, dialogData, backstage);\n\n    const labelPrefix = 'layout-inset';\n    const westEdgeX = anchor => anchor.x;\n    const middleX = (anchor, element) => anchor.x + anchor.width / 2 - element.width / 2;\n    const eastEdgeX = (anchor, element) => anchor.x + anchor.width - element.width;\n    const northY = anchor => anchor.y;\n    const southY = (anchor, element) => anchor.y + anchor.height - element.height;\n    const centreY = (anchor, element) => anchor.y + anchor.height / 2 - element.height / 2;\n    const southwest = (anchor, element, bubbles) => nu$6(eastEdgeX(anchor, element), southY(anchor, element), bubbles.insetSouthwest(), northwest$3(), 'southwest', boundsRestriction(anchor, {\n      right: 0,\n      bottom: 3\n    }), labelPrefix);\n    const southeast = (anchor, element, bubbles) => nu$6(westEdgeX(anchor), southY(anchor, element), bubbles.insetSoutheast(), northeast$3(), 'southeast', boundsRestriction(anchor, {\n      left: 1,\n      bottom: 3\n    }), labelPrefix);\n    const northwest = (anchor, element, bubbles) => nu$6(eastEdgeX(anchor, element), northY(anchor), bubbles.insetNorthwest(), southwest$3(), 'northwest', boundsRestriction(anchor, {\n      right: 0,\n      top: 2\n    }), labelPrefix);\n    const northeast = (anchor, element, bubbles) => nu$6(westEdgeX(anchor), northY(anchor), bubbles.insetNortheast(), southeast$3(), 'northeast', boundsRestriction(anchor, {\n      left: 1,\n      top: 2\n    }), labelPrefix);\n    const north = (anchor, element, bubbles) => nu$6(middleX(anchor, element), northY(anchor), bubbles.insetNorth(), south$3(), 'north', boundsRestriction(anchor, { top: 2 }), labelPrefix);\n    const south = (anchor, element, bubbles) => nu$6(middleX(anchor, element), southY(anchor, element), bubbles.insetSouth(), north$3(), 'south', boundsRestriction(anchor, { bottom: 3 }), labelPrefix);\n    const east = (anchor, element, bubbles) => nu$6(eastEdgeX(anchor, element), centreY(anchor, element), bubbles.insetEast(), west$3(), 'east', boundsRestriction(anchor, { right: 0 }), labelPrefix);\n    const west = (anchor, element, bubbles) => nu$6(westEdgeX(anchor), centreY(anchor, element), bubbles.insetWest(), east$3(), 'west', boundsRestriction(anchor, { left: 1 }), labelPrefix);\n    const lookupPreserveLayout = lastPlacement => {\n      switch (lastPlacement) {\n      case 'north':\n        return north;\n      case 'northeast':\n        return northeast;\n      case 'northwest':\n        return northwest;\n      case 'south':\n        return south;\n      case 'southeast':\n        return southeast;\n      case 'southwest':\n        return southwest;\n      case 'east':\n        return east;\n      case 'west':\n        return west;\n      }\n    };\n    const preserve = (anchor, element, bubbles, placee, bounds) => {\n      const layout = getPlacement(placee).map(lookupPreserveLayout).getOr(north);\n      return layout(anchor, element, bubbles, placee, bounds);\n    };\n    const lookupFlippedLayout = lastPlacement => {\n      switch (lastPlacement) {\n      case 'north':\n        return south;\n      case 'northeast':\n        return southeast;\n      case 'northwest':\n        return southwest;\n      case 'south':\n        return north;\n      case 'southeast':\n        return northeast;\n      case 'southwest':\n        return northwest;\n      case 'east':\n        return west;\n      case 'west':\n        return east;\n      }\n    };\n    const flip = (anchor, element, bubbles, placee, bounds) => {\n      const layout = getPlacement(placee).map(lookupFlippedLayout).getOr(north);\n      return layout(anchor, element, bubbles, placee, bounds);\n    };\n\n    const bubbleAlignments$2 = {\n      valignCentre: [],\n      alignCentre: [],\n      alignLeft: [],\n      alignRight: [],\n      right: [],\n      left: [],\n      bottom: [],\n      top: []\n    };\n    const getInlineDialogAnchor = (contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor) => {\n      const bubbleSize = 12;\n      const overrides = { maxHeightFunction: expandable$1() };\n      const editableAreaAnchor = () => ({\n        type: 'node',\n        root: getContentContainer(getRootNode(contentAreaElement())),\n        node: Optional.from(contentAreaElement()),\n        bubble: nu$5(bubbleSize, bubbleSize, bubbleAlignments$2),\n        layouts: {\n          onRtl: () => [northeast],\n          onLtr: () => [northwest]\n        },\n        overrides\n      });\n      const standardAnchor = () => ({\n        type: 'hotspot',\n        hotspot: lazyAnchorbar(),\n        bubble: nu$5(-bubbleSize, bubbleSize, bubbleAlignments$2),\n        layouts: {\n          onRtl: () => [southeast$2],\n          onLtr: () => [southwest$2]\n        },\n        overrides\n      });\n      return () => lazyUseEditableAreaAnchor() ? editableAreaAnchor() : standardAnchor();\n    };\n    const getBannerAnchor = (contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor) => {\n      const editableAreaAnchor = () => ({\n        type: 'node',\n        root: getContentContainer(getRootNode(contentAreaElement())),\n        node: Optional.from(contentAreaElement()),\n        layouts: {\n          onRtl: () => [north],\n          onLtr: () => [north]\n        }\n      });\n      const standardAnchor = () => ({\n        type: 'hotspot',\n        hotspot: lazyAnchorbar(),\n        layouts: {\n          onRtl: () => [south$2],\n          onLtr: () => [south$2]\n        }\n      });\n      return () => lazyUseEditableAreaAnchor() ? editableAreaAnchor() : standardAnchor();\n    };\n    const getCursorAnchor = (editor, bodyElement) => () => ({\n      type: 'selection',\n      root: bodyElement(),\n      getSelection: () => {\n        const rng = editor.selection.getRng();\n        return Optional.some(SimSelection.range(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));\n      }\n    });\n    const getNodeAnchor$1 = bodyElement => element => ({\n      type: 'node',\n      root: bodyElement(),\n      node: element\n    });\n    const getAnchors = (editor, lazyAnchorbar, isToolbarTop) => {\n      const useFixedToolbarContainer = useFixedContainer(editor);\n      const bodyElement = () => SugarElement.fromDom(editor.getBody());\n      const contentAreaElement = () => SugarElement.fromDom(editor.getContentAreaContainer());\n      const lazyUseEditableAreaAnchor = () => useFixedToolbarContainer || !isToolbarTop();\n      return {\n        inlineDialog: getInlineDialogAnchor(contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor),\n        banner: getBannerAnchor(contentAreaElement, lazyAnchorbar, lazyUseEditableAreaAnchor),\n        cursor: getCursorAnchor(editor, bodyElement),\n        node: getNodeAnchor$1(bodyElement)\n      };\n    };\n\n    const colorPicker = editor => (callback, value) => {\n      const dialog = colorPickerDialog(editor);\n      dialog(callback, value);\n    };\n    const hasCustomColors = editor => () => hasCustomColors$1(editor);\n    const getColors = editor => id => getColors$2(editor, id);\n    const getColorCols = editor => id => getColorCols$1(editor, id);\n    const ColorInputBackstage = editor => ({\n      colorPicker: colorPicker(editor),\n      hasCustomColors: hasCustomColors(editor),\n      getColors: getColors(editor),\n      getColorCols: getColorCols(editor)\n    });\n\n    const isDraggableModal = editor => () => isDraggableModal$1(editor);\n    const DialogBackstage = editor => ({ isDraggableModal: isDraggableModal(editor) });\n\n    const HeaderBackstage = editor => {\n      const mode = Cell(isToolbarLocationBottom(editor) ? 'bottom' : 'top');\n      return {\n        isPositionedAtTop: () => mode.get() === 'top',\n        getDockingMode: mode.get,\n        setDockingMode: mode.set\n      };\n    };\n\n    const isNestedFormat = format => hasNonNullableKey(format, 'items');\n    const isFormatReference = format => hasNonNullableKey(format, 'format');\n    const defaultStyleFormats = [\n      {\n        title: 'Headings',\n        items: [\n          {\n            title: 'Heading 1',\n            format: 'h1'\n          },\n          {\n            title: 'Heading 2',\n            format: 'h2'\n          },\n          {\n            title: 'Heading 3',\n            format: 'h3'\n          },\n          {\n            title: 'Heading 4',\n            format: 'h4'\n          },\n          {\n            title: 'Heading 5',\n            format: 'h5'\n          },\n          {\n            title: 'Heading 6',\n            format: 'h6'\n          }\n        ]\n      },\n      {\n        title: 'Inline',\n        items: [\n          {\n            title: 'Bold',\n            format: 'bold'\n          },\n          {\n            title: 'Italic',\n            format: 'italic'\n          },\n          {\n            title: 'Underline',\n            format: 'underline'\n          },\n          {\n            title: 'Strikethrough',\n            format: 'strikethrough'\n          },\n          {\n            title: 'Superscript',\n            format: 'superscript'\n          },\n          {\n            title: 'Subscript',\n            format: 'subscript'\n          },\n          {\n            title: 'Code',\n            format: 'code'\n          }\n        ]\n      },\n      {\n        title: 'Blocks',\n        items: [\n          {\n            title: 'Paragraph',\n            format: 'p'\n          },\n          {\n            title: 'Blockquote',\n            format: 'blockquote'\n          },\n          {\n            title: 'Div',\n            format: 'div'\n          },\n          {\n            title: 'Pre',\n            format: 'pre'\n          }\n        ]\n      },\n      {\n        title: 'Align',\n        items: [\n          {\n            title: 'Left',\n            format: 'alignleft'\n          },\n          {\n            title: 'Center',\n            format: 'aligncenter'\n          },\n          {\n            title: 'Right',\n            format: 'alignright'\n          },\n          {\n            title: 'Justify',\n            format: 'alignjustify'\n          }\n        ]\n      }\n    ];\n    const isNestedFormats = format => has$2(format, 'items');\n    const isBlockFormat = format => has$2(format, 'block');\n    const isInlineFormat = format => has$2(format, 'inline');\n    const isSelectorFormat = format => has$2(format, 'selector');\n    const mapFormats = userFormats => foldl(userFormats, (acc, fmt) => {\n      if (isNestedFormats(fmt)) {\n        const result = mapFormats(fmt.items);\n        return {\n          customFormats: acc.customFormats.concat(result.customFormats),\n          formats: acc.formats.concat([{\n              title: fmt.title,\n              items: result.formats\n            }])\n        };\n      } else if (isInlineFormat(fmt) || isBlockFormat(fmt) || isSelectorFormat(fmt)) {\n        const formatName = isString(fmt.name) ? fmt.name : fmt.title.toLowerCase();\n        const formatNameWithPrefix = `custom-${ formatName }`;\n        return {\n          customFormats: acc.customFormats.concat([{\n              name: formatNameWithPrefix,\n              format: fmt\n            }]),\n          formats: acc.formats.concat([{\n              title: fmt.title,\n              format: formatNameWithPrefix,\n              icon: fmt.icon\n            }])\n        };\n      } else {\n        return {\n          ...acc,\n          formats: acc.formats.concat(fmt)\n        };\n      }\n    }, {\n      customFormats: [],\n      formats: []\n    });\n    const registerCustomFormats = (editor, userFormats) => {\n      const result = mapFormats(userFormats);\n      const registerFormats = customFormats => {\n        each$1(customFormats, fmt => {\n          if (!editor.formatter.has(fmt.name)) {\n            editor.formatter.register(fmt.name, fmt.format);\n          }\n        });\n      };\n      if (editor.formatter) {\n        registerFormats(result.customFormats);\n      } else {\n        editor.on('init', () => {\n          registerFormats(result.customFormats);\n        });\n      }\n      return result.formats;\n    };\n    const getStyleFormats = editor => getUserStyleFormats(editor).map(userFormats => {\n      const registeredUserFormats = registerCustomFormats(editor, userFormats);\n      return shouldMergeStyleFormats(editor) ? defaultStyleFormats.concat(registeredUserFormats) : registeredUserFormats;\n    }).getOr(defaultStyleFormats);\n\n    const isSeparator$1 = format => {\n      const keys$1 = keys(format);\n      return keys$1.length === 1 && contains$2(keys$1, 'title');\n    };\n    const processBasic = (item, isSelectedFor, getPreviewFor) => ({\n      ...item,\n      type: 'formatter',\n      isSelected: isSelectedFor(item.format),\n      getStylePreview: getPreviewFor(item.format)\n    });\n    const register$a = (editor, formats, isSelectedFor, getPreviewFor) => {\n      const enrichSupported = item => processBasic(item, isSelectedFor, getPreviewFor);\n      const enrichMenu = item => {\n        const newItems = doEnrich(item.items);\n        return {\n          ...item,\n          type: 'submenu',\n          getStyleItems: constant$1(newItems)\n        };\n      };\n      const enrichCustom = item => {\n        const formatName = isString(item.name) ? item.name : generate$6(item.title);\n        const formatNameWithPrefix = `custom-${ formatName }`;\n        const newItem = {\n          ...item,\n          type: 'formatter',\n          format: formatNameWithPrefix,\n          isSelected: isSelectedFor(formatNameWithPrefix),\n          getStylePreview: getPreviewFor(formatNameWithPrefix)\n        };\n        editor.formatter.register(formatName, newItem);\n        return newItem;\n      };\n      const doEnrich = items => map$2(items, item => {\n        if (isNestedFormat(item)) {\n          return enrichMenu(item);\n        } else if (isFormatReference(item)) {\n          return enrichSupported(item);\n        } else if (isSeparator$1(item)) {\n          return {\n            ...item,\n            type: 'separator'\n          };\n        } else {\n          return enrichCustom(item);\n        }\n      });\n      return doEnrich(formats);\n    };\n\n    const init$8 = editor => {\n      const isSelectedFor = format => () => editor.formatter.match(format);\n      const getPreviewFor = format => () => {\n        const fmt = editor.formatter.get(format);\n        return fmt !== undefined ? Optional.some({\n          tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',\n          styles: editor.dom.parseStyle(editor.formatter.getCssText(format))\n        }) : Optional.none();\n      };\n      const settingsFormats = Cell([]);\n      const eventsFormats = Cell([]);\n      const replaceSettings = Cell(false);\n      editor.on('PreInit', _e => {\n        const formats = getStyleFormats(editor);\n        const enriched = register$a(editor, formats, isSelectedFor, getPreviewFor);\n        settingsFormats.set(enriched);\n      });\n      editor.on('addStyleModifications', e => {\n        const modifications = register$a(editor, e.items, isSelectedFor, getPreviewFor);\n        eventsFormats.set(modifications);\n        replaceSettings.set(e.replace);\n      });\n      const getData = () => {\n        const fromSettings = replaceSettings.get() ? [] : settingsFormats.get();\n        const fromEvents = eventsFormats.get();\n        return fromSettings.concat(fromEvents);\n      };\n      return { getData };\n    };\n\n    const isElement = node => isNonNullable(node) && node.nodeType === 1;\n    const trim = global$1.trim;\n    const hasContentEditableState = value => {\n      return node => {\n        if (isElement(node)) {\n          if (node.contentEditable === value) {\n            return true;\n          }\n          if (node.getAttribute('data-mce-contenteditable') === value) {\n            return true;\n          }\n        }\n        return false;\n      };\n    };\n    const isContentEditableTrue = hasContentEditableState('true');\n    const isContentEditableFalse = hasContentEditableState('false');\n    const create$1 = (type, title, url, level, attach) => ({\n      type,\n      title,\n      url,\n      level,\n      attach\n    });\n    const isChildOfContentEditableTrue = node => {\n      let tempNode = node;\n      while (tempNode = tempNode.parentNode) {\n        const value = tempNode.contentEditable;\n        if (value && value !== 'inherit') {\n          return isContentEditableTrue(tempNode);\n        }\n      }\n      return false;\n    };\n    const select = (selector, root) => {\n      return map$2(descendants(SugarElement.fromDom(root), selector), element => {\n        return element.dom;\n      });\n    };\n    const getElementText = elm => {\n      return elm.innerText || elm.textContent;\n    };\n    const getOrGenerateId = elm => {\n      return elm.id ? elm.id : generate$6('h');\n    };\n    const isAnchor = elm => {\n      return elm && elm.nodeName === 'A' && (elm.id || elm.name) !== undefined;\n    };\n    const isValidAnchor = elm => {\n      return isAnchor(elm) && isEditable(elm);\n    };\n    const isHeader = elm => {\n      return elm && /^(H[1-6])$/.test(elm.nodeName);\n    };\n    const isEditable = elm => {\n      return isChildOfContentEditableTrue(elm) && !isContentEditableFalse(elm);\n    };\n    const isValidHeader = elm => {\n      return isHeader(elm) && isEditable(elm);\n    };\n    const getLevel = elm => {\n      return isHeader(elm) ? parseInt(elm.nodeName.substr(1), 10) : 0;\n    };\n    const headerTarget = elm => {\n      var _a;\n      const headerId = getOrGenerateId(elm);\n      const attach = () => {\n        elm.id = headerId;\n      };\n      return create$1('header', (_a = getElementText(elm)) !== null && _a !== void 0 ? _a : '', '#' + headerId, getLevel(elm), attach);\n    };\n    const anchorTarget = elm => {\n      const anchorId = elm.id || elm.name;\n      const anchorText = getElementText(elm);\n      return create$1('anchor', anchorText ? anchorText : '#' + anchorId, '#' + anchorId, 0, noop);\n    };\n    const getHeaderTargets = elms => {\n      return map$2(filter$2(elms, isValidHeader), headerTarget);\n    };\n    const getAnchorTargets = elms => {\n      return map$2(filter$2(elms, isValidAnchor), anchorTarget);\n    };\n    const getTargetElements = elm => {\n      const elms = select('h1,h2,h3,h4,h5,h6,a:not([href])', elm);\n      return elms;\n    };\n    const hasTitle = target => {\n      return trim(target.title).length > 0;\n    };\n    const find = elm => {\n      const elms = getTargetElements(elm);\n      return filter$2(getHeaderTargets(elms).concat(getAnchorTargets(elms)), hasTitle);\n    };\n    const LinkTargets = { find };\n\n    const STORAGE_KEY = 'tinymce-url-history';\n    const HISTORY_LENGTH = 5;\n    const isHttpUrl = url => isString(url) && /^https?/.test(url);\n    const isArrayOfUrl = a => isArray(a) && a.length <= HISTORY_LENGTH && forall(a, isHttpUrl);\n    const isRecordOfUrlArray = r => isObject(r) && find$4(r, value => !isArrayOfUrl(value)).isNone();\n    const getAllHistory = () => {\n      const unparsedHistory = global$4.getItem(STORAGE_KEY);\n      if (unparsedHistory === null) {\n        return {};\n      }\n      let history;\n      try {\n        history = JSON.parse(unparsedHistory);\n      } catch (e) {\n        if (e instanceof SyntaxError) {\n          console.log('Local storage ' + STORAGE_KEY + ' was not valid JSON', e);\n          return {};\n        }\n        throw e;\n      }\n      if (!isRecordOfUrlArray(history)) {\n        console.log('Local storage ' + STORAGE_KEY + ' was not valid format', history);\n        return {};\n      }\n      return history;\n    };\n    const setAllHistory = history => {\n      if (!isRecordOfUrlArray(history)) {\n        throw new Error('Bad format for history:\\n' + JSON.stringify(history));\n      }\n      global$4.setItem(STORAGE_KEY, JSON.stringify(history));\n    };\n    const getHistory = fileType => {\n      const history = getAllHistory();\n      return get$g(history, fileType).getOr([]);\n    };\n    const addToHistory = (url, fileType) => {\n      if (!isHttpUrl(url)) {\n        return;\n      }\n      const history = getAllHistory();\n      const items = get$g(history, fileType).getOr([]);\n      const itemsWithoutUrl = filter$2(items, item => item !== url);\n      history[fileType] = [url].concat(itemsWithoutUrl).slice(0, HISTORY_LENGTH);\n      setAllHistory(history);\n    };\n\n    const isTruthy = value => !!value;\n    const makeMap = value => map$1(global$1.makeMap(value, /[, ]/), isTruthy);\n    const getPicker = editor => Optional.from(getFilePickerCallback(editor));\n    const getPickerTypes = editor => {\n      const optFileTypes = Optional.from(getFilePickerTypes(editor)).filter(isTruthy).map(makeMap);\n      return getPicker(editor).fold(never, _picker => optFileTypes.fold(always, types => keys(types).length > 0 ? types : false));\n    };\n    const getPickerSetting = (editor, filetype) => {\n      const pickerTypes = getPickerTypes(editor);\n      if (isBoolean(pickerTypes)) {\n        return pickerTypes ? getPicker(editor) : Optional.none();\n      } else {\n        return pickerTypes[filetype] ? getPicker(editor) : Optional.none();\n      }\n    };\n    const getUrlPicker = (editor, filetype) => getPickerSetting(editor, filetype).map(picker => entry => Future.nu(completer => {\n      const handler = (value, meta) => {\n        if (!isString(value)) {\n          throw new Error('Expected value to be string');\n        }\n        if (meta !== undefined && !isObject(meta)) {\n          throw new Error('Expected meta to be a object');\n        }\n        const r = {\n          value,\n          meta\n        };\n        completer(r);\n      };\n      const meta = {\n        filetype,\n        fieldname: entry.fieldname,\n        ...Optional.from(entry.meta).getOr({})\n      };\n      picker.call(editor, handler, entry.value, meta);\n    }));\n    const getTextSetting = value => Optional.from(value).filter(isString).getOrUndefined();\n    const getLinkInformation = editor => {\n      if (!useTypeaheadUrls(editor)) {\n        return Optional.none();\n      }\n      return Optional.some({\n        targets: LinkTargets.find(editor.getBody()),\n        anchorTop: getTextSetting(getAnchorTop(editor)),\n        anchorBottom: getTextSetting(getAnchorBottom(editor))\n      });\n    };\n    const getValidationHandler = editor => Optional.from(getFilePickerValidatorHandler(editor));\n    const UrlInputBackstage = editor => ({\n      getHistory,\n      addToHistory,\n      getLinkInformation: () => getLinkInformation(editor),\n      getValidationHandler: () => getValidationHandler(editor),\n      getUrlPicker: filetype => getUrlPicker(editor, filetype)\n    });\n\n    const init$7 = (lazySinks, editor, lazyAnchorbar) => {\n      const contextMenuState = Cell(false);\n      const toolbar = HeaderBackstage(editor);\n      const providers = {\n        icons: () => editor.ui.registry.getAll().icons,\n        menuItems: () => editor.ui.registry.getAll().menuItems,\n        translate: global$8.translate,\n        isDisabled: () => editor.mode.isReadOnly() || !editor.ui.isEnabled(),\n        getOption: editor.options.get\n      };\n      const urlinput = UrlInputBackstage(editor);\n      const styles = init$8(editor);\n      const colorinput = ColorInputBackstage(editor);\n      const dialogSettings = DialogBackstage(editor);\n      const isContextMenuOpen = () => contextMenuState.get();\n      const setContextMenuState = state => contextMenuState.set(state);\n      const commonBackstage = {\n        shared: {\n          providers,\n          anchors: getAnchors(editor, lazyAnchorbar, toolbar.isPositionedAtTop),\n          header: toolbar\n        },\n        urlinput,\n        styles,\n        colorinput,\n        dialog: dialogSettings,\n        isContextMenuOpen,\n        setContextMenuState\n      };\n      const popupBackstage = {\n        ...commonBackstage,\n        shared: {\n          ...commonBackstage.shared,\n          interpreter: s => interpretWithoutForm(s, {}, popupBackstage),\n          getSink: lazySinks.popup\n        }\n      };\n      const dialogBackstage = {\n        ...commonBackstage,\n        shared: {\n          ...commonBackstage.shared,\n          interpreter: s => interpretWithoutForm(s, {}, dialogBackstage),\n          getSink: lazySinks.dialog\n        }\n      };\n      return {\n        popup: popupBackstage,\n        dialog: dialogBackstage\n      };\n    };\n\n    const setup$b = (editor, mothership, uiMotherships) => {\n      const broadcastEvent = (name, evt) => {\n        each$1([\n          mothership,\n          ...uiMotherships\n        ], m => {\n          m.broadcastEvent(name, evt);\n        });\n      };\n      const broadcastOn = (channel, message) => {\n        each$1([\n          mothership,\n          ...uiMotherships\n        ], m => {\n          m.broadcastOn([channel], message);\n        });\n      };\n      const fireDismissPopups = evt => broadcastOn(dismissPopups(), { target: evt.target });\n      const doc = getDocument();\n      const onTouchstart = bind(doc, 'touchstart', fireDismissPopups);\n      const onTouchmove = bind(doc, 'touchmove', evt => broadcastEvent(documentTouchmove(), evt));\n      const onTouchend = bind(doc, 'touchend', evt => broadcastEvent(documentTouchend(), evt));\n      const onMousedown = bind(doc, 'mousedown', fireDismissPopups);\n      const onMouseup = bind(doc, 'mouseup', evt => {\n        if (evt.raw.button === 0) {\n          broadcastOn(mouseReleased(), { target: evt.target });\n        }\n      });\n      const onContentClick = raw => broadcastOn(dismissPopups(), { target: SugarElement.fromDom(raw.target) });\n      const onContentMouseup = raw => {\n        if (raw.button === 0) {\n          broadcastOn(mouseReleased(), { target: SugarElement.fromDom(raw.target) });\n        }\n      };\n      const onContentMousedown = () => {\n        each$1(editor.editorManager.get(), loopEditor => {\n          if (editor !== loopEditor) {\n            loopEditor.dispatch('DismissPopups', { relatedTarget: editor });\n          }\n        });\n      };\n      const onWindowScroll = evt => broadcastEvent(windowScroll(), fromRawEvent(evt));\n      const onWindowResize = evt => {\n        broadcastOn(repositionPopups(), {});\n        broadcastEvent(windowResize(), fromRawEvent(evt));\n      };\n      const onEditorResize = () => broadcastOn(repositionPopups(), {});\n      const onEditorProgress = evt => {\n        if (evt.state) {\n          broadcastOn(dismissPopups(), { target: SugarElement.fromDom(editor.getContainer()) });\n        }\n      };\n      const onDismissPopups = event => {\n        broadcastOn(dismissPopups(), { target: SugarElement.fromDom(event.relatedTarget.getContainer()) });\n      };\n      editor.on('PostRender', () => {\n        editor.on('click', onContentClick);\n        editor.on('tap', onContentClick);\n        editor.on('mouseup', onContentMouseup);\n        editor.on('mousedown', onContentMousedown);\n        editor.on('ScrollWindow', onWindowScroll);\n        editor.on('ResizeWindow', onWindowResize);\n        editor.on('ResizeEditor', onEditorResize);\n        editor.on('AfterProgressState', onEditorProgress);\n        editor.on('DismissPopups', onDismissPopups);\n      });\n      editor.on('remove', () => {\n        editor.off('click', onContentClick);\n        editor.off('tap', onContentClick);\n        editor.off('mouseup', onContentMouseup);\n        editor.off('mousedown', onContentMousedown);\n        editor.off('ScrollWindow', onWindowScroll);\n        editor.off('ResizeWindow', onWindowResize);\n        editor.off('ResizeEditor', onEditorResize);\n        editor.off('AfterProgressState', onEditorProgress);\n        editor.off('DismissPopups', onDismissPopups);\n        onMousedown.unbind();\n        onTouchstart.unbind();\n        onTouchmove.unbind();\n        onTouchend.unbind();\n        onMouseup.unbind();\n      });\n      editor.on('detach', () => {\n        each$1([\n          mothership,\n          ...uiMotherships\n        ], detachSystem);\n        each$1([\n          mothership,\n          ...uiMotherships\n        ], m => m.destroy());\n      });\n    };\n\n    const parts$a = AlloyParts;\n    const partType = PartType;\n\n    const schema$f = constant$1([\n      defaulted('shell', false),\n      required$1('makeItem'),\n      defaulted('setupItem', noop),\n      SketchBehaviours.field('listBehaviours', [Replacing])\n    ]);\n    const customListDetail = () => ({ behaviours: derive$1([Replacing.config({})]) });\n    const itemsPart = optional({\n      name: 'items',\n      overrides: customListDetail\n    });\n    const parts$9 = constant$1([itemsPart]);\n    const name = constant$1('CustomList');\n\n    const factory$f = (detail, components, _spec, _external) => {\n      const setItems = (list, items) => {\n        getListContainer(list).fold(() => {\n          console.error('Custom List was defined to not be a shell, but no item container was specified in components');\n          throw new Error('Custom List was defined to not be a shell, but no item container was specified in components');\n        }, container => {\n          const itemComps = Replacing.contents(container);\n          const numListsRequired = items.length;\n          const numListsToAdd = numListsRequired - itemComps.length;\n          const itemsToAdd = numListsToAdd > 0 ? range$2(numListsToAdd, () => detail.makeItem()) : [];\n          const itemsToRemove = itemComps.slice(numListsRequired);\n          each$1(itemsToRemove, item => Replacing.remove(container, item));\n          each$1(itemsToAdd, item => Replacing.append(container, item));\n          const builtLists = Replacing.contents(container);\n          each$1(builtLists, (item, i) => {\n            detail.setupItem(list, item, items[i], i);\n          });\n        });\n      };\n      const extra = detail.shell ? {\n        behaviours: [Replacing.config({})],\n        components: []\n      } : {\n        behaviours: [],\n        components\n      };\n      const getListContainer = component => detail.shell ? Optional.some(component) : getPart(component, detail, 'items');\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components: extra.components,\n        behaviours: augment(detail.listBehaviours, extra.behaviours),\n        apis: { setItems }\n      };\n    };\n    const CustomList = composite({\n      name: name(),\n      configFields: schema$f(),\n      partFields: parts$9(),\n      factory: factory$f,\n      apis: {\n        setItems: (apis, list, items) => {\n          apis.setItems(list, items);\n        }\n      }\n    });\n\n    const schema$e = constant$1([\n      required$1('dom'),\n      defaulted('shell', true),\n      field('toolbarBehaviours', [Replacing])\n    ]);\n    const enhanceGroups = () => ({ behaviours: derive$1([Replacing.config({})]) });\n    const parts$8 = constant$1([optional({\n        name: 'groups',\n        overrides: enhanceGroups\n      })]);\n\n    const factory$e = (detail, components, _spec, _externals) => {\n      const setGroups = (toolbar, groups) => {\n        getGroupContainer(toolbar).fold(() => {\n          console.error('Toolbar was defined to not be a shell, but no groups container was specified in components');\n          throw new Error('Toolbar was defined to not be a shell, but no groups container was specified in components');\n        }, container => {\n          Replacing.set(container, groups);\n        });\n      };\n      const getGroupContainer = component => detail.shell ? Optional.some(component) : getPart(component, detail, 'groups');\n      const extra = detail.shell ? {\n        behaviours: [Replacing.config({})],\n        components: []\n      } : {\n        behaviours: [],\n        components\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components: extra.components,\n        behaviours: augment(detail.toolbarBehaviours, extra.behaviours),\n        apis: { setGroups },\n        domModification: { attributes: { role: 'group' } }\n      };\n    };\n    const Toolbar = composite({\n      name: 'Toolbar',\n      configFields: schema$e(),\n      partFields: parts$8(),\n      factory: factory$e,\n      apis: {\n        setGroups: (apis, toolbar, groups) => {\n          apis.setGroups(toolbar, groups);\n        }\n      }\n    });\n\n    const setup$a = noop;\n    const isDocked$2 = never;\n    const getBehaviours$1 = constant$1([]);\n\n    var StaticHeader = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        setup: setup$a,\n        isDocked: isDocked$2,\n        getBehaviours: getBehaviours$1\n    });\n\n    const getOffsetParent = element => {\n      const isFixed = is$1(getRaw(element, 'position'), 'fixed');\n      const offsetParent$1 = isFixed ? Optional.none() : offsetParent(element);\n      return offsetParent$1.orThunk(() => {\n        const marker = SugarElement.fromTag('span');\n        return parent(element).bind(parent => {\n          append$2(parent, marker);\n          const offsetParent$1 = offsetParent(marker);\n          remove$5(marker);\n          return offsetParent$1;\n        });\n      });\n    };\n    const getOrigin = element => getOffsetParent(element).map(absolute$3).getOrThunk(() => SugarPosition(0, 0));\n\n    const morphAdt = Adt.generate([\n      { static: [] },\n      { absolute: ['positionCss'] },\n      { fixed: ['positionCss'] }\n    ]);\n    const appear = (component, contextualInfo) => {\n      const elem = component.element;\n      add$2(elem, contextualInfo.transitionClass);\n      remove$2(elem, contextualInfo.fadeOutClass);\n      add$2(elem, contextualInfo.fadeInClass);\n      contextualInfo.onShow(component);\n    };\n    const disappear = (component, contextualInfo) => {\n      const elem = component.element;\n      add$2(elem, contextualInfo.transitionClass);\n      remove$2(elem, contextualInfo.fadeInClass);\n      add$2(elem, contextualInfo.fadeOutClass);\n      contextualInfo.onHide(component);\n    };\n    const isPartiallyVisible = (box, viewport) => box.y < viewport.bottom && box.bottom > viewport.y;\n    const isTopCompletelyVisible = (box, viewport) => box.y >= viewport.y;\n    const isBottomCompletelyVisible = (box, viewport) => box.bottom <= viewport.bottom;\n    const isVisibleForModes = (modes, box, viewport) => forall(modes, mode => {\n      switch (mode) {\n      case 'bottom':\n        return isBottomCompletelyVisible(box, viewport);\n      case 'top':\n        return isTopCompletelyVisible(box, viewport);\n      }\n    });\n    const getPrior = (elem, state) => state.getInitialPos().map(pos => bounds(pos.bounds.x, pos.bounds.y, get$c(elem), get$d(elem)));\n    const storePrior = (elem, box, state) => {\n      state.setInitialPos({\n        style: getAllRaw(elem),\n        position: get$e(elem, 'position') || 'static',\n        bounds: box\n      });\n    };\n    const revertToOriginal = (elem, box, state) => state.getInitialPos().bind(position => {\n      state.clearInitialPos();\n      switch (position.position) {\n      case 'static':\n        return Optional.some(morphAdt.static());\n      case 'absolute':\n        const offsetBox = getOffsetParent(elem).map(box$1).getOrThunk(() => box$1(body()));\n        return Optional.some(morphAdt.absolute(NuPositionCss('absolute', get$g(position.style, 'left').map(_left => box.x - offsetBox.x), get$g(position.style, 'top').map(_top => box.y - offsetBox.y), get$g(position.style, 'right').map(_right => offsetBox.right - box.right), get$g(position.style, 'bottom').map(_bottom => offsetBox.bottom - box.bottom))));\n      default:\n        return Optional.none();\n      }\n    });\n    const morphToOriginal = (elem, viewport, state) => getPrior(elem, state).filter(box => isVisibleForModes(state.getModes(), box, viewport)).bind(box => revertToOriginal(elem, box, state));\n    const morphToFixed = (elem, viewport, state) => {\n      const box = box$1(elem);\n      if (!isVisibleForModes(state.getModes(), box, viewport)) {\n        storePrior(elem, box, state);\n        const winBox = win();\n        const left = box.x - winBox.x;\n        const top = viewport.y - winBox.y;\n        const bottom = winBox.bottom - viewport.bottom;\n        const isTop = box.y <= viewport.y;\n        return Optional.some(morphAdt.fixed(NuPositionCss('fixed', Optional.some(left), isTop ? Optional.some(top) : Optional.none(), Optional.none(), !isTop ? Optional.some(bottom) : Optional.none())));\n      } else {\n        return Optional.none();\n      }\n    };\n    const getMorph = (component, viewport, state) => {\n      const elem = component.element;\n      const isDocked = is$1(getRaw(elem, 'position'), 'fixed');\n      return isDocked ? morphToOriginal(elem, viewport, state) : morphToFixed(elem, viewport, state);\n    };\n    const getMorphToOriginal = (component, state) => {\n      const elem = component.element;\n      return getPrior(elem, state).bind(box => revertToOriginal(elem, box, state));\n    };\n\n    const morphToStatic = (component, config, state) => {\n      state.setDocked(false);\n      each$1([\n        'left',\n        'right',\n        'top',\n        'bottom',\n        'position'\n      ], prop => remove$6(component.element, prop));\n      config.onUndocked(component);\n    };\n    const morphToCoord = (component, config, state, position) => {\n      const isDocked = position.position === 'fixed';\n      state.setDocked(isDocked);\n      applyPositionCss(component.element, position);\n      const method = isDocked ? config.onDocked : config.onUndocked;\n      method(component);\n    };\n    const updateVisibility = (component, config, state, viewport, morphToDocked = false) => {\n      config.contextual.each(contextInfo => {\n        contextInfo.lazyContext(component).each(box => {\n          const isVisible = isPartiallyVisible(box, viewport);\n          if (isVisible !== state.isVisible()) {\n            state.setVisible(isVisible);\n            if (morphToDocked && !isVisible) {\n              add$1(component.element, [contextInfo.fadeOutClass]);\n              contextInfo.onHide(component);\n            } else {\n              const method = isVisible ? appear : disappear;\n              method(component, contextInfo);\n            }\n          }\n        });\n      });\n    };\n    const refreshInternal = (component, config, state) => {\n      const viewport = config.lazyViewport(component);\n      const isDocked = state.isDocked();\n      if (isDocked) {\n        updateVisibility(component, config, state, viewport);\n      }\n      getMorph(component, viewport, state).each(morph => {\n        morph.fold(() => morphToStatic(component, config, state), position => morphToCoord(component, config, state, position), position => {\n          updateVisibility(component, config, state, viewport, true);\n          morphToCoord(component, config, state, position);\n        });\n      });\n    };\n    const resetInternal = (component, config, state) => {\n      const elem = component.element;\n      state.setDocked(false);\n      getMorphToOriginal(component, state).each(morph => {\n        morph.fold(() => morphToStatic(component, config, state), position => morphToCoord(component, config, state, position), noop);\n      });\n      state.setVisible(true);\n      config.contextual.each(contextInfo => {\n        remove$1(elem, [\n          contextInfo.fadeInClass,\n          contextInfo.fadeOutClass,\n          contextInfo.transitionClass\n        ]);\n        contextInfo.onShow(component);\n      });\n      refresh$4(component, config, state);\n    };\n    const refresh$4 = (component, config, state) => {\n      if (component.getSystem().isConnected()) {\n        refreshInternal(component, config, state);\n      }\n    };\n    const reset = (component, config, state) => {\n      if (state.isDocked()) {\n        resetInternal(component, config, state);\n      }\n    };\n    const isDocked$1 = (component, config, state) => state.isDocked();\n    const setModes = (component, config, state, modes) => state.setModes(modes);\n    const getModes = (component, config, state) => state.getModes();\n\n    var DockingApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        refresh: refresh$4,\n        reset: reset,\n        isDocked: isDocked$1,\n        getModes: getModes,\n        setModes: setModes\n    });\n\n    const events$5 = (dockInfo, dockState) => derive$2([\n      runOnSource(transitionend(), (component, simulatedEvent) => {\n        dockInfo.contextual.each(contextInfo => {\n          if (has(component.element, contextInfo.transitionClass)) {\n            remove$1(component.element, [\n              contextInfo.transitionClass,\n              contextInfo.fadeInClass\n            ]);\n            const notify = dockState.isVisible() ? contextInfo.onShown : contextInfo.onHidden;\n            notify(component);\n          }\n          simulatedEvent.stop();\n        });\n      }),\n      run$1(windowScroll(), (component, _) => {\n        refresh$4(component, dockInfo, dockState);\n      }),\n      run$1(windowResize(), (component, _) => {\n        reset(component, dockInfo, dockState);\n      })\n    ]);\n\n    var ActiveDocking = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$5\n    });\n\n    var DockingSchema = [\n      optionObjOf('contextual', [\n        requiredString('fadeInClass'),\n        requiredString('fadeOutClass'),\n        requiredString('transitionClass'),\n        requiredFunction('lazyContext'),\n        onHandler('onShow'),\n        onHandler('onShown'),\n        onHandler('onHide'),\n        onHandler('onHidden')\n      ]),\n      defaultedFunction('lazyViewport', win),\n      defaultedArrayOf('modes', [\n        'top',\n        'bottom'\n      ], string),\n      onHandler('onDocked'),\n      onHandler('onUndocked')\n    ];\n\n    const init$6 = spec => {\n      const docked = Cell(false);\n      const visible = Cell(true);\n      const initialBounds = value$2();\n      const modes = Cell(spec.modes);\n      const readState = () => `docked:  ${ docked.get() }, visible: ${ visible.get() }, modes: ${ modes.get().join(',') }`;\n      return nu$8({\n        isDocked: docked.get,\n        setDocked: docked.set,\n        getInitialPos: initialBounds.get,\n        setInitialPos: initialBounds.set,\n        clearInitialPos: initialBounds.clear,\n        isVisible: visible.get,\n        setVisible: visible.set,\n        getModes: modes.get,\n        setModes: modes.set,\n        readState\n      });\n    };\n\n    var DockingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$6\n    });\n\n    const Docking = create$4({\n      fields: DockingSchema,\n      name: 'docking',\n      active: ActiveDocking,\n      apis: DockingApis,\n      state: DockingState\n    });\n\n    const toolbarHeightChange = constant$1(generate$6('toolbar-height-change'));\n\n    const visibility = {\n      fadeInClass: 'tox-editor-dock-fadein',\n      fadeOutClass: 'tox-editor-dock-fadeout',\n      transitionClass: 'tox-editor-dock-transition'\n    };\n    const editorStickyOnClass = 'tox-tinymce--toolbar-sticky-on';\n    const editorStickyOffClass = 'tox-tinymce--toolbar-sticky-off';\n    const scrollFromBehindHeader = (e, containerHeader) => {\n      const doc = owner$4(containerHeader);\n      const win = defaultView(containerHeader);\n      const viewHeight = win.dom.innerHeight;\n      const scrollPos = get$b(doc);\n      const markerElement = SugarElement.fromDom(e.elm);\n      const markerPos = absolute$2(markerElement);\n      const markerHeight = get$d(markerElement);\n      const markerTop = markerPos.y;\n      const markerBottom = markerTop + markerHeight;\n      const editorHeaderPos = absolute$3(containerHeader);\n      const editorHeaderHeight = get$d(containerHeader);\n      const editorHeaderTop = editorHeaderPos.top;\n      const editorHeaderBottom = editorHeaderTop + editorHeaderHeight;\n      const editorHeaderDockedAtTop = Math.abs(editorHeaderTop - scrollPos.top) < 2;\n      const editorHeaderDockedAtBottom = Math.abs(editorHeaderBottom - (scrollPos.top + viewHeight)) < 2;\n      if (editorHeaderDockedAtTop && markerTop < editorHeaderBottom) {\n        to(scrollPos.left, markerTop - editorHeaderHeight, doc);\n      } else if (editorHeaderDockedAtBottom && markerBottom > editorHeaderTop) {\n        const y = markerTop - viewHeight + markerHeight + editorHeaderHeight;\n        to(scrollPos.left, y, doc);\n      }\n    };\n    const isDockedMode = (header, mode) => contains$2(Docking.getModes(header), mode);\n    const updateIframeContentFlow = header => {\n      const getOccupiedHeight = elm => getOuter$2(elm) + (parseInt(get$e(elm, 'margin-top'), 10) || 0) + (parseInt(get$e(elm, 'margin-bottom'), 10) || 0);\n      const elm = header.element;\n      parentElement(elm).each(parentElem => {\n        const padding = 'padding-' + Docking.getModes(header)[0];\n        if (Docking.isDocked(header)) {\n          const parentWidth = get$c(parentElem);\n          set$8(elm, 'width', parentWidth + 'px');\n          set$8(parentElem, padding, getOccupiedHeight(elm) + 'px');\n        } else {\n          remove$6(elm, 'width');\n          remove$6(parentElem, padding);\n        }\n      });\n    };\n    const updateSinkVisibility = (sinkElem, visible) => {\n      if (visible) {\n        remove$2(sinkElem, visibility.fadeOutClass);\n        add$1(sinkElem, [\n          visibility.transitionClass,\n          visibility.fadeInClass\n        ]);\n      } else {\n        remove$2(sinkElem, visibility.fadeInClass);\n        add$1(sinkElem, [\n          visibility.fadeOutClass,\n          visibility.transitionClass\n        ]);\n      }\n    };\n    const updateEditorClasses = (editor, docked) => {\n      const editorContainer = SugarElement.fromDom(editor.getContainer());\n      if (docked) {\n        add$2(editorContainer, editorStickyOnClass);\n        remove$2(editorContainer, editorStickyOffClass);\n      } else {\n        add$2(editorContainer, editorStickyOffClass);\n        remove$2(editorContainer, editorStickyOnClass);\n      }\n    };\n    const restoreFocus = (headerElem, focusedElem) => {\n      const ownerDoc = owner$4(focusedElem);\n      active$1(ownerDoc).filter(activeElm => !eq(focusedElem, activeElm)).filter(activeElm => eq(activeElm, SugarElement.fromDom(ownerDoc.dom.body)) || contains(headerElem, activeElm)).each(() => focus$3(focusedElem));\n    };\n    const findFocusedElem = (rootElm, lazySink) => search(rootElm).orThunk(() => lazySink().toOptional().bind(sink => search(sink.element)));\n    const setup$9 = (editor, sharedBackstage, lazyHeader) => {\n      if (!editor.inline) {\n        if (!sharedBackstage.header.isPositionedAtTop()) {\n          editor.on('ResizeEditor', () => {\n            lazyHeader().each(Docking.reset);\n          });\n        }\n        editor.on('ResizeWindow ResizeEditor', () => {\n          lazyHeader().each(updateIframeContentFlow);\n        });\n        editor.on('SkinLoaded', () => {\n          lazyHeader().each(comp => {\n            Docking.isDocked(comp) ? Docking.reset(comp) : Docking.refresh(comp);\n          });\n        });\n        editor.on('FullscreenStateChanged', () => {\n          lazyHeader().each(Docking.reset);\n        });\n      }\n      editor.on('AfterScrollIntoView', e => {\n        lazyHeader().each(header => {\n          Docking.refresh(header);\n          const headerElem = header.element;\n          if (isVisible(headerElem)) {\n            scrollFromBehindHeader(e, headerElem);\n          }\n        });\n      });\n      editor.on('PostRender', () => {\n        updateEditorClasses(editor, false);\n      });\n    };\n    const isDocked = lazyHeader => lazyHeader().map(Docking.isDocked).getOr(false);\n    const getIframeBehaviours = () => [Receiving.config({ channels: { [toolbarHeightChange()]: { onReceive: updateIframeContentFlow } } })];\n    const getBehaviours = (editor, sharedBackstage) => {\n      const focusedElm = value$2();\n      const lazySink = sharedBackstage.getSink;\n      const runOnSinkElement = f => {\n        lazySink().each(sink => f(sink.element));\n      };\n      const onDockingSwitch = comp => {\n        if (!editor.inline) {\n          updateIframeContentFlow(comp);\n        }\n        updateEditorClasses(editor, Docking.isDocked(comp));\n        comp.getSystem().broadcastOn([repositionPopups()], {});\n        lazySink().each(sink => sink.getSystem().broadcastOn([repositionPopups()], {}));\n      };\n      const additionalBehaviours = editor.inline ? [] : getIframeBehaviours();\n      return [\n        Focusing.config({}),\n        Docking.config({\n          contextual: {\n            lazyContext: comp => {\n              const headerHeight = getOuter$2(comp.element);\n              const container = editor.inline ? editor.getContentAreaContainer() : editor.getContainer();\n              const box = box$1(SugarElement.fromDom(container));\n              const boxHeight = box.height - headerHeight;\n              const topBound = box.y + (isDockedMode(comp, 'top') ? 0 : headerHeight);\n              return Optional.some(bounds(box.x, topBound, box.width, boxHeight));\n            },\n            onShow: () => {\n              runOnSinkElement(elem => updateSinkVisibility(elem, true));\n            },\n            onShown: comp => {\n              runOnSinkElement(elem => remove$1(elem, [\n                visibility.transitionClass,\n                visibility.fadeInClass\n              ]));\n              focusedElm.get().each(elem => {\n                restoreFocus(comp.element, elem);\n                focusedElm.clear();\n              });\n            },\n            onHide: comp => {\n              findFocusedElem(comp.element, lazySink).fold(focusedElm.clear, focusedElm.set);\n              runOnSinkElement(elem => updateSinkVisibility(elem, false));\n            },\n            onHidden: () => {\n              runOnSinkElement(elem => remove$1(elem, [visibility.transitionClass]));\n            },\n            ...visibility\n          },\n          lazyViewport: comp => {\n            const win$1 = win();\n            const offset = getStickyToolbarOffset(editor);\n            const top = win$1.y + (isDockedMode(comp, 'top') ? offset : 0);\n            const height = win$1.height - (isDockedMode(comp, 'bottom') ? offset : 0);\n            return bounds(win$1.x, top, win$1.width, height);\n          },\n          modes: [sharedBackstage.header.getDockingMode()],\n          onDocked: onDockingSwitch,\n          onUndocked: onDockingSwitch\n        }),\n        ...additionalBehaviours\n      ];\n    };\n\n    var StickyHeader = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        setup: setup$9,\n        isDocked: isDocked,\n        getBehaviours: getBehaviours\n    });\n\n    const renderHeader = spec => {\n      const editor = spec.editor;\n      const getBehaviours$2 = spec.sticky ? getBehaviours : getBehaviours$1;\n      return {\n        uid: spec.uid,\n        dom: spec.dom,\n        components: spec.components,\n        behaviours: derive$1(getBehaviours$2(editor, spec.sharedBackstage))\n      };\n    };\n\n    const groupToolbarButtonSchema = objOf([\n      type,\n      requiredOf('items', oneOf([\n        arrOfObj([\n          name$1,\n          requiredArrayOf('items', string)\n        ]),\n        string\n      ]))\n    ].concat(baseToolbarButtonFields));\n    const createGroupToolbarButton = spec => asRaw('GroupToolbarButton', groupToolbarButtonSchema, spec);\n\n    const baseMenuButtonFields = [\n      optionString('text'),\n      optionString('tooltip'),\n      optionString('icon'),\n      defaultedOf('search', false, oneOf([\n        boolean,\n        objOf([optionString('placeholder')])\n      ], x => {\n        if (isBoolean(x)) {\n          return x ? Optional.some({ placeholder: Optional.none() }) : Optional.none();\n        } else {\n          return Optional.some(x);\n        }\n      })),\n      requiredFunction('fetch'),\n      defaultedFunction('onSetup', () => noop)\n    ];\n\n    const MenuButtonSchema = objOf([\n      type,\n      ...baseMenuButtonFields\n    ]);\n    const createMenuButton = spec => asRaw('menubutton', MenuButtonSchema, spec);\n\n    const splitButtonSchema = objOf([\n      type,\n      optionalTooltip,\n      optionalIcon,\n      optionalText,\n      optionalSelect,\n      fetch$1,\n      onSetup,\n      defaultedStringEnum('presets', 'normal', [\n        'normal',\n        'color',\n        'listpreview'\n      ]),\n      defaultedColumns(1),\n      onAction,\n      onItemAction\n    ]);\n    const createSplitButton = spec => asRaw('SplitButton', splitButtonSchema, spec);\n\n    const factory$d = (detail, spec) => {\n      const setMenus = (comp, menus) => {\n        const newMenus = map$2(menus, m => {\n          const buttonSpec = {\n            type: 'menubutton',\n            text: m.text,\n            fetch: callback => {\n              callback(m.getItems());\n            }\n          };\n          const internal = createMenuButton(buttonSpec).mapError(errInfo => formatError(errInfo)).getOrDie();\n          return renderMenuButton(internal, 'tox-mbtn', spec.backstage, Optional.some('menuitem'));\n        });\n        Replacing.set(comp, newMenus);\n      };\n      const apis = {\n        focus: Keying.focusIn,\n        setMenus\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components: [],\n        behaviours: derive$1([\n          Replacing.config({}),\n          config('menubar-events', [\n            runOnAttached(component => {\n              detail.onSetup(component);\n            }),\n            run$1(mouseover(), (comp, se) => {\n              descendant(comp.element, '.' + 'tox-mbtn--active').each(activeButton => {\n                closest$1(se.event.target, '.' + 'tox-mbtn').each(hoveredButton => {\n                  if (!eq(activeButton, hoveredButton)) {\n                    comp.getSystem().getByDom(activeButton).each(activeComp => {\n                      comp.getSystem().getByDom(hoveredButton).each(hoveredComp => {\n                        Dropdown.expand(hoveredComp);\n                        Dropdown.close(activeComp);\n                        Focusing.focus(hoveredComp);\n                      });\n                    });\n                  }\n                });\n              });\n            }),\n            run$1(focusShifted(), (comp, se) => {\n              se.event.prevFocus.bind(prev => comp.getSystem().getByDom(prev).toOptional()).each(prev => {\n                se.event.newFocus.bind(nu => comp.getSystem().getByDom(nu).toOptional()).each(nu => {\n                  if (Dropdown.isOpen(prev)) {\n                    Dropdown.expand(nu);\n                    Dropdown.close(prev);\n                  }\n                });\n              });\n            })\n          ]),\n          Keying.config({\n            mode: 'flow',\n            selector: '.' + 'tox-mbtn',\n            onEscape: comp => {\n              detail.onEscape(comp);\n              return Optional.some(true);\n            }\n          }),\n          Tabstopping.config({})\n        ]),\n        apis,\n        domModification: { attributes: { role: 'menubar' } }\n      };\n    };\n    var SilverMenubar = single({\n      factory: factory$d,\n      name: 'silver.Menubar',\n      configFields: [\n        required$1('dom'),\n        required$1('uid'),\n        required$1('onEscape'),\n        required$1('backstage'),\n        defaulted('onSetup', noop)\n      ],\n      apis: {\n        focus: (apis, comp) => {\n          apis.focus(comp);\n        },\n        setMenus: (apis, comp, menus) => {\n          apis.setMenus(comp, menus);\n        }\n      }\n    });\n\n    const promotionMessage = '\\u26A1\\ufe0fUpgrade';\n    const promotionLink = 'https://www.tiny.cloud/tinymce-self-hosted-premium-features/?utm_source=TinyMCE&utm_medium=SPAP&utm_campaign=SPAP&utm_id=editorreferral';\n    const renderPromotion = spec => {\n      return {\n        uid: spec.uid,\n        dom: spec.dom,\n        components: [{\n            dom: {\n              tag: 'a',\n              attributes: {\n                'href': promotionLink,\n                'rel': 'noopener',\n                'target': '_blank',\n                'aria-hidden': 'true'\n              },\n              classes: ['tox-promotion-link'],\n              innerHtml: promotionMessage\n            }\n          }]\n      };\n    };\n\n    const getAnimationRoot = (component, slideConfig) => slideConfig.getAnimationRoot.fold(() => component.element, get => get(component));\n\n    const getDimensionProperty = slideConfig => slideConfig.dimension.property;\n    const getDimension = (slideConfig, elem) => slideConfig.dimension.getDimension(elem);\n    const disableTransitions = (component, slideConfig) => {\n      const root = getAnimationRoot(component, slideConfig);\n      remove$1(root, [\n        slideConfig.shrinkingClass,\n        slideConfig.growingClass\n      ]);\n    };\n    const setShrunk = (component, slideConfig) => {\n      remove$2(component.element, slideConfig.openClass);\n      add$2(component.element, slideConfig.closedClass);\n      set$8(component.element, getDimensionProperty(slideConfig), '0px');\n      reflow(component.element);\n    };\n    const setGrown = (component, slideConfig) => {\n      remove$2(component.element, slideConfig.closedClass);\n      add$2(component.element, slideConfig.openClass);\n      remove$6(component.element, getDimensionProperty(slideConfig));\n    };\n    const doImmediateShrink = (component, slideConfig, slideState, _calculatedSize) => {\n      slideState.setCollapsed();\n      set$8(component.element, getDimensionProperty(slideConfig), getDimension(slideConfig, component.element));\n      disableTransitions(component, slideConfig);\n      setShrunk(component, slideConfig);\n      slideConfig.onStartShrink(component);\n      slideConfig.onShrunk(component);\n    };\n    const doStartShrink = (component, slideConfig, slideState, calculatedSize) => {\n      const size = calculatedSize.getOrThunk(() => getDimension(slideConfig, component.element));\n      slideState.setCollapsed();\n      set$8(component.element, getDimensionProperty(slideConfig), size);\n      reflow(component.element);\n      const root = getAnimationRoot(component, slideConfig);\n      remove$2(root, slideConfig.growingClass);\n      add$2(root, slideConfig.shrinkingClass);\n      setShrunk(component, slideConfig);\n      slideConfig.onStartShrink(component);\n    };\n    const doStartSmartShrink = (component, slideConfig, slideState) => {\n      const size = getDimension(slideConfig, component.element);\n      const shrinker = size === '0px' ? doImmediateShrink : doStartShrink;\n      shrinker(component, slideConfig, slideState, Optional.some(size));\n    };\n    const doStartGrow = (component, slideConfig, slideState) => {\n      const root = getAnimationRoot(component, slideConfig);\n      const wasShrinking = has(root, slideConfig.shrinkingClass);\n      const beforeSize = getDimension(slideConfig, component.element);\n      setGrown(component, slideConfig);\n      const fullSize = getDimension(slideConfig, component.element);\n      const startPartialGrow = () => {\n        set$8(component.element, getDimensionProperty(slideConfig), beforeSize);\n        reflow(component.element);\n      };\n      const startCompleteGrow = () => {\n        setShrunk(component, slideConfig);\n      };\n      const setStartSize = wasShrinking ? startPartialGrow : startCompleteGrow;\n      setStartSize();\n      remove$2(root, slideConfig.shrinkingClass);\n      add$2(root, slideConfig.growingClass);\n      setGrown(component, slideConfig);\n      set$8(component.element, getDimensionProperty(slideConfig), fullSize);\n      slideState.setExpanded();\n      slideConfig.onStartGrow(component);\n    };\n    const refresh$3 = (component, slideConfig, slideState) => {\n      if (slideState.isExpanded()) {\n        remove$6(component.element, getDimensionProperty(slideConfig));\n        const fullSize = getDimension(slideConfig, component.element);\n        set$8(component.element, getDimensionProperty(slideConfig), fullSize);\n      }\n    };\n    const grow = (component, slideConfig, slideState) => {\n      if (!slideState.isExpanded()) {\n        doStartGrow(component, slideConfig, slideState);\n      }\n    };\n    const shrink = (component, slideConfig, slideState) => {\n      if (slideState.isExpanded()) {\n        doStartSmartShrink(component, slideConfig, slideState);\n      }\n    };\n    const immediateShrink = (component, slideConfig, slideState) => {\n      if (slideState.isExpanded()) {\n        doImmediateShrink(component, slideConfig, slideState);\n      }\n    };\n    const hasGrown = (component, slideConfig, slideState) => slideState.isExpanded();\n    const hasShrunk = (component, slideConfig, slideState) => slideState.isCollapsed();\n    const isGrowing = (component, slideConfig, _slideState) => {\n      const root = getAnimationRoot(component, slideConfig);\n      return has(root, slideConfig.growingClass) === true;\n    };\n    const isShrinking = (component, slideConfig, _slideState) => {\n      const root = getAnimationRoot(component, slideConfig);\n      return has(root, slideConfig.shrinkingClass) === true;\n    };\n    const isTransitioning = (component, slideConfig, slideState) => isGrowing(component, slideConfig) || isShrinking(component, slideConfig);\n    const toggleGrow = (component, slideConfig, slideState) => {\n      const f = slideState.isExpanded() ? doStartSmartShrink : doStartGrow;\n      f(component, slideConfig, slideState);\n    };\n    const immediateGrow = (component, slideConfig, slideState) => {\n      if (!slideState.isExpanded()) {\n        setGrown(component, slideConfig);\n        set$8(component.element, getDimensionProperty(slideConfig), getDimension(slideConfig, component.element));\n        disableTransitions(component, slideConfig);\n        slideState.setExpanded();\n        slideConfig.onStartGrow(component);\n        slideConfig.onGrown(component);\n      }\n    };\n\n    var SlidingApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        refresh: refresh$3,\n        grow: grow,\n        shrink: shrink,\n        immediateShrink: immediateShrink,\n        hasGrown: hasGrown,\n        hasShrunk: hasShrunk,\n        isGrowing: isGrowing,\n        isShrinking: isShrinking,\n        isTransitioning: isTransitioning,\n        toggleGrow: toggleGrow,\n        disableTransitions: disableTransitions,\n        immediateGrow: immediateGrow\n    });\n\n    const exhibit = (base, slideConfig, _slideState) => {\n      const expanded = slideConfig.expanded;\n      return expanded ? nu$7({\n        classes: [slideConfig.openClass],\n        styles: {}\n      }) : nu$7({\n        classes: [slideConfig.closedClass],\n        styles: wrap$1(slideConfig.dimension.property, '0px')\n      });\n    };\n    const events$4 = (slideConfig, slideState) => derive$2([runOnSource(transitionend(), (component, simulatedEvent) => {\n        const raw = simulatedEvent.event.raw;\n        if (raw.propertyName === slideConfig.dimension.property) {\n          disableTransitions(component, slideConfig);\n          if (slideState.isExpanded()) {\n            remove$6(component.element, slideConfig.dimension.property);\n          }\n          const notify = slideState.isExpanded() ? slideConfig.onGrown : slideConfig.onShrunk;\n          notify(component);\n        }\n      })]);\n\n    var ActiveSliding = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        exhibit: exhibit,\n        events: events$4\n    });\n\n    var SlidingSchema = [\n      required$1('closedClass'),\n      required$1('openClass'),\n      required$1('shrinkingClass'),\n      required$1('growingClass'),\n      option$3('getAnimationRoot'),\n      onHandler('onShrunk'),\n      onHandler('onStartShrink'),\n      onHandler('onGrown'),\n      onHandler('onStartGrow'),\n      defaulted('expanded', false),\n      requiredOf('dimension', choose$1('property', {\n        width: [\n          output$1('property', 'width'),\n          output$1('getDimension', elem => get$c(elem) + 'px')\n        ],\n        height: [\n          output$1('property', 'height'),\n          output$1('getDimension', elem => get$d(elem) + 'px')\n        ]\n      }))\n    ];\n\n    const init$5 = spec => {\n      const state = Cell(spec.expanded);\n      const readState = () => 'expanded: ' + state.get();\n      return nu$8({\n        isExpanded: () => state.get() === true,\n        isCollapsed: () => state.get() === false,\n        setCollapsed: curry(state.set, false),\n        setExpanded: curry(state.set, true),\n        readState\n      });\n    };\n\n    var SlidingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$5\n    });\n\n    const Sliding = create$4({\n      fields: SlidingSchema,\n      name: 'sliding',\n      active: ActiveSliding,\n      apis: SlidingApis,\n      state: SlidingState\n    });\n\n    const owner = 'container';\n    const schema$d = [field('slotBehaviours', [])];\n    const getPartName = name => '<alloy.field.' + name + '>';\n    const sketch = sSpec => {\n      const parts = (() => {\n        const record = [];\n        const slot = (name, config) => {\n          record.push(name);\n          return generateOne$1(owner, getPartName(name), config);\n        };\n        return {\n          slot,\n          record: constant$1(record)\n        };\n      })();\n      const spec = sSpec(parts);\n      const partNames = parts.record();\n      const fieldParts = map$2(partNames, n => required({\n        name: n,\n        pname: getPartName(n)\n      }));\n      return composite$1(owner, schema$d, fieldParts, make$1, spec);\n    };\n    const make$1 = (detail, components) => {\n      const getSlotNames = _ => getAllPartNames(detail);\n      const getSlot = (container, key) => getPart(container, detail, key);\n      const onSlot = (f, def) => (container, key) => getPart(container, detail, key).map(slot => f(slot, key)).getOr(def);\n      const onSlots = f => (container, keys) => {\n        each$1(keys, key => f(container, key));\n      };\n      const doShowing = (comp, _key) => get$f(comp.element, 'aria-hidden') !== 'true';\n      const doShow = (comp, key) => {\n        if (!doShowing(comp)) {\n          const element = comp.element;\n          remove$6(element, 'display');\n          remove$7(element, 'aria-hidden');\n          emitWith(comp, slotVisibility(), {\n            name: key,\n            visible: true\n          });\n        }\n      };\n      const doHide = (comp, key) => {\n        if (doShowing(comp)) {\n          const element = comp.element;\n          set$8(element, 'display', 'none');\n          set$9(element, 'aria-hidden', 'true');\n          emitWith(comp, slotVisibility(), {\n            name: key,\n            visible: false\n          });\n        }\n      };\n      const isShowing = onSlot(doShowing, false);\n      const hideSlot = onSlot(doHide);\n      const hideSlots = onSlots(hideSlot);\n      const hideAllSlots = container => hideSlots(container, getSlotNames());\n      const showSlot = onSlot(doShow);\n      const apis = {\n        getSlotNames,\n        getSlot,\n        isShowing,\n        hideSlot,\n        hideAllSlots,\n        showSlot\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        behaviours: get$3(detail.slotBehaviours),\n        apis\n      };\n    };\n    const slotApis = map$1({\n      getSlotNames: (apis, c) => apis.getSlotNames(c),\n      getSlot: (apis, c, key) => apis.getSlot(c, key),\n      isShowing: (apis, c, key) => apis.isShowing(c, key),\n      hideSlot: (apis, c, key) => apis.hideSlot(c, key),\n      hideAllSlots: (apis, c) => apis.hideAllSlots(c),\n      showSlot: (apis, c, key) => apis.showSlot(c, key)\n    }, value => makeApi(value));\n    const SlotContainer = {\n      ...slotApis,\n      ...{ sketch }\n    };\n\n    const sidebarSchema = objOf([\n      optionalIcon,\n      optionalTooltip,\n      defaultedFunction('onShow', noop),\n      defaultedFunction('onHide', noop),\n      onSetup\n    ]);\n    const createSidebar = spec => asRaw('sidebar', sidebarSchema, spec);\n\n    const setup$8 = editor => {\n      const {sidebars} = editor.ui.registry.getAll();\n      each$1(keys(sidebars), name => {\n        const spec = sidebars[name];\n        const isActive = () => is$1(Optional.from(editor.queryCommandValue('ToggleSidebar')), name);\n        editor.ui.registry.addToggleButton(name, {\n          icon: spec.icon,\n          tooltip: spec.tooltip,\n          onAction: buttonApi => {\n            editor.execCommand('ToggleSidebar', false, name);\n            buttonApi.setActive(isActive());\n          },\n          onSetup: buttonApi => {\n            buttonApi.setActive(isActive());\n            const handleToggle = () => buttonApi.setActive(isActive());\n            editor.on('ToggleSidebar', handleToggle);\n            return () => {\n              editor.off('ToggleSidebar', handleToggle);\n            };\n          }\n        });\n      });\n    };\n    const getApi = comp => ({ element: () => comp.element.dom });\n    const makePanels = (parts, panelConfigs) => {\n      const specs = map$2(keys(panelConfigs), name => {\n        const spec = panelConfigs[name];\n        const bridged = getOrDie(createSidebar(spec));\n        return {\n          name,\n          getApi,\n          onSetup: bridged.onSetup,\n          onShow: bridged.onShow,\n          onHide: bridged.onHide\n        };\n      });\n      return map$2(specs, spec => {\n        const editorOffCell = Cell(noop);\n        return parts.slot(spec.name, {\n          dom: {\n            tag: 'div',\n            classes: ['tox-sidebar__pane']\n          },\n          behaviours: SimpleBehaviours.unnamedEvents([\n            onControlAttached(spec, editorOffCell),\n            onControlDetached(spec, editorOffCell),\n            run$1(slotVisibility(), (sidepanel, se) => {\n              const data = se.event;\n              const optSidePanelSpec = find$5(specs, config => config.name === data.name);\n              optSidePanelSpec.each(sidePanelSpec => {\n                const handler = data.visible ? sidePanelSpec.onShow : sidePanelSpec.onHide;\n                handler(sidePanelSpec.getApi(sidepanel));\n              });\n            })\n          ])\n        });\n      });\n    };\n    const makeSidebar = panelConfigs => SlotContainer.sketch(parts => ({\n      dom: {\n        tag: 'div',\n        classes: ['tox-sidebar__pane-container']\n      },\n      components: makePanels(parts, panelConfigs),\n      slotBehaviours: SimpleBehaviours.unnamedEvents([runOnAttached(slotContainer => SlotContainer.hideAllSlots(slotContainer))])\n    }));\n    const setSidebar = (sidebar, panelConfigs, showSidebar) => {\n      const optSlider = Composing.getCurrent(sidebar);\n      optSlider.each(slider => {\n        Replacing.set(slider, [makeSidebar(panelConfigs)]);\n        const configKey = showSidebar === null || showSidebar === void 0 ? void 0 : showSidebar.toLowerCase();\n        if (isString(configKey) && has$2(panelConfigs, configKey)) {\n          Composing.getCurrent(slider).each(slotContainer => {\n            SlotContainer.showSlot(slotContainer, configKey);\n            Sliding.immediateGrow(slider);\n            remove$6(slider.element, 'width');\n          });\n        }\n      });\n    };\n    const toggleSidebar = (sidebar, name) => {\n      const optSlider = Composing.getCurrent(sidebar);\n      optSlider.each(slider => {\n        const optSlotContainer = Composing.getCurrent(slider);\n        optSlotContainer.each(slotContainer => {\n          if (Sliding.hasGrown(slider)) {\n            if (SlotContainer.isShowing(slotContainer, name)) {\n              Sliding.shrink(slider);\n            } else {\n              SlotContainer.hideAllSlots(slotContainer);\n              SlotContainer.showSlot(slotContainer, name);\n            }\n          } else {\n            SlotContainer.hideAllSlots(slotContainer);\n            SlotContainer.showSlot(slotContainer, name);\n            Sliding.grow(slider);\n          }\n        });\n      });\n    };\n    const whichSidebar = sidebar => {\n      const optSlider = Composing.getCurrent(sidebar);\n      return optSlider.bind(slider => {\n        const sidebarOpen = Sliding.isGrowing(slider) || Sliding.hasGrown(slider);\n        if (sidebarOpen) {\n          const optSlotContainer = Composing.getCurrent(slider);\n          return optSlotContainer.bind(slotContainer => find$5(SlotContainer.getSlotNames(slotContainer), name => SlotContainer.isShowing(slotContainer, name)));\n        } else {\n          return Optional.none();\n        }\n      });\n    };\n    const fixSize = generate$6('FixSizeEvent');\n    const autoSize = generate$6('AutoSizeEvent');\n    const renderSidebar = spec => ({\n      uid: spec.uid,\n      dom: {\n        tag: 'div',\n        classes: ['tox-sidebar'],\n        attributes: { role: 'complementary' }\n      },\n      components: [{\n          dom: {\n            tag: 'div',\n            classes: ['tox-sidebar__slider']\n          },\n          components: [],\n          behaviours: derive$1([\n            Tabstopping.config({}),\n            Focusing.config({}),\n            Sliding.config({\n              dimension: { property: 'width' },\n              closedClass: 'tox-sidebar--sliding-closed',\n              openClass: 'tox-sidebar--sliding-open',\n              shrinkingClass: 'tox-sidebar--sliding-shrinking',\n              growingClass: 'tox-sidebar--sliding-growing',\n              onShrunk: slider => {\n                const optSlotContainer = Composing.getCurrent(slider);\n                optSlotContainer.each(SlotContainer.hideAllSlots);\n                emit(slider, autoSize);\n              },\n              onGrown: slider => {\n                emit(slider, autoSize);\n              },\n              onStartGrow: slider => {\n                emitWith(slider, fixSize, { width: getRaw(slider.element, 'width').getOr('') });\n              },\n              onStartShrink: slider => {\n                emitWith(slider, fixSize, { width: get$c(slider.element) + 'px' });\n              }\n            }),\n            Replacing.config({}),\n            Composing.config({\n              find: comp => {\n                const children = Replacing.contents(comp);\n                return head(children);\n              }\n            })\n          ])\n        }],\n      behaviours: derive$1([\n        ComposingConfigs.childAt(0),\n        config('sidebar-sliding-events', [\n          run$1(fixSize, (comp, se) => {\n            set$8(comp.element, 'width', se.event.width);\n          }),\n          run$1(autoSize, (comp, _se) => {\n            remove$6(comp.element, 'width');\n          })\n        ])\n      ])\n    });\n\n    const block = (component, config, state, getBusySpec) => {\n      set$9(component.element, 'aria-busy', true);\n      const root = config.getRoot(component).getOr(component);\n      const blockerBehaviours = derive$1([\n        Keying.config({\n          mode: 'special',\n          onTab: () => Optional.some(true),\n          onShiftTab: () => Optional.some(true)\n        }),\n        Focusing.config({})\n      ]);\n      const blockSpec = getBusySpec(root, blockerBehaviours);\n      const blocker = root.getSystem().build(blockSpec);\n      Replacing.append(root, premade(blocker));\n      if (blocker.hasConfigured(Keying) && config.focus) {\n        Keying.focusIn(blocker);\n      }\n      if (!state.isBlocked()) {\n        config.onBlock(component);\n      }\n      state.blockWith(() => Replacing.remove(root, blocker));\n    };\n    const unblock = (component, config, state) => {\n      remove$7(component.element, 'aria-busy');\n      if (state.isBlocked()) {\n        config.onUnblock(component);\n      }\n      state.clear();\n    };\n\n    var BlockingApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        block: block,\n        unblock: unblock\n    });\n\n    var BlockingSchema = [\n      defaultedFunction('getRoot', Optional.none),\n      defaultedBoolean('focus', true),\n      onHandler('onBlock'),\n      onHandler('onUnblock')\n    ];\n\n    const init$4 = () => {\n      const blocker = destroyable();\n      const blockWith = destroy => {\n        blocker.set({ destroy });\n      };\n      return nu$8({\n        readState: blocker.isSet,\n        blockWith,\n        clear: blocker.clear,\n        isBlocked: blocker.isSet\n      });\n    };\n\n    var BlockingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$4\n    });\n\n    const Blocking = create$4({\n      fields: BlockingSchema,\n      name: 'blocking',\n      apis: BlockingApis,\n      state: BlockingState\n    });\n\n    const getAttrs = elem => {\n      const attributes = elem.dom.attributes !== undefined ? elem.dom.attributes : [];\n      return foldl(attributes, (b, attr) => {\n        if (attr.name === 'class') {\n          return b;\n        } else {\n          return {\n            ...b,\n            [attr.name]: attr.value\n          };\n        }\n      }, {});\n    };\n    const getClasses = elem => Array.prototype.slice.call(elem.dom.classList, 0);\n    const fromHtml = html => {\n      const elem = SugarElement.fromHtml(html);\n      const children$1 = children(elem);\n      const attrs = getAttrs(elem);\n      const classes = getClasses(elem);\n      const contents = children$1.length === 0 ? {} : { innerHtml: get$9(elem) };\n      return {\n        tag: name$3(elem),\n        classes,\n        attributes: attrs,\n        ...contents\n      };\n    };\n\n    const getBusySpec$1 = providerBackstage => (_root, _behaviours) => ({\n      dom: {\n        tag: 'div',\n        attributes: {\n          'aria-label': providerBackstage.translate('Loading...'),\n          'tabindex': '0'\n        },\n        classes: ['tox-throbber__busy-spinner']\n      },\n      components: [{ dom: fromHtml('<div class=\"tox-spinner\"><div></div><div></div><div></div></div>') }]\n    });\n    const focusBusyComponent = throbber => Composing.getCurrent(throbber).each(comp => focus$3(comp.element));\n    const toggleEditorTabIndex = (editor, state) => {\n      const tabIndexAttr = 'tabindex';\n      const dataTabIndexAttr = `data-mce-${ tabIndexAttr }`;\n      Optional.from(editor.iframeElement).map(SugarElement.fromDom).each(iframe => {\n        if (state) {\n          getOpt(iframe, tabIndexAttr).each(tabIndex => set$9(iframe, dataTabIndexAttr, tabIndex));\n          set$9(iframe, tabIndexAttr, -1);\n        } else {\n          remove$7(iframe, tabIndexAttr);\n          getOpt(iframe, dataTabIndexAttr).each(tabIndex => {\n            set$9(iframe, tabIndexAttr, tabIndex);\n            remove$7(iframe, dataTabIndexAttr);\n          });\n        }\n      });\n    };\n    const toggleThrobber = (editor, comp, state, providerBackstage) => {\n      const element = comp.element;\n      toggleEditorTabIndex(editor, state);\n      if (state) {\n        Blocking.block(comp, getBusySpec$1(providerBackstage));\n        remove$6(element, 'display');\n        remove$7(element, 'aria-hidden');\n        if (editor.hasFocus()) {\n          focusBusyComponent(comp);\n        }\n      } else {\n        const throbberFocus = Composing.getCurrent(comp).exists(busyComp => hasFocus(busyComp.element));\n        Blocking.unblock(comp);\n        set$8(element, 'display', 'none');\n        set$9(element, 'aria-hidden', 'true');\n        if (throbberFocus) {\n          editor.focus();\n        }\n      }\n    };\n    const renderThrobber = spec => ({\n      uid: spec.uid,\n      dom: {\n        tag: 'div',\n        attributes: { 'aria-hidden': 'true' },\n        classes: ['tox-throbber'],\n        styles: { display: 'none' }\n      },\n      behaviours: derive$1([\n        Replacing.config({}),\n        Blocking.config({ focus: false }),\n        Composing.config({ find: comp => head(comp.components()) })\n      ]),\n      components: []\n    });\n    const isFocusEvent = event => event.type === 'focusin';\n    const isPasteBinTarget = event => {\n      if (isFocusEvent(event)) {\n        const node = event.composed ? head(event.composedPath()) : Optional.from(event.target);\n        return node.map(SugarElement.fromDom).filter(isElement$1).exists(targetElm => has(targetElm, 'mce-pastebin'));\n      } else {\n        return false;\n      }\n    };\n    const setup$7 = (editor, lazyThrobber, sharedBackstage) => {\n      const throbberState = Cell(false);\n      const timer = value$2();\n      const stealFocus = e => {\n        if (throbberState.get() && !isPasteBinTarget(e)) {\n          e.preventDefault();\n          focusBusyComponent(lazyThrobber());\n          editor.editorManager.setActive(editor);\n        }\n      };\n      if (!editor.inline) {\n        editor.on('PreInit', () => {\n          editor.dom.bind(editor.getWin(), 'focusin', stealFocus);\n          editor.on('BeforeExecCommand', e => {\n            if (e.command.toLowerCase() === 'mcefocus' && e.value !== true) {\n              stealFocus(e);\n            }\n          });\n        });\n      }\n      const toggle = state => {\n        if (state !== throbberState.get()) {\n          throbberState.set(state);\n          toggleThrobber(editor, lazyThrobber(), state, sharedBackstage.providers);\n          fireAfterProgressState(editor, state);\n        }\n      };\n      editor.on('ProgressState', e => {\n        timer.on(clearTimeout);\n        if (isNumber(e.time)) {\n          const timerId = global$9.setEditorTimeout(editor, () => toggle(e.state), e.time);\n          timer.set(timerId);\n        } else {\n          toggle(e.state);\n          timer.clear();\n        }\n      });\n    };\n\n    const generate$1 = (xs, f) => {\n      const init = {\n        len: 0,\n        list: []\n      };\n      const r = foldl(xs, (b, a) => {\n        const value = f(a, b.len);\n        return value.fold(constant$1(b), v => ({\n          len: v.finish,\n          list: b.list.concat([v])\n        }));\n      }, init);\n      return r.list;\n    };\n\n    const output = (within, extra, withinWidth) => ({\n      within,\n      extra,\n      withinWidth\n    });\n    const apportion = (units, total, len) => {\n      const parray = generate$1(units, (unit, current) => {\n        const width = len(unit);\n        return Optional.some({\n          element: unit,\n          start: current,\n          finish: current + width,\n          width\n        });\n      });\n      const within = filter$2(parray, unit => unit.finish <= total);\n      const withinWidth = foldr(within, (acc, el) => acc + el.width, 0);\n      const extra = parray.slice(within.length);\n      return {\n        within,\n        extra,\n        withinWidth\n      };\n    };\n    const toUnit = parray => map$2(parray, unit => unit.element);\n    const fitLast = (within, extra, withinWidth) => {\n      const fits = toUnit(within.concat(extra));\n      return output(fits, [], withinWidth);\n    };\n    const overflow = (within, extra, overflower, withinWidth) => {\n      const fits = toUnit(within).concat([overflower]);\n      return output(fits, toUnit(extra), withinWidth);\n    };\n    const fitAll = (within, extra, withinWidth) => output(toUnit(within), [], withinWidth);\n    const tryFit = (total, units, len) => {\n      const divide = apportion(units, total, len);\n      return divide.extra.length === 0 ? Optional.some(divide) : Optional.none();\n    };\n    const partition = (total, units, len, overflower) => {\n      const divide = tryFit(total, units, len).getOrThunk(() => apportion(units, total - len(overflower), len));\n      const within = divide.within;\n      const extra = divide.extra;\n      const withinWidth = divide.withinWidth;\n      if (extra.length === 1 && extra[0].width <= len(overflower)) {\n        return fitLast(within, extra, withinWidth);\n      } else if (extra.length >= 1) {\n        return overflow(within, extra, overflower, withinWidth);\n      } else {\n        return fitAll(within, extra, withinWidth);\n      }\n    };\n\n    const setGroups$1 = (toolbar, storedGroups) => {\n      const bGroups = map$2(storedGroups, g => premade(g));\n      Toolbar.setGroups(toolbar, bGroups);\n    };\n    const findFocusedComp = comps => findMap(comps, comp => search(comp.element).bind(focusedElm => comp.getSystem().getByDom(focusedElm).toOptional()));\n    const refresh$2 = (toolbar, detail, setOverflow) => {\n      const builtGroups = detail.builtGroups.get();\n      if (builtGroups.length === 0) {\n        return;\n      }\n      const primary = getPartOrDie(toolbar, detail, 'primary');\n      const overflowGroup = Coupling.getCoupled(toolbar, 'overflowGroup');\n      set$8(primary.element, 'visibility', 'hidden');\n      const groups = builtGroups.concat([overflowGroup]);\n      const focusedComp = findFocusedComp(groups);\n      setOverflow([]);\n      setGroups$1(primary, groups);\n      const availableWidth = get$c(primary.element);\n      const overflows = partition(availableWidth, detail.builtGroups.get(), comp => get$c(comp.element), overflowGroup);\n      if (overflows.extra.length === 0) {\n        Replacing.remove(primary, overflowGroup);\n        setOverflow([]);\n      } else {\n        setGroups$1(primary, overflows.within);\n        setOverflow(overflows.extra);\n      }\n      remove$6(primary.element, 'visibility');\n      reflow(primary.element);\n      focusedComp.each(Focusing.focus);\n    };\n\n    const schema$c = constant$1([\n      field('splitToolbarBehaviours', [Coupling]),\n      customField('builtGroups', () => Cell([]))\n    ]);\n\n    const schema$b = constant$1([\n      markers$1(['overflowToggledClass']),\n      optionFunction('getOverflowBounds'),\n      required$1('lazySink'),\n      customField('overflowGroups', () => Cell([])),\n      onHandler('onOpened'),\n      onHandler('onClosed')\n    ].concat(schema$c()));\n    const parts$7 = constant$1([\n      required({\n        factory: Toolbar,\n        schema: schema$e(),\n        name: 'primary'\n      }),\n      external({\n        schema: schema$e(),\n        name: 'overflow'\n      }),\n      external({ name: 'overflow-button' }),\n      external({ name: 'overflow-group' })\n    ]);\n\n    const expandable = constant$1((element, available) => {\n      setMax(element, Math.floor(available));\n    });\n\n    const schema$a = constant$1([\n      markers$1(['toggledClass']),\n      required$1('lazySink'),\n      requiredFunction('fetch'),\n      optionFunction('getBounds'),\n      optionObjOf('fireDismissalEventInstead', [defaulted('event', dismissRequested())]),\n      schema$y(),\n      onHandler('onToggled')\n    ]);\n    const parts$6 = constant$1([\n      external({\n        name: 'button',\n        overrides: detail => ({\n          dom: { attributes: { 'aria-haspopup': 'true' } },\n          buttonBehaviours: derive$1([Toggling.config({\n              toggleClass: detail.markers.toggledClass,\n              aria: { mode: 'expanded' },\n              toggleOnExecute: false,\n              onToggled: detail.onToggled\n            })])\n        })\n      }),\n      external({\n        factory: Toolbar,\n        schema: schema$e(),\n        name: 'toolbar',\n        overrides: detail => {\n          return {\n            toolbarBehaviours: derive$1([Keying.config({\n                mode: 'cyclic',\n                onEscape: comp => {\n                  getPart(comp, detail, 'button').each(Focusing.focus);\n                  return Optional.none();\n                }\n              })])\n          };\n        }\n      })\n    ]);\n\n    const toggle = (button, externals) => {\n      const toolbarSandbox = Coupling.getCoupled(button, 'toolbarSandbox');\n      if (Sandboxing.isOpen(toolbarSandbox)) {\n        Sandboxing.close(toolbarSandbox);\n      } else {\n        Sandboxing.open(toolbarSandbox, externals.toolbar());\n      }\n    };\n    const position = (button, toolbar, detail, layouts) => {\n      const bounds = detail.getBounds.map(bounder => bounder());\n      const sink = detail.lazySink(button).getOrDie();\n      Positioning.positionWithinBounds(sink, toolbar, {\n        anchor: {\n          type: 'hotspot',\n          hotspot: button,\n          layouts,\n          overrides: { maxWidthFunction: expandable() }\n        }\n      }, bounds);\n    };\n    const setGroups = (button, toolbar, detail, layouts, groups) => {\n      Toolbar.setGroups(toolbar, groups);\n      position(button, toolbar, detail, layouts);\n      Toggling.on(button);\n    };\n    const makeSandbox = (button, spec, detail) => {\n      const ariaControls = manager();\n      const onOpen = (sandbox, toolbar) => {\n        detail.fetch().get(groups => {\n          setGroups(button, toolbar, detail, spec.layouts, groups);\n          ariaControls.link(button.element);\n          Keying.focusIn(toolbar);\n        });\n      };\n      const onClose = () => {\n        Toggling.off(button);\n        Focusing.focus(button);\n        ariaControls.unlink(button.element);\n      };\n      return {\n        dom: {\n          tag: 'div',\n          attributes: { id: ariaControls.id }\n        },\n        behaviours: derive$1([\n          Keying.config({\n            mode: 'special',\n            onEscape: comp => {\n              Sandboxing.close(comp);\n              return Optional.some(true);\n            }\n          }),\n          Sandboxing.config({\n            onOpen,\n            onClose,\n            isPartOf: (container, data, queryElem) => {\n              return isPartOf$1(data, queryElem) || isPartOf$1(button, queryElem);\n            },\n            getAttachPoint: () => {\n              return detail.lazySink(button).getOrDie();\n            }\n          }),\n          Receiving.config({\n            channels: {\n              ...receivingChannel$1({\n                isExtraPart: never,\n                ...detail.fireDismissalEventInstead.map(fe => ({ fireEventInstead: { event: fe.event } })).getOr({})\n              }),\n              ...receivingChannel({\n                doReposition: () => {\n                  Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each(toolbar => {\n                    position(button, toolbar, detail, spec.layouts);\n                  });\n                }\n              })\n            }\n          })\n        ])\n      };\n    };\n    const factory$c = (detail, components, spec, externals) => ({\n      ...Button.sketch({\n        ...externals.button(),\n        action: button => {\n          toggle(button, externals);\n        },\n        buttonBehaviours: SketchBehaviours.augment({ dump: externals.button().buttonBehaviours }, [Coupling.config({\n            others: {\n              toolbarSandbox: button => {\n                return makeSandbox(button, spec, detail);\n              }\n            }\n          })])\n      }),\n      apis: {\n        setGroups: (button, groups) => {\n          Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each(toolbar => {\n            setGroups(button, toolbar, detail, spec.layouts, groups);\n          });\n        },\n        reposition: button => {\n          Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox')).each(toolbar => {\n            position(button, toolbar, detail, spec.layouts);\n          });\n        },\n        toggle: button => {\n          toggle(button, externals);\n        },\n        getToolbar: button => {\n          return Sandboxing.getState(Coupling.getCoupled(button, 'toolbarSandbox'));\n        },\n        isOpen: button => {\n          return Sandboxing.isOpen(Coupling.getCoupled(button, 'toolbarSandbox'));\n        }\n      }\n    });\n    const FloatingToolbarButton = composite({\n      name: 'FloatingToolbarButton',\n      factory: factory$c,\n      configFields: schema$a(),\n      partFields: parts$6(),\n      apis: {\n        setGroups: (apis, button, groups) => {\n          apis.setGroups(button, groups);\n        },\n        reposition: (apis, button) => {\n          apis.reposition(button);\n        },\n        toggle: (apis, button) => {\n          apis.toggle(button);\n        },\n        getToolbar: (apis, button) => apis.getToolbar(button),\n        isOpen: (apis, button) => apis.isOpen(button)\n      }\n    });\n\n    const schema$9 = constant$1([\n      required$1('items'),\n      markers$1(['itemSelector']),\n      field('tgroupBehaviours', [Keying])\n    ]);\n    const parts$5 = constant$1([group({\n        name: 'items',\n        unit: 'item'\n      })]);\n\n    const factory$b = (detail, components, _spec, _externals) => ({\n      uid: detail.uid,\n      dom: detail.dom,\n      components,\n      behaviours: augment(detail.tgroupBehaviours, [Keying.config({\n          mode: 'flow',\n          selector: detail.markers.itemSelector\n        })]),\n      domModification: { attributes: { role: 'toolbar' } }\n    });\n    const ToolbarGroup = composite({\n      name: 'ToolbarGroup',\n      configFields: schema$9(),\n      partFields: parts$5(),\n      factory: factory$b\n    });\n\n    const buildGroups = comps => map$2(comps, g => premade(g));\n    const refresh$1 = (toolbar, memFloatingToolbarButton, detail) => {\n      refresh$2(toolbar, detail, overflowGroups => {\n        detail.overflowGroups.set(overflowGroups);\n        memFloatingToolbarButton.getOpt(toolbar).each(floatingToolbarButton => {\n          FloatingToolbarButton.setGroups(floatingToolbarButton, buildGroups(overflowGroups));\n        });\n      });\n    };\n    const factory$a = (detail, components, spec, externals) => {\n      const memFloatingToolbarButton = record(FloatingToolbarButton.sketch({\n        fetch: () => Future.nu(resolve => {\n          resolve(buildGroups(detail.overflowGroups.get()));\n        }),\n        layouts: {\n          onLtr: () => [\n            southwest$2,\n            southeast$2\n          ],\n          onRtl: () => [\n            southeast$2,\n            southwest$2\n          ],\n          onBottomLtr: () => [\n            northwest$2,\n            northeast$2\n          ],\n          onBottomRtl: () => [\n            northeast$2,\n            northwest$2\n          ]\n        },\n        getBounds: spec.getOverflowBounds,\n        lazySink: detail.lazySink,\n        fireDismissalEventInstead: {},\n        markers: { toggledClass: detail.markers.overflowToggledClass },\n        parts: {\n          button: externals['overflow-button'](),\n          toolbar: externals.overflow()\n        },\n        onToggled: (comp, state) => detail[state ? 'onOpened' : 'onClosed'](comp)\n      }));\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        behaviours: augment(detail.splitToolbarBehaviours, [Coupling.config({\n            others: {\n              overflowGroup: () => {\n                return ToolbarGroup.sketch({\n                  ...externals['overflow-group'](),\n                  items: [memFloatingToolbarButton.asSpec()]\n                });\n              }\n            }\n          })]),\n        apis: {\n          setGroups: (toolbar, groups) => {\n            detail.builtGroups.set(map$2(groups, toolbar.getSystem().build));\n            refresh$1(toolbar, memFloatingToolbarButton, detail);\n          },\n          refresh: toolbar => refresh$1(toolbar, memFloatingToolbarButton, detail),\n          toggle: toolbar => {\n            memFloatingToolbarButton.getOpt(toolbar).each(floatingToolbarButton => {\n              FloatingToolbarButton.toggle(floatingToolbarButton);\n            });\n          },\n          isOpen: toolbar => memFloatingToolbarButton.getOpt(toolbar).map(FloatingToolbarButton.isOpen).getOr(false),\n          reposition: toolbar => {\n            memFloatingToolbarButton.getOpt(toolbar).each(floatingToolbarButton => {\n              FloatingToolbarButton.reposition(floatingToolbarButton);\n            });\n          },\n          getOverflow: toolbar => memFloatingToolbarButton.getOpt(toolbar).bind(FloatingToolbarButton.getToolbar)\n        },\n        domModification: { attributes: { role: 'group' } }\n      };\n    };\n    const SplitFloatingToolbar = composite({\n      name: 'SplitFloatingToolbar',\n      configFields: schema$b(),\n      partFields: parts$7(),\n      factory: factory$a,\n      apis: {\n        setGroups: (apis, toolbar, groups) => {\n          apis.setGroups(toolbar, groups);\n        },\n        refresh: (apis, toolbar) => {\n          apis.refresh(toolbar);\n        },\n        reposition: (apis, toolbar) => {\n          apis.reposition(toolbar);\n        },\n        toggle: (apis, toolbar) => {\n          apis.toggle(toolbar);\n        },\n        isOpen: (apis, toolbar) => apis.isOpen(toolbar),\n        getOverflow: (apis, toolbar) => apis.getOverflow(toolbar)\n      }\n    });\n\n    const schema$8 = constant$1([\n      markers$1([\n        'closedClass',\n        'openClass',\n        'shrinkingClass',\n        'growingClass',\n        'overflowToggledClass'\n      ]),\n      onHandler('onOpened'),\n      onHandler('onClosed')\n    ].concat(schema$c()));\n    const parts$4 = constant$1([\n      required({\n        factory: Toolbar,\n        schema: schema$e(),\n        name: 'primary'\n      }),\n      required({\n        factory: Toolbar,\n        schema: schema$e(),\n        name: 'overflow',\n        overrides: detail => {\n          return {\n            toolbarBehaviours: derive$1([\n              Sliding.config({\n                dimension: { property: 'height' },\n                closedClass: detail.markers.closedClass,\n                openClass: detail.markers.openClass,\n                shrinkingClass: detail.markers.shrinkingClass,\n                growingClass: detail.markers.growingClass,\n                onShrunk: comp => {\n                  getPart(comp, detail, 'overflow-button').each(button => {\n                    Toggling.off(button);\n                    Focusing.focus(button);\n                  });\n                  detail.onClosed(comp);\n                },\n                onGrown: comp => {\n                  Keying.focusIn(comp);\n                  detail.onOpened(comp);\n                },\n                onStartGrow: comp => {\n                  getPart(comp, detail, 'overflow-button').each(Toggling.on);\n                }\n              }),\n              Keying.config({\n                mode: 'acyclic',\n                onEscape: comp => {\n                  getPart(comp, detail, 'overflow-button').each(Focusing.focus);\n                  return Optional.some(true);\n                }\n              })\n            ])\n          };\n        }\n      }),\n      external({\n        name: 'overflow-button',\n        overrides: detail => ({\n          buttonBehaviours: derive$1([Toggling.config({\n              toggleClass: detail.markers.overflowToggledClass,\n              aria: { mode: 'pressed' },\n              toggleOnExecute: false\n            })])\n        })\n      }),\n      external({ name: 'overflow-group' })\n    ]);\n\n    const isOpen = (toolbar, detail) => getPart(toolbar, detail, 'overflow').map(Sliding.hasGrown).getOr(false);\n    const toggleToolbar = (toolbar, detail) => {\n      getPart(toolbar, detail, 'overflow-button').bind(() => getPart(toolbar, detail, 'overflow')).each(overf => {\n        refresh(toolbar, detail);\n        Sliding.toggleGrow(overf);\n      });\n    };\n    const refresh = (toolbar, detail) => {\n      getPart(toolbar, detail, 'overflow').each(overflow => {\n        refresh$2(toolbar, detail, groups => {\n          const builtGroups = map$2(groups, g => premade(g));\n          Toolbar.setGroups(overflow, builtGroups);\n        });\n        getPart(toolbar, detail, 'overflow-button').each(button => {\n          if (Sliding.hasGrown(overflow)) {\n            Toggling.on(button);\n          }\n        });\n        Sliding.refresh(overflow);\n      });\n    };\n    const factory$9 = (detail, components, spec, externals) => {\n      const toolbarToggleEvent = 'alloy.toolbar.toggle';\n      const doSetGroups = (toolbar, groups) => {\n        const built = map$2(groups, toolbar.getSystem().build);\n        detail.builtGroups.set(built);\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        behaviours: augment(detail.splitToolbarBehaviours, [\n          Coupling.config({\n            others: {\n              overflowGroup: toolbar => {\n                return ToolbarGroup.sketch({\n                  ...externals['overflow-group'](),\n                  items: [Button.sketch({\n                      ...externals['overflow-button'](),\n                      action: _button => {\n                        emit(toolbar, toolbarToggleEvent);\n                      }\n                    })]\n                });\n              }\n            }\n          }),\n          config('toolbar-toggle-events', [run$1(toolbarToggleEvent, toolbar => {\n              toggleToolbar(toolbar, detail);\n            })])\n        ]),\n        apis: {\n          setGroups: (toolbar, groups) => {\n            doSetGroups(toolbar, groups);\n            refresh(toolbar, detail);\n          },\n          refresh: toolbar => refresh(toolbar, detail),\n          toggle: toolbar => toggleToolbar(toolbar, detail),\n          isOpen: toolbar => isOpen(toolbar, detail)\n        },\n        domModification: { attributes: { role: 'group' } }\n      };\n    };\n    const SplitSlidingToolbar = composite({\n      name: 'SplitSlidingToolbar',\n      configFields: schema$8(),\n      partFields: parts$4(),\n      factory: factory$9,\n      apis: {\n        setGroups: (apis, toolbar, groups) => {\n          apis.setGroups(toolbar, groups);\n        },\n        refresh: (apis, toolbar) => {\n          apis.refresh(toolbar);\n        },\n        toggle: (apis, toolbar) => {\n          apis.toggle(toolbar);\n        },\n        isOpen: (apis, toolbar) => apis.isOpen(toolbar)\n      }\n    });\n\n    const renderToolbarGroupCommon = toolbarGroup => {\n      const attributes = toolbarGroup.title.fold(() => ({}), title => ({ attributes: { title } }));\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-toolbar__group'],\n          ...attributes\n        },\n        components: [ToolbarGroup.parts.items({})],\n        items: toolbarGroup.items,\n        markers: { itemSelector: '*:not(.tox-split-button) > .tox-tbtn:not([disabled]), ' + '.tox-split-button:not([disabled]), ' + '.tox-toolbar-nav-js:not([disabled])' },\n        tgroupBehaviours: derive$1([\n          Tabstopping.config({}),\n          Focusing.config({})\n        ])\n      };\n    };\n    const renderToolbarGroup = toolbarGroup => ToolbarGroup.sketch(renderToolbarGroupCommon(toolbarGroup));\n    const getToolbarBehaviours = (toolbarSpec, modeName) => {\n      const onAttached = runOnAttached(component => {\n        const groups = map$2(toolbarSpec.initGroups, renderToolbarGroup);\n        Toolbar.setGroups(component, groups);\n      });\n      return derive$1([\n        DisablingConfigs.toolbarButton(toolbarSpec.providers.isDisabled),\n        receivingConfig(),\n        Keying.config({\n          mode: modeName,\n          onEscape: toolbarSpec.onEscape,\n          selector: '.tox-toolbar__group'\n        }),\n        config('toolbar-events', [onAttached])\n      ]);\n    };\n    const renderMoreToolbarCommon = toolbarSpec => {\n      const modeName = toolbarSpec.cyclicKeying ? 'cyclic' : 'acyclic';\n      return {\n        uid: toolbarSpec.uid,\n        dom: {\n          tag: 'div',\n          classes: ['tox-toolbar-overlord']\n        },\n        parts: {\n          'overflow-group': renderToolbarGroupCommon({\n            title: Optional.none(),\n            items: []\n          }),\n          'overflow-button': renderIconButtonSpec({\n            name: 'more',\n            icon: Optional.some('more-drawer'),\n            enabled: true,\n            tooltip: Optional.some('More...'),\n            primary: false,\n            buttonType: Optional.none(),\n            borderless: false\n          }, Optional.none(), toolbarSpec.providers)\n        },\n        splitToolbarBehaviours: getToolbarBehaviours(toolbarSpec, modeName)\n      };\n    };\n    const renderFloatingMoreToolbar = toolbarSpec => {\n      const baseSpec = renderMoreToolbarCommon(toolbarSpec);\n      const overflowXOffset = 4;\n      const primary = SplitFloatingToolbar.parts.primary({\n        dom: {\n          tag: 'div',\n          classes: ['tox-toolbar__primary']\n        }\n      });\n      return SplitFloatingToolbar.sketch({\n        ...baseSpec,\n        lazySink: toolbarSpec.getSink,\n        getOverflowBounds: () => {\n          const headerElem = toolbarSpec.moreDrawerData.lazyHeader().element;\n          const headerBounds = absolute$2(headerElem);\n          const docElem = documentElement(headerElem);\n          const docBounds = absolute$2(docElem);\n          const height = Math.max(docElem.dom.scrollHeight, docBounds.height);\n          return bounds(headerBounds.x + overflowXOffset, docBounds.y, headerBounds.width - overflowXOffset * 2, height);\n        },\n        parts: {\n          ...baseSpec.parts,\n          overflow: {\n            dom: {\n              tag: 'div',\n              classes: ['tox-toolbar__overflow'],\n              attributes: toolbarSpec.attributes\n            }\n          }\n        },\n        components: [primary],\n        markers: { overflowToggledClass: 'tox-tbtn--enabled' },\n        onOpened: comp => toolbarSpec.onToggled(comp, true),\n        onClosed: comp => toolbarSpec.onToggled(comp, false)\n      });\n    };\n    const renderSlidingMoreToolbar = toolbarSpec => {\n      const primary = SplitSlidingToolbar.parts.primary({\n        dom: {\n          tag: 'div',\n          classes: ['tox-toolbar__primary']\n        }\n      });\n      const overflow = SplitSlidingToolbar.parts.overflow({\n        dom: {\n          tag: 'div',\n          classes: ['tox-toolbar__overflow']\n        }\n      });\n      const baseSpec = renderMoreToolbarCommon(toolbarSpec);\n      return SplitSlidingToolbar.sketch({\n        ...baseSpec,\n        components: [\n          primary,\n          overflow\n        ],\n        markers: {\n          openClass: 'tox-toolbar__overflow--open',\n          closedClass: 'tox-toolbar__overflow--closed',\n          growingClass: 'tox-toolbar__overflow--growing',\n          shrinkingClass: 'tox-toolbar__overflow--shrinking',\n          overflowToggledClass: 'tox-tbtn--enabled'\n        },\n        onOpened: comp => {\n          comp.getSystem().broadcastOn([toolbarHeightChange()], { type: 'opened' });\n          toolbarSpec.onToggled(comp, true);\n        },\n        onClosed: comp => {\n          comp.getSystem().broadcastOn([toolbarHeightChange()], { type: 'closed' });\n          toolbarSpec.onToggled(comp, false);\n        }\n      });\n    };\n    const renderToolbar = toolbarSpec => {\n      const modeName = toolbarSpec.cyclicKeying ? 'cyclic' : 'acyclic';\n      return Toolbar.sketch({\n        uid: toolbarSpec.uid,\n        dom: {\n          tag: 'div',\n          classes: ['tox-toolbar'].concat(toolbarSpec.type === ToolbarMode$1.scrolling ? ['tox-toolbar--scrolling'] : [])\n        },\n        components: [Toolbar.parts.groups({})],\n        toolbarBehaviours: getToolbarBehaviours(toolbarSpec, modeName)\n      });\n    };\n\n    const normalButtonFields = [\n      requiredStringEnum('type', ['button']),\n      text$1,\n      defaultedStringEnum('buttonType', 'secondary', [\n        'primary',\n        'secondary'\n      ]),\n      requiredFunction('onAction')\n    ];\n    const viewButtonSchema = choose$1('type', { button: normalButtonFields });\n\n    const viewSchema = objOf([\n      defaultedArrayOf('buttons', [], viewButtonSchema),\n      requiredFunction('onShow'),\n      requiredFunction('onHide')\n    ]);\n    const createView = spec => asRaw('view', viewSchema, spec);\n\n    const renderViewButton = (spec, providers) => {\n      return renderButton({\n        text: spec.text,\n        enabled: true,\n        primary: false,\n        name: 'name',\n        icon: Optional.none(),\n        borderless: false,\n        buttonType: Optional.some(spec.buttonType)\n      }, _comp => {\n        spec.onAction();\n      }, providers);\n    };\n    const renderViewHeader = spec => {\n      const endButtons = map$2(spec.buttons, btnspec => renderViewButton(btnspec, spec.providers));\n      return {\n        uid: spec.uid,\n        dom: {\n          tag: 'div',\n          classes: ['tox-view__header']\n        },\n        components: [\n          Container.sketch({\n            dom: {\n              tag: 'div',\n              classes: ['tox-view__header-start']\n            },\n            components: []\n          }),\n          Container.sketch({\n            dom: {\n              tag: 'div',\n              classes: ['tox-view__header-end']\n            },\n            components: endButtons\n          })\n        ]\n      };\n    };\n    const renderViewPane = spec => {\n      return {\n        uid: spec.uid,\n        dom: {\n          tag: 'div',\n          classes: ['tox-view__pane']\n        }\n      };\n    };\n    const factory$8 = (detail, components, _spec, _externals) => {\n      const apis = {\n        getPane: comp => parts$a.getPart(comp, detail, 'pane'),\n        getOnShow: _comp => detail.viewConfig.onShow,\n        getOnHide: _comp => detail.viewConfig.onHide\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        apis\n      };\n    };\n    var View = composite({\n      name: 'silver.View',\n      configFields: [required$1('viewConfig')],\n      partFields: [\n        optional({\n          factory: { sketch: renderViewHeader },\n          schema: [\n            required$1('buttons'),\n            required$1('providers')\n          ],\n          name: 'header'\n        }),\n        optional({\n          factory: { sketch: renderViewPane },\n          schema: [],\n          name: 'pane'\n        })\n      ],\n      factory: factory$8,\n      apis: {\n        getPane: (apis, comp) => apis.getPane(comp),\n        getOnShow: (apis, comp) => apis.getOnShow(comp),\n        getOnHide: (apis, comp) => apis.getOnHide(comp)\n      }\n    });\n\n    const makeViews = (parts, viewConfigs, providers) => {\n      return mapToArray(viewConfigs, (config, name) => {\n        const internalViewConfig = getOrDie(createView(config));\n        return parts.slot(name, View.sketch({\n          dom: {\n            tag: 'div',\n            classes: ['tox-view']\n          },\n          viewConfig: internalViewConfig,\n          components: [\n            ...internalViewConfig.buttons.length > 0 ? [View.parts.header({\n                buttons: internalViewConfig.buttons,\n                providers\n              })] : [],\n            View.parts.pane({})\n          ]\n        }));\n      });\n    };\n    const makeSlotContainer = (viewConfigs, providers) => SlotContainer.sketch(parts => ({\n      dom: {\n        tag: 'div',\n        classes: ['tox-view-wrap__slot-container']\n      },\n      components: makeViews(parts, viewConfigs, providers),\n      slotBehaviours: SimpleBehaviours.unnamedEvents([runOnAttached(slotContainer => SlotContainer.hideAllSlots(slotContainer))])\n    }));\n    const getCurrentName = slotContainer => {\n      return find$5(SlotContainer.getSlotNames(slotContainer), name => SlotContainer.isShowing(slotContainer, name));\n    };\n    const hideContainer = comp => {\n      const element = comp.element;\n      set$8(element, 'display', 'none');\n      set$9(element, 'aria-hidden', 'true');\n    };\n    const showContainer = comp => {\n      const element = comp.element;\n      remove$6(element, 'display');\n      remove$7(element, 'aria-hidden');\n    };\n    const makeViewInstanceApi = slot => ({ getContainer: constant$1(slot) });\n    const runOnPaneWithInstanceApi = (slotContainer, name, get) => {\n      SlotContainer.getSlot(slotContainer, name).each(view => {\n        View.getPane(view).each(pane => {\n          const onCallback = get(view);\n          onCallback(makeViewInstanceApi(pane.element.dom));\n        });\n      });\n    };\n    const runOnShow = (slotContainer, name) => runOnPaneWithInstanceApi(slotContainer, name, View.getOnShow);\n    const runOnHide = (slotContainer, name) => runOnPaneWithInstanceApi(slotContainer, name, View.getOnHide);\n    const factory$7 = (detail, spec) => {\n      const setViews = (comp, viewConfigs) => {\n        Replacing.set(comp, [makeSlotContainer(viewConfigs, spec.backstage.shared.providers)]);\n      };\n      const whichView = comp => {\n        return Composing.getCurrent(comp).bind(getCurrentName);\n      };\n      const toggleView = (comp, showMainView, hideMainView, name) => {\n        return Composing.getCurrent(comp).exists(slotContainer => {\n          const optCurrentSlotName = getCurrentName(slotContainer);\n          const isTogglingCurrentView = optCurrentSlotName.exists(current => name === current);\n          const exists = SlotContainer.getSlot(slotContainer, name).isSome();\n          if (exists) {\n            SlotContainer.hideAllSlots(slotContainer);\n            if (!isTogglingCurrentView) {\n              hideMainView();\n              showContainer(comp);\n              SlotContainer.showSlot(slotContainer, name);\n              runOnShow(slotContainer, name);\n            } else {\n              hideContainer(comp);\n              showMainView();\n            }\n            optCurrentSlotName.each(prevName => runOnHide(slotContainer, prevName));\n          }\n          return exists;\n        });\n      };\n      const apis = {\n        setViews,\n        whichView,\n        toggleView\n      };\n      return {\n        uid: detail.uid,\n        dom: {\n          tag: 'div',\n          classes: ['tox-view-wrap'],\n          attributes: { 'aria-hidden': 'true' },\n          styles: { display: 'none' }\n        },\n        components: [],\n        behaviours: derive$1([\n          Replacing.config({}),\n          Composing.config({\n            find: comp => {\n              const children = Replacing.contents(comp);\n              return head(children);\n            }\n          })\n        ]),\n        apis\n      };\n    };\n    var ViewWrapper = single({\n      factory: factory$7,\n      name: 'silver.ViewWrapper',\n      configFields: [required$1('backstage')],\n      apis: {\n        setViews: (apis, comp, views) => apis.setViews(comp, views),\n        toggleView: (apis, comp, outerContainer, editorCont, name) => apis.toggleView(comp, outerContainer, editorCont, name),\n        whichView: (apis, comp) => apis.whichView(comp)\n      }\n    });\n\n    const factory$6 = (detail, components, _spec) => {\n      let toolbarDrawerOpenState = false;\n      const apis = {\n        getSocket: comp => {\n          return parts$a.getPart(comp, detail, 'socket');\n        },\n        setSidebar: (comp, panelConfigs, showSidebar) => {\n          parts$a.getPart(comp, detail, 'sidebar').each(sidebar => setSidebar(sidebar, panelConfigs, showSidebar));\n        },\n        toggleSidebar: (comp, name) => {\n          parts$a.getPart(comp, detail, 'sidebar').each(sidebar => toggleSidebar(sidebar, name));\n        },\n        whichSidebar: comp => {\n          return parts$a.getPart(comp, detail, 'sidebar').bind(whichSidebar).getOrNull();\n        },\n        getHeader: comp => {\n          return parts$a.getPart(comp, detail, 'header');\n        },\n        getToolbar: comp => {\n          return parts$a.getPart(comp, detail, 'toolbar');\n        },\n        setToolbar: (comp, groups) => {\n          parts$a.getPart(comp, detail, 'toolbar').each(toolbar => {\n            const renderedGroups = map$2(groups, renderToolbarGroup);\n            toolbar.getApis().setGroups(toolbar, renderedGroups);\n          });\n        },\n        setToolbars: (comp, toolbars) => {\n          parts$a.getPart(comp, detail, 'multiple-toolbar').each(mToolbar => {\n            const renderedToolbars = map$2(toolbars, g => map$2(g, renderToolbarGroup));\n            CustomList.setItems(mToolbar, renderedToolbars);\n          });\n        },\n        refreshToolbar: comp => {\n          const toolbar = parts$a.getPart(comp, detail, 'toolbar');\n          toolbar.each(toolbar => toolbar.getApis().refresh(toolbar));\n        },\n        toggleToolbarDrawer: comp => {\n          parts$a.getPart(comp, detail, 'toolbar').each(toolbar => {\n            mapFrom(toolbar.getApis().toggle, toggle => toggle(toolbar));\n          });\n        },\n        isToolbarDrawerToggled: comp => {\n          return parts$a.getPart(comp, detail, 'toolbar').bind(toolbar => Optional.from(toolbar.getApis().isOpen).map(isOpen => isOpen(toolbar))).getOr(false);\n        },\n        getThrobber: comp => {\n          return parts$a.getPart(comp, detail, 'throbber');\n        },\n        focusToolbar: comp => {\n          const optToolbar = parts$a.getPart(comp, detail, 'toolbar').orThunk(() => parts$a.getPart(comp, detail, 'multiple-toolbar'));\n          optToolbar.each(toolbar => {\n            Keying.focusIn(toolbar);\n          });\n        },\n        setMenubar: (comp, menus) => {\n          parts$a.getPart(comp, detail, 'menubar').each(menubar => {\n            SilverMenubar.setMenus(menubar, menus);\n          });\n        },\n        focusMenubar: comp => {\n          parts$a.getPart(comp, detail, 'menubar').each(menubar => {\n            SilverMenubar.focus(menubar);\n          });\n        },\n        setViews: (comp, viewConfigs) => {\n          parts$a.getPart(comp, detail, 'viewWrapper').each(wrapper => {\n            ViewWrapper.setViews(wrapper, viewConfigs);\n          });\n        },\n        toggleView: (comp, name) => {\n          return parts$a.getPart(comp, detail, 'viewWrapper').exists(wrapper => ViewWrapper.toggleView(wrapper, () => apis.showMainView(comp), () => apis.hideMainView(comp), name));\n        },\n        whichView: comp => {\n          return parts$a.getPart(comp, detail, 'viewWrapper').bind(ViewWrapper.whichView).getOrNull();\n        },\n        hideMainView: comp => {\n          toolbarDrawerOpenState = apis.isToolbarDrawerToggled(comp);\n          if (toolbarDrawerOpenState) {\n            apis.toggleToolbarDrawer(comp);\n          }\n          parts$a.getPart(comp, detail, 'editorContainer').each(editorContainer => {\n            const element = editorContainer.element;\n            set$8(element, 'display', 'none');\n            set$9(element, 'aria-hidden', 'true');\n          });\n        },\n        showMainView: comp => {\n          if (toolbarDrawerOpenState) {\n            apis.toggleToolbarDrawer(comp);\n          }\n          parts$a.getPart(comp, detail, 'editorContainer').each(editorContainer => {\n            const element = editorContainer.element;\n            remove$6(element, 'display');\n            remove$7(element, 'aria-hidden');\n          });\n        }\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        apis,\n        behaviours: detail.behaviours\n      };\n    };\n    const partMenubar = partType.optional({\n      factory: SilverMenubar,\n      name: 'menubar',\n      schema: [required$1('backstage')]\n    });\n    const toolbarFactory = spec => {\n      if (spec.type === ToolbarMode$1.sliding) {\n        return renderSlidingMoreToolbar;\n      } else if (spec.type === ToolbarMode$1.floating) {\n        return renderFloatingMoreToolbar;\n      } else {\n        return renderToolbar;\n      }\n    };\n    const partMultipleToolbar = partType.optional({\n      factory: {\n        sketch: spec => CustomList.sketch({\n          uid: spec.uid,\n          dom: spec.dom,\n          listBehaviours: derive$1([Keying.config({\n              mode: 'acyclic',\n              selector: '.tox-toolbar'\n            })]),\n          makeItem: () => renderToolbar({\n            type: spec.type,\n            uid: generate$6('multiple-toolbar-item'),\n            cyclicKeying: false,\n            initGroups: [],\n            providers: spec.providers,\n            onEscape: () => {\n              spec.onEscape();\n              return Optional.some(true);\n            }\n          }),\n          setupItem: (_mToolbar, tc, data, _index) => {\n            Toolbar.setGroups(tc, data);\n          },\n          shell: true\n        })\n      },\n      name: 'multiple-toolbar',\n      schema: [\n        required$1('dom'),\n        required$1('onEscape')\n      ]\n    });\n    const partToolbar = partType.optional({\n      factory: {\n        sketch: spec => {\n          const renderer = toolbarFactory(spec);\n          const toolbarSpec = {\n            type: spec.type,\n            uid: spec.uid,\n            onEscape: () => {\n              spec.onEscape();\n              return Optional.some(true);\n            },\n            onToggled: (_comp, state) => spec.onToolbarToggled(state),\n            cyclicKeying: false,\n            initGroups: [],\n            getSink: spec.getSink,\n            providers: spec.providers,\n            moreDrawerData: {\n              lazyToolbar: spec.lazyToolbar,\n              lazyMoreButton: spec.lazyMoreButton,\n              lazyHeader: spec.lazyHeader\n            },\n            attributes: spec.attributes\n          };\n          return renderer(toolbarSpec);\n        }\n      },\n      name: 'toolbar',\n      schema: [\n        required$1('dom'),\n        required$1('onEscape'),\n        required$1('getSink')\n      ]\n    });\n    const partHeader = partType.optional({\n      factory: { sketch: renderHeader },\n      name: 'header',\n      schema: [required$1('dom')]\n    });\n    const partPromotion = partType.optional({\n      factory: { sketch: renderPromotion },\n      name: 'promotion',\n      schema: [required$1('dom')]\n    });\n    const partSocket = partType.optional({\n      name: 'socket',\n      schema: [required$1('dom')]\n    });\n    const partSidebar = partType.optional({\n      factory: { sketch: renderSidebar },\n      name: 'sidebar',\n      schema: [required$1('dom')]\n    });\n    const partThrobber = partType.optional({\n      factory: { sketch: renderThrobber },\n      name: 'throbber',\n      schema: [required$1('dom')]\n    });\n    const partViewWrapper = partType.optional({\n      factory: ViewWrapper,\n      name: 'viewWrapper',\n      schema: [required$1('backstage')]\n    });\n    const renderEditorContainer = spec => ({\n      uid: spec.uid,\n      dom: {\n        tag: 'div',\n        classes: ['tox-editor-container']\n      },\n      components: spec.components\n    });\n    const partEditorContainer = partType.optional({\n      factory: { sketch: renderEditorContainer },\n      name: 'editorContainer',\n      schema: []\n    });\n    var OuterContainer = composite({\n      name: 'OuterContainer',\n      factory: factory$6,\n      configFields: [\n        required$1('dom'),\n        required$1('behaviours')\n      ],\n      partFields: [\n        partHeader,\n        partMenubar,\n        partToolbar,\n        partMultipleToolbar,\n        partSocket,\n        partSidebar,\n        partPromotion,\n        partThrobber,\n        partViewWrapper,\n        partEditorContainer\n      ],\n      apis: {\n        getSocket: (apis, comp) => {\n          return apis.getSocket(comp);\n        },\n        setSidebar: (apis, comp, panelConfigs, showSidebar) => {\n          apis.setSidebar(comp, panelConfigs, showSidebar);\n        },\n        toggleSidebar: (apis, comp, name) => {\n          apis.toggleSidebar(comp, name);\n        },\n        whichSidebar: (apis, comp) => {\n          return apis.whichSidebar(comp);\n        },\n        getHeader: (apis, comp) => {\n          return apis.getHeader(comp);\n        },\n        getToolbar: (apis, comp) => {\n          return apis.getToolbar(comp);\n        },\n        setToolbar: (apis, comp, groups) => {\n          apis.setToolbar(comp, groups);\n        },\n        setToolbars: (apis, comp, toolbars) => {\n          apis.setToolbars(comp, toolbars);\n        },\n        refreshToolbar: (apis, comp) => {\n          return apis.refreshToolbar(comp);\n        },\n        toggleToolbarDrawer: (apis, comp) => {\n          apis.toggleToolbarDrawer(comp);\n        },\n        isToolbarDrawerToggled: (apis, comp) => {\n          return apis.isToolbarDrawerToggled(comp);\n        },\n        getThrobber: (apis, comp) => {\n          return apis.getThrobber(comp);\n        },\n        setMenubar: (apis, comp, menus) => {\n          apis.setMenubar(comp, menus);\n        },\n        focusMenubar: (apis, comp) => {\n          apis.focusMenubar(comp);\n        },\n        focusToolbar: (apis, comp) => {\n          apis.focusToolbar(comp);\n        },\n        setViews: (apis, comp, views) => {\n          apis.setViews(comp, views);\n        },\n        toggleView: (apis, comp, name) => {\n          return apis.toggleView(comp, name);\n        },\n        whichView: (apis, comp) => {\n          return apis.whichView(comp);\n        }\n      }\n    });\n\n    const defaultMenubar = 'file edit view insert format tools table help';\n    const defaultMenus = {\n      file: {\n        title: 'File',\n        items: 'newdocument restoredraft | preview | export print | deleteallconversations'\n      },\n      edit: {\n        title: 'Edit',\n        items: 'undo redo | cut copy paste pastetext | selectall | searchreplace'\n      },\n      view: {\n        title: 'View',\n        items: 'code | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments'\n      },\n      insert: {\n        title: 'Insert',\n        items: 'image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents footnotes | mergetags | insertdatetime'\n      },\n      format: {\n        title: 'Format',\n        items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat'\n      },\n      tools: {\n        title: 'Tools',\n        items: 'spellchecker spellcheckerlanguage | autocorrect capitalization | a11ycheck code wordcount'\n      },\n      table: {\n        title: 'Table',\n        items: 'inserttable | cell row column | advtablesort | tableprops deletetable'\n      },\n      help: {\n        title: 'Help',\n        items: 'help'\n      }\n    };\n    const make = (menu, registry, editor) => {\n      const removedMenuItems = getRemovedMenuItems(editor).split(/[ ,]/);\n      return {\n        text: menu.title,\n        getItems: () => bind$3(menu.items, i => {\n          const itemName = i.toLowerCase();\n          if (itemName.trim().length === 0) {\n            return [];\n          } else if (exists(removedMenuItems, removedMenuItem => removedMenuItem === itemName)) {\n            return [];\n          } else if (itemName === 'separator' || itemName === '|') {\n            return [{ type: 'separator' }];\n          } else if (registry.menuItems[itemName]) {\n            return [registry.menuItems[itemName]];\n          } else {\n            return [];\n          }\n        })\n      };\n    };\n    const parseItemsString = items => {\n      return items.split(' ');\n    };\n    const identifyMenus = (editor, registry) => {\n      const rawMenuData = {\n        ...defaultMenus,\n        ...registry.menus\n      };\n      const userDefinedMenus = keys(registry.menus).length > 0;\n      const menubar = registry.menubar === undefined || registry.menubar === true ? parseItemsString(defaultMenubar) : parseItemsString(registry.menubar === false ? '' : registry.menubar);\n      const validMenus = filter$2(menubar, menuName => {\n        const isDefaultMenu = has$2(defaultMenus, menuName);\n        if (userDefinedMenus) {\n          return isDefaultMenu || get$g(registry.menus, menuName).exists(menu => has$2(menu, 'items'));\n        } else {\n          return isDefaultMenu;\n        }\n      });\n      const menus = map$2(validMenus, menuName => {\n        const menuData = rawMenuData[menuName];\n        return make({\n          title: menuData.title,\n          items: parseItemsString(menuData.items)\n        }, registry, editor);\n      });\n      return filter$2(menus, menu => {\n        const isNotSeparator = item => isString(item) || item.type !== 'separator';\n        return menu.getItems().length > 0 && exists(menu.getItems(), isNotSeparator);\n      });\n    };\n\n    const fireSkinLoaded = editor => {\n      const done = () => {\n        editor._skinLoaded = true;\n        fireSkinLoaded$1(editor);\n      };\n      return () => {\n        if (editor.initialized) {\n          done();\n        } else {\n          editor.on('init', done);\n        }\n      };\n    };\n    const fireSkinLoadError = (editor, err) => () => fireSkinLoadError$1(editor, { message: err });\n\n    const loadStylesheet = (editor, stylesheetUrl, styleSheetLoader) => {\n      editor.on('remove', () => styleSheetLoader.unload(stylesheetUrl));\n      return styleSheetLoader.load(stylesheetUrl);\n    };\n    const loadUiSkins = (editor, skinUrl) => {\n      const skinUiCss = skinUrl + '/skin.min.css';\n      return loadStylesheet(editor, skinUiCss, editor.ui.styleSheetLoader);\n    };\n    const loadShadowDomUiSkins = (editor, skinUrl) => {\n      const isInShadowRoot$1 = isInShadowRoot(SugarElement.fromDom(editor.getElement()));\n      if (isInShadowRoot$1) {\n        const shadowDomSkinCss = skinUrl + '/skin.shadowdom.min.css';\n        return loadStylesheet(editor, shadowDomSkinCss, global$7.DOM.styleSheetLoader);\n      } else {\n        return Promise.resolve();\n      }\n    };\n    const loadSkin = (isInline, editor) => {\n      const skinUrl = getSkinUrl(editor);\n      if (skinUrl) {\n        editor.contentCSS.push(skinUrl + (isInline ? '/content.inline' : '/content') + '.min.css');\n      }\n      if (!isSkinDisabled(editor) && isString(skinUrl)) {\n        Promise.all([\n          loadUiSkins(editor, skinUrl),\n          loadShadowDomUiSkins(editor, skinUrl)\n        ]).then(fireSkinLoaded(editor), fireSkinLoadError(editor, 'Skin could not be loaded'));\n      } else {\n        fireSkinLoaded(editor)();\n      }\n    };\n    const iframe = curry(loadSkin, false);\n    const inline = curry(loadSkin, true);\n\n    const onSetupFormatToggle = (editor, name) => api => {\n      const boundCallback = unbindable();\n      const init = () => {\n        api.setActive(editor.formatter.match(name));\n        const binding = editor.formatter.formatChanged(name, api.setActive);\n        boundCallback.set(binding);\n      };\n      editor.initialized ? init() : editor.once('init', init);\n      return () => {\n        editor.off('init', init);\n        boundCallback.clear();\n      };\n    };\n    const onSetupEvent = (editor, event, f) => api => {\n      const handleEvent = () => f(api);\n      const init = () => {\n        f(api);\n        editor.on(event, handleEvent);\n      };\n      editor.initialized ? init() : editor.once('init', init);\n      return () => {\n        editor.off('init', init);\n        editor.off(event, handleEvent);\n      };\n    };\n    const onActionToggleFormat$1 = editor => rawItem => () => {\n      editor.undoManager.transact(() => {\n        editor.focus();\n        editor.execCommand('mceToggleFormat', false, rawItem.format);\n      });\n    };\n    const onActionExecCommand = (editor, command) => () => editor.execCommand(command);\n\n    const generateSelectItems = (_editor, backstage, spec) => {\n      const generateItem = (rawItem, response, invalid, value) => {\n        const translatedText = backstage.shared.providers.translate(rawItem.title);\n        if (rawItem.type === 'separator') {\n          return Optional.some({\n            type: 'separator',\n            text: translatedText\n          });\n        } else if (rawItem.type === 'submenu') {\n          const items = bind$3(rawItem.getStyleItems(), si => validate(si, response, value));\n          if (response === 0 && items.length <= 0) {\n            return Optional.none();\n          } else {\n            return Optional.some({\n              type: 'nestedmenuitem',\n              text: translatedText,\n              enabled: items.length > 0,\n              getSubmenuItems: () => bind$3(rawItem.getStyleItems(), si => validate(si, response, value))\n            });\n          }\n        } else {\n          return Optional.some({\n            type: 'togglemenuitem',\n            text: translatedText,\n            icon: rawItem.icon,\n            active: rawItem.isSelected(value),\n            enabled: !invalid,\n            onAction: spec.onAction(rawItem),\n            ...rawItem.getStylePreview().fold(() => ({}), preview => ({ meta: { style: preview } }))\n          });\n        }\n      };\n      const validate = (item, response, value) => {\n        const invalid = item.type === 'formatter' && spec.isInvalid(item);\n        if (response === 0) {\n          return invalid ? [] : generateItem(item, response, false, value).toArray();\n        } else {\n          return generateItem(item, response, invalid, value).toArray();\n        }\n      };\n      const validateItems = preItems => {\n        const value = spec.getCurrentValue();\n        const response = spec.shouldHide ? 0 : 1;\n        return bind$3(preItems, item => validate(item, response, value));\n      };\n      const getFetch = (backstage, getStyleItems) => (comp, callback) => {\n        const preItems = getStyleItems();\n        const items = validateItems(preItems);\n        const menu = build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {\n          isHorizontalMenu: false,\n          search: Optional.none()\n        });\n        callback(menu);\n      };\n      return {\n        validateItems,\n        getFetch\n      };\n    };\n    const createMenuItems = (editor, backstage, spec) => {\n      const dataset = spec.dataset;\n      const getStyleItems = dataset.type === 'basic' ? () => map$2(dataset.data, d => processBasic(d, spec.isSelectedFor, spec.getPreviewFor)) : dataset.getData;\n      return {\n        items: generateSelectItems(editor, backstage, spec),\n        getStyleItems\n      };\n    };\n    const createSelectButton = (editor, backstage, spec) => {\n      const {items, getStyleItems} = createMenuItems(editor, backstage, spec);\n      const getApi = comp => ({ getComponent: constant$1(comp) });\n      const onSetup = onSetupEvent(editor, 'NodeChange', api => {\n        const comp = api.getComponent();\n        spec.updateText(comp);\n      });\n      return renderCommonDropdown({\n        text: spec.icon.isSome() ? Optional.none() : spec.text,\n        icon: spec.icon,\n        tooltip: Optional.from(spec.tooltip),\n        role: Optional.none(),\n        fetch: items.getFetch(backstage, getStyleItems),\n        onSetup,\n        getApi,\n        columns: 1,\n        presets: 'normal',\n        classes: spec.icon.isSome() ? [] : ['bespoke'],\n        dropdownBehaviours: []\n      }, 'tox-tbtn', backstage.shared);\n    };\n\n    const process = rawFormats => map$2(rawFormats, item => {\n      let title = item, format = item;\n      const values = item.split('=');\n      if (values.length > 1) {\n        title = values[0];\n        format = values[1];\n      }\n      return {\n        title,\n        format\n      };\n    });\n    const buildBasicStaticDataset = data => ({\n      type: 'basic',\n      data\n    });\n    var Delimiter;\n    (function (Delimiter) {\n      Delimiter[Delimiter['SemiColon'] = 0] = 'SemiColon';\n      Delimiter[Delimiter['Space'] = 1] = 'Space';\n    }(Delimiter || (Delimiter = {})));\n    const split = (rawFormats, delimiter) => {\n      if (delimiter === Delimiter.SemiColon) {\n        return rawFormats.replace(/;$/, '').split(';');\n      } else {\n        return rawFormats.split(' ');\n      }\n    };\n    const buildBasicSettingsDataset = (editor, settingName, delimiter) => {\n      const rawFormats = editor.options.get(settingName);\n      const data = process(split(rawFormats, delimiter));\n      return {\n        type: 'basic',\n        data\n      };\n    };\n\n    const alignMenuItems = [\n      {\n        title: 'Left',\n        icon: 'align-left',\n        format: 'alignleft',\n        command: 'JustifyLeft'\n      },\n      {\n        title: 'Center',\n        icon: 'align-center',\n        format: 'aligncenter',\n        command: 'JustifyCenter'\n      },\n      {\n        title: 'Right',\n        icon: 'align-right',\n        format: 'alignright',\n        command: 'JustifyRight'\n      },\n      {\n        title: 'Justify',\n        icon: 'align-justify',\n        format: 'alignjustify',\n        command: 'JustifyFull'\n      }\n    ];\n    const getSpec$4 = editor => {\n      const getMatchingValue = () => find$5(alignMenuItems, item => editor.formatter.match(item.format));\n      const isSelectedFor = format => () => editor.formatter.match(format);\n      const getPreviewFor = _format => Optional.none;\n      const updateSelectMenuIcon = comp => {\n        const match = getMatchingValue();\n        const alignment = match.fold(constant$1('left'), item => item.title.toLowerCase());\n        emitWith(comp, updateMenuIcon, { icon: `align-${ alignment }` });\n      };\n      const dataset = buildBasicStaticDataset(alignMenuItems);\n      const onAction = rawItem => () => find$5(alignMenuItems, item => item.format === rawItem.format).each(item => editor.execCommand(item.command));\n      return {\n        tooltip: 'Align',\n        text: Optional.none(),\n        icon: Optional.some('align-left'),\n        isSelectedFor,\n        getCurrentValue: Optional.none,\n        getPreviewFor,\n        onAction,\n        updateText: updateSelectMenuIcon,\n        dataset,\n        shouldHide: false,\n        isInvalid: item => !editor.formatter.canApply(item.format)\n      };\n    };\n    const createAlignButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$4(editor));\n    const createAlignMenu = (editor, backstage) => {\n      const menuItems = createMenuItems(editor, backstage, getSpec$4(editor));\n      editor.ui.registry.addNestedMenuItem('align', {\n        text: backstage.shared.providers.translate('Align'),\n        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())\n      });\n    };\n\n    const findNearest = (editor, getStyles) => {\n      const styles = getStyles();\n      const formats = map$2(styles, style => style.format);\n      return Optional.from(editor.formatter.closest(formats)).bind(fmt => find$5(styles, data => data.format === fmt)).orThunk(() => someIf(editor.formatter.match('p'), {\n        title: 'Paragraph',\n        format: 'p'\n      }));\n    };\n\n    const getSpec$3 = editor => {\n      const fallbackFormat = 'Paragraph';\n      const isSelectedFor = format => () => editor.formatter.match(format);\n      const getPreviewFor = format => () => {\n        const fmt = editor.formatter.get(format);\n        if (fmt) {\n          return Optional.some({\n            tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',\n            styles: editor.dom.parseStyle(editor.formatter.getCssText(format))\n          });\n        } else {\n          return Optional.none();\n        }\n      };\n      const updateSelectMenuText = comp => {\n        const detectedFormat = findNearest(editor, () => dataset.data);\n        const text = detectedFormat.fold(constant$1(fallbackFormat), fmt => fmt.title);\n        emitWith(comp, updateMenuText, { text });\n      };\n      const dataset = buildBasicSettingsDataset(editor, 'block_formats', Delimiter.SemiColon);\n      return {\n        tooltip: 'Blocks',\n        text: Optional.some(fallbackFormat),\n        icon: Optional.none(),\n        isSelectedFor,\n        getCurrentValue: Optional.none,\n        getPreviewFor,\n        onAction: onActionToggleFormat$1(editor),\n        updateText: updateSelectMenuText,\n        dataset,\n        shouldHide: false,\n        isInvalid: item => !editor.formatter.canApply(item.format)\n      };\n    };\n    const createBlocksButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$3(editor));\n    const createBlocksMenu = (editor, backstage) => {\n      const menuItems = createMenuItems(editor, backstage, getSpec$3(editor));\n      editor.ui.registry.addNestedMenuItem('blocks', {\n        text: 'Blocks',\n        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())\n      });\n    };\n\n    const systemStackFonts = [\n      '-apple-system',\n      'Segoe UI',\n      'Roboto',\n      'Helvetica Neue',\n      'sans-serif'\n    ];\n    const splitFonts = fontFamily => {\n      const fonts = fontFamily.split(/\\s*,\\s*/);\n      return map$2(fonts, font => font.replace(/^['\"]+|['\"]+$/g, ''));\n    };\n    const isSystemFontStack = fontFamily => {\n      const matchesSystemStack = () => {\n        const fonts = splitFonts(fontFamily.toLowerCase());\n        return forall(systemStackFonts, font => fonts.indexOf(font.toLowerCase()) > -1);\n      };\n      return fontFamily.indexOf('-apple-system') === 0 && matchesSystemStack();\n    };\n    const getSpec$2 = editor => {\n      const systemFont = 'System Font';\n      const getMatchingValue = () => {\n        const getFirstFont = fontFamily => fontFamily ? splitFonts(fontFamily)[0] : '';\n        const fontFamily = editor.queryCommandValue('FontName');\n        const items = dataset.data;\n        const font = fontFamily ? fontFamily.toLowerCase() : '';\n        const matchOpt = find$5(items, item => {\n          const format = item.format;\n          return format.toLowerCase() === font || getFirstFont(format).toLowerCase() === getFirstFont(font).toLowerCase();\n        }).orThunk(() => {\n          return someIf(isSystemFontStack(font), {\n            title: systemFont,\n            format: font\n          });\n        });\n        return {\n          matchOpt,\n          font: fontFamily\n        };\n      };\n      const isSelectedFor = item => valueOpt => valueOpt.exists(value => value.format === item);\n      const getCurrentValue = () => {\n        const {matchOpt} = getMatchingValue();\n        return matchOpt;\n      };\n      const getPreviewFor = item => () => Optional.some({\n        tag: 'div',\n        styles: item.indexOf('dings') === -1 ? { 'font-family': item } : {}\n      });\n      const onAction = rawItem => () => {\n        editor.undoManager.transact(() => {\n          editor.focus();\n          editor.execCommand('FontName', false, rawItem.format);\n        });\n      };\n      const updateSelectMenuText = comp => {\n        const {matchOpt, font} = getMatchingValue();\n        const text = matchOpt.fold(constant$1(font), item => item.title);\n        emitWith(comp, updateMenuText, { text });\n      };\n      const dataset = buildBasicSettingsDataset(editor, 'font_family_formats', Delimiter.SemiColon);\n      return {\n        tooltip: 'Fonts',\n        text: Optional.some(systemFont),\n        icon: Optional.none(),\n        isSelectedFor,\n        getCurrentValue,\n        getPreviewFor,\n        onAction,\n        updateText: updateSelectMenuText,\n        dataset,\n        shouldHide: false,\n        isInvalid: never\n      };\n    };\n    const createFontFamilyButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$2(editor));\n    const createFontFamilyMenu = (editor, backstage) => {\n      const menuItems = createMenuItems(editor, backstage, getSpec$2(editor));\n      editor.ui.registry.addNestedMenuItem('fontfamily', {\n        text: backstage.shared.providers.translate('Fonts'),\n        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())\n      });\n    };\n\n    const legacyFontSizes = {\n      '8pt': '1',\n      '10pt': '2',\n      '12pt': '3',\n      '14pt': '4',\n      '18pt': '5',\n      '24pt': '6',\n      '36pt': '7'\n    };\n    const keywordFontSizes = {\n      'xx-small': '7pt',\n      'x-small': '8pt',\n      'small': '10pt',\n      'medium': '12pt',\n      'large': '14pt',\n      'x-large': '18pt',\n      'xx-large': '24pt'\n    };\n    const round = (number, precision) => {\n      const factor = Math.pow(10, precision);\n      return Math.round(number * factor) / factor;\n    };\n    const toPt = (fontSize, precision) => {\n      if (/[0-9.]+px$/.test(fontSize)) {\n        return round(parseInt(fontSize, 10) * 72 / 96, precision || 0) + 'pt';\n      } else {\n        return get$g(keywordFontSizes, fontSize).getOr(fontSize);\n      }\n    };\n    const toLegacy = fontSize => get$g(legacyFontSizes, fontSize).getOr('');\n    const getSpec$1 = editor => {\n      const getMatchingValue = () => {\n        let matchOpt = Optional.none();\n        const items = dataset.data;\n        const fontSize = editor.queryCommandValue('FontSize');\n        if (fontSize) {\n          for (let precision = 3; matchOpt.isNone() && precision >= 0; precision--) {\n            const pt = toPt(fontSize, precision);\n            const legacy = toLegacy(pt);\n            matchOpt = find$5(items, item => item.format === fontSize || item.format === pt || item.format === legacy);\n          }\n        }\n        return {\n          matchOpt,\n          size: fontSize\n        };\n      };\n      const isSelectedFor = item => valueOpt => valueOpt.exists(value => value.format === item);\n      const getCurrentValue = () => {\n        const {matchOpt} = getMatchingValue();\n        return matchOpt;\n      };\n      const getPreviewFor = constant$1(Optional.none);\n      const onAction = rawItem => () => {\n        editor.undoManager.transact(() => {\n          editor.focus();\n          editor.execCommand('FontSize', false, rawItem.format);\n        });\n      };\n      const updateSelectMenuText = comp => {\n        const {matchOpt, size} = getMatchingValue();\n        const text = matchOpt.fold(constant$1(size), match => match.title);\n        emitWith(comp, updateMenuText, { text });\n      };\n      const dataset = buildBasicSettingsDataset(editor, 'font_size_formats', Delimiter.Space);\n      return {\n        tooltip: 'Font sizes',\n        text: Optional.some('12pt'),\n        icon: Optional.none(),\n        isSelectedFor,\n        getPreviewFor,\n        getCurrentValue,\n        onAction,\n        updateText: updateSelectMenuText,\n        dataset,\n        shouldHide: false,\n        isInvalid: never\n      };\n    };\n    const createFontSizeButton = (editor, backstage) => createSelectButton(editor, backstage, getSpec$1(editor));\n    const createFontSizeMenu = (editor, backstage) => {\n      const menuItems = createMenuItems(editor, backstage, getSpec$1(editor));\n      editor.ui.registry.addNestedMenuItem('fontsize', {\n        text: 'Font sizes',\n        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())\n      });\n    };\n\n    const getSpec = (editor, dataset) => {\n      const fallbackFormat = 'Paragraph';\n      const isSelectedFor = format => () => editor.formatter.match(format);\n      const getPreviewFor = format => () => {\n        const fmt = editor.formatter.get(format);\n        return fmt !== undefined ? Optional.some({\n          tag: fmt.length > 0 ? fmt[0].inline || fmt[0].block || 'div' : 'div',\n          styles: editor.dom.parseStyle(editor.formatter.getCssText(format))\n        }) : Optional.none();\n      };\n      const updateSelectMenuText = comp => {\n        const getFormatItems = fmt => {\n          if (isNestedFormat(fmt)) {\n            return bind$3(fmt.items, getFormatItems);\n          } else if (isFormatReference(fmt)) {\n            return [{\n                title: fmt.title,\n                format: fmt.format\n              }];\n          } else {\n            return [];\n          }\n        };\n        const flattenedItems = bind$3(getStyleFormats(editor), getFormatItems);\n        const detectedFormat = findNearest(editor, constant$1(flattenedItems));\n        const text = detectedFormat.fold(constant$1(fallbackFormat), fmt => fmt.title);\n        emitWith(comp, updateMenuText, { text });\n      };\n      return {\n        tooltip: 'Formats',\n        text: Optional.some(fallbackFormat),\n        icon: Optional.none(),\n        isSelectedFor,\n        getCurrentValue: Optional.none,\n        getPreviewFor,\n        onAction: onActionToggleFormat$1(editor),\n        updateText: updateSelectMenuText,\n        shouldHide: shouldAutoHideStyleFormats(editor),\n        isInvalid: item => !editor.formatter.canApply(item.format),\n        dataset\n      };\n    };\n    const createStylesButton = (editor, backstage) => {\n      const dataset = {\n        type: 'advanced',\n        ...backstage.styles\n      };\n      return createSelectButton(editor, backstage, getSpec(editor, dataset));\n    };\n    const createStylesMenu = (editor, backstage) => {\n      const dataset = {\n        type: 'advanced',\n        ...backstage.styles\n      };\n      const menuItems = createMenuItems(editor, backstage, getSpec(editor, dataset));\n      editor.ui.registry.addNestedMenuItem('styles', {\n        text: 'Formats',\n        getSubmenuItems: () => menuItems.items.validateItems(menuItems.getStyleItems())\n      });\n    };\n\n    const events$3 = (reflectingConfig, reflectingState) => {\n      const update = (component, data) => {\n        reflectingConfig.updateState.each(updateState => {\n          const newState = updateState(component, data);\n          reflectingState.set(newState);\n        });\n        reflectingConfig.renderComponents.each(renderComponents => {\n          const newComponents = renderComponents(data, reflectingState.get());\n          const replacer = reflectingConfig.reuseDom ? withReuse : withoutReuse;\n          replacer(component, newComponents);\n        });\n      };\n      return derive$2([\n        run$1(receive(), (component, message) => {\n          const receivingData = message;\n          if (!receivingData.universal) {\n            const channel = reflectingConfig.channel;\n            if (contains$2(receivingData.channels, channel)) {\n              update(component, receivingData.data);\n            }\n          }\n        }),\n        runOnAttached((comp, _se) => {\n          reflectingConfig.initialData.each(rawData => {\n            update(comp, rawData);\n          });\n        })\n      ]);\n    };\n\n    var ActiveReflecting = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        events: events$3\n    });\n\n    const getState = (component, replaceConfig, reflectState) => reflectState;\n\n    var ReflectingApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        getState: getState\n    });\n\n    var ReflectingSchema = [\n      required$1('channel'),\n      option$3('renderComponents'),\n      option$3('updateState'),\n      option$3('initialData'),\n      defaultedBoolean('reuseDom', true)\n    ];\n\n    const init$3 = () => {\n      const cell = Cell(Optional.none());\n      const clear = () => cell.set(Optional.none());\n      const readState = () => cell.get().getOr('none');\n      return {\n        readState,\n        get: cell.get,\n        set: cell.set,\n        clear\n      };\n    };\n\n    var ReflectingState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init$3\n    });\n\n    const Reflecting = create$4({\n      fields: ReflectingSchema,\n      name: 'reflecting',\n      active: ActiveReflecting,\n      apis: ReflectingApis,\n      state: ReflectingState\n    });\n\n    const schema$7 = constant$1([\n      required$1('toggleClass'),\n      required$1('fetch'),\n      onStrictHandler('onExecute'),\n      defaulted('getHotspot', Optional.some),\n      defaulted('getAnchorOverrides', constant$1({})),\n      schema$y(),\n      onStrictHandler('onItemExecute'),\n      option$3('lazySink'),\n      required$1('dom'),\n      onHandler('onOpen'),\n      field('splitDropdownBehaviours', [\n        Coupling,\n        Keying,\n        Focusing\n      ]),\n      defaulted('matchWidth', false),\n      defaulted('useMinWidth', false),\n      defaulted('eventOrder', {}),\n      option$3('role')\n    ].concat(sandboxFields()));\n    const arrowPart = required({\n      factory: Button,\n      schema: [required$1('dom')],\n      name: 'arrow',\n      defaults: () => {\n        return { buttonBehaviours: derive$1([Focusing.revoke()]) };\n      },\n      overrides: detail => {\n        return {\n          dom: {\n            tag: 'span',\n            attributes: { role: 'presentation' }\n          },\n          action: arrow => {\n            arrow.getSystem().getByUid(detail.uid).each(emitExecute);\n          },\n          buttonBehaviours: derive$1([Toggling.config({\n              toggleOnExecute: false,\n              toggleClass: detail.toggleClass\n            })])\n        };\n      }\n    });\n    const buttonPart = required({\n      factory: Button,\n      schema: [required$1('dom')],\n      name: 'button',\n      defaults: () => {\n        return { buttonBehaviours: derive$1([Focusing.revoke()]) };\n      },\n      overrides: detail => {\n        return {\n          dom: {\n            tag: 'span',\n            attributes: { role: 'presentation' }\n          },\n          action: btn => {\n            btn.getSystem().getByUid(detail.uid).each(splitDropdown => {\n              detail.onExecute(splitDropdown, btn);\n            });\n          }\n        };\n      }\n    });\n    const parts$3 = constant$1([\n      arrowPart,\n      buttonPart,\n      optional({\n        factory: {\n          sketch: spec => {\n            return {\n              uid: spec.uid,\n              dom: {\n                tag: 'span',\n                styles: { display: 'none' },\n                attributes: { 'aria-hidden': 'true' },\n                innerHtml: spec.text\n              }\n            };\n          }\n        },\n        schema: [required$1('text')],\n        name: 'aria-descriptor'\n      }),\n      external({\n        schema: [tieredMenuMarkers()],\n        name: 'menu',\n        defaults: detail => {\n          return {\n            onExecute: (tmenu, item) => {\n              tmenu.getSystem().getByUid(detail.uid).each(splitDropdown => {\n                detail.onItemExecute(splitDropdown, tmenu, item);\n              });\n            }\n          };\n        }\n      }),\n      partType$1()\n    ]);\n\n    const factory$5 = (detail, components, spec, externals) => {\n      const switchToMenu = sandbox => {\n        Composing.getCurrent(sandbox).each(current => {\n          Highlighting.highlightFirst(current);\n          Keying.focusIn(current);\n        });\n      };\n      const action = component => {\n        const onOpenSync = switchToMenu;\n        togglePopup(detail, identity, component, externals, onOpenSync, HighlightOnOpen.HighlightMenuAndItem).get(noop);\n      };\n      const openMenu = comp => {\n        action(comp);\n        return Optional.some(true);\n      };\n      const executeOnButton = comp => {\n        const button = getPartOrDie(comp, detail, 'button');\n        emitExecute(button);\n        return Optional.some(true);\n      };\n      const buttonEvents = {\n        ...derive$2([runOnAttached((component, _simulatedEvent) => {\n            const ariaDescriptor = getPart(component, detail, 'aria-descriptor');\n            ariaDescriptor.each(descriptor => {\n              const descriptorId = generate$6('aria');\n              set$9(descriptor.element, 'id', descriptorId);\n              set$9(component.element, 'aria-describedby', descriptorId);\n            });\n          })]),\n        ...events$a(Optional.some(action))\n      };\n      const apis = {\n        repositionMenus: comp => {\n          if (Toggling.isOn(comp)) {\n            repositionMenus(comp);\n          }\n        }\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        apis,\n        eventOrder: {\n          ...detail.eventOrder,\n          [execute$5()]: [\n            'disabling',\n            'toggling',\n            'alloy.base.behaviour'\n          ]\n        },\n        events: buttonEvents,\n        behaviours: augment(detail.splitDropdownBehaviours, [\n          Coupling.config({\n            others: {\n              sandbox: hotspot => {\n                const arrow = getPartOrDie(hotspot, detail, 'arrow');\n                const extras = {\n                  onOpen: () => {\n                    Toggling.on(arrow);\n                    Toggling.on(hotspot);\n                  },\n                  onClose: () => {\n                    Toggling.off(arrow);\n                    Toggling.off(hotspot);\n                  }\n                };\n                return makeSandbox$1(detail, hotspot, extras);\n              }\n            }\n          }),\n          Keying.config({\n            mode: 'special',\n            onSpace: executeOnButton,\n            onEnter: executeOnButton,\n            onDown: openMenu\n          }),\n          Focusing.config({}),\n          Toggling.config({\n            toggleOnExecute: false,\n            aria: { mode: 'expanded' }\n          })\n        ]),\n        domModification: {\n          attributes: {\n            'role': detail.role.getOr('button'),\n            'aria-haspopup': true\n          }\n        }\n      };\n    };\n    const SplitDropdown = composite({\n      name: 'SplitDropdown',\n      configFields: schema$7(),\n      partFields: parts$3(),\n      factory: factory$5,\n      apis: { repositionMenus: (apis, comp) => apis.repositionMenus(comp) }\n    });\n\n    const getButtonApi = component => ({\n      isEnabled: () => !Disabling.isDisabled(component),\n      setEnabled: state => Disabling.set(component, !state)\n    });\n    const getToggleApi = component => ({\n      setActive: state => {\n        Toggling.set(component, state);\n      },\n      isActive: () => Toggling.isOn(component),\n      isEnabled: () => !Disabling.isDisabled(component),\n      setEnabled: state => Disabling.set(component, !state)\n    });\n    const getTooltipAttributes = (tooltip, providersBackstage) => tooltip.map(tooltip => ({\n      'aria-label': providersBackstage.translate(tooltip),\n      'title': providersBackstage.translate(tooltip)\n    })).getOr({});\n    const focusButtonEvent = generate$6('focus-button');\n    const renderCommonStructure = (icon, text, tooltip, receiver, behaviours, providersBackstage) => {\n      return {\n        dom: {\n          tag: 'button',\n          classes: ['tox-tbtn'].concat(text.isSome() ? ['tox-tbtn--select'] : []),\n          attributes: getTooltipAttributes(tooltip, providersBackstage)\n        },\n        components: componentRenderPipeline([\n          icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons)),\n          text.map(text => renderLabel(text, 'tox-tbtn', providersBackstage))\n        ]),\n        eventOrder: {\n          [mousedown()]: [\n            'focusing',\n            'alloy.base.behaviour',\n            'common-button-display-events'\n          ]\n        },\n        buttonBehaviours: derive$1([\n          DisablingConfigs.toolbarButton(providersBackstage.isDisabled),\n          receivingConfig(),\n          config('common-button-display-events', [run$1(mousedown(), (button, se) => {\n              se.event.prevent();\n              emit(button, focusButtonEvent);\n            })])\n        ].concat(receiver.map(r => Reflecting.config({\n          channel: r,\n          initialData: {\n            icon,\n            text\n          },\n          renderComponents: (data, _state) => componentRenderPipeline([\n            data.icon.map(iconName => renderIconFromPack(iconName, providersBackstage.icons)),\n            data.text.map(text => renderLabel(text, 'tox-tbtn', providersBackstage))\n          ])\n        })).toArray()).concat(behaviours.getOr([])))\n      };\n    };\n    const renderFloatingToolbarButton = (spec, backstage, identifyButtons, attributes) => {\n      const sharedBackstage = backstage.shared;\n      return FloatingToolbarButton.sketch({\n        lazySink: sharedBackstage.getSink,\n        fetch: () => Future.nu(resolve => {\n          resolve(map$2(identifyButtons(spec.items), renderToolbarGroup));\n        }),\n        markers: { toggledClass: 'tox-tbtn--enabled' },\n        parts: {\n          button: renderCommonStructure(spec.icon, spec.text, spec.tooltip, Optional.none(), Optional.none(), sharedBackstage.providers),\n          toolbar: {\n            dom: {\n              tag: 'div',\n              classes: ['tox-toolbar__overflow'],\n              attributes\n            }\n          }\n        }\n      });\n    };\n    const renderCommonToolbarButton = (spec, specialisation, providersBackstage) => {\n      const editorOffCell = Cell(noop);\n      const structure = renderCommonStructure(spec.icon, spec.text, spec.tooltip, Optional.none(), Optional.none(), providersBackstage);\n      return Button.sketch({\n        dom: structure.dom,\n        components: structure.components,\n        eventOrder: toolbarButtonEventOrder,\n        buttonBehaviours: derive$1([\n          config('toolbar-button-events', [\n            onToolbarButtonExecute({\n              onAction: spec.onAction,\n              getApi: specialisation.getApi\n            }),\n            onControlAttached(specialisation, editorOffCell),\n            onControlDetached(specialisation, editorOffCell)\n          ]),\n          DisablingConfigs.toolbarButton(() => !spec.enabled || providersBackstage.isDisabled()),\n          receivingConfig()\n        ].concat(specialisation.toolbarButtonBehaviours))\n      });\n    };\n    const renderToolbarButton = (spec, providersBackstage) => renderToolbarButtonWith(spec, providersBackstage, []);\n    const renderToolbarButtonWith = (spec, providersBackstage, bonusEvents) => renderCommonToolbarButton(spec, {\n      toolbarButtonBehaviours: bonusEvents.length > 0 ? [config('toolbarButtonWith', bonusEvents)] : [],\n      getApi: getButtonApi,\n      onSetup: spec.onSetup\n    }, providersBackstage);\n    const renderToolbarToggleButton = (spec, providersBackstage) => renderToolbarToggleButtonWith(spec, providersBackstage, []);\n    const renderToolbarToggleButtonWith = (spec, providersBackstage, bonusEvents) => renderCommonToolbarButton(spec, {\n      toolbarButtonBehaviours: [\n        Replacing.config({}),\n        Toggling.config({\n          toggleClass: 'tox-tbtn--enabled',\n          aria: { mode: 'pressed' },\n          toggleOnExecute: false\n        })\n      ].concat(bonusEvents.length > 0 ? [config('toolbarToggleButtonWith', bonusEvents)] : []),\n      getApi: getToggleApi,\n      onSetup: spec.onSetup\n    }, providersBackstage);\n    const fetchChoices = (getApi, spec, providersBackstage) => comp => Future.nu(callback => spec.fetch(callback)).map(items => Optional.from(createTieredDataFrom(deepMerge(createPartialChoiceMenu(generate$6('menu-value'), items, value => {\n      spec.onItemAction(getApi(comp), value);\n    }, spec.columns, spec.presets, ItemResponse$1.CLOSE_ON_EXECUTE, spec.select.getOr(never), providersBackstage), {\n      movement: deriveMenuMovement(spec.columns, spec.presets),\n      menuBehaviours: SimpleBehaviours.unnamedEvents(spec.columns !== 'auto' ? [] : [runOnAttached((comp, _se) => {\n          detectSize(comp, 4, classForPreset(spec.presets)).each(({numRows, numColumns}) => {\n            Keying.setGridSize(comp, numRows, numColumns);\n          });\n        })])\n    }))));\n    const renderSplitButton = (spec, sharedBackstage) => {\n      const displayChannel = generate$6('channel-update-split-dropdown-display');\n      const getApi = comp => ({\n        isEnabled: () => !Disabling.isDisabled(comp),\n        setEnabled: state => Disabling.set(comp, !state),\n        setIconFill: (id, value) => {\n          descendant(comp.element, 'svg path[id=\"' + id + '\"], rect[id=\"' + id + '\"]').each(underlinePath => {\n            set$9(underlinePath, 'fill', value);\n          });\n        },\n        setActive: state => {\n          set$9(comp.element, 'aria-pressed', state);\n          descendant(comp.element, 'span').each(button => {\n            comp.getSystem().getByDom(button).each(buttonComp => Toggling.set(buttonComp, state));\n          });\n        },\n        isActive: () => descendant(comp.element, 'span').exists(button => comp.getSystem().getByDom(button).exists(Toggling.isOn))\n      });\n      const editorOffCell = Cell(noop);\n      const specialisation = {\n        getApi,\n        onSetup: spec.onSetup\n      };\n      return SplitDropdown.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-split-button'],\n          attributes: {\n            'aria-pressed': false,\n            ...getTooltipAttributes(spec.tooltip, sharedBackstage.providers)\n          }\n        },\n        onExecute: button => {\n          spec.onAction(getApi(button));\n        },\n        onItemExecute: (_a, _b, _c) => {\n        },\n        splitDropdownBehaviours: derive$1([\n          DisablingConfigs.splitButton(sharedBackstage.providers.isDisabled),\n          receivingConfig(),\n          config('split-dropdown-events', [\n            run$1(focusButtonEvent, Focusing.focus),\n            onControlAttached(specialisation, editorOffCell),\n            onControlDetached(specialisation, editorOffCell)\n          ]),\n          Unselecting.config({})\n        ]),\n        eventOrder: {\n          [attachedToDom()]: [\n            'alloy.base.behaviour',\n            'split-dropdown-events'\n          ]\n        },\n        toggleClass: 'tox-tbtn--enabled',\n        lazySink: sharedBackstage.getSink,\n        fetch: fetchChoices(getApi, spec, sharedBackstage.providers),\n        parts: { menu: part(false, spec.columns, spec.presets) },\n        components: [\n          SplitDropdown.parts.button(renderCommonStructure(spec.icon, spec.text, Optional.none(), Optional.some(displayChannel), Optional.some([Toggling.config({\n              toggleClass: 'tox-tbtn--enabled',\n              toggleOnExecute: false\n            })]), sharedBackstage.providers)),\n          SplitDropdown.parts.arrow({\n            dom: {\n              tag: 'button',\n              classes: [\n                'tox-tbtn',\n                'tox-split-button__chevron'\n              ],\n              innerHtml: get$2('chevron-down', sharedBackstage.providers.icons)\n            },\n            buttonBehaviours: derive$1([\n              DisablingConfigs.splitButton(sharedBackstage.providers.isDisabled),\n              receivingConfig(),\n              addFocusableBehaviour()\n            ])\n          }),\n          SplitDropdown.parts['aria-descriptor']({ text: sharedBackstage.providers.translate('To open the popup, press Shift+Enter') })\n        ]\n      });\n    };\n\n    const defaultToolbar = [\n      {\n        name: 'history',\n        items: [\n          'undo',\n          'redo'\n        ]\n      },\n      {\n        name: 'styles',\n        items: ['styles']\n      },\n      {\n        name: 'formatting',\n        items: [\n          'bold',\n          'italic'\n        ]\n      },\n      {\n        name: 'alignment',\n        items: [\n          'alignleft',\n          'aligncenter',\n          'alignright',\n          'alignjustify'\n        ]\n      },\n      {\n        name: 'indentation',\n        items: [\n          'outdent',\n          'indent'\n        ]\n      },\n      {\n        name: 'permanent pen',\n        items: ['permanentpen']\n      },\n      {\n        name: 'comments',\n        items: ['addcomment']\n      }\n    ];\n    const renderFromBridge = (bridgeBuilder, render) => (spec, backstage, editor) => {\n      const internal = bridgeBuilder(spec).mapError(errInfo => formatError(errInfo)).getOrDie();\n      return render(internal, backstage, editor);\n    };\n    const types = {\n      button: renderFromBridge(createToolbarButton, (s, backstage) => renderToolbarButton(s, backstage.shared.providers)),\n      togglebutton: renderFromBridge(createToggleButton, (s, backstage) => renderToolbarToggleButton(s, backstage.shared.providers)),\n      menubutton: renderFromBridge(createMenuButton, (s, backstage) => renderMenuButton(s, 'tox-tbtn', backstage, Optional.none())),\n      splitbutton: renderFromBridge(createSplitButton, (s, backstage) => renderSplitButton(s, backstage.shared)),\n      grouptoolbarbutton: renderFromBridge(createGroupToolbarButton, (s, backstage, editor) => {\n        const buttons = editor.ui.registry.getAll().buttons;\n        const identify = toolbar => identifyButtons(editor, {\n          buttons,\n          toolbar,\n          allowToolbarGroups: false\n        }, backstage, Optional.none());\n        const attributes = { [Attribute]: backstage.shared.header.isPositionedAtTop() ? AttributeValue.TopToBottom : AttributeValue.BottomToTop };\n        switch (getToolbarMode(editor)) {\n        case ToolbarMode$1.floating:\n          return renderFloatingToolbarButton(s, backstage, identify, attributes);\n        default:\n          throw new Error('Toolbar groups are only supported when using floating toolbar mode');\n        }\n      })\n    };\n    const extractFrom = (spec, backstage, editor) => get$g(types, spec.type).fold(() => {\n      console.error('skipping button defined by', spec);\n      return Optional.none();\n    }, render => Optional.some(render(spec, backstage, editor)));\n    const bespokeButtons = {\n      styles: createStylesButton,\n      fontsize: createFontSizeButton,\n      fontfamily: createFontFamilyButton,\n      blocks: createBlocksButton,\n      align: createAlignButton\n    };\n    const removeUnusedDefaults = buttons => {\n      const filteredItemGroups = map$2(defaultToolbar, group => {\n        const items = filter$2(group.items, subItem => has$2(buttons, subItem) || has$2(bespokeButtons, subItem));\n        return {\n          name: group.name,\n          items\n        };\n      });\n      return filter$2(filteredItemGroups, group => group.items.length > 0);\n    };\n    const convertStringToolbar = strToolbar => {\n      const groupsStrings = strToolbar.split('|');\n      return map$2(groupsStrings, g => ({ items: g.trim().split(' ') }));\n    };\n    const isToolbarGroupSettingArray = toolbar => isArrayOf(toolbar, t => has$2(t, 'name') && has$2(t, 'items'));\n    const createToolbar = toolbarConfig => {\n      const toolbar = toolbarConfig.toolbar;\n      const buttons = toolbarConfig.buttons;\n      if (toolbar === false) {\n        return [];\n      } else if (toolbar === undefined || toolbar === true) {\n        return removeUnusedDefaults(buttons);\n      } else if (isString(toolbar)) {\n        return convertStringToolbar(toolbar);\n      } else if (isToolbarGroupSettingArray(toolbar)) {\n        return toolbar;\n      } else {\n        console.error('Toolbar type should be string, string[], boolean or ToolbarGroup[]');\n        return [];\n      }\n    };\n    const lookupButton = (editor, buttons, toolbarItem, allowToolbarGroups, backstage, prefixes) => get$g(buttons, toolbarItem.toLowerCase()).orThunk(() => prefixes.bind(ps => findMap(ps, prefix => get$g(buttons, prefix + toolbarItem.toLowerCase())))).fold(() => get$g(bespokeButtons, toolbarItem.toLowerCase()).map(r => r(editor, backstage)), spec => {\n      if (spec.type === 'grouptoolbarbutton' && !allowToolbarGroups) {\n        console.warn(`Ignoring the '${ toolbarItem }' toolbar button. Group toolbar buttons are only supported when using floating toolbar mode and cannot be nested.`);\n        return Optional.none();\n      } else {\n        return extractFrom(spec, backstage, editor);\n      }\n    });\n    const identifyButtons = (editor, toolbarConfig, backstage, prefixes) => {\n      const toolbarGroups = createToolbar(toolbarConfig);\n      const groups = map$2(toolbarGroups, group => {\n        const items = bind$3(group.items, toolbarItem => {\n          return toolbarItem.trim().length === 0 ? [] : lookupButton(editor, toolbarConfig.buttons, toolbarItem, toolbarConfig.allowToolbarGroups, backstage, prefixes).toArray();\n        });\n        return {\n          title: Optional.from(editor.translate(group.name)),\n          items\n        };\n      });\n      return filter$2(groups, group => group.items.length > 0);\n    };\n\n    const setToolbar = (editor, uiRefs, rawUiConfig, backstage) => {\n      const outerContainer = uiRefs.mainUi.outerContainer;\n      const toolbarConfig = rawUiConfig.toolbar;\n      const toolbarButtonsConfig = rawUiConfig.buttons;\n      if (isArrayOf(toolbarConfig, isString)) {\n        const toolbars = toolbarConfig.map(t => {\n          const config = {\n            toolbar: t,\n            buttons: toolbarButtonsConfig,\n            allowToolbarGroups: rawUiConfig.allowToolbarGroups\n          };\n          return identifyButtons(editor, config, backstage, Optional.none());\n        });\n        OuterContainer.setToolbars(outerContainer, toolbars);\n      } else {\n        OuterContainer.setToolbar(outerContainer, identifyButtons(editor, rawUiConfig, backstage, Optional.none()));\n      }\n    };\n\n    const detection = detect$1();\n    const isiOS12 = detection.os.isiOS() && detection.os.version.major <= 12;\n    const setupEvents$1 = (editor, uiRefs) => {\n      const {uiMotherships} = uiRefs;\n      const dom = editor.dom;\n      let contentWindow = editor.getWin();\n      const initialDocEle = editor.getDoc().documentElement;\n      const lastWindowDimensions = Cell(SugarPosition(contentWindow.innerWidth, contentWindow.innerHeight));\n      const lastDocumentDimensions = Cell(SugarPosition(initialDocEle.offsetWidth, initialDocEle.offsetHeight));\n      const resizeWindow = () => {\n        const outer = lastWindowDimensions.get();\n        if (outer.left !== contentWindow.innerWidth || outer.top !== contentWindow.innerHeight) {\n          lastWindowDimensions.set(SugarPosition(contentWindow.innerWidth, contentWindow.innerHeight));\n          fireResizeContent(editor);\n        }\n      };\n      const resizeDocument = () => {\n        const docEle = editor.getDoc().documentElement;\n        const inner = lastDocumentDimensions.get();\n        if (inner.left !== docEle.offsetWidth || inner.top !== docEle.offsetHeight) {\n          lastDocumentDimensions.set(SugarPosition(docEle.offsetWidth, docEle.offsetHeight));\n          fireResizeContent(editor);\n        }\n      };\n      const scroll = e => {\n        fireScrollContent(editor, e);\n      };\n      dom.bind(contentWindow, 'resize', resizeWindow);\n      dom.bind(contentWindow, 'scroll', scroll);\n      const elementLoad = capture(SugarElement.fromDom(editor.getBody()), 'load', resizeDocument);\n      editor.on('hide', () => {\n        each$1(uiMotherships, m => {\n          set$8(m.element, 'display', 'none');\n        });\n      });\n      editor.on('show', () => {\n        each$1(uiMotherships, m => {\n          remove$6(m.element, 'display');\n        });\n      });\n      editor.on('NodeChange', resizeDocument);\n      editor.on('remove', () => {\n        elementLoad.unbind();\n        dom.unbind(contentWindow, 'resize', resizeWindow);\n        dom.unbind(contentWindow, 'scroll', scroll);\n        contentWindow = null;\n      });\n    };\n    const attachUiMotherships$1 = (uiRoot, uiRefs) => {\n      attachSystem(uiRoot, uiRefs.dialogUi.mothership);\n    };\n    const render$1 = (editor, uiRefs, rawUiConfig, backstage, args) => {\n      const {mainUi, uiMotherships} = uiRefs;\n      const lastToolbarWidth = Cell(0);\n      const outerContainer = mainUi.outerContainer;\n      iframe(editor);\n      const eTargetNode = SugarElement.fromDom(args.targetNode);\n      const uiRoot = getContentContainer(getRootNode(eTargetNode));\n      attachSystemAfter(eTargetNode, mainUi.mothership);\n      attachUiMotherships$1(uiRoot, uiRefs);\n      editor.on('PostRender', () => {\n        OuterContainer.setSidebar(outerContainer, rawUiConfig.sidebar, getSidebarShow(editor));\n        setToolbar(editor, uiRefs, rawUiConfig, backstage);\n        lastToolbarWidth.set(editor.getWin().innerWidth);\n        OuterContainer.setMenubar(outerContainer, identifyMenus(editor, rawUiConfig));\n        OuterContainer.setViews(outerContainer, rawUiConfig.views);\n        setupEvents$1(editor, uiRefs);\n      });\n      const socket = OuterContainer.getSocket(outerContainer).getOrDie('Could not find expected socket element');\n      if (isiOS12) {\n        setAll(socket.element, {\n          'overflow': 'scroll',\n          '-webkit-overflow-scrolling': 'touch'\n        });\n        const limit = first(() => {\n          editor.dispatch('ScrollContent');\n        }, 20);\n        const unbinder = bind(socket.element, 'scroll', limit.throttle);\n        editor.on('remove', unbinder.unbind);\n      }\n      setupReadonlyModeSwitch(editor, uiRefs);\n      editor.addCommand('ToggleSidebar', (_ui, value) => {\n        OuterContainer.toggleSidebar(outerContainer, value);\n        editor.dispatch('ToggleSidebar');\n      });\n      editor.addQueryValueHandler('ToggleSidebar', () => {\n        var _a;\n        return (_a = OuterContainer.whichSidebar(outerContainer)) !== null && _a !== void 0 ? _a : '';\n      });\n      editor.addCommand('ToggleView', (_ui, value) => {\n        if (OuterContainer.toggleView(outerContainer, value)) {\n          const target = outerContainer.element;\n          mainUi.mothership.broadcastOn([dismissPopups()], { target });\n          each$1(uiMotherships, m => {\n            m.broadcastOn([dismissPopups()], { target });\n          });\n          if (isNull(OuterContainer.whichView(outerContainer))) {\n            editor.focus();\n            editor.nodeChanged();\n          }\n        }\n      });\n      editor.addQueryValueHandler('ToggleView', () => {\n        var _a;\n        return (_a = OuterContainer.whichView(outerContainer)) !== null && _a !== void 0 ? _a : '';\n      });\n      const toolbarMode = getToolbarMode(editor);\n      const refreshDrawer = () => {\n        OuterContainer.refreshToolbar(uiRefs.mainUi.outerContainer);\n      };\n      if (toolbarMode === ToolbarMode$1.sliding || toolbarMode === ToolbarMode$1.floating) {\n        editor.on('ResizeWindow ResizeEditor ResizeContent', () => {\n          const width = editor.getWin().innerWidth;\n          if (width !== lastToolbarWidth.get()) {\n            refreshDrawer();\n            lastToolbarWidth.set(width);\n          }\n        });\n      }\n      const api = {\n        setEnabled: state => {\n          broadcastReadonly(uiRefs, !state);\n        },\n        isEnabled: () => !Disabling.isDisabled(outerContainer)\n      };\n      return {\n        iframeContainer: socket.element.dom,\n        editorContainer: outerContainer.element.dom,\n        api\n      };\n    };\n\n    var Iframe = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        render: render$1\n    });\n\n    const parseToInt = val => {\n      const re = /^[0-9\\.]+(|px)$/i;\n      if (re.test('' + val)) {\n        return Optional.some(parseInt('' + val, 10));\n      }\n      return Optional.none();\n    };\n    const numToPx = val => isNumber(val) ? val + 'px' : val;\n    const calcCappedSize = (size, minSize, maxSize) => {\n      const minOverride = minSize.filter(min => size < min);\n      const maxOverride = maxSize.filter(max => size > max);\n      return minOverride.or(maxOverride).getOr(size);\n    };\n\n    const getHeight = editor => {\n      const baseHeight = getHeightOption(editor);\n      const minHeight = getMinHeightOption(editor);\n      const maxHeight = getMaxHeightOption(editor);\n      return parseToInt(baseHeight).map(height => calcCappedSize(height, minHeight, maxHeight));\n    };\n    const getHeightWithFallback = editor => {\n      const height = getHeight(editor);\n      return height.getOr(getHeightOption(editor));\n    };\n    const getWidth = editor => {\n      const baseWidth = getWidthOption(editor);\n      const minWidth = getMinWidthOption(editor);\n      const maxWidth = getMaxWidthOption(editor);\n      return parseToInt(baseWidth).map(width => calcCappedSize(width, minWidth, maxWidth));\n    };\n    const getWidthWithFallback = editor => {\n      const width = getWidth(editor);\n      return width.getOr(getWidthOption(editor));\n    };\n\n    const {ToolbarLocation, ToolbarMode} = Options;\n    const InlineHeader = (editor, targetElm, uiRefs, backstage, floatContainer) => {\n      const {mainUi, uiMotherships} = uiRefs;\n      const DOM = global$7.DOM;\n      const useFixedToolbarContainer = useFixedContainer(editor);\n      const isSticky = isStickyToolbar(editor);\n      const editorMaxWidthOpt = getMaxWidthOption(editor).or(getWidth(editor));\n      const headerBackstage = backstage.shared.header;\n      const isPositionedAtTop = headerBackstage.isPositionedAtTop;\n      const toolbarMode = getToolbarMode(editor);\n      const isSplitToolbar = toolbarMode === ToolbarMode.sliding || toolbarMode === ToolbarMode.floating;\n      const visible = Cell(false);\n      const isVisible = () => visible.get() && !editor.removed;\n      const calcToolbarOffset = toolbar => isSplitToolbar ? toolbar.fold(constant$1(0), tbar => tbar.components().length > 1 ? get$d(tbar.components()[1].element) : 0) : 0;\n      const calcMode = container => {\n        switch (getToolbarLocation(editor)) {\n        case ToolbarLocation.auto:\n          const toolbar = OuterContainer.getToolbar(mainUi.outerContainer);\n          const offset = calcToolbarOffset(toolbar);\n          const toolbarHeight = get$d(container.element) - offset;\n          const targetBounds = box$1(targetElm);\n          const roomAtTop = targetBounds.y > toolbarHeight;\n          if (roomAtTop) {\n            return 'top';\n          } else {\n            const doc = documentElement(targetElm);\n            const docHeight = Math.max(doc.dom.scrollHeight, get$d(doc));\n            const roomAtBottom = targetBounds.bottom < docHeight - toolbarHeight;\n            if (roomAtBottom) {\n              return 'bottom';\n            } else {\n              const winBounds = win();\n              const isRoomAtBottomViewport = winBounds.bottom < targetBounds.bottom - toolbarHeight;\n              return isRoomAtBottomViewport ? 'bottom' : 'top';\n            }\n          }\n        case ToolbarLocation.bottom:\n          return 'bottom';\n        case ToolbarLocation.top:\n        default:\n          return 'top';\n        }\n      };\n      const setupMode = mode => {\n        floatContainer.on(container => {\n          Docking.setModes(container, [mode]);\n          headerBackstage.setDockingMode(mode);\n          const verticalDir = isPositionedAtTop() ? AttributeValue.TopToBottom : AttributeValue.BottomToTop;\n          set$9(container.element, Attribute, verticalDir);\n        });\n      };\n      const updateChromeWidth = () => {\n        floatContainer.on(container => {\n          const maxWidth = editorMaxWidthOpt.getOrThunk(() => {\n            const bodyMargin = parseToInt(get$e(body(), 'margin-left')).getOr(0);\n            return get$c(body()) - absolute$3(targetElm).left + bodyMargin;\n          });\n          set$8(container.element, 'max-width', maxWidth + 'px');\n        });\n      };\n      const updateChromePosition = () => {\n        floatContainer.on(container => {\n          const toolbar = OuterContainer.getToolbar(mainUi.outerContainer);\n          const offset = calcToolbarOffset(toolbar);\n          const targetBounds = box$1(targetElm);\n          const top = isPositionedAtTop() ? Math.max(targetBounds.y - get$d(container.element) + offset, 0) : targetBounds.bottom;\n          setAll(mainUi.outerContainer.element, {\n            position: 'absolute',\n            top: Math.round(top) + 'px',\n            left: Math.round(targetBounds.x) + 'px'\n          });\n        });\n      };\n      const repositionPopups$1 = () => {\n        each$1(uiMotherships, m => {\n          m.broadcastOn([repositionPopups()], {});\n        });\n      };\n      const updateChromeUi = (resetDocking = false) => {\n        if (!isVisible()) {\n          return;\n        }\n        if (!useFixedToolbarContainer) {\n          updateChromeWidth();\n        }\n        if (isSplitToolbar) {\n          OuterContainer.refreshToolbar(mainUi.outerContainer);\n        }\n        if (!useFixedToolbarContainer) {\n          updateChromePosition();\n        }\n        if (isSticky) {\n          const action = resetDocking ? Docking.reset : Docking.refresh;\n          floatContainer.on(action);\n        }\n        repositionPopups$1();\n      };\n      const updateMode = (updateUi = true) => {\n        if (useFixedToolbarContainer || !isSticky || !isVisible()) {\n          return;\n        }\n        floatContainer.on(container => {\n          const currentMode = headerBackstage.getDockingMode();\n          const newMode = calcMode(container);\n          if (newMode !== currentMode) {\n            setupMode(newMode);\n            if (updateUi) {\n              updateChromeUi(true);\n            }\n          }\n        });\n      };\n      const show = () => {\n        visible.set(true);\n        set$8(mainUi.outerContainer.element, 'display', 'flex');\n        DOM.addClass(editor.getBody(), 'mce-edit-focus');\n        each$1(uiMotherships, m => {\n          remove$6(m.element, 'display');\n        });\n        updateMode(false);\n        updateChromeUi();\n      };\n      const hide = () => {\n        visible.set(false);\n        set$8(mainUi.outerContainer.element, 'display', 'none');\n        DOM.removeClass(editor.getBody(), 'mce-edit-focus');\n        each$1(uiMotherships, m => {\n          set$8(m.element, 'display', 'none');\n        });\n      };\n      return {\n        isVisible,\n        isPositionedAtTop,\n        show,\n        hide,\n        update: updateChromeUi,\n        updateMode,\n        repositionPopups: repositionPopups$1\n      };\n    };\n\n    const getTargetPosAndBounds = (targetElm, isToolbarTop) => {\n      const bounds = box$1(targetElm);\n      return {\n        pos: isToolbarTop ? bounds.y : bounds.bottom,\n        bounds\n      };\n    };\n    const setupEvents = (editor, targetElm, ui, toolbarPersist) => {\n      const prevPosAndBounds = Cell(getTargetPosAndBounds(targetElm, ui.isPositionedAtTop()));\n      const resizeContent = e => {\n        const {pos, bounds} = getTargetPosAndBounds(targetElm, ui.isPositionedAtTop());\n        const {\n          pos: prevPos,\n          bounds: prevBounds\n        } = prevPosAndBounds.get();\n        const hasResized = bounds.height !== prevBounds.height || bounds.width !== prevBounds.width;\n        prevPosAndBounds.set({\n          pos,\n          bounds\n        });\n        if (hasResized) {\n          fireResizeContent(editor, e);\n        }\n        if (ui.isVisible()) {\n          if (prevPos !== pos) {\n            ui.update(true);\n          } else if (hasResized) {\n            ui.updateMode();\n            ui.repositionPopups();\n          }\n        }\n      };\n      if (!toolbarPersist) {\n        editor.on('activate', ui.show);\n        editor.on('deactivate', ui.hide);\n      }\n      editor.on('SkinLoaded ResizeWindow', () => ui.update(true));\n      editor.on('NodeChange keydown', e => {\n        requestAnimationFrame(() => resizeContent(e));\n      });\n      editor.on('ScrollWindow', () => ui.updateMode());\n      const elementLoad = unbindable();\n      elementLoad.set(capture(SugarElement.fromDom(editor.getBody()), 'load', e => resizeContent(e.raw)));\n      editor.on('remove', () => {\n        elementLoad.clear();\n      });\n    };\n    const attachUiMotherships = (uiRoot, uiRefs) => {\n      attachSystem(uiRoot, uiRefs.dialogUi.mothership);\n    };\n    const render = (editor, uiRefs, rawUiConfig, backstage, args) => {\n      const {mainUi} = uiRefs;\n      const floatContainer = value$2();\n      const targetElm = SugarElement.fromDom(args.targetNode);\n      const ui = InlineHeader(editor, targetElm, uiRefs, backstage, floatContainer);\n      const toolbarPersist = isToolbarPersist(editor);\n      inline(editor);\n      const render = () => {\n        if (floatContainer.isSet()) {\n          ui.show();\n          return;\n        }\n        floatContainer.set(OuterContainer.getHeader(mainUi.outerContainer).getOrDie());\n        const uiContainer = getUiContainer(editor);\n        attachSystem(uiContainer, mainUi.mothership);\n        attachUiMotherships(uiContainer, uiRefs);\n        setToolbar(editor, uiRefs, rawUiConfig, backstage);\n        OuterContainer.setMenubar(mainUi.outerContainer, identifyMenus(editor, rawUiConfig));\n        ui.show();\n        setupEvents(editor, targetElm, ui, toolbarPersist);\n        editor.nodeChanged();\n      };\n      editor.on('show', render);\n      editor.on('hide', ui.hide);\n      if (!toolbarPersist) {\n        editor.on('focus', render);\n        editor.on('blur', ui.hide);\n      }\n      editor.on('init', () => {\n        if (editor.hasFocus() || toolbarPersist) {\n          render();\n        }\n      });\n      setupReadonlyModeSwitch(editor, uiRefs);\n      const api = {\n        show: render,\n        hide: ui.hide,\n        setEnabled: state => {\n          broadcastReadonly(uiRefs, !state);\n        },\n        isEnabled: () => !Disabling.isDisabled(mainUi.outerContainer)\n      };\n      return {\n        editorContainer: mainUi.outerContainer.element.dom,\n        api\n      };\n    };\n\n    var Inline = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        render: render\n    });\n\n    const LazyUiReferences = () => {\n      const dialogUi = value$2();\n      const popupUi = value$2();\n      const mainUi = value$2();\n      const setupDialogUi = ui => {\n        dialogUi.set(ui);\n      };\n      const lazyGetInOuterOrDie = (label, f) => () => mainUi.get().bind(oc => f(oc.outerContainer)).getOrDie(`Could not find ${ label } element in OuterContainer`);\n      return {\n        dialogUi,\n        popupUi,\n        mainUi,\n        getUiMotherships: () => [...dialogUi.get().map(e => e.mothership).toArray()],\n        setupDialogUi,\n        lazyGetInOuterOrDie\n      };\n    };\n\n    const showContextToolbarEvent = 'contexttoolbar-show';\n    const hideContextToolbarEvent = 'contexttoolbar-hide';\n\n    const getFormApi = input => ({\n      hide: () => emit(input, sandboxClose()),\n      getValue: () => Representing.getValue(input)\n    });\n    const runOnExecute = (memInput, original) => run$1(internalToolbarButtonExecute, (comp, se) => {\n      const input = memInput.get(comp);\n      const formApi = getFormApi(input);\n      original.onAction(formApi, se.event.buttonApi);\n    });\n    const renderContextButton = (memInput, button, providers) => {\n      const {primary, ...rest} = button.original;\n      const bridged = getOrDie(createToolbarButton({\n        ...rest,\n        type: 'button',\n        onAction: noop\n      }));\n      return renderToolbarButtonWith(bridged, providers, [runOnExecute(memInput, button)]);\n    };\n    const renderContextToggleButton = (memInput, button, providers) => {\n      const {primary, ...rest} = button.original;\n      const bridged = getOrDie(createToggleButton({\n        ...rest,\n        type: 'togglebutton',\n        onAction: noop\n      }));\n      return renderToolbarToggleButtonWith(bridged, providers, [runOnExecute(memInput, button)]);\n    };\n    const isToggleButton = button => button.type === 'contextformtogglebutton';\n    const generateOne = (memInput, button, providersBackstage) => {\n      if (isToggleButton(button)) {\n        return renderContextToggleButton(memInput, button, providersBackstage);\n      } else {\n        return renderContextButton(memInput, button, providersBackstage);\n      }\n    };\n    const generate = (memInput, buttons, providersBackstage) => {\n      const mementos = map$2(buttons, button => record(generateOne(memInput, button, providersBackstage)));\n      const asSpecs = () => map$2(mementos, mem => mem.asSpec());\n      const findPrimary = compInSystem => findMap(buttons, (button, i) => {\n        if (button.primary) {\n          return Optional.from(mementos[i]).bind(mem => mem.getOpt(compInSystem)).filter(not(Disabling.isDisabled));\n        } else {\n          return Optional.none();\n        }\n      });\n      return {\n        asSpecs,\n        findPrimary\n      };\n    };\n\n    const buildInitGroups = (ctx, providers) => {\n      const inputAttributes = ctx.label.fold(() => ({}), label => ({ 'aria-label': label }));\n      const memInput = record(Input.sketch({\n        inputClasses: [\n          'tox-toolbar-textfield',\n          'tox-toolbar-nav-js'\n        ],\n        data: ctx.initValue(),\n        inputAttributes,\n        selectOnFocus: true,\n        inputBehaviours: derive$1([Keying.config({\n            mode: 'special',\n            onEnter: input => commands.findPrimary(input).map(primary => {\n              emitExecute(primary);\n              return true;\n            }),\n            onLeft: (comp, se) => {\n              se.cut();\n              return Optional.none();\n            },\n            onRight: (comp, se) => {\n              se.cut();\n              return Optional.none();\n            }\n          })])\n      }));\n      const commands = generate(memInput, ctx.commands, providers);\n      return [\n        {\n          title: Optional.none(),\n          items: [memInput.asSpec()]\n        },\n        {\n          title: Optional.none(),\n          items: commands.asSpecs()\n        }\n      ];\n    };\n    const renderContextForm = (toolbarType, ctx, providers) => renderToolbar({\n      type: toolbarType,\n      uid: generate$6('context-toolbar'),\n      initGroups: buildInitGroups(ctx, providers),\n      onEscape: Optional.none,\n      cyclicKeying: true,\n      providers\n    });\n    const ContextForm = {\n      renderContextForm,\n      buildInitGroups\n    };\n\n    const isVerticalOverlap = (a, b, threshold) => b.bottom - a.y >= threshold && a.bottom - b.y >= threshold;\n    const getRangeRect = rng => {\n      const rect = rng.getBoundingClientRect();\n      if (rect.height <= 0 && rect.width <= 0) {\n        const leaf$1 = leaf(SugarElement.fromDom(rng.startContainer), rng.startOffset).element;\n        const elm = isText(leaf$1) ? parent(leaf$1) : Optional.some(leaf$1);\n        return elm.filter(isElement$1).map(e => e.dom.getBoundingClientRect()).getOr(rect);\n      } else {\n        return rect;\n      }\n    };\n    const getSelectionBounds = editor => {\n      const rng = editor.selection.getRng();\n      const rect = getRangeRect(rng);\n      if (editor.inline) {\n        const scroll = get$b();\n        return bounds(scroll.left + rect.left, scroll.top + rect.top, rect.width, rect.height);\n      } else {\n        const bodyPos = absolute$2(SugarElement.fromDom(editor.getBody()));\n        return bounds(bodyPos.x + rect.left, bodyPos.y + rect.top, rect.width, rect.height);\n      }\n    };\n    const getAnchorElementBounds = (editor, lastElement) => lastElement.filter(elem => inBody(elem) && isHTMLElement(elem)).map(absolute$2).getOrThunk(() => getSelectionBounds(editor));\n    const getHorizontalBounds = (contentAreaBox, viewportBounds, margin) => {\n      const x = Math.max(contentAreaBox.x + margin, viewportBounds.x);\n      const right = Math.min(contentAreaBox.right - margin, viewportBounds.right);\n      return {\n        x,\n        width: right - x\n      };\n    };\n    const getVerticalBounds = (editor, contentAreaBox, viewportBounds, isToolbarLocationTop, toolbarType, margin) => {\n      const container = SugarElement.fromDom(editor.getContainer());\n      const header = descendant(container, '.tox-editor-header').getOr(container);\n      const headerBox = box$1(header);\n      const isToolbarBelowContentArea = headerBox.y >= contentAreaBox.bottom;\n      const isToolbarAbove = isToolbarLocationTop && !isToolbarBelowContentArea;\n      if (editor.inline && isToolbarAbove) {\n        return {\n          y: Math.max(headerBox.bottom + margin, viewportBounds.y),\n          bottom: viewportBounds.bottom\n        };\n      }\n      if (editor.inline && !isToolbarAbove) {\n        return {\n          y: viewportBounds.y,\n          bottom: Math.min(headerBox.y - margin, viewportBounds.bottom)\n        };\n      }\n      const containerBounds = toolbarType === 'line' ? box$1(container) : contentAreaBox;\n      if (isToolbarAbove) {\n        return {\n          y: Math.max(headerBox.bottom + margin, viewportBounds.y),\n          bottom: Math.min(containerBounds.bottom - margin, viewportBounds.bottom)\n        };\n      }\n      return {\n        y: Math.max(containerBounds.y + margin, viewportBounds.y),\n        bottom: Math.min(headerBox.y - margin, viewportBounds.bottom)\n      };\n    };\n    const getContextToolbarBounds = (editor, sharedBackstage, toolbarType, margin = 0) => {\n      const viewportBounds = getBounds$3(window);\n      const contentAreaBox = box$1(SugarElement.fromDom(editor.getContentAreaContainer()));\n      const toolbarOrMenubarEnabled = isMenubarEnabled(editor) || isToolbarEnabled(editor) || isMultipleToolbars(editor);\n      const {x, width} = getHorizontalBounds(contentAreaBox, viewportBounds, margin);\n      if (editor.inline && !toolbarOrMenubarEnabled) {\n        return bounds(x, viewportBounds.y, width, viewportBounds.height);\n      } else {\n        const isToolbarTop = sharedBackstage.header.isPositionedAtTop();\n        const {y, bottom} = getVerticalBounds(editor, contentAreaBox, viewportBounds, isToolbarTop, toolbarType, margin);\n        return bounds(x, y, width, bottom - y);\n      }\n    };\n\n    const bubbleSize$1 = 12;\n    const bubbleAlignments$1 = {\n      valignCentre: [],\n      alignCentre: [],\n      alignLeft: ['tox-pop--align-left'],\n      alignRight: ['tox-pop--align-right'],\n      right: ['tox-pop--right'],\n      left: ['tox-pop--left'],\n      bottom: ['tox-pop--bottom'],\n      top: ['tox-pop--top'],\n      inset: ['tox-pop--inset']\n    };\n    const anchorOverrides = {\n      maxHeightFunction: expandable$1(),\n      maxWidthFunction: expandable()\n    };\n    const isEntireElementSelected = (editor, elem) => {\n      const rng = editor.selection.getRng();\n      const leaf$1 = leaf(SugarElement.fromDom(rng.startContainer), rng.startOffset);\n      return rng.startContainer === rng.endContainer && rng.startOffset === rng.endOffset - 1 && eq(leaf$1.element, elem);\n    };\n    const preservePosition = (elem, position, f) => {\n      const currentPosition = getRaw(elem, 'position');\n      set$8(elem, 'position', position);\n      const result = f(elem);\n      currentPosition.each(pos => set$8(elem, 'position', pos));\n      return result;\n    };\n    const shouldUseInsetLayouts = position => position === 'node';\n    const determineInsetLayout = (editor, contextbar, elem, data, bounds) => {\n      const selectionBounds = getSelectionBounds(editor);\n      const isSameAnchorElement = data.lastElement().exists(prev => eq(elem, prev));\n      if (isEntireElementSelected(editor, elem)) {\n        return isSameAnchorElement ? preserve : north;\n      } else if (isSameAnchorElement) {\n        return preservePosition(contextbar, data.getMode(), () => {\n          const isOverlapping = isVerticalOverlap(selectionBounds, box$1(contextbar), -20);\n          return isOverlapping && !data.isReposition() ? flip : preserve;\n        });\n      } else {\n        const yBounds = data.getMode() === 'fixed' ? bounds.y + get$b().top : bounds.y;\n        const contextbarHeight = get$d(contextbar) + bubbleSize$1;\n        return yBounds + contextbarHeight <= selectionBounds.y ? north : south;\n      }\n    };\n    const getAnchorSpec$2 = (editor, mobile, data, position) => {\n      const smartInsetLayout = elem => (anchor, element, bubbles, placee, bounds) => {\n        const layout = determineInsetLayout(editor, placee, elem, data, bounds);\n        const newAnchor = {\n          ...anchor,\n          y: bounds.y,\n          height: bounds.height\n        };\n        return {\n          ...layout(newAnchor, element, bubbles, placee, bounds),\n          alwaysFit: true\n        };\n      };\n      const getInsetLayouts = elem => shouldUseInsetLayouts(position) ? [smartInsetLayout(elem)] : [];\n      const desktopAnchorSpecLayouts = {\n        onLtr: elem => [\n          north$2,\n          south$2,\n          northeast$2,\n          southeast$2,\n          northwest$2,\n          southwest$2\n        ].concat(getInsetLayouts(elem)),\n        onRtl: elem => [\n          north$2,\n          south$2,\n          northwest$2,\n          southwest$2,\n          northeast$2,\n          southeast$2\n        ].concat(getInsetLayouts(elem))\n      };\n      const mobileAnchorSpecLayouts = {\n        onLtr: elem => [\n          south$2,\n          southeast$2,\n          southwest$2,\n          northeast$2,\n          northwest$2,\n          north$2\n        ].concat(getInsetLayouts(elem)),\n        onRtl: elem => [\n          south$2,\n          southwest$2,\n          southeast$2,\n          northwest$2,\n          northeast$2,\n          north$2\n        ].concat(getInsetLayouts(elem))\n      };\n      return mobile ? mobileAnchorSpecLayouts : desktopAnchorSpecLayouts;\n    };\n    const getAnchorLayout = (editor, position, isTouch, data) => {\n      if (position === 'line') {\n        return {\n          bubble: nu$5(bubbleSize$1, 0, bubbleAlignments$1),\n          layouts: {\n            onLtr: () => [east$2],\n            onRtl: () => [west$2]\n          },\n          overrides: anchorOverrides\n        };\n      } else {\n        return {\n          bubble: nu$5(0, bubbleSize$1, bubbleAlignments$1, 1 / bubbleSize$1),\n          layouts: getAnchorSpec$2(editor, isTouch, data, position),\n          overrides: anchorOverrides\n        };\n      }\n    };\n\n    const matchTargetWith = (elem, candidates) => {\n      const ctxs = filter$2(candidates, toolbarApi => toolbarApi.predicate(elem.dom));\n      const {pass, fail} = partition$3(ctxs, t => t.type === 'contexttoolbar');\n      return {\n        contextToolbars: pass,\n        contextForms: fail\n      };\n    };\n    const filterByPositionForStartNode = toolbars => {\n      if (toolbars.length <= 1) {\n        return toolbars;\n      } else {\n        const doesPositionExist = value => exists(toolbars, t => t.position === value);\n        const filterToolbarsByPosition = value => filter$2(toolbars, t => t.position === value);\n        const hasSelectionToolbars = doesPositionExist('selection');\n        const hasNodeToolbars = doesPositionExist('node');\n        if (hasSelectionToolbars || hasNodeToolbars) {\n          if (hasNodeToolbars && hasSelectionToolbars) {\n            const nodeToolbars = filterToolbarsByPosition('node');\n            const selectionToolbars = map$2(filterToolbarsByPosition('selection'), t => ({\n              ...t,\n              position: 'node'\n            }));\n            return nodeToolbars.concat(selectionToolbars);\n          } else {\n            return hasSelectionToolbars ? filterToolbarsByPosition('selection') : filterToolbarsByPosition('node');\n          }\n        } else {\n          return filterToolbarsByPosition('line');\n        }\n      }\n    };\n    const filterByPositionForAncestorNode = toolbars => {\n      if (toolbars.length <= 1) {\n        return toolbars;\n      } else {\n        const findPosition = value => find$5(toolbars, t => t.position === value);\n        const basePosition = findPosition('selection').orThunk(() => findPosition('node')).orThunk(() => findPosition('line')).map(t => t.position);\n        return basePosition.fold(() => [], pos => filter$2(toolbars, t => t.position === pos));\n      }\n    };\n    const matchStartNode = (elem, nodeCandidates, editorCandidates) => {\n      const nodeMatches = matchTargetWith(elem, nodeCandidates);\n      if (nodeMatches.contextForms.length > 0) {\n        return Optional.some({\n          elem,\n          toolbars: [nodeMatches.contextForms[0]]\n        });\n      } else {\n        const editorMatches = matchTargetWith(elem, editorCandidates);\n        if (editorMatches.contextForms.length > 0) {\n          return Optional.some({\n            elem,\n            toolbars: [editorMatches.contextForms[0]]\n          });\n        } else if (nodeMatches.contextToolbars.length > 0 || editorMatches.contextToolbars.length > 0) {\n          const toolbars = filterByPositionForStartNode(nodeMatches.contextToolbars.concat(editorMatches.contextToolbars));\n          return Optional.some({\n            elem,\n            toolbars\n          });\n        } else {\n          return Optional.none();\n        }\n      }\n    };\n    const matchAncestor = (isRoot, startNode, scopes) => {\n      if (isRoot(startNode)) {\n        return Optional.none();\n      } else {\n        return ancestor$2(startNode, ancestorElem => {\n          if (isElement$1(ancestorElem)) {\n            const {contextToolbars, contextForms} = matchTargetWith(ancestorElem, scopes.inNodeScope);\n            const toolbars = contextForms.length > 0 ? contextForms : filterByPositionForAncestorNode(contextToolbars);\n            return toolbars.length > 0 ? Optional.some({\n              elem: ancestorElem,\n              toolbars\n            }) : Optional.none();\n          } else {\n            return Optional.none();\n          }\n        }, isRoot);\n      }\n    };\n    const lookup$1 = (scopes, editor) => {\n      const rootElem = SugarElement.fromDom(editor.getBody());\n      const isRoot = elem => eq(elem, rootElem);\n      const isOutsideRoot = startNode => !isRoot(startNode) && !contains(rootElem, startNode);\n      const startNode = SugarElement.fromDom(editor.selection.getNode());\n      if (isOutsideRoot(startNode)) {\n        return Optional.none();\n      }\n      return matchStartNode(startNode, scopes.inNodeScope, scopes.inEditorScope).orThunk(() => matchAncestor(isRoot, startNode, scopes));\n    };\n\n    const categorise = (contextToolbars, navigate) => {\n      const forms = {};\n      const inNodeScope = [];\n      const inEditorScope = [];\n      const formNavigators = {};\n      const lookupTable = {};\n      const registerForm = (key, toolbarSpec) => {\n        const contextForm = getOrDie(createContextForm(toolbarSpec));\n        forms[key] = contextForm;\n        contextForm.launch.map(launch => {\n          formNavigators['form:' + key + ''] = {\n            ...toolbarSpec.launch,\n            type: launch.type === 'contextformtogglebutton' ? 'togglebutton' : 'button',\n            onAction: () => {\n              navigate(contextForm);\n            }\n          };\n        });\n        if (contextForm.scope === 'editor') {\n          inEditorScope.push(contextForm);\n        } else {\n          inNodeScope.push(contextForm);\n        }\n        lookupTable[key] = contextForm;\n      };\n      const registerToolbar = (key, toolbarSpec) => {\n        createContextToolbar(toolbarSpec).each(contextToolbar => {\n          if (toolbarSpec.scope === 'editor') {\n            inEditorScope.push(contextToolbar);\n          } else {\n            inNodeScope.push(contextToolbar);\n          }\n          lookupTable[key] = contextToolbar;\n        });\n      };\n      const keys$1 = keys(contextToolbars);\n      each$1(keys$1, key => {\n        const toolbarApi = contextToolbars[key];\n        if (toolbarApi.type === 'contextform') {\n          registerForm(key, toolbarApi);\n        } else if (toolbarApi.type === 'contexttoolbar') {\n          registerToolbar(key, toolbarApi);\n        }\n      });\n      return {\n        forms,\n        inNodeScope,\n        inEditorScope,\n        lookupTable,\n        formNavigators\n      };\n    };\n\n    const forwardSlideEvent = generate$6('forward-slide');\n    const backSlideEvent = generate$6('backward-slide');\n    const changeSlideEvent = generate$6('change-slide-event');\n    const resizingClass = 'tox-pop--resizing';\n    const renderContextToolbar = spec => {\n      const stack = Cell([]);\n      return InlineView.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-pop']\n        },\n        fireDismissalEventInstead: { event: 'doNotDismissYet' },\n        onShow: comp => {\n          stack.set([]);\n          InlineView.getContent(comp).each(c => {\n            remove$6(c.element, 'visibility');\n          });\n          remove$2(comp.element, resizingClass);\n          remove$6(comp.element, 'width');\n        },\n        inlineBehaviours: derive$1([\n          config('context-toolbar-events', [\n            runOnSource(transitionend(), (comp, se) => {\n              if (se.event.raw.propertyName === 'width') {\n                remove$2(comp.element, resizingClass);\n                remove$6(comp.element, 'width');\n              }\n            }),\n            run$1(changeSlideEvent, (comp, se) => {\n              const elem = comp.element;\n              remove$6(elem, 'width');\n              const currentWidth = get$c(elem);\n              InlineView.setContent(comp, se.event.contents);\n              add$2(elem, resizingClass);\n              const newWidth = get$c(elem);\n              set$8(elem, 'width', currentWidth + 'px');\n              InlineView.getContent(comp).each(newContents => {\n                se.event.focus.bind(f => {\n                  focus$3(f);\n                  return search(elem);\n                }).orThunk(() => {\n                  Keying.focusIn(newContents);\n                  return active$1(getRootNode(elem));\n                });\n              });\n              setTimeout(() => {\n                set$8(comp.element, 'width', newWidth + 'px');\n              }, 0);\n            }),\n            run$1(forwardSlideEvent, (comp, se) => {\n              InlineView.getContent(comp).each(oldContents => {\n                stack.set(stack.get().concat([{\n                    bar: oldContents,\n                    focus: active$1(getRootNode(comp.element))\n                  }]));\n              });\n              emitWith(comp, changeSlideEvent, {\n                contents: se.event.forwardContents,\n                focus: Optional.none()\n              });\n            }),\n            run$1(backSlideEvent, (comp, _se) => {\n              last$1(stack.get()).each(last => {\n                stack.set(stack.get().slice(0, stack.get().length - 1));\n                emitWith(comp, changeSlideEvent, {\n                  contents: premade(last.bar),\n                  focus: last.focus\n                });\n              });\n            })\n          ]),\n          Keying.config({\n            mode: 'special',\n            onEscape: comp => last$1(stack.get()).fold(() => spec.onEscape(), _ => {\n              emit(comp, backSlideEvent);\n              return Optional.some(true);\n            })\n          })\n        ]),\n        lazySink: () => Result.value(spec.sink)\n      });\n    };\n\n    const transitionClass = 'tox-pop--transition';\n    const register$9 = (editor, registryContextToolbars, sink, extras) => {\n      const backstage = extras.backstage;\n      const sharedBackstage = backstage.shared;\n      const isTouch = detect$1().deviceType.isTouch;\n      const lastElement = value$2();\n      const lastTrigger = value$2();\n      const lastContextPosition = value$2();\n      const contextbar = build$1(renderContextToolbar({\n        sink,\n        onEscape: () => {\n          editor.focus();\n          return Optional.some(true);\n        }\n      }));\n      const getBounds = () => {\n        const position = lastContextPosition.get().getOr('node');\n        const margin = shouldUseInsetLayouts(position) ? 1 : 0;\n        return getContextToolbarBounds(editor, sharedBackstage, position, margin);\n      };\n      const canLaunchToolbar = () => {\n        return !editor.removed && !(isTouch() && backstage.isContextMenuOpen());\n      };\n      const isSameLaunchElement = elem => is$1(lift2(elem, lastElement.get(), eq), true);\n      const shouldContextToolbarHide = () => {\n        if (!canLaunchToolbar()) {\n          return true;\n        } else {\n          const contextToolbarBounds = getBounds();\n          const anchorBounds = is$1(lastContextPosition.get(), 'node') ? getAnchorElementBounds(editor, lastElement.get()) : getSelectionBounds(editor);\n          return contextToolbarBounds.height <= 0 || !isVerticalOverlap(anchorBounds, contextToolbarBounds, 0.01);\n        }\n      };\n      const close = () => {\n        lastElement.clear();\n        lastTrigger.clear();\n        lastContextPosition.clear();\n        InlineView.hide(contextbar);\n      };\n      const hideOrRepositionIfNecessary = () => {\n        if (InlineView.isOpen(contextbar)) {\n          const contextBarEle = contextbar.element;\n          remove$6(contextBarEle, 'display');\n          if (shouldContextToolbarHide()) {\n            set$8(contextBarEle, 'display', 'none');\n          } else {\n            lastTrigger.set(0);\n            InlineView.reposition(contextbar);\n          }\n        }\n      };\n      const wrapInPopDialog = toolbarSpec => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-pop__dialog']\n        },\n        components: [toolbarSpec],\n        behaviours: derive$1([\n          Keying.config({ mode: 'acyclic' }),\n          config('pop-dialog-wrap-events', [\n            runOnAttached(comp => {\n              editor.shortcuts.add('ctrl+F9', 'focus statusbar', () => Keying.focusIn(comp));\n            }),\n            runOnDetached(_comp => {\n              editor.shortcuts.remove('ctrl+F9');\n            })\n          ])\n        ])\n      });\n      const getScopes = cached(() => categorise(registryContextToolbars, toolbarApi => {\n        const alloySpec = buildToolbar([toolbarApi]);\n        emitWith(contextbar, forwardSlideEvent, { forwardContents: wrapInPopDialog(alloySpec) });\n      }));\n      const buildContextToolbarGroups = (allButtons, ctx) => identifyButtons(editor, {\n        buttons: allButtons,\n        toolbar: ctx.items,\n        allowToolbarGroups: false\n      }, extras.backstage, Optional.some(['form:']));\n      const buildContextFormGroups = (ctx, providers) => ContextForm.buildInitGroups(ctx, providers);\n      const buildToolbar = toolbars => {\n        const {buttons} = editor.ui.registry.getAll();\n        const scopes = getScopes();\n        const allButtons = {\n          ...buttons,\n          ...scopes.formNavigators\n        };\n        const toolbarType = getToolbarMode(editor) === ToolbarMode$1.scrolling ? ToolbarMode$1.scrolling : ToolbarMode$1.default;\n        const initGroups = flatten(map$2(toolbars, ctx => ctx.type === 'contexttoolbar' ? buildContextToolbarGroups(allButtons, ctx) : buildContextFormGroups(ctx, sharedBackstage.providers)));\n        return renderToolbar({\n          type: toolbarType,\n          uid: generate$6('context-toolbar'),\n          initGroups,\n          onEscape: Optional.none,\n          cyclicKeying: true,\n          providers: sharedBackstage.providers\n        });\n      };\n      const getAnchor = (position, element) => {\n        const anchorage = position === 'node' ? sharedBackstage.anchors.node(element) : sharedBackstage.anchors.cursor();\n        const anchorLayout = getAnchorLayout(editor, position, isTouch(), {\n          lastElement: lastElement.get,\n          isReposition: () => is$1(lastTrigger.get(), 0),\n          getMode: () => Positioning.getMode(sink)\n        });\n        return deepMerge(anchorage, anchorLayout);\n      };\n      const launchContext = (toolbarApi, elem) => {\n        launchContextToolbar.cancel();\n        if (!canLaunchToolbar()) {\n          return;\n        }\n        const toolbarSpec = buildToolbar(toolbarApi);\n        const position = toolbarApi[0].position;\n        const anchor = getAnchor(position, elem);\n        lastContextPosition.set(position);\n        lastTrigger.set(1);\n        const contextBarEle = contextbar.element;\n        remove$6(contextBarEle, 'display');\n        if (!isSameLaunchElement(elem)) {\n          remove$2(contextBarEle, transitionClass);\n          Positioning.reset(sink, contextbar);\n        }\n        InlineView.showWithinBounds(contextbar, wrapInPopDialog(toolbarSpec), {\n          anchor,\n          transition: {\n            classes: [transitionClass],\n            mode: 'placement'\n          }\n        }, () => Optional.some(getBounds()));\n        elem.fold(lastElement.clear, lastElement.set);\n        if (shouldContextToolbarHide()) {\n          set$8(contextBarEle, 'display', 'none');\n        }\n      };\n      const launchContextToolbar = last(() => {\n        if (!editor.hasFocus() || editor.removed) {\n          return;\n        }\n        if (has(contextbar.element, transitionClass)) {\n          launchContextToolbar.throttle();\n        } else {\n          const scopes = getScopes();\n          lookup$1(scopes, editor).fold(close, info => {\n            launchContext(info.toolbars, Optional.some(info.elem));\n          });\n        }\n      }, 17);\n      editor.on('init', () => {\n        editor.on('remove', close);\n        editor.on('ScrollContent ScrollWindow ObjectResized ResizeEditor longpress', hideOrRepositionIfNecessary);\n        editor.on('click keyup focus SetContent', launchContextToolbar.throttle);\n        editor.on(hideContextToolbarEvent, close);\n        editor.on(showContextToolbarEvent, e => {\n          const scopes = getScopes();\n          get$g(scopes.lookupTable, e.toolbarKey).each(ctx => {\n            launchContext([ctx], someIf(e.target !== editor, e.target));\n            InlineView.getContent(contextbar).each(Keying.focusIn);\n          });\n        });\n        editor.on('focusout', _e => {\n          global$9.setEditorTimeout(editor, () => {\n            if (search(sink.element).isNone() && search(contextbar.element).isNone()) {\n              close();\n            }\n          }, 0);\n        });\n        editor.on('SwitchMode', () => {\n          if (editor.mode.isReadOnly()) {\n            close();\n          }\n        });\n        editor.on('AfterProgressState', event => {\n          if (event.state) {\n            close();\n          } else if (editor.hasFocus()) {\n            launchContextToolbar.throttle();\n          }\n        });\n        editor.on('NodeChange', _e => {\n          search(contextbar.element).fold(launchContextToolbar.throttle, noop);\n        });\n      });\n    };\n\n    const register$8 = editor => {\n      const alignToolbarButtons = [\n        {\n          name: 'alignleft',\n          text: 'Align left',\n          cmd: 'JustifyLeft',\n          icon: 'align-left'\n        },\n        {\n          name: 'aligncenter',\n          text: 'Align center',\n          cmd: 'JustifyCenter',\n          icon: 'align-center'\n        },\n        {\n          name: 'alignright',\n          text: 'Align right',\n          cmd: 'JustifyRight',\n          icon: 'align-right'\n        },\n        {\n          name: 'alignjustify',\n          text: 'Justify',\n          cmd: 'JustifyFull',\n          icon: 'align-justify'\n        }\n      ];\n      each$1(alignToolbarButtons, item => {\n        editor.ui.registry.addToggleButton(item.name, {\n          tooltip: item.text,\n          icon: item.icon,\n          onAction: onActionExecCommand(editor, item.cmd),\n          onSetup: onSetupFormatToggle(editor, item.name)\n        });\n      });\n      editor.ui.registry.addButton('alignnone', {\n        tooltip: 'No alignment',\n        icon: 'align-none',\n        onAction: onActionExecCommand(editor, 'JustifyNone')\n      });\n    };\n\n    const units = {\n      unsupportedLength: [\n        'em',\n        'ex',\n        'cap',\n        'ch',\n        'ic',\n        'rem',\n        'lh',\n        'rlh',\n        'vw',\n        'vh',\n        'vi',\n        'vb',\n        'vmin',\n        'vmax',\n        'cm',\n        'mm',\n        'Q',\n        'in',\n        'pc',\n        'pt',\n        'px'\n      ],\n      fixed: [\n        'px',\n        'pt'\n      ],\n      relative: ['%'],\n      empty: ['']\n    };\n    const pattern = (() => {\n      const decimalDigits = '[0-9]+';\n      const signedInteger = '[+-]?' + decimalDigits;\n      const exponentPart = '[eE]' + signedInteger;\n      const dot = '\\\\.';\n      const opt = input => `(?:${ input })?`;\n      const unsignedDecimalLiteral = [\n        'Infinity',\n        decimalDigits + dot + opt(decimalDigits) + opt(exponentPart),\n        dot + decimalDigits + opt(exponentPart),\n        decimalDigits + opt(exponentPart)\n      ].join('|');\n      const float = `[+-]?(?:${ unsignedDecimalLiteral })`;\n      return new RegExp(`^(${ float })(.*)$`);\n    })();\n    const isUnit = (unit, accepted) => exists(accepted, acc => exists(units[acc], check => unit === check));\n    const parse = (input, accepted) => {\n      const match = Optional.from(pattern.exec(input));\n      return match.bind(array => {\n        const value = Number(array[1]);\n        const unitRaw = array[2];\n        if (isUnit(unitRaw, accepted)) {\n          return Optional.some({\n            value,\n            unit: unitRaw\n          });\n        } else {\n          return Optional.none();\n        }\n      });\n    };\n    const normalise = (input, accepted) => parse(input, accepted).map(({value, unit}) => value + unit);\n\n    const registerController = (editor, spec) => {\n      const getMenuItems = () => {\n        const options = spec.getOptions(editor);\n        const initial = spec.getCurrent(editor).map(spec.hash);\n        const current = value$2();\n        return map$2(options, value => ({\n          type: 'togglemenuitem',\n          text: spec.display(value),\n          onSetup: api => {\n            const setActive = active => {\n              if (active) {\n                current.on(oldApi => oldApi.setActive(false));\n                current.set(api);\n              }\n              api.setActive(active);\n            };\n            setActive(is$1(initial, spec.hash(value)));\n            const unbindWatcher = spec.watcher(editor, value, setActive);\n            return () => {\n              current.clear();\n              unbindWatcher();\n            };\n          },\n          onAction: () => spec.setCurrent(editor, value)\n        }));\n      };\n      editor.ui.registry.addMenuButton(spec.name, {\n        tooltip: spec.text,\n        icon: spec.icon,\n        fetch: callback => callback(getMenuItems()),\n        onSetup: spec.onToolbarSetup\n      });\n      editor.ui.registry.addNestedMenuItem(spec.name, {\n        type: 'nestedmenuitem',\n        text: spec.text,\n        getSubmenuItems: getMenuItems,\n        onSetup: spec.onMenuSetup\n      });\n    };\n    const lineHeightSpec = {\n      name: 'lineheight',\n      text: 'Line height',\n      icon: 'line-height',\n      getOptions: getLineHeightFormats,\n      hash: input => normalise(input, [\n        'fixed',\n        'relative',\n        'empty'\n      ]).getOr(input),\n      display: identity,\n      watcher: (editor, value, callback) => editor.formatter.formatChanged('lineheight', callback, false, { value }).unbind,\n      getCurrent: editor => Optional.from(editor.queryCommandValue('LineHeight')),\n      setCurrent: (editor, value) => editor.execCommand('LineHeight', false, value)\n    };\n    const languageSpec = editor => {\n      const settingsOpt = Optional.from(getContentLanguages(editor));\n      return settingsOpt.map(settings => ({\n        name: 'language',\n        text: 'Language',\n        icon: 'language',\n        getOptions: constant$1(settings),\n        hash: input => isUndefined(input.customCode) ? input.code : `${ input.code }/${ input.customCode }`,\n        display: input => input.title,\n        watcher: (editor, value, callback) => {\n          var _a;\n          return editor.formatter.formatChanged('lang', callback, false, {\n            value: value.code,\n            customValue: (_a = value.customCode) !== null && _a !== void 0 ? _a : null\n          }).unbind;\n        },\n        getCurrent: editor => {\n          const node = SugarElement.fromDom(editor.selection.getNode());\n          return closest$4(node, n => Optional.some(n).filter(isElement$1).bind(ele => {\n            const codeOpt = getOpt(ele, 'lang');\n            return codeOpt.map(code => {\n              const customCode = getOpt(ele, 'data-mce-lang').getOrUndefined();\n              return {\n                code,\n                customCode,\n                title: ''\n              };\n            });\n          }));\n        },\n        setCurrent: (editor, lang) => editor.execCommand('Lang', false, lang),\n        onToolbarSetup: api => {\n          const unbinder = unbindable();\n          api.setActive(editor.formatter.match('lang', {}, undefined, true));\n          unbinder.set(editor.formatter.formatChanged('lang', api.setActive, true));\n          return unbinder.clear;\n        }\n      }));\n    };\n    const register$7 = editor => {\n      registerController(editor, lineHeightSpec);\n      languageSpec(editor).each(spec => registerController(editor, spec));\n    };\n\n    const register$6 = (editor, backstage) => {\n      createAlignMenu(editor, backstage);\n      createFontFamilyMenu(editor, backstage);\n      createStylesMenu(editor, backstage);\n      createBlocksMenu(editor, backstage);\n      createFontSizeMenu(editor, backstage);\n    };\n\n    const onSetupOutdentState = editor => onSetupEvent(editor, 'NodeChange', api => {\n      api.setEnabled(editor.queryCommandState('outdent'));\n    });\n    const registerButtons$2 = editor => {\n      editor.ui.registry.addButton('outdent', {\n        tooltip: 'Decrease indent',\n        icon: 'outdent',\n        onSetup: onSetupOutdentState(editor),\n        onAction: onActionExecCommand(editor, 'outdent')\n      });\n      editor.ui.registry.addButton('indent', {\n        tooltip: 'Increase indent',\n        icon: 'indent',\n        onAction: onActionExecCommand(editor, 'indent')\n      });\n    };\n    const register$5 = editor => {\n      registerButtons$2(editor);\n    };\n\n    const makeSetupHandler = (editor, pasteAsText) => api => {\n      api.setActive(pasteAsText.get());\n      const pastePlainTextToggleHandler = e => {\n        pasteAsText.set(e.state);\n        api.setActive(e.state);\n      };\n      editor.on('PastePlainTextToggle', pastePlainTextToggleHandler);\n      return () => editor.off('PastePlainTextToggle', pastePlainTextToggleHandler);\n    };\n    const register$4 = editor => {\n      const pasteAsText = Cell(getPasteAsText(editor));\n      const onAction = () => editor.execCommand('mceTogglePlainTextPaste');\n      editor.ui.registry.addToggleButton('pastetext', {\n        active: false,\n        icon: 'paste-text',\n        tooltip: 'Paste as text',\n        onAction,\n        onSetup: makeSetupHandler(editor, pasteAsText)\n      });\n      editor.ui.registry.addToggleMenuItem('pastetext', {\n        text: 'Paste as text',\n        icon: 'paste-text',\n        onAction,\n        onSetup: makeSetupHandler(editor, pasteAsText)\n      });\n    };\n\n    const onActionToggleFormat = (editor, fmt) => () => {\n      editor.execCommand('mceToggleFormat', false, fmt);\n    };\n    const registerFormatButtons = editor => {\n      global$1.each([\n        {\n          name: 'bold',\n          text: 'Bold',\n          icon: 'bold'\n        },\n        {\n          name: 'italic',\n          text: 'Italic',\n          icon: 'italic'\n        },\n        {\n          name: 'underline',\n          text: 'Underline',\n          icon: 'underline'\n        },\n        {\n          name: 'strikethrough',\n          text: 'Strikethrough',\n          icon: 'strike-through'\n        },\n        {\n          name: 'subscript',\n          text: 'Subscript',\n          icon: 'subscript'\n        },\n        {\n          name: 'superscript',\n          text: 'Superscript',\n          icon: 'superscript'\n        }\n      ], (btn, _idx) => {\n        editor.ui.registry.addToggleButton(btn.name, {\n          tooltip: btn.text,\n          icon: btn.icon,\n          onSetup: onSetupFormatToggle(editor, btn.name),\n          onAction: onActionToggleFormat(editor, btn.name)\n        });\n      });\n      for (let i = 1; i <= 6; i++) {\n        const name = 'h' + i;\n        editor.ui.registry.addToggleButton(name, {\n          text: name.toUpperCase(),\n          tooltip: 'Heading ' + i,\n          onSetup: onSetupFormatToggle(editor, name),\n          onAction: onActionToggleFormat(editor, name)\n        });\n      }\n    };\n    const registerCommandButtons = editor => {\n      global$1.each([\n        {\n          name: 'cut',\n          text: 'Cut',\n          action: 'Cut',\n          icon: 'cut'\n        },\n        {\n          name: 'copy',\n          text: 'Copy',\n          action: 'Copy',\n          icon: 'copy'\n        },\n        {\n          name: 'paste',\n          text: 'Paste',\n          action: 'Paste',\n          icon: 'paste'\n        },\n        {\n          name: 'help',\n          text: 'Help',\n          action: 'mceHelp',\n          icon: 'help'\n        },\n        {\n          name: 'selectall',\n          text: 'Select all',\n          action: 'SelectAll',\n          icon: 'select-all'\n        },\n        {\n          name: 'newdocument',\n          text: 'New document',\n          action: 'mceNewDocument',\n          icon: 'new-document'\n        },\n        {\n          name: 'removeformat',\n          text: 'Clear formatting',\n          action: 'RemoveFormat',\n          icon: 'remove-formatting'\n        },\n        {\n          name: 'remove',\n          text: 'Remove',\n          action: 'Delete',\n          icon: 'remove'\n        },\n        {\n          name: 'print',\n          text: 'Print',\n          action: 'mcePrint',\n          icon: 'print'\n        },\n        {\n          name: 'hr',\n          text: 'Horizontal line',\n          action: 'InsertHorizontalRule',\n          icon: 'horizontal-rule'\n        }\n      ], btn => {\n        editor.ui.registry.addButton(btn.name, {\n          tooltip: btn.text,\n          icon: btn.icon,\n          onAction: onActionExecCommand(editor, btn.action)\n        });\n      });\n    };\n    const registerCommandToggleButtons = editor => {\n      global$1.each([{\n          name: 'blockquote',\n          text: 'Blockquote',\n          action: 'mceBlockQuote',\n          icon: 'quote'\n        }], btn => {\n        editor.ui.registry.addToggleButton(btn.name, {\n          tooltip: btn.text,\n          icon: btn.icon,\n          onAction: onActionExecCommand(editor, btn.action),\n          onSetup: onSetupFormatToggle(editor, btn.name)\n        });\n      });\n    };\n    const registerButtons$1 = editor => {\n      registerFormatButtons(editor);\n      registerCommandButtons(editor);\n      registerCommandToggleButtons(editor);\n    };\n    const registerMenuItems$2 = editor => {\n      global$1.each([\n        {\n          name: 'bold',\n          text: 'Bold',\n          action: 'Bold',\n          icon: 'bold',\n          shortcut: 'Meta+B'\n        },\n        {\n          name: 'italic',\n          text: 'Italic',\n          action: 'Italic',\n          icon: 'italic',\n          shortcut: 'Meta+I'\n        },\n        {\n          name: 'underline',\n          text: 'Underline',\n          action: 'Underline',\n          icon: 'underline',\n          shortcut: 'Meta+U'\n        },\n        {\n          name: 'strikethrough',\n          text: 'Strikethrough',\n          action: 'Strikethrough',\n          icon: 'strike-through'\n        },\n        {\n          name: 'subscript',\n          text: 'Subscript',\n          action: 'Subscript',\n          icon: 'subscript'\n        },\n        {\n          name: 'superscript',\n          text: 'Superscript',\n          action: 'Superscript',\n          icon: 'superscript'\n        },\n        {\n          name: 'removeformat',\n          text: 'Clear formatting',\n          action: 'RemoveFormat',\n          icon: 'remove-formatting'\n        },\n        {\n          name: 'newdocument',\n          text: 'New document',\n          action: 'mceNewDocument',\n          icon: 'new-document'\n        },\n        {\n          name: 'cut',\n          text: 'Cut',\n          action: 'Cut',\n          icon: 'cut',\n          shortcut: 'Meta+X'\n        },\n        {\n          name: 'copy',\n          text: 'Copy',\n          action: 'Copy',\n          icon: 'copy',\n          shortcut: 'Meta+C'\n        },\n        {\n          name: 'paste',\n          text: 'Paste',\n          action: 'Paste',\n          icon: 'paste',\n          shortcut: 'Meta+V'\n        },\n        {\n          name: 'selectall',\n          text: 'Select all',\n          action: 'SelectAll',\n          icon: 'select-all',\n          shortcut: 'Meta+A'\n        },\n        {\n          name: 'print',\n          text: 'Print...',\n          action: 'mcePrint',\n          icon: 'print',\n          shortcut: 'Meta+P'\n        },\n        {\n          name: 'hr',\n          text: 'Horizontal line',\n          action: 'InsertHorizontalRule',\n          icon: 'horizontal-rule'\n        }\n      ], menuitem => {\n        editor.ui.registry.addMenuItem(menuitem.name, {\n          text: menuitem.text,\n          icon: menuitem.icon,\n          shortcut: menuitem.shortcut,\n          onAction: onActionExecCommand(editor, menuitem.action)\n        });\n      });\n      editor.ui.registry.addMenuItem('codeformat', {\n        text: 'Code',\n        icon: 'sourcecode',\n        onAction: onActionToggleFormat(editor, 'code')\n      });\n    };\n    const register$3 = editor => {\n      registerButtons$1(editor);\n      registerMenuItems$2(editor);\n    };\n\n    const onSetupUndoRedoState = (editor, type) => onSetupEvent(editor, 'Undo Redo AddUndo TypingUndo ClearUndos SwitchMode', api => {\n      api.setEnabled(!editor.mode.isReadOnly() && editor.undoManager[type]());\n    });\n    const registerMenuItems$1 = editor => {\n      editor.ui.registry.addMenuItem('undo', {\n        text: 'Undo',\n        icon: 'undo',\n        shortcut: 'Meta+Z',\n        onSetup: onSetupUndoRedoState(editor, 'hasUndo'),\n        onAction: onActionExecCommand(editor, 'undo')\n      });\n      editor.ui.registry.addMenuItem('redo', {\n        text: 'Redo',\n        icon: 'redo',\n        shortcut: 'Meta+Y',\n        onSetup: onSetupUndoRedoState(editor, 'hasRedo'),\n        onAction: onActionExecCommand(editor, 'redo')\n      });\n    };\n    const registerButtons = editor => {\n      editor.ui.registry.addButton('undo', {\n        tooltip: 'Undo',\n        icon: 'undo',\n        enabled: false,\n        onSetup: onSetupUndoRedoState(editor, 'hasUndo'),\n        onAction: onActionExecCommand(editor, 'undo')\n      });\n      editor.ui.registry.addButton('redo', {\n        tooltip: 'Redo',\n        icon: 'redo',\n        enabled: false,\n        onSetup: onSetupUndoRedoState(editor, 'hasRedo'),\n        onAction: onActionExecCommand(editor, 'redo')\n      });\n    };\n    const register$2 = editor => {\n      registerMenuItems$1(editor);\n      registerButtons(editor);\n    };\n\n    const onSetupVisualAidState = editor => onSetupEvent(editor, 'VisualAid', api => {\n      api.setActive(editor.hasVisual);\n    });\n    const registerMenuItems = editor => {\n      editor.ui.registry.addToggleMenuItem('visualaid', {\n        text: 'Visual aids',\n        onSetup: onSetupVisualAidState(editor),\n        onAction: onActionExecCommand(editor, 'mceToggleVisualAid')\n      });\n    };\n    const registerToolbarButton = editor => {\n      editor.ui.registry.addButton('visualaid', {\n        tooltip: 'Visual aids',\n        text: 'Visual aids',\n        onAction: onActionExecCommand(editor, 'mceToggleVisualAid')\n      });\n    };\n    const register$1 = editor => {\n      registerToolbarButton(editor);\n      registerMenuItems(editor);\n    };\n\n    const setup$6 = (editor, backstage) => {\n      register$8(editor);\n      register$3(editor);\n      register$6(editor, backstage);\n      register$2(editor);\n      register$c(editor);\n      register$1(editor);\n      register$5(editor);\n      register$7(editor);\n      register$4(editor);\n    };\n\n    const patchPipeConfig = config => isString(config) ? config.split(/[ ,]/) : config;\n    const option = name => editor => editor.options.get(name);\n    const register = editor => {\n      const registerOption = editor.options.register;\n      registerOption('contextmenu_avoid_overlap', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('contextmenu_never_use_native', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('contextmenu', {\n        processor: value => {\n          if (value === false) {\n            return {\n              value: [],\n              valid: true\n            };\n          } else if (isString(value) || isArrayOf(value, isString)) {\n            return {\n              value: patchPipeConfig(value),\n              valid: true\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be false or a string.'\n            };\n          }\n        },\n        default: 'link linkchecker image editimage table spellchecker configurepermanentpen'\n      });\n    };\n    const shouldNeverUseNative = option('contextmenu_never_use_native');\n    const getAvoidOverlapSelector = option('contextmenu_avoid_overlap');\n    const isContextMenuDisabled = editor => getContextMenu(editor).length === 0;\n    const getContextMenu = editor => {\n      const contextMenus = editor.ui.registry.getAll().contextMenus;\n      const contextMenu = editor.options.get('contextmenu');\n      if (editor.options.isSet('contextmenu')) {\n        return contextMenu;\n      } else {\n        return filter$2(contextMenu, item => has$2(contextMenus, item));\n      }\n    };\n\n    const nu = (x, y) => ({\n      type: 'makeshift',\n      x,\n      y\n    });\n    const transpose = (pos, dx, dy) => {\n      return nu(pos.x + dx, pos.y + dy);\n    };\n    const isTouchEvent$1 = e => e.type === 'longpress' || e.type.indexOf('touch') === 0;\n    const fromPageXY = e => {\n      if (isTouchEvent$1(e)) {\n        const touch = e.touches[0];\n        return nu(touch.pageX, touch.pageY);\n      } else {\n        return nu(e.pageX, e.pageY);\n      }\n    };\n    const fromClientXY = e => {\n      if (isTouchEvent$1(e)) {\n        const touch = e.touches[0];\n        return nu(touch.clientX, touch.clientY);\n      } else {\n        return nu(e.clientX, e.clientY);\n      }\n    };\n    const transposeContentAreaContainer = (element, pos) => {\n      const containerPos = global$7.DOM.getPos(element);\n      return transpose(pos, containerPos.x, containerPos.y);\n    };\n    const getPointAnchor = (editor, e) => {\n      if (e.type === 'contextmenu' || e.type === 'longpress') {\n        if (editor.inline) {\n          return fromPageXY(e);\n        } else {\n          return transposeContentAreaContainer(editor.getContentAreaContainer(), fromClientXY(e));\n        }\n      } else {\n        return getSelectionAnchor(editor);\n      }\n    };\n    const getSelectionAnchor = editor => {\n      return {\n        type: 'selection',\n        root: SugarElement.fromDom(editor.selection.getNode())\n      };\n    };\n    const getNodeAnchor = editor => ({\n      type: 'node',\n      node: Optional.some(SugarElement.fromDom(editor.selection.getNode())),\n      root: SugarElement.fromDom(editor.getBody())\n    });\n    const getAnchorSpec$1 = (editor, e, anchorType) => {\n      switch (anchorType) {\n      case 'node':\n        return getNodeAnchor(editor);\n      case 'point':\n        return getPointAnchor(editor, e);\n      case 'selection':\n        return getSelectionAnchor(editor);\n      }\n    };\n\n    const initAndShow$1 = (editor, e, buildMenu, backstage, contextmenu, anchorType) => {\n      const items = buildMenu();\n      const anchorSpec = getAnchorSpec$1(editor, e, anchorType);\n      build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {\n        isHorizontalMenu: false,\n        search: Optional.none()\n      }).map(menuData => {\n        e.preventDefault();\n        InlineView.showMenuAt(contextmenu, { anchor: anchorSpec }, {\n          menu: { markers: markers('normal') },\n          data: menuData\n        });\n      });\n    };\n\n    const layouts = {\n      onLtr: () => [\n        south$2,\n        southeast$2,\n        southwest$2,\n        northeast$2,\n        northwest$2,\n        north$2,\n        north,\n        south,\n        northeast,\n        southeast,\n        northwest,\n        southwest\n      ],\n      onRtl: () => [\n        south$2,\n        southwest$2,\n        southeast$2,\n        northwest$2,\n        northeast$2,\n        north$2,\n        north,\n        south,\n        northwest,\n        southwest,\n        northeast,\n        southeast\n      ]\n    };\n    const bubbleSize = 12;\n    const bubbleAlignments = {\n      valignCentre: [],\n      alignCentre: [],\n      alignLeft: ['tox-pop--align-left'],\n      alignRight: ['tox-pop--align-right'],\n      right: ['tox-pop--right'],\n      left: ['tox-pop--left'],\n      bottom: ['tox-pop--bottom'],\n      top: ['tox-pop--top']\n    };\n    const isTouchWithinSelection = (editor, e) => {\n      const selection = editor.selection;\n      if (selection.isCollapsed() || e.touches.length < 1) {\n        return false;\n      } else {\n        const touch = e.touches[0];\n        const rng = selection.getRng();\n        const rngRectOpt = getFirstRect(editor.getWin(), SimSelection.domRange(rng));\n        return rngRectOpt.exists(rngRect => rngRect.left <= touch.clientX && rngRect.right >= touch.clientX && rngRect.top <= touch.clientY && rngRect.bottom >= touch.clientY);\n      }\n    };\n    const setupiOSOverrides = editor => {\n      const originalSelection = editor.selection.getRng();\n      const selectionReset = () => {\n        global$9.setEditorTimeout(editor, () => {\n          editor.selection.setRng(originalSelection);\n        }, 10);\n        unbindEventListeners();\n      };\n      editor.once('touchend', selectionReset);\n      const preventMousedown = e => {\n        e.preventDefault();\n        e.stopImmediatePropagation();\n      };\n      editor.on('mousedown', preventMousedown, true);\n      const clearSelectionReset = () => unbindEventListeners();\n      editor.once('longpresscancel', clearSelectionReset);\n      const unbindEventListeners = () => {\n        editor.off('touchend', selectionReset);\n        editor.off('longpresscancel', clearSelectionReset);\n        editor.off('mousedown', preventMousedown);\n      };\n    };\n    const getAnchorSpec = (editor, e, anchorType) => {\n      const anchorSpec = getAnchorSpec$1(editor, e, anchorType);\n      const bubbleYOffset = anchorType === 'point' ? bubbleSize : 0;\n      return {\n        bubble: nu$5(0, bubbleYOffset, bubbleAlignments),\n        layouts,\n        overrides: {\n          maxWidthFunction: expandable(),\n          maxHeightFunction: expandable$1()\n        },\n        ...anchorSpec\n      };\n    };\n    const show = (editor, e, items, backstage, contextmenu, anchorType, highlightImmediately) => {\n      const anchorSpec = getAnchorSpec(editor, e, anchorType);\n      build(items, ItemResponse$1.CLOSE_ON_EXECUTE, backstage, {\n        isHorizontalMenu: true,\n        search: Optional.none()\n      }).map(menuData => {\n        e.preventDefault();\n        const highlightOnOpen = highlightImmediately ? HighlightOnOpen.HighlightMenuAndItem : HighlightOnOpen.HighlightNone;\n        InlineView.showMenuWithinBounds(contextmenu, { anchor: anchorSpec }, {\n          menu: {\n            markers: markers('normal'),\n            highlightOnOpen\n          },\n          data: menuData,\n          type: 'horizontal'\n        }, () => Optional.some(getContextToolbarBounds(editor, backstage.shared, anchorType === 'node' ? 'node' : 'selection')));\n        editor.dispatch(hideContextToolbarEvent);\n      });\n    };\n    const initAndShow = (editor, e, buildMenu, backstage, contextmenu, anchorType) => {\n      const detection = detect$1();\n      const isiOS = detection.os.isiOS();\n      const isMacOS = detection.os.isMacOS();\n      const isAndroid = detection.os.isAndroid();\n      const isTouch = detection.deviceType.isTouch();\n      const shouldHighlightImmediately = () => !(isAndroid || isiOS || isMacOS && isTouch);\n      const open = () => {\n        const items = buildMenu();\n        show(editor, e, items, backstage, contextmenu, anchorType, shouldHighlightImmediately());\n      };\n      if ((isMacOS || isiOS) && anchorType !== 'node') {\n        const openiOS = () => {\n          setupiOSOverrides(editor);\n          open();\n        };\n        if (isTouchWithinSelection(editor, e)) {\n          openiOS();\n        } else {\n          editor.once('selectionchange', openiOS);\n          editor.once('touchend', () => editor.off('selectionchange', openiOS));\n        }\n      } else {\n        open();\n      }\n    };\n\n    const isSeparator = item => isString(item) ? item === '|' : item.type === 'separator';\n    const separator = { type: 'separator' };\n    const makeContextItem = item => {\n      const commonMenuItem = item => ({\n        text: item.text,\n        icon: item.icon,\n        enabled: item.enabled,\n        shortcut: item.shortcut\n      });\n      if (isString(item)) {\n        return item;\n      } else {\n        switch (item.type) {\n        case 'separator':\n          return separator;\n        case 'submenu':\n          return {\n            type: 'nestedmenuitem',\n            ...commonMenuItem(item),\n            getSubmenuItems: () => {\n              const items = item.getSubmenuItems();\n              if (isString(items)) {\n                return items;\n              } else {\n                return map$2(items, makeContextItem);\n              }\n            }\n          };\n        default:\n          const commonItem = item;\n          return {\n            type: 'menuitem',\n            ...commonMenuItem(commonItem),\n            onAction: noarg(commonItem.onAction)\n          };\n        }\n      }\n    };\n    const addContextMenuGroup = (xs, groupItems) => {\n      if (groupItems.length === 0) {\n        return xs;\n      }\n      const lastMenuItem = last$1(xs).filter(item => !isSeparator(item));\n      const before = lastMenuItem.fold(() => [], _ => [separator]);\n      return xs.concat(before).concat(groupItems).concat([separator]);\n    };\n    const generateContextMenu = (contextMenus, menuConfig, selectedElement) => {\n      const sections = foldl(menuConfig, (acc, name) => {\n        return get$g(contextMenus, name.toLowerCase()).map(menu => {\n          const items = menu.update(selectedElement);\n          if (isString(items)) {\n            return addContextMenuGroup(acc, items.split(' '));\n          } else if (items.length > 0) {\n            const allItems = map$2(items, makeContextItem);\n            return addContextMenuGroup(acc, allItems);\n          } else {\n            return acc;\n          }\n        }).getOrThunk(() => acc.concat([name]));\n      }, []);\n      if (sections.length > 0 && isSeparator(sections[sections.length - 1])) {\n        sections.pop();\n      }\n      return sections;\n    };\n    const isNativeOverrideKeyEvent = (editor, e) => e.ctrlKey && !shouldNeverUseNative(editor);\n    const isTouchEvent = e => e.type === 'longpress' || has$2(e, 'touches');\n    const isTriggeredByKeyboard = (editor, e) => !isTouchEvent(e) && (e.button !== 2 || e.target === editor.getBody() && e.pointerType === '');\n    const getSelectedElement = (editor, e) => isTriggeredByKeyboard(editor, e) ? editor.selection.getStart(true) : e.target;\n    const getAnchorType = (editor, e) => {\n      const selector = getAvoidOverlapSelector(editor);\n      const anchorType = isTriggeredByKeyboard(editor, e) ? 'selection' : 'point';\n      if (isNotEmpty(selector)) {\n        const target = getSelectedElement(editor, e);\n        const selectorExists = closest(SugarElement.fromDom(target), selector);\n        return selectorExists ? 'node' : anchorType;\n      } else {\n        return anchorType;\n      }\n    };\n    const setup$5 = (editor, lazySink, backstage) => {\n      const detection = detect$1();\n      const isTouch = detection.deviceType.isTouch;\n      const contextmenu = build$1(InlineView.sketch({\n        dom: { tag: 'div' },\n        lazySink,\n        onEscape: () => editor.focus(),\n        onShow: () => backstage.setContextMenuState(true),\n        onHide: () => backstage.setContextMenuState(false),\n        fireDismissalEventInstead: {},\n        inlineBehaviours: derive$1([config('dismissContextMenu', [run$1(dismissRequested(), (comp, _se) => {\n              Sandboxing.close(comp);\n              editor.focus();\n            })])])\n      }));\n      const hideContextMenu = () => InlineView.hide(contextmenu);\n      const showContextMenu = e => {\n        if (shouldNeverUseNative(editor)) {\n          e.preventDefault();\n        }\n        if (isNativeOverrideKeyEvent(editor, e) || isContextMenuDisabled(editor)) {\n          return;\n        }\n        const anchorType = getAnchorType(editor, e);\n        const buildMenu = () => {\n          const selectedElement = getSelectedElement(editor, e);\n          const registry = editor.ui.registry.getAll();\n          const menuConfig = getContextMenu(editor);\n          return generateContextMenu(registry.contextMenus, menuConfig, selectedElement);\n        };\n        const initAndShow$2 = isTouch() ? initAndShow : initAndShow$1;\n        initAndShow$2(editor, e, buildMenu, backstage, contextmenu, anchorType);\n      };\n      editor.on('init', () => {\n        const hideEvents = 'ResizeEditor ScrollContent ScrollWindow longpresscancel' + (isTouch() ? '' : ' ResizeWindow');\n        editor.on(hideEvents, hideContextMenu);\n        editor.on('longpress contextmenu', showContextMenu);\n      });\n    };\n\n    const adt = Adt.generate([\n      {\n        offset: [\n          'x',\n          'y'\n        ]\n      },\n      {\n        absolute: [\n          'x',\n          'y'\n        ]\n      },\n      {\n        fixed: [\n          'x',\n          'y'\n        ]\n      }\n    ]);\n    const subtract = change => point => point.translate(-change.left, -change.top);\n    const add = change => point => point.translate(change.left, change.top);\n    const transform = changes => (x, y) => foldl(changes, (rest, f) => f(rest), SugarPosition(x, y));\n    const asFixed = (coord, scroll, origin) => coord.fold(transform([\n      add(origin),\n      subtract(scroll)\n    ]), transform([subtract(scroll)]), transform([]));\n    const asAbsolute = (coord, scroll, origin) => coord.fold(transform([add(origin)]), transform([]), transform([add(scroll)]));\n    const asOffset = (coord, scroll, origin) => coord.fold(transform([]), transform([subtract(origin)]), transform([\n      add(scroll),\n      subtract(origin)\n    ]));\n    const withinRange = (coord1, coord2, xRange, yRange, scroll, origin) => {\n      const a1 = asAbsolute(coord1, scroll, origin);\n      const a2 = asAbsolute(coord2, scroll, origin);\n      return Math.abs(a1.left - a2.left) <= xRange && Math.abs(a1.top - a2.top) <= yRange;\n    };\n    const getDeltas = (coord1, coord2, xRange, yRange, scroll, origin) => {\n      const a1 = asAbsolute(coord1, scroll, origin);\n      const a2 = asAbsolute(coord2, scroll, origin);\n      const left = Math.abs(a1.left - a2.left);\n      const top = Math.abs(a1.top - a2.top);\n      return SugarPosition(left, top);\n    };\n    const toStyles = (coord, scroll, origin) => {\n      const stylesOpt = coord.fold((x, y) => ({\n        position: Optional.some('absolute'),\n        left: Optional.some(x + 'px'),\n        top: Optional.some(y + 'px')\n      }), (x, y) => ({\n        position: Optional.some('absolute'),\n        left: Optional.some(x - origin.left + 'px'),\n        top: Optional.some(y - origin.top + 'px')\n      }), (x, y) => ({\n        position: Optional.some('fixed'),\n        left: Optional.some(x + 'px'),\n        top: Optional.some(y + 'px')\n      }));\n      return {\n        right: Optional.none(),\n        bottom: Optional.none(),\n        ...stylesOpt\n      };\n    };\n    const translate = (coord, deltaX, deltaY) => coord.fold((x, y) => offset(x + deltaX, y + deltaY), (x, y) => absolute(x + deltaX, y + deltaY), (x, y) => fixed(x + deltaX, y + deltaY));\n    const absorb = (partialCoord, originalCoord, scroll, origin) => {\n      const absorbOne = (stencil, nu) => (optX, optY) => {\n        const original = stencil(originalCoord, scroll, origin);\n        return nu(optX.getOr(original.left), optY.getOr(original.top));\n      };\n      return partialCoord.fold(absorbOne(asOffset, offset), absorbOne(asAbsolute, absolute), absorbOne(asFixed, fixed));\n    };\n    const offset = adt.offset;\n    const absolute = adt.absolute;\n    const fixed = adt.fixed;\n\n    const parseAttrToInt = (element, name) => {\n      const value = get$f(element, name);\n      return isUndefined(value) ? NaN : parseInt(value, 10);\n    };\n    const get = (component, snapsInfo) => {\n      const element = component.element;\n      const x = parseAttrToInt(element, snapsInfo.leftAttr);\n      const y = parseAttrToInt(element, snapsInfo.topAttr);\n      return isNaN(x) || isNaN(y) ? Optional.none() : Optional.some(SugarPosition(x, y));\n    };\n    const set = (component, snapsInfo, pt) => {\n      const element = component.element;\n      set$9(element, snapsInfo.leftAttr, pt.left + 'px');\n      set$9(element, snapsInfo.topAttr, pt.top + 'px');\n    };\n    const clear = (component, snapsInfo) => {\n      const element = component.element;\n      remove$7(element, snapsInfo.leftAttr);\n      remove$7(element, snapsInfo.topAttr);\n    };\n\n    const getCoords = (component, snapInfo, coord, delta) => get(component, snapInfo).fold(() => coord, fixed$1 => fixed(fixed$1.left + delta.left, fixed$1.top + delta.top));\n    const moveOrSnap = (component, snapInfo, coord, delta, scroll, origin) => {\n      const newCoord = getCoords(component, snapInfo, coord, delta);\n      const snap = snapInfo.mustSnap ? findClosestSnap(component, snapInfo, newCoord, scroll, origin) : findSnap(component, snapInfo, newCoord, scroll, origin);\n      const fixedCoord = asFixed(newCoord, scroll, origin);\n      set(component, snapInfo, fixedCoord);\n      return snap.fold(() => ({\n        coord: fixed(fixedCoord.left, fixedCoord.top),\n        extra: Optional.none()\n      }), spanned => ({\n        coord: spanned.output,\n        extra: spanned.extra\n      }));\n    };\n    const stopDrag = (component, snapInfo) => {\n      clear(component, snapInfo);\n    };\n    const findMatchingSnap = (snaps, newCoord, scroll, origin) => findMap(snaps, snap => {\n      const sensor = snap.sensor;\n      const inRange = withinRange(newCoord, sensor, snap.range.left, snap.range.top, scroll, origin);\n      return inRange ? Optional.some({\n        output: absorb(snap.output, newCoord, scroll, origin),\n        extra: snap.extra\n      }) : Optional.none();\n    });\n    const findClosestSnap = (component, snapInfo, newCoord, scroll, origin) => {\n      const snaps = snapInfo.getSnapPoints(component);\n      const matchSnap = findMatchingSnap(snaps, newCoord, scroll, origin);\n      return matchSnap.orThunk(() => {\n        const bestSnap = foldl(snaps, (acc, snap) => {\n          const sensor = snap.sensor;\n          const deltas = getDeltas(newCoord, sensor, snap.range.left, snap.range.top, scroll, origin);\n          return acc.deltas.fold(() => ({\n            deltas: Optional.some(deltas),\n            snap: Optional.some(snap)\n          }), bestDeltas => {\n            const currAvg = (deltas.left + deltas.top) / 2;\n            const bestAvg = (bestDeltas.left + bestDeltas.top) / 2;\n            if (currAvg <= bestAvg) {\n              return {\n                deltas: Optional.some(deltas),\n                snap: Optional.some(snap)\n              };\n            } else {\n              return acc;\n            }\n          });\n        }, {\n          deltas: Optional.none(),\n          snap: Optional.none()\n        });\n        return bestSnap.snap.map(snap => ({\n          output: absorb(snap.output, newCoord, scroll, origin),\n          extra: snap.extra\n        }));\n      });\n    };\n    const findSnap = (component, snapInfo, newCoord, scroll, origin) => {\n      const snaps = snapInfo.getSnapPoints(component);\n      return findMatchingSnap(snaps, newCoord, scroll, origin);\n    };\n    const snapTo$1 = (snap, scroll, origin) => ({\n      coord: absorb(snap.output, snap.output, scroll, origin),\n      extra: snap.extra\n    });\n\n    const snapTo = (component, dragConfig, _state, snap) => {\n      const target = dragConfig.getTarget(component.element);\n      if (dragConfig.repositionTarget) {\n        const doc = owner$4(component.element);\n        const scroll = get$b(doc);\n        const origin = getOrigin(target);\n        const snapPin = snapTo$1(snap, scroll, origin);\n        const styles = toStyles(snapPin.coord, scroll, origin);\n        setOptions(target, styles);\n      }\n    };\n\n    var DraggingApis = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        snapTo: snapTo\n    });\n\n    const initialAttribute = 'data-initial-z-index';\n    const resetZIndex = blocker => {\n      parent(blocker.element).filter(isElement$1).each(root => {\n        getOpt(root, initialAttribute).fold(() => remove$6(root, 'z-index'), zIndex => set$8(root, 'z-index', zIndex));\n        remove$7(root, initialAttribute);\n      });\n    };\n    const changeZIndex = blocker => {\n      parent(blocker.element).filter(isElement$1).each(root => {\n        getRaw(root, 'z-index').each(zindex => {\n          set$9(root, initialAttribute, zindex);\n        });\n        set$8(root, 'z-index', get$e(blocker.element, 'z-index'));\n      });\n    };\n    const instigate = (anyComponent, blocker) => {\n      anyComponent.getSystem().addToGui(blocker);\n      changeZIndex(blocker);\n    };\n    const discard = blocker => {\n      resetZIndex(blocker);\n      blocker.getSystem().removeFromGui(blocker);\n    };\n    const createComponent = (component, blockerClass, blockerEvents) => component.getSystem().build(Container.sketch({\n      dom: {\n        styles: {\n          'left': '0px',\n          'top': '0px',\n          'width': '100%',\n          'height': '100%',\n          'position': 'fixed',\n          'z-index': '1000000000000000'\n        },\n        classes: [blockerClass]\n      },\n      events: blockerEvents\n    }));\n\n    var SnapSchema = optionObjOf('snaps', [\n      required$1('getSnapPoints'),\n      onHandler('onSensor'),\n      required$1('leftAttr'),\n      required$1('topAttr'),\n      defaulted('lazyViewport', win),\n      defaulted('mustSnap', false)\n    ]);\n\n    const schema$6 = [\n      defaulted('useFixed', never),\n      required$1('blockerClass'),\n      defaulted('getTarget', identity),\n      defaulted('onDrag', noop),\n      defaulted('repositionTarget', true),\n      defaulted('onDrop', noop),\n      defaultedFunction('getBounds', win),\n      SnapSchema\n    ];\n\n    const getCurrentCoord = target => lift3(getRaw(target, 'left'), getRaw(target, 'top'), getRaw(target, 'position'), (left, top, position) => {\n      const nu = position === 'fixed' ? fixed : offset;\n      return nu(parseInt(left, 10), parseInt(top, 10));\n    }).getOrThunk(() => {\n      const location = absolute$3(target);\n      return absolute(location.left, location.top);\n    });\n    const clampCoords = (component, coords, scroll, origin, startData) => {\n      const bounds = startData.bounds;\n      const absoluteCoord = asAbsolute(coords, scroll, origin);\n      const newX = clamp(absoluteCoord.left, bounds.x, bounds.x + bounds.width - startData.width);\n      const newY = clamp(absoluteCoord.top, bounds.y, bounds.y + bounds.height - startData.height);\n      const newCoords = absolute(newX, newY);\n      return coords.fold(() => {\n        const offset$1 = asOffset(newCoords, scroll, origin);\n        return offset(offset$1.left, offset$1.top);\n      }, constant$1(newCoords), () => {\n        const fixed$1 = asFixed(newCoords, scroll, origin);\n        return fixed(fixed$1.left, fixed$1.top);\n      });\n    };\n    const calcNewCoord = (component, optSnaps, currentCoord, scroll, origin, delta, startData) => {\n      const newCoord = optSnaps.fold(() => {\n        const translated = translate(currentCoord, delta.left, delta.top);\n        const fixedCoord = asFixed(translated, scroll, origin);\n        return fixed(fixedCoord.left, fixedCoord.top);\n      }, snapInfo => {\n        const snapping = moveOrSnap(component, snapInfo, currentCoord, delta, scroll, origin);\n        snapping.extra.each(extra => {\n          snapInfo.onSensor(component, extra);\n        });\n        return snapping.coord;\n      });\n      return clampCoords(component, newCoord, scroll, origin, startData);\n    };\n    const dragBy = (component, dragConfig, startData, delta) => {\n      const target = dragConfig.getTarget(component.element);\n      if (dragConfig.repositionTarget) {\n        const doc = owner$4(component.element);\n        const scroll = get$b(doc);\n        const origin = getOrigin(target);\n        const currentCoord = getCurrentCoord(target);\n        const newCoord = calcNewCoord(component, dragConfig.snaps, currentCoord, scroll, origin, delta, startData);\n        const styles = toStyles(newCoord, scroll, origin);\n        setOptions(target, styles);\n      }\n      dragConfig.onDrag(component, target, delta);\n    };\n\n    const calcStartData = (dragConfig, comp) => ({\n      bounds: dragConfig.getBounds(),\n      height: getOuter$2(comp.element),\n      width: getOuter$1(comp.element)\n    });\n    const move = (component, dragConfig, dragState, dragMode, event) => {\n      const delta = dragState.update(dragMode, event);\n      const dragStartData = dragState.getStartData().getOrThunk(() => calcStartData(dragConfig, component));\n      delta.each(dlt => {\n        dragBy(component, dragConfig, dragStartData, dlt);\n      });\n    };\n    const stop = (component, blocker, dragConfig, dragState) => {\n      blocker.each(discard);\n      dragConfig.snaps.each(snapInfo => {\n        stopDrag(component, snapInfo);\n      });\n      const target = dragConfig.getTarget(component.element);\n      dragState.reset();\n      dragConfig.onDrop(component, target);\n    };\n    const handlers = events => (dragConfig, dragState) => {\n      const updateStartState = comp => {\n        dragState.setStartData(calcStartData(dragConfig, comp));\n      };\n      return derive$2([\n        run$1(windowScroll(), comp => {\n          dragState.getStartData().each(() => updateStartState(comp));\n        }),\n        ...events(dragConfig, dragState, updateStartState)\n      ]);\n    };\n\n    const init$2 = dragApi => derive$2([\n      run$1(mousedown(), dragApi.forceDrop),\n      run$1(mouseup(), dragApi.drop),\n      run$1(mousemove(), (comp, simulatedEvent) => {\n        dragApi.move(simulatedEvent.event);\n      }),\n      run$1(mouseout(), dragApi.delayDrop)\n    ]);\n\n    const getData$1 = event => Optional.from(SugarPosition(event.x, event.y));\n    const getDelta$1 = (old, nu) => SugarPosition(nu.left - old.left, nu.top - old.top);\n\n    var MouseData = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        getData: getData$1,\n        getDelta: getDelta$1\n    });\n\n    const events$2 = (dragConfig, dragState, updateStartState) => [run$1(mousedown(), (component, simulatedEvent) => {\n        const raw = simulatedEvent.event.raw;\n        if (raw.button !== 0) {\n          return;\n        }\n        simulatedEvent.stop();\n        const stop$1 = () => stop(component, Optional.some(blocker), dragConfig, dragState);\n        const delayDrop = DelayedFunction(stop$1, 200);\n        const dragApi = {\n          drop: stop$1,\n          delayDrop: delayDrop.schedule,\n          forceDrop: stop$1,\n          move: event => {\n            delayDrop.cancel();\n            move(component, dragConfig, dragState, MouseData, event);\n          }\n        };\n        const blocker = createComponent(component, dragConfig.blockerClass, init$2(dragApi));\n        const start = () => {\n          updateStartState(component);\n          instigate(component, blocker);\n        };\n        start();\n      })];\n    const schema$5 = [\n      ...schema$6,\n      output$1('dragger', { handlers: handlers(events$2) })\n    ];\n\n    const init$1 = dragApi => derive$2([\n      run$1(touchstart(), dragApi.forceDrop),\n      run$1(touchend(), dragApi.drop),\n      run$1(touchcancel(), dragApi.drop),\n      run$1(touchmove(), (comp, simulatedEvent) => {\n        dragApi.move(simulatedEvent.event);\n      })\n    ]);\n\n    const getDataFrom = touches => {\n      const touch = touches[0];\n      return Optional.some(SugarPosition(touch.clientX, touch.clientY));\n    };\n    const getData = event => {\n      const raw = event.raw;\n      const touches = raw.touches;\n      return touches.length === 1 ? getDataFrom(touches) : Optional.none();\n    };\n    const getDelta = (old, nu) => SugarPosition(nu.left - old.left, nu.top - old.top);\n\n    var TouchData = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        getData: getData,\n        getDelta: getDelta\n    });\n\n    const events$1 = (dragConfig, dragState, updateStartState) => {\n      const blockerSingleton = value$2();\n      const stopBlocking = component => {\n        stop(component, blockerSingleton.get(), dragConfig, dragState);\n        blockerSingleton.clear();\n      };\n      return [\n        run$1(touchstart(), (component, simulatedEvent) => {\n          simulatedEvent.stop();\n          const stop = () => stopBlocking(component);\n          const dragApi = {\n            drop: stop,\n            delayDrop: noop,\n            forceDrop: stop,\n            move: event => {\n              move(component, dragConfig, dragState, TouchData, event);\n            }\n          };\n          const blocker = createComponent(component, dragConfig.blockerClass, init$1(dragApi));\n          blockerSingleton.set(blocker);\n          const start = () => {\n            updateStartState(component);\n            instigate(component, blocker);\n          };\n          start();\n        }),\n        run$1(touchmove(), (component, simulatedEvent) => {\n          simulatedEvent.stop();\n          move(component, dragConfig, dragState, TouchData, simulatedEvent.event);\n        }),\n        run$1(touchend(), (component, simulatedEvent) => {\n          simulatedEvent.stop();\n          stopBlocking(component);\n        }),\n        run$1(touchcancel(), stopBlocking)\n      ];\n    };\n    const schema$4 = [\n      ...schema$6,\n      output$1('dragger', { handlers: handlers(events$1) })\n    ];\n\n    const events = (dragConfig, dragState, updateStartState) => [\n      ...events$2(dragConfig, dragState, updateStartState),\n      ...events$1(dragConfig, dragState, updateStartState)\n    ];\n    const schema$3 = [\n      ...schema$6,\n      output$1('dragger', { handlers: handlers(events) })\n    ];\n\n    const mouse = schema$5;\n    const touch = schema$4;\n    const mouseOrTouch = schema$3;\n\n    var DraggingBranches = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        mouse: mouse,\n        touch: touch,\n        mouseOrTouch: mouseOrTouch\n    });\n\n    const init = () => {\n      let previous = Optional.none();\n      let startData = Optional.none();\n      const reset = () => {\n        previous = Optional.none();\n        startData = Optional.none();\n      };\n      const calculateDelta = (mode, nu) => {\n        const result = previous.map(old => mode.getDelta(old, nu));\n        previous = Optional.some(nu);\n        return result;\n      };\n      const update = (mode, dragEvent) => mode.getData(dragEvent).bind(nuData => calculateDelta(mode, nuData));\n      const setStartData = data => {\n        startData = Optional.some(data);\n      };\n      const getStartData = () => startData;\n      const readState = constant$1({});\n      return nu$8({\n        readState,\n        reset,\n        update,\n        getStartData,\n        setStartData\n      });\n    };\n\n    var DragState = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        init: init\n    });\n\n    const Dragging = createModes({\n      branchKey: 'mode',\n      branches: DraggingBranches,\n      name: 'dragging',\n      active: {\n        events: (dragConfig, dragState) => {\n          const dragger = dragConfig.dragger;\n          return dragger.handlers(dragConfig, dragState);\n        }\n      },\n      extra: {\n        snap: sConfig => ({\n          sensor: sConfig.sensor,\n          range: sConfig.range,\n          output: sConfig.output,\n          extra: Optional.from(sConfig.extra)\n        })\n      },\n      state: DragState,\n      apis: DraggingApis\n    });\n\n    const snapWidth = 40;\n    const snapOffset = snapWidth / 2;\n    const calcSnap = (selectorOpt, td, x, y, width, height) => selectorOpt.fold(() => Dragging.snap({\n      sensor: absolute(x - snapOffset, y - snapOffset),\n      range: SugarPosition(width, height),\n      output: absolute(Optional.some(x), Optional.some(y)),\n      extra: { td }\n    }), selectorHandle => {\n      const sensorLeft = x - snapOffset;\n      const sensorTop = y - snapOffset;\n      const sensorWidth = snapWidth;\n      const sensorHeight = snapWidth;\n      const rect = selectorHandle.element.dom.getBoundingClientRect();\n      return Dragging.snap({\n        sensor: absolute(sensorLeft, sensorTop),\n        range: SugarPosition(sensorWidth, sensorHeight),\n        output: absolute(Optional.some(x - rect.width / 2), Optional.some(y - rect.height / 2)),\n        extra: { td }\n      });\n    });\n    const getSnapsConfig = (getSnapPoints, cell, onChange) => {\n      const isSameCell = (cellOpt, td) => cellOpt.exists(currentTd => eq(currentTd, td));\n      return {\n        getSnapPoints,\n        leftAttr: 'data-drag-left',\n        topAttr: 'data-drag-top',\n        onSensor: (component, extra) => {\n          const td = extra.td;\n          if (!isSameCell(cell.get(), td)) {\n            cell.set(td);\n            onChange(td);\n          }\n        },\n        mustSnap: true\n      };\n    };\n    const createSelector = snaps => record(Button.sketch({\n      dom: {\n        tag: 'div',\n        classes: ['tox-selector']\n      },\n      buttonBehaviours: derive$1([\n        Dragging.config({\n          mode: 'mouseOrTouch',\n          blockerClass: 'blocker',\n          snaps\n        }),\n        Unselecting.config({})\n      ]),\n      eventOrder: {\n        mousedown: [\n          'dragging',\n          'alloy.base.behaviour'\n        ],\n        touchstart: [\n          'dragging',\n          'alloy.base.behaviour'\n        ]\n      }\n    }));\n    const setup$4 = (editor, sink) => {\n      const tlTds = Cell([]);\n      const brTds = Cell([]);\n      const isVisible = Cell(false);\n      const startCell = value$2();\n      const finishCell = value$2();\n      const getTopLeftSnap = td => {\n        const box = absolute$2(td);\n        return calcSnap(memTopLeft.getOpt(sink), td, box.x, box.y, box.width, box.height);\n      };\n      const getTopLeftSnaps = () => map$2(tlTds.get(), td => getTopLeftSnap(td));\n      const getBottomRightSnap = td => {\n        const box = absolute$2(td);\n        return calcSnap(memBottomRight.getOpt(sink), td, box.right, box.bottom, box.width, box.height);\n      };\n      const getBottomRightSnaps = () => map$2(brTds.get(), td => getBottomRightSnap(td));\n      const topLeftSnaps = getSnapsConfig(getTopLeftSnaps, startCell, start => {\n        finishCell.get().each(finish => {\n          editor.dispatch('TableSelectorChange', {\n            start,\n            finish\n          });\n        });\n      });\n      const bottomRightSnaps = getSnapsConfig(getBottomRightSnaps, finishCell, finish => {\n        startCell.get().each(start => {\n          editor.dispatch('TableSelectorChange', {\n            start,\n            finish\n          });\n        });\n      });\n      const memTopLeft = createSelector(topLeftSnaps);\n      const memBottomRight = createSelector(bottomRightSnaps);\n      const topLeft = build$1(memTopLeft.asSpec());\n      const bottomRight = build$1(memBottomRight.asSpec());\n      const showOrHideHandle = (selector, cell, isAbove, isBelow) => {\n        const cellRect = cell.dom.getBoundingClientRect();\n        remove$6(selector.element, 'display');\n        const viewportHeight = defaultView(SugarElement.fromDom(editor.getBody())).dom.innerHeight;\n        const aboveViewport = isAbove(cellRect);\n        const belowViewport = isBelow(cellRect, viewportHeight);\n        if (aboveViewport || belowViewport) {\n          set$8(selector.element, 'display', 'none');\n        }\n      };\n      const snapTo = (selector, cell, getSnapConfig, pos) => {\n        const snap = getSnapConfig(cell);\n        Dragging.snapTo(selector, snap);\n        const isAbove = rect => rect[pos] < 0;\n        const isBelow = (rect, viewportHeight) => rect[pos] > viewportHeight;\n        showOrHideHandle(selector, cell, isAbove, isBelow);\n      };\n      const snapTopLeft = cell => snapTo(topLeft, cell, getTopLeftSnap, 'top');\n      const snapLastTopLeft = () => startCell.get().each(snapTopLeft);\n      const snapBottomRight = cell => snapTo(bottomRight, cell, getBottomRightSnap, 'bottom');\n      const snapLastBottomRight = () => finishCell.get().each(snapBottomRight);\n      if (detect$1().deviceType.isTouch()) {\n        editor.on('TableSelectionChange', e => {\n          if (!isVisible.get()) {\n            attach(sink, topLeft);\n            attach(sink, bottomRight);\n            isVisible.set(true);\n          }\n          startCell.set(e.start);\n          finishCell.set(e.finish);\n          e.otherCells.each(otherCells => {\n            tlTds.set(otherCells.upOrLeftCells);\n            brTds.set(otherCells.downOrRightCells);\n            snapTopLeft(e.start);\n            snapBottomRight(e.finish);\n          });\n        });\n        editor.on('ResizeEditor ResizeWindow ScrollContent', () => {\n          snapLastTopLeft();\n          snapLastBottomRight();\n        });\n        editor.on('TableSelectionClear', () => {\n          if (isVisible.get()) {\n            detach(topLeft);\n            detach(bottomRight);\n            isVisible.set(false);\n          }\n          startCell.clear();\n          finishCell.clear();\n        });\n      }\n    };\n\n    var Logo = \"<svg width=\\\"50px\\\" height=\\\"16px\\\" viewBox=\\\"0 0 50 16\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\">\\n  <path fill-rule=\\\"evenodd\\\" clip-rule=\\\"evenodd\\\" d=\\\"M10.143 0c2.608.015 5.186 2.178 5.186 5.331 0 0 .077 3.812-.084 4.87-.361 2.41-2.164 4.074-4.65 4.496-1.453.284-2.523.49-3.212.623-.373.071-.634.122-.785.152-.184.038-.997.145-1.35.145-2.732 0-5.21-2.04-5.248-5.33 0 0 0-3.514.03-4.442.093-2.4 1.758-4.342 4.926-4.963 0 0 3.875-.752 4.036-.782.368-.07.775-.1 1.15-.1Zm1.826 2.8L5.83 3.989v2.393l-2.455.475v5.968l6.137-1.189V9.243l2.456-.476V2.8ZM5.83 6.382l3.682-.713v3.574l-3.682.713V6.382Zm27.173-1.64-.084-1.066h-2.226v9.132h2.456V7.743c-.008-1.151.998-2.064 2.149-2.072 1.15-.008 1.987.92 1.995 2.072v5.065h2.455V7.359c-.015-2.18-1.657-3.929-3.837-3.913a3.993 3.993 0 0 0-2.908 1.296Zm-6.3-4.266L29.16 0v2.387l-2.456.475V.476Zm0 3.2v9.132h2.456V3.676h-2.456Zm18.179 11.787L49.11 3.676H46.58l-1.612 4.527-.46 1.382-.384-1.382-1.611-4.527H39.98l3.3 9.132L42.15 16l2.732-.537ZM22.867 9.738c0 .752.568 1.075.921 1.075.353 0 .668-.047.998-.154l.537 1.765c-.23.154-.92.537-2.225.537-1.305 0-2.655-.997-2.686-2.686a136.877 136.877 0 0 1 0-4.374H18.8V3.676h1.612v-1.98l2.455-.476v2.456h2.302V5.9h-2.302v3.837Z\\\"/>\\n</svg>\\n\";\n\n    const isHidden = elm => elm.nodeName === 'BR' || !!elm.getAttribute('data-mce-bogus') || elm.getAttribute('data-mce-type') === 'bookmark';\n    const renderElementPath = (editor, settings, providersBackstage) => {\n      var _a;\n      const delimiter = (_a = settings.delimiter) !== null && _a !== void 0 ? _a : '\\u203A';\n      const renderElement = (name, element, index) => Button.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-statusbar__path-item'],\n          attributes: {\n            'data-index': index,\n            'aria-level': index + 1\n          }\n        },\n        components: [text$2(name)],\n        action: _btn => {\n          editor.focus();\n          editor.selection.select(element);\n          editor.nodeChanged();\n        },\n        buttonBehaviours: derive$1([\n          DisablingConfigs.button(providersBackstage.isDisabled),\n          receivingConfig()\n        ])\n      });\n      const renderDivider = () => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-statusbar__path-divider'],\n          attributes: { 'aria-hidden': true }\n        },\n        components: [text$2(` ${ delimiter } `)]\n      });\n      const renderPathData = data => foldl(data, (acc, path, index) => {\n        const element = renderElement(path.name, path.element, index);\n        if (index === 0) {\n          return acc.concat([element]);\n        } else {\n          return acc.concat([\n            renderDivider(),\n            element\n          ]);\n        }\n      }, []);\n      const updatePath = parents => {\n        const newPath = [];\n        let i = parents.length;\n        while (i-- > 0) {\n          const parent = parents[i];\n          if (parent.nodeType === 1 && !isHidden(parent)) {\n            const args = fireResolveName(editor, parent);\n            if (!args.isDefaultPrevented()) {\n              newPath.push({\n                name: args.name,\n                element: parent\n              });\n            }\n            if (args.isPropagationStopped()) {\n              break;\n            }\n          }\n        }\n        return newPath;\n      };\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-statusbar__path'],\n          attributes: { role: 'navigation' }\n        },\n        behaviours: derive$1([\n          Keying.config({\n            mode: 'flow',\n            selector: 'div[role=button]'\n          }),\n          Disabling.config({ disabled: providersBackstage.isDisabled }),\n          receivingConfig(),\n          Tabstopping.config({}),\n          Replacing.config({}),\n          config('elementPathEvents', [runOnAttached((comp, _e) => {\n              editor.shortcuts.add('alt+F11', 'focus statusbar elementpath', () => Keying.focusIn(comp));\n              editor.on('NodeChange', e => {\n                const newPath = updatePath(e.parents);\n                const newChildren = newPath.length > 0 ? renderPathData(newPath) : [];\n                Replacing.set(comp, newChildren);\n              });\n            })])\n        ]),\n        components: []\n      };\n    };\n\n    var ResizeTypes;\n    (function (ResizeTypes) {\n      ResizeTypes[ResizeTypes['None'] = 0] = 'None';\n      ResizeTypes[ResizeTypes['Both'] = 1] = 'Both';\n      ResizeTypes[ResizeTypes['Vertical'] = 2] = 'Vertical';\n    }(ResizeTypes || (ResizeTypes = {})));\n    const getDimensions = (editor, deltas, resizeType, originalHeight, originalWidth) => {\n      const dimensions = { height: calcCappedSize(originalHeight + deltas.top, getMinHeightOption(editor), getMaxHeightOption(editor)) };\n      if (resizeType === ResizeTypes.Both) {\n        dimensions.width = calcCappedSize(originalWidth + deltas.left, getMinWidthOption(editor), getMaxWidthOption(editor));\n      }\n      return dimensions;\n    };\n    const resize = (editor, deltas, resizeType) => {\n      const container = SugarElement.fromDom(editor.getContainer());\n      const dimensions = getDimensions(editor, deltas, resizeType, get$d(container), get$c(container));\n      each(dimensions, (val, dim) => {\n        if (isNumber(val)) {\n          set$8(container, dim, numToPx(val));\n        }\n      });\n      fireResizeEditor(editor);\n    };\n\n    const getResizeType = editor => {\n      const resize = getResize(editor);\n      if (resize === false) {\n        return ResizeTypes.None;\n      } else if (resize === 'both') {\n        return ResizeTypes.Both;\n      } else {\n        return ResizeTypes.Vertical;\n      }\n    };\n    const keyboardHandler = (editor, resizeType, x, y) => {\n      const scale = 20;\n      const delta = SugarPosition(x * scale, y * scale);\n      resize(editor, delta, resizeType);\n      return Optional.some(true);\n    };\n    const renderResizeHandler = (editor, providersBackstage) => {\n      const resizeType = getResizeType(editor);\n      if (resizeType === ResizeTypes.None) {\n        return Optional.none();\n      }\n      return Optional.some(render$3('resize-handle', {\n        tag: 'div',\n        classes: ['tox-statusbar__resize-handle'],\n        attributes: { title: providersBackstage.translate('Resize') },\n        behaviours: [\n          Dragging.config({\n            mode: 'mouse',\n            repositionTarget: false,\n            onDrag: (_comp, _target, delta) => resize(editor, delta, resizeType),\n            blockerClass: 'tox-blocker'\n          }),\n          Keying.config({\n            mode: 'special',\n            onLeft: () => keyboardHandler(editor, resizeType, -1, 0),\n            onRight: () => keyboardHandler(editor, resizeType, 1, 0),\n            onUp: () => keyboardHandler(editor, resizeType, 0, -1),\n            onDown: () => keyboardHandler(editor, resizeType, 0, 1)\n          }),\n          Tabstopping.config({}),\n          Focusing.config({})\n        ]\n      }, providersBackstage.icons));\n    };\n\n    const renderWordCount = (editor, providersBackstage) => {\n      const replaceCountText = (comp, count, mode) => Replacing.set(comp, [text$2(providersBackstage.translate([\n          '{0} ' + mode,\n          count[mode]\n        ]))]);\n      return Button.sketch({\n        dom: {\n          tag: 'button',\n          classes: ['tox-statusbar__wordcount']\n        },\n        components: [],\n        buttonBehaviours: derive$1([\n          DisablingConfigs.button(providersBackstage.isDisabled),\n          receivingConfig(),\n          Tabstopping.config({}),\n          Replacing.config({}),\n          Representing.config({\n            store: {\n              mode: 'memory',\n              initialValue: {\n                mode: 'words',\n                count: {\n                  words: 0,\n                  characters: 0\n                }\n              }\n            }\n          }),\n          config('wordcount-events', [\n            runOnExecute$1(comp => {\n              const currentVal = Representing.getValue(comp);\n              const newMode = currentVal.mode === 'words' ? 'characters' : 'words';\n              Representing.setValue(comp, {\n                mode: newMode,\n                count: currentVal.count\n              });\n              replaceCountText(comp, currentVal.count, newMode);\n            }),\n            runOnAttached(comp => {\n              editor.on('wordCountUpdate', e => {\n                const {mode} = Representing.getValue(comp);\n                Representing.setValue(comp, {\n                  mode,\n                  count: e.wordCount\n                });\n                replaceCountText(comp, e.wordCount, mode);\n              });\n            })\n          ])\n        ]),\n        eventOrder: {\n          [execute$5()]: [\n            'disabling',\n            'alloy.base.behaviour',\n            'wordcount-events'\n          ]\n        }\n      });\n    };\n\n    const renderStatusbar = (editor, providersBackstage) => {\n      const renderBranding = () => {\n        return {\n          dom: {\n            tag: 'span',\n            classes: ['tox-statusbar__branding']\n          },\n          components: [{\n              dom: {\n                tag: 'a',\n                attributes: {\n                  'href': 'https://www.tiny.cloud/powered-by-tiny?utm_campaign=editor_referral&utm_medium=poweredby&utm_source=tinymce&utm_content=v6',\n                  'rel': 'noopener',\n                  'target': '_blank',\n                  'aria-label': global$8.translate([\n                    'Powered by {0}',\n                    'Tiny'\n                  ])\n                },\n                innerHtml: Logo.trim()\n              },\n              behaviours: derive$1([Focusing.config({})])\n            }]\n        };\n      };\n      const getTextComponents = () => {\n        const components = [];\n        if (useElementPath(editor)) {\n          components.push(renderElementPath(editor, {}, providersBackstage));\n        }\n        if (editor.hasPlugin('wordcount')) {\n          components.push(renderWordCount(editor, providersBackstage));\n        }\n        if (useBranding(editor)) {\n          components.push(renderBranding());\n        }\n        if (components.length > 0) {\n          return [{\n              dom: {\n                tag: 'div',\n                classes: ['tox-statusbar__text-container']\n              },\n              components\n            }];\n        }\n        return [];\n      };\n      const getComponents = () => {\n        const components = getTextComponents();\n        const resizeHandler = renderResizeHandler(editor, providersBackstage);\n        return components.concat(resizeHandler.toArray());\n      };\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-statusbar']\n        },\n        components: getComponents()\n      };\n    };\n\n    const getLazyMothership = (label, singleton) => singleton.get().getOrDie(`UI for ${ label } has not been rendered`);\n    const setup$3 = editor => {\n      const isInline = editor.inline;\n      const mode = isInline ? Inline : Iframe;\n      const header = isStickyToolbar(editor) ? StickyHeader : StaticHeader;\n      const lazyUiRefs = LazyUiReferences();\n      const lazyMothership = value$2();\n      const lazyDialogMothership = value$2();\n      const platform = detect$1();\n      const isTouch = platform.deviceType.isTouch();\n      const touchPlatformClass = 'tox-platform-touch';\n      const deviceClasses = isTouch ? [touchPlatformClass] : [];\n      const isToolbarBottom = isToolbarLocationBottom(editor);\n      const toolbarMode = getToolbarMode(editor);\n      const memAnchorBar = record({\n        dom: {\n          tag: 'div',\n          classes: ['tox-anchorbar']\n        }\n      });\n      const lazyHeader = () => lazyUiRefs.mainUi.get().map(ui => ui.outerContainer).bind(OuterContainer.getHeader);\n      const lazyDialogSinkResult = () => Result.fromOption(lazyUiRefs.dialogUi.get().map(ui => ui.sink), 'UI has not been rendered');\n      const lazyPopupSinkResult = () => Result.fromOption(lazyUiRefs.popupUi.get().map(ui => ui.sink), '(popup) UI has not been rendered');\n      const lazyAnchorBar = lazyUiRefs.lazyGetInOuterOrDie('anchor bar', memAnchorBar.getOpt);\n      const lazyToolbar = lazyUiRefs.lazyGetInOuterOrDie('toolbar', OuterContainer.getToolbar);\n      const lazyThrobber = lazyUiRefs.lazyGetInOuterOrDie('throbber', OuterContainer.getThrobber);\n      const backstages = init$7({\n        popup: lazyPopupSinkResult,\n        dialog: lazyDialogSinkResult\n      }, editor, lazyAnchorBar);\n      const makeHeaderPart = () => {\n        const verticalDirAttributes = { attributes: { [Attribute]: isToolbarBottom ? AttributeValue.BottomToTop : AttributeValue.TopToBottom } };\n        const partMenubar = OuterContainer.parts.menubar({\n          dom: {\n            tag: 'div',\n            classes: ['tox-menubar']\n          },\n          backstage: backstages.popup,\n          onEscape: () => {\n            editor.focus();\n          }\n        });\n        const partToolbar = OuterContainer.parts.toolbar({\n          dom: {\n            tag: 'div',\n            classes: ['tox-toolbar']\n          },\n          getSink: backstages.popup.shared.getSink,\n          providers: backstages.popup.shared.providers,\n          onEscape: () => {\n            editor.focus();\n          },\n          onToolbarToggled: state => {\n            fireToggleToolbarDrawer(editor, state);\n          },\n          type: toolbarMode,\n          lazyToolbar,\n          lazyHeader: () => lazyHeader().getOrDie('Could not find header element'),\n          ...verticalDirAttributes\n        });\n        const partMultipleToolbar = OuterContainer.parts['multiple-toolbar']({\n          dom: {\n            tag: 'div',\n            classes: ['tox-toolbar-overlord']\n          },\n          providers: backstages.popup.shared.providers,\n          onEscape: () => {\n            editor.focus();\n          },\n          type: toolbarMode\n        });\n        const hasMultipleToolbar = isMultipleToolbars(editor);\n        const hasToolbar = isToolbarEnabled(editor);\n        const hasMenubar = isMenubarEnabled(editor);\n        const shouldHavePromotion = promotionEnabled(editor);\n        const partPromotion = makePromotion();\n        const hasAnyContents = hasMultipleToolbar || hasToolbar || hasMenubar;\n        const getPartToolbar = () => {\n          if (hasMultipleToolbar) {\n            return [partMultipleToolbar];\n          } else if (hasToolbar) {\n            return [partToolbar];\n          } else {\n            return [];\n          }\n        };\n        const menubarCollection = shouldHavePromotion ? [\n          partPromotion,\n          partMenubar\n        ] : [partMenubar];\n        return OuterContainer.parts.header({\n          dom: {\n            tag: 'div',\n            classes: ['tox-editor-header'].concat(hasAnyContents ? [] : ['tox-editor-header--empty']),\n            ...verticalDirAttributes\n          },\n          components: flatten([\n            hasMenubar ? menubarCollection : [],\n            getPartToolbar(),\n            useFixedContainer(editor) ? [] : [memAnchorBar.asSpec()]\n          ]),\n          sticky: isStickyToolbar(editor),\n          editor,\n          sharedBackstage: backstages.popup.shared\n        });\n      };\n      const makePromotion = () => {\n        return OuterContainer.parts.promotion({\n          dom: {\n            tag: 'div',\n            classes: ['tox-promotion']\n          }\n        });\n      };\n      const makeSidebarDefinition = () => {\n        const partSocket = OuterContainer.parts.socket({\n          dom: {\n            tag: 'div',\n            classes: ['tox-edit-area']\n          }\n        });\n        const partSidebar = OuterContainer.parts.sidebar({\n          dom: {\n            tag: 'div',\n            classes: ['tox-sidebar']\n          }\n        });\n        return {\n          dom: {\n            tag: 'div',\n            classes: ['tox-sidebar-wrap']\n          },\n          components: [\n            partSocket,\n            partSidebar\n          ]\n        };\n      };\n      const renderDialogUi = () => {\n        const uiContainer = getUiContainer(editor);\n        const isGridUiContainer = eq(body(), uiContainer) && get$e(uiContainer, 'display') === 'grid';\n        const sinkSpec = {\n          dom: {\n            tag: 'div',\n            classes: [\n              'tox',\n              'tox-silver-sink',\n              'tox-tinymce-aux'\n            ].concat(deviceClasses),\n            attributes: { ...global$8.isRtl() ? { dir: 'rtl' } : {} }\n          },\n          behaviours: derive$1([Positioning.config({ useFixed: () => header.isDocked(lazyHeader) })])\n        };\n        const reactiveWidthSpec = {\n          dom: { styles: { width: document.body.clientWidth + 'px' } },\n          events: derive$2([run$1(windowResize(), comp => {\n              set$8(comp.element, 'width', document.body.clientWidth + 'px');\n            })])\n        };\n        const sink = build$1(deepMerge(sinkSpec, isGridUiContainer ? reactiveWidthSpec : {}));\n        const uiMothership = takeover(sink);\n        lazyDialogMothership.set(uiMothership);\n        return {\n          sink,\n          mothership: uiMothership\n        };\n      };\n      const renderPopupUi = identity;\n      const renderMainUi = () => {\n        const partHeader = makeHeaderPart();\n        const sidebarContainer = makeSidebarDefinition();\n        const partThrobber = OuterContainer.parts.throbber({\n          dom: {\n            tag: 'div',\n            classes: ['tox-throbber']\n          },\n          backstage: backstages.popup\n        });\n        const partViewWrapper = OuterContainer.parts.viewWrapper({ backstage: backstages.popup });\n        const statusbar = useStatusBar(editor) && !isInline ? Optional.some(renderStatusbar(editor, backstages.popup.shared.providers)) : Optional.none();\n        const editorComponents = flatten([\n          isToolbarBottom ? [] : [partHeader],\n          isInline ? [] : [sidebarContainer],\n          isToolbarBottom ? [partHeader] : []\n        ]);\n        const editorContainer = OuterContainer.parts.editorContainer({\n          components: flatten([\n            editorComponents,\n            isInline ? [] : statusbar.toArray()\n          ])\n        });\n        const isHidden = isDistractionFree(editor);\n        const attributes = {\n          role: 'application',\n          ...global$8.isRtl() ? { dir: 'rtl' } : {},\n          ...isHidden ? { 'aria-hidden': 'true' } : {}\n        };\n        const outerContainer = build$1(OuterContainer.sketch({\n          dom: {\n            tag: 'div',\n            classes: [\n              'tox',\n              'tox-tinymce'\n            ].concat(isInline ? ['tox-tinymce-inline'] : []).concat(isToolbarBottom ? ['tox-tinymce--toolbar-bottom'] : []).concat(deviceClasses),\n            styles: {\n              visibility: 'hidden',\n              ...isHidden ? {\n                opacity: '0',\n                border: '0'\n              } : {}\n            },\n            attributes\n          },\n          components: [\n            editorContainer,\n            ...isInline ? [] : [partViewWrapper],\n            partThrobber\n          ],\n          behaviours: derive$1([\n            receivingConfig(),\n            Disabling.config({ disableClass: 'tox-tinymce--disabled' }),\n            Keying.config({\n              mode: 'cyclic',\n              selector: '.tox-menubar, .tox-toolbar, .tox-toolbar__primary, .tox-toolbar__overflow--open, .tox-sidebar__overflow--open, .tox-statusbar__path, .tox-statusbar__wordcount, .tox-statusbar__branding a, .tox-statusbar__resize-handle'\n            })\n          ])\n        }));\n        const mothership = takeover(outerContainer);\n        lazyMothership.set(mothership);\n        return {\n          mothership,\n          outerContainer\n        };\n      };\n      const setEditorSize = outerContainer => {\n        const parsedHeight = numToPx(getHeightWithFallback(editor));\n        const parsedWidth = numToPx(getWidthWithFallback(editor));\n        if (!editor.inline) {\n          if (isValidValue('div', 'width', parsedWidth)) {\n            set$8(outerContainer.element, 'width', parsedWidth);\n          }\n          if (isValidValue('div', 'height', parsedHeight)) {\n            set$8(outerContainer.element, 'height', parsedHeight);\n          } else {\n            set$8(outerContainer.element, 'height', '400px');\n          }\n        }\n        return parsedHeight;\n      };\n      const setupShortcutsAndCommands = outerContainer => {\n        editor.addShortcut('alt+F9', 'focus menubar', () => {\n          OuterContainer.focusMenubar(outerContainer);\n        });\n        editor.addShortcut('alt+F10', 'focus toolbar', () => {\n          OuterContainer.focusToolbar(outerContainer);\n        });\n        editor.addCommand('ToggleToolbarDrawer', () => {\n          OuterContainer.toggleToolbarDrawer(outerContainer);\n        });\n        editor.addQueryStateHandler('ToggleToolbarDrawer', () => OuterContainer.isToolbarDrawerToggled(outerContainer));\n      };\n      const renderUIWithRefs = uiRefs => {\n        const {mainUi, popupUi, uiMotherships} = uiRefs;\n        map$1(getToolbarGroups(editor), (toolbarGroupButtonConfig, name) => {\n          editor.ui.registry.addGroupToolbarButton(name, toolbarGroupButtonConfig);\n        });\n        const {buttons, menuItems, contextToolbars, sidebars, views} = editor.ui.registry.getAll();\n        const toolbarOpt = getMultipleToolbarsOption(editor);\n        const rawUiConfig = {\n          menuItems,\n          menus: getMenus(editor),\n          menubar: getMenubar(editor),\n          toolbar: toolbarOpt.getOrThunk(() => getToolbar(editor)),\n          allowToolbarGroups: toolbarMode === ToolbarMode$1.floating,\n          buttons,\n          sidebar: sidebars,\n          views\n        };\n        setupShortcutsAndCommands(mainUi.outerContainer);\n        setup$b(editor, mainUi.mothership, uiMotherships);\n        header.setup(editor, backstages.popup.shared, lazyHeader);\n        setup$6(editor, backstages.popup);\n        setup$5(editor, backstages.popup.shared.getSink, backstages.popup);\n        setup$8(editor);\n        setup$7(editor, lazyThrobber, backstages.popup.shared);\n        register$9(editor, contextToolbars, popupUi.sink, { backstage: backstages.popup });\n        setup$4(editor, popupUi.sink);\n        const elm = editor.getElement();\n        const height = setEditorSize(mainUi.outerContainer);\n        const args = {\n          targetNode: elm,\n          height\n        };\n        return mode.render(editor, uiRefs, rawUiConfig, backstages.popup, args);\n      };\n      const renderUI = () => {\n        const mainUi = renderMainUi();\n        const dialogUi = renderDialogUi();\n        const popupUi = renderPopupUi(dialogUi);\n        lazyUiRefs.dialogUi.set(dialogUi);\n        lazyUiRefs.popupUi.set(popupUi);\n        lazyUiRefs.mainUi.set(mainUi);\n        const uiRefs = {\n          popupUi,\n          dialogUi,\n          mainUi,\n          uiMotherships: lazyUiRefs.getUiMotherships()\n        };\n        return renderUIWithRefs(uiRefs);\n      };\n      return {\n        popups: {\n          backstage: backstages.popup,\n          getMothership: () => getLazyMothership('popups', lazyDialogMothership)\n        },\n        dialogs: {\n          backstage: backstages.dialog,\n          getMothership: () => getLazyMothership('dialogs', lazyDialogMothership)\n        },\n        renderUI\n      };\n    };\n\n    const describedBy = (describedElement, describeElement) => {\n      const describeId = Optional.from(get$f(describedElement, 'id')).fold(() => {\n        const id = generate$6('dialog-describe');\n        set$9(describeElement, 'id', id);\n        return id;\n      }, identity);\n      set$9(describedElement, 'aria-describedby', describeId);\n    };\n\n    const labelledBy = (labelledElement, labelElement) => {\n      const labelId = getOpt(labelledElement, 'id').fold(() => {\n        const id = generate$6('dialog-label');\n        set$9(labelElement, 'id', id);\n        return id;\n      }, identity);\n      set$9(labelledElement, 'aria-labelledby', labelId);\n    };\n\n    const schema$2 = constant$1([\n      required$1('lazySink'),\n      option$3('dragBlockClass'),\n      defaultedFunction('getBounds', win),\n      defaulted('useTabstopAt', always),\n      defaulted('eventOrder', {}),\n      field('modalBehaviours', [Keying]),\n      onKeyboardHandler('onExecute'),\n      onStrictKeyboardHandler('onEscape')\n    ]);\n    const basic = { sketch: identity };\n    const parts$2 = constant$1([\n      optional({\n        name: 'draghandle',\n        overrides: (detail, spec) => {\n          return {\n            behaviours: derive$1([Dragging.config({\n                mode: 'mouse',\n                getTarget: handle => {\n                  return ancestor(handle, '[role=\"dialog\"]').getOr(handle);\n                },\n                blockerClass: detail.dragBlockClass.getOrDie(new Error('The drag blocker class was not specified for a dialog with a drag handle: \\n' + JSON.stringify(spec, null, 2)).message),\n                getBounds: detail.getDragBounds\n              })])\n          };\n        }\n      }),\n      required({\n        schema: [required$1('dom')],\n        name: 'title'\n      }),\n      required({\n        factory: basic,\n        schema: [required$1('dom')],\n        name: 'close'\n      }),\n      required({\n        factory: basic,\n        schema: [required$1('dom')],\n        name: 'body'\n      }),\n      optional({\n        factory: basic,\n        schema: [required$1('dom')],\n        name: 'footer'\n      }),\n      external({\n        factory: {\n          sketch: (spec, detail) => ({\n            ...spec,\n            dom: detail.dom,\n            components: detail.components\n          })\n        },\n        schema: [\n          defaulted('dom', {\n            tag: 'div',\n            styles: {\n              position: 'fixed',\n              left: '0px',\n              top: '0px',\n              right: '0px',\n              bottom: '0px'\n            }\n          }),\n          defaulted('components', [])\n        ],\n        name: 'blocker'\n      })\n    ]);\n\n    const factory$4 = (detail, components, spec, externals) => {\n      const dialogComp = value$2();\n      const showDialog = dialog => {\n        dialogComp.set(dialog);\n        const sink = detail.lazySink(dialog).getOrDie();\n        const externalBlocker = externals.blocker();\n        const blocker = sink.getSystem().build({\n          ...externalBlocker,\n          components: externalBlocker.components.concat([premade(dialog)]),\n          behaviours: derive$1([\n            Focusing.config({}),\n            config('dialog-blocker-events', [runOnSource(focusin(), () => {\n                Keying.focusIn(dialog);\n              })])\n          ])\n        });\n        attach(sink, blocker);\n        Keying.focusIn(dialog);\n      };\n      const hideDialog = dialog => {\n        dialogComp.clear();\n        parent(dialog.element).each(blockerDom => {\n          dialog.getSystem().getByDom(blockerDom).each(blocker => {\n            detach(blocker);\n          });\n        });\n      };\n      const getDialogBody = dialog => getPartOrDie(dialog, detail, 'body');\n      const getDialogFooter = dialog => getPartOrDie(dialog, detail, 'footer');\n      const setBusy = (dialog, getBusySpec) => {\n        Blocking.block(dialog, getBusySpec);\n      };\n      const setIdle = dialog => {\n        Blocking.unblock(dialog);\n      };\n      const modalEventsId = generate$6('modal-events');\n      const eventOrder = {\n        ...detail.eventOrder,\n        [attachedToDom()]: [modalEventsId].concat(detail.eventOrder['alloy.system.attached'] || [])\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        apis: {\n          show: showDialog,\n          hide: hideDialog,\n          getBody: getDialogBody,\n          getFooter: getDialogFooter,\n          setIdle,\n          setBusy\n        },\n        eventOrder,\n        domModification: {\n          attributes: {\n            'role': 'dialog',\n            'aria-modal': 'true'\n          }\n        },\n        behaviours: augment(detail.modalBehaviours, [\n          Replacing.config({}),\n          Keying.config({\n            mode: 'cyclic',\n            onEnter: detail.onExecute,\n            onEscape: detail.onEscape,\n            useTabstopAt: detail.useTabstopAt\n          }),\n          Blocking.config({ getRoot: dialogComp.get }),\n          config(modalEventsId, [runOnAttached(c => {\n              labelledBy(c.element, getPartOrDie(c, detail, 'title').element);\n              describedBy(c.element, getPartOrDie(c, detail, 'body').element);\n            })])\n        ])\n      };\n    };\n    const ModalDialog = composite({\n      name: 'ModalDialog',\n      configFields: schema$2(),\n      partFields: parts$2(),\n      factory: factory$4,\n      apis: {\n        show: (apis, dialog) => {\n          apis.show(dialog);\n        },\n        hide: (apis, dialog) => {\n          apis.hide(dialog);\n        },\n        getBody: (apis, dialog) => apis.getBody(dialog),\n        getFooter: (apis, dialog) => apis.getFooter(dialog),\n        setBusy: (apis, dialog, getBusySpec) => {\n          apis.setBusy(dialog, getBusySpec);\n        },\n        setIdle: (apis, dialog) => {\n          apis.setIdle(dialog);\n        }\n      }\n    });\n\n    const dialogToggleMenuItemSchema = objOf([\n      type,\n      name$1\n    ].concat(commonMenuItemFields));\n    const dialogToggleMenuItemDataProcessor = boolean;\n\n    const baseFooterButtonFields = [\n      generatedName('button'),\n      optionalIcon,\n      defaultedStringEnum('align', 'end', [\n        'start',\n        'end'\n      ]),\n      primary,\n      enabled,\n      optionStringEnum('buttonType', [\n        'primary',\n        'secondary'\n      ])\n    ];\n    const dialogFooterButtonFields = [\n      ...baseFooterButtonFields,\n      text$1\n    ];\n    const normalFooterButtonFields = [\n      requiredStringEnum('type', [\n        'submit',\n        'cancel',\n        'custom'\n      ]),\n      ...dialogFooterButtonFields\n    ];\n    const menuFooterButtonFields = [\n      requiredStringEnum('type', ['menu']),\n      optionalText,\n      optionalTooltip,\n      optionalIcon,\n      requiredArrayOf('items', dialogToggleMenuItemSchema),\n      ...baseFooterButtonFields\n    ];\n    const dialogFooterButtonSchema = choose$1('type', {\n      submit: normalFooterButtonFields,\n      cancel: normalFooterButtonFields,\n      custom: normalFooterButtonFields,\n      menu: menuFooterButtonFields\n    });\n\n    const alertBannerFields = [\n      type,\n      text$1,\n      requiredStringEnum('level', [\n        'info',\n        'warn',\n        'error',\n        'success'\n      ]),\n      icon,\n      defaulted('url', '')\n    ];\n    const alertBannerSchema = objOf(alertBannerFields);\n\n    const createBarFields = itemsField => [\n      type,\n      itemsField\n    ];\n\n    const buttonFields = [\n      type,\n      text$1,\n      enabled,\n      generatedName('button'),\n      optionalIcon,\n      borderless,\n      optionStringEnum('buttonType', [\n        'primary',\n        'secondary',\n        'toolbar'\n      ]),\n      primary\n    ];\n    const buttonSchema = objOf(buttonFields);\n\n    const formComponentFields = [\n      type,\n      name$1\n    ];\n    const formComponentWithLabelFields = formComponentFields.concat([optionalLabel]);\n\n    const checkboxFields = formComponentFields.concat([\n      label,\n      enabled\n    ]);\n    const checkboxSchema = objOf(checkboxFields);\n    const checkboxDataProcessor = boolean;\n\n    const collectionFields = formComponentWithLabelFields.concat([defaultedColumns('auto')]);\n    const collectionSchema = objOf(collectionFields);\n    const collectionDataProcessor = arrOfObj([\n      value$1,\n      text$1,\n      icon\n    ]);\n\n    const colorInputFields = formComponentWithLabelFields.concat([defaultedString('storageKey', 'default')]);\n    const colorInputSchema = objOf(colorInputFields);\n    const colorInputDataProcessor = string;\n\n    const colorPickerFields = formComponentWithLabelFields;\n    const colorPickerSchema = objOf(colorPickerFields);\n    const colorPickerDataProcessor = string;\n\n    const customEditorFields = formComponentFields.concat([\n      defaultedString('tag', 'textarea'),\n      requiredString('scriptId'),\n      requiredString('scriptUrl'),\n      defaultedPostMsg('settings', undefined)\n    ]);\n    const customEditorFieldsOld = formComponentFields.concat([\n      defaultedString('tag', 'textarea'),\n      requiredFunction('init')\n    ]);\n    const customEditorSchema = valueOf(v => asRaw('customeditor.old', objOfOnly(customEditorFieldsOld), v).orThunk(() => asRaw('customeditor.new', objOfOnly(customEditorFields), v)));\n    const customEditorDataProcessor = string;\n\n    const dropZoneFields = formComponentWithLabelFields;\n    const dropZoneSchema = objOf(dropZoneFields);\n    const dropZoneDataProcessor = arrOfVal();\n\n    const createGridFields = itemsField => [\n      type,\n      requiredNumber('columns'),\n      itemsField\n    ];\n\n    const htmlPanelFields = [\n      type,\n      requiredString('html'),\n      defaultedStringEnum('presets', 'presentation', [\n        'presentation',\n        'document'\n      ])\n    ];\n    const htmlPanelSchema = objOf(htmlPanelFields);\n\n    const iframeFields = formComponentWithLabelFields.concat([\n      defaultedBoolean('sandboxed', true),\n      defaultedBoolean('transparent', true)\n    ]);\n    const iframeSchema = objOf(iframeFields);\n    const iframeDataProcessor = string;\n\n    const imagePreviewSchema = objOf(formComponentFields.concat([optionString('height')]));\n    const imagePreviewDataProcessor = objOf([\n      requiredString('url'),\n      optionNumber('zoom'),\n      optionNumber('cachedWidth'),\n      optionNumber('cachedHeight')\n    ]);\n\n    const inputFields = formComponentWithLabelFields.concat([\n      optionString('inputMode'),\n      optionString('placeholder'),\n      defaultedBoolean('maximized', false),\n      enabled\n    ]);\n    const inputSchema = objOf(inputFields);\n    const inputDataProcessor = string;\n\n    const createLabelFields = itemsField => [\n      type,\n      label,\n      itemsField\n    ];\n\n    const listBoxSingleItemFields = [\n      text$1,\n      value$1\n    ];\n    const listBoxNestedItemFields = [\n      text$1,\n      requiredArrayOf('items', thunkOf('items', () => listBoxItemSchema))\n    ];\n    const listBoxItemSchema = oneOf([\n      objOf(listBoxSingleItemFields),\n      objOf(listBoxNestedItemFields)\n    ]);\n    const listBoxFields = formComponentWithLabelFields.concat([\n      requiredArrayOf('items', listBoxItemSchema),\n      enabled\n    ]);\n    const listBoxSchema = objOf(listBoxFields);\n    const listBoxDataProcessor = string;\n\n    const selectBoxFields = formComponentWithLabelFields.concat([\n      requiredArrayOfObj('items', [\n        text$1,\n        value$1\n      ]),\n      defaultedNumber('size', 1),\n      enabled\n    ]);\n    const selectBoxSchema = objOf(selectBoxFields);\n    const selectBoxDataProcessor = string;\n\n    const sizeInputFields = formComponentWithLabelFields.concat([\n      defaultedBoolean('constrain', true),\n      enabled\n    ]);\n    const sizeInputSchema = objOf(sizeInputFields);\n    const sizeInputDataProcessor = objOf([\n      requiredString('width'),\n      requiredString('height')\n    ]);\n\n    const sliderFields = formComponentFields.concat([\n      label,\n      defaultedNumber('min', 0),\n      defaultedNumber('max', 0)\n    ]);\n    const sliderSchema = objOf(sliderFields);\n    const sliderInputDataProcessor = number;\n\n    const tableFields = [\n      type,\n      requiredArrayOf('header', string),\n      requiredArrayOf('cells', arrOf(string))\n    ];\n    const tableSchema = objOf(tableFields);\n\n    const textAreaFields = formComponentWithLabelFields.concat([\n      optionString('placeholder'),\n      defaultedBoolean('maximized', false),\n      enabled\n    ]);\n    const textAreaSchema = objOf(textAreaFields);\n    const textAreaDataProcessor = string;\n\n    const urlInputFields = formComponentWithLabelFields.concat([\n      defaultedStringEnum('filetype', 'file', [\n        'image',\n        'media',\n        'file'\n      ]),\n      enabled\n    ]);\n    const urlInputSchema = objOf(urlInputFields);\n    const urlInputDataProcessor = objOf([\n      value$1,\n      defaultedMeta\n    ]);\n\n    const createItemsField = name => field$1('items', 'items', required$2(), arrOf(valueOf(v => asRaw(`Checking item of ${ name }`, itemSchema, v).fold(sErr => Result.error(formatError(sErr)), passValue => Result.value(passValue)))));\n    const itemSchema = valueThunk(() => choose$2('type', {\n      alertbanner: alertBannerSchema,\n      bar: objOf(createBarFields(createItemsField('bar'))),\n      button: buttonSchema,\n      checkbox: checkboxSchema,\n      colorinput: colorInputSchema,\n      colorpicker: colorPickerSchema,\n      dropzone: dropZoneSchema,\n      grid: objOf(createGridFields(createItemsField('grid'))),\n      iframe: iframeSchema,\n      input: inputSchema,\n      listbox: listBoxSchema,\n      selectbox: selectBoxSchema,\n      sizeinput: sizeInputSchema,\n      slider: sliderSchema,\n      textarea: textAreaSchema,\n      urlinput: urlInputSchema,\n      customeditor: customEditorSchema,\n      htmlpanel: htmlPanelSchema,\n      imagepreview: imagePreviewSchema,\n      collection: collectionSchema,\n      label: objOf(createLabelFields(createItemsField('label'))),\n      table: tableSchema,\n      panel: panelSchema\n    }));\n    const panelFields = [\n      type,\n      defaulted('classes', []),\n      requiredArrayOf('items', itemSchema)\n    ];\n    const panelSchema = objOf(panelFields);\n\n    const tabFields = [\n      generatedName('tab'),\n      title,\n      requiredArrayOf('items', itemSchema)\n    ];\n    const tabPanelFields = [\n      type,\n      requiredArrayOfObj('tabs', tabFields)\n    ];\n    const tabPanelSchema = objOf(tabPanelFields);\n\n    const dialogButtonFields = dialogFooterButtonFields;\n    const dialogButtonSchema = dialogFooterButtonSchema;\n    const dialogSchema = objOf([\n      requiredString('title'),\n      requiredOf('body', choose$2('type', {\n        panel: panelSchema,\n        tabpanel: tabPanelSchema\n      })),\n      defaultedString('size', 'normal'),\n      requiredArrayOf('buttons', dialogButtonSchema),\n      defaulted('initialData', {}),\n      defaultedFunction('onAction', noop),\n      defaultedFunction('onChange', noop),\n      defaultedFunction('onSubmit', noop),\n      defaultedFunction('onClose', noop),\n      defaultedFunction('onCancel', noop),\n      defaultedFunction('onTabChange', noop)\n    ]);\n    const createDialog = spec => asRaw('dialog', dialogSchema, spec);\n\n    const urlDialogButtonSchema = objOf([\n      requiredStringEnum('type', [\n        'cancel',\n        'custom'\n      ]),\n      ...dialogButtonFields\n    ]);\n    const urlDialogSchema = objOf([\n      requiredString('title'),\n      requiredString('url'),\n      optionNumber('height'),\n      optionNumber('width'),\n      optionArrayOf('buttons', urlDialogButtonSchema),\n      defaultedFunction('onAction', noop),\n      defaultedFunction('onCancel', noop),\n      defaultedFunction('onClose', noop),\n      defaultedFunction('onMessage', noop)\n    ]);\n    const createUrlDialog = spec => asRaw('dialog', urlDialogSchema, spec);\n\n    const getAllObjects = obj => {\n      if (isObject(obj)) {\n        return [obj].concat(bind$3(values(obj), getAllObjects));\n      } else if (isArray(obj)) {\n        return bind$3(obj, getAllObjects);\n      } else {\n        return [];\n      }\n    };\n\n    const isNamedItem = obj => isString(obj.type) && isString(obj.name);\n    const dataProcessors = {\n      checkbox: checkboxDataProcessor,\n      colorinput: colorInputDataProcessor,\n      colorpicker: colorPickerDataProcessor,\n      dropzone: dropZoneDataProcessor,\n      input: inputDataProcessor,\n      iframe: iframeDataProcessor,\n      imagepreview: imagePreviewDataProcessor,\n      selectbox: selectBoxDataProcessor,\n      sizeinput: sizeInputDataProcessor,\n      slider: sliderInputDataProcessor,\n      listbox: listBoxDataProcessor,\n      size: sizeInputDataProcessor,\n      textarea: textAreaDataProcessor,\n      urlinput: urlInputDataProcessor,\n      customeditor: customEditorDataProcessor,\n      collection: collectionDataProcessor,\n      togglemenuitem: dialogToggleMenuItemDataProcessor\n    };\n    const getDataProcessor = item => Optional.from(dataProcessors[item.type]);\n    const getNamedItems = structure => filter$2(getAllObjects(structure), isNamedItem);\n\n    const createDataValidator = structure => {\n      const namedItems = getNamedItems(structure);\n      const fields = bind$3(namedItems, item => getDataProcessor(item).fold(() => [], schema => [requiredOf(item.name, schema)]));\n      return objOf(fields);\n    };\n\n    const extract = structure => {\n      var _a;\n      const internalDialog = getOrDie(createDialog(structure));\n      const dataValidator = createDataValidator(structure);\n      const initialData = (_a = structure.initialData) !== null && _a !== void 0 ? _a : {};\n      return {\n        internalDialog,\n        dataValidator,\n        initialData\n      };\n    };\n    const DialogManager = {\n      open: (factory, structure) => {\n        const extraction = extract(structure);\n        return factory(extraction.internalDialog, extraction.initialData, extraction.dataValidator);\n      },\n      openUrl: (factory, structure) => {\n        const internalDialog = getOrDie(createUrlDialog(structure));\n        return factory(internalDialog);\n      },\n      redial: structure => extract(structure)\n    };\n\n    const toValidValues = values => {\n      const errors = [];\n      const result = {};\n      each(values, (value, name) => {\n        value.fold(() => {\n          errors.push(name);\n        }, v => {\n          result[name] = v;\n        });\n      });\n      return errors.length > 0 ? Result.error(errors) : Result.value(result);\n    };\n\n    const renderBodyPanel = (spec, dialogData, backstage) => {\n      const memForm = record(Form.sketch(parts => ({\n        dom: {\n          tag: 'div',\n          classes: ['tox-form'].concat(spec.classes)\n        },\n        components: map$2(spec.items, item => interpretInForm(parts, item, dialogData, backstage))\n      })));\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-dialog__body']\n        },\n        components: [{\n            dom: {\n              tag: 'div',\n              classes: ['tox-dialog__body-content']\n            },\n            components: [memForm.asSpec()]\n          }],\n        behaviours: derive$1([\n          Keying.config({\n            mode: 'acyclic',\n            useTabstopAt: not(isPseudoStop)\n          }),\n          ComposingConfigs.memento(memForm),\n          RepresentingConfigs.memento(memForm, {\n            postprocess: formValue => toValidValues(formValue).fold(err => {\n              console.error(err);\n              return {};\n            }, identity)\n          })\n        ])\n      };\n    };\n\n    const factory$3 = (detail, _spec) => ({\n      uid: detail.uid,\n      dom: detail.dom,\n      components: detail.components,\n      events: events$a(detail.action),\n      behaviours: augment(detail.tabButtonBehaviours, [\n        Focusing.config({}),\n        Keying.config({\n          mode: 'execution',\n          useSpace: true,\n          useEnter: true\n        }),\n        Representing.config({\n          store: {\n            mode: 'memory',\n            initialValue: detail.value\n          }\n        })\n      ]),\n      domModification: detail.domModification\n    });\n    const TabButton = single({\n      name: 'TabButton',\n      configFields: [\n        defaulted('uid', undefined),\n        required$1('value'),\n        field$1('dom', 'dom', mergeWithThunk(() => ({\n          attributes: {\n            'role': 'tab',\n            'id': generate$6('aria'),\n            'aria-selected': 'false'\n          }\n        })), anyValue()),\n        option$3('action'),\n        defaulted('domModification', {}),\n        field('tabButtonBehaviours', [\n          Focusing,\n          Keying,\n          Representing\n        ]),\n        required$1('view')\n      ],\n      factory: factory$3\n    });\n\n    const schema$1 = constant$1([\n      required$1('tabs'),\n      required$1('dom'),\n      defaulted('clickToDismiss', false),\n      field('tabbarBehaviours', [\n        Highlighting,\n        Keying\n      ]),\n      markers$1([\n        'tabClass',\n        'selectedClass'\n      ])\n    ]);\n    const tabsPart = group({\n      factory: TabButton,\n      name: 'tabs',\n      unit: 'tab',\n      overrides: barDetail => {\n        const dismissTab$1 = (tabbar, button) => {\n          Highlighting.dehighlight(tabbar, button);\n          emitWith(tabbar, dismissTab(), {\n            tabbar,\n            button\n          });\n        };\n        const changeTab$1 = (tabbar, button) => {\n          Highlighting.highlight(tabbar, button);\n          emitWith(tabbar, changeTab(), {\n            tabbar,\n            button\n          });\n        };\n        return {\n          action: button => {\n            const tabbar = button.getSystem().getByUid(barDetail.uid).getOrDie();\n            const activeButton = Highlighting.isHighlighted(tabbar, button);\n            const response = (() => {\n              if (activeButton && barDetail.clickToDismiss) {\n                return dismissTab$1;\n              } else if (!activeButton) {\n                return changeTab$1;\n              } else {\n                return noop;\n              }\n            })();\n            response(tabbar, button);\n          },\n          domModification: { classes: [barDetail.markers.tabClass] }\n        };\n      }\n    });\n    const parts$1 = constant$1([tabsPart]);\n\n    const factory$2 = (detail, components, _spec, _externals) => ({\n      'uid': detail.uid,\n      'dom': detail.dom,\n      components,\n      'debug.sketcher': 'Tabbar',\n      'domModification': { attributes: { role: 'tablist' } },\n      'behaviours': augment(detail.tabbarBehaviours, [\n        Highlighting.config({\n          highlightClass: detail.markers.selectedClass,\n          itemClass: detail.markers.tabClass,\n          onHighlight: (tabbar, tab) => {\n            set$9(tab.element, 'aria-selected', 'true');\n          },\n          onDehighlight: (tabbar, tab) => {\n            set$9(tab.element, 'aria-selected', 'false');\n          }\n        }),\n        Keying.config({\n          mode: 'flow',\n          getInitial: tabbar => {\n            return Highlighting.getHighlighted(tabbar).map(tab => tab.element);\n          },\n          selector: '.' + detail.markers.tabClass,\n          executeOnMove: true\n        })\n      ])\n    });\n    const Tabbar = composite({\n      name: 'Tabbar',\n      configFields: schema$1(),\n      partFields: parts$1(),\n      factory: factory$2\n    });\n\n    const factory$1 = (detail, _spec) => ({\n      uid: detail.uid,\n      dom: detail.dom,\n      behaviours: augment(detail.tabviewBehaviours, [Replacing.config({})]),\n      domModification: { attributes: { role: 'tabpanel' } }\n    });\n    const Tabview = single({\n      name: 'Tabview',\n      configFields: [field('tabviewBehaviours', [Replacing])],\n      factory: factory$1\n    });\n\n    const schema = constant$1([\n      defaulted('selectFirst', true),\n      onHandler('onChangeTab'),\n      onHandler('onDismissTab'),\n      defaulted('tabs', []),\n      field('tabSectionBehaviours', [])\n    ]);\n    const barPart = required({\n      factory: Tabbar,\n      schema: [\n        required$1('dom'),\n        requiredObjOf('markers', [\n          required$1('tabClass'),\n          required$1('selectedClass')\n        ])\n      ],\n      name: 'tabbar',\n      defaults: detail => {\n        return { tabs: detail.tabs };\n      }\n    });\n    const viewPart = required({\n      factory: Tabview,\n      name: 'tabview'\n    });\n    const parts = constant$1([\n      barPart,\n      viewPart\n    ]);\n\n    const factory = (detail, components, _spec, _externals) => {\n      const changeTab$1 = button => {\n        const tabValue = Representing.getValue(button);\n        getPart(button, detail, 'tabview').each(tabview => {\n          const tabWithValue = find$5(detail.tabs, t => t.value === tabValue);\n          tabWithValue.each(tabData => {\n            const panel = tabData.view();\n            getOpt(button.element, 'id').each(id => {\n              set$9(tabview.element, 'aria-labelledby', id);\n            });\n            Replacing.set(tabview, panel);\n            detail.onChangeTab(tabview, button, panel);\n          });\n        });\n      };\n      const changeTabBy = (section, byPred) => {\n        getPart(section, detail, 'tabbar').each(tabbar => {\n          byPred(tabbar).each(emitExecute);\n        });\n      };\n      return {\n        uid: detail.uid,\n        dom: detail.dom,\n        components,\n        behaviours: get$3(detail.tabSectionBehaviours),\n        events: derive$2(flatten([\n          detail.selectFirst ? [runOnAttached((section, _simulatedEvent) => {\n              changeTabBy(section, Highlighting.getFirst);\n            })] : [],\n          [\n            run$1(changeTab(), (section, simulatedEvent) => {\n              const button = simulatedEvent.event.button;\n              changeTab$1(button);\n            }),\n            run$1(dismissTab(), (section, simulatedEvent) => {\n              const button = simulatedEvent.event.button;\n              detail.onDismissTab(section, button);\n            })\n          ]\n        ])),\n        apis: {\n          getViewItems: section => {\n            return getPart(section, detail, 'tabview').map(tabview => Replacing.contents(tabview)).getOr([]);\n          },\n          showTab: (section, tabKey) => {\n            const getTabIfNotActive = tabbar => {\n              const candidates = Highlighting.getCandidates(tabbar);\n              const optTab = find$5(candidates, c => Representing.getValue(c) === tabKey);\n              return optTab.filter(tab => !Highlighting.isHighlighted(tabbar, tab));\n            };\n            changeTabBy(section, getTabIfNotActive);\n          }\n        }\n      };\n    };\n    const TabSection = composite({\n      name: 'TabSection',\n      configFields: schema(),\n      partFields: parts(),\n      factory,\n      apis: {\n        getViewItems: (apis, component) => apis.getViewItems(component),\n        showTab: (apis, component, tabKey) => {\n          apis.showTab(component, tabKey);\n        }\n      }\n    });\n\n    const measureHeights = (allTabs, tabview, tabviewComp) => map$2(allTabs, (_tab, i) => {\n      Replacing.set(tabviewComp, allTabs[i].view());\n      const rect = tabview.dom.getBoundingClientRect();\n      Replacing.set(tabviewComp, []);\n      return rect.height;\n    });\n    const getMaxHeight = heights => head(sort(heights, (a, b) => {\n      if (a > b) {\n        return -1;\n      } else if (a < b) {\n        return +1;\n      } else {\n        return 0;\n      }\n    }));\n    const getMaxTabviewHeight = (dialog, tabview, tablist) => {\n      const documentElement$1 = documentElement(dialog).dom;\n      const rootElm = ancestor(dialog, '.tox-dialog-wrap').getOr(dialog);\n      const isFixed = get$e(rootElm, 'position') === 'fixed';\n      let maxHeight;\n      if (isFixed) {\n        maxHeight = Math.max(documentElement$1.clientHeight, window.innerHeight);\n      } else {\n        maxHeight = Math.max(documentElement$1.offsetHeight, documentElement$1.scrollHeight);\n      }\n      const tabviewHeight = get$d(tabview);\n      const isTabListBeside = tabview.dom.offsetLeft >= tablist.dom.offsetLeft + get$c(tablist);\n      const currentTabHeight = isTabListBeside ? Math.max(get$d(tablist), tabviewHeight) : tabviewHeight;\n      const dialogTopMargin = parseInt(get$e(dialog, 'margin-top'), 10) || 0;\n      const dialogBottomMargin = parseInt(get$e(dialog, 'margin-bottom'), 10) || 0;\n      const dialogHeight = get$d(dialog) + dialogTopMargin + dialogBottomMargin;\n      const chromeHeight = dialogHeight - currentTabHeight;\n      return maxHeight - chromeHeight;\n    };\n    const showTab = (allTabs, comp) => {\n      head(allTabs).each(tab => TabSection.showTab(comp, tab.value));\n    };\n    const setTabviewHeight = (tabview, height) => {\n      set$8(tabview, 'height', height + 'px');\n      set$8(tabview, 'flex-basis', height + 'px');\n    };\n    const updateTabviewHeight = (dialogBody, tabview, maxTabHeight) => {\n      ancestor(dialogBody, '[role=\"dialog\"]').each(dialog => {\n        descendant(dialog, '[role=\"tablist\"]').each(tablist => {\n          maxTabHeight.get().map(height => {\n            set$8(tabview, 'height', '0');\n            set$8(tabview, 'flex-basis', '0');\n            return Math.min(height, getMaxTabviewHeight(dialog, tabview, tablist));\n          }).each(height => {\n            setTabviewHeight(tabview, height);\n          });\n        });\n      });\n    };\n    const getTabview = dialog => descendant(dialog, '[role=\"tabpanel\"]');\n    const smartMode = allTabs => {\n      const maxTabHeight = value$2();\n      const extraEvents = [\n        runOnAttached(comp => {\n          const dialog = comp.element;\n          getTabview(dialog).each(tabview => {\n            set$8(tabview, 'visibility', 'hidden');\n            comp.getSystem().getByDom(tabview).toOptional().each(tabviewComp => {\n              const heights = measureHeights(allTabs, tabview, tabviewComp);\n              const maxTabHeightOpt = getMaxHeight(heights);\n              maxTabHeightOpt.fold(maxTabHeight.clear, maxTabHeight.set);\n            });\n            updateTabviewHeight(dialog, tabview, maxTabHeight);\n            remove$6(tabview, 'visibility');\n            showTab(allTabs, comp);\n            requestAnimationFrame(() => {\n              updateTabviewHeight(dialog, tabview, maxTabHeight);\n            });\n          });\n        }),\n        run$1(windowResize(), comp => {\n          const dialog = comp.element;\n          getTabview(dialog).each(tabview => {\n            updateTabviewHeight(dialog, tabview, maxTabHeight);\n          });\n        }),\n        run$1(formResizeEvent, (comp, _se) => {\n          const dialog = comp.element;\n          getTabview(dialog).each(tabview => {\n            const oldFocus = active$1(getRootNode(tabview));\n            set$8(tabview, 'visibility', 'hidden');\n            const oldHeight = getRaw(tabview, 'height').map(h => parseInt(h, 10));\n            remove$6(tabview, 'height');\n            remove$6(tabview, 'flex-basis');\n            const newHeight = tabview.dom.getBoundingClientRect().height;\n            const hasGrown = oldHeight.forall(h => newHeight > h);\n            if (hasGrown) {\n              maxTabHeight.set(newHeight);\n              updateTabviewHeight(dialog, tabview, maxTabHeight);\n            } else {\n              oldHeight.each(h => {\n                setTabviewHeight(tabview, h);\n              });\n            }\n            remove$6(tabview, 'visibility');\n            oldFocus.each(focus$3);\n          });\n        })\n      ];\n      const selectFirst = false;\n      return {\n        extraEvents,\n        selectFirst\n      };\n    };\n\n    const SendDataToSectionChannel = 'send-data-to-section';\n    const SendDataToViewChannel = 'send-data-to-view';\n    const renderTabPanel = (spec, dialogData, backstage) => {\n      const storedValue = Cell({});\n      const updateDataWithForm = form => {\n        const formData = Representing.getValue(form);\n        const validData = toValidValues(formData).getOr({});\n        const currentData = storedValue.get();\n        const newData = deepMerge(currentData, validData);\n        storedValue.set(newData);\n      };\n      const setDataOnForm = form => {\n        const tabData = storedValue.get();\n        Representing.setValue(form, tabData);\n      };\n      const oldTab = Cell(null);\n      const allTabs = map$2(spec.tabs, tab => {\n        return {\n          value: tab.name,\n          dom: {\n            tag: 'div',\n            classes: ['tox-dialog__body-nav-item']\n          },\n          components: [text$2(backstage.shared.providers.translate(tab.title))],\n          view: () => {\n            return [Form.sketch(parts => ({\n                dom: {\n                  tag: 'div',\n                  classes: ['tox-form']\n                },\n                components: map$2(tab.items, item => interpretInForm(parts, item, dialogData, backstage)),\n                formBehaviours: derive$1([\n                  Keying.config({\n                    mode: 'acyclic',\n                    useTabstopAt: not(isPseudoStop)\n                  }),\n                  config('TabView.form.events', [\n                    runOnAttached(setDataOnForm),\n                    runOnDetached(updateDataWithForm)\n                  ]),\n                  Receiving.config({\n                    channels: wrapAll([\n                      {\n                        key: SendDataToSectionChannel,\n                        value: { onReceive: updateDataWithForm }\n                      },\n                      {\n                        key: SendDataToViewChannel,\n                        value: { onReceive: setDataOnForm }\n                      }\n                    ])\n                  })\n                ])\n              }))];\n          }\n        };\n      });\n      const tabMode = smartMode(allTabs);\n      return TabSection.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-dialog__body']\n        },\n        onChangeTab: (section, button, _viewItems) => {\n          const name = Representing.getValue(button);\n          emitWith(section, formTabChangeEvent, {\n            name,\n            oldName: oldTab.get()\n          });\n          oldTab.set(name);\n        },\n        tabs: allTabs,\n        components: [\n          TabSection.parts.tabbar({\n            dom: {\n              tag: 'div',\n              classes: ['tox-dialog__body-nav']\n            },\n            components: [Tabbar.parts.tabs({})],\n            markers: {\n              tabClass: 'tox-tab',\n              selectedClass: 'tox-dialog__body-nav-item--active'\n            },\n            tabbarBehaviours: derive$1([Tabstopping.config({})])\n          }),\n          TabSection.parts.tabview({\n            dom: {\n              tag: 'div',\n              classes: ['tox-dialog__body-content']\n            }\n          })\n        ],\n        selectFirst: tabMode.selectFirst,\n        tabSectionBehaviours: derive$1([\n          config('tabpanel', tabMode.extraEvents),\n          Keying.config({ mode: 'acyclic' }),\n          Composing.config({ find: comp => head(TabSection.getViewItems(comp)) }),\n          RepresentingConfigs.withComp(Optional.none(), tsection => {\n            tsection.getSystem().broadcastOn([SendDataToSectionChannel], {});\n            return storedValue.get();\n          }, (tsection, value) => {\n            storedValue.set(value);\n            tsection.getSystem().broadcastOn([SendDataToViewChannel], {});\n          })\n        ])\n      });\n    };\n\n    const dialogChannel = generate$6('update-dialog');\n    const titleChannel = generate$6('update-title');\n    const bodyChannel = generate$6('update-body');\n    const footerChannel = generate$6('update-footer');\n    const bodySendMessageChannel = generate$6('body-send-message');\n\n    const renderBody = (spec, dialogId, contentId, backstage, ariaAttrs) => {\n      const renderComponents = incoming => {\n        const body = incoming.body;\n        switch (body.type) {\n        case 'tabpanel': {\n            return [renderTabPanel(body, incoming.initialData, backstage)];\n          }\n        default: {\n            return [renderBodyPanel(body, incoming.initialData, backstage)];\n          }\n        }\n      };\n      const updateState = (_comp, incoming) => Optional.some({ isTabPanel: () => incoming.body.type === 'tabpanel' });\n      const ariaAttributes = { 'aria-live': 'polite' };\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-dialog__content-js'],\n          attributes: {\n            ...contentId.map(x => ({ id: x })).getOr({}),\n            ...ariaAttrs ? ariaAttributes : {}\n          }\n        },\n        components: [],\n        behaviours: derive$1([\n          ComposingConfigs.childAt(0),\n          Reflecting.config({\n            channel: `${ bodyChannel }-${ dialogId }`,\n            updateState,\n            renderComponents,\n            initialData: spec\n          })\n        ])\n      };\n    };\n    const renderInlineBody = (spec, dialogId, contentId, backstage, ariaAttrs) => renderBody(spec, dialogId, Optional.some(contentId), backstage, ariaAttrs);\n    const renderModalBody = (spec, dialogId, backstage) => {\n      const bodySpec = renderBody(spec, dialogId, Optional.none(), backstage, false);\n      return ModalDialog.parts.body(bodySpec);\n    };\n    const renderIframeBody = spec => {\n      const bodySpec = {\n        dom: {\n          tag: 'div',\n          classes: ['tox-dialog__content-js']\n        },\n        components: [{\n            dom: {\n              tag: 'div',\n              classes: ['tox-dialog__body-iframe']\n            },\n            components: [craft({\n                dom: {\n                  tag: 'iframe',\n                  attributes: { src: spec.url }\n                },\n                behaviours: derive$1([\n                  Tabstopping.config({}),\n                  Focusing.config({})\n                ])\n              })]\n          }],\n        behaviours: derive$1([Keying.config({\n            mode: 'acyclic',\n            useTabstopAt: not(isPseudoStop)\n          })])\n      };\n      return ModalDialog.parts.body(bodySpec);\n    };\n\n    function _typeof(obj) {\n      '@babel/helpers - typeof';\n      return _typeof = 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator ? function (obj) {\n        return typeof obj;\n      } : function (obj) {\n        return obj && 'function' == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;\n      }, _typeof(obj);\n    }\n    function _setPrototypeOf(o, p) {\n      _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n        o.__proto__ = p;\n        return o;\n      };\n      return _setPrototypeOf(o, p);\n    }\n    function _isNativeReflectConstruct() {\n      if (typeof Reflect === 'undefined' || !Reflect.construct)\n        return false;\n      if (Reflect.construct.sham)\n        return false;\n      if (typeof Proxy === 'function')\n        return true;\n      try {\n        Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {\n        }));\n        return true;\n      } catch (e) {\n        return false;\n      }\n    }\n    function _construct(Parent, args, Class) {\n      if (_isNativeReflectConstruct()) {\n        _construct = Reflect.construct;\n      } else {\n        _construct = function _construct(Parent, args, Class) {\n          var a = [null];\n          a.push.apply(a, args);\n          var Constructor = Function.bind.apply(Parent, a);\n          var instance = new Constructor();\n          if (Class)\n            _setPrototypeOf(instance, Class.prototype);\n          return instance;\n        };\n      }\n      return _construct.apply(null, arguments);\n    }\n    function _toConsumableArray(arr) {\n      return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();\n    }\n    function _arrayWithoutHoles(arr) {\n      if (Array.isArray(arr))\n        return _arrayLikeToArray(arr);\n    }\n    function _iterableToArray(iter) {\n      if (typeof Symbol !== 'undefined' && iter[Symbol.iterator] != null || iter['@@iterator'] != null)\n        return Array.from(iter);\n    }\n    function _unsupportedIterableToArray(o, minLen) {\n      if (!o)\n        return;\n      if (typeof o === 'string')\n        return _arrayLikeToArray(o, minLen);\n      var n = Object.prototype.toString.call(o).slice(8, -1);\n      if (n === 'Object' && o.constructor)\n        n = o.constructor.name;\n      if (n === 'Map' || n === 'Set')\n        return Array.from(o);\n      if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))\n        return _arrayLikeToArray(o, minLen);\n    }\n    function _arrayLikeToArray(arr, len) {\n      if (len == null || len > arr.length)\n        len = arr.length;\n      for (var i = 0, arr2 = new Array(len); i < len; i++)\n        arr2[i] = arr[i];\n      return arr2;\n    }\n    function _nonIterableSpread() {\n      throw new TypeError('Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.');\n    }\n    var hasOwnProperty = Object.hasOwnProperty, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen, getPrototypeOf = Object.getPrototypeOf, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\n    var freeze = Object.freeze, seal = Object.seal, create = Object.create;\n    var _ref = typeof Reflect !== 'undefined' && Reflect, apply = _ref.apply, construct = _ref.construct;\n    if (!apply) {\n      apply = function apply(fun, thisValue, args) {\n        return fun.apply(thisValue, args);\n      };\n    }\n    if (!freeze) {\n      freeze = function freeze(x) {\n        return x;\n      };\n    }\n    if (!seal) {\n      seal = function seal(x) {\n        return x;\n      };\n    }\n    if (!construct) {\n      construct = function construct(Func, args) {\n        return _construct(Func, _toConsumableArray(args));\n      };\n    }\n    var arrayForEach = unapply(Array.prototype.forEach);\n    var arrayPop = unapply(Array.prototype.pop);\n    var arrayPush = unapply(Array.prototype.push);\n    var stringToLowerCase = unapply(String.prototype.toLowerCase);\n    var stringMatch = unapply(String.prototype.match);\n    var stringReplace = unapply(String.prototype.replace);\n    var stringIndexOf = unapply(String.prototype.indexOf);\n    var stringTrim = unapply(String.prototype.trim);\n    var regExpTest = unapply(RegExp.prototype.test);\n    var typeErrorCreate = unconstruct(TypeError);\n    function unapply(func) {\n      return function (thisArg) {\n        for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n          args[_key - 1] = arguments[_key];\n        }\n        return apply(func, thisArg, args);\n      };\n    }\n    function unconstruct(func) {\n      return function () {\n        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n          args[_key2] = arguments[_key2];\n        }\n        return construct(func, args);\n      };\n    }\n    function addToSet(set, array) {\n      if (setPrototypeOf) {\n        setPrototypeOf(set, null);\n      }\n      var l = array.length;\n      while (l--) {\n        var element = array[l];\n        if (typeof element === 'string') {\n          var lcElement = stringToLowerCase(element);\n          if (lcElement !== element) {\n            if (!isFrozen(array)) {\n              array[l] = lcElement;\n            }\n            element = lcElement;\n          }\n        }\n        set[element] = true;\n      }\n      return set;\n    }\n    function clone(object) {\n      var newObject = create(null);\n      var property;\n      for (property in object) {\n        if (apply(hasOwnProperty, object, [property])) {\n          newObject[property] = object[property];\n        }\n      }\n      return newObject;\n    }\n    function lookupGetter(object, prop) {\n      while (object !== null) {\n        var desc = getOwnPropertyDescriptor(object, prop);\n        if (desc) {\n          if (desc.get) {\n            return unapply(desc.get);\n          }\n          if (typeof desc.value === 'function') {\n            return unapply(desc.value);\n          }\n        }\n        object = getPrototypeOf(object);\n      }\n      function fallbackValue(element) {\n        console.warn('fallback value for', element);\n        return null;\n      }\n      return fallbackValue;\n    }\n    var html$1 = freeze([\n      'a',\n      'abbr',\n      'acronym',\n      'address',\n      'area',\n      'article',\n      'aside',\n      'audio',\n      'b',\n      'bdi',\n      'bdo',\n      'big',\n      'blink',\n      'blockquote',\n      'body',\n      'br',\n      'button',\n      'canvas',\n      'caption',\n      'center',\n      'cite',\n      'code',\n      'col',\n      'colgroup',\n      'content',\n      'data',\n      'datalist',\n      'dd',\n      'decorator',\n      'del',\n      'details',\n      'dfn',\n      'dialog',\n      'dir',\n      'div',\n      'dl',\n      'dt',\n      'element',\n      'em',\n      'fieldset',\n      'figcaption',\n      'figure',\n      'font',\n      'footer',\n      'form',\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'head',\n      'header',\n      'hgroup',\n      'hr',\n      'html',\n      'i',\n      'img',\n      'input',\n      'ins',\n      'kbd',\n      'label',\n      'legend',\n      'li',\n      'main',\n      'map',\n      'mark',\n      'marquee',\n      'menu',\n      'menuitem',\n      'meter',\n      'nav',\n      'nobr',\n      'ol',\n      'optgroup',\n      'option',\n      'output',\n      'p',\n      'picture',\n      'pre',\n      'progress',\n      'q',\n      'rp',\n      'rt',\n      'ruby',\n      's',\n      'samp',\n      'section',\n      'select',\n      'shadow',\n      'small',\n      'source',\n      'spacer',\n      'span',\n      'strike',\n      'strong',\n      'style',\n      'sub',\n      'summary',\n      'sup',\n      'table',\n      'tbody',\n      'td',\n      'template',\n      'textarea',\n      'tfoot',\n      'th',\n      'thead',\n      'time',\n      'tr',\n      'track',\n      'tt',\n      'u',\n      'ul',\n      'var',\n      'video',\n      'wbr'\n    ]);\n    var svg$1 = freeze([\n      'svg',\n      'a',\n      'altglyph',\n      'altglyphdef',\n      'altglyphitem',\n      'animatecolor',\n      'animatemotion',\n      'animatetransform',\n      'circle',\n      'clippath',\n      'defs',\n      'desc',\n      'ellipse',\n      'filter',\n      'font',\n      'g',\n      'glyph',\n      'glyphref',\n      'hkern',\n      'image',\n      'line',\n      'lineargradient',\n      'marker',\n      'mask',\n      'metadata',\n      'mpath',\n      'path',\n      'pattern',\n      'polygon',\n      'polyline',\n      'radialgradient',\n      'rect',\n      'stop',\n      'style',\n      'switch',\n      'symbol',\n      'text',\n      'textpath',\n      'title',\n      'tref',\n      'tspan',\n      'view',\n      'vkern'\n    ]);\n    var svgFilters = freeze([\n      'feBlend',\n      'feColorMatrix',\n      'feComponentTransfer',\n      'feComposite',\n      'feConvolveMatrix',\n      'feDiffuseLighting',\n      'feDisplacementMap',\n      'feDistantLight',\n      'feFlood',\n      'feFuncA',\n      'feFuncB',\n      'feFuncG',\n      'feFuncR',\n      'feGaussianBlur',\n      'feImage',\n      'feMerge',\n      'feMergeNode',\n      'feMorphology',\n      'feOffset',\n      'fePointLight',\n      'feSpecularLighting',\n      'feSpotLight',\n      'feTile',\n      'feTurbulence'\n    ]);\n    var svgDisallowed = freeze([\n      'animate',\n      'color-profile',\n      'cursor',\n      'discard',\n      'fedropshadow',\n      'font-face',\n      'font-face-format',\n      'font-face-name',\n      'font-face-src',\n      'font-face-uri',\n      'foreignobject',\n      'hatch',\n      'hatchpath',\n      'mesh',\n      'meshgradient',\n      'meshpatch',\n      'meshrow',\n      'missing-glyph',\n      'script',\n      'set',\n      'solidcolor',\n      'unknown',\n      'use'\n    ]);\n    var mathMl$1 = freeze([\n      'math',\n      'menclose',\n      'merror',\n      'mfenced',\n      'mfrac',\n      'mglyph',\n      'mi',\n      'mlabeledtr',\n      'mmultiscripts',\n      'mn',\n      'mo',\n      'mover',\n      'mpadded',\n      'mphantom',\n      'mroot',\n      'mrow',\n      'ms',\n      'mspace',\n      'msqrt',\n      'mstyle',\n      'msub',\n      'msup',\n      'msubsup',\n      'mtable',\n      'mtd',\n      'mtext',\n      'mtr',\n      'munder',\n      'munderover'\n    ]);\n    var mathMlDisallowed = freeze([\n      'maction',\n      'maligngroup',\n      'malignmark',\n      'mlongdiv',\n      'mscarries',\n      'mscarry',\n      'msgroup',\n      'mstack',\n      'msline',\n      'msrow',\n      'semantics',\n      'annotation',\n      'annotation-xml',\n      'mprescripts',\n      'none'\n    ]);\n    var text = freeze(['#text']);\n    var html = freeze([\n      'accept',\n      'action',\n      'align',\n      'alt',\n      'autocapitalize',\n      'autocomplete',\n      'autopictureinpicture',\n      'autoplay',\n      'background',\n      'bgcolor',\n      'border',\n      'capture',\n      'cellpadding',\n      'cellspacing',\n      'checked',\n      'cite',\n      'class',\n      'clear',\n      'color',\n      'cols',\n      'colspan',\n      'controls',\n      'controlslist',\n      'coords',\n      'crossorigin',\n      'datetime',\n      'decoding',\n      'default',\n      'dir',\n      'disabled',\n      'disablepictureinpicture',\n      'disableremoteplayback',\n      'download',\n      'draggable',\n      'enctype',\n      'enterkeyhint',\n      'face',\n      'for',\n      'headers',\n      'height',\n      'hidden',\n      'high',\n      'href',\n      'hreflang',\n      'id',\n      'inputmode',\n      'integrity',\n      'ismap',\n      'kind',\n      'label',\n      'lang',\n      'list',\n      'loading',\n      'loop',\n      'low',\n      'max',\n      'maxlength',\n      'media',\n      'method',\n      'min',\n      'minlength',\n      'multiple',\n      'muted',\n      'name',\n      'nonce',\n      'noshade',\n      'novalidate',\n      'nowrap',\n      'open',\n      'optimum',\n      'pattern',\n      'placeholder',\n      'playsinline',\n      'poster',\n      'preload',\n      'pubdate',\n      'radiogroup',\n      'readonly',\n      'rel',\n      'required',\n      'rev',\n      'reversed',\n      'role',\n      'rows',\n      'rowspan',\n      'spellcheck',\n      'scope',\n      'selected',\n      'shape',\n      'size',\n      'sizes',\n      'span',\n      'srclang',\n      'start',\n      'src',\n      'srcset',\n      'step',\n      'style',\n      'summary',\n      'tabindex',\n      'title',\n      'translate',\n      'type',\n      'usemap',\n      'valign',\n      'value',\n      'width',\n      'xmlns',\n      'slot'\n    ]);\n    var svg = freeze([\n      'accent-height',\n      'accumulate',\n      'additive',\n      'alignment-baseline',\n      'ascent',\n      'attributename',\n      'attributetype',\n      'azimuth',\n      'basefrequency',\n      'baseline-shift',\n      'begin',\n      'bias',\n      'by',\n      'class',\n      'clip',\n      'clippathunits',\n      'clip-path',\n      'clip-rule',\n      'color',\n      'color-interpolation',\n      'color-interpolation-filters',\n      'color-profile',\n      'color-rendering',\n      'cx',\n      'cy',\n      'd',\n      'dx',\n      'dy',\n      'diffuseconstant',\n      'direction',\n      'display',\n      'divisor',\n      'dur',\n      'edgemode',\n      'elevation',\n      'end',\n      'fill',\n      'fill-opacity',\n      'fill-rule',\n      'filter',\n      'filterunits',\n      'flood-color',\n      'flood-opacity',\n      'font-family',\n      'font-size',\n      'font-size-adjust',\n      'font-stretch',\n      'font-style',\n      'font-variant',\n      'font-weight',\n      'fx',\n      'fy',\n      'g1',\n      'g2',\n      'glyph-name',\n      'glyphref',\n      'gradientunits',\n      'gradienttransform',\n      'height',\n      'href',\n      'id',\n      'image-rendering',\n      'in',\n      'in2',\n      'k',\n      'k1',\n      'k2',\n      'k3',\n      'k4',\n      'kerning',\n      'keypoints',\n      'keysplines',\n      'keytimes',\n      'lang',\n      'lengthadjust',\n      'letter-spacing',\n      'kernelmatrix',\n      'kernelunitlength',\n      'lighting-color',\n      'local',\n      'marker-end',\n      'marker-mid',\n      'marker-start',\n      'markerheight',\n      'markerunits',\n      'markerwidth',\n      'maskcontentunits',\n      'maskunits',\n      'max',\n      'mask',\n      'media',\n      'method',\n      'mode',\n      'min',\n      'name',\n      'numoctaves',\n      'offset',\n      'operator',\n      'opacity',\n      'order',\n      'orient',\n      'orientation',\n      'origin',\n      'overflow',\n      'paint-order',\n      'path',\n      'pathlength',\n      'patterncontentunits',\n      'patterntransform',\n      'patternunits',\n      'points',\n      'preservealpha',\n      'preserveaspectratio',\n      'primitiveunits',\n      'r',\n      'rx',\n      'ry',\n      'radius',\n      'refx',\n      'refy',\n      'repeatcount',\n      'repeatdur',\n      'restart',\n      'result',\n      'rotate',\n      'scale',\n      'seed',\n      'shape-rendering',\n      'specularconstant',\n      'specularexponent',\n      'spreadmethod',\n      'startoffset',\n      'stddeviation',\n      'stitchtiles',\n      'stop-color',\n      'stop-opacity',\n      'stroke-dasharray',\n      'stroke-dashoffset',\n      'stroke-linecap',\n      'stroke-linejoin',\n      'stroke-miterlimit',\n      'stroke-opacity',\n      'stroke',\n      'stroke-width',\n      'style',\n      'surfacescale',\n      'systemlanguage',\n      'tabindex',\n      'targetx',\n      'targety',\n      'transform',\n      'transform-origin',\n      'text-anchor',\n      'text-decoration',\n      'text-rendering',\n      'textlength',\n      'type',\n      'u1',\n      'u2',\n      'unicode',\n      'values',\n      'viewbox',\n      'visibility',\n      'version',\n      'vert-adv-y',\n      'vert-origin-x',\n      'vert-origin-y',\n      'width',\n      'word-spacing',\n      'wrap',\n      'writing-mode',\n      'xchannelselector',\n      'ychannelselector',\n      'x',\n      'x1',\n      'x2',\n      'xmlns',\n      'y',\n      'y1',\n      'y2',\n      'z',\n      'zoomandpan'\n    ]);\n    var mathMl = freeze([\n      'accent',\n      'accentunder',\n      'align',\n      'bevelled',\n      'close',\n      'columnsalign',\n      'columnlines',\n      'columnspan',\n      'denomalign',\n      'depth',\n      'dir',\n      'display',\n      'displaystyle',\n      'encoding',\n      'fence',\n      'frame',\n      'height',\n      'href',\n      'id',\n      'largeop',\n      'length',\n      'linethickness',\n      'lspace',\n      'lquote',\n      'mathbackground',\n      'mathcolor',\n      'mathsize',\n      'mathvariant',\n      'maxsize',\n      'minsize',\n      'movablelimits',\n      'notation',\n      'numalign',\n      'open',\n      'rowalign',\n      'rowlines',\n      'rowspacing',\n      'rowspan',\n      'rspace',\n      'rquote',\n      'scriptlevel',\n      'scriptminsize',\n      'scriptsizemultiplier',\n      'selection',\n      'separator',\n      'separators',\n      'stretchy',\n      'subscriptshift',\n      'supscriptshift',\n      'symmetric',\n      'voffset',\n      'width',\n      'xmlns'\n    ]);\n    var xml = freeze([\n      'xlink:href',\n      'xml:id',\n      'xlink:title',\n      'xml:space',\n      'xmlns:xlink'\n    ]);\n    var MUSTACHE_EXPR = seal(/\\{\\{[\\w\\W]*|[\\w\\W]*\\}\\}/gm);\n    var ERB_EXPR = seal(/<%[\\w\\W]*|[\\w\\W]*%>/gm);\n    var DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]/);\n    var ARIA_ATTR = seal(/^aria-[\\-\\w]+$/);\n    var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i);\n    var IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\n    var ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g);\n    var DOCTYPE_NAME = seal(/^html$/i);\n    var getGlobal = function getGlobal() {\n      return typeof window === 'undefined' ? null : window;\n    };\n    var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {\n      if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n        return null;\n      }\n      var suffix = null;\n      var ATTR_NAME = 'data-tt-policy-suffix';\n      if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {\n        suffix = document.currentScript.getAttribute(ATTR_NAME);\n      }\n      var policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n      try {\n        return trustedTypes.createPolicy(policyName, {\n          createHTML: function createHTML(html) {\n            return html;\n          }\n        });\n      } catch (_) {\n        console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n        return null;\n      }\n    };\n    function createDOMPurify() {\n      var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n      var DOMPurify = function DOMPurify(root) {\n        return createDOMPurify(root);\n      };\n      DOMPurify.version = '2.3.8';\n      DOMPurify.removed = [];\n      if (!window || !window.document || window.document.nodeType !== 9) {\n        DOMPurify.isSupported = false;\n        return DOMPurify;\n      }\n      var originalDocument = window.document;\n      var document = window.document;\n      var DocumentFragment = window.DocumentFragment, HTMLTemplateElement = window.HTMLTemplateElement, Node = window.Node, Element = window.Element, NodeFilter = window.NodeFilter, _window$NamedNodeMap = window.NamedNodeMap, NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, HTMLFormElement = window.HTMLFormElement, DOMParser = window.DOMParser, trustedTypes = window.trustedTypes;\n      var ElementPrototype = Element.prototype;\n      var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n      var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n      var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n      var getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n      if (typeof HTMLTemplateElement === 'function') {\n        var template = document.createElement('template');\n        if (template.content && template.content.ownerDocument) {\n          document = template.content.ownerDocument;\n        }\n      }\n      var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);\n      var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';\n      var _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;\n      var importNode = originalDocument.importNode;\n      var documentMode = {};\n      try {\n        documentMode = clone(document).documentMode ? document.documentMode : {};\n      } catch (_) {\n      }\n      var hooks = {};\n      DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;\n      var MUSTACHE_EXPR$1 = MUSTACHE_EXPR, ERB_EXPR$1 = ERB_EXPR, DATA_ATTR$1 = DATA_ATTR, ARIA_ATTR$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$1 = ATTR_WHITESPACE;\n      var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;\n      var ALLOWED_TAGS = null;\n      var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));\n      var ALLOWED_ATTR = null;\n      var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));\n      var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {\n        tagNameCheck: {\n          writable: true,\n          configurable: false,\n          enumerable: true,\n          value: null\n        },\n        attributeNameCheck: {\n          writable: true,\n          configurable: false,\n          enumerable: true,\n          value: null\n        },\n        allowCustomizedBuiltInElements: {\n          writable: true,\n          configurable: false,\n          enumerable: true,\n          value: false\n        }\n      }));\n      var FORBID_TAGS = null;\n      var FORBID_ATTR = null;\n      var ALLOW_ARIA_ATTR = true;\n      var ALLOW_DATA_ATTR = true;\n      var ALLOW_UNKNOWN_PROTOCOLS = false;\n      var SAFE_FOR_TEMPLATES = false;\n      var WHOLE_DOCUMENT = false;\n      var SET_CONFIG = false;\n      var FORCE_BODY = false;\n      var RETURN_DOM = false;\n      var RETURN_DOM_FRAGMENT = false;\n      var RETURN_TRUSTED_TYPE = false;\n      var SANITIZE_DOM = true;\n      var KEEP_CONTENT = true;\n      var IN_PLACE = false;\n      var USE_PROFILES = {};\n      var FORBID_CONTENTS = null;\n      var DEFAULT_FORBID_CONTENTS = addToSet({}, [\n        'annotation-xml',\n        'audio',\n        'colgroup',\n        'desc',\n        'foreignobject',\n        'head',\n        'iframe',\n        'math',\n        'mi',\n        'mn',\n        'mo',\n        'ms',\n        'mtext',\n        'noembed',\n        'noframes',\n        'noscript',\n        'plaintext',\n        'script',\n        'style',\n        'svg',\n        'template',\n        'thead',\n        'title',\n        'video',\n        'xmp'\n      ]);\n      var DATA_URI_TAGS = null;\n      var DEFAULT_DATA_URI_TAGS = addToSet({}, [\n        'audio',\n        'video',\n        'img',\n        'source',\n        'image',\n        'track'\n      ]);\n      var URI_SAFE_ATTRIBUTES = null;\n      var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [\n        'alt',\n        'class',\n        'for',\n        'id',\n        'label',\n        'name',\n        'pattern',\n        'placeholder',\n        'role',\n        'summary',\n        'title',\n        'value',\n        'style',\n        'xmlns'\n      ]);\n      var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n      var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n      var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n      var NAMESPACE = HTML_NAMESPACE;\n      var IS_EMPTY_INPUT = false;\n      var PARSER_MEDIA_TYPE;\n      var SUPPORTED_PARSER_MEDIA_TYPES = [\n        'application/xhtml+xml',\n        'text/html'\n      ];\n      var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n      var transformCaseFunc;\n      var CONFIG = null;\n      var formElement = document.createElement('form');\n      var isRegexOrFunction = function isRegexOrFunction(testValue) {\n        return testValue instanceof RegExp || testValue instanceof Function;\n      };\n      var _parseConfig = function _parseConfig(cfg) {\n        if (CONFIG && CONFIG === cfg) {\n          return;\n        }\n        if (!cfg || _typeof(cfg) !== 'object') {\n          cfg = {};\n        }\n        cfg = clone(cfg);\n        ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;\n        ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;\n        URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;\n        DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;\n        FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;\n        FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};\n        FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};\n        USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;\n        ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;\n        ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;\n        ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;\n        SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;\n        WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;\n        RETURN_DOM = cfg.RETURN_DOM || false;\n        RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;\n        RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;\n        FORCE_BODY = cfg.FORCE_BODY || false;\n        SANITIZE_DOM = cfg.SANITIZE_DOM !== false;\n        KEEP_CONTENT = cfg.KEEP_CONTENT !== false;\n        IN_PLACE = cfg.IN_PLACE || false;\n        IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;\n        NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n        if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {\n          CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;\n        }\n        if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {\n          CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;\n        }\n        if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {\n          CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;\n        }\n        PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;\n        transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {\n          return x;\n        } : stringToLowerCase;\n        if (SAFE_FOR_TEMPLATES) {\n          ALLOW_DATA_ATTR = false;\n        }\n        if (RETURN_DOM_FRAGMENT) {\n          RETURN_DOM = true;\n        }\n        if (USE_PROFILES) {\n          ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));\n          ALLOWED_ATTR = [];\n          if (USE_PROFILES.html === true) {\n            addToSet(ALLOWED_TAGS, html$1);\n            addToSet(ALLOWED_ATTR, html);\n          }\n          if (USE_PROFILES.svg === true) {\n            addToSet(ALLOWED_TAGS, svg$1);\n            addToSet(ALLOWED_ATTR, svg);\n            addToSet(ALLOWED_ATTR, xml);\n          }\n          if (USE_PROFILES.svgFilters === true) {\n            addToSet(ALLOWED_TAGS, svgFilters);\n            addToSet(ALLOWED_ATTR, svg);\n            addToSet(ALLOWED_ATTR, xml);\n          }\n          if (USE_PROFILES.mathMl === true) {\n            addToSet(ALLOWED_TAGS, mathMl$1);\n            addToSet(ALLOWED_ATTR, mathMl);\n            addToSet(ALLOWED_ATTR, xml);\n          }\n        }\n        if (cfg.ADD_TAGS) {\n          if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n            ALLOWED_TAGS = clone(ALLOWED_TAGS);\n          }\n          addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);\n        }\n        if (cfg.ADD_ATTR) {\n          if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n            ALLOWED_ATTR = clone(ALLOWED_ATTR);\n          }\n          addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);\n        }\n        if (cfg.ADD_URI_SAFE_ATTR) {\n          addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);\n        }\n        if (cfg.FORBID_CONTENTS) {\n          if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n            FORBID_CONTENTS = clone(FORBID_CONTENTS);\n          }\n          addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);\n        }\n        if (KEEP_CONTENT) {\n          ALLOWED_TAGS['#text'] = true;\n        }\n        if (WHOLE_DOCUMENT) {\n          addToSet(ALLOWED_TAGS, [\n            'html',\n            'head',\n            'body'\n          ]);\n        }\n        if (ALLOWED_TAGS.table) {\n          addToSet(ALLOWED_TAGS, ['tbody']);\n          delete FORBID_TAGS.tbody;\n        }\n        if (freeze) {\n          freeze(cfg);\n        }\n        CONFIG = cfg;\n      };\n      var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [\n        'mi',\n        'mo',\n        'mn',\n        'ms',\n        'mtext'\n      ]);\n      var HTML_INTEGRATION_POINTS = addToSet({}, [\n        'foreignobject',\n        'desc',\n        'title',\n        'annotation-xml'\n      ]);\n      var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [\n        'title',\n        'style',\n        'font',\n        'a',\n        'script'\n      ]);\n      var ALL_SVG_TAGS = addToSet({}, svg$1);\n      addToSet(ALL_SVG_TAGS, svgFilters);\n      addToSet(ALL_SVG_TAGS, svgDisallowed);\n      var ALL_MATHML_TAGS = addToSet({}, mathMl$1);\n      addToSet(ALL_MATHML_TAGS, mathMlDisallowed);\n      var _checkValidNamespace = function _checkValidNamespace(element) {\n        var parent = getParentNode(element);\n        if (!parent || !parent.tagName) {\n          parent = {\n            namespaceURI: HTML_NAMESPACE,\n            tagName: 'template'\n          };\n        }\n        var tagName = stringToLowerCase(element.tagName);\n        var parentTagName = stringToLowerCase(parent.tagName);\n        if (element.namespaceURI === SVG_NAMESPACE) {\n          if (parent.namespaceURI === HTML_NAMESPACE) {\n            return tagName === 'svg';\n          }\n          if (parent.namespaceURI === MATHML_NAMESPACE) {\n            return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);\n          }\n          return Boolean(ALL_SVG_TAGS[tagName]);\n        }\n        if (element.namespaceURI === MATHML_NAMESPACE) {\n          if (parent.namespaceURI === HTML_NAMESPACE) {\n            return tagName === 'math';\n          }\n          if (parent.namespaceURI === SVG_NAMESPACE) {\n            return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n          }\n          return Boolean(ALL_MATHML_TAGS[tagName]);\n        }\n        if (element.namespaceURI === HTML_NAMESPACE) {\n          if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {\n            return false;\n          }\n          if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {\n            return false;\n          }\n          return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);\n        }\n        return false;\n      };\n      var _forceRemove = function _forceRemove(node) {\n        arrayPush(DOMPurify.removed, { element: node });\n        try {\n          node.parentNode.removeChild(node);\n        } catch (_) {\n          try {\n            node.outerHTML = emptyHTML;\n          } catch (_) {\n            node.remove();\n          }\n        }\n      };\n      var _removeAttribute = function _removeAttribute(name, node) {\n        try {\n          arrayPush(DOMPurify.removed, {\n            attribute: node.getAttributeNode(name),\n            from: node\n          });\n        } catch (_) {\n          arrayPush(DOMPurify.removed, {\n            attribute: null,\n            from: node\n          });\n        }\n        node.removeAttribute(name);\n        if (name === 'is' && !ALLOWED_ATTR[name]) {\n          if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n            try {\n              _forceRemove(node);\n            } catch (_) {\n            }\n          } else {\n            try {\n              node.setAttribute(name, '');\n            } catch (_) {\n            }\n          }\n        }\n      };\n      var _initDocument = function _initDocument(dirty) {\n        var doc;\n        var leadingWhitespace;\n        if (FORCE_BODY) {\n          dirty = '<remove></remove>' + dirty;\n        } else {\n          var matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n          leadingWhitespace = matches && matches[0];\n        }\n        if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {\n          dirty = '<html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head><body>' + dirty + '</body></html>';\n        }\n        var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;\n        if (NAMESPACE === HTML_NAMESPACE) {\n          try {\n            doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);\n          } catch (_) {\n          }\n        }\n        if (!doc || !doc.documentElement) {\n          doc = implementation.createDocument(NAMESPACE, 'template', null);\n          try {\n            doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;\n          } catch (_) {\n          }\n        }\n        var body = doc.body || doc.documentElement;\n        if (dirty && leadingWhitespace) {\n          body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);\n        }\n        if (NAMESPACE === HTML_NAMESPACE) {\n          return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];\n        }\n        return WHOLE_DOCUMENT ? doc.documentElement : body;\n      };\n      var _createIterator = function _createIterator(root) {\n        return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);\n      };\n      var _isClobbered = function _isClobbered(elm) {\n        return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function');\n      };\n      var _isNode = function _isNode(object) {\n        return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';\n      };\n      var _executeHook = function _executeHook(entryPoint, currentNode, data) {\n        if (!hooks[entryPoint]) {\n          return;\n        }\n        arrayForEach(hooks[entryPoint], function (hook) {\n          hook.call(DOMPurify, currentNode, data, CONFIG);\n        });\n      };\n      var _sanitizeElements = function _sanitizeElements(currentNode) {\n        var content;\n        _executeHook('beforeSanitizeElements', currentNode, null);\n        if (_isClobbered(currentNode)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (regExpTest(/[\\u0080-\\uFFFF]/, currentNode.nodeName)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        var tagName = transformCaseFunc(currentNode.nodeName);\n        _executeHook('uponSanitizeElement', currentNode, {\n          tagName: tagName,\n          allowedTags: ALLOWED_TAGS\n        });\n        if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\\w]/g, currentNode.innerHTML) && regExpTest(/<[/\\w]/g, currentNode.textContent)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n          if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {\n            if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName))\n              return false;\n            if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName))\n              return false;\n          }\n          if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {\n            var parentNode = getParentNode(currentNode) || currentNode.parentNode;\n            var childNodes = getChildNodes(currentNode) || currentNode.childNodes;\n            if (childNodes && parentNode) {\n              var childCount = childNodes.length;\n              for (var i = childCount - 1; i >= 0; --i) {\n                parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));\n              }\n            }\n          }\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\\/no(script|embed)/i, currentNode.innerHTML)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {\n          content = currentNode.textContent;\n          content = stringReplace(content, MUSTACHE_EXPR$1, ' ');\n          content = stringReplace(content, ERB_EXPR$1, ' ');\n          if (currentNode.textContent !== content) {\n            arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });\n            currentNode.textContent = content;\n          }\n        }\n        _executeHook('afterSanitizeElements', currentNode, null);\n        return false;\n      };\n      var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {\n        if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {\n          return false;\n        }\n        if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName));\n        else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName));\n        else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n          if (_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value)));\n          else {\n            return false;\n          }\n        } else if (URI_SAFE_ATTRIBUTES[lcName]);\n        else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, '')));\n        else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]);\n        else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, '')));\n        else if (!value);\n        else {\n          return false;\n        }\n        return true;\n      };\n      var _basicCustomElementTest = function _basicCustomElementTest(tagName) {\n        return tagName.indexOf('-') > 0;\n      };\n      var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {\n        var attr;\n        var value;\n        var lcName;\n        var l;\n        _executeHook('beforeSanitizeAttributes', currentNode, null);\n        var attributes = currentNode.attributes;\n        if (!attributes) {\n          return;\n        }\n        var hookEvent = {\n          attrName: '',\n          attrValue: '',\n          keepAttr: true,\n          allowedAttributes: ALLOWED_ATTR\n        };\n        l = attributes.length;\n        while (l--) {\n          attr = attributes[l];\n          var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI;\n          value = name === 'value' ? attr.value : stringTrim(attr.value);\n          lcName = transformCaseFunc(name);\n          var initValue = value;\n          hookEvent.attrName = lcName;\n          hookEvent.attrValue = value;\n          hookEvent.keepAttr = true;\n          hookEvent.forceKeepAttr = undefined;\n          _executeHook('uponSanitizeAttribute', currentNode, hookEvent);\n          value = hookEvent.attrValue;\n          if (hookEvent.forceKeepAttr) {\n            continue;\n          }\n          if (!hookEvent.keepAttr) {\n            _removeAttribute(name, currentNode);\n            continue;\n          }\n          if (regExpTest(/\\/>/i, value)) {\n            _removeAttribute(name, currentNode);\n            continue;\n          }\n          if (SAFE_FOR_TEMPLATES) {\n            value = stringReplace(value, MUSTACHE_EXPR$1, ' ');\n            value = stringReplace(value, ERB_EXPR$1, ' ');\n          }\n          var lcTag = transformCaseFunc(currentNode.nodeName);\n          if (!_isValidAttribute(lcTag, lcName, value)) {\n            _removeAttribute(name, currentNode);\n            continue;\n          }\n          if (value !== initValue) {\n            try {\n              if (namespaceURI) {\n                currentNode.setAttributeNS(namespaceURI, name, value);\n              } else {\n                currentNode.setAttribute(name, value);\n              }\n            } catch (_) {\n              _removeAttribute(name, currentNode);\n            }\n          }\n        }\n        _executeHook('afterSanitizeAttributes', currentNode, null);\n      };\n      var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {\n        var shadowNode;\n        var shadowIterator = _createIterator(fragment);\n        _executeHook('beforeSanitizeShadowDOM', fragment, null);\n        while (shadowNode = shadowIterator.nextNode()) {\n          _executeHook('uponSanitizeShadowNode', shadowNode, null);\n          if (_sanitizeElements(shadowNode)) {\n            continue;\n          }\n          if (shadowNode.content instanceof DocumentFragment) {\n            _sanitizeShadowDOM(shadowNode.content);\n          }\n          _sanitizeAttributes(shadowNode);\n        }\n        _executeHook('afterSanitizeShadowDOM', fragment, null);\n      };\n      DOMPurify.sanitize = function (dirty, cfg) {\n        var body;\n        var importedNode;\n        var currentNode;\n        var oldNode;\n        var returnNode;\n        IS_EMPTY_INPUT = !dirty;\n        if (IS_EMPTY_INPUT) {\n          dirty = '<!-->';\n        }\n        if (typeof dirty !== 'string' && !_isNode(dirty)) {\n          if (typeof dirty.toString !== 'function') {\n            throw typeErrorCreate('toString is not a function');\n          } else {\n            dirty = dirty.toString();\n            if (typeof dirty !== 'string') {\n              throw typeErrorCreate('dirty is not a string, aborting');\n            }\n          }\n        }\n        if (!DOMPurify.isSupported) {\n          if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {\n            if (typeof dirty === 'string') {\n              return window.toStaticHTML(dirty);\n            }\n            if (_isNode(dirty)) {\n              return window.toStaticHTML(dirty.outerHTML);\n            }\n          }\n          return dirty;\n        }\n        if (!SET_CONFIG) {\n          _parseConfig(cfg);\n        }\n        DOMPurify.removed = [];\n        if (typeof dirty === 'string') {\n          IN_PLACE = false;\n        }\n        if (IN_PLACE) {\n          if (dirty.nodeName) {\n            var tagName = transformCaseFunc(dirty.nodeName);\n            if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n              throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');\n            }\n          }\n        } else if (dirty instanceof Node) {\n          body = _initDocument('<!---->');\n          importedNode = body.ownerDocument.importNode(dirty, true);\n          if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {\n            body = importedNode;\n          } else if (importedNode.nodeName === 'HTML') {\n            body = importedNode;\n          } else {\n            body.appendChild(importedNode);\n          }\n        } else {\n          if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {\n            return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;\n          }\n          body = _initDocument(dirty);\n          if (!body) {\n            return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';\n          }\n        }\n        if (body && FORCE_BODY) {\n          _forceRemove(body.firstChild);\n        }\n        var nodeIterator = _createIterator(IN_PLACE ? dirty : body);\n        while (currentNode = nodeIterator.nextNode()) {\n          if (currentNode.nodeType === 3 && currentNode === oldNode) {\n            continue;\n          }\n          if (_sanitizeElements(currentNode)) {\n            continue;\n          }\n          if (currentNode.content instanceof DocumentFragment) {\n            _sanitizeShadowDOM(currentNode.content);\n          }\n          _sanitizeAttributes(currentNode);\n          oldNode = currentNode;\n        }\n        oldNode = null;\n        if (IN_PLACE) {\n          return dirty;\n        }\n        if (RETURN_DOM) {\n          if (RETURN_DOM_FRAGMENT) {\n            returnNode = createDocumentFragment.call(body.ownerDocument);\n            while (body.firstChild) {\n              returnNode.appendChild(body.firstChild);\n            }\n          } else {\n            returnNode = body;\n          }\n          if (ALLOWED_ATTR.shadowroot) {\n            returnNode = importNode.call(originalDocument, returnNode, true);\n          }\n          return returnNode;\n        }\n        var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n        if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {\n          serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\\n' + serializedHTML;\n        }\n        if (SAFE_FOR_TEMPLATES) {\n          serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');\n          serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');\n        }\n        return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;\n      };\n      DOMPurify.setConfig = function (cfg) {\n        _parseConfig(cfg);\n        SET_CONFIG = true;\n      };\n      DOMPurify.clearConfig = function () {\n        CONFIG = null;\n        SET_CONFIG = false;\n      };\n      DOMPurify.isValidAttribute = function (tag, attr, value) {\n        if (!CONFIG) {\n          _parseConfig({});\n        }\n        var lcTag = transformCaseFunc(tag);\n        var lcName = transformCaseFunc(attr);\n        return _isValidAttribute(lcTag, lcName, value);\n      };\n      DOMPurify.addHook = function (entryPoint, hookFunction) {\n        if (typeof hookFunction !== 'function') {\n          return;\n        }\n        hooks[entryPoint] = hooks[entryPoint] || [];\n        arrayPush(hooks[entryPoint], hookFunction);\n      };\n      DOMPurify.removeHook = function (entryPoint) {\n        if (hooks[entryPoint]) {\n          return arrayPop(hooks[entryPoint]);\n        }\n      };\n      DOMPurify.removeHooks = function (entryPoint) {\n        if (hooks[entryPoint]) {\n          hooks[entryPoint] = [];\n        }\n      };\n      DOMPurify.removeAllHooks = function () {\n        hooks = {};\n      };\n      return DOMPurify;\n    }\n    var purify = createDOMPurify();\n\n    const sanitizeHtmlString = html => purify().sanitize(html);\n\n    const isTouch = global$5.deviceType.isTouch();\n    const hiddenHeader = (title, close) => ({\n      dom: {\n        tag: 'div',\n        styles: { display: 'none' },\n        classes: ['tox-dialog__header']\n      },\n      components: [\n        title,\n        close\n      ]\n    });\n    const pClose = (onClose, providersBackstage) => ModalDialog.parts.close(Button.sketch({\n      dom: {\n        tag: 'button',\n        classes: [\n          'tox-button',\n          'tox-button--icon',\n          'tox-button--naked'\n        ],\n        attributes: {\n          'type': 'button',\n          'aria-label': providersBackstage.translate('Close')\n        }\n      },\n      action: onClose,\n      buttonBehaviours: derive$1([Tabstopping.config({})])\n    }));\n    const pUntitled = () => ModalDialog.parts.title({\n      dom: {\n        tag: 'div',\n        classes: ['tox-dialog__title'],\n        innerHtml: '',\n        styles: { display: 'none' }\n      }\n    });\n    const pBodyMessage = (message, providersBackstage) => ModalDialog.parts.body({\n      dom: {\n        tag: 'div',\n        classes: ['tox-dialog__body']\n      },\n      components: [{\n          dom: {\n            tag: 'div',\n            classes: ['tox-dialog__body-content']\n          },\n          components: [{ dom: fromHtml(`<p>${ sanitizeHtmlString(providersBackstage.translate(message)) }</p>`) }]\n        }]\n    });\n    const pFooter = buttons => ModalDialog.parts.footer({\n      dom: {\n        tag: 'div',\n        classes: ['tox-dialog__footer']\n      },\n      components: buttons\n    });\n    const pFooterGroup = (startButtons, endButtons) => [\n      Container.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-dialog__footer-start']\n        },\n        components: startButtons\n      }),\n      Container.sketch({\n        dom: {\n          tag: 'div',\n          classes: ['tox-dialog__footer-end']\n        },\n        components: endButtons\n      })\n    ];\n    const renderDialog$1 = spec => {\n      const dialogClass = 'tox-dialog';\n      const blockerClass = dialogClass + '-wrap';\n      const blockerBackdropClass = blockerClass + '__backdrop';\n      const scrollLockClass = dialogClass + '__disable-scroll';\n      return ModalDialog.sketch({\n        lazySink: spec.lazySink,\n        onEscape: comp => {\n          spec.onEscape(comp);\n          return Optional.some(true);\n        },\n        useTabstopAt: elem => !isPseudoStop(elem),\n        dom: {\n          tag: 'div',\n          classes: [dialogClass].concat(spec.extraClasses),\n          styles: {\n            position: 'relative',\n            ...spec.extraStyles\n          }\n        },\n        components: [\n          spec.header,\n          spec.body,\n          ...spec.footer.toArray()\n        ],\n        parts: {\n          blocker: {\n            dom: fromHtml(`<div class=\"${ blockerClass }\"></div>`),\n            components: [{\n                dom: {\n                  tag: 'div',\n                  classes: isTouch ? [\n                    blockerBackdropClass,\n                    blockerBackdropClass + '--opaque'\n                  ] : [blockerBackdropClass]\n                }\n              }]\n          }\n        },\n        dragBlockClass: blockerClass,\n        modalBehaviours: derive$1([\n          Focusing.config({}),\n          config('dialog-events', spec.dialogEvents.concat([runOnSource(focusin(), (comp, _se) => {\n              Keying.focusIn(comp);\n            })])),\n          config('scroll-lock', [\n            runOnAttached(() => {\n              add$2(body(), scrollLockClass);\n            }),\n            runOnDetached(() => {\n              remove$2(body(), scrollLockClass);\n            })\n          ]),\n          ...spec.extraBehaviours\n        ]),\n        eventOrder: {\n          [execute$5()]: ['dialog-events'],\n          [attachedToDom()]: [\n            'scroll-lock',\n            'dialog-events',\n            'alloy.base.behaviour'\n          ],\n          [detachedFromDom()]: [\n            'alloy.base.behaviour',\n            'dialog-events',\n            'scroll-lock'\n          ],\n          ...spec.eventOrder\n        }\n      });\n    };\n\n    const renderClose = providersBackstage => Button.sketch({\n      dom: {\n        tag: 'button',\n        classes: [\n          'tox-button',\n          'tox-button--icon',\n          'tox-button--naked'\n        ],\n        attributes: {\n          'type': 'button',\n          'aria-label': providersBackstage.translate('Close'),\n          'title': providersBackstage.translate('Close')\n        }\n      },\n      components: [render$3('close', {\n          tag: 'div',\n          classes: ['tox-icon']\n        }, providersBackstage.icons)],\n      action: comp => {\n        emit(comp, formCancelEvent);\n      }\n    });\n    const renderTitle = (spec, dialogId, titleId, providersBackstage) => {\n      const renderComponents = data => [text$2(providersBackstage.translate(data.title))];\n      return {\n        dom: {\n          tag: 'div',\n          classes: ['tox-dialog__title'],\n          attributes: { ...titleId.map(x => ({ id: x })).getOr({}) }\n        },\n        components: [],\n        behaviours: derive$1([Reflecting.config({\n            channel: `${ titleChannel }-${ dialogId }`,\n            initialData: spec,\n            renderComponents\n          })])\n      };\n    };\n    const renderDragHandle = () => ({ dom: fromHtml('<div class=\"tox-dialog__draghandle\"></div>') });\n    const renderInlineHeader = (spec, dialogId, titleId, providersBackstage) => Container.sketch({\n      dom: fromHtml('<div class=\"tox-dialog__header\"></div>'),\n      components: [\n        renderTitle(spec, dialogId, Optional.some(titleId), providersBackstage),\n        renderDragHandle(),\n        renderClose(providersBackstage)\n      ],\n      containerBehaviours: derive$1([Dragging.config({\n          mode: 'mouse',\n          blockerClass: 'blocker',\n          getTarget: handle => {\n            return closest$1(handle, '[role=\"dialog\"]').getOrDie();\n          },\n          snaps: {\n            getSnapPoints: () => [],\n            leftAttr: 'data-drag-left',\n            topAttr: 'data-drag-top'\n          }\n        })])\n    });\n    const renderModalHeader = (spec, dialogId, providersBackstage) => {\n      const pTitle = ModalDialog.parts.title(renderTitle(spec, dialogId, Optional.none(), providersBackstage));\n      const pHandle = ModalDialog.parts.draghandle(renderDragHandle());\n      const pClose = ModalDialog.parts.close(renderClose(providersBackstage));\n      const components = [pTitle].concat(spec.draggable ? [pHandle] : []).concat([pClose]);\n      return Container.sketch({\n        dom: fromHtml('<div class=\"tox-dialog__header\"></div>'),\n        components\n      });\n    };\n\n    const getHeader = (title, dialogId, backstage) => renderModalHeader({\n      title: backstage.shared.providers.translate(title),\n      draggable: backstage.dialog.isDraggableModal()\n    }, dialogId, backstage.shared.providers);\n    const getBusySpec = (message, bs, providers) => ({\n      dom: {\n        tag: 'div',\n        classes: ['tox-dialog__busy-spinner'],\n        attributes: { 'aria-label': providers.translate(message) },\n        styles: {\n          left: '0px',\n          right: '0px',\n          bottom: '0px',\n          top: '0px',\n          position: 'absolute'\n        }\n      },\n      behaviours: bs,\n      components: [{ dom: fromHtml('<div class=\"tox-spinner\"><div></div><div></div><div></div></div>') }]\n    });\n    const getEventExtras = (lazyDialog, providers, extra) => ({\n      onClose: () => extra.closeWindow(),\n      onBlock: blockEvent => {\n        ModalDialog.setBusy(lazyDialog(), (_comp, bs) => getBusySpec(blockEvent.message, bs, providers));\n      },\n      onUnblock: () => {\n        ModalDialog.setIdle(lazyDialog());\n      }\n    });\n    const renderModalDialog = (spec, initialData, dialogEvents, backstage) => {\n      const updateState = (_comp, incoming) => Optional.some(incoming);\n      return build$1(renderDialog$1({\n        ...spec,\n        lazySink: backstage.shared.getSink,\n        extraBehaviours: [\n          Reflecting.config({\n            channel: `${ dialogChannel }-${ spec.id }`,\n            updateState,\n            initialData\n          }),\n          RepresentingConfigs.memory({}),\n          ...spec.extraBehaviours\n        ],\n        onEscape: comp => {\n          emit(comp, formCancelEvent);\n        },\n        dialogEvents,\n        eventOrder: {\n          [receive()]: [\n            Reflecting.name(),\n            Receiving.name()\n          ],\n          [attachedToDom()]: [\n            'scroll-lock',\n            Reflecting.name(),\n            'messages',\n            'dialog-events',\n            'alloy.base.behaviour'\n          ],\n          [detachedFromDom()]: [\n            'alloy.base.behaviour',\n            'dialog-events',\n            'messages',\n            Reflecting.name(),\n            'scroll-lock'\n          ]\n        }\n      }));\n    };\n    const mapMenuButtons = buttons => {\n      const mapItems = button => {\n        const items = map$2(button.items, item => {\n          const cell = Cell(false);\n          return {\n            ...item,\n            storage: cell\n          };\n        });\n        return {\n          ...button,\n          items\n        };\n      };\n      return map$2(buttons, button => {\n        return button.type === 'menu' ? mapItems(button) : button;\n      });\n    };\n    const extractCellsToObject = buttons => foldl(buttons, (acc, button) => {\n      if (button.type === 'menu') {\n        const menuButton = button;\n        return foldl(menuButton.items, (innerAcc, item) => {\n          innerAcc[item.name] = item.storage;\n          return innerAcc;\n        }, acc);\n      }\n      return acc;\n    }, {});\n\n    const initCommonEvents = (fireApiEvent, extras) => [\n      runWithTarget(focusin(), onFocus),\n      fireApiEvent(formCloseEvent, (_api, spec) => {\n        extras.onClose();\n        spec.onClose();\n      }),\n      fireApiEvent(formCancelEvent, (api, spec, _event, self) => {\n        spec.onCancel(api);\n        emit(self, formCloseEvent);\n      }),\n      run$1(formUnblockEvent, (_c, _se) => extras.onUnblock()),\n      run$1(formBlockEvent, (_c, se) => extras.onBlock(se.event))\n    ];\n    const initUrlDialog = (getInstanceApi, extras) => {\n      const fireApiEvent = (eventName, f) => run$1(eventName, (c, se) => {\n        withSpec(c, (spec, _c) => {\n          f(getInstanceApi(), spec, se.event, c);\n        });\n      });\n      const withSpec = (c, f) => {\n        Reflecting.getState(c).get().each(currentDialog => {\n          f(currentDialog, c);\n        });\n      };\n      return [\n        ...initCommonEvents(fireApiEvent, extras),\n        fireApiEvent(formActionEvent, (api, spec, event) => {\n          spec.onAction(api, { name: event.name });\n        })\n      ];\n    };\n    const initDialog = (getInstanceApi, extras, getSink) => {\n      const fireApiEvent = (eventName, f) => run$1(eventName, (c, se) => {\n        withSpec(c, (spec, _c) => {\n          f(getInstanceApi(), spec, se.event, c);\n        });\n      });\n      const withSpec = (c, f) => {\n        Reflecting.getState(c).get().each(currentDialogInit => {\n          f(currentDialogInit.internalDialog, c);\n        });\n      };\n      return [\n        ...initCommonEvents(fireApiEvent, extras),\n        fireApiEvent(formSubmitEvent, (api, spec) => spec.onSubmit(api)),\n        fireApiEvent(formChangeEvent, (api, spec, event) => {\n          spec.onChange(api, { name: event.name });\n        }),\n        fireApiEvent(formActionEvent, (api, spec, event, component) => {\n          const focusIn = () => Keying.focusIn(component);\n          const isDisabled = focused => has$1(focused, 'disabled') || getOpt(focused, 'aria-disabled').exists(val => val === 'true');\n          const rootNode = getRootNode(component.element);\n          const current = active$1(rootNode);\n          spec.onAction(api, {\n            name: event.name,\n            value: event.value\n          });\n          active$1(rootNode).fold(focusIn, focused => {\n            if (isDisabled(focused)) {\n              focusIn();\n            } else if (current.exists(cur => contains(focused, cur) && isDisabled(cur))) {\n              focusIn();\n            } else {\n              getSink().toOptional().filter(sink => !contains(sink.element, focused)).each(focusIn);\n            }\n          });\n        }),\n        fireApiEvent(formTabChangeEvent, (api, spec, event) => {\n          spec.onTabChange(api, {\n            newTabName: event.name,\n            oldTabName: event.oldName\n          });\n        }),\n        runOnDetached(component => {\n          const api = getInstanceApi();\n          Representing.setValue(component, api.getData());\n        })\n      ];\n    };\n    const SilverDialogEvents = {\n      initUrlDialog,\n      initDialog\n    };\n\n    const makeButton = (button, backstage) => renderFooterButton(button, button.type, backstage);\n    const lookup = (compInSystem, footerButtons, buttonName) => find$5(footerButtons, button => button.name === buttonName).bind(memButton => memButton.memento.getOpt(compInSystem));\n    const renderComponents = (_data, state) => {\n      const footerButtons = state.map(s => s.footerButtons).getOr([]);\n      const buttonGroups = partition$3(footerButtons, button => button.align === 'start');\n      const makeGroup = (edge, buttons) => Container.sketch({\n        dom: {\n          tag: 'div',\n          classes: [`tox-dialog__footer-${ edge }`]\n        },\n        components: map$2(buttons, button => button.memento.asSpec())\n      });\n      const startButtons = makeGroup('start', buttonGroups.pass);\n      const endButtons = makeGroup('end', buttonGroups.fail);\n      return [\n        startButtons,\n        endButtons\n      ];\n    };\n    const renderFooter = (initSpec, dialogId, backstage) => {\n      const updateState = (comp, data) => {\n        const footerButtons = map$2(data.buttons, button => {\n          const memButton = record(makeButton(button, backstage));\n          return {\n            name: button.name,\n            align: button.align,\n            memento: memButton\n          };\n        });\n        const lookupByName = buttonName => lookup(comp, footerButtons, buttonName);\n        return Optional.some({\n          lookupByName,\n          footerButtons\n        });\n      };\n      return {\n        dom: fromHtml('<div class=\"tox-dialog__footer\"></div>'),\n        components: [],\n        behaviours: derive$1([Reflecting.config({\n            channel: `${ footerChannel }-${ dialogId }`,\n            initialData: initSpec,\n            updateState,\n            renderComponents\n          })])\n      };\n    };\n    const renderInlineFooter = (initSpec, dialogId, backstage) => renderFooter(initSpec, dialogId, backstage);\n    const renderModalFooter = (initSpec, dialogId, backstage) => ModalDialog.parts.footer(renderFooter(initSpec, dialogId, backstage));\n\n    const getCompByName = (access, name) => {\n      const root = access.getRoot();\n      if (root.getSystem().isConnected()) {\n        const form = Composing.getCurrent(access.getFormWrapper()).getOr(access.getFormWrapper());\n        return Form.getField(form, name).orThunk(() => {\n          const footer = access.getFooter();\n          const footerState = Reflecting.getState(footer).get();\n          return footerState.bind(f => f.lookupByName(name));\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n    const validateData$1 = (access, data) => {\n      const root = access.getRoot();\n      return Reflecting.getState(root).get().map(dialogState => getOrDie(asRaw('data', dialogState.dataValidator, data))).getOr(data);\n    };\n    const getDialogApi = (access, doRedial, menuItemStates) => {\n      const withRoot = f => {\n        const root = access.getRoot();\n        if (root.getSystem().isConnected()) {\n          f(root);\n        }\n      };\n      const getData = () => {\n        const root = access.getRoot();\n        const valueComp = root.getSystem().isConnected() ? access.getFormWrapper() : root;\n        const representedValues = Representing.getValue(valueComp);\n        const menuItemCurrentState = map$1(menuItemStates, cell => cell.get());\n        return {\n          ...representedValues,\n          ...menuItemCurrentState\n        };\n      };\n      const setData = newData => {\n        withRoot(_ => {\n          const prevData = instanceApi.getData();\n          const mergedData = deepMerge(prevData, newData);\n          const newInternalData = validateData$1(access, mergedData);\n          const form = access.getFormWrapper();\n          Representing.setValue(form, newInternalData);\n          each(menuItemStates, (v, k) => {\n            if (has$2(mergedData, k)) {\n              v.set(mergedData[k]);\n            }\n          });\n        });\n      };\n      const setEnabled = (name, state) => {\n        getCompByName(access, name).each(state ? Disabling.enable : Disabling.disable);\n      };\n      const focus = name => {\n        getCompByName(access, name).each(Focusing.focus);\n      };\n      const block = message => {\n        if (!isString(message)) {\n          throw new Error('The dialogInstanceAPI.block function should be passed a blocking message of type string as an argument');\n        }\n        withRoot(root => {\n          emitWith(root, formBlockEvent, { message });\n        });\n      };\n      const unblock = () => {\n        withRoot(root => {\n          emit(root, formUnblockEvent);\n        });\n      };\n      const showTab = name => {\n        withRoot(_ => {\n          const body = access.getBody();\n          const bodyState = Reflecting.getState(body);\n          if (bodyState.get().exists(b => b.isTabPanel())) {\n            Composing.getCurrent(body).each(tabSection => {\n              TabSection.showTab(tabSection, name);\n            });\n          }\n        });\n      };\n      const redial = d => {\n        withRoot(root => {\n          const id = access.getId();\n          const dialogInit = doRedial(d);\n          root.getSystem().broadcastOn([`${ dialogChannel }-${ id }`], dialogInit);\n          root.getSystem().broadcastOn([`${ titleChannel }-${ id }`], dialogInit.internalDialog);\n          root.getSystem().broadcastOn([`${ bodyChannel }-${ id }`], dialogInit.internalDialog);\n          root.getSystem().broadcastOn([`${ footerChannel }-${ id }`], dialogInit.internalDialog);\n          instanceApi.setData(dialogInit.initialData);\n        });\n      };\n      const close = () => {\n        withRoot(root => {\n          emit(root, formCloseEvent);\n        });\n      };\n      const instanceApi = {\n        getData,\n        setData,\n        setEnabled,\n        focus,\n        block,\n        unblock,\n        showTab,\n        redial,\n        close\n      };\n      return instanceApi;\n    };\n\n    const getDialogSizeClasses = size => {\n      switch (size) {\n      case 'large':\n        return ['tox-dialog--width-lg'];\n      case 'medium':\n        return ['tox-dialog--width-md'];\n      default:\n        return [];\n      }\n    };\n    const renderDialog = (dialogInit, extra, backstage) => {\n      const dialogId = generate$6('dialog');\n      const internalDialog = dialogInit.internalDialog;\n      const header = getHeader(internalDialog.title, dialogId, backstage);\n      const body = renderModalBody({\n        body: internalDialog.body,\n        initialData: internalDialog.initialData\n      }, dialogId, backstage);\n      const storedMenuButtons = mapMenuButtons(internalDialog.buttons);\n      const objOfCells = extractCellsToObject(storedMenuButtons);\n      const footer = renderModalFooter({ buttons: storedMenuButtons }, dialogId, backstage);\n      const dialogEvents = SilverDialogEvents.initDialog(() => instanceApi, getEventExtras(() => dialog, backstage.shared.providers, extra), backstage.shared.getSink);\n      const dialogSize = getDialogSizeClasses(internalDialog.size);\n      const spec = {\n        id: dialogId,\n        header,\n        body,\n        footer: Optional.some(footer),\n        extraClasses: dialogSize,\n        extraBehaviours: [],\n        extraStyles: {}\n      };\n      const dialog = renderModalDialog(spec, dialogInit, dialogEvents, backstage);\n      const modalAccess = (() => {\n        const getForm = () => {\n          const outerForm = ModalDialog.getBody(dialog);\n          return Composing.getCurrent(outerForm).getOr(outerForm);\n        };\n        return {\n          getId: constant$1(dialogId),\n          getRoot: constant$1(dialog),\n          getBody: () => ModalDialog.getBody(dialog),\n          getFooter: () => ModalDialog.getFooter(dialog),\n          getFormWrapper: getForm\n        };\n      })();\n      const instanceApi = getDialogApi(modalAccess, extra.redial, objOfCells);\n      return {\n        dialog,\n        instanceApi\n      };\n    };\n\n    const renderInlineDialog = (dialogInit, extra, backstage, ariaAttrs) => {\n      const dialogId = generate$6('dialog');\n      const dialogLabelId = generate$6('dialog-label');\n      const dialogContentId = generate$6('dialog-content');\n      const internalDialog = dialogInit.internalDialog;\n      const updateState = (_comp, incoming) => Optional.some(incoming);\n      const memHeader = record(renderInlineHeader({\n        title: internalDialog.title,\n        draggable: true\n      }, dialogId, dialogLabelId, backstage.shared.providers));\n      const memBody = record(renderInlineBody({\n        body: internalDialog.body,\n        initialData: internalDialog.initialData\n      }, dialogId, dialogContentId, backstage, ariaAttrs));\n      const storagedMenuButtons = mapMenuButtons(internalDialog.buttons);\n      const objOfCells = extractCellsToObject(storagedMenuButtons);\n      const memFooter = record(renderInlineFooter({ buttons: storagedMenuButtons }, dialogId, backstage));\n      const dialogEvents = SilverDialogEvents.initDialog(() => instanceApi, {\n        onBlock: event => {\n          Blocking.block(dialog, (_comp, bs) => getBusySpec(event.message, bs, backstage.shared.providers));\n        },\n        onUnblock: () => {\n          Blocking.unblock(dialog);\n        },\n        onClose: () => extra.closeWindow()\n      }, backstage.shared.getSink);\n      const dialog = build$1({\n        dom: {\n          tag: 'div',\n          classes: [\n            'tox-dialog',\n            'tox-dialog-inline'\n          ],\n          attributes: {\n            role: 'dialog',\n            ['aria-labelledby']: dialogLabelId,\n            ['aria-describedby']: dialogContentId\n          }\n        },\n        eventOrder: {\n          [receive()]: [\n            Reflecting.name(),\n            Receiving.name()\n          ],\n          [execute$5()]: ['execute-on-form'],\n          [attachedToDom()]: [\n            'reflecting',\n            'execute-on-form'\n          ]\n        },\n        behaviours: derive$1([\n          Keying.config({\n            mode: 'cyclic',\n            onEscape: c => {\n              emit(c, formCloseEvent);\n              return Optional.some(true);\n            },\n            useTabstopAt: elem => !isPseudoStop(elem) && (name$3(elem) !== 'button' || get$f(elem, 'disabled') !== 'disabled')\n          }),\n          Reflecting.config({\n            channel: `${ dialogChannel }-${ dialogId }`,\n            updateState,\n            initialData: dialogInit\n          }),\n          Focusing.config({}),\n          config('execute-on-form', dialogEvents.concat([runOnSource(focusin(), (comp, _se) => {\n              Keying.focusIn(comp);\n            })])),\n          Blocking.config({ getRoot: () => Optional.some(dialog) }),\n          Replacing.config({}),\n          RepresentingConfigs.memory({})\n        ]),\n        components: [\n          memHeader.asSpec(),\n          memBody.asSpec(),\n          memFooter.asSpec()\n        ]\n      });\n      const instanceApi = getDialogApi({\n        getId: constant$1(dialogId),\n        getRoot: constant$1(dialog),\n        getFooter: () => memFooter.get(dialog),\n        getBody: () => memBody.get(dialog),\n        getFormWrapper: () => {\n          const body = memBody.get(dialog);\n          return Composing.getCurrent(body).getOr(body);\n        }\n      }, extra.redial, objOfCells);\n      return {\n        dialog,\n        instanceApi\n      };\n    };\n\n    var global = tinymce.util.Tools.resolve('tinymce.util.URI');\n\n    const getUrlDialogApi = root => {\n      const withRoot = f => {\n        if (root.getSystem().isConnected()) {\n          f(root);\n        }\n      };\n      const block = message => {\n        if (!isString(message)) {\n          throw new Error('The urlDialogInstanceAPI.block function should be passed a blocking message of type string as an argument');\n        }\n        withRoot(root => {\n          emitWith(root, formBlockEvent, { message });\n        });\n      };\n      const unblock = () => {\n        withRoot(root => {\n          emit(root, formUnblockEvent);\n        });\n      };\n      const close = () => {\n        withRoot(root => {\n          emit(root, formCloseEvent);\n        });\n      };\n      const sendMessage = data => {\n        withRoot(root => {\n          root.getSystem().broadcastOn([bodySendMessageChannel], data);\n        });\n      };\n      return {\n        block,\n        unblock,\n        close,\n        sendMessage\n      };\n    };\n\n    const SUPPORTED_MESSAGE_ACTIONS = [\n      'insertContent',\n      'setContent',\n      'execCommand',\n      'close',\n      'block',\n      'unblock'\n    ];\n    const isSupportedMessage = data => isObject(data) && SUPPORTED_MESSAGE_ACTIONS.indexOf(data.mceAction) !== -1;\n    const isCustomMessage = data => !isSupportedMessage(data) && isObject(data) && has$2(data, 'mceAction');\n    const handleMessage = (editor, api, data) => {\n      switch (data.mceAction) {\n      case 'insertContent':\n        editor.insertContent(data.content);\n        break;\n      case 'setContent':\n        editor.setContent(data.content);\n        break;\n      case 'execCommand':\n        const ui = isBoolean(data.ui) ? data.ui : false;\n        editor.execCommand(data.cmd, ui, data.value);\n        break;\n      case 'close':\n        api.close();\n        break;\n      case 'block':\n        api.block(data.message);\n        break;\n      case 'unblock':\n        api.unblock();\n        break;\n      }\n    };\n    const renderUrlDialog = (internalDialog, extra, editor, backstage) => {\n      const dialogId = generate$6('dialog');\n      const header = getHeader(internalDialog.title, dialogId, backstage);\n      const body = renderIframeBody(internalDialog);\n      const footer = internalDialog.buttons.bind(buttons => {\n        if (buttons.length === 0) {\n          return Optional.none();\n        } else {\n          return Optional.some(renderModalFooter({ buttons }, dialogId, backstage));\n        }\n      });\n      const dialogEvents = SilverDialogEvents.initUrlDialog(() => instanceApi, getEventExtras(() => dialog, backstage.shared.providers, extra));\n      const styles = {\n        ...internalDialog.height.fold(() => ({}), height => ({\n          'height': height + 'px',\n          'max-height': height + 'px'\n        })),\n        ...internalDialog.width.fold(() => ({}), width => ({\n          'width': width + 'px',\n          'max-width': width + 'px'\n        }))\n      };\n      const classes = internalDialog.width.isNone() && internalDialog.height.isNone() ? ['tox-dialog--width-lg'] : [];\n      const iframeUri = new global(internalDialog.url, { base_uri: new global(window.location.href) });\n      const iframeDomain = `${ iframeUri.protocol }://${ iframeUri.host }${ iframeUri.port ? ':' + iframeUri.port : '' }`;\n      const messageHandlerUnbinder = unbindable();\n      const extraBehaviours = [\n        config('messages', [\n          runOnAttached(() => {\n            const unbind = bind(SugarElement.fromDom(window), 'message', e => {\n              if (iframeUri.isSameOrigin(new global(e.raw.origin))) {\n                const data = e.raw.data;\n                if (isSupportedMessage(data)) {\n                  handleMessage(editor, instanceApi, data);\n                } else if (isCustomMessage(data)) {\n                  internalDialog.onMessage(instanceApi, data);\n                }\n              }\n            });\n            messageHandlerUnbinder.set(unbind);\n          }),\n          runOnDetached(messageHandlerUnbinder.clear)\n        ]),\n        Receiving.config({\n          channels: {\n            [bodySendMessageChannel]: {\n              onReceive: (comp, data) => {\n                descendant(comp.element, 'iframe').each(iframeEle => {\n                  const iframeWin = iframeEle.dom.contentWindow;\n                  if (isNonNullable(iframeWin)) {\n                    iframeWin.postMessage(data, iframeDomain);\n                  }\n                });\n              }\n            }\n          }\n        })\n      ];\n      const spec = {\n        id: dialogId,\n        header,\n        body,\n        footer,\n        extraClasses: classes,\n        extraBehaviours,\n        extraStyles: styles\n      };\n      const dialog = renderModalDialog(spec, internalDialog, dialogEvents, backstage);\n      const instanceApi = getUrlDialogApi(dialog);\n      return {\n        dialog,\n        instanceApi\n      };\n    };\n\n    const setup$2 = backstage => {\n      const sharedBackstage = backstage.shared;\n      const open = (message, callback) => {\n        const closeDialog = () => {\n          ModalDialog.hide(alertDialog);\n          callback();\n        };\n        const memFooterClose = record(renderFooterButton({\n          name: 'close-alert',\n          text: 'OK',\n          primary: true,\n          buttonType: Optional.some('primary'),\n          align: 'end',\n          enabled: true,\n          icon: Optional.none()\n        }, 'cancel', backstage));\n        const titleSpec = pUntitled();\n        const closeSpec = pClose(closeDialog, sharedBackstage.providers);\n        const alertDialog = build$1(renderDialog$1({\n          lazySink: () => sharedBackstage.getSink(),\n          header: hiddenHeader(titleSpec, closeSpec),\n          body: pBodyMessage(message, sharedBackstage.providers),\n          footer: Optional.some(pFooter(pFooterGroup([], [memFooterClose.asSpec()]))),\n          onEscape: closeDialog,\n          extraClasses: ['tox-alert-dialog'],\n          extraBehaviours: [],\n          extraStyles: {},\n          dialogEvents: [run$1(formCancelEvent, closeDialog)],\n          eventOrder: {}\n        }));\n        ModalDialog.show(alertDialog);\n        const footerCloseButton = memFooterClose.get(alertDialog);\n        Focusing.focus(footerCloseButton);\n      };\n      return { open };\n    };\n\n    const setup$1 = backstage => {\n      const sharedBackstage = backstage.shared;\n      const open = (message, callback) => {\n        const closeDialog = state => {\n          ModalDialog.hide(confirmDialog);\n          callback(state);\n        };\n        const memFooterYes = record(renderFooterButton({\n          name: 'yes',\n          text: 'Yes',\n          primary: true,\n          buttonType: Optional.some('primary'),\n          align: 'end',\n          enabled: true,\n          icon: Optional.none()\n        }, 'submit', backstage));\n        const footerNo = renderFooterButton({\n          name: 'no',\n          text: 'No',\n          primary: false,\n          buttonType: Optional.some('secondary'),\n          align: 'end',\n          enabled: true,\n          icon: Optional.none()\n        }, 'cancel', backstage);\n        const titleSpec = pUntitled();\n        const closeSpec = pClose(() => closeDialog(false), sharedBackstage.providers);\n        const confirmDialog = build$1(renderDialog$1({\n          lazySink: () => sharedBackstage.getSink(),\n          header: hiddenHeader(titleSpec, closeSpec),\n          body: pBodyMessage(message, sharedBackstage.providers),\n          footer: Optional.some(pFooter(pFooterGroup([], [\n            footerNo,\n            memFooterYes.asSpec()\n          ]))),\n          onEscape: () => closeDialog(false),\n          extraClasses: ['tox-confirm-dialog'],\n          extraBehaviours: [],\n          extraStyles: {},\n          dialogEvents: [\n            run$1(formCancelEvent, () => closeDialog(false)),\n            run$1(formSubmitEvent, () => closeDialog(true))\n          ],\n          eventOrder: {}\n        }));\n        ModalDialog.show(confirmDialog);\n        const footerYesButton = memFooterYes.get(confirmDialog);\n        Focusing.focus(footerYesButton);\n      };\n      return { open };\n    };\n\n    const validateData = (data, validator) => getOrDie(asRaw('data', validator, data));\n    const isAlertOrConfirmDialog = target => closest(target, '.tox-alert-dialog') || closest(target, '.tox-confirm-dialog');\n    const inlineAdditionalBehaviours = (editor, isStickyToolbar, isToolbarLocationTop) => {\n      if (isStickyToolbar && isToolbarLocationTop) {\n        return [];\n      } else {\n        return [Docking.config({\n            contextual: {\n              lazyContext: () => Optional.some(box$1(SugarElement.fromDom(editor.getContentAreaContainer()))),\n              fadeInClass: 'tox-dialog-dock-fadein',\n              fadeOutClass: 'tox-dialog-dock-fadeout',\n              transitionClass: 'tox-dialog-dock-transition'\n            },\n            modes: ['top']\n          })];\n      }\n    };\n    const setup = extras => {\n      const editor = extras.editor;\n      const isStickyToolbar$1 = isStickyToolbar(editor);\n      const alertDialog = setup$2(extras.backstages.dialog);\n      const confirmDialog = setup$1(extras.backstages.dialog);\n      const open = (config, params, closeWindow) => {\n        if (params !== undefined && params.inline === 'toolbar') {\n          return openInlineDialog(config, extras.backstages.popup.shared.anchors.inlineDialog(), closeWindow, params.ariaAttrs);\n        } else if (params !== undefined && params.inline === 'cursor') {\n          return openInlineDialog(config, extras.backstages.popup.shared.anchors.cursor(), closeWindow, params.ariaAttrs);\n        } else {\n          return openModalDialog(config, closeWindow);\n        }\n      };\n      const openUrl = (config, closeWindow) => openModalUrlDialog(config, closeWindow);\n      const openModalUrlDialog = (config, closeWindow) => {\n        const factory = contents => {\n          const dialog = renderUrlDialog(contents, {\n            closeWindow: () => {\n              ModalDialog.hide(dialog.dialog);\n              closeWindow(dialog.instanceApi);\n            }\n          }, editor, extras.backstages.dialog);\n          ModalDialog.show(dialog.dialog);\n          return dialog.instanceApi;\n        };\n        return DialogManager.openUrl(factory, config);\n      };\n      const openModalDialog = (config, closeWindow) => {\n        const factory = (contents, internalInitialData, dataValidator) => {\n          const initialData = internalInitialData;\n          const dialogInit = {\n            dataValidator,\n            initialData,\n            internalDialog: contents\n          };\n          const dialog = renderDialog(dialogInit, {\n            redial: DialogManager.redial,\n            closeWindow: () => {\n              ModalDialog.hide(dialog.dialog);\n              closeWindow(dialog.instanceApi);\n            }\n          }, extras.backstages.dialog);\n          ModalDialog.show(dialog.dialog);\n          dialog.instanceApi.setData(initialData);\n          return dialog.instanceApi;\n        };\n        return DialogManager.open(factory, config);\n      };\n      const openInlineDialog = (config$1, anchor, closeWindow, ariaAttrs = false) => {\n        const factory = (contents, internalInitialData, dataValidator) => {\n          const initialData = validateData(internalInitialData, dataValidator);\n          const inlineDialog = value$2();\n          const isToolbarLocationTop = extras.backstages.popup.shared.header.isPositionedAtTop();\n          const dialogInit = {\n            dataValidator,\n            initialData,\n            internalDialog: contents\n          };\n          const refreshDocking = () => inlineDialog.on(dialog => {\n            InlineView.reposition(dialog);\n            Docking.refresh(dialog);\n          });\n          const dialogUi = renderInlineDialog(dialogInit, {\n            redial: DialogManager.redial,\n            closeWindow: () => {\n              inlineDialog.on(InlineView.hide);\n              editor.off('ResizeEditor', refreshDocking);\n              inlineDialog.clear();\n              closeWindow(dialogUi.instanceApi);\n            }\n          }, extras.backstages.popup, ariaAttrs);\n          const inlineDialogComp = build$1(InlineView.sketch({\n            lazySink: extras.backstages.popup.shared.getSink,\n            dom: {\n              tag: 'div',\n              classes: []\n            },\n            fireDismissalEventInstead: {},\n            ...isToolbarLocationTop ? {} : { fireRepositionEventInstead: {} },\n            inlineBehaviours: derive$1([\n              config('window-manager-inline-events', [run$1(dismissRequested(), (_comp, _se) => {\n                  emit(dialogUi.dialog, formCancelEvent);\n                })]),\n              ...inlineAdditionalBehaviours(editor, isStickyToolbar$1, isToolbarLocationTop)\n            ]),\n            isExtraPart: (_comp, target) => isAlertOrConfirmDialog(target)\n          }));\n          inlineDialog.set(inlineDialogComp);\n          InlineView.showWithin(inlineDialogComp, premade(dialogUi.dialog), { anchor }, Optional.some(body()));\n          if (!isStickyToolbar$1 || !isToolbarLocationTop) {\n            Docking.refresh(inlineDialogComp);\n            editor.on('ResizeEditor', refreshDocking);\n          }\n          dialogUi.instanceApi.setData(initialData);\n          Keying.focusIn(dialogUi.dialog);\n          return dialogUi.instanceApi;\n        };\n        return DialogManager.open(factory, config$1);\n      };\n      const confirm = (message, callback) => {\n        confirmDialog.open(message, callback);\n      };\n      const alert = (message, callback) => {\n        alertDialog.open(message, callback);\n      };\n      const close = instanceApi => {\n        instanceApi.close();\n      };\n      return {\n        open,\n        openUrl,\n        alert,\n        close,\n        confirm\n      };\n    };\n\n    const registerOptions = editor => {\n      register$e(editor);\n      register$d(editor);\n      register(editor);\n    };\n    var Theme = () => {\n      global$a.add('silver', editor => {\n        registerOptions(editor);\n        const {dialogs, popups, renderUI} = setup$3(editor);\n        Autocompleter.register(editor, popups.backstage.shared);\n        const windowMgr = setup({\n          editor,\n          backstages: {\n            popup: popups.backstage,\n            dialog: dialogs.backstage\n          }\n        });\n        const getNotificationManagerImpl = () => NotificationManagerImpl(editor, { backstage: popups.backstage }, popups.getMothership());\n        return {\n          renderUI,\n          getWindowManagerImpl: constant$1(windowMgr),\n          getNotificationManagerImpl\n        };\n      });\n    };\n\n    Theme();\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/tinymce.d.ts",
    "content": "interface StringPathBookmark {\n    start: string;\n    end?: string;\n    forward?: boolean;\n}\ninterface RangeBookmark {\n    rng: Range;\n    forward?: boolean;\n}\ninterface IdBookmark {\n    id: string;\n    keep?: boolean;\n    forward?: boolean;\n}\ninterface IndexBookmark {\n    name: string;\n    index: number;\n}\ninterface PathBookmark {\n    start: number[];\n    end?: number[];\n    isFakeCaret?: boolean;\n    forward?: boolean;\n}\ndeclare type Bookmark = StringPathBookmark | RangeBookmark | IdBookmark | IndexBookmark | PathBookmark;\ndeclare type NormalizedEvent<E, T = any> = E & {\n    readonly type: string;\n    readonly target: T;\n    readonly isDefaultPrevented: () => boolean;\n    readonly preventDefault: () => void;\n    readonly isPropagationStopped: () => boolean;\n    readonly stopPropagation: () => void;\n    readonly isImmediatePropagationStopped: () => boolean;\n    readonly stopImmediatePropagation: () => void;\n};\ndeclare type MappedEvent<T extends {}, K extends string> = K extends keyof T ? T[K] : any;\ninterface NativeEventMap {\n    'beforepaste': Event;\n    'blur': FocusEvent;\n    'beforeinput': InputEvent;\n    'click': MouseEvent;\n    'compositionend': Event;\n    'compositionstart': Event;\n    'compositionupdate': Event;\n    'contextmenu': PointerEvent;\n    'copy': ClipboardEvent;\n    'cut': ClipboardEvent;\n    'dblclick': MouseEvent;\n    'drag': DragEvent;\n    'dragdrop': DragEvent;\n    'dragend': DragEvent;\n    'draggesture': DragEvent;\n    'dragover': DragEvent;\n    'dragstart': DragEvent;\n    'drop': DragEvent;\n    'focus': FocusEvent;\n    'focusin': FocusEvent;\n    'focusout': FocusEvent;\n    'input': InputEvent;\n    'keydown': KeyboardEvent;\n    'keypress': KeyboardEvent;\n    'keyup': KeyboardEvent;\n    'mousedown': MouseEvent;\n    'mouseenter': MouseEvent;\n    'mouseleave': MouseEvent;\n    'mousemove': MouseEvent;\n    'mouseout': MouseEvent;\n    'mouseover': MouseEvent;\n    'mouseup': MouseEvent;\n    'paste': ClipboardEvent;\n    'selectionchange': Event;\n    'submit': Event;\n    'touchend': TouchEvent;\n    'touchmove': TouchEvent;\n    'touchstart': TouchEvent;\n    'touchcancel': TouchEvent;\n    'wheel': WheelEvent;\n}\ndeclare type EditorEvent<T> = NormalizedEvent<T>;\ninterface EventDispatcherSettings {\n    scope?: any;\n    toggleEvent?: (name: string, state: boolean) => void | boolean;\n    beforeFire?: <T>(args: EditorEvent<T>) => void;\n}\ninterface EventDispatcherConstructor<T extends {}> {\n    readonly prototype: EventDispatcher<T>;\n    new (settings?: EventDispatcherSettings): EventDispatcher<T>;\n    isNative: (name: string) => boolean;\n}\ndeclare class EventDispatcher<T extends {}> {\n    static isNative(name: string): boolean;\n    private readonly settings;\n    private readonly scope;\n    private readonly toggleEvent;\n    private bindings;\n    constructor(settings?: EventDispatcherSettings);\n    fire<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U): EditorEvent<U>;\n    dispatch<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U): EditorEvent<U>;\n    on<K extends string>(name: K, callback: false | ((event: EditorEvent<MappedEvent<T, K>>) => void | boolean), prepend?: boolean, extra?: {}): this;\n    off<K extends string>(name?: K, callback?: (event: EditorEvent<MappedEvent<T, K>>) => void): this;\n    once<K extends string>(name: K, callback: (event: EditorEvent<MappedEvent<T, K>>) => void, prepend?: boolean): this;\n    has(name: string): boolean;\n}\ndeclare const enum UndoLevelType {\n    Fragmented = \"fragmented\",\n    Complete = \"complete\"\n}\ninterface BaseUndoLevel {\n    type: UndoLevelType;\n    bookmark: Bookmark | null;\n    beforeBookmark: Bookmark | null;\n}\ninterface FragmentedUndoLevel extends BaseUndoLevel {\n    type: UndoLevelType.Fragmented;\n    fragments: string[];\n    content: '';\n}\ninterface CompleteUndoLevel extends BaseUndoLevel {\n    type: UndoLevelType.Complete;\n    fragments: null;\n    content: string;\n}\ndeclare type NewUndoLevel = CompleteUndoLevel | FragmentedUndoLevel;\ndeclare type UndoLevel = NewUndoLevel & {\n    bookmark: Bookmark;\n};\ninterface UndoManager {\n    data: UndoLevel[];\n    typing: boolean;\n    add: (level?: Partial<UndoLevel>, event?: EditorEvent<any>) => UndoLevel | null;\n    dispatchChange: () => void;\n    beforeChange: () => void;\n    undo: () => UndoLevel | undefined;\n    redo: () => UndoLevel | undefined;\n    clear: () => void;\n    reset: () => void;\n    hasUndo: () => boolean;\n    hasRedo: () => boolean;\n    transact: (callback: () => void) => UndoLevel | null;\n    ignore: (callback: () => void) => void;\n    extra: (callback1: () => void, callback2: () => void) => void;\n}\ndeclare type SchemaType = 'html4' | 'html5' | 'html5-strict';\ninterface ElementSettings {\n    block_elements?: string;\n    boolean_attributes?: string;\n    move_caret_before_on_enter_elements?: string;\n    non_empty_elements?: string;\n    self_closing_elements?: string;\n    text_block_elements?: string;\n    text_inline_elements?: string;\n    void_elements?: string;\n    whitespace_elements?: string;\n    transparent_elements?: string;\n}\ninterface SchemaSettings extends ElementSettings {\n    custom_elements?: string;\n    extended_valid_elements?: string;\n    invalid_elements?: string;\n    invalid_styles?: string | Record<string, string>;\n    schema?: SchemaType;\n    valid_children?: string;\n    valid_classes?: string | Record<string, string>;\n    valid_elements?: string;\n    valid_styles?: string | Record<string, string>;\n    verify_html?: boolean;\n    padd_empty_block_inline_children?: boolean;\n}\ninterface Attribute {\n    required?: boolean;\n    defaultValue?: string;\n    forcedValue?: string;\n    validValues?: Record<string, {}>;\n}\ninterface DefaultAttribute {\n    name: string;\n    value: string;\n}\ninterface AttributePattern extends Attribute {\n    pattern: RegExp;\n}\ninterface ElementRule {\n    attributes: Record<string, Attribute>;\n    attributesDefault?: DefaultAttribute[];\n    attributesForced?: DefaultAttribute[];\n    attributesOrder: string[];\n    attributePatterns?: AttributePattern[];\n    attributesRequired?: string[];\n    paddEmpty?: boolean;\n    removeEmpty?: boolean;\n    removeEmptyAttrs?: boolean;\n    paddInEmptyBlock?: boolean;\n}\ninterface SchemaElement extends ElementRule {\n    outputName?: string;\n    parentsRequired?: string[];\n    pattern?: RegExp;\n}\ninterface SchemaMap {\n    [name: string]: {};\n}\ninterface SchemaRegExpMap {\n    [name: string]: RegExp;\n}\ninterface Schema {\n    type: SchemaType;\n    children: Record<string, SchemaMap>;\n    elements: Record<string, SchemaElement>;\n    getValidStyles: () => Record<string, string[]> | undefined;\n    getValidClasses: () => Record<string, SchemaMap> | undefined;\n    getBlockElements: () => SchemaMap;\n    getInvalidStyles: () => Record<string, SchemaMap> | undefined;\n    getVoidElements: () => SchemaMap;\n    getTextBlockElements: () => SchemaMap;\n    getTextInlineElements: () => SchemaMap;\n    getBoolAttrs: () => SchemaMap;\n    getElementRule: (name: string) => SchemaElement | undefined;\n    getSelfClosingElements: () => SchemaMap;\n    getNonEmptyElements: () => SchemaMap;\n    getMoveCaretBeforeOnEnterElements: () => SchemaMap;\n    getWhitespaceElements: () => SchemaMap;\n    getTransparentElements: () => SchemaMap;\n    getSpecialElements: () => SchemaRegExpMap;\n    isValidChild: (name: string, child: string) => boolean;\n    isValid: (name: string, attr?: string) => boolean;\n    getCustomElements: () => SchemaMap;\n    addValidElements: (validElements: string) => void;\n    setValidElements: (validElements: string) => void;\n    addCustomElements: (customElements: string) => void;\n    addValidChildren: (validChildren: any) => void;\n}\ndeclare type Attributes$1 = Array<{\n    name: string;\n    value: string;\n}> & {\n    map: Record<string, string>;\n};\ninterface AstNodeConstructor {\n    readonly prototype: AstNode;\n    new (name: string, type: number): AstNode;\n    create(name: string, attrs?: Record<string, string>): AstNode;\n}\ndeclare class AstNode {\n    static create(name: string, attrs?: Record<string, string>): AstNode;\n    name: string;\n    type: number;\n    attributes?: Attributes$1;\n    value?: string;\n    parent?: AstNode | null;\n    firstChild?: AstNode | null;\n    lastChild?: AstNode | null;\n    next?: AstNode | null;\n    prev?: AstNode | null;\n    raw?: boolean;\n    constructor(name: string, type: number);\n    replace(node: AstNode): AstNode;\n    attr(name: string, value: string | null | undefined): AstNode | undefined;\n    attr(name: Record<string, string | null | undefined> | undefined): AstNode | undefined;\n    attr(name: string): string | undefined;\n    clone(): AstNode;\n    wrap(wrapper: AstNode): AstNode;\n    unwrap(): void;\n    remove(): AstNode;\n    append(node: AstNode): AstNode;\n    insert(node: AstNode, refNode: AstNode, before?: boolean): AstNode;\n    getAll(name: string): AstNode[];\n    children(): AstNode[];\n    empty(): AstNode;\n    isEmpty(elements: SchemaMap, whitespace?: SchemaMap, predicate?: (node: AstNode) => boolean): boolean;\n    walk(prev?: boolean): AstNode | null | undefined;\n}\ndeclare type Content = string | AstNode;\ndeclare type ContentFormat = 'raw' | 'text' | 'html' | 'tree';\ninterface GetContentArgs {\n    format: ContentFormat;\n    get: boolean;\n    getInner: boolean;\n    no_events?: boolean;\n    save?: boolean;\n    source_view?: boolean;\n    [key: string]: any;\n}\ninterface SetContentArgs {\n    format: string;\n    set: boolean;\n    content: Content;\n    no_events?: boolean;\n    no_selection?: boolean;\n    paste?: boolean;\n    load?: boolean;\n    initial?: boolean;\n    [key: string]: any;\n}\ninterface GetSelectionContentArgs extends GetContentArgs {\n    selection?: boolean;\n    contextual?: boolean;\n}\ninterface SetSelectionContentArgs extends SetContentArgs {\n    content: string;\n    selection?: boolean;\n}\ninterface BlobInfoData {\n    id?: string;\n    name?: string;\n    filename?: string;\n    blob: Blob;\n    base64: string;\n    blobUri?: string;\n    uri?: string;\n}\ninterface BlobInfo {\n    id: () => string;\n    name: () => string;\n    filename: () => string;\n    blob: () => Blob;\n    base64: () => string;\n    blobUri: () => string;\n    uri: () => string | undefined;\n}\ninterface BlobCache {\n    create: {\n        (o: BlobInfoData): BlobInfo;\n        (id: string, blob: Blob, base64: string, name?: string, filename?: string): BlobInfo;\n    };\n    add: (blobInfo: BlobInfo) => void;\n    get: (id: string) => BlobInfo | undefined;\n    getByUri: (blobUri: string) => BlobInfo | undefined;\n    getByData: (base64: string, type: string) => BlobInfo | undefined;\n    findFirst: (predicate: (blobInfo: BlobInfo) => boolean) => BlobInfo | undefined;\n    removeByUri: (blobUri: string) => void;\n    destroy: () => void;\n}\ninterface BlobInfoImagePair {\n    image: HTMLImageElement;\n    blobInfo: BlobInfo;\n}\ndeclare class NodeChange {\n    private readonly editor;\n    private lastPath;\n    constructor(editor: Editor);\n    nodeChanged(args?: Record<string, any>): void;\n    private isSameElementPath;\n}\ninterface SelectionOverrides {\n    showCaret: (direction: number, node: HTMLElement, before: boolean, scrollIntoView?: boolean) => Range | null;\n    showBlockCaretContainer: (blockCaretContainer: HTMLElement) => void;\n    hideFakeCaret: () => void;\n    destroy: () => void;\n}\ninterface Quirks {\n    refreshContentEditable(): void;\n    isHidden(): boolean;\n}\ndeclare type DecoratorData = Record<string, any>;\ndeclare type Decorator = (uid: string, data: DecoratorData) => {\n    attributes?: {};\n    classes?: string[];\n};\ndeclare type AnnotationListener = (state: boolean, name: string, data?: {\n    uid: string;\n    nodes: any[];\n}) => void;\ndeclare type AnnotationListenerApi = AnnotationListener;\ninterface AnnotatorSettings {\n    decorate: Decorator;\n    persistent?: boolean;\n}\ninterface Annotator {\n    register: (name: string, settings: AnnotatorSettings) => void;\n    annotate: (name: string, data: DecoratorData) => void;\n    annotationChanged: (name: string, f: AnnotationListenerApi) => void;\n    remove: (name: string) => void;\n    removeAll: (name: string) => void;\n    getAll: (name: string) => Record<string, Element[]>;\n}\ninterface GeomRect {\n    readonly x: number;\n    readonly y: number;\n    readonly w: number;\n    readonly h: number;\n}\ninterface Rect {\n    inflate: (rect: GeomRect, w: number, h: number) => GeomRect;\n    relativePosition: (rect: GeomRect, targetRect: GeomRect, rel: string) => GeomRect;\n    findBestRelativePosition: (rect: GeomRect, targetRect: GeomRect, constrainRect: GeomRect, rels: string[]) => string | null;\n    intersect: (rect: GeomRect, cropRect: GeomRect) => GeomRect | null;\n    clamp: (rect: GeomRect, clampRect: GeomRect, fixedSize?: boolean) => GeomRect;\n    create: (x: number, y: number, w: number, h: number) => GeomRect;\n    fromClientRect: (clientRect: DOMRect) => GeomRect;\n}\ninterface NotificationManagerImpl {\n    open: (spec: NotificationSpec, closeCallback: () => void) => NotificationApi;\n    close: <T extends NotificationApi>(notification: T) => void;\n    getArgs: <T extends NotificationApi>(notification: T) => NotificationSpec;\n}\ninterface NotificationSpec {\n    type?: 'info' | 'warning' | 'error' | 'success';\n    text: string;\n    icon?: string;\n    progressBar?: boolean;\n    timeout?: number;\n    closeButton?: boolean;\n}\ninterface NotificationApi {\n    close: () => void;\n    progressBar: {\n        value: (percent: number) => void;\n    };\n    text: (text: string) => void;\n    reposition: () => void;\n    getEl: () => HTMLElement;\n    settings: NotificationSpec;\n}\ninterface NotificationManager {\n    open: (spec: NotificationSpec) => NotificationApi;\n    close: () => void;\n    getNotifications: () => NotificationApi[];\n}\ninterface UploadFailure {\n    message: string;\n    remove?: boolean;\n}\ndeclare type ProgressFn = (percent: number) => void;\ndeclare type UploadHandler = (blobInfo: BlobInfo, progress: ProgressFn) => Promise<string>;\ninterface UploadResult$2 {\n    url: string;\n    blobInfo: BlobInfo;\n    status: boolean;\n    error?: UploadFailure;\n}\ninterface RawPattern {\n    start?: any;\n    end?: any;\n    format?: any;\n    cmd?: any;\n    value?: any;\n    replacement?: any;\n}\ninterface InlineBasePattern {\n    readonly start: string;\n    readonly end: string;\n}\ninterface InlineFormatPattern extends InlineBasePattern {\n    readonly type: 'inline-format';\n    readonly format: string[];\n}\ninterface InlineCmdPattern extends InlineBasePattern {\n    readonly type: 'inline-command';\n    readonly cmd: string;\n    readonly value?: any;\n}\ndeclare type InlinePattern = InlineFormatPattern | InlineCmdPattern;\ninterface BlockBasePattern {\n    readonly start: string;\n}\ninterface BlockFormatPattern extends BlockBasePattern {\n    readonly type: 'block-format';\n    readonly format: string;\n}\ninterface BlockCmdPattern extends BlockBasePattern {\n    readonly type: 'block-command';\n    readonly cmd: string;\n    readonly value?: any;\n}\ndeclare type BlockPattern = BlockFormatPattern | BlockCmdPattern;\ndeclare type Pattern = InlinePattern | BlockPattern;\ninterface DynamicPatternContext {\n    readonly text: string;\n    readonly block: Element;\n}\ndeclare type DynamicPatternsLookup = (ctx: DynamicPatternContext) => Pattern[];\ndeclare type RawDynamicPatternsLookup = (ctx: DynamicPatternContext) => RawPattern[];\ninterface AlertBannerSpec {\n    type: 'alertbanner';\n    level: 'info' | 'warn' | 'error' | 'success';\n    text: string;\n    icon: string;\n    url?: string;\n}\ninterface ButtonSpec {\n    type: 'button';\n    text: string;\n    enabled?: boolean;\n    primary?: boolean;\n    name?: string;\n    icon?: string;\n    borderless?: boolean;\n    buttonType?: 'primary' | 'secondary' | 'toolbar';\n}\ninterface FormComponentSpec {\n    type: string;\n    name: string;\n}\ninterface FormComponentWithLabelSpec extends FormComponentSpec {\n    label?: string;\n}\ninterface CheckboxSpec extends FormComponentSpec {\n    type: 'checkbox';\n    label: string;\n    enabled?: boolean;\n}\ninterface CollectionSpec extends FormComponentWithLabelSpec {\n    type: 'collection';\n}\ninterface CollectionItem {\n    value: string;\n    text: string;\n    icon: string;\n}\ninterface ColorInputSpec extends FormComponentWithLabelSpec {\n    type: 'colorinput';\n    storageKey?: string;\n}\ninterface ColorPickerSpec extends FormComponentWithLabelSpec {\n    type: 'colorpicker';\n}\ninterface CustomEditorInit {\n    setValue: (value: string) => void;\n    getValue: () => string;\n    destroy: () => void;\n}\ndeclare type CustomEditorInitFn = (elm: HTMLElement, settings: any) => Promise<CustomEditorInit>;\ninterface CustomEditorOldSpec extends FormComponentSpec {\n    type: 'customeditor';\n    tag?: string;\n    init: (e: HTMLElement) => Promise<CustomEditorInit>;\n}\ninterface CustomEditorNewSpec extends FormComponentSpec {\n    type: 'customeditor';\n    tag?: string;\n    scriptId: string;\n    scriptUrl: string;\n    settings?: any;\n}\ndeclare type CustomEditorSpec = CustomEditorOldSpec | CustomEditorNewSpec;\ninterface DropZoneSpec extends FormComponentWithLabelSpec {\n    type: 'dropzone';\n}\ninterface GridSpec {\n    type: 'grid';\n    columns: number;\n    items: BodyComponentSpec[];\n}\ninterface HtmlPanelSpec {\n    type: 'htmlpanel';\n    html: string;\n    presets?: 'presentation' | 'document';\n}\ninterface IframeSpec extends FormComponentWithLabelSpec {\n    type: 'iframe';\n    sandboxed?: boolean;\n    transparent?: boolean;\n}\ninterface ImagePreviewSpec extends FormComponentSpec {\n    type: 'imagepreview';\n    height?: string;\n}\ninterface InputSpec extends FormComponentWithLabelSpec {\n    type: 'input';\n    inputMode?: string;\n    placeholder?: string;\n    maximized?: boolean;\n    enabled?: boolean;\n}\ninterface LabelSpec {\n    type: 'label';\n    label: string;\n    items: BodyComponentSpec[];\n}\ninterface ListBoxSingleItemSpec {\n    text: string;\n    value: string;\n}\ninterface ListBoxNestedItemSpec {\n    text: string;\n    items: ListBoxItemSpec[];\n}\ndeclare type ListBoxItemSpec = ListBoxNestedItemSpec | ListBoxSingleItemSpec;\ninterface ListBoxSpec extends FormComponentWithLabelSpec {\n    type: 'listbox';\n    items: ListBoxItemSpec[];\n    disabled?: boolean;\n}\ninterface PanelSpec {\n    type: 'panel';\n    classes?: string[];\n    items: BodyComponentSpec[];\n}\ninterface SelectBoxItemSpec {\n    text: string;\n    value: string;\n}\ninterface SelectBoxSpec extends FormComponentWithLabelSpec {\n    type: 'selectbox';\n    items: SelectBoxItemSpec[];\n    size?: number;\n    enabled?: boolean;\n}\ninterface SizeInputSpec extends FormComponentWithLabelSpec {\n    type: 'sizeinput';\n    constrain?: boolean;\n    enabled?: boolean;\n}\ninterface SliderSpec extends FormComponentSpec {\n    type: 'slider';\n    label: string;\n    min?: number;\n    max?: number;\n}\ninterface TableSpec {\n    type: 'table';\n    header: string[];\n    cells: string[][];\n}\ninterface TextAreaSpec extends FormComponentWithLabelSpec {\n    type: 'textarea';\n    placeholder?: string;\n    maximized?: boolean;\n    enabled?: boolean;\n}\ninterface UrlInputSpec extends FormComponentWithLabelSpec {\n    type: 'urlinput';\n    filetype?: 'image' | 'media' | 'file';\n    enabled?: boolean;\n}\ninterface UrlInputData {\n    value: string;\n    meta: {\n        text?: string;\n    };\n}\ndeclare type BodyComponentSpec = BarSpec | ButtonSpec | CheckboxSpec | TextAreaSpec | InputSpec | ListBoxSpec | SelectBoxSpec | SizeInputSpec | SliderSpec | IframeSpec | HtmlPanelSpec | UrlInputSpec | DropZoneSpec | ColorInputSpec | GridSpec | ColorPickerSpec | ImagePreviewSpec | AlertBannerSpec | CollectionSpec | LabelSpec | TableSpec | PanelSpec | CustomEditorSpec;\ninterface BarSpec {\n    type: 'bar';\n    items: BodyComponentSpec[];\n}\ninterface CommonMenuItemSpec {\n    enabled?: boolean;\n    text?: string;\n    value?: string;\n    meta?: Record<string, any>;\n    shortcut?: string;\n}\ninterface CommonMenuItemInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n}\ninterface DialogToggleMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'togglemenuitem';\n    name: string;\n}\ndeclare type DialogFooterMenuButtonItemSpec = DialogToggleMenuItemSpec;\ninterface BaseDialogFooterButtonSpec {\n    name?: string;\n    align?: 'start' | 'end';\n    primary?: boolean;\n    enabled?: boolean;\n    icon?: string;\n    buttonType?: 'primary' | 'secondary';\n}\ninterface DialogFooterNormalButtonSpec extends BaseDialogFooterButtonSpec {\n    type: 'submit' | 'cancel' | 'custom';\n    text: string;\n}\ninterface DialogFooterMenuButtonSpec extends BaseDialogFooterButtonSpec {\n    type: 'menu';\n    text?: string;\n    tooltip?: string;\n    icon?: string;\n    items: DialogFooterMenuButtonItemSpec[];\n}\ndeclare type DialogFooterButtonSpec = DialogFooterNormalButtonSpec | DialogFooterMenuButtonSpec;\ninterface TabSpec {\n    name?: string;\n    title: string;\n    items: BodyComponentSpec[];\n}\ninterface TabPanelSpec {\n    type: 'tabpanel';\n    tabs: TabSpec[];\n}\ndeclare type DialogDataItem = any;\ndeclare type DialogData = Record<string, DialogDataItem>;\ninterface DialogInstanceApi<T extends DialogData> {\n    getData: () => T;\n    setData: (data: Partial<T>) => void;\n    setEnabled: (name: string, state: boolean) => void;\n    focus: (name: string) => void;\n    showTab: (name: string) => void;\n    redial: (nu: DialogSpec<T>) => void;\n    block: (msg: string) => void;\n    unblock: () => void;\n    close: () => void;\n}\ninterface DialogActionDetails {\n    name: string;\n    value?: any;\n}\ninterface DialogChangeDetails<T> {\n    name: keyof T;\n}\ninterface DialogTabChangeDetails {\n    newTabName: string;\n    oldTabName: string;\n}\ndeclare type DialogActionHandler<T extends DialogData> = (api: DialogInstanceApi<T>, details: DialogActionDetails) => void;\ndeclare type DialogChangeHandler<T extends DialogData> = (api: DialogInstanceApi<T>, details: DialogChangeDetails<T>) => void;\ndeclare type DialogSubmitHandler<T extends DialogData> = (api: DialogInstanceApi<T>) => void;\ndeclare type DialogCloseHandler = () => void;\ndeclare type DialogCancelHandler<T extends DialogData> = (api: DialogInstanceApi<T>) => void;\ndeclare type DialogTabChangeHandler<T extends DialogData> = (api: DialogInstanceApi<T>, details: DialogTabChangeDetails) => void;\ndeclare type DialogSize = 'normal' | 'medium' | 'large';\ninterface DialogSpec<T extends DialogData> {\n    title: string;\n    size?: DialogSize;\n    body: TabPanelSpec | PanelSpec;\n    buttons: DialogFooterButtonSpec[];\n    initialData?: Partial<T>;\n    onAction?: DialogActionHandler<T>;\n    onChange?: DialogChangeHandler<T>;\n    onSubmit?: DialogSubmitHandler<T>;\n    onClose?: DialogCloseHandler;\n    onCancel?: DialogCancelHandler<T>;\n    onTabChange?: DialogTabChangeHandler<T>;\n}\ninterface UrlDialogInstanceApi {\n    block: (msg: string) => void;\n    unblock: () => void;\n    close: () => void;\n    sendMessage: (msg: any) => void;\n}\ninterface UrlDialogActionDetails {\n    name: string;\n    value?: any;\n}\ninterface UrlDialogMessage {\n    mceAction: string;\n    [key: string]: any;\n}\ndeclare type UrlDialogActionHandler = (api: UrlDialogInstanceApi, actions: UrlDialogActionDetails) => void;\ndeclare type UrlDialogCloseHandler = () => void;\ndeclare type UrlDialogCancelHandler = (api: UrlDialogInstanceApi) => void;\ndeclare type UrlDialogMessageHandler = (api: UrlDialogInstanceApi, message: UrlDialogMessage) => void;\ninterface UrlDialogFooterButtonSpec extends DialogFooterNormalButtonSpec {\n    type: 'cancel' | 'custom';\n}\ninterface UrlDialogSpec {\n    title: string;\n    url: string;\n    height?: number;\n    width?: number;\n    buttons?: UrlDialogFooterButtonSpec[];\n    onAction?: UrlDialogActionHandler;\n    onClose?: UrlDialogCloseHandler;\n    onCancel?: UrlDialogCancelHandler;\n    onMessage?: UrlDialogMessageHandler;\n}\ndeclare type CardContainerDirection = 'vertical' | 'horizontal';\ndeclare type CardContainerAlign = 'left' | 'right';\ndeclare type CardContainerValign = 'top' | 'middle' | 'bottom';\ninterface CardContainerSpec {\n    type: 'cardcontainer';\n    items: CardItemSpec[];\n    direction?: CardContainerDirection;\n    align?: CardContainerAlign;\n    valign?: CardContainerValign;\n}\ninterface CardImageSpec {\n    type: 'cardimage';\n    src: string;\n    alt?: string;\n    classes?: string[];\n}\ninterface CardTextSpec {\n    type: 'cardtext';\n    text: string;\n    name?: string;\n    classes?: string[];\n}\ndeclare type CardItemSpec = CardContainerSpec | CardImageSpec | CardTextSpec;\ninterface CardMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n}\ninterface CardMenuItemSpec extends Omit<CommonMenuItemSpec, 'text' | 'shortcut'> {\n    type: 'cardmenuitem';\n    label?: string;\n    items: CardItemSpec[];\n    onSetup?: (api: CardMenuItemInstanceApi) => (api: CardMenuItemInstanceApi) => void;\n    onAction?: (api: CardMenuItemInstanceApi) => void;\n}\ninterface SeparatorMenuItemSpec {\n    type?: 'separator';\n    text?: string;\n}\ndeclare type ColumnTypes$1 = number | 'auto';\ndeclare type SeparatorItemSpec = SeparatorMenuItemSpec;\ninterface AutocompleterItemSpec {\n    type?: 'autocompleteitem';\n    value: string;\n    text?: string;\n    icon?: string;\n    meta?: Record<string, any>;\n}\ndeclare type AutocompleterContents = SeparatorItemSpec | AutocompleterItemSpec | CardMenuItemSpec;\ninterface AutocompleterSpec {\n    type?: 'autocompleter';\n    ch?: string;\n    trigger?: string;\n    minChars?: number;\n    columns?: ColumnTypes$1;\n    matches?: (rng: Range, text: string, pattern: string) => boolean;\n    fetch: (pattern: string, maxResults: number, fetchOptions: Record<string, any>) => Promise<AutocompleterContents[]>;\n    onAction: (autocompleterApi: AutocompleterInstanceApi, rng: Range, value: string, meta: Record<string, any>) => void;\n    maxResults?: number;\n    highlightOn?: string[];\n}\ninterface AutocompleterInstanceApi {\n    hide: () => void;\n    reload: (fetchOptions: Record<string, any>) => void;\n}\ndeclare type ContextPosition = 'node' | 'selection' | 'line';\ndeclare type ContextScope = 'node' | 'editor';\ninterface ContextBarSpec {\n    predicate?: (elem: Element) => boolean;\n    position?: ContextPosition;\n    scope?: ContextScope;\n}\ninterface BaseToolbarButtonSpec<I extends BaseToolbarButtonInstanceApi> {\n    enabled?: boolean;\n    tooltip?: string;\n    icon?: string;\n    text?: string;\n    onSetup?: (api: I) => (api: I) => void;\n}\ninterface BaseToolbarButtonInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n}\ninterface ToolbarButtonSpec extends BaseToolbarButtonSpec<ToolbarButtonInstanceApi> {\n    type?: 'button';\n    onAction: (api: ToolbarButtonInstanceApi) => void;\n}\ninterface ToolbarButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n}\ninterface BaseToolbarToggleButtonSpec<I extends BaseToolbarButtonInstanceApi> extends BaseToolbarButtonSpec<I> {\n    active?: boolean;\n}\ninterface BaseToolbarToggleButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ninterface ToolbarToggleButtonSpec extends BaseToolbarToggleButtonSpec<ToolbarToggleButtonInstanceApi> {\n    type?: 'togglebutton';\n    onAction: (api: ToolbarToggleButtonInstanceApi) => void;\n}\ninterface ToolbarToggleButtonInstanceApi extends BaseToolbarToggleButtonInstanceApi {\n}\ninterface ContextFormLaunchButtonApi extends BaseToolbarButtonSpec<BaseToolbarButtonInstanceApi> {\n    type: 'contextformbutton';\n}\ninterface ContextFormLaunchToggleButtonSpec extends BaseToolbarToggleButtonSpec<BaseToolbarToggleButtonInstanceApi> {\n    type: 'contextformtogglebutton';\n}\ninterface ContextFormButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n}\ninterface ContextFormToggleButtonInstanceApi extends BaseToolbarToggleButtonInstanceApi {\n}\ninterface ContextFormButtonSpec extends BaseToolbarButtonSpec<ContextFormButtonInstanceApi> {\n    type?: 'contextformbutton';\n    primary?: boolean;\n    onAction: (formApi: ContextFormInstanceApi, api: ContextFormButtonInstanceApi) => void;\n}\ninterface ContextFormToggleButtonSpec extends BaseToolbarToggleButtonSpec<ContextFormToggleButtonInstanceApi> {\n    type?: 'contextformtogglebutton';\n    onAction: (formApi: ContextFormInstanceApi, buttonApi: ContextFormToggleButtonInstanceApi) => void;\n    primary?: boolean;\n}\ninterface ContextFormInstanceApi {\n    hide: () => void;\n    getValue: () => string;\n}\ninterface ContextFormSpec extends ContextBarSpec {\n    type?: 'contextform';\n    initValue?: () => string;\n    label?: string;\n    launch?: ContextFormLaunchButtonApi | ContextFormLaunchToggleButtonSpec;\n    commands: Array<ContextFormToggleButtonSpec | ContextFormButtonSpec>;\n}\ninterface ContextToolbarSpec extends ContextBarSpec {\n    type?: 'contexttoolbar';\n    items: string;\n}\ninterface ChoiceMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'choiceitem';\n    icon?: string;\n}\ninterface ChoiceMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ninterface ContextMenuItem extends CommonMenuItemSpec {\n    text: string;\n    icon?: string;\n    type?: 'item';\n    onAction: () => void;\n}\ninterface ContextSubMenu extends CommonMenuItemSpec {\n    type: 'submenu';\n    text: string;\n    icon?: string;\n    getSubmenuItems: () => string | Array<ContextMenuContents>;\n}\ndeclare type ContextMenuContents = string | ContextMenuItem | SeparatorMenuItemSpec | ContextSubMenu;\ninterface ContextMenuApi {\n    update: (element: Element) => string | Array<ContextMenuContents>;\n}\ninterface FancyActionArgsMap {\n    'inserttable': {\n        numRows: number;\n        numColumns: number;\n    };\n    'colorswatch': {\n        value: string;\n    };\n}\ninterface BaseFancyMenuItemSpec<T extends keyof FancyActionArgsMap> {\n    type: 'fancymenuitem';\n    fancytype: T;\n    initData?: Record<string, unknown>;\n    onAction?: (data: FancyActionArgsMap[T]) => void;\n}\ninterface InsertTableMenuItemSpec extends BaseFancyMenuItemSpec<'inserttable'> {\n    fancytype: 'inserttable';\n    initData?: {};\n}\ninterface ColorSwatchMenuItemSpec extends BaseFancyMenuItemSpec<'colorswatch'> {\n    fancytype: 'colorswatch';\n    initData?: {\n        allowCustomColors?: boolean;\n        colors?: ChoiceMenuItemSpec[];\n        storageKey?: string;\n    };\n}\ndeclare type FancyMenuItemSpec = InsertTableMenuItemSpec | ColorSwatchMenuItemSpec;\ninterface MenuItemSpec extends CommonMenuItemSpec {\n    type?: 'menuitem';\n    icon?: string;\n    onSetup?: (api: MenuItemInstanceApi) => (api: MenuItemInstanceApi) => void;\n    onAction?: (api: MenuItemInstanceApi) => void;\n}\ninterface MenuItemInstanceApi extends CommonMenuItemInstanceApi {\n}\ndeclare type NestedMenuItemContents = string | MenuItemSpec | NestedMenuItemSpec | ToggleMenuItemSpec | SeparatorMenuItemSpec | FancyMenuItemSpec;\ninterface NestedMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'nestedmenuitem';\n    icon?: string;\n    getSubmenuItems: () => string | Array<NestedMenuItemContents>;\n    onSetup?: (api: NestedMenuItemInstanceApi) => (api: NestedMenuItemInstanceApi) => void;\n}\ninterface NestedMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n}\ninterface ToggleMenuItemSpec extends CommonMenuItemSpec {\n    type?: 'togglemenuitem';\n    icon?: string;\n    active?: boolean;\n    onSetup?: (api: ToggleMenuItemInstanceApi) => void;\n    onAction: (api: ToggleMenuItemInstanceApi) => void;\n}\ninterface ToggleMenuItemInstanceApi extends CommonMenuItemInstanceApi {\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ntype PublicDialog_d_AlertBannerSpec = AlertBannerSpec;\ntype PublicDialog_d_BarSpec = BarSpec;\ntype PublicDialog_d_BodyComponentSpec = BodyComponentSpec;\ntype PublicDialog_d_ButtonSpec = ButtonSpec;\ntype PublicDialog_d_CheckboxSpec = CheckboxSpec;\ntype PublicDialog_d_CollectionItem = CollectionItem;\ntype PublicDialog_d_CollectionSpec = CollectionSpec;\ntype PublicDialog_d_ColorInputSpec = ColorInputSpec;\ntype PublicDialog_d_ColorPickerSpec = ColorPickerSpec;\ntype PublicDialog_d_CustomEditorSpec = CustomEditorSpec;\ntype PublicDialog_d_CustomEditorInit = CustomEditorInit;\ntype PublicDialog_d_CustomEditorInitFn = CustomEditorInitFn;\ntype PublicDialog_d_DialogData = DialogData;\ntype PublicDialog_d_DialogSize = DialogSize;\ntype PublicDialog_d_DialogSpec<T extends DialogData> = DialogSpec<T>;\ntype PublicDialog_d_DialogInstanceApi<T extends DialogData> = DialogInstanceApi<T>;\ntype PublicDialog_d_DialogFooterButtonSpec = DialogFooterButtonSpec;\ntype PublicDialog_d_DialogActionDetails = DialogActionDetails;\ntype PublicDialog_d_DialogChangeDetails<T> = DialogChangeDetails<T>;\ntype PublicDialog_d_DialogTabChangeDetails = DialogTabChangeDetails;\ntype PublicDialog_d_DropZoneSpec = DropZoneSpec;\ntype PublicDialog_d_GridSpec = GridSpec;\ntype PublicDialog_d_HtmlPanelSpec = HtmlPanelSpec;\ntype PublicDialog_d_IframeSpec = IframeSpec;\ntype PublicDialog_d_ImagePreviewSpec = ImagePreviewSpec;\ntype PublicDialog_d_InputSpec = InputSpec;\ntype PublicDialog_d_LabelSpec = LabelSpec;\ntype PublicDialog_d_ListBoxSpec = ListBoxSpec;\ntype PublicDialog_d_ListBoxItemSpec = ListBoxItemSpec;\ntype PublicDialog_d_ListBoxNestedItemSpec = ListBoxNestedItemSpec;\ntype PublicDialog_d_ListBoxSingleItemSpec = ListBoxSingleItemSpec;\ntype PublicDialog_d_PanelSpec = PanelSpec;\ntype PublicDialog_d_SelectBoxSpec = SelectBoxSpec;\ntype PublicDialog_d_SelectBoxItemSpec = SelectBoxItemSpec;\ntype PublicDialog_d_SizeInputSpec = SizeInputSpec;\ntype PublicDialog_d_SliderSpec = SliderSpec;\ntype PublicDialog_d_TableSpec = TableSpec;\ntype PublicDialog_d_TabSpec = TabSpec;\ntype PublicDialog_d_TabPanelSpec = TabPanelSpec;\ntype PublicDialog_d_TextAreaSpec = TextAreaSpec;\ntype PublicDialog_d_UrlInputData = UrlInputData;\ntype PublicDialog_d_UrlInputSpec = UrlInputSpec;\ntype PublicDialog_d_UrlDialogSpec = UrlDialogSpec;\ntype PublicDialog_d_UrlDialogFooterButtonSpec = UrlDialogFooterButtonSpec;\ntype PublicDialog_d_UrlDialogInstanceApi = UrlDialogInstanceApi;\ntype PublicDialog_d_UrlDialogActionDetails = UrlDialogActionDetails;\ntype PublicDialog_d_UrlDialogMessage = UrlDialogMessage;\ndeclare namespace PublicDialog_d {\n    export { PublicDialog_d_AlertBannerSpec as AlertBannerSpec, PublicDialog_d_BarSpec as BarSpec, PublicDialog_d_BodyComponentSpec as BodyComponentSpec, PublicDialog_d_ButtonSpec as ButtonSpec, PublicDialog_d_CheckboxSpec as CheckboxSpec, PublicDialog_d_CollectionItem as CollectionItem, PublicDialog_d_CollectionSpec as CollectionSpec, PublicDialog_d_ColorInputSpec as ColorInputSpec, PublicDialog_d_ColorPickerSpec as ColorPickerSpec, PublicDialog_d_CustomEditorSpec as CustomEditorSpec, PublicDialog_d_CustomEditorInit as CustomEditorInit, PublicDialog_d_CustomEditorInitFn as CustomEditorInitFn, PublicDialog_d_DialogData as DialogData, PublicDialog_d_DialogSize as DialogSize, PublicDialog_d_DialogSpec as DialogSpec, PublicDialog_d_DialogInstanceApi as DialogInstanceApi, PublicDialog_d_DialogFooterButtonSpec as DialogFooterButtonSpec, PublicDialog_d_DialogActionDetails as DialogActionDetails, PublicDialog_d_DialogChangeDetails as DialogChangeDetails, PublicDialog_d_DialogTabChangeDetails as DialogTabChangeDetails, PublicDialog_d_DropZoneSpec as DropZoneSpec, PublicDialog_d_GridSpec as GridSpec, PublicDialog_d_HtmlPanelSpec as HtmlPanelSpec, PublicDialog_d_IframeSpec as IframeSpec, PublicDialog_d_ImagePreviewSpec as ImagePreviewSpec, PublicDialog_d_InputSpec as InputSpec, PublicDialog_d_LabelSpec as LabelSpec, PublicDialog_d_ListBoxSpec as ListBoxSpec, PublicDialog_d_ListBoxItemSpec as ListBoxItemSpec, PublicDialog_d_ListBoxNestedItemSpec as ListBoxNestedItemSpec, PublicDialog_d_ListBoxSingleItemSpec as ListBoxSingleItemSpec, PublicDialog_d_PanelSpec as PanelSpec, PublicDialog_d_SelectBoxSpec as SelectBoxSpec, PublicDialog_d_SelectBoxItemSpec as SelectBoxItemSpec, PublicDialog_d_SizeInputSpec as SizeInputSpec, PublicDialog_d_SliderSpec as SliderSpec, PublicDialog_d_TableSpec as TableSpec, PublicDialog_d_TabSpec as TabSpec, PublicDialog_d_TabPanelSpec as TabPanelSpec, PublicDialog_d_TextAreaSpec as TextAreaSpec, PublicDialog_d_UrlInputData as UrlInputData, PublicDialog_d_UrlInputSpec as UrlInputSpec, PublicDialog_d_UrlDialogSpec as UrlDialogSpec, PublicDialog_d_UrlDialogFooterButtonSpec as UrlDialogFooterButtonSpec, PublicDialog_d_UrlDialogInstanceApi as UrlDialogInstanceApi, PublicDialog_d_UrlDialogActionDetails as UrlDialogActionDetails, PublicDialog_d_UrlDialogMessage as UrlDialogMessage, };\n}\ntype PublicInlineContent_d_AutocompleterSpec = AutocompleterSpec;\ntype PublicInlineContent_d_AutocompleterItemSpec = AutocompleterItemSpec;\ntype PublicInlineContent_d_AutocompleterContents = AutocompleterContents;\ntype PublicInlineContent_d_AutocompleterInstanceApi = AutocompleterInstanceApi;\ntype PublicInlineContent_d_ContextPosition = ContextPosition;\ntype PublicInlineContent_d_ContextScope = ContextScope;\ntype PublicInlineContent_d_ContextFormSpec = ContextFormSpec;\ntype PublicInlineContent_d_ContextFormInstanceApi = ContextFormInstanceApi;\ntype PublicInlineContent_d_ContextFormButtonSpec = ContextFormButtonSpec;\ntype PublicInlineContent_d_ContextFormButtonInstanceApi = ContextFormButtonInstanceApi;\ntype PublicInlineContent_d_ContextFormToggleButtonSpec = ContextFormToggleButtonSpec;\ntype PublicInlineContent_d_ContextFormToggleButtonInstanceApi = ContextFormToggleButtonInstanceApi;\ntype PublicInlineContent_d_ContextToolbarSpec = ContextToolbarSpec;\ntype PublicInlineContent_d_SeparatorItemSpec = SeparatorItemSpec;\ndeclare namespace PublicInlineContent_d {\n    export { PublicInlineContent_d_AutocompleterSpec as AutocompleterSpec, PublicInlineContent_d_AutocompleterItemSpec as AutocompleterItemSpec, PublicInlineContent_d_AutocompleterContents as AutocompleterContents, PublicInlineContent_d_AutocompleterInstanceApi as AutocompleterInstanceApi, PublicInlineContent_d_ContextPosition as ContextPosition, PublicInlineContent_d_ContextScope as ContextScope, PublicInlineContent_d_ContextFormSpec as ContextFormSpec, PublicInlineContent_d_ContextFormInstanceApi as ContextFormInstanceApi, PublicInlineContent_d_ContextFormButtonSpec as ContextFormButtonSpec, PublicInlineContent_d_ContextFormButtonInstanceApi as ContextFormButtonInstanceApi, PublicInlineContent_d_ContextFormToggleButtonSpec as ContextFormToggleButtonSpec, PublicInlineContent_d_ContextFormToggleButtonInstanceApi as ContextFormToggleButtonInstanceApi, PublicInlineContent_d_ContextToolbarSpec as ContextToolbarSpec, PublicInlineContent_d_SeparatorItemSpec as SeparatorItemSpec, };\n}\ntype PublicMenu_d_MenuItemSpec = MenuItemSpec;\ntype PublicMenu_d_MenuItemInstanceApi = MenuItemInstanceApi;\ntype PublicMenu_d_NestedMenuItemContents = NestedMenuItemContents;\ntype PublicMenu_d_NestedMenuItemSpec = NestedMenuItemSpec;\ntype PublicMenu_d_NestedMenuItemInstanceApi = NestedMenuItemInstanceApi;\ntype PublicMenu_d_FancyMenuItemSpec = FancyMenuItemSpec;\ntype PublicMenu_d_ColorSwatchMenuItemSpec = ColorSwatchMenuItemSpec;\ntype PublicMenu_d_InsertTableMenuItemSpec = InsertTableMenuItemSpec;\ntype PublicMenu_d_ToggleMenuItemSpec = ToggleMenuItemSpec;\ntype PublicMenu_d_ToggleMenuItemInstanceApi = ToggleMenuItemInstanceApi;\ntype PublicMenu_d_ChoiceMenuItemSpec = ChoiceMenuItemSpec;\ntype PublicMenu_d_ChoiceMenuItemInstanceApi = ChoiceMenuItemInstanceApi;\ntype PublicMenu_d_SeparatorMenuItemSpec = SeparatorMenuItemSpec;\ntype PublicMenu_d_ContextMenuApi = ContextMenuApi;\ntype PublicMenu_d_ContextMenuContents = ContextMenuContents;\ntype PublicMenu_d_ContextMenuItem = ContextMenuItem;\ntype PublicMenu_d_ContextSubMenu = ContextSubMenu;\ntype PublicMenu_d_CardMenuItemSpec = CardMenuItemSpec;\ntype PublicMenu_d_CardMenuItemInstanceApi = CardMenuItemInstanceApi;\ntype PublicMenu_d_CardItemSpec = CardItemSpec;\ntype PublicMenu_d_CardContainerSpec = CardContainerSpec;\ntype PublicMenu_d_CardImageSpec = CardImageSpec;\ntype PublicMenu_d_CardTextSpec = CardTextSpec;\ndeclare namespace PublicMenu_d {\n    export { PublicMenu_d_MenuItemSpec as MenuItemSpec, PublicMenu_d_MenuItemInstanceApi as MenuItemInstanceApi, PublicMenu_d_NestedMenuItemContents as NestedMenuItemContents, PublicMenu_d_NestedMenuItemSpec as NestedMenuItemSpec, PublicMenu_d_NestedMenuItemInstanceApi as NestedMenuItemInstanceApi, PublicMenu_d_FancyMenuItemSpec as FancyMenuItemSpec, PublicMenu_d_ColorSwatchMenuItemSpec as ColorSwatchMenuItemSpec, PublicMenu_d_InsertTableMenuItemSpec as InsertTableMenuItemSpec, PublicMenu_d_ToggleMenuItemSpec as ToggleMenuItemSpec, PublicMenu_d_ToggleMenuItemInstanceApi as ToggleMenuItemInstanceApi, PublicMenu_d_ChoiceMenuItemSpec as ChoiceMenuItemSpec, PublicMenu_d_ChoiceMenuItemInstanceApi as ChoiceMenuItemInstanceApi, PublicMenu_d_SeparatorMenuItemSpec as SeparatorMenuItemSpec, PublicMenu_d_ContextMenuApi as ContextMenuApi, PublicMenu_d_ContextMenuContents as ContextMenuContents, PublicMenu_d_ContextMenuItem as ContextMenuItem, PublicMenu_d_ContextSubMenu as ContextSubMenu, PublicMenu_d_CardMenuItemSpec as CardMenuItemSpec, PublicMenu_d_CardMenuItemInstanceApi as CardMenuItemInstanceApi, PublicMenu_d_CardItemSpec as CardItemSpec, PublicMenu_d_CardContainerSpec as CardContainerSpec, PublicMenu_d_CardImageSpec as CardImageSpec, PublicMenu_d_CardTextSpec as CardTextSpec, };\n}\ninterface SidebarInstanceApi {\n    element: () => HTMLElement;\n}\ninterface SidebarSpec {\n    icon?: string;\n    tooltip?: string;\n    onShow?: (api: SidebarInstanceApi) => void;\n    onSetup?: (api: SidebarInstanceApi) => (api: SidebarInstanceApi) => void;\n    onHide?: (api: SidebarInstanceApi) => void;\n}\ntype PublicSidebar_d_SidebarSpec = SidebarSpec;\ntype PublicSidebar_d_SidebarInstanceApi = SidebarInstanceApi;\ndeclare namespace PublicSidebar_d {\n    export { PublicSidebar_d_SidebarSpec as SidebarSpec, PublicSidebar_d_SidebarInstanceApi as SidebarInstanceApi, };\n}\ninterface ToolbarGroupSetting {\n    name: string;\n    items: string[];\n}\ndeclare type ToolbarConfig = string | ToolbarGroupSetting[];\ninterface GroupToolbarButtonInstanceApi extends BaseToolbarButtonInstanceApi {\n}\ninterface GroupToolbarButtonSpec extends BaseToolbarButtonSpec<GroupToolbarButtonInstanceApi> {\n    type?: 'grouptoolbarbutton';\n    items?: ToolbarConfig;\n}\ndeclare type MenuButtonItemTypes = NestedMenuItemContents;\ndeclare type SuccessCallback$1 = (menu: string | MenuButtonItemTypes[]) => void;\ninterface MenuButtonFetchContext {\n    pattern: string;\n}\ninterface BaseMenuButtonSpec {\n    text?: string;\n    tooltip?: string;\n    icon?: string;\n    search?: boolean | {\n        placeholder?: string;\n    };\n    fetch: (success: SuccessCallback$1, fetchContext: MenuButtonFetchContext) => void;\n    onSetup?: (api: BaseMenuButtonInstanceApi) => (api: BaseMenuButtonInstanceApi) => void;\n}\ninterface BaseMenuButtonInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ninterface ToolbarMenuButtonSpec extends BaseMenuButtonSpec {\n    type?: 'menubutton';\n    onSetup?: (api: ToolbarMenuButtonInstanceApi) => (api: ToolbarMenuButtonInstanceApi) => void;\n}\ninterface ToolbarMenuButtonInstanceApi extends BaseMenuButtonInstanceApi {\n}\ndeclare type ToolbarSplitButtonItemTypes = ChoiceMenuItemSpec | SeparatorMenuItemSpec;\ndeclare type SuccessCallback = (menu: ToolbarSplitButtonItemTypes[]) => void;\ndeclare type SelectPredicate = (value: string) => boolean;\ndeclare type PresetTypes = 'color' | 'normal' | 'listpreview';\ndeclare type ColumnTypes = number | 'auto';\ninterface ToolbarSplitButtonSpec {\n    type?: 'splitbutton';\n    tooltip?: string;\n    icon?: string;\n    text?: string;\n    select?: SelectPredicate;\n    presets?: PresetTypes;\n    columns?: ColumnTypes;\n    fetch: (success: SuccessCallback) => void;\n    onSetup?: (api: ToolbarSplitButtonInstanceApi) => (api: ToolbarSplitButtonInstanceApi) => void;\n    onAction: (api: ToolbarSplitButtonInstanceApi) => void;\n    onItemAction: (api: ToolbarSplitButtonInstanceApi, value: string) => void;\n}\ninterface ToolbarSplitButtonInstanceApi {\n    isEnabled: () => boolean;\n    setEnabled: (state: boolean) => void;\n    setIconFill: (id: string, value: string) => void;\n    isActive: () => boolean;\n    setActive: (state: boolean) => void;\n}\ntype PublicToolbar_d_ToolbarButtonSpec = ToolbarButtonSpec;\ntype PublicToolbar_d_ToolbarButtonInstanceApi = ToolbarButtonInstanceApi;\ntype PublicToolbar_d_ToolbarSplitButtonSpec = ToolbarSplitButtonSpec;\ntype PublicToolbar_d_ToolbarSplitButtonInstanceApi = ToolbarSplitButtonInstanceApi;\ntype PublicToolbar_d_ToolbarMenuButtonSpec = ToolbarMenuButtonSpec;\ntype PublicToolbar_d_ToolbarMenuButtonInstanceApi = ToolbarMenuButtonInstanceApi;\ntype PublicToolbar_d_ToolbarToggleButtonSpec = ToolbarToggleButtonSpec;\ntype PublicToolbar_d_ToolbarToggleButtonInstanceApi = ToolbarToggleButtonInstanceApi;\ntype PublicToolbar_d_GroupToolbarButtonSpec = GroupToolbarButtonSpec;\ntype PublicToolbar_d_GroupToolbarButtonInstanceApi = GroupToolbarButtonInstanceApi;\ndeclare namespace PublicToolbar_d {\n    export { PublicToolbar_d_ToolbarButtonSpec as ToolbarButtonSpec, PublicToolbar_d_ToolbarButtonInstanceApi as ToolbarButtonInstanceApi, PublicToolbar_d_ToolbarSplitButtonSpec as ToolbarSplitButtonSpec, PublicToolbar_d_ToolbarSplitButtonInstanceApi as ToolbarSplitButtonInstanceApi, PublicToolbar_d_ToolbarMenuButtonSpec as ToolbarMenuButtonSpec, PublicToolbar_d_ToolbarMenuButtonInstanceApi as ToolbarMenuButtonInstanceApi, PublicToolbar_d_ToolbarToggleButtonSpec as ToolbarToggleButtonSpec, PublicToolbar_d_ToolbarToggleButtonInstanceApi as ToolbarToggleButtonInstanceApi, PublicToolbar_d_GroupToolbarButtonSpec as GroupToolbarButtonSpec, PublicToolbar_d_GroupToolbarButtonInstanceApi as GroupToolbarButtonInstanceApi, };\n}\ninterface ViewNormalButtonSpec {\n    type: 'button';\n    text: string;\n    buttonType?: 'primary' | 'secondary';\n    onAction: () => void;\n}\ndeclare type ViewButtonSpec = ViewNormalButtonSpec;\ninterface ViewInstanceApi {\n    getContainer: () => HTMLElement;\n}\ninterface ViewSpec {\n    buttons?: ViewButtonSpec[];\n    onShow: (api: ViewInstanceApi) => void;\n    onHide: (api: ViewInstanceApi) => void;\n}\ntype PublicView_d_ViewSpec = ViewSpec;\ntype PublicView_d_ViewInstanceApi = ViewInstanceApi;\ndeclare namespace PublicView_d {\n    export { PublicView_d_ViewSpec as ViewSpec, PublicView_d_ViewInstanceApi as ViewInstanceApi, };\n}\ninterface Registry$1 {\n    addButton: (name: string, spec: ToolbarButtonSpec) => void;\n    addGroupToolbarButton: (name: string, spec: GroupToolbarButtonSpec) => void;\n    addToggleButton: (name: string, spec: ToolbarToggleButtonSpec) => void;\n    addMenuButton: (name: string, spec: ToolbarMenuButtonSpec) => void;\n    addSplitButton: (name: string, spec: ToolbarSplitButtonSpec) => void;\n    addMenuItem: (name: string, spec: MenuItemSpec) => void;\n    addNestedMenuItem: (name: string, spec: NestedMenuItemSpec) => void;\n    addToggleMenuItem: (name: string, spec: ToggleMenuItemSpec) => void;\n    addContextMenu: (name: string, spec: ContextMenuApi) => void;\n    addContextToolbar: (name: string, spec: ContextToolbarSpec) => void;\n    addContextForm: (name: string, spec: ContextFormSpec) => void;\n    addIcon: (name: string, svgData: string) => void;\n    addAutocompleter: (name: string, spec: AutocompleterSpec) => void;\n    addSidebar: (name: string, spec: SidebarSpec) => void;\n    addView: (name: string, spec: ViewSpec) => void;\n    getAll: () => {\n        buttons: Record<string, ToolbarButtonSpec | GroupToolbarButtonSpec | ToolbarMenuButtonSpec | ToolbarSplitButtonSpec | ToolbarToggleButtonSpec>;\n        menuItems: Record<string, MenuItemSpec | NestedMenuItemSpec | ToggleMenuItemSpec>;\n        popups: Record<string, AutocompleterSpec>;\n        contextMenus: Record<string, ContextMenuApi>;\n        contextToolbars: Record<string, ContextToolbarSpec | ContextFormSpec>;\n        icons: Record<string, string>;\n        sidebars: Record<string, SidebarSpec>;\n        views: Record<string, ViewSpec>;\n    };\n}\ninterface AutocompleteLookupData {\n    readonly matchText: string;\n    readonly items: AutocompleterContents[];\n    readonly columns: ColumnTypes$1;\n    readonly onAction: (autoApi: AutocompleterInstanceApi, rng: Range, value: string, meta: Record<string, any>) => void;\n    readonly highlightOn: string[];\n}\ninterface AutocompleterEventArgs {\n    readonly lookupData: AutocompleteLookupData[];\n}\ninterface RangeLikeObject {\n    startContainer: Node;\n    startOffset: number;\n    endContainer: Node;\n    endOffset: number;\n}\ndeclare type ApplyFormat = BlockFormat | InlineFormat | SelectorFormat;\ndeclare type RemoveFormat = RemoveBlockFormat | RemoveInlineFormat | RemoveSelectorFormat;\ndeclare type Format = ApplyFormat | RemoveFormat;\ndeclare type Formats = Record<string, Format | Format[]>;\ndeclare type FormatAttrOrStyleValue = string | ((vars?: FormatVars) => string | null);\ndeclare type FormatVars = Record<string, string | null>;\ninterface BaseFormat<T> {\n    ceFalseOverride?: boolean;\n    classes?: string | string[];\n    collapsed?: boolean;\n    exact?: boolean;\n    expand?: boolean;\n    links?: boolean;\n    mixed?: boolean;\n    block_expand?: boolean;\n    onmatch?: (node: Element, fmt: T, itemName: string) => boolean;\n    remove?: 'none' | 'empty' | 'all';\n    remove_similar?: boolean;\n    split?: boolean;\n    deep?: boolean;\n    preserve_attributes?: string[];\n}\ninterface Block {\n    block: string;\n    list_block?: string;\n    wrapper?: boolean;\n}\ninterface Inline {\n    inline: string;\n}\ninterface Selector {\n    selector: string;\n    inherit?: boolean;\n}\ninterface CommonFormat<T> extends BaseFormat<T> {\n    attributes?: Record<string, FormatAttrOrStyleValue>;\n    styles?: Record<string, FormatAttrOrStyleValue>;\n    toggle?: boolean;\n    preview?: string | false;\n    onformat?: (elm: Element, fmt: T, vars?: FormatVars, node?: Node | RangeLikeObject | null) => void;\n    clear_child_styles?: boolean;\n    merge_siblings?: boolean;\n    merge_with_parents?: boolean;\n}\ninterface BlockFormat extends Block, CommonFormat<BlockFormat> {\n}\ninterface InlineFormat extends Inline, CommonFormat<InlineFormat> {\n}\ninterface SelectorFormat extends Selector, CommonFormat<SelectorFormat> {\n}\ninterface CommonRemoveFormat<T> extends BaseFormat<T> {\n    attributes?: string[] | Record<string, FormatAttrOrStyleValue>;\n    styles?: string[] | Record<string, FormatAttrOrStyleValue>;\n}\ninterface RemoveBlockFormat extends Block, CommonRemoveFormat<RemoveBlockFormat> {\n}\ninterface RemoveInlineFormat extends Inline, CommonRemoveFormat<RemoveInlineFormat> {\n}\ninterface RemoveSelectorFormat extends Selector, CommonRemoveFormat<RemoveSelectorFormat> {\n}\ninterface Filter<C extends Function> {\n    name: string;\n    callbacks: C[];\n}\ninterface ParserArgs {\n    getInner?: boolean | number;\n    forced_root_block?: boolean | string;\n    context?: string;\n    isRootContent?: boolean;\n    format?: string;\n    invalid?: boolean;\n    no_events?: boolean;\n    [key: string]: any;\n}\ndeclare type ParserFilterCallback = (nodes: AstNode[], name: string, args: ParserArgs) => void;\ninterface ParserFilter extends Filter<ParserFilterCallback> {\n}\ninterface DomParserSettings {\n    allow_html_data_urls?: boolean;\n    allow_svg_data_urls?: boolean;\n    allow_conditional_comments?: boolean;\n    allow_html_in_named_anchor?: boolean;\n    allow_script_urls?: boolean;\n    allow_unsafe_link_target?: boolean;\n    convert_fonts_to_spans?: boolean;\n    fix_list_elements?: boolean;\n    font_size_legacy_values?: string;\n    forced_root_block?: boolean | string;\n    forced_root_block_attrs?: Record<string, string>;\n    preserve_cdata?: boolean;\n    remove_trailing_brs?: boolean;\n    root_name?: string;\n    validate?: boolean;\n    inline_styles?: boolean;\n    blob_cache?: BlobCache;\n    document?: Document;\n}\ninterface DomParser {\n    schema: Schema;\n    addAttributeFilter: (name: string, callback: ParserFilterCallback) => void;\n    getAttributeFilters: () => ParserFilter[];\n    removeAttributeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    addNodeFilter: (name: string, callback: ParserFilterCallback) => void;\n    getNodeFilters: () => ParserFilter[];\n    removeNodeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    parse: (html: string, args?: ParserArgs) => AstNode;\n}\ninterface StyleSheetLoaderSettings {\n    maxLoadTime?: number;\n    contentCssCors?: boolean;\n    referrerPolicy?: ReferrerPolicy;\n}\ninterface StyleSheetLoader {\n    load: (url: string) => Promise<void>;\n    loadAll: (urls: string[]) => Promise<string[]>;\n    unload: (url: string) => void;\n    unloadAll: (urls: string[]) => void;\n    _setReferrerPolicy: (referrerPolicy: ReferrerPolicy) => void;\n    _setContentCssCors: (contentCssCors: boolean) => void;\n}\ndeclare type Registry = Registry$1;\ninterface EditorUiApi {\n    show: () => void;\n    hide: () => void;\n    setEnabled: (state: boolean) => void;\n    isEnabled: () => boolean;\n}\ninterface EditorUi extends EditorUiApi {\n    registry: Registry;\n    styleSheetLoader: StyleSheetLoader;\n}\ntype Ui_d_Registry = Registry;\ntype Ui_d_EditorUiApi = EditorUiApi;\ntype Ui_d_EditorUi = EditorUi;\ndeclare namespace Ui_d {\n    export { Ui_d_Registry as Registry, PublicDialog_d as Dialog, PublicInlineContent_d as InlineContent, PublicMenu_d as Menu, PublicView_d as View, PublicSidebar_d as Sidebar, PublicToolbar_d as Toolbar, Ui_d_EditorUiApi as EditorUiApi, Ui_d_EditorUi as EditorUi, };\n}\ninterface WindowParams {\n    readonly inline?: 'cursor' | 'toolbar';\n    readonly ariaAttrs?: boolean;\n}\ndeclare type InstanceApi<T extends DialogData> = UrlDialogInstanceApi | DialogInstanceApi<T>;\ninterface WindowManagerImpl {\n    open: <T extends DialogData>(config: DialogSpec<T>, params: WindowParams | undefined, closeWindow: (dialog: DialogInstanceApi<T>) => void) => DialogInstanceApi<T>;\n    openUrl: (config: UrlDialogSpec, closeWindow: (dialog: UrlDialogInstanceApi) => void) => UrlDialogInstanceApi;\n    alert: (message: string, callback: () => void) => void;\n    confirm: (message: string, callback: (state: boolean) => void) => void;\n    close: (dialog: InstanceApi<any>) => void;\n}\ninterface WindowManager {\n    open: <T extends DialogData>(config: DialogSpec<T>, params?: WindowParams) => DialogInstanceApi<T>;\n    openUrl: (config: UrlDialogSpec) => UrlDialogInstanceApi;\n    alert: (message: string, callback?: () => void, scope?: any) => void;\n    confirm: (message: string, callback?: (state: boolean) => void, scope?: any) => void;\n    close: () => void;\n}\ninterface ExecCommandEvent {\n    command: string;\n    ui: boolean;\n    value?: any;\n}\ninterface BeforeGetContentEvent extends GetContentArgs {\n    selection?: boolean;\n}\ninterface GetContentEvent extends BeforeGetContentEvent {\n    content: string;\n}\ninterface BeforeSetContentEvent extends SetContentArgs {\n    content: string;\n    selection?: boolean;\n}\ninterface SetContentEvent extends BeforeSetContentEvent {\n    content: string;\n}\ninterface SaveContentEvent extends GetContentEvent {\n    save: boolean;\n}\ninterface NewBlockEvent {\n    newBlock: Element;\n}\ninterface NodeChangeEvent {\n    element: Element;\n    parents: Node[];\n    selectionChange?: boolean;\n    initial?: boolean;\n}\ninterface FormatEvent {\n    format: string;\n    vars?: FormatVars;\n    node?: Node | RangeLikeObject | null;\n}\ninterface ObjectResizeEvent {\n    target: HTMLElement;\n    width: number;\n    height: number;\n    origin: string;\n}\ninterface ObjectSelectedEvent {\n    target: Node;\n    targetClone?: Node;\n}\ninterface ScrollIntoViewEvent {\n    elm: HTMLElement;\n    alignToTop: boolean | undefined;\n}\ninterface SetSelectionRangeEvent {\n    range: Range;\n    forward: boolean | undefined;\n}\ninterface ShowCaretEvent {\n    target: Node;\n    direction: number;\n    before: boolean;\n}\ninterface SwitchModeEvent {\n    mode: string;\n}\ninterface ChangeEvent {\n    level: UndoLevel;\n    lastLevel: UndoLevel | undefined;\n}\ninterface AddUndoEvent extends ChangeEvent {\n    originalEvent: Event | undefined;\n}\ninterface UndoRedoEvent {\n    level: UndoLevel;\n}\ninterface WindowEvent<T extends DialogData> {\n    dialog: InstanceApi<T>;\n}\ninterface ProgressStateEvent {\n    state: boolean;\n    time?: number;\n}\ninterface AfterProgressStateEvent {\n    state: boolean;\n}\ninterface PlaceholderToggleEvent {\n    state: boolean;\n}\ninterface LoadErrorEvent {\n    message: string;\n}\ninterface PreProcessEvent extends ParserArgs {\n    node: Element;\n}\ninterface PostProcessEvent extends ParserArgs {\n    content: string;\n}\ninterface PastePlainTextToggleEvent {\n    state: boolean;\n}\ninterface PastePreProcessEvent {\n    content: string;\n    readonly internal: boolean;\n}\ninterface PastePostProcessEvent {\n    node: HTMLElement;\n    readonly internal: boolean;\n}\ninterface NewTableRowEvent {\n    node: HTMLTableRowElement;\n}\ninterface NewTableCellEvent {\n    node: HTMLTableCellElement;\n}\ninterface TableEventData {\n    readonly structure: boolean;\n    readonly style: boolean;\n}\ninterface TableModifiedEvent extends TableEventData {\n    readonly table: HTMLTableElement;\n}\ninterface BeforeOpenNotificationEvent {\n    notification: NotificationSpec;\n}\ninterface OpenNotificationEvent {\n    notification: NotificationApi;\n}\ninterface EditorEventMap extends Omit<NativeEventMap, 'blur' | 'focus'> {\n    'activate': {\n        relatedTarget: Editor | null;\n    };\n    'deactivate': {\n        relatedTarget: Editor;\n    };\n    'focus': {\n        blurredEditor: Editor | null;\n    };\n    'blur': {\n        focusedEditor: Editor | null;\n    };\n    'resize': UIEvent;\n    'scroll': UIEvent;\n    'detach': {};\n    'remove': {};\n    'init': {};\n    'ScrollIntoView': ScrollIntoViewEvent;\n    'AfterScrollIntoView': ScrollIntoViewEvent;\n    'ObjectResized': ObjectResizeEvent;\n    'ObjectResizeStart': ObjectResizeEvent;\n    'SwitchMode': SwitchModeEvent;\n    'ScrollWindow': Event;\n    'ResizeWindow': UIEvent;\n    'SkinLoaded': {};\n    'SkinLoadError': LoadErrorEvent;\n    'PluginLoadError': LoadErrorEvent;\n    'ModelLoadError': LoadErrorEvent;\n    'IconsLoadError': LoadErrorEvent;\n    'ThemeLoadError': LoadErrorEvent;\n    'LanguageLoadError': LoadErrorEvent;\n    'BeforeExecCommand': ExecCommandEvent;\n    'ExecCommand': ExecCommandEvent;\n    'NodeChange': NodeChangeEvent;\n    'FormatApply': FormatEvent;\n    'FormatRemove': FormatEvent;\n    'ShowCaret': ShowCaretEvent;\n    'SelectionChange': {};\n    'ObjectSelected': ObjectSelectedEvent;\n    'BeforeObjectSelected': ObjectSelectedEvent;\n    'GetSelectionRange': {\n        range: Range;\n    };\n    'SetSelectionRange': SetSelectionRangeEvent;\n    'AfterSetSelectionRange': SetSelectionRangeEvent;\n    'BeforeGetContent': BeforeGetContentEvent;\n    'GetContent': GetContentEvent;\n    'BeforeSetContent': BeforeSetContentEvent;\n    'SetContent': SetContentEvent;\n    'SaveContent': SaveContentEvent;\n    'RawSaveContent': SaveContentEvent;\n    'LoadContent': {\n        load: boolean;\n        element: HTMLElement;\n    };\n    'PreviewFormats': {};\n    'AfterPreviewFormats': {};\n    'ScriptsLoaded': {};\n    'PreInit': {};\n    'PostRender': {};\n    'NewBlock': NewBlockEvent;\n    'ClearUndos': {};\n    'TypingUndo': {};\n    'Redo': UndoRedoEvent;\n    'Undo': UndoRedoEvent;\n    'BeforeAddUndo': AddUndoEvent;\n    'AddUndo': AddUndoEvent;\n    'change': ChangeEvent;\n    'CloseWindow': WindowEvent<any>;\n    'OpenWindow': WindowEvent<any>;\n    'ProgressState': ProgressStateEvent;\n    'AfterProgressState': AfterProgressStateEvent;\n    'PlaceholderToggle': PlaceholderToggleEvent;\n    'tap': TouchEvent;\n    'longpress': TouchEvent;\n    'longpresscancel': {};\n    'PreProcess': PreProcessEvent;\n    'PostProcess': PostProcessEvent;\n    'AutocompleterStart': AutocompleterEventArgs;\n    'AutocompleterUpdate': AutocompleterEventArgs;\n    'AutocompleterEnd': {};\n    'PastePlainTextToggle': PastePlainTextToggleEvent;\n    'PastePreProcess': PastePreProcessEvent;\n    'PastePostProcess': PastePostProcessEvent;\n    'TableModified': TableModifiedEvent;\n    'NewRow': NewTableRowEvent;\n    'NewCell': NewTableCellEvent;\n    'SetAttrib': SetAttribEvent;\n    'hide': {};\n    'show': {};\n    'dirty': {};\n    'BeforeOpenNotification': BeforeOpenNotificationEvent;\n    'OpenNotification': OpenNotificationEvent;\n}\ninterface EditorManagerEventMap {\n    'AddEditor': {\n        editor: Editor;\n    };\n    'RemoveEditor': {\n        editor: Editor;\n    };\n    'BeforeUnload': {\n        returnValue: any;\n    };\n}\ntype EventTypes_d_ExecCommandEvent = ExecCommandEvent;\ntype EventTypes_d_BeforeGetContentEvent = BeforeGetContentEvent;\ntype EventTypes_d_GetContentEvent = GetContentEvent;\ntype EventTypes_d_BeforeSetContentEvent = BeforeSetContentEvent;\ntype EventTypes_d_SetContentEvent = SetContentEvent;\ntype EventTypes_d_SaveContentEvent = SaveContentEvent;\ntype EventTypes_d_NewBlockEvent = NewBlockEvent;\ntype EventTypes_d_NodeChangeEvent = NodeChangeEvent;\ntype EventTypes_d_FormatEvent = FormatEvent;\ntype EventTypes_d_ObjectResizeEvent = ObjectResizeEvent;\ntype EventTypes_d_ObjectSelectedEvent = ObjectSelectedEvent;\ntype EventTypes_d_ScrollIntoViewEvent = ScrollIntoViewEvent;\ntype EventTypes_d_SetSelectionRangeEvent = SetSelectionRangeEvent;\ntype EventTypes_d_ShowCaretEvent = ShowCaretEvent;\ntype EventTypes_d_SwitchModeEvent = SwitchModeEvent;\ntype EventTypes_d_ChangeEvent = ChangeEvent;\ntype EventTypes_d_AddUndoEvent = AddUndoEvent;\ntype EventTypes_d_UndoRedoEvent = UndoRedoEvent;\ntype EventTypes_d_WindowEvent<T extends DialogData> = WindowEvent<T>;\ntype EventTypes_d_ProgressStateEvent = ProgressStateEvent;\ntype EventTypes_d_AfterProgressStateEvent = AfterProgressStateEvent;\ntype EventTypes_d_PlaceholderToggleEvent = PlaceholderToggleEvent;\ntype EventTypes_d_LoadErrorEvent = LoadErrorEvent;\ntype EventTypes_d_PreProcessEvent = PreProcessEvent;\ntype EventTypes_d_PostProcessEvent = PostProcessEvent;\ntype EventTypes_d_PastePlainTextToggleEvent = PastePlainTextToggleEvent;\ntype EventTypes_d_PastePreProcessEvent = PastePreProcessEvent;\ntype EventTypes_d_PastePostProcessEvent = PastePostProcessEvent;\ntype EventTypes_d_NewTableRowEvent = NewTableRowEvent;\ntype EventTypes_d_NewTableCellEvent = NewTableCellEvent;\ntype EventTypes_d_TableEventData = TableEventData;\ntype EventTypes_d_TableModifiedEvent = TableModifiedEvent;\ntype EventTypes_d_BeforeOpenNotificationEvent = BeforeOpenNotificationEvent;\ntype EventTypes_d_OpenNotificationEvent = OpenNotificationEvent;\ntype EventTypes_d_EditorEventMap = EditorEventMap;\ntype EventTypes_d_EditorManagerEventMap = EditorManagerEventMap;\ndeclare namespace EventTypes_d {\n    export { EventTypes_d_ExecCommandEvent as ExecCommandEvent, EventTypes_d_BeforeGetContentEvent as BeforeGetContentEvent, EventTypes_d_GetContentEvent as GetContentEvent, EventTypes_d_BeforeSetContentEvent as BeforeSetContentEvent, EventTypes_d_SetContentEvent as SetContentEvent, EventTypes_d_SaveContentEvent as SaveContentEvent, EventTypes_d_NewBlockEvent as NewBlockEvent, EventTypes_d_NodeChangeEvent as NodeChangeEvent, EventTypes_d_FormatEvent as FormatEvent, EventTypes_d_ObjectResizeEvent as ObjectResizeEvent, EventTypes_d_ObjectSelectedEvent as ObjectSelectedEvent, EventTypes_d_ScrollIntoViewEvent as ScrollIntoViewEvent, EventTypes_d_SetSelectionRangeEvent as SetSelectionRangeEvent, EventTypes_d_ShowCaretEvent as ShowCaretEvent, EventTypes_d_SwitchModeEvent as SwitchModeEvent, EventTypes_d_ChangeEvent as ChangeEvent, EventTypes_d_AddUndoEvent as AddUndoEvent, EventTypes_d_UndoRedoEvent as UndoRedoEvent, EventTypes_d_WindowEvent as WindowEvent, EventTypes_d_ProgressStateEvent as ProgressStateEvent, EventTypes_d_AfterProgressStateEvent as AfterProgressStateEvent, EventTypes_d_PlaceholderToggleEvent as PlaceholderToggleEvent, EventTypes_d_LoadErrorEvent as LoadErrorEvent, EventTypes_d_PreProcessEvent as PreProcessEvent, EventTypes_d_PostProcessEvent as PostProcessEvent, EventTypes_d_PastePlainTextToggleEvent as PastePlainTextToggleEvent, EventTypes_d_PastePreProcessEvent as PastePreProcessEvent, EventTypes_d_PastePostProcessEvent as PastePostProcessEvent, EventTypes_d_NewTableRowEvent as NewTableRowEvent, EventTypes_d_NewTableCellEvent as NewTableCellEvent, EventTypes_d_TableEventData as TableEventData, EventTypes_d_TableModifiedEvent as TableModifiedEvent, EventTypes_d_BeforeOpenNotificationEvent as BeforeOpenNotificationEvent, EventTypes_d_OpenNotificationEvent as OpenNotificationEvent, EventTypes_d_EditorEventMap as EditorEventMap, EventTypes_d_EditorManagerEventMap as EditorManagerEventMap, };\n}\ntype Format_d_Formats = Formats;\ntype Format_d_Format = Format;\ntype Format_d_ApplyFormat = ApplyFormat;\ntype Format_d_BlockFormat = BlockFormat;\ntype Format_d_InlineFormat = InlineFormat;\ntype Format_d_SelectorFormat = SelectorFormat;\ntype Format_d_RemoveFormat = RemoveFormat;\ntype Format_d_RemoveBlockFormat = RemoveBlockFormat;\ntype Format_d_RemoveInlineFormat = RemoveInlineFormat;\ntype Format_d_RemoveSelectorFormat = RemoveSelectorFormat;\ndeclare namespace Format_d {\n    export { Format_d_Formats as Formats, Format_d_Format as Format, Format_d_ApplyFormat as ApplyFormat, Format_d_BlockFormat as BlockFormat, Format_d_InlineFormat as InlineFormat, Format_d_SelectorFormat as SelectorFormat, Format_d_RemoveFormat as RemoveFormat, Format_d_RemoveBlockFormat as RemoveBlockFormat, Format_d_RemoveInlineFormat as RemoveInlineFormat, Format_d_RemoveSelectorFormat as RemoveSelectorFormat, };\n}\ndeclare type StyleFormat = BlockStyleFormat | InlineStyleFormat | SelectorStyleFormat;\ndeclare type AllowedFormat = Separator | FormatReference | StyleFormat | NestedFormatting;\ninterface Separator {\n    title: string;\n}\ninterface FormatReference {\n    title: string;\n    format: string;\n    icon?: string;\n}\ninterface NestedFormatting {\n    title: string;\n    items: Array<FormatReference | StyleFormat>;\n}\ninterface CommonStyleFormat {\n    name?: string;\n    title: string;\n    icon?: string;\n}\ninterface BlockStyleFormat extends BlockFormat, CommonStyleFormat {\n}\ninterface InlineStyleFormat extends InlineFormat, CommonStyleFormat {\n}\ninterface SelectorStyleFormat extends SelectorFormat, CommonStyleFormat {\n}\ndeclare type EntityEncoding = 'named' | 'numeric' | 'raw' | 'named,numeric' | 'named+numeric' | 'numeric,named' | 'numeric+named';\ninterface ContentLanguage {\n    readonly title: string;\n    readonly code: string;\n    readonly customCode?: string;\n}\ndeclare type ThemeInitFunc = (editor: Editor, elm: HTMLElement) => {\n    editorContainer: HTMLElement;\n    iframeContainer: HTMLElement;\n    height?: number;\n    iframeHeight?: number;\n    api?: EditorUiApi;\n};\ndeclare type SetupCallback = (editor: Editor) => void;\ndeclare type FilePickerCallback = (callback: (value: string, meta?: Record<string, any>) => void, value: string, meta: Record<string, any>) => void;\ndeclare type FilePickerValidationStatus = 'valid' | 'unknown' | 'invalid' | 'none';\ndeclare type FilePickerValidationCallback = (info: {\n    type: string;\n    url: string;\n}, callback: (validation: {\n    status: FilePickerValidationStatus;\n    message: string;\n}) => void) => void;\ndeclare type PastePreProcessFn = (editor: Editor, args: PastePreProcessEvent) => void;\ndeclare type PastePostProcessFn = (editor: Editor, args: PastePostProcessEvent) => void;\ndeclare type URLConverter = (url: string, name: string, elm?: string | Element) => string;\ndeclare type URLConverterCallback = (url: string, node: Node | string | undefined, on_save: boolean, name: string) => string;\ninterface ToolbarGroup {\n    name?: string;\n    items: string[];\n}\ndeclare type ToolbarMode = 'floating' | 'sliding' | 'scrolling' | 'wrap';\ndeclare type ToolbarLocation = 'top' | 'bottom' | 'auto';\ninterface BaseEditorOptions {\n    a11y_advanced_options?: boolean;\n    add_form_submit_trigger?: boolean;\n    add_unload_trigger?: boolean;\n    allow_conditional_comments?: boolean;\n    allow_html_data_urls?: boolean;\n    allow_html_in_named_anchor?: boolean;\n    allow_script_urls?: boolean;\n    allow_svg_data_urls?: boolean;\n    allow_unsafe_link_target?: boolean;\n    anchor_bottom?: false | string;\n    anchor_top?: false | string;\n    auto_focus?: string | true;\n    automatic_uploads?: boolean;\n    base_url?: string;\n    block_formats?: string;\n    block_unsupported_drop?: boolean;\n    body_id?: string;\n    body_class?: string;\n    br_in_pre?: boolean;\n    br_newline_selector?: string;\n    browser_spellcheck?: boolean;\n    branding?: boolean;\n    cache_suffix?: string;\n    color_cols?: number;\n    color_cols_foreground?: number;\n    color_cols_background?: number;\n    color_map?: string[];\n    color_map_foreground?: string[];\n    color_map_background?: string[];\n    color_default_foreground?: string;\n    color_default_background?: string;\n    content_css?: boolean | string | string[];\n    content_css_cors?: boolean;\n    content_security_policy?: string;\n    content_style?: string;\n    content_langs?: ContentLanguage[];\n    contextmenu?: string | string[] | false;\n    contextmenu_never_use_native?: boolean;\n    convert_fonts_to_spans?: boolean;\n    convert_urls?: boolean;\n    custom_colors?: boolean;\n    custom_elements?: string;\n    custom_ui_selector?: string;\n    custom_undo_redo_levels?: number;\n    deprecation_warnings?: boolean;\n    directionality?: 'ltr' | 'rtl';\n    doctype?: string;\n    document_base_url?: string;\n    draggable_modal?: boolean;\n    editable_class?: string;\n    element_format?: 'xhtml' | 'html';\n    elementpath?: boolean;\n    encoding?: string;\n    end_container_on_empty_block?: boolean | string;\n    entities?: string;\n    entity_encoding?: EntityEncoding;\n    extended_valid_elements?: string;\n    event_root?: string;\n    file_picker_callback?: FilePickerCallback;\n    file_picker_types?: string;\n    file_picker_validator_handler?: FilePickerValidationCallback;\n    fix_list_elements?: boolean;\n    fixed_toolbar_container?: string;\n    fixed_toolbar_container_target?: HTMLElement;\n    font_css?: string | string[];\n    font_family_formats?: string;\n    font_size_classes?: string;\n    font_size_legacy_values?: string;\n    font_size_style_values?: string;\n    font_size_formats?: string;\n    forced_root_block?: string;\n    forced_root_block_attrs?: Record<string, string>;\n    formats?: Formats;\n    format_noneditable_selector?: string;\n    height?: number | string;\n    hidden_input?: boolean;\n    icons?: string;\n    icons_url?: string;\n    id?: string;\n    iframe_aria_text?: string;\n    iframe_attrs?: Record<string, string>;\n    images_file_types?: string;\n    images_replace_blob_uris?: boolean;\n    images_reuse_filename?: boolean;\n    images_upload_base_path?: string;\n    images_upload_credentials?: boolean;\n    images_upload_handler?: UploadHandler;\n    images_upload_url?: string;\n    indent?: boolean;\n    indent_after?: string;\n    indent_before?: string;\n    indent_use_margin?: boolean;\n    indentation?: string;\n    init_instance_callback?: SetupCallback;\n    inline?: boolean;\n    inline_boundaries?: boolean;\n    inline_boundaries_selector?: string;\n    inline_styles?: boolean;\n    invalid_elements?: string;\n    invalid_styles?: string | Record<string, string>;\n    keep_styles?: boolean;\n    language?: string;\n    language_load?: boolean;\n    language_url?: string;\n    line_height_formats?: string;\n    max_height?: number;\n    max_width?: number;\n    menu?: Record<string, {\n        title: string;\n        items: string;\n    }>;\n    menubar?: boolean | string;\n    min_height?: number;\n    min_width?: number;\n    model?: string;\n    model_url?: string;\n    newline_behavior?: 'block' | 'linebreak' | 'invert' | 'default';\n    no_newline_selector?: string;\n    noneditable_class?: string;\n    noneditable_regexp?: RegExp | RegExp[];\n    nowrap?: boolean;\n    object_resizing?: boolean | string;\n    paste_as_text?: boolean;\n    paste_block_drop?: boolean;\n    paste_data_images?: boolean;\n    paste_merge_formats?: boolean;\n    paste_postprocess?: PastePostProcessFn;\n    paste_preprocess?: PastePreProcessFn;\n    paste_remove_styles_if_webkit?: boolean;\n    paste_tab_spaces?: number;\n    paste_webkit_styles?: string;\n    placeholder?: string;\n    preserve_cdata?: boolean;\n    preview_styles?: false | string;\n    promotion?: boolean;\n    protect?: RegExp[];\n    readonly?: boolean;\n    referrer_policy?: ReferrerPolicy;\n    relative_urls?: boolean;\n    remove_script_host?: boolean;\n    remove_trailing_brs?: boolean;\n    removed_menuitems?: string;\n    resize?: boolean | 'both';\n    resize_img_proportional?: boolean;\n    root_name?: string;\n    schema?: SchemaType;\n    selector?: string;\n    setup?: SetupCallback;\n    sidebar_show?: string;\n    skin?: boolean | string;\n    skin_url?: string;\n    smart_paste?: boolean;\n    statusbar?: boolean;\n    style_formats?: AllowedFormat[];\n    style_formats_autohide?: boolean;\n    style_formats_merge?: boolean;\n    submit_patch?: boolean;\n    suffix?: string;\n    table_tab_navigation?: boolean;\n    target?: HTMLElement;\n    text_patterns?: RawPattern[] | false;\n    text_patterns_lookup?: RawDynamicPatternsLookup;\n    theme?: string | ThemeInitFunc | false;\n    theme_url?: string;\n    toolbar?: boolean | string | string[] | Array<ToolbarGroup>;\n    toolbar1?: string;\n    toolbar2?: string;\n    toolbar3?: string;\n    toolbar4?: string;\n    toolbar5?: string;\n    toolbar6?: string;\n    toolbar7?: string;\n    toolbar8?: string;\n    toolbar9?: string;\n    toolbar_groups?: Record<string, GroupToolbarButtonSpec>;\n    toolbar_location?: ToolbarLocation;\n    toolbar_mode?: ToolbarMode;\n    toolbar_sticky?: boolean;\n    toolbar_sticky_offset?: number;\n    typeahead_urls?: boolean;\n    url_converter?: URLConverter;\n    url_converter_scope?: any;\n    urlconverter_callback?: URLConverterCallback;\n    valid_children?: string;\n    valid_classes?: string | Record<string, string>;\n    valid_elements?: string;\n    valid_styles?: string | Record<string, string>;\n    verify_html?: boolean;\n    visual?: boolean;\n    visual_anchor_class?: string;\n    visual_table_class?: string;\n    width?: number | string;\n    disable_nodechange?: boolean;\n    forced_plugins?: string | string[];\n    plugin_base_urls?: Record<string, string>;\n    service_message?: string;\n    [key: string]: any;\n}\ninterface RawEditorOptions extends BaseEditorOptions {\n    external_plugins?: Record<string, string>;\n    mobile?: RawEditorOptions;\n    plugins?: string | string[];\n}\ninterface NormalizedEditorOptions extends BaseEditorOptions {\n    external_plugins: Record<string, string>;\n    forced_plugins: string[];\n    plugins: string[];\n}\ninterface EditorOptions extends NormalizedEditorOptions {\n    a11y_advanced_options: boolean;\n    allow_unsafe_link_target: boolean;\n    anchor_bottom: string;\n    anchor_top: string;\n    automatic_uploads: boolean;\n    block_formats: string;\n    body_class: string;\n    body_id: string;\n    br_newline_selector: string;\n    color_map: string[];\n    color_cols: number;\n    color_cols_foreground: number;\n    color_cols_background: number;\n    color_default_background: string;\n    color_default_foreground: string;\n    content_css: string[];\n    contextmenu: string[];\n    custom_colors: boolean;\n    document_base_url: string;\n    draggable_modal: boolean;\n    editable_class: string;\n    font_css: string[];\n    font_family_formats: string;\n    font_size_classes: string;\n    font_size_formats: string;\n    font_size_legacy_values: string;\n    font_size_style_values: string;\n    forced_root_block: string;\n    forced_root_block_attrs: Record<string, string>;\n    format_noneditable_selector: string;\n    height: number | string;\n    iframe_attrs: Record<string, string>;\n    images_file_types: string;\n    images_upload_base_path: string;\n    images_upload_credentials: boolean;\n    images_upload_url: string;\n    indent_use_margin: boolean;\n    indentation: string;\n    inline: boolean;\n    inline_boundaries_selector: string;\n    language: string;\n    language_load: boolean;\n    language_url: string;\n    line_height_formats: string;\n    menu: Record<string, {\n        title: string;\n        items: string;\n    }>;\n    menubar: boolean | string;\n    model: string;\n    no_newline_selector: string;\n    noneditable_class: string;\n    noneditable_regexp: RegExp[];\n    object_resizing: string;\n    paste_as_text: boolean;\n    preview_styles: string;\n    promotion: boolean;\n    readonly: boolean;\n    removed_menuitems: string;\n    toolbar: boolean | string | string[] | Array<ToolbarGroup>;\n    toolbar_groups: Record<string, GroupToolbarButtonSpec>;\n    toolbar_location: ToolbarLocation;\n    toolbar_mode: ToolbarMode;\n    toolbar_persist: boolean;\n    toolbar_sticky: boolean;\n    toolbar_sticky_offset: number;\n    text_patterns: Pattern[];\n    text_patterns_lookup: DynamicPatternsLookup;\n    visual: boolean;\n    visual_anchor_class: string;\n    visual_table_class: string;\n    width: number | string;\n}\ndeclare type StyleMap = Record<string, string | number>;\ninterface StylesSettings {\n    allow_script_urls?: boolean;\n    allow_svg_data_urls?: boolean;\n    url_converter?: URLConverter;\n    url_converter_scope?: any;\n}\ninterface Styles {\n    parse: (css: string | undefined) => Record<string, string>;\n    serialize: (styles: StyleMap, elementName?: string) => string;\n}\ndeclare type EventUtilsCallback<T> = (event: EventUtilsEvent<T>) => void | boolean;\ndeclare type EventUtilsEvent<T> = NormalizedEvent<T> & {\n    metaKey: boolean;\n};\ninterface Callback$1<T> {\n    func: EventUtilsCallback<T>;\n    scope: any;\n}\ninterface CallbackList<T> extends Array<Callback$1<T>> {\n    fakeName: string | false;\n    capture: boolean;\n    nativeHandler: EventListener;\n}\ninterface EventUtilsConstructor {\n    readonly prototype: EventUtils;\n    new (): EventUtils;\n    Event: EventUtils;\n}\ndeclare class EventUtils {\n    static Event: EventUtils;\n    domLoaded: boolean;\n    events: Record<number, Record<string, CallbackList<any>>>;\n    private readonly expando;\n    private hasFocusIn;\n    private count;\n    constructor();\n    bind<K extends keyof HTMLElementEventMap>(target: any, name: K, callback: EventUtilsCallback<HTMLElementEventMap[K]>, scope?: any): EventUtilsCallback<HTMLElementEventMap[K]>;\n    bind<T = any>(target: any, names: string, callback: EventUtilsCallback<T>, scope?: any): EventUtilsCallback<T>;\n    unbind<K extends keyof HTMLElementEventMap>(target: any, name: K, callback?: EventUtilsCallback<HTMLElementEventMap[K]>): this;\n    unbind<T = any>(target: any, names: string, callback?: EventUtilsCallback<T>): this;\n    unbind(target: any): this;\n    fire(target: any, name: string, args?: {}): this;\n    dispatch(target: any, name: string, args?: {}): this;\n    clean(target: any): this;\n    destroy(): void;\n    cancel<T>(e: EventUtilsEvent<T>): boolean;\n    private executeHandlers;\n}\ninterface SetAttribEvent {\n    attrElm: HTMLElement;\n    attrName: string;\n    attrValue: string | boolean | number | null;\n}\ninterface DOMUtilsSettings {\n    schema: Schema;\n    url_converter: URLConverter;\n    url_converter_scope: any;\n    ownEvents: boolean;\n    keep_values: boolean;\n    update_styles: boolean;\n    root_element: HTMLElement | null;\n    collect: boolean;\n    onSetAttrib: (event: SetAttribEvent) => void;\n    contentCssCors: boolean;\n    referrerPolicy: ReferrerPolicy;\n}\ndeclare type Target = Node | Window;\ndeclare type RunArguments<T extends Node = Node> = string | T | Array<string | T> | null;\ndeclare type BoundEvent = [\n    Target,\n    string,\n    EventUtilsCallback<any>,\n    any\n];\ndeclare type Callback<K extends string> = EventUtilsCallback<MappedEvent<HTMLElementEventMap, K>>;\ndeclare type RunResult<T, R> = T extends Array<any> ? R[] : false | R;\ninterface DOMUtils {\n    doc: Document;\n    settings: Partial<DOMUtilsSettings>;\n    win: Window;\n    files: Record<string, boolean>;\n    stdMode: boolean;\n    boxModel: boolean;\n    styleSheetLoader: StyleSheetLoader;\n    boundEvents: BoundEvent[];\n    styles: Styles;\n    schema: Schema;\n    events: EventUtils;\n    root: Node | null;\n    isBlock: {\n        (node: Node | null): node is HTMLElement;\n        (node: string): boolean;\n    };\n    clone: (node: Node, deep: boolean) => Node;\n    getRoot: () => HTMLElement;\n    getViewPort: (argWin?: Window) => GeomRect;\n    getRect: (elm: string | HTMLElement) => GeomRect;\n    getSize: (elm: string | HTMLElement) => {\n        w: number;\n        h: number;\n    };\n    getParent: {\n        <K extends keyof HTMLElementTagNameMap>(node: string | Node | null, selector: K, root?: Node): HTMLElementTagNameMap[K] | null;\n        <T extends Element>(node: string | Node | null, selector: string | ((node: Node) => node is T), root?: Node): T | null;\n        (node: string | Node | null, selector?: string | ((node: Node) => boolean | void), root?: Node): Node | null;\n    };\n    getParents: {\n        <K extends keyof HTMLElementTagNameMap>(elm: string | HTMLElementTagNameMap[K] | null, selector: K, root?: Node, collect?: boolean): Array<HTMLElementTagNameMap[K]>;\n        <T extends Element>(node: string | Node | null, selector: string | ((node: Node) => node is T), root?: Node, collect?: boolean): T[];\n        (elm: string | Node | null, selector?: string | ((node: Node) => boolean | void), root?: Node, collect?: boolean): Node[];\n    };\n    get: {\n        <T extends Node>(elm: T): T;\n        (elm: string): HTMLElement | null;\n    };\n    getNext: (node: Node | null, selector: string | ((node: Node) => boolean)) => Node | null;\n    getPrev: (node: Node | null, selector: string | ((node: Node) => boolean)) => Node | null;\n    select: {\n        <K extends keyof HTMLElementTagNameMap>(selector: K, scope?: string | Node): Array<HTMLElementTagNameMap[K]>;\n        <T extends HTMLElement = HTMLElement>(selector: string, scope?: string | Node): T[];\n    };\n    is: {\n        <T extends Element>(elm: Node | Node[] | null, selector: string): elm is T;\n        (elm: Node | Node[] | null, selector: string): boolean;\n    };\n    add: (parentElm: RunArguments, name: string | Element, attrs?: Record<string, string | boolean | number | null>, html?: string | Node | null, create?: boolean) => HTMLElement;\n    create: {\n        <K extends keyof HTMLElementTagNameMap>(name: K, attrs?: Record<string, string | boolean | number | null>, html?: string | Node | null): HTMLElementTagNameMap[K];\n        (name: string, attrs?: Record<string, string | boolean | number | null>, html?: string | Node | null): HTMLElement;\n    };\n    createHTML: (name: string, attrs?: Record<string, string | null>, html?: string) => string;\n    createFragment: (html?: string) => DocumentFragment;\n    remove: {\n        <T extends Node>(node: T | T[], keepChildren?: boolean): typeof node extends Array<any> ? T[] : T;\n        <T extends Node>(node: string, keepChildren?: boolean): T | false;\n    };\n    getStyle: {\n        (elm: Element, name: string, computed: true): string;\n        (elm: string | Element | null, name: string, computed?: boolean): string | undefined;\n    };\n    setStyle: (elm: string | Element | Element[], name: string, value: string | number | null) => void;\n    setStyles: (elm: string | Element | Element[], stylesArg: StyleMap) => void;\n    removeAllAttribs: (e: RunArguments<Element>) => void;\n    setAttrib: (elm: RunArguments<Element>, name: string, value: string | boolean | number | null) => void;\n    setAttribs: (elm: RunArguments<Element>, attrs: Record<string, string | boolean | number | null>) => void;\n    getAttrib: (elm: string | Element | null, name: string, defaultVal?: string) => string;\n    getAttribs: (elm: string | Element) => NamedNodeMap | Attr[];\n    getPos: (elm: string | Element, rootElm?: Node) => {\n        x: number;\n        y: number;\n    };\n    parseStyle: (cssText: string) => Record<string, string>;\n    serializeStyle: (stylesArg: StyleMap, name?: string) => string;\n    addStyle: (cssText: string) => void;\n    loadCSS: (url: string) => void;\n    hasClass: (elm: string | Element, cls: string) => boolean;\n    addClass: (elm: RunArguments<Element>, cls: string) => void;\n    removeClass: (elm: RunArguments<Element>, cls: string) => void;\n    toggleClass: (elm: RunArguments<Element>, cls: string, state?: boolean) => void;\n    show: (elm: string | Node | Node[]) => void;\n    hide: (elm: string | Node | Node[]) => void;\n    isHidden: (elm: string | Node) => boolean;\n    uniqueId: (prefix?: string) => string;\n    setHTML: (elm: RunArguments<Element>, html: string) => void;\n    getOuterHTML: (elm: string | Node) => string;\n    setOuterHTML: (elm: string | Node | Node[], html: string) => void;\n    decode: (text: string) => string;\n    encode: (text: string) => string;\n    insertAfter: {\n        <T extends Node>(node: T | T[], reference: string | Node): T;\n        <T extends Node>(node: RunArguments<T>, reference: string | Node): RunResult<typeof node, T>;\n    };\n    replace: {\n        <T extends Node>(newElm: Node, oldElm: T | T[], keepChildren?: boolean): T;\n        <T extends Node>(newElm: Node, oldElm: RunArguments<T>, keepChildren?: boolean): false | T;\n    };\n    rename: {\n        <K extends keyof HTMLElementTagNameMap>(elm: Element, name: K): HTMLElementTagNameMap[K];\n        (elm: Element, name: string): Element;\n    };\n    findCommonAncestor: (a: Node, b: Node) => Node | null;\n    run<R, T extends Node>(this: DOMUtils, elm: T | T[], func: (node: T) => R, scope?: any): typeof elm extends Array<any> ? R[] : R;\n    run<R, T extends Node>(this: DOMUtils, elm: RunArguments<T>, func: (node: T) => R, scope?: any): RunResult<typeof elm, R>;\n    isEmpty: (node: Node, elements?: Record<string, any>) => boolean;\n    createRng: () => Range;\n    nodeIndex: (node: Node, normalized?: boolean) => number;\n    split: {\n        <T extends Node>(parentElm: Node, splitElm: Node, replacementElm: T): T | undefined;\n        <T extends Node>(parentElm: Node, splitElm: T): T | undefined;\n    };\n    bind: {\n        <K extends string>(target: Target, name: K, func: Callback<K>, scope?: any): Callback<K>;\n        <K extends string>(target: Target[], name: K, func: Callback<K>, scope?: any): Callback<K>[];\n    };\n    unbind: {\n        <K extends string>(target: Target, name?: K, func?: EventUtilsCallback<MappedEvent<HTMLElementEventMap, K>>): EventUtils;\n        <K extends string>(target: Target[], name?: K, func?: EventUtilsCallback<MappedEvent<HTMLElementEventMap, K>>): EventUtils[];\n    };\n    fire: (target: Node | Window, name: string, evt?: {}) => EventUtils;\n    dispatch: (target: Node | Window, name: string, evt?: {}) => EventUtils;\n    getContentEditable: (node: Node) => string | null;\n    getContentEditableParent: (node: Node) => string | null;\n    destroy: () => void;\n    isChildOf: (node: Node, parent: Node) => boolean;\n    dumpRng: (r: Range) => string;\n}\ninterface ClientRect {\n    left: number;\n    top: number;\n    bottom: number;\n    right: number;\n    width: number;\n    height: number;\n}\ninterface BookmarkManager {\n    getBookmark: (type?: number, normalized?: boolean) => Bookmark;\n    moveToBookmark: (bookmark: Bookmark) => void;\n}\ninterface ControlSelection {\n    isResizable: (elm: Element) => boolean;\n    showResizeRect: (elm: HTMLElement) => void;\n    hideResizeRect: () => void;\n    updateResizeRect: (evt: EditorEvent<any>) => void;\n    destroy: () => void;\n}\ninterface WriterSettings {\n    element_format?: 'xhtml' | 'html';\n    entities?: string;\n    entity_encoding?: EntityEncoding;\n    indent?: boolean;\n    indent_after?: string;\n    indent_before?: string;\n}\ndeclare type Attributes = Array<{\n    name: string;\n    value: string;\n}>;\ninterface Writer {\n    cdata: (text: string) => void;\n    comment: (text: string) => void;\n    doctype: (text: string) => void;\n    end: (name: string) => void;\n    getContent: () => string;\n    pi: (name: string, text?: string) => void;\n    reset: () => void;\n    start: (name: string, attrs?: Attributes | null, empty?: boolean) => void;\n    text: (text: string, raw?: boolean) => void;\n}\ninterface HtmlSerializerSettings extends WriterSettings {\n    inner?: boolean;\n    validate?: boolean;\n}\ninterface HtmlSerializer {\n    serialize: (node: AstNode) => string;\n}\ninterface DomSerializerSettings extends DomParserSettings, WriterSettings, SchemaSettings, HtmlSerializerSettings {\n    url_converter?: URLConverter;\n    url_converter_scope?: {};\n}\ninterface DomSerializerImpl {\n    schema: Schema;\n    addNodeFilter: (name: string, callback: ParserFilterCallback) => void;\n    addAttributeFilter: (name: string, callback: ParserFilterCallback) => void;\n    getNodeFilters: () => ParserFilter[];\n    getAttributeFilters: () => ParserFilter[];\n    removeNodeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    removeAttributeFilter: (name: string, callback?: ParserFilterCallback) => void;\n    serialize: {\n        (node: Element, parserArgs: {\n            format: 'tree';\n        } & ParserArgs): AstNode;\n        (node: Element, parserArgs?: ParserArgs): string;\n    };\n    addRules: (rules: string) => void;\n    setRules: (rules: string) => void;\n    addTempAttr: (name: string) => void;\n    getTempAttrs: () => string[];\n}\ninterface DomSerializer extends DomSerializerImpl {\n}\ninterface EditorSelection {\n    bookmarkManager: BookmarkManager;\n    controlSelection: ControlSelection;\n    dom: DOMUtils;\n    win: Window;\n    serializer: DomSerializer;\n    editor: Editor;\n    collapse: (toStart?: boolean) => void;\n    setCursorLocation: {\n        (node: Node, offset: number): void;\n        (): void;\n    };\n    getContent: {\n        (args: {\n            format: 'tree';\n        } & Partial<GetSelectionContentArgs>): AstNode;\n        (args?: Partial<GetSelectionContentArgs>): string;\n    };\n    setContent: (content: string, args?: Partial<SetSelectionContentArgs>) => void;\n    getBookmark: (type?: number, normalized?: boolean) => Bookmark;\n    moveToBookmark: (bookmark: Bookmark) => void;\n    select: (node: Node, content?: boolean) => Node;\n    isCollapsed: () => boolean;\n    isForward: () => boolean;\n    setNode: (elm: Element) => Element;\n    getNode: () => HTMLElement;\n    getSel: () => Selection | null;\n    setRng: (rng: Range, forward?: boolean) => void;\n    getRng: () => Range;\n    getStart: (real?: boolean) => Element;\n    getEnd: (real?: boolean) => Element;\n    getSelectedBlocks: (startElm?: Element, endElm?: Element) => Element[];\n    normalize: () => Range;\n    selectorChanged: (selector: string, callback: (active: boolean, args: {\n        node: Node;\n        selector: String;\n        parents: Node[];\n    }) => void) => EditorSelection;\n    selectorChangedWithUnbind: (selector: string, callback: (active: boolean, args: {\n        node: Node;\n        selector: String;\n        parents: Node[];\n    }) => void) => {\n        unbind: () => void;\n    };\n    getScrollContainer: () => HTMLElement | undefined;\n    scrollIntoView: (elm?: HTMLElement, alignToTop?: boolean) => void;\n    placeCaretAt: (clientX: number, clientY: number) => void;\n    getBoundingClientRect: () => ClientRect | DOMRect;\n    destroy: () => void;\n    expand: (options?: {\n        type: 'word';\n    }) => void;\n}\ndeclare type EditorCommandCallback<S> = (this: S, ui: boolean, value: any) => void;\ndeclare type EditorCommandsCallback = (command: string, ui: boolean, value?: any) => void;\ninterface Commands {\n    state: Record<string, (command: string) => boolean>;\n    exec: Record<string, EditorCommandsCallback>;\n    value: Record<string, (command: string) => string>;\n}\ninterface ExecCommandArgs {\n    skip_focus?: boolean;\n}\ninterface EditorCommandsConstructor {\n    readonly prototype: EditorCommands;\n    new (editor: Editor): EditorCommands;\n}\ndeclare class EditorCommands {\n    private readonly editor;\n    private commands;\n    constructor(editor: Editor);\n    execCommand(command: string, ui?: boolean, value?: any, args?: ExecCommandArgs): boolean;\n    queryCommandState(command: string): boolean;\n    queryCommandValue(command: string): string;\n    addCommands<K extends keyof Commands>(commandList: Commands[K], type: K): void;\n    addCommands(commandList: Record<string, EditorCommandsCallback>): void;\n    addCommand<S>(command: string, callback: EditorCommandCallback<S>, scope: S): void;\n    addCommand(command: string, callback: EditorCommandCallback<Editor>): void;\n    queryCommandSupported(command: string): boolean;\n    addQueryStateHandler<S>(command: string, callback: (this: S) => boolean, scope: S): void;\n    addQueryStateHandler(command: string, callback: (this: Editor) => boolean): void;\n    addQueryValueHandler<S>(command: string, callback: (this: S) => string, scope: S): void;\n    addQueryValueHandler(command: string, callback: (this: Editor) => string): void;\n}\ninterface RawString {\n    raw: string;\n}\ndeclare type Primitive = string | number | boolean | Record<string | number, any> | Function;\ndeclare type TokenisedString = [\n    string,\n    ...Primitive[]\n];\ndeclare type Untranslated = Primitive | TokenisedString | RawString | null | undefined;\ndeclare type TranslatedString = string;\ninterface I18n {\n    getData: () => Record<string, Record<string, string>>;\n    setCode: (newCode: string) => void;\n    getCode: () => string;\n    add: (code: string, items: Record<string, string>) => void;\n    translate: (text: Untranslated) => TranslatedString;\n    isRtl: () => boolean;\n    hasCode: (code: string) => boolean;\n}\ninterface Observable<T extends {}> {\n    fire<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U, bubble?: boolean): EditorEvent<U>;\n    dispatch<K extends string, U extends MappedEvent<T, K>>(name: K, args?: U, bubble?: boolean): EditorEvent<U>;\n    on<K extends string>(name: K, callback: (event: EditorEvent<MappedEvent<T, K>>) => void, prepend?: boolean): EventDispatcher<T>;\n    off<K extends string>(name?: K, callback?: (event: EditorEvent<MappedEvent<T, K>>) => void): EventDispatcher<T>;\n    once<K extends string>(name: K, callback: (event: EditorEvent<MappedEvent<T, K>>) => void): EventDispatcher<T>;\n    hasEventListeners(name: string): boolean;\n}\ninterface URISettings {\n    base_uri?: URI;\n}\ninterface URIConstructor {\n    readonly prototype: URI;\n    new (url: string, settings?: URISettings): URI;\n    getDocumentBaseUrl: (loc: {\n        protocol: string;\n        host?: string;\n        href?: string;\n        pathname?: string;\n    }) => string;\n    parseDataUri: (uri: string) => {\n        type: string;\n        data: string;\n    };\n}\ninterface SafeUriOptions {\n    readonly allow_html_data_urls?: boolean;\n    readonly allow_script_urls?: boolean;\n    readonly allow_svg_data_urls?: boolean;\n}\ndeclare class URI {\n    static parseDataUri(uri: string): {\n        type: string | undefined;\n        data: string;\n    };\n    static isDomSafe(uri: string, context?: string, options?: SafeUriOptions): boolean;\n    static getDocumentBaseUrl(loc: {\n        protocol: string;\n        host?: string;\n        href?: string;\n        pathname?: string;\n    }): string;\n    source: string;\n    protocol: string | undefined;\n    authority: string | undefined;\n    userInfo: string | undefined;\n    user: string | undefined;\n    password: string | undefined;\n    host: string | undefined;\n    port: string | undefined;\n    relative: string | undefined;\n    path: string;\n    directory: string;\n    file: string | undefined;\n    query: string | undefined;\n    anchor: string | undefined;\n    settings: URISettings;\n    constructor(url: string, settings?: URISettings);\n    setPath(path: string): void;\n    toRelative(uri: string): string;\n    toAbsolute(uri: string, noHost?: boolean): string;\n    isSameOrigin(uri: URI): boolean;\n    toRelPath(base: string, path: string): string;\n    toAbsPath(base: string, path: string): string;\n    getURI(noProtoHost?: boolean): string;\n}\ninterface EditorManager extends Observable<EditorManagerEventMap> {\n    defaultOptions: RawEditorOptions;\n    majorVersion: string;\n    minorVersion: string;\n    releaseDate: string;\n    activeEditor: Editor | null;\n    focusedEditor: Editor | null;\n    baseURI: URI;\n    baseURL: string;\n    documentBaseURL: string;\n    i18n: I18n;\n    suffix: string;\n    add(this: EditorManager, editor: Editor): Editor;\n    addI18n: (code: string, item: Record<string, string>) => void;\n    createEditor(this: EditorManager, id: string, options: RawEditorOptions): Editor;\n    execCommand(this: EditorManager, cmd: string, ui: boolean, value: any): boolean;\n    get(this: EditorManager): Editor[];\n    get(this: EditorManager, id: number | string): Editor | null;\n    init(this: EditorManager, options: RawEditorOptions): Promise<Editor[]>;\n    overrideDefaults(this: EditorManager, defaultOptions: Partial<RawEditorOptions>): void;\n    remove(this: EditorManager): void;\n    remove(this: EditorManager, selector: string): void;\n    remove(this: EditorManager, editor: Editor): Editor | null;\n    setActive(this: EditorManager, editor: Editor): void;\n    setup(this: EditorManager): void;\n    translate: (text: Untranslated) => TranslatedString;\n    triggerSave: () => void;\n    _setBaseUrl(this: EditorManager, baseUrl: string): void;\n}\ninterface EditorObservable extends Observable<EditorEventMap> {\n    bindPendingEventDelegates(this: Editor): void;\n    toggleNativeEvent(this: Editor, name: string, state: boolean): void;\n    unbindAllNativeEvents(this: Editor): void;\n}\ninterface ProcessorSuccess<T> {\n    valid: true;\n    value: T;\n}\ninterface ProcessorError {\n    valid: false;\n    message: string;\n}\ndeclare type SimpleProcessor = (value: unknown) => boolean;\ndeclare type Processor<T> = (value: unknown) => ProcessorSuccess<T> | ProcessorError;\ninterface BuiltInOptionTypeMap {\n    'string': string;\n    'number': number;\n    'boolean': boolean;\n    'array': any[];\n    'function': Function;\n    'object': any;\n    'string[]': string[];\n    'object[]': any[];\n    'regexp': RegExp;\n}\ndeclare type BuiltInOptionType = keyof BuiltInOptionTypeMap;\ninterface BaseOptionSpec {\n    immutable?: boolean;\n    deprecated?: boolean;\n    docsUrl?: string;\n}\ninterface BuiltInOptionSpec<K extends BuiltInOptionType> extends BaseOptionSpec {\n    processor: K;\n    default?: BuiltInOptionTypeMap[K];\n}\ninterface SimpleOptionSpec<T> extends BaseOptionSpec {\n    processor: SimpleProcessor;\n    default?: T;\n}\ninterface OptionSpec<T, U> extends BaseOptionSpec {\n    processor: Processor<U>;\n    default?: T;\n}\ninterface Options {\n    register: {\n        <K extends BuiltInOptionType>(name: string, spec: BuiltInOptionSpec<K>): void;\n        <K extends keyof NormalizedEditorOptions>(name: K, spec: OptionSpec<NormalizedEditorOptions[K], EditorOptions[K]> | SimpleOptionSpec<NormalizedEditorOptions[K]>): void;\n        <T, U>(name: string, spec: OptionSpec<T, U>): void;\n        <T>(name: string, spec: SimpleOptionSpec<T>): void;\n    };\n    isRegistered: (name: string) => boolean;\n    get: {\n        <K extends keyof EditorOptions>(name: K): EditorOptions[K];\n        <T>(name: string): T | undefined;\n    };\n    set: <K extends string, T>(name: K, value: K extends keyof NormalizedEditorOptions ? NormalizedEditorOptions[K] : T) => boolean;\n    unset: (name: string) => boolean;\n    isSet: (name: string) => boolean;\n}\ninterface UploadResult$1 {\n    element: HTMLImageElement;\n    status: boolean;\n    blobInfo: BlobInfo;\n    uploadUri: string;\n    removed: boolean;\n}\ninterface EditorUpload {\n    blobCache: BlobCache;\n    addFilter: (filter: (img: HTMLImageElement) => boolean) => void;\n    uploadImages: () => Promise<UploadResult$1[]>;\n    uploadImagesAuto: () => Promise<UploadResult$1[]>;\n    scanForImages: () => Promise<BlobInfoImagePair[]>;\n    destroy: () => void;\n}\ndeclare type FormatChangeCallback = (state: boolean, data: {\n    node: Node;\n    format: string;\n    parents: Element[];\n}) => void;\ninterface FormatRegistry {\n    get: {\n        (name: string): Format[] | undefined;\n        (): Record<string, Format[]>;\n    };\n    has: (name: string) => boolean;\n    register: (name: string | Formats, format?: Format[] | Format) => void;\n    unregister: (name: string) => Formats;\n}\ninterface Formatter extends FormatRegistry {\n    apply: (name: string, vars?: FormatVars, node?: Node | RangeLikeObject | null) => void;\n    remove: (name: string, vars?: FormatVars, node?: Node | Range, similar?: boolean) => void;\n    toggle: (name: string, vars?: FormatVars, node?: Node) => void;\n    match: (name: string, vars?: FormatVars, node?: Node, similar?: boolean) => boolean;\n    closest: (names: string[]) => string | null;\n    matchAll: (names: string[], vars?: FormatVars) => string[];\n    matchNode: (node: Node | null, name: string, vars?: FormatVars, similar?: boolean) => Format | undefined;\n    canApply: (name: string) => boolean;\n    formatChanged: (names: string, callback: FormatChangeCallback, similar?: boolean, vars?: FormatVars) => {\n        unbind: () => void;\n    };\n    getCssText: (format: string | ApplyFormat) => string;\n}\ninterface EditorMode {\n    isReadOnly: () => boolean;\n    set: (mode: string) => void;\n    get: () => string;\n    register: (mode: string, api: EditorModeApi) => void;\n}\ninterface EditorModeApi {\n    activate: () => void;\n    deactivate: () => void;\n    editorReadOnly: boolean;\n}\ninterface Model {\n    readonly table: {\n        readonly getSelectedCells: () => HTMLTableCellElement[];\n        readonly clearSelectedCells: (container: Node) => void;\n    };\n}\ndeclare type ModelManager = AddOnManager<Model>;\ninterface Plugin {\n    getMetadata?: () => {\n        name: string;\n        url: string;\n    };\n    init?: (editor: Editor, url: string) => void;\n    [key: string]: any;\n}\ndeclare type PluginManager = AddOnManager<void | Plugin>;\ninterface ShortcutsConstructor {\n    readonly prototype: Shortcuts;\n    new (editor: Editor): Shortcuts;\n}\ndeclare type CommandFunc = string | [\n    string,\n    boolean,\n    any\n] | (() => void);\ndeclare class Shortcuts {\n    private readonly editor;\n    private readonly shortcuts;\n    private pendingPatterns;\n    constructor(editor: Editor);\n    add(pattern: string, desc: string | null, cmdFunc: CommandFunc, scope?: any): boolean;\n    remove(pattern: string): boolean;\n    private normalizeCommandFunc;\n    private createShortcut;\n    private hasModifier;\n    private isFunctionKey;\n    private matchShortcut;\n    private executeShortcutAction;\n}\ninterface RenderResult {\n    iframeContainer?: HTMLElement;\n    editorContainer: HTMLElement;\n    api?: Partial<EditorUiApi>;\n}\ninterface Theme {\n    ui?: any;\n    inline?: any;\n    execCommand?: (command: string, ui?: boolean, value?: any) => boolean;\n    destroy?: () => void;\n    init?: (editor: Editor, url: string) => void;\n    renderUI?: () => RenderResult;\n    getNotificationManagerImpl?: () => NotificationManagerImpl;\n    getWindowManagerImpl?: () => WindowManagerImpl;\n}\ndeclare type ThemeManager = AddOnManager<void | Theme>;\ninterface EditorConstructor {\n    readonly prototype: Editor;\n    new (id: string, options: RawEditorOptions, editorManager: EditorManager): Editor;\n}\ndeclare class Editor implements EditorObservable {\n    documentBaseUrl: string;\n    baseUri: URI;\n    id: string;\n    plugins: Record<string, Plugin>;\n    documentBaseURI: URI;\n    baseURI: URI;\n    contentCSS: string[];\n    contentStyles: string[];\n    ui: EditorUi;\n    mode: EditorMode;\n    options: Options;\n    shortcuts: Shortcuts;\n    loadedCSS: Record<string, any>;\n    editorCommands: EditorCommands;\n    suffix: string;\n    editorManager: EditorManager;\n    hidden: boolean;\n    inline: boolean;\n    hasVisual: boolean;\n    isNotDirty: boolean;\n    annotator: Annotator;\n    bodyElement: HTMLElement | undefined;\n    bookmark: any;\n    composing: boolean;\n    container: HTMLElement;\n    contentAreaContainer: HTMLElement;\n    contentDocument: Document;\n    contentWindow: Window;\n    delegates: Record<string, EventUtilsCallback<any>> | undefined;\n    destroyed: boolean;\n    dom: DOMUtils;\n    editorContainer: HTMLElement;\n    editorUpload: EditorUpload;\n    eventRoot: Element | undefined;\n    formatter: Formatter;\n    formElement: HTMLElement | undefined;\n    formEventDelegate: ((e: Event) => void) | undefined;\n    hasHiddenInput: boolean;\n    iframeElement: HTMLIFrameElement | null;\n    iframeHTML: string | undefined;\n    initialized: boolean;\n    notificationManager: NotificationManager;\n    orgDisplay: string;\n    orgVisibility: string | undefined;\n    parser: DomParser;\n    quirks: Quirks;\n    readonly: boolean;\n    removed: boolean;\n    schema: Schema;\n    selection: EditorSelection;\n    serializer: DomSerializer;\n    startContent: string;\n    targetElm: HTMLElement;\n    theme: Theme;\n    model: Model;\n    undoManager: UndoManager;\n    windowManager: WindowManager;\n    _beforeUnload: (() => void) | undefined;\n    _eventDispatcher: EventDispatcher<NativeEventMap> | undefined;\n    _nodeChangeDispatcher: NodeChange;\n    _pendingNativeEvents: string[];\n    _selectionOverrides: SelectionOverrides;\n    _skinLoaded: boolean;\n    bindPendingEventDelegates: EditorObservable['bindPendingEventDelegates'];\n    toggleNativeEvent: EditorObservable['toggleNativeEvent'];\n    unbindAllNativeEvents: EditorObservable['unbindAllNativeEvents'];\n    fire: EditorObservable['fire'];\n    dispatch: EditorObservable['dispatch'];\n    on: EditorObservable['on'];\n    off: EditorObservable['off'];\n    once: EditorObservable['once'];\n    hasEventListeners: EditorObservable['hasEventListeners'];\n    constructor(id: string, options: RawEditorOptions, editorManager: EditorManager);\n    render(): void;\n    focus(skipFocus?: boolean): void;\n    hasFocus(): boolean;\n    translate(text: Untranslated): TranslatedString;\n    getParam<K extends BuiltInOptionType>(name: string, defaultVal: BuiltInOptionTypeMap[K], type: K): BuiltInOptionTypeMap[K];\n    getParam<K extends keyof NormalizedEditorOptions>(name: K, defaultVal?: NormalizedEditorOptions[K], type?: BuiltInOptionType): NormalizedEditorOptions[K];\n    getParam<T>(name: string, defaultVal: T, type?: BuiltInOptionType): T;\n    hasPlugin(name: string, loaded?: boolean): boolean;\n    nodeChanged(args?: any): void;\n    addCommand<S>(name: string, callback: EditorCommandCallback<S>, scope: S): void;\n    addCommand(name: string, callback: EditorCommandCallback<Editor>): void;\n    addQueryStateHandler<S>(name: string, callback: (this: S) => boolean, scope?: S): void;\n    addQueryStateHandler(name: string, callback: (this: Editor) => boolean): void;\n    addQueryValueHandler<S>(name: string, callback: (this: S) => string, scope: S): void;\n    addQueryValueHandler(name: string, callback: (this: Editor) => string): void;\n    addShortcut(pattern: string, desc: string, cmdFunc: string | [\n        string,\n        boolean,\n        any\n    ] | (() => void), scope?: any): void;\n    execCommand(cmd: string, ui?: boolean, value?: any, args?: ExecCommandArgs): boolean;\n    queryCommandState(cmd: string): boolean;\n    queryCommandValue(cmd: string): string;\n    queryCommandSupported(cmd: string): boolean;\n    show(): void;\n    hide(): void;\n    isHidden(): boolean;\n    setProgressState(state: boolean, time?: number): void;\n    load(args?: Partial<SetContentArgs>): string;\n    save(args?: Partial<GetContentArgs>): string;\n    setContent(content: string, args?: Partial<SetContentArgs>): string;\n    setContent(content: AstNode, args?: Partial<SetContentArgs>): AstNode;\n    setContent(content: Content, args?: Partial<SetContentArgs>): Content;\n    getContent(args: {\n        format: 'tree';\n    } & Partial<GetContentArgs>): AstNode;\n    getContent(args?: Partial<GetContentArgs>): string;\n    insertContent(content: string, args?: any): void;\n    resetContent(initialContent?: string): void;\n    isDirty(): boolean;\n    setDirty(state: boolean): void;\n    getContainer(): HTMLElement;\n    getContentAreaContainer(): HTMLElement;\n    getElement(): HTMLElement;\n    getWin(): Window;\n    getDoc(): Document;\n    getBody(): HTMLElement;\n    convertURL(url: string, name: string, elm?: string | Element): string;\n    addVisual(elm?: HTMLElement): void;\n    remove(): void;\n    destroy(automatic?: boolean): void;\n    uploadImages(): Promise<UploadResult$1[]>;\n    _scanForImages(): Promise<BlobInfoImagePair[]>;\n}\ninterface UrlObject {\n    prefix: string;\n    resource: string;\n    suffix: string;\n}\ndeclare type WaitState = 'added' | 'loaded';\ndeclare type AddOnConstructor<T> = (editor: Editor, url: string) => T;\ninterface AddOnManager<T> {\n    items: AddOnConstructor<T>[];\n    urls: Record<string, string>;\n    lookup: Record<string, {\n        instance: AddOnConstructor<T>;\n    }>;\n    get: (name: string) => AddOnConstructor<T> | undefined;\n    requireLangPack: (name: string, languages?: string) => void;\n    add: (id: string, addOn: AddOnConstructor<T>) => AddOnConstructor<T>;\n    remove: (name: string) => void;\n    createUrl: (baseUrl: UrlObject, dep: string | UrlObject) => UrlObject;\n    load: (name: string, addOnUrl: string | UrlObject) => Promise<void>;\n    waitFor: (name: string, state?: WaitState) => Promise<void>;\n}\ninterface RangeUtils {\n    walk: (rng: Range, callback: (nodes: Node[]) => void) => void;\n    split: (rng: Range) => RangeLikeObject;\n    normalize: (rng: Range) => boolean;\n    expand: (rng: Range, options?: {\n        type: 'word';\n    }) => Range;\n}\ninterface ScriptLoaderSettings {\n    referrerPolicy?: ReferrerPolicy;\n}\ninterface ScriptLoaderConstructor {\n    readonly prototype: ScriptLoader;\n    new (): ScriptLoader;\n    ScriptLoader: ScriptLoader;\n}\ndeclare class ScriptLoader {\n    static ScriptLoader: ScriptLoader;\n    private settings;\n    private states;\n    private queue;\n    private scriptLoadedCallbacks;\n    private queueLoadedCallbacks;\n    private loading;\n    constructor(settings?: ScriptLoaderSettings);\n    _setReferrerPolicy(referrerPolicy: ReferrerPolicy): void;\n    loadScript(url: string): Promise<void>;\n    isDone(url: string): boolean;\n    markDone(url: string): void;\n    add(url: string): Promise<void>;\n    load(url: string): Promise<void>;\n    remove(url: string): void;\n    loadQueue(): Promise<void>;\n    loadScripts(scripts: string[]): Promise<void>;\n}\ndeclare type TextProcessCallback = (node: Text, offset: number, text: string) => number;\ninterface Spot {\n    container: Text;\n    offset: number;\n}\ninterface TextSeeker {\n    backwards: (node: Node, offset: number, process: TextProcessCallback, root?: Node) => Spot | null;\n    forwards: (node: Node, offset: number, process: TextProcessCallback, root?: Node) => Spot | null;\n}\ninterface DomTreeWalkerConstructor {\n    readonly prototype: DomTreeWalker;\n    new (startNode: Node, rootNode: Node): DomTreeWalker;\n}\ndeclare class DomTreeWalker {\n    private readonly rootNode;\n    private node;\n    constructor(startNode: Node, rootNode: Node);\n    current(): Node | null | undefined;\n    next(shallow?: boolean): Node | null | undefined;\n    prev(shallow?: boolean): Node | null | undefined;\n    prev2(shallow?: boolean): Node | null | undefined;\n    private findSibling;\n    private findPreviousNode;\n}\ninterface Version {\n    major: number;\n    minor: number;\n}\ninterface Env {\n    transparentSrc: string;\n    documentMode: number;\n    cacheSuffix: any;\n    container: any;\n    canHaveCSP: boolean;\n    windowsPhone: boolean;\n    browser: {\n        current: string | undefined;\n        version: Version;\n        isEdge: () => boolean;\n        isChromium: () => boolean;\n        isIE: () => boolean;\n        isOpera: () => boolean;\n        isFirefox: () => boolean;\n        isSafari: () => boolean;\n    };\n    os: {\n        current: string | undefined;\n        version: Version;\n        isWindows: () => boolean;\n        isiOS: () => boolean;\n        isAndroid: () => boolean;\n        isMacOS: () => boolean;\n        isLinux: () => boolean;\n        isSolaris: () => boolean;\n        isFreeBSD: () => boolean;\n        isChromeOS: () => boolean;\n    };\n    deviceType: {\n        isiPad: () => boolean;\n        isiPhone: () => boolean;\n        isTablet: () => boolean;\n        isPhone: () => boolean;\n        isTouch: () => boolean;\n        isWebView: () => boolean;\n        isDesktop: () => boolean;\n    };\n}\ninterface FakeClipboardItem {\n    readonly items: Record<string, any>;\n    readonly types: ReadonlyArray<string>;\n    readonly getType: <D = any>(type: string) => D | undefined;\n}\ninterface FakeClipboard {\n    readonly FakeClipboardItem: (items: Record<string, any>) => FakeClipboardItem;\n    readonly write: (data: FakeClipboardItem[]) => void;\n    readonly read: () => FakeClipboardItem[] | undefined;\n    readonly clear: () => void;\n}\ninterface FocusManager {\n    isEditorUIElement: (elm: Element) => boolean;\n}\ninterface EntitiesMap {\n    [name: string]: string;\n}\ninterface Entities {\n    encodeRaw: (text: string, attr?: boolean) => string;\n    encodeAllRaw: (text: string) => string;\n    encodeNumeric: (text: string, attr?: boolean) => string;\n    encodeNamed: (text: string, attr?: boolean, entities?: EntitiesMap) => string;\n    getEncodeFunc: (name: string, entities?: string) => (text: string, attr?: boolean) => string;\n    decode: (text: string) => string;\n}\ninterface IconPack {\n    icons: Record<string, string>;\n}\ninterface IconManager {\n    add: (id: string, iconPack: IconPack) => void;\n    get: (id: string) => IconPack;\n    has: (id: string) => boolean;\n}\ninterface Resource {\n    load: <T = any>(id: string, url: string) => Promise<T>;\n    add: (id: string, data: any) => void;\n    unload: (id: string) => void;\n}\ntype TextPatterns_d_Pattern = Pattern;\ntype TextPatterns_d_RawPattern = RawPattern;\ntype TextPatterns_d_DynamicPatternsLookup = DynamicPatternsLookup;\ntype TextPatterns_d_RawDynamicPatternsLookup = RawDynamicPatternsLookup;\ntype TextPatterns_d_DynamicPatternContext = DynamicPatternContext;\ntype TextPatterns_d_BlockCmdPattern = BlockCmdPattern;\ntype TextPatterns_d_BlockPattern = BlockPattern;\ntype TextPatterns_d_BlockFormatPattern = BlockFormatPattern;\ntype TextPatterns_d_InlineCmdPattern = InlineCmdPattern;\ntype TextPatterns_d_InlinePattern = InlinePattern;\ntype TextPatterns_d_InlineFormatPattern = InlineFormatPattern;\ndeclare namespace TextPatterns_d {\n    export { TextPatterns_d_Pattern as Pattern, TextPatterns_d_RawPattern as RawPattern, TextPatterns_d_DynamicPatternsLookup as DynamicPatternsLookup, TextPatterns_d_RawDynamicPatternsLookup as RawDynamicPatternsLookup, TextPatterns_d_DynamicPatternContext as DynamicPatternContext, TextPatterns_d_BlockCmdPattern as BlockCmdPattern, TextPatterns_d_BlockPattern as BlockPattern, TextPatterns_d_BlockFormatPattern as BlockFormatPattern, TextPatterns_d_InlineCmdPattern as InlineCmdPattern, TextPatterns_d_InlinePattern as InlinePattern, TextPatterns_d_InlineFormatPattern as InlineFormatPattern, };\n}\ninterface Delay {\n    setEditorInterval: (editor: Editor, callback: () => void, time?: number) => number;\n    setEditorTimeout: (editor: Editor, callback: () => void, time?: number) => number;\n}\ndeclare type UploadResult = UploadResult$2;\ninterface ImageUploader {\n    upload: (blobInfos: BlobInfo[], showNotification?: boolean) => Promise<UploadResult[]>;\n}\ndeclare type ArrayCallback$1<T, R> = (this: any, x: T, i: number, xs: ArrayLike<T>) => R;\ndeclare type ObjCallback$1<T, R> = (this: any, value: T, key: string, obj: Record<string, T>) => R;\ndeclare type ArrayCallback<T, R> = ArrayCallback$1<T, R>;\ndeclare type ObjCallback<T, R> = ObjCallback$1<T, R>;\ndeclare type WalkCallback<T> = (this: any, o: T, i: string, n: keyof T | undefined) => boolean | void;\ninterface Tools {\n    is: (obj: any, type?: string) => boolean;\n    isArray: <T>(arr: any) => arr is Array<T>;\n    inArray: <T>(arr: ArrayLike<T>, value: T) => number;\n    grep: {\n        <T>(arr: ArrayLike<T> | null | undefined, pred?: ArrayCallback<T, boolean>): T[];\n        <T>(arr: Record<string, T> | null | undefined, pred?: ObjCallback<T, boolean>): T[];\n    };\n    trim: (str: string | null | undefined) => string;\n    toArray: <T>(obj: ArrayLike<T>) => T[];\n    hasOwn: (obj: any, name: string) => boolean;\n    makeMap: (items: ArrayLike<string> | string | undefined, delim?: string | RegExp, map?: Record<string, {}>) => Record<string, {}>;\n    each: {\n        <T>(arr: ArrayLike<T> | null | undefined, cb: ArrayCallback<T, void | boolean>, scope?: any): boolean;\n        <T>(obj: Record<string, T> | null | undefined, cb: ObjCallback<T, void | boolean>, scope?: any): boolean;\n    };\n    map: {\n        <T, R>(arr: ArrayLike<T> | null | undefined, cb: ArrayCallback<T, R>): R[];\n        <T, R>(obj: Record<string, T> | null | undefined, cb: ObjCallback<T, R>): R[];\n    };\n    extend: (obj: Object, ext: Object, ...objs: Object[]) => any;\n    walk: <T extends Record<string, any>>(obj: T, f: WalkCallback<T>, n?: keyof T, scope?: any) => void;\n    resolve: (path: string, o?: Object) => any;\n    explode: (s: string | string[], d?: string | RegExp) => string[];\n    _addCacheSuffix: (url: string) => string;\n}\ninterface KeyboardLikeEvent {\n    shiftKey: boolean;\n    ctrlKey: boolean;\n    altKey: boolean;\n    metaKey: boolean;\n}\ninterface VK {\n    BACKSPACE: number;\n    DELETE: number;\n    DOWN: number;\n    ENTER: number;\n    ESC: number;\n    LEFT: number;\n    RIGHT: number;\n    SPACEBAR: number;\n    TAB: number;\n    UP: number;\n    PAGE_UP: number;\n    PAGE_DOWN: number;\n    END: number;\n    HOME: number;\n    modifierPressed: (e: KeyboardLikeEvent) => boolean;\n    metaKeyPressed: (e: KeyboardLikeEvent) => boolean;\n}\ninterface DOMUtilsNamespace {\n    (doc: Document, settings: Partial<DOMUtilsSettings>): DOMUtils;\n    DOM: DOMUtils;\n    nodeIndex: (node: Node, normalized?: boolean) => number;\n}\ninterface RangeUtilsNamespace {\n    (dom: DOMUtils): RangeUtils;\n    compareRanges: (rng1: RangeLikeObject, rng2: RangeLikeObject) => boolean;\n    getCaretRangeFromPoint: (clientX: number, clientY: number, doc: Document) => Range;\n    getSelectedNode: (range: Range) => Node;\n    getNode: (container: Node, offset: number) => Node;\n}\ninterface AddOnManagerNamespace {\n    <T>(): AddOnManager<T>;\n    language: string | undefined;\n    languageLoad: boolean;\n    baseURL: string;\n    PluginManager: PluginManager;\n    ThemeManager: ThemeManager;\n    ModelManager: ModelManager;\n}\ninterface BookmarkManagerNamespace {\n    (selection: EditorSelection): BookmarkManager;\n    isBookmarkNode: (node: Node) => boolean;\n}\ninterface TinyMCE extends EditorManager {\n    geom: {\n        Rect: Rect;\n    };\n    util: {\n        Delay: Delay;\n        Tools: Tools;\n        VK: VK;\n        URI: URIConstructor;\n        EventDispatcher: EventDispatcherConstructor<any>;\n        Observable: Observable<any>;\n        I18n: I18n;\n        LocalStorage: Storage;\n        ImageUploader: ImageUploader;\n    };\n    dom: {\n        EventUtils: EventUtilsConstructor;\n        TreeWalker: DomTreeWalkerConstructor;\n        TextSeeker: (dom: DOMUtils, isBlockBoundary?: (node: Node) => boolean) => TextSeeker;\n        DOMUtils: DOMUtilsNamespace;\n        ScriptLoader: ScriptLoaderConstructor;\n        RangeUtils: RangeUtilsNamespace;\n        Serializer: (settings: DomSerializerSettings, editor?: Editor) => DomSerializer;\n        ControlSelection: (selection: EditorSelection, editor: Editor) => ControlSelection;\n        BookmarkManager: BookmarkManagerNamespace;\n        Selection: (dom: DOMUtils, win: Window, serializer: DomSerializer, editor: Editor) => EditorSelection;\n        StyleSheetLoader: (documentOrShadowRoot: Document | ShadowRoot, settings: StyleSheetLoaderSettings) => StyleSheetLoader;\n        Event: EventUtils;\n    };\n    html: {\n        Styles: (settings?: StylesSettings, schema?: Schema) => Styles;\n        Entities: Entities;\n        Node: AstNodeConstructor;\n        Schema: (settings?: SchemaSettings) => Schema;\n        DomParser: (settings?: DomParserSettings, schema?: Schema) => DomParser;\n        Writer: (settings?: WriterSettings) => Writer;\n        Serializer: (settings?: HtmlSerializerSettings, schema?: Schema) => HtmlSerializer;\n    };\n    AddOnManager: AddOnManagerNamespace;\n    Annotator: (editor: Editor) => Annotator;\n    Editor: EditorConstructor;\n    EditorCommands: EditorCommandsConstructor;\n    EditorManager: EditorManager;\n    EditorObservable: EditorObservable;\n    Env: Env;\n    FocusManager: FocusManager;\n    Formatter: (editor: Editor) => Formatter;\n    NotificationManager: (editor: Editor) => NotificationManager;\n    Shortcuts: ShortcutsConstructor;\n    UndoManager: (editor: Editor) => UndoManager;\n    WindowManager: (editor: Editor) => WindowManager;\n    DOM: DOMUtils;\n    ScriptLoader: ScriptLoader;\n    PluginManager: PluginManager;\n    ThemeManager: ThemeManager;\n    ModelManager: ModelManager;\n    IconManager: IconManager;\n    Resource: Resource;\n    FakeClipboard: FakeClipboard;\n    trim: Tools['trim'];\n    isArray: Tools['isArray'];\n    is: Tools['is'];\n    toArray: Tools['toArray'];\n    makeMap: Tools['makeMap'];\n    each: Tools['each'];\n    map: Tools['map'];\n    grep: Tools['grep'];\n    inArray: Tools['inArray'];\n    extend: Tools['extend'];\n    walk: Tools['walk'];\n    resolve: Tools['resolve'];\n    explode: Tools['explode'];\n    _addCacheSuffix: Tools['_addCacheSuffix'];\n}\ndeclare const tinymce: TinyMCE;\nexport { AddOnManager, Annotator, AstNode, Bookmark, BookmarkManager, ControlSelection, DOMUtils, Delay, DomParser, DomParserSettings, DomSerializer, DomSerializerSettings, DomTreeWalker, Editor, EditorCommands, EditorEvent, EditorManager, EditorModeApi, EditorObservable, EditorOptions, EditorSelection, Entities, Env, EventDispatcher, EventUtils, EventTypes_d as Events, FakeClipboard, FocusManager, Format_d as Formats, Formatter, GeomRect, HtmlSerializer, HtmlSerializerSettings, I18n, IconManager, Model, ModelManager, NotificationApi, NotificationManager, NotificationSpec, Observable, Plugin, PluginManager, RangeUtils, RawEditorOptions, Rect, Resource, Schema, SchemaSettings, ScriptLoader, Shortcuts, StyleSheetLoader, Styles, TextPatterns_d as TextPatterns, TextSeeker, Theme, ThemeManager, TinyMCE, Tools, URI, Ui_d as Ui, UndoManager, VK, WindowManager, Writer, WriterSettings, tinymce as default };\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/assets/vendor/tinymce/tinymce.js",
    "content": "/**\n * TinyMCE version 6.3.2 (2023-02-22)\n */\n\n(function () {\n    'use strict';\n\n    var typeOf$1 = function (x) {\n      if (x === null) {\n        return 'null';\n      }\n      if (x === undefined) {\n        return 'undefined';\n      }\n      var t = typeof x;\n      if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {\n        return 'array';\n      }\n      if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {\n        return 'string';\n      }\n      return t;\n    };\n    var isEquatableType = function (x) {\n      return [\n        'undefined',\n        'boolean',\n        'number',\n        'string',\n        'function',\n        'xml',\n        'null'\n      ].indexOf(x) !== -1;\n    };\n\n    var sort$1 = function (xs, compareFn) {\n      var clone = Array.prototype.slice.call(xs);\n      return clone.sort(compareFn);\n    };\n\n    var contramap = function (eqa, f) {\n      return eq$2(function (x, y) {\n        return eqa.eq(f(x), f(y));\n      });\n    };\n    var eq$2 = function (f) {\n      return { eq: f };\n    };\n    var tripleEq = eq$2(function (x, y) {\n      return x === y;\n    });\n    var eqString = tripleEq;\n    var eqArray = function (eqa) {\n      return eq$2(function (x, y) {\n        if (x.length !== y.length) {\n          return false;\n        }\n        var len = x.length;\n        for (var i = 0; i < len; i++) {\n          if (!eqa.eq(x[i], y[i])) {\n            return false;\n          }\n        }\n        return true;\n      });\n    };\n    var eqSortedArray = function (eqa, compareFn) {\n      return contramap(eqArray(eqa), function (xs) {\n        return sort$1(xs, compareFn);\n      });\n    };\n    var eqRecord = function (eqa) {\n      return eq$2(function (x, y) {\n        var kx = Object.keys(x);\n        var ky = Object.keys(y);\n        if (!eqSortedArray(eqString).eq(kx, ky)) {\n          return false;\n        }\n        var len = kx.length;\n        for (var i = 0; i < len; i++) {\n          var q = kx[i];\n          if (!eqa.eq(x[q], y[q])) {\n            return false;\n          }\n        }\n        return true;\n      });\n    };\n    var eqAny = eq$2(function (x, y) {\n      if (x === y) {\n        return true;\n      }\n      var tx = typeOf$1(x);\n      var ty = typeOf$1(y);\n      if (tx !== ty) {\n        return false;\n      }\n      if (isEquatableType(tx)) {\n        return x === y;\n      } else if (tx === 'array') {\n        return eqArray(eqAny).eq(x, y);\n      } else if (tx === 'object') {\n        return eqRecord(eqAny).eq(x, y);\n      }\n      return false;\n    });\n\n    const getPrototypeOf$1 = Object.getPrototypeOf;\n    const hasProto = (v, constructor, predicate) => {\n      var _a;\n      if (predicate(v, constructor.prototype)) {\n        return true;\n      } else {\n        return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;\n      }\n    };\n    const typeOf = x => {\n      const t = typeof x;\n      if (x === null) {\n        return 'null';\n      } else if (t === 'object' && Array.isArray(x)) {\n        return 'array';\n      } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {\n        return 'string';\n      } else {\n        return t;\n      }\n    };\n    const isType$1 = type => value => typeOf(value) === type;\n    const isSimpleType = type => value => typeof value === type;\n    const eq$1 = t => a => t === a;\n    const is$4 = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf$1(o) === proto);\n    const isString = isType$1('string');\n    const isObject = isType$1('object');\n    const isPlainObject = value => is$4(value, Object);\n    const isArray$1 = isType$1('array');\n    const isNull = eq$1(null);\n    const isBoolean = isSimpleType('boolean');\n    const isUndefined = eq$1(undefined);\n    const isNullable = a => a === null || a === undefined;\n    const isNonNullable = a => !isNullable(a);\n    const isFunction = isSimpleType('function');\n    const isNumber = isSimpleType('number');\n    const isArrayOf = (value, pred) => {\n      if (isArray$1(value)) {\n        for (let i = 0, len = value.length; i < len; ++i) {\n          if (!pred(value[i])) {\n            return false;\n          }\n        }\n        return true;\n      }\n      return false;\n    };\n\n    const noop = () => {\n    };\n    const compose = (fa, fb) => {\n      return (...args) => {\n        return fa(fb.apply(null, args));\n      };\n    };\n    const compose1 = (fbc, fab) => a => fbc(fab(a));\n    const constant = value => {\n      return () => {\n        return value;\n      };\n    };\n    const identity = x => {\n      return x;\n    };\n    const tripleEquals = (a, b) => {\n      return a === b;\n    };\n    function curry(fn, ...initialArgs) {\n      return (...restArgs) => {\n        const all = initialArgs.concat(restArgs);\n        return fn.apply(null, all);\n      };\n    }\n    const not = f => t => !f(t);\n    const die = msg => {\n      return () => {\n        throw new Error(msg);\n      };\n    };\n    const apply$1 = f => {\n      return f();\n    };\n    const call = f => {\n      f();\n    };\n    const never = constant(false);\n    const always = constant(true);\n\n    class Optional {\n      constructor(tag, value) {\n        this.tag = tag;\n        this.value = value;\n      }\n      static some(value) {\n        return new Optional(true, value);\n      }\n      static none() {\n        return Optional.singletonNone;\n      }\n      fold(onNone, onSome) {\n        if (this.tag) {\n          return onSome(this.value);\n        } else {\n          return onNone();\n        }\n      }\n      isSome() {\n        return this.tag;\n      }\n      isNone() {\n        return !this.tag;\n      }\n      map(mapper) {\n        if (this.tag) {\n          return Optional.some(mapper(this.value));\n        } else {\n          return Optional.none();\n        }\n      }\n      bind(binder) {\n        if (this.tag) {\n          return binder(this.value);\n        } else {\n          return Optional.none();\n        }\n      }\n      exists(predicate) {\n        return this.tag && predicate(this.value);\n      }\n      forall(predicate) {\n        return !this.tag || predicate(this.value);\n      }\n      filter(predicate) {\n        if (!this.tag || predicate(this.value)) {\n          return this;\n        } else {\n          return Optional.none();\n        }\n      }\n      getOr(replacement) {\n        return this.tag ? this.value : replacement;\n      }\n      or(replacement) {\n        return this.tag ? this : replacement;\n      }\n      getOrThunk(thunk) {\n        return this.tag ? this.value : thunk();\n      }\n      orThunk(thunk) {\n        return this.tag ? this : thunk();\n      }\n      getOrDie(message) {\n        if (!this.tag) {\n          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');\n        } else {\n          return this.value;\n        }\n      }\n      static from(value) {\n        return isNonNullable(value) ? Optional.some(value) : Optional.none();\n      }\n      getOrNull() {\n        return this.tag ? this.value : null;\n      }\n      getOrUndefined() {\n        return this.value;\n      }\n      each(worker) {\n        if (this.tag) {\n          worker(this.value);\n        }\n      }\n      toArray() {\n        return this.tag ? [this.value] : [];\n      }\n      toString() {\n        return this.tag ? `some(${ this.value })` : 'none()';\n      }\n    }\n    Optional.singletonNone = new Optional(false);\n\n    const nativeSlice = Array.prototype.slice;\n    const nativeIndexOf = Array.prototype.indexOf;\n    const nativePush = Array.prototype.push;\n    const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);\n    const indexOf$1 = (xs, x) => {\n      const r = rawIndexOf(xs, x);\n      return r === -1 ? Optional.none() : Optional.some(r);\n    };\n    const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1;\n    const exists = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const map$3 = (xs, f) => {\n      const len = xs.length;\n      const r = new Array(len);\n      for (let i = 0; i < len; i++) {\n        const x = xs[i];\n        r[i] = f(x, i);\n      }\n      return r;\n    };\n    const each$e = (xs, f) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const eachr = (xs, f) => {\n      for (let i = xs.length - 1; i >= 0; i--) {\n        const x = xs[i];\n        f(x, i);\n      }\n    };\n    const partition$2 = (xs, pred) => {\n      const pass = [];\n      const fail = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        const arr = pred(x, i) ? pass : fail;\n        arr.push(x);\n      }\n      return {\n        pass,\n        fail\n      };\n    };\n    const filter$5 = (xs, pred) => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n    const foldr = (xs, f, acc) => {\n      eachr(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const foldl = (xs, f, acc) => {\n      each$e(xs, (x, i) => {\n        acc = f(acc, x, i);\n      });\n      return acc;\n    };\n    const findUntil$1 = (xs, pred, until) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(x);\n        } else if (until(x, i)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const find$2 = (xs, pred) => {\n      return findUntil$1(xs, pred, never);\n    };\n    const findIndex$2 = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (pred(x, i)) {\n          return Optional.some(i);\n        }\n      }\n      return Optional.none();\n    };\n    const flatten = xs => {\n      const r = [];\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        if (!isArray$1(xs[i])) {\n          throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);\n        }\n        nativePush.apply(r, xs[i]);\n      }\n      return r;\n    };\n    const bind$3 = (xs, f) => flatten(map$3(xs, f));\n    const forall = (xs, pred) => {\n      for (let i = 0, len = xs.length; i < len; ++i) {\n        const x = xs[i];\n        if (pred(x, i) !== true) {\n          return false;\n        }\n      }\n      return true;\n    };\n    const reverse = xs => {\n      const r = nativeSlice.call(xs, 0);\n      r.reverse();\n      return r;\n    };\n    const difference = (a1, a2) => filter$5(a1, x => !contains$2(a2, x));\n    const mapToObject = (xs, f) => {\n      const r = {};\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        r[String(x)] = f(x, i);\n      }\n      return r;\n    };\n    const sort = (xs, comparator) => {\n      const copy = nativeSlice.call(xs, 0);\n      copy.sort(comparator);\n      return copy;\n    };\n    const get$b = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();\n    const head = xs => get$b(xs, 0);\n    const last$3 = xs => get$b(xs, xs.length - 1);\n    const from = isFunction(Array.from) ? Array.from : x => nativeSlice.call(x);\n    const findMap = (arr, f) => {\n      for (let i = 0; i < arr.length; i++) {\n        const r = f(arr[i], i);\n        if (r.isSome()) {\n          return r;\n        }\n      }\n      return Optional.none();\n    };\n    const unique$1 = (xs, comparator) => {\n      const r = [];\n      const isDuplicated = isFunction(comparator) ? x => exists(r, i => comparator(i, x)) : x => contains$2(r, x);\n      for (let i = 0, len = xs.length; i < len; i++) {\n        const x = xs[i];\n        if (!isDuplicated(x)) {\n          r.push(x);\n        }\n      }\n      return r;\n    };\n\n    const keys = Object.keys;\n    const hasOwnProperty$2 = Object.hasOwnProperty;\n    const each$d = (obj, f) => {\n      const props = keys(obj);\n      for (let k = 0, len = props.length; k < len; k++) {\n        const i = props[k];\n        const x = obj[i];\n        f(x, i);\n      }\n    };\n    const map$2 = (obj, f) => {\n      return tupleMap(obj, (x, i) => ({\n        k: i,\n        v: f(x, i)\n      }));\n    };\n    const tupleMap = (obj, f) => {\n      const r = {};\n      each$d(obj, (x, i) => {\n        const tuple = f(x, i);\n        r[tuple.k] = tuple.v;\n      });\n      return r;\n    };\n    const objAcc = r => (x, i) => {\n      r[i] = x;\n    };\n    const internalFilter = (obj, pred, onTrue, onFalse) => {\n      each$d(obj, (x, i) => {\n        (pred(x, i) ? onTrue : onFalse)(x, i);\n      });\n    };\n    const bifilter = (obj, pred) => {\n      const t = {};\n      const f = {};\n      internalFilter(obj, pred, objAcc(t), objAcc(f));\n      return {\n        t,\n        f\n      };\n    };\n    const filter$4 = (obj, pred) => {\n      const t = {};\n      internalFilter(obj, pred, objAcc(t), noop);\n      return t;\n    };\n    const mapToArray = (obj, f) => {\n      const r = [];\n      each$d(obj, (value, name) => {\n        r.push(f(value, name));\n      });\n      return r;\n    };\n    const values = obj => {\n      return mapToArray(obj, identity);\n    };\n    const get$a = (obj, key) => {\n      return has$2(obj, key) ? Optional.from(obj[key]) : Optional.none();\n    };\n    const has$2 = (obj, key) => hasOwnProperty$2.call(obj, key);\n    const hasNonNullableKey = (obj, key) => has$2(obj, key) && obj[key] !== undefined && obj[key] !== null;\n    const equal$1 = (a1, a2, eq = eqAny) => eqRecord(eq).eq(a1, a2);\n\n    const stringArray = a => {\n      const all = {};\n      each$e(a, key => {\n        all[key] = {};\n      });\n      return keys(all);\n    };\n\n    const isArrayLike = o => o.length !== undefined;\n    const isArray = Array.isArray;\n    const toArray$1 = obj => {\n      if (!isArray(obj)) {\n        const array = [];\n        for (let i = 0, l = obj.length; i < l; i++) {\n          array[i] = obj[i];\n        }\n        return array;\n      } else {\n        return obj;\n      }\n    };\n    const each$c = (o, cb, s) => {\n      if (!o) {\n        return false;\n      }\n      s = s || o;\n      if (isArrayLike(o)) {\n        for (let n = 0, l = o.length; n < l; n++) {\n          if (cb.call(s, o[n], n, o) === false) {\n            return false;\n          }\n        }\n      } else {\n        for (const n in o) {\n          if (has$2(o, n)) {\n            if (cb.call(s, o[n], n, o) === false) {\n              return false;\n            }\n          }\n        }\n      }\n      return true;\n    };\n    const map$1 = (array, callback) => {\n      const out = [];\n      each$c(array, (item, index) => {\n        out.push(callback(item, index, array));\n      });\n      return out;\n    };\n    const filter$3 = (a, f) => {\n      const o = [];\n      each$c(a, (v, index) => {\n        if (!f || f(v, index, a)) {\n          o.push(v);\n        }\n      });\n      return o;\n    };\n    const indexOf = (a, v) => {\n      if (a) {\n        for (let i = 0, l = a.length; i < l; i++) {\n          if (a[i] === v) {\n            return i;\n          }\n        }\n      }\n      return -1;\n    };\n    const reduce = (collection, iteratee, accumulator, thisArg) => {\n      let acc = isUndefined(accumulator) ? collection[0] : accumulator;\n      for (let i = 0; i < collection.length; i++) {\n        acc = iteratee.call(thisArg, acc, collection[i], i);\n      }\n      return acc;\n    };\n    const findIndex$1 = (array, predicate, thisArg) => {\n      for (let i = 0, l = array.length; i < l; i++) {\n        if (predicate.call(thisArg, array[i], i, array)) {\n          return i;\n        }\n      }\n      return -1;\n    };\n    const last$2 = collection => collection[collection.length - 1];\n\n    const cached = f => {\n      let called = false;\n      let r;\n      return (...args) => {\n        if (!called) {\n          called = true;\n          r = f.apply(null, args);\n        }\n        return r;\n      };\n    };\n\n    const DeviceType = (os, browser, userAgent, mediaMatch) => {\n      const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;\n      const isiPhone = os.isiOS() && !isiPad;\n      const isMobile = os.isiOS() || os.isAndroid();\n      const isTouch = isMobile || mediaMatch('(pointer:coarse)');\n      const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)');\n      const isPhone = isiPhone || isMobile && !isTablet;\n      const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;\n      const isDesktop = !isPhone && !isTablet && !iOSwebview;\n      return {\n        isiPad: constant(isiPad),\n        isiPhone: constant(isiPhone),\n        isTablet: constant(isTablet),\n        isPhone: constant(isPhone),\n        isTouch: constant(isTouch),\n        isAndroid: os.isAndroid,\n        isiOS: os.isiOS,\n        isWebView: constant(iOSwebview),\n        isDesktop: constant(isDesktop)\n      };\n    };\n\n    const firstMatch = (regexes, s) => {\n      for (let i = 0; i < regexes.length; i++) {\n        const x = regexes[i];\n        if (x.test(s)) {\n          return x;\n        }\n      }\n      return undefined;\n    };\n    const find$1 = (regexes, agent) => {\n      const r = firstMatch(regexes, agent);\n      if (!r) {\n        return {\n          major: 0,\n          minor: 0\n        };\n      }\n      const group = i => {\n        return Number(agent.replace(r, '$' + i));\n      };\n      return nu$3(group(1), group(2));\n    };\n    const detect$5 = (versionRegexes, agent) => {\n      const cleanedAgent = String(agent).toLowerCase();\n      if (versionRegexes.length === 0) {\n        return unknown$2();\n      }\n      return find$1(versionRegexes, cleanedAgent);\n    };\n    const unknown$2 = () => {\n      return nu$3(0, 0);\n    };\n    const nu$3 = (major, minor) => {\n      return {\n        major,\n        minor\n      };\n    };\n    const Version = {\n      nu: nu$3,\n      detect: detect$5,\n      unknown: unknown$2\n    };\n\n    const detectBrowser$1 = (browsers, userAgentData) => {\n      return findMap(userAgentData.brands, uaBrand => {\n        const lcBrand = uaBrand.brand.toLowerCase();\n        return find$2(browsers, browser => {\n          var _a;\n          return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());\n        }).map(info => ({\n          current: info.name,\n          version: Version.nu(parseInt(uaBrand.version, 10), 0)\n        }));\n      });\n    };\n\n    const detect$4 = (candidates, userAgent) => {\n      const agent = String(userAgent).toLowerCase();\n      return find$2(candidates, candidate => {\n        return candidate.search(agent);\n      });\n    };\n    const detectBrowser = (browsers, userAgent) => {\n      return detect$4(browsers, userAgent).map(browser => {\n        const version = Version.detect(browser.versionRegexes, userAgent);\n        return {\n          current: browser.name,\n          version\n        };\n      });\n    };\n    const detectOs = (oses, userAgent) => {\n      return detect$4(oses, userAgent).map(os => {\n        const version = Version.detect(os.versionRegexes, userAgent);\n        return {\n          current: os.name,\n          version\n        };\n      });\n    };\n\n    const removeFromStart = (str, numChars) => {\n      return str.substring(numChars);\n    };\n\n    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;\n    const removeLeading = (str, prefix) => {\n      return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;\n    };\n    const contains$1 = (str, substr, start = 0, end) => {\n      const idx = str.indexOf(substr, start);\n      if (idx !== -1) {\n        return isUndefined(end) ? true : idx + substr.length <= end;\n      } else {\n        return false;\n      }\n    };\n    const startsWith = (str, prefix) => {\n      return checkRange(str, prefix, 0);\n    };\n    const endsWith = (str, suffix) => {\n      return checkRange(str, suffix, str.length - suffix.length);\n    };\n    const blank = r => s => s.replace(r, '');\n    const trim$3 = blank(/^\\s+|\\s+$/g);\n    const lTrim = blank(/^\\s+/g);\n    const rTrim = blank(/\\s+$/g);\n    const isNotEmpty = s => s.length > 0;\n    const isEmpty$3 = s => !isNotEmpty(s);\n    const repeat = (s, count) => count <= 0 ? '' : new Array(count + 1).join(s);\n    const toInt = (value, radix = 10) => {\n      const num = parseInt(value, radix);\n      return isNaN(num) ? Optional.none() : Optional.some(num);\n    };\n\n    const normalVersionRegex = /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/;\n    const checkContains = target => {\n      return uastring => {\n        return contains$1(uastring, target);\n      };\n    };\n    const browsers = [\n      {\n        name: 'Edge',\n        versionRegexes: [/.*?edge\\/ ?([0-9]+)\\.([0-9]+)$/],\n        search: uastring => {\n          return contains$1(uastring, 'edge/') && contains$1(uastring, 'chrome') && contains$1(uastring, 'safari') && contains$1(uastring, 'applewebkit');\n        }\n      },\n      {\n        name: 'Chromium',\n        brand: 'Chromium',\n        versionRegexes: [\n          /.*?chrome\\/([0-9]+)\\.([0-9]+).*/,\n          normalVersionRegex\n        ],\n        search: uastring => {\n          return contains$1(uastring, 'chrome') && !contains$1(uastring, 'chromeframe');\n        }\n      },\n      {\n        name: 'IE',\n        versionRegexes: [\n          /.*?msie\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*?rv:([0-9]+)\\.([0-9]+).*/\n        ],\n        search: uastring => {\n          return contains$1(uastring, 'msie') || contains$1(uastring, 'trident');\n        }\n      },\n      {\n        name: 'Opera',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?opera\\/([0-9]+)\\.([0-9]+).*/\n        ],\n        search: checkContains('opera')\n      },\n      {\n        name: 'Firefox',\n        versionRegexes: [/.*?firefox\\/\\ ?([0-9]+)\\.([0-9]+).*/],\n        search: checkContains('firefox')\n      },\n      {\n        name: 'Safari',\n        versionRegexes: [\n          normalVersionRegex,\n          /.*?cpu os ([0-9]+)_([0-9]+).*/\n        ],\n        search: uastring => {\n          return (contains$1(uastring, 'safari') || contains$1(uastring, 'mobile/')) && contains$1(uastring, 'applewebkit');\n        }\n      }\n    ];\n    const oses = [\n      {\n        name: 'Windows',\n        search: checkContains('win'),\n        versionRegexes: [/.*?windows\\ nt\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'iOS',\n        search: uastring => {\n          return contains$1(uastring, 'iphone') || contains$1(uastring, 'ipad');\n        },\n        versionRegexes: [\n          /.*?version\\/\\ ?([0-9]+)\\.([0-9]+).*/,\n          /.*cpu os ([0-9]+)_([0-9]+).*/,\n          /.*cpu iphone os ([0-9]+)_([0-9]+).*/\n        ]\n      },\n      {\n        name: 'Android',\n        search: checkContains('android'),\n        versionRegexes: [/.*?android\\ ?([0-9]+)\\.([0-9]+).*/]\n      },\n      {\n        name: 'macOS',\n        search: checkContains('mac os x'),\n        versionRegexes: [/.*?mac\\ os\\ x\\ ?([0-9]+)_([0-9]+).*/]\n      },\n      {\n        name: 'Linux',\n        search: checkContains('linux'),\n        versionRegexes: []\n      },\n      {\n        name: 'Solaris',\n        search: checkContains('sunos'),\n        versionRegexes: []\n      },\n      {\n        name: 'FreeBSD',\n        search: checkContains('freebsd'),\n        versionRegexes: []\n      },\n      {\n        name: 'ChromeOS',\n        search: checkContains('cros'),\n        versionRegexes: [/.*?chrome\\/([0-9]+)\\.([0-9]+).*/]\n      }\n    ];\n    const PlatformInfo = {\n      browsers: constant(browsers),\n      oses: constant(oses)\n    };\n\n    const edge = 'Edge';\n    const chromium = 'Chromium';\n    const ie = 'IE';\n    const opera = 'Opera';\n    const firefox = 'Firefox';\n    const safari = 'Safari';\n    const unknown$1 = () => {\n      return nu$2({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu$2 = info => {\n      const current = info.current;\n      const version = info.version;\n      const isBrowser = name => () => current === name;\n      return {\n        current,\n        version,\n        isEdge: isBrowser(edge),\n        isChromium: isBrowser(chromium),\n        isIE: isBrowser(ie),\n        isOpera: isBrowser(opera),\n        isFirefox: isBrowser(firefox),\n        isSafari: isBrowser(safari)\n      };\n    };\n    const Browser = {\n      unknown: unknown$1,\n      nu: nu$2,\n      edge: constant(edge),\n      chromium: constant(chromium),\n      ie: constant(ie),\n      opera: constant(opera),\n      firefox: constant(firefox),\n      safari: constant(safari)\n    };\n\n    const windows = 'Windows';\n    const ios = 'iOS';\n    const android = 'Android';\n    const linux = 'Linux';\n    const macos = 'macOS';\n    const solaris = 'Solaris';\n    const freebsd = 'FreeBSD';\n    const chromeos = 'ChromeOS';\n    const unknown = () => {\n      return nu$1({\n        current: undefined,\n        version: Version.unknown()\n      });\n    };\n    const nu$1 = info => {\n      const current = info.current;\n      const version = info.version;\n      const isOS = name => () => current === name;\n      return {\n        current,\n        version,\n        isWindows: isOS(windows),\n        isiOS: isOS(ios),\n        isAndroid: isOS(android),\n        isMacOS: isOS(macos),\n        isLinux: isOS(linux),\n        isSolaris: isOS(solaris),\n        isFreeBSD: isOS(freebsd),\n        isChromeOS: isOS(chromeos)\n      };\n    };\n    const OperatingSystem = {\n      unknown,\n      nu: nu$1,\n      windows: constant(windows),\n      ios: constant(ios),\n      android: constant(android),\n      linux: constant(linux),\n      macos: constant(macos),\n      solaris: constant(solaris),\n      freebsd: constant(freebsd),\n      chromeos: constant(chromeos)\n    };\n\n    const detect$3 = (userAgent, userAgentDataOpt, mediaMatch) => {\n      const browsers = PlatformInfo.browsers();\n      const oses = PlatformInfo.oses();\n      const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);\n      const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);\n      const deviceType = DeviceType(os, browser, userAgent, mediaMatch);\n      return {\n        browser,\n        os,\n        deviceType\n      };\n    };\n    const PlatformDetection = { detect: detect$3 };\n\n    const mediaMatch = query => window.matchMedia(query).matches;\n    let platform$2 = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch));\n    const detect$2 = () => platform$2();\n\n    const userAgent = navigator.userAgent;\n    const platform$1 = detect$2();\n    const browser$1 = platform$1.browser;\n    const os = platform$1.os;\n    const deviceType = platform$1.deviceType;\n    const windowsPhone = userAgent.indexOf('Windows Phone') !== -1;\n    const Env = {\n      transparentSrc: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',\n      documentMode: browser$1.isIE() ? document.documentMode || 7 : 10,\n      cacheSuffix: null,\n      container: null,\n      canHaveCSP: !browser$1.isIE(),\n      windowsPhone,\n      browser: {\n        current: browser$1.current,\n        version: browser$1.version,\n        isChromium: browser$1.isChromium,\n        isEdge: browser$1.isEdge,\n        isFirefox: browser$1.isFirefox,\n        isIE: browser$1.isIE,\n        isOpera: browser$1.isOpera,\n        isSafari: browser$1.isSafari\n      },\n      os: {\n        current: os.current,\n        version: os.version,\n        isAndroid: os.isAndroid,\n        isChromeOS: os.isChromeOS,\n        isFreeBSD: os.isFreeBSD,\n        isiOS: os.isiOS,\n        isLinux: os.isLinux,\n        isMacOS: os.isMacOS,\n        isSolaris: os.isSolaris,\n        isWindows: os.isWindows\n      },\n      deviceType: {\n        isDesktop: deviceType.isDesktop,\n        isiPad: deviceType.isiPad,\n        isiPhone: deviceType.isiPhone,\n        isPhone: deviceType.isPhone,\n        isTablet: deviceType.isTablet,\n        isTouch: deviceType.isTouch,\n        isWebView: deviceType.isWebView\n      }\n    };\n\n    const whiteSpaceRegExp$1 = /^\\s*|\\s*$/g;\n    const trim$2 = str => {\n      return isNullable(str) ? '' : ('' + str).replace(whiteSpaceRegExp$1, '');\n    };\n    const is$3 = (obj, type) => {\n      if (!type) {\n        return obj !== undefined;\n      }\n      if (type === 'array' && isArray(obj)) {\n        return true;\n      }\n      return typeof obj === type;\n    };\n    const makeMap$4 = (items, delim, map = {}) => {\n      const resolvedItems = isString(items) ? items.split(delim || ',') : items || [];\n      let i = resolvedItems.length;\n      while (i--) {\n        map[resolvedItems[i]] = {};\n      }\n      return map;\n    };\n    const hasOwnProperty$1 = has$2;\n    const extend$3 = (obj, ...exts) => {\n      for (let i = 0; i < exts.length; i++) {\n        const ext = exts[i];\n        for (const name in ext) {\n          if (has$2(ext, name)) {\n            const value = ext[name];\n            if (value !== undefined) {\n              obj[name] = value;\n            }\n          }\n        }\n      }\n      return obj;\n    };\n    const walk$4 = function (o, f, n, s) {\n      s = s || this;\n      if (o) {\n        if (n) {\n          o = o[n];\n        }\n        each$c(o, (o, i) => {\n          if (f.call(s, o, i, n) === false) {\n            return false;\n          } else {\n            walk$4(o, f, n, s);\n            return true;\n          }\n        });\n      }\n    };\n    const resolve$2 = (n, o = window) => {\n      const path = n.split('.');\n      for (let i = 0, l = path.length; i < l; i++) {\n        o = o[path[i]];\n        if (!o) {\n          break;\n        }\n      }\n      return o;\n    };\n    const explode$3 = (s, d) => {\n      if (isArray$1(s)) {\n        return s;\n      } else if (s === '') {\n        return [];\n      } else {\n        return map$1(s.split(d || ','), trim$2);\n      }\n    };\n    const _addCacheSuffix = url => {\n      const cacheSuffix = Env.cacheSuffix;\n      if (cacheSuffix) {\n        url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix;\n      }\n      return url;\n    };\n    const Tools = {\n      trim: trim$2,\n      isArray: isArray,\n      is: is$3,\n      toArray: toArray$1,\n      makeMap: makeMap$4,\n      each: each$c,\n      map: map$1,\n      grep: filter$3,\n      inArray: indexOf,\n      hasOwn: hasOwnProperty$1,\n      extend: extend$3,\n      walk: walk$4,\n      resolve: resolve$2,\n      explode: explode$3,\n      _addCacheSuffix\n    };\n\n    const is$2 = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));\n    const cat = arr => {\n      const r = [];\n      const push = x => {\n        r.push(x);\n      };\n      for (let i = 0; i < arr.length; i++) {\n        arr[i].each(push);\n      }\n      return r;\n    };\n    const lift2 = (oa, ob, f) => oa.isSome() && ob.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie())) : Optional.none();\n    const lift3 = (oa, ob, oc, f) => oa.isSome() && ob.isSome() && oc.isSome() ? Optional.some(f(oa.getOrDie(), ob.getOrDie(), oc.getOrDie())) : Optional.none();\n    const someIf = (b, a) => b ? Optional.some(a) : Optional.none();\n\n    typeof window !== 'undefined' ? window : Function('return this;')();\n\n    const COMMENT = 8;\n    const DOCUMENT = 9;\n    const DOCUMENT_FRAGMENT = 11;\n    const ELEMENT = 1;\n    const TEXT = 3;\n\n    const name = element => {\n      const r = element.dom.nodeName;\n      return r.toLowerCase();\n    };\n    const type$1 = element => element.dom.nodeType;\n    const isType = t => element => type$1(element) === t;\n    const isComment$1 = element => type$1(element) === COMMENT || name(element) === '#comment';\n    const isElement$7 = isType(ELEMENT);\n    const isText$b = isType(TEXT);\n    const isDocument$2 = isType(DOCUMENT);\n    const isDocumentFragment$1 = isType(DOCUMENT_FRAGMENT);\n    const isTag = tag => e => isElement$7(e) && name(e) === tag;\n\n    const rawSet = (dom, key, value) => {\n      if (isString(value) || isBoolean(value) || isNumber(value)) {\n        dom.setAttribute(key, value + '');\n      } else {\n        console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);\n        throw new Error('Attribute value was not simple');\n      }\n    };\n    const set$2 = (element, key, value) => {\n      rawSet(element.dom, key, value);\n    };\n    const setAll$1 = (element, attrs) => {\n      const dom = element.dom;\n      each$d(attrs, (v, k) => {\n        rawSet(dom, k, v);\n      });\n    };\n    const get$9 = (element, key) => {\n      const v = element.dom.getAttribute(key);\n      return v === null ? undefined : v;\n    };\n    const getOpt = (element, key) => Optional.from(get$9(element, key));\n    const has$1 = (element, key) => {\n      const dom = element.dom;\n      return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;\n    };\n    const remove$b = (element, key) => {\n      element.dom.removeAttribute(key);\n    };\n    const hasNone = element => {\n      const attrs = element.dom.attributes;\n      return attrs === undefined || attrs === null || attrs.length === 0;\n    };\n    const clone$4 = element => foldl(element.dom.attributes, (acc, attr) => {\n      acc[attr.name] = attr.value;\n      return acc;\n    }, {});\n\n    const read$4 = (element, attr) => {\n      const value = get$9(element, attr);\n      return value === undefined || value === '' ? [] : value.split(' ');\n    };\n    const add$4 = (element, attr, id) => {\n      const old = read$4(element, attr);\n      const nu = old.concat([id]);\n      set$2(element, attr, nu.join(' '));\n      return true;\n    };\n    const remove$a = (element, attr, id) => {\n      const nu = filter$5(read$4(element, attr), v => v !== id);\n      if (nu.length > 0) {\n        set$2(element, attr, nu.join(' '));\n      } else {\n        remove$b(element, attr);\n      }\n      return false;\n    };\n\n    const supports = element => element.dom.classList !== undefined;\n    const get$8 = element => read$4(element, 'class');\n    const add$3 = (element, clazz) => add$4(element, 'class', clazz);\n    const remove$9 = (element, clazz) => remove$a(element, 'class', clazz);\n    const toggle$2 = (element, clazz) => {\n      if (contains$2(get$8(element), clazz)) {\n        return remove$9(element, clazz);\n      } else {\n        return add$3(element, clazz);\n      }\n    };\n\n    const add$2 = (element, clazz) => {\n      if (supports(element)) {\n        element.dom.classList.add(clazz);\n      } else {\n        add$3(element, clazz);\n      }\n    };\n    const cleanClass = element => {\n      const classList = supports(element) ? element.dom.classList : get$8(element);\n      if (classList.length === 0) {\n        remove$b(element, 'class');\n      }\n    };\n    const remove$8 = (element, clazz) => {\n      if (supports(element)) {\n        const classList = element.dom.classList;\n        classList.remove(clazz);\n      } else {\n        remove$9(element, clazz);\n      }\n      cleanClass(element);\n    };\n    const toggle$1 = (element, clazz) => {\n      const result = supports(element) ? element.dom.classList.toggle(clazz) : toggle$2(element, clazz);\n      cleanClass(element);\n      return result;\n    };\n    const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);\n\n    const isSupported$1 = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);\n\n    const fromHtml$1 = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      if (!div.hasChildNodes() || div.childNodes.length > 1) {\n        const message = 'HTML does not have a single root node';\n        console.error(message, html);\n        throw new Error(message);\n      }\n      return fromDom$2(div.childNodes[0]);\n    };\n    const fromTag = (tag, scope) => {\n      const doc = scope || document;\n      const node = doc.createElement(tag);\n      return fromDom$2(node);\n    };\n    const fromText = (text, scope) => {\n      const doc = scope || document;\n      const node = doc.createTextNode(text);\n      return fromDom$2(node);\n    };\n    const fromDom$2 = node => {\n      if (node === null || node === undefined) {\n        throw new Error('Node cannot be null or undefined');\n      }\n      return { dom: node };\n    };\n    const fromPoint$2 = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$2);\n    const SugarElement = {\n      fromHtml: fromHtml$1,\n      fromTag,\n      fromText,\n      fromDom: fromDom$2,\n      fromPoint: fromPoint$2\n    };\n\n    const toArray = (target, f) => {\n      const r = [];\n      const recurse = e => {\n        r.push(e);\n        return f(e);\n      };\n      let cur = f(target);\n      do {\n        cur = cur.bind(recurse);\n      } while (cur.isSome());\n      return r;\n    };\n\n    const is$1 = (element, selector) => {\n      const dom = element.dom;\n      if (dom.nodeType !== ELEMENT) {\n        return false;\n      } else {\n        const elem = dom;\n        if (elem.matches !== undefined) {\n          return elem.matches(selector);\n        } else if (elem.msMatchesSelector !== undefined) {\n          return elem.msMatchesSelector(selector);\n        } else if (elem.webkitMatchesSelector !== undefined) {\n          return elem.webkitMatchesSelector(selector);\n        } else if (elem.mozMatchesSelector !== undefined) {\n          return elem.mozMatchesSelector(selector);\n        } else {\n          throw new Error('Browser lacks native selectors');\n        }\n      }\n    };\n    const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;\n    const all = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? [] : map$3(base.querySelectorAll(selector), SugarElement.fromDom);\n    };\n    const one = (selector, scope) => {\n      const base = scope === undefined ? document : scope.dom;\n      return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);\n    };\n\n    const eq = (e1, e2) => e1.dom === e2.dom;\n    const contains = (e1, e2) => {\n      const d1 = e1.dom;\n      const d2 = e2.dom;\n      return d1 === d2 ? false : d1.contains(d2);\n    };\n\n    const owner$1 = element => SugarElement.fromDom(element.dom.ownerDocument);\n    const documentOrOwner = dos => isDocument$2(dos) ? dos : owner$1(dos);\n    const documentElement = element => SugarElement.fromDom(documentOrOwner(element).dom.documentElement);\n    const defaultView = element => SugarElement.fromDom(documentOrOwner(element).dom.defaultView);\n    const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);\n    const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);\n    const parents$1 = (element, isRoot) => {\n      const stop = isFunction(isRoot) ? isRoot : never;\n      let dom = element.dom;\n      const ret = [];\n      while (dom.parentNode !== null && dom.parentNode !== undefined) {\n        const rawParent = dom.parentNode;\n        const p = SugarElement.fromDom(rawParent);\n        ret.push(p);\n        if (stop(p) === true) {\n          break;\n        } else {\n          dom = rawParent;\n        }\n      }\n      return ret;\n    };\n    const siblings = element => {\n      const filterSelf = elements => filter$5(elements, x => !eq(element, x));\n      return parent(element).map(children$1).map(filterSelf).getOr([]);\n    };\n    const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);\n    const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);\n    const prevSiblings = element => reverse(toArray(element, prevSibling));\n    const nextSiblings = element => toArray(element, nextSibling);\n    const children$1 = element => map$3(element.dom.childNodes, SugarElement.fromDom);\n    const child$1 = (element, index) => {\n      const cs = element.dom.childNodes;\n      return Optional.from(cs[index]).map(SugarElement.fromDom);\n    };\n    const firstChild = element => child$1(element, 0);\n    const lastChild = element => child$1(element, element.dom.childNodes.length - 1);\n    const childNodesCount = element => element.dom.childNodes.length;\n\n    const getHead = doc => {\n      const b = doc.dom.head;\n      if (b === null || b === undefined) {\n        throw new Error('Head is not available yet');\n      }\n      return SugarElement.fromDom(b);\n    };\n\n    const isShadowRoot = dos => isDocumentFragment$1(dos) && isNonNullable(dos.dom.host);\n    const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);\n    const isSupported = constant(supported);\n    const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;\n    const getStyleContainer = dos => isShadowRoot(dos) ? dos : getHead(documentOrOwner(dos));\n    const getContentContainer = dos => isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body);\n    const getShadowRoot = e => {\n      const r = getRootNode(e);\n      return isShadowRoot(r) ? Optional.some(r) : Optional.none();\n    };\n    const getShadowHost = e => SugarElement.fromDom(e.dom.host);\n    const getOriginalEventTarget = event => {\n      if (isSupported() && isNonNullable(event.target)) {\n        const el = SugarElement.fromDom(event.target);\n        if (isElement$7(el) && isOpenShadowHost(el)) {\n          if (event.composed && event.composedPath) {\n            const composedPath = event.composedPath();\n            if (composedPath) {\n              return head(composedPath);\n            }\n          }\n        }\n      }\n      return Optional.from(event.target);\n    };\n    const isOpenShadowHost = element => isNonNullable(element.dom.shadowRoot);\n\n    const inBody = element => {\n      const dom = isText$b(element) ? element.dom.parentNode : element.dom;\n      if (dom === undefined || dom === null || dom.ownerDocument === null) {\n        return false;\n      }\n      const doc = dom.ownerDocument;\n      return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));\n    };\n\n    const internalSet = (dom, property, value) => {\n      if (!isString(value)) {\n        console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);\n        throw new Error('CSS value must be a string: ' + value);\n      }\n      if (isSupported$1(dom)) {\n        dom.style.setProperty(property, value);\n      }\n    };\n    const internalRemove = (dom, property) => {\n      if (isSupported$1(dom)) {\n        dom.style.removeProperty(property);\n      }\n    };\n    const set$1 = (element, property, value) => {\n      const dom = element.dom;\n      internalSet(dom, property, value);\n    };\n    const setAll = (element, css) => {\n      const dom = element.dom;\n      each$d(css, (v, k) => {\n        internalSet(dom, k, v);\n      });\n    };\n    const get$7 = (element, property) => {\n      const dom = element.dom;\n      const styles = window.getComputedStyle(dom);\n      const r = styles.getPropertyValue(property);\n      return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;\n    };\n    const getUnsafeProperty = (dom, property) => isSupported$1(dom) ? dom.style.getPropertyValue(property) : '';\n    const getRaw$1 = (element, property) => {\n      const dom = element.dom;\n      const raw = getUnsafeProperty(dom, property);\n      return Optional.from(raw).filter(r => r.length > 0);\n    };\n    const getAllRaw = element => {\n      const css = {};\n      const dom = element.dom;\n      if (isSupported$1(dom)) {\n        for (let i = 0; i < dom.style.length; i++) {\n          const ruleName = dom.style.item(i);\n          css[ruleName] = dom.style[ruleName];\n        }\n      }\n      return css;\n    };\n    const remove$7 = (element, property) => {\n      const dom = element.dom;\n      internalRemove(dom, property);\n      if (is$2(getOpt(element, 'style').map(trim$3), '')) {\n        remove$b(element, 'style');\n      }\n    };\n    const reflow = e => e.dom.offsetWidth;\n\n    const before$3 = (marker, element) => {\n      const parent$1 = parent(marker);\n      parent$1.each(v => {\n        v.dom.insertBefore(element.dom, marker.dom);\n      });\n    };\n    const after$4 = (marker, element) => {\n      const sibling = nextSibling(marker);\n      sibling.fold(() => {\n        const parent$1 = parent(marker);\n        parent$1.each(v => {\n          append$1(v, element);\n        });\n      }, v => {\n        before$3(v, element);\n      });\n    };\n    const prepend = (parent, element) => {\n      const firstChild$1 = firstChild(parent);\n      firstChild$1.fold(() => {\n        append$1(parent, element);\n      }, v => {\n        parent.dom.insertBefore(element.dom, v.dom);\n      });\n    };\n    const append$1 = (parent, element) => {\n      parent.dom.appendChild(element.dom);\n    };\n    const wrap$2 = (element, wrapper) => {\n      before$3(element, wrapper);\n      append$1(wrapper, element);\n    };\n\n    const after$3 = (marker, elements) => {\n      each$e(elements, (x, i) => {\n        const e = i === 0 ? marker : elements[i - 1];\n        after$4(e, x);\n      });\n    };\n    const append = (parent, elements) => {\n      each$e(elements, x => {\n        append$1(parent, x);\n      });\n    };\n\n    const empty = element => {\n      element.dom.textContent = '';\n      each$e(children$1(element), rogue => {\n        remove$6(rogue);\n      });\n    };\n    const remove$6 = element => {\n      const dom = element.dom;\n      if (dom.parentNode !== null) {\n        dom.parentNode.removeChild(dom);\n      }\n    };\n    const unwrap = wrapper => {\n      const children = children$1(wrapper);\n      if (children.length > 0) {\n        after$3(wrapper, children);\n      }\n      remove$6(wrapper);\n    };\n\n    const fromHtml = (html, scope) => {\n      const doc = scope || document;\n      const div = doc.createElement('div');\n      div.innerHTML = html;\n      return children$1(SugarElement.fromDom(div));\n    };\n    const fromDom$1 = nodes => map$3(nodes, SugarElement.fromDom);\n\n    const get$6 = element => element.dom.innerHTML;\n    const set = (element, content) => {\n      const owner = owner$1(element);\n      const docDom = owner.dom;\n      const fragment = SugarElement.fromDom(docDom.createDocumentFragment());\n      const contentElements = fromHtml(content, docDom);\n      append(fragment, contentElements);\n      empty(element);\n      append$1(element, fragment);\n    };\n    const getOuter = element => {\n      const container = SugarElement.fromTag('div');\n      const clone = SugarElement.fromDom(element.dom.cloneNode(true));\n      append$1(container, clone);\n      return get$6(container);\n    };\n\n    const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({\n      target,\n      x,\n      y,\n      stop,\n      prevent,\n      kill,\n      raw\n    });\n    const fromRawEvent = rawEvent => {\n      const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));\n      const stop = () => rawEvent.stopPropagation();\n      const prevent = () => rawEvent.preventDefault();\n      const kill = compose(prevent, stop);\n      return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);\n    };\n    const handle$1 = (filter, handler) => rawEvent => {\n      if (filter(rawEvent)) {\n        handler(fromRawEvent(rawEvent));\n      }\n    };\n    const binder = (element, event, filter, handler, useCapture) => {\n      const wrapped = handle$1(filter, handler);\n      element.dom.addEventListener(event, wrapped, useCapture);\n      return { unbind: curry(unbind, element, event, wrapped, useCapture) };\n    };\n    const bind$2 = (element, event, filter, handler) => binder(element, event, filter, handler, false);\n    const unbind = (element, event, handler, useCapture) => {\n      element.dom.removeEventListener(event, handler, useCapture);\n    };\n\n    const r = (left, top) => {\n      const translate = (x, y) => r(left + x, top + y);\n      return {\n        left,\n        top,\n        translate\n      };\n    };\n    const SugarPosition = r;\n\n    const boxPosition = dom => {\n      const box = dom.getBoundingClientRect();\n      return SugarPosition(box.left, box.top);\n    };\n    const firstDefinedOrZero = (a, b) => {\n      if (a !== undefined) {\n        return a;\n      } else {\n        return b !== undefined ? b : 0;\n      }\n    };\n    const absolute = element => {\n      const doc = element.dom.ownerDocument;\n      const body = doc.body;\n      const win = doc.defaultView;\n      const html = doc.documentElement;\n      if (body === element.dom) {\n        return SugarPosition(body.offsetLeft, body.offsetTop);\n      }\n      const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop);\n      const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft);\n      const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);\n      const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);\n      return viewport(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);\n    };\n    const viewport = element => {\n      const dom = element.dom;\n      const doc = dom.ownerDocument;\n      const body = doc.body;\n      if (body === dom) {\n        return SugarPosition(body.offsetLeft, body.offsetTop);\n      }\n      if (!inBody(element)) {\n        return SugarPosition(0, 0);\n      }\n      return boxPosition(dom);\n    };\n\n    const get$5 = _DOC => {\n      const doc = _DOC !== undefined ? _DOC.dom : document;\n      const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;\n      const y = doc.body.scrollTop || doc.documentElement.scrollTop;\n      return SugarPosition(x, y);\n    };\n    const to = (x, y, _DOC) => {\n      const doc = _DOC !== undefined ? _DOC.dom : document;\n      const win = doc.defaultView;\n      if (win) {\n        win.scrollTo(x, y);\n      }\n    };\n    const intoView = (element, alignToTop) => {\n      const isSafari = detect$2().browser.isSafari();\n      if (isSafari && isFunction(element.dom.scrollIntoViewIfNeeded)) {\n        element.dom.scrollIntoViewIfNeeded(false);\n      } else {\n        element.dom.scrollIntoView(alignToTop);\n      }\n    };\n\n    const get$4 = _win => {\n      const win = _win === undefined ? window : _win;\n      if (detect$2().browser.isFirefox()) {\n        return Optional.none();\n      } else {\n        return Optional.from(win.visualViewport);\n      }\n    };\n    const bounds = (x, y, width, height) => ({\n      x,\n      y,\n      width,\n      height,\n      right: x + width,\n      bottom: y + height\n    });\n    const getBounds = _win => {\n      const win = _win === undefined ? window : _win;\n      const doc = win.document;\n      const scroll = get$5(SugarElement.fromDom(doc));\n      return get$4(win).fold(() => {\n        const html = win.document.documentElement;\n        const width = html.clientWidth;\n        const height = html.clientHeight;\n        return bounds(scroll.left, scroll.top, width, height);\n      }, visualViewport => bounds(Math.max(visualViewport.pageLeft, scroll.left), Math.max(visualViewport.pageTop, scroll.top), visualViewport.width, visualViewport.height));\n    };\n\n    const children = (scope, predicate) => filter$5(children$1(scope), predicate);\n    const descendants$1 = (scope, predicate) => {\n      let result = [];\n      each$e(children$1(scope), x => {\n        if (predicate(x)) {\n          result = result.concat([x]);\n        }\n        result = result.concat(descendants$1(x, predicate));\n      });\n      return result;\n    };\n\n    var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {\n      if (is(scope, a)) {\n        return Optional.some(scope);\n      } else if (isFunction(isRoot) && isRoot(scope)) {\n        return Optional.none();\n      } else {\n        return ancestor(scope, a, isRoot);\n      }\n    };\n\n    const ancestor$3 = (scope, predicate, isRoot) => {\n      let element = scope.dom;\n      const stop = isFunction(isRoot) ? isRoot : never;\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        if (predicate(el)) {\n          return Optional.some(el);\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const closest$4 = (scope, predicate, isRoot) => {\n      const is = (s, test) => test(s);\n      return ClosestOrAncestor(is, ancestor$3, scope, predicate, isRoot);\n    };\n    const sibling$1 = (scope, predicate) => {\n      const element = scope.dom;\n      if (!element.parentNode) {\n        return Optional.none();\n      }\n      return child(SugarElement.fromDom(element.parentNode), x => !eq(scope, x) && predicate(x));\n    };\n    const child = (scope, predicate) => {\n      const pred = node => predicate(SugarElement.fromDom(node));\n      const result = find$2(scope.dom.childNodes, pred);\n      return result.map(SugarElement.fromDom);\n    };\n    const descendant$1 = (scope, predicate) => {\n      const descend = node => {\n        for (let i = 0; i < node.childNodes.length; i++) {\n          const child = SugarElement.fromDom(node.childNodes[i]);\n          if (predicate(child)) {\n            return Optional.some(child);\n          }\n          const res = descend(node.childNodes[i]);\n          if (res.isSome()) {\n            return res;\n          }\n        }\n        return Optional.none();\n      };\n      return descend(scope.dom);\n    };\n\n    const ancestor$2 = (scope, selector, isRoot) => ancestor$3(scope, e => is$1(e, selector), isRoot);\n    const descendant = (scope, selector) => one(selector, scope);\n    const closest$3 = (scope, selector, isRoot) => {\n      const is = (element, selector) => is$1(element, selector);\n      return ClosestOrAncestor(is, ancestor$2, scope, selector, isRoot);\n    };\n\n    const ancestor$1 = (scope, selector, isRoot) => ancestor$2(scope, selector, isRoot).isSome();\n\n    class DomTreeWalker {\n      constructor(startNode, rootNode) {\n        this.node = startNode;\n        this.rootNode = rootNode;\n        this.current = this.current.bind(this);\n        this.next = this.next.bind(this);\n        this.prev = this.prev.bind(this);\n        this.prev2 = this.prev2.bind(this);\n      }\n      current() {\n        return this.node;\n      }\n      next(shallow) {\n        this.node = this.findSibling(this.node, 'firstChild', 'nextSibling', shallow);\n        return this.node;\n      }\n      prev(shallow) {\n        this.node = this.findSibling(this.node, 'lastChild', 'previousSibling', shallow);\n        return this.node;\n      }\n      prev2(shallow) {\n        this.node = this.findPreviousNode(this.node, shallow);\n        return this.node;\n      }\n      findSibling(node, startName, siblingName, shallow) {\n        if (node) {\n          if (!shallow && node[startName]) {\n            return node[startName];\n          }\n          if (node !== this.rootNode) {\n            let sibling = node[siblingName];\n            if (sibling) {\n              return sibling;\n            }\n            for (let parent = node.parentNode; parent && parent !== this.rootNode; parent = parent.parentNode) {\n              sibling = parent[siblingName];\n              if (sibling) {\n                return sibling;\n              }\n            }\n          }\n        }\n        return undefined;\n      }\n      findPreviousNode(node, shallow) {\n        if (node) {\n          const sibling = node.previousSibling;\n          if (this.rootNode && sibling === this.rootNode) {\n            return;\n          }\n          if (sibling) {\n            if (!shallow) {\n              for (let child = sibling.lastChild; child; child = child.lastChild) {\n                if (!child.lastChild) {\n                  return child;\n                }\n              }\n            }\n            return sibling;\n          }\n          const parent = node.parentNode;\n          if (parent && parent !== this.rootNode) {\n            return parent;\n          }\n        }\n        return undefined;\n      }\n    }\n\n    const isNodeType = type => {\n      return node => {\n        return !!node && node.nodeType === type;\n      };\n    };\n    const isRestrictedNode = node => !!node && !Object.getPrototypeOf(node);\n    const isElement$6 = isNodeType(1);\n    const matchNodeName = name => {\n      const lowerCasedName = name.toLowerCase();\n      return node => isNonNullable(node) && node.nodeName.toLowerCase() === lowerCasedName;\n    };\n    const matchNodeNames = names => {\n      const lowerCasedNames = names.map(s => s.toLowerCase());\n      return node => {\n        if (node && node.nodeName) {\n          const nodeName = node.nodeName.toLowerCase();\n          return contains$2(lowerCasedNames, nodeName);\n        }\n        return false;\n      };\n    };\n    const matchStyleValues = (name, values) => {\n      const items = values.toLowerCase().split(' ');\n      return node => {\n        if (isElement$6(node)) {\n          const win = node.ownerDocument.defaultView;\n          if (win) {\n            for (let i = 0; i < items.length; i++) {\n              const computed = win.getComputedStyle(node, null);\n              const cssValue = computed ? computed.getPropertyValue(name) : null;\n              if (cssValue === items[i]) {\n                return true;\n              }\n            }\n          }\n        }\n        return false;\n      };\n    };\n    const hasAttribute = attrName => {\n      return node => {\n        return isElement$6(node) && node.hasAttribute(attrName);\n      };\n    };\n    const hasAttributeValue = (attrName, attrValue) => {\n      return node => {\n        return isElement$6(node) && node.getAttribute(attrName) === attrValue;\n      };\n    };\n    const isBogus$2 = node => isElement$6(node) && node.hasAttribute('data-mce-bogus');\n    const isBogusAll$1 = node => isElement$6(node) && node.getAttribute('data-mce-bogus') === 'all';\n    const isTable$2 = node => isElement$6(node) && node.tagName === 'TABLE';\n    const hasContentEditableState = value => {\n      return node => {\n        if (isElement$6(node)) {\n          if (node.contentEditable === value) {\n            return true;\n          }\n          if (node.getAttribute('data-mce-contenteditable') === value) {\n            return true;\n          }\n        }\n        return false;\n      };\n    };\n    const isTextareaOrInput = matchNodeNames([\n      'textarea',\n      'input'\n    ]);\n    const isText$a = isNodeType(3);\n    const isCData = isNodeType(4);\n    const isPi = isNodeType(7);\n    const isComment = isNodeType(8);\n    const isDocument$1 = isNodeType(9);\n    const isDocumentFragment = isNodeType(11);\n    const isBr$6 = matchNodeName('br');\n    const isImg = matchNodeName('img');\n    const isContentEditableTrue$3 = hasContentEditableState('true');\n    const isContentEditableFalse$a = hasContentEditableState('false');\n    const isTableCell$3 = matchNodeNames([\n      'td',\n      'th'\n    ]);\n    const isTableCellOrCaption = matchNodeNames([\n      'td',\n      'th',\n      'caption'\n    ]);\n    const isMedia$2 = matchNodeNames([\n      'video',\n      'audio',\n      'object',\n      'embed'\n    ]);\n    const isListItem$2 = matchNodeName('li');\n\n    const zeroWidth = '\\uFEFF';\n    const nbsp = '\\xA0';\n    const isZwsp$1 = char => char === zeroWidth;\n    const removeZwsp = s => s.replace(/\\uFEFF/g, '');\n\n    const descendants = (scope, selector) => all(selector, scope);\n\n    const NodeValue = (is, name) => {\n      const get = element => {\n        if (!is(element)) {\n          throw new Error('Can only get ' + name + ' value of a ' + name + ' node');\n        }\n        return getOption(element).getOr('');\n      };\n      const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();\n      const set = (element, value) => {\n        if (!is(element)) {\n          throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');\n        }\n        element.dom.nodeValue = value;\n      };\n      return {\n        get,\n        getOption,\n        set\n      };\n    };\n\n    const api$1 = NodeValue(isText$b, 'text');\n    const get$3 = element => api$1.get(element);\n    const getOption = element => api$1.getOption(element);\n\n    const blocks = [\n      'article',\n      'aside',\n      'details',\n      'div',\n      'dt',\n      'figcaption',\n      'footer',\n      'form',\n      'fieldset',\n      'header',\n      'hgroup',\n      'html',\n      'main',\n      'nav',\n      'section',\n      'summary',\n      'body',\n      'p',\n      'dl',\n      'multicol',\n      'dd',\n      'figure',\n      'address',\n      'center',\n      'blockquote',\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'listing',\n      'xmp',\n      'pre',\n      'plaintext',\n      'menu',\n      'dir',\n      'ul',\n      'ol',\n      'li',\n      'hr',\n      'table',\n      'tbody',\n      'thead',\n      'tfoot',\n      'th',\n      'tr',\n      'td',\n      'caption'\n    ];\n    const tableCells = [\n      'td',\n      'th'\n    ];\n    const tableSections = [\n      'thead',\n      'tbody',\n      'tfoot'\n    ];\n    const textBlocks = [\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'p',\n      'div',\n      'address',\n      'pre',\n      'form',\n      'blockquote',\n      'center',\n      'dir',\n      'fieldset',\n      'header',\n      'footer',\n      'article',\n      'section',\n      'hgroup',\n      'aside',\n      'nav',\n      'figure'\n    ];\n    const headings = [\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6'\n    ];\n    const listItems$1 = [\n      'li',\n      'dd',\n      'dt'\n    ];\n    const lists = [\n      'ul',\n      'ol',\n      'dl'\n    ];\n    const wsElements = [\n      'pre',\n      'script',\n      'textarea',\n      'style'\n    ];\n    const wrapBlockElements = ['pre'].concat(headings);\n    const lazyLookup = items => {\n      let lookup;\n      return node => {\n        lookup = lookup ? lookup : mapToObject(items, always);\n        return has$2(lookup, name(node));\n      };\n    };\n    const isBlock$2 = lazyLookup(blocks);\n    const isTable$1 = node => name(node) === 'table';\n    const isInline$1 = node => isElement$7(node) && !isBlock$2(node);\n    const isBr$5 = node => isElement$7(node) && name(node) === 'br';\n    const isTextBlock$2 = lazyLookup(textBlocks);\n    const isList = lazyLookup(lists);\n    const isListItem$1 = lazyLookup(listItems$1);\n    const isTableSection = lazyLookup(tableSections);\n    const isTableCell$2 = lazyLookup(tableCells);\n    const isWsPreserveElement = lazyLookup(wsElements);\n    const isWrapBlockElement = lazyLookup(wrapBlockElements);\n    const isWrapElement = node => isWrapBlockElement(node) || isInline$1(node);\n\n    const getLastChildren$1 = elm => {\n      const children = [];\n      let rawNode = elm.dom;\n      while (rawNode) {\n        children.push(SugarElement.fromDom(rawNode));\n        rawNode = rawNode.lastChild;\n      }\n      return children;\n    };\n    const removeTrailingBr = elm => {\n      const allBrs = descendants(elm, 'br');\n      const brs = filter$5(getLastChildren$1(elm).slice(-1), isBr$5);\n      if (allBrs.length === brs.length) {\n        each$e(brs, remove$6);\n      }\n    };\n    const createPaddingBr = () => {\n      const br = SugarElement.fromTag('br');\n      set$2(br, 'data-mce-bogus', '1');\n      return br;\n    };\n    const fillWithPaddingBr = elm => {\n      empty(elm);\n      append$1(elm, createPaddingBr());\n    };\n    const trimBlockTrailingBr = elm => {\n      lastChild(elm).each(lastChild => {\n        prevSibling(lastChild).each(lastChildPrevSibling => {\n          if (isBlock$2(elm) && isBr$5(lastChild) && isBlock$2(lastChildPrevSibling)) {\n            remove$6(lastChild);\n          }\n        });\n      });\n    };\n\n    const ZWSP$1 = zeroWidth;\n    const isZwsp = isZwsp$1;\n    const trim$1 = removeZwsp;\n\n    const isElement$5 = isElement$6;\n    const isText$9 = isText$a;\n    const isCaretContainerBlock$1 = node => {\n      if (isText$9(node)) {\n        node = node.parentNode;\n      }\n      return isElement$5(node) && node.hasAttribute('data-mce-caret');\n    };\n    const isCaretContainerInline = node => isText$9(node) && isZwsp(node.data);\n    const isCaretContainer$2 = node => isCaretContainerBlock$1(node) || isCaretContainerInline(node);\n    const hasContent = node => node.firstChild !== node.lastChild || !isBr$6(node.firstChild);\n    const insertInline$1 = (node, before) => {\n      var _a;\n      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;\n      const textNode = doc.createTextNode(ZWSP$1);\n      const parentNode = node.parentNode;\n      if (!before) {\n        const sibling = node.nextSibling;\n        if (isText$9(sibling)) {\n          if (isCaretContainer$2(sibling)) {\n            return sibling;\n          }\n          if (startsWithCaretContainer$1(sibling)) {\n            sibling.splitText(1);\n            return sibling;\n          }\n        }\n        if (node.nextSibling) {\n          parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(textNode, node.nextSibling);\n        } else {\n          parentNode === null || parentNode === void 0 ? void 0 : parentNode.appendChild(textNode);\n        }\n      } else {\n        const sibling = node.previousSibling;\n        if (isText$9(sibling)) {\n          if (isCaretContainer$2(sibling)) {\n            return sibling;\n          }\n          if (endsWithCaretContainer$1(sibling)) {\n            return sibling.splitText(sibling.data.length - 1);\n          }\n        }\n        parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(textNode, node);\n      }\n      return textNode;\n    };\n    const isBeforeInline = pos => {\n      const container = pos.container();\n      if (!isText$a(container)) {\n        return false;\n      }\n      return container.data.charAt(pos.offset()) === ZWSP$1 || pos.isAtStart() && isCaretContainerInline(container.previousSibling);\n    };\n    const isAfterInline = pos => {\n      const container = pos.container();\n      if (!isText$a(container)) {\n        return false;\n      }\n      return container.data.charAt(pos.offset() - 1) === ZWSP$1 || pos.isAtEnd() && isCaretContainerInline(container.nextSibling);\n    };\n    const insertBlock = (blockName, node, before) => {\n      var _a;\n      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;\n      const blockNode = doc.createElement(blockName);\n      blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after');\n      blockNode.setAttribute('data-mce-bogus', 'all');\n      blockNode.appendChild(createPaddingBr().dom);\n      const parentNode = node.parentNode;\n      if (!before) {\n        if (node.nextSibling) {\n          parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(blockNode, node.nextSibling);\n        } else {\n          parentNode === null || parentNode === void 0 ? void 0 : parentNode.appendChild(blockNode);\n        }\n      } else {\n        parentNode === null || parentNode === void 0 ? void 0 : parentNode.insertBefore(blockNode, node);\n      }\n      return blockNode;\n    };\n    const startsWithCaretContainer$1 = node => isText$9(node) && node.data[0] === ZWSP$1;\n    const endsWithCaretContainer$1 = node => isText$9(node) && node.data[node.data.length - 1] === ZWSP$1;\n    const trimBogusBr = elm => {\n      var _a;\n      const brs = elm.getElementsByTagName('br');\n      const lastBr = brs[brs.length - 1];\n      if (isBogus$2(lastBr)) {\n        (_a = lastBr.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(lastBr);\n      }\n    };\n    const showCaretContainerBlock = caretContainer => {\n      if (caretContainer && caretContainer.hasAttribute('data-mce-caret')) {\n        trimBogusBr(caretContainer);\n        caretContainer.removeAttribute('data-mce-caret');\n        caretContainer.removeAttribute('data-mce-bogus');\n        caretContainer.removeAttribute('style');\n        caretContainer.removeAttribute('data-mce-style');\n        caretContainer.removeAttribute('_moz_abspos');\n        return caretContainer;\n      }\n      return null;\n    };\n    const isRangeInCaretContainerBlock = range => isCaretContainerBlock$1(range.startContainer);\n\n    const isContentEditableTrue$2 = isContentEditableTrue$3;\n    const isContentEditableFalse$9 = isContentEditableFalse$a;\n    const isBr$4 = isBr$6;\n    const isText$8 = isText$a;\n    const isInvalidTextElement = matchNodeNames([\n      'script',\n      'style',\n      'textarea'\n    ]);\n    const isAtomicInline = matchNodeNames([\n      'img',\n      'input',\n      'textarea',\n      'hr',\n      'iframe',\n      'video',\n      'audio',\n      'object',\n      'embed'\n    ]);\n    const isTable = matchNodeNames(['table']);\n    const isCaretContainer$1 = isCaretContainer$2;\n    const isCaretCandidate$3 = node => {\n      if (isCaretContainer$1(node)) {\n        return false;\n      }\n      if (isText$8(node)) {\n        return !isInvalidTextElement(node.parentNode);\n      }\n      return isAtomicInline(node) || isBr$4(node) || isTable(node) || isNonUiContentEditableFalse(node);\n    };\n    const isUnselectable = node => isElement$6(node) && node.getAttribute('unselectable') === 'true';\n    const isNonUiContentEditableFalse = node => !isUnselectable(node) && isContentEditableFalse$9(node);\n    const isInEditable = (node, root) => {\n      for (let tempNode = node.parentNode; tempNode && tempNode !== root; tempNode = tempNode.parentNode) {\n        if (isNonUiContentEditableFalse(tempNode)) {\n          return false;\n        }\n        if (isContentEditableTrue$2(tempNode)) {\n          return true;\n        }\n      }\n      return true;\n    };\n    const isAtomicContentEditableFalse = node => {\n      if (!isNonUiContentEditableFalse(node)) {\n        return false;\n      }\n      return !foldl(from(node.getElementsByTagName('*')), (result, elm) => {\n        return result || isContentEditableTrue$2(elm);\n      }, false);\n    };\n    const isAtomic$1 = node => isAtomicInline(node) || isAtomicContentEditableFalse(node);\n    const isEditableCaretCandidate$1 = (node, root) => isCaretCandidate$3(node) && isInEditable(node, root);\n\n    const whiteSpaceRegExp = /^[ \\t\\r\\n]*$/;\n    const isWhitespaceText = text => whiteSpaceRegExp.test(text);\n    const isCollapsibleWhitespace$1 = c => ' \\f\\t\\x0B'.indexOf(c) !== -1;\n    const isNewLineChar = c => c === '\\n' || c === '\\r';\n    const isNewline = (text, idx) => idx < text.length && idx >= 0 ? isNewLineChar(text[idx]) : false;\n    const normalize$4 = (text, tabSpaces = 4, isStartOfContent = true, isEndOfContent = true) => {\n      const tabSpace = repeat(' ', tabSpaces);\n      const normalizedText = text.replace(/\\t/g, tabSpace);\n      const result = foldl(normalizedText, (acc, c) => {\n        if (isCollapsibleWhitespace$1(c) || c === nbsp) {\n          if (acc.pcIsSpace || acc.str === '' && isStartOfContent || acc.str.length === normalizedText.length - 1 && isEndOfContent || isNewline(normalizedText, acc.str.length + 1)) {\n            return {\n              pcIsSpace: false,\n              str: acc.str + nbsp\n            };\n          } else {\n            return {\n              pcIsSpace: true,\n              str: acc.str + ' '\n            };\n          }\n        } else {\n          return {\n            pcIsSpace: isNewLineChar(c),\n            str: acc.str + c\n          };\n        }\n      }, {\n        pcIsSpace: false,\n        str: ''\n      });\n      return result.str;\n    };\n\n    const hasWhitespacePreserveParent = (node, rootNode) => {\n      const rootElement = SugarElement.fromDom(rootNode);\n      const startNode = SugarElement.fromDom(node);\n      return ancestor$1(startNode, 'pre,code', curry(eq, rootElement));\n    };\n    const isWhitespace$1 = (node, rootNode) => {\n      return isText$a(node) && isWhitespaceText(node.data) && !hasWhitespacePreserveParent(node, rootNode);\n    };\n    const isNamedAnchor = node => {\n      return isElement$6(node) && node.nodeName === 'A' && !node.hasAttribute('href') && (node.hasAttribute('name') || node.hasAttribute('id'));\n    };\n    const isContent$1 = (node, rootNode) => {\n      return isCaretCandidate$3(node) && !isWhitespace$1(node, rootNode) || isNamedAnchor(node) || isBookmark(node);\n    };\n    const isBookmark = hasAttribute('data-mce-bookmark');\n    const isBogus$1 = hasAttribute('data-mce-bogus');\n    const isBogusAll = hasAttributeValue('data-mce-bogus', 'all');\n    const isEmptyNode = (targetNode, skipBogus) => {\n      let brCount = 0;\n      if (isContent$1(targetNode, targetNode)) {\n        return false;\n      } else {\n        let node = targetNode.firstChild;\n        if (!node) {\n          return true;\n        }\n        const walker = new DomTreeWalker(node, targetNode);\n        do {\n          if (skipBogus) {\n            if (isBogusAll(node)) {\n              node = walker.next(true);\n              continue;\n            }\n            if (isBogus$1(node)) {\n              node = walker.next();\n              continue;\n            }\n          }\n          if (isBr$6(node)) {\n            brCount++;\n            node = walker.next();\n            continue;\n          }\n          if (isContent$1(node, targetNode)) {\n            return false;\n          }\n          node = walker.next();\n        } while (node);\n        return brCount <= 1;\n      }\n    };\n    const isEmpty$2 = (elm, skipBogus = true) => isEmptyNode(elm.dom, skipBogus);\n\n    const transparentBlockAttr = 'data-mce-block';\n    const elementNames = map => filter$5(keys(map), key => !/[A-Z]/.test(key));\n    const makeSelectorFromSchemaMap = map => elementNames(map).join(',');\n    const updateTransparent = (blocksSelector, transparent) => {\n      if (isNonNullable(transparent.querySelector(blocksSelector))) {\n        transparent.setAttribute(transparentBlockAttr, 'true');\n        if (transparent.getAttribute('data-mce-selected') === 'inline-boundary') {\n          transparent.removeAttribute('data-mce-selected');\n        }\n        return true;\n      } else {\n        transparent.removeAttribute(transparentBlockAttr);\n        return false;\n      }\n    };\n    const updateBlockStateOnChildren = (schema, scope) => {\n      const transparentSelector = makeSelectorFromSchemaMap(schema.getTransparentElements());\n      const blocksSelector = makeSelectorFromSchemaMap(schema.getBlockElements());\n      return filter$5(scope.querySelectorAll(transparentSelector), transparent => updateTransparent(blocksSelector, transparent));\n    };\n    const trimEdge = (el, leftSide) => {\n      var _a;\n      const childPropertyName = leftSide ? 'lastChild' : 'firstChild';\n      for (let child = el[childPropertyName]; child; child = child[childPropertyName]) {\n        if (isEmpty$2(SugarElement.fromDom(child))) {\n          (_a = child.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(child);\n          return;\n        }\n      }\n    };\n    const split$2 = (parentElm, splitElm) => {\n      const range = document.createRange();\n      const parentNode = parentElm.parentNode;\n      if (parentNode) {\n        range.setStartBefore(parentElm);\n        range.setEndBefore(splitElm);\n        const beforeFragment = range.extractContents();\n        trimEdge(beforeFragment, true);\n        range.setStartAfter(splitElm);\n        range.setEndAfter(parentElm);\n        const afterFragment = range.extractContents();\n        trimEdge(afterFragment, false);\n        if (!isEmpty$2(SugarElement.fromDom(beforeFragment))) {\n          parentNode.insertBefore(beforeFragment, parentElm);\n        }\n        if (!isEmpty$2(SugarElement.fromDom(splitElm))) {\n          parentNode.insertBefore(splitElm, parentElm);\n        }\n        if (!isEmpty$2(SugarElement.fromDom(afterFragment))) {\n          parentNode.insertBefore(afterFragment, parentElm);\n        }\n        parentNode.removeChild(parentElm);\n      }\n    };\n    const splitInvalidChildren = (schema, scope, transparentBlocks) => {\n      const blocksElements = schema.getBlockElements();\n      const rootNode = SugarElement.fromDom(scope);\n      const isBlock = el => name(el) in blocksElements;\n      const isRoot = el => eq(el, rootNode);\n      each$e(fromDom$1(transparentBlocks), transparentBlock => {\n        ancestor$3(transparentBlock, isBlock, isRoot).each(parentBlock => {\n          const invalidChildren = children(transparentBlock, el => isBlock(el) && !schema.isValidChild(name(parentBlock), name(el)));\n          if (invalidChildren.length > 0) {\n            const stateScope = parentElement(parentBlock);\n            each$e(invalidChildren, child => {\n              ancestor$3(child, isBlock, isRoot).each(parentBlock => {\n                split$2(parentBlock.dom, child.dom);\n              });\n            });\n            stateScope.each(scope => updateBlockStateOnChildren(schema, scope.dom));\n          }\n        });\n      });\n    };\n    const updateChildren = (schema, scope) => {\n      const transparentBlocks = updateBlockStateOnChildren(schema, scope);\n      splitInvalidChildren(schema, scope, transparentBlocks);\n    };\n    const updateElement = (schema, target) => {\n      if (isTransparentElement(schema, target)) {\n        const blocksSelector = makeSelectorFromSchemaMap(schema.getBlockElements());\n        updateTransparent(blocksSelector, target);\n      }\n    };\n    const updateCaret = (schema, root, caretParent) => {\n      const isRoot = el => eq(el, SugarElement.fromDom(root));\n      const parents = parents$1(SugarElement.fromDom(caretParent), isRoot);\n      get$b(parents, parents.length - 2).filter(isElement$7).fold(() => updateChildren(schema, root), scope => updateChildren(schema, scope.dom));\n    };\n    const hasBlockAttr = el => el.hasAttribute(transparentBlockAttr);\n    const isTransparentElementName = (schema, name) => has$2(schema.getTransparentElements(), name);\n    const isTransparentElement = (schema, node) => isElement$6(node) && isTransparentElementName(schema, node.nodeName);\n    const isTransparentBlock = (schema, node) => isTransparentElement(schema, node) && hasBlockAttr(node);\n    const isTransparentAstBlock = (schema, node) => node.type === 1 && isTransparentElementName(schema, node.name) && isString(node.attr(transparentBlockAttr));\n    const isTransparentAstInline = (schema, node) => node.type === 1 && isTransparentElementName(schema, node.name) && isUndefined(node.attr(transparentBlockAttr));\n\n    const browser = detect$2().browser;\n    const firstElement = nodes => find$2(nodes, isElement$7);\n    const getTableCaptionDeltaY = elm => {\n      if (browser.isFirefox() && name(elm) === 'table') {\n        return firstElement(children$1(elm)).filter(elm => {\n          return name(elm) === 'caption';\n        }).bind(caption => {\n          return firstElement(nextSiblings(caption)).map(body => {\n            const bodyTop = body.dom.offsetTop;\n            const captionTop = caption.dom.offsetTop;\n            const captionHeight = caption.dom.offsetHeight;\n            return bodyTop <= captionTop ? -captionHeight : 0;\n          });\n        }).getOr(0);\n      } else {\n        return 0;\n      }\n    };\n    const hasChild = (elm, child) => elm.children && contains$2(elm.children, child);\n    const getPos = (body, elm, rootElm) => {\n      let x = 0, y = 0;\n      const doc = body.ownerDocument;\n      rootElm = rootElm ? rootElm : body;\n      if (elm) {\n        if (rootElm === body && elm.getBoundingClientRect && get$7(SugarElement.fromDom(body), 'position') === 'static') {\n          const pos = elm.getBoundingClientRect();\n          x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - doc.documentElement.clientLeft;\n          y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - doc.documentElement.clientTop;\n          return {\n            x,\n            y\n          };\n        }\n        let offsetParent = elm;\n        while (offsetParent && offsetParent !== rootElm && offsetParent.nodeType && !hasChild(offsetParent, rootElm)) {\n          const castOffsetParent = offsetParent;\n          x += castOffsetParent.offsetLeft || 0;\n          y += castOffsetParent.offsetTop || 0;\n          offsetParent = castOffsetParent.offsetParent;\n        }\n        offsetParent = elm.parentNode;\n        while (offsetParent && offsetParent !== rootElm && offsetParent.nodeType && !hasChild(offsetParent, rootElm)) {\n          x -= offsetParent.scrollLeft || 0;\n          y -= offsetParent.scrollTop || 0;\n          offsetParent = offsetParent.parentNode;\n        }\n        y += getTableCaptionDeltaY(SugarElement.fromDom(elm));\n      }\n      return {\n        x,\n        y\n      };\n    };\n\n    const StyleSheetLoader = (documentOrShadowRoot, settings = {}) => {\n      let idCount = 0;\n      const loadedStates = {};\n      const edos = SugarElement.fromDom(documentOrShadowRoot);\n      const doc = documentOrOwner(edos);\n      const _setReferrerPolicy = referrerPolicy => {\n        settings.referrerPolicy = referrerPolicy;\n      };\n      const _setContentCssCors = contentCssCors => {\n        settings.contentCssCors = contentCssCors;\n      };\n      const addStyle = element => {\n        append$1(getStyleContainer(edos), element);\n      };\n      const removeStyle = id => {\n        const styleContainer = getStyleContainer(edos);\n        descendant(styleContainer, '#' + id).each(remove$6);\n      };\n      const getOrCreateState = url => get$a(loadedStates, url).getOrThunk(() => ({\n        id: 'mce-u' + idCount++,\n        passed: [],\n        failed: [],\n        count: 0\n      }));\n      const load = url => new Promise((success, failure) => {\n        let link;\n        const urlWithSuffix = Tools._addCacheSuffix(url);\n        const state = getOrCreateState(urlWithSuffix);\n        loadedStates[urlWithSuffix] = state;\n        state.count++;\n        const resolve = (callbacks, status) => {\n          each$e(callbacks, call);\n          state.status = status;\n          state.passed = [];\n          state.failed = [];\n          if (link) {\n            link.onload = null;\n            link.onerror = null;\n            link = null;\n          }\n        };\n        const passed = () => resolve(state.passed, 2);\n        const failed = () => resolve(state.failed, 3);\n        if (success) {\n          state.passed.push(success);\n        }\n        if (failure) {\n          state.failed.push(failure);\n        }\n        if (state.status === 1) {\n          return;\n        }\n        if (state.status === 2) {\n          passed();\n          return;\n        }\n        if (state.status === 3) {\n          failed();\n          return;\n        }\n        state.status = 1;\n        const linkElem = SugarElement.fromTag('link', doc.dom);\n        setAll$1(linkElem, {\n          rel: 'stylesheet',\n          type: 'text/css',\n          id: state.id\n        });\n        if (settings.contentCssCors) {\n          set$2(linkElem, 'crossOrigin', 'anonymous');\n        }\n        if (settings.referrerPolicy) {\n          set$2(linkElem, 'referrerpolicy', settings.referrerPolicy);\n        }\n        link = linkElem.dom;\n        link.onload = passed;\n        link.onerror = failed;\n        addStyle(linkElem);\n        set$2(linkElem, 'href', urlWithSuffix);\n      });\n      const loadAll = urls => {\n        const loadedUrls = Promise.allSettled(map$3(urls, url => load(url).then(constant(url))));\n        return loadedUrls.then(results => {\n          const parts = partition$2(results, r => r.status === 'fulfilled');\n          if (parts.fail.length > 0) {\n            return Promise.reject(map$3(parts.fail, result => result.reason));\n          } else {\n            return map$3(parts.pass, result => result.value);\n          }\n        });\n      };\n      const unload = url => {\n        const urlWithSuffix = Tools._addCacheSuffix(url);\n        get$a(loadedStates, urlWithSuffix).each(state => {\n          const count = --state.count;\n          if (count === 0) {\n            delete loadedStates[urlWithSuffix];\n            removeStyle(state.id);\n          }\n        });\n      };\n      const unloadAll = urls => {\n        each$e(urls, url => {\n          unload(url);\n        });\n      };\n      return {\n        load,\n        loadAll,\n        unload,\n        unloadAll,\n        _setReferrerPolicy,\n        _setContentCssCors\n      };\n    };\n\n    const create$d = () => {\n      const map = new WeakMap();\n      const forElement = (referenceElement, settings) => {\n        const root = getRootNode(referenceElement);\n        const rootDom = root.dom;\n        return Optional.from(map.get(rootDom)).getOrThunk(() => {\n          const sl = StyleSheetLoader(rootDom, settings);\n          map.set(rootDom, sl);\n          return sl;\n        });\n      };\n      return { forElement };\n    };\n    const instance = create$d();\n\n    const isSpan = node => node.nodeName.toLowerCase() === 'span';\n    const isInlineContent = (node, root) => isNonNullable(node) && (isContent$1(node, root) || isInline$1(SugarElement.fromDom(node)));\n    const surroundedByInlineContent = (node, root) => {\n      const prev = new DomTreeWalker(node, root).prev(false);\n      const next = new DomTreeWalker(node, root).next(false);\n      const prevIsInline = isUndefined(prev) || isInlineContent(prev, root);\n      const nextIsInline = isUndefined(next) || isInlineContent(next, root);\n      return prevIsInline && nextIsInline;\n    };\n    const isBookmarkNode$2 = node => isSpan(node) && node.getAttribute('data-mce-type') === 'bookmark';\n    const isKeepTextNode = (node, root) => isText$a(node) && node.data.length > 0 && surroundedByInlineContent(node, root);\n    const isKeepElement = node => isElement$6(node) ? node.childNodes.length > 0 : false;\n    const isDocument = node => isDocumentFragment(node) || isDocument$1(node);\n    const trimNode = (dom, node, root) => {\n      var _a;\n      const rootNode = root || node;\n      if (isElement$6(node) && isBookmarkNode$2(node)) {\n        return node;\n      }\n      const children = node.childNodes;\n      for (let i = children.length - 1; i >= 0; i--) {\n        trimNode(dom, children[i], rootNode);\n      }\n      if (isElement$6(node)) {\n        const currentChildren = node.childNodes;\n        if (currentChildren.length === 1 && isBookmarkNode$2(currentChildren[0])) {\n          (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(currentChildren[0], node);\n        }\n      }\n      if (!isDocument(node) && !isContent$1(node, rootNode) && !isKeepElement(node) && !isKeepTextNode(node, rootNode)) {\n        dom.remove(node);\n      }\n      return node;\n    };\n\n    const makeMap$3 = Tools.makeMap;\n    const attrsCharsRegExp = /[&<>\\\"\\u0060\\u007E-\\uD7FF\\uE000-\\uFFEF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g;\n    const textCharsRegExp = /[<>&\\u007E-\\uD7FF\\uE000-\\uFFEF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g;\n    const rawCharsRegExp = /[<>&\\\"\\']/g;\n    const entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi;\n    const asciiMap = {\n      128: '\\u20AC',\n      130: '\\u201A',\n      131: '\\u0192',\n      132: '\\u201E',\n      133: '\\u2026',\n      134: '\\u2020',\n      135: '\\u2021',\n      136: '\\u02c6',\n      137: '\\u2030',\n      138: '\\u0160',\n      139: '\\u2039',\n      140: '\\u0152',\n      142: '\\u017d',\n      145: '\\u2018',\n      146: '\\u2019',\n      147: '\\u201C',\n      148: '\\u201D',\n      149: '\\u2022',\n      150: '\\u2013',\n      151: '\\u2014',\n      152: '\\u02DC',\n      153: '\\u2122',\n      154: '\\u0161',\n      155: '\\u203A',\n      156: '\\u0153',\n      158: '\\u017e',\n      159: '\\u0178'\n    };\n    const baseEntities = {\n      '\"': '&quot;',\n      '\\'': '&#39;',\n      '<': '&lt;',\n      '>': '&gt;',\n      '&': '&amp;',\n      '`': '&#96;'\n    };\n    const reverseEntities = {\n      '&lt;': '<',\n      '&gt;': '>',\n      '&amp;': '&',\n      '&quot;': '\"',\n      '&apos;': `'`\n    };\n    const nativeDecode = text => {\n      const elm = SugarElement.fromTag('div').dom;\n      elm.innerHTML = text;\n      return elm.textContent || elm.innerText || text;\n    };\n    const buildEntitiesLookup = (items, radix) => {\n      const lookup = {};\n      if (items) {\n        const itemList = items.split(',');\n        radix = radix || 10;\n        for (let i = 0; i < itemList.length; i += 2) {\n          const chr = String.fromCharCode(parseInt(itemList[i], radix));\n          if (!baseEntities[chr]) {\n            const entity = '&' + itemList[i + 1] + ';';\n            lookup[chr] = entity;\n            lookup[entity] = chr;\n          }\n        }\n        return lookup;\n      } else {\n        return undefined;\n      }\n    };\n    const namedEntities = buildEntitiesLookup('50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);\n    const encodeRaw = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => {\n      return baseEntities[chr] || chr;\n    });\n    const encodeAllRaw = text => ('' + text).replace(rawCharsRegExp, chr => {\n      return baseEntities[chr] || chr;\n    });\n    const encodeNumeric = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => {\n      if (chr.length > 1) {\n        return '&#' + ((chr.charCodeAt(0) - 55296) * 1024 + (chr.charCodeAt(1) - 56320) + 65536) + ';';\n      }\n      return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';\n    });\n    const encodeNamed = (text, attr, entities) => {\n      const resolveEntities = entities || namedEntities;\n      return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => {\n        return baseEntities[chr] || resolveEntities[chr] || chr;\n      });\n    };\n    const getEncodeFunc = (name, entities) => {\n      const entitiesMap = buildEntitiesLookup(entities) || namedEntities;\n      const encodeNamedAndNumeric = (text, attr) => text.replace(attr ? attrsCharsRegExp : textCharsRegExp, chr => {\n        if (baseEntities[chr] !== undefined) {\n          return baseEntities[chr];\n        }\n        if (entitiesMap[chr] !== undefined) {\n          return entitiesMap[chr];\n        }\n        if (chr.length > 1) {\n          return '&#' + ((chr.charCodeAt(0) - 55296) * 1024 + (chr.charCodeAt(1) - 56320) + 65536) + ';';\n        }\n        return '&#' + chr.charCodeAt(0) + ';';\n      });\n      const encodeCustomNamed = (text, attr) => {\n        return encodeNamed(text, attr, entitiesMap);\n      };\n      const nameMap = makeMap$3(name.replace(/\\+/g, ','));\n      if (nameMap.named && nameMap.numeric) {\n        return encodeNamedAndNumeric;\n      }\n      if (nameMap.named) {\n        if (entities) {\n          return encodeCustomNamed;\n        }\n        return encodeNamed;\n      }\n      if (nameMap.numeric) {\n        return encodeNumeric;\n      }\n      return encodeRaw;\n    };\n    const decode = text => text.replace(entityRegExp, (all, numeric) => {\n      if (numeric) {\n        if (numeric.charAt(0).toLowerCase() === 'x') {\n          numeric = parseInt(numeric.substr(1), 16);\n        } else {\n          numeric = parseInt(numeric, 10);\n        }\n        if (numeric > 65535) {\n          numeric -= 65536;\n          return String.fromCharCode(55296 + (numeric >> 10), 56320 + (numeric & 1023));\n        }\n        return asciiMap[numeric] || String.fromCharCode(numeric);\n      }\n      return reverseEntities[all] || namedEntities[all] || nativeDecode(all);\n    });\n    const Entities = {\n      encodeRaw,\n      encodeAllRaw,\n      encodeNumeric,\n      encodeNamed,\n      getEncodeFunc,\n      decode\n    };\n\n    const lookupCache = {};\n    const mapCache = {};\n    const dummyObj = {};\n    const makeMap$2 = Tools.makeMap, each$b = Tools.each, extend$2 = Tools.extend, explode$2 = Tools.explode, inArray = Tools.inArray;\n    const split$1 = (items, delim) => {\n      items = Tools.trim(items);\n      return items ? items.split(delim || ' ') : [];\n    };\n    const createMap = (defaultValue, extendWith = {}) => {\n      const value = makeMap$2(defaultValue, ' ', makeMap$2(defaultValue.toUpperCase(), ' '));\n      return extend$2(value, extendWith);\n    };\n    const getTextRootBlockElements = schema => createMap('td th li dt dd figcaption caption details summary', schema.getTextBlockElements());\n    const compileSchema = type => {\n      const schema = {};\n      let globalAttributes, blockContent;\n      let phrasingContent, flowContent;\n      const add = (name, attributes = '', children = '') => {\n        const childNames = split$1(children);\n        const names = split$1(name);\n        let ni = names.length;\n        while (ni--) {\n          const attributesOrder = split$1([\n            globalAttributes,\n            attributes\n          ].join(' '));\n          schema[names[ni]] = {\n            attributes: mapToObject(attributesOrder, () => ({})),\n            attributesOrder,\n            children: mapToObject(childNames, constant(dummyObj))\n          };\n        }\n      };\n      const addAttrs = (name, attributes) => {\n        const names = split$1(name);\n        const attrs = split$1(attributes);\n        let ni = names.length;\n        while (ni--) {\n          const schemaItem = schema[names[ni]];\n          for (let i = 0, l = attrs.length; i < l; i++) {\n            schemaItem.attributes[attrs[i]] = {};\n            schemaItem.attributesOrder.push(attrs[i]);\n          }\n        }\n      };\n      if (lookupCache[type]) {\n        return lookupCache[type];\n      }\n      globalAttributes = 'id accesskey class dir lang style tabindex title role';\n      blockContent = 'address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul';\n      phrasingContent = 'a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd ' + 'label map noscript object q s samp script select small span strong sub sup ' + 'textarea u var #text #comment';\n      if (type !== 'html4') {\n        const transparentContent = 'a ins del canvas map';\n        globalAttributes += ' contenteditable contextmenu draggable dropzone ' + 'hidden spellcheck translate';\n        blockContent += ' article aside details dialog figure main header footer hgroup section nav ' + transparentContent;\n        phrasingContent += ' audio canvas command datalist mark meter output picture ' + 'progress time wbr video ruby bdi keygen';\n      }\n      if (type !== 'html5-strict') {\n        globalAttributes += ' xml:lang';\n        const html4PhrasingContent = 'acronym applet basefont big font strike tt';\n        phrasingContent = [\n          phrasingContent,\n          html4PhrasingContent\n        ].join(' ');\n        each$b(split$1(html4PhrasingContent), name => {\n          add(name, '', phrasingContent);\n        });\n        const html4BlockContent = 'center dir isindex noframes';\n        blockContent = [\n          blockContent,\n          html4BlockContent\n        ].join(' ');\n        flowContent = [\n          blockContent,\n          phrasingContent\n        ].join(' ');\n        each$b(split$1(html4BlockContent), name => {\n          add(name, '', flowContent);\n        });\n      }\n      flowContent = flowContent || [\n        blockContent,\n        phrasingContent\n      ].join(' ');\n      add('html', 'manifest', 'head body');\n      add('head', '', 'base command link meta noscript script style title');\n      add('title hr noscript br');\n      add('base', 'href target');\n      add('link', 'href rel media hreflang type sizes hreflang');\n      add('meta', 'name http-equiv content charset');\n      add('style', 'media type scoped');\n      add('script', 'src async defer type charset');\n      add('body', 'onafterprint onbeforeprint onbeforeunload onblur onerror onfocus ' + 'onhashchange onload onmessage onoffline ononline onpagehide onpageshow ' + 'onpopstate onresize onscroll onstorage onunload', flowContent);\n      add('address dt dd div caption', '', flowContent);\n      add('h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn', '', phrasingContent);\n      add('blockquote', 'cite', flowContent);\n      add('ol', 'reversed start type', 'li');\n      add('ul', '', 'li');\n      add('li', 'value', flowContent);\n      add('dl', '', 'dt dd');\n      add('a', 'href target rel media hreflang type', flowContent);\n      add('q', 'cite', phrasingContent);\n      add('ins del', 'cite datetime', flowContent);\n      add('img', 'src sizes srcset alt usemap ismap width height');\n      add('iframe', 'src name width height', flowContent);\n      add('embed', 'src type width height');\n      add('object', 'data type typemustmatch name usemap form width height', [\n        flowContent,\n        'param'\n      ].join(' '));\n      add('param', 'name value');\n      add('map', 'name', [\n        flowContent,\n        'area'\n      ].join(' '));\n      add('area', 'alt coords shape href target rel media hreflang type');\n      add('table', 'border', 'caption colgroup thead tfoot tbody tr' + (type === 'html4' ? ' col' : ''));\n      add('colgroup', 'span', 'col');\n      add('col', 'span');\n      add('tbody thead tfoot', '', 'tr');\n      add('tr', '', 'td th');\n      add('td', 'colspan rowspan headers', flowContent);\n      add('th', 'colspan rowspan headers scope abbr', flowContent);\n      add('form', 'accept-charset action autocomplete enctype method name novalidate target', flowContent);\n      add('fieldset', 'disabled form name', [\n        flowContent,\n        'legend'\n      ].join(' '));\n      add('label', 'form for', phrasingContent);\n      add('input', 'accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate ' + 'formtarget height list max maxlength min multiple name pattern readonly required size src step type value width');\n      add('button', 'disabled form formaction formenctype formmethod formnovalidate formtarget name type value', type === 'html4' ? flowContent : phrasingContent);\n      add('select', 'disabled form multiple name required size', 'option optgroup');\n      add('optgroup', 'disabled label', 'option');\n      add('option', 'disabled label selected value');\n      add('textarea', 'cols dirname disabled form maxlength name readonly required rows wrap');\n      add('menu', 'type label', [\n        flowContent,\n        'li'\n      ].join(' '));\n      add('noscript', '', flowContent);\n      if (type !== 'html4') {\n        add('wbr');\n        add('ruby', '', [\n          phrasingContent,\n          'rt rp'\n        ].join(' '));\n        add('figcaption', '', flowContent);\n        add('mark rt rp summary bdi', '', phrasingContent);\n        add('canvas', 'width height', flowContent);\n        add('video', 'src crossorigin poster preload autoplay mediagroup loop ' + 'muted controls width height buffered', [\n          flowContent,\n          'track source'\n        ].join(' '));\n        add('audio', 'src crossorigin preload autoplay mediagroup loop muted controls ' + 'buffered volume', [\n          flowContent,\n          'track source'\n        ].join(' '));\n        add('picture', '', 'img source');\n        add('source', 'src srcset type media sizes');\n        add('track', 'kind src srclang label default');\n        add('datalist', '', [\n          phrasingContent,\n          'option'\n        ].join(' '));\n        add('article section nav aside main header footer', '', flowContent);\n        add('hgroup', '', 'h1 h2 h3 h4 h5 h6');\n        add('figure', '', [\n          flowContent,\n          'figcaption'\n        ].join(' '));\n        add('time', 'datetime', phrasingContent);\n        add('dialog', 'open', flowContent);\n        add('command', 'type label icon disabled checked radiogroup command');\n        add('output', 'for form name', phrasingContent);\n        add('progress', 'value max', phrasingContent);\n        add('meter', 'value min max low high optimum', phrasingContent);\n        add('details', 'open', [\n          flowContent,\n          'summary'\n        ].join(' '));\n        add('keygen', 'autofocus challenge disabled form keytype name');\n      }\n      if (type !== 'html5-strict') {\n        addAttrs('script', 'language xml:space');\n        addAttrs('style', 'xml:space');\n        addAttrs('object', 'declare classid code codebase codetype archive standby align border hspace vspace');\n        addAttrs('embed', 'align name hspace vspace');\n        addAttrs('param', 'valuetype type');\n        addAttrs('a', 'charset name rev shape coords');\n        addAttrs('br', 'clear');\n        addAttrs('applet', 'codebase archive code object alt name width height align hspace vspace');\n        addAttrs('img', 'name longdesc align border hspace vspace');\n        addAttrs('iframe', 'longdesc frameborder marginwidth marginheight scrolling align');\n        addAttrs('font basefont', 'size color face');\n        addAttrs('input', 'usemap align');\n        addAttrs('select');\n        addAttrs('textarea');\n        addAttrs('h1 h2 h3 h4 h5 h6 div p legend caption', 'align');\n        addAttrs('ul', 'type compact');\n        addAttrs('li', 'type');\n        addAttrs('ol dl menu dir', 'compact');\n        addAttrs('pre', 'width xml:space');\n        addAttrs('hr', 'align noshade size width');\n        addAttrs('isindex', 'prompt');\n        addAttrs('table', 'summary width frame rules cellspacing cellpadding align bgcolor');\n        addAttrs('col', 'width align char charoff valign');\n        addAttrs('colgroup', 'width align char charoff valign');\n        addAttrs('thead', 'align char charoff valign');\n        addAttrs('tr', 'align char charoff valign bgcolor');\n        addAttrs('th', 'axis align char charoff valign nowrap bgcolor width height');\n        addAttrs('form', 'accept');\n        addAttrs('td', 'abbr axis scope align char charoff valign nowrap bgcolor width height');\n        addAttrs('tfoot', 'align char charoff valign');\n        addAttrs('tbody', 'align char charoff valign');\n        addAttrs('area', 'nohref');\n        addAttrs('body', 'background bgcolor text link vlink alink');\n      }\n      if (type !== 'html4') {\n        addAttrs('input button select textarea', 'autofocus');\n        addAttrs('input textarea', 'placeholder');\n        addAttrs('a', 'download');\n        addAttrs('link script img', 'crossorigin');\n        addAttrs('img', 'loading');\n        addAttrs('iframe', 'sandbox seamless allow allowfullscreen loading');\n      }\n      if (type !== 'html4') {\n        each$e([\n          schema.video,\n          schema.audio\n        ], item => {\n          delete item.children.audio;\n          delete item.children.video;\n        });\n      }\n      each$b(split$1('a form meter progress dfn'), name => {\n        if (schema[name]) {\n          delete schema[name].children[name];\n        }\n      });\n      delete schema.caption.children.table;\n      delete schema.script;\n      lookupCache[type] = schema;\n      return schema;\n    };\n    const compileElementMap = (value, mode) => {\n      if (value) {\n        const styles = {};\n        if (isString(value)) {\n          value = { '*': value };\n        }\n        each$b(value, (value, key) => {\n          styles[key] = styles[key.toUpperCase()] = mode === 'map' ? makeMap$2(value, /[, ]/) : explode$2(value, /[, ]/);\n        });\n        return styles;\n      } else {\n        return undefined;\n      }\n    };\n    const Schema = (settings = {}) => {\n      var _a;\n      const elements = {};\n      const children = {};\n      let patternElements = [];\n      const customElementsMap = {};\n      const specialElements = {};\n      const createLookupTable = (option, defaultValue, extendWith) => {\n        const value = settings[option];\n        if (!value) {\n          let newValue = mapCache[option];\n          if (!newValue) {\n            newValue = createMap(defaultValue, extendWith);\n            mapCache[option] = newValue;\n          }\n          return newValue;\n        } else {\n          return makeMap$2(value, /[, ]/, makeMap$2(value.toUpperCase(), /[, ]/));\n        }\n      };\n      const schemaType = (_a = settings.schema) !== null && _a !== void 0 ? _a : 'html5';\n      const schemaItems = compileSchema(schemaType);\n      if (settings.verify_html === false) {\n        settings.valid_elements = '*[*]';\n      }\n      const validStyles = compileElementMap(settings.valid_styles);\n      const invalidStyles = compileElementMap(settings.invalid_styles, 'map');\n      const validClasses = compileElementMap(settings.valid_classes, 'map');\n      const whitespaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object code');\n      const selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');\n      const voidElementsMap = createLookupTable('void_elements', 'area base basefont br col frame hr img input isindex link ' + 'meta param embed source wbr track');\n      const boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' + 'noshade nowrap readonly selected autoplay loop controls allowfullscreen');\n      const nonEmptyOrMoveCaretBeforeOnEnter = 'td th iframe video audio object script code';\n      const nonEmptyElementsMap = createLookupTable('non_empty_elements', nonEmptyOrMoveCaretBeforeOnEnter + ' pre', voidElementsMap);\n      const moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', nonEmptyOrMoveCaretBeforeOnEnter + ' table', voidElementsMap);\n      const textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' + 'blockquote center dir fieldset header footer article section hgroup aside main nav figure');\n      const blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' + 'th tr td li ol ul caption dl dt dd noscript menu isindex option ' + 'datalist select optgroup figcaption details summary', textBlockElementsMap);\n      const textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font s strike u var cite ' + 'dfn code mark q sup sub samp');\n      const transparentElementsMap = createLookupTable('transparent_elements', 'a ins del canvas map');\n      each$b('script noscript iframe noframes noembed title style textarea xmp plaintext'.split(' '), name => {\n        specialElements[name] = new RegExp('</' + name + '[^>]*>', 'gi');\n      });\n      const patternToRegExp = str => new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');\n      const addValidElements = validElements => {\n        const elementRuleRegExp = /^([#+\\-])?([^\\[!\\/]+)(?:\\/([^\\[!]+))?(?:(!?)\\[([^\\]]+)])?$/;\n        const attrRuleRegExp = /^([!\\-])?(\\w+[\\\\:]:\\w+|[^=~<]+)?(?:([=~<])(.*))?$/;\n        const hasPatternsRegExp = /[*?+]/;\n        if (validElements) {\n          const validElementsArr = split$1(validElements, ',');\n          let globalAttributes;\n          let globalAttributesOrder;\n          if (elements['@']) {\n            globalAttributes = elements['@'].attributes;\n            globalAttributesOrder = elements['@'].attributesOrder;\n          }\n          for (let ei = 0, el = validElementsArr.length; ei < el; ei++) {\n            let matches = elementRuleRegExp.exec(validElementsArr[ei]);\n            if (matches) {\n              const prefix = matches[1];\n              const elementName = matches[2];\n              const outputName = matches[3];\n              const attrData = matches[5];\n              const attributes = {};\n              const attributesOrder = [];\n              const element = {\n                attributes,\n                attributesOrder\n              };\n              if (prefix === '#') {\n                element.paddEmpty = true;\n              }\n              if (prefix === '-') {\n                element.removeEmpty = true;\n              }\n              if (matches[4] === '!') {\n                element.removeEmptyAttrs = true;\n              }\n              if (globalAttributes) {\n                each$d(globalAttributes, (value, key) => {\n                  attributes[key] = value;\n                });\n                if (globalAttributesOrder) {\n                  attributesOrder.push(...globalAttributesOrder);\n                }\n              }\n              if (attrData) {\n                const attrDatas = split$1(attrData, '|');\n                for (let ai = 0, al = attrDatas.length; ai < al; ai++) {\n                  matches = attrRuleRegExp.exec(attrDatas[ai]);\n                  if (matches) {\n                    const attr = {};\n                    const attrType = matches[1];\n                    const attrName = matches[2].replace(/[\\\\:]:/g, ':');\n                    const attrPrefix = matches[3];\n                    const value = matches[4];\n                    if (attrType === '!') {\n                      element.attributesRequired = element.attributesRequired || [];\n                      element.attributesRequired.push(attrName);\n                      attr.required = true;\n                    }\n                    if (attrType === '-') {\n                      delete attributes[attrName];\n                      attributesOrder.splice(inArray(attributesOrder, attrName), 1);\n                      continue;\n                    }\n                    if (attrPrefix) {\n                      if (attrPrefix === '=') {\n                        element.attributesDefault = element.attributesDefault || [];\n                        element.attributesDefault.push({\n                          name: attrName,\n                          value\n                        });\n                        attr.defaultValue = value;\n                      }\n                      if (attrPrefix === '~') {\n                        element.attributesForced = element.attributesForced || [];\n                        element.attributesForced.push({\n                          name: attrName,\n                          value\n                        });\n                        attr.forcedValue = value;\n                      }\n                      if (attrPrefix === '<') {\n                        attr.validValues = makeMap$2(value, '?');\n                      }\n                    }\n                    if (hasPatternsRegExp.test(attrName)) {\n                      const attrPattern = attr;\n                      element.attributePatterns = element.attributePatterns || [];\n                      attrPattern.pattern = patternToRegExp(attrName);\n                      element.attributePatterns.push(attrPattern);\n                    } else {\n                      if (!attributes[attrName]) {\n                        attributesOrder.push(attrName);\n                      }\n                      attributes[attrName] = attr;\n                    }\n                  }\n                }\n              }\n              if (!globalAttributes && elementName === '@') {\n                globalAttributes = attributes;\n                globalAttributesOrder = attributesOrder;\n              }\n              if (outputName) {\n                element.outputName = elementName;\n                elements[outputName] = element;\n              }\n              if (hasPatternsRegExp.test(elementName)) {\n                const patternElement = element;\n                patternElement.pattern = patternToRegExp(elementName);\n                patternElements.push(patternElement);\n              } else {\n                elements[elementName] = element;\n              }\n            }\n          }\n        }\n      };\n      const setValidElements = validElements => {\n        patternElements = [];\n        each$e(keys(elements), name => {\n          delete elements[name];\n        });\n        addValidElements(validElements);\n        each$b(schemaItems, (element, name) => {\n          children[name] = element.children;\n        });\n      };\n      const addCustomElements = customElements => {\n        const customElementRegExp = /^(~)?(.+)$/;\n        if (customElements) {\n          delete mapCache.text_block_elements;\n          delete mapCache.block_elements;\n          each$b(split$1(customElements, ','), rule => {\n            const matches = customElementRegExp.exec(rule);\n            if (matches) {\n              const inline = matches[1] === '~';\n              const cloneName = inline ? 'span' : 'div';\n              const name = matches[2];\n              children[name] = children[cloneName];\n              customElementsMap[name] = cloneName;\n              nonEmptyElementsMap[name.toUpperCase()] = {};\n              nonEmptyElementsMap[name] = {};\n              if (!inline) {\n                blockElementsMap[name.toUpperCase()] = {};\n                blockElementsMap[name] = {};\n              }\n              if (!elements[name]) {\n                let customRule = elements[cloneName];\n                customRule = extend$2({}, customRule);\n                delete customRule.removeEmptyAttrs;\n                delete customRule.removeEmpty;\n                elements[name] = customRule;\n              }\n              each$b(children, (element, elmName) => {\n                if (element[cloneName]) {\n                  children[elmName] = element = extend$2({}, children[elmName]);\n                  element[name] = element[cloneName];\n                }\n              });\n            }\n          });\n        }\n      };\n      const addValidChildren = validChildren => {\n        const childRuleRegExp = /^([+\\-]?)([A-Za-z0-9_\\-.\\u00b7\\u00c0-\\u00d6\\u00d8-\\u00f6\\u00f8-\\u037d\\u037f-\\u1fff\\u200c-\\u200d\\u203f-\\u2040\\u2070-\\u218f\\u2c00-\\u2fef\\u3001-\\ud7ff\\uf900-\\ufdcf\\ufdf0-\\ufffd]+)\\[([^\\]]+)]$/;\n        delete lookupCache[schemaType];\n        if (validChildren) {\n          each$b(split$1(validChildren, ','), rule => {\n            const matches = childRuleRegExp.exec(rule);\n            if (matches) {\n              const prefix = matches[1];\n              let parent;\n              if (prefix) {\n                parent = children[matches[2]];\n              } else {\n                parent = children[matches[2]] = { '#comment': {} };\n              }\n              parent = children[matches[2]];\n              each$b(split$1(matches[3], '|'), child => {\n                if (prefix === '-') {\n                  delete parent[child];\n                } else {\n                  parent[child] = {};\n                }\n              });\n            }\n          });\n        }\n      };\n      const getElementRule = name => {\n        const element = elements[name];\n        if (element) {\n          return element;\n        }\n        let i = patternElements.length;\n        while (i--) {\n          const patternElement = patternElements[i];\n          if (patternElement.pattern.test(name)) {\n            return patternElement;\n          }\n        }\n        return undefined;\n      };\n      if (!settings.valid_elements) {\n        each$b(schemaItems, (element, name) => {\n          elements[name] = {\n            attributes: element.attributes,\n            attributesOrder: element.attributesOrder\n          };\n          children[name] = element.children;\n        });\n        each$b(split$1('strong/b em/i'), item => {\n          const items = split$1(item, '/');\n          elements[items[1]].outputName = items[0];\n        });\n        each$b(textInlineElementsMap, (_val, name) => {\n          if (elements[name]) {\n            if (settings.padd_empty_block_inline_children) {\n              elements[name].paddInEmptyBlock = true;\n            }\n            elements[name].removeEmpty = true;\n          }\n        });\n        each$b(split$1('ol ul blockquote a table tbody'), name => {\n          if (elements[name]) {\n            elements[name].removeEmpty = true;\n          }\n        });\n        each$b(split$1('p h1 h2 h3 h4 h5 h6 th td pre div address caption li'), name => {\n          elements[name].paddEmpty = true;\n        });\n        each$b(split$1('span'), name => {\n          elements[name].removeEmptyAttrs = true;\n        });\n      } else {\n        setValidElements(settings.valid_elements);\n      }\n      addCustomElements(settings.custom_elements);\n      addValidChildren(settings.valid_children);\n      addValidElements(settings.extended_valid_elements);\n      addValidChildren('+ol[ul|ol],+ul[ul|ol]');\n      each$b({\n        dd: 'dl',\n        dt: 'dl',\n        li: 'ul ol',\n        td: 'tr',\n        th: 'tr',\n        tr: 'tbody thead tfoot',\n        tbody: 'table',\n        thead: 'table',\n        tfoot: 'table',\n        legend: 'fieldset',\n        area: 'map',\n        param: 'video audio object'\n      }, (parents, item) => {\n        if (elements[item]) {\n          elements[item].parentsRequired = split$1(parents);\n        }\n      });\n      if (settings.invalid_elements) {\n        each$b(explode$2(settings.invalid_elements), item => {\n          if (elements[item]) {\n            delete elements[item];\n          }\n        });\n      }\n      if (!getElementRule('span')) {\n        addValidElements('span[!data-mce-type|*]');\n      }\n      const getValidStyles = constant(validStyles);\n      const getInvalidStyles = constant(invalidStyles);\n      const getValidClasses = constant(validClasses);\n      const getBoolAttrs = constant(boolAttrMap);\n      const getBlockElements = constant(blockElementsMap);\n      const getTextBlockElements = constant(textBlockElementsMap);\n      const getTextInlineElements = constant(textInlineElementsMap);\n      const getVoidElements = constant(Object.seal(voidElementsMap));\n      const getSelfClosingElements = constant(selfClosingElementsMap);\n      const getNonEmptyElements = constant(nonEmptyElementsMap);\n      const getMoveCaretBeforeOnEnterElements = constant(moveCaretBeforeOnEnterElementsMap);\n      const getWhitespaceElements = constant(whitespaceElementsMap);\n      const getTransparentElements = constant(transparentElementsMap);\n      const getSpecialElements = constant(Object.seal(specialElements));\n      const isValidChild = (name, child) => {\n        const parent = children[name.toLowerCase()];\n        return !!(parent && parent[child.toLowerCase()]);\n      };\n      const isValid = (name, attr) => {\n        const rule = getElementRule(name);\n        if (rule) {\n          if (attr) {\n            if (rule.attributes[attr]) {\n              return true;\n            }\n            const attrPatterns = rule.attributePatterns;\n            if (attrPatterns) {\n              let i = attrPatterns.length;\n              while (i--) {\n                if (attrPatterns[i].pattern.test(attr)) {\n                  return true;\n                }\n              }\n            }\n          } else {\n            return true;\n          }\n        }\n        return false;\n      };\n      const getCustomElements = constant(customElementsMap);\n      return {\n        type: schemaType,\n        children,\n        elements,\n        getValidStyles,\n        getValidClasses,\n        getBlockElements,\n        getInvalidStyles,\n        getVoidElements,\n        getTextBlockElements,\n        getTextInlineElements,\n        getBoolAttrs,\n        getElementRule,\n        getSelfClosingElements,\n        getNonEmptyElements,\n        getMoveCaretBeforeOnEnterElements,\n        getWhitespaceElements,\n        getTransparentElements,\n        getSpecialElements,\n        isValidChild,\n        isValid,\n        getCustomElements,\n        addValidElements,\n        setValidElements,\n        addCustomElements,\n        addValidChildren\n      };\n    };\n\n    const Styles = (settings = {}, schema) => {\n      const urlOrStrRegExp = /(?:url(?:(?:\\(\\s*\\\"([^\\\"]+)\\\"\\s*\\))|(?:\\(\\s*\\'([^\\']+)\\'\\s*\\))|(?:\\(\\s*([^)\\s]+)\\s*\\))))|(?:\\'([^\\']+)\\')|(?:\\\"([^\\\"]+)\\\")/gi;\n      const styleRegExp = /\\s*([^:]+):\\s*([^;]+);?/g;\n      const trimRightRegExp = /\\s+$/;\n      const encodingLookup = {};\n      let validStyles;\n      let invalidStyles;\n      const invisibleChar = zeroWidth;\n      if (schema) {\n        validStyles = schema.getValidStyles();\n        invalidStyles = schema.getInvalidStyles();\n      }\n      const encodingItems = (`\\\\\" \\\\' \\\\; \\\\: ; : ` + invisibleChar).split(' ');\n      for (let i = 0; i < encodingItems.length; i++) {\n        encodingLookup[encodingItems[i]] = invisibleChar + i;\n        encodingLookup[invisibleChar + i] = encodingItems[i];\n      }\n      const self = {\n        parse: css => {\n          const styles = {};\n          let isEncoded = false;\n          const urlConverter = settings.url_converter;\n          const urlConverterScope = settings.url_converter_scope || self;\n          const compress = (prefix, suffix, noJoin) => {\n            const top = styles[prefix + '-top' + suffix];\n            if (!top) {\n              return;\n            }\n            const right = styles[prefix + '-right' + suffix];\n            if (!right) {\n              return;\n            }\n            const bottom = styles[prefix + '-bottom' + suffix];\n            if (!bottom) {\n              return;\n            }\n            const left = styles[prefix + '-left' + suffix];\n            if (!left) {\n              return;\n            }\n            const box = [\n              top,\n              right,\n              bottom,\n              left\n            ];\n            let i = box.length - 1;\n            while (i--) {\n              if (box[i] !== box[i + 1]) {\n                break;\n              }\n            }\n            if (i > -1 && noJoin) {\n              return;\n            }\n            styles[prefix + suffix] = i === -1 ? box[0] : box.join(' ');\n            delete styles[prefix + '-top' + suffix];\n            delete styles[prefix + '-right' + suffix];\n            delete styles[prefix + '-bottom' + suffix];\n            delete styles[prefix + '-left' + suffix];\n          };\n          const canCompress = key => {\n            const value = styles[key];\n            if (!value) {\n              return;\n            }\n            const values = value.split(' ');\n            let i = values.length;\n            while (i--) {\n              if (values[i] !== values[0]) {\n                return false;\n              }\n            }\n            styles[key] = values[0];\n            return true;\n          };\n          const compress2 = (target, a, b, c) => {\n            if (!canCompress(a)) {\n              return;\n            }\n            if (!canCompress(b)) {\n              return;\n            }\n            if (!canCompress(c)) {\n              return;\n            }\n            styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];\n            delete styles[a];\n            delete styles[b];\n            delete styles[c];\n          };\n          const encode = str => {\n            isEncoded = true;\n            return encodingLookup[str];\n          };\n          const decode = (str, keepSlashes) => {\n            if (isEncoded) {\n              str = str.replace(/\\uFEFF[0-9]/g, str => {\n                return encodingLookup[str];\n              });\n            }\n            if (!keepSlashes) {\n              str = str.replace(/\\\\([\\'\\\";:])/g, '$1');\n            }\n            return str;\n          };\n          const decodeSingleHexSequence = escSeq => {\n            return String.fromCharCode(parseInt(escSeq.slice(1), 16));\n          };\n          const decodeHexSequences = value => {\n            return value.replace(/\\\\[0-9a-f]+/gi, decodeSingleHexSequence);\n          };\n          const processUrl = (match, url, url2, url3, str, str2) => {\n            str = str || str2;\n            if (str) {\n              str = decode(str);\n              return `'` + str.replace(/\\'/g, `\\\\'`) + `'`;\n            }\n            url = decode(url || url2 || url3 || '');\n            if (!settings.allow_script_urls) {\n              const scriptUrl = url.replace(/[\\s\\r\\n]+/g, '');\n              if (/(java|vb)script:/i.test(scriptUrl)) {\n                return '';\n              }\n              if (!settings.allow_svg_data_urls && /^data:image\\/svg/i.test(scriptUrl)) {\n                return '';\n              }\n            }\n            if (urlConverter) {\n              url = urlConverter.call(urlConverterScope, url, 'style');\n            }\n            return `url('` + url.replace(/\\'/g, `\\\\'`) + `')`;\n          };\n          if (css) {\n            css = css.replace(/[\\u0000-\\u001F]/g, '');\n            css = css.replace(/\\\\[\\\"\\';:\\uFEFF]/g, encode).replace(/\\\"[^\\\"]+\\\"|\\'[^\\']+\\'/g, str => {\n              return str.replace(/[;:]/g, encode);\n            });\n            let matches;\n            while (matches = styleRegExp.exec(css)) {\n              styleRegExp.lastIndex = matches.index + matches[0].length;\n              let name = matches[1].replace(trimRightRegExp, '').toLowerCase();\n              let value = matches[2].replace(trimRightRegExp, '');\n              if (name && value) {\n                name = decodeHexSequences(name);\n                value = decodeHexSequences(value);\n                if (name.indexOf(invisibleChar) !== -1 || name.indexOf('\"') !== -1) {\n                  continue;\n                }\n                if (!settings.allow_script_urls && (name === 'behavior' || /expression\\s*\\(|\\/\\*|\\*\\//.test(value))) {\n                  continue;\n                }\n                if (name === 'font-weight' && value === '700') {\n                  value = 'bold';\n                } else if (name === 'color' || name === 'background-color') {\n                  value = value.toLowerCase();\n                }\n                value = value.replace(urlOrStrRegExp, processUrl);\n                styles[name] = isEncoded ? decode(value, true) : value;\n              }\n            }\n            compress('border', '', true);\n            compress('border', '-width');\n            compress('border', '-color');\n            compress('border', '-style');\n            compress('padding', '');\n            compress('margin', '');\n            compress2('border', 'border-width', 'border-style', 'border-color');\n            if (styles.border === 'medium none') {\n              delete styles.border;\n            }\n            if (styles['border-image'] === 'none') {\n              delete styles['border-image'];\n            }\n          }\n          return styles;\n        },\n        serialize: (styles, elementName) => {\n          let css = '';\n          const serializeStyles = (elemName, validStyleList) => {\n            const styleList = validStyleList[elemName];\n            if (styleList) {\n              for (let i = 0, l = styleList.length; i < l; i++) {\n                const name = styleList[i];\n                const value = styles[name];\n                if (value) {\n                  css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\n                }\n              }\n            }\n          };\n          const isValid = (name, elemName) => {\n            if (!invalidStyles || !elemName) {\n              return true;\n            }\n            let styleMap = invalidStyles['*'];\n            if (styleMap && styleMap[name]) {\n              return false;\n            }\n            styleMap = invalidStyles[elemName];\n            return !(styleMap && styleMap[name]);\n          };\n          if (elementName && validStyles) {\n            serializeStyles('*', validStyles);\n            serializeStyles(elementName, validStyles);\n          } else {\n            each$d(styles, (value, name) => {\n              if (value && isValid(name, elementName)) {\n                css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\n              }\n            });\n          }\n          return css;\n        }\n      };\n      return self;\n    };\n\n    const deprecated = {\n      keyLocation: true,\n      layerX: true,\n      layerY: true,\n      returnValue: true,\n      webkitMovementX: true,\n      webkitMovementY: true,\n      keyIdentifier: true,\n      mozPressure: true\n    };\n    const isNativeEvent = event => event instanceof Event || isFunction(event.initEvent);\n    const hasIsDefaultPrevented = event => event.isDefaultPrevented === always || event.isDefaultPrevented === never;\n    const needsNormalizing = event => isNullable(event.preventDefault) || isNativeEvent(event);\n    const clone$3 = (originalEvent, data) => {\n      const event = data !== null && data !== void 0 ? data : {};\n      for (const name in originalEvent) {\n        if (!has$2(deprecated, name)) {\n          event[name] = originalEvent[name];\n        }\n      }\n      if (isNonNullable(originalEvent.composedPath)) {\n        event.composedPath = () => originalEvent.composedPath();\n      }\n      return event;\n    };\n    const normalize$3 = (type, originalEvent, fallbackTarget, data) => {\n      var _a;\n      const event = clone$3(originalEvent, data);\n      event.type = type;\n      if (isNullable(event.target)) {\n        event.target = (_a = event.srcElement) !== null && _a !== void 0 ? _a : fallbackTarget;\n      }\n      if (needsNormalizing(originalEvent)) {\n        event.preventDefault = () => {\n          event.defaultPrevented = true;\n          event.isDefaultPrevented = always;\n          if (isFunction(originalEvent.preventDefault)) {\n            originalEvent.preventDefault();\n          }\n        };\n        event.stopPropagation = () => {\n          event.cancelBubble = true;\n          event.isPropagationStopped = always;\n          if (isFunction(originalEvent.stopPropagation)) {\n            originalEvent.stopPropagation();\n          }\n        };\n        event.stopImmediatePropagation = () => {\n          event.isImmediatePropagationStopped = always;\n          event.stopPropagation();\n        };\n        if (!hasIsDefaultPrevented(event)) {\n          event.isDefaultPrevented = event.defaultPrevented === true ? always : never;\n          event.isPropagationStopped = event.cancelBubble === true ? always : never;\n          event.isImmediatePropagationStopped = never;\n        }\n      }\n      return event;\n    };\n\n    const eventExpandoPrefix = 'mce-data-';\n    const mouseEventRe = /^(?:mouse|contextmenu)|click/;\n    const addEvent = (target, name, callback, capture) => {\n      target.addEventListener(name, callback, capture || false);\n    };\n    const removeEvent = (target, name, callback, capture) => {\n      target.removeEventListener(name, callback, capture || false);\n    };\n    const isMouseEvent = event => isNonNullable(event) && mouseEventRe.test(event.type);\n    const fix = (originalEvent, data) => {\n      const event = normalize$3(originalEvent.type, originalEvent, document, data);\n      if (isMouseEvent(originalEvent) && isUndefined(originalEvent.pageX) && !isUndefined(originalEvent.clientX)) {\n        const eventDoc = event.target.ownerDocument || document;\n        const doc = eventDoc.documentElement;\n        const body = eventDoc.body;\n        const mouseEvent = event;\n        mouseEvent.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);\n        mouseEvent.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);\n      }\n      return event;\n    };\n    const bindOnReady = (win, callback, eventUtils) => {\n      const doc = win.document, event = { type: 'ready' };\n      if (eventUtils.domLoaded) {\n        callback(event);\n        return;\n      }\n      const isDocReady = () => {\n        return doc.readyState === 'complete' || doc.readyState === 'interactive' && doc.body;\n      };\n      const readyHandler = () => {\n        removeEvent(win, 'DOMContentLoaded', readyHandler);\n        removeEvent(win, 'load', readyHandler);\n        if (!eventUtils.domLoaded) {\n          eventUtils.domLoaded = true;\n          callback(event);\n        }\n        win = null;\n      };\n      if (isDocReady()) {\n        readyHandler();\n      } else {\n        addEvent(win, 'DOMContentLoaded', readyHandler);\n      }\n      if (!eventUtils.domLoaded) {\n        addEvent(win, 'load', readyHandler);\n      }\n    };\n    class EventUtils {\n      constructor() {\n        this.domLoaded = false;\n        this.events = {};\n        this.count = 1;\n        this.expando = eventExpandoPrefix + (+new Date()).toString(32);\n        this.hasFocusIn = 'onfocusin' in document.documentElement;\n        this.count = 1;\n      }\n      bind(target, names, callback, scope) {\n        const self = this;\n        let callbackList;\n        const win = window;\n        const defaultNativeHandler = evt => {\n          self.executeHandlers(fix(evt || win.event), id);\n        };\n        if (!target || isText$a(target) || isComment(target)) {\n          return callback;\n        }\n        let id;\n        if (!target[self.expando]) {\n          id = self.count++;\n          target[self.expando] = id;\n          self.events[id] = {};\n        } else {\n          id = target[self.expando];\n        }\n        scope = scope || target;\n        const namesList = names.split(' ');\n        let i = namesList.length;\n        while (i--) {\n          let name = namesList[i];\n          let nativeHandler = defaultNativeHandler;\n          let capture = false;\n          let fakeName = false;\n          if (name === 'DOMContentLoaded') {\n            name = 'ready';\n          }\n          if (self.domLoaded && name === 'ready' && target.readyState === 'complete') {\n            callback.call(scope, fix({ type: name }));\n            continue;\n          }\n          if (!self.hasFocusIn && (name === 'focusin' || name === 'focusout')) {\n            capture = true;\n            fakeName = name === 'focusin' ? 'focus' : 'blur';\n            nativeHandler = evt => {\n              const event = fix(evt || win.event);\n              event.type = event.type === 'focus' ? 'focusin' : 'focusout';\n              self.executeHandlers(event, id);\n            };\n          }\n          callbackList = self.events[id][name];\n          if (!callbackList) {\n            self.events[id][name] = callbackList = [{\n                func: callback,\n                scope\n              }];\n            callbackList.fakeName = fakeName;\n            callbackList.capture = capture;\n            callbackList.nativeHandler = nativeHandler;\n            if (name === 'ready') {\n              bindOnReady(target, nativeHandler, self);\n            } else {\n              addEvent(target, fakeName || name, nativeHandler, capture);\n            }\n          } else {\n            if (name === 'ready' && self.domLoaded) {\n              callback(fix({ type: name }));\n            } else {\n              callbackList.push({\n                func: callback,\n                scope\n              });\n            }\n          }\n        }\n        target = callbackList = null;\n        return callback;\n      }\n      unbind(target, names, callback) {\n        if (!target || isText$a(target) || isComment(target)) {\n          return this;\n        }\n        const id = target[this.expando];\n        if (id) {\n          let eventMap = this.events[id];\n          if (names) {\n            const namesList = names.split(' ');\n            let i = namesList.length;\n            while (i--) {\n              const name = namesList[i];\n              const callbackList = eventMap[name];\n              if (callbackList) {\n                if (callback) {\n                  let ci = callbackList.length;\n                  while (ci--) {\n                    if (callbackList[ci].func === callback) {\n                      const nativeHandler = callbackList.nativeHandler;\n                      const fakeName = callbackList.fakeName, capture = callbackList.capture;\n                      const newCallbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));\n                      newCallbackList.nativeHandler = nativeHandler;\n                      newCallbackList.fakeName = fakeName;\n                      newCallbackList.capture = capture;\n                      eventMap[name] = newCallbackList;\n                    }\n                  }\n                }\n                if (!callback || callbackList.length === 0) {\n                  delete eventMap[name];\n                  removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);\n                }\n              }\n            }\n          } else {\n            each$d(eventMap, (callbackList, name) => {\n              removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);\n            });\n            eventMap = {};\n          }\n          for (const name in eventMap) {\n            if (has$2(eventMap, name)) {\n              return this;\n            }\n          }\n          delete this.events[id];\n          try {\n            delete target[this.expando];\n          } catch (ex) {\n            target[this.expando] = null;\n          }\n        }\n        return this;\n      }\n      fire(target, name, args) {\n        return this.dispatch(target, name, args);\n      }\n      dispatch(target, name, args) {\n        if (!target || isText$a(target) || isComment(target)) {\n          return this;\n        }\n        const event = fix({\n          type: name,\n          target\n        }, args);\n        do {\n          const id = target[this.expando];\n          if (id) {\n            this.executeHandlers(event, id);\n          }\n          target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;\n        } while (target && !event.isPropagationStopped());\n        return this;\n      }\n      clean(target) {\n        if (!target || isText$a(target) || isComment(target)) {\n          return this;\n        }\n        if (target[this.expando]) {\n          this.unbind(target);\n        }\n        if (!target.getElementsByTagName) {\n          target = target.document;\n        }\n        if (target && target.getElementsByTagName) {\n          this.unbind(target);\n          const children = target.getElementsByTagName('*');\n          let i = children.length;\n          while (i--) {\n            target = children[i];\n            if (target[this.expando]) {\n              this.unbind(target);\n            }\n          }\n        }\n        return this;\n      }\n      destroy() {\n        this.events = {};\n      }\n      cancel(e) {\n        if (e) {\n          e.preventDefault();\n          e.stopImmediatePropagation();\n        }\n        return false;\n      }\n      executeHandlers(evt, id) {\n        const container = this.events[id];\n        const callbackList = container && container[evt.type];\n        if (callbackList) {\n          for (let i = 0, l = callbackList.length; i < l; i++) {\n            const callback = callbackList[i];\n            if (callback && callback.func.call(callback.scope, evt) === false) {\n              evt.preventDefault();\n            }\n            if (evt.isImmediatePropagationStopped()) {\n              return;\n            }\n          }\n        }\n      }\n    }\n    EventUtils.Event = new EventUtils();\n\n    const each$a = Tools.each;\n    const grep = Tools.grep;\n    const internalStyleName = 'data-mce-style';\n    const numericalCssMap = Tools.makeMap('fill-opacity font-weight line-height opacity orphans widows z-index zoom', ' ');\n    const legacySetAttribute = (elm, name, value) => {\n      if (isNullable(value) || value === '') {\n        remove$b(elm, name);\n      } else {\n        set$2(elm, name, value);\n      }\n    };\n    const camelCaseToHyphens = name => name.replace(/[A-Z]/g, v => '-' + v.toLowerCase());\n    const findNodeIndex = (node, normalized) => {\n      let idx = 0;\n      if (node) {\n        for (let lastNodeType = node.nodeType, tempNode = node.previousSibling; tempNode; tempNode = tempNode.previousSibling) {\n          const nodeType = tempNode.nodeType;\n          if (normalized && isText$a(tempNode)) {\n            if (nodeType === lastNodeType || !tempNode.data.length) {\n              continue;\n            }\n          }\n          idx++;\n          lastNodeType = nodeType;\n        }\n      }\n      return idx;\n    };\n    const updateInternalStyleAttr = (styles, elm) => {\n      const rawValue = get$9(elm, 'style');\n      const value = styles.serialize(styles.parse(rawValue), name(elm));\n      legacySetAttribute(elm, internalStyleName, value);\n    };\n    const convertStyleToString = (cssValue, cssName) => {\n      if (isNumber(cssValue)) {\n        return has$2(numericalCssMap, cssName) ? cssValue + '' : cssValue + 'px';\n      } else {\n        return cssValue;\n      }\n    };\n    const applyStyle$1 = ($elm, cssName, cssValue) => {\n      const normalizedName = camelCaseToHyphens(cssName);\n      if (isNullable(cssValue) || cssValue === '') {\n        remove$7($elm, normalizedName);\n      } else {\n        set$1($elm, normalizedName, convertStyleToString(cssValue, normalizedName));\n      }\n    };\n    const setupAttrHooks = (styles, settings, getContext) => {\n      const keepValues = settings.keep_values;\n      const keepUrlHook = {\n        set: (elm, value, name) => {\n          const sugarElm = SugarElement.fromDom(elm);\n          if (isFunction(settings.url_converter) && isNonNullable(value)) {\n            value = settings.url_converter.call(settings.url_converter_scope || getContext(), String(value), name, elm);\n          }\n          const internalName = 'data-mce-' + name;\n          legacySetAttribute(sugarElm, internalName, value);\n          legacySetAttribute(sugarElm, name, value);\n        },\n        get: (elm, name) => {\n          const sugarElm = SugarElement.fromDom(elm);\n          return get$9(sugarElm, 'data-mce-' + name) || get$9(sugarElm, name);\n        }\n      };\n      const attrHooks = {\n        style: {\n          set: (elm, value) => {\n            const sugarElm = SugarElement.fromDom(elm);\n            if (keepValues) {\n              legacySetAttribute(sugarElm, internalStyleName, value);\n            }\n            remove$b(sugarElm, 'style');\n            if (isString(value)) {\n              setAll(sugarElm, styles.parse(value));\n            }\n          },\n          get: elm => {\n            const sugarElm = SugarElement.fromDom(elm);\n            const value = get$9(sugarElm, internalStyleName) || get$9(sugarElm, 'style');\n            return styles.serialize(styles.parse(value), name(sugarElm));\n          }\n        }\n      };\n      if (keepValues) {\n        attrHooks.href = attrHooks.src = keepUrlHook;\n      }\n      return attrHooks;\n    };\n    const DOMUtils = (doc, settings = {}) => {\n      const addedStyles = {};\n      const win = window;\n      const files = {};\n      let counter = 0;\n      const stdMode = true;\n      const boxModel = true;\n      const styleSheetLoader = instance.forElement(SugarElement.fromDom(doc), {\n        contentCssCors: settings.contentCssCors,\n        referrerPolicy: settings.referrerPolicy\n      });\n      const boundEvents = [];\n      const schema = settings.schema ? settings.schema : Schema({});\n      const styles = Styles({\n        url_converter: settings.url_converter,\n        url_converter_scope: settings.url_converter_scope\n      }, settings.schema);\n      const events = settings.ownEvents ? new EventUtils() : EventUtils.Event;\n      const blockElementsMap = schema.getBlockElements();\n      const isBlock = node => {\n        if (isString(node)) {\n          return has$2(blockElementsMap, node);\n        } else {\n          return isElement$6(node) && (has$2(blockElementsMap, node.nodeName) || isTransparentBlock(schema, node));\n        }\n      };\n      const get = elm => elm && doc && isString(elm) ? doc.getElementById(elm) : elm;\n      const _get = elm => {\n        const value = get(elm);\n        return isNonNullable(value) ? SugarElement.fromDom(value) : null;\n      };\n      const getAttrib = (elm, name, defaultVal = '') => {\n        let value;\n        const $elm = _get(elm);\n        if (isNonNullable($elm) && isElement$7($elm)) {\n          const hook = attrHooks[name];\n          if (hook && hook.get) {\n            value = hook.get($elm.dom, name);\n          } else {\n            value = get$9($elm, name);\n          }\n        }\n        return isNonNullable(value) ? value : defaultVal;\n      };\n      const getAttribs = elm => {\n        const node = get(elm);\n        return isNullable(node) ? [] : node.attributes;\n      };\n      const setAttrib = (elm, name, value) => {\n        run(elm, e => {\n          if (isElement$6(e)) {\n            const $elm = SugarElement.fromDom(e);\n            const val = value === '' ? null : value;\n            const originalValue = get$9($elm, name);\n            const hook = attrHooks[name];\n            if (hook && hook.set) {\n              hook.set($elm.dom, val, name);\n            } else {\n              legacySetAttribute($elm, name, val);\n            }\n            if (originalValue !== val && settings.onSetAttrib) {\n              settings.onSetAttrib({\n                attrElm: $elm.dom,\n                attrName: name,\n                attrValue: val\n              });\n            }\n          }\n        });\n      };\n      const clone = (node, deep) => {\n        return node.cloneNode(deep);\n      };\n      const getRoot = () => settings.root_element || doc.body;\n      const getViewPort = argWin => {\n        const vp = getBounds(argWin);\n        return {\n          x: vp.x,\n          y: vp.y,\n          w: vp.width,\n          h: vp.height\n        };\n      };\n      const getPos$1 = (elm, rootElm) => getPos(doc.body, get(elm), rootElm);\n      const setStyle = (elm, name, value) => {\n        run(elm, e => {\n          const $elm = SugarElement.fromDom(e);\n          applyStyle$1($elm, name, value);\n          if (settings.update_styles) {\n            updateInternalStyleAttr(styles, $elm);\n          }\n        });\n      };\n      const setStyles = (elm, stylesArg) => {\n        run(elm, e => {\n          const $elm = SugarElement.fromDom(e);\n          each$d(stylesArg, (v, n) => {\n            applyStyle$1($elm, n, v);\n          });\n          if (settings.update_styles) {\n            updateInternalStyleAttr(styles, $elm);\n          }\n        });\n      };\n      const getStyle = (elm, name, computed) => {\n        const $elm = get(elm);\n        if (isNullable($elm) || !isElement$6($elm)) {\n          return undefined;\n        }\n        if (computed) {\n          return get$7(SugarElement.fromDom($elm), camelCaseToHyphens(name));\n        } else {\n          name = name.replace(/-(\\D)/g, (a, b) => b.toUpperCase());\n          if (name === 'float') {\n            name = 'cssFloat';\n          }\n          return $elm.style ? $elm.style[name] : undefined;\n        }\n      };\n      const getSize = elm => {\n        const $elm = get(elm);\n        if (!$elm) {\n          return {\n            w: 0,\n            h: 0\n          };\n        }\n        let w = getStyle($elm, 'width');\n        let h = getStyle($elm, 'height');\n        if (!w || w.indexOf('px') === -1) {\n          w = '0';\n        }\n        if (!h || h.indexOf('px') === -1) {\n          h = '0';\n        }\n        return {\n          w: parseInt(w, 10) || $elm.offsetWidth || $elm.clientWidth,\n          h: parseInt(h, 10) || $elm.offsetHeight || $elm.clientHeight\n        };\n      };\n      const getRect = elm => {\n        const $elm = get(elm);\n        const pos = getPos$1($elm);\n        const size = getSize($elm);\n        return {\n          x: pos.x,\n          y: pos.y,\n          w: size.w,\n          h: size.h\n        };\n      };\n      const is = (elm, selector) => {\n        if (!elm) {\n          return false;\n        }\n        const elms = isArray$1(elm) ? elm : [elm];\n        return exists(elms, e => {\n          return is$1(SugarElement.fromDom(e), selector);\n        });\n      };\n      const getParents = (elm, selector, root, collect) => {\n        const result = [];\n        let node = get(elm);\n        collect = collect === undefined;\n        const resolvedRoot = root || (getRoot().nodeName !== 'BODY' ? getRoot().parentNode : null);\n        if (isString(selector)) {\n          if (selector === '*') {\n            selector = isElement$6;\n          } else {\n            const selectorVal = selector;\n            selector = node => is(node, selectorVal);\n          }\n        }\n        while (node) {\n          if (node === resolvedRoot || isNullable(node.nodeType) || isDocument$1(node) || isDocumentFragment(node)) {\n            break;\n          }\n          if (!selector || selector(node)) {\n            if (collect) {\n              result.push(node);\n            } else {\n              return [node];\n            }\n          }\n          node = node.parentNode;\n        }\n        return collect ? result : null;\n      };\n      const getParent = (node, selector, root) => {\n        const parents = getParents(node, selector, root, false);\n        return parents && parents.length > 0 ? parents[0] : null;\n      };\n      const _findSib = (node, selector, name) => {\n        let func = selector;\n        if (node) {\n          if (isString(selector)) {\n            func = node => {\n              return is(node, selector);\n            };\n          }\n          for (let tempNode = node[name]; tempNode; tempNode = tempNode[name]) {\n            if (isFunction(func) && func(tempNode)) {\n              return tempNode;\n            }\n          }\n        }\n        return null;\n      };\n      const getNext = (node, selector) => _findSib(node, selector, 'nextSibling');\n      const getPrev = (node, selector) => _findSib(node, selector, 'previousSibling');\n      const isParentNode = node => isFunction(node.querySelectorAll);\n      const select = (selector, scope) => {\n        var _a, _b;\n        const elm = (_b = (_a = get(scope)) !== null && _a !== void 0 ? _a : settings.root_element) !== null && _b !== void 0 ? _b : doc;\n        return isParentNode(elm) ? from(elm.querySelectorAll(selector)) : [];\n      };\n      const run = function (elm, func, scope) {\n        const context = scope !== null && scope !== void 0 ? scope : this;\n        if (isArray$1(elm)) {\n          const result = [];\n          each$a(elm, (e, i) => {\n            const node = get(e);\n            if (node) {\n              result.push(func.call(context, node, i));\n            }\n          });\n          return result;\n        } else {\n          const node = get(elm);\n          return !node ? false : func.call(context, node);\n        }\n      };\n      const setAttribs = (elm, attrs) => {\n        run(elm, $elm => {\n          each$d(attrs, (value, name) => {\n            setAttrib($elm, name, value);\n          });\n        });\n      };\n      const setHTML = (elm, html) => {\n        run(elm, e => {\n          const $elm = SugarElement.fromDom(e);\n          set($elm, html);\n        });\n      };\n      const add = (parentElm, name, attrs, html, create) => run(parentElm, parentElm => {\n        const newElm = isString(name) ? doc.createElement(name) : name;\n        if (isNonNullable(attrs)) {\n          setAttribs(newElm, attrs);\n        }\n        if (html) {\n          if (!isString(html) && html.nodeType) {\n            newElm.appendChild(html);\n          } else if (isString(html)) {\n            setHTML(newElm, html);\n          }\n        }\n        return !create ? parentElm.appendChild(newElm) : newElm;\n      });\n      const create = (name, attrs, html) => add(doc.createElement(name), name, attrs, html, true);\n      const decode = Entities.decode;\n      const encode = Entities.encodeAllRaw;\n      const createHTML = (name, attrs, html = '') => {\n        let outHtml = '<' + name;\n        for (const key in attrs) {\n          if (hasNonNullableKey(attrs, key)) {\n            outHtml += ' ' + key + '=\"' + encode(attrs[key]) + '\"';\n          }\n        }\n        if (isEmpty$3(html) && has$2(schema.getVoidElements(), name)) {\n          return outHtml + ' />';\n        } else {\n          return outHtml + '>' + html + '</' + name + '>';\n        }\n      };\n      const createFragment = html => {\n        const container = doc.createElement('div');\n        const frag = doc.createDocumentFragment();\n        frag.appendChild(container);\n        if (html) {\n          container.innerHTML = html;\n        }\n        let node;\n        while (node = container.firstChild) {\n          frag.appendChild(node);\n        }\n        frag.removeChild(container);\n        return frag;\n      };\n      const remove = (node, keepChildren) => {\n        return run(node, n => {\n          const $node = SugarElement.fromDom(n);\n          if (keepChildren) {\n            each$e(children$1($node), child => {\n              if (isText$b(child) && child.dom.length === 0) {\n                remove$6(child);\n              } else {\n                before$3($node, child);\n              }\n            });\n          }\n          remove$6($node);\n          return $node.dom;\n        });\n      };\n      const removeAllAttribs = e => run(e, e => {\n        const attrs = e.attributes;\n        for (let i = attrs.length - 1; i >= 0; i--) {\n          e.removeAttributeNode(attrs.item(i));\n        }\n      });\n      const parseStyle = cssText => styles.parse(cssText);\n      const serializeStyle = (stylesArg, name) => styles.serialize(stylesArg, name);\n      const addStyle = cssText => {\n        if (self !== DOMUtils.DOM && doc === document) {\n          if (addedStyles[cssText]) {\n            return;\n          }\n          addedStyles[cssText] = true;\n        }\n        let styleElm = doc.getElementById('mceDefaultStyles');\n        if (!styleElm) {\n          styleElm = doc.createElement('style');\n          styleElm.id = 'mceDefaultStyles';\n          styleElm.type = 'text/css';\n          const head = doc.head;\n          if (head.firstChild) {\n            head.insertBefore(styleElm, head.firstChild);\n          } else {\n            head.appendChild(styleElm);\n          }\n        }\n        if (styleElm.styleSheet) {\n          styleElm.styleSheet.cssText += cssText;\n        } else {\n          styleElm.appendChild(doc.createTextNode(cssText));\n        }\n      };\n      const loadCSS = urls => {\n        if (!urls) {\n          urls = '';\n        }\n        each$e(urls.split(','), url => {\n          files[url] = true;\n          styleSheetLoader.load(url).catch(noop);\n        });\n      };\n      const toggleClass = (elm, cls, state) => {\n        run(elm, e => {\n          if (isElement$6(e)) {\n            const $elm = SugarElement.fromDom(e);\n            const classes = cls.split(' ');\n            each$e(classes, c => {\n              if (isNonNullable(state)) {\n                const fn = state ? add$2 : remove$8;\n                fn($elm, c);\n              } else {\n                toggle$1($elm, c);\n              }\n            });\n          }\n        });\n      };\n      const addClass = (elm, cls) => {\n        toggleClass(elm, cls, true);\n      };\n      const removeClass = (elm, cls) => {\n        toggleClass(elm, cls, false);\n      };\n      const hasClass = (elm, cls) => {\n        const $elm = _get(elm);\n        const classes = cls.split(' ');\n        return isNonNullable($elm) && forall(classes, c => has($elm, c));\n      };\n      const show = elm => {\n        run(elm, e => remove$7(SugarElement.fromDom(e), 'display'));\n      };\n      const hide = elm => {\n        run(elm, e => set$1(SugarElement.fromDom(e), 'display', 'none'));\n      };\n      const isHidden = elm => {\n        const $elm = _get(elm);\n        return isNonNullable($elm) && is$2(getRaw$1($elm, 'display'), 'none');\n      };\n      const uniqueId = prefix => (!prefix ? 'mce_' : prefix) + counter++;\n      const getOuterHTML = elm => {\n        const $elm = _get(elm);\n        if (isNonNullable($elm)) {\n          return isElement$6($elm.dom) ? $elm.dom.outerHTML : getOuter($elm);\n        } else {\n          return '';\n        }\n      };\n      const setOuterHTML = (elm, html) => {\n        run(elm, $elm => {\n          if (isElement$6($elm)) {\n            $elm.outerHTML = html;\n          }\n        });\n      };\n      const insertAfter = (node, reference) => {\n        const referenceNode = get(reference);\n        return run(node, node => {\n          const parent = referenceNode === null || referenceNode === void 0 ? void 0 : referenceNode.parentNode;\n          const nextSibling = referenceNode === null || referenceNode === void 0 ? void 0 : referenceNode.nextSibling;\n          if (parent) {\n            if (nextSibling) {\n              parent.insertBefore(node, nextSibling);\n            } else {\n              parent.appendChild(node);\n            }\n          }\n          return node;\n        });\n      };\n      const replace = (newElm, oldElm, keepChildren) => run(oldElm, elm => {\n        var _a;\n        const replacee = isArray$1(oldElm) ? newElm.cloneNode(true) : newElm;\n        if (keepChildren) {\n          each$a(grep(elm.childNodes), node => {\n            replacee.appendChild(node);\n          });\n        }\n        (_a = elm.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(replacee, elm);\n        return elm;\n      });\n      const rename = (elm, name) => {\n        if (elm.nodeName !== name.toUpperCase()) {\n          const newElm = create(name);\n          each$a(getAttribs(elm), attrNode => {\n            setAttrib(newElm, attrNode.nodeName, getAttrib(elm, attrNode.nodeName));\n          });\n          replace(newElm, elm, true);\n          return newElm;\n        } else {\n          return elm;\n        }\n      };\n      const findCommonAncestor = (a, b) => {\n        let ps = a;\n        while (ps) {\n          let pe = b;\n          while (pe && ps !== pe) {\n            pe = pe.parentNode;\n          }\n          if (ps === pe) {\n            break;\n          }\n          ps = ps.parentNode;\n        }\n        if (!ps && a.ownerDocument) {\n          return a.ownerDocument.documentElement;\n        } else {\n          return ps;\n        }\n      };\n      const isNonEmptyElement = node => {\n        if (isElement$6(node)) {\n          const isNamedAnchor = node.nodeName.toLowerCase() === 'a' && !getAttrib(node, 'href') && getAttrib(node, 'id');\n          if (getAttrib(node, 'name') || getAttrib(node, 'data-mce-bookmark') || isNamedAnchor) {\n            return true;\n          }\n        }\n        return false;\n      };\n      const isEmpty = (node, elements) => {\n        let brCount = 0;\n        if (isNonEmptyElement(node)) {\n          return false;\n        }\n        const firstChild = node.firstChild;\n        if (firstChild) {\n          const walker = new DomTreeWalker(firstChild, node);\n          const whitespaceElements = schema ? schema.getWhitespaceElements() : {};\n          const nonEmptyElements = elements || (schema ? schema.getNonEmptyElements() : null);\n          let tempNode = firstChild;\n          do {\n            if (isElement$6(tempNode)) {\n              const bogusVal = tempNode.getAttribute('data-mce-bogus');\n              if (bogusVal) {\n                tempNode = walker.next(bogusVal === 'all');\n                continue;\n              }\n              const name = tempNode.nodeName.toLowerCase();\n              if (nonEmptyElements && nonEmptyElements[name]) {\n                if (name === 'br') {\n                  brCount++;\n                  tempNode = walker.next();\n                  continue;\n                }\n                return false;\n              }\n              if (isNonEmptyElement(tempNode)) {\n                return false;\n              }\n            }\n            if (isComment(tempNode)) {\n              return false;\n            }\n            if (isText$a(tempNode) && !isWhitespaceText(tempNode.data)) {\n              return false;\n            }\n            if (isText$a(tempNode) && tempNode.parentNode && whitespaceElements[tempNode.parentNode.nodeName] && isWhitespaceText(tempNode.data)) {\n              return false;\n            }\n            tempNode = walker.next();\n          } while (tempNode);\n        }\n        return brCount <= 1;\n      };\n      const createRng = () => doc.createRange();\n      const split = (parentElm, splitElm, replacementElm) => {\n        let range = createRng();\n        let beforeFragment;\n        let afterFragment;\n        if (parentElm && splitElm && parentElm.parentNode && splitElm.parentNode) {\n          const parentNode = parentElm.parentNode;\n          range.setStart(parentNode, findNodeIndex(parentElm));\n          range.setEnd(splitElm.parentNode, findNodeIndex(splitElm));\n          beforeFragment = range.extractContents();\n          range = createRng();\n          range.setStart(splitElm.parentNode, findNodeIndex(splitElm) + 1);\n          range.setEnd(parentNode, findNodeIndex(parentElm) + 1);\n          afterFragment = range.extractContents();\n          parentNode.insertBefore(trimNode(self, beforeFragment), parentElm);\n          if (replacementElm) {\n            parentNode.insertBefore(replacementElm, parentElm);\n          } else {\n            parentNode.insertBefore(splitElm, parentElm);\n          }\n          parentNode.insertBefore(trimNode(self, afterFragment), parentElm);\n          remove(parentElm);\n          return replacementElm || splitElm;\n        } else {\n          return undefined;\n        }\n      };\n      const bind = (target, name, func, scope) => {\n        if (isArray$1(target)) {\n          let i = target.length;\n          const rv = [];\n          while (i--) {\n            rv[i] = bind(target[i], name, func, scope);\n          }\n          return rv;\n        } else {\n          if (settings.collect && (target === doc || target === win)) {\n            boundEvents.push([\n              target,\n              name,\n              func,\n              scope\n            ]);\n          }\n          return events.bind(target, name, func, scope || self);\n        }\n      };\n      const unbind = (target, name, func) => {\n        if (isArray$1(target)) {\n          let i = target.length;\n          const rv = [];\n          while (i--) {\n            rv[i] = unbind(target[i], name, func);\n          }\n          return rv;\n        } else {\n          if (boundEvents.length > 0 && (target === doc || target === win)) {\n            let i = boundEvents.length;\n            while (i--) {\n              const [boundTarget, boundName, boundFunc] = boundEvents[i];\n              if (target === boundTarget && (!name || name === boundName) && (!func || func === boundFunc)) {\n                events.unbind(boundTarget, boundName, boundFunc);\n              }\n            }\n          }\n          return events.unbind(target, name, func);\n        }\n      };\n      const dispatch = (target, name, evt) => events.dispatch(target, name, evt);\n      const fire = (target, name, evt) => events.dispatch(target, name, evt);\n      const getContentEditable = node => {\n        if (node && isElement$6(node)) {\n          const contentEditable = node.getAttribute('data-mce-contenteditable');\n          if (contentEditable && contentEditable !== 'inherit') {\n            return contentEditable;\n          }\n          return node.contentEditable !== 'inherit' ? node.contentEditable : null;\n        } else {\n          return null;\n        }\n      };\n      const getContentEditableParent = node => {\n        const root = getRoot();\n        let state = null;\n        for (let tempNode = node; tempNode && tempNode !== root; tempNode = tempNode.parentNode) {\n          state = getContentEditable(tempNode);\n          if (state !== null) {\n            break;\n          }\n        }\n        return state;\n      };\n      const destroy = () => {\n        if (boundEvents.length > 0) {\n          let i = boundEvents.length;\n          while (i--) {\n            const [boundTarget, boundName, boundFunc] = boundEvents[i];\n            events.unbind(boundTarget, boundName, boundFunc);\n          }\n        }\n        each$d(files, (_, url) => {\n          styleSheetLoader.unload(url);\n          delete files[url];\n        });\n      };\n      const isChildOf = (node, parent) => {\n        return node === parent || parent.contains(node);\n      };\n      const dumpRng = r => 'startContainer: ' + r.startContainer.nodeName + ', startOffset: ' + r.startOffset + ', endContainer: ' + r.endContainer.nodeName + ', endOffset: ' + r.endOffset;\n      const self = {\n        doc,\n        settings,\n        win,\n        files,\n        stdMode,\n        boxModel,\n        styleSheetLoader,\n        boundEvents,\n        styles,\n        schema,\n        events,\n        isBlock: isBlock,\n        root: null,\n        clone,\n        getRoot,\n        getViewPort,\n        getRect,\n        getSize,\n        getParent,\n        getParents: getParents,\n        get,\n        getNext,\n        getPrev,\n        select,\n        is,\n        add,\n        create,\n        createHTML,\n        createFragment,\n        remove,\n        setStyle,\n        getStyle: getStyle,\n        setStyles,\n        removeAllAttribs,\n        setAttrib,\n        setAttribs,\n        getAttrib,\n        getPos: getPos$1,\n        parseStyle,\n        serializeStyle,\n        addStyle,\n        loadCSS,\n        addClass,\n        removeClass,\n        hasClass,\n        toggleClass,\n        show,\n        hide,\n        isHidden,\n        uniqueId,\n        setHTML,\n        getOuterHTML,\n        setOuterHTML,\n        decode,\n        encode,\n        insertAfter,\n        replace,\n        rename,\n        findCommonAncestor,\n        run,\n        getAttribs,\n        isEmpty,\n        createRng,\n        nodeIndex: findNodeIndex,\n        split,\n        bind: bind,\n        unbind: unbind,\n        fire,\n        dispatch,\n        getContentEditable,\n        getContentEditableParent,\n        destroy,\n        isChildOf,\n        dumpRng\n      };\n      const attrHooks = setupAttrHooks(styles, settings, constant(self));\n      return self;\n    };\n    DOMUtils.DOM = DOMUtils(document);\n    DOMUtils.nodeIndex = findNodeIndex;\n\n    const DOM$b = DOMUtils.DOM;\n    const QUEUED = 0;\n    const LOADING = 1;\n    const LOADED = 2;\n    const FAILED = 3;\n    class ScriptLoader {\n      constructor(settings = {}) {\n        this.states = {};\n        this.queue = [];\n        this.scriptLoadedCallbacks = {};\n        this.queueLoadedCallbacks = [];\n        this.loading = false;\n        this.settings = settings;\n      }\n      _setReferrerPolicy(referrerPolicy) {\n        this.settings.referrerPolicy = referrerPolicy;\n      }\n      loadScript(url) {\n        return new Promise((resolve, reject) => {\n          const dom = DOM$b;\n          let elm;\n          const cleanup = () => {\n            dom.remove(id);\n            if (elm) {\n              elm.onerror = elm.onload = elm = null;\n            }\n          };\n          const done = () => {\n            cleanup();\n            resolve();\n          };\n          const error = () => {\n            cleanup();\n            reject('Failed to load script: ' + url);\n          };\n          const id = dom.uniqueId();\n          elm = document.createElement('script');\n          elm.id = id;\n          elm.type = 'text/javascript';\n          elm.src = Tools._addCacheSuffix(url);\n          if (this.settings.referrerPolicy) {\n            dom.setAttrib(elm, 'referrerpolicy', this.settings.referrerPolicy);\n          }\n          elm.onload = done;\n          elm.onerror = error;\n          (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\n        });\n      }\n      isDone(url) {\n        return this.states[url] === LOADED;\n      }\n      markDone(url) {\n        this.states[url] = LOADED;\n      }\n      add(url) {\n        const self = this;\n        self.queue.push(url);\n        const state = self.states[url];\n        if (state === undefined) {\n          self.states[url] = QUEUED;\n        }\n        return new Promise((resolve, reject) => {\n          if (!self.scriptLoadedCallbacks[url]) {\n            self.scriptLoadedCallbacks[url] = [];\n          }\n          self.scriptLoadedCallbacks[url].push({\n            resolve,\n            reject\n          });\n        });\n      }\n      load(url) {\n        return this.add(url);\n      }\n      remove(url) {\n        delete this.states[url];\n        delete this.scriptLoadedCallbacks[url];\n      }\n      loadQueue() {\n        const queue = this.queue;\n        this.queue = [];\n        return this.loadScripts(queue);\n      }\n      loadScripts(scripts) {\n        const self = this;\n        const execCallbacks = (name, url) => {\n          get$a(self.scriptLoadedCallbacks, url).each(callbacks => {\n            each$e(callbacks, callback => callback[name](url));\n          });\n          delete self.scriptLoadedCallbacks[url];\n        };\n        const processResults = results => {\n          const failures = filter$5(results, result => result.status === 'rejected');\n          if (failures.length > 0) {\n            return Promise.reject(bind$3(failures, ({reason}) => isArray$1(reason) ? reason : [reason]));\n          } else {\n            return Promise.resolve();\n          }\n        };\n        const load = urls => Promise.allSettled(map$3(urls, url => {\n          if (self.states[url] === LOADED) {\n            execCallbacks('resolve', url);\n            return Promise.resolve();\n          } else if (self.states[url] === FAILED) {\n            execCallbacks('reject', url);\n            return Promise.reject(url);\n          } else {\n            self.states[url] = LOADING;\n            return self.loadScript(url).then(() => {\n              self.states[url] = LOADED;\n              execCallbacks('resolve', url);\n              const queue = self.queue;\n              if (queue.length > 0) {\n                self.queue = [];\n                return load(queue).then(processResults);\n              } else {\n                return Promise.resolve();\n              }\n            }, () => {\n              self.states[url] = FAILED;\n              execCallbacks('reject', url);\n              return Promise.reject(url);\n            });\n          }\n        }));\n        const processQueue = urls => {\n          self.loading = true;\n          return load(urls).then(results => {\n            self.loading = false;\n            const nextQueuedItem = self.queueLoadedCallbacks.shift();\n            Optional.from(nextQueuedItem).each(call);\n            return processResults(results);\n          });\n        };\n        const uniqueScripts = stringArray(scripts);\n        if (self.loading) {\n          return new Promise((resolve, reject) => {\n            self.queueLoadedCallbacks.push(() => processQueue(uniqueScripts).then(resolve, reject));\n          });\n        } else {\n          return processQueue(uniqueScripts);\n        }\n      }\n    }\n    ScriptLoader.ScriptLoader = new ScriptLoader();\n\n    const Cell = initial => {\n      let value = initial;\n      const get = () => {\n        return value;\n      };\n      const set = v => {\n        value = v;\n      };\n      return {\n        get,\n        set\n      };\n    };\n\n    const isRaw = str => isObject(str) && has$2(str, 'raw');\n    const isTokenised = str => isArray$1(str) && str.length > 1;\n    const data = {};\n    const currentCode = Cell('en');\n    const getLanguageData = () => get$a(data, currentCode.get());\n    const getData$1 = () => map$2(data, value => ({ ...value }));\n    const setCode = newCode => {\n      if (newCode) {\n        currentCode.set(newCode);\n      }\n    };\n    const getCode = () => currentCode.get();\n    const add$1 = (code, items) => {\n      let langData = data[code];\n      if (!langData) {\n        data[code] = langData = {};\n      }\n      each$d(items, (translation, name) => {\n        langData[name.toLowerCase()] = translation;\n      });\n    };\n    const translate = text => {\n      const langData = getLanguageData().getOr({});\n      const toString = obj => {\n        if (isFunction(obj)) {\n          return Object.prototype.toString.call(obj);\n        }\n        return !isEmpty(obj) ? '' + obj : '';\n      };\n      const isEmpty = text => text === '' || text === null || text === undefined;\n      const getLangData = text => {\n        const textstr = toString(text);\n        return get$a(langData, textstr.toLowerCase()).map(toString).getOr(textstr);\n      };\n      const removeContext = str => str.replace(/{context:\\w+}$/, '');\n      if (isEmpty(text)) {\n        return '';\n      }\n      if (isRaw(text)) {\n        return toString(text.raw);\n      }\n      if (isTokenised(text)) {\n        const values = text.slice(1);\n        const substitued = getLangData(text[0]).replace(/\\{([0-9]+)\\}/g, ($1, $2) => has$2(values, $2) ? toString(values[$2]) : $1);\n        return removeContext(substitued);\n      }\n      return removeContext(getLangData(text));\n    };\n    const isRtl$1 = () => getLanguageData().bind(items => get$a(items, '_dir')).exists(dir => dir === 'rtl');\n    const hasCode = code => has$2(data, code);\n    const I18n = {\n      getData: getData$1,\n      setCode,\n      getCode,\n      add: add$1,\n      translate,\n      isRtl: isRtl$1,\n      hasCode\n    };\n\n    const AddOnManager = () => {\n      const items = [];\n      const urls = {};\n      const lookup = {};\n      const _listeners = [];\n      const runListeners = (name, state) => {\n        const matchedListeners = filter$5(_listeners, listener => listener.name === name && listener.state === state);\n        each$e(matchedListeners, listener => listener.resolve());\n      };\n      const isLoaded = name => has$2(urls, name);\n      const isAdded = name => has$2(lookup, name);\n      const get = name => {\n        if (lookup[name]) {\n          return lookup[name].instance;\n        }\n        return undefined;\n      };\n      const loadLanguagePack = (name, languages) => {\n        const language = I18n.getCode();\n        const wrappedLanguages = ',' + (languages || '') + ',';\n        if (!language || languages && wrappedLanguages.indexOf(',' + language + ',') === -1) {\n          return;\n        }\n        ScriptLoader.ScriptLoader.add(urls[name] + '/langs/' + language + '.js');\n      };\n      const requireLangPack = (name, languages) => {\n        if (AddOnManager.languageLoad !== false) {\n          if (isLoaded(name)) {\n            loadLanguagePack(name, languages);\n          } else {\n            waitFor(name, 'loaded').then(() => loadLanguagePack(name, languages));\n          }\n        }\n      };\n      const add = (id, addOn) => {\n        items.push(addOn);\n        lookup[id] = { instance: addOn };\n        runListeners(id, 'added');\n        return addOn;\n      };\n      const remove = name => {\n        delete urls[name];\n        delete lookup[name];\n      };\n      const createUrl = (baseUrl, dep) => {\n        if (isString(dep)) {\n          return isString(baseUrl) ? {\n            prefix: '',\n            resource: dep,\n            suffix: ''\n          } : {\n            prefix: baseUrl.prefix,\n            resource: dep,\n            suffix: baseUrl.suffix\n          };\n        } else {\n          return dep;\n        }\n      };\n      const load = (name, addOnUrl) => {\n        if (urls[name]) {\n          return Promise.resolve();\n        }\n        let urlString = isString(addOnUrl) ? addOnUrl : addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;\n        if (urlString.indexOf('/') !== 0 && urlString.indexOf('://') === -1) {\n          urlString = AddOnManager.baseURL + '/' + urlString;\n        }\n        urls[name] = urlString.substring(0, urlString.lastIndexOf('/'));\n        const done = () => {\n          runListeners(name, 'loaded');\n          return Promise.resolve();\n        };\n        if (lookup[name]) {\n          return done();\n        } else {\n          return ScriptLoader.ScriptLoader.add(urlString).then(done);\n        }\n      };\n      const waitFor = (name, state = 'added') => {\n        if (state === 'added' && isAdded(name)) {\n          return Promise.resolve();\n        } else if (state === 'loaded' && isLoaded(name)) {\n          return Promise.resolve();\n        } else {\n          return new Promise(resolve => {\n            _listeners.push({\n              name,\n              state,\n              resolve\n            });\n          });\n        }\n      };\n      return {\n        items,\n        urls,\n        lookup,\n        get,\n        requireLangPack,\n        add,\n        remove,\n        createUrl,\n        load,\n        waitFor\n      };\n    };\n    AddOnManager.languageLoad = true;\n    AddOnManager.baseURL = '';\n    AddOnManager.PluginManager = AddOnManager();\n    AddOnManager.ThemeManager = AddOnManager();\n    AddOnManager.ModelManager = AddOnManager();\n\n    const singleton = doRevoke => {\n      const subject = Cell(Optional.none());\n      const revoke = () => subject.get().each(doRevoke);\n      const clear = () => {\n        revoke();\n        subject.set(Optional.none());\n      };\n      const isSet = () => subject.get().isSome();\n      const get = () => subject.get();\n      const set = s => {\n        revoke();\n        subject.set(Optional.some(s));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const repeatable = delay => {\n      const intervalId = Cell(Optional.none());\n      const revoke = () => intervalId.get().each(id => clearInterval(id));\n      const clear = () => {\n        revoke();\n        intervalId.set(Optional.none());\n      };\n      const isSet = () => intervalId.get().isSome();\n      const get = () => intervalId.get();\n      const set = functionToRepeat => {\n        revoke();\n        intervalId.set(Optional.some(setInterval(functionToRepeat, delay)));\n      };\n      return {\n        clear,\n        isSet,\n        get,\n        set\n      };\n    };\n    const value$2 = () => {\n      const subject = singleton(noop);\n      const on = f => subject.get().each(f);\n      return {\n        ...subject,\n        on\n      };\n    };\n\n    const first$1 = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        if (isNull(timer)) {\n          timer = setTimeout(() => {\n            timer = null;\n            fn.apply(null, args);\n          }, rate);\n        }\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n    const last$1 = (fn, rate) => {\n      let timer = null;\n      const cancel = () => {\n        if (!isNull(timer)) {\n          clearTimeout(timer);\n          timer = null;\n        }\n      };\n      const throttle = (...args) => {\n        cancel();\n        timer = setTimeout(() => {\n          timer = null;\n          fn.apply(null, args);\n        }, rate);\n      };\n      return {\n        cancel,\n        throttle\n      };\n    };\n\n    const annotation = constant('mce-annotation');\n    const dataAnnotation = constant('data-mce-annotation');\n    const dataAnnotationId = constant('data-mce-annotation-uid');\n    const dataAnnotationActive = constant('data-mce-annotation-active');\n    const dataAnnotationClasses = constant('data-mce-annotation-classes');\n    const dataAnnotationAttributes = constant('data-mce-annotation-attrs');\n\n    const isRoot$1 = root => node => eq(node, root);\n    const identify = (editor, annotationName) => {\n      const rng = editor.selection.getRng();\n      const start = SugarElement.fromDom(rng.startContainer);\n      const root = SugarElement.fromDom(editor.getBody());\n      const selector = annotationName.fold(() => '.' + annotation(), an => `[${ dataAnnotation() }=\"${ an }\"]`);\n      const newStart = child$1(start, rng.startOffset).getOr(start);\n      const closest = closest$3(newStart, selector, isRoot$1(root));\n      return closest.bind(c => getOpt(c, `${ dataAnnotationId() }`).bind(uid => getOpt(c, `${ dataAnnotation() }`).map(name => {\n        const elements = findMarkers(editor, uid);\n        return {\n          uid,\n          name,\n          elements\n        };\n      })));\n    };\n    const isAnnotation = elem => isElement$7(elem) && has(elem, annotation());\n    const isBogusElement = (elem, root) => has$1(elem, 'data-mce-bogus') || ancestor$1(elem, '[data-mce-bogus=\"all\"]', isRoot$1(root));\n    const findMarkers = (editor, uid) => {\n      const body = SugarElement.fromDom(editor.getBody());\n      const descendants$1 = descendants(body, `[${ dataAnnotationId() }=\"${ uid }\"]`);\n      return filter$5(descendants$1, descendant => !isBogusElement(descendant, body));\n    };\n    const findAll = (editor, name) => {\n      const body = SugarElement.fromDom(editor.getBody());\n      const markers = descendants(body, `[${ dataAnnotation() }=\"${ name }\"]`);\n      const directory = {};\n      each$e(markers, m => {\n        if (!isBogusElement(m, body)) {\n          const uid = get$9(m, dataAnnotationId());\n          const nodesAlready = get$a(directory, uid).getOr([]);\n          directory[uid] = nodesAlready.concat([m]);\n        }\n      });\n      return directory;\n    };\n\n    const setup$x = (editor, registry) => {\n      const changeCallbacks = Cell({});\n      const initData = () => ({\n        listeners: [],\n        previous: value$2()\n      });\n      const withCallbacks = (name, f) => {\n        updateCallbacks(name, data => {\n          f(data);\n          return data;\n        });\n      };\n      const updateCallbacks = (name, f) => {\n        const callbackMap = changeCallbacks.get();\n        const data = get$a(callbackMap, name).getOrThunk(initData);\n        const outputData = f(data);\n        callbackMap[name] = outputData;\n        changeCallbacks.set(callbackMap);\n      };\n      const fireCallbacks = (name, uid, elements) => {\n        withCallbacks(name, data => {\n          each$e(data.listeners, f => f(true, name, {\n            uid,\n            nodes: map$3(elements, elem => elem.dom)\n          }));\n        });\n      };\n      const fireNoAnnotation = name => {\n        withCallbacks(name, data => {\n          each$e(data.listeners, f => f(false, name));\n        });\n      };\n      const toggleActiveAttr = (uid, state) => {\n        each$e(findMarkers(editor, uid), elem => {\n          if (state) {\n            set$2(elem, dataAnnotationActive(), 'true');\n          } else {\n            remove$b(elem, dataAnnotationActive());\n          }\n        });\n      };\n      const onNodeChange = last$1(() => {\n        const annotations = sort(registry.getNames());\n        each$e(annotations, name => {\n          updateCallbacks(name, data => {\n            const prev = data.previous.get();\n            identify(editor, Optional.some(name)).fold(() => {\n              prev.each(uid => {\n                fireNoAnnotation(name);\n                data.previous.clear();\n                toggleActiveAttr(uid, false);\n              });\n            }, ({uid, name, elements}) => {\n              if (!is$2(prev, uid)) {\n                prev.each(uid => toggleActiveAttr(uid, false));\n                fireCallbacks(name, uid, elements);\n                data.previous.set(uid);\n                toggleActiveAttr(uid, true);\n              }\n            });\n            return {\n              previous: data.previous,\n              listeners: data.listeners\n            };\n          });\n        });\n      }, 30);\n      editor.on('remove', () => {\n        onNodeChange.cancel();\n      });\n      editor.on('NodeChange', () => {\n        onNodeChange.throttle();\n      });\n      const addListener = (name, f) => {\n        updateCallbacks(name, data => ({\n          previous: data.previous,\n          listeners: data.listeners.concat([f])\n        }));\n      };\n      return { addListener };\n    };\n\n    const setup$w = (editor, registry) => {\n      const dataAnnotation$1 = dataAnnotation();\n      const identifyParserNode = node => Optional.from(node.attr(dataAnnotation$1)).bind(registry.lookup);\n      const removeDirectAnnotation = node => {\n        var _a, _b;\n        node.attr(dataAnnotationId(), null);\n        node.attr(dataAnnotation(), null);\n        node.attr(dataAnnotationActive(), null);\n        const customAttrNames = Optional.from(node.attr(dataAnnotationAttributes())).map(names => names.split(',')).getOr([]);\n        const customClasses = Optional.from(node.attr(dataAnnotationClasses())).map(names => names.split(',')).getOr([]);\n        each$e(customAttrNames, name => node.attr(name, null));\n        const classList = (_b = (_a = node.attr('class')) === null || _a === void 0 ? void 0 : _a.split(' ')) !== null && _b !== void 0 ? _b : [];\n        const newClassList = difference(classList, [annotation()].concat(customClasses));\n        node.attr('class', newClassList.length > 0 ? newClassList.join(' ') : null);\n        node.attr(dataAnnotationClasses(), null);\n        node.attr(dataAnnotationAttributes(), null);\n      };\n      editor.serializer.addTempAttr(dataAnnotationActive());\n      editor.serializer.addAttributeFilter(dataAnnotation$1, nodes => {\n        for (const node of nodes) {\n          identifyParserNode(node).each(settings => {\n            if (settings.persistent === false) {\n              if (node.name === 'span') {\n                node.unwrap();\n              } else {\n                removeDirectAnnotation(node);\n              }\n            }\n          });\n        }\n      });\n    };\n\n    const create$c = () => {\n      const annotations = {};\n      const register = (name, settings) => {\n        annotations[name] = {\n          name,\n          settings\n        };\n      };\n      const lookup = name => get$a(annotations, name).map(a => a.settings);\n      const getNames = () => keys(annotations);\n      return {\n        register,\n        lookup,\n        getNames\n      };\n    };\n\n    let unique = 0;\n    const generate$1 = prefix => {\n      const date = new Date();\n      const time = date.getTime();\n      const random = Math.floor(Math.random() * 1000000000);\n      unique++;\n      return prefix + '_' + random + unique + String(time);\n    };\n\n    const add = (element, classes) => {\n      each$e(classes, x => {\n        add$2(element, x);\n      });\n    };\n    const remove$5 = (element, classes) => {\n      each$e(classes, x => {\n        remove$8(element, x);\n      });\n    };\n\n    const clone$2 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));\n    const shallow$1 = original => clone$2(original, false);\n    const deep$1 = original => clone$2(original, true);\n    const shallowAs = (original, tag) => {\n      const nu = SugarElement.fromTag(tag);\n      const attributes = clone$4(original);\n      setAll$1(nu, attributes);\n      return nu;\n    };\n    const mutate = (original, tag) => {\n      const nu = shallowAs(original, tag);\n      after$4(original, nu);\n      const children = children$1(original);\n      append(nu, children);\n      remove$6(original);\n      return nu;\n    };\n\n    const TextWalker = (startNode, rootNode, isBoundary = never) => {\n      const walker = new DomTreeWalker(startNode, rootNode);\n      const walk = direction => {\n        let next;\n        do {\n          next = walker[direction]();\n        } while (next && !isText$a(next) && !isBoundary(next));\n        return Optional.from(next).filter(isText$a);\n      };\n      return {\n        current: () => Optional.from(walker.current()).filter(isText$a),\n        next: () => walk('next'),\n        prev: () => walk('prev'),\n        prev2: () => walk('prev2')\n      };\n    };\n\n    const TextSeeker = (dom, isBoundary) => {\n      const isBlockBoundary = isBoundary ? isBoundary : node => dom.isBlock(node) || isBr$6(node) || isContentEditableFalse$a(node);\n      const walk = (node, offset, walker, process) => {\n        if (isText$a(node)) {\n          const newOffset = process(node, offset, node.data);\n          if (newOffset !== -1) {\n            return Optional.some({\n              container: node,\n              offset: newOffset\n            });\n          }\n        }\n        return walker().bind(next => walk(next.container, next.offset, walker, process));\n      };\n      const backwards = (node, offset, process, root) => {\n        const walker = TextWalker(node, root !== null && root !== void 0 ? root : dom.getRoot(), isBlockBoundary);\n        return walk(node, offset, () => walker.prev().map(prev => ({\n          container: prev,\n          offset: prev.length\n        })), process).getOrNull();\n      };\n      const forwards = (node, offset, process, root) => {\n        const walker = TextWalker(node, root !== null && root !== void 0 ? root : dom.getRoot(), isBlockBoundary);\n        return walk(node, offset, () => walker.next().map(next => ({\n          container: next,\n          offset: 0\n        })), process).getOrNull();\n      };\n      return {\n        backwards,\n        forwards\n      };\n    };\n\n    const round$2 = Math.round;\n    const clone$1 = rect => {\n      if (!rect) {\n        return {\n          left: 0,\n          top: 0,\n          bottom: 0,\n          right: 0,\n          width: 0,\n          height: 0\n        };\n      }\n      return {\n        left: round$2(rect.left),\n        top: round$2(rect.top),\n        bottom: round$2(rect.bottom),\n        right: round$2(rect.right),\n        width: round$2(rect.width),\n        height: round$2(rect.height)\n      };\n    };\n    const collapse = (rect, toStart) => {\n      rect = clone$1(rect);\n      if (toStart) {\n        rect.right = rect.left;\n      } else {\n        rect.left = rect.left + rect.width;\n        rect.right = rect.left;\n      }\n      rect.width = 0;\n      return rect;\n    };\n    const isEqual = (rect1, rect2) => rect1.left === rect2.left && rect1.top === rect2.top && rect1.bottom === rect2.bottom && rect1.right === rect2.right;\n    const isValidOverflow = (overflowY, rect1, rect2) => overflowY >= 0 && overflowY <= Math.min(rect1.height, rect2.height) / 2;\n    const isAbove$1 = (rect1, rect2) => {\n      const halfHeight = Math.min(rect2.height / 2, rect1.height / 2);\n      if (rect1.bottom - halfHeight < rect2.top) {\n        return true;\n      }\n      if (rect1.top > rect2.bottom) {\n        return false;\n      }\n      return isValidOverflow(rect2.top - rect1.bottom, rect1, rect2);\n    };\n    const isBelow$1 = (rect1, rect2) => {\n      if (rect1.top > rect2.bottom) {\n        return true;\n      }\n      if (rect1.bottom < rect2.top) {\n        return false;\n      }\n      return isValidOverflow(rect2.bottom - rect1.top, rect1, rect2);\n    };\n    const containsXY = (rect, clientX, clientY) => clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;\n    const boundingClientRectFromRects = rects => {\n      return foldl(rects, (acc, rect) => {\n        return acc.fold(() => Optional.some(rect), prevRect => {\n          const left = Math.min(rect.left, prevRect.left);\n          const top = Math.min(rect.top, prevRect.top);\n          const right = Math.max(rect.right, prevRect.right);\n          const bottom = Math.max(rect.bottom, prevRect.bottom);\n          return Optional.some({\n            top,\n            right,\n            bottom,\n            left,\n            width: right - left,\n            height: bottom - top\n          });\n        });\n      }, Optional.none());\n    };\n    const distanceToRectEdgeFromXY = (rect, x, y) => {\n      const cx = Math.max(Math.min(x, rect.left + rect.width), rect.left);\n      const cy = Math.max(Math.min(y, rect.top + rect.height), rect.top);\n      return Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));\n    };\n    const overlapY = (r1, r2) => Math.max(0, Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top));\n\n    const clamp$2 = (value, min, max) => Math.min(Math.max(value, min), max);\n\n    const getSelectedNode = range => {\n      const startContainer = range.startContainer, startOffset = range.startOffset;\n      if (startContainer === range.endContainer && startContainer.hasChildNodes() && range.endOffset === startOffset + 1) {\n        return startContainer.childNodes[startOffset];\n      }\n      return null;\n    };\n    const getNode$1 = (container, offset) => {\n      if (isElement$6(container) && container.hasChildNodes()) {\n        const childNodes = container.childNodes;\n        const safeOffset = clamp$2(offset, 0, childNodes.length - 1);\n        return childNodes[safeOffset];\n      } else {\n        return container;\n      }\n    };\n    const getNodeUnsafe = (container, offset) => {\n      if (offset < 0 && isElement$6(container) && container.hasChildNodes()) {\n        return undefined;\n      } else {\n        return getNode$1(container, offset);\n      }\n    };\n\n    const extendingChars = new RegExp('[\\u0300-\\u036f\\u0483-\\u0487\\u0488-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1-\\u05c2\\u05c4-\\u05c5\\u05c7\\u0610-\\u061a' + '\\u064b-\\u065f\\u0670\\u06d6-\\u06dc\\u06df-\\u06e4\\u06e7-\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0' + '\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0859-\\u085b\\u08e3-\\u0902\\u093a\\u093c' + '\\u0941-\\u0948\\u094d\\u0951-\\u0957\\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\\u0c00\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55-\\u0c56' + '\\u0c62-\\u0c63\\u0c81\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc-\\u0ccd\\u0cd5-\\u0cd6\\u0ce2-\\u0ce3\\u0d01\\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\\u0f8d-\\u0f97' + '\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039-\\u103a\\u103d-\\u103e\\u1058-\\u1059\\u105e-\\u1060\\u1071-\\u1074' + '\\u1082\\u1085-\\u1086\\u108d\\u109d\\u135d-\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752-\\u1753\\u1772-\\u1773\\u17b4-\\u17b5' + '\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927-\\u1928\\u1932\\u1939-\\u193b\\u1a17-\\u1a18' + '\\u1a1b\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1ab0-\\u1abd\\u1ABE\\u1b00-\\u1b03\\u1b34' + '\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80-\\u1b81\\u1ba2-\\u1ba5\\u1ba8-\\u1ba9\\u1bab-\\u1bad\\u1be6\\u1be8-\\u1be9' + '\\u1bed\\u1bef-\\u1bf1\\u1c2c-\\u1c33\\u1c36-\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1cf4\\u1cf8-\\u1cf9' + '\\u1dc0-\\u1df5\\u1dfc-\\u1dff\\u200c-\\u200d\\u20d0-\\u20dc\\u20DD-\\u20E0\\u20e1\\u20E2-\\u20E4\\u20e5-\\u20f0\\u2cef-\\u2cf1' + '\\u2d7f\\u2de0-\\u2dff\\u302a-\\u302d\\u302e-\\u302f\\u3099-\\u309a\\ua66f\\uA670-\\uA672\\ua674-\\ua67d\\ua69e-\\ua69f\\ua6f0-\\ua6f1' + '\\ua802\\ua806\\ua80b\\ua825-\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc' + '\\ua9e5\\uaa29-\\uaa2e\\uaa31-\\uaa32\\uaa35-\\uaa36\\uaa43\\uaa4c\\uaa7c\\uaab0\\uaab2-\\uaab4\\uaab7-\\uaab8\\uaabe-\\uaabf\\uaac1' + '\\uaaec-\\uaaed\\uaaf6\\uabe5\\uabe8\\uabed\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe2f\\uff9e-\\uff9f]');\n    const isExtendingChar = ch => isString(ch) && ch.charCodeAt(0) >= 768 && extendingChars.test(ch);\n\n    const or = (...args) => {\n      return x => {\n        for (let i = 0; i < args.length; i++) {\n          if (args[i](x)) {\n            return true;\n          }\n        }\n        return false;\n      };\n    };\n    const and = (...args) => {\n      return x => {\n        for (let i = 0; i < args.length; i++) {\n          if (!args[i](x)) {\n            return false;\n          }\n        }\n        return true;\n      };\n    };\n\n    const isElement$4 = isElement$6;\n    const isCaretCandidate$2 = isCaretCandidate$3;\n    const isBlock$1 = matchStyleValues('display', 'block table');\n    const isFloated = matchStyleValues('float', 'left right');\n    const isValidElementCaretCandidate = and(isElement$4, isCaretCandidate$2, not(isFloated));\n    const isNotPre = not(matchStyleValues('white-space', 'pre pre-line pre-wrap'));\n    const isText$7 = isText$a;\n    const isBr$3 = isBr$6;\n    const nodeIndex$1 = DOMUtils.nodeIndex;\n    const resolveIndex$1 = getNodeUnsafe;\n    const createRange$1 = doc => doc ? doc.createRange() : DOMUtils.DOM.createRng();\n    const isWhiteSpace$1 = chr => isString(chr) && /[\\r\\n\\t ]/.test(chr);\n    const isRange = rng => !!rng.setStart && !!rng.setEnd;\n    const isHiddenWhiteSpaceRange = range => {\n      const container = range.startContainer;\n      const offset = range.startOffset;\n      if (isWhiteSpace$1(range.toString()) && isNotPre(container.parentNode) && isText$a(container)) {\n        const text = container.data;\n        if (isWhiteSpace$1(text[offset - 1]) || isWhiteSpace$1(text[offset + 1])) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const getBrClientRect = brNode => {\n      const doc = brNode.ownerDocument;\n      const rng = createRange$1(doc);\n      const nbsp$1 = doc.createTextNode(nbsp);\n      const parentNode = brNode.parentNode;\n      parentNode.insertBefore(nbsp$1, brNode);\n      rng.setStart(nbsp$1, 0);\n      rng.setEnd(nbsp$1, 1);\n      const clientRect = clone$1(rng.getBoundingClientRect());\n      parentNode.removeChild(nbsp$1);\n      return clientRect;\n    };\n    const getBoundingClientRectWebKitText = rng => {\n      const sc = rng.startContainer;\n      const ec = rng.endContainer;\n      const so = rng.startOffset;\n      const eo = rng.endOffset;\n      if (sc === ec && isText$a(ec) && so === 0 && eo === 1) {\n        const newRng = rng.cloneRange();\n        newRng.setEndAfter(ec);\n        return getBoundingClientRect$1(newRng);\n      } else {\n        return null;\n      }\n    };\n    const isZeroRect = r => r.left === 0 && r.right === 0 && r.top === 0 && r.bottom === 0;\n    const getBoundingClientRect$1 = item => {\n      var _a;\n      let clientRect;\n      const clientRects = item.getClientRects();\n      if (clientRects.length > 0) {\n        clientRect = clone$1(clientRects[0]);\n      } else {\n        clientRect = clone$1(item.getBoundingClientRect());\n      }\n      if (!isRange(item) && isBr$3(item) && isZeroRect(clientRect)) {\n        return getBrClientRect(item);\n      }\n      if (isZeroRect(clientRect) && isRange(item)) {\n        return (_a = getBoundingClientRectWebKitText(item)) !== null && _a !== void 0 ? _a : clientRect;\n      }\n      return clientRect;\n    };\n    const collapseAndInflateWidth = (clientRect, toStart) => {\n      const newClientRect = collapse(clientRect, toStart);\n      newClientRect.width = 1;\n      newClientRect.right = newClientRect.left + 1;\n      return newClientRect;\n    };\n    const getCaretPositionClientRects = caretPosition => {\n      const clientRects = [];\n      const addUniqueAndValidRect = clientRect => {\n        if (clientRect.height === 0) {\n          return;\n        }\n        if (clientRects.length > 0) {\n          if (isEqual(clientRect, clientRects[clientRects.length - 1])) {\n            return;\n          }\n        }\n        clientRects.push(clientRect);\n      };\n      const addCharacterOffset = (container, offset) => {\n        const range = createRange$1(container.ownerDocument);\n        if (offset < container.data.length) {\n          if (isExtendingChar(container.data[offset])) {\n            return;\n          }\n          if (isExtendingChar(container.data[offset - 1])) {\n            range.setStart(container, offset);\n            range.setEnd(container, offset + 1);\n            if (!isHiddenWhiteSpaceRange(range)) {\n              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), false));\n              return;\n            }\n          }\n        }\n        if (offset > 0) {\n          range.setStart(container, offset - 1);\n          range.setEnd(container, offset);\n          if (!isHiddenWhiteSpaceRange(range)) {\n            addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), false));\n          }\n        }\n        if (offset < container.data.length) {\n          range.setStart(container, offset);\n          range.setEnd(container, offset + 1);\n          if (!isHiddenWhiteSpaceRange(range)) {\n            addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(range), true));\n          }\n        }\n      };\n      const container = caretPosition.container();\n      const offset = caretPosition.offset();\n      if (isText$7(container)) {\n        addCharacterOffset(container, offset);\n        return clientRects;\n      }\n      if (isElement$4(container)) {\n        if (caretPosition.isAtEnd()) {\n          const node = resolveIndex$1(container, offset);\n          if (isText$7(node)) {\n            addCharacterOffset(node, node.data.length);\n          }\n          if (isValidElementCaretCandidate(node) && !isBr$3(node)) {\n            addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), false));\n          }\n        } else {\n          const node = resolveIndex$1(container, offset);\n          if (isText$7(node)) {\n            addCharacterOffset(node, 0);\n          }\n          if (isValidElementCaretCandidate(node) && caretPosition.isAtEnd()) {\n            addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), false));\n            return clientRects;\n          }\n          const beforeNode = resolveIndex$1(caretPosition.container(), caretPosition.offset() - 1);\n          if (isValidElementCaretCandidate(beforeNode) && !isBr$3(beforeNode)) {\n            if (isBlock$1(beforeNode) || isBlock$1(node) || !isValidElementCaretCandidate(node)) {\n              addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(beforeNode), false));\n            }\n          }\n          if (isValidElementCaretCandidate(node)) {\n            addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect$1(node), true));\n          }\n        }\n      }\n      return clientRects;\n    };\n    const CaretPosition = (container, offset, clientRects) => {\n      const isAtStart = () => {\n        if (isText$7(container)) {\n          return offset === 0;\n        }\n        return offset === 0;\n      };\n      const isAtEnd = () => {\n        if (isText$7(container)) {\n          return offset >= container.data.length;\n        }\n        return offset >= container.childNodes.length;\n      };\n      const toRange = () => {\n        const range = createRange$1(container.ownerDocument);\n        range.setStart(container, offset);\n        range.setEnd(container, offset);\n        return range;\n      };\n      const getClientRects = () => {\n        if (!clientRects) {\n          clientRects = getCaretPositionClientRects(CaretPosition(container, offset));\n        }\n        return clientRects;\n      };\n      const isVisible = () => getClientRects().length > 0;\n      const isEqual = caretPosition => caretPosition && container === caretPosition.container() && offset === caretPosition.offset();\n      const getNode = before => resolveIndex$1(container, before ? offset - 1 : offset);\n      return {\n        container: constant(container),\n        offset: constant(offset),\n        toRange,\n        getClientRects,\n        isVisible,\n        isAtStart,\n        isAtEnd,\n        isEqual,\n        getNode\n      };\n    };\n    CaretPosition.fromRangeStart = range => CaretPosition(range.startContainer, range.startOffset);\n    CaretPosition.fromRangeEnd = range => CaretPosition(range.endContainer, range.endOffset);\n    CaretPosition.after = node => CaretPosition(node.parentNode, nodeIndex$1(node) + 1);\n    CaretPosition.before = node => CaretPosition(node.parentNode, nodeIndex$1(node));\n    CaretPosition.isAbove = (pos1, pos2) => lift2(head(pos2.getClientRects()), last$3(pos1.getClientRects()), isAbove$1).getOr(false);\n    CaretPosition.isBelow = (pos1, pos2) => lift2(last$3(pos2.getClientRects()), head(pos1.getClientRects()), isBelow$1).getOr(false);\n    CaretPosition.isAtStart = pos => pos ? pos.isAtStart() : false;\n    CaretPosition.isAtEnd = pos => pos ? pos.isAtEnd() : false;\n    CaretPosition.isTextPosition = pos => pos ? isText$a(pos.container()) : false;\n    CaretPosition.isElementPosition = pos => !CaretPosition.isTextPosition(pos);\n\n    const trimEmptyTextNode$1 = (dom, node) => {\n      if (isText$a(node) && node.data.length === 0) {\n        dom.remove(node);\n      }\n    };\n    const insertNode = (dom, rng, node) => {\n      rng.insertNode(node);\n      trimEmptyTextNode$1(dom, node.previousSibling);\n      trimEmptyTextNode$1(dom, node.nextSibling);\n    };\n    const insertFragment = (dom, rng, frag) => {\n      const firstChild = Optional.from(frag.firstChild);\n      const lastChild = Optional.from(frag.lastChild);\n      rng.insertNode(frag);\n      firstChild.each(child => trimEmptyTextNode$1(dom, child.previousSibling));\n      lastChild.each(child => trimEmptyTextNode$1(dom, child.nextSibling));\n    };\n    const rangeInsertNode = (dom, rng, node) => {\n      if (isDocumentFragment(node)) {\n        insertFragment(dom, rng, node);\n      } else {\n        insertNode(dom, rng, node);\n      }\n    };\n\n    const isText$6 = isText$a;\n    const isBogus = isBogus$2;\n    const nodeIndex = DOMUtils.nodeIndex;\n    const normalizedParent = node => {\n      const parentNode = node.parentNode;\n      if (isBogus(parentNode)) {\n        return normalizedParent(parentNode);\n      }\n      return parentNode;\n    };\n    const getChildNodes = node => {\n      if (!node) {\n        return [];\n      }\n      return reduce(node.childNodes, (result, node) => {\n        if (isBogus(node) && node.nodeName !== 'BR') {\n          result = result.concat(getChildNodes(node));\n        } else {\n          result.push(node);\n        }\n        return result;\n      }, []);\n    };\n    const normalizedTextOffset = (node, offset) => {\n      let tempNode = node;\n      while (tempNode = tempNode.previousSibling) {\n        if (!isText$6(tempNode)) {\n          break;\n        }\n        offset += tempNode.data.length;\n      }\n      return offset;\n    };\n    const equal = a => b => a === b;\n    const normalizedNodeIndex = node => {\n      let nodes, index;\n      nodes = getChildNodes(normalizedParent(node));\n      index = findIndex$1(nodes, equal(node), node);\n      nodes = nodes.slice(0, index + 1);\n      const numTextFragments = reduce(nodes, (result, node, i) => {\n        if (isText$6(node) && isText$6(nodes[i - 1])) {\n          result++;\n        }\n        return result;\n      }, 0);\n      nodes = filter$3(nodes, matchNodeNames([node.nodeName]));\n      index = findIndex$1(nodes, equal(node), node);\n      return index - numTextFragments;\n    };\n    const createPathItem = node => {\n      const name = isText$6(node) ? 'text()' : node.nodeName.toLowerCase();\n      return name + '[' + normalizedNodeIndex(node) + ']';\n    };\n    const parentsUntil$1 = (root, node, predicate) => {\n      const parents = [];\n      for (let tempNode = node.parentNode; tempNode && tempNode !== root; tempNode = tempNode.parentNode) {\n        if (predicate && predicate(tempNode)) {\n          break;\n        }\n        parents.push(tempNode);\n      }\n      return parents;\n    };\n    const create$b = (root, caretPosition) => {\n      let path = [];\n      let container = caretPosition.container();\n      let offset = caretPosition.offset();\n      let outputOffset;\n      if (isText$6(container)) {\n        outputOffset = normalizedTextOffset(container, offset);\n      } else {\n        const childNodes = container.childNodes;\n        if (offset >= childNodes.length) {\n          outputOffset = 'after';\n          offset = childNodes.length - 1;\n        } else {\n          outputOffset = 'before';\n        }\n        container = childNodes[offset];\n      }\n      path.push(createPathItem(container));\n      let parents = parentsUntil$1(root, container);\n      parents = filter$3(parents, not(isBogus$2));\n      path = path.concat(map$1(parents, node => {\n        return createPathItem(node);\n      }));\n      return path.reverse().join('/') + ',' + outputOffset;\n    };\n    const resolvePathItem = (node, name, index) => {\n      let nodes = getChildNodes(node);\n      nodes = filter$3(nodes, (node, index) => {\n        return !isText$6(node) || !isText$6(nodes[index - 1]);\n      });\n      nodes = filter$3(nodes, matchNodeNames([name]));\n      return nodes[index];\n    };\n    const findTextPosition = (container, offset) => {\n      let node = container;\n      let targetOffset = 0;\n      while (isText$6(node)) {\n        const dataLen = node.data.length;\n        if (offset >= targetOffset && offset <= targetOffset + dataLen) {\n          container = node;\n          offset = offset - targetOffset;\n          break;\n        }\n        if (!isText$6(node.nextSibling)) {\n          container = node;\n          offset = dataLen;\n          break;\n        }\n        targetOffset += dataLen;\n        node = node.nextSibling;\n      }\n      if (isText$6(container) && offset > container.data.length) {\n        offset = container.data.length;\n      }\n      return CaretPosition(container, offset);\n    };\n    const resolve$1 = (root, path) => {\n      if (!path) {\n        return null;\n      }\n      const parts = path.split(',');\n      const paths = parts[0].split('/');\n      const offset = parts.length > 1 ? parts[1] : 'before';\n      const container = reduce(paths, (result, value) => {\n        const match = /([\\w\\-\\(\\)]+)\\[([0-9]+)\\]/.exec(value);\n        if (!match) {\n          return null;\n        }\n        if (match[1] === 'text()') {\n          match[1] = '#text';\n        }\n        return resolvePathItem(result, match[1], parseInt(match[2], 10));\n      }, root);\n      if (!container) {\n        return null;\n      }\n      if (!isText$6(container) && container.parentNode) {\n        let nodeOffset;\n        if (offset === 'after') {\n          nodeOffset = nodeIndex(container) + 1;\n        } else {\n          nodeOffset = nodeIndex(container);\n        }\n        return CaretPosition(container.parentNode, nodeOffset);\n      }\n      return findTextPosition(container, parseInt(offset, 10));\n    };\n\n    const isContentEditableFalse$8 = isContentEditableFalse$a;\n    const getNormalizedTextOffset$1 = (trim, container, offset) => {\n      let trimmedOffset = trim(container.data.slice(0, offset)).length;\n      for (let node = container.previousSibling; node && isText$a(node); node = node.previousSibling) {\n        trimmedOffset += trim(node.data).length;\n      }\n      return trimmedOffset;\n    };\n    const getPoint = (dom, trim, normalized, rng, start) => {\n      const container = start ? rng.startContainer : rng.endContainer;\n      let offset = start ? rng.startOffset : rng.endOffset;\n      const point = [];\n      const root = dom.getRoot();\n      if (isText$a(container)) {\n        point.push(normalized ? getNormalizedTextOffset$1(trim, container, offset) : offset);\n      } else {\n        let after = 0;\n        const childNodes = container.childNodes;\n        if (offset >= childNodes.length && childNodes.length) {\n          after = 1;\n          offset = Math.max(0, childNodes.length - 1);\n        }\n        point.push(dom.nodeIndex(childNodes[offset], normalized) + after);\n      }\n      for (let node = container; node && node !== root; node = node.parentNode) {\n        point.push(dom.nodeIndex(node, normalized));\n      }\n      return point;\n    };\n    const getLocation = (trim, selection, normalized, rng) => {\n      const dom = selection.dom;\n      const start = getPoint(dom, trim, normalized, rng, true);\n      const forward = selection.isForward();\n      const fakeCaret = isRangeInCaretContainerBlock(rng) ? { isFakeCaret: true } : {};\n      if (!selection.isCollapsed()) {\n        const end = getPoint(dom, trim, normalized, rng, false);\n        return {\n          start,\n          end,\n          forward,\n          ...fakeCaret\n        };\n      } else {\n        return {\n          start,\n          forward,\n          ...fakeCaret\n        };\n      }\n    };\n    const findIndex = (dom, name, element) => {\n      let count = 0;\n      Tools.each(dom.select(name), node => {\n        if (node.getAttribute('data-mce-bogus') === 'all') {\n          return;\n        } else if (node === element) {\n          return false;\n        } else {\n          count++;\n          return;\n        }\n      });\n      return count;\n    };\n    const moveEndPoint$1 = (rng, start) => {\n      let container = start ? rng.startContainer : rng.endContainer;\n      let offset = start ? rng.startOffset : rng.endOffset;\n      if (isElement$6(container) && container.nodeName === 'TR') {\n        const childNodes = container.childNodes;\n        container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];\n        if (container) {\n          offset = start ? 0 : container.childNodes.length;\n          if (start) {\n            rng.setStart(container, offset);\n          } else {\n            rng.setEnd(container, offset);\n          }\n        }\n      }\n    };\n    const normalizeTableCellSelection = rng => {\n      moveEndPoint$1(rng, true);\n      moveEndPoint$1(rng, false);\n      return rng;\n    };\n    const findSibling = (node, offset) => {\n      if (isElement$6(node)) {\n        node = getNode$1(node, offset);\n        if (isContentEditableFalse$8(node)) {\n          return node;\n        }\n      }\n      if (isCaretContainer$2(node)) {\n        if (isText$a(node) && isCaretContainerBlock$1(node)) {\n          node = node.parentNode;\n        }\n        let sibling = node.previousSibling;\n        if (isContentEditableFalse$8(sibling)) {\n          return sibling;\n        }\n        sibling = node.nextSibling;\n        if (isContentEditableFalse$8(sibling)) {\n          return sibling;\n        }\n      }\n      return undefined;\n    };\n    const findAdjacentContentEditableFalseElm = rng => {\n      return findSibling(rng.startContainer, rng.startOffset) || findSibling(rng.endContainer, rng.endOffset);\n    };\n    const getOffsetBookmark = (trim, normalized, selection) => {\n      const element = selection.getNode();\n      const rng = selection.getRng();\n      if (element.nodeName === 'IMG' || isContentEditableFalse$8(element)) {\n        const name = element.nodeName;\n        return {\n          name,\n          index: findIndex(selection.dom, name, element)\n        };\n      }\n      const sibling = findAdjacentContentEditableFalseElm(rng);\n      if (sibling) {\n        const name = sibling.tagName;\n        return {\n          name,\n          index: findIndex(selection.dom, name, sibling)\n        };\n      }\n      return getLocation(trim, selection, normalized, rng);\n    };\n    const getCaretBookmark = selection => {\n      const rng = selection.getRng();\n      return {\n        start: create$b(selection.dom.getRoot(), CaretPosition.fromRangeStart(rng)),\n        end: create$b(selection.dom.getRoot(), CaretPosition.fromRangeEnd(rng)),\n        forward: selection.isForward()\n      };\n    };\n    const getRangeBookmark = selection => {\n      return {\n        rng: selection.getRng(),\n        forward: selection.isForward()\n      };\n    };\n    const createBookmarkSpan = (dom, id, filled) => {\n      const args = {\n        'data-mce-type': 'bookmark',\n        id,\n        'style': 'overflow:hidden;line-height:0px'\n      };\n      return filled ? dom.create('span', args, '&#xFEFF;') : dom.create('span', args);\n    };\n    const getPersistentBookmark = (selection, filled) => {\n      const dom = selection.dom;\n      let rng = selection.getRng();\n      const id = dom.uniqueId();\n      const collapsed = selection.isCollapsed();\n      const element = selection.getNode();\n      const name = element.nodeName;\n      const forward = selection.isForward();\n      if (name === 'IMG') {\n        return {\n          name,\n          index: findIndex(dom, name, element)\n        };\n      }\n      const rng2 = normalizeTableCellSelection(rng.cloneRange());\n      if (!collapsed) {\n        rng2.collapse(false);\n        const endBookmarkNode = createBookmarkSpan(dom, id + '_end', filled);\n        rangeInsertNode(dom, rng2, endBookmarkNode);\n      }\n      rng = normalizeTableCellSelection(rng);\n      rng.collapse(true);\n      const startBookmarkNode = createBookmarkSpan(dom, id + '_start', filled);\n      rangeInsertNode(dom, rng, startBookmarkNode);\n      selection.moveToBookmark({\n        id,\n        keep: true,\n        forward\n      });\n      return {\n        id,\n        forward\n      };\n    };\n    const getBookmark$2 = (selection, type, normalized = false) => {\n      if (type === 2) {\n        return getOffsetBookmark(trim$1, normalized, selection);\n      } else if (type === 3) {\n        return getCaretBookmark(selection);\n      } else if (type) {\n        return getRangeBookmark(selection);\n      } else {\n        return getPersistentBookmark(selection, false);\n      }\n    };\n    const getUndoBookmark = curry(getOffsetBookmark, identity, true);\n\n    const value$1 = value => {\n      const applyHelper = fn => fn(value);\n      const constHelper = constant(value);\n      const outputHelper = () => output;\n      const output = {\n        tag: true,\n        inner: value,\n        fold: (_onError, onValue) => onValue(value),\n        isValue: always,\n        isError: never,\n        map: mapper => Result.value(mapper(value)),\n        mapError: outputHelper,\n        bind: applyHelper,\n        exists: applyHelper,\n        forall: applyHelper,\n        getOr: constHelper,\n        or: outputHelper,\n        getOrThunk: constHelper,\n        orThunk: outputHelper,\n        getOrDie: constHelper,\n        each: fn => {\n          fn(value);\n        },\n        toOptional: () => Optional.some(value)\n      };\n      return output;\n    };\n    const error = error => {\n      const outputHelper = () => output;\n      const output = {\n        tag: false,\n        inner: error,\n        fold: (onError, _onValue) => onError(error),\n        isValue: never,\n        isError: always,\n        map: outputHelper,\n        mapError: mapper => Result.error(mapper(error)),\n        bind: outputHelper,\n        exists: never,\n        forall: always,\n        getOr: identity,\n        or: identity,\n        getOrThunk: apply$1,\n        orThunk: apply$1,\n        getOrDie: die(String(error)),\n        each: noop,\n        toOptional: Optional.none\n      };\n      return output;\n    };\n    const fromOption = (optional, err) => optional.fold(() => error(err), value$1);\n    const Result = {\n      value: value$1,\n      error,\n      fromOption\n    };\n\n    const generate = cases => {\n      if (!isArray$1(cases)) {\n        throw new Error('cases must be an array');\n      }\n      if (cases.length === 0) {\n        throw new Error('there must be at least one case');\n      }\n      const constructors = [];\n      const adt = {};\n      each$e(cases, (acase, count) => {\n        const keys$1 = keys(acase);\n        if (keys$1.length !== 1) {\n          throw new Error('one and only one name per case');\n        }\n        const key = keys$1[0];\n        const value = acase[key];\n        if (adt[key] !== undefined) {\n          throw new Error('duplicate key detected:' + key);\n        } else if (key === 'cata') {\n          throw new Error('cannot have a case named cata (sorry)');\n        } else if (!isArray$1(value)) {\n          throw new Error('case arguments must be an array');\n        }\n        constructors.push(key);\n        adt[key] = (...args) => {\n          const argLength = args.length;\n          if (argLength !== value.length) {\n            throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength);\n          }\n          const match = branches => {\n            const branchKeys = keys(branches);\n            if (constructors.length !== branchKeys.length) {\n              throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\\nActual: ' + branchKeys.join(','));\n            }\n            const allReqd = forall(constructors, reqKey => {\n              return contains$2(branchKeys, reqKey);\n            });\n            if (!allReqd) {\n              throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\\nRequired: ' + constructors.join(', '));\n            }\n            return branches[key].apply(null, args);\n          };\n          return {\n            fold: (...foldArgs) => {\n              if (foldArgs.length !== cases.length) {\n                throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + foldArgs.length);\n              }\n              const target = foldArgs[count];\n              return target.apply(null, args);\n            },\n            match,\n            log: label => {\n              console.log(label, {\n                constructors,\n                constructor: key,\n                params: args\n              });\n            }\n          };\n        };\n      });\n      return adt;\n    };\n    const Adt = { generate };\n\n    Adt.generate([\n      {\n        bothErrors: [\n          'error1',\n          'error2'\n        ]\n      },\n      {\n        firstError: [\n          'error1',\n          'value2'\n        ]\n      },\n      {\n        secondError: [\n          'value1',\n          'error2'\n        ]\n      },\n      {\n        bothValues: [\n          'value1',\n          'value2'\n        ]\n      }\n    ]);\n    const partition$1 = results => {\n      const errors = [];\n      const values = [];\n      each$e(results, result => {\n        result.fold(err => {\n          errors.push(err);\n        }, value => {\n          values.push(value);\n        });\n      });\n      return {\n        errors,\n        values\n      };\n    };\n\n    const isInlinePattern = pattern => pattern.type === 'inline-command' || pattern.type === 'inline-format';\n    const isBlockPattern = pattern => pattern.type === 'block-command' || pattern.type === 'block-format';\n    const normalizePattern = pattern => {\n      const err = message => Result.error({\n        message,\n        pattern\n      });\n      const formatOrCmd = (name, onFormat, onCommand) => {\n        if (pattern.format !== undefined) {\n          let formats;\n          if (isArray$1(pattern.format)) {\n            if (!forall(pattern.format, isString)) {\n              return err(name + ' pattern has non-string items in the `format` array');\n            }\n            formats = pattern.format;\n          } else if (isString(pattern.format)) {\n            formats = [pattern.format];\n          } else {\n            return err(name + ' pattern has non-string `format` parameter');\n          }\n          return Result.value(onFormat(formats));\n        } else if (pattern.cmd !== undefined) {\n          if (!isString(pattern.cmd)) {\n            return err(name + ' pattern has non-string `cmd` parameter');\n          }\n          return Result.value(onCommand(pattern.cmd, pattern.value));\n        } else {\n          return err(name + ' pattern is missing both `format` and `cmd` parameters');\n        }\n      };\n      if (!isObject(pattern)) {\n        return err('Raw pattern is not an object');\n      }\n      if (!isString(pattern.start)) {\n        return err('Raw pattern is missing `start` parameter');\n      }\n      if (pattern.end !== undefined) {\n        if (!isString(pattern.end)) {\n          return err('Inline pattern has non-string `end` parameter');\n        }\n        if (pattern.start.length === 0 && pattern.end.length === 0) {\n          return err('Inline pattern has empty `start` and `end` parameters');\n        }\n        let start = pattern.start;\n        let end = pattern.end;\n        if (end.length === 0) {\n          end = start;\n          start = '';\n        }\n        return formatOrCmd('Inline', format => ({\n          type: 'inline-format',\n          start,\n          end,\n          format\n        }), (cmd, value) => ({\n          type: 'inline-command',\n          start,\n          end,\n          cmd,\n          value\n        }));\n      } else if (pattern.replacement !== undefined) {\n        if (!isString(pattern.replacement)) {\n          return err('Replacement pattern has non-string `replacement` parameter');\n        }\n        if (pattern.start.length === 0) {\n          return err('Replacement pattern has empty `start` parameter');\n        }\n        return Result.value({\n          type: 'inline-command',\n          start: '',\n          end: pattern.start,\n          cmd: 'mceInsertContent',\n          value: pattern.replacement\n        });\n      } else {\n        if (pattern.start.length === 0) {\n          return err('Block pattern has empty `start` parameter');\n        }\n        return formatOrCmd('Block', formats => ({\n          type: 'block-format',\n          start: pattern.start,\n          format: formats[0]\n        }), (command, commandValue) => ({\n          type: 'block-command',\n          start: pattern.start,\n          cmd: command,\n          value: commandValue\n        }));\n      }\n    };\n    const getBlockPatterns = patterns => filter$5(patterns, isBlockPattern);\n    const getInlinePatterns = patterns => filter$5(patterns, isInlinePattern);\n    const createPatternSet = (patterns, dynamicPatternsLookup) => ({\n      inlinePatterns: getInlinePatterns(patterns),\n      blockPatterns: getBlockPatterns(patterns),\n      dynamicPatternsLookup\n    });\n    const fromRawPatterns = patterns => {\n      const normalized = partition$1(map$3(patterns, normalizePattern));\n      each$e(normalized.errors, err => console.error(err.message, err.pattern));\n      return normalized.values;\n    };\n    const fromRawPatternsLookup = lookupFn => {\n      return ctx => {\n        const rawPatterns = lookupFn(ctx);\n        return fromRawPatterns(rawPatterns);\n      };\n    };\n\n    const deviceDetection$1 = detect$2().deviceType;\n    const isTouch = deviceDetection$1.isTouch();\n    const DOM$a = DOMUtils.DOM;\n    const getHash = value => {\n      const items = value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(',');\n      return foldl(items, (output, item) => {\n        const arr = item.split('=');\n        const key = arr[0];\n        const val = arr.length > 1 ? arr[1] : key;\n        output[trim$3(key)] = trim$3(val);\n        return output;\n      }, {});\n    };\n    const isRegExp = x => is$4(x, RegExp);\n    const option = name => editor => editor.options.get(name);\n    const stringOrObjectProcessor = value => isString(value) || isObject(value);\n    const bodyOptionProcessor = (editor, defaultValue = '') => value => {\n      const valid = isString(value);\n      if (valid) {\n        if (value.indexOf('=') !== -1) {\n          const bodyObj = getHash(value);\n          return {\n            value: get$a(bodyObj, editor.id).getOr(defaultValue),\n            valid\n          };\n        } else {\n          return {\n            value,\n            valid\n          };\n        }\n      } else {\n        return {\n          valid: false,\n          message: 'Must be a string.'\n        };\n      }\n    };\n    const register$7 = editor => {\n      const registerOption = editor.options.register;\n      registerOption('id', {\n        processor: 'string',\n        default: editor.id\n      });\n      registerOption('selector', { processor: 'string' });\n      registerOption('target', { processor: 'object' });\n      registerOption('suffix', { processor: 'string' });\n      registerOption('cache_suffix', { processor: 'string' });\n      registerOption('base_url', { processor: 'string' });\n      registerOption('referrer_policy', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('language_load', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('inline', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('iframe_attrs', {\n        processor: 'object',\n        default: {}\n      });\n      registerOption('doctype', {\n        processor: 'string',\n        default: '<!DOCTYPE html>'\n      });\n      registerOption('document_base_url', {\n        processor: 'string',\n        default: editor.documentBaseUrl\n      });\n      registerOption('body_id', {\n        processor: bodyOptionProcessor(editor, 'tinymce'),\n        default: 'tinymce'\n      });\n      registerOption('body_class', {\n        processor: bodyOptionProcessor(editor),\n        default: ''\n      });\n      registerOption('content_security_policy', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('br_in_pre', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('forced_root_block', {\n        processor: value => {\n          const valid = isString(value) && isNotEmpty(value);\n          if (valid) {\n            return {\n              value,\n              valid\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be a non-empty string.'\n            };\n          }\n        },\n        default: 'p'\n      });\n      registerOption('forced_root_block_attrs', {\n        processor: 'object',\n        default: {}\n      });\n      registerOption('newline_behavior', {\n        processor: value => {\n          const valid = contains$2([\n            'block',\n            'linebreak',\n            'invert',\n            'default'\n          ], value);\n          return valid ? {\n            value,\n            valid\n          } : {\n            valid: false,\n            message: 'Must be one of: block, linebreak, invert or default.'\n          };\n        },\n        default: 'default'\n      });\n      registerOption('br_newline_selector', {\n        processor: 'string',\n        default: '.mce-toc h2,figcaption,caption'\n      });\n      registerOption('no_newline_selector', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('keep_styles', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('end_container_on_empty_block', {\n        processor: value => {\n          if (isBoolean(value)) {\n            return {\n              valid: true,\n              value\n            };\n          } else if (isString(value)) {\n            return {\n              valid: true,\n              value\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be boolean or a string'\n            };\n          }\n        },\n        default: 'blockquote'\n      });\n      registerOption('font_size_style_values', {\n        processor: 'string',\n        default: 'xx-small,x-small,small,medium,large,x-large,xx-large'\n      });\n      registerOption('font_size_legacy_values', {\n        processor: 'string',\n        default: 'xx-small,small,medium,large,x-large,xx-large,300%'\n      });\n      registerOption('font_size_classes', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('automatic_uploads', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('images_reuse_filename', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('images_replace_blob_uris', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('icons', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('icons_url', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('images_upload_url', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('images_upload_base_path', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('images_upload_credentials', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('images_upload_handler', { processor: 'function' });\n      registerOption('language', {\n        processor: 'string',\n        default: 'en'\n      });\n      registerOption('language_url', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('entity_encoding', {\n        processor: 'string',\n        default: 'named'\n      });\n      registerOption('indent', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('indent_before', {\n        processor: 'string',\n        default: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + 'tfoot,tbody,tr,section,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist'\n      });\n      registerOption('indent_after', {\n        processor: 'string',\n        default: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' + 'tfoot,tbody,tr,section,summary,article,hgroup,aside,figure,figcaption,option,optgroup,datalist'\n      });\n      registerOption('indent_use_margin', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('indentation', {\n        processor: 'string',\n        default: '40px'\n      });\n      registerOption('content_css', {\n        processor: value => {\n          const valid = value === false || isString(value) || isArrayOf(value, isString);\n          if (valid) {\n            if (isString(value)) {\n              return {\n                value: map$3(value.split(','), trim$3),\n                valid\n              };\n            } else if (isArray$1(value)) {\n              return {\n                value,\n                valid\n              };\n            } else if (value === false) {\n              return {\n                value: [],\n                valid\n              };\n            } else {\n              return {\n                value,\n                valid\n              };\n            }\n          } else {\n            return {\n              valid: false,\n              message: 'Must be false, a string or an array of strings.'\n            };\n          }\n        },\n        default: isInline(editor) ? [] : ['default']\n      });\n      registerOption('content_style', { processor: 'string' });\n      registerOption('content_css_cors', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('font_css', {\n        processor: value => {\n          const valid = isString(value) || isArrayOf(value, isString);\n          if (valid) {\n            const newValue = isArray$1(value) ? value : map$3(value.split(','), trim$3);\n            return {\n              value: newValue,\n              valid\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be a string or an array of strings.'\n            };\n          }\n        },\n        default: []\n      });\n      registerOption('inline_boundaries', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('inline_boundaries_selector', {\n        processor: 'string',\n        default: 'a[href],code,span.mce-annotation'\n      });\n      registerOption('object_resizing', {\n        processor: value => {\n          const valid = isBoolean(value) || isString(value);\n          if (valid) {\n            if (value === false || deviceDetection$1.isiPhone() || deviceDetection$1.isiPad()) {\n              return {\n                value: '',\n                valid\n              };\n            } else {\n              return {\n                value: value === true ? 'table,img,figure.image,div,video,iframe' : value,\n                valid\n              };\n            }\n          } else {\n            return {\n              valid: false,\n              message: 'Must be boolean or a string'\n            };\n          }\n        },\n        default: !isTouch\n      });\n      registerOption('resize_img_proportional', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('event_root', { processor: 'object' });\n      registerOption('service_message', { processor: 'string' });\n      registerOption('theme', {\n        processor: value => value === false || isString(value) || isFunction(value),\n        default: 'silver'\n      });\n      registerOption('theme_url', { processor: 'string' });\n      registerOption('formats', { processor: 'object' });\n      registerOption('format_empty_lines', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('format_noneditable_selector', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('preview_styles', {\n        processor: value => {\n          const valid = value === false || isString(value);\n          if (valid) {\n            return {\n              value: value === false ? '' : value,\n              valid\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be false or a string'\n            };\n          }\n        },\n        default: 'font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow'\n      });\n      registerOption('custom_ui_selector', {\n        processor: 'string',\n        default: ''\n      });\n      registerOption('hidden_input', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('submit_patch', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('encoding', { processor: 'string' });\n      registerOption('add_form_submit_trigger', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('add_unload_trigger', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('custom_undo_redo_levels', {\n        processor: 'number',\n        default: 0\n      });\n      registerOption('disable_nodechange', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('readonly', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('plugins', {\n        processor: 'string[]',\n        default: []\n      });\n      registerOption('external_plugins', { processor: 'object' });\n      registerOption('forced_plugins', { processor: 'string[]' });\n      registerOption('model', {\n        processor: 'string',\n        default: editor.hasPlugin('rtc') ? 'plugin' : 'dom'\n      });\n      registerOption('model_url', { processor: 'string' });\n      registerOption('block_unsupported_drop', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('visual', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('visual_table_class', {\n        processor: 'string',\n        default: 'mce-item-table'\n      });\n      registerOption('visual_anchor_class', {\n        processor: 'string',\n        default: 'mce-item-anchor'\n      });\n      registerOption('iframe_aria_text', {\n        processor: 'string',\n        default: 'Rich Text Area. Press ALT-0 for help.'\n      });\n      registerOption('setup', { processor: 'function' });\n      registerOption('init_instance_callback', { processor: 'function' });\n      registerOption('url_converter', {\n        processor: 'function',\n        default: editor.convertURL\n      });\n      registerOption('url_converter_scope', {\n        processor: 'object',\n        default: editor\n      });\n      registerOption('urlconverter_callback', { processor: 'function' });\n      registerOption('allow_conditional_comments', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('allow_html_data_urls', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('allow_svg_data_urls', { processor: 'boolean' });\n      registerOption('allow_html_in_named_anchor', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('allow_script_urls', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('allow_unsafe_link_target', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('convert_fonts_to_spans', {\n        processor: 'boolean',\n        default: true,\n        deprecated: true\n      });\n      registerOption('fix_list_elements', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('preserve_cdata', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('remove_trailing_brs', { processor: 'boolean' });\n      registerOption('inline_styles', {\n        processor: 'boolean',\n        default: true,\n        deprecated: true\n      });\n      registerOption('element_format', {\n        processor: 'string',\n        default: 'html'\n      });\n      registerOption('entities', { processor: 'string' });\n      registerOption('schema', {\n        processor: 'string',\n        default: 'html5'\n      });\n      registerOption('convert_urls', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('relative_urls', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('remove_script_host', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('custom_elements', { processor: 'string' });\n      registerOption('extended_valid_elements', { processor: 'string' });\n      registerOption('invalid_elements', { processor: 'string' });\n      registerOption('invalid_styles', { processor: stringOrObjectProcessor });\n      registerOption('valid_children', { processor: 'string' });\n      registerOption('valid_classes', { processor: stringOrObjectProcessor });\n      registerOption('valid_elements', { processor: 'string' });\n      registerOption('valid_styles', { processor: stringOrObjectProcessor });\n      registerOption('verify_html', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('auto_focus', { processor: value => isString(value) || value === true });\n      registerOption('browser_spellcheck', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('protect', { processor: 'array' });\n      registerOption('images_file_types', {\n        processor: 'string',\n        default: 'jpeg,jpg,jpe,jfi,jif,jfif,png,gif,bmp,webp'\n      });\n      registerOption('deprecation_warnings', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('a11y_advanced_options', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('api_key', { processor: 'string' });\n      registerOption('paste_block_drop', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('paste_data_images', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('paste_preprocess', { processor: 'function' });\n      registerOption('paste_postprocess', { processor: 'function' });\n      registerOption('paste_webkit_styles', {\n        processor: 'string',\n        default: 'none'\n      });\n      registerOption('paste_remove_styles_if_webkit', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('paste_merge_formats', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('smart_paste', {\n        processor: 'boolean',\n        default: true\n      });\n      registerOption('paste_as_text', {\n        processor: 'boolean',\n        default: false\n      });\n      registerOption('paste_tab_spaces', {\n        processor: 'number',\n        default: 4\n      });\n      registerOption('text_patterns', {\n        processor: value => {\n          if (isArrayOf(value, isObject) || value === false) {\n            const patterns = value === false ? [] : value;\n            return {\n              value: fromRawPatterns(patterns),\n              valid: true\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be an array of objects or false.'\n            };\n          }\n        },\n        default: [\n          {\n            start: '*',\n            end: '*',\n            format: 'italic'\n          },\n          {\n            start: '**',\n            end: '**',\n            format: 'bold'\n          },\n          {\n            start: '#',\n            format: 'h1'\n          },\n          {\n            start: '##',\n            format: 'h2'\n          },\n          {\n            start: '###',\n            format: 'h3'\n          },\n          {\n            start: '####',\n            format: 'h4'\n          },\n          {\n            start: '#####',\n            format: 'h5'\n          },\n          {\n            start: '######',\n            format: 'h6'\n          },\n          {\n            start: '1. ',\n            cmd: 'InsertOrderedList'\n          },\n          {\n            start: '* ',\n            cmd: 'InsertUnorderedList'\n          },\n          {\n            start: '- ',\n            cmd: 'InsertUnorderedList'\n          }\n        ]\n      });\n      registerOption('text_patterns_lookup', {\n        processor: value => {\n          if (isFunction(value)) {\n            return {\n              value: fromRawPatternsLookup(value),\n              valid: true\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be a single function'\n            };\n          }\n        },\n        default: _ctx => []\n      });\n      registerOption('noneditable_class', {\n        processor: 'string',\n        default: 'mceNonEditable'\n      });\n      registerOption('editable_class', {\n        processor: 'string',\n        default: 'mceEditable'\n      });\n      registerOption('noneditable_regexp', {\n        processor: value => {\n          if (isArrayOf(value, isRegExp)) {\n            return {\n              value,\n              valid: true\n            };\n          } else if (isRegExp(value)) {\n            return {\n              value: [value],\n              valid: true\n            };\n          } else {\n            return {\n              valid: false,\n              message: 'Must be a RegExp or an array of RegExp.'\n            };\n          }\n        },\n        default: []\n      });\n      registerOption('table_tab_navigation', {\n        processor: 'boolean',\n        default: true\n      });\n      editor.on('ScriptsLoaded', () => {\n        registerOption('directionality', {\n          processor: 'string',\n          default: I18n.isRtl() ? 'rtl' : undefined\n        });\n        registerOption('placeholder', {\n          processor: 'string',\n          default: DOM$a.getAttrib(editor.getElement(), 'placeholder')\n        });\n      });\n    };\n    const getIframeAttrs = option('iframe_attrs');\n    const getDocType = option('doctype');\n    const getDocumentBaseUrl = option('document_base_url');\n    const getBodyId = option('body_id');\n    const getBodyClass = option('body_class');\n    const getContentSecurityPolicy = option('content_security_policy');\n    const shouldPutBrInPre$1 = option('br_in_pre');\n    const getForcedRootBlock = option('forced_root_block');\n    const getForcedRootBlockAttrs = option('forced_root_block_attrs');\n    const getNewlineBehavior = option('newline_behavior');\n    const getBrNewLineSelector = option('br_newline_selector');\n    const getNoNewLineSelector = option('no_newline_selector');\n    const shouldKeepStyles = option('keep_styles');\n    const shouldEndContainerOnEmptyBlock = option('end_container_on_empty_block');\n    const isAutomaticUploadsEnabled = option('automatic_uploads');\n    const shouldReuseFileName = option('images_reuse_filename');\n    const shouldReplaceBlobUris = option('images_replace_blob_uris');\n    const getIconPackName = option('icons');\n    const getIconsUrl = option('icons_url');\n    const getImageUploadUrl = option('images_upload_url');\n    const getImageUploadBasePath = option('images_upload_base_path');\n    const getImagesUploadCredentials = option('images_upload_credentials');\n    const getImagesUploadHandler = option('images_upload_handler');\n    const shouldUseContentCssCors = option('content_css_cors');\n    const getReferrerPolicy = option('referrer_policy');\n    const getLanguageCode = option('language');\n    const getLanguageUrl = option('language_url');\n    const shouldIndentUseMargin = option('indent_use_margin');\n    const getIndentation = option('indentation');\n    const getContentCss = option('content_css');\n    const getContentStyle = option('content_style');\n    const getFontCss = option('font_css');\n    const getDirectionality = option('directionality');\n    const getInlineBoundarySelector = option('inline_boundaries_selector');\n    const getObjectResizing = option('object_resizing');\n    const getResizeImgProportional = option('resize_img_proportional');\n    const getPlaceholder = option('placeholder');\n    const getEventRoot = option('event_root');\n    const getServiceMessage = option('service_message');\n    const getTheme = option('theme');\n    const getThemeUrl = option('theme_url');\n    const getModel = option('model');\n    const getModelUrl = option('model_url');\n    const isInlineBoundariesEnabled = option('inline_boundaries');\n    const getFormats = option('formats');\n    const getPreviewStyles = option('preview_styles');\n    const canFormatEmptyLines = option('format_empty_lines');\n    const getFormatNoneditableSelector = option('format_noneditable_selector');\n    const getCustomUiSelector = option('custom_ui_selector');\n    const isInline = option('inline');\n    const hasHiddenInput = option('hidden_input');\n    const shouldPatchSubmit = option('submit_patch');\n    const shouldAddFormSubmitTrigger = option('add_form_submit_trigger');\n    const shouldAddUnloadTrigger = option('add_unload_trigger');\n    const getCustomUndoRedoLevels = option('custom_undo_redo_levels');\n    const shouldDisableNodeChange = option('disable_nodechange');\n    const isReadOnly$1 = option('readonly');\n    const hasContentCssCors = option('content_css_cors');\n    const getPlugins = option('plugins');\n    const getExternalPlugins$1 = option('external_plugins');\n    const shouldBlockUnsupportedDrop = option('block_unsupported_drop');\n    const isVisualAidsEnabled = option('visual');\n    const getVisualAidsTableClass = option('visual_table_class');\n    const getVisualAidsAnchorClass = option('visual_anchor_class');\n    const getIframeAriaText = option('iframe_aria_text');\n    const getSetupCallback = option('setup');\n    const getInitInstanceCallback = option('init_instance_callback');\n    const getUrlConverterCallback = option('urlconverter_callback');\n    const getAutoFocus = option('auto_focus');\n    const shouldBrowserSpellcheck = option('browser_spellcheck');\n    const getProtect = option('protect');\n    const shouldPasteBlockDrop = option('paste_block_drop');\n    const shouldPasteDataImages = option('paste_data_images');\n    const getPastePreProcess = option('paste_preprocess');\n    const getPastePostProcess = option('paste_postprocess');\n    const getPasteWebkitStyles = option('paste_webkit_styles');\n    const shouldPasteRemoveWebKitStyles = option('paste_remove_styles_if_webkit');\n    const shouldPasteMergeFormats = option('paste_merge_formats');\n    const isSmartPasteEnabled = option('smart_paste');\n    const isPasteAsTextEnabled = option('paste_as_text');\n    const getPasteTabSpaces = option('paste_tab_spaces');\n    const shouldAllowHtmlDataUrls = option('allow_html_data_urls');\n    const getTextPatterns = option('text_patterns');\n    const getTextPatternsLookup = option('text_patterns_lookup');\n    const getNonEditableClass = option('noneditable_class');\n    const getEditableClass = option('editable_class');\n    const getNonEditableRegExps = option('noneditable_regexp');\n    const shouldPreserveCData = option('preserve_cdata');\n    const hasTextPatternsLookup = editor => editor.options.isSet('text_patterns_lookup');\n    const getFontStyleValues = editor => Tools.explode(editor.options.get('font_size_style_values'));\n    const getFontSizeClasses = editor => Tools.explode(editor.options.get('font_size_classes'));\n    const isEncodingXml = editor => editor.options.get('encoding') === 'xml';\n    const getAllowedImageFileTypes = editor => Tools.explode(editor.options.get('images_file_types'));\n    const hasTableTabNavigation = option('table_tab_navigation');\n\n    const isElement$3 = isElement$6;\n    const isText$5 = isText$a;\n    const removeNode$1 = node => {\n      const parentNode = node.parentNode;\n      if (parentNode) {\n        parentNode.removeChild(node);\n      }\n    };\n    const trimCount = text => {\n      const trimmedText = trim$1(text);\n      return {\n        count: text.length - trimmedText.length,\n        text: trimmedText\n      };\n    };\n    const deleteZwspChars = caretContainer => {\n      let idx;\n      while ((idx = caretContainer.data.lastIndexOf(ZWSP$1)) !== -1) {\n        caretContainer.deleteData(idx, 1);\n      }\n    };\n    const removeUnchanged = (caretContainer, pos) => {\n      remove$4(caretContainer);\n      return pos;\n    };\n    const removeTextAndReposition = (caretContainer, pos) => {\n      const before = trimCount(caretContainer.data.substr(0, pos.offset()));\n      const after = trimCount(caretContainer.data.substr(pos.offset()));\n      const text = before.text + after.text;\n      if (text.length > 0) {\n        deleteZwspChars(caretContainer);\n        return CaretPosition(caretContainer, pos.offset() - before.count);\n      } else {\n        return pos;\n      }\n    };\n    const removeElementAndReposition = (caretContainer, pos) => {\n      const parentNode = pos.container();\n      const newPosition = indexOf$1(from(parentNode.childNodes), caretContainer).map(index => {\n        return index < pos.offset() ? CaretPosition(parentNode, pos.offset() - 1) : pos;\n      }).getOr(pos);\n      remove$4(caretContainer);\n      return newPosition;\n    };\n    const removeTextCaretContainer = (caretContainer, pos) => isText$5(caretContainer) && pos.container() === caretContainer ? removeTextAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos);\n    const removeElementCaretContainer = (caretContainer, pos) => pos.container() === caretContainer.parentNode ? removeElementAndReposition(caretContainer, pos) : removeUnchanged(caretContainer, pos);\n    const removeAndReposition = (container, pos) => CaretPosition.isTextPosition(pos) ? removeTextCaretContainer(container, pos) : removeElementCaretContainer(container, pos);\n    const remove$4 = caretContainerNode => {\n      if (isElement$3(caretContainerNode) && isCaretContainer$2(caretContainerNode)) {\n        if (hasContent(caretContainerNode)) {\n          caretContainerNode.removeAttribute('data-mce-caret');\n        } else {\n          removeNode$1(caretContainerNode);\n        }\n      }\n      if (isText$5(caretContainerNode)) {\n        deleteZwspChars(caretContainerNode);\n        if (caretContainerNode.data.length === 0) {\n          removeNode$1(caretContainerNode);\n        }\n      }\n    };\n\n    const isContentEditableFalse$7 = isContentEditableFalse$a;\n    const isMedia$1 = isMedia$2;\n    const isTableCell$1 = isTableCell$3;\n    const inlineFakeCaretSelector = '*[contentEditable=false],video,audio,embed,object';\n    const getAbsoluteClientRect = (root, element, before) => {\n      const clientRect = collapse(element.getBoundingClientRect(), before);\n      let scrollX;\n      let scrollY;\n      if (root.tagName === 'BODY') {\n        const docElm = root.ownerDocument.documentElement;\n        scrollX = root.scrollLeft || docElm.scrollLeft;\n        scrollY = root.scrollTop || docElm.scrollTop;\n      } else {\n        const rootRect = root.getBoundingClientRect();\n        scrollX = root.scrollLeft - rootRect.left;\n        scrollY = root.scrollTop - rootRect.top;\n      }\n      clientRect.left += scrollX;\n      clientRect.right += scrollX;\n      clientRect.top += scrollY;\n      clientRect.bottom += scrollY;\n      clientRect.width = 1;\n      let margin = element.offsetWidth - element.clientWidth;\n      if (margin > 0) {\n        if (before) {\n          margin *= -1;\n        }\n        clientRect.left += margin;\n        clientRect.right += margin;\n      }\n      return clientRect;\n    };\n    const trimInlineCaretContainers = root => {\n      var _a, _b;\n      const fakeCaretTargetNodes = descendants(SugarElement.fromDom(root), inlineFakeCaretSelector);\n      for (let i = 0; i < fakeCaretTargetNodes.length; i++) {\n        const node = fakeCaretTargetNodes[i].dom;\n        let sibling = node.previousSibling;\n        if (endsWithCaretContainer$1(sibling)) {\n          const data = sibling.data;\n          if (data.length === 1) {\n            (_a = sibling.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(sibling);\n          } else {\n            sibling.deleteData(data.length - 1, 1);\n          }\n        }\n        sibling = node.nextSibling;\n        if (startsWithCaretContainer$1(sibling)) {\n          const data = sibling.data;\n          if (data.length === 1) {\n            (_b = sibling.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(sibling);\n          } else {\n            sibling.deleteData(0, 1);\n          }\n        }\n      }\n    };\n    const FakeCaret = (editor, root, isBlock, hasFocus) => {\n      const lastVisualCaret = value$2();\n      let cursorInterval;\n      let caretContainerNode;\n      const caretBlock = getForcedRootBlock(editor);\n      const dom = editor.dom;\n      const show = (before, element) => {\n        let rng;\n        hide();\n        if (isTableCell$1(element)) {\n          return null;\n        }\n        if (isBlock(element)) {\n          const caretContainer = insertBlock(caretBlock, element, before);\n          const clientRect = getAbsoluteClientRect(root, element, before);\n          dom.setStyle(caretContainer, 'top', clientRect.top);\n          caretContainerNode = caretContainer;\n          const caret = dom.create('div', {\n            'class': 'mce-visual-caret',\n            'data-mce-bogus': 'all'\n          });\n          dom.setStyles(caret, { ...clientRect });\n          dom.add(root, caret);\n          lastVisualCaret.set({\n            caret,\n            element,\n            before\n          });\n          if (before) {\n            dom.addClass(caret, 'mce-visual-caret-before');\n          }\n          startBlink();\n          rng = element.ownerDocument.createRange();\n          rng.setStart(caretContainer, 0);\n          rng.setEnd(caretContainer, 0);\n        } else {\n          caretContainerNode = insertInline$1(element, before);\n          rng = element.ownerDocument.createRange();\n          if (isInlineFakeCaretTarget(caretContainerNode.nextSibling)) {\n            rng.setStart(caretContainerNode, 0);\n            rng.setEnd(caretContainerNode, 0);\n          } else {\n            rng.setStart(caretContainerNode, 1);\n            rng.setEnd(caretContainerNode, 1);\n          }\n          return rng;\n        }\n        return rng;\n      };\n      const hide = () => {\n        trimInlineCaretContainers(root);\n        if (caretContainerNode) {\n          remove$4(caretContainerNode);\n          caretContainerNode = null;\n        }\n        lastVisualCaret.on(caretState => {\n          dom.remove(caretState.caret);\n          lastVisualCaret.clear();\n        });\n        if (cursorInterval) {\n          clearInterval(cursorInterval);\n          cursorInterval = undefined;\n        }\n      };\n      const startBlink = () => {\n        cursorInterval = setInterval(() => {\n          lastVisualCaret.on(caretState => {\n            if (hasFocus()) {\n              dom.toggleClass(caretState.caret, 'mce-visual-caret-hidden');\n            } else {\n              dom.addClass(caretState.caret, 'mce-visual-caret-hidden');\n            }\n          });\n        }, 500);\n      };\n      const reposition = () => {\n        lastVisualCaret.on(caretState => {\n          const clientRect = getAbsoluteClientRect(root, caretState.element, caretState.before);\n          dom.setStyles(caretState.caret, { ...clientRect });\n        });\n      };\n      const destroy = () => clearInterval(cursorInterval);\n      const getCss = () => '.mce-visual-caret {' + 'position: absolute;' + 'background-color: black;' + 'background-color: currentcolor;' + '}' + '.mce-visual-caret-hidden {' + 'display: none;' + '}' + '*[data-mce-caret] {' + 'position: absolute;' + 'left: -1000px;' + 'right: auto;' + 'top: 0;' + 'margin: 0;' + 'padding: 0;' + '}';\n      return {\n        show,\n        hide,\n        getCss,\n        reposition,\n        destroy\n      };\n    };\n    const isFakeCaretTableBrowser = () => Env.browser.isFirefox();\n    const isInlineFakeCaretTarget = node => isContentEditableFalse$7(node) || isMedia$1(node);\n    const isFakeCaretTarget = node => isInlineFakeCaretTarget(node) || isTable$2(node) && isFakeCaretTableBrowser();\n\n    const isContentEditableTrue$1 = isContentEditableTrue$3;\n    const isContentEditableFalse$6 = isContentEditableFalse$a;\n    const isMedia = isMedia$2;\n    const isBlockLike = matchStyleValues('display', 'block table table-cell table-caption list-item');\n    const isCaretContainer = isCaretContainer$2;\n    const isCaretContainerBlock = isCaretContainerBlock$1;\n    const isElement$2 = isElement$6;\n    const isText$4 = isText$a;\n    const isCaretCandidate$1 = isCaretCandidate$3;\n    const isForwards = direction => direction > 0;\n    const isBackwards = direction => direction < 0;\n    const skipCaretContainers = (walk, shallow) => {\n      let node;\n      while (node = walk(shallow)) {\n        if (!isCaretContainerBlock(node)) {\n          return node;\n        }\n      }\n      return null;\n    };\n    const findNode = (node, direction, predicateFn, rootNode, shallow) => {\n      const walker = new DomTreeWalker(node, rootNode);\n      const isCefOrCaretContainer = isContentEditableFalse$6(node) || isCaretContainerBlock(node);\n      let tempNode;\n      if (isBackwards(direction)) {\n        if (isCefOrCaretContainer) {\n          tempNode = skipCaretContainers(walker.prev.bind(walker), true);\n          if (predicateFn(tempNode)) {\n            return tempNode;\n          }\n        }\n        while (tempNode = skipCaretContainers(walker.prev.bind(walker), shallow)) {\n          if (predicateFn(tempNode)) {\n            return tempNode;\n          }\n        }\n      }\n      if (isForwards(direction)) {\n        if (isCefOrCaretContainer) {\n          tempNode = skipCaretContainers(walker.next.bind(walker), true);\n          if (predicateFn(tempNode)) {\n            return tempNode;\n          }\n        }\n        while (tempNode = skipCaretContainers(walker.next.bind(walker), shallow)) {\n          if (predicateFn(tempNode)) {\n            return tempNode;\n          }\n        }\n      }\n      return null;\n    };\n    const getEditingHost = (node, rootNode) => {\n      const isCETrue = node => isContentEditableTrue$1(node.dom);\n      const isRoot = node => node.dom === rootNode;\n      return ancestor$3(SugarElement.fromDom(node), isCETrue, isRoot).map(elm => elm.dom).getOr(rootNode);\n    };\n    const getParentBlock$3 = (node, rootNode) => {\n      while (node && node !== rootNode) {\n        if (isBlockLike(node)) {\n          return node;\n        }\n        node = node.parentNode;\n      }\n      return null;\n    };\n    const isInSameBlock = (caretPosition1, caretPosition2, rootNode) => getParentBlock$3(caretPosition1.container(), rootNode) === getParentBlock$3(caretPosition2.container(), rootNode);\n    const getChildNodeAtRelativeOffset = (relativeOffset, caretPosition) => {\n      if (!caretPosition) {\n        return Optional.none();\n      }\n      const container = caretPosition.container();\n      const offset = caretPosition.offset();\n      if (!isElement$2(container)) {\n        return Optional.none();\n      }\n      return Optional.from(container.childNodes[offset + relativeOffset]);\n    };\n    const beforeAfter = (before, node) => {\n      var _a;\n      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;\n      const range = doc.createRange();\n      if (before) {\n        range.setStartBefore(node);\n        range.setEndBefore(node);\n      } else {\n        range.setStartAfter(node);\n        range.setEndAfter(node);\n      }\n      return range;\n    };\n    const isNodesInSameBlock = (root, node1, node2) => getParentBlock$3(node1, root) === getParentBlock$3(node2, root);\n    const lean = (left, root, node) => {\n      const siblingName = left ? 'previousSibling' : 'nextSibling';\n      let tempNode = node;\n      while (tempNode && tempNode !== root) {\n        let sibling = tempNode[siblingName];\n        if (sibling && isCaretContainer(sibling)) {\n          sibling = sibling[siblingName];\n        }\n        if (isContentEditableFalse$6(sibling) || isMedia(sibling)) {\n          if (isNodesInSameBlock(root, sibling, tempNode)) {\n            return sibling;\n          }\n          break;\n        }\n        if (isCaretCandidate$1(sibling)) {\n          break;\n        }\n        tempNode = tempNode.parentNode;\n      }\n      return null;\n    };\n    const before$2 = curry(beforeAfter, true);\n    const after$2 = curry(beforeAfter, false);\n    const normalizeRange = (direction, root, range) => {\n      let node;\n      const leanLeft = curry(lean, true, root);\n      const leanRight = curry(lean, false, root);\n      const container = range.startContainer;\n      const offset = range.startOffset;\n      if (isCaretContainerBlock$1(container)) {\n        const block = isText$4(container) ? container.parentNode : container;\n        const location = block.getAttribute('data-mce-caret');\n        if (location === 'before') {\n          node = block.nextSibling;\n          if (isFakeCaretTarget(node)) {\n            return before$2(node);\n          }\n        }\n        if (location === 'after') {\n          node = block.previousSibling;\n          if (isFakeCaretTarget(node)) {\n            return after$2(node);\n          }\n        }\n      }\n      if (!range.collapsed) {\n        return range;\n      }\n      if (isText$a(container)) {\n        if (isCaretContainer(container)) {\n          if (direction === 1) {\n            node = leanRight(container);\n            if (node) {\n              return before$2(node);\n            }\n            node = leanLeft(container);\n            if (node) {\n              return after$2(node);\n            }\n          }\n          if (direction === -1) {\n            node = leanLeft(container);\n            if (node) {\n              return after$2(node);\n            }\n            node = leanRight(container);\n            if (node) {\n              return before$2(node);\n            }\n          }\n          return range;\n        }\n        if (endsWithCaretContainer$1(container) && offset >= container.data.length - 1) {\n          if (direction === 1) {\n            node = leanRight(container);\n            if (node) {\n              return before$2(node);\n            }\n          }\n          return range;\n        }\n        if (startsWithCaretContainer$1(container) && offset <= 1) {\n          if (direction === -1) {\n            node = leanLeft(container);\n            if (node) {\n              return after$2(node);\n            }\n          }\n          return range;\n        }\n        if (offset === container.data.length) {\n          node = leanRight(container);\n          if (node) {\n            return before$2(node);\n          }\n          return range;\n        }\n        if (offset === 0) {\n          node = leanLeft(container);\n          if (node) {\n            return after$2(node);\n          }\n          return range;\n        }\n      }\n      return range;\n    };\n    const getRelativeCefElm = (forward, caretPosition) => getChildNodeAtRelativeOffset(forward ? 0 : -1, caretPosition).filter(isContentEditableFalse$6);\n    const getNormalizedRangeEndPoint = (direction, root, range) => {\n      const normalizedRange = normalizeRange(direction, root, range);\n      return direction === -1 ? CaretPosition.fromRangeStart(normalizedRange) : CaretPosition.fromRangeEnd(normalizedRange);\n    };\n    const getElementFromPosition = pos => Optional.from(pos.getNode()).map(SugarElement.fromDom);\n    const getElementFromPrevPosition = pos => Optional.from(pos.getNode(true)).map(SugarElement.fromDom);\n    const getVisualCaretPosition = (walkFn, caretPosition) => {\n      let pos = caretPosition;\n      while (pos = walkFn(pos)) {\n        if (pos.isVisible()) {\n          return pos;\n        }\n      }\n      return pos;\n    };\n    const isMoveInsideSameBlock = (from, to) => {\n      const inSameBlock = isInSameBlock(from, to);\n      if (!inSameBlock && isBr$6(from.getNode())) {\n        return true;\n      }\n      return inSameBlock;\n    };\n\n    var HDirection;\n    (function (HDirection) {\n      HDirection[HDirection['Backwards'] = -1] = 'Backwards';\n      HDirection[HDirection['Forwards'] = 1] = 'Forwards';\n    }(HDirection || (HDirection = {})));\n    const isContentEditableFalse$5 = isContentEditableFalse$a;\n    const isText$3 = isText$a;\n    const isElement$1 = isElement$6;\n    const isBr$2 = isBr$6;\n    const isCaretCandidate = isCaretCandidate$3;\n    const isAtomic = isAtomic$1;\n    const isEditableCaretCandidate = isEditableCaretCandidate$1;\n    const getParents$3 = (node, root) => {\n      const parents = [];\n      let tempNode = node;\n      while (tempNode && tempNode !== root) {\n        parents.push(tempNode);\n        tempNode = tempNode.parentNode;\n      }\n      return parents;\n    };\n    const nodeAtIndex = (container, offset) => {\n      if (container.hasChildNodes() && offset < container.childNodes.length) {\n        return container.childNodes[offset];\n      }\n      return null;\n    };\n    const getCaretCandidatePosition = (direction, node) => {\n      if (isForwards(direction)) {\n        if (isCaretCandidate(node.previousSibling) && !isText$3(node.previousSibling)) {\n          return CaretPosition.before(node);\n        }\n        if (isText$3(node)) {\n          return CaretPosition(node, 0);\n        }\n      }\n      if (isBackwards(direction)) {\n        if (isCaretCandidate(node.nextSibling) && !isText$3(node.nextSibling)) {\n          return CaretPosition.after(node);\n        }\n        if (isText$3(node)) {\n          return CaretPosition(node, node.data.length);\n        }\n      }\n      if (isBackwards(direction)) {\n        if (isBr$2(node)) {\n          return CaretPosition.before(node);\n        }\n        return CaretPosition.after(node);\n      }\n      return CaretPosition.before(node);\n    };\n    const moveForwardFromBr = (root, nextNode) => {\n      const nextSibling = nextNode.nextSibling;\n      if (nextSibling && isCaretCandidate(nextSibling)) {\n        if (isText$3(nextSibling)) {\n          return CaretPosition(nextSibling, 0);\n        } else {\n          return CaretPosition.before(nextSibling);\n        }\n      } else {\n        return findCaretPosition$1(HDirection.Forwards, CaretPosition.after(nextNode), root);\n      }\n    };\n    const findCaretPosition$1 = (direction, startPos, root) => {\n      let node;\n      let nextNode;\n      let innerNode;\n      let caretPosition;\n      if (!isElement$1(root) || !startPos) {\n        return null;\n      }\n      if (startPos.isEqual(CaretPosition.after(root)) && root.lastChild) {\n        caretPosition = CaretPosition.after(root.lastChild);\n        if (isBackwards(direction) && isCaretCandidate(root.lastChild) && isElement$1(root.lastChild)) {\n          return isBr$2(root.lastChild) ? CaretPosition.before(root.lastChild) : caretPosition;\n        }\n      } else {\n        caretPosition = startPos;\n      }\n      const container = caretPosition.container();\n      let offset = caretPosition.offset();\n      if (isText$3(container)) {\n        if (isBackwards(direction) && offset > 0) {\n          return CaretPosition(container, --offset);\n        }\n        if (isForwards(direction) && offset < container.length) {\n          return CaretPosition(container, ++offset);\n        }\n        node = container;\n      } else {\n        if (isBackwards(direction) && offset > 0) {\n          nextNode = nodeAtIndex(container, offset - 1);\n          if (isCaretCandidate(nextNode)) {\n            if (!isAtomic(nextNode)) {\n              innerNode = findNode(nextNode, direction, isEditableCaretCandidate, nextNode);\n              if (innerNode) {\n                if (isText$3(innerNode)) {\n                  return CaretPosition(innerNode, innerNode.data.length);\n                }\n                return CaretPosition.after(innerNode);\n              }\n            }\n            if (isText$3(nextNode)) {\n              return CaretPosition(nextNode, nextNode.data.length);\n            }\n            return CaretPosition.before(nextNode);\n          }\n        }\n        if (isForwards(direction) && offset < container.childNodes.length) {\n          nextNode = nodeAtIndex(container, offset);\n          if (isCaretCandidate(nextNode)) {\n            if (isBr$2(nextNode)) {\n              return moveForwardFromBr(root, nextNode);\n            }\n            if (!isAtomic(nextNode)) {\n              innerNode = findNode(nextNode, direction, isEditableCaretCandidate, nextNode);\n              if (innerNode) {\n                if (isText$3(innerNode)) {\n                  return CaretPosition(innerNode, 0);\n                }\n                return CaretPosition.before(innerNode);\n              }\n            }\n            if (isText$3(nextNode)) {\n              return CaretPosition(nextNode, 0);\n            }\n            return CaretPosition.after(nextNode);\n          }\n        }\n        node = nextNode ? nextNode : caretPosition.getNode();\n      }\n      if (node && (isForwards(direction) && caretPosition.isAtEnd() || isBackwards(direction) && caretPosition.isAtStart())) {\n        node = findNode(node, direction, always, root, true);\n        if (isEditableCaretCandidate(node, root)) {\n          return getCaretCandidatePosition(direction, node);\n        }\n      }\n      nextNode = node ? findNode(node, direction, isEditableCaretCandidate, root) : node;\n      const rootContentEditableFalseElm = last$2(filter$5(getParents$3(container, root), isContentEditableFalse$5));\n      if (rootContentEditableFalseElm && (!nextNode || !rootContentEditableFalseElm.contains(nextNode))) {\n        if (isForwards(direction)) {\n          caretPosition = CaretPosition.after(rootContentEditableFalseElm);\n        } else {\n          caretPosition = CaretPosition.before(rootContentEditableFalseElm);\n        }\n        return caretPosition;\n      }\n      if (nextNode) {\n        return getCaretCandidatePosition(direction, nextNode);\n      }\n      return null;\n    };\n    const CaretWalker = root => ({\n      next: caretPosition => {\n        return findCaretPosition$1(HDirection.Forwards, caretPosition, root);\n      },\n      prev: caretPosition => {\n        return findCaretPosition$1(HDirection.Backwards, caretPosition, root);\n      }\n    });\n\n    const walkToPositionIn = (forward, root, start) => {\n      const position = forward ? CaretPosition.before(start) : CaretPosition.after(start);\n      return fromPosition(forward, root, position);\n    };\n    const afterElement = node => isBr$6(node) ? CaretPosition.before(node) : CaretPosition.after(node);\n    const isBeforeOrStart = position => {\n      if (CaretPosition.isTextPosition(position)) {\n        return position.offset() === 0;\n      } else {\n        return isCaretCandidate$3(position.getNode());\n      }\n    };\n    const isAfterOrEnd = position => {\n      if (CaretPosition.isTextPosition(position)) {\n        const container = position.container();\n        return position.offset() === container.data.length;\n      } else {\n        return isCaretCandidate$3(position.getNode(true));\n      }\n    };\n    const isBeforeAfterSameElement = (from, to) => !CaretPosition.isTextPosition(from) && !CaretPosition.isTextPosition(to) && from.getNode() === to.getNode(true);\n    const isAtBr = position => !CaretPosition.isTextPosition(position) && isBr$6(position.getNode());\n    const shouldSkipPosition = (forward, from, to) => {\n      if (forward) {\n        return !isBeforeAfterSameElement(from, to) && !isAtBr(from) && isAfterOrEnd(from) && isBeforeOrStart(to);\n      } else {\n        return !isBeforeAfterSameElement(to, from) && isBeforeOrStart(from) && isAfterOrEnd(to);\n      }\n    };\n    const fromPosition = (forward, root, pos) => {\n      const walker = CaretWalker(root);\n      return Optional.from(forward ? walker.next(pos) : walker.prev(pos));\n    };\n    const navigate = (forward, root, from) => fromPosition(forward, root, from).bind(to => {\n      if (isInSameBlock(from, to, root) && shouldSkipPosition(forward, from, to)) {\n        return fromPosition(forward, root, to);\n      } else {\n        return Optional.some(to);\n      }\n    });\n    const navigateIgnore = (forward, root, from, ignoreFilter) => navigate(forward, root, from).bind(pos => ignoreFilter(pos) ? navigateIgnore(forward, root, pos, ignoreFilter) : Optional.some(pos));\n    const positionIn = (forward, element) => {\n      const startNode = forward ? element.firstChild : element.lastChild;\n      if (isText$a(startNode)) {\n        return Optional.some(CaretPosition(startNode, forward ? 0 : startNode.data.length));\n      } else if (startNode) {\n        if (isCaretCandidate$3(startNode)) {\n          return Optional.some(forward ? CaretPosition.before(startNode) : afterElement(startNode));\n        } else {\n          return walkToPositionIn(forward, element, startNode);\n        }\n      } else {\n        return Optional.none();\n      }\n    };\n    const nextPosition = curry(fromPosition, true);\n    const prevPosition = curry(fromPosition, false);\n    const firstPositionIn = curry(positionIn, true);\n    const lastPositionIn = curry(positionIn, false);\n\n    const CARET_ID = '_mce_caret';\n    const isCaretNode = node => isElement$6(node) && node.id === CARET_ID;\n    const getParentCaretContainer = (body, node) => {\n      let currentNode = node;\n      while (currentNode && currentNode !== body) {\n        if (isCaretNode(currentNode)) {\n          return currentNode;\n        }\n        currentNode = currentNode.parentNode;\n      }\n      return null;\n    };\n\n    const isStringPathBookmark = bookmark => isString(bookmark.start);\n    const isRangeBookmark = bookmark => has$2(bookmark, 'rng');\n    const isIdBookmark = bookmark => has$2(bookmark, 'id');\n    const isIndexBookmark = bookmark => has$2(bookmark, 'name');\n    const isPathBookmark = bookmark => Tools.isArray(bookmark.start);\n\n    const isForwardBookmark = bookmark => !isIndexBookmark(bookmark) && isBoolean(bookmark.forward) ? bookmark.forward : true;\n    const addBogus = (dom, node) => {\n      if (isElement$6(node) && dom.isBlock(node) && !node.innerHTML) {\n        node.innerHTML = '<br data-mce-bogus=\"1\" />';\n      }\n      return node;\n    };\n    const resolveCaretPositionBookmark = (dom, bookmark) => {\n      const startPos = Optional.from(resolve$1(dom.getRoot(), bookmark.start));\n      const endPos = Optional.from(resolve$1(dom.getRoot(), bookmark.end));\n      return lift2(startPos, endPos, (start, end) => {\n        const range = dom.createRng();\n        range.setStart(start.container(), start.offset());\n        range.setEnd(end.container(), end.offset());\n        return {\n          range,\n          forward: isForwardBookmark(bookmark)\n        };\n      });\n    };\n    const insertZwsp = (node, rng) => {\n      var _a;\n      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;\n      const textNode = doc.createTextNode(ZWSP$1);\n      node.appendChild(textNode);\n      rng.setStart(textNode, 0);\n      rng.setEnd(textNode, 0);\n    };\n    const isEmpty$1 = node => !node.hasChildNodes();\n    const tryFindRangePosition = (node, rng) => lastPositionIn(node).fold(never, pos => {\n      rng.setStart(pos.container(), pos.offset());\n      rng.setEnd(pos.container(), pos.offset());\n      return true;\n    });\n    const padEmptyCaretContainer = (root, node, rng) => {\n      if (isEmpty$1(node) && getParentCaretContainer(root, node)) {\n        insertZwsp(node, rng);\n        return true;\n      } else {\n        return false;\n      }\n    };\n    const setEndPoint = (dom, start, bookmark, rng) => {\n      const point = bookmark[start ? 'start' : 'end'];\n      const root = dom.getRoot();\n      if (point) {\n        let node = root;\n        let offset = point[0];\n        for (let i = point.length - 1; node && i >= 1; i--) {\n          const children = node.childNodes;\n          if (padEmptyCaretContainer(root, node, rng)) {\n            return true;\n          }\n          if (point[i] > children.length - 1) {\n            if (padEmptyCaretContainer(root, node, rng)) {\n              return true;\n            }\n            return tryFindRangePosition(node, rng);\n          }\n          node = children[point[i]];\n        }\n        if (isText$a(node)) {\n          offset = Math.min(point[0], node.data.length);\n        }\n        if (isElement$6(node)) {\n          offset = Math.min(point[0], node.childNodes.length);\n        }\n        if (start) {\n          rng.setStart(node, offset);\n        } else {\n          rng.setEnd(node, offset);\n        }\n      }\n      return true;\n    };\n    const isValidTextNode = node => isText$a(node) && node.data.length > 0;\n    const restoreEndPoint = (dom, suffix, bookmark) => {\n      const marker = dom.get(bookmark.id + '_' + suffix);\n      const markerParent = marker === null || marker === void 0 ? void 0 : marker.parentNode;\n      const keep = bookmark.keep;\n      if (marker && markerParent) {\n        let container;\n        let offset;\n        if (suffix === 'start') {\n          if (!keep) {\n            container = markerParent;\n            offset = dom.nodeIndex(marker);\n          } else {\n            if (marker.hasChildNodes()) {\n              container = marker.firstChild;\n              offset = 1;\n            } else if (isValidTextNode(marker.nextSibling)) {\n              container = marker.nextSibling;\n              offset = 0;\n            } else if (isValidTextNode(marker.previousSibling)) {\n              container = marker.previousSibling;\n              offset = marker.previousSibling.data.length;\n            } else {\n              container = markerParent;\n              offset = dom.nodeIndex(marker) + 1;\n            }\n          }\n        } else {\n          if (!keep) {\n            container = markerParent;\n            offset = dom.nodeIndex(marker);\n          } else {\n            if (marker.hasChildNodes()) {\n              container = marker.firstChild;\n              offset = 1;\n            } else if (isValidTextNode(marker.previousSibling)) {\n              container = marker.previousSibling;\n              offset = marker.previousSibling.data.length;\n            } else {\n              container = markerParent;\n              offset = dom.nodeIndex(marker);\n            }\n          }\n        }\n        if (!keep) {\n          const prev = marker.previousSibling;\n          const next = marker.nextSibling;\n          Tools.each(Tools.grep(marker.childNodes), node => {\n            if (isText$a(node)) {\n              node.data = node.data.replace(/\\uFEFF/g, '');\n            }\n          });\n          let otherMarker;\n          while (otherMarker = dom.get(bookmark.id + '_' + suffix)) {\n            dom.remove(otherMarker, true);\n          }\n          if (isText$a(next) && isText$a(prev) && !Env.browser.isOpera()) {\n            const idx = prev.data.length;\n            prev.appendData(next.data);\n            dom.remove(next);\n            container = prev;\n            offset = idx;\n          }\n        }\n        return Optional.some(CaretPosition(container, offset));\n      } else {\n        return Optional.none();\n      }\n    };\n    const resolvePaths = (dom, bookmark) => {\n      const range = dom.createRng();\n      if (setEndPoint(dom, true, bookmark, range) && setEndPoint(dom, false, bookmark, range)) {\n        return Optional.some({\n          range,\n          forward: isForwardBookmark(bookmark)\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n    const resolveId = (dom, bookmark) => {\n      const startPos = restoreEndPoint(dom, 'start', bookmark);\n      const endPos = restoreEndPoint(dom, 'end', bookmark);\n      return lift2(startPos, endPos.or(startPos), (spos, epos) => {\n        const range = dom.createRng();\n        range.setStart(addBogus(dom, spos.container()), spos.offset());\n        range.setEnd(addBogus(dom, epos.container()), epos.offset());\n        return {\n          range,\n          forward: isForwardBookmark(bookmark)\n        };\n      });\n    };\n    const resolveIndex = (dom, bookmark) => Optional.from(dom.select(bookmark.name)[bookmark.index]).map(elm => {\n      const range = dom.createRng();\n      range.selectNode(elm);\n      return {\n        range,\n        forward: true\n      };\n    });\n    const resolve = (selection, bookmark) => {\n      const dom = selection.dom;\n      if (bookmark) {\n        if (isPathBookmark(bookmark)) {\n          return resolvePaths(dom, bookmark);\n        } else if (isStringPathBookmark(bookmark)) {\n          return resolveCaretPositionBookmark(dom, bookmark);\n        } else if (isIdBookmark(bookmark)) {\n          return resolveId(dom, bookmark);\n        } else if (isIndexBookmark(bookmark)) {\n          return resolveIndex(dom, bookmark);\n        } else if (isRangeBookmark(bookmark)) {\n          return Optional.some({\n            range: bookmark.rng,\n            forward: isForwardBookmark(bookmark)\n          });\n        }\n      }\n      return Optional.none();\n    };\n\n    const getBookmark$1 = (selection, type, normalized) => {\n      return getBookmark$2(selection, type, normalized);\n    };\n    const moveToBookmark = (selection, bookmark) => {\n      resolve(selection, bookmark).each(({range, forward}) => {\n        selection.setRng(range, forward);\n      });\n    };\n    const isBookmarkNode$1 = node => {\n      return isElement$6(node) && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark';\n    };\n\n    const is = expected => actual => expected === actual;\n    const isNbsp = is(nbsp);\n    const isWhiteSpace = chr => chr !== '' && ' \\f\\n\\r\\t\\x0B'.indexOf(chr) !== -1;\n    const isContent = chr => !isWhiteSpace(chr) && !isNbsp(chr) && !isZwsp$1(chr);\n\n    const hexColour = value => ({ value });\n    const toHex = component => {\n      const hex = component.toString(16);\n      return (hex.length === 1 ? '0' + hex : hex).toUpperCase();\n    };\n    const fromRgba = rgbaColour => {\n      const value = toHex(rgbaColour.red) + toHex(rgbaColour.green) + toHex(rgbaColour.blue);\n      return hexColour(value);\n    };\n\n    const rgbRegex = /^\\s*rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)\\s*$/i;\n    const rgbaRegex = /^\\s*rgba\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d?(?:\\.\\d+)?)\\s*\\)\\s*$/i;\n    const rgbaColour = (red, green, blue, alpha) => ({\n      red,\n      green,\n      blue,\n      alpha\n    });\n    const fromStringValues = (red, green, blue, alpha) => {\n      const r = parseInt(red, 10);\n      const g = parseInt(green, 10);\n      const b = parseInt(blue, 10);\n      const a = parseFloat(alpha);\n      return rgbaColour(r, g, b, a);\n    };\n    const fromString = rgbaString => {\n      if (rgbaString === 'transparent') {\n        return Optional.some(rgbaColour(0, 0, 0, 0));\n      }\n      const rgbMatch = rgbRegex.exec(rgbaString);\n      if (rgbMatch !== null) {\n        return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], '1'));\n      }\n      const rgbaMatch = rgbaRegex.exec(rgbaString);\n      if (rgbaMatch !== null) {\n        return Optional.some(fromStringValues(rgbaMatch[1], rgbaMatch[2], rgbaMatch[3], rgbaMatch[4]));\n      }\n      return Optional.none();\n    };\n\n    const rgbaToHexString = color => fromString(color).map(fromRgba).map(h => '#' + h.value).getOr(color);\n\n    const getRanges$1 = selection => {\n      const ranges = [];\n      if (selection) {\n        for (let i = 0; i < selection.rangeCount; i++) {\n          ranges.push(selection.getRangeAt(i));\n        }\n      }\n      return ranges;\n    };\n    const getSelectedNodes = ranges => {\n      return bind$3(ranges, range => {\n        const node = getSelectedNode(range);\n        return node ? [SugarElement.fromDom(node)] : [];\n      });\n    };\n    const hasMultipleRanges = selection => {\n      return getRanges$1(selection).length > 1;\n    };\n\n    const getCellsFromRanges = ranges => filter$5(getSelectedNodes(ranges), isTableCell$2);\n    const getCellsFromElement = elm => descendants(elm, 'td[data-mce-selected],th[data-mce-selected]');\n    const getCellsFromElementOrRanges = (ranges, element) => {\n      const selectedCells = getCellsFromElement(element);\n      return selectedCells.length > 0 ? selectedCells : getCellsFromRanges(ranges);\n    };\n    const getCellsFromEditor = editor => getCellsFromElementOrRanges(getRanges$1(editor.selection.getSel()), SugarElement.fromDom(editor.getBody()));\n    const getClosestTable = (cell, isRoot) => ancestor$2(cell, 'table', isRoot);\n\n    const getStartNode = rng => {\n      const sc = rng.startContainer, so = rng.startOffset;\n      if (isText$a(sc)) {\n        return so === 0 ? Optional.some(SugarElement.fromDom(sc)) : Optional.none();\n      } else {\n        return Optional.from(sc.childNodes[so]).map(SugarElement.fromDom);\n      }\n    };\n    const getEndNode = rng => {\n      const ec = rng.endContainer, eo = rng.endOffset;\n      if (isText$a(ec)) {\n        return eo === ec.data.length ? Optional.some(SugarElement.fromDom(ec)) : Optional.none();\n      } else {\n        return Optional.from(ec.childNodes[eo - 1]).map(SugarElement.fromDom);\n      }\n    };\n    const getFirstChildren = node => {\n      return firstChild(node).fold(constant([node]), child => {\n        return [node].concat(getFirstChildren(child));\n      });\n    };\n    const getLastChildren = node => {\n      return lastChild(node).fold(constant([node]), child => {\n        if (name(child) === 'br') {\n          return prevSibling(child).map(sibling => {\n            return [node].concat(getLastChildren(sibling));\n          }).getOr([]);\n        } else {\n          return [node].concat(getLastChildren(child));\n        }\n      });\n    };\n    const hasAllContentsSelected = (elm, rng) => {\n      return lift2(getStartNode(rng), getEndNode(rng), (startNode, endNode) => {\n        const start = find$2(getFirstChildren(elm), curry(eq, startNode));\n        const end = find$2(getLastChildren(elm), curry(eq, endNode));\n        return start.isSome() && end.isSome();\n      }).getOr(false);\n    };\n    const moveEndPoint = (dom, rng, node, start) => {\n      const root = node;\n      const walker = new DomTreeWalker(node, root);\n      const moveCaretBeforeOnEnterElementsMap = filter$4(dom.schema.getMoveCaretBeforeOnEnterElements(), (_, name) => !contains$2([\n        'td',\n        'th',\n        'table'\n      ], name.toLowerCase()));\n      let currentNode = node;\n      do {\n        if (isText$a(currentNode) && Tools.trim(currentNode.data).length !== 0) {\n          if (start) {\n            rng.setStart(currentNode, 0);\n          } else {\n            rng.setEnd(currentNode, currentNode.data.length);\n          }\n          return;\n        }\n        if (moveCaretBeforeOnEnterElementsMap[currentNode.nodeName]) {\n          if (start) {\n            rng.setStartBefore(currentNode);\n          } else {\n            if (currentNode.nodeName === 'BR') {\n              rng.setEndBefore(currentNode);\n            } else {\n              rng.setEndAfter(currentNode);\n            }\n          }\n          return;\n        }\n      } while (currentNode = start ? walker.next() : walker.prev());\n      if (root.nodeName === 'BODY') {\n        if (start) {\n          rng.setStart(root, 0);\n        } else {\n          rng.setEnd(root, root.childNodes.length);\n        }\n      }\n    };\n    const hasAnyRanges = editor => {\n      const sel = editor.selection.getSel();\n      return isNonNullable(sel) && sel.rangeCount > 0;\n    };\n    const runOnRanges = (editor, executor) => {\n      const fakeSelectionNodes = getCellsFromEditor(editor);\n      if (fakeSelectionNodes.length > 0) {\n        each$e(fakeSelectionNodes, elem => {\n          const node = elem.dom;\n          const fakeNodeRng = editor.dom.createRng();\n          fakeNodeRng.setStartBefore(node);\n          fakeNodeRng.setEndAfter(node);\n          executor(fakeNodeRng, true);\n        });\n      } else {\n        executor(editor.selection.getRng(), false);\n      }\n    };\n    const preserve = (selection, fillBookmark, executor) => {\n      const bookmark = getPersistentBookmark(selection, fillBookmark);\n      executor(bookmark);\n      selection.moveToBookmark(bookmark);\n    };\n\n    const isNode = node => isNumber(node === null || node === void 0 ? void 0 : node.nodeType);\n    const isElementNode$1 = node => isElement$6(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$2(node);\n    const isElementDirectlySelected = (dom, node) => {\n      if (isElementNode$1(node) && !/^(TD|TH)$/.test(node.nodeName)) {\n        const selectedAttr = dom.getAttrib(node, 'data-mce-selected');\n        const value = parseInt(selectedAttr, 10);\n        return !isNaN(value) && value > 0;\n      } else {\n        return false;\n      }\n    };\n    const isEditable$3 = elm => elm.isContentEditable === true;\n    const preserveSelection = (editor, action, shouldMoveStart) => {\n      const {selection, dom} = editor;\n      const selectedNodeBeforeAction = selection.getNode();\n      const isSelectedBeforeNodeNoneditable = isContentEditableFalse$a(selectedNodeBeforeAction);\n      preserve(selection, true, () => {\n        action();\n      });\n      const isBeforeNodeStillNoneditable = isSelectedBeforeNodeNoneditable && isContentEditableFalse$a(selectedNodeBeforeAction);\n      if (isBeforeNodeStillNoneditable && dom.isChildOf(selectedNodeBeforeAction, editor.getBody())) {\n        editor.selection.select(selectedNodeBeforeAction);\n      } else if (shouldMoveStart(selection.getStart())) {\n        moveStartToNearestText(dom, selection);\n      }\n    };\n    const moveStartToNearestText = (dom, selection) => {\n      var _a, _b;\n      const rng = selection.getRng();\n      const {startContainer, startOffset} = rng;\n      const selectedNode = selection.getNode();\n      if (isElementDirectlySelected(dom, selectedNode)) {\n        return;\n      }\n      if (isElement$6(startContainer)) {\n        const nodes = startContainer.childNodes;\n        const root = dom.getRoot();\n        let walker;\n        if (startOffset < nodes.length) {\n          const startNode = nodes[startOffset];\n          walker = new DomTreeWalker(startNode, (_a = dom.getParent(startNode, dom.isBlock)) !== null && _a !== void 0 ? _a : root);\n        } else {\n          const startNode = nodes[nodes.length - 1];\n          walker = new DomTreeWalker(startNode, (_b = dom.getParent(startNode, dom.isBlock)) !== null && _b !== void 0 ? _b : root);\n          walker.next(true);\n        }\n        for (let node = walker.current(); node; node = walker.next()) {\n          if (dom.getContentEditable(node) === 'false') {\n            return;\n          } else if (isText$a(node) && !isWhiteSpaceNode$1(node)) {\n            rng.setStart(node, 0);\n            selection.setRng(rng);\n            return;\n          }\n        }\n      }\n    };\n    const getNonWhiteSpaceSibling = (node, next, inc) => {\n      if (node) {\n        const nextName = next ? 'nextSibling' : 'previousSibling';\n        for (node = inc ? node : node[nextName]; node; node = node[nextName]) {\n          if (isElement$6(node) || !isWhiteSpaceNode$1(node)) {\n            return node;\n          }\n        }\n      }\n      return undefined;\n    };\n    const isTextBlock$1 = (schema, node) => !!schema.getTextBlockElements()[node.nodeName.toLowerCase()] || isTransparentBlock(schema, node);\n    const isValid = (ed, parent, child) => {\n      return ed.schema.isValidChild(parent, child);\n    };\n    const isWhiteSpaceNode$1 = (node, allowSpaces = false) => {\n      if (isNonNullable(node) && isText$a(node)) {\n        const data = allowSpaces ? node.data.replace(/ /g, '\\xA0') : node.data;\n        return isWhitespaceText(data);\n      } else {\n        return false;\n      }\n    };\n    const isEmptyTextNode$1 = node => {\n      return isNonNullable(node) && isText$a(node) && node.length === 0;\n    };\n    const isWrapNoneditableTarget = (editor, node) => {\n      const baseDataSelector = '[data-mce-cef-wrappable]';\n      const formatNoneditableSelector = getFormatNoneditableSelector(editor);\n      const selector = isEmpty$3(formatNoneditableSelector) ? baseDataSelector : `${ baseDataSelector },${ formatNoneditableSelector }`;\n      return is$1(SugarElement.fromDom(node), selector);\n    };\n    const isWrappableNoneditable = (editor, node) => {\n      const dom = editor.dom;\n      return isElementNode$1(node) && dom.getContentEditable(node) === 'false' && isWrapNoneditableTarget(editor, node) && dom.select('[contenteditable=\"true\"]', node).length === 0;\n    };\n    const replaceVars = (value, vars) => {\n      if (isFunction(value)) {\n        return value(vars);\n      } else if (isNonNullable(vars)) {\n        value = value.replace(/%(\\w+)/g, (str, name) => {\n          return vars[name] || str;\n        });\n      }\n      return value;\n    };\n    const isEq$5 = (str1, str2) => {\n      str1 = str1 || '';\n      str2 = str2 || '';\n      str1 = '' + (str1.nodeName || str1);\n      str2 = '' + (str2.nodeName || str2);\n      return str1.toLowerCase() === str2.toLowerCase();\n    };\n    const normalizeStyleValue = (value, name) => {\n      if (isNullable(value)) {\n        return null;\n      } else {\n        let strValue = String(value);\n        if (name === 'color' || name === 'backgroundColor') {\n          strValue = rgbaToHexString(strValue);\n        }\n        if (name === 'fontWeight' && value === 700) {\n          strValue = 'bold';\n        }\n        if (name === 'fontFamily') {\n          strValue = strValue.replace(/[\\'\\\"]/g, '').replace(/,\\s+/g, ',');\n        }\n        return strValue;\n      }\n    };\n    const getStyle = (dom, node, name) => {\n      const style = dom.getStyle(node, name);\n      return normalizeStyleValue(style, name);\n    };\n    const getTextDecoration = (dom, node) => {\n      let decoration;\n      dom.getParent(node, n => {\n        if (isElement$6(n)) {\n          decoration = dom.getStyle(n, 'text-decoration');\n          return !!decoration && decoration !== 'none';\n        } else {\n          return false;\n        }\n      });\n      return decoration;\n    };\n    const getParents$2 = (dom, node, selector) => {\n      return dom.getParents(node, selector, dom.getRoot());\n    };\n    const isFormatPredicate = (editor, formatName, predicate) => {\n      const formats = editor.formatter.get(formatName);\n      return isNonNullable(formats) && exists(formats, predicate);\n    };\n    const isVariableFormatName = (editor, formatName) => {\n      const hasVariableValues = format => {\n        const isVariableValue = val => isFunction(val) || val.length > 1 && val.charAt(0) === '%';\n        return exists([\n          'styles',\n          'attributes'\n        ], key => get$a(format, key).exists(field => {\n          const fieldValues = isArray$1(field) ? field : values(field);\n          return exists(fieldValues, isVariableValue);\n        }));\n      };\n      return isFormatPredicate(editor, formatName, hasVariableValues);\n    };\n    const areSimilarFormats = (editor, formatName, otherFormatName) => {\n      const validKeys = [\n        'inline',\n        'block',\n        'selector',\n        'attributes',\n        'styles',\n        'classes'\n      ];\n      const filterObj = format => filter$4(format, (_, key) => exists(validKeys, validKey => validKey === key));\n      return isFormatPredicate(editor, formatName, fmt1 => {\n        const filteredFmt1 = filterObj(fmt1);\n        return isFormatPredicate(editor, otherFormatName, fmt2 => {\n          const filteredFmt2 = filterObj(fmt2);\n          return equal$1(filteredFmt1, filteredFmt2);\n        });\n      });\n    };\n    const isBlockFormat = format => hasNonNullableKey(format, 'block');\n    const isWrappingBlockFormat = format => isBlockFormat(format) && format.wrapper === true;\n    const isNonWrappingBlockFormat = format => isBlockFormat(format) && format.wrapper !== true;\n    const isSelectorFormat = format => hasNonNullableKey(format, 'selector');\n    const isInlineFormat = format => hasNonNullableKey(format, 'inline');\n    const isMixedFormat = format => isSelectorFormat(format) && isInlineFormat(format) && is$2(get$a(format, 'mixed'), true);\n    const shouldExpandToSelector = format => isSelectorFormat(format) && format.expand !== false && !isInlineFormat(format);\n\n    const isBookmarkNode = isBookmarkNode$1;\n    const getParents$1 = getParents$2;\n    const isWhiteSpaceNode = isWhiteSpaceNode$1;\n    const isTextBlock = isTextBlock$1;\n    const isBogusBr = node => {\n      return isBr$6(node) && node.getAttribute('data-mce-bogus') && !node.nextSibling;\n    };\n    const findParentContentEditable = (dom, node) => {\n      let parent = node;\n      while (parent) {\n        if (isElement$6(parent) && dom.getContentEditable(parent)) {\n          return dom.getContentEditable(parent) === 'false' ? parent : node;\n        }\n        parent = parent.parentNode;\n      }\n      return node;\n    };\n    const walkText = (start, node, offset, predicate) => {\n      const str = node.data;\n      if (start) {\n        for (let i = offset; i > 0; i--) {\n          if (predicate(str.charAt(i - 1))) {\n            return i;\n          }\n        }\n      } else {\n        for (let i = offset; i < str.length; i++) {\n          if (predicate(str.charAt(i))) {\n            return i;\n          }\n        }\n      }\n      return -1;\n    };\n    const findSpace = (start, node, offset) => walkText(start, node, offset, c => isNbsp(c) || isWhiteSpace(c));\n    const findContent = (start, node, offset) => walkText(start, node, offset, isContent);\n    const findWordEndPoint = (dom, body, container, offset, start, includeTrailingSpaces) => {\n      let lastTextNode;\n      const rootNode = dom.getParent(container, dom.isBlock) || body;\n      const walk = (container, offset, pred) => {\n        const textSeeker = TextSeeker(dom);\n        const walker = start ? textSeeker.backwards : textSeeker.forwards;\n        return Optional.from(walker(container, offset, (text, textOffset) => {\n          if (isBookmarkNode(text.parentNode)) {\n            return -1;\n          } else {\n            lastTextNode = text;\n            return pred(start, text, textOffset);\n          }\n        }, rootNode));\n      };\n      const spaceResult = walk(container, offset, findSpace);\n      return spaceResult.bind(result => includeTrailingSpaces ? walk(result.container, result.offset + (start ? -1 : 0), findContent) : Optional.some(result)).orThunk(() => lastTextNode ? Optional.some({\n        container: lastTextNode,\n        offset: start ? 0 : lastTextNode.length\n      }) : Optional.none());\n    };\n    const findSelectorEndPoint = (dom, formatList, rng, container, siblingName) => {\n      const sibling = container[siblingName];\n      if (isText$a(container) && isEmpty$3(container.data) && sibling) {\n        container = sibling;\n      }\n      const parents = getParents$1(dom, container);\n      for (let i = 0; i < parents.length; i++) {\n        for (let y = 0; y < formatList.length; y++) {\n          const curFormat = formatList[y];\n          if (isNonNullable(curFormat.collapsed) && curFormat.collapsed !== rng.collapsed) {\n            continue;\n          }\n          if (isSelectorFormat(curFormat) && dom.is(parents[i], curFormat.selector)) {\n            return parents[i];\n          }\n        }\n      }\n      return container;\n    };\n    const findBlockEndPoint = (dom, formatList, container, siblingName) => {\n      var _a;\n      let node = container;\n      const root = dom.getRoot();\n      const format = formatList[0];\n      if (isBlockFormat(format)) {\n        node = format.wrapper ? null : dom.getParent(container, format.block, root);\n      }\n      if (!node) {\n        const scopeRoot = (_a = dom.getParent(container, 'LI,TD,TH')) !== null && _a !== void 0 ? _a : root;\n        node = dom.getParent(isText$a(container) ? container.parentNode : container, node => node !== root && isTextBlock(dom.schema, node), scopeRoot);\n      }\n      if (node && isBlockFormat(format) && format.wrapper) {\n        node = getParents$1(dom, node, 'ul,ol').reverse()[0] || node;\n      }\n      if (!node) {\n        node = container;\n        while (node && node[siblingName] && !dom.isBlock(node[siblingName])) {\n          node = node[siblingName];\n          if (isEq$5(node, 'br')) {\n            break;\n          }\n        }\n      }\n      return node || container;\n    };\n    const isAtBlockBoundary$1 = (dom, root, container, siblingName) => {\n      const parent = container.parentNode;\n      if (isNonNullable(container[siblingName])) {\n        return false;\n      } else if (parent === root || isNullable(parent) || dom.isBlock(parent)) {\n        return true;\n      } else {\n        return isAtBlockBoundary$1(dom, root, parent, siblingName);\n      }\n    };\n    const findParentContainer = (dom, formatList, container, offset, start) => {\n      let parent = container;\n      const siblingName = start ? 'previousSibling' : 'nextSibling';\n      const root = dom.getRoot();\n      if (isText$a(container) && !isWhiteSpaceNode(container)) {\n        if (start ? offset > 0 : offset < container.data.length) {\n          return container;\n        }\n      }\n      while (parent) {\n        if (!formatList[0].block_expand && dom.isBlock(parent)) {\n          return parent;\n        }\n        for (let sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {\n          const allowSpaces = isText$a(sibling) && !isAtBlockBoundary$1(dom, root, sibling, siblingName);\n          if (!isBookmarkNode(sibling) && !isBogusBr(sibling) && !isWhiteSpaceNode(sibling, allowSpaces)) {\n            return parent;\n          }\n        }\n        if (parent === root || parent.parentNode === root) {\n          container = parent;\n          break;\n        }\n        parent = parent.parentNode;\n      }\n      return container;\n    };\n    const isSelfOrParentBookmark = container => isBookmarkNode(container.parentNode) || isBookmarkNode(container);\n    const expandRng = (dom, rng, formatList, includeTrailingSpace = false) => {\n      let {startContainer, startOffset, endContainer, endOffset} = rng;\n      const format = formatList[0];\n      if (isElement$6(startContainer) && startContainer.hasChildNodes()) {\n        startContainer = getNode$1(startContainer, startOffset);\n        if (isText$a(startContainer)) {\n          startOffset = 0;\n        }\n      }\n      if (isElement$6(endContainer) && endContainer.hasChildNodes()) {\n        endContainer = getNode$1(endContainer, rng.collapsed ? endOffset : endOffset - 1);\n        if (isText$a(endContainer)) {\n          endOffset = endContainer.data.length;\n        }\n      }\n      startContainer = findParentContentEditable(dom, startContainer);\n      endContainer = findParentContentEditable(dom, endContainer);\n      if (isSelfOrParentBookmark(startContainer)) {\n        startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;\n        if (rng.collapsed) {\n          startContainer = startContainer.previousSibling || startContainer;\n        } else {\n          startContainer = startContainer.nextSibling || startContainer;\n        }\n        if (isText$a(startContainer)) {\n          startOffset = rng.collapsed ? startContainer.length : 0;\n        }\n      }\n      if (isSelfOrParentBookmark(endContainer)) {\n        endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;\n        if (rng.collapsed) {\n          endContainer = endContainer.nextSibling || endContainer;\n        } else {\n          endContainer = endContainer.previousSibling || endContainer;\n        }\n        if (isText$a(endContainer)) {\n          endOffset = rng.collapsed ? 0 : endContainer.length;\n        }\n      }\n      if (rng.collapsed) {\n        const startPoint = findWordEndPoint(dom, dom.getRoot(), startContainer, startOffset, true, includeTrailingSpace);\n        startPoint.each(({container, offset}) => {\n          startContainer = container;\n          startOffset = offset;\n        });\n        const endPoint = findWordEndPoint(dom, dom.getRoot(), endContainer, endOffset, false, includeTrailingSpace);\n        endPoint.each(({container, offset}) => {\n          endContainer = container;\n          endOffset = offset;\n        });\n      }\n      if (isInlineFormat(format) || format.block_expand) {\n        if (!isInlineFormat(format) || (!isText$a(startContainer) || startOffset === 0)) {\n          startContainer = findParentContainer(dom, formatList, startContainer, startOffset, true);\n        }\n        if (!isInlineFormat(format) || (!isText$a(endContainer) || endOffset === endContainer.data.length)) {\n          endContainer = findParentContainer(dom, formatList, endContainer, endOffset, false);\n        }\n      }\n      if (shouldExpandToSelector(format)) {\n        startContainer = findSelectorEndPoint(dom, formatList, rng, startContainer, 'previousSibling');\n        endContainer = findSelectorEndPoint(dom, formatList, rng, endContainer, 'nextSibling');\n      }\n      if (isBlockFormat(format) || isSelectorFormat(format)) {\n        startContainer = findBlockEndPoint(dom, formatList, startContainer, 'previousSibling');\n        endContainer = findBlockEndPoint(dom, formatList, endContainer, 'nextSibling');\n        if (isBlockFormat(format)) {\n          if (!dom.isBlock(startContainer)) {\n            startContainer = findParentContainer(dom, formatList, startContainer, startOffset, true);\n          }\n          if (!dom.isBlock(endContainer)) {\n            endContainer = findParentContainer(dom, formatList, endContainer, endOffset, false);\n          }\n        }\n      }\n      if (isElement$6(startContainer) && startContainer.parentNode) {\n        startOffset = dom.nodeIndex(startContainer);\n        startContainer = startContainer.parentNode;\n      }\n      if (isElement$6(endContainer) && endContainer.parentNode) {\n        endOffset = dom.nodeIndex(endContainer) + 1;\n        endContainer = endContainer.parentNode;\n      }\n      return {\n        startContainer,\n        startOffset,\n        endContainer,\n        endOffset\n      };\n    };\n\n    const walk$3 = (dom, rng, callback) => {\n      var _a;\n      const startOffset = rng.startOffset;\n      const startContainer = getNode$1(rng.startContainer, startOffset);\n      const endOffset = rng.endOffset;\n      const endContainer = getNode$1(rng.endContainer, endOffset - 1);\n      const exclude = nodes => {\n        const firstNode = nodes[0];\n        if (isText$a(firstNode) && firstNode === startContainer && startOffset >= firstNode.data.length) {\n          nodes.splice(0, 1);\n        }\n        const lastNode = nodes[nodes.length - 1];\n        if (endOffset === 0 && nodes.length > 0 && lastNode === endContainer && isText$a(lastNode)) {\n          nodes.splice(nodes.length - 1, 1);\n        }\n        return nodes;\n      };\n      const collectSiblings = (node, name, endNode) => {\n        const siblings = [];\n        for (; node && node !== endNode; node = node[name]) {\n          siblings.push(node);\n        }\n        return siblings;\n      };\n      const findEndPoint = (node, root) => dom.getParent(node, node => node.parentNode === root, root);\n      const walkBoundary = (startNode, endNode, next) => {\n        const siblingName = next ? 'nextSibling' : 'previousSibling';\n        for (let node = startNode, parent = node.parentNode; node && node !== endNode; node = parent) {\n          parent = node.parentNode;\n          const siblings = collectSiblings(node === startNode ? node : node[siblingName], siblingName);\n          if (siblings.length) {\n            if (!next) {\n              siblings.reverse();\n            }\n            callback(exclude(siblings));\n          }\n        }\n      };\n      if (startContainer === endContainer) {\n        return callback(exclude([startContainer]));\n      }\n      const ancestor = (_a = dom.findCommonAncestor(startContainer, endContainer)) !== null && _a !== void 0 ? _a : dom.getRoot();\n      if (dom.isChildOf(startContainer, endContainer)) {\n        return walkBoundary(startContainer, ancestor, true);\n      }\n      if (dom.isChildOf(endContainer, startContainer)) {\n        return walkBoundary(endContainer, ancestor);\n      }\n      const startPoint = findEndPoint(startContainer, ancestor) || startContainer;\n      const endPoint = findEndPoint(endContainer, ancestor) || endContainer;\n      walkBoundary(startContainer, startPoint, true);\n      const siblings = collectSiblings(startPoint === startContainer ? startPoint : startPoint.nextSibling, 'nextSibling', endPoint === endContainer ? endPoint.nextSibling : endPoint);\n      if (siblings.length) {\n        callback(exclude(siblings));\n      }\n      walkBoundary(endContainer, endPoint);\n    };\n\n    const validBlocks = [\n      'pre[class*=language-][contenteditable=\"false\"]',\n      'figure.image',\n      'div[data-ephox-embed-iri]',\n      'div.tiny-pageembed',\n      'div.mce-toc',\n      'div[data-mce-toc]'\n    ];\n    const isZeroWidth = elem => isText$b(elem) && get$3(elem) === ZWSP$1;\n    const context = (editor, elem, wrapName, nodeName) => parent(elem).fold(() => 'skipping', parent => {\n      if (nodeName === 'br' || isZeroWidth(elem)) {\n        return 'valid';\n      } else if (isAnnotation(elem)) {\n        return 'existing';\n      } else if (isCaretNode(elem.dom)) {\n        return 'caret';\n      } else if (exists(validBlocks, selector => is$1(elem, selector))) {\n        return 'valid-block';\n      } else if (!isValid(editor, wrapName, nodeName) || !isValid(editor, name(parent), wrapName)) {\n        return 'invalid-child';\n      } else {\n        return 'valid';\n      }\n    });\n\n    const applyWordGrab = (editor, rng) => {\n      const r = expandRng(editor.dom, rng, [{ inline: 'span' }]);\n      rng.setStart(r.startContainer, r.startOffset);\n      rng.setEnd(r.endContainer, r.endOffset);\n      editor.selection.setRng(rng);\n    };\n    const applyAnnotation = (elem, masterUId, data, annotationName, decorate, directAnnotation) => {\n      const {uid = masterUId, ...otherData} = data;\n      add$2(elem, annotation());\n      set$2(elem, `${ dataAnnotationId() }`, uid);\n      set$2(elem, `${ dataAnnotation() }`, annotationName);\n      const {attributes = {}, classes = []} = decorate(uid, otherData);\n      setAll$1(elem, attributes);\n      add(elem, classes);\n      if (directAnnotation) {\n        if (classes.length > 0) {\n          set$2(elem, `${ dataAnnotationClasses() }`, classes.join(','));\n        }\n        const attributeNames = keys(attributes);\n        if (attributeNames.length > 0) {\n          set$2(elem, `${ dataAnnotationAttributes() }`, attributeNames.join(','));\n        }\n      }\n    };\n    const removeDirectAnnotation = elem => {\n      remove$8(elem, annotation());\n      remove$b(elem, `${ dataAnnotationId() }`);\n      remove$b(elem, `${ dataAnnotation() }`);\n      remove$b(elem, `${ dataAnnotationActive() }`);\n      const customAttrNames = getOpt(elem, `${ dataAnnotationAttributes() }`).map(names => names.split(',')).getOr([]);\n      const customClasses = getOpt(elem, `${ dataAnnotationClasses() }`).map(names => names.split(',')).getOr([]);\n      each$e(customAttrNames, name => remove$b(elem, name));\n      remove$5(elem, customClasses);\n      remove$b(elem, `${ dataAnnotationClasses() }`);\n      remove$b(elem, `${ dataAnnotationAttributes() }`);\n    };\n    const makeAnnotation = (eDoc, uid, data, annotationName, decorate) => {\n      const master = SugarElement.fromTag('span', eDoc);\n      applyAnnotation(master, uid, data, annotationName, decorate, false);\n      return master;\n    };\n    const annotate = (editor, rng, uid, annotationName, decorate, data) => {\n      const newWrappers = [];\n      const master = makeAnnotation(editor.getDoc(), uid, data, annotationName, decorate);\n      const wrapper = value$2();\n      const finishWrapper = () => {\n        wrapper.clear();\n      };\n      const getOrOpenWrapper = () => wrapper.get().getOrThunk(() => {\n        const nu = shallow$1(master);\n        newWrappers.push(nu);\n        wrapper.set(nu);\n        return nu;\n      });\n      const processElements = elems => {\n        each$e(elems, processElement);\n      };\n      const processElement = elem => {\n        const ctx = context(editor, elem, 'span', name(elem));\n        switch (ctx) {\n        case 'invalid-child': {\n            finishWrapper();\n            const children = children$1(elem);\n            processElements(children);\n            finishWrapper();\n            break;\n          }\n        case 'valid-block': {\n            finishWrapper();\n            applyAnnotation(elem, uid, data, annotationName, decorate, true);\n            break;\n          }\n        case 'valid': {\n            const w = getOrOpenWrapper();\n            wrap$2(elem, w);\n            break;\n          }\n        }\n      };\n      const processNodes = nodes => {\n        const elems = map$3(nodes, SugarElement.fromDom);\n        processElements(elems);\n      };\n      walk$3(editor.dom, rng, nodes => {\n        finishWrapper();\n        processNodes(nodes);\n      });\n      return newWrappers;\n    };\n    const annotateWithBookmark = (editor, name, settings, data) => {\n      editor.undoManager.transact(() => {\n        const selection = editor.selection;\n        const initialRng = selection.getRng();\n        const hasFakeSelection = getCellsFromEditor(editor).length > 0;\n        const masterUid = generate$1('mce-annotation');\n        if (initialRng.collapsed && !hasFakeSelection) {\n          applyWordGrab(editor, initialRng);\n        }\n        if (selection.getRng().collapsed && !hasFakeSelection) {\n          const wrapper = makeAnnotation(editor.getDoc(), masterUid, data, name, settings.decorate);\n          set(wrapper, nbsp);\n          selection.getRng().insertNode(wrapper.dom);\n          selection.select(wrapper.dom);\n        } else {\n          preserve(selection, false, () => {\n            runOnRanges(editor, selectionRng => {\n              annotate(editor, selectionRng, masterUid, name, settings.decorate, data);\n            });\n          });\n        }\n      });\n    };\n\n    const Annotator = editor => {\n      const registry = create$c();\n      setup$w(editor, registry);\n      const changes = setup$x(editor, registry);\n      const isSpan = isTag('span');\n      const removeAnnotations = elements => {\n        each$e(elements, element => {\n          if (isSpan(element)) {\n            unwrap(element);\n          } else {\n            removeDirectAnnotation(element);\n          }\n        });\n      };\n      return {\n        register: (name, settings) => {\n          registry.register(name, settings);\n        },\n        annotate: (name, data) => {\n          registry.lookup(name).each(settings => {\n            annotateWithBookmark(editor, name, settings, data);\n          });\n        },\n        annotationChanged: (name, callback) => {\n          changes.addListener(name, callback);\n        },\n        remove: name => {\n          const bookmark = editor.selection.getBookmark();\n          identify(editor, Optional.some(name)).each(({elements}) => {\n            removeAnnotations(elements);\n          });\n          editor.selection.moveToBookmark(bookmark);\n        },\n        removeAll: name => {\n          const bookmark = editor.selection.getBookmark();\n          each$d(findAll(editor, name), (elements, _) => {\n            removeAnnotations(elements);\n          });\n          editor.selection.moveToBookmark(bookmark);\n        },\n        getAll: name => {\n          const directory = findAll(editor, name);\n          return map$2(directory, elems => map$3(elems, elem => elem.dom));\n        }\n      };\n    };\n\n    const BookmarkManager = selection => {\n      return {\n        getBookmark: curry(getBookmark$1, selection),\n        moveToBookmark: curry(moveToBookmark, selection)\n      };\n    };\n    BookmarkManager.isBookmarkNode = isBookmarkNode$1;\n\n    const isXYWithinRange = (clientX, clientY, range) => {\n      if (range.collapsed) {\n        return false;\n      } else {\n        return exists(range.getClientRects(), rect => containsXY(rect, clientX, clientY));\n      }\n    };\n\n    const firePreProcess = (editor, args) => editor.dispatch('PreProcess', args);\n    const firePostProcess = (editor, args) => editor.dispatch('PostProcess', args);\n    const fireRemove = editor => {\n      editor.dispatch('remove');\n    };\n    const fireDetach = editor => {\n      editor.dispatch('detach');\n    };\n    const fireSwitchMode = (editor, mode) => {\n      editor.dispatch('SwitchMode', { mode });\n    };\n    const fireObjectResizeStart = (editor, target, width, height, origin) => {\n      editor.dispatch('ObjectResizeStart', {\n        target,\n        width,\n        height,\n        origin\n      });\n    };\n    const fireObjectResized = (editor, target, width, height, origin) => {\n      editor.dispatch('ObjectResized', {\n        target,\n        width,\n        height,\n        origin\n      });\n    };\n    const firePreInit = editor => {\n      editor.dispatch('PreInit');\n    };\n    const firePostRender = editor => {\n      editor.dispatch('PostRender');\n    };\n    const fireInit = editor => {\n      editor.dispatch('Init');\n    };\n    const firePlaceholderToggle = (editor, state) => {\n      editor.dispatch('PlaceholderToggle', { state });\n    };\n    const fireError = (editor, errorType, error) => {\n      editor.dispatch(errorType, error);\n    };\n    const fireFormatApply = (editor, format, node, vars) => {\n      editor.dispatch('FormatApply', {\n        format,\n        node,\n        vars\n      });\n    };\n    const fireFormatRemove = (editor, format, node, vars) => {\n      editor.dispatch('FormatRemove', {\n        format,\n        node,\n        vars\n      });\n    };\n    const fireBeforeSetContent = (editor, args) => editor.dispatch('BeforeSetContent', args);\n    const fireSetContent = (editor, args) => editor.dispatch('SetContent', args);\n    const fireBeforeGetContent = (editor, args) => editor.dispatch('BeforeGetContent', args);\n    const fireGetContent = (editor, args) => editor.dispatch('GetContent', args);\n    const fireAutocompleterStart = (editor, args) => {\n      editor.dispatch('AutocompleterStart', args);\n    };\n    const fireAutocompleterUpdate = (editor, args) => {\n      editor.dispatch('AutocompleterUpdate', args);\n    };\n    const fireAutocompleterEnd = editor => {\n      editor.dispatch('AutocompleterEnd');\n    };\n    const firePastePreProcess = (editor, html, internal) => editor.dispatch('PastePreProcess', {\n      content: html,\n      internal\n    });\n    const firePastePostProcess = (editor, node, internal) => editor.dispatch('PastePostProcess', {\n      node,\n      internal\n    });\n    const firePastePlainTextToggle = (editor, state) => editor.dispatch('PastePlainTextToggle', { state });\n\n    const VK = {\n      BACKSPACE: 8,\n      DELETE: 46,\n      DOWN: 40,\n      ENTER: 13,\n      ESC: 27,\n      LEFT: 37,\n      RIGHT: 39,\n      SPACEBAR: 32,\n      TAB: 9,\n      UP: 38,\n      PAGE_UP: 33,\n      PAGE_DOWN: 34,\n      END: 35,\n      HOME: 36,\n      modifierPressed: e => {\n        return e.shiftKey || e.ctrlKey || e.altKey || VK.metaKeyPressed(e);\n      },\n      metaKeyPressed: e => {\n        return Env.os.isMacOS() || Env.os.isiOS() ? e.metaKey : e.ctrlKey && !e.altKey;\n      }\n    };\n\n    const elementSelectionAttr = 'data-mce-selected';\n    const controlElmSelector = 'table,img,figure.image,hr,video,span.mce-preview-object';\n    const abs = Math.abs;\n    const round$1 = Math.round;\n    const resizeHandles = {\n      nw: [\n        0,\n        0,\n        -1,\n        -1\n      ],\n      ne: [\n        1,\n        0,\n        1,\n        -1\n      ],\n      se: [\n        1,\n        1,\n        1,\n        1\n      ],\n      sw: [\n        0,\n        1,\n        -1,\n        1\n      ]\n    };\n    const isTouchEvent = evt => evt.type === 'longpress' || evt.type.indexOf('touch') === 0;\n    const ControlSelection = (selection, editor) => {\n      const dom = editor.dom;\n      const editableDoc = editor.getDoc();\n      const rootDocument = document;\n      const rootElement = editor.getBody();\n      let selectedElm, selectedElmGhost, resizeHelper, selectedHandle, resizeBackdrop;\n      let startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;\n      let width;\n      let height;\n      let startScrollWidth;\n      let startScrollHeight;\n      const isImage = elm => isNonNullable(elm) && (isImg(elm) || dom.is(elm, 'figure.image'));\n      const isMedia = elm => isMedia$2(elm) || dom.hasClass(elm, 'mce-preview-object');\n      const isEventOnImageOutsideRange = (evt, range) => {\n        if (isTouchEvent(evt)) {\n          const touch = evt.touches[0];\n          return isImage(evt.target) && !isXYWithinRange(touch.clientX, touch.clientY, range);\n        } else {\n          return isImage(evt.target) && !isXYWithinRange(evt.clientX, evt.clientY, range);\n        }\n      };\n      const contextMenuSelectImage = evt => {\n        const target = evt.target;\n        if (isEventOnImageOutsideRange(evt, editor.selection.getRng()) && !evt.isDefaultPrevented()) {\n          editor.selection.select(target);\n        }\n      };\n      const getResizeTargets = elm => {\n        if (dom.hasClass(elm, 'mce-preview-object') && isNonNullable(elm.firstElementChild)) {\n          return [\n            elm,\n            elm.firstElementChild\n          ];\n        } else if (dom.is(elm, 'figure.image')) {\n          return [elm.querySelector('img')];\n        } else {\n          return [elm];\n        }\n      };\n      const isResizable = elm => {\n        const selector = getObjectResizing(editor);\n        if (!selector) {\n          return false;\n        }\n        if (elm.getAttribute('data-mce-resize') === 'false') {\n          return false;\n        }\n        if (elm === editor.getBody()) {\n          return false;\n        }\n        if (dom.hasClass(elm, 'mce-preview-object') && isNonNullable(elm.firstElementChild)) {\n          return is$1(SugarElement.fromDom(elm.firstElementChild), selector);\n        } else {\n          return is$1(SugarElement.fromDom(elm), selector);\n        }\n      };\n      const createGhostElement = elm => {\n        if (isMedia(elm)) {\n          return dom.create('img', { src: Env.transparentSrc });\n        } else {\n          return elm.cloneNode(true);\n        }\n      };\n      const setSizeProp = (element, name, value) => {\n        if (isNonNullable(value)) {\n          const targets = getResizeTargets(element);\n          each$e(targets, target => {\n            if (target.style[name] || !editor.schema.isValid(target.nodeName.toLowerCase(), name)) {\n              dom.setStyle(target, name, value);\n            } else {\n              dom.setAttrib(target, name, '' + value);\n            }\n          });\n        }\n      };\n      const setGhostElmSize = (ghostElm, width, height) => {\n        setSizeProp(ghostElm, 'width', width);\n        setSizeProp(ghostElm, 'height', height);\n      };\n      const resizeGhostElement = e => {\n        let deltaX, deltaY, proportional;\n        let resizeHelperX, resizeHelperY;\n        deltaX = e.screenX - startX;\n        deltaY = e.screenY - startY;\n        width = deltaX * selectedHandle[2] + startW;\n        height = deltaY * selectedHandle[3] + startH;\n        width = width < 5 ? 5 : width;\n        height = height < 5 ? 5 : height;\n        if ((isImage(selectedElm) || isMedia(selectedElm)) && getResizeImgProportional(editor) !== false) {\n          proportional = !VK.modifierPressed(e);\n        } else {\n          proportional = VK.modifierPressed(e);\n        }\n        if (proportional) {\n          if (abs(deltaX) > abs(deltaY)) {\n            height = round$1(width * ratio);\n            width = round$1(height / ratio);\n          } else {\n            width = round$1(height / ratio);\n            height = round$1(width * ratio);\n          }\n        }\n        setGhostElmSize(selectedElmGhost, width, height);\n        resizeHelperX = selectedHandle.startPos.x + deltaX;\n        resizeHelperY = selectedHandle.startPos.y + deltaY;\n        resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0;\n        resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0;\n        dom.setStyles(resizeHelper, {\n          left: resizeHelperX,\n          top: resizeHelperY,\n          display: 'block'\n        });\n        resizeHelper.innerHTML = width + ' &times; ' + height;\n        if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {\n          dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));\n        }\n        if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {\n          dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));\n        }\n        deltaX = rootElement.scrollWidth - startScrollWidth;\n        deltaY = rootElement.scrollHeight - startScrollHeight;\n        if (deltaX + deltaY !== 0) {\n          dom.setStyles(resizeHelper, {\n            left: resizeHelperX - deltaX,\n            top: resizeHelperY - deltaY\n          });\n        }\n        if (!resizeStarted) {\n          fireObjectResizeStart(editor, selectedElm, startW, startH, 'corner-' + selectedHandle.name);\n          resizeStarted = true;\n        }\n      };\n      const endGhostResize = () => {\n        const wasResizeStarted = resizeStarted;\n        resizeStarted = false;\n        if (wasResizeStarted) {\n          setSizeProp(selectedElm, 'width', width);\n          setSizeProp(selectedElm, 'height', height);\n        }\n        dom.unbind(editableDoc, 'mousemove', resizeGhostElement);\n        dom.unbind(editableDoc, 'mouseup', endGhostResize);\n        if (rootDocument !== editableDoc) {\n          dom.unbind(rootDocument, 'mousemove', resizeGhostElement);\n          dom.unbind(rootDocument, 'mouseup', endGhostResize);\n        }\n        dom.remove(selectedElmGhost);\n        dom.remove(resizeHelper);\n        dom.remove(resizeBackdrop);\n        showResizeRect(selectedElm);\n        if (wasResizeStarted) {\n          fireObjectResized(editor, selectedElm, width, height, 'corner-' + selectedHandle.name);\n          dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style'));\n        }\n        editor.nodeChanged();\n      };\n      const showResizeRect = targetElm => {\n        unbindResizeHandleEvents();\n        const position = dom.getPos(targetElm, rootElement);\n        const selectedElmX = position.x;\n        const selectedElmY = position.y;\n        const rect = targetElm.getBoundingClientRect();\n        const targetWidth = rect.width || rect.right - rect.left;\n        const targetHeight = rect.height || rect.bottom - rect.top;\n        if (selectedElm !== targetElm) {\n          hideResizeRect();\n          selectedElm = targetElm;\n          width = height = 0;\n        }\n        const e = editor.dispatch('ObjectSelected', { target: targetElm });\n        if (isResizable(targetElm) && !e.isDefaultPrevented()) {\n          each$d(resizeHandles, (handle, name) => {\n            const startDrag = e => {\n              const target = getResizeTargets(selectedElm)[0];\n              startX = e.screenX;\n              startY = e.screenY;\n              startW = target.clientWidth;\n              startH = target.clientHeight;\n              ratio = startH / startW;\n              selectedHandle = handle;\n              selectedHandle.name = name;\n              selectedHandle.startPos = {\n                x: targetWidth * handle[0] + selectedElmX,\n                y: targetHeight * handle[1] + selectedElmY\n              };\n              startScrollWidth = rootElement.scrollWidth;\n              startScrollHeight = rootElement.scrollHeight;\n              resizeBackdrop = dom.add(rootElement, 'div', {\n                'class': 'mce-resize-backdrop',\n                'data-mce-bogus': 'all'\n              });\n              dom.setStyles(resizeBackdrop, {\n                position: 'fixed',\n                left: '0',\n                top: '0',\n                width: '100%',\n                height: '100%'\n              });\n              selectedElmGhost = createGhostElement(selectedElm);\n              dom.addClass(selectedElmGhost, 'mce-clonedresizable');\n              dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all');\n              selectedElmGhost.contentEditable = 'false';\n              dom.setStyles(selectedElmGhost, {\n                left: selectedElmX,\n                top: selectedElmY,\n                margin: 0\n              });\n              setGhostElmSize(selectedElmGhost, targetWidth, targetHeight);\n              selectedElmGhost.removeAttribute(elementSelectionAttr);\n              rootElement.appendChild(selectedElmGhost);\n              dom.bind(editableDoc, 'mousemove', resizeGhostElement);\n              dom.bind(editableDoc, 'mouseup', endGhostResize);\n              if (rootDocument !== editableDoc) {\n                dom.bind(rootDocument, 'mousemove', resizeGhostElement);\n                dom.bind(rootDocument, 'mouseup', endGhostResize);\n              }\n              resizeHelper = dom.add(rootElement, 'div', {\n                'class': 'mce-resize-helper',\n                'data-mce-bogus': 'all'\n              }, startW + ' &times; ' + startH);\n            };\n            let handleElm = dom.get('mceResizeHandle' + name);\n            if (handleElm) {\n              dom.remove(handleElm);\n            }\n            handleElm = dom.add(rootElement, 'div', {\n              'id': 'mceResizeHandle' + name,\n              'data-mce-bogus': 'all',\n              'class': 'mce-resizehandle',\n              'unselectable': true,\n              'style': 'cursor:' + name + '-resize; margin:0; padding:0'\n            });\n            dom.bind(handleElm, 'mousedown', e => {\n              e.stopImmediatePropagation();\n              e.preventDefault();\n              startDrag(e);\n            });\n            handle.elm = handleElm;\n            dom.setStyles(handleElm, {\n              left: targetWidth * handle[0] + selectedElmX - handleElm.offsetWidth / 2,\n              top: targetHeight * handle[1] + selectedElmY - handleElm.offsetHeight / 2\n            });\n          });\n        } else {\n          hideResizeRect(false);\n        }\n      };\n      const throttledShowResizeRect = first$1(showResizeRect, 0);\n      const hideResizeRect = (removeSelected = true) => {\n        throttledShowResizeRect.cancel();\n        unbindResizeHandleEvents();\n        if (selectedElm && removeSelected) {\n          selectedElm.removeAttribute(elementSelectionAttr);\n        }\n        each$d(resizeHandles, (value, name) => {\n          const handleElm = dom.get('mceResizeHandle' + name);\n          if (handleElm) {\n            dom.unbind(handleElm);\n            dom.remove(handleElm);\n          }\n        });\n      };\n      const isChildOrEqual = (node, parent) => dom.isChildOf(node, parent);\n      const updateResizeRect = e => {\n        if (resizeStarted || editor.removed || editor.composing) {\n          return;\n        }\n        const targetElm = e.type === 'mousedown' ? e.target : selection.getNode();\n        const controlElm = closest$3(SugarElement.fromDom(targetElm), controlElmSelector).map(e => e.dom).getOrUndefined();\n        const selectedValue = isNonNullable(controlElm) ? dom.getAttrib(controlElm, elementSelectionAttr, '1') : '1';\n        each$e(dom.select(`img[${ elementSelectionAttr }],hr[${ elementSelectionAttr }]`), img => {\n          img.removeAttribute(elementSelectionAttr);\n        });\n        if (isNonNullable(controlElm) && isChildOrEqual(controlElm, rootElement)) {\n          disableGeckoResize();\n          const startElm = selection.getStart(true);\n          if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {\n            dom.setAttrib(controlElm, elementSelectionAttr, selectedValue);\n            throttledShowResizeRect.throttle(controlElm);\n            return;\n          }\n        }\n        hideResizeRect();\n      };\n      const unbindResizeHandleEvents = () => {\n        each$d(resizeHandles, handle => {\n          if (handle.elm) {\n            dom.unbind(handle.elm);\n            delete handle.elm;\n          }\n        });\n      };\n      const disableGeckoResize = () => {\n        try {\n          editor.getDoc().execCommand('enableObjectResizing', false, 'false');\n        } catch (ex) {\n        }\n      };\n      editor.on('init', () => {\n        disableGeckoResize();\n        editor.on('NodeChange ResizeEditor ResizeWindow ResizeContent drop', updateResizeRect);\n        editor.on('keyup compositionend', e => {\n          if (selectedElm && selectedElm.nodeName === 'TABLE') {\n            updateResizeRect(e);\n          }\n        });\n        editor.on('hide blur', hideResizeRect);\n        editor.on('contextmenu longpress', contextMenuSelectImage, true);\n      });\n      editor.on('remove', unbindResizeHandleEvents);\n      const destroy = () => {\n        throttledShowResizeRect.cancel();\n        selectedElm = selectedElmGhost = resizeBackdrop = null;\n      };\n      return {\n        isResizable,\n        showResizeRect,\n        hideResizeRect,\n        updateResizeRect,\n        destroy\n      };\n    };\n\n    const setStart = (rng, situ) => {\n      situ.fold(e => {\n        rng.setStartBefore(e.dom);\n      }, (e, o) => {\n        rng.setStart(e.dom, o);\n      }, e => {\n        rng.setStartAfter(e.dom);\n      });\n    };\n    const setFinish = (rng, situ) => {\n      situ.fold(e => {\n        rng.setEndBefore(e.dom);\n      }, (e, o) => {\n        rng.setEnd(e.dom, o);\n      }, e => {\n        rng.setEndAfter(e.dom);\n      });\n    };\n    const relativeToNative = (win, startSitu, finishSitu) => {\n      const range = win.document.createRange();\n      setStart(range, startSitu);\n      setFinish(range, finishSitu);\n      return range;\n    };\n    const exactToNative = (win, start, soffset, finish, foffset) => {\n      const rng = win.document.createRange();\n      rng.setStart(start.dom, soffset);\n      rng.setEnd(finish.dom, foffset);\n      return rng;\n    };\n\n    const adt$3 = Adt.generate([\n      {\n        ltr: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      },\n      {\n        rtl: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      }\n    ]);\n    const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);\n    const getRanges = (win, selection) => selection.match({\n      domRange: rng => {\n        return {\n          ltr: constant(rng),\n          rtl: Optional.none\n        };\n      },\n      relative: (startSitu, finishSitu) => {\n        return {\n          ltr: cached(() => relativeToNative(win, startSitu, finishSitu)),\n          rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu)))\n        };\n      },\n      exact: (start, soffset, finish, foffset) => {\n        return {\n          ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),\n          rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, soffset)))\n        };\n      }\n    });\n    const doDiagnose = (win, ranges) => {\n      const rng = ranges.ltr();\n      if (rng.collapsed) {\n        const reversed = ranges.rtl().filter(rev => rev.collapsed === false);\n        return reversed.map(rev => adt$3.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)).getOrThunk(() => fromRange(win, adt$3.ltr, rng));\n      } else {\n        return fromRange(win, adt$3.ltr, rng);\n      }\n    };\n    const diagnose = (win, selection) => {\n      const ranges = getRanges(win, selection);\n      return doDiagnose(win, ranges);\n    };\n    adt$3.ltr;\n    adt$3.rtl;\n\n    const create$a = (start, soffset, finish, foffset) => ({\n      start,\n      soffset,\n      finish,\n      foffset\n    });\n    const SimRange = { create: create$a };\n\n    const caretPositionFromPoint = (doc, x, y) => {\n      var _a, _b;\n      return Optional.from((_b = (_a = doc.dom).caretPositionFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)).bind(pos => {\n        if (pos.offsetNode === null) {\n          return Optional.none();\n        }\n        const r = doc.dom.createRange();\n        r.setStart(pos.offsetNode, pos.offset);\n        r.collapse();\n        return Optional.some(r);\n      });\n    };\n    const caretRangeFromPoint = (doc, x, y) => {\n      var _a, _b;\n      return Optional.from((_b = (_a = doc.dom).caretRangeFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y));\n    };\n    const availableSearch = (() => {\n      if (document.caretPositionFromPoint) {\n        return caretPositionFromPoint;\n      } else if (document.caretRangeFromPoint) {\n        return caretRangeFromPoint;\n      } else {\n        return Optional.none;\n      }\n    })();\n    const fromPoint$1 = (win, x, y) => {\n      const doc = SugarElement.fromDom(win.document);\n      return availableSearch(doc, x, y).map(rng => SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));\n    };\n\n    const adt$2 = Adt.generate([\n      { before: ['element'] },\n      {\n        on: [\n          'element',\n          'offset'\n        ]\n      },\n      { after: ['element'] }\n    ]);\n    const cata = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);\n    const getStart$2 = situ => situ.fold(identity, identity, identity);\n    const before$1 = adt$2.before;\n    const on = adt$2.on;\n    const after$1 = adt$2.after;\n    const Situ = {\n      before: before$1,\n      on,\n      after: after$1,\n      cata,\n      getStart: getStart$2\n    };\n\n    const adt$1 = Adt.generate([\n      { domRange: ['rng'] },\n      {\n        relative: [\n          'startSitu',\n          'finishSitu'\n        ]\n      },\n      {\n        exact: [\n          'start',\n          'soffset',\n          'finish',\n          'foffset'\n        ]\n      }\n    ]);\n    const exactFromRange = simRange => adt$1.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);\n    const getStart$1 = selection => selection.match({\n      domRange: rng => SugarElement.fromDom(rng.startContainer),\n      relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),\n      exact: (start, _soffset, _finish, _foffset) => start\n    });\n    const domRange = adt$1.domRange;\n    const relative = adt$1.relative;\n    const exact = adt$1.exact;\n    const getWin = selection => {\n      const start = getStart$1(selection);\n      return defaultView(start);\n    };\n    const range = SimRange.create;\n    const SimSelection = {\n      domRange,\n      relative,\n      exact,\n      exactFromRange,\n      getWin,\n      range\n    };\n\n    const beforeSpecial = (element, offset) => {\n      const name$1 = name(element);\n      if ('input' === name$1) {\n        return Situ.after(element);\n      } else if (!contains$2([\n          'br',\n          'img'\n        ], name$1)) {\n        return Situ.on(element, offset);\n      } else {\n        return offset === 0 ? Situ.before(element) : Situ.after(element);\n      }\n    };\n    const preprocessRelative = (startSitu, finishSitu) => {\n      const start = startSitu.fold(Situ.before, beforeSpecial, Situ.after);\n      const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after);\n      return SimSelection.relative(start, finish);\n    };\n    const preprocessExact = (start, soffset, finish, foffset) => {\n      const startSitu = beforeSpecial(start, soffset);\n      const finishSitu = beforeSpecial(finish, foffset);\n      return SimSelection.relative(startSitu, finishSitu);\n    };\n    const preprocess = selection => selection.match({\n      domRange: rng => {\n        const start = SugarElement.fromDom(rng.startContainer);\n        const finish = SugarElement.fromDom(rng.endContainer);\n        return preprocessExact(start, rng.startOffset, finish, rng.endOffset);\n      },\n      relative: preprocessRelative,\n      exact: preprocessExact\n    });\n\n    const fromElements = (elements, scope) => {\n      const doc = scope || document;\n      const fragment = doc.createDocumentFragment();\n      each$e(elements, element => {\n        fragment.appendChild(element.dom);\n      });\n      return SugarElement.fromDom(fragment);\n    };\n\n    const toNative = selection => {\n      const win = SimSelection.getWin(selection).dom;\n      const getDomRange = (start, soffset, finish, foffset) => exactToNative(win, start, soffset, finish, foffset);\n      const filtered = preprocess(selection);\n      return diagnose(win, filtered).match({\n        ltr: getDomRange,\n        rtl: getDomRange\n      });\n    };\n    const getAtPoint = (win, x, y) => fromPoint$1(win, x, y);\n\n    const fromPoint = (clientX, clientY, doc) => {\n      const win = defaultView(SugarElement.fromDom(doc));\n      return getAtPoint(win.dom, clientX, clientY).map(simRange => {\n        const rng = doc.createRange();\n        rng.setStart(simRange.start.dom, simRange.soffset);\n        rng.setEnd(simRange.finish.dom, simRange.foffset);\n        return rng;\n      }).getOrUndefined();\n    };\n\n    const isEq$4 = (rng1, rng2) => {\n      return isNonNullable(rng1) && isNonNullable(rng2) && (rng1.startContainer === rng2.startContainer && rng1.startOffset === rng2.startOffset) && (rng1.endContainer === rng2.endContainer && rng1.endOffset === rng2.endOffset);\n    };\n\n    const findParent = (node, rootNode, predicate) => {\n      let currentNode = node;\n      while (currentNode && currentNode !== rootNode) {\n        if (predicate(currentNode)) {\n          return currentNode;\n        }\n        currentNode = currentNode.parentNode;\n      }\n      return null;\n    };\n    const hasParent$1 = (node, rootNode, predicate) => findParent(node, rootNode, predicate) !== null;\n    const hasParentWithName = (node, rootNode, name) => hasParent$1(node, rootNode, node => node.nodeName === name);\n    const isCeFalseCaretContainer = (node, rootNode) => isCaretContainer$2(node) && !hasParent$1(node, rootNode, isCaretNode);\n    const hasBrBeforeAfter = (dom, node, left) => {\n      const parentNode = node.parentNode;\n      if (parentNode) {\n        const walker = new DomTreeWalker(node, dom.getParent(parentNode, dom.isBlock) || dom.getRoot());\n        let currentNode;\n        while (currentNode = walker[left ? 'prev' : 'next']()) {\n          if (isBr$6(currentNode)) {\n            return true;\n          }\n        }\n      }\n      return false;\n    };\n    const isPrevNode = (node, name) => {\n      var _a;\n      return ((_a = node.previousSibling) === null || _a === void 0 ? void 0 : _a.nodeName) === name;\n    };\n    const hasContentEditableFalseParent = (root, node) => {\n      let currentNode = node;\n      while (currentNode && currentNode !== root) {\n        if (isContentEditableFalse$a(currentNode)) {\n          return true;\n        }\n        currentNode = currentNode.parentNode;\n      }\n      return false;\n    };\n    const findTextNodeRelative = (dom, isAfterNode, collapsed, left, startNode) => {\n      const body = dom.getRoot();\n      const nonEmptyElementsMap = dom.schema.getNonEmptyElements();\n      const parentNode = startNode.parentNode;\n      let lastInlineElement;\n      let node;\n      if (!parentNode) {\n        return Optional.none();\n      }\n      const parentBlockContainer = dom.getParent(parentNode, dom.isBlock) || body;\n      if (left && isBr$6(startNode) && isAfterNode && dom.isEmpty(parentBlockContainer)) {\n        return Optional.some(CaretPosition(parentNode, dom.nodeIndex(startNode)));\n      }\n      const walker = new DomTreeWalker(startNode, parentBlockContainer);\n      while (node = walker[left ? 'prev' : 'next']()) {\n        if (dom.getContentEditableParent(node) === 'false' || isCeFalseCaretContainer(node, body)) {\n          return Optional.none();\n        }\n        if (isText$a(node) && node.data.length > 0) {\n          if (!hasParentWithName(node, body, 'A')) {\n            return Optional.some(CaretPosition(node, left ? node.data.length : 0));\n          }\n          return Optional.none();\n        }\n        if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {\n          return Optional.none();\n        }\n        lastInlineElement = node;\n      }\n      if (isComment(lastInlineElement)) {\n        return Optional.none();\n      }\n      if (collapsed && lastInlineElement) {\n        return Optional.some(CaretPosition(lastInlineElement, 0));\n      }\n      return Optional.none();\n    };\n    const normalizeEndPoint = (dom, collapsed, start, rng) => {\n      const body = dom.getRoot();\n      let node;\n      let normalized = false;\n      let container = start ? rng.startContainer : rng.endContainer;\n      let offset = start ? rng.startOffset : rng.endOffset;\n      const isAfterNode = isElement$6(container) && offset === container.childNodes.length;\n      const nonEmptyElementsMap = dom.schema.getNonEmptyElements();\n      let directionLeft = start;\n      if (isCaretContainer$2(container)) {\n        return Optional.none();\n      }\n      if (isElement$6(container) && offset > container.childNodes.length - 1) {\n        directionLeft = false;\n      }\n      if (isDocument$1(container)) {\n        container = body;\n        offset = 0;\n      }\n      if (container === body) {\n        if (directionLeft) {\n          node = container.childNodes[offset > 0 ? offset - 1 : 0];\n          if (node) {\n            if (isCaretContainer$2(node)) {\n              return Optional.none();\n            }\n            if (nonEmptyElementsMap[node.nodeName] || isTable$2(node)) {\n              return Optional.none();\n            }\n          }\n        }\n        if (container.hasChildNodes()) {\n          offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);\n          container = container.childNodes[offset];\n          offset = isText$a(container) && isAfterNode ? container.data.length : 0;\n          if (!collapsed && container === body.lastChild && isTable$2(container)) {\n            return Optional.none();\n          }\n          if (hasContentEditableFalseParent(body, container) || isCaretContainer$2(container)) {\n            return Optional.none();\n          }\n          if (container.hasChildNodes() && !isTable$2(container)) {\n            node = container;\n            const walker = new DomTreeWalker(container, body);\n            do {\n              if (isContentEditableFalse$a(node) || isCaretContainer$2(node)) {\n                normalized = false;\n                break;\n              }\n              if (isText$a(node) && node.data.length > 0) {\n                offset = directionLeft ? 0 : node.data.length;\n                container = node;\n                normalized = true;\n                break;\n              }\n              if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCellOrCaption(node)) {\n                offset = dom.nodeIndex(node);\n                container = node.parentNode;\n                if (!directionLeft) {\n                  offset++;\n                }\n                normalized = true;\n                break;\n              }\n            } while (node = directionLeft ? walker.next() : walker.prev());\n          }\n        }\n      }\n      if (collapsed) {\n        if (isText$a(container) && offset === 0) {\n          findTextNodeRelative(dom, isAfterNode, collapsed, true, container).each(pos => {\n            container = pos.container();\n            offset = pos.offset();\n            normalized = true;\n          });\n        }\n        if (isElement$6(container)) {\n          node = container.childNodes[offset];\n          if (!node) {\n            node = container.childNodes[offset - 1];\n          }\n          if (node && isBr$6(node) && !isPrevNode(node, 'A') && !hasBrBeforeAfter(dom, node, false) && !hasBrBeforeAfter(dom, node, true)) {\n            findTextNodeRelative(dom, isAfterNode, collapsed, true, node).each(pos => {\n              container = pos.container();\n              offset = pos.offset();\n              normalized = true;\n            });\n          }\n        }\n      }\n      if (directionLeft && !collapsed && isText$a(container) && offset === container.data.length) {\n        findTextNodeRelative(dom, isAfterNode, collapsed, false, container).each(pos => {\n          container = pos.container();\n          offset = pos.offset();\n          normalized = true;\n        });\n      }\n      return normalized && container ? Optional.some(CaretPosition(container, offset)) : Optional.none();\n    };\n    const normalize$2 = (dom, rng) => {\n      const collapsed = rng.collapsed, normRng = rng.cloneRange();\n      const startPos = CaretPosition.fromRangeStart(rng);\n      normalizeEndPoint(dom, collapsed, true, normRng).each(pos => {\n        if (!collapsed || !CaretPosition.isAbove(startPos, pos)) {\n          normRng.setStart(pos.container(), pos.offset());\n        }\n      });\n      if (!collapsed) {\n        normalizeEndPoint(dom, collapsed, false, normRng).each(pos => {\n          normRng.setEnd(pos.container(), pos.offset());\n        });\n      }\n      if (collapsed) {\n        normRng.collapse(true);\n      }\n      return isEq$4(rng, normRng) ? Optional.none() : Optional.some(normRng);\n    };\n\n    const splitText = (node, offset) => {\n      return node.splitText(offset);\n    };\n    const split = rng => {\n      let startContainer = rng.startContainer, startOffset = rng.startOffset, endContainer = rng.endContainer, endOffset = rng.endOffset;\n      if (startContainer === endContainer && isText$a(startContainer)) {\n        if (startOffset > 0 && startOffset < startContainer.data.length) {\n          endContainer = splitText(startContainer, startOffset);\n          startContainer = endContainer.previousSibling;\n          if (endOffset > startOffset) {\n            endOffset = endOffset - startOffset;\n            const newContainer = splitText(endContainer, endOffset).previousSibling;\n            startContainer = endContainer = newContainer;\n            endOffset = newContainer.data.length;\n            startOffset = 0;\n          } else {\n            endOffset = 0;\n          }\n        }\n      } else {\n        if (isText$a(startContainer) && startOffset > 0 && startOffset < startContainer.data.length) {\n          startContainer = splitText(startContainer, startOffset);\n          startOffset = 0;\n        }\n        if (isText$a(endContainer) && endOffset > 0 && endOffset < endContainer.data.length) {\n          const newContainer = splitText(endContainer, endOffset).previousSibling;\n          endContainer = newContainer;\n          endOffset = newContainer.data.length;\n        }\n      }\n      return {\n        startContainer,\n        startOffset,\n        endContainer,\n        endOffset\n      };\n    };\n\n    const RangeUtils = dom => {\n      const walk = (rng, callback) => {\n        return walk$3(dom, rng, callback);\n      };\n      const split$1 = split;\n      const normalize = rng => {\n        return normalize$2(dom, rng).fold(never, normalizedRng => {\n          rng.setStart(normalizedRng.startContainer, normalizedRng.startOffset);\n          rng.setEnd(normalizedRng.endContainer, normalizedRng.endOffset);\n          return true;\n        });\n      };\n      const expand = (rng, options = { type: 'word' }) => {\n        if (options.type === 'word') {\n          const rangeLike = expandRng(dom, rng, [{ inline: 'span' }]);\n          const newRange = dom.createRng();\n          newRange.setStart(rangeLike.startContainer, rangeLike.startOffset);\n          newRange.setEnd(rangeLike.endContainer, rangeLike.endOffset);\n          return newRange;\n        }\n        return rng;\n      };\n      return {\n        walk,\n        split: split$1,\n        expand,\n        normalize\n      };\n    };\n    RangeUtils.compareRanges = isEq$4;\n    RangeUtils.getCaretRangeFromPoint = fromPoint;\n    RangeUtils.getSelectedNode = getSelectedNode;\n    RangeUtils.getNode = getNode$1;\n\n    const Dimension = (name, getOffset) => {\n      const set = (element, h) => {\n        if (!isNumber(h) && !h.match(/^[0-9]+$/)) {\n          throw new Error(name + '.set accepts only positive integer values. Value was ' + h);\n        }\n        const dom = element.dom;\n        if (isSupported$1(dom)) {\n          dom.style[name] = h + 'px';\n        }\n      };\n      const get = element => {\n        const r = getOffset(element);\n        if (r <= 0 || r === null) {\n          const css = get$7(element, name);\n          return parseFloat(css) || 0;\n        }\n        return r;\n      };\n      const getOuter = get;\n      const aggregate = (element, properties) => foldl(properties, (acc, property) => {\n        const val = get$7(element, property);\n        const value = val === undefined ? 0 : parseInt(val, 10);\n        return isNaN(value) ? acc : acc + value;\n      }, 0);\n      const max = (element, value, properties) => {\n        const cumulativeInclusions = aggregate(element, properties);\n        const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;\n        return absoluteMax;\n      };\n      return {\n        set,\n        get,\n        getOuter,\n        aggregate,\n        max\n      };\n    };\n\n    const api = Dimension('height', element => {\n      const dom = element.dom;\n      return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;\n    });\n    const get$2 = element => api.get(element);\n\n    const getDocument = () => SugarElement.fromDom(document);\n\n    const walkUp = (navigation, doc) => {\n      const frame = navigation.view(doc);\n      return frame.fold(constant([]), f => {\n        const parent = navigation.owner(f);\n        const rest = walkUp(navigation, parent);\n        return [f].concat(rest);\n      });\n    };\n    const pathTo = (element, navigation) => {\n      const d = navigation.owner(element);\n      return walkUp(navigation, d);\n    };\n\n    const view = doc => {\n      var _a;\n      const element = doc.dom === document ? Optional.none() : Optional.from((_a = doc.dom.defaultView) === null || _a === void 0 ? void 0 : _a.frameElement);\n      return element.map(SugarElement.fromDom);\n    };\n    const owner = element => documentOrOwner(element);\n\n    var Navigation = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        view: view,\n        owner: owner\n    });\n\n    const find = element => {\n      const doc = getDocument();\n      const scroll = get$5(doc);\n      const frames = pathTo(element, Navigation);\n      const offset = viewport(element);\n      const r = foldr(frames, (b, a) => {\n        const loc = viewport(a);\n        return {\n          left: b.left + loc.left,\n          top: b.top + loc.top\n        };\n      }, {\n        left: 0,\n        top: 0\n      });\n      return SugarPosition(r.left + offset.left + scroll.left, r.top + offset.top + scroll.top);\n    };\n\n    const excludeFromDescend = element => name(element) === 'textarea';\n    const fireScrollIntoViewEvent = (editor, data) => {\n      const scrollEvent = editor.dispatch('ScrollIntoView', data);\n      return scrollEvent.isDefaultPrevented();\n    };\n    const fireAfterScrollIntoViewEvent = (editor, data) => {\n      editor.dispatch('AfterScrollIntoView', data);\n    };\n    const descend = (element, offset) => {\n      const children = children$1(element);\n      if (children.length === 0 || excludeFromDescend(element)) {\n        return {\n          element,\n          offset\n        };\n      } else if (offset < children.length && !excludeFromDescend(children[offset])) {\n        return {\n          element: children[offset],\n          offset: 0\n        };\n      } else {\n        const last = children[children.length - 1];\n        if (excludeFromDescend(last)) {\n          return {\n            element,\n            offset\n          };\n        } else {\n          if (name(last) === 'img') {\n            return {\n              element: last,\n              offset: 1\n            };\n          } else if (isText$b(last)) {\n            return {\n              element: last,\n              offset: get$3(last).length\n            };\n          } else {\n            return {\n              element: last,\n              offset: children$1(last).length\n            };\n          }\n        }\n      }\n    };\n    const markerInfo = (element, cleanupFun) => {\n      const pos = absolute(element);\n      const height = get$2(element);\n      return {\n        element,\n        bottom: pos.top + height,\n        height,\n        pos,\n        cleanup: cleanupFun\n      };\n    };\n    const createMarker$1 = (element, offset) => {\n      const startPoint = descend(element, offset);\n      const span = SugarElement.fromHtml('<span data-mce-bogus=\"all\" style=\"display: inline-block;\">' + ZWSP$1 + '</span>');\n      before$3(startPoint.element, span);\n      return markerInfo(span, () => remove$6(span));\n    };\n    const elementMarker = element => markerInfo(SugarElement.fromDom(element), noop);\n    const withMarker = (editor, f, rng, alignToTop) => {\n      preserveWith(editor, (_s, _e) => applyWithMarker(editor, f, rng, alignToTop), rng);\n    };\n    const withScrollEvents = (editor, doc, f, marker, alignToTop) => {\n      const data = {\n        elm: marker.element.dom,\n        alignToTop\n      };\n      if (fireScrollIntoViewEvent(editor, data)) {\n        return;\n      }\n      const scrollTop = get$5(doc).top;\n      f(doc, scrollTop, marker, alignToTop);\n      fireAfterScrollIntoViewEvent(editor, data);\n    };\n    const applyWithMarker = (editor, f, rng, alignToTop) => {\n      const body = SugarElement.fromDom(editor.getBody());\n      const doc = SugarElement.fromDom(editor.getDoc());\n      reflow(body);\n      const marker = createMarker$1(SugarElement.fromDom(rng.startContainer), rng.startOffset);\n      withScrollEvents(editor, doc, f, marker, alignToTop);\n      marker.cleanup();\n    };\n    const withElement = (editor, element, f, alignToTop) => {\n      const doc = SugarElement.fromDom(editor.getDoc());\n      withScrollEvents(editor, doc, f, elementMarker(element), alignToTop);\n    };\n    const preserveWith = (editor, f, rng) => {\n      const startElement = rng.startContainer;\n      const startOffset = rng.startOffset;\n      const endElement = rng.endContainer;\n      const endOffset = rng.endOffset;\n      f(SugarElement.fromDom(startElement), SugarElement.fromDom(endElement));\n      const newRng = editor.dom.createRng();\n      newRng.setStart(startElement, startOffset);\n      newRng.setEnd(endElement, endOffset);\n      editor.selection.setRng(rng);\n    };\n    const scrollToMarker = (marker, viewHeight, alignToTop, doc) => {\n      const pos = marker.pos;\n      if (alignToTop) {\n        to(pos.left, pos.top, doc);\n      } else {\n        const y = pos.top - viewHeight + marker.height;\n        to(pos.left, y, doc);\n      }\n    };\n    const intoWindowIfNeeded = (doc, scrollTop, viewHeight, marker, alignToTop) => {\n      const viewportBottom = viewHeight + scrollTop;\n      const markerTop = marker.pos.top;\n      const markerBottom = marker.bottom;\n      const largerThanViewport = markerBottom - markerTop >= viewHeight;\n      if (markerTop < scrollTop) {\n        scrollToMarker(marker, viewHeight, alignToTop !== false, doc);\n      } else if (markerTop > viewportBottom) {\n        const align = largerThanViewport ? alignToTop !== false : alignToTop === true;\n        scrollToMarker(marker, viewHeight, align, doc);\n      } else if (markerBottom > viewportBottom && !largerThanViewport) {\n        scrollToMarker(marker, viewHeight, alignToTop === true, doc);\n      }\n    };\n    const intoWindow = (doc, scrollTop, marker, alignToTop) => {\n      const viewHeight = defaultView(doc).dom.innerHeight;\n      intoWindowIfNeeded(doc, scrollTop, viewHeight, marker, alignToTop);\n    };\n    const intoFrame = (doc, scrollTop, marker, alignToTop) => {\n      const frameViewHeight = defaultView(doc).dom.innerHeight;\n      intoWindowIfNeeded(doc, scrollTop, frameViewHeight, marker, alignToTop);\n      const op = find(marker.element);\n      const viewportBounds = getBounds(window);\n      if (op.top < viewportBounds.y) {\n        intoView(marker.element, alignToTop !== false);\n      } else if (op.top > viewportBounds.bottom) {\n        intoView(marker.element, alignToTop === true);\n      }\n    };\n    const rangeIntoWindow = (editor, rng, alignToTop) => withMarker(editor, intoWindow, rng, alignToTop);\n    const elementIntoWindow = (editor, element, alignToTop) => withElement(editor, element, intoWindow, alignToTop);\n    const rangeIntoFrame = (editor, rng, alignToTop) => withMarker(editor, intoFrame, rng, alignToTop);\n    const elementIntoFrame = (editor, element, alignToTop) => withElement(editor, element, intoFrame, alignToTop);\n    const scrollElementIntoView = (editor, element, alignToTop) => {\n      const scroller = editor.inline ? elementIntoWindow : elementIntoFrame;\n      scroller(editor, element, alignToTop);\n    };\n    const scrollRangeIntoView = (editor, rng, alignToTop) => {\n      const scroller = editor.inline ? rangeIntoWindow : rangeIntoFrame;\n      scroller(editor, rng, alignToTop);\n    };\n\n    const focus$1 = element => element.dom.focus();\n    const hasFocus$1 = element => {\n      const root = getRootNode(element).dom;\n      return element.dom === root.activeElement;\n    };\n    const active$1 = (root = getDocument()) => Optional.from(root.dom.activeElement).map(SugarElement.fromDom);\n    const search = element => active$1(getRootNode(element)).filter(e => element.dom.contains(e.dom));\n\n    const clamp$1 = (offset, element) => {\n      const max = isText$b(element) ? get$3(element).length : children$1(element).length + 1;\n      if (offset > max) {\n        return max;\n      } else if (offset < 0) {\n        return 0;\n      }\n      return offset;\n    };\n    const normalizeRng = rng => SimSelection.range(rng.start, clamp$1(rng.soffset, rng.start), rng.finish, clamp$1(rng.foffset, rng.finish));\n    const isOrContains = (root, elm) => !isRestrictedNode(elm.dom) && (contains(root, elm) || eq(root, elm));\n    const isRngInRoot = root => rng => isOrContains(root, rng.start) && isOrContains(root, rng.finish);\n    const shouldStore = editor => editor.inline || Env.browser.isFirefox();\n    const nativeRangeToSelectionRange = r => SimSelection.range(SugarElement.fromDom(r.startContainer), r.startOffset, SugarElement.fromDom(r.endContainer), r.endOffset);\n    const readRange = win => {\n      const selection = win.getSelection();\n      const rng = !selection || selection.rangeCount === 0 ? Optional.none() : Optional.from(selection.getRangeAt(0));\n      return rng.map(nativeRangeToSelectionRange);\n    };\n    const getBookmark = root => {\n      const win = defaultView(root);\n      return readRange(win.dom).filter(isRngInRoot(root));\n    };\n    const validate = (root, bookmark) => Optional.from(bookmark).filter(isRngInRoot(root)).map(normalizeRng);\n    const bookmarkToNativeRng = bookmark => {\n      const rng = document.createRange();\n      try {\n        rng.setStart(bookmark.start.dom, bookmark.soffset);\n        rng.setEnd(bookmark.finish.dom, bookmark.foffset);\n        return Optional.some(rng);\n      } catch (_) {\n        return Optional.none();\n      }\n    };\n    const store = editor => {\n      const newBookmark = shouldStore(editor) ? getBookmark(SugarElement.fromDom(editor.getBody())) : Optional.none();\n      editor.bookmark = newBookmark.isSome() ? newBookmark : editor.bookmark;\n    };\n    const getRng = editor => {\n      const bookmark = editor.bookmark ? editor.bookmark : Optional.none();\n      return bookmark.bind(x => validate(SugarElement.fromDom(editor.getBody()), x)).bind(bookmarkToNativeRng);\n    };\n    const restore = editor => {\n      getRng(editor).each(rng => editor.selection.setRng(rng));\n    };\n\n    const isEditorUIElement$1 = elm => {\n      const className = elm.className.toString();\n      return className.indexOf('tox-') !== -1 || className.indexOf('mce-') !== -1;\n    };\n    const FocusManager = { isEditorUIElement: isEditorUIElement$1 };\n\n    const wrappedSetTimeout = (callback, time) => {\n      if (!isNumber(time)) {\n        time = 0;\n      }\n      return setTimeout(callback, time);\n    };\n    const wrappedSetInterval = (callback, time) => {\n      if (!isNumber(time)) {\n        time = 0;\n      }\n      return setInterval(callback, time);\n    };\n    const Delay = {\n      setEditorTimeout: (editor, callback, time) => {\n        return wrappedSetTimeout(() => {\n          if (!editor.removed) {\n            callback();\n          }\n        }, time);\n      },\n      setEditorInterval: (editor, callback, time) => {\n        const timer = wrappedSetInterval(() => {\n          if (!editor.removed) {\n            callback();\n          } else {\n            clearInterval(timer);\n          }\n        }, time);\n        return timer;\n      }\n    };\n\n    const isManualNodeChange = e => {\n      return e.type === 'nodechange' && e.selectionChange;\n    };\n    const registerPageMouseUp = (editor, throttledStore) => {\n      const mouseUpPage = () => {\n        throttledStore.throttle();\n      };\n      DOMUtils.DOM.bind(document, 'mouseup', mouseUpPage);\n      editor.on('remove', () => {\n        DOMUtils.DOM.unbind(document, 'mouseup', mouseUpPage);\n      });\n    };\n    const registerMouseUp = (editor, throttledStore) => {\n      editor.on('mouseup touchend', _e => {\n        throttledStore.throttle();\n      });\n    };\n    const registerEditorEvents = (editor, throttledStore) => {\n      registerMouseUp(editor, throttledStore);\n      editor.on('keyup NodeChange AfterSetSelectionRange', e => {\n        if (!isManualNodeChange(e)) {\n          store(editor);\n        }\n      });\n    };\n    const register$6 = editor => {\n      const throttledStore = first$1(() => {\n        store(editor);\n      }, 0);\n      editor.on('init', () => {\n        if (editor.inline) {\n          registerPageMouseUp(editor, throttledStore);\n        }\n        registerEditorEvents(editor, throttledStore);\n      });\n      editor.on('remove', () => {\n        throttledStore.cancel();\n      });\n    };\n\n    let documentFocusInHandler;\n    const DOM$9 = DOMUtils.DOM;\n    const isEditorUIElement = elm => {\n      return isElement$6(elm) && FocusManager.isEditorUIElement(elm);\n    };\n    const isEditorContentAreaElement = elm => {\n      const classList = elm.classList;\n      if (classList !== undefined) {\n        return classList.contains('tox-edit-area') || classList.contains('tox-edit-area__iframe') || classList.contains('mce-content-body');\n      } else {\n        return false;\n      }\n    };\n    const isUIElement = (editor, elm) => {\n      const customSelector = getCustomUiSelector(editor);\n      const parent = DOM$9.getParent(elm, elm => {\n        return isEditorUIElement(elm) || (customSelector ? editor.dom.is(elm, customSelector) : false);\n      });\n      return parent !== null;\n    };\n    const getActiveElement = editor => {\n      try {\n        const root = getRootNode(SugarElement.fromDom(editor.getElement()));\n        return active$1(root).fold(() => document.body, x => x.dom);\n      } catch (ex) {\n        return document.body;\n      }\n    };\n    const registerEvents$1 = (editorManager, e) => {\n      const editor = e.editor;\n      register$6(editor);\n      editor.on('focusin', () => {\n        const focusedEditor = editorManager.focusedEditor;\n        if (focusedEditor !== editor) {\n          if (focusedEditor) {\n            focusedEditor.dispatch('blur', { focusedEditor: editor });\n          }\n          editorManager.setActive(editor);\n          editorManager.focusedEditor = editor;\n          editor.dispatch('focus', { blurredEditor: focusedEditor });\n          editor.focus(true);\n        }\n      });\n      editor.on('focusout', () => {\n        Delay.setEditorTimeout(editor, () => {\n          const focusedEditor = editorManager.focusedEditor;\n          if (!isUIElement(editor, getActiveElement(editor)) && focusedEditor === editor) {\n            editor.dispatch('blur', { focusedEditor: null });\n            editorManager.focusedEditor = null;\n          }\n        });\n      });\n      if (!documentFocusInHandler) {\n        documentFocusInHandler = e => {\n          const activeEditor = editorManager.activeEditor;\n          if (activeEditor) {\n            getOriginalEventTarget(e).each(target => {\n              const elem = target;\n              if (elem.ownerDocument === document) {\n                if (elem !== document.body && !isUIElement(activeEditor, elem) && editorManager.focusedEditor === activeEditor) {\n                  activeEditor.dispatch('blur', { focusedEditor: null });\n                  editorManager.focusedEditor = null;\n                }\n              }\n            });\n          }\n        };\n        DOM$9.bind(document, 'focusin', documentFocusInHandler);\n      }\n    };\n    const unregisterDocumentEvents = (editorManager, e) => {\n      if (editorManager.focusedEditor === e.editor) {\n        editorManager.focusedEditor = null;\n      }\n      if (!editorManager.activeEditor && documentFocusInHandler) {\n        DOM$9.unbind(document, 'focusin', documentFocusInHandler);\n        documentFocusInHandler = null;\n      }\n    };\n    const setup$v = editorManager => {\n      editorManager.on('AddEditor', curry(registerEvents$1, editorManager));\n      editorManager.on('RemoveEditor', curry(unregisterDocumentEvents, editorManager));\n    };\n\n    const getContentEditableHost = (editor, node) => editor.dom.getParent(node, node => editor.dom.getContentEditable(node) === 'true');\n    const getCollapsedNode = rng => rng.collapsed ? Optional.from(getNode$1(rng.startContainer, rng.startOffset)).map(SugarElement.fromDom) : Optional.none();\n    const getFocusInElement = (root, rng) => getCollapsedNode(rng).bind(node => {\n      if (isTableSection(node)) {\n        return Optional.some(node);\n      } else if (!contains(root, node)) {\n        return Optional.some(root);\n      } else {\n        return Optional.none();\n      }\n    });\n    const normalizeSelection = (editor, rng) => {\n      getFocusInElement(SugarElement.fromDom(editor.getBody()), rng).bind(elm => {\n        return firstPositionIn(elm.dom);\n      }).fold(() => {\n        editor.selection.normalize();\n      }, caretPos => editor.selection.setRng(caretPos.toRange()));\n    };\n    const focusBody = body => {\n      if (body.setActive) {\n        try {\n          body.setActive();\n        } catch (ex) {\n          body.focus();\n        }\n      } else {\n        body.focus();\n      }\n    };\n    const hasElementFocus = elm => hasFocus$1(elm) || search(elm).isSome();\n    const hasIframeFocus = editor => isNonNullable(editor.iframeElement) && hasFocus$1(SugarElement.fromDom(editor.iframeElement));\n    const hasInlineFocus = editor => {\n      const rawBody = editor.getBody();\n      return rawBody && hasElementFocus(SugarElement.fromDom(rawBody));\n    };\n    const hasUiFocus = editor => {\n      const dos = getRootNode(SugarElement.fromDom(editor.getElement()));\n      return active$1(dos).filter(elem => !isEditorContentAreaElement(elem.dom) && isUIElement(editor, elem.dom)).isSome();\n    };\n    const hasFocus = editor => editor.inline ? hasInlineFocus(editor) : hasIframeFocus(editor);\n    const hasEditorOrUiFocus = editor => hasFocus(editor) || hasUiFocus(editor);\n    const focusEditor = editor => {\n      const selection = editor.selection;\n      const body = editor.getBody();\n      let rng = selection.getRng();\n      editor.quirks.refreshContentEditable();\n      if (isNonNullable(editor.bookmark) && !hasFocus(editor)) {\n        getRng(editor).each(bookmarkRng => {\n          editor.selection.setRng(bookmarkRng);\n          rng = bookmarkRng;\n        });\n      }\n      const contentEditableHost = getContentEditableHost(editor, selection.getNode());\n      if (contentEditableHost && editor.dom.isChildOf(contentEditableHost, body)) {\n        focusBody(contentEditableHost);\n        normalizeSelection(editor, rng);\n        activateEditor(editor);\n        return;\n      }\n      if (!editor.inline) {\n        if (!Env.browser.isOpera()) {\n          focusBody(body);\n        }\n        editor.getWin().focus();\n      }\n      if (Env.browser.isFirefox() || editor.inline) {\n        focusBody(body);\n        normalizeSelection(editor, rng);\n      }\n      activateEditor(editor);\n    };\n    const activateEditor = editor => editor.editorManager.setActive(editor);\n    const focus = (editor, skipFocus) => {\n      if (editor.removed) {\n        return;\n      }\n      if (skipFocus) {\n        activateEditor(editor);\n      } else {\n        focusEditor(editor);\n      }\n    };\n\n    const getEndpointElement = (root, rng, start, real, resolve) => {\n      const container = start ? rng.startContainer : rng.endContainer;\n      const offset = start ? rng.startOffset : rng.endOffset;\n      return Optional.from(container).map(SugarElement.fromDom).map(elm => !real || !rng.collapsed ? child$1(elm, resolve(elm, offset)).getOr(elm) : elm).bind(elm => isElement$7(elm) ? Optional.some(elm) : parent(elm).filter(isElement$7)).map(elm => elm.dom).getOr(root);\n    };\n    const getStart = (root, rng, real = false) => getEndpointElement(root, rng, true, real, (elm, offset) => Math.min(childNodesCount(elm), offset));\n    const getEnd$1 = (root, rng, real = false) => getEndpointElement(root, rng, false, real, (elm, offset) => offset > 0 ? offset - 1 : offset);\n    const skipEmptyTextNodes = (node, forwards) => {\n      const orig = node;\n      while (node && isText$a(node) && node.length === 0) {\n        node = forwards ? node.nextSibling : node.previousSibling;\n      }\n      return node || orig;\n    };\n    const getNode = (root, rng) => {\n      if (!rng) {\n        return root;\n      }\n      let startContainer = rng.startContainer;\n      let endContainer = rng.endContainer;\n      const startOffset = rng.startOffset;\n      const endOffset = rng.endOffset;\n      let node = rng.commonAncestorContainer;\n      if (!rng.collapsed) {\n        if (startContainer === endContainer) {\n          if (endOffset - startOffset < 2) {\n            if (startContainer.hasChildNodes()) {\n              node = startContainer.childNodes[startOffset];\n            }\n          }\n        }\n        if (isText$a(startContainer) && isText$a(endContainer)) {\n          if (startContainer.length === startOffset) {\n            startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);\n          } else {\n            startContainer = startContainer.parentNode;\n          }\n          if (endOffset === 0) {\n            endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);\n          } else {\n            endContainer = endContainer.parentNode;\n          }\n          if (startContainer && startContainer === endContainer) {\n            node = startContainer;\n          }\n        }\n      }\n      const elm = isText$a(node) ? node.parentNode : node;\n      return isElement$6(elm) ? elm : root;\n    };\n    const getSelectedBlocks = (dom, rng, startElm, endElm) => {\n      const selectedBlocks = [];\n      const root = dom.getRoot();\n      const start = dom.getParent(startElm || getStart(root, rng, rng.collapsed), dom.isBlock);\n      const end = dom.getParent(endElm || getEnd$1(root, rng, rng.collapsed), dom.isBlock);\n      if (start && start !== root) {\n        selectedBlocks.push(start);\n      }\n      if (start && end && start !== end) {\n        let node = start;\n        const walker = new DomTreeWalker(start, root);\n        while ((node = walker.next()) && node !== end) {\n          if (dom.isBlock(node)) {\n            selectedBlocks.push(node);\n          }\n        }\n      }\n      if (end && start !== end && end !== root) {\n        selectedBlocks.push(end);\n      }\n      return selectedBlocks;\n    };\n    const select = (dom, node, content) => Optional.from(node).bind(node => Optional.from(node.parentNode).map(parent => {\n      const idx = dom.nodeIndex(node);\n      const rng = dom.createRng();\n      rng.setStart(parent, idx);\n      rng.setEnd(parent, idx + 1);\n      if (content) {\n        moveEndPoint(dom, rng, node, true);\n        moveEndPoint(dom, rng, node, false);\n      }\n      return rng;\n    }));\n\n    const processRanges = (editor, ranges) => map$3(ranges, range => {\n      const evt = editor.dispatch('GetSelectionRange', { range });\n      return evt.range !== range ? evt.range : range;\n    });\n\n    const getEnd = element => name(element) === 'img' ? 1 : getOption(element).fold(() => children$1(element).length, v => v.length);\n    const isTextNodeWithCursorPosition = el => getOption(el).filter(text => text.trim().length !== 0 || text.indexOf(nbsp) > -1).isSome();\n    const elementsWithCursorPosition = [\n      'img',\n      'br'\n    ];\n    const isCursorPosition = elem => {\n      const hasCursorPosition = isTextNodeWithCursorPosition(elem);\n      return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem));\n    };\n\n    const first = element => descendant$1(element, isCursorPosition);\n    const last = element => descendantRtl(element, isCursorPosition);\n    const descendantRtl = (scope, predicate) => {\n      const descend = element => {\n        const children = children$1(element);\n        for (let i = children.length - 1; i >= 0; i--) {\n          const child = children[i];\n          if (predicate(child)) {\n            return Optional.some(child);\n          }\n          const res = descend(child);\n          if (res.isSome()) {\n            return res;\n          }\n        }\n        return Optional.none();\n      };\n      return descend(scope);\n    };\n\n    const autocompleteSelector = '[data-mce-autocompleter]';\n    const create$9 = (editor, range) => {\n      if (findIn(SugarElement.fromDom(editor.getBody())).isNone()) {\n        const wrapper = SugarElement.fromHtml('<span data-mce-autocompleter=\"1\" data-mce-bogus=\"1\"></span>', editor.getDoc());\n        append$1(wrapper, SugarElement.fromDom(range.extractContents()));\n        range.insertNode(wrapper.dom);\n        parent(wrapper).each(elm => elm.dom.normalize());\n        last(wrapper).map(last => {\n          editor.selection.setCursorLocation(last.dom, getEnd(last));\n        });\n      }\n    };\n    const detect$1 = elm => closest$3(elm, autocompleteSelector);\n    const findIn = elm => descendant(elm, autocompleteSelector);\n    const remove$3 = (editor, elm) => findIn(elm).each(wrapper => {\n      const bookmark = editor.selection.getBookmark();\n      unwrap(wrapper);\n      editor.selection.moveToBookmark(bookmark);\n    });\n\n    const typeLookup = {\n      '#text': 3,\n      '#comment': 8,\n      '#cdata': 4,\n      '#pi': 7,\n      '#doctype': 10,\n      '#document-fragment': 11\n    };\n    const walk$2 = (node, root, prev) => {\n      const startName = prev ? 'lastChild' : 'firstChild';\n      const siblingName = prev ? 'prev' : 'next';\n      if (node[startName]) {\n        return node[startName];\n      }\n      if (node !== root) {\n        let sibling = node[siblingName];\n        if (sibling) {\n          return sibling;\n        }\n        for (let parent = node.parent; parent && parent !== root; parent = parent.parent) {\n          sibling = parent[siblingName];\n          if (sibling) {\n            return sibling;\n          }\n        }\n      }\n      return undefined;\n    };\n    const isEmptyTextNode = node => {\n      var _a;\n      const text = (_a = node.value) !== null && _a !== void 0 ? _a : '';\n      if (!isWhitespaceText(text)) {\n        return false;\n      }\n      const parentNode = node.parent;\n      if (parentNode && (parentNode.name !== 'span' || parentNode.attr('style')) && /^[ ]+$/.test(text)) {\n        return false;\n      }\n      return true;\n    };\n    const isNonEmptyElement = node => {\n      const isNamedAnchor = node.name === 'a' && !node.attr('href') && node.attr('id');\n      return node.attr('name') || node.attr('id') && !node.firstChild || node.attr('data-mce-bookmark') || isNamedAnchor;\n    };\n    class AstNode {\n      constructor(name, type) {\n        this.name = name;\n        this.type = type;\n        if (type === 1) {\n          this.attributes = [];\n          this.attributes.map = {};\n        }\n      }\n      static create(name, attrs) {\n        const node = new AstNode(name, typeLookup[name] || 1);\n        if (attrs) {\n          each$d(attrs, (value, attrName) => {\n            node.attr(attrName, value);\n          });\n        }\n        return node;\n      }\n      replace(node) {\n        const self = this;\n        if (node.parent) {\n          node.remove();\n        }\n        self.insert(node, self);\n        self.remove();\n        return self;\n      }\n      attr(name, value) {\n        const self = this;\n        if (!isString(name)) {\n          if (isNonNullable(name)) {\n            each$d(name, (value, key) => {\n              self.attr(key, value);\n            });\n          }\n          return self;\n        }\n        const attrs = self.attributes;\n        if (attrs) {\n          if (value !== undefined) {\n            if (value === null) {\n              if (name in attrs.map) {\n                delete attrs.map[name];\n                let i = attrs.length;\n                while (i--) {\n                  if (attrs[i].name === name) {\n                    attrs.splice(i, 1);\n                    return self;\n                  }\n                }\n              }\n              return self;\n            }\n            if (name in attrs.map) {\n              let i = attrs.length;\n              while (i--) {\n                if (attrs[i].name === name) {\n                  attrs[i].value = value;\n                  break;\n                }\n              }\n            } else {\n              attrs.push({\n                name,\n                value\n              });\n            }\n            attrs.map[name] = value;\n            return self;\n          }\n          return attrs.map[name];\n        }\n        return undefined;\n      }\n      clone() {\n        const self = this;\n        const clone = new AstNode(self.name, self.type);\n        const selfAttrs = self.attributes;\n        if (selfAttrs) {\n          const cloneAttrs = [];\n          cloneAttrs.map = {};\n          for (let i = 0, l = selfAttrs.length; i < l; i++) {\n            const selfAttr = selfAttrs[i];\n            if (selfAttr.name !== 'id') {\n              cloneAttrs[cloneAttrs.length] = {\n                name: selfAttr.name,\n                value: selfAttr.value\n              };\n              cloneAttrs.map[selfAttr.name] = selfAttr.value;\n            }\n          }\n          clone.attributes = cloneAttrs;\n        }\n        clone.value = self.value;\n        return clone;\n      }\n      wrap(wrapper) {\n        const self = this;\n        if (self.parent) {\n          self.parent.insert(wrapper, self);\n          wrapper.append(self);\n        }\n        return self;\n      }\n      unwrap() {\n        const self = this;\n        for (let node = self.firstChild; node;) {\n          const next = node.next;\n          self.insert(node, self, true);\n          node = next;\n        }\n        self.remove();\n      }\n      remove() {\n        const self = this, parent = self.parent, next = self.next, prev = self.prev;\n        if (parent) {\n          if (parent.firstChild === self) {\n            parent.firstChild = next;\n            if (next) {\n              next.prev = null;\n            }\n          } else if (prev) {\n            prev.next = next;\n          }\n          if (parent.lastChild === self) {\n            parent.lastChild = prev;\n            if (prev) {\n              prev.next = null;\n            }\n          } else if (next) {\n            next.prev = prev;\n          }\n          self.parent = self.next = self.prev = null;\n        }\n        return self;\n      }\n      append(node) {\n        const self = this;\n        if (node.parent) {\n          node.remove();\n        }\n        const last = self.lastChild;\n        if (last) {\n          last.next = node;\n          node.prev = last;\n          self.lastChild = node;\n        } else {\n          self.lastChild = self.firstChild = node;\n        }\n        node.parent = self;\n        return node;\n      }\n      insert(node, refNode, before) {\n        if (node.parent) {\n          node.remove();\n        }\n        const parent = refNode.parent || this;\n        if (before) {\n          if (refNode === parent.firstChild) {\n            parent.firstChild = node;\n          } else if (refNode.prev) {\n            refNode.prev.next = node;\n          }\n          node.prev = refNode.prev;\n          node.next = refNode;\n          refNode.prev = node;\n        } else {\n          if (refNode === parent.lastChild) {\n            parent.lastChild = node;\n          } else if (refNode.next) {\n            refNode.next.prev = node;\n          }\n          node.next = refNode.next;\n          node.prev = refNode;\n          refNode.next = node;\n        }\n        node.parent = parent;\n        return node;\n      }\n      getAll(name) {\n        const self = this;\n        const collection = [];\n        for (let node = self.firstChild; node; node = walk$2(node, self)) {\n          if (node.name === name) {\n            collection.push(node);\n          }\n        }\n        return collection;\n      }\n      children() {\n        const self = this;\n        const collection = [];\n        for (let node = self.firstChild; node; node = node.next) {\n          collection.push(node);\n        }\n        return collection;\n      }\n      empty() {\n        const self = this;\n        if (self.firstChild) {\n          const nodes = [];\n          for (let node = self.firstChild; node; node = walk$2(node, self)) {\n            nodes.push(node);\n          }\n          let i = nodes.length;\n          while (i--) {\n            const node = nodes[i];\n            node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;\n          }\n        }\n        self.firstChild = self.lastChild = null;\n        return self;\n      }\n      isEmpty(elements, whitespace = {}, predicate) {\n        var _a;\n        const self = this;\n        let node = self.firstChild;\n        if (isNonEmptyElement(self)) {\n          return false;\n        }\n        if (node) {\n          do {\n            if (node.type === 1) {\n              if (node.attr('data-mce-bogus')) {\n                continue;\n              }\n              if (elements[node.name]) {\n                return false;\n              }\n              if (isNonEmptyElement(node)) {\n                return false;\n              }\n            }\n            if (node.type === 8) {\n              return false;\n            }\n            if (node.type === 3 && !isEmptyTextNode(node)) {\n              return false;\n            }\n            if (node.type === 3 && node.parent && whitespace[node.parent.name] && isWhitespaceText((_a = node.value) !== null && _a !== void 0 ? _a : '')) {\n              return false;\n            }\n            if (predicate && predicate(node)) {\n              return false;\n            }\n          } while (node = walk$2(node, self));\n        }\n        return true;\n      }\n      walk(prev) {\n        return walk$2(this, null, prev);\n      }\n    }\n\n    const isConditionalComment = (html, startIndex) => /^\\s*\\[if [\\w\\W]+\\]>.*<!\\[endif\\](--!?)?>/.test(html.substr(startIndex));\n    const findCommentEndIndex = (html, isBogus, startIndex = 0) => {\n      const lcHtml = html.toLowerCase();\n      if (lcHtml.indexOf('[if ', startIndex) !== -1 && isConditionalComment(lcHtml, startIndex)) {\n        const endIfIndex = lcHtml.indexOf('[endif]', startIndex);\n        return lcHtml.indexOf('>', endIfIndex);\n      } else {\n        if (isBogus) {\n          const endIndex = lcHtml.indexOf('>', startIndex);\n          return endIndex !== -1 ? endIndex : lcHtml.length;\n        } else {\n          const endCommentRegexp = /--!?>/g;\n          endCommentRegexp.lastIndex = startIndex;\n          const match = endCommentRegexp.exec(html);\n          return match ? match.index + match[0].length : lcHtml.length;\n        }\n      }\n    };\n    const findMatchingEndTagIndex = (schema, html, startIndex) => {\n      const startTagRegExp = /<([!?\\/])?([A-Za-z0-9\\-_:.]+)/g;\n      const endTagRegExp = /(?:\\s(?:[^'\">]+(?:\"[^\"]*\"|'[^']*'))*[^\"'>]*(?:\"[^\">]*|'[^'>]*)?|\\s*|\\/)>/g;\n      const voidElements = schema.getVoidElements();\n      let count = 1, index = startIndex;\n      while (count !== 0) {\n        startTagRegExp.lastIndex = index;\n        while (true) {\n          const startMatch = startTagRegExp.exec(html);\n          if (startMatch === null) {\n            return index;\n          } else if (startMatch[1] === '!') {\n            if (startsWith(startMatch[2], '--')) {\n              index = findCommentEndIndex(html, false, startMatch.index + '!--'.length);\n            } else {\n              index = findCommentEndIndex(html, true, startMatch.index + 1);\n            }\n            break;\n          } else {\n            endTagRegExp.lastIndex = startTagRegExp.lastIndex;\n            const endMatch = endTagRegExp.exec(html);\n            if (isNull(endMatch) || endMatch.index !== startTagRegExp.lastIndex) {\n              continue;\n            }\n            if (startMatch[1] === '/') {\n              count -= 1;\n            } else if (!has$2(voidElements, startMatch[2])) {\n              count += 1;\n            }\n            index = startTagRegExp.lastIndex + endMatch[0].length;\n            break;\n          }\n        }\n      }\n      return index;\n    };\n    const trimHtml$1 = (tempAttrs, html) => {\n      const trimContentRegExp = new RegExp(['\\\\s?(' + tempAttrs.join('|') + ')=\"[^\"]+\"'].join('|'), 'gi');\n      return html.replace(trimContentRegExp, '');\n    };\n    const trimInternal = (serializer, html) => {\n      const bogusAllRegExp = /<(\\w+) [^>]*data-mce-bogus=\"all\"[^>]*>/g;\n      const schema = serializer.schema;\n      let content = trimHtml$1(serializer.getTempAttrs(), html);\n      const voidElements = schema.getVoidElements();\n      let matches;\n      while (matches = bogusAllRegExp.exec(content)) {\n        const index = bogusAllRegExp.lastIndex;\n        const matchLength = matches[0].length;\n        let endTagIndex;\n        if (voidElements[matches[1]]) {\n          endTagIndex = index;\n        } else {\n          endTagIndex = findMatchingEndTagIndex(schema, content, index);\n        }\n        content = content.substring(0, index - matchLength) + content.substring(endTagIndex);\n        bogusAllRegExp.lastIndex = index - matchLength;\n      }\n      return trim$1(content);\n    };\n    const trimExternal = trimInternal;\n\n    const cleanupBogusElements = parent => {\n      const bogusElements = descendants(parent, '[data-mce-bogus]');\n      each$e(bogusElements, elem => {\n        const bogusValue = get$9(elem, 'data-mce-bogus');\n        if (bogusValue === 'all') {\n          remove$6(elem);\n        } else if (isBr$5(elem)) {\n          before$3(elem, SugarElement.fromText(zeroWidth));\n          remove$6(elem);\n        } else {\n          unwrap(elem);\n        }\n      });\n    };\n    const cleanupInputNames = parent => {\n      const inputs = descendants(parent, 'input');\n      each$e(inputs, input => {\n        remove$b(input, 'name');\n      });\n    };\n\n    const trimEmptyContents = (editor, html) => {\n      const blockName = getForcedRootBlock(editor);\n      const emptyRegExp = new RegExp(`^(<${ blockName }[^>]*>(&nbsp;|&#160;|\\\\s|\\u00a0|<br \\\\/>|)<\\\\/${ blockName }>[\\r\\n]*|<br \\\\/>[\\r\\n]*)$`);\n      return html.replace(emptyRegExp, '');\n    };\n    const getPlainTextContent = (editor, body) => {\n      const doc = editor.getDoc();\n      const dos = getRootNode(SugarElement.fromDom(editor.getBody()));\n      const offscreenDiv = SugarElement.fromTag('div', doc);\n      set$2(offscreenDiv, 'data-mce-bogus', 'all');\n      setAll(offscreenDiv, {\n        position: 'fixed',\n        left: '-9999999px',\n        top: '0'\n      });\n      set(offscreenDiv, body.innerHTML);\n      cleanupBogusElements(offscreenDiv);\n      cleanupInputNames(offscreenDiv);\n      const root = getContentContainer(dos);\n      append$1(root, offscreenDiv);\n      const content = trim$1(offscreenDiv.dom.innerText);\n      remove$6(offscreenDiv);\n      return content;\n    };\n    const getContentFromBody = (editor, args, body) => {\n      let content;\n      if (args.format === 'raw') {\n        content = Tools.trim(trimExternal(editor.serializer, body.innerHTML));\n      } else if (args.format === 'text') {\n        content = getPlainTextContent(editor, body);\n      } else if (args.format === 'tree') {\n        content = editor.serializer.serialize(body, args);\n      } else {\n        content = trimEmptyContents(editor, editor.serializer.serialize(body, args));\n      }\n      const shouldTrim = args.format !== 'text' && !isWsPreserveElement(SugarElement.fromDom(body));\n      return shouldTrim && isString(content) ? Tools.trim(content) : content;\n    };\n    const getContentInternal = (editor, args) => Optional.from(editor.getBody()).fold(constant(args.format === 'tree' ? new AstNode('body', 11) : ''), body => getContentFromBody(editor, args, body));\n\n    const makeMap$1 = Tools.makeMap;\n    const Writer = settings => {\n      const html = [];\n      settings = settings || {};\n      const indent = settings.indent;\n      const indentBefore = makeMap$1(settings.indent_before || '');\n      const indentAfter = makeMap$1(settings.indent_after || '');\n      const encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);\n      const htmlOutput = settings.element_format !== 'xhtml';\n      return {\n        start: (name, attrs, empty) => {\n          if (indent && indentBefore[name] && html.length > 0) {\n            const value = html[html.length - 1];\n            if (value.length > 0 && value !== '\\n') {\n              html.push('\\n');\n            }\n          }\n          html.push('<', name);\n          if (attrs) {\n            for (let i = 0, l = attrs.length; i < l; i++) {\n              const attr = attrs[i];\n              html.push(' ', attr.name, '=\"', encode(attr.value, true), '\"');\n            }\n          }\n          if (!empty || htmlOutput) {\n            html[html.length] = '>';\n          } else {\n            html[html.length] = ' />';\n          }\n          if (empty && indent && indentAfter[name] && html.length > 0) {\n            const value = html[html.length - 1];\n            if (value.length > 0 && value !== '\\n') {\n              html.push('\\n');\n            }\n          }\n        },\n        end: name => {\n          let value;\n          html.push('</', name, '>');\n          if (indent && indentAfter[name] && html.length > 0) {\n            value = html[html.length - 1];\n            if (value.length > 0 && value !== '\\n') {\n              html.push('\\n');\n            }\n          }\n        },\n        text: (text, raw) => {\n          if (text.length > 0) {\n            html[html.length] = raw ? text : encode(text);\n          }\n        },\n        cdata: text => {\n          html.push('<![CDATA[', text, ']]>');\n        },\n        comment: text => {\n          html.push('<!--', text, '-->');\n        },\n        pi: (name, text) => {\n          if (text) {\n            html.push('<?', name, ' ', encode(text), '?>');\n          } else {\n            html.push('<?', name, '?>');\n          }\n          if (indent) {\n            html.push('\\n');\n          }\n        },\n        doctype: text => {\n          html.push('<!DOCTYPE', text, '>', indent ? '\\n' : '');\n        },\n        reset: () => {\n          html.length = 0;\n        },\n        getContent: () => {\n          return html.join('').replace(/\\n$/, '');\n        }\n      };\n    };\n\n    const HtmlSerializer = (settings = {}, schema = Schema()) => {\n      const writer = Writer(settings);\n      settings.validate = 'validate' in settings ? settings.validate : true;\n      const serialize = node => {\n        const validate = settings.validate;\n        const handlers = {\n          3: node => {\n            var _a;\n            writer.text((_a = node.value) !== null && _a !== void 0 ? _a : '', node.raw);\n          },\n          8: node => {\n            var _a;\n            writer.comment((_a = node.value) !== null && _a !== void 0 ? _a : '');\n          },\n          7: node => {\n            writer.pi(node.name, node.value);\n          },\n          10: node => {\n            var _a;\n            writer.doctype((_a = node.value) !== null && _a !== void 0 ? _a : '');\n          },\n          4: node => {\n            var _a;\n            writer.cdata((_a = node.value) !== null && _a !== void 0 ? _a : '');\n          },\n          11: node => {\n            let tempNode = node;\n            if (tempNode = tempNode.firstChild) {\n              do {\n                walk(tempNode);\n              } while (tempNode = tempNode.next);\n            }\n          }\n        };\n        writer.reset();\n        const walk = node => {\n          var _a;\n          const handler = handlers[node.type];\n          if (!handler) {\n            const name = node.name;\n            const isEmpty = name in schema.getVoidElements();\n            let attrs = node.attributes;\n            if (validate && attrs && attrs.length > 1) {\n              const sortedAttrs = [];\n              sortedAttrs.map = {};\n              const elementRule = schema.getElementRule(node.name);\n              if (elementRule) {\n                for (let i = 0, l = elementRule.attributesOrder.length; i < l; i++) {\n                  const attrName = elementRule.attributesOrder[i];\n                  if (attrName in attrs.map) {\n                    const attrValue = attrs.map[attrName];\n                    sortedAttrs.map[attrName] = attrValue;\n                    sortedAttrs.push({\n                      name: attrName,\n                      value: attrValue\n                    });\n                  }\n                }\n                for (let i = 0, l = attrs.length; i < l; i++) {\n                  const attrName = attrs[i].name;\n                  if (!(attrName in sortedAttrs.map)) {\n                    const attrValue = attrs.map[attrName];\n                    sortedAttrs.map[attrName] = attrValue;\n                    sortedAttrs.push({\n                      name: attrName,\n                      value: attrValue\n                    });\n                  }\n                }\n                attrs = sortedAttrs;\n              }\n            }\n            writer.start(name, attrs, isEmpty);\n            if (!isEmpty) {\n              let child = node.firstChild;\n              if (child) {\n                if ((name === 'pre' || name === 'textarea') && child.type === 3 && ((_a = child.value) === null || _a === void 0 ? void 0 : _a[0]) === '\\n') {\n                  writer.text('\\n', true);\n                }\n                do {\n                  walk(child);\n                } while (child = child.next);\n              }\n              writer.end(name);\n            }\n          } else {\n            handler(node);\n          }\n        };\n        if (node.type === 1 && !settings.inner) {\n          walk(node);\n        } else if (node.type === 3) {\n          handlers[3](node);\n        } else {\n          handlers[11](node);\n        }\n        return writer.getContent();\n      };\n      return { serialize };\n    };\n\n    const nonInheritableStyles = new Set();\n    (() => {\n      const nonInheritableStylesArr = [\n        'margin',\n        'margin-left',\n        'margin-right',\n        'margin-top',\n        'margin-bottom',\n        'padding',\n        'padding-left',\n        'padding-right',\n        'padding-top',\n        'padding-bottom',\n        'border',\n        'border-width',\n        'border-style',\n        'border-color',\n        'background',\n        'background-attachment',\n        'background-clip',\n        'background-color',\n        'background-image',\n        'background-origin',\n        'background-position',\n        'background-repeat',\n        'background-size',\n        'float',\n        'position',\n        'left',\n        'right',\n        'top',\n        'bottom',\n        'z-index',\n        'display',\n        'transform',\n        'width',\n        'max-width',\n        'min-width',\n        'height',\n        'max-height',\n        'min-height',\n        'overflow',\n        'overflow-x',\n        'overflow-y',\n        'text-overflow',\n        'vertical-align',\n        'transition',\n        'transition-delay',\n        'transition-duration',\n        'transition-property',\n        'transition-timing-function'\n      ];\n      each$e(nonInheritableStylesArr, style => {\n        nonInheritableStyles.add(style);\n      });\n    })();\n    const shorthandStyleProps = [\n      'font',\n      'text-decoration',\n      'text-emphasis'\n    ];\n    const getStyleProps = (dom, node) => keys(dom.parseStyle(dom.getAttrib(node, 'style')));\n    const isNonInheritableStyle = style => nonInheritableStyles.has(style);\n    const hasInheritableStyles = (dom, node) => forall(getStyleProps(dom, node), style => !isNonInheritableStyle(style));\n    const getLonghandStyleProps = styles => filter$5(styles, style => exists(shorthandStyleProps, prop => startsWith(style, prop)));\n    const hasStyleConflict = (dom, node, parentNode) => {\n      const nodeStyleProps = getStyleProps(dom, node);\n      const parentNodeStyleProps = getStyleProps(dom, parentNode);\n      const valueMismatch = prop => {\n        var _a, _b;\n        const nodeValue = (_a = dom.getStyle(node, prop)) !== null && _a !== void 0 ? _a : '';\n        const parentValue = (_b = dom.getStyle(parentNode, prop)) !== null && _b !== void 0 ? _b : '';\n        return isNotEmpty(nodeValue) && isNotEmpty(parentValue) && nodeValue !== parentValue;\n      };\n      return exists(nodeStyleProps, nodeStyleProp => {\n        const propExists = props => exists(props, prop => prop === nodeStyleProp);\n        if (!propExists(parentNodeStyleProps) && propExists(shorthandStyleProps)) {\n          const longhandProps = getLonghandStyleProps(parentNodeStyleProps);\n          return exists(longhandProps, valueMismatch);\n        } else {\n          return valueMismatch(nodeStyleProp);\n        }\n      });\n    };\n\n    const isChar = (forward, predicate, pos) => Optional.from(pos.container()).filter(isText$a).exists(text => {\n      const delta = forward ? 0 : -1;\n      return predicate(text.data.charAt(pos.offset() + delta));\n    });\n    const isBeforeSpace = curry(isChar, true, isWhiteSpace);\n    const isAfterSpace = curry(isChar, false, isWhiteSpace);\n    const isEmptyText = pos => {\n      const container = pos.container();\n      return isText$a(container) && (container.data.length === 0 || isZwsp(container.data) && BookmarkManager.isBookmarkNode(container.parentNode));\n    };\n    const matchesElementPosition = (before, predicate) => pos => getChildNodeAtRelativeOffset(before ? 0 : -1, pos).filter(predicate).isSome();\n    const isImageBlock = node => isImg(node) && get$7(SugarElement.fromDom(node), 'display') === 'block';\n    const isCefNode = node => isContentEditableFalse$a(node) && !isBogusAll$1(node);\n    const isBeforeImageBlock = matchesElementPosition(true, isImageBlock);\n    const isAfterImageBlock = matchesElementPosition(false, isImageBlock);\n    const isBeforeMedia = matchesElementPosition(true, isMedia$2);\n    const isAfterMedia = matchesElementPosition(false, isMedia$2);\n    const isBeforeTable = matchesElementPosition(true, isTable$2);\n    const isAfterTable = matchesElementPosition(false, isTable$2);\n    const isBeforeContentEditableFalse = matchesElementPosition(true, isCefNode);\n    const isAfterContentEditableFalse = matchesElementPosition(false, isCefNode);\n\n    const dropLast = xs => xs.slice(0, -1);\n    const parentsUntil = (start, root, predicate) => {\n      if (contains(root, start)) {\n        return dropLast(parents$1(start, elm => {\n          return predicate(elm) || eq(elm, root);\n        }));\n      } else {\n        return [];\n      }\n    };\n    const parents = (start, root) => parentsUntil(start, root, never);\n    const parentsAndSelf = (start, root) => [start].concat(parents(start, root));\n\n    const navigateIgnoreEmptyTextNodes = (forward, root, from) => navigateIgnore(forward, root, from, isEmptyText);\n    const getClosestBlock$1 = (root, pos) => find$2(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2);\n    const isAtBeforeAfterBlockBoundary = (forward, root, pos) => navigateIgnoreEmptyTextNodes(forward, root.dom, pos).forall(newPos => getClosestBlock$1(root, pos).fold(() => !isInSameBlock(newPos, pos, root.dom), fromBlock => !isInSameBlock(newPos, pos, root.dom) && contains(fromBlock, SugarElement.fromDom(newPos.container()))));\n    const isAtBlockBoundary = (forward, root, pos) => getClosestBlock$1(root, pos).fold(() => navigateIgnoreEmptyTextNodes(forward, root.dom, pos).forall(newPos => !isInSameBlock(newPos, pos, root.dom)), parent => navigateIgnoreEmptyTextNodes(forward, parent.dom, pos).isNone());\n    const isAtStartOfBlock = curry(isAtBlockBoundary, false);\n    const isAtEndOfBlock = curry(isAtBlockBoundary, true);\n    const isBeforeBlock = curry(isAtBeforeAfterBlockBoundary, false);\n    const isAfterBlock = curry(isAtBeforeAfterBlockBoundary, true);\n\n    const isBr$1 = pos => getElementFromPosition(pos).exists(isBr$5);\n    const findBr = (forward, root, pos) => {\n      const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2);\n      const scope = head(parentBlocks).getOr(root);\n      return fromPosition(forward, scope.dom, pos).filter(isBr$1);\n    };\n    const isBeforeBr$1 = (root, pos) => getElementFromPosition(pos).exists(isBr$5) || findBr(true, root, pos).isSome();\n    const isAfterBr = (root, pos) => getElementFromPrevPosition(pos).exists(isBr$5) || findBr(false, root, pos).isSome();\n    const findPreviousBr = curry(findBr, false);\n    const findNextBr = curry(findBr, true);\n\n    const isInMiddleOfText = pos => CaretPosition.isTextPosition(pos) && !pos.isAtStart() && !pos.isAtEnd();\n    const getClosestBlock = (root, pos) => {\n      const parentBlocks = filter$5(parentsAndSelf(SugarElement.fromDom(pos.container()), root), isBlock$2);\n      return head(parentBlocks).getOr(root);\n    };\n    const hasSpaceBefore = (root, pos) => {\n      if (isInMiddleOfText(pos)) {\n        return isAfterSpace(pos);\n      } else {\n        return isAfterSpace(pos) || prevPosition(getClosestBlock(root, pos).dom, pos).exists(isAfterSpace);\n      }\n    };\n    const hasSpaceAfter = (root, pos) => {\n      if (isInMiddleOfText(pos)) {\n        return isBeforeSpace(pos);\n      } else {\n        return isBeforeSpace(pos) || nextPosition(getClosestBlock(root, pos).dom, pos).exists(isBeforeSpace);\n      }\n    };\n    const isPreValue = value => contains$2([\n      'pre',\n      'pre-wrap'\n    ], value);\n    const isInPre = pos => getElementFromPosition(pos).bind(elm => closest$4(elm, isElement$7)).exists(elm => isPreValue(get$7(elm, 'white-space')));\n    const isAtBeginningOfBody = (root, pos) => prevPosition(root.dom, pos).isNone();\n    const isAtEndOfBody = (root, pos) => nextPosition(root.dom, pos).isNone();\n    const isAtLineBoundary = (root, pos) => isAtBeginningOfBody(root, pos) || isAtEndOfBody(root, pos) || isAtStartOfBlock(root, pos) || isAtEndOfBlock(root, pos) || isAfterBr(root, pos) || isBeforeBr$1(root, pos);\n    const isCefBlock = node => isNonNullable(node) && isContentEditableFalse$a(node) && isBlockLike(node);\n    const isSiblingCefBlock = (root, direction) => container => {\n      return isCefBlock(new DomTreeWalker(container, root)[direction]());\n    };\n    const isBeforeCefBlock = (root, pos) => {\n      const nextPos = nextPosition(root.dom, pos).getOr(pos);\n      const isNextCefBlock = isSiblingCefBlock(root.dom, 'next');\n      return pos.isAtEnd() && (isNextCefBlock(pos.container()) || isNextCefBlock(nextPos.container()));\n    };\n    const isAfterCefBlock = (root, pos) => {\n      const prevPos = prevPosition(root.dom, pos).getOr(pos);\n      const isPrevCefBlock = isSiblingCefBlock(root.dom, 'prev');\n      return pos.isAtStart() && (isPrevCefBlock(pos.container()) || isPrevCefBlock(prevPos.container()));\n    };\n    const needsToHaveNbsp = (root, pos) => {\n      if (isInPre(pos)) {\n        return false;\n      } else {\n        return isAtLineBoundary(root, pos) || hasSpaceBefore(root, pos) || hasSpaceAfter(root, pos);\n      }\n    };\n    const needsToBeNbspLeft = (root, pos) => {\n      if (isInPre(pos)) {\n        return false;\n      } else {\n        return isAtStartOfBlock(root, pos) || isBeforeBlock(root, pos) || isAfterBr(root, pos) || hasSpaceBefore(root, pos) || isAfterCefBlock(root, pos);\n      }\n    };\n    const leanRight = pos => {\n      const container = pos.container();\n      const offset = pos.offset();\n      if (isText$a(container) && offset < container.data.length) {\n        return CaretPosition(container, offset + 1);\n      } else {\n        return pos;\n      }\n    };\n    const needsToBeNbspRight = (root, pos) => {\n      if (isInPre(pos)) {\n        return false;\n      } else {\n        return isAtEndOfBlock(root, pos) || isAfterBlock(root, pos) || isBeforeBr$1(root, pos) || hasSpaceAfter(root, pos) || isBeforeCefBlock(root, pos);\n      }\n    };\n    const needsToBeNbsp = (root, pos) => needsToBeNbspLeft(root, pos) || needsToBeNbspRight(root, leanRight(pos));\n    const isNbspAt = (text, offset) => isNbsp(text.charAt(offset));\n    const isWhiteSpaceAt = (text, offset) => isWhiteSpace(text.charAt(offset));\n    const hasNbsp = pos => {\n      const container = pos.container();\n      return isText$a(container) && contains$1(container.data, nbsp);\n    };\n    const normalizeNbspMiddle = text => {\n      const chars = text.split('');\n      return map$3(chars, (chr, i) => {\n        if (isNbsp(chr) && i > 0 && i < chars.length - 1 && isContent(chars[i - 1]) && isContent(chars[i + 1])) {\n          return ' ';\n        } else {\n          return chr;\n        }\n      }).join('');\n    };\n    const normalizeNbspAtStart = (root, node, makeNbsp) => {\n      const text = node.data;\n      const firstPos = CaretPosition(node, 0);\n      if (!makeNbsp && isNbspAt(text, 0) && !needsToBeNbsp(root, firstPos)) {\n        node.data = ' ' + text.slice(1);\n        return true;\n      } else if (makeNbsp && isWhiteSpaceAt(text, 0) && needsToBeNbspLeft(root, firstPos)) {\n        node.data = nbsp + text.slice(1);\n        return true;\n      } else {\n        return false;\n      }\n    };\n    const normalizeNbspInMiddleOfTextNode = node => {\n      const text = node.data;\n      const newText = normalizeNbspMiddle(text);\n      if (newText !== text) {\n        node.data = newText;\n        return true;\n      } else {\n        return false;\n      }\n    };\n    const normalizeNbspAtEnd = (root, node, makeNbsp) => {\n      const text = node.data;\n      const lastPos = CaretPosition(node, text.length - 1);\n      if (!makeNbsp && isNbspAt(text, text.length - 1) && !needsToBeNbsp(root, lastPos)) {\n        node.data = text.slice(0, -1) + ' ';\n        return true;\n      } else if (makeNbsp && isWhiteSpaceAt(text, text.length - 1) && needsToBeNbspRight(root, lastPos)) {\n        node.data = text.slice(0, -1) + nbsp;\n        return true;\n      } else {\n        return false;\n      }\n    };\n    const normalizeNbsps = (root, pos) => {\n      const container = pos.container();\n      if (!isText$a(container)) {\n        return Optional.none();\n      }\n      if (hasNbsp(pos)) {\n        const normalized = normalizeNbspAtStart(root, container, false) || normalizeNbspInMiddleOfTextNode(container) || normalizeNbspAtEnd(root, container, false);\n        return someIf(normalized, pos);\n      } else if (needsToBeNbsp(root, pos)) {\n        const normalized = normalizeNbspAtStart(root, container, true) || normalizeNbspAtEnd(root, container, true);\n        return someIf(normalized, pos);\n      } else {\n        return Optional.none();\n      }\n    };\n    const normalizeNbspsInEditor = editor => {\n      const root = SugarElement.fromDom(editor.getBody());\n      if (editor.selection.isCollapsed()) {\n        normalizeNbsps(root, CaretPosition.fromRangeStart(editor.selection.getRng())).each(pos => {\n          editor.selection.setRng(pos.toRange());\n        });\n      }\n    };\n\n    const normalize$1 = (node, offset, count) => {\n      if (count === 0) {\n        return;\n      }\n      const elm = SugarElement.fromDom(node);\n      const root = ancestor$3(elm, isBlock$2).getOr(elm);\n      const whitespace = node.data.slice(offset, offset + count);\n      const isEndOfContent = offset + count >= node.data.length && needsToBeNbspRight(root, CaretPosition(node, node.data.length));\n      const isStartOfContent = offset === 0 && needsToBeNbspLeft(root, CaretPosition(node, 0));\n      node.replaceData(offset, count, normalize$4(whitespace, 4, isStartOfContent, isEndOfContent));\n    };\n    const normalizeWhitespaceAfter = (node, offset) => {\n      const content = node.data.slice(offset);\n      const whitespaceCount = content.length - lTrim(content).length;\n      normalize$1(node, offset, whitespaceCount);\n    };\n    const normalizeWhitespaceBefore = (node, offset) => {\n      const content = node.data.slice(0, offset);\n      const whitespaceCount = content.length - rTrim(content).length;\n      normalize$1(node, offset - whitespaceCount, whitespaceCount);\n    };\n    const mergeTextNodes = (prevNode, nextNode, normalizeWhitespace, mergeToPrev = true) => {\n      const whitespaceOffset = rTrim(prevNode.data).length;\n      const newNode = mergeToPrev ? prevNode : nextNode;\n      const removeNode = mergeToPrev ? nextNode : prevNode;\n      if (mergeToPrev) {\n        newNode.appendData(removeNode.data);\n      } else {\n        newNode.insertData(0, removeNode.data);\n      }\n      remove$6(SugarElement.fromDom(removeNode));\n      if (normalizeWhitespace) {\n        normalizeWhitespaceAfter(newNode, whitespaceOffset);\n      }\n      return newNode;\n    };\n\n    const needsReposition = (pos, elm) => {\n      const container = pos.container();\n      const offset = pos.offset();\n      return !CaretPosition.isTextPosition(pos) && container === elm.parentNode && offset > CaretPosition.before(elm).offset();\n    };\n    const reposition = (elm, pos) => needsReposition(pos, elm) ? CaretPosition(pos.container(), pos.offset() - 1) : pos;\n    const beforeOrStartOf = node => isText$a(node) ? CaretPosition(node, 0) : CaretPosition.before(node);\n    const afterOrEndOf = node => isText$a(node) ? CaretPosition(node, node.data.length) : CaretPosition.after(node);\n    const getPreviousSiblingCaretPosition = elm => {\n      if (isCaretCandidate$3(elm.previousSibling)) {\n        return Optional.some(afterOrEndOf(elm.previousSibling));\n      } else {\n        return elm.previousSibling ? lastPositionIn(elm.previousSibling) : Optional.none();\n      }\n    };\n    const getNextSiblingCaretPosition = elm => {\n      if (isCaretCandidate$3(elm.nextSibling)) {\n        return Optional.some(beforeOrStartOf(elm.nextSibling));\n      } else {\n        return elm.nextSibling ? firstPositionIn(elm.nextSibling) : Optional.none();\n      }\n    };\n    const findCaretPositionBackwardsFromElm = (rootElement, elm) => {\n      return Optional.from(elm.previousSibling ? elm.previousSibling : elm.parentNode).bind(node => prevPosition(rootElement, CaretPosition.before(node))).orThunk(() => nextPosition(rootElement, CaretPosition.after(elm)));\n    };\n    const findCaretPositionForwardsFromElm = (rootElement, elm) => nextPosition(rootElement, CaretPosition.after(elm)).orThunk(() => prevPosition(rootElement, CaretPosition.before(elm)));\n    const findCaretPositionBackwards = (rootElement, elm) => getPreviousSiblingCaretPosition(elm).orThunk(() => getNextSiblingCaretPosition(elm)).orThunk(() => findCaretPositionBackwardsFromElm(rootElement, elm));\n    const findCaretPositionForward = (rootElement, elm) => getNextSiblingCaretPosition(elm).orThunk(() => getPreviousSiblingCaretPosition(elm)).orThunk(() => findCaretPositionForwardsFromElm(rootElement, elm));\n    const findCaretPosition = (forward, rootElement, elm) => forward ? findCaretPositionForward(rootElement, elm) : findCaretPositionBackwards(rootElement, elm);\n    const findCaretPosOutsideElmAfterDelete = (forward, rootElement, elm) => findCaretPosition(forward, rootElement, elm).map(curry(reposition, elm));\n    const setSelection$1 = (editor, forward, pos) => {\n      pos.fold(() => {\n        editor.focus();\n      }, pos => {\n        editor.selection.setRng(pos.toRange(), forward);\n      });\n    };\n    const eqRawNode = rawNode => elm => elm.dom === rawNode;\n    const isBlock = (editor, elm) => elm && has$2(editor.schema.getBlockElements(), name(elm));\n    const paddEmptyBlock = elm => {\n      if (isEmpty$2(elm)) {\n        const br = SugarElement.fromHtml('<br data-mce-bogus=\"1\">');\n        empty(elm);\n        append$1(elm, br);\n        return Optional.some(CaretPosition.before(br.dom));\n      } else {\n        return Optional.none();\n      }\n    };\n    const deleteNormalized = (elm, afterDeletePosOpt, normalizeWhitespace) => {\n      const prevTextOpt = prevSibling(elm).filter(isText$b);\n      const nextTextOpt = nextSibling(elm).filter(isText$b);\n      remove$6(elm);\n      return lift3(prevTextOpt, nextTextOpt, afterDeletePosOpt, (prev, next, pos) => {\n        const prevNode = prev.dom, nextNode = next.dom;\n        const offset = prevNode.data.length;\n        mergeTextNodes(prevNode, nextNode, normalizeWhitespace);\n        return pos.container() === nextNode ? CaretPosition(prevNode, offset) : pos;\n      }).orThunk(() => {\n        if (normalizeWhitespace) {\n          prevTextOpt.each(elm => normalizeWhitespaceBefore(elm.dom, elm.dom.length));\n          nextTextOpt.each(elm => normalizeWhitespaceAfter(elm.dom, 0));\n        }\n        return afterDeletePosOpt;\n      });\n    };\n    const isInlineElement = (editor, element) => has$2(editor.schema.getTextInlineElements(), name(element));\n    const deleteElement$2 = (editor, forward, elm, moveCaret = true) => {\n      const afterDeletePos = findCaretPosOutsideElmAfterDelete(forward, editor.getBody(), elm.dom);\n      const parentBlock = ancestor$3(elm, curry(isBlock, editor), eqRawNode(editor.getBody()));\n      const normalizedAfterDeletePos = deleteNormalized(elm, afterDeletePos, isInlineElement(editor, elm));\n      if (editor.dom.isEmpty(editor.getBody())) {\n        editor.setContent('');\n        editor.selection.setCursorLocation();\n      } else {\n        parentBlock.bind(paddEmptyBlock).fold(() => {\n          if (moveCaret) {\n            setSelection$1(editor, forward, normalizedAfterDeletePos);\n          }\n        }, paddPos => {\n          if (moveCaret) {\n            setSelection$1(editor, forward, Optional.some(paddPos));\n          }\n        });\n      }\n    };\n\n    const strongRtl = /[\\u0591-\\u07FF\\uFB1D-\\uFDFF\\uFE70-\\uFEFC]/;\n    const hasStrongRtl = text => strongRtl.test(text);\n\n    const isInlineTarget = (editor, elm) => is$1(SugarElement.fromDom(elm), getInlineBoundarySelector(editor)) && !isTransparentBlock(editor.schema, elm);\n    const isRtl = element => {\n      var _a;\n      return DOMUtils.DOM.getStyle(element, 'direction', true) === 'rtl' || hasStrongRtl((_a = element.textContent) !== null && _a !== void 0 ? _a : '');\n    };\n    const findInlineParents = (isInlineTarget, rootNode, pos) => filter$5(DOMUtils.DOM.getParents(pos.container(), '*', rootNode), isInlineTarget);\n    const findRootInline = (isInlineTarget, rootNode, pos) => {\n      const parents = findInlineParents(isInlineTarget, rootNode, pos);\n      return Optional.from(parents[parents.length - 1]);\n    };\n    const hasSameParentBlock = (rootNode, node1, node2) => {\n      const block1 = getParentBlock$3(node1, rootNode);\n      const block2 = getParentBlock$3(node2, rootNode);\n      return isNonNullable(block1) && block1 === block2;\n    };\n    const isAtZwsp = pos => isBeforeInline(pos) || isAfterInline(pos);\n    const normalizePosition = (forward, pos) => {\n      const container = pos.container(), offset = pos.offset();\n      if (forward) {\n        if (isCaretContainerInline(container)) {\n          if (isText$a(container.nextSibling)) {\n            return CaretPosition(container.nextSibling, 0);\n          } else {\n            return CaretPosition.after(container);\n          }\n        } else {\n          return isBeforeInline(pos) ? CaretPosition(container, offset + 1) : pos;\n        }\n      } else {\n        if (isCaretContainerInline(container)) {\n          if (isText$a(container.previousSibling)) {\n            return CaretPosition(container.previousSibling, container.previousSibling.data.length);\n          } else {\n            return CaretPosition.before(container);\n          }\n        } else {\n          return isAfterInline(pos) ? CaretPosition(container, offset - 1) : pos;\n        }\n      }\n    };\n    const normalizeForwards = curry(normalizePosition, true);\n    const normalizeBackwards = curry(normalizePosition, false);\n\n    const execCommandIgnoreInputEvents = (editor, command) => {\n      const inputBlocker = e => e.stopImmediatePropagation();\n      editor.on('beforeinput input', inputBlocker, true);\n      editor.getDoc().execCommand(command);\n      editor.off('beforeinput input', inputBlocker);\n    };\n    const execEditorDeleteCommand = editor => {\n      editor.execCommand('delete');\n    };\n    const execNativeDeleteCommand = editor => execCommandIgnoreInputEvents(editor, 'Delete');\n    const execNativeForwardDeleteCommand = editor => execCommandIgnoreInputEvents(editor, 'ForwardDelete');\n    const isBeforeRoot = rootNode => elm => is$2(parent(elm), rootNode, eq);\n    const isTextBlockOrListItem = element => isTextBlock$2(element) || isListItem$1(element);\n    const getParentBlock$2 = (rootNode, elm) => {\n      if (contains(rootNode, elm)) {\n        return closest$4(elm, isTextBlockOrListItem, isBeforeRoot(rootNode));\n      } else {\n        return Optional.none();\n      }\n    };\n    const paddEmptyBody = (editor, moveSelection = true) => {\n      if (editor.dom.isEmpty(editor.getBody())) {\n        editor.setContent('', { no_selection: !moveSelection });\n      }\n    };\n    const willDeleteLastPositionInElement = (forward, fromPos, elm) => lift2(firstPositionIn(elm), lastPositionIn(elm), (firstPos, lastPos) => {\n      const normalizedFirstPos = normalizePosition(true, firstPos);\n      const normalizedLastPos = normalizePosition(false, lastPos);\n      const normalizedFromPos = normalizePosition(false, fromPos);\n      if (forward) {\n        return nextPosition(elm, normalizedFromPos).exists(nextPos => nextPos.isEqual(normalizedLastPos) && fromPos.isEqual(normalizedFirstPos));\n      } else {\n        return prevPosition(elm, normalizedFromPos).exists(prevPos => prevPos.isEqual(normalizedFirstPos) && fromPos.isEqual(normalizedLastPos));\n      }\n    }).getOr(true);\n    const freefallRtl = root => {\n      const child = isComment$1(root) ? prevSibling(root) : lastChild(root);\n      return child.bind(freefallRtl).orThunk(() => Optional.some(root));\n    };\n    const deleteRangeContents = (editor, rng, root, moveSelection = true) => {\n      var _a;\n      rng.deleteContents();\n      const lastNode = freefallRtl(root).getOr(root);\n      const lastBlock = SugarElement.fromDom((_a = editor.dom.getParent(lastNode.dom, editor.dom.isBlock)) !== null && _a !== void 0 ? _a : root.dom);\n      if (lastBlock.dom === editor.getBody()) {\n        paddEmptyBody(editor, moveSelection);\n      } else if (isEmpty$2(lastBlock)) {\n        fillWithPaddingBr(lastBlock);\n        if (moveSelection) {\n          editor.selection.setCursorLocation(lastBlock.dom, 0);\n        }\n      }\n      if (!eq(root, lastBlock)) {\n        const additionalCleanupNodes = is$2(parent(lastBlock), root) ? [] : siblings(lastBlock);\n        each$e(additionalCleanupNodes.concat(children$1(root)), node => {\n          if (!eq(node, lastBlock) && !contains(node, lastBlock) && isEmpty$2(node)) {\n            remove$6(node);\n          }\n        });\n      }\n    };\n\n    const isRootFromElement = root => cur => eq(root, cur);\n    const getTableCells = table => descendants(table, 'td,th');\n    const getTableDetailsFromRange = (rng, isRoot) => {\n      const getTable = node => getClosestTable(SugarElement.fromDom(node), isRoot);\n      const startTable = getTable(rng.startContainer);\n      const endTable = getTable(rng.endContainer);\n      const isStartInTable = startTable.isSome();\n      const isEndInTable = endTable.isSome();\n      const isSameTable = lift2(startTable, endTable, eq).getOr(false);\n      const isMultiTable = !isSameTable && isStartInTable && isEndInTable;\n      return {\n        startTable,\n        endTable,\n        isStartInTable,\n        isEndInTable,\n        isSameTable,\n        isMultiTable\n      };\n    };\n\n    const tableCellRng = (start, end) => ({\n      start,\n      end\n    });\n    const tableSelection = (rng, table, cells) => ({\n      rng,\n      table,\n      cells\n    });\n    const deleteAction = Adt.generate([\n      {\n        singleCellTable: [\n          'rng',\n          'cell'\n        ]\n      },\n      { fullTable: ['table'] },\n      {\n        partialTable: [\n          'cells',\n          'outsideDetails'\n        ]\n      },\n      {\n        multiTable: [\n          'startTableCells',\n          'endTableCells',\n          'betweenRng'\n        ]\n      }\n    ]);\n    const getClosestCell$1 = (container, isRoot) => closest$3(SugarElement.fromDom(container), 'td,th', isRoot);\n    const isExpandedCellRng = cellRng => !eq(cellRng.start, cellRng.end);\n    const getTableFromCellRng = (cellRng, isRoot) => getClosestTable(cellRng.start, isRoot).bind(startParentTable => getClosestTable(cellRng.end, isRoot).bind(endParentTable => someIf(eq(startParentTable, endParentTable), startParentTable)));\n    const isSingleCellTable = (cellRng, isRoot) => !isExpandedCellRng(cellRng) && getTableFromCellRng(cellRng, isRoot).exists(table => {\n      const rows = table.dom.rows;\n      return rows.length === 1 && rows[0].cells.length === 1;\n    });\n    const getCellRng = (rng, isRoot) => {\n      const startCell = getClosestCell$1(rng.startContainer, isRoot);\n      const endCell = getClosestCell$1(rng.endContainer, isRoot);\n      return lift2(startCell, endCell, tableCellRng);\n    };\n    const getCellRangeFromStartTable = isRoot => startCell => getClosestTable(startCell, isRoot).bind(table => last$3(getTableCells(table)).map(endCell => tableCellRng(startCell, endCell)));\n    const getCellRangeFromEndTable = isRoot => endCell => getClosestTable(endCell, isRoot).bind(table => head(getTableCells(table)).map(startCell => tableCellRng(startCell, endCell)));\n    const getTableSelectionFromCellRng = isRoot => cellRng => getTableFromCellRng(cellRng, isRoot).map(table => tableSelection(cellRng, table, getTableCells(table)));\n    const getTableSelections = (cellRng, selectionDetails, rng, isRoot) => {\n      if (rng.collapsed || !cellRng.forall(isExpandedCellRng)) {\n        return Optional.none();\n      } else if (selectionDetails.isSameTable) {\n        const sameTableSelection = cellRng.bind(getTableSelectionFromCellRng(isRoot));\n        return Optional.some({\n          start: sameTableSelection,\n          end: sameTableSelection\n        });\n      } else {\n        const startCell = getClosestCell$1(rng.startContainer, isRoot);\n        const endCell = getClosestCell$1(rng.endContainer, isRoot);\n        const startTableSelection = startCell.bind(getCellRangeFromStartTable(isRoot)).bind(getTableSelectionFromCellRng(isRoot));\n        const endTableSelection = endCell.bind(getCellRangeFromEndTable(isRoot)).bind(getTableSelectionFromCellRng(isRoot));\n        return Optional.some({\n          start: startTableSelection,\n          end: endTableSelection\n        });\n      }\n    };\n    const getCellIndex = (cells, cell) => findIndex$2(cells, x => eq(x, cell));\n    const getSelectedCells = tableSelection => lift2(getCellIndex(tableSelection.cells, tableSelection.rng.start), getCellIndex(tableSelection.cells, tableSelection.rng.end), (startIndex, endIndex) => tableSelection.cells.slice(startIndex, endIndex + 1));\n    const isSingleCellTableContentSelected = (optCellRng, rng, isRoot) => optCellRng.exists(cellRng => isSingleCellTable(cellRng, isRoot) && hasAllContentsSelected(cellRng.start, rng));\n    const unselectCells = (rng, selectionDetails) => {\n      const {startTable, endTable} = selectionDetails;\n      const otherContentRng = rng.cloneRange();\n      startTable.each(table => otherContentRng.setStartAfter(table.dom));\n      endTable.each(table => otherContentRng.setEndBefore(table.dom));\n      return otherContentRng;\n    };\n    const handleSingleTable = (cellRng, selectionDetails, rng, isRoot) => getTableSelections(cellRng, selectionDetails, rng, isRoot).bind(({start, end}) => start.or(end)).bind(tableSelection => {\n      const {isSameTable} = selectionDetails;\n      const selectedCells = getSelectedCells(tableSelection).getOr([]);\n      if (isSameTable && tableSelection.cells.length === selectedCells.length) {\n        return Optional.some(deleteAction.fullTable(tableSelection.table));\n      } else if (selectedCells.length > 0) {\n        if (isSameTable) {\n          return Optional.some(deleteAction.partialTable(selectedCells, Optional.none()));\n        } else {\n          const otherContentRng = unselectCells(rng, selectionDetails);\n          return Optional.some(deleteAction.partialTable(selectedCells, Optional.some({\n            ...selectionDetails,\n            rng: otherContentRng\n          })));\n        }\n      } else {\n        return Optional.none();\n      }\n    });\n    const handleMultiTable = (cellRng, selectionDetails, rng, isRoot) => getTableSelections(cellRng, selectionDetails, rng, isRoot).bind(({start, end}) => {\n      const startTableSelectedCells = start.bind(getSelectedCells).getOr([]);\n      const endTableSelectedCells = end.bind(getSelectedCells).getOr([]);\n      if (startTableSelectedCells.length > 0 && endTableSelectedCells.length > 0) {\n        const otherContentRng = unselectCells(rng, selectionDetails);\n        return Optional.some(deleteAction.multiTable(startTableSelectedCells, endTableSelectedCells, otherContentRng));\n      } else {\n        return Optional.none();\n      }\n    });\n    const getActionFromRange = (root, rng) => {\n      const isRoot = isRootFromElement(root);\n      const optCellRng = getCellRng(rng, isRoot);\n      const selectionDetails = getTableDetailsFromRange(rng, isRoot);\n      if (isSingleCellTableContentSelected(optCellRng, rng, isRoot)) {\n        return optCellRng.map(cellRng => deleteAction.singleCellTable(rng, cellRng.start));\n      } else if (selectionDetails.isMultiTable) {\n        return handleMultiTable(optCellRng, selectionDetails, rng, isRoot);\n      } else {\n        return handleSingleTable(optCellRng, selectionDetails, rng, isRoot);\n      }\n    };\n\n    const cleanCells = cells => each$e(cells, cell => {\n      remove$b(cell, 'contenteditable');\n      fillWithPaddingBr(cell);\n    });\n    const getOutsideBlock = (editor, container) => Optional.from(editor.dom.getParent(container, editor.dom.isBlock)).map(SugarElement.fromDom);\n    const handleEmptyBlock = (editor, startInTable, emptyBlock) => {\n      emptyBlock.each(block => {\n        if (startInTable) {\n          remove$6(block);\n        } else {\n          fillWithPaddingBr(block);\n          editor.selection.setCursorLocation(block.dom, 0);\n        }\n      });\n    };\n    const deleteContentInsideCell = (editor, cell, rng, isFirstCellInSelection) => {\n      const insideTableRng = rng.cloneRange();\n      if (isFirstCellInSelection) {\n        insideTableRng.setStart(rng.startContainer, rng.startOffset);\n        insideTableRng.setEndAfter(cell.dom.lastChild);\n      } else {\n        insideTableRng.setStartBefore(cell.dom.firstChild);\n        insideTableRng.setEnd(rng.endContainer, rng.endOffset);\n      }\n      deleteCellContents(editor, insideTableRng, cell, false).each(action => action());\n    };\n    const collapseAndRestoreCellSelection = editor => {\n      const selectedCells = getCellsFromEditor(editor);\n      const selectedNode = SugarElement.fromDom(editor.selection.getNode());\n      if (isTableCell$3(selectedNode.dom) && isEmpty$2(selectedNode)) {\n        editor.selection.setCursorLocation(selectedNode.dom, 0);\n      } else {\n        editor.selection.collapse(true);\n      }\n      if (selectedCells.length > 1 && exists(selectedCells, cell => eq(cell, selectedNode))) {\n        set$2(selectedNode, 'data-mce-selected', '1');\n      }\n    };\n    const emptySingleTableCells = (editor, cells, outsideDetails) => Optional.some(() => {\n      const editorRng = editor.selection.getRng();\n      const cellsToClean = outsideDetails.bind(({rng, isStartInTable}) => {\n        const outsideBlock = getOutsideBlock(editor, isStartInTable ? rng.endContainer : rng.startContainer);\n        rng.deleteContents();\n        handleEmptyBlock(editor, isStartInTable, outsideBlock.filter(isEmpty$2));\n        const endPointCell = isStartInTable ? cells[0] : cells[cells.length - 1];\n        deleteContentInsideCell(editor, endPointCell, editorRng, isStartInTable);\n        if (!isEmpty$2(endPointCell)) {\n          return Optional.some(isStartInTable ? cells.slice(1) : cells.slice(0, -1));\n        } else {\n          return Optional.none();\n        }\n      }).getOr(cells);\n      cleanCells(cellsToClean);\n      collapseAndRestoreCellSelection(editor);\n    });\n    const emptyMultiTableCells = (editor, startTableCells, endTableCells, betweenRng) => Optional.some(() => {\n      const rng = editor.selection.getRng();\n      const startCell = startTableCells[0];\n      const endCell = endTableCells[endTableCells.length - 1];\n      deleteContentInsideCell(editor, startCell, rng, true);\n      deleteContentInsideCell(editor, endCell, rng, false);\n      const startTableCellsToClean = isEmpty$2(startCell) ? startTableCells : startTableCells.slice(1);\n      const endTableCellsToClean = isEmpty$2(endCell) ? endTableCells : endTableCells.slice(0, -1);\n      cleanCells(startTableCellsToClean.concat(endTableCellsToClean));\n      betweenRng.deleteContents();\n      collapseAndRestoreCellSelection(editor);\n    });\n    const deleteCellContents = (editor, rng, cell, moveSelection = true) => Optional.some(() => {\n      deleteRangeContents(editor, rng, cell, moveSelection);\n    });\n    const deleteTableElement = (editor, table) => Optional.some(() => deleteElement$2(editor, false, table));\n    const deleteCellRange = (editor, rootElm, rng) => getActionFromRange(rootElm, rng).bind(action => action.fold(curry(deleteCellContents, editor), curry(deleteTableElement, editor), curry(emptySingleTableCells, editor), curry(emptyMultiTableCells, editor)));\n    const deleteCaptionRange = (editor, caption) => emptyElement(editor, caption);\n    const deleteTableRange = (editor, rootElm, rng, startElm) => getParentCaption(rootElm, startElm).fold(() => deleteCellRange(editor, rootElm, rng), caption => deleteCaptionRange(editor, caption));\n    const deleteRange$2 = (editor, startElm, selectedCells) => {\n      const rootNode = SugarElement.fromDom(editor.getBody());\n      const rng = editor.selection.getRng();\n      return selectedCells.length !== 0 ? emptySingleTableCells(editor, selectedCells, Optional.none()) : deleteTableRange(editor, rootNode, rng, startElm);\n    };\n    const getParentCell = (rootElm, elm) => find$2(parentsAndSelf(elm, rootElm), isTableCell$2);\n    const getParentCaption = (rootElm, elm) => find$2(parentsAndSelf(elm, rootElm), isTag('caption'));\n    const deleteBetweenCells = (editor, rootElm, forward, fromCell, from) => navigate(forward, editor.getBody(), from).bind(to => getParentCell(rootElm, SugarElement.fromDom(to.getNode())).bind(toCell => eq(toCell, fromCell) ? Optional.none() : Optional.some(noop)));\n    const emptyElement = (editor, elm) => Optional.some(() => {\n      fillWithPaddingBr(elm);\n      editor.selection.setCursorLocation(elm.dom, 0);\n    });\n    const isDeleteOfLastCharPos = (fromCaption, forward, from, to) => firstPositionIn(fromCaption.dom).bind(first => lastPositionIn(fromCaption.dom).map(last => forward ? from.isEqual(first) && to.isEqual(last) : from.isEqual(last) && to.isEqual(first))).getOr(true);\n    const emptyCaretCaption = (editor, elm) => emptyElement(editor, elm);\n    const validateCaretCaption = (rootElm, fromCaption, to) => getParentCaption(rootElm, SugarElement.fromDom(to.getNode())).fold(() => Optional.some(noop), toCaption => someIf(!eq(toCaption, fromCaption), noop));\n    const deleteCaretInsideCaption = (editor, rootElm, forward, fromCaption, from) => navigate(forward, editor.getBody(), from).fold(() => Optional.some(noop), to => isDeleteOfLastCharPos(fromCaption, forward, from, to) ? emptyCaretCaption(editor, fromCaption) : validateCaretCaption(rootElm, fromCaption, to));\n    const deleteCaretCells = (editor, forward, rootElm, startElm) => {\n      const from = CaretPosition.fromRangeStart(editor.selection.getRng());\n      return getParentCell(rootElm, startElm).bind(fromCell => isEmpty$2(fromCell) ? emptyElement(editor, fromCell) : deleteBetweenCells(editor, rootElm, forward, fromCell, from));\n    };\n    const deleteCaretCaption = (editor, forward, rootElm, fromCaption) => {\n      const from = CaretPosition.fromRangeStart(editor.selection.getRng());\n      return isEmpty$2(fromCaption) ? emptyElement(editor, fromCaption) : deleteCaretInsideCaption(editor, rootElm, forward, fromCaption, from);\n    };\n    const isNearTable = (forward, pos) => forward ? isBeforeTable(pos) : isAfterTable(pos);\n    const isBeforeOrAfterTable = (editor, forward) => {\n      const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng());\n      return isNearTable(forward, fromPos) || fromPosition(forward, editor.getBody(), fromPos).exists(pos => isNearTable(forward, pos));\n    };\n    const deleteCaret$3 = (editor, forward, startElm) => {\n      const rootElm = SugarElement.fromDom(editor.getBody());\n      return getParentCaption(rootElm, startElm).fold(() => deleteCaretCells(editor, forward, rootElm, startElm).orThunk(() => someIf(isBeforeOrAfterTable(editor, forward), noop)), fromCaption => deleteCaretCaption(editor, forward, rootElm, fromCaption));\n    };\n    const backspaceDelete$9 = (editor, forward) => {\n      const startElm = SugarElement.fromDom(editor.selection.getStart(true));\n      const cells = getCellsFromEditor(editor);\n      return editor.selection.isCollapsed() && cells.length === 0 ? deleteCaret$3(editor, forward, startElm) : deleteRange$2(editor, startElm, cells);\n    };\n\n    const getContentEditableRoot$1 = (root, node) => {\n      let tempNode = node;\n      while (tempNode && tempNode !== root) {\n        if (isContentEditableTrue$3(tempNode) || isContentEditableFalse$a(tempNode)) {\n          return tempNode;\n        }\n        tempNode = tempNode.parentNode;\n      }\n      return null;\n    };\n\n    const internalAttributesPrefixes = [\n      'data-ephox-',\n      'data-mce-',\n      'data-alloy-',\n      'data-snooker-',\n      '_'\n    ];\n    const each$9 = Tools.each;\n    const ElementUtils = editor => {\n      const dom = editor.dom;\n      const internalAttributes = new Set(editor.serializer.getTempAttrs());\n      const compare = (node1, node2) => {\n        if (node1.nodeName !== node2.nodeName || node1.nodeType !== node2.nodeType) {\n          return false;\n        }\n        const getAttribs = node => {\n          const attribs = {};\n          each$9(dom.getAttribs(node), attr => {\n            const name = attr.nodeName.toLowerCase();\n            if (name !== 'style' && !isAttributeInternal(name)) {\n              attribs[name] = dom.getAttrib(node, name);\n            }\n          });\n          return attribs;\n        };\n        const compareObjects = (obj1, obj2) => {\n          for (const name in obj1) {\n            if (has$2(obj1, name)) {\n              const value = obj2[name];\n              if (isUndefined(value)) {\n                return false;\n              }\n              if (obj1[name] !== value) {\n                return false;\n              }\n              delete obj2[name];\n            }\n          }\n          for (const name in obj2) {\n            if (has$2(obj2, name)) {\n              return false;\n            }\n          }\n          return true;\n        };\n        if (isElement$6(node1) && isElement$6(node2)) {\n          if (!compareObjects(getAttribs(node1), getAttribs(node2))) {\n            return false;\n          }\n          if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {\n            return false;\n          }\n        }\n        return !isBookmarkNode$1(node1) && !isBookmarkNode$1(node2);\n      };\n      const isAttributeInternal = attributeName => exists(internalAttributesPrefixes, value => startsWith(attributeName, value)) || internalAttributes.has(attributeName);\n      return {\n        compare,\n        isAttributeInternal\n      };\n    };\n\n    const traverse = (root, fn) => {\n      let node = root;\n      while (node = node.walk()) {\n        fn(node);\n      }\n    };\n    const matchNode$1 = (nodeFilters, attributeFilters, node, matches) => {\n      const name = node.name;\n      for (let ni = 0, nl = nodeFilters.length; ni < nl; ni++) {\n        const filter = nodeFilters[ni];\n        if (filter.name === name) {\n          const match = matches.nodes[name];\n          if (match) {\n            match.nodes.push(node);\n          } else {\n            matches.nodes[name] = {\n              filter,\n              nodes: [node]\n            };\n          }\n        }\n      }\n      if (node.attributes) {\n        for (let ai = 0, al = attributeFilters.length; ai < al; ai++) {\n          const filter = attributeFilters[ai];\n          const attrName = filter.name;\n          if (attrName in node.attributes.map) {\n            const match = matches.attributes[attrName];\n            if (match) {\n              match.nodes.push(node);\n            } else {\n              matches.attributes[attrName] = {\n                filter,\n                nodes: [node]\n              };\n            }\n          }\n        }\n      }\n    };\n    const findMatchingNodes = (nodeFilters, attributeFilters, node) => {\n      const matches = {\n        nodes: {},\n        attributes: {}\n      };\n      if (node.firstChild) {\n        traverse(node, childNode => {\n          matchNode$1(nodeFilters, attributeFilters, childNode, matches);\n        });\n      }\n      return matches;\n    };\n    const runFilters = (matches, args) => {\n      const run = (matchRecord, filteringAttributes) => {\n        each$d(matchRecord, match => {\n          const nodes = from(match.nodes);\n          each$e(match.filter.callbacks, callback => {\n            for (let i = nodes.length - 1; i >= 0; i--) {\n              const node = nodes[i];\n              const valueMatches = filteringAttributes ? node.attr(match.filter.name) !== undefined : node.name === match.filter.name;\n              if (!valueMatches || isNullable(node.parent)) {\n                nodes.splice(i, 1);\n              }\n            }\n            if (nodes.length > 0) {\n              callback(nodes, match.filter.name, args);\n            }\n          });\n        });\n      };\n      run(matches.nodes, false);\n      run(matches.attributes, true);\n    };\n    const filter$2 = (nodeFilters, attributeFilters, node, args = {}) => {\n      const matches = findMatchingNodes(nodeFilters, attributeFilters, node);\n      runFilters(matches, args);\n    };\n\n    const paddEmptyNode = (args, isBlock, node) => {\n      if (args.insert && isBlock(node)) {\n        const astNode = new AstNode('br', 1);\n        astNode.attr('data-mce-bogus', '1');\n        node.empty().append(astNode);\n      } else {\n        node.empty().append(new AstNode('#text', 3)).value = nbsp;\n      }\n    };\n    const isPaddedWithNbsp = node => {\n      var _a;\n      return hasOnlyChild(node, '#text') && ((_a = node === null || node === void 0 ? void 0 : node.firstChild) === null || _a === void 0 ? void 0 : _a.value) === nbsp;\n    };\n    const hasOnlyChild = (node, name) => {\n      const firstChild = node === null || node === void 0 ? void 0 : node.firstChild;\n      return isNonNullable(firstChild) && firstChild === node.lastChild && firstChild.name === name;\n    };\n    const isPadded = (schema, node) => {\n      const rule = schema.getElementRule(node.name);\n      return (rule === null || rule === void 0 ? void 0 : rule.paddEmpty) === true;\n    };\n    const isEmpty = (schema, nonEmptyElements, whitespaceElements, node) => node.isEmpty(nonEmptyElements, whitespaceElements, node => isPadded(schema, node));\n    const isLineBreakNode = (node, isBlock) => isNonNullable(node) && (isBlock(node) || node.name === 'br');\n\n    const removeOrUnwrapInvalidNode = (node, schema, originalNodeParent = node.parent) => {\n      if (schema.getSpecialElements()[node.name]) {\n        node.empty().remove();\n      } else {\n        const children = node.children();\n        for (const childNode of children) {\n          if (originalNodeParent && !schema.isValidChild(originalNodeParent.name, childNode.name)) {\n            removeOrUnwrapInvalidNode(childNode, schema, originalNodeParent);\n          }\n        }\n        node.unwrap();\n      }\n    };\n    const cleanInvalidNodes = (nodes, schema, onCreate = noop) => {\n      const textBlockElements = schema.getTextBlockElements();\n      const nonEmptyElements = schema.getNonEmptyElements();\n      const whitespaceElements = schema.getWhitespaceElements();\n      const nonSplittableElements = Tools.makeMap('tr,td,th,tbody,thead,tfoot,table');\n      const fixed = new Set();\n      for (let ni = 0; ni < nodes.length; ni++) {\n        const node = nodes[ni];\n        let parent;\n        let newParent;\n        let tempNode;\n        if (!node.parent || fixed.has(node)) {\n          continue;\n        }\n        if (textBlockElements[node.name] && node.parent.name === 'li') {\n          let sibling = node.next;\n          while (sibling) {\n            if (textBlockElements[sibling.name]) {\n              sibling.name = 'li';\n              fixed.add(sibling);\n              node.parent.insert(sibling, node.parent);\n            } else {\n              break;\n            }\n            sibling = sibling.next;\n          }\n          node.unwrap();\n          continue;\n        }\n        const parents = [node];\n        for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && !nonSplittableElements[parent.name]; parent = parent.parent) {\n          parents.push(parent);\n        }\n        if (parent && parents.length > 1) {\n          if (schema.isValidChild(parent.name, node.name)) {\n            parents.reverse();\n            newParent = parents[0].clone();\n            onCreate(newParent);\n            let currentNode = newParent;\n            for (let i = 0; i < parents.length - 1; i++) {\n              if (schema.isValidChild(currentNode.name, parents[i].name)) {\n                tempNode = parents[i].clone();\n                onCreate(tempNode);\n                currentNode.append(tempNode);\n              } else {\n                tempNode = currentNode;\n              }\n              for (let childNode = parents[i].firstChild; childNode && childNode !== parents[i + 1];) {\n                const nextNode = childNode.next;\n                tempNode.append(childNode);\n                childNode = nextNode;\n              }\n              currentNode = tempNode;\n            }\n            if (!isEmpty(schema, nonEmptyElements, whitespaceElements, newParent)) {\n              parent.insert(newParent, parents[0], true);\n              parent.insert(node, newParent);\n            } else {\n              parent.insert(node, parents[0], true);\n            }\n            parent = parents[0];\n            if (isEmpty(schema, nonEmptyElements, whitespaceElements, parent) || hasOnlyChild(parent, 'br')) {\n              parent.empty().remove();\n            }\n          } else {\n            removeOrUnwrapInvalidNode(node, schema);\n          }\n        } else if (node.parent) {\n          if (node.name === 'li') {\n            let sibling = node.prev;\n            if (sibling && (sibling.name === 'ul' || sibling.name === 'ol')) {\n              sibling.append(node);\n              continue;\n            }\n            sibling = node.next;\n            if (sibling && (sibling.name === 'ul' || sibling.name === 'ol') && sibling.firstChild) {\n              sibling.insert(node, sibling.firstChild, true);\n              continue;\n            }\n            const wrapper = new AstNode('ul', 1);\n            onCreate(wrapper);\n            node.wrap(wrapper);\n            continue;\n          }\n          if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {\n            const wrapper = new AstNode('div', 1);\n            onCreate(wrapper);\n            node.wrap(wrapper);\n          } else {\n            removeOrUnwrapInvalidNode(node, schema);\n          }\n        }\n      }\n    };\n    const hasClosest = (node, parentName) => {\n      let tempNode = node;\n      while (tempNode) {\n        if (tempNode.name === parentName) {\n          return true;\n        }\n        tempNode = tempNode.parent;\n      }\n      return false;\n    };\n    const isInvalid = (schema, node, parent = node.parent) => {\n      if (parent && schema.children[node.name] && !schema.isValidChild(parent.name, node.name)) {\n        return true;\n      } else if (parent && node.name === 'a' && hasClosest(parent, 'a')) {\n        return true;\n      } else {\n        return false;\n      }\n    };\n\n    const createRange = (sc, so, ec, eo) => {\n      const rng = document.createRange();\n      rng.setStart(sc, so);\n      rng.setEnd(ec, eo);\n      return rng;\n    };\n    const normalizeBlockSelectionRange = rng => {\n      const startPos = CaretPosition.fromRangeStart(rng);\n      const endPos = CaretPosition.fromRangeEnd(rng);\n      const rootNode = rng.commonAncestorContainer;\n      return fromPosition(false, rootNode, endPos).map(newEndPos => {\n        if (!isInSameBlock(startPos, endPos, rootNode) && isInSameBlock(startPos, newEndPos, rootNode)) {\n          return createRange(startPos.container(), startPos.offset(), newEndPos.container(), newEndPos.offset());\n        } else {\n          return rng;\n        }\n      }).getOr(rng);\n    };\n    const normalize = rng => rng.collapsed ? rng : normalizeBlockSelectionRange(rng);\n\n    const hasOnlyOneChild$1 = node => {\n      return isNonNullable(node.firstChild) && node.firstChild === node.lastChild;\n    };\n    const isPaddingNode = node => {\n      return node.name === 'br' || node.value === nbsp;\n    };\n    const isPaddedEmptyBlock = (schema, node) => {\n      const blockElements = schema.getBlockElements();\n      return blockElements[node.name] && hasOnlyOneChild$1(node) && isPaddingNode(node.firstChild);\n    };\n    const isEmptyFragmentElement = (schema, node) => {\n      const nonEmptyElements = schema.getNonEmptyElements();\n      return isNonNullable(node) && (node.isEmpty(nonEmptyElements) || isPaddedEmptyBlock(schema, node));\n    };\n    const isListFragment = (schema, fragment) => {\n      let firstChild = fragment.firstChild;\n      let lastChild = fragment.lastChild;\n      if (firstChild && firstChild.name === 'meta') {\n        firstChild = firstChild.next;\n      }\n      if (lastChild && lastChild.attr('id') === 'mce_marker') {\n        lastChild = lastChild.prev;\n      }\n      if (isEmptyFragmentElement(schema, lastChild)) {\n        lastChild = lastChild === null || lastChild === void 0 ? void 0 : lastChild.prev;\n      }\n      if (!firstChild || firstChild !== lastChild) {\n        return false;\n      }\n      return firstChild.name === 'ul' || firstChild.name === 'ol';\n    };\n    const cleanupDomFragment = domFragment => {\n      var _a, _b;\n      const firstChild = domFragment.firstChild;\n      const lastChild = domFragment.lastChild;\n      if (firstChild && firstChild.nodeName === 'META') {\n        (_a = firstChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(firstChild);\n      }\n      if (lastChild && lastChild.id === 'mce_marker') {\n        (_b = lastChild.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(lastChild);\n      }\n      return domFragment;\n    };\n    const toDomFragment = (dom, serializer, fragment) => {\n      const html = serializer.serialize(fragment);\n      const domFragment = dom.createFragment(html);\n      return cleanupDomFragment(domFragment);\n    };\n    const listItems = elm => {\n      var _a;\n      return filter$5((_a = elm === null || elm === void 0 ? void 0 : elm.childNodes) !== null && _a !== void 0 ? _a : [], child => {\n        return child.nodeName === 'LI';\n      });\n    };\n    const isPadding = node => {\n      return node.data === nbsp || isBr$6(node);\n    };\n    const isListItemPadded = node => {\n      return isNonNullable(node === null || node === void 0 ? void 0 : node.firstChild) && node.firstChild === node.lastChild && isPadding(node.firstChild);\n    };\n    const isEmptyOrPadded = elm => {\n      return !elm.firstChild || isListItemPadded(elm);\n    };\n    const trimListItems = elms => {\n      return elms.length > 0 && isEmptyOrPadded(elms[elms.length - 1]) ? elms.slice(0, -1) : elms;\n    };\n    const getParentLi = (dom, node) => {\n      const parentBlock = dom.getParent(node, dom.isBlock);\n      return parentBlock && parentBlock.nodeName === 'LI' ? parentBlock : null;\n    };\n    const isParentBlockLi = (dom, node) => {\n      return !!getParentLi(dom, node);\n    };\n    const getSplit = (parentNode, rng) => {\n      const beforeRng = rng.cloneRange();\n      const afterRng = rng.cloneRange();\n      beforeRng.setStartBefore(parentNode);\n      afterRng.setEndAfter(parentNode);\n      return [\n        beforeRng.cloneContents(),\n        afterRng.cloneContents()\n      ];\n    };\n    const findFirstIn = (node, rootNode) => {\n      const caretPos = CaretPosition.before(node);\n      const caretWalker = CaretWalker(rootNode);\n      const newCaretPos = caretWalker.next(caretPos);\n      return newCaretPos ? newCaretPos.toRange() : null;\n    };\n    const findLastOf = (node, rootNode) => {\n      const caretPos = CaretPosition.after(node);\n      const caretWalker = CaretWalker(rootNode);\n      const newCaretPos = caretWalker.prev(caretPos);\n      return newCaretPos ? newCaretPos.toRange() : null;\n    };\n    const insertMiddle = (target, elms, rootNode, rng) => {\n      const parts = getSplit(target, rng);\n      const parentElm = target.parentNode;\n      if (parentElm) {\n        parentElm.insertBefore(parts[0], target);\n        Tools.each(elms, li => {\n          parentElm.insertBefore(li, target);\n        });\n        parentElm.insertBefore(parts[1], target);\n        parentElm.removeChild(target);\n      }\n      return findLastOf(elms[elms.length - 1], rootNode);\n    };\n    const insertBefore$1 = (target, elms, rootNode) => {\n      const parentElm = target.parentNode;\n      if (parentElm) {\n        Tools.each(elms, elm => {\n          parentElm.insertBefore(elm, target);\n        });\n      }\n      return findFirstIn(target, rootNode);\n    };\n    const insertAfter$1 = (target, elms, rootNode, dom) => {\n      dom.insertAfter(elms.reverse(), target);\n      return findLastOf(elms[0], rootNode);\n    };\n    const insertAtCaret$1 = (serializer, dom, rng, fragment) => {\n      const domFragment = toDomFragment(dom, serializer, fragment);\n      const liTarget = getParentLi(dom, rng.startContainer);\n      const liElms = trimListItems(listItems(domFragment.firstChild));\n      const BEGINNING = 1, END = 2;\n      const rootNode = dom.getRoot();\n      const isAt = location => {\n        const caretPos = CaretPosition.fromRangeStart(rng);\n        const caretWalker = CaretWalker(dom.getRoot());\n        const newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos);\n        const newPosNode = newPos === null || newPos === void 0 ? void 0 : newPos.getNode();\n        return newPosNode ? getParentLi(dom, newPosNode) !== liTarget : true;\n      };\n      if (!liTarget) {\n        return null;\n      } else if (isAt(BEGINNING)) {\n        return insertBefore$1(liTarget, liElms, rootNode);\n      } else if (isAt(END)) {\n        return insertAfter$1(liTarget, liElms, rootNode, dom);\n      } else {\n        return insertMiddle(liTarget, liElms, rootNode, rng);\n      }\n    };\n\n    const mergeableWrappedElements = ['pre'];\n    const shouldPasteContentOnly = (dom, fragment, parentNode, root) => {\n      var _a;\n      const firstNode = fragment.firstChild;\n      const lastNode = fragment.lastChild;\n      const last = lastNode.attr('data-mce-type') === 'bookmark' ? lastNode.prev : lastNode;\n      const isPastingSingleElement = firstNode === last;\n      const isWrappedElement = contains$2(mergeableWrappedElements, firstNode.name);\n      if (isPastingSingleElement && isWrappedElement) {\n        const isContentEditable = firstNode.attr('contenteditable') !== 'false';\n        const isPastingInTheSameBlockTag = ((_a = dom.getParent(parentNode, dom.isBlock)) === null || _a === void 0 ? void 0 : _a.nodeName.toLowerCase()) === firstNode.name;\n        const isPastingInContentEditable = Optional.from(getContentEditableRoot$1(root, parentNode)).forall(isContentEditableTrue$3);\n        return isContentEditable && isPastingInTheSameBlockTag && isPastingInContentEditable;\n      } else {\n        return false;\n      }\n    };\n    const isTableCell = isTableCell$3;\n    const isTableCellContentSelected = (dom, rng, cell) => {\n      if (isNonNullable(cell)) {\n        const endCell = dom.getParent(rng.endContainer, isTableCell);\n        return cell === endCell && hasAllContentsSelected(SugarElement.fromDom(cell), rng);\n      } else {\n        return false;\n      }\n    };\n    const validInsertion = (editor, value, parentNode) => {\n      var _a;\n      if (parentNode.getAttribute('data-mce-bogus') === 'all') {\n        (_a = parentNode.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(editor.dom.createFragment(value), parentNode);\n      } else {\n        const node = parentNode.firstChild;\n        const node2 = parentNode.lastChild;\n        if (!node || node === node2 && node.nodeName === 'BR') {\n          editor.dom.setHTML(parentNode, value);\n        } else {\n          editor.selection.setContent(value, { no_events: true });\n        }\n      }\n    };\n    const trimBrsFromTableCell = (dom, elm) => {\n      Optional.from(dom.getParent(elm, 'td,th')).map(SugarElement.fromDom).each(trimBlockTrailingBr);\n    };\n    const reduceInlineTextElements = (editor, merge) => {\n      const textInlineElements = editor.schema.getTextInlineElements();\n      const dom = editor.dom;\n      if (merge) {\n        const root = editor.getBody();\n        const elementUtils = ElementUtils(editor);\n        Tools.each(dom.select('*[data-mce-fragment]'), node => {\n          const isInline = isNonNullable(textInlineElements[node.nodeName.toLowerCase()]);\n          if (isInline && hasInheritableStyles(dom, node)) {\n            for (let parentNode = node.parentElement; isNonNullable(parentNode) && parentNode !== root; parentNode = parentNode.parentElement) {\n              const styleConflict = hasStyleConflict(dom, node, parentNode);\n              if (styleConflict) {\n                break;\n              }\n              if (elementUtils.compare(parentNode, node)) {\n                dom.remove(node, true);\n                break;\n              }\n            }\n          }\n        });\n      }\n    };\n    const markFragmentElements = fragment => {\n      let node = fragment;\n      while (node = node.walk()) {\n        if (node.type === 1) {\n          node.attr('data-mce-fragment', '1');\n        }\n      }\n    };\n    const unmarkFragmentElements = elm => {\n      Tools.each(elm.getElementsByTagName('*'), elm => {\n        elm.removeAttribute('data-mce-fragment');\n      });\n    };\n    const isPartOfFragment = node => {\n      return !!node.getAttribute('data-mce-fragment');\n    };\n    const canHaveChildren = (editor, node) => {\n      return isNonNullable(node) && !editor.schema.getVoidElements()[node.nodeName];\n    };\n    const moveSelectionToMarker = (editor, marker) => {\n      var _a, _b, _c;\n      let nextRng;\n      const dom = editor.dom;\n      const selection = editor.selection;\n      if (!marker) {\n        return;\n      }\n      selection.scrollIntoView(marker);\n      const parentEditableElm = getContentEditableRoot$1(editor.getBody(), marker);\n      if (parentEditableElm && dom.getContentEditable(parentEditableElm) === 'false') {\n        dom.remove(marker);\n        selection.select(parentEditableElm);\n        return;\n      }\n      let rng = dom.createRng();\n      const node = marker.previousSibling;\n      if (isText$a(node)) {\n        rng.setStart(node, (_b = (_a = node.nodeValue) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0);\n        const node2 = marker.nextSibling;\n        if (isText$a(node2)) {\n          node.appendData(node2.data);\n          (_c = node2.parentNode) === null || _c === void 0 ? void 0 : _c.removeChild(node2);\n        }\n      } else {\n        rng.setStartBefore(marker);\n        rng.setEndBefore(marker);\n      }\n      const findNextCaretRng = rng => {\n        let caretPos = CaretPosition.fromRangeStart(rng);\n        const caretWalker = CaretWalker(editor.getBody());\n        caretPos = caretWalker.next(caretPos);\n        return caretPos === null || caretPos === void 0 ? void 0 : caretPos.toRange();\n      };\n      const parentBlock = dom.getParent(marker, dom.isBlock);\n      dom.remove(marker);\n      if (parentBlock && dom.isEmpty(parentBlock)) {\n        empty(SugarElement.fromDom(parentBlock));\n        rng.setStart(parentBlock, 0);\n        rng.setEnd(parentBlock, 0);\n        if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) {\n          rng = nextRng;\n          dom.remove(parentBlock);\n        } else {\n          dom.add(parentBlock, dom.create('br', { 'data-mce-bogus': '1' }));\n        }\n      }\n      selection.setRng(rng);\n    };\n    const deleteSelectedContent = editor => {\n      const dom = editor.dom;\n      const rng = normalize(editor.selection.getRng());\n      editor.selection.setRng(rng);\n      const startCell = dom.getParent(rng.startContainer, isTableCell);\n      if (isTableCellContentSelected(dom, rng, startCell)) {\n        deleteCellContents(editor, rng, SugarElement.fromDom(startCell));\n      } else if (rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset === 1 && isText$a(rng.startContainer.childNodes[rng.startOffset])) {\n        rng.deleteContents();\n      } else {\n        editor.getDoc().execCommand('Delete', false);\n      }\n    };\n    const insertHtmlAtCaret = (editor, value, details) => {\n      var _a, _b;\n      const selection = editor.selection;\n      const dom = editor.dom;\n      const parser = editor.parser;\n      const merge = details.merge;\n      const serializer = HtmlSerializer({ validate: true }, editor.schema);\n      const bookmarkHtml = '<span id=\"mce_marker\" data-mce-type=\"bookmark\">&#xFEFF;</span>';\n      if (value.indexOf('{$caret}') === -1) {\n        value += '{$caret}';\n      }\n      value = value.replace(/\\{\\$caret\\}/, bookmarkHtml);\n      let rng = selection.getRng();\n      const caretElement = rng.startContainer;\n      const body = editor.getBody();\n      if (caretElement === body && selection.isCollapsed()) {\n        if (dom.isBlock(body.firstChild) && canHaveChildren(editor, body.firstChild) && dom.isEmpty(body.firstChild)) {\n          rng = dom.createRng();\n          rng.setStart(body.firstChild, 0);\n          rng.setEnd(body.firstChild, 0);\n          selection.setRng(rng);\n        }\n      }\n      if (!selection.isCollapsed()) {\n        deleteSelectedContent(editor);\n      }\n      const parentNode = selection.getNode();\n      const parserArgs = {\n        context: parentNode.nodeName.toLowerCase(),\n        data: details.data,\n        insert: true\n      };\n      const fragment = parser.parse(value, parserArgs);\n      if (details.paste === true && isListFragment(editor.schema, fragment) && isParentBlockLi(dom, parentNode)) {\n        rng = insertAtCaret$1(serializer, dom, selection.getRng(), fragment);\n        if (rng) {\n          selection.setRng(rng);\n        }\n        return value;\n      }\n      if (details.paste === true && shouldPasteContentOnly(dom, fragment, parentNode, editor.getBody())) {\n        (_a = fragment.firstChild) === null || _a === void 0 ? void 0 : _a.unwrap();\n      }\n      markFragmentElements(fragment);\n      let node = fragment.lastChild;\n      if (node && node.attr('id') === 'mce_marker') {\n        const marker = node;\n        for (node = node.prev; node; node = node.walk(true)) {\n          if (node.type === 3 || !dom.isBlock(node.name)) {\n            if (node.parent && editor.schema.isValidChild(node.parent.name, 'span')) {\n              node.parent.insert(marker, node, node.name === 'br');\n            }\n            break;\n          }\n        }\n      }\n      editor._selectionOverrides.showBlockCaretContainer(parentNode);\n      if (!parserArgs.invalid) {\n        value = serializer.serialize(fragment);\n        validInsertion(editor, value, parentNode);\n      } else {\n        editor.selection.setContent(bookmarkHtml);\n        let parentNode = selection.getNode();\n        let tempNode;\n        const rootNode = editor.getBody();\n        if (isDocument$1(parentNode)) {\n          parentNode = tempNode = rootNode;\n        } else {\n          tempNode = parentNode;\n        }\n        while (tempNode && tempNode !== rootNode) {\n          parentNode = tempNode;\n          tempNode = tempNode.parentNode;\n        }\n        value = parentNode === rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);\n        const root = parser.parse(value);\n        for (let markerNode = root; markerNode; markerNode = markerNode.walk()) {\n          if (markerNode.attr('id') === 'mce_marker') {\n            markerNode.replace(fragment);\n            break;\n          }\n        }\n        const toExtract = fragment.children();\n        const parent = (_b = fragment.parent) !== null && _b !== void 0 ? _b : root;\n        fragment.unwrap();\n        const invalidChildren = filter$5(toExtract, node => isInvalid(editor.schema, node, parent));\n        cleanInvalidNodes(invalidChildren, editor.schema);\n        filter$2(parser.getNodeFilters(), parser.getAttributeFilters(), root);\n        value = serializer.serialize(root);\n        if (parentNode === rootNode) {\n          dom.setHTML(rootNode, value);\n        } else {\n          dom.setOuterHTML(parentNode, value);\n        }\n      }\n      reduceInlineTextElements(editor, merge);\n      moveSelectionToMarker(editor, dom.get('mce_marker'));\n      unmarkFragmentElements(editor.getBody());\n      trimBrsFromTableCell(dom, selection.getStart());\n      updateCaret(editor.schema, editor.getBody(), selection.getStart());\n      return value;\n    };\n\n    const isTreeNode = content => content instanceof AstNode;\n\n    const moveSelection = editor => {\n      if (hasFocus(editor)) {\n        firstPositionIn(editor.getBody()).each(pos => {\n          const node = pos.getNode();\n          const caretPos = isTable$2(node) ? firstPositionIn(node).getOr(pos) : pos;\n          editor.selection.setRng(caretPos.toRange());\n        });\n      }\n    };\n    const setEditorHtml = (editor, html, noSelection) => {\n      editor.dom.setHTML(editor.getBody(), html);\n      if (noSelection !== true) {\n        moveSelection(editor);\n      }\n    };\n    const setContentString = (editor, body, content, args) => {\n      if (content.length === 0 || /^\\s+$/.test(content)) {\n        const padd = '<br data-mce-bogus=\"1\">';\n        if (body.nodeName === 'TABLE') {\n          content = '<tr><td>' + padd + '</td></tr>';\n        } else if (/^(UL|OL)$/.test(body.nodeName)) {\n          content = '<li>' + padd + '</li>';\n        }\n        const forcedRootBlockName = getForcedRootBlock(editor);\n        if (editor.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {\n          content = padd;\n          content = editor.dom.createHTML(forcedRootBlockName, getForcedRootBlockAttrs(editor), content);\n        } else if (!content) {\n          content = padd;\n        }\n        setEditorHtml(editor, content, args.no_selection);\n        return {\n          content,\n          html: content\n        };\n      } else {\n        if (args.format !== 'raw') {\n          content = HtmlSerializer({ validate: false }, editor.schema).serialize(editor.parser.parse(content, {\n            isRootContent: true,\n            insert: true\n          }));\n        }\n        const trimmedHtml = isWsPreserveElement(SugarElement.fromDom(body)) ? content : Tools.trim(content);\n        setEditorHtml(editor, trimmedHtml, args.no_selection);\n        return {\n          content: trimmedHtml,\n          html: trimmedHtml\n        };\n      }\n    };\n    const setContentTree = (editor, body, content, args) => {\n      filter$2(editor.parser.getNodeFilters(), editor.parser.getAttributeFilters(), content);\n      const html = HtmlSerializer({ validate: false }, editor.schema).serialize(content);\n      const trimmedHtml = isWsPreserveElement(SugarElement.fromDom(body)) ? html : Tools.trim(html);\n      setEditorHtml(editor, trimmedHtml, args.no_selection);\n      return {\n        content,\n        html: trimmedHtml\n      };\n    };\n    const setContentInternal = (editor, content, args) => {\n      return Optional.from(editor.getBody()).map(body => {\n        if (isTreeNode(content)) {\n          return setContentTree(editor, body, content, args);\n        } else {\n          return setContentString(editor, body, content, args);\n        }\n      }).getOr({\n        content,\n        html: isTreeNode(args.content) ? '' : args.content\n      });\n    };\n\n    const sibling = (scope, predicate) => sibling$1(scope, predicate).isSome();\n\n    const ensureIsRoot = isRoot => isFunction(isRoot) ? isRoot : never;\n    const ancestor = (scope, transform, isRoot) => {\n      let element = scope.dom;\n      const stop = ensureIsRoot(isRoot);\n      while (element.parentNode) {\n        element = element.parentNode;\n        const el = SugarElement.fromDom(element);\n        const transformed = transform(el);\n        if (transformed.isSome()) {\n          return transformed;\n        } else if (stop(el)) {\n          break;\n        }\n      }\n      return Optional.none();\n    };\n    const closest$2 = (scope, transform, isRoot) => {\n      const current = transform(scope);\n      const stop = ensureIsRoot(isRoot);\n      return current.orThunk(() => stop(scope) ? Optional.none() : ancestor(scope, transform, stop));\n    };\n\n    const isEq$3 = isEq$5;\n    const matchesUnInheritedFormatSelector = (ed, node, name) => {\n      const formatList = ed.formatter.get(name);\n      if (formatList) {\n        for (let i = 0; i < formatList.length; i++) {\n          const format = formatList[i];\n          if (isSelectorFormat(format) && format.inherit === false && ed.dom.is(node, format.selector)) {\n            return true;\n          }\n        }\n      }\n      return false;\n    };\n    const matchParents = (editor, node, name, vars, similar) => {\n      const root = editor.dom.getRoot();\n      if (node === root) {\n        return false;\n      }\n      const matchedNode = editor.dom.getParent(node, elm => {\n        if (matchesUnInheritedFormatSelector(editor, elm, name)) {\n          return true;\n        }\n        return elm.parentNode === root || !!matchNode(editor, elm, name, vars, true);\n      });\n      return !!matchNode(editor, matchedNode, name, vars, similar);\n    };\n    const matchName = (dom, node, format) => {\n      if (isInlineFormat(format) && isEq$3(node, format.inline)) {\n        return true;\n      }\n      if (isBlockFormat(format) && isEq$3(node, format.block)) {\n        return true;\n      }\n      if (isSelectorFormat(format)) {\n        return isElement$6(node) && dom.is(node, format.selector);\n      }\n      return false;\n    };\n    const matchItems = (dom, node, format, itemName, similar, vars) => {\n      const items = format[itemName];\n      const matchAttributes = itemName === 'attributes';\n      if (isFunction(format.onmatch)) {\n        return format.onmatch(node, format, itemName);\n      }\n      if (items) {\n        if (!isArrayLike(items)) {\n          for (const key in items) {\n            if (has$2(items, key)) {\n              const value = matchAttributes ? dom.getAttrib(node, key) : getStyle(dom, node, key);\n              const expectedValue = replaceVars(items[key], vars);\n              const isEmptyValue = isNullable(value) || isEmpty$3(value);\n              if (isEmptyValue && isNullable(expectedValue)) {\n                continue;\n              }\n              if (similar && isEmptyValue && !format.exact) {\n                return false;\n              }\n              if ((!similar || format.exact) && !isEq$3(value, normalizeStyleValue(expectedValue, key))) {\n                return false;\n              }\n            }\n          }\n        } else {\n          for (let i = 0; i < items.length; i++) {\n            if (matchAttributes ? dom.getAttrib(node, items[i]) : getStyle(dom, node, items[i])) {\n              return true;\n            }\n          }\n        }\n      }\n      return true;\n    };\n    const matchNode = (ed, node, name, vars, similar) => {\n      const formatList = ed.formatter.get(name);\n      const dom = ed.dom;\n      if (formatList && isElement$6(node)) {\n        for (let i = 0; i < formatList.length; i++) {\n          const format = formatList[i];\n          if (matchName(ed.dom, node, format) && matchItems(dom, node, format, 'attributes', similar, vars) && matchItems(dom, node, format, 'styles', similar, vars)) {\n            const classes = format.classes;\n            if (classes) {\n              for (let x = 0; x < classes.length; x++) {\n                if (!ed.dom.hasClass(node, replaceVars(classes[x], vars))) {\n                  return;\n                }\n              }\n            }\n            return format;\n          }\n        }\n      }\n      return undefined;\n    };\n    const match$2 = (editor, name, vars, node, similar) => {\n      if (node) {\n        return matchParents(editor, node, name, vars, similar);\n      }\n      node = editor.selection.getNode();\n      if (matchParents(editor, node, name, vars, similar)) {\n        return true;\n      }\n      const startNode = editor.selection.getStart();\n      if (startNode !== node) {\n        if (matchParents(editor, startNode, name, vars, similar)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const matchAll = (editor, names, vars) => {\n      const matchedFormatNames = [];\n      const checkedMap = {};\n      const startElement = editor.selection.getStart();\n      editor.dom.getParent(startElement, node => {\n        for (let i = 0; i < names.length; i++) {\n          const name = names[i];\n          if (!checkedMap[name] && matchNode(editor, node, name, vars)) {\n            checkedMap[name] = true;\n            matchedFormatNames.push(name);\n          }\n        }\n      }, editor.dom.getRoot());\n      return matchedFormatNames;\n    };\n    const closest$1 = (editor, names) => {\n      const isRoot = elm => eq(elm, SugarElement.fromDom(editor.getBody()));\n      const match = (elm, name) => matchNode(editor, elm.dom, name) ? Optional.some(name) : Optional.none();\n      return Optional.from(editor.selection.getStart(true)).bind(rawElm => closest$2(SugarElement.fromDom(rawElm), elm => findMap(names, name => match(elm, name)), isRoot)).getOrNull();\n    };\n    const canApply = (editor, name) => {\n      const formatList = editor.formatter.get(name);\n      const dom = editor.dom;\n      if (formatList) {\n        const startNode = editor.selection.getStart();\n        const parents = getParents$2(dom, startNode);\n        for (let x = formatList.length - 1; x >= 0; x--) {\n          const format = formatList[x];\n          if (!isSelectorFormat(format)) {\n            return true;\n          }\n          for (let i = parents.length - 1; i >= 0; i--) {\n            if (dom.is(parents[i], format.selector)) {\n              return true;\n            }\n          }\n        }\n      }\n      return false;\n    };\n    const matchAllOnNode = (editor, node, formatNames) => foldl(formatNames, (acc, name) => {\n      const matchSimilar = isVariableFormatName(editor, name);\n      if (editor.formatter.matchNode(node, name, {}, matchSimilar)) {\n        return acc.concat([name]);\n      } else {\n        return acc;\n      }\n    }, []);\n\n    const ZWSP = ZWSP$1;\n    const importNode = (ownerDocument, node) => {\n      return ownerDocument.importNode(node, true);\n    };\n    const getEmptyCaretContainers = node => {\n      const nodes = [];\n      let tempNode = node;\n      while (tempNode) {\n        if (isText$a(tempNode) && tempNode.data !== ZWSP || tempNode.childNodes.length > 1) {\n          return [];\n        }\n        if (isElement$6(tempNode)) {\n          nodes.push(tempNode);\n        }\n        tempNode = tempNode.firstChild;\n      }\n      return nodes;\n    };\n    const isCaretContainerEmpty = node => {\n      return getEmptyCaretContainers(node).length > 0;\n    };\n    const findFirstTextNode = node => {\n      if (node) {\n        const walker = new DomTreeWalker(node, node);\n        for (let tempNode = walker.current(); tempNode; tempNode = walker.next()) {\n          if (isText$a(tempNode)) {\n            return tempNode;\n          }\n        }\n      }\n      return null;\n    };\n    const createCaretContainer = fill => {\n      const caretContainer = SugarElement.fromTag('span');\n      setAll$1(caretContainer, {\n        'id': CARET_ID,\n        'data-mce-bogus': '1',\n        'data-mce-type': 'format-caret'\n      });\n      if (fill) {\n        append$1(caretContainer, SugarElement.fromText(ZWSP));\n      }\n      return caretContainer;\n    };\n    const trimZwspFromCaretContainer = caretContainerNode => {\n      const textNode = findFirstTextNode(caretContainerNode);\n      if (textNode && textNode.data.charAt(0) === ZWSP) {\n        textNode.deleteData(0, 1);\n      }\n      return textNode;\n    };\n    const removeCaretContainerNode = (editor, node, moveCaret = true) => {\n      const dom = editor.dom, selection = editor.selection;\n      if (isCaretContainerEmpty(node)) {\n        deleteElement$2(editor, false, SugarElement.fromDom(node), moveCaret);\n      } else {\n        const rng = selection.getRng();\n        const block = dom.getParent(node, dom.isBlock);\n        const startContainer = rng.startContainer;\n        const startOffset = rng.startOffset;\n        const endContainer = rng.endContainer;\n        const endOffset = rng.endOffset;\n        const textNode = trimZwspFromCaretContainer(node);\n        dom.remove(node, true);\n        if (startContainer === textNode && startOffset > 0) {\n          rng.setStart(textNode, startOffset - 1);\n        }\n        if (endContainer === textNode && endOffset > 0) {\n          rng.setEnd(textNode, endOffset - 1);\n        }\n        if (block && dom.isEmpty(block)) {\n          fillWithPaddingBr(SugarElement.fromDom(block));\n        }\n        selection.setRng(rng);\n      }\n    };\n    const removeCaretContainer = (editor, node, moveCaret = true) => {\n      const dom = editor.dom, selection = editor.selection;\n      if (!node) {\n        node = getParentCaretContainer(editor.getBody(), selection.getStart());\n        if (!node) {\n          while (node = dom.get(CARET_ID)) {\n            removeCaretContainerNode(editor, node, false);\n          }\n        }\n      } else {\n        removeCaretContainerNode(editor, node, moveCaret);\n      }\n    };\n    const insertCaretContainerNode = (editor, caretContainer, formatNode) => {\n      var _a, _b;\n      const dom = editor.dom;\n      const block = dom.getParent(formatNode, curry(isTextBlock$1, editor.schema));\n      if (block && dom.isEmpty(block)) {\n        (_a = formatNode.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(caretContainer, formatNode);\n      } else {\n        removeTrailingBr(SugarElement.fromDom(formatNode));\n        if (dom.isEmpty(formatNode)) {\n          (_b = formatNode.parentNode) === null || _b === void 0 ? void 0 : _b.replaceChild(caretContainer, formatNode);\n        } else {\n          dom.insertAfter(caretContainer, formatNode);\n        }\n      }\n    };\n    const appendNode = (parentNode, node) => {\n      parentNode.appendChild(node);\n      return node;\n    };\n    const insertFormatNodesIntoCaretContainer = (formatNodes, caretContainer) => {\n      var _a;\n      const innerMostFormatNode = foldr(formatNodes, (parentNode, formatNode) => {\n        return appendNode(parentNode, formatNode.cloneNode(false));\n      }, caretContainer);\n      const doc = (_a = innerMostFormatNode.ownerDocument) !== null && _a !== void 0 ? _a : document;\n      return appendNode(innerMostFormatNode, doc.createTextNode(ZWSP));\n    };\n    const cleanFormatNode = (editor, caretContainer, formatNode, name, vars, similar) => {\n      const formatter = editor.formatter;\n      const dom = editor.dom;\n      const validFormats = filter$5(keys(formatter.get()), formatName => formatName !== name && !contains$1(formatName, 'removeformat'));\n      const matchedFormats = matchAllOnNode(editor, formatNode, validFormats);\n      const uniqueFormats = filter$5(matchedFormats, fmtName => !areSimilarFormats(editor, fmtName, name));\n      if (uniqueFormats.length > 0) {\n        const clonedFormatNode = formatNode.cloneNode(false);\n        dom.add(caretContainer, clonedFormatNode);\n        formatter.remove(name, vars, clonedFormatNode, similar);\n        dom.remove(clonedFormatNode);\n        return Optional.some(clonedFormatNode);\n      } else {\n        return Optional.none();\n      }\n    };\n    const applyCaretFormat = (editor, name, vars) => {\n      let caretContainer;\n      const selection = editor.selection;\n      const formatList = editor.formatter.get(name);\n      if (!formatList) {\n        return;\n      }\n      const selectionRng = selection.getRng();\n      let offset = selectionRng.startOffset;\n      const container = selectionRng.startContainer;\n      const text = container.nodeValue;\n      caretContainer = getParentCaretContainer(editor.getBody(), selection.getStart());\n      const wordcharRegex = /[^\\s\\u00a0\\u00ad\\u200b\\ufeff]/;\n      if (text && offset > 0 && offset < text.length && wordcharRegex.test(text.charAt(offset)) && wordcharRegex.test(text.charAt(offset - 1))) {\n        const bookmark = selection.getBookmark();\n        selectionRng.collapse(true);\n        let rng = expandRng(editor.dom, selectionRng, formatList);\n        rng = split(rng);\n        editor.formatter.apply(name, vars, rng);\n        selection.moveToBookmark(bookmark);\n      } else {\n        let textNode = caretContainer ? findFirstTextNode(caretContainer) : null;\n        if (!caretContainer || (textNode === null || textNode === void 0 ? void 0 : textNode.data) !== ZWSP) {\n          caretContainer = importNode(editor.getDoc(), createCaretContainer(true).dom);\n          textNode = caretContainer.firstChild;\n          selectionRng.insertNode(caretContainer);\n          offset = 1;\n          editor.formatter.apply(name, vars, caretContainer);\n        } else {\n          editor.formatter.apply(name, vars, caretContainer);\n        }\n        selection.setCursorLocation(textNode, offset);\n      }\n    };\n    const removeCaretFormat = (editor, name, vars, similar) => {\n      const dom = editor.dom;\n      const selection = editor.selection;\n      let hasContentAfter = false;\n      const formatList = editor.formatter.get(name);\n      if (!formatList) {\n        return;\n      }\n      const rng = selection.getRng();\n      const container = rng.startContainer;\n      const offset = rng.startOffset;\n      let node = container;\n      if (isText$a(container)) {\n        if (offset !== container.data.length) {\n          hasContentAfter = true;\n        }\n        node = node.parentNode;\n      }\n      const parents = [];\n      let formatNode;\n      while (node) {\n        if (matchNode(editor, node, name, vars, similar)) {\n          formatNode = node;\n          break;\n        }\n        if (node.nextSibling) {\n          hasContentAfter = true;\n        }\n        parents.push(node);\n        node = node.parentNode;\n      }\n      if (!formatNode) {\n        return;\n      }\n      if (hasContentAfter) {\n        const bookmark = selection.getBookmark();\n        rng.collapse(true);\n        let expandedRng = expandRng(dom, rng, formatList, true);\n        expandedRng = split(expandedRng);\n        editor.formatter.remove(name, vars, expandedRng, similar);\n        selection.moveToBookmark(bookmark);\n      } else {\n        const caretContainer = getParentCaretContainer(editor.getBody(), formatNode);\n        const newCaretContainer = createCaretContainer(false).dom;\n        insertCaretContainerNode(editor, newCaretContainer, caretContainer !== null && caretContainer !== void 0 ? caretContainer : formatNode);\n        const cleanedFormatNode = cleanFormatNode(editor, newCaretContainer, formatNode, name, vars, similar);\n        const caretTextNode = insertFormatNodesIntoCaretContainer(parents.concat(cleanedFormatNode.toArray()), newCaretContainer);\n        if (caretContainer) {\n          removeCaretContainerNode(editor, caretContainer, false);\n        }\n        selection.setCursorLocation(caretTextNode, 1);\n        if (dom.isEmpty(formatNode)) {\n          dom.remove(formatNode);\n        }\n      }\n    };\n    const disableCaretContainer = (editor, keyCode) => {\n      const selection = editor.selection, body = editor.getBody();\n      removeCaretContainer(editor, null, false);\n      if ((keyCode === 8 || keyCode === 46) && selection.isCollapsed() && selection.getStart().innerHTML === ZWSP) {\n        removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()));\n      }\n      if (keyCode === 37 || keyCode === 39) {\n        removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()));\n      }\n    };\n    const setup$u = editor => {\n      editor.on('mouseup keydown', e => {\n        disableCaretContainer(editor, e.keyCode);\n      });\n    };\n    const replaceWithCaretFormat = (targetNode, formatNodes) => {\n      const caretContainer = createCaretContainer(false);\n      const innerMost = insertFormatNodesIntoCaretContainer(formatNodes, caretContainer.dom);\n      before$3(SugarElement.fromDom(targetNode), caretContainer);\n      remove$6(SugarElement.fromDom(targetNode));\n      return CaretPosition(innerMost, 0);\n    };\n    const isFormatElement = (editor, element) => {\n      const inlineElements = editor.schema.getTextInlineElements();\n      return has$2(inlineElements, name(element)) && !isCaretNode(element.dom) && !isBogus$2(element.dom);\n    };\n    const isEmptyCaretFormatElement = element => {\n      return isCaretNode(element.dom) && isCaretContainerEmpty(element.dom);\n    };\n\n    const postProcessHooks = {};\n    const isPre = matchNodeNames(['pre']);\n    const addPostProcessHook = (name, hook) => {\n      const hooks = postProcessHooks[name];\n      if (!hooks) {\n        postProcessHooks[name] = [];\n      }\n      postProcessHooks[name].push(hook);\n    };\n    const postProcess$1 = (name, editor) => {\n      if (has$2(postProcessHooks, name)) {\n        each$e(postProcessHooks[name], hook => {\n          hook(editor);\n        });\n      }\n    };\n    addPostProcessHook('pre', editor => {\n      const rng = editor.selection.getRng();\n      const hasPreSibling = blocks => pre => {\n        const prev = pre.previousSibling;\n        return isPre(prev) && contains$2(blocks, prev);\n      };\n      const joinPre = (pre1, pre2) => {\n        const sPre2 = SugarElement.fromDom(pre2);\n        const doc = documentOrOwner(sPre2).dom;\n        remove$6(sPre2);\n        append(SugarElement.fromDom(pre1), [\n          SugarElement.fromTag('br', doc),\n          SugarElement.fromTag('br', doc),\n          ...children$1(sPre2)\n        ]);\n      };\n      if (!rng.collapsed) {\n        const blocks = editor.selection.getSelectedBlocks();\n        const preBlocks = filter$5(filter$5(blocks, isPre), hasPreSibling(blocks));\n        each$e(preBlocks, pre => {\n          joinPre(pre.previousSibling, pre);\n        });\n      }\n    });\n\n    const listItemStyles = [\n      'fontWeight',\n      'fontStyle',\n      'color',\n      'fontSize',\n      'fontFamily'\n    ];\n    const hasListStyles = fmt => isObject(fmt.styles) && exists(keys(fmt.styles), name => contains$2(listItemStyles, name));\n    const findExpandedListItemFormat = formats => find$2(formats, fmt => isInlineFormat(fmt) && fmt.inline === 'span' && hasListStyles(fmt));\n    const getExpandedListItemFormat = (formatter, format) => {\n      const formatList = formatter.get(format);\n      return isArray$1(formatList) ? findExpandedListItemFormat(formatList) : Optional.none();\n    };\n    const isRngStartAtStartOfElement = (rng, elm) => prevPosition(elm, CaretPosition.fromRangeStart(rng)).isNone();\n    const isRngEndAtEndOfElement = (rng, elm) => {\n      return nextPosition(elm, CaretPosition.fromRangeEnd(rng)).exists(pos => !isBr$6(pos.getNode()) || nextPosition(elm, pos).isSome()) === false;\n    };\n    const isEditableListItem = dom => elm => isListItem$2(elm) && dom.getContentEditableParent(elm) !== 'false';\n    const getFullySelectedBlocks = selection => {\n      const blocks = selection.getSelectedBlocks();\n      const rng = selection.getRng();\n      if (selection.isCollapsed()) {\n        return [];\n      }\n      if (blocks.length === 1) {\n        return isRngStartAtStartOfElement(rng, blocks[0]) && isRngEndAtEndOfElement(rng, blocks[0]) ? blocks : [];\n      } else {\n        const first = head(blocks).filter(elm => isRngStartAtStartOfElement(rng, elm)).toArray();\n        const last = last$3(blocks).filter(elm => isRngEndAtEndOfElement(rng, elm)).toArray();\n        const middle = blocks.slice(1, -1);\n        return first.concat(middle).concat(last);\n      }\n    };\n    const getFullySelectedListItems = selection => filter$5(getFullySelectedBlocks(selection), isEditableListItem(selection.dom));\n    const getPartiallySelectedListItems = selection => filter$5(selection.getSelectedBlocks(), isEditableListItem(selection.dom));\n\n    const each$8 = Tools.each;\n    const isElementNode = node => isElement$6(node) && !isBookmarkNode$1(node) && !isCaretNode(node) && !isBogus$2(node);\n    const findElementSibling = (node, siblingName) => {\n      for (let sibling = node; sibling; sibling = sibling[siblingName]) {\n        if (isText$a(sibling) && isNotEmpty(sibling.data)) {\n          return node;\n        }\n        if (isElement$6(sibling) && !isBookmarkNode$1(sibling)) {\n          return sibling;\n        }\n      }\n      return node;\n    };\n    const mergeSiblingsNodes = (editor, prev, next) => {\n      const elementUtils = ElementUtils(editor);\n      const isPrevEditable = isElement$6(prev) && isEditable$3(prev);\n      const isNextEditable = isElement$6(next) && isEditable$3(next);\n      if (isPrevEditable && isNextEditable) {\n        const prevSibling = findElementSibling(prev, 'previousSibling');\n        const nextSibling = findElementSibling(next, 'nextSibling');\n        if (elementUtils.compare(prevSibling, nextSibling)) {\n          for (let sibling = prevSibling.nextSibling; sibling && sibling !== nextSibling;) {\n            const tmpSibling = sibling;\n            sibling = sibling.nextSibling;\n            prevSibling.appendChild(tmpSibling);\n          }\n          editor.dom.remove(nextSibling);\n          Tools.each(Tools.grep(nextSibling.childNodes), node => {\n            prevSibling.appendChild(node);\n          });\n          return prevSibling;\n        }\n      }\n      return next;\n    };\n    const mergeSiblings = (editor, format, vars, node) => {\n      var _a;\n      if (node && format.merge_siblings !== false) {\n        const newNode = (_a = mergeSiblingsNodes(editor, getNonWhiteSpaceSibling(node), node)) !== null && _a !== void 0 ? _a : node;\n        mergeSiblingsNodes(editor, newNode, getNonWhiteSpaceSibling(newNode, true));\n      }\n    };\n    const clearChildStyles = (dom, format, node) => {\n      if (format.clear_child_styles) {\n        const selector = format.links ? '*:not(a)' : '*';\n        each$8(dom.select(selector, node), childNode => {\n          if (isElementNode(childNode) && isEditable$3(childNode)) {\n            each$8(format.styles, (_value, name) => {\n              dom.setStyle(childNode, name, '');\n            });\n          }\n        });\n      }\n    };\n    const processChildElements = (node, filter, process) => {\n      each$8(node.childNodes, node => {\n        if (isElementNode(node)) {\n          if (filter(node)) {\n            process(node);\n          }\n          if (node.hasChildNodes()) {\n            processChildElements(node, filter, process);\n          }\n        }\n      });\n    };\n    const unwrapEmptySpan = (dom, node) => {\n      if (node.nodeName === 'SPAN' && dom.getAttribs(node).length === 0) {\n        dom.remove(node, true);\n      }\n    };\n    const hasStyle = (dom, name) => node => !!(node && getStyle(dom, node, name));\n    const applyStyle = (dom, name, value) => node => {\n      dom.setStyle(node, name, value);\n      if (node.getAttribute('style') === '') {\n        node.removeAttribute('style');\n      }\n      unwrapEmptySpan(dom, node);\n    };\n\n    const removeResult = Adt.generate([\n      { keep: [] },\n      { rename: ['name'] },\n      { removed: [] }\n    ]);\n    const MCE_ATTR_RE = /^(src|href|style)$/;\n    const each$7 = Tools.each;\n    const isEq$2 = isEq$5;\n    const isTableCellOrRow = node => /^(TR|TH|TD)$/.test(node.nodeName);\n    const isChildOfInlineParent = (dom, node, parent) => dom.isChildOf(node, parent) && node !== parent && !dom.isBlock(parent);\n    const getContainer = (ed, rng, start) => {\n      let container = rng[start ? 'startContainer' : 'endContainer'];\n      let offset = rng[start ? 'startOffset' : 'endOffset'];\n      if (isElement$6(container)) {\n        const lastIdx = container.childNodes.length - 1;\n        if (!start && offset) {\n          offset--;\n        }\n        container = container.childNodes[offset > lastIdx ? lastIdx : offset];\n      }\n      if (isText$a(container) && start && offset >= container.data.length) {\n        container = new DomTreeWalker(container, ed.getBody()).next() || container;\n      }\n      if (isText$a(container) && !start && offset === 0) {\n        container = new DomTreeWalker(container, ed.getBody()).prev() || container;\n      }\n      return container;\n    };\n    const normalizeTableSelection = (node, start) => {\n      const prop = start ? 'firstChild' : 'lastChild';\n      const childNode = node[prop];\n      if (isTableCellOrRow(node) && childNode) {\n        if (node.nodeName === 'TR') {\n          return childNode[prop] || childNode;\n        } else {\n          return childNode;\n        }\n      }\n      return node;\n    };\n    const wrap$1 = (dom, node, name, attrs) => {\n      var _a;\n      const wrapper = dom.create(name, attrs);\n      (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(wrapper, node);\n      wrapper.appendChild(node);\n      return wrapper;\n    };\n    const wrapWithSiblings = (dom, node, next, name, attrs) => {\n      const start = SugarElement.fromDom(node);\n      const wrapper = SugarElement.fromDom(dom.create(name, attrs));\n      const siblings = next ? nextSiblings(start) : prevSiblings(start);\n      append(wrapper, siblings);\n      if (next) {\n        before$3(start, wrapper);\n        prepend(wrapper, start);\n      } else {\n        after$4(start, wrapper);\n        append$1(wrapper, start);\n      }\n      return wrapper.dom;\n    };\n    const isColorFormatAndAnchor = (node, format) => format.links && node.nodeName === 'A';\n    const removeNode = (ed, node, format) => {\n      const parentNode = node.parentNode;\n      let rootBlockElm;\n      const dom = ed.dom;\n      const forcedRootBlock = getForcedRootBlock(ed);\n      if (isBlockFormat(format)) {\n        if (parentNode === dom.getRoot()) {\n          if (!format.list_block || !isEq$2(node, format.list_block)) {\n            each$e(from(node.childNodes), node => {\n              if (isValid(ed, forcedRootBlock, node.nodeName.toLowerCase())) {\n                if (!rootBlockElm) {\n                  rootBlockElm = wrap$1(dom, node, forcedRootBlock);\n                  dom.setAttribs(rootBlockElm, getForcedRootBlockAttrs(ed));\n                } else {\n                  rootBlockElm.appendChild(node);\n                }\n              } else {\n                rootBlockElm = null;\n              }\n            });\n          }\n        }\n      }\n      if (isMixedFormat(format) && !isEq$2(format.inline, node)) {\n        return;\n      }\n      dom.remove(node, true);\n    };\n    const processFormatAttrOrStyle = (name, value, vars) => {\n      if (isNumber(name)) {\n        return {\n          name: value,\n          value: null\n        };\n      } else {\n        return {\n          name,\n          value: replaceVars(value, vars)\n        };\n      }\n    };\n    const removeEmptyStyleAttributeIfNeeded = (dom, elm) => {\n      if (dom.getAttrib(elm, 'style') === '') {\n        elm.removeAttribute('style');\n        elm.removeAttribute('data-mce-style');\n      }\n    };\n    const removeStyles = (dom, elm, format, vars, compareNode) => {\n      let stylesModified = false;\n      each$7(format.styles, (value, name) => {\n        const {\n          name: styleName,\n          value: styleValue\n        } = processFormatAttrOrStyle(name, value, vars);\n        const normalizedStyleValue = normalizeStyleValue(styleValue, styleName);\n        if (format.remove_similar || isNull(styleValue) || !isElement$6(compareNode) || isEq$2(getStyle(dom, compareNode, styleName), normalizedStyleValue)) {\n          dom.setStyle(elm, styleName, '');\n        }\n        stylesModified = true;\n      });\n      if (stylesModified) {\n        removeEmptyStyleAttributeIfNeeded(dom, elm);\n      }\n    };\n    const removeListStyleFormats = (editor, name, vars) => {\n      if (name === 'removeformat') {\n        each$e(getPartiallySelectedListItems(editor.selection), li => {\n          each$e(listItemStyles, name => editor.dom.setStyle(li, name, ''));\n          removeEmptyStyleAttributeIfNeeded(editor.dom, li);\n        });\n      } else {\n        getExpandedListItemFormat(editor.formatter, name).each(liFmt => {\n          each$e(getPartiallySelectedListItems(editor.selection), li => removeStyles(editor.dom, li, liFmt, vars, null));\n        });\n      }\n    };\n    const removeFormatInternal = (ed, format, vars, node, compareNode) => {\n      const dom = ed.dom;\n      const elementUtils = ElementUtils(ed);\n      const schema = ed.schema;\n      if (isInlineFormat(format) && isTransparentElementName(schema, format.inline) && isTransparentBlock(schema, node) && node.parentElement === ed.getBody()) {\n        removeNode(ed, node, format);\n        return removeResult.removed();\n      }\n      if (!format.ceFalseOverride && node && dom.getContentEditableParent(node) === 'false') {\n        return removeResult.keep();\n      }\n      if (node && !matchName(dom, node, format) && !isColorFormatAndAnchor(node, format)) {\n        return removeResult.keep();\n      }\n      const elm = node;\n      const preserveAttributes = format.preserve_attributes;\n      if (isInlineFormat(format) && format.remove === 'all' && isArray$1(preserveAttributes)) {\n        const attrsToPreserve = filter$5(dom.getAttribs(elm), attr => contains$2(preserveAttributes, attr.name.toLowerCase()));\n        dom.removeAllAttribs(elm);\n        each$e(attrsToPreserve, attr => dom.setAttrib(elm, attr.name, attr.value));\n        if (attrsToPreserve.length > 0) {\n          return removeResult.rename('span');\n        }\n      }\n      if (format.remove !== 'all') {\n        removeStyles(dom, elm, format, vars, compareNode);\n        each$7(format.attributes, (value, name) => {\n          const {\n            name: attrName,\n            value: attrValue\n          } = processFormatAttrOrStyle(name, value, vars);\n          if (format.remove_similar || isNull(attrValue) || !isElement$6(compareNode) || isEq$2(dom.getAttrib(compareNode, attrName), attrValue)) {\n            if (attrName === 'class') {\n              const currentValue = dom.getAttrib(elm, attrName);\n              if (currentValue) {\n                let valueOut = '';\n                each$e(currentValue.split(/\\s+/), cls => {\n                  if (/mce\\-\\w+/.test(cls)) {\n                    valueOut += (valueOut ? ' ' : '') + cls;\n                  }\n                });\n                if (valueOut) {\n                  dom.setAttrib(elm, attrName, valueOut);\n                  return;\n                }\n              }\n            }\n            if (MCE_ATTR_RE.test(attrName)) {\n              elm.removeAttribute('data-mce-' + attrName);\n            }\n            if (attrName === 'style' && matchNodeNames(['li'])(elm) && dom.getStyle(elm, 'list-style-type') === 'none') {\n              elm.removeAttribute(attrName);\n              dom.setStyle(elm, 'list-style-type', 'none');\n              return;\n            }\n            if (attrName === 'class') {\n              elm.removeAttribute('className');\n            }\n            elm.removeAttribute(attrName);\n          }\n        });\n        each$7(format.classes, value => {\n          value = replaceVars(value, vars);\n          if (!isElement$6(compareNode) || dom.hasClass(compareNode, value)) {\n            dom.removeClass(elm, value);\n          }\n        });\n        const attrs = dom.getAttribs(elm);\n        for (let i = 0; i < attrs.length; i++) {\n          const attrName = attrs[i].nodeName;\n          if (!elementUtils.isAttributeInternal(attrName)) {\n            return removeResult.keep();\n          }\n        }\n      }\n      if (format.remove !== 'none') {\n        removeNode(ed, elm, format);\n        return removeResult.removed();\n      }\n      return removeResult.keep();\n    };\n    const removeFormat$1 = (ed, format, vars, node, compareNode) => removeFormatInternal(ed, format, vars, node, compareNode).fold(never, newName => {\n      ed.dom.rename(node, newName);\n      return true;\n    }, always);\n    const findFormatRoot = (editor, container, name, vars, similar) => {\n      let formatRoot;\n      if (container.parentNode) {\n        each$e(getParents$2(editor.dom, container.parentNode).reverse(), parent => {\n          if (!formatRoot && isElement$6(parent) && parent.id !== '_start' && parent.id !== '_end') {\n            const format = matchNode(editor, parent, name, vars, similar);\n            if (format && format.split !== false) {\n              formatRoot = parent;\n            }\n          }\n        });\n      }\n      return formatRoot;\n    };\n    const removeFormatFromClone = (editor, format, vars, clone) => removeFormatInternal(editor, format, vars, clone, clone).fold(constant(clone), newName => {\n      const fragment = editor.dom.createFragment();\n      fragment.appendChild(clone);\n      return editor.dom.rename(clone, newName);\n    }, constant(null));\n    const wrapAndSplit = (editor, formatList, formatRoot, container, target, split, format, vars) => {\n      var _a, _b;\n      let lastClone;\n      let firstClone;\n      const dom = editor.dom;\n      if (formatRoot) {\n        const formatRootParent = formatRoot.parentNode;\n        for (let parent = container.parentNode; parent && parent !== formatRootParent; parent = parent.parentNode) {\n          let clone = dom.clone(parent, false);\n          for (let i = 0; i < formatList.length; i++) {\n            clone = removeFormatFromClone(editor, formatList[i], vars, clone);\n            if (clone === null) {\n              break;\n            }\n          }\n          if (clone) {\n            if (lastClone) {\n              clone.appendChild(lastClone);\n            }\n            if (!firstClone) {\n              firstClone = clone;\n            }\n            lastClone = clone;\n          }\n        }\n        if (split && (!format.mixed || !dom.isBlock(formatRoot))) {\n          container = (_a = dom.split(formatRoot, container)) !== null && _a !== void 0 ? _a : container;\n        }\n        if (lastClone && firstClone) {\n          (_b = target.parentNode) === null || _b === void 0 ? void 0 : _b.insertBefore(lastClone, target);\n          firstClone.appendChild(target);\n          if (isInlineFormat(format)) {\n            mergeSiblings(editor, format, vars, lastClone);\n          }\n        }\n      }\n      return container;\n    };\n    const remove$2 = (ed, name, vars, node, similar) => {\n      const formatList = ed.formatter.get(name);\n      const format = formatList[0];\n      const dom = ed.dom;\n      const selection = ed.selection;\n      const splitToFormatRoot = container => {\n        const formatRoot = findFormatRoot(ed, container, name, vars, similar);\n        return wrapAndSplit(ed, formatList, formatRoot, container, container, true, format, vars);\n      };\n      const isRemoveBookmarkNode = node => isBookmarkNode$1(node) && isElement$6(node) && (node.id === '_start' || node.id === '_end');\n      const removeNodeFormat = node => exists(formatList, fmt => removeFormat$1(ed, fmt, vars, node, node));\n      const process = node => {\n        const children = from(node.childNodes);\n        const removed = removeNodeFormat(node);\n        const currentNodeMatches = removed || exists(formatList, f => matchName(dom, node, f));\n        const parentNode = node.parentNode;\n        if (!currentNodeMatches && isNonNullable(parentNode) && shouldExpandToSelector(format)) {\n          removeNodeFormat(parentNode);\n        }\n        if (format.deep) {\n          if (children.length) {\n            for (let i = 0; i < children.length; i++) {\n              process(children[i]);\n            }\n          }\n        }\n        const textDecorations = [\n          'underline',\n          'line-through',\n          'overline'\n        ];\n        each$e(textDecorations, decoration => {\n          if (isElement$6(node) && ed.dom.getStyle(node, 'text-decoration') === decoration && node.parentNode && getTextDecoration(dom, node.parentNode) === decoration) {\n            removeFormat$1(ed, {\n              deep: false,\n              exact: true,\n              inline: 'span',\n              styles: { textDecoration: decoration }\n            }, undefined, node);\n          }\n        });\n      };\n      const unwrap = start => {\n        const node = dom.get(start ? '_start' : '_end');\n        if (node) {\n          let out = node[start ? 'firstChild' : 'lastChild'];\n          if (isRemoveBookmarkNode(out)) {\n            out = out[start ? 'firstChild' : 'lastChild'];\n          }\n          if (isText$a(out) && out.data.length === 0) {\n            out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling;\n          }\n          dom.remove(node, true);\n          return out;\n        } else {\n          return null;\n        }\n      };\n      const removeRngStyle = rng => {\n        let startContainer;\n        let endContainer;\n        let expandedRng = expandRng(dom, rng, formatList, rng.collapsed);\n        if (format.split) {\n          expandedRng = split(expandedRng);\n          startContainer = getContainer(ed, expandedRng, true);\n          endContainer = getContainer(ed, expandedRng);\n          if (startContainer !== endContainer) {\n            startContainer = normalizeTableSelection(startContainer, true);\n            endContainer = normalizeTableSelection(endContainer, false);\n            if (isChildOfInlineParent(dom, startContainer, endContainer)) {\n              const marker = Optional.from(startContainer.firstChild).getOr(startContainer);\n              splitToFormatRoot(wrapWithSiblings(dom, marker, true, 'span', {\n                'id': '_start',\n                'data-mce-type': 'bookmark'\n              }));\n              unwrap(true);\n              return;\n            }\n            if (isChildOfInlineParent(dom, endContainer, startContainer)) {\n              const marker = Optional.from(endContainer.lastChild).getOr(endContainer);\n              splitToFormatRoot(wrapWithSiblings(dom, marker, false, 'span', {\n                'id': '_end',\n                'data-mce-type': 'bookmark'\n              }));\n              unwrap(false);\n              return;\n            }\n            startContainer = wrap$1(dom, startContainer, 'span', {\n              'id': '_start',\n              'data-mce-type': 'bookmark'\n            });\n            endContainer = wrap$1(dom, endContainer, 'span', {\n              'id': '_end',\n              'data-mce-type': 'bookmark'\n            });\n            const newRng = dom.createRng();\n            newRng.setStartAfter(startContainer);\n            newRng.setEndBefore(endContainer);\n            walk$3(dom, newRng, nodes => {\n              each$e(nodes, n => {\n                if (!isBookmarkNode$1(n) && !isBookmarkNode$1(n.parentNode)) {\n                  splitToFormatRoot(n);\n                }\n              });\n            });\n            splitToFormatRoot(startContainer);\n            splitToFormatRoot(endContainer);\n            startContainer = unwrap(true);\n            endContainer = unwrap();\n          } else {\n            startContainer = endContainer = splitToFormatRoot(startContainer);\n          }\n          expandedRng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer;\n          expandedRng.startOffset = dom.nodeIndex(startContainer);\n          expandedRng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer;\n          expandedRng.endOffset = dom.nodeIndex(endContainer) + 1;\n        }\n        walk$3(dom, expandedRng, nodes => {\n          each$e(nodes, process);\n        });\n      };\n      if (node) {\n        if (isNode(node)) {\n          const rng = dom.createRng();\n          rng.setStartBefore(node);\n          rng.setEndAfter(node);\n          removeRngStyle(rng);\n        } else {\n          removeRngStyle(node);\n        }\n        fireFormatRemove(ed, name, node, vars);\n        return;\n      }\n      if (!selection.isCollapsed() || !isInlineFormat(format) || getCellsFromEditor(ed).length) {\n        preserveSelection(ed, () => runOnRanges(ed, removeRngStyle), startNode => isInlineFormat(format) && match$2(ed, name, vars, startNode));\n        ed.nodeChanged();\n      } else {\n        removeCaretFormat(ed, name, vars, similar);\n      }\n      removeListStyleFormats(ed, name, vars);\n      fireFormatRemove(ed, name, node, vars);\n    };\n\n    const each$6 = Tools.each;\n    const mergeTextDecorationsAndColor = (dom, format, vars, node) => {\n      const processTextDecorationsAndColor = n => {\n        if (isElement$6(n) && isElement$6(n.parentNode) && isEditable$3(n)) {\n          const parentTextDecoration = getTextDecoration(dom, n.parentNode);\n          if (dom.getStyle(n, 'color') && parentTextDecoration) {\n            dom.setStyle(n, 'text-decoration', parentTextDecoration);\n          } else if (dom.getStyle(n, 'text-decoration') === parentTextDecoration) {\n            dom.setStyle(n, 'text-decoration', null);\n          }\n        }\n      };\n      if (format.styles && (format.styles.color || format.styles.textDecoration)) {\n        Tools.walk(node, processTextDecorationsAndColor, 'childNodes');\n        processTextDecorationsAndColor(node);\n      }\n    };\n    const mergeBackgroundColorAndFontSize = (dom, format, vars, node) => {\n      if (format.styles && format.styles.backgroundColor) {\n        const hasFontSize = hasStyle(dom, 'fontSize');\n        processChildElements(node, elm => hasFontSize(elm) && isEditable$3(elm), applyStyle(dom, 'backgroundColor', replaceVars(format.styles.backgroundColor, vars)));\n      }\n    };\n    const mergeSubSup = (dom, format, vars, node) => {\n      if (isInlineFormat(format) && (format.inline === 'sub' || format.inline === 'sup')) {\n        const hasFontSize = hasStyle(dom, 'fontSize');\n        processChildElements(node, elm => hasFontSize(elm) && isEditable$3(elm), applyStyle(dom, 'fontSize', ''));\n        const inverseTagDescendants = filter$5(dom.select(format.inline === 'sup' ? 'sub' : 'sup', node), isEditable$3);\n        dom.remove(inverseTagDescendants, true);\n      }\n    };\n    const mergeWithChildren = (editor, formatList, vars, node) => {\n      each$6(formatList, format => {\n        if (isInlineFormat(format)) {\n          each$6(editor.dom.select(format.inline, node), child => {\n            if (isElementNode(child)) {\n              removeFormat$1(editor, format, vars, child, format.exact ? child : null);\n            }\n          });\n        }\n        clearChildStyles(editor.dom, format, node);\n      });\n    };\n    const mergeWithParents = (editor, format, name, vars, node) => {\n      const parentNode = node.parentNode;\n      if (matchNode(editor, parentNode, name, vars)) {\n        if (removeFormat$1(editor, format, vars, node)) {\n          return;\n        }\n      }\n      if (format.merge_with_parents && parentNode) {\n        editor.dom.getParent(parentNode, parent => {\n          if (matchNode(editor, parent, name, vars)) {\n            removeFormat$1(editor, format, vars, node);\n            return true;\n          } else {\n            return false;\n          }\n        });\n      }\n    };\n\n    const each$5 = Tools.each;\n    const canFormatBR = (editor, format, node, parentName) => {\n      if (canFormatEmptyLines(editor) && isInlineFormat(format) && node.parentNode) {\n        const validBRParentElements = getTextRootBlockElements(editor.schema);\n        const hasCaretNodeSibling = sibling(SugarElement.fromDom(node), sibling => isCaretNode(sibling.dom));\n        return hasNonNullableKey(validBRParentElements, parentName) && isEmpty$2(SugarElement.fromDom(node.parentNode), false) && !hasCaretNodeSibling;\n      } else {\n        return false;\n      }\n    };\n    const applyStyles = (dom, elm, format, vars) => {\n      each$5(format.styles, (value, name) => {\n        dom.setStyle(elm, name, replaceVars(value, vars));\n      });\n      if (format.styles) {\n        const styleVal = dom.getAttrib(elm, 'style');\n        if (styleVal) {\n          dom.setAttrib(elm, 'data-mce-style', styleVal);\n        }\n      }\n    };\n    const applyFormat$1 = (ed, name, vars, node) => {\n      const formatList = ed.formatter.get(name);\n      const format = formatList[0];\n      const isCollapsed = !node && ed.selection.isCollapsed();\n      const dom = ed.dom;\n      const selection = ed.selection;\n      const setElementFormat = (elm, fmt = format) => {\n        if (isFunction(fmt.onformat)) {\n          fmt.onformat(elm, fmt, vars, node);\n        }\n        applyStyles(dom, elm, fmt, vars);\n        each$5(fmt.attributes, (value, name) => {\n          dom.setAttrib(elm, name, replaceVars(value, vars));\n        });\n        each$5(fmt.classes, value => {\n          const newValue = replaceVars(value, vars);\n          if (!dom.hasClass(elm, newValue)) {\n            dom.addClass(elm, newValue);\n          }\n        });\n      };\n      const applyNodeStyle = (formatList, node) => {\n        let found = false;\n        each$5(formatList, format => {\n          if (!isSelectorFormat(format)) {\n            return false;\n          }\n          if (dom.getContentEditable(node) === 'false' && !format.ceFalseOverride) {\n            return true;\n          }\n          if (isNonNullable(format.collapsed) && format.collapsed !== isCollapsed) {\n            return true;\n          }\n          if (dom.is(node, format.selector) && !isCaretNode(node)) {\n            setElementFormat(node, format);\n            found = true;\n            return false;\n          }\n          return true;\n        });\n        return found;\n      };\n      const createWrapElement = wrapName => {\n        if (isString(wrapName)) {\n          const wrapElm = dom.create(wrapName);\n          setElementFormat(wrapElm);\n          return wrapElm;\n        } else {\n          return null;\n        }\n      };\n      const applyRngStyle = (dom, rng, nodeSpecific) => {\n        const newWrappers = [];\n        let contentEditable = true;\n        const wrapName = format.inline || format.block;\n        const wrapElm = createWrapElement(wrapName);\n        const isMatchingWrappingBlock = node => isWrappingBlockFormat(format) && matchNode(ed, node, name, vars);\n        const canRenameBlock = (node, parentName, isEditableDescendant) => {\n          const isValidBlockFormatForNode = isNonWrappingBlockFormat(format) && isTextBlock$1(ed.schema, node) && isValid(ed, parentName, wrapName);\n          return isEditableDescendant && isValidBlockFormatForNode;\n        };\n        const canWrapNode = (node, parentName, isEditableDescendant, isWrappableNoneditableElm) => {\n          const nodeName = node.nodeName.toLowerCase();\n          const isValidWrapNode = isValid(ed, wrapName, nodeName) && isValid(ed, parentName, wrapName);\n          const isZwsp$1 = !nodeSpecific && isText$a(node) && isZwsp(node.data);\n          const isCaret = isCaretNode(node);\n          const isCorrectFormatForNode = !isInlineFormat(format) || !dom.isBlock(node);\n          return (isEditableDescendant || isWrappableNoneditableElm) && isValidWrapNode && !isZwsp$1 && !isCaret && isCorrectFormatForNode;\n        };\n        walk$3(dom, rng, nodes => {\n          let currentWrapElm;\n          const process = node => {\n            let hasContentEditableState = false;\n            let lastContentEditable = contentEditable;\n            let isWrappableNoneditableElm = false;\n            const parentNode = node.parentNode;\n            const parentName = parentNode.nodeName.toLowerCase();\n            const contentEditableValue = dom.getContentEditable(node);\n            if (isNonNullable(contentEditableValue)) {\n              lastContentEditable = contentEditable;\n              contentEditable = contentEditableValue === 'true';\n              hasContentEditableState = true;\n              isWrappableNoneditableElm = isWrappableNoneditable(ed, node);\n            }\n            const isEditableDescendant = contentEditable && !hasContentEditableState;\n            if (isBr$6(node) && !canFormatBR(ed, format, node, parentName)) {\n              currentWrapElm = null;\n              if (isBlockFormat(format)) {\n                dom.remove(node);\n              }\n              return;\n            }\n            if (isMatchingWrappingBlock(node)) {\n              currentWrapElm = null;\n              return;\n            }\n            if (canRenameBlock(node, parentName, isEditableDescendant)) {\n              const elm = dom.rename(node, wrapName);\n              setElementFormat(elm);\n              newWrappers.push(elm);\n              currentWrapElm = null;\n              return;\n            }\n            if (isSelectorFormat(format)) {\n              let found = applyNodeStyle(formatList, node);\n              if (!found && isNonNullable(parentNode) && shouldExpandToSelector(format)) {\n                found = applyNodeStyle(formatList, parentNode);\n              }\n              if (!isInlineFormat(format) || found) {\n                currentWrapElm = null;\n                return;\n              }\n            }\n            if (isNonNullable(wrapElm) && canWrapNode(node, parentName, isEditableDescendant, isWrappableNoneditableElm)) {\n              if (!currentWrapElm) {\n                currentWrapElm = dom.clone(wrapElm, false);\n                parentNode.insertBefore(currentWrapElm, node);\n                newWrappers.push(currentWrapElm);\n              }\n              if (isWrappableNoneditableElm && hasContentEditableState) {\n                contentEditable = lastContentEditable;\n              }\n              currentWrapElm.appendChild(node);\n            } else {\n              currentWrapElm = null;\n              each$e(from(node.childNodes), process);\n              if (hasContentEditableState) {\n                contentEditable = lastContentEditable;\n              }\n              currentWrapElm = null;\n            }\n          };\n          each$e(nodes, process);\n        });\n        if (format.links === true) {\n          each$e(newWrappers, node => {\n            const process = node => {\n              if (node.nodeName === 'A') {\n                setElementFormat(node, format);\n              }\n              each$e(from(node.childNodes), process);\n            };\n            process(node);\n          });\n        }\n        each$e(newWrappers, node => {\n          const getChildCount = node => {\n            let count = 0;\n            each$e(node.childNodes, node => {\n              if (!isEmptyTextNode$1(node) && !isBookmarkNode$1(node)) {\n                count++;\n              }\n            });\n            return count;\n          };\n          const mergeStyles = node => {\n            const childElement = find$2(node.childNodes, isElementNode$1).filter(child => dom.getContentEditable(child) !== 'false' && matchName(dom, child, format));\n            return childElement.map(child => {\n              const clone = dom.clone(child, false);\n              setElementFormat(clone);\n              dom.replace(clone, node, true);\n              dom.remove(child, true);\n              return clone;\n            }).getOr(node);\n          };\n          const childCount = getChildCount(node);\n          if ((newWrappers.length > 1 || !dom.isBlock(node)) && childCount === 0) {\n            dom.remove(node, true);\n            return;\n          }\n          if (isInlineFormat(format) || isBlockFormat(format) && format.wrapper) {\n            if (!format.exact && childCount === 1) {\n              node = mergeStyles(node);\n            }\n            mergeWithChildren(ed, formatList, vars, node);\n            mergeWithParents(ed, format, name, vars, node);\n            mergeBackgroundColorAndFontSize(dom, format, vars, node);\n            mergeTextDecorationsAndColor(dom, format, vars, node);\n            mergeSubSup(dom, format, vars, node);\n            mergeSiblings(ed, format, vars, node);\n          }\n        });\n      };\n      const targetNode = isNode(node) ? node : selection.getNode();\n      if (dom.getContentEditable(targetNode) === 'false' && !isWrappableNoneditable(ed, targetNode)) {\n        node = targetNode;\n        applyNodeStyle(formatList, node);\n        fireFormatApply(ed, name, node, vars);\n        return;\n      }\n      if (format) {\n        if (node) {\n          if (isNode(node)) {\n            if (!applyNodeStyle(formatList, node)) {\n              const rng = dom.createRng();\n              rng.setStartBefore(node);\n              rng.setEndAfter(node);\n              applyRngStyle(dom, expandRng(dom, rng, formatList), true);\n            }\n          } else {\n            applyRngStyle(dom, node, true);\n          }\n        } else {\n          if (!isCollapsed || !isInlineFormat(format) || getCellsFromEditor(ed).length) {\n            selection.setRng(normalize(selection.getRng()));\n            preserveSelection(ed, () => {\n              runOnRanges(ed, (selectionRng, fake) => {\n                const expandedRng = fake ? selectionRng : expandRng(dom, selectionRng, formatList);\n                applyRngStyle(dom, expandedRng, false);\n              });\n            }, always);\n            ed.nodeChanged();\n          } else {\n            applyCaretFormat(ed, name, vars);\n          }\n          getExpandedListItemFormat(ed.formatter, name).each(liFmt => {\n            each$e(getFullySelectedListItems(ed.selection), li => applyStyles(dom, li, liFmt, vars));\n          });\n        }\n        postProcess$1(name, ed);\n      }\n      fireFormatApply(ed, name, node, vars);\n    };\n\n    const hasVars = value => has$2(value, 'vars');\n    const setup$t = (registeredFormatListeners, editor) => {\n      registeredFormatListeners.set({});\n      editor.on('NodeChange', e => {\n        updateAndFireChangeCallbacks(editor, e.element, registeredFormatListeners.get());\n      });\n      editor.on('FormatApply FormatRemove', e => {\n        const element = Optional.from(e.node).map(nodeOrRange => isNode(nodeOrRange) ? nodeOrRange : nodeOrRange.startContainer).bind(node => isElement$6(node) ? Optional.some(node) : Optional.from(node.parentElement)).getOrThunk(() => fallbackElement(editor));\n        updateAndFireChangeCallbacks(editor, element, registeredFormatListeners.get());\n      });\n    };\n    const fallbackElement = editor => editor.selection.getStart();\n    const matchingNode = (editor, parents, format, similar, vars) => {\n      const isMatchingNode = node => {\n        const matchingFormat = editor.formatter.matchNode(node, format, vars !== null && vars !== void 0 ? vars : {}, similar);\n        return !isUndefined(matchingFormat);\n      };\n      const isUnableToMatch = node => {\n        if (matchesUnInheritedFormatSelector(editor, node, format)) {\n          return true;\n        } else {\n          if (!similar) {\n            return isNonNullable(editor.formatter.matchNode(node, format, vars, true));\n          } else {\n            return false;\n          }\n        }\n      };\n      return findUntil$1(parents, isMatchingNode, isUnableToMatch);\n    };\n    const getParents = (editor, elm) => {\n      const element = elm !== null && elm !== void 0 ? elm : fallbackElement(editor);\n      return filter$5(getParents$2(editor.dom, element), node => isElement$6(node) && !isBogus$2(node));\n    };\n    const updateAndFireChangeCallbacks = (editor, elm, registeredCallbacks) => {\n      const parents = getParents(editor, elm);\n      each$d(registeredCallbacks, (data, format) => {\n        const runIfChanged = spec => {\n          const match = matchingNode(editor, parents, format, spec.similar, hasVars(spec) ? spec.vars : undefined);\n          const isSet = match.isSome();\n          if (spec.state.get() !== isSet) {\n            spec.state.set(isSet);\n            const node = match.getOr(elm);\n            if (hasVars(spec)) {\n              spec.callback(isSet, {\n                node,\n                format,\n                parents\n              });\n            } else {\n              each$e(spec.callbacks, callback => callback(isSet, {\n                node,\n                format,\n                parents\n              }));\n            }\n          }\n        };\n        each$e([\n          data.withSimilar,\n          data.withoutSimilar\n        ], runIfChanged);\n        each$e(data.withVars, runIfChanged);\n      });\n    };\n    const addListeners = (editor, registeredFormatListeners, formats, callback, similar, vars) => {\n      const formatChangeItems = registeredFormatListeners.get();\n      each$e(formats.split(','), format => {\n        const group = get$a(formatChangeItems, format).getOrThunk(() => {\n          const base = {\n            withSimilar: {\n              state: Cell(false),\n              similar: true,\n              callbacks: []\n            },\n            withoutSimilar: {\n              state: Cell(false),\n              similar: false,\n              callbacks: []\n            },\n            withVars: []\n          };\n          formatChangeItems[format] = base;\n          return base;\n        });\n        const getCurrent = () => {\n          const parents = getParents(editor);\n          return matchingNode(editor, parents, format, similar, vars).isSome();\n        };\n        if (isUndefined(vars)) {\n          const toAppendTo = similar ? group.withSimilar : group.withoutSimilar;\n          toAppendTo.callbacks.push(callback);\n          if (toAppendTo.callbacks.length === 1) {\n            toAppendTo.state.set(getCurrent());\n          }\n        } else {\n          group.withVars.push({\n            state: Cell(getCurrent()),\n            similar,\n            vars,\n            callback\n          });\n        }\n      });\n      registeredFormatListeners.set(formatChangeItems);\n    };\n    const removeListeners = (registeredFormatListeners, formats, callback) => {\n      const formatChangeItems = registeredFormatListeners.get();\n      each$e(formats.split(','), format => get$a(formatChangeItems, format).each(group => {\n        formatChangeItems[format] = {\n          withSimilar: {\n            ...group.withSimilar,\n            callbacks: filter$5(group.withSimilar.callbacks, cb => cb !== callback)\n          },\n          withoutSimilar: {\n            ...group.withoutSimilar,\n            callbacks: filter$5(group.withoutSimilar.callbacks, cb => cb !== callback)\n          },\n          withVars: filter$5(group.withVars, item => item.callback !== callback)\n        };\n      }));\n      registeredFormatListeners.set(formatChangeItems);\n    };\n    const formatChangedInternal = (editor, registeredFormatListeners, formats, callback, similar, vars) => {\n      addListeners(editor, registeredFormatListeners, formats, callback, similar, vars);\n      return { unbind: () => removeListeners(registeredFormatListeners, formats, callback) };\n    };\n\n    const toggle = (editor, name, vars, node) => {\n      const fmt = editor.formatter.get(name);\n      if (fmt) {\n        if (match$2(editor, name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {\n          remove$2(editor, name, vars, node);\n        } else {\n          applyFormat$1(editor, name, vars, node);\n        }\n      }\n    };\n\n    function _typeof(obj) {\n      '@babel/helpers - typeof';\n      return _typeof = 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator ? function (obj) {\n        return typeof obj;\n      } : function (obj) {\n        return obj && 'function' == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;\n      }, _typeof(obj);\n    }\n    function _setPrototypeOf(o, p) {\n      _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n        o.__proto__ = p;\n        return o;\n      };\n      return _setPrototypeOf(o, p);\n    }\n    function _isNativeReflectConstruct() {\n      if (typeof Reflect === 'undefined' || !Reflect.construct)\n        return false;\n      if (Reflect.construct.sham)\n        return false;\n      if (typeof Proxy === 'function')\n        return true;\n      try {\n        Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {\n        }));\n        return true;\n      } catch (e) {\n        return false;\n      }\n    }\n    function _construct(Parent, args, Class) {\n      if (_isNativeReflectConstruct()) {\n        _construct = Reflect.construct;\n      } else {\n        _construct = function _construct(Parent, args, Class) {\n          var a = [null];\n          a.push.apply(a, args);\n          var Constructor = Function.bind.apply(Parent, a);\n          var instance = new Constructor();\n          if (Class)\n            _setPrototypeOf(instance, Class.prototype);\n          return instance;\n        };\n      }\n      return _construct.apply(null, arguments);\n    }\n    function _toConsumableArray(arr) {\n      return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();\n    }\n    function _arrayWithoutHoles(arr) {\n      if (Array.isArray(arr))\n        return _arrayLikeToArray(arr);\n    }\n    function _iterableToArray(iter) {\n      if (typeof Symbol !== 'undefined' && iter[Symbol.iterator] != null || iter['@@iterator'] != null)\n        return Array.from(iter);\n    }\n    function _unsupportedIterableToArray(o, minLen) {\n      if (!o)\n        return;\n      if (typeof o === 'string')\n        return _arrayLikeToArray(o, minLen);\n      var n = Object.prototype.toString.call(o).slice(8, -1);\n      if (n === 'Object' && o.constructor)\n        n = o.constructor.name;\n      if (n === 'Map' || n === 'Set')\n        return Array.from(o);\n      if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))\n        return _arrayLikeToArray(o, minLen);\n    }\n    function _arrayLikeToArray(arr, len) {\n      if (len == null || len > arr.length)\n        len = arr.length;\n      for (var i = 0, arr2 = new Array(len); i < len; i++)\n        arr2[i] = arr[i];\n      return arr2;\n    }\n    function _nonIterableSpread() {\n      throw new TypeError('Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.');\n    }\n    var hasOwnProperty = Object.hasOwnProperty, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen, getPrototypeOf = Object.getPrototypeOf, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\n    var freeze = Object.freeze, seal = Object.seal, create$8 = Object.create;\n    var _ref = typeof Reflect !== 'undefined' && Reflect, apply = _ref.apply, construct = _ref.construct;\n    if (!apply) {\n      apply = function apply(fun, thisValue, args) {\n        return fun.apply(thisValue, args);\n      };\n    }\n    if (!freeze) {\n      freeze = function freeze(x) {\n        return x;\n      };\n    }\n    if (!seal) {\n      seal = function seal(x) {\n        return x;\n      };\n    }\n    if (!construct) {\n      construct = function construct(Func, args) {\n        return _construct(Func, _toConsumableArray(args));\n      };\n    }\n    var arrayForEach = unapply(Array.prototype.forEach);\n    var arrayPop = unapply(Array.prototype.pop);\n    var arrayPush = unapply(Array.prototype.push);\n    var stringToLowerCase = unapply(String.prototype.toLowerCase);\n    var stringMatch = unapply(String.prototype.match);\n    var stringReplace = unapply(String.prototype.replace);\n    var stringIndexOf = unapply(String.prototype.indexOf);\n    var stringTrim = unapply(String.prototype.trim);\n    var regExpTest = unapply(RegExp.prototype.test);\n    var typeErrorCreate = unconstruct(TypeError);\n    function unapply(func) {\n      return function (thisArg) {\n        for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n          args[_key - 1] = arguments[_key];\n        }\n        return apply(func, thisArg, args);\n      };\n    }\n    function unconstruct(func) {\n      return function () {\n        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n          args[_key2] = arguments[_key2];\n        }\n        return construct(func, args);\n      };\n    }\n    function addToSet(set, array) {\n      if (setPrototypeOf) {\n        setPrototypeOf(set, null);\n      }\n      var l = array.length;\n      while (l--) {\n        var element = array[l];\n        if (typeof element === 'string') {\n          var lcElement = stringToLowerCase(element);\n          if (lcElement !== element) {\n            if (!isFrozen(array)) {\n              array[l] = lcElement;\n            }\n            element = lcElement;\n          }\n        }\n        set[element] = true;\n      }\n      return set;\n    }\n    function clone(object) {\n      var newObject = create$8(null);\n      var property;\n      for (property in object) {\n        if (apply(hasOwnProperty, object, [property])) {\n          newObject[property] = object[property];\n        }\n      }\n      return newObject;\n    }\n    function lookupGetter(object, prop) {\n      while (object !== null) {\n        var desc = getOwnPropertyDescriptor(object, prop);\n        if (desc) {\n          if (desc.get) {\n            return unapply(desc.get);\n          }\n          if (typeof desc.value === 'function') {\n            return unapply(desc.value);\n          }\n        }\n        object = getPrototypeOf(object);\n      }\n      function fallbackValue(element) {\n        console.warn('fallback value for', element);\n        return null;\n      }\n      return fallbackValue;\n    }\n    var html$1 = freeze([\n      'a',\n      'abbr',\n      'acronym',\n      'address',\n      'area',\n      'article',\n      'aside',\n      'audio',\n      'b',\n      'bdi',\n      'bdo',\n      'big',\n      'blink',\n      'blockquote',\n      'body',\n      'br',\n      'button',\n      'canvas',\n      'caption',\n      'center',\n      'cite',\n      'code',\n      'col',\n      'colgroup',\n      'content',\n      'data',\n      'datalist',\n      'dd',\n      'decorator',\n      'del',\n      'details',\n      'dfn',\n      'dialog',\n      'dir',\n      'div',\n      'dl',\n      'dt',\n      'element',\n      'em',\n      'fieldset',\n      'figcaption',\n      'figure',\n      'font',\n      'footer',\n      'form',\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'head',\n      'header',\n      'hgroup',\n      'hr',\n      'html',\n      'i',\n      'img',\n      'input',\n      'ins',\n      'kbd',\n      'label',\n      'legend',\n      'li',\n      'main',\n      'map',\n      'mark',\n      'marquee',\n      'menu',\n      'menuitem',\n      'meter',\n      'nav',\n      'nobr',\n      'ol',\n      'optgroup',\n      'option',\n      'output',\n      'p',\n      'picture',\n      'pre',\n      'progress',\n      'q',\n      'rp',\n      'rt',\n      'ruby',\n      's',\n      'samp',\n      'section',\n      'select',\n      'shadow',\n      'small',\n      'source',\n      'spacer',\n      'span',\n      'strike',\n      'strong',\n      'style',\n      'sub',\n      'summary',\n      'sup',\n      'table',\n      'tbody',\n      'td',\n      'template',\n      'textarea',\n      'tfoot',\n      'th',\n      'thead',\n      'time',\n      'tr',\n      'track',\n      'tt',\n      'u',\n      'ul',\n      'var',\n      'video',\n      'wbr'\n    ]);\n    var svg$1 = freeze([\n      'svg',\n      'a',\n      'altglyph',\n      'altglyphdef',\n      'altglyphitem',\n      'animatecolor',\n      'animatemotion',\n      'animatetransform',\n      'circle',\n      'clippath',\n      'defs',\n      'desc',\n      'ellipse',\n      'filter',\n      'font',\n      'g',\n      'glyph',\n      'glyphref',\n      'hkern',\n      'image',\n      'line',\n      'lineargradient',\n      'marker',\n      'mask',\n      'metadata',\n      'mpath',\n      'path',\n      'pattern',\n      'polygon',\n      'polyline',\n      'radialgradient',\n      'rect',\n      'stop',\n      'style',\n      'switch',\n      'symbol',\n      'text',\n      'textpath',\n      'title',\n      'tref',\n      'tspan',\n      'view',\n      'vkern'\n    ]);\n    var svgFilters = freeze([\n      'feBlend',\n      'feColorMatrix',\n      'feComponentTransfer',\n      'feComposite',\n      'feConvolveMatrix',\n      'feDiffuseLighting',\n      'feDisplacementMap',\n      'feDistantLight',\n      'feFlood',\n      'feFuncA',\n      'feFuncB',\n      'feFuncG',\n      'feFuncR',\n      'feGaussianBlur',\n      'feImage',\n      'feMerge',\n      'feMergeNode',\n      'feMorphology',\n      'feOffset',\n      'fePointLight',\n      'feSpecularLighting',\n      'feSpotLight',\n      'feTile',\n      'feTurbulence'\n    ]);\n    var svgDisallowed = freeze([\n      'animate',\n      'color-profile',\n      'cursor',\n      'discard',\n      'fedropshadow',\n      'font-face',\n      'font-face-format',\n      'font-face-name',\n      'font-face-src',\n      'font-face-uri',\n      'foreignobject',\n      'hatch',\n      'hatchpath',\n      'mesh',\n      'meshgradient',\n      'meshpatch',\n      'meshrow',\n      'missing-glyph',\n      'script',\n      'set',\n      'solidcolor',\n      'unknown',\n      'use'\n    ]);\n    var mathMl$1 = freeze([\n      'math',\n      'menclose',\n      'merror',\n      'mfenced',\n      'mfrac',\n      'mglyph',\n      'mi',\n      'mlabeledtr',\n      'mmultiscripts',\n      'mn',\n      'mo',\n      'mover',\n      'mpadded',\n      'mphantom',\n      'mroot',\n      'mrow',\n      'ms',\n      'mspace',\n      'msqrt',\n      'mstyle',\n      'msub',\n      'msup',\n      'msubsup',\n      'mtable',\n      'mtd',\n      'mtext',\n      'mtr',\n      'munder',\n      'munderover'\n    ]);\n    var mathMlDisallowed = freeze([\n      'maction',\n      'maligngroup',\n      'malignmark',\n      'mlongdiv',\n      'mscarries',\n      'mscarry',\n      'msgroup',\n      'mstack',\n      'msline',\n      'msrow',\n      'semantics',\n      'annotation',\n      'annotation-xml',\n      'mprescripts',\n      'none'\n    ]);\n    var text = freeze(['#text']);\n    var html = freeze([\n      'accept',\n      'action',\n      'align',\n      'alt',\n      'autocapitalize',\n      'autocomplete',\n      'autopictureinpicture',\n      'autoplay',\n      'background',\n      'bgcolor',\n      'border',\n      'capture',\n      'cellpadding',\n      'cellspacing',\n      'checked',\n      'cite',\n      'class',\n      'clear',\n      'color',\n      'cols',\n      'colspan',\n      'controls',\n      'controlslist',\n      'coords',\n      'crossorigin',\n      'datetime',\n      'decoding',\n      'default',\n      'dir',\n      'disabled',\n      'disablepictureinpicture',\n      'disableremoteplayback',\n      'download',\n      'draggable',\n      'enctype',\n      'enterkeyhint',\n      'face',\n      'for',\n      'headers',\n      'height',\n      'hidden',\n      'high',\n      'href',\n      'hreflang',\n      'id',\n      'inputmode',\n      'integrity',\n      'ismap',\n      'kind',\n      'label',\n      'lang',\n      'list',\n      'loading',\n      'loop',\n      'low',\n      'max',\n      'maxlength',\n      'media',\n      'method',\n      'min',\n      'minlength',\n      'multiple',\n      'muted',\n      'name',\n      'nonce',\n      'noshade',\n      'novalidate',\n      'nowrap',\n      'open',\n      'optimum',\n      'pattern',\n      'placeholder',\n      'playsinline',\n      'poster',\n      'preload',\n      'pubdate',\n      'radiogroup',\n      'readonly',\n      'rel',\n      'required',\n      'rev',\n      'reversed',\n      'role',\n      'rows',\n      'rowspan',\n      'spellcheck',\n      'scope',\n      'selected',\n      'shape',\n      'size',\n      'sizes',\n      'span',\n      'srclang',\n      'start',\n      'src',\n      'srcset',\n      'step',\n      'style',\n      'summary',\n      'tabindex',\n      'title',\n      'translate',\n      'type',\n      'usemap',\n      'valign',\n      'value',\n      'width',\n      'xmlns',\n      'slot'\n    ]);\n    var svg = freeze([\n      'accent-height',\n      'accumulate',\n      'additive',\n      'alignment-baseline',\n      'ascent',\n      'attributename',\n      'attributetype',\n      'azimuth',\n      'basefrequency',\n      'baseline-shift',\n      'begin',\n      'bias',\n      'by',\n      'class',\n      'clip',\n      'clippathunits',\n      'clip-path',\n      'clip-rule',\n      'color',\n      'color-interpolation',\n      'color-interpolation-filters',\n      'color-profile',\n      'color-rendering',\n      'cx',\n      'cy',\n      'd',\n      'dx',\n      'dy',\n      'diffuseconstant',\n      'direction',\n      'display',\n      'divisor',\n      'dur',\n      'edgemode',\n      'elevation',\n      'end',\n      'fill',\n      'fill-opacity',\n      'fill-rule',\n      'filter',\n      'filterunits',\n      'flood-color',\n      'flood-opacity',\n      'font-family',\n      'font-size',\n      'font-size-adjust',\n      'font-stretch',\n      'font-style',\n      'font-variant',\n      'font-weight',\n      'fx',\n      'fy',\n      'g1',\n      'g2',\n      'glyph-name',\n      'glyphref',\n      'gradientunits',\n      'gradienttransform',\n      'height',\n      'href',\n      'id',\n      'image-rendering',\n      'in',\n      'in2',\n      'k',\n      'k1',\n      'k2',\n      'k3',\n      'k4',\n      'kerning',\n      'keypoints',\n      'keysplines',\n      'keytimes',\n      'lang',\n      'lengthadjust',\n      'letter-spacing',\n      'kernelmatrix',\n      'kernelunitlength',\n      'lighting-color',\n      'local',\n      'marker-end',\n      'marker-mid',\n      'marker-start',\n      'markerheight',\n      'markerunits',\n      'markerwidth',\n      'maskcontentunits',\n      'maskunits',\n      'max',\n      'mask',\n      'media',\n      'method',\n      'mode',\n      'min',\n      'name',\n      'numoctaves',\n      'offset',\n      'operator',\n      'opacity',\n      'order',\n      'orient',\n      'orientation',\n      'origin',\n      'overflow',\n      'paint-order',\n      'path',\n      'pathlength',\n      'patterncontentunits',\n      'patterntransform',\n      'patternunits',\n      'points',\n      'preservealpha',\n      'preserveaspectratio',\n      'primitiveunits',\n      'r',\n      'rx',\n      'ry',\n      'radius',\n      'refx',\n      'refy',\n      'repeatcount',\n      'repeatdur',\n      'restart',\n      'result',\n      'rotate',\n      'scale',\n      'seed',\n      'shape-rendering',\n      'specularconstant',\n      'specularexponent',\n      'spreadmethod',\n      'startoffset',\n      'stddeviation',\n      'stitchtiles',\n      'stop-color',\n      'stop-opacity',\n      'stroke-dasharray',\n      'stroke-dashoffset',\n      'stroke-linecap',\n      'stroke-linejoin',\n      'stroke-miterlimit',\n      'stroke-opacity',\n      'stroke',\n      'stroke-width',\n      'style',\n      'surfacescale',\n      'systemlanguage',\n      'tabindex',\n      'targetx',\n      'targety',\n      'transform',\n      'transform-origin',\n      'text-anchor',\n      'text-decoration',\n      'text-rendering',\n      'textlength',\n      'type',\n      'u1',\n      'u2',\n      'unicode',\n      'values',\n      'viewbox',\n      'visibility',\n      'version',\n      'vert-adv-y',\n      'vert-origin-x',\n      'vert-origin-y',\n      'width',\n      'word-spacing',\n      'wrap',\n      'writing-mode',\n      'xchannelselector',\n      'ychannelselector',\n      'x',\n      'x1',\n      'x2',\n      'xmlns',\n      'y',\n      'y1',\n      'y2',\n      'z',\n      'zoomandpan'\n    ]);\n    var mathMl = freeze([\n      'accent',\n      'accentunder',\n      'align',\n      'bevelled',\n      'close',\n      'columnsalign',\n      'columnlines',\n      'columnspan',\n      'denomalign',\n      'depth',\n      'dir',\n      'display',\n      'displaystyle',\n      'encoding',\n      'fence',\n      'frame',\n      'height',\n      'href',\n      'id',\n      'largeop',\n      'length',\n      'linethickness',\n      'lspace',\n      'lquote',\n      'mathbackground',\n      'mathcolor',\n      'mathsize',\n      'mathvariant',\n      'maxsize',\n      'minsize',\n      'movablelimits',\n      'notation',\n      'numalign',\n      'open',\n      'rowalign',\n      'rowlines',\n      'rowspacing',\n      'rowspan',\n      'rspace',\n      'rquote',\n      'scriptlevel',\n      'scriptminsize',\n      'scriptsizemultiplier',\n      'selection',\n      'separator',\n      'separators',\n      'stretchy',\n      'subscriptshift',\n      'supscriptshift',\n      'symmetric',\n      'voffset',\n      'width',\n      'xmlns'\n    ]);\n    var xml = freeze([\n      'xlink:href',\n      'xml:id',\n      'xlink:title',\n      'xml:space',\n      'xmlns:xlink'\n    ]);\n    var MUSTACHE_EXPR = seal(/\\{\\{[\\w\\W]*|[\\w\\W]*\\}\\}/gm);\n    var ERB_EXPR = seal(/<%[\\w\\W]*|[\\w\\W]*%>/gm);\n    var DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]/);\n    var ARIA_ATTR = seal(/^aria-[\\-\\w]+$/);\n    var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i);\n    var IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\n    var ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g);\n    var DOCTYPE_NAME = seal(/^html$/i);\n    var getGlobal = function getGlobal() {\n      return typeof window === 'undefined' ? null : window;\n    };\n    var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {\n      if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n        return null;\n      }\n      var suffix = null;\n      var ATTR_NAME = 'data-tt-policy-suffix';\n      if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {\n        suffix = document.currentScript.getAttribute(ATTR_NAME);\n      }\n      var policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n      try {\n        return trustedTypes.createPolicy(policyName, {\n          createHTML: function createHTML(html) {\n            return html;\n          }\n        });\n      } catch (_) {\n        console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n        return null;\n      }\n    };\n    function createDOMPurify() {\n      var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n      var DOMPurify = function DOMPurify(root) {\n        return createDOMPurify(root);\n      };\n      DOMPurify.version = '2.3.8';\n      DOMPurify.removed = [];\n      if (!window || !window.document || window.document.nodeType !== 9) {\n        DOMPurify.isSupported = false;\n        return DOMPurify;\n      }\n      var originalDocument = window.document;\n      var document = window.document;\n      var DocumentFragment = window.DocumentFragment, HTMLTemplateElement = window.HTMLTemplateElement, Node = window.Node, Element = window.Element, NodeFilter = window.NodeFilter, _window$NamedNodeMap = window.NamedNodeMap, NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, HTMLFormElement = window.HTMLFormElement, DOMParser = window.DOMParser, trustedTypes = window.trustedTypes;\n      var ElementPrototype = Element.prototype;\n      var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n      var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n      var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n      var getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n      if (typeof HTMLTemplateElement === 'function') {\n        var template = document.createElement('template');\n        if (template.content && template.content.ownerDocument) {\n          document = template.content.ownerDocument;\n        }\n      }\n      var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);\n      var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';\n      var _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;\n      var importNode = originalDocument.importNode;\n      var documentMode = {};\n      try {\n        documentMode = clone(document).documentMode ? document.documentMode : {};\n      } catch (_) {\n      }\n      var hooks = {};\n      DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;\n      var MUSTACHE_EXPR$1 = MUSTACHE_EXPR, ERB_EXPR$1 = ERB_EXPR, DATA_ATTR$1 = DATA_ATTR, ARIA_ATTR$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$1 = ATTR_WHITESPACE;\n      var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;\n      var ALLOWED_TAGS = null;\n      var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));\n      var ALLOWED_ATTR = null;\n      var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));\n      var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {\n        tagNameCheck: {\n          writable: true,\n          configurable: false,\n          enumerable: true,\n          value: null\n        },\n        attributeNameCheck: {\n          writable: true,\n          configurable: false,\n          enumerable: true,\n          value: null\n        },\n        allowCustomizedBuiltInElements: {\n          writable: true,\n          configurable: false,\n          enumerable: true,\n          value: false\n        }\n      }));\n      var FORBID_TAGS = null;\n      var FORBID_ATTR = null;\n      var ALLOW_ARIA_ATTR = true;\n      var ALLOW_DATA_ATTR = true;\n      var ALLOW_UNKNOWN_PROTOCOLS = false;\n      var SAFE_FOR_TEMPLATES = false;\n      var WHOLE_DOCUMENT = false;\n      var SET_CONFIG = false;\n      var FORCE_BODY = false;\n      var RETURN_DOM = false;\n      var RETURN_DOM_FRAGMENT = false;\n      var RETURN_TRUSTED_TYPE = false;\n      var SANITIZE_DOM = true;\n      var KEEP_CONTENT = true;\n      var IN_PLACE = false;\n      var USE_PROFILES = {};\n      var FORBID_CONTENTS = null;\n      var DEFAULT_FORBID_CONTENTS = addToSet({}, [\n        'annotation-xml',\n        'audio',\n        'colgroup',\n        'desc',\n        'foreignobject',\n        'head',\n        'iframe',\n        'math',\n        'mi',\n        'mn',\n        'mo',\n        'ms',\n        'mtext',\n        'noembed',\n        'noframes',\n        'noscript',\n        'plaintext',\n        'script',\n        'style',\n        'svg',\n        'template',\n        'thead',\n        'title',\n        'video',\n        'xmp'\n      ]);\n      var DATA_URI_TAGS = null;\n      var DEFAULT_DATA_URI_TAGS = addToSet({}, [\n        'audio',\n        'video',\n        'img',\n        'source',\n        'image',\n        'track'\n      ]);\n      var URI_SAFE_ATTRIBUTES = null;\n      var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [\n        'alt',\n        'class',\n        'for',\n        'id',\n        'label',\n        'name',\n        'pattern',\n        'placeholder',\n        'role',\n        'summary',\n        'title',\n        'value',\n        'style',\n        'xmlns'\n      ]);\n      var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n      var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n      var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n      var NAMESPACE = HTML_NAMESPACE;\n      var IS_EMPTY_INPUT = false;\n      var PARSER_MEDIA_TYPE;\n      var SUPPORTED_PARSER_MEDIA_TYPES = [\n        'application/xhtml+xml',\n        'text/html'\n      ];\n      var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n      var transformCaseFunc;\n      var CONFIG = null;\n      var formElement = document.createElement('form');\n      var isRegexOrFunction = function isRegexOrFunction(testValue) {\n        return testValue instanceof RegExp || testValue instanceof Function;\n      };\n      var _parseConfig = function _parseConfig(cfg) {\n        if (CONFIG && CONFIG === cfg) {\n          return;\n        }\n        if (!cfg || _typeof(cfg) !== 'object') {\n          cfg = {};\n        }\n        cfg = clone(cfg);\n        ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;\n        ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;\n        URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;\n        DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;\n        FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;\n        FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};\n        FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};\n        USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;\n        ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;\n        ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;\n        ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;\n        SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;\n        WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;\n        RETURN_DOM = cfg.RETURN_DOM || false;\n        RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;\n        RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;\n        FORCE_BODY = cfg.FORCE_BODY || false;\n        SANITIZE_DOM = cfg.SANITIZE_DOM !== false;\n        KEEP_CONTENT = cfg.KEEP_CONTENT !== false;\n        IN_PLACE = cfg.IN_PLACE || false;\n        IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;\n        NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;\n        if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {\n          CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;\n        }\n        if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {\n          CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;\n        }\n        if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {\n          CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;\n        }\n        PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;\n        transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {\n          return x;\n        } : stringToLowerCase;\n        if (SAFE_FOR_TEMPLATES) {\n          ALLOW_DATA_ATTR = false;\n        }\n        if (RETURN_DOM_FRAGMENT) {\n          RETURN_DOM = true;\n        }\n        if (USE_PROFILES) {\n          ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));\n          ALLOWED_ATTR = [];\n          if (USE_PROFILES.html === true) {\n            addToSet(ALLOWED_TAGS, html$1);\n            addToSet(ALLOWED_ATTR, html);\n          }\n          if (USE_PROFILES.svg === true) {\n            addToSet(ALLOWED_TAGS, svg$1);\n            addToSet(ALLOWED_ATTR, svg);\n            addToSet(ALLOWED_ATTR, xml);\n          }\n          if (USE_PROFILES.svgFilters === true) {\n            addToSet(ALLOWED_TAGS, svgFilters);\n            addToSet(ALLOWED_ATTR, svg);\n            addToSet(ALLOWED_ATTR, xml);\n          }\n          if (USE_PROFILES.mathMl === true) {\n            addToSet(ALLOWED_TAGS, mathMl$1);\n            addToSet(ALLOWED_ATTR, mathMl);\n            addToSet(ALLOWED_ATTR, xml);\n          }\n        }\n        if (cfg.ADD_TAGS) {\n          if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n            ALLOWED_TAGS = clone(ALLOWED_TAGS);\n          }\n          addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);\n        }\n        if (cfg.ADD_ATTR) {\n          if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n            ALLOWED_ATTR = clone(ALLOWED_ATTR);\n          }\n          addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);\n        }\n        if (cfg.ADD_URI_SAFE_ATTR) {\n          addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);\n        }\n        if (cfg.FORBID_CONTENTS) {\n          if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n            FORBID_CONTENTS = clone(FORBID_CONTENTS);\n          }\n          addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);\n        }\n        if (KEEP_CONTENT) {\n          ALLOWED_TAGS['#text'] = true;\n        }\n        if (WHOLE_DOCUMENT) {\n          addToSet(ALLOWED_TAGS, [\n            'html',\n            'head',\n            'body'\n          ]);\n        }\n        if (ALLOWED_TAGS.table) {\n          addToSet(ALLOWED_TAGS, ['tbody']);\n          delete FORBID_TAGS.tbody;\n        }\n        if (freeze) {\n          freeze(cfg);\n        }\n        CONFIG = cfg;\n      };\n      var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [\n        'mi',\n        'mo',\n        'mn',\n        'ms',\n        'mtext'\n      ]);\n      var HTML_INTEGRATION_POINTS = addToSet({}, [\n        'foreignobject',\n        'desc',\n        'title',\n        'annotation-xml'\n      ]);\n      var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [\n        'title',\n        'style',\n        'font',\n        'a',\n        'script'\n      ]);\n      var ALL_SVG_TAGS = addToSet({}, svg$1);\n      addToSet(ALL_SVG_TAGS, svgFilters);\n      addToSet(ALL_SVG_TAGS, svgDisallowed);\n      var ALL_MATHML_TAGS = addToSet({}, mathMl$1);\n      addToSet(ALL_MATHML_TAGS, mathMlDisallowed);\n      var _checkValidNamespace = function _checkValidNamespace(element) {\n        var parent = getParentNode(element);\n        if (!parent || !parent.tagName) {\n          parent = {\n            namespaceURI: HTML_NAMESPACE,\n            tagName: 'template'\n          };\n        }\n        var tagName = stringToLowerCase(element.tagName);\n        var parentTagName = stringToLowerCase(parent.tagName);\n        if (element.namespaceURI === SVG_NAMESPACE) {\n          if (parent.namespaceURI === HTML_NAMESPACE) {\n            return tagName === 'svg';\n          }\n          if (parent.namespaceURI === MATHML_NAMESPACE) {\n            return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);\n          }\n          return Boolean(ALL_SVG_TAGS[tagName]);\n        }\n        if (element.namespaceURI === MATHML_NAMESPACE) {\n          if (parent.namespaceURI === HTML_NAMESPACE) {\n            return tagName === 'math';\n          }\n          if (parent.namespaceURI === SVG_NAMESPACE) {\n            return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];\n          }\n          return Boolean(ALL_MATHML_TAGS[tagName]);\n        }\n        if (element.namespaceURI === HTML_NAMESPACE) {\n          if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {\n            return false;\n          }\n          if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {\n            return false;\n          }\n          return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);\n        }\n        return false;\n      };\n      var _forceRemove = function _forceRemove(node) {\n        arrayPush(DOMPurify.removed, { element: node });\n        try {\n          node.parentNode.removeChild(node);\n        } catch (_) {\n          try {\n            node.outerHTML = emptyHTML;\n          } catch (_) {\n            node.remove();\n          }\n        }\n      };\n      var _removeAttribute = function _removeAttribute(name, node) {\n        try {\n          arrayPush(DOMPurify.removed, {\n            attribute: node.getAttributeNode(name),\n            from: node\n          });\n        } catch (_) {\n          arrayPush(DOMPurify.removed, {\n            attribute: null,\n            from: node\n          });\n        }\n        node.removeAttribute(name);\n        if (name === 'is' && !ALLOWED_ATTR[name]) {\n          if (RETURN_DOM || RETURN_DOM_FRAGMENT) {\n            try {\n              _forceRemove(node);\n            } catch (_) {\n            }\n          } else {\n            try {\n              node.setAttribute(name, '');\n            } catch (_) {\n            }\n          }\n        }\n      };\n      var _initDocument = function _initDocument(dirty) {\n        var doc;\n        var leadingWhitespace;\n        if (FORCE_BODY) {\n          dirty = '<remove></remove>' + dirty;\n        } else {\n          var matches = stringMatch(dirty, /^[\\r\\n\\t ]+/);\n          leadingWhitespace = matches && matches[0];\n        }\n        if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {\n          dirty = '<html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head><body>' + dirty + '</body></html>';\n        }\n        var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;\n        if (NAMESPACE === HTML_NAMESPACE) {\n          try {\n            doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);\n          } catch (_) {\n          }\n        }\n        if (!doc || !doc.documentElement) {\n          doc = implementation.createDocument(NAMESPACE, 'template', null);\n          try {\n            doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;\n          } catch (_) {\n          }\n        }\n        var body = doc.body || doc.documentElement;\n        if (dirty && leadingWhitespace) {\n          body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);\n        }\n        if (NAMESPACE === HTML_NAMESPACE) {\n          return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];\n        }\n        return WHOLE_DOCUMENT ? doc.documentElement : body;\n      };\n      var _createIterator = function _createIterator(root) {\n        return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);\n      };\n      var _isClobbered = function _isClobbered(elm) {\n        return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function');\n      };\n      var _isNode = function _isNode(object) {\n        return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';\n      };\n      var _executeHook = function _executeHook(entryPoint, currentNode, data) {\n        if (!hooks[entryPoint]) {\n          return;\n        }\n        arrayForEach(hooks[entryPoint], function (hook) {\n          hook.call(DOMPurify, currentNode, data, CONFIG);\n        });\n      };\n      var _sanitizeElements = function _sanitizeElements(currentNode) {\n        var content;\n        _executeHook('beforeSanitizeElements', currentNode, null);\n        if (_isClobbered(currentNode)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (regExpTest(/[\\u0080-\\uFFFF]/, currentNode.nodeName)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        var tagName = transformCaseFunc(currentNode.nodeName);\n        _executeHook('uponSanitizeElement', currentNode, {\n          tagName: tagName,\n          allowedTags: ALLOWED_TAGS\n        });\n        if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\\w]/g, currentNode.innerHTML) && regExpTest(/<[/\\w]/g, currentNode.textContent)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n          if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {\n            if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName))\n              return false;\n            if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName))\n              return false;\n          }\n          if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {\n            var parentNode = getParentNode(currentNode) || currentNode.parentNode;\n            var childNodes = getChildNodes(currentNode) || currentNode.childNodes;\n            if (childNodes && parentNode) {\n              var childCount = childNodes.length;\n              for (var i = childCount - 1; i >= 0; --i) {\n                parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));\n              }\n            }\n          }\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\\/no(script|embed)/i, currentNode.innerHTML)) {\n          _forceRemove(currentNode);\n          return true;\n        }\n        if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {\n          content = currentNode.textContent;\n          content = stringReplace(content, MUSTACHE_EXPR$1, ' ');\n          content = stringReplace(content, ERB_EXPR$1, ' ');\n          if (currentNode.textContent !== content) {\n            arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });\n            currentNode.textContent = content;\n          }\n        }\n        _executeHook('afterSanitizeElements', currentNode, null);\n        return false;\n      };\n      var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {\n        if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {\n          return false;\n        }\n        if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName));\n        else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName));\n        else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {\n          if (_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value)));\n          else {\n            return false;\n          }\n        } else if (URI_SAFE_ATTRIBUTES[lcName]);\n        else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, '')));\n        else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]);\n        else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, '')));\n        else if (!value);\n        else {\n          return false;\n        }\n        return true;\n      };\n      var _basicCustomElementTest = function _basicCustomElementTest(tagName) {\n        return tagName.indexOf('-') > 0;\n      };\n      var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {\n        var attr;\n        var value;\n        var lcName;\n        var l;\n        _executeHook('beforeSanitizeAttributes', currentNode, null);\n        var attributes = currentNode.attributes;\n        if (!attributes) {\n          return;\n        }\n        var hookEvent = {\n          attrName: '',\n          attrValue: '',\n          keepAttr: true,\n          allowedAttributes: ALLOWED_ATTR\n        };\n        l = attributes.length;\n        while (l--) {\n          attr = attributes[l];\n          var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI;\n          value = name === 'value' ? attr.value : stringTrim(attr.value);\n          lcName = transformCaseFunc(name);\n          var initValue = value;\n          hookEvent.attrName = lcName;\n          hookEvent.attrValue = value;\n          hookEvent.keepAttr = true;\n          hookEvent.forceKeepAttr = undefined;\n          _executeHook('uponSanitizeAttribute', currentNode, hookEvent);\n          value = hookEvent.attrValue;\n          if (hookEvent.forceKeepAttr) {\n            continue;\n          }\n          if (!hookEvent.keepAttr) {\n            _removeAttribute(name, currentNode);\n            continue;\n          }\n          if (regExpTest(/\\/>/i, value)) {\n            _removeAttribute(name, currentNode);\n            continue;\n          }\n          if (SAFE_FOR_TEMPLATES) {\n            value = stringReplace(value, MUSTACHE_EXPR$1, ' ');\n            value = stringReplace(value, ERB_EXPR$1, ' ');\n          }\n          var lcTag = transformCaseFunc(currentNode.nodeName);\n          if (!_isValidAttribute(lcTag, lcName, value)) {\n            _removeAttribute(name, currentNode);\n            continue;\n          }\n          if (value !== initValue) {\n            try {\n              if (namespaceURI) {\n                currentNode.setAttributeNS(namespaceURI, name, value);\n              } else {\n                currentNode.setAttribute(name, value);\n              }\n            } catch (_) {\n              _removeAttribute(name, currentNode);\n            }\n          }\n        }\n        _executeHook('afterSanitizeAttributes', currentNode, null);\n      };\n      var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {\n        var shadowNode;\n        var shadowIterator = _createIterator(fragment);\n        _executeHook('beforeSanitizeShadowDOM', fragment, null);\n        while (shadowNode = shadowIterator.nextNode()) {\n          _executeHook('uponSanitizeShadowNode', shadowNode, null);\n          if (_sanitizeElements(shadowNode)) {\n            continue;\n          }\n          if (shadowNode.content instanceof DocumentFragment) {\n            _sanitizeShadowDOM(shadowNode.content);\n          }\n          _sanitizeAttributes(shadowNode);\n        }\n        _executeHook('afterSanitizeShadowDOM', fragment, null);\n      };\n      DOMPurify.sanitize = function (dirty, cfg) {\n        var body;\n        var importedNode;\n        var currentNode;\n        var oldNode;\n        var returnNode;\n        IS_EMPTY_INPUT = !dirty;\n        if (IS_EMPTY_INPUT) {\n          dirty = '<!-->';\n        }\n        if (typeof dirty !== 'string' && !_isNode(dirty)) {\n          if (typeof dirty.toString !== 'function') {\n            throw typeErrorCreate('toString is not a function');\n          } else {\n            dirty = dirty.toString();\n            if (typeof dirty !== 'string') {\n              throw typeErrorCreate('dirty is not a string, aborting');\n            }\n          }\n        }\n        if (!DOMPurify.isSupported) {\n          if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {\n            if (typeof dirty === 'string') {\n              return window.toStaticHTML(dirty);\n            }\n            if (_isNode(dirty)) {\n              return window.toStaticHTML(dirty.outerHTML);\n            }\n          }\n          return dirty;\n        }\n        if (!SET_CONFIG) {\n          _parseConfig(cfg);\n        }\n        DOMPurify.removed = [];\n        if (typeof dirty === 'string') {\n          IN_PLACE = false;\n        }\n        if (IN_PLACE) {\n          if (dirty.nodeName) {\n            var tagName = transformCaseFunc(dirty.nodeName);\n            if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {\n              throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');\n            }\n          }\n        } else if (dirty instanceof Node) {\n          body = _initDocument('<!---->');\n          importedNode = body.ownerDocument.importNode(dirty, true);\n          if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {\n            body = importedNode;\n          } else if (importedNode.nodeName === 'HTML') {\n            body = importedNode;\n          } else {\n            body.appendChild(importedNode);\n          }\n        } else {\n          if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {\n            return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;\n          }\n          body = _initDocument(dirty);\n          if (!body) {\n            return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';\n          }\n        }\n        if (body && FORCE_BODY) {\n          _forceRemove(body.firstChild);\n        }\n        var nodeIterator = _createIterator(IN_PLACE ? dirty : body);\n        while (currentNode = nodeIterator.nextNode()) {\n          if (currentNode.nodeType === 3 && currentNode === oldNode) {\n            continue;\n          }\n          if (_sanitizeElements(currentNode)) {\n            continue;\n          }\n          if (currentNode.content instanceof DocumentFragment) {\n            _sanitizeShadowDOM(currentNode.content);\n          }\n          _sanitizeAttributes(currentNode);\n          oldNode = currentNode;\n        }\n        oldNode = null;\n        if (IN_PLACE) {\n          return dirty;\n        }\n        if (RETURN_DOM) {\n          if (RETURN_DOM_FRAGMENT) {\n            returnNode = createDocumentFragment.call(body.ownerDocument);\n            while (body.firstChild) {\n              returnNode.appendChild(body.firstChild);\n            }\n          } else {\n            returnNode = body;\n          }\n          if (ALLOWED_ATTR.shadowroot) {\n            returnNode = importNode.call(originalDocument, returnNode, true);\n          }\n          return returnNode;\n        }\n        var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;\n        if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {\n          serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\\n' + serializedHTML;\n        }\n        if (SAFE_FOR_TEMPLATES) {\n          serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');\n          serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');\n        }\n        return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;\n      };\n      DOMPurify.setConfig = function (cfg) {\n        _parseConfig(cfg);\n        SET_CONFIG = true;\n      };\n      DOMPurify.clearConfig = function () {\n        CONFIG = null;\n        SET_CONFIG = false;\n      };\n      DOMPurify.isValidAttribute = function (tag, attr, value) {\n        if (!CONFIG) {\n          _parseConfig({});\n        }\n        var lcTag = transformCaseFunc(tag);\n        var lcName = transformCaseFunc(attr);\n        return _isValidAttribute(lcTag, lcName, value);\n      };\n      DOMPurify.addHook = function (entryPoint, hookFunction) {\n        if (typeof hookFunction !== 'function') {\n          return;\n        }\n        hooks[entryPoint] = hooks[entryPoint] || [];\n        arrayPush(hooks[entryPoint], hookFunction);\n      };\n      DOMPurify.removeHook = function (entryPoint) {\n        if (hooks[entryPoint]) {\n          return arrayPop(hooks[entryPoint]);\n        }\n      };\n      DOMPurify.removeHooks = function (entryPoint) {\n        if (hooks[entryPoint]) {\n          hooks[entryPoint] = [];\n        }\n      };\n      DOMPurify.removeAllHooks = function () {\n        hooks = {};\n      };\n      return DOMPurify;\n    }\n    var purify = createDOMPurify();\n\n    const explode$1 = Tools.explode;\n    const create$7 = () => {\n      const filters = {};\n      const addFilter = (name, callback) => {\n        each$e(explode$1(name), name => {\n          if (!has$2(filters, name)) {\n            filters[name] = {\n              name,\n              callbacks: []\n            };\n          }\n          filters[name].callbacks.push(callback);\n        });\n      };\n      const getFilters = () => values(filters);\n      const removeFilter = (name, callback) => {\n        each$e(explode$1(name), name => {\n          if (has$2(filters, name)) {\n            if (isNonNullable(callback)) {\n              const filter = filters[name];\n              const newCallbacks = filter$5(filter.callbacks, c => c !== callback);\n              if (newCallbacks.length > 0) {\n                filter.callbacks = newCallbacks;\n              } else {\n                delete filters[name];\n              }\n            } else {\n              delete filters[name];\n            }\n          }\n        });\n      };\n      return {\n        addFilter,\n        getFilters,\n        removeFilter\n      };\n    };\n\n    const removeAttrs = (node, names) => {\n      each$e(names, name => {\n        node.attr(name, null);\n      });\n    };\n    const addFontToSpansFilter = (domParser, styles, fontSizes) => {\n      domParser.addNodeFilter('font', nodes => {\n        each$e(nodes, node => {\n          const props = styles.parse(node.attr('style'));\n          const color = node.attr('color');\n          const face = node.attr('face');\n          const size = node.attr('size');\n          if (color) {\n            props.color = color;\n          }\n          if (face) {\n            props['font-family'] = face;\n          }\n          if (size) {\n            toInt(size).each(num => {\n              props['font-size'] = fontSizes[num - 1];\n            });\n          }\n          node.name = 'span';\n          node.attr('style', styles.serialize(props));\n          removeAttrs(node, [\n            'color',\n            'face',\n            'size'\n          ]);\n        });\n      });\n    };\n    const addStrikeFilter = (domParser, schema, styles) => {\n      domParser.addNodeFilter('strike', nodes => {\n        const convertToSTag = schema.type !== 'html4';\n        each$e(nodes, node => {\n          if (convertToSTag) {\n            node.name = 's';\n          } else {\n            const props = styles.parse(node.attr('style'));\n            props['text-decoration'] = 'line-through';\n            node.name = 'span';\n            node.attr('style', styles.serialize(props));\n          }\n        });\n      });\n    };\n    const addFilters = (domParser, settings, schema) => {\n      var _a;\n      const styles = Styles();\n      if (settings.convert_fonts_to_spans) {\n        addFontToSpansFilter(domParser, styles, Tools.explode((_a = settings.font_size_legacy_values) !== null && _a !== void 0 ? _a : ''));\n      }\n      addStrikeFilter(domParser, schema, styles);\n    };\n    const register$5 = (domParser, settings, schema) => {\n      if (settings.inline_styles) {\n        addFilters(domParser, settings, schema);\n      }\n    };\n\n    const blobUriToBlob = url => fetch(url).then(res => res.ok ? res.blob() : Promise.reject()).catch(() => Promise.reject(`Cannot convert ${ url } to Blob. Resource might not exist or is inaccessible.`));\n    const extractBase64Data = data => {\n      const matches = /([a-z0-9+\\/=\\s]+)/i.exec(data);\n      return matches ? matches[1] : '';\n    };\n    const parseDataUri = uri => {\n      const [type, ...rest] = uri.split(',');\n      const data = rest.join(',');\n      const matches = /data:([^/]+\\/[^;]+)(;.+)?/.exec(type);\n      if (matches) {\n        const base64Encoded = matches[2] === ';base64';\n        const extractedData = base64Encoded ? extractBase64Data(data) : decodeURIComponent(data);\n        return Optional.some({\n          type: matches[1],\n          data: extractedData,\n          base64Encoded\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n    const buildBlob = (type, data, base64Encoded = true) => {\n      let str = data;\n      if (base64Encoded) {\n        try {\n          str = atob(data);\n        } catch (e) {\n          return Optional.none();\n        }\n      }\n      const arr = new Uint8Array(str.length);\n      for (let i = 0; i < arr.length; i++) {\n        arr[i] = str.charCodeAt(i);\n      }\n      return Optional.some(new Blob([arr], { type }));\n    };\n    const dataUriToBlob = uri => {\n      return new Promise((resolve, reject) => {\n        parseDataUri(uri).bind(({type, data, base64Encoded}) => buildBlob(type, data, base64Encoded)).fold(() => reject('Invalid data URI'), resolve);\n      });\n    };\n    const uriToBlob = url => {\n      if (startsWith(url, 'blob:')) {\n        return blobUriToBlob(url);\n      } else if (startsWith(url, 'data:')) {\n        return dataUriToBlob(url);\n      } else {\n        return Promise.reject('Unknown URI format');\n      }\n    };\n    const blobToDataUri = blob => {\n      return new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.onloadend = () => {\n          resolve(reader.result);\n        };\n        reader.onerror = () => {\n          var _a;\n          reject((_a = reader.error) === null || _a === void 0 ? void 0 : _a.message);\n        };\n        reader.readAsDataURL(blob);\n      });\n    };\n\n    let count$1 = 0;\n    const uniqueId$1 = prefix => {\n      return (prefix || 'blobid') + count$1++;\n    };\n    const processDataUri = (dataUri, base64Only, generateBlobInfo) => {\n      return parseDataUri(dataUri).bind(({data, type, base64Encoded}) => {\n        if (base64Only && !base64Encoded) {\n          return Optional.none();\n        } else {\n          const base64 = base64Encoded ? data : btoa(data);\n          return generateBlobInfo(base64, type);\n        }\n      });\n    };\n    const createBlobInfo$1 = (blobCache, blob, base64) => {\n      const blobInfo = blobCache.create(uniqueId$1(), blob, base64);\n      blobCache.add(blobInfo);\n      return blobInfo;\n    };\n    const dataUriToBlobInfo = (blobCache, dataUri, base64Only = false) => {\n      return processDataUri(dataUri, base64Only, (base64, type) => Optional.from(blobCache.getByData(base64, type)).orThunk(() => buildBlob(type, base64).map(blob => createBlobInfo$1(blobCache, blob, base64))));\n    };\n    const imageToBlobInfo = (blobCache, imageSrc) => {\n      const invalidDataUri = () => Promise.reject('Invalid data URI');\n      if (startsWith(imageSrc, 'blob:')) {\n        const blobInfo = blobCache.getByUri(imageSrc);\n        if (isNonNullable(blobInfo)) {\n          return Promise.resolve(blobInfo);\n        } else {\n          return uriToBlob(imageSrc).then(blob => {\n            return blobToDataUri(blob).then(dataUri => {\n              return processDataUri(dataUri, false, base64 => {\n                return Optional.some(createBlobInfo$1(blobCache, blob, base64));\n              }).getOrThunk(invalidDataUri);\n            });\n          });\n        }\n      } else if (startsWith(imageSrc, 'data:')) {\n        return dataUriToBlobInfo(blobCache, imageSrc).fold(invalidDataUri, blobInfo => Promise.resolve(blobInfo));\n      } else {\n        return Promise.reject('Unknown image data format');\n      }\n    };\n\n    const isBogusImage = img => isNonNullable(img.attr('data-mce-bogus'));\n    const isInternalImageSource = img => img.attr('src') === Env.transparentSrc || isNonNullable(img.attr('data-mce-placeholder'));\n    const registerBase64ImageFilter = (parser, settings) => {\n      const {blob_cache: blobCache} = settings;\n      if (blobCache) {\n        const processImage = img => {\n          const inputSrc = img.attr('src');\n          if (isInternalImageSource(img) || isBogusImage(img) || isNullable(inputSrc)) {\n            return;\n          }\n          dataUriToBlobInfo(blobCache, inputSrc, true).each(blobInfo => {\n            img.attr('src', blobInfo.blobUri());\n          });\n        };\n        parser.addAttributeFilter('src', nodes => each$e(nodes, processImage));\n      }\n    };\n    const register$4 = (parser, settings) => {\n      const schema = parser.schema;\n      if (settings.remove_trailing_brs) {\n        parser.addNodeFilter('br', (nodes, _, args) => {\n          const blockElements = Tools.extend({}, schema.getBlockElements());\n          const nonEmptyElements = schema.getNonEmptyElements();\n          const whitespaceElements = schema.getWhitespaceElements();\n          blockElements.body = 1;\n          const isBlock = node => node.name in blockElements && isTransparentAstInline(schema, node);\n          for (let i = 0, l = nodes.length; i < l; i++) {\n            let node = nodes[i];\n            let parent = node.parent;\n            if (parent && blockElements[parent.name] && node === parent.lastChild) {\n              let prev = node.prev;\n              while (prev) {\n                const prevName = prev.name;\n                if (prevName !== 'span' || prev.attr('data-mce-type') !== 'bookmark') {\n                  if (prevName === 'br') {\n                    node = null;\n                  }\n                  break;\n                }\n                prev = prev.prev;\n              }\n              if (node) {\n                node.remove();\n                if (isEmpty(schema, nonEmptyElements, whitespaceElements, parent)) {\n                  const elementRule = schema.getElementRule(parent.name);\n                  if (elementRule) {\n                    if (elementRule.removeEmpty) {\n                      parent.remove();\n                    } else if (elementRule.paddEmpty) {\n                      paddEmptyNode(args, isBlock, parent);\n                    }\n                  }\n                }\n              }\n            } else {\n              let lastParent = node;\n              while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {\n                lastParent = parent;\n                if (blockElements[parent.name]) {\n                  break;\n                }\n                parent = parent.parent;\n              }\n              if (lastParent === parent) {\n                const textNode = new AstNode('#text', 3);\n                textNode.value = nbsp;\n                node.replace(textNode);\n              }\n            }\n          }\n        });\n      }\n      parser.addAttributeFilter('href', nodes => {\n        let i = nodes.length;\n        const appendRel = rel => {\n          const parts = rel.split(' ').filter(p => p.length > 0);\n          return parts.concat(['noopener']).sort().join(' ');\n        };\n        const addNoOpener = rel => {\n          const newRel = rel ? Tools.trim(rel) : '';\n          if (!/\\b(noopener)\\b/g.test(newRel)) {\n            return appendRel(newRel);\n          } else {\n            return newRel;\n          }\n        };\n        if (!settings.allow_unsafe_link_target) {\n          while (i--) {\n            const node = nodes[i];\n            if (node.name === 'a' && node.attr('target') === '_blank') {\n              node.attr('rel', addNoOpener(node.attr('rel')));\n            }\n          }\n        }\n      });\n      if (!settings.allow_html_in_named_anchor) {\n        parser.addAttributeFilter('id,name', nodes => {\n          let i = nodes.length, sibling, prevSibling, parent, node;\n          while (i--) {\n            node = nodes[i];\n            if (node.name === 'a' && node.firstChild && !node.attr('href')) {\n              parent = node.parent;\n              sibling = node.lastChild;\n              while (sibling && parent) {\n                prevSibling = sibling.prev;\n                parent.insert(sibling, node);\n                sibling = prevSibling;\n              }\n            }\n          }\n        });\n      }\n      if (settings.fix_list_elements) {\n        parser.addNodeFilter('ul,ol', nodes => {\n          let i = nodes.length, node, parentNode;\n          while (i--) {\n            node = nodes[i];\n            parentNode = node.parent;\n            if (parentNode && (parentNode.name === 'ul' || parentNode.name === 'ol')) {\n              if (node.prev && node.prev.name === 'li') {\n                node.prev.append(node);\n              } else {\n                const li = new AstNode('li', 1);\n                li.attr('style', 'list-style-type: none');\n                node.wrap(li);\n              }\n            }\n          }\n        });\n      }\n      const validClasses = schema.getValidClasses();\n      if (settings.validate && validClasses) {\n        parser.addAttributeFilter('class', nodes => {\n          var _a;\n          let i = nodes.length;\n          while (i--) {\n            const node = nodes[i];\n            const clazz = (_a = node.attr('class')) !== null && _a !== void 0 ? _a : '';\n            const classList = Tools.explode(clazz, ' ');\n            let classValue = '';\n            for (let ci = 0; ci < classList.length; ci++) {\n              const className = classList[ci];\n              let valid = false;\n              let validClassesMap = validClasses['*'];\n              if (validClassesMap && validClassesMap[className]) {\n                valid = true;\n              }\n              validClassesMap = validClasses[node.name];\n              if (!valid && validClassesMap && validClassesMap[className]) {\n                valid = true;\n              }\n              if (valid) {\n                if (classValue) {\n                  classValue += ' ';\n                }\n                classValue += className;\n              }\n            }\n            if (!classValue.length) {\n              classValue = null;\n            }\n            node.attr('class', classValue);\n          }\n        });\n      }\n      registerBase64ImageFilter(parser, settings);\n    };\n\n    const each$4 = Tools.each, trim = Tools.trim;\n    const queryParts = [\n      'source',\n      'protocol',\n      'authority',\n      'userInfo',\n      'user',\n      'password',\n      'host',\n      'port',\n      'relative',\n      'path',\n      'directory',\n      'file',\n      'query',\n      'anchor'\n    ];\n    const DEFAULT_PORTS = {\n      ftp: 21,\n      http: 80,\n      https: 443,\n      mailto: 25\n    };\n    const safeSvgDataUrlElements = [\n      'img',\n      'video'\n    ];\n    const blockSvgDataUris = (allowSvgDataUrls, tagName) => {\n      if (isNonNullable(allowSvgDataUrls)) {\n        return !allowSvgDataUrls;\n      } else {\n        return isNonNullable(tagName) ? !contains$2(safeSvgDataUrlElements, tagName) : true;\n      }\n    };\n    const decodeUri = encodedUri => {\n      try {\n        return decodeURIComponent(encodedUri);\n      } catch (ex) {\n        return unescape(encodedUri);\n      }\n    };\n    const isInvalidUri = (settings, uri, tagName) => {\n      const decodedUri = decodeUri(uri);\n      if (settings.allow_script_urls) {\n        return false;\n      } else if (/((java|vb)script|mhtml):/i.test(decodedUri)) {\n        return true;\n      } else if (settings.allow_html_data_urls) {\n        return false;\n      } else if (/^data:image\\//i.test(decodedUri)) {\n        return blockSvgDataUris(settings.allow_svg_data_urls, tagName) && /^data:image\\/svg\\+xml/i.test(decodedUri);\n      } else {\n        return /^data:/i.test(decodedUri);\n      }\n    };\n    class URI {\n      constructor(url, settings = {}) {\n        this.path = '';\n        this.directory = '';\n        url = trim(url);\n        this.settings = settings;\n        const baseUri = settings.base_uri;\n        const self = this;\n        if (/^([\\w\\-]+):([^\\/]{2})/i.test(url) || /^\\s*#/.test(url)) {\n          self.source = url;\n          return;\n        }\n        const isProtocolRelative = url.indexOf('//') === 0;\n        if (url.indexOf('/') === 0 && !isProtocolRelative) {\n          url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url;\n        }\n        if (!/^[\\w\\-]*:?\\/\\//.test(url)) {\n          const baseUrl = baseUri ? baseUri.path : new URI(document.location.href).directory;\n          if ((baseUri === null || baseUri === void 0 ? void 0 : baseUri.protocol) === '') {\n            url = '//mce_host' + self.toAbsPath(baseUrl, url);\n          } else {\n            const match = /([^#?]*)([#?]?.*)/.exec(url);\n            if (match) {\n              url = (baseUri && baseUri.protocol || 'http') + '://mce_host' + self.toAbsPath(baseUrl, match[1]) + match[2];\n            }\n          }\n        }\n        url = url.replace(/@@/g, '(mce_at)');\n        const urlMatch = /^(?:(?![^:@]+:[^:@\\/]*@)([^:\\/?#.]+):)?(?:\\/\\/)?((?:(([^:@\\/]*):?([^:@\\/]*))?@)?(\\[[a-zA-Z0-9:.%]+\\]|[^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/.exec(url);\n        if (urlMatch) {\n          each$4(queryParts, (v, i) => {\n            let part = urlMatch[i];\n            if (part) {\n              part = part.replace(/\\(mce_at\\)/g, '@@');\n            }\n            self[v] = part;\n          });\n        }\n        if (baseUri) {\n          if (!self.protocol) {\n            self.protocol = baseUri.protocol;\n          }\n          if (!self.userInfo) {\n            self.userInfo = baseUri.userInfo;\n          }\n          if (!self.port && self.host === 'mce_host') {\n            self.port = baseUri.port;\n          }\n          if (!self.host || self.host === 'mce_host') {\n            self.host = baseUri.host;\n          }\n          self.source = '';\n        }\n        if (isProtocolRelative) {\n          self.protocol = '';\n        }\n      }\n      static parseDataUri(uri) {\n        let type;\n        const uriComponents = decodeURIComponent(uri).split(',');\n        const matches = /data:([^;]+)/.exec(uriComponents[0]);\n        if (matches) {\n          type = matches[1];\n        }\n        return {\n          type,\n          data: uriComponents[1]\n        };\n      }\n      static isDomSafe(uri, context, options = {}) {\n        if (options.allow_script_urls) {\n          return true;\n        } else {\n          const decodedUri = Entities.decode(uri).replace(/[\\s\\u0000-\\u001F]+/g, '');\n          return !isInvalidUri(options, decodedUri, context);\n        }\n      }\n      static getDocumentBaseUrl(loc) {\n        var _a;\n        let baseUrl;\n        if (loc.protocol.indexOf('http') !== 0 && loc.protocol !== 'file:') {\n          baseUrl = (_a = loc.href) !== null && _a !== void 0 ? _a : '';\n        } else {\n          baseUrl = loc.protocol + '//' + loc.host + loc.pathname;\n        }\n        if (/^[^:]+:\\/\\/\\/?[^\\/]+\\//.test(baseUrl)) {\n          baseUrl = baseUrl.replace(/[\\?#].*$/, '').replace(/[\\/\\\\][^\\/]+$/, '');\n          if (!/[\\/\\\\]$/.test(baseUrl)) {\n            baseUrl += '/';\n          }\n        }\n        return baseUrl;\n      }\n      setPath(path) {\n        const pathMatch = /^(.*?)\\/?(\\w+)?$/.exec(path);\n        if (pathMatch) {\n          this.path = pathMatch[0];\n          this.directory = pathMatch[1];\n          this.file = pathMatch[2];\n        }\n        this.source = '';\n        this.getURI();\n      }\n      toRelative(uri) {\n        if (uri === './') {\n          return uri;\n        }\n        const relativeUri = new URI(uri, { base_uri: this });\n        if (relativeUri.host !== 'mce_host' && this.host !== relativeUri.host && relativeUri.host || this.port !== relativeUri.port || this.protocol !== relativeUri.protocol && relativeUri.protocol !== '') {\n          return relativeUri.getURI();\n        }\n        const tu = this.getURI(), uu = relativeUri.getURI();\n        if (tu === uu || tu.charAt(tu.length - 1) === '/' && tu.substr(0, tu.length - 1) === uu) {\n          return tu;\n        }\n        let output = this.toRelPath(this.path, relativeUri.path);\n        if (relativeUri.query) {\n          output += '?' + relativeUri.query;\n        }\n        if (relativeUri.anchor) {\n          output += '#' + relativeUri.anchor;\n        }\n        return output;\n      }\n      toAbsolute(uri, noHost) {\n        const absoluteUri = new URI(uri, { base_uri: this });\n        return absoluteUri.getURI(noHost && this.isSameOrigin(absoluteUri));\n      }\n      isSameOrigin(uri) {\n        if (this.host == uri.host && this.protocol == uri.protocol) {\n          if (this.port == uri.port) {\n            return true;\n          }\n          const defaultPort = this.protocol ? DEFAULT_PORTS[this.protocol] : null;\n          if (defaultPort && (this.port || defaultPort) == (uri.port || defaultPort)) {\n            return true;\n          }\n        }\n        return false;\n      }\n      toRelPath(base, path) {\n        let breakPoint = 0, out = '', i, l;\n        const normalizedBase = base.substring(0, base.lastIndexOf('/')).split('/');\n        const items = path.split('/');\n        if (normalizedBase.length >= items.length) {\n          for (i = 0, l = normalizedBase.length; i < l; i++) {\n            if (i >= items.length || normalizedBase[i] !== items[i]) {\n              breakPoint = i + 1;\n              break;\n            }\n          }\n        }\n        if (normalizedBase.length < items.length) {\n          for (i = 0, l = items.length; i < l; i++) {\n            if (i >= normalizedBase.length || normalizedBase[i] !== items[i]) {\n              breakPoint = i + 1;\n              break;\n            }\n          }\n        }\n        if (breakPoint === 1) {\n          return path;\n        }\n        for (i = 0, l = normalizedBase.length - (breakPoint - 1); i < l; i++) {\n          out += '../';\n        }\n        for (i = breakPoint - 1, l = items.length; i < l; i++) {\n          if (i !== breakPoint - 1) {\n            out += '/' + items[i];\n          } else {\n            out += items[i];\n          }\n        }\n        return out;\n      }\n      toAbsPath(base, path) {\n        let nb = 0;\n        const tr = /\\/$/.test(path) ? '/' : '';\n        const normalizedBase = base.split('/');\n        const normalizedPath = path.split('/');\n        const baseParts = [];\n        each$4(normalizedBase, k => {\n          if (k) {\n            baseParts.push(k);\n          }\n        });\n        const pathParts = [];\n        for (let i = normalizedPath.length - 1; i >= 0; i--) {\n          if (normalizedPath[i].length === 0 || normalizedPath[i] === '.') {\n            continue;\n          }\n          if (normalizedPath[i] === '..') {\n            nb++;\n            continue;\n          }\n          if (nb > 0) {\n            nb--;\n            continue;\n          }\n          pathParts.push(normalizedPath[i]);\n        }\n        const i = baseParts.length - nb;\n        let outPath;\n        if (i <= 0) {\n          outPath = reverse(pathParts).join('/');\n        } else {\n          outPath = baseParts.slice(0, i).join('/') + '/' + reverse(pathParts).join('/');\n        }\n        if (outPath.indexOf('/') !== 0) {\n          outPath = '/' + outPath;\n        }\n        if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) {\n          outPath += tr;\n        }\n        return outPath;\n      }\n      getURI(noProtoHost = false) {\n        let s;\n        if (!this.source || noProtoHost) {\n          s = '';\n          if (!noProtoHost) {\n            if (this.protocol) {\n              s += this.protocol + '://';\n            } else {\n              s += '//';\n            }\n            if (this.userInfo) {\n              s += this.userInfo + '@';\n            }\n            if (this.host) {\n              s += this.host;\n            }\n            if (this.port) {\n              s += ':' + this.port;\n            }\n          }\n          if (this.path) {\n            s += this.path;\n          }\n          if (this.query) {\n            s += '?' + this.query;\n          }\n          if (this.anchor) {\n            s += '#' + this.anchor;\n          }\n          this.source = s;\n        }\n        return this.source;\n      }\n    }\n\n    const makeMap = Tools.makeMap, extend$1 = Tools.extend;\n    const basePurifyConfig = {\n      IN_PLACE: true,\n      ALLOW_UNKNOWN_PROTOCOLS: true,\n      ALLOWED_TAGS: [\n        '#comment',\n        '#cdata-section',\n        'body'\n      ],\n      ALLOWED_ATTR: []\n    };\n    const filteredUrlAttrs = Tools.makeMap('src,href,data,background,action,formaction,poster,xlink:href');\n    const internalElementAttr = 'data-mce-type';\n    const getPurifyConfig = (settings, mimeType) => {\n      const config = { ...basePurifyConfig };\n      config.PARSER_MEDIA_TYPE = mimeType;\n      if (settings.allow_script_urls) {\n        config.ALLOWED_URI_REGEXP = /.*/;\n      } else if (settings.allow_html_data_urls) {\n        config.ALLOWED_URI_REGEXP = /^(?!(\\w+script|mhtml):)/i;\n      }\n      return config;\n    };\n    const setupPurify = (settings, schema) => {\n      const purify$1 = purify();\n      const specialElements = schema.getSpecialElements();\n      const validate = settings.validate;\n      let uid = 0;\n      purify$1.addHook('uponSanitizeElement', (ele, evt) => {\n        var _a, _b, _c;\n        if (ele.nodeType === COMMENT && !settings.allow_conditional_comments && /^\\[if/i.test((_a = ele.nodeValue) !== null && _a !== void 0 ? _a : '')) {\n          ele.nodeValue = ' ' + ele.nodeValue;\n        }\n        const tagName = evt.tagName;\n        if (ele.nodeType !== ELEMENT || tagName === 'body') {\n          return;\n        }\n        const element = SugarElement.fromDom(ele);\n        const lcTagName = tagName.toLowerCase();\n        const isInternalElement = has$1(element, internalElementAttr);\n        const bogus = get$9(element, 'data-mce-bogus');\n        if (!isInternalElement && isString(bogus)) {\n          if (bogus === 'all') {\n            remove$6(element);\n          } else {\n            unwrap(element);\n          }\n          return;\n        }\n        const rule = schema.getElementRule(lcTagName);\n        if (validate && !rule) {\n          if (has$2(specialElements, lcTagName)) {\n            remove$6(element);\n          } else {\n            unwrap(element);\n          }\n          return;\n        } else {\n          evt.allowedTags[tagName] = true;\n        }\n        if (validate && rule && !isInternalElement) {\n          each$e((_b = rule.attributesForced) !== null && _b !== void 0 ? _b : [], attr => {\n            set$2(element, attr.name, attr.value === '{$uid}' ? `mce_${ uid++ }` : attr.value);\n          });\n          each$e((_c = rule.attributesDefault) !== null && _c !== void 0 ? _c : [], attr => {\n            if (!has$1(element, attr.name)) {\n              set$2(element, attr.name, attr.value === '{$uid}' ? `mce_${ uid++ }` : attr.value);\n            }\n          });\n          if (rule.attributesRequired && !exists(rule.attributesRequired, attr => has$1(element, attr))) {\n            unwrap(element);\n            return;\n          }\n          if (rule.removeEmptyAttrs && hasNone(element)) {\n            unwrap(element);\n            return;\n          }\n          if (rule.outputName && rule.outputName !== lcTagName) {\n            mutate(element, rule.outputName);\n          }\n        }\n      });\n      purify$1.addHook('uponSanitizeAttribute', (ele, evt) => {\n        const tagName = ele.tagName.toLowerCase();\n        const {attrName, attrValue} = evt;\n        evt.keepAttr = !validate || schema.isValid(tagName, attrName) || startsWith(attrName, 'data-') || startsWith(attrName, 'aria-');\n        if (attrName in filteredUrlAttrs && isInvalidUri(settings, attrValue, tagName)) {\n          evt.keepAttr = false;\n        }\n        if (evt.keepAttr) {\n          evt.allowedAttributes[attrName] = true;\n          if (attrName in schema.getBoolAttrs()) {\n            evt.attrValue = attrName;\n          }\n          if (settings.allow_svg_data_urls && startsWith(attrValue, 'data:image/svg+xml')) {\n            evt.forceKeepAttr = true;\n          }\n        } else if (ele.hasAttribute(internalElementAttr) && (attrName === 'id' || attrName === 'class' || attrName === 'style')) {\n          evt.forceKeepAttr = true;\n        }\n      });\n      return purify$1;\n    };\n    const transferChildren = (parent, nativeParent, specialElements) => {\n      const parentName = parent.name;\n      const isSpecial = parentName in specialElements && parentName !== 'title' && parentName !== 'textarea';\n      const childNodes = nativeParent.childNodes;\n      for (let ni = 0, nl = childNodes.length; ni < nl; ni++) {\n        const nativeChild = childNodes[ni];\n        const child = new AstNode(nativeChild.nodeName.toLowerCase(), nativeChild.nodeType);\n        if (isElement$6(nativeChild)) {\n          const attributes = nativeChild.attributes;\n          for (let ai = 0, al = attributes.length; ai < al; ai++) {\n            const attr = attributes[ai];\n            child.attr(attr.name, attr.value);\n          }\n        } else if (isText$a(nativeChild)) {\n          child.value = nativeChild.data;\n          if (isSpecial) {\n            child.raw = true;\n          }\n        } else if (isComment(nativeChild) || isCData(nativeChild) || isPi(nativeChild)) {\n          child.value = nativeChild.data;\n        }\n        transferChildren(child, nativeChild, specialElements);\n        parent.append(child);\n      }\n    };\n    const walkTree = (root, preprocessors, postprocessors) => {\n      const traverseOrder = [];\n      for (let node = root, lastNode = node; node; lastNode = node, node = node.walk()) {\n        const tempNode = node;\n        each$e(preprocessors, preprocess => preprocess(tempNode));\n        if (isNullable(tempNode.parent) && tempNode !== root) {\n          node = lastNode;\n        } else {\n          traverseOrder.push(tempNode);\n        }\n      }\n      for (let i = traverseOrder.length - 1; i >= 0; i--) {\n        const node = traverseOrder[i];\n        each$e(postprocessors, postprocess => postprocess(node));\n      }\n    };\n    const whitespaceCleaner = (root, schema, settings, args) => {\n      const validate = settings.validate;\n      const nonEmptyElements = schema.getNonEmptyElements();\n      const whitespaceElements = schema.getWhitespaceElements();\n      const blockElements = extend$1(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());\n      const textRootBlockElements = getTextRootBlockElements(schema);\n      const allWhiteSpaceRegExp = /[ \\t\\r\\n]+/g;\n      const startWhiteSpaceRegExp = /^[ \\t\\r\\n]+/;\n      const endWhiteSpaceRegExp = /[ \\t\\r\\n]+$/;\n      const hasWhitespaceParent = node => {\n        let tempNode = node.parent;\n        while (isNonNullable(tempNode)) {\n          if (tempNode.name in whitespaceElements) {\n            return true;\n          } else {\n            tempNode = tempNode.parent;\n          }\n        }\n        return false;\n      };\n      const isTextRootBlockEmpty = node => {\n        let tempNode = node;\n        while (isNonNullable(tempNode)) {\n          if (tempNode.name in textRootBlockElements) {\n            return isEmpty(schema, nonEmptyElements, whitespaceElements, tempNode);\n          } else {\n            tempNode = tempNode.parent;\n          }\n        }\n        return false;\n      };\n      const isBlock = node => node.name in blockElements && !isTransparentAstInline(schema, node);\n      const isAtEdgeOfBlock = (node, start) => {\n        const neighbour = start ? node.prev : node.next;\n        if (isNonNullable(neighbour) || isNullable(node.parent)) {\n          return false;\n        }\n        return isBlock(node.parent) && (node.parent !== root || args.isRootContent === true);\n      };\n      const preprocess = node => {\n        var _a;\n        if (node.type === 3) {\n          if (!hasWhitespaceParent(node)) {\n            let text = (_a = node.value) !== null && _a !== void 0 ? _a : '';\n            text = text.replace(allWhiteSpaceRegExp, ' ');\n            if (isLineBreakNode(node.prev, isBlock) || isAtEdgeOfBlock(node, true)) {\n              text = text.replace(startWhiteSpaceRegExp, '');\n            }\n            if (text.length === 0) {\n              node.remove();\n            } else {\n              node.value = text;\n            }\n          }\n        }\n      };\n      const postprocess = node => {\n        var _a;\n        if (node.type === 1) {\n          const elementRule = schema.getElementRule(node.name);\n          if (validate && elementRule) {\n            const isNodeEmpty = isEmpty(schema, nonEmptyElements, whitespaceElements, node);\n            if (elementRule.paddInEmptyBlock && isNodeEmpty && isTextRootBlockEmpty(node)) {\n              paddEmptyNode(args, isBlock, node);\n            } else if (elementRule.removeEmpty && isNodeEmpty) {\n              if (isBlock(node)) {\n                node.remove();\n              } else {\n                node.unwrap();\n              }\n            } else if (elementRule.paddEmpty && (isNodeEmpty || isPaddedWithNbsp(node))) {\n              paddEmptyNode(args, isBlock, node);\n            }\n          }\n        } else if (node.type === 3) {\n          if (!hasWhitespaceParent(node)) {\n            let text = (_a = node.value) !== null && _a !== void 0 ? _a : '';\n            if (node.next && isBlock(node.next) || isAtEdgeOfBlock(node, false)) {\n              text = text.replace(endWhiteSpaceRegExp, '');\n            }\n            if (text.length === 0) {\n              node.remove();\n            } else {\n              node.value = text;\n            }\n          }\n        }\n      };\n      return [\n        preprocess,\n        postprocess\n      ];\n    };\n    const getRootBlockName = (settings, args) => {\n      var _a;\n      const name = (_a = args.forced_root_block) !== null && _a !== void 0 ? _a : settings.forced_root_block;\n      if (name === false) {\n        return '';\n      } else if (name === true) {\n        return 'p';\n      } else {\n        return name;\n      }\n    };\n    const DomParser = (settings = {}, schema = Schema()) => {\n      const nodeFilterRegistry = create$7();\n      const attributeFilterRegistry = create$7();\n      const defaultedSettings = {\n        validate: true,\n        root_name: 'body',\n        ...settings\n      };\n      const parser = new DOMParser();\n      const purify = setupPurify(defaultedSettings, schema);\n      const parseAndSanitizeWithContext = (html, rootName, format = 'html') => {\n        const mimeType = format === 'xhtml' ? 'application/xhtml+xml' : 'text/html';\n        const isSpecialRoot = has$2(schema.getSpecialElements(), rootName.toLowerCase());\n        const content = isSpecialRoot ? `<${ rootName }>${ html }</${ rootName }>` : html;\n        const wrappedHtml = format === 'xhtml' ? `<html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head><body>${ content }</body></html>` : `<body>${ content }</body>`;\n        const body = parser.parseFromString(wrappedHtml, mimeType).body;\n        purify.sanitize(body, getPurifyConfig(defaultedSettings, mimeType));\n        purify.removed = [];\n        return isSpecialRoot ? body.firstChild : body;\n      };\n      const addNodeFilter = nodeFilterRegistry.addFilter;\n      const getNodeFilters = nodeFilterRegistry.getFilters;\n      const removeNodeFilter = nodeFilterRegistry.removeFilter;\n      const addAttributeFilter = attributeFilterRegistry.addFilter;\n      const getAttributeFilters = attributeFilterRegistry.getFilters;\n      const removeAttributeFilter = attributeFilterRegistry.removeFilter;\n      const findInvalidChildren = (node, invalidChildren) => {\n        if (isInvalid(schema, node)) {\n          invalidChildren.push(node);\n        }\n      };\n      const isWrappableNode = (blockElements, node) => {\n        const isInternalElement = isString(node.attr(internalElementAttr));\n        const isInlineElement = node.type === 1 && (!has$2(blockElements, node.name) && !isTransparentAstBlock(schema, node));\n        return node.type === 3 || isInlineElement && !isInternalElement;\n      };\n      const addRootBlocks = (rootNode, rootBlockName) => {\n        const blockElements = extend$1(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());\n        const startWhiteSpaceRegExp = /^[ \\t\\r\\n]+/;\n        const endWhiteSpaceRegExp = /[ \\t\\r\\n]+$/;\n        let node = rootNode.firstChild, rootBlockNode = null;\n        const trim = rootBlock => {\n          var _a, _b;\n          if (rootBlock) {\n            node = rootBlock.firstChild;\n            if (node && node.type === 3) {\n              node.value = (_a = node.value) === null || _a === void 0 ? void 0 : _a.replace(startWhiteSpaceRegExp, '');\n            }\n            node = rootBlock.lastChild;\n            if (node && node.type === 3) {\n              node.value = (_b = node.value) === null || _b === void 0 ? void 0 : _b.replace(endWhiteSpaceRegExp, '');\n            }\n          }\n        };\n        if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {\n          return;\n        }\n        while (node) {\n          const next = node.next;\n          if (isWrappableNode(blockElements, node)) {\n            if (!rootBlockNode) {\n              rootBlockNode = new AstNode(rootBlockName, 1);\n              rootBlockNode.attr(defaultedSettings.forced_root_block_attrs);\n              rootNode.insert(rootBlockNode, node);\n              rootBlockNode.append(node);\n            } else {\n              rootBlockNode.append(node);\n            }\n          } else {\n            trim(rootBlockNode);\n            rootBlockNode = null;\n          }\n          node = next;\n        }\n        trim(rootBlockNode);\n      };\n      const parse = (html, args = {}) => {\n        var _a;\n        const validate = defaultedSettings.validate;\n        const rootName = (_a = args.context) !== null && _a !== void 0 ? _a : defaultedSettings.root_name;\n        const element = parseAndSanitizeWithContext(html, rootName, args.format);\n        updateChildren(schema, element);\n        const rootNode = new AstNode(rootName, 11);\n        transferChildren(rootNode, element, schema.getSpecialElements());\n        element.innerHTML = '';\n        const [whitespacePre, whitespacePost] = whitespaceCleaner(rootNode, schema, defaultedSettings, args);\n        const invalidChildren = [];\n        const invalidFinder = validate ? node => findInvalidChildren(node, invalidChildren) : noop;\n        const matches = {\n          nodes: {},\n          attributes: {}\n        };\n        const matchFinder = node => matchNode$1(getNodeFilters(), getAttributeFilters(), node, matches);\n        walkTree(rootNode, [\n          whitespacePre,\n          matchFinder\n        ], [\n          whitespacePost,\n          invalidFinder\n        ]);\n        invalidChildren.reverse();\n        if (validate && invalidChildren.length > 0) {\n          if (args.context) {\n            const {\n              pass: topLevelChildren,\n              fail: otherChildren\n            } = partition$2(invalidChildren, child => child.parent === rootNode);\n            cleanInvalidNodes(otherChildren, schema, matchFinder);\n            args.invalid = topLevelChildren.length > 0;\n          } else {\n            cleanInvalidNodes(invalidChildren, schema, matchFinder);\n          }\n        }\n        const rootBlockName = getRootBlockName(defaultedSettings, args);\n        if (rootBlockName && (rootNode.name === 'body' || args.isRootContent)) {\n          addRootBlocks(rootNode, rootBlockName);\n        }\n        if (!args.invalid) {\n          runFilters(matches, args);\n        }\n        return rootNode;\n      };\n      const exports = {\n        schema,\n        addAttributeFilter,\n        getAttributeFilters,\n        removeAttributeFilter,\n        addNodeFilter,\n        getNodeFilters,\n        removeNodeFilter,\n        parse\n      };\n      register$4(exports, defaultedSettings);\n      register$5(exports, defaultedSettings, schema);\n      return exports;\n    };\n\n    const serializeContent = content => isTreeNode(content) ? HtmlSerializer({ validate: false }).serialize(content) : content;\n    const withSerializedContent = (content, fireEvent) => {\n      const serializedContent = serializeContent(content);\n      const eventArgs = fireEvent(serializedContent);\n      if (eventArgs.isDefaultPrevented()) {\n        return eventArgs;\n      } else if (isTreeNode(content)) {\n        if (eventArgs.content !== serializedContent) {\n          const rootNode = DomParser({\n            validate: false,\n            forced_root_block: false\n          }).parse(eventArgs.content, { context: content.name });\n          return {\n            ...eventArgs,\n            content: rootNode\n          };\n        } else {\n          return {\n            ...eventArgs,\n            content\n          };\n        }\n      } else {\n        return eventArgs;\n      }\n    };\n    const preProcessGetContent = (editor, args) => {\n      if (args.no_events) {\n        return Result.value(args);\n      } else {\n        const eventArgs = fireBeforeGetContent(editor, args);\n        if (eventArgs.isDefaultPrevented()) {\n          return Result.error(fireGetContent(editor, {\n            content: '',\n            ...eventArgs\n          }).content);\n        } else {\n          return Result.value(eventArgs);\n        }\n      }\n    };\n    const postProcessGetContent = (editor, content, args) => {\n      if (args.no_events) {\n        return content;\n      } else {\n        const processedEventArgs = withSerializedContent(content, c => fireGetContent(editor, {\n          ...args,\n          content: c\n        }));\n        return processedEventArgs.content;\n      }\n    };\n    const preProcessSetContent = (editor, args) => {\n      if (args.no_events) {\n        return Result.value(args);\n      } else {\n        const processedEventArgs = withSerializedContent(args.content, content => fireBeforeSetContent(editor, {\n          ...args,\n          content\n        }));\n        if (processedEventArgs.isDefaultPrevented()) {\n          fireSetContent(editor, processedEventArgs);\n          return Result.error(undefined);\n        } else {\n          return Result.value(processedEventArgs);\n        }\n      }\n    };\n    const postProcessSetContent = (editor, content, args) => {\n      if (!args.no_events) {\n        fireSetContent(editor, {\n          ...args,\n          content\n        });\n      }\n    };\n\n    const tableModel = (element, width, rows) => ({\n      element,\n      width,\n      rows\n    });\n    const tableRow = (element, cells) => ({\n      element,\n      cells\n    });\n    const cellPosition = (x, y) => ({\n      x,\n      y\n    });\n    const getSpan = (td, key) => {\n      return getOpt(td, key).bind(toInt).getOr(1);\n    };\n    const fillout = (table, x, y, tr, td) => {\n      const rowspan = getSpan(td, 'rowspan');\n      const colspan = getSpan(td, 'colspan');\n      const rows = table.rows;\n      for (let y2 = y; y2 < y + rowspan; y2++) {\n        if (!rows[y2]) {\n          rows[y2] = tableRow(deep$1(tr), []);\n        }\n        for (let x2 = x; x2 < x + colspan; x2++) {\n          const cells = rows[y2].cells;\n          cells[x2] = y2 === y && x2 === x ? td : shallow$1(td);\n        }\n      }\n    };\n    const cellExists = (table, x, y) => {\n      const rows = table.rows;\n      const cells = rows[y] ? rows[y].cells : [];\n      return !!cells[x];\n    };\n    const skipCellsX = (table, x, y) => {\n      while (cellExists(table, x, y)) {\n        x++;\n      }\n      return x;\n    };\n    const getWidth = rows => {\n      return foldl(rows, (acc, row) => {\n        return row.cells.length > acc ? row.cells.length : acc;\n      }, 0);\n    };\n    const findElementPos = (table, element) => {\n      const rows = table.rows;\n      for (let y = 0; y < rows.length; y++) {\n        const cells = rows[y].cells;\n        for (let x = 0; x < cells.length; x++) {\n          if (eq(cells[x], element)) {\n            return Optional.some(cellPosition(x, y));\n          }\n        }\n      }\n      return Optional.none();\n    };\n    const extractRows = (table, sx, sy, ex, ey) => {\n      const newRows = [];\n      const rows = table.rows;\n      for (let y = sy; y <= ey; y++) {\n        const cells = rows[y].cells;\n        const slice = sx < ex ? cells.slice(sx, ex + 1) : cells.slice(ex, sx + 1);\n        newRows.push(tableRow(rows[y].element, slice));\n      }\n      return newRows;\n    };\n    const subTable = (table, startPos, endPos) => {\n      const sx = startPos.x, sy = startPos.y;\n      const ex = endPos.x, ey = endPos.y;\n      const newRows = sy < ey ? extractRows(table, sx, sy, ex, ey) : extractRows(table, sx, ey, ex, sy);\n      return tableModel(table.element, getWidth(newRows), newRows);\n    };\n    const createDomTable = (table, rows) => {\n      const tableElement = shallow$1(table.element);\n      const tableBody = SugarElement.fromTag('tbody');\n      append(tableBody, rows);\n      append$1(tableElement, tableBody);\n      return tableElement;\n    };\n    const modelRowsToDomRows = table => {\n      return map$3(table.rows, row => {\n        const cells = map$3(row.cells, cell => {\n          const td = deep$1(cell);\n          remove$b(td, 'colspan');\n          remove$b(td, 'rowspan');\n          return td;\n        });\n        const tr = shallow$1(row.element);\n        append(tr, cells);\n        return tr;\n      });\n    };\n    const fromDom = tableElm => {\n      const table = tableModel(shallow$1(tableElm), 0, []);\n      each$e(descendants(tableElm, 'tr'), (tr, y) => {\n        each$e(descendants(tr, 'td,th'), (td, x) => {\n          fillout(table, skipCellsX(table, x, y), y, tr, td);\n        });\n      });\n      return tableModel(table.element, getWidth(table.rows), table.rows);\n    };\n    const toDom = table => {\n      return createDomTable(table, modelRowsToDomRows(table));\n    };\n    const subsection = (table, startElement, endElement) => {\n      return findElementPos(table, startElement).bind(startPos => {\n        return findElementPos(table, endElement).map(endPos => {\n          return subTable(table, startPos, endPos);\n        });\n      });\n    };\n\n    const findParentListContainer = parents => find$2(parents, elm => name(elm) === 'ul' || name(elm) === 'ol');\n    const getFullySelectedListWrappers = (parents, rng) => find$2(parents, elm => name(elm) === 'li' && hasAllContentsSelected(elm, rng)).fold(constant([]), _li => findParentListContainer(parents).map(listCont => {\n      const listElm = SugarElement.fromTag(name(listCont));\n      const listStyles = filter$4(getAllRaw(listCont), (_style, name) => startsWith(name, 'list-style'));\n      setAll(listElm, listStyles);\n      return [\n        SugarElement.fromTag('li'),\n        listElm\n      ];\n    }).getOr([]));\n    const wrap = (innerElm, elms) => {\n      const wrapped = foldl(elms, (acc, elm) => {\n        append$1(elm, acc);\n        return elm;\n      }, innerElm);\n      return elms.length > 0 ? fromElements([wrapped]) : wrapped;\n    };\n    const directListWrappers = commonAnchorContainer => {\n      if (isListItem$1(commonAnchorContainer)) {\n        return parent(commonAnchorContainer).filter(isList).fold(constant([]), listElm => [\n          commonAnchorContainer,\n          listElm\n        ]);\n      } else {\n        return isList(commonAnchorContainer) ? [commonAnchorContainer] : [];\n      }\n    };\n    const getWrapElements = (rootNode, rng) => {\n      const commonAnchorContainer = SugarElement.fromDom(rng.commonAncestorContainer);\n      const parents = parentsAndSelf(commonAnchorContainer, rootNode);\n      const wrapElements = filter$5(parents, isWrapElement);\n      const listWrappers = getFullySelectedListWrappers(parents, rng);\n      const allWrappers = wrapElements.concat(listWrappers.length ? listWrappers : directListWrappers(commonAnchorContainer));\n      return map$3(allWrappers, shallow$1);\n    };\n    const emptyFragment = () => fromElements([]);\n    const getFragmentFromRange = (rootNode, rng) => wrap(SugarElement.fromDom(rng.cloneContents()), getWrapElements(rootNode, rng));\n    const getParentTable = (rootElm, cell) => ancestor$2(cell, 'table', curry(eq, rootElm));\n    const getTableFragment = (rootNode, selectedTableCells) => getParentTable(rootNode, selectedTableCells[0]).bind(tableElm => {\n      const firstCell = selectedTableCells[0];\n      const lastCell = selectedTableCells[selectedTableCells.length - 1];\n      const fullTableModel = fromDom(tableElm);\n      return subsection(fullTableModel, firstCell, lastCell).map(sectionedTableModel => fromElements([toDom(sectionedTableModel)]));\n    }).getOrThunk(emptyFragment);\n    const getSelectionFragment = (rootNode, ranges) => ranges.length > 0 && ranges[0].collapsed ? emptyFragment() : getFragmentFromRange(rootNode, ranges[0]);\n    const read$3 = (rootNode, ranges) => {\n      const selectedCells = getCellsFromElementOrRanges(ranges, rootNode);\n      return selectedCells.length > 0 ? getTableFragment(rootNode, selectedCells) : getSelectionFragment(rootNode, ranges);\n    };\n\n    const isCollapsibleWhitespace = (text, index) => index >= 0 && index < text.length && isWhiteSpace(text.charAt(index));\n    const getInnerText = bin => {\n      return trim$1(bin.innerText);\n    };\n    const getContextNodeName = parentBlockOpt => parentBlockOpt.map(block => block.nodeName).getOr('div').toLowerCase();\n    const getTextContent = editor => Optional.from(editor.selection.getRng()).map(rng => {\n      var _a;\n      const parentBlockOpt = Optional.from(editor.dom.getParent(rng.commonAncestorContainer, editor.dom.isBlock));\n      const body = editor.getBody();\n      const contextNodeName = getContextNodeName(parentBlockOpt);\n      const rangeContentClone = SugarElement.fromDom(rng.cloneContents());\n      cleanupBogusElements(rangeContentClone);\n      cleanupInputNames(rangeContentClone);\n      const bin = editor.dom.add(body, contextNodeName, {\n        'data-mce-bogus': 'all',\n        'style': 'overflow: hidden; opacity: 0;'\n      }, rangeContentClone.dom);\n      const text = getInnerText(bin);\n      const nonRenderedText = trim$1((_a = bin.textContent) !== null && _a !== void 0 ? _a : '');\n      editor.dom.remove(bin);\n      if (isCollapsibleWhitespace(nonRenderedText, 0) || isCollapsibleWhitespace(nonRenderedText, nonRenderedText.length - 1)) {\n        const parentBlock = parentBlockOpt.getOr(body);\n        const parentBlockText = getInnerText(parentBlock);\n        const textIndex = parentBlockText.indexOf(text);\n        if (textIndex === -1) {\n          return text;\n        } else {\n          const hasProceedingSpace = isCollapsibleWhitespace(parentBlockText, textIndex - 1);\n          const hasTrailingSpace = isCollapsibleWhitespace(parentBlockText, textIndex + text.length);\n          return (hasProceedingSpace ? ' ' : '') + text + (hasTrailingSpace ? ' ' : '');\n        }\n      } else {\n        return text;\n      }\n    }).getOr('');\n    const getSerializedContent = (editor, args) => {\n      const rng = editor.selection.getRng(), tmpElm = editor.dom.create('body');\n      const sel = editor.selection.getSel();\n      const ranges = processRanges(editor, getRanges$1(sel));\n      const fragment = args.contextual ? read$3(SugarElement.fromDom(editor.getBody()), ranges).dom : rng.cloneContents();\n      if (fragment) {\n        tmpElm.appendChild(fragment);\n      }\n      return editor.selection.serializer.serialize(tmpElm, args);\n    };\n    const extractSelectedContent = (editor, args) => {\n      if (args.format === 'text') {\n        return getTextContent(editor);\n      } else {\n        const content = getSerializedContent(editor, args);\n        if (args.format === 'tree') {\n          return content;\n        } else {\n          return editor.selection.isCollapsed() ? '' : content;\n        }\n      }\n    };\n    const setupArgs$3 = (args, format) => ({\n      ...args,\n      format,\n      get: true,\n      selection: true,\n      getInner: true\n    });\n    const getSelectedContentInternal = (editor, format, args = {}) => {\n      const defaultedArgs = setupArgs$3(args, format);\n      return preProcessGetContent(editor, defaultedArgs).fold(identity, updatedArgs => {\n        const content = extractSelectedContent(editor, updatedArgs);\n        return postProcessGetContent(editor, content, updatedArgs);\n      });\n    };\n\n    const KEEP = 0, INSERT = 1, DELETE = 2;\n    const diff = (left, right) => {\n      const size = left.length + right.length + 2;\n      const vDown = new Array(size);\n      const vUp = new Array(size);\n      const snake = (start, end, diag) => {\n        return {\n          start,\n          end,\n          diag\n        };\n      };\n      const buildScript = (start1, end1, start2, end2, script) => {\n        const middle = getMiddleSnake(start1, end1, start2, end2);\n        if (middle === null || middle.start === end1 && middle.diag === end1 - end2 || middle.end === start1 && middle.diag === start1 - start2) {\n          let i = start1;\n          let j = start2;\n          while (i < end1 || j < end2) {\n            if (i < end1 && j < end2 && left[i] === right[j]) {\n              script.push([\n                KEEP,\n                left[i]\n              ]);\n              ++i;\n              ++j;\n            } else {\n              if (end1 - start1 > end2 - start2) {\n                script.push([\n                  DELETE,\n                  left[i]\n                ]);\n                ++i;\n              } else {\n                script.push([\n                  INSERT,\n                  right[j]\n                ]);\n                ++j;\n              }\n            }\n          }\n        } else {\n          buildScript(start1, middle.start, start2, middle.start - middle.diag, script);\n          for (let i2 = middle.start; i2 < middle.end; ++i2) {\n            script.push([\n              KEEP,\n              left[i2]\n            ]);\n          }\n          buildScript(middle.end, end1, middle.end - middle.diag, end2, script);\n        }\n      };\n      const buildSnake = (start, diag, end1, end2) => {\n        let end = start;\n        while (end - diag < end2 && end < end1 && left[end] === right[end - diag]) {\n          ++end;\n        }\n        return snake(start, end, diag);\n      };\n      const getMiddleSnake = (start1, end1, start2, end2) => {\n        const m = end1 - start1;\n        const n = end2 - start2;\n        if (m === 0 || n === 0) {\n          return null;\n        }\n        const delta = m - n;\n        const sum = n + m;\n        const offset = (sum % 2 === 0 ? sum : sum + 1) / 2;\n        vDown[1 + offset] = start1;\n        vUp[1 + offset] = end1 + 1;\n        let d, k, i, x, y;\n        for (d = 0; d <= offset; ++d) {\n          for (k = -d; k <= d; k += 2) {\n            i = k + offset;\n            if (k === -d || k !== d && vDown[i - 1] < vDown[i + 1]) {\n              vDown[i] = vDown[i + 1];\n            } else {\n              vDown[i] = vDown[i - 1] + 1;\n            }\n            x = vDown[i];\n            y = x - start1 + start2 - k;\n            while (x < end1 && y < end2 && left[x] === right[y]) {\n              vDown[i] = ++x;\n              ++y;\n            }\n            if (delta % 2 !== 0 && delta - d <= k && k <= delta + d) {\n              if (vUp[i - delta] <= vDown[i]) {\n                return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2);\n              }\n            }\n          }\n          for (k = delta - d; k <= delta + d; k += 2) {\n            i = k + offset - delta;\n            if (k === delta - d || k !== delta + d && vUp[i + 1] <= vUp[i - 1]) {\n              vUp[i] = vUp[i + 1] - 1;\n            } else {\n              vUp[i] = vUp[i - 1];\n            }\n            x = vUp[i] - 1;\n            y = x - start1 + start2 - k;\n            while (x >= start1 && y >= start2 && left[x] === right[y]) {\n              vUp[i] = x--;\n              y--;\n            }\n            if (delta % 2 === 0 && -d <= k && k <= d) {\n              if (vUp[i] <= vDown[i + delta]) {\n                return buildSnake(vUp[i], k + start1 - start2, end1, end2);\n              }\n            }\n          }\n        }\n        return null;\n      };\n      const script = [];\n      buildScript(0, left.length, 0, right.length, script);\n      return script;\n    };\n\n    const getOuterHtml = elm => {\n      if (isElement$6(elm)) {\n        return elm.outerHTML;\n      } else if (isText$a(elm)) {\n        return Entities.encodeRaw(elm.data, false);\n      } else if (isComment(elm)) {\n        return '<!--' + elm.data + '-->';\n      }\n      return '';\n    };\n    const createFragment = html => {\n      let node;\n      const container = document.createElement('div');\n      const frag = document.createDocumentFragment();\n      if (html) {\n        container.innerHTML = html;\n      }\n      while (node = container.firstChild) {\n        frag.appendChild(node);\n      }\n      return frag;\n    };\n    const insertAt = (elm, html, index) => {\n      const fragment = createFragment(html);\n      if (elm.hasChildNodes() && index < elm.childNodes.length) {\n        const target = elm.childNodes[index];\n        elm.insertBefore(fragment, target);\n      } else {\n        elm.appendChild(fragment);\n      }\n    };\n    const removeAt = (elm, index) => {\n      if (elm.hasChildNodes() && index < elm.childNodes.length) {\n        const target = elm.childNodes[index];\n        elm.removeChild(target);\n      }\n    };\n    const applyDiff = (diff, elm) => {\n      let index = 0;\n      each$e(diff, action => {\n        if (action[0] === KEEP) {\n          index++;\n        } else if (action[0] === INSERT) {\n          insertAt(elm, action[1], index);\n          index++;\n        } else if (action[0] === DELETE) {\n          removeAt(elm, index);\n        }\n      });\n    };\n    const read$2 = elm => {\n      return filter$5(map$3(from(elm.childNodes), getOuterHtml), item => {\n        return item.length > 0;\n      });\n    };\n    const write = (fragments, elm) => {\n      const currentFragments = map$3(from(elm.childNodes), getOuterHtml);\n      applyDiff(diff(currentFragments, fragments), elm);\n      return elm;\n    };\n\n    const lazyTempDocument = cached(() => document.implementation.createHTMLDocument('undo'));\n    const hasIframes = html => {\n      return html.indexOf('</iframe>') !== -1;\n    };\n    const createFragmentedLevel = fragments => {\n      return {\n        type: 'fragmented',\n        fragments,\n        content: '',\n        bookmark: null,\n        beforeBookmark: null\n      };\n    };\n    const createCompleteLevel = content => {\n      return {\n        type: 'complete',\n        fragments: null,\n        content,\n        bookmark: null,\n        beforeBookmark: null\n      };\n    };\n    const createFromEditor = editor => {\n      const fragments = read$2(editor.getBody());\n      const trimmedFragments = bind$3(fragments, html => {\n        const trimmed = trimInternal(editor.serializer, html);\n        return trimmed.length > 0 ? [trimmed] : [];\n      });\n      const content = trimmedFragments.join('');\n      return hasIframes(content) ? createFragmentedLevel(trimmedFragments) : createCompleteLevel(content);\n    };\n    const applyToEditor = (editor, level, before) => {\n      const bookmark = before ? level.beforeBookmark : level.bookmark;\n      if (level.type === 'fragmented') {\n        write(level.fragments, editor.getBody());\n      } else {\n        editor.setContent(level.content, {\n          format: 'raw',\n          no_selection: isNonNullable(bookmark) && isPathBookmark(bookmark) ? !bookmark.isFakeCaret : true\n        });\n      }\n      if (bookmark) {\n        editor.selection.moveToBookmark(bookmark);\n        editor.selection.scrollIntoView();\n      }\n    };\n    const getLevelContent = level => {\n      return level.type === 'fragmented' ? level.fragments.join('') : level.content;\n    };\n    const getCleanLevelContent = level => {\n      const elm = SugarElement.fromTag('body', lazyTempDocument());\n      set(elm, getLevelContent(level));\n      each$e(descendants(elm, '*[data-mce-bogus]'), unwrap);\n      return get$6(elm);\n    };\n    const hasEqualContent = (level1, level2) => getLevelContent(level1) === getLevelContent(level2);\n    const hasEqualCleanedContent = (level1, level2) => getCleanLevelContent(level1) === getCleanLevelContent(level2);\n    const isEq$1 = (level1, level2) => {\n      if (!level1 || !level2) {\n        return false;\n      } else if (hasEqualContent(level1, level2)) {\n        return true;\n      } else {\n        return hasEqualCleanedContent(level1, level2);\n      }\n    };\n\n    const isUnlocked = locks => locks.get() === 0;\n\n    const setTyping = (undoManager, typing, locks) => {\n      if (isUnlocked(locks)) {\n        undoManager.typing = typing;\n      }\n    };\n    const endTyping = (undoManager, locks) => {\n      if (undoManager.typing) {\n        setTyping(undoManager, false, locks);\n        undoManager.add();\n      }\n    };\n    const endTypingLevelIgnoreLocks = undoManager => {\n      if (undoManager.typing) {\n        undoManager.typing = false;\n        undoManager.add();\n      }\n    };\n\n    const beforeChange$1 = (editor, locks, beforeBookmark) => {\n      if (isUnlocked(locks)) {\n        beforeBookmark.set(getUndoBookmark(editor.selection));\n      }\n    };\n    const addUndoLevel$1 = (editor, undoManager, index, locks, beforeBookmark, level, event) => {\n      const currentLevel = createFromEditor(editor);\n      const newLevel = Tools.extend(level || {}, currentLevel);\n      if (!isUnlocked(locks) || editor.removed) {\n        return null;\n      }\n      const lastLevel = undoManager.data[index.get()];\n      if (editor.dispatch('BeforeAddUndo', {\n          level: newLevel,\n          lastLevel,\n          originalEvent: event\n        }).isDefaultPrevented()) {\n        return null;\n      }\n      if (lastLevel && isEq$1(lastLevel, newLevel)) {\n        return null;\n      }\n      if (undoManager.data[index.get()]) {\n        beforeBookmark.get().each(bm => {\n          undoManager.data[index.get()].beforeBookmark = bm;\n        });\n      }\n      const customUndoRedoLevels = getCustomUndoRedoLevels(editor);\n      if (customUndoRedoLevels) {\n        if (undoManager.data.length > customUndoRedoLevels) {\n          for (let i = 0; i < undoManager.data.length - 1; i++) {\n            undoManager.data[i] = undoManager.data[i + 1];\n          }\n          undoManager.data.length--;\n          index.set(undoManager.data.length);\n        }\n      }\n      newLevel.bookmark = getUndoBookmark(editor.selection);\n      if (index.get() < undoManager.data.length - 1) {\n        undoManager.data.length = index.get() + 1;\n      }\n      undoManager.data.push(newLevel);\n      index.set(undoManager.data.length - 1);\n      const args = {\n        level: newLevel,\n        lastLevel,\n        originalEvent: event\n      };\n      if (index.get() > 0) {\n        editor.setDirty(true);\n        editor.dispatch('AddUndo', args);\n        editor.dispatch('change', args);\n      } else {\n        editor.dispatch('AddUndo', args);\n      }\n      return newLevel;\n    };\n    const clear$1 = (editor, undoManager, index) => {\n      undoManager.data = [];\n      index.set(0);\n      undoManager.typing = false;\n      editor.dispatch('ClearUndos');\n    };\n    const extra$1 = (editor, undoManager, index, callback1, callback2) => {\n      if (undoManager.transact(callback1)) {\n        const bookmark = undoManager.data[index.get()].bookmark;\n        const lastLevel = undoManager.data[index.get() - 1];\n        applyToEditor(editor, lastLevel, true);\n        if (undoManager.transact(callback2)) {\n          undoManager.data[index.get() - 1].beforeBookmark = bookmark;\n        }\n      }\n    };\n    const redo$1 = (editor, index, data) => {\n      let level;\n      if (index.get() < data.length - 1) {\n        index.set(index.get() + 1);\n        level = data[index.get()];\n        applyToEditor(editor, level, false);\n        editor.setDirty(true);\n        editor.dispatch('Redo', { level });\n      }\n      return level;\n    };\n    const undo$1 = (editor, undoManager, locks, index) => {\n      let level;\n      if (undoManager.typing) {\n        undoManager.add();\n        undoManager.typing = false;\n        setTyping(undoManager, false, locks);\n      }\n      if (index.get() > 0) {\n        index.set(index.get() - 1);\n        level = undoManager.data[index.get()];\n        applyToEditor(editor, level, true);\n        editor.setDirty(true);\n        editor.dispatch('Undo', { level });\n      }\n      return level;\n    };\n    const reset$1 = undoManager => {\n      undoManager.clear();\n      undoManager.add();\n    };\n    const hasUndo$1 = (editor, undoManager, index) => index.get() > 0 || undoManager.typing && undoManager.data[0] && !isEq$1(createFromEditor(editor), undoManager.data[0]);\n    const hasRedo$1 = (undoManager, index) => index.get() < undoManager.data.length - 1 && !undoManager.typing;\n    const transact$1 = (undoManager, locks, callback) => {\n      endTyping(undoManager, locks);\n      undoManager.beforeChange();\n      undoManager.ignore(callback);\n      return undoManager.add();\n    };\n    const ignore$1 = (locks, callback) => {\n      try {\n        locks.set(locks.get() + 1);\n        callback();\n      } finally {\n        locks.set(locks.get() - 1);\n      }\n    };\n\n    const addVisualInternal = (editor, elm) => {\n      const dom = editor.dom;\n      const scope = isNonNullable(elm) ? elm : editor.getBody();\n      each$e(dom.select('table,a', scope), matchedElm => {\n        switch (matchedElm.nodeName) {\n        case 'TABLE':\n          const cls = getVisualAidsTableClass(editor);\n          const value = dom.getAttrib(matchedElm, 'border');\n          if ((!value || value === '0') && editor.hasVisual) {\n            dom.addClass(matchedElm, cls);\n          } else {\n            dom.removeClass(matchedElm, cls);\n          }\n          break;\n        case 'A':\n          if (!dom.getAttrib(matchedElm, 'href')) {\n            const value = dom.getAttrib(matchedElm, 'name') || matchedElm.id;\n            const cls = getVisualAidsAnchorClass(editor);\n            if (value && editor.hasVisual) {\n              dom.addClass(matchedElm, cls);\n            } else {\n              dom.removeClass(matchedElm, cls);\n            }\n          }\n          break;\n        }\n      });\n      editor.dispatch('VisualAid', {\n        element: elm,\n        hasVisual: editor.hasVisual\n      });\n    };\n\n    const makePlainAdaptor = editor => ({\n      init: { bindEvents: noop },\n      undoManager: {\n        beforeChange: (locks, beforeBookmark) => beforeChange$1(editor, locks, beforeBookmark),\n        add: (undoManager, index, locks, beforeBookmark, level, event) => addUndoLevel$1(editor, undoManager, index, locks, beforeBookmark, level, event),\n        undo: (undoManager, locks, index) => undo$1(editor, undoManager, locks, index),\n        redo: (index, data) => redo$1(editor, index, data),\n        clear: (undoManager, index) => clear$1(editor, undoManager, index),\n        reset: undoManager => reset$1(undoManager),\n        hasUndo: (undoManager, index) => hasUndo$1(editor, undoManager, index),\n        hasRedo: (undoManager, index) => hasRedo$1(undoManager, index),\n        transact: (undoManager, locks, callback) => transact$1(undoManager, locks, callback),\n        ignore: (locks, callback) => ignore$1(locks, callback),\n        extra: (undoManager, index, callback1, callback2) => extra$1(editor, undoManager, index, callback1, callback2)\n      },\n      formatter: {\n        match: (name, vars, node, similar) => match$2(editor, name, vars, node, similar),\n        matchAll: (names, vars) => matchAll(editor, names, vars),\n        matchNode: (node, name, vars, similar) => matchNode(editor, node, name, vars, similar),\n        canApply: name => canApply(editor, name),\n        closest: names => closest$1(editor, names),\n        apply: (name, vars, node) => applyFormat$1(editor, name, vars, node),\n        remove: (name, vars, node, similar) => remove$2(editor, name, vars, node, similar),\n        toggle: (name, vars, node) => toggle(editor, name, vars, node),\n        formatChanged: (registeredFormatListeners, formats, callback, similar, vars) => formatChangedInternal(editor, registeredFormatListeners, formats, callback, similar, vars)\n      },\n      editor: {\n        getContent: args => getContentInternal(editor, args),\n        setContent: (content, args) => setContentInternal(editor, content, args),\n        insertContent: (value, details) => insertHtmlAtCaret(editor, value, details),\n        addVisual: elm => addVisualInternal(editor, elm)\n      },\n      selection: { getContent: (format, args) => getSelectedContentInternal(editor, format, args) },\n      autocompleter: {\n        addDecoration: range => create$9(editor, range),\n        removeDecoration: () => remove$3(editor, SugarElement.fromDom(editor.getBody()))\n      },\n      raw: { getModel: () => Optional.none() }\n    });\n    const makeRtcAdaptor = rtcEditor => {\n      const defaultVars = vars => isObject(vars) ? vars : {};\n      const {init, undoManager, formatter, editor, selection, autocompleter, raw} = rtcEditor;\n      return {\n        init: { bindEvents: init.bindEvents },\n        undoManager: {\n          beforeChange: undoManager.beforeChange,\n          add: undoManager.add,\n          undo: undoManager.undo,\n          redo: undoManager.redo,\n          clear: undoManager.clear,\n          reset: undoManager.reset,\n          hasUndo: undoManager.hasUndo,\n          hasRedo: undoManager.hasRedo,\n          transact: (_undoManager, _locks, fn) => undoManager.transact(fn),\n          ignore: (_locks, callback) => undoManager.ignore(callback),\n          extra: (_undoManager, _index, callback1, callback2) => undoManager.extra(callback1, callback2)\n        },\n        formatter: {\n          match: (name, vars, _node, similar) => formatter.match(name, defaultVars(vars), similar),\n          matchAll: formatter.matchAll,\n          matchNode: formatter.matchNode,\n          canApply: name => formatter.canApply(name),\n          closest: names => formatter.closest(names),\n          apply: (name, vars, _node) => formatter.apply(name, defaultVars(vars)),\n          remove: (name, vars, _node, _similar) => formatter.remove(name, defaultVars(vars)),\n          toggle: (name, vars, _node) => formatter.toggle(name, defaultVars(vars)),\n          formatChanged: (_rfl, formats, callback, similar, vars) => formatter.formatChanged(formats, callback, similar, vars)\n        },\n        editor: {\n          getContent: args => editor.getContent(args),\n          setContent: (content, args) => {\n            return {\n              content: editor.setContent(content, args),\n              html: ''\n            };\n          },\n          insertContent: (content, _details) => {\n            editor.insertContent(content);\n            return '';\n          },\n          addVisual: editor.addVisual\n        },\n        selection: { getContent: (_format, args) => selection.getContent(args) },\n        autocompleter: {\n          addDecoration: autocompleter.addDecoration,\n          removeDecoration: autocompleter.removeDecoration\n        },\n        raw: { getModel: () => Optional.some(raw.getRawModel()) }\n      };\n    };\n    const makeNoopAdaptor = () => {\n      const nul = constant(null);\n      const empty = constant('');\n      return {\n        init: { bindEvents: noop },\n        undoManager: {\n          beforeChange: noop,\n          add: nul,\n          undo: nul,\n          redo: nul,\n          clear: noop,\n          reset: noop,\n          hasUndo: never,\n          hasRedo: never,\n          transact: nul,\n          ignore: noop,\n          extra: noop\n        },\n        formatter: {\n          match: never,\n          matchAll: constant([]),\n          matchNode: constant(undefined),\n          canApply: never,\n          closest: empty,\n          apply: noop,\n          remove: noop,\n          toggle: noop,\n          formatChanged: constant({ unbind: noop })\n        },\n        editor: {\n          getContent: empty,\n          setContent: constant({\n            content: '',\n            html: ''\n          }),\n          insertContent: constant(''),\n          addVisual: noop\n        },\n        selection: { getContent: empty },\n        autocompleter: {\n          addDecoration: noop,\n          removeDecoration: noop\n        },\n        raw: { getModel: constant(Optional.none()) }\n      };\n    };\n    const isRtc = editor => has$2(editor.plugins, 'rtc');\n    const getRtcSetup = editor => get$a(editor.plugins, 'rtc').bind(rtcPlugin => Optional.from(rtcPlugin.setup));\n    const setup$s = editor => {\n      const editorCast = editor;\n      return getRtcSetup(editor).fold(() => {\n        editorCast.rtcInstance = makePlainAdaptor(editor);\n        return Optional.none();\n      }, setup => {\n        editorCast.rtcInstance = makeNoopAdaptor();\n        return Optional.some(() => setup().then(rtcEditor => {\n          editorCast.rtcInstance = makeRtcAdaptor(rtcEditor);\n          return rtcEditor.rtc.isRemote;\n        }));\n      });\n    };\n    const getRtcInstanceWithFallback = editor => editor.rtcInstance ? editor.rtcInstance : makePlainAdaptor(editor);\n    const getRtcInstanceWithError = editor => {\n      const rtcInstance = editor.rtcInstance;\n      if (!rtcInstance) {\n        throw new Error('Failed to get RTC instance not yet initialized.');\n      } else {\n        return rtcInstance;\n      }\n    };\n    const beforeChange = (editor, locks, beforeBookmark) => {\n      getRtcInstanceWithError(editor).undoManager.beforeChange(locks, beforeBookmark);\n    };\n    const addUndoLevel = (editor, undoManager, index, locks, beforeBookmark, level, event) => getRtcInstanceWithError(editor).undoManager.add(undoManager, index, locks, beforeBookmark, level, event);\n    const undo = (editor, undoManager, locks, index) => getRtcInstanceWithError(editor).undoManager.undo(undoManager, locks, index);\n    const redo = (editor, index, data) => getRtcInstanceWithError(editor).undoManager.redo(index, data);\n    const clear = (editor, undoManager, index) => {\n      getRtcInstanceWithError(editor).undoManager.clear(undoManager, index);\n    };\n    const reset = (editor, undoManager) => {\n      getRtcInstanceWithError(editor).undoManager.reset(undoManager);\n    };\n    const hasUndo = (editor, undoManager, index) => getRtcInstanceWithError(editor).undoManager.hasUndo(undoManager, index);\n    const hasRedo = (editor, undoManager, index) => getRtcInstanceWithError(editor).undoManager.hasRedo(undoManager, index);\n    const transact = (editor, undoManager, locks, callback) => getRtcInstanceWithError(editor).undoManager.transact(undoManager, locks, callback);\n    const ignore = (editor, locks, callback) => {\n      getRtcInstanceWithError(editor).undoManager.ignore(locks, callback);\n    };\n    const extra = (editor, undoManager, index, callback1, callback2) => {\n      getRtcInstanceWithError(editor).undoManager.extra(undoManager, index, callback1, callback2);\n    };\n    const matchFormat = (editor, name, vars, node, similar) => getRtcInstanceWithError(editor).formatter.match(name, vars, node, similar);\n    const matchAllFormats = (editor, names, vars) => getRtcInstanceWithError(editor).formatter.matchAll(names, vars);\n    const matchNodeFormat = (editor, node, name, vars, similar) => getRtcInstanceWithError(editor).formatter.matchNode(node, name, vars, similar);\n    const canApplyFormat = (editor, name) => getRtcInstanceWithError(editor).formatter.canApply(name);\n    const closestFormat = (editor, names) => getRtcInstanceWithError(editor).formatter.closest(names);\n    const applyFormat = (editor, name, vars, node) => {\n      getRtcInstanceWithError(editor).formatter.apply(name, vars, node);\n    };\n    const removeFormat = (editor, name, vars, node, similar) => {\n      getRtcInstanceWithError(editor).formatter.remove(name, vars, node, similar);\n    };\n    const toggleFormat = (editor, name, vars, node) => {\n      getRtcInstanceWithError(editor).formatter.toggle(name, vars, node);\n    };\n    const formatChanged = (editor, registeredFormatListeners, formats, callback, similar, vars) => getRtcInstanceWithError(editor).formatter.formatChanged(registeredFormatListeners, formats, callback, similar, vars);\n    const getContent$2 = (editor, args) => getRtcInstanceWithFallback(editor).editor.getContent(args);\n    const setContent$2 = (editor, content, args) => getRtcInstanceWithFallback(editor).editor.setContent(content, args);\n    const insertContent$1 = (editor, value, details) => getRtcInstanceWithFallback(editor).editor.insertContent(value, details);\n    const getSelectedContent = (editor, format, args) => getRtcInstanceWithError(editor).selection.getContent(format, args);\n    const addVisual$1 = (editor, elm) => getRtcInstanceWithError(editor).editor.addVisual(elm);\n    const bindEvents = editor => getRtcInstanceWithError(editor).init.bindEvents();\n    const addAutocompleterDecoration = (editor, range) => getRtcInstanceWithError(editor).autocompleter.addDecoration(range);\n    const removeAutocompleterDecoration = editor => getRtcInstanceWithError(editor).autocompleter.removeDecoration();\n\n    const getContent$1 = (editor, args = {}) => {\n      const format = args.format ? args.format : 'html';\n      return getSelectedContent(editor, format, args);\n    };\n\n    const removeEmpty = text => {\n      if (text.dom.length === 0) {\n        remove$6(text);\n        return Optional.none();\n      } else {\n        return Optional.some(text);\n      }\n    };\n    const walkPastBookmark = (node, start) => node.filter(elm => BookmarkManager.isBookmarkNode(elm.dom)).bind(start ? nextSibling : prevSibling);\n    const merge$1 = (outer, inner, rng, start) => {\n      const outerElm = outer.dom;\n      const innerElm = inner.dom;\n      const oldLength = start ? outerElm.length : innerElm.length;\n      if (start) {\n        mergeTextNodes(outerElm, innerElm, false, !start);\n        rng.setStart(innerElm, oldLength);\n      } else {\n        mergeTextNodes(innerElm, outerElm, false, !start);\n        rng.setEnd(innerElm, oldLength);\n      }\n    };\n    const normalizeTextIfRequired = (inner, start) => {\n      parent(inner).each(root => {\n        const text = inner.dom;\n        if (start && needsToBeNbspLeft(root, CaretPosition(text, 0))) {\n          normalizeWhitespaceAfter(text, 0);\n        } else if (!start && needsToBeNbspRight(root, CaretPosition(text, text.length))) {\n          normalizeWhitespaceBefore(text, text.length);\n        }\n      });\n    };\n    const mergeAndNormalizeText = (outerNode, innerNode, rng, start) => {\n      outerNode.bind(outer => {\n        const normalizer = start ? normalizeWhitespaceBefore : normalizeWhitespaceAfter;\n        normalizer(outer.dom, start ? outer.dom.length : 0);\n        return innerNode.filter(isText$b).map(inner => merge$1(outer, inner, rng, start));\n      }).orThunk(() => {\n        const innerTextNode = walkPastBookmark(innerNode, start).or(innerNode).filter(isText$b);\n        return innerTextNode.map(inner => normalizeTextIfRequired(inner, start));\n      });\n    };\n    const rngSetContent = (rng, fragment) => {\n      const firstChild = Optional.from(fragment.firstChild).map(SugarElement.fromDom);\n      const lastChild = Optional.from(fragment.lastChild).map(SugarElement.fromDom);\n      rng.deleteContents();\n      rng.insertNode(fragment);\n      const prevText = firstChild.bind(prevSibling).filter(isText$b).bind(removeEmpty);\n      const nextText = lastChild.bind(nextSibling).filter(isText$b).bind(removeEmpty);\n      mergeAndNormalizeText(prevText, firstChild, rng, true);\n      mergeAndNormalizeText(nextText, lastChild, rng, false);\n      rng.collapse(false);\n    };\n    const setupArgs$2 = (args, content) => ({\n      format: 'html',\n      ...args,\n      set: true,\n      selection: true,\n      content\n    });\n    const cleanContent = (editor, args) => {\n      if (args.format !== 'raw') {\n        const rng = editor.selection.getRng();\n        const contextBlock = editor.dom.getParent(rng.commonAncestorContainer, editor.dom.isBlock);\n        const contextArgs = contextBlock ? { context: contextBlock.nodeName.toLowerCase() } : {};\n        const node = editor.parser.parse(args.content, {\n          forced_root_block: false,\n          ...contextArgs,\n          ...args\n        });\n        return HtmlSerializer({ validate: false }, editor.schema).serialize(node);\n      } else {\n        return args.content;\n      }\n    };\n    const setContent$1 = (editor, content, args = {}) => {\n      const defaultedArgs = setupArgs$2(args, content);\n      preProcessSetContent(editor, defaultedArgs).each(updatedArgs => {\n        const cleanedContent = cleanContent(editor, updatedArgs);\n        const rng = editor.selection.getRng();\n        rngSetContent(rng, rng.createContextualFragment(cleanedContent));\n        editor.selection.setRng(rng);\n        scrollRangeIntoView(editor, rng);\n        postProcessSetContent(editor, cleanedContent, updatedArgs);\n      });\n    };\n\n    const deleteFromCallbackMap = (callbackMap, selector, callback) => {\n      if (has$2(callbackMap, selector)) {\n        const newCallbacks = filter$5(callbackMap[selector], cb => cb !== callback);\n        if (newCallbacks.length === 0) {\n          delete callbackMap[selector];\n        } else {\n          callbackMap[selector] = newCallbacks;\n        }\n      }\n    };\n    var SelectorChanged = (dom, editor) => {\n      let selectorChangedData;\n      let currentSelectors;\n      const findMatchingNode = (selector, nodes) => find$2(nodes, node => dom.is(node, selector));\n      const getParents = elem => dom.getParents(elem, undefined, dom.getRoot());\n      const setup = () => {\n        selectorChangedData = {};\n        currentSelectors = {};\n        editor.on('NodeChange', e => {\n          const node = e.element;\n          const parents = getParents(node);\n          const matchedSelectors = {};\n          each$d(selectorChangedData, (callbacks, selector) => {\n            findMatchingNode(selector, parents).each(node => {\n              if (!currentSelectors[selector]) {\n                each$e(callbacks, callback => {\n                  callback(true, {\n                    node,\n                    selector,\n                    parents\n                  });\n                });\n                currentSelectors[selector] = callbacks;\n              }\n              matchedSelectors[selector] = callbacks;\n            });\n          });\n          each$d(currentSelectors, (callbacks, selector) => {\n            if (!matchedSelectors[selector]) {\n              delete currentSelectors[selector];\n              each$e(callbacks, callback => {\n                callback(false, {\n                  node,\n                  selector,\n                  parents\n                });\n              });\n            }\n          });\n        });\n      };\n      return {\n        selectorChangedWithUnbind: (selector, callback) => {\n          if (!selectorChangedData) {\n            setup();\n          }\n          if (!selectorChangedData[selector]) {\n            selectorChangedData[selector] = [];\n          }\n          selectorChangedData[selector].push(callback);\n          findMatchingNode(selector, getParents(editor.selection.getStart())).each(() => {\n            currentSelectors[selector] = selectorChangedData[selector];\n          });\n          return {\n            unbind: () => {\n              deleteFromCallbackMap(selectorChangedData, selector, callback);\n              deleteFromCallbackMap(currentSelectors, selector, callback);\n            }\n          };\n        }\n      };\n    };\n\n    const isAttachedToDom = node => {\n      return !!(node && node.ownerDocument) && contains(SugarElement.fromDom(node.ownerDocument), SugarElement.fromDom(node));\n    };\n    const isValidRange = rng => {\n      if (!rng) {\n        return false;\n      } else {\n        return isAttachedToDom(rng.startContainer) && isAttachedToDom(rng.endContainer);\n      }\n    };\n    const EditorSelection = (dom, win, serializer, editor) => {\n      let selectedRange;\n      let explicitRange;\n      const {selectorChangedWithUnbind} = SelectorChanged(dom, editor);\n      const setCursorLocation = (node, offset) => {\n        const rng = dom.createRng();\n        if (isNonNullable(node) && isNonNullable(offset)) {\n          rng.setStart(node, offset);\n          rng.setEnd(node, offset);\n          setRng(rng);\n          collapse(false);\n        } else {\n          moveEndPoint(dom, rng, editor.getBody(), true);\n          setRng(rng);\n        }\n      };\n      const getContent = args => getContent$1(editor, args);\n      const setContent = (content, args) => setContent$1(editor, content, args);\n      const getStart$1 = real => getStart(editor.getBody(), getRng$1(), real);\n      const getEnd = real => getEnd$1(editor.getBody(), getRng$1(), real);\n      const getBookmark = (type, normalized) => bookmarkManager.getBookmark(type, normalized);\n      const moveToBookmark = bookmark => bookmarkManager.moveToBookmark(bookmark);\n      const select$1 = (node, content) => {\n        select(dom, node, content).each(setRng);\n        return node;\n      };\n      const isCollapsed = () => {\n        const rng = getRng$1(), sel = getSel();\n        if (!rng || rng.item) {\n          return false;\n        }\n        if (rng.compareEndPoints) {\n          return rng.compareEndPoints('StartToEnd', rng) === 0;\n        }\n        return !sel || rng.collapsed;\n      };\n      const collapse = toStart => {\n        const rng = getRng$1();\n        rng.collapse(!!toStart);\n        setRng(rng);\n      };\n      const getSel = () => win.getSelection ? win.getSelection() : win.document.selection;\n      const getRng$1 = () => {\n        let rng;\n        const tryCompareBoundaryPoints = (how, sourceRange, destinationRange) => {\n          try {\n            return sourceRange.compareBoundaryPoints(how, destinationRange);\n          } catch (ex) {\n            return -1;\n          }\n        };\n        const doc = win.document;\n        if (isNonNullable(editor.bookmark) && !hasFocus(editor)) {\n          const bookmark = getRng(editor);\n          if (bookmark.isSome()) {\n            return bookmark.map(r => processRanges(editor, [r])[0]).getOr(doc.createRange());\n          }\n        }\n        try {\n          const selection = getSel();\n          if (selection && !isRestrictedNode(selection.anchorNode)) {\n            if (selection.rangeCount > 0) {\n              rng = selection.getRangeAt(0);\n            } else {\n              rng = doc.createRange();\n            }\n            rng = processRanges(editor, [rng])[0];\n          }\n        } catch (ex) {\n        }\n        if (!rng) {\n          rng = doc.createRange();\n        }\n        if (isDocument$1(rng.startContainer) && rng.collapsed) {\n          const elm = dom.getRoot();\n          rng.setStart(elm, 0);\n          rng.setEnd(elm, 0);\n        }\n        if (selectedRange && explicitRange) {\n          if (tryCompareBoundaryPoints(rng.START_TO_START, rng, selectedRange) === 0 && tryCompareBoundaryPoints(rng.END_TO_END, rng, selectedRange) === 0) {\n            rng = explicitRange;\n          } else {\n            selectedRange = null;\n            explicitRange = null;\n          }\n        }\n        return rng;\n      };\n      const setRng = (rng, forward) => {\n        if (!isValidRange(rng)) {\n          return;\n        }\n        const sel = getSel();\n        const evt = editor.dispatch('SetSelectionRange', {\n          range: rng,\n          forward\n        });\n        rng = evt.range;\n        if (sel) {\n          explicitRange = rng;\n          try {\n            sel.removeAllRanges();\n            sel.addRange(rng);\n          } catch (ex) {\n          }\n          if (forward === false && sel.extend) {\n            sel.collapse(rng.endContainer, rng.endOffset);\n            sel.extend(rng.startContainer, rng.startOffset);\n          }\n          selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;\n        }\n        if (!rng.collapsed && rng.startContainer === rng.endContainer && (sel === null || sel === void 0 ? void 0 : sel.setBaseAndExtent)) {\n          if (rng.endOffset - rng.startOffset < 2) {\n            if (rng.startContainer.hasChildNodes()) {\n              const node = rng.startContainer.childNodes[rng.startOffset];\n              if (node && node.nodeName === 'IMG') {\n                sel.setBaseAndExtent(rng.startContainer, rng.startOffset, rng.endContainer, rng.endOffset);\n                if (sel.anchorNode !== rng.startContainer || sel.focusNode !== rng.endContainer) {\n                  sel.setBaseAndExtent(node, 0, node, 1);\n                }\n              }\n            }\n          }\n        }\n        editor.dispatch('AfterSetSelectionRange', {\n          range: rng,\n          forward\n        });\n      };\n      const setNode = elm => {\n        setContent(dom.getOuterHTML(elm));\n        return elm;\n      };\n      const getNode$1 = () => getNode(editor.getBody(), getRng$1());\n      const getSelectedBlocks$1 = (startElm, endElm) => getSelectedBlocks(dom, getRng$1(), startElm, endElm);\n      const isForward = () => {\n        const sel = getSel();\n        const anchorNode = sel === null || sel === void 0 ? void 0 : sel.anchorNode;\n        const focusNode = sel === null || sel === void 0 ? void 0 : sel.focusNode;\n        if (!sel || !anchorNode || !focusNode || isRestrictedNode(anchorNode) || isRestrictedNode(focusNode)) {\n          return true;\n        }\n        const anchorRange = dom.createRng();\n        const focusRange = dom.createRng();\n        try {\n          anchorRange.setStart(anchorNode, sel.anchorOffset);\n          anchorRange.collapse(true);\n          focusRange.setStart(focusNode, sel.focusOffset);\n          focusRange.collapse(true);\n        } catch (e) {\n          return true;\n        }\n        return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;\n      };\n      const normalize = () => {\n        const rng = getRng$1();\n        const sel = getSel();\n        if (!hasMultipleRanges(sel) && hasAnyRanges(editor)) {\n          const normRng = normalize$2(dom, rng);\n          normRng.each(normRng => {\n            setRng(normRng, isForward());\n          });\n          return normRng.getOr(rng);\n        }\n        return rng;\n      };\n      const selectorChanged = (selector, callback) => {\n        selectorChangedWithUnbind(selector, callback);\n        return exports;\n      };\n      const getScrollContainer = () => {\n        let scrollContainer;\n        let node = dom.getRoot();\n        while (node && node.nodeName !== 'BODY') {\n          if (node.scrollHeight > node.clientHeight) {\n            scrollContainer = node;\n            break;\n          }\n          node = node.parentNode;\n        }\n        return scrollContainer;\n      };\n      const scrollIntoView = (elm, alignToTop) => {\n        if (isNonNullable(elm)) {\n          scrollElementIntoView(editor, elm, alignToTop);\n        } else {\n          scrollRangeIntoView(editor, getRng$1(), alignToTop);\n        }\n      };\n      const placeCaretAt = (clientX, clientY) => setRng(fromPoint(clientX, clientY, editor.getDoc()));\n      const getBoundingClientRect = () => {\n        const rng = getRng$1();\n        return rng.collapsed ? CaretPosition.fromRangeStart(rng).getClientRects()[0] : rng.getBoundingClientRect();\n      };\n      const destroy = () => {\n        win = selectedRange = explicitRange = null;\n        controlSelection.destroy();\n      };\n      const expand = (options = { type: 'word' }) => setRng(RangeUtils(dom).expand(getRng$1(), options));\n      const exports = {\n        dom,\n        win,\n        serializer,\n        editor,\n        expand,\n        collapse,\n        setCursorLocation,\n        getContent,\n        setContent,\n        getBookmark,\n        moveToBookmark,\n        select: select$1,\n        isCollapsed,\n        isForward,\n        setNode,\n        getNode: getNode$1,\n        getSel,\n        setRng,\n        getRng: getRng$1,\n        getStart: getStart$1,\n        getEnd,\n        getSelectedBlocks: getSelectedBlocks$1,\n        normalize,\n        selectorChanged,\n        selectorChangedWithUnbind,\n        getScrollContainer,\n        scrollIntoView,\n        placeCaretAt,\n        getBoundingClientRect,\n        destroy\n      };\n      const bookmarkManager = BookmarkManager(exports);\n      const controlSelection = ControlSelection(exports, editor);\n      exports.bookmarkManager = bookmarkManager;\n      exports.controlSelection = controlSelection;\n      return exports;\n    };\n\n    const register$3 = (htmlParser, settings, dom) => {\n      htmlParser.addAttributeFilter('data-mce-tabindex', (nodes, name) => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          node.attr('tabindex', node.attr('data-mce-tabindex'));\n          node.attr(name, null);\n        }\n      });\n      htmlParser.addAttributeFilter('src,href,style', (nodes, name) => {\n        const internalName = 'data-mce-' + name;\n        const urlConverter = settings.url_converter;\n        const urlConverterScope = settings.url_converter_scope;\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          let value = node.attr(internalName);\n          if (value !== undefined) {\n            node.attr(name, value.length > 0 ? value : null);\n            node.attr(internalName, null);\n          } else {\n            value = node.attr(name);\n            if (name === 'style') {\n              value = dom.serializeStyle(dom.parseStyle(value), node.name);\n            } else if (urlConverter) {\n              value = urlConverter.call(urlConverterScope, value, name, node.name);\n            }\n            node.attr(name, value.length > 0 ? value : null);\n          }\n        }\n      });\n      htmlParser.addAttributeFilter('class', nodes => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          let value = node.attr('class');\n          if (value) {\n            value = value.replace(/(?:^|\\s)mce-item-\\w+(?!\\S)/g, '');\n            node.attr('class', value.length > 0 ? value : null);\n          }\n        }\n      });\n      htmlParser.addAttributeFilter('data-mce-type', (nodes, name, args) => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          if (node.attr('data-mce-type') === 'bookmark' && !args.cleanup) {\n            const hasChildren = Optional.from(node.firstChild).exists(firstChild => {\n              var _a;\n              return !isZwsp((_a = firstChild.value) !== null && _a !== void 0 ? _a : '');\n            });\n            if (hasChildren) {\n              node.unwrap();\n            } else {\n              node.remove();\n            }\n          }\n        }\n      });\n      htmlParser.addNodeFilter('noscript', nodes => {\n        var _a;\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i].firstChild;\n          if (node) {\n            node.value = Entities.decode((_a = node.value) !== null && _a !== void 0 ? _a : '');\n          }\n        }\n      });\n      htmlParser.addNodeFilter('script,style', (nodes, name) => {\n        var _a;\n        const trim = value => {\n          return value.replace(/(<!--\\[CDATA\\[|\\]\\]-->)/g, '\\n').replace(/^[\\r\\n]*|[\\r\\n]*$/g, '').replace(/^\\s*((<!--)?(\\s*\\/\\/)?\\s*<!\\[CDATA\\[|(<!--\\s*)?\\/\\*\\s*<!\\[CDATA\\[\\s*\\*\\/|(\\/\\/)?\\s*<!--|\\/\\*\\s*<!--\\s*\\*\\/)\\s*[\\r\\n]*/gi, '').replace(/\\s*(\\/\\*\\s*\\]\\]>\\s*\\*\\/(-->)?|\\s*\\/\\/\\s*\\]\\]>(-->)?|\\/\\/\\s*(-->)?|\\]\\]>|\\/\\*\\s*-->\\s*\\*\\/|\\s*-->\\s*)\\s*$/g, '');\n        };\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          const firstChild = node.firstChild;\n          const value = (_a = firstChild === null || firstChild === void 0 ? void 0 : firstChild.value) !== null && _a !== void 0 ? _a : '';\n          if (name === 'script') {\n            const type = node.attr('type');\n            if (type) {\n              node.attr('type', type === 'mce-no/type' ? null : type.replace(/^mce\\-/, ''));\n            }\n            if (settings.element_format === 'xhtml' && firstChild && value.length > 0) {\n              firstChild.value = '// <![CDATA[\\n' + trim(value) + '\\n// ]]>';\n            }\n          } else {\n            if (settings.element_format === 'xhtml' && firstChild && value.length > 0) {\n              firstChild.value = '<!--\\n' + trim(value) + '\\n-->';\n            }\n          }\n        }\n      });\n      htmlParser.addNodeFilter('#comment', nodes => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          const value = node.value;\n          if (settings.preserve_cdata && (value === null || value === void 0 ? void 0 : value.indexOf('[CDATA[')) === 0) {\n            node.name = '#cdata';\n            node.type = 4;\n            node.value = dom.decode(value.replace(/^\\[CDATA\\[|\\]\\]$/g, ''));\n          } else if ((value === null || value === void 0 ? void 0 : value.indexOf('mce:protected ')) === 0) {\n            node.name = '#text';\n            node.type = 3;\n            node.raw = true;\n            node.value = unescape(value).substr(14);\n          }\n        }\n      });\n      htmlParser.addNodeFilter('xml:namespace,input', (nodes, name) => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          if (node.type === 7) {\n            node.remove();\n          } else if (node.type === 1) {\n            if (name === 'input' && !node.attr('type')) {\n              node.attr('type', 'text');\n            }\n          }\n        }\n      });\n      htmlParser.addAttributeFilter('data-mce-type', nodes => {\n        each$e(nodes, node => {\n          if (node.attr('data-mce-type') === 'format-caret') {\n            if (node.isEmpty(htmlParser.schema.getNonEmptyElements())) {\n              node.remove();\n            } else {\n              node.unwrap();\n            }\n          }\n        });\n      });\n      htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style,' + 'data-mce-selected,data-mce-expando,data-mce-block,' + 'data-mce-type,data-mce-resize,data-mce-placeholder', (nodes, name) => {\n        let i = nodes.length;\n        while (i--) {\n          nodes[i].attr(name, null);\n        }\n      });\n    };\n    const trimTrailingBr = rootNode => {\n      const isBr = node => {\n        return (node === null || node === void 0 ? void 0 : node.name) === 'br';\n      };\n      const brNode1 = rootNode.lastChild;\n      if (isBr(brNode1)) {\n        const brNode2 = brNode1.prev;\n        if (isBr(brNode2)) {\n          brNode1.remove();\n          brNode2.remove();\n        }\n      }\n    };\n\n    const preProcess$1 = (editor, node, args) => {\n      let oldDoc;\n      const dom = editor.dom;\n      let clonedNode = node.cloneNode(true);\n      const impl = document.implementation;\n      if (impl.createHTMLDocument) {\n        const doc = impl.createHTMLDocument('');\n        Tools.each(clonedNode.nodeName === 'BODY' ? clonedNode.childNodes : [clonedNode], node => {\n          doc.body.appendChild(doc.importNode(node, true));\n        });\n        if (clonedNode.nodeName !== 'BODY') {\n          clonedNode = doc.body.firstChild;\n        } else {\n          clonedNode = doc.body;\n        }\n        oldDoc = dom.doc;\n        dom.doc = doc;\n      }\n      firePreProcess(editor, {\n        ...args,\n        node: clonedNode\n      });\n      if (oldDoc) {\n        dom.doc = oldDoc;\n      }\n      return clonedNode;\n    };\n    const shouldFireEvent = (editor, args) => {\n      return isNonNullable(editor) && editor.hasEventListeners('PreProcess') && !args.no_events;\n    };\n    const process$1 = (editor, node, args) => {\n      return shouldFireEvent(editor, args) ? preProcess$1(editor, node, args) : node;\n    };\n\n    const addTempAttr = (htmlParser, tempAttrs, name) => {\n      if (Tools.inArray(tempAttrs, name) === -1) {\n        htmlParser.addAttributeFilter(name, (nodes, name) => {\n          let i = nodes.length;\n          while (i--) {\n            nodes[i].attr(name, null);\n          }\n        });\n        tempAttrs.push(name);\n      }\n    };\n    const postProcess = (editor, args, content) => {\n      if (!args.no_events && editor) {\n        const outArgs = firePostProcess(editor, {\n          ...args,\n          content\n        });\n        return outArgs.content;\n      } else {\n        return content;\n      }\n    };\n    const getHtmlFromNode = (dom, node, args) => {\n      const html = trim$1(args.getInner ? node.innerHTML : dom.getOuterHTML(node));\n      return args.selection || isWsPreserveElement(SugarElement.fromDom(node)) ? html : Tools.trim(html);\n    };\n    const parseHtml = (htmlParser, html, args) => {\n      const parserArgs = args.selection ? {\n        forced_root_block: false,\n        ...args\n      } : args;\n      const rootNode = htmlParser.parse(html, parserArgs);\n      trimTrailingBr(rootNode);\n      return rootNode;\n    };\n    const serializeNode = (settings, schema, node) => {\n      const htmlSerializer = HtmlSerializer(settings, schema);\n      return htmlSerializer.serialize(node);\n    };\n    const toHtml = (editor, settings, schema, rootNode, args) => {\n      const content = serializeNode(settings, schema, rootNode);\n      return postProcess(editor, args, content);\n    };\n    const DomSerializerImpl = (settings, editor) => {\n      const tempAttrs = ['data-mce-selected'];\n      const dom = editor && editor.dom ? editor.dom : DOMUtils.DOM;\n      const schema = editor && editor.schema ? editor.schema : Schema(settings);\n      settings.entity_encoding = settings.entity_encoding || 'named';\n      settings.remove_trailing_brs = 'remove_trailing_brs' in settings ? settings.remove_trailing_brs : true;\n      const htmlParser = DomParser(settings, schema);\n      register$3(htmlParser, settings, dom);\n      const serialize = (node, parserArgs = {}) => {\n        const args = {\n          format: 'html',\n          ...parserArgs\n        };\n        const targetNode = process$1(editor, node, args);\n        const html = getHtmlFromNode(dom, targetNode, args);\n        const rootNode = parseHtml(htmlParser, html, args);\n        return args.format === 'tree' ? rootNode : toHtml(editor, settings, schema, rootNode, args);\n      };\n      return {\n        schema,\n        addNodeFilter: htmlParser.addNodeFilter,\n        addAttributeFilter: htmlParser.addAttributeFilter,\n        serialize: serialize,\n        addRules: schema.addValidElements,\n        setRules: schema.setValidElements,\n        addTempAttr: curry(addTempAttr, htmlParser, tempAttrs),\n        getTempAttrs: constant(tempAttrs),\n        getNodeFilters: htmlParser.getNodeFilters,\n        getAttributeFilters: htmlParser.getAttributeFilters,\n        removeNodeFilter: htmlParser.removeNodeFilter,\n        removeAttributeFilter: htmlParser.removeAttributeFilter\n      };\n    };\n\n    const DomSerializer = (settings, editor) => {\n      const domSerializer = DomSerializerImpl(settings, editor);\n      return {\n        schema: domSerializer.schema,\n        addNodeFilter: domSerializer.addNodeFilter,\n        addAttributeFilter: domSerializer.addAttributeFilter,\n        serialize: domSerializer.serialize,\n        addRules: domSerializer.addRules,\n        setRules: domSerializer.setRules,\n        addTempAttr: domSerializer.addTempAttr,\n        getTempAttrs: domSerializer.getTempAttrs,\n        getNodeFilters: domSerializer.getNodeFilters,\n        getAttributeFilters: domSerializer.getAttributeFilters,\n        removeNodeFilter: domSerializer.removeNodeFilter,\n        removeAttributeFilter: domSerializer.removeAttributeFilter\n      };\n    };\n\n    const defaultFormat$1 = 'html';\n    const setupArgs$1 = (args, format) => ({\n      ...args,\n      format,\n      get: true,\n      getInner: true\n    });\n    const getContent = (editor, args = {}) => {\n      const format = args.format ? args.format : defaultFormat$1;\n      const defaultedArgs = setupArgs$1(args, format);\n      return preProcessGetContent(editor, defaultedArgs).fold(identity, updatedArgs => {\n        const content = getContent$2(editor, updatedArgs);\n        return postProcessGetContent(editor, content, updatedArgs);\n      });\n    };\n\n    const defaultFormat = 'html';\n    const setupArgs = (args, content) => ({\n      format: defaultFormat,\n      ...args,\n      set: true,\n      content\n    });\n    const setContent = (editor, content, args = {}) => {\n      const defaultedArgs = setupArgs(args, content);\n      return preProcessSetContent(editor, defaultedArgs).map(updatedArgs => {\n        const result = setContent$2(editor, updatedArgs.content, updatedArgs);\n        postProcessSetContent(editor, result.html, updatedArgs);\n        return result.content;\n      }).getOr(content);\n    };\n\n    const removedOptions = ('autoresize_on_init,content_editable_state,padd_empty_with_br,block_elements,' + 'boolean_attributes,editor_deselector,editor_selector,elements,file_browser_callback_types,filepicker_validator_handler,' + 'force_hex_style_colors,force_p_newlines,gecko_spellcheck,images_dataimg_filter,media_scripts,mode,move_caret_before_on_enter_elements,' + 'non_empty_elements,self_closing_elements,short_ended_elements,special,spellchecker_select_languages,spellchecker_whitelist,' + 'tab_focus,tabfocus_elements,table_responsive_width,text_block_elements,text_inline_elements,toolbar_drawer,types,validate,whitespace_elements,' + 'paste_enable_default_filters,paste_filter_drop,paste_word_valid_elements,paste_retain_style_properties,paste_convert_word_fake_lists').split(',');\n    const removedPlugins = 'bbcode,colorpicker,contextmenu,fullpage,legacyoutput,spellchecker,textcolor'.split(',');\n    const getRemovedOptions = options => {\n      const settingNames = filter$5(removedOptions, setting => has$2(options, setting));\n      const forcedRootBlock = options.forced_root_block;\n      if (forcedRootBlock === false || forcedRootBlock === '') {\n        settingNames.push('forced_root_block (false only)');\n      }\n      return sort(settingNames);\n    };\n    const getRemovedPlugins = options => {\n      const plugins = Tools.makeMap(options.plugins, ' ');\n      const hasPlugin = plugin => has$2(plugins, plugin);\n      const pluginNames = filter$5(removedPlugins, hasPlugin);\n      return sort(pluginNames);\n    };\n    const logRemovedWarnings = (rawOptions, normalizedOptions) => {\n      const removedOptions = getRemovedOptions(rawOptions);\n      const removedPlugins = getRemovedPlugins(normalizedOptions);\n      const hasRemovedPlugins = removedPlugins.length > 0;\n      const hasRemovedOptions = removedOptions.length > 0;\n      const isLegacyMobileTheme = normalizedOptions.theme === 'mobile';\n      if (hasRemovedPlugins || hasRemovedOptions || isLegacyMobileTheme) {\n        const listJoiner = '\\n- ';\n        const themesMessage = isLegacyMobileTheme ? `\\n\\nThemes:${ listJoiner }mobile` : '';\n        const pluginsMessage = hasRemovedPlugins ? `\\n\\nPlugins:${ listJoiner }${ removedPlugins.join(listJoiner) }` : '';\n        const optionsMessage = hasRemovedOptions ? `\\n\\nOptions:${ listJoiner }${ removedOptions.join(listJoiner) }` : '';\n        console.warn('The following deprecated features are currently enabled and have been removed in TinyMCE 6.0. These features will no longer work and should be removed from the TinyMCE configuration. ' + 'See https://www.tiny.cloud/docs/tinymce/6/migration-from-5x/ for more information.' + themesMessage + pluginsMessage + optionsMessage);\n      }\n    };\n    const logWarnings = (rawOptions, normalizedOptions) => {\n      logRemovedWarnings(rawOptions, normalizedOptions);\n    };\n\n    const DOM$8 = DOMUtils.DOM;\n    const restoreOriginalStyles = editor => {\n      DOM$8.setStyle(editor.id, 'display', editor.orgDisplay);\n    };\n    const safeDestroy = x => Optional.from(x).each(x => x.destroy());\n    const clearDomReferences = editor => {\n      const ed = editor;\n      ed.contentAreaContainer = ed.formElement = ed.container = ed.editorContainer = null;\n      ed.bodyElement = ed.contentDocument = ed.contentWindow = null;\n      ed.iframeElement = ed.targetElm = null;\n      const selection = editor.selection;\n      if (selection) {\n        const dom = selection.dom;\n        ed.selection = selection.win = selection.dom = dom.doc = null;\n      }\n    };\n    const restoreForm = editor => {\n      const form = editor.formElement;\n      if (form) {\n        if (form._mceOldSubmit) {\n          form.submit = form._mceOldSubmit;\n          delete form._mceOldSubmit;\n        }\n        DOM$8.unbind(form, 'submit reset', editor.formEventDelegate);\n      }\n    };\n    const remove$1 = editor => {\n      if (!editor.removed) {\n        const {_selectionOverrides, editorUpload} = editor;\n        const body = editor.getBody();\n        const element = editor.getElement();\n        if (body) {\n          editor.save({ is_removing: true });\n        }\n        editor.removed = true;\n        editor.unbindAllNativeEvents();\n        if (editor.hasHiddenInput && isNonNullable(element === null || element === void 0 ? void 0 : element.nextSibling)) {\n          DOM$8.remove(element.nextSibling);\n        }\n        fireRemove(editor);\n        editor.editorManager.remove(editor);\n        if (!editor.inline && body) {\n          restoreOriginalStyles(editor);\n        }\n        fireDetach(editor);\n        DOM$8.remove(editor.getContainer());\n        safeDestroy(_selectionOverrides);\n        safeDestroy(editorUpload);\n        editor.destroy();\n      }\n    };\n    const destroy = (editor, automatic) => {\n      const {selection, dom} = editor;\n      if (editor.destroyed) {\n        return;\n      }\n      if (!automatic && !editor.removed) {\n        editor.remove();\n        return;\n      }\n      if (!automatic) {\n        editor.editorManager.off('beforeunload', editor._beforeUnload);\n        if (editor.theme && editor.theme.destroy) {\n          editor.theme.destroy();\n        }\n        safeDestroy(selection);\n        safeDestroy(dom);\n      }\n      restoreForm(editor);\n      clearDomReferences(editor);\n      editor.destroyed = true;\n    };\n\n    const CreateIconManager = () => {\n      const lookup = {};\n      const add = (id, iconPack) => {\n        lookup[id] = iconPack;\n      };\n      const get = id => {\n        if (lookup[id]) {\n          return lookup[id];\n        } else {\n          return { icons: {} };\n        }\n      };\n      const has = id => has$2(lookup, id);\n      return {\n        add,\n        get,\n        has\n      };\n    };\n    const IconManager = CreateIconManager();\n\n    const ModelManager = AddOnManager.ModelManager;\n\n    const getProp = (propName, elm) => {\n      const rawElm = elm.dom;\n      return rawElm[propName];\n    };\n    const getComputedSizeProp = (propName, elm) => parseInt(get$7(elm, propName), 10);\n    const getClientWidth = curry(getProp, 'clientWidth');\n    const getClientHeight = curry(getProp, 'clientHeight');\n    const getMarginTop = curry(getComputedSizeProp, 'margin-top');\n    const getMarginLeft = curry(getComputedSizeProp, 'margin-left');\n    const getBoundingClientRect = elm => elm.dom.getBoundingClientRect();\n    const isInsideElementContentArea = (bodyElm, clientX, clientY) => {\n      const clientWidth = getClientWidth(bodyElm);\n      const clientHeight = getClientHeight(bodyElm);\n      return clientX >= 0 && clientY >= 0 && clientX <= clientWidth && clientY <= clientHeight;\n    };\n    const transpose = (inline, elm, clientX, clientY) => {\n      const clientRect = getBoundingClientRect(elm);\n      const deltaX = inline ? clientRect.left + elm.dom.clientLeft + getMarginLeft(elm) : 0;\n      const deltaY = inline ? clientRect.top + elm.dom.clientTop + getMarginTop(elm) : 0;\n      const x = clientX - deltaX;\n      const y = clientY - deltaY;\n      return {\n        x,\n        y\n      };\n    };\n    const isXYInContentArea = (editor, clientX, clientY) => {\n      const bodyElm = SugarElement.fromDom(editor.getBody());\n      const targetElm = editor.inline ? bodyElm : documentElement(bodyElm);\n      const transposedPoint = transpose(editor.inline, targetElm, clientX, clientY);\n      return isInsideElementContentArea(targetElm, transposedPoint.x, transposedPoint.y);\n    };\n    const fromDomSafe = node => Optional.from(node).map(SugarElement.fromDom);\n    const isEditorAttachedToDom = editor => {\n      const rawContainer = editor.inline ? editor.getBody() : editor.getContentAreaContainer();\n      return fromDomSafe(rawContainer).map(inBody).getOr(false);\n    };\n\n    var NotificationManagerImpl = () => {\n      const unimplemented = () => {\n        throw new Error('Theme did not provide a NotificationManager implementation.');\n      };\n      return {\n        open: unimplemented,\n        close: unimplemented,\n        getArgs: unimplemented\n      };\n    };\n\n    const NotificationManager = editor => {\n      const notifications = [];\n      const getImplementation = () => {\n        const theme = editor.theme;\n        return theme && theme.getNotificationManagerImpl ? theme.getNotificationManagerImpl() : NotificationManagerImpl();\n      };\n      const getTopNotification = () => {\n        return Optional.from(notifications[0]);\n      };\n      const isEqual = (a, b) => {\n        return a.type === b.type && a.text === b.text && !a.progressBar && !a.timeout && !b.progressBar && !b.timeout;\n      };\n      const reposition = () => {\n        each$e(notifications, notification => {\n          notification.reposition();\n        });\n      };\n      const addNotification = notification => {\n        notifications.push(notification);\n      };\n      const closeNotification = notification => {\n        findIndex$2(notifications, otherNotification => {\n          return otherNotification === notification;\n        }).each(index => {\n          notifications.splice(index, 1);\n        });\n      };\n      const open = (spec, fireEvent = true) => {\n        if (editor.removed || !isEditorAttachedToDom(editor)) {\n          return {};\n        }\n        if (fireEvent) {\n          editor.dispatch('BeforeOpenNotification', { notification: spec });\n        }\n        return find$2(notifications, notification => {\n          return isEqual(getImplementation().getArgs(notification), spec);\n        }).getOrThunk(() => {\n          editor.editorManager.setActive(editor);\n          const notification = getImplementation().open(spec, () => {\n            closeNotification(notification);\n            reposition();\n            getTopNotification().fold(() => editor.focus(), top => focus$1(SugarElement.fromDom(top.getEl())));\n          });\n          addNotification(notification);\n          reposition();\n          editor.dispatch('OpenNotification', { notification: { ...notification } });\n          return notification;\n        });\n      };\n      const close = () => {\n        getTopNotification().each(notification => {\n          getImplementation().close(notification);\n          closeNotification(notification);\n          reposition();\n        });\n      };\n      const getNotifications = constant(notifications);\n      const registerEvents = editor => {\n        editor.on('SkinLoaded', () => {\n          const serviceMessage = getServiceMessage(editor);\n          if (serviceMessage) {\n            open({\n              text: serviceMessage,\n              type: 'warning',\n              timeout: 0\n            }, false);\n          }\n          reposition();\n        });\n        editor.on('show ResizeEditor ResizeWindow NodeChange', () => {\n          requestAnimationFrame(reposition);\n        });\n        editor.on('remove', () => {\n          each$e(notifications.slice(), notification => {\n            getImplementation().close(notification);\n          });\n        });\n      };\n      registerEvents(editor);\n      return {\n        open,\n        close,\n        getNotifications\n      };\n    };\n\n    const PluginManager = AddOnManager.PluginManager;\n\n    const ThemeManager = AddOnManager.ThemeManager;\n\n    var WindowManagerImpl = () => {\n      const unimplemented = () => {\n        throw new Error('Theme did not provide a WindowManager implementation.');\n      };\n      return {\n        open: unimplemented,\n        openUrl: unimplemented,\n        alert: unimplemented,\n        confirm: unimplemented,\n        close: unimplemented\n      };\n    };\n\n    const WindowManager = editor => {\n      let dialogs = [];\n      const getImplementation = () => {\n        const theme = editor.theme;\n        return theme && theme.getWindowManagerImpl ? theme.getWindowManagerImpl() : WindowManagerImpl();\n      };\n      const funcBind = (scope, f) => {\n        return (...args) => {\n          return f ? f.apply(scope, args) : undefined;\n        };\n      };\n      const fireOpenEvent = dialog => {\n        editor.dispatch('OpenWindow', { dialog });\n      };\n      const fireCloseEvent = dialog => {\n        editor.dispatch('CloseWindow', { dialog });\n      };\n      const addDialog = dialog => {\n        dialogs.push(dialog);\n        fireOpenEvent(dialog);\n      };\n      const closeDialog = dialog => {\n        fireCloseEvent(dialog);\n        dialogs = filter$5(dialogs, otherDialog => {\n          return otherDialog !== dialog;\n        });\n        if (dialogs.length === 0) {\n          editor.focus();\n        }\n      };\n      const getTopDialog = () => {\n        return Optional.from(dialogs[dialogs.length - 1]);\n      };\n      const storeSelectionAndOpenDialog = openDialog => {\n        editor.editorManager.setActive(editor);\n        store(editor);\n        editor.ui.show();\n        const dialog = openDialog();\n        addDialog(dialog);\n        return dialog;\n      };\n      const open = (args, params) => {\n        return storeSelectionAndOpenDialog(() => getImplementation().open(args, params, closeDialog));\n      };\n      const openUrl = args => {\n        return storeSelectionAndOpenDialog(() => getImplementation().openUrl(args, closeDialog));\n      };\n      const alert = (message, callback, scope) => {\n        const windowManagerImpl = getImplementation();\n        windowManagerImpl.alert(message, funcBind(scope ? scope : windowManagerImpl, callback));\n      };\n      const confirm = (message, callback, scope) => {\n        const windowManagerImpl = getImplementation();\n        windowManagerImpl.confirm(message, funcBind(scope ? scope : windowManagerImpl, callback));\n      };\n      const close = () => {\n        getTopDialog().each(dialog => {\n          getImplementation().close(dialog);\n          closeDialog(dialog);\n        });\n      };\n      editor.on('remove', () => {\n        each$e(dialogs, dialog => {\n          getImplementation().close(dialog);\n        });\n      });\n      return {\n        open,\n        openUrl,\n        alert,\n        confirm,\n        close\n      };\n    };\n\n    const displayNotification = (editor, message) => {\n      editor.notificationManager.open({\n        type: 'error',\n        text: message\n      });\n    };\n    const displayError = (editor, message) => {\n      if (editor._skinLoaded) {\n        displayNotification(editor, message);\n      } else {\n        editor.on('SkinLoaded', () => {\n          displayNotification(editor, message);\n        });\n      }\n    };\n    const uploadError = (editor, message) => {\n      displayError(editor, I18n.translate([\n        'Failed to upload image: {0}',\n        message\n      ]));\n    };\n    const logError = (editor, errorType, msg) => {\n      fireError(editor, errorType, { message: msg });\n      console.error(msg);\n    };\n    const createLoadError = (type, url, name) => name ? `Failed to load ${ type }: ${ name } from url ${ url }` : `Failed to load ${ type } url: ${ url }`;\n    const pluginLoadError = (editor, url, name) => {\n      logError(editor, 'PluginLoadError', createLoadError('plugin', url, name));\n    };\n    const iconsLoadError = (editor, url, name) => {\n      logError(editor, 'IconsLoadError', createLoadError('icons', url, name));\n    };\n    const languageLoadError = (editor, url, name) => {\n      logError(editor, 'LanguageLoadError', createLoadError('language', url, name));\n    };\n    const themeLoadError = (editor, url, name) => {\n      logError(editor, 'ThemeLoadError', createLoadError('theme', url, name));\n    };\n    const modelLoadError = (editor, url, name) => {\n      logError(editor, 'ModelLoadError', createLoadError('model', url, name));\n    };\n    const pluginInitError = (editor, name, err) => {\n      const message = I18n.translate([\n        'Failed to initialize plugin: {0}',\n        name\n      ]);\n      fireError(editor, 'PluginLoadError', { message });\n      initError(message, err);\n      displayError(editor, message);\n    };\n    const initError = (message, ...x) => {\n      const console = window.console;\n      if (console) {\n        if (console.error) {\n          console.error(message, ...x);\n        } else {\n          console.log(message, ...x);\n        }\n      }\n    };\n\n    const isContentCssSkinName = url => /^[a-z0-9\\-]+$/i.test(url);\n    const getContentCssUrls = editor => {\n      return transformToUrls(editor, getContentCss(editor));\n    };\n    const getFontCssUrls = editor => {\n      return transformToUrls(editor, getFontCss(editor));\n    };\n    const transformToUrls = (editor, cssLinks) => {\n      const skinUrl = editor.editorManager.baseURL + '/skins/content';\n      const suffix = editor.editorManager.suffix;\n      const contentCssFile = `content${ suffix }.css`;\n      return map$3(cssLinks, url => {\n        if (isContentCssSkinName(url) && !editor.inline) {\n          return `${ skinUrl }/${ url }/${ contentCssFile }`;\n        } else {\n          return editor.documentBaseURI.toAbsolute(url);\n        }\n      });\n    };\n    const appendContentCssFromSettings = editor => {\n      editor.contentCSS = editor.contentCSS.concat(getContentCssUrls(editor), getFontCssUrls(editor));\n    };\n\n    const filter$1 = always;\n    const bind$1 = (element, event, handler) => bind$2(element, event, filter$1, handler);\n\n    const getAllImages = elm => {\n      return elm ? from(elm.getElementsByTagName('img')) : [];\n    };\n    const ImageScanner = (uploadStatus, blobCache) => {\n      const cachedPromises = {};\n      const findAll = (elm, predicate = always) => {\n        const images = filter$5(getAllImages(elm), img => {\n          const src = img.src;\n          if (img.hasAttribute('data-mce-bogus')) {\n            return false;\n          }\n          if (img.hasAttribute('data-mce-placeholder')) {\n            return false;\n          }\n          if (!src || src === Env.transparentSrc) {\n            return false;\n          }\n          if (startsWith(src, 'blob:')) {\n            return !uploadStatus.isUploaded(src) && predicate(img);\n          }\n          if (startsWith(src, 'data:')) {\n            return predicate(img);\n          }\n          return false;\n        });\n        const promises = map$3(images, img => {\n          const imageSrc = img.src;\n          if (has$2(cachedPromises, imageSrc)) {\n            return cachedPromises[imageSrc].then(imageInfo => {\n              if (isString(imageInfo)) {\n                return imageInfo;\n              } else {\n                return {\n                  image: img,\n                  blobInfo: imageInfo.blobInfo\n                };\n              }\n            });\n          } else {\n            const newPromise = imageToBlobInfo(blobCache, imageSrc).then(blobInfo => {\n              delete cachedPromises[imageSrc];\n              return {\n                image: img,\n                blobInfo\n              };\n            }).catch(error => {\n              delete cachedPromises[imageSrc];\n              return error;\n            });\n            cachedPromises[imageSrc] = newPromise;\n            return newPromise;\n          }\n        });\n        return Promise.all(promises);\n      };\n      return { findAll };\n    };\n\n    const UploadStatus = () => {\n      const PENDING = 1, UPLOADED = 2;\n      let blobUriStatuses = {};\n      const createStatus = (status, resultUri) => {\n        return {\n          status,\n          resultUri\n        };\n      };\n      const hasBlobUri = blobUri => {\n        return blobUri in blobUriStatuses;\n      };\n      const getResultUri = blobUri => {\n        const result = blobUriStatuses[blobUri];\n        return result ? result.resultUri : null;\n      };\n      const isPending = blobUri => {\n        return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === PENDING : false;\n      };\n      const isUploaded = blobUri => {\n        return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === UPLOADED : false;\n      };\n      const markPending = blobUri => {\n        blobUriStatuses[blobUri] = createStatus(PENDING, null);\n      };\n      const markUploaded = (blobUri, resultUri) => {\n        blobUriStatuses[blobUri] = createStatus(UPLOADED, resultUri);\n      };\n      const removeFailed = blobUri => {\n        delete blobUriStatuses[blobUri];\n      };\n      const destroy = () => {\n        blobUriStatuses = {};\n      };\n      return {\n        hasBlobUri,\n        getResultUri,\n        isPending,\n        isUploaded,\n        markPending,\n        markUploaded,\n        removeFailed,\n        destroy\n      };\n    };\n\n    let count = 0;\n    const seed = () => {\n      const rnd = () => {\n        return Math.round(Math.random() * 4294967295).toString(36);\n      };\n      const now = new Date().getTime();\n      return 's' + now.toString(36) + rnd() + rnd() + rnd();\n    };\n    const uuid = prefix => {\n      return prefix + count++ + seed();\n    };\n\n    const BlobCache = () => {\n      let cache = [];\n      const mimeToExt = mime => {\n        const mimes = {\n          'image/jpeg': 'jpg',\n          'image/jpg': 'jpg',\n          'image/gif': 'gif',\n          'image/png': 'png',\n          'image/apng': 'apng',\n          'image/avif': 'avif',\n          'image/svg+xml': 'svg',\n          'image/webp': 'webp',\n          'image/bmp': 'bmp',\n          'image/tiff': 'tiff'\n        };\n        return mimes[mime.toLowerCase()] || 'dat';\n      };\n      const create = (o, blob, base64, name, filename) => {\n        if (isString(o)) {\n          const id = o;\n          return toBlobInfo({\n            id,\n            name,\n            filename,\n            blob: blob,\n            base64: base64\n          });\n        } else if (isObject(o)) {\n          return toBlobInfo(o);\n        } else {\n          throw new Error('Unknown input type');\n        }\n      };\n      const toBlobInfo = o => {\n        if (!o.blob || !o.base64) {\n          throw new Error('blob and base64 representations of the image are required for BlobInfo to be created');\n        }\n        const id = o.id || uuid('blobid');\n        const name = o.name || id;\n        const blob = o.blob;\n        return {\n          id: constant(id),\n          name: constant(name),\n          filename: constant(o.filename || name + '.' + mimeToExt(blob.type)),\n          blob: constant(blob),\n          base64: constant(o.base64),\n          blobUri: constant(o.blobUri || URL.createObjectURL(blob)),\n          uri: constant(o.uri)\n        };\n      };\n      const add = blobInfo => {\n        if (!get(blobInfo.id())) {\n          cache.push(blobInfo);\n        }\n      };\n      const findFirst = predicate => find$2(cache, predicate).getOrUndefined();\n      const get = id => findFirst(cachedBlobInfo => cachedBlobInfo.id() === id);\n      const getByUri = blobUri => findFirst(blobInfo => blobInfo.blobUri() === blobUri);\n      const getByData = (base64, type) => findFirst(blobInfo => blobInfo.base64() === base64 && blobInfo.blob().type === type);\n      const removeByUri = blobUri => {\n        cache = filter$5(cache, blobInfo => {\n          if (blobInfo.blobUri() === blobUri) {\n            URL.revokeObjectURL(blobInfo.blobUri());\n            return false;\n          }\n          return true;\n        });\n      };\n      const destroy = () => {\n        each$e(cache, cachedBlobInfo => {\n          URL.revokeObjectURL(cachedBlobInfo.blobUri());\n        });\n        cache = [];\n      };\n      return {\n        create,\n        add,\n        get,\n        getByUri,\n        getByData,\n        findFirst,\n        removeByUri,\n        destroy\n      };\n    };\n\n    const Uploader = (uploadStatus, settings) => {\n      const pendingPromises = {};\n      const pathJoin = (path1, path2) => {\n        if (path1) {\n          return path1.replace(/\\/$/, '') + '/' + path2.replace(/^\\//, '');\n        }\n        return path2;\n      };\n      const defaultHandler = (blobInfo, progress) => new Promise((success, failure) => {\n        const xhr = new XMLHttpRequest();\n        xhr.open('POST', settings.url);\n        xhr.withCredentials = settings.credentials;\n        xhr.upload.onprogress = e => {\n          progress(e.loaded / e.total * 100);\n        };\n        xhr.onerror = () => {\n          failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);\n        };\n        xhr.onload = () => {\n          if (xhr.status < 200 || xhr.status >= 300) {\n            failure('HTTP Error: ' + xhr.status);\n            return;\n          }\n          const json = JSON.parse(xhr.responseText);\n          if (!json || !isString(json.location)) {\n            failure('Invalid JSON: ' + xhr.responseText);\n            return;\n          }\n          success(pathJoin(settings.basePath, json.location));\n        };\n        const formData = new FormData();\n        formData.append('file', blobInfo.blob(), blobInfo.filename());\n        xhr.send(formData);\n      });\n      const uploadHandler = isFunction(settings.handler) ? settings.handler : defaultHandler;\n      const noUpload = () => new Promise(resolve => {\n        resolve([]);\n      });\n      const handlerSuccess = (blobInfo, url) => ({\n        url,\n        blobInfo,\n        status: true\n      });\n      const handlerFailure = (blobInfo, error) => ({\n        url: '',\n        blobInfo,\n        status: false,\n        error\n      });\n      const resolvePending = (blobUri, result) => {\n        Tools.each(pendingPromises[blobUri], resolve => {\n          resolve(result);\n        });\n        delete pendingPromises[blobUri];\n      };\n      const uploadBlobInfo = (blobInfo, handler, openNotification) => {\n        uploadStatus.markPending(blobInfo.blobUri());\n        return new Promise(resolve => {\n          let notification;\n          let progress;\n          try {\n            const closeNotification = () => {\n              if (notification) {\n                notification.close();\n                progress = noop;\n              }\n            };\n            const success = url => {\n              closeNotification();\n              uploadStatus.markUploaded(blobInfo.blobUri(), url);\n              resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url));\n              resolve(handlerSuccess(blobInfo, url));\n            };\n            const failure = error => {\n              closeNotification();\n              uploadStatus.removeFailed(blobInfo.blobUri());\n              resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, error));\n              resolve(handlerFailure(blobInfo, error));\n            };\n            progress = percent => {\n              if (percent < 0 || percent > 100) {\n                return;\n              }\n              Optional.from(notification).orThunk(() => Optional.from(openNotification).map(apply$1)).each(n => {\n                notification = n;\n                n.progressBar.value(percent);\n              });\n            };\n            handler(blobInfo, progress).then(success, err => {\n              failure(isString(err) ? { message: err } : err);\n            });\n          } catch (ex) {\n            resolve(handlerFailure(blobInfo, ex));\n          }\n        });\n      };\n      const isDefaultHandler = handler => handler === defaultHandler;\n      const pendingUploadBlobInfo = blobInfo => {\n        const blobUri = blobInfo.blobUri();\n        return new Promise(resolve => {\n          pendingPromises[blobUri] = pendingPromises[blobUri] || [];\n          pendingPromises[blobUri].push(resolve);\n        });\n      };\n      const uploadBlobs = (blobInfos, openNotification) => {\n        blobInfos = Tools.grep(blobInfos, blobInfo => !uploadStatus.isUploaded(blobInfo.blobUri()));\n        return Promise.all(Tools.map(blobInfos, blobInfo => uploadStatus.isPending(blobInfo.blobUri()) ? pendingUploadBlobInfo(blobInfo) : uploadBlobInfo(blobInfo, uploadHandler, openNotification)));\n      };\n      const upload = (blobInfos, openNotification) => !settings.url && isDefaultHandler(uploadHandler) ? noUpload() : uploadBlobs(blobInfos, openNotification);\n      return { upload };\n    };\n\n    const openNotification = editor => () => editor.notificationManager.open({\n      text: editor.translate('Image uploading...'),\n      type: 'info',\n      timeout: -1,\n      progressBar: true\n    });\n    const createUploader = (editor, uploadStatus) => Uploader(uploadStatus, {\n      url: getImageUploadUrl(editor),\n      basePath: getImageUploadBasePath(editor),\n      credentials: getImagesUploadCredentials(editor),\n      handler: getImagesUploadHandler(editor)\n    });\n    const ImageUploader = editor => {\n      const uploadStatus = UploadStatus();\n      const uploader = createUploader(editor, uploadStatus);\n      return { upload: (blobInfos, showNotification = true) => uploader.upload(blobInfos, showNotification ? openNotification(editor) : undefined) };\n    };\n\n    const EditorUpload = editor => {\n      const blobCache = BlobCache();\n      let uploader, imageScanner;\n      const uploadStatus = UploadStatus();\n      const urlFilters = [];\n      const aliveGuard = callback => {\n        return result => {\n          if (editor.selection) {\n            return callback(result);\n          }\n          return [];\n        };\n      };\n      const cacheInvalidator = url => url + (url.indexOf('?') === -1 ? '?' : '&') + new Date().getTime();\n      const replaceString = (content, search, replace) => {\n        let index = 0;\n        do {\n          index = content.indexOf(search, index);\n          if (index !== -1) {\n            content = content.substring(0, index) + replace + content.substr(index + search.length);\n            index += replace.length - search.length + 1;\n          }\n        } while (index !== -1);\n        return content;\n      };\n      const replaceImageUrl = (content, targetUrl, replacementUrl) => {\n        const replacementString = `src=\"${ replacementUrl }\"${ replacementUrl === Env.transparentSrc ? ' data-mce-placeholder=\"1\"' : '' }`;\n        content = replaceString(content, `src=\"${ targetUrl }\"`, replacementString);\n        content = replaceString(content, 'data-mce-src=\"' + targetUrl + '\"', 'data-mce-src=\"' + replacementUrl + '\"');\n        return content;\n      };\n      const replaceUrlInUndoStack = (targetUrl, replacementUrl) => {\n        each$e(editor.undoManager.data, level => {\n          if (level.type === 'fragmented') {\n            level.fragments = map$3(level.fragments, fragment => replaceImageUrl(fragment, targetUrl, replacementUrl));\n          } else {\n            level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);\n          }\n        });\n      };\n      const replaceImageUriInView = (image, resultUri) => {\n        const src = editor.convertURL(resultUri, 'src');\n        replaceUrlInUndoStack(image.src, resultUri);\n        setAll$1(SugarElement.fromDom(image), {\n          'src': shouldReuseFileName(editor) ? cacheInvalidator(resultUri) : resultUri,\n          'data-mce-src': src\n        });\n      };\n      const uploadImages = () => {\n        if (!uploader) {\n          uploader = createUploader(editor, uploadStatus);\n        }\n        return scanForImages().then(aliveGuard(imageInfos => {\n          const blobInfos = map$3(imageInfos, imageInfo => imageInfo.blobInfo);\n          return uploader.upload(blobInfos, openNotification(editor)).then(aliveGuard(result => {\n            const imagesToRemove = [];\n            let shouldDispatchChange = false;\n            const filteredResult = map$3(result, (uploadInfo, index) => {\n              const {blobInfo, image} = imageInfos[index];\n              let removed = false;\n              if (uploadInfo.status && shouldReplaceBlobUris(editor)) {\n                if (uploadInfo.url && !contains$1(image.src, uploadInfo.url)) {\n                  shouldDispatchChange = true;\n                }\n                blobCache.removeByUri(image.src);\n                if (isRtc(editor)) ; else {\n                  replaceImageUriInView(image, uploadInfo.url);\n                }\n              } else if (uploadInfo.error) {\n                if (uploadInfo.error.remove) {\n                  replaceUrlInUndoStack(image.src, Env.transparentSrc);\n                  imagesToRemove.push(image);\n                  removed = true;\n                }\n                uploadError(editor, uploadInfo.error.message);\n              }\n              return {\n                element: image,\n                status: uploadInfo.status,\n                uploadUri: uploadInfo.url,\n                blobInfo,\n                removed\n              };\n            });\n            if (imagesToRemove.length > 0 && !isRtc(editor)) {\n              editor.undoManager.transact(() => {\n                each$e(imagesToRemove, element => {\n                  editor.dom.remove(element);\n                  blobCache.removeByUri(element.src);\n                });\n              });\n            } else if (shouldDispatchChange) {\n              editor.undoManager.dispatchChange();\n            }\n            return filteredResult;\n          }));\n        }));\n      };\n      const uploadImagesAuto = () => isAutomaticUploadsEnabled(editor) ? uploadImages() : Promise.resolve([]);\n      const isValidDataUriImage = imgElm => forall(urlFilters, filter => filter(imgElm));\n      const addFilter = filter => {\n        urlFilters.push(filter);\n      };\n      const scanForImages = () => {\n        if (!imageScanner) {\n          imageScanner = ImageScanner(uploadStatus, blobCache);\n        }\n        return imageScanner.findAll(editor.getBody(), isValidDataUriImage).then(aliveGuard(result => {\n          const filteredResult = filter$5(result, resultItem => {\n            if (isString(resultItem)) {\n              displayError(editor, resultItem);\n              return false;\n            } else {\n              return true;\n            }\n          });\n          if (isRtc(editor)) ; else {\n            each$e(filteredResult, resultItem => {\n              replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri());\n              resultItem.image.src = resultItem.blobInfo.blobUri();\n              resultItem.image.removeAttribute('data-mce-src');\n            });\n          }\n          return filteredResult;\n        }));\n      };\n      const destroy = () => {\n        blobCache.destroy();\n        uploadStatus.destroy();\n        imageScanner = uploader = null;\n      };\n      const replaceBlobUris = content => {\n        return content.replace(/src=\"(blob:[^\"]+)\"/g, (match, blobUri) => {\n          const resultUri = uploadStatus.getResultUri(blobUri);\n          if (resultUri) {\n            return 'src=\"' + resultUri + '\"';\n          }\n          let blobInfo = blobCache.getByUri(blobUri);\n          if (!blobInfo) {\n            blobInfo = foldl(editor.editorManager.get(), (result, editor) => {\n              return result || editor.editorUpload && editor.editorUpload.blobCache.getByUri(blobUri);\n            }, undefined);\n          }\n          if (blobInfo) {\n            const blob = blobInfo.blob();\n            return 'src=\"data:' + blob.type + ';base64,' + blobInfo.base64() + '\"';\n          }\n          return match;\n        });\n      };\n      editor.on('SetContent', () => {\n        if (isAutomaticUploadsEnabled(editor)) {\n          uploadImagesAuto();\n        } else {\n          scanForImages();\n        }\n      });\n      editor.on('RawSaveContent', e => {\n        e.content = replaceBlobUris(e.content);\n      });\n      editor.on('GetContent', e => {\n        if (e.source_view || e.format === 'raw' || e.format === 'tree') {\n          return;\n        }\n        e.content = replaceBlobUris(e.content);\n      });\n      editor.on('PostRender', () => {\n        editor.parser.addNodeFilter('img', images => {\n          each$e(images, img => {\n            const src = img.attr('src');\n            if (!src || blobCache.getByUri(src)) {\n              return;\n            }\n            const resultUri = uploadStatus.getResultUri(src);\n            if (resultUri) {\n              img.attr('src', resultUri);\n            }\n          });\n        });\n      });\n      return {\n        blobCache,\n        addFilter,\n        uploadImages,\n        uploadImagesAuto,\n        scanForImages,\n        destroy\n      };\n    };\n\n    const get$1 = editor => {\n      const dom = editor.dom;\n      const schemaType = editor.schema.type;\n      const formats = {\n        valigntop: [{\n            selector: 'td,th',\n            styles: { verticalAlign: 'top' }\n          }],\n        valignmiddle: [{\n            selector: 'td,th',\n            styles: { verticalAlign: 'middle' }\n          }],\n        valignbottom: [{\n            selector: 'td,th',\n            styles: { verticalAlign: 'bottom' }\n          }],\n        alignleft: [\n          {\n            selector: 'figure.image',\n            collapsed: false,\n            classes: 'align-left',\n            ceFalseOverride: true,\n            preview: 'font-family font-size'\n          },\n          {\n            selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre',\n            styles: { textAlign: 'left' },\n            inherit: false,\n            preview: false\n          },\n          {\n            selector: 'img,audio,video',\n            collapsed: false,\n            styles: { float: 'left' },\n            preview: 'font-family font-size'\n          },\n          {\n            selector: 'table',\n            collapsed: false,\n            styles: {\n              marginLeft: '0px',\n              marginRight: 'auto'\n            },\n            onformat: table => {\n              dom.setStyle(table, 'float', null);\n            },\n            preview: 'font-family font-size'\n          },\n          {\n            selector: '.mce-preview-object,[data-ephox-embed-iri]',\n            ceFalseOverride: true,\n            styles: { float: 'left' }\n          }\n        ],\n        aligncenter: [\n          {\n            selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre',\n            styles: { textAlign: 'center' },\n            inherit: false,\n            preview: 'font-family font-size'\n          },\n          {\n            selector: 'figure.image',\n            collapsed: false,\n            classes: 'align-center',\n            ceFalseOverride: true,\n            preview: 'font-family font-size'\n          },\n          {\n            selector: 'img,audio,video',\n            collapsed: false,\n            styles: {\n              display: 'block',\n              marginLeft: 'auto',\n              marginRight: 'auto'\n            },\n            preview: false\n          },\n          {\n            selector: 'table',\n            collapsed: false,\n            styles: {\n              marginLeft: 'auto',\n              marginRight: 'auto'\n            },\n            preview: 'font-family font-size'\n          },\n          {\n            selector: '.mce-preview-object',\n            ceFalseOverride: true,\n            styles: {\n              display: 'table',\n              marginLeft: 'auto',\n              marginRight: 'auto'\n            },\n            preview: false\n          },\n          {\n            selector: '[data-ephox-embed-iri]',\n            ceFalseOverride: true,\n            styles: {\n              marginLeft: 'auto',\n              marginRight: 'auto'\n            },\n            preview: false\n          }\n        ],\n        alignright: [\n          {\n            selector: 'figure.image',\n            collapsed: false,\n            classes: 'align-right',\n            ceFalseOverride: true,\n            preview: 'font-family font-size'\n          },\n          {\n            selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre',\n            styles: { textAlign: 'right' },\n            inherit: false,\n            preview: 'font-family font-size'\n          },\n          {\n            selector: 'img,audio,video',\n            collapsed: false,\n            styles: { float: 'right' },\n            preview: 'font-family font-size'\n          },\n          {\n            selector: 'table',\n            collapsed: false,\n            styles: {\n              marginRight: '0px',\n              marginLeft: 'auto'\n            },\n            onformat: table => {\n              dom.setStyle(table, 'float', null);\n            },\n            preview: 'font-family font-size'\n          },\n          {\n            selector: '.mce-preview-object,[data-ephox-embed-iri]',\n            ceFalseOverride: true,\n            styles: { float: 'right' },\n            preview: false\n          }\n        ],\n        alignjustify: [{\n            selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li,pre',\n            styles: { textAlign: 'justify' },\n            inherit: false,\n            preview: 'font-family font-size'\n          }],\n        bold: [\n          {\n            inline: 'strong',\n            remove: 'all',\n            preserve_attributes: [\n              'class',\n              'style'\n            ]\n          },\n          {\n            inline: 'span',\n            styles: { fontWeight: 'bold' }\n          },\n          {\n            inline: 'b',\n            remove: 'all',\n            preserve_attributes: [\n              'class',\n              'style'\n            ]\n          }\n        ],\n        italic: [\n          {\n            inline: 'em',\n            remove: 'all',\n            preserve_attributes: [\n              'class',\n              'style'\n            ]\n          },\n          {\n            inline: 'span',\n            styles: { fontStyle: 'italic' }\n          },\n          {\n            inline: 'i',\n            remove: 'all',\n            preserve_attributes: [\n              'class',\n              'style'\n            ]\n          }\n        ],\n        underline: [\n          {\n            inline: 'span',\n            styles: { textDecoration: 'underline' },\n            exact: true\n          },\n          {\n            inline: 'u',\n            remove: 'all',\n            preserve_attributes: [\n              'class',\n              'style'\n            ]\n          }\n        ],\n        strikethrough: (() => {\n          const span = {\n            inline: 'span',\n            styles: { textDecoration: 'line-through' },\n            exact: true\n          };\n          const strike = {\n            inline: 'strike',\n            remove: 'all',\n            preserve_attributes: [\n              'class',\n              'style'\n            ]\n          };\n          const s = {\n            inline: 's',\n            remove: 'all',\n            preserve_attributes: [\n              'class',\n              'style'\n            ]\n          };\n          return schemaType !== 'html4' ? [\n            s,\n            span,\n            strike\n          ] : [\n            span,\n            s,\n            strike\n          ];\n        })(),\n        forecolor: {\n          inline: 'span',\n          styles: { color: '%value' },\n          links: true,\n          remove_similar: true,\n          clear_child_styles: true\n        },\n        hilitecolor: {\n          inline: 'span',\n          styles: { backgroundColor: '%value' },\n          links: true,\n          remove_similar: true,\n          clear_child_styles: true\n        },\n        fontname: {\n          inline: 'span',\n          toggle: false,\n          styles: { fontFamily: '%value' },\n          clear_child_styles: true\n        },\n        fontsize: {\n          inline: 'span',\n          toggle: false,\n          styles: { fontSize: '%value' },\n          clear_child_styles: true\n        },\n        lineheight: {\n          selector: 'h1,h2,h3,h4,h5,h6,p,li,td,th,div',\n          styles: { lineHeight: '%value' }\n        },\n        fontsize_class: {\n          inline: 'span',\n          attributes: { class: '%value' }\n        },\n        blockquote: {\n          block: 'blockquote',\n          wrapper: true,\n          remove: 'all'\n        },\n        subscript: { inline: 'sub' },\n        superscript: { inline: 'sup' },\n        code: { inline: 'code' },\n        link: {\n          inline: 'a',\n          selector: 'a',\n          remove: 'all',\n          split: true,\n          deep: true,\n          onmatch: (node, _fmt, _itemName) => {\n            return isElement$6(node) && node.hasAttribute('href');\n          },\n          onformat: (elm, _fmt, vars) => {\n            Tools.each(vars, (value, key) => {\n              dom.setAttrib(elm, key, value);\n            });\n          }\n        },\n        lang: {\n          inline: 'span',\n          clear_child_styles: true,\n          remove_similar: true,\n          attributes: {\n            'lang': '%value',\n            'data-mce-lang': vars => {\n              var _a;\n              return (_a = vars === null || vars === void 0 ? void 0 : vars.customValue) !== null && _a !== void 0 ? _a : null;\n            }\n          }\n        },\n        removeformat: [\n          {\n            selector: 'b,strong,em,i,font,u,strike,s,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins,small',\n            remove: 'all',\n            split: true,\n            expand: false,\n            block_expand: true,\n            deep: true\n          },\n          {\n            selector: 'span',\n            attributes: [\n              'style',\n              'class'\n            ],\n            remove: 'empty',\n            split: true,\n            expand: false,\n            deep: true\n          },\n          {\n            selector: '*',\n            attributes: [\n              'style',\n              'class'\n            ],\n            split: false,\n            expand: false,\n            deep: true\n          }\n        ]\n      };\n      Tools.each('p h1 h2 h3 h4 h5 h6 div address pre dt dd samp'.split(/\\s/), name => {\n        formats[name] = {\n          block: name,\n          remove: 'all'\n        };\n      });\n      return formats;\n    };\n\n    const genericBase = {\n      remove_similar: true,\n      inherit: false\n    };\n    const cellBase = {\n      selector: 'td,th',\n      ...genericBase\n    };\n    const cellFormats = {\n      tablecellbackgroundcolor: {\n        styles: { backgroundColor: '%value' },\n        ...cellBase\n      },\n      tablecellverticalalign: {\n        styles: { 'vertical-align': '%value' },\n        ...cellBase\n      },\n      tablecellbordercolor: {\n        styles: { borderColor: '%value' },\n        ...cellBase\n      },\n      tablecellclass: {\n        classes: ['%value'],\n        ...cellBase\n      },\n      tableclass: {\n        selector: 'table',\n        classes: ['%value'],\n        ...genericBase\n      },\n      tablecellborderstyle: {\n        styles: { borderStyle: '%value' },\n        ...cellBase\n      },\n      tablecellborderwidth: {\n        styles: { borderWidth: '%value' },\n        ...cellBase\n      }\n    };\n    const get = constant(cellFormats);\n\n    const FormatRegistry = editor => {\n      const formats = {};\n      const get$2 = name => isNonNullable(name) ? formats[name] : formats;\n      const has = name => has$2(formats, name);\n      const register = (name, format) => {\n        if (name) {\n          if (!isString(name)) {\n            each$d(name, (format, name) => {\n              register(name, format);\n            });\n          } else {\n            if (!isArray$1(format)) {\n              format = [format];\n            }\n            each$e(format, format => {\n              if (isUndefined(format.deep)) {\n                format.deep = !isSelectorFormat(format);\n              }\n              if (isUndefined(format.split)) {\n                format.split = !isSelectorFormat(format) || isInlineFormat(format);\n              }\n              if (isUndefined(format.remove) && isSelectorFormat(format) && !isInlineFormat(format)) {\n                format.remove = 'none';\n              }\n              if (isSelectorFormat(format) && isInlineFormat(format)) {\n                format.mixed = true;\n                format.block_expand = true;\n              }\n              if (isString(format.classes)) {\n                format.classes = format.classes.split(/\\s+/);\n              }\n            });\n            formats[name] = format;\n          }\n        }\n      };\n      const unregister = name => {\n        if (name && formats[name]) {\n          delete formats[name];\n        }\n        return formats;\n      };\n      register(get$1(editor));\n      register(get());\n      register(getFormats(editor));\n      return {\n        get: get$2,\n        has,\n        register,\n        unregister\n      };\n    };\n\n    const each$3 = Tools.each;\n    const dom = DOMUtils.DOM;\n    const isPreviewItem = item => isNonNullable(item) && isObject(item);\n    const parsedSelectorToHtml = (ancestry, editor) => {\n      const schema = editor && editor.schema || Schema({});\n      const decorate = (elm, item) => {\n        if (item.classes.length > 0) {\n          dom.addClass(elm, item.classes.join(' '));\n        }\n        dom.setAttribs(elm, item.attrs);\n      };\n      const createElement = sItem => {\n        const item = isString(sItem) ? {\n          name: sItem,\n          classes: [],\n          attrs: {}\n        } : sItem;\n        const elm = dom.create(item.name);\n        decorate(elm, item);\n        return elm;\n      };\n      const getRequiredParent = (elm, candidate) => {\n        const elmRule = schema.getElementRule(elm.nodeName.toLowerCase());\n        const parentsRequired = elmRule === null || elmRule === void 0 ? void 0 : elmRule.parentsRequired;\n        if (parentsRequired && parentsRequired.length) {\n          return candidate && contains$2(parentsRequired, candidate) ? candidate : parentsRequired[0];\n        } else {\n          return false;\n        }\n      };\n      const wrapInHtml = (elm, ancestors, siblings) => {\n        let parentCandidate;\n        const ancestor = ancestors[0];\n        const ancestorName = isPreviewItem(ancestor) ? ancestor.name : undefined;\n        const parentRequired = getRequiredParent(elm, ancestorName);\n        if (parentRequired) {\n          if (ancestorName === parentRequired) {\n            parentCandidate = ancestor;\n            ancestors = ancestors.slice(1);\n          } else {\n            parentCandidate = parentRequired;\n          }\n        } else if (ancestor) {\n          parentCandidate = ancestor;\n          ancestors = ancestors.slice(1);\n        } else if (!siblings) {\n          return elm;\n        }\n        const parent = parentCandidate ? createElement(parentCandidate) : dom.create('div');\n        parent.appendChild(elm);\n        if (siblings) {\n          Tools.each(siblings, sibling => {\n            const siblingElm = createElement(sibling);\n            parent.insertBefore(siblingElm, elm);\n          });\n        }\n        const parentSiblings = isPreviewItem(parentCandidate) ? parentCandidate.siblings : undefined;\n        return wrapInHtml(parent, ancestors, parentSiblings);\n      };\n      const fragment = dom.create('div');\n      if (ancestry.length > 0) {\n        const item = ancestry[0];\n        const elm = createElement(item);\n        const siblings = isPreviewItem(item) ? item.siblings : undefined;\n        fragment.appendChild(wrapInHtml(elm, ancestry.slice(1), siblings));\n      }\n      return fragment;\n    };\n    const parseSelectorItem = item => {\n      item = Tools.trim(item);\n      let tagName = 'div';\n      const obj = {\n        name: tagName,\n        classes: [],\n        attrs: {},\n        selector: item\n      };\n      if (item !== '*') {\n        tagName = item.replace(/(?:([#\\.]|::?)([\\w\\-]+)|(\\[)([^\\]]+)\\]?)/g, ($0, $1, $2, $3, $4) => {\n          switch ($1) {\n          case '#':\n            obj.attrs.id = $2;\n            break;\n          case '.':\n            obj.classes.push($2);\n            break;\n          case ':':\n            if (Tools.inArray('checked disabled enabled read-only required'.split(' '), $2) !== -1) {\n              obj.attrs[$2] = $2;\n            }\n            break;\n          }\n          if ($3 === '[') {\n            const m = $4.match(/([\\w\\-]+)(?:\\=\\\"([^\\\"]+))?/);\n            if (m) {\n              obj.attrs[m[1]] = m[2];\n            }\n          }\n          return '';\n        });\n      }\n      obj.name = tagName || 'div';\n      return obj;\n    };\n    const parseSelector = selector => {\n      if (!isString(selector)) {\n        return [];\n      }\n      selector = selector.split(/\\s*,\\s*/)[0];\n      selector = selector.replace(/\\s*(~\\+|~|\\+|>)\\s*/g, '$1');\n      return Tools.map(selector.split(/(?:>|\\s+(?![^\\[\\]]+\\]))/), item => {\n        const siblings = Tools.map(item.split(/(?:~\\+|~|\\+)/), parseSelectorItem);\n        const obj = siblings.pop();\n        if (siblings.length) {\n          obj.siblings = siblings;\n        }\n        return obj;\n      }).reverse();\n    };\n    const getCssText = (editor, format) => {\n      let previewCss = '';\n      let previewStyles = getPreviewStyles(editor);\n      if (previewStyles === '') {\n        return '';\n      }\n      const removeVars = val => {\n        return isString(val) ? val.replace(/%(\\w+)/g, '') : '';\n      };\n      const getComputedStyle = (name, elm) => {\n        return dom.getStyle(elm !== null && elm !== void 0 ? elm : editor.getBody(), name, true);\n      };\n      if (isString(format)) {\n        const formats = editor.formatter.get(format);\n        if (!formats) {\n          return '';\n        }\n        format = formats[0];\n      }\n      if ('preview' in format) {\n        const preview = format.preview;\n        if (preview === false) {\n          return '';\n        } else {\n          previewStyles = preview || previewStyles;\n        }\n      }\n      let name = format.block || format.inline || 'span';\n      let previewFrag;\n      const items = parseSelector(format.selector);\n      if (items.length > 0) {\n        if (!items[0].name) {\n          items[0].name = name;\n        }\n        name = format.selector;\n        previewFrag = parsedSelectorToHtml(items, editor);\n      } else {\n        previewFrag = parsedSelectorToHtml([name], editor);\n      }\n      const previewElm = dom.select(name, previewFrag)[0] || previewFrag.firstChild;\n      each$3(format.styles, (value, name) => {\n        const newValue = removeVars(value);\n        if (newValue) {\n          dom.setStyle(previewElm, name, newValue);\n        }\n      });\n      each$3(format.attributes, (value, name) => {\n        const newValue = removeVars(value);\n        if (newValue) {\n          dom.setAttrib(previewElm, name, newValue);\n        }\n      });\n      each$3(format.classes, value => {\n        const newValue = removeVars(value);\n        if (!dom.hasClass(previewElm, newValue)) {\n          dom.addClass(previewElm, newValue);\n        }\n      });\n      editor.dispatch('PreviewFormats');\n      dom.setStyles(previewFrag, {\n        position: 'absolute',\n        left: -65535\n      });\n      editor.getBody().appendChild(previewFrag);\n      const rawParentFontSize = getComputedStyle('fontSize');\n      const parentFontSize = /px$/.test(rawParentFontSize) ? parseInt(rawParentFontSize, 10) : 0;\n      each$3(previewStyles.split(' '), name => {\n        let value = getComputedStyle(name, previewElm);\n        if (name === 'background-color' && /transparent|rgba\\s*\\([^)]+,\\s*0\\)/.test(value)) {\n          value = getComputedStyle(name);\n          if (rgbaToHexString(value).toLowerCase() === '#ffffff') {\n            return;\n          }\n        }\n        if (name === 'color') {\n          if (rgbaToHexString(value).toLowerCase() === '#000000') {\n            return;\n          }\n        }\n        if (name === 'font-size') {\n          if (/em|%$/.test(value)) {\n            if (parentFontSize === 0) {\n              return;\n            }\n            const numValue = parseFloat(value) / (/%$/.test(value) ? 100 : 1);\n            value = numValue * parentFontSize + 'px';\n          }\n        }\n        if (name === 'border' && value) {\n          previewCss += 'padding:0 2px;';\n        }\n        previewCss += name + ':' + value + ';';\n      });\n      editor.dispatch('AfterPreviewFormats');\n      dom.remove(previewFrag);\n      return previewCss;\n    };\n\n    const setup$r = editor => {\n      editor.addShortcut('meta+b', '', 'Bold');\n      editor.addShortcut('meta+i', '', 'Italic');\n      editor.addShortcut('meta+u', '', 'Underline');\n      for (let i = 1; i <= 6; i++) {\n        editor.addShortcut('access+' + i, '', [\n          'FormatBlock',\n          false,\n          'h' + i\n        ]);\n      }\n      editor.addShortcut('access+7', '', [\n        'FormatBlock',\n        false,\n        'p'\n      ]);\n      editor.addShortcut('access+8', '', [\n        'FormatBlock',\n        false,\n        'div'\n      ]);\n      editor.addShortcut('access+9', '', [\n        'FormatBlock',\n        false,\n        'address'\n      ]);\n    };\n\n    const Formatter = editor => {\n      const formats = FormatRegistry(editor);\n      const formatChangeState = Cell({});\n      setup$r(editor);\n      setup$u(editor);\n      if (!isRtc(editor)) {\n        setup$t(formatChangeState, editor);\n      }\n      return {\n        get: formats.get,\n        has: formats.has,\n        register: formats.register,\n        unregister: formats.unregister,\n        apply: (name, vars, node) => {\n          applyFormat(editor, name, vars, node);\n        },\n        remove: (name, vars, node, similar) => {\n          removeFormat(editor, name, vars, node, similar);\n        },\n        toggle: (name, vars, node) => {\n          toggleFormat(editor, name, vars, node);\n        },\n        match: (name, vars, node, similar) => matchFormat(editor, name, vars, node, similar),\n        closest: names => closestFormat(editor, names),\n        matchAll: (names, vars) => matchAllFormats(editor, names, vars),\n        matchNode: (node, name, vars, similar) => matchNodeFormat(editor, node, name, vars, similar),\n        canApply: name => canApplyFormat(editor, name),\n        formatChanged: (formats, callback, similar, vars) => formatChanged(editor, formatChangeState, formats, callback, similar, vars),\n        getCssText: curry(getCssText, editor)\n      };\n    };\n\n    const shouldIgnoreCommand = cmd => {\n      switch (cmd.toLowerCase()) {\n      case 'undo':\n      case 'redo':\n      case 'mcefocus':\n        return true;\n      default:\n        return false;\n      }\n    };\n    const registerEvents = (editor, undoManager, locks) => {\n      const isFirstTypedCharacter = Cell(false);\n      const addNonTypingUndoLevel = e => {\n        setTyping(undoManager, false, locks);\n        undoManager.add({}, e);\n      };\n      editor.on('init', () => {\n        undoManager.add();\n      });\n      editor.on('BeforeExecCommand', e => {\n        const cmd = e.command;\n        if (!shouldIgnoreCommand(cmd)) {\n          endTyping(undoManager, locks);\n          undoManager.beforeChange();\n        }\n      });\n      editor.on('ExecCommand', e => {\n        const cmd = e.command;\n        if (!shouldIgnoreCommand(cmd)) {\n          addNonTypingUndoLevel(e);\n        }\n      });\n      editor.on('ObjectResizeStart cut', () => {\n        undoManager.beforeChange();\n      });\n      editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel);\n      editor.on('dragend', addNonTypingUndoLevel);\n      editor.on('keyup', e => {\n        const keyCode = e.keyCode;\n        if (e.isDefaultPrevented()) {\n          return;\n        }\n        if (keyCode >= 33 && keyCode <= 36 || keyCode >= 37 && keyCode <= 40 || keyCode === 45 || e.ctrlKey) {\n          addNonTypingUndoLevel();\n          editor.nodeChanged();\n        }\n        if (keyCode === 46 || keyCode === 8) {\n          editor.nodeChanged();\n        }\n        if (isFirstTypedCharacter.get() && undoManager.typing && !isEq$1(createFromEditor(editor), undoManager.data[0])) {\n          if (!editor.isDirty()) {\n            editor.setDirty(true);\n          }\n          editor.dispatch('TypingUndo');\n          isFirstTypedCharacter.set(false);\n          editor.nodeChanged();\n        }\n      });\n      editor.on('keydown', e => {\n        const keyCode = e.keyCode;\n        if (e.isDefaultPrevented()) {\n          return;\n        }\n        if (keyCode >= 33 && keyCode <= 36 || keyCode >= 37 && keyCode <= 40 || keyCode === 45) {\n          if (undoManager.typing) {\n            addNonTypingUndoLevel(e);\n          }\n          return;\n        }\n        const modKey = e.ctrlKey && !e.altKey || e.metaKey;\n        if ((keyCode < 16 || keyCode > 20) && keyCode !== 224 && keyCode !== 91 && !undoManager.typing && !modKey) {\n          undoManager.beforeChange();\n          setTyping(undoManager, true, locks);\n          undoManager.add({}, e);\n          isFirstTypedCharacter.set(true);\n        }\n      });\n      editor.on('mousedown', e => {\n        if (undoManager.typing) {\n          addNonTypingUndoLevel(e);\n        }\n      });\n      const isInsertReplacementText = event => event.inputType === 'insertReplacementText';\n      const isInsertTextDataNull = event => event.inputType === 'insertText' && event.data === null;\n      const isInsertFromPasteOrDrop = event => event.inputType === 'insertFromPaste' || event.inputType === 'insertFromDrop';\n      editor.on('input', e => {\n        if (e.inputType && (isInsertReplacementText(e) || isInsertTextDataNull(e) || isInsertFromPasteOrDrop(e))) {\n          addNonTypingUndoLevel(e);\n        }\n      });\n      editor.on('AddUndo Undo Redo ClearUndos', e => {\n        if (!e.isDefaultPrevented()) {\n          editor.nodeChanged();\n        }\n      });\n    };\n    const addKeyboardShortcuts = editor => {\n      editor.addShortcut('meta+z', '', 'Undo');\n      editor.addShortcut('meta+y,meta+shift+z', '', 'Redo');\n    };\n\n    const UndoManager = editor => {\n      const beforeBookmark = value$2();\n      const locks = Cell(0);\n      const index = Cell(0);\n      const undoManager = {\n        data: [],\n        typing: false,\n        beforeChange: () => {\n          beforeChange(editor, locks, beforeBookmark);\n        },\n        add: (level, event) => {\n          return addUndoLevel(editor, undoManager, index, locks, beforeBookmark, level, event);\n        },\n        dispatchChange: () => {\n          editor.setDirty(true);\n          const level = createFromEditor(editor);\n          level.bookmark = getUndoBookmark(editor.selection);\n          editor.dispatch('change', {\n            level,\n            lastLevel: get$b(undoManager.data, index.get()).getOrUndefined()\n          });\n        },\n        undo: () => {\n          return undo(editor, undoManager, locks, index);\n        },\n        redo: () => {\n          return redo(editor, index, undoManager.data);\n        },\n        clear: () => {\n          clear(editor, undoManager, index);\n        },\n        reset: () => {\n          reset(editor, undoManager);\n        },\n        hasUndo: () => {\n          return hasUndo(editor, undoManager, index);\n        },\n        hasRedo: () => {\n          return hasRedo(editor, undoManager, index);\n        },\n        transact: callback => {\n          return transact(editor, undoManager, locks, callback);\n        },\n        ignore: callback => {\n          ignore(editor, locks, callback);\n        },\n        extra: (callback1, callback2) => {\n          extra(editor, undoManager, index, callback1, callback2);\n        }\n      };\n      if (!isRtc(editor)) {\n        registerEvents(editor, undoManager, locks);\n      }\n      addKeyboardShortcuts(editor);\n      return undoManager;\n    };\n\n    const nonTypingKeycodes = [\n      9,\n      27,\n      VK.HOME,\n      VK.END,\n      19,\n      20,\n      44,\n      144,\n      145,\n      33,\n      34,\n      45,\n      16,\n      17,\n      18,\n      91,\n      92,\n      93,\n      VK.DOWN,\n      VK.UP,\n      VK.LEFT,\n      VK.RIGHT\n    ].concat(Env.browser.isFirefox() ? [224] : []);\n    const placeholderAttr = 'data-mce-placeholder';\n    const isKeyboardEvent = e => e.type === 'keydown' || e.type === 'keyup';\n    const isDeleteEvent = e => {\n      const keyCode = e.keyCode;\n      return keyCode === VK.BACKSPACE || keyCode === VK.DELETE;\n    };\n    const isNonTypingKeyboardEvent = e => {\n      if (isKeyboardEvent(e)) {\n        const keyCode = e.keyCode;\n        return !isDeleteEvent(e) && (VK.metaKeyPressed(e) || e.altKey || keyCode >= 112 && keyCode <= 123 || contains$2(nonTypingKeycodes, keyCode));\n      } else {\n        return false;\n      }\n    };\n    const isTypingKeyboardEvent = e => isKeyboardEvent(e) && !(isDeleteEvent(e) || e.type === 'keyup' && e.keyCode === 229);\n    const isVisuallyEmpty = (dom, rootElm, forcedRootBlock) => {\n      if (isEmpty$2(SugarElement.fromDom(rootElm), false)) {\n        const firstElement = rootElm.firstElementChild;\n        if (!firstElement) {\n          return true;\n        } else if (dom.getStyle(rootElm.firstElementChild, 'padding-left') || dom.getStyle(rootElm.firstElementChild, 'padding-right')) {\n          return false;\n        } else {\n          return forcedRootBlock === firstElement.nodeName.toLowerCase();\n        }\n      } else {\n        return false;\n      }\n    };\n    const setup$q = editor => {\n      var _a;\n      const dom = editor.dom;\n      const rootBlock = getForcedRootBlock(editor);\n      const placeholder = (_a = getPlaceholder(editor)) !== null && _a !== void 0 ? _a : '';\n      const updatePlaceholder = (e, initial) => {\n        if (isNonTypingKeyboardEvent(e)) {\n          return;\n        }\n        const body = editor.getBody();\n        const showPlaceholder = isTypingKeyboardEvent(e) ? false : isVisuallyEmpty(dom, body, rootBlock);\n        const isPlaceholderShown = dom.getAttrib(body, placeholderAttr) !== '';\n        if (isPlaceholderShown !== showPlaceholder || initial) {\n          dom.setAttrib(body, placeholderAttr, showPlaceholder ? placeholder : null);\n          dom.setAttrib(body, 'aria-placeholder', showPlaceholder ? placeholder : null);\n          firePlaceholderToggle(editor, showPlaceholder);\n          editor.on(showPlaceholder ? 'keydown' : 'keyup', updatePlaceholder);\n          editor.off(showPlaceholder ? 'keyup' : 'keydown', updatePlaceholder);\n        }\n      };\n      if (isNotEmpty(placeholder)) {\n        editor.on('init', e => {\n          updatePlaceholder(e, true);\n          editor.on('change SetContent ExecCommand', updatePlaceholder);\n          editor.on('paste', e => Delay.setEditorTimeout(editor, () => updatePlaceholder(e)));\n        });\n      }\n    };\n\n    const blockPosition = (block, position) => ({\n      block,\n      position\n    });\n    const blockBoundary = (from, to) => ({\n      from,\n      to\n    });\n    const getBlockPosition = (rootNode, pos) => {\n      const rootElm = SugarElement.fromDom(rootNode);\n      const containerElm = SugarElement.fromDom(pos.container());\n      return getParentBlock$2(rootElm, containerElm).map(block => blockPosition(block, pos));\n    };\n    const isDifferentBlocks = blockBoundary => !eq(blockBoundary.from.block, blockBoundary.to.block);\n    const getClosestHost = (root, scope) => {\n      const isRoot = node => eq(node, root);\n      const isHost = node => isTableCell$2(node) || isContentEditableTrue$3(node.dom);\n      return closest$4(scope, isHost, isRoot).filter(isElement$7).getOr(root);\n    };\n    const hasSameHost = (rootNode, blockBoundary) => {\n      const root = SugarElement.fromDom(rootNode);\n      return eq(getClosestHost(root, blockBoundary.from.block), getClosestHost(root, blockBoundary.to.block));\n    };\n    const isEditable$2 = blockBoundary => isContentEditableFalse$a(blockBoundary.from.block.dom) === false && isContentEditableFalse$a(blockBoundary.to.block.dom) === false;\n    const hasValidBlocks = blockBoundary => {\n      const isValidBlock = block => isTextBlock$2(block) || hasBlockAttr(block.dom);\n      return isValidBlock(blockBoundary.from.block) && isValidBlock(blockBoundary.to.block);\n    };\n    const skipLastBr = (rootNode, forward, blockPosition) => {\n      if (isBr$6(blockPosition.position.getNode()) && !isEmpty$2(blockPosition.block)) {\n        return positionIn(false, blockPosition.block.dom).bind(lastPositionInBlock => {\n          if (lastPositionInBlock.isEqual(blockPosition.position)) {\n            return fromPosition(forward, rootNode, lastPositionInBlock).bind(to => getBlockPosition(rootNode, to));\n          } else {\n            return Optional.some(blockPosition);\n          }\n        }).getOr(blockPosition);\n      } else {\n        return blockPosition;\n      }\n    };\n    const readFromRange = (rootNode, forward, rng) => {\n      const fromBlockPos = getBlockPosition(rootNode, CaretPosition.fromRangeStart(rng));\n      const toBlockPos = fromBlockPos.bind(blockPos => fromPosition(forward, rootNode, blockPos.position).bind(to => getBlockPosition(rootNode, to).map(blockPos => skipLastBr(rootNode, forward, blockPos))));\n      return lift2(fromBlockPos, toBlockPos, blockBoundary).filter(blockBoundary => isDifferentBlocks(blockBoundary) && hasSameHost(rootNode, blockBoundary) && isEditable$2(blockBoundary) && hasValidBlocks(blockBoundary));\n    };\n    const read$1 = (rootNode, forward, rng) => rng.collapsed ? readFromRange(rootNode, forward, rng) : Optional.none();\n\n    const getChildrenUntilBlockBoundary = block => {\n      const children = children$1(block);\n      return findIndex$2(children, isBlock$2).fold(constant(children), index => children.slice(0, index));\n    };\n    const extractChildren = block => {\n      const children = getChildrenUntilBlockBoundary(block);\n      each$e(children, remove$6);\n      return children;\n    };\n    const removeEmptyRoot = (rootNode, block) => {\n      const parents = parentsAndSelf(block, rootNode);\n      return find$2(parents.reverse(), element => isEmpty$2(element)).each(remove$6);\n    };\n    const isEmptyBefore = el => filter$5(prevSiblings(el), el => !isEmpty$2(el)).length === 0;\n    const nestedBlockMerge = (rootNode, fromBlock, toBlock, insertionPoint) => {\n      if (isEmpty$2(toBlock)) {\n        fillWithPaddingBr(toBlock);\n        return firstPositionIn(toBlock.dom);\n      }\n      if (isEmptyBefore(insertionPoint) && isEmpty$2(fromBlock)) {\n        before$3(insertionPoint, SugarElement.fromTag('br'));\n      }\n      const position = prevPosition(toBlock.dom, CaretPosition.before(insertionPoint.dom));\n      each$e(extractChildren(fromBlock), child => {\n        before$3(insertionPoint, child);\n      });\n      removeEmptyRoot(rootNode, fromBlock);\n      return position;\n    };\n    const sidelongBlockMerge = (rootNode, fromBlock, toBlock) => {\n      if (isEmpty$2(toBlock)) {\n        remove$6(toBlock);\n        if (isEmpty$2(fromBlock)) {\n          fillWithPaddingBr(fromBlock);\n        }\n        return firstPositionIn(fromBlock.dom);\n      }\n      const position = lastPositionIn(toBlock.dom);\n      each$e(extractChildren(fromBlock), child => {\n        append$1(toBlock, child);\n      });\n      removeEmptyRoot(rootNode, fromBlock);\n      return position;\n    };\n    const findInsertionPoint = (toBlock, block) => {\n      const parentsAndSelf$1 = parentsAndSelf(block, toBlock);\n      return Optional.from(parentsAndSelf$1[parentsAndSelf$1.length - 1]);\n    };\n    const getInsertionPoint = (fromBlock, toBlock) => contains(toBlock, fromBlock) ? findInsertionPoint(toBlock, fromBlock) : Optional.none();\n    const trimBr = (first, block) => {\n      positionIn(first, block.dom).bind(position => Optional.from(position.getNode())).map(SugarElement.fromDom).filter(isBr$5).each(remove$6);\n    };\n    const mergeBlockInto = (rootNode, fromBlock, toBlock) => {\n      trimBr(true, fromBlock);\n      trimBr(false, toBlock);\n      return getInsertionPoint(fromBlock, toBlock).fold(curry(sidelongBlockMerge, rootNode, fromBlock, toBlock), curry(nestedBlockMerge, rootNode, fromBlock, toBlock));\n    };\n    const mergeBlocks = (rootNode, forward, block1, block2) => forward ? mergeBlockInto(rootNode, block2, block1) : mergeBlockInto(rootNode, block1, block2);\n\n    const backspaceDelete$8 = (editor, forward) => {\n      const rootNode = SugarElement.fromDom(editor.getBody());\n      const position = read$1(rootNode.dom, forward, editor.selection.getRng()).map(blockBoundary => () => {\n        mergeBlocks(rootNode, forward, blockBoundary.from.block, blockBoundary.to.block).each(pos => {\n          editor.selection.setRng(pos.toRange());\n        });\n      });\n      return position;\n    };\n\n    const deleteRangeMergeBlocks = (rootNode, selection) => {\n      const rng = selection.getRng();\n      return lift2(getParentBlock$2(rootNode, SugarElement.fromDom(rng.startContainer)), getParentBlock$2(rootNode, SugarElement.fromDom(rng.endContainer)), (block1, block2) => {\n        if (!eq(block1, block2)) {\n          return Optional.some(() => {\n            rng.deleteContents();\n            mergeBlocks(rootNode, true, block1, block2).each(pos => {\n              selection.setRng(pos.toRange());\n            });\n          });\n        } else {\n          return Optional.none();\n        }\n      }).getOr(Optional.none());\n    };\n    const isRawNodeInTable = (root, rawNode) => {\n      const node = SugarElement.fromDom(rawNode);\n      const isRoot = curry(eq, root);\n      return ancestor$3(node, isTableCell$2, isRoot).isSome();\n    };\n    const isSelectionInTable = (root, rng) => isRawNodeInTable(root, rng.startContainer) || isRawNodeInTable(root, rng.endContainer);\n    const isEverythingSelected = (root, rng) => {\n      const noPrevious = prevPosition(root.dom, CaretPosition.fromRangeStart(rng)).isNone();\n      const noNext = nextPosition(root.dom, CaretPosition.fromRangeEnd(rng)).isNone();\n      return !isSelectionInTable(root, rng) && noPrevious && noNext;\n    };\n    const emptyEditor = editor => {\n      return Optional.some(() => {\n        editor.setContent('');\n        editor.selection.setCursorLocation();\n      });\n    };\n    const deleteRange$1 = editor => {\n      const rootNode = SugarElement.fromDom(editor.getBody());\n      const rng = editor.selection.getRng();\n      return isEverythingSelected(rootNode, rng) ? emptyEditor(editor) : deleteRangeMergeBlocks(rootNode, editor.selection);\n    };\n    const backspaceDelete$7 = (editor, _forward) => editor.selection.isCollapsed() ? Optional.none() : deleteRange$1(editor);\n\n    const showCaret = (direction, editor, node, before, scrollIntoView) => Optional.from(editor._selectionOverrides.showCaret(direction, node, before, scrollIntoView));\n    const getNodeRange = node => {\n      const rng = node.ownerDocument.createRange();\n      rng.selectNode(node);\n      return rng;\n    };\n    const selectNode = (editor, node) => {\n      const e = editor.dispatch('BeforeObjectSelected', { target: node });\n      if (e.isDefaultPrevented()) {\n        return Optional.none();\n      }\n      return Optional.some(getNodeRange(node));\n    };\n    const renderCaretAtRange = (editor, range, scrollIntoView) => {\n      const normalizedRange = normalizeRange(1, editor.getBody(), range);\n      const caretPosition = CaretPosition.fromRangeStart(normalizedRange);\n      const caretPositionNode = caretPosition.getNode();\n      if (isInlineFakeCaretTarget(caretPositionNode)) {\n        return showCaret(1, editor, caretPositionNode, !caretPosition.isAtEnd(), false);\n      }\n      const caretPositionBeforeNode = caretPosition.getNode(true);\n      if (isInlineFakeCaretTarget(caretPositionBeforeNode)) {\n        return showCaret(1, editor, caretPositionBeforeNode, false, false);\n      }\n      const ceRoot = getContentEditableRoot$1(editor.dom.getRoot(), caretPosition.getNode());\n      if (isInlineFakeCaretTarget(ceRoot)) {\n        return showCaret(1, editor, ceRoot, false, scrollIntoView);\n      }\n      return Optional.none();\n    };\n    const renderRangeCaret = (editor, range, scrollIntoView) => range.collapsed ? renderCaretAtRange(editor, range, scrollIntoView).getOr(range) : range;\n\n    const isBeforeBoundary = pos => isBeforeContentEditableFalse(pos) || isBeforeMedia(pos);\n    const isAfterBoundary = pos => isAfterContentEditableFalse(pos) || isAfterMedia(pos);\n    const trimEmptyTextNode = (dom, node) => {\n      if (isText$a(node) && node.data.length === 0) {\n        dom.remove(node);\n      }\n    };\n    const deleteContentAndShowCaret = (editor, range, node, direction, forward, peekCaretPosition) => {\n      showCaret(direction, editor, peekCaretPosition.getNode(!forward), forward, true).each(caretRange => {\n        if (range.collapsed) {\n          const deleteRange = range.cloneRange();\n          if (forward) {\n            deleteRange.setEnd(caretRange.startContainer, caretRange.startOffset);\n          } else {\n            deleteRange.setStart(caretRange.endContainer, caretRange.endOffset);\n          }\n          deleteRange.deleteContents();\n        } else {\n          range.deleteContents();\n        }\n        editor.selection.setRng(caretRange);\n      });\n      trimEmptyTextNode(editor.dom, node);\n    };\n    const deleteBoundaryText = (editor, forward) => {\n      const range = editor.selection.getRng();\n      if (!isText$a(range.commonAncestorContainer)) {\n        return Optional.none();\n      }\n      const direction = forward ? HDirection.Forwards : HDirection.Backwards;\n      const caretWalker = CaretWalker(editor.getBody());\n      const getNextPosFn = curry(getVisualCaretPosition, forward ? caretWalker.next : caretWalker.prev);\n      const isBeforeFn = forward ? isBeforeBoundary : isAfterBoundary;\n      const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range);\n      const nextCaretPosition = getNextPosFn(caretPosition);\n      const normalizedNextCaretPosition = nextCaretPosition ? normalizePosition(forward, nextCaretPosition) : nextCaretPosition;\n      if (!normalizedNextCaretPosition || !isMoveInsideSameBlock(caretPosition, normalizedNextCaretPosition)) {\n        return Optional.none();\n      } else if (isBeforeFn(normalizedNextCaretPosition)) {\n        return Optional.some(() => deleteContentAndShowCaret(editor, range, caretPosition.getNode(), direction, forward, normalizedNextCaretPosition));\n      }\n      const peekCaretPosition = getNextPosFn(normalizedNextCaretPosition);\n      if (peekCaretPosition && isBeforeFn(peekCaretPosition)) {\n        if (isMoveInsideSameBlock(normalizedNextCaretPosition, peekCaretPosition)) {\n          return Optional.some(() => deleteContentAndShowCaret(editor, range, caretPosition.getNode(), direction, forward, peekCaretPosition));\n        }\n      }\n      return Optional.none();\n    };\n    const backspaceDelete$6 = (editor, forward) => deleteBoundaryText(editor, forward);\n\n    const getEdgeCefPosition = (editor, atStart) => {\n      const root = editor.getBody();\n      return atStart ? firstPositionIn(root).filter(isBeforeContentEditableFalse) : lastPositionIn(root).filter(isAfterContentEditableFalse);\n    };\n    const isCefAtEdgeSelected = editor => {\n      const rng = editor.selection.getRng();\n      return !rng.collapsed && (getEdgeCefPosition(editor, true).exists(pos => pos.isEqual(CaretPosition.fromRangeStart(rng))) || getEdgeCefPosition(editor, false).exists(pos => pos.isEqual(CaretPosition.fromRangeEnd(rng))));\n    };\n\n    const isCompoundElement = node => isNonNullable(node) && (isTableCell$2(SugarElement.fromDom(node)) || isListItem$1(SugarElement.fromDom(node)));\n    const DeleteAction = Adt.generate([\n      { remove: ['element'] },\n      { moveToElement: ['element'] },\n      { moveToPosition: ['position'] }\n    ]);\n    const isAtContentEditableBlockCaret = (forward, from) => {\n      const elm = from.getNode(!forward);\n      const caretLocation = forward ? 'after' : 'before';\n      return isElement$6(elm) && elm.getAttribute('data-mce-caret') === caretLocation;\n    };\n    const isDeleteFromCefDifferentBlocks = (root, forward, from, to) => {\n      const inSameBlock = elm => isInline$1(SugarElement.fromDom(elm)) && !isInSameBlock(from, to, root);\n      return getRelativeCefElm(!forward, from).fold(() => getRelativeCefElm(forward, to).fold(never, inSameBlock), inSameBlock);\n    };\n    const deleteEmptyBlockOrMoveToCef = (root, forward, from, to) => {\n      const toCefElm = to.getNode(!forward);\n      return getParentBlock$2(SugarElement.fromDom(root), SugarElement.fromDom(from.getNode())).map(blockElm => isEmpty$2(blockElm) ? DeleteAction.remove(blockElm.dom) : DeleteAction.moveToElement(toCefElm)).orThunk(() => Optional.some(DeleteAction.moveToElement(toCefElm)));\n    };\n    const findCefPosition = (root, forward, from) => fromPosition(forward, root, from).bind(to => {\n      if (isCompoundElement(to.getNode())) {\n        return Optional.none();\n      } else if (isDeleteFromCefDifferentBlocks(root, forward, from, to)) {\n        return Optional.none();\n      } else if (forward && isContentEditableFalse$a(to.getNode())) {\n        return deleteEmptyBlockOrMoveToCef(root, forward, from, to);\n      } else if (!forward && isContentEditableFalse$a(to.getNode(true))) {\n        return deleteEmptyBlockOrMoveToCef(root, forward, from, to);\n      } else if (forward && isAfterContentEditableFalse(from)) {\n        return Optional.some(DeleteAction.moveToPosition(to));\n      } else if (!forward && isBeforeContentEditableFalse(from)) {\n        return Optional.some(DeleteAction.moveToPosition(to));\n      } else {\n        return Optional.none();\n      }\n    });\n    const getContentEditableBlockAction = (forward, elm) => {\n      if (isNullable(elm)) {\n        return Optional.none();\n      } else if (forward && isContentEditableFalse$a(elm.nextSibling)) {\n        return Optional.some(DeleteAction.moveToElement(elm.nextSibling));\n      } else if (!forward && isContentEditableFalse$a(elm.previousSibling)) {\n        return Optional.some(DeleteAction.moveToElement(elm.previousSibling));\n      } else {\n        return Optional.none();\n      }\n    };\n    const skipMoveToActionFromInlineCefToContent = (root, from, deleteAction) => deleteAction.fold(elm => Optional.some(DeleteAction.remove(elm)), elm => Optional.some(DeleteAction.moveToElement(elm)), to => {\n      if (isInSameBlock(from, to, root)) {\n        return Optional.none();\n      } else {\n        return Optional.some(DeleteAction.moveToPosition(to));\n      }\n    });\n    const getContentEditableAction = (root, forward, from) => {\n      if (isAtContentEditableBlockCaret(forward, from)) {\n        return getContentEditableBlockAction(forward, from.getNode(!forward)).orThunk(() => findCefPosition(root, forward, from));\n      } else {\n        return findCefPosition(root, forward, from).bind(deleteAction => skipMoveToActionFromInlineCefToContent(root, from, deleteAction));\n      }\n    };\n    const read = (root, forward, rng) => {\n      const normalizedRange = normalizeRange(forward ? 1 : -1, root, rng);\n      const from = CaretPosition.fromRangeStart(normalizedRange);\n      const rootElement = SugarElement.fromDom(root);\n      if (!forward && isAfterContentEditableFalse(from)) {\n        return Optional.some(DeleteAction.remove(from.getNode(true)));\n      } else if (forward && isBeforeContentEditableFalse(from)) {\n        return Optional.some(DeleteAction.remove(from.getNode()));\n      } else if (!forward && isBeforeContentEditableFalse(from) && isAfterBr(rootElement, from)) {\n        return findPreviousBr(rootElement, from).map(br => DeleteAction.remove(br.getNode()));\n      } else if (forward && isAfterContentEditableFalse(from) && isBeforeBr$1(rootElement, from)) {\n        return findNextBr(rootElement, from).map(br => DeleteAction.remove(br.getNode()));\n      } else {\n        return getContentEditableAction(root, forward, from);\n      }\n    };\n\n    const deleteElement$1 = (editor, forward) => element => {\n      editor._selectionOverrides.hideFakeCaret();\n      deleteElement$2(editor, forward, SugarElement.fromDom(element));\n      return true;\n    };\n    const moveToElement = (editor, forward) => element => {\n      const pos = forward ? CaretPosition.before(element) : CaretPosition.after(element);\n      editor.selection.setRng(pos.toRange());\n      return true;\n    };\n    const moveToPosition = editor => pos => {\n      editor.selection.setRng(pos.toRange());\n      return true;\n    };\n    const getAncestorCe = (editor, node) => Optional.from(getContentEditableRoot$1(editor.getBody(), node));\n    const backspaceDeleteCaret = (editor, forward) => {\n      const selectedNode = editor.selection.getNode();\n      return getAncestorCe(editor, selectedNode).filter(isContentEditableFalse$a).fold(() => read(editor.getBody(), forward, editor.selection.getRng()).map(deleteAction => () => deleteAction.fold(deleteElement$1(editor, forward), moveToElement(editor, forward), moveToPosition(editor))), () => Optional.some(noop));\n    };\n    const deleteOffscreenSelection = rootElement => {\n      each$e(descendants(rootElement, '.mce-offscreen-selection'), remove$6);\n    };\n    const backspaceDeleteRange = (editor, forward) => {\n      const selectedNode = editor.selection.getNode();\n      if (isContentEditableFalse$a(selectedNode) && !isTableCell$3(selectedNode)) {\n        const hasCefAncestor = getAncestorCe(editor, selectedNode.parentNode).filter(isContentEditableFalse$a);\n        return hasCefAncestor.fold(() => Optional.some(() => {\n          deleteOffscreenSelection(SugarElement.fromDom(editor.getBody()));\n          deleteElement$2(editor, forward, SugarElement.fromDom(editor.selection.getNode()));\n          paddEmptyBody(editor);\n        }), () => Optional.some(noop));\n      }\n      if (isCefAtEdgeSelected(editor)) {\n        return Optional.some(() => {\n          deleteRangeContents(editor, editor.selection.getRng(), SugarElement.fromDom(editor.getBody()));\n        });\n      }\n      return Optional.none();\n    };\n    const paddEmptyElement = editor => {\n      const dom = editor.dom, selection = editor.selection;\n      const ceRoot = getContentEditableRoot$1(editor.getBody(), selection.getNode());\n      if (isContentEditableTrue$3(ceRoot) && dom.isBlock(ceRoot) && dom.isEmpty(ceRoot)) {\n        const br = dom.create('br', { 'data-mce-bogus': '1' });\n        dom.setHTML(ceRoot, '');\n        ceRoot.appendChild(br);\n        selection.setRng(CaretPosition.before(br).toRange());\n      }\n      return true;\n    };\n    const backspaceDelete$5 = (editor, forward) => {\n      if (editor.selection.isCollapsed()) {\n        return backspaceDeleteCaret(editor, forward);\n      } else {\n        return backspaceDeleteRange(editor, forward);\n      }\n    };\n\n    const deleteCaret$2 = (editor, forward) => {\n      const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng());\n      return fromPosition(forward, editor.getBody(), fromPos).filter(pos => forward ? isBeforeImageBlock(pos) : isAfterImageBlock(pos)).bind(pos => getChildNodeAtRelativeOffset(forward ? 0 : -1, pos)).map(elm => () => editor.selection.select(elm));\n    };\n    const backspaceDelete$4 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$2(editor, forward) : Optional.none();\n\n    const isText$2 = isText$a;\n    const startsWithCaretContainer = node => isText$2(node) && node.data[0] === ZWSP$1;\n    const endsWithCaretContainer = node => isText$2(node) && node.data[node.data.length - 1] === ZWSP$1;\n    const createZwsp = node => {\n      var _a;\n      const doc = (_a = node.ownerDocument) !== null && _a !== void 0 ? _a : document;\n      return doc.createTextNode(ZWSP$1);\n    };\n    const insertBefore = node => {\n      var _a;\n      if (isText$2(node.previousSibling)) {\n        if (endsWithCaretContainer(node.previousSibling)) {\n          return node.previousSibling;\n        } else {\n          node.previousSibling.appendData(ZWSP$1);\n          return node.previousSibling;\n        }\n      } else if (isText$2(node)) {\n        if (startsWithCaretContainer(node)) {\n          return node;\n        } else {\n          node.insertData(0, ZWSP$1);\n          return node;\n        }\n      } else {\n        const newNode = createZwsp(node);\n        (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(newNode, node);\n        return newNode;\n      }\n    };\n    const insertAfter = node => {\n      var _a, _b;\n      if (isText$2(node.nextSibling)) {\n        if (startsWithCaretContainer(node.nextSibling)) {\n          return node.nextSibling;\n        } else {\n          node.nextSibling.insertData(0, ZWSP$1);\n          return node.nextSibling;\n        }\n      } else if (isText$2(node)) {\n        if (endsWithCaretContainer(node)) {\n          return node;\n        } else {\n          node.appendData(ZWSP$1);\n          return node;\n        }\n      } else {\n        const newNode = createZwsp(node);\n        if (node.nextSibling) {\n          (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(newNode, node.nextSibling);\n        } else {\n          (_b = node.parentNode) === null || _b === void 0 ? void 0 : _b.appendChild(newNode);\n        }\n        return newNode;\n      }\n    };\n    const insertInline = (before, node) => before ? insertBefore(node) : insertAfter(node);\n    const insertInlineBefore = curry(insertInline, true);\n    const insertInlineAfter = curry(insertInline, false);\n\n    const insertInlinePos = (pos, before) => {\n      if (isText$a(pos.container())) {\n        return insertInline(before, pos.container());\n      } else {\n        return insertInline(before, pos.getNode());\n      }\n    };\n    const isPosCaretContainer = (pos, caret) => {\n      const caretNode = caret.get();\n      return caretNode && pos.container() === caretNode && isCaretContainerInline(caretNode);\n    };\n    const renderCaret = (caret, location) => location.fold(element => {\n      remove$4(caret.get());\n      const text = insertInlineBefore(element);\n      caret.set(text);\n      return Optional.some(CaretPosition(text, text.length - 1));\n    }, element => firstPositionIn(element).map(pos => {\n      if (!isPosCaretContainer(pos, caret)) {\n        remove$4(caret.get());\n        const text = insertInlinePos(pos, true);\n        caret.set(text);\n        return CaretPosition(text, 1);\n      } else {\n        const node = caret.get();\n        return CaretPosition(node, 1);\n      }\n    }), element => lastPositionIn(element).map(pos => {\n      if (!isPosCaretContainer(pos, caret)) {\n        remove$4(caret.get());\n        const text = insertInlinePos(pos, false);\n        caret.set(text);\n        return CaretPosition(text, text.length - 1);\n      } else {\n        const node = caret.get();\n        return CaretPosition(node, node.length - 1);\n      }\n    }), element => {\n      remove$4(caret.get());\n      const text = insertInlineAfter(element);\n      caret.set(text);\n      return Optional.some(CaretPosition(text, 1));\n    });\n\n    const evaluateUntil = (fns, args) => {\n      for (let i = 0; i < fns.length; i++) {\n        const result = fns[i].apply(null, args);\n        if (result.isSome()) {\n          return result;\n        }\n      }\n      return Optional.none();\n    };\n\n    const Location = Adt.generate([\n      { before: ['element'] },\n      { start: ['element'] },\n      { end: ['element'] },\n      { after: ['element'] }\n    ]);\n    const rescope$1 = (rootNode, node) => {\n      const parentBlock = getParentBlock$3(node, rootNode);\n      return parentBlock ? parentBlock : rootNode;\n    };\n    const before = (isInlineTarget, rootNode, pos) => {\n      const nPos = normalizeForwards(pos);\n      const scope = rescope$1(rootNode, nPos.container());\n      return findRootInline(isInlineTarget, scope, nPos).fold(() => nextPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget, scope)).map(inline => Location.before(inline)), Optional.none);\n    };\n    const isNotInsideFormatCaretContainer = (rootNode, elm) => getParentCaretContainer(rootNode, elm) === null;\n    const findInsideRootInline = (isInlineTarget, rootNode, pos) => findRootInline(isInlineTarget, rootNode, pos).filter(curry(isNotInsideFormatCaretContainer, rootNode));\n    const start$1 = (isInlineTarget, rootNode, pos) => {\n      const nPos = normalizeBackwards(pos);\n      return findInsideRootInline(isInlineTarget, rootNode, nPos).bind(inline => {\n        const prevPos = prevPosition(inline, nPos);\n        return prevPos.isNone() ? Optional.some(Location.start(inline)) : Optional.none();\n      });\n    };\n    const end = (isInlineTarget, rootNode, pos) => {\n      const nPos = normalizeForwards(pos);\n      return findInsideRootInline(isInlineTarget, rootNode, nPos).bind(inline => {\n        const nextPos = nextPosition(inline, nPos);\n        return nextPos.isNone() ? Optional.some(Location.end(inline)) : Optional.none();\n      });\n    };\n    const after = (isInlineTarget, rootNode, pos) => {\n      const nPos = normalizeBackwards(pos);\n      const scope = rescope$1(rootNode, nPos.container());\n      return findRootInline(isInlineTarget, scope, nPos).fold(() => prevPosition(scope, nPos).bind(curry(findRootInline, isInlineTarget, scope)).map(inline => Location.after(inline)), Optional.none);\n    };\n    const isValidLocation = location => !isRtl(getElement(location));\n    const readLocation = (isInlineTarget, rootNode, pos) => {\n      const location = evaluateUntil([\n        before,\n        start$1,\n        end,\n        after\n      ], [\n        isInlineTarget,\n        rootNode,\n        pos\n      ]);\n      return location.filter(isValidLocation);\n    };\n    const getElement = location => location.fold(identity, identity, identity, identity);\n    const getName = location => location.fold(constant('before'), constant('start'), constant('end'), constant('after'));\n    const outside = location => location.fold(Location.before, Location.before, Location.after, Location.after);\n    const inside = location => location.fold(Location.start, Location.start, Location.end, Location.end);\n    const isEq = (location1, location2) => getName(location1) === getName(location2) && getElement(location1) === getElement(location2);\n    const betweenInlines = (forward, isInlineTarget, rootNode, from, to, location) => lift2(findRootInline(isInlineTarget, rootNode, from), findRootInline(isInlineTarget, rootNode, to), (fromInline, toInline) => {\n      if (fromInline !== toInline && hasSameParentBlock(rootNode, fromInline, toInline)) {\n        return Location.after(forward ? fromInline : toInline);\n      } else {\n        return location;\n      }\n    }).getOr(location);\n    const skipNoMovement = (fromLocation, toLocation) => fromLocation.fold(always, fromLocation => !isEq(fromLocation, toLocation));\n    const findLocationTraverse = (forward, isInlineTarget, rootNode, fromLocation, pos) => {\n      const from = normalizePosition(forward, pos);\n      const to = fromPosition(forward, rootNode, from).map(curry(normalizePosition, forward));\n      const location = to.fold(() => fromLocation.map(outside), to => readLocation(isInlineTarget, rootNode, to).map(curry(betweenInlines, forward, isInlineTarget, rootNode, from, to)).filter(curry(skipNoMovement, fromLocation)));\n      return location.filter(isValidLocation);\n    };\n    const findLocationSimple = (forward, location) => {\n      if (forward) {\n        return location.fold(compose(Optional.some, Location.start), Optional.none, compose(Optional.some, Location.after), Optional.none);\n      } else {\n        return location.fold(Optional.none, compose(Optional.some, Location.before), Optional.none, compose(Optional.some, Location.end));\n      }\n    };\n    const findLocation$1 = (forward, isInlineTarget, rootNode, pos) => {\n      const from = normalizePosition(forward, pos);\n      const fromLocation = readLocation(isInlineTarget, rootNode, from);\n      return readLocation(isInlineTarget, rootNode, from).bind(curry(findLocationSimple, forward)).orThunk(() => findLocationTraverse(forward, isInlineTarget, rootNode, fromLocation, pos));\n    };\n\n    const hasSelectionModifyApi = editor => {\n      return isFunction(editor.selection.getSel().modify);\n    };\n    const moveRel = (forward, selection, pos) => {\n      const delta = forward ? 1 : -1;\n      selection.setRng(CaretPosition(pos.container(), pos.offset() + delta).toRange());\n      selection.getSel().modify('move', forward ? 'forward' : 'backward', 'word');\n      return true;\n    };\n    const moveByWord = (forward, editor) => {\n      const rng = editor.selection.getRng();\n      const pos = forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng);\n      if (!hasSelectionModifyApi(editor)) {\n        return false;\n      } else if (forward && isBeforeInline(pos)) {\n        return moveRel(true, editor.selection, pos);\n      } else if (!forward && isAfterInline(pos)) {\n        return moveRel(false, editor.selection, pos);\n      } else {\n        return false;\n      }\n    };\n\n    var BreakType;\n    (function (BreakType) {\n      BreakType[BreakType['Br'] = 0] = 'Br';\n      BreakType[BreakType['Block'] = 1] = 'Block';\n      BreakType[BreakType['Wrap'] = 2] = 'Wrap';\n      BreakType[BreakType['Eol'] = 3] = 'Eol';\n    }(BreakType || (BreakType = {})));\n    const flip = (direction, positions) => direction === HDirection.Backwards ? reverse(positions) : positions;\n    const walk$1 = (direction, caretWalker, pos) => direction === HDirection.Forwards ? caretWalker.next(pos) : caretWalker.prev(pos);\n    const getBreakType = (scope, direction, currentPos, nextPos) => {\n      if (isBr$6(nextPos.getNode(direction === HDirection.Forwards))) {\n        return BreakType.Br;\n      } else if (isInSameBlock(currentPos, nextPos) === false) {\n        return BreakType.Block;\n      } else {\n        return BreakType.Wrap;\n      }\n    };\n    const getPositionsUntil = (predicate, direction, scope, start) => {\n      const caretWalker = CaretWalker(scope);\n      let currentPos = start;\n      const positions = [];\n      while (currentPos) {\n        const nextPos = walk$1(direction, caretWalker, currentPos);\n        if (!nextPos) {\n          break;\n        }\n        if (isBr$6(nextPos.getNode(false))) {\n          if (direction === HDirection.Forwards) {\n            return {\n              positions: flip(direction, positions).concat([nextPos]),\n              breakType: BreakType.Br,\n              breakAt: Optional.some(nextPos)\n            };\n          } else {\n            return {\n              positions: flip(direction, positions),\n              breakType: BreakType.Br,\n              breakAt: Optional.some(nextPos)\n            };\n          }\n        }\n        if (!nextPos.isVisible()) {\n          currentPos = nextPos;\n          continue;\n        }\n        if (predicate(currentPos, nextPos)) {\n          const breakType = getBreakType(scope, direction, currentPos, nextPos);\n          return {\n            positions: flip(direction, positions),\n            breakType,\n            breakAt: Optional.some(nextPos)\n          };\n        }\n        positions.push(nextPos);\n        currentPos = nextPos;\n      }\n      return {\n        positions: flip(direction, positions),\n        breakType: BreakType.Eol,\n        breakAt: Optional.none()\n      };\n    };\n    const getAdjacentLinePositions = (direction, getPositionsUntilBreak, scope, start) => getPositionsUntilBreak(scope, start).breakAt.map(pos => {\n      const positions = getPositionsUntilBreak(scope, pos).positions;\n      return direction === HDirection.Backwards ? positions.concat(pos) : [pos].concat(positions);\n    }).getOr([]);\n    const findClosestHorizontalPositionFromPoint = (positions, x) => foldl(positions, (acc, newPos) => acc.fold(() => Optional.some(newPos), lastPos => lift2(head(lastPos.getClientRects()), head(newPos.getClientRects()), (lastRect, newRect) => {\n      const lastDist = Math.abs(x - lastRect.left);\n      const newDist = Math.abs(x - newRect.left);\n      return newDist <= lastDist ? newPos : lastPos;\n    }).or(acc)), Optional.none());\n    const findClosestHorizontalPosition = (positions, pos) => head(pos.getClientRects()).bind(targetRect => findClosestHorizontalPositionFromPoint(positions, targetRect.left));\n    const getPositionsUntilPreviousLine = curry(getPositionsUntil, CaretPosition.isAbove, -1);\n    const getPositionsUntilNextLine = curry(getPositionsUntil, CaretPosition.isBelow, 1);\n    const getPositionsAbove = curry(getAdjacentLinePositions, -1, getPositionsUntilPreviousLine);\n    const getPositionsBelow = curry(getAdjacentLinePositions, 1, getPositionsUntilNextLine);\n    const isAtFirstLine = (scope, pos) => getPositionsUntilPreviousLine(scope, pos).breakAt.isNone();\n    const isAtLastLine = (scope, pos) => getPositionsUntilNextLine(scope, pos).breakAt.isNone();\n    const getFirstLinePositions = scope => firstPositionIn(scope).map(pos => [pos].concat(getPositionsUntilNextLine(scope, pos).positions)).getOr([]);\n    const getLastLinePositions = scope => lastPositionIn(scope).map(pos => getPositionsUntilPreviousLine(scope, pos).positions.concat(pos)).getOr([]);\n    const getClosestPositionAbove = (scope, pos) => findClosestHorizontalPosition(getPositionsAbove(scope, pos), pos);\n    const getClosestPositionBelow = (scope, pos) => findClosestHorizontalPosition(getPositionsBelow(scope, pos), pos);\n\n    const isContentEditableFalse$4 = isContentEditableFalse$a;\n    const distanceToRectLeft$1 = (clientRect, clientX) => Math.abs(clientRect.left - clientX);\n    const distanceToRectRight$1 = (clientRect, clientX) => Math.abs(clientRect.right - clientX);\n    const isNodeClientRect = rect => hasNonNullableKey(rect, 'node');\n    const findClosestClientRect = (clientRects, clientX) => reduce(clientRects, (oldClientRect, clientRect) => {\n      const oldDistance = Math.min(distanceToRectLeft$1(oldClientRect, clientX), distanceToRectRight$1(oldClientRect, clientX));\n      const newDistance = Math.min(distanceToRectLeft$1(clientRect, clientX), distanceToRectRight$1(clientRect, clientX));\n      if (newDistance === oldDistance && isNodeClientRect(clientRect) && isContentEditableFalse$4(clientRect.node)) {\n        return clientRect;\n      }\n      if (newDistance < oldDistance) {\n        return clientRect;\n      }\n      return oldClientRect;\n    });\n\n    const getNodeClientRects = node => {\n      const toArrayWithNode = clientRects => {\n        return map$3(clientRects, rect => {\n          const clientRect = clone$1(rect);\n          clientRect.node = node;\n          return clientRect;\n        });\n      };\n      if (isElement$6(node)) {\n        return toArrayWithNode(node.getClientRects());\n      } else if (isText$a(node)) {\n        const rng = node.ownerDocument.createRange();\n        rng.setStart(node, 0);\n        rng.setEnd(node, node.data.length);\n        return toArrayWithNode(rng.getClientRects());\n      } else {\n        return [];\n      }\n    };\n    const getClientRects = nodes => bind$3(nodes, getNodeClientRects);\n\n    var VDirection;\n    (function (VDirection) {\n      VDirection[VDirection['Up'] = -1] = 'Up';\n      VDirection[VDirection['Down'] = 1] = 'Down';\n    }(VDirection || (VDirection = {})));\n    const findUntil = (direction, root, predicateFn, node) => {\n      let currentNode = node;\n      while (currentNode = findNode(currentNode, direction, isEditableCaretCandidate$1, root)) {\n        if (predicateFn(currentNode)) {\n          return;\n        }\n      }\n    };\n    const walkUntil = (direction, isAboveFn, isBeflowFn, root, predicateFn, caretPosition) => {\n      let line = 0;\n      const result = [];\n      const add = node => {\n        let clientRects = getClientRects([node]);\n        if (direction === -1) {\n          clientRects = clientRects.reverse();\n        }\n        for (let i = 0; i < clientRects.length; i++) {\n          const clientRect = clientRects[i];\n          if (isBeflowFn(clientRect, targetClientRect)) {\n            continue;\n          }\n          if (result.length > 0 && isAboveFn(clientRect, last$2(result))) {\n            line++;\n          }\n          clientRect.line = line;\n          if (predicateFn(clientRect)) {\n            return true;\n          }\n          result.push(clientRect);\n        }\n        return false;\n      };\n      const targetClientRect = last$2(caretPosition.getClientRects());\n      if (!targetClientRect) {\n        return result;\n      }\n      const node = caretPosition.getNode();\n      if (node) {\n        add(node);\n        findUntil(direction, root, add, node);\n      }\n      return result;\n    };\n    const aboveLineNumber = (lineNumber, clientRect) => clientRect.line > lineNumber;\n    const isLineNumber = (lineNumber, clientRect) => clientRect.line === lineNumber;\n    const upUntil = curry(walkUntil, VDirection.Up, isAbove$1, isBelow$1);\n    const downUntil = curry(walkUntil, VDirection.Down, isBelow$1, isAbove$1);\n    const getLastClientRect = caretPosition => {\n      return last$2(caretPosition.getClientRects());\n    };\n    const positionsUntil = (direction, root, predicateFn, node) => {\n      const caretWalker = CaretWalker(root);\n      let walkFn;\n      let isBelowFn;\n      let isAboveFn;\n      let caretPosition;\n      const result = [];\n      let line = 0;\n      if (direction === 1) {\n        walkFn = caretWalker.next;\n        isBelowFn = isBelow$1;\n        isAboveFn = isAbove$1;\n        caretPosition = CaretPosition.after(node);\n      } else {\n        walkFn = caretWalker.prev;\n        isBelowFn = isAbove$1;\n        isAboveFn = isBelow$1;\n        caretPosition = CaretPosition.before(node);\n      }\n      const targetClientRect = getLastClientRect(caretPosition);\n      do {\n        if (!caretPosition.isVisible()) {\n          continue;\n        }\n        const rect = getLastClientRect(caretPosition);\n        if (isAboveFn(rect, targetClientRect)) {\n          continue;\n        }\n        if (result.length > 0 && isBelowFn(rect, last$2(result))) {\n          line++;\n        }\n        const clientRect = clone$1(rect);\n        clientRect.position = caretPosition;\n        clientRect.line = line;\n        if (predicateFn(clientRect)) {\n          return result;\n        }\n        result.push(clientRect);\n      } while (caretPosition = walkFn(caretPosition));\n      return result;\n    };\n    const isAboveLine = lineNumber => clientRect => aboveLineNumber(lineNumber, clientRect);\n    const isLine = lineNumber => clientRect => isLineNumber(lineNumber, clientRect);\n\n    const moveToRange = (editor, rng) => {\n      editor.selection.setRng(rng);\n      scrollRangeIntoView(editor, editor.selection.getRng());\n    };\n    const renderRangeCaretOpt = (editor, range, scrollIntoView) => Optional.some(renderRangeCaret(editor, range, scrollIntoView));\n    const moveHorizontally = (editor, direction, range, isBefore, isAfter, isElement) => {\n      const forwards = direction === HDirection.Forwards;\n      const caretWalker = CaretWalker(editor.getBody());\n      const getNextPosFn = curry(getVisualCaretPosition, forwards ? caretWalker.next : caretWalker.prev);\n      const isBeforeFn = forwards ? isBefore : isAfter;\n      if (!range.collapsed) {\n        const node = getSelectedNode(range);\n        if (isElement(node)) {\n          return showCaret(direction, editor, node, direction === HDirection.Backwards, false);\n        } else if (isCefAtEdgeSelected(editor)) {\n          const newRange = range.cloneRange();\n          newRange.collapse(direction === HDirection.Backwards);\n          return Optional.from(newRange);\n        }\n      }\n      const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range);\n      if (isBeforeFn(caretPosition)) {\n        return selectNode(editor, caretPosition.getNode(!forwards));\n      }\n      let nextCaretPosition = getNextPosFn(caretPosition);\n      const rangeIsInContainerBlock = isRangeInCaretContainerBlock(range);\n      if (!nextCaretPosition) {\n        return rangeIsInContainerBlock ? Optional.some(range) : Optional.none();\n      } else {\n        nextCaretPosition = normalizePosition(forwards, nextCaretPosition);\n      }\n      if (isBeforeFn(nextCaretPosition)) {\n        return showCaret(direction, editor, nextCaretPosition.getNode(!forwards), forwards, false);\n      }\n      const peekCaretPosition = getNextPosFn(nextCaretPosition);\n      if (peekCaretPosition && isBeforeFn(peekCaretPosition)) {\n        if (isMoveInsideSameBlock(nextCaretPosition, peekCaretPosition)) {\n          return showCaret(direction, editor, peekCaretPosition.getNode(!forwards), forwards, false);\n        }\n      }\n      if (rangeIsInContainerBlock) {\n        return renderRangeCaretOpt(editor, nextCaretPosition.toRange(), false);\n      }\n      return Optional.none();\n    };\n    const moveVertically = (editor, direction, range, isBefore, isAfter, isElement) => {\n      const caretPosition = getNormalizedRangeEndPoint(direction, editor.getBody(), range);\n      const caretClientRect = last$2(caretPosition.getClientRects());\n      const forwards = direction === VDirection.Down;\n      const root = editor.getBody();\n      if (!caretClientRect) {\n        return Optional.none();\n      }\n      if (isCefAtEdgeSelected(editor)) {\n        const caretPosition = forwards ? CaretPosition.fromRangeEnd(range) : CaretPosition.fromRangeStart(range);\n        const getClosestFn = !forwards ? getClosestPositionAbove : getClosestPositionBelow;\n        return getClosestFn(root, caretPosition).orThunk(() => Optional.from(caretPosition)).map(pos => pos.toRange());\n      }\n      const walkerFn = forwards ? downUntil : upUntil;\n      const linePositions = walkerFn(root, isAboveLine(1), caretPosition);\n      const nextLinePositions = filter$5(linePositions, isLine(1));\n      const clientX = caretClientRect.left;\n      const nextLineRect = findClosestClientRect(nextLinePositions, clientX);\n      if (nextLineRect && isElement(nextLineRect.node)) {\n        const dist1 = Math.abs(clientX - nextLineRect.left);\n        const dist2 = Math.abs(clientX - nextLineRect.right);\n        return showCaret(direction, editor, nextLineRect.node, dist1 < dist2, false);\n      }\n      let currentNode;\n      if (isBefore(caretPosition)) {\n        currentNode = caretPosition.getNode();\n      } else if (isAfter(caretPosition)) {\n        currentNode = caretPosition.getNode(true);\n      } else {\n        currentNode = getSelectedNode(range);\n      }\n      if (currentNode) {\n        const caretPositions = positionsUntil(direction, root, isAboveLine(1), currentNode);\n        let closestNextLineRect = findClosestClientRect(filter$5(caretPositions, isLine(1)), clientX);\n        if (closestNextLineRect) {\n          return renderRangeCaretOpt(editor, closestNextLineRect.position.toRange(), false);\n        }\n        closestNextLineRect = last$2(filter$5(caretPositions, isLine(0)));\n        if (closestNextLineRect) {\n          return renderRangeCaretOpt(editor, closestNextLineRect.position.toRange(), false);\n        }\n      }\n      if (nextLinePositions.length === 0) {\n        return getLineEndPoint(editor, forwards).filter(forwards ? isAfter : isBefore).map(pos => renderRangeCaret(editor, pos.toRange(), false));\n      }\n      return Optional.none();\n    };\n    const getLineEndPoint = (editor, forward) => {\n      const rng = editor.selection.getRng();\n      const from = forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng);\n      const host = getEditingHost(from.container(), editor.getBody());\n      if (forward) {\n        const lineInfo = getPositionsUntilNextLine(host, from);\n        return last$3(lineInfo.positions);\n      } else {\n        const lineInfo = getPositionsUntilPreviousLine(host, from);\n        return head(lineInfo.positions);\n      }\n    };\n    const moveToLineEndPoint$3 = (editor, forward, isElementPosition) => getLineEndPoint(editor, forward).filter(isElementPosition).exists(pos => {\n      editor.selection.setRng(pos.toRange());\n      return true;\n    });\n\n    const setCaretPosition = (editor, pos) => {\n      const rng = editor.dom.createRng();\n      rng.setStart(pos.container(), pos.offset());\n      rng.setEnd(pos.container(), pos.offset());\n      editor.selection.setRng(rng);\n    };\n    const setSelected = (state, elm) => {\n      if (state) {\n        elm.setAttribute('data-mce-selected', 'inline-boundary');\n      } else {\n        elm.removeAttribute('data-mce-selected');\n      }\n    };\n    const renderCaretLocation = (editor, caret, location) => renderCaret(caret, location).map(pos => {\n      setCaretPosition(editor, pos);\n      return location;\n    });\n    const getPositionFromRange = (range, root, forward) => {\n      const start = CaretPosition.fromRangeStart(range);\n      if (range.collapsed) {\n        return start;\n      } else {\n        const end = CaretPosition.fromRangeEnd(range);\n        return forward ? prevPosition(root, end).getOr(end) : nextPosition(root, start).getOr(start);\n      }\n    };\n    const findLocation = (editor, caret, forward) => {\n      const rootNode = editor.getBody();\n      const from = getPositionFromRange(editor.selection.getRng(), rootNode, forward);\n      const isInlineTarget$1 = curry(isInlineTarget, editor);\n      const location = findLocation$1(forward, isInlineTarget$1, rootNode, from);\n      return location.bind(location => renderCaretLocation(editor, caret, location));\n    };\n    const toggleInlines = (isInlineTarget, dom, elms) => {\n      const inlineBoundaries = map$3(descendants(SugarElement.fromDom(dom.getRoot()), '*[data-mce-selected=\"inline-boundary\"]'), e => e.dom);\n      const selectedInlines = filter$5(inlineBoundaries, isInlineTarget);\n      const targetInlines = filter$5(elms, isInlineTarget);\n      each$e(difference(selectedInlines, targetInlines), curry(setSelected, false));\n      each$e(difference(targetInlines, selectedInlines), curry(setSelected, true));\n    };\n    const safeRemoveCaretContainer = (editor, caret) => {\n      const caretValue = caret.get();\n      if (editor.selection.isCollapsed() && !editor.composing && caretValue) {\n        const pos = CaretPosition.fromRangeStart(editor.selection.getRng());\n        if (CaretPosition.isTextPosition(pos) && !isAtZwsp(pos)) {\n          setCaretPosition(editor, removeAndReposition(caretValue, pos));\n          caret.set(null);\n        }\n      }\n    };\n    const renderInsideInlineCaret = (isInlineTarget, editor, caret, elms) => {\n      if (editor.selection.isCollapsed()) {\n        const inlines = filter$5(elms, isInlineTarget);\n        each$e(inlines, _inline => {\n          const pos = CaretPosition.fromRangeStart(editor.selection.getRng());\n          readLocation(isInlineTarget, editor.getBody(), pos).bind(location => renderCaretLocation(editor, caret, location));\n        });\n      }\n    };\n    const move$2 = (editor, caret, forward) => isInlineBoundariesEnabled(editor) ? findLocation(editor, caret, forward).isSome() : false;\n    const moveWord = (forward, editor, _caret) => isInlineBoundariesEnabled(editor) ? moveByWord(forward, editor) : false;\n    const setupSelectedState = editor => {\n      const caret = Cell(null);\n      const isInlineTarget$1 = curry(isInlineTarget, editor);\n      editor.on('NodeChange', e => {\n        if (isInlineBoundariesEnabled(editor)) {\n          toggleInlines(isInlineTarget$1, editor.dom, e.parents);\n          safeRemoveCaretContainer(editor, caret);\n          renderInsideInlineCaret(isInlineTarget$1, editor, caret, e.parents);\n        }\n      });\n      return caret;\n    };\n    const moveNextWord = curry(moveWord, true);\n    const movePrevWord = curry(moveWord, false);\n    const moveToLineEndPoint$2 = (editor, forward, caret) => {\n      if (isInlineBoundariesEnabled(editor)) {\n        const linePoint = getLineEndPoint(editor, forward).getOrThunk(() => {\n          const rng = editor.selection.getRng();\n          return forward ? CaretPosition.fromRangeEnd(rng) : CaretPosition.fromRangeStart(rng);\n        });\n        return readLocation(curry(isInlineTarget, editor), editor.getBody(), linePoint).exists(loc => {\n          const outsideLoc = outside(loc);\n          return renderCaret(caret, outsideLoc).exists(pos => {\n            setCaretPosition(editor, pos);\n            return true;\n          });\n        });\n      } else {\n        return false;\n      }\n    };\n\n    const rangeFromPositions = (from, to) => {\n      const range = document.createRange();\n      range.setStart(from.container(), from.offset());\n      range.setEnd(to.container(), to.offset());\n      return range;\n    };\n    const hasOnlyTwoOrLessPositionsLeft = elm => lift2(firstPositionIn(elm), lastPositionIn(elm), (firstPos, lastPos) => {\n      const normalizedFirstPos = normalizePosition(true, firstPos);\n      const normalizedLastPos = normalizePosition(false, lastPos);\n      return nextPosition(elm, normalizedFirstPos).forall(pos => pos.isEqual(normalizedLastPos));\n    }).getOr(true);\n    const setCaretLocation = (editor, caret) => location => renderCaret(caret, location).map(pos => () => setCaretPosition(editor, pos));\n    const deleteFromTo = (editor, caret, from, to) => {\n      const rootNode = editor.getBody();\n      const isInlineTarget$1 = curry(isInlineTarget, editor);\n      editor.undoManager.ignore(() => {\n        editor.selection.setRng(rangeFromPositions(from, to));\n        execNativeDeleteCommand(editor);\n        readLocation(isInlineTarget$1, rootNode, CaretPosition.fromRangeStart(editor.selection.getRng())).map(inside).bind(setCaretLocation(editor, caret)).each(call);\n      });\n      editor.nodeChanged();\n    };\n    const rescope = (rootNode, node) => {\n      const parentBlock = getParentBlock$3(node, rootNode);\n      return parentBlock ? parentBlock : rootNode;\n    };\n    const backspaceDeleteCollapsed = (editor, caret, forward, from) => {\n      const rootNode = rescope(editor.getBody(), from.container());\n      const isInlineTarget$1 = curry(isInlineTarget, editor);\n      const fromLocation = readLocation(isInlineTarget$1, rootNode, from);\n      const location = fromLocation.bind(location => {\n        if (forward) {\n          return location.fold(constant(Optional.some(inside(location))), Optional.none, constant(Optional.some(outside(location))), Optional.none);\n        } else {\n          return location.fold(Optional.none, constant(Optional.some(outside(location))), Optional.none, constant(Optional.some(inside(location))));\n        }\n      });\n      return location.map(setCaretLocation(editor, caret)).getOrThunk(() => {\n        const toPosition = navigate(forward, rootNode, from);\n        const toLocation = toPosition.bind(pos => readLocation(isInlineTarget$1, rootNode, pos));\n        return lift2(fromLocation, toLocation, () => findRootInline(isInlineTarget$1, rootNode, from).bind(elm => {\n          if (hasOnlyTwoOrLessPositionsLeft(elm)) {\n            return Optional.some(() => {\n              deleteElement$2(editor, forward, SugarElement.fromDom(elm));\n            });\n          } else {\n            return Optional.none();\n          }\n        })).getOrThunk(() => toLocation.bind(() => toPosition.map(to => {\n          return () => {\n            if (forward) {\n              deleteFromTo(editor, caret, from, to);\n            } else {\n              deleteFromTo(editor, caret, to, from);\n            }\n          };\n        })));\n      });\n    };\n    const backspaceDelete$3 = (editor, caret, forward) => {\n      if (editor.selection.isCollapsed() && isInlineBoundariesEnabled(editor)) {\n        const from = CaretPosition.fromRangeStart(editor.selection.getRng());\n        return backspaceDeleteCollapsed(editor, caret, forward, from);\n      }\n      return Optional.none();\n    };\n\n    const getParentInlines = (rootElm, startElm) => {\n      const parents = parentsAndSelf(startElm, rootElm);\n      return findIndex$2(parents, isBlock$2).fold(constant(parents), index => parents.slice(0, index));\n    };\n    const hasOnlyOneChild = elm => childNodesCount(elm) === 1;\n    const deleteLastPosition = (forward, editor, target, parentInlines) => {\n      const isFormatElement$1 = curry(isFormatElement, editor);\n      const formatNodes = map$3(filter$5(parentInlines, isFormatElement$1), elm => elm.dom);\n      if (formatNodes.length === 0) {\n        deleteElement$2(editor, forward, target);\n      } else {\n        const pos = replaceWithCaretFormat(target.dom, formatNodes);\n        editor.selection.setRng(pos.toRange());\n      }\n    };\n    const deleteCaret$1 = (editor, forward) => {\n      const rootElm = SugarElement.fromDom(editor.getBody());\n      const startElm = SugarElement.fromDom(editor.selection.getStart());\n      const parentInlines = filter$5(getParentInlines(rootElm, startElm), hasOnlyOneChild);\n      return last$3(parentInlines).bind(target => {\n        const fromPos = CaretPosition.fromRangeStart(editor.selection.getRng());\n        if (willDeleteLastPositionInElement(forward, fromPos, target.dom) && !isEmptyCaretFormatElement(target)) {\n          return Optional.some(() => deleteLastPosition(forward, editor, target, parentInlines));\n        } else {\n          return Optional.none();\n        }\n      });\n    };\n    const backspaceDelete$2 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret$1(editor, forward) : Optional.none();\n\n    const deleteElement = (editor, forward, element) => {\n      if (isNonNullable(element)) {\n        return Optional.some(() => {\n          editor._selectionOverrides.hideFakeCaret();\n          deleteElement$2(editor, forward, SugarElement.fromDom(element));\n        });\n      } else {\n        return Optional.none();\n      }\n    };\n    const deleteCaret = (editor, forward) => {\n      const isNearMedia = forward ? isBeforeMedia : isAfterMedia;\n      const direction = forward ? HDirection.Forwards : HDirection.Backwards;\n      const fromPos = getNormalizedRangeEndPoint(direction, editor.getBody(), editor.selection.getRng());\n      if (isNearMedia(fromPos)) {\n        return deleteElement(editor, forward, fromPos.getNode(!forward));\n      } else {\n        return Optional.from(normalizePosition(forward, fromPos)).filter(pos => isNearMedia(pos) && isMoveInsideSameBlock(fromPos, pos)).bind(pos => deleteElement(editor, forward, pos.getNode(!forward)));\n      }\n    };\n    const deleteRange = (editor, forward) => {\n      const selectedNode = editor.selection.getNode();\n      return isMedia$2(selectedNode) ? deleteElement(editor, forward, selectedNode) : Optional.none();\n    };\n    const backspaceDelete$1 = (editor, forward) => editor.selection.isCollapsed() ? deleteCaret(editor, forward) : deleteRange(editor, forward);\n\n    const isEditable$1 = target => closest$4(target, elm => isContentEditableTrue$3(elm.dom) || isContentEditableFalse$a(elm.dom)).exists(elm => isContentEditableTrue$3(elm.dom));\n    const parseIndentValue = value => toInt(value !== null && value !== void 0 ? value : '').getOr(0);\n    const getIndentStyleName = (useMargin, element) => {\n      const indentStyleName = useMargin || isTable$1(element) ? 'margin' : 'padding';\n      const suffix = get$7(element, 'direction') === 'rtl' ? '-right' : '-left';\n      return indentStyleName + suffix;\n    };\n    const indentElement = (dom, command, useMargin, value, unit, element) => {\n      const indentStyleName = getIndentStyleName(useMargin, SugarElement.fromDom(element));\n      const parsedValue = parseIndentValue(dom.getStyle(element, indentStyleName));\n      if (command === 'outdent') {\n        const styleValue = Math.max(0, parsedValue - value);\n        dom.setStyle(element, indentStyleName, styleValue ? styleValue + unit : '');\n      } else {\n        const styleValue = parsedValue + value + unit;\n        dom.setStyle(element, indentStyleName, styleValue);\n      }\n    };\n    const validateBlocks = (editor, blocks) => forall(blocks, block => {\n      const indentStyleName = getIndentStyleName(shouldIndentUseMargin(editor), block);\n      const intentValue = getRaw$1(block, indentStyleName).map(parseIndentValue).getOr(0);\n      const contentEditable = editor.dom.getContentEditable(block.dom);\n      return contentEditable !== 'false' && intentValue > 0;\n    });\n    const canOutdent = editor => {\n      const blocks = getBlocksToIndent(editor);\n      return !editor.mode.isReadOnly() && (blocks.length > 1 || validateBlocks(editor, blocks));\n    };\n    const isListComponent = el => isList(el) || isListItem$1(el);\n    const parentIsListComponent = el => parent(el).exists(isListComponent);\n    const getBlocksToIndent = editor => filter$5(fromDom$1(editor.selection.getSelectedBlocks()), el => !isListComponent(el) && !parentIsListComponent(el) && isEditable$1(el));\n    const handle = (editor, command) => {\n      var _a, _b;\n      const {dom} = editor;\n      const indentation = getIndentation(editor);\n      const indentUnit = (_b = (_a = /[a-z%]+$/i.exec(indentation)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 'px';\n      const indentValue = parseIndentValue(indentation);\n      const useMargin = shouldIndentUseMargin(editor);\n      each$e(getBlocksToIndent(editor), block => {\n        indentElement(dom, command, useMargin, indentValue, indentUnit, block.dom);\n      });\n    };\n    const indent = editor => handle(editor, 'indent');\n    const outdent = editor => handle(editor, 'outdent');\n\n    const backspaceDelete = editor => {\n      if (editor.selection.isCollapsed() && canOutdent(editor)) {\n        const dom = editor.dom;\n        const rng = editor.selection.getRng();\n        const pos = CaretPosition.fromRangeStart(rng);\n        const block = dom.getParent(rng.startContainer, dom.isBlock);\n        if (block !== null && isAtStartOfBlock(SugarElement.fromDom(block), pos)) {\n          return Optional.some(() => outdent(editor));\n        }\n      }\n      return Optional.none();\n    };\n\n    const findAction = (editor, caret, forward) => findMap([\n      backspaceDelete,\n      backspaceDelete$5,\n      backspaceDelete$6,\n      (editor, forward) => backspaceDelete$3(editor, caret, forward),\n      backspaceDelete$8,\n      backspaceDelete$9,\n      backspaceDelete$4,\n      backspaceDelete$1,\n      backspaceDelete$7,\n      backspaceDelete$2\n    ], item => item(editor, forward));\n    const deleteCommand = (editor, caret) => {\n      const result = findAction(editor, caret, false);\n      result.fold(() => {\n        execNativeDeleteCommand(editor);\n        paddEmptyBody(editor);\n      }, call);\n    };\n    const forwardDeleteCommand = (editor, caret) => {\n      const result = findAction(editor, caret, true);\n      result.fold(() => execNativeForwardDeleteCommand(editor), call);\n    };\n    const setup$p = (editor, caret) => {\n      editor.addCommand('delete', () => {\n        deleteCommand(editor, caret);\n      });\n      editor.addCommand('forwardDelete', () => {\n        forwardDeleteCommand(editor, caret);\n      });\n    };\n\n    const SIGNIFICANT_MOVE = 5;\n    const LONGPRESS_DELAY = 400;\n    const getTouch = event => {\n      if (event.touches === undefined || event.touches.length !== 1) {\n        return Optional.none();\n      }\n      return Optional.some(event.touches[0]);\n    };\n    const isFarEnough = (touch, data) => {\n      const distX = Math.abs(touch.clientX - data.x);\n      const distY = Math.abs(touch.clientY - data.y);\n      return distX > SIGNIFICANT_MOVE || distY > SIGNIFICANT_MOVE;\n    };\n    const setup$o = editor => {\n      const startData = value$2();\n      const longpressFired = Cell(false);\n      const debounceLongpress = last$1(e => {\n        editor.dispatch('longpress', {\n          ...e,\n          type: 'longpress'\n        });\n        longpressFired.set(true);\n      }, LONGPRESS_DELAY);\n      editor.on('touchstart', e => {\n        getTouch(e).each(touch => {\n          debounceLongpress.cancel();\n          const data = {\n            x: touch.clientX,\n            y: touch.clientY,\n            target: e.target\n          };\n          debounceLongpress.throttle(e);\n          longpressFired.set(false);\n          startData.set(data);\n        });\n      }, true);\n      editor.on('touchmove', e => {\n        debounceLongpress.cancel();\n        getTouch(e).each(touch => {\n          startData.on(data => {\n            if (isFarEnough(touch, data)) {\n              startData.clear();\n              longpressFired.set(false);\n              editor.dispatch('longpresscancel');\n            }\n          });\n        });\n      }, true);\n      editor.on('touchend touchcancel', e => {\n        debounceLongpress.cancel();\n        if (e.type === 'touchcancel') {\n          return;\n        }\n        startData.get().filter(data => data.target.isEqualNode(e.target)).each(() => {\n          if (longpressFired.get()) {\n            e.preventDefault();\n          } else {\n            editor.dispatch('tap', {\n              ...e,\n              type: 'tap'\n            });\n          }\n        });\n      }, true);\n    };\n\n    const isBlockElement = (blockElements, node) => has$2(blockElements, node.nodeName);\n    const isValidTarget = (schema, node) => {\n      if (isText$a(node)) {\n        return true;\n      } else if (isElement$6(node)) {\n        return !isBlockElement(schema.getBlockElements(), node) && !isBookmarkNode$1(node) && !isTransparentBlock(schema, node);\n      } else {\n        return false;\n      }\n    };\n    const hasBlockParent = (blockElements, root, node) => {\n      return exists(parents(SugarElement.fromDom(node), SugarElement.fromDom(root)), elm => {\n        return isBlockElement(blockElements, elm.dom);\n      });\n    };\n    const shouldRemoveTextNode = (blockElements, node) => {\n      if (isText$a(node)) {\n        if (node.data.length === 0) {\n          return true;\n        } else if (/^\\s+$/.test(node.data) && (!node.nextSibling || isBlockElement(blockElements, node.nextSibling))) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const createRootBlock = editor => editor.dom.create(getForcedRootBlock(editor), getForcedRootBlockAttrs(editor));\n    const addRootBlocks = editor => {\n      const dom = editor.dom, selection = editor.selection;\n      const schema = editor.schema;\n      const blockElements = schema.getBlockElements();\n      const startNode = selection.getStart();\n      const rootNode = editor.getBody();\n      let rootBlockNode;\n      let tempNode;\n      let wrapped = false;\n      const forcedRootBlock = getForcedRootBlock(editor);\n      if (!startNode || !isElement$6(startNode)) {\n        return;\n      }\n      const rootNodeName = rootNode.nodeName.toLowerCase();\n      if (!schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase()) || hasBlockParent(blockElements, rootNode, startNode)) {\n        return;\n      }\n      const rng = selection.getRng();\n      const {startContainer, startOffset, endContainer, endOffset} = rng;\n      const restoreSelection = hasFocus(editor);\n      let node = rootNode.firstChild;\n      while (node) {\n        if (isElement$6(node)) {\n          updateElement(schema, node);\n        }\n        if (isValidTarget(schema, node)) {\n          if (shouldRemoveTextNode(blockElements, node)) {\n            tempNode = node;\n            node = node.nextSibling;\n            dom.remove(tempNode);\n            continue;\n          }\n          if (!rootBlockNode) {\n            rootBlockNode = createRootBlock(editor);\n            rootNode.insertBefore(rootBlockNode, node);\n            wrapped = true;\n          }\n          tempNode = node;\n          node = node.nextSibling;\n          rootBlockNode.appendChild(tempNode);\n        } else {\n          rootBlockNode = null;\n          node = node.nextSibling;\n        }\n      }\n      if (wrapped && restoreSelection) {\n        rng.setStart(startContainer, startOffset);\n        rng.setEnd(endContainer, endOffset);\n        selection.setRng(rng);\n        editor.nodeChanged();\n      }\n    };\n    const insertEmptyLine = (editor, root, insertBlock) => {\n      const block = SugarElement.fromDom(createRootBlock(editor));\n      const br = createPaddingBr();\n      append$1(block, br);\n      insertBlock(root, block);\n      const rng = document.createRange();\n      rng.setStartBefore(br.dom);\n      rng.setEndBefore(br.dom);\n      return rng;\n    };\n    const setup$n = editor => {\n      editor.on('NodeChange', curry(addRootBlocks, editor));\n    };\n\n    const hasClass = checkClassName => node => (' ' + node.attr('class') + ' ').indexOf(checkClassName) !== -1;\n    const replaceMatchWithSpan = (editor, content, cls) => {\n      return function (match) {\n        const args = arguments, index = args[args.length - 2];\n        const prevChar = index > 0 ? content.charAt(index - 1) : '';\n        if (prevChar === '\"') {\n          return match;\n        }\n        if (prevChar === '>') {\n          const findStartTagIndex = content.lastIndexOf('<', index);\n          if (findStartTagIndex !== -1) {\n            const tagHtml = content.substring(findStartTagIndex, index);\n            if (tagHtml.indexOf('contenteditable=\"false\"') !== -1) {\n              return match;\n            }\n          }\n        }\n        return '<span class=\"' + cls + '\" data-mce-content=\"' + editor.dom.encode(args[0]) + '\">' + editor.dom.encode(typeof args[1] === 'string' ? args[1] : args[0]) + '</span>';\n      };\n    };\n    const convertRegExpsToNonEditable = (editor, nonEditableRegExps, e) => {\n      let i = nonEditableRegExps.length, content = e.content;\n      if (e.format === 'raw') {\n        return;\n      }\n      while (i--) {\n        content = content.replace(nonEditableRegExps[i], replaceMatchWithSpan(editor, content, getNonEditableClass(editor)));\n      }\n      e.content = content;\n    };\n    const setup$m = editor => {\n      const contentEditableAttrName = 'contenteditable';\n      const editClass = ' ' + Tools.trim(getEditableClass(editor)) + ' ';\n      const nonEditClass = ' ' + Tools.trim(getNonEditableClass(editor)) + ' ';\n      const hasEditClass = hasClass(editClass);\n      const hasNonEditClass = hasClass(nonEditClass);\n      const nonEditableRegExps = getNonEditableRegExps(editor);\n      if (nonEditableRegExps.length > 0) {\n        editor.on('BeforeSetContent', e => {\n          convertRegExpsToNonEditable(editor, nonEditableRegExps, e);\n        });\n      }\n      editor.parser.addAttributeFilter('class', nodes => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          if (hasEditClass(node)) {\n            node.attr(contentEditableAttrName, 'true');\n          } else if (hasNonEditClass(node)) {\n            node.attr(contentEditableAttrName, 'false');\n          }\n        }\n      });\n      editor.serializer.addAttributeFilter(contentEditableAttrName, nodes => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          if (!hasEditClass(node) && !hasNonEditClass(node)) {\n            continue;\n          }\n          if (nonEditableRegExps.length > 0 && node.attr('data-mce-content')) {\n            node.name = '#text';\n            node.type = 3;\n            node.raw = true;\n            node.value = node.attr('data-mce-content');\n          } else {\n            node.attr(contentEditableAttrName, null);\n          }\n        }\n      });\n    };\n\n    const findBlockCaretContainer = editor => descendant(SugarElement.fromDom(editor.getBody()), '*[data-mce-caret]').map(elm => elm.dom).getOrNull();\n    const showBlockCaretContainer = (editor, blockCaretContainer) => {\n      if (blockCaretContainer.hasAttribute('data-mce-caret')) {\n        showCaretContainerBlock(blockCaretContainer);\n        editor.selection.setRng(editor.selection.getRng());\n        editor.selection.scrollIntoView(blockCaretContainer);\n      }\n    };\n    const handleBlockContainer = (editor, e) => {\n      const blockCaretContainer = findBlockCaretContainer(editor);\n      if (!blockCaretContainer) {\n        return;\n      }\n      if (e.type === 'compositionstart') {\n        e.preventDefault();\n        e.stopPropagation();\n        showBlockCaretContainer(editor, blockCaretContainer);\n        return;\n      }\n      if (hasContent(blockCaretContainer)) {\n        showBlockCaretContainer(editor, blockCaretContainer);\n        editor.undoManager.add();\n      }\n    };\n    const setup$l = editor => {\n      editor.on('keyup compositionstart', curry(handleBlockContainer, editor));\n    };\n\n    const isContentEditableFalse$3 = isContentEditableFalse$a;\n    const moveToCeFalseHorizontally = (direction, editor, range) => moveHorizontally(editor, direction, range, isBeforeContentEditableFalse, isAfterContentEditableFalse, isContentEditableFalse$3);\n    const moveToCeFalseVertically = (direction, editor, range) => {\n      const isBefore = caretPosition => isBeforeContentEditableFalse(caretPosition) || isBeforeTable(caretPosition);\n      const isAfter = caretPosition => isAfterContentEditableFalse(caretPosition) || isAfterTable(caretPosition);\n      return moveVertically(editor, direction, range, isBefore, isAfter, isContentEditableFalse$3);\n    };\n    const createTextBlock = editor => {\n      const textBlock = editor.dom.create(getForcedRootBlock(editor));\n      textBlock.innerHTML = '<br data-mce-bogus=\"1\">';\n      return textBlock;\n    };\n    const exitPreBlock = (editor, direction, range) => {\n      const caretWalker = CaretWalker(editor.getBody());\n      const getVisualCaretPosition$1 = curry(getVisualCaretPosition, direction === 1 ? caretWalker.next : caretWalker.prev);\n      if (range.collapsed) {\n        const pre = editor.dom.getParent(range.startContainer, 'PRE');\n        if (!pre) {\n          return;\n        }\n        const caretPos = getVisualCaretPosition$1(CaretPosition.fromRangeStart(range));\n        if (!caretPos) {\n          const newBlock = SugarElement.fromDom(createTextBlock(editor));\n          if (direction === 1) {\n            after$4(SugarElement.fromDom(pre), newBlock);\n          } else {\n            before$3(SugarElement.fromDom(pre), newBlock);\n          }\n          editor.selection.select(newBlock.dom, true);\n          editor.selection.collapse();\n        }\n      }\n    };\n    const getHorizontalRange = (editor, forward) => {\n      const direction = forward ? HDirection.Forwards : HDirection.Backwards;\n      const range = editor.selection.getRng();\n      return moveToCeFalseHorizontally(direction, editor, range).orThunk(() => {\n        exitPreBlock(editor, direction, range);\n        return Optional.none();\n      });\n    };\n    const getVerticalRange = (editor, down) => {\n      const direction = down ? 1 : -1;\n      const range = editor.selection.getRng();\n      return moveToCeFalseVertically(direction, editor, range).orThunk(() => {\n        exitPreBlock(editor, direction, range);\n        return Optional.none();\n      });\n    };\n    const moveH$2 = (editor, forward) => getHorizontalRange(editor, forward).exists(newRange => {\n      moveToRange(editor, newRange);\n      return true;\n    });\n    const moveV$3 = (editor, down) => getVerticalRange(editor, down).exists(newRange => {\n      moveToRange(editor, newRange);\n      return true;\n    });\n    const moveToLineEndPoint$1 = (editor, forward) => {\n      const isCefPosition = forward ? isAfterContentEditableFalse : isBeforeContentEditableFalse;\n      return moveToLineEndPoint$3(editor, forward, isCefPosition);\n    };\n    const selectToEndPoint = (editor, forward) => getEdgeCefPosition(editor, !forward).map(pos => {\n      const rng = pos.toRange();\n      const curRng = editor.selection.getRng();\n      if (forward) {\n        rng.setStart(curRng.startContainer, curRng.startOffset);\n      } else {\n        rng.setEnd(curRng.endContainer, curRng.endOffset);\n      }\n      return rng;\n    }).exists(rng => {\n      moveToRange(editor, rng);\n      return true;\n    });\n\n    const isTarget = node => contains$2(['figcaption'], name(node));\n    const getClosestTargetBlock = (pos, root) => {\n      const isRoot = curry(eq, root);\n      return closest$4(SugarElement.fromDom(pos.container()), isBlock$2, isRoot).filter(isTarget);\n    };\n    const isAtFirstOrLastLine = (root, forward, pos) => forward ? isAtLastLine(root.dom, pos) : isAtFirstLine(root.dom, pos);\n    const moveCaretToNewEmptyLine = (editor, forward) => {\n      const root = SugarElement.fromDom(editor.getBody());\n      const pos = CaretPosition.fromRangeStart(editor.selection.getRng());\n      return getClosestTargetBlock(pos, root).exists(() => {\n        if (isAtFirstOrLastLine(root, forward, pos)) {\n          const insertFn = forward ? append$1 : prepend;\n          const rng = insertEmptyLine(editor, root, insertFn);\n          editor.selection.setRng(rng);\n          return true;\n        } else {\n          return false;\n        }\n      });\n    };\n    const moveV$2 = (editor, forward) => {\n      if (editor.selection.isCollapsed()) {\n        return moveCaretToNewEmptyLine(editor, forward);\n      } else {\n        return false;\n      }\n    };\n\n    const baseKeyPattern = {\n      shiftKey: false,\n      altKey: false,\n      ctrlKey: false,\n      metaKey: false,\n      keyCode: 0\n    };\n    const defaultPatterns = patterns => map$3(patterns, pattern => ({\n      ...baseKeyPattern,\n      ...pattern\n    }));\n    const defaultDelayedPatterns = patterns => map$3(patterns, pattern => ({\n      ...baseKeyPattern,\n      ...pattern\n    }));\n    const matchesEvent = (pattern, evt) => evt.keyCode === pattern.keyCode && evt.shiftKey === pattern.shiftKey && evt.altKey === pattern.altKey && evt.ctrlKey === pattern.ctrlKey && evt.metaKey === pattern.metaKey;\n    const match$1 = (patterns, evt) => bind$3(defaultPatterns(patterns), pattern => matchesEvent(pattern, evt) ? [pattern] : []);\n    const matchDelayed = (patterns, evt) => bind$3(defaultDelayedPatterns(patterns), pattern => matchesEvent(pattern, evt) ? [pattern] : []);\n    const action = (f, ...x) => () => f.apply(null, x);\n    const execute = (patterns, evt) => find$2(match$1(patterns, evt), pattern => pattern.action());\n    const executeWithDelayedAction = (patterns, evt) => findMap(matchDelayed(patterns, evt), pattern => pattern.action());\n\n    const moveH$1 = (editor, forward) => {\n      const direction = forward ? HDirection.Forwards : HDirection.Backwards;\n      const range = editor.selection.getRng();\n      return moveHorizontally(editor, direction, range, isBeforeMedia, isAfterMedia, isMedia$2).exists(newRange => {\n        moveToRange(editor, newRange);\n        return true;\n      });\n    };\n    const moveV$1 = (editor, down) => {\n      const direction = down ? 1 : -1;\n      const range = editor.selection.getRng();\n      return moveVertically(editor, direction, range, isBeforeMedia, isAfterMedia, isMedia$2).exists(newRange => {\n        moveToRange(editor, newRange);\n        return true;\n      });\n    };\n    const moveToLineEndPoint = (editor, forward) => {\n      const isNearMedia = forward ? isAfterMedia : isBeforeMedia;\n      return moveToLineEndPoint$3(editor, forward, isNearMedia);\n    };\n\n    const adt = Adt.generate([\n      { none: ['current'] },\n      { first: ['current'] },\n      {\n        middle: [\n          'current',\n          'target'\n        ]\n      },\n      { last: ['current'] }\n    ]);\n    const none = current => adt.none(current);\n    const CellLocation = {\n      ...adt,\n      none\n    };\n\n    const firstLayer = (scope, selector) => {\n      return filterFirstLayer(scope, selector, always);\n    };\n    const filterFirstLayer = (scope, selector, predicate) => {\n      return bind$3(children$1(scope), x => {\n        if (is$1(x, selector)) {\n          return predicate(x) ? [x] : [];\n        } else {\n          return filterFirstLayer(x, selector, predicate);\n        }\n      });\n    };\n\n    const lookup$1 = (tags, element, isRoot = never) => {\n      if (isRoot(element)) {\n        return Optional.none();\n      }\n      if (contains$2(tags, name(element))) {\n        return Optional.some(element);\n      }\n      const isRootOrUpperTable = elm => is$1(elm, 'table') || isRoot(elm);\n      return ancestor$2(element, tags.join(','), isRootOrUpperTable);\n    };\n    const cell = (element, isRoot) => lookup$1([\n      'td',\n      'th'\n    ], element, isRoot);\n    const cells = ancestor => firstLayer(ancestor, 'th,td');\n    const table = (element, isRoot) => closest$3(element, 'table', isRoot);\n\n    const walk = (all, current, index, direction, isEligible = always) => {\n      const forwards = direction === 1;\n      if (!forwards && index <= 0) {\n        return CellLocation.first(all[0]);\n      } else if (forwards && index >= all.length - 1) {\n        return CellLocation.last(all[all.length - 1]);\n      } else {\n        const newIndex = index + direction;\n        const elem = all[newIndex];\n        return isEligible(elem) ? CellLocation.middle(current, elem) : walk(all, current, newIndex, direction, isEligible);\n      }\n    };\n    const detect = (current, isRoot) => {\n      return table(current, isRoot).bind(table => {\n        const all = cells(table);\n        const index = findIndex$2(all, x => eq(current, x));\n        return index.map(index => ({\n          index,\n          all\n        }));\n      });\n    };\n    const next = (current, isEligible, isRoot) => {\n      const detection = detect(current, isRoot);\n      return detection.fold(() => {\n        return CellLocation.none(current);\n      }, info => {\n        return walk(info.all, current, info.index, 1, isEligible);\n      });\n    };\n    const prev = (current, isEligible, isRoot) => {\n      const detection = detect(current, isRoot);\n      return detection.fold(() => {\n        return CellLocation.none();\n      }, info => {\n        return walk(info.all, current, info.index, -1, isEligible);\n      });\n    };\n\n    const closest = target => closest$3(target, '[contenteditable]');\n    const isEditable = (element, assumeEditable = false) => {\n      if (inBody(element)) {\n        return element.dom.isContentEditable;\n      } else {\n        return closest(element).fold(constant(assumeEditable), editable => getRaw(editable) === 'true');\n      }\n    };\n    const getRaw = element => element.dom.contentEditable;\n\n    const deflate = (rect, delta) => ({\n      left: rect.left - delta,\n      top: rect.top - delta,\n      right: rect.right + delta * 2,\n      bottom: rect.bottom + delta * 2,\n      width: rect.width + delta,\n      height: rect.height + delta\n    });\n    const getCorners = (getYAxisValue, tds) => bind$3(tds, td => {\n      const rect = deflate(clone$1(td.getBoundingClientRect()), -1);\n      return [\n        {\n          x: rect.left,\n          y: getYAxisValue(rect),\n          cell: td\n        },\n        {\n          x: rect.right,\n          y: getYAxisValue(rect),\n          cell: td\n        }\n      ];\n    });\n    const findClosestCorner = (corners, x, y) => foldl(corners, (acc, newCorner) => acc.fold(() => Optional.some(newCorner), oldCorner => {\n      const oldDist = Math.sqrt(Math.abs(oldCorner.x - x) + Math.abs(oldCorner.y - y));\n      const newDist = Math.sqrt(Math.abs(newCorner.x - x) + Math.abs(newCorner.y - y));\n      return Optional.some(newDist < oldDist ? newCorner : oldCorner);\n    }), Optional.none());\n    const getClosestCell = (getYAxisValue, isTargetCorner, table, x, y) => {\n      const cells = descendants(SugarElement.fromDom(table), 'td,th,caption').map(e => e.dom);\n      const corners = filter$5(getCorners(getYAxisValue, cells), corner => isTargetCorner(corner, y));\n      return findClosestCorner(corners, x, y).map(corner => corner.cell);\n    };\n    const getBottomValue = rect => rect.bottom;\n    const getTopValue = rect => rect.top;\n    const isAbove = (corner, y) => corner.y < y;\n    const isBelow = (corner, y) => corner.y > y;\n    const getClosestCellAbove = curry(getClosestCell, getBottomValue, isAbove);\n    const getClosestCellBelow = curry(getClosestCell, getTopValue, isBelow);\n    const findClosestPositionInAboveCell = (table, pos) => head(pos.getClientRects()).bind(rect => getClosestCellAbove(table, rect.left, rect.top)).bind(cell => findClosestHorizontalPosition(getLastLinePositions(cell), pos));\n    const findClosestPositionInBelowCell = (table, pos) => last$3(pos.getClientRects()).bind(rect => getClosestCellBelow(table, rect.left, rect.top)).bind(cell => findClosestHorizontalPosition(getFirstLinePositions(cell), pos));\n\n    const hasNextBreak = (getPositionsUntil, scope, lineInfo) => lineInfo.breakAt.exists(breakPos => getPositionsUntil(scope, breakPos).breakAt.isSome());\n    const startsWithWrapBreak = lineInfo => lineInfo.breakType === BreakType.Wrap && lineInfo.positions.length === 0;\n    const startsWithBrBreak = lineInfo => lineInfo.breakType === BreakType.Br && lineInfo.positions.length === 1;\n    const isAtTableCellLine = (getPositionsUntil, scope, pos) => {\n      const lineInfo = getPositionsUntil(scope, pos);\n      if (startsWithWrapBreak(lineInfo) || !isBr$6(pos.getNode()) && startsWithBrBreak(lineInfo)) {\n        return !hasNextBreak(getPositionsUntil, scope, lineInfo);\n      } else {\n        return lineInfo.breakAt.isNone();\n      }\n    };\n    const isAtFirstTableCellLine = curry(isAtTableCellLine, getPositionsUntilPreviousLine);\n    const isAtLastTableCellLine = curry(isAtTableCellLine, getPositionsUntilNextLine);\n    const isCaretAtStartOrEndOfTable = (forward, rng, table) => {\n      const caretPos = CaretPosition.fromRangeStart(rng);\n      return positionIn(!forward, table).exists(pos => pos.isEqual(caretPos));\n    };\n    const navigateHorizontally = (editor, forward, table, _td) => {\n      const rng = editor.selection.getRng();\n      const direction = forward ? 1 : -1;\n      if (isFakeCaretTableBrowser() && isCaretAtStartOrEndOfTable(forward, rng, table)) {\n        showCaret(direction, editor, table, !forward, false).each(newRng => {\n          moveToRange(editor, newRng);\n        });\n        return true;\n      }\n      return false;\n    };\n    const getClosestAbovePosition = (root, table, start) => findClosestPositionInAboveCell(table, start).orThunk(() => head(start.getClientRects()).bind(rect => findClosestHorizontalPositionFromPoint(getPositionsAbove(root, CaretPosition.before(table)), rect.left))).getOr(CaretPosition.before(table));\n    const getClosestBelowPosition = (root, table, start) => findClosestPositionInBelowCell(table, start).orThunk(() => head(start.getClientRects()).bind(rect => findClosestHorizontalPositionFromPoint(getPositionsBelow(root, CaretPosition.after(table)), rect.left))).getOr(CaretPosition.after(table));\n    const getTable = (previous, pos) => {\n      const node = pos.getNode(previous);\n      return isTable$2(node) ? Optional.some(node) : Optional.none();\n    };\n    const renderBlock = (down, editor, table) => {\n      editor.undoManager.transact(() => {\n        const insertFn = down ? after$4 : before$3;\n        const rng = insertEmptyLine(editor, SugarElement.fromDom(table), insertFn);\n        moveToRange(editor, rng);\n      });\n    };\n    const moveCaret = (editor, down, pos) => {\n      const table = down ? getTable(true, pos) : getTable(false, pos);\n      const last = down === false;\n      table.fold(() => moveToRange(editor, pos.toRange()), table => positionIn(last, editor.getBody()).filter(lastPos => lastPos.isEqual(pos)).fold(() => moveToRange(editor, pos.toRange()), _ => renderBlock(down, editor, table)));\n    };\n    const navigateVertically = (editor, down, table, td) => {\n      const rng = editor.selection.getRng();\n      const pos = CaretPosition.fromRangeStart(rng);\n      const root = editor.getBody();\n      if (!down && isAtFirstTableCellLine(td, pos)) {\n        const newPos = getClosestAbovePosition(root, table, pos);\n        moveCaret(editor, down, newPos);\n        return true;\n      } else if (down && isAtLastTableCellLine(td, pos)) {\n        const newPos = getClosestBelowPosition(root, table, pos);\n        moveCaret(editor, down, newPos);\n        return true;\n      } else {\n        return false;\n      }\n    };\n    const move$1 = (editor, forward, mover) => Optional.from(editor.dom.getParent(editor.selection.getNode(), 'td,th')).bind(td => Optional.from(editor.dom.getParent(td, 'table')).map(table => mover(editor, forward, table, td))).getOr(false);\n    const moveH = (editor, forward) => move$1(editor, forward, navigateHorizontally);\n    const moveV = (editor, forward) => move$1(editor, forward, navigateVertically);\n    const getCellFirstCursorPosition = cell => {\n      const selection = SimSelection.exact(cell, 0, cell, 0);\n      return toNative(selection);\n    };\n    const tabGo = (editor, isRoot, cell) => {\n      return cell.fold(Optional.none, Optional.none, (_current, next) => {\n        return first(next).map(cell => {\n          return getCellFirstCursorPosition(cell);\n        });\n      }, current => {\n        editor.execCommand('mceTableInsertRowAfter');\n        return tabForward(editor, isRoot, current);\n      });\n    };\n    const tabForward = (editor, isRoot, cell) => tabGo(editor, isRoot, next(cell, isEditable));\n    const tabBackward = (editor, isRoot, cell) => tabGo(editor, isRoot, prev(cell, isEditable));\n    const handleTab = (editor, forward) => {\n      const rootElements = [\n        'table',\n        'li',\n        'dl'\n      ];\n      const body = SugarElement.fromDom(editor.getBody());\n      const isRoot = element => {\n        const name$1 = name(element);\n        return eq(element, body) || contains$2(rootElements, name$1);\n      };\n      const rng = editor.selection.getRng();\n      const container = SugarElement.fromDom(!forward ? rng.startContainer : rng.endContainer);\n      return cell(container, isRoot).map(cell => {\n        table(cell, isRoot).each(table => {\n          editor.model.table.clearSelectedCells(table.dom);\n        });\n        editor.selection.collapse(!forward);\n        const navigation = !forward ? tabBackward : tabForward;\n        const rng = navigation(editor, isRoot, cell);\n        rng.each(range => {\n          editor.selection.setRng(range);\n        });\n        return true;\n      }).getOr(false);\n    };\n\n    const executeKeydownOverride$4 = (editor, caret, evt) => {\n      const isMac = Env.os.isMacOS() || Env.os.isiOS();\n      execute([\n        {\n          keyCode: VK.RIGHT,\n          action: action(moveH$2, editor, true)\n        },\n        {\n          keyCode: VK.LEFT,\n          action: action(moveH$2, editor, false)\n        },\n        {\n          keyCode: VK.UP,\n          action: action(moveV$3, editor, false)\n        },\n        {\n          keyCode: VK.DOWN,\n          action: action(moveV$3, editor, true)\n        },\n        ...isMac ? [\n          {\n            keyCode: VK.UP,\n            action: action(selectToEndPoint, editor, false),\n            metaKey: true,\n            shiftKey: true\n          },\n          {\n            keyCode: VK.DOWN,\n            action: action(selectToEndPoint, editor, true),\n            metaKey: true,\n            shiftKey: true\n          }\n        ] : [],\n        {\n          keyCode: VK.RIGHT,\n          action: action(moveH, editor, true)\n        },\n        {\n          keyCode: VK.LEFT,\n          action: action(moveH, editor, false)\n        },\n        {\n          keyCode: VK.UP,\n          action: action(moveV, editor, false)\n        },\n        {\n          keyCode: VK.DOWN,\n          action: action(moveV, editor, true)\n        },\n        {\n          keyCode: VK.RIGHT,\n          action: action(moveH$1, editor, true)\n        },\n        {\n          keyCode: VK.LEFT,\n          action: action(moveH$1, editor, false)\n        },\n        {\n          keyCode: VK.UP,\n          action: action(moveV$1, editor, false)\n        },\n        {\n          keyCode: VK.DOWN,\n          action: action(moveV$1, editor, true)\n        },\n        {\n          keyCode: VK.RIGHT,\n          action: action(move$2, editor, caret, true)\n        },\n        {\n          keyCode: VK.LEFT,\n          action: action(move$2, editor, caret, false)\n        },\n        {\n          keyCode: VK.RIGHT,\n          ctrlKey: !isMac,\n          altKey: isMac,\n          action: action(moveNextWord, editor, caret)\n        },\n        {\n          keyCode: VK.LEFT,\n          ctrlKey: !isMac,\n          altKey: isMac,\n          action: action(movePrevWord, editor, caret)\n        },\n        {\n          keyCode: VK.UP,\n          action: action(moveV$2, editor, false)\n        },\n        {\n          keyCode: VK.DOWN,\n          action: action(moveV$2, editor, true)\n        }\n      ], evt).each(_ => {\n        evt.preventDefault();\n      });\n    };\n    const setup$k = (editor, caret) => {\n      editor.on('keydown', evt => {\n        if (!evt.isDefaultPrevented()) {\n          executeKeydownOverride$4(editor, caret, evt);\n        }\n      });\n    };\n\n    const point = (container, offset) => ({\n      container,\n      offset\n    });\n\n    const DOM$7 = DOMUtils.DOM;\n    const alwaysNext = startNode => node => startNode === node ? -1 : 0;\n    const isBoundary = dom => node => dom.isBlock(node) || contains$2([\n      'BR',\n      'IMG',\n      'HR',\n      'INPUT'\n    ], node.nodeName) || dom.getContentEditable(node) === 'false';\n    const textBefore = (node, offset, rootNode) => {\n      if (isText$a(node) && offset >= 0) {\n        return Optional.some(point(node, offset));\n      } else {\n        const textSeeker = TextSeeker(DOM$7);\n        return Optional.from(textSeeker.backwards(node, offset, alwaysNext(node), rootNode)).map(prev => point(prev.container, prev.container.data.length));\n      }\n    };\n    const textAfter = (node, offset, rootNode) => {\n      if (isText$a(node) && offset >= node.length) {\n        return Optional.some(point(node, offset));\n      } else {\n        const textSeeker = TextSeeker(DOM$7);\n        return Optional.from(textSeeker.forwards(node, offset, alwaysNext(node), rootNode)).map(prev => point(prev.container, 0));\n      }\n    };\n    const scanLeft = (node, offset, rootNode) => {\n      if (!isText$a(node)) {\n        return Optional.none();\n      }\n      const text = node.data;\n      if (offset >= 0 && offset <= text.length) {\n        return Optional.some(point(node, offset));\n      } else {\n        const textSeeker = TextSeeker(DOM$7);\n        return Optional.from(textSeeker.backwards(node, offset, alwaysNext(node), rootNode)).bind(prev => {\n          const prevText = prev.container.data;\n          return scanLeft(prev.container, offset + prevText.length, rootNode);\n        });\n      }\n    };\n    const scanRight = (node, offset, rootNode) => {\n      if (!isText$a(node)) {\n        return Optional.none();\n      }\n      const text = node.data;\n      if (offset <= text.length) {\n        return Optional.some(point(node, offset));\n      } else {\n        const textSeeker = TextSeeker(DOM$7);\n        return Optional.from(textSeeker.forwards(node, offset, alwaysNext(node), rootNode)).bind(next => scanRight(next.container, offset - text.length, rootNode));\n      }\n    };\n    const repeatLeft = (dom, node, offset, process, rootNode) => {\n      const search = TextSeeker(dom, isBoundary(dom));\n      return Optional.from(search.backwards(node, offset, process, rootNode));\n    };\n\n    const isValidTextRange = rng => rng.collapsed && isText$a(rng.startContainer);\n    const getText = rng => trim$1(rng.toString().replace(/\\u00A0/g, ' '));\n    const isWhitespace = chr => chr !== '' && ' \\xA0\\f\\n\\r\\t\\x0B'.indexOf(chr) !== -1;\n\n    const stripTrigger = (text, trigger) => text.substring(trigger.length);\n    const findTrigger = (text, index, trigger) => {\n      let i;\n      const firstChar = trigger.charAt(0);\n      for (i = index - 1; i >= 0; i--) {\n        const char = text.charAt(i);\n        if (isWhitespace(char)) {\n          return Optional.none();\n        }\n        if (firstChar === char && contains$1(text, trigger, i, index)) {\n          break;\n        }\n      }\n      return Optional.some(i);\n    };\n    const findStart = (dom, initRange, trigger, minChars = 0) => {\n      if (!isValidTextRange(initRange)) {\n        return Optional.none();\n      }\n      const buffer = {\n        text: '',\n        offset: 0\n      };\n      const findTriggerIndex = (element, offset, text) => {\n        buffer.text = text + buffer.text;\n        buffer.offset += offset;\n        return findTrigger(buffer.text, buffer.offset, trigger).getOr(offset);\n      };\n      const root = dom.getParent(initRange.startContainer, dom.isBlock) || dom.getRoot();\n      return repeatLeft(dom, initRange.startContainer, initRange.startOffset, findTriggerIndex, root).bind(spot => {\n        const range = initRange.cloneRange();\n        range.setStart(spot.container, spot.offset);\n        range.setEnd(initRange.endContainer, initRange.endOffset);\n        if (range.collapsed) {\n          return Optional.none();\n        }\n        const text = getText(range);\n        const triggerIndex = text.lastIndexOf(trigger);\n        if (triggerIndex !== 0 || stripTrigger(text, trigger).length < minChars) {\n          return Optional.none();\n        } else {\n          return Optional.some({\n            text: stripTrigger(text, trigger),\n            range,\n            trigger\n          });\n        }\n      });\n    };\n    const getContext = (dom, initRange, trigger, minChars = 0) => detect$1(SugarElement.fromDom(initRange.startContainer)).fold(() => findStart(dom, initRange, trigger, minChars), elm => {\n      const range = dom.createRng();\n      range.selectNode(elm.dom);\n      const text = getText(range);\n      return Optional.some({\n        range,\n        text: stripTrigger(text, trigger),\n        trigger\n      });\n    });\n\n    const isText$1 = node => node.nodeType === TEXT;\n    const isElement = node => node.nodeType === ELEMENT;\n    const toLast = node => {\n      if (isText$1(node)) {\n        return point(node, node.data.length);\n      } else {\n        const children = node.childNodes;\n        return children.length > 0 ? toLast(children[children.length - 1]) : point(node, children.length);\n      }\n    };\n    const toLeaf = (node, offset) => {\n      const children = node.childNodes;\n      if (children.length > 0 && offset < children.length) {\n        return toLeaf(children[offset], 0);\n      } else if (children.length > 0 && isElement(node) && children.length === offset) {\n        return toLast(children[children.length - 1]);\n      } else {\n        return point(node, offset);\n      }\n    };\n\n    const isPreviousCharContent = (dom, leaf) => {\n      var _a;\n      const root = (_a = dom.getParent(leaf.container, dom.isBlock)) !== null && _a !== void 0 ? _a : dom.getRoot();\n      return repeatLeft(dom, leaf.container, leaf.offset, (_element, offset) => offset === 0 ? -1 : offset, root).filter(spot => {\n        const char = spot.container.data.charAt(spot.offset - 1);\n        return !isWhitespace(char);\n      }).isSome();\n    };\n    const isStartOfWord = dom => rng => {\n      const leaf = toLeaf(rng.startContainer, rng.startOffset);\n      return !isPreviousCharContent(dom, leaf);\n    };\n    const getTriggerContext = (dom, initRange, database) => findMap(database.triggers, trigger => getContext(dom, initRange, trigger));\n    const lookup = (editor, getDatabase) => {\n      const database = getDatabase();\n      const rng = editor.selection.getRng();\n      return getTriggerContext(editor.dom, rng, database).bind(context => lookupWithContext(editor, getDatabase, context));\n    };\n    const lookupWithContext = (editor, getDatabase, context, fetchOptions = {}) => {\n      var _a;\n      const database = getDatabase();\n      const rng = editor.selection.getRng();\n      const startText = (_a = rng.startContainer.nodeValue) !== null && _a !== void 0 ? _a : '';\n      const autocompleters = filter$5(database.lookupByTrigger(context.trigger), autocompleter => context.text.length >= autocompleter.minChars && autocompleter.matches.getOrThunk(() => isStartOfWord(editor.dom))(context.range, startText, context.text));\n      if (autocompleters.length === 0) {\n        return Optional.none();\n      }\n      const lookupData = Promise.all(map$3(autocompleters, ac => {\n        const fetchResult = ac.fetch(context.text, ac.maxResults, fetchOptions);\n        return fetchResult.then(results => ({\n          matchText: context.text,\n          items: results,\n          columns: ac.columns,\n          onAction: ac.onAction,\n          highlightOn: ac.highlightOn\n        }));\n      }));\n      return Optional.some({\n        lookupData,\n        context\n      });\n    };\n\n    var SimpleResultType;\n    (function (SimpleResultType) {\n      SimpleResultType[SimpleResultType['Error'] = 0] = 'Error';\n      SimpleResultType[SimpleResultType['Value'] = 1] = 'Value';\n    }(SimpleResultType || (SimpleResultType = {})));\n    const fold$1 = (res, onError, onValue) => res.stype === SimpleResultType.Error ? onError(res.serror) : onValue(res.svalue);\n    const partition = results => {\n      const values = [];\n      const errors = [];\n      each$e(results, obj => {\n        fold$1(obj, err => errors.push(err), val => values.push(val));\n      });\n      return {\n        values,\n        errors\n      };\n    };\n    const mapError = (res, f) => {\n      if (res.stype === SimpleResultType.Error) {\n        return {\n          stype: SimpleResultType.Error,\n          serror: f(res.serror)\n        };\n      } else {\n        return res;\n      }\n    };\n    const map = (res, f) => {\n      if (res.stype === SimpleResultType.Value) {\n        return {\n          stype: SimpleResultType.Value,\n          svalue: f(res.svalue)\n        };\n      } else {\n        return res;\n      }\n    };\n    const bind = (res, f) => {\n      if (res.stype === SimpleResultType.Value) {\n        return f(res.svalue);\n      } else {\n        return res;\n      }\n    };\n    const bindError = (res, f) => {\n      if (res.stype === SimpleResultType.Error) {\n        return f(res.serror);\n      } else {\n        return res;\n      }\n    };\n    const svalue = v => ({\n      stype: SimpleResultType.Value,\n      svalue: v\n    });\n    const serror = e => ({\n      stype: SimpleResultType.Error,\n      serror: e\n    });\n    const toResult = res => fold$1(res, Result.error, Result.value);\n    const fromResult = res => res.fold(serror, svalue);\n    const SimpleResult = {\n      fromResult,\n      toResult,\n      svalue,\n      partition,\n      serror,\n      bind,\n      bindError,\n      map,\n      mapError,\n      fold: fold$1\n    };\n\n    const formatObj = input => {\n      return isObject(input) && keys(input).length > 100 ? ' removed due to size' : JSON.stringify(input, null, 2);\n    };\n    const formatErrors = errors => {\n      const es = errors.length > 10 ? errors.slice(0, 10).concat([{\n          path: [],\n          getErrorInfo: constant('... (only showing first ten failures)')\n        }]) : errors;\n      return map$3(es, e => {\n        return 'Failed path: (' + e.path.join(' > ') + ')\\n' + e.getErrorInfo();\n      });\n    };\n\n    const nu = (path, getErrorInfo) => {\n      return SimpleResult.serror([{\n          path,\n          getErrorInfo\n        }]);\n    };\n    const missingRequired = (path, key, obj) => nu(path, () => 'Could not find valid *required* value for \"' + key + '\" in ' + formatObj(obj));\n    const missingKey = (path, key) => nu(path, () => 'Choice schema did not contain choice key: \"' + key + '\"');\n    const missingBranch = (path, branches, branch) => nu(path, () => 'The chosen schema: \"' + branch + '\" did not exist in branches: ' + formatObj(branches));\n    const custom = (path, err) => nu(path, constant(err));\n\n    const chooseFrom = (path, input, branches, ch) => {\n      const fields = get$a(branches, ch);\n      return fields.fold(() => missingBranch(path, branches, ch), vp => vp.extract(path.concat(['branch: ' + ch]), input));\n    };\n    const choose$1 = (key, branches) => {\n      const extract = (path, input) => {\n        const choice = get$a(input, key);\n        return choice.fold(() => missingKey(path, key), chosen => chooseFrom(path, input, branches, chosen));\n      };\n      const toString = () => 'chooseOn(' + key + '). Possible values: ' + keys(branches);\n      return {\n        extract,\n        toString\n      };\n    };\n\n    const shallow = (old, nu) => {\n      return nu;\n    };\n    const deep = (old, nu) => {\n      const bothObjects = isPlainObject(old) && isPlainObject(nu);\n      return bothObjects ? deepMerge(old, nu) : nu;\n    };\n    const baseMerge = merger => {\n      return (...objects) => {\n        if (objects.length === 0) {\n          throw new Error(`Can't merge zero objects`);\n        }\n        const ret = {};\n        for (let j = 0; j < objects.length; j++) {\n          const curObject = objects[j];\n          for (const key in curObject) {\n            if (has$2(curObject, key)) {\n              ret[key] = merger(ret[key], curObject[key]);\n            }\n          }\n        }\n        return ret;\n      };\n    };\n    const deepMerge = baseMerge(deep);\n    const merge = baseMerge(shallow);\n\n    const required = () => ({\n      tag: 'required',\n      process: {}\n    });\n    const defaultedThunk = fallbackThunk => ({\n      tag: 'defaultedThunk',\n      process: fallbackThunk\n    });\n    const defaulted$1 = fallback => defaultedThunk(constant(fallback));\n    const asOption = () => ({\n      tag: 'option',\n      process: {}\n    });\n\n    const mergeValues = (values, base) => values.length > 0 ? SimpleResult.svalue(deepMerge(base, merge.apply(undefined, values))) : SimpleResult.svalue(base);\n    const mergeErrors = errors => compose(SimpleResult.serror, flatten)(errors);\n    const consolidateObj = (objects, base) => {\n      const partition = SimpleResult.partition(objects);\n      return partition.errors.length > 0 ? mergeErrors(partition.errors) : mergeValues(partition.values, base);\n    };\n    const consolidateArr = objects => {\n      const partitions = SimpleResult.partition(objects);\n      return partitions.errors.length > 0 ? mergeErrors(partitions.errors) : SimpleResult.svalue(partitions.values);\n    };\n    const ResultCombine = {\n      consolidateObj,\n      consolidateArr\n    };\n\n    const field$1 = (key, newKey, presence, prop) => ({\n      tag: 'field',\n      key,\n      newKey,\n      presence,\n      prop\n    });\n    const customField$1 = (newKey, instantiator) => ({\n      tag: 'custom',\n      newKey,\n      instantiator\n    });\n    const fold = (value, ifField, ifCustom) => {\n      switch (value.tag) {\n      case 'field':\n        return ifField(value.key, value.newKey, value.presence, value.prop);\n      case 'custom':\n        return ifCustom(value.newKey, value.instantiator);\n      }\n    };\n\n    const value = validator => {\n      const extract = (path, val) => {\n        return SimpleResult.bindError(validator(val), err => custom(path, err));\n      };\n      const toString = constant('val');\n      return {\n        extract,\n        toString\n      };\n    };\n    const anyValue$1 = value(SimpleResult.svalue);\n\n    const requiredAccess = (path, obj, key, bundle) => get$a(obj, key).fold(() => missingRequired(path, key, obj), bundle);\n    const fallbackAccess = (obj, key, fallback, bundle) => {\n      const v = get$a(obj, key).getOrThunk(() => fallback(obj));\n      return bundle(v);\n    };\n    const optionAccess = (obj, key, bundle) => bundle(get$a(obj, key));\n    const optionDefaultedAccess = (obj, key, fallback, bundle) => {\n      const opt = get$a(obj, key).map(val => val === true ? fallback(obj) : val);\n      return bundle(opt);\n    };\n    const extractField = (field, path, obj, key, prop) => {\n      const bundle = av => prop.extract(path.concat([key]), av);\n      const bundleAsOption = optValue => optValue.fold(() => SimpleResult.svalue(Optional.none()), ov => {\n        const result = prop.extract(path.concat([key]), ov);\n        return SimpleResult.map(result, Optional.some);\n      });\n      switch (field.tag) {\n      case 'required':\n        return requiredAccess(path, obj, key, bundle);\n      case 'defaultedThunk':\n        return fallbackAccess(obj, key, field.process, bundle);\n      case 'option':\n        return optionAccess(obj, key, bundleAsOption);\n      case 'defaultedOptionThunk':\n        return optionDefaultedAccess(obj, key, field.process, bundleAsOption);\n      case 'mergeWithThunk': {\n          return fallbackAccess(obj, key, constant({}), v => {\n            const result = deepMerge(field.process(obj), v);\n            return bundle(result);\n          });\n        }\n      }\n    };\n    const extractFields = (path, obj, fields) => {\n      const success = {};\n      const errors = [];\n      for (const field of fields) {\n        fold(field, (key, newKey, presence, prop) => {\n          const result = extractField(presence, path, obj, key, prop);\n          SimpleResult.fold(result, err => {\n            errors.push(...err);\n          }, res => {\n            success[newKey] = res;\n          });\n        }, (newKey, instantiator) => {\n          success[newKey] = instantiator(obj);\n        });\n      }\n      return errors.length > 0 ? SimpleResult.serror(errors) : SimpleResult.svalue(success);\n    };\n    const objOf = values => {\n      const extract = (path, o) => extractFields(path, o, values);\n      const toString = () => {\n        const fieldStrings = map$3(values, value => fold(value, (key, _okey, _presence, prop) => key + ' -> ' + prop.toString(), (newKey, _instantiator) => 'state(' + newKey + ')'));\n        return 'obj{\\n' + fieldStrings.join('\\n') + '}';\n      };\n      return {\n        extract,\n        toString\n      };\n    };\n    const arrOf = prop => {\n      const extract = (path, array) => {\n        const results = map$3(array, (a, i) => prop.extract(path.concat(['[' + i + ']']), a));\n        return ResultCombine.consolidateArr(results);\n      };\n      const toString = () => 'array(' + prop.toString() + ')';\n      return {\n        extract,\n        toString\n      };\n    };\n\n    const valueOf = validator => value(v => validator(v).fold(SimpleResult.serror, SimpleResult.svalue));\n    const extractValue = (label, prop, obj) => {\n      const res = prop.extract([label], obj);\n      return SimpleResult.mapError(res, errs => ({\n        input: obj,\n        errors: errs\n      }));\n    };\n    const asRaw = (label, prop, obj) => SimpleResult.toResult(extractValue(label, prop, obj));\n    const formatError = errInfo => {\n      return 'Errors: \\n' + formatErrors(errInfo.errors).join('\\n') + '\\n\\nInput object: ' + formatObj(errInfo.input);\n    };\n    const choose = (key, branches) => choose$1(key, map$2(branches, objOf));\n\n    const anyValue = constant(anyValue$1);\n    const typedValue = (validator, expectedType) => value(a => {\n      const actualType = typeof a;\n      return validator(a) ? SimpleResult.svalue(a) : SimpleResult.serror(`Expected type: ${ expectedType } but got: ${ actualType }`);\n    });\n    const number = typedValue(isNumber, 'number');\n    const string = typedValue(isString, 'string');\n    const boolean = typedValue(isBoolean, 'boolean');\n    const functionProcessor = typedValue(isFunction, 'function');\n\n    const field = field$1;\n    const customField = customField$1;\n    const validateEnum = values => valueOf(value => contains$2(values, value) ? Result.value(value) : Result.error(`Unsupported value: \"${ value }\", choose one of \"${ values.join(', ') }\".`));\n    const requiredOf = (key, schema) => field(key, key, required(), schema);\n    const requiredString = key => requiredOf(key, string);\n    const requiredFunction = key => requiredOf(key, functionProcessor);\n    const requiredArrayOf = (key, schema) => field(key, key, required(), arrOf(schema));\n    const optionOf = (key, schema) => field(key, key, asOption(), schema);\n    const optionString = key => optionOf(key, string);\n    const optionFunction = key => optionOf(key, functionProcessor);\n    const defaulted = (key, fallback) => field(key, key, defaulted$1(fallback), anyValue());\n    const defaultedOf = (key, fallback, schema) => field(key, key, defaulted$1(fallback), schema);\n    const defaultedNumber = (key, fallback) => defaultedOf(key, fallback, number);\n    const defaultedString = (key, fallback) => defaultedOf(key, fallback, string);\n    const defaultedStringEnum = (key, fallback, values) => defaultedOf(key, fallback, validateEnum(values));\n    const defaultedBoolean = (key, fallback) => defaultedOf(key, fallback, boolean);\n    const defaultedFunction = (key, fallback) => defaultedOf(key, fallback, functionProcessor);\n    const defaultedArrayOf = (key, fallback, schema) => defaultedOf(key, fallback, arrOf(schema));\n\n    const type = requiredString('type');\n    const fetch$1 = requiredFunction('fetch');\n    const onAction = requiredFunction('onAction');\n    const onSetup = defaultedFunction('onSetup', () => noop);\n    const optionalText = optionString('text');\n    const optionalIcon = optionString('icon');\n    const optionalTooltip = optionString('tooltip');\n    const optionalLabel = optionString('label');\n    const active = defaultedBoolean('active', false);\n    const enabled = defaultedBoolean('enabled', true);\n    const primary = defaultedBoolean('primary', false);\n    const defaultedColumns = num => defaulted('columns', num);\n    const defaultedType = type => defaultedString('type', type);\n\n    const autocompleterSchema = objOf([\n      type,\n      requiredString('trigger'),\n      defaultedNumber('minChars', 1),\n      defaultedColumns(1),\n      defaultedNumber('maxResults', 10),\n      optionFunction('matches'),\n      fetch$1,\n      onAction,\n      defaultedArrayOf('highlightOn', [], string)\n    ]);\n    const createAutocompleter = spec => asRaw('Autocompleter', autocompleterSchema, {\n      trigger: spec.ch,\n      ...spec\n    });\n\n    const baseToolbarButtonFields = [\n      enabled,\n      optionalTooltip,\n      optionalIcon,\n      optionalText,\n      onSetup\n    ];\n\n    const baseToolbarToggleButtonFields = [active].concat(baseToolbarButtonFields);\n\n    const contextBarFields = [\n      defaultedFunction('predicate', never),\n      defaultedStringEnum('scope', 'node', [\n        'node',\n        'editor'\n      ]),\n      defaultedStringEnum('position', 'selection', [\n        'node',\n        'selection',\n        'line'\n      ])\n    ];\n\n    const contextButtonFields = baseToolbarButtonFields.concat([\n      defaultedType('contextformbutton'),\n      primary,\n      onAction,\n      customField('original', identity)\n    ]);\n    const contextToggleButtonFields = baseToolbarToggleButtonFields.concat([\n      defaultedType('contextformbutton'),\n      primary,\n      onAction,\n      customField('original', identity)\n    ]);\n    const launchButtonFields = baseToolbarButtonFields.concat([defaultedType('contextformbutton')]);\n    const launchToggleButtonFields = baseToolbarToggleButtonFields.concat([defaultedType('contextformtogglebutton')]);\n    const toggleOrNormal = choose('type', {\n      contextformbutton: contextButtonFields,\n      contextformtogglebutton: contextToggleButtonFields\n    });\n    objOf([\n      defaultedType('contextform'),\n      defaultedFunction('initValue', constant('')),\n      optionalLabel,\n      requiredArrayOf('commands', toggleOrNormal),\n      optionOf('launch', choose('type', {\n        contextformbutton: launchButtonFields,\n        contextformtogglebutton: launchToggleButtonFields\n      }))\n    ].concat(contextBarFields));\n\n    const register$2 = editor => {\n      const popups = editor.ui.registry.getAll().popups;\n      const dataset = map$2(popups, popup => createAutocompleter(popup).fold(err => {\n        throw new Error(formatError(err));\n      }, identity));\n      const triggers = stringArray(mapToArray(dataset, v => v.trigger));\n      const datasetValues = values(dataset);\n      const lookupByTrigger = trigger => filter$5(datasetValues, dv => dv.trigger === trigger);\n      return {\n        dataset,\n        triggers,\n        lookupByTrigger\n      };\n    };\n\n    const setupEditorInput = (editor, api) => {\n      const update = last$1(api.load, 50);\n      editor.on('keypress compositionend', e => {\n        if (e.which === 27) {\n          return;\n        }\n        update.throttle();\n      });\n      editor.on('keydown', e => {\n        const keyCode = e.which;\n        if (keyCode === 8) {\n          update.throttle();\n        } else if (keyCode === 27) {\n          api.cancelIfNecessary();\n        }\n      });\n      editor.on('remove', update.cancel);\n    };\n    const setup$j = editor => {\n      const activeAutocompleter = value$2();\n      const uiActive = Cell(false);\n      const isActive = activeAutocompleter.isSet;\n      const cancelIfNecessary = () => {\n        if (isActive()) {\n          removeAutocompleterDecoration(editor);\n          fireAutocompleterEnd(editor);\n          uiActive.set(false);\n          activeAutocompleter.clear();\n        }\n      };\n      const commenceIfNecessary = context => {\n        if (!isActive()) {\n          addAutocompleterDecoration(editor, context.range);\n          activeAutocompleter.set({\n            trigger: context.trigger,\n            matchLength: context.text.length\n          });\n        }\n      };\n      const getAutocompleters = cached(() => register$2(editor));\n      const doLookup = fetchOptions => activeAutocompleter.get().map(ac => getContext(editor.dom, editor.selection.getRng(), ac.trigger).bind(newContext => lookupWithContext(editor, getAutocompleters, newContext, fetchOptions))).getOrThunk(() => lookup(editor, getAutocompleters));\n      const load = fetchOptions => {\n        doLookup(fetchOptions).fold(cancelIfNecessary, lookupInfo => {\n          commenceIfNecessary(lookupInfo.context);\n          lookupInfo.lookupData.then(lookupData => {\n            activeAutocompleter.get().map(ac => {\n              const context = lookupInfo.context;\n              if (ac.trigger === context.trigger) {\n                if (context.text.length - ac.matchLength >= 10) {\n                  cancelIfNecessary();\n                } else {\n                  activeAutocompleter.set({\n                    ...ac,\n                    matchLength: context.text.length\n                  });\n                  if (uiActive.get()) {\n                    fireAutocompleterUpdate(editor, { lookupData });\n                  } else {\n                    uiActive.set(true);\n                    fireAutocompleterStart(editor, { lookupData });\n                  }\n                }\n              }\n            });\n          });\n        });\n      };\n      editor.addCommand('mceAutocompleterReload', (_ui, value) => {\n        const fetchOptions = isObject(value) ? value.fetchOptions : {};\n        load(fetchOptions);\n      });\n      editor.addCommand('mceAutocompleterClose', cancelIfNecessary);\n      setupEditorInput(editor, {\n        cancelIfNecessary,\n        load\n      });\n    };\n\n    const createAndFireInputEvent = eventType => (editor, inputType, specifics = {}) => {\n      const target = editor.getBody();\n      const overrides = {\n        bubbles: true,\n        composed: true,\n        data: null,\n        isComposing: false,\n        detail: 0,\n        view: null,\n        target,\n        currentTarget: target,\n        eventPhase: Event.AT_TARGET,\n        originalTarget: target,\n        explicitOriginalTarget: target,\n        isTrusted: false,\n        srcElement: target,\n        cancelable: false,\n        preventDefault: noop,\n        inputType\n      };\n      const input = clone$3(new InputEvent(eventType));\n      return editor.dispatch(eventType, {\n        ...input,\n        ...overrides,\n        ...specifics\n      });\n    };\n    const fireFakeInputEvent = createAndFireInputEvent('input');\n    const fireFakeBeforeInputEvent = createAndFireInputEvent('beforeinput');\n\n    const executeKeydownOverride$3 = (editor, caret, evt) => {\n      const inputType = evt.keyCode === VK.BACKSPACE ? 'deleteContentBackward' : 'deleteContentForward';\n      executeWithDelayedAction([\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete, editor)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$5, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$5, editor, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$6, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$6, editor, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$3, editor, caret, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$3, editor, caret, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$9, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$9, editor, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$4, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$4, editor, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$1, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$1, editor, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$7, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$7, editor, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$8, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$8, editor, true)\n        },\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(backspaceDelete$2, editor, false)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(backspaceDelete$2, editor, true)\n        }\n      ], evt).each(applyAction => {\n        evt.preventDefault();\n        const beforeInput = fireFakeBeforeInputEvent(editor, inputType);\n        if (!beforeInput.isDefaultPrevented()) {\n          applyAction();\n          fireFakeInputEvent(editor, inputType);\n        }\n      });\n    };\n    const executeKeyupOverride = (editor, evt) => {\n      execute([\n        {\n          keyCode: VK.BACKSPACE,\n          action: action(paddEmptyElement, editor)\n        },\n        {\n          keyCode: VK.DELETE,\n          action: action(paddEmptyElement, editor)\n        }\n      ], evt);\n    };\n    const setup$i = (editor, caret) => {\n      editor.on('keydown', evt => {\n        if (!evt.isDefaultPrevented()) {\n          executeKeydownOverride$3(editor, caret, evt);\n        }\n      });\n      editor.on('keyup', evt => {\n        if (!evt.isDefaultPrevented()) {\n          executeKeyupOverride(editor, evt);\n        }\n      });\n    };\n\n    const firstNonWhiteSpaceNodeSibling = node => {\n      while (node) {\n        if (isElement$6(node) || isText$a(node) && node.data && /[\\r\\n\\s]/.test(node.data)) {\n          return node;\n        }\n        node = node.nextSibling;\n      }\n      return null;\n    };\n    const moveToCaretPosition = (editor, root) => {\n      const dom = editor.dom;\n      const moveCaretBeforeOnEnterElementsMap = editor.schema.getMoveCaretBeforeOnEnterElements();\n      if (!root) {\n        return;\n      }\n      if (/^(LI|DT|DD)$/.test(root.nodeName)) {\n        const firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);\n        if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {\n          root.insertBefore(dom.doc.createTextNode(nbsp), root.firstChild);\n        }\n      }\n      const rng = dom.createRng();\n      root.normalize();\n      if (root.hasChildNodes()) {\n        const walker = new DomTreeWalker(root, root);\n        let lastNode = root;\n        let node;\n        while (node = walker.current()) {\n          if (isText$a(node)) {\n            rng.setStart(node, 0);\n            rng.setEnd(node, 0);\n            break;\n          }\n          if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) {\n            rng.setStartBefore(node);\n            rng.setEndBefore(node);\n            break;\n          }\n          lastNode = node;\n          node = walker.next();\n        }\n        if (!node) {\n          rng.setStart(lastNode, 0);\n          rng.setEnd(lastNode, 0);\n        }\n      } else {\n        if (isBr$6(root)) {\n          if (root.nextSibling && dom.isBlock(root.nextSibling)) {\n            rng.setStartBefore(root);\n            rng.setEndBefore(root);\n          } else {\n            rng.setStartAfter(root);\n            rng.setEndAfter(root);\n          }\n        } else {\n          rng.setStart(root, 0);\n          rng.setEnd(root, 0);\n        }\n      }\n      editor.selection.setRng(rng);\n      scrollRangeIntoView(editor, rng);\n    };\n    const getEditableRoot = (dom, node) => {\n      const root = dom.getRoot();\n      let editableRoot;\n      let parent = node;\n      while (parent !== root && parent && dom.getContentEditable(parent) !== 'false') {\n        if (dom.getContentEditable(parent) === 'true') {\n          editableRoot = parent;\n        }\n        parent = parent.parentNode;\n      }\n      return parent !== root ? editableRoot : root;\n    };\n    const getParentBlock$1 = editor => {\n      return Optional.from(editor.dom.getParent(editor.selection.getStart(true), editor.dom.isBlock));\n    };\n    const getParentBlockName = editor => {\n      return getParentBlock$1(editor).fold(constant(''), parentBlock => {\n        return parentBlock.nodeName.toUpperCase();\n      });\n    };\n    const isListItemParentBlock = editor => {\n      return getParentBlock$1(editor).filter(elm => {\n        return isListItem$1(SugarElement.fromDom(elm));\n      }).isSome();\n    };\n\n    const hasFirstChild = (elm, name) => {\n      return elm.firstChild && elm.firstChild.nodeName === name;\n    };\n    const isFirstChild = elm => {\n      var _a;\n      return ((_a = elm.parentNode) === null || _a === void 0 ? void 0 : _a.firstChild) === elm;\n    };\n    const hasParent = (elm, parentName) => {\n      const parentNode = elm === null || elm === void 0 ? void 0 : elm.parentNode;\n      return isNonNullable(parentNode) && parentNode.nodeName === parentName;\n    };\n    const isListBlock = elm => {\n      return isNonNullable(elm) && /^(OL|UL|LI)$/.test(elm.nodeName);\n    };\n    const isListItem = elm => {\n      return isNonNullable(elm) && /^(LI|DT|DD)$/.test(elm.nodeName);\n    };\n    const isNestedList = elm => {\n      return isListBlock(elm) && isListBlock(elm.parentNode);\n    };\n    const getContainerBlock = containerBlock => {\n      const containerBlockParent = containerBlock.parentNode;\n      return isListItem(containerBlockParent) ? containerBlockParent : containerBlock;\n    };\n    const isFirstOrLastLi = (containerBlock, parentBlock, first) => {\n      let node = containerBlock[first ? 'firstChild' : 'lastChild'];\n      while (node) {\n        if (isElement$6(node)) {\n          break;\n        }\n        node = node[first ? 'nextSibling' : 'previousSibling'];\n      }\n      return node === parentBlock;\n    };\n    const insert$3 = (editor, createNewBlock, containerBlock, parentBlock, newBlockName) => {\n      const dom = editor.dom;\n      const rng = editor.selection.getRng();\n      const containerParent = containerBlock.parentNode;\n      if (containerBlock === editor.getBody() || !containerParent) {\n        return;\n      }\n      if (isNestedList(containerBlock)) {\n        newBlockName = 'LI';\n      }\n      let newBlock = createNewBlock(newBlockName);\n      if (isFirstOrLastLi(containerBlock, parentBlock, true) && isFirstOrLastLi(containerBlock, parentBlock, false)) {\n        if (hasParent(containerBlock, 'LI')) {\n          const containerBlockParent = getContainerBlock(containerBlock);\n          dom.insertAfter(newBlock, containerBlockParent);\n          if (isFirstChild(containerBlock)) {\n            dom.remove(containerBlockParent);\n          } else {\n            dom.remove(containerBlock);\n          }\n        } else {\n          dom.replace(newBlock, containerBlock);\n        }\n      } else if (isFirstOrLastLi(containerBlock, parentBlock, true)) {\n        if (hasParent(containerBlock, 'LI')) {\n          dom.insertAfter(newBlock, getContainerBlock(containerBlock));\n          newBlock.appendChild(dom.doc.createTextNode(' '));\n          newBlock.appendChild(containerBlock);\n        } else {\n          containerParent.insertBefore(newBlock, containerBlock);\n        }\n        dom.remove(parentBlock);\n      } else if (isFirstOrLastLi(containerBlock, parentBlock, false)) {\n        dom.insertAfter(newBlock, getContainerBlock(containerBlock));\n        dom.remove(parentBlock);\n      } else {\n        containerBlock = getContainerBlock(containerBlock);\n        const tmpRng = rng.cloneRange();\n        tmpRng.setStartAfter(parentBlock);\n        tmpRng.setEndAfter(containerBlock);\n        const fragment = tmpRng.extractContents();\n        if (newBlockName === 'LI' && hasFirstChild(fragment, 'LI')) {\n          newBlock = fragment.firstChild;\n          dom.insertAfter(fragment, containerBlock);\n        } else {\n          dom.insertAfter(fragment, containerBlock);\n          dom.insertAfter(newBlock, containerBlock);\n        }\n        dom.remove(parentBlock);\n      }\n      moveToCaretPosition(editor, newBlock);\n    };\n\n    const trimZwsp = fragment => {\n      each$e(descendants$1(SugarElement.fromDom(fragment), isText$b), text => {\n        const rawNode = text.dom;\n        rawNode.nodeValue = trim$1(rawNode.data);\n      });\n    };\n    const isWithinNonEditableList = (editor, node) => {\n      const parentList = editor.dom.getParent(node, 'ol,ul,dl');\n      return parentList !== null && editor.dom.getContentEditableParent(parentList) === 'false';\n    };\n    const isEmptyAnchor = (dom, elm) => {\n      return elm && elm.nodeName === 'A' && dom.isEmpty(elm);\n    };\n    const emptyBlock = elm => {\n      elm.innerHTML = '<br data-mce-bogus=\"1\">';\n    };\n    const containerAndSiblingName = (container, nodeName) => {\n      return container.nodeName === nodeName || container.previousSibling && container.previousSibling.nodeName === nodeName;\n    };\n    const canSplitBlock = (dom, node) => {\n      return isNonNullable(node) && dom.isBlock(node) && !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && !/^(fixed|absolute)/i.test(node.style.position) && dom.getContentEditable(node) !== 'true';\n    };\n    const trimInlineElementsOnLeftSideOfBlock = (dom, nonEmptyElementsMap, block) => {\n      var _a;\n      const firstChilds = [];\n      if (!block) {\n        return;\n      }\n      let currentNode = block;\n      while (currentNode = currentNode.firstChild) {\n        if (dom.isBlock(currentNode)) {\n          return;\n        }\n        if (isElement$6(currentNode) && !nonEmptyElementsMap[currentNode.nodeName.toLowerCase()]) {\n          firstChilds.push(currentNode);\n        }\n      }\n      let i = firstChilds.length;\n      while (i--) {\n        currentNode = firstChilds[i];\n        if (!currentNode.hasChildNodes() || currentNode.firstChild === currentNode.lastChild && ((_a = currentNode.firstChild) === null || _a === void 0 ? void 0 : _a.nodeValue) === '') {\n          dom.remove(currentNode);\n        } else {\n          if (isEmptyAnchor(dom, currentNode)) {\n            dom.remove(currentNode);\n          }\n        }\n      }\n    };\n    const normalizeZwspOffset = (start, container, offset) => {\n      if (!isText$a(container)) {\n        return offset;\n      } else if (start) {\n        return offset === 1 && container.data.charAt(offset - 1) === ZWSP$1 ? 0 : offset;\n      } else {\n        return offset === container.data.length - 1 && container.data.charAt(offset) === ZWSP$1 ? container.data.length : offset;\n      }\n    };\n    const includeZwspInRange = rng => {\n      const newRng = rng.cloneRange();\n      newRng.setStart(rng.startContainer, normalizeZwspOffset(true, rng.startContainer, rng.startOffset));\n      newRng.setEnd(rng.endContainer, normalizeZwspOffset(false, rng.endContainer, rng.endOffset));\n      return newRng;\n    };\n    const trimLeadingLineBreaks = node => {\n      let currentNode = node;\n      do {\n        if (isText$a(currentNode)) {\n          currentNode.data = currentNode.data.replace(/^[\\r\\n]+/, '');\n        }\n        currentNode = currentNode.firstChild;\n      } while (currentNode);\n    };\n    const applyAttributes = (editor, node, forcedRootBlockAttrs) => {\n      const dom = editor.dom;\n      Optional.from(forcedRootBlockAttrs.style).map(dom.parseStyle).each(attrStyles => {\n        const currentStyles = getAllRaw(SugarElement.fromDom(node));\n        const newStyles = {\n          ...currentStyles,\n          ...attrStyles\n        };\n        dom.setStyles(node, newStyles);\n      });\n      const attrClassesOpt = Optional.from(forcedRootBlockAttrs.class).map(attrClasses => attrClasses.split(/\\s+/));\n      const currentClassesOpt = Optional.from(node.className).map(currentClasses => filter$5(currentClasses.split(/\\s+/), clazz => clazz !== ''));\n      lift2(attrClassesOpt, currentClassesOpt, (attrClasses, currentClasses) => {\n        const filteredClasses = filter$5(currentClasses, clazz => !contains$2(attrClasses, clazz));\n        const newClasses = [\n          ...attrClasses,\n          ...filteredClasses\n        ];\n        dom.setAttrib(node, 'class', newClasses.join(' '));\n      });\n      const appliedAttrs = [\n        'style',\n        'class'\n      ];\n      const remainingAttrs = filter$4(forcedRootBlockAttrs, (_, attrs) => !contains$2(appliedAttrs, attrs));\n      dom.setAttribs(node, remainingAttrs);\n    };\n    const setForcedBlockAttrs = (editor, node) => {\n      const forcedRootBlockName = getForcedRootBlock(editor);\n      if (forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {\n        const forcedRootBlockAttrs = getForcedRootBlockAttrs(editor);\n        applyAttributes(editor, node, forcedRootBlockAttrs);\n      }\n    };\n    const wrapSelfAndSiblingsInDefaultBlock = (editor, newBlockName, rng, container, offset) => {\n      var _a;\n      const dom = editor.dom;\n      const editableRoot = (_a = getEditableRoot(dom, container)) !== null && _a !== void 0 ? _a : dom.getRoot();\n      let parentBlock = dom.getParent(container, dom.isBlock);\n      if (!parentBlock || !canSplitBlock(dom, parentBlock)) {\n        parentBlock = parentBlock || editableRoot;\n        let rootBlockName;\n        if (parentBlock === editor.getBody() || isTableCellOrCaption(parentBlock)) {\n          rootBlockName = parentBlock.nodeName.toLowerCase();\n        } else if (parentBlock.parentNode) {\n          rootBlockName = parentBlock.parentNode.nodeName.toLowerCase();\n        } else {\n          rootBlockName = '';\n        }\n        if (!parentBlock.hasChildNodes()) {\n          const newBlock = dom.create(newBlockName);\n          setForcedBlockAttrs(editor, newBlock);\n          parentBlock.appendChild(newBlock);\n          rng.setStart(newBlock, 0);\n          rng.setEnd(newBlock, 0);\n          return newBlock;\n        }\n        let node = container;\n        while (node && node.parentNode !== parentBlock) {\n          node = node.parentNode;\n        }\n        let startNode;\n        while (node && !dom.isBlock(node)) {\n          startNode = node;\n          node = node.previousSibling;\n        }\n        if (startNode && editor.schema.isValidChild(rootBlockName, newBlockName.toLowerCase())) {\n          const startNodeParent = startNode.parentNode;\n          const newBlock = dom.create(newBlockName);\n          setForcedBlockAttrs(editor, newBlock);\n          startNodeParent.insertBefore(newBlock, startNode);\n          node = startNode;\n          while (node && !dom.isBlock(node)) {\n            const next = node.nextSibling;\n            newBlock.appendChild(node);\n            node = next;\n          }\n          rng.setStart(container, offset);\n          rng.setEnd(container, offset);\n        }\n      }\n      return container;\n    };\n    const addBrToBlockIfNeeded = (dom, block) => {\n      block.normalize();\n      const lastChild = block.lastChild;\n      if (!lastChild || isElement$6(lastChild) && /^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true))) {\n        dom.add(block, 'br');\n      }\n    };\n    const shouldEndContainer = (editor, container) => {\n      const optionValue = shouldEndContainerOnEmptyBlock(editor);\n      if (isNullable(container)) {\n        return false;\n      } else if (isString(optionValue)) {\n        return contains$2(Tools.explode(optionValue), container.nodeName.toLowerCase());\n      } else {\n        return optionValue;\n      }\n    };\n    const insert$2 = (editor, evt) => {\n      let container;\n      let offset;\n      let parentBlockName;\n      let containerBlock;\n      let isAfterLastNodeInContainer = false;\n      const dom = editor.dom;\n      const schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements();\n      const rng = editor.selection.getRng();\n      const newBlockName = getForcedRootBlock(editor);\n      const createNewBlock = name => {\n        let node = container;\n        const textInlineElements = schema.getTextInlineElements();\n        let block;\n        if (name || parentBlockName === 'TABLE' || parentBlockName === 'HR') {\n          block = dom.create(name || newBlockName);\n        } else {\n          block = parentBlock.cloneNode(false);\n        }\n        let caretNode = block;\n        if (shouldKeepStyles(editor) === false) {\n          dom.setAttrib(block, 'style', null);\n          dom.setAttrib(block, 'class', null);\n        } else {\n          do {\n            if (textInlineElements[node.nodeName]) {\n              if (isCaretNode(node) || isBookmarkNode$1(node)) {\n                continue;\n              }\n              const clonedNode = node.cloneNode(false);\n              dom.setAttrib(clonedNode, 'id', '');\n              if (block.hasChildNodes()) {\n                clonedNode.appendChild(block.firstChild);\n                block.appendChild(clonedNode);\n              } else {\n                caretNode = clonedNode;\n                block.appendChild(clonedNode);\n              }\n            }\n          } while ((node = node.parentNode) && node !== editableRoot);\n        }\n        setForcedBlockAttrs(editor, block);\n        emptyBlock(caretNode);\n        return block;\n      };\n      const isCaretAtStartOrEndOfBlock = start => {\n        const normalizedOffset = normalizeZwspOffset(start, container, offset);\n        if (isText$a(container) && (start ? normalizedOffset > 0 : normalizedOffset < container.data.length)) {\n          return false;\n        }\n        if (container.parentNode === parentBlock && isAfterLastNodeInContainer && !start) {\n          return true;\n        }\n        if (start && isElement$6(container) && container === parentBlock.firstChild) {\n          return true;\n        }\n        if (containerAndSiblingName(container, 'TABLE') || containerAndSiblingName(container, 'HR')) {\n          return isAfterLastNodeInContainer && !start || !isAfterLastNodeInContainer && start;\n        }\n        const walker = new DomTreeWalker(container, parentBlock);\n        if (isText$a(container)) {\n          if (start && normalizedOffset === 0) {\n            walker.prev();\n          } else if (!start && normalizedOffset === container.data.length) {\n            walker.next();\n          }\n        }\n        let node;\n        while (node = walker.current()) {\n          if (isElement$6(node)) {\n            if (!node.getAttribute('data-mce-bogus')) {\n              const name = node.nodeName.toLowerCase();\n              if (nonEmptyElementsMap[name] && name !== 'br') {\n                return false;\n              }\n            }\n          } else if (isText$a(node) && !isWhitespaceText(node.data)) {\n            return false;\n          }\n          if (start) {\n            walker.prev();\n          } else {\n            walker.next();\n          }\n        }\n        return true;\n      };\n      const insertNewBlockAfter = () => {\n        let block;\n        if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName !== 'HGROUP') {\n          block = createNewBlock(newBlockName);\n        } else {\n          block = createNewBlock();\n        }\n        if (shouldEndContainer(editor, containerBlock) && canSplitBlock(dom, containerBlock) && dom.isEmpty(parentBlock)) {\n          block = dom.split(containerBlock, parentBlock);\n        } else {\n          dom.insertAfter(block, parentBlock);\n        }\n        moveToCaretPosition(editor, block);\n        return block;\n      };\n      normalize$2(dom, rng).each(normRng => {\n        rng.setStart(normRng.startContainer, normRng.startOffset);\n        rng.setEnd(normRng.endContainer, normRng.endOffset);\n      });\n      container = rng.startContainer;\n      offset = rng.startOffset;\n      const shiftKey = !!(evt && evt.shiftKey);\n      const ctrlKey = !!(evt && evt.ctrlKey);\n      if (isElement$6(container) && container.hasChildNodes()) {\n        isAfterLastNodeInContainer = offset > container.childNodes.length - 1;\n        container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;\n        if (isAfterLastNodeInContainer && isText$a(container)) {\n          offset = container.data.length;\n        } else {\n          offset = 0;\n        }\n      }\n      const editableRoot = getEditableRoot(dom, container);\n      if (!editableRoot || isWithinNonEditableList(editor, container)) {\n        return;\n      }\n      if (!shiftKey) {\n        container = wrapSelfAndSiblingsInDefaultBlock(editor, newBlockName, rng, container, offset);\n      }\n      let parentBlock = dom.getParent(container, dom.isBlock) || dom.getRoot();\n      containerBlock = isNonNullable(parentBlock === null || parentBlock === void 0 ? void 0 : parentBlock.parentNode) ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;\n      parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : '';\n      const containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : '';\n      if (containerBlockName === 'LI' && !ctrlKey) {\n        const liBlock = containerBlock;\n        parentBlock = liBlock;\n        containerBlock = liBlock.parentNode;\n        parentBlockName = containerBlockName;\n      }\n      if (/^(LI|DT|DD)$/.test(parentBlockName) && isElement$6(containerBlock)) {\n        if (dom.isEmpty(parentBlock)) {\n          insert$3(editor, createNewBlock, containerBlock, parentBlock, newBlockName);\n          return;\n        }\n      }\n      if (parentBlock === editor.getBody()) {\n        return;\n      }\n      const parentBlockParent = parentBlock.parentNode;\n      let newBlock;\n      if (isCaretContainerBlock$1(parentBlock)) {\n        newBlock = showCaretContainerBlock(parentBlock);\n        if (dom.isEmpty(parentBlock)) {\n          emptyBlock(parentBlock);\n        }\n        setForcedBlockAttrs(editor, newBlock);\n        moveToCaretPosition(editor, newBlock);\n      } else if (isCaretAtStartOrEndOfBlock(false)) {\n        newBlock = insertNewBlockAfter();\n      } else if (isCaretAtStartOrEndOfBlock(true) && parentBlockParent) {\n        newBlock = parentBlockParent.insertBefore(createNewBlock(), parentBlock);\n        moveToCaretPosition(editor, containerAndSiblingName(parentBlock, 'HR') ? newBlock : parentBlock);\n      } else {\n        const tmpRng = includeZwspInRange(rng).cloneRange();\n        tmpRng.setEndAfter(parentBlock);\n        const fragment = tmpRng.extractContents();\n        trimZwsp(fragment);\n        trimLeadingLineBreaks(fragment);\n        newBlock = fragment.firstChild;\n        dom.insertAfter(fragment, parentBlock);\n        trimInlineElementsOnLeftSideOfBlock(dom, nonEmptyElementsMap, newBlock);\n        addBrToBlockIfNeeded(dom, parentBlock);\n        if (dom.isEmpty(parentBlock)) {\n          emptyBlock(parentBlock);\n        }\n        newBlock.normalize();\n        if (dom.isEmpty(newBlock)) {\n          dom.remove(newBlock);\n          insertNewBlockAfter();\n        } else {\n          setForcedBlockAttrs(editor, newBlock);\n          moveToCaretPosition(editor, newBlock);\n        }\n      }\n      dom.setAttrib(newBlock, 'id', '');\n      editor.dispatch('NewBlock', { newBlock });\n    };\n    const fakeEventName$1 = 'insertParagraph';\n    const blockbreak = {\n      insert: insert$2,\n      fakeEventName: fakeEventName$1\n    };\n\n    const hasRightSideContent = (schema, container, parentBlock) => {\n      const walker = new DomTreeWalker(container, parentBlock);\n      let node;\n      const nonEmptyElementsMap = schema.getNonEmptyElements();\n      while (node = walker.next()) {\n        if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || isText$a(node) && node.length > 0) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const moveSelectionToBr = (editor, brElm, extraBr) => {\n      const rng = editor.dom.createRng();\n      if (!extraBr) {\n        rng.setStartAfter(brElm);\n        rng.setEndAfter(brElm);\n      } else {\n        rng.setStartBefore(brElm);\n        rng.setEndBefore(brElm);\n      }\n      editor.selection.setRng(rng);\n      scrollRangeIntoView(editor, rng);\n    };\n    const insertBrAtCaret = (editor, evt) => {\n      const selection = editor.selection;\n      const dom = editor.dom;\n      const rng = selection.getRng();\n      let brElm;\n      let extraBr = false;\n      normalize$2(dom, rng).each(normRng => {\n        rng.setStart(normRng.startContainer, normRng.startOffset);\n        rng.setEnd(normRng.endContainer, normRng.endOffset);\n      });\n      let offset = rng.startOffset;\n      let container = rng.startContainer;\n      if (isElement$6(container) && container.hasChildNodes()) {\n        const isAfterLastNodeInContainer = offset > container.childNodes.length - 1;\n        container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;\n        if (isAfterLastNodeInContainer && isText$a(container)) {\n          offset = container.data.length;\n        } else {\n          offset = 0;\n        }\n      }\n      let parentBlock = dom.getParent(container, dom.isBlock);\n      const containerBlock = parentBlock && parentBlock.parentNode ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;\n      const containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : '';\n      const isControlKey = !!(evt && evt.ctrlKey);\n      if (containerBlockName === 'LI' && !isControlKey) {\n        parentBlock = containerBlock;\n      }\n      if (isText$a(container) && offset >= container.data.length) {\n        if (!hasRightSideContent(editor.schema, container, parentBlock || dom.getRoot())) {\n          brElm = dom.create('br');\n          rng.insertNode(brElm);\n          rng.setStartAfter(brElm);\n          rng.setEndAfter(brElm);\n          extraBr = true;\n        }\n      }\n      brElm = dom.create('br');\n      rangeInsertNode(dom, rng, brElm);\n      moveSelectionToBr(editor, brElm, extraBr);\n      editor.undoManager.add();\n    };\n    const insertBrBefore = (editor, inline) => {\n      const br = SugarElement.fromTag('br');\n      before$3(SugarElement.fromDom(inline), br);\n      editor.undoManager.add();\n    };\n    const insertBrAfter = (editor, inline) => {\n      if (!hasBrAfter(editor.getBody(), inline)) {\n        after$4(SugarElement.fromDom(inline), SugarElement.fromTag('br'));\n      }\n      const br = SugarElement.fromTag('br');\n      after$4(SugarElement.fromDom(inline), br);\n      moveSelectionToBr(editor, br.dom, false);\n      editor.undoManager.add();\n    };\n    const isBeforeBr = pos => {\n      return isBr$6(pos.getNode());\n    };\n    const hasBrAfter = (rootNode, startNode) => {\n      if (isBeforeBr(CaretPosition.after(startNode))) {\n        return true;\n      } else {\n        return nextPosition(rootNode, CaretPosition.after(startNode)).map(pos => {\n          return isBr$6(pos.getNode());\n        }).getOr(false);\n      }\n    };\n    const isAnchorLink = elm => {\n      return elm && elm.nodeName === 'A' && 'href' in elm;\n    };\n    const isInsideAnchor = location => {\n      return location.fold(never, isAnchorLink, isAnchorLink, never);\n    };\n    const readInlineAnchorLocation = editor => {\n      const isInlineTarget$1 = curry(isInlineTarget, editor);\n      const position = CaretPosition.fromRangeStart(editor.selection.getRng());\n      return readLocation(isInlineTarget$1, editor.getBody(), position).filter(isInsideAnchor);\n    };\n    const insertBrOutsideAnchor = (editor, location) => {\n      location.fold(noop, curry(insertBrBefore, editor), curry(insertBrAfter, editor), noop);\n    };\n    const insert$1 = (editor, evt) => {\n      const anchorLocation = readInlineAnchorLocation(editor);\n      if (anchorLocation.isSome()) {\n        anchorLocation.each(curry(insertBrOutsideAnchor, editor));\n      } else {\n        insertBrAtCaret(editor, evt);\n      }\n    };\n    const fakeEventName = 'insertLineBreak';\n    const linebreak = {\n      insert: insert$1,\n      fakeEventName\n    };\n\n    const matchesSelector = (editor, selector) => {\n      return getParentBlock$1(editor).filter(parentBlock => {\n        return selector.length > 0 && is$1(SugarElement.fromDom(parentBlock), selector);\n      }).isSome();\n    };\n    const shouldInsertBr = editor => {\n      return matchesSelector(editor, getBrNewLineSelector(editor));\n    };\n    const shouldBlockNewLine$1 = editor => {\n      return matchesSelector(editor, getNoNewLineSelector(editor));\n    };\n\n    const newLineAction = Adt.generate([\n      { br: [] },\n      { block: [] },\n      { none: [] }\n    ]);\n    const shouldBlockNewLine = (editor, _shiftKey) => {\n      return shouldBlockNewLine$1(editor);\n    };\n    const inListBlock = requiredState => {\n      return (editor, _shiftKey) => {\n        return isListItemParentBlock(editor) === requiredState;\n      };\n    };\n    const inBlock = (blockName, requiredState) => (editor, _shiftKey) => {\n      const state = getParentBlockName(editor) === blockName.toUpperCase();\n      return state === requiredState;\n    };\n    const inCefBlock = editor => {\n      const editableRoot = getEditableRoot(editor.dom, editor.selection.getStart());\n      return isNullable(editableRoot);\n    };\n    const inPreBlock = requiredState => inBlock('pre', requiredState);\n    const inSummaryBlock = () => inBlock('summary', true);\n    const shouldPutBrInPre = requiredState => {\n      return (editor, _shiftKey) => {\n        return shouldPutBrInPre$1(editor) === requiredState;\n      };\n    };\n    const inBrContext = (editor, _shiftKey) => {\n      return shouldInsertBr(editor);\n    };\n    const hasShiftKey = (_editor, shiftKey) => {\n      return shiftKey;\n    };\n    const canInsertIntoEditableRoot = editor => {\n      const forcedRootBlock = getForcedRootBlock(editor);\n      const rootEditable = getEditableRoot(editor.dom, editor.selection.getStart());\n      return isNonNullable(rootEditable) && editor.schema.isValidChild(rootEditable.nodeName, forcedRootBlock);\n    };\n    const match = (predicates, action) => {\n      return (editor, shiftKey) => {\n        const isMatch = foldl(predicates, (res, p) => {\n          return res && p(editor, shiftKey);\n        }, true);\n        return isMatch ? Optional.some(action) : Optional.none();\n      };\n    };\n    const getAction = (editor, evt) => {\n      return evaluateUntil([\n        match([shouldBlockNewLine], newLineAction.none()),\n        match([\n          inPreBlock(true),\n          inCefBlock\n        ], newLineAction.none()),\n        match([inSummaryBlock()], newLineAction.br()),\n        match([\n          inPreBlock(true),\n          shouldPutBrInPre(false),\n          hasShiftKey\n        ], newLineAction.br()),\n        match([\n          inPreBlock(true),\n          shouldPutBrInPre(false)\n        ], newLineAction.block()),\n        match([\n          inPreBlock(true),\n          shouldPutBrInPre(true),\n          hasShiftKey\n        ], newLineAction.block()),\n        match([\n          inPreBlock(true),\n          shouldPutBrInPre(true)\n        ], newLineAction.br()),\n        match([\n          inListBlock(true),\n          hasShiftKey\n        ], newLineAction.br()),\n        match([inListBlock(true)], newLineAction.block()),\n        match([inBrContext], newLineAction.br()),\n        match([hasShiftKey], newLineAction.br()),\n        match([canInsertIntoEditableRoot], newLineAction.block())\n      ], [\n        editor,\n        !!(evt && evt.shiftKey)\n      ]).getOr(newLineAction.none());\n    };\n\n    const insertBreak = (breakType, editor, evt) => {\n      if (!editor.selection.isCollapsed()) {\n        execEditorDeleteCommand(editor);\n      }\n      if (isNonNullable(evt)) {\n        const event = fireFakeBeforeInputEvent(editor, breakType.fakeEventName);\n        if (event.isDefaultPrevented()) {\n          return;\n        }\n      }\n      breakType.insert(editor, evt);\n      if (isNonNullable(evt)) {\n        fireFakeInputEvent(editor, breakType.fakeEventName);\n      }\n    };\n    const insert = (editor, evt) => {\n      const br = () => insertBreak(linebreak, editor, evt);\n      const block = () => insertBreak(blockbreak, editor, evt);\n      const logicalAction = getAction(editor, evt);\n      switch (getNewlineBehavior(editor)) {\n      case 'linebreak':\n        logicalAction.fold(br, br, noop);\n        break;\n      case 'block':\n        logicalAction.fold(block, block, noop);\n        break;\n      case 'invert':\n        logicalAction.fold(block, br, noop);\n        break;\n      default:\n        logicalAction.fold(br, block, noop);\n        break;\n      }\n    };\n\n    const handleEnterKeyEvent = (editor, event) => {\n      if (event.isDefaultPrevented()) {\n        return;\n      }\n      event.preventDefault();\n      endTypingLevelIgnoreLocks(editor.undoManager);\n      editor.undoManager.transact(() => {\n        insert(editor, event);\n      });\n    };\n    const setup$h = editor => {\n      editor.on('keydown', event => {\n        if (event.keyCode === VK.ENTER) {\n          handleEnterKeyEvent(editor, event);\n        }\n      });\n    };\n\n    const executeKeydownOverride$2 = (editor, caret, evt) => {\n      const isMac = Env.os.isMacOS() || Env.os.isiOS();\n      execute([\n        {\n          keyCode: VK.END,\n          action: action(moveToLineEndPoint$1, editor, true)\n        },\n        {\n          keyCode: VK.HOME,\n          action: action(moveToLineEndPoint$1, editor, false)\n        },\n        ...!isMac ? [\n          {\n            keyCode: VK.HOME,\n            action: action(selectToEndPoint, editor, false),\n            ctrlKey: true,\n            shiftKey: true\n          },\n          {\n            keyCode: VK.END,\n            action: action(selectToEndPoint, editor, true),\n            ctrlKey: true,\n            shiftKey: true\n          }\n        ] : [],\n        {\n          keyCode: VK.END,\n          action: action(moveToLineEndPoint, editor, true)\n        },\n        {\n          keyCode: VK.HOME,\n          action: action(moveToLineEndPoint, editor, false)\n        },\n        {\n          keyCode: VK.END,\n          action: action(moveToLineEndPoint$2, editor, true, caret)\n        },\n        {\n          keyCode: VK.HOME,\n          action: action(moveToLineEndPoint$2, editor, false, caret)\n        }\n      ], evt).each(_ => {\n        evt.preventDefault();\n      });\n    };\n    const setup$g = (editor, caret) => {\n      editor.on('keydown', evt => {\n        if (!evt.isDefaultPrevented()) {\n          executeKeydownOverride$2(editor, caret, evt);\n        }\n      });\n    };\n\n    const setup$f = editor => {\n      editor.on('input', e => {\n        if (!e.isComposing) {\n          normalizeNbspsInEditor(editor);\n        }\n      });\n    };\n\n    const platform = detect$2();\n    const executeKeyupAction = (editor, caret, evt) => {\n      execute([\n        {\n          keyCode: VK.PAGE_UP,\n          action: action(moveToLineEndPoint$2, editor, false, caret)\n        },\n        {\n          keyCode: VK.PAGE_DOWN,\n          action: action(moveToLineEndPoint$2, editor, true, caret)\n        }\n      ], evt);\n    };\n    const stopImmediatePropagation = e => e.stopImmediatePropagation();\n    const isPageUpDown = evt => evt.keyCode === VK.PAGE_UP || evt.keyCode === VK.PAGE_DOWN;\n    const setNodeChangeBlocker = (blocked, editor, block) => {\n      if (block && !blocked.get()) {\n        editor.on('NodeChange', stopImmediatePropagation, true);\n      } else if (!block && blocked.get()) {\n        editor.off('NodeChange', stopImmediatePropagation);\n      }\n      blocked.set(block);\n    };\n    const setup$e = (editor, caret) => {\n      if (platform.os.isMacOS()) {\n        return;\n      }\n      const blocked = Cell(false);\n      editor.on('keydown', evt => {\n        if (isPageUpDown(evt)) {\n          setNodeChangeBlocker(blocked, editor, true);\n        }\n      });\n      editor.on('keyup', evt => {\n        if (!evt.isDefaultPrevented()) {\n          executeKeyupAction(editor, caret, evt);\n        }\n        if (isPageUpDown(evt) && blocked.get()) {\n          setNodeChangeBlocker(blocked, editor, false);\n          editor.nodeChanged();\n        }\n      });\n    };\n\n    const insertTextAtPosition = (text, pos) => {\n      const container = pos.container();\n      const offset = pos.offset();\n      if (isText$a(container)) {\n        container.insertData(offset, text);\n        return Optional.some(CaretPosition(container, offset + text.length));\n      } else {\n        return getElementFromPosition(pos).map(elm => {\n          const textNode = SugarElement.fromText(text);\n          if (pos.isAtEnd()) {\n            after$4(elm, textNode);\n          } else {\n            before$3(elm, textNode);\n          }\n          return CaretPosition(textNode.dom, text.length);\n        });\n      }\n    };\n    const insertNbspAtPosition = curry(insertTextAtPosition, nbsp);\n    const insertSpaceAtPosition = curry(insertTextAtPosition, ' ');\n\n    const locationToCaretPosition = root => location => location.fold(element => prevPosition(root.dom, CaretPosition.before(element)), element => firstPositionIn(element), element => lastPositionIn(element), element => nextPosition(root.dom, CaretPosition.after(element)));\n    const insertInlineBoundarySpaceOrNbsp = (root, pos) => checkPos => needsToHaveNbsp(root, checkPos) ? insertNbspAtPosition(pos) : insertSpaceAtPosition(pos);\n    const setSelection = editor => pos => {\n      editor.selection.setRng(pos.toRange());\n      editor.nodeChanged();\n      return true;\n    };\n    const insertSpaceOrNbspAtSelection = editor => {\n      const pos = CaretPosition.fromRangeStart(editor.selection.getRng());\n      const root = SugarElement.fromDom(editor.getBody());\n      if (editor.selection.isCollapsed()) {\n        const isInlineTarget$1 = curry(isInlineTarget, editor);\n        const caretPosition = CaretPosition.fromRangeStart(editor.selection.getRng());\n        return readLocation(isInlineTarget$1, editor.getBody(), caretPosition).bind(locationToCaretPosition(root)).map(checkPos => () => insertInlineBoundarySpaceOrNbsp(root, pos)(checkPos).each(setSelection(editor)));\n      } else {\n        return Optional.none();\n      }\n    };\n\n    const executeKeydownOverride$1 = (editor, evt) => {\n      executeWithDelayedAction([{\n          keyCode: VK.SPACEBAR,\n          action: action(insertSpaceOrNbspAtSelection, editor)\n        }], evt).each(applyAction => {\n        evt.preventDefault();\n        const event = fireFakeBeforeInputEvent(editor, 'insertText', { data: ' ' });\n        if (!event.isDefaultPrevented()) {\n          applyAction();\n          fireFakeInputEvent(editor, 'insertText', { data: ' ' });\n        }\n      });\n    };\n    const setup$d = editor => {\n      editor.on('keydown', evt => {\n        if (!evt.isDefaultPrevented()) {\n          executeKeydownOverride$1(editor, evt);\n        }\n      });\n    };\n\n    const tableTabNavigation = editor => {\n      if (hasTableTabNavigation(editor)) {\n        return [\n          {\n            keyCode: VK.TAB,\n            action: action(handleTab, editor, true)\n          },\n          {\n            keyCode: VK.TAB,\n            shiftKey: true,\n            action: action(handleTab, editor, false)\n          }\n        ];\n      } else {\n        return [];\n      }\n    };\n    const executeKeydownOverride = (editor, evt) => {\n      execute([...tableTabNavigation(editor)], evt).each(_ => {\n        evt.preventDefault();\n      });\n    };\n    const setup$c = editor => {\n      editor.on('keydown', evt => {\n        if (!evt.isDefaultPrevented()) {\n          executeKeydownOverride(editor, evt);\n        }\n      });\n    };\n\n    const setup$b = editor => {\n      editor.addShortcut('Meta+P', '', 'mcePrint');\n      setup$j(editor);\n      if (isRtc(editor)) {\n        return Cell(null);\n      } else {\n        const caret = setupSelectedState(editor);\n        setup$l(editor);\n        setup$k(editor, caret);\n        setup$i(editor, caret);\n        setup$h(editor);\n        setup$d(editor);\n        setup$f(editor);\n        setup$c(editor);\n        setup$g(editor, caret);\n        setup$e(editor, caret);\n        return caret;\n      }\n    };\n\n    class NodeChange {\n      constructor(editor) {\n        this.lastPath = [];\n        this.editor = editor;\n        let lastRng;\n        const self = this;\n        if (!('onselectionchange' in editor.getDoc())) {\n          editor.on('NodeChange click mouseup keyup focus', e => {\n            const nativeRng = editor.selection.getRng();\n            const fakeRng = {\n              startContainer: nativeRng.startContainer,\n              startOffset: nativeRng.startOffset,\n              endContainer: nativeRng.endContainer,\n              endOffset: nativeRng.endOffset\n            };\n            if (e.type === 'nodechange' || !isEq$4(fakeRng, lastRng)) {\n              editor.dispatch('SelectionChange');\n            }\n            lastRng = fakeRng;\n          });\n        }\n        editor.on('contextmenu', () => {\n          editor.dispatch('SelectionChange');\n        });\n        editor.on('SelectionChange', () => {\n          const startElm = editor.selection.getStart(true);\n          if (!startElm) {\n            return;\n          }\n          if (hasAnyRanges(editor) && !self.isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {\n            editor.nodeChanged({ selectionChange: true });\n          }\n        });\n        editor.on('mouseup', e => {\n          if (!e.isDefaultPrevented() && hasAnyRanges(editor)) {\n            if (editor.selection.getNode().nodeName === 'IMG') {\n              Delay.setEditorTimeout(editor, () => {\n                editor.nodeChanged();\n              });\n            } else {\n              editor.nodeChanged();\n            }\n          }\n        });\n      }\n      nodeChanged(args = {}) {\n        const selection = this.editor.selection;\n        let node;\n        if (this.editor.initialized && selection && !shouldDisableNodeChange(this.editor) && !this.editor.mode.isReadOnly()) {\n          const root = this.editor.getBody();\n          node = selection.getStart(true) || root;\n          if (node.ownerDocument !== this.editor.getDoc() || !this.editor.dom.isChildOf(node, root)) {\n            node = root;\n          }\n          const parents = [];\n          this.editor.dom.getParent(node, node => {\n            if (node === root) {\n              return true;\n            } else {\n              parents.push(node);\n              return false;\n            }\n          });\n          this.editor.dispatch('NodeChange', {\n            ...args,\n            element: node,\n            parents\n          });\n        }\n      }\n      isSameElementPath(startElm) {\n        let i;\n        const editor = this.editor;\n        const currentPath = reverse(editor.dom.getParents(startElm, always, editor.getBody()));\n        if (currentPath.length === this.lastPath.length) {\n          for (i = currentPath.length; i >= 0; i--) {\n            if (currentPath[i] !== this.lastPath[i]) {\n              break;\n            }\n          }\n          if (i === -1) {\n            this.lastPath = currentPath;\n            return true;\n          }\n        }\n        this.lastPath = currentPath;\n        return false;\n      }\n    }\n\n    const internalMimeType = 'x-tinymce/html';\n    const internalHtmlMime = constant(internalMimeType);\n    const internalMark = '<!-- ' + internalMimeType + ' -->';\n    const mark = html => internalMark + html;\n    const unmark = html => html.replace(internalMark, '');\n    const isMarked = html => html.indexOf(internalMark) !== -1;\n\n    const isPlainText = text => {\n      return !/<(?:\\/?(?!(?:div|p|br|span)>)\\w+|(?:(?!(?:span style=\"white-space:\\s?pre;?\">)|br\\s?\\/>))\\w+\\s[^>]+)>/i.test(text);\n    };\n    const openContainer = (rootTag, rootAttrs) => {\n      let tag = '<' + rootTag;\n      const attrs = mapToArray(rootAttrs, (value, key) => key + '=\"' + Entities.encodeAllRaw(value) + '\"');\n      if (attrs.length) {\n        tag += ' ' + attrs.join(' ');\n      }\n      return tag + '>';\n    };\n    const toBlockElements = (text, rootTag, rootAttrs) => {\n      const blocks = text.split(/\\n\\n/);\n      const tagOpen = openContainer(rootTag, rootAttrs);\n      const tagClose = '</' + rootTag + '>';\n      const paragraphs = map$3(blocks, p => {\n        return p.split(/\\n/).join('<br />');\n      });\n      const stitch = p => {\n        return tagOpen + p + tagClose;\n      };\n      return paragraphs.length === 1 ? paragraphs[0] : map$3(paragraphs, stitch).join('');\n    };\n\n    const pasteBinDefaultContent = '%MCEPASTEBIN%';\n    const create$6 = (editor, lastRngCell) => {\n      const {dom, selection} = editor;\n      const body = editor.getBody();\n      lastRngCell.set(selection.getRng());\n      const pasteBinElm = dom.add(editor.getBody(), 'div', {\n        'id': 'mcepastebin',\n        'class': 'mce-pastebin',\n        'contentEditable': true,\n        'data-mce-bogus': 'all',\n        'style': 'position: fixed; top: 50%; width: 10px; height: 10px; overflow: hidden; opacity: 0'\n      }, pasteBinDefaultContent);\n      if (Env.browser.isFirefox()) {\n        dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) === 'rtl' ? 65535 : -65535);\n      }\n      dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', e => {\n        e.stopPropagation();\n      });\n      pasteBinElm.focus();\n      selection.select(pasteBinElm, true);\n    };\n    const remove = (editor, lastRngCell) => {\n      const dom = editor.dom;\n      if (getEl(editor)) {\n        let pasteBinClone;\n        const lastRng = lastRngCell.get();\n        while (pasteBinClone = getEl(editor)) {\n          dom.remove(pasteBinClone);\n          dom.unbind(pasteBinClone);\n        }\n        if (lastRng) {\n          editor.selection.setRng(lastRng);\n        }\n      }\n      lastRngCell.set(null);\n    };\n    const getEl = editor => editor.dom.get('mcepastebin');\n    const isPasteBin = elm => isNonNullable(elm) && elm.id === 'mcepastebin';\n    const getHtml = editor => {\n      const dom = editor.dom;\n      const copyAndRemove = (toElm, fromElm) => {\n        toElm.appendChild(fromElm);\n        dom.remove(fromElm, true);\n      };\n      const [pasteBinElm, ...pasteBinClones] = filter$5(editor.getBody().childNodes, isPasteBin);\n      each$e(pasteBinClones, pasteBinClone => {\n        copyAndRemove(pasteBinElm, pasteBinClone);\n      });\n      const dirtyWrappers = dom.select('div[id=mcepastebin]', pasteBinElm);\n      for (let i = dirtyWrappers.length - 1; i >= 0; i--) {\n        const cleanWrapper = dom.create('div');\n        pasteBinElm.insertBefore(cleanWrapper, dirtyWrappers[i]);\n        copyAndRemove(cleanWrapper, dirtyWrappers[i]);\n      }\n      return pasteBinElm ? pasteBinElm.innerHTML : '';\n    };\n    const isDefaultPasteBinContent = content => content === pasteBinDefaultContent;\n    const PasteBin = editor => {\n      const lastRng = Cell(null);\n      return {\n        create: () => create$6(editor, lastRng),\n        remove: () => remove(editor, lastRng),\n        getEl: () => getEl(editor),\n        getHtml: () => getHtml(editor),\n        getLastRng: lastRng.get\n      };\n    };\n\n    const filter = (content, items) => {\n      Tools.each(items, v => {\n        if (is$4(v, RegExp)) {\n          content = content.replace(v, '');\n        } else {\n          content = content.replace(v[0], v[1]);\n        }\n      });\n      return content;\n    };\n    const innerText = html => {\n      const schema = Schema();\n      const domParser = DomParser({}, schema);\n      let text = '';\n      const voidElements = schema.getVoidElements();\n      const ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' ');\n      const blockElements = schema.getBlockElements();\n      const walk = node => {\n        const name = node.name, currentNode = node;\n        if (name === 'br') {\n          text += '\\n';\n          return;\n        }\n        if (name === 'wbr') {\n          return;\n        }\n        if (voidElements[name]) {\n          text += ' ';\n        }\n        if (ignoreElements[name]) {\n          text += ' ';\n          return;\n        }\n        if (node.type === 3) {\n          text += node.value;\n        }\n        if (!(node.name in schema.getVoidElements())) {\n          let currentNode = node.firstChild;\n          if (currentNode) {\n            do {\n              walk(currentNode);\n            } while (currentNode = currentNode.next);\n          }\n        }\n        if (blockElements[name] && currentNode.next) {\n          text += '\\n';\n          if (name === 'p') {\n            text += '\\n';\n          }\n        }\n      };\n      html = filter(html, [/<!\\[[^\\]]+\\]>/g]);\n      walk(domParser.parse(html));\n      return text;\n    };\n    const trimHtml = html => {\n      const trimSpaces = (all, s1, s2) => {\n        if (!s1 && !s2) {\n          return ' ';\n        }\n        return nbsp;\n      };\n      html = filter(html, [\n        /^[\\s\\S]*<body[^>]*>\\s*|\\s*<\\/body[^>]*>[\\s\\S]*$/ig,\n        /<!--StartFragment-->|<!--EndFragment-->/g,\n        [\n          /( ?)<span class=\"Apple-converted-space\">\\u00a0<\\/span>( ?)/g,\n          trimSpaces\n        ],\n        /<br class=\"Apple-interchange-newline\">/g,\n        /<br>$/i\n      ]);\n      return html;\n    };\n    const createIdGenerator = prefix => {\n      let count = 0;\n      return () => {\n        return prefix + count++;\n      };\n    };\n    const getImageMimeType = ext => {\n      const lowerExt = ext.toLowerCase();\n      const mimeOverrides = {\n        jpg: 'jpeg',\n        jpe: 'jpeg',\n        jfi: 'jpeg',\n        jif: 'jpeg',\n        jfif: 'jpeg',\n        pjpeg: 'jpeg',\n        pjp: 'jpeg',\n        svg: 'svg+xml'\n      };\n      return Tools.hasOwn(mimeOverrides, lowerExt) ? 'image/' + mimeOverrides[lowerExt] : 'image/' + lowerExt;\n    };\n\n    const preProcess = (editor, html) => {\n      const parser = DomParser({}, editor.schema);\n      parser.addNodeFilter('meta', nodes => {\n        Tools.each(nodes, node => {\n          node.remove();\n        });\n      });\n      const fragment = parser.parse(html, {\n        forced_root_block: false,\n        isRootContent: true\n      });\n      return HtmlSerializer({ validate: true }, editor.schema).serialize(fragment);\n    };\n    const processResult = (content, cancelled) => ({\n      content,\n      cancelled\n    });\n    const postProcessFilter = (editor, html, internal) => {\n      const tempBody = editor.dom.create('div', { style: 'display:none' }, html);\n      const postProcessArgs = firePastePostProcess(editor, tempBody, internal);\n      return processResult(postProcessArgs.node.innerHTML, postProcessArgs.isDefaultPrevented());\n    };\n    const filterContent = (editor, content, internal) => {\n      const preProcessArgs = firePastePreProcess(editor, content, internal);\n      const filteredContent = preProcess(editor, preProcessArgs.content);\n      if (editor.hasEventListeners('PastePostProcess') && !preProcessArgs.isDefaultPrevented()) {\n        return postProcessFilter(editor, filteredContent, internal);\n      } else {\n        return processResult(filteredContent, preProcessArgs.isDefaultPrevented());\n      }\n    };\n    const process = (editor, html, internal) => {\n      return filterContent(editor, html, internal);\n    };\n\n    const pasteHtml$1 = (editor, html) => {\n      editor.insertContent(html, {\n        merge: shouldPasteMergeFormats(editor),\n        paste: true\n      });\n      return true;\n    };\n    const isAbsoluteUrl = url => /^https?:\\/\\/[\\w\\-\\/+=.,!;:&%@^~(){}?#]+$/i.test(url);\n    const isImageUrl = (editor, url) => {\n      return isAbsoluteUrl(url) && exists(getAllowedImageFileTypes(editor), type => endsWith(url.toLowerCase(), `.${ type.toLowerCase() }`));\n    };\n    const createImage = (editor, url, pasteHtmlFn) => {\n      editor.undoManager.extra(() => {\n        pasteHtmlFn(editor, url);\n      }, () => {\n        editor.insertContent('<img src=\"' + url + '\">');\n      });\n      return true;\n    };\n    const createLink = (editor, url, pasteHtmlFn) => {\n      editor.undoManager.extra(() => {\n        pasteHtmlFn(editor, url);\n      }, () => {\n        editor.execCommand('mceInsertLink', false, url);\n      });\n      return true;\n    };\n    const linkSelection = (editor, html, pasteHtmlFn) => !editor.selection.isCollapsed() && isAbsoluteUrl(html) ? createLink(editor, html, pasteHtmlFn) : false;\n    const insertImage = (editor, html, pasteHtmlFn) => isImageUrl(editor, html) ? createImage(editor, html, pasteHtmlFn) : false;\n    const smartInsertContent = (editor, html) => {\n      Tools.each([\n        linkSelection,\n        insertImage,\n        pasteHtml$1\n      ], action => {\n        return !action(editor, html, pasteHtml$1);\n      });\n    };\n    const insertContent = (editor, html, pasteAsText) => {\n      if (pasteAsText || !isSmartPasteEnabled(editor)) {\n        pasteHtml$1(editor, html);\n      } else {\n        smartInsertContent(editor, html);\n      }\n    };\n\n    const uniqueId = createIdGenerator('mceclip');\n    const doPaste = (editor, content, internal, pasteAsText) => {\n      const args = process(editor, content, internal);\n      if (!args.cancelled) {\n        insertContent(editor, args.content, pasteAsText);\n      }\n    };\n    const pasteHtml = (editor, html, internalFlag) => {\n      const internal = internalFlag ? internalFlag : isMarked(html);\n      doPaste(editor, unmark(html), internal, false);\n    };\n    const pasteText = (editor, text) => {\n      const encodedText = editor.dom.encode(text).replace(/\\r\\n/g, '\\n');\n      const normalizedText = normalize$4(encodedText, getPasteTabSpaces(editor));\n      const html = toBlockElements(normalizedText, getForcedRootBlock(editor), getForcedRootBlockAttrs(editor));\n      doPaste(editor, html, false, true);\n    };\n    const getDataTransferItems = dataTransfer => {\n      const items = {};\n      if (dataTransfer && dataTransfer.types) {\n        for (let i = 0; i < dataTransfer.types.length; i++) {\n          const contentType = dataTransfer.types[i];\n          try {\n            items[contentType] = dataTransfer.getData(contentType);\n          } catch (ex) {\n            items[contentType] = '';\n          }\n        }\n      }\n      return items;\n    };\n    const hasContentType = (clipboardContent, mimeType) => mimeType in clipboardContent && clipboardContent[mimeType].length > 0;\n    const hasHtmlOrText = content => hasContentType(content, 'text/html') || hasContentType(content, 'text/plain');\n    const extractFilename = (editor, str) => {\n      const m = str.match(/([\\s\\S]+?)(?:\\.[a-z0-9.]+)$/i);\n      return isNonNullable(m) ? editor.dom.encode(m[1]) : undefined;\n    };\n    const createBlobInfo = (editor, blobCache, file, base64) => {\n      const id = uniqueId();\n      const useFileName = shouldReuseFileName(editor) && isNonNullable(file.name);\n      const name = useFileName ? extractFilename(editor, file.name) : id;\n      const filename = useFileName ? file.name : undefined;\n      const blobInfo = blobCache.create(id, file, base64, name, filename);\n      blobCache.add(blobInfo);\n      return blobInfo;\n    };\n    const pasteImage = (editor, imageItem) => {\n      parseDataUri(imageItem.uri).each(({data, type, base64Encoded}) => {\n        const base64 = base64Encoded ? data : btoa(data);\n        const file = imageItem.file;\n        const blobCache = editor.editorUpload.blobCache;\n        const existingBlobInfo = blobCache.getByData(base64, type);\n        const blobInfo = existingBlobInfo !== null && existingBlobInfo !== void 0 ? existingBlobInfo : createBlobInfo(editor, blobCache, file, base64);\n        pasteHtml(editor, `<img src=\"${ blobInfo.blobUri() }\">`, false);\n      });\n    };\n    const isClipboardEvent = event => event.type === 'paste';\n    const readFilesAsDataUris = items => Promise.all(map$3(items, file => {\n      return blobToDataUri(file).then(uri => ({\n        file,\n        uri\n      }));\n    }));\n    const isImage = editor => {\n      const allowedExtensions = getAllowedImageFileTypes(editor);\n      return file => startsWith(file.type, 'image/') && exists(allowedExtensions, extension => {\n        return getImageMimeType(extension) === file.type;\n      });\n    };\n    const getImagesFromDataTransfer = (editor, dataTransfer) => {\n      const items = dataTransfer.items ? bind$3(from(dataTransfer.items), item => {\n        return item.kind === 'file' ? [item.getAsFile()] : [];\n      }) : [];\n      const files = dataTransfer.files ? from(dataTransfer.files) : [];\n      return filter$5(items.length > 0 ? items : files, isImage(editor));\n    };\n    const pasteImageData = (editor, e, rng) => {\n      const dataTransfer = isClipboardEvent(e) ? e.clipboardData : e.dataTransfer;\n      if (shouldPasteDataImages(editor) && dataTransfer) {\n        const images = getImagesFromDataTransfer(editor, dataTransfer);\n        if (images.length > 0) {\n          e.preventDefault();\n          readFilesAsDataUris(images).then(fileResults => {\n            if (rng) {\n              editor.selection.setRng(rng);\n            }\n            each$e(fileResults, result => {\n              pasteImage(editor, result);\n            });\n          });\n          return true;\n        }\n      }\n      return false;\n    };\n    const isBrokenAndroidClipboardEvent = e => {\n      var _a, _b;\n      return Env.os.isAndroid() && ((_b = (_a = e.clipboardData) === null || _a === void 0 ? void 0 : _a.items) === null || _b === void 0 ? void 0 : _b.length) === 0;\n    };\n    const isKeyboardPasteEvent = e => VK.metaKeyPressed(e) && e.keyCode === 86 || e.shiftKey && e.keyCode === 45;\n    const insertClipboardContent = (editor, clipboardContent, html, plainTextMode) => {\n      let content = trimHtml(html);\n      const isInternal = hasContentType(clipboardContent, internalHtmlMime()) || isMarked(html);\n      const isPlainTextHtml = !isInternal && isPlainText(content);\n      const isAbsoluteUrl$1 = isAbsoluteUrl(content);\n      if (isDefaultPasteBinContent(content) || !content.length || isPlainTextHtml && !isAbsoluteUrl$1) {\n        plainTextMode = true;\n      }\n      if (plainTextMode || isAbsoluteUrl$1) {\n        if (hasContentType(clipboardContent, 'text/plain') && isPlainTextHtml) {\n          content = clipboardContent['text/plain'];\n        } else {\n          content = innerText(content);\n        }\n      }\n      if (isDefaultPasteBinContent(content)) {\n        return;\n      }\n      if (plainTextMode) {\n        pasteText(editor, content);\n      } else {\n        pasteHtml(editor, content, isInternal);\n      }\n    };\n    const registerEventHandlers = (editor, pasteBin, pasteFormat) => {\n      let keyboardPastePlainTextState;\n      const getLastRng = () => pasteBin.getLastRng() || editor.selection.getRng();\n      editor.on('keydown', e => {\n        if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {\n          keyboardPastePlainTextState = e.shiftKey && e.keyCode === 86;\n        }\n      });\n      editor.on('paste', e => {\n        if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) {\n          return;\n        }\n        const plainTextMode = pasteFormat.get() === 'text' || keyboardPastePlainTextState;\n        keyboardPastePlainTextState = false;\n        const clipboardContent = getDataTransferItems(e.clipboardData);\n        if (!hasHtmlOrText(clipboardContent) && pasteImageData(editor, e, getLastRng())) {\n          return;\n        }\n        if (hasContentType(clipboardContent, 'text/html')) {\n          e.preventDefault();\n          insertClipboardContent(editor, clipboardContent, clipboardContent['text/html'], plainTextMode);\n        } else {\n          pasteBin.create();\n          Delay.setEditorTimeout(editor, () => {\n            const html = pasteBin.getHtml();\n            pasteBin.remove();\n            insertClipboardContent(editor, clipboardContent, html, plainTextMode);\n          }, 0);\n        }\n      });\n    };\n    const registerDataImageFilter = editor => {\n      const isWebKitFakeUrl = src => startsWith(src, 'webkit-fake-url');\n      const isDataUri = src => startsWith(src, 'data:');\n      const isPasteInsert = args => {\n        var _a;\n        return ((_a = args.data) === null || _a === void 0 ? void 0 : _a.paste) === true;\n      };\n      editor.parser.addNodeFilter('img', (nodes, name, args) => {\n        if (!shouldPasteDataImages(editor) && isPasteInsert(args)) {\n          for (const node of nodes) {\n            const src = node.attr('src');\n            if (isString(src) && !node.attr('data-mce-object') && src !== Env.transparentSrc) {\n              if (isWebKitFakeUrl(src)) {\n                node.remove();\n              } else if (!shouldAllowHtmlDataUrls(editor) && isDataUri(src)) {\n                node.remove();\n              }\n            }\n          }\n        }\n      });\n    };\n    const registerEventsAndFilters = (editor, pasteBin, pasteFormat) => {\n      registerEventHandlers(editor, pasteBin, pasteFormat);\n      registerDataImageFilter(editor);\n    };\n\n    const togglePlainTextPaste = (editor, pasteFormat) => {\n      if (pasteFormat.get() === 'text') {\n        pasteFormat.set('html');\n        firePastePlainTextToggle(editor, false);\n      } else {\n        pasteFormat.set('text');\n        firePastePlainTextToggle(editor, true);\n      }\n      editor.focus();\n    };\n    const register$1 = (editor, pasteFormat) => {\n      editor.addCommand('mceTogglePlainTextPaste', () => {\n        togglePlainTextPaste(editor, pasteFormat);\n      });\n      editor.addCommand('mceInsertClipboardContent', (ui, value) => {\n        if (value.html) {\n          pasteHtml(editor, value.html, value.internal);\n        }\n        if (value.text) {\n          pasteText(editor, value.text);\n        }\n      });\n    };\n\n    const setHtml5Clipboard = (clipboardData, html, text) => {\n      if (clipboardData) {\n        try {\n          clipboardData.clearData();\n          clipboardData.setData('text/html', html);\n          clipboardData.setData('text/plain', text);\n          clipboardData.setData(internalHtmlMime(), html);\n          return true;\n        } catch (e) {\n          return false;\n        }\n      } else {\n        return false;\n      }\n    };\n    const setClipboardData = (evt, data, fallback, done) => {\n      if (setHtml5Clipboard(evt.clipboardData, data.html, data.text)) {\n        evt.preventDefault();\n        done();\n      } else {\n        fallback(data.html, done);\n      }\n    };\n    const fallback = editor => (html, done) => {\n      const {dom, selection} = editor;\n      const outer = dom.create('div', {\n        'contenteditable': 'false',\n        'data-mce-bogus': 'all'\n      });\n      const inner = dom.create('div', { contenteditable: 'true' }, html);\n      dom.setStyles(outer, {\n        position: 'fixed',\n        top: '0',\n        left: '-3000px',\n        width: '1000px',\n        overflow: 'hidden'\n      });\n      outer.appendChild(inner);\n      dom.add(editor.getBody(), outer);\n      const range = selection.getRng();\n      inner.focus();\n      const offscreenRange = dom.createRng();\n      offscreenRange.selectNodeContents(inner);\n      selection.setRng(offscreenRange);\n      Delay.setEditorTimeout(editor, () => {\n        selection.setRng(range);\n        dom.remove(outer);\n        done();\n      }, 0);\n    };\n    const getData = editor => ({\n      html: mark(editor.selection.getContent({ contextual: true })),\n      text: editor.selection.getContent({ format: 'text' })\n    });\n    const isTableSelection = editor => !!editor.dom.getParent(editor.selection.getStart(), 'td[data-mce-selected],th[data-mce-selected]', editor.getBody());\n    const hasSelectedContent = editor => !editor.selection.isCollapsed() || isTableSelection(editor);\n    const cut = editor => evt => {\n      if (!evt.isDefaultPrevented() && hasSelectedContent(editor)) {\n        setClipboardData(evt, getData(editor), fallback(editor), () => {\n          if (Env.browser.isChromium() || Env.browser.isFirefox()) {\n            const rng = editor.selection.getRng();\n            Delay.setEditorTimeout(editor, () => {\n              editor.selection.setRng(rng);\n              editor.execCommand('Delete');\n            }, 0);\n          } else {\n            editor.execCommand('Delete');\n          }\n        });\n      }\n    };\n    const copy = editor => evt => {\n      if (!evt.isDefaultPrevented() && hasSelectedContent(editor)) {\n        setClipboardData(evt, getData(editor), fallback(editor), noop);\n      }\n    };\n    const register = editor => {\n      editor.on('cut', cut(editor));\n      editor.on('copy', copy(editor));\n    };\n\n    const getCaretRangeFromEvent = (editor, e) => {\n      var _a, _b;\n      return RangeUtils.getCaretRangeFromPoint((_a = e.clientX) !== null && _a !== void 0 ? _a : 0, (_b = e.clientY) !== null && _b !== void 0 ? _b : 0, editor.getDoc());\n    };\n    const isPlainTextFileUrl = content => {\n      const plainTextContent = content['text/plain'];\n      return plainTextContent ? plainTextContent.indexOf('file://') === 0 : false;\n    };\n    const setFocusedRange = (editor, rng) => {\n      editor.focus();\n      if (rng) {\n        editor.selection.setRng(rng);\n      }\n    };\n    const hasImage = dataTransfer => exists(dataTransfer.files, file => /^image\\//.test(file.type));\n    const setup$a = (editor, draggingInternallyState) => {\n      if (shouldPasteBlockDrop(editor)) {\n        editor.on('dragend dragover draggesture dragdrop drop drag', e => {\n          e.preventDefault();\n          e.stopPropagation();\n        });\n      }\n      if (!shouldPasteDataImages(editor)) {\n        editor.on('drop', e => {\n          const dataTransfer = e.dataTransfer;\n          if (dataTransfer && hasImage(dataTransfer)) {\n            e.preventDefault();\n          }\n        });\n      }\n      editor.on('drop', e => {\n        if (e.isDefaultPrevented() || draggingInternallyState.get()) {\n          return;\n        }\n        const rng = getCaretRangeFromEvent(editor, e);\n        if (isNullable(rng)) {\n          return;\n        }\n        const dropContent = getDataTransferItems(e.dataTransfer);\n        const internal = hasContentType(dropContent, internalHtmlMime());\n        if ((!hasHtmlOrText(dropContent) || isPlainTextFileUrl(dropContent)) && pasteImageData(editor, e, rng)) {\n          return;\n        }\n        const internalContent = dropContent[internalHtmlMime()];\n        const content = internalContent || dropContent['text/html'] || dropContent['text/plain'];\n        if (content) {\n          e.preventDefault();\n          Delay.setEditorTimeout(editor, () => {\n            editor.undoManager.transact(() => {\n              if (internalContent) {\n                editor.execCommand('Delete');\n              }\n              setFocusedRange(editor, rng);\n              const trimmedContent = trimHtml(content);\n              if (dropContent['text/html']) {\n                pasteHtml(editor, trimmedContent, internal);\n              } else {\n                pasteText(editor, trimmedContent);\n              }\n            });\n          });\n        }\n      });\n      editor.on('dragstart', _e => {\n        draggingInternallyState.set(true);\n      });\n      editor.on('dragover dragend', e => {\n        if (shouldPasteDataImages(editor) && !draggingInternallyState.get()) {\n          e.preventDefault();\n          setFocusedRange(editor, getCaretRangeFromEvent(editor, e));\n        }\n        if (e.type === 'dragend') {\n          draggingInternallyState.set(false);\n        }\n      });\n    };\n\n    const setup$9 = editor => {\n      const processEvent = f => e => {\n        f(editor, e);\n      };\n      const preProcess = getPastePreProcess(editor);\n      if (isFunction(preProcess)) {\n        editor.on('PastePreProcess', processEvent(preProcess));\n      }\n      const postProcess = getPastePostProcess(editor);\n      if (isFunction(postProcess)) {\n        editor.on('PastePostProcess', processEvent(postProcess));\n      }\n    };\n\n    const addPreProcessFilter = (editor, filterFunc) => {\n      editor.on('PastePreProcess', e => {\n        e.content = filterFunc(editor, e.content, e.internal);\n      });\n    };\n    const rgbRegExp = /rgb\\s*\\(\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*\\)/gi;\n    const rgbToHex = value => Tools.trim(value).replace(rgbRegExp, rgbaToHexString).toLowerCase();\n    const removeWebKitStyles = (editor, content, internal) => {\n      const webKitStylesOption = getPasteWebkitStyles(editor);\n      if (internal || webKitStylesOption === 'all' || !shouldPasteRemoveWebKitStyles(editor)) {\n        return content;\n      }\n      const webKitStyles = webKitStylesOption ? webKitStylesOption.split(/[, ]/) : [];\n      if (webKitStyles && webKitStylesOption !== 'none') {\n        const dom = editor.dom, node = editor.selection.getNode();\n        content = content.replace(/(<[^>]+) style=\"([^\"]*)\"([^>]*>)/gi, (all, before, value, after) => {\n          const inputStyles = dom.parseStyle(dom.decode(value));\n          const outputStyles = {};\n          for (let i = 0; i < webKitStyles.length; i++) {\n            const inputValue = inputStyles[webKitStyles[i]];\n            let compareInput = inputValue;\n            let currentValue = dom.getStyle(node, webKitStyles[i], true);\n            if (/color/.test(webKitStyles[i])) {\n              compareInput = rgbToHex(compareInput);\n              currentValue = rgbToHex(currentValue);\n            }\n            if (currentValue !== compareInput) {\n              outputStyles[webKitStyles[i]] = inputValue;\n            }\n          }\n          const outputStyle = dom.serializeStyle(outputStyles, 'span');\n          if (outputStyle) {\n            return before + ' style=\"' + outputStyle + '\"' + after;\n          }\n          return before + after;\n        });\n      } else {\n        content = content.replace(/(<[^>]+) style=\"([^\"]*)\"([^>]*>)/gi, '$1$3');\n      }\n      content = content.replace(/(<[^>]+) data-mce-style=\"([^\"]+)\"([^>]*>)/gi, (all, before, value, after) => {\n        return before + ' style=\"' + value + '\"' + after;\n      });\n      return content;\n    };\n    const setup$8 = editor => {\n      if (Env.browser.isChromium() || Env.browser.isSafari()) {\n        addPreProcessFilter(editor, removeWebKitStyles);\n      }\n    };\n\n    const setup$7 = editor => {\n      const draggingInternallyState = Cell(false);\n      const pasteFormat = Cell(isPasteAsTextEnabled(editor) ? 'text' : 'html');\n      const pasteBin = PasteBin(editor);\n      setup$8(editor);\n      register$1(editor, pasteFormat);\n      setup$9(editor);\n      editor.on('PreInit', () => {\n        register(editor);\n        setup$a(editor, draggingInternallyState);\n        registerEventsAndFilters(editor, pasteBin, pasteFormat);\n      });\n    };\n\n    const preventSummaryToggle = editor => {\n      editor.on('click', e => {\n        if (editor.dom.getParent(e.target, 'details')) {\n          e.preventDefault();\n        }\n      });\n    };\n    const filterDetails = editor => {\n      editor.parser.addNodeFilter('details', elms => {\n        each$e(elms, details => {\n          details.attr('data-mce-open', details.attr('open'));\n          details.attr('open', 'open');\n        });\n      });\n      editor.serializer.addNodeFilter('details', elms => {\n        each$e(elms, details => {\n          const open = details.attr('data-mce-open');\n          details.attr('open', isString(open) ? open : null);\n          details.attr('data-mce-open', null);\n        });\n      });\n    };\n    const setup$6 = editor => {\n      preventSummaryToggle(editor);\n      filterDetails(editor);\n    };\n\n    const isBr = isBr$6;\n    const isText = isText$a;\n    const isContentEditableFalse$2 = elm => isContentEditableFalse$a(elm.dom);\n    const isContentEditableTrue = elm => isContentEditableTrue$3(elm.dom);\n    const isRoot = rootNode => elm => eq(SugarElement.fromDom(rootNode), elm);\n    const getClosestScope = (node, rootNode) => closest$4(SugarElement.fromDom(node), elm => isContentEditableTrue(elm) || isBlock$2(elm), isRoot(rootNode)).getOr(SugarElement.fromDom(rootNode)).dom;\n    const getClosestCef = (node, rootNode) => closest$4(SugarElement.fromDom(node), isContentEditableFalse$2, isRoot(rootNode));\n    const findEdgeCaretCandidate = (startNode, scope, forward) => {\n      const walker = new DomTreeWalker(startNode, scope);\n      const next = forward ? walker.next.bind(walker) : walker.prev.bind(walker);\n      let result = startNode;\n      for (let current = forward ? startNode : next(); current && !isBr(current); current = next()) {\n        if (isCaretCandidate$3(current)) {\n          result = current;\n        }\n      }\n      return result;\n    };\n    const findClosestBlockRange = (startRng, rootNode) => {\n      const startPos = CaretPosition.fromRangeStart(startRng);\n      const clickNode = startPos.getNode();\n      const scope = getClosestScope(clickNode, rootNode);\n      const startNode = findEdgeCaretCandidate(clickNode, scope, false);\n      const endNode = findEdgeCaretCandidate(clickNode, scope, true);\n      const rng = document.createRange();\n      getClosestCef(startNode, scope).fold(() => {\n        if (isText(startNode)) {\n          rng.setStart(startNode, 0);\n        } else {\n          rng.setStartBefore(startNode);\n        }\n      }, cef => rng.setStartBefore(cef.dom));\n      getClosestCef(endNode, scope).fold(() => {\n        if (isText(endNode)) {\n          rng.setEnd(endNode, endNode.data.length);\n        } else {\n          rng.setEndAfter(endNode);\n        }\n      }, cef => rng.setEndAfter(cef.dom));\n      return rng;\n    };\n    const onTripleClickSelect = editor => {\n      const rng = findClosestBlockRange(editor.selection.getRng(), editor.getBody());\n      editor.selection.setRng(normalize(rng));\n    };\n    const setup$5 = editor => {\n      editor.on('mousedown', e => {\n        if (e.detail >= 3) {\n          e.preventDefault();\n          onTripleClickSelect(editor);\n        }\n      });\n    };\n\n    var FakeCaretPosition;\n    (function (FakeCaretPosition) {\n      FakeCaretPosition['Before'] = 'before';\n      FakeCaretPosition['After'] = 'after';\n    }(FakeCaretPosition || (FakeCaretPosition = {})));\n    const distanceToRectLeft = (clientRect, clientX) => Math.abs(clientRect.left - clientX);\n    const distanceToRectRight = (clientRect, clientX) => Math.abs(clientRect.right - clientX);\n    const isInsideY = (clientY, clientRect) => clientY >= clientRect.top && clientY <= clientRect.bottom;\n    const collidesY = (r1, r2) => r1.top < r2.bottom && r1.bottom > r2.top;\n    const isOverlapping = (r1, r2) => {\n      const overlap = overlapY(r1, r2) / Math.min(r1.height, r2.height);\n      return collidesY(r1, r2) && overlap > 0.5;\n    };\n    const splitRectsPerAxis = (rects, y) => {\n      const intersectingRects = filter$5(rects, rect => isInsideY(y, rect));\n      return boundingClientRectFromRects(intersectingRects).fold(() => [\n        [],\n        rects\n      ], boundingRect => {\n        const {\n          pass: horizontal,\n          fail: vertical\n        } = partition$2(rects, rect => isOverlapping(rect, boundingRect));\n        return [\n          horizontal,\n          vertical\n        ];\n      });\n    };\n    const clientInfo = (rect, clientX) => {\n      return {\n        node: rect.node,\n        position: distanceToRectLeft(rect, clientX) < distanceToRectRight(rect, clientX) ? FakeCaretPosition.Before : FakeCaretPosition.After\n      };\n    };\n    const horizontalDistance = (rect, x, _y) => x > rect.left && x < rect.right ? 0 : Math.min(Math.abs(rect.left - x), Math.abs(rect.right - x));\n    const closestChildCaretCandidateNodeRect = (children, clientX, clientY) => {\n      const caretCandidateRect = rect => {\n        if (isCaretCandidate$3(rect.node)) {\n          return Optional.some(rect);\n        } else if (isElement$6(rect.node)) {\n          return closestChildCaretCandidateNodeRect(from(rect.node.childNodes), clientX, clientY);\n        } else {\n          return Optional.none();\n        }\n      };\n      const getClosestTextNode = (rects, distance) => {\n        if (rects.length >= 2) {\n          const r1 = caretCandidateRect(rects[0]).getOr(rects[0]);\n          const r2 = caretCandidateRect(rects[1]).getOr(rects[1]);\n          const deltaDistance = Math.abs(distance(r1, clientX, clientY) - distance(r2, clientX, clientY));\n          if (deltaDistance < 2) {\n            if (isText$a(r1.node)) {\n              return Optional.some(r1);\n            } else if (isText$a(r2.node)) {\n              return Optional.some(r2);\n            }\n          }\n        }\n        return Optional.none();\n      };\n      const findClosestCaretCandidateNodeRect = (rects, distance) => {\n        const sortedRects = sort(rects, (r1, r2) => distance(r1, clientX, clientY) - distance(r2, clientX, clientY));\n        return getClosestTextNode(sortedRects, distance).orThunk(() => findMap(sortedRects, caretCandidateRect));\n      };\n      const [horizontalRects, verticalRects] = splitRectsPerAxis(getClientRects(children), clientY);\n      const {\n        pass: above,\n        fail: below\n      } = partition$2(verticalRects, rect => rect.top < clientY);\n      return findClosestCaretCandidateNodeRect(horizontalRects, horizontalDistance).orThunk(() => findClosestCaretCandidateNodeRect(below, distanceToRectEdgeFromXY)).orThunk(() => findClosestCaretCandidateNodeRect(above, distanceToRectEdgeFromXY));\n    };\n    const traverseUp = (rootElm, scope, clientX, clientY) => {\n      const helper = (scope, prevScope) => {\n        const isDragGhostContainer = node => isElement$6(node) && node.classList.contains('mce-drag-container');\n        const childNodesWithoutGhost = filter$5(scope.dom.childNodes, not(isDragGhostContainer));\n        return prevScope.fold(() => closestChildCaretCandidateNodeRect(childNodesWithoutGhost, clientX, clientY), prevScope => {\n          const uncheckedChildren = filter$5(childNodesWithoutGhost, node => node !== prevScope.dom);\n          return closestChildCaretCandidateNodeRect(uncheckedChildren, clientX, clientY);\n        }).orThunk(() => {\n          const parent = eq(scope, rootElm) ? Optional.none() : parentElement(scope);\n          return parent.bind(newScope => helper(newScope, Optional.some(scope)));\n        });\n      };\n      return helper(scope, Optional.none());\n    };\n    const closestCaretCandidateNodeRect = (root, clientX, clientY) => {\n      const rootElm = SugarElement.fromDom(root);\n      const ownerDoc = documentOrOwner(rootElm);\n      const elementAtPoint = SugarElement.fromPoint(ownerDoc, clientX, clientY).filter(elm => contains(rootElm, elm));\n      const element = elementAtPoint.getOr(rootElm);\n      return traverseUp(rootElm, element, clientX, clientY);\n    };\n    const closestFakeCaretCandidate = (root, clientX, clientY) => closestCaretCandidateNodeRect(root, clientX, clientY).filter(rect => isFakeCaretTarget(rect.node)).map(rect => clientInfo(rect, clientX));\n\n    const getAbsolutePosition = elm => {\n      var _a, _b;\n      const clientRect = elm.getBoundingClientRect();\n      const doc = elm.ownerDocument;\n      const docElem = doc.documentElement;\n      const win = doc.defaultView;\n      return {\n        top: clientRect.top + ((_a = win === null || win === void 0 ? void 0 : win.scrollY) !== null && _a !== void 0 ? _a : 0) - docElem.clientTop,\n        left: clientRect.left + ((_b = win === null || win === void 0 ? void 0 : win.scrollX) !== null && _b !== void 0 ? _b : 0) - docElem.clientLeft\n      };\n    };\n    const getBodyPosition = editor => editor.inline ? getAbsolutePosition(editor.getBody()) : {\n      left: 0,\n      top: 0\n    };\n    const getScrollPosition = editor => {\n      const body = editor.getBody();\n      return editor.inline ? {\n        left: body.scrollLeft,\n        top: body.scrollTop\n      } : {\n        left: 0,\n        top: 0\n      };\n    };\n    const getBodyScroll = editor => {\n      const body = editor.getBody(), docElm = editor.getDoc().documentElement;\n      const inlineScroll = {\n        left: body.scrollLeft,\n        top: body.scrollTop\n      };\n      const iframeScroll = {\n        left: body.scrollLeft || docElm.scrollLeft,\n        top: body.scrollTop || docElm.scrollTop\n      };\n      return editor.inline ? inlineScroll : iframeScroll;\n    };\n    const getMousePosition = (editor, event) => {\n      if (event.target.ownerDocument !== editor.getDoc()) {\n        const iframePosition = getAbsolutePosition(editor.getContentAreaContainer());\n        const scrollPosition = getBodyScroll(editor);\n        return {\n          left: event.pageX - iframePosition.left + scrollPosition.left,\n          top: event.pageY - iframePosition.top + scrollPosition.top\n        };\n      }\n      return {\n        left: event.pageX,\n        top: event.pageY\n      };\n    };\n    const calculatePosition = (bodyPosition, scrollPosition, mousePosition) => ({\n      pageX: mousePosition.left - bodyPosition.left + scrollPosition.left,\n      pageY: mousePosition.top - bodyPosition.top + scrollPosition.top\n    });\n    const calc = (editor, event) => calculatePosition(getBodyPosition(editor), getScrollPosition(editor), getMousePosition(editor, event));\n\n    const scrollPixelsPerInterval = 32;\n    const scrollIntervalValue = 100;\n    const mouseRangeToTriggerScrollInsideEditor = 8;\n    const mouseRangeToTriggerScrollOutsideEditor = 16;\n    const isContentEditableFalse$1 = isContentEditableFalse$a;\n    const isContentEditable = or(isContentEditableFalse$1, isContentEditableTrue$3);\n    const isDraggable = (rootElm, elm) => isContentEditableFalse$1(elm) && elm !== rootElm;\n    const isValidDropTarget = (editor, targetElement, dragElement) => {\n      if (isNullable(targetElement)) {\n        return false;\n      } else if (targetElement === dragElement || editor.dom.isChildOf(targetElement, dragElement)) {\n        return false;\n      } else {\n        return !isContentEditableFalse$1(targetElement);\n      }\n    };\n    const cloneElement = elm => {\n      const cloneElm = elm.cloneNode(true);\n      cloneElm.removeAttribute('data-mce-selected');\n      return cloneElm;\n    };\n    const createGhost = (editor, elm, width, height) => {\n      const dom = editor.dom;\n      const clonedElm = elm.cloneNode(true);\n      dom.setStyles(clonedElm, {\n        width,\n        height\n      });\n      dom.setAttrib(clonedElm, 'data-mce-selected', null);\n      const ghostElm = dom.create('div', {\n        'class': 'mce-drag-container',\n        'data-mce-bogus': 'all',\n        'unselectable': 'on',\n        'contenteditable': 'false'\n      });\n      dom.setStyles(ghostElm, {\n        position: 'absolute',\n        opacity: 0.5,\n        overflow: 'hidden',\n        border: 0,\n        padding: 0,\n        margin: 0,\n        width,\n        height\n      });\n      dom.setStyles(clonedElm, {\n        margin: 0,\n        boxSizing: 'border-box'\n      });\n      ghostElm.appendChild(clonedElm);\n      return ghostElm;\n    };\n    const appendGhostToBody = (ghostElm, bodyElm) => {\n      if (ghostElm.parentNode !== bodyElm) {\n        bodyElm.appendChild(ghostElm);\n      }\n    };\n    const scrollEditor = (direction, amount) => win => () => {\n      const current = direction === 'left' ? win.scrollX : win.scrollY;\n      win.scroll({\n        [direction]: current + amount,\n        behavior: 'smooth'\n      });\n    };\n    const scrollLeft = scrollEditor('left', -scrollPixelsPerInterval);\n    const scrollRight = scrollEditor('left', scrollPixelsPerInterval);\n    const scrollUp = scrollEditor('top', -scrollPixelsPerInterval);\n    const scrollDown = scrollEditor('top', scrollPixelsPerInterval);\n    const moveGhost = (ghostElm, position, width, height, maxX, maxY, mouseY, mouseX, contentAreaContainer, win, state, mouseEventOriginatedFromWithinTheEditor) => {\n      let overflowX = 0, overflowY = 0;\n      ghostElm.style.left = position.pageX + 'px';\n      ghostElm.style.top = position.pageY + 'px';\n      if (position.pageX + width > maxX) {\n        overflowX = position.pageX + width - maxX;\n      }\n      if (position.pageY + height > maxY) {\n        overflowY = position.pageY + height - maxY;\n      }\n      ghostElm.style.width = width - overflowX + 'px';\n      ghostElm.style.height = height - overflowY + 'px';\n      const clientHeight = contentAreaContainer.clientHeight;\n      const clientWidth = contentAreaContainer.clientWidth;\n      const outerMouseY = mouseY + contentAreaContainer.getBoundingClientRect().top;\n      const outerMouseX = mouseX + contentAreaContainer.getBoundingClientRect().left;\n      state.on(state => {\n        state.intervalId.clear();\n        if (state.dragging && mouseEventOriginatedFromWithinTheEditor) {\n          if (mouseY + mouseRangeToTriggerScrollInsideEditor >= clientHeight) {\n            state.intervalId.set(scrollDown(win));\n          } else if (mouseY - mouseRangeToTriggerScrollInsideEditor <= 0) {\n            state.intervalId.set(scrollUp(win));\n          } else if (mouseX + mouseRangeToTriggerScrollInsideEditor >= clientWidth) {\n            state.intervalId.set(scrollRight(win));\n          } else if (mouseX - mouseRangeToTriggerScrollInsideEditor <= 0) {\n            state.intervalId.set(scrollLeft(win));\n          } else if (outerMouseY + mouseRangeToTriggerScrollOutsideEditor >= window.innerHeight) {\n            state.intervalId.set(scrollDown(window));\n          } else if (outerMouseY - mouseRangeToTriggerScrollOutsideEditor <= 0) {\n            state.intervalId.set(scrollUp(window));\n          } else if (outerMouseX + mouseRangeToTriggerScrollOutsideEditor >= window.innerWidth) {\n            state.intervalId.set(scrollRight(window));\n          } else if (outerMouseX - mouseRangeToTriggerScrollOutsideEditor <= 0) {\n            state.intervalId.set(scrollLeft(window));\n          }\n        }\n      });\n    };\n    const removeElement = elm => {\n      if (elm && elm.parentNode) {\n        elm.parentNode.removeChild(elm);\n      }\n    };\n    const isLeftMouseButtonPressed = e => e.button === 0;\n    const applyRelPos = (state, position) => ({\n      pageX: position.pageX - state.relX,\n      pageY: position.pageY + 5\n    });\n    const start = (state, editor) => e => {\n      if (isLeftMouseButtonPressed(e)) {\n        const ceElm = find$2(editor.dom.getParents(e.target), isContentEditable).getOr(null);\n        if (isNonNullable(ceElm) && isDraggable(editor.getBody(), ceElm)) {\n          const elmPos = editor.dom.getPos(ceElm);\n          const bodyElm = editor.getBody();\n          const docElm = editor.getDoc().documentElement;\n          state.set({\n            element: ceElm,\n            dragging: false,\n            screenX: e.screenX,\n            screenY: e.screenY,\n            maxX: (editor.inline ? bodyElm.scrollWidth : docElm.offsetWidth) - 2,\n            maxY: (editor.inline ? bodyElm.scrollHeight : docElm.offsetHeight) - 2,\n            relX: e.pageX - elmPos.x,\n            relY: e.pageY - elmPos.y,\n            width: ceElm.offsetWidth,\n            height: ceElm.offsetHeight,\n            ghost: createGhost(editor, ceElm, ceElm.offsetWidth, ceElm.offsetHeight),\n            intervalId: repeatable(scrollIntervalValue)\n          });\n        }\n      }\n    };\n    const move = (state, editor) => {\n      const throttledPlaceCaretAt = first$1((clientX, clientY) => {\n        editor._selectionOverrides.hideFakeCaret();\n        closestFakeCaretCandidate(editor.getBody(), clientX, clientY).fold(() => editor.selection.placeCaretAt(clientX, clientY), caretInfo => {\n          const range = editor._selectionOverrides.showCaret(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false);\n          if (range) {\n            editor.selection.setRng(range);\n          } else {\n            editor.selection.placeCaretAt(clientX, clientY);\n          }\n        });\n      }, 0);\n      editor.on('remove', throttledPlaceCaretAt.cancel);\n      const state_ = state;\n      return e => state.on(state => {\n        const movement = Math.max(Math.abs(e.screenX - state.screenX), Math.abs(e.screenY - state.screenY));\n        if (!state.dragging && movement > 10) {\n          const args = editor.dispatch('dragstart', { target: state.element });\n          if (args.isDefaultPrevented()) {\n            return;\n          }\n          state.dragging = true;\n          editor.focus();\n        }\n        if (state.dragging) {\n          const mouseEventOriginatedFromWithinTheEditor = e.currentTarget === editor.getDoc().documentElement;\n          const targetPos = applyRelPos(state, calc(editor, e));\n          appendGhostToBody(state.ghost, editor.getBody());\n          moveGhost(state.ghost, targetPos, state.width, state.height, state.maxX, state.maxY, e.clientY, e.clientX, editor.getContentAreaContainer(), editor.getWin(), state_, mouseEventOriginatedFromWithinTheEditor);\n          throttledPlaceCaretAt.throttle(e.clientX, e.clientY);\n        }\n      });\n    };\n    const getRawTarget = selection => {\n      const sel = selection.getSel();\n      if (isNonNullable(sel)) {\n        const rng = sel.getRangeAt(0);\n        const startContainer = rng.startContainer;\n        return isText$a(startContainer) ? startContainer.parentNode : startContainer;\n      } else {\n        return null;\n      }\n    };\n    const drop = (state, editor) => e => {\n      state.on(state => {\n        state.intervalId.clear();\n        if (state.dragging) {\n          if (isValidDropTarget(editor, getRawTarget(editor.selection), state.element)) {\n            const targetClone = cloneElement(state.element);\n            const args = editor.dispatch('drop', {\n              clientX: e.clientX,\n              clientY: e.clientY\n            });\n            if (!args.isDefaultPrevented()) {\n              editor.undoManager.transact(() => {\n                removeElement(state.element);\n                editor.insertContent(editor.dom.getOuterHTML(targetClone));\n                editor._selectionOverrides.hideFakeCaret();\n              });\n            }\n          }\n          editor.dispatch('dragend');\n        }\n      });\n      removeDragState(state);\n    };\n    const stop = (state, editor) => () => {\n      state.on(state => {\n        state.intervalId.clear();\n        if (state.dragging) {\n          editor.dispatch('dragend');\n        }\n      });\n      removeDragState(state);\n    };\n    const removeDragState = state => {\n      state.on(state => {\n        state.intervalId.clear();\n        removeElement(state.ghost);\n      });\n      state.clear();\n    };\n    const bindFakeDragEvents = editor => {\n      const state = value$2();\n      const pageDom = DOMUtils.DOM;\n      const rootDocument = document;\n      const dragStartHandler = start(state, editor);\n      const dragHandler = move(state, editor);\n      const dropHandler = drop(state, editor);\n      const dragEndHandler = stop(state, editor);\n      editor.on('mousedown', dragStartHandler);\n      editor.on('mousemove', dragHandler);\n      editor.on('mouseup', dropHandler);\n      pageDom.bind(rootDocument, 'mousemove', dragHandler);\n      pageDom.bind(rootDocument, 'mouseup', dragEndHandler);\n      editor.on('remove', () => {\n        pageDom.unbind(rootDocument, 'mousemove', dragHandler);\n        pageDom.unbind(rootDocument, 'mouseup', dragEndHandler);\n      });\n      editor.on('keydown', e => {\n        if (e.keyCode === VK.ESC) {\n          dragEndHandler();\n        }\n      });\n    };\n    const blockUnsupportedFileDrop = editor => {\n      const preventFileDrop = e => {\n        if (!e.isDefaultPrevented()) {\n          const dataTransfer = e.dataTransfer;\n          if (dataTransfer && (contains$2(dataTransfer.types, 'Files') || dataTransfer.files.length > 0)) {\n            e.preventDefault();\n            if (e.type === 'drop') {\n              displayError(editor, 'Dropped file type is not supported');\n            }\n          }\n        }\n      };\n      const preventFileDropIfUIElement = e => {\n        if (isUIElement(editor, e.target)) {\n          preventFileDrop(e);\n        }\n      };\n      const setup = () => {\n        const pageDom = DOMUtils.DOM;\n        const dom = editor.dom;\n        const doc = document;\n        const editorRoot = editor.inline ? editor.getBody() : editor.getDoc();\n        const eventNames = [\n          'drop',\n          'dragover'\n        ];\n        each$e(eventNames, name => {\n          pageDom.bind(doc, name, preventFileDropIfUIElement);\n          dom.bind(editorRoot, name, preventFileDrop);\n        });\n        editor.on('remove', () => {\n          each$e(eventNames, name => {\n            pageDom.unbind(doc, name, preventFileDropIfUIElement);\n            dom.unbind(editorRoot, name, preventFileDrop);\n          });\n        });\n      };\n      editor.on('init', () => {\n        Delay.setEditorTimeout(editor, setup, 0);\n      });\n    };\n    const init$2 = editor => {\n      bindFakeDragEvents(editor);\n      if (shouldBlockUnsupportedDrop(editor)) {\n        blockUnsupportedFileDrop(editor);\n      }\n    };\n\n    const setup$4 = editor => {\n      const renderFocusCaret = first$1(() => {\n        if (!editor.removed && editor.getBody().contains(document.activeElement)) {\n          const rng = editor.selection.getRng();\n          if (rng.collapsed) {\n            const caretRange = renderRangeCaret(editor, rng, false);\n            editor.selection.setRng(caretRange);\n          }\n        }\n      }, 0);\n      editor.on('focus', () => {\n        renderFocusCaret.throttle();\n      });\n      editor.on('blur', () => {\n        renderFocusCaret.cancel();\n      });\n    };\n\n    const setup$3 = editor => {\n      editor.on('init', () => {\n        editor.on('focusin', e => {\n          const target = e.target;\n          if (isMedia$2(target)) {\n            const ceRoot = getContentEditableRoot$1(editor.getBody(), target);\n            const node = isContentEditableFalse$a(ceRoot) ? ceRoot : target;\n            if (editor.selection.getNode() !== node) {\n              selectNode(editor, node).each(rng => editor.selection.setRng(rng));\n            }\n          }\n        });\n      });\n    };\n\n    const isContentEditableFalse = isContentEditableFalse$a;\n    const getContentEditableRoot = (editor, node) => getContentEditableRoot$1(editor.getBody(), node);\n    const SelectionOverrides = editor => {\n      const selection = editor.selection, dom = editor.dom;\n      const rootNode = editor.getBody();\n      const fakeCaret = FakeCaret(editor, rootNode, dom.isBlock, () => hasFocus(editor));\n      const realSelectionId = 'sel-' + dom.uniqueId();\n      const elementSelectionAttr = 'data-mce-selected';\n      let selectedElement;\n      const isFakeSelectionElement = node => isNonNullable(node) && dom.hasClass(node, 'mce-offscreen-selection');\n      const isFakeSelectionTargetElement = node => node !== rootNode && (isContentEditableFalse(node) || isMedia$2(node)) && dom.isChildOf(node, rootNode);\n      const setRange = range => {\n        if (range) {\n          selection.setRng(range);\n        }\n      };\n      const showCaret = (direction, node, before, scrollIntoView = true) => {\n        const e = editor.dispatch('ShowCaret', {\n          target: node,\n          direction,\n          before\n        });\n        if (e.isDefaultPrevented()) {\n          return null;\n        }\n        if (scrollIntoView) {\n          selection.scrollIntoView(node, direction === -1);\n        }\n        return fakeCaret.show(before, node);\n      };\n      const showBlockCaretContainer = blockCaretContainer => {\n        if (blockCaretContainer.hasAttribute('data-mce-caret')) {\n          showCaretContainerBlock(blockCaretContainer);\n          selection.scrollIntoView(blockCaretContainer);\n        }\n      };\n      const registerEvents = () => {\n        editor.on('click', e => {\n          const contentEditableRoot = getContentEditableRoot(editor, e.target);\n          if (contentEditableRoot) {\n            if (isContentEditableFalse(contentEditableRoot)) {\n              e.preventDefault();\n              editor.focus();\n            }\n          }\n        });\n        editor.on('blur NewBlock', removeElementSelection);\n        editor.on('ResizeWindow FullscreenStateChanged', fakeCaret.reposition);\n        editor.on('tap', e => {\n          const targetElm = e.target;\n          const contentEditableRoot = getContentEditableRoot(editor, targetElm);\n          if (isContentEditableFalse(contentEditableRoot)) {\n            e.preventDefault();\n            selectNode(editor, contentEditableRoot).each(setElementSelection);\n          } else if (isFakeSelectionTargetElement(targetElm)) {\n            selectNode(editor, targetElm).each(setElementSelection);\n          }\n        }, true);\n        editor.on('mousedown', e => {\n          const targetElm = e.target;\n          if (targetElm !== rootNode && targetElm.nodeName !== 'HTML' && !dom.isChildOf(targetElm, rootNode)) {\n            return;\n          }\n          if (!isXYInContentArea(editor, e.clientX, e.clientY)) {\n            return;\n          }\n          removeElementSelection();\n          hideFakeCaret();\n          const closestContentEditable = getContentEditableRoot(editor, targetElm);\n          if (isContentEditableFalse(closestContentEditable)) {\n            e.preventDefault();\n            selectNode(editor, closestContentEditable).each(setElementSelection);\n          } else {\n            closestFakeCaretCandidate(rootNode, e.clientX, e.clientY).each(caretInfo => {\n              e.preventDefault();\n              const range = showCaret(1, caretInfo.node, caretInfo.position === FakeCaretPosition.Before, false);\n              setRange(range);\n              if (isElement$6(closestContentEditable)) {\n                closestContentEditable.focus();\n              } else {\n                editor.getBody().focus();\n              }\n            });\n          }\n        });\n        editor.on('keypress', e => {\n          if (VK.modifierPressed(e)) {\n            return;\n          }\n          if (isContentEditableFalse(selection.getNode())) {\n            e.preventDefault();\n          }\n        });\n        editor.on('GetSelectionRange', e => {\n          let rng = e.range;\n          if (selectedElement) {\n            if (!selectedElement.parentNode) {\n              selectedElement = null;\n              return;\n            }\n            rng = rng.cloneRange();\n            rng.selectNode(selectedElement);\n            e.range = rng;\n          }\n        });\n        editor.on('SetSelectionRange', e => {\n          e.range = normalizeVoidElementSelection(e.range);\n          const rng = setElementSelection(e.range, e.forward);\n          if (rng) {\n            e.range = rng;\n          }\n        });\n        const isPasteBin = node => isElement$6(node) && node.id === 'mcepastebin';\n        editor.on('AfterSetSelectionRange', e => {\n          const rng = e.range;\n          const parent = rng.startContainer.parentElement;\n          if (!isRangeInCaretContainer(rng) && !isPasteBin(parent)) {\n            hideFakeCaret();\n          }\n          if (!isFakeSelectionElement(parent)) {\n            removeElementSelection();\n          }\n        });\n        init$2(editor);\n        setup$4(editor);\n        setup$3(editor);\n      };\n      const isWithinCaretContainer = node => isCaretContainer$2(node) || startsWithCaretContainer$1(node) || endsWithCaretContainer$1(node);\n      const isRangeInCaretContainer = rng => isWithinCaretContainer(rng.startContainer) || isWithinCaretContainer(rng.endContainer);\n      const normalizeVoidElementSelection = rng => {\n        const voidElements = editor.schema.getVoidElements();\n        const newRng = dom.createRng();\n        const startContainer = rng.startContainer;\n        const startOffset = rng.startOffset;\n        const endContainer = rng.endContainer;\n        const endOffset = rng.endOffset;\n        if (has$2(voidElements, startContainer.nodeName.toLowerCase())) {\n          if (startOffset === 0) {\n            newRng.setStartBefore(startContainer);\n          } else {\n            newRng.setStartAfter(startContainer);\n          }\n        } else {\n          newRng.setStart(startContainer, startOffset);\n        }\n        if (has$2(voidElements, endContainer.nodeName.toLowerCase())) {\n          if (endOffset === 0) {\n            newRng.setEndBefore(endContainer);\n          } else {\n            newRng.setEndAfter(endContainer);\n          }\n        } else {\n          newRng.setEnd(endContainer, endOffset);\n        }\n        return newRng;\n      };\n      const setupOffscreenSelection = (node, targetClone) => {\n        const body = SugarElement.fromDom(editor.getBody());\n        const doc = editor.getDoc();\n        const realSelectionContainer = descendant(body, '#' + realSelectionId).getOrThunk(() => {\n          const newContainer = SugarElement.fromHtml('<div data-mce-bogus=\"all\" class=\"mce-offscreen-selection\"></div>', doc);\n          set$2(newContainer, 'id', realSelectionId);\n          append$1(body, newContainer);\n          return newContainer;\n        });\n        const newRange = dom.createRng();\n        empty(realSelectionContainer);\n        append(realSelectionContainer, [\n          SugarElement.fromText(nbsp, doc),\n          SugarElement.fromDom(targetClone),\n          SugarElement.fromText(nbsp, doc)\n        ]);\n        newRange.setStart(realSelectionContainer.dom.firstChild, 1);\n        newRange.setEnd(realSelectionContainer.dom.lastChild, 0);\n        setAll(realSelectionContainer, { top: dom.getPos(node, editor.getBody()).y + 'px' });\n        focus$1(realSelectionContainer);\n        const sel = selection.getSel();\n        if (sel) {\n          sel.removeAllRanges();\n          sel.addRange(newRange);\n        }\n        return newRange;\n      };\n      const selectElement = elm => {\n        const targetClone = elm.cloneNode(true);\n        const e = editor.dispatch('ObjectSelected', {\n          target: elm,\n          targetClone\n        });\n        if (e.isDefaultPrevented()) {\n          return null;\n        }\n        const range = setupOffscreenSelection(elm, e.targetClone);\n        const nodeElm = SugarElement.fromDom(elm);\n        each$e(descendants(SugarElement.fromDom(editor.getBody()), `*[${ elementSelectionAttr }]`), elm => {\n          if (!eq(nodeElm, elm)) {\n            remove$b(elm, elementSelectionAttr);\n          }\n        });\n        if (!dom.getAttrib(elm, elementSelectionAttr)) {\n          elm.setAttribute(elementSelectionAttr, '1');\n        }\n        selectedElement = elm;\n        hideFakeCaret();\n        return range;\n      };\n      const setElementSelection = (range, forward) => {\n        if (!range) {\n          return null;\n        }\n        if (range.collapsed) {\n          if (!isRangeInCaretContainer(range)) {\n            const dir = forward ? 1 : -1;\n            const caretPosition = getNormalizedRangeEndPoint(dir, rootNode, range);\n            const beforeNode = caretPosition.getNode(!forward);\n            if (isNonNullable(beforeNode)) {\n              if (isFakeCaretTarget(beforeNode)) {\n                return showCaret(dir, beforeNode, forward ? !caretPosition.isAtEnd() : false, false);\n              }\n              if (isCaretContainerInline(beforeNode) && isContentEditableFalse$a(beforeNode.nextSibling)) {\n                const rng = dom.createRng();\n                rng.setStart(beforeNode, 0);\n                rng.setEnd(beforeNode, 0);\n                return rng;\n              }\n            }\n            const afterNode = caretPosition.getNode(forward);\n            if (isNonNullable(afterNode)) {\n              if (isFakeCaretTarget(afterNode)) {\n                return showCaret(dir, afterNode, forward ? false : !caretPosition.isAtEnd(), false);\n              }\n              if (isCaretContainerInline(afterNode) && isContentEditableFalse$a(afterNode.previousSibling)) {\n                const rng = dom.createRng();\n                rng.setStart(afterNode, 1);\n                rng.setEnd(afterNode, 1);\n                return rng;\n              }\n            }\n          }\n          return null;\n        }\n        let startContainer = range.startContainer;\n        let startOffset = range.startOffset;\n        const endOffset = range.endOffset;\n        if (isText$a(startContainer) && startOffset === 0 && isContentEditableFalse(startContainer.parentNode)) {\n          startContainer = startContainer.parentNode;\n          startOffset = dom.nodeIndex(startContainer);\n          startContainer = startContainer.parentNode;\n        }\n        if (!isElement$6(startContainer)) {\n          return null;\n        }\n        if (endOffset === startOffset + 1 && startContainer === range.endContainer) {\n          const node = startContainer.childNodes[startOffset];\n          if (isFakeSelectionTargetElement(node)) {\n            return selectElement(node);\n          }\n        }\n        return null;\n      };\n      const removeElementSelection = () => {\n        if (selectedElement) {\n          selectedElement.removeAttribute(elementSelectionAttr);\n        }\n        descendant(SugarElement.fromDom(editor.getBody()), '#' + realSelectionId).each(remove$6);\n        selectedElement = null;\n      };\n      const destroy = () => {\n        fakeCaret.destroy();\n        selectedElement = null;\n      };\n      const hideFakeCaret = () => {\n        fakeCaret.hide();\n      };\n      if (!isRtc(editor)) {\n        registerEvents();\n      }\n      return {\n        showCaret,\n        showBlockCaretContainer,\n        hideFakeCaret,\n        destroy\n      };\n    };\n\n    const getNormalizedTextOffset = (container, offset) => {\n      let normalizedOffset = offset;\n      for (let node = container.previousSibling; isText$a(node); node = node.previousSibling) {\n        normalizedOffset += node.data.length;\n      }\n      return normalizedOffset;\n    };\n    const generatePath = (dom, root, node, offset, normalized) => {\n      if (isText$a(node) && (offset < 0 || offset > node.data.length)) {\n        return [];\n      }\n      const p = normalized && isText$a(node) ? [getNormalizedTextOffset(node, offset)] : [offset];\n      let current = node;\n      while (current !== root && current.parentNode) {\n        p.push(dom.nodeIndex(current, normalized));\n        current = current.parentNode;\n      }\n      return current === root ? p.reverse() : [];\n    };\n    const generatePathRange = (dom, root, startNode, startOffset, endNode, endOffset, normalized = false) => {\n      const start = generatePath(dom, root, startNode, startOffset, normalized);\n      const end = generatePath(dom, root, endNode, endOffset, normalized);\n      return {\n        start,\n        end\n      };\n    };\n    const resolvePath = (root, path) => {\n      const nodePath = path.slice();\n      const offset = nodePath.pop();\n      if (!isNumber(offset)) {\n        return Optional.none();\n      } else {\n        const resolvedNode = foldl(nodePath, (optNode, index) => optNode.bind(node => Optional.from(node.childNodes[index])), Optional.some(root));\n        return resolvedNode.bind(node => {\n          if (isText$a(node) && (offset < 0 || offset > node.data.length)) {\n            return Optional.none();\n          } else {\n            return Optional.some({\n              node,\n              offset\n            });\n          }\n        });\n      }\n    };\n    const resolvePathRange = (root, range) => resolvePath(root, range.start).bind(({\n      node: startNode,\n      offset: startOffset\n    }) => resolvePath(root, range.end).map(({\n      node: endNode,\n      offset: endOffset\n    }) => {\n      const rng = document.createRange();\n      rng.setStart(startNode, startOffset);\n      rng.setEnd(endNode, endOffset);\n      return rng;\n    }));\n    const generatePathRangeFromRange = (dom, root, range, normalized = false) => generatePathRange(dom, root, range.startContainer, range.startOffset, range.endContainer, range.endOffset, normalized);\n\n    const cleanEmptyNodes = (dom, node, isRoot) => {\n      if (node && dom.isEmpty(node) && !isRoot(node)) {\n        const parent = node.parentNode;\n        dom.remove(node);\n        cleanEmptyNodes(dom, parent, isRoot);\n      }\n    };\n    const deleteRng = (dom, rng, isRoot, clean = true) => {\n      const startParent = rng.startContainer.parentNode;\n      const endParent = rng.endContainer.parentNode;\n      rng.deleteContents();\n      if (clean && !isRoot(rng.startContainer)) {\n        if (isText$a(rng.startContainer) && rng.startContainer.data.length === 0) {\n          dom.remove(rng.startContainer);\n        }\n        if (isText$a(rng.endContainer) && rng.endContainer.data.length === 0) {\n          dom.remove(rng.endContainer);\n        }\n        cleanEmptyNodes(dom, startParent, isRoot);\n        if (startParent !== endParent) {\n          cleanEmptyNodes(dom, endParent, isRoot);\n        }\n      }\n    };\n    const getParentBlock = (editor, rng) => Optional.from(editor.dom.getParent(rng.startContainer, editor.dom.isBlock));\n    const resolveFromDynamicPatterns = (patternSet, block, beforeText) => {\n      const dynamicPatterns = patternSet.dynamicPatternsLookup({\n        text: beforeText,\n        block\n      });\n      return {\n        ...patternSet,\n        blockPatterns: getBlockPatterns(dynamicPatterns).concat(patternSet.blockPatterns),\n        inlinePatterns: getInlinePatterns(dynamicPatterns).concat(patternSet.inlinePatterns)\n      };\n    };\n    const getBeforeText = (dom, block, node, offset) => {\n      const rng = dom.createRng();\n      rng.setStart(block, 0);\n      rng.setEnd(node, offset);\n      return rng.toString();\n    };\n\n    const stripPattern = (dom, block, pattern) => {\n      const firstTextNode = textAfter(block, 0, block);\n      firstTextNode.each(spot => {\n        const node = spot.container;\n        scanRight(node, pattern.start.length, block).each(end => {\n          const rng = dom.createRng();\n          rng.setStart(node, 0);\n          rng.setEnd(end.container, end.offset);\n          deleteRng(dom, rng, e => e === block);\n        });\n      });\n    };\n    const applyPattern$1 = (editor, match) => {\n      const dom = editor.dom;\n      const pattern = match.pattern;\n      const rng = resolvePathRange(dom.getRoot(), match.range).getOrDie('Unable to resolve path range');\n      const isBlockFormatName = (name, formatter) => {\n        const formatSet = formatter.get(name);\n        return isArray$1(formatSet) && head(formatSet).exists(format => has$2(format, 'block'));\n      };\n      getParentBlock(editor, rng).each(block => {\n        if (pattern.type === 'block-format') {\n          if (isBlockFormatName(pattern.format, editor.formatter)) {\n            editor.undoManager.transact(() => {\n              stripPattern(editor.dom, block, pattern);\n              editor.formatter.apply(pattern.format);\n            });\n          }\n        } else if (pattern.type === 'block-command') {\n          editor.undoManager.transact(() => {\n            stripPattern(editor.dom, block, pattern);\n            editor.execCommand(pattern.cmd, false, pattern.value);\n          });\n        }\n      });\n      return true;\n    };\n    const sortPatterns$1 = patterns => sort(patterns, (a, b) => b.start.length - a.start.length);\n    const findPattern$1 = (patterns, text) => {\n      const sortedPatterns = sortPatterns$1(patterns);\n      const nuText = text.replace(nbsp, ' ');\n      return find$2(sortedPatterns, pattern => text.indexOf(pattern.start) === 0 || nuText.indexOf(pattern.start) === 0);\n    };\n    const findPatterns$1 = (editor, block, patternSet, normalizedMatches) => {\n      var _a;\n      const dom = editor.dom;\n      const forcedRootBlock = getForcedRootBlock(editor);\n      if (!dom.is(block, forcedRootBlock)) {\n        return [];\n      }\n      const blockText = (_a = block.textContent) !== null && _a !== void 0 ? _a : '';\n      return findPattern$1(patternSet.blockPatterns, blockText).map(pattern => {\n        if (Tools.trim(blockText).length === pattern.start.length) {\n          return [];\n        }\n        return [{\n            pattern,\n            range: generatePathRange(dom, dom.getRoot(), block, 0, block, 0, normalizedMatches)\n          }];\n      }).getOr([]);\n    };\n    const applyMatches$1 = (editor, matches) => {\n      if (matches.length === 0) {\n        return;\n      }\n      const bookmark = editor.selection.getBookmark();\n      each$e(matches, match => applyPattern$1(editor, match));\n      editor.selection.moveToBookmark(bookmark);\n    };\n\n    const newMarker = (dom, id) => dom.create('span', {\n      'data-mce-type': 'bookmark',\n      id\n    });\n    const rangeFromMarker = (dom, marker) => {\n      const rng = dom.createRng();\n      rng.setStartAfter(marker.start);\n      rng.setEndBefore(marker.end);\n      return rng;\n    };\n    const createMarker = (dom, markerPrefix, pathRange) => {\n      const rng = resolvePathRange(dom.getRoot(), pathRange).getOrDie('Unable to resolve path range');\n      const startNode = rng.startContainer;\n      const endNode = rng.endContainer;\n      const textEnd = rng.endOffset === 0 ? endNode : endNode.splitText(rng.endOffset);\n      const textStart = rng.startOffset === 0 ? startNode : startNode.splitText(rng.startOffset);\n      const startParentNode = textStart.parentNode;\n      const endParentNode = textEnd.parentNode;\n      return {\n        prefix: markerPrefix,\n        end: endParentNode.insertBefore(newMarker(dom, markerPrefix + '-end'), textEnd),\n        start: startParentNode.insertBefore(newMarker(dom, markerPrefix + '-start'), textStart)\n      };\n    };\n    const removeMarker = (dom, marker, isRoot) => {\n      cleanEmptyNodes(dom, dom.get(marker.prefix + '-end'), isRoot);\n      cleanEmptyNodes(dom, dom.get(marker.prefix + '-start'), isRoot);\n    };\n\n    const isReplacementPattern = pattern => pattern.start.length === 0;\n    const matchesPattern = patternContent => (element, offset) => {\n      const text = element.data;\n      const searchText = text.substring(0, offset);\n      const startEndIndex = searchText.lastIndexOf(patternContent.charAt(patternContent.length - 1));\n      const startIndex = searchText.lastIndexOf(patternContent);\n      if (startIndex !== -1) {\n        return startIndex + patternContent.length;\n      } else if (startEndIndex !== -1) {\n        return startEndIndex + 1;\n      } else {\n        return -1;\n      }\n    };\n    const findPatternStartFromSpot = (dom, pattern, block, spot) => {\n      const startPattern = pattern.start;\n      const startSpot = repeatLeft(dom, spot.container, spot.offset, matchesPattern(startPattern), block);\n      return startSpot.bind(spot => {\n        var _a, _b;\n        const startPatternIndex = (_b = (_a = block.textContent) === null || _a === void 0 ? void 0 : _a.indexOf(startPattern)) !== null && _b !== void 0 ? _b : -1;\n        const isCompleteMatch = startPatternIndex !== -1 && spot.offset >= startPatternIndex + startPattern.length;\n        if (isCompleteMatch) {\n          const rng = dom.createRng();\n          rng.setStart(spot.container, spot.offset - startPattern.length);\n          rng.setEnd(spot.container, spot.offset);\n          return Optional.some(rng);\n        } else {\n          const offset = spot.offset - startPattern.length;\n          return scanLeft(spot.container, offset, block).map(nextSpot => {\n            const rng = dom.createRng();\n            rng.setStart(nextSpot.container, nextSpot.offset);\n            rng.setEnd(spot.container, spot.offset);\n            return rng;\n          }).filter(rng => rng.toString() === startPattern).orThunk(() => findPatternStartFromSpot(dom, pattern, block, point(spot.container, 0)));\n        }\n      });\n    };\n    const findPatternStart = (dom, pattern, node, offset, block, requireGap = false) => {\n      if (pattern.start.length === 0 && !requireGap) {\n        const rng = dom.createRng();\n        rng.setStart(node, offset);\n        rng.setEnd(node, offset);\n        return Optional.some(rng);\n      }\n      return textBefore(node, offset, block).bind(spot => {\n        const start = findPatternStartFromSpot(dom, pattern, block, spot);\n        return start.bind(startRange => {\n          var _a;\n          if (requireGap) {\n            if (startRange.endContainer === spot.container && startRange.endOffset === spot.offset) {\n              return Optional.none();\n            } else if (spot.offset === 0 && ((_a = startRange.endContainer.textContent) === null || _a === void 0 ? void 0 : _a.length) === startRange.endOffset) {\n              return Optional.none();\n            }\n          }\n          return Optional.some(startRange);\n        });\n      });\n    };\n    const findPattern = (editor, block, details, normalizedMatches) => {\n      const dom = editor.dom;\n      const root = dom.getRoot();\n      const pattern = details.pattern;\n      const endNode = details.position.container;\n      const endOffset = details.position.offset;\n      return scanLeft(endNode, endOffset - details.pattern.end.length, block).bind(spot => {\n        const endPathRng = generatePathRange(dom, root, spot.container, spot.offset, endNode, endOffset, normalizedMatches);\n        if (isReplacementPattern(pattern)) {\n          return Optional.some({\n            matches: [{\n                pattern,\n                startRng: endPathRng,\n                endRng: endPathRng\n              }],\n            position: spot\n          });\n        } else {\n          const resultsOpt = findPatternsRec(editor, details.remainingPatterns, spot.container, spot.offset, block, normalizedMatches);\n          const results = resultsOpt.getOr({\n            matches: [],\n            position: spot\n          });\n          const pos = results.position;\n          const start = findPatternStart(dom, pattern, pos.container, pos.offset, block, resultsOpt.isNone());\n          return start.map(startRng => {\n            const startPathRng = generatePathRangeFromRange(dom, root, startRng, normalizedMatches);\n            return {\n              matches: results.matches.concat([{\n                  pattern,\n                  startRng: startPathRng,\n                  endRng: endPathRng\n                }]),\n              position: point(startRng.startContainer, startRng.startOffset)\n            };\n          });\n        }\n      });\n    };\n    const findPatternsRec = (editor, patterns, node, offset, block, normalizedMatches) => {\n      const dom = editor.dom;\n      return textBefore(node, offset, dom.getRoot()).bind(endSpot => {\n        const text = getBeforeText(dom, block, node, offset);\n        for (let i = 0; i < patterns.length; i++) {\n          const pattern = patterns[i];\n          if (!endsWith(text, pattern.end)) {\n            continue;\n          }\n          const patternsWithoutCurrent = patterns.slice();\n          patternsWithoutCurrent.splice(i, 1);\n          const result = findPattern(editor, block, {\n            pattern,\n            remainingPatterns: patternsWithoutCurrent,\n            position: endSpot\n          }, normalizedMatches);\n          if (result.isNone() && offset > 0) {\n            return findPatternsRec(editor, patterns, node, offset - 1, block, normalizedMatches);\n          }\n          if (result.isSome()) {\n            return result;\n          }\n        }\n        return Optional.none();\n      });\n    };\n    const applyPattern = (editor, pattern, patternRange) => {\n      editor.selection.setRng(patternRange);\n      if (pattern.type === 'inline-format') {\n        each$e(pattern.format, format => {\n          editor.formatter.apply(format);\n        });\n      } else {\n        editor.execCommand(pattern.cmd, false, pattern.value);\n      }\n    };\n    const applyReplacementPattern = (editor, pattern, marker, isRoot) => {\n      const markerRange = rangeFromMarker(editor.dom, marker);\n      deleteRng(editor.dom, markerRange, isRoot);\n      applyPattern(editor, pattern, markerRange);\n    };\n    const applyPatternWithContent = (editor, pattern, startMarker, endMarker, isRoot) => {\n      const dom = editor.dom;\n      const markerEndRange = rangeFromMarker(dom, endMarker);\n      const markerStartRange = rangeFromMarker(dom, startMarker);\n      deleteRng(dom, markerStartRange, isRoot);\n      deleteRng(dom, markerEndRange, isRoot);\n      const patternMarker = {\n        prefix: startMarker.prefix,\n        start: startMarker.end,\n        end: endMarker.start\n      };\n      const patternRange = rangeFromMarker(dom, patternMarker);\n      applyPattern(editor, pattern, patternRange);\n    };\n    const addMarkers = (dom, matches) => {\n      const markerPrefix = generate$1('mce_textpattern');\n      const matchesWithEnds = foldr(matches, (acc, match) => {\n        const endMarker = createMarker(dom, markerPrefix + `_end${ acc.length }`, match.endRng);\n        return acc.concat([{\n            ...match,\n            endMarker\n          }]);\n      }, []);\n      return foldr(matchesWithEnds, (acc, match) => {\n        const idx = matchesWithEnds.length - acc.length - 1;\n        const startMarker = isReplacementPattern(match.pattern) ? match.endMarker : createMarker(dom, markerPrefix + `_start${ idx }`, match.startRng);\n        return acc.concat([{\n            ...match,\n            startMarker\n          }]);\n      }, []);\n    };\n    const sortPatterns = patterns => sort(patterns, (a, b) => b.end.length - a.end.length);\n    const getBestMatches = (matches, matchesWithSortedPatterns) => {\n      const hasSameMatches = forall(matches, match => exists(matchesWithSortedPatterns, sortedMatch => match.pattern.start === sortedMatch.pattern.start && match.pattern.end === sortedMatch.pattern.end));\n      if (matches.length === matchesWithSortedPatterns.length) {\n        if (hasSameMatches) {\n          return matches;\n        } else {\n          return matchesWithSortedPatterns;\n        }\n      }\n      return matches.length > matchesWithSortedPatterns.length ? matches : matchesWithSortedPatterns;\n    };\n    const findPatterns = (editor, block, node, offset, patternSet, normalizedMatches) => {\n      const matches = findPatternsRec(editor, patternSet.inlinePatterns, node, offset, block, normalizedMatches).fold(() => [], result => result.matches);\n      const matchesWithSortedPatterns = findPatternsRec(editor, sortPatterns(patternSet.inlinePatterns), node, offset, block, normalizedMatches).fold(() => [], result => result.matches);\n      return getBestMatches(matches, matchesWithSortedPatterns);\n    };\n    const applyMatches = (editor, matches) => {\n      if (matches.length === 0) {\n        return;\n      }\n      const dom = editor.dom;\n      const bookmark = editor.selection.getBookmark();\n      const matchesWithMarkers = addMarkers(dom, matches);\n      each$e(matchesWithMarkers, match => {\n        const block = dom.getParent(match.startMarker.start, dom.isBlock);\n        const isRoot = node => node === block;\n        if (isReplacementPattern(match.pattern)) {\n          applyReplacementPattern(editor, match.pattern, match.endMarker, isRoot);\n        } else {\n          applyPatternWithContent(editor, match.pattern, match.startMarker, match.endMarker, isRoot);\n        }\n        removeMarker(dom, match.endMarker, isRoot);\n        removeMarker(dom, match.startMarker, isRoot);\n      });\n      editor.selection.moveToBookmark(bookmark);\n    };\n\n    const handleEnter = (editor, patternSet) => {\n      const rng = editor.selection.getRng();\n      return getParentBlock(editor, rng).map(block => {\n        var _a;\n        const offset = Math.max(0, rng.startOffset);\n        const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, (_a = block.textContent) !== null && _a !== void 0 ? _a : '');\n        const inlineMatches = findPatterns(editor, block, rng.startContainer, offset, dynamicPatternSet, true);\n        const blockMatches = findPatterns$1(editor, block, dynamicPatternSet, true);\n        if (blockMatches.length > 0 || inlineMatches.length > 0) {\n          editor.undoManager.add();\n          editor.undoManager.extra(() => {\n            editor.execCommand('mceInsertNewLine');\n          }, () => {\n            editor.insertContent(zeroWidth);\n            applyMatches(editor, inlineMatches);\n            applyMatches$1(editor, blockMatches);\n            const range = editor.selection.getRng();\n            const spot = textBefore(range.startContainer, range.startOffset, editor.dom.getRoot());\n            editor.execCommand('mceInsertNewLine');\n            spot.each(s => {\n              const node = s.container;\n              if (node.data.charAt(s.offset - 1) === zeroWidth) {\n                node.deleteData(s.offset - 1, 1);\n                cleanEmptyNodes(editor.dom, node.parentNode, e => e === editor.dom.getRoot());\n              }\n            });\n          });\n          return true;\n        }\n        return false;\n      }).getOr(false);\n    };\n    const handleInlineKey = (editor, patternSet) => {\n      const rng = editor.selection.getRng();\n      getParentBlock(editor, rng).map(block => {\n        const offset = Math.max(0, rng.startOffset - 1);\n        const beforeText = getBeforeText(editor.dom, block, rng.startContainer, offset);\n        const dynamicPatternSet = resolveFromDynamicPatterns(patternSet, block, beforeText);\n        const inlineMatches = findPatterns(editor, block, rng.startContainer, offset, dynamicPatternSet, false);\n        if (inlineMatches.length > 0) {\n          editor.undoManager.transact(() => {\n            applyMatches(editor, inlineMatches);\n          });\n        }\n      });\n    };\n    const checkKeyEvent = (codes, event, predicate) => {\n      for (let i = 0; i < codes.length; i++) {\n        if (predicate(codes[i], event)) {\n          return true;\n        }\n      }\n      return false;\n    };\n    const checkKeyCode = (codes, event) => checkKeyEvent(codes, event, (code, event) => {\n      return code === event.keyCode && !VK.modifierPressed(event);\n    });\n    const checkCharCode = (chars, event) => checkKeyEvent(chars, event, (chr, event) => {\n      return chr.charCodeAt(0) === event.charCode;\n    });\n\n    const setup$2 = editor => {\n      const charCodes = [\n        ',',\n        '.',\n        ';',\n        ':',\n        '!',\n        '?'\n      ];\n      const keyCodes = [32];\n      const getPatternSet = () => createPatternSet(getTextPatterns(editor), getTextPatternsLookup(editor));\n      const hasDynamicPatterns = () => hasTextPatternsLookup(editor);\n      editor.on('keydown', e => {\n        if (e.keyCode === 13 && !VK.modifierPressed(e) && editor.selection.isCollapsed()) {\n          const patternSet = getPatternSet();\n          const hasPatterns = patternSet.inlinePatterns.length > 0 || patternSet.blockPatterns.length > 0 || hasDynamicPatterns();\n          if (hasPatterns && handleEnter(editor, patternSet)) {\n            e.preventDefault();\n          }\n        }\n      }, true);\n      const handleInlineTrigger = () => {\n        if (editor.selection.isCollapsed()) {\n          const patternSet = getPatternSet();\n          const hasPatterns = patternSet.inlinePatterns.length > 0 || hasDynamicPatterns();\n          if (hasPatterns) {\n            handleInlineKey(editor, patternSet);\n          }\n        }\n      };\n      editor.on('keyup', e => {\n        if (checkKeyCode(keyCodes, e)) {\n          handleInlineTrigger();\n        }\n      });\n      editor.on('keypress', e => {\n        if (checkCharCode(charCodes, e)) {\n          Delay.setEditorTimeout(editor, handleInlineTrigger);\n        }\n      });\n    };\n\n    const setup$1 = editor => {\n      setup$2(editor);\n    };\n\n    const Quirks = editor => {\n      const each = Tools.each;\n      const BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection, parser = editor.parser;\n      const browser = Env.browser;\n      const isGecko = browser.isFirefox();\n      const isWebKit = browser.isChromium() || browser.isSafari();\n      const isiOS = Env.deviceType.isiPhone() || Env.deviceType.isiPad();\n      const isMac = Env.os.isMacOS() || Env.os.isiOS();\n      const setEditorCommandState = (cmd, state) => {\n        try {\n          editor.getDoc().execCommand(cmd, false, String(state));\n        } catch (ex) {\n        }\n      };\n      const isDefaultPrevented = e => {\n        return e.isDefaultPrevented();\n      };\n      const emptyEditorWhenDeleting = () => {\n        const serializeRng = rng => {\n          const body = dom.create('body');\n          const contents = rng.cloneContents();\n          body.appendChild(contents);\n          return selection.serializer.serialize(body, { format: 'html' });\n        };\n        const allContentsSelected = rng => {\n          const selection = serializeRng(rng);\n          const allRng = dom.createRng();\n          allRng.selectNode(editor.getBody());\n          const allSelection = serializeRng(allRng);\n          return selection === allSelection;\n        };\n        editor.on('keydown', e => {\n          const keyCode = e.keyCode;\n          if (!isDefaultPrevented(e) && (keyCode === DELETE || keyCode === BACKSPACE)) {\n            const isCollapsed = editor.selection.isCollapsed();\n            const body = editor.getBody();\n            if (isCollapsed && !dom.isEmpty(body)) {\n              return;\n            }\n            if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {\n              return;\n            }\n            e.preventDefault();\n            editor.setContent('');\n            if (body.firstChild && dom.isBlock(body.firstChild)) {\n              editor.selection.setCursorLocation(body.firstChild, 0);\n            } else {\n              editor.selection.setCursorLocation(body, 0);\n            }\n            editor.nodeChanged();\n          }\n        });\n      };\n      const selectAll = () => {\n        editor.shortcuts.add('meta+a', null, 'SelectAll');\n      };\n      const documentElementEditingFocus = () => {\n        if (!editor.inline) {\n          dom.bind(editor.getDoc(), 'mousedown mouseup', e => {\n            let rng;\n            if (e.target === editor.getDoc().documentElement) {\n              rng = selection.getRng();\n              editor.getBody().focus();\n              if (e.type === 'mousedown') {\n                if (isCaretContainer$2(rng.startContainer)) {\n                  return;\n                }\n                selection.placeCaretAt(e.clientX, e.clientY);\n              } else {\n                selection.setRng(rng);\n              }\n            }\n          });\n        }\n      };\n      const removeHrOnBackspace = () => {\n        editor.on('keydown', e => {\n          if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {\n            if (!editor.getBody().getElementsByTagName('hr').length) {\n              return;\n            }\n            if (selection.isCollapsed() && selection.getRng().startOffset === 0) {\n              const node = selection.getNode();\n              const previousSibling = node.previousSibling;\n              if (node.nodeName === 'HR') {\n                dom.remove(node);\n                e.preventDefault();\n                return;\n              }\n              if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === 'hr') {\n                dom.remove(previousSibling);\n                e.preventDefault();\n              }\n            }\n          }\n        });\n      };\n      const focusBody = () => {\n        if (!Range.prototype.getClientRects) {\n          editor.on('mousedown', e => {\n            if (!isDefaultPrevented(e) && e.target.nodeName === 'HTML') {\n              const body = editor.getBody();\n              body.blur();\n              Delay.setEditorTimeout(editor, () => {\n                body.focus();\n              });\n            }\n          });\n        }\n      };\n      const selectControlElements = () => {\n        const visualAidsAnchorClass = getVisualAidsAnchorClass(editor);\n        editor.on('click', e => {\n          const target = e.target;\n          if (/^(IMG|HR)$/.test(target.nodeName) && dom.getContentEditableParent(target) !== 'false') {\n            e.preventDefault();\n            editor.selection.select(target);\n            editor.nodeChanged();\n          }\n          if (target.nodeName === 'A' && dom.hasClass(target, visualAidsAnchorClass) && target.childNodes.length === 0) {\n            e.preventDefault();\n            selection.select(target);\n          }\n        });\n      };\n      const removeStylesWhenDeletingAcrossBlockElements = () => {\n        const getAttributeApplyFunction = () => {\n          const template = dom.getAttribs(selection.getStart().cloneNode(false));\n          return () => {\n            const target = selection.getStart();\n            if (target !== editor.getBody()) {\n              dom.setAttrib(target, 'style', null);\n              each(template, attr => {\n                target.setAttributeNode(attr.cloneNode(true));\n              });\n            }\n          };\n        };\n        const isSelectionAcrossElements = () => {\n          return !selection.isCollapsed() && dom.getParent(selection.getStart(), dom.isBlock) !== dom.getParent(selection.getEnd(), dom.isBlock);\n        };\n        editor.on('keypress', e => {\n          let applyAttributes;\n          if (!isDefaultPrevented(e) && (e.keyCode === 8 || e.keyCode === 46) && isSelectionAcrossElements()) {\n            applyAttributes = getAttributeApplyFunction();\n            editor.getDoc().execCommand('delete', false);\n            applyAttributes();\n            e.preventDefault();\n            return false;\n          } else {\n            return true;\n          }\n        });\n        dom.bind(editor.getDoc(), 'cut', e => {\n          if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {\n            const applyAttributes = getAttributeApplyFunction();\n            Delay.setEditorTimeout(editor, () => {\n              applyAttributes();\n            });\n          }\n        });\n      };\n      const disableBackspaceIntoATable = () => {\n        editor.on('keydown', e => {\n          if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {\n            if (selection.isCollapsed() && selection.getRng().startOffset === 0) {\n              const previousSibling = selection.getNode().previousSibling;\n              if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === 'table') {\n                e.preventDefault();\n                return false;\n              }\n            }\n          }\n          return true;\n        });\n      };\n      const removeBlockQuoteOnBackSpace = () => {\n        editor.on('keydown', e => {\n          if (isDefaultPrevented(e) || e.keyCode !== VK.BACKSPACE) {\n            return;\n          }\n          let rng = selection.getRng();\n          const container = rng.startContainer;\n          const offset = rng.startOffset;\n          const root = dom.getRoot();\n          let parent = container;\n          if (!rng.collapsed || offset !== 0) {\n            return;\n          }\n          while (parent.parentNode && parent.parentNode.firstChild === parent && parent.parentNode !== root) {\n            parent = parent.parentNode;\n          }\n          if (parent.nodeName === 'BLOCKQUOTE') {\n            editor.formatter.toggle('blockquote', undefined, parent);\n            rng = dom.createRng();\n            rng.setStart(container, 0);\n            rng.setEnd(container, 0);\n            selection.setRng(rng);\n          }\n        });\n      };\n      const setGeckoEditingOptions = () => {\n        const setOpts = () => {\n          setEditorCommandState('StyleWithCSS', false);\n          setEditorCommandState('enableInlineTableEditing', false);\n          if (!getObjectResizing(editor)) {\n            setEditorCommandState('enableObjectResizing', false);\n          }\n        };\n        if (!isReadOnly$1(editor)) {\n          editor.on('BeforeExecCommand mousedown', setOpts);\n        }\n      };\n      const addBrAfterLastLinks = () => {\n        const fixLinks = () => {\n          each(dom.select('a:not([data-mce-block])'), node => {\n            var _a;\n            let parentNode = node.parentNode;\n            const root = dom.getRoot();\n            if ((parentNode === null || parentNode === void 0 ? void 0 : parentNode.lastChild) === node) {\n              while (parentNode && !dom.isBlock(parentNode)) {\n                if (((_a = parentNode.parentNode) === null || _a === void 0 ? void 0 : _a.lastChild) !== parentNode || parentNode === root) {\n                  return;\n                }\n                parentNode = parentNode.parentNode;\n              }\n              dom.add(parentNode, 'br', { 'data-mce-bogus': 1 });\n            }\n          });\n        };\n        editor.on('SetContent ExecCommand', e => {\n          if (e.type === 'setcontent' || e.command === 'mceInsertLink') {\n            fixLinks();\n          }\n        });\n      };\n      const setDefaultBlockType = () => {\n        editor.on('init', () => {\n          setEditorCommandState('DefaultParagraphSeparator', getForcedRootBlock(editor));\n        });\n      };\n      const isAllContentSelected = editor => {\n        const body = editor.getBody();\n        const rng = editor.selection.getRng();\n        return rng.startContainer === rng.endContainer && rng.startContainer === body && rng.startOffset === 0 && rng.endOffset === body.childNodes.length;\n      };\n      const normalizeSelection = () => {\n        editor.on('keyup focusin mouseup', e => {\n          if (!VK.modifierPressed(e) && !isAllContentSelected(editor)) {\n            selection.normalize();\n          }\n        }, true);\n      };\n      const showBrokenImageIcon = () => {\n        editor.contentStyles.push('img:-moz-broken {' + '-moz-force-broken-image-icon:1;' + 'min-width:24px;' + 'min-height:24px' + '}');\n      };\n      const restoreFocusOnKeyDown = () => {\n        if (!editor.inline) {\n          editor.on('keydown', () => {\n            if (document.activeElement === document.body) {\n              editor.getWin().focus();\n            }\n          });\n        }\n      };\n      const bodyHeight = () => {\n        if (!editor.inline) {\n          editor.contentStyles.push('body {min-height: 150px}');\n          editor.on('click', e => {\n            let rng;\n            if (e.target.nodeName === 'HTML') {\n              rng = editor.selection.getRng();\n              editor.getBody().focus();\n              editor.selection.setRng(rng);\n              editor.selection.normalize();\n              editor.nodeChanged();\n            }\n          });\n        }\n      };\n      const blockCmdArrowNavigation = () => {\n        if (isMac) {\n          editor.on('keydown', e => {\n            if (VK.metaKeyPressed(e) && !e.shiftKey && (e.keyCode === 37 || e.keyCode === 39)) {\n              e.preventDefault();\n              const selection = editor.selection.getSel();\n              selection.modify('move', e.keyCode === 37 ? 'backward' : 'forward', 'lineboundary');\n            }\n          });\n        }\n      };\n      const tapLinksAndImages = () => {\n        editor.on('click', e => {\n          let elm = e.target;\n          do {\n            if (elm.tagName === 'A') {\n              e.preventDefault();\n              return;\n            }\n          } while (elm = elm.parentNode);\n        });\n        editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');\n      };\n      const blockFormSubmitInsideEditor = () => {\n        editor.on('init', () => {\n          editor.dom.bind(editor.getBody(), 'submit', e => {\n            e.preventDefault();\n          });\n        });\n      };\n      const removeAppleInterchangeBrs = () => {\n        parser.addNodeFilter('br', nodes => {\n          let i = nodes.length;\n          while (i--) {\n            if (nodes[i].attr('class') === 'Apple-interchange-newline') {\n              nodes[i].remove();\n            }\n          }\n        });\n      };\n      const refreshContentEditable = noop;\n      const isHidden = () => {\n        if (!isGecko || editor.removed) {\n          return false;\n        }\n        const sel = editor.selection.getSel();\n        return !sel || !sel.rangeCount || sel.rangeCount === 0;\n      };\n      const setupRtc = () => {\n        if (isWebKit) {\n          documentElementEditingFocus();\n          selectControlElements();\n          blockFormSubmitInsideEditor();\n          selectAll();\n          if (isiOS) {\n            restoreFocusOnKeyDown();\n            bodyHeight();\n            tapLinksAndImages();\n          }\n        }\n        if (isGecko) {\n          focusBody();\n          setGeckoEditingOptions();\n          showBrokenImageIcon();\n          blockCmdArrowNavigation();\n        }\n      };\n      const setup = () => {\n        removeBlockQuoteOnBackSpace();\n        emptyEditorWhenDeleting();\n        if (!Env.windowsPhone) {\n          normalizeSelection();\n        }\n        if (isWebKit) {\n          documentElementEditingFocus();\n          selectControlElements();\n          setDefaultBlockType();\n          blockFormSubmitInsideEditor();\n          disableBackspaceIntoATable();\n          removeAppleInterchangeBrs();\n          if (isiOS) {\n            restoreFocusOnKeyDown();\n            bodyHeight();\n            tapLinksAndImages();\n          } else {\n            selectAll();\n          }\n        }\n        if (isGecko) {\n          removeHrOnBackspace();\n          focusBody();\n          removeStylesWhenDeletingAcrossBlockElements();\n          setGeckoEditingOptions();\n          addBrAfterLastLinks();\n          showBrokenImageIcon();\n          blockCmdArrowNavigation();\n          disableBackspaceIntoATable();\n        }\n      };\n      if (isRtc(editor)) {\n        setupRtc();\n      } else {\n        setup();\n      }\n      return {\n        refreshContentEditable,\n        isHidden\n      };\n    };\n\n    const DOM$6 = DOMUtils.DOM;\n    const appendStyle = (editor, text) => {\n      const body = SugarElement.fromDom(editor.getBody());\n      const container = getStyleContainer(getRootNode(body));\n      const style = SugarElement.fromTag('style');\n      set$2(style, 'type', 'text/css');\n      append$1(style, SugarElement.fromText(text));\n      append$1(container, style);\n      editor.on('remove', () => {\n        remove$6(style);\n      });\n    };\n    const getRootName = editor => editor.inline ? editor.getElement().nodeName.toLowerCase() : undefined;\n    const removeUndefined = obj => filter$4(obj, v => isUndefined(v) === false);\n    const mkParserSettings = editor => {\n      const getOption = editor.options.get;\n      const blobCache = editor.editorUpload.blobCache;\n      return removeUndefined({\n        allow_conditional_comments: getOption('allow_conditional_comments'),\n        allow_html_data_urls: getOption('allow_html_data_urls'),\n        allow_svg_data_urls: getOption('allow_svg_data_urls'),\n        allow_html_in_named_anchor: getOption('allow_html_in_named_anchor'),\n        allow_script_urls: getOption('allow_script_urls'),\n        allow_unsafe_link_target: getOption('allow_unsafe_link_target'),\n        convert_fonts_to_spans: getOption('convert_fonts_to_spans'),\n        fix_list_elements: getOption('fix_list_elements'),\n        font_size_legacy_values: getOption('font_size_legacy_values'),\n        forced_root_block: getOption('forced_root_block'),\n        forced_root_block_attrs: getOption('forced_root_block_attrs'),\n        preserve_cdata: getOption('preserve_cdata'),\n        remove_trailing_brs: getOption('remove_trailing_brs'),\n        inline_styles: getOption('inline_styles'),\n        root_name: getRootName(editor),\n        validate: true,\n        blob_cache: blobCache,\n        document: editor.getDoc()\n      });\n    };\n    const mkSchemaSettings = editor => {\n      const getOption = editor.options.get;\n      return removeUndefined({\n        custom_elements: getOption('custom_elements'),\n        extended_valid_elements: getOption('extended_valid_elements'),\n        invalid_elements: getOption('invalid_elements'),\n        invalid_styles: getOption('invalid_styles'),\n        schema: getOption('schema'),\n        valid_children: getOption('valid_children'),\n        valid_classes: getOption('valid_classes'),\n        valid_elements: getOption('valid_elements'),\n        valid_styles: getOption('valid_styles'),\n        verify_html: getOption('verify_html'),\n        padd_empty_block_inline_children: getOption('format_empty_lines')\n      });\n    };\n    const mkSerializerSettings = editor => {\n      const getOption = editor.options.get;\n      return {\n        ...mkParserSettings(editor),\n        ...mkSchemaSettings(editor),\n        ...removeUndefined({\n          url_converter: getOption('url_converter'),\n          url_converter_scope: getOption('url_converter_scope'),\n          element_format: getOption('element_format'),\n          entities: getOption('entities'),\n          entity_encoding: getOption('entity_encoding'),\n          indent: getOption('indent'),\n          indent_after: getOption('indent_after'),\n          indent_before: getOption('indent_before')\n        })\n      };\n    };\n    const createParser = editor => {\n      const parser = DomParser(mkParserSettings(editor), editor.schema);\n      parser.addAttributeFilter('src,href,style,tabindex', (nodes, name) => {\n        const dom = editor.dom;\n        const internalName = 'data-mce-' + name;\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          let value = node.attr(name);\n          if (value && !node.attr(internalName)) {\n            if (value.indexOf('data:') === 0 || value.indexOf('blob:') === 0) {\n              continue;\n            }\n            if (name === 'style') {\n              value = dom.serializeStyle(dom.parseStyle(value), node.name);\n              if (!value.length) {\n                value = null;\n              }\n              node.attr(internalName, value);\n              node.attr(name, value);\n            } else if (name === 'tabindex') {\n              node.attr(internalName, value);\n              node.attr(name, null);\n            } else {\n              node.attr(internalName, editor.convertURL(value, name, node.name));\n            }\n          }\n        }\n      });\n      parser.addNodeFilter('script', nodes => {\n        let i = nodes.length;\n        while (i--) {\n          const node = nodes[i];\n          const type = node.attr('type') || 'no/type';\n          if (type.indexOf('mce-') !== 0) {\n            node.attr('type', 'mce-' + type);\n          }\n        }\n      });\n      if (shouldPreserveCData(editor)) {\n        parser.addNodeFilter('#cdata', nodes => {\n          var _a;\n          let i = nodes.length;\n          while (i--) {\n            const node = nodes[i];\n            node.type = 8;\n            node.name = '#comment';\n            node.value = '[CDATA[' + editor.dom.encode((_a = node.value) !== null && _a !== void 0 ? _a : '') + ']]';\n          }\n        });\n      }\n      parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', nodes => {\n        let i = nodes.length;\n        const nonEmptyElements = editor.schema.getNonEmptyElements();\n        while (i--) {\n          const node = nodes[i];\n          if (node.isEmpty(nonEmptyElements) && node.getAll('br').length === 0) {\n            node.append(new AstNode('br', 1));\n          }\n        }\n      });\n      return parser;\n    };\n    const autoFocus = editor => {\n      const autoFocus = getAutoFocus(editor);\n      if (autoFocus) {\n        Delay.setEditorTimeout(editor, () => {\n          let focusEditor;\n          if (autoFocus === true) {\n            focusEditor = editor;\n          } else {\n            focusEditor = editor.editorManager.get(autoFocus);\n          }\n          if (focusEditor && !focusEditor.destroyed) {\n            focusEditor.focus();\n            focusEditor.selection.scrollIntoView();\n          }\n        }, 100);\n      }\n    };\n    const moveSelectionToFirstCaretPosition = editor => {\n      const root = editor.dom.getRoot();\n      if (!editor.inline && (!hasAnyRanges(editor) || editor.selection.getStart(true) === root)) {\n        firstPositionIn(root).each(pos => {\n          const node = pos.getNode();\n          const caretPos = isTable$2(node) ? firstPositionIn(node).getOr(pos) : pos;\n          editor.selection.setRng(caretPos.toRange());\n        });\n      }\n    };\n    const initEditor = editor => {\n      editor.bindPendingEventDelegates();\n      editor.initialized = true;\n      fireInit(editor);\n      editor.focus(true);\n      moveSelectionToFirstCaretPosition(editor);\n      editor.nodeChanged({ initial: true });\n      const initInstanceCallback = getInitInstanceCallback(editor);\n      if (isFunction(initInstanceCallback)) {\n        initInstanceCallback.call(editor, editor);\n      }\n      autoFocus(editor);\n    };\n    const getStyleSheetLoader$1 = editor => editor.inline ? editor.ui.styleSheetLoader : editor.dom.styleSheetLoader;\n    const makeStylesheetLoadingPromises = (editor, css, framedFonts) => {\n      const promises = [getStyleSheetLoader$1(editor).loadAll(css)];\n      if (editor.inline) {\n        return promises;\n      } else {\n        return promises.concat([editor.ui.styleSheetLoader.loadAll(framedFonts)]);\n      }\n    };\n    const loadContentCss = editor => {\n      const styleSheetLoader = getStyleSheetLoader$1(editor);\n      const fontCss = getFontCss(editor);\n      const css = editor.contentCSS;\n      const removeCss = () => {\n        styleSheetLoader.unloadAll(css);\n        if (!editor.inline) {\n          editor.ui.styleSheetLoader.unloadAll(fontCss);\n        }\n      };\n      const loaded = () => {\n        if (editor.removed) {\n          removeCss();\n        } else {\n          editor.on('remove', removeCss);\n        }\n      };\n      if (editor.contentStyles.length > 0) {\n        let contentCssText = '';\n        Tools.each(editor.contentStyles, style => {\n          contentCssText += style + '\\r\\n';\n        });\n        editor.dom.addStyle(contentCssText);\n      }\n      const allStylesheets = Promise.all(makeStylesheetLoadingPromises(editor, css, fontCss)).then(loaded).catch(loaded);\n      const contentStyle = getContentStyle(editor);\n      if (contentStyle) {\n        appendStyle(editor, contentStyle);\n      }\n      return allStylesheets;\n    };\n    const preInit = editor => {\n      const doc = editor.getDoc(), body = editor.getBody();\n      firePreInit(editor);\n      if (!shouldBrowserSpellcheck(editor)) {\n        doc.body.spellcheck = false;\n        DOM$6.setAttrib(body, 'spellcheck', 'false');\n      }\n      editor.quirks = Quirks(editor);\n      firePostRender(editor);\n      const directionality = getDirectionality(editor);\n      if (directionality !== undefined) {\n        body.dir = directionality;\n      }\n      const protect = getProtect(editor);\n      if (protect) {\n        editor.on('BeforeSetContent', e => {\n          Tools.each(protect, pattern => {\n            e.content = e.content.replace(pattern, str => {\n              return '<!--mce:protected ' + escape(str) + '-->';\n            });\n          });\n        });\n      }\n      editor.on('SetContent', () => {\n        editor.addVisual(editor.getBody());\n      });\n      editor.on('compositionstart compositionend', e => {\n        editor.composing = e.type === 'compositionstart';\n      });\n    };\n    const loadInitialContent = editor => {\n      if (!isRtc(editor)) {\n        editor.load({\n          initial: true,\n          format: 'html'\n        });\n      }\n      editor.startContent = editor.getContent({ format: 'raw' });\n    };\n    const initEditorWithInitialContent = editor => {\n      if (editor.removed !== true) {\n        loadInitialContent(editor);\n        initEditor(editor);\n      }\n    };\n    const contentBodyLoaded = editor => {\n      const targetElm = editor.getElement();\n      let doc = editor.getDoc();\n      if (editor.inline) {\n        DOM$6.addClass(targetElm, 'mce-content-body');\n        editor.contentDocument = doc = document;\n        editor.contentWindow = window;\n        editor.bodyElement = targetElm;\n        editor.contentAreaContainer = targetElm;\n      }\n      const body = editor.getBody();\n      body.disabled = true;\n      editor.readonly = isReadOnly$1(editor);\n      if (!editor.readonly) {\n        if (editor.inline && DOM$6.getStyle(body, 'position', true) === 'static') {\n          body.style.position = 'relative';\n        }\n        body.contentEditable = 'true';\n      }\n      body.disabled = false;\n      editor.editorUpload = EditorUpload(editor);\n      editor.schema = Schema(mkSchemaSettings(editor));\n      editor.dom = DOMUtils(doc, {\n        keep_values: true,\n        url_converter: editor.convertURL,\n        url_converter_scope: editor,\n        update_styles: true,\n        root_element: editor.inline ? editor.getBody() : null,\n        collect: editor.inline,\n        schema: editor.schema,\n        contentCssCors: shouldUseContentCssCors(editor),\n        referrerPolicy: getReferrerPolicy(editor),\n        onSetAttrib: e => {\n          editor.dispatch('SetAttrib', e);\n        }\n      });\n      editor.parser = createParser(editor);\n      editor.serializer = DomSerializer(mkSerializerSettings(editor), editor);\n      editor.selection = EditorSelection(editor.dom, editor.getWin(), editor.serializer, editor);\n      editor.annotator = Annotator(editor);\n      editor.formatter = Formatter(editor);\n      editor.undoManager = UndoManager(editor);\n      editor._nodeChangeDispatcher = new NodeChange(editor);\n      editor._selectionOverrides = SelectionOverrides(editor);\n      setup$o(editor);\n      setup$6(editor);\n      setup$m(editor);\n      if (!isRtc(editor)) {\n        setup$5(editor);\n        setup$1(editor);\n      }\n      const caret = setup$b(editor);\n      setup$p(editor, caret);\n      setup$n(editor);\n      setup$q(editor);\n      setup$7(editor);\n      const setupRtcThunk = setup$s(editor);\n      preInit(editor);\n      setupRtcThunk.fold(() => {\n        loadContentCss(editor).then(() => initEditorWithInitialContent(editor));\n      }, setupRtc => {\n        editor.setProgressState(true);\n        loadContentCss(editor).then(() => {\n          setupRtc().then(_rtcMode => {\n            editor.setProgressState(false);\n            initEditorWithInitialContent(editor);\n            bindEvents(editor);\n          }, err => {\n            editor.notificationManager.open({\n              type: 'error',\n              text: String(err)\n            });\n            initEditorWithInitialContent(editor);\n            bindEvents(editor);\n          });\n        });\n      });\n    };\n    const initContentBody = (editor, skipWrite) => {\n      if (!editor.inline) {\n        editor.getElement().style.visibility = editor.orgVisibility;\n      }\n      if (!skipWrite && !editor.inline) {\n        const iframe = editor.iframeElement;\n        const binder = bind$1(SugarElement.fromDom(iframe), 'load', () => {\n          binder.unbind();\n          editor.contentDocument = iframe.contentDocument;\n          contentBodyLoaded(editor);\n        });\n        if (Env.browser.isFirefox()) {\n          const doc = editor.getDoc();\n          doc.open();\n          doc.write(editor.iframeHTML);\n          doc.close();\n        } else {\n          iframe.srcdoc = editor.iframeHTML;\n        }\n      } else {\n        contentBodyLoaded(editor);\n      }\n    };\n\n    const DOM$5 = DOMUtils.DOM;\n    const createIframeElement = (id, title, customAttrs, tabindex) => {\n      const iframe = SugarElement.fromTag('iframe');\n      tabindex.each(t => set$2(iframe, 'tabindex', t));\n      setAll$1(iframe, customAttrs);\n      setAll$1(iframe, {\n        id: id + '_ifr',\n        frameBorder: '0',\n        allowTransparency: 'true',\n        title\n      });\n      add$2(iframe, 'tox-edit-area__iframe');\n      return iframe;\n    };\n    const getIframeHtml = editor => {\n      let iframeHTML = getDocType(editor) + '<html><head>';\n      if (getDocumentBaseUrl(editor) !== editor.documentBaseUrl) {\n        iframeHTML += '<base href=\"' + editor.documentBaseURI.getURI() + '\" />';\n      }\n      iframeHTML += '<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />';\n      const bodyId = getBodyId(editor);\n      const bodyClass = getBodyClass(editor);\n      const translatedAriaText = editor.translate(getIframeAriaText(editor));\n      if (getContentSecurityPolicy(editor)) {\n        iframeHTML += '<meta http-equiv=\"Content-Security-Policy\" content=\"' + getContentSecurityPolicy(editor) + '\" />';\n      }\n      iframeHTML += '</head>' + `<body id=\"${ bodyId }\" class=\"mce-content-body ${ bodyClass }\" data-id=\"${ editor.id }\" aria-label=\"${ translatedAriaText }\">` + '<br>' + '</body></html>';\n      return iframeHTML;\n    };\n    const createIframe = (editor, boxInfo) => {\n      const iframeTitle = editor.translate('Rich Text Area');\n      const tabindex = getOpt(SugarElement.fromDom(editor.getElement()), 'tabindex').bind(toInt);\n      const ifr = createIframeElement(editor.id, iframeTitle, getIframeAttrs(editor), tabindex).dom;\n      ifr.onload = () => {\n        ifr.onload = null;\n        editor.dispatch('load');\n      };\n      editor.contentAreaContainer = boxInfo.iframeContainer;\n      editor.iframeElement = ifr;\n      editor.iframeHTML = getIframeHtml(editor);\n      DOM$5.add(boxInfo.iframeContainer, ifr);\n    };\n    const init$1 = (editor, boxInfo) => {\n      createIframe(editor, boxInfo);\n      if (boxInfo.editorContainer) {\n        boxInfo.editorContainer.style.display = editor.orgDisplay;\n        editor.hidden = DOM$5.isHidden(boxInfo.editorContainer);\n      }\n      editor.getElement().style.display = 'none';\n      DOM$5.setAttrib(editor.id, 'aria-hidden', 'true');\n      initContentBody(editor);\n    };\n\n    const DOM$4 = DOMUtils.DOM;\n    const initPlugin = (editor, initializedPlugins, plugin) => {\n      const Plugin = PluginManager.get(plugin);\n      const pluginUrl = PluginManager.urls[plugin] || editor.documentBaseUrl.replace(/\\/$/, '');\n      plugin = Tools.trim(plugin);\n      if (Plugin && Tools.inArray(initializedPlugins, plugin) === -1) {\n        if (editor.plugins[plugin]) {\n          return;\n        }\n        try {\n          const pluginInstance = Plugin(editor, pluginUrl) || {};\n          editor.plugins[plugin] = pluginInstance;\n          if (isFunction(pluginInstance.init)) {\n            pluginInstance.init(editor, pluginUrl);\n            initializedPlugins.push(plugin);\n          }\n        } catch (e) {\n          pluginInitError(editor, plugin, e);\n        }\n      }\n    };\n    const trimLegacyPrefix = name => {\n      return name.replace(/^\\-/, '');\n    };\n    const initPlugins = editor => {\n      const initializedPlugins = [];\n      each$e(getPlugins(editor), name => {\n        initPlugin(editor, initializedPlugins, trimLegacyPrefix(name));\n      });\n    };\n    const initIcons = editor => {\n      const iconPackName = Tools.trim(getIconPackName(editor));\n      const currentIcons = editor.ui.registry.getAll().icons;\n      const loadIcons = {\n        ...IconManager.get('default').icons,\n        ...IconManager.get(iconPackName).icons\n      };\n      each$d(loadIcons, (svgData, icon) => {\n        if (!has$2(currentIcons, icon)) {\n          editor.ui.registry.addIcon(icon, svgData);\n        }\n      });\n    };\n    const initTheme = editor => {\n      const theme = getTheme(editor);\n      if (isString(theme)) {\n        const Theme = ThemeManager.get(theme);\n        editor.theme = Theme(editor, ThemeManager.urls[theme]) || {};\n        if (isFunction(editor.theme.init)) {\n          editor.theme.init(editor, ThemeManager.urls[theme] || editor.documentBaseUrl.replace(/\\/$/, ''));\n        }\n      } else {\n        editor.theme = {};\n      }\n    };\n    const initModel = editor => {\n      const model = getModel(editor);\n      const Model = ModelManager.get(model);\n      editor.model = Model(editor, ModelManager.urls[model]);\n    };\n    const renderFromLoadedTheme = editor => {\n      const render = editor.theme.renderUI;\n      return render ? render() : renderThemeFalse(editor);\n    };\n    const renderFromThemeFunc = editor => {\n      const elm = editor.getElement();\n      const theme = getTheme(editor);\n      const info = theme(editor, elm);\n      if (info.editorContainer.nodeType) {\n        info.editorContainer.id = info.editorContainer.id || editor.id + '_parent';\n      }\n      if (info.iframeContainer && info.iframeContainer.nodeType) {\n        info.iframeContainer.id = info.iframeContainer.id || editor.id + '_iframecontainer';\n      }\n      info.height = info.iframeHeight ? info.iframeHeight : elm.offsetHeight;\n      return info;\n    };\n    const createThemeFalseResult = (element, iframe) => {\n      return {\n        editorContainer: element,\n        iframeContainer: iframe,\n        api: {}\n      };\n    };\n    const renderThemeFalseIframe = targetElement => {\n      const iframeContainer = DOM$4.create('div');\n      DOM$4.insertAfter(iframeContainer, targetElement);\n      return createThemeFalseResult(iframeContainer, iframeContainer);\n    };\n    const renderThemeFalse = editor => {\n      const targetElement = editor.getElement();\n      return editor.inline ? createThemeFalseResult(null) : renderThemeFalseIframe(targetElement);\n    };\n    const renderThemeUi = editor => {\n      const elm = editor.getElement();\n      editor.orgDisplay = elm.style.display;\n      if (isString(getTheme(editor))) {\n        return renderFromLoadedTheme(editor);\n      } else if (isFunction(getTheme(editor))) {\n        return renderFromThemeFunc(editor);\n      } else {\n        return renderThemeFalse(editor);\n      }\n    };\n    const augmentEditorUiApi = (editor, api) => {\n      const uiApiFacade = {\n        show: Optional.from(api.show).getOr(noop),\n        hide: Optional.from(api.hide).getOr(noop),\n        isEnabled: Optional.from(api.isEnabled).getOr(always),\n        setEnabled: state => {\n          if (!editor.mode.isReadOnly()) {\n            Optional.from(api.setEnabled).each(f => f(state));\n          }\n        }\n      };\n      editor.ui = {\n        ...editor.ui,\n        ...uiApiFacade\n      };\n    };\n    const init = editor => {\n      editor.dispatch('ScriptsLoaded');\n      initIcons(editor);\n      initTheme(editor);\n      initModel(editor);\n      initPlugins(editor);\n      const renderInfo = renderThemeUi(editor);\n      augmentEditorUiApi(editor, Optional.from(renderInfo.api).getOr({}));\n      editor.editorContainer = renderInfo.editorContainer;\n      appendContentCssFromSettings(editor);\n      if (editor.inline) {\n        initContentBody(editor);\n      } else {\n        init$1(editor, {\n          editorContainer: renderInfo.editorContainer,\n          iframeContainer: renderInfo.iframeContainer\n        });\n      }\n    };\n\n    const DOM$3 = DOMUtils.DOM;\n    const hasSkipLoadPrefix = name => name.charAt(0) === '-';\n    const loadLanguage = (scriptLoader, editor) => {\n      const languageCode = getLanguageCode(editor);\n      const languageUrl = getLanguageUrl(editor);\n      if (!I18n.hasCode(languageCode) && languageCode !== 'en') {\n        const url = isNotEmpty(languageUrl) ? languageUrl : `${ editor.editorManager.baseURL }/langs/${ languageCode }.js`;\n        scriptLoader.add(url).catch(() => {\n          languageLoadError(editor, url, languageCode);\n        });\n      }\n    };\n    const loadTheme = (editor, suffix) => {\n      const theme = getTheme(editor);\n      if (isString(theme) && !hasSkipLoadPrefix(theme) && !has$2(ThemeManager.urls, theme)) {\n        const themeUrl = getThemeUrl(editor);\n        const url = themeUrl ? editor.documentBaseURI.toAbsolute(themeUrl) : `themes/${ theme }/theme${ suffix }.js`;\n        ThemeManager.load(theme, url).catch(() => {\n          themeLoadError(editor, url, theme);\n        });\n      }\n    };\n    const loadModel = (editor, suffix) => {\n      const model = getModel(editor);\n      if (model !== 'plugin' && !has$2(ModelManager.urls, model)) {\n        const modelUrl = getModelUrl(editor);\n        const url = isString(modelUrl) ? editor.documentBaseURI.toAbsolute(modelUrl) : `models/${ model }/model${ suffix }.js`;\n        ModelManager.load(model, url).catch(() => {\n          modelLoadError(editor, url, model);\n        });\n      }\n    };\n    const getIconsUrlMetaFromUrl = editor => Optional.from(getIconsUrl(editor)).filter(isNotEmpty).map(url => ({\n      url,\n      name: Optional.none()\n    }));\n    const getIconsUrlMetaFromName = (editor, name, suffix) => Optional.from(name).filter(name => isNotEmpty(name) && !IconManager.has(name)).map(name => ({\n      url: `${ editor.editorManager.baseURL }/icons/${ name }/icons${ suffix }.js`,\n      name: Optional.some(name)\n    }));\n    const loadIcons = (scriptLoader, editor, suffix) => {\n      const defaultIconsUrl = getIconsUrlMetaFromName(editor, 'default', suffix);\n      const customIconsUrl = getIconsUrlMetaFromUrl(editor).orThunk(() => getIconsUrlMetaFromName(editor, getIconPackName(editor), ''));\n      each$e(cat([\n        defaultIconsUrl,\n        customIconsUrl\n      ]), urlMeta => {\n        scriptLoader.add(urlMeta.url).catch(() => {\n          iconsLoadError(editor, urlMeta.url, urlMeta.name.getOrUndefined());\n        });\n      });\n    };\n    const loadPlugins = (editor, suffix) => {\n      const loadPlugin = (name, url) => {\n        PluginManager.load(name, url).catch(() => {\n          pluginLoadError(editor, url, name);\n        });\n      };\n      each$d(getExternalPlugins$1(editor), (url, name) => {\n        loadPlugin(name, url);\n        editor.options.set('plugins', getPlugins(editor).concat(name));\n      });\n      each$e(getPlugins(editor), plugin => {\n        plugin = Tools.trim(plugin);\n        if (plugin && !PluginManager.urls[plugin] && !hasSkipLoadPrefix(plugin)) {\n          loadPlugin(plugin, `plugins/${ plugin }/plugin${ suffix }.js`);\n        }\n      });\n    };\n    const isThemeLoaded = editor => {\n      const theme = getTheme(editor);\n      return !isString(theme) || isNonNullable(ThemeManager.get(theme));\n    };\n    const isModelLoaded = editor => {\n      const model = getModel(editor);\n      return isNonNullable(ModelManager.get(model));\n    };\n    const loadScripts = (editor, suffix) => {\n      const scriptLoader = ScriptLoader.ScriptLoader;\n      const initEditor = () => {\n        if (!editor.removed && isThemeLoaded(editor) && isModelLoaded(editor)) {\n          init(editor);\n        }\n      };\n      loadTheme(editor, suffix);\n      loadModel(editor, suffix);\n      loadLanguage(scriptLoader, editor);\n      loadIcons(scriptLoader, editor, suffix);\n      loadPlugins(editor, suffix);\n      scriptLoader.loadQueue().then(initEditor, initEditor);\n    };\n    const getStyleSheetLoader = (element, editor) => instance.forElement(element, {\n      contentCssCors: hasContentCssCors(editor),\n      referrerPolicy: getReferrerPolicy(editor)\n    });\n    const render = editor => {\n      const id = editor.id;\n      I18n.setCode(getLanguageCode(editor));\n      const readyHandler = () => {\n        DOM$3.unbind(window, 'ready', readyHandler);\n        editor.render();\n      };\n      if (!EventUtils.Event.domLoaded) {\n        DOM$3.bind(window, 'ready', readyHandler);\n        return;\n      }\n      if (!editor.getElement()) {\n        return;\n      }\n      const element = SugarElement.fromDom(editor.getElement());\n      const snapshot = clone$4(element);\n      editor.on('remove', () => {\n        eachr(element.dom.attributes, attr => remove$b(element, attr.name));\n        setAll$1(element, snapshot);\n      });\n      editor.ui.styleSheetLoader = getStyleSheetLoader(element, editor);\n      if (!isInline(editor)) {\n        editor.orgVisibility = editor.getElement().style.visibility;\n        editor.getElement().style.visibility = 'hidden';\n      } else {\n        editor.inline = true;\n      }\n      const form = editor.getElement().form || DOM$3.getParent(id, 'form');\n      if (form) {\n        editor.formElement = form;\n        if (hasHiddenInput(editor) && !isTextareaOrInput(editor.getElement())) {\n          DOM$3.insertAfter(DOM$3.create('input', {\n            type: 'hidden',\n            name: id\n          }), id);\n          editor.hasHiddenInput = true;\n        }\n        editor.formEventDelegate = e => {\n          editor.dispatch(e.type, e);\n        };\n        DOM$3.bind(form, 'submit reset', editor.formEventDelegate);\n        editor.on('reset', () => {\n          editor.resetContent();\n        });\n        if (shouldPatchSubmit(editor) && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {\n          form._mceOldSubmit = form.submit;\n          form.submit = () => {\n            editor.editorManager.triggerSave();\n            editor.setDirty(false);\n            return form._mceOldSubmit(form);\n          };\n        }\n      }\n      editor.windowManager = WindowManager(editor);\n      editor.notificationManager = NotificationManager(editor);\n      if (isEncodingXml(editor)) {\n        editor.on('GetContent', e => {\n          if (e.save) {\n            e.content = DOM$3.encode(e.content);\n          }\n        });\n      }\n      if (shouldAddFormSubmitTrigger(editor)) {\n        editor.on('submit', () => {\n          if (editor.initialized) {\n            editor.save();\n          }\n        });\n      }\n      if (shouldAddUnloadTrigger(editor)) {\n        editor._beforeUnload = () => {\n          if (editor.initialized && !editor.destroyed && !editor.isHidden()) {\n            editor.save({\n              format: 'raw',\n              no_events: true,\n              set_dirty: false\n            });\n          }\n        };\n        editor.editorManager.on('BeforeUnload', editor._beforeUnload);\n      }\n      editor.editorManager.add(editor);\n      loadScripts(editor, editor.suffix);\n    };\n\n    const sectionResult = (sections, settings) => ({\n      sections: constant(sections),\n      options: constant(settings)\n    });\n    const deviceDetection = detect$2().deviceType;\n    const isPhone = deviceDetection.isPhone();\n    const isTablet = deviceDetection.isTablet();\n    const normalizePlugins = plugins => {\n      if (isNullable(plugins)) {\n        return [];\n      } else {\n        const pluginNames = isArray$1(plugins) ? plugins : plugins.split(/[ ,]/);\n        const trimmedPlugins = map$3(pluginNames, trim$3);\n        return filter$5(trimmedPlugins, isNotEmpty);\n      }\n    };\n    const extractSections = (keys, options) => {\n      const result = bifilter(options, (value, key) => {\n        return contains$2(keys, key);\n      });\n      return sectionResult(result.t, result.f);\n    };\n    const getSection = (sectionResult, name, defaults = {}) => {\n      const sections = sectionResult.sections();\n      const sectionOptions = get$a(sections, name).getOr({});\n      return Tools.extend({}, defaults, sectionOptions);\n    };\n    const hasSection = (sectionResult, name) => {\n      return has$2(sectionResult.sections(), name);\n    };\n    const getSectionConfig = (sectionResult, name) => {\n      return hasSection(sectionResult, name) ? sectionResult.sections()[name] : {};\n    };\n    const getMobileOverrideOptions = (mobileOptions, isPhone) => {\n      const defaultMobileOptions = {\n        table_grid: false,\n        object_resizing: false,\n        resize: false,\n        toolbar_mode: get$a(mobileOptions, 'toolbar_mode').getOr('scrolling'),\n        toolbar_sticky: false\n      };\n      const defaultPhoneOptions = { menubar: false };\n      return {\n        ...defaultMobileOptions,\n        ...isPhone ? defaultPhoneOptions : {}\n      };\n    };\n    const getExternalPlugins = (overrideOptions, options) => {\n      var _a;\n      const userDefinedExternalPlugins = (_a = options.external_plugins) !== null && _a !== void 0 ? _a : {};\n      if (overrideOptions && overrideOptions.external_plugins) {\n        return Tools.extend({}, overrideOptions.external_plugins, userDefinedExternalPlugins);\n      } else {\n        return userDefinedExternalPlugins;\n      }\n    };\n    const combinePlugins = (forcedPlugins, plugins) => [\n      ...normalizePlugins(forcedPlugins),\n      ...normalizePlugins(plugins)\n    ];\n    const getPlatformPlugins = (isMobileDevice, sectionResult, desktopPlugins, mobilePlugins) => {\n      if (isMobileDevice && hasSection(sectionResult, 'mobile')) {\n        return mobilePlugins;\n      } else {\n        return desktopPlugins;\n      }\n    };\n    const processPlugins = (isMobileDevice, sectionResult, defaultOverrideOptions, options) => {\n      const forcedPlugins = normalizePlugins(defaultOverrideOptions.forced_plugins);\n      const desktopPlugins = normalizePlugins(options.plugins);\n      const mobileConfig = getSectionConfig(sectionResult, 'mobile');\n      const mobilePlugins = mobileConfig.plugins ? normalizePlugins(mobileConfig.plugins) : desktopPlugins;\n      const platformPlugins = getPlatformPlugins(isMobileDevice, sectionResult, desktopPlugins, mobilePlugins);\n      const combinedPlugins = combinePlugins(forcedPlugins, platformPlugins);\n      return Tools.extend(options, {\n        forced_plugins: forcedPlugins,\n        plugins: combinedPlugins\n      });\n    };\n    const isOnMobile = (isMobileDevice, sectionResult) => {\n      return isMobileDevice && hasSection(sectionResult, 'mobile');\n    };\n    const combineOptions = (isMobileDevice, isPhone, defaultOptions, defaultOverrideOptions, options) => {\n      var _a;\n      const deviceOverrideOptions = isMobileDevice ? { mobile: getMobileOverrideOptions((_a = options.mobile) !== null && _a !== void 0 ? _a : {}, isPhone) } : {};\n      const sectionResult = extractSections(['mobile'], deepMerge(deviceOverrideOptions, options));\n      const extendedOptions = Tools.extend(defaultOptions, defaultOverrideOptions, sectionResult.options(), isOnMobile(isMobileDevice, sectionResult) ? getSection(sectionResult, 'mobile') : {}, { external_plugins: getExternalPlugins(defaultOverrideOptions, sectionResult.options()) });\n      return processPlugins(isMobileDevice, sectionResult, defaultOverrideOptions, extendedOptions);\n    };\n    const normalizeOptions = (defaultOverrideOptions, options) => combineOptions(isPhone || isTablet, isPhone, options, defaultOverrideOptions, options);\n\n    const addVisual = (editor, elm) => addVisual$1(editor, elm);\n\n    const registerExecCommands$3 = editor => {\n      const toggleFormat = (name, value) => {\n        editor.formatter.toggle(name, value);\n        editor.nodeChanged();\n      };\n      const toggleAlign = align => () => {\n        each$e('left,center,right,justify'.split(','), name => {\n          if (align !== name) {\n            editor.formatter.remove('align' + name);\n          }\n        });\n        if (align !== 'none') {\n          toggleFormat('align' + align);\n        }\n      };\n      editor.editorCommands.addCommands({\n        JustifyLeft: toggleAlign('left'),\n        JustifyCenter: toggleAlign('center'),\n        JustifyRight: toggleAlign('right'),\n        JustifyFull: toggleAlign('justify'),\n        JustifyNone: toggleAlign('none')\n      });\n    };\n    const registerQueryStateCommands$1 = editor => {\n      const alignStates = name => () => {\n        const selection = editor.selection;\n        const nodes = selection.isCollapsed() ? [editor.dom.getParent(selection.getNode(), editor.dom.isBlock)] : selection.getSelectedBlocks();\n        return exists(nodes, node => isNonNullable(editor.formatter.matchNode(node, name)));\n      };\n      editor.editorCommands.addCommands({\n        JustifyLeft: alignStates('alignleft'),\n        JustifyCenter: alignStates('aligncenter'),\n        JustifyRight: alignStates('alignright'),\n        JustifyFull: alignStates('alignjustify')\n      }, 'state');\n    };\n    const registerCommands$a = editor => {\n      registerExecCommands$3(editor);\n      registerQueryStateCommands$1(editor);\n    };\n\n    const registerCommands$9 = editor => {\n      editor.editorCommands.addCommands({\n        'Cut,Copy,Paste': command => {\n          const doc = editor.getDoc();\n          let failed;\n          try {\n            doc.execCommand(command);\n          } catch (ex) {\n            failed = true;\n          }\n          if (command === 'paste' && !doc.queryCommandEnabled(command)) {\n            failed = true;\n          }\n          if (failed || !doc.queryCommandSupported(command)) {\n            let msg = editor.translate(`Your browser doesn't support direct access to the clipboard. ` + 'Please use the Ctrl+X/C/V keyboard shortcuts instead.');\n            if (Env.os.isMacOS() || Env.os.isiOS()) {\n              msg = msg.replace(/Ctrl\\+/g, '\\u2318+');\n            }\n            editor.notificationManager.open({\n              text: msg,\n              type: 'error'\n            });\n          }\n        }\n      });\n    };\n\n    const trimOrPadLeftRight = (dom, rng, html) => {\n      const root = SugarElement.fromDom(dom.getRoot());\n      if (needsToBeNbspLeft(root, CaretPosition.fromRangeStart(rng))) {\n        html = html.replace(/^ /, '&nbsp;');\n      } else {\n        html = html.replace(/^&nbsp;/, ' ');\n      }\n      if (needsToBeNbspRight(root, CaretPosition.fromRangeEnd(rng))) {\n        html = html.replace(/(&nbsp;| )(<br( \\/)>)?$/, '&nbsp;');\n      } else {\n        html = html.replace(/&nbsp;(<br( \\/)?>)?$/, ' ');\n      }\n      return html;\n    };\n\n    const processValue$1 = value => {\n      if (typeof value !== 'string') {\n        const details = Tools.extend({\n          paste: value.paste,\n          data: { paste: value.paste }\n        }, value);\n        return {\n          content: value.content,\n          details\n        };\n      }\n      return {\n        content: value,\n        details: {}\n      };\n    };\n    const trimOrPad = (editor, value) => {\n      const selection = editor.selection;\n      const dom = editor.dom;\n      if (/^ | $/.test(value)) {\n        return trimOrPadLeftRight(dom, selection.getRng(), value);\n      } else {\n        return value;\n      }\n    };\n    const insertAtCaret = (editor, value) => {\n      const {content, details} = processValue$1(value);\n      preProcessSetContent(editor, {\n        ...details,\n        content: trimOrPad(editor, content),\n        format: 'html',\n        set: false,\n        selection: true\n      }).each(args => {\n        const insertedContent = insertContent$1(editor, args.content, details);\n        postProcessSetContent(editor, insertedContent, args);\n        editor.addVisual();\n      });\n    };\n\n    const registerCommands$8 = editor => {\n      editor.editorCommands.addCommands({\n        mceCleanup: () => {\n          const bm = editor.selection.getBookmark();\n          editor.setContent(editor.getContent());\n          editor.selection.moveToBookmark(bm);\n        },\n        insertImage: (_command, _ui, value) => {\n          insertAtCaret(editor, editor.dom.createHTML('img', { src: value }));\n        },\n        insertHorizontalRule: () => {\n          editor.execCommand('mceInsertContent', false, '<hr>');\n        },\n        insertText: (_command, _ui, value) => {\n          insertAtCaret(editor, editor.dom.encode(value));\n        },\n        insertHTML: (_command, _ui, value) => {\n          insertAtCaret(editor, value);\n        },\n        mceInsertContent: (_command, _ui, value) => {\n          insertAtCaret(editor, value);\n        },\n        mceSetContent: (_command, _ui, value) => {\n          editor.setContent(value);\n        },\n        mceReplaceContent: (_command, _ui, value) => {\n          editor.execCommand('mceInsertContent', false, value.replace(/\\{\\$selection\\}/g, editor.selection.getContent({ format: 'text' })));\n        },\n        mceNewDocument: () => {\n          editor.setContent('');\n        }\n      });\n    };\n\n    const legacyPropNames = {\n      'font-size': 'size',\n      'font-family': 'face'\n    };\n    const isFont = isTag('font');\n    const getSpecifiedFontProp = (propName, rootElm, elm) => {\n      const getProperty = elm => getRaw$1(elm, propName).orThunk(() => {\n        if (isFont(elm)) {\n          return get$a(legacyPropNames, propName).bind(legacyPropName => getOpt(elm, legacyPropName));\n        } else {\n          return Optional.none();\n        }\n      });\n      const isRoot = elm => eq(SugarElement.fromDom(rootElm), elm);\n      return closest$2(SugarElement.fromDom(elm), elm => getProperty(elm), isRoot);\n    };\n    const normalizeFontFamily = fontFamily => fontFamily.replace(/[\\'\\\"\\\\]/g, '').replace(/,\\s+/g, ',');\n    const getComputedFontProp = (propName, elm) => Optional.from(DOMUtils.DOM.getStyle(elm, propName, true));\n    const getFontProp = propName => (rootElm, elm) => Optional.from(elm).map(SugarElement.fromDom).filter(isElement$7).bind(element => getSpecifiedFontProp(propName, rootElm, element.dom).or(getComputedFontProp(propName, element.dom))).getOr('');\n    const getFontSize = getFontProp('font-size');\n    const getFontFamily = compose(normalizeFontFamily, getFontProp('font-family'));\n\n    const findFirstCaretElement = editor => firstPositionIn(editor.getBody()).bind(caret => {\n      const container = caret.container();\n      return Optional.from(isText$a(container) ? container.parentNode : container);\n    });\n    const getCaretElement = editor => Optional.from(editor.selection.getRng()).bind(rng => {\n      const root = editor.getBody();\n      const atStartOfNode = rng.startContainer === root && rng.startOffset === 0;\n      return atStartOfNode ? Optional.none() : Optional.from(editor.selection.getStart(true));\n    });\n    const bindRange = (editor, binder) => getCaretElement(editor).orThunk(curry(findFirstCaretElement, editor)).map(SugarElement.fromDom).filter(isElement$7).bind(binder);\n    const mapRange = (editor, mapper) => bindRange(editor, compose1(Optional.some, mapper));\n\n    const fromFontSizeNumber = (editor, value) => {\n      if (/^[0-9.]+$/.test(value)) {\n        const fontSizeNumber = parseInt(value, 10);\n        if (fontSizeNumber >= 1 && fontSizeNumber <= 7) {\n          const fontSizes = getFontStyleValues(editor);\n          const fontClasses = getFontSizeClasses(editor);\n          if (fontClasses.length > 0) {\n            return fontClasses[fontSizeNumber - 1] || value;\n          } else {\n            return fontSizes[fontSizeNumber - 1] || value;\n          }\n        } else {\n          return value;\n        }\n      } else {\n        return value;\n      }\n    };\n    const normalizeFontNames = font => {\n      const fonts = font.split(/\\s*,\\s*/);\n      return map$3(fonts, font => {\n        if (font.indexOf(' ') !== -1 && !(startsWith(font, '\"') || startsWith(font, `'`))) {\n          return `'${ font }'`;\n        } else {\n          return font;\n        }\n      }).join(',');\n    };\n    const fontNameAction = (editor, value) => {\n      const font = fromFontSizeNumber(editor, value);\n      editor.formatter.toggle('fontname', { value: normalizeFontNames(font) });\n      editor.nodeChanged();\n    };\n    const fontNameQuery = editor => mapRange(editor, elm => getFontFamily(editor.getBody(), elm.dom)).getOr('');\n    const fontSizeAction = (editor, value) => {\n      editor.formatter.toggle('fontsize', { value: fromFontSizeNumber(editor, value) });\n      editor.nodeChanged();\n    };\n    const fontSizeQuery = editor => mapRange(editor, elm => getFontSize(editor.getBody(), elm.dom)).getOr('');\n\n    const lineHeightQuery = editor => mapRange(editor, elm => {\n      const root = SugarElement.fromDom(editor.getBody());\n      const specifiedStyle = closest$2(elm, elm => getRaw$1(elm, 'line-height'), curry(eq, root));\n      const computedStyle = () => {\n        const lineHeight = parseFloat(get$7(elm, 'line-height'));\n        const fontSize = parseFloat(get$7(elm, 'font-size'));\n        return String(lineHeight / fontSize);\n      };\n      return specifiedStyle.getOrThunk(computedStyle);\n    }).getOr('');\n    const lineHeightAction = (editor, lineHeight) => {\n      editor.formatter.toggle('lineheight', { value: String(lineHeight) });\n      editor.nodeChanged();\n    };\n\n    const registerExecCommands$2 = editor => {\n      const toggleFormat = (name, value) => {\n        editor.formatter.toggle(name, value);\n        editor.nodeChanged();\n      };\n      editor.editorCommands.addCommands({\n        'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': command => {\n          toggleFormat(command);\n        },\n        'ForeColor,HiliteColor': (command, _ui, value) => {\n          toggleFormat(command, { value });\n        },\n        'BackColor': (_command, _ui, value) => {\n          toggleFormat('hilitecolor', { value });\n        },\n        'FontName': (_command, _ui, value) => {\n          fontNameAction(editor, value);\n        },\n        'FontSize': (_command, _ui, value) => {\n          fontSizeAction(editor, value);\n        },\n        'LineHeight': (_command, _ui, value) => {\n          lineHeightAction(editor, value);\n        },\n        'Lang': (command, _ui, lang) => {\n          var _a;\n          toggleFormat(command, {\n            value: lang.code,\n            customValue: (_a = lang.customCode) !== null && _a !== void 0 ? _a : null\n          });\n        },\n        'RemoveFormat': command => {\n          editor.formatter.remove(command);\n        },\n        'mceBlockQuote': () => {\n          toggleFormat('blockquote');\n        },\n        'FormatBlock': (_command, _ui, value) => {\n          toggleFormat(isString(value) ? value : 'p');\n        },\n        'mceToggleFormat': (_command, _ui, value) => {\n          toggleFormat(value);\n        }\n      });\n    };\n    const registerQueryValueCommands = editor => {\n      const isFormatMatch = name => editor.formatter.match(name);\n      editor.editorCommands.addCommands({\n        'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': command => isFormatMatch(command),\n        'mceBlockQuote': () => isFormatMatch('blockquote')\n      }, 'state');\n      editor.editorCommands.addQueryValueHandler('FontName', () => fontNameQuery(editor));\n      editor.editorCommands.addQueryValueHandler('FontSize', () => fontSizeQuery(editor));\n      editor.editorCommands.addQueryValueHandler('LineHeight', () => lineHeightQuery(editor));\n    };\n    const registerCommands$7 = editor => {\n      registerExecCommands$2(editor);\n      registerQueryValueCommands(editor);\n    };\n\n    const registerCommands$6 = editor => {\n      editor.editorCommands.addCommands({\n        mceAddUndoLevel: () => {\n          editor.undoManager.add();\n        },\n        mceEndUndoLevel: () => {\n          editor.undoManager.add();\n        },\n        Undo: () => {\n          editor.undoManager.undo();\n        },\n        Redo: () => {\n          editor.undoManager.redo();\n        }\n      });\n    };\n\n    const registerCommands$5 = editor => {\n      editor.editorCommands.addCommands({\n        Indent: () => {\n          indent(editor);\n        },\n        Outdent: () => {\n          outdent(editor);\n        }\n      });\n      editor.editorCommands.addCommands({ Outdent: () => canOutdent(editor) }, 'state');\n    };\n\n    const registerCommands$4 = editor => {\n      const applyLinkToSelection = (_command, _ui, value) => {\n        const linkDetails = isString(value) ? { href: value } : value;\n        const anchor = editor.dom.getParent(editor.selection.getNode(), 'a');\n        if (isObject(linkDetails) && isString(linkDetails.href)) {\n          linkDetails.href = linkDetails.href.replace(/ /g, '%20');\n          if (!anchor || !linkDetails.href) {\n            editor.formatter.remove('link');\n          }\n          if (linkDetails.href) {\n            editor.formatter.apply('link', linkDetails, anchor);\n          }\n        }\n      };\n      editor.editorCommands.addCommands({\n        unlink: () => {\n          if (editor.selection.isCollapsed()) {\n            const elm = editor.dom.getParent(editor.selection.getStart(), 'a');\n            if (elm) {\n              editor.dom.remove(elm, true);\n            }\n            return;\n          }\n          editor.formatter.remove('link');\n        },\n        mceInsertLink: applyLinkToSelection,\n        createLink: applyLinkToSelection\n      });\n    };\n\n    const registerExecCommands$1 = editor => {\n      editor.editorCommands.addCommands({\n        'InsertUnorderedList,InsertOrderedList': command => {\n          editor.getDoc().execCommand(command);\n          const listElm = editor.dom.getParent(editor.selection.getNode(), 'ol,ul');\n          if (listElm) {\n            const listParent = listElm.parentNode;\n            if (listParent && /^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\n              const bm = editor.selection.getBookmark();\n              editor.dom.split(listParent, listElm);\n              editor.selection.moveToBookmark(bm);\n            }\n          }\n        }\n      });\n    };\n    const registerQueryStateCommands = editor => {\n      editor.editorCommands.addCommands({\n        'InsertUnorderedList,InsertOrderedList': command => {\n          const list = editor.dom.getParent(editor.selection.getNode(), 'ul,ol');\n          return list && (command === 'insertunorderedlist' && list.tagName === 'UL' || command === 'insertorderedlist' && list.tagName === 'OL');\n        }\n      }, 'state');\n    };\n    const registerCommands$3 = editor => {\n      registerExecCommands$1(editor);\n      registerQueryStateCommands(editor);\n    };\n\n    const registerCommands$2 = editor => {\n      editor.editorCommands.addCommands({\n        insertParagraph: () => {\n          insertBreak(blockbreak, editor);\n        },\n        mceInsertNewLine: (_command, _ui, value) => {\n          insert(editor, value);\n        },\n        InsertLineBreak: (_command, _ui, _value) => {\n          insertBreak(linebreak, editor);\n        }\n      });\n    };\n\n    const registerCommands$1 = editor => {\n      editor.editorCommands.addCommands({\n        mceSelectNodeDepth: (_command, _ui, value) => {\n          let counter = 0;\n          editor.dom.getParent(editor.selection.getNode(), node => {\n            if (isElement$6(node) && counter++ === value) {\n              editor.selection.select(node);\n              return false;\n            } else {\n              return true;\n            }\n          }, editor.getBody());\n        },\n        mceSelectNode: (_command, _ui, value) => {\n          editor.selection.select(value);\n        },\n        selectAll: () => {\n          const editingHost = editor.dom.getParent(editor.selection.getStart(), isContentEditableTrue$3);\n          if (editingHost) {\n            const rng = editor.dom.createRng();\n            rng.selectNodeContents(editingHost);\n            editor.selection.setRng(rng);\n          }\n        }\n      });\n    };\n\n    const registerExecCommands = editor => {\n      editor.editorCommands.addCommands({\n        mceRemoveNode: (_command, _ui, value) => {\n          const node = value !== null && value !== void 0 ? value : editor.selection.getNode();\n          if (node !== editor.getBody()) {\n            const bm = editor.selection.getBookmark();\n            editor.dom.remove(node, true);\n            editor.selection.moveToBookmark(bm);\n          }\n        },\n        mcePrint: () => {\n          editor.getWin().print();\n        },\n        mceFocus: (_command, _ui, value) => {\n          focus(editor, value === true);\n        },\n        mceToggleVisualAid: () => {\n          editor.hasVisual = !editor.hasVisual;\n          editor.addVisual();\n        }\n      });\n    };\n    const registerCommands = editor => {\n      registerCommands$a(editor);\n      registerCommands$9(editor);\n      registerCommands$6(editor);\n      registerCommands$1(editor);\n      registerCommands$8(editor);\n      registerCommands$4(editor);\n      registerCommands$5(editor);\n      registerCommands$2(editor);\n      registerCommands$3(editor);\n      registerCommands$7(editor);\n      registerExecCommands(editor);\n    };\n\n    const selectionSafeCommands = ['toggleview'];\n    const isSelectionSafeCommand = command => contains$2(selectionSafeCommands, command.toLowerCase());\n    class EditorCommands {\n      constructor(editor) {\n        this.commands = {\n          state: {},\n          exec: {},\n          value: {}\n        };\n        this.editor = editor;\n      }\n      execCommand(command, ui = false, value, args) {\n        const editor = this.editor;\n        const lowerCaseCommand = command.toLowerCase();\n        const skipFocus = args === null || args === void 0 ? void 0 : args.skip_focus;\n        if (editor.removed) {\n          return false;\n        }\n        if (lowerCaseCommand !== 'mcefocus') {\n          if (!/^(mceAddUndoLevel|mceEndUndoLevel)$/i.test(lowerCaseCommand) && !skipFocus) {\n            editor.focus();\n          } else {\n            restore(editor);\n          }\n        }\n        const eventArgs = editor.dispatch('BeforeExecCommand', {\n          command,\n          ui,\n          value\n        });\n        if (eventArgs.isDefaultPrevented()) {\n          return false;\n        }\n        const func = this.commands.exec[lowerCaseCommand];\n        if (isFunction(func)) {\n          func(lowerCaseCommand, ui, value);\n          editor.dispatch('ExecCommand', {\n            command,\n            ui,\n            value\n          });\n          return true;\n        }\n        return false;\n      }\n      queryCommandState(command) {\n        if (!isSelectionSafeCommand(command) && this.editor.quirks.isHidden() || this.editor.removed) {\n          return false;\n        }\n        const lowerCaseCommand = command.toLowerCase();\n        const func = this.commands.state[lowerCaseCommand];\n        if (isFunction(func)) {\n          return func(lowerCaseCommand);\n        }\n        return false;\n      }\n      queryCommandValue(command) {\n        if (!isSelectionSafeCommand(command) && this.editor.quirks.isHidden() || this.editor.removed) {\n          return '';\n        }\n        const lowerCaseCommand = command.toLowerCase();\n        const func = this.commands.value[lowerCaseCommand];\n        if (isFunction(func)) {\n          return func(lowerCaseCommand);\n        }\n        return '';\n      }\n      addCommands(commandList, type = 'exec') {\n        const commands = this.commands;\n        each$d(commandList, (callback, command) => {\n          each$e(command.toLowerCase().split(','), command => {\n            commands[type][command] = callback;\n          });\n        });\n      }\n      addCommand(command, callback, scope) {\n        const lowerCaseCommand = command.toLowerCase();\n        this.commands.exec[lowerCaseCommand] = (_command, ui, value) => callback.call(scope !== null && scope !== void 0 ? scope : this.editor, ui, value);\n      }\n      queryCommandSupported(command) {\n        const lowerCaseCommand = command.toLowerCase();\n        if (this.commands.exec[lowerCaseCommand]) {\n          return true;\n        } else {\n          return false;\n        }\n      }\n      addQueryStateHandler(command, callback, scope) {\n        this.commands.state[command.toLowerCase()] = () => callback.call(scope !== null && scope !== void 0 ? scope : this.editor);\n      }\n      addQueryValueHandler(command, callback, scope) {\n        this.commands.value[command.toLowerCase()] = () => callback.call(scope !== null && scope !== void 0 ? scope : this.editor);\n      }\n    }\n\n    const internalContentEditableAttr = 'data-mce-contenteditable';\n    const toggleClass = (elm, cls, state) => {\n      if (has(elm, cls) && !state) {\n        remove$8(elm, cls);\n      } else if (state) {\n        add$2(elm, cls);\n      }\n    };\n    const setEditorCommandState = (editor, cmd, state) => {\n      try {\n        editor.getDoc().execCommand(cmd, false, String(state));\n      } catch (ex) {\n      }\n    };\n    const setContentEditable = (elm, state) => {\n      elm.dom.contentEditable = state ? 'true' : 'false';\n    };\n    const switchOffContentEditableTrue = elm => {\n      each$e(descendants(elm, '*[contenteditable=\"true\"]'), elm => {\n        set$2(elm, internalContentEditableAttr, 'true');\n        setContentEditable(elm, false);\n      });\n    };\n    const switchOnContentEditableTrue = elm => {\n      each$e(descendants(elm, `*[${ internalContentEditableAttr }=\"true\"]`), elm => {\n        remove$b(elm, internalContentEditableAttr);\n        setContentEditable(elm, true);\n      });\n    };\n    const removeFakeSelection = editor => {\n      Optional.from(editor.selection.getNode()).each(elm => {\n        elm.removeAttribute('data-mce-selected');\n      });\n    };\n    const restoreFakeSelection = editor => {\n      editor.selection.setRng(editor.selection.getRng());\n    };\n    const toggleReadOnly = (editor, state) => {\n      const body = SugarElement.fromDom(editor.getBody());\n      toggleClass(body, 'mce-content-readonly', state);\n      if (state) {\n        editor.selection.controlSelection.hideResizeRect();\n        editor._selectionOverrides.hideFakeCaret();\n        removeFakeSelection(editor);\n        editor.readonly = true;\n        setContentEditable(body, false);\n        switchOffContentEditableTrue(body);\n      } else {\n        editor.readonly = false;\n        setContentEditable(body, true);\n        switchOnContentEditableTrue(body);\n        setEditorCommandState(editor, 'StyleWithCSS', false);\n        setEditorCommandState(editor, 'enableInlineTableEditing', false);\n        setEditorCommandState(editor, 'enableObjectResizing', false);\n        if (hasEditorOrUiFocus(editor)) {\n          editor.focus();\n        }\n        restoreFakeSelection(editor);\n        editor.nodeChanged();\n      }\n    };\n    const isReadOnly = editor => editor.readonly;\n    const registerFilters = editor => {\n      editor.parser.addAttributeFilter('contenteditable', nodes => {\n        if (isReadOnly(editor)) {\n          each$e(nodes, node => {\n            node.attr(internalContentEditableAttr, node.attr('contenteditable'));\n            node.attr('contenteditable', 'false');\n          });\n        }\n      });\n      editor.serializer.addAttributeFilter(internalContentEditableAttr, nodes => {\n        if (isReadOnly(editor)) {\n          each$e(nodes, node => {\n            node.attr('contenteditable', node.attr(internalContentEditableAttr));\n          });\n        }\n      });\n      editor.serializer.addTempAttr(internalContentEditableAttr);\n    };\n    const registerReadOnlyContentFilters = editor => {\n      if (editor.serializer) {\n        registerFilters(editor);\n      } else {\n        editor.on('PreInit', () => {\n          registerFilters(editor);\n        });\n      }\n    };\n    const isClickEvent = e => e.type === 'click';\n    const allowedEvents = ['copy'];\n    const isReadOnlyAllowedEvent = e => contains$2(allowedEvents, e.type);\n    const getAnchorHrefOpt = (editor, elm) => {\n      const isRoot = elm => eq(elm, SugarElement.fromDom(editor.getBody()));\n      return closest$3(elm, 'a', isRoot).bind(a => getOpt(a, 'href'));\n    };\n    const processReadonlyEvents = (editor, e) => {\n      if (isClickEvent(e) && !VK.metaKeyPressed(e)) {\n        const elm = SugarElement.fromDom(e.target);\n        getAnchorHrefOpt(editor, elm).each(href => {\n          e.preventDefault();\n          if (/^#/.test(href)) {\n            const targetEl = editor.dom.select(`${ href },[name=\"${ removeLeading(href, '#') }\"]`);\n            if (targetEl.length) {\n              editor.selection.scrollIntoView(targetEl[0], true);\n            }\n          } else {\n            window.open(href, '_blank', 'rel=noopener noreferrer,menubar=yes,toolbar=yes,location=yes,status=yes,resizable=yes,scrollbars=yes');\n          }\n        });\n      } else if (isReadOnlyAllowedEvent(e)) {\n        editor.dispatch(e.type, e);\n      }\n    };\n    const registerReadOnlySelectionBlockers = editor => {\n      editor.on('ShowCaret', e => {\n        if (isReadOnly(editor)) {\n          e.preventDefault();\n        }\n      });\n      editor.on('ObjectSelected', e => {\n        if (isReadOnly(editor)) {\n          e.preventDefault();\n        }\n      });\n    };\n\n    const nativeEvents = Tools.makeMap('focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange ' + 'mouseout mouseenter mouseleave wheel keydown keypress keyup input beforeinput contextmenu dragstart dragend dragover ' + 'draggesture dragdrop drop drag submit ' + 'compositionstart compositionend compositionupdate touchstart touchmove touchend touchcancel', ' ');\n    class EventDispatcher {\n      constructor(settings) {\n        this.bindings = {};\n        this.settings = settings || {};\n        this.scope = this.settings.scope || this;\n        this.toggleEvent = this.settings.toggleEvent || never;\n      }\n      static isNative(name) {\n        return !!nativeEvents[name.toLowerCase()];\n      }\n      fire(name, args) {\n        return this.dispatch(name, args);\n      }\n      dispatch(name, args) {\n        const lcName = name.toLowerCase();\n        const event = normalize$3(lcName, args !== null && args !== void 0 ? args : {}, this.scope);\n        if (this.settings.beforeFire) {\n          this.settings.beforeFire(event);\n        }\n        const handlers = this.bindings[lcName];\n        if (handlers) {\n          for (let i = 0, l = handlers.length; i < l; i++) {\n            const callback = handlers[i];\n            if (callback.removed) {\n              continue;\n            }\n            if (callback.once) {\n              this.off(lcName, callback.func);\n            }\n            if (event.isImmediatePropagationStopped()) {\n              return event;\n            }\n            if (callback.func.call(this.scope, event) === false) {\n              event.preventDefault();\n              return event;\n            }\n          }\n        }\n        return event;\n      }\n      on(name, callback, prepend, extra) {\n        if (callback === false) {\n          callback = never;\n        }\n        if (callback) {\n          const wrappedCallback = {\n            func: callback,\n            removed: false\n          };\n          if (extra) {\n            Tools.extend(wrappedCallback, extra);\n          }\n          const names = name.toLowerCase().split(' ');\n          let i = names.length;\n          while (i--) {\n            const currentName = names[i];\n            let handlers = this.bindings[currentName];\n            if (!handlers) {\n              handlers = [];\n              this.toggleEvent(currentName, true);\n            }\n            if (prepend) {\n              handlers = [\n                wrappedCallback,\n                ...handlers\n              ];\n            } else {\n              handlers = [\n                ...handlers,\n                wrappedCallback\n              ];\n            }\n            this.bindings[currentName] = handlers;\n          }\n        }\n        return this;\n      }\n      off(name, callback) {\n        if (name) {\n          const names = name.toLowerCase().split(' ');\n          let i = names.length;\n          while (i--) {\n            const currentName = names[i];\n            let handlers = this.bindings[currentName];\n            if (!currentName) {\n              each$d(this.bindings, (_value, bindingName) => {\n                this.toggleEvent(bindingName, false);\n                delete this.bindings[bindingName];\n              });\n              return this;\n            }\n            if (handlers) {\n              if (!callback) {\n                handlers.length = 0;\n              } else {\n                const filteredHandlers = partition$2(handlers, handler => handler.func === callback);\n                handlers = filteredHandlers.fail;\n                this.bindings[currentName] = handlers;\n                each$e(filteredHandlers.pass, handler => {\n                  handler.removed = true;\n                });\n              }\n              if (!handlers.length) {\n                this.toggleEvent(name, false);\n                delete this.bindings[currentName];\n              }\n            }\n          }\n        } else {\n          each$d(this.bindings, (_value, name) => {\n            this.toggleEvent(name, false);\n          });\n          this.bindings = {};\n        }\n        return this;\n      }\n      once(name, callback, prepend) {\n        return this.on(name, callback, prepend, { once: true });\n      }\n      has(name) {\n        name = name.toLowerCase();\n        const binding = this.bindings[name];\n        return !(!binding || binding.length === 0);\n      }\n    }\n\n    const getEventDispatcher = obj => {\n      if (!obj._eventDispatcher) {\n        obj._eventDispatcher = new EventDispatcher({\n          scope: obj,\n          toggleEvent: (name, state) => {\n            if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) {\n              obj.toggleNativeEvent(name, state);\n            }\n          }\n        });\n      }\n      return obj._eventDispatcher;\n    };\n    const Observable = {\n      fire(name, args, bubble) {\n        return this.dispatch(name, args, bubble);\n      },\n      dispatch(name, args, bubble) {\n        const self = this;\n        if (self.removed && name !== 'remove' && name !== 'detach') {\n          return normalize$3(name.toLowerCase(), args !== null && args !== void 0 ? args : {}, self);\n        }\n        const dispatcherArgs = getEventDispatcher(self).dispatch(name, args);\n        if (bubble !== false && self.parent) {\n          let parent = self.parent();\n          while (parent && !dispatcherArgs.isPropagationStopped()) {\n            parent.dispatch(name, dispatcherArgs, false);\n            parent = parent.parent ? parent.parent() : undefined;\n          }\n        }\n        return dispatcherArgs;\n      },\n      on(name, callback, prepend) {\n        return getEventDispatcher(this).on(name, callback, prepend);\n      },\n      off(name, callback) {\n        return getEventDispatcher(this).off(name, callback);\n      },\n      once(name, callback) {\n        return getEventDispatcher(this).once(name, callback);\n      },\n      hasEventListeners(name) {\n        return getEventDispatcher(this).has(name);\n      }\n    };\n\n    const DOM$2 = DOMUtils.DOM;\n    let customEventRootDelegates;\n    const getEventTarget = (editor, eventName) => {\n      if (eventName === 'selectionchange') {\n        return editor.getDoc();\n      }\n      if (!editor.inline && /^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(eventName)) {\n        return editor.getDoc().documentElement;\n      }\n      const eventRoot = getEventRoot(editor);\n      if (eventRoot) {\n        if (!editor.eventRoot) {\n          editor.eventRoot = DOM$2.select(eventRoot)[0];\n        }\n        return editor.eventRoot;\n      }\n      return editor.getBody();\n    };\n    const isListening = editor => !editor.hidden && !isReadOnly(editor);\n    const fireEvent = (editor, eventName, e) => {\n      if (isListening(editor)) {\n        editor.dispatch(eventName, e);\n      } else if (isReadOnly(editor)) {\n        processReadonlyEvents(editor, e);\n      }\n    };\n    const bindEventDelegate = (editor, eventName) => {\n      if (!editor.delegates) {\n        editor.delegates = {};\n      }\n      if (editor.delegates[eventName] || editor.removed) {\n        return;\n      }\n      const eventRootElm = getEventTarget(editor, eventName);\n      if (getEventRoot(editor)) {\n        if (!customEventRootDelegates) {\n          customEventRootDelegates = {};\n          editor.editorManager.on('removeEditor', () => {\n            if (!editor.editorManager.activeEditor) {\n              if (customEventRootDelegates) {\n                each$d(customEventRootDelegates, (_value, name) => {\n                  editor.dom.unbind(getEventTarget(editor, name));\n                });\n                customEventRootDelegates = null;\n              }\n            }\n          });\n        }\n        if (customEventRootDelegates[eventName]) {\n          return;\n        }\n        const delegate = e => {\n          const target = e.target;\n          const editors = editor.editorManager.get();\n          let i = editors.length;\n          while (i--) {\n            const body = editors[i].getBody();\n            if (body === target || DOM$2.isChildOf(target, body)) {\n              fireEvent(editors[i], eventName, e);\n            }\n          }\n        };\n        customEventRootDelegates[eventName] = delegate;\n        DOM$2.bind(eventRootElm, eventName, delegate);\n      } else {\n        const delegate = e => {\n          fireEvent(editor, eventName, e);\n        };\n        DOM$2.bind(eventRootElm, eventName, delegate);\n        editor.delegates[eventName] = delegate;\n      }\n    };\n    const EditorObservable = {\n      ...Observable,\n      bindPendingEventDelegates() {\n        const self = this;\n        Tools.each(self._pendingNativeEvents, name => {\n          bindEventDelegate(self, name);\n        });\n      },\n      toggleNativeEvent(name, state) {\n        const self = this;\n        if (name === 'focus' || name === 'blur') {\n          return;\n        }\n        if (self.removed) {\n          return;\n        }\n        if (state) {\n          if (self.initialized) {\n            bindEventDelegate(self, name);\n          } else {\n            if (!self._pendingNativeEvents) {\n              self._pendingNativeEvents = [name];\n            } else {\n              self._pendingNativeEvents.push(name);\n            }\n          }\n        } else if (self.initialized && self.delegates) {\n          self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);\n          delete self.delegates[name];\n        }\n      },\n      unbindAllNativeEvents() {\n        const self = this;\n        const body = self.getBody();\n        const dom = self.dom;\n        if (self.delegates) {\n          each$d(self.delegates, (value, name) => {\n            self.dom.unbind(getEventTarget(self, name), name, value);\n          });\n          delete self.delegates;\n        }\n        if (!self.inline && body && dom) {\n          body.onload = null;\n          dom.unbind(self.getWin());\n          dom.unbind(self.getDoc());\n        }\n        if (dom) {\n          dom.unbind(body);\n          dom.unbind(self.getContainer());\n        }\n      }\n    };\n\n    const stringListProcessor = value => {\n      if (isString(value)) {\n        return {\n          value: value.split(/[ ,]/),\n          valid: true\n        };\n      } else if (isArrayOf(value, isString)) {\n        return {\n          value,\n          valid: true\n        };\n      } else {\n        return {\n          valid: false,\n          message: `The value must be a string[] or a comma/space separated string.`\n        };\n      }\n    };\n    const getBuiltInProcessor = type => {\n      const validator = (() => {\n        switch (type) {\n        case 'array':\n          return isArray$1;\n        case 'boolean':\n          return isBoolean;\n        case 'function':\n          return isFunction;\n        case 'number':\n          return isNumber;\n        case 'object':\n          return isObject;\n        case 'string':\n          return isString;\n        case 'string[]':\n          return stringListProcessor;\n        case 'object[]':\n          return val => isArrayOf(val, isObject);\n        case 'regexp':\n          return val => is$4(val, RegExp);\n        default:\n          return always;\n        }\n      })();\n      return value => processValue(value, validator, `The value must be a ${ type }.`);\n    };\n    const isBuiltInSpec = spec => isString(spec.processor);\n    const getErrorMessage = (message, result) => {\n      const additionalText = isEmpty$3(result.message) ? '' : `. ${ result.message }`;\n      return message + additionalText;\n    };\n    const isValidResult = result => result.valid;\n    const processValue = (value, processor, message = '') => {\n      const result = processor(value);\n      if (isBoolean(result)) {\n        return result ? {\n          value: value,\n          valid: true\n        } : {\n          valid: false,\n          message\n        };\n      } else {\n        return result;\n      }\n    };\n    const processDefaultValue = (name, defaultValue, processor) => {\n      if (!isUndefined(defaultValue)) {\n        const result = processValue(defaultValue, processor);\n        if (isValidResult(result)) {\n          return result.value;\n        } else {\n          console.error(getErrorMessage(`Invalid default value passed for the \"${ name }\" option`, result));\n        }\n      }\n      return undefined;\n    };\n    const create$5 = (editor, initialOptions) => {\n      const registry = {};\n      const values = {};\n      const setValue = (name, value, processor) => {\n        const result = processValue(value, processor);\n        if (isValidResult(result)) {\n          values[name] = result.value;\n          return true;\n        } else {\n          console.warn(getErrorMessage(`Invalid value passed for the ${ name } option`, result));\n          return false;\n        }\n      };\n      const register = (name, spec) => {\n        const processor = isBuiltInSpec(spec) ? getBuiltInProcessor(spec.processor) : spec.processor;\n        const defaultValue = processDefaultValue(name, spec.default, processor);\n        registry[name] = {\n          ...spec,\n          default: defaultValue,\n          processor\n        };\n        const initValue = get$a(values, name).orThunk(() => get$a(initialOptions, name));\n        initValue.each(value => setValue(name, value, processor));\n      };\n      const isRegistered = name => has$2(registry, name);\n      const get = name => get$a(values, name).orThunk(() => get$a(registry, name).map(spec => spec.default)).getOrUndefined();\n      const set = (name, value) => {\n        if (!isRegistered(name)) {\n          console.warn(`\"${ name }\" is not a registered option. Ensure the option has been registered before setting a value.`);\n          return false;\n        } else {\n          const spec = registry[name];\n          if (spec.immutable) {\n            console.error(`\"${ name }\" is an immutable option and cannot be updated`);\n            return false;\n          } else {\n            return setValue(name, value, spec.processor);\n          }\n        }\n      };\n      const unset = name => {\n        const registered = isRegistered(name);\n        if (registered) {\n          delete values[name];\n        }\n        return registered;\n      };\n      const isSet = name => has$2(values, name);\n      return {\n        register,\n        isRegistered,\n        get,\n        set,\n        unset,\n        isSet\n      };\n    };\n\n    const defaultModes = [\n      'design',\n      'readonly'\n    ];\n    const switchToMode = (editor, activeMode, availableModes, mode) => {\n      const oldMode = availableModes[activeMode.get()];\n      const newMode = availableModes[mode];\n      try {\n        newMode.activate();\n      } catch (e) {\n        console.error(`problem while activating editor mode ${ mode }:`, e);\n        return;\n      }\n      oldMode.deactivate();\n      if (oldMode.editorReadOnly !== newMode.editorReadOnly) {\n        toggleReadOnly(editor, newMode.editorReadOnly);\n      }\n      activeMode.set(mode);\n      fireSwitchMode(editor, mode);\n    };\n    const setMode = (editor, availableModes, activeMode, mode) => {\n      if (mode === activeMode.get()) {\n        return;\n      } else if (!has$2(availableModes, mode)) {\n        throw new Error(`Editor mode '${ mode }' is invalid`);\n      }\n      if (editor.initialized) {\n        switchToMode(editor, activeMode, availableModes, mode);\n      } else {\n        editor.on('init', () => switchToMode(editor, activeMode, availableModes, mode));\n      }\n    };\n    const registerMode = (availableModes, mode, api) => {\n      if (contains$2(defaultModes, mode)) {\n        throw new Error(`Cannot override default mode ${ mode }`);\n      }\n      return {\n        ...availableModes,\n        [mode]: {\n          ...api,\n          deactivate: () => {\n            try {\n              api.deactivate();\n            } catch (e) {\n              console.error(`problem while deactivating editor mode ${ mode }:`, e);\n            }\n          }\n        }\n      };\n    };\n\n    const create$4 = editor => {\n      const activeMode = Cell('design');\n      const availableModes = Cell({\n        design: {\n          activate: noop,\n          deactivate: noop,\n          editorReadOnly: false\n        },\n        readonly: {\n          activate: noop,\n          deactivate: noop,\n          editorReadOnly: true\n        }\n      });\n      registerReadOnlyContentFilters(editor);\n      registerReadOnlySelectionBlockers(editor);\n      return {\n        isReadOnly: () => isReadOnly(editor),\n        set: mode => setMode(editor, availableModes.get(), activeMode, mode),\n        get: () => activeMode.get(),\n        register: (mode, api) => {\n          availableModes.set(registerMode(availableModes.get(), mode, api));\n        }\n      };\n    };\n\n    const each$2 = Tools.each, explode = Tools.explode;\n    const keyCodeLookup = {\n      f1: 112,\n      f2: 113,\n      f3: 114,\n      f4: 115,\n      f5: 116,\n      f6: 117,\n      f7: 118,\n      f8: 119,\n      f9: 120,\n      f10: 121,\n      f11: 122,\n      f12: 123\n    };\n    const modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access');\n    const isModifier = key => key in modifierNames;\n    const parseShortcut = pattern => {\n      const shortcut = {};\n      const isMac = Env.os.isMacOS() || Env.os.isiOS();\n      each$2(explode(pattern.toLowerCase(), '+'), value => {\n        if (isModifier(value)) {\n          shortcut[value] = true;\n        } else {\n          if (/^[0-9]{2,}$/.test(value)) {\n            shortcut.keyCode = parseInt(value, 10);\n          } else {\n            shortcut.charCode = value.charCodeAt(0);\n            shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);\n          }\n        }\n      });\n      const id = [shortcut.keyCode];\n      let key;\n      for (key in modifierNames) {\n        if (shortcut[key]) {\n          id.push(key);\n        } else {\n          shortcut[key] = false;\n        }\n      }\n      shortcut.id = id.join(',');\n      if (shortcut.access) {\n        shortcut.alt = true;\n        if (isMac) {\n          shortcut.ctrl = true;\n        } else {\n          shortcut.shift = true;\n        }\n      }\n      if (shortcut.meta) {\n        if (isMac) {\n          shortcut.meta = true;\n        } else {\n          shortcut.ctrl = true;\n          shortcut.meta = false;\n        }\n      }\n      return shortcut;\n    };\n    class Shortcuts {\n      constructor(editor) {\n        this.shortcuts = {};\n        this.pendingPatterns = [];\n        this.editor = editor;\n        const self = this;\n        editor.on('keyup keypress keydown', e => {\n          if ((self.hasModifier(e) || self.isFunctionKey(e)) && !e.isDefaultPrevented()) {\n            each$2(self.shortcuts, shortcut => {\n              if (self.matchShortcut(e, shortcut)) {\n                self.pendingPatterns = shortcut.subpatterns.slice(0);\n                if (e.type === 'keydown') {\n                  self.executeShortcutAction(shortcut);\n                }\n              }\n            });\n            if (self.matchShortcut(e, self.pendingPatterns[0])) {\n              if (self.pendingPatterns.length === 1) {\n                if (e.type === 'keydown') {\n                  self.executeShortcutAction(self.pendingPatterns[0]);\n                }\n              }\n              self.pendingPatterns.shift();\n            }\n          }\n        });\n      }\n      add(pattern, desc, cmdFunc, scope) {\n        const self = this;\n        const func = self.normalizeCommandFunc(cmdFunc);\n        each$2(explode(Tools.trim(pattern)), pattern => {\n          const shortcut = self.createShortcut(pattern, desc, func, scope);\n          self.shortcuts[shortcut.id] = shortcut;\n        });\n        return true;\n      }\n      remove(pattern) {\n        const shortcut = this.createShortcut(pattern);\n        if (this.shortcuts[shortcut.id]) {\n          delete this.shortcuts[shortcut.id];\n          return true;\n        }\n        return false;\n      }\n      normalizeCommandFunc(cmdFunc) {\n        const self = this;\n        const cmd = cmdFunc;\n        if (typeof cmd === 'string') {\n          return () => {\n            self.editor.execCommand(cmd, false, null);\n          };\n        } else if (Tools.isArray(cmd)) {\n          return () => {\n            self.editor.execCommand(cmd[0], cmd[1], cmd[2]);\n          };\n        } else {\n          return cmd;\n        }\n      }\n      createShortcut(pattern, desc, cmdFunc, scope) {\n        const shortcuts = Tools.map(explode(pattern, '>'), parseShortcut);\n        shortcuts[shortcuts.length - 1] = Tools.extend(shortcuts[shortcuts.length - 1], {\n          func: cmdFunc,\n          scope: scope || this.editor\n        });\n        return Tools.extend(shortcuts[0], {\n          desc: this.editor.translate(desc),\n          subpatterns: shortcuts.slice(1)\n        });\n      }\n      hasModifier(e) {\n        return e.altKey || e.ctrlKey || e.metaKey;\n      }\n      isFunctionKey(e) {\n        return e.type === 'keydown' && e.keyCode >= 112 && e.keyCode <= 123;\n      }\n      matchShortcut(e, shortcut) {\n        if (!shortcut) {\n          return false;\n        }\n        if (shortcut.ctrl !== e.ctrlKey || shortcut.meta !== e.metaKey) {\n          return false;\n        }\n        if (shortcut.alt !== e.altKey || shortcut.shift !== e.shiftKey) {\n          return false;\n        }\n        if (e.keyCode === shortcut.keyCode || e.charCode && e.charCode === shortcut.charCode) {\n          e.preventDefault();\n          return true;\n        }\n        return false;\n      }\n      executeShortcutAction(shortcut) {\n        return shortcut.func ? shortcut.func.call(shortcut.scope) : null;\n      }\n    }\n\n    const create$3 = () => {\n      const buttons = {};\n      const menuItems = {};\n      const popups = {};\n      const icons = {};\n      const contextMenus = {};\n      const contextToolbars = {};\n      const sidebars = {};\n      const views = {};\n      const add = (collection, type) => (name, spec) => {\n        collection[name.toLowerCase()] = {\n          ...spec,\n          type\n        };\n      };\n      const addIcon = (name, svgData) => icons[name.toLowerCase()] = svgData;\n      return {\n        addButton: add(buttons, 'button'),\n        addGroupToolbarButton: add(buttons, 'grouptoolbarbutton'),\n        addToggleButton: add(buttons, 'togglebutton'),\n        addMenuButton: add(buttons, 'menubutton'),\n        addSplitButton: add(buttons, 'splitbutton'),\n        addMenuItem: add(menuItems, 'menuitem'),\n        addNestedMenuItem: add(menuItems, 'nestedmenuitem'),\n        addToggleMenuItem: add(menuItems, 'togglemenuitem'),\n        addAutocompleter: add(popups, 'autocompleter'),\n        addContextMenu: add(contextMenus, 'contextmenu'),\n        addContextToolbar: add(contextToolbars, 'contexttoolbar'),\n        addContextForm: add(contextToolbars, 'contextform'),\n        addSidebar: add(sidebars, 'sidebar'),\n        addView: add(views, 'views'),\n        addIcon,\n        getAll: () => ({\n          buttons,\n          menuItems,\n          icons,\n          popups,\n          contextMenus,\n          contextToolbars,\n          sidebars,\n          views\n        })\n      };\n    };\n\n    const registry = () => {\n      const bridge = create$3();\n      return {\n        addAutocompleter: bridge.addAutocompleter,\n        addButton: bridge.addButton,\n        addContextForm: bridge.addContextForm,\n        addContextMenu: bridge.addContextMenu,\n        addContextToolbar: bridge.addContextToolbar,\n        addIcon: bridge.addIcon,\n        addMenuButton: bridge.addMenuButton,\n        addMenuItem: bridge.addMenuItem,\n        addNestedMenuItem: bridge.addNestedMenuItem,\n        addSidebar: bridge.addSidebar,\n        addSplitButton: bridge.addSplitButton,\n        addToggleButton: bridge.addToggleButton,\n        addGroupToolbarButton: bridge.addGroupToolbarButton,\n        addToggleMenuItem: bridge.addToggleMenuItem,\n        addView: bridge.addView,\n        getAll: bridge.getAll\n      };\n    };\n\n    const DOM$1 = DOMUtils.DOM;\n    const extend = Tools.extend, each$1 = Tools.each;\n    class Editor {\n      constructor(id, options, editorManager) {\n        this.plugins = {};\n        this.contentCSS = [];\n        this.contentStyles = [];\n        this.loadedCSS = {};\n        this.isNotDirty = false;\n        this.composing = false;\n        this.destroyed = false;\n        this.hasHiddenInput = false;\n        this.iframeElement = null;\n        this.initialized = false;\n        this.readonly = false;\n        this.removed = false;\n        this.startContent = '';\n        this._pendingNativeEvents = [];\n        this._skinLoaded = false;\n        this.editorManager = editorManager;\n        this.documentBaseUrl = editorManager.documentBaseURL;\n        extend(this, EditorObservable);\n        const self = this;\n        this.id = id;\n        this.hidden = false;\n        const normalizedOptions = normalizeOptions(editorManager.defaultOptions, options);\n        this.options = create$5(self, normalizedOptions);\n        register$7(self);\n        const getOption = this.options.get;\n        if (getOption('deprecation_warnings')) {\n          logWarnings(options, normalizedOptions);\n        }\n        const suffix = getOption('suffix');\n        if (suffix) {\n          editorManager.suffix = suffix;\n        }\n        this.suffix = editorManager.suffix;\n        const baseUrl = getOption('base_url');\n        if (baseUrl) {\n          editorManager._setBaseUrl(baseUrl);\n        }\n        this.baseUri = editorManager.baseURI;\n        const referrerPolicy = getReferrerPolicy(self);\n        if (referrerPolicy) {\n          ScriptLoader.ScriptLoader._setReferrerPolicy(referrerPolicy);\n          DOMUtils.DOM.styleSheetLoader._setReferrerPolicy(referrerPolicy);\n        }\n        const contentCssCors = hasContentCssCors(self);\n        if (isNonNullable(contentCssCors)) {\n          DOMUtils.DOM.styleSheetLoader._setContentCssCors(contentCssCors);\n        }\n        AddOnManager.languageLoad = getOption('language_load');\n        AddOnManager.baseURL = editorManager.baseURL;\n        this.setDirty(false);\n        this.documentBaseURI = new URI(getDocumentBaseUrl(self), { base_uri: this.baseUri });\n        this.baseURI = this.baseUri;\n        this.inline = isInline(self);\n        this.hasVisual = isVisualAidsEnabled(self);\n        this.shortcuts = new Shortcuts(this);\n        this.editorCommands = new EditorCommands(this);\n        registerCommands(this);\n        const cacheSuffix = getOption('cache_suffix');\n        if (cacheSuffix) {\n          Env.cacheSuffix = cacheSuffix.replace(/^[\\?\\&]+/, '');\n        }\n        this.ui = {\n          registry: registry(),\n          styleSheetLoader: undefined,\n          show: noop,\n          hide: noop,\n          setEnabled: noop,\n          isEnabled: always\n        };\n        this.mode = create$4(self);\n        editorManager.dispatch('SetupEditor', { editor: this });\n        const setupCallback = getSetupCallback(self);\n        if (isFunction(setupCallback)) {\n          setupCallback.call(self, self);\n        }\n      }\n      render() {\n        render(this);\n      }\n      focus(skipFocus) {\n        this.execCommand('mceFocus', false, skipFocus);\n      }\n      hasFocus() {\n        return hasFocus(this);\n      }\n      translate(text) {\n        return I18n.translate(text);\n      }\n      getParam(name, defaultVal, type) {\n        const options = this.options;\n        if (!options.isRegistered(name)) {\n          if (isNonNullable(type)) {\n            options.register(name, {\n              processor: type,\n              default: defaultVal\n            });\n          } else {\n            options.register(name, {\n              processor: always,\n              default: defaultVal\n            });\n          }\n        }\n        return !options.isSet(name) && !isUndefined(defaultVal) ? defaultVal : options.get(name);\n      }\n      hasPlugin(name, loaded) {\n        const hasPlugin = contains$2(getPlugins(this), name);\n        if (hasPlugin) {\n          return loaded ? PluginManager.get(name) !== undefined : true;\n        } else {\n          return false;\n        }\n      }\n      nodeChanged(args) {\n        this._nodeChangeDispatcher.nodeChanged(args);\n      }\n      addCommand(name, callback, scope) {\n        this.editorCommands.addCommand(name, callback, scope);\n      }\n      addQueryStateHandler(name, callback, scope) {\n        this.editorCommands.addQueryStateHandler(name, callback, scope);\n      }\n      addQueryValueHandler(name, callback, scope) {\n        this.editorCommands.addQueryValueHandler(name, callback, scope);\n      }\n      addShortcut(pattern, desc, cmdFunc, scope) {\n        this.shortcuts.add(pattern, desc, cmdFunc, scope);\n      }\n      execCommand(cmd, ui, value, args) {\n        return this.editorCommands.execCommand(cmd, ui, value, args);\n      }\n      queryCommandState(cmd) {\n        return this.editorCommands.queryCommandState(cmd);\n      }\n      queryCommandValue(cmd) {\n        return this.editorCommands.queryCommandValue(cmd);\n      }\n      queryCommandSupported(cmd) {\n        return this.editorCommands.queryCommandSupported(cmd);\n      }\n      show() {\n        const self = this;\n        if (self.hidden) {\n          self.hidden = false;\n          if (self.inline) {\n            self.getBody().contentEditable = 'true';\n          } else {\n            DOM$1.show(self.getContainer());\n            DOM$1.hide(self.id);\n          }\n          self.load();\n          self.dispatch('show');\n        }\n      }\n      hide() {\n        const self = this;\n        if (!self.hidden) {\n          self.save();\n          if (self.inline) {\n            self.getBody().contentEditable = 'false';\n            if (self === self.editorManager.focusedEditor) {\n              self.editorManager.focusedEditor = null;\n            }\n          } else {\n            DOM$1.hide(self.getContainer());\n            DOM$1.setStyle(self.id, 'display', self.orgDisplay);\n          }\n          self.hidden = true;\n          self.dispatch('hide');\n        }\n      }\n      isHidden() {\n        return this.hidden;\n      }\n      setProgressState(state, time) {\n        this.dispatch('ProgressState', {\n          state,\n          time\n        });\n      }\n      load(args = {}) {\n        const self = this;\n        const elm = self.getElement();\n        if (self.removed) {\n          return '';\n        }\n        if (elm) {\n          const loadArgs = {\n            ...args,\n            load: true\n          };\n          const value = isTextareaOrInput(elm) ? elm.value : elm.innerHTML;\n          const html = self.setContent(value, loadArgs);\n          if (!loadArgs.no_events) {\n            self.dispatch('LoadContent', {\n              ...loadArgs,\n              element: elm\n            });\n          }\n          return html;\n        } else {\n          return '';\n        }\n      }\n      save(args = {}) {\n        const self = this;\n        let elm = self.getElement();\n        if (!elm || !self.initialized || self.removed) {\n          return '';\n        }\n        const getArgs = {\n          ...args,\n          save: true,\n          element: elm\n        };\n        let html = self.getContent(getArgs);\n        const saveArgs = {\n          ...getArgs,\n          content: html\n        };\n        if (!saveArgs.no_events) {\n          self.dispatch('SaveContent', saveArgs);\n        }\n        if (saveArgs.format === 'raw') {\n          self.dispatch('RawSaveContent', saveArgs);\n        }\n        html = saveArgs.content;\n        if (!isTextareaOrInput(elm)) {\n          if (args.is_removing || !self.inline) {\n            elm.innerHTML = html;\n          }\n          const form = DOM$1.getParent(self.id, 'form');\n          if (form) {\n            each$1(form.elements, elm => {\n              if (elm.name === self.id) {\n                elm.value = html;\n                return false;\n              } else {\n                return true;\n              }\n            });\n          }\n        } else {\n          elm.value = html;\n        }\n        saveArgs.element = getArgs.element = elm = null;\n        if (saveArgs.set_dirty !== false) {\n          self.setDirty(false);\n        }\n        return html;\n      }\n      setContent(content, args) {\n        return setContent(this, content, args);\n      }\n      getContent(args) {\n        return getContent(this, args);\n      }\n      insertContent(content, args) {\n        if (args) {\n          content = extend({ content }, args);\n        }\n        this.execCommand('mceInsertContent', false, content);\n      }\n      resetContent(initialContent) {\n        if (initialContent === undefined) {\n          setContent(this, this.startContent, { format: 'raw' });\n        } else {\n          setContent(this, initialContent);\n        }\n        this.undoManager.reset();\n        this.setDirty(false);\n        this.nodeChanged();\n      }\n      isDirty() {\n        return !this.isNotDirty;\n      }\n      setDirty(state) {\n        const oldState = !this.isNotDirty;\n        this.isNotDirty = !state;\n        if (state && state !== oldState) {\n          this.dispatch('dirty');\n        }\n      }\n      getContainer() {\n        const self = this;\n        if (!self.container) {\n          self.container = self.editorContainer || DOM$1.get(self.id + '_parent');\n        }\n        return self.container;\n      }\n      getContentAreaContainer() {\n        return this.contentAreaContainer;\n      }\n      getElement() {\n        if (!this.targetElm) {\n          this.targetElm = DOM$1.get(this.id);\n        }\n        return this.targetElm;\n      }\n      getWin() {\n        const self = this;\n        if (!self.contentWindow) {\n          const elm = self.iframeElement;\n          if (elm) {\n            self.contentWindow = elm.contentWindow;\n          }\n        }\n        return self.contentWindow;\n      }\n      getDoc() {\n        const self = this;\n        if (!self.contentDocument) {\n          const win = self.getWin();\n          if (win) {\n            self.contentDocument = win.document;\n          }\n        }\n        return self.contentDocument;\n      }\n      getBody() {\n        var _a, _b;\n        const doc = this.getDoc();\n        return (_b = (_a = this.bodyElement) !== null && _a !== void 0 ? _a : doc === null || doc === void 0 ? void 0 : doc.body) !== null && _b !== void 0 ? _b : null;\n      }\n      convertURL(url, name, elm) {\n        const self = this, getOption = self.options.get;\n        const urlConverterCallback = getUrlConverterCallback(self);\n        if (isFunction(urlConverterCallback)) {\n          return urlConverterCallback.call(self, url, elm, true, name);\n        }\n        if (!getOption('convert_urls') || elm === 'link' || isObject(elm) && elm.nodeName === 'LINK' || url.indexOf('file:') === 0 || url.length === 0) {\n          return url;\n        }\n        if (getOption('relative_urls')) {\n          return self.documentBaseURI.toRelative(url);\n        }\n        url = self.documentBaseURI.toAbsolute(url, getOption('remove_script_host'));\n        return url;\n      }\n      addVisual(elm) {\n        addVisual(this, elm);\n      }\n      remove() {\n        remove$1(this);\n      }\n      destroy(automatic) {\n        destroy(this, automatic);\n      }\n      uploadImages() {\n        return this.editorUpload.uploadImages();\n      }\n      _scanForImages() {\n        return this.editorUpload.scanForImages();\n      }\n    }\n\n    const DOM = DOMUtils.DOM;\n    const each = Tools.each;\n    let boundGlobalEvents = false;\n    let beforeUnloadDelegate;\n    let editors = [];\n    const globalEventDelegate = e => {\n      const type = e.type;\n      each(EditorManager.get(), editor => {\n        switch (type) {\n        case 'scroll':\n          editor.dispatch('ScrollWindow', e);\n          break;\n        case 'resize':\n          editor.dispatch('ResizeWindow', e);\n          break;\n        }\n      });\n    };\n    const toggleGlobalEvents = state => {\n      if (state !== boundGlobalEvents) {\n        const DOM = DOMUtils.DOM;\n        if (state) {\n          DOM.bind(window, 'resize', globalEventDelegate);\n          DOM.bind(window, 'scroll', globalEventDelegate);\n        } else {\n          DOM.unbind(window, 'resize', globalEventDelegate);\n          DOM.unbind(window, 'scroll', globalEventDelegate);\n        }\n        boundGlobalEvents = state;\n      }\n    };\n    const removeEditorFromList = targetEditor => {\n      const oldEditors = editors;\n      editors = filter$5(editors, editor => {\n        return targetEditor !== editor;\n      });\n      if (EditorManager.activeEditor === targetEditor) {\n        EditorManager.activeEditor = editors.length > 0 ? editors[0] : null;\n      }\n      if (EditorManager.focusedEditor === targetEditor) {\n        EditorManager.focusedEditor = null;\n      }\n      return oldEditors.length !== editors.length;\n    };\n    const purgeDestroyedEditor = editor => {\n      if (editor && editor.initialized && !(editor.getContainer() || editor.getBody()).parentNode) {\n        removeEditorFromList(editor);\n        editor.unbindAllNativeEvents();\n        editor.destroy(true);\n        editor.removed = true;\n      }\n    };\n    const isQuirksMode = document.compatMode !== 'CSS1Compat';\n    const EditorManager = {\n      ...Observable,\n      baseURI: null,\n      baseURL: null,\n      defaultOptions: {},\n      documentBaseURL: null,\n      suffix: null,\n      majorVersion: '6',\n      minorVersion: '3.2',\n      releaseDate: '2023-02-22',\n      i18n: I18n,\n      activeEditor: null,\n      focusedEditor: null,\n      setup() {\n        const self = this;\n        let baseURL = '';\n        let suffix = '';\n        let documentBaseURL = URI.getDocumentBaseUrl(document.location);\n        if (/^[^:]+:\\/\\/\\/?[^\\/]+\\//.test(documentBaseURL)) {\n          documentBaseURL = documentBaseURL.replace(/[\\?#].*$/, '').replace(/[\\/\\\\][^\\/]+$/, '');\n          if (!/[\\/\\\\]$/.test(documentBaseURL)) {\n            documentBaseURL += '/';\n          }\n        }\n        const preInit = window.tinymce || window.tinyMCEPreInit;\n        if (preInit) {\n          baseURL = preInit.base || preInit.baseURL;\n          suffix = preInit.suffix;\n        } else {\n          const scripts = document.getElementsByTagName('script');\n          for (let i = 0; i < scripts.length; i++) {\n            const src = scripts[i].src || '';\n            if (src === '') {\n              continue;\n            }\n            const srcScript = src.substring(src.lastIndexOf('/'));\n            if (/tinymce(\\.full|\\.jquery|)(\\.min|\\.dev|)\\.js/.test(src)) {\n              if (srcScript.indexOf('.min') !== -1) {\n                suffix = '.min';\n              }\n              baseURL = src.substring(0, src.lastIndexOf('/'));\n              break;\n            }\n          }\n          if (!baseURL && document.currentScript) {\n            const src = document.currentScript.src;\n            if (src.indexOf('.min') !== -1) {\n              suffix = '.min';\n            }\n            baseURL = src.substring(0, src.lastIndexOf('/'));\n          }\n        }\n        self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);\n        self.documentBaseURL = documentBaseURL;\n        self.baseURI = new URI(self.baseURL);\n        self.suffix = suffix;\n        setup$v(self);\n      },\n      overrideDefaults(defaultOptions) {\n        const baseUrl = defaultOptions.base_url;\n        if (baseUrl) {\n          this._setBaseUrl(baseUrl);\n        }\n        const suffix = defaultOptions.suffix;\n        if (suffix) {\n          this.suffix = suffix;\n        }\n        this.defaultOptions = defaultOptions;\n        const pluginBaseUrls = defaultOptions.plugin_base_urls;\n        if (pluginBaseUrls !== undefined) {\n          each$d(pluginBaseUrls, (pluginBaseUrl, pluginName) => {\n            AddOnManager.PluginManager.urls[pluginName] = pluginBaseUrl;\n          });\n        }\n      },\n      init(options) {\n        const self = this;\n        let result;\n        const invalidInlineTargets = Tools.makeMap('area base basefont br col frame hr img input isindex link meta param embed source wbr track ' + 'colgroup option table tbody tfoot thead tr th td script noscript style textarea video audio iframe object menu', ' ');\n        const isInvalidInlineTarget = (options, elm) => options.inline && elm.tagName.toLowerCase() in invalidInlineTargets;\n        const createId = elm => {\n          let id = elm.id;\n          if (!id) {\n            id = get$a(elm, 'name').filter(name => !DOM.get(name)).getOrThunk(DOM.uniqueId);\n            elm.setAttribute('id', id);\n          }\n          return id;\n        };\n        const execCallback = name => {\n          const callback = options[name];\n          if (!callback) {\n            return;\n          }\n          return callback.apply(self, []);\n        };\n        const findTargets = options => {\n          if (Env.browser.isIE() || Env.browser.isEdge()) {\n            initError('TinyMCE does not support the browser you are using. For a list of supported' + ' browsers please see: https://www.tiny.cloud/docs/tinymce/6/support/#supportedwebbrowsers');\n            return [];\n          } else if (isQuirksMode) {\n            initError('Failed to initialize the editor as the document is not in standards mode. ' + 'TinyMCE requires standards mode.');\n            return [];\n          } else if (isString(options.selector)) {\n            return DOM.select(options.selector);\n          } else if (isNonNullable(options.target)) {\n            return [options.target];\n          } else {\n            return [];\n          }\n        };\n        let provideResults = editors => {\n          result = editors;\n        };\n        const initEditors = () => {\n          let initCount = 0;\n          const editors = [];\n          let targets;\n          const createEditor = (id, options, targetElm) => {\n            const editor = new Editor(id, options, self);\n            editors.push(editor);\n            editor.on('init', () => {\n              if (++initCount === targets.length) {\n                provideResults(editors);\n              }\n            });\n            editor.targetElm = editor.targetElm || targetElm;\n            editor.render();\n          };\n          DOM.unbind(window, 'ready', initEditors);\n          execCallback('onpageload');\n          targets = unique$1(findTargets(options));\n          Tools.each(targets, elm => {\n            purgeDestroyedEditor(self.get(elm.id));\n          });\n          targets = Tools.grep(targets, elm => {\n            return !self.get(elm.id);\n          });\n          if (targets.length === 0) {\n            provideResults([]);\n          } else {\n            each(targets, elm => {\n              if (isInvalidInlineTarget(options, elm)) {\n                initError('Could not initialize inline editor on invalid inline target element', elm);\n              } else {\n                createEditor(createId(elm), options, elm);\n              }\n            });\n          }\n        };\n        DOM.bind(window, 'ready', initEditors);\n        return new Promise(resolve => {\n          if (result) {\n            resolve(result);\n          } else {\n            provideResults = editors => {\n              resolve(editors);\n            };\n          }\n        });\n      },\n      get(id) {\n        if (arguments.length === 0) {\n          return editors.slice(0);\n        } else if (isString(id)) {\n          return find$2(editors, editor => {\n            return editor.id === id;\n          }).getOr(null);\n        } else if (isNumber(id)) {\n          return editors[id] ? editors[id] : null;\n        } else {\n          return null;\n        }\n      },\n      add(editor) {\n        const self = this;\n        const existingEditor = self.get(editor.id);\n        if (existingEditor === editor) {\n          return editor;\n        }\n        if (existingEditor === null) {\n          editors.push(editor);\n        }\n        toggleGlobalEvents(true);\n        self.activeEditor = editor;\n        self.dispatch('AddEditor', { editor });\n        if (!beforeUnloadDelegate) {\n          beforeUnloadDelegate = e => {\n            const event = self.dispatch('BeforeUnload');\n            if (event.returnValue) {\n              e.preventDefault();\n              e.returnValue = event.returnValue;\n              return event.returnValue;\n            }\n          };\n          window.addEventListener('beforeunload', beforeUnloadDelegate);\n        }\n        return editor;\n      },\n      createEditor(id, options) {\n        return this.add(new Editor(id, options, this));\n      },\n      remove(selector) {\n        const self = this;\n        let editor;\n        if (!selector) {\n          for (let i = editors.length - 1; i >= 0; i--) {\n            self.remove(editors[i]);\n          }\n          return;\n        }\n        if (isString(selector)) {\n          each(DOM.select(selector), elm => {\n            editor = self.get(elm.id);\n            if (editor) {\n              self.remove(editor);\n            }\n          });\n          return;\n        }\n        editor = selector;\n        if (isNull(self.get(editor.id))) {\n          return null;\n        }\n        if (removeEditorFromList(editor)) {\n          self.dispatch('RemoveEditor', { editor });\n        }\n        if (editors.length === 0) {\n          window.removeEventListener('beforeunload', beforeUnloadDelegate);\n        }\n        editor.remove();\n        toggleGlobalEvents(editors.length > 0);\n        return editor;\n      },\n      execCommand(cmd, ui, value) {\n        var _a;\n        const self = this;\n        const editorId = isObject(value) ? (_a = value.id) !== null && _a !== void 0 ? _a : value.index : value;\n        switch (cmd) {\n        case 'mceAddEditor': {\n            if (!self.get(editorId)) {\n              const editorOptions = value.options;\n              new Editor(editorId, editorOptions, self).render();\n            }\n            return true;\n          }\n        case 'mceRemoveEditor': {\n            const editor = self.get(editorId);\n            if (editor) {\n              editor.remove();\n            }\n            return true;\n          }\n        case 'mceToggleEditor': {\n            const editor = self.get(editorId);\n            if (!editor) {\n              self.execCommand('mceAddEditor', false, value);\n              return true;\n            }\n            if (editor.isHidden()) {\n              editor.show();\n            } else {\n              editor.hide();\n            }\n            return true;\n          }\n        }\n        if (self.activeEditor) {\n          return self.activeEditor.execCommand(cmd, ui, value);\n        }\n        return false;\n      },\n      triggerSave: () => {\n        each(editors, editor => {\n          editor.save();\n        });\n      },\n      addI18n: (code, items) => {\n        I18n.add(code, items);\n      },\n      translate: text => {\n        return I18n.translate(text);\n      },\n      setActive(editor) {\n        const activeEditor = this.activeEditor;\n        if (this.activeEditor !== editor) {\n          if (activeEditor) {\n            activeEditor.dispatch('deactivate', { relatedTarget: editor });\n          }\n          editor.dispatch('activate', { relatedTarget: activeEditor });\n        }\n        this.activeEditor = editor;\n      },\n      _setBaseUrl(baseUrl) {\n        this.baseURL = new URI(this.documentBaseURL).toAbsolute(baseUrl.replace(/\\/+$/, ''));\n        this.baseURI = new URI(this.baseURL);\n      }\n    };\n    EditorManager.setup();\n\n    const setup = () => {\n      const dataValue = value$2();\n      const FakeClipboardItem = items => ({\n        items,\n        types: keys(items),\n        getType: type => get$a(items, type).getOrUndefined()\n      });\n      const write = data => {\n        dataValue.set(data);\n      };\n      const read = () => dataValue.get().getOrUndefined();\n      const clear = dataValue.clear;\n      return {\n        FakeClipboardItem,\n        write,\n        read,\n        clear\n      };\n    };\n    const FakeClipboard = setup();\n\n    const min = Math.min, max = Math.max, round = Math.round;\n    const relativePosition = (rect, targetRect, rel) => {\n      let x = targetRect.x;\n      let y = targetRect.y;\n      const w = rect.w;\n      const h = rect.h;\n      const targetW = targetRect.w;\n      const targetH = targetRect.h;\n      const relChars = (rel || '').split('');\n      if (relChars[0] === 'b') {\n        y += targetH;\n      }\n      if (relChars[1] === 'r') {\n        x += targetW;\n      }\n      if (relChars[0] === 'c') {\n        y += round(targetH / 2);\n      }\n      if (relChars[1] === 'c') {\n        x += round(targetW / 2);\n      }\n      if (relChars[3] === 'b') {\n        y -= h;\n      }\n      if (relChars[4] === 'r') {\n        x -= w;\n      }\n      if (relChars[3] === 'c') {\n        y -= round(h / 2);\n      }\n      if (relChars[4] === 'c') {\n        x -= round(w / 2);\n      }\n      return create$2(x, y, w, h);\n    };\n    const findBestRelativePosition = (rect, targetRect, constrainRect, rels) => {\n      for (let i = 0; i < rels.length; i++) {\n        const pos = relativePosition(rect, targetRect, rels[i]);\n        if (pos.x >= constrainRect.x && pos.x + pos.w <= constrainRect.w + constrainRect.x && pos.y >= constrainRect.y && pos.y + pos.h <= constrainRect.h + constrainRect.y) {\n          return rels[i];\n        }\n      }\n      return null;\n    };\n    const inflate = (rect, w, h) => {\n      return create$2(rect.x - w, rect.y - h, rect.w + w * 2, rect.h + h * 2);\n    };\n    const intersect = (rect, cropRect) => {\n      const x1 = max(rect.x, cropRect.x);\n      const y1 = max(rect.y, cropRect.y);\n      const x2 = min(rect.x + rect.w, cropRect.x + cropRect.w);\n      const y2 = min(rect.y + rect.h, cropRect.y + cropRect.h);\n      if (x2 - x1 < 0 || y2 - y1 < 0) {\n        return null;\n      }\n      return create$2(x1, y1, x2 - x1, y2 - y1);\n    };\n    const clamp = (rect, clampRect, fixedSize) => {\n      let x1 = rect.x;\n      let y1 = rect.y;\n      let x2 = rect.x + rect.w;\n      let y2 = rect.y + rect.h;\n      const cx2 = clampRect.x + clampRect.w;\n      const cy2 = clampRect.y + clampRect.h;\n      const underflowX1 = max(0, clampRect.x - x1);\n      const underflowY1 = max(0, clampRect.y - y1);\n      const overflowX2 = max(0, x2 - cx2);\n      const overflowY2 = max(0, y2 - cy2);\n      x1 += underflowX1;\n      y1 += underflowY1;\n      if (fixedSize) {\n        x2 += underflowX1;\n        y2 += underflowY1;\n        x1 -= overflowX2;\n        y1 -= overflowY2;\n      }\n      x2 -= overflowX2;\n      y2 -= overflowY2;\n      return create$2(x1, y1, x2 - x1, y2 - y1);\n    };\n    const create$2 = (x, y, w, h) => {\n      return {\n        x,\n        y,\n        w,\n        h\n      };\n    };\n    const fromClientRect = clientRect => {\n      return create$2(clientRect.left, clientRect.top, clientRect.width, clientRect.height);\n    };\n    const Rect = {\n      inflate,\n      relativePosition,\n      findBestRelativePosition,\n      intersect,\n      clamp,\n      create: create$2,\n      fromClientRect\n    };\n\n    const awaiter = (resolveCb, rejectCb, timeout = 1000) => {\n      let done = false;\n      let timer = null;\n      const complete = completer => (...args) => {\n        if (!done) {\n          done = true;\n          if (timer !== null) {\n            clearTimeout(timer);\n            timer = null;\n          }\n          completer.apply(null, args);\n        }\n      };\n      const resolve = complete(resolveCb);\n      const reject = complete(rejectCb);\n      const start = (...args) => {\n        if (!done && timer === null) {\n          timer = setTimeout(() => reject.apply(null, args), timeout);\n        }\n      };\n      return {\n        start,\n        resolve,\n        reject\n      };\n    };\n    const create$1 = () => {\n      const tasks = {};\n      const resultFns = {};\n      const load = (id, url) => {\n        const loadErrMsg = `Script at URL \"${ url }\" failed to load`;\n        const runErrMsg = `Script at URL \"${ url }\" did not call \\`tinymce.Resource.add('${ id }', data)\\` within 1 second`;\n        if (tasks[id] !== undefined) {\n          return tasks[id];\n        } else {\n          const task = new Promise((resolve, reject) => {\n            const waiter = awaiter(resolve, reject);\n            resultFns[id] = waiter.resolve;\n            ScriptLoader.ScriptLoader.loadScript(url).then(() => waiter.start(runErrMsg), () => waiter.reject(loadErrMsg));\n          });\n          tasks[id] = task;\n          return task;\n        }\n      };\n      const add = (id, data) => {\n        if (resultFns[id] !== undefined) {\n          resultFns[id](data);\n          delete resultFns[id];\n        }\n        tasks[id] = Promise.resolve(data);\n      };\n      const unload = id => {\n        delete tasks[id];\n      };\n      return {\n        load,\n        add,\n        unload\n      };\n    };\n    const Resource = create$1();\n\n    const create = () => (() => {\n      let data = {};\n      let keys = [];\n      const storage = {\n        getItem: key => {\n          const item = data[key];\n          return item ? item : null;\n        },\n        setItem: (key, value) => {\n          keys.push(key);\n          data[key] = String(value);\n        },\n        key: index => {\n          return keys[index];\n        },\n        removeItem: key => {\n          keys = keys.filter(k => k === key);\n          delete data[key];\n        },\n        clear: () => {\n          keys = [];\n          data = {};\n        },\n        length: 0\n      };\n      Object.defineProperty(storage, 'length', {\n        get: () => keys.length,\n        configurable: false,\n        enumerable: false\n      });\n      return storage;\n    })();\n\n    let localStorage;\n    try {\n      const test = '__storage_test__';\n      localStorage = window.localStorage;\n      localStorage.setItem(test, test);\n      localStorage.removeItem(test);\n    } catch (e) {\n      localStorage = create();\n    }\n    var LocalStorage = localStorage;\n\n    const publicApi = {\n      geom: { Rect },\n      util: {\n        Delay,\n        Tools,\n        VK,\n        URI,\n        EventDispatcher,\n        Observable,\n        I18n,\n        LocalStorage,\n        ImageUploader\n      },\n      dom: {\n        EventUtils,\n        TreeWalker: DomTreeWalker,\n        TextSeeker,\n        DOMUtils,\n        ScriptLoader,\n        RangeUtils,\n        Serializer: DomSerializer,\n        StyleSheetLoader,\n        ControlSelection,\n        BookmarkManager,\n        Selection: EditorSelection,\n        Event: EventUtils.Event\n      },\n      html: {\n        Styles,\n        Entities,\n        Node: AstNode,\n        Schema,\n        DomParser,\n        Writer,\n        Serializer: HtmlSerializer\n      },\n      Env,\n      AddOnManager,\n      Annotator,\n      Formatter,\n      UndoManager,\n      EditorCommands,\n      WindowManager,\n      NotificationManager,\n      EditorObservable,\n      Shortcuts,\n      Editor,\n      FocusManager,\n      EditorManager,\n      DOM: DOMUtils.DOM,\n      ScriptLoader: ScriptLoader.ScriptLoader,\n      PluginManager,\n      ThemeManager,\n      ModelManager,\n      IconManager,\n      Resource,\n      FakeClipboard,\n      trim: Tools.trim,\n      isArray: Tools.isArray,\n      is: Tools.is,\n      toArray: Tools.toArray,\n      makeMap: Tools.makeMap,\n      each: Tools.each,\n      map: Tools.map,\n      grep: Tools.grep,\n      inArray: Tools.inArray,\n      extend: Tools.extend,\n      walk: Tools.walk,\n      resolve: Tools.resolve,\n      explode: Tools.explode,\n      _addCacheSuffix: Tools._addCacheSuffix\n    };\n    const tinymce = Tools.extend(EditorManager, publicApi);\n\n    const exportToModuleLoaders = tinymce => {\n      if (typeof module === 'object') {\n        try {\n          module.exports = tinymce;\n        } catch (_) {\n        }\n      }\n    };\n    const exportToWindowGlobal = tinymce => {\n      window.tinymce = tinymce;\n      window.tinyMCE = tinymce;\n    };\n    exportToWindowGlobal(tinymce);\n    exportToModuleLoaders(tinymce);\n\n})();\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/cachecloud-web.conf",
    "content": "PID_FOLDER=/data/cachecloud-web\nLOG_FOLDER=/data/cachecloud-web/logs\nLOG_FILENAME=cachecloud-web.log\nAPP_NAME=cachecloud-web\nRUN_ARGS=\nJAVA_HOME=/usr/local/jdk\nJAVA_OPTS=\"-server -Xmx4g -Xms4g -Xss256k -XX:MaxDirectMemorySize=1G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=40 -XX:+PrintGCDateStamps -Xloggc:/opt/cachecloud-web/logs/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/cachecloud-web/logs/java.hprof -XX:+DisableExplicitGC -XX:-OmitStackTraceInFastThrow -XX:+PrintCommandLineFlags -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Djava.util.Arrays.useLegacyMergeSort=true -Dfile.encoding=UTF-8 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=22099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false\""
  },
  {
    "path": "cachecloud-web/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!--\r\n  Example LOGBACK Configuration File\r\n  http://logback.qos.ch/manual/configuration.html\r\n  -->\r\n<configuration scan=\"true\" scanPeriod=\"30 seconds\">\r\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\r\n    <jmxConfigurator/>\r\n\r\n    <springProfile name=\"local,open\">\r\n        <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\r\n            <encoder>\r\n                <pattern>${CONSOLE_LOG_PATTERN}</pattern>\r\n            </encoder>\r\n        </appender>\r\n        <root level=\"INFO\">\r\n            <appender-ref ref=\"console\"/>\r\n        </root>\r\n    </springProfile>\r\n\r\n    <springProfile name=\"test,online\">\r\n        <if condition='isDefined(\"MY_POD_NAME\")'>\r\n            <then>\r\n                <property name=\"log.dir\" value=\"/data/${MY_POD_NAME}/logs\"/>\r\n            </then>\r\n            <else>\r\n                <property name=\"log.dir\" value=\"/data/logs\"/>\r\n            </else>\r\n        </if>\r\n\r\n        <property name=\"log.level\" value=\"INFO\"/>\r\n        <appender name=\"stdout\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n            <file>${log.dir}/cachecloud.log</file>\r\n            <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\r\n                <pattern>${FILE_LOG_PATTERN}</pattern>\r\n            </encoder>\r\n\r\n            <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\r\n                <level>${log.level}</level>\r\n            </filter>\r\n\r\n            <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n                <FileNamePattern>${log.dir}/otherdays/cachecloud.%d{yyyy-MM-dd}.log</FileNamePattern>\r\n                <MaxHistory>30</MaxHistory>\r\n            </rollingPolicy>\r\n        </appender>\r\n\r\n        <appender name=\"stderr\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n            <file>${log.dir}/cachecloud_stderr.log</file>\r\n            <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\r\n                <pattern>${FILE_LOG_PATTERN}</pattern>\r\n            </encoder>\r\n\r\n            <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\r\n                <level>WARN</level>\r\n            </filter>\r\n\r\n            <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n                <FileNamePattern>${log.dir}/otherdays/cachecloud_stderr.%d{yyyy-MM-dd}.log</FileNamePattern>\r\n                <MaxHistory>30</MaxHistory>\r\n            </rollingPolicy>\r\n        </appender>\r\n\r\n        <appender name=\"managerAppender\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n            <file>${log.dir}/manager/cachecloud_manager.log</file>\r\n            <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n                <fileNamePattern>${log.dir}/manager/cachecloud_manager.%d{yyyyMMddHH}.log\r\n                </fileNamePattern>\r\n                <maxHistory>30</maxHistory>\r\n            </rollingPolicy>\r\n            <encoder>\r\n                <pattern>${FILE_LOG_PATTERN}</pattern>\r\n            </encoder>\r\n        </appender>\r\n\r\n        <appender name=\"quartzAppender\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n            <file>${log.dir}/quartz.log</file>\r\n            <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\r\n                <pattern>${FILE_LOG_PATTERN}</pattern>\r\n            </encoder>\r\n\r\n            <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n                <FileNamePattern>${log.dir}/otherdays/quartz.%d{yyyy-MM-dd}.log</FileNamePattern>\r\n                <MaxHistory>30</MaxHistory>\r\n            </rollingPolicy>\r\n        </appender>\r\n\r\n        <springProperty scope=\"context\" name=\"appName\" source=\"spring.application.name\"/>\r\n        <appender name=\"json\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n            <file>${log.dir}/json.log</file>\r\n            <encoder class=\"net.logstash.logback.encoder.LogstashEncoder\">\r\n                <customFields>\r\n                    {\r\n                    \"cluster_name\": \"${CLUSTER_NAME}\",\r\n                    \"my_pod_namespace\": \"${MY_POD_NAMESPACE}\",\r\n                    \"my_pod_name\": \"${MY_POD_NAME}\",\r\n                    \"my_node_name\": \"${MY_NODE_NAME}\",\r\n                    \"my_pod_ip\": \"${MY_POD_IP}\"\r\n                    }\r\n                </customFields>\r\n            </encoder>\r\n            <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n                <FileNamePattern>${log.dir}/json.%d{yyyy-MM-dd}.log</FileNamePattern>\r\n                <MaxHistory>1</MaxHistory>\r\n            </rollingPolicy>\r\n        </appender>\r\n\r\n        <root level=\"INFO\">\r\n            <appender-ref ref=\"stdout\"/>\r\n            <appender-ref ref=\"stderr\"/>\r\n            <if condition='isDefined(\"MY_POD_NAME\")'>\r\n                <then>\r\n                    <appender-ref ref=\"json\"/>\r\n                </then>\r\n            </if>\r\n        </root>\r\n\r\n        <logger name=\"com.sohu.cache.web.controller.AppManageController\" level=\"WARN\">\r\n            <appender-ref ref=\"managerAppender\"/>\r\n        </logger>\r\n\r\n        <logger name=\"com.sohu.cache.web.controller.InstanceManageController\" level=\"WARN\">\r\n            <appender-ref ref=\"managerAppender\"/>\r\n        </logger>\r\n\r\n        <logger name=\"org.quartz\" level=\"INFO\" additivity=\"false\">\r\n            <appender-ref ref=\"quartzAppender\"/>\r\n        </logger>\r\n\r\n        <logger name=\"org.quartz.core.QuartzSchedulerThread\" level=\"DEBUG\" additivity=\"false\">\r\n            <appender-ref ref=\"quartzAppender\"/>\r\n        </logger>\r\n\r\n        <!--<logger name=\"net.schmizz.sshj.transport.TransportImpl\" level=\"off\"/>\r\n        <logger name=\"net.schmizz.sshj.transport.random.BouncyCastleRandom\" level=\"off\"/>-->\r\n    </springProfile>\r\n\r\n\r\n</configuration>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppAlertRecordDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppAlertRecordDao\">\n    <sql id=\"alert_record_fields\">\n        create_time, visible_type, important_level, app_id, instance_id, ip, port, title, content\n    </sql>\n\n    <sql id=\"alert_record_query_fields\">\n        id, create_time, visible_type, important_level, app_id, instance_id, ip, port, title, content\n    </sql>\n\n    <insert id=\"save\" parameterType=\"com.sohu.cache.entity.AppAlertRecord\" keyProperty=\"id\" useGeneratedKeys=\"true\" >\n    \tinsert into app_alert_record\n    \t\t(<include refid=\"alert_record_fields\"/>)\n\t\tvalues\n\t\t\t(\n            #{createTime},#{visibleType},#{importantLevel},#{appId},#{instanceId},#{ip},#{port},#{title},#{content}\n            )\n    </insert>\n\n    <insert id=\"batchSave\">\n        insert ignore into app_alert_record (<include refid=\"alert_record_fields\"/>)\n        values\n        <foreach collection=\"list\" item=\"appAlertRecord\" separator=\",\">\n            (\n            #{appAlertRecord.createTime},#{appAlertRecord.visibleType},#{appAlertRecord.importantLevel},#{appAlertRecord.appId},\n            #{appAlertRecord.instanceId},#{appAlertRecord.ip},#{appAlertRecord.port},#{appAlertRecord.title},#{appAlertRecord.content}\n            )\n        </foreach>\n    </insert>\n\n</mapper>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppAuditDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.AppAuditDao\">\r\n    <sql id=\"columns\">\r\n    \tapp_id,user_id,user_name,type,param1,param2,param3,info,status,modify_time\r\n    </sql>\r\n\r\n    <select id=\"selectWaitAppAudits\" resultType=\"com.sohu.cache.entity.AppAudit\">\r\n        select id,<include refid=\"columns\"/>,task_id,create_time,operate_id,refuse_reason\r\n        from app_audit\r\n        <where>\r\n            <choose>\r\n                <when test=\"status != null and status!=4\">and status=#{status}</when>\r\n                <when test=\"status == null\">and status in (0,2)</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"type != null\">and type=#{type}</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"auditId != null\">and id=#{auditId}</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"userId != null\">and user_id=#{userId}</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"operateId != null\">and operate_id=#{operateId}</when>\r\n            </choose>\r\n        </where>\r\n        order by create_time desc\r\n    </select>\r\n\r\n    <select id=\"getAppAudit\" resultType=\"com.sohu.cache.entity.AppAudit\">\r\n        select id,<include refid=\"columns\"/>,create_time,refuse_reason\r\n        from app_audit\r\n        where id=#{id}\r\n    </select>\r\n\r\n    <insert id=\"insertAppAudit\" parameterType=\"com.sohu.cache.entity.AppAudit\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n        insert into app_audit(<include refid=\"columns\"/>)\r\n        values(#{appId},#{userId},#{userName},#{type},#{param1},#{param2},#{param3},#{info},#{status},#{modifyTime})\r\n    </insert>\r\n\r\n    <update id=\"updateAppAuditUser\" parameterType=\"com.sohu.cache.entity.AppAudit\">\r\n        update app_audit\r\n        set\r\n          status = #{status},\r\n          operate_id = #{operateId},\r\n          modify_time = current_timestamp()\r\n        where id=#{id}\r\n    </update>\r\n\r\n    <update id=\"updateAppAuditOperateUser\" parameterType=\"com.sohu.cache.entity.AppAudit\">\r\n        update app_audit\r\n        set\r\n          operate_id = #{operateId},\r\n          modify_time = current_timestamp()\r\n        where id=#{id}\r\n    </update>\r\n\r\n    <update id=\"updateAppAudit\" parameterType=\"com.sohu.cache.entity.AppAudit\">\r\n        update app_audit\r\n        set\r\n          status = #{status},\r\n          modify_time = current_timestamp()\r\n        where id=#{id}\r\n    </update>\r\n\r\n    <update id=\"updateRefuseReason\" parameterType=\"com.sohu.cache.entity.AppAudit\">\r\n        update app_audit\r\n        set\r\n          refuse_reason = #{refuseReason}, modify_time = current_timestamp()\r\n        where id=#{id}\r\n    </update>\r\n\r\n    <select id=\"getAppAuditByAppId\" resultType=\"com.sohu.cache.entity.AppAudit\">\r\n        select id,<include refid=\"columns\"/>,create_time,refuse_reason\r\n        from app_audit\r\n        where app_id=#{appId}\r\n    </select>\r\n\r\n    <select id=\"getAppAuditByCondition\" resultType=\"com.sohu.cache.entity.AppAudit\">\r\n        select id,<include refid=\"columns\"/>,create_time,refuse_reason\r\n        from app_audit\r\n        where app_id=#{appId} and type=#{type}\r\n    </select>\r\n\r\n    <select id=\"getAppAuditByTypeAndTimeRange\" resultType=\"com.sohu.cache.entity.AppAudit\">\r\n        select id,<include refid=\"columns\"/>,create_time,refuse_reason\r\n        from app_audit\r\n        where app_id=#{appId}\r\n            and type=#{type}\r\n            <choose>\r\n                <when test=\"startTime != null and endTime != null\">\r\n                    and modify_time between #{startTime} and #{endTime}\r\n                </when>\r\n            </choose>\r\n            order by modify_time desc\r\n    </select>\r\n\r\n    <update id=\"updateTaskId\">\r\n        update app_audit set task_id = #{taskId}, modify_time = now() where id=#{id}\r\n    </update>\r\n\r\n    <select id=\"getStatisticGroupByStatus\" resultType=\"hashmap\">\r\n        select sum(1) count,status\r\n        from app_audit\r\n        <where>\r\n            <choose>\r\n                <when test=\"operateId != null\">and operate_id=#{operateId}</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"userId != null\">and user_id=#{userId}</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"startTime != null and endTime != null\">and modify_time between #{startTime} and #{endTime}\r\n                </when>\r\n            </choose>\r\n        </where>\r\n        group by status;\r\n    </select>\r\n\r\n    <select id=\"getStatisticGroupByType\" resultType=\"hashmap\">\r\n        select sum(1) count,type\r\n        from app_audit\r\n        <where>\r\n            <choose>\r\n                <when test=\"operateId != null\">and operate_id=#{operateId}</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"userId != null\">and user_id=#{userId}</when>\r\n            </choose>\r\n            <choose>\r\n                <when test=\"startTime != null and endTime != null\">and modify_time between #{startTime} and #{endTime}\r\n                </when>\r\n            </choose>\r\n        </where>\r\n        group by type;\r\n    </select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppAuditLogDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n\r\n<mapper namespace=\"com.sohu.cache.dao.AppAuditLogDao\">\r\n    <sql id=\"app_audit_log_column\">\r\n    \tapp_id,user_id,info,create_time,type,app_audit_id\r\n    </sql>\r\n    \r\n    <insert id=\"save\" parameterType=\"AppAuditLog\"  keyProperty=\"id\" useGeneratedKeys=\"true\" >\r\n    \tinsert into app_audit_log\r\n    \t\t(<include refid=\"app_audit_log_column\"/>)\r\n\t\tvalues\r\n\t\t\t(#{appId},#{userId},#{info},#{createTime},#{type},#{appAuditId})    \t\r\n    </insert>\r\n    \r\n    <select id=\"getAuditByType\" resultType=\"AppAuditLog\">\r\n        select id,<include refid=\"app_audit_log_column\"/>\r\n        from app_audit_log\r\n        where app_audit_id=#{appAuditId} and type=#{type}\r\n        order by create_time desc\r\n    </select>\r\n    \r\n</mapper>\r\n\r\n\r\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppBizDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppBizDao\">\n\t<sql id=\"biz_column\">\n\t\tid,name,biz_desc\n    </sql>\n\n    <select id=\"get\" resultType=\"com.sohu.cache.entity.AppBiz\" parameterType=\"long\">\n        select           \n        \t<include refid=\"biz_column\"/>\n        from app_biz\n        where id = #{id};\n    </select>\n\n    <insert id=\"save\" parameterType=\"com.sohu.cache.entity.AppBiz\" keyProperty=\"id\" useGeneratedKeys=\"true\">\n    \tinsert into app_biz\n    \t\t(<include refid=\"biz_column\"/>)\n\t\tvalues\n\t\t\t(#{id},#{name},#{bizDesc})\n    </insert>\n    \n    <delete id=\"delete\" parameterType=\"long\">\n    \tdelete from app_biz where id=#{id}\n    </delete>\n\n    <update id=\"update\" parameterType=\"com.sohu.cache.entity.AppBiz\">\n    \tupdate app_biz\n    \t\tset name=#{name}, biz_desc=#{bizDesc}\n    \twhere id=#{id}\n    </update>\n\n    <select id=\"getBizList\" resultType=\"com.sohu.cache.entity.AppBiz\">\n        select\n        <include refid=\"biz_column\"/>\n        from app_biz order by id\n    </select>\n    \n</mapper>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppCapacityMonitorDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppCapacityMonitorDao\">\n    <sql id=\"save_fields\">\n        app_id, sharding_master_num, mem, cur_mem, mem_used, mem_used_history,\n        sharding_mem, cur_sharding_mem, sharding_mem_used,\n        expand_mem_percent, expand_ratio, expand_ratio_total, is_expand, is_reduce,\n        update_time, expand_time, schedule_status, schedule_time, expand_count\n    </sql>\n\n    <sql id=\"query_fields\">\n        id, app_id, sharding_master_num, mem, cur_mem, mem_used, mem_used_history,\n        sharding_mem, cur_sharding_mem, sharding_mem_used,\n        expand_mem_percent, expand_ratio, expand_ratio_total, is_expand, is_reduce,\n        update_time, expand_time, schedule_status, schedule_time, reduce_ratio_min, reduce_ratio_max, expand_count\n    </sql>\n\n    <insert id=\"save\" parameterType=\"com.sohu.cache.entity.AppCapacityMonitor\" keyProperty=\"id\" useGeneratedKeys=\"true\" >\n    \tinsert into app_capacity_monitor\n    \t\t(<include refid=\"save_fields\"/>)\n\t\tvalues\n\t\t\t(\n            #{appId},#{shardingMasterNum},#{mem},#{curMem},#{memUsed},#{memUsedHistory},\n            #{shardingMem},#{curShardingMem},#{shardingMemUsed},\n            #{expandMemPercent},#{expandRatio},#{expandRatioTotal},#{isExpand},#{isReduce},\n            #{updateTime},#{expandTime},#{scheduleStatus},#{scheduleTime},#{expandCount}\n            )\n    </insert>\n\n    <insert id=\"batchSave\">\n        insert ignore into app_capacity_monitor (<include refid=\"save_fields\"/>)\n        values\n        <foreach collection=\"list\" item=\"appCapacityMonitor\" separator=\",\">\n            (\n            #{appCapacityMonitor.appId},#{appCapacityMonitor.shardingMasterNum},#{appCapacityMonitor.mem},#{appCapacityMonitor.curMem},#{appCapacityMonitor.memUsed},#{appCapacityMonitor.memUsedHistory},\n            #{appCapacityMonitor.shardingMem},#{appCapacityMonitor.curShardingMem},#{appCapacityMonitor.shardingMemUsed},\n            #{appCapacityMonitor.expandMemPercent},#{appCapacityMonitor.expandRatio},#{appCapacityMonitor.expandRatioTotal},\n            #{appCapacityMonitor.isExpand},#{appCapacityMonitor.isReduce},#{appCapacityMonitor.updateTime},#{appCapacityMonitor.expandTime},\n            #{appCapacityMonitor.scheduleStatus},#{appCapacityMonitor.scheduleTime},#{appCapacityMonitor.expandCount}\n            )\n        </foreach>\n    </insert>\n\n    <update id=\"update\" parameterType=\"com.sohu.cache.entity.AppCapacityMonitor\">\n        update app_capacity_monitor\n            <set>\n                <if test=\"shardingMasterNum != null\">\n                    sharding_master_num = #{shardingMasterNum},\n                </if>\n                <if test=\"curMem != null and curMem > 0\">\n                    cur_mem = #{curMem},\n                </if>\n                <if test=\"memUsed != null and memUsed > 0\">\n                    mem_used = #{memUsed},\n                </if>\n                <if test=\"memUsedHistory != null and memUsedHistory > 0\">\n                    mem_used_history = #{memUsedHistory},\n                </if>\n                <if test=\"curShardingMem != null and curShardingMem > 0\">\n                    cur_sharding_mem = #{curShardingMem},\n                </if>\n                <if test=\"shardingMemUsed != null and shardingMemUsed > 0\">\n                    sharding_mem_used = #{shardingMemUsed},\n                </if>\n                <if test=\"expandMemPercent != null and expandMemPercent >= 0\">\n                    expand_mem_percent = #{expandMemPercent},\n                </if>\n                <if test=\"expandRatio != null and expandRatio >= 0\">\n                    expand_ratio = #{expandRatio},\n                </if>\n                <if test=\"expandRatioTotal != null and expandRatioTotal >= 0\">\n                    expand_ratio_total = #{expandRatioTotal},\n                </if>\n                <if test=\"isExpand != null\">\n                    is_expand = #{isExpand},\n                </if>\n                <if test=\"isReduce != null\">\n                    is_reduce = #{isReduce},\n                </if>\n                <if test=\"isReduce != null and isReduce == 0\">\n                    schedule_time = null,\n                </if>\n                <if test=\"reduceRatioMin != null and reduceRatioMin > 0\">\n                    reduce_ratio_min = #{reduceRatioMin},\n                </if>\n                <if test=\"reduceRatioMax != null and reduceRatioMax > 0\">\n                    reduce_ratio_max = #{reduceRatioMax},\n                </if>\n                    update_time = now(),\n                <if test=\"expandTime != null\">\n                    expand_time = #{expandTime},\n                </if>\n                <if test=\"scheduleStatus != null\">\n                    schedule_status = #{scheduleStatus},\n                </if>\n                <if test=\"scheduleTime != null\">\n                    schedule_time = #{scheduleTime},\n                </if>\n                <if test=\"expandCount != null and expandCount > 0\">\n                    expand_count = #{expandCount},\n                </if>\n            </set>\n        where id = #{id}\n    </update>\n\n    <update id=\"updateAppCapacityReduceSchedule\" parameterType=\"com.sohu.cache.entity.AppCapacityMonitor\">\n        update app_capacity_monitor\n        set\n            update_time = now(),\n            schedule_status = #{scheduleStatus},\n            schedule_time = #{scheduleTime}\n        where id = #{id} and schedule_status != #{scheduleStatus}\n    </update>\n\n    <update id=\"updateAppUsedMemHistory\">\n        update app_capacity_monitor\n        set mem_used_history = #{memUsedHistory}\n        where app_id = #{appId}\n    </update>\n\n    <select id=\"getAppCapacityMonitorByAppId\" resultType=\"com.sohu.cache.entity.AppCapacityMonitor\">\n        select\n            <include refid=\"query_fields\"/>\n        from app_capacity_monitor\n        where app_id = #{appId}\n    </select>\n\n    <select id=\"getAppCapacityMonitorAll\" resultType=\"com.sohu.cache.entity.AppCapacityMonitor\">\n        select\n            acm.id, acm.app_id, acm.sharding_master_num, mem, cur_mem, mem_used, mem_used_history,\n            sharding_mem, cur_sharding_mem, sharding_mem_used,\n            expand_mem_percent, expand_ratio, expand_ratio_total, is_expand, is_reduce,\n            acm.update_time, expand_time, schedule_status, schedule_time, reduce_ratio_min, reduce_ratio_max, expand_count\n        from app_capacity_monitor acm\n        left join app_desc ad\n        on acm.app_id = ad.app_id\n        where ad.status = 2\n    </select>\n\n</mapper>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientCommandStatisticsDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.AppClientCommandStatisticsDao\">\n    <sql id=\"columns\">\n\t\tapp_id,current_min,client_ip,command,cost,count,bytes_in,bytes_out\n\t</sql>\n\n    <insert id=\"batchSave\">\n        insert ignore into app_client_command_minute_statistics (<include refid=\"columns\"/>)\n        values\n        <foreach collection=\"list\" item=\"clientException\" separator=\",\">\n            (#{clientException.appId},#{clientException.currentMin},#{clientException.clientIp},\n            #{clientException.command},#{clientException.cost},#{clientException.count},#{clientException.bytesIn},#{clientException.bytesOut})\n        </foreach>\n    </insert>\n\n    <select id=\"getAppDistinctCommand\" resultType=\"string\">\n   \t\tselect command from app_client_command_minute_statistics where app_id=#{appId} and current_min between #{startTime} and #{endTime} group by command order by sum(count) desc;\n    </select>\n\n    <select id=\"getAppDistinctClients\" resultType=\"string\">\n   \t\tselect distinct client_ip from app_client_command_minute_statistics where app_id=#{appId} and current_min between #{startTime} and #{endTime};\n    </select>\n\n    <select id=\"getAppCommandStatistics\" resultType=\"hashmap\">\n        select\n        app_id,current_min,UNIX_TIMESTAMP(current_min) timestamp,client_ip,command,sum(cost)/sum(count) cost,sum(count)\n        count,sum(bytes_in) bytes_in,sum(bytes_out) bytes_out\n        from app_client_command_minute_statistics\n        where app_id=#{appId} and current_min between #{startTime} and #{endTime}\n        <choose>\n            <when test=\"command != null and command != ''\">\n                and command=#{command}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"clientIp != null and clientIp != ''\">\n                and client_ip=#{clientIp}\n            </when>\n        </choose>\n        group by client_ip,current_min\n        order by current_min asc\n    </select>\n\n    <select id=\"getAppClientCmdStat\" resultType=\"AppClientStatisticGather\">\n        select app_id, sum(count) as cmd_count, SUM(cost)/sum(count) as avg_cmd_cost, DATE_FORMAT(current_min,'%Y-%m-%d') gather_time\n        from app_client_command_minute_statistics\n        where current_min between #{startTime} and #{endTime}\n        group by app_id\n    </select>\n\n    <select id=\"getSumCmdStatByCmd\" resultType=\"hashmap\">\n        select app_id,client_ip,sum(count) count,sum(cost)/sum(count) cost\n        from app_client_command_minute_statistics\n        where app_id=#{appId}\n        <choose>\n            <when test=\"command != null and command != 'all' and command != ''\">\n                and command=#{command}\n            </when>\n        </choose>\n        and current_min between #{startTime} and #{endTime}\n        group by client_ip\n    </select>\n\n    <select id=\"getSumCmdStatByClient\" resultType=\"hashmap\">\n        select app_id,command,sum(count) count,sum(cost)/sum(count) cost\n        from app_client_command_minute_statistics\n        where app_id=#{appId}\n        <choose>\n            <when test=\"clientIp != null and clientIp != 'all' and clientIp != ''\">\n                and client_ip=#{clientIp}\n            </when>\n        </choose>\n        and current_min between #{startTime} and #{endTime}\n        group by command\n    </select>\n\n    <delete id=\"cleanCommandStatisticsBeforeCurrentMin\">\n        delete from app_client_command_minute_statistics\n        where current_min &lt;#{currentMin}\n    </delete>\n\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientCostTimeStatDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.AppClientCostTimeStatDao\">\r\n\t<sql id=\"columns\">\r\n\t\tapp_id,collect_time,client_ip,report_time,create_time,command,\r\n\t\tmedian,mean,ninety_percent_max,ninety_nine_percent_max,hundred_max,\r\n\t\tcount,instance_host,instance_port,instance_id\r\n\t</sql>\r\n\r\n\t<insert id=\"save\" parameterType=\"AppClientCostTimeStat\"\r\n\t\tkeyProperty=\"id\" useGeneratedKeys=\"true\">\r\n\t\tinsert into app_client_costtime_minute_stat(<include refid=\"columns\"/>)\r\n\t\tvalues(#{appId},#{collectTime},#{clientIp},#{reportTime},#{createTime},#{command},\r\n\t\t       #{median},#{mean},#{ninetyPercentMax},#{ninetyNinePercentMax},#{hundredMax},\r\n\t\t       #{count},#{instanceHost},#{instancePort},#{instanceId})\r\n\t</insert>\r\n\t\r\n\t<insert id=\"batchSave\">\r\n\t\tinsert into app_client_costtime_minute_stat(<include refid=\"columns\" />)\r\n\t\tvalues\r\n\t\t<foreach collection=\"list\" item=\"clientCostTime\" separator=\",\">\r\n\t\t\t(#{clientCostTime.appId},#{clientCostTime.collectTime},#{clientCostTime.clientIp},#{clientCostTime.reportTime},#{clientCostTime.createTime},#{clientCostTime.command},\r\n\t\t       #{clientCostTime.median},#{clientCostTime.mean},#{clientCostTime.ninetyPercentMax},#{clientCostTime.ninetyNinePercentMax},#{clientCostTime.hundredMax},\r\n\t\t       #{clientCostTime.count},#{clientCostTime.instanceHost},#{clientCostTime.instancePort},#{clientCostTime.instanceId})\r\n\t    </foreach>\r\n\t</insert>\r\n\t\r\n    <select id=\"getAppCommandClientToInstanceStat\" resultType=\"AppClientCostTimeStat\">\r\n    \tselect id,<include refid=\"columns\"/>\r\n    \tfrom app_client_costtime_minute_stat\r\n    \twhere app_id=#{appId} and instance_id=#{instanceId} and client_ip=#{clientIp} and collect_time between #{startTime} and #{endTime} and command=#{command};\r\n    </select>\r\n    \r\n    <select id=\"getTableMinimumId\" resultType=\"long\">\r\n    \tselect id from app_client_costtime_minute_stat order by id limit 1\r\n    </select>\r\n    \r\n    <select id=\"getMinimumIdByCollectTime\" resultType=\"long\">\r\n    \tselect id from app_client_costtime_minute_stat where collect_time = #{collectTime} order by id limit 1\r\n    </select>\r\n    \r\n    <delete id=\"deleteByIds\">\r\n    \tdelete from app_client_costtime_minute_stat where id>=#{startId} and id &lt;#{endId}\r\n    </delete>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientCostTimeTotalStatDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.AppClientCostTimeTotalStatDao\">\r\n\t<sql id=\"columns\">\r\n\t\tapp_id,collect_time,create_time,command,mean,median,ninety_percent_max,ninety_nine_percent_max,\r\n\t\thundred_max,total_cost,total_count,max_instance_host,max_instance_port,max_instance_id,max_client_ip,accumulation\r\n\t</sql>\r\n\t<sql id=\"duplicate_update\">\r\n        on duplicate key update\r\n            total_cost = total_cost + VALUES(total_cost),\r\n            total_count = total_count + VALUES(total_count),\r\n            median = if(median>VALUES(median),median,VALUES(median)),\r\n            ninety_percent_max = if(ninety_percent_max>VALUES(ninety_percent_max),ninety_percent_max,VALUES(ninety_percent_max)),\r\n            ninety_nine_percent_max = if(ninety_nine_percent_max>VALUES(ninety_nine_percent_max),ninety_nine_percent_max,VALUES(ninety_nine_percent_max)),\r\n            hundred_max = if(hundred_max>VALUES(hundred_max),hundred_max,VALUES(hundred_max)),\r\n            max_instance_host = if(hundred_max>VALUES(hundred_max),max_instance_host,VALUES(max_instance_host)),\r\n            max_instance_port = if(hundred_max>VALUES(hundred_max),max_instance_port,VALUES(max_instance_port)),\r\n            max_instance_id =   if(hundred_max>VALUES(hundred_max),max_instance_id,VALUES(max_instance_id)),\r\n            max_client_ip =     if(hundred_max>VALUES(hundred_max),max_client_ip,VALUES(max_client_ip)),\r\n            mean = format(total_cost/total_count,2),\r\n            create_time = now(),\r\n            accumulation = accumulation + 1;\r\n    </sql>\r\n\t<insert id=\"save\" parameterType=\"AppClientCostTimeTotalStat\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n\t\t insert into app_client_costtime_minute_stat_total\r\n\t\t (<include refid=\"columns\"/>)\r\n        values\r\n          (#{appId},#{collectTime},#{createTime},#{command},#{mean},#{median},\r\n          #{ninetyPercentMax},#{ninetyNinePercentMax},#{hundredMax},#{totalCost},#{totalCount},#{maxInstanceHost},#{maxInstancePort},#{maxInstanceId},#{maxClientIp}, 1)\r\n        <include refid=\"duplicate_update\"/>\r\n\t</insert>\r\n\t\r\n\t<insert id=\"batchSave\">\r\n\t\tinsert into app_client_costtime_minute_stat_total(<include refid=\"columns\"/>)\r\n\t\tvalues\r\n\t\t<foreach collection=\"list\" item=\"clientCostTime\" separator=\",\">\r\n\t\t\t (#{clientCostTime.appId},#{clientCostTime.collectTime},#{clientCostTime.createTime},#{clientCostTime.command},#{clientCostTime.mean},#{clientCostTime.median},\r\n          #{clientCostTime.ninetyPercentMax},#{clientCostTime.ninetyNinePercentMax},#{clientCostTime.hundredMax},#{clientCostTime.totalCost},#{clientCostTime.totalCount},#{clientCostTime.maxInstanceHost},#{clientCostTime.maxInstancePort},#{clientCostTime.maxInstanceId},#{clientCostTime.maxClientIp}, 1)\r\n\t    </foreach>\r\n        <include refid=\"duplicate_update\"/>\r\n\t</insert>\r\n\t\r\n    <select id=\"getAppDistinctCommand\" resultType=\"string\">\r\n   \t\tselect command from app_client_costtime_minute_stat_total where app_id=#{appId} and collect_time between #{startTime} and #{endTime} group by command order by sum(total_count) desc;\r\n    </select>\r\n    \r\n    <select id=\"getAppClientCommandStat\" resultType=\"AppClientCostTimeTotalStat\">\r\n    \tselect id,<include refid=\"columns\"/> from app_client_costtime_minute_stat_total\r\n    \twhere app_id=#{appId} and collect_time between #{startTime} and #{endTime} and command=#{command}\r\n    </select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientExceptionStatDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.AppClientExceptionStatDao\">\r\n\t<sql id=\"columns\">\r\n\t\tapp_id,collect_time,client_ip,report_time,create_time,exception_class,exception_count,instance_host,instance_port,instance_id,type\r\n\t</sql>\r\n\t\r\n\t<insert id=\"save\" parameterType=\"AppClientExceptionStat\"\r\n\t\tkeyProperty=\"id\" useGeneratedKeys=\"true\">\r\n\t\tinsert into app_client_exception_minute_stat(<include refid=\"columns\" />)\r\n\t\tvalues(#{appId},#{collectTime},#{clientIp},#{reportTime},#{createTime},#{exceptionClass},#{exceptionCount},#{instanceHost},#{instancePort},#{instanceId},#{type})\r\n\t</insert>\r\n\t\r\n\t<insert id=\"batchSave\">\r\n\t\tinsert into app_client_exception_minute_stat(<include refid=\"columns\" />)\r\n\t\tvalues\r\n\t\t<foreach collection=\"list\" item=\"clientException\" separator=\",\">\r\n\t\t\t(#{clientException.appId},#{clientException.collectTime},#{clientException.clientIp},#{clientException.reportTime},#{clientException.createTime},\r\n\t\t\t#{clientException.exceptionClass},#{clientException.exceptionCount},#{clientException.instanceHost},#{clientException.instancePort},#{clientException.instanceId},#{clientException.type})\r\n\t    </foreach>\r\n\t</insert>\r\n\t\r\n\t<select id=\"getAppExceptionList\" resultType=\"AppClientExceptionStat\">\r\n    \tselect id,<include refid=\"columns\" />\r\n    \tfrom app_client_exception_minute_stat\r\n    \twhere app_id=#{appId} and collect_time between #{startTime} and #{endTime}\r\n    \t<choose>\r\n        \t<when test=\"type > 0\">\r\n        \t \tand type=#{type}\r\n        \t</when>\r\n        </choose>\r\n        <choose>\r\n        \t<when test=\"clientIp != '' and clientIp != null\">\r\n        \t \tand client_ip=#{clientIp}\r\n        \t</when>\r\n        </choose>\r\n        order by collect_time desc\r\n        <choose>\r\n        \t<when test=\"page != null\">\r\n        \t\t<choose>\r\n\t\t        \t<when test=\"page.totalCount > page.pageSize\">\r\n            \t\t\tlimit #{page.start},#{page.pageSize};\r\n\t\t        \t</when>\r\n\t\t        \t<otherwise>\r\n\t\t        \t    limit #{page.totalCount}\r\n\t\t        \t</otherwise>\r\n\t\t        </choose>\r\n        \t</when>\r\n        </choose>\r\n    </select>\r\n    \r\n    <select id=\"getAppExceptionCount\" resultType=\"int\">\r\n    \tselect count(*)\r\n    \tfrom app_client_exception_minute_stat\r\n    \twhere app_id=#{appId} and collect_time between #{startTime} and #{endTime}\r\n    \t<choose>\r\n        \t<when test=\"type > 0\">\r\n        \t \tand type=#{type}\r\n        \t</when>\r\n        </choose>\r\n        <choose>\r\n        \t<when test=\"clientIp != '' and clientIp != null\">\r\n        \t \tand client_ip=#{clientIp}\r\n        \t</when>\r\n        </choose>\r\n    </select>\r\n    \r\n    \r\n    <select id=\"getInstanceExceptionStat\" resultType=\"ClientInstanceException\">\r\n    \tselect app_id,instance_id,instance_host,instance_port,count(1) as exceptionCount from app_client_exception_minute_stat \r\n    \t<where>\r\n\t    \t<choose>\r\n\t        \t<when test=\"collectTime > 0\">\r\n\t        \t \tand collect_time>=#{collectTime}\r\n\t        \t</when>\r\n\t        </choose>\r\n\t        <choose>\r\n\t        \t<when test=\"ip != '' and ip != null\">\r\n\t        \t \tand instance_host=#{ip}\r\n\t        \t</when>\r\n\t        </choose>\r\n    \t</where>\r\n    \tgroup by instance_id order by exceptionCount desc;\r\n    </select>\r\n    \r\n    \r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientExceptionStatisticsDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.AppClientExceptionStatisticsDao\">\n    <sql id=\"columns\">\n\t\tapp_id,current_min,client_ip,redis_pool_config,type,node,cost,count,latency_commands\n\t</sql>\n\n    <insert id=\"batchSave\">\n        insert ignore into app_client_exception_minute_statistics (<include refid=\"columns\"/>)\n        values\n        <foreach collection=\"list\" item=\"clientException\" separator=\",\">\n            (#{clientException.appId},#{clientException.currentMin},#{clientException.clientIp},#{clientException.redisPoolConfig},\n            #{clientException.type},#{clientException.node},#{clientException.cost},#{clientException.count},#{clientException.latencyCommands})\n        </foreach>\n    </insert>\n\n    <select id=\"getAppExceptionStatistics\" resultType=\"hashmap\">\n        select app_id,type,current_min,UNIX_TIMESTAMP(current_min) timestamp,client_ip,sum(cost)/sum(count)\n        cost,sum(count) count\n        from app_client_exception_minute_statistics\n        where app_id=#{appId} and current_min between #{startTime} and #{endTime}\n        <choose>\n            <when test=\"type != null and type != '-1'\">\n                and type=#{type}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"clientIp != null and clientIp != ''\">\n                and client_ip=#{clientIp}\n            </when>\n        </choose>\n        group by client_ip,current_min\n        order by current_min asc\n    </select>\n\n    <select id=\"getDistinctClientNodeStatistics\" resultType=\"hashmap\">\n        select node, sum(count) count, sum(cost)/sum(count) cost\n        from app_client_exception_minute_statistics\n        where app_id=#{appId}\n        <choose>\n            <when test=\"type != null and type != '-1'\">\n                and type=#{type}\n            </when>\n        </choose>\n        and client_ip=#{clientIp} and current_min between #{startTime} and #{endTime}\n        group by node\n        order by count desc;\n    </select>\n\n    <select id=\"getAppDistinctClientConfig\" resultType=\"hashmap\">\n        select distinct client_ip, redis_pool_config\n        from app_client_exception_minute_statistics\n        where app_id=#{appId}\n        <choose>\n            <when test=\"type != null and type != '-1'\">\n                and type=#{type}\n            </when>\n        </choose>\n        and current_min between #{startTime} and #{endTime};\n    </select>\n\n    <select id=\"getAppClientConfigs\" resultType=\"hashmap\">\n        select client_ip, redis_pool_config, DATE_FORMAT(min(current_min),'%Y-%m-%d %H:%i:%s') as change_time\n        from app_client_exception_minute_statistics\n        where app_id=#{appId}\n        <choose>\n            <when test=\"type != null and type != '-1'\">\n                and type=#{type}\n            </when>\n        </choose>\n        and current_min between #{startTime} and #{endTime}\n        group by client_ip, redis_pool_config\n        order by change_time desc;\n    </select>\n\n    <select id=\"getLatencyCommandsByNode\" resultType=\"string\">\n        select latency_commands, count, cost\n        from app_client_exception_minute_statistics\n        where client_ip=#{clientIp}\n        and type=1\n        and node=#{node}\n        and current_min between #{startTime} and #{endTime}\n    </select>\n\n\n    <select id=\"getLatencyCommandsByNodeV2\" resultType=\"string\">\n        select latency_commands\n        from app_client_exception_minute_statistics\n        where current_min=#{searchTime}\n        and node=#{node}\n        and type=1\n    </select>\n\n    <select id=\"getSumCmdExpStatGroupByNode\" resultType=\"hashmap\">\n        select node,sum(count) count,sum(cost)/sum(count) cost\n        from app_client_exception_minute_statistics\n        where current_min=#{searchTime} and app_id=#{appId} and type=1\n        group by node\n    </select>\n\n    <select id=\"getAppClientConnExpStat\" resultType=\"AppClientStatisticGather\">\n        select app_id, sum(count) as conn_exp_count, sum(cost)/sum(count) as avg_conn_exp_cost, DATE_FORMAT(current_min,'%Y-%m-%d') gather_time\n        from app_client_exception_minute_statistics\n        where type=0 and current_min between #{startTime} and #{endTime}\n        group by app_id\n    </select>\n\n    <select id=\"getAppClientConnExpCount\" resultType=\"int\">\n        select case when sum(count) is null then 0 else sum(count) end as count\n        from app_client_exception_minute_statistics\n        where app_id = #{appId} and type=0 and current_min between #{startTime} and #{endTime}\n    </select>\n\n    <select id=\"getAppClientCmdExpStat\" resultType=\"AppClientStatisticGather\">\n        select app_id, sum(count) as cmd_exp_count, sum(cost)/sum(count) as avg_cmd_exp_cost, DATE_FORMAT(current_min,'%Y-%m-%d') gather_time\n        from app_client_exception_minute_statistics\n        where type=1 and current_min between #{startTime} and #{endTime}\n        group by app_id\n    </select>\n\n    <select id=\"getAppClientCmdExpCount\" resultType=\"int\">\n        select case when sum(count) is null then 0 else sum(count) end as count\n        from app_client_exception_minute_statistics\n        where app_id = #{appId} and type=1 and current_min between #{startTime} and #{endTime}\n    </select>\n\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientLatencyCommandDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.AppClientLatencyCommandDao\">\n    <sql id=\"columns\">\n\t\tcommand,args,size,invoke_time\n\t</sql>\n\n    <insert id=\"batchSave\" parameterType=\"List\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        insert into app_client_latency_command (id,<include refid=\"columns\"/>)\n        values\n        <foreach collection=\"list\" item=\"latencyCommand\" separator=\",\">\n            (#{latencyCommand.id},#{latencyCommand.command},#{latencyCommand.args},#{latencyCommand.size},#{latencyCommand.invokeTime})\n        </foreach>\n    </insert>\n\n    <select id=\"getLatencyCommandByIds\" resultType=\"hashmap\" parameterType=\"java.util.List\">\n        SELECT id,<include refid=\"columns\"/>,FROM_UNIXTIME(invoke_time/1000,'%Y-%m-%d %H:%i:%s') as format_invoke_time\n        FROM app_client_latency_command\n        WHERE id in\n        <foreach collection=\"ids\" index=\"index\" item=\"id\" open=\"(\" separator=\",\" close=\")\">\n            #{id}\n        </foreach>\n        order by create_time desc\n    </select>\n</mapper>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientReportDataSizeDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.AppClientReportDataSizeDao\">\r\n\t<sql id=\"columns\">\r\n\t\tcollect_time,client_ip,report_time,create_time,cost_map_size,value_map_size,exception_map_size,collect_map_size\r\n\t</sql>\r\n\r\n\t<insert id=\"save\" parameterType=\"AppClientDataSizeStat\"\r\n\t\tkeyProperty=\"id\" useGeneratedKeys=\"true\">\r\n\t\tinsert into app_client_datasize_minute_stat(<include refid=\"columns\" />)\r\n\t\tvalues(#{collectTime},#{clientIp},#{reportTime},#{createTime},#{costMapSize},\r\n\t\t#{valueMapSize},#{exceptionMapSize},#{collectMapSize})\r\n\t</insert>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientStatisticGatherDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.AppClientStatisticGatherDao\">\n    <sql id=\"columns\">\n\t\tapp_id,gather_time,\n\t\tcmd_count,avg_cmd_cost,\n\t\tconn_exp_count,avg_conn_exp_cost,\n\t\tcmd_exp_count,avg_cmd_exp_cost,\n\t\tinstance_count,avg_mem_frag_ratio,mem_used_ratio,\n\t\texception_count,\n\t\tslow_log_count,\n\t\tlatency_count,\n\t\tconnected_clients,\n\t\tobject_size,\n\t\tused_memory,\n\t\tused_memory_rss,\n\t\tmax_cpu_sys,\n\t\tmax_cpu_user,\n\t\ttopology_exam_result,\n        used_disk,\n        server_cmd_count\n\t</sql>\n\n    <insert id=\"batchSave\">\n        insert into app_client_statistic_gather (<include refid=\"columns\"/>)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.cmdCount},#{stat.cmdCost},#{stat.expCount},#{stat.expCost})\n        </foreach>\n    </insert>\n\n    <insert id=\"batchSaveCmdStats\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,cmd_count,avg_cmd_cost)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.cmdCount},#{stat.avgCmdCost})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        cmd_count=values(cmd_count),\n        avg_cmd_cost=values(avg_cmd_cost)\n    </insert>\n    <insert id=\"batchAddCmdStats\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,cmd_count,avg_cmd_cost)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.cmdCount},#{stat.avgCmdCost})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        cmd_count=cmd_count+values(cmd_count),\n        avg_cmd_cost=values(avg_cmd_cost)\n    </insert>\n\n    <insert id=\"batchSaveConnExpStats\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,conn_exp_count,avg_conn_exp_cost)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.connExpCount},#{stat.avgConnExpCost})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        conn_exp_count=values(conn_exp_count),\n        avg_conn_exp_cost=values(avg_conn_exp_cost)\n    </insert>\n    <insert id=\"batchAddConnExpStats\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,conn_exp_count,avg_conn_exp_cost)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.connExpCount},#{stat.avgConnExpCost})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        conn_exp_count=conn_exp_count+values(conn_exp_count),\n        avg_conn_exp_cost=values(avg_conn_exp_cost)\n    </insert>\n\n    <insert id=\"batchSaveCmdExpStats\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,cmd_exp_count,avg_cmd_exp_cost)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.cmdExpCount},#{stat.avgCmdExpCost})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        cmd_exp_count=values(cmd_exp_count),\n        avg_cmd_exp_cost=values(avg_cmd_exp_cost)\n    </insert>\n    <insert id=\"batchAddCmdExpStats\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,cmd_exp_count,avg_cmd_exp_cost)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.cmdExpCount},#{stat.avgCmdExpCost})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        cmd_exp_count=cmd_exp_count+values(cmd_exp_count),\n        avg_cmd_exp_cost=values(avg_cmd_exp_cost)\n    </insert>\n\n    <insert id=\"batchSaveMemFragRatio\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,instance_count,avg_mem_frag_ratio,mem_used_ratio)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.instanceCount},#{stat.avgMemFragRatio},#{stat.memUsedRatio})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        instance_count=values(instance_count),\n        avg_mem_frag_ratio=values(avg_mem_frag_ratio),\n        mem_used_ratio=values(mem_used_ratio)\n    </insert>\n\n    <insert id=\"batchSaveSlowLogCount\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,slow_log_count)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.slowLogCount})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        slow_log_count=values(slow_log_count)\n    </insert>\n    <insert id=\"batchAddSlowLogCount\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,slow_log_count)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.slowLogCount})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        slow_log_count=slow_log_count+values(slow_log_count)\n    </insert>\n\n    <insert id=\"batchSaveLatencyCount\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,latency_count)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.latencyCount})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        latency_count=values(latency_count)\n    </insert>\n    <insert id=\"batchAddLatencyCount\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,latency_count)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.latencyCount})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        latency_count=latency_count+values(latency_count)\n    </insert>\n\n\n    <insert id=\"batchSaveAppStats\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,object_size,used_memory,used_memory_rss,avg_mem_frag_ratio,max_cpu_sys,max_cpu_user,used_disk)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.objectSize},#{stat.usedMemory},#{stat.usedMemoryRss},#{stat.avgMemFragRatio},#{stat.maxCpuSys},#{stat.maxCpuUser},#{stat.usedDisk})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        object_size=values(object_size),\n        used_memory=values(used_memory),\n        used_memory_rss=values(used_memory_rss),\n        avg_mem_frag_ratio=values(avg_mem_frag_ratio),\n        max_cpu_sys=GREATEST(max_cpu_sys,values(max_cpu_sys)),\n        max_cpu_user=GREATEST(max_cpu_user,values(max_cpu_user)),\n        used_disk=values(used_disk)\n    </insert>\n\n    <insert id=\"batchAddAppServerCmdCount\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,server_cmd_count)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.serverCmdCount})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        server_cmd_count=server_cmd_count+values(server_cmd_count)\n    </insert>\n\n\n    <insert id=\"batchSaveConnClients\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,connected_clients)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.connectedClients})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        connected_clients=values(connected_clients)\n    </insert>\n\n    <insert id=\"batchSaveTopologyExam\" parameterType=\"list\">\n        insert into app_client_statistic_gather\n        (app_id,gather_time,topology_exam_result)\n        values\n        <foreach collection=\"list\" item=\"stat\" separator=\",\">\n            (#{stat.appId},#{stat.gatherTime},#{stat.topologyExamResult})\n        </foreach>\n        ON DUPLICATE KEY UPDATE\n        app_id=values(app_id),\n        gather_time=values(gather_time),\n        topology_exam_result=values(topology_exam_result)\n    </insert>\n\n\n    <select id=\"getAppClientStatisticByGatherTime\" resultType=\"hashmap\">\n        select\n        <include refid=\"columns\"/>\n        from app_client_statistic_gather\n        where gather_time = #{gatherTime}\n        <choose>\n            <when test=\"appId != null and appId > 0\">\n                and app_id = #{appId}\n            </when>\n        </choose>\n    </select>\n\n    <select id=\"getTopologyExamFailedByGatherTime\" resultType=\"com.sohu.cache.entity.AppClientStatisticGather\">\n        select\n        <include refid=\"columns\"/>\n        from app_client_statistic_gather\n        where gather_time = #{gatherTime}\n        and topology_exam_result != 0\n    </select>\n\n    <select id=\"getExpAppStatisticByGatherTime\" resultType=\"hashmap\">\n        select\n        <include refid=\"columns\"/>,(conn_exp_count+cmd_exp_count) as exp_count\n        from app_client_statistic_gather\n        where gather_time = #{gatherTime}\n        ORDER BY exp_count DESC\n    </select>\n\n\n    <select id=\"getExpAppStatsByGatherTime\" resultType=\"hashmap\">\n        select app_client_statistic_gather.app_id as app_id,is_test,\n        (conn_exp_count + cmd_exp_count) as exp_count,\n        conn_exp_count,cmd_exp_count,\n        slow_log_count,latency_count\n        from app_client_statistic_gather,app_desc\n        where gather_time = #{gatherTime}\n        and app_client_statistic_gather.app_id = app_desc.app_id\n        and app_desc.is_test = 0\n        and conn_exp_count + cmd_exp_count > 0\n        ORDER BY exp_count DESC\n    </select>\n\n\n    <select id=\"getLatencyAppStatsByGatherTime\" resultType=\"hashmap\">\n        select app_client_statistic_gather.app_id as app_id,is_test,\n        slow_log_count,latency_count\n        from app_client_statistic_gather,app_desc\n        where gather_time = #{gatherTime}\n        and app_client_statistic_gather.app_id = app_desc.app_id\n        and app_desc.is_test = 0\n        and slow_log_count>100\n        ORDER BY slow_log_count DESC\n        LIMIT 10\n    </select>\n\n\n    <select id=\"getMemAlterAppStatsByGatherTime\" resultType=\"hashmap\">\n        select app_client_statistic_gather.app_id as app_id,is_test,\n        mem_used_ratio,round(used_memory/1024/1024,2) as format_used_memory\n        from app_client_statistic_gather,app_desc\n        where gather_time = #{gatherTime}\n        and app_client_statistic_gather.app_id = app_desc.app_id\n        and app_desc.is_test = 0\n        and mem_used_ratio &lt; 50\n        ORDER BY mem_used_ratio asc\n        LIMIT 10\n    </select>\n\n\n    <select id=\"getFragRatioAppStatsByGatherTime\" resultType=\"hashmap\">\n        select app_client_statistic_gather.app_id as app_id,is_test,\n        round(used_memory/1024/1024,2) as format_used_memory,\n        round(used_memory_rss/1024/1024,2) as format_used_memory_rss,\n        avg_mem_frag_ratio\n        from app_client_statistic_gather,app_desc\n        where gather_time = '2020-06-03'\n        and app_client_statistic_gather.app_id = app_desc.app_id\n        and app_desc.is_test = 0\n        and avg_mem_frag_ratio>1.2\n        ORDER BY avg_mem_frag_ratio DESC;\n    </select>\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppClientVersionDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.AppClientVersionDao\">\r\n\t<sql id=\"columns\">\r\n\t\tapp_id,client_ip,client_version,report_time\r\n\t</sql>\r\n\r\n\t<insert id=\"saveOrUpdateClientVersion\" parameterType=\"AppClientVersion\" keyProperty=\"id\"\r\n\t\tuseGeneratedKeys=\"true\">\r\n\t\tinsert into app_client_version_statistic(<include refid=\"columns\"/>)\r\n\t\tvalues(#{appId},#{clientIp},#{clientVersion},#{reportTime})\r\n\t\ton duplicate key\r\n\t\tupdate client_version=#{clientVersion},report_time=#{reportTime}\r\n\t</insert>\r\n\t\r\n\t<select id=\"getByClientIp\" resultType=\"AppClientVersion\">\r\n\t\tselect id,<include refid=\"columns\"/> from app_client_version_statistic where client_ip=#{clientIp}\r\n\t</select>\r\n\t\r\n\t<select id=\"getAppAllClientVersion\" resultType=\"AppClientVersion\">\r\n\t\tselect id,<include refid=\"columns\"/> from app_client_version_statistic where app_id=#{appId}\r\n\t</select>\r\n\r\n\t<select id=\"getAppMaxClientVersion\" resultType=\"string\">\r\n\t\tselect max(client_version) from app_client_version_statistic where app_id=#{appId}\r\n\t</select>\r\n\r\n\t<select id=\"getAllMaxClientVersion\" resultType=\"map\">\r\n\t\tselect app_id \"appId\",max(client_version) \"clientVersion\"\r\n\t\tfrom app_client_version_statistic\r\n\t\tgroup by app_id\r\n\t</select>\r\n\t\r\n\t<select id=\"getAll\" resultType=\"AppClientVersion\">\r\n\t\tselect id,<include refid=\"columns\"/> from app_client_version_statistic where 1=1\r\n\t\t<choose>\r\n        \t<when test=\"appId > 0\">\r\n        \t \tand app_id=#{appId}\r\n        \t</when>\r\n        </choose>\r\n\t</select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppDailyDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppDailyDao\">\n\t<sql id=\"app_daily_column\">\n\t\tapp_id,date,create_time,slow_log_count,client_exception_count,max_minute_client_count,\n\t\tavg_minute_client_count,max_minute_command_count,avg_minute_command_count,avg_hit_ratio,\n\t\tmin_minute_hit_ratio,max_minute_hit_ratio,avg_used_memory,max_used_memory,expired_keys_count,\n\t\tevicted_keys_count,avg_minute_net_input_byte,max_minute_net_input_byte,avg_minute_net_output_byte,\n\t\tmax_minute_net_output_byte,avg_object_size,max_object_size,big_key_times,big_key_info,\n\t\tclient_cmd_count,client_avg_cmd_cost,client_conn_exp_count,client_avg_conn_exp_cost,client_cmd_exp_count,\n\t\tclient_avg_cmd_exp_cost,avg_used_disk,max_used_disk\n    </sql>\n    \n    <insert id=\"save\" parameterType=\"AppDailyData\">\n    \tinsert into app_daily\n    \t\t(<include refid=\"app_daily_column\"/>)\n\t\tvalues\n\t\t(#{appId},#{date},now(),#{slowLogCount},#{clientExceptionCount},#{maxMinuteClientCount},\n\t\t#{avgMinuteClientCount},#{maxMinuteCommandCount},#{avgMinuteCommandCount},#{avgHitRatio},\n\t\t#{minMinuteHitRatio},#{maxMinuteHitRatio},#{avgUsedMemory},#{maxUsedMemory},#{expiredKeysCount},\n\t\t#{evictedKeysCount},#{avgMinuteNetInputByte},#{maxMinuteNetInputByte},#{avgMinuteNetOutputByte},\n\t\t#{maxMinuteNetOutputByte},#{avgObjectSize},#{maxObjectSize},#{bigKeyTimes},#{bigKeyInfo},\n\t\t#{clientCmdCount},#{clientAvgCmdCost},#{clientConnExpCount},#{clientAvgConnExpCost},\n\t\t#{clientCmdExpCount},#{clientAvgCmdExpCost},#{avgUsedDisk},#{maxUsedDisk})\n    </insert>\n    \n    <select id=\"getAppDaily\" resultType=\"AppDailyData\">\n\t\tselect id,<include refid=\"app_daily_column\"/> from app_daily where app_id=#{appId} and date =#{date}\n\t</select>\n\n\t<select id=\"getAppDailyList\" resultType=\"AppDailyData\">\n\t\tselect id,<include refid=\"app_daily_column\"/> from app_daily where app_id=#{appId} and date between #{startDate} and #{endDate}\n\t</select>\n\n</mapper>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppDao\">\n    <sql id=\"app_desc_add_fields\">\n        app_id,name,user_id,status,intro,create_time,passed_time,type,officer,\n        ver_id,is_test,has_back_store,need_persistence,need_hot_back_up,forecase_qps,\n        forecast_obj_num,mem_alert_value,client_machine_room,app_key,client_conn_alert_value,\n        hit_precent_alert_value,is_access_monitor,important_level,password,version_id,\n        custom_password,maxmemory_policy\n    </sql>\n    <sql id=\"app_desc_fields\">\n        app_id,name,user_id,status,intro,create_time,passed_time,type,officer,\n        ver_id,is_test,has_back_store,need_persistence,need_hot_back_up,forecase_qps,\n        forecast_obj_num,mem_alert_value,client_machine_room,app_key,client_conn_alert_value,\n        hit_precent_alert_value,is_access_monitor,important_level,password \"pkey\",version_id,\n        custom_password, persistence_type,maxmemory_policy\n    </sql>\n    <!--通过appId查询app的信息-->\n    <select id=\"getAppDescById\" resultType=\"AppDesc\" parameterType=\"long\">\n        SELECT\n            <include refid=\"app_desc_fields\"/>\n        FROM app_desc\n        WHERE app_id = #{appId};\n    </select>\n\n\n    <select id=\"getOnlineAppDescById\" resultType=\"AppDesc\" parameterType=\"long\">\n        SELECT\n        <include refid=\"app_desc_fields\"/>\n        FROM app_desc\n        WHERE app_id = #{appId}\n        AND status = 2\n    </select>\n\n    <!-- 根据应用名查询app信息 -->\n    <select id=\"getByAppName\" resultType=\"AppDesc\" parameterType=\"string\">\n        SELECT\n            <include refid=\"app_desc_fields\"/>\n        FROM app_desc\n        WHERE name = #{appName};\n    </select>\n\n\n    <insert id=\"save\" parameterType=\"AppDesc\"  keyProperty=\"appId\" useGeneratedKeys=\"true\" >\n    \tinsert into app_desc\n    \t\t(<include refid=\"app_desc_add_fields\"/>)\n\t\tvalues\n\t\t\t(\n            #{appId},#{name},#{userId},#{status},#{intro},#{createTime},#{passedTime},\n\t\t\t#{type},#{officer},#{verId},#{isTest},#{hasBackStore},#{needPersistence},\n\t\t\t#{needHotBackUp},#{forecaseQps},#{forecastObjNum},#{memAlertValue},#{clientMachineRoom},\n            #{appKey},#{clientConnAlertValue},#{hitPrecentAlertValue},#{isAccessMonitor},\n            #{importantLevel},#{pkey},#{versionId},#{customPassword},#{maxmemoryPolicy}\n            )\n    </insert>\n\n    <update id=\"update\" parameterType=\"AppDesc\">\n    \tupdate app_desc \n    \t\tset name=#{name}, user_id=#{userId}, status=#{status}, intro=#{intro}, create_time=#{createTime},\n    \t\t\tpassed_time=#{passedTime},type=#{type},\n    \t\t\tofficer=#{officer},ver_id=#{verId},mem_alert_value=#{memAlertValue},\n    \t\t\tclient_conn_alert_value=#{clientConnAlertValue},hit_precent_alert_value=#{hitPrecentAlertValue},is_access_monitor=#{isAccessMonitor},important_level=#{importantLevel},\n    \t\t\tpassword=#{pkey},version_id=#{versionId}\n    \twhere app_id=#{appId}\n    </update>\n\n    <update id=\"updateWithCustomPwd\" parameterType=\"AppDesc\">\n        update app_desc\n        set name=#{name}, user_id=#{userId}, status=#{status}, intro=#{intro}, create_time=#{createTime},\n        passed_time=#{passedTime},type=#{type},\n        officer=#{officer},ver_id=#{verId},mem_alert_value=#{memAlertValue},\n        client_conn_alert_value=#{clientConnAlertValue},hit_precent_alert_value=#{hitPrecentAlertValue},is_access_monitor=#{isAccessMonitor},important_level=#{importantLevel},\n        password=#{pkey},version_id=#{versionId},custom_password=#{customPassword}\n        where app_id=#{appId}\n    </update>\n\n    <sql id=\"app_desc_select_column\">\n    \tapp_desc.app_id,name,app_desc.user_id,status,intro,create_time,passed_time,type,officer,ver_id,app_key,password \"pkey\",version_id,is_test,custom_password\n    </sql>\n    <select id=\"getAppDescList\" resultType=\"AppDesc\" parameterType=\"long\">\n    \tselect\n            ad.app_id,ad.name,ad.user_id,ad.status,ad.intro,ad.create_time,\n            ad.passed_time,ad.type,ad.officer,ad.ver_id,ad.app_key,ad.password \"pkey\",\n            ad.version_id,ad.is_test,ad.custom_password,ad.mem_alert_value,\n            ad.client_conn_alert_value, ad.is_access_monitor\n        from\n            app_desc ad\n        left join app_to_user atu\n            on ad.app_id = atu.app_id\n        where atu.user_id=#{userId}\n          and ad.status &lt; 3\n        GROUP BY ad.app_id\n        ORDER BY ad.app_id desc\n    </select>\n\n    <select id=\"getOnlineApps\" resultType=\"AppDesc\">\n        select\n        <include refid=\"app_desc_select_column\"/>\n        from app_desc\n        where status = 2\n    </select>\n\n\n    <select id=\"getOnlineAppsNonTest\" resultType=\"AppDesc\">\n        select\n        <include refid=\"app_desc_select_column\"/>\n        from app_desc\n        where status = 2 AND is_test = 0\n    </select>\n\n    <select id=\"getAllApps\" resultType=\"AppDesc\">\n        select\n        <include refid=\"app_desc_select_column\"/>\n        from app_desc\n        order by app_id\n    </select>\n\n    <select id=\"getUserAppCount\" resultType=\"int\" parameterType=\"long\">\n    \tselect count(app_desc.app_id) from app_desc,app_to_user where app_to_user.user_id=#{userId} and app_to_user.app_id=app_desc.app_id and app_desc.status &lt; 3\n    </select>\n\n    <select id=\"getAllAppCount\" resultType=\"int\" parameterType=\"AppSearch\">\n\n        select count(distinct ad.app_id)\n        from app_desc ad\n        <if test=\"bizId != null and bizId > 0\">\n            left join app_to_user atu\n                on ad.app_id = atu.app_id\n            left join app_user au\n                on atu.user_id = au.id\n        </if>\n        where ad.status &lt; 3\n        <choose>\n            <when test=\"appName != null and appName != ''\">\n                and instr(ad.name, #{appName}) > 0\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appType != null and appType > 0\">\n                and ad.type = #{appType}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appStatus != null and appStatus >= 0\">\n                and ad.status = #{appStatus}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appId != null and appId > 0\">\n                and ad.app_id = #{appId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"importantLevel != null and importantLevel > 0\">\n                and ad.important_level = #{importantLevel}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"versionId != null and versionId > 0\">\n                and ad.version_id = #{versionId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"userId != null and userId > 0\">\n                and ad.app_id in (select app_id from app_to_user where user_id = #{userId})\n            </when>\n        </choose>\n        <choose>\n            <when test=\"persistenceType != null\">\n                and ad.persistence_type = #{persistenceType}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"bizId != null and bizId > 0\">\n                and au.biz_id = #{bizId}\n            </when>\n        </choose>\n    </select>\n\n    <select id=\"getAllAppDescList\" resultType=\"AppDesc\" parameterType=\"AppSearch\">\n    \tselect\n            ad.app_id, ad.name, ad.user_id, ad.status, ad.intro, ad.create_time, ad.passed_time,\n            ad.type, ad.officer, ad.ver_id, ad.is_test, ad.has_back_store, ad.need_persistence,\n            ad.need_hot_back_up, ad.forecase_qps, ad.forecast_obj_num, ad.mem_alert_value,\n            ad.client_machine_room, ad.app_key, ad.client_conn_alert_value, ad.hit_precent_alert_value,\n            ad.is_access_monitor, ad.important_level, ad.password \"pkey\", ad.version_id,\n            ad.custom_password, ad.persistence_type, ad.maxmemory_policy,\n            group_concat(distinct ab.name order by ab.name desc separator ',') bizGroup\n        from\n            app_desc ad\n        left join app_to_user atu\n            on ad.app_id = atu.app_id\n        left join app_user au\n            on atu.user_id = au.id\n        left join app_biz ab\n            on au.biz_id  = ab.id\n        where status &lt; 3\n        <choose>\n            <when test=\"appName != null and appName != ''\">\n                and instr(ad.name, #{appName}) > 0\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appType != null and appType > 0\">\n            \tand ad.type = #{appType}\n       \t \t</when>\n        </choose>\n        <choose>\n            <when test=\"appStatus != null and appStatus >= 0\">\n            \tand ad.status = #{appStatus}\n       \t \t</when>\n        </choose>\n        <choose>\n            <when test=\"appId != null and appId > 0\">\n            \tand ad.app_id = #{appId}\n       \t \t</when>\n        </choose>\n        <choose>\n            <when test=\"importantLevel != null and importantLevel > 0\">\n            \tand ad.important_level = #{importantLevel}\n       \t \t</when>\n        </choose>\n        <choose>\n            <when test=\"versionId != null and versionId > 0\">\n                and ad.version_id = #{versionId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"userId != null and userId > 0\">\n                and ad.app_id in (select app_id from app_to_user where user_id = #{userId})\n            </when>\n        </choose>\n        <choose>\n            <when test=\"persistenceType != null\">\n                and ad.persistence_type = #{persistenceType}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"isTest != null and isTest > 0\">\n                and ad.is_test = #{isTest}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"bizId != null and bizId > 0\">\n                and ab.id = #{bizId}\n            </when>\n        </choose>\n        GROUP BY ad.app_id\n        ORDER BY ad.app_id desc\n        <choose>\n        \t<when test=\"page != null\">\n        \t\t<choose>\n\t\t        \t<when test=\"page.totalCount > page.pageSize\">\n            \t\t\tlimit #{page.start},#{page.pageSize}\n\t\t        \t</when>\n\t\t        \t<otherwise>\n\t\t        \t    limit #{page.totalCount}\n\t\t        \t</otherwise>\n\t\t        </choose>\n        \t</when>\n        </choose>\n    </select>\n\n    <select id=\"getTotalAppCount\" resultType=\"int\" parameterType=\"AppSearch\">\n        select count(app_id) from app_desc\n    </select>\n\n    <update id=\"updateAppKey\">\n    \tupdate app_desc set app_key=#{appKey} where app_id=#{appId}\n    </update>\n\n    <update id=\"updateAppPwd\">\n        update app_desc set password=#{pkey} where app_id=#{appId}\n    </update>\n\n    <update id=\"updateAppPersistenceType\">\n    \tupdate app_desc set persistence_type=#{persistenceType} where app_id=#{appId}\n    </update>\n\n    <update id=\"updateAppMaxmemoryPolicy\">\n        update app_desc set maxmemory_policy=#{maxmemoryPolicy} where app_id=#{appId}\n    </update>\n\n    <select id=\"getAppDescByIds\" resultType=\"AppDesc\" parameterType=\"java.util.List\">\n        select * from app_desc where app_id in\n        <foreach collection=\"appIds\" index=\"index\" item=\"appId\" open=\"(\" separator=\",\" close=\")\">\n            #{appId}\n        </foreach>\n    </select>\n\n    <select id=\"getMonitorStatistics\" resultType=\"com.sohu.cache.entity.AppMonitorStatisticsResult\" parameterType=\"com.sohu.cache.entity.AppStatisticsSearch\">\n        select\n            sum(acsg.conn_exp_count) conn_exp_count,\n            sum(acsg.cmd_exp_count) cmd_exp_count,\n            sum(acsg.slow_log_count) slow_log_count,\n            sum(acsg.latency_count) latency_count,\n            sum(acsg.cmd_count) cmd_count,\n            acsg.gather_time\n        from app_desc ad\n        <if test=\"isAdmin != null and !isAdmin\">\n            left join app_to_user atu\n                on ad.app_id = atu.app_id\n        </if>\n        left join app_client_statistic_gather acsg\n            on ad.app_id = acsg.app_id\n        where\n            ad.status = 2\n        <if test=\"isAdmin != null and !isAdmin\">\n            and atu.user_id =  #{userId}\n        </if>\n        and gather_time &lt;= #{endTime}\n        and gather_time > #{startTime}\n        group by gather_time\n    </select>\n\n    <select id=\"getCapacityStatistics\" resultType=\"com.sohu.cache.entity.AppCapacityStatisticsResult\" parameterType=\"com.sohu.cache.entity.AppStatisticsSearch\">\n        select\n            ad.app_id, ad.version_id, acm.cur_mem,\n            acm.mem_used, acm.sharding_master_num\n        from app_desc ad\n        <if test=\"isAdmin != null and !isAdmin\">\n            left join app_to_user atu\n            on ad.app_id = atu.app_id\n        </if>\n        left join app_capacity_monitor acm\n            on ad.app_id = acm.app_id\n        where\n            ad.status = 2\n        <if test=\"isAdmin != null and !isAdmin\">\n            and atu.user_id = #{userId}\n        </if>\n    </select>\n\n</mapper>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppDataMigrateStatusDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppDataMigrateStatusDao\">\n    <sql id=\"migrate_data_status_fields\">\n        migrate_id,migrate_tool,migrate_machine_ip,migrate_machine_port,source_migrate_type,source_servers,target_migrate_type,target_servers,\n        source_app_id,target_app_id,redis_source_version,redis_target_version,\n        user_id,status,start_time,end_time,log_path,config_path\n    </sql>\n\n    <insert id=\"save\" parameterType=\"AppDataMigrateStatus\" keyProperty=\"id\" useGeneratedKeys=\"true\">\n        insert into app_data_migrate_status\n        (<include refid=\"migrate_data_status_fields\"/>)\n        values\n        (#{migrateId},#{migrateTool},#{migrateMachineIp},#{migrateMachinePort},#{sourceMigrateType},#{sourceServers},#{targetMigrateType},#{targetServers},\n        #{sourceAppId},#{targetAppId},#{redisSourceVersion},#{redisTargetVersion},\n        #{userId},#{status},#{startTime},#{endTime},#{logPath},#{configPath})\n    </insert>\n\n    <select id=\"getMigrateTaskCount\" resultType=\"int\">\n        select count(DISTINCT migrate_id)\n        from app_data_migrate_status where 1=1 and source_migrate_type != 9\n        <choose>\n            <when test=\"appDataMigrateSearch.sourceAppId != null and appDataMigrateSearch.sourceAppId > 0\">\n                and source_app_id = #{appDataMigrateSearch.sourceAppId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.targetAppId != null and appDataMigrateSearch.targetAppId > 0\">\n                and target_app_id = #{appDataMigrateSearch.targetAppId}\n            </when>\n        </choose>\n\n        <choose>\n            <when test=\"appDataMigrateSearch.sourceInstanceIp != null and appDataMigrateSearch.sourceInstanceIp != ''\">\n                and instr(source_servers, #{appDataMigrateSearch.sourceInstanceIp}) > 0\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.targetInstanceIp != null and appDataMigrateSearch.targetInstanceIp != ''\">\n                and instr(target_servers, #{appDataMigrateSearch.targetInstanceIp}) > 0\n\n            </when>\n        </choose>\n\n        <choose>\n            <when test=\"appDataMigrateSearch.startDate != null and appDataMigrateSearch.startDate != ''\">\n                and start_time > #{appDataMigrateSearch.startDate}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.endDate != null and appDataMigrateSearch.endDate != ''\">\n                and end_time &lt;#{appDataMigrateSearch.endDate}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.status >= 0\">\n                and status = #{appDataMigrateSearch.status}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.migrateMachine != null and appDataMigrateSearch.migrateMachine != ''\">\n                and migrate_machine_ip = #{appDataMigrateSearch.migrateMachine}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.userId != null and appDataMigrateSearch.userId>0\">\n                and user_id = #{appDataMigrateSearch.userId}\n            </when>\n        </choose>\n    </select>\n\n    <select id=\"search\" resultType=\"AppDataMigrateStatus\">\n        select app_data_migrate_status.id,<include refid=\"migrate_data_status_fields\"/>,app_user.ch_name as user_name,count(migrate_id) as task_count\n        from app_data_migrate_status, app_user where 1=1 and source_migrate_type != 9 and app_data_migrate_status.user_id=app_user.id\n        <choose>\n            <when test=\"appDataMigrateSearch.migrateId != null and appDataMigrateSearch.migrateId > 0\">\n                and migrate_id = #{appDataMigrateSearch.migrateId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.sourceAppId != null and appDataMigrateSearch.sourceAppId > 0\">\n                and source_app_id = #{appDataMigrateSearch.sourceAppId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.targetAppId != null and appDataMigrateSearch.targetAppId > 0\">\n                and target_app_id = #{appDataMigrateSearch.targetAppId}\n            </when>\n        </choose>\n\n        <choose>\n            <when test=\"appDataMigrateSearch.sourceInstanceIp != null and appDataMigrateSearch.sourceInstanceIp != ''\">\n                and instr(source_servers, #{appDataMigrateSearch.sourceInstanceIp}) > 0\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.targetInstanceIp != null and appDataMigrateSearch.targetInstanceIp != ''\">\n                and instr(target_servers, #{appDataMigrateSearch.targetInstanceIp}) > 0\n            </when>\n        </choose>\n\n        <choose>\n            <when test=\"appDataMigrateSearch.startDate != null and appDataMigrateSearch.startDate != ''\">\n                and start_time > #{appDataMigrateSearch.startDate}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.endDate != null and appDataMigrateSearch.endDate != ''\">\n                and end_time &lt;#{appDataMigrateSearch.endDate}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.status >= 0\">\n                and status = #{appDataMigrateSearch.status}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.migrateMachine != null and appDataMigrateSearch.migrateMachine != ''\">\n                and migrate_machine_ip = #{appDataMigrateSearch.migrateMachine}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"appDataMigrateSearch.userId != null and appDataMigrateSearch.userId >0\">\n                and user_id = #{appDataMigrateSearch.userId}\n            </when>\n        </choose>\n        group by migrate_id order by start_time desc\n        <choose>\n            <when test=\"appDataMigrateSearch.page != null\">\n                <choose>\n                    <when test=\"appDataMigrateSearch.page.totalCount > appDataMigrateSearch.page.pageSize\">\n                        limit #{appDataMigrateSearch.page.start},#{appDataMigrateSearch.page.pageSize}\n                    </when>\n                    <otherwise>\n                        limit #{appDataMigrateSearch.page.totalCount}\n                    </otherwise>\n                </choose>\n            </when>\n        </choose>\n    </select>\n\n    <select id=\"get\" resultType=\"AppDataMigrateStatus\">\n        select id,\n        <include refid=\"migrate_data_status_fields\"/>\n        from app_data_migrate_status where id = #{id}\n    </select>\n\n    <select id=\"getList\" resultType=\"AppDataMigrateStatus\">\n        select id,\n        <include refid=\"migrate_data_status_fields\"/>\n        from app_data_migrate_status where migrate_id = #{migrateId}\n    </select>\n\n\n    <select id=\"getByMigrateId\" resultType=\"AppDataMigrateStatus\">\n        select id,\n        <include refid=\"migrate_data_status_fields\"/>\n        from app_data_migrate_status where migrate_id = #{migrateId}\n    </select>\n\n    <update id=\"updateStatus\">\n    \tupdate app_data_migrate_status set status = #{status}, end_time=now() where id = #{id} and status != 1\n    </update>\n\n    <select id=\"getAllOnMigrateId\" resultType=\"long\">\n        select id from app_data_migrate_status where status !=1;\n    </select>\n\n    <select id=\"getRunningMigrateByIp\" resultType=\"AppDataMigrateStatus\">\n        select id,\n            <include refid=\"migrate_data_status_fields\"/>\n        from app_data_migrate_status\n        where migrate_machine_ip = #{migrateMachineIp}\n          and (status !=1 and status != 2)\n    </select>\n\n    <select id=\"getMaxMigratePortByIp\" resultType=\"int\">\n        select max(migrate_machine_port)\n        from app_data_migrate_status\n        where migrate_machine_ip = #{migrateMachineIp}\n    </select>\n\n</mapper>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppImportDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppImportDao\">\n    <sql id=\"columns\">\n    \tid,app_id,mem_size,source_type,redis_version_name,instance_info,redis_password,status,step,app_build_task_id,migrate_id\n    </sql>\n\n    <select id=\"get\" resultType=\"com.sohu.cache.entity.AppImport\">\n        select id,\n        <include refid=\"columns\"/>,create_time,update_time\n        from app_import\n        where id=#{id}\n    </select>\n\n    <insert id=\"save\" parameterType=\"AppImport\" keyProperty=\"id\" useGeneratedKeys=\"true\">\n        insert into app_import\n        (<include refid=\"columns\"/>)\n        values\n        (#{id},#{appId},#{memSize},#{sourceType},#{redisVersionName},#{instanceInfo},#{redisPassword},#{status},#{step},#{appBuildTaskId},#{migrateId})\n    </insert>\n\n    <update id=\"update\" parameterType=\"AppImport\">\n    \tupdate app_import\n    \t\tset app_id=#{appId},mem_size=#{memSize},source_type=#{sourceType}, redis_version_name=#{redisVersionName},instance_info=#{instanceInfo}, redis_password=#{redisPassword}, status=#{status}, step=#{step},app_build_task_id=#{appBuildTaskId}, migrate_id=#{migrateId}\n    \twhere id=#{id}\n    </update>\n\n    <select id=\"getAppImports\" resultType=\"AppImport\" parameterType=\"int\">\n        select id,\n        <include refid=\"columns\"/>,create_time,update_time\n        from app_import where 1=1\n        <choose>\n            <when test=\"status != null and status > -1\">\n                and status = #{status}\n            </when>\n        </choose>\n    </select>\n</mapper>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppInstanceClientRelationDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.AppInstanceClientRelationDao\">\r\n\t<sql id=\"columns\">\r\n\t\tapp_id,client_ip,instance_host,instance_port,instance_id,day\r\n\t</sql>\r\n\t\r\n\t<insert id=\"save\" parameterType=\"AppInstanceClientRelation\">\r\n\t\tinsert ignore into app_client_instance(<include refid=\"columns\"/>)\r\n\t\tvalues(#{appId},#{clientIp},#{instanceHost},#{instancePort},#{instanceId},#{day})\r\n\t</insert>\r\n\t\r\n\t<insert id=\"batchSave\" >\r\n\t\tinsert ignore into app_client_instance(<include refid=\"columns\" />)\r\n\t\tvalues\r\n\t\t<foreach collection=\"list\" item=\"appInstanceClientRelation\" separator=\",\">\r\n\t\t\t(#{appInstanceClientRelation.appId},#{appInstanceClientRelation.clientIp},#{appInstanceClientRelation.instanceHost},#{appInstanceClientRelation.instancePort},#{appInstanceClientRelation.instanceId},#{appInstanceClientRelation.day})\r\n\t    </foreach>\r\n\t</insert>\r\n\t\r\n\t<select id=\"isExist\" resultType=\"int\">\r\n\t\tselect count(*) from app_client_instance where app_id=#{appId} and day=#{day} and client_ip=#{clientIp} and instance_id=#{instanceId}\r\n\t</select>\r\n\t\r\n\t<select id=\"getAppInstanceClientRelationList\" resultType=\"AppInstanceClientRelation\">\r\n\t\tselect <include refid=\"columns\"/> from app_client_instance where app_id=#{appId} and day=date(#{day})\r\n\t</select>\r\n\t\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppStatsDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.AppStatsDao\">\n    <sql id=\"app_minute_statistics_columns\">\n        app_id,collect_time,hits,misses,command_count,used_memory,used_memory_rss,\n        expired_keys,evicted_keys,net_input_byte,net_output_byte,\n        connected_clients,object_size,\n        cpu_sys,cpu_user,cpu_sys_children,cpu_user_children,\n        modify_time,accumulation,used_disk\n    </sql>\n\n    <sql id=\"select_table\">\n        <choose>\n            <when test=\"td.dimensionality ==0\">app_minute_command_statistics</when>\n            <otherwise>app_hour_command_statistics</otherwise>\n        </choose>\n    </sql>\n\n    <sql id=\"select_stat_table\">\n        <choose>\n            <when test=\"td.dimensionality ==0\">app_minute_statistics</when>\n            <otherwise>app_hour_statistics</otherwise>\n        </choose>\n    </sql>\n\n    <sql id=\"select_stat_filed\">\n        <choose>\n            <when test=\"td.dimensionality ==0\">\n                <include refid=\"app_minute_statistics_columns\"/>\n            </when>\n            <otherwise>app_id,collect_time,hits,misses,command_count,used_memory/60 used_memory,\n                expired_keys,evicted_keys,net_input_byte,net_output_byte,\n                connected_clients,object_size,modify_time,accumulation\n            </otherwise>\n        </choose>\n    </sql>\n\n    <sql id=\"app_minute_command_statistics_columns\">\n        app_id,collect_time,command_name,command_count,modify_time\n    </sql>\n\n    <insert id=\"mergeMinuteAppStats\" parameterType=\"AppStats\">\n        insert into app_minute_statistics\n        (<include refid=\"app_minute_statistics_columns\"/>)\n        values\n        (#{appId},#{collectTime},#{hits},#{misses},#{commandCount},#{usedMemory},#{usedMemoryRss},\n        #{expiredKeys},#{evictedKeys},#{netInputByte},#{netOutputByte},\n        #{connectedClients},#{objectSize},\n        #{cpuSys},#{cpuUser},#{cpuSysChildren},#{cpuUserChildren},\n        #{modifyTime},1,#{usedDisk})\n        on duplicate key update\n        hits = hits + #{hits},\n        misses = misses + #{misses},\n        command_count = command_count + #{commandCount},\n        used_memory = used_memory + #{usedMemory},\n        used_memory_rss = used_memory_rss + #{usedMemoryRss},\n        expired_keys = expired_keys + #{expiredKeys},\n        evicted_keys = evicted_keys + #{evictedKeys},\n        net_input_byte = net_input_byte + #{netInputByte},\n        net_output_byte = net_output_byte + #{netOutputByte},\n        connected_clients = connected_clients + #{connectedClients},\n        object_size = object_size + #{objectSize},\n        cpu_sys = cpu_sys + #{cpuSys},\n        cpu_user = cpu_user + #{cpuUser},\n        cpu_sys_children = cpu_sys_children + #{cpuSysChildren},\n        cpu_user_children = cpu_user_children + #{cpuUserChildren},\n        modify_time = #{modifyTime},\n        accumulation = accumulation + 1,\n        used_disk = used_disk + #{usedDisk}\n    </insert>\n\n    <insert id=\"mergeMinuteCommandStatus\" parameterType=\"AppCommandStats\">\n        insert into app_minute_command_statistics\n        (<include refid=\"app_minute_command_statistics_columns\"/>)\n        values\n        (#{appId},#{collectTime},#{commandName},#{commandCount},#{modifyTime})\n        on duplicate key update\n        command_count = command_count + #{commandCount},\n        modify_time = #{modifyTime}\n    </insert>\n\n    <insert id=\"mergeHourAppStats\" parameterType=\"AppStats\">\n        insert into app_hour_statistics\n        (<include refid=\"app_minute_statistics_columns\"/>)\n        values\n        (#{appId},substring(#{collectTime}, 1,\n        10),#{hits},#{misses},#{commandCount},#{usedMemory},#{usedMemoryRss},#{expiredKeys},\n        #{evictedKeys},#{netInputByte},#{netOutputByte},#{connectedClients},#{objectSize},\n        #{cpuSys},#{cpuUser},#{cpuSysChildren},#{cpuUserChildren},\n        #{modifyTime},1,#{usedDisk})\n        on duplicate key update\n        hits = hits + #{hits},\n        misses = misses + #{misses},\n        command_count = command_count + #{commandCount},\n        used_memory = used_memory+#{usedMemory} ,\n        used_memory_rss = used_memory_rss+#{usedMemoryRss} ,\n        expired_keys = expired_keys + #{expiredKeys},\n        evicted_keys = evicted_keys + #{evictedKeys},\n        net_input_byte = net_input_byte + #{netInputByte},\n        net_output_byte = net_output_byte + #{netOutputByte},\n        connected_clients = connected_clients + #{connectedClients},\n        object_size = object_size + #{objectSize},\n        cpu_sys = cpu_sys + #{cpuSys},\n        cpu_user = cpu_user + #{cpuUser},\n        cpu_sys_children = cpu_sys_children + #{cpuSysChildren},\n        cpu_user_children = cpu_user_children + #{cpuUserChildren},\n        modify_time = #{modifyTime},\n        accumulation = accumulation + 1,\n        used_disk = used_disk + #{usedDisk}\n    </insert>\n\n    <insert id=\"mergeHourCommandStatus\" parameterType=\"AppCommandStats\">\n        insert into app_hour_command_statistics\n        (<include refid=\"app_minute_command_statistics_columns\"/>)\n        values\n        (#{appId},substring(#{collectTime}, 1, 10),#{commandName},#{commandCount},#{modifyTime})\n        on duplicate key update\n        command_count = command_count + #{commandCount},\n        modify_time = #{modifyTime}\n    </insert>\n\n    <select id=\"getAppStatsList\" resultType=\"AppStats\">\n        select\n        <include refid=\"select_stat_filed\"/>,create_time\n        from\n        <include refid=\"select_stat_table\"/>\n        where\n        app_id = #{appId} and collect_time between #{td.begin} and #{td.end}\n    </select>\n\n    <select id=\"getAppStatsByMinute\" resultType=\"AppStats\">\n        select\n        <include refid=\"app_minute_statistics_columns\"/>\n        , create_time\n        from app_minute_statistics\n        where\n        app_id = #{appId} and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n    <select id=\"getUsedMemoryMaxByTimeBetween\" resultType=\"java.lang.Long\">\n        select\n            max(used_memory)\n        from app_minute_statistics\n        where\n            app_id = #{appId} and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n    <select id=\"getOneAppStatsByMinute\" resultType=\"AppStats\">\n        select\n        <include refid=\"app_minute_statistics_columns\"/>\n        , create_time\n        from app_minute_statistics\n        where\n        app_id = #{appId}\n\n        and collect_time between #{beginTime} and #{endTime}\n        limit 0, 1\n    </select>\n\n    <select id=\"gatherAppsStats\" resultType=\"AppClientStatisticGather\">\n        select app_id,\n        DATE_FORMAT(create_time, '%Y-%m-%d') gather_time,\n        object_size,\n        used_memory,\n        used_memory_rss,\n        format(used_memory_rss/used_memory,2) as avg_mem_frag_ratio,\n        max(cpu_sys)  as                     max_cpu_sys,\n        max(cpu_user) as                     max_cpu_user,\n        used_disk\n        from app_minute_statistics\n        where modify_time between #{startTime} and #{endTime}\n        group by app_id;\n    </select>\n\n\n    <select id=\"getAppStatsByHour\" resultType=\"AppStats\">\n        select\n        <include refid=\"app_minute_statistics_columns\"/>\n        , create_time\n        from app_hour_statistics\n        where\n        app_id = #{appId} and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n    <select id=\"getAppHourStatsByTime\" resultType=\"AppStats\">\n        select\n        <include refid=\"app_minute_statistics_columns\"/>\n        , create_time\n        from app_hour_statistics\n        where collect_time between #{beginTime} and #{endTime}\n    </select>\n\n    <select id=\"getAppCommandStatsList\" resultType=\"AppCommandStats\">\n        select\n        <include refid=\"app_minute_command_statistics_columns\"/>\n        , create_time\n        from\n        <include refid=\"select_table\"/>\n        where\n        app_id = #{appId}\n        and command_name = #{commandName}\n        and collect_time between #{td.begin} and #{td.end}\n    </select>\n\n    <select id=\"getAppAllCommandStatsListByMinute\" resultType=\"AppCommandStats\">\n        select\n        app_id,\n        collect_time,\n        'commands' command_name,\n        command_count,\n        create_time,\n        modify_time\n        from app_minute_statistics\n        where\n          app_id = #{appId}\n          and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n\n    <select id=\"getAppAllCommandStatsListByHour\" resultType=\"AppCommandStats\">\n        select\n        app_id,\n        collect_time,\n        'commands' command_name,\n        command_count,\n        create_time,\n        modify_time\n        from app_hour_statistics\n        where\n          app_id = #{appId}\n          and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n    <select id=\"getAppCommandStatsListByMinuteWithCommand\" resultType=\"AppCommandStats\">\n        select\n        <include refid=\"app_minute_command_statistics_columns\"/>\n        , create_time\n        from app_minute_command_statistics\n        where\n        app_id = #{appId}\n        and command_name = #{commandName}\n        and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n\n    <select id=\"getAppCommandStatsListByHourWithCommand\" resultType=\"AppCommandStats\">\n        select\n        <include refid=\"app_minute_command_statistics_columns\"/>\n        , create_time\n        from app_hour_command_statistics\n        where\n        app_id = #{appId}\n        and command_name = #{commandName}\n        and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n\n    <select id=\"getAppAllCommandStatsList\" resultType=\"AppCommandStats\">\n        select\n        app_id,\n        collect_time,\n        'commands' command_name,\n        command_count,\n        create_time,\n        modify_time\n        from\n        <include refid=\"select_stat_table\"/>\n        where\n        app_id = #{appId}\n        and collect_time between #{td.begin} and #{td.end}\n    </select>\n\n    <!-- 获取应用命令调用次数分布 -->\n    <select id=\"getAppCommandGroup\" resultType=\"AppCommandGroup\">\n        select\n        command_name,sum(command_count) as count\n        from\n        <include refid=\"select_table\"/>\n        where\n        app_id = #{appId}\n        and collect_time between #{td.begin} and #{td.end}\n        group by command_name\n    </select>\n\n    <!-- 获取应用命令调用总次数 -->\n    <select id=\"getAppCommandCount\" resultType=\"java.lang.Long\">\n        select\n            sum(command_count) as count\n        from app_minute_command_statistics\n        where\n            app_id = #{appId}\n            and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n    <!--查询一天中应用的命令执行次数的top-->\n    <select id=\"getTopAppCommandStatsList\" resultType=\"AppCommandStats\">\n        select\n        app_id, command_name , max(collect_time) collect_time, sum(command_count) command_count ,\n        max(modify_time) modify_time, max(create_time) create_time\n        from\n        <include refid=\"select_table\"/>\n        where app_id = #{appId}\n        and collect_time between #{td.begin} and #{td.end}\n        group by app_id,command_name\n        order by command_count desc , modify_time desc\n        limit #{top};\n    </select>\n\n\n    <select id=\"getTopAppCommandGroupSum\" resultType=\"AppCommandStats\">\n        select\n        app_id,collect_time,command_name,sum(command_count) as command_count,modify_time, create_time\n        from\n        <include refid=\"select_table\"/>\n        where app_id = #{appId}\n        and collect_time between #{td.begin} and #{td.end}\n        group by command_name\n        order by sum(command_count) desc\n        limit #{top};\n    </select>\n\n    <select id=\"getCommandClimax\" resultType=\"AppCommandStats\">\n        select t2.*\n        from (select app_id, command_name, max(command_count) command_count\n        from\n        <include refid=\"select_table\"/>\n        where\n        app_id = #{appId}\n        and command_name = #{commandName}\n        and collect_time between #{td.begin} and #{td.end}\n        group by app_id,command_name) t1 ,\n        <include refid=\"select_table\"/>\n        t2\n        where t1.app_id = t2.app_id\n        and t1.command_name=t2.command_name\n        and t1.command_count=t2.command_count\n        and t2.collect_time between #{td.begin} and #{td.end}\n        limit 1;\n    </select>\n\n    <select id=\"getCommandClimaxCount\" resultType=\"AppCommandStats\">\n        select max(command_count) as command_count\n        from\n        <include refid=\"select_table\"/>\n        where app_id = #{appId} and collect_time between #{td.begin} and #{td.end} and command_name = #{commandName}\n    </select>\n\n    <select id=\"getCommandClimaxCreateTime\" resultType=\"AppCommandStats\">\n        select create_time\n        from\n        <include refid=\"select_table\"/>\n        where app_id = #{appId} and collect_time between #{td.begin} and #{td.end} and command_name = #{commandName} and\n        command_count=#{commandCount} limit 1\n    </select>\n\n    <select id=\"getAppMinuteStat\" resultType=\"hashmap\">\n        select \n        \tavg(connected_clients) as avgClientCount,\n        \tmax(connected_clients) as maxClientCount,\n        \tavg(command_count) as avgCommandCount, \n        \tmax(command_count) as maxCommandCount,\n            sum(hits)/(sum(hits) + sum(misses)) as avgHitRatio,\n\t        max(hits/(hits + misses)) as maxHitRatio,\n\t        min(hits/(hits + misses)) as minHitRatio,\n\t        avg(used_memory) as avgUsedMemory,\n\t        max(used_memory) as maxUsedMemory,\n\t        sum(expired_keys) as expiredKeys,\n\t        sum(evicted_keys) as evictedKeys,\n\t        avg(net_input_byte) as avgNetInputByte,\n\t        max(net_input_byte) as maxNetInputByte,\n\t        avg(net_output_byte) as avgNetOutputByte,\n\t        max(net_output_byte) as maxNetOutputByte,\n\t        avg(object_size) as avgObjectSize,\n\t        max(object_size) as maxObjectSize,\n            avg(used_disk) as avgUsedDisk,\n            max(used_disk) as maxUsedDisk\n        from app_minute_statistics\n        where app_id = #{appId} and collect_time between #{beginTime} and #{endTime}\n    </select>\n\n    <select id=\"getTopMemFragRatioApps\" resultType=\"AppTopMemFragRatio\">\n        select c.app_id,c.name,c.officer,c.status,c.intro,c.is_test,a.instanceCount,a.avgMemFragRatio,a.memUsedRatio,b.exceptionCount,d.name as versionName\n        from\n        (select app_id,count(id) as instanceCount,format(avg(mem_fragmentation_ratio),2) as avgMemFragRatio, round(sum(used_memory)/sum(max_memory)*100,2) as memUsedRatio\n          FROM instance_statistics\n          WHERE modify_time between #{startTime} and #{endTime} group by app_id order by avg(mem_fragmentation_ratio) desc) a\n        left join\n        (select app_id,sum(exception_count) as exceptionCount\n          FROM app_client_exception_minute_stat\n          WHERE collect_time between #{startTime} and #{endTime} group by app_id) b\n        on a.app_id=b.app_id\n        left join\n        app_desc c\n        on a.app_id=c.app_id\n        left join\n        redis_version d\n        on c.version_id=d.id\n        where c.status=2;\n    </select>\n\n    <select id=\"getMemFragRatios\" resultType=\"AppClientStatisticGather\">\n        select app_id, count(id) as instanceCount, format(avg(mem_fragmentation_ratio),2) as avgMemFragRatio, round(sum(used_memory)/sum(max_memory)*100,2) as memUsedRatio, DATE_FORMAT(modify_time,'%Y-%m-%d') gather_time\n        FROM instance_statistics\n        WHERE modify_time between #{startTime} and #{endTime}\n        group by app_id\n    </select>\n\n    <select id=\"getExceptionCount\" resultType=\"AppClientStatisticGather\">\n        select app_id, sum(exception_count) as exceptionCount, DATE_FORMAT(collect_time,'%Y-%m-%d') gather_time\n        FROM app_client_exception_minute_stat\n        WHERE collect_time between #{startTime} and #{endTime}\n        group by app_id\n    </select>\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppToUserDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n\r\n<mapper namespace=\"com.sohu.cache.dao.AppToUserDao\">\r\n    <sql id=\"app_to_user_column\">\r\n    \tid,user_id,app_id\r\n    </sql>\r\n    \r\n    <insert id=\"save\" parameterType=\"AppToUser\">\r\n    \tinsert into app_to_user\r\n    \t\t(<include refid=\"app_to_user_column\"/>)\r\n\t\tvalues\r\n\t\t\t(#{id},#{userId},#{appId})    \t\r\n    </insert>\r\n    \r\n    <!-- 获取用户的app -->\r\n    <select id=\"getByUserId\" resultType=\"AppToUser\" parameterType=\"long\">\r\n    \tselect           \r\n        \t<include refid=\"app_to_user_column\"/>\r\n        from app_to_user where user_id=#{userId}\r\n    </select>\r\n    \r\n    <!-- 获取app的用户 -->\r\n    <select id=\"getByAppId\" resultType=\"AppToUser\" parameterType=\"long\">\r\n    \tselect           \r\n        \t<include refid=\"app_to_user_column\"/>\r\n        from app_to_user where app_id=#{appId}\r\n    </select>\r\n    \r\n    \r\n    <delete id=\"deleteByAppId\" parameterType=\"long\">\r\n    \tdelete from app_to_user where app_id=#{appId}\r\n    </delete>\r\n    \r\n    <select id=\"deleteAppToUser\" parameterType=\"AppToUser\">\r\n        delete from app_to_user where app_id=#{appId} and user_id=#{userId}\r\n    </select>\r\n\r\n    <update id=\"takeOverAppToUser\">\r\n        update app_to_user\r\n            set user_id = #{newUserId}\r\n        where app_id=#{appId} and user_id = #{originUserId}\r\n    </update>\r\n\r\n</mapper>\r\n\r\n\r\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/AppUserDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.sohu.cache.dao.AppUserDao\">\n\t<!-- \n\t字典sql\n        set @tn='app_user';\n        select\n            group_concat(column_name)\n        from\n            information_schema.columns\n        where\n            table_schema = 'cache-cloud' and table_name = @tn;\n\t-->\n\t<sql id=\"user_column\">\n\t\tid,name,ch_name,email,mobile,weChat,type,isAlert,password,company,purpose,register_time,biz_id\n    </sql>\n\n    <sql id=\"user_insert_column\">\n\t\tid,name,ch_name,email,mobile,weChat,type,isAlert,password,company,purpose, biz_id\n    </sql>\n\n    <select id=\"get\" resultType=\"com.sohu.cache.entity.AppUser\" parameterType=\"long\">\n        select           \n        \t<include refid=\"user_column\"/>\n        from app_user\n        where id = #{id};\n    </select>\n    \n    <select id=\"getByName\" resultType=\"com.sohu.cache.entity.AppUser\" parameterType=\"string\">\n        select           \n        \t<include refid=\"user_column\"/>\n        from app_user\n        where binary name = #{name};\n    </select>\n\n    <select id=\"getByEmail\" resultType=\"com.sohu.cache.entity.AppUser\" parameterType=\"string\">\n        select\n        <include refid=\"user_column\"/>\n        from app_user\n        where email = #{email};\n    </select>\n    \n    <insert id=\"save\" parameterType=\"com.sohu.cache.entity.AppUser\" keyProperty=\"id\" useGeneratedKeys=\"true\">\n    \tinsert into app_user\n    \t\t(<include refid=\"user_insert_column\"/>)\n\t\tvalues\n\t\t\t(#{id},#{name},#{chName},#{email},#{mobile},#{weChat},#{type},#{isAlert},#{password},#{company},#{purpose},#{bizId})\n    </insert>\n    \n    <delete id=\"delete\" parameterType=\"long\">\n    \tdelete from app_user where id=#{id}\n    </delete>\n\n    <update id=\"updatePwd\">\n    \tupdate app_user\n    \t\tset password=#{password}\n    \twhere id=#{id}\n    </update>\n\n    <update id=\"update\" parameterType=\"com.sohu.cache.entity.AppUser\">\n    \tupdate app_user \n    \t\tset name=#{name}, ch_name=#{chName}, email=#{email}, mobile=#{mobile}, weChat=#{weChat}, type=#{type}, isAlert=#{isAlert}, company=#{company}, purpose=#{purpose}, biz_id=#{bizId}\n    \twhere id=#{id}\n    </update>\n    \n    <select id=\"getUserList\" resultType=\"com.sohu.cache.entity.AppUser\" parameterType=\"string\">\n    \tselect           \n        \t<include refid=\"user_column\"/>\n        from app_user where 1=1\n        <choose>\n        \t<when test=\"chName != '' and chName != null\">\n        \t \tand ch_name=#{chName}\n        \t</when>\n        </choose>\n    </select>\n\n    <select id=\"getUserWithBizList\" resultType=\"com.sohu.cache.web.vo.AppUserVo\">\n        select\n            au.id, au.name, au.ch_name, au.email, au.mobile, au.weChat, au.type,\n            au.isAlert, au.password, au.company, au.purpose, au.register_time, au.biz_id,\n            ab.name bizName, ab.biz_desc\n        from app_user au\n        left join app_biz ab\n            on au.biz_id = ab.id\n        <where>\n            <choose>\n                <when test=\"chName != '' and chName != null\">\n                    and au.ch_name=#{chName}\n                </when>\n            </choose>\n            <choose>\n                <when test=\"bizName != '' and bizName != null\">\n                    and ab.name=#{bizName}\n                </when>\n            </choose>\n        </where>\n    </select>\n\n    <select id=\"getAllUser\" resultType=\"com.sohu.cache.entity.AppUser\">\n        select\n        <include refid=\"user_column\"/>\n        from app_user where type >= 0\n    </select>\n\n    <select id=\"getAdminList\" resultType=\"com.sohu.cache.entity.AppUser\">\n        select\n        <include refid=\"user_column\"/>\n        from app_user where type = 0\n    </select>\n    \n</mapper>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/ConfigDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.ConfigDao\">\r\n\t<sql id=\"columns\">\r\n\t\tconfig_key,config_value ,info,status,order_id\r\n\t</sql>\r\n\r\n\t<select id=\"getConfigList\" resultType=\"com.sohu.cache.entity.SystemConfig\">\r\n\t\tselect\r\n\t\t<include refid=\"columns\" />\r\n\t\tfrom system_config where status=#{status} order by order_id\r\n\t</select>\r\n\r\n\t<update id=\"update\">\r\n\t\tupdate system_config set config_value = #{configValue} where config_key=#{configKey}\r\n\t</update>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/ConfigRestartRecordDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.ConfigRestartRecordDao\">\n\n    <sql id=\"config_restart_record_columns\">\n        app_id, app_name, instances, operate_type, param, status, log, user_id, user_name, start_time, end_time, create_time, update_time\n    </sql>\n    \n    <sql id=\"config_restart_record_fields\">\n    \t#{appId}, #{appName}, #{instances}, #{operateType}, #{param}, #{status}, #{log}, #{userId}, #{userName}, #{startTime}, #{endTime}, #{createTime}, #{updateTime}\n    </sql>\n\n    <insert id=\"save\" parameterType=\"com.sohu.cache.entity.ConfigRestartRecord\" keyProperty=\"id\" useGeneratedKeys=\"true\">\n        insert into config_restart_record\n        \t(<include refid=\"config_restart_record_columns\"/>)\n        values\n        \t(<include refid=\"config_restart_record_fields\"/>)\n    </insert>\n    \n    <select id=\"getById\" resultType=\"com.sohu.cache.entity.ConfigRestartRecord\">\n        select id,<include refid=\"config_restart_record_columns\"/>, execute_ip_port\n        from config_restart_record\n        where id = #{id}\n    </select>\n    \n    <update id=\"updateStatus\">\n    \tupdate config_restart_record set status = #{status}, update_time = now() where id = #{id}\n    </update>\n\n    <update id=\"updateByCondition\">\n    \tupdate config_restart_record\n    \t<set>\n            <if test=\"status != null and status > 0\">\n                status = #{status},\n            </if>\n            <if test=\"startTime != null\">\n                start_time = #{startTime},\n            </if>\n            <if test=\"endTime != null\">\n                end_time = #{endTime},\n            </if>\n            <if test=\"updateTime != null\">\n                update_time = #{updateTime},\n            </if>\n            <if test=\"log != null\">\n                log = #{log},\n            </if>\n            update_time = now()\n        </set>\n    \twhere id = #{id}\n    </update>\n    \n    <select id=\"getCountByCondition\" resultType=\"int\" parameterType=\"com.sohu.cache.entity.ConfigRestartRecord\">\n        select\n            count(0)\n        from config_restart_record\n        <where>\n            <if test=\"appId != null and appId > 0\">\n                and app_id = #{appId}\n            </if>\n            <if test=\"status != null and status >= 0\">\n                and status = #{status}\n            </if>\n        </where>\n    </select>\n    \n    <select id=\"getListByCondition\" resultType=\"com.sohu.cache.entity.ConfigRestartRecord\" parameterType=\"com.sohu.cache.entity.ConfigRestartRecord\">\n        select\n            id,<include refid=\"config_restart_record_columns\"/>\n        from config_restart_record\n        <where>\n            <if test=\"appId != null and appId > 0\">\n                 and app_id = #{appId}\n            </if>\n            <if test=\"status != null and status >= 0\">\n                 and status = #{status}\n            </if>\n        </where>\n        order by create_time desc\n        <if test=\"page != null\">\n            <choose>\n                <when test=\"page.totalCount > page.pageSize\">\n                    limit #{page.start},#{page.pageSize};\n                </when>\n                <otherwise>\n                    limit #{page.totalCount}\n                </otherwise>\n            </choose>\n        </if>\n    </select>\n    \n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/DiagnosticTaskRecordDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.DiagnosticTaskRecordDao\">\n    <sql id=\"columns\">\n    \tapp_id,type,task_id,parent_task_id,audit_id,status,cost,redis_key,node,diagnostic_condition,param1,param2\n    </sql>\n\n    <insert id=\"insertDiagnosticTaskRecord\" parameterType=\"com.sohu.cache.entity.DiagnosticTaskRecord\" keyProperty=\"id\"\n            useGeneratedKeys=\"true\">\n        insert into diagnostic_task_record (<include refid=\"columns\"/>)\n        values(#{appId},#{type},#{taskId},#{parentTaskId},#{auditId},#{status},#{cost},#{redisKey},#{node},#{diagnosticCondition},#{param1},#{param2})\n    </insert>\n\n    <update id=\"updateDiagnosticStatus\">\n        update diagnostic_task_record\n        set redis_key = #{redisKey},status = #{status}, cost = #{cost}\n        where id=#{id}\n    </update>\n\n    <select id=\"getDiagnosticTaskRecords\" resultType=\"DiagnosticTaskRecord\">\n        select *,concat(concat(cost div 60000, 'm:'), concat(cost mod 60000 div 1000 , 's:'), concat(cost mod 1000, 'ms')) as format_cost_time\n        from diagnostic_task_record\n        where 1=1\n        <choose>\n            <when test=\"appId != null and appId > 0\">\n                and app_id = #{appId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"parentTaskId != null and parentTaskId > 0\">\n                and parent_task_id = #{parentTaskId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"auditId != null and auditId > 0\">\n                and audit_id = #{auditId}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"type != null and type > -1\">\n                and type = #{type}\n            </when>\n        </choose>\n        <choose>\n            <when test=\"status != null and status > -1\">\n                and status = #{status}\n            </when>\n        </choose>\n        order by create_time desc\n    </select>\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceAlertConfigDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.InstanceAlertConfigDao\">\r\n\r\n    <sql id=\"instance_alert_config_column\">\r\n    \talert_config,alert_value,config_info,type,important_level,instance_id,status,compare_type,\r\n    \tcheck_cycle,update_time,last_check_time,app_type\r\n    </sql>\r\n    \r\n    <insert id=\"save\" parameterType=\"InstanceAlertConfig\"  keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n\t\tinsert into instance_alert_configs(<include refid=\"instance_alert_config_column\" />)\r\n\t\tvalues(#{alertConfig},#{alertValue},#{configInfo},#{type},#{importantLevel},#{instanceId},#{status},#{compareType},\r\n\t\t#{checkCycle},#{updateTime},#{lastCheckTime},#{appType})\r\n\t\ton duplicate key update \r\n\t\talert_value = #{alertValue},check_cycle = #{checkCycle}, update_time = now(),status = #{status},last_check_time=now()      \t\r\n\t</insert>\r\n\r\n    <insert id=\"batchSave\">\r\n        insert into instance_alert_configs(<include refid=\"instance_alert_config_column\" />)\r\n        values\r\n        <foreach collection=\"list\" item=\"instance\" separator=\",\" >\r\n            (#{instance.alertConfig},#{instance.alertValue},#{instance.configInfo},#{instance.type},#{instance.importantLevel},#{instance.instanceId},#{instance.status},#{instance.compareType},\r\n            #{instance.checkCycle},#{instance.updateTime},#{instance.lastCheckTime},#{instance.appType})\r\n        </foreach>\r\n        on duplicate key update\r\n        alert_value = VALUES(alert_value),\r\n        compare_type = VALUES(compare_type),\r\n        check_cycle = VALUES(check_cycle),\r\n        update_time = now(),\r\n        status = VALUES(status),\r\n        last_check_time=now()\r\n    </insert>\r\n    \r\n    <select id=\"getAll\" resultType=\"InstanceAlertConfig\">\r\n        select id,<include refid=\"instance_alert_config_column\"/> from instance_alert_configs\r\n    </select>\r\n    \r\n    <select id=\"get\" resultType=\"InstanceAlertConfig\">\r\n        select id,<include refid=\"instance_alert_config_column\"/> from instance_alert_configs where id = #{id}\r\n    </select>\r\n\t\r\n\t<delete id=\"remove\">\r\n\t\tdelete from instance_alert_configs where id = #{id}\r\n\t</delete>\r\n\t\r\n\t<select id=\"getByType\" resultType=\"InstanceAlertConfig\">\r\n        select id,<include refid=\"instance_alert_config_column\"/> from instance_alert_configs where type = #{type} order by id\r\n    </select>\r\n\r\n    <select id=\"getByTypeAndAppType\" resultType=\"InstanceAlertConfig\">\r\n        select id,<include refid=\"instance_alert_config_column\"/> from instance_alert_configs\r\n       where\r\n           type = #{type}\r\n            <if test=\"appType != null and (appType == 0 or appType == 1)\">\r\n                and app_type = #{appType}\r\n            </if>\r\n       order by id\r\n    </select>\r\n\r\n    <select id=\"getByAlertConfig\" resultType=\"InstanceAlertConfig\">\r\n        select id,<include refid=\"instance_alert_config_column\"/> from instance_alert_configs where alert_config = #{alertConfig}\r\n    </select>\r\n\r\n    <select id=\"getByAlertConfigAndType\" resultType=\"InstanceAlertConfig\">\r\n        select\r\n            id,<include refid=\"instance_alert_config_column\"/>\r\n        from instance_alert_configs\r\n        where alert_config = #{alertConfig} and type = #{type}\r\n        <if test=\"appType != null and (appType == 0 or appType == 1)\">\r\n            and app_type = #{appType}\r\n        </if>\r\n    </select>\r\n\r\n    <update id=\"update\">\r\n    \t\tupdate instance_alert_configs set alert_value = #{alertValue}, check_cycle = #{checkCycle} , compare_type = #{compareType}, important_level = #{importantLevel}, update_time = now() where id = #{id}\r\n    </update>\r\n\r\n    <update id=\"updateImportantLevel\">\r\n    \t\tupdate instance_alert_configs set important_level = #{importantLevel} where alert_config = #{alertConfig} and compare_type = #{compareType} and app_type = #{appType}\r\n    </update>\r\n\r\n    <update id=\"updateLastCheckTime\">\r\n    \t\tupdate instance_alert_configs set last_check_time = #{lastCheckTime} where id = #{id}\r\n    </update>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceBigKeyDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.InstanceBigKeyDao\">\r\n\r\n    <sql id=\"instance_big_key_columns\">\r\n        instance_id,app_id,audit_id,role,ip,port,big_key,type,length,create_time\r\n    </sql>\r\n\r\n    <insert id=\"batchSave\">\r\n        insert into instance_big_key(<include refid=\"instance_big_key_columns\"/>)\r\n        values\r\n        <foreach collection=\"instanceBigKeyList\" item=\"instanceBigKey\" separator=\",\">\r\n        \t(#{instanceBigKey.instanceId},#{instanceBigKey.appId},#{instanceBigKey.auditId},\r\n        \t#{instanceBigKey.role},#{instanceBigKey.ip},#{instanceBigKey.port},#{instanceBigKey.bigKey},\r\n        \t#{instanceBigKey.type},#{instanceBigKey.length},#{instanceBigKey.createTime})\r\n\t    </foreach>\r\n    </insert>\r\n    \r\n    <select id=\"getAppBigKeyCount\" resultType=\"int\">\r\n        select count(*) as count\r\n        from instance_big_key where app_id = #{appId} and audit_id = #{auditId}\r\n    </select>\r\n    \r\n    <select id=\"getAppBigKeyList\" resultType=\"InstanceBigKey\">\r\n        select id,<include refid=\"instance_big_key_columns\"/>\r\n        from instance_big_key where app_id = #{appId} and audit_id = #{auditId}\r\n        <choose>\r\n        \t<when test=\"page != null\">\r\n        \t\t<choose>\r\n\t\t        \t<when test=\"page.totalCount > page.pageSize\">\r\n            \t\t\tlimit #{page.start},#{page.pageSize};\r\n\t\t        \t</when>\r\n\t\t        \t<otherwise>\r\n\t\t        \t    limit #{page.totalCount}\r\n\t\t        \t</otherwise>\r\n\t\t        </choose>\r\n        \t</when>\r\n        </choose>\r\n    </select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceConfigDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.InstanceConfigDao\">\r\n    <sql id=\"columns\">\r\n    \tconfig_key, config_value, info, update_time, type, status,version_id, refresh, value_type\r\n    </sql>\r\n\r\n    <select id=\"getByType\" resultType=\"com.sohu.cache.entity.InstanceConfig\">\r\n        select id,<include refid=\"columns\"/> from instance_config where type = #{type}\r\n    </select>\r\n\r\n    <select id=\"getByVersionAndType\" resultType=\"com.sohu.cache.entity.InstanceConfig\">\r\n        select id,<include refid=\"columns\"/> from instance_config where type = #{type} and version_id = #{versionId}\r\n    </select>\r\n\r\n    <select id=\"getByVersion\" resultType=\"com.sohu.cache.entity.InstanceConfig\">\r\n        select id,<include refid=\"columns\"/> from instance_config where version_id = #{versionId} and status = 1\r\n    </select>\r\n    \r\n    <select id=\"getById\" resultType=\"com.sohu.cache.entity.InstanceConfig\">\r\n        select id,<include refid=\"columns\"/> from instance_config where id = #{id}\r\n    </select>\r\n    \r\n    <select id=\"getByConfigKeyAndType\" resultType=\"com.sohu.cache.entity.InstanceConfig\">\r\n        select id,<include refid=\"columns\"/> from instance_config where config_key = #{configKey} and type = #{type}\r\n    </select>\r\n    \r\n    <select id=\"getAllInstanceConfig\" resultType=\"com.sohu.cache.entity.InstanceConfig\">\r\n        select id,<include refid=\"columns\"/> from instance_config\r\n    </select>\r\n\r\n    <select id=\"getConfigByRedisVersionId\" resultType=\"com.sohu.cache.entity.InstanceConfig\">\r\n        select id,<include refid=\"columns\"/> from instance_config where version_id = #{versionId}\r\n    </select>\r\n\r\n    <insert id=\"saveOrUpdate\">\r\n\t\tinsert into instance_config(<include refid=\"columns\" />)\r\n\t\tvalues(#{configKey},#{configValue},#{info},now(),#{type},#{status},#{versionId}, #{refresh}, #{valueType})\r\n\t    on duplicate key update\r\n\t    config_value = #{configValue}, info = #{info}, \r\n\t    update_time = now(),status = #{status} ,version_id=#{versionId}, refresh = #{refresh}, value_type = #{valueType}\r\n\t</insert>\r\n\t\r\n\t<update id=\"updateStatus\">\r\n\t\tupdate instance_config set status = #{status} where id = #{id}\r\n\t</update>\r\n\t\r\n\t<delete id=\"remove\">\r\n\t\tdelete from instance_config where id = #{id}\r\n\t</delete>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n\r\n<mapper namespace=\"com.sohu.cache.dao.InstanceDao\">\r\n    <sql id=\"instance_info_columns\">\r\n        id, app_id, host_id, ip, port, status, mem, conn, cmd, type, update_time\r\n    </sql>\r\n\r\n    <!--通过type查询实例列表-->\r\n    <select id=\"getInstListByType\" resultType=\"InstanceInfo\" parameterType=\"int\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE type = #{type} and status=1\r\n        ORDER BY id;\r\n    </select>\r\n\r\n    <!--通过appId查询实例列表-->\r\n    <select id=\"getInstListByAppId\" resultType=\"InstanceInfo\" parameterType=\"long\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE app_id = #{appId}\r\n        ORDER BY id;\r\n    </select>\r\n\r\n    <!--通过appId查询有效的实例列表-->\r\n    <select id=\"getEffectiveInstListByAppId\" resultType=\"InstanceInfo\" parameterType=\"long\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE app_id = #{appId} and status in (0,1)\r\n        ORDER BY id;\r\n    </select>\r\n\r\n    <select id=\"getMemoryByHost\" resultType=\"int\">\r\n        select ifnull(sum(mem),0)\r\n        from instance_info\r\n        where ip = #{host} and status in (0,1)\r\n    </select>\r\n\r\n    <select id=\"getInstanceCountByHost\" resultType=\"int\">\r\n        select count(*)\r\n        from instance_info\r\n        where ip = #{host} and status in (0,1)\r\n    </select>\r\n\r\n    <!--通过host和port查询一个实例信息-->\r\n    <select id=\"getInstByIpAndPort\" resultType=\"InstanceInfo\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE ip = #{ip} AND port = #{port} AND status = 1\r\n    </select>\r\n\r\n    <select id=\"getAllInstByIpAndPort\" resultType=\"InstanceInfo\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE ip = #{ip} AND port = #{port}\r\n    </select>\r\n\r\n    <!--通过host和port查询一个实例信息-->\r\n    <select id=\"getAllInsts\" resultType=\"InstanceInfo\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE status in (0,1)\r\n    </select>\r\n\r\n    <select id=\"checkHeartStopInstance\" resultType=\"InstanceInfo\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE ip = #{ip} and status in (0,1)\r\n    </select>\r\n\r\n    <select id=\"getAllHeartStopInstance\" resultType=\"InstanceInfo\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE status in (0,-1)\r\n    </select>\r\n\r\n    <!--通过host和port查询一个实例数-->\r\n    <select id=\"getCountByIpAndPort\" resultType=\"int\">\r\n        SELECT count(*)\r\n        FROM instance_info\r\n        WHERE ip = #{ip} AND port = #{port}\r\n    </select>\r\n\r\n    <!--通过id查询一个实例信息-->\r\n    <select id=\"getInstanceInfoById\" resultType=\"InstanceInfo\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE id = #{id}\r\n    </select>\r\n\r\n    <!--保存实例-->\r\n    <insert id=\"saveInstance\" parameterType=\"InstanceInfo\" keyColumn=\"id\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n        INSERT INTO instance_info (app_id, host_id, ip, port, status, mem, conn, cmd, type)\r\n        VALUE(#{appId}, #{hostId}, #{ip}, #{port}, #{status}, #{mem}, #{conn}, #{cmd}, #{type});\r\n    </insert>\r\n\r\n    <update id=\"update\" parameterType=\"InstanceInfo\">\r\n        update instance_info\r\n        set\r\n        <if test=\"appId != null\">app_id=#{appId},</if>\r\n        <if test=\"hostId != null\">host_id=#{hostId},</if>\r\n        <if test=\"ip != null\">ip=#{ip},</if>\r\n        <if test=\"port != null\">port=#{port},</if>\r\n        <if test=\"mem != null\">mem=#{mem},</if>\r\n        <if test=\"conn != null\">conn=#{conn},</if>\r\n        <if test=\"cmd != null\">cmd=#{cmd},</if>\r\n        status=#{status},\r\n        update_time = now()\r\n        where\r\n        id=#{id}\r\n    </update>\r\n\r\n    <select id=\"getAppInstanceInfo\" parameterType=\"long\" resultType=\"InstanceInfo\">\r\n        select\r\n        <include refid=\"instance_info_columns\"/>\r\n        from instance_info where app_id=#{appId} and status=1\r\n    </select>\r\n\r\n    <select id=\"getInstanceTypeCount\" resultType=\"int\">\r\n        select count(*)\r\n        from instance_info\r\n        where ip = #{ip} and type = #{type}\r\n    </select>\r\n\r\n    <select id=\"getInstancesByType\" resultType=\"InstanceInfo\">\r\n        select  <include refid=\"instance_info_columns\"/>\r\n        from instance_info\r\n        where app_id = #{app_id} and type = #{type} and status=1\r\n    </select>\r\n    \r\n    <!--通过机器ip查询实例列表-->\r\n    <select id=\"getInstListByIp\" resultType=\"InstanceInfo\" parameterType=\"string\">\r\n        SELECT\r\n        <include refid=\"instance_info_columns\"/>\r\n        FROM instance_info\r\n        WHERE ip = #{ip} and status in (0,1)\r\n        ORDER BY id;\r\n    </select>\r\n    \r\n    <select id=\"getMachineInstanceCountMap\" resultType=\"hashmap\">\r\n    \tselect ip, count(*) as count from instance_info where status=1 group by ip;\r\n    </select>\r\n\r\n    <select id=\"getTotalEffectiveInst\" resultType=\"hashmap\">\r\n        SELECT info.id from app_desc app,instance_info info where app.app_id=info.app_id and app.`status`=2 and info.`status`=1;\r\n    </select>\r\n\r\n    <select id=\"getAppIdListByIp\" resultType=\"long\" parameterType=\"java.util.List\">\r\n        select app_id FROM instance_info WHERE status=1 AND ip IN\r\n        <foreach collection=\"list\" index=\"index\" item=\"ip\" open=\"(\" separator=\",\" close=\")\">\r\n            #{ip}\r\n        </foreach>\r\n        GROUP BY app_id\r\n    </select>\r\n\r\n    <update id=\"updateStatus\">\r\n        update instance_info set status=#{status},update_time = now() where app_id=#{appId} and ip=#{ip} and port=#{port}\r\n    </update>\r\n\r\n    <sql id=\"instance_info_ii_columns\">\r\n        ii.id, ii.app_id, ii.host_id, ii.ip, ii.port, ii.status, ii.mem, ii.conn, ii.cmd, ii.type, ii.update_time\r\n    </sql>\r\n\r\n    <select id=\"getInstancesByCondition\" parameterType=\"com.sohu.cache.entity.InstanceInfo\" resultType=\"com.sohu.cache.entity.InstanceInfo\">\r\n        select  <include refid=\"instance_info_ii_columns\"/>\r\n        from instance_info ii\r\n            <if test=\"status != null and status == 2\">\r\n                left join app_desc ad on ii.app_id = ad.app_id\r\n            </if>\r\n        <where>\r\n            <if test=\"appId != null and appId > 0\">\r\n                and ii.app_id = #{appId}\r\n            </if>\r\n            <if test=\"type != null and type > 0\">\r\n                and ii.type = #{type}\r\n            </if>\r\n            <if test=\"status != null and status == 2\">\r\n                and ad.status != 3\r\n                and ii.status = 2\r\n            </if>\r\n            <if test=\"status != null and status !=2 and status > 0\">\r\n                and ii.status = #{status}\r\n            </if>\r\n            <if test=\"ip != null and ip != ''\">\r\n                and ii.ip = #{ip}\r\n            </if>\r\n        </where>\r\n    </select>\r\n\r\n    <select id=\"getOfflineInstByAppIdAndHostport\" resultType=\"com.sohu.cache.entity.InstanceInfo\">\r\n        select\r\n            app_id, ip, port\r\n        from (\r\n            select\r\n                app_id, ip, port, concat(ip,\":\",port) hostport\r\n            from instance_info\r\n            where\r\n                app_id in\r\n                <foreach collection=\"appIds\" item=\"appId\" separator=\",\" open=\"(\" close=\")\">\r\n                    #{appId}\r\n                </foreach>\r\n                and status = 2\r\n        ) t\r\n        where hostport in\r\n            <foreach collection=\"hostports\" item=\"hostport\" separator=\",\" open=\"(\" close=\")\">\r\n                #{hostport}\r\n            </foreach>\r\n    </select>\r\n\r\n</mapper>\r\n\r\n\r\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceFaultDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n\r\n<mapper namespace=\"com.sohu.cache.dao.InstanceFaultDao\">\r\n    <sql id=\"instance_fault_column\">\r\n\t\tapp_id,inst_id,ip,port,status,create_time,type,reason\r\n    </sql>\r\n\r\n    <insert id=\"insert\" parameterType=\"InstanceFault\" keyColumn=\"id\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n        insert into instance_fault(app_id,inst_id,ip,port,status,create_time,type,reason)\r\n        VALUE (#{appId},#{instId},#{ip},#{port},#{status},#{createTime},#{type},#{reason})\r\n    </insert>\r\n\r\n    <select id=\"getListByInstId\" resultType=\"InstanceFault\">\r\n        select\r\n          id, <include refid=\"instance_fault_column\"/>\r\n        from instance_fault\r\n        where inst_id = #{instId}\r\n        order by create_time desc\r\n    </select>\r\n\r\n    <select id=\"getListByAppId\" resultType=\"InstanceFault\">\r\n        select\r\n        id, <include refid=\"instance_fault_column\"/>\r\n        from instance_fault\r\n        where app_id = #{appId}\r\n        order by create_time desc\r\n    </select>\r\n\r\n\r\n</mapper>\r\n\r\n\r\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceLatencyHistoryDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.InstanceLatencyHistoryDao\">\n\n    <sql id=\"instance_latency_history_columns\">\n        instance_id,app_id,ip,port,event,execute_date,execution_cost\n    </sql>\n\n    <insert id=\"batchSave\">\n        insert ignore into instance_latency_history(<include refid=\"instance_latency_history_columns\"/>)\n        values\n        <foreach collection=\"list\" item=\"instanceLatencyHistory\" separator=\",\">\n            (#{instanceLatencyHistory.instanceId},\n            #{instanceLatencyHistory.appId},\n            #{instanceLatencyHistory.ip},\n            #{instanceLatencyHistory.port},\n            #{instanceLatencyHistory.event},\n            #{instanceLatencyHistory.executeDate},\n            #{instanceLatencyHistory.executionCost})\n        </foreach>\n    </insert>\n\n    <select id=\"getAppLatencyStats\" resultType=\"hashmap\">\n        select app_id,count(1)count,\n        FROM_UNIXTIME(UNIX_TIMESTAMP(execute_date), '%Y%m%d%H%i') execute_date_min,\n        UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(execute_date), '%Y-%m-%d %H:%i')) timestamp,\n        event\n        from instance_latency_history\n        where app_id=#{appId} and execute_date between #{startTime} and #{endTime}\n        group by event,execute_date_min\n        order by execute_date_min asc\n    </select>\n\n    <select id=\"getAppLatencyStatsGroupByInstance\" resultType=\"hashmap\">\n        select concat(ip,\":\",port) as host_port,instance_id,count(*) as count\n        from instance_latency_history\n        where app_id=#{appId}\n        and execute_date between #{startTime} and #{endTime}\n        group by host_port\n    </select>\n\n    <select id=\"getAppLatencyStatsCount\" resultType=\"int\">\n        select count(0) as count\n        from instance_latency_history\n        where app_id=#{appId}\n        and execute_date between #{startTime} and #{endTime}\n    </select>\n\n    <select id=\"getAppLatencyInfo\" resultType=\"hashmap\">\n        select concat(ip,\":\",port) as host_port,UNIX_TIMESTAMP(execute_date) timestamp,\n        <include refid=\"instance_latency_history_columns\"/>\n        from instance_latency_history\n        where app_id=#{appId}\n        <choose>\n            <when test=\"event != null and event != ''\">\n                and event=#{event}\n            </when>\n        </choose>\n        and execute_date between #{startTime} and #{endTime}\n    </select>\n\n\n    <select id=\"getAppLatencyCountStat\" resultType=\"AppClientStatisticGather\">\n        select app_id, count(1) as latency_count, DATE_FORMAT(execute_date,'%Y-%m-%d') gather_time\n        from instance_latency_history\n        where execute_date between #{startTime} and #{endTime}\n        group by app_id\n    </select>\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceReshardProcessDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.InstanceReshardProcessDao\">\r\n\r\n    <sql id=\"instance_reshard_process_column\">\r\n\t    \tapp_id,audit_id,source_instance_id,target_instance_id,start_slot,end_slot,\r\n\t    \tmigrating_slot,is_pipeline,finish_slot_num,status,start_time,end_time,create_time,update_time\r\n    </sql>\r\n    \r\n    <insert id=\"save\" parameterType=\"InstanceReshardProcess\"  keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n\t\tinsert into instance_reshard_process(<include refid=\"instance_reshard_process_column\" />)\r\n\t\tvalues(#{appId},#{auditId},#{sourceInstanceId},#{targetInstanceId},#{startSlot},#{endSlot},\r\n\t\t#{migratingSlot},#{isPipeline},#{finishSlotNum},#{status},#{startTime},#{endTime},#{createTime},#{updateTime})\r\n\t</insert>\r\n    \r\n    <select id=\"getByAuditId\" resultType=\"InstanceReshardProcess\">\r\n        select id,<include refid=\"instance_reshard_process_column\"/> from instance_reshard_process where audit_id = #{auditId}\r\n    </select>\r\n    \r\n    <select id=\"get\" resultType=\"InstanceReshardProcess\">\r\n        select id,<include refid=\"instance_reshard_process_column\"/> from instance_reshard_process where id = #{id}\r\n    </select>\r\n\t\r\n    <update id=\"updateStatus\">\r\n    \t\tupdate instance_reshard_process set status = #{status} where id = #{id}\r\n    </update>\r\n    \r\n    <update id=\"updateEndTime\">\r\n    \t\tupdate instance_reshard_process set end_time = #{endTime} where id = #{id}\r\n    </update>\r\n    \r\n    <update id=\"increaseFinishSlotNum\">\r\n    \t\tupdate instance_reshard_process set finish_slot_num = finish_slot_num + 1 where id = #{id}\r\n    </update>\r\n    \r\n    <update id=\"updateMigratingSlot\">\r\n    \t\tupdate instance_reshard_process set migrating_slot = #{migratingSlot} where id = #{id}\r\n    </update>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceSlowLogDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.InstanceSlowLogDao\">\n\n    <sql id=\"instance_slow_log_columns\">\n        instance_id,app_id,ip,port,slow_log_id,cost_time,command,execute_time,create_time\n    </sql>\n\n    <insert id=\"batchSave\">\n        insert ignore into instance_slow_log(<include refid=\"instance_slow_log_columns\"/>)\n        values\n        <foreach collection=\"list\" item=\"instanceSlowLog\" separator=\",\">\n            (#{instanceSlowLog.instanceId},#{instanceSlowLog.appId},#{instanceSlowLog.ip},#{instanceSlowLog.port},#{instanceSlowLog.slowLogId},#{instanceSlowLog.costTime},#{instanceSlowLog.command},#{instanceSlowLog.executeTime},#{instanceSlowLog.createTime})\n        </foreach>\n    </insert>\n\n    <select id=\"getByAppId\" resultType=\"InstanceSlowLog\">\n        select id,\n        <include refid=\"instance_slow_log_columns\"/>\n        from instance_slow_log where app_id = #{appId}\n    </select>\n\n    <select id=\"getByInstanceExecuteTime\" resultType=\"InstanceSlowLog\">\n        select id,\n        <include refid=\"instance_slow_log_columns\"/>\n        from instance_slow_log\n        where instance_id = #{instanceId} and execute_time=#{executeDate}\n        limit 10\n    </select>\n\n\n    <select id=\"search\" resultType=\"InstanceSlowLog\">\n        select id,\n        <include refid=\"instance_slow_log_columns\"/>\n        from instance_slow_log where app_id = #{appId} and create_time between DATE_FORMAT(#{startDate}, '%Y-%m-%d') and\n        DATE_FORMAT(#{endDate}, '%Y-%m-%d') order by execute_time desc\n    </select>\n\n    <select id=\"getInstanceSlowLogCountMapByAppId\" resultType=\"hashmap\">\n        select concat(ip,\":\",port) as hostPort,count(*) as count\n        from instance_slow_log where app_id = #{appId} and create_time between DATE_FORMAT(#{startDate}, '%Y-%m-%d') and DATE_FORMAT(#{endDate}, '%Y-%m-%d') group by hostPort order by count desc\n    </select>\n\n    <select id=\"getAppSlowLogCount\" resultType=\"int\">\n        select count(*) as count\n        from instance_slow_log where app_id = #{appId} and execute_time between DATE_FORMAT(#{startDate}, '%Y-%m-%d') and DATE_FORMAT(#{endDate}, '%Y-%m-%d')\n    </select>\n\n    <select id=\"getAppSlowLogCountStat\" resultType=\"AppClientStatisticGather\">\n        select app_id, count(*) as slowLogCount, DATE_FORMAT(execute_time,'%Y-%m-%d') gather_time\n        from instance_slow_log\n        where execute_time between #{startTime} and #{endTime}\n        group by app_id\n    </select>\n\n\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/InstanceStatsDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.InstanceStatsDao\">\r\n\r\n    <sql id=\"instance_statistics_columns\">\r\n        inst_id,app_id,host_id,ip,port,role,max_memory,used_memory,curr_items,curr_connections,misses,hits,modify_time,mem_fragmentation_ratio,aof_delayed_fsync,used_disk\r\n    </sql>\r\n\r\n    <insert id=\"updateInstanceStats\" parameterType=\"InstanceStats\">\r\n        INSERT INTO instance_statistics\r\n          (<include refid=\"instance_statistics_columns\"/>)\r\n        VALUES (#{instId},#{appId},#{hostId},#{ip},#{port},#{role},#{maxMemory},#{usedMemory},#{currItems},#{currConnections},#{misses},#{hits},\r\n                  #{modifyTime},#{memFragmentationRatio},#{aofDelayedFsync},#{usedDisk})\r\n        ON DUPLICATE KEY UPDATE\r\n          inst_id = #{instId},\r\n          host_id = #{hostId},\r\n          role = #{role},\r\n          max_memory = #{maxMemory},\r\n          used_memory = #{usedMemory},\r\n          curr_items = #{currItems},\r\n          curr_connections = #{currConnections},\r\n          misses = #{misses},\r\n          hits = #{hits},\r\n          modify_time = #{modifyTime},\r\n          mem_fragmentation_ratio = #{memFragmentationRatio},\r\n          aof_delayed_fsync = #{aofDelayedFsync},\r\n          used_disk = #{usedDisk}\r\n    </insert>\r\n\r\n    <select id=\"getInstanceStatsByHost\" resultType=\"InstanceStats\">\r\n        select id,<include refid=\"instance_statistics_columns\"/>\r\n        from instance_statistics\r\n        where ip = #{ip} and port = #{port}\r\n    </select>\r\n\r\n    <select id=\"getInstanceStatsByIp\" resultType=\"InstanceStats\">\r\n        select id,<include refid=\"instance_statistics_columns\"/>\r\n        from instance_statistics\r\n        where ip = #{ip}\r\n    </select>\r\n\r\n    <select id=\"getInstanceStatsByInsId\" resultType=\"InstanceStats\">\r\n        select id,<include refid=\"instance_statistics_columns\"/>\r\n        from instance_statistics\r\n        where inst_id = #{id}\r\n    </select>\r\n\r\n    <select id=\"getInstanceStatsByAppId\" resultType=\"InstanceStats\">\r\n        select id,<include refid=\"instance_statistics_columns\"/>\r\n        from instance_statistics\r\n        where app_id = #{appId}\r\n    </select>\r\n\r\n    <select id=\"getInstanceStats\" resultType=\"InstanceStats\">\r\n        select id,<include refid=\"instance_statistics_columns\"/>\r\n        from instance_statistics\r\n    </select>\r\n\r\n    <select id=\"getMachineMemByIp\" resultType=\"java.util.Map\">\r\n        SELECT count(statistic.id) instanceNum, sum(statistic.max_memory) applyMem, sum(statistic.used_memory) usedMem,\r\n        sum(statistic.mem_fragmentation_ratio*statistic.used_memory) usedMemRss, sum(statistic.used_disk) usedDisk,\r\n        sum(info.mem) applyDisk\r\n        FROM instance_statistics statistic, instance_info info\r\n        WHERE statistic.inst_id = info.id\r\n        AND info.status = 1\r\n        AND statistic.ip = #{ip}\r\n    </select>\r\n\r\n    <select id=\"getTotalMem\" resultType=\"hashmap\">\r\n        SELECT SUM(max_memory)/1024/1024/1024 totalMem,sum(used_memory)/1024/1024/1024 totalUsedMem from instance_statistics;\r\n    </select>\r\n\r\n    <select id=\"getTotalAppMem\" resultType=\"hashmap\">\r\n        SELECT SUM(max_memory)/1024/1024/1024 totalAppMem,sum(used_memory)/1024/1024/1024 totalAppUsedMem from instance_statistics where inst_id in\r\n        <foreach collection=\"list\" item=\"item\" index=\"instancelist\" open=\"(\" separator=\",\" close=\")\">\r\n            #{item}\r\n        </foreach>\r\n        and role=1;\r\n    </select>\r\n\r\n    <select id=\"getMachineInstanceStatList\" resultType=\"MachineInstanceStat\">\r\n        SELECT stat.ip,sum(stat.max_memory) as max_memory,sum(stat.used_memory) as used_memory,\r\n               sum(stat.used_disk) as used_disk, sum(info.mem) as apply_disk\r\n        from instance_statistics stat,instance_info info\r\n        where stat.inst_id = info.id and info.status=1 group by stat.ip;\r\n    </select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/MachineDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.MachineDao\">\r\n    <sql id=\"machine_info_columns\">\r\n        ssh_user,ssh_passwd,ip,room,mem,cpu,`virtual`,real_ip,service_time,fault_count,warn,available,type,groupId,extra_desc,collect,version_install,use_type,k8s_type,rack,disk,dis_type\r\n    </sql>\r\n\r\n    <sql id=\"machine_men_stat_info_columns\">\r\n        ip,mem,cpu,use_type,room,real_ip,rack\r\n    </sql>\r\n\r\n    <sql id=\"machine_info_fields\">\r\n        #{sshUser},#{sshPasswd},#{ip},#{room},#{mem},#{cpu},#{virtual},#{realIp},#{serviceTime},\r\n        #{faultCount},#{warn},#{available},#{type},#{groupId},#{extraDesc},#{collect},#{versionInstall},#{useType},#{k8sType},#{rack},#{disk},#{disType}\r\n    </sql>\r\n\r\n    <!--返回所有可用的机器资源-->\r\n    <select id=\"getAllMachines\" resultType=\"MachineInfo\">\r\n        select\r\n        id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info\r\n        where available = 1\r\n    </select>\r\n\r\n    <!--通过ip查找机器-->\r\n    <select id=\"getMachineInfoByIp\" resultType=\"MachineInfo\">\r\n        select\r\n          id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info\r\n        where ip = #{ip};\r\n    </select>\r\n\r\n    <!--通过ip查找机器-->\r\n    <select id=\"getMachineInfoByLikeIp\" resultType=\"MachineInfo\">\r\n        select\r\n          id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info where available = 1\r\n        <choose>\r\n            <when test=\"ipLike != '' and ipLike != null\">\r\n                and instr(ip, #{ipLike}) > 0\r\n            </when>\r\n        </choose>\r\n    </select>\r\n\r\n    <!--通过ip查找机器-->\r\n    <select id=\"getMachineInfoByCondition\" resultType=\"MachineInfo\">\r\n        select\r\n        id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info where available = 1\r\n        <choose>\r\n            <when test=\"ipLike != '' and ipLike != null\">\r\n                and instr(ip, #{ipLike}) > 0\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"useType != '-1'.toString() and useType != null\">\r\n                and use_type = #{useType}\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"type != '-1'.toString() and type != null\">\r\n                and type = #{type}\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"versionStr != '' and versionStr != null\">\r\n                and instr(version_install, #{versionStr}) > 0\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"k8sType != '-1'.toString() and k8sType != null\">\r\n                and k8s_type = #{k8sType}\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"realip != '' and realip != null\">\r\n                and real_ip = #{realip}\r\n            </when>\r\n        </choose>\r\n    </select>\r\n\r\n    <!--通过ip模糊查找机器-->\r\n    <select id=\"getMachineListByCondition\" resultType=\"MachineInfo\">\r\n        select\r\n        id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info where available = 1\r\n        <choose>\r\n            <when test=\"ipLike != '' and ipLike != null\">\r\n                and instr(ip, #{ipLike}) > 0\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"realIpLike != '' and realIpLike != null\">\r\n                and instr(real_ip, #{realIpLike}) > 0\r\n            </when>\r\n        </choose>\r\n    </select>\r\n\r\n    <!--通过ip real_ip查找机器-->\r\n    <select id=\"existk8sMachine\" resultType=\"MachineInfo\">\r\n        select\r\n        id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info where ip = #{ip} and k8s_type=1\r\n    </select>\r\n\r\n    <select id=\"getK8sMachineList\" resultType=\"MachineInfo\">\r\n        select\r\n        id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info where available = 1 and k8s_type=1\r\n    </select>\r\n\r\n    <!--通过room和useType查找机器-->\r\n    <select id=\"getMachineMemStatInfoByCondition\" resultType=\"MachineMemStatInfo\">\r\n        select\r\n        id, <include refid=\"machine_men_stat_info_columns\"/>\r\n        from machine_info where available = 1\r\n        <choose>\r\n            <when test=\"room != null\">\r\n                and instr(room, #{room}) > 0\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"useType != '' and useType != null\">\r\n                and use_type = #{useType}\r\n            </when>\r\n        </choose>\r\n    </select>\r\n\r\n    <select id=\"getMachineMemStatInfoByIp\" resultType=\"MachineMemStatInfo\">\r\n        select\r\n        id, <include refid=\"machine_men_stat_info_columns\"/>\r\n        from machine_info where available = 1\r\n        where ip = #{ip};\r\n    </select>\r\n\r\n    <select id=\"getMachineMemStatInfoByIpList\" resultType=\"MachineMemStatInfo\" parameterType=\"java.util.List\">\r\n        select\r\n        id, <include refid=\"machine_men_stat_info_columns\"/>\r\n        from machine_info where available = 1\r\n        and ip IN\r\n        <foreach collection=\"list\" index=\"index\" item=\"item\" open=\"(\" separator=\",\" close=\")\">\r\n        #{item}\r\n        </foreach>\r\n    </select>\r\n\r\n    <select id=\"getMachineInfoByIpList\" resultType=\"MachineInfo\" parameterType=\"java.util.List\">\r\n        select\r\n        id, <include refid=\"machine_info_columns\"/>\r\n        from machine_info where available = 1\r\n        and ip IN\r\n        <foreach collection=\"list\" index=\"index\" item=\"item\" open=\"(\" separator=\",\" close=\")\">\r\n            #{item}\r\n        </foreach>\r\n    </select>\r\n\r\n    <!--保存机器信息-->\r\n    <insert id=\"saveMachineInfo\" parameterType=\"MachineInfo\">\r\n        insert into machine_info\r\n          (<include refid=\"machine_info_columns\"/>)\r\n        values\r\n          (<include refid=\"machine_info_fields\"/>)\r\n        on duplicate key\r\n        update ssh_user=#{sshUser},ssh_passwd=#{sshPasswd},room=#{room},mem=#{mem},cpu=#{cpu},disk=#{disk},\r\n        `virtual`=#{virtual},real_ip=#{realIp},service_time=#{serviceTime},fault_count=#{faultCount},\r\n        warn=#{warn},available=#{available},type=#{type},groupId=#{groupId},extra_desc=#{extraDesc},collect=#{collect},version_install=#{versionInstall},\r\n        use_type=#{useType},k8s_type=#{k8sType},rack=#{rack},dis_type=#{disType}\r\n    </insert>\r\n\r\n    <!--根据ip删除一台机器的信息:置无效-->\r\n    <update id=\"removeMachineInfoByIp\" parameterType=\"string\">\r\n        update machine_info set available = 0 WHERE ip = #{ip}\r\n    </update>\r\n\r\n    <!--根据ip更新物理机ip-->\r\n    <update id=\"updateMachineRealIpByIp\" parameterType=\"string\">\r\n        update machine_info set real_ip = #{real_ip} WHERE ip = #{ip}\r\n    </update>\r\n\r\n    <!--通过type查找机器-->\r\n    <select id=\"getMachineInfoByType\" resultType=\"MachineInfo\">\r\n        select\r\n          id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info where type = #{type} and available = 1\r\n    </select>\r\n\r\n    <!-- 更新机器类型 -->\r\n    <update id=\"updateMachineType\">\r\n    \tupdate machine_info set type = #{type} where id = #{id}\r\n    </update>\r\n\r\n    <select id=\"getTotalMachineMem\" resultType=\"hashmap\">\r\n        SELECT sum(memory_total)/1024/1024/1024 machineTotal,sum(memory_free)/1024/1024/1024 machineFreeTotal from  machine_statistics ;\r\n    </select>\r\n\r\n    <select id=\"getRoomStat\" resultType=\"hashmap\">\r\n        SELECT room as name,count(room) as num from machine_info where available=1 group by room;\r\n    </select>\r\n\r\n    <update id=\"updateMachineAllocate\">\r\n    \tupdate machine_info set is_allocating = #{status} where ip = #{ip}\r\n    </update>\r\n\r\n    <select id=\"getMachineInfoAndUsedInfo\" resultType=\"com.sohu.cache.entity.MachineMemStatInfo\">\r\n       select\r\n            mi.id, mi.ip, mi.room, mi.mem, mi.cpu, mi.real_ip, mi.available, mi.use_type, mi.k8s_type, mi.rack, mi.dis_type, mi.disk, mi.type,\r\n            count(is2.id) instanceNum,\r\n            sum(is2.max_memory) applyMem,\r\n            sum(is2.used_memory) usedMem,\r\n            sum(is2.mem_fragmentation_ratio*is2.used_memory) usedMemRss,\r\n            sum(is2.used_disk) usedDisk\r\n        from machine_info mi\r\n        left join\r\n        (\r\n            select\r\n                ist.id, ist.ip, ist.max_memory, ist.used_memory, ist.mem_fragmentation_ratio, ist.used_disk\r\n            from instance_statistics ist\r\n            left join instance_info ii on ist.inst_id = ii.id\r\n            where ist.modify_time > current_date() and ii.status != 2\r\n        ) is2 on mi.ip = is2.ip\r\n        where mi.available = 1\r\n            <if test=\"room != null and room != ''\">\r\n                and instr(room, #{room}) > 0\r\n            </if>\r\n            <if test=\"type != null and type != ''\">\r\n                and type = #{type}\r\n            </if>\r\n            <if test=\"type != null and type == 0\">\r\n                and type = #{type}\r\n            </if>\r\n            <if test=\"useType != null and useType != ''\">\r\n                and use_type = #{useType}\r\n            </if>\r\n            <if test=\"useType != null and useType == 0\">\r\n                and use_type = #{useType}\r\n            </if>\r\n            <if test=\"disType != null and disType != ''\">\r\n                and dis_type = #{disType}\r\n            </if>\r\n            <if test=\"disType != null and disType == 0\">\r\n                and dis_type = #{disType}\r\n            </if>\r\n            <if test=\"ip != null and ip != ''\">\r\n                and mi.ip = #{ip}\r\n            </if>\r\n        group by mi.ip\r\n    </select>\r\n\r\n    <!--通过realIp查找机器-->\r\n    <select id=\"getMachineListByRealIp\" resultType=\"MachineInfo\">\r\n        select\r\n            id, modify_time, <include refid=\"machine_info_columns\"/>\r\n        from machine_info\r\n        where available = 1\r\n            and real_ip = #{realIp}\r\n    </select>\r\n\r\n</mapper>\r\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/MachineRelationDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.sohu.cache.dao.MachineRelationDao\">\n\n    <sql id=\"columns\">\n        ip,real_ip,update_time,status,extra_desc\n    </sql>\n\n    <insert id=\"saveOrUpdateMachineRelation\" parameterType=\"MachineRelation\" keyProperty=\"id\" useGeneratedKeys=\"true\">\n        insert into machine_relation(<include refid=\"columns\"/>)\n        values(#{ip},#{realIp},#{updateTime},#{status},#{extraDesc})\n        on duplicate key\n        update ip=#{ip},real_ip=#{realIp},update_time=#{updateTime},status=#{status},extra_desc=#{extraDesc}\n    </insert>\n\n    <update id=\"updateMachineRelation\">\n        update machine_relation set taskid = #{taskid}, is_sync= #{is_sync} where id = #{id}\n    </update>\n\n    <select id=\"getRelationList\" resultType=\"MachineRelation\">\n        select * from machine_relation\n        <choose>\n            <when test=\"ip != '' and ip != null\">\n                where ip = #{ip}\n            </when>\n        </choose>\n        ORDER BY ip,update_time DESC\n    </select>\n\n    <select id=\"getOnlinePodList\" resultType=\"MachineRelation\">\n        select * from machine_relation where 1 = 1 and status = #{status}\n        <choose>\n            <when test=\"ip != '' and ip != null\">\n                and ip = #{ip}\n            </when>\n        </choose>\n        ORDER BY ip,update_time DESC\n    </select>\n\n    <select id=\"getUnSyncRelationList\" resultType=\"MachineRelation\">\n        select * from machine_relation where ip = #{ip} and real_ip= #{real_ip} and is_sync in (0,-1)\n    </select>\n\n    <update id=\"updateMachineSyncStatus\">\n        update machine_relation set is_sync=#{is_sync},sync_time=now() where id = #{id}\n    </update>\n    \n    <select id=\"getMachineSyncStatus\" resultType=\"MachineRelation\">\n        select * from machine_relation where ip = #{ip} and real_ip= #{real_ip} and is_sync = #{is_sync}\n    </select>\n    \n</mapper>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/MachineStatsDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.MachineStatsDao\">\r\n\r\n    <sql id=\"machine_statistics_columns\">\r\n        ip,host_id,cpu_usage,`load`,traffic,max_memory,machine_memory,instance_count,memory_usage_ratio,memory_free,memory_total,modify_time,disk_available,disk_total,disk_usage_ratio\r\n    </sql>\r\n\r\n    <sql id=\"instance_info_columns\">\r\n        instance.id, instance.parent_id, instance.app_id, instance.host_id, instance.ip, instance.PORT, instance.STATUS, instance.mem, instance.conn, instance.cmd, instance.type\r\n    </sql>\r\n\r\n    <!--保存机器当前最新的统计信息-->\r\n    <insert id=\"mergeMachineStats\" parameterType=\"MachineStats\">\r\n        insert into machine_statistics\r\n          (<include refid=\"machine_statistics_columns\"/>)\r\n        values\r\n          (#{ip},#{hostId},#{cpuUsage},#{load},#{traffic},#{maxMemory},#{machineMemory},#{instanceCount},#{memoryUsageRatio},#{memoryFree},#{memoryTotal},#{modifyTime},#{diskAvailable},#{diskTotal},#{diskUsageRatio})\r\n        on duplicate key update\r\n           host_id = #{hostId},\r\n           cpu_usage = #{cpuUsage},\r\n           `load` = #{load},\r\n           traffic = #{traffic},\r\n           memory_usage_ratio = #{memoryUsageRatio},\r\n           memory_free = #{memoryFree},\r\n           memory_total = #{memoryTotal},\r\n           max_memory = #{maxMemory},\r\n           instance_count = #{instanceCount},\r\n           machine_memory = #{machineMemory},\r\n           modify_time = #{modifyTime},\r\n           disk_available = #{diskAvailable},\r\n           disk_total = #{diskTotal},\r\n           disk_usage_ratio = #{diskUsageRatio}\r\n    </insert>\r\n\r\n    <!--查询机器下的所有实例的静态信息-->\r\n    <select id=\"getInstInfoOfMachine\" resultType=\"InstanceInfo\">\r\n        SELECT\r\n           <include refid=\"instance_info_columns\"/>\r\n        FROM\r\n            machine_info AS machine,\r\n            instance_info AS instance\r\n        WHERE\r\n            machine.id = instance.host_id\r\n        AND machine.ip = instance.ip\r\n        AND machine.available = 1\r\n        AND machine.ip = #{ip}\r\n    </select>\r\n\r\n    <!--查询机器下的所有实例的最新状态信息-->\r\n    <select id=\"getInstStatOfMachine\" resultType=\"InstanceStats\" parameterType=\"long\">\r\n        SELECT istat.*\r\n        FROM machine_statistics as mstat, instance_statistics AS istat\r\n        WHERE mstat.host_id = #{hostId}\r\n        AND mstat.host_id = istat.host_id\r\n        ORDER BY istat.max_memory DESC;\r\n    </select>\r\n\r\n    <select id=\"getMachineStats\" resultType=\"MachineStats\">\r\n        select\r\n          id,<include refid=\"machine_statistics_columns\"/>\r\n        from machine_statistics\r\n        <choose>\r\n            <when test=\"ipLike != '' and ipLike != null\">\r\n                where instr(ip, #{ipLike}) > 0\r\n            </when>\r\n        </choose>\r\n    </select>\r\n    \r\n    <select id=\"getAllMachineStats\" resultType=\"MachineStats\">\r\n        select\r\n          id,<include refid=\"machine_statistics_columns\"/>\r\n        from machine_statistics\r\n    </select>\r\n\r\n    <!--根据机器的ip查询机器的最新状态信息-->\r\n    <select id=\"getMachineStatsByIp\" resultType=\"MachineStats\">\r\n        select\r\n          id,<include refid=\"machine_statistics_columns\"/>\r\n        from machine_statistics\r\n        where ip = #{ip};\r\n    </select>\r\n\r\n    <!--根据机器的hostId查询机器的最新状态信息-->\r\n    <select id=\"getMachineStatsByHostId\" resultType=\"MachineStats\" parameterType=\"long\">\r\n        select\r\n          id,<include refid=\"machine_statistics_columns\"/>\r\n        from machine_statistics\r\n        where host_id = #{hostId};\r\n    </select>\r\n    \r\n    <!-- 删除机器统计信息 -->\r\n    <delete id=\"deleteMachineStatsByIp\" parameterType=\"string\">\r\n    \tdelete from machine_statistics where ip = #{ip};\r\n    </delete>\r\n\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/QuartzDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n\r\n<mapper namespace=\"com.sohu.cache.dao.QuartzDao\">\r\n    <sql id=\"quartz_triggers_columns\">\r\n        t.SCHED_NAME,t.TRIGGER_NAME,t.TRIGGER_GROUP,t.JOB_NAME,t.JOB_GROUP,t.DESCRIPTION,t.NEXT_FIRE_TIME,t.PREV_FIRE_TIME,t.PRIORITY,\r\n        t.TRIGGER_STATE,t.TRIGGER_TYPE,t.START_TIME,t.END_TIME,t.CALENDAR_NAME,t.MISFIRE_INSTR\r\n    </sql>\r\n\r\n    <!--查询job分组下的所有trigger-->\r\n    <select id=\"getTriggersByJobGroup\" resultType=\"TriggerInfo\" parameterType=\"String\">\r\n        SELECT <include refid=\"quartz_triggers_columns\"/>\r\n        FROM QRTZ_TRIGGERS t\r\n        WHERE JOB_GROUP = #{jobGroup};\r\n    </select>\r\n\r\n    <!--返回所有trigger-->\r\n    <select id=\"getAllTriggers\" resultType=\"TriggerInfo\">\r\n        SELECT <include refid=\"quartz_triggers_columns\"/>, ifnull(ct.CRON_EXPRESSION,'') cron\r\n        FROM QRTZ_TRIGGERS t\r\n        LEFT JOIN QRTZ_CRON_TRIGGERS ct\r\n        on (t.SCHED_NAME = ct.SCHED_NAME and t.TRIGGER_NAME = ct.TRIGGER_NAME and t.TRIGGER_GROUP=ct.TRIGGER_GROUP)\r\n    </select>\r\n\r\n    <!--查询trigger：模糊匹配trigger name或trigger group-->\r\n    <select id=\"searchTriggerByNameOrGroup\" resultType=\"TriggerInfo\" parameterType=\"String\">\r\n        SELECT <include refid=\"quartz_triggers_columns\"/>, ifnull(ct.CRON_EXPRESSION,'') cron\r\n        FROM QRTZ_TRIGGERS t\r\n        LEFT JOIN QRTZ_CRON_TRIGGERS ct\r\n        on (t.SCHED_NAME = ct.SCHED_NAME and t.TRIGGER_NAME = ct.TRIGGER_NAME and t.TRIGGER_GROUP=ct.TRIGGER_GROUP)\r\n        WHERE t.TRIGGER_NAME REGEXP #{queryString}\r\n        OR t.TRIGGER_GROUP REGEXP #{queryString};\r\n    </select>\r\n\r\n    <select id=\"getTriggerStateCount\" resultType=\"int\">\r\n        select count(*) from QRTZ_TRIGGERS where TRIGGER_STATE = #{triggerState}\r\n    </select>\r\n\r\n    <select id=\"getMisFireTriggerCount\" resultType=\"int\">\r\n        select count(*) from QRTZ_TRIGGERS where NEXT_FIRE_TIME &lt; unix_timestamp(now())*1000\r\n    </select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/ResourceDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.ResourceDao\">\r\n\t<sql id=\"resource_columns\">\r\n\t\tid,name,intro,type,lastmodify,dir,url,ispush,status,username,task_id,order_num\r\n\t</sql>\r\n\r\n\t<sql id=\"resource_fields\">\r\n\t\t#{id}, #{name}, #{intro}, #{type}, #{lastmodify}, #{dir}, #{url}, #{ispush}, #{status}, #{username},#{taskId},#{orderNum}\r\n\t</sql>\r\n\r\n\t<select id=\"getResourceList\" resultType=\"com.sohu.cache.entity.SystemResource\">\r\n\t\tselect\r\n\t\t<include refid=\"resource_columns\" />\r\n\t\tfrom system_resource where status = 1\r\n\t\t<choose>\r\n            <when test=\"resourceType > 0\">\r\n                and type = #{resourceType}\r\n            </when>\r\n        </choose>\r\n\t\t<choose>\r\n\t\t\t<when test=\"resourceType > 0\">\r\n\t\t\t\torder by order_num desc,dir asc,name desc\r\n\t\t\t</when>\r\n\t\t\t<otherwise>\r\n\t\t\t\torder by type asc,dir asc,name desc\r\n\t\t\t</otherwise>\r\n\t\t</choose>\r\n\r\n\t</select>\r\n\r\n\t<select id=\"getResourceListByName\" resultType=\"com.sohu.cache.entity.SystemResource\">\r\n\t\tselect\r\n\t\t<include refid=\"resource_columns\" />\r\n\t\tfrom system_resource where status = 1 and name like CONCAT(\"%\",#{searchName},\"%\")  and type = #{resourceType}\r\n\t\torder by type asc,dir asc,name desc\r\n\t</select>\r\n\r\n\t<select id=\"getResourceById\" resultType=\"com.sohu.cache.entity.SystemResource\">\r\n\t\tselect\r\n\t\t<include refid=\"resource_columns\" />\r\n\t\tfrom system_resource where id = #{resourceId}\r\n\t</select>\r\n\r\n\t<select id=\"getResourceByName\" resultType=\"com.sohu.cache.entity.SystemResource\">\r\n\t\tselect\r\n\t\t<include refid=\"resource_columns\" />\r\n\t\tfrom system_resource where name = #{resourceName} and status=1\r\n\t</select>\r\n\r\n\t<insert id=\"save\" parameterType=\"SystemResource\" keyColumn=\"id\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n\t\tinsert into system_resource (<include refid=\"resource_columns\"/>)\r\n\t\tvalue(<include refid=\"resource_fields\"/>);\r\n\t</insert>\r\n\r\n\t<update id=\"update\" parameterType=\"SystemResource\">\r\n\t\tupdate system_resource\r\n\t\tset name=#{name}, intro=#{intro}, type=#{type}, url=#{url},dir=#{dir}, ispush=#{ispush}, lastmodify=now(),\r\n\t\tstatus=#{status}, username=#{username}, task_id=#{taskId}, order_num=#{orderNum}\r\n\t\twhere id=#{id}\r\n\t</update>\r\n\r\n\t<select id=\"getAppUseRedis\" resultType=\"java.util.HashMap\">\r\n\t\tSELECT version_id,count(version_id) as num from app_desc where `status`=2 GROUP BY version_id\r\n\t</select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/ServerStatusDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n\r\n<mapper namespace=\"com.sohu.cache.dao.ServerStatusDao\">\r\n\t\r\n\t<!-- 获取服务器信息 -->\r\n    <select id=\"queryServerInfo\" resultType=\"ServerInfo\">\r\n    \tselect ip,host,nmon,cpus,cpu_model cpuModel,dist,kernel,ulimit\r\n        from server where ip=#{ip}\r\n    </select>\r\n\r\n\t<select id=\"getAllServerInfo\" resultType=\"ServerInfo\">\r\n\t\tSELECT ser.ip,ser.dist from machine_info machine, server ser\r\n\t\twhere machine.ip = ser.ip and machine.available=1\r\n\t</select>\r\n    \r\n    <!-- 保存服务器发行版信息 -->\r\n    <insert id=\"saveServerInfo\">\r\n    \tinsert ignore into server(ip,dist) values (#{ip},#{dist})\r\n    </insert>\r\n    \r\n    <!-- 删除服务器信息 -->\r\n    <select id=\"deleteServerInfo\">\r\n    \tdelete from server where ip=#{ip}\r\n    </select>\r\n    \r\n    <!-- 保存/更新服务器信息 -->\r\n    <insert id=\"saveAndUpdateServerInfo\">\r\n    \tinsert into server (ip,host,nmon,cpus,cpu_model,kernel,ulimit) \r\n\t\tvalues (#{server.ip},#{server.host},#{server.nmon},#{server.cpus},#{server.cpuModel},#{server.kernel},#{server.ulimit})\r\n\t\ton duplicate key update host=values(host), nmon=values(nmon), cpus=values(cpus),\r\n\t\tcpu_model=values(cpu_model), kernel=values(kernel), ulimit=values(ulimit)\r\n    </insert>\r\n    \r\n    <!-- 获取服务器状态 -->\r\n    <select id=\"queryServerOverview\" resultType=\"ServerStatus\">\r\n    \tselect ip,cdate,ctime,cuser,csys,cwio,cload1,cload5,cload15,\r\n\t\t\t   mtotal,mfree,mcache,mbuffer,mswap,mswap_free mswapFree,\r\n\t\t\t   nin,nout,tuse,torphan,twait,\r\n\t\t\t   dread,dwrite,diops,dbusy\r\n\t\tfrom server_stat\r\n\t\twhere ip=#{ip} and cdate=#{cdate}\r\n    </select>\r\n    \r\n    <!-- 获取服务器cpu状态 -->\r\n    <select id=\"queryServerCpu\" resultType=\"ServerStatus\">\r\n    \tselect ctime, c_ext cExt\r\n\t\tfrom server_stat\r\n\t\twhere ip=#{ip} and cdate=#{cdate}\r\n    </select>\r\n    \r\n    <!-- 获取服务器net状态 -->\r\n    <select id=\"queryServerNet\" resultType=\"ServerStatus\">\r\n    \tselect ctime, nin_ext ninExt,nout_ext noutExt\r\n\t\tfrom server_stat\r\n\t\twhere ip=#{ip} and cdate=#{cdate}\r\n    </select>\r\n    \r\n    <!-- 获取服务器disk状态 -->\r\n    <select id=\"queryServerDisk\" resultType=\"ServerStatus\">\r\n    \tselect ctime, d_ext dExt,dspace\r\n\t\tfrom server_stat\r\n\t\twhere ip=#{ip} and cdate=#{cdate}\r\n    </select>\r\n    \r\n    <!-- 获取服务器状态 -->\r\n    <select id=\"queryServerStat\" resultType=\"ServerStatus\">\r\n    \tselect ip,cdate,ctime,cuser,csys,cwio,c_ext cExt,cload1,cload5,cload15,\r\n\t\t\t   mtotal,mfree,mcache,mbuffer,mswap,mswap_free mswapFree,\r\n\t\t\t   nin,nout,nin_ext ninExt,nout_ext noutExt,tuse,torphan,twait,\r\n\t\t\t   dread,dwrite,diops,dbusy,d_ext dExt,dspace\"\r\n\t\tfrom server_stat\r\n\t\twhere ip=#{ip} and cdate=#{cdate}\r\n    </select>\r\n    \r\n    <!-- 保存服务器状态-->\r\n    <insert id=\"saveServerStat\">\r\n    \tinsert ignore into server_stat(ip,cdate,ctime,cuser,csys,cwio,c_ext,\r\n    \t\t   cload1,cload5,cload15,\r\n    \t\t   mtotal,mfree,mcache,mbuffer,mswap,mswap_free,\r\n    \t\t   nin,nout,nin_ext,nout_ext,\r\n    \t\t   tuse,torphan,twait,\r\n    \t\t   dread,dwrite,diops,dbusy,d_ext,dspace)\r\n     \tvalues(#{server.ip},#{server.collectTime},#{server.time},\r\n     \t\t   #{server.cpu.user},#{server.cpu.sys},#{server.cpu.wait},#{server.cpu.ext},\r\n     \t\t   #{server.load.load1},#{server.load.load5},#{server.load.load15},\r\n     \t\t   #{server.mem.total},#{server.mem.totalFree},#{server.mem.cache},\r\n     \t\t   #{server.mem.buffer},#{server.mem.swap},#{server.mem.swapFree},\r\n     \t\t   #{server.net.nin},#{server.net.nout},#{server.net.ninDetail},#{server.net.noutDetail},\r\n     \t\t   #{server.connection.established},#{server.connection.orphan},#{server.connection.timeWait},\r\n     \t\t   #{server.disk.read},#{server.disk.write},#{server.disk.iops},#{server.disk.busy},\r\n     \t\t   #{server.disk.ext},#{server.disk.space})\r\n    </insert>\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/StandardStatsDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.StandardStatsDao\">\r\n\r\n    <insert id=\"mergeStandardStats\" parameterType=\"StandardStats\">\r\n        replace into standard_statistics(collect_time,ip,port,db_type,info_json,diff_json,cluster_info_json)\r\n        values (#{collectTime},#{ip},#{port},#{dbType},#{infoJson},#{diffJson},#{clusterInfoJson})\r\n    </insert>\r\n\r\n    <!-- StandardStats保留10分钟 -->\r\n    <insert id=\"mergeInstanceMinuteStats\" parameterType=\"StandardStats\">\r\n        replace into instance_minute_stats(collect_time,ip,port,db_type,json)\r\n        values (#{collectTime},#{ip},#{port},#{dbType},#{diffJson})\r\n    </insert>\r\n\r\n    <!-- instance_minute_stats默认保留7天,清理逻辑见:CleanupDayDimensionalityJob -->\r\n    <select id=\"getDiffJsonList\" resultType=\"StandardStats\">\r\n        select collect_time , json as diff_json\r\n        from instance_minute_stats\r\n        where  ip=#{ip} and port=#{port} and db_type=#{dbType}\r\n        and collect_time between #{beginTime} and #{endTime}\r\n    </select>\r\n\r\n    <select id=\"getStandardStats\" resultType=\"StandardStats\">\r\n        select id,collect_time,ip,port,db_type,info_json,diff_json,cluster_info_json,created_time\r\n        from standard_statistics\r\n        where  ip=#{ip} and port=#{port} and db_type=#{dbType}\r\n          and collect_time = #{collectTime}\r\n    </select>\r\n\r\n    <delete id=\"deleteStandardStatsLessCreatedTime\">\r\n        delete\r\n        from standard_statistics\r\n        where <![CDATA[ created_time <=  #{createdTime}\r\n        ]]>\r\n    </delete>\r\n\r\n    <select id=\"getStandardStatsByCreateTime\" resultType=\"StandardStats\">\r\n        select id,collect_time,ip,port,db_type,info_json,diff_json,cluster_info_json,created_time\r\n        from standard_statistics\r\n        where created_time between #{beginTime} and #{endTime} and db_type=#{dbType}\r\n    </select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/TaskQueueDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.TaskQueueDao\">\r\n\r\n    <sql id=\"task_queue_columns\">\r\n    \tapp_id, class_name, param, init_param, status, parent_task_id, create_time, update_time,\r\n    \tstart_time, end_time, priority, error_code, error_msg, task_note, important_info\r\n    </sql>\r\n    \r\n    <sql id=\"task_queue_fields\">\r\n    \t#{appId}, #{className}, #{param}, #{initParam}, #{status}, #{parentTaskId}, #{createTime}, #{updateTime},\r\n    \t#{startTime}, #{endTime}, #{priority}, #{errorCode}, #{errorMsg}, #{taskNote}, #{importantInfo}\r\n    </sql>\r\n\r\n    <insert id=\"save\" parameterType=\"TaskQueue\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n        insert into task_queue\r\n        \t(<include refid=\"task_queue_columns\"/>)\r\n        values\r\n        \t(<include refid=\"task_queue_fields\"/>)\r\n    </insert>\r\n    \r\n    <select id=\"getById\" resultType=\"TaskQueue\">\r\n        select id,<include refid=\"task_queue_columns\"/>, execute_ip_port\r\n        from task_queue\r\n        where id = #{taskId}\r\n    </select>\r\n    \r\n    <select id=\"getChildTaskQueueList\" resultType=\"TaskQueue\">\r\n    \tselect id,<include refid=\"task_queue_columns\"/>, execute_ip_port\r\n        from task_queue\r\n        where parent_task_id = #{taskId}\r\n    </select>\r\n    \r\n    <update id=\"updateStatus\">\r\n    \tupdate task_queue set status = #{status} where id = #{taskId}\r\n    </update>\r\n    \r\n    <select id=\"getTaskQueueListByStatus\" resultType=\"TaskQueue\">\r\n        select id,<include refid=\"task_queue_columns\"/>, execute_ip_port\r\n        from task_queue where status = #{status} order by id\r\n    </select>\r\n    \r\n    <select id=\"getByAppAndClass\" resultType=\"TaskQueue\">\r\n        select id,<include refid=\"task_queue_columns\"/>, execute_ip_port\r\n        from task_queue where app_id = #{appId} and class_name=#{className}\r\n    </select>\r\n    \r\n    \r\n    <update id=\"updateParam\">\r\n    \tupdate task_queue set param = #{param} where id = #{taskId}\r\n    </update>\r\n\r\n\t<update id=\"updateStartTime\">\r\n    \tupdate task_queue set start_time = #{startTime} where id = #{taskId}\r\n    </update>\r\n    \r\n    <update id=\"updateEndTime\">\r\n    \tupdate task_queue set end_time = #{endTime} where id = #{taskId}\r\n    </update>\r\n    \r\n    <update id=\"updateExecuteIpPort\">\r\n    \tupdate task_queue set execute_ip_port = #{executeIpPort} where id = #{taskId}\r\n    </update>\r\n    \r\n    <select id=\"getTaskQueueCount\" resultType=\"int\">\r\n        select count(*) from task_queue where 1=1\r\n        <choose>\r\n            <when test=\"className != null and className != ''\">\r\n                 and class_name = #{className}\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"appId != null and appId > 0\">\r\n                 and app_id = #{appId}\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"status >= 0\">\r\n                 and status = #{status}\r\n            </when>\r\n        </choose>\r\n    </select>\r\n    \r\n    <select id=\"getTaskQueueList\" resultType=\"TaskQueue\">\r\n        select id,<include refid=\"task_queue_columns\"/>, execute_ip_port\r\n        from task_queue where 1=1\r\n        <choose>\r\n            <when test=\"className != null and className != ''\">\r\n                 and class_name = #{className}\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"appId != null and appId > 0\">\r\n                 and app_id = #{appId}\r\n            </when>\r\n        </choose>\r\n        <choose>\r\n            <when test=\"status >= 0\">\r\n                 and status = #{status}\r\n            </when>\r\n        </choose>\r\n        order by id desc\r\n        <choose>\r\n        \t<when test=\"page != null\">\r\n        \t\t<choose>\r\n\t\t        \t<when test=\"page.totalCount > page.pageSize\">\r\n            \t\t\tlimit #{page.start},#{page.pageSize}\r\n\t\t        \t</when>\r\n\t\t        \t<otherwise>\r\n\t\t        \t    limit #{page.totalCount}\r\n\t\t        \t</otherwise>\r\n\t\t        </choose>\r\n        \t</when>\r\n        </choose>\r\n    </select>\r\n    \r\n    <select id=\"getStatusCount\" resultType=\"int\">\r\n        select count(*) from task_queue where status = #{status}\r\n    </select>\r\n    \r\n    \r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/TaskStepFlowDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.TaskStepFlowDao\">\r\n\r\n    <sql id=\"task_step_flow_columns\">\r\n    \ttask_id, class_name, step_name, order_no, status,\r\n    \tlog, start_time, end_time, create_time, update_time, child_task_id\r\n    </sql>\r\n\r\n    <sql id=\"task_step_flow_fields\">\r\n    \t#{taskId}, #{className}, #{stepName}, #{orderNo}, #{status},\r\n    \t#{log}, #{startTime}, #{endTime}, #{createTime}, #{updateTime}, #{childTaskId}\r\n    </sql>\r\n\r\n    <insert id=\"save\" parameterType=\"TaskStepFlow\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n        insert into task_step_flow\r\n        (<include refid=\"task_step_flow_columns\"/>)\r\n        values\r\n        (<include refid=\"task_step_flow_fields\"/>)\r\n    </insert>\r\n\r\n    <select id=\"getByTaskClassStep\" resultType=\"TaskStepFlow\">\r\n        select id,<include refid=\"task_step_flow_columns\"/>, execute_ip_port\r\n        from task_step_flow\r\n        where task_id = #{taskId} and class_name = #{className} and step_name = #{stepName}\r\n    </select>\r\n\r\n    <update id=\"updateStatus\">\r\n    \tupdate task_step_flow set status = #{status} where id = #{id}\r\n    </update>\r\n\r\n    <update id=\"updateLog\">\r\n    \tupdate task_step_flow set log = #{log} where id = #{id}\r\n    </update>\r\n\r\n    <update id=\"updateStartTime\">\r\n    \tupdate task_step_flow set start_time = #{startTime} where id = #{id}\r\n    </update>\r\n\r\n    <update id=\"updateEndTime\">\r\n    \tupdate task_step_flow set end_Time = #{endTime} where id = #{id}\r\n    </update>\r\n\r\n    <update id=\"updateChildTaskId\">\r\n    \tupdate task_step_flow set child_task_id = #{childTaskId} where id = #{id}\r\n    </update>\r\n\r\n    <update id=\"updateExecuteIpPort\">\r\n    \tupdate task_step_flow set execute_ip_port = #{executeIpPort} where id = #{taskId}\r\n    </update>\r\n\r\n    <select id=\"getTaskStepFlowList\" resultType=\"TaskStepFlow\">\r\n        select id,<include refid=\"task_step_flow_columns\"/>, execute_ip_port\r\n        from task_step_flow\r\n        where task_id = #{taskId} order by order_no\r\n    </select>\r\n\r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mapper/TaskStepMetaDao.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"com.sohu.cache.dao.TaskStepMetaDao\">\r\n\r\n    <sql id=\"task_step_meta_columns\">\r\n    \tclass_name, step_name, step_desc, ops_device,\r\n    \ttimeout, order_no, create_time, update_time\r\n    </sql>\r\n    \r\n    <sql id=\"task_step_meta_fields\">\r\n    \t#{className}, #{stepName}, #{stepDesc}, #{opsDevice},\r\n    \t#{timeout}, #{orderNo}, #{createTime}, #{updateTime}\r\n    </sql>\r\n\r\n    <insert id=\"save\" parameterType=\"TaskStepMeta\" keyProperty=\"id\" useGeneratedKeys=\"true\">\r\n        insert into task_step_meta\r\n        \t(<include refid=\"task_step_meta_columns\"/>)\r\n        values\r\n        \t(<include refid=\"task_step_meta_fields\"/>)\r\n    </insert>\r\n    \r\n    <select id=\"getTaskStepMetaList\" resultType=\"TaskStepMeta\">\r\n        select id,<include refid=\"task_step_meta_columns\"/>\r\n        from task_step_meta\r\n        where class_name = #{className}\r\n    </select>\r\n    \r\n</mapper>"
  },
  {
    "path": "cachecloud-web/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\r\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\r\n\r\n<configuration>\r\n\r\n    <!--数据库的字段名到pojo类的属性名的自动映射-->\r\n    <settings>\r\n        <!-- 使用jdbc的getGeneratedKeys自动获取主键主键策略:默认false -->\r\n        <setting name=\"useGeneratedKeys\" value=\"true\"/>\r\n        <!-- 使用列别名代替列名,默认true -->\r\n        <setting name=\"useColumnLabel\" value=\"true\"/>\r\n        <!-- 开启自动驼峰-下划线命名规则,默认false: Table(create_time) -> Entity(createTime) -->\r\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\r\n        <!-- 开启debug调试日志 -->\r\n        <!--<setting name=\"logImpl\" value=\"STDOUT_LOGGING\"/>-->\r\n    </settings>\r\n\r\n</configuration>"
  },
  {
    "path": "cachecloud-web/src/main/resources/spring/spring-client-report.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:util=\"http://www.springframework.org/schema/util\"\r\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\r\n                     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\"\r\n       default-autowire=\"byName\">\r\n\r\n\r\n    <!-- 客户端异常 -->\r\n    <bean id=\"clientReportExceptionService\"\r\n          class=\"com.sohu.cache.client.service.impl.ClientReportExceptionServiceImpl\"/>\r\n\r\n    <!-- 客户端版本收集 -->\r\n    <bean id=\"clientVersionService\" class=\"com.sohu.cache.client.service.impl.ClientVersionServiceImpl\"/>\r\n\r\n    <!-- 应用下节点和客户端对应关系 -->\r\n    <bean id=\"appInstanceClientRelationService\"\r\n          class=\"com.sohu.cache.client.service.impl.AppInstanceClientRelationServiceImpl\"/>\r\n\r\n    <bean id=\"clientReportInstanceService\" class=\"com.sohu.cache.client.service.impl.ClientReportInstanceServiceImpl\"/>\r\n\r\n    <!-- 客户端应用服务类 -->\r\n    <bean id=\"appClientService\" class=\"com.sohu.cache.client.service.impl.AppClientServiceImpl\"/>\r\n\r\n</beans>"
  },
  {
    "path": "cachecloud-web/src/main/resources/spring/spring-data.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\r\n        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\" default-autowire=\"byName\">\r\n\r\n\r\n    <bean id=\"jdbcTemplate\" class=\"org.springframework.jdbc.core.JdbcTemplate\">\r\n        <property name=\"dataSource\" ref=\"cacheCloudDB\"/>\r\n    </bean>\r\n\r\n    <bean name=\"transactionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\r\n        <property name=\"dataSource\" ref=\"cacheCloudDB\"/>\r\n    </bean>\r\n\r\n</beans>"
  },
  {
    "path": "cachecloud-web/src/main/resources/spring/spring-inspector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\r\n        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\" default-autowire=\"byName\">\r\n\r\n    <bean id=\"hostInspectHandler\" class=\"com.sohu.cache.inspect.impl.HostInspectHandler\" init-method=\"init\">\r\n        <property name=\"inspectorList\">\r\n            <list>\r\n                <bean class=\"com.sohu.cache.inspect.impl.InstanceRunInspector\"/>\r\n                <bean class=\"com.sohu.cache.inspect.impl.RedisIsolationPersistenceInspector\"/>\r\n            </list>\r\n        </property>\r\n    </bean>\r\n\r\n    <bean id=\"appInspectHandler\" class=\"com.sohu.cache.inspect.impl.AppInspectHandler\" init-method=\"init\">\r\n        <property name=\"inspectorList\">\r\n        \t<list>\r\n        \t\t<bean class=\"com.sohu.cache.inspect.impl.AppMemInspector\"/>\r\n            \t<bean class=\"com.sohu.cache.inspect.impl.AppClientConnInspector\"/>\r\n            \t<bean class=\"com.sohu.cache.inspect.impl.AppHitPrecentInspector\"/>\r\n        \t</list>\r\n        </property>\r\n    </bean>\r\n\r\n</beans>"
  },
  {
    "path": "cachecloud-web/src/main/resources/spring/spring-mvc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n        http://www.springframework.org/schema/context\n        http://www.springframework.org/schema/context/spring-context-3.0.xsd\n        http://www.springframework.org/schema/mvc\n        http://www.springframework.org/schema/mvc/spring-mvc.xsd\" default-autowire=\"byName\">\n\n    <context:component-scan base-package=\"com.sohu.cache.client.heartbeat,com.sohu.cache.web.controller\"/>\n\n    <mvc:resources mapping=\"/assets/**\" location=\"classpath:assets/\"/>\n    <mvc:resources mapping=\"/img/**\" location=\"classpath:static/img/\"/>\n\n    <mvc:interceptors>\n        <!-- 前台验证 -->\n        <mvc:interceptor>\n            <mvc:mapping path=\"/admin/app/*\"/>\n            <mvc:mapping path=\"/admin/instance/*\"/>\n            <mvc:mapping path=\"/client/show/**\"/>\n            <mvc:mapping path=\"/server/*\"/>\n            <bean class=\"com.sohu.cache.interceptor.FrontUserLoginInterceptor\"/>\n        </mvc:interceptor>\n        <!-- 后台管理员验证 -->\n        <mvc:interceptor>\n            <mvc:mapping path=\"/manage/app/*\"/>\n            <mvc:mapping path=\"/manage/app/**\"/>\n            <mvc:mapping path=\"/manage/import/**\"/>\n            <mvc:mapping path=\"/manage/task/**\"/>\n            <mvc:mapping path=\"/manage/instance/*\"/>\n            <mvc:mapping path=\"/manage/user/*\"/>\n            <mvc:mapping path=\"/manage/quartz/*\"/>\n            <mvc:mapping path=\"/manage/total/*\"/>\n            <mvc:mapping path=\"/manage/machine/*\"/>\n            <mvc:mapping path=\"/manage/notice/*\"/>\n            <mvc:mapping path=\"/import/app/*\"/>\n            <mvc:mapping path=\"/manage/config/*\"/>\n            <mvc:mapping path=\"/data/migrate/**\"/>\n            <mvc:mapping path=\"/manage/redisConfig/*\"/>\n            <mvc:mapping path=\"/manage/instanceAlert/*\"/>\n            <mvc:mapping path=\"/manage/redis/upgrade/*\"/>\n            <mvc:mapping path=\"/manage/app/migrate/*\"/>\n            <bean class=\"com.sohu.cache.interceptor.ManageUserLoginInterceptor\"/>\n        </mvc:interceptor>\n        <!-- 应用和实例权限验证 -->\n        <mvc:interceptor>\n            <mvc:mapping path=\"/admin/app/*\"/>\n            <mvc:mapping path=\"/admin/instance/*\"/>\n            <bean class=\"com.sohu.cache.interceptor.AppAndInstanceAuthorityInterceptor\"/>\n        </mvc:interceptor>\n    </mvc:interceptors>\n\n    <bean class=\"org.springframework.web.servlet.view.ContentNegotiatingViewResolver\">\n        <property name=\"order\" value=\"1\"/>\n        <property name=\"contentNegotiationManager\">\n            <bean class=\"org.springframework.web.accept.ContentNegotiationManager\">\n                <constructor-arg>\n                    <bean class=\"org.springframework.web.accept.PathExtensionContentNegotiationStrategy\">\n                        <constructor-arg>\n                            <map>\n                                <entry key=\"json\" value=\"application/json\"/>\n                            </map>\n                        </constructor-arg>\n                    </bean>\n                </constructor-arg>\n            </bean>\n        </property>\n        <property name=\"defaultViews\">\n            <list>\n                <bean class=\"org.springframework.web.servlet.view.json.MappingJackson2JsonView\"/>\n            </list>\n        </property>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/spring/spring-mybatis.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n                     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n                     http://www.springframework.org/schema/context\n                     http://www.springframework.org/schema/context/spring-context.xsd\">\n\n    <bean id=\"mysqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\" primary=\"true\">\n        <property name=\"dataSource\" ref=\"cacheCloudDB\"/>\n        <!-- mybatis配置文件的位置 -->\n        <property name=\"configLocation\" value=\"classpath:mybatis-config.xml\"/>\n        <!-- domain的包路径，类似MyBatis的typeAliases配置 -->\n        <property name=\"vfs\" value=\"org.mybatis.spring.boot.autoconfigure.SpringBootVFS\"/>\n        <property name=\"typeAliasesPackage\" value=\"com.sohu.cache.entity,com.sohu.cache.task.entity\"/>\n        <!-- mapper配置文件的路径，类似MyBatis的mappers配置 -->\n        <property name=\"mapperLocations\" value=\"classpath:mapper/*.xml\"/>\n    </bean>\n\n    <!-- 扫描接口类的包路径 -->\n    <bean class=\"org.mybatis.spring.mapper.MapperScannerConfigurer\">\n        <property name=\"sqlSessionFactoryBeanName\" value=\"mysqlSessionFactory\"/>\n        <property name=\"basePackage\" value=\"com.sohu.cache.dao\"/>\n    </bean>\n\n</beans>"
  },
  {
    "path": "cachecloud-web/src/main/resources/spring/spring-quartz.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n                     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\"\n       default-autowire=\"byName\">\n\n    <bean id=\"systemConfigRefreshTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"systemConfigRefreshTrigger\"/>\n        <property name=\"group\" value=\"systemConfigRefreshGroup\"/>\n        <property name=\"jobDetail\" ref=\"systemConfigRefreshJobDetail\"/>\n        <property name=\"cronExpression\" value=\"0/30 * * ? * *\"/>\n        <property name=\"misfireInstruction\" value=\"2\"/>\n    </bean>\n    <bean id=\"dispatcherBrevityScheduleTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"dispatcherBrevityScheduleTrigger\"/>\n        <property name=\"group\" value=\"dispatcherBrevityScheduleGroup\"/>\n        <property name=\"jobDetail\" ref=\"dispatcherBrevityScheduleJobDetail\"/>\n        <property name=\"cronExpression\" value=\"1 0/1 * ? * *\"/>\n    </bean>\n\n    <bean id=\"systemConfigRefreshJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"systemConfigRefreshJobDetail\"></property>\n        <property name=\"group\" value=\"systemConfigRefreshGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.SystemConfigRefreshJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n    <bean id=\"dispatcherBrevityScheduleJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"dispatcherBrevityScheduleJobDetail\"></property>\n        <property name=\"group\" value=\"dispatcherBrevityScheduleGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.brevity.DispatcherBrevityScheduleJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <!-- inJvm模式 -->\n    <bean id=\"jvmQuartzScheduler\" name=\"jvmQuartzScheduler\" lazy-init=\"false\"\n          class=\"org.springframework.scheduling.quartz.SchedulerFactoryBean\">\n        <property name=\"taskExecutor\" ref=\"jvmQuartzThreadPool\"/>\n        <property name=\"quartzProperties\">\n            <props>\n                <prop key=\"org.quartz.scheduler.instanceName\">cachecloudJvmScheduler</prop>\n                <prop key=\"org.quartz.jobStore.class\">org.quartz.simpl.RAMJobStore</prop>\n                <prop key=\"org.quartz.scheduler.instanceId\">AUTO</prop>\n                <prop key=\"org.quartz.scheduler.jmx.export\">true</prop>\n                <prop key=\"org.quartz.plugin.shutdownHook.class\">org.quartz.plugins.management.ShutdownHookPlugin</prop>\n                <prop key=\"org.quartz.plugin.shutdownHook.cleanShutdown\">true</prop>\n                <prop key=\"org.quartz.plugin.triggHistory.class\">org.quartz.plugins.history.LoggingJobHistoryPlugin</prop>\n            </props>\n        </property>\n        <property name=\"startupDelay\" value=\"10\"/>\n        <property name=\"applicationContextSchedulerContextKey\" value=\"applicationContext\"/>\n        <property name=\"overwriteExistingJobs\" value=\"true\"/>\n        <property name=\"autoStartup\" value=\"true\"/>\n\n        <property name=\"triggers\">\n            <array>\n                <ref bean=\"systemConfigRefreshTrigger\"/>\n                <ref bean=\"dispatcherBrevityScheduleTrigger\"/>\n            </array>\n        </property>\n\n        <property name=\"jobDetails\">\n            <array>\n                <ref bean=\"systemConfigRefreshJobDetail\"/>\n                <ref bean=\"dispatcherBrevityScheduleJobDetail\"/>\n            </array>\n        </property>\n    </bean>\n\n    <!-- quartz线程池 -->\n    <bean id=\"jvmQuartzThreadPool\" class=\"org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor\">\n        <!-- 核心线程数  -->\n        <property name=\"corePoolSize\" value=\"30\"/>\n        <!-- 最大线程数 -->\n        <property name=\"maxPoolSize\" value=\"30\"/>\n        <!-- 队列最大长度 >=mainExecutor.maxSize -->\n        <property name=\"queueCapacity\" value=\"200\"/>\n        <!-- 线程池维护线程所允许的空闲时间 -->\n        <property name=\"keepAliveSeconds\" value=\"300\"/>\n        <!-- 拒绝任务策略:被拒绝后直接在调用者线程中运行当前被放弃任务 -->\n        <property name=\"rejectedExecutionHandler\">\n            <bean class=\"java.util.concurrent.ThreadPoolExecutor$DiscardPolicy\"/>\n        </property>\n    </bean>\n\n    <!-- quartz线程池 -->\n    <bean id=\"quartzThreadPool\" class=\"org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor\">\n        <!-- 核心线程数  -->\n        <property name=\"corePoolSize\" value=\"200\"/>\n        <!-- 最大线程数 -->\n        <property name=\"maxPoolSize\" value=\"200\"/>\n        <!-- 队列最大长度 >=mainExecutor.maxSize -->\n        <property name=\"queueCapacity\" value=\"1024\"/>\n        <!-- 线程池维护线程所允许的空闲时间 -->\n        <property name=\"keepAliveSeconds\" value=\"300\"/>\n        <!-- 拒绝任务策略:被拒绝后直接在调用者线程中运行当前被放弃任务 -->\n        <property name=\"rejectedExecutionHandler\">\n            <bean class=\"java.util.concurrent.ThreadPoolExecutor$DiscardPolicy\"/>\n        </property>\n    </bean>\n\n    <!-- 分布式QuartzScheduler -->\n    <bean id=\"clusterScheduler\" name=\"clusterScheduler\" lazy-init=\"false\"\n          class=\"org.springframework.scheduling.quartz.SchedulerFactoryBean\">\n        <property name=\"dataSource\" ref=\"cacheCloudDB\"></property>\n        <property name=\"taskExecutor\" ref=\"quartzThreadPool\"/>\n        <property name=\"quartzProperties\">\n            <props>\n                <prop key=\"org.quartz.scheduler.instanceName\">CacheCloudScheduler</prop>\n                <prop key=\"org.quartz.scheduler.instanceId\">AUTO</prop>\n                <prop key=\"org.quartz.jobStore.class\">org.springframework.scheduling.quartz.LocalDataSourceJobStore</prop>\n                <prop key=\"org.quartz.scheduler.batchTriggerAcquisitionMaxCount\">200</prop>\n                <prop key=\"org.quartz.jobStore.driverDelegateClass\">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>\n                <prop key=\"org.quartz.jobStore.acquireTriggersWithinLock\">true</prop>\n                <!-- 表名前缀 -->\n                <prop key=\"org.quartz.jobStore.tablePrefix\">QRTZ_</prop>\n                <prop key=\"org.quartz.jobStore.isClustered\">true</prop>\n                <prop key=\"org.quartz.jobStore.clusterCheckinInterval\">15000</prop>\n                <prop key=\"org.quartz.jobStore.maxMisfiresToHandleAtATime\">20</prop>\n                <prop key=\"org.quartz.jobStore.misfireThreshold\">60000</prop>\n                <!-- 打开JMX 配置 -->\n                <prop key=\"org.quartz.scheduler.jmx.export\">true</prop>\n                <prop key=\"org.quartz.plugin.shutdownHook.class\">org.quartz.plugins.management.ShutdownHookPlugin</prop>\n                <prop key=\"org.quartz.plugin.shutdownHook.cleanShutdown\">true</prop>\n                <prop key=\"org.terracotta.quartz.skipUpdateCheck\">true</prop>\n                <prop key=\"org.quartz.plugin.triggHistory.class\">org.quartz.plugins.history.LoggingJobHistoryPlugin</prop>\n            </props>\n        </property>\n        <property name=\"schedulerName\" value=\"CacheCloudScheduler\"/>\n        <property name=\"applicationContextSchedulerContextKey\" value=\"applicationContext\"/>\n        <property name=\"overwriteExistingJobs\" value=\"true\"/>\n        <property name=\"waitForJobsToCompleteOnShutdown\" value=\"false\"/>\n        <property name=\"startupDelay\" value=\"10\"/>\n        <property name=\"autoStartup\" value=\"true\"/>\n        <property name=\"triggers\">\n            <array>\n                <ref bean=\"hostInspectorTrigger\"/>\n                <ref bean=\"appInspectorTrigger\"/>\n                <ref bean=\"appDailyTrigger\"/>\n                <ref bean=\"appCapacityMonitorJobTrigger\"/>\n                <ref bean=\"expAppsDailyTrigger\"/>\n                <ref bean=\"instanceAlertValueTrigger\"/>\n\n                <ref bean=\"taskExecuteJobTrigger\"/>\n                <ref bean=\"maintainBrevitySchedulerTrigger\"/>\n\n                <ref bean=\"cleanupDayDimensionalityTrigger\"/>\n                <ref bean=\"cleanupMinuteDimensionalityTrigger\"/>\n                <ref bean=\"gatherAppClientStatisticsTrigger\"/>\n                <ref bean=\"gatherAppClientStatisticsServerCmdCountTrigger\"/>\n                <ref bean=\"reviseAppClientStatisGatherTrigger\"/>\n                <ref bean=\"cleanupDayAppClientStatTrigger\"/>\n                <ref bean=\"instanceStateTrigger\"/>\n                <ref bean=\"appPersistenceCheckTrigger\"/>\n            </array>\n        </property>\n        <property name=\"jobDetails\">\n            <array>\n                <ref bean=\"inspectorJobDetail\"/>\n                <ref bean=\"appDailyJobDetail\"/>\n                <ref bean=\"appCapacityMonitorJobDetail\"/>\n                <ref bean=\"expAppsDailyJobDetail\"/>\n                <ref bean=\"instanceAlertValueJobDetail\"/>\n\n                <ref bean=\"taskExecuteJobDetail\"/>\n                <ref bean=\"maintainBrevitySchedulerJobDetail\"/>\n\n                <ref bean=\"cleanupDayDimensionalityJobDetail\"/>\n                <ref bean=\"cleanupMinuteDimensionalityJobDetail\"/>\n                <ref bean=\"gatherAppClientStatisticsJobDetail\"/>\n                <ref bean=\"gatherAppClientStatisticsServerCmdCountJobDetail\"/>\n                <ref bean=\"reviseAppClientStatisGatherJobDetail\"/>\n                <ref bean=\"cleanupDayAppClientStatJobDetail\"/>\n                <ref bean=\"instanceStateJobDetail\"/>\n                <ref bean=\"appPersistenceCheckJobDetail\"/>\n            </array>\n        </property>\n\n    </bean>\n\n    <bean id=\"maintainBrevitySchedulerJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"maintainBrevitySchedulerJob\"></property>\n        <property name=\"group\" value=\"maintainBrevitySchedulerGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.brevity.BrevitySchedulerJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"maintainBrevitySchedulerTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"maintainBrevitySchedulerTrigger\"/>\n        <property name=\"group\" value=\"maintainBrevitySchedulerGroup\"/>\n        <property name=\"jobDetail\" ref=\"maintainBrevitySchedulerJobDetail\"/>\n        <property name=\"cronExpression\" value=\"30 0/1 * ? * *\"/>\n    </bean>\n\n    <bean id=\"cleanupMinuteDimensionalityJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"cleanUpMinuteStatisticsJobDetail\"></property>\n        <property name=\"group\" value=\"cleanUpMinuteStatisticsGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.CleanupMinuteDimensionalityJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"cleanupMinuteDimensionalityTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"cleanupMinuteDimensionalityTrigger\"/>\n        <property name=\"group\" value=\"cleanUpMinuteStatisticsGroup\"/>\n        <property name=\"jobDetail\" ref=\"cleanupMinuteDimensionalityJobDetail\"/>\n        <!-- 每10分钟执行一次 -->\n        <property name=\"cronExpression\" value=\"0 0/10 * ? * *\"/>\n    </bean>\n\n    <bean id=\"gatherAppClientStatisticsJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"gatherAppClientStatisticsJobDetail\"></property>\n        <property name=\"group\" value=\"gatherAppClientStatisticsGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.GatherAppClientStatisticsJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"gatherAppClientStatisticsTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"gatherAppClientStatisticsTrigger\"/>\n        <property name=\"group\" value=\"gatherAppClientStatisticsGroup\"/>\n        <property name=\"jobDetail\" ref=\"gatherAppClientStatisticsJobDetail\"/>\n        <!-- 每5分钟执行一次 -->\n        <property name=\"cronExpression\" value=\"0 0/5 * ? * *\"/>\n    </bean>\n\n    <bean id=\"gatherAppClientStatisticsServerCmdCountJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"gatherAppClientStatisticsServerCmdCountJobDetail\"></property>\n        <property name=\"group\" value=\"gatherAppClientStatisticsServerCmdCountGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.GatherAppClientStatisticsServerCmdCountJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"gatherAppClientStatisticsServerCmdCountTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"gatherAppClientStatisticsServerCmdCountTrigger\"/>\n        <property name=\"group\" value=\"gatherAppClientStatisticsServerCmdCountGroup\"/>\n        <property name=\"jobDetail\" ref=\"gatherAppClientStatisticsServerCmdCountJobDetail\"/>\n        <!-- 每1小时的第5分钟执行一次 -->\n        <property name=\"cronExpression\" value=\"0 5 0/1 ? * *\"/>\n    </bean>\n\n    <bean id=\"reviseAppClientStatisGatherJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"reviseAppClientStatisGatherJobDetail\"></property>\n        <property name=\"group\" value=\"reviseAppClientStatisGatherGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.ReviseAppClientStatisGatherJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"reviseAppClientStatisGatherTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"reviseAppClientStatisGatherTrigger\"/>\n        <property name=\"group\" value=\"reviseAppClientStatisGatherGroup\"/>\n        <property name=\"jobDetail\" ref=\"reviseAppClientStatisGatherJobDetail\"/>\n        <!-- 每天1点执行一次 -->\n        <property name=\"cronExpression\" value=\"0 0 1 * * ?\"/>\n    </bean>\n\n    <bean id=\"cleanupDayAppClientStatJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"cleanupDayAppClientStatJobDetail\"></property>\n        <property name=\"group\" value=\"cleanupDayAppClientStatGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.CleanupDayAppClientStatJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"cleanupDayAppClientStatTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"cleanupDayAppClientStatTrigger\"/>\n        <property name=\"group\" value=\"cleanupDayAppClientStatGroup\"/>\n        <property name=\"jobDetail\" ref=\"cleanupDayAppClientStatJobDetail\"/>\n        <!-- 每天10点执行一次 -->\n        <property name=\"cronExpression\" value=\"0 0 10 * * ?\"/>\n    </bean>\n\n    <bean id=\"cleanupDayDimensionalityJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"cleanUpDayStatisticsJobDetail\"></property>\n        <property name=\"group\" value=\"cleanUpDayStatisticsGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.CleanupDayDimensionalityJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"cleanupDayDimensionalityTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"cleanupDayDimensionalityTrigger\"/>\n        <property name=\"group\" value=\"cleanUpDayStatisticsGroup\"/>\n        <property name=\"jobDetail\" ref=\"cleanupDayDimensionalityJobDetail\"/>\n        <!-- 每小时执行一次 -->\n        <property name=\"cronExpression\" value=\"0 0 0/1 ? * *\"/>\n    </bean>\n\n    <bean id=\"inspectorJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"inspectorJob\"></property>\n        <property name=\"group\" value=\"inspector\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.inspect.InspectorJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"hostInspectorTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"hostInspectorTrigger\"/>\n        <property name=\"group\" value=\"inspector\"/>\n        <property name=\"jobDetail\" ref=\"inspectorJobDetail\"/>\n        <!-- 5分钟执行一次 -->\n        <property name=\"cronExpression\" value=\"0 0/5 * ? * *\"/>\n        <property name=\"jobDataAsMap\">\n            <map>\n                <entry key=\"inspectorType\" value=\"host\"/>\n            </map>\n        </property>\n        <property name=\"misfireInstruction\" value=\"2\"/>\n    </bean>\n\n    <bean id=\"appInspectorTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"appInspectorTrigger\"/>\n        <property name=\"group\" value=\"inspector\"/>\n        <property name=\"jobDetail\" ref=\"inspectorJobDetail\"/>\n        <!-- 20分钟执行一次 -->\n        <property name=\"cronExpression\" value=\"0 0/10 * ? * *\"/>\n        <property name=\"jobDataAsMap\">\n            <map>\n                <entry key=\"inspectorType\" value=\"app\"/>\n            </map>\n        </property>\n        <property name=\"misfireInstruction\" value=\"2\"/>\n    </bean>\n\n    <bean id=\"taskExecuteJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"taskExecuteJob\"></property>\n        <property name=\"group\" value=\"taskExecuteGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.TaskExecuteJob\"/>\n        <property name=\"requestsRecovery\" value=\"true\"/>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"taskExecuteJobTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"taskExecuteTrigger\"/>\n        <property name=\"group\" value=\"taskExecuteGroup\"/>\n        <property name=\"jobDetail\" ref=\"taskExecuteJobDetail\"/>\n        <property name=\"cronExpression\" value=\"0/10 * * ? * *\"/>\n    </bean>\n\n    <bean id=\"appDailyJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"appDailyJob\"></property>\n        <property name=\"group\" value=\"appDaily\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.AppDailyJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"appDailyTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"appDailyTrigger\"/>\n        <property name=\"group\" value=\"appDaily\"/>\n        <property name=\"jobDetail\" ref=\"appDailyJobDetail\"/>\n        <property name=\"cronExpression\" value=\"0 0 10 * * ?\"/>\n    </bean>\n\n    <bean id=\"appCapacityMonitorJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"appCapacityMonitorJob\"></property>\n        <property name=\"group\" value=\"appCapacityMonitor\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.AppCapacityMonitorJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"appCapacityMonitorJobTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"appCapacityMonitorJob\"/>\n        <property name=\"group\" value=\"appCapacityMonitor\"/>\n        <property name=\"jobDetail\" ref=\"appCapacityMonitorJobDetail\"/>\n        <property name=\"cronExpression\" value=\"0 4 0 * * ?\"/>\n    </bean>\n\n    <bean id=\"expAppsDailyJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"expAppsDailyJob\"></property>\n        <property name=\"group\" value=\"expAppsDaily\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.ExpAppsDailyJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"expAppsDailyTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"expAppsDailyTrigger\"/>\n        <property name=\"group\" value=\"expAppsDaily\"/>\n        <property name=\"jobDetail\" ref=\"expAppsDailyJobDetail\"/>\n        <property name=\"cronExpression\" value=\"0 0 9 * * ?\"/>\n    </bean>\n\n    <bean id=\"instanceAlertValueJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"instanceAlertValueJob\"></property>\n        <property name=\"group\" value=\"instanceAlertValue\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.InstanceAlertValueJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"instanceAlertValueTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"instanceAlertValueTrigger\"/>\n        <property name=\"group\" value=\"instanceAlertValue\"/>\n        <property name=\"jobDetail\" ref=\"instanceAlertValueJobDetail\"/>\n        <property name=\"cronExpression\" value=\"0 0/1 * ? * *\"/>\n        <property name=\"misfireInstruction\" value=\"2\"/>\n    </bean>\n\n    <bean id=\"instanceStateJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"instanceStateJob\"></property>\n        <property name=\"group\" value=\"instanceState\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.InstanceStatJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"instanceStateTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"instanceStateTrigger\"/>\n        <property name=\"group\" value=\"instanceState\"/>\n        <property name=\"jobDetail\" ref=\"instanceStateJobDetail\"/>\n        <property name=\"cronExpression\" value=\"0 0/10 * ? * *\"/>\n    </bean>\n\n    <bean id=\"appPersistenceCheckJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">\n        <property name=\"name\" value=\"appPersistenceCheckJobDetail\"></property>\n        <property name=\"group\" value=\"appPersistenceCheckJobGroup\"></property>\n        <property name=\"jobClass\" value=\"com.sohu.cache.schedule.jobs.AppPersistenceCheckJob\"></property>\n        <property name=\"durability\" value=\"true\"/>\n    </bean>\n\n    <bean id=\"appPersistenceCheckTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerFactoryBean\">\n        <property name=\"name\" value=\"appPersistenceCheckTrigger\"/>\n        <property name=\"group\" value=\"appPersistenceCheckJobGroup\"/>\n        <property name=\"jobDetail\" ref=\"appPersistenceCheckJobDetail\"/>\n        <property name=\"cronExpression\" value=\"15 0/2 * ? * *\"/>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/spring/spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\">\n    <import resource=\"classpath:spring/spring-quartz.xml\"/>\n    <import resource=\"classpath:spring/spring-mybatis.xml\"/>\n    <import resource=\"classpath:spring/spring-data.xml\"/>\n    <import resource=\"classpath:spring/spring-mvc.xml\"/>\n\t<import resource=\"classpath:spring/spring-client-report.xml\"/>\n\t<import resource=\"classpath:spring/spring-inspector.xml\"/>\n</beans>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/client.md",
    "content": "# 客户端接入\n\n<a name=\"access\"/>\n\n## 一、<span id=\"cc1\">Java接入方法</span>\n\n### 1、<span id=\"cc12\">cachecloud-client-redis接入方式</span>\n\n**Maven坐标:**\n\n```\n<!-- cachecloud封装的jedis客户端，需要用cachecloud-client下的jedis模块打包 -->\n<dependency>\n    <groupId>redis.clients</groupId>\n    <artifactId>jedis</artifactId>\n    <version>x.y.z-RELEASE</version>\n</dependency>\n\n<!-- cachecloud客户端 -->\n<dependency>\n    <groupId>com.sohu.tv</groupId>\n    <artifactId>cachecloud-client-redis</artifactId>\n    <version>u.v.w-RELEASE</version>\n</dependency>\n\n<!-- 公司内部Nexus仓库配置 -->\n<repositories>\n    <repository>\n        <id>xx.nexus</id>\n        <url>http://xx/nexus/content/groups/public</url>\n    </repository>\n</repositories>\n\n```\n\n**Configuration:**\n\n```\n@Configuration\npublic class RedisConfiguration {\n    \n    /**\n     * Redis Cluster\n     */\n    @Bean(destroyMethod = \"close\")\n    public PipelineCluster pipelineCluster(@Value(\"${cachecloud.demo.appId}\") long appId) {\n        //默认配置\n        PipelineCluster pipelineCluster = ClientBuilder.redisCluster(appId).build();\n        return pipelineCluster;\n    }\n    \n    /**\n     * Redis Sentinel\n     */\n    @Bean(destroyMethod = \"destroy\")\n    public JedisSentinelPool jedisSentinelPool(@Value(\"${cachecloud.demo.appId}\") long appId) {\n        //默认配置\n        JedisSentinelPool jedisSentinelPool = ClientBuilder.redisSentinel(appId).build();\n        return jedisSentinelPool;\n    }\n    \n    /**\n     * Redis Standalone\n     */\n    @Bean(destroyMethod = \"destroy\")\n    public JedisPool jedisPool(@Value(\"${cachecloud.demo.appId}\") long appId) {\n        //默认配置\n        JedisPool jedisPool = ClientBuilder.redisStandalone(appId).build();\n        return jedisPool;\n    }\n}\n    \n    \n```\n**Usage:**\n```\n@Component\n@Slf4j\npublic class RedisDao {\n\n    @Autowired\n    private PipelineCluster pipelineCluster;\n\n    @Autowired\n    private JedisSentinelPool jedisSentinelPool;\n\n    @Autowired\n    private JedisPool jedisPool;\n\n    public String getFromCluster(String key) {\n        String value = pipelineCluster.get(key);\n        log.info(\"value={}\", value);\n        return value;\n    }\n\n    public String getFromSentinel(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = jedisSentinelPool.getResource();\n            String value = jedis.get(key);\n            log.info(\"value={}\", value);\n            return value;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n        return null;\n    }\n\n    public String getFromStandalone(String key) {\n        Jedis jedis = null;\n        try {\n            jedis = jedisPool.getResource();\n            String value = jedis.get(key);\n            log.info(\"value={}\", value);\n            return value;\n        } catch (Exception e) {\n            log.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null) {\n                jedis.close();\n            }\n        }\n        return null;\n    }\n}\n\n```\n\n\n### 2、<span id=\"cc3\">cachecloud-client-lettuce接入方式</span>\n\n**Maven坐标:**\n\n```\n\n<!-- lettuce客户端依赖 -->\n<dependency>\n    <groupId>com.sohu.tv</groupId>\n    <artifactId>cachecloud-client-lettuce</artifactId>\n    <version>x.y.z-RELEASE</version>\n</dependency>\n\n```\n\n**Configuration:**\n```\n@Configuration\npublic class LettuceConfiguration {\n\n    @Bean\n    public ClientResources.Builder clientResourcesBuilder() {\n        return DefaultClientResources.builder()\n                .ioThreadPoolSize(8)\n                .computationThreadPoolSize(10);\n    }\n\n    @Bean\n    public ClusterClientOptions.Builder clusterClientOptionsBuilder() {\n        SocketOptions socketOptions = SocketOptions.builder().keepAlive(true).tcpNoDelay(false)\n                .connectTimeout(Duration.ofSeconds(5)).build();\n\n        ClusterClientOptions.Builder clientOptionsBuilder = ClusterClientOptions.builder()\n                .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(5)))\n                .socketOptions(socketOptions);\n\n        return clientOptionsBuilder;\n    }\n\n    @Bean(destroyMethod = \"shutdown\")\n    public RedisClusterClient redisClusterClient(@Value(\"${cachecloud.demo.appId}\") long appId,\n                                                 @Value(\"${cachecloud.demo.password}\") String password,\n                                                 ClientResources.Builder clientResourcesBuilder,\n                                                 ClusterClientOptions.Builder clusterClientOptionsBuilder) {\n\n        RedisClusterClient redisClusterClient = LettuceClientBuilder\n                .redisCluster(appId, password)\n                .setClientResourcesBuilder(clientResourcesBuilder)\n                .setClusterClientOptionsBuilder(clusterClientOptionsBuilder)\n                .build();\n\n        return redisClusterClient;\n    }\n\n    @Bean(destroyMethod = \"close\")\n    public StatefulRedisClusterConnection<String, String> clusterConnection(RedisClusterClient redisClusterClient) {\n\n        StatefulRedisClusterConnection<String, String> connection = redisClusterClient.connect();\n        connection.setReadFrom(ReadFrom.REPLICA_PREFERRED);\n        return connection;\n    }\n\n}\n\n```\n**Usage:**\n```\n@Component\n@Slf4j\npublic class RedisDao {\n\n    @Autowired\n    private StatefulRedisClusterConnection<String, String> clusterConnection;\n\n    public String get(String key){\n        RedisAdvancedClusterCommands<String, String> clusterCommands = clusterConnection.sync();\n        String value = clusterCommands.get(key);\n        log.info(\"value={}\", value);\n        return value;\n    }\n}\n```\n\n### 3、<span id=\"cc4\">跨机房客户端cachecloud-client-crossroom-redis接入方式</span>\n\n**Maven坐标:**\n\n```\n<!-- 跨机房客户端依赖 -->\n<dependency>\n    <groupId>com.sohu.tv</groupId>\n    <artifactId>cachecloud-client-crossroom-redis</artifactId>\n    <version>x.y.z-RELEASE</version>\n</dependency>\n\n```\n\n**Configuration:**\n```\n@Configuration\npublic class RedisConfiguration {\n\n    /**\n     * 需要同一个业务申请两个应用，部署在不同机房，目前支持两个机房的跨机房使用\n     *\n     * @param majorAppId 部署在机房一的应用Id\n     * @param minorAppId 部署在机房二的应用Id\n     */\n    @Bean\n    public RedisCrossRoomClient redisCrossRoomClient(@Value(\"${cachecloud.demo.majorAppId}\") long majorAppId,\n                                                     @Value(\"${cachecloud.demo.minorAppId}\") long minorAppId) {\n        PipelineCluster majorPipelineCluster = ClientBuilder.redisCluster(majorAppId).build();\n        PipelineCluster minorPipelineCluster = ClientBuilder.redisCluster(minorAppId).build();\n        RedisCrossRoomClient redisCrossRoomClient = RedisCrossRoomClientBuilder\n                .redisCluster(majorAppId, majorPipelineCluster, minorAppId, minorPipelineCluster)\n                .build();\n        return redisCrossRoomClient;\n    }\n}\n\n```\n\n**usage:** 和cachecloud-client-redis使用方式相同\n\n## 二、<span id=\"cc2\">REST API接入方法</span>\n\n### (1) 接口地址\n\thttp://ip:port/cache/client/redis/{appType}/{appId}.json?clientVersion={clientVersion}\n\t\n\tappType=cluster|sentinel|standalone    \n\n\tappId是应用id\n\n\tclientVersion是客户端版本\n\n### (2) REST接口结果\n\t{\n\t  message: \"client is up to date, Cheers!\",\n\t  shardNum: 10,\n\t  appId: 10192,\n\t  status: 1,\n\t  shardInfo: \"10.10.xx.xx:6390,10.10.xx.xx:6382 10.10.xx.xx:6387,10.10.xx.xx:6379 10.10.xx.xx:6387,10.10.xx.xx:7382 10.10.xx.xx:6380,10.10.xx.xx:6392\"\n\t}\n\n## <span id=\"cc3\">三、python客户端接入</span>\n\n**Redis Cluster:** 这里使用支持集群模式的python客户端[redis-py-cluster](https://github.com/Grokzen/redis-py-cluster)，需要先安装 `` pip install redis-py-cluster ``\n\n```\nimport requests\nfrom rediscluster import RedisCluster\n\napp_id = 10782\nredis_type = 'cluster'\npassword = 'password'\ndomain = 'xxx.com'\n\nurl = 'http://{0}/cache/client/redis/{1}/{2}.json?clientVersion=2.0.3-RELEASE'.format(domain, redis_type, app_id)\n\nresponse = requests.get(url, timeout=1)\n\njson = response.json()\n\nshard_info_array = json['shardInfo'].split(' ')\n\nstartup_nodes = []\n\nif len(shard_info_array) > 0:\n    for shardinfo in shard_info_array:\n        seed = {'host': 'localhost', 'port': '6379'}\n        seed['host'] = shardinfo.split(':')[0]\n        seed['port'] = shardinfo.split(':')[1]\n        startup_nodes.append(seed)\n\nif len(startup_nodes) <= 0:\n    startup_nodes = [{'host': 'localhost', 'port': '6379'}]\n\n# Note: decode_responses must be set to True when used with python3\nrc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True, password=password)\n\nrc.set(\"foo\", \"bar\")\n\nprint(rc.get(\"foo\"))\n\n```\n\n**Redis Sentinel&Standalone:** 使用[redis.io](https://redis.io/clients#python)推荐的python客户端[redis-py](https://github.com/andymccurdy/redis-py)，\n需要先安装 `` pip install redis-py ``\n\nRedis Sentinel:\n\n```\nfrom redis.sentinel import Sentinel\nimport requests\n\napp_id = 10669\nredis_type = 'sentinel'\npassword = 'password'\ndomain = 'xxx.com'\n\nurl = 'http://{0}/cache/client/redis/{1}/{2}.json?clientVersion=2.0.3-RELEASE'.format(domain, redis_type, app_id)\n\nresponse = requests.get(url, timeout=1)\n\njson = response.json()\n\nsentinels_array = json['sentinels'].split(' ')\n\nsentinels = []\n\nfor node in sentinels_array:\n    sentinels.append((node.split(':')))\n\nmaster_name = json['masterName']\n\nsentinel = Sentinel(sentinels)\n\nmaster = sentinel.master_for(master_name, socket_timeout=1, password=password)\n\nmaster.set(\"key1\", \"value\")\n\nv1 = master.get('key1')\n\nprint(v1)\n\n```\nRedis Standalone:\n\n```\nimport redis\nimport requests\n\napp_id = 10744\nredis_type = 'standalone'\npassword = 'password'\ndomain = 'xxx.com'\n\nurl = 'http://{0}/cache/client/redis/{0}/{1}.json?clientVersion=2.0.3-RELEASE'.format(domain, redis_type, app_id)\n\nresponse = requests.get(url, timeout=1)\n\njson = response.json()\n\nhost_port = json['standalone'].split(':')\n\nhost = 'localhost'\nport = 6379\n\nif len(host_port) > 0:\n    host = host_port[0]\n    port = host_port[1]\n\nr = redis.Redis(host=host, port=port, password=password)\n\nv2 = r.get('key1')\n\nprint(v2)\n\n```\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/client.toc.md",
    "content": "##### 目录\n* [一、Java接入方式](#cc1)\n    - [1.cachecloud-client-redis接入方式](#cc12)\n    - [2.cachecloud-client-lettuce接入方式](#cc13)\n    - [3.跨机房客户端cachecloud-client-crossroom-redis接入方式](#cc13)\n* [二、REST API接入方式](#cc2)\n* [三、Python接入方式](#cc3)\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/config.md",
    "content": "## 系统配置说明\n\n### 一、系统配置说明\n\n在早期的CacheCloud版本中，许多配置需要对源码进行一些修复，对于一些运维人员以及对java不太熟悉的朋友来说不是很友好，为此添加了系统配置页面，系统配置页面可以动态修改系统的一些配置，例如ssh的相关信息，客户端版本信息，机器报警阀值，文档地址、maven仓库、手机和邮件报警、值班联系人配置等等，具体示意图如下：\n\n<img src=\"../../img/access/globalconfig.png\" width=\"80%\"/>\n\n### 二、详细说明\n\n#### 1.ssh相关配置：\n\nssh配置主要是打通cachecloud服务器与Redis机器交互通讯方式。\n\n> 1.1 ssh授权方式：\n\n- ssh授权方式\n\n````\ncachecloud.ssh.auth.type = (1：密码，2：public key)\n\n````\n\n> 1.2 ssh用户名密码配置:\n\n- ssh用户名\n- ssh密码\n- ssh端口\n\n```\ncachecloud.machine.username = cachecloud\ncachecloud.machine.password = cachecloud\ncachecloud.machine.ssh.port = 22\n```\n\n> 1.3 [ssh公钥配置](../operate/ssh.md):\n\n- 公钥用户名\n- 密钥路径\n\n````\ncachecloud.public.key.pem = /opt/ssh/id_rsa (cachecloud服务器上路径)\ncachecloud.public.user.name = cachecloud\n````\n\n#### 2.管理员配置:\n\n- admin用户名：cachecloud超级管理员登录名\n- admin密码： cachecloud超级管理员密码\n- 超级管理员组: 除了admin，可以添加其他成员，用于做下线的操作(超级管理员与普通管理员的区别：目前只是下线应用)\n\n#### 3.机器报警阀值：\ncachecloud会定期对机器重要指标进行检测进行报警(报警实现请参考：https://github.com/sohutv/cachecloud/wiki/3.%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E6%8E%A5%E5%85%A5%E6%96%87%E6%A1%A3#cc5-2)\n\n- cpu报警阀值 \n- 内存报警阀值\n- 负载报警阀值\n- 应用连接数报警阀值\n\n\n#### 4.系统信息：\n   \n- CacheCloud系统文档：[文档地址](../intro/index.md)\n- 值班电话：系统相关负责人联系方式\n\n[系统信息](../../img/access/baseinfo.png)\n\n#### 5.报警信息\n   \n- 邮件联系人：用逗号隔开\n- 邮件报警接口：用逗号隔开\n- 微信报警接口：用逗号隔开\n- 微信号报警：用逗号隔开\n\n\n#### 6.客户端版本管理（**修改请慎重，修改不当可能造成客户端启动失效**）\n   \n- 可用客户端版本(用逗号隔开)：良好的客户端版本列表，没有bug，如果客户端使用该版本的客户端可正常使用。\n- 警告客户端版本(用逗号隔开): 即将被放弃的客户端版本列表，如果客户端使用该版本能启动成功，但是可能存在bug，建议替换成可用版本的客户端。\n- 不可用客户端版本(用逗号隔开)： 不可用的客户端版本列表，如果客户端使用该版本将启动失败。 \n注意，最后一个可用版本的版本号，就是接入代码中的版本号，代表最新的版本，例如1.1,1.2,1.5 ,具体客户端版本参考: [CC客户端版本](../intro/releaseNote.md)\n\n#### 7. 相关工具\n\n- redis-migrate-tool安装路径；\n- redis-shake安装路径；\n- redis-full-check安装路径。\n\n    \n####\n\n### 三、注意\n\n系统配置的初始化值，需要导入2.0.sql(目录:cachecloud-web/sql)中的相关表和数据：\n\n```\nCREATE TABLE `system_config` (\n  `config_key` varchar(255) NOT NULL COMMENT '配置key',\n  `config_value` varchar(512) NOT NULL COMMENT '配置value',\n  `info` varchar(255) NOT NULL COMMENT '配置说明',\n  `status` tinyint(4) NOT NULL COMMENT '1:可用,0:不可用',\n  `order_id` int(11) NOT NULL COMMENT '顺序',\n  PRIMARY KEY (`config_key`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统配置';\n\n--\n-- init cachecloud data\n--\n\nBEGIN;\nINSERT INTO `system_config` VALUES \n ('cachecloud.admin.user.name', 'admin', 'cachecloud-admin用户名', '1', '6'),  \n('cachecloud.admin.user.password', 'admin', 'cachelcoud-admin密码', '1', '7'),  \n('cachecloud.app.client.conn.threshold', '2000', '应用连接数报警阀值', '1', '24'),  \n('cachecloud.base.dir', '/opt', 'cachecloud根目录，要和cachecloud-init.sh脚本中的目录一致', '1', '23'),  \n('cachecloud.contact', 'user1:(xx@zz.com, user1:135xxxxxxxx)<br/>user2: (user2@zz.com, user2:138xxxxxxxx)', '值班联系人信息', '1', '16'),  \n('cachecloud.email.alert.interface', '', '邮件报警接口(说明:http://cachecloud.github.io 邮件和短信报警接口规范)', '1', '25'),  \n('cachecloud.machine.ssh.name', 'cachecloud', '机器ssh用户名', '1', '1'),  \n('cachecloud.machine.ssh.password', 'cachecloud', '机器ssh密码', '1', '2'),  \n('cachecloud.machine.ssh.port', '22', '机器ssh端口', '1', '3'),  \n('cachecloud.machine.stats.cron.minute', '1', '机器性能统计周期(分钟)', '1', '30'),  \n('cachecloud.mobile.alert.interface', '', '短信报警接口(说明:http://cachecloud.github.io 邮件和短信报警接口规范)', '1', '26'),  \n('cachecloud.nmon.dir', '/opt/cachecloud', 'nmon安装目录', '1', '31'),  \n('cachecloud.owner.email', 'xx@sohu.com,yy@qq.com', '邮件报警(逗号隔开)', '1', '13'),  \n('cachecloud.owner.phone', '13812345678,13787654321', '手机号报警(逗号隔开)', '1', '14'),  \n('cachecloud.public.key.pem', '/opt/ssh/id_rsa', '密钥路径', '1', '3'), \n('cachecloud.public.user.name', 'cachecloud', '公钥用户名', '1', '3'),  \n('cachecloud.ssh.auth.type', '1', 'ssh授权方式', '1', '4'),  \n('cachecloud.superAdmin', 'admin,xx,yy', '超级管理员组', '1', '8'),  \n('cachecloud.whether.schedule.clean.data', 'false', '是否定期清理统计数据', '1', '28'),  \n('machine.mem.alert.ratio', '80.0', '机器内存报警阀值', '1', '10');\nCOMMIT;\n```"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/docker.md",
    "content": "## 一、Redis docker部署步骤\n\n### 1. 准备Redis二进制资源包： \n\n  - 1). 资源下载: [下载Redis资源包](http://download.redis.io/releases)\n  - 2). 下载所需要用到Redis版本资源: 例如: [redis-3.0.7](http://download.redis.io/releases/redis-3.0.7.tar.gz)、[redis-3.2.12](http://download.redis.io/releases/redis-3.2.12.tar.gz)、[redis-4.0.14](http://download.redis.io/releases/redis-4.0.14.tar.gz)、[redis-5.0.7](http://download.redis.io/releases/redis-5.0.7.tar.gz)\n  - 3). 在宿主/容器环境安装(make)redis资源包，对make编译完成资源重新打包redis-${大版本}.${小版本}.${增量版本}-make.tar.gz\n        \n### 2. 文件说明：\n\n```\n   1).cachecloud-init-docker.sh: 宿主环境(物理机/虚拟机)初始化系统环境变量;\n   2).cachecloud-env.sh: cachecloud安装初始化脚本,如果是docker环境 默认会指定初始化;\n   3).supervisord.conf: 进程管理工具，可指定容器默认启动的后台守护进程,用于管理sshd以及容器初始化的过程;\n   4).authorized_keys: 默认为空，构建镜像会自动生成公钥写入到文件\n   5).build.sh: 编译dockerfile创建镜像;\n```\n\n### 3. dockerfile说明\n\n````\nFROM centos:7.5.1804\nRUN \\\n    /usr/bin/yum -y install wget make openssh-clients openssh-server;\\\n    mkdir -p /opt/cachecloud /home/cachecloud/.ssh\nADD redis-3.0.7-make.tar.gz /opt/cachecloud\nADD redis-3.2.12-make.tar.gz /opt/cachecloud\nADD redis-4.0.14-make.tar.gz /opt/cachecloud\nADD redis-5.0.6-make.tar.gz /opt/cachecloud\nADD cachecloud-env.sh /opt/cachecloud\nADD cachecloud-init-docker.sh /opt/cachecloud\nADD profile /etc\nADD supervisord.conf /etc\nADD public/authorized_keys /home/cachecloud/.ssh\nRUN \\\n    source /etc/profile;\\\n    chmod +x /opt/cachecloud/*;\\\n    chown 600 /home/cachecloud/.ssh/authorized_keys;\\\n    sh /opt/cachecloud/cachecloud-init-docker.sh;\\\n    sh /opt/cachecloud/cachecloud-env.sh;\\\n    make -C /opt/cachecloud/redis-5.0.6 install;\\\n    ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key ;\\\n    ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key ;\\\n    ssh-keygen -t rsa -f /etc/ssh/ssh_host_ed25519_key ;\\\n    ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key ;\\\n    echo \"StrictModes no\" >> /etc/ssh/sshd_config;\\\n    yum -y install python-setuptools;\\\n    easy_install supervisor\nEXPOSE 22\nCMD /bin/bash\n````\n构建说明：\n\n1).基于centos7.5操作系统构建镜像；\n\n2).工具包安装: sshd工具/supervisord进程管理工具等；\n\n3).Redis二进制资源包解压安装/基础目录/用户权限；\n\n4).build.sh: 编译dockerfile创建镜像;\n\n### 4. 构建镜像:  sh build.sh\n\n```\n    前提: 当前环境需要安装docker环境\n    1). 查看镜像\n        $ docker images\n        \n        REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE\n        redis                 202002111559        c1f0da6454ab        3 minutes ago       1.08GB\n\n    2). 以交互模式启动一个容器\n        $ docker run -i -t redis:202002111559 /bin/bash\n    \n        [root@d5e8923560bd cachecloud]# tree -L 1\n        .\n        |-- cachecloud-env.sh\n        |-- cachecloud-init-docker.sh\n        |-- conf\n        |-- data\n        |-- logs\n        |-- redis           \n        |-- redis-3.2.12\n        |-- redis-4.0.14\n        `-- redis-5.0.6 \n```\n\n## 二、Cachecloud docker部署步骤\n\n\n## 三、迁移工具部署\n\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/docker.toc.md",
    "content": "##### 目录\n* [一、Redis docker部署步骤](#d1)\n* [二、Cachecloud docker部署步骤](#d2)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/index.md",
    "content": "## CacheCloud接入流程\n\n### 一、[系统初始化](init.md)\n\n+ **1.初始化数据库** ：导入项目中sql文件初始化库表结构；\n+ **2.CacheCloud项目配置** ：系统需要[初始化配置参数](config.md)；\n+ **3.启动cachecloud系统** ：启动运行cachecloud项目；\n+ **4.手动安装机器环境**：redis机器手动安装环境；\n+ **5.访问机器方式**：用户名/密码或公钥方式访问机器；\n+ **6.一些扩展性问题说明**：扩展问题说明；\n\n### 二、用户申请应用流程\n\n<img src=\"../../img/access/app-apply.png\" width=\"60%\"/>\n\n+ **1.应用申请** ：填写客户端需求以及申请应用类型。\n+ **2.后台开通** ：管理员根据客户端需求，分配合理机器资源进行初始化应用。\n+ **3.邮件通知** ：管理员审核成功后，会通过邮件告知申请人员可以接入使用。\n+ **4.[客户端接入](client.md)** ：使用方通过appId接入CacheCloud资源。\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/init.md",
    "content": "## 系统接入\n\n* [一、初始化数据库](#cc1)\n* [二、CacheCloud系统配置](#cc2)\n* [三、CacheCloud部署拓扑](#cc3)\n* [四、CacheCloud服务部署](#cc4)\n* [五、Redis机器环境安装](#cc5)\n* [六、系统扩展性模块说明](#cc6)\n   \n\n<a name=\"cc1\"/>\n\n### 一、初始化数据库\n  导入项目sql目录下初始化库表结构，默认插入admin超级管理员。以下表为相关sql文件说明:\n  \n  | 序号 | sql文件名 | 说明  | \n  | :-------------------------- | :-------------------------- |:----------------------------- | \n  | 1 | 1.2.sql     | CacheCloud1.2版本的sql文件 | \n  | 2 | 1.2-2.0.sql     | CacheCloud从1.2版本升级到2.0版本增量sql文件 |  \n  | 3 | 2.0.sql | CacheCloud2.0版本的sql文件 |  \n  | 4 | update 2.0 to 3.0 sql | CacheCloud从2.0版本升级到3.0版本增量sql文件 |\n  | 5 | 3.0 sql | CacheCloud3.0版本的sql文件 |\n\n<a name=\"cc2\"/>\n\n### 二、CacheCloud系统配置\n\n项目采用springboot部署方式，通过application-${profile}.yml来区分不同环境资源隔离,其中profile:本地(默认为local)、测试(test)、线上(online)。\n当第一次启动系统后，需要对管理后台进行初始化配置，详细配置请参考: [系统配置说明](config.md) \t\t\n    \n<img src=\"../../img/access/globalconfig.png\" width=\"60%\"/>\n    \n<a name=\"cc3\"/> \n    \n### 三、CacheCloud部署拓扑\n\n<img src=\"../../img/access/deploy.png\" width=\"80%\"/>\n\n````\n服务拓扑说明:\n1. 资源说明: nginx机器/cachecloud机器各4台，其中每2台机器分布在不同机房；\n2. 域名说明: 申请nginx域名： cc.**.**.com用于访问\n3. 虚ip说明：每组虚ip映射2台nginx机器，防止nginx出现单点问题；\n4. 机房说明: nginx/cachecloud机器每2台资源会分布在不同机房，为防止因为某个机房网络问题导致无法访问cc后台；\n````\n\t\n<a name=\"cc4\"/>\n\n### 四、CacheCloud服务部署\n\n- 1.本地环境:\n\n````\n(a). 在cachecloud根目录编译：mvn clean compile install\n(b). 在cachecloud-web模块下启动：mvn spring-boot:run\n````\n\n- 2.生产环境:\n\n````\n(a).项目根目录下编译打包：mvn clean compile install -Ponline\n(b).上传war包(cachecloud-web/target/cachecloud-web-online.war)到服务器/opt/cachecloud-web目录下\n(c).启动资源包：java -jar cachecloud-web-online.war -Dspring.profiles.active=online\n````\n\n- 3.后台登录:\n\n(a) 访问：http://127.0.0.1:9999/manage/login \n\n(b) 如果访问正常，请使用用户名:admin、密码:admin访问系统，跳转到应用列表下：\n\n <img src=\"../../img/access/login.png\" width=\"40%\"/>\n\n<a name=\"cc5\"/>\n\n### 五、Redis机器环境安装\n\n- 5.1 Redis机器环境手动安装：\n\n````\n1. 运行脚本:\n\n  cachecloud项目中的cachecloud-init.sh(目录：cachecloud-web\\script\\cachecloud-init.sh)脚本是用来初始化Redis服务器环境，主要工作如下：\n    (a). 创建cachecloud项目用户;\n    (b). 创建cachecloud项目的工作目录、数据目录、配置目录、日志目录、redis安装目录、临时目录等等;\n    (c). 安装最新的release版本的Redis;\n\n2. 脚本执行:\n    (a). 使用root登录目标服务器;\n    (b). 将cachecloud-init.sh脚本拷贝到目标服务器当前用户目录下;\n    (c). 执行 sh cachecloud-init.sh ${yourusername};\n    (d). 两次确认密码;\n    (e). 一路安装直到成功;\n请确保机器和用户名与cachecloud后台中系统用户名/密码配置一致。\n\n3. 建议和警告 \n    (a). 请在root用户下执行初始化脚本，因为初始化脚本涉及到了用户的创建等较高的权限。\n    (b). 出于安全的考虑，所选的机器最好不要有外网IP地址。\n    (c). 用户名和密码最好不要用cachecloud, 密码尽可能复杂。\n    (d). 请确保/opt/有足够的硬盘空间，因为/opt/cachecloud/data要存储RDB和AOF的持久化文件，如果硬盘过小，会造成持久化失败。（如果硬盘确实很小，建议建立一个软链接到/opt/cachecloud/data,且保证软链接的目录也是username用户，一定要保证/opt/cachecloud的目录结构）\n    (e). 脚本中目前使用的是redis-3.0.7，如有需要请自行替换，建议使用3.0 release以后的版本。但是要注意3.2版本中bind的默认是127.0.0.1\n````\n\n- 5.2 Redis机器环境镜像安装：[Redis镜像安装](docker.md)\n- 5.3 Redis机器授权方式：[访问Redis机器方式](../operate/ssh.md)\n\n<a name=\"cc6\"/>\n\n### 六、系统扩展性模块说明\n\n#### 1. 登录模块:\n\n除了超级管理员admin:admin以外，所有的用户在登录时都要做两项内容的验证：\n\n- (1). 实现登陆逻辑：\n根据各公司自身需要实现逻辑可以在cachecloud-custom模块com.sohu.cache.login.impl.DefaultLoginComponent类中实现：\n\n```Java\n    public class DefaultLoginComponent implements LoginComponent {\n    \n        @Override\n        public boolean passportCheck(String userName, String password) {\n            /**\n             * your company login check code\n             */\n            return true;\n        }\n    \n        @Override\n        public String getEmail(String ticket) {\n            return null;\n        }\n    \n        @Override\n        public String getRedirectUrl(HttpServletRequest request) {\n            return null;\n        }\n    \n        @Override\n        public String getLogoutUrl() {\n            return null;\n        }\n    }\n```\n- (2). 是否为cachecloud用户\n\n    需要验证用户是否在app_user表中\n\n#### 2. 报警模块说明\n\ncachecloud提供了邮件和微信两种形式的报警工具,这里主要是对Redis机器/实例等重要指标进行监控。\n各个公司、项目组可以根据需要实现对应报警接口逻辑，具体参考：\n\n- 2.1 代码实现：\n\n    - (1).邮件报警接口实现:com.sohu.cache.alert.impl.DefaultEmailComponent (cachecloud-custom)\n    ```Java\n    public class DefaultEmailComponent implements EmailComponent {\n    \n        @Override\n        public boolean sendMail(String title, String content, List<String> emailList, List<String> ccList) {\n            //todo\n            throw new UnsupportedOperationException();\n        }\n    \n        @Override\n        public boolean sendMail(String title, String content, List<String> emailList) {\n            //todo\n            throw new UnsupportedOperationException();\n        }\n    \n        @Override\n        public boolean sendMailToAdmin(String title, String content) {\n            //todo\n            throw new UnsupportedOperationException();\n        }\n    \n        @Override\n        public String getAdminEmail() {\n            //todo\n            throw new UnsupportedOperationException();\n        }\n    }\n    ```\n\n    - (2).微信报警接口实现:com.sohu.cache.alert.impl.DefaultWeChatComponent (cachecloud-custom)\n    ```Java\n    public class DefaultWeChatComponent implements WeChatComponent {\n    \n        @Override\n        public boolean sendWeChat(String title, String message, List<String> weChatList) {\n            //todo\n            throw new UnsupportedOperationException();\n        }\n    \n        @Override\n        public boolean sendWeChatToAll(String title, String message, List<String> weChatList) {\n            //todo\n            throw new UnsupportedOperationException();\n        }\n    \n        @Override\n        public boolean sendWeChatToAdmin(String title, String message) {\n            //todo\n            throw new UnsupportedOperationException();\n        }\n    }\n    ```\n    \n- 2.2 接口实现\n\n提供了http接口规范，只要按照规范开发http接口，任何语言都可以实现。\n\n(1).邮件：\n\n参数 | 含义 | 是否必须\n---|--- |---\ntitle | 邮件标题 | 是\ncontent | 邮件内容 | 是\nreceiver | 收件人列表 | 是\ncc | 抄送人列表 | 否\n\n\n例如我们用python语言按照上面的参数开发了一个http接口\n```\nwww.xxx.com/emailAlert?title=xx&content=xx&receiver=x&cc=x\n```\n(2).微信：\n\n参数 | 含义 | 是否必须\n---|--- |---\ntitle | 短信内容 | 是\nmessage | 手机号列表 | 是\nweChatList | 微信人员列表 | 是\n\n例如我们用python语言按照上面的参数开发了一个http接口\n```\nwww.xxx.com/weChatAlert?title=xx&message=xx&weChatList=xx\n```\n\n(3). 修改系统配置：\n\n确认无误后，我们需要把它添加到系统配置修改中即可：\n![](http://i0.itc.cn/20160713/3084_7a9245a4_c1a1_d6ec_a7cd_9c7f269d3fc6_1.png)\n\n\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/init.toc.md",
    "content": "##### 目录\n\n* [一、初始化数据库](#cc1)\n* [二、CacheCloud系统配置](#cc2)\n* [三、CacheCloud部署拓扑](#cc3)\n* [四、CacheCloud服务部署](#cc4)\n* [五、Redis机器环境安装](#cc5)\n* [六、系统扩展性模块说明](#cc6)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/resource.md",
    "content": "## 系统资源管理\n\n<a name=\"cc1\"/>\n\n### 一、目的\n\nCacheCloud系统需要依赖一些脚本，Redis资源包，迁移工具，公钥/私钥等资源，由于比较零碎，此项功能用于对资源及其版本同一管理控制，简化运维提升效率。\n主要分以下几大类：\n\n- 仓库配置：配置资源的仓库地址，可用于推送和下载资源；\n- 目录管理：区分各类资源的目录结构，便于管理资源；\n- 脚本管理：管理系统依赖资源的脚本；\n- Redis资源管理： 管理redis资源包不同版本及推送下载；\n- 迁移工具资源管理：管理迁移工具不同资源包版本及推送下载；\n\n<a name=\"cc2\"/>\n\n### 二、仓库配置\n\n<img src=\"../../img/operate/version/respo.png\" width=\"100%\"/>\n\n远程仓库地址：可访问的仓库ip地址；例如:10.%.%.%\n\n根目录：仓库资源的根目录；例如:/opt/download/software/cachecloud/resource\n\n资源下载地址：可访问资源地址；例如:http://%.%.%.com/software/cachecloud/resource\n\n<a name=\"cc3\"/>\n\n### 三、目录管理\n\n<img src=\"../../img/operate/version/dir.png\" width=\"100%\"/>\n\n对不同类型资源创建不同目录，需要做推送后及在远程仓库创建对应资源目录(格式:/${dir})；\n\n<a name=\"cc4\"/>\n\n### 四、脚本管理\n\n<img src=\"../../img/operate/version/script.png\" width=\"100%\"/>\n\n对系统依赖脚本进行版本管理，可在编译内容和推送脚本到仓库上；\n\n<a name=\"cc5\"/>\n\n### 五、Redis资源管理\n\n<img src=\"../../img/operate/version/redis.png\" width=\"100%\"/>\n\nRedis资源管理支持对Redis大小版本做统一管理和版本控制(3.0.x -> 3.2.x -> 4.0.x -> 5.0.x -> 6.0.0-rc)的不断迭代更新，项目也需要用到不同版本和新特性。\n\n#### 5.1.新增资源包\n\n- 1).Redis版本格式规范，验证格式：redis-主.子.增量版本\n- 2).安装目录规范：/opt/cachecloud/redis-主.子.增量版本\n- 3).是否备份：可直接继承之前版本配置模板，进行增量修改配置\n\n#### 5.2.修改配置\n\n点击`修改配置`，可新增配置项：\n\n<img src=\"../../img/operate/version/versionConfig.png\" width=\"60%\"/>\n\n#### 5.3、应用版本升级\n\n只支持小版本增量升级(Redis-3.2.x/Redis-4.0.x/Redis-5.0.x):\n例如:Redis-5.0.6 升级到 Redis-5.0.7\n\n<img src=\"../../img/operate/version/upgrade.png\" width=\"100%\"/>\n\n点击`版本升级`，可升级当前应用版本: \n\n<img src=\"../../img/operate/version/upgrade-process.png\" width=\"60%\"/>\n\n升级完后，若为升级版本5.0.7为当前管理的最终小版本号，则应用按钮会显示为`最新版本`。\n\n<a name=\"cc6\"/>\n\n### 六、迁移工具资源管理\n\n<img src=\"../../img/operate/version/tool.png\" width=\"100%\"/>\n\n对系统迁移工具版本管理，方便对资源的可控和管理；\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/access/resource.toc.md",
    "content": "##### 目录\n\n* [一、目的](#cc1)\n* [二、仓库配置](#cc2)\n* [三、目录管理](#cc3)\n* [四、脚本管理](#cc4)\n* [五、Redis资源管理](#cc5)\n* [六、迁移工具资源管理](#cc6)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/architecture/index.md",
    "content": "## CacheCloud系统架构介绍\n\nCacheCloud系统架构介绍，主要包含以下方面:\n\n+ 1.[服务架构](service)\n    + 1.1 类型架构\n    + 1.2 伸缩架构\n+ 2.[技术架构](tech)\n\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/architecture/service.md",
    "content": "## CacheCloud服务架构\n\n<a name=\"cc7-1\"/>\n\n### 一、类型架构\n- 1.standalone类型架构\n\n<img src=\"http://i0.itc.cn/20160126/3084_a31f490d_64e3_3e43_6b99_18edbb45d8bc_1.png\"/>\n\n用于可穿透业务场景，如后端有DB存储，脱机影响不大的应用。\n\n- 2.sentinel类型架构\n\n<img src=\"http://i2.itc.cn/20160126/3084_0e82352a_0037_592e_89b3_29bee971bb71_1.png\"/>\n\n用于高可用需求场景,可用于高可用Cache,存储等场景。\n内存/QPS受限于单机。\n\n- 3.cluster类型架构\n\n<img src=\"http://i0.itc.cn/20160126/3084_e7ab6ad2_359b_d617_c255_6d56b25e2cd9_1.png\"/>\n\n用于高可用需求场景,可用于大数据量高可用Cache/存储等场景。\n内存/QPS不受限于单机，可受益于分布式集群高扩展性\n\n<a name=\"cc7-2\"/>\n\n### 二、伸缩架构\n\n- 1.垂直伸缩架构\n\n<img src=\"http://i1.itc.cn/20160126/3084_dbf1c47c_d145_1145_cebb_92aa6bb7fef5_1.png\"/>\n\n通过统一调整每个实例可用内存量做到垂直拓展，受限于机器物理内存资源.\n适用于所有redis类型应用\n\n- 2.水平(sentinel)伸缩架构\n\n<img src=\"http://i1.itc.cn/20160126/3084_93deddfb_fd46_21db_a1cf_46256ffd09e3_1.png\"/>\n\n通过在线切换主从关系和实例所属机器实现扩容。\n适用于sentinel应用,在物理资源不够用/换掉故障机器时使用.\n\n- 3.水平(cluster)伸缩架构\n\n<img src=\"http://i1.itc.cn/20160126/3084_b974e379_96cb_edad_2e8a_ecf0821c3020_1.png\"/>\n\n通过动态加减实例并在线迁移数据实现伸缩性。\n适用于redis-cluster应用,伸缩性最灵活但是速度最慢。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/architecture/tech.md",
    "content": "## CacheCloud技术架构\n\n<a name=\"cc7-3\"/>\n\n### 一、技术架构：\n\n<img src=\"http://i0.itc.cn/20160126/3084_07c6991a_f1c6_e6b7_0300_5e024d45f7ea_1.png\"/>\n<img src=\"http://i1.itc.cn/20160126/3084_36e5d705_e812_7b83_e18e_cdad6c6d4c37_1.jpg\"/>\n\n<a name=\"cc8\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-analysis.md",
    "content": "## 键值分析\n\n键值分析主要用来分析应用BigKey/过期键/键值分布等情况。\n\n<img src=\"../../img/function/client/app-analysis.png\" width=\"100%\">\n\n点击”键值分析“，提交分析申请。\n\n<img src=\"../../img/function/client/app-analysis-application.png\" width=\"100%\">\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-appStats.md",
    "content": "## 应用统计信息\n\n当您的应用开始使用后，您可以按照如下步骤查看应用统计数据和各实例统计数据。\n\n### 一、全局统计\n\n- 在页面详情页，默认展示应用统计信息，可以看到全局信息、命令统计、各命令峰值信息，全命令统计、命中统计、网络流量等全天统计曲线。按照顺序，\n  + 全局信息：展示应用的全局信息，包括内存使用率、当前连接数、应用redis版本、应用类型、主从节点数、命中率、当前对象数、当前状态及分布的机器数；\n  + 命令统计：展示当前应用执行最频繁的5个命令的分布情况；\n  + 各命令峰值信息：展示当前应用执行最频繁的5个命令的峰值统计；\n  + 全命令统计：展示当前应用的命令执行次数趋势图；\n  + 命中统计：展示当前应用的命中次数趋势图；\n  + 网络流量：展示应用的输入输出流量趋势图，亦可查看应用下各实例的网络输入输出流量；\n  + CPU消耗：展示应用的CPU消耗情况趋势图，亦可查看应用下各实例的CPU消耗情况趋势；\n  + 内存使用量：展示应用的内存使用情况趋势图，亦可查看应用下各实例的内存碎片率情况；\n  + 客户端连接统计：展示该应用下客户端连接数趋势图；\n  + 键个数统计：展示该应用下键个数趋势图；\n  + 过期/淘汰键统计：展示该应用下过期/淘汰键个数趋势图，亦可查看应用下各实例的过期/淘汰键统计。\n  \n![](../../img/function/client/client-statsInfo.jpg)\n\n![](../../img/function/client/client-statsInfo1.jpg)\n\n![](../../img/function/client/client-statsInfo2.jpg)\n\n![](../../img/function/client/client-statsInfo3.jpg)\n\n    - 扩容/缩容，在应用统计信息页面，点击申请扩容，弹出如图弹框，按照要求填写对应信息，提交。提交成功后，将收到进度邮件。\n    \n   ![](../../img/function/client/client-capacity.png)\n   \n    - 客户端统计\n\n- 命令曲线，命令曲线展示了该应用下执行最频繁的5个命令的执行次数趋势比较图，可以点击不同命令查看个命令的趋势图。\n\n![](../../img/function/client/client-codeImg.jpg)[]()\n\n### 二、客户端统计\n\n应用首页查看客户端统计，主要客户命令调用，异常统计情况。\n某个客户端调用/流量/耗时\n具体命令调用/流量/耗时\n\n<img width=\"100%\" src=\"../../img/function/client/client-statistic.png\"/>\n\n- 客户命令调用统计，展示应用下命令调用全局统计情况，包括命令调用次数、命令平均耗时、输入/输出流量趋势。\n\n![](../../img/function/client/command-statistic.png)\n![](../../img/function/client/command-statistic1.png)\n![](../../img/function/client/command-statistic2.png)\n    \n**点击“客户端详情”，查看不同客户端下各命令的调用情况。**\n\n![](../../img/function/client/command-statistic-client.png)\n\n\n![](../../img/function/client/command-statistic-all.png)\n\n- 客户端异常情况统计：展示应用下异常情况全局统计情况，包括异常次数、异常平均耗时趋势。\n\n![](../../img/function/client/exception-statistic.png)\n\n**异常分为客户端连接异常及命令调用超时异常，可分别点击“客户端连接异常详情”和“客户端命令超时详情”查看不同客户端下对应的异常情况。**\n\n**1.客户端连接异常详情**\n\n![](../../img/function/client/exception-statistic-client-conn.png)\n\n**2.客户端命令超时详情**\n\n![](../../img/function/client/exception-statistic-client.png)\n\n![](../../img/function/client/exception-statistic-client1.png)\n\n    点击图中点查看超时命令详情，包括命令的执行时间，命令明文，参数明文和字节数信息，如下图：\n    \n![](../../img/function/client/exception-statistic-client-cmd.png)\n \n "
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-cmd.md",
    "content": "## 命令曲线\n\n命令曲线展示了该应用下执行最频繁的5个命令的执行次数趋势比较图，可以点击不同命令查看个命令的趋势图。\n\n![](../../img/function/client/client-codeImg.jpg)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-cmdexe.md",
    "content": "## 命令执行\n\n在应用或实例页面，点击命令执行，打开命令执行页面，支持在console中执行只读命令。\n\n![](../../img/function/client/client-appCmd.png)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-conn.md",
    "content": "## 连接信息\n\n支持查看当前客户端连接信息，包括客户端ip、连接数、客户端类型，点击”查看实例连接信息“可查看具体redis实例下的连接信息。\n\n<img src=\"../../img/function/client/instance-connInfo.png\" width=\"100%\">\n\n- 应用客户端：该分类下展示用户端连接信息，可通过查看此连接确认是否有用户使用该应用，为应用迁移/下线提供极大便利；\n- cc客户端：该分类下展示CacheCloud客户端的连接信息；\n- redis客户端：该分类下展示redis实例连接信息；\n- 所有客户端：该分类下展示所有客户端连接信息。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-daily.md",
    "content": "## 日报统计\n\n日报统计汇总了全天的应用使用情况，分为客户端相关和服务端相关指标统计，帮助用户了解应用的使用情况，发现问题等。\n\n<img src=\"../../img/function/client/app-daily.png\" width=100%>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-desc.md",
    "content": "## 应用详情&权限管理\n\n应用详情可以运维应用详情，配置应用报警，管理应用下的用户。\n\n![](../../img/function/client/client-appDetail.png)\n\n- 该界面展示应用的基本信息，\n- 修改应用信息：支持修改应用名、应用描述信息及项目负责人；\n-  添加用户：负责人有权限为应用添加用户。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-instances.md",
    "content": "## 实例列表&应用拓扑\n\n### 一、实例列表\n\n- 实例列表，展示应用下各实例的具体情况，可以看到该应用的具体实例分布情况，包括ip、端口，实例状态，角色以及关联实例的id，根据应用类型的不同，可能会有区别。\n\n<img src=\"../../img/function/client/client-instancelists.png\" width=\"100%\">\n\n\n### 二、应用拓扑\n\n- 应用拓扑，展示应用实例在各机器上的分布情况。\n\n<img src=\"../../img/function/client/client-topology.png\" width=\"100%\">\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-latency.md",
    "content": "## 延迟监控\n\n延迟监控统计了应用下发生的”延迟事件“。根据发生事件点，将”延迟事件“和”慢查询命令“对应，更好的辅助用户发现、定位redis的使用问题。\n\n![](../../img/function/client/instance-latency.png)\n\n- 延迟事件统计：统计了各延迟事件数量的趋势图，点击图中点可查看延迟事件详情，如下图：查看某个时间点下某个redis实例发生的延迟事件，关联的慢查询情况等。\n![](../../img/function/client/instance-latency1.png)\n\n- redis实例延迟&慢查询统计：统计每个redis实例下发生的延迟事件数和慢查询数。\n- 各实例慢查询情况：展示详细的实例下慢查询情况。\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-register.md",
    "content": "## 账户申请\n\n用户首次使用CacheCloud执行所有操作之前，用户首先需要申请一个系统账号。当需要开通账户时，填写相关信息。管理员收到申请邮件时确认开通。\n\n- 进入CacheCloud首页，点击注册按钮\n\n![](../../img/function/client/client-login.png)\n\n- 填写用户相关信息并提交申请\n\n![](../../img/function/client/client-register.png)\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client-statistic.md",
    "content": "## 客户端指标统计汇总\n\n- 统计全天应用各维度汇总数据,包括: 应用内存使用率、应用碎片率、应用慢查询、异常数量以及命令调用情况汇总。\n    \n<img src=\"../../img/function/exception.png\" width=\"100%\">\n "
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/client.md",
    "content": "<a name=\"cc1\"/>\n\n客户端模块介绍普通用户使用CacheCloud平台功能，包括账户申请、应用管理和我的申请三大模块。\n\n- 账户申请：用户首次使用CacheCloud执行所有操作之前，用户首先需要注册一个系统账号；\n- 应用管理：用户可以查看、管理自己名下的redis应用，包括应用的统计信息、应用详情、实例列表、连接信息、命令曲线、延迟监控和日报统计等；\n- 我的申请： 用户可以查询、申请对自己名下redis应用的相关操作，保留申请应用、导入应用、数据清理、诊断应用等操作。\n\n用户使用CacheCloud平台总体流程如下：\n\n![总体使用流程](../../img/function/client/client-process.png)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/index.md",
    "content": "## CacheCloud功能介绍\n\n### CacheCloud平台提供以下五个功能模块:\n+ wiki管理：系统介绍、FAQ、系统接入、系统功能、功能手册、常见问题\n+ Redis搭建：环境初始化、实例部署安装、类型架构支持\n+ 客户端接入：Java-SDK接入、客户端监控、其他语言接入；\n+ 运维管理：宿主环境、资源管理、应用审计、应用运维、应用质量监控、应用诊断扥西\n+ 弹性伸缩：资源评估、垂直伸缩、水平伸缩、外部接入；\n+ 统计监控：指标采集、应用统计、节点统计、机器统计、监控报警、问题诊断；\n\n根据用户类型可分为：前台-客户端功能（普通用户），后台-运维端功能（管理员）。\n\n<img src=\"../../img/readme/CacheCloud-structure.png\" width=\"100%\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/job.md",
    "content": "## 我的申请\nCacheloud系统提供完善的工单申请-审批流程。用户可以提交自己名下相关应用的工单申请，由管理员驳回/处理、通过后，一条完整的工单流程执行完毕。 \n\n### 我的工单\n\n- 点击\"我的申请\"进入工单列表界面：\n\n  <img src=\"../../img/function/job/myJobs.png\" width=\"100%\">\n\n  展示了工单申请/处理情况汇总，每条工单具体的记录，工单状态分为：待审、已受理、通过、驳回，工单受理后显示处理人。\n\n  <img src=\"../../img/function/job/jobList.png\" width=\"100%\">\n\n### 创建工单\n\n- \"创建工单\"按不同类型分为：申请应用，应用导入，数据清理（全库清理/删除数据），下线应用，数据迁移，诊断应用，键值分析，扩容/缩容，修改应用配置和修改报警等。\n\n    a.应用申请：\n\n    <img src=\"../../img/function/job/appInit.png\" width=\"80%\"> \n   \n    b.导入应用：用户将现有redis实例导入cachecloud平台，进行集中管理。\n  \n\t<img src=\"../../img/function/job/appImport.png\" width=\"80%\"> \n\n    c.数据清理：分为全库清理和删除数据\n    \n    <img src=\"../../img/function/job/appClean.png\" width=\"80%\"> \n\n    <img src=\"../../img/function/job/appDelKey.png\" width=\"80%\">\n\n    d.下线应用：\n    \n    <img src=\"../../img/function/job/appOffline.png\" width=\"80%\"> \n    \n    e.数据迁移：将redis数据迁移到新的应用下\n    \t\n\t<img src=\"../../img/function/job/appMigrate.png\" width=\"80%\"> \n\n    f.诊断应用:\n    \n    <img src=\"../../img/function/job/appDiagnostic.png\" width=\"80%\"> \n\n    g.键值分析:\n    \n    <img src=\"../../img/function/job/appKeyAnalysis.png\" width=\"80%\"> \n\n    h.扩容/缩容:\n    \n    <img src=\"../../img/function/job/appScale.png\" width=\"80%\"> \n\n    i.修改应用配置:\n    \n    <img src=\"../../img/function/job/appConfig.png\" width=\"80%\"> \n\n    g.修改报警:\n    \n    <img src=\"../../img/function/job/appAlterConfig.png\" width=\"80%\"> \n   "
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-alert.md",
    "content": "## 报警配置\n\n管理redis报警阈值，支持对全部应用/单个应用/单个实例进行报警配置。\n\n<img width=\"100%\" src=\"../../img/function/server/cc-alert-all.png\"/>\n\n<img width=\"100%\" src=\"../../img/function/server/cc-alert-special.png\"/>\n\n<img width=\"100%\" src=\"../../img/function/server/cc-alert-add-common.png\"/>\n\n<img width=\"100%\" src=\"../../img/function/server/cc-alert-add-app.png\"/>\n\n<img width=\"100%\" src=\"../../img/function/server/cc-alert-add-instance.png\"/>\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-app.md",
    "content": "###### 目录\n* [1. 应用运维](#cc1)\n\t- [管理应用实例](#cc1-1)\n\t- [管理应用模块](#cc1-6)\n\t- [应用机器列表](#cc1-2)\n\t- [应用详情和审批列表](#cc1-3)\n\t- [应用密码修改](#cc1-4)\n\t- [应用拓扑诊断](#cc1-5)\n* [2. 应用迁移](#cc2)\n* [3. 应用下线](#cc3)\n* [4. 版本升级](#cc4)\n\n## 应用运维\n\n管理员在应用运维中可查看应用列表，了解各应用的使用情况，如版本、类型、内存详细、碎片率等，并对应用进行一系列的管理操作。\n\n<img src=\"../../img/function/operation/operation-app.png\" width=\"100%\">\n\n<a name=\"cc1\"/>\n### 应用运维\n点击”应用运维“操作，可管理应用实例、查看应用机器列表，应用详情和审批列表，修改应用密码等。\n\n<a name=\"cc1-1\"/>\n#### 管理应用实例\n\n**1. Redis Sentinel类型应用**\n\na. 一键添加sentinel节点  \nb. 一键Failover  \nc. 上下线实例  \nd. 添加slave节点  \ne. 查看操作日志  \nf. 修改配置 \n\n<img src=\"../../img/function/server/app-operation-sentinel1.png\" width=\"100%\">\n\n<a name=\"cc2-1-cluster\"/>\n\n**2. Redis Cluster应用类型**\n\na. 添加Slave  \nb. 一键Failover  \nc. 上下线实例  \nd. 查看操作日志  \ne. 修改配置  \nf. config/restart（修改配置/滚动重启）\n\n<img src=\"../../img/function/server/app-operation-cluster.png\" width=\"100%\">\n\n<a name=\"cc1-6\"/>\n#### 管理应用模块\n\n查看机器集成模块情况，实例装载模块情况，安装模块，查看实例日志等。\n\n<img src=\"../../img/function/server/app-module.png\" width=\"100%\">\n\n<a name=\"cc1-2\"/>\n#### 应用机器列表\n\n查看该应用分布机器的使用情况，包括内存使用率、已分配内存、cpu使用率等信息。\n\n<img src=\"../../img/function/server/app-machines.png\" width=\"100%\">\n\n\n<a name=\"cc1-3\"/>\n#### 应用详情和审批列表\n\n<img src=\"../../img/function/server/app-detail.png\" width=\"100%\">\n\n<a name=\"cc1-4\"/>\n#### 应用密码修改\n\n<img src=\"../../img/function/server/app-redisPassword.png\" width=\"100%\">\n\n<a name=\"cc1-5\"/>\n#### 应用拓扑诊断\n\n提供对应用拓扑分布规范的检查功能，便于发现问题，做出调整。\n\n<img src=\"../../img/function/server/topology.png\" width=\"100%\"/>\n\n<a name=\"cc2\"/>\n#### 2.应用迁移\n\n在CacheCloud后台的“应用运维”页面，选择要进行迁移的应用，点击“应用迁移”。 这种迁移方式不会更换应用的appId，通过主从节点的failover实现，是对客户端无感知迁移。注意：此操作通过failover实现节点切换，仅支持同redis版本/小版本应用的迁移升级。\n\n<img src=\"../../img/operate/appMigrate/app-migrate0.png\" width=\"100%\"/>\n\n应用迁移整体包含八个主要步骤：\n\n+ (1) 应用信息：查看源应用的实例IP，角色和Redis版本等信息，选择迁移的目标机房和迁移机器；\n\n<img src=\"../../img/operate/appMigrate/app-migrate1.png\" width=\"100%\"/>\n\n+ (2) 应用迁移计划：这一步主要查看节点的变更信息，新增实例（Slave）的IP和端口号，点击继续进行新老Salve节点的替换；\n\n<img src=\"../../img/operate/appMigrate/app-migrate2.png\" width=\"100%\"/>\n\n+ (3) 新老Salve节点替换: 替换完成之后查看最新实例信息，点击继续，进行主从切换；\n\n<img src=\"../../img/operate/appMigrate/app-migrate3.png\" width=\"100%\"/>\n\n+ (4) 主从Failover: 完成主从节点切换，点击继续，添加新的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate4.png\" width=\"100%\"/>\n\n+ (5) 添加Slave: 添加新的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate5.png\" width=\"100%\"/>\n\n+ (6) 新实例状态检测：检查新实例的连接状态，是否异常，点击继续，下线老的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate6.png\" width=\"100%\"/>\n\n+ (7) 下线Slave: 下线老的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate7.png\" width=\"100%\"/>\n\n+ (8) 迁移完成: 完成迁移。\n\n<img src=\"../../img/operate/appMigrate/app-migrate8.png\" width=\"100%\"/>\n\n<a name=\"cc3\"/>\n#### 3.应用下线\n\n下线当前应用，销毁所有存活节点。点击“应用下线”，跳转到应用下线任务流，可查看下线情况。\n\n<a name=\"cc4\"/>\n#### 4.版本升级\n\n升级应用的redis版本。详细操作可参考：[运维手册-Redis版本管理.应用版本升级](../access/redisVersion.md)。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-diagnostic.md",
    "content": "## 应用诊断工具\n\n提供给应用诊断的便捷工具，方便管理员排查应用的问题。\n\n### redis-cli工具\n\n集成redis-cli工具，选择应用和实例，可执行redis-cli命令。\n\n<img width=\"100%\" src=\"../../img/function/appTool/index.png\"/>\n\n### scan检测\n\n提交scan诊断，选择应用、实例等，扫描匹配的key：\n\n<img width=\"100%\" src=\"../../img/function/appTool/scan.png\"/>\n\n扫描完成后，可查看结果：\n\n<img width=\"60%\" src=\"../../img/function/appTool/scan-res.png\"/>\n\n### memoryUsed诊断\n\n提交memoryUsed诊断，选择应用、实例、内存占用量，扫描出大key：\n\n<img width=\"100%\" src=\"../../img/function/appTool/memory.png\"/>\n\n执行完成后，可查看结果：\n\n<img width=\"60%\" src=\"../../img/function/appTool/memory-res.png\"/>\n\n### idlekey诊断\n\n提交idlekey诊断，选择应用、实例、空闲天数，扫描出key：\n\n<img width=\"100%\" src=\"../../img/function/appTool/idle.png\"/>\n\n执行完成后，可查看结果：\n\n<img width=\"60%\" src=\"../../img/function/appTool/idle-res.png\"/>\n\n### hotkeys/bigkeys/memkeys诊断\n\n采样诊断应用的hotkeys/bigkeys/memkeys，并给出统计结果。\n\n### 删除任务\n\n执行匹配key的删除任务，并统计删除数量。\n\n### 集群slot分析\n\n提交redis集群的slot分布，选择应用、实例，分析出slot上key分布：\n\n<img width=\"100%\" src=\"../../img/function/appTool/slot.png\"/>\n\n分析完成后，可查看结果：slot分布和误差较大的slot\n\n<img width=\"60%\" src=\"../../img/function/appTool/slot-res1.png\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-import.md",
    "content": "## 应用导入\n\n在使用Cachecloud平台前，用户已有自己部署的redis实例，该功能支持将redis实例导入Cachecloud平台进行运维管理。用户提交的“导入应用”工单信息会记录在此，管理员点击“导入”或者\"查看\"可以追踪redis实例导入应用的情况。\n\n<img src=\"../../img/function/server/import-list.png\" width=\"100%\"/>\n\n应用导入共5个步骤，引导用户完成redis实例到Cachecloud应用的导入。\n\n1. 确认导入配置：应用导入前的准备工作，确认源redis实例信息和目标应用信息无误后，点击“开始导入”。\n<img src=\"../../img/function/server/import-step1.png\" width=\"100%\"/>\n\n2. 创建Redis版本：系统自动检测平台中是否有满足用户需求的版本号，如果没有，会提示“redis-x.x.x存在”，管理员先“【创建版本】”；如果有满足的版本，直接进入下一步“新建应用”。\n\n3. 新建应用：管理员“部署应用”。\n<img src=\"../../img/function/server/import-step3.png\" width=\"100%\"/>\n系统自动检测应用部署状态，如果部署出错，会提示管理员“【修复】 或 【重新部署】”应用。当应用部署成功，会进行到下一步“数据迁移”。\n<img src=\"../../img/function/server/import-step3err.png\" width=\"100%\"/>\n\n4. 数据迁移：进行源redis实例到目标应用间的数据迁移。\n<img src=\"../../img/function/server/import-step4.png\" width=\"100%\"/>\n系统自动检测数据迁移状态，如果迁移出错，会提示管理员“【修复】 或 【重新迁移】”。当应用数据迁移完毕，会跳转到“应用导入完成提示”。\n<img src=\"../../img/function/server/import-step4err.png\" width=\"100%\"/>\n\n5. 应用导入完成：应用导入成功，用户可以通过Cachecloud平台管理该应用，旧的redis实例可做下线处理。\n<img src=\"../../img/function/server/import-step5.png\" width=\"100%\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-instance.md",
    "content": "###### 目录\n* [实例运维]\n\t- [1.配置检测](#cc1)\n\t- [2.命令检测](#cc2)\n\t- [3.重启记录](#cc3)\n\n\n## 实例运维\n\n管理员在实例运维中可进行实例配置检测、命令检测，并查看检测结果及修复配置检测问题；此外还可查看修改配置/滚动重启记录。\n\n<img src=\"../../img/function/operation/operation-instance.png\" width=\"100%\">\n\n<a name=\"cc1\"/>\n#### 1.配置检测\n配置检测，可检测redis实例某个配置项是否符合检测规则。\n\n<a name=\"cc1-1\"/>\n#### 设定检测条件\n\na. 指定redis版本（可选所有及指定某个redis版本）   \nb. 填写配置项（配置项需严格按照config中名称进行指定）   \nc. 指定比较类型   \nd. 指定比较值（如不填写，则与空值进行比较（等同于配置不存在））    \n\n<img src=\"../../img/function/instance/instance-operation-config-setting.png\" width=\"100%\">\n\n<a name=\"cc1-2\"/>\n#### 查看检测异常及修复\n\na. 确认是否有异常，如果有异常，点击异常查看一列中的“查看修复“按钮    \n<img src=\"../../img/function/instance/instance-operation-config-result.png\" width=\"100%\">\n\nb. 点击“查看修复“按钮，跳转到实例配置检测异常结果详情页面    \n<img src=\"../../img/function/instance/instance-operation-config-detail.png\" width=\"100%\">\n\nc. 点击异常结果详情页面中的“查看修复“按钮，可跳转到应用运维——应用运维——应用实例页面。    \n目前cluster集群下的redis实例可支持直接弹框修改，其他的redis实例，可自行点击对应实例的“修改配置”\n<img src=\"../../img/function/instance/instance-operation-config-fix.png\" width=\"100%\">\n\n<a name=\"cc2\"/>\n#### 2.命令检测\n命令检测，可检测redis实例执行bgsave/bgrewriteaof命令时，是否会出现crash异常。\n\n<a name=\"cc2-1\"/>\n#### 设定检测条件\n\na. 指定宿主机ip（可选）  \nb. 指定pod ip（可选）   \nc. 指定命令（bgsave/bgrewriteaof）    \n说明：目前支持的两个命令检测操作较重，特别对于单个实例占用内存较大的情况，请不要经常使用，或精确指定执行的范围。\n\n<img src=\"../../img/function/instance/instance-operation-command-setting.png\" width=\"100%\">\n\n<a name=\"cc2-2\"/>\n#### 查看检测结果\n\na. 确认是否失败，如果失败，点击失败详情一列中的“查看“按钮    \n<img src=\"../../img/function/instance/instance-operation-command-result.png\" width=\"100%\">\n\nb. 点击“查看“按钮，跳转到实例命令检测结果详情页面    \n<img src=\"../../img/function/instance/instance-operation-command-detail.png\" width=\"100%\">\n\n<a name=\"cc3\"/>\n#### 3.重启记录\n可查看应用滚动重启/修改配置记录，并可根据应用id进行查询。    \n<img src=\"../../img/function/instance/instance-operation-restart-record.png\" width=\"100%\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-job.md",
    "content": "## 工单审批\n\n##### 目录\n\n* [1. 工单类型](#cc1-1)\n* [2. 开通应用](#cc1-2)\n* [3. 应用导入](#cc1-3)\n* [4. 扩容申请](#cc1-4)\n* [5. 数据迁移](#cc1-5)\n* [6. 数据清理](#cc1-6)\n* [7. 应用诊断](#cc1-7)\n* [8. 配置修改申请](#cc1-8)\n* [9. 注册用户申请](#cc1-9)\n* [10. 键值分析](#cc1-10)\n\n\n\nCacheloud系统提供完善的工单申请-审批流程。用户可以提交自己名下相关应用的工单申请，由管理员驳回/处理、通过后，一条完整的工单流程执行完毕。\n\n工单审批界面展示了\"工单处理汇总情况\"和\"审批列表\"，用户提交的申请在此进行审批处理。工单状态分为：待审、处理中、通过、驳回，工单受理后显示处理人。\n\n<img src=\"../../img/function/job/auditList.png\" width=\"100%\"> \n\n<a name=\"cc1-1\"/>\n\n### 1. 工单类型\n\n如下图所示，来自用户的工单有以下几种：\n\n+ 应用申请：用户需要申请开通redis standalone、redis sentinel和redis cluster；\n+ 导入应用：用户将现有redis实例导入cachecloud平台，进行集中管理；\n+ 应用扩容：用户需要对当前的应用进行内存容量扩容；\n+  应用数据迁移：新老应用间数据迁移；\n+  清理数据：删除固定前缀的key；\n+  应用诊断：诊断应用问题，包括扫描key，诊断bigkey/hotkey等，集群slot分析；\n+  应用下线：对无用应用进行下线处理，回收资源；\n+ 应用配置修改：用户希望对当前的redis配置做调整；\n+ 键值分析：用户申请分析应用BigKey/过期键/键值分布等情况；\n+ 注册用户申请：管理员只需要开通或驳回就可以了。\n\n<a name=\"cc1-2\"/>\n\n### 2. 开通应用\n\n\t(1). 不同类型的redis，开通使用不同的格式。\n\t(2). 一键开通中唯一需要的就是机器的IP。\n\t添加机器时，要综合考虑，用户提交关于客户端的基本信息：QPS、容量、机房、主从等信息，决定选用的什么配置、什么机房的机器。\n\n#### (1) redis-standalone开通\n- 如下图，按照步骤填选：\n\n<img src=\"../../img/function/server/server-standalone.png\" width=\"100%\"/>\n\n- 点击自动生成“部署预览”，确认部署信息后，点击“开始部署”：\n\n<img src=\"../../img/function/server/server-standalone-deploy.png\" width=\"100%\"/>\n\n#### (2) redis-sentinel开通\n\n步骤同上\n\n![](../../img/function/server/server-sentinel.png)\n\n#### (3) redis-cluster开通\n\n步骤同上\n\n![](../../img/function/server/server-cluster.png)\n\n详细操作可参考：[运维手册-应用部署](../../wiki/operate/appDeploy.md)。\n\n待应用导入开通，点击”通过“，该工单审批完成。\n\n<a name=\"cc1-3\"/>\n\n### 3. 应用导入\n\n在使用Cachecloud平台前，用户已有自己部署的redis实例，该功能支持将redis实例导入Cachecloud平台进行运维管理。用户提交的“导入应用”工单信息会记录在此，管理员点击“导入”或者\"查看\"可以追踪redis实例导入应用的情况。\n\n<img src=\"../../img/function/server/import-list.png\" width=\"100%\"/>\n\n应用导入共5个步骤，引导用户完成redis实例到Cachecloud应用的导入，具体操作请参考：[运维功能-应用导入](../../wiki/function/operation-import.md)。\n\n待应用导入完毕，点击”通过“，该工单审批完成。\n\n\n<a name=\"cc1-4\"/>\n### 4. 扩容申请\n\n- 垂直扩容\n在“扩容配置”一栏填写扩容后单实例最大内存即可\n\n<img src=\"../../img/function/server/server-expand.png\" width=\"100%\"/>\n\n- 水平扩容\n\n水平扩容相对麻烦且费时一些，在开通时候管理员尽量根据用户提交的信息(QPS，容量等)，尽量提前预支一些实例，如果还是抗不住，就可做水平扩容。\n\na. 添加一个redis-cluster节点，格式为masterIp:memSize:slaveIp，并meet到集群中；\n\nb. 迁移slot: 迁移slot速度较慢，CacheCloud支持slot断点续传的功能。\n\n<img src=\"../../img/function/server/server-expand-hor.png\" width=\"100%\"/>\n\n<a name=\"cc1-5\"/>\n\n### 5. 数据迁移\n\n支持redis数据同步工作，点击”审批处理“跳转到数据迁移界面，具体操作请参考：[运维功能-数据迁移](../../wiki/operate/migrateTool.md)。\n待数据迁移完毕，点击”通过“，该工单审批完成。\n\n<a name=\"cc1-6\"/>\n\n### 6. 数据清理\n\n待数据清理完毕，点击”通过“，该工单审批完成。\n\n<a name=\"cc1-7\"/>\n\n### 7. 应用诊断\n\n待应用诊断完毕，点击”通过“，该工单审批完成。\n\n<a name=\"cc1-8\"/>\n\n### 8. 配置修改申请\n填写需要修改的配置项和配置值。\n\n<img src=\"http://i1.itc.cn/20160125/3084_25e4c289_a9d6_ee95_6fd9_032effce7b8d_1.jpg\"  width=\"100%\"/>\n     \n<a name=\"cc1-9\"/>\n\n### 9. 键值分析\n点击“分析”，进行键值分析\n\n<img src=\"../../img/function/server/key-analysis.png\" width=\"100%\"/>\n\n跳转到键值分析任务流\n\n<img src=\"../../img/function/server/key-analysis-taskflow.png\" width=\"100%\"/>\n\n<a name=\"cc1-10\"/>\n\n### 10. 注册用户申请\n直接点击通过或者驳回即可。\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-machine.md",
    "content": "## 机器管理\n\n提供对机器机房的管理运维操作。\n\n### 机器管理\n\n新机器除了要用脚本进行初始化安装CacheCloud的环境以外，还要统一进行管理，CacheCloud后台提供了机器的增删改查功能，还有一些指标的(cpu，网络，负载)监控功能。\n\n<img src=\"../../img/function/server/server-machine.png\" width=\"100%\"/>\n\n支持批量添加、管理机器。\n\n<img src=\"../../img/function/server/server-machine-add.png\" width=\"60%\"/>\n\n\n注：CacheCloud之所以没有提供完整的机器监控功能，是因为各个公司一般都有自己专门的机器运维和监控工具，其中或自己开发或使用像ganglia、nagios等软件搭建。\n\n#### 机器实例管理\n\n点击机器管理页面列表中ip一列中的某个ip即可跳转到机器实例管理页面，提供机器下所有实例滚动重启、指定实例一键迁移、单实例的下线等功能。\n\n<img src=\"../../img/function/server/server-machine-instance.png\" width=\"60%\"/>\n\n### 机房管理\n\n同时支持添加管理机器。\n\n<img src=\"../../img/function/server/server-room.png\" width=\"100%\"/>\n\n支持批量添加、管理机器。\n\n<img src=\"../../img/function/server/server-room-add.png\" width=\"60%\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-migrate.md",
    "content": "## 数据迁移\n\n”数据迁移“帮助完成应用间的数据迁移、同步功能。\n\n### 一、CacheCloud数据迁移工具能做什么？\n\nCachecloud数据迁移工具支持redis数据同步工作，可以完成如下功能：\n\n+ 支持任意两种类型的source和target进行数据迁移，如RDB文件、Redis Standalone、Redis Sentinel、Redis Cluster、CacheCloud应用；\n+ 迁移过程中，源集群不影响对外提供服务；\n+ 高效性，多线程并发迁移；\n+ 迁移过程便捷，流程可视化；\n+ 集成redis-migrate-tool、redis-shake redis数据同步的工具。\n+ 数据迁移具有实时性，所以使用合理可以基本保证数据一致性(原理可以参考第二小节)；\n+ 数据校验功能。\n\n<img width=\"100%\" src=\"../../img/operate/migrate/migratetool.png\"/>\n\n### 二、CacheCloud数据迁移工具是如何实现的？\n\nCacheCloud数据迁移工具底层使用了redis-migrate-tool和redis-shake两种主流redis数据同步的工具。所以这里有必要对这两种数据迁移工具做简单说明。\n\n+ [redis-migrate-tool](https://github.com/vipshop/redis-migrate-tool)\n\n    redis-migrate-tool是用c语言开发的Redis数据迁移工具，可以做到在stadalone、sentinel、cluster、rdb(不支持做为target)彼此迁移数据，\n服务于唯品会公司数千个Redis节点，从数据迁移的准确性、稳定性、高效性等方面都能满足的生产环境的需求。  \n使用文档：[https://github.com/vipshop/redis-migrate-tool](https://github.com/vipshop/redis-migrate-tool)\n\n+ [redis-shake](https://github.com/alibaba/RedisShake)\n\n    redis-migrate-tool仅支持Redis3-4的迁移，不支持redis5-5/4/3 版本数据同步(rdb文件解析异常)，而且已经不在维护，故升级为阿里云开源的redis-shake工具。\n    该工具友好地支持2.8-5.0版本的数据同步/解析、恢复、备份，支持多种部署结构迁移场景，后续会支持断点续传、多活等，且由Redis&MongoDB团队持续优化和维护。  \n    使用文档： [https://github.com/alibaba/RedisShake](https://github.com/alibaba/RedisShake)\n         \n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;迁移工具是基于复制的原理，所以是实时迁移的，这点比起redis自带的redis-trib.rb的import 功能要方便很多。\n故CacheCloud选择它们作为数据迁移的基础组件，通过可视化的方式完成参数配置、节点数据迁移、进度查询、日志查询、配置查询和历史记录等一系列功能，使得数据迁移更简单、便捷。\n\n<img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-tool.png\"/>\n\n### 三、CacheCloud数据迁移工具如何部署和使用\n\n1. 准备迁移工具机器\n    + 初始化机器，在“管理后台-机器管理”页面点击“添加新机器”，请参考相关文档；\n    + 添加机器时候，选择机器类型为“Redis迁移工具机器”，建议单独使用一台机器做迁移，因为迁移的过程可能会占用机器的很多资源。  \n![](../../img/operate/migrate/cc-migrate-machine.png)\n\n2. 安装部署redis-migrate-tool/redis-shake  \n    + redis-migrate-tool  \n在“管理后台-系统配置管理”中配置安装目录，  \n![](../../img/operate/migrate/cc-migrate-redis-migtool-dir.png)  \n    安装方法可以参考redis-migrate-tool主页或者按照如下安装:\n        ```\n        $ cd /opt/cachecloud/\n        $ wget https://github.com/vipshop/redis-migrate-tool/archive/master.zip\n        $ mv master master.zip\n        $ unzip master.zip\n        $ mv redis-migrate-tool-master redis-migrate-tool\n        $ cd redis-migrate-tool\n        $ mkdir data\n        $ autoreconf -fvi\n        $ ./configure\n        $ make\n        $ src/redis-migrate-tool -h\n        ```\n      最为重要的一步是, 注意这里的cachecloud-open是ssh的用户名\n      ```\t\n      $ chown -R ${cachecloud-ssh-username}.${cachecloud-ssh-username} /opt/cachecloud/redis-migrate-tool\n      ```\n    + redis-shake  \n    在“管理后台-系统配置管理”中配置安装目录：  \n    ![](../../img/operate/migrate/cc-migrate-redis-shake-dir.png)  \n    安装方法可以参考redis-shake主页或者按照如下安装：\n        ```\n        1.下载redis-shake安装包，下载地址 https://github.com/alibaba/RedisShake/releases\n        2.解压到/opt/cachecloud/redis-shake目录下\n        3.创建文件夹conf（存放配置文件）、 logs（存放日志）和pid（存放进程信息）\n        4.下载redis-full-check安装包，下载地址 https://github.com/alibaba/RedisFullCheck/releases\n        5.解压到/opt/cachecloud/redis-full-shake目录下\n        6.创建文件夹data（存放比对数据结果）\n        ```\n3. 添加迁移任务  \n    a. 点击“迁移数据工具”进入迁移数据记录列表页面。  \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-enter-migtool.png\"/>  \n    b. 在移数据记录列表中可以查看历史迁移任务信息，任务的配置文件、迁移状态、迁移日志，停止任务，进行数据校验等运维操作。\n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-tasklist.png\"/>  \n    c. 点击“点击添加新的迁移按钮”，选择迁移工具，迁移机器，配置源和目标信息，点击验证按钮，按照通过后就开启了一个迁移的任务，回到迁移列表，就可以观察迁移日志、关闭迁移任务、迁移状态查询等。   \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-addtask.png\"/>  \n  \n4. 数据校验  \n  - 数据迁移完毕后，点击“数据校验”按钮，跳转到如下界面，说明校验程序已经启动：  \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-start-check.png\"/>  \n  - 可通过点击“校验日志”，查看log（默认log 5s刷新）：  \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-checklog.png\"/>  \n  - 查看数据校验结果    \n    校验数据报错在校验机器的/opt/cachecloud/redis-full-check/data目录下：  \n    ![](../../img/operate/migrate/cc-migrate-checkres.png)  \n    查看不一致结果记录到result文件：  \n    ![](../../img/operate/migrate/cc-migrate-checkres2.png)  \n\n### 四、客户端怎么迁移\n1. 当迁移工作基本完成后，我们就需要迁移客户端了，为了方便演示我们假设只有两个客户端。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client1.png\"/> \n2. 迁移第一个客户端，观察客户端是否出现异常。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client2.png\"/>  \n3. 迁移第二个客户端，继续观察。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client3.png\"/>  \n4. 检查source应用是否还有调用。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client4.png\"/>  \n5. 下线source应用和迁移工具。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client5.png\"/>   \n\n### 五、一些建议\n- 尽可能单独找一台机器作为迁移机器，因为迁移的过程可能会占用机器的很多资源；\n- redis-migrate-tool/redis-shake很具体的原理问题和细节可以到社区或CacheCloud群里找作者提问；\n- redis-shake使用或迁移日志中的问题可以参考：  \n    [https://github.com/alibaba/RedisShake/wiki/FAQ](https://github.com/alibaba/RedisShake/wiki/FAQ)  \n    [https://github.com/alibaba/RedisShake/issues](https://github.com/alibaba/RedisShake/issues)\n- 迁移工具页面还有很多要优化的地方，后期会听取大家意见逐渐改善。\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-module.md",
    "content": "## Redis模块管理\n\n此功能是Redis module模块管理，用于部署或修改应用时，如指定安装某些redis module，则可根据模块管理中的配置获取指定模块包并安装。\n\n支持模块的新增和移除，及各模块中版本管理\n\n<img width=\"100%\" src=\"../../img/function/module/redis-module.png\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-resource.md",
    "content": "## 系统资源管理\n\n##### 目录\n\n* [一、目的](#cc1)\n* [二、仓库配置](#cc2)\n* [三、目录管理](#cc3)\n* [四、脚本管理](#cc4)\n* [五、Redis资源管理](#cc5)\n* [六、迁移工具资源管理](#cc6)\n\n\n<a name=\"cc1\"/>\n\n### 一、目的\n\nCacheCloud系统需要依赖一些脚本，Redis资源包，迁移工具，公钥/私钥等资源，由于比较零碎，此项功能用于对资源及其版本同一管理控制，简化运维提升效率。\n主要分以下几大类：\n\n- 仓库配置：配置资源的仓库地址，可用于推送和下载资源；\n- 目录管理：区分各类资源的目录结构，便于管理资源；\n- 脚本管理：管理系统依赖资源的脚本；\n- Redis资源管理： 管理redis资源包不同版本及推送下载；\n- 迁移工具资源管理：管理迁移工具不同资源包版本及推送下载；\n\n<a name=\"cc2\"/>\n\n### 二、仓库配置\n\n<img src=\"../../img/operate/version/respo.png\" width=\"100%\"/>\n\n远程仓库地址：可访问的仓库ip地址；例如:10.%.%.%\n\n根目录：仓库资源的根目录；例如:/opt/download/software/cachecloud/resource\n\n资源下载地址：可访问资源地址；例如:http://%.%.sohuno.com/software/cachecloud/resource\n\n<a name=\"cc3\"/>\n\n### 三、目录管理\n\n<img src=\"../../img/operate/version/dir.png\" width=\"100%\"/>\n\n对不同类型资源创建不同目录，需要做推送后及在远程仓库创建对应资源目录(格式:/${dir})；\n\n<a name=\"cc4\"/>\n\n### 四、脚本管理\n\n<img src=\"../../img/operate/version/script.png\" width=\"100%\"/>\n\n对系统依赖脚本进行版本管理，可在编译内容和推送脚本到仓库上；\n\n<a name=\"cc5\"/>\n\n### 五、Redis资源管理\n\n<img src=\"../../img/operate/version/redis.png\" width=\"100%\"/>\n\nRedis资源管理支持对Redis大小版本做统一管理和版本控制(3.0.x -> 3.2.x -> 4.0.x -> 5.0.x -> 6.0.0-rc)的不断迭代更新，项目也需要用到不同版本和新特性。\n\n#### 5.1.新增资源包\n\n- 1).Redis版本格式规范，验证格式：redis-主.子.增量版本\n- 2).安装目录规范：/opt/cachecloud/redis-主.子.增量版本\n- 3).是否备份：可直接继承之前版本配置模板，进行增量修改配置\n\n#### 5.2.修改配置\n\n点击`修改配置`，可新增配置项：\n\n<img src=\"../../img/operate/version/versionConfig.png\" width=\"60%\"/>\n\n#### 5.3、应用版本升级\n\n只支持小版本增量升级(Redis-3.2.x/Redis-4.0.x/Redis-5.0.x):\n例如:Redis-5.0.6 升级到 Redis-5.0.7\n\n<img src=\"../../img/operate/version/upgrade.png\" width=\"100%\"/>\n\n点击`版本升级`，可升级当前应用版本: \n\n<img src=\"../../img/operate/version/upgrade-process.png\" width=\"60%\"/>\n\n升级完后，若为升级版本5.0.7为当前管理的最终小版本号，则应用按钮会显示为`最新版本`。\n\n<a name=\"cc6\"/>\n\n### 六、迁移工具资源管理\n\n<img src=\"../../img/operate/version/tool.png\" width=\"100%\"/>\n\n对系统迁移工具版本管理，方便对资源的可控和管理；\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-schedule.md",
    "content": "## 调度任务\n\n对CacheCloud平台的定时任务进行管理，可开启/暂停/删除某个定时任务。\n\n<img width=\"100%\" src=\"../../img/function/server/cc-trigger.png\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-systemalert.md",
    "content": "## 系统配置管理\n\n对CacheCloud平台的基本配置信息进行管理。具体配置模板信息如下：\n\n<img width=\"100%\" src=\"../../img/function/server/cc-redisConfiguration.png\"/>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-task.md",
    "content": "## Redis任务流管理\n\nCacheCloud平台引入任务流机制，将用户操作提交为异步任务，复杂任务拆分为多个子任务，每个子任务又按照步骤依次执行，每个任务/步骤直接是相互独立，可查看任务关系、任务信息、执行状态、运行日志等信息。\n\n<img width=\"100%\" src=\"../../img/function/server/taskmanage.png\"/>\n\n- 下面以“RedisClusterAppDeployTask”任务为例进行使用讲解。\n\n<img width=\"100%\" src=\"../../img/function/server/cc-taskflow-appDeplogy.png\"/>\n\n1. 管理员提交“redis-cluster开通”任务（参考[应用运维-Redis Cluster](#cc2-1-cluster)）；\n2. 系统自动生成“RedisClusterAppDeployTask”*（部署redis-cluster应用）父任务及多个“RedisServerInstallTask”（在机器上安装redis server）子任务，可查看各任务的基本信息、状态、进度等信息；\n3. 点击“执行步骤”可查看任务的具体执行步骤，包括任务的基本信息（各种参数），任务流/步骤列表，任务详细日志（帮助问题排查）；\n4. 对运行中的任务可以暂停，对暂停/失败的任务亦可提交“重试任务”。\n\n<img width=\"100%\" src=\"../../img/function/server/cc-taskflow-appDeplogy-detail1.png\"/>\n\n<img width=\"100%\" src=\"../../img/function/server/cc-taskflow-appDeplogy-detail2.png\"/>\n\n<img width=\"100%\" src=\"../../img/function/server/cc-taskflow-appDeplogy-detail3.png\"/>\n  \n<img width=\"100%\" src=\"../../img/function/server/cc-taskflow.png\"/>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-template.md",
    "content": "## Redis配置模板管理\n\n此功能是Redis全局配置模板(每次开启应用时用到)，并非用于修改线上配置。可对redis的不同版本不同配置项进行增删改查操作，请谨慎修改。\n\n<img width=\"100%\" src=\"../../img/function/server/cc-configuration.png\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operation-user.md",
    "content": "## 用户管理\n\n管理CacheCloud平台用户信息。\n\n<img src=\"../../img/function/server/cc-user.png\" width=\"100%\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/operations.md",
    "content": "CacheCloud除了有面向用户的界面，还有面向管理员的运维界面，帮助管理员做一些如工单处理、日常运维管理等工作。\n按照功能可分为一下六大块功能：\n\n- 数据统计\n- 运维功能\n- 配置管理\n- 任务管理\n- 用户管理\n- 系统通知\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/server-statistic.md",
    "content": "## 服务端指标统计汇总\n\n- 1.应用内存指标:用于快速发现需要伸缩内存的应用，统计了内存使用率、分片数、客户端连接数等指标。\n<img src=\"../../img/function/mem.png\" width=\"100%\">\n     \n- 2.应用碎片率指标:用于快速发现高碎片率应用，统计了rss内存使用、redis版本、键数量等指标。\n<img src=\"../../img/function/fragratio.png\" width=\"100%\"> \n      \n- 3.应用拓扑诊断指标:用于快速发现应用拓扑是否异常，例如:主从节点是否同一台机器、故障转移是否满足、宿主环境资源分布等。      \n<img src=\"../../img/function/topology.png\" width=\"100%\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/server-statistic.toc.md",
    "content": "##### 目录\n\n* [一、内存指标](#cc1)\n* [二、碎片率指标](#cc2)\n* [三、应用拓扑诊断](#cc3)\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/statistics.md",
    "content": "\n## 服务端全局统计\n\n- 1.资源总览：统计所有在线运行应用数量、实例数量、机器数量、redis版本数量以及机器内存分配和使用情况汇总。\n    <img width=\"100%\" src=\"../../img/function/server/total.png\"/>\n\n- 2.内存使用分布及统计：统计不同机房维度机器内存使用率、分配率情况。\n\n    <img width=\"100%\" src=\"../../img/function/server/machine-mem.png\"/>\n    <img width=\"100%\" src=\"../../img/function/server/machine-room.png\"/>\n\n\n- 3.调度任务统计：当前系统运行调度任务数量。\n\n    <img width=\"100%\" src=\"../../img/function/server/quartz.png\"/>\n\n- 4.任务流统计: 当前系统执行所有任务流的状态：总任务数/成功任务数/失败任务数。\n\n    <img width=\"100%\" src=\"../../img/function/server/task.png\"/>\n\n---------- \n    "
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/function/system-alert.md",
    "content": "## 系统通知\n\n支持发布CacheCloud平台系统通知。\n\n<img src=\"../../img/function/server/system-alter.png\" width=\"100%\"/>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/intro/index.md",
    "content": "### 目录\n* [一、CacheCloud发展](#cc1)\n* [二、CacheCloud是什么](#cc2)\n* [三、CacheCloud功能架构](#cc3)\n* [四、CacheCloud使用规模](#cc4)\n* [五、CacheCloud资源部署VS云厂商](#cc5)  \n* [六、ECS服务器试用版本](#cc6) \n* [七、FAQ快速接入](#cc7)\n* [八、支持与帮助](#cc8)\n\n\n<a name=\"cc1\"/>\n\n## 一、CacheCloud发展\n\n[![Stargazers over time](https://starchart.cc/sohutv/cachecloud.svg)](https://starchart.cc/sohutv/cachecloud)\n\nCacheCloud项目是从2016年以来一直在持续迭代开发,**如果你喜欢这个项目[CacheCloud试用版本](http://43.137.44.6:8080/admin/app/list),欢迎Star多多支持.**  \n\n为了给大家带来更好的CacheCloud使用体验,希望您能在issue里提供宝贵建议：https://github.com/sohutv/cachecloud/issues/281\n\n也感谢大家一直对项目支持：\n\n[![Stargazers repo roster for @sohutv/cachecloud](https://reporoster.com/stars/sohutv/cachecloud)](https://github.com/sohutv/cachecloud/stargazers)\n[![Forkers repo roster for @sohutv/cachecloud](https://reporoster.com/forks/sohutv/cachecloud)](https://github.com/sohutv/cachecloud/network/members)\n\n<a name=\"cc2\"/>\n\n## 二、CacheCloud是什么\n\nCacheCloud是一个Redis云管理平台：支持Redis多种架构(Standalone、Sentinel、Cluster)高效管理、有效降低大规模redis运维成本，提升资源管控能力和利用率。平台提供快速搭建/迁移，运维管理，弹性伸缩，统计监控，客户端整合接入等功能。\n\n![cachecloud云平台](../../img/readme/cachecloud.png)\n\n<a name=\"cc3\"/>\n\n## 三、CacheCloud功能架构\n\n+ Redis搭建：环境初始化、实例部署安装、类型架构支持；\n+ 客户端接入：Java-SDK接入、客户端监控、其他语言接入；\n+ 运维管理：宿主环境、资源管理、应用审计、应用运维、应用质量监控、应用拓扑诊断；\n+ 弹性伸缩：资源评估、垂直伸缩、水平伸缩、外部接入；\n+ 统计监控：指标采集、应用统计、节点统计、机器统计、监控报警、问题诊断；\n<img src=\"../../img/readme/CacheCloud功能架构.png\" width=\"100%\"/>\n\n<a name=\"cc4\"/>\n\n## 四、CacheCloud使用规模\n\n+ 400亿+ commands/day\n+ 15T+ Memory Total\n+ 300+ app Total / 3000+ Instances Total\n+ 200+ Machines Total\n\n<a name=\"cc5\"/>\n\n## 五、CacheCloud资源部署VS云厂商\n\n<img src=\"../../img/readme/sentinel-cost.png\" width=\"50%\"/><img src=\"../../img/readme/cluster-cost.png\" width=\"50%\"/>\n\n<div align=\"center\">Redis 主从/集群部署成本:</div>\n\n<a name=\"cc6\"/>\n\n## 六、ECS服务器试用版本\n+ CacheCloud后台地址：[地址](http://43.137.44.6:8080/admin/app/list)\n+ 新用户注册成功可试用Redis集群\n+ 开源版本试用截止时间：2025-02-01，如果大家有空闲公网资源可以贡献，请[联系我们](#cc8)\n\n<a name=\"cc7\"/> \n\n## 七、FAQ快速接入\n+ [快速开始](../../wiki/quickstart/index.md)\n+ [客户端接入](../../wiki/access/client.md)\n\n<a name=\"cc8\"/>\n\n## 八、支持与帮助\n\n+ QQ群:\n\n   CacheCloud开发运维 已满\n   \n   CacheCloud开发运维2群:894022242\n   \n   CacheCloud开发运维3群:908821300\n   \n+ 微信群:发布Cachecloud最新动态，帮大家减轻工作负担。\n\n<img src=\"http://photocdn.tv.sohu.com/img/cachecloud/weixin.jpg\" width=\"40%\"/>\n\n<img src=\"../../img/readme/subcribe.png\" width=\"40%\"/>\n\n+ 微信：如果大家有公网资源可以联系我，会加入到开源版本服务资源部署试用，提高大家的用户体验。\n\n<img src=\"../../img/readme/wechat.png\" width=\"40%\"/>\n\n如果你觉得CacheCloud对你有帮助，欢迎Star。\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/intro/redisVersion.md",
    "content": "Redis开源代码地址：[https://github.com/antirez/redis](https://github.com/antirez/redis)，作者blog地址：http://antirez.com/\n目前Redis各大版本lastest-release如下：\n\n> **<span id=\"r5\">6.0-rc</span>**\n\n[redis6.0-changlog](https://raw.githubusercontent.com/antirez/redis/6.0/00-RELEASENOTES)\n\n````\n1.新的模块系统：提供新的模块API，模块可实现在RDB中存储数据，关联服务器事件，捕获重写命令，根据key阻塞客户端等功能 \n2.重写过期淘汰：新的实现支持动态调整且能够更高效的回收过期键。\n3.SSL加密：所有的通信管道支持SSL。\n4.ACL支持: 控制用户只能访问某些命令以及对某些键的访问。\n5.新协议RESP3: 新协议可以返回更多响应语义，新客户端可以从该协议中读取更多调用内容。\n6.协助客户端缓存: Redis可以辅助优化客户端缓存实现。该功能仍处于开发阶段后续会有调整，更多内容: https://redis.io/topics/client-side-caching  \n7.多线程IO: Redis可使用多线程处理网络数据的读写和协议解析，在不使用pipeline情况下，提升redis2倍的IO执行效率。\n8.无盘复制: 从节点也支持无盘复制，开启配置后从节点可以从socket缓冲区加载RDB数据到内存。\n9.基准测试：redis-benchmark 现在支持集群模式。\n10.随机命令：优化SRANDMEMBER等随机命令的分布\n11.redis-cli优化\n12.重写Systemd支持\n13.发布集群代理架构：https://github.com/artix75/redis-cluster-proxy\n14.发布Disque(消息队列)模块：https://github.com/antirez/disque-module\n````\n\n> **<span id=\"r4\">5.0.7</span>**\n\n[redis5.0 changlog](https://raw.githubusercontent.com/antirez/redis/5.0/00-RELEASENOTES)\n\n````\n1.新的流数据类型(Stream data type) https://redis.io/topics/streams-intro\n2.新的 Redis 模块 API：定时器、集群和字典 API(Timers, Cluster and Dictionary APIs)\n3.RDB 增加 LFU 和 LRU 信息\n4.集群管理器从 Ruby (redis-trib.rb) 移植到了redis-cli 中的 C 语言代码\n5.新的有序集合(sorted set)命令：ZPOPMIN/MAX 和阻塞变体(blocking variants)\n6.升级 Active defragmentation 至 v2 版本\n7.增强 HyperLogLog 的实现\n8.更好的内存统计报告\n9.许多包含子命令的命令现在都有一个 HELP 子命令\n10.客户端频繁连接和断开连接时，性能表现更好\n11.许多错误修复和其他方面的改进\n12.升级 Jemalloc 至 5.1 版本\n13.引入 CLIENT UNBLOCK 和 CLIENT ID\n14.新增 LOLWUT 命令 http://antirez.com/news/123\n15.在不存在需要保持向后兼容性的地方，弃用 \"slave\" 术语\n16.网络层中的差异优化\n17.Lua 相关的改进\n18.引入动态的 HZ(Dynamic HZ) 以平衡空闲 CPU 使用率和响应性\n19.对 Redis 核心代码进行了重构并在许多方面进行了改进\n````\n\n\n> **<span id=\"r3\">4.0.14</span>**\n\n[redis4.0 changlog](https://raw.githubusercontent.com/antirez/redis/4.0/00-RELEASENOTES)\n\n```\n1.Redis模块系统 modules system\n2.psync2全量/增量数据同步增强\n3.Lazyfree异步机制\n4.支持aof+rdb混合持久化 \n5.内存分析及优化(memory help查看支持内存命令) \n6.内存碎片整理\n```\n\n> **<span id=\"r2\">3.2.12</span>**\n\n[redis3.2 changlog](https://raw.githubusercontent.com/antirez/redis/3.2/00-RELEASENOTES)\n\n> **<span id=\"r1\">3.0.7</span>**\n\n[redis3.0 changlog](https://raw.githubusercontent.com/antirez/redis/3.0/00-RELEASENOTES)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/intro/redisVersion.toc.md",
    "content": "##### redis lastest release版本说明\n\n* 一、[6.0.4](#r5)\n* 二、[5.0.9](#r4)\n* 三、[4.0.14](#r3)\n* 四、[3.2.12](#r2)\n* 五、[3.0.7](#r1)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/intro/releaseNote.md",
    "content": "## 以下是关于CacheCloud定制客户端版本变更记录\n\n### <span style=\"color:green\">CacheCloud定制客户端分类说明：</span>\n\nCacheCloud 3.0以上版本中已移除cachecloud-client模块，请移至[cachecloud-client](https://github.com/sohutv/cachecloud-client)开源地址查看。\n\n- 1.[cachecloud-client-redis](#j1) (基于Jedis定制客户端)\n- 2.[cachecloud-jedis](#j2) (定制完善Jedis客户端)\n- 3.[cachecloud-client-crossroom-redis](#j3) (跨机房容灾高可用客户端)\n- 4.[cachecloud-client-lettuce](#j4) (基于lettuce定制客户端)\n- 5.[cachecloud-client-spectator](#j5) (客户端上报类库)\n\n\n### 一、**<span id=\"j1\">cachecloud-client-redis客户端</span>**\n\n> <span id=\"j10\">2.0.1版本</span>\n\n  - **5. 版本号: 2.0.1-RELEASE**\n      + 4.1. 发布日期：2020-03-17\n      + 4.2. 重要变更：    \n        ````\n         1) 增加对指标收集器StatCollector非空判断，为空则不上报指标，兼容不通过Builder构造Jedis实例的情况；\n         2) 优化命令调用逻辑，解决因为命令调用较少导致的漏报和延迟上报的问题。\n        ````\n\n> <span id=\"j11\">2.0.0版本</span>\n\n  - **4. 版本号: 2.0.0-RELEASE**\n      + 4.1. 发布日期：2020-02-12\n      + 4.2. 重要变更：    \n        ````\n         1) 支持Redis 5 API，Jedis相关类路径变更；\n         2) 重构Jedis客户端，增加命令超时和连接异常等指标统计上报逻辑；\n         3) RedisClusterBuilder/RedisSentinelBuilder/RedisStandaloneBuilder客户端对同一appid只实例化一次。\n        ````\n      \n> <span id=\"j12\">1.7.0版本</span>\n\n   - **3. 版本号: 1.7.1-SNAPSHOT**\n     \n      + 3.1. 发布日期：2019-03-21\n      + 3.2. 重要变更：\n         ````\n         1) 优化Redis集群下应用只用pipeline批量读写，当redis节点变更或下线，客户端无法获取到新节点拓扑。\n         ````\n      \n   - **2. 版本号: 1.7.0-SNAPSHOT**\n     + 2.1 发布日期：2018-09-14\n     + 2.2 重要变更：\n         ````\n         1) 支持standalone/sentinel/cluster在线密码修改；\n         2) 修复上报因版本编译产生的内存泄漏问题。\n         ````\n\n> <span id=\"j13\">1.6.0版本</span>\n\n   - **1. 版本号: 1.6.0-SNAPSHOT**\n     + 1.1 发布日期：2018-09-14\n     + 1.2 重要变更：\n         ````\n         1) 支持connectTimeOut和soTimeOut参数，同时兼容老版本的setTime方法；\n         2) 增加支持客户端上报开关；\n         3) 客户端键值区间支持。\n         ````\n\n### 二、**<span id=\"j2\">Jedis</span>**\n\n> <span id=\"j20\">3.1.0版本</span>\n\n- **3. 版本号: 3.1.0-CC-2-RELEASE**\n     + 2.1 发布日期：2020-03-17\n     + 2.2 重要变更：\n         ````\n         1) 加上对指标收集器StatCollector非空判断，为空则不上报指标，兼容不通过Builder构造Jedis实例的情况。\n         ````\n\n- **3. 版本号: 3.1.0-CC-1-RELEASE**\n     + 2.1 发布日期：2020-02-12\n     + 2.2 重要变更：\n         ````\n         1) 合并官方jedis3.1.0版本；\n         2) 支持Redis 5相关api。\n         ````\n\n> <span id=\"j22\">2.9.0版本</span>\n\n- **2. 版本号: 2.9.0-CC-4-SNAPSHOT**\n     + 1.1 发布日期：2019-06-28\n     + 1.2 重要变更：\n         ````\n          1) 合并官方jedis2.9.0版本；\n          2) 优化集群风暴。\n         ````\n- **1. 版本号: 2.9.0-CC-3-SNAPSHOT**\n     + 1.1 发布日期：2018-09-14\n     + 1.2 重要变更：\n         ````\n          1) 支持standalone/sentinel/cluster在线密码修改。\n         ````\n\n### 三、**<span id=\"j3\">cachecloud-client-crossroom-redis</span>**\n\n><span id=\"j31\">1.4.0版本</span>\n\n- **3. 版本号: 1.4.0-RELEASE**\n  \n     + 3.1 发布日期：2020-02-12\n     + 3.2 重要变更：\n         ````\n         1) 升级Jedis版本至3.1.0-CC-1-RELEASE，cachecloud-client-redis版本至2.0.0-RELEASE；\n         ````\n><span id=\"j32\">1.3.2版本</span>\n- **2. 版本号: 1.3.2-SNAPSHOT**\n     + 2.1 发布日期：2019-12-09\n     + 2.2 重要变更：\n       \n         ````\n         1) 跨机房api支持命令zadd、lpop、llen、rpush、zrem、mzadds、mHgetAll、sadd、sismember、scard等。\n         ````\n><span id=\"j33\">1.2版本</span>\n- **1. 版本号: 1.2-SNAPSHOT**\n     + 1.1 发布日期：2016-09-28\n     + 1.2 重要变更：\n         ````\n         1) 修改客户端初始化方式。\n         ````\n\n### 四、**<span id=\"j4\">cachecloud-client-lettuce</span>**\n\n><span id=\"j41\">1.0版本</span>\n- **2. 版本号: 1.0-RELEASE**\n     + 1.1 发布日期：2020-02-20\n     + 1.2 重要变更：\n         ````\n         1) 升级原生lettuce至5.2.0.RELEASE版本。\n         ````\n- **1. 版本号: 1.0-SNAPSHOT**\n     + 1.1 发布日期：2018-12-24\n     + 1.2 重要变更：\n         ````\n         1) 增加CacheCloud初始化方式。\n         ````\n\n### 五、**<span id=\"j5\">cachecloud-client-spectator</span>**\n\n><span id=\"j51\">1.0版本</span>\n- **2. 版本号: 1.0.1-RELEASE**\n     + 1.1 发布日期：2020-03-17\n     + 1.2 重要变更：\n         ````\n         1) 优化命令调用逻辑，解决因为命令调用较少导致的漏报和延迟上报的问题。\n         ````\n\n- **1. 版本号: 1.0.0-RELEASE**\n     + 1.1 发布日期：2020-02-12\n     + 1.2 重要变更：\n         ````\n         1) 重构Jedis命令调用和异常上报逻辑。\n         ````"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/intro/releaseNote.toc.md",
    "content": "##### 客户端变更版本目录\n\n* 一、[cachecloud-client-redis](#j1)\n    - [2.0.0版本](#j11)\n    - [1.7.0版本](#j12)\n    - [1.6.0版本](#j13)\n* 二、[jedis](#j2)\n    - [3.1.0-cc版本](#j21)\n    - [2.9.0-cc版本](#j22)\n* 三、[cachecloud-client-crossroom-redis](#j3)\n    - [1.4.0版本](#j31)\n    - [1.3.2版本](#j32)\n    - [1.2版本](#j33)\n* 四、[cachecloud-client-lettuce](#j4)\n    - [1.0版本](#j41)\n* 五、[cachecloud-client-spectator](#j5)\n    - [1.0版本](#j51)\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/appAlert.md",
    "content": "## 一、应用报警配置\n\n应用报警是指用户对自己申请的应用重要指标设置的报警，主要监控应用的内存使用率，客户端连接数等指标。\n\n在用户申请应用时，CacheCloud会要求设置内存报警和客户端连接数报警的阈值；\n\n<img src=\"../../img/operate/alert/alert-when-apply.png\" width=\"100%\"/>\n\n对已存在的应用，通过CacheCloud后台应用详情页面，可查看当前应用报警指标的配置，以及配置接收报警的用户；\n\n<img src=\"../../img/operate/alert/app-alert.png\" width=\"100%\"/>\n\n点击“应用报警配置”，对报警的阈值进行修改。\n\n<img src=\"../../img/operate/alert/app-alert-config.png\" width=\"100%\"/>\n\n## 二、Redis报警配置\n\nRedis报警是CacheCloud后台对用户申请的应用和Redis实例相关监控指标设置的报警。\n\n进入CacheCloud后台页面，在Redis报警阈值页面，可看到目前配置的全局监控指标和特殊实例监控指标：\n\n<img src=\"../../img/operate/alert/redis-alert-common.png\" width=\"100%\"/>\n\n<img src=\"../../img/operate/alert/redis-alert-special.png\" width=\"100%\"/>\n\nRedis全局报警指标如下：\n\n| 配置名 | 说明  | 监控周期 |  关系  |  参考阈值 |\n| :-------------------------- |:----------------------------- | :------------------------------------------:|  :------------------------------------------:|  :------------------------------------------:|\n| aof_current_size | aof当前尺寸(单位：MB) | 30分钟 | 大于 | 6000 |\n| aof_delayed_fsync | 分钟aof阻塞个数 | 1分钟 | 大于 | 3 |\n| client_biggest_input_buf | 输入缓冲区最大buffer大小(单位：MB) | 1分钟 | 大于 | 10 |\n| client_longest_output_list | 输出缓冲区最大队列长度 | 1分钟 | 大于 | 50000 |\n| instantaneous_ops_per_sec | 实时ops | 1分钟 | 大于 | 60000 |\n| latest_fork_usec | 上次fork所用时间（单位：微妙 | 1天 | 大于 | 400000 |\n| mem_fragmentation_ratio | 内存碎片率（检测大于500MB的） | 1天 | 大于 | 1.5 |\n| rdb_last_bgsave_status | 上一次bgsave状态 | 1小时 | 不等于 | ok |\n| total_net_output_bytes | 分钟网络输出流量(单位：MB) | 1分钟 | 大于 | 5000 |\n| total_net_input_bytes | 分钟网络输入流量(单位：MB)\t| 1分钟 | 大于 | 1200 |\n| sync_partial_err | 分钟部分复制失败次数 | 1分钟 | 大于 | 0 |\n| sync_partial_ok | 分钟部分复制成功次数 | 1分钟 | 大于 | 0 |\n| sync_full | 分钟全量复制执行次数 | 1分钟 | 大于 | 0 |\n| rejected_connections | 分钟拒绝连接数\t | 1分钟 | 大于 | 0 |\n| master_slave_offset_diff | 主从节点偏移量差(单位：字节)\t | 5分钟 | 大于 | 20000000 |\n| cluster_state | 集群状态 | 1分钟 | 不等于 | ok |\n| cluster_slots_ok | 集群成功分配槽个数\t | 1分钟 | 不等于 | 16384 |\n\nCacheCloud默认按照全局报警配置监控所有Redis实例，如果某些应用或者实例需要特殊配置报警阈值，可通过以下按钮，添加特殊报警项。\n\n<img src=\"../../img/operate/alert/redis-alert-special-config.png\" width=\"100%\"/>\n\n \t\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/appDeploy.md",
    "content": "\n\n## 应用部署\n\n​    用户提交应用申请（详细操作可参考：[系统功能-客户端.应用申请](../../wiki/function/client.md)）后，管理员在可在后台-流程审批页面驳回或开通应用，如下图：\n\n<img src=\"../../img/operate/appDeploy/app_deal.png\" width=\"100%\"/>\n\n### 驳回应用需要填写原因  \n<img src=\"../../img/operate/appDeploy/app_reject.png\" width=\"60%\"/>  \n\n### 开通应用  \n点击“审批处理”开始应用部署，如下图：   \n<img src=\"../../img/operate/appDeploy/app_deploy.png\" width=\"100%\"/>  \n应用部署可分为以下步骤：  \n\n1. 查看应用申请详情，如存储种类，是否测试、客户端机房信息、内存申请详情、Redis版本等，按照申请要求开通应用，可做适当调整。\n2. 填写应用基础信息，应用级别：S/A/B(默认)/c，重要性依次降低；选择Redis版本；给应用（Redis）的默认密码。\n3. 填写应用部署信息，\n      - 应用类型：RedisStandalone/sentinel+Redis/RedisCluster等；\n\n      - maxMemory：单个实例的内存，单位MB；\n\n      - 选择部署Redis机器及每台机器部署的实例数。机器信息分为四个部分：机器ip（代表所属机房），使用/总核数（使用率），使用/总内存（使用率）以及特殊说明。  \n        **注意**：可参考机器信息挑选机器，注意一个应用下的实例尽量部署在统一机房，一个实例最少占用一核，剩余内存需大于单实例的内存。\n        <img src=\"../../img/operate/appDeploy/machine_info.png\" width=\"100%\"/>\n4. 信息填写完毕后，点击“生成部署预览”，可生成部署机器信息和部署信息预览，如下图，  \n    <img src=\"../../img/function/server/server-cluster.png\" width=\"100%\"/>  \n      - 部署机器信息展示了挑选的机器的ip、已存在的实例数/核数、已使用/剩余/总内存、需要部署的实例角色数量、机器所在的宿主机/机器信息，可参考挑选机器原则重新挑选。  \n        \n      - 部署信息预览，不同类型的redis，开通使用不同的格式：  \n        cluster类型：master_ip:memory:slave_ip (至少三组，测试应用可以没有slave)，如上图；  \n        sentinel类型：master_ip:memory:slave_ip，sentinel_ip (至少三组，可跨机房)，如图：  \n        <img src=\"../../img/function/server/server-sentinel.png\" width=\"100%\"/>  \n        standalone类型：master_ip:memory，如图：  \n        <img src=\"../../img/function/server/server-standalone-deploy.png\" width=\"100%\"/>  \n        如果部署信息不满足要求，可重新填写信息，挑选机器，重新“生成部署预览”。\n\n  5. 确认部署预览信息无误后，点击开始部署，跳转到应用任务流部署页面，检查应用部署情况：  \n     <img src=\"../../img/operate/appDeploy/skip.png\" width=\"50%\"/>  \n     <img src=\"../../img/operate/appDeploy/deploy_taskflow.png\" width=\"100%\"/>  \n     确认应用部署成功后，回到后台-流程审批页面，点击“通过”，就此整个应用开通完成。\n\n### 总结\n\n        (1).不同类型的redis，使用不同的格式部署。\n        (2).一键部署中唯一需要的就是机器的IP。\n        (3).添加机器时，要综合考虑，用户提交关于客户端的基本信息：QPS、容量、机房、主从等信息，决定挑选的什么配置、什么机房的机器。\n        (4).应用开始部署后，部署任务流页面确认应用部署完毕，需要跳回到审批页点击“通过”，就此整个应用开通才算完成。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/appMigrate.md",
    "content": "## 应用迁移步骤\n\n<a name=\"step1\"/>\n\n### 1. 进入CacheCloud后台\n\n在CacheCloud后台的“应用运维”页面，选择要进行迁移的应用，点击“应用迁移”。 这种迁移方式不会更换应用的appId，通过主从节点的failover实现，是对客户端无感知迁移。\n\n<img src=\"../../img/operate/appMigrate/app-migrate0.png\" width=\"100%\"/>\n\n<a name=\"step2\"/>\n\n### 2. 使用迁移工具进行迁移\n\n应用迁移整体包含八个主要步骤：\n\n+ (1) 应用信息：查看源应用的实例IP，角色和Redis版本等信息，选择迁移的目标机房和迁移机器；\n\n<img src=\"../../img/operate/appMigrate/app-migrate1.png\" width=\"100%\"/>\n\n+ (2) 应用迁移计划：这一步主要查看节点的变更信息，新增实例（Slave）的IP和端口号，点击继续进行新老Salve节点的替换；\n\n<img src=\"../../img/operate/appMigrate/app-migrate2.png\" width=\"100%\"/>\n\n+ (3) 新老Salve节点替换: 替换完成之后查看最新实例信息，点击继续，进行主从切换；\n\n<img src=\"../../img/operate/appMigrate/app-migrate3.png\" width=\"100%\"/>\n\n+ (4) 主从Failover: 完成主从节点切换，点击继续，添加新的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate4.png\" width=\"100%\"/>\n\n+ (5) 添加Slave: 添加新的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate5.png\" width=\"100%\"/>\n\n+ (6) 新实例状态检测：检查新实例的连接状态，是否异常，点击继续，下线老的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate6.png\" width=\"100%\"/>\n\n+ (7) 下线Slave: 下线老的Slave；\n\n<img src=\"../../img/operate/appMigrate/app-migrate7.png\" width=\"100%\"/>\n\n+ (8) 迁移完成: 完成迁移。\n\n<img src=\"../../img/operate/appMigrate/app-migrate8.png\" width=\"100%\"/>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/appUpgrade.md",
    "content": "## 应用升级\n\n应用升级即对应用的redis版本进行升级，小版本和大版本采取不同升级方式。\n\n### 1. 应用小版本升级\n\n使用“应用迁移工具”，逐步替换节点。\n\n#### 1）进入CacheCloud后台\n\n在CacheCloud后台的“应用运维”页面，选择要进行迁移的应用，点击“应用迁移”。 这种迁移方式不会更换应用的appId，通过主从节点的failover实现，是对客户端无感知迁移。\n\n<img src=\"../../img/operate/appMigrate/app-migrate0.png\" width=\"100%\"/>\n\n\n#### 2）使用迁移工具进行迁移\n\n应用迁移整体包含八个主要步骤，具体可参考：[应用迁移文档](../../wiki/function/operation-app#cc2)\n\n+ (1) 应用信息：查看源应用的实例IP，角色和Redis版本等信息，选择迁移的目标机房和迁移机器；\n+ (2) 应用迁移计划：这一步主要查看节点的变更信息，新增实例（Slave）的IP和端口号，点击继续进行新老Salve节点的替换；\n+ (3) 新老Salve节点替换: 替换完成之后查看最新实例信息，点击继续，进行主从切换；\n+ (4) 主从Failover: 完成主从节点切换，点击继续，添加新的Slave；\n+ (5) 添加Slave: 添加新的Slave；\n+ (6) 新实例状态检测：检查新实例的连接状态，是否异常，点击继续，下线老的Slave；\n+ (7) 下线Slave: 下线老的Slave；\n+ (8) 迁移完成: 完成迁移。\n\n### 2. 应用大版本升级\n\nredis大版本间由于各版本配置不兼容的问题，无法使用“应用迁移工具”进行应用升级，我们推荐使用“应用迁移工具”实现大版本的升级工作。\n\n#### 1）创建新的redis应用\n+ 用户申请redis应用，参考[“系统功能-我的申请-创建工单-应用申请”](../../wiki/function/job)。\n+ 管理员审批、开通应用，参考[“开通应用”](../../wiki/function/operation-job#cc1-2)。\n\n#### 2）进行新老redis应用数据迁移\n+ 新老应用数据迁移，参考[“数据迁移”](../../wiki/function/operation-migrate)。\n+ 数据迁移完毕，通知客户端更新到新应用。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/baseConcept.md",
    "content": "## 基础概念：\n\n### 一. 应用、实例、机器、CacheCloud客户端、CacheCloud服务端\n\n- 1.1 `实例`\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个Redis进程，例如在ip为10.10.xx.xx启动了端口为6379的Redis进程。\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;实例可以是Redis单机数据节点、Redis-Sentinel类型下的Redis数据节点和sentinel节点，也可以是Redis-Cluster下的Redis数据节点。\n\n- 1.2 `应用`\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个redis集群是由若干个实例组成的，分配给每个CacheCloud用户的redis集群就是一个应用（包含应用id）。\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个应用可以是一个Redis单机、一个Redis-Sentinel集群，或者一个Redis-Cluster集群。但是对于CacheCloud用户来说，不需要知道每个Redis实例的ip:port信息，只需要一个appId就可以获取这些细节。主流云服务都采用appId的形式分配给用户，提供各种云服务。\n\n- 1.3 `机器`\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;机器资源可以理解为是物理机、虚拟机、docker容器、k8s容器等。一台机器可以部署多个应用，多个实例。\n\n- 1.4 `CacheCloud客户端`\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;提供用户通过appId快速接入CacheCloud系统的api（可以理解成获取应用对应Redis实例的REST接口或者封装好的jar包）。\n\n- 1.5 `CacheCloud服务端`\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CacheCloud用户通过appId获取Redis服务地址的服务端。用于收集，展示和管理实例、应用、机器各个层面的数据，帮助CacheCloud用户快速掌握Redis集群使用状态。\n\n<a name=\"cc1-2\"/>\n\n### 二、实例状态：待分配、心跳停止、已下线、运行中\n\n+ `待分配`：在应用开通期间，实例处于分配的过程中，如果分配成功则是运行中状态；\n+ `心跳停止`：通过系统监控判断实例有可能下线状态，但是由于网络等原因，实例可能并没有下线，所以需要管理员人工确认；\n+ `已下线`：管理员对Redis实例进行下线操作；\n+ `运行中`：正在运行的Redis实例状态。\n\n<img width=\"100%\" src=\"../../img/operate/appDeploy/instance-status.png\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/baseOperate.md",
    "content": "## 基础运维\n\n<a name=\"cc1\"/>\n\n### 1. 客户端服务机房问题\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redis是比较依赖于网络环境的，较差的网络环境会带来一系列问题，最明显的就是访问速度，之前做过一个统计，跨机房要比同一个机房慢数倍。\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;所以用户在申请应用的时候，要填写自己的服务所在的机房，这样CacheCloud的管理员会根据您的机房情况给予合理的分配。\n\n<img width=\"90%\" src=\"../../img/operate/operate/machine-room.png\"/>\n\n<a name=\"cc2\"/>\n\n### 2. 配置修改问题\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cachecloud中各种类型(Redis-standalone、Redis-sentinel, Redis-cluster)，在开启应用时，使用我们认为最优的配置。\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;但是，每个用户自己服务类型不同，需求可能会不同，例如对于maxmemory-policy、list-max-ziplist-entries、list-max-ziplist-value等配置就会有所不同，如果有需要修改配置，用户可以自行提交配置修改。\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;修改方法如下：\n+ (1) 进入应用页面的“拓扑结构”选项卡，点击ID进入实例页面\n<img width=\"100%\" src=\"../../img/operate/operate/profile-modify.png\"/>\n\n+ (2) 进入“配置查询”选项卡\n<img width=\"100%\" src=\"../../img/operate/operate/profile-modify2.png\"/>\n\n+ (3) 点击“申请修改配置”，填写相应字段和值\n<img width=\"60%\" src=\"../../img/operate/operate/profile-modify3.png\"/>\n\n+ (4) 后台管理员接收到申请邮件，进行处理，处理完成后，用户将收到相应处理邮件\n\n#### 注： 修改的配置会对应用的所有节点生效，因为所有节点的配置是统一的。\n\n<a name=\"cc3\"/>\n\n### 3. maxmemory-policy配置说明\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cachecloud对于每个Redis节点都设置了maxmemory, 所以需要有对应的maxmemory-policy，默认策略是volatile-lru。\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redis提供了6种策略应对maxmemory, 用户可以根据自己的需求选择对应的策略。\n+ volatile-lru -> 用lru算法删除过期的键值\n+ allkeys-lru -> 用lru算法删除所有键值\n+ volatile-random -> 随机删除过期的键值\n+ allkeys-random -> 随机删除任何键值\n+ volatile-ttl -> 删除最近要到期的键值（监控TTL）\n+ noeviction -> 不会写操作，返回一个错误\n\n<a name=\"cc4\"/>\n\n### 4. 数据清理问题\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cachecloud测试应用用户可以自行清理内存，处于安全的考虑正式应用需要联系管理员进行清理。\n<img width=\"100%\" src=\"../../img/operate/operate/data-clear.png\"/>\n\n<a name=\"cc5\"/>\n\n### 5. 应用选型问题\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如上图所示，目前CacheCloud提供了3种类型的存储类型\n\n<img width=\"60%\" src=\"../../img/operate/operate/redis-type.png\"/>\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用户可以根据自己的需要选择适合自己的类型，下面对Redis的三种类型使用场景进行简单说明：\n* (1) 底层数据（例如mysql, hbase等等）允许被穿透，而且不考虑高可用性，可以考虑Redis-Standalone类型。\n    * 缺点： 非高可用、受单机影响扩展性(容量，读写等)有限\n    * 优点：结构简单\n\n<img width=\"80%\" src=\"../../img/operate/operate/standalone.png\"/>\n\n* (2) 如想保证Redis的高可用（通过主从和sentinel机制），可以选择Redis-Sentinel类型\n    * 缺点：受单机影响扩展性(容量，读写等)有限\n    * 优点：高可用，结构相对简单\n\n<img width=\"80%\" src=\"../../img/operate/operate/sentinel.png\"/>\n\n* (3) 如想保证高可用、可扩展、大容量等要求，可以选择Redis-Cluster\n下面给一些建议：\n    * 并不是说Redis-Cluster比其他两种类型好，用户需要根据自己的需求选择（比如内存需求量不大，保证高可用选择Redis-Sentinel类型就足够了）\n    * 如果当前或者未来需要的内存量超过6个G， 建议选择Redis-Cluster类型。\n\n<img width=\"80%\" src=\"../../img/operate/operate/cluster.png\"/>\n\n<a name=\"cc6\"/>\n\n### 6. Jedis连接池GenericObjectPoolConfig配置\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Jedis源码中使用了common-pool2作为连接池管理工具，无论是对于Redis-Standalone, Redis-Sentinel, Redis-Cluster进行操作，通常是通过jedis连接池(JedisPool, JedisSentinelPool)获取连接，所以需要选择合理的连接池配置。\n通常的代码如下（可以参考cachecloud中代码接入模块）：\n```Java\nGenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();\npoolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5); \npoolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3); \npoolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2); \npoolConfig.setJmxEnabled(true); \npoolConfig.setMaxWaitMillis(3000); \nPipelineCluster redisCluster = ClientBuilder.redisCluster(appId) \n    .setJedisPoolConfig(poolConfig) \n    .setTimeout(1) \n    .setMaxRedirections(5) \n    .build();\n```\n\t其中poolConfig就是对于连接池的配置，其中有几个配置较为重要。\n\tmaxActive: 链接池中最大连接数,默认为8. (并非越大越好，具体原因可以参考GenericObjectPool的实现)\n\tmaxIdle: 链接池中最大空闲的连接数,默认为8.\n\tminIdle: 连接池中最少空闲的连接数,默认为0.\n\tmaxWait: 当连接池资源耗尽时，调用者最大阻塞的时间，超时将跑出异常。单位，毫秒数;默认为-1.表示永不超时.\n\tjmxEnabled： 当设置为true, 且服务开启的jmx服务时，使用jconsole, jvisualvm等工具将看到如下关于连接池的很全面的统计，这些统计结果有助于优化自己的配置。\n\n<img width=\"100%\" src=\"../../img/operate/operate/jedis-pool.png\"/>\n\n### 其余配置如下：\n+\t 1. minEvictableIdleTimeMillis: 连接空闲的最小时间，达到此值后空闲连接将可能会被移除。负值(-1)表示永远不移除。默认-1。这个也很重要，一般不用移除，每次makeObject()开销比较大。\n+    2. numTestsPerEvictionRun: 对于“空闲连接”检测线程而言，每次检测的连接资源的个数。默认为3。\n+    3. testOnBorrow: 向调用者输出“连接”资源时，是否检测有效，如果无效则从连接池中移除，并尝试继续获取。默认为false。借资源时候是否要验证，比如jedis对象验证是ip:port是否发生改变，且执行一个ping命令。\n+    4. testOnReturn:  向连接池“归还”连接时，是否检测“连接”对象的有效性。默认为false。还资源时候是否要验证，同上。\n+    5. testWhileIdle:  向调用者输出“连接”对象时，是否检测它的空闲超时；如果“连接”空闲超时，将会被移除。默认为false。空闲是否要定时检查一遍所有的对象。\n+    6. timeBetweenEvictionRunsMillis:  “空闲连接”检测线程，检测的周期，毫秒数。如果为负值，表示不运行“检测线程”。默认为-1。\n+    7. whenExhaustedAction: 当“连接池”中active数量达到阀值时，即“连接”资源耗尽时，连接池需要采取的手段, 默认为1。\n\n<a name=\"cc7\"/>\n\n### 7. Redis-Sentinel类型的客户端调用过程（建议）\n(1) 初始化JedisSentinelPool (通常是单例的，可以通过java代码或者spring实现)，具体方法可以参考CacheCloud接入代码模块\n```Java\n//spring 配置\n<bean id=\"redisSentinelFactory\" class=\"com.sohu.tv.mobil.common.data.RedisSentinelFactory\" init-method=\"init\">\n    <property name=\"appId\" value=\"appId\"/>\n</bean>\n<bean id=\"redisSentinelPool\" factory-bean=\"redisSentinelFactory\" factory-method=\"getJedisSentinelPool\"/>\n\npackage xx.xx;\nimport com.sohu.tv.builder.ClientBuilder;\nimport org.apache.commons.pool2.impl.GenericObjectPoolConfig;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport redis.clients.jedis.JedisSentinelPool;\npublic class RedisSentinelFactory {\n\n    private final Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    private JedisSentinelPool jedisSentinelPool;\n\n    private int appId;\n\n    public void init(){\n        //根据自己需要设置poolConfig\n        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();\n        poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 10);\n        poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 5);\n        poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 2);\n        poolConfig.setMaxWaitMillis(1000L);\n        poolConfig.setJmxEnabled(true);\n        try {\n            //根据自己需要设置超时时间\n            jedisSentinelPool = ClientBuilder.redisSentinel(appId)\n                .setTimeout(2000)\n                .setPoolConfig(poolConfig)\n                .build();\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        }\n    }\n\n    public JedisSentinelPool getJedisSentinelPool() {\n        return jedisSentinelPool;\n    }\n\n    public void setAppId(int appId) {\n        this.appId = appId;\n    }\n}\n```\n\n(2) 每次调用的建议写法：\n```Java\n\t\tJedis jedis = null;\n        try {\n            jedis = redisSentinelPool.getResource();\n            //jedis command\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n        } finally {\n            if (jedis != null)\n                jedis.close();\n        }\n```\n其中jedis.close()并非关闭连接（可能是jedis定义上带来的歧义），如果看jedis源码会发现close实现如下:\n```Java\n    public void close() {\n        if (dataSource != null) {\n            if (client.isBroken()) {\n                this.dataSource.returnBrokenResource(this);\n            } else {\n                this.dataSource.returnResource(this);\n            }\n        } else {\n            client.close();\n        }\n    }\n```\n它会帮助你决定returnBrokenResource还是returnResource来归回jedis连接对象，其中dataSource!=null说明使用pool管理jedis连接(具体可以参考JedisPool, JedisSentinelPool的实现中 setDataSource)。\n\n<a name=\"cc8\"/>\n\n### 8. Redis-Cluster类型的客户端调用过程（建议）\n相比于Redis-Sentinel类型Jedis-Cluster的调用相对简单(Jedis源码内部对于cluster节点的选择以及pool的管理做了透明处理)\n+ (1) 初始化JedisCluster(通常是单例的，可以通过java代码或者spring实现)，具体方法可以参考cachecloud接入代码模块。\n+ (2) 每次调用直接使用jedisCluster.command即可。\n\n<a name=\"cc9\"/>\n\n### 9. 数据库个数问题\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redis实例（Redis-Cluster除外）默认提供了16个数据库，用户可以使用select x来实现数据库的切换，但是这个功能从总体上弊大于利，不建议使用，默认使用数据库0即可。\n具体原因：\n如果你在其他语言的客户端中执行Redis命令，并且该客户端没有像redis-cli那样一直显示目标数据库的号码，那么在数次切换数据库之后，你很可能会忘记自己当前正在使用的是哪个数据库。当出现这种情况时，为了避免对数据库进行误操作，在执行Redis命令特别是像FLUSHDB这样的危险命令之前，最好先执行一个SELECT命令，显式地切换到指定的数据库，然后才执行别的命令，总之会陷入混乱。\n\n<a name=\"cc10\"/>\n\n### 10. Redis-Sentinel类型应用统计中publish命令\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;细心的人会发现即使你的Redis-Sentinel类型的应用没做过任何publish操作，命令统计依然会有周期性publish的调用记录。\n\n<img width=\"100%\" src=\"../../img/operate/operate/publish.png\"/>\n\n\n原因是因为sentinel为了保证高可用性，订阅节点上 __sentinel__:hello 频道上publish的信息，确认redis节点的可用性。\n所以这个统计用户不需要关心，可以忽略。\n\n<a name=\"cc11\"/>\n\n### 11. 命令执行模块问题\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了保证数据安全，对于测试应用可以执行任意命令。\n\n<img width=\"100%\" src=\"../../img/operate/operate/cmd.png\"/>\n\n对于线上应用，只允许执行如下命令：\n\n\tdebug,exists,object,ttl,type,scan,get,getbit,getrange,mget,setrange,strlen,hexists,hget,hgetall,hkeys,hlen,hmget,hvals,hscan,lindex,llen,\n\tlrange,scard,sismember,sscan,srandmember,zcard,zcount,zrange,zrangebyscore,zrank,zrevrange,zscore,zscan,dbsize,info,time,lastsave;\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/baseOperate.toc.md",
    "content": "##### 目录\n\n* [1. 客户端服务机房问题](#cc1)\n* [2. 配置修改问题](#cc2)\n* [3. maxmemory-policy配置说明](#cc3)\n* [4. 数据清理问题](#cc4)\n* [5. 应用选型问题](#cc5)\n* [6. Jedis连接池GenericObjectPoolConfig配置](#cc6)\n* [7. Redis-Sentinel类型的客户端调用过程（建议）](#cc7)\n* [8. Redis-Cluster类型的客户端调用过程（建议）](#cc8)\n* [9. 数据库个数问题](#cc9)\n* [10. Redis-Sentinel类型应用统计中publish命令](#cc10)\n* [11. 命令执行模块问题](#cc11)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/baseOptimize.md",
    "content": "## 系统运维优化\n\n### 1. aof持久化隔离自动运维\n\n<img src=\"../../img/operate/optimize/aof-schedule.png\" width=\"70%\"/>\n\n异步工作线程扫描每台机器aof文件增长率。\n对于达到aof重写机制的实例在本机轮流触发重写，实现重写隔离机制。\n\n#### 2. redis复制流程分析和优化\n\n+ 2.1 redis复制操作\n分为全量复制和增量复制，全量复制开销主要在三方面：\n    + (a) master节点bgsave是rdb落地操作。\n    + (b) rdb文件在主从之间传输开销。\n    + (c) slave节点加载rdb文件，避免不必须的全量复制。\n\n+ 2.2 增量复制用于网络不稳定等原因造成rdb重传的问题\n    + (a) 使用psync master_run_id offset 向主节点获取miss数据，\n    + (b) master检查repl-backlog-buffer是否存在offset内的数据，\n    + (c) 存在时，发送增量数据\n    + (d) 不存在时做全量复制操作\n\n+ 2.3 总结：增量复制是否生效依赖于以下三点，根据应用场景做优化\n    + repl-backlog-size参数大小\n    + 主从节点写入量\n    + 主从网络环境\n\n### 3. redis-cluster 批量操作（mget）优化\n\nredis-cluster节点不支持mget，mset等批量操作，client端只能批量get模拟mget操作，与redis节点通信io复杂度N(keys)。\n\nCacheCloud-client端使用pipeline封装mget等批量操作，与redis节点通信io复杂度为N(nodes)。\n\n<img src=\"../../img/operate/optimize/pipeline.png\" width=\"60%\"/>\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/index.md",
    "content": "## CacheCloud运维手册\n\n本节主要从管理员运维角度，介绍CacheCloud平台的常用运维及系统优化。CacheCloud平台的运维主要分为三大部分：应用运维、机器运维和系统运维，如下图：\n\n<img width=\"90%\" src=\"../../img/operate/index.png\"/>\n\n下面的章节我们将对每个部分涉及到的运维功能和技巧展开阐述。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/migrateTool.md",
    "content": "### 一、CacheCloud数据迁移工具能做什么？\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cachecloud数据迁移工具支持redis数据同步工作，可以完成如下功能：\n\n+ 支持任意两种类型的source和target进行数据迁移，如RDB文件、Redis Standalone、Redis Sentinel、Redis Cluster、CacheCloud应用；\n+ 迁移过程中，源集群不影响对外提供服务；\n+ 高效性，多线程并发迁移；\n+ 迁移过程便捷，流程可视化；\n+ 集成redis-migrate-tool、redis-shake redis数据同步的工具。\n+ 数据迁移具有实时性，所以使用合理可以基本保证数据一致性(原理可以参考第二小节)；\n+ 数据校验功能。\n\n<img width=\"100%\" src=\"../../img/operate/migrate/migratetool.png\"/>\n\n### 二、CacheCloud数据迁移工具是如何实现的？\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CacheCloud数据迁移工具底层使用了redis-migrate-tool和redis-shake两种主流redis数据同步的工具。所以这里有必要对这两种数据迁移工具做简单说明。\n\n+ [redis-migrate-tool](https://github.com/vipshop/redis-migrate-tool)\n\n    redis-migrate-tool是用c语言开发的Redis数据迁移工具，可以做到在stadalone、sentinel、cluster、rdb(不支持做为target)彼此迁移数据，\n服务于唯品会公司数千个Redis节点，从数据迁移的准确性、稳定性、高效性等方面都能满足的生产环境的需求。  \n使用文档：[https://github.com/vipshop/redis-migrate-tool](https://github.com/vipshop/redis-migrate-tool)\n\n+ [redis-shake](https://github.com/alibaba/RedisShake)\n\n    redis-migrate-tool仅支持Redis3-4的迁移，不支持redis5-5/4/3 版本数据同步(rdb文件解析异常)，而且已经不在维护，故升级为阿里云开源的redis-shake工具。\n    该工具友好地支持2.8-5.0版本的数据同步/解析、恢复、备份，支持多种部署结构迁移场景，后续会支持断点续传、多活等，且由Redis&MongoDB团队持续优化和维护。  \n    使用文档： [https://github.com/alibaba/RedisShake](https://github.com/alibaba/RedisShake)\n         \n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;迁移工具是基于复制的原理，所以是实时迁移的，这点比起redis自带的redis-trib.rb的import 功能要方便很多。\n故CacheCloud选择它们作为数据迁移的基础组件，通过可视化的方式完成参数配置、节点数据迁移、进度查询、日志查询、配置查询和历史记录等一系列功能，使得数据迁移更简单、便捷。\n\n<img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-tool.png\"/>\n\n### 三、CacheCloud数据迁移工具如何部署和使用\n\n1. 准备迁移工具机器\n    + 初始化机器，在“管理后台-机器管理”页面点击“添加新机器”，请参考相关文档；\n    + 添加机器时候，选择机器类型为“Redis迁移工具机器”，建议单独使用一台机器做迁移，因为迁移的过程可能会占用机器的很多资源。  \n![](../../img/operate/migrate/cc-migrate-machine.png)\n\n2. 安装部署redis-migrate-tool/redis-shake  \n    + redis-migrate-tool  \n在“管理后台-系统配置管理”中配置安装目录，  \n![](../../img/operate/migrate/cc-migrate-redis-migtool-dir.png)  \n    安装方法可以参考redis-migrate-tool主页或者按照如下安装:\n        ```\n        $ cd /opt/cachecloud/\n        $ wget https://github.com/vipshop/redis-migrate-tool/archive/master.zip\n        $ mv master master.zip\n        $ unzip master.zip\n        $ mv redis-migrate-tool-master redis-migrate-tool\n        $ cd redis-migrate-tool\n        $ mkdir data\n        $ autoreconf -fvi\n        $ ./configure\n        $ make\n        $ src/redis-migrate-tool -h\n        ```\n      最为重要的一步是, 注意这里的cachecloud-open是ssh的用户名\n      ```\t\n      $ chown -R ${cachecloud-ssh-username}.${cachecloud-ssh-username} /opt/cachecloud/redis-migrate-tool\n      ```\n    + redis-shake  \n    在“管理后台-系统配置管理”中配置安装目录：  \n    ![](../../img/operate/migrate/cc-migrate-redis-shake-dir.png)  \n    安装方法可以参考redis-shake主页或者按照如下安装：\n        ```\n        1.下载redis-shake安装包，下载地址 https://github.com/alibaba/RedisShake/releases\n        2.解压到/opt/cachecloud/redis-shake目录下\n        3.创建文件夹conf（存放配置文件）、 logs（存放日志）和pid（存放进程信息）\n        4.下载redis-full-check安装包，下载地址 https://github.com/alibaba/RedisFullCheck/releases\n        5.解压到/opt/cachecloud/redis-full-shake目录下\n        6.创建文件夹data（存放比对数据结果）\n        ```\n3. 添加迁移任务  \n    a. 点击“迁移数据工具”进入迁移数据记录列表页面。  \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-enter-migtool.png\"/>  \n    b. 在移数据记录列表中可以查看历史迁移任务信息，任务的配置文件、迁移状态、迁移日志，停止任务，进行数据校验等运维操作。\n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-tasklist.png\"/>  \n    c. 点击“点击添加新的迁移按钮”，选择迁移工具，迁移机器，配置源和目标信息，点击验证按钮，按照通过后就开启了一个迁移的任务，回到迁移列表，就可以观察迁移日志、关闭迁移任务、迁移状态查询等。   \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-addtask.png\"/>  \n  \n4. 数据校验  \n  - 数据迁移完毕后，点击“数据校验”按钮，跳转到如下界面，说明校验程序已经启动：  \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-start-check.png\"/>  \n  - 可通过点击“校验日志”，查看log（默认log 5s刷新）：  \n    <img width=\"100%\" src=\"../../img/operate/migrate/cc-migrate-checklog.png\"/>  \n  - 查看数据校验结果    \n    校验数据报错在校验机器的/opt/cachecloud/redis-full-check/data目录下：  \n    ![](../../img/operate/migrate/cc-migrate-checkres.png)  \n    查看不一致结果记录到result文件：  \n    ![](../../img/operate/migrate/cc-migrate-checkres2.png)  \n### 四、客户端怎么迁移\n1. 当迁移工作基本完成后，我们就需要迁移客户端了，为了方便演示我们假设只有两个客户端。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client1.png\"/> \n2. 迁移第一个客户端，观察客户端是否出现异常。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client2.png\"/>  \n3. 迁移第二个客户端，继续观察。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client3.png\"/>  \n4. 检查source应用是否还有调用。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client4.png\"/>  \n5. 下线source应用和迁移工具。  \n<img width=\"90%\" src=\"../../img/operate/migrate/cc-migrate-client5.png\"/>   \n### 五、一些建议\n- 尽可能单独找一台机器作为迁移机器，因为迁移的过程可能会占用机器的很多资源；\n- redis-migrate-tool/redis-shake很具体的原理问题和细节可以到社区或CacheCloud群里找作者提问；\n- redis-shake使用或迁移日志中的问题可以参考：  \n    [https://github.com/alibaba/RedisShake/wiki/FAQ](https://github.com/alibaba/RedisShake/wiki/FAQ)  \n    [https://github.com/alibaba/RedisShake/issues](https://github.com/alibaba/RedisShake/issues)\n- 迁移工具页面还有很多要优化的地方，后期会听取大家意见逐渐改善。\n\n\n​     \n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/redisVersion.toc.md",
    "content": "#### 目录\n\n* [一、目的](#cc1)\n* [二、新增版本及配置](#cc2)\n    * [2.1 新增版本](#cc21)\n    * [2.2 新增配置](#cc22)\n    * [2.3 申请应用](#cc23)\n* [三、应用版本升级](#cc3)\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/rediscode.md",
    "content": "## CacheCloud支持密码方案\n\n### 一. 密码设计\n\n\t密码粒度分布\n\t\t密码按照应用划分，相同应用的redis使用同一个密码。\n\t\t密码保持健壮性，防止被暴力破解，密码格式: `md5(key+{appId})`\n\t\t密码需要埋入SDK项目内部。\n\n## 二. 接入方式\t\t\n\n- 改造Jedis: 优点:升级sdk支持在线切换，数据没有丢失。缺点：只支持Jedis，其他接入方式需要手动修改修改密码，密码生成过程固定。\n````   \n    a. standalone 模式: 改造JeidsPool.makeObject.\n    b. cluster 模式: 集群内所有节点使用同一密码，防止failover失败。Jedis改造JedisClusterConnectionHandler.initializeSlotsCache 方法。\n    c. sentinel模式:  主从所有节点必须使用同一个密码。改造JedisSentinelPool.initPool \n````\n- 支持失败判定：NOAUTH Authentication required 异常后，中断连接下次重连。\n````    \n    a. RESTful请求携带密码。优点:不需要改造客户端，密码生成规则灵活 缺点：升级协同困难。\n    b. RESTful数据中密码字段使用对称加密，防止直接传输明文。\n````\n- 客户端sdk加入判断是否设置密码功能。\n````    \n    a. 在线修改密码:\n        redis客户端连接未使用密码时，修改密码后报错需要重连。\n        redis客户端连接使用密码连接时，修改密码原有连接继续可用。\n````\n\t\t\t\t\t\n### 三、Cachecloud改造\n\n- 确保CacheCloud管理功能支持密码 修改和校验。\n- 代码点: Jeids,jedisPool , PipelineCluster , JedisCluster, redis-cli(shell)\n\t应用界面支持密码查询。\n- cachecloud 增加redis节点(三种类型)在线密码修改及配置落地。\n    - standalone \n    ````\n    1. 在线添加密码：config set requirepass 123\n    2. 有密码未认证测试：ping\t=> NOAUTH Authentication required\n    3. 有密码已认证测试: 验证密码 auth 123\t | ping => PONG\n    4. 持久化配置后测试：配置落地 config rewrite | auth 123 | ping=>PONG\n    ````\n    **结论**：standalone模式能够在线支持密码修改，并持久化配置，客户端auth之后可以正常使用，测试通过.\n\n\t- sentinel哨兵\n\t````\n\t**观察设置密码过程中master 、slave、sentinel的表现**\n\t\n\t1. 在线对maste添加密码：config set requirepass 123\n        1.1 slave表现为从master断开\n        1.2 由于sentinel无法和设有密码的master通信，故sentinel认为该节点down掉，选举\n            slave为master\n\t\n\t2.slave也设置密码：config set requirepass 123\n\t\t2.1 由于无法和master&slave通信，故认为都down掉了，sentinel会重复的进行选举\n\t\t\t\n\t3. sentinel在线修改配置：sentinel set ${mastername} auth-pass 123\n\t4. 强制刷新：sentinel flushconfig(redis版本>=3.0.2支持)\n\t5. slave此时并未完成master的密码配置，slave日志会有同步失败的日志\n        5.1 为slave设置链接master的密码：config set masterauth 123, slave full resync from master(master会做一次全量复制)\n        5.2 对master和slave执行config rewrite完成配置持久化\n\t````\n\t**结论**：\t\n\tsentinel模式下的master&slave，能够在线支持密码修改，并持久化配置，客户端auth之后可以正常使用，测试通过\n\t另外：由于sentinel模式下，master<->slave可能相互转换，所以，master和slave均需要配置requirepass和masterauth\n\t\n\t- cluster集群\n    ````\n\t1. 在线对maste添加密码：config set requirepass 123\n        1.1 slave变现为从master断开\n        1.2 redis cluster并未认为被设置密码的master down调，这点与sentinel表现不\n            同，估计gossip协议忽略了redis的密码 \n        1.3 对slave设置链接master密码：config set masterauth passwd123 , \n            slave full resync from master\n        1.4 对slave设置密码：config set requirepass 123\n        1.5 对master和slave进行持久化配置：config rewrite\t\t\n    ````\n\t**结论**：cluster模式能够在线支持密码修改，并ß持久化配置，客户端auth之后可以正常使用，测试通过,另外：由于cluster模式下，master<->slave可能相互转换，所以，master和slave均需要配置requirepass和masterauth.\n\n### 四、cachecloud后台设置\n\n- 创建应用,默认密码\n\n\n- 修改密码\n    <img src=\"http://i0.itc.cn/20170814/aac_a309d856_c48c_5526_6e61_478f0382b321_2.png\" width=\"100%\"/>\n\n    - `更新`按钮:redis密码设置\n    - `校验`按钮:用于校验redis实例密码是否都设置成功\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/operate/ssh.md",
    "content": "## Redis机器授权方式\n\n### 1.用户名/密码访问\n\n参考系统配置初始化参数，机器ssh用户名：${yourusername}/机器ssh密码${yourpassword}\n\n<a name=\"cc52\"/>\n\n### 2.公钥访问\n\n参考系统配置初始化参数，密钥路径：${path}/公钥用户名：${yourusername}\n\n> 生成公钥/私钥文件流程:\n\n#### 1.生成公钥/私钥文件\n```\t\n1).执行命令:ssh-keygen -t rsa -C ${yourusername} -f /opt/id_rsa(-f可指定文件路径)\n2).目录: /opt\n3).公钥文件:id_rsa.pub\n4).私钥文件:id_rsa    \n```\n\n#### 2.公钥/私钥配置方式\n\n- 2.1 公钥配置\n\n所有Redis机器需要基于cachecloud用户目录下写入公钥信息并授权\n````\n1).写入公钥信息:\necho \"***\" >> /home/cachecloud/.ssh/authorized_keys  （***为上面生成id_rsa.pub公钥信息）\n2).文件权限:\nchmod 755 /home/cachecloud/.ssh/authorized_keys\n````\n\n- 2.2 私钥配置\n\ncachecloud后台机器上配置私钥\n````\n1).指定私钥文件路径：/opt/ssh/id_rsa\n2).授权私钥文件权限：chmod 600 /opt/ssh/id_rsa\n3).通过cachecloud用户登录redis机器: ssh -i /opt/ssh/id_rsa -l cachecloud ${ip}\n````\n\n#### 3.后台配置公钥/私钥信息\n\n- 密钥路径：cachecloud机器上id_rsa文件路径\n- 公钥用户名： 连接Redis机器的用户名\n\n<img width=\"60%\" height=\"40%\" src=\"../../img/access/config.png\"/>"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/quickstart/index.md",
    "content": "##### 目录\n* [一、准备war包](#cc1)\n* [二、初始化数据](#cc2)\n* [三、启动工程](#cc3)\n* [四、系统初始配置](#cc4)\n* [五、创建一个用户](#cc5)\n* [六、准备资源](#cc6)\n* [七、创建一个应用](#cc7)\n\n\n<a name=\"cc1\"/>\n\n## 快速接入 quick start \n”快速接入“ 帮助你快速启动cachecloud中台，并创建一个应用。\n\n<img src=\"../../img/quickstart/quickstart.png\" width=\"60%\"/>\n\n<a name=\"cc1\"/>\n\n### 一、准备war包\t\na.下载war包：\n\n[2.2版本 cachecloud-web.war](https://github.com/sohutv/cachecloud/releases/download/2.2/cachecloud-web.war)\n\n[3.2版本 cachecloud-web.war](https://github.com/sohutv/cachecloud/releases/download/3.2/cachecloud-web.war)\n\nb.也可以clone源码，自行打包：\n\n\t//clone项目\n\tgit clone https://github.com/sohutv/cachecloud.git\n\t//切换到指定分支\n\tgit checkout 3.2\n\t//修改指定应用和数据库配置\n\t[参见 三、启动工程](#cc3)\n\t//打包\n\tmvn clean package\n\n<a name=\"cc2\"/>\n\n### 二、初始化数据\n导入项目sql目录下初始化库表结构，默认插入admin超级管理员。\n\n创建数据库：\n\n\turl: jdbc:mysql://xxx:3306/cachecloud_open\n\tuser: xxx\n\tpassword: xxx\n\n导入建表文件：/cachecloud-web/sql/cc3.2.sql\n（根据指定版本导入，如为升级，请执行update {源版本} to {目标版本}.sql文件）\n\n<a name=\"cc3\"/>\n\n### 三、启动工程\n准备配置文件：cachecloud-web/src/main/resources/application-open.yml，修改指定应用和数据库配置\n\n\t#配置应用名称\n\tspring:\n\t  application:\n\t    name: cloud.cachecloud-web.open\n\t#配置访问端口,默认8080\n\tserver:\n\t  port: 8080\n\t#配置数据库\n\tcachecloud:\n\t  primary: #mysql数据库\n\t    url: ${jdbcUrl}\n\t    user: ${username}\n\t    password: ${password}\n\n启动工程\n\n \t//启动web工程\n\tnohup java -jar -Dspring.profiles.active=open cachecloud-web.war &\n\t\n访问web：<http://localhost:8080/manage/login> 使用默认管理员账户登录：用户名 admin 密码 admin\n\n<img src=\"../../img/quickstart/login.png\" width=\"50%\"/>\n\n<a name=\"cc4\"/>\n\n### 四、系统初始配置\n系统已有默认配置，如需更改，请参考 [系统配置说明](../access/config.md)。\n\n<a name=\"cc5\"/>\n\n### 五、创建一个用户\n新用户注册：<http://localhost:8080/user/register> 提交用户注册申请。\n\n<img src=\"../../img/quickstart/register.png\" width=\"60%\"/>\n\n管理员审核（<http://localhost:8080/manage/app/auditList>）通过后，用户可登录/使用系统。\n\n\n<a name=\"cc6\"/>\n\n### 六、准备资源\n\n<a name=\"cc61\"/>\n\n* 1.准备脚本\n\t\n\t下载 [cachecloud-init.sh](http://43.137.44.6/redis-ecs/script)\n\t\n\t\tcachecloud-init.sh脚本作用：\n\t\ta.初始化环境变量\n\t\tb.创建用户;\n\t\tc.创建默认redis路径并授权;\n\t\td.默认安装\"redis-3.0.7\" \"redis-3.2.12\" \"redis-4.0.14\" \"redis-5.0.9\";\n\t\t  注意：Redis6.0及以上版本需要依赖操作系统gcc4.9.0以上版本编译\n\t\te.@usage: sh cachecloud-init.sh [username]\n\n<a name=\"cc62\"/>\n\n* 2.执行脚本\n\n\t采用”用户名/密码“授权方式，请确保机器的用户名/密码与cachecloud后台”系统配置“页面的”机器ssh用户名/机器ssh密码“一致。\n\n\t\ta.使用root登录目标服务器;\n\t\tb.将cachecloud-init.sh脚本拷贝到目标服务器当前用户目录下/opt ;\n\t\tc.执行 sh cachecloud-init.sh ${username}，默认username=\"cachecloud-open\"；\n\t\td.两次确认密码;\n\t\te.一路安装直到成功。\n\n<a name=\"cc63\"/>\n\n* 3.添加机器\n\n\t将刚准备好资源的机器添加到cachecloud中台：<http://localhost:8080/manage/machine/index?tabTag=machine>\n\n\t<img src=\"../../img/quickstart/addmachine.png\" width=\"50%\"/>\n\n<a name=\"cc7\"/>\n\n### 七、创建一个应用\n\n<a name=\"cc71\"/>\n\n* 1.申请应用\n\n\t用户申请应用<http://localhost:8080/admin/app/init>。\n\t\n<a name=\"cc72\"/>\n\t\n* 2.部署应用\n\n\ta.管理员审批应用申请：<http://localhost:8080/manage/app/auditList>，点击”审批处理“。\n\t\n\t<img src=\"../../img/quickstart/appjob.png\" width=\"100%\"/>\n\t\n\tb.应用部署：点击”生成部署预览“，确认拓扑无误，点击”开始部署“，跳转到应用部署任务流。\n\n\t<img src=\"../../img/quickstart/appdeploy.png\" width=\"80%\"/>\n\t\t\n\tc.通过：等待应用部署成功后，在审批页点击”通过“，就此应用开通成功。用户接入使用可参考：[客户端接入文档](../access/client.md)。\n\t\n\t(提示：任务流日志是存储在redis中的，即项目启动时配置文件application-open.yml中的cachecloud.redis信息)\n\t\n\t<img src=\"../../img/quickstart/apptask.png\" width=\"80%\"/>\n\t\n<a name=\"cc73\"/>\n\t\n* 3.导入应用\n\n\t该功能支持将外部redis实例导入Cachecloud平台进行运维管理。具体操作参考：[应用导入](../../wiki/function/operation-import.md)\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/quickstart/index.toc.md",
    "content": "##### 目录\n* [一、准备war包](#cc1)\n* [二、初始化数据](#cc2)\n* [三、启动工程](#cc3)\n* [四、系统初始配置](#cc4)\n* [五、创建一个用户](#cc5)\n* [六、准备资源](#cc6)\n    * [1.准备脚本](#cc61)\n    * [2.执行脚本](#cc62)\n    * [3.添加机器](#cc63)\n* [七、创建一个应用](#cc7)\n    * [1.申请应用](#cc71)\n    * [2.部署应用](#cc72)\n    * [3.导入应用](#cc73)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/activefrag.md",
    "content": "## activeDefrag引起redis周期性延迟\n\n<a name=\"c1\"/>\n\n### 1.问题描述\n\nredis客户端连接Redis节点会出现周期性超时情况，CC监控排查到cpu使用率异常。\n\n<img src=\"../../img/troubleshooting/cpu-monitor.png\" width=\"100%\" />\n\n客户端机器测试redis节点延迟情况:\n\n````\nredis-cli -h 10.xx.xxx.69 -p 6387 -a 5d948b734065a8f163cb931701b7ade2 --latency-history\n** min: 0, max: 2894, avg: 3.87 (1077 samples) -- 15.01 seconds range ** \nmin: 0, max: 30, avg: 1.20 (1332 samples) -- 15.02 seconds range\nmin: 0, max: 218, avg: 1.40 (1308 samples) -- 15.01 seconds range\nmin: 0, max: 25, avg: 1.23 (1327 samples) -- 15.00 seconds range\nmin: 0, max: 20, avg: 1.25 (1326 samples) -- 15.01 seconds range\nmin: 0, max: 21, avg: 1.24 (1326 samples) -- 15.01 seconds range\nmin: 0, max: 74, avg: 1.33 (1315 samples) -- 15.01 seconds range\nmin: 0, max: 37, avg: 1.25 (1325 samples) -- 15.00 seconds range\n** min: 0, max: 2435, avg: 3.65 (1094 samples) -- 15.01 seconds range ** \nmin: 0, max: 18, avg: 1.22 (1329 samples) -- 15.01 seconds range\n** min: 0, max: 2810, avg: 3.80 (1081 samples) -- 15.00 seconds range ** \nmin: 0, max: 30, avg: 1.23 (1329 samples) -- 15.02 seconds range\nmin: 0, max: 210, avg: 1.38 (1309 samples) -- 15.00 seconds range\nmin: 0, max: 28, avg: 1.27 (1323 samples) -- 15.01 seconds range\nmin: 0, max: 21, avg: 1.24 (1328 samples) -- 15.02 seconds range\nmin: 0, max: 70, avg: 1.35 (1320 samples) -- 15.08 seconds range\nmin: 0, max: 28, avg: 1.23 (1328 samples) -- 15.01 seconds range\nmin: 0, max: 37, avg: 1.26 (1324 samples) -- 15.01 seconds range\n** min: 0, max: 2456, avg: 3.64 (1094 samples) -- 15.00 seconds range ** \nmin: 0, max: 19, avg: 1.23 (1327 samples) -- 15.00 seconds range\nmin: 0, max: 2953, avg: 3.90 (1073 samples) -- 15.00 seconds range\nmin: 0, max: 29, avg: 1.20 (1331 samples) -- 15.01 seconds range\n....\n````\n\n上面带**的最大耗时均为秒级，redis节点周期性的出现延迟。\n\n<a name=\"c2\"/>\n\n### 2. 故障定位\n\n排查慢查询，命令分析，IO问题定位 均无法定位问题，集中把异常的节点变为从节点，主节点使用dkmanager机器，延迟问题得到恢复。\n在延迟的从节点上开启watchdog观察redis本身的慢调用栈。\n\n````\n# 超过100毫秒记录redis调用栈\nCONFIG SET watchdog-period 100\n````\n跟踪redis日志，发现周期性输出如下调用栈。\n````\nBacktrace:\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](logStackTrace+0x3c)[0x4673fc]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](watchdogSignalHandler+0x1b)[0x4674ab]\n/lib64/libpthread.so.0(+0xf7e0)[0x7f24bf9c37e0]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](dictIterDefragEntry+0x6c)[0x496c3c]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](defragKey+0x25a)[0x49703a]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](defragScanCallback+0x9)[0x497209]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](dictScan+0x23a)[0x426e7a]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](activeDefragCycle+0x1da)[0x49666a]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](databasesCron+0x7b)[0x42ae3b]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](serverCron+0x193)[0x42cae3]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](aeProcessEvents+0x32f)[0x424ecf]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](aeMain+0x2b)[0x42509b]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster](main+0x500)[0x42d750]\n/lib64/libc.so.6(__libc_start_main+0xfd)[0x7f24bf63ed1d]\n/opt/cachecloud/redis-4.0.11/src/redis-server 0.0.0.0:6387 [cluster][0x422409]\n3884:signal-handler (1570711815) --------\n````\n堆栈信息定位是 redis内部定时任务触发的在线内存碎片整理逻辑，redis 定时任务和网络IO事件共享单线程架构。\n\n慢调用栈输出时间点和客户端延迟时间点吻合，初步定位是该问题导致。\n\n关闭在线碎片整理，延迟调用问题恢复。\n\n````\nmin: 0, max: 2488, avg: 3.61 (1098 samples) -- 15.00 seconds range\nmin: 0, max: 28, avg: 1.17 (1335 samples) -- 15.00 seconds range\nmin: 0, max: 2972, avg: 3.94 (1072 samples) -- 15.00 seconds range\nmin: 0, max: 27, avg: 1.14 (1340 samples) -- 15.02 seconds range\nmin: 0, max: 30, avg: 1.19 (1334 samples) -- 15.01 seconds range\nmin: 0, max: 223, avg: 1.34 (1316 samples) -- 15.01 seconds range\nmin: 0, max: 28, avg: 1.15 (1338 samples) -- 15.01 seconds range\nmin: 0, max: 17, avg: 1.14 (1339 samples) -- 15.01 seconds range\n# config set activedefrag no 关闭在线碎片整理时间点\nmin: 0, max: 16, avg: 0.36 (1439 samples) -- 15.01 seconds range \nmin: 0, max: 1, avg: 0.06 (1482 samples) -- 15.01 seconds range\nmin: 0, max: 1, avg: 0.05 (1482 samples) -- 15.01 seconds range\nmin: 0, max: 1, avg: 0.07 (1482 samples) -- 15.01 seconds range\nmin: 0, max: 7, avg: 0.07 (1481 samples) -- 15.01 seconds range\n````\n\n开启和关闭节点CPU表现:\n\n<img src=\"../../img/troubleshooting/machine-cpu-monitor.png\" width=\"100%\" />\n\n<a name=\"c3\"/>\n\n### 3.进一步定位\n\n查看redis关于碎片整理的源码，Redis碎片代码整理粒度: \n\n<span style=\"color:red;font:blod\">当redis_version <= 4.0.xx ，在线内存整理以键作为最小扫描单位，针对非字符串的结构需要迭代所有的元素指针才能返回，复杂度为O(N)。因此对于非字符串bigkeys碎片整理，返回时间不再可控从而造成阻塞。</span>\n\nRedis 4.0.x 源码: redis<= 4.0.x之前的代码碎片整理迭代逻辑，至少扫描16个键或指针整理数超过1000，检查cpu使用超时退出。\n\n````\n//defrag.c\ndo {\n    cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db);\n    /* Once in 16 scan iterations, or 1000 pointer reallocations\n     * (if we have a lot of pointers in one hash bucket), check if we\n     * reached the tiem limit. */\n    if (cursor && (++iterations > 16 || server.stat_active_defrag_hits - defragged > 1000)) {\n        if ((ustime() - start) > timelimit) {\n            return;\n        }\n        iterations = 0;\n        defragged = server.stat_active_defrag_hits;\n    }\n} while(cursor);\n````\n\nRedis 5.0.x 源码: \n\n<span style=\"color:red;font:blod\">redis> 5.0.x 升级日志 \"Active defragmentation version 2\",\n\n碎片扫描粒度：16次迭代或512次指针移动或64个碎片扫描，检测超时时间。</span>\n\n````\n//degrag.c\n/* Perform incremental defragmentation work from the serverCron.\n * This works in a similar way to activeExpireCycle, in the sense that\n * we do incremental work across calls. */\nvoid activeDefragCycle(void) {\n        // ....\n        do {\n            /* before scanning the next bucket, see if we have big keys left from the previous bucket to scan */\n            //扫描完整个db后，按 bigkeys 的元素进行迭代扫描\n            if (defragLaterStep(db, endtime)) {\n                quit = 1; /* time is up, we didn't finish all the work */\n                break; /* this will exit the function and we'll continue on the next cycle */\n            }\n            // 按键迭代扫描\n            cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db);\n \n            /* Once in 16 scan iterations, 512 pointer reallocations. or 64 keys\n             * (if we have a lot of pointers in one hash bucket or rehasing),\n             * check if we reached the time limit.\n             * But regardless, don't start a new db in this loop, this is because after\n             * the last db we call defragOtherGlobals, which must be done in once cycle */\n            if (!cursor || (++iterations > 16 ||\n                            server.stat_active_defrag_hits - prev_defragged > 512 ||\n                            server.stat_active_defrag_scanned - prev_scanned > 64)) {\n                if (!cursor || ustime() > endtime) {\n                    quit = 1;\n                    break;\n                }\n                iterations = 0;\n                prev_defragged = server.stat_active_defrag_hits;\n                prev_scanned = server.stat_active_defrag_scanned;\n            }\n        } while(cursor && !quit);\n    } while(!quit);\n \n    latencyEndMonitor(latency);\n    latencyAddSampleIfNeeded(\"active-defrag-cycle\",latency);\n}\n````\n<img src=\"../../img/troubleshooting/activefrag-code.png\" width=\"100%\" />\n\n<span style=\"color:red\">碎片扫描逻辑默认对元素数超过1000的结构放入链表中延迟处理，待整个DB扫描完毕后再单独按元素扫描，解决bigkeys引起的阻塞和时效性问题。</span>\n\n<span style=\"color:green\">5.0 新增参数配置:</span>\n\n````\n# Maximum number of set/hash/zset/list fields that will be processed from the main dictionary scan\n# active-defrag-max-scan-fields 1000\n````\n<a name=\"c4\"/>\n\n### 4.优化方案\n\n- <span style=\"color:green\">优化方案:</span>\n    - redis版本 <= 4.0.x，关闭碎片整理 config set activedefrag no；\n    - redis版本 <= 4.0.x，扫描bigkey并优化，防止因遍历bigkey导致阻塞；\n    - 升级到redis版本到4.0.14 或 5.0.x以上版本,新增配置对bigkey遍历键项数 active-defrag-max-scan-fields 1000(默认配置)；"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/activefrag.toc.md",
    "content": "##### 目录\n\n* 一、[问题描述](#c1)\n* 二、[故障定位](#c2)\n* 三、[进一步定位](#c3)\n* 四、[优化方案](#c4)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/bigkey.md",
    "content": "## bigkey的寻找和优化\n\n* [一、什么是bigkey](#cc1)\n* [二、bigkey的危害](#cc2)\n* [三、如何发现](#cc3)\n* [四、如何删除](#cc4)\n* [五、如何优化](#cc5)\n* [六、总结](#cc6)\n\n### 一、什么是bigkey\n\n虽然叫bigkey，但实际上它是指key对应的value所占的内存空间比较大，例如一个字符串类型的value可以最大存到512M，一个列表类型的value最多可以存储2的32次方减1个元素(大约4G)。\n如果按照数据结构来细分的话，一般分为字符串类型bigkey和非字符串类型bigkey：\n\n1. 字符串类型：它的big体现在单个value值很大，一般认为超过100KB就是bigkey。\n2. 非字符串类型：哈希、列表、集合、有序集合，它们的big体现在元素个数太多。\n \n### 二、bigkey的危害\n\nbigkey的危害比较多的，主要体现在以下方面:\n\n1. 内存空间不均匀(平衡)：在Redis集群中，bigkey会造成部分节点的内存空间使用不均匀。\n\n    <img src=\"../../img/troubleshooting/mem-imbalance.png\" width=\"100%\"/>\n\n2. 超时阻塞：由于Redis单线程的特性，操作bigkey的比较耗时，也就意味着阻塞Redis可能性越大，这样会造成客户端阻塞或者引起故障切换。\n\n3. 网络拥塞：bigkey也就意味着每次获取要产生的网络流量较大，假设一个bigkey为1M，客户端每秒访问量为1000，那么每秒产生1000M的流量，对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾，而且一般服务器会采用单机多实例的方式来部署，也就是说一个bigkey可能会对其他实例造成影响，其后果不堪设想。\n\n4. 过期删除：如果没有使用Redis 4.0的过期异步删除(lazyfree-lazy-expire yes)，就会存在阻塞Redis的可能性，而且这个过期删除不会从主节点的慢查询发现（因为这个删除不是客户端产生的，是内部循环事件，可以从latency命令中获取或者从slave节点慢查询发现）。\n\n5. [内存碎片整理](activefrag.md)：在redis4.0.*版本如果开启碎片整理且存在超大bigkey，可能会导致客户端出现周期性超时问题。\n\n\n### 三、如何发现\n\n1. redis-cli --bigkeys\n\n    redis-cli提供了--bigkeys来查找bigkey，例如下面就是一次执行结果：\n\n    ````\n    -------- summary -------\n\n    Sampled 215036 keys in the keyspace!\n    Total key length in bytes is 13428208 (avg len 62.45)\n\n    Biggest string found 'v4_firstPageData_*****' has 2549527 bytes\n    Biggest    set found 'ASSOCIATE_TRACE_CHILDREN_*******' has 7322 members\n    Biggest   zset found 'TEMPLATE_STRATEGY_*****_' has 10 members\n\n    159497 strings with 3183765503 bytes (74.17% of keys, avg size 19961.29)\n    0 lists with 0 items (00.00% of keys, avg size 0.00)\n    55535 sets with 130291 members (25.83% of keys, avg size 2.35)\n    0 hashs with 0 fields (00.00% of keys, avg size 0.00)\n    4 zsets with 25 members (00.00% of keys, avg size 6.25)\n    0 streams with 0 entries (00.00% of keys, avg size 0.00)\n    ````\n    可以看到--bigkeys扫描给出了每种数据结构的top1 bigkey，同时给出了每种数据类型的键值个数以及平均大小。\n\n    --bigkeys对问题的排查非常方便，但是在使用它时候也有几点需要注意：\n    ````\n    1. 建议对从节点执行，由于--bigkeys是通过scan扫描完成的；\n    2. 建议在从节点机器上去执行，减少网络开销；\n    3. 如果没有从节点，可通过-i 参数控制执行频率(-i 0.1 每100ms执行一次)\n    ````\n\n2. debug object\n\n    Redis提供了debug object ${key}命令获取键值的相关信息：\n    ````\n    127.0.0.1:6379> hlen hash_obj\n    (integer)500000\n    127.0.0.1:6379> debug object hash_obj\n    Value at:0x7fc06c1b1430 refcount:1 encoding:raw serializedlength:87256350 lru:11686193 lru_seconds_idle:20\n    (1.08s)\n    ````\n    其中serializedlength表示key对应的value序列化之后的字节数,这样你就可以用scan + debug object的方式遍历Redis所有的键值.\n    不过debug object命令执行如果遇到bigkey也可能会阻塞客户端，建议对从节点执行。\n\n3. memory usage\n\n    memory usage ${key} 用于分析某个key在redis内部实际占用多少内存(单位：字节)，从Redis 4.0开始及以上版本支持。\n    ````\n    127.0.0.1:6379> memory usage bighash (bighash占用内存686KB)\n    (integer) 686755\n    127.0.0.1:6379> DEBUG OBJECT bighash （bighash序列化大小为227KB）\n    Value at:0x7f00c8e884b0 refcount:1 encoding:hashtable serializedlength:227790 lru:7578074 lru_seconds_idle:288\n    127.0.0.1:6379> hlen bighash\n    (integer) 10000\n    127.0.0.1:6379> memory usage key:001016520509\n    (integer) 65\n    ````\n    如果你使用Redis 4.0+，你就可以用scan + memory usage替代scan + debug object的方式遍历Redis所有的键值,执行速度也较快。\n\n4. 监控报警\n\n    bigkey的大操作，通常会引起客户端输入或者输出缓冲区的异常，Redis提供了info clients里面包含的客户端输入缓冲区的字节数以及输出缓冲区的队列长度，可以重点关注下：\n\n    ````\n    instanceId:9427> info clients\n    # Clients\n    connected_clients:58\n    client_longest_output_list:0\n    client_biggest_input_buf:0\n    blocked_clients:0\n    \n    以下为6.0版本，会记录历史最大输入、输出缓冲区队列\n    127.0.0.1:6379> info clients\n    # Clients\n    connected_clients:1\n    client_recent_max_input_buffer:2\n    client_recent_max_output_buffer:0\n    ````\n    如果想知道具体哪个客户端ip，可以使用client list命令来查找。\n    ````\n    127.0.0.1:6379> client list\n    id=3 addr=127.0.0.1:56040 fd=9 name= age=161 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client\n    ````\n\n### 四、如何删除\n\n当发现Redis中有bigkey时并且确认要删除时，如何优雅地删除bigkey？下面给出一组测试数据分别对string、hash、list、set、sorted set五种数据结构的bigkey进行删除，bigkey的元素个数和每个元素的大小不尽相同。\n\n\n1. 下图为删除512K~10M的字符串类型数据所花费的时间，总体来说由于字符串类型结构相对简单，删除速度比较快，但是随着value值的不断增大，删除速度也逐渐变慢。\n\n    <img src=\"../../img/troubleshooting/delstr-cost.png\" width=\"60%\"/>\n\n2. 下图展示了非字符串类型在不同数量级、不同元素大小下对bigkey执行del的时间，总体上看元素个数越多，元素越大删除时间越长，并相对于字符串类型，这种删除速度已经足够可以阻塞掉Redis。\n\n    <img src=\"../../img/troubleshooting/del-cost.png\" width=\"100%\"/>\n\n    <img src=\"../../img/troubleshooting/del-compare.png\" width=\"60%\"/>\n\n看到除了string类型，其他四种数据结构元素数量和每个元素字节数越大删除的速度越慢，这样的删除速度势必会阻塞Redis。既然不能用del，那有没有比较优雅的方式进行删除呢，这时候就需要将第三章介绍的scan命令的若干类似命令拿出来：sscan、hscan、zscan。\n\n1. 字符串：\n\n  一般来说，对于string类型使用del命令不会产生阻塞。\n  \n2. hash：\n  \n  使用hscan命令，每次获取部分(例如100个)field-value，在利用hdel删除每个field(为了快速可以使用pipeline)。\n  \n    ````\n    public void delBigHash(String bigKey) {\n        Jedis jedis = new Jedis(\"127.0.0.1\", 6379);\n        // 游标\n        String cursor = \"0\";\n        while (true) {\n            ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(bigKey, cursor, new ScanParams().count(100));\n            // 每次扫描后获取新的游标\n            cursor = scanResult.getStringCursor();\n            // 获取扫描结果\n            List<Entry<String, String>> list = scanResult.getResult();\n            if (list == null || list.size() == 0) {\n                continue;\n            }\n            String[] fields = getFieldsFrom(list);\n            // 删除多个field\n            jedis.hdel(bigKey, fields);\n            // 游标为0时停止\n            if (cursor.equals(\"0\")) {\n                break;\n            }\n        }\n        //最终删除key\n        jedis.del(bigKey);\n    }\n     \n    /**\n     * 获取field数组\n     * @param list\n     * @return\n     */\n    private String[] getFieldsFrom(List<Entry<String, String>> list) {\n        List<String> fields = new ArrayList<String>();\n        for(Entry<String, String> entry : list) {\n            fields.add(entry.getKey());\n        }\n        return fields.toArray(new String[fields.size()]);\n    }\n    ````\n\n3. list\n\n    Redis并没有提供lscan这样的API来遍历列表类型，但是提供了ltrim这样的命令可以渐进式的删除列表元素，直到把列表删除。\n\n4. set\n\n    使用sscan命令，每次获取部分(例如100个)元素，在利用srem删除每个元素。\n\n    ````\n    public void delBigSet(String bigKey) {\n        Jedis jedis = new Jedis(\"127.0.0.1\", 6379);\n        // 游标\n        String cursor = \"0\";\n        while (true) {\n            ScanResult<String> scanResult = jedis.sscan(bigKey, cursor, new ScanParams().count(100));\n            // 每次扫描后获取新的游标\n            cursor = scanResult.getStringCursor();\n            // 获取扫描结果\n            List<String> list = scanResult.getResult();\n            if (list == null || list.size() == 0) {\n                continue;\n            }\n            jedis.srem(bigKey, list.toArray(new String[list.size()]));\n            // 游标为0时停止\n            if (cursor.equals(\"0\")) {\n                break;\n            }\n        }\n        //最终删除key\n        jedis.del(bigKey);\n    }\n    ````\n\n5. sorted set\n\n    使用zscan命令，每次获取部分(例如100个)元素，在利用zremrangebyrank删除元素。\n\n    ````\n    /**\n     * 137258ms\n     * @param bigKey\n     */\n    public void delBigSortedSet(String bigKey) {\n        long startTime = System.currentTimeMillis();\n        Jedis jedis = new Jedis(HOST, PORT);\n        // 游标\n        String cursor = \"0\";\n        while (true) {\n            ScanResult<Tuple> scanResult = jedis.zscan(bigKey, cursor, new ScanParams().count(100));\n            // 每次扫描后获取新的游标\n            cursor = scanResult.getStringCursor();\n            // 获取扫描结果\n            List<Tuple> list = scanResult.getResult();\n            if (list == null || list.size() == 0) {\n                continue;\n            }\n            String[] members = getMembers(list);\n            jedis.zrem(bigKey, members);\n            // 游标为0时停止\n            if (cursor.equals(\"0\")) {\n                break;\n            }\n        }\n        // 最终删除key\n        jedis.del(bigKey);\n    }\n     \n    /**\n     * 60529ms\n     * @param bigKey\n     */\n    public void delBigSortedSet2(String bigKey) {\n        Jedis jedis = new Jedis(HOST, PORT);\n        long zcard = jedis.zcard(bigKey);\n        int counter = 0;\n        int incr = 100;\n        while (counter < zcard) {\n            jedis.zremrangeByRank(bigKey, 0, 100);\n            //每次从左侧截掉100个\n            counter+=incr;\n        }\n        // 最终删除key\n        jedis.del(bigKey);\n    }\n    ````\n  \n### 五、如何优化\n\n1. 拆\n\n    - big list： list1、list2、...listN\n\n    - big hash：可以做二次的hash，例如hash%100\n\n    - 日期类：key20190320、key20190321、key_20190322\n\n2. 本地缓存\n\n减少访问redis次数，降低危害，但是要注意这里有可能因此本地的一些开销（例如使用堆外内存会涉及序列化，bigkey对序列化的开销也不小）\n\n### 六、总结：\n\n由于开发人员对Redis的理解程度不同，在实际开发中出现bigkey在所难免，重要的能通过合理的检测机制及时找到它们，进行处理。作为开发人员应该在业务开发时不能将Redis简单暴力的使用，应该在数据结构的选择和设计上更加合理，例如出现了bigkey，要思考一下可不可以做一些优化(例如二级索引)尽量的让这些bigkey消失在业务中，如果bigkey不可避免，也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要hmget，而不是hgetall)，删除也是一样，尽量使用优雅的方式来处理。\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/cachecloud.md",
    "content": "## 关于Cachecloud平台使用问题\n\n##### 目录\n\n1. [数据库时间问题](#Q1)\n2. [平台本身需要的redis问题](#Q2)\n3. [用户登录注册问题](#Q3)\n4. [现有redis导入问题](#Q4)\n\n<a name=\"Q1\"/>\n### 1.数据库时间问题\nCacheCloud版本2 使用mysql-connector-java 8, 与数据库版本不匹配时，时区显示可能有问题。建议在配置jdbcUrl时指定时区serverTimezone=Asia/Shanghai，如下：\n\n    cachecloud:\n\t  primary:\n\t    url: jdbc:mysql://129.0.0.1:3306/redis_open?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&connectTimeout=3000&socketTimeout=10000&serverTimezone=Asia/Shanghai\n\n<a name=\"Q2\"/>\n### 2.平台本身需要的redis问题\ncachecloud-web工程配置文件中可为平台配置一个redis实例信息，用于存储任务流的日志。这一项不是必须的，可在项目启动后再创建配置。\n\n\tcachecloud:\n\t  redis: #配置cachecloud-web需要的redis，用户存储任务流log，可稍后配置\n\t    main:\n\t      host: ${ip}\n\t      port: ${port}\n\t      password: ${pwd}\n\n<a name=\"Q3\"/>\n### 3.用户登录注册问题\nCacheCloud版本2.2 将支持用户自主提交&修改密码功能。\n\n<a name=\"Q4\"/>\n### 4.现有redis导入问题\nCacheCloud版本1 支持简单的原生redis导入功能，但仅限制于导入redis的应用信息查询，节点监控等功能。\n\nCacheCloud版本2.1 新提供“应用导入功能”，实现全方位的redis实例托管，可参考：[\"应用导入\"](../../wiki/function/operation-import)。\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/exception.md",
    "content": "# 异常信息目录:\n\n* [一、redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool](#e1)\n* [二、redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream](#e2)\n* [三、redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory](#e3)\n* [四、redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached](#e4)\n* [五、redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out](#e5)\n* [六、redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required](#e6)\n* [七、redis.clients.jedis.exceptions.JedisDataException: EXECABORT Transaction discarded because of previous errors](#e7)\n* [八、java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.List](#e8)\n* [九、redis.clients.jedis.exceptions.JedisDataException: WRONGTYPE Operation against a key holding the wrong kind of value](#e9)\n* [十、redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'](#e10)\n* [十一、redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out](#e11)\n\n## 一.无法从连接池获取到Jedis连接\n\n<a name=\"e1\"/>\n### 1.异常堆栈\n\n- (1) 连接池参数blockWhenExhausted = true(默认)\n\n如果连接池没有可用Jedis连接，会等待maxWaitMillis(毫秒)，依然没有获取到可用Jedis连接，会抛出如下异常:\n````\nredis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool\n    …\nCaused by: java.util.NoSuchElementException: Timeout waiting for idle object\n    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)\n````\n\n- (2) 连接池参数blockWhenExhausted = false\n设置如果连接池没有可用Jedis连接，立即抛出异常：\n````\nredis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool\n    …\nCaused by: java.util.NoSuchElementException: Pool exhausted\n    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)\n````\n\n### 异常描述\n\n![jedspool](../../img/troubleshooting/jedispool.png)\n\n上述异常是客户端没有从连接池(最大maxTotal个)拿到可用Jedis连接造成的，具体可能有如下原因：\n\n- (1) 连接泄露 (较为常见)\n\nJedisPool默认的maxTotal=8，下面的代码从JedisPool中借了8次Jedis，但是没有归还，当第9次(jedisPool.getResource().ping())\n\n````\nGenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();\nJedisPool jedisPool = new JedisPool(poolConfig, \"127.0.0.1\", 6379);\n//向JedisPool借用8次连接，但是没有执行归还操作。\nfor (int i = 0; i < 8; i++) {\n    Jedis jedis = null;\n    try {\n        jedis = jedisPool.getResource();\n        jedis.ping();\n    } catch (Exception e) {\n        logger.error(e.getMessage(), e);\n    }\n}\njedisPool.getResource().ping();\n````\n\n所以推荐使用的代码规范是：\n````\n执行命令如下：\nJedis jedis = null;\ntry {\n    jedis = jedisPool.getResource();\n    //具体的命令\n    jedis.executeCommand()\n} catch (Exception e) {\n    //如果命令有key最好把key也在错误日志打印出来，对于集群版来说通过key可以帮助定位到具体节点。\n    logger.error(e.getMessage(), e);\n} finally {\n    //注意这里不是关闭连接，在JedisPool模式下，Jedis会被归还给资源池。\n    if (jedis != null) \n        jedis.close();\n}\n````\n\n- (2) 业务并发量大，maxTotal确实设置过小。\n\n举个例子：\n\n一次命令时间（borrow|return resource + Jedis执行命令(含网络) ）的平均耗时约为1ms，一个连接的QPS大约是1000，\n业务期望的QPS是50000，\n那么理论上需要的资源池大小是50000 / 1000 = 50个，实际maxTotal可以根据理论值进行微调。\n\n- (3) Jedis连接还的太慢\n\n例如Redis发生了阻塞(例如慢查询等原因)，所有连接在超时时间范围内等待，并发量较大时，会造成连接池资源不足。\n\n- (4) 其他问题\n\n例如丢包、DNS、客户端TCP参数配置等.\n\n<a name=\"e2\"/>\n\n## 二、客户端缓冲区异常\n\n### 1.异常堆栈\n````\nredis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.\n    at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:199)\n    at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)\n    at redis.clients.jedis.Protocol.process(Protocol.java:151)\n......\n````\n\n### 2.异常描述：\n这个异常是客户端缓冲区异常，产生这个问题可能有三个原因：\n\n(1) 多个线程使用一个Jedis连接，正常的情况是一个线程使用一个Jedis连接，可以使用JedisPool管理Jedis连接，实现线程安全，防止出现这种情况，例如下面代码中两个线程用了一个Jedis连接：\n\n````\nnew Thread(new Runnable() {\n\n    public void run() {\n        for (int i = 0; i < 100; i++) {\n            jedis.get(\"hello\");\n        }\n    }\n}).start();\n\nnew Thread(new Runnable() {\n\n    public void run() {\n        for (int i = 0; i < 100; i++) {\n            jedis.hget(\"haskey\", \"f\");\n        }\n    }\n}).start();\n````\n\n(2) 客户端缓冲区满了\n\nRedis有三种客户端缓冲区：\n\n普通客户端缓冲区(normal)：用于接受普通的命令，例如get、set、mset、hgetall、zrange等；\n\nslave客户端缓冲区(slave)：用于同步master节点的写命令，完成复制；\n\n发布订阅缓冲区(pubsub)：pubsub不是普通的命令，因此有单独的缓冲区。\n\n![jedspool](../../img/troubleshooting/clientbuffer.png)\nRedis的客户端缓冲区配置具体格式是：\n````\nclient-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>\n````\n(a) class: 客户端类型：(a) normal、(b) slave、(c) pubsub\n\n(b) hard limit: 如果客户端使用的输出缓冲区大于hard limit，客户端会被立即关闭。\n\n(c) soft limit和soft seconds: 如果客户端使用的输出缓冲区超过了soft limit并且持续了soft limit秒，客户端会被立即关闭\n\n例如下面是一份Redis缓冲区的配置，所以当条件满足时，客户端连接会被关闭，就会出现Unexpected end of stream。\n\n````\nredis> config get client-output-buffer-limit\n1) \"client-output-buffer-limit\"\n2) \"normal 524288000 0 0 slave 2147483648 536870912 480 pubsub 33554432 8388608 60\"\n````\n\n(3) 长时间闲置连接被服务端主动断开，可以查询timeout配置的值以及自身连接池配置是否需要做空闲检测。\n\n<a name=\"e3\"/>\n\n## 三、Redis正在加载持久化文件\n \n### 1.异常堆栈：\n````\nredis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory\n````\n\n### 2.异常描述：\n\nJedis调用Redis时，如果Redis正在加载持久化文件，无法进行正常的读写。\n\n<a name=\"e4\"/>\n\n## 四、客户端连接数达到最大值\n\n### 1.异常堆栈\n````\nredis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached\n````\n\n### 2.异常描述：\n如果客户端连接数超过了Redis实例配置的最大maxclients，会抛出该异常。\n\n<a name=\"e5\"/>\n\n## 五、客户端读写超时\n\n### 1.异常堆栈\n````\nredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out\n````\n\n### 2.异常描述：\n该问题原因可能有如下几种：\n\n(1) 读写超时设置的过短。\n\n(2) 有慢查询或者Redis发生阻塞。\n\n(3) 网络不稳定。\n\n<a name=\"e6\"/>\n\n## 六、密码相关的异常\n\n### 1.异常堆栈\n\n- 1) Redis设置了密码，客户端请求没传密码：\n````\nException in thread \"main\" redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required.\n     at redis.clients.jedis.Protocol.processError(Protocol.java:127)\n     at redis.clients.jedis.Protocol.process(Protocol.java:161)\n     at redis.clients.jedis.Protocol.read(Protocol.java:215)\n````\n\n- 2) Redis没有设置密码，客户端传了密码：\n````\nException in thread \"main\" redis.clients.jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is set\n     at redis.clients.jedis.Protocol.processError(Protocol.java:127)\n     at redis.clients.jedis.Protocol.process(Protocol.java:161)\n     at redis.clients.jedis.Protocol.read(Protocol.java:215)\n````\n\n- 3) 客户端传了错误的密码：\n````\nredis.clients.jedis.exceptions.JedisDataException: ERR invalid password\n    at redis.clients.jedis.Protocol.processError(Protocol.java:117)\n    at redis.clients.jedis.Protocol.process(Protocol.java:151)\n    at redis.clients.jedis.Protocol.read(Protocol.java:205)\n````\n\n<a name=\"e7\"/>\n\n## 七、事务异常\n\n### 1.异常堆栈\n````\nredis.clients.jedis.exceptions.JedisDataException: EXECABORT Transaction discarded because of previous errors\n````\n\n### 2.异常描述：\n这个是Redis的事务异常：事务中包含了错误的命令，例如如下sett是个不存在的命令。\n````\n127.0.0.1:6379> multi\nOK\n127.0.0.1:6379> sett key world\n(error) ERR unknown command 'sett'\n127.0.0.1:6379> incr counter\nQUEUED\n127.0.0.1:6379> exec\n(error) EXECABORT Transaction discarded because of previous errors.\n````\n\n<a name=\"e8\"/>\n\n## 八、类转换错误\n\n### 1.异常堆栈\n\n````\njava.lang.ClassCastException: java.lang.Long cannot be cast to java.util.List\n         at redis.clients.jedis.Connection.getBinaryMultiBulkReply(Connection.java:199)\n         at redis.clients.jedis.Jedis.hgetAll(Jedis.java:851)\n         at redis.clients.jedis.ShardedJedis.hgetAll(ShardedJedis.java:198)\n````\n\n````\njava.lang.ClassCastException: java.util.ArrayList cannot be cast to [B\n         at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:182)\n         at redis.clients.jedis.Connection.getBulkReply(Connection.java:171)\n         at redis.clients.jedis.Jedis.rpop(Jedis.java:1109)\n         at redis.clients.jedis.ShardedJedis.rpop(ShardedJedis.java:258)\n.......\n````\n\n### 2.异常描述：\nJedis正确的使用方法是：一个线程操作一个Jedis，通常来讲产生该错误是由于没有使用JedisPool造成的，例如如下代码在两个线程并发使用了一个Jedis。(get、hgetAll返回类型也是不一样的)\n\n````\nnew Thread(new Runnable() {\n\n    public void run() {\n        for (int i = 0; i < 100; i++) {\n            jedis.set(\"hello\", \"world\");\n            jedis.get(\"hello\");\n        }\n    }\n}).start();\n\nnew Thread(new Runnable() {\n\n    public void run() {\n        for (int i = 0; i < 100; i++) {\n            jedis.hset(\"hashkey\", \"f\", \"v\");\n            jedis.hgetAll(\"hashkey\");\n        }\n    }\n}).start();\n````\n\n<a name=\"e9\"/>\n\n## 九、命令使用错误\n\n### 1.异常堆栈\n````\nException in thread \"main\" redis.clients.jedis.exceptions.JedisDataException: WRONGTYPE Operation against a key holding the wrong kind of value\n    at redis.clients.jedis.Protocol.processError(Protocol.java:127)\n    at redis.clients.jedis.Protocol.process(Protocol.java:161)\n    at redis.clients.jedis.Protocol.read(Protocol.java:215)\n.....\n````\n\n### 2.异常描述：\n例如key=\"hello\"是字符串类型的键，而hgetAll是哈希类型的键，所以出现了错误。\n\n````\njedis.set(\"hello\",\"world\");\njedis.hgetAll(\"hello\");\n````\n\n<a name=\"e10\"/>\n\n## 十、Redis使用的内存超过maxmemory配置\n\n### 1.异常堆栈\n````\nredis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.\n````\n\n### 2.异常描述：\nRedis节点(如果是集群，则是其中一个节点)使用大于该实例的内存规格(maxmemory配置)。\n\n### 3.解决方法：\n原因可能有以下几个：\n\n1).业务数据正常增加;\n\n2).客户端缓冲区异常：例如使用了monitor、pub/sub使用不当等等;\n\n3).纯缓存使用场景，但是maxmemory-policy配置有误(例如没有过期键的业务配置volatile-lru)。\n\n<a name=\"e11\"/>\n\n## 十一、连接超时\n\n### 1.异常堆栈\n````\nredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out\n````\n\n### 2.异常描述：\n\n可能产生的原因：\n\n1).连接超时设置的过短。\n\n2).tcp-backlog满，造成新的连接失败。\n\n3).客户端与服务端网络不正常。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/hotkey.md",
    "content": "## 寻找热点key\n\n热门新闻事件或商品通常会给系统带来巨大的流量，但对存储这类信息的Redis来说是一个巨大的挑战。以Redis Cluster为例，它会造成整体流量的不均衡，个别节点出现OPS过大的情况，极端情况下热点key甚至会超过Redis本身能够承受的OPS，因此寻找热点key对于开发和运维人员非常重要。\n\n### 一、客户端\n客户端其实是距离key\"最近\"的地方，因为Redis命令就是从客户端发出的，例如在客户端设置全局字典(key和调用次数)，每次调用Redis命令时，使用这个字典进行记录，如下所示。\n\n```\npublic static final AtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create(); \nString get(String key) {\n    counterKey(key);\n    //ignore\n}\nString set(String key, String value) {\n    counterKey(key);\n    //ignore\n}\nvoid counterKey(String key) {\n    ATOMIC_LONG_MAP.incrementAndGet(key);\n}\n```\n<!--more-->\n\n为了减少对客户端代码的侵入，可以在Redis客户端的关键部分进行计数，例如Jedis的Connection类中的sendCommand方法是所有命令执行的枢纽。\n\n```\npublic Connection sendCommand(final ProtocolCommand cmd, final byte[]... args) {\n    //从参数中获取key\n    String key = analysis(args);\n    //计数\n    counterKey(key);\n    //ignore\n}\n```\n\n同时为了防止ATOMIC_LONG_MAP过大，可以对其进行定期清理。\n\n```\npublic void scheduleCleanMap() {\n    ERROR_NAME_VALUE_MAP.clear();\n}\n```\n\n使用客户端进行热点key的统计非常容易实现，但是同时问题也非常多：\n\n(1). 无法预知key的个数，存在内存泄露的危险。\n\n(2). 对于客户端代码有侵入，各个语言的客户端都需要维护此逻辑，维护成本较高。\n\n(3). 只能了解当前客户端的热点key，无法实现规模化运维统计。\n\n当然除了使用本地字典计数外，还可以使用其他存储来完成异步计数，从而解决本地内存泄露问题。但是同样会存在第(2)(3)个问题。\n\n### 二、代理端\n\n像Twemproxy、Codis这些基于代理的Redis分布式架构，所有客户端的请求都是通过代理端完成的，如下图所示。此架构是最适合做热点key统计的，因为代理是所有Redis客户端和服务端的桥梁。但并不是所有Redis都是采用此种架构。\n\n<img src=\"../../img/troubleshooting/hotkey/hotkey-proxy.png\" width=\"100%\"/>\n\n\n### 三、Redis服务端\n使用monitor命令统计热点key是很多开发和运维人员首先想到，monitor命令可以监控到Redis执行的所有命令，下面为一次monitor命令执行后部分结果。\n\n```\n1477638175.920489 [0 10.16.xx.183:54465] \"GET\" \"tab:relate:kp:162818\"\n1477638175.925794 [0 10.10.xx.14:35334] \"HGETALL\" \"rf:v1:84083217_83727736\"\n1477638175.938106 [0 10.16.xx.180:60413] \"GET\" \"tab:relate:kp:900\"\n1477638175.939651 [0 10.16.xx.183:54320] \"GET\" \"tab:relate:kp:15907\"\n...\n1477638175.962519 [0 10.10.xx.14:35334] \"GET\" \"tab:relate:kp:3079\"\n1477638175.963216 [0 10.10.xx.14:35334] \"GET\" \"tab:relate:kp:3079\"\n1477638175.964395 [0 10.10.xx.204:57395] \"HGETALL\" \"rf:v1:80547158_83076533\"\n```\n \n<img src=\"../../img/troubleshooting/hotkey/hotkey-monitor.png\" width=\"100%\"/>\n\n如上图所示，利用monitor的结果就可以统计出一段时间内的热点key排行榜，命令排行榜，客户端分布等数据，例如下面的伪代码统计了最近10万条命令中的热点key。\n\n\n```\n//获取10万条命令\nList<String> keyList = redis.monitor(100000);\n//存入到字典中，分别是key和对应的次数\nAtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create(); \n//统计\nfor (String command : commandList) {\n\tATOMIC_LONG_MAP.incrementAndGet(key);\n}\n//后续统计和分析热点key\nstatHotKey(ATOMIC_LONG_MAP);\n```\n\n\nFacebook开源的redis-faina 正是利用上述原理使用Python语言实现的，例如下面获取最近10万条命令的热点key、热点命令、耗时分布等数据。为了减少网络开销以及加快输出缓冲区的消费速度，monitor尽可能在本机执行。\n\nredis-cli -p 6380 monitor | head -n 100000 | ./redis-faina.py\n\n```\nOverall Stats\n========================================\nLines Processed  \t50000\nCommands/Sec     \t900.48\nTop Prefixes\n========================================\ntab        \t27565\t(55.13%)\nrf         \t15111\t(30.22%)\nugc        \t2051 \t(4.10%)\n...\nTop Keys\n========================================\ntab:relate:kp:9350          \t2110\t(4.22%)\ntab:relate:kp:15907         \t1594\t(3.19%)\n...\nTop Commands\n========================================\nGET      \t25700\t(51.40%)\nHGETALL  \t15111\t(30.22%)\n...\nCommand Time (microsecs)\n========================================\nMedian  \t622.75\n75%     \t1504.0\n90%     \t2820.0\n99%     \t6798.0\n```\n\n此种方法会有两个问题：\n\n(1) 本书多次强调monitor命令在高并发条件下，会存在内存暴增和影响Redis性能的隐患，所以此种方法适合在短时间内使用。\n\n(2) 只能统计一个Redis节点的热点key，对于Redis集群需要进行汇总统计。\n\n### 四、机器层面\n\n第四章我们介绍过，Redis客户端使用TCP协议与服务端进行交互，通信协议采用的是RESP。如果站在机器的角度，可以通过对机器上所有Redis端口的TCP数据包进行抓取完成热点key的统计，如下图所示。\n\n<img src=\"../../img/troubleshooting/hotkey/hotkey-tcppacket.png\" width=\"100%\"/>\n \n\n此种方法对于Redis客户端和服务端来说毫无侵入，是比较完美的方案，但是依然存在两个问题：\n\n(1)\t需要一定的开发成本，但是一些开源方案实现了该功能，例如ELK(ElasticSearch Logstash Kibana)体系下的[packetbeat](https://www.elastic.co/guide/en/beats/packetbeat/current/index.html)插件，可以实现对Redis、MySQL等众多主流服务的数据包抓取、分析、报表展示。\n\n(2)\t由于是以机器为单位进行统计，要想了解一个集群的热点key，需要进行后期汇总。\n\n最后通过下给出上述四种方案的特点。\n\n方案 | 优点 | 缺点\n--- |--- |---\n客户端 | 实现简单 | 内存泄露隐患<br/>维护成本高<br/>只能统计单个客户端\n代理 | 代理是客户端和服务端的桥梁，实现最方便最系统 | 增加代理端的开发部署成本 \n服务端 | 实现简单 | monitor本身的使用成本和危害，只能短时间使用<br/>只能统计单个Redis节点\n机器TCP流量 |对于客户端和服务端无侵入和影响 | 需要专业的运维团队开发，并且增加了机器的部署成本\n\t\t\n最后我们给出三种解决热点key问题的思路，具体选用那种要根据具体业务场景来决定。\n\n(1)\t拆分复杂数据结构： 如果当前key的类型是一个二级数据结构，例如哈希类型。如果该哈希元素个数较多，可以考虑将当前hash进行拆分，这样该热点key可以拆分为若干个新的key分布到不同Redis节点上，从而减轻压力。\n\n(2)\t迁移热点key：以Redis Cluster为例，可以将热点key所在的slot单独迁移到一个新的Redis节点上，但此操作会增加运维成本。\n\n(3)\t本地缓存加通知机制：可以将热点key放在业务端的本地缓存中，因为是在业务端的本地内存中，处理能力要高出Redis数十倍，但当数据更新时，此种模式会造成各个业务端和Redis数据不一致，通常会使用发布订阅机制来解决类似问题。\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/index.md",
    "content": "## FAQ常见问题\n\n关于CacheCloud的一些问题，分为CacheCloud中台的使用问题和Redis运维的问题。\n\n我们将大家关心的常见问题，和github issue上提到的问题，会在这里做说明，并持续更新...\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/jedispoolconfig.md",
    "content": "## JedisPool资源池优化\n\n### 背景\n合理的JedisPool资源池参数设置能为业务使用Redis保驾护航，本文将对JedisPool的使用、资源池的参数进行详细说明，最后给出“最合理”配置。\n\n<a name=\"c1\"/>\n\n### 一、使用方法\n\n以官方的2.9.0为例子(Jedis Release)，Maven依赖如下：\n\n````\n<dependency>\n    <groupId>redis.clients</groupId>\n    <artifactId>jedis</artifactId>\n    <version>2.9.0</version>\n    <scope>compile</scope>\n</dependency>\n````\n\nJedis使用apache commons-pool2对Jedis资源池进行管理，所以在定义JedisPool时一个很重要的参数就是资源池GenericObjectPoolConfig，使用方式如下，其中有很多资源管理和使用的参数\n\n注意：后面会提到建议用JedisPoolConfig代替GenericObjectPoolConfig\n\n````\nGenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig();\njedisPoolConfig.setMaxTotal(..);\njedisPoolConfig.setMaxIdle(..);\njedisPoolConfig.setMinIdle(..);\njedisPoolConfig.setMaxWaitMillis(..);\n...\n````\n\nJedisPool的初始化如下:\n\n````\n// redisHost和redisPort是实例的IP和端口\n// redisPassword是实例的密码\n// timeout，这里既是连接超时又是读写超时，从Jedis 2.8开始有区分connectionTimeout和soTimeout的构造函数\n\nJedisPool jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort, timeout, redisPassword);\n执行命令如下：\nJedis jedis = null;\ntry {\n    jedis = jedisPool.getResource();\n    //具体的命令\n    jedis.executeCommand()\n} catch (Exception e) {\n    logger.error(e.getMessage(), e);\n} finally {\n    //注意这里不是关闭连接，在JedisPool模式下，Jedis会被归还给资源池。\n    if (jedis != null) \n        jedis.close();\n}\n````\n<a name=\"c2\"/>\n\n### 二、参数说明\n\nJedisPool保证资源在一个可控范围内，并且提供了线程安全，但是一个合理的GenericObjectPoolConfig配置能为应用使用Redis保驾护航，下面将对它的一些重要参数进行说明和建议：\n在当前环境下，Jedis连接就是资源，JedisPool管理的就是Jedis连接。\n\n1. 资源设置和使用\n\n序号 | 参数名 | 含义 | 默认值 | 使用建议\n---|--- |--- |---|---\n1 | maxTotal | 资源池中最大连接数 | 8\t| 设置建议见下节 \n2 | maxIdle | 资源池允许最大空闲的连接数 | 8 | 设置建议见下节\n3 | minIdle | \t资源池确保最少空闲的连接数 | 0 | 设置建议见下节\n4 | blockWhenExhausted | 当资源池用尽后，调用者是否要等待。只有当为true时，下面的maxWaitMillis才会生效 | true | 建议使用默认值\n5 | maxWaitMillis | 当资源池连接用尽后，调用者的最大等待时间(单位为毫秒) | -1：表示永不超时 | 不建议使用默认值\n6 | testOnBorrow | 向资源池借用连接时是否做连接有效性检测(ping)，无效连接会被移除 | false | 业务量很大时候建议设置为false(多一次ping的开销)。\n7 | testOnReturn | 向资源池归还连接时是否做连接有效性检测(ping)，无效连接会被移除 | false | 业务量很大时候建议设置为false(多一次ping的开销)。\n8 | jmxEnabled | 是否开启jmx监控，可用于监控 | true | 建议开启，但应用本身也要开启\n\n2. 空闲资源监测\n\n空闲Jedis对象检测，下面四个参数组合来完成，testWhileIdle是该功能的开关。\n\n序号 | 参数名 | 含义 | 默认值 | 使用建议\n---|--- |--- |---|---\n1 | testWhileIdle | 是否开启空闲资源监测 | false | true\n2 | timeBetweenEvictionRunsMillis | 空闲资源的检测周期(单位为毫秒) | -1：不检测 | 建议设置，周期自行选择，也可以默认也可以使用下面JedisPoolConfig中的配置\n3 | minEvictableIdleTimeMillis | 资源池中资源最小空闲时间(单位为毫秒)，达到此值后空闲资源将被移除 | 1000 60 30 = 30分钟 | 可根据自身业务决定，大部分默认值即可，也可以考虑使用下面JeidsPoolConfig中的配置\n4 | numTestsPerEvictionRun | 做空闲资源检测时，每次的采样数 | 3 | 可根据自身应用连接数进行微调,如果设置为-1，就是对所有连接做空闲监测\n\n为了方便使用，Jedis提供了JedisPoolConfig，它本身继承了GenericObjectPoolConfig设置了一些空闲监测设置\n````\npublic class JedisPoolConfig extends GenericObjectPoolConfig {\n  public JedisPoolConfig() {\n    // defaults to make your life with connection pool easier :)\n    setTestWhileIdle(true);\n    //\n    setMinEvictableIdleTimeMillis(60000);\n    //\n    setTimeBetweenEvictionRunsMillis(30000);\n    setNumTestsPerEvictionRun(-1);\n    }\n}\n````\n所有默认值可以从org.apache.commons.pool2.impl.BaseObjectPoolConfig中看到。\n\n<a name=\"c3\"/>\n\n### 三、资源池大小(maxTotal)、空闲(maxIdle minIdle)设置建议\n\n**1. maxTotal：最大连接数**\n\n实际上这个是一个很难回答的问题，考虑的因素比较多：\n    \n+ 业务希望Redis并发量；\n+ 客户端执行命令时间；\n+ Redis资源：例如 nodes(例如应用个数) * maxTotal 是不能超过redis的最大连接数；\n+ 资源开销：例如虽然希望控制空闲连接，但是不希望因为连接池的频繁释放创建连接造成不必靠开销。\n\n以一个例子说明，假设:\n\n+ 一次命令时间（borrow|return resource + Jedis执行命令(含网络) ）的平均耗时约为1ms，一个连接的QPS大约是1000\n+ 业务期望的QPS是50000\n\n那么理论上需要的资源池大小是50000 / 1000 = 50个。但事实上这是个理论值，还要考虑到要比理论值预留一些资源，通常来讲maxTotal可以比理论值大一些。\n但这个值不是越大越好，一方面连接太多占用客户端和服务端资源，另一方面对于Redis这种高QPS的服务器，一个大命令的阻塞即使设置再大资源池仍然会无济于事。\n\n**2. maxIdle minIdle**\n\nmaxIdle实际上才是业务需要的最大连接数，maxTotal是为了给出余量，所以maxIdle不要设置过小，否则会有new Jedis(新连接)开销，而minIdle是为了控制空闲资源监测。\n\n连接池的最佳性能是maxTotal = maxIdle ,这样就避免连接池伸缩带来的性能干扰。但是如果并发量不大或者maxTotal设置过高，会导致不必要的连接资源浪费。\n可以根据实际总OPS和调用redis客户端的规模整体评估每个节点所使用的连接池。\n\n**3.监控**\n\n实际上最靠谱的值是通过监控来得到“最佳值”的，可以考虑通过一些手段(例如jmx)实现监控，找到合理值。\n\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/jedispoolconfig.toc.md",
    "content": "##### 目录\n\n* [一、使用方法](#c1)\n* [二、参数说明](#c2)\n* [三、资源池配置建议](#c3)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/liunx.md",
    "content": "## Redis的Linux系统优化\n\n通常来看，Redis开发和运维人员更加关注的是Redis本身的一些配置优化，例如AOF和RDB的配置优化、数据结构的配置优化等，但是对于操作系统是否需要针对Redis做一些配置优化不甚了解或者不太关心，然而事实证明一个良好的系统操作配置能够为Redis服务良好运行保驾护航。\n\n众所周知Redis的作者对于Windows操作系统并不感冒，目前大部分公司都会将Web服务器、数据库服务器等部署在Linux操作系统上，Redis也不例外。所以接下来介绍Linux操作系统如何优化Redis，包含如下七个方面。\n\n\n\n<a name=\"chapter1\"/>\n\n## 一. 内存分配控制\n\n### 1. vm.overcommit_memory\n\nRedis在启动时可能会出现这样的日志：\n\n```\n# WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the \ncommand 'sysctl vm.overcommit_memory=1' for this to take effect.\n```\n\n<!--more-->\n\n在分析这个问题之前，首先要弄清楚什么是overcommit？Linux操作系统对大部分申请内存的请求都回复yes，以便能运行更多的程序。因为申请内存后，并不会马上使用内存，这种技术叫做overcommit。如果Redis在启动时有上面的日志，说明vm.overcommit_memory=0，Redis提示把它设置为1。\n\nvm.overcommit_memory用来设置内存分配策略，它有三个可选值，如下表所示。\n\nvm.overcommit_memory | 含义 \n---|--- \n0 | 表示内核将检查是否有足够的可用内存。如果有足够的可用内存，内存申请通过，否则内存申请失败，并把错误返回给应用进程 \n1 | 表示内核允许超量使用内存直到用完为止 \n2 | 表示内核决不过量的(\"never overcommit\")使用内存，即系统整个内存地址空间不能超过swap+50%的RAM值，50%是overcommit_ratio默认值，此参数同样支持修改 \n\n```\n注意：本文的可用内存代表物理内存与swap之和。\n```\n\n日志中的Background save代表的是bgsave和bgrewriteaof，如果当前可用内存不足，操作系统应该如何处理fork。如果vm.overcommit_memory=0，代表如果没有可用内存，就申请内存失败，对应到Redis就是fork执行失败，在Redis的日志会出现：\n```\nCannot allocate memory \n```\nRedis建议把这个值设置为1，是为了让fork能够在低内存下也执行成功。\n\n\n### 2. 获取和设置\n\n#### 获取：\n\n```\ncat /proc/sys/vm/overcommit_memory\n0\n```\n\n#### 设置：\n\n```\necho \"vm.overcommit_memory=1\" >> /etc/sysctl.conf\nsysctl vm.overcommit_memory=1\n```\n\n### 3. 最佳实践\n\n+ Redis设置合理的maxmemory，保证机器有20%~30%的闲置内存。\n+ 集中化管理aof重写和rdb的bgsave。\n+ 设置vm.overcommit_memory=1，防止极端情况下，会造成fork失败。\n\n<a name=\"chapter2\"/>\n\n## 二. swappiness\n\n### 1. 参数说明\n\nswap对于操作系统来比较重要，当物理内存不足时，可以swap out一部分内存页，以解燃眉之急。但世界上没有免费午餐，swap空间由硬盘提供，对于需要高并发、高吞吐的应用来说，磁盘IO通常会成为系统瓶颈。在Linux中，并不是要等到所有物理内存都使用完才会使用到swap，系统参数swppiness会决定操作系统使用swap的倾向程度。swappiness的取值范围是0~100，swappiness的值越大，说明操作系统可能使用swap的概率越高，swappiness值越低，表示操作系统更加倾向于使用物理内存。swap的默认值是60，了解这个值的含义后，有利于Redis的性能优化。下表对swappiness的重要值进行了说明。\n\nswapniess | 策略\n---|--- \n0 | Linux3.5以及以上：宁愿OOM killer也不用swap<br/>Linux3.4以及更早：宁愿swap也不要OOM killer\n1 | Linux3.5以及以上：宁愿swap也不要OOM killer \n60 | 默认值\n100 | 操作系统会主动地使用swap\n\n```\n运维提示：OOM(Out Of Memory) killer机制是指Linux操作系统发现可用内存不足时，强制杀死一些用户进程（非内核进程），来保证系统有足够的可用内存进行分配。\n```\n\n从下表中可以看出，swappiness参数在Linux 3.5版本前后的表现并不完全相同，Redis运维人员在设置这个值需要关注当前操作系统的内核版本。\n\n### 2. 设置方法\n\nswappiness设置方法如下：\n\n```\necho {bestvalue} > /proc/sys/vm/swappiness\n```\n\n但是上述方法在系统重启后就会失效，为了让配置在重启Linux操作系统后立即生效，只需要在/etc/sysctl.conf追加 vm.swappiness={bestvalue}即可。\n\n```\necho vm.swappiness={bestvalue} >> /etc/sysctl.conf\n```\n\n需要注意/proc/sys/vm/swappiness是设置操作，/etc/sysctl.conf是追加操作。\n\n\n### 3. 如何监控swap\n#### (1) 查看swap的总体情况\nLinux提供了free命令来查询操作系统的内存使用情况，其中也包含了swap的相关使用情况。下面是某台Linux服务器执行free –m(以兆为到位)的结果，其中需要重点关注的是最后一行的swap统计，从执行结果看，swap一共有4095M，使用了0M，空闲了4095M。\n\n```\n             total       used       free     shared    buffers     cached\nMem:         64385      31573      32812          0        505      10026\n-/+ buffers/cache:      21040      43344\nSwap:         4095          0       4095\n```\n\n在另一台Linux服务器同样执行free -m，这台服务器开启了8189M swap，其中使用了5241M。\n\n```\n             total       used       free     shared    buffers     cached\nMem:         24096       8237      15859          0        136       2483\n-/+ buffers/cache:       5617      18479\nSwap:         8189       5241       2947\n```\n\n#### (2) 实时查看swap的使用\nLinux提供了vmstat命令查询系统的相关性能指标，其中包含负载、CPU、内存、swap、IO的相关属性。但其中和swap有关的指标是si和so，它们分别代表了操作系统的swap in和swap out。下面是执行vmstat 1（每隔一秒输出）的效果，可以看到si和so都为0，代表当前没有使用swap。\n\n```\n# vmstat  1\nprocs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st\n 1  0      0 33593468 517656 10271928    0    0     0     1    0    0  8  0 91  0  0\t\n 4  0      0 33594516 517656 10271928    0    0     0     0 10606 9647 10  1 90  0  0\t\n 1  0      0 33594392 517656 10271928    0    0     0     0 11490 10244 11  1 89  0  0\t\n 6  0      0 33594292 517656 10271928    0    0     0    36 12406 10681 13  1 87  0  0\t\n```\n\n#### (3) 查看指定进程的swap使用情况\nLinux操作系统中，/proc/{pid}目录是存储指定进程的相关信息，其中/proc/{pid}/smaps是记录了当前进程所对应的内存映像信息，这个信息对于查询指定进程的swap使用情况很有帮助。下面以一个Redis实例进行说明\n通过info server获取Redis的进程号process_id:\n\n```\nredis-cli -h ip -p port info server | grep process_id\nprocess_id:986\n```\n\n通过cat /proc/986/smaps查询Redis的smaps信息，由于有多个内存块信息，这里只输出一个内存块镜像信息进行观察。\n\n```\n2aab0a400000-2aab35c00000 rw-p 2aab0a400000 00:00 0 \nSize:            712704 kB\nRss:             617872 kB\nShared_Clean:         0 kB\nShared_Dirty:         0 kB\nPrivate_Clean:    15476 kB\nPrivate_Dirty:   602396 kB\nSwap:             58056 kB\nPss:             617872 kB\n```\n\n其中Swap字段代表该内存块存在swap分区的数据大小。通过执行如下命令，就可以找到每个内存块镜像信息中，这个进程使用到的swap量，通过求和就可以算出总的swap用量。 \n\n```\ncat /proc/986/smaps | grep Swap\nSwap:                 0 kB\nSwap:                 0 kB\n…\nSwap:                 0 kB\nSwap:            478320 kB\n…\nSwap:               624 kB\nSwap:                 0 kB\n```\n\n\n### 4. 最佳实践\n\n如果Linux>3.5，vm.swapniess=1，否则vm.swapniess=0，从而实现如下两个目标：\n\n+ 物理内存充足时候，使Redis足够快。\n+ 物理内存不足时候，避免Redis死掉(如果当前Redis为高可用，死掉比阻塞更好)。\n\n<a name=\"chapter3\"/>\n\n## 三. Transparent Huge Pages \n\nRedis在启动时可能会看到如下日志：\n\n```\nWARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.\n```\n\n从提示看Redis建议修改Transparent Huge Pages (THP)的相关配置，Linux kernel在2.6.38内核增加了Transparent Huge Pages (THP)特性 ，支持大内存页(2MB)分配，默认开启。当开启时可以降低fork子进程的速度，但fork之后，每个内存页从原来4KB变为2MB，会大幅增加重写期间父进程内存消耗。同时每次写命令引起的复制内存页单位放大了512倍，会拖慢写操作的执行时间，导致大量写操作慢查询。例如简单的incr命令也会出现在慢查询中。因此Redis日志中建议将此特性进行禁用，禁用方法如下：\n\n```\necho never >  /sys/kernel/mm/transparent_hugepage/enabled\n```\n\n而且为了使机器重启后THP配置依然生效，可以在/etc/rc.local中追加echo never > /sys/kernel/mm/transparent_hugepage/enabled。\n\n在设置THP配置时需要注意：有些Linux的发行版本没有将THP放到/sys/kernel/mm/transparent_hugepage/enabled中，例如Red Hat 6以上的THP配置放到/sys/kernel/mm/redhat_transparent_hugepage/enabled中。而Redis源码中检查THP时，把THP位置写死。\n\n```\nFILE *fp = fopen(\"/sys/kernel/mm/transparent_hugepage/enabled\",\"r\");\nif (!fp) return 0;   \n```\n\n所以在发行版中，虽然没有THP的日志提示，但是依然存在THP所带来的问题。\n\n```\necho never >  /sys/kernel/mm/redhat_transparent_hugepage/enabled\n```\n\n<a name=\"chapter4\"/>\n## 四. OOM killer\n\nOOM killer会在可用内存不足时选择性的杀掉用户进程，它的运行规则是怎样的，会选择哪些用户进程“下手”呢？OOM killer进程会为每个用户进程设置一个权值，这个权值越高，被“下手”的概率就越高，反之概率越低。每个进程的权值存放在/proc/{progress_id}/oom_score中，这个值是受/proc/{progress_id}/oom_adj的控制，oom_adj在不同的Linux版本的最小值不同，可以参考Linux源码中oom.h(从-15到-17)。当oom_adj设置为最小值时，该进程将不会被OOM killer杀掉，设置方法如下。\n\n```\necho {value} > /proc/${process_id}/oom_adj\n```\n\n对于Redis所在的服务器来说，可以将所有Redis的oom_adj设置为最低值或者稍小的值，降低被OOM killer杀掉的概率。\n\n```\nfor redis_pid in $(pgrep -f \"redis-server\")\ndo\n  echo -17 > /proc/${redis_pid}/oom_adj\ndone\n```\n\n\n### 运维提示：\t\n+ 有关OOM killer的详细细节，可以参考Linux源码mm/oom_kill.c中oom_badness函数。\n+ 笔者认为oom_adj参数只能起到辅助作用，合理的规划内存更为重要。\n+ 通常在高可用情况下，被杀掉比僵死更好，因此不要过多依赖oom_adj配置\n\n\n<a name=\"chapter5\"/>\n## 五. 使用NTP\n\nNTP(Network Time Protocol)网络时间协议，一种保证不同机器时钟一致性的服务。我们知道像Redis Sentinel和Redis Cluster这两种需要多个Redis实例的类型，可能会涉及多台服务器。虽然Redis并没有对多个服务器的时钟有严格的要求，但是假如多个Redis实例所在的服务器时钟不一致，对于一些异常情况的日志排查是非常困难的，例如Redis Cluster的故障转移，如果日志时间不一致，对于我们排查问题带来很大的困扰(注：但不会影响集群功能，集群节点依赖各自时钟)。一般公司里都会有NTP服务用来提供标准时间服务，从而达到纠正时钟的效果(如下图所示)，为此我们可以每天定时去同步一次系统时间，从而使得集群中的时间是统一。\n\n<img src=\"../../img/troubleshooting/NTP.png\" width=\"100%\"/>\n\n  \n例如每小时的同步1次NTP服务\n```\n0 * * * * /usr/sbin/ntpdate ntp.xx.com > /dev/null 2>&1\n```\n\n<a name=\"chapter6\"/>\n## 六. ulimit\n\n在Linux中，可以通过ulimit查看和设置系统的当前用户进程的资源数。其中ulimit -a命令包含的open files参数，是单个用户同时打开的最大文件个数。\n\n```\n# ulimit –a\n…\nmax locked memory       (kbytes, -l) 64\nmax memory size         (kbytes, -m) unlimited\nopen files                      (-n) 1024\npipe size            (512 bytes, -p) 8\n…\n```\n\nRedis允许同时有多个客户端通过网络进行连接，可以通过配置maxclients来限制最大客户端连接数。对Linux操作系统来说这些网络连接都是文件句柄。假设当前open files是4096，那么启动Redis时会看到如下日志。\n\n```\n# You requested maxclients of 10000 requiring at least 10032 max file descriptors.\n# Redis can’t set maximum open files to 10032 because of OS error: Operation not permitted.\n# Current maximum open files is 4096. Maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase ‘ulimit –n’.\n```\n\n上面的日志解释如下：\n\n+ 第一行：Redis建议把open files至少设置成10032，那么这个10032是如何来的呢？因为maxclients的默认是10000，这些是用来处理客户端连接的，除此之外，Redis内部会使用最多32个文件描述符，所以这里的10032 = 10000 + 32。\n+ 第二行：Redis不能将open files设置成10032，因为它没有权限设置。\n+ 第三行：当前系统的open files是4096，所以maxclients被设置成4096-32=4064个，如果你想设置更高的maxclients，请使用ulimit -n来设置。\n\n从上面的三行日志分析可以看出open files的限制优先级比maxclients大。\nopen files的设置方法如下：\n\n```\nulimit –Sn {max-open-files}\n```\n\n<a name=\"chapter7\"/>\n## 七. TCP backlog\n\nRedis默认的tcp-backlog为511，可以通过修改配置tcp-backlog进行调整，如果Linux的tcp-backlog小于Redis设置的tcp-backlog，那么在Redis启动时会看到如下日志：\n\n```\n# WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.\n```\n\n查看方法：\n\n```\ncat /proc/sys/net/core/somaxconn\n128\n```\n\n修改方法：.\n\n```\necho 511 > /proc/sys/net/core/somaxconn\n```"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/liunx.toc.md",
    "content": "##### 目录\n\n* 一. [内存分配控制](#chapter1)\n* 二. [swappiness](#chapter2)\n* 三. [Transparent Huge Pages](#chapter3)\n* 四. [OOM killer](#chapter4)\n* 五. [使用NTP](#chapter5)\n* 六. [ulimit](#chapter6)\n* 七. [TCP backlog](#chapter7)"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/memory.md",
    "content": "Redis所有的数据都在内存中，而内存又是非常宝贵的资源。对于如何优化内存使用一直是Redis用户非常关注的问题。本文让我们深入到Redis细节中，学习内存优化的技巧。分为如下几个部分：\n\n<a name=\"chapter1\"/>\n\n## 一. redisObject对象\n\nRedis存储的所有值对象在内部定义为redisObject结构体，内部结构如下图所示。\n\n<img src=\"../../img/troubleshooting/memory/redis-object.png\" width=\"100%\"/>\n\nRedis存储的数据都使用redisObject来封装，包括string,hash,list,set,zset在内的所有数据类型。理解redisObject对内存优化非常有帮助，下面针对每个字段做详细说明：\n\n<!--more-->\n\n### 1.type字段:\n\n表示当前对象使用的数据类型，Redis主要支持5种数据类型:string,hash,list,set,zset。可以使用type {key}命令查看对象所属类型，type命令返回的是值对象类型，键都是string类型。\n\n### 2.encoding字段:\n\n表示Redis内部编码类型，encoding在Redis内部使用，代表当前对象内部采用哪种数据结构实现。理解Redis内部编码方式对于优化内存非常重要，同一个对象采用不同的编码实现内存占用存在明显差异，具体细节见之后编码优化部分。\n\n### 3.lru字段:\n\n记录对象最后一次被访问的时间，当配置了maxmemory和maxmemory-policy=volatile-lru | allkeys-lru 时， 用于辅助LRU算法删除键数据。可以使用object idletime {key}命令在不更新lru字段情况下查看当前键的空闲时间。\n\n```\n开发提示：可以使用scan + object idletime 命令批量查询哪些键长时间未被访问，找出长时间不访问的键进行清理降低内存占用。\n```\n\n### 4.refcount字段:\n\n记录当前对象被引用的次数，用于通过引用次数回收内存，当refcount=0时，可以安全回收当前对象空间。使用object refcount {key}获取当前对象引用。当对象为整数且范围在[0-9999]时，Redis可以使用共享对象的方式来节省内存，具体细节见之后共享对象池部分。\n\n### 5. *ptr字段: \n\n与对象的数据内容相关，如果是整数直接存储数据，否则表示指向数据的指针。Redis在3.0之后对值对象是字符串且长度<=39字节的数据，内部编码采用embstr类型，字符串sds和redisObject一起分配，从而只要一次内存操作。\n\n```\n开发提示：高并发写入场景中，在条件允许的情况下建议字符串长度控制在39字节以内，减少创建redisObject内存分配次数，从而提高性能。\n```\n<a name=\"chapter2\"/>\n\n## 二. 缩减键值对象\n\n降低Redis内存使用最直接的方式就是缩减键（key）和值（value）的长度。\n\n+ key长度：如在设计键时，在完整描述业务情况下，键值越短越好。\n\n+ value长度：值对象缩减比较复杂，常见需求是把业务对象序列化成二进制数组放入Redis。首先应该在业务上精简业务对象，去掉不必要的属性避免存储无效数据。其次在序列化工具选择上，应该选择更高效的序列化工具来降低字节数组大小。以JAVA为例，内置的序列化方式无论从速度还是压缩比都不尽如人意，这时可以选择更高效的序列化工具，如: protostuff，kryo等，下图是JAVA常见序列化工具空间压缩对比。\n\n<img src=\"../../img/troubleshooting/memory/comparison-of-serializaiton-tools.png\" width=\"100%\"/>\n\n其中java-built-in-serializer表示JAVA内置序列化方式，更多数据见jvm-serializers项目:https://github.com/eishay/jvm-serializers/wiki，其它语言也有各自对应的高效序列化工具。\n\n值对象除了存储二进制数据之外，通常还会使用通用格式存储数据比如:json，xml等作为字符串存储在Redis中。这种方式优点是方便调试和跨语言，但是同样的数据相比字节数组所需的空间更大，在内存紧张的情况下，可以使用通用压缩算法压缩json,xml后再存入Redis，从而降低内存占用，例如使用GZIP压缩后的json可降低约60%的空间。\n\n```\n开发提示：当频繁压缩解压json等文本数据时，开发人员需要考虑压缩速度和计算开销成本，这里推荐使用google的Snappy压缩工具，在特定的压缩率情况下效率远远高于GZIP等传统压缩工具，且支持所有主流语言环境。\n```\n<a name=\"chapter3\"/>\n\n## 三. 共享对象池\n\n对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销，每个redisObject内部结构至少占16字节，甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池，用于节约内存。除了整数值对象，其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下，尽量使用整数对象以节省内存。\n整数对象池在Redis中通过变量REDIS_SHARED_INTEGERS定义，不能通过配置修改。可以通过object refcount 命令查看对象引用数验证是否启用整数对象池技术，如下:\n\n```\nredis> set foo 100 \nOK\nredis> object refcount foo\n(integer) 2\nredis> set bar 100\nOK\nredis> object refcount bar\n(integer) 3\n```\n\n\n设置键foo等于100时，直接使用共享池内整数对象，因此引用数是2，再设置键bar等于100时，引用数又变为3，如下图所示。\n\n<img src=\"../../img/troubleshooting/memory/refcount.png\" width=\"100%\"/>\n\n使用整数对象池究竟能降低多少内存？让我们通过测试来对比对象池的内存优化效果，如下表所示。\n\n\n操作说明 | 是否对象共享 | key大小 | value大小 | used_mem | used_memory_rss\n--- |--- |--- |--- |--- |--- \n插入200万 | 否 | 20字节 | [0-9999]整数 | 199.91MB | 205.28MB\n插入200万 | 是 | 20字节 | [0-9999]整数 | 138.87MB | 143.28MB\n\n```\n注意本文所有测试环境都保持一致，信息如下:\n服务器信息: cpu=Intel-Xeon E5606@2.13GHz memory=32GB\nRedis版本:Redis server v=3.0.7 sha=00000000:0 malloc=jemalloc-3.6.0 bits=64\n```\n\n使用共享对象池后，相同的数据内存使用降低30%以上。可见当数据大量使用[0-9999]的整数时，共享对象池可以节约大量内存。需要注意的是对象池并不是只要存储[0-9999]的整数就可以工作。当设置maxmemory并启用LRU相关淘汰策略如:volatile-lru，allkeys-lru时，Redis禁止使用共享对象池，测试命令如下：\n\n```\nredis> set key:1 99\nOK //设置key:1=99\nredis> object refcount key:1\n(integer) 2 //使用了对象共享,引用数为2\nredis> config set maxmemory-policy volatile-lru\nOK //开启LRU淘汰策略\nredis> set key:2 99\nOK //设置key:2=99\nredis> object refcount key:2\n(integer) 3 //使用了对象共享,引用数变为3\nredis> config set maxmemory 1GB\nOK //设置最大可用内存\nredis> set key:3 99\nOK //设置key:3=99\nredis> object refcount key:3\n(integer) 1 //未使用对象共享,引用数为1\nredis> config set maxmemory-policy volatile-ttl\nOK //设置非LRU淘汰策略\nredis> set key:4 99\nOK //设置key:4=99\nredis> object refcount key:4\n(integer) 4 //又可以使用对象共享,引用数变为4\n```\n\n\n#### 为什么开启maxmemory和LRU淘汰策略后对象池无效?\n\nLRU算法需要获取对象最后被访问时间，以便淘汰最长未访问数据，每个对象最后访问时间存储在redisObject对象的lru字段。对象共享意味着多个引用共享同一个redisObject，这时lru字段也会被共享，导致无法获取每个对象的最后访问时间。如果没有设置maxmemory，直到内存被用尽Redis也不会触发内存回收，所以共享对象池可以正常工作。\n\n综上所述，共享对象池与maxmemory+LRU策略冲突，使用时需要注意。 对于ziplist编码的值对象，即使内部数据为整数也无法使用共享对象池，因为ziplist使用压缩且内存连续的结构，对象共享判断成本过高，ziplist编码细节后面内容详细说明。\n\n#### 为什么只有整数对象池？\n首先整数对象池复用的几率最大，其次对象共享的一个关键操作就是判断相等性，Redis之所以只有整数对象池，是因为整数比较算法时间复杂度为O(1)，只保留一万个整数为了防止对象池浪费。如果是字符串判断相等性，时间复杂度变为O(n)，特别是长字符串更消耗性能(浮点数在Redis内部使用字符串存储)。对于更复杂的数据结构如hash,list等，相等性判断需要O(n<sup>2</sup>)。对于单线程的Redis来说，这样的开销显然不合理，因此Redis只保留整数共享对象池。\n\n<a name=\"chapter4\"/>\n\n## 四. 字符串优化\n字符串对象是Redis内部最常用的数据类型。所有的键都是字符串类型， 值对象数据除了整数之外都使用字符串存储。比如执行命令:lpush cache:type \"redis\" \"memcache\" \"tair\" \"levelDB\" ，Redis首先创建\"cache:type\"键字符串，然后创建链表对象，链表对象内再包含四个字符串对象，排除Redis内部用到的字符串对象之外至少创建5个字符串对象。可见字符串对象在Redis内部使用非常广泛，因此深刻理解Redis字符串对于内存优化非常有帮助。\n\n### 1.字符串结构\n\nRedis没有采用原生C语言的字符串类型而是自己实现了字符串结构，内部简单动态字符串(simple dynamic string)，简称SDS。结构下图所示。\n\n<img src=\"../../img/troubleshooting/memory/SDS.png\" width=\"100%\"/>\n\nRedis自身实现的字符串结构有如下特点:\n\n+ O(1)时间复杂度获取：字符串长度，已用长度，未用长度。\n+ 可用于保存字节数组，支持安全的二进制数据存储。\n+ 内部实现空间预分配机制，降低内存再分配次数。\n+ 惰性删除机制，字符串缩减后的空间不释放，作为预分配空间保留。\n\n\n### 2.预分配机制\n\n因为字符串(SDS)存在预分配机制，日常开发中要小心预分配带来的内存浪费，例如下表的测试用例。\n\n#### 表：字符串内存预分配测试\n\n阶段 | 数据量 | 操作说明 | 命令 | key大小 | value大小 | used_mem | used_memory_rss | mem_fragmentation_ratio\n--- |--- |--- |--- |--- |--- |--- |--- |---\n阶段1 | 200w | 新插入200w数据 | set |20字节 |60字节 |321.98MB |331.44MB |1.02\n阶段2 | 200w | 在阶段1上每个对象追加60字节数据 | append |20字节 |60字节 |657.67MB |752.80MB |1.14\n阶段3 | 200w | 重新插入200w数据 | set |20字节 |120字节 |474.56MB |482.45MB |1.02\n\n\n从测试数据可以看出，同样的数据追加后内存消耗非常严重，下面我们结合图来分析这一现象。阶段1每个字符串对象空间占用如下图所示。\n\n<img src=\"../../img/troubleshooting/memory/SDS-example1.png\" width=\"100%\"/>\n \n阶段1插入新的字符串后，free字段保留空间为0，总占用空间=实际占用空间+1字节，最后1字节保存‘\\0’标示结尾，这里忽略int类型len和free字段消耗的8字节。在阶段1原有字符串上追加60字节数据空间占用如下图所示。\n\n<img src=\"../../img/troubleshooting/memory/SDS-example2.png\" width=\"100%\"/>\n\n追加操作后字符串对象预分配了一倍容量作为预留空间，而且大量追加操作需要内存重新分配，造成内存碎片率(mem_fragmentation_ratio)上升。直接插入与阶段2相同数据的空间占用，如下图所示。\n\n<img src=\"../../img/troubleshooting/memory/SDS-example3.png\" width=\"100%\"/>\n \n\n阶段3直接插入同等数据后，相比阶段2节省了每个字符串对象预分配的空间，同时降低了碎片率。\n字符串之所以采用预分配的方式是防止修改操作需要不断重分配内存和字节数据拷贝。但同样也会造成内存的浪费。字符串预分配每次并不都是翻倍扩容，空间预分配规则如下:\n\n1. 第一次创建len属性等于数据实际大小，free等于0，不做预分配。\n2. 修改后如果已有free空间不够且数据小于1M，每次预分配一倍容量。如原有len=60byte，free=0，再追加60byte，预分配120byte，总占用空间:60byte+60byte+120byte+1byte。\n3. 修改后如果已有free空间不够且数据大于1MB，每次预分配1MB数据。如原有len=30MB，free=0，当再追加100byte ,预分配1MB，总占用空间:1MB+100byte+1MB+1byte。\n\n```\n开发提示:尽量减少字符串频繁修改操作如append，setrange, 改为直接使用set修改字符串，降低预分配带来的内存浪费和内存碎片化。\n```\n\n### 3.字符串重构\n\n字符串重构：指不一定把每份数据作为字符串整体存储，像json这样的数据可以使用hash结构，使用二级结构存储也能帮我们节省内存。同时可以使用hmget,hmset命令支持字段的部分读取修改，而不用每次整体存取。例如下面的json数据：\n```\n{\n    \"vid\": \"4133687xx\",\n    \"title\": \"搜狐屌丝男士\",\n    \"videoAlbumPic\": \"http://xxx.sohu.com/60160518/vrsa_ver8400079_ae433_pic26.jpg\",\n    \"pid\": \"6494271\",\n    \"type\": \"1024\",\n    \"playlist\": \"6494271\",\n    \"playTime\": \"468\"\n}\n```\n\n分别使用字符串和hash结构测试内存表现，如下表所示。\n\n#### 表：测试内存表现\n\n数据量 | key | 存储类型 | value | 配置 | used_mem\n--- |--- |--- |--- |--- |--- \n200W | 20字节 | string | json字符串 | 默认 | 612.62M\n200W | 20字节 | hash | key-value对 | 默认 | 默认\t1.88GB\n200W | 20字节 | hash | key-value对 | hash-max-ziplist-value:66 | 535.60M\n\n根据测试结构，第一次默认配置下使用hash类型，内存消耗不但没有降低反而比字符串存储多出2倍，而调整hash-max-ziplist-value=66之后内存降低为535.60M。因为json的videoAlbumPic属性长度是65，而hash-max-ziplist-value默认值是64，Redis采用hashtable编码方式，反而消耗了大量内存。调整配置后hash类型内部编码方式变为ziplist，相比字符串更省内存且支持属性的部分操作。下一节将具体介绍ziplist编码优化细节。\n\n<a name=\"chapter5\"/>\n\n## 五. 编码优化\n\n### 1.了解编码\n\nRedis对外提供了string,list,hash,set,zet等类型，但是Redis内部针对不同类型存在编码的概念，所谓编码就是具体使用哪种底层数据结构来实现。编码不同将直接影响数据的内存占用和读写效率。使用object encoding {key}命令获取编码类型。如下:\n\n```\nredis> set str:1 hello\nOK\nredis> object encoding str:1\n\"embstr\" // embstr编码字符串\nredis> lpush list:1 1 2 3\n(integer) 3\nredis> object encoding list:1\n\"ziplist\" // ziplist编码列表\n```\n\nRedis针对每种数据类型(type)可以采用至少两种编码方式来实现，下表表示type和encoding的对应关系。\n\n#### 表：type和encoding对应关系表\n\n类型 | 编码方式 | 数据结构\n--- |--- |--- \nstring | raw<br/>embstr<br/>int | 动态字符串编码<br/>优化内存分配的字符串编码<br/>整数编码\nhash | hashtable<br/>ziplist | 散列表编码<br/>压缩列表编码\nlist | linkedlist<br/>ziplist<br/>quicklist<br/> | 双向链表编码<br/>压缩列表编码<br/>3.2版本新的列表编码\nset | hashtable<br/>intset | 散列表编码<br/>整数集合编码\nzset | skiplist<br/>ziplist | 跳跃表编码<br/>压缩列表编码\n\n了解编码和类型对应关系之后，我们不禁疑惑Redis为什么需要对一种数据结构实现多种编码方式？\n主要原因是Redis作者想通过不同编码实现效率和空间的平衡。比如当我们的存储只有10个元素的列表，当使用双向链表数据结构时，必然需要维护大量的内部字段如每个元素需要：前置指针，后置指针，数据指针等，造成空间浪费，如果采用连续内存结构的压缩列表(ziplist)，将会节省大量内存，而由于数据长度较小，存取操作时间复杂度即使为O(n<sup>2</sup>)性能也可满足需求。\n\n\n### 2.控制编码类型\n\n编码类型转换在Redis写入数据时自动完成，这个转换过程是不可逆的，转换规则只能从小内存编码向大内存编码转换。例如：\n\n```\nredis> lpush list:1 a b c d\n(integer) 4 //存储4个元素\nredis> object encoding list:1\n\"ziplist\" //采用ziplist压缩列表编码\nredis> config set list-max-ziplist-entries 4\nOK //设置列表类型ziplist编码最大允许4个元素\nredis> lpush list:1 e\n(integer) 5 //写入第5个元素e\nredis> object encoding list:1\n\"linkedlist\" //编码类型转换为链表\nredis> rpop list:1\n\"a\" //弹出元素a\nredis> llen list:1\n(integer) 4 // 列表此时有4个元素\nredis> object encoding list:1\n\"linkedlist\" //编码类型依然为链表，未做编码回退\n```\n\n以上命令体现了list类型编码的转换过程，其中Redis之所以不支持编码回退，主要是数据增删频繁时，数据向压缩编码转换非常消耗CPU，得不偿失。以上示例用到了list-max-ziplist-entries参数，这个参数用来决定列表长度在多少范围内使用ziplist编码。当然还有其它参数控制各种数据类型的编码，如下表所示：\n\n#### 表：hash,list,set,zset内部编码配置\n\n\n类型 | 编码 | 决定条件\n--- |--- |--- \nhash | ziplist | 满足所有条件:<br/>value最大空间(字节)<=hash-max-ziplist-value<br/>field个数<=hash-max-ziplist-entries\n同上 | hashtable | 满足任意条件:<br/>value最大空间(字节)>hash-max-ziplist-value<br/>field个数>hash-max-ziplist-entries\nlist | ziplist | ziplist\t满足所有条件:<br/>value最大空间(字节)<=list-max-ziplist-value<br/>链表长度<=list-max-ziplist-entries\n同上 | linkedlist | 满足任意条件<br/>value最大空间(字节)>list-max-ziplist-value<br/>链表长度>list-max-ziplist-entries\n同上 | quicklist | 3.2版本新编码:<br/>废弃list-max-ziplist-entries和list-max-ziplist-entries配置<br/>使用新配置: <br/>list-max-ziplist-size:表示最大压缩空间或长度,最大空间使用[-5-1]范围配置，默认-2表示8KB,正整数表示最大压缩长度<br/>list-compress-depth:表示最大压缩深度，默认=0不压缩\nset | intset | 满足所有条件:<br/>元素必须为整数<br/>集合长度<=set-max-intset-entries\n同上 | hashtable | 满足任意条件<br/>元素非整数类型<br/>集合长度>hash-max-ziplist-entries\nzset | ziplist | 满足所有条件:<br/>value最大空间(字节)<=zset-max-ziplist-value<br/>有序集合长度<=zset-max-ziplist-entries\n同上 | skiplist | 满足任意条件:<br/>value最大空间(字节)>zset-max-ziplist-value<br/>有序集合长度>zset-max-ziplist-entries\n\t\n掌握编码转换机制，对我们通过编码来优化内存使用非常有帮助。下面以hash类型为例，介绍编码转换的运行流程，如下图所示。\n\n<img src=\"../../img/troubleshooting/memory/hash-encoding-transform.png\" width=\"100%\"/> \n\n理解编码转换流程和相关配置之后，可以使用config set命令设置编码相关参数来满足使用压缩编码的条件。对于已经采用非压缩编码类型的数据如hashtable,linkedlist等，设置参数后即使数据满足压缩编码条件，Redis也不会做转换，需要重启Redis重新加载数据才能完成转换。\n\n### 3.ziplist编码\n\nziplist编码主要目的是为了节约内存，因此所有数据都是采用线性连续的内存结构。ziplist编码是应用范围最广的一种，可以分别作为hash、list、zset类型的底层数据结构实现。首先从ziplist编码结构开始分析，它的内部结构类似这样:<zlbytes><zltail><zllen><entry-1><entry-2><....><entry-n><zlend>。一个ziplist可以包含多个entry(元素)，每个entry保存具体的数据(整数或者字节数组)，内部结构如下图所示。\n \n<img src=\"../../img/troubleshooting/memory/ziplist-structure.png\" width=\"100%\"/>\n\nziplist结构字段含义：\n\n1. zlbytes:记录整个压缩列表所占字节长度，方便重新调整ziplist空间。类型是int-32，长度为4字节\n2. zltail:记录距离尾节点的偏移量，方便尾节点弹出操作。类型是int-32，长度为4字节\n3. zllen:记录压缩链表节点数量，当长度超过216-2时需要遍历整个列表获取长度，一般很少见。类型是int-16，长度为2字节\n4. entry:记录具体的节点，长度根据实际存储的数据而定。 \n    1. prev_entry_bytes_length:记录前一个节点所占空间，用于快速定位上一个节点，可实现列表反向迭代。\n    2. encoding:标示当前节点编码和长度，前两位表示编码类型：字符串/整数，其余位表示数据长度。 \n    3. contents:保存节点的值，针对实际数据长度做内存占用优化。\n5. zlend:记录列表结尾，占用一个字节。\n\n根据以上对ziplist字段说明，可以分析出该数据结构特点如下:\n\n1. 内部表现为数据紧凑排列的一块连续内存数组。\n2. 可以模拟双向链表结构，以O(1)时间复杂度入队和出队。\n3. 新增删除操作涉及内存重新分配或释放，加大了操作的复杂性。\n4. 读写操作涉及复杂的指针移动，最坏时间复杂度为O(n<sup>2</sup>)。\n5. 适合存储小对象和长度有限的数据。\n\n下面通过测试展示ziplist编码在不同类型中内存和速度的表现，如下表所示。\n\n#### 表：ziplist在hash,list,zset内存和速度测试\n\n类型 |\t数据量\t| key总数量\t| 长度|\tvalue大小\t|普通编码内存量/平均耗时\t|压缩编码内存量/平均耗时\t|内存降低比例|\t耗时增长倍数\n--- |--- |--- |--- |--- |--- |---\nhash|\t100万|\t1千|\t1千|\t36字节|\t103.37M/0.84微秒|\t43.83M/13.24微秒\t|57.5%|\t15倍\nlist|\t100万|\t1千|\t1千|\t36字节|\t92.46M/2.04微秒|\t39.92M/5.45微秒|\t56.8%\t|2.5倍|\nzset|\t100万|\t1千|\t1千|\t36字节|\t151.84M/1.85微秒|\t43.83M/77.88微秒\t|71%\t|42倍\n\n\n测试数据采用100W个36字节数据，划分为1000个键，每个类型长度统一为1000。从测试结果可以看出：\n\n1. 使用ziplist可以分别作为hash,list,zset数据类型实现。\n2. 使用ziplist编码类型可以大幅降低内存占用。\n3. ziplist实现的数据类型相比原生结构，命令操作更加耗时，不同类型耗时排序:list < hash < zset。\n\nziplist压缩编码的性能表现跟值长度和元素个数密切相关，正因为如此Redis提供了{type}-max-ziplist-value和{type}-max-ziplist-entries相关参数来做控制ziplist编码转换。最后再次强调使用ziplist压缩编码的原则：追求空间和时间的平衡。\n\n```\n开发提示：\n1）针对性能要求较高的场景使用ziplist，建议长度不要超过1000，每个元素大小控制在512字节以内。\n2）命令平均耗时使用info Commandstats命令获取，包含每个命令调用次数，总耗时，平均耗时，单位微秒。\n```\n\n### 4.intset编码\n\nintset编码是集合(set)类型编码的一种，内部表现为存储有序，不重复的整数集。当集合只包含整数且长度不超过set-max-intset-entries配置时被启用。执行以下命令查看intset表现：\n\n```\n127.0.0.1:6379> sadd set:test 3 4 2 6 8 9 2\n(integer) 6 //乱序写入6个整数\n127.0.0.1:6379> object encoding set:test\n\"intset\" //使用intset编码\n127.0.0.1:6379> smembers set:test\n\"2\" \"3\" \"4\" \"6\" \"8\" \"9\" // 排序输出整数结合\nredis> config set set-max-intset-entries 6\nOK //设置intset最大允许整数长度\nredis> sadd set:test 5\n(integer) 1 //写入第7个整数 5\nredis> object encoding set:test\n\"hashtable\" // 编码变为hashtable\nredis> smembers set:test\n\"8\" \"3\" \"5\" \"9\" \"4\" \"2\" \"6\" //乱序输出\n```\n\n以上命令可以看出intset对写入整数进行排序，通过O(log(n))时间复杂度实现查找和去重操作，intset编码结构如下图所示。\n\n<img src=\"../../img/troubleshooting/memory/intset-structure.png\" width=\"100%\"/>\n\n\nintset的字段结构含义：\n\n1. encodind:整数表示类型，根据集合内最长整数值确定类型，整数类型划分三种：int-16，int-32，int-64。\n2. length:表示集合元素个数。\n3. contents:整数数组，按从小到大顺序保存。\n\nintset保存的整数类型根据长度划分，当保存的整数超出当前类型时，将会触发自动升级操作且升级后不再做回退。升级操作将会导致重新申请内存空间，把原有数据按转换类型后拷贝到新数组。\n\n```\n开发提示：使用intset编码的集合时，尽量保持整数范围一致，如都在int-16范围内。防止个别大整数触发集合升级操作，产生内存浪费。\n下面通过测试查看ziplist编码的集合内存和速度表现，如下表所示。\n```\n\n#### 表：ziplist编码在set下内存和速度表现\n\n数据量|\tkey大小|\tvalue大小|\t编码\t| 集合长度|\t内存量|\t内存降低比例|\t平均耗时\n--- |--- |--- |--- |--- |--- |---\n100w|\t20byte|\t7字节|\thashtable|\t1千|\t61.97MB| -- | 0.78毫秒\n100w|\t20byte|\t7字节|\tintset|\t1千|\t4.77MB|\t92.6%|\t0.51毫秒\n100w|\t20byte|\t7字节|\tziplist|\t1千|\t8.67MB|\t86.2%|\t13.12毫秒\n\n根据以上测试结果发现intset表现非常好，同样的数据内存占用只有不到hashtable编码的十分之一。intset数据结构插入命令复杂度为O(n)，查询命令为O(log(n))，由于整数占用空间非常小，所以在集合长度可控的基础上，写入命令执行速度也会非常快，因此当使用整数集合时尽量使用intset编码。上表测试第三行把ziplist-hash类型也放入其中，主要因为intset编码必须存储整数，当集合内保存非整数数据时，无法使用intset实现内存优化。这时可以使用ziplist-hash类型对象模拟集合类型，hash的field当作集合中的元素，value设置为1字节占位符即可。使用ziplist编码的hash类型依然比使用hashtable编码的集合节省大量内存。\n\n<a name=\"chapter6\"/>\n\n## 六 控制key的数量\n\n当使用Redis存储大量数据时，通常会存在大量键，过多的键同样会消耗大量内存。Redis本质是一个数据结构服务器，它为我们提供多种数据结构，如hash，list，set，zset 等结构。使用Redis时不要进入一个误区，大量使用get/set这样的API，把Redis当成Memcached使用。对于存储相同的数据内容利用Redis的数据结构降低外层键的数量，也可以节省大量内存。如下图所示，通过在客户端预估键规模，把大量键分组映射到多个hash结构中降低键的数量。\n\n<img src=\"../../img/troubleshooting/memory/keys-hash.png\" width=\"100%\"/>\n\n\nhash结构降低键数量分析：\n\n+\t根据键规模在客户端通过分组映射到一组hash对象中，如存在100万个键，可以映射到1000个hash中，每个hash保存1000个元素。\n+\thash的field可用于记录原始key字符串，方便哈希查找。\n+\thash的value保存原始值对象，确保不要超过hash-max-ziplist-value限制。\n\n下面测试这种优化技巧的内存表现，如下表所示。\n\n\n#### 表：hash分组控制键规模测试\n\n数据量|\tkey大小|\tvalue大小|\tstring类型占用内存|\thash-ziplist类型占用内存|\t内存降低比例|\tstring:set平均耗时|\thash:hset平均耗时|\n--- |--- |--- |--- |--- |--- |--- |---\n200w|\t20byte|\t512byte|\t1392.64MB|\t1000.97MB|\t28.1%|\t2.13微秒|\t21.28微秒\n200w|\t20byte|\t200byte|\t596.62MB|\t399.38MB|\t33.1%|\t1.49微秒|\t16.08微秒\n200w|\t20byte|\t100byte|\t382.99MB|\t211.88MB|\t44.6%|\t1.30微秒|\t14.92微秒\n200w|\t20byte|\t50byte|\t291.46MB|\t110.32MB|\t62.1%|\t1.28微秒|\t13.48微秒\n200w|\t20byte|\t20byte|\t246.40MB|\t55.63MB|\t77.4%|\t1.10微秒|\t13.21微秒\n200w|\t20byte|\t5byte|\t199.93MB|\t24.42MB|\t87.7%|\t1.10微秒|\t13.06微秒\n\n通过这个测试数据，可以说明：\n\n+ 同样的数据使用ziplist编码的hash类型存储比string类型节约内存\n+ 节省内存量随着value空间的减少，越来越明显。\n+ hash-ziplist类型比string类型写入耗时，但随着value空间的减少，耗时逐渐降低。\n\n使用hash重构后节省内存量效果非常明显，特变对于存储小对象的场景，内存只有不到原来的1/5。下面分析这种内存优化技巧的关键点：\n\n1. hash类型节省内存的原理是使用ziplist编码，如果使用hashtable编码方式反而会增加内存消耗。\n2. ziplist长度需要控制在1000以内，否则由于存取操作时间复杂度在O(n)到O(n2)之间，长列表会导致CPU消耗严重，得不偿失。\n3. ziplist适合存储的小对象，对于大对象不但内存优化效果不明显还会增加命令操作耗时。\n4. 需要预估键的规模，从而确定每个hash结构需要存储的元素数量。\n5. 根据hash长度和元素大小，调整hash-max-ziplist-entries和hash-max-ziplist-value参数，确保hash类型使用ziplist编码。\n\n关于hash键和field键的设计：\n\n1. 当键离散度较高时，可以按字符串位截取，把后三位作为哈希的field，之前部分作为哈希的键。如：key=1948480 哈希key=group:hash:1948，哈希field=480。\n2. 当键离散度较低时，可以使用哈希算法打散键，如:使用crc32(key)&10000函数把所有的键映射到“0-9999”整数范围内，哈希field存储键的原始值。\n3. 尽量减少hash键和field的长度，如使用部分键内容。\n\n使用hash结构控制键的规模虽然可以大幅降低内存，但同样会带来问题，需要提前做好规避处理。如下:\n\n1. 客户端需要预估键的规模并设计hash分组规则，加重客户端开发成本。\n2. hash重构后所有的键无法再使用超时(expire)和LRU淘汰机制自动删除，需要手动维护删除。\n3. 对于大对象，如1KB以上的对象。使用hash-ziplist结构控制键数量。\n不过瑕不掩瑜，对于大量小对象的存储场景，非常适合使用ziplist编码的hash类型控制键的规模来降低内存。\n\n```\n开发提示：使用ziplist+hash优化keys后，如果想使用超时删除功能，开发人员可以存储每个对象写入的时间，再通过定时任务使用hscan命令扫描数据，找出hash内超时的数据项删除即可。\n```\n\n本文主要讲解Redis内存优化技巧，Redis的数据特性是\"ALL IN MEMORY\"，优化内存将变得非常重要。对于内存优化建议读者先要掌握Redis内存存储的特性比如字符串，压缩编码，整数集合等，再根据数据规模和所用命令需求去调整，从而达到空间和效率的最佳平衡。建议使用Redis存储大量数据时，把内存优化环节加入到前期设计阶段，否则数据大幅增长后，开发人员需要面对重新优化内存所带来开发和数据迁移的双重成本。当Redis内存不足时，首先考虑的问题不是加机器做水平扩展，应该先尝试做内存优化。当遇到瓶颈时，再去考虑水平扩展。即使对于集群化方案，垂直层面优化也同样重要，避免不必要的资源浪费和集群化后的管理成本。"
  },
  {
    "path": "cachecloud-web/src/main/resources/static/wiki/troubleshooting/memory.toc.md",
    "content": "##### 目录\n\n* 一. [redisObject对象](#chapter1)\n* 二. [缩减键值对象](#chapter2)\n* 三. [共享对象池](#chapter3)\n* 四. [字符串优化](#chapter4)\n* 五. [编码优化](#chapter5)\n* 六. [控制key的数量](#chapter6)"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/404.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>CacheCloud</title>\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n    <#include '/inc/frontResources.html'>\n  </head>\n\n  <body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n    <div class=\"wrapper\">\n      <#include '/inc/head.html'>\n      <div class=\"content-wrapper ml-0\">\n        <div class=\"content\">\n          <div class=\"container-fluid\">\n            <div class=\"row\">\n              <div class=\"col-12\">\n                <div class=\"card\">\n                  <div class=\"card-body\">\n                    <h4>\n                      <img width=\"70\" height=\"60\" src=\"${request.contextPath}/assets/img/cry.jpg\">\n                      您查找的内容不存在，请确认。\n                    </h4>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/OperationAlert.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>CacheCloud运维操作通知</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n            <ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">运维操作机器通知：</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        机器ip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        宿主机ip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        机器详情\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        操作\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        Status\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        Message\n                    </td>\n                </tr>\n                <#list operationAlertValueResultList as item>\n                    <tr>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.ip!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <#if item.machineInfo??>\n                                ${item.machineInfo.realIp!}\n                            </#if>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <#if item.machineInfo??>\n                                内存：${item.machineInfo.mem!} G<br/>\n                                cpu：${item.machineInfo.cpu!}<br/>\n                                备注说明：${item.machineInfo.extraDesc!}\n                            </#if>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.type!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.status!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.message!}\n                        </td>\n                    </tr>\n                </#list>\n\n            </table>\n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/analysis/appKey.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      集群键值分析\n      <a target=\"_blank\" href=\"${request.contextPath}/admin/app/appKeyAnalysis?appId=${appDesc.appId}\" class=\"btn btn-info\" role=\"button\">键值分析</a>\n    </h3>\n  </div>\n  <div class=\"card-body table-responsive\">\n    <!-- table begin -->\n    <table class=\"table table-striped\">\n      <thead>\n      <th scope=\"col\">审批ID</td>\n      <th scope=\"col\">申请时间</td>\n      <th scope=\"col\">申请人</td>\n      <th scope=\"col\">审批状态</td>\n      <th scope=\"col\">申请描述</td>\n      <th scope=\"col\">详情</td>\n      </thead>\n      <tbody>\n        <#list appAuditList as appAudit>\n          <tr>\n            <td>${appAudit.id}</td>\n            <td>\n              ${appAudit.createTime?string(\"yyyy-MM-dd HH:mm:ss\")}\n            </td>\n            <td>${appAudit.userName}</td>\n            <td>\n              <#if (appAudit.status == 0)>待审\n              <#elseif (appAudit.status == 1)>通过\n              <#elseif (appAudit.status == 2)>审核已处理\n              <#elseif (appAudit.status == -1)>驳回\n              </#if>\n            </td>\n            <td>${appAudit.info}</td>\n            <td>\n              <#if (appAudit.status == 1)>\n                <a href=\"${request.contextPath}/admin/app/keyAnalysisResult?appId=${appDesc.appId}&auditId=${appAudit.id}\">[查看结果]</a>\n              <#else>\n                [分析中]\n              </#if>\n            </td>\n          </tr>\n        </#list>\n      </tbody>\n    </table>\n  <!-- End Table with stripped rows -->\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/analysis/keyAnalysis.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>键值分析</title>\n  <#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n<div class=\"wrapper\">\n  <#include '/inc/head.html'>\n  <div class=\"content-wrapper ml-0\">\n    <div class=\"content\">\n      <div class=\"container-fluid\">\n        <div class=\"row\">\n          <div class=\"col-12\">\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h4 class=\"card-title\">集群键值分析</h4>\n              </div>\n              <div class=\"card-body\">\n                <div class=\"row\">\n                  <div class=\"col-md-6\">\n                    <div id=\"idleKeyDistriContainer\"\n                         style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n                  </div>\n                  <div class=\"col-md-6\">\n                    <div id=\"keyTtlDistriContainer\"\n                         style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n                  </div>\n                  <div class=\"col-md-6\">\n                    <div id=\"keyTypeDistriContainer\"\n                         style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n                  </div>\n                  <div class=\"col-md-6\">\n                    <div id=\"keyValueSizeDistriContainer\"\n                         style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n                  </div>\n                </div>\n              </div>\n            </div>\n\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                  键值类型分布\n                </h3>\n              </div>\n              <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-hover\" style=\"margin-top: 0px\">\n                  <thead>\n                  <tr>\n                    <th scope=\"col\">类型</th>\n                    <th scope=\"col\">个数</th>\n                  </tr>\n                  </thead>\n                  <tbody>\n                  <#list keyTypeParamCountList as keyTypeParamCount>\n                  <tr>\n                    <td>${keyTypeParamCount.param!}</td>\n                    <td>${keyTypeParamCount.count!}</td>\n                  </tr>\n                  </#list>\n                  </tbody>\n                </table>\n              </div>\n            </div>\n\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                  键值过期分布\n                </h3>\n              </div>\n              <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-hover\" style=\"margin-top: 0px\">\n                  <thead>\n                  <tr>\n                    <th scope=\"col\">类型</th>\n                    <th scope=\"col\">个数</th>\n                  </tr>\n                  </thead>\n                  <tbody>\n                  <#list keyTypeParamCountList as keyTypeParamCount>\n                  <tr>\n                    <td>${keyTypeParamCount.param!}</td>\n                    <td>${keyTypeParamCount.count!}</td>\n                  </tr>\n                  </#list>\n                  </tbody>\n                </table>\n              </div>\n            </div>\n\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                  键值空闲分布\n                </h3>\n              </div>\n              <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-hover\" style=\"margin-top: 0px\">\n                  <thead>\n                  <tr>\n                    <th scope=\"col\">分布</th>\n                    <th scope=\"col\">个数</th>\n                  </tr>\n                  </thead>\n                  <tbody>\n                  <#list idleKeyParamCountList as idleKeyParamCount>\n                  <tr>\n                    <td>${idleKeyParamCount.param!}</td>\n                    <td>${idleKeyParamCount.count!}</td>\n                  </tr>\n                  </#list>\n                  </tbody>\n                </table>\n              </div>\n            </div>\n\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                  键值值分布\n                </h3>\n              </div>\n              <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-hover\" style=\"margin-top: 0px\">\n                  <thead>\n                  <tr>\n                    <th scope=\"col\">分布</th>\n                    <th scope=\"col\">个数</th>\n                  </tr>\n                  </thead>\n                  <tbody>\n                  <#list keyValueSizeParamCountList as keyValueSizeParamCount>\n                  <tr>\n                    <td>${keyValueSizeParamCount.param!}</td>\n                    <td>${keyValueSizeParamCount.count!}</td>\n                  </tr>\n                  </#list>\n                  </tbody>\n                </table>\n              </div>\n            </div>\n\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                  BigKey列表(共${instanceBigKeyCount!0}个)\n                </h3>\n              </div>\n              <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-hover\" style=\"margin-top: 0px\">\n                  <thead>\n                  <tr>\n                    <th scope=\"col\">实例信息</th>\n                    <th scope=\"col\">键名称</th>\n                    <th scope=\"col\">类型</th>\n                    <th scope=\"col\">长度</th>\n                    <th scope=\"col\">创建时间</th>\n                  </tr>\n                  </thead>\n                  <tbody>\n                  <#list instanceBigKeyList as instanceBigKey>\n                  <tr>\n                    <td>${instanceBigKey.ip!}:${instanceBigKey.port!}</td>\n                    <td>${instanceBigKey.bigKey!}</td>\n                    <td>${instanceBigKey.type!}</td>\n                    <td>${instanceBigKey.length!}</td>\n                    <td>\n                      ${instanceBigKey.createTime?string('yyyy-MM-dd HH:mm:ss')}\n                    </td>\n                  </tr>\n                  </#list>\n                  </tbody>\n                </table>\n              </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <#include \"/inc/footer.html\">\n</div>\n\n<script type=\"text/javascript\">\n  $(function(){\n    var idleKeyStr = ${idleKeyDistri!};\n    var idleKeyData = [];\n    if(idleKeyStr != null && idleKeyStr != ''){\n      idleKeyData = idleKeyStr;\n    }\n    generateDataPieChart('idleKeyDistriContainer', 'key闲置分布', idleKeyData);\n    var keyTtlStr = ${keyTtlDistri!};\n    var keyTtlData = [];\n    if(keyTtlStr != null && keyTtlStr != ''){\n      keyTtlData = keyTtlStr;\n    }\n    generateDataPieChart('keyTtlDistriContainer', 'key过期分布', keyTtlData);\n    var keyTypeStr = ${keyTypeDistri!};\n    var keyTypeData = [];\n    if(keyTypeStr != null && keyTypeStr != ''){\n      keyTypeData = keyTypeStr;\n    }\n    generateDataPieChart('keyTypeDistriContainer', 'key类型分布', keyTypeData);\n    var keyValueSizeStr = ${keyValueSizeDistri!};\n    var keyValueSizeData = [];\n    if(keyValueSizeStr != null && keyValueSizeStr != ''){\n      keyValueSizeData = keyValueSizeStr;\n    }\n    generateDataPieChart('keyValueSizeDistriContainer', 'key值分布', keyValueSizeData);\n  });\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appClientList.html",
    "content": "<div class=\"col-md-12\">\n    <div class=\"card-header\">\n      <h4 class=\"card-title\">\n        应用连接信息\n      </h4>\n    </div>\n    <h6 class=\"mt-3 ms-3\">\n      <form method=\"post\" action=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId}&tabTag=app_clientList\" id=\"conditionFrom\">\n        <input type=\"radio\" value=\"0\" <#if (condition == 0)>checked=\"checked\"</#if>\n        onchange=\"changeCondition(this.value)\"/> 应用客户端\n        <input type=\"radio\" value=\"1\" <#if (condition == 1)>checked=\"checked\"</#if>\n        onchange=\"changeCondition(this.value)\"/> cc客户端\n        <input type=\"radio\" value=\"2\" <#if (condition == 2)>checked=\"checked\"</#if>\n        onchange=\"changeCondition(this.value)\"/> redis客户端\n        <input type=\"radio\" value=\"3\" <#if (condition == 3)>checked=\"checked\"</#if>\n        onchange=\"changeCondition(this.value)\"/> 所有客户端\n        <input type=\"hidden\" id=\"condition\" name=\"condition\" value=\"${condition}\">\n      </form>\n    </h6>\n    <div class=\"table-responsive\">\n      <!-- table begin -->\n      <table class=\"table table-striped\">\n        <thead>\n        <th scope=\"col\">序号</td>\n        <th scope=\"col\">客户端ip</td>\n        <th scope=\"col\">总连接数</td>\n        <th scope=\"col\">客户端类型</td>\n        <th scope=\"col\">实例详细</td>\n        </thead>\n        <tbody>\n        <#list addrInstanceList as addrInstance>\n          <tr>\n            <td>${addrInstance_index + 1}</td>\n            <td>${addrInstance['addr']}</td>\n            <td>${addrInstance['size']}</td>\n            <td>\n              <#list addrInstance['flags'] as clientType>\n                ${clientType}\n                <br/>\n              </#list>\n            </td>\n            <td>\n              <button type=\"button\" class=\"btn btn-success\" data-bs-target=\"#modal-${addrInstance_index}\"\n                      data-bs-toggle=\"modal\">\n                查看实例连接统计\n              </button>\n\n              <div id=\"modal-${addrInstance_index}\" class=\"modal fade\" tabindex=\"-1\">\n                <div class=\"modal-dialog\" style=\"width: 100%\">\n                  <div class=\"modal-content\">\n                    <div class=\"modal-header\">\n                      <h4 class=\"modal-title\">客户端 ${addrInstance['addr']} 连接信息</h4>\n                      <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"\n                              aria-hidden=\"true\"></button>\n                    </div>\n\n                    <div class=\"modal-body\">\n                      <div class=\"container-fluid\">\n                        <div class=\"row\">\n                          <!-- 控件开始 -->\n                          <div class=\"col-md-12\">\n                            <table class=\"table table-bordered table-striped table-hover\">\n                              <thead>\n                              <tr>\n                                <td>实例id</td>\n                                <td>实例</td>\n                                <td>连接数</td>\n                              </tr>\n                              </thead>\n                              <tbody>\n                              <#list addrInstance['instanceClientStats']?keys as key>\n                                <#assign instanceId = key>\n                                <tr>\n                                  <td>${instanceId}</td>\n                                  <td><a href=\"${request.contextPath}/admin/instance/index?instanceId=${instanceId}&tabTag=instance_clientList\" target=\"_blank\">${instanceMap?api.get(instanceId)}</a></td>\n                                  <td>${((addrInstance['instanceClientStats'])?api.get(instanceId))['count']}</td>\n                                </tr>\n                              </#list>\n                              </tbody>\n                            </table>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n                    <div class=\"modal-footer\">\n                      <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </td>\n          </tr>\n        </#list>\n        </tbody>\n      </table>\n      <!-- End Table with stripped rows -->\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  function changeCondition(value) {\n    console.log('radio:'+value);\n    $('#condition').val(value);\n    $('#conditionFrom').submit();\n  }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appCommand.html",
    "content": "<script type=\"text/javascript\" src=\"${request.contextPath}/assets/js/custom/jquery-console.js\"></script>\n<link href=\"${request.contextPath}/assets/css/mem-cloud.css\" rel=\"stylesheet\" />\n<div class=\"col-md-12\">\n  <div class=\"table-responsive pt-3\">\n    <div id=\"console\" class=\"console\"></div>\n    <script type=\"text/javascript\">\n      $(document).ready(function () {\n        var console = $('#console');\n        var controller = console.console({\n          promptLabel: 'appId:${appId}> ',\n          commandValidate: function (line) {\n            if (line == \"\") return false;\n            else return true;\n          },\n          commandHandle: function (line,report) {\n            $.ajax({\n              url: \"${request.contextPath}/admin/app/commandExecute.json\",\n              data: {appId: $('#appId').val(), command: line},\n              dataType: \"json\",\n              success: function (result) {\n                report([\n                  {msg: result.result,\n                    className: \"jquery-console-message-value\"}\n                ]);\n              }\n            });\n          },\n          autofocus: true,\n          animateScroll: true,\n          promptHistory: true\n        });\n      });\n    </script>\n    <center><h4><font color='red'>注意：非测试应用只可以执行只读命令，如有需要清理数据请联系管理员！</font></h4></center>\n\n    <input type=\"hidden\" id=\"appId\" value=\"${appId}\">\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appCommandAnalysis.html",
    "content": "<div class=\"col-md-12\">\n  <script type=\"text/javascript\">\n    var firstCommand = '${firstCommand}';\n    var startDate = '${startDate}';\n    var endDate = '${endDate}';\n    var yesterDate = '${yesterDay}';\n    var betweenOneDay = '${betweenOneDay}';\n    var appId = '${appId}';\n    var chartParams = \"&startDate=\"+startDate+\"&endDate=\"+endDate;\n    var chartParamsCompare = \"&startDate=\"+yesterDate+\"&endDate=\"+startDate;\n    var betweenParams = \"&startDate=\"+yesterDate+\"&endDate=\"+endDate;\n    Highcharts.setOptions({\n      global : {\n        useUTC : false\n      }\n    });\n    Highcharts.setOptions({\n      colors : ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n        '#492970', '#804000', '#f28f43', '#77a1e5',\n        '#c42525', '#a6c96a']\n    });\n\n    function changeCommandChart(value){\n      document.getElementById(\"firstCommand\").value = value;\n      document.getElementById(\"formSingCommand\").submit();\n    }\n  </script>\n\n  <form method=\"post\" action=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId}&tabTag=app_command_analysis\" id=\"formSingCommand\">\n    <div class=\"row justify-content-end\">\n    <label class=\"col-auto col-form-label\" style=\"font-weight:bold;\">\n        开始日期:&nbsp;&nbsp;\n      </label>\n      <div class=\"col-auto\">\n        <input type=\"date\" class=\"form-control\" size=\"21\" name=\"startDate\" id=\"startDate\" value=\"${startDate}\"/>\n      </div>\n      <label class=\"col-auto col-form-label\" style=\"font-weight:bold;\">\n        结束日期:&nbsp;&nbsp;\n      </label>\n      <div class=\"col-auto\">\n        <input type=\"date\" class=\"form-control\" size=\"20\" name=\"endDate\" id=\"endDate\" value=\"${endDate}\"/>\n        <input type=\"hidden\" id=\"firstCommand\" name=\"firstCommand\" value=\"${firstCommand}\">\n      </div>\n      <input type=\"submit\" class=\"btn btn-primary col-auto\" value=\"查询\"/>\n    </div>\n    <div class=\"row\">\n      <div class=\"d-flex justify-content-start\">\n        <#assign needSelect = \"0\">\n        &nbsp;&nbsp;Top5命令:&nbsp;&nbsp;\n        <#list allCommands as item>\n          <#if (item_index < 5)>\n              <div class=\"form-check\">\n                <input type=\"radio\" class=\"form-check-input\" name=\"optionsRadios\" value=\"${item.commandName}\"\n                <#if (firstCommand == item.commandName)>checked=\"checked\"</#if>\n                onchange=\"changeCommandChart(this.value)\" />\n                ${item.commandName}&nbsp;&nbsp;\n              </div>\n          <#else>\n            <#assign needSelect = \"1\">\n          </#if>\n        </#list>\n        <div class=\"col-auto\">\n          <#if (needSelect == \"1\")>\n            <div class=\"row\">\n              <label class=\"col-auto\">\n                &nbsp;其余命令:\n              </label>\n              <div class=\"col-auto\">\n                <select name=\"optionsRadios\" class=\"form-select w-100 mb-2\" onchange=\"changeCommandChart(this.value)\">\n                  <option>请选择</option>\n                  <#list allCommands as item>\n                    <#if (item_index >= 5)>\n                        <label>\n                          <option value=\"${item.commandName}\" <#if (firstCommand == item.commandName)>selected</#if>>\n                          ${item.commandName}\n                          </option>\n                        </label>\n                    </#if>\n                  </#list>\n                </select>\n              </div>\n            </div>\n          </#if>\n        </div>\n      </div>\n    </div>\n  </form>\n  <script type=\"text/javascript\">\n    var title = \"<b>\"+firstCommand+\"命令</b>\";\n    if(betweenOneDay == 1){\n      $(document).ready(\n              function() {\n                var options = getOption(\"containerSingleCommand\", \"<b>全命令统计</b>\", \"次数\");\n                var commandsUrl = \"${request.contextPath}/admin/app/getMutiDatesCommandStats.json?appId=\" + appId + \"&commandName=\" + firstCommand + betweenParams;\n                $.ajax({\n                  type : \"get\",\n                  url : commandsUrl,\n                  async : true,\n                  success : function(data) {\n                    var dates = new Array();\n                    dates.push(startDate);\n                    dates.push(yesterDate);\n                    pushOptionSeries(options, data, dates, \"命令趋势图\");\n                    new Highcharts.Chart(options);\n                  }\n                });\n              });\n    }else{\n      $(document).ready(\n              function() {\n                var options = getOption(\"containerSingleCommand\", title, \"次数\");\n                var commandsUrl = \"${request.contextPath}/admin/app/getCommandStats?appId=\" + appId + \"&commandName=\" + firstCommand + chartParams;\n                $.ajax({\n                  type : \"get\",\n                  url : commandsUrl,\n                  async : true,\n                  success : function(data) {\n                    var nameLegend = firstCommand + \"命令趋势图\";\n                    var finalPoints = getSeriesPoints(data, nameLegend);\n                    options.series.push(finalPoints);\n                    new Highcharts.Chart(options);\n                  }\n                });\n              });\n    }\n  </script>\n\n\n  <div id=\"containerSingleCommand\"\n       style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appDaily.html",
    "content": "<div class=\"container-fluid\">\n  <div class=\"card\">\n    <div class=\"card-body\">\n      <form method=\"get\" action=\"${request.contextPath}/admin/app/index\" class=\"row d-flex justify-content-end\">\n        <label class=\"col-form-label col-auto\" style=\"font-weight:bold;text-align:left;\">\n          &nbsp;日期:&nbsp;&nbsp;\n        </label>\n        <input type=\"date\" class=\"col-auto\" size=\"21\" name=\"dailyDate\" id=\"dailyDate\" value=\"${dailyDate}\">\n        <input type=\"hidden\" name=\"appId\" value=\"${appDesc.appId}\">\n        <input type=\"hidden\" name=\"tabTag\" value=\"app_daily\">\n        <input type=\"submit\" class=\"btn btn-info col-auto ml-2\" value=\"查询\"/>\n      </form>\n    </div>\n  </div>\n  <div class=\"row align-items-center\">\n    <div class=\"col-12\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">\n            客户端相关\n          </h3>\n        </div>\n        <div class=\"card-body table-responsive\">\n          <table class=\"table table-striped table-hover\">\n            <tbody>\n            <#if appDailyData??>\n              <tr>\n                <td>客户端值分布(全天)</td>\n                <td>${appDailyData.valueSizeDistributeCountDescHtml!}</td>\n                <td>客户端异常个数(全天)</td>\n                <td>${appDailyData.clientExceptionCount!}</td>\n                <td>客户端连接数(每分钟)</td>\n                <td>\n                  最大值:${appDailyData.maxMinuteClientCount!} <br/>\n                  平均值:${appDailyData.avgMinuteClientCount!}\n                </td>\n              </tr>\n              <tr>\n                <td>命令调用统计(全天):</td>\n                <td>\n                  次数:${appDailyData.clientCmdCount!}<br/>\n                  平均耗时(ms):${appDailyData.clientAvgCmdCost!}\n                </td>\n                <td>连接异常统计(全天)：</td>\n                <td>\n                  次数:${appDailyData.clientConnExpCount!}<br/>\n                  平均耗时(ms):${appDailyData.clientAvgConnExpCost!}\n                </td>\n                <td>命令超时统计(每分钟)：</td>\n                <td>\n                  次数:${appDailyData.clientCmdExpCount!} <br/>\n                  平均耗时(ms):${appDailyData.clientAvgCmdExpCost!}\n                </td>\n              </tr>\n            <#else>\n            <tr>\n              <td>无客户端上报数据</td>\n            </tr>\n            </#if>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-12\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">\n            服务端相关\n          </h3>\n        </div>\n        <div class=\"card-body table-responsive\">\n          <table class=\"table table-striped table-hover\">\n            <tbody>\n            <#if appDailyData??>\n              <tr>\n                <td>慢查询个数(全天)</td>\n                <td>${appDailyData.slowLogCount!}</td>\n                <td>命令次数(每分钟)</td>\n                <td>\n                  最大值:${appDailyData.maxMinuteCommandCount!} <br/>\n                  平均值:${appDailyData.avgMinuteCommandCount!}\n                </td>\n                <td>命中率(每分钟)</td>\n                <td>\n                  最大值:${appDailyData.maxMinuteHitRatio!}% <br/>\n                  最小值:${appDailyData.minMinuteHitRatio!}% <br/>\n                  平均值:${appDailyData.avgHitRatio!}%\n                </td>\n              </tr>\n\n              <tr>\n                <td>空间使用量(全天)</td>\n                <td>\n                  平均使用量:${appDailyData.avgUsedMemory!} M<br/>\n                  最大使用量:${appDailyData.maxUsedMemory!} M\n                </td>\n                <td>过期键数(全天)</td>\n                <td>\n                  ${appDailyData.expiredKeysCount!}\n                </td>\n                <td>剔除键数(全天)</td>\n                <td>\n                  ${appDailyData.evictedKeysCount!}\n                </td>\n              </tr>\n              <tr>\n                <td>键个数(全天)</td>\n                <td>\n                  平均值:${appDailyData.avgObjectSize!}<br/>\n                  最大值:${appDailyData.maxObjectSize!}\n                </td>\n                <td>input流量(每分钟)</td>\n                <td>\n                  平均值:${appDailyData.avgMinuteNetInputByte!} M<br/>\n                  最大值:${appDailyData.maxMinuteNetInputByte!} M\n                </td>\n                <td>output流量(每分钟)</td>\n                <td>\n                  平均:${appDailyData.avgMinuteNetOutputByte!} M<br/>\n                  最大:${appDailyData.maxMinuteNetOutputByte!} M<br/>\n                </td>\n              </tr>\n            <#else>\n              <tr>\n                <td>无客户端上报数据</td>\n              </tr>\n            </#if>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appDetail.html",
    "content": "<script type=\"text/javascript\">\n  $(function () {\n    $('.selectpicker').selectpicker({\n      noneSelectedText: '选择用户',\n    });\n\n    var td_officer = \"\";\n    for (var i = 0; i < $(\"#officer_select\").find(\"option:selected\").length; i++) {\n      if (i == 0) {\n        td_officer = $(\"#officer_select\").find(\"option:selected\")[i].innerText;\n      } else {\n        td_officer = td_officer + ',' + $(\"#officer_select\").find(\"option:selected\")[i].innerText;\n      }\n    }\n    $('#td_officer').html(td_officer);\n  })\n</script>\n<div class=\"row\">\n  <div class=\"col-md-8\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">\n          应用详情&nbsp;&nbsp;&nbsp;\n          <#if hasAuth>\n            <button type=\"button\" class=\"btn btn-primary\" data-bs-target=\"#updateAppDetailModal\"\n                    data-bs-toggle=\"modal\">修改应用信息\n            </button>\n          </#if>\n        </h4>\n      </div>\n      <#assign officer_arr=(appDetail.appDesc.officer)?split(\",\")>\n      <input id=\"officer_input\" value=\"${appDetail.appDesc.officer!}\" hidden/>\n      <div class=\"card-body table-responsive\">\n        <!-- table begin -->\n        <table class=\"table table-striped table-sm\">\n          <tbody>\n            <tr>\n              <th style=\"white-space: nowrap\">应用id</th>\n              <td>${appDetail.appDesc.appId!}</td>\n              <th style=\"white-space: nowrap\">应用名称</th>\n              <td>${appDetail.appDesc.name!}</td>\n            </tr>\n            <tr>\n              <th style=\"white-space: nowrap\">申请人</th>\n              <#assign appUserId = -1>\n              <#if appDetail.appDesc.userId??>\n                <#assign appUserId = (appDetail.appDesc.userId)>\n              </#if>\n\n              <#if userMap?? && (appUserId != -1) && userMap?api.get(appUserId)??>\n                <td title=\"${userMap?api.get(appUserId).email!}\">${userMap?api.get(appUserId).chName!}</td>\n              <#else>\n                <td>${appUserId!}</td>\n              </#if>\n              <th style=\"white-space: nowrap\">应用类型</th>\n              <td>${appDetail.appDesc.typeDesc!}</td>\n            </tr>\n            <tr>\n              <th style=\"white-space: nowrap\">报警用户</th>\n              <td>\n                <#if appDetail.alertUsers??>\n                  <#list appDetail.alertUsers as appUser>\n                    <#if (appUser_index != 0)>,</#if>${appUser.chName!}(${appUser.name!})\n                  </#list>\n                </#if>\n              </td>\n              <th style=\"white-space: nowrap\">负责人</th>\n              <td id=\"td_officer\"></td>\n            </tr>\n            <tr>\n              <th style=\"white-space: nowrap\">存储空间</th>\n              <td>${(appDetail.mem / 1024 * 1.0)?string('#.##')}G</td>\n              <th style=\"white-space: nowrap\">分布机器数</th>\n              <td>${appDetail.machineNum!}</td>\n            </tr>\n            <tr>\n              <th style=\"white-space: nowrap\">主节点数</th>\n              <td>${appDetail.masterNum!}</td>\n              <th style=\"white-space: nowrap\">从节点数</th>\n              <td>${appDetail.slaveNum!}</td>\n            </tr>\n            <tr>\n              <th style=\"white-space: nowrap\">appKey</th>\n              <td>\n                <#if ((!(appDetail.appDesc.appKey??)) || (appDetail.appDesc.appKey == ''))>\n                  暂无\n                <#else>\n                  ${appDetail.appDesc.appKey!}\n                </#if>\n              </td>\n              <th style=\"white-space: nowrap\">redis密码</th>\n              <td>\n                <#if (appDetail.appDesc.customPassword??)>\n                  ${appDetail.appDesc.customPassword!}\n                <#elseif (appDetail.appDesc.pkey??)>\n                  ${password!}\n                <#else>\n                  无\n                </#if>\n\n                <#if acluser??>\n                  <br>\n                  <font color=\"red\" style=\"text-decoration: underline;\">以下为只读用户，仅供查询使用</font>\n                  <br>\n                  只读用户：${acluser!} <br>\n                  只读密码：${aclpassword!}\n                </#if>\n                <#if userInfo.type == 0>\n                  <#if adminUser??>\n                    <br>\n                    <font color=\"red\" style=\"text-decoration: underline;\">管理员用户/密码</font>\n                    <br>\n                    ${adminUser!}/${adminPassword!}\n                  </#if>\n                </#if>\n              </td>\n            </tr>\n            <tr>\n              <th style=\"white-space: nowrap\">应用描述</th>\n              <td>${appDetail.appDesc.intro!}</td>\n              <th style=\"white-space: nowrap\">淘汰策略</th>\n              <td>\n                <#if (appDetail.appDesc.maxmemoryPolicyDesc??)>\n                  ${appDetail.appDesc.maxmemoryPolicyDesc!}\n                </#if>\n              </td>\n            </tr>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n\n\n  <div class=\"col-md-4\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title ms-3\">\n          报警指标\n          <a target=\"_blank\" href=\"${request.contextPath}/admin/app/appAlterConfig?appId=${appDetail.appDesc.appId!}\" class=\"btn btn-danger\" role=\"button\">应用报警配置</a>\n        </h3>\n      </div>\n      <div class=\"card-body table-responsive\">\n        <!-- table begin -->\n        <table class=\"table table-striped table-hover\">\n          <thead>\n          <tr>\n            <th scope=\"col\">id</th>\n            <th scope=\"col\">报警key</td>\n            <th scope=\"col\">阀值</td>\n            <th scope=\"col\">周期</td>\n          </tr>\n          </thead>\n          <tbody>\n            <tr>\n              <td>1</td>\n              <td>空间使用率大于</td>\n              <td>${appDetail.appDesc.memAlertValue!}%</td>\n              <td>每20分钟</td>\n            </tr>\n            <tr>\n              <td>2</td>\n              <td>客户端连接数大于</td>\n              <td>${appDetail.appDesc.clientConnAlertValue!}</td>\n              <td>每20分钟</td>\n            </tr>\n            <tr>\n              <td>3</td>\n              <td>应用平均命中率小于</td>\n              <td>${appDetail.appDesc.hitPrecentAlertValue!}%</td>\n              <#if (appDetail.appDesc.hitPrecentAlertValue > 0)>\n                  <td>每20分钟</td>\n              <#else>\n                  <td>监控关闭</td>\n              </#if>\n            </tr>\n            <tr>\n              <td>4</td>\n              <td>全局报警项邮件通知</td>\n              <td></td>\n              <#if (appDetail.appDesc.isAccessMonitor == 0)>\n                <td>监控关闭</td>\n              <#else>\n                <td>监控开启</td>\n              </#if>\n            </tr>\n            </tbody>\n          </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n<#if (appDetail.appDesc.isAccessMonitor ==1)>\n  <div class=\"row\">\n    <div class=\"col-md-12\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h4 class=\"card-title\">\n            应用全局报警项:\n            <button type=\"button\" class=\"btn btn-danger\" data-bs-target=\"#updateWholeAlertConfigModal\"\n                    data-bs-toggle=\"modal\">报警项修改申请\n            </button>\n          </h4>\n        </div>\n        <div class=\"card-body\">\n          <div class=\"table-responsive\">\n            <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n              <thead>\n              <tr>\n                <th scope=\"col\">id</th>\n                <th scope=\"col\">配置名</th>\n                <th scope=\"col\">配置说明</th>\n                <th scope=\"col\">关系</th>\n                <th scope=\"col\">阀值</th>\n                <th scope=\"col\">周期</th>\n                <th scope=\"col\">最近检测时间</th>\n              </tr>\n              </thead>\n              <tbody>\n                <#list instanceAlertAllList as config>\n                  <tr>\n                    <td>\n                      ${config.id!}\n                    </td>\n                    <td>\n                      ${config.alertConfig!}\n                    </td>\n                    <td>\n                      ${config.configInfo!}\n                    </td>\n                    <td>\n                      <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                        <#if (config.compareType == instanceAlertCompareTypeEnum.value)>${instanceAlertCompareTypeEnum.info!}</#if>\n                      </#list>\n                    </td>\n                    <td>\n                      ${config.alertValue!}\n                    </td>\n                    <td>\n                      <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                        <#if (config.checkCycle == instanceAlertCheckCycleEnum.value)>${instanceAlertCheckCycleEnum.info!}</#if>\n                      </#list>\n                    </td>\n                    <td>\n                      ${config.lastCheckTime?string('yyyy-MM-dd HH:mm:ss')}\n                    </td>\n                  </tr>\n                </#list>\n              </tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n      <!-- END TABLE PORTLET-->\n    </div>\n  </div>\n</#if>\n\n<div class=\"row\">\n  <div class=\"col-md-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">\n          用户管理&nbsp;&nbsp;&nbsp;\n          <#if hasAuth>\n            <button type=\"button\" class=\"btn btn-success\" data-bs-target=\"#appAddUserModal\" data-bs-toggle=\"modal\">\n              添加用户\n            </button>\n          </#if>\n        </h4>\n      </div>\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-striped table-hover\">\n          <thead>\n            <th scope=\"col\">id</th>\n            <th scope=\"col\">域账户</td>\n            <th scope=\"col\">中文名</td>\n            <th scope=\"col\">邮箱</td>\n            <th scope=\"col\">手机</td>\n            <th scope=\"col\">微信</td>\n            <th scope=\"col\">是否报警</td>\n            <th scope=\"col\">业务组</td>\n            <#if hasAuth>\n              <th scope=\"col\">操作</td>\n            </#if>\n          </thead>\n          <tbody>\n          <#if appDetail.appUsers?? && (appDetail.appUsers?size gt 0)>\n            <#list appDetail.appUsers as appDetailUser>\n              <tr>\n                <td>${appDetailUser.id!}</td>\n                <td>${appDetailUser.name!}</td>\n                <td>${appDetailUser.chName!}</td>\n                <td>${appDetailUser.email!}</td>\n                <td>${appDetailUser.mobile!}</td>\n                <td>${appDetailUser.weChat!}</td>\n                <td>\n                  <#if (appDetailUser.isAlert==0)>否</#if>\n                  <#if (appDetailUser.isAlert==1)>是</#if>\n                </td>\n                <td>\n                  <#list bizList as bizInfo>\n                    <#if appDetailUser.bizId?? && (appDetailUser.bizId == bizInfo.id)>${bizInfo.name!}</#if>\n                  </#list>\n                </td>\n                <#if hasAuth>\n                  <td>\n                    <a href=\"javascript;\" data-bs-target=\"#addUserModal${appDetailUser.id!}\" data-bs-toggle=\"modal\">[修改]</a>\n                    <a href=\"javascript:void(0);\"\n                       onclick=\"deleteAppUser('${appDetailUser.id!}','${appDetail.appDesc.appId!}', '${request.contextPath}')\">[删除]</a>\n                  </td>\n                </#if>\n              </tr>\n            </#list>\n          </#if>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"appAddUserModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\" aria-labelledby=\"appAddUserModalLabel\" aria-hidden=\"true\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\" id=\"appAddUserModalLabel\">添加用户</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>\n      </div>\n\n      <div class=\"modal-body\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-lg-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">\n                    用户名:\n                  </label>\n                  <div class=\"col-lg-7\">\n                    <select id=\"addAppToUser\" class=\"selectpicker border rounded w-100\" multiple\n                            data-live-search=\"true\" style=\"height: 40px;width: 150px;border-radius: 5px;border: 1px solid #ced4da; text-align: center;margin-left:5px;\">\n                      <#list userMap?keys as key>\n                        <#assign toAddUser = userMap?api.get(key)>\n                        <option value=\"${toAddUser.id!}\">${toAddUser.chName!}(${toAddUser.name!})</option>\n                      </#list>\n                    </select>\n                  </div>\n                </div>\n              <!-- form-body 结束 -->\n            </div>\n            <div id=\"appAddUserInfo\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n      </div>\n\n      <div class=\"modal-footer\">\n        <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        <button type=\"button\" id=\"appAddUserBtn\" class=\"btn btn-danger\"\n                onclick=\"appAddUser('${appDetail.appDesc.appId!}', '${request.contextPath}')\">Ok\n        </button>\n      </div>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"appAlertConfigModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">应用报警修改</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-lg-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">空间报警阀值:</label>\n                  <div class=\"col-lg-7\">\n                    <input type=\"text\" name=\"memAlertValue\"\n                           value=\"${appDetail.appDesc.memAlertValue!}\" id=\"memAlertValue\"\n                           placeholder=\"空间报警阀值\" class=\"form-control\" onchange=\"testisNum(this.id)\">\n                    <span class=\"help-block\">例如:如果想空间使用率超过90%报警，填写90<br/><font color=\"red\">(如果不需要报警请填写100以上的数字)</font></span>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">客户端连接数报警阀值:</label>\n                  <div class=\"col-lg-7\">\n                    <input type=\"text\" name=\"clientConnAlertValue\"\n                           value=\"${appDetail.appDesc.clientConnAlertValue!}\"\n                           id=\"clientConnAlertValue\" placeholder=\"客户端连接数报警阀值\" class=\"form-control\"\n                           onchange=\"testisNum(this.id)\">\n                    <span class=\"help-block\">例如:如果想客户端连接数率超过2000报警，填写2000</span>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">应用平均命中率报警阀值:</label>\n                  <div class=\"col-lg-7\">\n                    <input type=\"text\" name=\"hitPrecentAlertValue\"\n                           value=\"${appDetail.appDesc.hitPrecentAlertValue!}\"\n                           id=\"hitPrecentAlertValue\" placeholder=\"平均命中率报警阀值\" class=\"form-control\"\n                           onchange=\"testisNum(this.id)\">\n                    <span class=\"help-block\">例如:如果应用平均命中率低于80%报警，填写80</span>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">开启应用全局报警:</label>\n                  <div class=\"col-lg-7\">\n                    <select id=\"isAccessMonitor\" name=\"isAccessMonitor\" class=\"form-control\">\n                      <option value=\"0\"\n                      <#if (appDetail.appDesc.isAccessMonitor?? || appDetail.appDesc.isAccessMonitor == 0)>selected</#if>>\n                      否\n                      </option>\n                      <option value=\"1\"\n                      <#if (appDetail.appDesc.isAccessMonitor == 1)>selected</#if>>\n                      是\n                      </option>\n                    </select>\n                    <span class=\"help-block\">是:接收到全局报警指标报警邮件 否:反之则不接收</span>\n                  </div>\n                </div>\n\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <div id=\"appConfigChangeInfo\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"appConfigChangeBtn\" class=\"btn btn-danger\"\n                  onclick=\"appAlertConfigChange('${appDetail.appDesc.appId!}', '${request.contextPath}')\">Ok\n          </button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>\n\n<div id=\"updateWholeAlertConfigModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">全局报警项配置修改</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-lg-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">配置项:</label>\n                  <div class=\"col-lg-8\">\n                    <input type=\"text\" name=\"appConfigKey\" id=\"appMonitorConfigKey\" value=\"\"\n                           placeholder=\"例如:aof_current_size\" class=\"form-control\"/>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">配置值:</label>\n                  <div class=\"col-lg-8\">\n                    <input type=\"text\" name=\"appConfigValue\" id=\"appMonitorConfigValue\" value=\"\"\n                           placeholder=\"例如:10000\" class=\"form-control\">\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">修改原因:</label>\n                  <div class=\"col-lg-8\">\n                                        <textarea name=\"appConfigReason\" id=\"appMonitorConfigReason\" value=\"\"\n                                                  placeholder=\"例如：修改原因:aof实例内存较大。\" class=\"form-control\"></textarea>\n                  </div>\n                </div>\n\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <div id=\"updateConfigChangeInfo\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"updateConfigChangeBtn\" class=\"btn btn-danger\"\n                  onclick=\"updateWholeAlertConfigChange('${appDetail.appDesc.appId!}', '${request.contextPath}')\">Ok\n          </button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n\n<div id=\"updateAppDetailModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">应用信息修改</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n        <div class=\"modal-body\">\n          <div class=\"container-fluid\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"col-lg-12\">\n                <!-- form-body开始 -->\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">应用名:</label>\n                  <div class=\"col-lg-7\">\n                    <input type=\"text\" name=\"appDescName\" value=\"${appDetail.appDesc.name!}\"\n                           id=\"appDescName\" class=\"form-control\">\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">应用描述:</label>\n                  <div class=\"col-lg-7\">\n                                        <textarea class=\"form-control\" name=\"appDescIntro\" rows=\"3\" id=\"appDescIntro\"\n                                                  placeholder=\"应用描述\">${appDetail.appDesc.intro!}</textarea>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-lg-3\">项目负责人:</label>\n                  <input type=\"text\" name=\"officer\" value=\"${appDetail.appDesc.officer!}\" id=\"officer\" hidden/>\n                  <div class=\"col-lg-7\">\n                    <select id=\"officer_select\"\n                            class=\"selectpicker border rounded w-100\" multiple\n                            data-live-search=\"true\" >\n                      <#assign officerList = []>\n                      <#if appDetail.appDesc.officer??>\n                        <#assign officerList = appDetail.appDesc.officer?split(',')>\n                      </#if>\n                      <#list userMap?keys as key>\n                        <#assign updateAppUser = userMap?api.get(key)>\n                        <option value=\"${updateAppUser.id!}\" <#if officerList?seq_contains(\"${updateAppUser.id!}\")>selected=\"selected\"</#if>>${updateAppUser.chName!}(${updateAppUser.name!})</option>\n                      </#list>\n                    </select>\n                  </div>\n                </div>\n              </div>\n              <div id=\"updateAppDetailInfo\"></div>\n              <!-- 控件结束 -->\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"updateAppDetailBtn\" class=\"btn btn-danger\"\n                  onclick=\"updateAppDetailChange('${appDetail.appDesc.appId!}', '${request.contextPath}')\">Ok\n          </button>\n        </div>\n\n    </div>\n  </div>\n</div>\n\n\n<#if appDetail.appUsers?? && (appDetail.appUsers?size gt 0)>\n  <#list appDetail.appUsers as appDetailUser>\n    <div id=\"addUserModal${appDetailUser.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n      <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n          <div class=\"modal-header\">\n            <h4 class=\"modal-title\">管理用户</h4>\n            <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n          </div>\n\n          <div class=\"modal-body\">\n            <div class=\"container-fluid\">\n              <div class=\"row\">\n                <!-- 控件开始 -->\n                <div class=\"col-lg-12\">\n                  <!-- form-body开始 -->\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-lg-3\">\n                      域账户名:\n                    </label>\n                    <div class=\"col-lg-8\">\n                      <input type=\"text\" name=\"name\" id=\"name${appDetailUser.id!}\"\n                             value=\"${appDetailUser.name!}\" placeholder=\"域账户名(邮箱前缀)\"\n                             class=\"form-control\"/>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-lg-3\">\n                      中文名:\n                    </label>\n                    <div class=\"col-lg-8\">\n                      <input type=\"text\" name=\"chName\" id=\"chName${appDetailUser.id!}\"\n                             value=\"${appDetailUser.chName!}\" placeholder=\"中文名\"\n                             class=\"form-control\"/>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-lg-3\">\n                      邮箱:\n                    </label>\n                    <div class=\"col-lg-8\">\n                      <input type=\"text\" name=\"email\" id=\"email${appDetailUser.id!}\"\n                             value=\"${appDetailUser.email!}\" placeholder=\"邮箱\"\n                             class=\"form-control\"/>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-lg-3\">\n                      手机:\n                    </label>\n                    <div class=\"col-lg-8\">\n                      <input type=\"text\" name=\"mobile\" id=\"mobile${appDetailUser.id!}\"\n                             value=\"${appDetailUser.mobile!}\" placeholder=\"手机\"\n                             class=\"form-control\"/>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-lg-3\">\n                      微信:\n                    </label>\n                    <div class=\"col-lg-8\">\n                      <input type=\"text\" name=\"weChat\" id=\"weChat${appDetailUser.id!}\"\n                             value=\"${appDetailUser.weChat!}\" placeholder=\"微信\"\n                             class=\"form-control\"/>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-lg-3\">\n                      是否收报警:\n                    </label>\n                    <div class=\"col-lg-8\">\n                      <select name=\"isAlert\" id=\"isAlert${appDetailUser.id!}\"\n                              class=\"form-select\">\n                        <option value=\"1\" <#if (appDetailUser.isAlert == 1)>selected</#if>>\n                        是\n                        </option>\n                        <option value=\"0\" <#if (appDetailUser.isAlert == 0)>selected</#if>>\n                        否\n                        </option>\n                      </select>\n                    </div>\n                  </div>\n\n                  <#if openswitch??>\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-lg-3\">\n                        公司名称:\n                      </label>\n                      <div class=\"col-lg-8\">\n                        <input type=\"text\" name=\"company\" id=\"company${appDetailUser.id!}\"\n                               value=\"${appDetailUser.company!}\" placeholder=\"公司名称\"\n                               class=\"form-control\"/>\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-lg-3\">\n                        使用目的:\n                      </label>\n                      <div class=\"col-lg-8\">\n                        <input type=\"text\" name=\"purpose\" id=\"purpose${appDetailUser.id!}\"\n                               value=\"${appDetailUser.purpose!}\" placeholder=\"使用目的\"\n                               class=\"form-control\"/>\n                      </div>\n                    </div>\n                  </#if>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-lg-3\">\n                      所属业务组:\n                    </label>\n                    <div class=\"col-lg-8\">\n                      <select id=\"bizId${appDetailUser.id!}\" name=\"bizId\" class=\"form-select\">\n                        <option value=\"\">业务组</option>\n                        <#list bizList as biz>\n                          <option value=\"${biz.id!}\" <#if appDetailUser.bizId?? && (biz.id == appDetailUser.bizId)>selected</#if>>${biz.name!}</option>\n                        </#list>\n                      </select>\n                    </div>\n                  </div>\n\n                  <input type=\"hidden\" id=\"type${appDetailUser.id!}\" value=\"${appDetailUser.type!}\">\n                  <input type=\"hidden\" id=\"userId${appDetailUser.id!}\" name=\"userId\" value=\"${appDetailUser.id!}\"/>\n                </div>\n                <div id=\"info${appDetailUser.id!}\"></div>\n                <!-- 控件结束 -->\n              </div>\n            </div>\n          </div>\n\n          <div class=\"modal-footer\">\n            <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n            <button type=\"button\" class=\"btn btn-danger\"\n                    onclick=\"saveOrUpdateUser('${appDetailUser.id!}','${appDetail.appDesc.appId!}', '${request.contextPath}')\">Ok\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n  </#list>\n</#if>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appHitRatio.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>CacheCloud命中率</title>\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n    <#include '/inc/frontResources.html'>\n  </head>\n  <body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n    <div class=\"wrapper\">\n      <#include '/inc/head.html'>\n      <div class=\"content-wrapper ml-0\">\n        <div class=\"content\">\n          <div class=\"container-fluid\">\n            <div class=\"row\">\n              <div class=\"col-12\">\n                <div class=\"card\">\n                  <div class=\"card-body\">\n                    <div class=\"tab-content\">\n                      <div class=\"tab-pane active\" id=\"app_client_command_statistics\">\n                        <div class=\"col-12\" id=\"mainClientCommandContainer\">\n                          <br/>\n\n                          <form id=\"appHitRatiosForm\" class=\"row\">\n                            <div class=\"row justify-content-end\">\n                              <label class=\"col-form-label col-auto\" style=\"font-weight:bold;text-align:left;\">\n                                &nbsp;查询日期:&nbsp;&nbsp;\n                              </label>\n                              <div class=\"col-auto\">\n                                <input type=\"date\" class=\"form-control\" size=\"20\" name=\"endDate\" id=\"endDate\" value=\"${searchDate}\"/>\n                              </div>\n                              <input type=\"hidden\" name=\"appId\" id=\"appId\" value=\"${appId}\">\n                              <input type=\"hidden\" name=\"statName\" value=\"avg_hit_ratio\">\n                              <div class=\"col-auto\">\n                                <label><button type=\"button\" class=\"btn btn-info\" onclick=\"loadAppHitRatio()\"/>查询</button></label>\n                              </div>\n                            </div>\n                          </form>\n\n\n                          <script type=\"text/javascript\">\n                            var initialEndDate = '${searchDate}';\n                            var initialAppId = '${appId}';\n                            $(document).ready(\n                              function () {\n                                loadAppHitRatio(initialAppId, initialEndDate);\n                              }\n                            );\n\n                            function loadAppHitRatio(appId, endDate){\n                              if(appId == null || appId == undefined){\n                                appId = $(\"#appId\").val()\n                              }\n                              if(endDate == null || endDate == undefined){\n                                endDate = $(\"#endDate\").val()\n                              }\n\n                              var options = getOption(\"containerHitRatio\", \"<b>命中率统计</b>\", \"%\");\n                              var commandsUrl = \"${request.contextPath}/admin/app/getMutiDatesAppHitRatioStats.json?appId=\" + appId + \"&statName=avg_hit_ratio&endDate=\" + endDate;\n                              $.ajax({\n                                type: \"get\",\n                                url: commandsUrl,\n                                async: true,\n                                success: function (data) {\n                                  var dataArr = eval(data.data);\n                                  var dataSeries = [];\n                                  var count;\n                                  for (var i = 0; i < dataArr.length; i++) {\n                                      var dataInfo = dataArr[i];\n                                      count = dataInfo.y;\n                                      var pointName = dataInfo.date + \":  \" + count + \"%\";\n                                      var dataPoint = {\n                                          name: pointName,\n                                          x: dataInfo.x,\n                                          y: count,\n                                      };\n                                      dataSeries.push(dataPoint);\n                                  }\n\n                                  var seriesPoints = {\n                                      name: \"命中趋势图\",\n                                      data: dataSeries,\n                                      marker: {\n                                          radius: 1,  //曲线点半径，默认是4\n                                      }\n                                  };\n                                  options.series.push(seriesPoints);\n                                  new Highcharts.Chart(options);\n                                }\n                              });\n                            }\n                          </script>\n\n                          <!-- 命中率相关 -->\n                          <div id=\"containerHitRatio\"\n                               style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n                          <br/>\n                        </div>\n                        <div>\n\n                        </div>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      <#include \"/inc/footer.html\">\n    </div>\n  </body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appInstanceCpuStat.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n    <#include '/inc/frontResources.html'>\n    <script type=\"text/javascript\">\n      var startDate = '${startDate!}';\n      var endDate = '${endDate!}';\n      var appId = '${appId!}';\n      var chartParams = \"&startDate=\"+startDate+\"&endDate=\"+endDate;\n      Highcharts.setOptions({\n        global : {\n          useUTC : false\n        }\n      });\n      Highcharts.setOptions({\n        colors : ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n          '#492970', '#804000', '#f28f43', '#77a1e5',\n          '#c42525', '#a6c96a']\n      });\n\n      function search() {\n        var startDate = document.getElementById(\"startDate\").value;\n        document.getElementById(\"appInstanceCpuStatForm\").submit();\n      }\n    </script>\n  </head>\n\n  <body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n    <div class=\"wrapper\">\n      <!-- Header End -->\n      <#include \"/inc/head.html\">\n      <div class=\"content-wrapper ml-0\">\n        <div class=\"content\">\n          <div class=\"container-fluid\">\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                  应用(<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId!}\">${appDesc.name!}</a>)实例CPU消耗统计\n                </h3>\n              </div>\n              <div class=\"card-body\">\n                <form method=\"get\" action=\"${request.contextPath}/admin/app/appInstanceCpuStat\" id=\"appInstanceCpuStatForm\" class=\"d-flex justify-content-end\">\n                    <div style=\"float:right\">\n                      <label style=\"font-weight:bold;text-align:left;\">\n                        日期:\n                      </label>\n                      <input type=\"date\" size=\"21\" name=\"startDate\" id=\"startDate\" value=\"${startDate!}\"/>\n                      <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n                      <label><input type=\"button\" class=\"btn btn-primary\" value=\"查询\" onclick=\"search()\"/></label>\n                    </div>\n                </form>\n                <script type=\"text/javascript\">\n\n                  function genDetailContainer(i) {\n                    var mainContainer = document.getElementById(\"allInstanceContainers\");\n                    var divNode = document.createElement(\"div\");\n                    divNode.setAttribute('id', \"appInstanceCpuContainer\" + i);\n                    divNode.setAttribute(\"style\",\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto;\");\n                    mainContainer.appendChild(divNode);\n                  }\n\n                  function fillDetailTable(instanceInfo, index) {\n                    var tb =document.getElementById('instanceCpuDetailTable');\n                    var newTr = tb.insertRow(-1);\n                    newTr.align='center';\n                    var indexTd = newTr.insertCell();\n                    var instanceTd = newTr.insertCell();\n                    indexTd.innerHTML = (index+1);\n                    instanceTd.innerHTML = instanceInfo;\n                  }\n\n                  $(document).ready(\n                    function() {\n                      var url = \"${request.contextPath}/admin/app/getAppInstancesCpuStat.json?appId=\" + appId + chartParams;\n                      $.ajax({\n                        type : \"get\",\n                        url : url,\n                        async : true,\n                        success : function(data) {\n                          var dataArr = eval(\"(\" + data + \")\");\n                          var length = dataArr.length;\n                          for (var i = 0; i < length; i++) {\n                            var instance = dataArr[i];\n                            var instanceCpuStatMapList = instance.instanceCpuStatMapList;\n                            if(instanceCpuStatMapList.length == 0) {\n                              continue;\n                            }\n                            genDetailContainer(i);\n                            var title = \"<b>实例(\"+instance.instanceInfo+\")CPU消耗流量</b>\";\n                            var options = getOption(\"appInstanceCpuContainer\" + i, title,\"次数\");\n\n                            var csPoints = getInstanceCpuPoints(instance, \"cpu_sys\", \"cs\");\n                            options.yAxis.title.text = csPoints.unitTxt;\n                            var cuPoints = getInstanceCpuPoints(instance, \"cpu_user\", \"cu\");\n                            var csChildPoints = getInstanceCpuPoints(instance, \"cpu_sys_children\", \"cs_child\");\n                            var cuChildPoints = getInstanceCpuPoints(instance, \"cpu_user_children\", \"cu_child\");\n                            var cpuBasePoints = getInstanceBaseCpuPoints(instance, \"cpu_warning\", 0.3);\n\n                            options.series.push(csPoints);\n                            options.series.push(cuPoints);\n                            options.series.push(csChildPoints);\n                            options.series.push(cuChildPoints);\n                            options.series.push(cpuBasePoints);\n\n                            new Highcharts.Chart(options);\n                          }\n                        }\n                      });\n                    });\n                </script>\n\n                <div id=\"allInstanceContainers\">\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appInstanceExpiredEvictedKeysStat.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n  <script type=\"text/javascript\">\n    var startDate = '${startDate!}';\n    var endDate = '${endDate!}';\n    var appId = '${appId!}';\n    var chartParams = \"&startDate=\"+startDate+\"&endDate=\"+endDate;\n    Highcharts.setOptions({\n      global : {\n        useUTC : false\n      }\n    });\n    Highcharts.setOptions({\n      colors : ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n        '#492970', '#804000', '#f28f43', '#77a1e5',\n        '#c42525', '#a6c96a']\n    });\n\n    function search() {\n      var startDate = document.getElementById(\"startDate\").value;\n      document.getElementById(\"appInstanceExpiredEvictedKeysStatForm\").submit();\n    }\n  </script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <#include \"/inc/head.html\">\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"card\">\n          <div class=\"card-header\">\n            <h4 class=\"card-title\">\n              应用(<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId!}\">${appDesc.name!}</a>)实例过期/淘汰键统计\n            </h4>\n          </div>\n          <div class=\"card-body\">\n            <form method=\"get\" action=\"${request.contextPath}/admin/app/appInstanceExpiredEvictedKeysStat\" id=\"appInstanceExpiredEvictedKeysStatForm\" class=\"d-flex justify-content-end\">\n              <div style=\"float:right\">\n                <label style=\"font-weight:bold;text-align:left;\">\n                  日期:\n                </label>\n                <input type=\"date\" size=\"21\" name=\"startDate\" id=\"startDate\" value=\"${startDate!}\"/>\n                <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n                <label><input type=\"button\" class=\"btn btn-primary\" value=\"查询\" onclick=\"search()\"/></label>\n              </div>\n            </form>\n            <script type=\"text/javascript\">\n              function genDetailContainer(i) {\n                var mainContainer = document.getElementById(\"allInstanceContainers\");\n                var divNode = document.createElement(\"div\");\n                divNode.setAttribute('id', \"appInstanceExpiredEvictedKeysContainer\" + i);\n                divNode.setAttribute(\"style\",\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto;\");\n                mainContainer.appendChild(divNode);\n              }\n\n              function fillDetailTable(instanceInfo, index) {\n                var tb =document.getElementById('instanceExpiredEvictedKeysDetailTable');\n                var newTr = tb.insertRow(-1);\n                newTr.align='center';\n                var indexTd = newTr.insertCell();\n                var instanceTd = newTr.insertCell();\n                indexTd.innerHTML = (index+1);\n                instanceTd.innerHTML = instanceInfo;\n              }\n\n              $(document).ready(\n                function() {\n                  var url = \"${request.contextPath}/admin/app/getAppInstancesExpiredEvictedKeysStat.json?appId=\" + appId + chartParams;\n                  $.ajax({\n                    type : \"get\",\n                    url : url,\n                    async : false,\n                    success : function(data) {\n                      console.log(data);\n                      var dataArr = eval(\"(\" + data + \")\");\n                      var length = dataArr.length;\n                      for (var i = 0; i < length; i++) {\n                        var instance = dataArr[i];\n                        if(instance.length == 0) {\n                          continue;\n                        }\n                        genDetailContainer(i);\n\n                        var title = \"<b>实例(\"+instance.instanceInfo+\")过期/淘汰键统计</b>\";\n                        var options = getOption(\"appInstanceExpiredEvictedKeysContainer\" + i, title,\"\");\n\n                        var expiredKeysPoints = getInstanceKeyPoints(instance, \"expired_keys\", \"exkey\");\n                        options.yAxis.title.text = expiredKeysPoints.unitTxt;\n                        var evictedKeysPoints = getInstanceKeyPoints(instance, \"evicted_keys\", \"evkey\");\n\n                        options.series.push(expiredKeysPoints);\n                        options.series.push(evictedKeysPoints);\n\n                        new Highcharts.Chart(options);\n                      }\n                    }\n                  });\n                });\n            </script>\n            <div id=\"allInstanceContainers\">\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appInstanceMemFragRatioStat.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n  <script type=\"text/javascript\">\n    var startDate = '${startDate!}';\n    var endDate = '${endDate!}';\n    var appId = '${appId!}';\n    var chartParams = \"&startDate=\"+startDate+\"&endDate=\"+endDate;\n    Highcharts.setOptions({\n      global : {\n        useUTC : false\n      }\n    });\n    Highcharts.setOptions({\n      colors : ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n        '#492970', '#804000', '#f28f43', '#77a1e5',\n        '#c42525', '#a6c96a']\n    });\n\n    function search() {\n      var startDate = document.getElementById(\"startDate\").value;\n      document.getElementById(\"appInstanceMemFragRatioStatForm\").submit();\n    }\n  </script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <!-- Header End -->\n    <#include \"/inc/head.html\">\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"card\">\n          <div class=\"card-header\">\n            <h3 class=\"card-title\">\n              应用(<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId!}\">${appDesc.name!}</a>)实例内存碎片率统计\n            </h3>\n          </div>\n          <div class=\"card-body\">\n            <form method=\"get\" action=\"${request.contextPath}/admin/app/appInstanceMemFragRatioStat\" id=\"appInstanceMemFragRatioStatForm\" class=\"d-flex justify-content-end\">\n              <div style=\"float:right\">\n                <label style=\"font-weight:bold;text-align:left;\">\n                  日期:\n                </label>\n                <input type=\"date\" size=\"21\" name=\"startDate\" id=\"startDate\" value=\"${startDate!}\"/>\n                <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n                <label><input type=\"button\" class=\"btn btn-primary\" value=\"查询\" onclick=\"search()\"/></label>\n              </div>\n            </form>\n            <script type=\"text/javascript\">\n\n\n              function genDetailContainer(i) {\n                var mainContainer = document.getElementById(\"allInstanceContainers\");\n                var divNode = document.createElement(\"div\");\n                divNode.setAttribute('id', \"appInstanceMemFragRatioContainer\" + i);\n                divNode.setAttribute(\"style\",\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto;\");\n                mainContainer.appendChild(divNode);\n              }\n\n              function fillDetailTable(instanceInfo, index) {\n                var tb =document.getElementById('instanceMemFragRatioDetailTable');\n                var newTr = tb.insertRow(-1);\n                newTr.align='center';\n                var indexTd = newTr.insertCell();\n                var instanceTd = newTr.insertCell();\n                indexTd.innerHTML = (index+1);\n                instanceTd.innerHTML = instanceInfo;\n              }\n\n              $(document).ready(\n                function() {\n                  var url = \"${request.contextPath}/admin/app/getAppInstancesMemFragRatioStat.json?appId=\" + appId + chartParams;\n                  $.ajax({\n                    type : \"get\",\n                    url : url,\n                    async : false,\n                    success : function(data) {\n                      var dataArr = eval(\"(\" + data + \")\");\n                      var length = dataArr.length;\n                      for (var i = 0; i < length; i++) {\n                        var instance = dataArr[i];\n                        var instanceMemFragRatioStatMapList = instance.instanceMemFragRatioStatMapList;\n                        if(instanceMemFragRatioStatMapList.length == 0) {\n                          continue;\n                        }\n                        genDetailContainer(i);\n                        var title = \"<b>实例(\"+instance.instanceInfo+\")内存碎片率</b>\";\n                        var options = getOption(\"appInstanceMemFragRatioContainer\" + i, title,\"\");\n\n                        var ratioPoints = getInstanceMemFragRatioPoints(instance, \"mem_fragmentation_ratio\", \"ratio\");\n                        options.yAxis.title.text = ratioPoints.unitTxt;\n\n                        options.series.push(ratioPoints);\n\n                        new Highcharts.Chart(options);\n                      }\n                    }\n                  });\n                });\n            </script>\n\n            <div id=\"allInstanceContainers\">\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appInstanceNetStat.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n  <script type=\"text/javascript\">\n    var startDate = '${startDate!}';\n    var endDate = '${endDate!}';\n    var appId = '${appId!}';\n    var chartParams = \"&startDate=\"+startDate+\"&endDate=\"+endDate;\n    Highcharts.setOptions({\n      global : {\n        useUTC : false\n      }\n    });\n    Highcharts.setOptions({\n      colors : ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n        '#492970', '#804000', '#f28f43', '#77a1e5',\n        '#c42525', '#a6c96a']\n    });\n\n    function search() {\n      var startDate = document.getElementById(\"startDate\").value;\n      document.getElementById(\"appInstanceNetStatForm\").submit();\n    }\n  </script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <!-- Header End -->\n    <#include \"/inc/head.html\">\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"card\">\n          <div class=\"card-header\">\n            <h4 class=\"card-title\">\n              应用(<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId!}\">${appDesc.name!}</a>)实例流量统计\n            </h4>\n          </div>\n          <div class=\"card-body\">\n            <form method=\"get\" action=\"${request.contextPath}/admin/app/appInstanceNetStat\" id=\"appInstanceNetStatForm\" class=\"d-flex justify-content-end\">\n              <div style=\"float:right\">\n                <label style=\"font-weight:bold;text-align:left;\">\n                  日期:&nbsp;&nbsp;\n                </label>\n                <input type=\"date\" size=\"21\" name=\"startDate\" id=\"startDate\" value=\"${startDate!}\"/>\n                <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n                <label>&nbsp;<input type=\"button\" class=\"btn btn-primary\" value=\"查询\" onclick=\"search()\"/></label>\n              </div>\n            </form>\n          </div>\n          <script type=\"text/javascript\">\n            function genDetailContainer(i) {\n              var mainContainer = document.getElementById(\"allInstanceContainers\");\n              var divNode = document.createElement(\"div\");\n              divNode.setAttribute('id', \"appInstanceNetContainer\" + i);\n              divNode.setAttribute(\"style\",\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto;\");\n              mainContainer.appendChild(divNode);\n            }\n\n            function fillDetailTable(instanceInfo, index) {\n              var tb =document.getElementById('instanceNetDetailTable');\n              var newTr = tb.insertRow(-1);\n              newTr.align='center';\n              var indexTd = newTr.insertCell();\n              var instanceTd = newTr.insertCell();\n              indexTd.innerHTML = (index+1);\n              instanceTd.innerHTML = instanceInfo;\n            }\n\n            $(document).ready(\n              function() {\n                var url = \"${request.contextPath}/admin/app/getAppInstancesNetStat.json?appId=\" + appId + chartParams;\n                $.ajax({\n                  type : \"get\",\n                  url : url,\n                  async : true,\n                  success : function(data) {\n                    var dataArr = eval(\"(\" + data + \")\");\n                    var length = dataArr.length;\n                    for (var i = 0; i < length; i++) {\n                      var instance = dataArr[i];\n                      var instanceNetStatMapList = instance.instanceNetStatMapList;\n                      if(instanceNetStatMapList.length == 0) {\n                        continue;\n                      }\n                      genDetailContainer(i);\n                      var title = \"<b>实例(\"+instance.instanceInfo+\")网络流量</b>\";\n                      var options = getOption(\"appInstanceNetContainer\" + i, title,\"次数\");\n                      var inPoints = getInstanceNetPoints(instance, \"net_input\", \"i\");\n\n                      //统一流量单位\n                      options.yAxis.title.text = inPoints.unitTxt;\n                      var unit = inPoints.unit;\n\n                      var outPoints = getInstanceNetPoints(instance, \"net_output\", \"o\", unit);\n                      options.series.push(inPoints);\n                      options.series.push(outPoints);\n                      new Highcharts.Chart(options);\n                    }\n                  }\n                });\n              });\n          </script>\n\n          <div id=\"allInstanceContainers\">\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appLatencyInfoDetail.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <title>应用延迟详情</title>\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <!-- Header End -->\n    <#include \"/inc/head.html\">\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"card\">\n          <div class=\"card-header\">\n            <h3 class=\"card-title\">\n              应用：<label class=\"label label-success\">${appId!}</label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n              查询时间：<label class=\"label label-success\">${searchTime!}</label>\n            </h3>\n          </div>\n          <div class=\"card-body table-responsive\">\n            <table class=\"table table-striped table-hover\">\n              <thead>\n              <tr>\n                <th scope=\"col\">序号</th>\n                <th scope=\"col\">实例信息</th>\n                <th scope=\"col\">延迟个数</th>\n              </tr>\n              </thead>\n              <tbody>\n              <#list sumInstanceLatencyStatMap?keys as key>\n                <tr>\n                  <td>${key_index + 1}</td>\n                  <td><a href=\"#${key!}\">${key!}</a></td>\n                  <td>${sumInstanceLatencyStatMap[key]}</td>\n                </tr>\n              </#list>\n              </tbody>\n            </table>\n          </div>\n        </div>\n\n        <div class=\"card\">\n          <div class=\"card-body\">\n            <#list latencyInfoDetailMap?keys as key>\n              <div style=\"margin-top: 20px\">\n                <div class=\"page-header\" id=\"${key!}\">\n                  <#assign instanceId = (latencyInfoDetailMap[key])[0].instance_id>\n                  <h5>\n                    <li><a href=\"${request.contextPath}/admin/instance/index?instanceId=${instanceId!}\"\n                           target=\"_blank\">${key!}</a>\n                    </li>\n                  </h5>\n                </div>\n                <div class=\"table-responsive\">\n                  <table class=\"table table-bordered table-striped table-hover\">\n                    <thead>\n                      <tr>\n                        <th scope=\"col\">序号</th>\n                        <th scope=\"col\">事件名称</th>\n                        <th scope=\"col\">延迟时间点</th>\n                        <th scope=\"col\">延迟耗时(单位：毫秒)</th>\n                        <th scope=\"col\">关联慢查询</th>\n                      </tr>\n                    </thead>\n                    <tbody>\n                      <#list latencyInfoDetailMap[key] as latencyInfoDetail>\n                        <tr>\n                          <td>${latencyInfoDetail_index + 1}</td>\n                          <td>${latencyInfoDetail.event!}</td>\n                          <td>${latencyInfoDetail.execute_date!}</td>\n                          <td>${latencyInfoDetail.execution_cost!}</td>\n                          <td>\n                            <button type=\"button\" class=\"btn btn-success\" data-bs-target=\"#slowlog-modal\"\n                                    data-bs-toggle=\"modal\"\n                                    data-instanceid=\"${latencyInfoDetail.instance_id!}\"\n                                    data-executedate=\"${latencyInfoDetail.execute_date!}\">\n                              查看关联慢查询\n                            </button>\n                          </td>\n                        </tr>\n                      </#list>\n                    </tbody>\n                  </table>\n                </div>\n              </div>\n            </#list>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div id=\"slowlog-modal\" class=\"modal fade\" tabindex=\"-1\">\n    <div class=\"modal-dialog\" style=\"width: 100%\">\n      <div class=\"modal-content\">\n\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">关联慢查询</h4>\n          <button type=\"button\" class=\"close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <form class=\"form-horizontal form-bordered form-row-stripped\">\n          <div class=\"modal-body\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"table-responsive\">\n                <table class=\"table table-bordered table-striped table-hover\">\n                  <thead>\n                  <tr>\n                    <td>实例id</td>\n                    <td>redis实例</td>\n                    <td>慢查询id</td>\n                    <td>耗时(单位:微秒)</td>\n                    <td>命令</td>\n                    <td>发生时间</td>\n                  </tr>\n                  </thead>\n                  <tbody id=\"slowlogTable\"></tbody>\n                </table>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"modal-footer\">\n            <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn\">Close</button>\n          </div>\n        </form>\n      </div>\n    </div>\n  </div>\n\n</body>\n</html>\n\n<script type=\"text/javascript\">\n  $('#slowlog-modal').on('shown.bs.modal', function (e) {\n    var instanceId = $(e.relatedTarget).data('instanceid');\n    var executeDate = $(e.relatedTarget).data('executedate');\n\n    console.log(instanceId + ' ' + executeDate);\n    $('#slowlogTable').html('');\n    $.get(\n            '${request.contextPath}/admin/app/latencyRelatedSlowLog.json',\n            {\n              instanceId: instanceId,\n              executeDate: executeDate\n            },\n            function (data) {\n              var instanceSlowLogList = data.instanceSlowLogList;\n\n              instanceSlowLogList.forEach(function (slowlog, index) {\n                var instanceId = slowlog.instanceId;\n                var instance = slowlog.ip + \":\" + slowlog.port;\n                var slowLogId = slowlog.slowLogId;\n                var command = slowlog.command;\n                var costTime = slowlog.costTime;\n                var executeTime = slowlog.executeTime;\n\n                let date = new Date(executeTime)\n                var formatExecuteTime = dateFormat(\"YYYY-mm-dd HH:MM:SS\", date)\n\n                $('#slowlogTable').append(\n                        '<tr>' +\n                        '<td>' + instanceId + '</td>' +\n                        '<td>' + instance + '</td>' +\n                        '<td>' + slowLogId + '</td>' +\n                        '<td>' + costTime + '</td>' +\n                        '<td>' + command + '</td>' +\n                        '<td>' + formatExecuteTime + '</td>' +\n                        '</tr>'\n                );\n              })\n            }\n    );\n\n  });\n\n\n  function dateFormat(fmt, date) {\n    let ret;\n    const opt = {\n      \"Y+\": date.getFullYear().toString(),        // 年\n      \"m+\": (date.getMonth() + 1).toString(),     // 月\n      \"d+\": date.getDate().toString(),            // 日\n      \"H+\": date.getHours().toString(),           // 时\n      \"M+\": date.getMinutes().toString(),         // 分\n      \"S+\": date.getSeconds().toString()          // 秒\n    };\n    for (let k in opt) {\n      ret = new RegExp(\"(\" + k + \")\").exec(fmt);\n      if (ret) {\n        fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, \"0\")))\n      }\n      ;\n    }\n    ;\n    return fmt;\n  }\n\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appList.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>CacheCloud应用列表</title>\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n    <#include '/inc/frontResources.html'>\n  </head>\n\n  <body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n    <div class=\"wrapper\">\n      <!-- Header End -->\n      <#include \"/inc/head.html\">\n      <div class=\"content-wrapper ml-0\">\n        <div class=\"content\">\n          <div class=\"\">\n            <!--<div class=\"card\">\n              <div class=\"card-body\">-->\n                <!-- stat info start -->\n                <div class=\"container-fluid\">\n                  <div class=\"row\">\n                    <div class=\"col-lg-4\">\n                      <div class=\"card\" id=\"memDiv\" style=\"display: none\">\n                        <div class=\"card-header\">\n                          <h3 class=\"card-title\">应用统计</h3>\n                        </div>\n                        <div class=\"card-body\">\n                          <table class=\"table table-striped table-hover\">\n                            <tbody>\n                            <tr>\n                              <td>总应用数</td>\n                              <td id=\"appCount\"></td>\n                              <td>主节点数</td>\n                              <td id=\"masterInsCount\"></td>\n                            </tr>\n                            <tr>\n                              <td>申请内存</td>\n                              <td id=\"applyMem\"></td>\n                              <td>内存使用率</td>\n                              <td id=\"memUsedRatio\"></td>\n                            </tr>\n                            </tbody>\n                          </table>\n\n                          <div class=\"row\">\n                            <div class=\"col-lg-12\">\n                              <div id=\"memChart\" style=\"min-height: 300px;\" class=\"echart\"></div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n\n                    <div class=\"col-lg-4\">\n                      <div class=\"card\" id=\"monitorDiv\" style=\"display: none\">\n                        <div class=\"card-header\">\n                          <h3 class=\"card-title\">监控统计</h3>\n                        </div>\n                        <div class=\"card-body\">\n                          <table class=\"table table-striped table-hover\">\n                            <tbody>\n                            <tr>\n                              <td>总异常数</td>\n                              <td id=\"excpCount\"></td>\n                              <td>慢查询数</td>\n                              <td id=\"slowCount\"></td>\n                            </tr>\n                            <tr>\n                              <td>延迟事件数</td>\n                              <td id=\"latencyCount\"></td>\n                              <td>命令调用数</td>\n                              <td id=\"cmdCount\"></td>\n                            </tr>\n                            </tbody>\n                          </table>\n\n                          <div class=\"row\">\n                            <div class=\"col-lg-12\">\n                              <div id=\"monitorChart\" style=\"min-height: 300px;\" class=\"echart\"></div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n\n                    <div class=\"col-lg-4\">\n                      <div class=\"card\" id=\"auditDiv\" style=\"display: none\">\n                        <div class=\"card-header\">\n                          <h3 class=\"card-title\">内存审计top5</h3>\n                        </div>\n                        <div class=\"card-body\">\n                          <table class=\"table table-striped table-hover\">\n                            <tbody>\n                            <tr>\n                              <td>请合理配置应用资源</td>\n                            </tr>\n                            <tr>\n                              <td>展示内存使用率最低的前5个应用</td>\n                            </tr>\n                            </tbody>\n                          </table>\n                          <div class=\"row\">\n                            <div class=\"col-lg-12\">\n                              <div id=\"auditChart\" style=\"min-height: 300px;\" class=\"echart\"></div>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n                </div>\n           <!--   </div>\n            </div>-->\n          </div>\n\n          <div class=\"container-fluid\">\n            <!-- stat info End -->\n            <div class=\"card\">\n              <div class=\"card-header\">\n                <h3 class=\"card-title\">应用列表</h3>\n              </div>\n              <#if currentUser?? && currentUser.type?? && currentUser.type == 0>\n                <div class=\"card-body\">\n                  <!-- form begin -->\n                  <div class=\"\">\n                    <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/admin/app/list\" id=\"appList\" name=\"ec\">\n                      <div class=\"col-auto\">\n                        <select id=\"bizId\" name=\"bizId\" class=\"selectpicker w-100 border rounded\" data-live-search=\"true\">\n                          <option value=\"-1\">业务组</option>\n                          <#list bizList as biz>\n                            <option value=\"${biz.id}\" <#if appSearch.bizId?? && biz.id == appSearch.bizId>selected</#if>>${biz.name}</option>\n                          </#list>\n                        </select>\n                      </div>\n                      <div class=\"col-auto\">\n                        <select id=\"userId\" name=\"userId\" class=\"selectpicker w-100 border rounded\" data-live-search=\"true\">\n                          <option value=\"-1\">用户</option>\n                          <#list userList as userInfo>\n                            <option value=\"${userInfo.id}\" <#if (appSearch.userId?? && userInfo.id == appSearch.userId)>selected</#if>>${userInfo.chName}(${userInfo.name})</option>\n                          </#list>\n                        </select>\n                      </div>\n                      <div class=\"col-md-2\">\n                        <input type=\"text\" class=\"form-control\" id=\"appParam\" name=\"appParam\" value=\"${appParam!}\" placeholder=\"应用ID/应用名\">\n                      </div>\n                      <div class=\"col-auto\">\n                        <select name=\"versionId\" id=\"versionId\" class=\"form-select select2_category\">\n                          <option value=\"\">Redis版本</option>\n                          <#list resourcelist as version>\n                          <option value=\"${version.id}\" <#if appSearch.versionId?? && version.id == appSearch.versionId>selected</#if>>${version.name}</option>\n                        </#list>\n                        </select>\n                      </div>\n                      <div class=\"col-auto\">\n                        <select name=\"appType\" class=\"form-select w-100\">\n                          <option value=\"\">\n                            全部类型\n                          </option>\n                          <option value=\"2\" <#if appSearch.appType?? && appSearch.appType == 2>selected</#if>>\n                          Redis-Cluster\n                          </option>\n                          <option value=\"5\" <#if appSearch.appType?? && appSearch.appType == 5>selected</#if>>\n                          Redis-Sentinel\n                          </option>\n                          <option value=\"6\" <#if appSearch.appType?? && appSearch.appType == 6>selected</#if>>\n                          Redis-Standalone\n                          </option>\n                        </select>\n                      </div>\n                      <div class=\"col-auto\">\n                        <select name=\"appStatus\" class=\"form-select w-100\">\n                          <option value=\"-1\">\n                            全部状态\n                          </option>\n                          <option value=\"0\" <#if appSearch.appStatus?? && appSearch.appStatus == 0>selected</#if>>\n                          未分配\n                          </option>\n                          <option value=\"1\" <#if appSearch.appStatus?? && appSearch.appStatus == 1>selected</#if>>\n                          申请中\n                          </option>\n                          <option value=\"2\" <#if appSearch.appStatus?? && appSearch.appStatus == 2>selected</#if>>\n                          运行中\n                          </option>\n                          <option value=\"3\" <#if appSearch.appStatus?? && appSearch.appStatus == 3>selected</#if>>\n                          已下线\n                          </option>\n                          <option value=\"4\" <#if appSearch.appStatus?? && appSearch.appStatus == 4>selected</#if>>\n                          驳回\n                          </option>\n                        </select>\n                      </div>\n                      <div class=\"col-auto\">\n                        <select name=\"pageSize\" class=\"form-select w-100\">\n                          <option value=\"10\" <#if page.pageSize?? && page.pageSize == 10>selected</#if>>\n                          10行\n                          </option>\n                          <option value=\"20\" <#if page.pageSize?? && page.pageSize == 20>selected</#if>>\n                          20行\n                          </option>\n                          <option value=\"50\" <#if page.pageSize?? && page.pageSize == 50>selected</#if>>\n                          50行\n                          </option>\n                          <option value=\"100\" <#if page.pageSize?? && page.pageSize == 100>selected</#if>>\n                          100行\n                          </option>\n                        </select>\n                      </div>\n                      <input type=\"hidden\" name=\"pageNo\" id=\"pageNo\">\n                      <button type=\"submit\" class=\"btn btn-primary col-auto\">查询</button>\n                    </form>\n                  </div>\n                  <!-- form end -->\n                </div>\n              </#if>\n\n              <div class=\"card-body table-responsive\">\n                <!-- table begin -->\n                <table class=\"table table-striped table-hover table-sm border-top border-bottom\" style=\"white-space: nowrap\">\n                  <thead>\n                      <th scope=\"col\">应用ID</td>\n                      <th scope=\"col\">应用名</td>\n                      <th scope=\"col\">环境</td>\n                      <th scope=\"col\">版本</td>\n                      <th scope=\"col\">应用类型</td>\n                      <th scope=\"col\">空间详情</td>\n                      <th scope=\"col\">命中率</td>\n                      <th scope=\"col\">已运行时间</td>\n                  </thead>\n                  <tbody>\n                  <#list appDetailList as appDetail>\n                    <tr>\n                      <th scope=\"row\">\n                        <#if appDetail.appDesc.status == 0 || appDetail.appDesc.status == 1>\n                          ${appDetail.appDesc.appId}\n                        <#elseif appDetail.appDesc.status == 2 || appDetail.appDesc.status == 3 || appDetail.appDesc.status == 4>\n                          <a title=\"${appDetail.appDesc.intro}\" target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId}\">${appDetail.appDesc.appId}</a>\n                        </#if>\n                      </th>\n                      <td>\n                        <#if appDetail.appDesc.status == 0 || appDetail.appDesc.status == 1>\n                            ${appDetail.appDesc.name}\n                        <#elseif appDetail.appDesc.status == 2 || appDetail.appDesc.status == 3 || appDetail.appDesc.status == 4>\n                          <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId}\">${appDetail.appDesc.name}</a>\n                        </#if>\n                      </td>\n                      <td>\n                        <#if appDetail.appDesc.isTest == 2>\n                          试用\n                        <#elseif appDetail.appDesc.isTest == 1>\n                          测试\n                        <#elseif appDetail.appDesc.isTest == 0>\n                          正式\n                        </#if>\n                      </td>\n                      <td>\n                        ${appDetail.appDesc.versionName?replace('redis-','')}\n                      </td>\n                      <td>${appDetail.appDesc.typeDesc!}</td>\n                      <td>\n                        <div class=\"progress progress-fs-1\" role=\"progressbar\" aria-valuenow=\"${appDetail.memUsePercent}\" aria-valuemax=\"100\"\n                             aria-valuemin=\"0\" >\n                          <#if (appDetail.memUsePercent >= 80)>\n                          <#assign progressBarStatus=\"bg-danger\"/>\n                          <#else>\n                          <#assign progressBarStatus=\"bg-success\"/>\n                          </#if>\n                          <div class=\"progress-bar ${progressBarStatus}\"\n                               style=\"width: ${appDetail.memUsePercent}%; overflow: visible;\">\n                            <span style=\"color: #000000; margin-bottom: 0\">\n                              ${(appDetail.mem * appDetail.memUsePercent / 100 / 1024)?string('#.##')}G&nbsp;&nbsp;Used / ${(appDetail.mem / 1024 * 1.0)?string('#.##')}G&nbsp;&nbsp;Total\n                            </span>\n                          </div>\n                        </div>\n                      </td>\n                      <td>\n                        <#if (appDetail.hitPercent <= 0)>\n                        无\n                        <#elseif (appDetail.hitPercent <= 30)>\n                        <label class=\"badge bg-danger\">${appDetail.hitPercent}%</label>\n                        <#elseif (appDetail.hitPercent >= 30 && appDetail.hitPercent < 50)>\n                        <label class=\"badge bg-warning\">${appDetail.hitPercent}%</label>\n                        <#elseif (appDetail.hitPercent >= 50 && appDetail.hitPercent < 90)>\n                        <label class=\"badge bg-info\">${appDetail.hitPercent}%</label>\n                        <#else>\n                        <label class=\"badge bg-success\">${appDetail.hitPercent}%</label>\n                        </#if>\n                      </td>\n                      <td>\n                        <#if appDetail.appDesc.status == 0>\n                        <font color=\"red\">未分配</font>\n                        <#elseif appDetail.appDesc.status == 1>\n                        <font color=\"red\">申请中</font>\n                        <#elseif appDetail.appDesc.status == 2>\n                          <#if (appDetail.appDesc.isTest == 2)>\n                            <label style=\"color:orange\">\n                              ${(30-((.now?long) - (appDetail.appDesc.passedTime?long))/1000/60/60/24)?string(\"0.0\")}天后到期\n                            </label>\n                          <#else>\n                            <label style=\"color:yellowgreen\">\n                              ${appDetail.appDesc.appRunDays!}天\n                            </label>\n                          </#if>\n                        <#elseif appDetail.appDesc.status == 3>\n                        <font color=\"red\">已下线</font>\n                        <#elseif appDetail.appDesc.status == 4>\n                        <font color=\"red\">驳回</font>\n                        </#if>\n                      </td>\n                    </tr>\n                  </#list>\n                  </tbody>\n                </table>\n                <!-- End Table with stripped rows -->\n                <#if currentUser?? && currentUser.type?? && currentUser.type == 0>\n                <div style=\"margin-bottom: 10px;\">\n                  <span>\n                    <div id=\"pageDetail\" style=\"float:left;padding-top:7px;color:#4A64A4;\">\n                      <#if page??>\n                          共${page.totalPages!}页,${page.totalCount!}条\n                      </#if>\n                    </div>\n                    <nav aria-label=\"Page navigation example\" id=\"nav_navigation\" class=\"d-inline-flex float-end\">\n                      <#include \"/inc/page.html\">\n                    </nav>\n                  </span>\n                </div>\n                </#if>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      <#include \"/inc/footer.html\">\n    </div>\n  </body>\n</html>\n<script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/paginator/bootstrap-paginator.js\"></script>\n<script type=\"text/javascript\">\n  function searchJob(page){\n    //form传参用pageSize\n    document.getElementById(\"pageNo\").value=page;\n    document.getElementById(\"appList\").submit();\n  }\n\n  function setAppStatisticOption(legendData, capacityData, versionData){\n    var option = {\n      tooltip: {\n        trigger: 'item',\n        formatter: '{a} <br/>{b}: {c} ({d}%)'\n      },\n      legend: {\n        data: legendData\n      },\n      series: [\n        {\n          name: '版本',\n          type: 'pie',\n          selectedMode: 'single',\n          radius: [0, '30%'],\n          label: {\n            position: 'inner',\n            fontSize: 14\n          },\n          labelLine: {\n            show: false\n          },\n          data: versionData\n        },\n        {\n          type: 'pie',\n          radius: ['45%', '60%'],\n          labelLine: {\n            length: 20\n          },\n          label: {\n            name: '内存',\n            formatter: '{b|{b}：} \\n{per|{d}%}  ',\n            backgroundColor: '#F6F8FC',\n            borderColor: '#8C8D8E',\n            borderWidth: 1,\n            borderRadius: 2,\n            rich: {\n              a: {\n                color: '#6E7079',\n                lineHeight: 22,\n                align: 'center'\n              },\n              hr: {\n                borderColor: '#8C8D8E',\n                width: '100%',\n                borderWidth: 1,\n                height: 0\n              },\n              b: {\n                color: '#4C5058',\n                fontSize: 14,\n                fontWeight: 'bold',\n                lineHeight: 33\n              },\n              per: {\n                color: '#fff',\n                backgroundColor: '#4C5058',\n                padding: [3, 4],\n                borderRadius: 4\n              }\n            }\n          },\n          data: capacityData\n        }\n      ]\n    };\n    return option;\n  }\n\n  function initFigure(id, legendData, capacityData, versionData){\n    echarts.init(document.querySelector(\"#\" + id)).setOption(setAppStatisticOption(legendData, capacityData, versionData));\n  };\n\n  function setAppMonitorStatisticOption(dateData, slowLogData, excpData, latencyData){\n    var option = {\n      tooltip: {\n        trigger: 'axis'\n      },\n      legend: {\n        data: ['慢查询', '异常', '延迟事件']\n      },\n      grid: {\n        left: '4%',\n        right: '4%',\n        bottom: '5%',\n        containLabel: true\n      },\n      xAxis: {\n        type: 'category',\n        data: dateData\n      },\n      yAxis: {\n        type: 'value'\n      },\n      series: [\n        {\n          name: '慢查询',\n          type: 'line',\n          smooth: true,\n          data: slowLogData\n        },\n        {\n          name: '异常',\n          type: 'line',\n          smooth: true,\n          data: excpData\n        },\n        {\n          name: '延迟事件',\n          type: 'line',\n          smooth: true,\n          data: latencyData\n        },\n      ]\n    };\n    return option;\n  }\n\n  function initMonitorFigure(id, dateData, slowLogData, excpData, latencyData){\n    echarts.init(document.querySelector(\"#\" + id)).setOption(setAppMonitorStatisticOption(dateData, slowLogData, excpData, latencyData));\n  };\n\n  function setAppCapacityAuditOption(xData, data){\n    var option = {\n      title: {\n        text: \"内存审计top5\"\n      },\n      tooltip: {\n        trigger: 'axis',\n        formatter: \"{c} %\"\n      },\n      xAxis: {\n        type: 'category',\n        data: xData\n      },\n      yAxis: {\n        type: 'value'\n      },\n      series: [\n        {\n          data: data,\n          type: 'bar'\n        }\n      ]\n    };\n    return option;\n  }\n\n  function initAuditFigure(id, xData, data){\n    echarts.init(document.querySelector(\"#\" + id)).setOption(setAppCapacityAuditOption(xData, data));\n  };\n\n  $(document).ready(\n          function () {\n            var pieUrl = \"${request.contextPath}/admin/app/listStats\";\n            $.ajax({\n              type: \"get\",\n              url: pieUrl,\n              async: true,\n              success: function (data) {\n                console.log(data);\n                // var data = eval(\"(\" + data + \")\");\n                var capacityMap = data.capacityMap;\n                var monitorList = data.monitorList;\n                var appCount = capacityMap.appCount;\n                var applyMem = capacityMap.applyMem;\n                var usedMem = capacityMap.usedMem;\n                var notUsedMem = capacityMap.notUsedMem;\n                var masterCount = capacityMap.masterCount;\n                var versionList = capacityMap.versionList;\n                var auditList = capacityMap.auditList;\n                var legendData = [\n                        '使用内存-' + usedMem,\n                        '未使用内存-' + notUsedMem\n                ]\n                var versionData = [];\n                for(i = 0; i < versionList.length; i++){\n                  versionData.push({\n                      \"value\": versionList[i].value,\n                      \"name\": versionList[i].key\n                  });\n                }\n                var capacityData = [];\n                capacityData.push({\n                  \"value\": usedMem,\n                  \"name\": \"使用内存\"\n                });\n                capacityData.push({\n                  \"value\": notUsedMem,\n                  \"name\": \"未使用内存\"\n                });\n\n                var memNum = new Number(applyMem/1024/1024/1024);\n                var usedRatio = \"0%\";\n                if(applyMem != 0){\n                  var ratioNum = new Number(usedMem * 100/applyMem);\n                  usedRatio = ratioNum.toFixed(2) + \"%\";\n                }\n                $(\"#appCount\").text(appCount + \"个\");\n                $(\"#masterInsCount\").text(masterCount + \"个\");\n                $(\"#applyMem\").text(memNum.toFixed(2) + \"G\");\n                $(\"#memUsedRatio\").text(usedRatio);\n                $(\"#memDiv\").attr(\"style\",  \"display:'';\");\n\n\n\n                var excpCount = 0;\n                var slowCount = 0;\n                var latencyCount = 0;\n                var cmdCount = 0;\n                if(monitorList.length > 0){\n                  var monitor = monitorList[monitorList.length - 1];\n                  excpCount = monitor.connExpCount + monitor.cmdExpCount;\n                  slowCount = monitor.slowLogCount;\n                  latencyCount = monitor.latencyCount;\n                  cmdCount = monitor.cmdCount;\n                }\n                $(\"#excpCount\").html('<a href=\"#\">' + excpCount + '</a>');\n                $(\"#slowCount\").html('<a href=\"#\">' + slowCount + '</a>');\n                $(\"#latencyCount\").html('<a href=\"#\">' + latencyCount + '</a>');\n                $(\"#cmdCount\").html('<a href=\"#\">' + cmdCount + '</a>');\n                $(\"#monitorDiv\").attr(\"style\",  \"display:'';\");\n\n                $(\"#auditDiv\").attr(\"style\",  \"display:'';\");\n                initFigure('memChart', legendData, capacityData, versionData);\n\n                var dateData = [];\n                var slowLogData = [];\n                var excpData = [];\n                var latencyData = [];\n                for(i = 0; i < monitorList.length; i++){\n                  var monitor = monitorList[i];\n                  dateData.push(monitor.gatherTime);\n                  slowLogData.push(monitor.slowLogCount);\n                  excpData.push(monitor.connExpCount + monitor.cmdExpCount);\n                  latencyData.push(monitor.latencyCount);\n                }\n                initMonitorFigure('monitorChart', dateData, slowLogData, excpData, latencyData);\n\n                var xData = [];\n                var auditData = [];\n                var color = [\n                  '#a90000',\n                  '#0056ff',\n                  '#bfx0b0',\n                  '#ff6600',\n                  '#ffac00'\n                ];\n                for(i = 0; i < auditList.length; i++){\n                  var audit = auditList[i];\n                  xData.push(audit.key);\n                  auditData.push({\n                    \"value\": audit.value,\n                    \"itemStyle\" : {\n                      \"color\": color[i]\n                    }\n                  })\n                }\n\n                initAuditFigure('auditChart', xData, auditData);\n              }\n            });\n          });\n\n\n</script>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appMachineInstancesTopology.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"card\">\n    <div class=\"card-body mt-3 table-responsive\">\n      <div class=\"page-header\">\n        <a target=\"_blank\" data-container=\"body\" class='button button-caution button-square button-small'> </a>\n        <label>代表master节点</label>\n        <a target=\"_blank\" data-container=\"body\" class='button button-caution button-square button-small button-border'> </a>\n        <label>代表slave节点</label>\n        <#if (appDesc.type == 5)>\n          <a target=\"_blank\" data-container=\"body\" class='button button-action button-square button-small'> </a>\n          <label>代表Sentinel节点</label>\n        </#if>\n      </div>\n\n      <!-- table begin -->\n      <table class=\"table table-striped table-hover\">\n        <thead>\n        <tr>\n          <th scope=\"col\" style='vertical-align: middle;text-align: center;'>机器</th>\n          <#list 1..instancePairCount as instanceIndex>\n            <th scope=\"col\" style='vertical-align: middle;text-align: center;'>实例对${instanceIndex}</th>\n          </#list>\n        </tr>\n        </thead>\n        <tbody>\n        <#list machineInstanceMap?keys as key>\n          <tr>\n            <td  style='vertical-align: middle;text-align: center;'>${key}</td>\n            <#list 1..instancePairCount as instanceIndex>\n              <td style='vertical-align: middle;text-align: center;'>\n                <#list machineInstanceMap[key] as instance>\n                  <#assign instanceNodeTypeStyle=\"button-caution\">\n                  <#if (instance.type == 5)>\n                    <#assign instanceNodeTypeStyle=\"button-action\">\n                  </#if>\n                  <#if (instance.groupId == instanceIndex)>\n                    <a target=\"_blank\" data-container=\"body\" data-bs-toggle=\"popover\" data-bs-placement=\"top\" data-bs-content=\"<a target='_blank' href='${request.contextPath}/admin/instance/index?instanceId=${instance.id}'>${instance.ip}:${instance.port}</a>\" href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.id}\" class='button ${instanceNodeTypeStyle} button-square button-small <#if (instance.masterInstanceId > 0)>button-border</#if> '>${instance.groupId}</a>\n                    <#if (instance.status == 0)>\n                      (心跳停止)\n                    </#if>\n                  </#if>\n\n                </#list>\n              </td>\n            </#list>\n          </tr>\n        </#list>\n        </tbody>\n      </table>\n  <!-- End Table with stripped rows -->\n    </div>\n  </div>\n</div>\n<link href=\"${request.contextPath}/assets/css/buttons.css\" rel=\"stylesheet\" />\n<script type=\"text/javascript\" src=\"${request.contextPath}/assets/js/custom/myPopover.js\"></script>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appStat.html",
    "content": "<div class=\"\">\n  <script>\n    var startDate = '${startDate}';\n    var endDate = '${endDate}';\n    var yesterDate = '${yesterDay}';\n    var betweenOneDay = '${betweenOneDay}';\n    var masterNum = '${appDetail.masterNum}';\n    var appId = '${appId}';\n    var chartType = 'line';\n    var chartParams = \"&startDate=\" + startDate + \"&endDate=\" + endDate;\n    var chartParamsCompare = \"&startDate=\" + yesterDate + \"&endDate=\" + startDate;\n    var betweenParams = \"&startDate=\" + yesterDate + \"&endDate=\" + endDate;\n    var appTotalMem = '${appDetail.mem}';\n\n    //改变内存阀值\n    function cleanAppData(appId){\n      var appCleanDataBtn = document.getElementById(\"appCleanDataBtn\");\n      appCleanDataBtn.disabled = true;\n\n      $.post(\n              '${request.contextPath}/admin/app/cleanAppData',\n              {\n                appId: appId\n              },\n              function(data){\n                if(data==1){\n                  alert(\"appId:\" + appId + \"清除数据成功！\");\n                }else{\n                  alert(\"appId:\" + appId + \"清除数据失败,请联系管理员查看原因！\");\n                }\n                appCleanDataBtn.disabled = false;\n              }\n      );\n    }\n  </script>\n  <div class=\"\">\n    <div class=\"card\">\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <div class=\"col-lg-5 text-lg-left\">\n            <h3 class=\"card-title\">应用:\n            <#if isMaster?? && (isMaster!)>\n              <a href=\"${request.contextPath}/manage/app/index?appId=${appId!}\" target=\"_blank\">${appDetail.appDesc.name!}</a>\n            <#else>\n              ${appDetail.appDesc.name!}\n            </#if>\n            </h3>\n          </div>\n          <div class=\"col-lg-7\">\n            <form method=\"post\" action=\"${request.contextPath}/admin/app/index\" id=\"ec\" name=\"ec\" class=\"row d-flex justify-content-end\">\n              <label class=\"col-form-label col-auto\" style=\"font-weight:bold;text-align:left;\">\n                日期:&nbsp;&nbsp;\n              </label>\n              <input class=\"col-auto\" type=\"date\" size=\"21\" name=\"startDate\" id=\"startDate\" value=\"${startDate}\"/>\n\n              <input type=\"hidden\" size=\"20\" name=\"endDate\" id=\"endDate\" value=\"${endDate}\"/>\n              <input type=\"hidden\" name=\"appId\" value=\"${appDetail.appDesc.appId}\">\n              <input type=\"submit\" class=\"btn btn-primary col-auto ml-2\" value=\"查询\"/>\n            </form>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <div class=\"row\">\n    <div class=\"col-lg-6 text-lg-left\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">\n            全局信息&nbsp;&nbsp;\n            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/appScale?appId=${appId}\" class=\"btn btn-primary\" role=\"button\">申请扩容</a>\n            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/appConfig?appId=${appId}\" class=\"btn btn-primary\" role=\"button\">申请修改配置</a>\n            <a target=\"_blank\" href=\"${request.contextPath}/client/show/index?appId=${appId}\" class=\"btn btn-primary\"\n               role=\"button\">客户端统计</a>\n            <#if appDetail.appDesc.appPassword??>\n              <button type=\"button\" class=\"btn btn-primary\" data-bs-toggle=\"modal\" data-bs-target=\"#appCodeChangeModal\">\n                应用密码\n              </button>\n            </#if>\n            <#if (appDetail.appDesc.status == 2) && (appDetail.appDesc.isTest == 1)>\n                <button id=\"appCleanDataBtn\" type=\"button\" class=\"btn btn-primary\" onclick=\"if(window.confirm('确认要清除应用appid=${appDetail.appDesc.appId}的数据?!')){cleanAppData('${appDetail.appDesc.appId}');return true;}else{return false;}\">清空数据</button>\n            </#if>\n          </h3>\n        </div>\n        <div class=\"card-body\">\n          <div class=\"table-responsive\">\n            <table class=\"table table-striped table-hover\">\n              <tbody>\n              <tr>\n                <td>内存使用率</td>\n                <td>\n                  <div class=\"progress progress-fs-1\" role=\"progressbar\" aria-valuenow=\"${appDetail.memUsePercent}\" aria-valuemax=\"100\"\n                       aria-valuemin=\"0\" >\n                    <#if (appDetail.memUsePercent >= 80.00)>\n                        <div class=\"progress-bar bg-danger\"\n                             style=\"width: ${appDetail.memUsePercent}%;overflow:visible;\">\n                    <#else>\n                        <div class=\"progress-bar bg-success\"\n                             style=\"width: ${appDetail.memUsePercent}%;overflow:visible;\">\n                    </#if>\n                        <span style=\"color: #000000; margin-bottom: 0\">\n                        ${(appDetail.mem  * appDetail.memUsePercent / 100 / 1024)?string(\"#.##\")}G&nbsp;&nbsp;Used/${(appDetail.mem / 1024 * 1.0)?string(\"#.##\")}G&nbsp;&nbsp;Total\n                        </span>\n                    </div>\n                  </div>\n                </td>\n                <td>当前连接数</td>\n                <td>${appDetail.conn}</td>\n              </tr>\n              <tr>\n                <td>应用版本</td>\n                <td>${appDetail.appDesc.versionName}</td>\n                <td>应用类型</td>\n                <td>${appDetail.appDesc.typeDesc!}</td>\n              </tr>\n                <tr>\n                  <td>应用主节点数</td>\n                  <td>${appDetail.masterNum}</td>\n                  <td>应用从节点数</td>\n                  <td>${appDetail.slaveNum}</td>\n                </tr>\n                <tr>\n                  <td>应用命中率</td>\n                  <td><a href=\"${request.contextPath}/admin/app/getAppHitRatioInfo?appId=${appDetail.appDesc.appId}\" target=\"_blank\" >${appDetail.hitPercent}%</a></td>\n                  <td>当前对象数</td>\n                  <td>${(appDetail.currentObjNum)?string(\"#,#00\")}\n                </tr>\n                <tr>\n                  <td>应用当前状态</td>\n                  <td>${appDetail.appDesc.statusDesc}</td>\n                  <td>应用分布机器数量</td>\n                  <td>${appDetail.machineNum}</td>\n                </tr>\n              </tbody>\n            </table>\n          </div>\n        </div>\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">各命令峰值信息</h3>\n        </div>\n        <div class=\"table-responsive\">\n          <table class=\"table table-striped table-hover\">\n            <thead>\n              <tr>\n                <th scope=\"col\">命令</th>\n                <th scope=\"col\">峰值QPM</th>\n                <th scope=\"col\">峰值产生时间</th>\n              </tr>\n            </thead>\n            <tbody>\n              <#list  top5ClimaxList as command>\n                <tr>\n                  <td>${command.commandName}</td>\n                  <td>${(command.commandCount)?string(00)}</td>\n                  <td>${((command.createTime)?string(\"yyyy-MM-dd HH:mm\"))!}</td>\n                </tr>\n              </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-lg-6\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">命令调用统计</h3>\n        </div>\n        <div class=\"table-responsive\">\n          <table class=\"table table-striped table-hover\">\n            <thead>\n            <tr>\n              <th scope=\"col\">总调用量(天)</th>\n              <th scope=\"col\">${commandCount!}</th>\n            </tr>\n            </thead>\n          </table>\n        </div>\n\n        <div class=\"card-body\">\n          <div id=\"containerTop5\" class=\"echart p-4\"  style=\"min-height: 400px;\">\n            <script>\n              function setFigureOption(title, dataArr){\n                var length = dataArr.length;\n                var series = [];\n                var labels = [];\n                var dataSeries = [];\n                for (var i = 0; i < length; i++) {\n                  var data = dataArr[i];\n                  labels.push(data.commandName);\n                  series.push(data.y);\n                  var dataPoint = {\n                    name: data.commandName + \" : \" + data.y,\n                    value: data.y,\n                  };\n                  dataSeries.push(dataPoint);\n                }\n                var options = {\n                  title: {\n                    text: title,\n                    left: 'center'\n                  },\n                  tooltip: {\n                    trigger: 'item'\n                  },\n                  legend: {\n                    orient: 'vertical',\n                    left: 'right'\n                  },\n                  series: [{\n                    type: 'pie',\n                    radius: '65%',\n                    data: dataSeries,\n                    emphasis: {\n                      itemStyle: {\n                        shadowBlur: 10,\n                        shadowOffsetX: 0,\n                        shadowColor: 'rgba(0, 0, 0, 0.5)'\n                      }\n                    }\n                  }]\n                };\n                return options;\n              }\n\n              function initFigure(id, title, dataArr){\n                echarts.init(document.querySelector(\"#\" + id)).setOption(setFigureOption(title, dataArr));\n              };\n\n              $(document).ready(\n                      function () {\n                        var pieUrl = \"${request.contextPath}/admin/app/getTop5Commands?appId=\" + appId + chartParams;\n                        $.ajax({\n                          type: \"get\",\n                          url: pieUrl,\n                          async: true,\n                          success: function (data) {\n                            var dataArr = eval(\"(\" + data + \")\");\n                            initFigure('containerTop5', '命令分布', dataArr);\n                          }\n                        });\n                      });\n            </script>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n      <!-- 全命中统计 -->\n  <div id=\"containerCommands\"\n       style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n  <!-- 命中相关 -->\n  <div id=\"containerHits\"\n       style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n  <!-- 网络流量 -->\n  <div id=\"containerNet\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n  <!-- CPU消耗流量 -->\n  <div id=\"containerCpuUsed\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n  <!-- 内存变化相关 -->\n  <div id=\"containerMemory\"\n       style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n  <!-- 客户端连接数相关 -->\n  <div id=\"containerClients\"\n       style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n  <!-- dbsize相关 -->\n  <div id=\"containerDbsize\"\n       style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n\n  <!-- 过期/淘汰键变化相关 -->\n  <div id=\"containerExpiredEvictedKeys\"\n       style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  <!-- 命令相关 -->\n  <script type=\"text/javascript\">\n    //查询一天出每分钟数据\n    if (betweenOneDay == 1) {\n      $(document).ready(\n              function () {\n                var options = getOption(\"containerCommands\", \"<b>全命令统计</b>\", \"次数\");\n                var commandsUrl = \"${request.contextPath}/admin/app/getMutiDatesCommandStats.json?appId=\" + appId + betweenParams;\n                $.ajax({\n                  type: \"get\",\n                  url: commandsUrl,\n                  async: true,\n                  success: function (data) {\n                    var dates = new Array();\n                    dates.push(startDate);\n                    dates.push(yesterDate);\n                    pushOptionSeries(options, data, dates, \"命令趋势图\");\n                    new Highcharts.Chart(options);\n                  }\n                });\n\n              });\n    } else {\n      $(document).ready(\n              function () {\n                var options = getOption(\"containerCommands\", \"<b>全命令统计</b>\", \"次数\");\n                var commandsUrl = \"${request.contextPath}/admin/app/getCommandStats?appId=\" + appId + chartParams;\n                $.ajax({\n                  type: \"get\",\n                  url: commandsUrl,\n                  async: true,\n                  success: function (data) {\n                    var nameLegend = \"命令趋势图\";\n                    var finalPoints = getSeriesPoints(data, nameLegend);\n                    options.series.push(finalPoints);\n                    new Highcharts.Chart(options);\n                  }\n                });\n              });\n    }\n  </script>\n\n  <!-- 命中相关 -->\n  <script type=\"text/javascript\">\n    //查询一天出每分钟数据\n    if (betweenOneDay == 1) {\n      $(document).ready(\n              function () {\n                var options = getOption(\"containerHits\", \"<b>命中统计</b>\", \"次数\");\n                var commandsUrl = \"${request.contextPath}/admin/app/getMutiDatesAppStats.json?appId=\" + appId + \"&statName=hits\" + betweenParams;\n                $.ajax({\n                  type: \"get\",\n                  url: commandsUrl,\n                  async: true,\n                  success: function (data) {\n                    var dates = new Array();\n                    dates.push(startDate);\n                    dates.push(yesterDate);\n                    pushOptionSeries(options, data, dates, \"命中趋势图\");\n                    new Highcharts.Chart(options);\n                  }\n                });\n              });\n    } else {\n      $(document).ready(\n              function () {\n                var options = getOption(\"containerHits\", \"<b>命中统计</b>\", \"次数\");\n                var commandsUrl = \"${request.contextPath}/admin/app/getAppStats.json?appId=\" + appId + \"&statName=hits\" + chartParams + \"&timeDimensionalityIndex=1\";\n                $.ajax({\n                  type: \"get\",\n                  url: commandsUrl,\n                  async: true,\n                  success: function (data) {\n                    var nameLegend = \"命中趋势图\";\n                    var finalPoints = getSeriesPoints(data, nameLegend);\n                    options.series.push(finalPoints);\n                    new Highcharts.Chart(options);\n                  }\n                });\n              });\n    }\n  </script>\n\n  <!-- 网络流量 -->\n  <script type=\"text/javascript\">\n    var allInstanceNetStatUrl = \"${request.contextPath}/admin/app/appInstanceNetStat?appId=\" + appId + chartParams;\n    $(document).ready(\n            function () {\n              var options = getOption(\"containerNet\", \"<b>网络流量<a href='\" + allInstanceNetStatUrl + \"' target='_blank'>(查看实例流量)</a><b>\", \"\");\n              //网络流量\n              var netUrl = \"${request.contextPath}/admin/app/getMutiStatAppStats.json?appId=\" + appId + \"&statName=netInput,netOutput\" + chartParams;\n              $.ajax({\n                type: \"get\",\n                url: netUrl,\n                async: true,\n                success: function (data) {\n\n                  var dataObject = eval(\"(\" + data.data + \")\");\n                  var inputDataArr = dataObject[\"netInput\"];\n\n                  //1.input\n                  var inputPoints = getNetPoints(inputDataArr, \"net_input\");\n                  //确认单位\n                  options.yAxis.title.text = inputPoints.unitTxt;\n                  var unit = inputPoints.unit;\n\n                  options.series.push(inputPoints);\n\n                  //2.output\n                  var outputDataArr = dataObject[\"netOutput\"];\n                  var outputPoints = getNetPoints(outputDataArr, \"net_output\", unit);\n                  options.series.push(outputPoints);\n\n                  new Highcharts.Chart(options);\n                }\n              });\n            });\n  </script>\n\n  <!-- CPU消耗流量 -->\n  <script type=\"text/javascript\">\n    var containerCpuUsedUrl = \"${request.contextPath}/admin/app/appInstanceCpuStat?appId=\" + appId + chartParams;\n    $(document).ready(\n            function () {\n              var options = getOption(\"containerCpuUsed\", \"<b>CPU消耗<a href='\" + containerCpuUsedUrl + \"' target='_blank'>(查看实例CPU消耗)</a><b>\", \"\");\n              //网络流量\n              var cpuUsedUrl = \"${request.contextPath}/admin/app/getMutiStatAppStats.json?appId=\" + appId + \"&statName=cpuSys,cpuUser,cpuSysChildren,cpuUserChildren\" + chartParams;\n              $.ajax({\n                type: \"get\",\n                url: cpuUsedUrl,\n                async: true,\n                success: function (data) {\n                  var dataObject = eval(\"(\" + data.data + \")\");\n\n                  //1.cpuSys\n                  var cpuSysDataArr = dataObject[\"cpuSys\"];\n                  var cpuSysPoints = getCpuPoints(cpuSysDataArr, \"cpu_sys\", 1);\n                  options.series.push(cpuSysPoints);\n                  //确认单位\n                  var unit = cpuSysPoints.unit;\n                  options.yAxis.title.text = cpuSysPoints.unitTxt;\n\n                  //2.cpuUser\n                  var cpuUserDataArr = dataObject[\"cpuUser\"];\n                  var cpuUserPoints = getCpuPoints(cpuUserDataArr, \"cpu_user\", 1);\n                  options.series.push(cpuUserPoints);\n\n                  //3.cpuSysChildren\n                  var cpuSysChildrenDataArr = dataObject[\"cpuSysChildren\"];\n                  var cpuSysChildrenPoints = getCpuPoints(cpuSysChildrenDataArr, \"cpu_sys_children\", 1);\n                  options.series.push(cpuSysChildrenPoints);\n\n                  //4.cpuUserChildren\n                  var cpuUserChildrenDataArr = dataObject[\"cpuUserChildren\"];\n                  var cpuUserChildrenPoints = getCpuPoints(cpuUserChildrenDataArr, \"cpu_user_children\", 1);\n                  options.series.push(cpuUserChildrenPoints);\n\n                  // //5.cou base\n                  // var cpuBasePoints = getBaseCpuPoints(cpuSysDataArr, \"cpu_warning\", 1);\n                  // options.series.push(cpuBasePoints);\n\n                  new Highcharts.Chart(options);\n                }\n              });\n            });\n  </script>\n\n  <!-- 内存变化相关 -->\n  <script type=\"text/javascript\">\n\n\n    /*if(betweenOneDay == 1){*/\n    var containerMemFragRatioUrl = \"${request.contextPath}/admin/app/appInstanceMemFragRatioStat?appId=\" + appId + chartParams;\n    $(document).ready(\n            function () {\n              var options = getOption(\"containerMemory\", \"<b>内存使用量<a href='\" + containerMemFragRatioUrl + \"' target='_blank'>(查看实例内存碎片率)</a></b>\", \"M\");\n              var commandsUrl = \"${request.contextPath}/admin/app/getMutiStatAppStats.json?appId=\" + appId + \"&statName=usedMemory,usedMemoryRss\" + chartParams;\n              $.ajax({\n                type: \"get\",\n                url: commandsUrl,\n                async: true,\n                success: function (data) {\n                  var dataObject = eval(\"(\" + data.data + \")\");\n                  var usedMemoryDataArr = dataObject[\"usedMemory\"];\n\n                  //1.usedMemory\n                  var usedMemoryPoints = getMemoryPoints(usedMemoryDataArr, \"used_memory\", 1);\n                  //确认单位\n                  options.yAxis.title.text = usedMemoryPoints.unitTxt;\n                  var unit = usedMemoryPoints.unit;\n\n                  options.series.push(usedMemoryPoints);\n\n                  //2.usedMemoryRss\n                  var usedMemoryRssDataArr = dataObject[\"usedMemoryRss\"];\n                  var usedMemoryRssPoints = getMemoryPoints(usedMemoryRssDataArr, \"used_memory_rss\", 1);\n                  //确认单位\n                  options.yAxis.title.text = usedMemoryRssPoints.unitTxt;\n                  var unit = usedMemoryRssPoints.unit;\n                  options.series.push(usedMemoryRssPoints);\n\n                  //3. 应用总内存\n                  var maxMemoryPoints = getMemoryPoints(usedMemoryRssDataArr, \"应用总内存\", 1, parseInt(appTotalMem));\n                  options.series.push(maxMemoryPoints);\n\n                  new Highcharts.Chart(options);\n\n                }\n              });\n            });\n  </script>\n\n  <!-- 客户端连接数相关 -->\n  <script type=\"text/javascript\">\n    //查询一天出每分钟数据\n    $(document).ready(\n            function () {\n              var options = getOption(\"containerClients\", \"<b>客户端连接统计</b>\", \"个\");\n              var commandsUrl = \"${request.contextPath}/admin/app/getMutiDatesAppStats.json?appId=\" + appId + \"&statName=connectedClient\" + betweenParams;\n              $.ajax({\n                type: \"get\",\n                url: commandsUrl,\n                async: true,\n                success: function (data) {\n                  var dates = new Array();\n                  dates.push(startDate);\n                  dates.push(yesterDate);\n                  pushOptionSeries(options, data, dates, \"客户端连接趋势图\", \"个\");\n                  new Highcharts.Chart(options);\n                }\n              });\n            });\n  </script>\n\n  <!-- bsize相关 -->\n  <script type=\"text/javascript\">\n    //查询一天出每分钟数据\n    $(document).ready(\n            function () {\n              var options = getOption(\"containerDbsize\", \"<b>键个数统计</b>\", \"个\");\n              var commandsUrl = \"${request.contextPath}/admin/app/getMutiDatesAppStats.json?appId=\" + appId + \"&statName=objectSize\" + betweenParams;\n              $.ajax({\n                type: \"get\",\n                url: commandsUrl,\n                async: true,\n                success: function (data) {\n                  var dates = new Array();\n                  dates.push(startDate);\n                  dates.push(yesterDate);\n                  pushOptionSeries(options, data, dates, \"键个数趋势图\", \"个\");\n                  new Highcharts.Chart(options);\n                }\n              });\n            });\n  </script>\n\n  <!-- 过期/淘汰键变化相关 -->\n  <script type=\"text/javascript\">\n\n\n    /*if(betweenOneDay == 1){*/\n    var containerExpiredKeysUrl = \"${request.contextPath}/admin/app/appInstanceExpiredEvictedKeysStat?appId=\" + appId + chartParams;\n    $(document).ready(\n            function () {\n              var options = getOption(\"containerExpiredEvictedKeys\", \"<b>过期/淘汰键统计<a href='\" + containerExpiredKeysUrl + \"' target='_blank'>(查看实例过期/淘汰键统计)</a></b>\", \"个\");\n              var commandsUrl = \"${request.contextPath}/admin/app/getMutiStatAppStats.json?appId=\" + appId + \"&statName=expiredKeys,evictedKeys\" + chartParams;\n              $.ajax({\n                type: \"get\",\n                url: commandsUrl,\n                async: true,\n                success: function (data) {\n                  var dataObject = eval(\"(\" + data.data + \")\");\n                  var expiredKeysDataArr = dataObject[\"expiredKeys\"];\n                  //1.expiredKeys\n                  var expiredKeysPoints = getKeyPoints(expiredKeysDataArr, \"expired_keys\", 0);\n                  //确认单位\n\n                  options.yAxis.title.text = expiredKeysPoints.unitTxt;\n                  var unit = expiredKeysPoints.unit;\n\n                  options.series.push(expiredKeysPoints);\n\n                  //2.evictedKeys\n                  var evictedKeysDataArr = dataObject[\"evictedKeys\"];\n                  var evictedKeysPoints = getKeyPoints(evictedKeysDataArr, \"evicted_keys\", 0);\n                  //确认单位\n                  options.yAxis.title.text = evictedKeysPoints.unitTxt;\n                  var unit = evictedKeysPoints.unit;\n                  options.series.push(evictedKeysPoints);\n\n                  new Highcharts.Chart(options);\n\n                }\n              });\n            });\n  </script>\n\n  <div id=\"appCodeChangeModal\" class=\"modal fade\" tabindex=\"-1\" aria-labelledby=\"appCodeChangeModalLabel\" aria-hidden=\"true\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\" id=\"appCodeChangeModalLabel\">应用密码</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\" onclick=\"closeModal('appCodeChangeModal')\"></button>\n        </div>\n\n        <div class=\"modal-body\">\n          <div class=\"container-fluid\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"col-md-12\">\n                <div class=\"form-group row\">\n                  <label class=\"col-md-3 col-form-label\">当前应用密码:</label>\n                  <div class=\"col-md-9\">\n                    <input type=\"text\" name=\"appCodeKey\" id=\"appCodeKey\" value=\"${md5password!}\"\n                         class=\"form-control\"/>\n                  </div>\n                </div>\n                <#if acluser??>\n                  <div class=\"form-group row\">\n                    <div class=\"col-md-12\">\n                      <label class=\"col-form-label\"><font color=\"red\" style=\"text-decoration: underline;\">以下为只读用户，仅供查询使用</font></label>\n                    </div>\n                    <label class=\"col-form-label col-md-3\">只读用户:</label>\n                    <div class=\"col-md-9\">\n                      <input type=\"text\" name=\"acluser\" value=\"${acluser!}\"\n                             class=\"form-control\"/>\n                    </div>\n                    <label class=\"col-form-label col-md-3\">只读用户密码:</label>\n                    <div class=\"col-md-9\">\n                      <input type=\"text\" name=\"appCodeKey\" id=\"aclCodeKey\" value=\"${aclpassword!}\"\n                             class=\"form-control\"/>\n                    </div>\n                  </div>\n                </#if>\n\n                <#if userInfo.type == 0>\n                  <#if adminUser??>\n                    <div class=\"form-group row\">\n                      <div class=\"col-md-12\">\n                        <label class=\"col-form-label\"><font color=\"red\" style=\"text-decoration: underline;\">以下为管理员用户，请谨慎使用</font></label>\n                      </div>\n                      <label class=\"col-form-label col-md-3\">管理员:</label>\n                      <div class=\"col-md-9\">\n                        <input type=\"text\" value=\"${adminUser!}\"\n                               class=\"form-control\"/>\n                      </div>\n                      <label class=\"col-form-label col-md-3\">密码:</label>\n                      <div class=\"col-md-9\">\n                        <input type=\"text\" value=\"${adminPassword!}\"\n                               class=\"form-control\"/>\n                      </div>\n                    </div>\n                  </#if>\n                </#if>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\" onclick=\"closeModal('appCodeChangeModal')\">Close</button>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<script>\n  function closeModal(id){\n    let modal = $('#' + id);\n    let bootstrapModal = bootstrap.Modal.getOrCreateInstance(modal);\n    bootstrapModal.hide();\n  }\n\n  // $(function () {\n  //   let modal = $('#appCodeChangeModal');\n  //   let modalButton = document.getElementById('modal-button');\n  //   modalButton.click();\n  //\n  //   setTimeout(function () {\n  //\n  //     let bootstrapModal = new bootstrap.Modal(modal);\n  //     bootstrapModal.toggle()\n  //   }, 3100);\n  //\n  // });\n</script>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/appTopology.html",
    "content": "<script src=\"${request.contextPath}/assets/vendor/clipboard/clipboard.min.js\"></script>\n<div class=\"col-md-12\">\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      应用拓扑结构-<a href=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId}\">${appDesc.name}</a>\n    </h3>\n  </div>\n  <div class=\"table-responsive\">\n    <!-- table begin -->\n    <table class=\"table table-striped table-hover table-bordered\">\n      <thead>\n      <th scope=\"col\">ID</td>\n      <th scope=\"col\">实例</td>\n      <th scope=\"col\">实例状态</td>\n      <th scope=\"col\">内存使用</td>\n      <th scope=\"col\">对象数</td>\n      <th scope=\"col\">连接数</td>\n      <th scope=\"col\">命中率</td>\n      <th scope=\"col\">碎片率</td>\n      <th scope=\"col\">角色</td>\n      <th scope=\"col\">主实例ID</td>\n      </thead>\n      <tbody>\n      <#list instanceList as instance>\n        <#assign instanceStatsMapKey=(instance.ip + \":\" + instance.port)>\n      <#if instanceStatsMap?? && (instanceStatsMap[instanceStatsMapKey])??>\n        <#assign instanceStatsInfo=(instanceStatsMap[instanceStatsMapKey])>\n      <#else>\n        <#assign instanceStatsInfo=''>\n      </#if>\n      <#if (instance.status==1 || instance.status==0)>\n        <tr>\n          <th scope=\"row\">\n            <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.id}\" target=\"_blank\">${instance.id}</a>\n            <#if (instance.masterInstanceId == 0 && instance.status != 2)>\n              <span class=\"bi bi-star-fill\"></span>\n            </#if>\n          </th>\n          <td><a href=\"${request.contextPath}/server/index?ip=${instance.ip}\" target=\"_blank\">${instance.ip}</a>:${instance.port}\n            <#if isAdmin == 1>\n              &nbsp;\n              <button class=\"btn\" data-bs-container=\"body\" data-bs-toggle=\"popover\" data-bs-trigger=\"focus\" data-bs-placement=\"right\" data-bs-content=\"Copied!\" data-clipboard-text=\"redis-cli -h ${instance.ip} -p ${instance.port} -a ${appDesc.appPassword}\">\n                <span class=\"bi bi-link-45deg\"></span>\n              </button>\n            </#if>\n          </td>\n          <td>${instance.statusDesc}</td>\n          <td>\n            <#if instanceStatsInfo?? && (instanceStatsInfo != '') && (instanceStatsInfo.memUsePercent >= 80)>\n              <#assign progressBarStatus='bg-danger'>\n            <#else>\n              <#assign progressBarStatus='bg-success'>\n            </#if>\n            <div class=\"progress progress-fs-1\">\n              <div class=\"progress-bar ${progressBarStatus}\"\n                   role=\"progressbar\"\n                   <#if instanceStatsInfo?? && (instanceStatsInfo != '')>\n                     aria-valuenow=\"${instanceStatsInfo.memUsePercent}\"\n                     aria-valuemax=\"100\"\n                     aria-valuemin=\"0\"\n                     style=\"width: ${instanceStatsInfo.memUsePercent}%; overflow: visible;\"\n                   <#else>\n                      aria-valuenow=\"0\"\n                      aria-valuemax=\"100\"\n                      aria-valuemin=\"0\"\n                      style=\"width: 0%; overflow: visible;\"\n                   </#if>\n                >\n                <span style=\"color: #000000; margin-bottom: 0\">\n                  <#if instanceStatsInfo?? && (instanceStatsInfo != '')>\n                    ${(instanceStatsInfo.usedMemory / 1024 / 1024 / 1024)?string('#.##')}G&nbsp;&nbsp;Used\n                    /${(instanceStatsInfo.maxMemory / 1024 / 1024 / 1024)?string('#.##')}G&nbsp;&nbsp;Total\n                   </#if>\n                </span>\n              </div>\n            </div>\n          </td>\n          <td>\n            <#if instanceStatsInfo?? && (instanceStatsInfo != '')>\n              ${instanceStatsInfo.currItems}\n            </#if>\n          </td>\n          <td>\n            <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.id}&tabTag=instance_clientList\" target=\"_blank\">\n              <#if instanceStatsInfo?? && (instanceStatsInfo != '')>\n                ${instanceStatsInfo.currConnections}\n              </#if>\n            </a>\n          </td>\n          <td>\n            <#if instanceStatsInfo?? && (instanceStatsInfo != '')>\n              ${instanceStatsInfo.hitPercent}\n            </#if>\n          </td>\n          <td>\n            <#if instanceStatsInfo?? && (instanceStatsInfo != '')>\n              <#assign memFragmentationRatio=instanceStatsInfo.memFragmentationRatio>\n            <#else>\n              <#assign memFragmentationRatio=0>\n            </#if>\n            <#if (memFragmentationRatio > 5) && instanceStatsInfo?? && (instanceStatsInfo != '')  && instanceStatsInfo.usedMemory?? && (instanceStatsInfo.usedMemory > 1024 * 1024 * 100)>\n              <#assign memFragmentationRatioLabel='bg-danger'>\n            <#elseif (memFragmentationRatio >= 3) && (memFragmentationRatio < 5) && instanceStatsInfo?? && (instanceStatsInfo != '') && instanceStatsInfo.usedMemory?? && (instanceStatsInfo.usedMemory > 1024 * 1024 * 100)>\n              <#assign memFragmentationRatioLabel='bg-warning'>\n            <#else>\n              <#assign memFragmentationRatioLabel='bg-success'>\n            </#if>\n            <label class=\"${memFragmentationRatioLabel} rounded\">&nbsp;${memFragmentationRatio}&nbsp;</label>\n          </td>\n          <#if isAdmin == 1>\n            <td><a target=\"_blank\" href=\"${request.contextPath}/manage/instance/log?instanceId=${instance.id}&pageSize=1000\">${instance.roleDesc!}</a></td>\n          <#else>\n            <td>${instance.roleDesc}</td>\n          </#if>\n          <#if (instance.masterInstanceId >0)>\n            <td>\n              <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.masterInstanceId}\" target=\"_blank\">${instance.masterInstanceId!}</a>\n            </td>\n          <#else>\n              <td></td>\n          </#if>\n        </tr>\n        </#if>\n      </#list>\n      </tbody>\n    </table>\n    <!-- End Table with stripped rows -->\n  </div>\n</div>\n<script type=\"text/javascript\">\n  $(function () {\n    var clipboard = new ClipboardJS('.btn');\n    const popoverTriggerList = document.querySelectorAll('[data-bs-toggle=\"popover\"]')\n    const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))\n  });\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appAlertConfig.html",
    "content": "<script type=\"text/javascript\">\n  function appAlertConfigChange() {\n    var appId = $('#appAlterConfig_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appAlterConfig_appId').focus();\n      return false;\n    }\n\n    var memAlertValue = document.getElementById(\"memAlertValue\");\n    if (memAlertValue.value == \"\") {\n      alert(\"空间报警阀值不能为空\");\n      memAlertValue.focus();\n      return false;\n    }\n    var clientConnAlertValue = document.getElementById(\"clientConnAlertValue\");\n    if (clientConnAlertValue.value == \"\") {\n      alert(\"客户端连接数报警阀值不能为空\");\n      clientConnAlertValue.focus();\n      return false;\n    }\n    var hitPrecentAlertValue = document.getElementById(\"hitPrecentAlertValue\");\n    if (hitPrecentAlertValue.value == \"\") {\n      alert(\"应用平均命中率报警阀值不能为空\");\n      hitPrecentAlertValue.focus();\n      return false;\n    }\n    var isAccessMonitor = jQuery(\"#isAccessMonitor option:selected\");\n    if (isAccessMonitor.attr(\"value\") == \"\") {\n      alert(\"应用全局报警不能为空\");\n      isAccessMonitor.focus();\n      return false;\n    }\n    if(!confirm(\"确认提交报警修改申请？\")){\n      return ;\n    }\n    var btn = document.getElementById(\"appAlterConfigChangeBtn\");\n    btn.disabled = true;\n    $.post(\n            '${request.contextPath}/admin/app/changeAppAlertConfig',\n            {\n              appId: appId,\n              memAlertValue: memAlertValue.value,\n              clientConnAlertValue: clientConnAlertValue.value,\n              hitPrecentAlertValue: hitPrecentAlertValue.value,\n              isAccessMonitor: isAccessMonitor.attr(\"value\")\n            },\n            function (data) {\n              if (data == 1) {\n                $(\"#appAlterConfigChangeInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>配置修改成功，已生效！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1500);\n              } else {\n                btn.disabled = false;\n                $(\"#appAlterConfigChangeInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n  function changeAppId(appId) {\n    $('#memAlertValue').val('');\n    $('#clientConnAlertValue').val('');\n    $('#hitPrecentAlertValue').val('');\n    $.post('${request.contextPath}/admin/app/appDesc',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                var appDesc = data.appDesc;\n                $('#memAlertValue').val(appDesc.memAlertValue);\n                $('#clientConnAlertValue').val(appDesc.clientConnAlertValue);\n                $('#hitPrecentAlertValue').val(appDesc.hitPrecentAlertValue);\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    修改报警\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-7\">\n          <select id=\"appAlterConfig_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\"\n                  onchange=\"changeAppId(this.value)\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n                <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\" <#if (appId?? && appId == appDesc.appId)>selected=\"selected\"</#if>>\n                  【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">空间报警阀值<font color='red'>(*)</font></label>\n        <div class=\"col-md-7\">\n          <input type=\"text\" name=\"memAlertValue\" <#if appId??>value=\"${appDescMap?api.get(appId).memAlertValue}\"</#if>\n                  id=\"memAlertValue\" placeholder=\"空间报警阀值\"\n                 class=\"form-control\" onchange=\"testisNum(this.id)\">\n          <span class=\"help-block\">如果空间使用率超过90%报警，请填90<font color=\"red\">（不需要报警请填100以上)</font></span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">客户端连接数报警阀值<font color='red'>(*)</font></label>\n        <div class=\"col-md-7\">\n          <input type=\"text\" name=\"clientConnAlertValue\" <#if appId??>value=\"${appDescMap?api.get(appId).clientConnAlertValue}\"</#if>\n                 id=\"clientConnAlertValue\" placeholder=\"客户端连接数报警阀值\" class=\"form-control\"\n                 onchange=\"testisNum(this.id)\">\n          <span class=\"help-block\">如果客户端连接数超过2000报警，请填2000</span>\n        </div>\n      </div>\n\n      <div class=\"row mb-2\" id=\"instance-div\" style=\"display:none;\">\n        <label class=\"col-form-label col-md-3\">>应用平均命中率报警阀值<font color='red'>(*)</font></label>\n        <div class=\"col-md-7\">\n          <input type=\"text\" name=\"hitPrecentAlertValue\" <#if appId??>value=\"${appDescMap?api.get(appId).hitPrecentAlertValue}\"</#if>\n                 id=\"hitPrecentAlertValue\" placeholder=\"平均命中率报警阀值\" class=\"form-control\"\n                 onchange=\"testisNum(this.id)\">\n          <span class=\"help-block\">如果应用平均命中率低于80%报警，请填80</span>\n         </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">开启应用全局报警<font color='red'>(*)</font></label>\n        <div class=\"col-md-7\">\n          <select id=\"isAccessMonitor\" name=\"isAccessMonitor\" class=\"form-select\">\n            <option value=\"0\"\n            <#if (appDetail?? && (!(appDetail.appDesc.isAccessMonitor??) || appDetail.appDesc.isAccessMonitor == 0))>selected</#if>>\n            否\n            </option>\n            <option value=\"1\"\n            <#if (appDetail?? && appDetail.appDesc.isAccessMonitor?? && appDetail.appDesc.isAccessMonitor == 1)>selected</#if>>\n            是\n            </option>\n          </select>\n          <span class=\"help-block\">是否接收全局报警邮件</span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appAlterConfigChangeBtn\" class=\"btn btn-danger\" onclick=\"appAlertConfigChange()\">\n            <i class=\"bi bi-check\"></i>\n            提交申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div id=\"appAlterConfigChangeInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n\n    </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appAlterConfigIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n  <script src=\"${request.contextPath}/assets/js/custom/getInstancesByAppId.js\"></script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appAlertConfig.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appAlertTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appCleanIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n  <script src=\"${request.contextPath}/assets/js/custom/getInstancesByAppId.js\"></script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appDel.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appCleanTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appConfig.html",
    "content": "<script type=\"text/javascript\">\n  function changeAppIdSelect(appId, instance_select) {\n    document.getElementById(instance_select).options.length = 0;\n    document.getElementById('appConfigKey').options.length = 0;\n    $('#appConfigKey').selectpicker('refresh');\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>\" + \"所有实例\" + \"</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].id;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）';\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n  function changeInstanceSelect(instanceId, appConfigKey) {\n    console.log(instanceId);\n\n    var appId = $('#appConfig_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appConfig_appId').focus();\n      return false;\n    }\n\n    document.getElementById(appConfigKey).options.length = 0;\n    $('#' + appConfigKey).selectpicker('destroy');\n    $('#' + appConfigKey).selectpicker();\n\n    $.post('${request.contextPath}/admin/app/redisConfig',\n            {\n              appId: appId,\n              instanceId:  instanceId\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + appConfigKey).selectpicker('destroy');\n                var redisConfigMap = data.redisConfigMap;\n                for(var key in redisConfigMap){\n                  var item= '配置项：'+key+\"  配置值：\"+redisConfigMap[key];\n                  $('#' + appConfigKey).append(\"<option value='\" + key + \"'>\" + item + \"</option>\");\n                }\n                $('#' + appConfigKey).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n  function appConfigChange() {\n    var appId = $('#appConfig_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appConfig_appId').focus();\n      return false;\n    }\n    var instanceId = $('#instance-select').selectpicker('val');\n    console.log(\"instanceId:\" + instanceId);\n    if(instanceId == null){\n      alert(\"请选择实例\");\n      $('#instance-select').focus();\n      return false;\n    }\n\n    var appConfigKey = document.getElementById(\"appConfigKey\");\n    if (appConfigKey.value == \"\") {\n      alert(\"配置项不能为空\");\n      appConfigKey.focus();\n      return false;\n    }\n\n    var appConfigValue = document.getElementById(\"appConfigValue\");\n    if (appConfigValue.value == \"\") {\n      alert(\"配置值不能为空\");\n      appConfigValue.focus();\n      return false;\n    }\n\n    var appConfigReason = document.getElementById(\"appConfigReason\");\n    if (appConfigReason.value == \"\") {\n      alert(\"配置原因不能为空\");\n      appConfigReason.focus();\n      return false;\n    }\n\n    if(!confirm(\"确认提交应用配置修改？\")){\n      return ;\n    }\n\n    var appConfigChangeBtn = document.getElementById(\"appConfigChangeBtn\");\n    appConfigChangeBtn.disabled = true;\n\n    var url;\n    var data;\n    if (instanceId == null || instanceId == '') {\n      url = '${request.contextPath}/admin/app/changeAppConfig';\n      data = {\n        appId: appId,\n        instanceId: instanceId,\n        appConfigKey: appConfigKey.value,\n        appConfigValue: appConfigValue.value,\n        appConfigReason: appConfigReason.value\n      };\n    } else {\n      url = '${request.contextPath}/admin/app/changeInstanceConfig';\n      data = {\n        appId: appId,\n        instanceId: instanceId,\n        instanceConfigKey: appConfigKey.value,\n        instanceConfigValue: appConfigValue.value,\n        instanceConfigReason: appConfigReason.value\n      };\n    }\n\n    $.post(\n            url,\n            data,\n            function (data) {\n              if (data == 1) {\n                $(\"#appConfigChangeInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>配置修改申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1000);\n              } else {\n                appConfigChangeBtn.disabled = false;\n                $(\"#appConfigChangeInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    修改应用配置\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"appConfig_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\"\n                  onchange=\"changeAppIdSelect(this.value,'instance-select')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n                <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\" <#if (appId?? && appId == appDesc.appId)>selected=\"selected\"</#if>>\n                    【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">实例</label>\n        <div class=\"col-md-5\">\n          <select id=\"instance-select\" name=\"nodes\"\n                  class=\"selectpicker col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"请选择实例 \"data-size=\"8\"\n                  onchange=\"changeInstanceSelect(this.value,'appConfigKey')\">\n            <#if appInstanceList??>\n              <#list appInstanceList as instanceInfo>\n                <option value=\"${instanceInfo.id}\" <#if (instanceId?? && instanceId == instanceInfo.id)>selected</#if>>\n                ${instanceInfo.hostPort}（角色：${instanceInfo.roleDesc})\n                </option>\n              </#list>\n            </#if>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">配置项<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"appConfigKey\" name=\"appConfigKey\"\n                  class=\"selectpicker col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择配置项\" data-size=\"8\">\n            <#list redisConfigMap?keys as key>\n              <option value=\"${appDesc.appId}\">\n                配置项：${key} 配置值：+${redisConfigMap[key]};\n              </option>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">新配置值<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <input type=\"text\" name=\"appConfigValue\" id=\"appConfigValue\"\n                 placeholder=\"填写新的配置值\" class=\"form-control\">\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">修改原因<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n              <textarea name=\"appConfigReason\" id=\"appConfigReason\" placeholder=\"如：1.需要更多的连接数。\"\n                        class=\"form-control\"></textarea>\n        </div>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appConfigChangeBtn\" class=\"btn btn-danger\" onclick=\"appConfigChange()\">\n            <i class=\"bi bi-check\"></i>\n            提交申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div id=\"appConfigInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n    </div>\n    <div id=\"appConfigChangeInfo\"></div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appConfigIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appConfig.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appConfigTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appDataMigrate.html",
    "content": "<script type=\"text/javascript\">\n  function appDataMigrateApply() {\n\n    var source = $('#sourceDataType option:selected').val();\n    var source_appId = $('#source_appId').selectpicker('val');\n    if(source == 1){\n      if ( source_appId == null || source_appId == '') {\n        alert(\"请选择源应用\");\n        $('#source_appId').focus();\n        return false;\n      }\n    }\n\n    var othersource_info = document.getElementById(\"othersource_info\");\n    if (source ==0){\n      if(othersource_info.value == \"\") {\n        alert(\"请填入实例信息\");\n        $('#othersource_info').focus();\n        return false;\n      }\n    }\n\n    var target_appId = $('#target_appId').selectpicker('val');\n    if (target_appId == null || target_appId == '') {\n      alert(\"请选择目标应用\");\n      $('#target_appId').focus();\n      return false;\n    }\n\n\n\n    var reason = document.getElementById(\"appOfflineReason\");\n    if (reason.value == \"\") {\n      alert(\"请填写原因!\");\n      reason.focus();\n      return false;\n    }\n\n    if (!confirm(\"确认提交应用数据迁移申请？\")) {\n      return;\n    }\n\n    var btn = document.getElementById(\"appDataMigrateBtn\");\n    btn.disabled = true;\n\n    $.post(\n            '${request.contextPath}/admin/app/job/submit',\n            {\n              jobType: 11,\n              appId: source_appId,\n              reason: reason.value+othersource_info.value,\n              param: '源应用：' + source_appId + ' 迁移到目标应用：' + target_appId\n            },\n            function (data) {\n              if (data == 1) {\n                $(\"#appMigrateInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>应用数据迁移申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1000);\n              } else {\n                btn.disabled = false;\n                $(\"#appMigrateInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n\n  function changeDataType( choose) {\n    var dataType = choose.options[choose.selectedIndex].value;\n    if (dataType == 1) {\n      $(\"#cc-source\").attr(\"style\",\"display:display\");\n      $(\"#other-source\").attr(\"style\",\"display:none\");\n    } else if (dataType == 0) {\n      $(\"#cc-source\").attr(\"style\",\"display:none\");\n      $(\"#other-source\").attr(\"style\",\"display:display\");\n    }\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title border-bottom\">\n    应用数据迁移\n  </h4>\n</div>\n\n<div class=\"card-body\">\n  <div class=\"row\">\n    <div class=\"form-group row\">\n      <label class=\"col-form-label col-md-2 offset-md-1\">来源:</label>\n      <div class=\"col-md-5\">\n        <select id=\"sourceDataType\" name=\"sourceDataType\"\n                class=\"form-select\"\n                onchange=\"changeDataType(this)\">\n          <option value=\"1\" selected=\"selected\">\n            cachecloud\n          </option>\n          <option value=\"0\" <#if (sourceDataType?? && sourceDataType==0)>selected=\"selected\"</#if> >\n          非cachecloud\n          </option>\n        </select>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <label class=\"col-form-label col-md-2 offset-md-1\">源应用<font color='red'>(*)</font></label>\n    <div class=\"col-md-5\">\n      <select id=\"source_appId\" name=\"appId\" class=\"selectpicker col-md-12 border rounded\"\n              data-live-search=\"true\" title=\"选择应用\">\n        <option value=\"\">选择应用</option>\n        <#list appDescMap?keys as key>\n          <#assign appDesc = appDescMap?api.get(key)>\n          <#if (appDesc.status==2)>\n            <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\">\n              【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n            </option>\n          </#if>\n        </#list>\n      </select>\n    </div>\n  </div>\n\n  <!-- 其他来源 -->\n  <div class=\"form-group row\" id=\"other-source\" style=\"display:none;\">\n    <label class=\"col-form-label col-md-2 offset-md-1\">实例信息：<font color='red'>(*)</font></label>\n    <div class=\"col-md-5\">\n     <textarea rows=\"6\" name=\"othersource_info\" id=\"othersource_info\" placeholder=\"类型：集群/主从/单机&#13;&#10;内存大小:?G,比如2G&#13;&#10;密码：*****&#13;&#10;实例信息：masterip1:slaveip1;masterip2:slaveip2&#13;&#10;迁移其他说明:\"\n               class=\"form-control\"></textarea>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <label class=\"col-form-label col-md-2 offset-md-1\">目标应用<font color='red'>(*)</font></label>\n    <div class=\"col-md-5\">\n      <select id=\"target_appId\" name=\"appId\" class=\"selectpicker col-md-12 border rounded\"\n              data-live-search=\"true\" title=\"选择应用\">\n        <option value=\"\">选择应用</option>\n        <#list appDescMap?keys as key>\n          <#assign appDesc = appDescMap?api.get(key)>\n          <#if (appDesc.status==2)>\n          <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\">\n            【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n          </option>\n          </#if>\n        </#list>\n      </select>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <label class=\"col-form-label col-md-2 offset-md-1\">申请原因<font color='red'>(*)</font></label>\n    <div class=\"col-md-5\">\n        <textarea rows=\"5\" name=\"appOfflineReason\" id=\"appOfflineReason\" placeholder=\"申请原因\"\n                  class=\"form-control\"></textarea>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <div class=\"text-center\">\n      <button id=\"appDataMigrateBtn\" class=\"btn btn-info\" onclick=\"appDataMigrateApply()\">\n        <i class=\"bi bi-check\"></i>\n        提交数据迁移申请\n      </button>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <div id=\"appMigrateInfo\" class=\"offset-md-3 col-md-9\"></div>\n  </div>\n\n</div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appDataMigrateIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appDataMigrate.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appDataMigrateTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appDel.html",
    "content": "<script type=\"text/javascript\">\n  $(function () {\n    $(\"input[name='cleanType']\").change(function () {\n      if ($(this).val() == \"0\") {\n        $(\"#instance-div\").hide();\n        $(\"#pattern-div\").hide();\n      } else {\n        $(\"#instance-div\").show();\n        $(\"#pattern-div\").show();\n      }\n    });\n  });\n\n  function appDataCleanApply() {\n    var appId = $('#appDel_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appDel_appId').focus();\n      return false;\n    }\n    var cleanType = $('input[name=\"cleanType\"]:checked').val();\n\n    var nodeInfos = $('#instance-select').selectpicker('val');\n    var pattern = document.getElementById(\"delPattern\");\n    if (cleanType==1){\n      if (pattern.value == \"\") {\n        alert(\"删除数据需要填写键匹配的格式!\");\n        pattern.focus();\n        return false;\n      }\n    }\n    var reason = document.getElementById(\"appDelReason\");\n    if (reason.value == \"\") {\n      alert(\"请填写数据清理原因!\");\n      reason.focus();\n      return false;\n    }\n\n    if (!confirm(\"确认提交数据清理申请？\")) {\n      return;\n    }\n\n    var btn = document.getElementById(\"appDelBtn\");\n    btn.disabled = true;\n\n    var cleanTypeDesc = cleanType == 0 ? \"清理全库\" : \"删除数据\";\n    $.post(\n            '${request.contextPath}/admin/app/job/submit',\n            {\n              jobType: 7,\n              appId: appId,\n              nodeInfos: nodeInfos == null ? \"\" : nodeInfos.toString(),\n              param: '数据清理类型: ' + cleanTypeDesc + '，key格式: ' + pattern.value,\n              reason: reason.value\n            },\n            function (data) {\n              if (data == 1) {\n                $(\"#appDelApplyInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>应用诊断申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1000);\n              } else {\n                btn.disabled = false;\n                $(\"#appDelApplyInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    数据清理\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"appDel_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\"\n                  onchange=\"changeAppIdSelect(this.value,'instance-select', '${request.contextPath}')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n                <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\">\n                  【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">数据清理类型<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <div class=\"form-check form-check-inline\">\n            <label class=\"form-check-label\">\n              <input type=\"radio\" class=\"form-check-input\" name=\"cleanType\" value=\"0\" checked> 清理全库\n            </label>\n          </div>\n          <div class=\"form-check form-check-inline\">\n            <label class=\"form-check-label\">\n              <input type=\"radio\" class=\"form-check-input\" name=\"cleanType\" value=\"1\"> 删除数据\n            </label>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"form-group row\" id=\"instance-div\" style=\"display:none;\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">实例</label>\n        <div class=\"col-md-5\">\n          <select id=\"instance-select\" name=\"nodes\"\n                  class=\"selectpicker col-md-12 border rounded\" multiple\n                  data-live-search=\"true\" title=\"选择实例 (默认全部)\" data-size=\"8\">\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\" id=\"pattern-div\" style=\"display:none;\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">key格式<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n                        <textarea rows=\"5\" class=\"form-control\" name=\"delPattern\" id=\"delPattern\" placeholder=\"键匹配格式，*表示通配符，多个用英文逗号分隔，如：\nabc*,\n*abc,\nab*c\"></textarea>\n          <span class=\"help-block\" style=\"color: green\">\n                            如，清理以abc开头的键：abc*,\n                            清理包含abc的键：*abc*,\n                            清理以abc结尾的键：*abc\n                        </span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">申请原因<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n                        <textarea rows=\"3\" name=\"appDelReason\" id=\"appDelReason\" placeholder=\"申请数据清理原因\"\n                                  class=\"form-control\"></textarea>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appDelBtn\" class=\"btn btn-danger\" onclick=\"appDataCleanApply()\">\n            <i class=\"bi bi-check\"></i>\n            提交申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div id=\"appDelApplyInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n\n    </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appDiagnostic.html",
    "content": "<script type=\"text/javascript\">\n  function appDiagnosticApply() {\n    var appId = $('#appDiagnostic_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appDiagnostic_appId').focus();\n      return false;\n    }\n\n    var type = document.getElementById(\"diagnostic_type\");\n    if (type.value == \"\") {\n      alert(\"请选择诊断类型!\");\n      type.focus();\n      return false;\n    }\n\n    var nodeInfos = $('#instance-select').selectpicker('val');\n\n    var reason = document.getElementById(\"appDiagnosticReason\");\n    if (reason.value == \"\") {\n      alert(\"请填写原因!\");\n      reason.focus();\n      return false;\n    }\n    if(!confirm(\"确认提交应用诊断申请？\")){\n      return ;\n    }\n\n    var btn = document.getElementById(\"appDiagnosticBtn\");\n    btn.disabled = true;\n\n    $.post(\n            '${request.contextPath}/admin/app/job/submit',\n            {\n              jobType: 8,\n              appId: appId,\n              param: type.value,\n              nodeInfos: nodeInfos == null ? \"\" : nodeInfos.toString(),\n              reason: reason.value\n            },\n            function (data) {\n              if (data == 1) {\n                $(\"#appDiagnosticInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>应用诊断申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1000);\n              } else {\n                btn.disabled = false;\n                $(\"#appDiagnosticInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    诊断应用\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"appDiagnostic_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\"\n                  onchange=\"changeAppIdSelect(this.value,'instance-select', '${request.contextPath}')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n                <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\">\n                  【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">实例</label>\n        <div class=\"col-md-5\">\n          <select id=\"instance-select\" name=\"nodes\"\n                  class=\"selectpicker col-md-12 border rounded\" multiple\n                  data-live-search=\"true\" title=\"选择实例 (默认全部)\" data-size=\"8\">\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">诊断类型</label>\n        <div class=\"col-md-5\">\n          <select id=\"diagnostic_type\" name=\"type\" class=\"form-select\">\n            <#list diagnosticTypeMap?keys as key>\n              <option value=\"${diagnosticTypeMap?api.get(key)}\">\n                ${diagnosticTypeMap?api.get(key)}\n              </option>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">申请原因/描述<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n              <textarea rows=\"6\" name=\"appDiagnosticReason\" id=\"appDiagnosticReason\"\n                        placeholder=\"scan：扫描匹配的键，如abc*&#13;&#10;memoryUsed：扫描大内存键，如内存大于1MB&#13;&#10;idlekey：扫描空闲时间长的键，如7天&#13;&#10;hotkey：扫描热点键&#13;&#10;deletekey：删除匹配的键，如abc*&#13;&#10;slotAnalysis：分析集群槽的键分布&#13;&#10;sampleCompare：数据抽样比对&#13;&#10;realTimeMonitor：实时监控\"\n                        class=\"form-control\"></textarea>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appDiagnosticBtn\" class=\"btn btn-info\" onclick=\"appDiagnosticApply()\">\n            <i class=\"bi bi-check\"></i>\n            提交申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div id=\"appDiagnosticInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n\n    </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appDiagnosticIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n  <script src=\"${request.contextPath}/assets/js/custom/getInstancesByAppId.js\"></script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appDiagnostic.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appDiagnosticTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appImport.html",
    "content": "<script type=\"text/javascript\">\n  function checkAppImportFormat() {\n    var res = saveAppDesc();\n    if (res == false) {\n      return\n    }\n    var password = document.getElementById(\"password\");\n    var type = document.getElementById(\"type\")\n    $.post(\n            '${request.contextPath}/import/app/check.json',\n            {\n              type: type.value,\n              password: password.value,\n              appInstanceInfo: appInstanceInfo.value\n            },\n            function (data) {\n              var status = data.status;\n              alert(data.message);\n              if (status == 1) {\n                var submitButton = document.getElementById(\"submitButton\");\n                submitButton.disabled = false;\n\n                var checkButton = document.getElementById(\"checkButton\");\n                checkButton.disabled = true;\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h3 class=\"card-title\">\n      应用导入申请\n      <font color='red' size=\"4\">\n        <#if success?? && (success == 1)>(更新成功)</#if>\n      </font>\n    </h3>\n</div>\n\n<div class=\"card-body\">\n  <form action=\"${request.contextPath}/admin/app/import/submit\" method=\"post\"\n        class=\"form-horizontal\">\n    <!-- 源信息 -->\n    <div class=\"card-header\">\n      <h4 class=\"card-title\">\n        源：Redis实例信息\n      </h4>\n    </div>\n    <div class=\"row\">\n      <div class=\"form-group row mt-3\">\n        <label class=\"col-form-label col-md-2\">\n          存储类型:\n        </label>\n        <div class=\"col-md-5\">\n          <select id=\"sourceType\" name=\"sourceType\" class=\"form-select\">\n            <option value=\"5\">\n              Redis-Standalone\n            </option>\n            <option value=\"6\">\n              Redis-Sentinel\n            </option>\n            <option value=\"7\" selected=\"selected\">\n              Redis-Cluster\n            </option>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row flex-sm-nowrap\">\n        <label class=\"col-form-label col-md-2\">\n          实例详情:<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-5\">\n                                    <textarea rows=\"13\" name=\"appInstanceInfo\" id=\"appInstanceInfo\"\n                                              placeholder=\"节点详情，样例：&#10;1. standalone类型：&#10;127.0.0.1:6379&#10;2. sentinel类型：&#10;mymastername:master&#10;127.0.0.1:6379&#10;127.0.0.1:6380&#10;127.0.0.1:6381&#10;3. cluster类型：&#10;127.0.0.1:6379&#10;127.0.0.1:6380&#10;127.0.0.1:6381\n                                        \" class=\"form-control\"></textarea>\n\n        </div>\n        <div class=\"col-md-5 d-flex justify-content-center\">\n                                    <span class=\"help-block\">\n                                        <strong>实例格式说明</strong><br/>\n                                        每行格式都是:&nbsp;&nbsp;ip:port(例如：10.10.xx.xx:6379)<br/>\n                                        1. standalone类型：<span style=\"color: #00BE67\" title=\"standalone模式下，需要填写单个redis节点的地址\" class=\"bi bi-question-circle-fill\"></span><br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp:masterPort<br/>\n                                        2. sentinel类型：<span style=\"color: #00BE67\" title=\"sentinel模式下，需要填写sentinel_master_name:master_or_slave&#10;和多个sentinel_cluster_address。sentinel_master_name表示sentinel配置下master的名字，master_or_slave表示从sentinel中选择的redis是master还是slave，sentinel_cluster_address表示sentinel的单节点或者集群地址。例如：&#10;mymaster:master&#10;127.0.0.1:26379&#10;127.0.0.1:26380\" class=\"bi bi-question-circle-fill\"></span><br/>\n                                        &nbsp;&nbsp;&nbsp;master_name:master<br/>\n                                        &nbsp;&nbsp;&nbsp;sentinelIp1:sentinelPort1<br/>\n                                        &nbsp;&nbsp;&nbsp;sentinelIp2:sentinelPort2<br/>\n                                        &nbsp;&nbsp;&nbsp;sentinelIp3:sentinelPort3<br/>\n                                        3. cluster类型：<span style=\"color: #00BE67\" title=\"cluster模式下，需要填写集群地址\" class=\"bi bi-question-circle-fill\"></span><br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp1:masterPort1<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp2:masterPort2<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp3:masterPort3<br/>\n                                    </span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2\">\n          redis密码:\n        </label>\n        <div class=\"col-md-5\">\n          <input type=\"text\" name=\"password\" id=\"password\" placeholder=\"redis密码\"\n                 class=\"form-control\"/>\n          <span class=\"help-block\">\n                                        redis密码，如果没有则为空\n                                    </span>\n        </div>\n      </div>\n    </div>\n\n    <!-- 目标信息 -->\n    <div class=\"card-header\">\n      <h4 class=\"card-title\">\n        目标：应用信息\n      </h4>\n    </div>\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          应用名称<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-5\">\n          <input type=\"text\" name=\"name\" id=\"appName\"\n                <#noparse>placeholder=\"${服务名}-${机房:js/tc}-${环境:online/test}\"</#noparse>\n                 class=\"form-control\" onchange=\"checkAppNameExist('${request.contextPath}')\"/>\n          <span class=\"help-block\">\n                                            如：cachecloud-js-online，全局唯一\n                                        </span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          应用描述<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-5\">\n                                        <textarea class=\"form-control\" name=\"intro\"\n                                                  rows=\"3\" id=\"appIntro\" placeholder=\"应用描述\"></textarea>\n          <span class=\"help-block\">\n                                            不超过128个字符，可以包含中文\n                                        </span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          项目负责人<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-5\">\n          <select id=\"officer\" name=\"officer\"\n                  class=\"selectpicker col-md-12 border rounded\" multiple\n                  data-live-search=\"true\" title=\"请选择\">\n            <#list userList as user>\n              <option data-icon=\"bi bi-person-fill\"\n                      value=\"${user.id}\">${user.chName}【${user.email}】\n              </option>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          存储种类:\n        </label>\n        <div class=\"col-md-2\">\n          <select id=\"type\" name=\"type\" class=\"form-select\">\n            <option value=\"2\">\n              Redis-Cluster\n            </option>\n            <option value=\"5\">\n              Redis-Sentinel\n            </option>\n            <option value=\"6\">\n              Redis-Standalone\n            </option>\n          </select>\n        </div>\n\n        <label class=\"col-form-label col-md-2\">\n          内存总量<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-2\">\n          <input type=\"text\" name=\"memSize\" id=\"memSize\" placeholder=\"填整数,单位G\"\n                 class=\"form-control\"/>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          Redis部署版本:\n        </label>\n        <div class=\"col-md-2\">\n          <select id=\"versionId\" name=\"versionId\" class=\"form-select\">\n            <option value=\"-1\">自定义</option>\n            <#list versionList as version>\n              <option value=\"${version.id}\">${version.name}</option>\n            </#list>\n          </select>\n        </div>\n        <label class=\"col-form-label col-md-2\">\n          其他:\n        </label>\n        <div class=\"col-md-2\">\n          <input type=\"text\" id=\"versionName\" name=\"versionName\"\n                 placeholder=\"格式：redis-x.x.x\" class=\"form-control\"/>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          测试:\n        </label>\n        <div class=\"col-md-2\">\n          <select id=\"isTest\" name=\"isTest\" class=\"form-select\">\n            <option value=\"0\">\n              否\n            </option>\n            <option value=\"1\">\n              是\n            </option>\n          </select>\n        </div>\n        <label class=\"col-form-label col-md-2\">\n          是否有数据备份:\n        </label>\n        <div class=\"col-md-2\">\n          <select id=\"hasBackStore\" name=\"hasBackStore\" class=\"form-select\">\n            <option value=\"1\">\n              是\n            </option>\n            <option value=\"0\">\n              否\n            </option>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          是否需要持久化:\n        </label>\n        <div class=\"col-md-2\">\n          <select id=\"needPersistence\" name=\"needPersistence\" class=\"form-select\">\n            <option value=\"1\">\n              是\n            </option>\n            <option value=\"0\">\n              否\n            </option>\n          </select>\n        </div>\n        <label class=\"col-form-label col-md-2\">\n          是否需要slave:\n        </label>\n        <div class=\"col-md-2\">\n          <select id=\"needHotBackUp\" name=\"needHotBackUp\" class=\"form-select\">\n            <option value=\"1\">\n              是\n            </option>\n            <option value=\"0\">\n              否\n            </option>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          预估QPS<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-2\">\n          <input type=\"text\" name=\"forecaseQps\" id=\"forecaseQps\" value=\"800\"\n                 class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n        </div>\n        <label class=\"col-form-label col-md-2\">\n          预估条目数量:<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-2\">\n          <input type=\"text\" name=\"forecastObjNum\" id=\"forecastObjNum\" value=\"100000\"\n                 class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          客户端机房:<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-5\">\n          <select name=\"clientMachineRoom\" id=\"clientMachineRoom\"\n                  class=\"form-select\">\n            <#list roomList as room>\n              <option value=\"${room.name}\">${room.name} (${room.ipNetwork})</option>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          内存报警阀值<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-5\">\n          <input type=\"text\" name=\"memAlertValue\" id=\"memAlertValue\" value=\"90\"\n                 class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n          <span class=\"help-block\">\n                                            例如：内存使用率超过90%报警，请填写90（<font color=\"red\">大于100以上则不报警</font>）\n                                        </span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-3\">\n          客户端连接数报警阀值<font color='red'>(*)</font>:\n        </label>\n        <div class=\"col-md-5\">\n          <input type=\"text\" name=\"clientConnAlertValue\" id=\"clientConnAlertValue\"\n                 value=\"2000\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n          <span class=\"help-block\">\n                                            例如：如客户端连接数超过2000报警，填写2000\n                                        </span>\n        </div>\n      </div>\n    </div>\n\n    <!-- 提交按钮 -->\n    <div class=\"row\">\n      <input name=\"userId\" id=\"userId\" value=\"${userInfo.id}\" type=\"hidden\"/>\n      <input id=\"appExist\" value=\"0\" type=\"hidden\"/>\n\n      <div class=\"d-flex justify-content-center\">\n        <button id=\"checkButton\" type=\"button\" class=\"btn btn-info\"\n                onclick=\"checkAppImportFormat()\">\n          <i class=\"bi bi-check\"></i>\n          检查格式\n        </button>\n        &nbsp;&nbsp;&nbsp;&nbsp;\n        <button id=\"submitButton\" type=\"submit\" class=\"btn btn-info\"\n                disabled=\"disabled\">\n          <i class=\"bi bi-check\"></i>\n          提交申请\n        </button>\n      </div>\n    </div>\n  </form>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appImportIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Vendor CSS Files -->\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appImport.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appImportTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appInit.html",
    "content": "<script>\n  var html = \"\";\n  $(function (){\n      html += \"<select id='versionId' name='versionId' class='form-select'>\";\n      <#if versionList?? && (versionList?size > 0)>\n          <#list versionList as version>\n            html += \"<option value='${version.id}'>${version.name}</option>\";\n          </#list>\n      </#if>\n      html += \"</select>\";\n  });\n\n  function changeType(){\n    var type = $(\"#type\").val();\n    $(\"#versionDivId\").html(html);\n    $(\"#maxmemoryPolicyDiv\").attr(\"style\",\"display:display\");\n  }\n</script>\n\n<div class=\"card-header\">\n    <h3 class=\"card-title\">\n      申请应用\n      <font color='red' size=\"4\">\n        <#if success?? && (success == 1)>(更新成功)</#if>\n      </font>\n    </h3>\n</div>\n\n<div class=\"card-body\">\n    <!-- BEGIN FORM-->\n    <form action=\"${request.contextPath}/admin/app/add\" method=\"post\"\n          class=\"form-horizontal\" onsubmit=\"return saveAppDesc()\">\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            应用名称<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <input type=\"text\" name=\"name\" id=\"appName\" <#noparse>placeholder=\"${服务名}-${机房:js/tc}-${环境:online/test}\"</#noparse>\n                   class=\"form-control\" onchange=\"checkAppNameExist('${request.contextPath}')\"/>\n            <span class=\"help-block\">\n                                          如：cachecloud-js-online，全局唯一\n                                      </span>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            应用描述<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n                                      <textarea class=\"form-control\" name=\"intro\"\n                                                rows=\"3\" id=\"appIntro\" placeholder=\"应用描述\"></textarea>\n            <span class=\"help-block\">\n                                          不超过128个字符，可以包含中文\n                                      </span>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            存储种类:\n          </label>\n          <div class=\"col-md-5\">\n            <select id=\"type\" name=\"type\" class=\"form-select\" onchange=\"changeType()\">\n              <option value=\"2\">\n                Redis-Cluster\n              </option>\n              <option value=\"5\">\n                Redis-Sentinel\n              </option>\n              <option value=\"6\">\n                Redis-Standalone\n              </option>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            空间总量<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <input type=\"text\" name=\"memSize\" id=\"memSize\" placeholder=\"如：1/2/4/.../32\" class=\"form-control\"/>\n            <span class=\"help-block\">\n                填写整数，单位GB\n            </span>\n          </div>\n\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            项目负责人<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <select id=\"officer\" name=\"officer\" class=\"selectpicker col-md-12 border rounded\" multiple data-live-search=\"true\" title=\"请选择\">\n              <#list userList as user>\n                <option data-icon=\"bi bi-person-fill\" value=\"${user.id}\">${user.chName}【${user.email}】</option>\n              </#list>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            部署版本:\n          </label>\n          <div class=\"col-md-5\" id=\"versionDivId\">\n            <select id=\"versionId\" name=\"versionId\" class=\"form-select\">\n              <#list versionList as version>\n                <option value=\"${version.id}\">${version.name}</option>\n              </#list>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            测试:\n          </label>\n          <div class=\"col-md-5\">\n            <select id=\"isTest\" name=\"isTest\" class=\"form-select\">\n              <option value=\"0\">\n                否\n              </option>\n              <option value=\"1\">\n                是\n              </option>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            是否有数据备份:\n          </label>\n          <div class=\"col-md-5\">\n            <select id=\"hasBackStore\" name=\"hasBackStore\" class=\"form-select\">\n              <option value=\"1\">\n                是\n              </option>\n              <option value=\"0\">\n                否\n              </option>\n            </select>\n            <span class=\"help-block\">\n                                          即是否用作数据缓存，有其他数据备份策略\n                                      </span>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            是否需要持久化:\n          </label>\n          <div class=\"col-md-5\">\n            <select id=\"needPersistence\" name=\"needPersistence\" class=\"form-select\">\n              <option value=\"1\">\n                是\n              </option>\n              <option value=\"0\">\n                否\n              </option>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            是否需要slave节点:\n          </label>\n          <div class=\"col-md-5\">\n            <select id=\"needHotBackUp\" name=\"needHotBackUp\" class=\"form-select\">\n              <option value=\"1\">\n                是\n              </option>\n              <option value=\"0\">\n                否\n              </option>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            预估QPS<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <input type=\"text\" name=\"forecaseQps\" id=\"forecaseQps\" value=\"800\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            <span class=\"help-block\">\n                                          预估QPS，如：800\n                                      </span>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            预估条目数量:<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <input type=\"text\" name=\"forecastObjNum\" id=\"forecastObjNum\" value=\"100000\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            <span class=\"help-block\">\n                                          预估键数量，如：100000\n                                      </span>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            客户端机房:<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <select name=\"clientMachineRoom\" id=\"clientMachineRoom\" class=\"form-select\">\n              <#list roomList as room>\n                <option value=\"${room.name}\">${room.name} (${room.ipNetwork})</option>\n              </#list>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            内存报警阀值<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <input type=\"text\" name=\"memAlertValue\" id=\"memAlertValue\" value=\"90\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            <span class=\"help-block\">\n                                          例如：内存使用率超过90%报警，请填写90（<font color=\"red\">大于100以上则不报警</font>）\n                                      </span>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            客户端连接数报警阀值<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <input type=\"text\" name=\"clientConnAlertValue\" id=\"clientConnAlertValue\" value=\"2000\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            <span class=\"help-block\">\n                                          例如：如客户端连接数超过2000报警，填写2000\n                                      </span>\n          </div>\n        </div>\n\n        <div class=\"form-group row\" id=\"maxmemoryPolicyDiv\">\n          <label class=\"col-form-label col-md-4 text-end\">\n            淘汰策略<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <select name=\"maxmemoryPolicy\" id=\"maxmemoryPolicy\" class=\"form-select\">\n              <#list policyList as policy>\n                <#if (policy.type == 4)>\n                    <option value=\"${policy.type}\" selected>${policy.name()} (${policy.desc})</option>\n                <#else>\n                    <option value=\"${policy.type}\">${policy.name()} (${policy.desc})</option>\n                </#if>\n              </#list>\n            </select>\n          </div>\n        </div>\n\n        <input name=\"userId\" id=\"userId\" value=\"${userInfo.id}\" type=\"hidden\" />\n        <input id=\"appExist\" value=\"0\" type=\"hidden\" />\n\n        <div class=\"form-actions fluid\">\n            <div class=\"text-center\">\n              <button type=\"submit\" class=\"btn btn-info\">\n                <i class=\"bi bi-check\"></i>\n                提交申请\n              </button>\n            </div>\n        </div>\n    </form>\n    <!-- END FORM-->\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appInitIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appInit.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appInitTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appKeyAnalysis.html",
    "content": "<script type=\"text/javascript\">\n  function appKeyAnalysis(){\n    var appId = $('#keyAnalysis_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#keyAnalysis_appId').focus();\n      return false;\n    }\n\n    var reason = document.getElementById(\"appAnalysisReason\");\n    if(reason.value == \"\"){\n      alert(\"请填写键值分析原因!\");\n      reason.focus();\n      return false;\n    }\n    var nodeInfos = $('#instance-select').selectpicker('val');\n\n    if(!confirm(\"确认提交键值分析申请？\")){\n      return ;\n    }\n\n    var btn = document.getElementById(\"appKeyAnalysisBtn\");\n    btn.disabled = true;\n\n    $.post(\n            '${request.contextPath}/admin/app/keyAnalysis',\n            {\n              appId: appId,\n              nodeInfo: nodeInfos == null ? \"\" : nodeInfos.toString(),\n              appAnalysisReason: reason.value\n            },\n            function(data){\n              if (data == 1) {\n                $(\"#appKeyAnalysisInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>键值分析申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1000);\n              } else {\n                btn.disabled = false;\n                $(\"#appKeyAnalysisInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    集群键值分析\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"keyAnalysis_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12  border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\"\n                  onchange=\"changeAppIdSelect(this.value,'instance-select', '${request.contextPath}')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n            <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\" <#if (appId?? && appId == appDesc.appId)>selected</#if>>\n                  【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">实例</label>\n        <div class=\"col-md-5\">\n          <select id=\"instance-select\" name=\"nodes\"\n                  class=\"selectpicker col-md-12 border rounded\" multiple\n                  data-live-search=\"true\" title=\"选择实例 (默认所有主节点)\" data-size=\"8\">\n            <#if appInstanceList??>\n              <#list appInstanceList as instanceInfo>\n                <option value=\"${instanceInfo.hostPort}\" <#if instanceId?? && (instanceId == instanceInfo.id)>selected</#if>>\n                ${instanceInfo.hostPort}（角色：${instanceInfo.roleDesc})\n                </option>\n              </#list>\n            </#if>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">申请原因<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <textarea rows=\"5\"  name=\"appAnalysisReason\" id=\"appAnalysisReason\" placeholder=\"申请原因\" class=\"form-control\"></textarea>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appKeyAnalysisBtn\" class=\"btn btn-info\" onclick=\"appKeyAnalysis()\">\n            <i class=\"bi bi-check\"></i>\n            提交申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div id=\"appKeyAnalysisInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n\n    </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appKeyAnalysisIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n  <script src=\"${request.contextPath}/assets/js/custom/getInstancesByAppId.js\"></script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appKeyAnalysis.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appKeyAnslysisTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appOffline.html",
    "content": "<script type=\"text/javascript\">\n  function appOfflineApply(){\n    var appId = $('#appOffline_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appOffline_appId').focus();\n      return false;\n    }\n\n    var reason = document.getElementById(\"appOfflineReason\");\n    if(reason.value == \"\"){\n      alert(\"请填应用下线原因!\");\n      reason.focus();\n      return false;\n    }\n\n    if(!confirm(\"确认提交应用下线申请？\")){\n      return ;\n    }\n\n    var btn = document.getElementById(\"appOfflineBtn\");\n    btn.disabled = true;\n\n    $.post(\n            '${request.contextPath}/admin/app/job/submit',\n            {\n              jobType: 10,\n              appId: appId,\n              reason: reason.value\n            },\n            function(data){\n              if(data==1){\n                $(\"#appOfflineInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>应用诊断申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\",1000);\n              }else{\n                btn.disabled = false;\n                $(\"#appOfflineInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    应用下线\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"appOffline_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n                <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\">\n                  【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">申请原因<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <textarea rows=\"5\" name=\"appOfflineReason\" id=\"appOfflineReason\" placeholder=\"申请下线原因\" class=\"form-control\"></textarea>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appOfflineBtn\" class=\"btn btn-info\" onclick=\"appOfflineApply()\">\n            <i class=\"bi bi-check\"></i>\n            提交下线申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"input-group row\">\n        <div id=\"appOfflineInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n\n    </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appOfflineIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appOffline.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appOfflineTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appScale.html",
    "content": "<script type=\"text/javascript\">\n  function appScaleApply(){\n    var appId = $('#appScale_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appScale_appId').focus();\n      return false;\n    }\n\n    var applyMemSize = document.getElementById(\"applyMemSize\");\n    if(applyMemSize.value == \"\"){\n      alert(\"请填写要扩容的容量!\");\n      applyMemSize.focus();\n      return false;\n    }\n\n    var appScaleReason = document.getElementById(\"appScaleReason\");\n    if(appScaleReason.value == \"\"){\n      alert(\"请填写申请扩容的原因!\");\n      appScaleReason.focus();\n      return false;\n    }\n    var appScaleApplyBtn = document.getElementById(\"appScaleApplyBtn\");\n    appScaleApplyBtn.disabled = true;\n\n    $.post(\n            '${request.contextPath}/admin/app/scale',\n            {\n              appId: appId,\n              applyMemSize: applyMemSize.value,\n              appScaleReason: appScaleReason.value\n            },\n            function(data){\n              if (data == 1) {\n                $(\"#appScaleApplyInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>容量变更申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1000);\n              } else {\n                btn.disabled = false;\n                $(\"#appScaleApplyInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>更新失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    扩容/缩容\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"appScale_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n                <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\" <#if (appId?? && appId == appDesc.appId)>selected=\"selected\"</#if>>\n                    【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用容量<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <input type=\"text\" name=\"applyMemSize\" id=\"applyMemSize\" value=\"2G\" class=\"form-control\"/>\n          <span class=\"help-block\">请填写变更后的应用总容量</span>\n          <span class=\"help-block\">如：256M,512M,1G..20G</span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">申请原因<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <textarea rows=\"5\" name=\"appScaleReason\" id=\"appScaleReason\" placeholder=\"申请扩容原因\" class=\"form-control\"></textarea>\n        </div>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appScaleApplyBtn\" class=\"btn btn-info\" onclick=\"appScaleApply()\">\n            <i class=\"bi bi-check\"></i>\n            提交申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div id=\"appScaleApplyInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n    </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appScaleIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appScale.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appScaleTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appScanClean.html",
    "content": "<script type=\"text/javascript\">\n  function appDataScanCleanApply() {\n    var appId = $('#appDel_appId').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      $('#appDel_appId').focus();\n      return false;\n    }\n    var isClean = $('input[name=\"isClean\"]:checked').val();\n\n    var nodeInfos = $('#instance-select').selectpicker('val');\n    var condition = document.getElementById(\"condition\");\n    if (condition.value == \"\") {\n      alert(\"需要填写分析清理条件!\");\n      condition.focus();\n      return false;\n    }\n    var reason = document.getElementById(\"appDelReason\");\n    if (reason.value == \"\") {\n      alert(\"请填写分析清理原因!\");\n      reason.focus();\n      return false;\n    }\n\n    if (!confirm(\"确认提交数据分析清理申请？\")) {\n      return;\n    }\n\n    var btn = document.getElementById(\"appDelBtn\");\n    btn.disabled = true;\n\n    var cleanTypeDesc = isClean == 0 ? \"分析数据\" : (isClean == 1 ? \"删除数据\" : \"重置ttl\");\n    $.post(\n            '${request.contextPath}/admin/app/job/submit',\n            {\n              jobType: 13,\n              appId: appId,\n              nodeInfos: nodeInfos == null ? \"\" : nodeInfos.toString(),\n              param: '分析清理类型: ' + cleanTypeDesc + '，条件: ' + condition.value,\n              reason: reason.value\n            },\n            function (data) {\n              if (data == 1) {\n                $(\"#appDelApplyInfo\").html(\"<div class='alert alert-error' ><font color='green'><strong>Success!</strong>数据分析清理申请提交成功，即将跳转工单列表！</font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"location.href = '${request.contextPath}/admin/app/jobs'\", 1000);\n              } else {\n                btn.disabled = false;\n                $(\"#appDelApplyInfo\").html(\"<div class='alert alert-error' ><font color='red'><strong>Error!</strong>数据分析清理申请失败！<font><button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card-header\">\n  <h4 class=\"card-title\">\n    数据分析清理\n  </h4>\n</div>\n\n<div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">应用<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <select id=\"appDel_appId\" name=\"appId\" class=\"selectpicker show-tick col-md-12 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\" onchange=\"changeAppIdSelect(this.value,'instance-select', '${request.contextPath}')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <#if (appDesc.status==2)>\n                <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\">\n                    【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n                </option>\n              </#if>\n            </#list>\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"form-check-label col-md-2 offset-md-1\">分析清理类型<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n          <div class=\"form-check form-check-inline\">\n            <label class=\"form-check-label\">\n              <input type=\"radio\" class=\"form-check-input\" name=\"isClean\" value=\"0\" checked> 分析\n            </label>\n          </div>\n          <div class=\"form-check form-check-inline\">\n            <label class=\"form-check-label\">\n              <input type=\"radio\" class=\"form-check-input\" name=\"isClean\" value=\"1\"> 清理\n            </label>\n          </div>\n          <div class=\"form-check form-check-inline\">\n            <label class=\"form-check-label\">\n              <input type=\"radio\" class=\"form-check-input\" name=\"isClean\" value=\"2\"> 重置ttl\n            </label>\n          </div>\n          <div class=\"row\">\n            <span class=\"help-block\" style=\"color: red\">\n                如生产集群申请该操作，请发送邮件，给相关同事和领导\n            </span>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">实例</label>\n        <div class=\"col-md-5\">\n          <select id=\"instance-select\" name=\"nodes\"\n                  class=\"selectpicker col-md-12 border rounded\" multiple\n                  data-live-search=\"true\" title=\"选择实例 (默认全部) \"data-size=\"8\">\n          </select>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">分析清理条件<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n            <textarea rows=\"8\" class=\"form-control\" name=\"condition\" id=\"condition\" placeholder=\"键匹配，对key进行过滤。\n1.（必填）匹配模式：abc_*\n2.（可选）如需筛选key中某部分大于或小于指定值，在匹配串中添加 @Less{指定值}Less@ ，如为大于，将Less替换为More\n3.（可选）重置ttl时间大于（1000）小于（3000）\n4.（可选）筛选ttl剩余时间小于（3600）/大于（7000）\"></textarea>\n          <span class=\"help-block\" style=\"color: green\">\n                                  如，abc开头、包含、结尾的键：abc*、*abc*、*abc,<br>\n                                  如，abc_{数字开头}的键：abc_[0-9]*<br>\n                                  如，abc_{数字开头和结尾}的键：abc_[0-9]*[0-9]<br>\n                                  如，abc_{数字}大于2000的键：abc_@More{2000}More@<br>\n                                  如，重置键ttl时间：重置ttl时间大于（1000）小于（3000）<br>\n                                  如，根据ttl剩余时间过滤键，ttl小于（3600）/大于（7000）。\n                              </span>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <label class=\"col-form-label col-md-2 offset-md-1\">申请原因<font color='red'>(*)</font></label>\n        <div class=\"col-md-5\">\n                <textarea rows=\"3\" name=\"appDelReason\" id=\"appDelReason\" placeholder=\"申请数据分析清理原因\"\n                          class=\"form-control\"></textarea>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div class=\"text-center\">\n          <button id=\"appConfigChangeBtn\" class=\"btn btn-danger\" onclick=\"appDataScanCleanApply()\">\n            <i class=\"bi bi-check\"></i>\n            提交申请\n          </button>\n        </div>\n      </div>\n\n      <div class=\"form-group row\">\n        <div id=\"appDelApplyInfo\" class=\"offset-md-3 col-md-9\"></div>\n      </div>\n    </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appScanCleanIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n  <script src=\"${request.contextPath}/assets/js/custom/getInstancesByAppId.js\"></script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appScanClean.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appScanCleanTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appTrailInit.html",
    "content": "<script type=\"text/javascript\">\n  function checkParam(){\n\n    //应用名\n    var appName = document.getElementById(\"appName\");\n    if(appName.value == \"\"){\n      alert(\"应用名不能为空\");\n      appName.focus();\n      return false;\n    }\n\n    //应用描述\n    var appIntro = document.getElementById(\"appIntro\");\n    if(appIntro.value == \"\"){\n      alert(\"应用描述不能为空\");\n      appIntro.focus();\n      return false;\n    }\n  }\n</script>\n\n<div class=\"row\">\n  <h4 class=\"card-title border-bottom\">\n    申请试用集群<label style=\"color:red\">(每个用户只能申请1个)</label>\n  </h4>\n\n    <div class=\"row\">\n      <!-- BEGIN FORM-->\n      <form action=\"${request.contextPath}/admin/app/trail/add\" method=\"post\"\n            class=\"form-horizontal form-bordered form-row-stripped\" onsubmit=\"return checkParam()\">\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-3\">\n            应用名称<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <input type=\"text\" name=\"name\" id=\"appName\" <#noparse>placeholder=\"${服务名}-${机房:js/tc}-${环境:online/test}\"</#noparse>\n                   class=\"form-control\" onchange=\"checkAppNameExist('${request.contextPath}')\"/>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-3\">\n            应用描述<font color='red'>(*)</font>:\n          </label>\n          <div class=\"col-md-5\">\n            <textarea class=\"form-control\" name=\"intro\"\n                      rows=\"3\" id=\"appIntro\" placeholder=\"应用描述,不超过128个字符\"></textarea>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-3\">\n            集群类型:\n          </label>\n          <div class=\"col-md-5\">\n            <select name=\"type\" class=\"form-select\">\n              <option value=\"2\">\n                Redis集群\n              </option>\n              <option value=\"5\">\n                Redis哨兵\n              </option>\n              <option value=\"6\">\n                Redis单点\n              </option>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-3\">\n            内存总量<font color='red'>(*)</font>:\n          </label>\n          <label class=\"col-form-label col-md-1\">\n            50MB\n          </label>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-3\">\n            Redis部署版本:\n          </label>\n          <div class=\"col-md-5\">\n            <select id=\"versionId\" name=\"versionId\" class=\"form-select\">\n              <#list versionList as version>\n                <option value=\"${version.id}\">${version.name}</option>\n              </#list>\n            </select>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <label class=\"col-form-label col-md-3\">\n            试用期限:\n          </label>\n          <label class=\"col-form-label col-md-1\" style=\"color:orange\">\n            1个月\n          </label>\n        </div>\n\n        <input name=\"userId\" id=\"userId\" value=\"${userInfo.id}\" type=\"hidden\" />\n        <input id=\"appExist\" value=\"0\" type=\"hidden\" />\n\n        <div class=\"form-actions fluid\">\n          <div class=\"row\">\n            <div class=\"col-md-12\">\n              <div class=\"offset-md-3 col-md-9\">\n                <#if (trailSize ==  0  || userInfo.type == 0)>\n                  <button type=\"submit\" class=\"btn btn-info\">\n                    <i class=\"bi bi-check\"></i>\n                    申请开通\n                  </button>\n                <#else>\n                  <button  class=\"btn btn-info\" disabled=\"disabled\">\n                    <i class=\"bi bi-check\"></i>\n                    暂不能申请\n                  </button> <label style=\"color:red\">(已开通过试用集群${trailSize}个)</label>\n                </#if>\n              </div>\n            </div>\n          </div>\n        </div>\n    </form>\n  </div>\n\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/appTrailInitIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <!-- Vendor CSS Files -->\n  <!-- Template Main CSS File -->\n  <link href=\"${request.contextPath}/assets/css/jobindex.css\" rel=\"stylesheet\">\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/appTrailInit.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function () {\n    $(\"#newJobTab\").addClass(\"active\");\n    $(\"#appTrailInitTab\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/head.html",
    "content": "<!-- Navbar -->\n<nav class=\"main-header navbar navbar-expand navbar-white navbar-light\">\n    <!-- Left navbar links -->\n    <ul class=\"navbar-nav\">\n        <li class=\"nav-item\">\n            <a class=\"nav-link\" data-widget=\"pushmenu\" href=\"#\" role=\"button\"><i class=\"fas fa-bars\"></i></a>\n        </li>\n        <li class=\"nav-item d-sm-inline-block\">\n            <a href=\"${request.contextPath}/\" class=\"nav-link\">Home</a>\n        </li>\n        <li class=\"nav-item d-none d-sm-inline-block\">\n            <a target=\"_blank\" href=\"https://github.com/sohutv/cachecloud\" class=\"nav-link\">Github</a>\n        </li>\n        <li class=\"nav-item d-none d-sm-inline-block\">\n            <a target=\"_blank\" href=\"${request.contextPath}/wiki/quickstart/index\" class=\"nav-link\">Docs</a>\n        </li>\n        <li class=\"nav-item d-none d-sm-inline-block\">\n            <a target=\"_blank\" href=\"/web/resource/inc/daily\" class=\"nav-link\">Contact</a>\n        </li>\n    </ul>\n\n\n    <!-- Right navbar links -->\n    <ul class=\"navbar-nav ml-auto\">\n        <!-- Messages Dropdown Menu -->\n        <li class=\"nav-item dropdown\">\n            <a class=\"nav-link\" data-bs-toggle=\"dropdown\" href=\"#\">\n                <i class=\"bi bi-gear-fill\"></i>\n                <span class=\"dropdown-toggle ps-2\">\n                        <#if userInfo?? && userInfo.chName??>\n                            ${userInfo.chName!}\n                        </#if>\n                </span>\n            </a>\n            <ul class=\"dropdown-menu dropdown-menu-end dropdown-menu-arrow profile\">\n\n                <#if (userInfo?? && (userInfo.type == 0))>\n                    <li>\n                        <a target=\"_blank\" href=\"${request.contextPath}/manage/total/statlist\" class=\"dropdown-item d-flex align-items-center\">\n                            <i class=\"bi bi-grid-fill\"></i>\n                            <span>管理后台</span>\n                        </a>\n                        <div class=\"dropdown-divider\"></div>\n                    </li>\n                </#if>\n                <li>\n                    <a target=\"_blank\" class=\"dropdown-item d-flex align-items-center\" href=\"${request.contextPath}/admin/app/list\">\n                        <i class=\"bi bi-card-list\"></i>\n                        <span>应用列表</span>\n                    </a>\n                    <div class=\"dropdown-divider\"></div>\n                </li>\n                <li>\n                    <a target=\"_blank\" class=\"dropdown-item d-flex align-items-center\" href=\"${request.contextPath}/admin/app/jobs\">\n                        <i class=\"bi bi-list-task\"></i>\n                        <span>我的申请</span>\n                    </a>\n                    <div class=\"dropdown-divider\"></div>\n                </li>\n                <li>\n                    <a class=\"dropdown-item d-flex align-items-center\" href=\"${request.contextPath}/manage/logout\">\n                        <i class=\"bi bi-box-arrow-right\"></i>\n                        <span>注销</span>\n                    </a>\n                </li>\n            </ul><!-- End Profile Dropdown Items -->\n        </li>\n        <li class=\"nav-item\">\n            <a class=\"nav-link\" data-widget=\"fullscreen\" href=\"#\" role=\"button\">\n                <i class=\"fas fa-expand-arrows-alt\"></i>\n            </a>\n        </li>\n    </ul>\n</nav>\n<!-- /.navbar -->\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n\n  <#include '/inc/frontResources.html'>\n  <script src=\"${request.contextPath}/assets/vendor/data-tables/js/jquery.dataTables.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/data-tables/js/dataTables.bootstrap5.js\"></script>\n  <link href=\"${request.contextPath}/assets/vendor/data-tables/css/dataTables.bootstrap5.css\" rel=\"stylesheet\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n  <div class=\"wrapper\">\n\n    <#include \"/app/jobIndex/head.html\">\n    <#include \"/app/jobIndex/nav.html\">\n\n    <div class=\"content-wrapper\">\n      <!-- Main content -->\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <#include \"/app/jobIndex/myJobs.html\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n\n<#include \"/manage/inc/footer.html\">\n\n</body>\n<script>\n  $(function(){\n    $(\"#myjobTab\").addClass(\"active\");\n  })\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/myJobs.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        if (!jQuery().dataTable) {\n          return;\n        }\n        $('#job_tableDataList').dataTable({\n          \"searching\": true,\n          // \"scrollX\": true,\n          // \"autoWidth\": true,\n          \"ordering\": false,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"paginate\": {\n              \"first\": \"<<\",\n              \"previous\": \"<\",\n              \"next\": \">\",\n              \"last\": \">>\"\n            },\n            \"lengthMenu\": \"每页显示 _MENU_条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n          }\n        });\n      }\n    };\n  }();\n\n  $(function () {\n    TableManaged.init();\n  });\n</script>\n\n<div class=\"card-header\">\n  <h3 class=\"card-title\">\n    我的工单\n  </h3>\n</div>\n\n<div class=\"card-body table-responsive\">\n  <table class=\"table table-striped table-bordered table-hover\">\n    <tr>\n      <td><span style=\"font-weight:bold\">工单总数</span></td>\n      <td>${jobList?size}</td>\n      <td><span style=\"font-weight:bold\">完成工单数</span></td>\n      <td>\n        <#if statusStatisMap['1']??>\n          ${statusStatisMap['1']!}\n        <#else>\n          0\n        </#if>\n      </td>\n      <td style=\"color:orange;\"><span style=\"font-weight:bold\">待处理工单数</span></td>\n      <td style=\"color:orange;\">\n        <#if (statusStatisMap['0']?? && statusStatisMap['2']??)>\n          ${statusStatisMap['0'] + statusStatisMap['2']}\n        <#else>\n        0\n        </#if>\n      </td>\n      <td><span style=\"font-weight:bold\">被驳回工单数</span></td>\n      <td>\n        <#if statusStatisMap['-1']??>\n          ${statusStatisMap['-1']!}\n        <#else>\n          0\n        </#if>\n      </td>\n    </tr>\n    <tr>\n      <td><span style=\"font-weight:bold\">申请应用</span></td>\n      <td>\n        <#if typeStatisMap['0']??>\n          ${typeStatisMap['0']!}\n        <#else>\n          0\n        </#if>\n      </td>\n      <td><span style=\"font-weight:bold\">下线应用</span></td>\n      <td>\n        <#if typeStatisMap['10']??>\n          ${typeStatisMap['10']!}\n        <#else>\n          0\n        </#if>\n      </td>\n      <td><span style=\"font-weight:bold\">键值分析</span></td>\n      <td>\n        <#if typeStatisMap['6']??>\n          ${typeStatisMap['6']!}\n        <#else>\n          0\n        </#if>\n      </td>\n      <td><span style=\"font-weight:bold\">诊断应用</span></td>\n      <td>\n        <#if typeStatisMap['8']??>\n          ${typeStatisMap['8']!}\n        <#else>\n          0\n        </#if>\n      </td>\n    </tr>\n    <tr>\n      <td><span style=\"font-weight:bold\">容量变更</span></td>\n      <td>\n        <#if typeStatisMap['1']??>\n          ${typeStatisMap['1']!}\n        <#else>\n          0\n        </#if>\n      </td>\n      <td><span style=\"font-weight:bold\">配置修改</span></td>\n      <td>\n        <#if (typeStatisMap['4']?? && typeStatisMap['2']??)>\n          ${typeStatisMap['4']+typeStatisMap['2']}\n        <#else>\n          0\n        </#if>\n      </td>\n      <td><span style=\"font-weight:bold\">用户注册</span></td>\n      <td>\n        <#if typeStatisMap['3']??>\n          ${typeStatisMap['3']!}\n        <#else>\n          0\n        </#if>\n      </td>\n      <td><span style=\"font-weight:bold\">应用数据迁移</span></td>\n      <td>\n        <#if typeStatisMap['11']??>\n          ${typeStatisMap['11']!}\n        <#else>\n          0\n        </#if>\n      </td>\n    </tr>\n  </table>\n</div>\n\n<div class=\"card-body table-responsive\">\n  <table class=\"table table-bordered table-hover table-sm\" id=\"job_tableDataList\">\n    <thead>\n    <tr>\n      <th>序号</th>\n      <th>应用ID</th>\n      <th>应用名</th>\n      <th>申请人</th>\n      <th>申请类型</th>\n      <th>申请描述</th>\n      <th>申请时间</th>\n      <th>审批状态</th>\n      <th>处理人</th>\n      <th>完成时间</th>\n      <th>结果</th>\n    </tr>\n    </thead>\n    <tbody>\n    <#list jobList as item>\n      <tr>\n        <td>${item_index + 1}</td>\n        <td>\n          <#if (item.type == 3)>\n            无\n          <#else>\n            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${item.appId!}\">${item.appId!}</a>\n          </#if>\n        </td>\n        <td>\n          <#if (item.type == 3)>\n              无\n          <#else>\n            <#if item.appDesc?? && item.appDesc.name??>\n              ${item.appDesc.name!}\n            </#if>\n          </#if>\n        </td>\n        <td>${item.userName!}</td>\n        <td>${(appAuditTypeMap?api.get(item.type)).info!}</td>\n        <td>${item.info!}</td>\n        <td>${item.createTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n        <td>\n          <#if (item.status == 0)><font style=\"color: orange;\">待审</font>\n          <#elseif (item.status == 1)><font style=\"color: green;\">通过</font>\n          <#elseif (item.status == 2)><font style=\"color: cornflowerblue;\">已受理</font>\n          <#elseif (item.status == -1)><font style=\"color: darkred;\">驳回</font>\n          </#if>\n        </td>\n        <td>\n          <#if adminMap?? && item.operateId?? && (adminMap?api.get(item.operateId))??>\n            ${(adminMap?api.get(item.operateId)).chName!}\n          </#if>\n        </td>\n        <td>\n          <#if (item.status == 1 || item.status == -1)>${item.modifyTime?string(\"yyyy-MM-dd HH:mm:ss\")}</#if>\n        </td>\n        <td>\n          <#if (item.status == 1) && (item.type == 6)><a target=\"_blank\" href=\"${request.contextPath}/admin/app/keyAnalysisResult?appId=${item.appId!}&auditId=${item.id!}\">[查看结果]</a>\n          <#elseif (item.status == -1)>${item.refuseReason!}\n          </#if>\n        </td>\n      </tr>\n    </#list>\n    </tbody>\n  </table>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/jobIndex/nav.html",
    "content": "<!-- ======= Sidebar ======= -->\n<aside id=\"sidebar\" class=\"main-sidebar sidebar-dark-primary elevation-4\">\n    <div class=\"sidebar\">\n        <nav class=\"mt-2\">\n            <ul class=\"nav nav-pills nav-sidebar flex-column\" data-widget=\"treeview\" role=\"menu\" data-accordion=\"false\" id=\"sidebar-nav\">\n                <li class=\"nav-item\">\n                    <a id=\"myjobTab\" class=\"nav-link\" href=\"${request.contextPath}/admin/app/jobs\">\n                        <i class=\"bi bi-layout-text-window-reverse\"></i>\n                        <p>我的工单</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item menu-is-opening menu-open\">\n                    <a id=\"newJobTab\" class=\"nav-link collapsed\" data-bs-target=\"#tables-nav\" data-bs-toggle=\"collapse\" href=\"#\">\n                        <i class=\"bi bi-plus-lg\"></i>\n                        <p>\n                            创建工单\n                            <i class=\"right bi bi-chevron-left\"></i>\n                        </p>\n                    </a>\n\n                    <ul id=\"tables-nav\" class=\"nav nav-treeview\" style=\"display: block;\">\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/init\" id=\"appInitTab\">\n                                <i class=\"bi bi-cloud-fill\"></i>\n                                <p>申请应用</p>\n                            </a>\n                        </li>\n\n                        <#if trailswitch?? && trailswitch>\n                            <li class=\"nav-item\">\n                                <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/trail/init\" id=\"appTrailInitTab\">\n                                    <i class=\"bi bi-arrow-down-right-square\"></i>\n                                    <p>申请试用集群</p>\n                                </a>\n                            </li>\n                        </#if>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/import\" id=\"appImportTab\">\n                                <i class=\"bi bi-arrow-down-right-circle\"></i>\n                                <p>导入应用</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appDel\" id=\"appCleanTab\">\n                                <i class=\"bi bi-trash3\"></i>\n                                <p>数据清理</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appOffline\" id=\"appOfflineTab\">\n                                <i class=\"bi bi-x-octagon\"></i>\n                                <p>下线应用</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appDataMigrate\" id=\"appDataMigrateTab\">\n                                <i class=\"bi bi-repeat\"></i>\n                                <p>数据迁移</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appDiagnostic\" id=\"appDiagnosticTab\">\n                                <i class=\"bi bi-shield-plus\"></i>\n                                <p>诊断应用</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appKeyAnalysis?appId=${appId!}\" id=\"appKeyAnslysisTab\">\n                                <i class=\"bi bi-clipboard-data\"></i>\n                                <p>键值分析</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appScale?appId=${appId!}\" id=\"appScaleTab\">\n                                <i class=\"bi bi-plus-slash-minus\"></i>\n                                <p>扩容/缩容</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appConfig?appId=${appId!}&instanceId=${instanceId!}\" id=\"appConfigTab\">\n                                <i class=\"bi bi-pencil-square\"></i>\n                                <p>修改应用配置</p>\n                            </a>\n                        </li>\n\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appAlterConfig?appId=${appId!}\" id=\"appAlertTab\">\n                                <i class=\"bi bi-bell-fill\"></i>\n                                <p>修改报警</p>\n                            </a>\n                        </li>\n\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link\" href=\"${request.contextPath}/admin/app/appScanClean\" id=\"appScanCleanTab\">\n                                <i class=\"bi bi-clipboard-plus\"></i>\n                                <p>数据分析清理</p>\n                            </a>\n                        </li>\n\n                    </ul>\n                </li>\n            </ul>\n        </nav>\n    </div>\n</aside><!-- End Sidebar-->\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/slowLog.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"card-header\">\n    <div class=\"\">\n      <form method=\"get\" action=\"${request.contextPath}/admin/app/index\" class=\"row d-flex justify-content-end\">\n        <label class=\"col-form-label col-auto\" style=\"font-weight:bold;text-align:left;\">\n          &nbsp;查询日期:&nbsp;&nbsp;\n        </label>\n        <input type=\"date\" class=\"col-auto\" size=\"20\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate}\">\n        <input type=\"hidden\" name=\"appId\" value=\"${appDesc.appId}\">\n        <input type=\"hidden\" name=\"tabTag\" value=\"app_latency\">\n        <input type=\"submit\" class=\"btn btn-info col-auto ml-2\" value=\"查询\"/>\n      </form>\n    </div>\n  </div>\n\n  <div class=\"\">\n    <script type=\"text/javascript\">\n      var appId = '${appDesc.appId}';\n      var searchDate = '${searchDate}';\n      var appLatencyStatsMap = '${appLatencyStatsJson}';\n      var appLatencyStatsJson = eval(\"(\" + appLatencyStatsMap + \")\");\n\n      Highcharts.setOptions({\n        global: {\n          useUTC: false\n        }\n      });\n      Highcharts.setOptions({\n        colors: ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n          '#492970', '#804000', '#f28f43', '#77a1e5',\n          '#c42525', '#a6c96a']\n      });\n\n      $(document).ready(\n              function () {\n                var data = appLatencyStatsJson;\n                var unit = \"个数\";\n                var appTotalOptions = getOption(\"LatencyContainer\", \"<b>\" + \"延迟事件统计</b>\", unit);\n                appTotalOptions.series = getAppLatencyInfo(data, 'count', unit, searchDate, '${request.contextPath}');\n                var appTotalchart = new Highcharts.Chart(appTotalOptions);\n              });\n    </script>\n\n\n    <div class=\"alert alert-warning alert-dismissible\">\n      提示：点击图中点查看延迟事件详情！\n      <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n    </div>\n    <div id=\"LatencyContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  </div>\n\n  <div class=\"card-header\">\n    <h5 class=\"card-title\">\n      redis实例延迟&慢查询统计\n    </h5>\n  </div>\n  <div class=\"\">\n    <table class=\"table table-striped table-hover\">\n      <thead>\n      <tr>\n        <th scope=\"col\">序号</th>\n        <th scope=\"col\">实例信息</th>\n        <th scope=\"col\">延迟事件个数</th>\n        <th scope=\"col\">慢查询个数</th>\n      </tr>\n      </thead>\n      <tbody>\n      <#list instanceSet as key>\n        <tr>\n          <td scope=\"row\">${key_index + 1}</td>\n          <td><a href=\"#${key}\">${key}</a></td>\n          <td>\n            <#assign latency_count = \"0\">\n            <#if appLatencyStatsGroupByInstance?? && appLatencyStatsGroupByInstance[key]??>\n              <#assign latency_count = appLatencyStatsGroupByInstance[key]>\n            </#if>\n            ${latency_count}\n          </td>\n          <td>\n            <#assign slowlog_count=\"0\">\n            <#if appInstanceSlowLogCountMap?? && appInstanceSlowLogCountMap[key]??>\n              <#assign slowlog_count=appInstanceSlowLogCountMap[key]>\n            </#if>\n            ${slowlog_count}\n        </tr>\n      </#list>\n      </tbody>\n    </table>\n  </div>\n\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      各实例慢查询情况&nbsp&nbsp<label class=\"label label-info\">共${appInstanceSlowLogList?size}次</label>\n    </h3>\n  </div>\n  <div class=\"\">\n    <#list instaceSlowLogMap?keys as key>\n      <div style=\"margin-top: 0px\">\n        <div class=\"page-header\" id=\"${key}\">\n          <h5>\n            <#assign instanceId=(instaceSlowLogMap[key])[0].instanceId>\n            <li>\n              <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instanceId}\"\n                 target=\"_blank\">${key}</a>\n            </li>\n          </h5>\n        </div>\n        <div class=\"table-responsive\">\n          <table class=\"table table-bordered table-striped table-hover\">\n            <thead>\n            <tr>\n              <th scope=\"col\">序号</th>\n              <th scope=\"col\">慢查询id</th>\n              <th scope=\"col\">耗时(单位:微秒)</th>\n              <th scope=\"col\">命令</th>\n              <th scope=\"col\">发生时间</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#if instaceSlowLogMap?? && instaceSlowLogMap[key]?? && (instaceSlowLogMap[key]?size gt 0)>\n              <#assign slowLogList = instaceSlowLogMap[key]>\n              <#assign slowLogIndex = 0>\n              <#list slowLogList as slowLog>\n                <#assign slowLogIndex = (slowLogIndex + 1)>\n                <tr>\n                  <td scope=\"row\">${slowLogIndex}</td>\n                  <td>${slowLog.slowLogId}</td>\n                  <td>${slowLog.costTime?string(\"#,#00\")}</td>\n                  <td>${slowLog.command}</td>\n                  <td>${slowLog.executeTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n                </tr>\n              </#list>\n            </#if>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </#list>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/app/userAppsIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <title>CacheCloud应用统计信息</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n<div class=\"wrapper\">\n  <#include '/inc/head.html'>\n  <div class=\"content-wrapper ml-0\">\n    <div class=\"content\">\n      <div class=\"container-fluid\">\n        <div id=\"systemAlert\">\n        </div>\n        <div class=\"row\">\n          <div class=\"col-12\">\n            <div class=\"card\">\n              <div class=\"card-body\">\n                <nav class=\"nav\">\n                  <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                    <li id=\"app_stat\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/stat?appId=${appId!}&startDate=${startDate!}&endDate=${endDate!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_stat\">应用统计信息</a>\n                    </li>\n                    <li id=\"app_topology\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/topology?appId=${appId!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_topology\">实例列表</a>\n                    </li>\n                    <li id=\"app_detail\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/detail?appId=${appId!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_detail\">应用详情</a>\n                    </li>\n                    <li id=\"app_clientList\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/clientList?appId=${appId!}&condition=${condition!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_clientList\">连接信息</a>\n                    </li>\n                    <li id=\"app_command_analysis\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/commandAnalysis?appId=${appId!}&startDate=${startDate!}&endDate=${endDate!}&firstCommand=${firstCommand!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_command_analysis\">命令曲线</a>\n                    </li>\n                    <li id=\"app_command\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/command?appId=${appId!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_command\">命令执行</a>\n                    </li>\n                    <li id=\"app_demo\" class=\"nav-item\" data-url=\"${request.contextPath}/wiki/access/client?entry=client\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_demo\">接入代码</a>\n                    </li>\n                    <li id=\"app_latency\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/latencyMonitor?appId=${appId}&searchDate=${searchDate!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_latency\">延迟监控</a>\n                    </li>\n                    <li id=\"app_top_pic\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/machineInstancesTopology?appId=${appId!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_top_pic\">应用拓扑</a>\n                    </li>\n                    <li id=\"app_daily\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/daily?appId=${appId}&dailyDate=${dailyDate!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_daily\">日报统计</a>\n                    </li>\n                    <li id=\"app_key_analysis\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/key?appId=${appId!}\">\n                      <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_key_analysis\">键值分析</a>\n                    </li>\n                  </ul>\n                </nav>\n              </div>\n            </div>\n\n            <div class=\"card\">\n              <div class=\"card-body\">\n                <div class=\"tab-content\">\n                  <div class=\"tab-pane active\" id=\"app_statTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_topologyTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_detailTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_clientListTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_command_analysisTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_commandTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_demoTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_latencyTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_top_picTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_dailyTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"app_key_analysisTab\">\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <#include \"/inc/footer.html\">\n</div>\n<script type=\"text/javascript\">\n  function showTab(tab) {\n    var url = $(\"#\" + tab).attr(\"data-url\");\n    if(url == null || url == undefined || url == \"\"){\n      return;\n    }\n    $.get($(\"#\" + tab).attr(\"data-url\"), function (result) {\n      $(\"#\" + tab + \"Tab\").html(result);\n    });\n  }\n\n  function refreshActiveTab() {\n    var tab = getQueryString(\"tabTag\");\n    if (tab) {\n      $(\"#\" + tab + \" a\").addClass(\"active\");\n      $(\"#\" + tab + \"Tab\").addClass(\"active\").siblings().removeClass(\"active\");\n    } else {\n      tab = \"app_stat\";\n      $(\"#\" + tab + \" a\").addClass(\"active\");\n    }\n    console.log(\"tab:\" + tab)\n    showTab(tab);\n    $(\"#tabs li a\").tooltip({placement: \"bottom\"});\n  }\n\n  $(function () {\n    refreshActiveTab();\n  });\n\n  function getQueryString(name) {\n    var reg = new RegExp(\"(^|&)\" + name + \"=([^&]*)(&|$)\");\n    console.log(\"window.location.search: \"+ window.location.search);\n    var r = window.location.search.substr(1).match(reg);\n    if (r != null) return unescape(r[2]);\n    return null;\n  }\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/appAlert.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>应用Redis分钟报警</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n            <ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">应用Redis分钟报警：</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        port\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        属性值\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        说明\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        其他信息\n                    </td>\n                </tr>\n\n                <#list instanceAlertValueResultList as item>\n                    <tr>\n                        <#assign appid=item.appId?c>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a href='${ccDomain}/admin/app/index?appId=${appid}'>${appid}</a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.appDesc.name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.appDesc.officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.instanceInfo.ip!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.instanceInfo.port!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${item.instanceAlertConfig.alertConfig!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${item.alertMessage!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${item.otherInfo!}\n                        </td>\n                    </tr>\n                </#list>\n\n            </table>\n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/appAudit.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>CacheCloud审核邮件</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td style=\"width:5px;\"></td>\n        <td style=\"color:#3f3f3f; font-weight: bold;font-size:12px; font-family: '宋体'; padding: 5px 0 15px 0\">\n            ${appAudit.userName!}，您好，欢迎使用CacheCloud平台，您的申请处理进度如下：\n        </td>\n    </tr>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n            <ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">申请处理信息：</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        申请类型：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appAudit.typeDesc!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; text-align: left;height:33px; width: 50px;\">\n                        申请描述：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appAudit.info!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; text-align: left;height:33px; width: 50px;\">\n                        申请时间：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appAudit.createTimeFormat!}\n                    </td>\n                </tr>\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        处理状态：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px; color: red;\">\n                        ${appAudit.statusDesc!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        处理描述：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px; color: red;\">\n                        ${appAudit.refuseReason!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        处理时间：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appAudit.modifyTime?string(\"yyyy-MM-dd HH:mm:ss\")!}\n                    </td>\n                </tr>\n            </table>\n            <br/>\n            <#if appDesc??>\n                <ul>\n                    <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">应用详细信息：</span></li>\n                </ul>\n                <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                    <tr>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            应用名称：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${appDesc.name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            应用类型：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${appDesc.typeDesc!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            应用状态：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px; color: red;\">\n                            ${appDesc.statusDesc!}\n                        </td>\n                    </tr>\n                    <tr>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            是否测试：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            <#if appDesc.isTest == 1>\n                                是\n                            <#else>\n                                否\n                            </#if>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            是否持久化：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            <#if appDesc.needPersistence == 1>\n                                是\n                            <#else>\n                                否\n                            </#if>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            是否有后端数据源：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            <#if appDesc.hasBackStore == 1>\n                                是\n                            <#else>\n                                否\n                            </#if>\n                        </td>\n                    </tr>\n                    <tr>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            应用描述：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${appDesc.intro!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            应用负责人：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${appDesc.officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                            应用申请时间：\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${appDesc.createTime?string(\"yyyy-MM-dd HH:mm:ss\")!}\n                        </td>\n                    </tr>\n                </table>\n            </#if>\n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/appDaily.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>CacheCloud应用日报</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n        \t<ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">应用详细信息：${appDesc.appId?c}</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        应用名称：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDesc.name!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        应用类型：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDesc.typeDesc!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        应用状态：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDesc.statusDesc!}\n                    </td>\n                </tr>\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        应用描述：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDesc.intro!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        应用负责人：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDesc.officer!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        应用申请时间：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDesc.createTime?string(\"yyyy-MM-dd HH:mm:ss\")!}\n                    </td>\n                </tr>\n            </table>\n        \n            <ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">客户端相关(确保使用有客户端统计的版本):</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        客户端值分布(全天):\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n\t\t\t\t\t\t${appDailyData.valueSizeDistributeCountDesc!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; text-align: left;height:33px; width: 50px;\">\n                        客户端连接数(每分钟)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        最大值:${appDailyData.maxMinuteClientCount!} <br/>\n                        平均值:${appDailyData.avgMinuteClientCount!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; text-align: left;height:33px; width: 50px;\">\n                        客户端延迟事件数(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDailyData.latencyCount!}\n                    </td>\n                </tr>\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        命令调用统计(全天):\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        次数:${appDailyData.clientCmdCount!}<br/>\n                        平均耗时(ms):${appDailyData.clientAvgCmdCost!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; text-align: left;height:33px; width: 50px;\">\n                        连接异常统计(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        次数:${appDailyData.clientConnExpCount!}<br/>\n                        平均耗时(ms):${appDailyData.clientAvgConnExpCost!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; text-align: left;height:33px; width: 50px;\">\n                        命令超时统计(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        次数:${appDailyData.clientCmdExpCount!} <br/>\n                        平均耗时(ms):${appDailyData.clientAvgCmdExpCost!}\n                    </td>\n                </tr>\n            </table>\n            <br/>\n            \n            <ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">服务端相关:</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        慢查询个数(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        ${appDailyData.slowLogCount!}\n                    </td>\n\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        命令次数(每分钟)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                       \t 最大值:${appDailyData.maxMinuteCommandCount!} <br/>\n                       \t 平均值:${appDailyData.avgMinuteCommandCount!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        命中率(每分钟)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                       \t 最大值:${appDailyData.maxMinuteHitRatio!}% <br/>\n                       \t 最小值:${appDailyData.minMinuteHitRatio!}% <br/>\n                       \t 平均值:${appDailyData.avgHitRatio!}%\n                    </td>\n                </tr>\n                \n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        内存使用量(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        平均使用量:${appDailyData.avgUsedMemory!} M<br/>\n                        最大使用量:${appDailyData.maxUsedMemory!} M\n                    </td>\n\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        过期键数(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                       \t ${appDailyData.expiredKeysCount!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        剔除键数(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                       \t ${appDailyData.evictedKeysCount!}\n                    </td>\n                </tr>\n                \n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                       键个数(全天)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        平均值:${appDailyData.avgObjectSize!}<br/>\n                        最大值:${appDailyData.maxObjectSize!}\n                    </td>\n\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        input流量(每分钟)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                       \t 平均值:${appDailyData.avgMinuteNetInputByte!} M<br/>\n                       \t 最大值:${appDailyData.maxMinuteNetInputByte!} M<br/>\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767;text-align: left; height:33px; width: 50px;\">\n                        output流量(每分钟)：\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                       \t 平均:${appDailyData.avgMinuteNetOutputByte!} M<br/>\n                       \t 最大:${appDailyData.maxMinuteNetOutputByte!} M<br/>\n                    </td>\n                </tr>\n            </table>\n            <br/>\n            \n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/client/appClientIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>CacheCloud客户端信息</title>\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n    <#include '/inc/frontResources.html'>\n  </head>\n  <body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n    <div class=\"wrapper\">\n      <#include '/inc/head.html'>\n      <div class=\"content-wrapper ml-0\">\n        <div class=\"content\">\n          <div class=\"container-fluid\">\n            <div class=\"row\">\n              <div class=\"col-12\">\n                <div class=\"card\">\n                  <div class=\"card-body\">\n                    <nav class=\"nav\">\n                      <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                        <li id=\"app_client_command_statistics_id\" class=\"nav-item\" data-bs-toggle=\"tab\" data-url=\"${request.contextPath}/client/show/commandStatistics?appId=${appId!}&searchDate=${searchDate!}\">\n                          <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&searchDate=${searchDate!}&tabTag=app_client_command_statistics\">命令调用统计&nbsp;<sup><label class=\"bi bi-star-fill\"></label></sup></a>\n                        </li>\n                        <li id=\"app_client_exception_statistics_id\" class=\"nav-item\" data-bs-toggle=\"tab\" data-url=\"${request.contextPath}/client/show/exceptionStatistics?appId=${appId!}&searchDate=${searchDate!}\">\n                          <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&searchDate=${searchDate!}&tabTag=app_client_exception_statistics\">异常情况统计&nbsp;<sup><label class=\"bi bi-star-fill\"></label></sup></a>\n                        </li>\n                        <li class=\"nav-item\">\n                          <a class=\"nav-link d-flex\" href=\"${request.contextPath}/admin/app/index?appId=${appId!}\" target=\"_blank\">返回查看应用&nbsp;<sup><label class=\"bi bi-arrow-return-left\"></label></sup></a>\n                        </li>\n                      </ul>\n                    </nav>\n                  </div>\n                </div>\n                <div class=\"card\">\n                  <div class=\"card-body\">\n                    <div class=\"tab-content\">\n                      <div class=\"tab-pane active\" id=\"app_client_command_statistics\"></div>\n                      <div class=\"tab-pane\" id=\"app_client_exception_statistics\"></div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      <#include \"/inc/footer.html\">\n    </div>\n    <script type=\"text/javascript\">\n      function showTab(tab) {\n        $.get($(\"#\" + tab + \"_id\").attr(\"data-url\"), function (result) {\n          $(\"#\" + tab).html(result);\n        });\n      }\n\n      function activeTab(tab) {\n        if (tab) {\n          $(\"#\" + tab + \"_id a\").addClass(\"active\");\n          $(\"#\" + tab).addClass(\"active\").siblings().removeClass(\"active\");\n        } else {\n          tab = \"app_client_command_statistics\";\n          $(\"#\" + tab + \"_id a\").addClass(\"active\");\n        }\n        showTab(tab);\n        $(\"#app_tabs li a\").tooltip({placement: \"bottom\"});\n      }\n\n      $(function () {\n        var tabTag = \"${tabTag!}\";\n        activeTab(tabTag);\n      });\n    </script>\n  </body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/client/clientCommandStatistics.html",
    "content": "<div class=\"col-12\" id=\"mainClientCommandContainer\">\n  <br/>\n  <div class=\"alert alert-default-success alert-dismissable\">\n    提示：请升级cachecloud-client-redis版本到2.0.1-RELEASE及以上！\n    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-hidden=\"true\"></button>\n  </div>\n\n  <form method=\"get\" action=\"${request.contextPath}/client/show/index\" id=\"clientCommandStatisticsForm\" class=\"row\">\n    <div class=\"row justify-content-end\">\n      <label class=\"col-form-label col-auto\" style=\"font-weight:bold;text-align:left;\">\n        &nbsp;查询日期:&nbsp;&nbsp;\n      </label>\n      <div class=\"col-auto\">\n        <input type=\"date\" class=\"form-control\" size=\"20\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate}\"/>\n      </div>\n      <input type=\"hidden\" name=\"appId\" value=\"${appId}\">\n      <input type=\"hidden\" name=\"tabTag\" value=\"app_client_command_statistics\">\n      <div class=\"col-auto\">\n        <label><input type=\"submit\" class=\"btn btn-info\" value=\"查询\"/></label>\n      </div>\n    </div>\n  </form>\n\n\n  <script type=\"text/javascript\">\n    var searchDate = '${searchDate}';\n    var appId = '${appId}';\n    var chartParams = \"&searchDate=\" + searchDate;\n    //应用下各客户端命令统计\n    var appClientCommandStatisticsMap = '${appClientCommandStatisticsJson}';\n    var appClientCommandStatisticsJson = eval(\"(\" + appClientCommandStatisticsMap + \")\");\n\n    Highcharts.setOptions({\n      global: {\n        useUTC: false\n      }\n    });\n    Highcharts.setOptions({\n      colors: ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n        '#492970', '#804000', '#f28f43', '#77a1e5',\n        '#c42525', '#a6c96a']\n    });\n\n    $(document).ready(\n            function () {\n              var data = appClientCommandStatisticsJson;\n\n              var count_unit = \"次数\";\n              var count_appTotalOptions = getOption(\"cmd_countContainer\", \"<b>\" + \"命令调用次数</b>\", count_unit);\n              count_appTotalOptions.series = getClientStatisticsByType(data, 'count', count_unit, searchDate);\n              var count_appTotalchart = new Highcharts.Chart(count_appTotalOptions);\n\n              //cost\n              var cost_unit = \"毫秒\";\n              var cost_appTotalOptions = getOption(\"cmd_costContainer\", \"<b>\" + \"命令平均耗时</b>\", cost_unit);\n              cost_appTotalOptions.series = getClientStatisticsByType(data, 'cost', cost_unit, searchDate);\n              var cost_appTotalchart = new Highcharts.Chart(cost_appTotalOptions);\n\n              //bytesIn\n              var byte_unit = \"MB\";\n              var byte_appTotalOptions = getOption(\"cmd_byteInContainer\", \"<b>\" + \"命令输入流量</b>\", byte_unit);\n              byte_appTotalOptions.series = getClientStatisticsByType(data, 'bytesIn', byte_unit, searchDate);\n              var byte_appTotalchart = new Highcharts.Chart(byte_appTotalOptions);\n\n              //bytesOut\n              var byteOut_appTotalOptions = getOption(\"cmd_byteOutContainer\", \"<b>\" + \"命令输出流量</b>\", byte_unit);\n              byteOut_appTotalOptions.series = getClientStatisticsByType(data, 'bytesOut', byte_unit, searchDate);\n              var byteOut_appTotalOptions = new Highcharts.Chart(byteOut_appTotalOptions);\n            });\n  </script>\n  <div class=\"page-header\">\n    <h4>命令调用全局统计情况</h4>\n    <a target=\"_blank\" href=\"${request.contextPath}/client/show/commandStatistics/client?appId=${appId}&searchDate=${searchDate}\">\n      <#assign cmd_exp_count = appClientGatherStat['cmd_count']>\n      <#if !(cmd_exp_count??)><#assign cmd_exp_count = '0'></#if>\n      客户端详情（${cmd_exp_count}次）\n    </a>\n  </div>\n  <div id=\"cmd_countContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  <br/>\n  <div id=\"cmd_costContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  <br/>\n  <div id=\"cmd_byteInContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  <br/>\n  <div id=\"cmd_byteOutContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  <br/>\n\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/client/clientExceptionStatistics.html",
    "content": "<div class=\"col-12\" id=\"mainClientExceptionContainer\">\n  <br/>\n  <div class=\"alert alert-default-success alert-dismissable\">\n    提示：请升级cachecloud-client-redis版本到2.0.1-RELEASE及以上！\n    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-hidden=\"true\"></button>\n  </div>\n\n  <form method=\"get\" action=\"${request.contextPath}/client/show/index\" id=\"clientExceptionStatisticsForm\">\n    <div class=\"row justify-content-end\">\n      <label class=\"col-form-label col-auto\" style=\"font-weight:bold;text-align:left;\">\n        &nbsp;查询日期:&nbsp;&nbsp;\n      </label>\n      <div class=\"col-auto\">\n        <input type=\"date\" class=\"form-control\" size=\"20\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate}\">\n      </div>\n      <input type=\"hidden\" name=\"appId\" value=\"${appId}\">\n      <input type=\"hidden\" name=\"tabTag\" value=\"app_client_exception_statistics\">\n      <div class=\"col-auto\">\n        <label>&nbsp;<input type=\"submit\" class=\"btn btn-info\" value=\"查询\"/></label>\n      </div>\n    </div>\n  </form>\n\n\n  <script type=\"text/javascript\">\n    var searchDate = '${searchDate}';\n    var appId = '${appId}';\n    var chartParams = \"&searchDate=\" + searchDate;\n    //应用下各客户端命令统计\n    var appClientExceptionStatisticsMap = '${appClientExceptionStatisticsJson}';\n    var appClientExceptionStatisticsJson = eval(\"(\" + appClientExceptionStatisticsMap + \")\");\n\n    Highcharts.setOptions({\n      global: {\n        useUTC: false\n      }\n    });\n    Highcharts.setOptions({\n      colors: ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n        '#492970', '#804000', '#f28f43', '#77a1e5',\n        '#c42525', '#a6c96a']\n    });\n\n    $(document).ready(\n            function () {\n              var data = appClientExceptionStatisticsJson;\n\n              var count_unit = \"次数\";\n              var count_appTotalOptions = getOption(\"countContainer\", \"<b>\" + \"异常次数</b>\", count_unit);\n              count_appTotalOptions.series = getClientStatisticsByType(data, 'count', count_unit, searchDate);\n              var count_appTotalchart = new Highcharts.Chart(count_appTotalOptions);\n\n              //cost\n              var cost_unit = \"毫秒\";\n              var cost_appTotalOptions = getOption(\"costContainer\", \"<b>\" + \"异常平均耗时</b>\", cost_unit);\n              cost_appTotalOptions.series = getClientStatisticsByType(data, 'cost', cost_unit, searchDate);\n              var cost_appTotalchart = new Highcharts.Chart(cost_appTotalOptions);\n\n            });\n  </script>\n  <div class=\"page-header\">\n    <h4>异常情况全局统计</h4>\n    <ul>\n      <li><a target=\"_blank\" href=\"${request.contextPath}/client/show/exceptionStatistics/client?appId=${appId}&searchDate=${searchDate}&exceptionType=0\">\n        <#if appClientGatherStat?? && (appClientGatherStat['conn_exp_count'])??>\n          <#assign conn_exp_count = appClientGatherStat['conn_exp_count']>\n        <#else>\n          <#assign conn_exp_count = '0'>\n        </#if>\n        <#if !(conn_exp_count??)><#assign conn_exp_count = '0'></#if>\n        客户端连接异常详情（${conn_exp_count}次）\n      </a>\n      </li>\n      <li><a target=\"_blank\" href=\"${request.contextPath}/client/show/exceptionStatistics/client?appId=${appId}&searchDate=${searchDate}&exceptionType=1\">\n        <#if appClientGatherStat?? && (appClientGatherStat['cmd_exp_count'])??>\n          <#assign cmd_exp_count = appClientGatherStat['cmd_exp_count']>\n        <#else>\n          <#assign cmd_exp_count = '0'>\n        </#if>\n        <#if !(cmd_exp_count??)><#assign cmd_exp_count = '0'></#if>\n        客户端命令超时详情（${cmd_exp_count}次）\n      </a>\n      </li>\n    </ul>\n  </div>\n  <div id=\"countContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  <br/>\n  <div id=\"costContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n  <br/>\n\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/client/cmdExceptionCommandDetail.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <title>客户端超时命令详情</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <#include '/inc/head.html'>\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"container\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <div class=\"card-header\">\n                  <h3 class=\"card-title\">&nbsp;&nbsp;应用：<label class=\"label label-success\">${appId!}</label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n                    查询时间：<label class=\"label label-success\">${searchDate!}</label>\n                  </h3>\n                </div>\n                <div class=\"card-body table-responsive\">\n                  <table class=\"table table-striped table-hover\">\n                    <thead>\n                    <tr>\n                      <td>序号</td>\n                      <td>实例信息</td>\n                      <td>次数</td>\n                      <td>平均耗时(单位:毫秒)</td>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#list sumCmdExpStatMap?keys as key>\n                      <tr>\n                        <td>${key_index + 1}</td>\n                        <td><a href=\"#${key!}\">${key!}</a></td>\n                        <td>${sumCmdExpStatMap[key]['count']}</td>\n                        <td>${sumCmdExpStatMap[key]['cost']}</td>\n                      </tr>\n                    </#list>\n                    </tbody>\n                  </table>\n                </div>\n              </div>\n\n              <div class=\"card\">\n                <div class=\"card-body\">\n                  <#list latencyCommandDetailMap?keys as key>\n                    <div style=\"margin-top: 20px\">\n                      <div class=\"page-header\" id=\"${key!}\">\n                        <h4>${key!}</h4>\n                      </div>\n                      <table class=\"table table-bordered table-striped table-hover\">\n                        <thead>\n                        <tr>\n                          <td>序号</td>\n                          <td>执行时间</td>\n                          <td>命令明文</td>\n                          <td>参数(长参数裁剪)</td>\n                          <td>参数字节数</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <#list latencyCommandDetailMap[key]  as latencyCommandDetail>\n                          <tr>\n                            <td>${latencyCommandDetail_index + 1}</td>\n                            <td>${latencyCommandDetail.format_invoke_time!}</td>\n                            <td>${latencyCommandDetail.command!}</td>\n                            <td>${latencyCommandDetail.args!}</td>\n                            <td>${latencyCommandDetail.size!}</td>\n                          </tr>\n                        </#list>\n                        </tbody>\n                      </table>\n                    </div>\n                  </#list>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n    <#include \"/inc/footer.html\">\n  </div>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/client/cmdExceptionStatisticsByClient.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <title>客户端命令超时统计</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <!-- Header End -->\n    <#include \"/inc/head.html\">\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"container\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <div class=\"card-body\">\n                  <div class=\"\">\n                    <form method=\"get\" action=\"${request.contextPath}/client/show/exceptionStatistics/client\" id=\"clientCommandStatisticsForm\">\n                      <div class=\"row\">\n                        <div class=\"d-flex justify-content-end\">\n                          <label class=\"col-form-label col-auto\" style=\"font-weight:bold;text-align:left;\">\n                            &nbsp;查询日期:&nbsp;&nbsp;\n                          </label>\n                          <div class=\"col-auto\">\n                            <input class=\"form-control\" type=\"date\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate!}\">\n                          </div>\n                          <label>&nbsp;<input type=\"submit\" class=\"btn btn-info col-auto\" value=\"查询\"/></label>\n                        </div>\n                      </div>\n                      <div class=\"row page-header\">\n                        <h4>&nbsp;&nbsp;应用：<label class=\"label label-success\">${appId!}</label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n                          查询时间：<label class=\"label label-success\">${searchDate!}</label>\n                        </h4>\n                      </div>\n                      <div class=\"dropdown-divider\"></div>\n                      <div class=\"row\">\n                        <div class=\"d-flex justify-content-start\">\n                          <#assign client_needSelect=\"0\">\n                          <label class=\"col-auto\">\n                            &nbsp;&nbsp;客户端: &nbsp;&nbsp;\n                          </label>\n                          <#list clientConfigMap?keys as key>\n                            <#assign item = key >\n                            <#if (key_index < 5)>\n                              <div class=\"form-check\">\n                                <input type=\"radio\" name=\"client_optionsRadios\" value=\"${item!}\"\n                                <#if firstClient?? && (firstClient == item)>checked=\"checked\"</#if>\n                                onchange=\"changeClientChart(this.value)\"/>\n                                ${item!}&nbsp;\n                              </div>\n                            <#else>\n                              <#assign client_needSelect=\"1\">\n                            </#if>\n                          </#list>\n                          <#if (client_needSelect?number == 1)>\n                            <label class=\"col-auto\">\n                              &nbsp;&nbsp;&nbsp;其他:\n                            </label>\n                            <div class=\"col-auto\">\n                              <select name=\"optionsRadios\" onchange=\"changeClientChart(this.value)\">\n                                <option>请选择</option>\n                                <#list clientConfigMap?keys as key>\n                                  <#assign item = key >\n                                  <#if (key_index >= 5)>\n                                    <label>\n                                      <option value=\"${item!}\" <#if firstClient?? && (firstClient == item)>selected</#if>>\n                                      ${item!}\n                                      </option>\n                                    </label>\n                                  </#if>\n                                </#list>\n                              </select>\n                            </div>\n                          </#if>\n                        </div>\n                      </div>\n\n                      <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n                      <input type=\"hidden\" id=\"firstClient\" name=\"firstClient\" value=\"${firstClient!}\">\n                      <input type=\"hidden\" id=\"exceptionType\" name=\"exceptionType\" value=1>\n\n                      <div class=\"dropdown-divider\"></div>\n\n                      <div class=\"row\">\n                        <label class=\"col-auto\">\n                          &nbsp;&nbsp;Jedis配置：\n                        </label>\n                        <#if clientConfigMap?? && firstClient?? && clientConfigMap[firstClient]??>\n                          <#assign configs = clientConfigMap[firstClient]>\n                        </#if>\n                        <div class=\"col-auto\">\n                          <select class=\"label label-info w-100\" style=\"border: 0;\">\n                            <#list configs! as item>\n                              <option>${item!}</option>\n                            </#list>\n                          </select>\n                        </div>\n                      </div>\n                    </form>\n                  </div>\n\n                  <script type=\"text/javascript\">\n                    var appId = '${appId!}';\n                    var searchDate = '${searchDate!}';\n                    //应用下某客户端某命令的统计情况\n                    var appClientExceptionStatisticsMap = '${appClientExceptionStatisticsJson!}';\n                    var appClientExceptionStatisticsJson = eval(\"(\" + appClientExceptionStatisticsMap + \")\");\n\n                    Highcharts.setOptions({\n                      global: {\n                        useUTC: false\n                      }\n                    });\n                    Highcharts.setOptions({\n                      colors: ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n                        '#492970', '#804000', '#f28f43', '#77a1e5',\n                        '#c42525', '#a6c96a']\n                    });\n\n                    function changeClientChart(value) {\n                      document.getElementById(\"firstClient\").value = value;\n                      document.getElementById(\"clientCommandStatisticsForm\").submit();\n                    }\n\n                    $(document).ready(\n                            function () {\n                              var data = appClientExceptionStatisticsJson;\n\n                              var count_unit = \"次数\";\n                              var count_appTotalOptions = getOption(\"countContainer\", \"<b>\" + \"命令超时次数</b>\", count_unit);\n                              count_appTotalOptions.series = getClientStatisticsByType(data, 'count', count_unit, searchDate, 1);\n                              var count_appTotalchart = new Highcharts.Chart(count_appTotalOptions);\n\n                              //cost\n                              var cost_unit = \"毫秒\";\n                              var cost_appTotalOptions = getOption(\"costContainer\", \"<b>\" + \"命令超时耗时</b>\", cost_unit);\n                              cost_appTotalOptions.series = getClientStatisticsByType(data, 'cost', cost_unit, searchDate, 1);\n                              var cost_appTotalchart = new Highcharts.Chart(cost_appTotalOptions);\n                            });\n                  </script>\n                  <div class=\"alert alert-warning alert-dismissable\">\n                    提示：点击图中点查看超时命令详情！\n                    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-hidden=\"true\"></button>\n                  </div>\n                  <br/>\n                  <div id=\"countContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n                  <br/>\n                  <div id=\"costContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n                  <br/>\n                  <div class=\"page-header\">\n                    <h4>\n                      redis节点命令超时统计表\n                    </h4>\n                  </div>\n                  <table id=\"instanceDetailTable\" class=\"table table-striped table-hover table-bordered\" style=\"margin-top: 0px\">\n                    <thead>\n                    <tr>\n                      <td>序号</td>\n                      <td>redis实例</td>\n                      <td>超时总次数</td>\n                      <td>平均耗时(单位:毫秒)</td>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#list appNodeExceptionStatisticsList as item>\n                      <tr>\n                        <td>${item_index}</td>\n                        <td>${item.node!}</td>\n                        <td>${item.count!}</td>\n                        <td>${item.cost!}</td>\n                      </tr>\n                    </#list>\n                    </tbody>\n                  </table>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n    <#include \"/inc/footer.html\">\n  </div>\n  <script type=\"text/javascript\">\n    $(function () {\n      $(\"div[name='nodeCmdDetail-modal']\").on('shown.bs.modal', function () {\n        var nodeCmdDetail_id = $(this).attr('id');\n        var nodeFormat = nodeCmdDetail_id.split('_')[1] + \"_\" + nodeCmdDetail_id.split('_')[2];\n        var host = nodeCmdDetail_id.split('_')[1].replace(/-/g, \".\");\n        var port = nodeCmdDetail_id.split('_')[2];\n        var node = host + \":\" + port;\n        var header = \"<h4>&nbsp;&nbsp;redis实例：\" + node + \"</h4>\";\n        $('#header_' + nodeFormat).html(header);\n\n        var searchDate = $('#searchDate').val();\n        var clientIp = $('#firstClient').val();\n        $('#modal-body-tbody-' + nodeFormat).html('');\n        $.post(\n                '${request.contextPath}/client/show/latencyCommandDetail/node',\n                {\n                  client: clientIp,\n                  node: node,\n                  searchDate: searchDate\n                },\n                function (data) {\n                  var result = data.result;\n                  var latencyCommandDetailList = data.latencyCommandDetailList;\n                  if (result != 'success') {\n                  } else {\n                    var tbody = \"\";\n                    $.each(latencyCommandDetailList, function (n, value) {\n                      var trs = \"\";\n                      trs += \"<tr>\"\n                              + \"<td>\" + value.id + \" </td>\"\n                              + \"<td>\" + value.create_time + \" </td>\"\n                              + \"<td>\" + value.command + \"</td >\"\n                              + \"<td>\" + value.args + \"</td >\"\n                              + \"<td>\" + value.size + \"</td >\"\n                              + \" </tr>\";\n                      tbody += trs;\n                    });\n                    $('#modal-body-tbody-' + nodeFormat).append(tbody);\n                  }\n                }\n        );\n      });\n    })\n  </script>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/client/commandStatisticsByClient.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <title>客户端命令调用详情</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n<div class=\"wrapper\">\n    <!-- Header End -->\n  <#include \"/inc/head.html\">\n  <div class=\"content-wrapper ml-0\">\n    <div class=\"content\">\n      <div class=\"container\">\n        <div class=\"row\">\n          <div class=\"col-12\">\n            <div class=\"card\">\n              <div class=\"card-body\">\n                <form method=\"get\" action=\"${request.contextPath}/client/show/commandStatistics/client\" id=\"clientCommandStatisticsForm\">\n                  <div class=\"row\">\n                    <div class=\"d-flex justify-content-end\">\n                      <label class=\"col-form-label\" style=\"font-weight:bold;text-align:left;\">\n                        &nbsp;查询日期:&nbsp;&nbsp;\n                      </label>\n                      <div class=\"col-auto\">\n                        <input class=\"form-control\" type=\"date\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate!}\">\n                      </div>\n                      <label>&nbsp;<input type=\"submit\" class=\"btn btn-info\" value=\"查询\"/></label>\n                    </div>\n                  </div>\n                  <div class=\"row\">\n                    <h4>&nbsp;&nbsp;应用：<label class=\"label label-success\">${appId!}</label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n                      查询时间：<label class=\"label label-success\">${searchDate!}</label>\n                    </h4>\n                  </div>\n                  <div class=\"dropdown-divider\"></div>\n\n                  <div class=\"row\">\n                    <div class=\"d-flex justify-content-start\">\n                      <#assign client_needSelect=\"0\">\n                      <label class=\"col-auto\">\n                        &nbsp;&nbsp;客户端: &nbsp;&nbsp;\n                      </label>\n                      <#list clientList as item>\n                        <#if (item_index < 5)>\n                          <div class=\"form-check\">\n                            <input type=\"radio\" class=\"form-check-input\" name=\"client_optionsRadios\" value=\"${item!}\"\n                            <#if firstClient?? && (firstClient == item)>checked=\"checked\"</#if>\n                            onchange=\"changeClientChart(this.value)\"/>\n                            ${item!}&nbsp;&nbsp;\n                          </div>\n                        <#else>\n                            <#assign client_needSelect = '1'>\n                        </#if>\n                      </#list>\n                      <div class=\"col-auto\">\n                        <#if (client_needSelect?number == 1)>\n                          <div class=\"row\">\n                            <label class=\"col-auto\">\n                            &nbsp;其他:\n                            </label>\n                            <div class=\"col-auto\">\n                              <select name=\"optionsRadios\" onchange=\"changeClientChart(this.value)\">\n                                <option>请选择</option>\n                                <#list clientList as item>\n                                  <#if (item_index >= 5)>\n                                    <label>\n                                      <option value=\"${item!}\" <#if firstClient?? && (firstClient == item)>selected</#if>>\n                                      ${item!}\n                                      </option>\n                                    </label>\n                                  </#if>\n                                </#list>\n                              </select>\n                            </div>\n                          </div>\n                        </#if>\n                      </div>\n                      <div class=\"form-check\">\n                        <input type=\"radio\" class=\"form-check-input\" name=\"client_optionsRadios\" value=\"all\"\n                        <#if firstClient?? && (firstClient == 'all')>checked=\"checked\"</#if>\n                        onchange=\"changeClientChart(this.value)\"/>all\n                      </div>\n                    </div>\n                  </div>\n                  <div class=\"dropdown-divider\"></div>\n\n\n                  <div class=\"row\">\n                    <div class=\"d-flex justify-content-start\">\n                      <#assign command_needSelect = \"0\">\n                      <label class=\"col-auto\">\n                        &nbsp;&nbsp;命令: &nbsp;&nbsp;\n                      </label>\n                      <#list commandList as item>\n                        <#if (item_index < 5)>\n                          <div class=\"form-check\">\n                            <input type=\"radio\" class=\"form-check-input\" name=\"optionsRadios\" value=\"${item!}\"\n                            <#if firstCommand?? && (firstCommand == item)>checked=\"checked\"</#if>\n                            onchange=\"changeCommandChart(this.value)\"/>\n                            ${item!}&nbsp;&nbsp;\n                          </div>\n                        <#else>\n                          <#assign command_needSelect=\"1\">\n                        </#if>\n                      </#list>\n                      <div class=\"col-auto\">\n                        <#if (command_needSelect?number == 1)>\n                          <div class=\"row\">\n                            <label class=\"col-auto\">\n                              &nbsp;其他:\n                            </label>\n                            <div class=\"col-auto\">\n                              <select name=\"command_optionsRadios\" onchange=\"changeCommandChart(this.value)\">\n                                <option>请选择</option>\n                                <#list commandList as item>\n                                  <#if (item_index >= 5)>\n                                    <label>\n                                      <option value=\"${item!}\" <#if firstCommand?? && (firstCommand == item)>selected</#if>>\n                                      ${item!}\n                                      </option>\n                                    </label>\n                                  </#if>\n                                </#list>\n                              </select>\n                            </div>\n                          </div>\n                        </#if>\n                      </div>\n                      <div class=\"form-check\">\n                        <input type=\"radio\" class=\"form-check-input\" name=\"optionsRadios\" value=\"all\"\n                        <#if firstCommand?? && (firstCommand == 'all')>checked=\"checked\"</#if>\n                        onchange=\"changeCommandChart(this.value)\"/>all\n                      </div>\n                    </div>\n                  </div>\n                  <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n                  <input type=\"hidden\" id=\"firstCommand\" name=\"firstCommand\" value=\"${firstCommand!}\">\n                  <input type=\"hidden\" id=\"firstClient\" name=\"firstClient\" value=\"${firstClient!}\">\n                </form>\n              </div>\n            </div>\n\n            <script type=\"text/javascript\">\n              var searchDate = '${searchDate!}';\n              var firstCommand = '${firstCommand!}';\n              var firstClient = '${firstClient!}';\n\n              Highcharts.setOptions({\n                global: {\n                  useUTC: false\n                }\n              });\n              Highcharts.setOptions({\n                colors: ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n                  '#492970', '#804000', '#f28f43', '#77a1e5',\n                  '#c42525', '#a6c96a']\n              });\n\n              function changeCommandChart(value) {\n                document.getElementById(\"firstCommand\").value = value;\n                document.getElementById(\"clientCommandStatisticsForm\").submit();\n              }\n\n              function changeClientChart(value) {\n                document.getElementById(\"firstClient\").value = value;\n                document.getElementById(\"clientCommandStatisticsForm\").submit();\n              }\n\n\n              $(document).ready(\n                      function () {\n                        if (firstClient == 'all') {\n                          var sumCommandStatJson = '${sumCommandStatJson!}';\n                          var sumCommandStatList = eval(\"(\" + sumCommandStatJson + \")\");\n                          var tbody = \"\";\n                          $.each(sumCommandStatList, function (n, value) {\n                            console.log(\"value:\" + value);\n                            var trs = \"\";\n                            trs += \"<tr>\"\n                                    + \"<td>\" + (n + 1) + \" </td>\"\n                                    + \"<td>\" + value.client_ip + \" </td>\"\n                                    + \"<td>\" + value.count + \"</td >\"\n                                    + \"<td>\" + value.cost + \"</td >\"\n                                    + \" </tr>\";\n                            tbody += trs;\n                          });\n                          $('#sumTable-tbody').append(tbody);\n                          $(\"#countContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#byteContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#byteOutContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#costContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#sumTable\").removeAttr(\"hidden\");\n                        } else if (firstCommand == 'all') {\n                          var sumClientStatJson = '${sumClientStatJson!}';\n                          var sumClientStatList = eval(\"(\" + sumClientStatJson + \")\");\n                          var tbody = \"\";\n                          $.each(sumClientStatList, function (n, value) {\n                            console.log(\"value:\" + value);\n                            var trs = \"\";\n                            trs += \"<tr>\"\n                                    + \"<td>\" + n + \" </td>\"\n                                    + \"<td>\" + value.command + \" </td>\"\n                                    + \"<td>\" + value.count + \"</td >\"\n                                    + \"<td>\" + value.cost + \"</td >\"\n                                    + \" </tr>\";\n                            tbody += trs;\n                          });\n                          $('#sumTable-tbody').append(tbody);\n                          $(\"#countContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#byteContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#byteOutContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#costContainer\").attr(\"hidden\", \"hidden\");\n                          $(\"#sumTable\").removeAttr(\"hidden\");\n                        } else {\n                          $(\"#sumTable\").attr(\"hidden\", \"hidden\");\n                          var appClientCommandStatisticsMap = '${appClientCommandStatisticsJson!}';\n                          var appClientCommandStatisticsJson = eval(\"(\" + appClientCommandStatisticsMap + \")\");\n\n                          var data = appClientCommandStatisticsJson;\n\n                          var count_unit = \"次数\";\n                          var count_appTotalOptions = getOption(\"countContainer\", \"<b>\" + firstCommand + \"命令调用次数</b>\", count_unit);\n                          count_appTotalOptions.series = getClientStatisticsByType(data, 'count', count_unit, searchDate);\n                          var count_appTotalchart = new Highcharts.Chart(count_appTotalOptions);\n\n                          //cost\n                          var cost_unit = \"毫秒\";\n                          var cost_appTotalOptions = getOption(\"costContainer\", \"<b>\" + firstCommand + \"命令平均耗时</b>\", cost_unit);\n                          cost_appTotalOptions.series = getClientStatisticsByType(data, 'cost', cost_unit, searchDate);\n                          var cost_appTotalchart = new Highcharts.Chart(cost_appTotalOptions);\n\n                          //bytesIn\n                          var byte_unit = \"MB\";\n                          var byte_appTotalOptions = getOption(\"byteContainer\", \"<b>\" + firstCommand + \"命令输入流量</b>\", byte_unit);\n                          byte_appTotalOptions.series = getClientStatisticsByType(data, 'bytesIn', byte_unit, searchDate);\n                          var byte_appTotalchart = new Highcharts.Chart(byte_appTotalOptions);\n\n                          //bytesOut\n                          var byteOut_appTotalOptions = getOption(\"byteOutContainer\", \"<b>\" + firstCommand + \"命令输出流量</b>\", byte_unit);\n                          byteOut_appTotalOptions.series = getClientStatisticsByType(data, 'bytesOut', byte_unit, searchDate);\n                          var byteOut_appTotalOptions = new Highcharts.Chart(byteOut_appTotalOptions);\n\n                        }\n                      });\n            </script>\n\n            <div id=\"sumTable\" style=\"max-width: 100%; min-width: 310px; margin: 0 auto\">\n              <div class=\"card\">\n                <div class=\"card-header\">\n                  <h5 class=\"card-title text-center\">\n                    <#if firstClient?? && (firstClient=='all')>命令&nbsp;${firstCommand!}</#if>\n                    <#if firstCommand?? && (firstCommand=='all')>客户端&nbsp;${firstClient!}</#if>\n                    &nbsp;--&nbsp;分布统计表\n                  </h5>\n                </div>\n                <div class=\"card-body\">\n                  <table class=\"table table-striped table-hover table-bordered\">\n                    <thead>\n                    <tr>\n                      <th scope=\"col\">序号</th>\n                      <th scope=\"col\">\n                        <#if firstClient?? && (firstClient == 'all')>客户端\n                        <#elseif firstCommand?? && (firstCommand == 'all')>命令\n                        </#if>\n                      </th>\n                      <th scope=\"col\">调用总次数</th>\n                      <th scope=\"col\">平均耗时(单位:毫秒)</th>\n                    </tr>\n                    </thead>\n                    <tbody id=\"sumTable-tbody\"></tbody>\n                  </table>\n                </div>\n              </div>\n            </div>\n\n            <div id=\"countContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n            <div id=\"costContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n            <div id=\"byteContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n            <div id=\"byteOutContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <#include \"/inc/footer.html\">\n  </div>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/client/connExceptionStatisticsByClient.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <title>客户端连接异常统计</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <!-- Header End -->\n    <#include \"/inc/head.html\">\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"container\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <div class=\"card-body\">\n                  <div class=\"row\">\n                    <form method=\"get\" action=\"${request.contextPath}/client/show/exceptionStatistics/client\" id=\"clientExceptionStatisticsForm\">\n                      <div class=\"row\">\n                        <div class=\"d-flex justify-content-end\">\n                          <label class=\"col-form-label\" style=\"font-weight:bold;text-align:left;\">\n                            &nbsp;查询日期:&nbsp;&nbsp;\n                          </label>\n                          <div class=\"col-auto\">\n                            <input type=\"date\" class=\"form-control\" size=\"20\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate!}\">\n                          </div>\n                          <label>&nbsp;<input type=\"submit\" class=\"btn btn-info\" value=\"查询\"/></label>\n                        </div>\n                      </div>\n                      <div class=\"row\">\n                        <h4>&nbsp;&nbsp;应用：<label class=\"label label-success\">${appId!}</label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n                          查询时间：<label class=\"label label-success\">${searchDate!}</label>\n                        </h4>\n                      </div>\n                      <div class=\"dropdown-divider\"></div>\n\n                      <div class=\"row card-title\">\n                        <div class=\"d-flex justify-content-start\">\n                          <#assign client_needSelect=\"0\">\n                          <label class=\"col-auto\">\n                            &nbsp;客户端:\n                          </label>\n                          <#list clientConfigMap?keys as key>\n                            <#assign item=key>\n                            <#if (key_index < 5)>\n                              <div class=\"col-auto form-check\">\n                                <input type=\"radio\" name=\"client_optionsRadios\" value=\"${item!}\"\n                                <#if firstClient?? && (firstClient == item)>checked=\"checked\"</#if>\n                                  onchange=\"changeClientChart(this.value)\"/>\n                                  ${item!}&nbsp;\n                              </div>\n                            <#else>\n                              <#assign client_needSelect=\"1\">\n                            </#if>\n                          </#list>\n                          <div class=\"col-auto\">\n                            <#if (client_needSelect?number == 1)>\n                              <div class=\"row\">\n                                <label class=\"col-auto\">\n                                  &nbsp;其他:\n                                </label>\n                                <div class=\"col-auto\">\n                                  <select name=\"optionsRadios\" onchange=\"changeClientChart(this.value)\">\n                                    <option>请选择</option>\n                                    <#list clientConfigMap?keys as key>\n                                      <#assign item=key>\n                                      <#if (key_index >= 5)>\n                                        <label>\n                                          <option value=\"${item!}\" <#if firstClient?? && (firstClient == item)>selected</#if>>\n                                          ${item!}\n                                          </option>\n                                        </label>\n                                      </#if>\n                                    </#list>\n                                  </select>\n                                </div>\n                              </div>\n                            </#if>\n                          </div>\n                        </div>\n                      </div>\n                      <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n                      <input type=\"hidden\" id=\"firstClient\" name=\"firstClient\" value=\"${firstClient!}\">\n                      <input type=\"hidden\" id=\"exceptionType\" name=\"exceptionType\" value=0>\n                    </form>\n                    <div class=\"dropdown-divider\"></div>\n                  </div>\n\n                  <script type=\"text/javascript\">\n                    var searchDate = '${searchDate}';\n                    //应用下某客户端某命令的统计情况\n                    var appClientExceptionStatisticsMap = '${appClientExceptionStatisticsJson}';\n                    var appClientExceptionStatisticsJson = eval(\"(\" + appClientExceptionStatisticsMap + \")\");\n\n                    Highcharts.setOptions({\n                      global: {\n                        useUTC: false\n                      }\n                    });\n                    Highcharts.setOptions({\n                      colors: ['#2f7ed8', '#E3170D', '#0d233a', '#8bbc21', '#1aadce',\n                        '#492970', '#804000', '#f28f43', '#77a1e5',\n                        '#c42525', '#a6c96a']\n                    });\n\n                    function changeClientChart(value) {\n                      document.getElementById(\"firstClient\").value = value;\n                      document.getElementById(\"clientExceptionStatisticsForm\").submit();\n                    }\n\n                    $(document).ready(\n                            function () {\n                              var data = appClientExceptionStatisticsJson;\n\n                              var count_unit = \"次数\";\n                              var count_appTotalOptions = getOption(\"countContainer\", \"<b>\" + \"连接异常次数</b>\", count_unit);\n                              count_appTotalOptions.series = getClientStatisticsByType(data, 'count', count_unit, searchDate);\n                              var count_appTotalchart = new Highcharts.Chart(count_appTotalOptions);\n\n                              //cost\n                              var cost_unit = \"毫秒\";\n                              var cost_appTotalOptions = getOption(\"costContainer\", \"<b>\" + \"连接异常平均耗时</b>\", cost_unit);\n                              cost_appTotalOptions.series = getClientStatisticsByType(data, 'cost', cost_unit, searchDate);\n                              var cost_appTotalchart = new Highcharts.Chart(cost_appTotalOptions);\n                            });\n                  </script>\n\n                  <div class=\"row\">\n                    <label class=\"col-auto\">\n                      &nbsp;&nbsp;Jedis配置：\n                    </label>\n                    <#if firstClient?? && clientConfigMap?? && clientConfigMap[firstClient]??>\n                      <#assign configs = clientConfigMap[firstClient]>\n                    </#if>\n                    <div class=\"col-auto\">\n                      <select class=\"label label-info w-100\" style=\"border: 0;\">\n                        <#list configs! as item>\n                          <option>${item!}</option>\n                        </#list>\n                      </select>\n                    </div>\n                  </div>\n                </div>\n              </div>\n              <div id=\"countContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n              <br/>\n              <div id=\"costContainer\" style=\"max-width: 100%; min-width: 310px; height: 350px; margin: 0 auto\"></div>\n              <br/>\n              <div class=\"card\">\n                <div class=\"card-header\">\n                  <h4 class=\"card-title\">\n                    redis节点连接异常统计表\n                  </h4>\n                </div>\n                <div class=\"card-body table-responsive\">\n                  <table id=\"instanceDetailTable\" class=\"table table-striped table-hover table-bordered\" style=\"margin-top: 0px\">\n                    <thead>\n                    <tr>\n                      <td>序号</td>\n                      <td>redis实例</td>\n                      <td>异常总次数</td>\n                      <td>平均耗时(单位:毫秒)</td>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#list appNodeExceptionStatisticsList as item>\n                      <tr>\n                        <td>${item_index}</td>\n                        <td>${item.node!}</td>\n                        <td>${item.count!}</td>\n                        <td>${item.cost!}</td>\n                      </tr>\n                    </#list>\n                    </tbody>\n                  </table>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n    <#include \"/inc/footer.html\">\n  </div>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/error.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\" />\n\t<title>CacheCloud异常</title>\n\t<meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n\t<#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n\t<div class=\"wrapper\">\n\t\t<#include '/inc/head.html'>\n\t\t<div class=\"content-wrapper ml-0\">\n\t\t\t<div class=\"content\">\n\t\t\t\t<div class=\"container-fluid\">\n\t\t\t\t\t<div class=\"row\">\n\t\t\t\t\t\t<div class=\"col-12\">\n\t\t\t\t\t\t\t<div class=\"card\">\n\t\t\t\t\t\t\t\t<div class=\"card-body\">\n\t\t\t\t\t\t\t\t\t<h4>\n\t\t\t\t\t\t\t\t\t\t<img width=\"70\" height=\"60\" src=\"${request.contextPath}/assets/img/cry.jpg\">\n\t\t\t\t\t\t\t\t\t\t出错了！请联系我们。\n\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/expAppsDaily.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>应用日报</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n\n    <tr>\n        <td>\n        </td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n\n            <ul>\n                <li>\n                    <span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">\n                        应用异常情况\n                        <a target=\"_blank\" href=\"${ccDomain}/manage/app/stat/list?tabId=0&searchDate=${searchDate}\">【查看更多--后台】</a>\n                    </span>\n                </li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        应用负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        异常总量(全天)\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        连接异常(全天)\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        命令超时(全天)\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        延迟事件(全天)\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        慢查询(全天)\n                    </td>\n                </tr>\n                <#assign expAppStats=appClientGatherStatGroup[\"expAppStats\"]>\n                <#list expAppStats as item>\n                    <tr>\n                        <#assign appid=item[\"app_id\"]?c>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\" href=\"${ccDomain}/admin/app/index?appId=${appid}\">\n                                ${appid}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                            ${appDescMap[appid].officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/client/show/index?searchDate=${searchDate}&appId=${appid}\"\n                               &tabTag=app_client_exception_statistics\">\n                                ${item[\"exp_count\"]}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/client/show/exceptionStatistics/client?searchDate=${searchDate}&appId=${appid}&exceptionType=0\">\n                                次数:${item[\"conn_exp_count\"]}\n                            </a>\n                            <br/>\n                            平均耗时(ms):${item['avg_conn_exp_cost']}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/client/show/exceptionStatistics/client?searchDate=${searchDate}&appId=${appid}&exceptionType=1\">\n                                次数:${item[\"cmd_exp_count\"]}\n                            </a>\n                            <br/>\n                            平均耗时(ms):${item[\"avg_cmd_exp_cost\"]}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?searchDate=${searchDate}&appId=${appid}&tabTag=app_latency\">\n                                ${item[\"latency_count\"]}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?searchDate=${searchDate}&appId=${appid}&tabTag=app_latency\">\n                                ${item[\"slow_log_count\"]}\n                            </a>\n                        </td>\n                    </tr>\n                </#list>\n            </table>\n\n\n            <ul>\n                <li>\n                    <span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">\n                    应用延迟事件（top 10）\n                        <a target=\"_blank\" href=\"${ccDomain}/manage/app/stat/list?tabId=0&searchDate=${searchDate}\">【查看更多--后台】</a>\n                    </span>\n                </li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        应用负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        延迟事件(全天)\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        慢查询(全天)\n                    </td>\n                </tr>\n                <#assign latencyAppStats=appClientGatherStatGroup[\"latencyAppStats\"]>\n                <#list latencyAppStats as item>\n                    <tr>\n                        <#assign appid=item[\"app_id\"]?c>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?appId=${appid}\">\n                                ${appid}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                            ${appDescMap[appid].officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?searchDate=${searchDate}&appId=${appid}&tabTag=app_latency\">\n                                ${item['latency_count']}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?searchDate=${searchDate}&appId=${appid}&tabTag=app_latency\">\n                                ${item['slow_log_count']}\n                            </a>\n                        </td>\n                    </tr>\n                </#list>\n            </table>\n\n            <ul>\n                <li>\n                    <span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">\n                    应用拓扑诊断\n                        <a target=\"_blank\"\n                           href=\"${ccDomain}/manage/app/stat/list/server?tabId=3&searchDate=${searchDate}\">【查看更多--后台】</a>\n                    </span>\n                </li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        应用负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        redis类型\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        redis版本\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        拓扑诊断结果\n                    </td>\n                </tr>\n                <#assign topologyAppStats=appClientGatherStatGroup[\"topologyAppStats\"]>\n                <#list topologyAppStats as item>\n                    <tr>\n                        <#assign appid=item[\"app_id\"]?c>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?appId=${appid}\">\n                                ${appid}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                            ${appDescMap[appid].officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].typeDesc!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].versionName!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <span style=\"color:red\">异常</span>\n                            [<a target=\"_blank\"\n                                href=\"${ccDomain}/manage/app/index?appId=${appid}&tabTag=app_ops_tool\">查看诊断</a>]\n                        </td>\n                    </tr>\n                </#list>\n            </table>\n\n            <ul>\n                <li>\n                    <span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">\n                    宿主环境检测\n                        <a target=\"_blank\"\n                           href=\"${ccDomain}/manage/app/stat/list/server?tabId=5&searchDate=${searchDate}\">【查看更多--后台】</a>\n                    </span>\n                </li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 70px;\">\n                        宿主机ip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 70px;\">\n                        cachecloud_nprocs\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        fsync_slow_times\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        somaxconn\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        redis实例数\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        文件句柄used/total\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                        disk used\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        诊断结果\n                    </td>\n                </tr>\n            <#assign machineEnvs=exceptionMachineEnv[\"host\"]>\n            <#list machineEnvs as machine>\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 70px;\">\n                        ${machine.ip}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 70px;\">\n                       ${machine.envs.nproc_threads}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${machine.envs.fsync_delay_times}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                        ${machine.envs.somaxconn}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${machine.envs.instanceNum}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${machine.envs.unlimit_used}/${machine.envs.unlimit}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                        ${machine.envs.diskUsed}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        <span style=\"color:red\">异常</span>\n                    </td>\n                </tr>\n            </#list>\n            </table>\n\n            <ul>\n                <li>\n                    <span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">\n                    容器环境检测\n                        <a target=\"_blank\"\n                           href=\"${ccDomain}/manage/app/stat/list/server?tabId=4&searchDate=${searchDate}\">【查看更多--后台】</a>\n                    </span>\n                </li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 70px;\">\n                        容器ip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 70px;\">\n                        overcommit_memory\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 120px;/\">\n                        thp_enabled\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 120px;/\">\n                        thp_defrag\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        swappiness\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                        nproc\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        诊断结果\n                    </td>\n                </tr>\n            <#assign machineEnvs=exceptionMachineEnv[\"container\"]>\n            <#list machineEnvs as container>\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 70px;\">\n                        ${container.ip}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${container.envs.overcommit_memory}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                        ${container.envs.transparent_hugepage_enable}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${container.envs.transparent_hugepage_defrag}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${container.envs.swappines}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                        ${container.envs.nproc}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        <span style=\"color:red\">异常</span>\n                    </td>\n                </tr>\n            </#list>\n            </table>\n\n            <ul>\n                <li>\n                    <span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">\n                        应用内存使用情况（bottom 10）\n                        <a target=\"_blank\"\n                           href=\"${ccDomain}/manage/app/stat/list/server?tabId=1&searchDate=${searchDate}\">【查看更多--后台】</a>\n                </span>\n                </li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        应用负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        redis类型\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        内存\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        使用内存\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        内存使用率\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用客户端连接\n                    </td>\n                </tr>\n                <#assign memAlterAppStats=appClientGatherStatGroup[\"memAlterAppStats\"]>\n                <#list memAlterAppStats as item>\n                    <tr>\n                        <#assign appid=item[\"app_id\"]?c>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?appId=${appid}\">\n                                ${appid}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                            ${appDescMap[appid].officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].typeDesc!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item[\"format_mem\"]} G\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item[\"format_used_memory\"]}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item[\"mem_used_ratio\"]} %\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a href=\"${ccDomain}/admin/app/index?appId=${appid}&tabTag=app_clientList\"\n                               target=\"_blank\">\n                                ${item[\"connected_clients\"]}\n                            </a>\n                        </td>\n                    </tr>\n                </#list>\n            </table>\n\n\n            <ul>\n                <li>\n                    <span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">\n                        应用碎片率情况（top 10）\n                        <a target=\"_blank\"\n                           href=\"${ccDomain}/manage/app/stat/list/server?tabId=2&searchDate=${searchDate}\">【查看更多--后台】</a>\n                </span>\n                </li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 150px;/\">\n                        应用负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        redis版本\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        内存使用\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        rss内存使用\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        平均碎片率\n                    </td>\n                </tr>\n                <#assign fragRatioAppStats=appClientGatherStatGroup[\"fragRatioAppStats\"]>\n                <#list fragRatioAppStats as item>\n                    <tr>\n                        <#assign appid=item[\"app_id\"]?c>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a target=\"_blank\"\n                               href=\"${ccDomain}/admin/app/index?appId=${appid}\">\n                                ${appid}\n                            </a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                            ${appDescMap[appid].officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${appDescMap[appid].versionName!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item[\"format_used_memory\"]} G\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item[\"format_used_memory_rss\"]} G\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item[\"avg_mem_frag_ratio\"]}\n                        </td>\n                    </tr>\n                </#list>\n            </table>\n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/contact.html",
    "content": "<ul>\n    <#if contact??>\n        ${contact?html}\n    </#if>\n</ul>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/daily.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap/js/bootstrap.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap/js/bootstrap.bundle.min.js\"></script>\n\n<!-- Google Font: Source Sans Pro -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/vendor/fonts/fonts-googleapis-source-sans-pro.css\">\n<!-- Font Awesome -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/fontawesome-free/css/all.min.css\">\n<!-- Theme style -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/dist/css/adminlte.min.css\">\n<!-- overlayScrollbars -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/overlayScrollbars/css/OverlayScrollbars.min.css\">\n\n<link href=\"${request.contextPath}/assets/vendor/bootstrap/css/bootstrap.min.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-icons/bootstrap-icons.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.css\" rel=\"stylesheet\" />\n<head>\n    <title>CacheCloud值班电话</title>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n    <div class=\"wrapper\">\n        <#include '/inc/head.html'>\n        <div class=\"content-wrapper ml-0\">\n            <div class=\"content\">\n                <div class=\"container\">\n                    <div class=\"card\">\n                        <div class=\"card-header\">\n                            <h4 class=\"card-title\">\n                                值班联系人:\n                            </h4>\n                        </div>\n                        <div class=\"card-body\">\n                            <div id=\"contact\" class=\"page-body\">\n                               <div class=\"well\">\n                                    <#include '/inc/contact.html'>\n                               </div>\n                            </div>\n\n                            <div id=\"contactWechat\" class=\"page-body\">\n                               <h5>微信群：</h5>\n                               <div class=\"well\">\n                                    <img width=\"200px\" height=\"200px\" src=\"http://photocdn.tv.sohu.com/img/cachecloud/weixin.jpg?time=<%=System.currentTimeMillis()%>\"/>\n                               </div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\t    </div>\n    </div>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/footer.html",
    "content": "<!-- ======= Footer ======= -->\n<footer id=\"footer\" class=\"main-footer ml-0\">\n    <strong>Copyright &copy; Cachecloud.</strong>\n    All Rights Reserved.\n    <div class=\"float-right d-none d-sm-inline-block\">\n        <b>Version</b> 3.0.0\n    </div>\n</footer><!-- End Footer -->"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/frontResources.html",
    "content": "<!-- Vendor CSS Files -->\n<link href=\"${request.contextPath}/assets/vendor/bootstrap/css/bootstrap.min.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-icons/bootstrap-icons.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.css\" rel=\"stylesheet\" />\n<link href=\"${request.contextPath}/assets/vendor/boxicons/css/boxicons.min.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/quill/quill.snow.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/quill/quill.bubble.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/remixicon/remixicon.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/simple-datatables/style.css\" rel=\"stylesheet\">\n<!-- Customized Bootstrap Stylesheet -->\n\n<!-- Google Font: Source Sans Pro -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/vendor/fonts/fonts-googleapis-source-sans-pro.css\">\n<!-- Font Awesome -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/fontawesome-free/css/all.min.css\">\n<!-- Theme style -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/dist/css/adminlte.min.css\">\n<!-- overlayScrollbars -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/overlayScrollbars/css/OverlayScrollbars.min.css\">\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/css/custom.css\">\n\n<script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/apexcharts/apexcharts.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap/js/bootstrap.bundle.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/chart.js/chart.umd.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/echarts/echarts.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/highchart/highcharts.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/quill/quill.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/simple-datatables/simple-datatables.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/tinymce/tinymce.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/php-email-form/validate.js\"></script>\n<script src=\"${request.contextPath}/assets/js/myhighchart.js\"></script>\n<script src=\"${request.contextPath}/assets/js/custom/appDetail.js\"></script>\n<script src=\"${request.contextPath}/assets/js/custom/appInit.js\"></script>\n<script src=\"${request.contextPath}/assets/dist/js/adminlte.js\"></script>\n<script type=\"text/javascript\">\n    $(function () {\n        if (typeof (alert) != 'undefined') {\n            if (alert.isAlert == 0) {\n                return;\n            }\n            var url = '${request.contextPath}/manage/notice/get.json';\n            $.get(url, {}, function (data) {\n                console.log(data);\n                if (data.status == 1) {\n                    var alertDiv = $('<div class=\"alert alert-warning alert-dismissable\"></div>');\n                    $('<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times;</button>').appendTo(alertDiv);\n                    $('<p><strong>系统提醒：</strong></p>').appendTo(alertDiv);\n                    var i = 0;\n                    list = data.data;\n                    for (var value in list) {\n                        i++;\n                        $('<p>' + list[value] + '</p>').appendTo(alertDiv);\n                    }\n                    alertDiv.appendTo($('#systemAlert'));\n                }\n            });\n        }\n    });\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/head.html",
    "content": "<!-- ======= Header ======= -->\n<header id=\"header\" class=\"header fixed-top\">\n    <!-- Navbar -->\n    <nav class=\"main-header navbar navbar-expand-md ml-0 bg-gradient-light\">\n        <div class=\"container\">\n            <a href=\"${request.contextPath}/\" class=\"navbar-brand text-primary\">\n                <img src=\"${request.contextPath}/assets/img/logo_new.png\" alt=\"Cachecloud Logo\" class=\"brand-image img-lg img-rounded elevation-0 w-auto\" style=\"opacity: .8; height: 33px;\">\n            </a>\n\n            <div class=\"collapse navbar-collapse order-3\" id=\"navbarCollapse\">\n                <!-- Left navbar links -->\n                <ul class=\"navbar-nav\">\n                    <li class=\"nav-item\">\n                        <a href=\"${request.contextPath}/\" class=\"nav-link text-primary fs-6\"><strong><font color=\"#000BFF\">Home</font></strong></a>\n                    </li>\n                    <li class=\"nav-item\">\n                        <a target=\"_blank\" href=\"https://github.com/sohutv/cachecloud\" class=\"nav-link fs-6\"><strong><font color=\"#000BFF\">Github</font></strong></a>\n                    </li>\n                    <li class=\"nav-item\">\n                        <a target=\"_blank\" href=\"${request.contextPath}/wiki/quickstart/index\" class=\"nav-link text-primary fs-6\"><strong><font color=\"#000BFF\">Docs</font></strong></a>\n                    </li>\n                    <li class=\"nav-item\">\n                        <a target=\"_blank\" href=\"${request.contextPath}/web/resource/inc/daily\" class=\"nav-link text-primary fs-6\"><strong><font color=\"#000BFF\">Contact</font></strong></a>\n                    </li>\n                </ul>\n            </div>\n\n            <!-- Right navbar links -->\n            <ul class=\"order-1 order-md-3 navbar-nav navbar-no-expand ml-auto\">\n                <!-- Dropdown Menu -->\n                <li class=\"nav-item dropdown\">\n                    <a class=\"nav-link text-primary\" data-bs-toggle=\"dropdown\" href=\"#\">\n                        <strong>\n                            <i class=\"bi bi-gear\"></i>\n                            <span class=\"dropdown-toggle ps-2 fs-6\">\n                                <font color=\"#000BFF\">\n                                <#if userInfo??>\n                                    ${userInfo.chName!}\n                                <#else>\n                                </#if>\n                                </font>\n                            </span>\n                        </strong>\n                    </a>\n                    <div class=\"dropdown-menu dropdown-menu-lg dropdown-menu-right\">\n                        <#if (userInfo?? && (userInfo.type == 0))>\n                            <a target=\"_blank\" href=\"${request.contextPath}/manage/total/statlist\" class=\"dropdown-item\">\n                                <i class=\"bi bi-grid-fill\"></i>&nbsp;&nbsp;&nbsp;&nbsp;<span>管理后台</span>\n                            </a>\n                            <div class=\"dropdown-divider\"></div>\n                        </#if>\n                        <div class=\"dropdown-divider\"></div>\n                        <a class=\"dropdown-item\" href=\"${request.contextPath}/admin/app/list\">\n                            <i class=\"bi bi-card-list\"></i>&nbsp;&nbsp;&nbsp;&nbsp;<span>应用列表</span>\n                        </a>\n                        <div class=\"dropdown-divider\"></div>\n                        <a target=\"_blank\" href=\"${request.contextPath}/admin/app/jobs\" class=\"dropdown-item\">\n                            <i class=\"bi bi-list-task\"></i>&nbsp;&nbsp;&nbsp;&nbsp;<span>我的申请</span>\n                        </a>\n                        <div class=\"dropdown-divider\"></div>\n                        <a href=\"${request.contextPath}/manage/logout\" class=\"dropdown-item\">\n                            <i class=\"bi bi-box-arrow-right\"></i>&nbsp;&nbsp;&nbsp;&nbsp;<span>注销</span>\n                        </a>\n                    </div>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link text-primary\" data-widget=\"fullscreen\" href=\"#\" role=\"button\">\n                        <i class=\"fas fa-expand-arrows-alt\"></i>\n                    </a>\n                </li>\n            </ul>\n        </div>\n    </nav>\n    <!-- /.navbar -->\n</header>\n<!-- Navbar End -->"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/page.html",
    "content": "<script type='text/javascript'>\n    $(function(){\n        refreshPages(${page.totalPages},${page.pageNo})\n    });\n\n    function refreshPages(totalPage, currentPage) {\n        //安全判断\n        if (currentPage < 1 ) {\n            currentPage = 1;\n        }else if (currentPage > totalPage) {\n            currentPage = totalPage;\n        }\n        var paginationInfo = getPagination(totalPage, currentPage);\n        //用id选择器写入页码部分代码（根据需求修改）\n        $(\"#nav_navigation\").html(paginationInfo);\n    }\n\n    function getPagination(totalPage, currentPage){\n\n        var paginationInfo = \"<ul class='pagination' style='flex-grow: 1'>\";\n        if (currentPage == 1) {\n            paginationInfo += \"<li class='page-item disabled'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage-1) + \")'\"+\">«</a></li>\";\n        }else {\n            //前一页\n            paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage-1) + \")'\"+\">«</a></li>\";\n        }\n\n        if(totalPage<=10){\n            //totalPage<=10\n            for(var i=1; i<=totalPage; i++){\n                if (i == currentPage) {\n                    paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \" </a></li>\";\n                }else {\n                    paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \" </a></li>\";\n                }\n            }\n        }\n        else{\n            //totalPage > 10\n            if(currentPage<=3){\n                for(var i=1; i<=currentPage+2; i++){\n                    //页码1、2\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    } else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    }\n                }\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后一页的页码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + totalPage + \")'>\" + totalPage + \"</a></li>\";\n            }else if(currentPage<=totalPage-5){\n                //totalPage > 10   currentPage > 3 currentPage<=totalPage-5，  页码在中间部分\n                //页码为1的代码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(1)'>\" + 1 + \"</a></li>\";\n\n                //页码1后面的省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n\n                //中间部分代码\n                for(var i=currentPage-1; i<=currentPage+2; i++){\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    } else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    }\n                }\n                //后面的省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后一个页码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + totalPage + \")'>\"+totalPage+\"</a></li>\";\n            }else{\n                //totalPage > 10  并且currentPage > totalPage-5 显示后面的页码\n\n                //页码1\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(1)'>\"+1+\"</a></li>\";\n                //省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后几位页码\n                for(var i=currentPage-1; i<=totalPage; i++){\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \"'>\"+i+\"</a></li>\";\n                    }else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\"+i+\"</a></li>\";\n                    }\n                }\n            }\n        }\n\n        //下一页\n        if (currentPage == totalPage) {\n            paginationInfo += \"<li class='page-item disabled'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage + 1) + \")'\" + \">»</a></li>\";\n        } else {\n            paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage+1) + \")'\"+\">»</a></li>\";\n        }\n        paginationInfo += \"</ul>\";\n        //返回结果\n        return paginationInfo;\n    }\n\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/wikiHead.html",
    "content": "<!-- Navbar -->\n<nav class=\"main-header navbar navbar-expand navbar-white navbar-light\">\n    <!-- Left navbar links -->\n    <ul class=\"navbar-nav\">\n        <li class=\"nav-item\">\n            <a class=\"nav-link\" data-widget=\"pushmenu\" href=\"#\" role=\"button\"><i class=\"fas fa-bars\"></i></a>\n        </li>\n\n        <li class=\"nav-item\">\n            <a href=\"${request.contextPath}/\" class=\"nav-link text-primary fs-6\"><strong><font color=\"#000BFF\">Home</font></strong></a>\n        </li>\n        <li class=\"nav-item\">\n            <a target=\"_blank\" href=\"https://github.com/sohutv/cachecloud\" class=\"nav-link fs-6\"><strong><font color=\"#000BFF\">Github</font></strong></a>\n        </li>\n        <li class=\"nav-item\">\n            <a target=\"_blank\" href=\"${request.contextPath}/wiki/quickstart/index\" class=\"nav-link text-primary fs-6\"><strong><font color=\"#000BFF\">Docs</font></strong></a>\n        </li>\n        <li class=\"nav-item\">\n            <a target=\"_blank\" href=\"${request.contextPath}/web/resource/inc/daily\" class=\"nav-link text-primary fs-6\"><strong><font color=\"#000BFF\">Contact</font></strong></a>\n        </li>\n    </ul>\n\n    <!-- Right navbar links -->\n    <ul class=\"navbar-nav ml-auto\">\n        <!-- Dropdown Menu -->\n        <li class=\"nav-item dropdown\">\n            <a class=\"nav-link text-primary\" data-bs-toggle=\"dropdown\" href=\"#\">\n                <strong>\n                    <i class=\"bi bi-gear\"></i>\n                    <span class=\"dropdown-toggle ps-2 fs-6\">\n                        <font color=\"#000BFF\">\n                        <#if userInfo??>\n                            ${userInfo.chName!}\n                        <#else>\n                        </#if>\n                        </font>\n                    </span>\n                </strong>\n            </a>\n            <ul class=\"dropdown-menu dropdown-menu-end dropdown-menu-arrow\">\n                <#if (userInfo?? && (userInfo.type == 0))>\n                    <li>\n                        <a target=\"_blank\" href=\"${request.contextPath}/manage/total/statlist\" class=\"dropdown-item d-flex align-items-center\">\n                            <i class=\"bi bi-grid-fill\"></i> <span>管理后台</span>\n                        </a>\n                    </li>\n                    <li><hr class=\"dropdown-divider\"></li>\n                </#if>\n                <li>\n                    <a class=\"dropdown-item d-flex align-items-center\" href=\"${request.contextPath}/admin/app/list\">\n                        <i class=\"bi bi-card-list\"></i><span>应用列表</span>\n                    </a>\n                </li>\n                <li><hr class=\"dropdown-divider\"></li>\n                <li>\n                    <a target=\"_blank\" href=\"${request.contextPath}/admin/app/jobs\" class=\"dropdown-item d-flex align-items-center\">\n                        <i class=\"bi bi-list-task\"></i><span>我的申请</span>\n                    </a>\n                </li>\n                <li><hr class=\"dropdown-divider\"></li>\n                <li>\n                    <a href=\"${request.contextPath}/manage/logout\" class=\"dropdown-item d-flex align-items-center\">\n                        <i class=\"bi bi-box-arrow-right\"></i><span>注销</span>\n                    </a>\n                </li>\n            </ul>\n        </li>\n        <li class=\"nav-item\">\n            <a class=\"nav-link text-primary\" data-widget=\"fullscreen\" href=\"#\" role=\"button\">\n                <i class=\"fas fa-expand-arrows-alt\"></i>\n            </a>\n        </li>\n    </ul>\n</nav>\n<!-- Navbar End -->"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/inc/wikiLeft.html",
    "content": "<!-- ======= Sidebar ======= -->\n<aside id=\"leftBox\" class=\"main-sidebar sidebar-dark-primary elevation-4\">\n\n\t<!-- Brand Logo -->\n\t<a href=\"${request.contextPath}/\" class=\"brand-link\">\n\t\t<img src=\"${request.contextPath}/assets/img/logo_new.png\" alt=\"Cachecloud\" class=\"brand-image img-rounded elevation-0\" style=\"opacity: .8\">\n\t\t<span class=\"brand-text font-weight-light\">Cachecloud</span>\n\t</a>\n\n\t<div class=\"sidebar\">\n\n\t\t<!-- Sidebar Menu -->\n\t\t<nav class=\"mt-2\">\n\t\t\t<ul class=\"nav nav-pills nav-sidebar flex-column\" data-widget=\"treeview\" role=\"menu\" data-accordion=\"false\" id=\"sidebar-nav\">\n\t\t\t\t<li id=\"quickstart_index\" class=\"nav-item\">\n\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/quickstart/index\">\n\t\t\t\t\t\t<i class=\"bi bi-play-circle\"></i>\n\t\t\t\t\t\t<p>快速接入</p>\n\t\t\t\t\t</a>\n\t\t\t\t</li>\n\n\t\t\t\t<li class=\"nav-item menu-is-opening menu-open\">\n\t\t\t\t\t<a id=\"intro_index\" class=\"nav-item nav-link py-0\" href=\"${request.contextPath}/wiki/intro/index\">\n\t\t\t\t\t\t<i class=\"bi bi-tag\"></i>\n\t\t\t\t\t\t<p>系统介绍</p>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t<li id=\"intro_redisVersion\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/intro/redisVersion\">\n\t\t\t\t\t\t\t\tRedis版本说明\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"intro_releaseNote\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/intro/releaseNote\">\n\t\t\t\t\t\t\t\tCC客户端版本说明\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\n\n\t\t\t\t<li class=\"nav-item menu-is-opening menu-open\">\n\t\t\t\t\t<a id=\"architecture_index\"  class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/architecture/index\">\n\t\t\t\t\t\t<i class=\"bi bi-boxes\"></i>\n\t\t\t\t\t\t<p>系统架构</p>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t<li id=\"architecture_service\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/architecture/service\">\n\t\t\t\t\t\t\t\t服务架构\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"architecture_tech\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/architecture/tech\">\n\t\t\t\t\t\t\t\t技术架构\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\n\t\t\t\t<li class=\"nav-item menu-is-opening menu-open\">\n\t\t\t\t\t<a id=\"access_index\" class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/access/index\">\n\t\t\t\t\t\t<i class=\"bi bi-arrow-down-right-circle\"></i>\n\t\t\t\t\t\t<p>系统接入</p>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t<li id=\"access_init\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/access/init\">\n\t\t\t\t\t\t\t\t系统初始化\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"access_config\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/access/config\">\n\t\t\t\t\t\t\t\t系统配置说明\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"access_resource\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/access/resource\">\n\t\t\t\t\t\t\t\t系统资源管理\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"access_client\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/access/client\">\n\t\t\t\t\t\t\t\t客户端接入\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\n\n\t\t\t\t<li class=\"nav-item menu-is-opening menu-open\">\n\t\t\t\t\t<a id=\"function_index\" class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/function/index\">\n\t\t\t\t\t\t<i class=\"bi bi-list\"></i>\n\t\t\t\t\t\t<p>系统功能</p>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t<li class=\"nav-item ml-4 menu-is-opening menu-open\">\n\t\t\t\t\t\t\t<a id=\"function_client\" class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/function/client\">\n\t\t\t\t\t\t\t\t客户端功能\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t\t\t<li id=\"function_client-register\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/function/client-register\">\n\t\t\t\t\t\t\t\t\t\t账户申请\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li class=\"nav-item dropdown ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0 dropdown-toggle\" data-bs-toggle=\"dropdown\">\n\t\t\t\t\t\t\t\t\t\t应用管理\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t<ul class=\"dark-mode dropdown-menu text-center\">\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-appStats\"><a href=\"${request.contextPath}/wiki/function/client-appStats\">统计信息</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-desc\"><a href=\"${request.contextPath}/wiki/function/client-desc\">应用详情</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-instances\"><a href=\"${request.contextPath}/wiki/function/client-instances\">实例列表&应用拓扑</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-conn\"><a href=\"${request.contextPath}/wiki/function/client-conn\">连接信息</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-cmd\"><a href=\"${request.contextPath}/wiki/function/client-cmd\">命令曲线</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-latency\"><a href=\"${request.contextPath}/wiki/function/client-latency\">延迟监控</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-daily\"><a href=\"${request.contextPath}/wiki/function/client-daily\">日报统计</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-cmdexe\"><a href=\"${request.contextPath}/wiki/function/client-cmdexe\">命令执行</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-analysis\"><a href=\"${request.contextPath}/wiki/function/client-analysis\">键值分析</a></li>\n\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li id=\"function_job-register\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/function/job\">\n\t\t\t\t\t\t\t\t\t\t我的申请\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</li>\n\n\t\t\t\t\t\t<li class=\"nav-item ml-4 menu-is-opening menu-open\">\n\t\t\t\t\t\t\t<a id=\"function_operations\" class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/function/operations\">\n\t\t\t\t\t\t\t\t运维端功能\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t\t\t<li class=\"nav-item dropdown ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0 dropdown-toggle\" data-bs-toggle=\"dropdown\">\n\t\t\t\t\t\t\t\t\t\t数据统计\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t<ul class=\"dark-mode dropdown-menu text-center\">\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_statistics\"><a href=\"${request.contextPath}/wiki/function/statistics\">全局统计</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_client-statistic\"><a href=\"${request.contextPath}/wiki/function/client-statistic\">client统计</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_server-statistic\"><a href=\"${request.contextPath}/wiki/function/server-statistic\">server统计</a></li>\n\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li class=\"nav-item dropdown ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0 dropdown-toggle\" data-bs-toggle=\"dropdown\">\n\t\t\t\t\t\t\t\t\t\t运维功能\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t<ul class=\"dark-mode dropdown-menu text-center\">\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-job\"><a href=\"${request.contextPath}/wiki/function/operation-job\">工单审批</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-app\"><a href=\"${request.contextPath}/wiki/function/operation-app\">应用运维</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-instance\"><a href=\"${request.contextPath}/wiki/function/operation-instance\">实例运维</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-import\"><a href=\"${request.contextPath}/wiki/function/operation-import\">应用导入</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-migrate\"><a href=\"${request.contextPath}/wiki/function/operation-migrate\">数据迁移</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-diagnostic\"><a href=\"${request.contextPath}/wiki/function/operation-diagnostic\">诊断工具</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-machine\"><a href=\"${request.contextPath}/wiki/function/operation-machine\">机器管理</a></li>\n\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li class=\"nav-item dropdown ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0 dropdown-toggle\" data-bs-toggle=\"dropdown\">\n\t\t\t\t\t\t\t\t\t\t配置管理\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t<ul class=\"dark-mode dropdown-menu text-center\">\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-alert\"><a href=\"${request.contextPath}/wiki/function/operation-alert\">报警配置</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-systemalert\"><a href=\"${request.contextPath}/wiki/function/operation-systemalert\">系统配置</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-resource\"><a href=\"${request.contextPath}/wiki/function/operation-resource\">源管理</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-template\"><a href=\"${request.contextPath}/wiki/function/operation-template\">模板配置</a></li>\n\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li class=\"nav-item dropdown ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0 dropdown-toggle\" data-bs-toggle=\"dropdown\">\n\t\t\t\t\t\t\t\t\t\t任务管理\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t<ul class=\"dark-mode dropdown-menu text-center\">\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-task\"><a href=\"${request.contextPath}/wiki/function/operation-task\">任务流</a></li>\n\t\t\t\t\t\t\t\t\t\t<li id=\"function_operation-schedule\"><a href=\"${request.contextPath}/wiki/function/operation-schedule\">调度任务</a></li>\n\t\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li id=\"function_operation-user\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/function/operation-user\">\n\t\t\t\t\t\t\t\t\t\t用户管理\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li id=\"function_system-alert\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/function/system-alert\">\n\t\t\t\t\t\t\t\t\t\t系统通知\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\n\n\t\t\t\t<li class=\"nav-item menu-is-opening menu-open\">\n\t\t\t\t\t<a id=\"operate_index\" class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/index\">\n\t\t\t\t\t\t<i class=\"bi bi-book\"></i>\n\t\t\t\t\t\t<p>运维手册</p>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t<li id=\"operate_baseConcept\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/baseConcept\">\n\t\t\t\t\t\t\t\t基础概念\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"operate_baseOperate\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/baseOperate\">\n\t\t\t\t\t\t\t\t基础运维\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"operate_appDeploy\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/appDeploy\">\n\t\t\t\t\t\t\t\t应用部署\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"operate_appUpgrade\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/appUpgrade\">\n\t\t\t\t\t\t\t\t应用升级\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"operate_appAlert\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/appAlert\">\n\t\t\t\t\t\t\t\t应用报警\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"operate_ssh\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/ssh\">\n\t\t\t\t\t\t\t\tRedis机器授权\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li id=\"operate_baseOptimize\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/baseOptimize\">\n\t\t\t\t\t\t\t\t系统运维优化\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\n\t\t\t\t<li class=\"nav-item menu-is-opening menu-open\">\n\t\t\t\t\t<a id=\"troubleshooting_index\" class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/operate/index\">\n\t\t\t\t\t\t<i class=\"bi bi-question-circle\"></i>\n\t\t\t\t\t\t<p>FAQ常见问题</p>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class=\"nav nav-treeview\">\n\t\t\t\t\t\t<li id=\"troubleshooting_cachecloud\" class=\"nav-item ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0\" href=\"${request.contextPath}/wiki/troubleshooting/cachecloud\">\n\t\t\t\t\t\t\t\t关于Cachecloud平台\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li class=\"nav-item dropdown ml-4\">\n\t\t\t\t\t\t\t<a class=\"nav-link py-0 dropdown-toggle\" data-bs-toggle=\"dropdown\">\n\t\t\t\t\t\t\t\t关于Redis使用\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<ul class=\"dark-mode dropdown-menu text-center\">\n\t\t\t\t\t\t\t\t<li id=\"troubleshooting_exception\"><a href=\"${request.contextPath}/wiki/troubleshooting/exception\">常见Jedis异常类</a></li>\n\t\t\t\t\t\t\t\t<li id=\"troubleshooting_jedispoolconfig\"><a href=\"${request.contextPath}/wiki/troubleshooting/jedispoolconfig\">JedisPool优化</a></li>\n\t\t\t\t\t\t\t\t<li id=\"troubleshooting_bigkey\"><a href=\"${request.contextPath}/wiki/troubleshooting/bigkey\">bigkey的寻找和优化</a></li>\n\t\t\t\t\t\t\t\t<li id=\"troubleshooting_hotkey\"><a href=\"${request.contextPath}/wiki/troubleshooting/hotkey\">hotkey的寻找和优化</a></li>\n\t\t\t\t\t\t\t\t<li id=\"troubleshooting_memory\"><a href=\"${request.contextPath}/wiki/troubleshooting/memory\">Redis内存优化</a></li>\n\t\t\t\t\t\t\t\t<li id=\"troubleshooting_liunx\"><a href=\"${request.contextPath}/wiki/troubleshooting/liunx\">Liunx系统优化</a></li>\n\t\t\t\t\t\t\t\t<li id=\"troubleshooting_activefrag\"><a href=\"${request.contextPath}/wiki/troubleshooting/activefrag\">Activefrag引起redis周期性延迟</a></li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</nav>\n\t</div>\n\n</aside><!-- End Sidebar-->\n\n<script type=\"text/javascript\">\n\t$(function(){\n\t\tvar id = window.location.pathname.substring(6).replace(/\\//g,\"_\");\n\t\tconsole.log(\"id:\"+id);\n\t\t$(\"#\"+id).addClass(\"active\");\n\t\t$(\".right-side-bar a\").each(function(){\n\t\t\t$(this).click(function(){\n\t\t\t\t$(\"html,body\").animate({scrollTop: $($(this).attr('href')).offset().top - 52}, 500);\n\t\t\t\treturn false;\n\t\t\t});\n\t\t});\n\t\t$(\".right-side-bar a:first\").addClass(\"active\");\n\t\t//浏览器滚动条滚动触发的事件\n\t    $(window).scroll(function() {\n\t        //获取当前滚动条的高度\n\t        var scrollTop = $(window).scrollTop();\n\t        if(scrollTop <= 0) {\n\t        \t$(\".right-side-bar a\").removeClass(\"active\");\n\t        \t$(\".right-side-bar a:first\").addClass(\"active\");\n\t        \treturn;\n\t        }\n\t        var top = scrollTop + $(window).height() - 100;\n\t        //遍历所有的div\n\t        $(\".markdown-body h2 > span\").each(function(index) {\n\t            var thisTop = $(this).offset().top;\n\t            if (top >= thisTop) {\n\t                $(\".right-side-bar a\").removeClass(\"active\");\n\t                $(\".right-side-bar a[href='#\"+$(this).attr(\"id\")+\"']\").addClass(\"active\");\n\t            }\n\t        });\n\t    });\n\t\t\n\t    var page = window.location.pathname.substr(6);\n\t    page = page.replace(/\\//g, \"_\");\n\t    if(page && $(\"#\"+page).offset()){\n\t    \tif($(\"#\"+page).offset().top > 510){\n\t\t\t\t$(\"#leftBox\").animate({scrollTop: $(\"#\"+page).offset().top - 81}, 1000);\n\t    \t}\n\t\t}\n\n\t    doJustThis();\n\t});\n\n\tfunction doJustThis(){\n\t\tvar btn = document.getElementById('intro_index');\n\t\tstopBtnPropagation(btn);\n\t\tbtn = document.getElementById('architecture_index');\n\t\tstopBtnPropagation(btn);\n\t\tbtn = document.getElementById('access_index');\n\t\tstopBtnPropagation(btn);\n\t\tbtn = document.getElementById('function_index');\n\t\tstopBtnPropagation(btn);\n\t\tbtn = document.getElementById('operate_index');\n\t\tstopBtnPropagation(btn);\n\t\tbtn = document.getElementById('troubleshooting_index');\n\t\tstopBtnPropagation(btn);\n\t\tbtn = document.getElementById('function_client');\n\t\tstopBtnPropagation(btn);\n\t\tbtn = document.getElementById('function_operations');\n\t\tstopBtnPropagation(btn);\n\t}\n\n\tfunction stopBtnPropagation(btn){\n\t\tbtn.addEventListener('click', function(event) {\n\t\t\tevent.stopPropagation();\n\t\t\tlet attribute = btnEle.getAttribute(\"href\");\n\t\t\twindow.location.href = attribute;\n\t\t});\n\t}\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceAdvancedAnalysis.html",
    "content": "<script>\n  var instance_advancedAnalysis_c_cu = {\n  <#list appCommandStats as appCommand>${appCommand.commandName}:\n    '${request.contextPath}/admin/instance/getCommandStatsV2.json?instanceId=${instanceInfo.id}&commandName=${appCommand.commandName}&startDate=${startDate}&endDate=${endDate}'<#if ((appCommand_index + 1) != appCommandStats?size)>,\n    </#if>\n  </#list>\n  }\n  var instance_advancedAnalysis_c_o = {\n  <#list appCommandStats as appCommand>${appCommand.commandName}:\n    {\n      plotOptions: {\n      area:{\n      marker: {\n      enabled: false,\n      symbol: 'circle',\n      radius: 0,\n      states:{\n      hover: {\n      enabled: true\n    }\n    }\n    }\n    }\n    }\n    }\n    <#if ((appCommand_index + 1) != appCommandStats?size)>,</#if></#list>\n  }\n</script>\n\n<div class=\"col-md-12\">\n  <div class=\"card\">\n    <div class=\"card-body table-responsive mt-2 d-flex justify-content-end\">\n      <form method=\"get\" action=\"${request.contextPath}/admin/instance/index\" id=\"ec\" name=\"ec\">\n        <label class=\"col-form-label\" style=\"font-weight:bold;text-align:left;\">\n          开始日期:&nbsp;&nbsp;\n        </label>\n        <input type=\"date\" size=\"21\" name=\"startDate\" id=\"startDate\" value=\"${startDate}\">\n        <label class=\"col-form-label\" style=\"font-weight:bold;text-align:left;\">\n          结束日期:\n        </label>\n        <input type=\"date\" size=\"20\" name=\"endDate\" id=\"endDate\" value=\"${endDate}\">\n        <input type=\"hidden\" name=\"instanceId\" value=\"${instanceInfo.id}\">\n        <input type=\"hidden\" name=\"tabTag\" value=\"instance_advancedAnalysis\">\n        <label>&nbsp;<input type=\"submit\" class=\"btn btn-info\" value=\"查询\"/></label>\n      </form>\n    </div>\n  </div>\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h3 class=\"card-title\">\n        TOP的命令执行趋势比较\n      </h3>\n    </div>\n    <div class=\"card-body table-responsive\">\n      <#list appCommandStats as appCommand>\n        <div id=\"${appCommand.commandName}\" class=\"page-body\">\n        </div>\n        <br/>\n      </#list>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceClientList.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h3 class=\"card-title\">\n        客户端连接统计\n      </h3>\n    </div>\n    <div class=\"card-body table-responsive\">\n      <h5>\n        <form method=\"post\" action=\"${request.contextPath}/admin/instance/index?instanceId=${instanceId}&tabTag=instance_clientList\" id=\"conditionFrom\">\n          <input type=\"radio\" value=\"0\" <#if (condition == 0)>checked=\"checked\"</#if>\n          onchange=\"changeCondition(this.value)\"/> 应用客户端\n          <input type=\"radio\" value=\"1\" <#if (condition == 1)>checked=\"checked\"</#if>\n          onchange=\"changeCondition(this.value)\"/> cc客户端\n          <input type=\"radio\" value=\"2\" <#if (condition == 2)>checked=\"checked\"</#if>\n          onchange=\"changeCondition(this.value)\"/> redis客户端\n          <input type=\"radio\" value=\"3\" <#if (condition == 3)>checked=\"checked\"</#if>\n          onchange=\"changeCondition(this.value)\"/> 所有客户端\n          <input type=\"hidden\" id=\"condition\" name=\"condition\" value=\"${condition}\">\n        </form>\n      </h5>\n\n      <!-- table begin -->\n      <table class=\"table table-striped\">\n        <thead>\n        <th scope=\"col\">序号</td>\n        <th scope=\"col\">客户端ip</td>\n        <th scope=\"col\">客户端类型</td>\n        <th scope=\"col\">连接数</td>\n        <th scope=\"col\">连接信息</td>\n        </thead>\n        <tbody>\n        <#list clientMapList as clientMap>\n          <tr>\n            <td>${clientMap_index + 1}</td>\n            <td>${clientMap['addr']}</td>\n            <td>\n              <#list clientMap['clientTypeSet'] as clientType>\n                ${clientType}\n                <br/>\n              </#list>\n            </td>\n            <td>${clientMap['count']}</td>\n            <td>\n              <button type=\"button\" class=\"btn btn-success\" data-bs-target=\"#modal-${clientMap_index}\"\n                      data-bs-toggle=\"modal\">\n                查看连接信息\n              </button>\n              <div id=\"modal-${clientMap_index}\" class=\"modal fade\" tabindex=\"-1\">\n                <div class=\"modal-dialog\" style=\"width: 100%\">\n                  <div class=\"modal-content\">\n                    <div class=\"modal-header\">\n                      <h4 class=\"modal-title\">连接信息</h4>\n                      <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"\n                              aria-hidden=\"true\"></button>\n                    </div>\n\n                    <div class=\"modal-body\">\n                      <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12 table-responsive\">\n                          <table class=\"table table-bordered table-striped table-hover\">\n                            <thead>\n                            <tr>\n                              <td>客户端类型</td>\n                              <td>执行命令</td>\n                              <td>连接</td>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            <#list clientMap['clientInfoList'] as client>\n                            <tr>\n                              <td>${client['flags']!}</td>\n                              <td>${client['cmd']!}</td>\n                              <td>${client?api.toString()}</td>\n                            </tr>\n                            </#list>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                    </div>\n\n                    <div class=\"modal-footer\">\n                      <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </td>\n          </tr>\n        </#list>\n      </tbody>\n  </table>\n  <!-- End Table with stripped rows -->\n</div>\n</div>\n<div class=\"card\">\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      客户端连接信息\n    </h3>\n  </div>\n  <div class=\"card-body table-responsive\">\n    <table class=\"table table-bordered table-striped table-hover\">\n      <tbody>\n      <#list clientList as client>\n        <tr>\n          <td>${client_index + 1}</td>\n          <td>${client}</td>\n        </tr>\n      </#list>\n      </tbody>\n    </table>\n  </div>\n</div>\n</div>\n\n<script type=\"text/javascript\">\n  function changeCondition(value) {\n    console.log('radio:'+value);\n    $('#condition').val(value);\n    $('#conditionFrom').submit();\n  }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceCommand.html",
    "content": "<script type=\"text/javascript\" src=\"${request.contextPath}/assets/js/custom/jquery-console.js\"></script>\n<link href=\"${request.contextPath}/assets/css/mem-cloud.css\" rel=\"stylesheet\" />\n<div class=\"col-md-12\">\n  <div class=\"table-responsive\">\n    <div id=\"console\" class=\"console\"></div>\n    <script type=\"text/javascript\">\n      $(document).ready(function () {\n        var console = $('#console');\n        var controller = console.console({\n          promptLabel: 'instanceId:${instanceId!}> ',\n          commandValidate: function (line) {\n            if (line == \"\") return false;\n            else return true;\n          },\n          commandHandle: function (line,report) {\n            $.ajax({\n              url: \"${request.contextPath}/admin/instance/commandExecute.json\",\n              data: {instanceId: $('#instanceId').val(), command: line},\n              dataType: \"json\",\n              success: function (result) {\n                report([\n                  {msg: result.result,\n                    className: \"jquery-console-message-value\"}\n                ]);\n              }\n            });\n          },\n          autofocus: true,\n          animateScroll: true,\n          promptHistory: true\n        });\n      });\n    </script>\n\n    <input type=\"hidden\" id=\"instanceId\" value=\"${instanceId}\">\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceConfigSelect.html",
    "content": "<script type=\"text/javascript\">\n  function reloadInstanceDetailPage(appId,instanceId){\n    location.href = \"${request.contextPath}/admin/instance/index?instanceId=\"+instanceId + \"&appId=\"+appId + \"&tabTag=instance_configSelect\";\n  }\n  function instanceConfigChange(appId, instanceId){\n    var instanceConfigKey = document.getElementById(\"instanceConfigKey\");\n    if(instanceConfigKey.value == \"\"){\n      alert(\"配置项不能为空\");\n      instanceConfigKey.focus();\n      return false;\n    }\n\n    var instanceConfigValue = document.getElementById(\"instanceConfigValue\");\n    if(instanceConfigValue.value == \"\"){\n      alert(\"配置值不能为空\");\n      instanceConfigValue.focus();\n      return false;\n    }\n\n    var instanceConfigReason = document.getElementById(\"instanceConfigReason\");\n    if(instanceConfigReason.value == \"\"){\n      alert(\"配置原因不能为空\");\n      instanceConfigReason.focus();\n      return false;\n    }\n\n    var instanceConfigChangeBtn = document.getElementById(\"instanceConfigChangeBtn\");\n    instanceConfigChangeBtn.disabled = true;\n\n    $.post(\n            '${request.contextPath}/admin/app/changeInstanceConfig',\n            {\n              appId: appId,\n              instanceId: instanceId,\n              instanceConfigKey: instanceConfigKey.value,\n              instanceConfigValue: instanceConfigValue.value,\n              instanceConfigReason: instanceConfigReason.value\n            },\n            function(data){\n              if(data==1){\n                alert(\"申请成功，请在邮件中关注申请状况.\");\n                $(\"#instanceConfigChangeInfo\").html(\"<div class='alert alert-error' ><strong>Success!</strong>更新成功，窗口会自动关闭<button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                setTimeout(\"$('instanceConfigChangeModal').modal('hide');reloadInstanceDetailPage(\"+appId+\",\"+instanceId+\");\",1000);\n              }else{\n                instanceConfigChangeBtn.disabled = false;\n                $(\"#instanceConfigChangeInfo\").html(\"<div class='alert alert-error' ><strong>Error!</strong>更新失败！<button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n<div class=\"col-md-12\">\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      实例当前配置信息\n      <a target=\"_blank\" href=\"${request.contextPath}/admin/app/appConfig?appId=${appId!}&instanceId=${instanceId!}\" class=\"btn btn-info\" role=\"button\">配置修改申请(当前实例)</a>\n    </h3>\n  </div>\n  <div class=\"card-body table-responsive\">\n    <!-- table begin -->\n    <table class=\"table table-striped border\">\n      <thead>\n      <th scope=\"col\">配置项</td>\n      <th scope=\"col\">配置值</td>\n      </thead>\n      <tbody>\n      <#list redisConfigList?keys as key>\n        <tr>\n          <th scope=\"row\">\n            ${key}\n          </th>\n          <td>${(redisConfigList[key])!}</td>\n        </tr>\n      </#list>\n      </tbody>\n    </table>\n    <!-- End Table with stripped rows -->\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceFault.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      实例故障情况\n    </h3>\n  </div>\n  <div class=\"card-body table-responsive\">\n    <!-- table begin -->\n    <table class=\"table table-striped\">\n      <thead>\n        <th scope=\"col\">应用ID</td>\n        <th scope=\"col\">实例ip:port</td>\n        <th scope=\"col\">实例状态</td>\n        <th scope=\"col\">类型</td>\n        <th scope=\"col\">触发时间</td>\n      </thead>\n      <tbody>\n      <#list list as instance>\n        <tr>\n          <td>\n            <a href=\"${request.contextPath}/admin/app/index?appId=${instance.appId}\" target=\"_blank\">${instance.appId}</a>\n          </td>\n          <td>\n            <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.instId}\" target=\"_blank\">${instance.ip}:${instance.port}</a>\n          </td>\n          <td>${instance.statusDesc}</td>\n          <td>${instance.typeDesc}</td>\n          <td>${instance.createTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n        </tr>\n      </#list>\n      </tbody>\n    </table>\n    <!-- End Table with stripped rows -->\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>实例统计信息</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n  <div class=\"wrapper\">\n    <#include '/inc/head.html'>\n    <div class=\"content-wrapper ml-0\">\n      <div class=\"content\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div id=\"systemAlert\">\n              </div>\n              <div class=\"card\">\n                <div class=\"card-body\">\n                  <nav class=\"nav\">\n                    <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                      <li id=\"instance_stat\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/instance/stat?instanceId=${instanceId!}&startDate=${startDate!}&endDate=${endDate!}\">\n                        <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=instance_stat\">实例统计信息</a>\n                      </li>\n                      <li id=\"app_topology\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/app/topology?appId=${appId!}\">\n                        <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=app_topology\">拓扑结构</a>\n                      </li>\n                      <#if (type == 2) || (type == 6) ||  (type == 11) || (type == 12)>\n                        <li id=\"instance_slowSelect\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/instance/slowSelect?instanceId=${instanceId!}\">\n                          <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=instance_slowSelect\">慢查询分析</a>\n                        </li>\n                        <li id=\"instance_configSelect\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/instance/configSelect?instanceId=${instanceId!}&appId=${appId!}\">\n                          <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=instance_configSelect\">配置查询</a>\n                        </li>\n                      </#if>\n                      <li id=\"instance_clientList\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/instance/clientList?instanceId=${instanceId!}&condition=${condition!}\">\n                        <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=instance_clientList\">连接信息</a>\n                      </li>\n                      <li id=\"instance_fault\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/instance/fault?instanceId=${instanceId!}&startDate=${startDate!}&endDate=${endDate!}\">\n                        <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=instance_fault\">故障报警</a>\n                      </li>\n              <!--        <li id=\"instance_advancedAnalysis\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/instance/advancedAnalysis?instanceId=${instanceId!}&startDate=${startDate!}&endDate=${endDate!}\">-->\n              <!--          <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=instance_advancedAnalysis\">命令曲线</a>-->\n              <!--        </li>-->\n                      <li id=\"instance_command\" class=\"nav-item\" data-url=\"${request.contextPath}/admin/instance/command?instanceId=${instanceId!}\">\n                        <a class=\"nav-link d-flex\" href=\"?instanceId=${instanceId!}&tabTag=instance_command\">命令执行</a>\n                      </li>\n                    </ul>\n                  </nav>\n                </div>\n              </div>\n\n              <div class=\"card\">\n                <div class=\"card-body\">\n                  <div class=\"tab-content\">\n                    <div class=\"tab-pane active\" id=\"instance_statTab\"></div>\n                    <div class=\"tab-pane\" id=\"app_topologyTab\"></div>\n                    <div class=\"tab-pane\" id=\"instance_slowSelectTab\"></div>\n                    <div class=\"tab-pane\" id=\"instance_configSelectTab\"></div>\n                    <div class=\"tab-pane\" id=\"instance_clientListTab\"></div>\n                    <div class=\"tab-pane\" id=\"instance_faultTab\"></div>\n                    <div class=\"tab-pane\" id=\"instance_advancedAnalysisTab\"></div>\n                    <div class=\"tab-pane\" id=\"instance_commandTab\"></div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n    <#include \"/inc/footer.html\">\n  </div>\n  <script type=\"text/javascript\">\n    function showTab(tab) {\n      var url = $(\"#\" + tab).attr(\"data-url\");\n      if(url == null || url == undefined || url == \"\"){\n        return;\n      }\n      $.get($(\"#\" + tab).attr(\"data-url\"), function (result) {\n        $(\"#\" + tab + \"Tab\").html(result);\n      });\n    }\n\n    function refreshActiveTab() {\n      var tab = getQueryString(\"tabTag\");\n      if (tab) {\n        $(\"#\" + tab + \" a\").addClass(\"active\");\n        $(\"#\" + tab + \"Tab\").addClass(\"active\").siblings().removeClass(\"active\");\n      } else {\n        tab = \"instance_stat\";\n        $(\"#\" + tab + \" a\").addClass(\"active\");\n      }\n      console.log(\"tab:\" + tab)\n      showTab(tab);\n      $(\"#tabs li a\").tooltip({placement: \"bottom\"});\n    }\n\n    $(function () {\n      refreshActiveTab();\n    });\n\n    function getQueryString(name) {\n      var reg = new RegExp(\"(^|&)\" + name + \"=([^&]*)(&|$)\");\n      console.log(\"window.location.search: \"+ window.location.search);\n      var r = window.location.search.substr(1).match(reg);\n      if (r != null) return unescape(r[2]);\n      return null;\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceSlowSelect.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      慢查询列表\n    </h3>\n  </div>\n  <div class=\"card-body table-responsive\">\n    <!-- table begin -->\n    <table class=\"table table-striped\">\n      <thead>\n      <th scope=\"col\">id</td>\n      <th scope=\"col\">时间</td>\n      <th scope=\"col\">执行耗时(微秒)</td>\n      <th scope=\"col\">命令</td>\n      </thead>\n      <tbody>\n      <#list redisSlowLogs as redisSlowLog>\n        <tr>\n          <th scope=\"row\">\n            ${redisSlowLog.id!}\n          </th>\n          <td>${redisSlowLog.timeStamp!}</td>\n          <td>\n            ${redisSlowLog.executionTime?string('#,#00')}\n          </td>\n          <td>\n            ${redisSlowLog.command!}\n          </td>\n        </tr>\n      </#list>\n      </tbody>\n    </table>\n  <!-- End Table with stripped rows -->\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instance/instanceStat.html",
    "content": "<div class=\"col-12\">\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h4 class=\"card-title\">实例信息-所属应用<a href=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId!}\" target=\"_blank\">【${appDetail.appDesc.name!}】</a></h4>\n    </div>\n    <div class=\"card-body table-responsive\">\n      <table class=\"table table-striped table-hover\">\n        <tbody>\n          <tr>\n            <th>空间使用率</td>\n            <td>\n                <div class=\"progress progress-fs-1\">\n                  <#if instanceStats?? && instanceStats.memUsePercent??>\n                    <#assign memUsePercentValue = instanceStats.memUsePercent!>\n                  <#else>\n                    <#assign memUsePercentValue = 0>\n                  </#if>\n                  <#if (memUsePercentValue >= 80)>\n                    <div class=\"progress-bar bg-danger\"\n                         role=\"progressbar\" aria-valuenow=\"${memUsePercentValue!}\" aria-valuemax=\"100\"\n                         aria-valuemin=\"0\" style=\"width: ${memUsePercentValue!}%; overflow: visible;\">\n                  <#else>\n                    <div class=\"progress-bar bg-success\"\n                         role=\"progressbar\" aria-valuenow=\"${memUsePercentValue!}\" aria-valuemax=\"100\"\n                         aria-valuemin=\"0\" style=\"width: ${memUsePercentValue!}%; overflow: visible;\">\n                  </#if>\n                    <span style=\"color: #000000; margin-bottom: 0\">\n                      ${(instanceInfo.mem  * memUsePercentValue / 100 / 1024)?string(\"0.00\")}G&nbsp;&nbsp;Used/${instanceInfo.mem / 1024 * 1.0}G&nbsp;&nbsp;Total\n                    </span>\n                  </div>\n                </div>\n            </td>\n            <th>命中率</th>\n            <#if instanceStats??>\n              <#if instanceStats.misses??>\n                  <#assign missValue = instanceStats.misses!>\n              <#else>\n                  <#assign missValue = 0>\n              </#if>\n              <#if instanceStats.hits??>\n                <#assign hitsValue = instanceStats.hits!>\n              <#else>\n                <#assign hitsValue = 0>\n              </#if>\n            <#else>\n              <#assign missValue = 0>\n              <#assign hitsValue = 0>\n            </#if>\n            <#if (missValue+hitsValue==0)>\n                <td>无操作数据</td>\n            <#else>\n              <td>${(hitsValue/(missValue+hitsValue))?string(\"0.00%\")}</td>\n            </#if>\n            <th>实例角色</th>\n            <td>\n              <#if instanceStats?? && instanceStats.role??>\n                <#if (instanceStats.role == 1)>master</#if>\n                <#if (instanceStats.role == 2)>slave</#if>\n              </#if>\n            </td>\n          </tr>\n          <tr>\n            <th>当前对象数</th>\n            <td>\n              <#if instanceStats?? && instanceStats.currItems??>\n                ${instanceStats.currItems?string(\"#,#00\")}\n              </#if>\n            </td>\n            <th>实例类型</th>\n            <td>${instanceInfo.typeDesc!}</td>\n            <th>当前连接数</th>\n            <td>\n              <#if instanceStats?? && instanceStats.currConnections??>\n                ${instanceStats.currConnections!}\n              </#if>\n            </td>\n          </tr>\n          <tr>\n            <th>实例地址</th>\n            <td>${instanceInfo.ip!}:${instanceInfo.port!}</td>\n            <th>运行状态</th>\n            <td>${instanceInfo.statusDesc!}</td>\n            <th>运行天数</th>\n            <td>\n              <#if (instanceInfo.type == 1) && instanceStats.infoMap??>\n                ${(instanceStats.infoMap['stats'].uptime/60/60/24)?string(\"0.0\")}\n              <#else>\n                <#if instanceStats?? && instanceStats.infoMap??>\n                  ${instanceStats.infoMap['Server'].uptime_in_days!}\n                </#if>\n              </#if>\n            </td>\n          </tr>\n        </tbody>\n      </table>\n    </div>\n  </div>\n\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h3 class=\"card-title\">\n        实时状态\n      </h3>\n    </div>\n    <div class=\"card-body table-responsive\">\n      <#if (instanceInfo.type == 2) || (instanceInfo.type == 5) || (instanceInfo.type == 6) || (instanceInfo.type == 11) || (instanceInfo.type == 12)>\n        <table class=\"table table-bordered table-striped table-hover\">\n          <thead>\n          <tr>\n            <th scope=\"col\">分组</th>\n            <th scope=\"col\">键值</th>\n            <th scope=\"col\">值</th>\n          </tr>\n          </thead>\n          <tbody>\n            <#if instanceStats?? && instanceStats.infoMap??>\n              <#list instanceStats.infoMap as instanceKey, infoMap>\n                <#list infoMap?keys as key>\n                  <#if (key_index == ((infoMap?size) - 1))>\n                    <#assign rows = (key_index + 1)>\n                  </#if>\n                </#list>\n                <#list infoMap?keys as key>\n                  <tr>\n                    <#if key_index == 0>\n                      <td rowspan=\"${rows!}\">${instanceKey}</td>\n                    </#if>\n                    <td>${key!}</td>\n                    <td>${infoMap[key]!}</td>\n                  </tr>\n                </#list>\n              </#list>\n            </#if>\n          </tbody>\n        </table>\n      </#if>\n    </div>\n  </div>\n</div>\n\n    \n    \n    \n    \n    \n    \n    \n    \n    \n    \n "
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instanceAlert.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>Redis实例分钟报警</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n        \t<ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">Redis实例分钟报警：</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \tip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                    \t\tport\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                    \t\t属性值\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                    \t\t说明\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            重要程度\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                    \t\t其他信息\n                    </td>\n                </tr>\n\t\t\t\t<#list instanceAlertValueResultList as item>\n\t\t\t\t\t<tr>\n                        <#assign appid=item.appId?c>\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n\t\t\t\t\t\t\t<a href='${ccDomain}/admin/app/index?appId=${appid}'>${appid}</a>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n\t\t\t\t\t\t\t${item.appDesc.name!}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n\t\t\t\t\t\t\t${item.appDesc.officer!}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n\t\t\t\t\t\t\t${item.instanceInfo.ip!}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n\t\t\t\t\t\t\t${item.instanceInfo.port!}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n\t\t\t\t\t\t\t${item.instanceAlertConfig.alertConfig!}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n\t\t\t\t\t\t\t${item.alertMessage!}\n\t\t\t\t\t\t</td>\n\n                        <#if item.instanceAlertConfig.importantLevel??>\n                            <#if item.instanceAlertConfig.importantLevel == 0>\n                                <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                                    一般\n                                </td>\n                            </#if>\n                            <#if item.instanceAlertConfig.importantLevel == 1>\n                                <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;color: darkorange\">\n                                    重要\n                                </td>\n                            </#if>\n                            <#if item.instanceAlertConfig.importantLevel == 2>\n                                <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px; color: red\">\n                                    紧急\n                                </td>\n                            </#if>\n                        <#else>\n                            <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                                一般\n                            </td>\n                        </#if>\n\n\t\t\t\t\t\t<td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n\t\t\t\t\t\t\t${item.otherInfo!}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</#list>\n\n            </table>\n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instanceRecover.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>Pod重启探测Redis实例</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n        \t<ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">Redis实例自动拉起列表：</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t容器ip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                    \t\tport\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                    \t\t说明\n                    </td>\n                </tr>\n                <#list instanceAlertValueResultList as item>\n                <tr>\n                    <#assign appid=item.instanceInfo.appId?c>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        <a href='${ccDomain}/admin/app/index?appId=${appid}'>${appid}</a>\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${item.instanceInfo.ip!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        ${item.instanceInfo.port!}\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                        实例恢复时间：${item.otherInfo!}\n                    </td>\n                </tr>\n                </#list>\n\n            </table>\n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/instanceState.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>Redis实例状态监控报警</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n        \t<ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">Redis实例状态监控报警：</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t应用id\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t应用名\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \t负责人\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        \tip\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                    \t\tport\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                    \t\t状态\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                    \t\t说明\n                    </td>\n                </tr>\n                <#list instanceAlertValueResultList as item>\n                    <tr>\n                        <#assign appid=item.appId?c>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            <a href='${ccDomain}/admin/app/index?appId=${appid}'>${appid}</a>\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.appDesc.name!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.appDesc.officer!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.instanceInfo.ip!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item.instanceInfo.port!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${item.instanceInfo.status!}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 140px;\">\n                            ${item.otherInfo!}\n                        </td>\n                    </tr>\n                </#list>\n\n            </table>\n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/appAudit.html",
    "content": "<div class=\"col-12\">\n    <div id=\"appRefuseModal${item.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n        <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n\n                <div class=\"modal-header\">\n                    <h4 class=\"modal-title\">审批驳回意见</h4>\n                    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n                </div>\n\n                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                    <div class=\"modal-body\">\n                        <div class=\"row\">\n                            <!-- 控件开始 -->\n                            <div class=\"col-md-12\">\n                                <!-- form-body开始 -->\n                                <div class=\"form-body\">\n\n                                    <div class=\"input-group\">\n                                        <label class=\"col-form-label col-md-3\">驳回原因:</label>\n                                        <div class=\"col-md-7\">\n                                            <textarea rows=\"5\"  name=\"refuseReason${item.id!}\" id=\"refuseReason${item.id!}\" placeholder=\"驳回原因 \" class=\"form-control\"></textarea>\n                                        </div>\n                                    </div>\n                                </div>\n                                <!-- form-body 结束 -->\n                            </div>\n                            <div id=\"appRefuseInfo${item.id}\"></div>\n                            <!-- 控件结束 -->\n                        </div>\n                    </div>\n\n                    <div class=\"modal-footer\">\n                        <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                        <button type=\"button\" id=\"appRefuseBtn${item.id!}\" class=\"btn btn-danger\" onclick=\"appRefuse('${item.id!}', '${item.type!}', '${request.contextPath}')\">提交</button>\n                    </div>\n\n                </form>\n            </div>\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/appAuditList.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\"><i class=\"bi bi-tags-fill\">工单处理汇总</i></h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <div class=\"col-md-12\">\n                        <div class=\"table-toolbar\">\n                            <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/auditList\"\n                                  id=\"search_form\">\n                                <input name=\"tabId\" id=\"tabId\" value=\"${tabId!}\" type=\"hidden\"/>\n                                <label class=\"col-form-label col-auto\">开始日期:</label>\n                                <div class=\"col-auto\">\n                                    <input type=\"date\" class=\"form-control\" size=\"15\" name=\"startDate\" id=\"startDate\"\n                                           value=\"${startDate!}\"/>\n                                </div>\n                                <label class=\"col-form-label col-auto\">结束日期:</label>\n                                <div class=\"col-auto\">\n                                    <input type=\"date\" class=\"form-control\" size=\"15\" name=\"endDate\" id=\"endDate\" value=\"${endDate!}\"/>\n                                </div>\n                                <label class=\"col-form-label col-auto\">处理人:</label>\n                                <div class=\"col-auto\">\n                                    <select name=\"adminId\" style=\"width: 150px\" class=\"form-select\">\n                                        <option value=\"\" <#if !(adminId??)>selected=\"selected\"</#if>>\n                                        所有\n                                        </option>\n                                        <#list userMap?keys as key>\n                                            <#assign  admin = userMap?api.get(key)>\n                                            <#if (admin.type==0)>\n                                                <option value=\"${admin.id!}\"\n                                                <#if adminId?? && (adminId == admin.id)>selected</#if>>\n                                                【${admin.id!}】${admin.name!}&nbsp;${admin.chName!}\n                                                </option>\n                                            </#if>\n                                        </#list>\n                                    </select>\n                                </div>\n                                <button type=\"submit\" class=\"btn btn-primary col-auto\">查询</button>\n                            </form>\n                        </div>\n                        <br/>\n                        <div class=\"table-responsive\">\n                            <table class=\"table table-striped table-bordered table-hover\">\n                                <tr>\n                                    <td><span style=\"font-weight:bold\">工单总数</span></td>\n                                    <td>\n                                        ${(statusStatisMap['0']!0 + statusStatisMap['1']!0 + statusStatisMap['2']!0 + statusStatisMap['-1']!0)}\n                                    </td>\n                                    <td><span style=\"font-weight:bold\">完成工单数</span></td>\n                                    <td>${statusStatisMap['1']!0}</td>\n                                    <td style=\"color:orange;\"><span style=\"font-weight:bold\">待处理工单数</span></td>\n                                    <td style=\"color:orange;\">${(statusStatisMap['0']!0 + statusStatisMap['2']!0)}</td>\n                                    <td><span style=\"font-weight:bold\">被驳回工单数</span></td>\n                                    <td>${statusStatisMap['-1']!0}</td>\n                                </tr>\n                                <tr>\n                                    <td><span style=\"font-weight:bold\">申请应用</span></td>\n                                    <td>${typeStatisMap['0']!0}</td>\n                                    <td><span style=\"font-weight:bold\">下线应用</span></td>\n                                    <td>${typeStatisMap['10']!0}</td>\n                                    <td><span style=\"font-weight:bold\">键值分析</span></td>\n                                    <td>${typeStatisMap['6']!0}</td>\n                                    <td><span style=\"font-weight:bold\">诊断应用</span></td>\n                                    <td>${typeStatisMap['8']!0}</td>\n                                </tr>\n                                <tr>\n                                    <td><span style=\"font-weight:bold\">容量变更</span></td>\n                                    <td>${typeStatisMap['1']!0}</td>\n                                    <td><span style=\"font-weight:bold\">配置修改</span></td>\n                                    <td>${(typeStatisMap['4']!0 + typeStatisMap['2']!0)}</td>\n                                    <td><span style=\"font-weight:bold\">用户注册</span></td>\n                                    <td>${typeStatisMap['3']!0}</td>\n                                    <td><span style=\"font-weight:bold\">应用数据迁移</span></td>\n                                    <td>${typeStatisMap['11']!0}</td>\n                                </tr>\n                            </table>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">应用审批列表</h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <div class=\"col-md-12\">\n                        <div class=\"table-toolbar\">\n                            <div class=\"col-md-12\">\n                                <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/auditList\">\n                                    <label class=\"col-form-label col-auto\">\n                                        申请人:\n                                    </label>\n                                    <div class=\"col-md-2\">\n                                        <select name=\"userId\" class=\"selectpicker border rounded w-100\"\n                                                data-live-search=\"true\">\n                                            <option value=\"\" <#if !(userId??)>selected=\"selected\"</#if>>\n                                            所有\n                                            </option>\n                                            <#list userMap?keys as key>\n                                                <#assign user = userMap?api.get(key)>\n                                                <option value=\"${user.id!}\"\n                                                <#if userId?? && (userId == user.id)>selected</#if>>\n                                                【${user.id!}】${user.name!}&nbsp;${user.chName!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                    <label class=\"col-form-label col-auto\">\n                                        处理人:\n                                    </label>\n                                    <div class=\"col-md-2\">\n                                        <select class=\"form-select w-100\" name=\"operateId\">\n                                            <option value=\"\"\n                                            <#if !(operateId??)>selected=\"selected\"</#if>>\n                                            所有\n                                            </option>\n                                            <#list userMap?keys as key>\n                                                <#assign admin = userMap?api.get(key)>\n                                                <#if (admin.type==0)>\n                                                    <option value=\"${admin.id!}\"\n                                                    <#if operateId?? && (operateId == admin.id)>selected</#if>>\n                                                    【${admin.id!}】${admin.name!}&nbsp;${admin.chName!}\n                                                    </option>\n                                                </#if>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                    <label class=\"col-form-label col-auto\">\n                                        类型:\n                                    </label>\n                                    <div class=\"col-md-2\">\n                                        <select class=\"form-select w-100\" name=\"type\">\n                                            <option value=\"\" <#if !(type??)>selected=\"selected\"</#if>>\n                                            所有类型\n                                            </option>\n                                            <#list appAuditTypeMap?keys as key>\n                                                <#assign auditType = appAuditTypeMap?api.get(key)>\n                                                <option value=\"${auditType.value!}\"\n                                                <#if type?? && (type == auditType.value)>selected=\"selected\"</#if>>\n                                                ${auditType.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                    <label class=\"col-form-label col-auto\">\n                                        状态:\n                                    </label>\n                                    <div class=\"col-md-2\">\n                                        <select class=\"form-select w-100\" name=\"status\">\n                                            <option value=\"0\" <#if status?? && (status == 0)>selected=\"selected\"</#if>>\n                                            待处理列表\n                                            </option>\n                                            <option value=\"2\" <#if status?? && (status == 2)>selected=\"selected\"</#if>>\n                                            已受理任务\n                                            </option>\n                                            <option value=\"1\" <#if status?? && (status == 1)>selected=\"selected\"</#if>>\n                                            通过列表\n                                            </option>\n                                            <option value=\"-1\" <#if status?? && (status == -1)>selected=\"selected\"</#if>>\n                                            驳回列表\n                                            </option>\n                                            <option value=\"4\" <#if status?? && (status == 4)>selected=\"selected\"</#if>>\n                                            所有状态\n                                            </option>\n                                        </select>\n                                    </div>\n                                    <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n                                </form>\n                            </div>\n                            <br/>\n                        </div>\n\n                        <div class=\"table-responsive\">\n                            <table class=\"table table-bordered table-striped table-hover table-sm\" id=\"tableDataList\">\n                                <thead class=\"table-light\">\n                                <tr>\n                                    <th scope=\"col\" width=\"6%\">审批id</th>\n                                    <th scope=\"col\" width=\"7%\">appID</th>\n                                    <th scope=\"col\" width=\"12%\">应用名</th>\n                                    <th scope=\"col\" width=\"9%\">申请人</th>\n                                    <th scope=\"col\" width=\"7%\">申请类型</th>\n                                    <th scope=\"col\" width=\"30%\">申请描述</th>\n                                    <th scope=\"col\" width=\"10%\">申请时间</th>\n                                    <th scope=\"col\" width=\"5%\">审核状态</th>\n                                    <th scope=\"col\" width=\"7%\">处理人</th>\n                                    <th scope=\"col\" width=\"7%\">操作</th>\n                                </tr>\n                                </thead>\n                                <tbody>\n                                <#list list as item>\n                                    <tr>\n                                        <td width=\"6%\">\n                                            ${item.id!}\n                                        </td>\n                                        <td width=\"7%\">\n                                            <#if (item.type == 3)>\n                                                无\n                                            <#else>\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/admin/app/index?appId=${item.appId!}\">${item.appId!}</a>\n                                            </#if>\n                                        </td>\n                                        <td width=\"12%\">\n                                            <#if (item.type == 3)>\n                                                无\n                                            <#else>\n                                                <#if item?? && item.appDesc??>\n                                                    ${item.appDesc.name!}\n                                                </#if>\n                                            </#if>\n                                        </td>\n                                        <td width=\"9%\">${item.userName!}</td>\n                                        <td width=\"7%\">${(appAuditTypeMap?api.get(item.type)).info!}</td>\n                                        <td width=\"30%\">${item.info!}</td>\n                                        <td width=\"10%\">${item.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n\n                                        <td width=\"5%\">\n                                            <#if (item.status == 0)><font\n                                                    style=\"color: orange;\">待审</font>\n                                            <#elseif (item.status == 1)><font\n                                                    style=\"color: green;\">通过</font>\n                                            <#elseif (item.status == 2)><font\n                                                    style=\"color: cornflowerblue;\">处理中</font>\n                                            <#elseif (item.status == -1)><font style=\"color: darkred;\">驳回\n                                            </#if>\n                                        </td>\n                                        <td width=\"7%\">\n                                            <#if (userMap?api.get(item.operateId))??>\n                                                ${(userMap?api.get(item.operateId)).chName!}\n                                            </#if>\n                                        </td>\n                                        <td width=\"7%\" style=\"white-space: nowrap\">\n                                            <!-- 任务-->\n                                            <#if (item.taskId > 0)>\n                                                <a target=\"_blank\" class=\"btn btn-info btn-sm\" href=\"${request.contextPath}/manage/task/flow?taskId=${item.taskId!}\">查看任务</a>\n                                            </#if>\n                                            <!--驳回-->\n                                            <#if (item.status == 0)>\n                                                <a href=\"javascript:void(0);\" class=\"btn btn-danger btn-sm\" data-bs-target=\"#appRefuseModal${item.id!}\"\n                                                   data-bs-toggle=\"modal\">驳回</a>\n                                            </#if>\n\n                                            <!--处理-->\n                                            <#if (item.status == 2)>\n                                                <#assign auditUrl = \"${request.contextPath}/manage/app/addAuditStatus?status=1&appAuditId=${item.id!}\">\n                                                <a onclick=\"if(window.confirm('确认要通过该申请请求吗?')){return true;}else{return false;}\"\n                                                   href=\"${auditUrl!}\" class=\"btn btn-success btn-sm\" >通过</a>\n                                                <#if (item.type == 12)>\n                                                    <a target=\"_blank\" class=\"btn btn-info btn-sm\"\n                                                       href=\"${request.contextPath}/import/app/init?importId=${item.param1!}\">迁移进度</a>\n                                                </#if>\n                                            </#if>\n                                            &nbsp;\n                                            <#if (item.status == 0)>\n                                                <#if (item.type == 0)>\n                                                    <#assign auditDealUrl = \"${request.contextPath}/manage/app/initAppDeploy?appAuditId=${item.id!}\">\n                                                    <a target=\"_blank\" href=\"${auditDealUrl!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n                                                <#elseif (item.type == 1)>\n                                                    <#assign auditDealUrl=\"${request.contextPath}/manage/app/initAppScaleApply?appAuditId=${item.id!}\">\n                                                    <a target=\"_blank\" href=\"${auditDealUrl!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n                                                <#elseif (item.type == 2)>\n                                                    <#assign auditDealUrl = \"${request.contextPath}/manage/app/initAppConfigChange?appAuditId=${item.id!}\">\n                                                    <a target=\"_blank\" href=\"${auditDealUrl!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n\n                                                <#elseif (item.type == 3)>\n                                                    <a onclick=\"if(window.confirm('确认要通过该申请请求吗?')){return true;}else{return false;}\"\n                                                       href=\"${request.contextPath}/manage/user/addAuditStatus?status=1&appAuditId=${item.id!}\" class=\"btn btn-success btn-sm\">通过</a>\n\n                                                <#elseif (item.type == 4)>\n                                                    <#assign auditDealUrl = \"${request.contextPath}/manage/instance/initInstanceConfigChange?appAuditId=${item.id!}\">\n                                                    <a target=\"_blank\" href=\"${auditDealUrl!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n\n                                                <#elseif (item.type == 5)>\n                                                    <#assign auditDealUrl = \"${request.contextPath}/manage/app/addAuditStatus?status=1&appAuditId=${item.id!}\">\n                                                    <a onclick=\"if(window.confirm('确认要通过该申请请求吗?')){return true;}else{return false;}\"\n                                                       href=\"${auditUrl!}\" class=\"btn btn-success btn-sm\">通过</a>\n\n                                                <#elseif (item.type == 6)>\n                                                    <a onclick=\"if(window.confirm('确认要处理该申请吗?')){return true;}else{return false;}\"\n                                                       target=\"_blank\"\n                                                       href=\"${request.contextPath}/manage/app/startKeyAnalysis?status=1&appId=${item.appDesc.appId!}&appAuditId=${item.id!}\" class=\"btn btn-warning btn-sm\">分析</a>\n\n                                                <#elseif (item.type == 7)>\n                                                    <#assign auditDealUrl = \"${request.contextPath}/manage/app/addAuditStatus?status=2&appAuditId=${item.id!}&type=7&appId=${item.appId!}\">\n                                                    <a target=\"_blank\" href=\"${auditDealUrl!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n\n                                                <#elseif (item.type == 8)>\n                                                    <a onclick=\"if(window.confirm('确认要处理该申请吗?')){return true;}else{return false;}\"\n                                                       target=\"_blank\"\n                                                       href=\"${request.contextPath}/manage/app/addAuditStatus?status=2&appAuditId=${item.id!}&type=8\" class=\"btn btn-warning btn-sm\">诊断</a>\n\n                                                <#elseif (item.type == 10)>\n                                                    <a target=\"_blank\"\n                                                       href=\"${request.contextPath}/manage/app/addAuditStatus?status=2&appAuditId=${item.id!}&type=10&appId=${item.appId!}\" class=\"btn btn-danger btn-sm\">下线</a>\n\n                                                <#elseif (item.type == 11)>\n                                                    <a target=\"_blank\"\n                                                       href=\"${request.contextPath}/manage/app/addAuditStatus?status=2&appAuditId=${item.id!}&type=11&appId=${item.appId!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n\n                                                <#elseif (item.type == 12)>\n                                                    <a target=\"_blank\"\n                                                       href=\"${request.contextPath}/manage/app/addAuditStatus?status=2&appAuditId=${item.id!}&type=12&appId=${item.appId!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n\n                                                <#elseif (item.type == 13)>\n                                                    <a target=\"_blank\"\n                                                       href=\"${request.contextPath}/manage/app/addAuditStatus?status=2&appAuditId=${item.id!}&type=13&appId=${item.appId!}\" class=\"btn btn-warning btn-sm\">审批处理</a>\n                                                </#if>\n                                            </#if>\n                                        </td>\n                                    </tr>\n                                </#list>\n                                </tbody>\n                            </table>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n<#list list as item>\n    <#include \"/manage/appAudit/appAudit.html\">\n</#list> "
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/appConfigChangeDetail.html",
    "content": "<div class=\"row\">\n<div class=\"col-12\">\n  <#include \"instanceConfig.html\">\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <div class=\"card-title\">\n        <h4>配置修改(<font color=\"red\">${appAudit.info!}</font>)</h4>\n      </div>\n    </div>\n    <div class=\"card-body\">\n      <div class=\"row\">\n        <h4 class=\"card-title\">\n          <i class=\"bi bi-globe\"></i>配置\n        </h4>\n      </div>\n      <!-- BEGIN FORM-->\n      <form action=\"${request.contextPath}/manage/app/addAppConfigChange\" method=\"post\"\n            class=\"form-horizontal form-bordered form-row-stripped\"\n            onsubmit=\"return checkAppConfig();\">\n        <div class=\"form-body\">\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              配置项:\n            </label>\n            <div class=\"col-md-5\">\n              <input type=\"text\" name=\"appConfigKey\" id=\"appConfigKey\" value=\"${appConfigKey!}\" class=\"form-control\" />\n            </div>\n          </div>\n\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">配置值:</label>\n            <div class=\"col-md-5\">\n              <input type=\"text\" name=\"appConfigValue\" id=\"appConfigValue\" value=\"${appConfigValue!}\" class=\"form-control\">\n            </div>\n          </div>\n\n          <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n          <input type=\"hidden\" name=\"appAuditId\" value=\"${appAuditId!}\">\n\n          <div class=\"form-group row\">\n            <div class=\"offset-md-3 col-md-9\">\n              <button type=\"submit\" class=\"btn btn-success\">\n                <i class=\"bi bi-check\"></i>\n                确认\n              </button>\n            </div>\n          </div>\n        </div>\n      </form>\n      <!-- END FORM-->\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/appIntanceReferList.html",
    "content": "<div class=\"card\">\n  <div class=\"card-header\">\n    <h4 class=\"card-title\">\n      应用(id=${appId!})实例列表\n    </h4>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <h4 class=\"card-title\">\n        <i class=\"bi bi-globe\"></i>实例列表\n      </h4>\n    </div>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n        <thead>\n        <tr>\n          <th>ID</th>\n          <th>服务器ip:port</th>\n          <th>实例内存使用</th>\n          <th>角色</th>\n          <th>实例所在机器可用内存</th>\n          <th>对象数</th>\n          <th>slot分布</th>\n        </tr>\n        </thead>\n        <tbody>\n        <#list instanceList as instance>\n          <tr>\n            <td><a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.id!}\"\n                   target=\"_blank\">${instance.id!}</a></td>\n            <td>${instance.ip!}:${instance.port!}</td>\n            <td>\n              <#assign instanceStatsMapKey = (instance.ip + \":\" + instance.port)>\n              <div class=\"progress progress-fs-1\">\n                <#if instanceStatsMap?? && (instanceStatsMap[instanceStatsMapKey])?? && (instanceStatsMap[instanceStatsMapKey]).memUsePercent??>\n                  <#if ((instanceStatsMap[instanceStatsMapKey]).memUsePercent >= 80)>\n                    <#assign progressBarStatus = \"bg-danger\">\n                  <#else>\n                    <#assign progressBarStatus = \"bg-success\">\n                  </#if>\n                  <#assign ariaValuenow = (instanceStatsMap[instanceStatsMapKey]).memUsePercent>\n                <#else>\n                  <#assign ariaValuenow = 0>\n                  <#assign progressBarStatus = \"bg-success\">\n                </#if>\n                <div class=\"progress-bar ${progressBarStatus!}\" role=\"progressbar\"\n                     aria-valuenow=\"${ariaValuenow!}\"\n                     aria-valuemax=\"100\"\n                     aria-valuemin=\"0\"\n                     style=\"width: ${ariaValuenow!}%; overflow: visible;\">\n                  <span style=\"color: #000000; margin-bottom: 0\">\n                    <#if instanceStatsMap?? && (instanceStatsMap[instanceStatsMapKey])??>\n                      <#if (instanceStatsMap[instanceStatsMapKey]).usedMemory??>\n                        <#assign userMemoryValue = ((instanceStatsMap[instanceStatsMapKey]).usedMemory / 1024 / 1024 / 1024)>\n                      <#else>\n                        <#assign userMemoryValue = 0>\n                      </#if>\n                      <#if (instanceStatsMap[instanceStatsMapKey]).maxMemory??>\n                        <#assign maxMemoryValue = ((instanceStatsMap[instanceStatsMapKey]).maxMemory / 1024 / 1024 / 1024)>\n                      <#else>\n                        <#assign maxMemoryValue = 0>\n                      </#if>\n                    <#else>\n                      <#assign userMemoryValue = 0>\n                      <#assign maxMemoryValue = 0>\n                    </#if>\n\n                    ${userMemoryValue?string(\"0.00\")}\n                    G&nbsp;&nbsp;Used\n                    /${maxMemoryValue?string(\"0.00\")}G&nbsp;&nbsp;Total\n                  </span>\n                </div>\n              </div>\n            </td>\n            <td>${instance.roleDesc!}</td>\n            <td>\n              <#if machineCanUseMem?? && machineCanUseMem[instance.ip]??>\n                ${((machineCanUseMem[instance.ip])/1024/1024/1024)?string(\"0.00\")}\n              </#if>\n            </td>\n            <td>\n              <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                ${instanceStatsMap[instanceStatsMapKey].currItems!}\n              </#if>\n            </td>\n            <td>\n              <#if clusterSlotsMap?? && clusterSlotsMap[instanceStatsMapKey]??>\n                <#assign index = 0>\n                <#if clusterSlotsMap[instanceStatsMapKey].slotDistributeList??>\n                  <#list clusterSlotsMap[instanceStatsMapKey].slotDistributeList as slotDistribute>\n                    <#if index != 0>,</#if>\n                    ${slotDistribute?trim}\n                    <#assign index = index+1>\n                  </#list>\n                </#if>\n              </#if>\n            </td>\n          </tr>\n        </#list>\n        </tbody>\n      </table>\n      <br/>\n    </div>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped table-bordered table-hover\">\n        <tbody>\n        <tr>\n          <td>申请人：</td>\n          <td>${appAudit.userName!}</td>\n          <td>appId:</td>\n          <td>${appAudit.appId!}</td>\n        </tr>\n        <tr>\n          <td>申请原因：</td>\n          <td>${appAudit.info!}</td>\n          <td>申请时间：</td>\n          <td>${appAudit.createTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n        </tr>\n        </tbody>\n      </table>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/appScaleApplyDetail.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <#include \"appIntanceReferList.html\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\"><i class=\"bi bi-globe\"></i>填写扩容配置</h4>\n      </div>\n      <div class=\"card-body\">\n        <!-- BEGIN FORM-->\n        <form action=\"${request.contextPath}/manage/app/addAppScaleApply\" method=\"post\"\n              class=\"form-horizontal form-bordered form-row-stripped\"\n              onsubmit=\"return checkAppScaleText();\">\n          <div class=\"form-body\">\n            <div class=\"form-group row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                扩容配置:<font color='red'>(*)</font>:\n              </label>\n              <div class=\"col-md-5\">\n                  <textarea rows=\"1\" name=\"appScaleText\" id=\"appScaleText\"\n                            placeholder=\"请输入扩容后单实例最大空间（填写数字即可，单位MB）\"\n                            class=\"form-control\"></textarea>\n              </div>\n            </div>\n            <input type=\"hidden\" name=\"appId\" value=\"${appId}\">\n            <input type=\"hidden\" name=\"appAuditId\" value=\"${appAuditId}\">\n\n            <div class=\"form-group row\">\n              <div class=\"text-center\">\n                <button type=\"submit\" class=\"btn btn-success\">\n                  <i class=\"bi bi-check\"></i>\n                  确认\n                </button>\n                <#if (appDesc.type == 2) || (appDesc.type == 12)>\n                    <a class=\"btn btn-info ml-3\" href=\"${request.contextPath}/manage/app/initHorizontalScaleApply?appAuditId=${appAuditId}\" title=\"不推荐（需要迁移slot，操作较慢，对redis服务有一定影响，且一旦失败，回滚较困难。）\"><del>水平扩容</del><font color=\"red\">?</font></a>\n                </#if>\n              </div>\n            </div>\n          </div>\n        </form>\n        <!-- END FORM-->\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/deploy/appDeployDetail.html",
    "content": "<!-- app deploy -->\n<script src=\"${request.contextPath}/assets/js/custom/appDeploy.js?<%=System.currentTimeMillis()%>\" type=\"text/javascript\"></script>\n<!-- 提示工具-->\n<link href=\"${request.contextPath}/assets/css/common.css\" rel=\"stylesheet\" type=\"text/css\"/>\n<link href=\"${request.contextPath}/assets/vendor/toastr/toastr.min.css\" rel=\"stylesheet\" type=\"text/css\">\n<script type=\"text/javascript\" src=\"${request.contextPath}/assets/vendor/toastr/toastr.min.js\"></script>\n\n<!--<link href=\"${request.contextPath}/resources/manage/css/style-metronic.css\" rel=\"stylesheet\" type=\"text/css\"/>-->\n\n<script type=\"text/javascript\">\n  $(window).on('load', function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat'\n    });\n  });\n</script>\n\n<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">应用部署 ID:${appDesc.appId!}</h3>\n      </div>\n      <div class=\"card-body\">\n        <input type=\"hidden\" id=\"hiddenAppId\" name=\"hiddenAppId\" value=\"${appId!}\">\n        <input type=\"hidden\" id=\"appAuditId\" name=\"appAuditId\" value=\"${appAuditId!}\">\n        <div class=\"table-responsive\">\n          <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n            <tr>\n              <th>应用id</th>\n              <td>${appDesc.appId!}</td>\n              <th>应用名称</th>\n              <td>${appDesc.name!}</td>\n            </tr>\n            <tr>\n              <th>存储种类</th>\n              <td>\n                <#if (appDesc.type == 2)>Redis-Cluster\n                <#elseif (appDesc.type == 5)>Redis-Sentinel\n                <#elseif (appDesc.type == 6)>Redis-Standalone\n                </#if>\n              </td>\n              <th>空间申请详情</th>\n              <td><font color=\"red\">${appAudit.info!}</font></td>\n            </tr>\n            <tr>\n              <th>是否需要热备</th>\n              <td>\n                <#if (appDesc.needHotBackUp == 1)>是\n                <#elseif (appDesc.needHotBackUp == 0)>否\n                </#if>\n              </td>\n              <th>是否有后端数据源</th>\n              <td>\n                <#if (appDesc.hasBackStore == 1)>有\n                <#elseif (appDesc.hasBackStore == 0)>无\n                </#if>\n              </td>\n            </tr>\n            <tr>\n              <th>是否测试</th>\n              <td>\n                <#if (appDesc.isTest == 1)>是\n                <#elseif (appDesc.isTest == 0)>否\n                </#if>\n              </td>\n              <th>是否需要持久化</th>\n              <td>\n                <#if (appDesc.needPersistence == 1)>是\n                <#elseif (appDesc.needPersistence == 0)>否\n                  </#if>\n              </td>\n            </tr>\n            <tr>\n              <th>预估QPS</th>\n              <td>${appDesc.forecaseQps!}</td>\n              <th>预估条目数量</th>\n              <td>${appDesc.forecastObjNum!}</td>\n            </tr>\n            <tr>\n              <th>客户端机房信息</th>\n              <td>${appDesc.clientMachineRoom!}</td>\n              <th>部署版本</th>\n              <td>${version.name!}</td>\n            </tr>\n            <tr>\n              <th>淘汰策略</th>\n              <td>${appDesc.maxmemoryPolicyDesc!}</td>\n              <th></th>\n              <td></td>\n            </tr>\n          </table>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"card\" id=\"redisVersionDiv\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\"><i class=\"bi bi-globe\"></i>应用基础信息</h4>\n      </div>\n      <div class=\"card-body\">\n        <!-- BEGIN FORM-->\n        <form class=\"form-horizontal form-bordered form-row-stripped\">\n          <div class=\"row form-group\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              应用级别\n            </label>\n            <div class=\"col-md-5\">\n              <select id=\"importantLevel\" name=\"importantLevel\" class=\"form-select\">\n                <option <#if (appDesc.importantLevel == 1)>selected</#if> value=\"1\">\n                  S\n                </option>\n                <option <#if (appDesc.importantLevel == 2)>selected</#if> value=\"2\">\n                  A\n                </option>\n                <option <#if (appDesc.importantLevel == 3)>selected</#if> value=\"3\">\n                  B\n                </option>\n                <option <#if (appDesc.importantLevel == 4)>selected</#if> value=\"4\">\n                  C\n                </option>\n              </select>\n            </div>\n          </div>\n          <div class=\"row form-group\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              部署版本:\n            </label>\n            <div class=\"col-md-5\">\n              <select name=\"type\" id=\"versionId\" class=\"form-select select2_category\">\n                <option versionid=\"-1\"> --- 请选择Redis版本 ---</option>\n                <#list versionList as version>\n                  <option <#if (version.id == appDesc.versionId)>selected</#if> versionid=\"${version.id!}\">${version.name!} </option>\n                </#list>\n              </select>\n            </div>\n          </div>\n          <div class=\"row form-group\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              应用密码：\n            </label>\n            <div class=\"col-md-5\">\n              <input type=\"text\" name=\"md5Password\" id=\"md5Password\" class=\"form-control\" value=\"${md5password!}\" readonly/>\n            </div>\n            <div class=\"col-md-3\">\n              <input type=\"checkbox\" id=\"isSetCustomPwd\" name=\"isSetCustomPwd\" class=\"form-check-input\" onchange=\"changePwd('${md5password!}')\"/>设置自定义密码\n            </div>\n          </div>\n        </form>\n        <!-- END FORM-->\n      </div>\n    </div>\n\n    <div class=\"card\" id=\"appDeployDiv\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\"><i class=\"bi bi-globe\"></i>应用部署信息</h4>\n      </div>\n      <div class=\"card-body\">\n        <!-- BEGIN FORM-->\n        <form id=\"getDeployInfo\" action=\"${request.contextPath}/manage/app/generateDeployInfo\" method=\"post\"\n              class=\"form-horizontal bordered\">\n          <div class=\"form-body\">\n            <div class=\"row form-group\">\n              <label class=\"col-form-label col-auto\">\n                应用类型<font color='red'>*</font>：\n              </label>\n              <div class=\"col-md-3\">\n                <select id=\"appType\" name=\"type\" class=\"form-select w-100\">\n                  <option value=\"2\" <#if (appDesc.type == 2)>selected</#if>>\n                  Redis-Cluster\n                  </option>\n                  <option value=\"5\" <#if (appDesc.type == 5)>selected</#if>>\n                  Redis-Sentinel\n                  </option>\n                  <option value=\"6\" <#if (appDesc.type == 6)>selected</#if>>\n                  Redis-Standalone\n                  </option>\n                </select>\n              </div>\n              <label class=\"col-form-label col-auto\">\n                maxMemory<font color='red'>*</font>：\n              </label>\n              <div class=\"col-md-3\">\n                <input type=\"text\" name=\"maxMemory\" id=\"maxMemory\" placeholder=\"实例内存(MB)\" class=\"form-control\" value=\"\"/>\n                <p id=\"notenum\" style=\"color: red; display: none\">\n                  <i class=\"ace-icon bi bi-exclamation-triangle\"></i>\n                  实例内存(MB)\n                </p>\n              </div>\n            </div>\n\n            <!--sentinelMachines-->\n            <div id=\"sentinelMachines\" class=\"row form-group\">\n              <label class=\"col-form-label col-auto\">\n                Sentinel机器:\n              </label>\n              <div class=\"col-md-6\">\n                <select id=\"sentinelMachineList\" class=\"selectpicker w-100 border rounded\" multiple data-live-search=\"true\">\n                  <#list machineList as machine>\n                    <#if (machine.info.type==3)>\n                      <#if machine.info.ip?? && machineInstanceCountMap[machine.info.ip]??>\n                        <#assign usedCpu = (machineInstanceCountMap[machine.info.ip!])?string(\"0\")?number>\n                      <#else>\n                        <#assign usedCpu = 0>\n                      </#if>\n                      <#assign cpu = machine.info.cpu?string(\"0\")?number>\n                      <#assign cpuUsage = (usedCpu/cpu*100)?string(\"0\")>\n                      <#assign usedMemRss = ((machine.machineMemInfo.usedMemRss?number)/1024/1024/1024)?string(\"0.0\")?number>\n                      <#assign mem = (machine.info.mem)?string(\"0.0\")?number>\n                      <#assign memUsage = (usedMemRss/mem*100)?string(\"0\")>\n                      <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}-${machine.info.rack!}】【sentinel】</option>\n                    </#if>\n                  </#list>\n                  </optgroup>\n                </select>\n                <p id=\"noteSentinelMachines\" style=\"color: red; display: none\">\n                  sentinel机器为奇数个(建议默认:3个）\n                </p>\n              </div>\n              <label class=\"col-form-label col-auto\">\n                部署实例数:\n              </label>\n              <div class=\"col-auto\">\n                <select name=\"sentinelNum\" id=\"sentinelNum\" class=\"form-select\">\n                  <option value=\"1\">1</option>\n                  <option value=\"2\">2</option>\n                  <option value=\"3\">3</option>\n                  <option value=\"4\">4</option>\n                  <option value=\"5\">5</option>\n                  <option value=\"6\">6</option>\n                  <option value=\"7\">7</option>\n                  <option value=\"8\">8</option>\n                </select>\n              </div>\n            </div>\n            <div id=\"twemproxyMachines\" class=\"row form-group\">\n              <label class=\"col-form-label col-auto\">\n                Twemproxy机器:\n              </label>\n              <div class=\"col-md-6\">\n                <select id=\"twemproxyMachineList\" class=\"selectpicker w-100 border rounded\" multiple data-live-search=\"true\">\n                  <#list machineList as machine>\n                    <#if (machine.info.type==4)>\n                      <option value=\"${machine.ip!}\">${machine.ip!}【twemproxy】</option>\n                    </#if>\n                  </#list>\n                  </optgroup>\n                </select>\n              </div>\n              <label class=\"col-form-label col-auto\">\n                部署实例数:\n              </label>\n              <div class=\"col-auto\">\n                <select name=\"twemproxyNum\" id=\"twemproxyNum\" class=\"form-select\">\n                  <option value=\"1\">1</option>\n                  <option value=\"2\">2</option>\n                  <option value=\"3\">3</option>\n                  <option value=\"4\">4</option>\n                  <option value=\"5\">5</option>\n                  <option value=\"6\">6</option>\n                  <option value=\"7\">7</option>\n                  <option value=\"8\">8</option>\n                </select>\n              </div>\n            </div>\n\n            <!-- redis machines -->\n            <div id=\"redisMachines\" class=\"row form-group\" >\n              <label class=\"col-form-label col-auto\">\n                Redis机器：\n              </label>\n              <div class=\"col-md-6\">\n                <select id=\"redisMachineList\" class=\"selectpicker w-100 border rounded\" multiple data-live-search=\"true\">\n                  <#list machineList as machine>\n                    <#if (machine.info.type==0)>\n                      <#if machine.info.ip?? && machineInstanceCountMap[machine.info.ip]??>\n                        <#assign usedCpu = (machineInstanceCountMap[machine.info.ip!])?string(\"0\")?number>\n                      <#else>\n                        <#assign usedCpu = 0>\n                      </#if>\n                      <#assign cpu = machine.info.cpu?string(\"0\")?number>\n                      <#assign cpuUsage = (usedCpu/cpu*100)?string(\"0\")>\n                      <#assign usedMemRss = ((machine.machineMemInfo.usedMemRss)/1024/1024/1024)?string(\"0.0\")?number>\n                      <#assign mem = (machine.info.mem)?string(\"0.0\")?number>\n                      <#assign memUsage = (usedMemRss/mem*100)?string(\"0\")?number>\n                      <#if (machine.info.useType==0)>\n                        <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}-${machine.info.rack!}】【专用:${machine.info.extraDesc!}】</option>\n                      </#if>\n                      <#if (machine.info.useType==1)>\n                        <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}-${machine.info.rack!}】【测试:${machine.info.extraDesc!}】</option>\n                      </#if>\n                      <#if (machine.info.useType==2)>\n                        <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}-${machine.info.rack!}】【混合:${machine.info.extraDesc!}】</option>\n                      </#if>\n                    </#if>\n                  </#list>\n                  </optgroup>\n                </select>\n              </div>\n              <label class=\"col-form-label col-auto\">\n                实例数:\n              </label>\n              <div class=\"col-auto\">\n                <select name=\"redisNum\" id=\"redisNum\" class=\"form-select\">\n                  <option value=\"1\">1主1从</option>\n                  <option value=\"0\">1个(主/从)</option>\n                  <option value=\"2\">2主2从</option>\n                  <option value=\"3\">3主3从</option>\n                  <option value=\"4\">4主4从</option>\n                  <option value=\"5\">5主5从</option>\n                  <option value=\"6\">6主6从</option>\n                  <option value=\"7\">7主7从</option>\n                  <option value=\"8\">8主8从</option>\n                  <option value=\"9\">9主9从</option>\n                  <option value=\"10\">10主10从</option>\n                </select>\n              </div>\n            </div>\n\n            <div id=\"pikaMachines\" class=\"row form-group\">\n              <label class=\"col-form-label col-auto\">\n                Pika机器:\n              </label>\n              <div class=\"col-md-6\">\n                <select id=\"pikaMachineList\" class=\"selectpicker w-100 border rounded\" multiple data-live-search=\"true\">\n                  <#list machineList as machine>\n                    <#if (machine.info.type==5)>\n                      <option value=\"${machine.ip!}\">${machine.ip!} 【pika】</option>\n                    </#if>\n                  </#list>\n                  </optgroup>\n                </select>\n              </div>\n              <label class=\"col-form-label col-auto\">\n                部署实例数:\n              </label>\n              <div class=\"col-auto\">\n                <select name=\"pikaNum\" id=\"pikaNum\" class=\"form-select\">\n                  <option value=\"1\">1</option>\n                  <option value=\"2\">2</option>\n                  <option value=\"3\">3</option>\n                  <option value=\"4\">4</option>\n                  <option value=\"5\">5</option>\n                  <option value=\"6\">6</option>\n                  <option value=\"7\">7</option>\n                  <option value=\"8\">8</option>\n                </select>\n              </div>\n            </div>\n\n            <div class=\"row\" style=\"display:none\" id=\"selectMachineId\">\n              <label class=\"col-form-label col-auto\">\n                部署机器信息\n              </label>\n              <div class=\"col-md-10 table-responsive\">\n                <table class=\"table table-striped bordered table-hover\" id=\"tableList\" style=\"white-space: nowrap\">\n                  <thead>\n                  <tr>\n                    <th>ip</th>\n                    <th>实例数/核数</th>\n                    <th>已使用/剩余/总内存</th>\n                    <th>已使用/剩余/总磁盘</th>\n                    <th>master/salve/sentinel</th>\n                    <th>宿主机/机架信息</th>\n                  </tr>\n                  </thead>\n                  <tbody>\n                  </tbody>\n                </table>\n              </div>\n            </div>\n            <div class=\"row form-group\">\n              <label class=\"col-form-label col-auto\">\n                <br><br><br>部署信息预览:<font color='red'>(*)</font>:\n              </label>\n              <div class=\"col-md-5\">\n                <textarea rows=\"10\" name=\"appDeployInfo\" id=\"appDeployInfo\" placeholder=\"部署详情\" class=\"form-control\" disabled=\"disabled\"></textarea>\n              </div>\n              <div class=\"col-auto\">\n                <br/>\n                <button id=\"clearInfo\" type=\"button\" class=\"btn btn-info\" onclick=\"clearinfo()\" data-bs-toggle=\"modal\" style=\"background:#CCCCCC;display:none;\">清除</button>\n                <br>\n                <button id=\"manualSwitch\" type=\"button\" class=\"btn btn-info\" onclick=\"manualSwitchFunc()\" title=\"可启用编辑，修改部署信息，将按此信息进行部署\"  data-bs-toggle=\"modal\" style=\"background:#FF0000;display:none;\">编辑</button>\n              </div>\n            </div>\n\n            <div class=\"dropdown-divider\"></div>\n            <div class=\"row float-right\">\n              <button type=\"button\" class=\"btn btn-primary col-auto\" onclick=\"generateDeployInfo('${request.contextPath}')\">\n                <span id=\"deployPreview\">生成部署预览</span>\n              </button>\n              <button type=\"button\" id=\"appDeployBtn\" class=\"btn btn-primary col-auto ms-3\" onclick=\"addAppDeployTask('${request.contextPath}')\">\n                <span id=\"deploy\">开始部署</span>\n              </button>\n            </div>\n            <input id=\"importId\" type=\"hidden\" value=\"${importId!}\">\n          </div>\n        </form>\n        <!-- END FORM-->\n      </div>\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  // 初始化应用类型\n  $(function() {\n    appType($('#appType').val());\n  });\n\n  // 根据类型选择机器\n  $('#appType').change(function () {\n    appType($('#appType').val());\n  });\n  // 根据应用类型获取不同资源\n  function appType(type) {\n    if (type == 5) {\n      // sentinel +redis\n      $(\"#sentinelMachines\").show();\n      $(\"#redisMachines\").show();\n      $(\"#twemproxyMachines\").hide();\n      $(\"#pikaMachines\").hide();\n    } else if (type == 7) {\n      // twemproxy+Redis\n      $(\"#sentinelMachines\").show();\n      $(\"#redisMachines\").show();\n      $(\"#twemproxyMachines\").show();\n      $(\"#pikaMachines\").hide();\n    } else if (type == 8) {\n      // sentinel+Pika\n      $(\"#sentinelMachines\").show();\n      $(\"#redisMachines\").hide();\n      $(\"#twemproxyMachines\").hide();\n      $(\"#pikaMachines\").show();\n    } else if (type == 9) {\n      // twemproxy+Pika\n      $(\"#sentinelMachines\").show();\n      $(\"#redisMachines\").hide();\n      $(\"#twemproxyMachines\").show();\n      $(\"#pikaMachines\").show();\n    }else {\n      // standalone | rediscluster\n      $(\"#redisMachines\").show();\n      $(\"#sentinelMachines\").hide();\n      $(\"#twemproxyMachines\").hide();\n      $(\"#pikaMachines\").hide();\n    }\n  }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/deploy/initAppDeploy.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n  <div class=\"wrapper\">\n    <#include \"/manage/inc/head.html\">\n    <#include \"/manage/inc/left.html\">\n\n    <div class=\"content-wrapper\">\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <#include \"appDeployDetail.html\">\n        </div>\n      </section>\n    </div>\n    <#include \"/manage/inc/footer.html\">\n    <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n  </div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/handleHorizontalScale.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"handleHorizontalScaleDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/handleHorizontalScaleDetail.html",
    "content": "<script src=\"${request.contextPath}/assets/js/custom/auditManage.js\" type=\"text/javascript\"></script>\n<div class=\"row\">\n<div class=\"col-12\">\n  <#include \"appIntanceReferList.html\">\n  <#include \"horizontalScaleProcessList.html\">\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h4 class=\"card-title\">迁移计划</h4>\n    </div>\n    <div class=\"card-body\">\n      <div class=\"row\">\n        <h4 class=\"card-title\">\n          <i class=\"bi bi-globe\"></i>填写迁移计划\n        </h4>\n      </div>\n      <!-- BEGIN FORM-->\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"form-body\">\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              源实例ID:<font color='red'>(*)</font>:\n            </label>\n            <div class=\"col-md-5\">\n              <input type=\"text\" name=\"sourceId\" id=\"sourceId\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            </div>\n          </div>\n\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              目标实例ID:<font color='red'>(*)</font>:\n            </label>\n            <div class=\"col-md-5\">\n              <input type=\"text\" name=\"targetId\" id=\"targetId\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            </div>\n          </div>\n\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              开始slot:<font color='red'>(*)</font>:\n            </label>\n            <div class=\"col-md-5\">\n              <input type=\"text\" name=\"startSlot\" id=\"startSlot\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            </div>\n          </div>\n\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              结束slot:<font color='red'>(*)</font>:\n            </label>\n            <div class=\"col-md-5\">\n              <input type=\"text\" name=\"endSlot\" id=\"endSlot\" class=\"form-control\" onchange=\"testisNum(this.id)\"/>\n            </div>\n          </div>\n\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              批量migrate<font color='red'>(*)</font>:\n            </label>\n            <div class=\"col-md-5\">\n              <select id=\"migrateType\" name=\"migrateType\" class=\"form-select select2_category\">\n                <option value=\"0\">\n                  否\n                </option>\n                <option value=\"1\">\n                  是\n                </option>\n              </select>\n            </div>\n          </div>\n\n          <div class=\"form-group row\">\n            <label class=\"col-form-label offset-md-2 col-md-8 text-left\">\n              redis版本低于<font color='red'>4.0.7</font>时，如待迁移集群设置有密码，由于redis migrate命令问题，不能正常执行，该情况下不支持水平扩容。\n            </label>\n          </div>\n\n          <input type=\"hidden\" name=\"appId\" id=\"appId\" value=\"${appAudit.appId}\">\n          <input type=\"hidden\" name=\"appAuditId\" id=\"appAuditId\" value=\"${appAudit.id}\">\n\n          <div class=\"form-group row\">\n            <div class=\"offset-md-3 col-md-3\">\n              <button id=\"submitButton\" disabled=\"disabled\" type=\"button\" class=\"btn btn-success\" onclick=\"startHorizontalScale('${request.contextPath}')\">\n                <i class=\"bi bi-check\"></i>\n                开始迁移\n              </button>\n              <button id=\"checkButton\" type=\"button\" class=\"btn btn-success\" onclick=\"checkHorizontalScale('${request.contextPath}')\">\n                <i class=\"bi bi-check\"></i>\n                验证格式\n              </button>\n            </div>\n          </div>\n        </div>\n      </form>\n      <!-- END FORM-->\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/horizontalScaleApplyDetail.html",
    "content": "<script type=\"text/javascript\">\n  function checkNodes(){\n    var masterSizeSlave = document.getElementById(\"masterSizeSlave\");\n    if(masterSizeSlave.value == \"\"){\n      alert(\"节点信息不能为空\");\n      masterSizeSlave.focus();\n      return false;\n    }\n    var appAuditId = document.getElementById(\"appAuditId\");\n    $.get(\n            '${request.contextPath}/manage/app/checkHorizontalNodes.json',\n            {\n              appAuditId: appAuditId.value,\n              masterSizeSlave: masterSizeSlave.value\n            },\n            function(data){\n              var status = data.status;\n              alert(data.message);\n              if (status == 1) {\n                var nodeDeployBtn = document.getElementById(\"nodeDeployBtn\");\n                nodeDeployBtn.disabled = false;\n\n                var nodeCheckBtn = document.getElementById(\"nodeCheckBtn\");\n                nodeCheckBtn.disabled = true;\n\n                masterSizeSlave.disabled = true;\n              } else {\n                masterSizeSlave.focus();\n              }\n            }\n    );\n  }\n\n  function deployNodes(){\n    var masterSizeSlave = document.getElementById(\"masterSizeSlave\");\n    var appAuditId = document.getElementById(\"appAuditId\");\n    var nodeDeployBtn = document.getElementById(\"nodeDeployBtn\");\n    nodeDeployBtn.disabled = true;\n    $.get(\n            '${request.contextPath}/manage/app/addHorizontalNodes.json',\n            {\n              appAuditId: appAuditId.value,\n              masterSizeSlave: masterSizeSlave.value\n            },\n            function(data){\n              var status = data.status;\n              if (status == 1) {\n                alert(\"添加部署成功,确认后将跳转到ReShard页面!\");\n                window.location.href=\"${request.contextPath}/manage/app/handleHorizontalScale?appAuditId=\" + appAuditId.value;\n              } else {\n                alert(\"节点部署失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">添加新的节点(不分配slot，只meet到集群)</h3>\n      </div>\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <h4 class=\"card-title\">\n            <i class=\"bi bi-globe\"></i>填写扩容配置:\n          </h4>\n        </div>\n          <!-- BEGIN FORM-->\n          <form class=\"form-horizontal form-bordered form-row-stripped\">\n            <div class=\"form-body\">\n              <div class=\"form-group row\">\n                <label class=\"col-form-label col-md-3 text-end\">\n                  主从分片配置:\n                </label>\n                <div class=\"col-md-5\">\n                  <textarea id=\"masterSizeSlave\" name=\"masterSizeSlave\" type=\"text\" rows=\"5\" placeholder=\"materIp:memSize:slaveIp\" class=\"form-control\"></textarea>\n                </div>\n              </div>\n\n              <div class=\"form-group row\">\n                <label class=\"col-form-label col-md-3 text-end\">添加多实例规则如下:<br/><br/><br/></label>\n                &nbsp;&nbsp;&nbsp;&nbsp;masterIp1:memSize(M):slaveIp1<br/>\n                &nbsp;&nbsp;&nbsp;&nbsp;masterIp2:memSize(M):slaveIp2<br/>\n                &nbsp;&nbsp;&nbsp;&nbsp;masterIp3:memSize(M):slaveIp3<br/>\n              </div>\n\n              <input type=\"hidden\" id=\"appAuditId\" name=\"appAuditId\" value=\"${appAudit.id!}\">\n\n              <div class=\"form-group row\">\n                <div class=\"offset-md-3 col-md-6\">\n                  <button disabled=\"disabled\" id=\"nodeDeployBtn\" type=\"button\" class=\"btn btn-success\" onclick=\"deployNodes()\">\n                    <i class=\"bi bi-check\"></i>\n                    部署节点\n                  </button>\n                  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n                  <button type=\"button\" id=\"nodeCheckBtn\" class=\"btn btn-success\" onclick=\"checkNodes()\">\n                    <i class=\"bi bi-check\"></i>\n                    验证格式\n                  </button>\n                  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n                  <a target=\"_blank\" class=\"btn btn-success\" href=\"${request.contextPath}/manage/app/handleHorizontalScale?appAuditId=${appAudit.id!}\">ReShard页面</a>\n                </div>\n              </div>\n            </div>\n          </form>\n          <!-- END FORM-->\n        </div>\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/horizontalScaleProcessList.html",
    "content": "<div class=\"card\">\n  <div class=\"card-header\">\n    <h4 class=\"card-title\">\n      迁移进度\n    </h4>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <h4 class=\"card-title\"><i class=\"bi bi-globe\"></i>扩容进度</h4>\n    </div>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n        <thead>\n        <tr>\n          <th>id</th>\n          <th>slot迁移进度</th>\n          <th>目标源实例</th>\n          <th>开始结束slot</th>\n          <th>正在迁移的slot</th>\n          <th>状态</th>\n          <th>开始时间</th>\n          <th>结束时间</th>\n          <th>操作</th>\n        </tr>\n        </thead>\n        <tbody>\n        <#list instanceReshardProcessList as instanceReshardProcess>\n          <tr>\n            <td>${instanceReshardProcess.id!}</td>\n            <td>\n              <div class=\"progress progress-fs-1\">\n                <div id=\"reshardSlotProgress${instanceReshardProcess.id!}\" class=\"progress-bar bg-success\"\n                     role=\"progressbar\" aria-valuenow=\"${instanceReshardProcess.finishSlotNum!}\" aria-valuemax=\"${instanceReshardProcess.totalSlot!}\"\n                     aria-valuemin=\"0\" style=\"width: ${(instanceReshardProcess.finishSlotNum) / (instanceReshardProcess.totalSlot)}; overflow: visible;\">\n                  <span style=\"color: #000000; margin-bottom: 0\">\n                    <span id=\"finishSlotNum${instanceReshardProcess.id!}\">${instanceReshardProcess.finishSlotNum!}</span>&nbsp;&nbsp;Finish/<span id=\"totalSlot${instanceReshardProcess.id!}\">${instanceReshardProcess.totalSlot!}</span>&nbsp;&nbsp;Total\n                  </span>\n                </div>\n              </div>\n            </td>\n            <td id=\"sourceTargetInstance${instanceReshardProcess.id!}\">\n              ${instanceReshardProcess.sourceInstanceId!}(${instanceInfoMap?api.get(instanceReshardProcess.sourceInstanceId).ip!}:${instanceInfoMap?api.get(instanceReshardProcess.sourceInstanceId).port!})\n              -->\n              ${instanceReshardProcess.targetInstanceId!}(${instanceInfoMap?api.get(instanceReshardProcess.targetInstanceId).ip!}:${instanceInfoMap?api.get(instanceReshardProcess.targetInstanceId).port!})\n            </td>\n            <td id=\"startEndSlot${instanceReshardProcess.id!}\">\n              ${instanceReshardProcess.startSlot!}\n              -->\n              ${instanceReshardProcess.endSlot!}\n            </td>\n            <td id=\"migratingSlot${instanceReshardProcess.id!}\">\n              ${instanceReshardProcess.migratingSlot!}\n            </td>\n            <td id=\"statusDesc${instanceReshardProcess.id!}\">\n              <#if (instanceReshardProcess.status == 0)>\n                运行中\n              <#elseif (instanceReshardProcess.status == 1)>\n                完成\n              <#elseif (instanceReshardProcess.status == 2)>\n                出错\n              </#if>\n            </td>\n            <td>\n              ${instanceReshardProcess.startTime?string(\"yyyy-MM-dd HH:mm:ss\")}\n            </td>\n            <td id=\"endTime${instanceReshardProcess.id!}\">\n              <#if (instanceReshardProcess.status == 1)>\n                ${instanceReshardProcess.endTime?string(\"yyyy-MM-dd HH:mm:ss\")}\n              </#if>\n            </td>\n            <td>\n              <#if (instanceReshardProcess.status == 2)>\n                <button id=\"retryBtn${instanceReshardProcess.id!}\"  type=\"button\" class=\"btn btn-danger btn-sm\" onclick=\"retryHorizontalScale('${instanceReshardProcess.id!}', '${request.contextPath}')\">\n                  重试\n                </button>\n              </#if>\n            </td>\n          </tr>\n        </#list>\n        </tbody>\n      </table>\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  $(function(){\n    function show(){\n      var auditId = document.getElementById(\"appAuditId\").value\n      var url = \"${request.contextPath}/manage/app/showReshardProcess.json?auditId=\" + auditId;\n      $.get(url, function(data) {\n        var dataArr = eval(\"(\" + data + \")\");\n        var length = dataArr.length;\n\n        for (var i = 0; i < length; i++) {\n          var data = dataArr[i];\n          var id = data.id;\n          var appId = data.appId;\n          var finishSlotNum = data.finishSlotNum;\n          var totalSlot = data.totalSlot;\n          var status = data.status;\n          var statusDesc = data.statusDesc;\n          var migratingSlot = data.migratingSlot;\n          var endTimeFormat = data.endTimeFormat;\n          document.getElementById(\"finishSlotNum\" + id).innerHTML = finishSlotNum;\n          document.getElementById(\"totalSlot\" + id).innerHTML = totalSlot;\n          document.getElementById(\"reshardSlotProgress\" + id).style.width = (finishSlotNum * 100 / totalSlot ) + \"%\";\n          document.getElementById(\"statusDesc\" + id).innerHTML = statusDesc;\n          document.getElementById(\"migratingSlot\" + id).innerHTML = migratingSlot;\n          //如果完成显示结束时间\n          if (status == 1) {\n            document.getElementById(\"endTime\" + id).innerHTML = endTimeFormat;\n          }\n          //非出错不显示\n          if (status != 2) {\n            var retryBtn = document.getElementById(\"retryBtn\" + id);\n            if (retryBtn != null) {\n              retryBtn.style.display = \"none\";\n            }\n          }\n        }\n      });\n    }\n    setInterval(show,2000);// 注意函数名没有引号和括弧！\n    // 使用setInterval(\"show()\",3000);会报“缺少对象”\n  });\n</script>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/initAppConfigChange.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"appConfigChangeDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/initAppScaleApply.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"appScaleApplyDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/initHorizontalScaleApply.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"horizontalScaleApplyDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/initInstanceConfigChange.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"instanceConfigChangeDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/instanceConfig.html",
    "content": "<div class=\"card\">\n  <div class=\"card-header\">\n    <h4 class=\"card-title\">\n      配置信息\n    </h4>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <h4 class=\"card-title\"><i class=\"bi bi-globe\"></i>配置信息列表</h4>\n    </div>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n        <thead>\n        <tr>\n          <th>配置项</th>\n          <th>配置值:port</th>\n        </tr>\n        </thead>\n        <tbody>\n        <#list redisConfigList?keys as key>\n          <tr>\n            <td>${key!}</td>\n            <td>${redisConfigList[key]!}</td>\n          </tr>\n        </#list>\n        </tbody>\n      </table>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/instanceConfigChangeDetail.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <#include \"instanceConfig.html\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">配置修改(<font color=\"red\">${appAudit.info!}</font>)</h4>\n      </div>\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <h4 class=\"card-title\">\n            <i class=\"bi bi-globe\"></i>配置\n          </h4>\n        </div>\n        <!-- BEGIN FORM-->\n        <form action=\"${request.contextPath}/manage/instance/addInstanceConfigChange\" method=\"post\"\n              class=\"form-horizontal form-bordered form-row-stripped\"\n              onsubmit=\"return checkInstanceConfig();\">\n          <div class=\"form-body\">\n            <div class=\"form-group row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                配置项:\n              </label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" name=\"instanceConfigKey\" id=\"instanceConfigKey\" value=\"${instanceConfigKey!}\" class=\"form-control\" />\n              </div>\n            </div>\n\n            <div class=\"form-group row\">\n              <label class=\"col-form-label col-md-3 text-end\">配置值:</label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" name=\"instanceConfigValue\" id=\"instanceConfigValue\" value=\"${instanceConfigValue!}\" class=\"form-control\">\n              </div>\n            </div>\n            <#if instanceInfo??>\n              <input type=\"hidden\" name=\"host\" value=\"${instanceInfo.ip!}\">\n              <input type=\"hidden\" name=\"port\" value=\"${instanceInfo.port!}\">\n            </#if>\n\n            <input type=\"hidden\" name=\"appId\" value=\"${appId!}\">\n            <input type=\"hidden\" name=\"appAuditId\" value=\"${appAuditId!}\">\n\n            <div class=\"form-group row\">\n              <div class=\"offset-md-3 col-md-9\">\n                <button type=\"submit\" class=\"btn btn-success\">\n                  <i class=\"bi bi-check\"></i>\n                  确认\n                </button>\n              </div>\n            </div>\n          </div>\n        </form>\n        <!-- END FORM-->\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appAudit/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"/manage/appAudit/appAuditList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n\n<script>\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n\n        if (!jQuery().dataTable) {\n          return;\n        }\n        // begin first table\n        $('#tableDataList').dataTable({\n          \"pageLength\": 50,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          },\n        });\n        jQuery('#tableDataList_wrapper>div:first-child').css(\"display\",\"none\");\n      }\n    };\n  }();\n\n  $(function() {\n    TableManaged.init();\n  });\n</script>\n\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appImport/appImport.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n    <title>CacheCloud管理后台</title>\n    <meta content=\"\" name=\"description\">\n    <meta content=\"\" name=\"keywords\">\n    <#include \"/manage/inc/backendResources.html\">\n    <link href=\"${request.contextPath}/assets/css/common.css\" rel=\"stylesheet\">\n</head>\n<script type=\"text/javascript\">\n    $(window).on('load', function () {\n        $('.selectpicker').selectpicker({\n            'selectedText': 'cat'\n        });\n    });\n</script>\n\n<body>\n    <div class=\"card\">\n        <div class=\"modal\" tabindex=\"-1\" style=\"display: block;\">\n            <div class=\"modal-dialog modal-xl\">\n                <div class=\"modal-content\">\n                    <div class=\"modal-header\">\n                        <h4 class=\"modal-title\">应用导入流程</h4>\n                    </div>\n\n                    <form class=\"form-horizontal form-bordered form-row-stripped align-items-center\">\n                        <div class=\"modal-body\">\n                            <div class=\"row bs-wizard\" style=\"border-bottom:0;\">\n                                <div id=\"appInfo\" class=\"col-sm-2 offset-sm-1 bs-wizard-step\">\n                                    <div class=\"text-center bs-wizard-stepnum\">1.确认导入配置</div>\n                                    <div class=\"progress\">\n                                        <div class=\"progress-bar\"></div>\n                                    </div>\n                                    <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                                    <div class=\"bs-wizard-info text-center\">导入前准备</div>\n                                </div>\n\n                                <div id=\"createVersion\" class=\"col-sm-2 bs-wizard-step disabled\">\n                                    <div class=\"text-center bs-wizard-stepnum\">2.创建Redis版本</div>\n                                    <div class=\"progress\">\n                                        <div class=\"progress-bar\"></div>\n                                    </div>\n                                    <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                                    <div class=\"bs-wizard-info text-center\">为应用创建版本</div>\n                                </div>\n\n                                <div id=\"build\" class=\"col-sm-2 bs-wizard-step disabled\">\n                                    <div class=\"text-center bs-wizard-stepnum\">3.新建应用</div>\n                                    <div class=\"progress\">\n                                        <div class=\"progress-bar\"></div>\n                                    </div>\n                                    <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                                    <div class=\"bs-wizard-info text-center\">部署应用</div>\n                                </div>\n\n                                <div id=\"appMigrate\" class=\"col-sm-2 bs-wizard-step disabled\">\n                                    <div class=\"text-center bs-wizard-stepnum\">4.数据迁移</div>\n                                    <div class=\"progress\">\n                                        <div class=\"progress-bar\"></div>\n                                    </div>\n                                    <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                                    <div class=\"bs-wizard-info text-center\">新老实例redis数据迁移</div>\n                                </div>\n\n                                <div id=\"importDone\" class=\"col-sm-2 bs-wizard-step disabled\">\n                                    <div class=\"text-center bs-wizard-stepnum\">5.应用导入完成</div>\n                                    <div class=\"progress\">\n                                        <div class=\"progress-bar\"></div>\n                                    </div>\n                                    <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                                    <div class=\"bs-wizard-info text-center\">导入完成</div>\n                                </div>\n                            </div>\n\n                            <br/>\n                            <div class=\"row d-none\" id=\"pre\">\n                                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                                    <div class=\"form-body\">\n                                        <div class=\"row\">\n                                            <div class=\"page-header row offset-md-1 col-md-11\">\n                                                <h4 class=\"card-title\">源：Redis信息</h4>\n                                            </div>\n                                            <div class=\"row offset-md-1\">\n                                                <div class=\"row offset-md-1\">\n                                                    <label class=\"col-form-label col-md-2 text-end\">\n                                                        存储类型:\n                                                    </label>\n                                                    <div class=\"col-md-5\">\n                                                        <select id=\"sourceType\" name=\"sourceType\" class=\"form-select w-100\" disabled>\n                                                            <option value=\"5\" <#if (appImport.sourceType==5)>selected=\"selected\"</#if> >\n                                                            Redis-Standalone\n                                                            </option>\n                                                            <option value=\"6\" <#if (appImport.sourceType==6)>selected=\"selected\"</#if> >\n                                                            Redis-Sentinel\n                                                            </option>\n                                                            <option value=\"7\" <#if (appImport.sourceType==7)>selected=\"selected\"</#if> >\n                                                            Redis-Cluster\n                                                            </option>\n                                                        </select>\n                                                    </div>\n                                                </div>\n                                                <div class=\"row offset-md-1\" id=\"instanceSourceDiv\">\n                                                    <label class=\"col-form-label col-md-2 text-end\">源实例信息: </label>\n                                                    <div class=\"col-md-5\">\n                                                        <textarea id=\"instanceSourceInfo\" type=\"text\" rows=\"10\" class=\"form-text w-100\"\n                                                                  readonly>${appImport.instanceInfo!}</textarea>\n                                                    </div>\n                                                    <label id=\"instanceSourceLog\" class=\"col-form-label\"></label>\n                                                </div>\n\n                                                <div class=\"row offset-md-1\">\n                                                    <label class=\"col-form-label col-md-2 text-end\"> 密码: </label>\n                                                    <div class=\"col-md-5\">\n                                                        <input id=\"password\" type=\"text\" name=\"password\" class=\"form-control\"\n                                                               readonly value=\"${appImport.redisPassword!}\">\n                                                    </div>\n                                                </div>\n                                            </div>\n                                        </div>\n\n                                        <div class=\"row\">\n                                            <div class=\"page-header row offset-md-1 col-md-11\">\n                                                <h4 class=\"card-title\">目标：应用信息</h4>\n                                            </div>\n                                            <div class=\"row offset-md-1\">\n                                                <label class=\"col-form-label col-md-2 text-end\"> 应用名称: </label>\n                                                <div class=\"col-md-3\">\n                                                    <input id=\"appName\" type=\"text\" name=\"appName\" class=\"form-control\"\n                                                           readonly value=\"\"\n                                                        <#if appDesc?? && appDesc.name??>\n                                                            ${appDesc.name!}\n                                                        </#if>\n                                                    \">\n                                                </div>\n                                                <label class=\"col-form-label col-md-2 text-end\"> Redis类型: </label>\n                                                <div class=\"col-md-3\">\n                                                    <input id=\"type\" type=\"text\" name=\"type\" class=\"form-control\" readonly value=\"\n                                                        <#if appDesc?? && (appDesc.type==2)>Cluster</#if>\n                                                        <#if appDesc?? && (appDesc.type==5)>Sentinel</#if>\n                                                        <#if appDesc?? && (appDesc.type==6)>Standalone</#if>\n                                                    \">\n                                                </div>\n                                            </div>\n\n                                             <div class=\"row offset-md-1\">\n                                                <label class=\"col-form-label col-md-2 text-end\"> 应用描述: </label>\n                                                <div class=\"col-md-3\">\n                                                    <input id=\"appIntro\" type=\"text\" name=\"appIntro\" class=\"form-control\"\n                                                           readonly value=\"\n                                                        <#if appDesc?? && appDesc.intro??>\n                                                            ${appDesc.intro!}\n                                                        </#if>\n                                                    \">\n                                                </div>\n                                                <label class=\"col-form-label col-md-2 text-end\"> Redis版本: </label>\n                                                <div class=\"col-md-3\">\n                                                    <input id=\"version\" type=\"text\" name=\"type\" class=\"form-control\"\n                                                           readonly value=\"${appImport.redisVersionName!}\">\n                                                </div>\n                                            </div>\n\n                                            <div class=\"row offset-md-1\">\n                                                <label class=\"col-form-label col-md-2 text-end\"> 应用总内存: </label>\n                                                <div class=\"col-md-3\">\n                                                    <input id=\"appMem\" type=\"text\" name=\"appMem\" class=\"form-control\"\n                                                           readonly value=\"${appImport.memSize!} G\">\n                                                </div>\n\n                                                <label class=\"col-form-label col-md-2 text-end\"> 是否测试: </label>\n                                                <div class=\"col-md-3\">\n                                                    <input id=\"isTest\" type=\"text\" name=\"isTest\" class=\"form-control\" readonly\n                                                           value=\"\n                                                        <#if appDesc?? && (appDesc.isTest==0)>正式</#if>\n                                                        <#if appDesc?? && (appDesc.isTest==1)>测试</#if>\n                                                    \">\n                                                </div>\n                                            </div>\n                                        </div>\n                                    </div>\n                                </form>\n                            </div>\n\n                            <div class=\"row d-none\" id=\"redisVersion\" style=\"text-align: center\">\n                                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                                    <div class=\"row offset-md-1 col-md-11\">\n                                        <label> redis版本状态: </label>\n                                        <#if hasRedisVersion?? && (hasRedisVersion==0)>\n                                            <label> ${appImport.redisVersionName!} 不存在\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/manage/app/resource/index?tab=redis\">【创建版本】</a>\n                                            </label>\n                                        </#if>\n                                    </div>\n                                </form>\n                            </div>\n\n                            <div class=\"row display\" id=\"appBuild\" style=\"text-align: center\">\n                                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                                    <div class=\"row offset-md-1\">\n                                        <label class=\"col-form-label col-md-2 text-end\"> 应用创建状态: </label>\n                                        <#if (appImport.status==21)>\n                                            <label class=\"col-form-label col-md-7\" style=\"text-align: left\">\n                                                请部署应用：${appImport.appId!}\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/manage/app/initAppDeploy?appId=${appImport.appId!}&importId=${appImport.id!}\">【部署应用】</a>\n                                            </label>\n                                        </#if>\n                                        <#if (appImport.status==22)>\n                                            <label class=\"col-form-label col-md-7\"\n                                                   style=\"text-align: left\"> ${appImport.appId!} 应用部署中，请稍等...\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/manage/task/flow?taskId=${appImport.appBuildTaskId!}\">【查看部署任务】</a>\n                                            </label>\n                                        </#if>\n                                        <#if (appImport.status==23)>\n                                            <label class=\"col-form-label col-md-7\"\n                                                   style=\"text-align: left;color: red\"> ${appImport.appId!}应用部署异常，请\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/manage/task/flow?taskId=${appImport.appBuildTaskId!}\">【修复】</a>\n                                                或\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/manage/app/initAppDeploy?appId=${appImport.appId!}&importId=${appImport.id!}\"\n                                                   onclick=\"return preRebuildApp(${appImport.id!},${appImport.appId!})\">【重新部署】</a>\n                                            </label>\n                                        </#if>\n                                    </div>\n                                </form>\n                            </div>\n\n                            <div class=\"row d-none\" id=\"migrate\" style=\"text-align: center\">\n                                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                                    <div class=\"row offset-md-1\">\n                                        <label class=\"col-form-label col-md-2 offset-md-3\"> 数据迁移状态: </label>\n                                        <#if (appImport.status==30)>\n                                            <label class=\"col-form-label col-md-7\" style=\"text-align: left\"> 暂无数据迁移任务，请\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/data/migrate/init?importId=${appImport.id!}\">【进行数据迁移】</a>\n                                            </label>\n                                        </#if>\n                                        <#if (appImport.status==32)>\n                                            <label class=\"col-form-label col-md-7\" style=\"text-align: left\"> 数据迁移中，请稍等...\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/data/migrate/index?migrateId=${appImport.migrateId!}&status=-2\">【查看迁移任务】</a>\n                                            </label>\n                                        </#if>\n                                        <#if (appImport.status==33)>\n                                            <label class=\"col-form-label col-md-7\" style=\"text-align: left;color: red\">\n                                                数据迁移异常，请\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/data/migrate/index?migrateId=${appImport.migrateId!}&status=-2\">【修复】</a>\n                                                或\n                                                <a target=\"_blank\"\n                                                   href=\"${request.contextPath}/data/migrate/init?importId=${appImport.id!}\"\n                                                   onclick=\"if(window.confirm('确认 应用${appImport.appId!} 数据已经清空 或 允许重写?')){return true;}else{return false;}\">\n                                                    【重新迁移】\n                                                </a>\n                                            </label>\n                                        </#if>\n\n                                    </div>\n                                </form>\n                            </div>\n\n                            <div class=\"row d-none\" id=\"done\" style=\"text-align: center\">\n                                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                                    <div class=\"row offset-md-1\">\n                                        <label style=\"color: #00BE67; font: bold;\">\n                                            恭喜您，应用导入成功！\n                                            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appImport.appId!}\">【查看应用】</a>\n                                        </label>\n                                    </div>\n                                </form>\n                            </div>\n\n                        </div>\n\n                        <div class=\"modal-footer\">\n                            <button id=\"import\" type=\"button\" class=\"btn btn-primary\"\n                                    onclick=\"importApp(${appImport.id!},11,0,0)\">\n                                开始导入\n                            </button>\n                        </div>\n                    </form>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n<input type=\"hidden\" id=\"status\" name=\"status\" value=\"${appImport.status!}\">\n\n<script>\n    var navs = [\"appInfo\", \"createVersion\", \"build\", \"appMigrate\", \"importDone\"];\n    var divs = [\"pre\", \"redisVersion\", \"appBuild\", \"migrate\", \"done\"];\n    $(function () {\n        var status = document.getElementById(\"status\").value;\n        console.log(\"status: \" + status);\n        if (status == 0) {\n            $(\"#import\").html(\"开始导入\");\n            active(\"appInfo\");\n            show(\"pre\");\n        } else if (status >= 10 && status < 20) {\n            hidden(\"import\");\n            complete(\"appInfo\");\n            active(\"createVersion\");\n            show(\"redisVersion\");\n        } else if (status >= 20 && status < 30) {\n            hidden(\"import\");\n            complete(\"appInfo\");\n            complete(\"createVersion\");\n            active(\"build\");\n            show(\"appBuild\");\n        } else if (status >= 30 && status < 40) {\n            hidden(\"import\");\n            complete(\"appInfo\");\n            complete(\"createVersion\");\n            complete(\"build\");\n            active(\"appMigrate\");\n            show(\"migrate\");\n        } else if (status == 3) {\n            $(\"#import\").html(\"导入完成\");\n            display(\"import\");\n            disable(\"import\");\n            complete(\"appInfo\");\n            complete(\"createVersion\");\n            complete(\"build\");\n            complete(\"appMigrate\");\n            active(\"importDone\");\n            show(\"done\");\n        }\n    });\n\n    function importApp(importId, status, appBuildTaskId, migrateId) {\n        $.get(\n            '/import/app/goOn.json',\n            {\n                importId: importId,\n                status: status,\n                appBuildTaskId: appBuildTaskId,\n                migrateId: migrateId\n            },\n            function (data) {\n                var success = data.success;\n                if (success == 1) {\n                    console.log(\"success: \" + success);\n                    window.location.reload();\n                }\n            }\n        );\n    }\n\n    function updateMigrate(importId) {\n        var migrateId = document.getElementById(\"migrateId\");\n        if (migrateId.value == '') {\n            migrateId.focus();\n            return false;\n        } else if (migrateId.value > 0) {\n            importApp(importId, 0, 0, migrateId.value);\n        }\n    }\n\n    function updateAppBuild(importId) {\n        var appBuildTaskId = document.getElementById(\"appBuildTaskId\");\n        if (appBuildTaskId.value == '') {\n            appBuildTaskId.focus();\n            return false;\n        } else if (appBuildTaskId.value > 0) {\n            importApp(importId, 0, appBuildTaskId.value, 0);\n        }\n    }\n\n    function preRebuildApp(importId, appId) {\n        $.get(\n            '/import/app/preRebuildApp.json',\n            {\n                importId: importId,\n                appId: appId\n            },\n            function (data) {\n                var success = data.success;\n                if (success == 1) {\n                    console.log(\"preRebuildApp success\");\n                    return true;\n                } else {\n                    alert(\"应用部署回退异常！\");\n                    return false;\n                }\n            }\n        );\n    }\n\n    function show(id) {\n        display(id)\n        for (var i = 0; i < divs.length; i++) {\n            if (divs[i] != id) {\n                hidden(divs[i]);\n            }\n        }\n    }\n\n    function warn(id) {\n        $(\"#\" + id).addClass(\"warn\");\n    }\n\n    function disable(id) {\n        $(\"#\" + id).removeClass(\"active\").addClass(\"disabled\");\n    }\n\n    function active(id) {\n        $(\"#\" + id).removeClass(\"disabled\").removeClass(\"warn\").addClass(\"active\");\n    }\n\n    function complete(id) {\n        $(\"#\" + id).removeClass(\"disabled\").removeClass(\"active\").removeClass(\"warn\").addClass(\"complete\");\n    }\n\n    function hidden(id) {\n        $(\"#\" + id).removeClass(\"display\").addClass(\"d-none\");\n    }\n\n    function display(id) {\n        $(\"#\" + id).removeClass(\"d-none\").addClass(\"display\");\n    }\n</script>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appImport/index.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">应用导入列表</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-hover table-sm\" id=\"tableDataList\">\n                    <thead>\n                    <tr>\n                        <th scope=\"col\">序号</th>\n                        <th scope=\"col\">源：实例信息</th>\n                        <th scope=\"col\">目标：应用id</th>\n                        <th scope=\"col\">应用部署任务id</th>\n                        <th scope=\"col\">迁移任务id</th>\n                        <th scope=\"col\">创建时间</th>\n                        <th scope=\"col\">更新时间</th>\n                        <th scope=\"col\">导入状态</th>\n                        <th scope=\"col\">查看流程</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                        <#list appImportList as record>\n                            <#assign app_id = record.appId>\n                            <tr>\n                                <td>${record_index + 1!}</td>\n                                <td>\n                                    <#assign instanceInfo = record.instanceInfo?replace(\"\\n\", '<br/>')>\n                                    ${instanceInfo!}\n                                </td>\n                                <td>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                                </td>\n                                <td>\n                                    <#if record.appBuildTaskId?? && (record.appBuildTaskId > 0)>\n                                        <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.appBuildTaskId!}\">${record.appBuildTaskId!}</a>\n                                    </#if>\n                                </td>\n                                <td>\n                                    <#if (record.migrateId>0)>${record.migrateId!}</#if>\n                                </td>\n                                <td>\n                                    ${record.createTime?string('yyyy-MM-dd HH:mm:ss')}\n                                </td>\n                                <td>\n                                    ${record.updateTime?string('yyyy-MM-dd HH:mm:ss')}\n                                </td>\n                                <td>${appImportStatusMap?api.get(record.status).info!}</td>\n                                <td>\n                                    <#if (record.status==0)>\n                                        <a target=\"_blank\" type=\"button\" class=\"btn btn-sm btn-success\"\n                                           href=\"${request.contextPath}/import/app/init?importId=${record.id!}\">导入</a>\n                                    </#if>\n                                    <#if (record.status!=0)>\n                                        <a target=\"_blank\" type=\"button\" class=\"btn btn-sm btn-info\"\n                                           href=\"${request.contextPath}/import/app/init?importId=${record.id!}\">查看</a>\n                                    </#if>\n                                </td>\n                            </tr>\n                        </#list>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appImport/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"index.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n\n<script>\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        if (!jQuery().dataTable) {\n          return;\n        }\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          \"ordering\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 10,\n          \"language\": {\n            \"paginate\": {\n              \"first\": \"<<\",\n              \"previous\": \"<\",\n              \"next\": \">\",\n              \"last\": \">>\"\n            },\n            \"lengthMenu\": \"每页显示 _MENU_条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\"\n          }\n        });\n      }\n    };\n  }();\n\n  $(function () {\n    TableManaged.init();\n  });\n</script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appOps/appCodeInit.html",
    "content": "<div class=\"row col-12\">\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h3 class=\"card-title\">redis密码修改</h3>\n    </div>\n    <div class=\"card-body\">\n      <!-- BEGIN FORM-->\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"form-body\">\n          <#if customPassword?? && (customPassword != '')>\n            <input type=\"hidden\" id=\"oldPassword\" name=\"oldPassword\" value=\"${customPassword!}\">\n          </#if>\n          <#if !(customPassword??) || (customPassword == '')>\n            <input type=\"hidden\" id=\"oldPassword\" name=\"oldPassword\" value=\"${pkey!}\">\n          </#if>\n          <input type=\"hidden\" id=\"appId\" name=\"appId\" value=\"${appId!}\">\n          <div class=\"form-group row\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              redis密码\n            </label>\n            <div class=\"col-md-4\">\n              <#if customPassword?? && (customPassword != '')>\n                <input type=\"text\" name=\"password\" id=\"password\" value=\"${customPassword!}\" class=\"form-control\"/>\n              </#if>\n              <#if !(customPassword??) || (customPassword == '')>\n                <input type=\"text\" name=\"password\" id=\"password\" value=\"${pkey!}\" class=\"form-control\"/>\n              </#if>\n            </div>\n            <div class=\"col-md-2\">\n              <#if customPassword?? && (customPassword != '')>\n                <input type=\"checkbox\" id=\"isSetPasswd\" name=\"isSetPasswd\" checked=\"checked\"/>设置自定义密码\n              </#if>\n              <#if !(customPassword??) || (customPassword == '')>\n                <input type=\"checkbox\" id=\"isSetPasswd\" name=\"isSetPasswd\" />设置自定义密码\n              </#if>\n            </div>\n            <div class=\"col-md-2\">\n              <button type=\"button\" class=\"btn btn-sm btn-primary\" onclick=\"updateAppPassword('${customPassword!}')\">\n                更新\n              </button>\n              <button type=\"button\" class=\"btn btn-sm btn-primary\" onclick=\"checkAppPassword()\">\n                校验\n              </button>\n            </div>\n          </div>\n          <div class=\"form-group row\">\n            <label class=\"col-form-label offset-md-3 col-md-6\" style=\"color: orangered; text-align: left; \">\n              自定义密码：用户定义的密码，设置的值即为密码；<br>\n              默认密码：设置的值仅为基础值，对该值经过系统默认加密处理从而生成密码。<br>\n              自定义密码优先级高于默认密码，如需清除密码，请先置空自定义密码，然后置空默认密码。\n            </label>\n          </div>\n          <div class=\"form-group row\">\n            <label class=\"col-form-label offset-md-3 col-md-8\" style=\"color: orange; text-align: left; \">\n              请注意，redis6.0.0——6.0.8版本由于源码bug(#7899)，不支持清除密码 &nbsp;&nbsp;<a href=\"https://raw.githubusercontent.com/redis/redis/6.0/00-RELEASENOTES\">点击查看</a>\n            </label>\n          </div>\n        </div>\n      </form>\n      <!-- END FORM-->\n    </div>\n  </div>\n\n  <#if aclEnable?? && aclEnable>\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">acl用户密码</h3>\n      </div>\n      <div class=\"card-body\">\n        <table class=\"table table-bordered table-striped table-hover text-center\">\n          <thead>\n            <tr>\n              <th>用户名</th>\n              <th>密码</th>\n            </tr>\n            </thead>\n            <tbody>\n            <tr>\n              <td>cachecloud(管理员)</td><td>${adminPassword!}</td>\n            </tr>\n            <tr>\n              <td>default(默认用户)</td><td>${defaultPassword!}</td>\n            </tr>\n            <tr>\n              <td>readonly(只读用户)</td><td>${readonlyPassword!}</td>\n            </tr>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </#if>\n</div>\n\n<script>\n    function updateAppPassword(customPwd) {\n        var oldPassword = document.getElementById(\"oldPassword\");\n        var password = document.getElementById(\"password\");\n        var appId = document.getElementById(\"appId\");\n        var isSetPasswd = document.getElementById(\"isSetPasswd\").checked;\n        if(customPwd != null && customPwd != ''){\n            if(isSetPasswd == true){\n                if(oldPassword.value.trim() == password.value.trim()){\n                    alert(\"密码未变更,不更新!\");\n                    return false;\n                }\n            }\n        }\n        if(customPwd == null || customPwd == ''){\n            if(isSetPasswd == false){\n                if(oldPassword.value.trim() == password.value.trim()){\n                    alert(\"密码未变更,不更新!\");\n                    return false;\n                }\n            }\n        }\n\n        var originType = (customPwd != null && customPwd != '') ? \"自定义密码\" : \"默认密码\";\n        var newType = isSetPasswd == true ? \"自定义密码\" : \"默认密码\";\n\n        $.get(\n            '${request.contextPath}/manage/app/updateAppPassword.json',\n            {\n                password: password.value.trim(),\n                appId: appId.value,\n                isSetPasswd: isSetPasswd\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"设置成功! 原有:\" + originType + \"【\"+oldPassword.value+\"】已更新成新密码：\" + newType + \"【\"+password.value+\"】\");\n                    window.location.reload();\n                    // $(\"#oldPassword\").attr(\"value\",password.value);\n                } else {\n                    alert(\"设置失败!\");\n                }\n            }\n        );\n    }\n\n    // 检验密码一致性\n    function checkAppPassword() {\n        //var password = document.getElementById(\"password\");\n        var appId = document.getElementById(\"appId\");\n\n        $.get(\n            '${request.contextPath}/manage/app/checkAppPassword.json',\n            {\n                //password: password.value,\n                appId: appId.value\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"应用密码是有效且一致!\");\n                } else {\n                    alert(\"应用密码是有效不一致!\");\n                }\n            }\n        );\n    }\n</script>\n\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appOps/appInfoAndAudit.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          应用详情-\n          ${appDesc.name!}\n          (${appDesc.typeDesc!})\n        </h3>\n      </div>\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-bordered table-striped table-hover\">\n          <tbody>\n          <tr>\n            <td>应用id</td>\n            <td>${appDesc.appId!}</td>\n            <td>应用名称</td>\n            <td>${appDesc.name!}</td>\n          </tr>\n          <tr>\n            <td>应用申请人</td>\n            <td>${appDesc.officer!}</td>\n            <td>应用类型</td>\n            <td>${appDesc.typeDesc!}</td>\n          </tr>\n          <tr>\n            <td>负责人</td>\n            <td>${appDesc.officer!}</td>\n            <td>详情</td>\n            <td>${appDesc.intro!}</td>\n          </tr>\n          <tr>\n            <td>创建时间</td>\n            <td>${appDesc.createTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n            <td>审批通过时间</td>\n            <td>${appDesc.passedTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n          </tr>\n          <tr>\n            <td>测试</td>\n            <td>\n              <#if (appDesc.isTest == 0)>否\n              <#elseif (appDesc.isTest == 1)>是\n              </#if>\n            </td>\n            <td>后端是否有数据源</td>\n            <td>\n              <#if (appDesc.hasBackStore == 0)>有\n              <#elseif (appDesc.hasBackStore == 1)>无\n              </#if>\n            </td>\n          </tr>\n          <tr>\n            <td>是否需要持久化</td>\n            <td>\n              <#if (appDesc.needPersistence == 0)>不需要\n              <#elseif (appDesc.needPersistence == 1)>需要\n              </#if>\n            </td>\n            <td>预计对象数</td>\n            <td>${appDesc.forecastObjNum!}</td>\n          </tr>\n          <tr>\n            <td>预计QPS</td>\n            <td>${appDesc.forecaseQps!}</td>\n            <td>是否需要热备</td>\n            <td>\n              <#if (appDesc.needHotBackUp == 0)>不需要\n              <#elseif (appDesc.needHotBackUp == 1)>需要\n              </#if>\n            </td>\n          </tr>\n          <tr>\n            <td>内存报警阀值</td>\n            <td>${appDesc.memAlertValue!}%</td>\n            <td>淘汰策略</td>\n            <td>${appDesc.maxmemoryPolicyDesc!}</td>\n          </tr>\n          </tbody>\n        </table>\n      </div>\n    </div>\n\n    <div class=\"card\">\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-bordered table-striped table-hover\">\n          <thead>\n          <tr>\n            <th>审批id</th>\n            <th>申请人</th>\n            <th>申请类型</th>\n            <th>申请详情</th>\n            <th>申请时间</th>\n            <th>审批时间</th>\n            <th>审批人</th>\n            <th>审批结果</th>\n            <th>审批意见</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list appAuditList as appAudit>\n            <tr>\n              <td>${appAudit.id!}</td>\n              <td>${appAudit.userName!}</td>\n              <td>\n                <#if (appAudit.type == 0)>申请应用\n                <#elseif (appAudit.type == 1)>应用扩容\n                <#elseif (appAudit.type == 2)>修改配置\n                </#if>\n              </td>\n              <td>${appAudit.info!}</td>\n              <td>${appAudit.createTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n              <td>${appAudit.modifyTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n              <td>\n                <#if appAudit.appAuditLog?? && appAudit.appAuditLog.appUser??>\n                  ${appAudit.appAuditLog.appUser.name!}\n                </#if>\n              </td>\n              <td>\n                <#if (appAudit.status == 1)>审批通过\n                <#elseif (appAudit.status == -1)>驳回\n                </#if>\n              </td>\n              <td>${appAudit.refuseReason!}</td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appOps/appInstance.html",
    "content": "<script>\n  $(window).on('load', function () {\n    $('.selectpicker').selectpicker({'selectedText': 'cat'});\n    $('.selectpicker').selectpicker('refresh');\n    $('.selectpicker').selectpicker('render');\n  });\n  $(function () {\n\n    var appId = document.getElementById('appId').value;\n\n    $('.selectpicker').selectpicker();\n\n    var slaveIp_select = document.getElementById('slaveIp');\n    $(\"div[name='addSlave-modal']\").on('shown.bs.modal', function () {\n      var addSlaveModal_id = $(this).attr('id');\n      var instanceId = addSlaveModal_id.split('_')[1];\n\n      if (document.getElementById('slaveIp' + instanceId).options.length == 0) {\n        for (var i = 0; i < slaveIp_select.options.length; i++) {\n          var text = slaveIp_select.options[i].text;\n          var value = slaveIp_select.options[i].value;\n          $('#slaveIp' + instanceId).append(\"<option value='\" + value + \"'>\" + text + \"</option>\");\n        }\n\n        $('#slaveIp' + instanceId).selectpicker('refresh');\n        $('#slaveIp' + instanceId).selectpicker('render');\n\n        $('.dropdown-toggle').on('click',function(){\n          $('.dropdown-toggle').dropdown();\n        });\n      }\n    });\n\n    $(\"div[name='addConfig-modal']\").on('shown.bs.modal', function () {\n      var addConfigModal_id = $(this).attr('id');\n      var instanceId = addConfigModal_id.split('_')[1];\n      $.post('${request.contextPath}/admin/app/redisConfig',\n              {\n                appId: appId,\n                instanceId: instanceId\n              },\n              function (data) {\n                var status = data.status;\n                if (status == 1) {\n                  var redisConfigMap = data.redisConfigMap;\n                  $('#appConfigKey' + instanceId).append(\"<option value=''>\" + '请选择' + \"</option>\");\n                  for (var key in redisConfigMap) {\n                    var item = '配置项：' + key + \" | 配置值：\" + redisConfigMap[key];\n                    $('#appConfigKey' + instanceId).append(\"<option value='\" + key + \"'>\" + item + \"</option>\");\n                  }\n                  $('#appConfigKey' + instanceId).selectpicker('refresh');\n                  $('#appConfigKey' + instanceId).selectpicker('render');\n\n                  $('.dropdown-toggle').on('click', function () {\n                    $('.dropdown-toggle').dropdown();\n                  });\n                }\n              }\n      );\n    });\n\n    $(\"div[name='configAndRestart-modal']\").on('shown.bs.modal', function () {\n      var instanceId;\n      var masterIds = document.getElementsByName(`selectOneMaster`);\n      if(masterIds != undefined && masterIds.length != undefined && masterIds.length != null){\n        for (var key of masterIds) {\n          instanceId = masterIds[0].value;\n          break;\n        }\n      }\n      if(!(instanceId > 0)){\n        alert(\"未能获取配置列表，请手动填写！\");\n        return false;\n      }\n      $.post('${request.contextPath}/admin/app/redisConfig',\n              {\n                appId: appId,\n                instanceId: instanceId\n              },\n              function (data) {\n                var status = data.status;\n                if (status == 1) {\n                  var redisConfigMap = data.redisConfigMap;\n                  $('#configListId').append(\"<option value=''>\" + '请选择' + \"</option>\");\n                  for (var key in redisConfigMap) {\n                    var item = '配置项：' + key + \" | 配置值：\" + redisConfigMap[key];\n                    $('#configListId').append(\"<option value='\" + key + \"'>\" + item + \"</option>\");\n                  }\n                  $('#configListId').selectpicker('refresh');\n                  $('#configListId').selectpicker('render');\n\n                  $('.dropdown-toggle').on('click', function () {\n                    $('.dropdown-toggle').dropdown();\n                  });\n                }\n              }\n      );\n    });\n\n    $(\"div[name='configCommand-modal']\").on('shown.bs.modal', function () {\n      var instanceId;\n      var masterIds = document.getElementsByName(`selectOneMaster`);\n      if(masterIds != undefined && masterIds.length != undefined && masterIds.length != null){\n        for (var key of masterIds) {\n          instanceId = masterIds[0].value;\n          break;\n        }\n      }\n      if(!(instanceId > 0)){\n        alert(\"未能获取命令列表，请确认！\");\n        return false;\n      }\n      //清空\n      document.getElementById('cmdListId').options.length = 0;\n      $('#cmdListId').selectpicker('destroy');\n      $('#cmdListId').selectpicker();\n      $('#existCommandName').html(\"\");\n      $('#commandName').html(\"\");\n\n      $.post('${request.contextPath}/admin/app/redisCommand',\n              {\n                appId: appId,\n                instanceId: instanceId\n              },\n              function (data) {\n                var status = data.status;\n                if (status == 1) {\n                  var commandList = data.commandList;\n                  var cmdListHtml = \"<option value=''>\" + '请选择' + \"</option>\";\n\n                  // 获取重命名配置，并更新已选\n                  getRenameCommand(appId, instanceId).then(existCmdSet => {\n                    for (var index in commandList) {\n                      cmdListHtml += \"<option id='\" + commandList[index] + \"'\";\n                      if(existCmdSet != undefined && existCmdSet.has(commandList[index])){\n                        cmdListHtml += \" selected \";\n                      }\n                      cmdListHtml += \">\" + commandList[index] + \"</option>\";\n                    }\n                    $('#cmdListId').selectpicker('destroy');\n                    $('#cmdListId').append(cmdListHtml);\n                    $('#cmdListId').selectpicker();\n\n                    setSelectedRenameCmd();\n                    $('.dropdown-toggle').on('click', function () {\n                      $('.dropdown-toggle').dropdown();\n                    });\n                  })\n                  .catch(error => {\n                    alert(error); // 打印错误信息\n                    for (var index in commandList) {\n                      cmdListHtml += \"<option id='\" + commandList[index] + \"'\";\n                      cmdListHtml += \">\" + commandList[index] + \"</option>\";\n                    }\n                    $('#cmdListId').selectpicker('destroy');\n                    $('#cmdListId').append(cmdListHtml);\n                    $('#cmdListId').selectpicker();\n                    $('.dropdown-toggle').on('click', function () {\n                      $('.dropdown-toggle').dropdown();\n                    });\n                  });\n                }\n              }\n      );\n    });\n\n    $(function () { $(\"[data-bs-toggle='tooltip']\").tooltip(); });\n\n    {\n      var search = window.location.search;\n      var appId = getSearchString(\"appId\", search);\n      var configName = getSearchString(\"configName\", search);\n      var expectValue = getSearchString(\"expectValue\", search);\n      var instanceIds = getSearchString(\"instanceIds\", search);\n      if(appId != null && configName != null && instanceIds != null) {\n        document.getElementById(\"configRestartId\").click();\n        $('#configAndRestartModal input[name=\"isUpdateConfig\"][value=\"1\"]').prop(\"checked\", \"checked\");\n        moduleSelect('1');\n        document.getElementById(\"configName\").value = configName;\n        var configValues = document.getElementsByName(\"configValue\");\n        configValues[0].value = expectValue;\n        var instanceSelect = document.getElementById(\"configInstanceList\");\n        var instanceIdStrs = instanceIds.split(\",\");\n        $('#configInstanceList').selectpicker('val', instanceIdStrs);\n        $('#configInstancneList').selectpicker('refresh');\n\n        var transferSelect = document.getElementById(\"msTransferFlag\");\n        for(var i = 0; i < transferSelect.options.length; i++){\n          if(transferSelect.options[i].value == 0){\n            transferSelect.options[i].selected = true;\n          }else{\n            transferSelect.options[i].selected = false;\n          }\n        }\n      }\n    }\n  })\n\n  function getRenameCommand(appId, instanceId){\n   return new Promise((resolve, reject) => {\n    $.post('${request.contextPath}/admin/app/redisRenameCommand',\n          {\n            appId: appId,\n            instanceId: instanceId\n          },\n          function (data) {\n            var status = data.status;\n            var commandSet = new Set();\n            if (status == 1) {\n              var renameCommands = data.renameCommands;\n              var renameCmdHtml = \"\";\n              for (var index in renameCommands) {\n                renameCmdHtml += renameCommands[index].value + \"\\n\";\n                var cmdConfig = renameCommands[index].value;\n                commandSet.add(cmdConfig.substring(0, cmdConfig.indexOf(\" \")).trim());\n              }\n              $('#existCommandName').html(renameCmdHtml);\n              resolve(commandSet);\n            } else {\n              reject(\"未获取到已有重命名命令，请确认\");\n            }\n          }\n      );\n      });\n  }\n</script>\n\n<script>\n  // key(需要检索的键） url（传入的需要分割的url地址，例：?id=2&age=18）\n  function getSearchString(key, Url) {\n      var str = Url;\n      str = str.substring(1, str.length); // 获取URL中?之后的字符（去掉第一位的问号）\n      // 以&分隔字符串，获得类似name=xiaoli这样的元素数组\n      var arr = str.split(\"&\");\n      var obj = new Object();\n      // 将每一个数组元素以=分隔并赋给obj对象\n      for (var i = 0; i < arr.length; i++) {\n          var tmp_arr = arr[i].split(\"=\");\n          obj[decodeURIComponent(tmp_arr[0])] = decodeURIComponent(tmp_arr[1]);\n      }\n      return obj[key];\n  }\n\n  function setSelectedConfig(){\n      var configName = $('#configListId option:selected').val();\n      var configItem = $('#configListId option:selected').text();\n      var splitStr = configItem.split(\"配置值：\");\n      var expectValue = splitStr[1];\n      if(configName != null){\n          document.getElementById(\"configName\").value = configName;\n          var configValues = document.getElementsByName(\"configValue\");\n          configValues[0].value = expectValue;\n      }\n  }\n\n  function setSelectedRenameCmd(){\n    var newCommandHtml = \"\";\n    $('#cmdListId option:selected').each(function(){\n      if($(this).val() != null){\n        newCommandHtml += $(this).val() + \"\\n\";\n      }\n    });\n    document.getElementById(\"commandName\").value = newCommandHtml;\n  }\n\n  function startInstance(appId, instanceId) {\n      if (confirm(\"确认要开启\" + instanceId + \"实例吗?\")) {\n          $.ajax({\n              type: \"get\",\n              url: \"${request.contextPath}/manage/instance/startInstance.json\",\n              data:\n                  {\n                      appId: appId,\n                      instanceId: instanceId\n                  },\n              success: function (result) {\n                  if (result.success == 1) {\n                      alert(\"开启成功!\");\n                  } else {\n                      alert(\"开启失败, msg: \" + result.message);\n                  }\n                  window.location.reload();\n              }\n          });\n      }\n  }\n\n  function shutdownInstance(appId, instanceId) {\n      if (confirm(\"确认要下线\" + instanceId + \"实例吗?\")) {\n          $.ajax({\n              type: \"get\",\n              url: \"${request.contextPath}/manage/instance/shutdownInstance.json\",\n              data:\n                  {\n                      appId: appId,\n                      instanceId: instanceId\n                  },\n              success: function (result) {\n                  if (result.success == 1) {\n                      alert(\"关闭成功!\");\n                  } else {\n                      alert(\"关闭失败, msg: \" + result.message);\n                  }\n                  window.location.reload();\n              }\n          });\n      }\n  }\n\n  function addConfigInstance(appId, instanceId, host, port) {\n      var configKey = $('#appConfigKey' + instanceId).selectpicker('val');\n      var newConfigKey = document.getElementById(\"newConfigKey\" + instanceId).value;\n      var configVal = document.getElementById(\"appConfigValue\" + instanceId).value;\n\n      if (newConfigKey == \"\") {\n          if (configKey == \"\") {\n              alert(\"配置项不能为空\");\n              return false;\n          }\n      } else {\n          configKey = newConfigKey;\n      }\n\n      if (confirm(\"确认更新实例\" + instanceId + \"的配置: \" + configKey + \":\" + configVal)) {\n\n\n          $.post(\"${request.contextPath}/manage/instance/addInstanceConfigChange\",\n              {\n                  appId: appId,\n                  host: host,\n                  port: port,\n                  instanceConfigKey: configKey,\n                  instanceConfigValue: configVal\n              },\n              function (data) {\n                  if (data.result == 1) {\n                      alert(\"配置更新成功!\");\n                      window.location.reload();\n                  } else {\n                      alert(\"配置更新失败...\");\n                  }\n              });\n      }\n  }\n\n  function forgetInstance(appId, instanceId) {\n      if (confirm(\"确认要永久下线\" + instanceId + \"实例吗?\")) {\n          $.ajax({\n              type: \"get\",\n              url: \"${request.contextPath}/manage/instance/forgetInstance.json\",\n              data:\n                  {\n                      appId: appId,\n                      instanceId: instanceId\n                  },\n              success: function (result) {\n                  if (result.success == 1) {\n                      alert(\"关闭成功!\");\n                  } else {\n                      alert(\"关闭失败, msg: \" + result.message);\n                  }\n                  window.location.reload();\n              }\n          });\n      }\n  }\n\n\n  function redisClusterFailOverManual(appId, instanceId) {\n      var redisClusterFailOverManualBtn = document.getElementById(\"redisClusterFailOverManualBtn\" + instanceId);\n      redisClusterFailOverManualBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/clusterSlaveFailOver',\n          {\n              appId: appId,\n              slaveInstanceId: instanceId,\n              failoverParam: ''\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisClusterFailOverManualInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Success!</strong>执行成功，应用的拓扑结构要1分钟之后生效，请耐心等待<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisClusterFailOverManualModal\" + instanceId;\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisClusterFailOverManualBtn.disabled = false;\n                  $(\"#redisClusterFailOverManualInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function redisClusterFailOverForce(appId, instanceId) {\n      var redisClusterFailOverForceBtn = document.getElementById(\"redisClusterFailOverForceBtn\" + instanceId);\n      redisClusterFailOverForceBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/clusterSlaveFailOver',\n          {\n              appId: appId,\n              slaveInstanceId: instanceId,\n              failoverParam: 'force'\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisClusterFailOverForceInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Success!</strong>执行成功，应用的拓扑结构要1分钟之后生效，请耐心等待<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisClusterFailOverForceModal\" + instanceId;\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisClusterFailOverForceBtn.disabled = false;\n                  $(\"#redisClusterFailOverForceInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function redisClusterFailOverTakeOver(appId, instanceId) {\n      var redisClusterFailOverTakeOverBtn = document.getElementById(\"redisClusterFailOverTakeOverBtn\" + instanceId);\n      redisClusterFailOverTakeOverBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/clusterSlaveFailOver',\n          {\n              appId: appId,\n              slaveInstanceId: instanceId,\n              failoverParam: 'takeover'\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisClusterFailOverTakeOverInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Success!</strong>执行成功，应用的拓扑结构要1分钟之后生效，请耐心等待<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisClusterFailOverTakeOverModal\" + instanceId;\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisClusterFailOverTakeOverBtn.disabled = false;\n                  $(\"#redisClusterFailOverTakeOverInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function redisClusterDelNode(appId, instanceId) {\n      var redisClusterDelNodeBtn = document.getElementById(\"redisClusterDelNodeBtn\" + instanceId);\n      redisClusterDelNodeBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/clusterDelNode.json',\n          {\n              appId: appId,\n              delNodeInstanceId: instanceId,\n          },\n          function (data) {\n              var success = data.success;\n              var message = data.message;\n              if (success == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisClusterDelNodeInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Success!</strong>执行成功，应用的拓扑结构要1分钟之后生效，请耐心等待<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisClusterDelNodeModal\" + instanceId;\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  alert(message);\n                  redisClusterDelNodeBtn.disabled = false;\n                  $(\"#redisClusterDelNodeInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function genSlaveIp(appId, instanceId) {\n      var genSlaveIpNote = document.getElementById('genSlaveIpNote' + instanceId);\n      genSlaveIpNote.style.display = 'none';\n      $.post(\n          '${request.contextPath}/manage/app/genSlaveIp',\n          {\n              appId: appId,\n              masterInstanceId: instanceId\n          },\n          function (data) {\n              var result = data.result;\n              var ip = data.machineRes;\n              if (result != 'success') {\n                  genSlaveIpNote.innerHTML = '自动生成slave节点ip失败: ' + result;\n                  genSlaveIpNote.style.display = '';\n              } else {\n                  console.log(ip);\n                  $('#slaveIp' + instanceId).selectpicker('val', ip);\n                  $('#slaveIp' + instanceId).selectpicker('refresh');\n                  console.log($('#slaveIp' + instanceId).selectpicker('val'));\n                  if ($('#slaveIp' + instanceId).selectpicker('val') == '-1') {\n                      genSlaveIpNote.innerHTML = '自动生成slave节点ip失败，请重试';\n                      genSlaveIpNote.style.display = '';\n                  }\n              }\n          }\n      );\n  }\n\n\n  function redisClusterAddSlave(appId, instanceId) {\n      var slaveIpObj = document.getElementById(\"slaveIp\" + instanceId);\n      var index = slaveIpObj.selectedIndex;\n      var slaveIp = slaveIpObj[index].value;\n      if (slaveIp == \"\" || slaveIp == \"-1\") {\n          alert(\"从节点Ip不能为空\");\n          return false;\n      }\n      var redisClusterAddSlaveBtn = document.getElementById(\"redisClusterAddSlaveBtn\" + instanceId);\n      redisClusterAddSlaveBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/addSlave',\n          {\n              appId: appId,\n              masterInstanceId: instanceId,\n              slaveHost: slaveIp\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisClusterAddSlaveInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Success!</strong>添加成功!<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisClusterAddSlaveModal\" + instanceId;\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisClusterAddSlaveBtn.disabled = false;\n                  $(\"#redisClusterAddSlaveInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function redisSentinelAddSlave(appId, instanceId) {\n      var slaveIp = document.getElementById(\"sentinelSlaveIp\" + instanceId);\n      if (slaveIp.value == \"\") {\n          alert(\"从节点Ip不能为空\");\n          slaveIp.focus();\n          return false;\n      }\n      var redisSentinelAddSlaveBtn = document.getElementById(\"redisSentinelAddSlaveBtn\" + instanceId);\n      redisSentinelAddSlaveBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/addSlave',\n          {\n              appId: appId,\n              masterInstanceId: instanceId,\n              slaveHost: slaveIp.value\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisSentinelAddSlaveInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Success!</strong>添加成功!<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisSentinelAddSlaveModal\" + instanceId;\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisSentinelAddSlaveBtn.disabled = false;\n                  $(\"#redisSentinelAddSlaveInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n\n  function redisSentinelFailOver(appId) {\n      var redisSentinelFailOverBtn = document.getElementById(\"redisSentinelFailOverBtn\");\n      redisSentinelFailOverBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/sentinelFailOver',\n          {\n              appId: appId\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisSentinelFailOverInfo\").html(\"<div class='alert alert-error' ><strong>Success!</strong>执行成功，应用的拓扑结构要1分钟之后生效，请耐心等待<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisSentinelFailOverModal\";\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisSentinelFailOverBtn.disabled = false;\n                  $(\"#redisSentinelFailOverInfo\").html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function redisSentinelReset(appId) {\n      var redisSentinelFailOverBtn = document.getElementById(\"redisSentinelResetBtn\");\n      redisSentinelFailOverBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/sentinelReset',\n          {\n              appId: appId\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisSentinelResetInfo\").html(\"<div class='alert alert-error' ><strong>Success!</strong>执行成功，应用的拓扑结构要1分钟之后生效，请耐心等待<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisSentinelResetModal\";\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisSentinelFailOverBtn.disabled = false;\n                  $(\"#redisSentinelResetInfo\").html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function redisAddFailSlotsMaster(appId, instanceId) {\n      var failSlotsMasterHost = document.getElementById(\"failSlotsMasterHost\" + instanceId);\n      var redisAddFailSlotsMasterBtn = document.getElementById(\"redisAddFailSlotsMasterBtn\" + instanceId);\n      redisAddFailSlotsMasterBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/addFailSlotsMaster',\n          {\n              appId: appId,\n              failSlotsMasterHost: failSlotsMasterHost.value,\n              instanceId: instanceId\n          },\n          function (data) {\n              if (data == 1 || data == 2) {\n                  if (data == 1) {\n                      alert(\"执行成功!\");\n                  } else {\n                      alert(\"集群所有slots已经分配，无需补充！\");\n                  }\n                  $(\"#redisAddFailSlotsMasterInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Success!</strong>执行成功，应用的拓扑结构要1分钟之后生效，请耐心等待<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisAddFailSlotsMasterModal\" + instanceId;\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisAddFailSlotsMasterBtn.disabled = false;\n                  $(\"#redisAddFailSlotsMasterInfo\" + instanceId).html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function redisAddSentinel(appId) {\n      var sentinelIp = document.getElementById(\"sentinelIp\");\n      if (sentinelIp.value == \"\") {\n          alert(\"sentinel Ip不能为空\");\n          slaveIp.focus();\n          return false;\n      }\n      var redisAddSentinelBtn = document.getElementById(\"redisAddSentinelBtn\");\n      redisAddSentinelBtn.disabled = true;\n      $.post(\n          '${request.contextPath}/manage/app/addSentinel',\n          {\n              appId: appId,\n              sentinelHost: sentinelIp.value\n          },\n          function (data) {\n              if (data == 1) {\n                  alert(\"执行成功!\");\n                  $(\"#redisAddSentinelInfo\").html(\"<div class='alert alert-error' ><strong>Success!</strong>添加成功!<button class='close' data-bs-dismiss='alert'></button></div>\");\n                  var targetId = \"#redisAddSentinelModal\";\n                  setTimeout(\"$('\" + targetId + \"').modal('hide');window.location.reload();\", 1000);\n              } else {\n                  redisAddSentinelBtn.disabled = false;\n                  $(\"#redisAddSentinelInfo\").html(\"<div class='alert alert-error' ><strong>Error!</strong>执行失败，请查找原因！<button class='close' data-bs-dismiss='alert'></button></div>\");\n              }\n          }\n      );\n  }\n\n  function moduleSelect(radioVal){\n      if(radioVal == '0'){\n          $(\"#moduleInfo\").attr(\"style\",\"display:none\");\n      }\n      if(radioVal == '1'){\n          $(\"#moduleInfo\").attr(\"style\",\"display:display\");\n      }\n  }\n\n  function addConfigRow(){\n      var ul = document.getElementById(\"configRowGroup\");\n      var lis = document.getElementsByName(`configRow`);\n      var li = lis[0].cloneNode(true);\n      li.getElementsByTagName('input')[0].value=\"\";\n      ul.appendChild(li);\n  }\n\n  function config(appId) {\n      var configList = null;\n      var configInstanceList = null;\n      var isUpdateConfig = $('input:radio[name=\"isUpdateConfig\"]:checked').val();\n      var tipInfo = \"滚动重启\";\n      if(isUpdateConfig == 1){\n          var configName = document.getElementById(\"configName\").value;\n          if(configName == null || configName == \"\"){\n              alert(\"请填写配置项\");\n              return false;\n          }\n          var configValues = document.getElementsByName(\"configValue\");\n          configList = [];\n          var newConfigValue = [];\n          for(i = 0; i < configValues.length; i++){\n              configList.push({\n                  configName: configName,\n                  configValue: configValues[i].value\n              });\n              newConfigValue.push(configValues[i].value);\n          }\n          if(configList.length < 1){\n              alert(\"请至少填写一个配置值\");\n              return false;\n          }\n          configInstanceList = [];\n          $('#configInstanceList option:selected').each(function(){\n              if($(this).val() != null){\n                  configInstanceList.push($(this).val());\n              }\n          });\n          if(configInstanceList.length == 0){\n              alert(\"请选择实例\");\n              return false;\n          }\n\n          tipInfo = \"修改配置\" + configName + \"的值为：\" + newConfigValue;\n      }\n      var msTransferFlag = document.getElementById(\"msTransferFlag\").value;\n      var data = {\n          appId: appId,\n          configFlag: isUpdateConfig == 1,\n          transferFlag: msTransferFlag == 1,\n          instanceList: configInstanceList,\n          configList: configList\n      };\n      if (confirm(\"确认\" + tipInfo)) {\n          document.getElementById(\"configAndRestartCloseBtn\").disabled = 'true';\n          document.getElementById(\"configAndRestartBtn\").disabled = 'true';\n          document.getElementById(\"msTransferFlag\").disabled = false;\n          document.getElementById(\"configInstanceList\").disabled = false;\n          document.getElementById(\"addConfigRowBtn\").disabled = false;\n          $(\"#configAndRestartModal\").find(\"input[type='radio']\").attr(\"disabled\", \"disabled\");\n          $(\"#configAndRestartModal\").find(\"input[type='text']\").attr(\"disabled\", \"disabled\");\n          $.ajax({\n              type: 'post',\n              url:'${request.contextPath}/manage/app/restart/updateConfig',\n              contentType:'application/json',\n              data: JSON.stringify(data),\n              dataType:'json',\n              success:function (data) {\n                  if (data.status == 200) {\n                      if(data.data.commandSet == true){\n                          alert(\"配置更新成功!\");\n                          window.location.href = \"${request.contextPath}/manage/app/index?appId=\" + appId;\n                      }else{\n                          alert(\"请点击重启，激活配置!\");\n                          document.getElementById(\"recordId\").value = data.data.recordId;\n                          document.getElementById(\"restartAfterConfigBtn\").style.display = 'inline';\n                          document.getElementById(\"configAndRestartCloseBtn\").disabled = 'true';\n                          document.getElementById(\"configAndRestartBtn\").disabled = 'true';\n                      }\n                  } else {\n                      alert(\"配置更新失败，请人工确认 : \" + data.error);\n                      document.getElementById(\"msTransferFlag\").disabled = 'true';\n                      document.getElementById(\"configInstanceList\").disabled = 'true';\n                      document.getElementById(\"addConfigRowBtn\").disabled = 'true';\n                      $(\"#configAndRestartModal\").find(\"input[type='radio']\").removeAttr(\"disabled\");\n                      $(\"#configAndRestartModal\").find(\"input[type='text']\").removeAttr(\"disabled\");\n                      document.getElementById(\"configAndRestartCloseBtn\").disabled = false;\n                      document.getElementById(\"configAndRestartBtn\").disabled = false;\n                  }\n              }\n          });\n      }\n  }\n\n  function restart(appId) {\n      var configList = null;\n      var configInstanceList = null;\n      var isUpdateConfig = $('input:radio[name=\"isUpdateConfig\"]:checked').val();\n      var tipInfo = \"滚动重启\";\n      if(isUpdateConfig == 1){\n          var configName = document.getElementById(\"configName\").value;\n          if(configName == null || configName == \"\"){\n              alert(\"请填写配置项\");\n              return false;\n          }\n          var configValues = document.getElementsByName(\"configValue\");\n          configList = [];\n          for(i = 0; i < configValues.length; i++){\n              console.log(configValues[i].value);\n              configList.push({\n                  configName: configName,\n                  configValue: configValues[i].value\n              });\n          }\n\n          if(configList.length < 1){\n              alert(\"请至少填写一个配置值\");\n              return false;\n          }\n          configInstanceList = [];\n          $('#configInstanceList option:selected').each(function(){\n              if($(this).val() != null){\n                  configInstanceList.push($(this).val());\n              }\n          });\n          if(configInstanceList.length == 0){\n              alert(\"请选择实例\");\n              return false;\n          }\n          tipInfo = \"修改配置\" + configName + \"并更新\";\n      }\n      var msTransferFlag = document.getElementById(\"msTransferFlag\").value;\n      var msQuickFinishFlag = document.getElementById(\"msQuickFinishFlag\").value;\n      var recordId = document.getElementById(\"recordId\").value;\n      var data = {\n          appId: appId,\n          recordId: recordId,\n          configFlag: isUpdateConfig == 1,\n          transferFlag: msTransferFlag == 1,\n          quickFinishFlag: msQuickFinishFlag == 1,\n          instanceList: configInstanceList,\n          configList: configList\n      };\n      if (confirm(\"确认\" + tipInfo)) {\n          document.getElementById(\"configAndRestartCloseBtn\").disabled = 'true';\n          document.getElementById(\"configAndRestartBtn\").disabled = 'true';\n          $.ajax({\n              type: 'post',\n              url:'${request.contextPath}/manage/app/restart/scrollRestart',\n              contentType:'application/json',\n              data: JSON.stringify(data),\n              dataType:'json',\n              success:function (data) {\n                  if (data.status == 200) {\n                      alert(data.data);\n                      window.location.reload();\n                  } else {\n                      alert(\"重启失败，请确认 : \" + data.error);\n                  }\n                  document.getElementById(\"configAndRestartCloseBtn\").disabled = false;\n                  document.getElementById(\"configAndRestartBtn\").disabled = false;\n              }\n          });\n      }else{\n          document.getElementById(\"configAndRestartBtn\").disabled = false;\n      }\n  }\n\n  function configAndRestart(appId) {\n      var isUpdateConfig = $('input:radio[name=\"isUpdateConfig\"]:checked').val();\n      if(isUpdateConfig == 1){\n          return config(appId);\n      }\n      return restart(appId);\n  }\n\n  function configAndRestartClose(appId) {\n      window.location.href = \"${request.contextPath}/manage/app/index?appId=\" + appId;\n  }\n\n    function configCommandClose(appId) {\n      window.location.href = \"${request.contextPath}/manage/app/index?appId=\" + appId;\n  }\n\n  function openRestartRecord(appId){\n      window.open(\"${request.contextPath}/manage/instance/opsList?tabId=3&appId=\" + appId + \"&pageNo=1\");\n      return;\n  }\n\n  function configCommand(appId) {\n    var configList = null;\n    var configInstanceList = null;\n    var configName = \"rename-command\";\n    var cmdSuffix = document.getElementById(\"commandSuffix\").value;\n    var commandSet = new Set();\n\n    var newRenameCommands = document.getElementById(\"commandName\").value;\n    var newCmds = newRenameCommands.split(\"\\n\");\n    for(i = 0; i < newCmds.length; i++){\n      commandSet.add(newCmds[i]);\n    }\n\n    // var existRenameCommands = document.getElementById(\"existCommandName\").value;\n    // var existsCmdConfigs = existRenameCommands.split(\"\\n\");\n    // for(i = 0; i < existsCmdConfigs.length; i++){\n      // var cmdConfig = existsCmdConfigs[i];\n      // commandSet.add(cmdConfig.substring(0, cmdConfig.indexOf(\" \")).trim());\n    // }\n\n    configList = [];\n    var newConfigValue = [];\n    for(var command of commandSet){\n      if(command != null && command != ''){\n        configList.push({\n            configName: configName,\n            configValue: command + \" \" + (command + cmdSuffix)\n        });\n        newConfigValue.push(command + \" \" + (command + cmdSuffix));\n      }\n    }\n    if(configList.length < 1){\n      if (!confirm(\"确认清空命令重命名？\")) {\n        return;\n      }\n      configList.push({\n            configName: configName,\n            configValue: \"\"\n        });\n    }\n    configInstanceList = [];\n    $('#cmdInstanceList option:selected').each(function(){\n        if($(this).val() != null){\n            configInstanceList.push($(this).val());\n        }\n    });\n    if(configInstanceList.length == 0){\n        alert(\"请选择实例\");\n        return false;\n    }\n\n    tipInfo = \"重命名命令\" + configName + \"的值为：\" + newConfigValue;\n    var data = {\n        appId: appId,\n        configFlag: true,\n        transferFlag: false,\n        instanceList: configInstanceList,\n        configList: configList\n    };\n    if (confirm(\"确认\" + tipInfo)) {\n        document.getElementById(\"configCommandCloseBtn\").disabled = 'true';\n        document.getElementById(\"configCommandBtn\").disabled = 'true';\n        document.getElementById(\"cmdInstanceList\").disabled = false;\n        $(\"#configCommandModal\").find(\"input[type='radio']\").attr(\"disabled\", \"disabled\");\n        $(\"#configCommandModal\").find(\"input[type='text']\").attr(\"disabled\", \"disabled\");\n        $.ajax({\n            type: 'post',\n            url:'${request.contextPath}/manage/app/restart/updateConfig',\n            contentType:'application/json',\n            data: JSON.stringify(data),\n            dataType:'json',\n            success:function (data) {\n                if (data.status == 200) {\n                    if(data.data.commandSet == true){\n                        alert(\"配置更新成功!\");\n                        window.location.href = \"${request.contextPath}/manage/app/index?appId=\" + appId;\n                    }else{\n                        alert(\"请点击重启，激活配置!\");\n                        document.getElementById(\"configCommandCloseBtn\").disabled = 'true';\n                        document.getElementById(\"configCommandBtn\").disabled = 'true';\n                    }\n                } else {\n                    alert(\"配置更新失败，请人工确认 : \" + data.error);\n                    document.getElementById(\"cmdInstanceList\").disabled = 'true';\n                    $(\"#configCommandModal\").find(\"input[type='radio']\").removeAttr(\"disabled\");\n                    $(\"#configCommandModal\").find(\"input[type='text']\").removeAttr(\"disabled\");\n                    document.getElementById(\"configCommandCloseBtn\").disabled = false;\n                    document.getElementById(\"configCommandBtn\").disabled = false;\n                }\n            }\n        });\n      }\n  }\n</script>\n<div class=\"row\">\n  <div class=\"col-12\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h4 class=\"card-title\">\n            应用实例管理-\n            <a href=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId!}\" target=\"_blank\">\n              ${appDesc.name!}\n            </a>\n            (${appDesc.typeDesc!})\n            <input type=\"hidden\" id=\"appId\" value=\"${appDesc.appId!}\"/>\n            <#if (appDesc.type == 2)>\n              <#if lossSlotsSegmentMap?? && (lossSlotsSegmentMap?size > 0)>\n                <font color=\"red\">\n                  丢失的slots:[\n                  <#list lossSlotsSegmentMap?keys as key>\n                    ${key!}-${lossSlotsSegmentMap?api.get(key)!}\n                  </#list>\n                  ]\n                </font>\n              </#if>\n            <#elseif (appDesc.type == 5)>\n              <button type=\"button\" class=\"btn btn-primary\" data-bs-target=\"#redisAddSentinelModal\"\n                      data-bs-toggle=\"modal\">添加sentinel节点\n              </button>\n              <button type=\"button\" class=\"btn btn-primary\" data-bs-target=\"#redisSentinelFailOverModal\"\n                      data-bs-toggle=\"modal\">FailOver\n              </button>\n              <button type=\"button\" class=\"btn btn-primary\" data-bs-target=\"#redisSentinelResetModal\"\n                      data-bs-toggle=\"modal\">Reset\n              </button>\n            </#if>\n            <#if (appDesc.type == 2)>\n              <button type=\"button\" id=\"configRestartId\" class=\"btn btn-primary ml-3\" data-bs-target=\"#configAndRestartModal\"\n                      data-bs-toggle=\"modal\">config / restart\n              </button>\n            </#if>\n          </h4>\n        </div>\n      </div>\n  </div>\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-bordered table-striped table-hover table-sm\">\n          <thead>\n          <tr>\n            <th>ID</th>\n            <th>实例</th>\n            <th>k8s容器</th>\n            <th>实例状态</th>\n            <th>角色</th>\n            <th>主实例ID</th>\n            <th>内存使用</th>\n            <th>对象数</th>\n            <th>连接数</th>\n            <th>命中率</th>\n            <th>碎片率</th>\n            <th>日志</th>\n            <th>节点运维</th>\n            <th>故障转移</th>\n          </tr>\n          </thead>\n          <tbody>\n          <div hidden=\"hidden\">\n            <select style=\"display:none\" id=\"instanceList\">\n              <#list instanceList as instance>\n                <option value=\"${instance.id!}\"/>\n                <#if (instance.status == 1) && (instance.masterInstanceId == 0) && (instance.type != 5)>\n                  <input name=\"selectOneMaster\" hidden value=\"${instance.id!}\"/>\n                </#if>\n              </#list>\n              </optgroup>\n            </select>\n          </div>\n\n          <#list instanceListMap?keys as key>\n            <#list instanceListMap?api.get(key) as instance>\n              <#assign instanceStatsMapKey = ((instance.ip) + \":\" + (instance.port))>\n              <tr>\n                <td>\n                  <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.id!}\" target=\"_blank\">${instance.id!}</a>\n                </td>\n                <td>\n                  <#if (k8sMachineMaps?api.get(instance.ip))??>\n                    <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/pod/changelist?ip=${instance.ip!}\"\n                       title=\"查看pod变更记录\">${instance.ip!}</a>:${instance.port!}\n                  </#if>\n                  <#if !((k8sMachineMaps?api.get(instance.ip))??)>\n                    ${instance.ip!}:${instance.port!}\n                  </#if>\n                </td>\n                <td>\n                  <#if (k8sMachineMaps?api.get(instance.ip))??>\n                    <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/pod/changelist?ip=${instance.ip!}\"\n                       title=\"查看pod变更记录\">是</a>\n                  </#if>\n                  <#if !((k8sMachineMaps?api.get(instance.ip))??)>\n                    否\n                  </#if>\n                </td>\n                <td>\n                  ${instance.statusDesc!}\n                  <#if (instance.status==2) || (instance.status==3)>\n                    <br/>\n                    ${instance.updateTimeDesc!}\n                  </#if>\n                </td>\n                <td>${instance.roleDesc!}</td>\n                  <#if (instance.masterInstanceId >0)>\n                    <td>\n                      <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.masterInstanceId!}\"\n                         target=\"_blank\">${instance.masterInstanceId!}</a>\n                    </td>\n                  <#else>\n                    <td></td>\n                  </#if>\n                <td>\n                  <div class=\"progress progress-fs-1\" role=\"progressbar\"\n                       aria-valuenow=\"${memUsePercentValue!}\"\n                       aria-valuemax=\"100\"\n                       aria-valuemin=\"0\">\n                    <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]?? && ((instanceStatsMap[instanceStatsMapKey]).memUsePercent >= 80)>\n                      <#assign progressBarStatus='bg-danger'>\n                    <#else>\n                      <#assign progressBarStatus='bg-success'>\n                    </#if>\n                    <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                      <#assign memUsePercentValue = ((instanceStatsMap[instanceStatsMapKey]).memUsePercent!)>\n                    <#else>\n                      <#assign memUsePercentValue='0'>\n                    </#if>\n                    <div class=\"progress-bar ${progressBarStatus!}\"\n                         style=\"width: ${memUsePercentValue!}%; overflow: visible;\">\n                      <span style=\"color: #000000; margin-bottom: 0\">\n                        <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                          ${((instanceStatsMap[instanceStatsMapKey]).usedMemory / 1024 / 1024 / 1024)?string('#.##')} G&nbsp;&nbsp;Used\n                            /${(instance.mem / 1024)?string('#.##')}G&nbsp;&nbsp;Total\n                        </#if>\n                      </span>\n                    </div>\n                  </div>\n                </td>\n                <td>\n                  <#if (instanceStatsMap[instanceStatsMapKey])??>\n                    ${(instanceStatsMap[instanceStatsMapKey]).currItems!}\n                  </#if>\n                </td>\n                <td>\n                  <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.id!}&tabTag=instance_clientList\"\n                     target=\"_blank\">\n                    <#if (instanceStatsMap[instanceStatsMapKey])??>\n                      ${(instanceStatsMap[instanceStatsMapKey]).currConnections!}\n                    </#if>\n                  </a>\n                </td>\n                <td>\n                  <#if (instanceStatsMap[instanceStatsMapKey])??>\n                    ${(instanceStatsMap[instanceStatsMapKey]).hitPercent!}\n                  </#if>\n                </td>\n                <td>\n                  <#if instanceStatsMap?? && (instanceStatsMap[instanceStatsMapKey])??>\n                    <#assign memFragmentationRatio=(instanceStatsMap[instanceStatsMapKey]).memFragmentationRatio>\n                  <#else>\n                    <#assign memFragmentationRatio=0>\n                  </#if>\n                  <#if (memFragmentationRatio > 5) && ((instanceStatsMap[instanceStatsMapKey]).usedMemory > 1024 * 1024 * 100)>\n                    <#assign memFragmentationRatioLabel='bg-danger'>\n                  <#elseif (memFragmentationRatio >= 3) && (memFragmentationRatio < 5) && ((instanceStatsMap[instanceStatsMapKey]).usedMemory > 1024 * 1024 * 100)>\n                    <#assign memFragmentationRatioLabel='bg-warning'>\n                  <#else>\n                    <#assign memFragmentationRatioLabel='bg-success'>\n                  </#if>\n                  <label class=\"${memFragmentationRatioLabel!} rounded\">&nbsp;${memFragmentationRatio!}&nbsp;</label>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/instance/log?instanceId=${instance.id!}\">查看</a>\n                </td>\n                <td style=\"white-space: nowrap\">\n                  <div>\n                    <#if (instance.status ==2)>\n                      <button type=\"button\" class=\"btn btn-sm btn-success mb-2\"\n                              onclick=\"startInstance('${appDesc.appId!}','${instance.id!}')\">\n                        启动实例\n                      </button>\n                      <#if (instance.type ==2)>\n                        <br>\n                        <button type=\"button\" class=\"btn btn-sm btn-danger mb-2\"\n                                onclick=\"forgetInstance('${appDesc.appId!}','${instance.id!}')\">\n                          永久下线\n                        </button>\n                      </#if>\n                    <#elseif (instance.status ==0)>\n                      <button type=\"button\" class=\"btn btn-sm btn-success mb-2\"\n                              onclick=\"startInstance('${appDesc.appId!}','${instance.id!}')\">\n                        启动实例\n                      </button>\n                      <br>\n\n                      <button type=\"button\" class=\"btn btn-sm btn-warning mb-2\"\n                              onclick=\"shutdownInstance('${appDesc.appId!}','${instance.id!}')\">\n                        下线实例\n                      </button>\n                      <#if (instance.masterInstanceId == 0) && (appDesc.type == 2) && (lossSlotsSegmentMap[instanceStatsMapKey]??) && (lossSlotsSegmentMap[instanceStatsMapKey] != '')>\n                          <br>\n                          <button type=\"button\" class=\"btn btn-sm btn-primary mb-2\"\n                                  data-bs-target=\"#redisAddFailSlotsMasterModal${instance.id!}\"\n                                  data-bs-toggle=\"modal\">修复slot丢失数据\n                          </button>\n                      </#if>\n                    <#elseif (instance.status == -1)>\n                      <button type=\"button\" class=\"btn btn-sm btn-success mb-2\"\n                              onclick=\"startInstance('${appDesc.appId!}','${instance.id!}')\">\n                        启动实例\n                      </button>\n                      <br>\n                      <button type=\"button\" class=\"btn btn-sm btn-warning mb-2\"\n                              onclick=\"shutdownInstance('${appDesc.appId!}','${instance.id!}')\">\n                        下线实例\n                      </button>\n                    <#elseif (instance.status == 1)>\n                      <button type=\"button\" class=\"btn btn-sm btn-warning mb-2\"\n                              onclick=\"shutdownInstance('${appDesc.appId!}', '${instance.id!}')\">\n                        下线实例\n                      </button>\n                      <br>\n                      <button type=\"button\" class=\"btn btn-sm btn-info mb-2\"\n                              data-bs-target=\"#instanceAddConfigModal_${instance.id!}\"\n                              data-bs-toggle=\"modal\">修改配置\n                      </button>\n                      <#if (instance.masterInstanceId == 0) && (instance.type != 5)>\n                        <br>\n                        <button type=\"button\" class=\"btn btn-sm btn-primary mb-2\"\n                                data-bs-target=\"#redisClusterAddSlaveModal_${instance.id!}\"\n                                data-bs-toggle=\"modal\">添加Slave\n                        </button>\n                      </#if>\n                    </#if>\n                  </div>\n                </td>\n                <td>\n                  <div>\n                    <#if (instance.status == 1)>\n                      <#if (instance.masterInstanceId > 0) && (instance.type == 2)>\n                        <button type=\"button\" class=\"btn btn-sm btn-success mb-2\"\n                                data-bs-target=\"#redisClusterFailOverManualModal${instance.id!}\"\n                                data-bs-toggle=\"modal\">Manual\n                        </button>\n                        <br>\n                        <button type=\"button\" class=\"btn btn-sm btn-primary mb-2\"\n                                data-bs-target=\"#redisClusterFailOverForceModal${instance.id!}\"\n                                data-bs-toggle=\"modal\">Force\n                        </button>\n                        <br>\n                        <button type=\"button\" class=\"btn btn-sm btn-danger\"\n                                data-bs-target=\"#redisClusterFailOverTakeOverModal${instance.id!}\"\n                                data-bs-toggle=\"modal\">TakeOver\n                        </button>\n                      </#if>\n                    </#if>\n                  </div>\n                </td>\n              </tr>\n            </#list>\n            <tr style=\"height: 10px\"></tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n  </div>\n</div>\n\n<div id=\"redisAddSentinelModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">添加sentinel节点</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"control-label col-md-3\">sentinel节点Ip:</label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"sentinelIp\" id=\"sentinelIp\" placeholder=\"sentinel节点Ip\"\n                           class=\"form-control\">\n                  </div>\n                </div>\n              </div>\n              <!-- form-body 结束 -->\n              <div id=\"redisAddSentinelInfo\"></div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"redisAddSentinelBtn\" class=\"btn btn-danger\"\n                  onclick=\"redisAddSentinel('${appDesc.appId!}')\">Ok\n          </button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"redisSentinelFailOverModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">redis-Sentinel从节点FailOver操作</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <div class=\"modal-body\">\n        <div class=\"row\">\n          <!-- 控件开始 -->\n          <div class=\"container\">\n            <div class=\"col-md-12\">\n              <div>你确定执行failOver操作?</div>\n              <div id=\"redisSentinelFailOverInfo\"></div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"modal-footer\">\n        <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        <button type=\"button\" id=\"redisSentinelFailOverBtn\" class=\"btn btn-danger\"\n                onclick=\"redisSentinelFailOver('${appDesc.appId!}')\">Ok\n        </button>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div id=\"redisSentinelResetModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">redis-Sentinel Reset操作</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <div class=\"modal-body\">\n        <div class=\"row\">\n          <!-- 控件开始 -->\n          <div class=\"container\">\n            <div class=\"col-md-12\">\n              <div>你确定重置sentinel实例状态?</div>\n              <div id=\"redisSentinelResetInfo\"></div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"modal-footer\">\n        <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        <button type=\"button\" id=\"redisSentinelResetBtn\" class=\"btn btn-danger\"\n                onclick=\"redisSentinelReset('${appDesc.appId!}')\">Ok\n        </button>\n      </div>\n    </div>\n  </div>\n</div>\n\n<input type=\"hidden\" id=\"appId\" value=\"${appDesc.appId!}\">\n\n<#list instanceList as instance>\n  <div id=\"redisClusterFailOverManualModal${instance.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">Redis-Cluster从节点FailOver Manual操作</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"container\">\n              <div class=\"col-md-12\">\n                <div>你确定对实例${instance.id!}执行FailOver Manual操作?</div>\n                <div id=\"redisClusterFailOverManualInfo${instance.id!}\"></div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"redisClusterFailOverManualBtn${instance.id!}\" class=\"btn btn-danger\"\n                  onclick=\"redisClusterFailOverManual('${appDesc.appId!}', '${instance.id!}')\">Ok\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div id=\"redisClusterFailOverForceModal${instance.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">Redis-Cluster从节点FailOver Force操作</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"container\">\n              <div class=\"col-md-12\">\n                <div>你确定对实例${instance.id!}执行FailOver Force操作?</div>\n                <div id=\"redisClusterFailOverForceInfo${instance.id!}\"></div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"redisClusterFailOverForceBtn${instance.id!}\" class=\"btn btn-danger\"\n                  onclick=\"redisClusterFailOverForce('${appDesc.appId!}', '${instance.id!}')\">Ok\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n\n\n  <div id=\"redisClusterFailOverTakeOverModal${instance.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">Redis-Cluster从节点FailOver TakeOver操作</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"container\">\n              <div class=\"col-md-12\">\n                <div>你确定对实例${instance.id!}执行FailOver TakeOver操作?</div>\n                <div id=\"redisClusterFailOverTakeOverInfo${instance.id!}\"></div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"redisClusterFailOverTakeOverBtn${instance.id!}\" class=\"btn btn-danger\"\n                  onclick=\"redisClusterFailOverTakeOver('${appDesc.appId!}', '${instance.id!}')\">Ok\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n\n\n  <div id=\"redisClusterDelNodeModal${instance.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">Redis-Cluster节点删除操作</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <div class=\"container\">\n              <div class=\"col-md-12\">\n                <div>你确定对实例${instance.id!}执行删除操作?</div>\n                <div id=\"redisClusterDelNodeInfo${instance.id!}\"></div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n          <button type=\"button\" id=\"redisClusterDelNodeBtn${instance.id!}\" class=\"btn btn-danger\"\n                  onclick=\"redisClusterDelNode('${appDesc.appId!}', '${instance.id!}')\">Ok\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n\n\n  <div id=\"instanceAddConfigModal_${instance.id!}\" name=\"addConfig-modal\" class=\"modal fade\" tabindex=\"-1\"\n       data-width=\"400\"\n       aria-hidden=\"true\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">\n            更新配置\n            <h5>【实例】${instance.ip!}:${instance.port!}</h5>\n          </h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <form class=\"form-horizontal form-bordered form-row-stripped\">\n          <div class=\"modal-body\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"col-md-12\">\n                <!-- form-body开始 -->\n                <div class=\"form-body\">\n                  <div class=\"form-group row\">\n                    <label class=\"control-label col-md-3\">配置项:</label>\n                    <div class=\"col-md-8\">\n                      <select id=\"appConfigKey${instance.id!}\" name=\"appConfigKey\"\n                              class=\"selectpicker border rounded w-100\"\n                              data-live-search=\"true\" title=\"选择配置项\" data-width=\"30%\"\n                              data-size=\"8\">\n                      </select>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <div class=\"col-md-8 offset-md-3\">\n                      <input type=\"text\" id=\"newConfigKey${instance.id!}\" class=\"form-control\"\n                             placeholder=\"新增配置项\"/>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"control-label col-md-3\">配置值:</label>\n                    <div class=\"col-md-8\">\n                      <input type=\"text\" name=\"appConfigValue\" id=\"appConfigValue${instance.id!}\"\n                             value=\"${appConfigValue!}\" class=\"form-control\">\n                    </div>\n                  </div>\n                </div>\n                <!-- form-body 结束 -->\n              </div>\n            </div>\n          </div>\n\n          <div class=\"modal-footer\">\n            <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n            <button type=\"button\" id=\"instanceAddConfigBtn${instance.id!}\" class=\"btn btn-danger\"\n                    onclick=\"addConfigInstance('${appDesc.appId!}', '${instance.id!}','${instance.ip!}','${instance.port!}')\">\n              Ok\n            </button>\n          </div>\n        </form>\n      </div>\n    </div>\n  </div>\n\n  <div id=\"redisClusterAddSlaveModal_${instance.id!}\" name=\"addSlave-modal\" class=\"modal fade\" tabindex=\"-1\"\n       data-width=\"400\"\n       aria-hidden=\"true\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">添加slave节点</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <h5>主节点信息：</h5>\n        <#if !(machineMap[instance.ip]??) || !(machineMap[instance.ip].info.realIp??)>\n          <#assign realIp = \"无\">\n        <#else>\n          <#assign realIp = machineMap[instance.ip].info.realIp!>\n        </#if>\n        <#if !(machineMap[instance.ip]??) || !(machineMap[instance.ip].info.rack??)>\n          <#assign rack = \"无\">\n        <#else>\n          <#assign rack = machineMap[instance.ip].info.rack!>\n        </#if>\n        <h6>【ID】${instance.id!} 【实例】${instance.ip!}:${instance.port!}\n          【宿主机-机架】${realIp!}-${rack!}</h6>\n\n        <form class=\"form-horizontal form-bordered form-row-stripped\">\n          <div class=\"modal-body\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"col-md-12\">\n                <!-- form-body开始 -->\n                <div class=\"form-body\">\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-auto\">slave节点:</label>\n                    <div class=\"col-md-6\" id=\"div_slaveIp${instance.id!}\">\n                      <select id=\"slaveIp${instance.id!}\" name=\"slaveIp\"\n                              class=\"selectpicker border rounded w-100\" data-live-search=\"true\">\n                      </select>\n                    </div>\n                    <button type=\"button\" class=\"btn btn-success btn-sm col-auto offset-md-1\"\n                            onclick=\"genSlaveIp('${appDesc.appId!}', '${instance.id!}')\">自动生成\n                    </button>\n                    <br/><br/>\n                    <p id=\"genSlaveIpNote${instance.id!}\" class=\"col-md-8 offset-md-3\"\n                       style=\"color: red; display: none\">\n                      <i class=\"ace-icon bi bi-exclamation-triangle bigger-120\"></i>&nbsp;&nbsp;&nbsp;自动生成slave节点ip失败\n                    </p>\n                  </div>\n                </div>\n                <!-- form-body 结束 -->\n                <div id=\"redisClusterAddSlaveInfo${instance.id!}\"></div>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"modal-footer\">\n            <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n            <button type=\"button\" id=\"redisClusterAddSlaveBtn${instance.id!}\" class=\"btn btn-danger\"\n                    onclick=\"redisClusterAddSlave('${appDesc.appId!}', '${instance.id!}')\">Ok\n            </button>\n          </div>\n        </form>\n      </div>\n    </div>\n  </div>\n\n  <div id=\"redisSentinelAddSlaveModal${instance.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">添加slave节点(主节点:${instance.id!}, ${instance.ip!}:${instance.port!})</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <form class=\"form-horizontal form-bordered form-row-stripped\">\n          <div class=\"modal-body\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"col-md-12\">\n                <!-- form-body开始 -->\n                <div class=\"form-body\">\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">Slave节点Ip:</label>\n                    <div class=\"col-md-7\">\n                      <input type=\"text\" name=\"sentinelSlaveIp\" id=\"sentinelSlaveIp${instance.id!}\"\n                             placeholder=\"Slave节点Ip\" class=\"form-control\">\n                    </div>\n                  </div>\n                </div>\n                <!-- form-body 结束 -->\n                <div id=\"redisSentinelAddSlaveInfo${instance.id!}\"></div>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"modal-footer\">\n            <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n            <button type=\"button\" id=\"redisSentinelAddSlaveBtn${instance.id!}\" class=\"btn btn-danger\"\n                    onclick=\"redisSentinelAddSlave('${appDesc.appId!}', '${instance.id!}')\">Ok\n            </button>\n          </div>\n        </form>\n      </div>\n    </div>\n  </div>\n\n  <div id=\"redisAddFailSlotsMasterModal${instance.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">修复failslots</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <form class=\"form-horizontal form-bordered form-row-stripped\">\n          <div class=\"modal-body\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"col-md-12\">\n                <!-- form-body开始 -->\n                <div class=\"form-body\">\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">节点Ip:</label>\n                    <div class=\"col-md-7\">\n                      <input type=\"text\" name=\"failSlotsMasterHost\"\n                             id=\"failSlotsMasterHost${instance.id!}\"\n                             placeholder=\"failSlotsMasterHost\" class=\"form-control\">\n                    </div>\n                  </div>\n                </div>\n                <!-- form-body 结束 -->\n                <div id=\"redisAddFailSlotsMasterInfo${instance.id!}\"></div>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"modal-footer\">\n            <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n            <button type=\"button\" id=\"redisAddFailSlotsMasterBtn${instance.id!}\" class=\"btn btn-danger\"\n                    onclick=\"redisAddFailSlotsMaster('${appDesc.appId!}', '${instance.id!}')\">Ok\n            </button>\n          </div>\n        </form>\n      </div>\n    </div>\n  </div>\n</#list>\n\n\n<select id=\"slaveIp\" name=\"slaveIp\" hidden=\"hidden\">\n  <option value=\"-1\">请选择slave</option>\n  <#list machineMap?keys as key>\n    <#assign machine = machineMap?api.get(key)>\n    <#if machineInstanceCountMap?? && (machineInstanceCountMap[machine.info.ip]??)>\n      <#assign usedCpu = machineInstanceCountMap[machine.info.ip]?string(\"0\")>\n    <#else>\n      <#assign usedCpu = \"0\">\n    </#if>\n    <#assign cpu = (machine.info.cpu)?string(\"0\")>\n    <#assign cpuUsage = ((usedCpu?number)/(cpu?number)*100)?string(\"0\")>\n    <#assign usedMemRss = ((machine.machineMemInfo.usedMemRss?number)/1024/1024/1024)?string(\"0.0\")>\n    <#assign mem = (machine.info.mem)?string(\"0.0\")>\n    <#assign memUsage = ((usedMemRss?number)/(mem?number)*100)?string(\"0\")>\n    <#assign realIp = machine.info.realIp!>\n    <#assign rack = machine.info.rack!>\n    <#assign disk = machine.info.disk?string(\"0.0\")>\n    <#if machine.diskUsageRatio?? && machine.diskTotal??>\n      <#assign diskUsageRatio = machine.diskUsageRatio>\n      <#assign diskUsage = (((machine.diskTotal?number)/1024 - (machine.diskAvailable?number)/1024)?string(\"0\"))>\n    <#else>\n      <#assign diskUsageRatio = '0'>\n      <#assign diskUsage = '0'>\n    </#if>\n    <#if machine.info.realIp?? && (machine.info.realIp=='')><#assign realIp =\"无realIp\"></#if>\n    <#if machine.info.rack?? && (machine.info.rack=='')><#assign rack =\"无rack\"></#if>\n    <#if (machine.info.useType==0)>\n      <#assign extraDesc =\"专用-\" + machine.info.extraDesc>\n    <#elseif (machine.info.useType==1)>\n      <#assign extraDesc = \"测试-\" + machine.info.extraDesc>\n    <#elseif (machine.info.useType==2)>\n      <#assign extraDesc = \"混合-\" + machine.info.extraDesc>\n    <#else>\n      <#assign extraDesc = \"\" + machine.info.extraDesc>\n    </#if>\n    <#if (appDesc.type == 12) && (machine.info.type == 6)>\n      <option value=\"${machine.ip!}\">${machine.ip!}：【${usedCpu!}/${cpu!}核(${cpuUsage!}%)】【${usedMemRss!}/${mem!}G(${memUsage!}%)】【${diskUsage!}/${disk!}G(${diskUsageRatio!}%)】【${realIp!}-${rack!}】【${extraDesc!}】</option>\n    <#elseif ((appDesc.type == 2) || (appDesc.type == 5) || (appDesc.type == 6)) && (machine.info.type == 0)>\n      <option value=\"${machine.ip!}\">${machine.ip!}：【${usedCpu!}/${cpu!}核(${cpuUsage!}%)】【${usedMemRss!}/${mem!}G(${memUsage!}%)】【${diskUsage!}/${disk!}G(${diskUsageRatio!}%)】【${realIp!}-${rack!}】【${extraDesc!}】</option>\n    </#if>\n  </#list>\n</select>\n\n<div id=\"configAndRestartModal\" name=\"configAndRestart-modal\" class=\"modal fade\" tabindex=\"-1\"\n     data-width=\"400\"\n     aria-hidden=\"true\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">修改配置/滚动重启</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n      <div class=\"d-block\">\n        <h5>【应用】${appDesc.appId!}:${appDesc.name!}</h5>\n        <button class=\"btn btn-warning btn-sm\" style=\"float: right;\" onclick=\"openRestartRecord('${appDesc.appId!}')\">\n          查看重启进程记录\n        </button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"control-label col-md-3\">\n                    操作类型:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <label class=\"radio-inline\">\n                      <input type=\"hidden\" id=\"recordId\" name=\"recordId\" class=\"form-control\"/>\n                      <input type=\"radio\" name=\"isUpdateConfig\" value=\"0\" onchange=\"moduleSelect('0')\" checked=\"checked\"> 滚动重启\n                      <span class=\"glyphicon glyphicon-question-sign\" data-bs-toggle=\"tooltip\"\n                            data-placement=\"right\" title=\"按照主从分组后重启\">\n                                            </span>\n                    </label>\n                    <label>\n                    </label>\n                    <label>&nbsp;&nbsp;</label>\n                    <label class=\"radio-inline\">\n                      <input type=\"radio\" name=\"isUpdateConfig\" value=\"1\" onchange=\"moduleSelect('1')\"> 修改配置\n                      <span class=\"glyphicon glyphicon-question-sign\" data-bs-toggle=\"tooltip\"\n                            data-placement=\"right\" title=\"某些配置修改后，需要重启实例\">\n                                            </span>\n                    </label>\n                  </div>\n                </div>\n\n                <div id=\"moduleInfo\" style=\"display: none\">\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">配置项列表:</label>\n                    <div class=\"col-md-8\">\n                      <select id=\"configListId\" name=\"appConfigKey\"\n                              class=\"selectpicker rounded border w-100\"\n                              data-live-search=\"true\" title=\"选择配置项\" data-width=\"31%\"\n                              data-size=\"8\" onchange=\"setSelectedConfig()\">\n                      </select>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">修改配置项:</label>\n                    <div class=\"col-md-8\">\n                      <input type=\"text\" id=\"configName\" name=\"configName\" class=\"form-control\"\n                             placeholder=\"参考配置项列表\"/>\n                    </div>\n                  </div>\n\n                  <ul id=\"configRowGroup\">\n                    <span id=\"configValueTip\" style=\"color: orange\">* 请确认配置值是否需分开</span>\n                    <li name=\"configRow\">\n                      <div class=\"form-group row\">\n                        <label class=\"col-form-label col-md-3\">配置值:</label>\n                        <div class=\"col-md-8\">\n                          <input type=\"text\" name=\"configValue\" class=\"form-control\"\n                                 placeholder=\"如同一配置项有多个配置值，请分开填写\"/>\n                        </div>\n                      </div>\n                    </li>\n                  </ul>\n\n                  <div class=\"form-group row\">\n                    <div class=\"col-md-1 offset-md-9\">\n                      <button id=\"addConfigRowBtn\" type=\"button\" onclick=\"addConfigRow()\">+</button>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">\n                      选择实例<font color='red'>(*)</font>:\n                    </label>\n                    <div class=\"col-md-8\">\n                      <select id=\"configInstanceList\" name=\"configInstance\" class=\"selectpicker border rounded w-100\" multiple data-live-search=\"true\" data-width=\"31%\">\n                        <option value=\"0\">所有实例</option>\n                        <#list instanceList as instanceInfo>\n                          <#if (instanceInfo.status == 1) && ((instanceInfo.type == 2) || (instanceInfo.type == 6))>\n                            <option value=\"${instanceInfo.id!}\">${instanceInfo.ip!}:${instanceInfo.port!}</option>\n                          </#if>\n                        </#list>\n                      </select>\n                    </div>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3\">\n                    是否允许主从切换:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <select id=\"msTransferFlag\" name=\"msTransferFlag\" class=\"form-select\">\n                      <option value=\"0\" selected>\n                        否\n                      </option>\n                      <option value=\"1\">\n                        是\n                      </option>\n                    </select>\n                    <span class=\"help-block\">\n                                            当滚动重启和修改配置需重启时，是否允许原有的主节点切换为从节点\n                                        </span>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3\">\n                    是否快速重启:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <select id=\"msQuickFinishFlag\" name=\"msQuickFinishFlag\" class=\"form-select\">\n                      <option value=\"0\" selected>\n                        否\n                      </option>\n                      <option value=\"1\">\n                        是\n                      </option>\n                    </select>\n                    <span class=\"help-block\">\n                                              当滚动重启和修改配置需重启时，是否快速重启(不考虑客户端集群拓扑耗时)\n                                          </span>\n                  </div>\n                </div>\n\n\n              </div>\n\n              <!-- form-body 结束 -->\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" id=\"configAndRestartCloseBtn\" class=\"btn btn-secondary\" onclick=\"configAndRestartClose('${appDesc.appId!}')\">Close</button>\n          <button type=\"button\" id=\"configAndRestartBtn\" class=\"btn btn-danger\"\n                  onclick=\"configAndRestart('${appDesc.appId!}')\">\n            Ok\n          </button>\n          <button type=\"button\" id=\"restartAfterConfigBtn\" class=\"btn btn-danger\"\n                  onclick=\"restart('${appDesc.appId!}')\" style=\"display: none\">\n            Restart to active config\n          </button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"configCommandModal\" name=\"configCommand-modal\" class=\"modal fade\" tabindex=\"-1\"\n     data-width=\"400\"\n     aria-hidden=\"true\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">修改命令（改名/映射）</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n      <div class=\"d-block\">\n        <h5>【应用】${appDesc.appId!}:${appDesc.name!}</h5>\n        <button class=\"btn btn-warning btn-sm\" style=\"float: right;\" onclick=\"openRestartRecord('${appDesc.appId!}')\">\n          查看重启进程记录\n        </button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"control-label col-md-3\">\n                    操作类型:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <label class=\"radio-inline\">\n                      <input type=\"radio\" name=\"isRenameCommand\" value=\"0\" checked=\"checked\"> 命令重命名\n                      <!--<span class=\"glyphicon glyphicon-question-sign\" data-bs-toggle=\"tooltip\"\n                            data-placement=\"right\" title=\"按照主从分组后重启\">\n                                            </span>-->\n                    </label>\n                    <label>\n                    </label>\n                    <!--<label>&nbsp;&nbsp;</label>\n                    <label class=\"radio-inline\">\n                      <input type=\"radio\" name=\"isMappingCommand\" value=\"1\" onchange=\"moduleSelect('1')\"> 命令映射\n                      <span class=\"glyphicon glyphicon-question-sign\" data-bs-toggle=\"tooltip\"\n                            data-placement=\"right\" title=\"某些配置修改后，需要重启实例\">\n                                            </span>\n                    </label>-->\n                  </div>\n                </div>\n\n                <div id=\"commandInfo\">\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">命令列表:</label>\n                    <div class=\"col-md-8\">\n                      <select id=\"cmdListId\"\n                              class=\"selectpicker rounded border w-100\" multiple\n                              data-live-search=\"true\" title=\"选择命令\" data-width=\"31%\"\n                              data-size=\"8\" onchange=\"setSelectedRenameCmd()\">\n                      </select>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">已改名命令:</label>\n                    <div class=\"col-md-8\">\n                      <textarea rows=\"5\" id=\"existCommandName\" name=\"existCommandName\" class=\"form-control\"\n                                disabled=\"disabled\"></textarea>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">待改名命令:</label>\n                    <div class=\"col-md-8\">\n                      <textarea rows=\"5\" id=\"commandName\" name=\"commandName\" class=\"form-control\"></textarea>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">改名命令-后缀:</label>\n                    <div class=\"col-md-8\">\n                      <input type=\"text\" id=\"commandSuffix\" name=\"commandSuffix\" class=\"form-control\" value=\"_dep\"\n                             placeholder=\"添加到命令尾部\"/>\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3\">\n                      选择实例<font color='red'>(*)</font>:\n                    </label>\n                    <div class=\"col-md-8\">\n                      <select id=\"cmdInstanceList\" name=\"configInstance\" class=\"selectpicker border rounded w-100\" multiple data-live-search=\"true\" data-width=\"31%\">\n                        <option value=\"0\">所有实例</option>\n                        <#list instanceList as instanceInfo>\n                        <#if (instanceInfo.status == 1) && ((instanceInfo.type == 2) || (instanceInfo.type == 6))>\n                        <option value=\"${instanceInfo.id!}\">${instanceInfo.ip!}:${instanceInfo.port!}</option>\n                        </#if>\n                      </#list>\n                      </select>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            <!-- form-body 结束 -->\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" id=\"configCommandCloseBtn\" class=\"btn btn-secondary\" onclick=\"configCommandClose('${appDesc.appId!}')\">Close</button>\n          <button type=\"button\" id=\"configCommandBtn\" class=\"btn btn-danger\"\n                  onclick=\"configCommand('${appDesc.appId!}')\">\n            Ok\n          </button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appOps/appMachine.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          应用机器列表-${appDesc.name!}(${appDesc.typeDesc!})\n        </h3>\n      </div>\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-bordered table-striped table-hover\">\n          <thead>\n          <tr>\n            <th>ip</th>\n            <th>内存使用率</th>\n            <th>已分配内存</th>\n            <th>磁盘使用率</th>\n            <th>已分配磁盘</th>\n            <th>CPU使用率</th>\n            <th>网络流量</th>\n            <th>机器负载</th>\n            <th>最后统计时间</th>\n            <th>是否虚机</th>\n            <th>机房</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list appMachineList as machine>\n            <tr>\n              <td>\n                <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/machineInstances?ip=${machine.ip!}\">${machine.ip!}</a>\n              </td>\n              <td>\n                <span style=\"display:none\">${((machine.memoryUsageRatio?number) / 100)?string(\"0.00\")}</span>\n                <div class=\"progress progress-fs-1\" role=\"progressbar\" aria-valuenow=\"${machine.memoryUsageRatio!}\" aria-valuemax=\"100\"\n                     aria-valuemin=\"0\" >\n                  <#assign fmtMemoryUsageRatio = machine.memoryUsageRatio>\n                  <#if (fmtMemoryUsageRatio?number >= 80.00)>\n                    <div class=\"progress-bar bg-danger\"\n                        style=\"width: ${machine.memoryUsageRatio!}%; overflow: visible;\">\n                      <span style=\"color: #000000; margin-bottom: 0\">\n                        ${((machine.memoryTotal?number-machine.memoryFree?number)/1024/1024/1024)?string(\"0.00\")}G&nbsp;&nbsp;Used/\n                        ${(machine.memoryTotal?number/1024/1024/1024)?string(\"0.00\")}G&nbsp;&nbsp;Total\n                      </span>\n                    </div>\n                  <#else>\n                    <div class=\"progress-bar bg-success\"\n                         style=\"width: ${machine.memoryUsageRatio!}%; overflow: visible;\">\n                      <span style=\"color: #000000; margin-bottom: 0\">\n                        ${((machine.memoryTotal?number-machine.memoryFree?number)/1024/1024/1024)?string(\"0.00\")}G&nbsp;&nbsp;Used/\n                        ${(machine.memoryTotal?number/1024/1024/1024)?string(\"0.00\")}G&nbsp;&nbsp;Total\n                      </span>\n                    </div>\n                  </#if>\n                </div>\n              </td>\n              <td>\n                <#assign fmtMemoryAllocatedRatio = (((machine.memoryAllocated?number)/1024)*100.0/(machine.memoryTotal?number/1024/1024/1024))?string(\"0.00\")>\n                <span  style=\"display:none\">${((fmtMemoryAllocatedRatio?number) / 100)?string(\"0.00\")}</span>\n                <div class=\"progress progress-fs-1\" role=\"progressbar\" aria-valuenow=\"${fmtMemoryAllocatedRatio!}\" aria-valuemax=\"100\"\n                     aria-valuemin=\"0\">\n                  <#if (fmtMemoryAllocatedRatio?number >= 80.00)>\n                    <div class=\"progress-bar bg-danger\"\n                         style=\"width: ${fmtMemoryAllocatedRatio!}%; overflow: visible;\">\n                      <span style=\"color: #000000; margin-bottom: 0\">\n                        ${((machine.memoryAllocated?number)/1024)?string(\"0.00\")}G&nbsp;&nbsp;Used/${(machine.memoryTotal?number/1024/1024/1024)?string(\"0.00\")}G&nbsp;&nbsp;Total\n                      </span>\n                    </div>\n                  <#else>\n                    <div class=\"progress-bar bg-success\"\n                        style=\"width: ${fmtMemoryAllocatedRatio!}%; overflow: visible;\">\n                      <span style=\"color: #000000; margin-bottom: 0\">\n                        ${((machine.memoryAllocated?number)/1024)?string(\"0.00\")}G&nbsp;&nbsp;Used/${(machine.memoryTotal?number/1024/1024/1024)?string(\"0.00\")}G&nbsp;&nbsp;Total\n                      </span>\n                    </div>\n                  </#if>\n                </div>\n              </td>\n              <td>todo--</td>\n              <td>todo--</td>\n              <td>${machine.cpuUsage!}</td>\n              <td>${machine.traffic!}</td>\n              <td>${machine.load!}</td>\n              <td>${(machine.modifyTime)?string(\"yyyy-MM-dd HH:mm\")}</td>\n              <td>\n                <#if (machine.info.virtual == 1)>\n                  是\n                  <br/>\n                  物理机:${machine.info.realIp!}\n                <#else>\n                  否\n                </#if>\n              </td>\n              <td>${machine.info.room!}</td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appOps/appMigrate.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n    <title>CacheCloud管理后台</title>\n    <meta content=\"\" name=\"description\">\n    <meta content=\"\" name=\"keywords\">\n    <link href=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.css\" rel=\"stylesheet\" />\n    <link href=\"${request.contextPath}/assets/vendor/bootstrap/css/bootstrap.min.css\" rel=\"stylesheet\">\n    <link href=\"${request.contextPath}/assets/css/common.css\" rel=\"stylesheet\">\n\n    <script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n    <script src=\"${request.contextPath}/assets/vendor/bootstrap/js/bootstrap.bundle.min.js\"></script>\n    <script src=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.js\"></script>\n\n</head>\n<script type=\"text/javascript\">\n    $(window).on('load', function () {\n        $('.selectpicker').selectpicker({\n            'selectedText': 'cat'\n        });\n    });\n</script>\n\n<body>\n<div class=\"modal modal-xl\" tabindex=\"-1\" style=\"display: block\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">应用迁移工具</h4>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped align-items-center\">\n                <div class=\"modal-body\">\n                    <div class=\"row bs-wizard\" style=\"border-bottom:0;\">\n                        <div id=\"appInfo\" class=\"col-sm-1 bs-wizard-step\">\n                            <div class=\"text-center bs-wizard-stepnum\">1.应用信息</div>\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\"></div>\n                            </div>\n                            <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                            <div class=\"bs-wizard-info text-center\">应用及实例配置</div>\n                        </div>\n\n                        <div id=\"createVersion\" class=\"col-sm-2 bs-wizard-step disabled\">\n                            <div class=\"text-center bs-wizard-stepnum\">2.应用迁移计划</div>\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\"></div>\n                            </div>\n                            <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                            <div class=\"bs-wizard-info text-center\">迁移实例计划</div>\n                        </div>\n\n                        <div id=\"slaveChange\" class=\"col-sm-2 bs-wizard-step disabled\">\n                            <div class=\"text-center bs-wizard-stepnum\">3.新老Slave节点替换</div>\n                            <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                            <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                            <div class=\"bs-wizard-info text-center\">Slave节点上下线</div>\n                        </div>\n\n                        <div id=\"msFailover\" class=\"col-sm-2 bs-wizard-step disabled\">\n                            <div class=\"text-center bs-wizard-stepnum\">4.主从Failover</div>\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\"></div>\n                            </div>\n                            <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                            <div class=\"bs-wizard-info text-center\">主从节点切换</div>\n                        </div>\n\n                        <div id=\"addNewSlave\" class=\"col-sm-1 bs-wizard-step disabled\">\n                            <div class=\"text-center bs-wizard-stepnum\">5.添加Slave</div>\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\"></div>\n                            </div>\n                            <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                            <div class=\"bs-wizard-info text-center\">添加新Slave</div>\n                        </div>\n\n                        <div id=\"instanceCheck\" class=\"col-sm-2 bs-wizard-step disabled\">\n                            <div class=\"text-center bs-wizard-stepnum\">6.新实例状态检测</div>\n                            <div class=\"progress\">\n                                <div class=\"progress-bar\"></div>\n                            </div>\n                            <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                            <div class=\"bs-wizard-info text-center\">连接、异常、状态检测</div>\n                        </div>\n\n                        <div id=\"downOldSlave\" class=\"col-sm-1 bs-wizard-step disabled\">\n                          <div class=\"text-center bs-wizard-stepnum\">7.下线slave</div>\n                          <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                          <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                          <div class=\"bs-wizard-info text-center\">下线老Slave</div>\n                        </div>\n\n                        <div id=\"migrateComplete\" class=\"col-sm-1 bs-wizard-step disabled\">\n                          <div class=\"text-center bs-wizard-stepnum\">8.迁移完成</div>\n                          <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                          <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                          <div class=\"bs-wizard-info text-center\">迁移完成</div>\n                        </div>\n                    </div>\n\n                    <br/>\n                    <div class=\"row\">\n                        <form class=\"form-horizontal form-bordered form-row-stripped\" id=\"ns\">\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-2 text-end\"> 应用ID: </label>\n                                    <div class=\"col-md-3\">\n                                        <label id=\"appId\" name=\"appId\" class=\"form-control\" readonly>${appDetail.appDesc.appId!}<#if (appDetail.appDesc.isTest == 1)><font style=\"color:green\">(测试)</font></#if></label>\n                                    </div>\n                                    <label class=\"col-form-label col-md-2 text-end\"> Redis类型: </label>\n                                    <div class=\"col-md-3\">\n                                        <label id=\"type\" name=\"type\" class=\"form-control\" readonly>\n                                            <#if (appDetail.appDesc.type==2)>Cluster集群</#if>\n                                            <#if (appDetail.appDesc.type==5)>Sentinel</#if>\n                                            <#if (appDetail.appDesc.type==6)>Standalone</#if>\n                                        </label>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-2 text-end\"> 内存总容量: </label>\n                                    <div class=\"col-md-3\">\n                                        <label id=\"appMem\" name=\"appMem\" class=\"form-control\" readonly>${appDetail.mem!} MB</label>\n                                    </div>\n                                    <label class=\"col-form-label col-md-2 text-end\"> 实例容量: </label>\n                                    <div class=\"col-md-3\">\n                                        <label id=\"instanceMem\" name=\"instanceMem\" class=\"form-control\" readonly>${(appDetail.mem/appDetail.masterNum)?string(\"0\")} MB</label>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-2 text-end\"> 应用机器数: </label>\n                                    <div class=\"col-md-3\">\n                                        <label id=\"machineNum\" name=\"machineNum\" class=\"form-control\" readonly>${appDetail.machineNum!} 台</label>\n                                    </div>\n                                    <label class=\"col-form-label col-md-2 text-end\"> Redis节点数: </label>\n                                    <div class=\"col-md-3\">\n                                        <label id=\"num\" name=\"num\" class=\"form-control\" readonly>master:${appDetail.masterNum!} &nbsp;slave:${appDetail.slaveNum!}</label>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\" id=\"instanceSourceDiv\">\n                                    <label class=\"col-form-label col-md-2 text-end\"> <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId!}\">源实例信息</a>: </label>\n                                    <div class=\"col-md-8\">\n                                        <textarea id=\"instanceSourceInfo\" type=\"text\" rows=\"10\" class=\"form-control\" readonly>${instanceSourceInfo!}</textarea>\n                                    </div>\n                                    <label id=\"instanceSourceLog\" class=\"col-form-label\"></label>\n                                </div>\n\n<!--                                <div class=\"form-group row\">-->\n<!--                                    <label class=\"col-form-label col-md-2 text-end\"> 迁移目标机房: </label>-->\n<!--                                    <div class=\"col-md-3\">-->\n<!--                                        <select name=\"room\" id=\"room\" class=\"form-select\">-->\n<!--                                            <option value=\"\" >-->\n<!--                                                不作限制-->\n<!--                                            </option>-->\n<!--                                            <#list roomList as room>-->\n<!--                                                <option value=\"${room.name!}\" <#if (appDetail.appDesc.clientMachineRoom == room.name)>selected=\"selected\"</#if>>${room.name!} (${room.ipNetwork!})</option>-->\n<!--                                            </#list>-->\n<!--                                        </select>-->\n<!--                                    </div>-->\n<!--                                    <label class=\"col-form-label col-md-2 text-end\"> 迁移部署类型: </label>-->\n<!--                                    <div class=\"col-md-3\">-->\n<!--                                        <select id=\"deployType\" name=\"deployType\" class=\"form-select\">-->\n<!--                                            <option value=\"2\" <#if (appDetail.appDesc.isTest == 0)>selected=\"selected\"</#if>>混合部署</option>-->\n<!--                                            <option value=\"1\" <#if (appDetail.appDesc.isTest == 1)>selected=\"selected\"</#if>>测试机器部署</option>-->\n<!--                                            <option value=\"0\">专用机器部署</option>-->\n<!--                                        </select>-->\n<!--                                    </div>-->\n<!--                                    <label class=\"col-form-label col-md-2\" style=\"color:red\">('自动挑选机器'选填)</label>-->\n<!--                                </div>-->\n\n\n                                <div class=\"form-group row\" id=\"migrateMachineDiv\">\n                                    <label class=\"col-form-label col-md-2 text-end\" style=\"color:red\">Redis迁移机器:</label>\n                                    <div class=\"col-md-8\">\n                                        <select id=\"id_select\" class=\"selectpicker w-100 border rounded\" multiple data-live-search=\"true\" title=\"--请选择--\">\n                                            <#list machinelist as machine>\n                                                <#if machineInstanceCountMap?? && machine.info.ip?? && machineInstanceCountMap[machine.info.ip]??>\n                                                    <#assign usedCpu = machineInstanceCountMap[machine.info.ip]?string(\"0\")>\n                                                <#else>\n                                                    <#assign usedCpu = \"0\">\n                                                </#if>\n                                                <#assign cpu = machine.info.cpu?string(\"0\")>\n                                                <#assign cpuUsage = ((usedCpu?number)/(cpu?number)*100)?string(\"0\")>\n                                                <#assign usedMemRss = ((machine.machineMemInfo.usedMemRss)/1024/1024/1024)?string(\"0.0\")>\n                                                <#assign mem = machine.info.mem?string(\"0.0\")>\n                                                <#assign memUsage = ((usedMemRss?number)/(mem?number)*100)?string(\"0.0\")>\n                                                <#if (machine.info.useType==0)>\n                                                    <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}<#if machine.info.rack?? && (machine.info.rack != '')>-${machine.info.rack!}</#if>】【专用-${machine.info.extraDesc!}】</option>\n                                                </#if>\n                                                <#if (machine.info.useType==1)>\n                                                    <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}<#if machine.info.rack?? && (machine.info.rack != '')>-${machine.info.rack!}</#if>】【测试-${machine.info.extraDesc!}】</option>\n                                                </#if>\n                                                <#if (machine.info.useType==2)>\n                                                    <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}<#if machine.info.rack?? && (machine.info.rack != '')>-${machine.info.rack!}</#if>】【混合-${machine.info.extraDesc!}】</option>\n                                                </#if>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                    <div class=\"col-md-2\">\n                                        <button type=\"button\" id=\"chooseRedis\" class=\"btn btn-success btn-sm\" style=\"float: left;background-color:rgb(53, 170, 71);\" onclick=\"autoSelectMachine(${appDetail.appDesc.type!},${appDetail.machineNum!},${appDetail.mem!},${appDetail.masterNum!},${appDetail.slaveNum!})\" >\n                                             \t自动挑选机器\n                                        </button>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\" id=\"redisMachineInfoDiv\" style=\"display:none\">\n                                    <label class=\"col-form-label col-md-2\">\n                                    </label>\n                                    <div class=\"col-md-8\">\n                                        <table class=\"table table-striped table-bordered table-hover\" id=\"tableList\">\n                                            <thead>\n                                            <tr>\n                                                <th>ip</th>\n                                                <th>总内存</th>\n                                                <th>剩余内存</th>\n                                                <th>实例数/核数</th>\n                                                <th>master数/salve数/sentinel数</th>\n                                                <th>宿主机/机架信息</th>\n                                                <th>详情</th>\n                                            </tr>\n                                            </thead>\n                                            <tbody>\n                                            </tbody>\n                                        </table>\n                                        <#if (appDetail.appDesc.isTest == 1)>\n                                            <div><font style=\"color: green\">测试应用对剩余内存和剩余核数不作要求</font></div>\n                                        </#if>\n                                    </div>\n                                </div>\n\n                                <#if (appDetail.appDesc.type==5)>\n                                    <div class=\"form-group row\" id=\"migrateMachineDiv\">\n                                        <label class=\"col-form-label col-md-2 text-end\" style=\"color:red\">Sentinel迁移机器:</label>\n                                        <div class=\"col-md-8\">\n                                            <select id=\"id_select2\" class=\"selectpicker w-100 rounded border\" multiple data-live-search=\"true\"  title=\"--请选择--\">\n                                                <#list machinelist as machine>\n                                                    <option value=\"${machine.ip!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) 【${machine.info.realIp!}<#if machine.info.rack?? && (machine.info.rack != '')>-${machine.info.rack!}</#if>】<#if machine.info.extraDesc?? && (machine.info.extraDesc?length > 0)>(${machine.info.extraDesc!})</#if></option>\n                                                </#list>\n                                            </select>\n                                        </div>\n                                        <div class=\"col-md-2\">\n                                            <button type=\"button\" id=\"chooseSentinel\" class=\"btn btn-success btn-sm\" style=\"float: left;background-color:rgb(53, 170, 71);\" onclick=\"selectSentinelMachine(${appDetail.appDesc.appId!})\" >\n                                                 \t自动挑选机器\n                                            </button>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\" id=\"sentinelMachineInfoDiv\" style=\"display:none\">\n                                        <label class=\"col-form-label col-md-2\">\n                                        </label>\n                                        <div class=\"col-md-8\">\n                                            <table class=\"table table-striped table-bordered table-hover\" id=\"sentinelTableList\">\n                                                <thead>\n                                                <tr>\n                                                    <th>ip</th>\n                                                    <th>机房</th>\n                                                    <th>总内存</th>\n                                                    <th>分配内存</th>\n                                                    <th>使用内存</th>\n                                                    <th>剩余内存</th>\n                                                    <th>实例数/核数</th>\n                                                    <th>详情</th>\n                                                </tr>\n                                                </thead>\n                                                <tbody>\n                                                </tbody>\n                                            </table>\n                                        </div>\n                                    </div>\n                                </#if>\n\n                                <div class=\"form-group row\" id=\"nodeChangeDiv\" hidden=\"hidden\">\n                                    <label class=\"col-form-label col-md-2 text-end\" style=\"color:red\"> 节点变更信息:</label>\n                                    <div class=\"col-md-8\">\n                                        <textarea id=\"nodeChangeInfo\" type=\"text\" rows=\"<#if (appDetail.appDesc.type == 5)>${appDetail.masterNum+appDetail.slaveNum+5}</#if><#if (appDetail.appDesc.type != 5)>${appDetail.masterNum+appDetail.slaveNum}</#if>\" class=\"form-control\" readonly></textarea>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\" id=\"instanceTargetDiv\" hidden=\"hidden\">\n                                    <label class=\"col-form-label col-md-2 text-end\"> <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId!}\" style=\"color:red\">最新实例信息:</a></label>\n                                    <div class=\"col-md-8\">\n                                        <textarea id=\"instanceTargetInfo\" type=\"text\" rows=\"<#if (appDetail.appDesc.type == 5)>${appDetail.masterNum+appDetail.slaveNum+5}</#if><#if (appDetail.appDesc.type != 5)>${appDetail.masterNum*3}</#if>\" class=\"form-control\" style=\"background-color:#BC8F8F\" readonly></textarea>\n                                    </div>\n                                    <label id=\"instanceTargetLog\" class=\"col-form-label col-md-2\"></label>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\"> 提示: </label>\n                                    <div class=\"col-md-8\">\n                                        <div class=\"form-control-static\">如果在迁移过程中遇到错误警告，请登录服务器查看日志解决后再<b style=\"color:cornflowerblue\t\">继续</b>执行</div>\n                                    </div>\n                                </div>\n                            </div>\n                        </form>\n                    </div>\n                </div>\n            </form>\n\n            <div class=\"modal-footer\">\n                 <button type=\"button\" id=\"skipbutton\" class=\"btn btn-secondary\" data-bs-toggle=\"modal\" onclick=\"\" style=\"display:none;\"><span id=\"skip\">跳过</span></button>\n                 <button type=\"button\" class=\"btn btn-primary\" data-bs-toggle=\"modal\" onclick=\"migrate(${appDetail.appDesc.appId!})\"><span id=\"install\">开始迁移</span></button>\n            </div>\n\n            <input type=\"hidden\" id=\"downInstanceIds\" name=\"downInstanceIds\" value=\"${downInstanceIds!}\">\n            <input type=\"hidden\" id=\"downSentinelIds\" name=\"downSentinelIds\" value=\"${downSentinelIds!}\">\n            <input type=\"hidden\" id=\"appType\" name=\"appType\" value=\"${appDetail.appDesc.type!}\">\n        </div>\n    </div>\n</div>\n\n\n\n<script>\nvar migrateId = \"\";\n// step forward method\nvar method = \"\";\n\nvar machineInfo = \"\";\nvar machineSentinelInfo = \"\";\n\n\n\n/**\n * 自动挑选机器\n */\nfunction autoSelectMachine(type,machineNum,mem,masterNum,slaveNum) {\n    $('#tableList tbody tr').empty();\n\n    $.post('${request.contextPath}/manage/app/migrate/selectMachine',\n        {\n            type: type,\n            useType: $('#deployType').val(),\n            room: $('#room').val(),\n            machineNum: machineNum,\n            mem: mem,\n            masterNum: masterNum,\n            slaveNum: slaveNum\n        },\n        function (data) {\n            if(data.status == 1){\n                var selectedMachineList=data.resMachineList;\n                var selectedMachineArray=new Array();\n                selectedMachineList.forEach(function (machine) {\n                    selectedMachineArray.push(machine.ip);\n                    $(\"#tableList tbody\").prepend(\n                        '<tr>\\n' +\n                        \"   <td> <a target='_blank' href='${request.contextPath}/manage/machine/machineInstances?ip=\"+machine.ip+\" '>\"+machine.ip+\"</a>\\n\" +\n                        '   <td>'+machine.mem+'G</td>\\n' +\n                        '   <td ><font color=\"#FF0000\">'+(machine.mem-(machine.applyMem/1024/1024/1024)).toFixed(2)+'G </font> </td>\\n' +\n                        '   <td>'+machine.instanceNum+\"&nbsp;&nbsp;/&nbsp;&nbsp;\"+machine.cpu+'</td>\\n' +\n                        // '   <td>'+machineDeployStat.masterNum+\"&nbsp;&nbsp;/&nbsp;&nbsp;\"+machineDeployStat.slaveNum+\"&nbsp;&nbsp;/&nbsp;&nbsp;\"+machineDeployStat.sentinelNum+'</td>\\n' +\n                        '   <td>'+machine.realIp+\"&nbsp;&nbsp;/&nbsp;&nbsp;\"+machine.rack+'</td>\\n' +\n                        \"   <td> <a target='_blank' href='${request.contextPath}/manage/machine/index?tabTag=machine&ipLike=\"+machine.ip+\" '>查看</a>\\n\" +\n                        '</tr>'\n                    )\n                    $(\"#redisMachineInfoDiv\").removeAttr(\"style\");\n                });\n                $('#id_select').selectpicker('val',selectedMachineArray);\n\n            } else if(data.status == -1){\n                toastr.error(\"异常信息：\"+data.message);\n                warn(migratePlanId);\n                goOn(\"migratePlan\");\n            } else {\n                toastr.error(\"操作失败,请查看日志!\");\n                warn(migratePlanId);\n                goOn(\"migratePlan\");\n            }\n        }\n    );\n}\n\nfunction selectSentinelMachine(appId) {\n\n    $('#sentinelTableList tbody tr').empty();\n\n    $.post('${request.contextPath}/manage/app/migrate/selectSentinelMachine',\n        {\n            appId: appId\n        },\n        function (data) {\n            if(data.status == 1){\n                var selectedMachineList=data.sentinelMachineList;\n                var selectedMachineArray=new Array();\n                selectedMachineList.forEach(function (machine) {\n                    selectedMachineArray.push(machine.ip);\n                    $(\"#sentinelTableList tbody\").prepend(\n                        '<tr>\\n' +\n                        \"   <td> <a target='_blank' href='${request.contextPath}/manage/machine/machineInstances?ip=\"+machine.ip+\" '>\"+machine.ip+\"</a>\\n\" +\n                        '   <td>'+machine.room+'G</td>\\n' +\n                        '   <td>'+machine.mem+'</td>\\n' +\n                        '   <td>'+(machine.applyMem/1024/1024/1024).toFixed(2)+'G</td>\\n' +\n                        '   <td>'+(machine.usedMem/1024/1024/1024).toFixed(2)+'G</td>\\n' +\n                        '   <td ><font color=\"#FF0000\">'+(machine.mem-(machine.applyMem/1024/1024/1024)).toFixed(2)+'G </font> </td>\\n' +\n                        '   <td>'+machine.instanceNum+\"&nbsp;&nbsp;/&nbsp;&nbsp;\"+machine.cpu+'</td>\\n' +\n                        \"   <td> <a target='_blank' href='${request.contextPath}/manage/machine/index?tabTag=machine&ipLike=\"+machine.ip+\" '>查看</a>\\n\" +\n                        '</tr>'\n                    )\n                    $(\"#sentinelMachineInfoDiv\").removeAttr(\"style\");\n                });\n                $('#id_select2').selectpicker('val',selectedMachineArray);\n\n            } else if(data.status == -1){\n                toastr.error(\"异常信息：\"+data.message);\n                warn(migratePlanId);\n                goOn(\"migratePlan\");\n            } else {\n                toastr.error(\"操作失败,请查看日志!\");\n                warn(migratePlanId);\n                goOn(\"migratePlan\");\n            }\n        }\n    );\n\n}\n\n /**\n * 一键迁移\n */\nfunction migrate(appId){\n    migrateId = appId;\n\n    // 迁移机器验证\n    if(machineInfo == ''){\n        $(\"#id_select option:selected\").each(function(){\n            if(this.value != '' ){\n            machineInfo += this.value +\";\";\n            }\n        });\n    }\n    if(machineSentinelInfo == ''){\n        $(\"#id_select2 option:selected\").each(function(){\n            if(this.value != ''){\n                machineSentinelInfo += this.value +\";\";\n            }\n        });\n    }\n\n    // 迁移redis机器验证\n    if($(\"#appType\").attr(\"value\") == '5'){\n        if(machineInfo==''){\n            toastr.error(\"未添加机器，请选择Redis迁移机器\");\n            return ;\n        }\n    }\n    // 迁移sentinel机器验证\n    if($(\"#appType\").attr(\"value\") == '5'){\n        if(machineSentinelInfo==''){\n        toastr.error(\"未添加机器，请选择Sentinel迁移机器\");\n        return ;\n        }\n    }\n\n    complete(\"appInfo\");\n\n\tif($('#install').html() == \"继续\"){\n        // step forward\n\t\tsetTimeout(method+'()', 500);\n\t}else if($('#install').html() == \"迁移完成\"){\n        // step complete\n        window.location.reload();\n    }else{\n        migratePlan();\n    }\n}\n/**\n * 继续 step forward\n */\nfunction goOn(m){\n\t$(\"#install\").html(\"继续\");\n\tmethod = m;\n}\n\n/**\n*  跳过步骤\n*/\nfunction skip(currentMethod,nextMethod){\n    active(currentMethod);\n    complete(currentMethod);\n    goOn(nextMethod);\n    $(\"#skipbutton\").attr(\"style\",\"display:none;\");\n}\n\n/**\n* 2.应用迁移计划\n*/\nfunction migratePlan(){\n    var migratePlanId = \"migratePlan\";\n    active(migratePlanId);\n    $(\"#install\").html(\"迁移检查中...\");\n\n\n    $.post('${request.contextPath}/manage/app/migrate/checkPlan',\n        {\n        \"appId\":migrateId,\n        \"machineInfo\":machineInfo,\n        \"machineSentinelInfo\":machineSentinelInfo,\n        \"type\":$(\"#appType\").attr(\"value\")\n    },\n        function(data){\n            if(data.status == 1){\n                // 选择迁移机器\n                $(\"#id_select\").attr(\"disabled\",\"disabled\");\n                $(\"#id_select2\").attr(\"disabled\",\"disabled\");\n                $(\"#downSlaveDiv\").removeAttr(\"hidden\");\n                $(\"#nodeChangeDiv\").removeAttr(\"hidden\");\n                $(\"#downInstanceIds\").attr(\"value\",data.downInstanceIds);\n                $(\"#downSentinelIds\").attr(\"value\",data.downSentinelIds);\n                $(\"#nodeChangeInfo\").html(data.newInstanceInfo+data.downInstanceInfo);\n                // 隐藏机器挑选细节\n                $(\"#redisMachineInfoDiv\").attr(\"style\",\"display:none\");\n                $(\"#sentinelMachineInfoDiv\").attr(\"style\",\"display:none\");\n                $(\"#chooseRedis\").attr(\"style\",\"display:none\");\n                $(\"#chooseSentinel\").attr(\"style\",\"display:none\");\n                complete(migratePlanId);\n                goOn(\"slaveChange\");\n                // 可跳过步骤\n                $(\"#skipbutton\").removeAttr(\"style\");\n                $(\"#skipbutton\").attr(\"onclick\",\"skip('slaveChange','msFailover')\");\n            } else if(data.status == -1){\n                toastr.error(\"异常信息：\"+data.message);\n                warn(migratePlanId);\n                goOn(\"migratePlan\");\n            } else {\n                toastr.error(\"操作失败,请查看日志!\");\n                warn(migratePlanId);\n                goOn(\"migratePlan\");\n            }\n        }\n    );\n}\n\n/**\n* 3.新老Slave节点替换\n*/\nfunction slaveChange(){\n    var slaveChangeId = \"slaveChange\";\n    active(slaveChangeId);\n    $(\"#install\").html(\"slave节点替换中...\");\n    $.post('${request.contextPath}/manage/app/migrate/nodeReplace',\n    {\n    \"appId\":migrateId,\n    //\"machineInfo\":$(\"button[data-id='id_select']\").attr(\"title\"),\n    //\"machineSentinelInfo\":$(\"button[data-id='id_select2']\").attr(\"title\"),\n    \"machineInfo\":machineInfo,\n    \"machineSentinelInfo\":machineSentinelInfo,\n    \"downInstanceIds\":$(\"#downInstanceIds\").attr(\"value\"),\n    \"type\":$(\"#appType\").attr(\"value\")\n    },\n    function(data){\n        if(data.status == 1){\n            // master-slave 日志\n            $(\"#instanceTargetDiv\").removeAttr(\"hidden\");\n            $(\"#instanceTargetInfo\").html(data.instanceTargetInfo);\n            $(\"#instanceTargetLog\").html(data.instanceTargetLog);\n            $(\"#nodeChangeDiv\").attr(\"hidden\",\"hidden\");\n            complete(slaveChangeId);\n            goOn(\"msFailover\");\n        } else if(data.status == -1){\n            toastr.error(\"异常信息：\"+data.message);\n            warn(slaveChangeId);\n            goOn(\"slaveChange\");\n        } else {\n            toastr.error(\"操作失败,请查看日志!\");\n            warn(slaveChangeId);\n            goOn(\"slaveChange\");\n        }\n    });\n}\n\n/**\n* 4.主从Failover\n*/\nfunction msFailover(){\n    var failoverId = \"msFailover\";\n    active(failoverId);\n    $(\"#install\").html(\"主从切换中...\");\n    $.post('${request.contextPath}/manage/app/migrate/msFailover',\n    {\n        \"appId\":migrateId,\n        \"type\":$(\"#appType\").attr(\"value\")\n    },\n    function(data){\n        if(data.status == 1){\n            // master-slave 日志\n            $(\"#instanceTargetDiv\").removeAttr(\"hidden\");\n            $(\"#instanceTargetInfo\").html(data.instanceTargetInfo);\n            $(\"#instanceTargetLog\").html(data.instanceTargetLog);\n            // 老redis实例下线\n            $(\"#downInstanceIds\").attr(\"value\",data.downInstanceIds);\n            complete(failoverId);\n            // 下一步添加新的从节点\n            goOn(\"addNewSlave\");\n            // 可跳过步骤\n            $(\"#skipbutton\").removeAttr(\"style\");\n            $(\"#skipbutton\").attr(\"onclick\",\"skip('addNewSlave','instanceCheck')\");\n        } else if(data.status == -1){\n            toastr.error(\"异常信息：\"+data.message);\n            warn(failoverId);\n            goOn(\"msFailover\");\n        } else {\n            toastr.error(\"操作失败,请查看日志!\");\n            warn(failoverId);\n            goOn(\"msFailover\");\n        }\n    });\n}\n\n/**\n* 5.new Slave配置替换重启\n*/\nfunction addNewSlave(){\n    var addNewSlaveId = \"addNewSlave\";\n    active(addNewSlaveId);\n    $(\"#install\").html(\"添加新slave...\");\n    $(\"#skipbutton\").attr(\"style\",\"display:none;\");\n    $.post('${request.contextPath}/manage/app/migrate/addSlave',\n    {\n        \"appId\":migrateId,\n        //\"machineInfo\":$(\"button[data-id='id_select']\").attr(\"title\"),\n        \"machineInfo\":machineInfo,\n        \"type\":$(\"#appType\").attr(\"value\")\n    },\n    function(data){\n        if(data.status == 1){\n            $(\"#instanceTargetInfo\").html(data.instanceTargetInfo);\n            $(\"#instanceTargetLog\").html(data.instanceTargetLog);\n            complete(addNewSlaveId);\n            goOn(\"instanceCheck\");\n        } else if(data.status == -1){\n            toastr.error(\"异常信息：\"+data.message);\n            warn(addNewSlaveId);\n            goOn(\"addNewSlave\");\n        } else {\n            toastr.error(\"操作失败,请查看日志!\");\n            warn(addNewSlaveId);\n            goOn(\"addNewSlave\");\n        }\n    });\n}\n\n\n\n/**\n* 6.验证当前实例状态\n*/\nfunction instanceCheck(){\n    var instanceCheckId = \"instanceCheck\";\n    active(instanceCheckId);\n    $(\"#install\").html(\"实例检测中...\");\n    // 显示下线实例\n    //$(\"#nodeChangeDiv\").removeAttr(\"hidden\",\"hidden\");\n    $.post('${request.contextPath}/manage/app/migrate/appCheck',\n    {\n        \"appId\":migrateId,\n        \"type\":$(\"#appType\").attr(\"value\")\n    },\n    function(data){\n        if(data.status == 1){\n            complete(instanceCheckId);\n            goOn(\"downOldSlave\");\n        } else if(data.status == -1){\n            toastr.error(\"异常信息：\"+data.message);\n            warn(instanceCheckId);\n        } else {\n            toastr.error(\"操作失败,请查看日志!\");\n            warn(instanceCheckId);\n        }\n    });\n\n}\n\n/**\n* 7.下线老实例\n*/\nfunction downOldSlave(){\n    var downOldSlaveId = \"downOldSlave\";\n    active(downOldSlaveId);\n    $(\"#install\").html(\"下线老slave实例...\");\n    var downInstanceIds = $(\"#downInstanceIds\").attr(\"value\");\n    // 如果是sentinel\n    if($(\"#appType\").attr(\"value\") == '5'){\n        downInstanceIds = $(\"#downInstanceIds\").attr(\"value\")+\",\"+$(\"#downSentinelIds\").attr(\"value\")\n    }\n    $.post('${request.contextPath}/manage/app/migrate/downSlave',\n    {\n        \"appId\":migrateId,\n        \"downInstanceIds\":downInstanceIds,\n        \"type\":$(\"#appType\").attr(\"value\")\n    },\n    function(data){\n        if(data.status == 1){\n            $(\"#instanceTargetInfo\").html(data.instanceTargetInfo);\n            $(\"#instanceTargetLog\").html(data.instanceTargetLog);\n            complete(downOldSlaveId);\n            goOn(\"migrateComplete\");\n        } else if(data.status == -1){\n            toastr.error(\"异常信息：\"+data.message);\n            warn(downOldSlaveId);\n        } else {\n            toastr.error(\"操作失败,请查看日志!\");\n            warn(downOldSlaveId);\n        }\n    });\n\n}\n\n/**\n* 8.迁移完成\n*/\nfunction migrateComplete(){\n    var upgradeCompleteId = \"migrateComplete\";\n    active(upgradeCompleteId);\n    $(\"#install\").html(\"迁移完成\");\n    $.post('${request.contextPath}/manage/app/migrate/complete',\n    {\n        \"appId\":migrat,\n        \"upgradeVersionId\":$('#versionSelect'+installId+\" option:selected\").attr('versionid')\n    },\n    function(data){\n        if(data.status == 1){\n            complete(upgradeCompleteId);\n        } else if(data.status == -1){\n            toastr.error(\"异常信息：\"+data.message);\n            warn(upgradeCompleteId);\n        } else {\n            toastr.error(\"操作失败,请查看日志!\");\n            warn(upgradeCompleteId);\n        }\n    });\n}\n\nfunction warn(id){\n\t$(\"#\"+id).addClass(\"warn\");\n}\nfunction disable(id){\n\t$(\"#\"+id).removeClass(\"active\").addClass(\"disabled\");\n}\nfunction active(id){\n\t$(\"#\"+id).removeClass(\"disabled\").removeClass(\"warn\").addClass(\"active\");\n}\nfunction complete(id){\n\t$(\"#\"+id).removeClass(\"active\").removeClass(\"warn\").addClass(\"complete\");\n}\n</script>\n\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appOps/appOpsIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>CacheCloud应用运维</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n  <#include '/manage/inc/backendResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n  <div class=\"wrapper\">\n    <#include \"/manage/inc/head.html\">\n    <#include \"/manage/inc/left.html\">\n    <div class=\"content-wrapper\">\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"card\">\n            <div class=\"card-body\">\n              <nav class=\"nav\">\n                <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                  <li id=\"app_ops_instance\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/instance?appId=${appId}\">\n                    <a class=\"nav-link d-flex\" href=\"?appId=${appId}&tabTag=app_ops_instance\">应用实例</a>\n                  </li>\n                  <li id=\"app_ops_machine\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/machine?appId=${appId}\">\n                    <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_ops_machine\">应用机器列表</a>\n                  </li>\n                  <li id=\"app_ops_detail\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/detail?appId=${appId}\">\n                    <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_ops_detail\">应用详情和审批列表</a>\n                  </li>\n                  <li id=\"app_ops_code\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/initAppPassword?appId=${appId}\">\n                    <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_ops_code\">应用密码修改</a>\n                  </li>\n                  <li id=\"app_ops_tool\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/tool/topologyExam?appId=${appId}\">\n                    <a class=\"nav-link d-flex\" href=\"?appId=${appId!}&tabTag=app_ops_tool\">应用拓扑诊断</a>\n                  </li>\n                </ul>\n              </nav>\n            </div>\n          </div>\n          <div class=\"tab-content\">\n            <div class=\"tab-pane active\" id=\"app_ops_instanceTab\">\n            </div>\n            <div class=\"tab-pane\" id=\"app_ops_machineTab\">\n            </div>\n            <div class=\"tab-pane\" id=\"app_ops_detailTab\">\n            </div>\n            <div class=\"tab-pane\" id=\"app_ops_codeTab\">\n            </div>\n            <div class=\"tab-pane\" id=\"app_ops_toolTab\">\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n    <#include \"/manage/inc/footer.html\">\n  </div>\n\n  <script type=\"text/javascript\">\n    function showTab(tab) {\n      $.get($(\"#\" + tab).attr(\"data-url\"), function (result) {\n        $(\"#\" + tab + \"Tab\").html(result);\n      });\n    }\n\n    function refreshActiveTab() {\n      var tab = getQueryString(\"tabTag\");\n      if (tab) {\n        $(\"#\" + tab + \" a\").addClass(\"active\");\n        $(\"#\" + tab + \"Tab\").addClass(\"active\").siblings().removeClass(\"active\");\n      } else {\n        tab = \"app_ops_instance\";\n        $(\"#\" + tab + \" a\").addClass(\"active\");\n      }\n      console.log(\"tab:\" + tab)\n      showTab(tab);\n      $(\"#app_tabs li a\").tooltip({placement: \"bottom\"});\n    }\n\n    function activeLeftAppOpsTab() {\n        $(\"#appOpsTab\").addClass(\"active\");\n    }\n\n    $(function () {\n      refreshActiveTab();\n      activeLeftAppOpsTab();\n    });\n\n    function getQueryString(name) {\n      var reg = new RegExp(\"(^|&)\" + name + \"=([^&]*)(&|$)\");\n      console.log(\"window.location.search: \"+ window.location.search);\n      var r = window.location.search.substr(1).match(reg);\n      if (r != null) return unescape(r[2]);\n      return null;\n    }\n  </script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appStat/appStatList.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-body\">\n                <form class=\"row justify-content-end\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/stat/list\" id=\"search_form\">\n                    <input name=\"tabId\" id=\"tabId\" value=\"${tabId!}\" type=\"hidden\"/>\n                    <label for=\"searchDate\" class=\"col-form-label col-auto text-end\">查询日期</label>\n                    <div class=\"col-auto\">\n                        <input class=\"form-control\" type=\"date\" size=\"15\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate!}\"/>\n                    </div>\n                    <button type=\"submit\" class=\"btn btn-primary col-auto\">查询</button> &nbsp;\n                    <button class=\"btn btn-primary col-auto\" onclick=\"sendExpAppsStatDataEmail()\">发送邮件</button>\n                </form>\n            </div>\n        </div>\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">应用列表指标上报情况统计</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-hover table-bordered table-sm\" id=\"tableDataList\">\n                    <thead>\n                    <tr>\n                        <th scope=\"col\">appId</th>\n                        <th scope=\"col\">应用名称</th>\n                        <th scope=\"col\">是否测试</th>\n                        <th scope=\"col\">延迟事件</th>\n                        <th scope=\"col\">慢查询</th>\n                        <th scope=\"col\">连接异常</th>\n                        <th scope=\"col\">avg连接异常耗时(ms)</th>\n                        <th scope=\"col\">命令超时</th>\n                        <th scope=\"col\">avg命令超时耗时(ms)</th>\n                        <th scope=\"col\">命令调用</th>\n                        <th scope=\"col\">avg命令耗时(ms)</th>\n                        <th scope=\"col\">server命令调用</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                        <#list list as machine>\n                        <tr>\n                            <td>\n                                <#assign app_id = machine.appId>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/manage/app/index?appId=${machine.appId!}\">${machine.appId!}</a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/admin/app/index?appId=${machine.appId!}\">${machine.name!}</a>\n                            </td>\n                            <td>\n                                <#if (machine.isTest == 1)>测试\n                                    <#else>正式\n                                </#if>\n                            </td>\n                            <td>\n                                <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?searchDate=${startDate!}&appId=${machine.appId!}&tabTag=app_latency\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['latency_count']??)>\n                                        <#assign latency_count = appClientGatherStatMap?api.get(app_id)['latency_count']>\n                                    <#else>\n                                        <#assign latency_count = 0>\n                                    </#if>\n                                    ${latency_count!0}\n                                </a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?searchDate=${startDate!}&appId=${machine.appId!}&tabTag=app_latency\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['slow_log_count']??)>\n                                        <#assign slow_log_count = appClientGatherStatMap?api.get(app_id)['slow_log_count']>\n                                    <#else>\n                                        <#assign slow_log_count = 0>\n                                    </#if>\n                                    ${slow_log_count!0}\n                                </a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/client/show/index?searchDate=${startDate!}&appId=${machine.appId!}&tabTag=app_client_exception_statistics\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['conn_exp_count']??)>\n                                        <#assign conn_exp_count = appClientGatherStatMap?api.get(app_id)['conn_exp_count']>\n                                    <#else>\n                                        <#assign conn_exp_count = 0>\n                                    </#if>\n                                    ${conn_exp_count!0}\n                                </a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/client/show/index?searchDate=${startDate!}&appId=${machine.appId!}&tabTag=app_client_exception_statistics\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['avg_conn_exp_cost']??)>\n                                        <#assign avg_conn_exp_cost = appClientGatherStatMap?api.get(app_id)['avg_conn_exp_cost']>\n                                    <#else>\n                                        <#assign avg_conn_exp_cost = 0>\n                                    </#if>\n                                    ${avg_conn_exp_cost!0}\n                                </a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/client/show/index?searchDate=${startDate!}&appId=${machine.appId!}&tabTag=app_client_exception_statistics\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['cmd_exp_count']??)>\n                                        <#assign cmd_exp_count = appClientGatherStatMap?api.get(app_id)['cmd_exp_count']>\n                                    <#else>\n                                        <#assign cmd_exp_count = 0>\n                                    </#if>\n                                    ${cmd_exp_count!0}\n                                </a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/client/show/index?searchDate=${startDate!}&appId=${machine.appId!}&tabTag=app_client_exception_statistics\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['avg_cmd_exp_cost']??)>\n                                        <#assign avg_cmd_exp_cost = appClientGatherStatMap?api.get(app_id)['avg_cmd_exp_cost']>\n                                    <#else>\n                                        <#assign avg_cmd_exp_cost = 0>\n                                    </#if>\n                                    ${avg_cmd_exp_cost!0}\n                                </a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/client/show/index?searchDate${startDate!}&appId=${machine.appId!}&tabTag=app_client_command_statistics\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['cmd_count']??)>\n                                        <#assign cmd_count = appClientGatherStatMap?api.get(app_id)['cmd_count']>\n                                    <#else>\n                                        <#assign cmd_count = 0>\n                                    </#if>\n                                    ${cmd_count!0}\n                                </a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/client/show/index?searchDate${startDate!}&appId=${machine.appId!}&tabTag=app_client_command_statistics\">\n                                    <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['avg_cmd_cost']??)>\n                                        <#assign avg_cmd_cost = appClientGatherStatMap?api.get(app_id)['avg_cmd_cost']>\n                                    <#else>\n                                        <#assign avg_cmd_cost = 0>\n                                    </#if>\n                                    ${avg_cmd_cost!0}\n                                </a>\n                            </td>\n                            <td>\n                                <#if (appClientGatherStatMap?? && appClientGatherStatMap?api.get(app_id)?? && appClientGatherStatMap?api.get(app_id)['cmd_count']??)>\n                                    <#assign server_cmd_count = appClientGatherStatMap?api.get(app_id)['server_cmd_count']>\n                                <#else>\n                                    <#assign server_cmd_count = 0>\n                                </#if>\n                                ${server_cmd_count!0}\n                            </td>\n                        </tr>\n                    </#list>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n    </div>\n</div>\n\n<script type=\"text/javascript\">\n    $(function () {\n        var searchDate = $('#searchDate').val();\n        if (searchDate == null || searchDate == '') {\n            var time = new Date();\n            var day = (\"0\" + time.getDate()).slice(-2);\n            var month = (\"0\" + (time.getMonth() + 1)).slice(-2);\n            var today = time.getFullYear() + \"-\" + (month) + \"-\" + (day);\n            $('#searchDate').val(today);\n        }\n    })\n\n    //验证是数字\n    function testisNum(id) {\n        var value = document.getElementById(id).value;\n        if (value != \"\" && isNaN(value)) {\n            alert(\"请输入数字类型!\");\n            document.getElementById(id).value = \"\";\n            document.getElementById(id).focus();\n        }\n    }\n\n    function sendExpAppsStatDataEmail() {\n        var searchDate = document.getElementById(\"searchDate\").value;\n        $.get('${request.contextPath}/manage/app/tool/sendExpAppsStatDataEmail', {searchDate: searchDate});\n        alert(\"异常应用日报已发送，请查收\")\n    }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appStat/appStatListServer.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <nav class=\"nav\">\n                        <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                            <li class=\"nav-item\">\n                                <a class=\"nav-link d-flex <#if (tabId == 1)>active</#if>\" href=\"${request.contextPath}/manage/app/stat/list/server?tabId=1\">空间指标</a>\n                            </li>\n                            <li class=\"nav-item\">\n                                <a class=\"nav-link d-flex <#if (tabId == 2)>active</#if>\" href=\"${request.contextPath}/manage/app/stat/list/server?tabId=2\">碎片率指标</a>\n                            </li>\n                            <li class=\"nav-item\">\n                                <a class=\"nav-link d-flex <#if (tabId == 3)>active</#if>\" href=\"${request.contextPath}/manage/app/stat/list/server?tabId=3\">应用拓扑诊断</a>\n                            </li>\n                            <li class=\"nav-item\">\n                                <a class=\"nav-link d-flex <#if (tabId == 4)>active</#if>\" href=\"${request.contextPath}/manage/app/stat/list/server?tabId=4\">容器环境诊断</a>\n                            </li>\n                            <li class=\"nav-item\">\n                                <a class=\"nav-link d-flex <#if (tabId == 5)>active</#if>\" href=\"${request.contextPath}/manage/app/stat/list/server?tabId=5\">宿主环境诊断</a>\n                            </li>\n                            <li class=\"nav-item\">\n                                <a class=\"nav-link d-flex <#if (tabId == 6)>active</#if>\" href=\"${request.contextPath}/manage/app/stat/list/server?tabId=6\">内存审计</a>\n                            </li>\n                        </ul>\n                    </nav>\n                </div>\n\n                <div class=\"row mt-3\">\n                    <form class=\"row justify-content-end\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/stat/list/server\" id=\"search_form\">\n                        <input name=\"tabId\" id=\"tabId\" value=\"${tabId!}\" type=\"hidden\"/>\n                        <#if tabId?? && (tabId == 6)>\n                        <#else>\n                            <label for=\"searchDate\" class=\"col-form-label col-auto\">&nbsp;查询日期:</label>\n                            <div class=\"col-auto\">\n                                <input type=\"date\" class=\"form-control\" size=\"15\" name=\"searchDate\" id=\"searchDate\" value=\"${searchDate}\"/>\n                            </div>\n                            <button type=\"submit\" class=\"btn btn-primary col-auto\">查询</button> &nbsp;\n                            <button type=\"button\" class=\"btn btn-primary col-auto\" onclick=\"sendExpAppsStatDataEmail()\">发送日报</button>\n                        </#if>\n                    </form>\n                </div>\n            </div>\n        </div>\n\n        <div class=\"card\" id=\"memIndex\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">空间使用情况统计</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <!-- 内存指标 -->\n                <table class=\"table table-striped table-hover table-bordered table-sm\" id=\"tableDataList\" name=\"tableDataList\">\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">appId</th>\n                            <th scope=\"col\">应用名称</th>\n                            <th scope=\"col\">是否测试</th>\n                            <th scope=\"col\">应用类型</th>\n                            <th scope=\"col\">分片数*分片空间G</th>\n                            <th scope=\"col\">空间使用</th>\n                            <th scope=\"col\">rss内存使用</th>\n                            <th scope=\"col\">空间使用率%</th>\n                            <th scope=\"col\">空间报警阀值%</th>\n                            <th scope=\"col\">客户端连接数</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        <#list appDescList! as appDesc>\n                        <#assign app_id = appDesc.appId>\n                        <#assign appDetail = appDetailVOMap?api.get(app_id)>\n                        <tr>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/manage/app/index?appId=${appDesc.appId}\">${appDesc.appId}</a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/admin/app/index?appId=${appDesc.appId}\">${appDesc.name}</a>\n                            </td>\n                            <td>\n                                <#if (appDesc.isTest == 1)>测试\n                                <#else>正式\n                                </#if>\n                            </td>\n                            <td>${appDesc.typeDesc!}</td>\n                            <td>\n                                <#assign master_num = appDetailVOMap?api.get(app_id).masterNum>\n                                <#assign slave_num = appDetailVOMap?api.get(app_id).slaveNum>\n                                <#if !(master_num??)>\n                                    <#assign master_num = 0>\n                                </#if>\n                                ${master_num}&nbsp;*&nbsp;${(appDetailVOMap?api.get(app_id).mem/master_num/1024)?string(\"##.##\")}G\n                            </td>\n\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign usedMem = ((appClientGatherStatMap?api.get(app_id)['used_memory'])/1024/1024/1024)?string(\"0.00\")?number>\n                                <#else>\n                                    <#assign usedMem = 0>\n                                </#if>\n                                <#assign mem = (appDetail.mem/1024)?string(\"0.00\")?number>\n                                <#assign fmtMemoryUsageRatio = (usedMem/mem*100.0)?string(\"0.00\")?number>\n                                <div class=\"progress progress-fs-1\" role=\"progressbar\" aria-valuenow=\"${fmtMemoryUsageRatio}\"\n                                     aria-valuemax=\"100\"\n                                     aria-valuemin=\"0\" >\n                                    <#if (fmtMemoryUsageRatio >= 80.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-danger'>\n                                    <#elseif (fmtMemoryUsageRatio >= 60.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-warning'>\n                                    <#else>\n                                        <#assign memUsedProgressBarStatus = 'bg-success'>\n                                    </#if>\n                                    <div class=\"progress-bar ${memUsedProgressBarStatus}\"\n                                        style=\"width: ${fmtMemoryUsageRatio}%; overflow: visible;\">\n                                        <span style=\"color: #000000; margin-bottom: 0\">\n                                            ${usedMem}G&nbsp;&nbsp;Used/\n                                            ${mem}G&nbsp;&nbsp;Total\n                                        </span>\n                                    </div>\n                                </div>\n                            </td>\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign usedMemRss = ((appClientGatherStatMap?api.get(app_id)['used_memory_rss'])/1024/1024/1024)?string(\"0.00\")?number>\n                                <#else>\n                                    <#assign usedMemRss = 0>\n                                </#if>\n                                <#assign mem = (appDetail.mem/1024)?string(\"0.00\")?number>\n                                <#assign fmtMemoryUsageRatio = (usedMemRss/mem*100.0)?string(\"0.00\")?number>\n                                <div class=\"progress progress-fs-1\" role=\"progressbar\" aria-valuenow=\"${fmtMemoryUsageRatio}\"\n                                     aria-valuemax=\"100\"\n                                     aria-valuemin=\"0\">\n                                    <#if (fmtMemoryUsageRatio >= 80.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-danger'>\n                                    <#elseif (fmtMemoryUsageRatio >= 60.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-warning'>\n                                    <#else>\n                                        <#assign memUsedProgressBarStatus = 'bg-success'>\n                                    </#if>\n                                    <div class=\"progress-bar ${memUsedProgressBarStatus}\"\n                                         style=\"width: ${fmtMemoryUsageRatio}%; overflow: visible;\">\n                                        <span style=\"color: #000000; margin-bottom: 0\">\n                                            ${usedMemRss}G&nbsp;&nbsp;Used\n                                            /${mem}G&nbsp;&nbsp;Total\n                                        </span>\n                                    </div>\n                                </div>\n                            </td>\n\n                            <td>\n                                <#assign mem_used_ratio = appDetailVOMap?api.get(app_id).memUsePercent>\n                                <#if !(mem_used_ratio??)>\n                                    <#assign mem_used_ratio = '0'>\n                                </#if>\n                                ${mem_used_ratio}\n                            </td>\n                            <td>\n                                <#assign memUseThreshold = appDetailVOMap?api.get(app_id).appDesc.memAlertValue>\n                                ${memUseThreshold}\n                            </td>\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign conn = appClientGatherStatMap?api.get(app_id)['connected_clients']>\n                                <#else>\n                                    <#assign conn = 0>\n                                </#if>\n                                <#if !(conn??)>\n                                    <#assign conn = '0'>\n                                </#if>\n                                <button class=\"btn btn-warning btn-sm w-100\">\n                                    <a href=\"${request.contextPath}/admin/app/index?appId=${app_id}&tabTag=app_clientList\"\n                                       target=\"_blank\"><font style=\"color: white\">${conn} 连接</font></a>\n                                </button>\n                            </td>\n                        </tr>\n                    </#list>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n\n        <div class=\"card\" id=\"fragRatioIndex\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">碎片率指标情况统计</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <!-- 碎片率指标 -->\n                <table class=\"table table-striped table-hover table-bordered table-sm\" id=\"tableDataList\" name=\"tableDataList\">\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">appId</th>\n                            <th scope=\"col\">应用名称</th>\n                            <th scope=\"col\">是否测试</th>\n                            <th scope=\"col\">redis版本</th>\n                            <th scope=\"col\">key数量</th>\n                            <th scope=\"col\">内存使用</th>\n                            <th scope=\"col\">rss内存使用</th>\n                            <th scope=\"col\">平均碎片率(%)</th>\n                            <th scope=\"col\">max cpuSys(s)</th>\n                            <th scope=\"col\">max cpuUser(s)</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        <#list appDescList! as appDesc>\n                        <#assign app_id = appDesc.appId>\n                        <#assign appDetail = appDetailVOMap?api.get(app_id)>\n                        <tr>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/manage/app/index?appId=${app_id}\">${app_id}</a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/admin/app/index?appId=${app_id}\">${appDesc.name}</a>\n                            </td>\n                            <td>\n                                <#if (appDesc.isTest == 1)>测试\n                                <#else>正式\n                                </#if>\n                            </td>\n                            <td>${appDesc.versionName}</td>\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign object_size = appClientGatherStatMap?api.get(app_id)['object_size']>\n                                <#else>\n                                    <#assign object_size = 0>\n                                </#if>\n                                <#if !(object_size??)>\n                                    <#assign object_size = 0>\n                                </#if>\n                                ${object_size}\n                            </td>\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign used_memory = ((appClientGatherStatMap?api.get(app_id)['used_memory'])/1024/1024)?string(\"0.00\")?number>\n                                <#else>\n                                    <#assign used_memory = 0>\n                                </#if>\n                                ${used_memory}\n                            </td>\n\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign used_memory_rss = ((appClientGatherStatMap?api.get(app_id)['used_memory_rss'])/1024/1024)?string(\"0.00\")?number>\n                                <#else>\n                                    <#assign used_memory_rss = 0>\n                                </#if>\n                                ${used_memory_rss}\n                            </td>\n\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))?? && (appClientGatherStatMap?api.get(app_id)['avg_mem_frag_ratio'])??>\n                                    <#assign avg_mem_frag_ratio = appClientGatherStatMap?api.get(app_id)['avg_mem_frag_ratio']>\n                                <#else>\n                                    <#assign avg_mem_frag_ratio = '0'>\n                                </#if>\n                                <#if !(avg_mem_frag_ratio??)>\n                                    <#assign avg_mem_frag_ratio = '0'>\n                                </#if>\n                                ${avg_mem_frag_ratio}\n                            </td>\n\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign max_cpu_sys = appClientGatherStatMap?api.get(app_id)['max_cpu_sys']>\n                                <#else>\n                                    <#assign max_cpu_sys = '0'>\n                                </#if>\n                                <#if !(max_cpu_sys??)>\n                                    <#assign max_cpu_sys = '0'>\n                                </#if>\n                                ${max_cpu_sys}\n                            </td>\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??>\n                                    <#assign max_cpu_user = appClientGatherStatMap?api.get(app_id)['max_cpu_user']>\n                                <#else>\n                                    <#assign max_cpu_user = '0'>\n                                </#if>\n                                <#if !(max_cpu_user??)>\n                                    <#assign max_cpu_user = '0'>\n                                </#if>\n                                ${max_cpu_user}\n                            </td>\n                        </tr>\n                    </#list>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n\n        <!-- 应用拓扑诊断 -->\n        <div class=\"card\" id=\"appTopologyIndex\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">应用拓扑诊断</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <div class=\"row\">\n                    <div class=\"col-auto\">\n                        <button id=\"autoFixTopology\" class=\"btn btn-danger btn-sm\" type=\"button\" data-bs-target=\"#autoFixTopologyModal\" data-bs-toggle=\"modal\">\n                            自动修复拓扑<i class=\"bi bi-bag-plus\"></i>\n                        </button>\n                    </div>\n                </div>\n                <table class=\"table table-striped table-hover table-bordered table-sm\" id=\"tableDataList\" name=\"tableDataList\">\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">appId</th>\n                            <th scope=\"col\">应用名称</th>\n                            <th scope=\"col\">是否测试</th>\n                            <th scope=\"col\">类型</th>\n                            <th scope=\"col\">redis版本</th>\n                            <th scope=\"col\">节点分布</th>\n                            <th scope=\"col\">拓扑诊断\n                                <button class=\"btn btn-success btn-sm\" onclick=\"topologyUpdate()\">诊断更新</button>\n                            </th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        <#list appDescList! as appDesc>\n                        <#assign app_id = appDesc.appId>\n                        <#assign appDetail = appDetailVOMap?api.get(app_id)>\n                        <tr>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/manage/app/index?appId=${app_id}\">${app_id}</a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/admin/app/index?appId=${app_id}\">${appDesc.name}</a>\n                            </td>\n                            <td>\n                                <#if (appDesc.isTest == 1)>测试\n                                <#else>正式\n                                </#if>\n                            </td>\n                            <td>${appDesc.typeDesc}</td>\n                            <td>${appDesc.versionName}</td>\n                            <td>\n                                <#assign master_num = appDetailVOMap?api.get(app_id).masterNum>\n                                <#assign slave_num = appDetailVOMap?api.get(app_id).slaveNum>\n                                <#if !(master_num??)>\n                                    <#assign master_num = 0>\n                                </#if>\n                                <#if !(slave_num??)>\n                                    <#assign slave_num = 0>\n                                </#if>\n                                主从节点:\n                                <#if (master_num == slave_num)>${master_num},${slave_num}\n                                <#else><span\n                                            style=\"color:red\">${master_num},${slave_num}</span>\n                                </#if>\n                            </td>\n                            <td>\n                                <#if appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??\n                                && appClientGatherStatMap?api.get(app_id)['topology_exam_result']??\n                                && (appClientGatherStatMap?api.get(app_id)['topology_exam_result']== 0)>\n                                    <span style=\"color:green\">正常</span>\n                                <#elseif appClientGatherStatMap?? && (appClientGatherStatMap?api.get(app_id))??\n                                && appClientGatherStatMap?api.get(app_id)['topology_exam_result']??\n                                && (appClientGatherStatMap?api.get(app_id)['topology_exam_result']== 1)>\n                                    <span style=\"color:red\">异常</span>\n                                <#else>\n                                    <span style=\"color:red\">未检测</span>\n                                </#if>\n                                [<a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id}&tabTag=app_ops_tool\">查看诊断</a>]\n                            </td>\n                        </tr>\n                    </#list>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n\n        <!-- 容器环境诊断 -->\n        <div class=\"card\" id=\"containerIndex\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">容器环境诊断</h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <label class=\"col-form-label col-md-3\"></label>\n                    <textarea rows=\"5\"  class=\"form-control\" style=\"color:darkgray;overflow-y:hidden\" readonly=\"readonly\">\n    容器环境基准值:\n        1).内存分配策略: 是否为1,内核允许分配所有的物理内存;\n        2).thp大内存页配置: 设置为never,防止fork过程中消耗大内存拷贝导致阻塞;\n        3).内存swap配置: 是否为0关闭swap，避免内存io转换磁盘io导致阻塞;\n        4).容器nproc配置: 获取当前容器的nproc配置;\n                    </textarea>\n                </div>\n                <div class=\"table-responsive\">\n                    <table class=\"table table-striped table-hover table-bordered table-sm\" id=\"tableDataList\" name=\"tableDataList\">\n                        <thead>\n                            <tr>\n                                <th scope=\"col\">容器ip</th>\n                                <th scope=\"col\">overcommit_memory</th>\n                                <th scope=\"col\">swappiness</th>\n                                <th scope=\"col\">thp_enabled</th>\n                                <th scope=\"col\">thp_defrag</th>\n                                <th scope=\"col\">nproc</th>\n                                <th scope=\"col\">类型</th>\n                                <th scope=\"col\">诊断情况</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                        <#if machineEnvMap?? && machineEnvMap.container?? && machineEnvMap.container?size gt 0>\n                            <#list machineEnvMap.container! as container>\n                                <tr>\n                                    <td>\n                                        ${container.ip}\n                                    </td>\n                                    <td>${container.envs.overcommit_memory}</td>\n                                    <td>${container.envs.swappines}</td>\n                                    <td>${container.envs.transparent_hugepage_enable}</td>\n                                    <td>${container.envs.transparent_hugepage_defrag}</td>\n                                    <td>${container.envs.nproc}</td>\n                                    <td>\n                                        容器\n                                    </td>\n                                    <td>\n                                        <#if (container.status == 1)>\n                                            <span style=\"color:green\">检测一致</span>\n                                        <#elseif (container.status == 2)>\n                                            <span style=\"color:red\">检测不一致</span>\n                                        <#elseif (container.status == 3)>\n                                            <span style=\"color:red\">检测异常</span>\n                                        </#if>\n                                    </td>\n                                </tr>\n                            </#list>\n                        </#if>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </div>\n\n        <!-- 宿主环境诊断 -->\n        <div class=\"card\" id=\"machineIndex\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">宿主环境诊断</h3>\n            </div>\n            <div class=\"card-body\">\n                <div>\n                    <label class=\"col-form-label col-md-3\"></label>\n                    <textarea rows=\"8\"  class=\"form-control\"  style=\"color:darkgray;overflow-y:hidden\" readonly=\"readonly\">\n    宿主环境基准值:\n        1).检测cachecloud用户连接的进程数,报警阈值 cachecloud_nprocs>=1024;\n        2).检测宿主机某天的所有实例aof写盘阻塞 ,报警阈值 fsync_slow_times >=10次;\n        3).检测tcp连接队列数: 配置 somaxconn = 511;\n        4).检测sshpass安装: 检测宿主文件拷贝命令支持;\n        5).检测运行redis实例总数;\n        6).检测打开文件数/最大文件数;\n        7).检测磁盘容量使用率 disk used >= 80%;\n                        </textarea>\n                </div>\n                <div class=\"table-responsive\">\n                    <table class=\"table table-striped table-hover table-bordered table-sm\" id=\"tableDataList\" name=\"tableDataList\">\n                        <thead>\n                            <tr>\n                                <th scope=\"col\">宿主ip</th>\n                                <th scope=\"col\">cachecloud_nprocs</th>\n                                <th scope=\"col\">fsync_slow_times</th>\n                                <th scope=\"col\">somaxconn</th>\n                                <th scope=\"col\">sshpass</th>\n                                <th scope=\"col\">redis实例数</th>\n                                <th scope=\"col\">ulimit文件数</th>\n                                <th scope=\"col\">disk used</th>\n                                <th scope=\"col\" style=\"white-space: nowrap;\">nproc</th>\n                                <th scope=\"col\">类型</th>\n                                <th scope=\"col\">诊断情况</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            <#if machineEnvMap?? && machineEnvMap.host?? && machineEnvMap.host?size gt 0>\n                                <#list machineEnvMap.host! as machine>\n                                <tr>\n                                    <td>\n                                        ${machine.ip}\n                                    </td>\n                                    <td> ${machine.envs.nproc_threads}</td>\n                                    <td> ${machine.envs.fsync_delay_times}</td>\n                                    <td>${machine.envs.somaxconn}</td>\n                                    <td> ${machine.envs.sshPass}</td>\n                                    <td> ${machine.envs.instanceNum}</td>\n                                    <td>${machine.envs.unlimit}</td>\n                                    <td>${machine.envs.diskUsed}</td>\n                                    <td style=\"white-space: nowrap;\">${machine.envs.nproc!}</td>\n                                    <td>宿主机</td>\n                                    <td>\n                                        <#if (machine.status == 1)>\n                                            <span style=\"color:green\">检测一致</span>\n                                        <#elseif (machine.status == 2)>\n                                            <span style=\"color:red\">检测不一致</span>\n                                        <#elseif (machine.status == 3)>\n                                            <span style=\"color:red\">检测异常</span>\n                                        </#if>\n                                    </td>\n                                </tr>\n                                </#list>\n                            </#if>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </div>\n\n        <!-- 内存审计 -->\n        <div class=\"card\" id=\"capacityAuditIndex\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">内存审计</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-hover table-bordered table-sm\" id=\"tableDataList\" name=\"tableDataList\" style=\"white-space: nowrap\">\n                    <thead>\n                        <tr>\n                            <th scope=\"col\">appId</th>\n                            <th scope=\"col\">应用名称</th>\n                            <th scope=\"col\">是否<br>测试</th>\n                            <th scope=\"col\">应用类型</th>\n                            <th scope=\"col\">分片数 * <br>分片内存 G</th>\n                            <th scope=\"col\">初始内存 -> <br>当前内存</th>\n                            <th scope=\"col\">内存使用</th>\n                            <th scope=\"col\">内存使用率%</th>\n                            <th scope=\"col\">分片内存使用</th>\n                            <th scope=\"col\">分片内存使用率%</th>\n                            <th scope=\"col\">自动扩容<br>(是否可扩容)</th>\n                            <th scope=\"col\">自动扩容<br>(内存使用率)</th>\n                            <th scope=\"col\">自动扩容<br>(扩容比率)</th>\n                            <th scope=\"col\">自动扩容<br>(总扩容比率)</th>\n                            <th scope=\"col\">自动扩容<br>(上次扩容时间)</th>\n                            <th scope=\"col\">是否<br>可缩容</th>\n                            <th scope=\"col\">预计缩容时间</th>\n                            <th scope=\"col\">操作</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        <#list appCapacityAuditList! as appAudit>\n                        <tr>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/manage/app/index?appId=${appAudit.appDesc.appId}\">${appAudit.appDesc.appId}</a>\n                            </td>\n                            <td>\n                                <a target=\"_blank\"\n                                   href=\"${request.contextPath}/admin/app/index?appId=${appAudit.appDesc.appId}\">${appAudit.appDesc.name}</a>\n                            </td>\n                            <td>\n                                <#if (appAudit.appDesc.isTest == 1)>测试\n                                <#else>正式\n                                </#if>\n                            </td>\n                            <td>${appAudit.appDesc.typeDesc!}</td>\n                            <td>\n                                <#assign shardingMem = (appAudit.appCapacityMonitor.curShardingMem / 1024 / 1024 / 1024)?string(\"0.00\")?number>\n                                    ${appAudit.appCapacityMonitor.shardingMasterNum}&nbsp;*&nbsp;${shardingMem}G\n                            </td>\n\n                            <td>\n                                <#assign mem = (appAudit.appCapacityMonitor.mem / 1024 / 1024 / 1024)?string(\"0.00\")?number>\n                                <#assign curMem = (appAudit.appCapacityMonitor.curMem / 1024 / 1024 / 1024)?string(\"0.00\")?number>\n                                <#if (mem != curMem)>\n                                    <span style=\"color:red\">${mem}G -> ${curMem}G</span>\n                                <#else>\n                                    ${mem}G -> ${curMem}G\n                                </#if>\n                            </td>\n\n                            <td>\n                                <#assign memUsed = (appAudit.appCapacityMonitor.memUsed / 1024 / 1024 / 1024)?string(\"0.00\")>\n                                ${memUsed}G\n                            </td>\n\n                            <td>\n                                <#assign memUsedRatio = (appAudit.appCapacityMonitor.memUsed * 100 / appAudit.appCapacityMonitor.curMem)?string(\"0.00\")>\n                                <#if ((appAudit.appCapacityMonitor.memUsed * 100 / appAudit.appCapacityMonitor.curMem) >= appAudit.appCapacityMonitor.expandMemPercent)>\n                                        <span style=\"color:red\">\n                                            ${memUsedRatio} %\n                                        </span>\n                                <#elseif (((appAudit.appCapacityMonitor.memUsed * 100 / appAudit.appCapacityMonitor.curMem) < appAudit.appCapacityMonitor.expandMemPercent) && ((appAudit.appCapacityMonitor.memUsed * 100 / appAudit.appCapacityMonitor.curMem) >= 60))>\n                                        <span style=\"color:green\">\n                                            ${memUsedRatio} %\n                                        </span>\n                                <#elseif ((appAudit.appCapacityMonitor.memUsed * 100 / appAudit.appCapacityMonitor.curMem) < 60)>\n                                        <span style=\"color:orange\">\n                                            ${memUsedRatio} %\n                                        </span>\n                                <#else>\n                                        <span style=\"color:lightgray\">\n                                            ${memUsedRatio} %\n                                        </span>\n                                </#if>\n                            </td>\n\n                            <td>\n                                <#assign shardingMemUsed = (appAudit.appCapacityMonitor.shardingMemUsed / 1024 / 1024 / 1024)?string(\"0.00\")>\n                                ${shardingMemUsed}G\n                            </td>\n\n                            <td>\n                                <#assign shardingMemUsedRatio = (appAudit.appCapacityMonitor.shardingMemUsed * 100 / appAudit.appCapacityMonitor.curShardingMem)?string(\"0.00\")>\n                                <#if ((appAudit.appCapacityMonitor.shardingMemUsed * 100 / appAudit.appCapacityMonitor.curShardingMem) >= appAudit.appCapacityMonitor.expandMemPercent)>\n                                        <span style=\"color:red\">\n                                            ${shardingMemUsedRatio} %\n                                        </span>\n                                <#elseif (((appAudit.appCapacityMonitor.shardingMemUsed * 100 / appAudit.appCapacityMonitor.curShardingMem) < appAudit.appCapacityMonitor.expandMemPercent) && ((appAudit.appCapacityMonitor.shardingMemUsed * 100 / appAudit.appCapacityMonitor.curShardingMem) >= 60))>\n                                        <span style=\"color:green\">\n                                            ${shardingMemUsedRatio} %\n                                        </span>\n                                <#elseif ((appAudit.appCapacityMonitor.shardingMemUsed * 100 / appAudit.appCapacityMonitor.curShardingMem) < 60)>\n                                        <span style=\"color:orange\">\n                                            ${shardingMemUsedRatio} %\n                                        </span>\n                                <#else>\n                                        <span style=\"color:lightgray\">\n                                            ${shardingMemUsedRatio} %\n                                        </span>\n                                </#if>\n                            </td>\n\n                            <td>\n                                <#if appAudit.appCapacityMonitor.isExpand?? && (appAudit.appCapacityMonitor.isExpand == 1)>\n                                    <span style=\"color:green\">是</span>\n                                <#else>\n                                    <span style=\"color:red\">否</span>\n                                </#if>\n                            </td>\n\n                            <td>\n                                ${appAudit.appCapacityMonitor.expandMemPercent}\n                            </td>\n\n                            <td>\n                                ${appAudit.appCapacityMonitor.expandRatio}\n                            </td>\n\n                            <td>\n                                ${appAudit.appCapacityMonitor.expandRatioTotal}\n                            </td>\n\n                            <td>\n                                <#if appAudit.appCapacityMonitor.expandTime??>\n                                    ${appAudit.appCapacityMonitor.expandTime?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                </#if>\n                            </td>\n\n                            <td>\n                                <#if appAudit.appCapacityMonitor.isReduce?? && (appAudit.appCapacityMonitor.isReduce == 1)>\n                                    <span style=\"color:green\">是</span>\n                                <#else>\n                                    <span style=\"color:red\">否</span>\n                                </#if>\n                            </td>\n\n                            <td>\n                                <#if appAudit.appCapacityMonitor.scheduleTime??>\n                                   ${appAudit.appCapacityMonitor.scheduleTime?string(\"yyyy-MM-dd\")}\n                                </#if>\n                            </td>\n                            <td>\n                                <button type=\"button\" class=\"btn btn-info btn-sm\" data-bs-target=\"#changeAppCapacityMonitorModal${appAudit.appCapacityMonitor.id}\" data-bs-toggle=\"modal\" href=\"#\">修改</button>\n                            </td>\n                        </tr>\n                    </#list>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n    </div>\n</div>\n\n<#list appCapacityAuditList! as appAudit>\n    <div id=\"changeAppCapacityMonitorModal${appAudit.appCapacityMonitor.id}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n        <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n\n                <div class=\"modal-header\">\n                    <h4 class=\"modal-title\">修改应用内存审计——自动扩缩容配置</h4>\n                    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n                </div>\n\n                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                    <div class=\"modal-body\">\n                        <div class=\"row\">\n                            <!-- 控件开始 -->\n                            <div class=\"col-md-12\">\n                                <!-- form-body开始 -->\n                                <div class=\"form-body offset-md-1\">\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3 text-end\">\n                                            是否可扩容:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <select name=\"isExpand${appAudit.appCapacityMonitor.id}\" id=\"isExpand${appAudit.appCapacityMonitor.id}\" class=\"form-select w-100\">\n                                                <option value=\"0\" <#if !(appAudit.appCapacityMonitor.isExpand??) || (appAudit.appCapacityMonitor.isExpand == 0)>selected</#if>>否</option>\n                                                <option value=\"1\" <#if (appAudit.appCapacityMonitor.isExpand == 1)>selected</#if>>是</option>\n                                            </select>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3  text-end\">\n                                            扩容判定内存使用率:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <input type=\"text\" name=\"expandMemPercent${appAudit.appCapacityMonitor.id}\" id=\"expandMemPercent${appAudit.appCapacityMonitor.id}\" value=\"${appAudit.appCapacityMonitor.expandMemPercent}\"\n                                                   class=\"form-control\" />\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3  text-end\">\n                                            每次扩容比率:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <input type=\"text\" name=\"expandRatio${appAudit.appCapacityMonitor.id}\" id=\"expandRatio${appAudit.appCapacityMonitor.id}\" value=\"${appAudit.appCapacityMonitor.expandRatio}\"\n                                                   class=\"form-control\" />\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3  text-end\">\n                                            总扩容比率:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <input type=\"text\" name=\"expandRatioTotal${appAudit.appCapacityMonitor.id}\" id=\"expandRatioTotal${appAudit.appCapacityMonitor.id}\" value=\"${appAudit.appCapacityMonitor.expandRatioTotal}\"\n                                                   class=\"form-control\" />\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3  text-end\">\n                                            是否可缩容:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <select name=\"isReduce${appAudit.appCapacityMonitor.id}\" id=\"isReduce${appAudit.appCapacityMonitor.id}\" class=\"form-select w-100\" onchange=\"dealScheduleTime('${appAudit.appCapacityMonitor.id}')\">\n                                                <option value=\"0\" <#if !(appAudit.appCapacityMonitor.isReduce??) || (appAudit.appCapacityMonitor.isReduce == 0)>selected</#if>>否</option>\n                                                <option value=\"1\" <#if (appAudit.appCapacityMonitor.isReduce == 1)>selected</#if>>是</option>\n                                            </select>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3  text-end\">\n                                            预计缩容时间:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <#if appAudit.appCapacityMonitor.scheduleTime??>\n                                                <#assign scheduleTime = appAudit.appCapacityMonitor.scheduleTime?string(\"yyyy-MM-dd\")>\n                                            <#else>\n                                                <#assign scheduleTime = \"\">\n                                            </#if>\n                                            <input type=\"date\" name=\"scheduleTime${appAudit.appCapacityMonitor.id}\" id=\"scheduleTime${appAudit.appCapacityMonitor.id}\" value=\"${scheduleTime}\"\n                                                   class=\"form-control\" />\n                                        </div>\n                                    </div>\n\n                                </div>\n                                <!-- form-body 结束 -->\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"modal-footer\">\n                        <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                        <button type=\"button\" id=\"updateCapacityMonitorBtn${appAudit.appCapacityMonitor.id}\" class=\"btn btn-danger\" onclick=\"updateAppCapacityMonitor('${appAudit.appCapacityMonitor.id}')\">Ok</button>\n                    </div>\n                </form>\n            </div>\n        </div>\n    </div>\n</#list>\n\n<div id=\"autoFixTopologyModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">自动修复拓扑-仅修复主节点分布不均场景</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-2 text-end\">\n                                        appId:\n                                    </label>\n                                    <div class=\"col-md-8\">\n                                        <input type=\"text\" name=\"ip\" id=\"fixTopologyAppIds\"\n                                               placeholder=\"不填，修复所有；多个应用id,用,分隔\" class=\"form-control\" />\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                        <div id=\"autoFixTopologyInfo\"></div>\n                        <!-- 控件结束 -->\n                    </div>\n                </div>\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"autoFixTopologyModalBtn\" class=\"btn btn-danger\" onclick=\"autoFixTopology()\">Ok</button>\n                </div>\n\n            </form>\n        </div>\n    </div>\n</div>\n\n<script type=\"text/javascript\">\n    $(function () {\n        var tabVal = $('#tabId').val();\n        var searchDate;\n        if(tabVal != 6){\n            searchDate = $('#searchDate').val();\n            if (searchDate == null || searchDate == '') {\n                var time = new Date();\n                var day = (\"0\" + time.getDate()).slice(-2);\n                var month = (\"0\" + (time.getMonth() + 1)).slice(-2);\n                var today = time.getFullYear() + \"-\" + (month) + \"-\" + (day);\n                $('#searchDate').val(today);\n            }\n        }\n        if (tabVal == 1) {\n            $('#memIndex').removeAttr(\"hidden\");\n            $('#fragRatioIndex').attr(\"hidden\", \"hidden\");\n            $('#appTopologyIndex').attr(\"hidden\", \"hidden\");\n            $('#containerIndex').attr(\"hidden\", \"hidden\");\n            $('#machineIndex').attr(\"hidden\", \"hidden\");\n            $('#capacityAuditIndex').attr(\"hidden\", \"hidden\");\n        } else if (tabVal == 2) {\n            $('#memIndex').attr(\"hidden\", \"hidden\");\n            $('#fragRatioIndex').removeAttr(\"hidden\");\n            $('#appTopologyIndex').attr(\"hidden\", \"hidden\");\n            $('#containerIndex').attr(\"hidden\", \"hidden\");\n            $('#machineIndex').attr(\"hidden\", \"hidden\");\n            $('#capacityAuditIndex').attr(\"hidden\", \"hidden\");\n        } else if (tabVal == 3) {\n            $('#memIndex').attr(\"hidden\", \"hidden\");\n            $('#fragRatioIndex').attr(\"hidden\", \"hidden\");\n            $('#appTopologyIndex').removeAttr(\"hidden\");\n            $('#containerIndex').attr(\"hidden\", \"hidden\");\n            $('#machineIndex').attr(\"hidden\", \"hidden\");\n            $('#capacityAuditIndex').attr(\"hidden\", \"hidden\");\n        } else if (tabVal == 4) {\n            $('#memIndex').attr(\"hidden\", \"hidden\");\n            $('#fragRatioIndex').attr(\"hidden\", \"hidden\");\n            $('#appTopologyIndex').attr(\"hidden\", \"hidden\");\n            $('#containerIndex').removeAttr(\"hidden\");\n            $('#machineIndex').attr(\"hidden\", \"hidden\");\n            $('#capacityAuditIndex').attr(\"hidden\", \"hidden\");\n        } else if (tabVal == 5) {\n            $('#memIndex').attr(\"hidden\", \"hidden\");\n            $('#fragRatioIndex').attr(\"hidden\", \"hidden\");\n            $('#appTopologyIndex').attr(\"hidden\", \"hidden\");\n            $('#containerIndex').attr(\"hidden\", \"hidden\");\n            $('#machineIndex').removeAttr(\"hidden\");\n            $('#capacityAuditIndex').attr(\"hidden\", \"hidden\");\n        }else if (tabVal == 6) {\n            $('#memIndex').attr(\"hidden\", \"hidden\");\n            $('#fragRatioIndex').attr(\"hidden\", \"hidden\");\n            $('#appTopologyIndex').attr(\"hidden\", \"hidden\");\n            $('#containerIndex').attr(\"hidden\", \"hidden\");\n            $('#machineIndex').attr(\"hidden\", \"hidden\");\n            $('#capacityAuditIndex').removeAttr(\"hidden\");\n        }\n\n    })\n\n    //验证是数字\n    function testisNum(id) {\n        var value = document.getElementById(id).value;\n        if (value != \"\" && isNaN(value)) {\n            alert(\"请输入数字类型!\");\n            document.getElementById(id).value = \"\";\n            document.getElementById(id).focus();\n        }\n    }\n\n    function sendExpAppsStatDataEmail() {\n        var searchDate = document.getElementById(\"searchDate\").value;\n        $.get('${request.contextPath}/manage/app/tool/sendExpAppsStatDataEmail', {searchDate: searchDate});\n        alert(\"异常应用日报已发送，请查收\")\n    }\n\n    function topologyUpdate() {\n        if(confirm(\"确认提交拓扑检查更新？确认后请等待，更新完毕页面会自动刷新\")){\n            $.get('${request.contextPath}/manage/app/stat/topologyUpdate.json', function (result) {\n                if (result.status == 1) {\n                    location.href = \"${request.contextPath}/manage/app/stat/list/server?tabId=3\";\n                }else {\n                    alert(\"拓扑检测更新失败！\")\n                }\n            });\n        }\n\n    }\n\n    function dealScheduleTime(id) {\n        var isReduce = $(\"#isReduce\" + id);\n        alert(isReduce.val());\n        if(isReduce.val() == 0){\n            document.getElementById(\"scheduleTime\" + id).value = '';\n        }\n    }\n\n    function updateAppCapacityMonitor(id) {\n\n        var expandMemPercent = document.getElementById(\"expandMemPercent\" + id);\n        var expandRatio = document.getElementById(\"expandRatio\" + id);\n        var expandRatioTotal = document.getElementById(\"expandRatioTotal\" + id);\n        var isExpand = $(\"#isExpand\" + id);\n        var isReduce = $(\"#isReduce\" + id);\n        var scheduleTime = document.getElementById(\"scheduleTime\" + id);\n\n        if (expandMemPercent.value == \"\") {\n            alert(\"内存使用率不能为空\");\n            memAlertValue.focus();\n            return false;\n        }\n        if (expandRatio.value == \"\") {\n            alert(\"扩容比率不能为空\");\n            clientConnAlertValue.focus();\n            return false;\n        }\n        if (expandRatioTotal.value == \"\") {\n            alert(\"总扩容比率不能为空\");\n            hitPrecentAlertValue.focus();\n            return false;\n        }\n        if(!confirm(\"确认提交内存审计修改？\")){\n            return ;\n        }\n        var btn = document.getElementById(\"updateCapacityMonitorBtn\" + id);\n        btn.disabled = true;\n        $.post(\n            '${request.contextPath}/manage/app/updateAppCapacityMonitor',\n            {\n                id: id,\n                expandMemPercent: expandMemPercent.value,\n                expandRatio: expandRatio.value,\n                expandRatioTotal: expandRatioTotal.value,\n                isExpand: isExpand.val(),\n                isReduce: isReduce.val(),\n                scheduleTime: scheduleTime.value\n            },\n            function (data) {\n                if (data == 1) {\n                    alert(\"修改成功\");\n                    setTimeout(\"location.href = '${request.contextPath}/manage/app/stat/list/server?tabId=6'\", 1000);\n                } else {\n                    btn.disabled = false;\n                    alert(\"修改失败，请确认\");\n                }\n            }\n        );\n    }\n\n    function autoFixTopology() {\n        var fixTopologyAppIds = document.getElementById(\"fixTopologyAppIds\");\n        var btn = document.getElementById(\"autoFixTopologyModalBtn\");\n        btn.disabled = true;\n        $.post(\n            '${request.contextPath}/manage/app/autoFixTopology.json',\n            {\n                appIds: fixTopologyAppIds.value,\n            },\n            function (data) {\n                if (data.result) {\n                    $(\"#autoFixTopologyInfo\").html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Success!</strong>创建任务\" + data.appCount + \"个，涉及应用：\" + data.appIds + \"</div>\");\n                    var targetId = \"#autoFixTopologyModal\";\n                } else {\n                    btn.disabled = false;\n                    $(\"#autoFixTopologyInfo\").html(\"<div class='alert alert-error' ><button class='close' data-dismiss='alert'>×</button><strong>Error!</strong>任务创建失败！</div>\");\n                }\n            }\n        );\n    }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appStat/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"/manage/appStat/appStatList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n\n<script>\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n\n        if (!jQuery().dataTable) {\n          return;\n        }\n\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 20,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        // jQuery('#tableDataList_wrapper>div:first-child').css(\"display\",\"none\");\n      }\n    };\n  }();\n\n  $(function() {\n    TableManaged.init();\n  });\n</script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appStat/listServer.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"/manage/appStat/appStatListServer.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n\n<script>\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n\n        if (!jQuery().dataTable) {\n          return;\n        }\n\n        $(\"table[name='tableDataList']\").dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 20,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n\n          }\n        });\n\n        jQuery('#tableDataList_wrapper>div:first-child').css(\"display\",\"none\");\n      }\n    };\n  }();\n\n  $(function() {\n    TableManaged.init();\n  });\n</script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/appTool/appKeysDealTool.html",
    "content": "<div class=\"row col-12\">\n  <div class=\"card\">\n    <div class=\"card-header pt-3\">\n      <h4 class=\"card-title\">\n        拓扑问题诊断\n        <#if checkInfo?? && (!(checkInfo.tips??) || (checkInfo.tips?size == 0))>\n          <a class=\"btn btn-success\">正常</a>\n        <#else>\n          <a class=\"btn btn-danger\">异常</a>\n        </#if>\n      </h4>\n    </div>\n    <div class=\"card-body table-responsive\">\n      <h5 style=\"font:bold;color:green;\">\n        应用ID:${checkInfo.appDesc.appId!}  &nbsp;&nbsp;&nbsp; 应用名:${checkInfo.appDesc.name!} &nbsp;&nbsp;&nbsp; 应用类型:${checkInfo.appDesc.typeDesc!}\n      </h5>\n      <table class=\"table table-bordered table-striped table-hover\">\n        <thead>\n        <tr>\n          <th>序号</th>\n          <th>诊断问题</th>\n          <th>应用信息</th>\n\n        </tr>\n        </thead>\n        <tbody>\n        <#assign index = \"0\">\n        <#if (checkInfo.tips?size == 0)>\n          <tr>\n            <td colspan=\"3\" align=\"center\">无</td>\n          </tr>\n        </#if>\n        <#if (checkInfo.tips?size > 0)>\n          <#list checkInfo.tips as tip>\n            <tr>\n              <td>\n                <#assign index = (tip_index + 1)/>\n                ${index!}\n              </td>\n              <td>\n                ${tip.status!}\n              </td>\n              <td>\n                ${tip.desc!}\n              </td>\n            </tr>\n          </#list>\n        </#if>\n        </tbody>\n      </table>\n    </div>\n  </div>\n\n  <div class=\"card\">\n    <div class=\"card-body table-responsive\">\n      <#if (checkInfo.appDesc.type==2) || (checkInfo.appDesc.type==5)>\n\n        <div class=\"row\">\n          <div class=\"col-md-12\">\n            <h3 class=\"page-header\">\n              诊断1:同网段分析\n              <#if checkInfo?? && checkInfo.sameNetSegment?? && !(checkInfo.sameNetSegment)>\n                <a class=\"btn btn-danger\">异常</a>\n              <#else>\n                <a class=\"btn btn-success\">正常</a>\n              </#if>\n            </h3>\n          </div>\n        </div>\n\n        <div class=\"row\">\n          <div class=\"col-md-12\">\n            <h3 class=\"page-header\">\n              诊断2:主从节点分析\n              <#if checkInfo?? && checkInfo.master_slaves?? && checkInfo.slaveNum?? && checkInfo.msFlag?? &&\n              (checkInfo.master_slaves?size == checkInfo.slaveNum) && !(checkInfo.msFlag)>\n                <a class=\"btn btn-success\">正常</a>\n              <#else>\n                <a class=\"btn btn-danger\">异常</a>\n              </#if>\n            </h3>\n          </div>\n        </div>\n\n        <div class=\"col-md-12\">\n          <div>\n            <#if (checkInfo.appDesc.type==5)>sentinel节点数量：${checkInfo.sentinels?size}</#if>\n            <#if checkInfo.master_slaves??>\n              <#assign masterSlavesSize = checkInfo.master_slaves?size>\n            <#else>\n              <#assign masterSlavesSize = 0>\n            </#if>\n            主节点数量：${masterSlavesSize} &nbsp;\n            从节点数量：${checkInfo.slaveNum!}  &nbsp;<br/>\n            <#if checkInfo.msFlag?? && (checkInfo.msFlag)><span style=\"color:red;font:bold\">【异常诊断】：主从节点有分布同一台物理机</span><br/></#if>\n            <#if checkInfo.master_slaves?? && (checkInfo.master_slaves?size != checkInfo.slaveNum)><span style=\"color:red;font:bold\">【异常诊断】：主从节点数量不一致</span><br/></#if>\n            <#if (checkInfo.appDesc.type==5) && (checkInfo.sentinels?size < 3)><span style=\"color:red;font:bold\">【异常诊断】：sentinel节点数量少于3个</span><br/></#if>\n          </div>\n          <#if (checkInfo.appDesc.type==5)>\n            ${instanceInfoMap!}\n            <table class=\"table table-striped table-bordered table-hover\">\n              <thead>\n              <tr>\n                <th width=\"20px\">序号</th>\n                <th width=\"40px\">sentinel实例</th>\n              </tr>\n              </thead>\n              <tbody>\n              <#assign index = \"0\">\n              <#list checkInfo.sentinels as sentinel>\n                <tr>\n                  <td width=\"20px\">\n                    <#assign index = (sentinel_index + 1)>\n                    ${index!}\n                  </td>\n                  <td width=\"40px\">\n                    ${sentinel.ip!}:${sentinel.port!}\n                  </td>\n                </tr>\n              </#list>\n              </tbody>\n            </table>\n          </#if>\n\n          <table class=\"table table-striped table-bordered table-hover\">\n            <thead>\n            <tr>\n              <th>master</th>\n              <th>slave</th>\n              <th>是否同一物理机</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#if checkInfo.master_slaves??>\n              <#list checkInfo.master_slaves?keys as master>\n                <tr>\n                  <td>\n                    ${master.ip!}:${master.port!}\n                  </td>\n                  <td>\n                    <#list checkInfo.master_slaves?api.get(master) as slave>\n                      ${slave.ip!}:${slave.port!} <br/>\n                    </#list>\n                  </td>\n                  <td>\n                    <#if (checkInfo.master_slaves?api.get(master)?size > 0)>\n                    <#list checkInfo.master_slaves?api.get(master) as slave>\n                      <#if slave_index == 0>\n                        <#assign checkSameMachineIpValue = slave.ip>\n                      </#if>\n                      <#break>\n                    </#list>\n\n                      <#if (checkInfo.instanceInfoMap?api.get(master.ip).realIp == checkInfo.instanceInfoMap?api.get(checkSameMachineIpValue!).realIp)><a style=\"color:red\">是</a></#if>\n                      <#if (checkInfo.instanceInfoMap?api.get(master.ip).realIp != checkInfo.instanceInfoMap?api.get(checkSameMachineIpValue!).realIp)>否</#if>\n                    </#if>\n                    <#if (checkInfo.master_slaves?api.get(master)?size == 0)> <a style=\"color:red\">无slave</a> </#if>\n                  </td>\n                </tr>\n              </#list>\n            </#if>\n            </tbody>\n          </table>\n\n        </div>\n\n        <!--  机器分布 -->\n        <div class=\"row\">\n          <div class=\"col-md-12\">\n            <h3 class=\"page-header\">\n              诊断3:物理机/机架分布\n              <#if checkInfo.machineInfoMap?? && (checkInfo.machineInfoMap?size >= 3) && ((!(checkInfo.failoverStatus??)) || checkInfo.failoverStatus)>\n                <a class=\"btn btn-success\">正常</a>\n              <#else>\n                <a class=\"btn btn-danger\">异常</a>\n              </#if>\n            </h3>\n          </div>\n        </div>\n\n        <div class=\"col-md-12\">\n          <div>\n            <#if checkInfo.machineInfoMap??>\n              <#assign machineInfoMapSize = checkInfo.machineInfoMap?size>\n            <#else>\n              <#assign machineInfoMapSize = 0>\n            </#if>\n            物理机节点数：${machineInfoMapSize} &nbsp;<br/>\n            <#if checkInfo.machineInfoMap?? && (checkInfo.machineInfoMap?size < 3)>\n              <span style=\"color:red;font:bold;\">【异常诊断】：物理机分布数量少于3组</span><br/>\n            </#if>\n            <#if checkInfo.failoverStatus?? && !(checkInfo.failoverStatus)>\n              <span style=\"color:red;font:bold;\">【异常诊断】：主节点分布少于3台物理机,其中一台物理机宕机不满足故障转移条件</span>\n            </#if>\n          </div>\n          <table class=\"table table-striped table-bordered table-hover\">\n            <thead>\n            <tr>\n              <th>宿主机</th>\n              <th>机房/机架信息</th>\n              <th>实例信息</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#if checkInfo?? && checkInfo.machineInstancesMap??>\n              <#list checkInfo.machineInstancesMap?keys as key>\n                <tr>\n                  <td>${key!}</td>\n                  <td>${checkInfo.machineInfoMap?api.get(key).room!}:${checkInfo.machineInfoMap?api.get(key).rack!}</td>\n                  <td>\n                    <#list checkInfo.machineInstancesMap[key] as instance>\n                      ${instance.ip!}:${instance.port!} (${instance.roleDesc!})<br/>\n                    </#list>\n                  </td>\n                </tr>\n              </#list>\n            </#if>\n            </tbody>\n          </table>\n        </div>\n      </#if>\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n    $(window).on('load', function () {\n        $('.selectpicker').selectpicker({\n            'selectedText': 'cat'\n        });\n    });\n    $(function(){\n        $('#searchType_bigKey').change(function () {\n            if($(this).val()==1)\n            {\n                $(\"#instanceInfo\").show();\n            }else{\n                $(\"#instanceInfo\").hide();\n            }\n        });\n        $('#searchType_idleKey').change(function () {\n            if($(this).val()==1)\n            {\n                $(\"#instanceInfo_idle\").show();\n            }else{\n                $(\"#instanceInfo_idle\").hide();\n            }\n        });\n        $('#searchType_delKey').change(function () {\n            if($(this).val()==1)\n            {\n                $(\"#instanceInfo_del\").show();\n            }else{\n                $(\"#instanceInfo_del\").hide();\n            }\n        });\n    });\n\n    var appId=$('#appId').val();\n    function findInstancePatternKeys() {\n        var flag=1;\n        var instance=document.getElementById('inst_scanKey');\n        var pattern=document.getElementById('pattern_scanKey');\n        var scanKeyListText=document.getElementById('scanKeyList');\n        scanKeyListText.value='';\n\n        var note_inst_scanKey=document.getElementById('note_inst_scanKey');\n        var note_pattern_scanKey=document.getElementById('note_pattern_scanKey');\n        note_inst_scanKey.style.display='none';\n        note_pattern_scanKey.style.display='none';\n\n        if(instance.value==''){\n            note_inst_scanKey.style.display='';\n            flag=0;\n        }\n        if(pattern.value==''){\n            note_pattern_scanKey.style.display='';\n            flag=0;\n        }\n        if(flag==0) return;\n        //清除上次记录\n        $('#scanKeyList').empty();\n        var ip=instance.value.split(':')[0];\n        var port=instance.value.split(':')[1];\n        $.post(\n            '${request.contextPath}/manage/app/tool/findInstancePatternKeys.json',\n            {\n                appId: appId,\n                ip: ip,\n                port: port,\n                pattern: pattern.value\n            },\n            function (data) {\n                var instancePatternKeyList=data.instancePatternKeyList;\n                if(instancePatternKeyList==null||instancePatternKeyList.length==0){\n                    alert(\"无满足条件的key!\");\n                }else {\n                    instancePatternKeyList.forEach(function (key) {\n                        scanKeyListText.value=scanKeyListText.value+key+\"\\n\";\n                    })\n                }\n            }\n        );\n\n    }\n    function findBigKey() {\n        var searchType_bigKey=$('#searchType_bigKey').val();\n        var startBytes=document.getElementById('startBytes');\n        var endBytes=document.getElementById('endBytes');\n        var bigKeyListText=document.getElementById('bigKeyList');\n        bigKeyListText.value='';\n\n        var note_endBytes=document.getElementById('note_endBytes');\n        if(startBytes.value==''||endBytes.value==''){\n            note_endBytes.style.display='';\n            return;\n        }\n        if(searchType_bigKey==1){\n            var instance=document.getElementById('inst_bigKey');\n            var ip=instance.value.split(':')[0];\n            var port=instance.value.split(':')[1];\n            $.post(\n                '${request.contextPath}/manage/app/tool/findInstanceBigKey.json',\n                {\n                    appId: appId,\n                    ip: ip,\n                    port: port,\n                    startBytes: startBytes.value,\n                    endBytes: endBytes.value\n                },\n                function (data) {\n                    var instanceBigKeyList=data.instanceBigKeyList;\n                    if(instanceBigKeyList==null||instanceBigKeyList.length==0){\n                        alert(\"无满足条件的key!\");\n                    }else {\n                        instanceBigKeyList.forEach(function (key) {\n                            bigKeyListText.value=bigKeyListText.value+key+\"\\n\";\n                        })\n                    }\n                }\n            );\n        }else if(searchType_bigKey==0){\n            $.post(\n                '${request.contextPath}/manage/app/tool/findClusterBigKey.json',\n                {\n                    appId: appId,\n                    startBytes: startBytes.value,\n                    endBytes: endBytes.value\n                },\n                function (data) {\n                    var clusterBigKeyList=data.clusterBigKeyList;\n                    if(clusterBigKeyList==null||clusterBigKeyList.length==0){\n                        alert(\"无满足条件的key!\");\n                    }else {\n                        clusterBigKeyList.forEach(function (key) {\n                            bigKeyListText.value=bigKeyListText.value+key+\"\\n\";\n                        })\n                    }\n                }\n            );\n        }\n    }\n    function findIdleKey() {\n        var searchType_idleKey=$('#searchType_idleKey').val();\n        var idleDays=document.getElementById('idleDays');\n        var idleKeyList=document.getElementById('idleKeyList');\n        idleKeyList.value='';\n\n        var note_idleDays=document.getElementById('note_idleDays');\n        if(idleDays.value==''){\n            note_idleDays.style.display='';\n            return;\n        }\n        if(searchType_idleKey==1){\n            var instance=document.getElementById('inst_idleKey');\n            var ip=instance.value.split(':')[0];\n            var port=instance.value.split(':')[1];\n            $.post(\n                '${request.contextPath}/manage/app/tool/findInstanceIdleKeys.json',\n                {\n                    appId: appId,\n                    ip: ip,\n                    port: port,\n                    idleDays: idleDays.value\n                },\n                function (data) {\n                    var instanceIdleKeyList=data.instanceIdleKeyList;\n                    if(instanceIdleKeyList==null||instanceIdleKeyList.length==0){\n                        alert(\"无满足条件的key!\");\n                    }else {\n                        instanceIdleKeyList.forEach(function (key) {\n                            idleKeyList.value=idleKeyList.value+key+\"\\n\";\n                        })\n                    }\n                }\n            );\n        }else if(searchType_idleKey==0){\n            $.post(\n                '${request.contextPath}/manage/app/tool/findClusterIdleKeys.json',\n                {\n                    appId: appId,\n                    idleDays: idleDays.value\n                },\n                function (data) {\n                    var clusterIdleKeyList=data.clusterIdleKeyList;\n                    if(clusterIdleKeyList==null||clusterIdleKeyList.length==0){\n                        alert(\"无满足条件的key!\");\n                    }else {\n                        clusterIdleKeyList.forEach(function (key) {\n                            idleKeyList.value=idleKeyList.value+key+\"\\n\";\n                        })\n                    }\n                }\n            );\n        }\n    }\n    function deleteKey() {\n        var searchType_delKey=$('#searchType_delKey').val();\n        var pattern=document.getElementById('pattern_delKey');\n        var note_pattern=document.getElementById('note_pattern_delKey');\n        if(pattern.value==''){\n            note_pattern.style.display='';\n            return;\n        }\n\n        if(searchType_delKey==1){\n            var instance=document.getElementById('inst_delKey');\n            var ip=instance.value.split(':')[0];\n            var port=instance.value.split(':')[1];\n            $.post(\n                '${request.contextPath}/manage/app/tool/delInstancePatternKeys.json',\n                {\n                    appId: appId,\n                    ip: ip,\n                    port: port,\n                    pattern: pattern.value\n                },\n                function(data){\n                    var result=data.result;\n                    if(result==1){\n                        alert(\"匹配\"+pattern.value+\"的keys删除成功\");\n                    }else {\n                        alert(\"匹配\"+pattern.value+\"的keys删除失败\");\n                    }\n                }\n            );\n\n        }else if(searchType_delKey==0){\n            $.post(\n                '${request.contextPath}/manage/app/tool/delClusterPatternKey.json',\n                {\n                    appId: appId,\n                    pattern: pattern.value\n                },\n                function(data){\n                    var result=data.result;\n                    if(result==1){\n                        alert(\"匹配\"+pattern.value+\"的keys删除成功\");\n                    }else {\n                        alert(\"匹配\"+pattern.value+\"的keys删除失败\");\n                    }\n                }\n            );\n        }\n    }\n    function appTopologyExam(){\n\n        $.post(\n            '${request.contextPath}/manage/tool/topologyExam.json',\n            {\n                appId: appId,\n                examType: 3\n            },\n            function(data){\n                var taskId=data.taskId;\n                if(taskId>0){\n                    alert(\"应用检查任务提交成功，任务ID：\"+taskId+\"，请查看结果邮件！\");\n                }else {\n                    alert(\"应用检查任务提交失败！\");\n                }\n            }\n        );\n    }\n</script>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/backendTemplate.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <#include \"inc/include.html\">\n        <#include \"inc/meta.html\">\n    </head>\n    <body>\n        <#include \"inc/header.html\">\n        <main>\n            <#include \"index/column.html\">\n            <div id=\"cover\"></div>\n            <div class=\"container\" id=\"columnContent\">\n                <#include \"${view}.html\">\n            </div>\n        </main>\n        <#include \"inc/footer.html\">\n    </body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/config/init.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"initConfigDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/config/initConfigDetail.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                    <i class=\"bi bi-globe\"></i>配置修改\n                    <#if success?? && (success == \"1\")>\n                    <font color=\"red\">更新成功</font>\n                    <#elseif success?? && (success == \"0\")>\n                    <font color=\"red\">更新失败</font>\n                </#if>\n                </h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <div class=\"col-md-12\">\n                        <!-- BEGIN FORM-->\n                        <form action=\"${request.contextPath}/manage/config/update\" method=\"post\" class=\"form-horizontal bordered form-row-stripped\">\n                            <div class=\"form-body\">\n                                <#list configList as config>\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label offset-md-1 col-md-4 text-end\">\n                                            ${config.info}<font color='red'>(*)</font>:\n                                        </label>\n                                        <div class=\"col-md-6\">\n                                            <#if (config.configKey == 'cachecloud.whether.schedule.clean.data')>\n                                                <select name=\"${config.configKey}\" class=\"form-control\">\n                                                    <option value=\"false\" <#if (config.configValue == 'false')>selected</#if>>\n                                                    否\n                                                    </option>\n                                                    <option value=\"true\" <#if (config.configValue == 'true')>selected</#if>>\n                                                    是\n                                                    </option>\n                                                </select>\n                                            <#elseif (config.configKey == 'cachecloud.ssh.auth.type')>\n                                                <select name=\"${config.configKey}\" class=\"form-control\">\n                                                    <option value=\"1\" <#if (config.configValue == '1')>selected</#if>>\n                                                    密码\n                                                    </option>\n                                                    <option value=\"2\" <#if (config.configValue == '2')>selected</#if>>\n                                                    public key\n                                                    </option>\n                                                </select>\n                                            <#else>\n                                                <input type=\"text\" name=\"${config.configKey!}\" class=\"form-control\" value='${config.configValue!}' />\n                                            </#if>\n                                        </div>\n                                    </div>\n                                </#list>\n\n                                <div class=\"form-actions fluid\">\n                                    <div class=\"form-group row\">\n                                        <div class=\"col-md-12\">\n                                            <div class=\"col-md-12 text-center\">\n                                                <button type=\"submit\" class=\"btn btn-success\">\n                                                    <i class=\"bi bi-check\"></i>\n                                                    确认修改\n                                                </button>\n                                            </div>\n                                        </div>\n                                    </div>\n                                </div>\n                            </div>\n                        </form>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticDelKey.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#delkey_tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        $('#delkey_tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>所有主节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function submitDiagnostic(type) {\n    var appId;\n    var nodes;\n    var params = [];\n    if (type == 4) {\n      appId = $('#delkey-select').selectpicker('val');\n      if (appId == null || appId == '') {\n        alert(\"请选择应用\");\n        return;\n      }\n      nodes = $('#delkey_instance-select').selectpicker('val');\n\n      params.push($('#delkey-pattern').val());\n    }\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: type,\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              params: params.toString()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=deleteKey\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n\n  function submitSampleScan() {\n    document.getElementById('checkSampleScan').style.display = 'none';\n    document.getElementById('submitDiagnosticBtn').disabled = true;\n\n    $('#sampleResultCount').html('');\n    $('#sampleResultTable').html('');\n    var appId = $('#delkey-select').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      return;\n    }\n\n    var nodes = $('#delkey_instance-select').selectpicker('val');\n\n    var pattern = $('#delkey-pattern').val();\n\n\n    document.getElementById('checkSampleScan').style.display = 'inline';\n    $('#checkSampleScan').html('采样进行中，请等待...');\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/sampleScan.json',\n            {\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              pattern: pattern\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"采样校验完成，请确认校验结果后提交删除任务！\");\n                $('#checkSampleScan').html('查看采样结果');\n                document.getElementById('submitDiagnosticBtn').disabled = false;\n\n                $('#sampleResultCount').append(\n                        '<tr>' +\n                        '<td>key (共计' + data.count + '个）</td>' +\n                        '</tr>'\n                );\n                var diagnosticResultList = data.result;\n                diagnosticResultList.forEach(function (diagnosticResult, index) {\n                  $('#sampleResultTable').append(\n                          '<tr>' +\n                          '<td>' + diagnosticResult + '</td>' +\n                          '</tr>'\n                  );\n                })\n              } else {\n                toastr.error(\"采样校验失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n      <div class=\"col-md-3\">\n        <select id=\"delkey-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                data-live-search=\"true\" title=\"选择应用\"\n                onchange=\"changeAppIdSelect(this.value,'delkey_instance-select')\">\n          <option value=\"\">选择应用</option>\n          <#list appDescMap?keys as key>\n            <#assign appDesc = appDescMap?api.get(key)>\n            <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n              【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n            </option>\n          </#list>\n        </select>\n      </div>\n      <div class=\"col-md-3\">\n        <select id=\"delkey_instance-select\" name=\"nodes\"\n                class=\"selectpicker w-100 border rounded\" multiple\n                data-live-search=\"true\" title=\"选择实例\">\n        </select>\n      </div>\n      <div class=\"col-md-2\">\n        <input id=\"delkey-pattern\" type=\"text\" class=\"form-control\" name=\"pattern\" placeholder=\"匹配模式 pattern，多个用英文逗号分割\">\n      </div>\n\n      <div class=\"col-auto\">\n        <button id=\"submitDiagnosticBtn\" type=\"button\" class=\"btn btn-danger\" disabled=\"disabled\" onclick=\"submitDiagnostic(4)\">提交删除</button>\n      </div>\n\n\n      <div class=\"col-auto\" style=\"float:right;\">\n        <button type=\"button\" class=\"btn btn-warning\" onclick=\"submitSampleScan()\">采样校验</button>\n        <a id=\"checkSampleScan\" style=\"display: none\" data-bs-target=\"#modal-sampleResult\" data-bs-toggle=\"modal\" data-title=\"应用${app_id!}\">采样进行中，请等待...</a>\n        <div id=\"modal-sampleResult\" class=\"modal fade\" tabindex=\"-1\">\n          <div class=\"modal-dialog\" style=\"width: 100%\">\n            <div class=\"modal-content\">\n              <div class=\"modal-header\">\n                <h4 class=\"modal-title\">\n                  采样诊断结果\n                  <small><label id=\"modal-title\" style=\"color: #00BE67\"></label></small>\n                </h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n              </div>\n\n              <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n                  <div class=\"row\">\n                    <!-- 控件开始 -->\n                    <div class=\"col-md-12\">\n                      <table class=\"table table-bordered table-striped table-hover\">\n                        <thead id=\"sampleResultCount\"></thead>\n                        <tbody id=\"sampleResultTable\"></tbody>\n                      </table>\n                    </div>\n                  </div>\n                </div>\n\n                <div class=\"modal-footer\">\n                  <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n                </div>\n              </form>\n            </div>\n          </div>\n        </div>\n      </div>\n    </form>\n  </div>\n\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">删除任务列表</h3>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div style=\"float:left\">\n          <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=deleteKey\" id=\"appList\" name=\"ec\">\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                     value=\"${appId!}\" placeholder=\"应用id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                     value=\"${parentTaskId!}\" placeholder=\"任务id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                     value=\"${auditId!}\" placeholder=\"审批id\">\n            </div>\n\n            <div class=\"col-md-2\">\n              <select name=\"diagnosticStatus\" class=\"form-select\">\n                <option value=\"\" <#if !(diagnosticStatus??) || (diagnosticStatus?? && (diagnosticStatus == ''))>selected</#if>>\n                诊断状态\n                </option>\n                <option value=\"0\" <#if diagnosticStatus?? && (diagnosticStatus == 0)>selected</#if>>\n                诊断中\n                </option>\n                <option value=\"1\" <#if diagnosticStatus?? && (diagnosticStatus == 1)>selected</#if>>\n                诊断完成\n                </option>\n                <option value=\"2\" <#if diagnosticStatus?? && (diagnosticStatus == 2)>selected</#if>>\n                诊断异常\n                </option>\n              </select>\n            </div>\n            <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n          </form>\n        </div>\n      </div>\n    </div>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"table-responsive\">\n          <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"delkey_tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>appId</th>\n              <th>应用名称</th>\n              <th>诊断类型</th>\n              <th>任务id</th>\n              <th>子任务id</th>\n              <th>审批id</th>\n              <th>节点</th>\n              <th>诊断条件</th>\n\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>删除键数量</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\"><#if app_id?? && appDescMap?? && appDescMap?api.get(app_id)??>${appDescMap?api.get(app_id).name!}</#if></a>\n                </td>\n                <td>\n                  删除任务\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                    ${record.parentTaskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                    ${record.auditId!}\n                  </a>\n                </td>\n                <td>\n                  ${record.node!}\n                </td>\n                <td>\n                  ${record.diagnosticCondition!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  ${record.redisKey!}\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticHotKey.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#hotkey_tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        $('#hotkey_tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  $('#modal-hotkeyResult').on('shown.bs.modal', function (e) {\n    $('#modal-hotkeyTitle').html('');\n    $('#hotkeyDiv').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    var title = $(e.relatedTarget).data('title');\n    $('#modal-title').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 3\n            },\n            function (data) {\n              var res = data.result;\n\n              $('#hotkeyDiv').html(res);\n            }\n    );\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>所有主节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function submitDiagnostic(type) {\n\n    var appId;\n    var nodes;\n    var params = [];\n    if (type == 3) {\n      appId = $('#hotkey-select').selectpicker('val');\n      if (appId == null || appId == '') {\n        alert(\"请选择应用\");\n        return;\n      }\n\n      nodes = $('#hotkey_instance-select').selectpicker('val');\n\n      var command = $('#keyType-select').val();\n      params.push(command);\n    }\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: type,\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              params: params.toString()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=hotkey\";\n                // alert(\"检测任务提交成功，即将跳转任务流列表，任务id：\" + data.taskId);\n                // location.href = \"${request.contextPath}/manage/task/flow?taskId=\" + data.taskId;\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n      <div class=\"col-md-3\">\n        <select id=\"hotkey-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                data-live-search=\"true\" title=\"选择应用\"\n                onchange=\"changeAppIdSelect(this.value,'hotkey_instance-select')\">\n          <option value=\"\">选择应用</option>\n          <#list appDescMap?keys as key>\n            <#assign appDesc = appDescMap?api.get(key)>\n            <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n              【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n            </option>\n          </#list>\n        </select>\n      </div>\n      <div class=\"col-md-3\">\n        <select id=\"hotkey_instance-select\" name=\"nodes\"\n                class=\"selectpicker show-tick w-100 border rounded\" multiple\n                data-live-search=\"true\" title=\"选择实例\">\n        </select>\n      </div>\n      <div class=\"col-md-2\">\n        <select id=\"keyType-select\" name=\"keyType\" class=\"form-select w-100\"\n                title=\"选择命令\">\n          <option value=\"--hotkeys\">\n            --hotkeys (Sample Redis keys looking for hot keys.)\n          </option>\n          <option value=\"--bigkeys\">\n            --bigkeys (Sample Redis keys looking for keys with many elements (complexity).)\n          </option>\n          <option value=\"--memkeys\">\n            --memkeys (要求redis版本在5以上. Sample Redis keys looking for keys consuming a lot of memory.)\n          </option>\n        </select>\n      </div>\n\n      <div class=\"col-auto\">\n        <button type=\"button\" class=\"btn btn-success\" onclick=\"submitDiagnostic(3)\">采样诊断</button>\n      </div>\n    </form>\n  </div>\n\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">诊断任务列表</h3>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div style=\"float:left\">\n          <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=hotkey\" id=\"appList\" name=\"ec\">\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                     value=\"${appId!}\" placeholder=\"应用id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                     value=\"${parentTaskId!}\" placeholder=\"任务id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                     value=\"${auditId!}\" placeholder=\"审批id\">\n            </div>\n\n            <div class=\"col-auto\">\n              <select name=\"diagnosticStatus\" class=\"form-select\">\n                <option value=\"\" <#if !(diagnosticStatus??) || (diagnosticStatus?? && (diagnosticStatus == ''))>selected</#if>>\n                诊断状态\n                </option>\n                <option value=\"0\" <#if diagnosticStatus?? && (diagnosticStatus == 0)>selected</#if>>\n                诊断中\n                </option>\n                <option value=\"1\" <#if diagnosticStatus?? && (diagnosticStatus == 1)>selected</#if>>\n                诊断完成\n                </option>\n                <option value=\"2\" <#if diagnosticStatus?? && (diagnosticStatus == 2)>selected</#if>>\n                诊断异常\n                </option>\n              </select>\n            </div>\n            <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n          </form>\n        </div>\n      </div>\n    </div>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"table-responsive\">\n          <table class=\"table table-striped table-bordered table-hover\" id=\"hotkey_tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>appId</th>\n              <th>应用名称</th>\n              <th>诊断类型</th>\n              <th>任务id</th>\n              <th>子任务id</th>\n              <th>审批id</th>\n              <th>节点</th>\n              <th>诊断条件</th>\n\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>诊断结果</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\"><#if app_id?? && appDescMap?? && appDescMap?api.get(app_id)??>${appDescMap?api.get(app_id).name!}</#if></a>\n                </td>\n                <td>\n                  hotkey诊断\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                    ${record.parentTaskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                    ${record.auditId!}\n                  </a>\n                </td>\n                <td>\n                  ${record.node!}\n                </td>\n                <td>\n                  ${record.diagnosticCondition!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  <#if (record.status==1) && (record.type!=4)>\n                    <button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-hotkeyResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-title=\"应用${app_id!} 节点${record.node!}\">\n                      查看结果\n                    </button>\n                  </#if>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"modal-hotkeyResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          诊断结果\n          <small><label id=\"modal-hotkeyTitle\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\" id=\"hotkeyDiv\">\n\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticIdleKey.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#idlekey_tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        $('#idlekey_tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  $('#modal-idlekeyResult').on('shown.bs.modal', function (e) {\n    $('#modal-idlekeytitle').html('');\n    $('#idleKeyResultCount').html('');\n    $('#idleKeyResultTable').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    var title = $(e.relatedTarget).data('title');\n    $('#modal-idlekeytitle').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 2\n            },\n            function (data) {\n              $('#idleKeyResultCount').append(\n                      '<tr>' +\n                      '<td>key (共计' + data.count + '个）</td>' +\n                      '<td>idleTime(单位：天)</td>' +\n                      '</tr>'\n              );\n              var map = data.result;\n              for (var key in map) {\n                $('#idleKeyResultTable').append(\n                        '<tr>' +\n                        '<td>' + key + '</td>' +\n                        '<td>' + (map[key] / 3600 / 24).toFixed(1) + '</td>' +\n                        '</tr>'\n                );\n              }\n            }\n    );\n\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>所有主节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function submitDiagnostic(type) {\n\n    var appId;\n    var size;\n    var bytesFrom;\n    var nodes;\n    var params = [];\n    if (type == 2) {\n      appId = $('#idlekey-select').selectpicker('val');\n\n      if (appId == null || appId == '') {\n        alert(\"请选择应用\");\n        return;\n      }\n      nodes = $('#idlekey_instance-select').selectpicker('val');\n      var idleDay = $('#idleDay').val();\n\n      params.push(idleDay == '' ? 7 : idleDay);\n      size = $('#idlekey_size-select').val();\n\n      params.push(size);\n    }\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: type,\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              params: params.toString()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=idlekey\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n\n  function testisNum(id) {\n    var value = document.getElementById(id).value;\n    if (value != \"\" && isNaN(value)) {\n      alert(\"字节数请填入整数\");\n      document.getElementById(id).value = \"\";\n      document.getElementById(id).focus();\n      return false;\n    }\n    return true;\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div id=\"idlekey-div\">\n      <form class=\"row align-items-center\" role=\"form\"  method=\"post\" action=\"\" id=\"idlekey-form\" name=\"ec\">\n        <div class=\"col-md-3\">\n          <select id=\"idlekey-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\"\n                  onchange=\"changeAppIdSelect(this.value,'idlekey_instance-select')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n                【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n              </option>\n            </#list>\n          </select>\n        </div>\n        <div class=\"col-md-3\">\n          <select id=\"idlekey_instance-select\" name=\"nodes\"\n                  class=\"selectpicker show-tick w-100 border rounded\" multiple\n                  data-live-search=\"true\" title=\"选择实例\">\n          </select>\n        </div>\n        <label class=\"bi bi-question-circle col-auto\" aria-hidden=\"true\" title=\"空闲时间，单位天，默认7天\"></label>\n        <div class=\"col-auto\">\n          <input id=\"idleDay\" type=\"text\" class=\"form-control\" name=\"idleDay\"\n                 placeholder=\"空闲天数\" onchange=\"testisNum(this.id)\">\n        </div>\n        <div class=\"col-auto\">\n          <select id=\"idlekey_size-select\" name=\"size\" class=\"form-select w-100\" title=\"数量\">\n            <option value=\"-1\">\n              全部\n            </option>\n            <option value=\"20\">\n              top 20\n            </option>\n            <option value=\"50\">\n              top 50\n            </option>\n            <option value=\"100\">\n              top 100\n            </option>\n            <option value=\"200\">\n              top 200\n            </option>\n            <option value=\"500\">\n              top 500\n            </option>\n          </select>\n        </div>\n\n        <div class=\"col-auto\">\n          <button type=\"button\" class=\"btn btn-success\" onclick=\"submitDiagnostic(2)\">提交检测</button>\n        </div>\n      </form>\n    </div>\n  </div>\n\n  <div class=\"card-header\">\n      <h3 class=\"card-title\">idlekey诊断任务列表</h3>\n  </div>\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div style=\"float:left\">\n          <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=idlekey\" id=\"appList\" name=\"ec\">\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                     value=\"${appId!}\" placeholder=\"应用id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                     value=\"${parentTaskId!}\" placeholder=\"任务id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                     value=\"${auditId!}\" placeholder=\"审批id\">\n            </div>\n\n            <div class=\"col-auto\">\n              <select name=\"diagnosticStatus\" class=\"form-select\">\n                <option value=\"\" <#if !(diagnosticStatus??) || (diagnosticStatus?? && (diagnosticStatus == ''))>selected</#if>>\n                诊断状态\n                </option>\n                <option value=\"0\" <#if diagnosticStatus?? && (diagnosticStatus == 0)>selected</#if>>\n                诊断中\n                </option>\n                <option value=\"1\" <#if diagnosticStatus?? && (diagnosticStatus == 1)>selected</#if>>\n                诊断完成\n                </option>\n                <option value=\"2\" <#if diagnosticStatus?? && (diagnosticStatus == 2)>selected</#if>>\n                诊断异常\n                </option>\n              </select>\n            </div>\n            <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n          </form>\n        </div>\n      </div>\n    </div>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"table-responsive\">\n          <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"idlekey_tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>appId</th>\n              <th>应用名称</th>\n              <th>诊断类型</th>\n              <th>任务id</th>\n              <th>子任务id</th>\n              <th>审批id</th>\n              <th>节点</th>\n              <th>诊断条件</th>\n\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>诊断结果</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\"><#if app_id?? && appDescMap?? && appDescMap?api.get(app_id)??>${appDescMap?api.get(app_id).name!}</#if></a>\n                </td>\n                <td>\n                  idlekey诊断\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                    ${record.parentTaskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                    ${record.auditId!}\n                  </a>\n                </td>\n                <td>\n                  ${record.node!}\n                </td>\n                <td>\n                  ${record.diagnosticCondition!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  <#if (record.status==1) && (record.type!=4)>\n                    <button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-idlekeyResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-title=\"应用${app_id!} 节点${record.node!}\">\n                      查看结果\n                    </button>\n                  </#if>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"modal-idlekeyResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          诊断结果\n          <small><label id=\"modal-idlekeytitle\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <table class=\"table table-bordered table-striped table-hover\">\n                <thead id=\"idleKeyResultCount\"></thead>\n                <tbody id=\"idleKeyResultTable\"></tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticMemUsed.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#bigkey_tableDataList').dataTable({\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\"\n          }\n        });\n\n        $('#bigkey_tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  $('#modal-memoryUsedResult').on('shown.bs.modal', function (e) {\n    $('#modal-memoryUsedtitle').html('');\n    $('#memoryUsedResultCount').html('');\n    $('#memoryUsedResultTable').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    console.log(\"rediskey:\" + redisKey);\n    var title = $(e.relatedTarget).data('title');\n    $('#modal-memoryUsedtitle').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 1\n            },\n            function (data) {\n              $('#memoryUsedResultCount').append(\n                      '<tr>' +\n                      '<td>key (共计' + data.count + '个）</td>' +\n                      '<td>memoryUsed</td>' +\n                      '</tr>'\n              );\n              var memoryUsedResultMap = data.result;\n              for (var key in memoryUsedResultMap) {\n                $('#memoryUsedResultTable').append(\n                        '<tr>' +\n                        '<td>' + key + '</td>' +\n                        '<td>' + (memoryUsedResultMap[key] / 1024).toFixed(2) + 'K (' + memoryUsedResultMap[key] + 'bytes)</td>' +\n                        '</tr>'\n                );\n              }\n            }\n    );\n\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>所有主节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function submitDiagnostic(type) {\n\n    var appId;\n    var size;\n    var bytesFrom;\n    var nodes;\n    var params = [];\n    if (type == 1) {\n      appId = $('#bigkey-select').selectpicker('val');\n\n      if (appId == null || appId == '') {\n        alert(\"请选择应用\");\n        return;\n      }\n      nodes = $('#bigkey_instance-select').selectpicker('val');\n      size = $('#bigkey_size-select').val();\n      bytesFrom = $('#bigkey-from-select').val();\n      params.push(bytesFrom);\n      params.push(size);\n    }\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: type,\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              params: params.toString()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=memoryUsed\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n\n  function testisNum(id) {\n    var value = document.getElementById(id).value;\n    if (value != \"\" && isNaN(value)) {\n      alert(\"字节数请填入整数\");\n      document.getElementById(id).value = \"\";\n      document.getElementById(id).focus();\n      return false;\n    }\n    return true;\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n      <div class=\"col-md-3\">\n        <select id=\"bigkey-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                data-live-search=\"true\" title=\"选择应用\"\n                onchange=\"changeAppIdSelect(this.value,'bigkey_instance-select')\">\n          <option value=\"\">选择应用</option>\n          <#list appDescMap?keys as key>\n            <#assign appDesc = appDescMap?api.get(key)>\n            <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n              【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n            </option>\n          </#list>\n        </select>\n      </div>\n      <div class=\"col-md-3\">\n        <select id=\"bigkey_instance-select\" name=\"nodes\"\n                class=\"selectpicker show-tick w-100 border rounded\" multiple\n                data-live-search=\"true\" title=\"选择实例\">\n        </select>\n      </div>\n      <div class=\"col-auto\">\n        <select id=\"bigkey_size-select\" name=\"size\" class=\"form-select w-100\" title=\"数量\">\n          <option value=\"20\">\n            top 20\n          </option>\n          <option value=\"50\">\n            top 50\n          </option>\n          <option value=\"100\">\n            top 100\n          </option>\n          <option value=\"200\">\n            top 200\n          </option>\n          <option value=\"500\">\n            top 500\n          </option>\n        </select>\n      </div>\n      <div class=\"col-auto\">\n        <select id=\"bigkey-from-select\" name=\"size\" class=\"form-select w-100\" title=\"数量\">\n          <option value=\"10\">\n            > 10K\n          </option>\n          <option value=\"50\">\n            > 50K\n          </option>\n          <option value=\"200\">\n            > 200K\n          </option>\n          <option value=\"500\">\n            > 500K\n          </option>\n          <option value=\"1024\">\n            > 1M\n          </option>\n        </select>\n      </div>\n      <div class=\"col-auto\">\n        <button type=\"button\" class=\"btn btn-success\" onclick=\"submitDiagnostic(1)\">提交检测</button>\n      </div>\n    </form>\n  </div>\n\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">memoryUsed诊断任务列表</h3>\n  </div>\n\n  <div class=\"card-body\">\n    <form class=\"row\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=memoryUsed\" id=\"appList\" name=\"ec\">\n      <div class=\"col-md-2\">\n        <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n               value=\"${appId!}\" placeholder=\"应用id\">\n      </div>\n      <div class=\"col-md-2\">\n        <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n               value=\"${parentTaskId!}\" placeholder=\"任务id\">\n      </div>\n      <div class=\"col-md-2\">\n        <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n               value=\"${auditId!}\" placeholder=\"审批id\">\n      </div>\n\n      <div class=\"col-auto\">\n        <select name=\"diagnosticStatus\" class=\"form-select\">\n          <option value=\"\" <#if !(diagnosticStatus??) || (diagnosticStatus?? && (diagnosticStatus == ''))>selected</#if>>\n          诊断状态\n          </option>\n          <option value=\"0\" <#if diagnosticStatus?? && (diagnosticStatus == 0)>selected</#if>>\n          诊断中\n          </option>\n          <option value=\"1\" <#if diagnosticStatus?? && (diagnosticStatus == 1)>selected</#if>>\n          诊断完成\n          </option>\n          <option value=\"2\" <#if diagnosticStatus?? && (diagnosticStatus == 2)>selected</#if>>\n          诊断异常\n          </option>\n        </select>\n      </div>\n      <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n    </form>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"portlet box light-grey table-responsive\" id=\"clientIndex\">\n          <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"bigkey_tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>appId</th>\n              <th>应用名称</th>\n              <th>诊断类型</th>\n              <th>任务id</th>\n              <th>子任务id</th>\n              <th>审批id</th>\n              <th>节点</th>\n              <th>诊断条件</th>\n\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>诊断结果</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\"><#if app_id?? && appDescMap?? && appDescMap?api.get(app_id)??>${appDescMap?api.get(app_id).name!}</#if></a>\n                </td>\n                <td>\n                  memoryUsed诊断\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                    ${record.parentTaskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                    ${record.auditId!}\n                  </a>\n                </td>\n                <td>\n                  ${record.node!}\n                </td>\n                <td>\n                  ${record.diagnosticCondition!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  <#if (record.status==1) && (record.type!=4)>\n                    <button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-memoryUsedResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-title=\"应用${app_id!} 节点${record.node!}\">\n                      查看结果\n                    </button>\n                  </#if>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div id=\"modal-memoryUsedResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          诊断结果\n          <small><label id=\"modal-memoryUsedtitle\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <table class=\"table table-bordered table-striped table-hover\">\n                <thead id=\"memoryUsedResultCount\"></thead>\n                <tbody id=\"memoryUsedResultTable\"></tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticResult.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h3 class=\"card-title\">应用诊断任务列表</h3>\n    </div>\n    <div class=\"card-body\">\n      <div id=\"idlekey-div\">\n        <form class=\"row align-items-center\" role=\"form\"  method=\"post\" action=\"${request.contextPath}/manage/app/tool/diagnostic/result\"\n              id=\"appList\" name=\"ec\">\n          <div class=\"col-auto\">\n            <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                   value=\"${appId!}\" placeholder=\"应用id\">\n          </div>\n          <div class=\"col-auto\">\n            <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                   value=\"${parentTaskId!}\" placeholder=\"任务id\">\n          </div>\n          <div class=\"col-auto\">\n            <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                   value=\"${auditId!}\" placeholder=\"审批id\">\n          </div>\n          <div class=\"col-md-2\">\n            <select name=\"type\" class=\"form-select w-100\">\n              <option value=\"\" <#if (type == '')>selected</#if>>\n              诊断类型\n              </option>\n              <#list diagnosticTypeMap?keys as key>\n                <option value=\"${key!}\" <#if (key == type)>selected</#if>>\n                  ${diagnosticTypeMap[key]!}\n                </option>\n              </#list>\n            </select>\n          </div>\n\n          <div class=\"col-md-2\">\n            <select name=\"diagnosticStatus\" class=\"form-select w-100\">\n              <option value=\"\" <#if (diagnosticStatus == '')>selected</#if>>\n              诊断状态\n              </option>\n              <option value=\"0\" <#if (diagnosticStatus == 0)>selected</#if>>\n              诊断中\n              </option>\n              <option value=\"1\" <#if (diagnosticStatus == 1)>selected</#if>>\n              诊断完成\n              </option>\n              <option value=\"2\" <#if (diagnosticStatus == 2)>selected</#if>>\n              诊断异常\n              </option>\n            </select>\n          </div>\n          <button type=\"submit\" class=\"btn btn-success\">查询</button>\n        </form>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"col-md-12\">\n          <div class=\"portlet box light-grey\" id=\"clientIndex\">\n\n            <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n              <thead>\n              <tr>\n                <td>序号</td>\n                <th>appId</th>\n                <th>应用名称</th>\n                <th>诊断类型</th>\n                <th>任务id</th>\n                <th>子任务id</th>\n                <th>审批id</th>\n                <th>节点</th>\n                <th>诊断条件</th>\n\n                <th>创建时间</th>\n                <th>诊断状态</th>\n                <th>诊断耗时</th>\n                <th>诊断结果</th>\n              </tr>\n              </thead>\n              <tbody>\n              <#list diagnosticTaskRecordList as record>\n                <#assign app_id = record.appId>\n                <tr>\n                  <td>${record_index + 1}</td>\n                  <td>\n                    <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                  </td>\n                  <td>\n                    <a target=\"_blank\"\n                       href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\">${appDescMap?api.get(app_id).name!}</a>\n                  </td>\n                  <td>\n                    ${diagnosticTypeMap[record.type]!}\n                  </td>\n                  <td>\n                    <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                      ${record.parentTaskId!}\n                    </a>\n                  </td>\n                  <td>\n                    <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                      ${record.taskId!}\n                    </a>\n                  </td>\n                  <td>\n                    <a target=\"_blank\"\n                       href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                      ${record.auditId!}\n                    </a>\n                  </td>\n                  <td>\n                    ${record.node!}\n                  </td>\n                  <td>\n                    ${record.diagnosticCondition!}\n                  </td>\n                  <td>\n                    ${record.createTime?string(\"yyyy-MM-dd HH:mm:ss\")}\n                  </td>\n                  <td>\n                    <#if (record.status==0)>诊断中</#if>\n                    <#if (record.status==1)>诊断完成</#if>\n                    <#if (record.status==2)>诊断异常</#if>\n                  </td>\n                  <td>\n                    <#if (record.status==1)>${record.formatCostTime!}</#if>\n                  </td>\n                  <td>\n                    <#if (record.status==1) && (record.type!=4)>\n                      <button type=\"button\" class=\"btn btn-sm btn-info\"\n                              data-bs-target=\"#modal-diagnosticResult\" data-bs-toggle=\"modal\"\n                              data-rediskey=\"${record.redisKey!}\"\n                              data-title=\"应用${app_id!} 节点${record.node!}\">\n                        查看结果\n                      </button>\n                    </#if>\n                  </td>\n                </tr>\n              </#list>\n              </tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"modal-diagnosticResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          诊断结果\n          <small><label id=\"modal-title\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <table class=\"table table-bordered table-striped table-hover\">\n                <thead id=\"diagnosticResultCount\"></thead>\n                <tbody id=\"diagnosticResultTable\"></tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n\n</div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticSampleCompare.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        $('#tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  $('#modal-diagnosticResult').on('shown.bs.modal', function (e) {\n    $('#modal-title').html('');\n    $('#diagnosticResultCount').html('');\n    $('#diagnosticResultTable').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    var title = $(e.relatedTarget).data('title');\n    $('#modal-title').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 7\n            },\n            function (data) {\n              var diagnosticResultList = data.result;\n              diagnosticResultList.forEach(function (diagnosticResult, index) {\n                $('#diagnosticResultTable').append(\n                        '<tr>' +\n                        '<td>' + diagnosticResult + '</td>' +\n                        '</tr>'\n                );\n              })\n            }\n    );\n\n  });\n\n  function changeAppId(selectElement, divElement, instanceElement, pwdElement) {\n    var appId = $('#' + selectElement).selectpicker('val');\n    if(appId != null && appId != ''){\n      $('#' + divElement).css(\"display\", \"none\");\n      $('#'+ instanceElement).val('');\n      $('#' + pwdElement).val('');\n    }else{\n      $('#' + divElement).css(\"display\", \"block\");\n    }\n  }\n\n  function submitDiagnostic() {\n    var paramMap = new Map();\n    var sourceAppId = $('#sample-source-select').selectpicker('val');\n    var sourceInstances = $('#sourceInstances').val();\n    var sourcePwd = $('#sourcePwd').val();\n    if ((sourceAppId == null || sourceAppId == '') &&\n      (sourceInstances == null || sourceInstances == '')) {\n      alert(\"请选择或输入源应用\");\n      return;\n    }\n\n    if(sourceInstances != null && sourceInstances != ''){\n      // 使用换行符分割字符串，得到一组server信息\n      var hostPortArray = sourceInstances.split('\\n');\n      // 遍历主机信息数组，提取每个主机的IP地址\n      if(hostPortArray.length > 0) {\n          sourceInstances = hostPortArray[0];\n       }\n    }\n\n    var targetAppId = $('#sample-target-select').selectpicker('val');\n    var targetInstances = $('#targetInstances').val();\n    var targetPwd = $('#targetPwd').val();\n    if ((targetAppId == null || targetAppId == '') &&\n      (targetInstances == null || targetInstances == '')) {\n      alert(\"请选择或输入目标应用\");\n      return;\n    }\n\n    if(targetInstances != null && targetInstances != ''){\n      // 使用换行符分割字符串，得到一组server信息\n      var hostPortArray = targetInstances.split('\\n');\n      // 遍历主机信息数组，提取每个主机的IP地址\n      if(hostPortArray.length > 0) {\n          targetInstances = hostPortArray[0];\n       }\n    }\n\n    paramMap.set(\"sourceAppId\", sourceAppId);\n    paramMap.set(\"sourceServers\", sourceInstances);\n    paramMap.set(\"sourcePwd\", sourcePwd);\n    paramMap.set(\"targetAppId\", targetAppId);\n    paramMap.set(\"targetServers\", targetInstances);\n    paramMap.set(\"targetPwd\", targetPwd);\n    paramMap.set(\"perCount\", $('#scanCount').val());\n    paramMap.set(\"totalCount\", $('#totalCount').val());\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: 7,\n              params: mapToJson(paramMap)\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=sampleCompare\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n\n  function mapToJson(map) {\n    var str = '{';\n    var i = 1;\n    for (var [key, value] of map.entries()) {\n      if (i == map.size) {\n        str += '\"' + key + '\":\"'+ value + '\"';\n      }else{\n        str += '\"' + key + '\":\"'+ value + '\",';\n      }\n      i++;\n    }\n    str += '}';\n    return str;\n  }\n\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div id=\"scan-div\">\n      <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n        <div class=\"row mb-2\">\n          <div class=\"col-md-4\">\n            <select id=\"sample-source-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                    data-live-search=\"true\" title=\"选择源应用\"\n                    onchange=\"changeAppId('sample-source-select', 'sourceDiv', 'sourceInstances', 'sourcePwd')\">\n              <option value=\"\">选择源应用</option>\n              <#list appDescMap?keys as key>\n                <#assign appDesc = appDescMap?api.get(key)>\n                <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n                  【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n                </option>\n              </#list>\n            </select>\n          </div>\n        </div>\n        <div  id=\"sourceDiv\" class=\"margin-top-10\">\n          <div class=\"row mb-2\">\n            <label for=\"sourceInstances\" class=\"col-form-label col-auto\">源应用节点</label>\n            <div class=\"col-md-6\">\n              <textarea id=\"sourceInstances\" type=\"text\" class=\"form-control\" name=\"sourceInstances\"\n                     placeholder=\"ip:port 多个实例换行分割\"></textarea>\n            </div>\n            <label for=\"sourcePwd\" class=\"col-form-label col-auto\">源密码</label>\n            <div class=\"col-md-3\">\n              <input id=\"sourcePwd\" name=\"sourcePwd\" type=\"text\" class=\"form-control\" placeholder=\"源redis密码\">\n            </div>\n          </div>\n        </div>\n\n        <div class=\"row mb-2\">\n          <div class=\"col-md-4\">\n            <select id=\"sample-target-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                    data-live-search=\"true\" title=\"选择目标应用\"\n                    onchange=\"changeAppId('sample-target-select', 'targetDiv', 'targetInstances', 'targetPwd')\">\n              <option value=\"\">选择目标应用</option>\n              <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n                【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n              </option>\n            </#list>\n            </select>\n          </div>\n        </div>\n\n        <div  id=\"targetDiv\" class=\"margin-top-10\">\n          <div class=\"row mb-2\">\n            <label for=\"targetInstances\" class=\"col-form-label col-auto\">目标应用节点</label>\n            <div class=\"col-md-6\">\n              <textarea id=\"targetInstances\" type=\"text\" class=\"form-control\" name=\"targetInstances\"\n                        placeholder=\"ip:port 多个实例换行分割\"></textarea>\n            </div>\n            <label for=\"targetPwd\" class=\"col-form-label col-auto\">目标密码</label>\n            <div class=\"col-md-3\">\n              <input id=\"targetPwd\" name=\"targetPwd\" type=\"text\" class=\"form-control\" placeholder=\"目标redis密码\">\n            </div>\n          </div>\n        </div>\n\n        <div class=\"row mb-2\">\n          <label for=\"scanCount\" class=\"col-form-label col-auto\">scan count</label>\n          <div class=\"col-md-2\">\n            <input id=\"scanCount\" name=\"scanCount\" type=\"text\" class=\"form-control\" placeholder=\"每次批处理执行条数\">\n          </div>\n          <label for=\"totalCount\" class=\"col-form-label col-auto\">total count</label>\n          <div class=\"col-md-2\">\n            <input id=\"totalCount\" name=\"totalCount\" type=\"text\" class=\"form-control\" placeholder=\"每个节点处理总条数\">\n          </div>\n          <div class=\"col-md-2\">\n              <button type=\"button\" class=\"btn btn-success offset-md-1 col-auto\" onclick=\"submitDiagnostic()\">执行任务</button>\n          </div>\n        </div>\n      </form>\n    </div>\n  </div>\n\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">数据抽样比对列表</h3>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div>\n          <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=sampleCompare\" id=\"appList\" name=\"ec\">\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                     value=\"${appId!}\" placeholder=\"应用id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                     value=\"${parentTaskId!}\" placeholder=\"任务id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                     value=\"${auditId!}\" placeholder=\"审批id\">\n            </div>\n            <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n          </form>\n        </div>\n      </div>\n    </div>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"portlet box light-grey table-responsive\" id=\"sampleCompareIndex\">\n\n          <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>任务id</th>\n              <th>审批id</th>\n              <th>抽样信息</th>\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>诊断结果</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                    <a target=\"_blank\"\n                       href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                      ${record.auditId!}\n                    </a>\n                </td>\n                <td width=\"20%\" style=\"word-wrap:break-word;word-break:break-all;\">\n                  ${record.diagnosticCondition!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  <#if (record.status==0) || (record.status==1)>\n                    <a target=\"_blank\"\n                       href=\"${request.contextPath}/manage/app/tool//diagnostic/sampleCompareData?redisKey=${record.redisKey!}\">\n                      查看结果\n                    </a>\n                    <!--<button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-diagnosticResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-title=\"数据抽样对比\">\n                      查看结果\n                    </button>-->\n                  </#if>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div id=\"modal-diagnosticResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          诊断结果\n          <small><label id=\"modal-title\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <table class=\"table table-bordered table-striped table-hover\">\n                <thead id=\"diagnosticResultCount\"></thead>\n                <tbody id=\"diagnosticResultTable\"></tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticSampleCompareResult.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>采样校验源数据和目标数据</title>\n  <#include '/inc/frontResources.html'>\n</head>\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n  校验程序已启动，结果如下：<br/>\n  <#if diagnosticResultList?? && (diagnosticResultList?size > 0)>\n    <#list diagnosticResultList as line>\n      <font color=\"white\">${line!}</font><br/>\n    </#list>\n  </#if>\n  <br/>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticScan.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n\n        $('#tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  $('#modal-diagnosticResult').on('shown.bs.modal', function (e) {\n    $('#modal-title').html('');\n    $('#diagnosticResultCount').html('');\n    $('#diagnosticResultTable').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    var title = $(e.relatedTarget).data('title');\n    $('#modal-title').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 0\n            },\n            function (data) {\n              $('#diagnosticResultCount').append(\n                      '<tr>' +\n                      '<td>key (共计' + data.count + '个）</td>' +\n                      '</tr>'\n              );\n              var diagnosticResultList = data.result;\n              diagnosticResultList.forEach(function (diagnosticResult, index) {\n                $('#diagnosticResultTable').append(\n                        '<tr>' +\n                        '<td>' + diagnosticResult + '</td>' +\n                        '</tr>'\n                );\n              })\n            }\n    );\n\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>所有主节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function submitDiagnostic(type) {\n    var appId;\n    var size;\n    var nodes;\n    var params = [];\n    if (type == 0) {\n      appId = $('#scan-select').selectpicker('val');\n      if (appId == null || appId == '') {\n        alert(\"请选择应用\");\n        return;\n      }\n\n      nodes = $('#scan_instance-select').selectpicker('val');\n\n      size = $('#scan_size-select').val();\n\n      params.push($('#scan-pattern').val());\n      params.push(size);\n    }\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: type,\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              params: params.toString()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=scan\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div id=\"scan-div\">\n      <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n        <div class=\"col-md-3\">\n          <select id=\"scan-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\"\n                  onchange=\"changeAppIdSelect(this.value,'scan_instance-select')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n                【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n              </option>\n            </#list>\n          </select>\n        </div>\n        <div class=\"col-md-3\">\n          <select id=\"scan_instance-select\" name=\"nodes\"\n                  class=\"selectpicker show-tick w-100 border rounded\" multiple\n                  data-live-search=\"true\" title=\"选择实例\">\n          </select>\n        </div>\n        <div class=\"col-auto\">\n          <select id=\"scan_size-select\" name=\"size\" class=\"form-select w-100\" title=\"数量\">\n            <option value=\"20\">\n              top 20\n            </option>\n            <option value=\"50\">\n              top 50\n            </option>\n            <option value=\"100\">\n              top 100\n            </option>\n            <option value=\"200\">\n              top 200\n            </option>\n          </select>\n        </div>\n        <div class=\"col-md-2\">\n          <input id=\"scan-pattern\" type=\"text\" class=\"form-control\" name=\"pattern\"\n                 placeholder=\"匹配模式 pattern\">\n        </div>\n\n        <div class=\"col-auto\">\n          <button type=\"button\" class=\"btn btn-success\" onclick=\"submitDiagnostic(0)\">提交检测</button>\n        </div>\n      </form>\n    </div>\n  </div>\n\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">scan诊断任务列表</h3>\n  </div>\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-12\">\n        <div style=\"float:left\">\n          <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=scan\" id=\"appList\" name=\"ec\">\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                     value=\"${appId!}\" placeholder=\"应用id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                     value=\"${parentTaskId!}\" placeholder=\"任务id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                     value=\"${auditId!}\" placeholder=\"审批id\">\n            </div>\n\n            <div class=\"col-auto\">\n              <select name=\"diagnosticStatus\" class=\"form-select\">\n                <option value=\"\" <#if !(diagnosticStatus??) || (diagnosticStatus?? && (diagnosticStatus == ''))>selected</#if>>\n                诊断状态\n                </option>\n                <option value=\"0\" <#if diagnosticStatus?? && (diagnosticStatus == 0)>selected</#if>>\n                诊断中\n                </option>\n                <option value=\"1\" <#if diagnosticStatus?? && (diagnosticStatus == 1)>selected</#if>>\n                诊断完成\n                </option>\n                <option value=\"2\" <#if diagnosticStatus?? && (diagnosticStatus == 2)>selected</#if>>\n                诊断异常\n                </option>\n              </select>\n            </div>\n            <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n          </form>\n        </div>\n      </div>\n    </div>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-12\">\n        <div class=\"table-responsive\" id=\"clientIndex\">\n          <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>appId</th>\n              <th>应用名称</th>\n              <th>诊断类型</th>\n              <th>任务id</th>\n              <th>子任务id</th>\n              <th>审批id</th>\n              <th>节点</th>\n              <th>诊断条件</th>\n\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>诊断结果</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\"><#if app_id?? && appDescMap?? && appDescMap?api.get(app_id)??>${appDescMap?api.get(app_id).name!}</#if></a>\n                </td>\n                <td>\n                  scan检测\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                    ${record.parentTaskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                    ${record.auditId!}\n                  </a>\n                </td>\n                <td>\n                  ${record.node!}\n                </td>\n                <td>\n                  ${record.diagnosticCondition!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  <#if (record.status==1) && (record.type!=4)>\n                    <button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-diagnosticResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-title=\"应用${app_id!} 节点${record.node!}\">\n                      查看结果\n                    </button>\n                  </#if>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"modal-diagnosticResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          诊断结果\n          <small><label id=\"modal-title\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-12\">\n              <table class=\"table table-bordered table-striped table-hover\">\n                <thead id=\"diagnosticResultCount\"></thead>\n                <tbody id=\"diagnosticResultTable\"></tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticScanClean.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        $('#tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  $('#modal-diagnosticResult').on('shown.bs.modal', function (e) {\n    $('#modal-title').html('');\n    $('#diagnosticResultCount').html('');\n    $('#diagnosticResultTable').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    var title = $(e.relatedTarget).data('title');\n    $('#modal-title').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 6\n            },\n            function (data) {\n              /*$('#diagnosticResultCount').append(\n                  '<tr>' +\n                  '<td>key (共计' + data.count + '个）</td>' +\n                  '</tr>'\n              );*/\n              var diagnosticResultList = data.result;\n              diagnosticResultList.forEach(function (diagnosticResult, index) {\n                $('#diagnosticResultTable').append(\n                        '<tr>' +\n                        '<td>' + diagnosticResult + '</td>' +\n                        '</tr>'\n                );\n              })\n            }\n    );\n\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value='allMaster'>所有主节点</option>\");\n                $('#' + instance_select).append(\"<option value='allSlave'>所有从节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function changeTtlResetShow() {\n    var operateType = $('#scan_clean').selectpicker('val');\n    if(operateType == 0 || operateType == 1){\n      $('#resetTtl').css(\"display\", \"none\");\n    }else{\n      $('#resetTtl').css(\"display\", \"block\");\n    }\n  }\n\n  function submitDiagnostic() {\n    var appId;\n    var nodes;\n    var paramMap = new Map();\n    appId = $('#scan-select').selectpicker('val');\n    if (appId == null || appId == '') {\n      alert(\"请选择应用\");\n      return;\n    }\n    nodes = $('#scan_instance-select').selectpicker('val');\n    if (nodes == null || nodes == ''\n            || nodes.toString() == null || nodes.toString() ==  '') {\n      alert(\"请选择实例\");\n      return;\n    }\n\n    var pattern = $('#scan-pattern').val();\n    if (pattern == null || pattern == '') {\n      alert(\"请填写键匹配字符串\");\n      return;\n    }\n\n    var ttlResetLess = null;\n    var ttlResetMore = null;\n    var operateType = $('#scan_clean').selectpicker('val');\n    if (operateType == 2) {\n      ttlResetLess = $('#ttl-reset-less').val();\n      ttlResetMore = $('#ttl-reset-more').val();\n      if (ttlResetLess == null || ttlResetLess == ''\n              || ttlResetMore == null || ttlResetMore == ''\n              || ttlResetLess <= ttlResetMore) {\n        alert(\"请填写ttl重置时间范围，且不能为同一时间，尽量分开，避免集中过期\");\n        return;\n      }\n    }\n    paramMap.set(\"nodes\", nodes.toString());\n    paramMap.set(\"operateType\", operateType);\n    paramMap.set(\"pattern\", pattern);\n    paramMap.set(\"ttlLess\", $('#ttl-value-less').val());\n    paramMap.set(\"ttlMore\", $('#ttl-value-more').val());\n    paramMap.set(\"ttlResetLess\", ttlResetLess);\n    paramMap.set(\"ttlResetMore\", ttlResetMore);\n    paramMap.set(\"perCount\", $('#per_count').val());\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: 6,\n              appId: appId,\n              params: mapToJson(paramMap)\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=scanClean\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n\n  function mapToJson(map) {\n    var str = '{';\n    var i = 1;\n    for (var [key, value] of map.entries()) {\n      if (i == map.size) {\n        str += '\"' + key + '\":\"'+ value + '\"';\n      }else{\n        str += '\"' + key + '\":\"'+ value + '\",';\n      }\n      i++;\n    }\n    str += '}';\n    return str;\n  }\n\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div id=\"scan-div\">\n      <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n        <div class=\"row mb-2\">\n          <div class=\"col-md-3\">\n            <select id=\"scan-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                    data-live-search=\"true\" title=\"选择应用\"\n                    onchange=\"changeAppIdSelect(this.value,'scan_instance-select')\">\n              <option value=\"\">选择应用</option>\n              <#list appDescMap?keys as key>\n                <#assign appDesc = appDescMap?api.get(key)>\n                <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n                  【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n                </option>\n              </#list>\n            </select>\n          </div>\n          <div class=\"col-md-3\">\n            <select id=\"scan_instance-select\" name=\"nodes\"\n                    class=\"selectpicker show-tick w-100 border rounded\" multiple\n                    data-live-search=\"true\" title=\"选择实例\">\n            </select>\n          </div>\n        </div>\n        <div  id=\"patternDiv\" class=\"margin-top-10\">\n          <div class=\"row mb-2\">\n            <label for=\"scan-pattern\" class=\"col-form-label col-auto\">键匹配</label>\n            <div class=\"col-md-3\">\n              <input id=\"scan-pattern\" type=\"text\" class=\"form-control\" name=\"scan-pattern\"\n                     placeholder=\"支持通配符*和数字[0-9]匹配\">\n            </div>\n            <div class=\"col-md-8\">\n              <label><font style=\"font-weight: lighter; color: yellowgreen\">如需筛选key中某部分小于指定值，在匹配串中添加<font style=\"font-weight: bold\">@Less{指定值}Less@</font>，大于时，将Less替换为More</font></label>\n            </div>\n          </div>\n\n          <div class=\"row mb-2\">\n            <label for=\"per_count\" class=\"col-form-label col-auto\">扫描数量</label>\n            <div class=\"col-md-3\">\n              <input id=\"per_count\" name=\"per_count\" type=\"text\" class=\"form-control\" placeholder=\"每次获取数量\">\n            </div>\n\n            <label for=\"scan_clean\" class=\"col-form-label col-auto\">操作类型</label>\n            <div class=\"col-md-3\">\n              <select id=\"scan_clean\" name=\"scan_clean\" class=\"selectpicker border rounded\" data-live-search=\"false\" title=\"操作类型\" onchange=\"changeTtlResetShow()\">\n                <option value=\"0\">\n                  仅扫描分析\n                </option>\n                <option value=\"1\">\n                  分析清理\n                </option>\n                <option value=\"2\">\n                  分析重置ttl\n                </option>\n              </select>\n            </div>\n          </div>\n        </div>\n\n        <div id = \"resetTtl\" class=\"row\" style=\"display: none\">\n          <div class=\"row mb-2\">\n            <label for=\"ttl-reset-more\" class=\"col-form-label col-auto col-form-label\">重置ttl时间 ttl > </label>\n            <div class=\"col-md-3\">\n              <input id=\"ttl-reset-more\" type=\"text\" class=\"form-control\" name=\"ttl-reset-more\"\n                     placeholder=\"最小过期时间\">\n            </div>\n            <label for=\"ttl-reset-less\" class=\"col-form-label col-auto col-form-label\"> and ttl <</label>\n            <div class=\"col-md-3\">\n              <input id=\"ttl-reset-less\" type=\"text\" class=\"form-control\" name=\"ttl-reset-less\"\n                     placeholder=\"最大过期时间\">\n            </div>\n          </div>\n        </div>\n\n        <div id=\"filterDiv\" class=\"row mb-2\">\n          <label class=\"row\"><font color=\"orange\">过滤匹配：提供键包含、键关键字段值、ttl过滤</font></label>\n          <div class=\"row mb-2\">\n            <label for=\"ttl-value-more\" class=\"col-form-label col-auto\"> 剩余时间 ttl > </label>\n            <div class=\"col-md-3\">\n              <input id=\"ttl-value-more\" type=\"text\" class=\"form-control\" name=\"ttl-value-more\"\n                     placeholder=\"最小剩余时间\">\n            </div>\n            <label for=\"ttl-value-less\" class=\"col-form-label col-auto\"> and ttl < </label>\n            <div class=\"col-md-3\">\n              <input id=\"ttl-value-less\" type=\"text\" class=\"form-control\" name=\"ttl-value-less\"\n                     placeholder=\"最大剩余时间\">\n            </div>\n            <button type=\"button\" class=\"btn btn-success offset-md-1 col-auto\" onclick=\"submitDiagnostic()\">执行任务</button>\n          </div>\n        </div>\n      </form>\n    </div>\n  </div>\n\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">分析清理任务列表</h3>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div>\n          <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=scanClean\" id=\"appList\" name=\"ec\">\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                     value=\"${appId!}\" placeholder=\"应用id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                     value=\"${parentTaskId!}\" placeholder=\"任务id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                     value=\"${auditId!}\" placeholder=\"审批id\">\n            </div>\n            <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n          </form>\n        </div>\n      </div>\n    </div>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"portlet box light-grey table-responsive\" id=\"clientIndex\">\n\n          <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>appId</th>\n              <th>应用名称</th>\n              <th>诊断类型</th>\n              <th>任务id</th>\n              <th>子任务id</th>\n              <th>审批id</th>\n              <th>节点</th>\n              <th width=\"20%\" style=\"word-break: break-word\">诊断条件</th>\n\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>诊断结果</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\"><#if app_id?? && appDescMap?? && appDescMap?api.get(app_id)??>${appDescMap?api.get(app_id).name!}</#if></a>\n                </td>\n                <td>\n                  分析清理\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                    ${record.parentTaskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                    ${record.auditId!}\n                  </a>\n                </td>\n                <td>\n                  ${record.node!}\n                </td>\n                <td width=\"20%\" style=\"word-wrap:break-word;word-break:break-all;\">\n                  ${record.diagnosticCondition!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  <#if (record.status==1) && (record.type!=4)>\n                    <button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-diagnosticResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-title=\"应用${app_id!} 节点${record.node!}\">\n                      查看结果\n                    </button>\n                  </#if>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div id=\"modal-diagnosticResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          诊断结果\n          <small><label id=\"modal-title\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <table class=\"table table-bordered table-striped table-hover\">\n                <thead id=\"diagnosticResultCount\"></thead>\n                <tbody id=\"diagnosticResultTable\"></tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticSlot.html",
    "content": "<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#slot_tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        $('#slot_tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    TableManaged.init();\n  });\n\n  $('#modal-slotAnalysisResult').on('shown.bs.modal', function (e) {\n    $('#modal-slotAnalysisTitle').html('');\n    $('#slotAnalysisResultCount').html('');\n    $('#slotAnalysisResultTable').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    var title = $(e.relatedTarget).data('title');\n    var error = $(e.relatedTarget).data('error');\n    $('#modal-slotAnalysisTitle').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 5,\n              err: error\n            },\n            function (data) {\n              $('#slotAnalysisResultCount').append(\n                      '<tr>' +\n                      '<td>slot (共计' + data.count + '个）</td>' +\n                      '<td>countkeys键数;（error误差率; benchmark基准）</td>' +\n                      '</tr>'\n              );\n              var map = data.result;\n              for (var key in map) {\n                $('#slotAnalysisResultTable').append(\n                        '<tr>' +\n                        '<td>' + key + '</td>' +\n                        '<td>' + map[key] + '</td>' +\n                        '</tr>'\n                );\n              }\n            }\n    );\n\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>所有主节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function submitDiagnostic(type) {\n    var appId;\n    var nodes;\n    var params = [];\n    if (type == 5) {\n      appId = $('#slot-select').selectpicker('val');\n      if (appId == null || appId == '') {\n        alert(\"请选择应用\");\n        return;\n      }\n      nodes = $('#slot_instance-select').selectpicker('val');\n\n    }\n\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: type,\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              params: params.toString()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=slotAnalysis\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n      <div class=\"col-md-3\">\n        <select id=\"slot-select\" name=\"appId\" class=\"selectpicker show-tick w-100 border rounded\"\n                data-live-search=\"true\" title=\"选择应用\"\n                onchange=\"changeAppIdSelect(this.value,'slot_instance-select')\">\n          <option value=\"\">选择应用</option>\n          <#list appDescMap?keys as key>\n            <#assign appDesc = appDescMap?api.get(key)>\n            <option value=\"${appDesc.appId!}\" title=\"${appDesc.appId!} ${appDesc.name!}\">\n              【${appDesc.appId!}】&nbsp;名称：${appDesc.name!}&nbsp;类型：${appDesc.typeDesc!}&nbsp;版本：${appDesc.versionName!}\n            </option>\n          </#list>\n        </select>\n      </div>\n      <div class=\"col-md-3\">\n        <select id=\"slot_instance-select\" name=\"nodes\"\n                class=\"selectpicker show-tick w-100 border rounded\" multiple\n                data-live-search=\"true\" title=\"选择实例\">\n        </select>\n      </div>\n\n      <div class=\"col-auto\">\n        <button type=\"button\" class=\"btn btn-success\" onclick=\"submitDiagnostic(5)\">slot分析</button>\n      </div>\n    </form>\n  </div>\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">集群slot分析任务列表</h3>\n  </div>\n\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div style=\"float:left\">\n          <form class=\"row align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/tool/index?tabTag=slotAnalysis\" id=\"appList\" name=\"ec\">\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\"\n                     value=\"${appId!}\" placeholder=\"应用id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"parentTaskId\" name=\"parentTaskId\"\n                     value=\"${parentTaskId!}\" placeholder=\"任务id\">\n            </div>\n            <div class=\"col-md-2\">\n              <input type=\"text\" class=\"form-control\" id=\"auditId\" name=\"auditId\"\n                     value=\"${auditId!}\" placeholder=\"审批id\">\n            </div>\n\n            <div class=\"col-md-2\">\n              <select name=\"diagnosticStatus\" class=\"form-select\">\n                <option value=\"\" <#if !(diagnosticStatus??) || (diagnosticStatus?? && (diagnosticStatus == ''))>selected</#if>>\n                诊断状态\n                </option>\n                <option value=\"0\" <#if diagnosticStatus?? && (diagnosticStatus == 0)>selected</#if>>\n                诊断中\n                </option>\n                <option value=\"1\" <#if diagnosticStatus?? && (diagnosticStatus == 1)>selected</#if>>\n                诊断完成\n                </option>\n                <option value=\"2\" <#if diagnosticStatus?? && (diagnosticStatus == 2)>selected</#if>>\n                诊断异常\n                </option>\n              </select>\n            </div>\n            <button type=\"submit\" class=\"btn btn-success col-auto\">查询</button>\n          </form>\n        </div>\n      </div>\n    </div>\n    <br/>\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"table-responsive\">\n          <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"slot_tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <td>序号</td>\n              <th>appId</th>\n              <th>应用名称</th>\n              <th>诊断类型</th>\n              <th>任务id</th>\n              <th>子任务id</th>\n              <th>审批id</th>\n              <th>节点</th>\n\n              <th>创建时间</th>\n              <th>诊断状态</th>\n              <th>诊断耗时</th>\n              <th>诊断结果</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list diagnosticTaskRecordList as record>\n              <#assign app_id = record.appId>\n              <tr>\n                <td>${record_index + 1}</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${app_id!}\">${app_id!}</a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/admin/app/index?appId=${app_id!}\"><#if app_id?? && appDescMap?? && appDescMap?api.get(app_id)??>${appDescMap?api.get(app_id).name!}</#if></a>\n                </td>\n                <td>\n                  slot analysis\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.parentTaskId!}\">\n                    ${record.parentTaskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${record.taskId!}\">\n                    ${record.taskId!}\n                  </a>\n                </td>\n                <td>\n                  <a target=\"_blank\"\n                     href=\"${request.contextPath}/manage/app/auditList?auditId=${record.auditId!}\">\n                    ${record.auditId!}\n                  </a>\n                </td>\n                <td>\n                  ${record.node!}\n                </td>\n                <td>\n                  ${record.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  <#if (record.status==0)>诊断中</#if>\n                  <#if (record.status==1)>诊断完成</#if>\n                  <#if (record.status==2)>诊断异常</#if>\n                </td>\n                <td>\n                  <#if (record.status==1)>${record.formatCostTime!}</#if>\n                </td>\n                <td>\n                  <#if (record.status==1) && (record.type!=4)>\n                    <button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-slotAnalysisResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-error=\"false\"\n                            data-title=\"应用${app_id!} 节点${record.node!}\">\n                      slot分布\n                    </button>\n                    <button type=\"button\" class=\"btn btn-sm btn-info\"\n                            data-bs-target=\"#modal-slotAnalysisResult\" data-bs-toggle=\"modal\"\n                            data-rediskey=\"${record.redisKey!}\"\n                            data-error=\"true\"\n                            data-title=\"应用${app_id!} 节点${record.node!}\">\n                      误差slot\n                    </button>\n                  </#if>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n\n<div id=\"modal-slotAnalysisResult\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog\" style=\"width: 100%\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          集群slot分析结果\n          <small><label id=\"modal-slotAnalysisTitle\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"height:500px; overflow:scroll;\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <table class=\"table table-bordered table-striped table-hover\">\n                <thead id=\"slotAnalysisResultCount\"></thead>\n                <tbody id=\"slotAnalysisResultTable\"></tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/diagnosticTool.html",
    "content": "<script type=\"text/javascript\" src=\"${request.contextPath}/assets/js/custom/jquery-console.js\"></script>\n<script type=\"text/javascript\">\n  var container = $('#console');\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    $('.dropdown-toggle').on('click', function () {\n      $('.dropdown-toggle').dropdown();\n    });\n\n    var controller = container.console({\n      promptLabel: 'redis-cli > ',\n      commandValidate: function (line) {\n        if (line == \"\") return false;\n        else return true;\n      },\n      commandHandle: function (line, report) {\n        var appId = $('#cli-select').selectpicker('val');\n        if (appId == null || appId == '') {\n          alert(\"请选择应用\");\n          return false;\n        }\n\n        var node = $('#cli_instance-select').selectpicker('val');\n        if (node == null || node == '') {\n          alert(\"请选择实例\");\n          return false;\n        }\n\n        var timeout = $('#timeout').val();\n\n\n        $.post('${request.contextPath}/manage/app/tool/commandExecute',\n                {\n                  appId: appId,\n                  node: node,\n                  command: line,\n                  timeout: timeout\n                },\n                function (data) {\n                  report([\n                    {msg: data.result, className: \"jquery-console-message-value\"}\n                  ]);\n                }\n        );\n      },\n      autofocus: true,\n      animateScroll: true,\n      promptHistory: true\n    });\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    console.log('instance_select:' + instance_select);\n    console.log(appId);\n\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function testisNum(id) {\n    var value = document.getElementById(id).value;\n    if (value != \"\" && isNaN(value)) {\n      alert(\"超时时间请填入整数，单位为ms\");\n      document.getElementById(id).value = \"\";\n      document.getElementById(id).focus();\n    }\n  }\n</script>\n\n<link href=\"${request.contextPath}/assets/css/mem-cloud.css\" rel=\"stylesheet\" />\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div id=\"scan-div\" class=\"col-md-12\">\n      <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n        <label for=\"cli-select\" class=\"col-auto\">应用</label>\n        <div class=\"col-md-3\">\n          <select id=\"cli-select\" name=\"appId\" class=\"selectpicker border rounded\"\n                  data-live-search=\"true\" title=\"选择应用\" onchange=\"changeAppIdSelect(this.value,'cli_instance-select')\">\n            <option value=\"\">选择应用</option>\n            <#list appDescMap?keys as key>\n              <#assign appDesc = appDescMap?api.get(key)>\n              <option value=\"${appDesc.appId}\" title=\"${appDesc.appId} ${appDesc.name}\">\n                【${appDesc.appId}】&nbsp;名称：${appDesc.name}&nbsp;类型：${appDesc.typeDesc}&nbsp;版本：${appDesc.versionName}\n              </option>\n            </#list>\n          </select>\n        </div>\n        <label for=\"cli_instance-select\" class=\"col-auto\">实例</label>\n        <div class=\"col-md-3\">\n          <select id=\"cli_instance-select\" name=\"nodes\"\n                  class=\"selectpicker show-tick border rounded\"\n                  data-live-search=\"true\" title=\"选择实例\" data-size=\"8\">\n          </select>\n        </div>\n        <label class=\"bi bi-question-circle-fill col-auto\" aria-hidden=\"true\" title=\"超时时间，单位ms，默认30000ms\"></label>\n        <div class=\"col-md-3\">\n          <input id=\"timeout\" type=\"text\" class=\"form-control\" name=\"timeout\"\n                 placeholder=\"超时时间（默认30000ms）\" onchange=\"testisNum(this.id)\">\n        </div>\n      </form>\n\n      <br/>\n      <div class=\"col-md-12\">\n        <a class=\"bi bi-file-earmark-text-fill\">\n          输入 --help 查看可用命令\n        </a>\n\n      </div>\n      <br/>\n      <div class=\"col-md-12\">\n        <form role=\"form\">\n          <div id=\"console\" class=\"console\"></div>\n        </form>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/index.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"tabbable-custom\">\n            <div class=\"card\">\n                <div class=\"card-body\">\n                    <nav class=\"nav\">\n                        <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                            <li id=\"redis-cli\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?tabTag=redis-cli\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=redis-cli\">redis-cli工具</a>\n                            </li>\n                            <li id=\"scan\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?appId=${appId!}&parentTaskId=${parentTaskId!}&auditId=${auditId!}&diagnosticStatus=${diagnosticStatus!}&tabTag=scan\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=scan\">scan检测</a>\n                            </li>\n                            <li id=\"memoryUsed\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?appId=${appId!}&parentTaskId=${parentTaskId!}&auditId=${auditId!}&diagnosticStatus=${diagnosticStatus!}&tabTag=memoryUsed\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=memoryUsed\">memoryUsed诊断</a>\n                            </li>\n                            <li id=\"idlekey\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?appId=${appId!}&parentTaskId=${parentTaskId!}&auditId=${auditId!}&diagnosticStatus=${diagnosticStatus!}&tabTag=idlekey\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=idlekey\">idlekey诊断</a>\n                            </li>\n                            <li id=\"hotkey\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?appId=${appId!}&parentTaskId=${parentTaskId!}&auditId=${auditId!}&diagnosticStatus=${diagnosticStatus!}&tabTag=hotkey\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=hotkey\">hotkeys/bigkeys/memkeys诊断</a>\n                            </li>\n                            <li id=\"deleteKey\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?appId=${appId!}&parentTaskId=${parentTaskId!}&auditId=${auditId!}&diagnosticStatus=${diagnosticStatus!}&tabTag=deleteKey\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=deleteKey\">删除任务</a>\n                            </li>\n                            <li id=\"slotAnalysis\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?appId=${appId!}&parentTaskId=${parentTaskId!}&auditId=${auditId!}&diagnosticStatus=${diagnosticStatus!}&tabTag=slotAnalysis\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=slotAnalysis\">集群slot分析</a>\n                            </li>\n                            <li id=\"scanClean\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/tool/diagnostic/tool?appId=${appId!}&parentTaskId=${parentTaskId!}&auditId=${auditId!}&diagnosticStatus=${diagnosticStatus!}&tabTag=scanClean\">\n                                <a class=\"nav-link d-flex\" href=\"?tabTag=scanClean\">数据清理任务</a>\n                            </li>\n                        </ul>\n                    </nav>\n                </div>\n            </div>\n            <div class=\"tab-content\">\n                <div class=\"tab-pane active\" id=\"redis-cliTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"scanTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"memoryUsedTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"idlekeyTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"hotkeyTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"deleteKeyTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"slotAnalysisTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"scanCleanTab\">\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n<script type=\"text/javascript\">\n    function showTab(tab) {\n        $.get($(\"#\" + tab).attr(\"data-url\"), function (result) {\n            $(\"#\" + tab + \"Tab\").html(result);\n        });\n    }\n\n    function refreshActiveTab() {\n        var tab = getQueryString(\"tabTag\");\n        if (tab) {\n            $(\"#\" + tab + \" a\").addClass(\"active\");\n            $(\"#\" + tab + \"Tab\").addClass(\"active\").siblings().removeClass(\"active\");\n        } else {\n            tab = \"redis-cli\";\n            $(\"#\" + tab + \" a\").addClass(\"active\");\n        }\n        console.log(\"tab:\" + tab)\n        showTab(tab);\n        $(\"#tabs li a\").tooltip({placement: \"bottom\"});\n    }\n\n    $(function () {\n        refreshActiveTab();\n    });\n\n    function getQueryString(name) {\n        var reg = new RegExp(\"(^|&)\" + name + \"=([^&]*)(&|$)\");\n        var r = window.location.search.substr(1).match(reg);\n        if (r != null) return unescape(r[2]);\n        return null;\n    }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"index.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/diagnosticTool/resultList.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body>\n\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <main id=\"main\" class=\"main\">\n    <section class=\"section dashboard\">\n      <div class=\"row\">\n        <#include \"diagnosticResult.html\">\n      </div>\n    </section>\n\n  </main><!-- End #main -->\n\n  <!-- ======= Footer ======= -->\n  <footer id=\"footer\" class=\"footer\">\n    <div class=\"copyright\">\n      &copy; Copyright <strong><span>Cachecloud</span></strong>. All Rights Reserved\n    </div>\n  </footer><!-- End Footer -->\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n\n  <script>\n    var TableManaged = function () {\n      return {\n        //main function to initiate the module\n        init: function () {\n\n          if (!jQuery().dataTable) {\n            return;\n          }\n\n          $('#tableDataList').dataTable({\n            \"searching\": true,\n            \"lengthChange\": false,\n            \"pageLength\": 20,\n            \"language\": {\n              \"lengthMenu\": \"Display _MENU_ records\",\n              \"paginate\": {\n                \"previous\": \"<\",\n                \"next\": \">\"\n              },\n              \"info\": \"共_PAGES_页,_TOTAL_条\",\n              \"infoFiltered\": \"\",\n              \"infoEmpty\":\"共0页,0条\",\n              \"zeroRecords\": \"没有找到符合条件的数据\",\n            }\n          });\n\n          jQuery('#tableDataList_wrapper>div:first-child').css(\"display\",\"none\");\n        }\n      };\n    }();\n\n    jQuery(document).ready(function() {\n      TableManaged.init();\n    });\n\n    $('#modal-diagnosticResult').on('shown.bs.modal', function (e) {\n      $('#modal-title').html('');\n      $('#diagnosticResultCount').html('');\n      $('#diagnosticResultTable').html('');\n\n      var redisKey = $(e.relatedTarget).data('rediskey');\n      var title = $(e.relatedTarget).data('title');\n      $('#modal-title').html(title);\n      $.get(\n              '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n              {\n                redisKey: redisKey\n              },\n              function (data) {\n                $('#diagnosticResultCount').append(\n                        '<tr>' +\n                        '<td>key (共计' + data.count + '个）</td>' +\n                        '</tr>'\n                );\n                var diagnosticResultList = data.result;\n                diagnosticResultList.forEach(function (diagnosticResult, index) {\n                  $('#diagnosticResultTable').append(\n                          '<tr>' +\n                          '<td>' + diagnosticResult + '</td>' +\n                          '</tr>'\n                  );\n                })\n              }\n      );\n\n    });\n\n  </script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/fault/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"memFaultList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/fault/memFaultList.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card-header\">\n      <h3 class=\"card-title\">\n        <i class=\"bi bi-globe\"></i>故障列表\n      </h3>\n    </div>\n    <div class=\"card-body\">\n      <div class=\"row\">\n        <div class=\"col-md-12\">\n          <div class=\"portlet box light-grey\" id=\"clientIndex\">\n\n            <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n              <thead>\n              <tr>\n                <th>AppID</th>\n                <th>名称</th>\n                <th>IP</th>\n                <th>PORT</th>\n                <th>M/S</th>\n                <th>Time</th>\n                <th>联系人</th>\n                <th>IsMemCloud</th>\n                <th>IsRecover</th>\n                <th>原因</th>\n              </tr>\n              </thead>\n              <tbody>\n              <#list faults as item>\n                <tr>\n                  <td>${item.appId!}</td>\n                  <td>2</td>\n                  <td>${item.ip!}</td>\n                  <td>${item.port!}</td>\n                  <td>5</td>\n                  <td>${item.createTime!}</td>\n                  <td>7</td>\n                  <td>${item.isMemcloud!}</td>\n                  <td>7</td>\n                  <td>${item.reason!}</td>\n                </tr>\n              </#list>\n              </tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/inc/backendEndResources.html",
    "content": "<!-- Template Main JS File -->\n<script src=\"${request.contextPath}/assets/js/main.js\"></script>\n<!-- AdminLTE App -->\n<script src=\"${request.contextPath}/assets/plugins/overlayScrollbars/js/jquery.overlayScrollbars.js\"></script>\n<script src=\"${request.contextPath}/assets/dist/js/adminlte.js\"></script>\n<script src=\"${request.contextPath}/assets/dist/js/demo.js\"></script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/inc/backendResources.html",
    "content": "<!-- Vendor CSS Files -->\n<link href=\"${request.contextPath}/assets/vendor/bootstrap/css/bootstrap.min.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-icons/bootstrap-icons.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.css\" rel=\"stylesheet\" />\n<link href=\"${request.contextPath}/assets/vendor/boxicons/css/boxicons.min.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/quill/quill.snow.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/quill/quill.bubble.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/remixicon/remixicon.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/simple-datatables/style.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/data-tables/css/dataTables.bootstrap5.css\" rel=\"stylesheet\">\n\n\n<!-- Google Font: Source Sans Pro -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/vendor/fonts/fonts-googleapis-source-sans-pro.css\">\n<!-- Font Awesome -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/fontawesome-free/css/all.min.css\">\n<!-- Theme style -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/dist/css/adminlte.min.css\">\n<!-- overlayScrollbars -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/overlayScrollbars/css/OverlayScrollbars.min.css\">\n\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/css/custom.css\">\n\n<!-- Vendor JS Files -->\n<script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/apexcharts/apexcharts.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap/js/bootstrap.bundle.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/chart.js/chart.umd.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/echarts/echarts.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/quill/quill.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/data-tables/js/jquery.dataTables.js\" type=\"text/javascript\"></script>\n<script src=\"${request.contextPath}/assets/vendor/data-tables/js/dataTables.bootstrap5.js\" type=\"text/javascript\"></script>\n<script src=\"${request.contextPath}/assets/vendor/simple-datatables/simple-datatables.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/tinymce/tinymce.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/php-email-form/validate.js\"></script>\n<script src=\"${request.contextPath}/assets/js/custom/auditManage.js\" type=\"text/javascript\"></script>\n<script src=\"${request.contextPath}/assets/js/custom/userManage.js\" type=\"text/javascript\"></script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/inc/footer.html",
    "content": "<!-- ======= Footer ======= -->\n<footer id=\"footer\" class=\"main-footer\">\n    <strong>Copyright &copy; Cachecloud.</strong>\n    All Rights Reserved.\n    <div class=\"float-right d-none d-sm-inline-block\">\n        <b>Version</b> 3.0.0\n    </div>\n</footer><!-- End Footer -->"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/inc/head.html",
    "content": "<!-- Navbar -->\n<nav class=\"main-header navbar navbar-expand navbar-white navbar-light\">\n    <!-- Left navbar links -->\n    <ul class=\"navbar-nav\">\n        <li class=\"nav-item\">\n            <a class=\"nav-link\" data-widget=\"pushmenu\" href=\"#\" role=\"button\"><i class=\"fas fa-bars\"></i></a>\n        </li>\n    </ul>\n\n    <!-- Right navbar links -->\n    <ul class=\"navbar-nav ml-auto\">\n        <!-- Messages Dropdown Menu -->\n        <li class=\"nav-item dropdown\">\n            <a class=\"nav-link\" data-bs-toggle=\"dropdown\" href=\"#\">\n                <i class=\"bi bi-gear-fill\"></i>\n                <span class=\"dropdown-toggle ps-2\">\n                        <#if userInfo?? && userInfo.chName??>\n                            ${userInfo.chName!}\n                        </#if>\n                </span>\n            </a>\n            <ul class=\"dropdown-menu dropdown-menu-end dropdown-menu-arrow profile\">\n                <li>\n                    <a target=\"_blank\" class=\"dropdown-item d-flex align-items-center\" href=\"${request.contextPath}/admin/app/list\">\n                        <i class=\"bi bi-house\"></i>\n                        &nbsp;&nbsp;&nbsp;&nbsp;<span>应用前台</span>\n                    </a>\n                </li>\n                <li>\n                    <hr class=\"dropdown-divider\">\n                </li>\n\n                <li>\n                    <#if userInfo?? && userInfo.id??>\n                    <#assign userInfoId = userInfo.id!>\n                    <#else>\n                    <#assign userInfoId = ''>\n                </#if>\n                <a class=\"dropdown-item d-flex align-items-center\" data-bs-target=\"#updateUserModal${userInfoId!}\" data-bs-toggle=\"modal\">\n                    <i class=\"bi bi-gear\"></i>\n                    &nbsp;&nbsp;&nbsp;&nbsp;<span>修改资料</span>\n                </a>\n                </li>\n                <li>\n                    <hr class=\"dropdown-divider\">\n                </li>\n\n                <li>\n                    <a class=\"dropdown-item d-flex align-items-center\" href=\"${request.contextPath}/manage/logout\">\n                        <i class=\"bi bi-box-arrow-right\"></i>\n                        &nbsp;&nbsp;&nbsp;&nbsp;<span>注销</span>\n                    </a>\n                </li>\n\n            </ul><!-- End Profile Dropdown Items -->\n        </li>\n        <li class=\"nav-item\">\n            <a class=\"nav-link\" data-widget=\"fullscreen\" href=\"#\" role=\"button\">\n                <i class=\"fas fa-expand-arrows-alt\"></i>\n            </a>\n        </li>\n        <li class=\"nav-item\">\n            <a class=\"nav-link\" data-widget=\"control-sidebar\" data-controlsidebar-slide=\"true\" href=\"#\" role=\"button\">\n                <i class=\"fas fa-th-large\"></i>\n            </a>\n        </li>\n    </ul>\n</nav>\n<!-- /.navbar -->\n\n<#if userInfo??>\n    <#assign headUser = userInfo>\n</#if>\n<#include \"/manage/user/updateUser.html\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/inc/left.html",
    "content": "<!-- ======= Sidebar ======= -->\n<aside id=\"sidebar\" class=\"main-sidebar sidebar-dark-primary elevation-4\">\n\n    <!-- Brand Logo -->\n    <a href=\"${request.contextPath}/manage/total/statlist\" class=\"brand-link\">\n        <img src=\"${request.contextPath}/assets/img/logo_new.png\" alt=\"Cachecloud\" class=\"brand-image img-rounded elevation-0\" style=\"opacity: .8\">\n        <span class=\"brand-text font-weight-light\">Cachecloud</span>\n    </a>\n\n    <div class=\"sidebar\">\n\n        <!-- SidebarSearch Form -->\n        <div class=\"form-inline\">\n            <div class=\"input-group\" data-widget=\"sidebar-search\">\n                <input class=\"form-control form-control-sidebar\" type=\"search\" placeholder=\"Search\" aria-label=\"Search\">\n                <div class=\"input-group-append\">\n                    <button class=\"btn btn-sidebar\">\n                        <i class=\"fas fa-search fa-fw\"></i>\n                    </button>\n                </div>\n            </div>\n        </div>\n\n        <!-- Sidebar Menu -->\n        <nav class=\"mt-2\">\n            <ul class=\"nav nav-pills nav-sidebar flex-column\" data-widget=\"treeview\" role=\"menu\" data-accordion=\"false\" id=\"sidebar-nav\">\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if totalActive?? && (totalActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/total/statlist\">\n                        <i class=\"bi bi-grid\"></i>\n                        <p>全局统计</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if appStatActive?? && (appStatActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/app/stat/list\">\n                        <i class=\"bi bi-bar-chart\"></i>\n                        <p>client统计</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if appStatServerActive?? && (appStatServerActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/app/stat/list/server\">\n                        <i class=\"bi bi-bar-chart\"></i>\n                        <p>server统计</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if checkActive?? && (checkActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/app/auditList\">\n                        <i class=\"bi bi-fast-forward\"></i>\n                        <p>工单审批</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item\">\n                    <a id=\"appOpsTab\" class=\"nav-link <#if appOperateActive?? && (appOperateActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/total/list\">\n                        <i class=\"bi bi-card-list\"></i>\n                        <p>应用运维</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if insOperateActive?? && (insOperateActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/instance/opsList\">\n                        <i class=\"bi bi-view-list\"></i>\n                        <p>实例运维</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if appImportActive?? && (appImportActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/import/app/index\">\n                        <i class=\"bi bi-box-arrow-in-down\"></i>\n                        <p>应用导入</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if appMigrateActive?? && (appMigrateActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/data/migrate/index\">\n                        <i class=\"bi bi-database-down\"></i>\n                        <p>数据迁移</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if diagnosticActive?? && (diagnosticActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/app/tool/index\">\n                        <i class=\"bi bi-question-circle\"></i>\n                        <p>诊断工具</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if machineActive?? && (machineActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/machine/index\">\n                        <i class=\"bi bi-wrench\"></i>\n                        <p>机器管理</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link\">\n                        <i class=\"bi bi-exclamation-triangle\"></i>\n                        <p>报警配置</p>\n                        <i class=\"fas fa-angle-left right\"></i>\n                    </a>\n                    <ul class=\"nav nav-treeview <#if redisAlertValueActive??>menu-open</#if>\" style=\"display: none;\">\n                        <li class=\"nav-item\">\n                            <a href=\"${request.contextPath}/manage/instanceAlert/init\" class=\"nav-link <#if redisAlertValueActive?? && (redisAlertValueActive == 1)>active<#else>collapsed</#if>\">\n                                <i class=\"far fa-circle nav-icon\"></i>\n                                <p>Redis报警配置</p>\n                            </a>\n                        </li>\n                    </ul>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if configActive?? && (configActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/config/init\">\n                        <i class=\"bi bi-gear\"></i>\n                        <p>系统配置</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if reourcesActive?? && (reourcesActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/app/resource/index\">\n                        <i class=\"bi bi-cloud\"></i>\n                        <p>资源管理</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link\">\n                        <i class=\"bi bi-palette\"></i>\n                        <p>模版配置</p>\n                        <i class=\"fas fa-angle-left right\"></i>\n                    </a>\n                    <ul class=\"nav nav-treeview <#if redisConfigActive??>menu-open</#if>\" style=\"display: none;\">\n                        <li class=\"nav-item\">\n                            <a href=\"${request.contextPath}/manage/redisConfig/init\" class=\"nav-link <#if redisConfigActive?? && (redisConfigActive == 1)>active<#else>collapsed</#if>\">\n                                <i class=\"far fa-circle nav-icon\"></i>\n                                <p>Redis配置模版</p>\n                            </a>\n                        </li>\n                    </ul>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if taskActive?? && (taskActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/task/list?status=1\">\n                        <i class=\"bi bi-chat-left-text\"></i>\n                        <p>任务管理</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if quartzActive?? && (quartzActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/quartz/list\">\n                        <i class=\"bi bi-list-task\"></i>\n                        <p>调度任务</p>\n                    </a>\n                </li>\n\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if userActive?? && (userActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/user/list\">\n                        <i class=\"bi bi-person-gear\"></i>\n                        <p>用户管理</p>\n                    </a>\n                </li>\n                <li class=\"nav-item\">\n                    <a class=\"nav-link <#if noticeActive?? && (noticeActive == 1)>active<#else>collapsed</#if>\" href=\"${request.contextPath}/manage/notice/initNotice\">\n                        <i class=\"bi bi-bell\"></i>\n                        <p>系统通知</p>\n                    </a>\n                </li>\n            </ul>\n        </nav>\n    </div>\n\n</aside><!-- End Sidebar-->\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/inc/page.html",
    "content": "<script type='text/javascript'>\n    $(function(){\n        var totalPageValue = <#if page?? && page.totalPages??>${page.totalPages!}<#else>0</#if>;\n        var pageNoValue = <#if page?? && page.pageNo??>${page.pageNo!}<#else>1</#if>;\n        refreshPages(totalPageValue,pageNoValue);\n    });\n\n    function refreshPages(totalPage, currentPage) {\n        //安全判断\n        if (currentPage < 1 ) {\n            currentPage = 1;\n        }else if (currentPage > totalPage) {\n            currentPage = totalPage;\n        }\n        var paginationInfo = getPagination(totalPage, currentPage);\n        //用id选择器写入页码部分代码（根据需求修改）\n        $(\"#ccPagenitor\").html(paginationInfo);\n    }\n\n    function searchJob(page){\n        document.getElementById(\"pageNo\").value=page;\n        document.getElementById(\"pageForm\").submit();\n    }\n    function getPagination(totalPage, currentPage){\n\n        var paginationInfo = \"<ul class='pagination' style='flex-grow: 1'>\";\n        if (currentPage == 1) {\n            paginationInfo += \"<li class='page-item disabled'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage - 1) + \")'\"+\">«</a></li>\";\n        }else {\n            //前一页\n            paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage - 1) + \")'\"+\">«</a></li>\";\n        }\n\n        if(totalPage <= 10){\n            //totalPage<=10\n            for(var i = 1; i <= totalPage; i++){\n                if (i == currentPage) {\n                    paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \" </a></li>\";\n                }else {\n                    paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \" </a></li>\";\n                }\n            }\n        } else{\n            //totalPage > 10\n            if(currentPage <= 3){\n                for(var i = 1; i <= currentPage + 2; i++){\n                    //页码1、2\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    } else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    }\n                }\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后一页的页码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + totalPage + \")'>\" + totalPage + \"</a></li>\";\n            }else if(currentPage <= totalPage - 5){\n                //totalPage > 10   currentPage > 3 currentPage<=totalPage-5，  页码在中间部分\n                //页码为1的代码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(1)'>\" + 1 + \"</a></li>\";\n\n                //页码1后面的省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n\n                //中间部分代码\n                for(var i = currentPage - 1; i <= currentPage + 2; i++){\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    } else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\" + i + \"</a></li>\";\n                    }\n                }\n                //后面的省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后一个页码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + totalPage + \")'>\" + totalPage + \"</a></li>\";\n            }else{\n                //totalPage > 10  并且currentPage > totalPage-5 显示后面的页码\n\n                //页码1\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(1)'>\"+1+\"</a></li>\";\n                //省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后几位页码\n                for(var i = currentPage - 1; i <= totalPage; i++){\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \"'>\" + i + \"</a></li>\";\n                    }else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \")'>\"+i+\"</a></li>\";\n                    }\n                }\n            }\n        }\n\n        //下一页\n        if (currentPage == totalPage) {\n            paginationInfo += \"<li class='page-item disabled'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage + 1) + \")'\" + \">»</a></li>\";\n        } else {\n            paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage + 1) + \")'\"+\">»</a></li>\";\n        }\n        paginationInfo += \"</ul>\";\n        //返回结果\n        return paginationInfo;\n    }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instance/log.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>Redis日志</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n</head>\n<body>\n\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n  <#if instanceLogList?? && instanceLogList?size gt 0>\n    <#list instanceLogList as line>\n      <#assign targetColor = \"white\">\n      <#if line?contains(\"#\")>\n        <#assign targetColor = \"red\">\n      </#if>\n      <font color=\"${targetColor}\">${line!}</font><br/>\n    </#list>\n  </#if>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceAlert/init.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"initConfigDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceAlert/initConfigDetail.html",
    "content": "<script type=\"text/javascript\">\n\n    //查看实例是否存在\n    function checkInstanceExist(){\n        var instanceHostPort = document.getElementById(\"instanceHostPort\").value;\n        if (instanceHostPort == null || instanceHostPort == \"\"){\n            alert(\"请填写实例ip:port\");\n            instanceHostPort.focus();\n            return false;\n        }\n        if(instanceHostPort != ''){\n            $.post(\n                '${request.contextPath}/manage/instanceAlert/checkInstanceHostPort.json',\n                {\n                    instanceHostPort: instanceHostPort,\n                },\n                function(data){\n                    var success = data.status;\n                    if(success==0){\n                        alert(data.message);\n                        document.getElementById(\"instanceHostPort\").focus();\n                    }\n                }\n            );\n        }\n    }\n\n    function removeAlertConfig(id) {\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/remove.json',\n            {\n                id: id\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"删除成功!\");\n                } else {\n                    alert(\"删除失败, msg: \" + result.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n\n    function changeAlertConfig(id) {\n        var alertValue = document.getElementById(\"alertValue\" + id);\n        var checkCycle = document.getElementById(\"checkCycle\" + id);\n        var compareType = document.getElementById(\"compareType\" + id);\n        var importantLevel = document.getElementById(\"importantLevel\" + id);\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/update.json',\n            {\n                id: id,\n                alertValue: alertValue.value,\n                checkCycle: checkCycle.value,\n                compareType: compareType.value,\n                importantLevel: importantLevel.value\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"修改成功！\");\n                    window.location.reload();\n                } else {\n                    alert(\"修改失败！\" + data.message);\n                }\n\n            }\n        );\n    }\n\n    function saveInstanceAlertConfig() {\n        var alertConfig = document.getElementById(\"alertConfig\");\n        var alertValue = document.getElementById(\"alertValue\");\n        if (alertValue.value == \"\"){\n            alert(\"请填写阈值\");\n            alertValue.focus();\n            return false;\n        }\n        var compareType = document.getElementById(\"compareType\");\n        var checkCycle = document.getElementById(\"checkCycle\");\n        var instanceHostPort = document.getElementById(\"instanceHostPort\");\n        if (instanceHostPort.value == null || instanceHostPort.value == \"\"){\n            alert(\"请填写实例ip:port\");\n            instanceHostPort.focus();\n            return false;\n        }\n        var type = 1;\n        if (instanceHostPort.value != null && instanceHostPort.value != '') {\n            type = 2;\n        }\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/add.json',\n            {\n                alertConfig: alertConfig.value,\n                alertValue: alertValue.value,\n                configInfo: alertConfig.options[alertConfig.selectedIndex].text,\n                compareType: compareType.value,\n                checkCycle: checkCycle.value,\n                instanceHostPort: instanceHostPort.value,\n                appType: 0,\n                type: type\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"添加成功！\");\n                } else {\n                    alert(\"添加失败！\" + data.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n\n    //保存全局实例\n    function saveGlobalInstanceAlertConfig() {\n        var alertConfig = document.getElementById(\"alertConfigGlobal\");\n        if (alertConfig.value == \"\"){\n            alert(\"请填写配置名\");\n            alertConfig.focus();\n            return false;\n        }\n        var alertValue = document.getElementById(\"alertValueGlobal\");\n        if (alertValue.value == \"\"){\n            alert(\"请填写阈值\");\n            alertValue.focus();\n            return false;\n        }\n        var configInfo = document.getElementById(\"configInfoGlobal\");\n        if (configInfo.value == \"\"){\n            alert(\"请填写配置说明\");\n            configInfo.focus();\n            return false;\n        }\n        var compareType = document.getElementById(\"compareTypeGlobal\");\n        var checkCycle = document.getElementById(\"checkCycleGlobal\");\n        var importantLevel = document.getElementById(\"importantLevel\");\n        var type = 1;\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/add.json',\n            {\n                alertConfig: alertConfig.value,\n                configInfo: configInfo.value,\n                alertValue: alertValue.value,\n                compareType: compareType.value,\n                checkCycle: checkCycle.value,\n                importantLevel: importantLevel.value,\n                appType: 0,\n                type: type\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"添加成功！\");\n                } else {\n                    alert(\"添加失败！\" + data.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n\n    // 应用添加报警\n    function saveAppAlertConfig() {\n        var alertConfig = document.getElementById(\"alertAppConfig\");\n        var alertValue = document.getElementById(\"alertAppValue\");\n        if (alertValue.value == \"\"){\n            alert(\"请填写阈值\");\n            alertValue.focus();\n            return false;\n        }\n        var compareType = document.getElementById(\"compareAppType\");\n        var checkCycle = document.getElementById(\"checkAppCycle\");\n        var appid = document.getElementById(\"appid\");\n        if (appid.value == \"\"){\n            alert(\"请填写appid\");\n            appid.focus();\n            return false;\n        }\n\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/addApp.json',\n            {\n                alertConfig: alertConfig.value,\n                alertValue: alertValue.value,\n                configInfo: alertConfig.options[alertConfig.selectedIndex].text,\n                compareType: compareType.value,\n                checkCycle: checkCycle.value,\n                appType: 0,\n                appid: appid.value\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"添加成功！\");\n                } else {\n                    alert(\"添加失败！\" + data.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n</script>\n<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <div class=\"row\">\n                    <div class=\"col-md-4\">\n                        <h3 class=\"card-title\">\n                            <i class=\"bi bi-globe\"></i>\n                            全局实例报警项:\n                        </h3>\n                    </div>\n                    <div class=\"col-md-8 text-end\">\n                        <button type=\"button\" class=\"btn btn-success\" style=\"margin-top: 0px\" data-bs-target=\"#addGlobalInstanceAlertModal\" data-bs-toggle=\"modal\" href=\"#\">添加全局报警项</button>\n                    </div>\n                </div>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"table-responsive\">\n                    <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n                        <thead>\n                        <tr>\n                            <th>id</th>\n                            <th>配置名</th>\n                            <th>配置说明</th>\n                            <th>关系</th>\n                            <th>阀值</th>\n                            <th>周期</th>\n                            <th>重要程度</th>\n                            <th>最近检测时间</th>\n                            <th style=\"white-space: nowrap\">操作</th>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <#list instanceAlertAllList as config>\n                            <tr>\n                                <td>\n                                    ${config.id!}\n                                </td>\n                                <td>\n                                    ${config.alertConfig!}\n                                </td>\n                                <td>\n                                    ${config.configInfo!}\n                                </td>\n                                <td>\n                                    <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                        <#if (config.compareType == instanceAlertCompareTypeEnum.value)>${instanceAlertCompareTypeEnum.info!}</#if>\n                                    </#list>\n                                </td>\n                                <td>\n                                    ${config.alertValue!}\n                                </td>\n                                <td>\n                                    <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                        <#if (config.checkCycle == instanceAlertCheckCycleEnum.value)>${instanceAlertCheckCycleEnum.info!}</#if>\n                                    </#list>\n                                </td>\n\n                                <td>\n                                    <#if (config.importantLevel == 0)>一般</#if>\n                                    <#if (config.importantLevel == 1)>重要</#if>\n                                    <#if (config.importantLevel == 2)>紧急</#if>\n                                </td>\n\n                                <td>\n                                    ${config.lastCheckTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                </td>\n                                <td style=\"white-space: nowrap\">\n                                    <button type=\"button\" class=\"btn btn-info\" data-bs-target=\"#changeInstanceAlertModal${config.id!}\" data-bs-toggle=\"modal\" href=\"#\">修改</button>\n                                    <button type=\"button\" class=\"btn btn-info\" onclick=\"if(window.confirm('确认要清除id=${config.id!}的配置?!')){removeAlertConfig('${config.id!}');return true;}else{return false;}\">删除</button>\n                                </td>\n                            </tr>\n                        </#list>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </div>\n\n\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <div class=\"row\">\n                    <div class=\"col-md-4\">\n                        <h3 class=\"card-title\">\n                            <i class=\"bi bi-globe\"></i>\n                            特殊实例报警:\n                        </h3>\n                    </div>\n                    <div class=\"col-md-8 text-end\">\n                        <div class=\"btn-group\">\n                            <button type=\"button\" class=\"btn btn-success\" style=\"margin-top: 0px\" data-bs-target=\"#addAppAlertModal\" data-bs-toggle=\"modal\" href=\"#\">添加应用报警项</button>\n                        </div>\n                        <div class=\"btn-group\">\n                            <button type=\"button\" class=\"btn btn-success\" style=\"margin-top: 0px\" data-bs-target=\"#addInstanceAlertModal\" data-bs-toggle=\"modal\" href=\"#\">添加新实例报警项</button>\n                        </div>\n                    </div>\n                </div>\n            </div>\n                <!-- END TABLE PORTLET-->\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <div class=\"table-responsive\">\n                        <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n                            <thead>\n                            <tr>\n                                <th>id</th>\n                                <th>实例信息</th>\n                                <th>配置名</th>\n                                <th>配置说明</th>\n                                <th>关系</th>\n                                <th>阀值</th>\n                                <th>周期</th>\n                                <th>最近检测时间</th>\n                                <th>操作</th>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            <#list instanceAlertSpecialList as config>\n                                <tr>\n                                    <td>\n                                        ${config.id!}\n                                    </td>\n                                    <#assign instanceId = config.instanceId>\n                                    <td>\n                                        <#if (config.type == 2)>\n                                            ${config.instanceInfo.hostPort!}\n                                            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${config.instanceInfo.appId!}\">(${config.instanceInfo.appId!})</a>\n                                        </#if>\n                                        <#if (config.type == 3)>\n                                            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${config.instanceId!}\">${config.instanceId!}</a>\n                                        </#if>\n                                    </td>\n                                    <td>\n                                        ${config.alertConfig!}\n                                    </td>\n                                    <td>\n                                        ${config.configInfo!}\n                                    </td>\n                                    <td>\n                                        <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                            <#if (config.compareType == instanceAlertCompareTypeEnum.value)>${instanceAlertCompareTypeEnum.info!}</#if>\n                                        </#list>\n                                    </td>\n                                    <td>\n                                        ${config.alertValue!}\n                                    </td>\n                                    <td>\n                                        <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                            <#if (config.checkCycle == instanceAlertCheckCycleEnum.value)>${instanceAlertCheckCycleEnum.info!}</#if>\n                                        </#list>\n                                    </td>\n                                    <td>\n                                        ${config.lastCheckTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                    </td>\n                                    <td>\n                                        <button type=\"button\" class=\"btn btn-info\" data-bs-target=\"#changeInstanceAlertModal${config.id!}\" data-bs-toggle=\"modal\" href=\"#\">修改</button>\n                                        <button type=\"button\" class=\"btn btn-info\" onclick=\"if(window.confirm('确认要清除id=${config.id!}的配置?!')){removeAlertConfig('${config.id!}');return true;}else{return false;}\">删除</button>\n                                    </td>\n                                </tr>\n                            </#list>\n                            </tbody>\n                        </table>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n\n<!-- 单实例报警项 -->\n<div id=\"addInstanceAlertModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">添加实例报警项</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置名:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"alertConfig\" id=\"alertConfig\" class=\"form-select select2_category\">\n                                            <#list redisUsedGlobalAlertConfigList as redisAlertConfig>\n                                                <option value=\"${redisAlertConfig.value!}\">\n                                                    ${redisAlertConfig.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        比较:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"compareType\" id=\"compareType\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                <option value=\"${instanceAlertCompareTypeEnum.value!}\">\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        阀值:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertValue\" id=\"alertValue\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        实例:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"instanceHostPort\" id=\"instanceHostPort\"\n                                               class=\"form-control\" placeholder=\"单个实例ip:port\" onchange=\"checkInstanceExist()\"/>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        周期:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"checkCycle\" id=\"checkCycle\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                <option value=\"${instanceAlertCheckCycleEnum.value!}\">\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"configBtn\" class=\"btn btn-danger\" onclick=\"saveInstanceAlertConfig()\">Ok</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n\n<!-- 全局实例报警项 -->\n<div id=\"addGlobalInstanceAlertModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">添加全局实例报警项</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置名:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertConfig\" id=\"alertConfigGlobal\"\n                                               class=\"form-control\" placeholder=\"参照redis info中的字段名\"/>\n                                    </div>\n                                </div>\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置说明:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"configInfo\" id=\"configInfoGlobal\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        比较:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"compareType\" id=\"compareTypeGlobal\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                <option value=\"${instanceAlertCompareTypeEnum.value!}\">\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        阀值:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertValue\" id=\"alertValueGlobal\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        周期:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"checkCycle\" id=\"checkCycleGlobal\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                <option value=\"${instanceAlertCheckCycleEnum.value!}\">\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        重要程度:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"importantLevel\" id=\"importantLevel\" class=\"form-select select2_category\">\n                                            <option value=\"0\" selected>一般</option>\n                                            <option value=\"1\">重要</option>\n                                            <option value=\"2\">紧急</option>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"configBtnGlobal\" class=\"btn btn-danger\" onclick=\"saveGlobalInstanceAlertConfig()\">Ok</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n\n<!-- 应用报警项 -->\n<div id=\"addAppAlertModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">添加应用报警项</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置名:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"alertConfig\" id=\"alertAppConfig\" class=\"form-select select2_category\">\n                                            <#list redisUsedGlobalAlertConfigList as redisAlertConfig>\n                                                <option value=\"${redisAlertConfig.value!}\">\n                                                    ${redisAlertConfig.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        比较:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"compareType\" id=\"compareAppType\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                <option value=\"${instanceAlertCompareTypeEnum.value!}\">\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        阀值:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertValue\" id=\"alertAppValue\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        应用appid:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"appid\" id=\"appid\"\n                                               class=\"form-control\" placeholder=\"应用appid\" onchange=\"checkAppExist()\"/>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        周期:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"checkCycle\" id=\"checkAppCycle\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                <option value=\"${instanceAlertCheckCycleEnum.value!}\">\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"configAppBtn\" class=\"btn btn-danger\" onclick=\"saveAppAlertConfig()\">Ok</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n\n<#list instanceAlertList as config>\n    <div id=\"changeInstanceAlertModal${config.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n        <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n\n                <div class=\"modal-header\">\n                    <h4 class=\"modal-title\">修改实例报警项</h4>\n                    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n                </div>\n\n                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                    <div class=\"modal-body\">\n                        <div class=\"row\">\n                            <!-- 控件开始 -->\n                            <div class=\"col-md-12\">\n                                <!-- form-body开始 -->\n                                <div class=\"form-body\">\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3 text-end\">\n                                            比较:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <select name=\"compareType${config.id!}\" id=\"compareType${config.id!}\" class=\"form-select select2_category\">\n                                                <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                    <option value=\"${instanceAlertCompareTypeEnum.value!}\" <#if (config.compareType == instanceAlertCompareTypeEnum.value)>selected</#if>>\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                    </option>\n                                                </#list>\n                                            </select>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3 text-end\">\n                                            阀值:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <input type=\"text\" name=\"alertValue${config.id!}\" id=\"alertValue${config.id!}\" value=\"${config.alertValue!}\"\n                                                   class=\"form-control\" />\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3 text-end\">\n                                            周期:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <select name=\"checkCycle${config.id!}\" id=\"checkCycle${config.id!}\" class=\"form-select select2_category\">\n                                                <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                    <option value=\"${instanceAlertCheckCycleEnum.value!}\" <#if (config.checkCycle == instanceAlertCheckCycleEnum.value)>selected</#if>>\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                    </option>\n                                                </#list>\n                                            </select>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\" <#if (config.type != 1)>hidden</#if>>\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        重要程度:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"importantLevel${config.id!}\" id=\"importantLevel${config.id!}\" class=\"form-select select2_category\">\n                                            <option value=\"0\" <#if (config.importantLevel == 0)>selected</#if>>一般</option>\n                                            <option value=\"1\" <#if (config.importantLevel == 1)>selected</#if>>重要</option>\n                                            <option value=\"2\" <#if (config.importantLevel == 2)>selected</#if>>紧急</option>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n            </div>\n\n            <div class=\"modal-footer\">\n                <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                <button type=\"button\" id=\"configBtn${config.id!}\" class=\"btn btn-danger\" onclick=\"changeAlertConfig('${config.id!}')\">Ok</button>\n            </div>\n            </form>\n        </div>\n    </div>\n</div>\n</#list>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceAlert/tendisAlert/init.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"initConfigDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceAlert/tendisAlert/initConfigDetail.html",
    "content": "<script type=\"text/javascript\">\n\n    //查看实例是否存在\n    function checkInstanceExist(){\n        var instanceHostPort = document.getElementById(\"instanceHostPort\").value;\n        if (instanceHostPort == null || instanceHostPort == \"\"){\n            alert(\"请填写实例ip:port\");\n            instanceHostPort.focus();\n            return false;\n        }\n        if(instanceHostPort != ''){\n            $.post(\n                '${request.contextPath}/manage/instanceAlert/checkInstanceHostPort.json',\n                {\n                    instanceHostPort: instanceHostPort,\n                },\n                function(data){\n                    var success = data.status;\n                    if(success==0){\n                        alert(data.message);\n                        document.getElementById(\"instanceHostPort\").focus();\n                    }\n                }\n            );\n        }\n    }\n\n    function removeAlertConfig(id) {\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/remove.json',\n            {\n                id: id\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"删除成功!\");\n                } else {\n                    alert(\"删除失败, msg: \" + result.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n\n    function changeAlertConfig(id) {\n        var alertValue = document.getElementById(\"alertValue\" + id);\n        var checkCycle = document.getElementById(\"checkCycle\" + id);\n        var compareType = document.getElementById(\"compareType\" + id);\n        var importantLevel = document.getElementById(\"importantLevel\" + id);\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/update.json',\n            {\n                id: id,\n                alertValue: alertValue.value,\n                checkCycle: checkCycle.value,\n                compareType: compareType.value,\n                importantLevel: importantLevel.value\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"修改成功！\");\n                    window.location.reload();\n                } else {\n                    alert(\"修改失败！\" + data.message);\n                }\n\n            }\n        );\n    }\n\n    function saveInstanceAlertConfig() {\n        var alertConfig = document.getElementById(\"alertConfig\");\n        var alertValue = document.getElementById(\"alertValue\");\n        if (alertValue.value == \"\"){\n            alert(\"请填写阈值\");\n            alertValue.focus();\n            return false;\n        }\n        var compareType = document.getElementById(\"compareType\");\n        var checkCycle = document.getElementById(\"checkCycle\");\n        var instanceHostPort = document.getElementById(\"instanceHostPort\");\n        if (instanceHostPort.value == null || instanceHostPort.value == \"\"){\n            alert(\"请填写实例ip:port\");\n            instanceHostPort.focus();\n            return false;\n        }\n        var type = 1;\n        if (instanceHostPort.value != null && instanceHostPort.value != '') {\n            type = 2;\n        }\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/add.json',\n            {\n                alertConfig: alertConfig.value,\n                alertValue: alertValue.value,\n                configInfo: alertConfig.options[alertConfig.selectedIndex].text,\n                compareType: compareType.value,\n                checkCycle: checkCycle.value,\n                instanceHostPort: instanceHostPort.value,\n                appType: 1,\n                type: type\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"添加成功！\");\n                } else {\n                    alert(\"添加失败！\" + data.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n\n    //保存全局实例\n    function saveGlobalInstanceAlertConfig() {\n        var alertConfig = document.getElementById(\"alertConfigGlobal\");\n        if (alertConfig.value == \"\"){\n            alert(\"请填写配置名\");\n            alertConfig.focus();\n            return false;\n        }\n        var alertValue = document.getElementById(\"alertValueGlobal\");\n        if (alertValue.value == \"\"){\n            alert(\"请填写阈值\");\n            alertValue.focus();\n            return false;\n        }\n        var configInfo = document.getElementById(\"configInfoGlobal\");\n        if (configInfo.value == \"\"){\n            alert(\"请填写配置说明\");\n            configInfo.focus();\n            return false;\n        }\n        var compareType = document.getElementById(\"compareTypeGlobal\");\n        var checkCycle = document.getElementById(\"checkCycleGlobal\");\n        var importantLevel = document.getElementById(\"importantLevel\");\n        var type = 1;\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/add.json',\n            {\n                alertConfig: alertConfig.value,\n                configInfo: configInfo.value,\n                alertValue: alertValue.value,\n                compareType: compareType.value,\n                checkCycle: checkCycle.value,\n                importantLevel: importantLevel.value,\n                appType: 1,\n                type: type\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"添加成功！\");\n                } else {\n                    alert(\"添加失败！\" + data.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n\n    // 应用添加报警\n    function saveAppAlertConfig() {\n        var alertConfig = document.getElementById(\"alertAppConfig\");\n        var alertValue = document.getElementById(\"alertAppValue\");\n        if (alertValue.value == \"\"){\n            alert(\"请填写阈值\");\n            alertValue.focus();\n            return false;\n        }\n        var compareType = document.getElementById(\"compareAppType\");\n        var checkCycle = document.getElementById(\"checkAppCycle\");\n        var appid = document.getElementById(\"appid\");\n        if (appid.value == \"\"){\n            alert(\"请填写appid\");\n            appid.focus();\n            return false;\n        }\n\n        $.get(\n            '${request.contextPath}/manage/instanceAlert/addApp.json',\n            {\n                alertConfig: alertConfig.value,\n                alertValue: alertValue.value,\n                configInfo: alertConfig.options[alertConfig.selectedIndex].text,\n                compareType: compareType.value,\n                checkCycle: checkCycle.value,\n                appType: 1,\n                appid: appid.value\n            },\n            function(data){\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"添加成功！\");\n                } else {\n                    alert(\"添加失败！\" + data.message);\n                }\n                window.location.reload();\n            }\n        );\n    }\n</script>\n<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <div class=\"row\">\n                    <div class=\"col-md-4\">\n                        <h3 class=\"card-title\">\n                            <i class=\"bi bi-globe\"></i>\n                            全局实例报警项:\n                        </h3>\n                    </div>\n                    <div class=\"col-md-8 text-end\">\n                        <button type=\"button\" class=\"btn btn-success\" style=\"margin-top: 0px\" data-bs-target=\"#addGlobalInstanceAlertModal\" data-bs-toggle=\"modal\" href=\"#\">添加全局报警项</button>\n                    </div>\n                </div>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"table-responsive\">\n                    <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n                        <thead>\n                        <tr>\n                            <th>id</th>\n                            <th>配置名</th>\n                            <th>配置说明</th>\n                            <th>关系</th>\n                            <th>阀值</th>\n                            <th>周期</th>\n                            <th>重要程度</th>\n                            <th>最近检测时间</th>\n                            <th style=\"white-space: nowrap\">操作</th>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <#list instanceAlertAllList as config>\n                            <tr>\n                                <td>\n                                    ${config.id!}\n                                </td>\n                                <td>\n                                    ${config.alertConfig!}\n                                </td>\n                                <td>\n                                    ${config.configInfo!}\n                                </td>\n                                <td>\n                                    <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                        <#if (config.compareType == instanceAlertCompareTypeEnum.value)>${instanceAlertCompareTypeEnum.info!}</#if>\n                                    </#list>\n                                </td>\n                                <td>\n                                    ${config.alertValue!}\n                                </td>\n                                <td>\n                                    <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                        <#if (config.checkCycle == instanceAlertCheckCycleEnum.value)>${instanceAlertCheckCycleEnum.info!}</#if>\n                                    </#list>\n                                </td>\n\n                                <td>\n                                    <#if (config.importantLevel == 0)>一般</#if>\n                                    <#if (config.importantLevel == 1)>重要</#if>\n                                    <#if (config.importantLevel == 2)>紧急</#if>\n                                </td>\n\n                                <td>\n                                    ${config.lastCheckTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                </td>\n                                <td style=\"white-space: nowrap\">\n                                    <button type=\"button\" class=\"btn btn-info\" data-bs-target=\"#changeInstanceAlertModal${config.id!}\" data-bs-toggle=\"modal\" href=\"#\">修改</button>\n                                    <button type=\"button\" class=\"btn btn-info\" onclick=\"if(window.confirm('确认要清除id=${config.id!}的配置?!')){removeAlertConfig('${config.id!}');return true;}else{return false;}\">删除</button>\n                                </td>\n                            </tr>\n                        </#list>\n                        </tbody>\n                    </table>\n                </div>\n            </div>\n        </div>\n\n\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <div class=\"row\">\n                    <div class=\"col-md-4\">\n                        <h3 class=\"card-title\">\n                            <i class=\"bi bi-globe\"></i>\n                            特殊实例报警:\n                        </h3>\n                    </div>\n                    <div class=\"col-md-8 text-end\">\n                        <div class=\"btn-group\">\n                            <button type=\"button\" class=\"btn btn-success\" style=\"margin-top: 0px\" data-bs-target=\"#addAppAlertModal\" data-bs-toggle=\"modal\" href=\"#\">添加应用报警项</button>\n                        </div>\n                        <div class=\"btn-group\">\n                            <button type=\"button\" class=\"btn btn-success\" style=\"margin-top: 0px\" data-bs-target=\"#addInstanceAlertModal\" data-bs-toggle=\"modal\" href=\"#\">添加新实例报警项</button>\n                        </div>\n                    </div>\n                </div>\n            </div>\n                <!-- END TABLE PORTLET-->\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <div class=\"table-responsive\">\n                        <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n                            <thead>\n                            <tr>\n                                <th>id</th>\n                                <th>实例信息</th>\n                                <th>配置名</th>\n                                <th>配置说明</th>\n                                <th>关系</th>\n                                <th>阀值</th>\n                                <th>周期</th>\n                                <th>最近检测时间</th>\n                                <th>操作</th>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            <#list instanceAlertSpecialList as config>\n                                <tr>\n                                    <td>\n                                        ${config.id!}\n                                    </td>\n                                    <#assign instanceId = config.instanceId>\n                                    <td>\n                                        <#if (config.type == 2)>\n                                            ${config.instanceInfo.hostPort!}\n                                            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${config.instanceInfo.appId!}\">(${config.instanceInfo.appId!})</a>\n                                        </#if>\n                                        <#if (config.type == 3)>\n                                            <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${config.instanceId!}\">${config.instanceId!}</a>\n                                        </#if>\n                                    </td>\n                                    <td>\n                                        ${config.alertConfig!}\n                                    </td>\n                                    <td>\n                                        ${config.configInfo!}\n                                    </td>\n                                    <td>\n                                        <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                            <#if (config.compareType == instanceAlertCompareTypeEnum.value)>${instanceAlertCompareTypeEnum.info!}</#if>\n                                        </#list>\n                                    </td>\n                                    <td>\n                                        ${config.alertValue!}\n                                    </td>\n                                    <td>\n                                        <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                            <#if (config.checkCycle == instanceAlertCheckCycleEnum.value)>${instanceAlertCheckCycleEnum.info!}</#if>\n                                        </#list>\n                                    </td>\n                                    <td>\n                                        ${config.lastCheckTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                    </td>\n                                    <td>\n                                        <button type=\"button\" class=\"btn btn-info\" data-bs-target=\"#changeInstanceAlertModal${config.id!}\" data-bs-toggle=\"modal\" href=\"#\">修改</button>\n                                        <button type=\"button\" class=\"btn btn-info\" onclick=\"if(window.confirm('确认要清除id=${config.id!}的配置?!')){removeAlertConfig('${config.id!}');return true;}else{return false;}\">删除</button>\n                                    </td>\n                                </tr>\n                            </#list>\n                            </tbody>\n                        </table>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n\n<!-- 单实例报警项 -->\n<div id=\"addInstanceAlertModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">添加实例报警项</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置名:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"alertConfig\" id=\"alertConfig\" class=\"form-select select2_category\">\n                                            <#list redisUsedGlobalAlertConfigList as redisAlertConfig>\n                                                <option value=\"${redisAlertConfig.value!}\">\n                                                    ${redisAlertConfig.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        比较:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"compareType\" id=\"compareType\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                <option value=\"${instanceAlertCompareTypeEnum.value!}\">\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        阀值:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertValue\" id=\"alertValue\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        实例:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"instanceHostPort\" id=\"instanceHostPort\"\n                                               class=\"form-control\" placeholder=\"单个实例ip:port\" onchange=\"checkInstanceExist()\"/>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        周期:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"checkCycle\" id=\"checkCycle\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                <option value=\"${instanceAlertCheckCycleEnum.value!}\">\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"configBtn\" class=\"btn btn-danger\" onclick=\"saveInstanceAlertConfig()\">Ok</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n\n<!-- 全局实例报警项 -->\n<div id=\"addGlobalInstanceAlertModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">添加全局实例报警项</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置名:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertConfig\" id=\"alertConfigGlobal\"\n                                               class=\"form-control\" placeholder=\"参照redis info中的字段名\"/>\n                                    </div>\n                                </div>\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置说明:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"configInfo\" id=\"configInfoGlobal\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        比较:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"compareType\" id=\"compareTypeGlobal\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                <option value=\"${instanceAlertCompareTypeEnum.value!}\">\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        阀值:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertValue\" id=\"alertValueGlobal\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        周期:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"checkCycle\" id=\"checkCycleGlobal\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                <option value=\"${instanceAlertCheckCycleEnum.value!}\">\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        重要程度:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"importantLevel\" id=\"importantLevel\" class=\"form-select select2_category\">\n                                            <option value=\"0\" selected>一般</option>\n                                            <option value=\"1\">重要</option>\n                                            <option value=\"2\">紧急</option>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"configBtnGlobal\" class=\"btn btn-danger\" onclick=\"saveGlobalInstanceAlertConfig()\">Ok</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n\n<!-- 应用报警项 -->\n<div id=\"addAppAlertModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">添加应用报警项</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        配置名:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"alertConfig\" id=\"alertAppConfig\" class=\"form-select select2_category\">\n                                            <#list redisUsedGlobalAlertConfigList as redisAlertConfig>\n                                                <option value=\"${redisAlertConfig.value!}\">\n                                                    ${redisAlertConfig.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        比较:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"compareType\" id=\"compareAppType\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                <option value=\"${instanceAlertCompareTypeEnum.value!}\">\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        阀值:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"alertValue\" id=\"alertAppValue\"\n                                               class=\"form-control\" />\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        应用appid:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <input type=\"text\" name=\"appid\" id=\"appid\"\n                                               class=\"form-control\" placeholder=\"应用appid\" onchange=\"checkAppExist()\"/>\n                                    </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        周期:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"checkCycle\" id=\"checkAppCycle\" class=\"form-select select2_category\">\n                                            <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                <option value=\"${instanceAlertCheckCycleEnum.value!}\">\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                </option>\n                                            </#list>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n                </div>\n\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"configAppBtn\" class=\"btn btn-danger\" onclick=\"saveAppAlertConfig()\">Ok</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n\n<#list instanceAlertList as config>\n    <div id=\"changeInstanceAlertModal${config.id!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n        <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n\n                <div class=\"modal-header\">\n                    <h4 class=\"modal-title\">修改实例报警项</h4>\n                    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n                </div>\n\n                <form class=\"form-horizontal form-bordered form-row-stripped\">\n                    <div class=\"modal-body\">\n                        <div class=\"row\">\n                            <!-- 控件开始 -->\n                            <div class=\"col-md-12\">\n                                <!-- form-body开始 -->\n                                <div class=\"form-body\">\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3 text-end\">\n                                            比较:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <select name=\"compareType${config.id!}\" id=\"compareType${config.id!}\" class=\"form-select select2_category\">\n                                                <#list instanceAlertCompareTypeEnumList as instanceAlertCompareTypeEnum>\n                                                    <option value=\"${instanceAlertCompareTypeEnum.value!}\" <#if (config.compareType == instanceAlertCompareTypeEnum.value)>selected</#if>>\n                                                    ${instanceAlertCompareTypeEnum.info!}\n                                                    </option>\n                                                </#list>\n                                            </select>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3 text-end\">\n                                            阀值:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <input type=\"text\" name=\"alertValue${config.id!}\" id=\"alertValue${config.id!}\" value=\"${config.alertValue!}\"\n                                                   class=\"form-control\" />\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\">\n                                        <label class=\"col-form-label col-md-3 text-end\">\n                                            周期:\n                                        </label>\n                                        <div class=\"col-md-7\">\n                                            <select name=\"checkCycle${config.id!}\" id=\"checkCycle${config.id!}\" class=\"form-select select2_category\">\n                                                <#list instanceAlertCheckCycleEnumList as instanceAlertCheckCycleEnum>\n                                                    <option value=\"${instanceAlertCheckCycleEnum.value!}\" <#if (config.checkCycle == instanceAlertCheckCycleEnum.value)>selected</#if>>\n                                                    ${instanceAlertCheckCycleEnum.info!}\n                                                    </option>\n                                                </#list>\n                                            </select>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"form-group row\" <#if (config.type != 1)>hidden</#if>>\n                                    <label class=\"col-form-label col-md-3 text-end\">\n                                        重要程度:\n                                    </label>\n                                    <div class=\"col-md-7\">\n                                        <select name=\"importantLevel${config.id!}\" id=\"importantLevel${config.id!}\" class=\"form-select select2_category\">\n                                            <option value=\"0\" <#if (config.importantLevel == 0)>selected</#if>>一般</option>\n                                            <option value=\"1\" <#if (config.importantLevel == 1)>selected</#if>>重要</option>\n                                            <option value=\"2\" <#if (config.importantLevel == 2)>selected</#if>>紧急</option>\n                                        </select>\n                                    </div>\n                                </div>\n                            </div>\n                            <!-- form-body 结束 -->\n                        </div>\n                    </div>\n            </div>\n\n            <div class=\"modal-footer\">\n                <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                <button type=\"button\" id=\"configBtn${config.id!}\" class=\"btn btn-danger\" onclick=\"changeAlertConfig('${config.id!}')\">Ok</button>\n            </div>\n            </form>\n        </div>\n    </div>\n</div>\n</#list>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceOps/instanceCommandCheckList.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud实例运维</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n<div class=\"wrapper\">\n\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <div class=\"row\">\n          <div class=\"card\">\n            <div class=\"card-header\">\n              <h3 class=\"card-title\">\n                <i class=\"bi bi-globe\"></i>实例redis命令检测结果\n              </h3>\n            </div>\n            <div class=\"card-body\">\n              <table class=\"table table-striped table-bordered table-hover\" name=\"checkResultList\">\n                <thead>\n                <tr>\n                  <th>实例id</th>\n                  <th>实例</th>\n                  <th>检测时间</th>\n                  <th>执行命令</th>\n                  <th>异常信息</th>\n                </tr>\n                </thead>\n                <tbody>\n                <#if checkResult??>\n                  <#list checkResult.instanceCheckList as instanceCheck>\n                    <#if !(instanceCheck.success)>\n                      <tr>\n                        <td>\n                          <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instanceCheck.instanceInfo.id!}\"\n                             target=\"_blank\">${instanceCheck.instanceInfo.id!}</a>\n                        </td>\n                        <td>\n                          ${instanceCheck.instanceInfo.ip!}:${instanceCheck.instanceInfo.port!}\n                        </td>\n                        <td>\n                          ${checkResult.createTimeStr!}\n                        </td>\n                        <td>\n                          ${checkResult.command!}\n                        </td>\n                        <td>\n                          <#if instanceCheck.success>否</#if>\n                          <#if !(instanceCheck.success)>\n                            ${instanceCheck.message!}\n                          </#if>\n                        </td>\n                      </tr>\n                    </#if>\n                  </#list>\n                </#if>\n                </tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceOps/instanceConfigCheckList.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud实例运维</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n<div class=\"wrapper\">\n\n<#include \"/manage/inc/head.html\">\n<#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <div class=\"row\">\n          <div class=\"card\">\n            <div class=\"card-header\">\n              <h3 class=\"card-title\">\n                <i class=\"bi bi-globe\"></i>实例redis配置检测结果\n              </h3>\n            </div>\n            <div class=\"card-body\">\n              <table class=\"table table-striped table-bordered table-hover\" name=\"checkResultList\">\n                <thead>\n                <tr>\n                  <th>实例id</th>\n                  <th>实例</th>\n                  <th>版本</th>\n                  <th>所属应用id</th>\n                  <th>所属应用名称</th>\n                  <th>检测时间</th>\n                  <th>检测条件</th>\n                  <th>异常信息</th>\n                  <th>操作</th>\n                </tr>\n                </thead>\n                <tbody>\n                <#if checkResult?? && checkResult.instanceCheckList??>\n                  <#list checkResult.instanceCheckList as instanceCheck>\n                    <#if !(instanceCheck.success)>\n                      <tr>\n                        <td>\n                          <a href=\"${request.contextPath}/admin/instance/index?instanceId=${instanceCheck.instanceInfo.id!}\"\n                             target=\"_blank\">${instanceCheck.instanceInfo.id!}</a>\n                        </td>\n                        <td>\n                          ${instanceCheck.instanceInfo.ip!}:${instanceCheck.instanceInfo.port!}\n                        </td>\n                        <td>\n                          <#list redisVersionList as redisVersion>\n                            <#if checkResult.appDesc?? && checkResult.appDesc.versionId?? && (redisVersion.id==checkResult.appDesc.versionId)>\n                              ${redisVersion.name!}\n                            </#if>\n                          </#list>\n                        </td>\n                        <td>\n                          <a href=\"${request.contextPath}/manage/app/index?appId=${checkResult.appDesc.appId!}\"\n                             target=\"_blank\">${checkResult.appDesc.appId!}</a>\n                        </td>\n                        <td>\n                          <a href=\"${request.contextPath}/manage/app/index?appId=${checkResult.appDesc.appId!}\"\n                             target=\"_blank\">${checkResult.appDesc.name!}</a>\n                        </td>\n                        <td>\n                          ${checkResult.createTimeStr!}\n                        </td>\n                        <td>\n                          ${checkResult.configName!}\n                        </td>\n                        <td>\n                          <#if (instanceCheck.success)>否</#if>\n                          <#if !(instanceCheck.success)>\n\n                            期望值：\n                            <#list compareTypeList as compareTypeEnum>\n                              <#if (checkResult.compareType == compareTypeEnum.type)>\n                                ${compareTypeEnum.info!}\n                              </#if>\n                            </#list>\n                            ${checkResult.expectValue!}\n                            <br>\n                            实际值：\n                            ${instanceCheck.realValue!}\n                          </#if>\n                        </td>\n                        <td>\n                          <#if instanceCheck.success></#if>\n                          <#if !(instanceCheck.success)>\n                            <button class=\"btn btn-warning btn-sm\" style=\"float: right;\">\n                              <a href=\"${request.contextPath}/manage/app/index?appId=${checkResult.appDesc.appId!}&configName=${checkResult.configName!}&&expectValue=${checkResult.expectValue!}&instanceIds=${instanceCheck.instanceInfo.id!}\"\n                                 target=\"_blank\"><font style=\"color: white\">查看修复</font></a>\n                            </button>\n                          </#if>\n                        </td>\n                      </tr>\n                    </#if>\n                  </#list>\n                </#if>\n            </tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div><!-- End #main -->\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceOps/instanceOpsIndex.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"instanceOpsList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceOps/instanceOpsList.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-body\">\n                <nav class=\"nav\">\n                    <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link d-flex <#if (tabId == 1)>active</#if>\" href=\"${request.contextPath}/manage/instance/opsList?tabId=1\">配置检测</a>\n                        </li>\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link d-flex <#if (tabId == 2)>active</#if>\" href=\"${request.contextPath}/manage/instance/opsList?tabId=2\">命令检测</a>\n                        </li>\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link d-flex <#if (tabId == 3)>active</#if>\" href=\"${request.contextPath}/manage/instance/opsList?tabId=3\">重启记录</a>\n                        </li>\n                    </ul>\n                </nav>\n\n                <div class=\"btn-group\" style=\"float:right\">\n                    <form class=\"form-inline\" role=\"form\">\n                        <input name=\"tabId\" id=\"tabId\" value=\"${tabId!}\" type=\"hidden\"/>\n                    </form>\n                </div>\n\n\n                <div class=\"card-group\" id=\"configIndex\">\n                    <form class=\"row align-items-center\" role=\"form\">\n                        <div class=\"input-group my-2\">\n                            <label class=\"col-form-label col-auto\">\n                                redis版本:\n                            </label>\n                            <div class=\"col-md-2\">\n                                <select name=\"versionId\" id=\"versionId\" class=\"form-select select2_category\">\n                                    <option value=\"\" selected>请选择</option>\n                                    <option value=\"0\">所有</option>\n                                    <#list redisVersionList! as redisVersion>\n                                        <option value=\"${redisVersion.id!}\">\n                                            ${redisVersion.name!}\n                                        </option>\n                                    </#list>\n                                </select>\n                            </div>\n                        </div>\n                        <div class=\"input-group\">\n                            <div class=\"row\">\n                                <!-- 控件开始 -->\n                                <label class=\"col-form-label col-auto\">\n                                    配置项:\n                                </label>\n                                <div class=\"col-md-2\">\n                                    <input type=\"text\" name=\"configName\" id=\"configName\"\n                                           class=\"form-control\" />\n                                </div>\n                                <label class=\"col-form-label col-auto\">\n                                    比较:\n                                </label>\n                                <div class=\"col-auto\">\n                                    <select name=\"compareType\" id=\"compareType\" class=\"form-select select2_category\">\n                                        <#list compareTypeList! as compareTypeEnum>\n                                            <option value=\"${compareTypeEnum.type!}\">\n                                                ${compareTypeEnum.info!}\n                                            </option>\n                                        </#list>\n                                    </select>\n                                </div>\n                                <label class=\"col-form-label col-auto\">\n                                    比较值:\n                                </label>\n                                <div class=\"col-md-2\">\n                                    <input type=\"text\" name=\"expectValue\" id=\"expectValue\"\n                                           class=\"form-control\" />\n                                </div>\n                            </div>\n                            <div class=\"col-form-label col-auto\" >\n                                <button type=\"button\" id=\"moduleBtn\" class=\"btn btn-success\" onclick=\"configCheck()\">Ok</button>\n                            </div>\n                        </div>\n                    </form>\n\n                    <div class=\"table-responsive\">\n                        <table class=\"table table-striped table-bordered table-hover\" name=\"checkResultList\">\n                            <thead>\n                            <tr>\n                                <th>redis版本</th>\n                                <th>操作人</th>\n                                <th>检测时间</th>\n                                <th>检测条件</th>\n                                <th>是否异常</th>\n                                <th>异常查看</th>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            <#list checkResultList! as checkResult>\n                                <tr>\n                                    <td>\n                                        <#list redisVersionList as redisVersion>\n                                            <#if (redisVersion.id==checkResult.versionId)>\n                                                ${redisVersion.name!}\n                                            </#if>\n                                        </#list>\n                                        <#if !(checkResult.versionId??) || (checkResult.versionId == 0)>\n                                            所有\n                                        </#if>\n                                    </td>\n                                    <td>\n                                        ${checkResult.userName!}\n                                    </td>\n                                    <td>\n                                        ${checkResult.createTimeStr!}\n                                    </td>\n                                    <td>\n                                        ${checkResult.configName!}\n                                        &nbsp;\n\n                                        <#list compareTypeList as compareTypeEnum>\n                                            <#if (checkResult.compareType == compareTypeEnum.type)>\n                                                ${compareTypeEnum.info!}\n                                            </#if>\n                                        </#list>\n                                        &nbsp;\n                                        ${checkResult.expectValue!}\n                                    </td>\n                                    <td>\n                                        <#if (checkResult.success==true)>否</#if>\n                                        <#if (checkResult.success==false)>是</#if>\n                                    </td>\n\n                                    <td>\n                                        <#if (checkResult.success==true)></#if>\n                                        <#if (checkResult.success==false)>\n                                            <button class=\"btn btn-warning btn-sm\" style=\"float: right;\">\n                                                <a href=\"${request.contextPath}/manage/instance/getConfigCheck?uuid=${checkResult.key!}\"\n                                                   target=\"_blank\"><font style=\"color: white\">查看修复</font></a>\n                                            </button>\n                                        </#if>\n                                    </td>\n                                </tr>\n                            </#list>\n                            </tbody>\n                        </table>\n                    </div>\n                </div>\n\n                <div class=\"card-group\" id=\"commandIndex\" style=\"min-height: 80%\">\n                    <form class=\"row align-items-center bordered\">\n                        <div class=\"modal-contained\">\n                            <div class=\"row my-2\">\n                                <label class=\"col-form-label col-auto\">\n                                    宿主机ip:\n                                </label>\n                                <div class=\"col-md-5\">\n                                    <select id=\"machineIp\" name=\"machineIp\"\n                                            class=\"selectpicker show-tick border rounded\"\n                                            data-live-search=\"true\" title=\"选择宿主机\"\n                                            data-size=\"8\">\n                                    </select>\n                                </div>\n                                <label class=\"col-form-label col-auto\">\n                                    pod ip:\n                                </label>\n                                <div class=\"col-md-4\">\n                                    <select id=\"podIp\" name=\"podIp\"\n                                            class=\"selectpicker show-tick border rounded\"\n                                            data-live-search=\"true\" title=\"选择pod\"\n                                            data-size=\"8\">\n                                    </select>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"modal-contained\">\n                            <div class=\"row mb-2\">\n                                <label class=\"col-form-label col-auto\">\n                                    命令:\n                                </label>\n                                <div class=\"col-auto\">\n                                    <select name=\"command\" id=\"command\" class=\"form-select select2_category\">\n                                        <option value=\"bgsave\">bgsave</option>\n                                        <option value=\"bgrewriteaof\">bgrewriteaof</option>\n                                    </select>\n                                </div>\n                                <button type=\"button\" id=\"commandCheckBtn\" class=\"btn btn-success offset-md-6 col-auto\" onclick=\"commandCheck()\">Ok</button>\n                            </div>\n                        </div>\n                    </form>\n                    <div class=\"table-responsive\">\n                        <table class=\"table table-striped table-bordered table-hover\" name=\"tableDataList\">\n                            <thead>\n                            <tr>\n                                <th>操作人</th>\n                                <th>检测时间</th>\n                                <th>宿主机ip</th>\n                                <th>pod ip</th>\n                                <th>执行命令</th>\n                                <th>是否失败</th>\n                                <th>失败详情</th>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            <#list commandCheckResult! as checkResult>\n                                <tr>\n                                    <td>${checkResult.userName!}</td>\n                                    <td>${checkResult.createTimeStr!}</td>\n                                    <td>${checkResult.machineIps!}</td>\n                                    <td>${checkResult.podIp!}</td>\n                                    <td>${checkResult.command!}</td>\n                                    <td>\n                                        <#if (checkResult.success==true)>否</#if>\n                                        <#if (checkResult.success==false)>是</#if>\n                                    </td>\n                                    <td>\n                                        <#if (checkResult.success==true)></#if>\n                                        <#if (checkResult.success==false)>\n                                            <button class=\"btn btn-warning btn-sm\" style=\"float: right;\">\n                                                <a href=\"${request.contextPath}/manage/instance/getCommandCheck?uuid=${checkResult.key!}\"\n                                                   target=\"_blank\"><font style=\"color: white\">查看</font></a>\n                                            </button>\n                                        </#if>\n                                    </td>\n                                </tr>\n                            </#list>\n                            </tbody>\n                        </table>\n                    </div>\n                </div>\n\n\n                <div class=\"portlet box light-grey\" id=\"restartIndex\">\n                    <div class=\"card-header\">\n                        <div class=\"card-title\">\n                            <i class=\"bi bi-globe\"></i>应用滚动重启/修改配置记录\n                        </div>\n                    </div>\n\n                    <div class=\"row my-2\">\n                        <form class=\"row d-flex justify-content-end\" role=\"form\" action=\"${request.contextPath}/manage/instance/opsList\" method=\"get\" id=\"appList\">\n                            <div class=\"col-auto\">\n                                <input type=\"text\" class=\"form-control\" id=\"appId\" name=\"appId\" value=\"${appId!}\"\n                                       placeholder=\"应用ID\">\n                                <input type=\"hidden\" name=\"pageNo\" id=\"pageNo\">\n                                <input type=\"hidden\" name=\"tabId\" value=\"3\">\n                            </div>\n                            <button type=\"submit\" class=\"btn btn-info col-auto\">查询</button>\n                        </form>\n                    </div>\n                    <div class=\"table-responsive\">\n                        <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\">\n                            <thead>\n                            <tr>\n                                <th>记录ID</th>\n                                <th>操作人</th>\n                                <th>应用ID</th>\n                                <th>应用名称</th>\n                                <th>涉及实例<br><font size=\"1\" style=\"font-weight: bold\">点击实例查看日志</font></th>\n                                <th>重启类型</th>\n                                <th>日志</th>\n                                <th>开始时间</th>\n                                <th>结束时间</th>\n                                <th>状态</th>\n                                <th>操作</th>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            <#list restartRecordList! as configRestartRecord>\n\n                                <#if configRestartRecord.status?? && (configRestartRecord.status == 3)>\n                                    <tr style=\"color:red\">\n                                </#if>\n                                <#if configRestartRecord.status?? && (configRestartRecord.status == 4)>\n                                    <tr style=\"color:dodgerblue\">\n                                </#if>\n                                <#if configRestartRecord.status?? && ((configRestartRecord.status == 1) || (configRestartRecord.status == 5))>\n                                    <tr style=\"color:#e5680f\">\n                                </#if>\n                                <#if configRestartRecord.status?? && (configRestartRecord.status == 6)>\n                                    <tr style=\"color:#c3680f\">\n                                </#if>\n                                <#if !(configRestartRecord.status??) ||  (\n                                        (configRestartRecord.status != 3) && (configRestartRecord.status != 4)\n                                        && (configRestartRecord.status != 1) && (configRestartRecord.status != 5)\n                                        && (configRestartRecord.status != 6)\n                                )>\n                                    <tr>\n                                </#if>\n                                <td>\n                                    ${configRestartRecord.id!}\n                                </td>\n                                <td>\n                                    ${configRestartRecord.userName!}\n                                </td>\n                                <td>\n                                    ${configRestartRecord.appId!}\n                                </td>\n                                <td>\n                                    ${configRestartRecord.appName!}\n                                </td>\n                                <td>\n                                    <#list configRestartRecord.instanceIdList as instance_id>\n                                        <#if instanceInfoMap?? && instanceInfoMap?api.get(instance_id)??>\n                                            <#assign hostPort = instanceInfoMap?api.get(instance_id).hostPort!>\n                                        <#else>\n                                            <#assign hostPort = ''>\n                                        </#if>\n                                        <a href=\"${request.contextPath}/manage/instance/log?instanceId=${instance_id!}\" target=\"_blank\">${instance_id!}</a>(${hostPort!})\n                                        <br/>\n                                    </#list>\n                                </td>\n                                <td>\n                                    <#if configRestartRecord.operateType?? && (configRestartRecord.operateType == 0)>\n                                        滚动重启\n                                    </#if>\n                                    <#if configRestartRecord.operateType?? && (configRestartRecord.operateType == 1)>\n                                        修改冷配置并重启\n                                    </#if>\n                                    <#if configRestartRecord.operateType?? && (configRestartRecord.operateType == 2)>\n                                        修改热配置\n                                    </#if>\n                                    <#if configRestartRecord.operateType?? && (configRestartRecord.operateType == 3)>\n                                        升级模块\n                                    </#if>\n                                </td>\n                                <td escapeXml=\"false\">\n                                    <#list configRestartRecord.logList as log>\n                                        ${log!}\n                                        <br/>\n                                    </#list>\n                                </td>\n                                <td>\n                                    ${configRestartRecord.startTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                </td>\n                                <td>\n                                    ${configRestartRecord.endTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                </td>\n                                <td>\n                                    <#if configRestartRecord.status?? && (configRestartRecord.status == 0)>\n                                        等待中\n                                    </#if>\n                                    <#if configRestartRecord.status?? && (configRestartRecord.status == 1)>\n                                        运行中\n                                    </#if>\n                                    <#if configRestartRecord.status?? && (configRestartRecord.status == 2)>\n                                        成功\n                                    </#if>\n                                    <#if configRestartRecord.status?? && (configRestartRecord.status == 3)>\n                                        失败\n                                    </#if>\n                                    <#if configRestartRecord.status?? && (configRestartRecord.status == 4)>\n                                        配置已改待重启\n                                    </#if>\n                                    <#if configRestartRecord.status?? && (configRestartRecord.status == 5)>\n                                        配置已改重启中\n                                    </#if>\n                                    <#if configRestartRecord.status?? && (configRestartRecord.status == 6)>\n                                        已停止\n                                    </#if>\n                                </td>\n                                <td>\n                                    <#if configRestartRecord.status?? && (((configRestartRecord.status == 1) && (configRestartRecord.operateType == 0)) || ((configRestartRecord.status == 5) && (configRestartRecord.operateType == 1)))>\n                                        <button class=\"btn btn-info\" onclick=\"stopRestart('${configRestartRecord.appId!}')\">停止</button>\n                                    </#if>\n                                </td>\n                                </tr>\n                            </#list>\n                            </tbody>\n                        </table>\n                        <div style=\"margin-top: 10px;\">\n                            <span>\n                              <div id=\"pageDetail\" style=\"float:left;padding-top:7px;color:#4A64A4;\">\n                                <#if page??>\n                                    共${page.totalPages!}页,${page.totalCount!}条\n                                </#if>\n                              </div>\n                              <nav id='ccPagenitor' aria-label=\"Page navigation example\" class=\"d-inline-flex float-end\">\n                              </nav>\n                            </span>\n                        </div>\n                    </div>\n                </div>\n\n            </div>\n        </div>\n    </div>\n</div>\n<script type=\"text/javascript\">\n    $(function () {\n        var searchDate = $('#searchDate').val();\n        if (searchDate == null || searchDate == '') {\n            var time = new Date();\n            var day = (\"0\" + time.getDate()).slice(-2);\n            var month = (\"0\" + (time.getMonth() + 1)).slice(-2);\n            var today = time.getFullYear() + \"-\" + (month) + \"-\" + (day);\n            $('#searchDate').val(today);\n        }\n        var tabVal = $('#tabId').val();\n        if (tabVal == 1) {\n            $('#configIndex').removeAttr(\"hidden\");\n            $('#commandIndex').attr(\"hidden\", \"hidden\");\n            $('#restartIndex').attr(\"hidden\", \"hidden\");\n            $('#aofMonitorIndex').attr(\"hidden\", \"hidden\");\n            $('#backupIndex').attr(\"hidden\", \"hidden\");\n        } else if (tabVal == 2) {\n            $('#configIndex').attr(\"hidden\", \"hidden\");\n            $('#commandIndex').removeAttr(\"hidden\");\n            $('#restartIndex').attr(\"hidden\", \"hidden\");\n            $('#aofMonitorIndex').attr(\"hidden\", \"hidden\");\n            $('#backupIndex').attr(\"hidden\", \"hidden\");\n        }else if (tabVal == 3) {\n            $('#configIndex').attr(\"hidden\", \"hidden\");\n            $('#commandIndex').attr(\"hidden\", \"hidden\");\n            $('#restartIndex').removeAttr(\"hidden\");\n            $('#aofMonitorIndex').attr(\"hidden\", \"hidden\");\n            $('#backupIndex').attr(\"hidden\", \"hidden\");\n        }else if (tabVal == 4) {\n            $('#configIndex').attr(\"hidden\", \"hidden\");\n            $('#commandIndex').attr(\"hidden\", \"hidden\");\n            $('#restartIndex').attr(\"hidden\", \"hidden\");\n            $('#aofMonitorIndex').removeAttr(\"hidden\");\n            $('#backupIndex').attr(\"hidden\", \"hidden\");\n        } else if (tabVal == 5) {\n            $('#configIndex').attr(\"hidden\", \"hidden\");\n            $('#commandIndex').attr(\"hidden\", \"hidden\");\n            $('#restartIndex').attr(\"hidden\", \"hidden\");\n            $('#aofMonitorIndex').attr(\"hidden\", \"hidden\");\n            $('#backupIndex').removeAttr(\"hidden\");\n        }\n\n        {\n            $.post('${request.contextPath}/manage/instance/getMachineList.json',\n                {\n                    ip: null,\n                    realIp: null,\n                    searchType: 1\n                },\n                function (data) {\n                    var status = data.status;\n                    if (status == 1) {\n                        var ipSet = data.ipSet;\n                        $('#machineIp').append(\"<option value=''>\" + '请选择' + \"</option>\");\n                        for (var key in ipSet) {\n                            $('#machineIp').append(\"<option value='\" + ipSet[key] + \"'>\" + ipSet[key] + \"</option>\");\n                        }\n                        $('#machineIp').selectpicker('refresh');\n                        $('#machineIp').selectpicker('render');\n\n                        $('.dropdown-toggle').on('click', function () {\n                            $('.dropdown-toggle').dropdown();\n                        });\n                    }\n                }\n            );\n\n            $.post('${request.contextPath}/manage/instance/getMachineList.json',\n                {\n                    ip: null,\n                    realIp: null,\n                    searchType: 2\n                },\n                function (data) {\n                    var status = data.status;\n                    if (status == 1) {\n                        var ipSet = data.ipSet;\n                        $('#podIp').append(\"<option value=''>\" + '请选择' + \"</option>\");\n                        for (var key in ipSet) {\n                            $('#podIp').append(\"<option value='\" + ipSet[key] + \"'>\" + ipSet[key] + \"</option>\");\n                        }\n                        $('#podIp').selectpicker('refresh');\n                        $('#podIp').selectpicker('render');\n\n                        $('.dropdown-toggle').on('click', function () {\n                            $('.dropdown-toggle').dropdown();\n                        });\n                    }\n                }\n            );\n        }\n    });\n\n    //验证是数字\n    function testisNum(id) {\n        var value = document.getElementById(id).value;\n        if (value != \"\" && isNaN(value)) {\n            alert(\"请输入数字类型!\");\n            document.getElementById(id).value = \"\";\n            document.getElementById(id).focus();\n        }\n    }\n\n    function sendExpAppsStatDataEmail() {\n        var searchDate = document.getElementById(\"searchDate\").value;\n        $.get('${request.contextPath}/manage/app/tool/sendExpAppsStatDataEmail', {searchDate: searchDate});\n        alert(\"异常应用日报已发送，请查收\")\n    }\n\n    //配置检测\n    function configCheck(){\n        var versionId = document.getElementById(\"versionId\");\n        var configName = document.getElementById(\"configName\");\n        if (configName.value == \"\"){\n            alert(\"请填写配置名\");\n            configName.focus();\n            return false;\n        }\n        var expectValue = document.getElementById(\"expectValue\");\n        var compareType = document.getElementById(\"compareType\");\n        document.getElementById(\"moduleBtn\").disabled = \"true\";\n        alert(\"即将执行检测，请稍等。\")\n        $.post(\n            '${request.contextPath}/manage/instance/configCheck.json',\n            {\n                versionId: versionId.value,\n                configName: configName.value,\n                compareType: compareType.value,\n                expectValue: expectValue.value\n            },\n            function (data) {\n                document.getElementById(\"moduleBtn\").disabled = false;\n                var status = data.status;\n                if (status == 1) {\n                    alert(\"已执行，即将刷新页面展示结果\");\n                    window.location.reload();\n                } else {\n                    alert(data.message);\n                    window.location.reload();\n                }\n            }\n        );\n    }\n\n    //命令检测\n    function commandCheck(){\n        var machineIp = document.getElementById(\"machineIp\");\n        var podIp = document.getElementById(\"podIp\");\n        var command = document.getElementById(\"command\");\n        document.getElementById(\"commandCheckBtn\").disabled = \"true\";\n        if (confirm(\"确认执行命令检测？\")) {\n            alert(\"即将执行，请稍等。\");\n            $.post(\n                '${request.contextPath}/manage/instance/commandCheck.json',\n                {\n                    machineIps: machineIp.value,\n                    podIp: podIp.value,\n                    command: command.value\n                },\n                function (data) {\n                    document.getElementById(\"commandCheckBtn\").disabled = false;\n                    var status = data.status;\n                    if (status == 1) {\n                        alert(\"已执行，即将刷新页面展示结果\");\n                        window.location.reload();\n                    } else {\n                        alert(data.message);\n                        window.location.reload();\n                    }\n                }\n            );\n        }\n    }\n\n    /**\n     * 停止滚动重启\n     * @param appId\n     */\n    function stopRestart(appId) {\n        if (confirm(\"确认停止重启任务吗？\")) {\n            $.get(\n                '${request.contextPath}/manage/app/restart/stopRestart?appId=' + appId,\n                function (data) {\n                    var status = data.status;\n                    if (status == 200) {\n                        alert(data.data);\n                    } else {\n                        alert(data.error);\n                    }\n                }\n            );\n        }\n    }\n\n</script>\n<#include \"page.html\">\n\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/instanceOps/page.html",
    "content": "<script type='text/javascript'>\n    $(function(){\n        var totalPageValue = <#if page?? && page.totalPages??>${page.totalPages!}<#else>0</#if>;\n        var pageNoValue = <#if page?? && page.pageNo??>${page.pageNo!}<#else>1</#if>;\n        refreshPages(totalPageValue,pageNoValue);\n    });\n\n    function refreshPages(totalPage, currentPage) {\n        //安全判断\n        if (currentPage < 1 ) {\n            currentPage = 1;\n        }else if (currentPage > totalPage) {\n            currentPage = totalPage;\n        }\n        var tabVal = 0;\n        if($('#tabId') && $('#tabId') != undefined){\n            tabVal = $('#tabId').val();\n        }\n        var paginationInfo = getPagination(totalPage, currentPage, tabVal);\n\n        //用id选择器写入页码部分代码（根据需求修改）\n        if(tabVal == 0) {\n            $(\"#ccPagenitor\").html(paginationInfo);\n        } else if (tabVal == 1) {\n        } else if (tabVal == 2) {\n        } else if (tabVal == 3) {\n            $(\"#ccPagenitor\").html(paginationInfo);\n        } else if (tabVal == 4) {\n            $(\"#aofPagenitor\").html(paginationInfo);\n        } else if (tabVal == 5) {\n            $(\"#backupPagenitor\").html(paginationInfo);\n        }\n    }\n\n    function searchJob(page, tabVal){\n        if(tabVal == 0){\n            //form传参用pageSize\n            document.getElementById(\"pageNo\").value=page;\n            document.getElementById(\"tableDataList\").submit();\n        } else if(tabVal == 3){\n            //form传参用pageSize\n            document.getElementById(\"pageNo\").value=page;\n            document.getElementById(\"appList\").submit();\n        } else if(tabVal == 4){\n            //form传参用pageSize\n            document.getElementById(\"aofPageNo\").value=page;\n            document.getElementById(\"aofList\").submit();\n        } else if (tabVal == 5){\n            //form传参用pageSize\n            document.getElementById(\"backupPageNo\").value=page;\n            document.getElementById(\"backupList\").submit();\n        }\n    }\n\n    function getPagination(totalPage, currentPage, tabVal){\n\n        var paginationInfo = \"<ul class='pagination' style='flex-grow: 1'>\";\n        if (currentPage == 1) {\n            paginationInfo += \"<li class='page-item disabled'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage - 1) + \", \" + tabVal + \")'\"+\">«</a></li>\";\n        }else {\n            //前一页\n            paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage - 1) + \", \" + tabVal + \")'\"+\">«</a></li>\";\n        }\n\n        if(totalPage <= 10){\n            //totalPage<=10\n            for(var i = 1; i <= totalPage; i++){\n                if (i == currentPage) {\n                    paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \")'>\" + i + \" </a></li>\";\n                }else {\n                    paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \")'>\" + i + \" </a></li>\";\n                }\n            }\n        } else{\n            //totalPage > 10\n            if(currentPage <= 3){\n                for(var i = 1; i <= currentPage + 2; i++){\n                    //页码1、2\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \")'>\" + i + \"</a></li>\";\n                    } else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \")'>\" + i + \"</a></li>\";\n                    }\n                }\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后一页的页码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + totalPage + \", \" + tabVal + \")'>\" + totalPage + \"</a></li>\";\n            }else if(currentPage <= totalPage - 5){\n                //totalPage > 10   currentPage > 3 currentPage<=totalPage-5，  页码在中间部分\n                //页码为1的代码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(1,\" + tabVal + \")'>\" + 1 + \"</a></li>\";\n\n                //页码1后面的省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n\n                //中间部分代码\n                for(var i = currentPage - 1; i <= currentPage + 2; i++){\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \")'>\" + i + \"</a></li>\";\n                    } else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \")'>\" + i + \"</a></li>\";\n                    }\n                }\n                //后面的省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后一个页码\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + totalPage + \", \" + tabVal + \")'>\" + totalPage + \"</a></li>\";\n            }else{\n                //totalPage > 10  并且currentPage > totalPage-5 显示后面的页码\n\n                //页码1\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(1,\" + tabVal + \")'>\"+1+\"</a></li>\";\n                //省略号\n                paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);'>...</a></li>\";\n                //最后几位页码\n                for(var i = currentPage - 1; i <= totalPage; i++){\n                    if (i == currentPage) {\n                        paginationInfo += \"<li class='page-item active'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \"'>\" + i + \"</a></li>\";\n                    }else {\n                        paginationInfo += \"<li class='page-item'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + i + \", \" + tabVal + \")'>\"+i+\"</a></li>\";\n                    }\n                }\n            }\n        }\n\n        //下一页\n        if (currentPage == totalPage) {\n            paginationInfo += \"<li class='page-item disabled'> <a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage + 1) + \", \" + tabVal + \")'\" + \">»</a></li>\";\n        } else {\n            paginationInfo += \"<li class='page-item'><a class='page-link' href='javascript:void(0);' onclick='searchJob(\" + (currentPage + 1) + \", \" + tabVal + \")'\"+\">»</a></li>\";\n        }\n        paginationInfo += \"</ul>\";\n        //返回结果\n        return paginationInfo;\n    }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/login.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>CacheCloud系统</title>\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n    <link href=\"${request.contextPath}/assets/vendor/bootstrap/css/bootstrap.min.css\" rel=\"stylesheet\">\n    <link href=\"${request.contextPath}/assets/vendor/bootstrap-icons/bootstrap-icons.css\" rel=\"stylesheet\">\n    <script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n    <script src=\"${request.contextPath}/assets/vendor/jquery/jquery.md5.js\" type=\"text/javascript\"></script>\n    <script type=\"text/javascript\">\n      function loginIn(pwdFlag) {\n        var userName = document.getElementById(\"userName\");\n        var password = document.getElementById(\"password\");\n        var redirectUrl = document.getElementById(\"redirectUrl\").value;\n        if(userName.value == \"\"){\n          alert(\"用户名不能为空!\");\n          userName.focus();\n          return false;\n        }\n        if(password.value == \"\"){\n          alert(\"密码不能为空!\");\n          password.focus();\n          return false;\n        }\n        var realPwd = password.value;\n        if(pwdFlag){\n          realPwd = $.md5(password.value);\n        }\n        $.post(\n                '${request.contextPath}/manage/loginIn.json',\n                {\n                  userName: userName.value,\n                  password: realPwd,\n                  isAdmin: false\n                },\n                function(data){\n                  var success = data.success;\n                  var admin = data.admin;\n                  if(success==1){\n                    if (redirectUrl != \"\") {\n                      window.location = redirectUrl;\n                    } else {\n                      if(admin == 1){\n                        window.location = \"${request.contextPath}/manage/total/list\";\n                      }else{\n                        window.location = \"${request.contextPath}/admin/app/list\";\n                      }\n                    }\n                  }else if(success == 0){\n                    alert(\"用户名或者密码错误，请重新输入!\");\n                  }else if(success == -1){\n                    alert(\"系统不存在该用户名，请确认该用户申请了cachecloud权限!\");\n                  }else if(success == -2){\n                    alert(\"您不是超级管理员!\");\n                  }\n                }\n        );\n      }\n    </script>\n  </head>\n\n  <body>\n  <div class=\"card\">\n    <img src=\"${request.contextPath}/assets/img/bg.png\" class=\"card-img\" style=\"position: fixed; top: 0; left: 0; bottom: 0; right: 0; z-index: -1; height: 100%\">\n      <div class=\"container card-img-overlay\">\n        <div class=\"row justify-content-center\">\n          <div style=\"width: 495px; height: 150px; margin-top: 100px;\" class=\"align-items-center\">\n            <img class=\"img-fluid\" src=\"${request.contextPath}/assets/img/logo.png\">\n          </div>\n        </div>\n\n        <div class=\"info\">\n          <form method=\"post\" name=\"login\" autocomplete=\"off\">\n            <div class=\"row justify-content-center mb-3\">\n              <div class=\"col-md-4\">\n                <div class=\"input-group\">\n                  <span class=\"input-group-text\"><i class=\"bi bi-person-circle\"></i></span>\n                  <div class=\"form-floating\">\n                    <input type=\"text\" class=\"form-control\" id=\"userName\" name=\"userName\" autocomplete=\"off\"placeholder=\"用户名\">\n                    <label for=\"userName\">用户名</label>\n                  </div>\n                </div>\n              </div>\n            </div>\n            <div class=\"row justify-content-center mb-3\">\n              <div class=\"col-md-4\">\n                <div class=\"input-group\">\n                  <span class=\"input-group-text\"><i class=\"bi bi-file-lock2\"></i></span>\n                  <div class=\"form-floating\">\n                    <input type=\"text\" class=\"form-control\" id=\"password\" name=\"password\" autocomplete=\"off\" placeholder=\"密码\">\n                    <label for=\"password\">密码</label>\n                  </div>\n                </div>\n              </div>\n            </div>\n            <input type=\"hidden\" value=\"${redirectUrl!}\" id=\"redirectUrl\" name=\"redirectUrl\">\n            <div class=\"row justify-content-center mb-2\">\n              <div class=\"col-md-4\">\n                <div class=\"input-group\">\n                  <input type=\"button\" class=\"btn btn-danger rounded w-100\" value=\"登 录\" onclick=\"loginIn(${pwdswitch?c})\">\n                </div>\n              </div>\n            </div>\n\n          </form>\n          <div class=\"row mb-2\">\n            <div class=\"text-center\">\n              <a class=\"register\" href=\"${request.contextPath}/user/register\" c>新用户注册</a>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/addMachine.html",
    "content": "<#if machine?? && machine.info??>\n  <#assign machineInfoId = machine.info.id>\n<#else>\n  <#assign machineInfoId = ''>\n</#if>\n<div id=\"addMachineModal${machineInfoId!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">管理机器</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    机器ip:\n                  </label>\n                  <div class=\"col-md-5\">\n                    <#if machine?? && machine.info??>\n                      <#assign ipValue = machine.info.ip!>\n                    <#else>\n                      <#assign ipValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"ip\" id=\"ip${machineInfoId!}\"\n                           value=\"${ipValue!}\" placeholder=\"机器ip，多台机器,分隔\"\n                           class=\"form-control\" />\n                  </div>\n                  <label class=\"col-form-label col-md-4\">\n                    注:多台机器用,分隔\n                  </label>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    机房:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"machineoom\" id=\"machineRoom${machineInfoId!}\" class=\"form-select select2_category w-100\">\n                      <option value=\"默认\">默认机房</option>\n                      <#list roomList as room>\n                        <option value=\"${room.name!}\" <#if machine?? && machine.info?? && (room.name == (machine.info.room!))>selected</#if>>${room.name!} (${room.ipNetwork!})</option>\n                      </#list>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    内存:\n                  </label>\n                  <div class=\"col-md-5\">\n                    <#if machine?? && machine.info??>\n                      <#assign memValue = machine.info.mem!>\n                    <#else>\n                      <#assign memValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"mem\" id=\"mem${machineInfoId!}\"\n                           value=\"${memValue!}\" placeholder=\"机器内存（单位G）\"\n                           class=\"form-control\" />\n                  </div>\n                  <label class=\"col-form-label col-md-1\">\n                    G\n                  </label>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    cpu:\n                  </label>\n                  <div class=\"col-md-5\">\n                    <#if machine?? && machine.info??>\n                      <#assign cpuValue = machine.info.cpu!>\n                    <#else>\n                      <#assign cpuValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"cpu\" id=\"cpu${machineInfoId!}\"\n                           value=\"${cpuValue!}\" placeholder=\"机器CPU核数\"\n                           class=\"form-control\" />\n                  </div>\n                  <label class=\"col-form-label col-md-1\">\n                    核\n                  </label>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    disk:\n                  </label>\n                  <div class=\"col-md-5\">\n                    <#if machine?? && machine.info??>\n                      <#assign diskValue = machine.info.disk!>\n                    <#else>\n                      <#assign diskValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"disk\" id=\"disk${machineInfoId!}\"\n                           value=\"${diskValue!}\" placeholder=\"机器磁盘空间:G\"\n                           class=\"form-control\" />\n                  </div>\n                  <label class=\"col-form-label col-md-1\">\n                    G\n                  </label>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    是否虚机:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"virtual\" id=\"virtual${machineInfoId!}\" class=\"form-select select2_category w-100\">\n                      <option value=\"0\" <#if machine?? && machine.info?? && (machine.info.virtual == 0)>selected=\"selected\"</#if>>\n                      否\n                      </option>\n                      <option value=\"1\" <#if machine?? && machine.info?? && (machine.info.virtual == 1)>selected=\"selected\"</#if>>\n                      是\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    操作系统:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"disType\" id=\"disType${machineInfoId!}\" class=\"form-select select2_category w-100\">\n                      <option value=\"0\" <#if machine?? && machine.info?? && (machine.info.disType == 0)>selected=\"selected\"</#if>>\n                      centos\n                      </option>\n                      <option value=\"1\" <#if machine?? && machine.info?? && (machine.info.disType == 1)>selected=\"selected\"</#if>>\n                      ubuntu\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    宿主机ip:\n                  </label>\n                  <div class=\"col-md-5\">\n                    <#if machine?? && machine.info??>\n                      <#assign realIpValue = machine.info.realIp!>\n                    <#else>\n                      <#assign realIpValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"realIp\" id=\"realIp${machineInfoId!}\"\n                           value=\"${realIpValue!}\" placeholder=\"宿主机ip,多台机器,分隔\"\n                           class=\"form-control\" />\n                  </div>\n                  <label class=\"col-form-label col-md-4\">\n                    注:多台机器用,分隔\n                  </label>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    机架信息:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <#if machine?? && machine.info??>\n                      <#assign rackValue = machine.info.rack!>\n                    <#else>\n                      <#assign rackValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"rack\" id=\"rack${machineInfoId!}\"\n                           value=\"${rackValue!}\" placeholder=\"机器信息\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    机器类型:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"machineType\" id=\"machineType${machineInfoId!}\" class=\"form-select select2_category w-100\">\n                      <option value=\"0\" <#if machine?? && machine.info?? && (machine.info.type == 0)>selected=\"selected\"</#if>>\n                      Redis机器(默认)\n                      </option>\n                      <option value=\"3\" <#if machine?? && machine.info?? && (machine.info.type == 3)>selected=\"selected\"</#if>>\n                      Sentinel机器\n                      </option>\n                      <option value=\"2\" <#if machine?? && machine.info?? && (machine.info.type == 2)>selected=\"selected\"</#if>>\n                      Redis迁移工具机器\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    部署类型:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"useType\" id=\"useType1${machineInfoId!}\" class=\"form-select select2_category w-100\">\n                      <option value=\"2\" <#if machine?? && machine.info?? && (machine.info.useType == 2)>selected=\"selected\"</#if>>\n                      混合部署\n                      </option>\n                      <option value=\"0\" <#if machine?? && machine.info?? && (machine.info.useType == 0)>selected=\"selected\"</#if>>\n                      专用服务部署\n                      </option>\n                      <option value=\"1\" <#if machine?? && machine.info?? && (machine.info.useType == 1)>selected=\"selected\"</#if>>\n                      测试服务部署\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    容器类型:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"k8sType\" id=\"k8sType${machineInfoId!}\" class=\"form-select select2_category w-100\">\n                      <option value=\"0\" <#if machine?? && machine.info?? && (machine.info.k8sType == 0)>selected=\"selected\"</#if>>\n                      普通容器\n                      </option>\n                      <option value=\"1\" <#if machine?? && machine.info?? && (machine.info.k8sType == 1)> selected=\"selected\"</#if>>\n                      k8s容器\n                      </option>\n                      <option value=\"2\" <#if machine?? && machine.info?? && (machine.info.k8sType == 2)> selected=\"selected\"</#if>>\n                      物理机\n                      </option>\n                      <option value=\"3\" <#if machine?? && machine.info?? && (machine.info.k8sType == 3)> selected=\"selected\"</#if>>\n                      虚拟机\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    机器说明:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <#if machine?? && machine.info??>\n                      <#assign extraDescValue = machine.info.extraDesc!>\n                    <#else>\n                      <#assign extraDescValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"extraDesc\" id=\"extraDesc${machineInfoId!}\"\n                           value=\"${extraDescValue!}\" placeholder=\"描述说明(所属服务、相关人员)\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    状态收集:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"collect\" id=\"collect${machineInfoId!}\" class=\"form-select select2_category w-100\">\n                      <option value=\"0\" <#if machine?? && machine.info?? && (machine.info.collect == 0)>selected=\"selected\"</#if>>\n                      关闭\n                      </option>\n                      <option value=\"1\" <#if machine?? && machine.info?? && (machine.info.collect == 1) || (machineInfoId?? && (machineInfoId == ''))>selected=\"selected\"</#if>>\n                      开启\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <input type=\"hidden\" id=\"machineId${machineInfoId!}\" name=\"machineId\" value=\"${machineInfoId!}\"/>\n                <#if machine?? && machine.info??>\n                  <#assign versionInstallValue = machine.info.versionInstall!>\n                <#else>\n                  <#assign versionInstallValue = ''>\n                </#if>\n                <input type=\"hidden\" id=\"versionInfo${machineInfoId!}\" name=\"versionInfo\" value=\"${versionInstallValue!}\"/>\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <div id=\"machineInfo${machineInfoId!}\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" id=\"addMachineBtn${machineInfoId!}\" class=\"btn btn-danger\" onclick=\"saveOrUpdateMachine('${machineInfoId!}', '${request.contextPath}')\">Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/addRoom.html",
    "content": "<#if record??>\n  <#assign roomId = record.id>\n<#else>\n  <#assign roomId = ''>\n</#if>\n<div id=\"addRoomModal${roomId!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">管理机房</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    机房名称:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <#if record??>\n                      <#assign nameValue = record.name!>\n                    <#else>\n                      <#assign nameValue = ''>\n                    </#if>\n                  <input type=\"text\" name=\"name\" id=\"name${roomId!}\"\n                         value=\"${nameValue!}\" placeholder=\"机房名称\"\n                         class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    机房状态:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"status\" id=\"status${roomId!}\" class=\"form-select select2_category\">\n                      <option value=\"1\" <#if record?? && (record.status == 1)>selected</#if>>有效</option>\n                      <option value=\"0\" <#if record?? && (record.status == 0)>selected</#if>>无效</option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    描述:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <#if record??>\n                      <#assign descValue = record.desc!>\n                    <#else>\n                      <#assign descValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"desc\" id=\"desc${roomId!}\"\n                           value=\"${descValue!}\" placeholder=\"描述\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    网段:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <#if record??>\n                      <#assign ipNetworkValue = record.ipNetwork!>\n                    <#else>\n                      <#assign ipNetworkValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"ipNetwork\" id=\"ipNetwork${roomId!}\"\n                           value=\"${ipNetworkValue!}\" placeholder=\"网段示例:xx.xx.*.*\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    运营商:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <#if record??>\n                      <#assign operatorValue = record.operator!>\n                    <#else>\n                      <#assign operatorValue = ''>\n                    </#if>\n                    <input type=\"text\" name=\"operator\" id=\"operator${roomId!}\"\n                           value=\"${operatorValue!}\" placeholder=\"运营商\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n              <input type=\"hidden\" id=\"roomId${roomId!}\" name=\"roomId\" value=\"${roomId!}\"/>\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <div id=\"machineRoom${roomId!}\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" id=\"addRoomBtn${roomId!}\" class=\"btn btn-danger\" onclick=\"saveOrUpdateRoom('${roomId!}', '${request.contextPath}')\">Ok</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/downCrashMachine.html",
    "content": "<div id=\"downCrashMachineModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"800\">\n  <div class=\"modal-dialog modal-lg\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">物理机迁移——当前仅适用自动补全app缺失的slave</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-2\">\n                    物理机ip:\n                  </label>\n                  <div class=\"col-md-5\">\n                    <input type=\"text\" name=\"ip\" id=\"downMachineIp\"\n                           placeholder=\"物理机ip\" class=\"form-control\" />\n                  </div>\n                  <div class=\"offset-md-2 col-md-3\">\n                    <a class=\"btn btn-info\" target=\"_blank\" href=\"${request.contextPath}/manage/task/list?className=MachineDownAndAppFixTask\">查看历史任务</a>\n                  </div>\n                </div>\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <!-- 控件结束 -->\n          </div>\n\n          <div class=\"row\">\n            <h5>机器部署详情：</h5>\n            <p id=\"deployDetailId\">\n            </p>\n\n            <div class=\"form-group row\" id=\"availableMachineId\" style=\"display: none\">\n              <div class=\"col-md-12\">\n                <p id=\"tipId\"></p>\n                <p id=\"selectedResourceId\"></p>\n              </div>\n              <label class=\"col-form-label col-auto\">可用pod:</label>\n              <div class=\"col-md-6\" id=\"availablePodId\">\n                <select id=\"availablePodIp\" name=\"availablePodIp\"\n                        class=\"selectpicker border rounded w-100\" data-live-search=\"true\" multiple data-size=\"8\" onchange=\"availableMachineChange()\">\n                </select>\n              </div>\n            </div>\n            <br><br>\n            <div id=\"downMachineInfo\"></div>\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" class=\"btn btn-info\" onclick=\"prepareMachinedownResource('${request.contextPath}')\">生成备选pod</button>\n          <button type=\"button\" id=\"downCrashMachineModalBtn\" class=\"btn btn-danger\" onclick=\"downCrashMachineAddSlave('${request.contextPath}')\" disabled>Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/index.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-body\">\n                <nav class=\"nav\">\n                    <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                        <li id=\"machine\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/machine/list?tabTag=machine&ipLike=${ipLike!}&useType=${useType!}&type=${type!}&versionId=${versionId!}&isInstall=${isInstall!}&k8sType=${k8sType!}&realip=${realip!}\">\n                            <a class=\"nav-link d-flex\" href=\"?tabTag=machine\">机器管理</a>\n                        </li>\n                        <li id=\"room\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/machine/list?tabTag=room\">\n                            <a class=\"nav-link d-flex\" href=\"?tabTag=room\">机房管理</a>\n                        </li>\n                    </ul>\n                </nav>\n            </div>\n        </div>\n        <div class=\"tab-content\">\n            <div class=\"tab-pane active\" id=\"machineTab\">\n            </div>\n            <div class=\"tab-pane\" id=\"roomTab\">\n            </div>\n        </div>\n    </div>\n</div>\n\n<script type=\"text/javascript\">\n    function showTab(tab) {\n        $.get($(\"#\" + tab).attr(\"data-url\"), function (result) {\n            $(\"#\" + tab + \"Tab\").html(result);\n        });\n    }\n\n    function refreshActiveTab() {\n        var tab = getQueryString(\"tabTag\");\n        if (tab) {\n            $(\"#\" + tab + \" a\").addClass(\"active\");\n            $(\"#\" + tab + \"Tab\").addClass(\"active\").siblings().removeClass(\"active\");\n        } else {\n            tab = \"machine\";\n            $(\"#\" + tab + \" a\").addClass(\"active\");\n        }\n        console.log(\"tab:\" + tab)\n        showTab(tab);\n        $(\"#tabs li a\").tooltip({placement: \"bottom\"});\n    }\n\n    $(function () {\n        refreshActiveTab();\n    });\n\n    function getQueryString(name) {\n        var reg = new RegExp(\"(^|&)\" + name + \"=([^&]*)(&|$)\");\n        var r = window.location.search.substr(1).match(reg);\n        if (r != null) return unescape(r[2]);\n        return null;\n    }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content\">\n      <div class=\"container-fluid\">\n        <#include \"index.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/machineInstances.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"machineInstancesDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/machineInstancesDetail.html",
    "content": "<script type=\"text/javascript\">\n  $(window).on('load', function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat'\n    });\n  });\n\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        // begin first table\n        $('#tableDataList').dataTable({\n          \"searching\": false,\n          \"lengthChange\": false,\n          \"pageLength\": 100,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          },\n        });\n        jQuery('#tableDataList_wrapper>div:first-child').css(\"display\",\"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    TableManaged.init();\n  });\n\n  function startInstance(appId, instanceId){\n    if(confirm(\"确认要开启\"+instanceId+\"实例吗?\")){\n      $.ajax({\n        type: \"get\",\n        url: \"${request.contextPath}/manage/instance/startInstance.json\",\n        data:\n                {\n                  appId: appId,\n                  instanceId: instanceId\n                },\n        success: function (result) {\n          if(result.success == 1){\n            alert(\"开启成功!\");\n          }else{\n            alert(\"开启失败, msg: \" + result.message)\n          }\n          window.location.reload();\n        }\n      });\n    }\n  }\n\n  function scrollStartInstance(machineIp){\n    if(confirm(\"确认滚动重启容器\"+machineIp+\"下所有实例吗?\")){\n      $.ajax({\n        type: \"get\",\n        url: \"${request.contextPath}/manage/instance/scrollStartInstance.json\",\n        data:\n                {\n                  machineIp: machineIp\n                },\n        success: function (result) {\n          if(result.success == 1){\n            alert(result.message);\n          }else{\n            alert(\"重启失败, msg: \" + result.message)\n          }\n          window.location.reload();\n        }\n      });\n    }\n  }\n\n  function migrateInstance(machineIp){\n\n    var content = \"\";\n    // 1.判断是全部实例迁移还是部分实例\n    var instanceIds =\"\";\n    $('#selectInstance option:selected').each(function(){\n      var instanceId = parseInt($(this).attr(\"value\"));\n      if(instanceId > 0){\n        instanceIds += instanceId+\",\";\n      }else{\n        instanceIds = -1;\n        return false;;\n      }\n    });\n    // 2.目标机器判断\n    var targetIp = $('#targetContainer option:selected').attr(\"value\");\n    if(typeof(targetIp) == \"undefined\" || targetIp == ''){\n      alert(\"请选择目标机器！\");\n      return;\n    }\n    //3.确认实例\n    if(isEmpty(instanceIds)){\n      alert(\"请选择要迁移的实例！\");\n      return;\n    }\n\n    // 4.是否强制迁移\n    var forceFlag = $('#forceFlag option:selected').attr(\"value\");\n    if(forceFlag == 'true'){\n      if(parseInt(instanceIds) == -1){\n        content = \"确认将容器\"+machineIp+\"全部实例——强制——迁移到\"+targetIp+\"容器?\";\n      }else{\n        alert(\"强制迁移无需选择实例！\");\n        return;\n      }\n    }else{\n      if(parseInt(instanceIds) == -1){\n        content = \"确认将容器\"+machineIp+\"全部实例迁移到\"+targetIp+\"容器?\";\n      }else{\n        content = \"确认将容器\"+machineIp+\"实例ID(\"+instanceIds+\")迁移到\"+targetIp+\"容器?\";\n      }\n    }\n\n    // 4.资源是否充足判断\n    if(parseFloat($(\"#containerMM\").attr(\"value\")).toFixed(2) * 0.9 < parseFloat($(\"#needMM\").attr(\"value\")).toFixed(2)){\n      alert(\"目标机器内存使用率将超过90%,请重新分配实例或选择其他目标机器！\");\n      return;\n    }\n\n    // 5.发起迁移\n    if(confirm(content)){\n      $.ajax({\n        type: \"get\",\n        url: \"${request.contextPath}/manage/instance/migrate.json\",\n        data:\n                {\n                  sourceIp: machineIp,\n                  targetIp: targetIp,\n                  instanceIds: instanceIds, // -1:迁移所有实例 ，部分实例迁移,部分实例迁移存这些实例id\n                  forceFlag: forceFlag // 是否强制迁移\n                },\n        success: function (result) {\n          if(result.status == 1){\n            $(\"#migrateInstanceBtn\").click();\n            if(parseInt(instanceIds) == -1){\n              $(\"#tips\").html(\"<label style=\\\"color:red\\\">当前机器实例正在迁移到新容器:<a target=\\\"blank\\\" href=\\\"${request.contextPath}/manage/machine/machineInstances?ip=\"+targetIp+\"\\\">\"+targetIp+\"</a></label>\");\n            }else{\n              $(\"#tips\").html(\"<label style=\\\"color:red\\\">当前机器实例(\"+instanceIds+\")正在迁移到新容器:<a target=\\\"blank\\\" href=\\\"${request.contextPath}/manage/machine/machineInstances?ip=\"+targetIp+\"\\\">\"+targetIp+\"</a></label>\");\n            }\n          }\n        }\n      });\n    }\n  }\n\n  function isEmpty(property) {\n    return (property === null || property === \"\" || typeof property === \"undefined\");\n  }\n\n  // 计算迁移实例需要内存\n  function instanceChange(){\n\n    var totalUsed = 0.00;\n    $('#selectInstance option:selected').each(function(){\n      totalUsed += parseFloat($(this).attr(\"memoryUsed\"));\n    });\n    $(\"#needMM\").html(\"<label style=\\\"color:darkgray\\\">需要空间:\"+totalUsed.toFixed(2)+\"G<label>\");\n    $(\"#needMM\").attr(\"value\",totalUsed);\n  }\n\n  // 迁移机器内存剩余内存\n  function containerChange(){\n\n    var totalUsed = 0.00;\n    $('#targetContainer option:selected').each(function(){\n      totalUsed += parseFloat($(this).attr(\"memoryUsed\"));\n    });\n    $(\"#containerMM\").attr(\"value\",totalUsed);\n\n    //内存提示\n    if(parseFloat(totalUsed).toFixed(2) - parseFloat($(\"#needMM\").attr(\"value\")).toFixed(2) > 0){\n      $(\"#containerMM\").html(\"<label style=\\\"color:green\\\">剩余空间:\"+totalUsed.toFixed(2)+\"G<label>\");\n    }else{\n      $(\"#containerMM\").html(\"<label style=\\\"color:red\\\">空间不够:\"+totalUsed.toFixed(2)+\"G<label>\");\n    }\n  }\n\n  function shutdownInstance(appId, instanceId){\n    if(confirm(\"确认要下线\"+instanceId+\"实例吗?\")){\n      $.ajax({\n        type: \"get\",\n        url: \"${request.contextPath}/manage/instance/shutdownInstance.json\",\n        data:\n                {\n                  appId: appId,\n                  instanceId: instanceId\n                },\n        success: function (result) {\n          if(result.success == 1){\n            alert(\"关闭成功!\");\n          }else{\n            alert(\"关闭失败, msg: \" + result.message)\n          }\n          window.location.reload();\n        }\n      });\n    }\n  }\n</script>\n\n<div class=\"card col-md-12\">\n  <div class=\"card-header\">\n    <h3 class=\"card-title\">\n      <#if (machineInfo.k8sType==1)>\n        机器(ip=<a target=\"_blank\" href=\"${request.contextPath}/manage/machine/pod/changelist?ip=${machineInfo.ip!}\" title=\"查看pod变更记录\">${machineInfo.ip!}</a>)实例列表\n      <#else>\n        机器(ip=${machineInfo.ip!})实例列表\n      </#if>\n      <a target=\"_blank\" onclick=\"scrollStartInstance('${machineInfo.ip!}')\" class=\"btn btn-success\">\n        滚动重启\n      </a>\n      <button id=\"sample_editable_1_new\" class=\"btn btn-success\" data-bs-target=\"#migrateInstanceModal\" data-bs-toggle=\"modal\" onclick=\"instanceChange()\">\n        一键迁移\n      </button>\n      <label id=\"tips\"></label>\n    </h3>\n  </div>\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"col-md-12\">\n        <div class=\"portlet-title\">\n          <div class=\"caption\"><i class=\"bi bi-globe\"></i>实例列表</div>\n          <div class=\"tools\">\n            <a href=\"javascript:;\" class=\"collapse\"></a>\n          </div>\n        </div>\n        <div class=\"portlet box light-grey table-responsive\" id=\"clientIndex\">\n          <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <th scope=\"col\">ID</th>\n              <th scope=\"col\">应用ID</th>\n              <th scope=\"col\">应用名</th>\n              <th scope=\"col\">应用类型</th>\n              <th scope=\"col\">负责人</th>\n              <th scope=\"col\">服务器ip:port</th>\n              <th scope=\"col\">实例空间使用情况</th>\n              <#if machineInfo.type?? && (machineInfo.type == 6)>\n                <th scope=\"col\">实例磁盘使用情况</th>\n              </#if>\n              <th scope=\"col\">连接数</th>\n              <th scope=\"col\">角色</th>\n              <th scope=\"col\">机器可用内存(G)</th>\n              <#if machineInfo.type?? && (machineInfo.type == 6)>\n                <th scope=\"col\">机器可用磁盘(G)</th>\n              </#if>\n              <th scope=\"col\">日志</th>\n              <th scope=\"col\">实例操作</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list instanceList as instance>\n            <tr>\n              <td><a href=\"${request.contextPath}/admin/instance/index?instanceId=${instance.id!}\"\n                     target=\"_blank\">${instance.id!}</a></td>\n              <#assign instanceStatsMapKey = (instance.ip + \":\" + instance.port)>\n              <#assign curAppId = instance.appId>\n              <td>\n                <#if (curAppId > 0)>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${curAppId!}\">\n                    ${curAppId!}\n                  </a>\n                </#if>\n              </td>\n              <td>\n                <#if (curAppId > 0) && appInfoMap?? && (appInfoMap?api.get(curAppId))??>\n                  <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${curAppId!}\">\n                    ${(appInfoMap?api.get(curAppId)).name!}\n                  </a>\n                </#if>\n              </td>\n              <td>\n                <#if (curAppId > 0) && appInfoMap?? && (appInfoMap?api.get(curAppId))??>\n                  <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${curAppId!}\">\n                    ${(appInfoMap?api.get(curAppId)).typeDesc!}\n                  </a>\n                </#if>\n              </td>\n              <td>\n                <#if appInfoMap?? && (appInfoMap?api.get(curAppId))??>\n                  ${(appInfoMap?api.get(curAppId)).officer!}\n                </#if>\n              </td>\n              <td>${instance.ip!}:${instance.port!}</td>\n              <td>\n                <div class=\"progress progress-fs-1\">\n                  <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]?? && instanceStatsMap[instanceStatsMapKey].memUsePercent?? && (instanceStatsMap[instanceStatsMapKey].memUsePercent >= 80)>\n                    <#assign progressBarStatus = \"bg-danger\">\n                  <#else>\n                    <#assign progressBarStatus = \"bg-success\">\n                  </#if>\n                  <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                    <#assign memUsePercentValue = (instanceStatsMap[instanceStatsMapKey].memUsePercent)!>\n                  <#else>\n                    <#assign memUsePercentValue = 0>\n                  </#if>\n                  <div class=\"progress-bar ${progressBarStatus!}\"\n                       role=\"progressbar\"\n                       aria-valuenow=\"${memUsePercentValue!}\"\n                       aria-valuemax=\"100\"\n                       aria-valuemin=\"0\"\n                       style=\"width: ${memUsePercentValue!}%; overflow: visible;\">\n                    <span style=\"color: #000000; margin-bottom: 0\">\n                      <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                        <#assign usedMemoryValue = (instanceStatsMap[instanceStatsMapKey].usedMemory!?number)>\n                        <#assign maxMemoryValue = (instanceStatsMap[instanceStatsMapKey].maxMemory!?number)>\n                      <#else>\n                        <#assign usedMemoryValue = 0>\n                        <#assign maxMemoryValue = 0>\n                      </#if>\n                      ${(usedMemoryValue/ 1024 / 1024 / 1024)?string(\"0.00\")}G&nbsp;&nbsp;Used\n                      <#if machineInfo.type?? && (machineInfo.type != 6)>\n                        /${(maxMemoryValue/ 1024 / 1024 / 1024)?string(\"0.00\")}G&nbsp;&nbsp;Total\n                      </#if>\n                    </span>\n                  </div>\n                </div>\n              </td>\n              <#if machineInfo.type?? && (machineInfo.type == 6)>\n                <td>\n                  <div class=\"progress progress-fs-1\">\n                    <#if instanceStatsMap?? && (instanceStatsMap[instanceStatsMapKey])?? && ((instanceStatsMap[instanceStatsMapKey]).usedDisk??)\n                    && (instance.mem > 0)>\n                      <#assign diskUsedPercentValue = ((instanceStatsMap[instanceStatsMapKey]).usedDisk * 100/(instance.mem * 1024 * 1024))>\n                    <#else>\n                      <#assign diskUsedPercentValue = 0>\n                    </#if>\n                    <#if (diskUsedPercentValue >= 80)>\n                      <#assign progressBarStatus = \"bg-danger\">\n                    <#else>\n                      <#assign progressBarStatus = \"bg-success\">\n                    </#if>\n                    <div class=\"progress-bar ${progressBarStatus!}\" role=\"progressbar\"\n                         aria-valuenow=\"${diskUsedPercentValue}\"\n                         aria-valuemax=\"100\"\n                         aria-valuemin=\"0\"\n                         style=\"width: ${diskUsedPercentValue!}%; overflow: visible;\">\n                      <span style=\"color: #000000; margin-bottom: 0\">\n                        <#if instanceStatsMap?? && (instanceStatsMap[instanceStatsMapKey])?? && ((instanceStatsMap[instanceStatsMapKey]).usedDisk??)>\n                          <#assign userDiskValue = ((instanceStatsMap[instanceStatsMapKey]).usedDisk / 1024 / 1024 / 1024)>\n                        <#else>\n                          <#assign userDiskValue = 0>\n                        </#if>\n                        <#if (instance.mem??)>\n                          <#assign maxDiskValue = (instance.mem / 1024)>\n                        <#else>\n                          <#assign maxDiskValue = 0>\n                        </#if>\n                        ${userDiskValue?string(\"0.00\")}G&nbsp;&nbsp;Used/${maxDiskValue?string(\"0.00\")}G&nbsp;&nbsp;Total\n                      </span>\n                    </div>\n                  </div>\n                </td>\n              </#if>\n              <td>\n                <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                  <#assign currConnectionsValue = (instanceStatsMap[instanceStatsMapKey].currConnections)!>\n                <#else>\n                  <#assign currConnectionsValue = 0>\n                </#if>\n                ${currConnectionsValue!}\n              </td>\n              <td>${instance.roleDesc!}</td>\n              <td>\n                <#if machineInfo.type?? && (machineInfo.type == 6)>\n                  <#if machineStatsMap?? && machineStatsMap[instance.ip]?? && (machineStatsMap[instance.ip].info??) &&\n                    (machineStatsMap[instance.ip].info.mem??) && (machineStatsMap[instance.ip].machineMemInfo??) && (machineStatsMap[instance.ip].machineMemInfo.usedMemRss??)>\n                    ${((machineStatsMap[instance.ip].info.mem)?number - (machineStatsMap[instance.ip].machineMemInfo.usedMemRss/1024/1024/1024))?string(\"0.00\")}\n                  </#if>\n                <#else>\n                  <#if machineCanUseMem?? && (machineCanUseMem[instance.ip])??>\n                    <#assign machineCanUseMemValue = (machineCanUseMem[instance.ip])>\n                  <#else>\n                    <#assign machineCanUseMemValue = 0>\n                  </#if>\n                  ${(machineCanUseMemValue/1024/1024/1024)?string(\"0.00\")}\n                </#if>\n              </td>\n              <#if machineInfo.type?? && (machineInfo.type == 6)>\n                <td>\n                  <#if machineStatsMap?? && machineStatsMap[instance.ip]?? && (machineStatsMap[instance.ip].info??) &&\n                    (machineStatsMap[instance.ip].info.disk??) && (machineStatsMap[instance.ip].machineMemInfo??) && (machineStatsMap[instance.ip].machineMemInfo.usedDisk??)>\n                    ${((machineStatsMap[instance.ip].info.disk)?number - (machineStatsMap[instance.ip].machineMemInfo.usedDisk/1024/1024/1024))?string(\"0.00\")}\n                  </#if>\n                </td>\n              </#if>\n              <td>\n                <a target=\"_blank\" href=\"${request.contextPath}/manage/instance/log?instanceId=${instance.id!}\">查看</a>\n              </td>\n              <td>\n                <#if (instance.status == 0)>\n                  <a target=\"_blank\" onclick=\"startInstance('${curAppId!}', '${instance.id!}')\" class=\"btn btn-success\">\n                    启动实例\n                  </a>\n                </#if>\n                <a target=\"_blank\" onclick=\"shutdownInstance('${curAppId!}', '${instance.id!}')\" class=\"btn btn-danger\">\n                  下线实例\n                </a>\n              </td>\n            </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div id=\"migrateInstanceModal\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog modal-lg\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">实例替换迁移</h4>\n        <b style=\"font-size:12px;color:blue\"><i>支持所有/部分cluster实例迁移</i></b>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    当前机器ip：\n                  </label>\n                  <div class=\"col-md-8\">\n                    ${machineInfo.ip!}\n                    <#if machineInfo.type?? && (machineInfo.type == 6)>\n                      <label class=\"label label-info\">宿主:${machineInfo.realIp!} cpu:${machineInfo.cpu!}核  mem:${machineInfo.mem!}G  disk:${machineInfo.disk!}G</label>\n                    <#else>\n                      <label class=\"label label-info\">宿主:${machineInfo.realIp!} cpu:${machineInfo.cpu!}核  mem:${machineInfo.mem!}G</label>\n                    </#if>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    <font color=\"red\">*</font>选择实例：\n                  </label>\n                  <div class=\"col-md-5\">\n                    <#assign instanceMemoryUsed = 0>\n                    <#assign instanceDiskUsed = 0>\n                    <#list instanceList as instance>\n                      <#if (instance.type==2) || (instance.type==12)>\n                        <#assign instanceStatsMapKey = (instance.ip + \":\" + instance.port)>\n                        <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                          <#assign instanceMemoryUsed = (((instanceStatsMap[instanceStatsMapKey].usedMemory?number) / 1024 / 1024 / 1024) + instanceMemoryUsed)>\n                        <#else>\n                          <#assign instanceMemoryUsed = (0 + instanceMemoryUsed)>\n                        </#if>\n                      </#if>\n\n                      <#if (instance.type==12)>\n                        <#assign instanceStatsMapKey = (instance.ip + \":\" + instance.port)>\n                        <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                          <#assign instanceDiskUsed = (((instanceStatsMap[instanceStatsMapKey].usedDisk?number) / 1024 / 1024 / 1024) + instanceDiskUsed)>\n                        <#else>\n                          <#assign instanceDiskUsed = (0 + instanceDiskUsed)>\n                        </#if>\n                      </#if>\n                    </#list>\n\n                    <#if (machineInfo.type==6)>\n                        <#assign machineUsedCapacity = instanceDiskUsed>\n                    <#else>\n                        <#assign machineUsedCapacity = instanceMemoryUsed>\n                    </#if>\n\n                    <select id=\"selectInstance\" class=\"selectpicker bla bla bli w-100 border rounded\" multiple data-live-search=\"true\" onchange=\"instanceChange()\">\n                      <option value=\"-1\" selected=\"true\" memoryUsed=\"${machineUsedCapacity!}\">全部cluster实例(默认)</option>\n                      <#list instanceList as instance>\n                        <#assign instanceStatsMapKey = (instance.ip + \":\" + instance.port)>\n                        <#if instanceStatsMap?? && instanceStatsMap[instanceStatsMapKey]??>\n                          <#assign instanceMemoryUsedValue = ((instanceStatsMap[instanceStatsMapKey].usedMemory?number) / 1024 / 1024 / 1024)>\n                          <#assign instanceDiskUsedValue = ((instanceStatsMap[instanceStatsMapKey].usedDisk?number) / 1024 / 1024 / 1024)>\n                          <#assign maxMemoryValue = ((instanceStatsMap[instanceStatsMapKey].maxMemory?number) / 1024 / 1024 / 1024)>\n                        <#else>\n                          <#assign instanceMemoryUsedValue = 0>\n                          <#assign instanceDiskUsedValue = 0>\n                          <#assign maxMemoryValue = 0>\n                        </#if>\n                        <#if (instance.type==2)>\n                          <option value=\"${instance.id!}\" memoryUsed=\"${instanceMemoryUsedValue!}\"> 【${instance.id!}】 ${instance.ip!}:${instance.port!}\n                            (Used:${(instanceMemoryUsedValue)?string(\"0.00\")}G/\n                              ${(maxMemoryValue)?string(\"0.00\")}G)\n                          </option>\n                        </#if>\n                        <#if (instance.type==12)>\n                          <option value=\"${instance.id!}\" memoryUsed=\"${instanceDiskUsedValue!}\"> 【${instance.id!}】 ${instance.ip!}:${instance.port!}\n                            (Used:${(instanceDiskUsedValue)?string(\"0.00\")}G/\n                            ${(instance.mem/1024)?string(\"0.00\")}G) (Mem:${(instanceMemoryUsedValue)?string(\"0.00\")}G)\n                          </option>\n                        </#if>\n                      </#list>\n                    </select>\n                  </div>\n                  <label class=\"col-form-label col-md-4\" id=\"needMM\" value=\"0\">\n                  </label>\n                </div>\n\n                <div id=\"redisMachines\" class=\"form-group row\" >\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    <font color=\"red\">*</font>目标机器ip：\n                  </label>\n                  <div class=\"col-md-5\">\n                    <select id=\"targetContainer\" class=\"selectpicker bla bla bli w-100 border rounded\" data-live-search=\"true\" onchange=\"containerChange()\">\n                      <#list machineList as machine>\n                        <#if (machine.info.type==0) || (machine.info.type==6)>\n                          <#if machine.info.ip?? && machineInstanceCountMap[machine.info.ip]??>\n                            <#assign usedCpu = machineInstanceCountMap[machine.info.ip]?string(\"0\")>\n                          <#else>\n                            <#assign usedCpu = \"0\">\n                          </#if>\n                          <#assign cpu = machine.info.cpu?string(\"0\")>\n                          <#assign cpuUsage = ((usedCpu?number)/(cpu?number)*10)?string(\"0\")>\n                          <#assign usedMemRss = ((machine.machineMemInfo.usedMemRss?number)/1024/1024/1024)?string(\"0.0\")>\n                          <#assign mem = machine.info.mem?string(\"0.0\")>\n                          <#assign disk = machine.info.disk?string(\"0.0\")>\n                          <#assign memUsage = ((usedMemRss?number)/(mem?number)*100)?string(\"0\")>\n                          <#if machine.diskUsageRatio?? && machine.diskTotal??>\n                            <#assign diskUsageRatio = machine.diskUsageRatio>\n                            <#assign diskUsage = (((machine.diskTotal?number)/1024 - (machine.diskAvailable?number)/1024)?string(\"0\"))>\n                          <#else>\n                            <#assign diskUsageRatio = '0'>\n                            <#assign diskUsage = '0'>\n                          </#if>\n\n                          <#if (machine.info.useType==0)>\n                            <option value=\"${machine.ip!}\" memoryUsed=\"${(mem?number)-(usedMemRss?number)}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%)  ${diskUsage!}/${disk!}G(${diskUsageRatio!}%) 【${machine.info.realIp!}-${machine.info.rack!}】【专用:${machine.info.extraDesc!}】</option>\n                          </#if>\n                          <#if (machine.info.useType==1)>\n                            <option value=\"${machine.ip!}\" memoryUsed=\"${(mem?number)-(usedMemRss?number)!}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}-${machine.info.rack!}】【测试:${machine.info.extraDesc!}】</option>\n                          </#if>\n                          <#if (machine.info.useType==2)>\n                            <option value=\"${machine.ip!}\" memoryUsed=\"${(mem?number)-(usedMemRss?number)}\">${machine.ip!}：${usedCpu!}/${cpu!}核(${cpuUsage!}%) ${usedMemRss!}/${mem!}G(${memUsage!}%) 【${machine.info.realIp!}-${machine.info.rack!}】【混合:${machine.info.extraDesc!}】</option>\n                          </#if>\n                        </#if>\n                      </#list>\n                      </optgroup>\n                    </select>\n                  </div>\n                  <label class=\"col-form-label col-md-4\" id=\"containerMM\" value=\"0\">\n                  </label>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    <font color=\"red\">!</font>是否强制：\n                  </label>\n                  <div class=\"col-md-5\">\n                    <select id=\"forceFlag\" name=\"forceFlag\" class=\"selectpicker bla bla bli w-100 border rounded\">\n                      <option value=\"false\" selected>否 &nbsp;&nbsp;&nbsp;&nbsp;</option>\n                      <option value=\"true\">是——仅机器(磁盘)故障使用&nbsp;&nbsp;&nbsp;&nbsp;</option>\n                    </select>\n                  </div>\n                </div>\n\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <#if machine?? && machine.info??>\n                <#assign machineInfoId = machine.info.id!>\n            <#else>\n                <#assign machineInfoId = ''>\n            </#if>\n            <div id=\"machineInfo${machineInfoId!}\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" id=\"migrateInstanceBtn\"  data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" class=\"btn btn-danger\" onclick=\"migrateInstance('${machineInfo.ip!}')\">Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/machineList.html",
    "content": "<script src=\"${request.contextPath}/assets/js/custom/machineManage.js\"></script>\n<script type=\"text/javascript\">\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 15,\n          \"language\": {\n            \"lengthMenu\": \"Display _MENU_ records\",\n            \"paginate\": {\n              \"previous\": \"<\",\n              \"next\": \">\"\n            },\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n          }\n        });\n        $('#tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n      }\n    };\n  }();\n\n  $(function () {\n    $('.selectpicker').selectpicker({\n      'selectedText': 'cat',\n      'size': 8,\n      'dropupAuto': false\n    });\n    $('.selectpicker').selectpicker('refresh');\n\n    TableManaged.init();\n  });\n\n  $('#modal-diagnosticResult').on('shown.bs.modal', function (e) {\n    $('#modal-title').html('');\n    $('#diagnosticResultCount').html('');\n    $('#diagnosticResultTable').html('');\n\n    var redisKey = $(e.relatedTarget).data('rediskey');\n    var title = $(e.relatedTarget).data('title');\n    $('#modal-title').html(title);\n    $.get(\n            '${request.contextPath}/manage/app/tool/diagnostic/data.json',\n            {\n              redisKey: redisKey,\n              type: 0\n            },\n            function (data) {\n              $('#diagnosticResultCount').append(\n                      '<tr>' +\n                      '<td>key (共计' + data.count + '个）</td>' +\n                      '</tr>'\n              );\n              var diagnosticResultList = data.result;\n              diagnosticResultList.forEach(function (diagnosticResult, index) {\n                $('#diagnosticResultTable').append(\n                        '<tr>' +\n                        '<td>' + diagnosticResult + '</td>' +\n                        '</tr>'\n                );\n              })\n            }\n    );\n  });\n\n  function changeAppIdSelect(appId, instance_select) {\n    document.getElementById(instance_select).options.length = 0;\n    $('#' + instance_select).selectpicker('destroy');\n    $('#' + instance_select).selectpicker();\n    $.post('${request.contextPath}/manage/app/tool/diagnostic/appInstances',\n            {\n              appId: appId,\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                $('#' + instance_select).selectpicker('destroy');\n                var appInstanceList = data.appInstanceList;\n                $('#' + instance_select).append(\"<option value=''>所有主节点</option>\");\n                for (var i = 0; i < appInstanceList.length; i++) {\n                  var val = appInstanceList[i].hostPort;\n                  var term = appInstanceList[i].hostPort + '（角色：' + appInstanceList[i].roleDesc + '）'\n                  $('#' + instance_select).append(\"<option value='\" + val + \"'>\" + term + \"</option>\");\n                }\n                $('#' + instance_select).selectpicker();\n              } else {\n                console.log('data.status:' + status);\n              }\n            }\n    );\n  }\n\n  function submitDiagnostic(type) {\n    var appId;\n    var size;\n    var nodes;\n    var params = [];\n    if (type == 0) {\n      appId = $('#scan-select').selectpicker('val');\n      if (appId == null || appId == '') {\n        alert(\"请选择应用\");\n        return;\n      }\n\n      nodes = $('#scan_instance-select').selectpicker('val');\n      size = $('#scan_size-select').selectpicker('val');\n      params.push($('#scan-pattern').val());\n      params.push(size);\n    }\n    $.post(\n            '${request.contextPath}/manage/app/tool/diagnostic/submit.json',\n            {\n              type: type,\n              appId: appId,\n              nodes: nodes == null ? \"\" : nodes.toString(),\n              params: params.toString()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 'success') {\n                alert(\"检测任务提交成功，任务id：\" + data.taskId);\n                location.href = \"${request.contextPath}/manage/app/tool/index?tabTag=scan\";\n              } else {\n                toastr.error(\"检测任务提交失败,请查看系统日志确认相关原因!\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div class=\"\">\n        <form class=\"row align-items-center\" action=\"${request.contextPath}/manage/machine/index?tabTag=machine\" method=\"post\" role=\"form\" name=\"ec\">\n          <div class=\"col-md-2\">\n            <input type=\"text\" name=\"ipLike\" class=\"form-control\" id=\"ipLike\" value=\"${ipLike!}\" placeholder=\"机器ip\"/>\n          </div>\n          <div class=\"col-md-2\">\n            <input type=\"text\" name=\"realip\" class=\"form-control\" id=\"realip\" value=\"${realip!}\" placeholder=\"宿主机ip\"/>\n          </div>\n          <div class=\"col-md-2\">\n            <select name=\"useType\" id=\"useType\" class=\"form-select\">\n              <option value=\"-1\">使用类型</option>\n              <option value=\"0\" <#if useType?? && (useType == 0)>selected</#if>>Redis专用机器</option>\n              <option value=\"1\" <#if useType?? && (useType == 1)>selected</#if>>Redis测试机器</option>\n              <option value=\"2\" <#if useType?? && (useType == 2)>selected</#if>>混合部署机器</option>\n            </select>\n          </div>\n          <div class=\"col-md-2\">\n            <select name=\"type\" id=\"type\" class=\"form-select\">\n              <option value=\"-1\">机器类型</option>\n              <option value=\"0\" <#if type?? && (type == 0)>selected</#if>>Redis机器</option>\n              <option value=\"3\" <#if type?? && (type == 3)>selected</#if>>Sentinel机器</option>\n              <option value=\"2\" <#if type?? && (type == 2)>selected</#if>>Redis迁移工具</option>\n            </select>\n          </div>\n          <div class=\"col-md-2\">\n            <select name=\"k8sType\" id=\"k8sType\" class=\"form-select\">\n              <option value=\"-1\">容器类型</option>\n              <option value=\"0\" <#if k8sType?? && (k8sType == 0)>selected</#if>>普通容器</option>\n              <option value=\"1\" <#if k8sType?? && (k8sType == 1)>selected</#if>>k8s容器</option>\n              <option value=\"2\" <#if k8sType?? && (k8sType == 2)>selected</#if>>物理机</option>\n              <option value=\"3\" <#if k8sType?? && (k8sType == 3)>selected</#if>>虚拟机</option>\n            </select>\n          </div>\n          <button type=\"submit\" class=\"btn btn-primary btn-sm col-auto\">查询</button>\n        </form>\n      </div>\n    </div>\n    <div class=\"row ml-2 mt-2\">\n      <button id=\"sample_editable_1_new\" class=\"btn btn-success btn-sm col-auto mr-3\" type=\"button\" data-bs-target=\"#addMachineModal\" data-bs-toggle=\"modal\">\n        添加新机器 <i class=\"bi bi-plus\"></i>\n      </button>\n      <button id=\"down_crash_machine_modal\" class=\"btn btn-danger btn-sm col-auto mr-3\" type=\"button\" data-bs-target=\"#downCrashMachineModal\" data-bs-toggle=\"modal\">\n        物理机迁移 <i class=\"bi bi-plus\"></i>\n      </button>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\" style=\"white-space: nowrap\">\n          <thead>\n          <tr>\n            <th scope=\"col\">ip</th>\n            <th scope=\"col\">宿主机ip</th>\n            <th scope=\"col\">机器类型</th>\n            <th scope=\"col\">实例数/核数</th>\n            <th scope=\"col\">内存使用率</th>\n            <th scope=\"col\">rss内存使用</th>\n            <th scope=\"col\">已分配内存</th>\n            <th scope=\"col\">实例SSD使用率</th>\n            <th scope=\"col\">机器SSD使用率</th>\n            <th scope=\"col\">CPU使用率</th>\n            <th scope=\"col\">机器负载</th>\n            <th scope=\"col\">最后统计时间</th>\n            <th scope=\"col\">机房</th>\n<!--            <th scope=\"col\">使用类型</th>-->\n            <th scope=\"col\">操作系统</th>\n            <th scope=\"col\">额外说明</th>\n            <th scope=\"col\">状态收集</th>\n            <th scope=\"col\">操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list list as machine>\n            <tr>\n              <td>\n                <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/machineInstances?ip=${machine.info.ip!}\">${machine.info.ip!}</a>\n                <#if (machine.info.k8sType==1)>(<a target=\"_blank\" href=\"${request.contextPath}/manage/machine/pod/changelist?ip=${machine.info.ip!}\" title=\"查看pod变更记录\">k8s</a>)</#if>\n                <#if podMonitor?? && machine.info.extraDesc??>\n                  <#assign extraDesc = (machine.info.extraDesc?replace('k8s container:', ''))>\n                  &nbsp;&nbsp;\n                  <a target=\"_blank\" href=\"${podMonitor?replace('@pod@', extraDesc)}\" title=\"查看pod监控\"><i class=\"text-danger bi bi-graph-up\"></i></a>\n                </#if>\n              </td>\n              <td>\n                ${machine.info.realIp!}\n                <#if machineMonitor?? && machine.info.realIp??>\n                  &nbsp;&nbsp;\n                  <a target=\"_blank\" href=\"${machineMonitor?replace('@machine@', machine.info.realIp)}\" title=\"查看物理机监控\"><i class=\"text-danger bi bi-graph-up\"></i></a>\n                </#if>\n              </td>\n              <td>\n                <#if (machine.info.type==0)><span class=\"btn btn-primary\">Redis</span></#if>\n                <#if (machine.info.type==3)><span class=\"btn btn-secondary\">sentinel</span></#if>\n                <#if (machine.info.type==2)><span class=\"btn btn-warning\">RedisTool</span></#if>\n              </td>\n              <td>\n                <#if machineInstanceCountMap?? && machine.info.ip?? && machineInstanceCountMap?api.get(machine.info.ip)??>\n                  <#assign v1 = (machineInstanceCountMap!?api.get(machine.info.ip!)?string(\"0.00\")!?number)>\n                <#else>\n                  <#assign v1 = 0>\n                </#if>\n                <#assign v2 = ((machine.info.cpu)!?string(\"0.00\")!?number)>\n                <#if (v1/v2*100.0 > 100)>\n                  <#assign fmtInstanceCpuRatio = 100>\n                </#if>\n                <#if (v1/v2*100.0 <= 100)>\n                  <#assign fmtInstanceCpuRatio = ((v1/v2*100.0)!?string(\"0.00\")!?number)>\n                </#if>\n                <span style=\"display:none\">${(fmtInstanceCpuRatio / 100)?string(\"0.00\")}></span>\n                <div class=\"progress progress-fs-1\">\n                  <#if (v1/v2*100.0>100)>\n                    <#assign instanceCpuProgressBarStatus = \"bg-danger\">\n                  <#elseif (fmtInstanceCpuRatio >= 80.00)>\n                    <#assign instanceCpuProgressBarStatus = \"bg-warning\">\n                  <#else>\n                    <#assign instanceCpuProgressBarStatus = \"bg-success\">\n                  </#if>\n                  <#if (fmtInstanceCpuRatio == 0.00)>\n                      <#assign instanceCount = \"0\">\n                  <#else>\n                    <#if machineInstanceCountMap?? && machine.info.ip??>\n                      <#assign instanceCount=(machineInstanceCountMap[machine.info.ip])>\n                    <#else>\n                      <#assign instanceCount = 0>\n                    </#if>\n                  </#if>\n                  <div class=\"progress-bar ${instanceCpuProgressBarStatus!}\"\n                       role=\"progressbar\" aria-valuenow=\"${fmtInstanceCpuRatio!}\" aria-valuemax=\"100\"\n                       aria-valuemin=\"0\" style=\"width: ${fmtInstanceCpuRatio!}%; overflow: visible;\">\n                    <span style=\"color: #000000; margin-bottom: 0; text-align: center;\">\n                      ${instanceCount!}&nbsp;&nbsp;/${machine.info.cpu!}&nbsp;&nbsp;\n                    </span>\n                  </div>\n                </div>\n              </td>\n              <td>\n                <#assign usedMem = (((machine.machineMemInfo.usedMem)/1024/1024/1024)?string(\"0.00\")!?number)>\n                <#assign mem = ((machine.info.mem)?string(\"0.00\")!?number)>\n                <#assign fmtMemoryUsageRatio = ((usedMem/mem*100.0)?string(\"0.00\")!?number)>\n                <div class=\"progress progress-fs-1\">\n                  <#if (fmtMemoryUsageRatio >= 80.00)>\n                    <#assign memUsedProgressBarStatus = \"bg-danger\">\n                  <#elseif (fmtMemoryUsageRatio >= 60.00)>\n                    <#assign memUsedProgressBarStatus = \"bg-warning\">\n                  <#else>\n                    <#assign memUsedProgressBarStatus = \"bg-success\">\n                  </#if>\n                  <div class=\"progress-bar ${memUsedProgressBarStatus!}\"\n                       role=\"progressbar\" aria-valuenow=\"${fmtMemoryUsageRatio!}\" aria-valuemax=\"100\"\n                       aria-valuemin=\"0\" style=\"width: ${fmtMemoryUsageRatio!}%; overflow: visible;\">\n                    <span style=\"color: #000000; margin-bottom: 0\">\n                      ${usedMem!}G&nbsp;&nbsp;Used/\n                      ${mem!}G&nbsp;&nbsp;Total\n                    </span>\n                  </div>\n                </div>\n              </td>\n              <td>\n                <#assign usedMemRss = (((machine.machineMemInfo.usedMemRss)/1024/1024/1024)?string(\"0.00\")!?number)>\n                <#assign mem = (machine.info.mem?string(\"0.00\")!?number)>\n                <#assign fmtMemoryUsageRssRatio = (((usedMemRss)/(mem)*100.0)?string(\"0.00\")!?number)>\n                <div class=\"progress progress-fs-1\">\n                  <#if (fmtMemoryUsageRssRatio >= 80.00)>\n                    <#assign memUsedRssProgressBarStatus = \"bg-danger\">\n                  <#elseif (fmtMemoryUsageRssRatio >= 70.00)>\n                    <#assign memUsedRssProgressBarStatus = \"bg-warning\">\n                  <#else>\n                    <#assign memUsedRssProgressBarStatus = \"bg-success\">\n                  </#if>\n                  <div class=\"progress-bar ${memUsedRssProgressBarStatus!}\"\n                       role=\"progressbar\" aria-valuenow=\"${fmtMemoryUsageRssRatio!}\" aria-valuemax=\"100\"\n                       aria-valuemin=\"0\" style=\"width: ${fmtMemoryUsageRatio!}%; overflow: visible;\">\n                    <span style=\"color: #000000; margin-bottom: 0\">\n                      ${usedMemRss!}G&nbsp;&nbsp;Used/\n                      ${mem!}G&nbsp;&nbsp;Total\n                    </span>\n                  </div>\n                </div>\n              </td>\n              <td>\n                <#assign applyMem = (((machine.machineMemInfo.applyMem)/1024/1024/1024)?string(\"0.00\")?number)>\n                <#assign mem = (machine.info.mem?string(\"0.00\")?number)>\n                <#if (applyMem/mem*100.0>100)>\n                  <#assign fmtMemoryAllocatedRatio = 100>\n                </#if>\n                <#if (applyMem/mem*100.0<=100)>\n                  <#assign fmtMemoryAllocatedRatio = ((applyMem/mem*100.0)?string(\"0.00\")!?number)>\n                </#if>\n                <div class=\"progress progress-fs-1\">\n                  <#if (applyMem/mem*100.0 > 100.00)>\n                    <#assign memAllocateProgressBarStatus = \"bg-danger\">\n                  <#elseif (fmtMemoryAllocatedRatio >= 80.00)>\n                    <#assign memAllocateProgressBarStatus = \"bg-warning\">\n                  <#else>\n                    <#assign memAllocateProgressBarStatus = \"bg-success\">\n                  </#if>\n                  <div class=\"progress-bar ${memAllocateProgressBarStatus!}\"\n                       role=\"progressbar\" aria-valuenow=\"${fmtMemoryAllocatedRatio!}\" aria-valuemax=\"100\"\n                       aria-valuemin=\"0\" style=\"width: ${fmtMemoryAllocatedRatio!}%; overflow: visible;\">\n                    <span style=\"color: #000000; margin-bottom: 0\">\n                      ${applyMem!}G&nbsp;&nbsp;Used/\n                      ${mem!}G&nbsp;&nbsp;Total\n                    </span>\n                  </div>\n                </div>\n              </td>\n              <td>\n                <!--实例-->\n                <#assign usedDisk = (((machine.machineMemInfo.usedDisk)/1024/1024/1024)?string(\"0.00\")!?number)>\n                <#assign totalDisk = ((machine.info.disk)?string(\"0.00\")!?number)>\n                <#if (totalDisk == 0)>\n                <#assign fmtDiskUsageRatio = 0>\n                <#else>\n                <#assign fmtDiskUsageRatio = ((usedDisk/totalDisk*100.0)?string(\"0.00\")!?number)>\n              </#if>\n                <!-- 实例使用率-->\n                <div class=\"progress progress-fs-1\">\n                    <#if (fmtDiskUsageRatio >= 80.00)>\n                    <#assign diskUsedProgressBarStatus = \"bg-danger\">\n                    <#elseif (fmtDiskUsageRatio >= 60.00)>\n                    <#assign diskUsedProgressBarStatus = \"bg-warning\">\n                    <#else>\n                    <#assign diskUsedProgressBarStatus = \"bg-success\">\n                  </#if>\n                  <div class=\"progress-bar ${diskUsedProgressBarStatus!}\"\n                       role=\"progressbar\" aria-valuenow=\"${fmtDiskUsageRatio!}\" aria-valuemax=\"100\"\n                       aria-valuemin=\"0\" style=\"width: ${fmtDiskUsageRatio!}%; overflow: visible;\">\n                              <span style=\"color: #000000; margin-bottom: 0\">\n                                ${usedDisk!}G&nbsp;&nbsp;Used/${totalDisk}G Total\n                              </span>\n                  </div>\n                </div>\n              </td>\n\n              <td>\n                <!--机器-->\n                <#if machine.diskTotal??>\n                  <#assign machineTotalDisk = (((machine.diskTotal?number)/1024)?string(\"0\"))>\n                <#else>\n                    <#assign machineTotalDisk = \"0\">\n                </#if>\n                <#if machine.diskTotal?? && machine.diskAvailable??>\n                    <#assign machineUsedDisk = (((machine.diskTotal?number)/1024 - (machine.diskAvailable?number)/1024)?string(\"0\"))>\n                <#else>\n                    <#assign machineUsedDisk = \"0\">\n                </#if>\n                <#if machine.diskUsageRatio??>\n                    <#assign diskUsageRatio = (machine.diskUsageRatio!?number)>\n                <#else>\n                    <#assign diskUsageRatio = 0>\n                </#if>\n                <div class=\"progress progress-fs-1\">\n                  <#if (diskUsageRatio >= 80.00)>\n                    <#assign machineDiskUsedProgressBarStatus = \"bg-danger\">\n                    <#elseif (diskUsageRatio >= 60.00)>\n                    <#assign machineDiskUsedProgressBarStatus = \"bg-warning\">\n                    <#else>\n                    <#assign machineDiskUsedProgressBarStatus = \"bg-success\">\n                  </#if>\n                  <div class=\"progress-bar ${machineDiskUsedProgressBarStatus!}\"\n                       role=\"progressbar\" aria-valuenow=\"${diskUsageRatio!}\" aria-valuemax=\"100\"\n                       aria-valuemin=\"0\" style=\"width: ${diskUsageRatio!}%; overflow: visible;\">\n                            <span style=\"color: #000000; margin-bottom: 0\">\n                              ${machineUsedDisk}G&nbsp;Used/\n                              ${machineTotalDisk}G&nbsp;Total\n                              &nbsp;(${diskUsageRatio}%)\n                            </span>\n                  </div>\n                </div>\n              </td>\n\n              <td>\n                <#if !(machine.cpuUsage??) || (machine.cpuUsage?? && (machine.cpuUsage == ''))>\n                  收集中..${collectAlert!}\n                <#else>\n                  ${machine.cpuUsage!}\n                </#if>\n              </td>\n              <td>\n                <#if !(machine.load??) || (machine.load?? && (machine.load == ''))>\n                  收集中..${collectAlert!}\n                <#else>\n                  ${machine.load!}\n                </#if>\n              </td>\n              <td>\n                <#if machine.modifyTime??>\n                  ${machine.modifyTime?string(\"yyyy-MM-dd HH:mm\")}\n                </#if>\n              </td>\n              <td>${machine.info.room!}</td>\n              <td>\n                <#if machine.info.disType == 0>\n                    <span class=\"badge badge-primary\">Centos</span>\n                <#elseif machine.info.disType == 1>\n                    <span class=\"badge badge-secondary\">Ubuntu</span>\n                </#if>\n\n              </td>\n              <td>\n                ${machine.info.extraDesc!}\n                <#if (machine.info.type == 2)>\n                  <font color='red'>(迁移工具机器)</font>\n                </#if>\n              </td>\n              <#if (machine.info.collect == 1)>\n                <td>开启</td>\n              <#else>\n                <td>关闭</td>\n              </#if>\n              <td>\n                <a href=\"${request.contextPath}/server/index?ip=${machine.info.ip!}\" class=\"btn btn-info btn-sm\" target=\"_blank\">监控</a>\n                &nbsp;\n                <a href=\"javascript:void(0);\" data-bs-target=\"#addMachineModal${machine.info.id!}\" class=\"btn btn-warning btn-sm\" data-bs-toggle=\"modal\">修改</a>\n                &nbsp;\n                <button id=\"removeMachineBtn${machine.info.id!}\" onclick=\"removeMachine(this.id,'${machine.info.ip!}', '${request.contextPath}')\" type=\"button\" class=\"btn btn-danger btn-sm\">删除</button>\n              </td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n<#list list as machine>\n  <#include \"addMachine.html\">\n</#list>\n<#include \"addMachine.html\">\n<#include \"downCrashMachine.html\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/roomList.html",
    "content": "<script src=\"${request.contextPath}/assets/js/custom/machineManage.js\"></script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div class=\"row mb-2\">\n      <button class=\"btn btn-success btn-sm col-auto\" type=\"button\" data-bs-target=\"#addRoomModal\" data-bs-toggle=\"modal\">\n        添加机房 <i class=\"bi bi-plus\"></i>\n      </button>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\">\n          <thead>\n          <tr>\n            <th scope=\"col\">序号</th>\n            <th scope=\"col\">机房名称</th>\n            <th scope=\"col\">可用状态</th>\n            <th scope=\"col\">描述</th>\n            <th scope=\"col\">网段</th>\n            <th scope=\"col\">运营商</th>\n            <th scope=\"col\">操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list roomList as record>\n            <tr>\n              <td>${record_index + 1}</td>\n              <td>${record.name!}</td>\n              <td>\n                <#if (record.status==0)>无效</#if>\n                <#if (record.status==1)>有效</#if>\n              </td>\n              <td>${record.desc!}</td>\n              <td>${record.ipNetwork!}</td>\n              <td>${record.operator!}</td>\n              <td>\n                <#include \"addRoom.html\">\n                <a href=\"javascript:void(0);\" data-bs-target=\"#addRoomModal${record.id!}\"\n                   class=\"btn btn-sm btn-info\" data-bs-toggle=\"modal\">修改</a>\n                <button id=\"removeBtn${record.id!}\" type=\"button\" class=\"btn btn-sm btn-info\"\n                        onclick=\"removeRoom(this.id,'${record.id!}', '${request.contextPath}')\">删除\n                </button>\n              </td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n<#include \"addRoom.html\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/taskInfo.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>迁移信息</title>\n  <#include '/inc/frontResources.html'>\n</head>\n<body role=\"document dashboard\">\n<div class=\"container\">\n  <#include '/inc/head.html'>\n  <div id=\"systemAlert\">\n  </div>\n  <main id=\"main\" class=\"main px-5 ms-0\">\n    <div class=\"page-content\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">\n            迁移信息\n          </h3>\n        </div>\n\n        <div class=\"form\">\n          <div class=\"form-body\">\n            <div class=\"card-body\">\n              <div class=\"form-group row\">\n                <div class=\"col-md-12\">\n                  <h3 class=\"card-title text-warning\">\n                    迁移状态： ${taskStatus!}  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\n                    <#if taskId??>\n                      <a target=\"_blank\" href=\"${request.contextPath}/manage/task/flow?taskId=${taskId!}\"> 查看任务详情</a>\n                    </#if>\n                  </h3>\n                  <br>\n                  <br>\n                  <h3 class=\"card-title\">\n                    迁移信息：\n                  </h3>\n                  <table class=\"table table-striped table-bordered table-hover table-sm\" style=\"white-space: nowrap\">\n                    <thead>\n                      <tr>\n                        <th scope=\"col\">物理机ip</th>\n                        <th scope=\"col\">待迁移ip</th>\n                        <th scope=\"col\">已迁移实例/总实例数</th>\n                        <th scope=\"col\">新pod 已迁移实例数</th>\n                      </tr>\n                    </thead>\n                    <tbody>\n                        <tr>\n                          <td>\n                            ${machineIp!}\n                          </td>\n                          <td>\n                            <#if podInstancesMap?? && podInstancesMap?keys??>\n                              <#list podInstancesMap?keys as podIp>\n                                <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/machineInstances?ip=${podIp!}\"> ${podIp!}</a>\n                                <br>\n                              </#list>\n                            </#if>\n                          </td>\n                          <td>\n                            <#assign totalAddCount = 0>\n                            <#if newPodAddedInstancesMap??>\n                              <#list newPodAddedInstancesMap?keys as podIp>\n                                <#assign addCount = newPodAddedInstancesMap[podIp]>\n                                <#assign totalAddCount = totalAddCount + addCount>\n                              </#list>\n                            </#if>\n                            <#assign totalCount = 0>\n                            <#if podInstancesMap?? && podInstancesMap?keys??>\n                              <#list podInstancesMap?keys as podIp>\n                                <#assign totalCount = totalCount + podInstancesMap[podIp]>\n                              </#list>\n                            </#if>\n                            ${totalAddCount!} / ${totalCount!}\n                          </td>\n\n                          <#if newPodAddedInstancesMap??>\n                            <td>\n                              <#list newPodAddedInstancesMap?keys as podIp>\n                                  <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/machineInstances?ip=${podIp!}\"> ${podIp!}</a> &nbsp;&nbsp;&nbsp;&nbsp; ${newPodAddedInstancesMap[podIp]}\n                                  <br>\n                              </#list>\n                            </td>\n                          <#else>\n                            <td></td>\n                          </#if>\n                        </tr>\n                    </tbody>\n                  </table>\n                  <br/>\n              </div>\n            </form>\n          </div>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/machine/taskInfo_bak.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>迁移信息</title>\n  <#include '/inc/frontResources.html'>\n</head>\n<body role=\"document dashboard\">\n<div class=\"container\">\n  <#include '/inc/head.html'>\n  <div id=\"systemAlert\">\n  </div>\n  <main id=\"main\" class=\"main px-5 ms-0\">\n    <div class=\"page-content\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">\n            迁移信息\n          </h3>\n        </div>\n\n        <div class=\"form\">\n          <div class=\"form-body\">\n            <div class=\"card-body\">\n              <div class=\"form-group row\">\n                <div class=\"col-md-12\">\n\n                  <h3 class=\"card-title\">\n                    待迁移pod——部署实例数\n                  </h3>\n                  <table class=\"table table-striped table-bordered table-hover table-sm\" style=\"white-space: nowrap\">\n                    <thead>\n                      <tr>\n                        <th scope=\"col\">ip</th>\n                        <th scope=\"col\">实例数</th>\n                      </tr>\n                    </thead>\n                    <tbody>\n                      <#if podInstancesMap?? && podInstancesMap?keys??>\n                        <#list podInstancesMap?keys as podIp>\n                        <tr>\n                          <td>${podIp}</td>\n                          <td>${podInstancesMap[podIp]}</td>\n                        </tr>\n                        </#list>\n                      </#if>\n                    </tbody>\n                  </table>\n                  <br/>\n\n                  <h3 class=\"card-title\">\n                    待迁移pod——不可迁移实例信息\n                  </h3>\n                  <table class=\"table table-striped table-bordered table-hover table-sm\" style=\"white-space: nowrap\">\n                    <thead>\n                    <tr>\n                      <th scope=\"col\">ip</th>\n                      <th scope=\"col\">实例</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#if podNoMigrateInstanceMap?? && podNoMigrateInstanceMap?keys??>\n                      <#list podNoMigrateInstanceMap?keys as podIp>\n                        <tr>\n                          <td>${podIp!}</td>\n                          <td>\n                            <#assign podInstances = podNoMigrateInstanceMap[podIp]>\n                            <#if podInstances??>\n                              <#list podInstances as instance>\n                                ${instance!}<br/>\n                              </#list>\n                            </#if>\n                          </td>\n                        </tr>\n                      </#list>\n                    </#if>\n                    </tbody>\n                  </table>\n                  <br/>\n\n                  <h3 class=\"card-title\">\n                    待迁移应用——需迁移实例数\n                  </h3>\n                  <table class=\"table table-striped table-bordered table-hover table-sm\" style=\"white-space: nowrap\">\n                    <thead>\n                    <tr>\n                      <th scope=\"col\">appId</th>\n                      <th scope=\"col\">实例数</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#if appInstancesMap??>\n                      <#list appInstancesMap?keys as appId>\n                      <tr>\n                        <td>${appId!}</td>\n                        <td>${appInstancesMap[appId]}</td>\n                      </tr>\n                      </#list>\n                    </#if>\n                    </tbody>\n                  </table>\n                  <br/>\n\n                  <h3 class=\"card-title\">\n                    待迁移应用——待添加从节点信息及结果（结果会随页面刷新而刷新）\n                  </h3>\n                  <table class=\"table table-striped table-bordered table-hover table-sm\" style=\"white-space: nowrap\">\n                    <thead>\n                    <tr>\n                      <th scope=\"col\">appId</th>\n                      <th scope=\"col\">主节点</th>\n                      <th scope=\"col\">待添加从ip</th>\n                      <th scope=\"col\">结果</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#if toAddInstancesMap??>\n                      <#list toAddInstancesMap?keys as appId>\n                      <tr>\n                        <td>${appId!}</td>\n                        <#assign addInstance = toAddInstancesMap[appId]>\n                        <#if addInstance??>\n                          <td>${addInstance?api.getLeft()!}</td>\n                          <#if addInstance?api.getMiddle()??>\n                            <td>${addInstance?api.getMiddle()}</td>\n                          <#else>\n                            <td>未分配到从节点，请关注</td>\n                          </#if>\n                          <td>${addInstance?api.getRight()!}</td>\n                        <#else>\n                          <td></td>\n                          <td></td>\n                          <td></td>\n                        </#if>\n                      </tr>\n                      </#list>\n                    </#if>\n                    </tbody>\n                  </table>\n                  <br/>\n\n                  <h3 class=\"card-title\">\n                    新分配pod——实例部署信息与结果\n                  </h3>\n                  <table class=\"table table-striped table-bordered table-hover table-sm\" style=\"white-space: nowrap\">\n                    <thead>\n                    <tr>\n                      <th scope=\"col\">podIp</th>\n                      <th scope=\"col\">需补充实例数</th>\n                      <th scope=\"col\">已完成实例数</th>\n                      <th scope=\"col\">未完成实例数</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#if newPodInstancesMap??>\n                      <#list newPodInstancesMap?keys as podIp>\n                      <tr>\n                        <td>${podIp!}</td>\n                        <#assign todoCount = newPodInstancesMap[podIp]>\n                        <#if newPodAddedInstancesMap??>\n                          <#assign addCount = newPodAddedInstancesMap[podIp]>\n                        <#else>\n                          <#assign addCount = 0>\n                        </#if>\n                        <td>${todoCount}</td>\n                        <td>${addCount}</td>\n                        <td>${todoCount - addCount}</td>\n                      </tr>\n                      </#list>\n                    </#if>\n                    </tbody>\n                  </table>\n                  <br/>\n\n              </div>\n            </form>\n          </div>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/migrate/index.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">迁移数据记录列表</h3>\n            </div>\n            <div class=\"card-body\">\n                <h4>\n                    <a target=\"_blank\" href=\"${request.contextPath}/data/migrate/init\" class=\"btn btn-info btn-success\"\n                       role=\"button\">+ 添加新迁移</a>\n                </h4>\n\n                <form class=\"row mb-2 align-items-center\" role=\"form\" method=\"post\" action=\"${request.contextPath}/data/migrate/index\" id=\"pageForm\" name=\"ec\">\n                    <div class=\"col-md-2\">\n                        <input type=\"text\" class=\"form-control\" id=\"sourceAppId\" name=\"sourceAppId\"\n                               value=\"${appDataMigrateSearch.sourceAppId!}\" placeholder=\"源appId\">\n                    </div>\n                    <div class=\"col-md-2\">\n                        <input type=\"text\" class=\"form-control\" id=\"targetAppId\" name=\"targetAppId\"\n                               value=\"${appDataMigrateSearch.targetAppId!}\" placeholder=\"目标appId\">\n                    </div>\n                    <div class=\"col-md-2\">\n                        <input type=\"text\" class=\"form-control\" id=\"migrateMachine\" name=\"migrateMachine\"\n                               value=\"${appDataMigrateSearch.migrateMachine!}\" placeholder=\"迁移机器\">\n                    </div>\n                    <div class=\"col-md-2\">\n                        <select name=\"userId\" class=\"form-select\">\n                            <option value=\"\" <#if !(appDataMigrateSearch??) || (appDataMigrateSearch?? && (appDataMigrateSearch.userId! == ''))>selected</#if>>\n                            操作人\n                            </option>\n                            <#list adminList as admin>\n                                <option value=\"${admin.id!}\" <#if appDataMigrateSearch?? && (appDataMigrateSearch.userId! == (admin.id?string))>selected</#if>>\n                                【${admin.id!}】${admin.name!}&nbsp;${admin.chName!}\n                                </option>\n                            </#list>\n                        </select>\n                    </div>\n\n                    <div class=\"col-md-2\">\n                        <select name=\"status\" class=\"form-select\">\n                            <option value=\"3\" <#if (appDataMigrateSearch.status == 3)>selected</#if>>\n                            增量同步\n                            </option>\n                            <option value=\"-2\" <#if (appDataMigrateSearch.status == -2)>selected</#if>>\n                            全部状态\n                            </option>\n                            <option value=\"0\" <#if (appDataMigrateSearch.status == 0)>selected</#if>>\n                            全量同步\n                            </option>\n                            <option value=\"1\" <#if (appDataMigrateSearch.status == 1)>selected</#if>>\n                            同步结束\n                            </option>\n                            <option value=\"2\" <#if (appDataMigrateSearch.status == 2)>selected</#if>>\n                            同步异常\n                            </option>\n                        </select>\n                    </div>\n\n                    <input type=\"hidden\" name=\"pageNo\" id=\"pageNo\">\n                    <button type=\"submit\" class=\"btn btn-primary col-md-1\">查询</button>\n                </form>\n            </div>\n        </div>\n\n        <div class=\"card\">\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-striped table-bordered table-hover table-sm\">\n                    <thead>\n                    <tr>\n                        <th scope=\"col\">任务ID</th>\n                        <th scope=\"col\">迁移ID</th>\n<!--                        <th scope=\"col\">迁移工具</th>-->\n                        <th scope=\"col\">迁移机器</th>\n                        <th scope=\"col\">操作人</th>\n                        <th scope=\"col\">源数据</th>\n                        <th scope=\"col\">目标数据</th>\n                        <th scope=\"col\">开始/结束时间</th>\n                        <th scope=\"col\">状态</th>\n                        <th scope=\"col\">查看</th>\n                        <th scope=\"col\">操作</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                        <#list appDataMigrateStatusList as appDataMigrateStatus>\n                            <tr>\n                                <td>\n                                    ${appDataMigrateStatus.id!}\n                                    <input type=\"hidden\" id=\"id\" value=\"${appDataMigrateStatus.id!}\"/>\n                                </td>\n                                <td>\n                                    ${appDataMigrateStatus.migrateId!}<br/>\n                                    <#if appDataMigrateStatus.configPath??>\n                                        <span class=\"btn btn-sm btn-secondary\">${appDataMigrateStatus.configPath?split(\"/\")[3]}</b>\n                                    </#if>\n                                    <input type=\"hidden\" id=\"migrateTool\" value=\"${appDataMigrateStatus.migrateTool!}\"/>\n                                </td>\n<!--                                <td>-->\n<!--                                    <#if (appDataMigrateStatus.migrateTool == 0)>-->\n<!--                                        redis-shake-->\n<!--                                    <#else>-->\n<!--                                        redis-migrate-tool-->\n<!--                                    </#if>-->\n<!--                                </td>-->\n                                <td>\n                                    <#if (appDataMigrateStatus.migrateTool == 1)>\n                                        ${appDataMigrateStatus.migrateMachineIp!}:${appDataMigrateStatus.migrateMachinePort!}\n                                    <#else>\n                                        ${appDataMigrateStatus.migrateMachineIp!}\n                                    </#if>\n                                </td>\n                                <td>${appDataMigrateStatus.userName!}</td>\n                                <td>\n                                    数据源：\n                                    <#if (appDataMigrateStatus.sourceAppId <= 0)>\n                                        非cachecloud\n                                        <br/>\n                                        ${appDataMigrateStatus.sourceServers!}\n                                    <#else>\n                                        cachecloud:<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDataMigrateStatus.sourceAppId!}\">${appDataMigrateStatus.sourceAppId!}</a>\n                                    </#if>\n                                    <br/>\n                                    源类型：redis-${appDataMigrateStatus.sourceMigrateTypeDesc!}\n                                    <br/>\n                                    redis版本：${appDataMigrateStatus.redisSourceVersion!}<br/>\n                                    <#if (appDataMigrateStatus.sourceMigrateType == 8 || appDataMigrateStatus.sourceMigrateType == 9)>\n                                        <a href=\"/data/migrate/tasklist?migrate_id=${appDataMigrateStatus.migrateId!}\" class=\"btn btn-sm btn-warning\">查看任务:${appDataMigrateStatus.taskCount!}</a>\n                                    </#if>\n                                </td>\n                                <td>\n                                    数据源：\n                                    <#if (appDataMigrateStatus.targetAppId <= 0)>\n                                        非cachecloud\n                                        <br/>\n                                        ${appDataMigrateStatus.targetServers!}\n                                    <#else>\n                                        cachecloud:<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDataMigrateStatus.targetAppId!}\">${appDataMigrateStatus.targetAppId!}</a>\n                                    </#if>\n                                    <br/>\n\n                                    <#if (appDataMigrateStatus.sourceMigrateType == 8 || appDataMigrateStatus.sourceMigrateType == 9)>\n                                    <#else>\n                                         目标类型：redis-${appDataMigrateStatus.targetMigrateTypeDesc!}\n                                    </#if>\n                                    <br/>\n                                    redis版本：${appDataMigrateStatus.redisTargetVersion!}\n                                </td>\n                                <td>\n                                    ${appDataMigrateStatus.startTimeFormat!}<br/>\n                                    ${appDataMigrateStatus.endTimeFormat!}\n                                </td>\n                                <td>${appDataMigrateStatus.statusDesc!}</td>\n                                <td>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/data/migrate/log?id=${appDataMigrateStatus.id!}\">日志</a><br/><br/>\n                                    <a target=\"_blank\"\n                                       href=\"${request.contextPath}/data/migrate/config?id=${appDataMigrateStatus.id!}\">配置</a><br/><br/>\n                                    <#if (appDataMigrateStatus.status == 0)>\n                                        <#if (appDataMigrateStatus.migrateTool == 1)>\n                                            <a target=\"_blank\"\n                                               href=\"${request.contextPath}/data/migrate/process?id=${appDataMigrateStatus.id!}&migrateTool=${appDataMigrateStatus.migrateTool!}\">进度</a>\n                                        </#if>\n                                        <#if (appDataMigrateStatus.migrateTool == 0)>\n                                            <a data-bs-toggle=\"modal\" data-target=\"#processModal\">进度</a>\n                                        </#if>\n                                    </#if>\n                                    <div class=\"modal fade\" id=\"processModal\" tabindex=\"-1\" role=\"dialog\"\n                                         aria-labelledby=\"myModalLabel\" aria-hidden=\"true\">\n                                        <div class=\"modal-dialog\">\n                                            <div class=\"modal-content\">\n                                                <div class=\"modal-header\">\n                                                    <h4 class=\"modal-title\" id=\"myModalLabel\">\n                                                        迁移进度\n                                                    </h4>\n                                                    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n                                                </div>\n                                                <div class=\"modal-body\" id=\"processModal_body\">\n\n                                                </div>\n                                                <div class=\"modal-footer\">\n                                                    <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">关闭\n                                                    </button>\n                                                </div>\n                                            </div><!-- /.modal-content -->\n                                        </div><!-- /.modal -->\n                                    </div>\n                                </td>\n                                <td>\n                                    <button onclick=\"resyncMigrate(${appDataMigrateStatus.migrateId!})\" type=\"button\"\n                                    class=\"btn btn-sm btn-warning\">重新同步\n                                    </button>\n                                    <br/>\n                                    <button  onclick=\"stopMigrate(${appDataMigrateStatus.id!},${appDataMigrateStatus.migrateId!})\" type=\"button\"\n                                         <#if (appDataMigrateStatus.status == 1 || appDataMigrateStatus.status == 2)>\n                                                disabled=\"disabled\" class=\"btn btn-sm btn-secondary\"\n                                        <#else>\n                                                class=\"btn btn-sm btn-danger\"\n                                        </#if>\n                                    >停止任务\n                                    </button>\n                                    <br/>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/data/migrate/checkDataRandom?id=${appDataMigrateStatus.id!}\"\n                                       class=\"btn btn-sm btn-info\" role=\"button\">数据校验</a>\n                                </td>\n                            </tr>\n                        </#list>\n                    </tbody>\n                </table>\n                <div style=\"margin-bottom: 10px;\">\n                    <span>\n                        <div id=\"pageDetail\"\n                             style=\"float:left;padding-top:7px;color:#4A64A4;\">\n                            <#if page??>\n                                共${page.totalPages!}页,${page.totalCount!}条\n                            </#if>\n                        </div>\n                        <nav id='ccPagenitor' aria-label=\"Page navigation example\" class=\"d-inline-flex float-end\">\n                        </nav>\n                    </span>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n<script type=\"text/javascript\">\n    $(function () {\n        $('#processModal').on('shown.bs.modal',\n            function () {\n                $.get(\n                    '${request.contextPath}/data/migrate/process.json',\n                    {\n                        id: document.getElementById(\"id\").value,\n                        migrateTool: document.getElementById(\"migrateTool\").value\n                    },\n                    function (data) {\n                        var result = data.process.replace(/\\n/, '<br>');\n                        $(\"#processModal_body\").html(result);\n                    }\n                );\n            })\n    });\n    function stopMigrate(id,migrate_id) {\n        if (window.confirm(\"确认要停掉id=\" + id + \"的迁移任务吗?\")) {\n            $.get(\n                '${request.contextPath}/data/migrate/stop.json',\n                {\n                    id: id,\n                    migrate_id: migrate_id\n                },\n                function (data) {\n                    var status = data.status;\n                    alert(data.message);\n                    location.href = \"${request.contextPath}/data/migrate/index\";\n                }\n            );\n        }\n    }\n\n    function resyncMigrate(migrate_id) {\n        if (window.confirm(\"确认重新同步迁移id=\" + migrate_id + \"的迁移任务吗?\")) {\n            $.get(\n                '${request.contextPath}/data/migrate/resync.json',\n                {\n                    migrate_id: migrate_id\n                },\n                function (data) {\n                    var status = data.status;\n                    alert(data.message);\n                    location.href = \"${request.contextPath}/data/migrate/index\";\n                }\n            );\n        }\n    }\n\n\n</script>\n<#include \"/manage/inc/page.html\">\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/migrate/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"index.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n\n<script>\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        if (!jQuery().dataTable) {\n          return;\n        }\n        console.log(\"jQuery().dataTable\");\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          // \"scrollX\": true,\n          // \"autoWidth\": true,\n          \"ordering\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 10,\n          \"language\": {\n            \"paginate\": {\n              \"first\": \"<<\",\n              \"previous\": \"<\",\n              \"next\": \">\",\n              \"last\": \">>\"\n            },\n            \"lengthMenu\": \"每页显示 _MENU_条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n          }\n        });\n      }\n    };\n  }();\n\n  $(function () {\n    TableManaged.init();\n  });\n</script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/migrate/taskIndex.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">迁移任务列表</h3>\n                <span type=\"button\" class=\"btn btn-sm btn-warning\" onclick=\"refeshMigrate('${migrate_id}')\">更新状态</span>\n            </div>\n            <div class=\"card-body\">\n            </div>\n        </div>\n\n<div class=\"card\">\n    <div class=\"card-body table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover table-sm\">\n            <thead>\n            <tr>\n                <th scope=\"col\">任务ID</th>\n                <th scope=\"col\">迁移ID</th>\n                <th scope=\"col\">类型</th>\n                <th scope=\"col\">迁移机器</th>\n                <th scope=\"col\">源数据</th>\n                <th scope=\"col\">目标数据</th>\n                <th scope=\"col\">开始/结束时间</th>\n                <th scope=\"col\">状态</th>\n                <th scope=\"col\">查看</th>\n                <th scope=\"col\">操作</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list taskList as appDataMigrateStatus>\n            <tr>\n                <td>\n                    ${appDataMigrateStatus.id!}\n                    <input type=\"hidden\" id=\"id\" value=\"${appDataMigrateStatus.id!}\"/>\n                </td>\n                <td>\n                    ${appDataMigrateStatus.migrateId!}<br/>\n                </td>\n                <td>\n                    <#if (appDataMigrateStatus.sourceMigrateType == 9)>\n                        <span class=\"btn btn-sm btn-warning\">predixy</span>\n                    <#else>\n                        <span class=\"btn btn-sm btn-success\">shake</span>\n                    </#if>\n                </td>\n                <td>\n                    ${appDataMigrateStatus.migrateMachineIp!}:${appDataMigrateStatus.migrateMachinePort!}\n                </td>\n                <td>\n                    数据源：\n                <#if (appDataMigrateStatus.sourceAppId <= 0)>\n                    非cachecloud\n                    <br/>\n                    ${appDataMigrateStatus.sourceServers!}\n                <#else>\n                    cachecloud:<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDataMigrateStatus.sourceAppId!}\">${appDataMigrateStatus.sourceAppId!}</a>\n                </#if>\n                <br/>\n                <#if appDataMigrateStatus.configPath?? && appDataMigrateStatus.sourceMigrateType == 8>\n                    <#assign splitList = appDataMigrateStatus.configPath?split(\"-\")>\n                同步实例：<span class=\"text-success\">${splitList[splitList?size-1]?replace(\".toml\",\"\")}</span> <br/>\n                </#if>\n                redis版本：${appDataMigrateStatus.redisSourceVersion!}<br/>\n\n            </td>\n            <td>\n                数据源：\n                <#if (appDataMigrateStatus.targetAppId <= 0)>\n                非cachecloud\n                <br/>\n                ${appDataMigrateStatus.targetServers!}\n                <#else>\n                cachecloud:<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDataMigrateStatus.targetAppId!}\">${appDataMigrateStatus.targetAppId!}</a>\n            </#if>\n            <br/>\n            redis版本：${appDataMigrateStatus.redisTargetVersion!}\n            </td>\n            <td>\n                ${appDataMigrateStatus.startTimeFormat!}\n                <br/>\n                ${appDataMigrateStatus.endTimeFormat!}\n            </td>\n            <td>${appDataMigrateStatus.statusDesc!}</td>\n            <td>\n                <a target=\"_blank\" href=\"${request.contextPath}/data/migrate/log?id=${appDataMigrateStatus.id!}\">日志</a><br/><br/>\n                <a target=\"_blank\"\n                   href=\"${request.contextPath}/data/migrate/config?id=${appDataMigrateStatus.id!}\">配置</a><br/><br/>\n                <#if (appDataMigrateStatus.status == 0)>\n                <#if (appDataMigrateStatus.migrateTool == 1)>\n                <a target=\"_blank\"\n                   href=\"${request.contextPath}/data/migrate/process?id=${appDataMigrateStatus.id!}&migrateTool=${appDataMigrateStatus.migrateTool!}\">进度</a>\n            </#if>\n            <#if (appDataMigrateStatus.migrateTool == 0)>\n            <a data-bs-toggle=\"modal\" data-target=\"#processModal\">进度</a>\n            </#if>\n        </#if>\n        <div class=\"modal fade\" id=\"processModal\" tabindex=\"-1\" role=\"dialog\"\n             aria-labelledby=\"myModalLabel\" aria-hidden=\"true\">\n            <div class=\"modal-dialog\">\n                <div class=\"modal-content\">\n                    <div class=\"modal-header\">\n                        <h4 class=\"modal-title\" id=\"myModalLabel\">\n                            迁移进度\n                        </h4>\n                        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n                    </div>\n                    <div class=\"modal-body\" id=\"processModal_body\">\n\n                    </div>\n                    <div class=\"modal-footer\">\n                        <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">关闭\n                        </button>\n                    </div>\n                </div><!-- /.modal-content -->\n            </div><!-- /.modal -->\n        </div>\n        </td>\n        <td>\n            <button onclick=\"resyncMigrate(${appDataMigrateStatus.migrateId!},${appDataMigrateStatus.id!})\" type=\"button\"\n                    class=\"btn btn-sm btn-info\">重新同步\n            </button>\n    </button>\n    </td>\n    </tr>\n</#list>\n</tbody>\n</table>\n<div style=\"margin-bottom: 10px;\">\n                    <span>\n                        <div id=\"pageDetail\"\n                             style=\"float:left;padding-top:7px;color:#4A64A4;\">\n                            <#if page??>\n                                共${page.totalPages!}页,${page.totalCount!}条\n                            </#if>\n</div>\n<nav id='ccPagenitor' aria-label=\"Page navigation example\" class=\"d-inline-flex float-end\">\n</nav>\n</span>\n</div>\n</div>\n</div>\n</div>\n</div>\n\n<script type=\"text/javascript\">\n\n    function stopMigrate(id,migrate_id) {\n        if (window.confirm(\"确认要停掉id=\" + id + \"的迁移任务吗?\")) {\n            $.get(\n                '${request.contextPath}/data/migrate/stop.json',\n                {\n                    id: id,\n                    migrate_id: migrate_id\n                },\n                function (data) {\n                    var status = data.status;\n                    alert(data.message);\n                    location.href = \"${request.contextPath}/data/migrate/index\";\n                }\n            );\n        }\n    }\n\n\n    function refeshMigrate(migrate_id) {\n        $.get(\n            '${request.contextPath}/data/migrate/refresh.json',\n            {\n                migrate_id: migrate_id\n            },\n            function (data) {\n                alert(data.message);\n                location.href = \"${request.contextPath}/data/migrate/index\";\n                window.location.reload(2000);\n            }\n        );\n    }\n\n    function resyncMigrate(migrate_id,task_id) {\n        if (window.confirm(\"确认重新同步id=\" + task_id + \"的迁移任务吗?\")) {\n            $.get(\n                '${request.contextPath}/data/migrate/resync.json',\n                {\n                    task_id: task_id,\n                    migrate_id: migrate_id\n                },\n                function (data) {\n                    alert(data.message);\n                    window.location.reload(1000);\n                }\n            );\n        }\n    }\n\n\n</script>\n<#include \"/manage/inc/page.html\">\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/migrate/tasklist.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"taskIndex.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n\n<script>\n  var TableManaged = function () {\n    return {\n      //main function to initiate the module\n      init: function () {\n        if (!jQuery().dataTable) {\n          return;\n        }\n        console.log(\"jQuery().dataTable\");\n        $('#tableDataList').dataTable({\n          \"searching\": true,\n          // \"scrollX\": true,\n          // \"autoWidth\": true,\n          \"ordering\": true,\n          \"lengthChange\": false,\n          \"pageLength\": 10,\n          \"language\": {\n            \"paginate\": {\n              \"first\": \"<<\",\n              \"previous\": \"<\",\n              \"next\": \">\",\n              \"last\": \">>\"\n            },\n            \"lengthMenu\": \"每页显示 _MENU_条\",\n            \"zeroRecords\": \"没有找到符合条件的数据\",\n            \"info\": \"共_PAGES_页,_TOTAL_条\",\n            \"infoFiltered\": \"\",\n            \"infoEmpty\":\"共0页,0条\",\n          }\n        });\n      }\n    };\n  }();\n\n  $(function () {\n    TableManaged.init();\n  });\n</script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/notice/initNotice.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"initNoticeDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/notice/initNoticeDetail.html",
    "content": "<script type=\"text/javascript\">\n    function checkNoticeText(){\n        var notice = document.getElementById(\"notice\");\n        if(notice.value == \"\"){\n            alert(\"系统通知不能为空!\");\n            notice.focus();\n            return false;\n        }\n        $.post(\n            '${request.contextPath}/manage/notice/add.json',\n            {\n                notice: notice.value,\n            },\n            function(data){\n                if(data.success==1){\n                    alert(\"更新成功!\");\n                }else{\n                    alert(\"更新失败!\");\n                }\n                window.location.reload();\n            }\n        );\n\n    }\n</script>\n\n<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">\n                    系统通知管理\n                    <#if success?? && (success == 1)>\n                        <font color=\"red\">更新成功</font>\n                    <#elseif success?? && (success == 0)>\n                        <font color=\"red\">更新失败</font>\n                    </#if>\n                </h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"row\">\n                    <div class=\"col-md-12\">\n                        <form action=\"${request.contextPath}/manage/notice/add\" method=\"post\" class=\"w-100 align-items-center\">\n                            <div class=\"row mb-2\">\n                                <label class=\"col-form-label offset-md-1 col-md-2\">\n                                    系统通知:<font color='red'>(*)</font>:\n                                </label>\n                                <div class=\"col-md-7\">\n                                    <textarea rows=\"10\" name=\"notice\" id=\"notice\" placeholder=\"系统通知\" class=\"form-text w-100\">${notice!}</textarea>\n                                    <span class=\"help-block\">\n                                        例如:<br/>\n                                        1.CacheCloud相关文档可以在菜单栏查询(2014-12-16)<br/>\n                                        2.接入代码模块加入了Protostuff序列化的演示。(2014-12-19)\n                                        </span>\n                                </div>\n                            </div>\n                            <div class=\"row\">\n                                <button type=\"submit\" class=\"btn btn-success btn-sm offset-md-5 col-md-1\" onclick=\"checkNoticeText()\">\n                                    <i class=\"bi bi-check\"></i>提交</button>\n                            </div>\n                            <br>\n                            <br>\n                        </form>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/pod/changeList.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          容器:${ip} [宿主机:${realIp}]变更历史\n        </h3>\n      </div>\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <div class=\"col-md-12\">\n            <div class=\"portlet box light-grey\" id=\"clientIndex\">\n              <div class=\"caption\">\n                <i class=\"bi bi-globe\"></i>变更列表\n              </div>\n              <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\">\n                <thead>\n                <tr>\n                  <th>ip</th>\n                  <th>宿主机ip</th>\n                  <th>pod最后变更时间</th>\n                  <th>pod部署名称</th>\n                  <th>操作状态</th>\n                  <th>是否同步</th>\n                  <th>操作</th>\n                </tr>\n                </thead>\n                <tbody>\n                <#list relationList as relation>\n                  <tr>\n                    <td>\n                      <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/machineInstances?ip=${relation.ip}\">${relation.ip}</a>\n                    </td>\n                    <td>\n                      ${relation.realIp}\n                    </td>\n                    <td>${relation.updateTime?string(\"yyyy-MM-dd HH:mm:ss\")}</td>\n                    <td>\n                      ${relation.extraDesc}\n                    </td>\n                    <td>\n                      <#if (relation.status == 0)>\n                        pod下线\n                      <#elseif (relation.status == 1)>\n                        pod上线\n                      </#if>\n                    </td>\n                    <td>\n                      <#if (relation.isSync == 0) && (relation.realIp != realIp)>\n                        未同步\n                      <#elseif (relation.isSync == -1) && (relation.realIp != realIp)>\n                        同步中...\n                      <#elseif (relation.isSync == -2) && (relation.realIp != realIp)>\n                        <label style=\"color:red\">同步失败</label>\n                      <#elseif (relation.isSync == 1)>\n                        <label style=\"color:green\">已同步</label>\n                      </#if>\n                    </td>\n                    <td>\n                      <#if (relation.realIp != realIp) && (relation.isSync ==0) && (relation.status == 1)>\n                        <button id=\"syncDaTa${relation.id}\" onclick=\"syncData('${relation.realIp}','${realIp}','${ip}','${relation.id}')\" type=\"button\" class=\"btn btn-info\">同步数据</button>\n                      </#if>\n                      <#if (relation.taskid >0)>\n                        <button id=\"syncDaTa${relation.id}\" onclick=\"jumpTask('${relation.taskid}')\" type=\"button\" class=\"btn btn-success\">查看同步任务</button>\n                      </#if>\n                    </td>\n                  </tr>\n                </#list>\n                </tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n\n  function syncData(sourceIp,targetIp,containerIp,relationId){\n\n    if(sourceIp == targetIp){\n      toastr.error(\"警告:源主机:\"+sourceIp+\" 和目标主机:\"+targetIp+\" 是同一台机器，不能同步!\");\n      return;\n    }\n    // 确认是否同步\n    if(confirm(\"确认将源主机:\"+sourceIp+\" 数据目录同步到目标主机 :\"+targetIp+\" ？\")){\n      $.post('${request.contextPath}/manage/machine/pod/add/syncTask',\n              {\n                relationId: relationId,\n                containerIp: containerIp,\n                sourceIp: sourceIp,\n                targetIp: targetIp\n              },function(data) {\n                var status = data.status;\n                if (status == 1) {\n                  toastr.success(\"添加同步任务成功,请查看!\");\n                } else {\n                  toastr.error(\"添加同步任务失败！\" + data.message);\n                }\n                setTimeout(\"window.location.reload()\",100);\n              });\n    }else{\n      toastr.error(\"取消同步!\");\n      return;\n    }\n  }\n\n  //跳转任务详情页面\n  function jumpTask(taskid){\n    window.open(\"${request.contextPath}/manage/task/flow?taskId=\" + taskid,'_blank');\n  }\n\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/pod/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"changeList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n<script type=\"text/javascript\">\n  $(window).on('load', function () {\n    $('.selectpicker').selectpicker({'selectedText': 'cat'});\n    $('.selectpicker').selectpicker('refresh');\n    $('.selectpicker').selectpicker('render');\n  });\n</script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/quartz/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"quartzList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/quartz/quartzList.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          定时任务管理\n        </h3>\n      </div>\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <form class=\"row justify-content-end\" action=\"${request.contextPath}/manage/quartz/list\" method=\"post\" role=\"form\">\n            <label class=\"col-form-label col-auto\">\n              查询:\n            </label>\n            <div class=\"col-auto\">\n              <input type=\"text\" name=\"query\" class=\"form-control\" id=\"ipLike\" value=\"${query!}\" placeholder=\"\"/>\n            </div>\n            <button type=\"submit\" class=\"btn btn-primary btn-sm col-auto\">查询</button>\n          </form>\n        </div>\n\n        <div class=\"row mt-3 table-responsive\">\n          <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\" style=\"white-space: nowrap\">\n            <thead>\n            <tr>\n              <th scope=\"col\">triggerName</th>\n              <th scope=\"col\">triggerGroup</th>\n              <th scope=\"col\">cron</th>\n              <th scope=\"col\">nextFireDate</th>\n              <th scope=\"col\">prevFireDate</th>\n              <th scope=\"col\">startDate</th>\n              <th scope=\"col\">triggerState</th>\n              <th scope=\"col\">操作(s)</th>\n            </tr>\n            </thead>\n            <tbody>\n            <#list triggerList as t>\n              <tr>\n                <td>${t.triggerName!}</td>\n                <td>${t.triggerGroup!}</td>\n                <td>${t.cron!}</td>\n                <td>${t.nextFireDate!}</td>\n                <td>${t.prevFireDate!}</td>\n                <td>${t.startDate!}</td>\n                <td>${t.triggerState!}</td>\n                <td>\n                  <#if (t.triggerState == 'PAUSED')>\n                    <a onclick=\"if(window.confirm('确认恢复吗?!')){return true;}else{return false;}\"\n                       href=\"${request.contextPath}/manage/quartz/resume?name=${t.triggerName!}&group=${t.triggerGroup!}\">[恢复]\n                    </a>\n                  </#if>\n                  <#if (t.triggerState != 'PAUSED')>\n                    <a onclick=\"if(window.confirm('确认暂停吗?!')){return true;}else{return false;}\"\n                       href=\"${request.contextPath}/manage/quartz/pause?name=${t.triggerName!}&group=${t.triggerGroup!}\">[暂停]\n                    </a>\n                  </#if>\n                  <a onclick=\"if(window.confirm('确认删除吗?!')){return true;}else{return false;}\"\n                     href=\"${request.contextPath}/manage/quartz/remove?name=${t.triggerName!}&group=${t.triggerGroup!}\">[删除]\n                  </a>\n                </td>\n              </tr>\n            </#list>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/redisConfig/contrast.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n  <title>配置预览</title>\n</head>\n\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n\n==================================<font color=\"green\">1.${currentVersion.name!}&${upgradeVersion.name!} 配置差异项:</font>======================================\n<br/>\n版本<a style=\"color:green\" href=\"${request.contextPath}/manage/redisConfig/init?versionid=${upgradeVersion.id!}\" targer=\"_self\">${upgradeVersion.name!}</a> 差异项数:(<font style=\"color:red\">${upgradeConfigMap?size}</font>)<br/>\n<#list upgradeConfigMap?keys as key>\n  <font color=\"red\">${key!}:${upgradeConfigMap[key]!}</font><br/>\n</#list>\n<br/>\n版本<a style=\"color:green\" href=\"${request.contextPath}/manage/redisConfig/init?versionid=${currentVersion.id!}\" targer=\"_self\">${currentVersion.name!}</a> 差异项数:(<font style=\"color:red\">${currentConfigMap?size}</font>)<br/>\n<#list currentConfigMap?keys as key>\n  <font style=\"color:red\">${key!}:${currentConfigMap[key]!}</font><br/>\n</#list>\n<br/>\n==================================<font color=\"green\">2.${currentVersion.name!}&${upgradeVersion.name!} 配置相同项:</font>===================================================\n<br/>\n配置项数:(<font color=\"green\">${sameConfigMap?size}</font>)<br/>\n<#list sameConfigMap?keys as key>\n  <font color=\"white\">${key!}:${sameConfigMap[key]!}</font><br/>\n</#list>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/redisConfig/init.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"initConfigDetail.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/redisConfig/initConfigDetail.html",
    "content": "<script type=\"text/javascript\">\n  <!-- add redis config -->\n  function removeConfig(id, configKey) {\n    if (confirm(\"确认要删除key=\"+configKey+\"配置?\")) {\n      $.get(\n              '${request.contextPath}/manage/redisConfig/remove.json',\n              {\n                id: id,\n                versionName:$('#currentVersionName').val()\n              },\n              function(data){\n                var status = data.status;\n                if (status == 1) {\n                  alert(\"删除成功!\");\n                } else {\n                  alert(\"删除失败, msg: \" + result.message);\n                }\n                window.location.reload();\n              }\n      );\n    }\n  }\n\n  function changeConfig(id, configKey) {\n    var configValue = document.getElementById(\"configValue\" + id);\n    var info = document.getElementById(\"info\" + id);\n    var status = document.getElementById(\"status\" + id);\n    var valueType = document.getElementById(\"valueType\" + id);\n    $.get(\n            '${request.contextPath}/manage/redisConfig/update.json',\n            {\n              id: id,\n              configKey: configKey,\n              configValue: configValue.value,\n              info: info.value,\n              status: status.value,\n              versionName:$('#currentVersionName').val(),\n              valueType: valueType.value\n            },\n            function(data){\n              var status = data.status;\n              if (status == 1) {\n                alert(\"修改成功！\");\n                window.location.reload();\n              } else {\n                alert(\"修改失败！\" + data.message);\n              }\n\n            }\n    );\n  }\n\n  function saveRedisConfig() {\n    var configKey = document.getElementById(\"configKey\");\n    if (configKey.value == \"\"){\n      alert(\"请填写配置名\");\n      configKey.focus();\n      return false;\n    }\n    var configValue = document.getElementById(\"configValue\");\n    var info = document.getElementById(\"info\");\n    if (info.value == \"\") {\n      alert(\"请填写配置说明\");\n      info.focus();\n      return false;\n    }\n    var type = document.getElementById(\"type\");\n    var valueType = document.getElementById(\"valueType\");\n    $.get(\n            '${request.contextPath}/manage/redisConfig/add.json',\n            {\n              configKey: configKey.value,\n              configValue: configValue.value,\n              info: info.value,\n              type: type.value,\n              versionid: $('#version option:selected').attr(\"versionid\"),\n              versionName:$('#currentVersionName').val(),\n              valueType: valueType.value\n            },\n            function(data){\n              var status = data.status;\n              if (status == 1) {\n                alert(\"添加成功！\");\n              } else {\n                alert(\"添加失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n</script>\n\n<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-body\">\n        <nav class=\"nav\">\n          <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n            <#list resourceList as resource>\n              <li class=\"nav-item\" data-url=\"${request.contextPath}/manage/redisConfig/init?versionid=${resource.id!}\">\n                <a class=\"nav-link d-flex <#if currentVersion?? && currentVersion.id?? && (resource.id == currentVersion.id)>active</#if>\" href=\"?versionid=${resource.id!}\">${resource.name!}</a>\n              </li>\n            </#list>\n          </ul>\n        </nav>\n      </div>\n    </div>\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <div class=\"row\">\n          <div class=\"col-md-6\">\n            <h3 class=\"card-title\">\n              <#if currentVersion??>\n                ${currentVersion.name!}\n              </#if>\n              <#if type?? && (type==6)>普通配置\n              <#elseif type?? && (type==2)>Cluster\n              <#elseif type?? && (type==5)>Sentinel\n              </#if>\n              <a target=\"_blank\" href=\"${request.contextPath}/manage/redisConfig/preview?type=${type!}&versionId=${versionid!}\" class=\"btn btn-info\" role=\"button\">配置模板 预览</a>\n              <button id=\"sample_editable_1_new\" class=\"btn btn-info\" data-bs-target=\"#addRedisConfigModal\" data-bs-toggle=\"modal\">\n                添加配置项 <i class=\"bi bi-plus\"></i>\n              </button>\n            </h3>\n          </div>\n          <div class=\"offset-md-1 col-md-5 float-end\">\n            <form class=\"row align-items-center\" action=\"${request.contextPath}/manage/redisConfig/init\" method=\"post\">\n              <input name=\"versionid\" id=\"versionid\" value=\"${versionid!}\" type=\"hidden\"/>\n              <label class=\"col-form-label col-auto\">\n                Redis类型:\n              </label>\n              <div class=\"col-auto\">\n                <select name=\"type\" class=\"form-select\">\n                  <option value=\"2\" <#if type?? && (type == 2)>selected</#if>>\n                    Redis-Cluster\n                  </option>\n                  <option value=\"5\" <#if type?? && (type == 5)>selected</#if>>\n                    Redis-Sentinel\n                  </option>\n                  <option value=\"6\" <#if type?? && (type == 6)>selected</#if>>\n                    Redis-Standalone\n                  </option>\n                  </select>\n              </div>\n              <button id=\"search\" type=\"submit\" class=\"btn btn-success btn-sm col-auto\">查询</button>\n            </form>\n          </div>\n        </div>\n      </div>\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <div class=\"col-md-12\">\n            <div class=\"alert alert-warning\" role=\"alert\">\n              1. 此功能是Redis全局配置模板(每次开启应用时用到)，请谨慎修改.<br/>\n              2. 配置中的%d,%s代表Cachecloud会动态配置，最好不要修改.<br/>\n              3. 使用方法详见<a target=\"_blank\" href='http://cachecloud.github.io/2016/07/13/1.2.%20Redis%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95/'>Redis配置模板使用方法</a><br/>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          <i class=\"bi bi-globe\"></i>\n          填写配置:(配置项数:${redisConfigList?size})\n        </h3>\n      </div>\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <div class=\"col-md-12\">\n            <#list redisConfigList as config>\n              <div class=\"form\">\n                <form class=\"border-top border-bottom\" role=\"form\">\n                  <div class=\"form-body\">\n                    <div class=\"row my-2\">\n                      <label class=\"col-form-label col-md-2 text-end\">\n                        <#if (config.status == 0)>\n                          <font color='red'>（无效配置）</font>\n                        </#if>\n                        ${config.configKey!}:\n                      </label>\n                      <div class=\"col-md-2\">\n                        <input id=\"configValue${config.id!}\" type=\"text\" name=\"configValue\" class=\"form-control\" value=\"${config.configValue!}\" />\n                      </div>\n                      <div class=\"col-md-2\">\n                        <input id=\"info${config.id!}\" type=\"text\" name=\"info\" class=\"form-control\" value=\"${config.info!}\" />\n                      </div>\n                      <div class=\"col-md-2\">\n                        <select id=\"status${config.id!}\" name=\"status\" class=\"form-select\">\n                          <option value=\"1\" <#if (config.status == 1)>selected</#if>>\n                          有效\n                          </option>\n                          <option value=\"0\" <#if (config.status == 0)>selected</#if>>\n                          无效\n                          </option>\n                        </select>\n                      </div>\n                      <div class=\"col-md-2\">\n                        <select id=\"valueType${config.id!}\" name=\"valueType\" class=\"form-select\">\n                          <option value=\"1\" <#if (config.valueType == 1)>selected</#if>>\n                            取值：优先从已有节点拷贝\n                          </option>\n                          <option value=\"0\" <#if (config.valueType == 0)>selected</#if>>\n                            取值：默认值\n                          </option>\n                        </select>\n                      </div>\n                      <div class=\"col-md-2\">\n                        <button type=\"button\" class=\"btn btn-sm btn-info\" onclick=\"changeConfig('${config.id!}','${config.configKey!}')\">\n                          修改\n                        </button>\n                        <button type=\"button\" class=\"btn btn-sm btn-warning\" onclick=\"removeConfig('${config.id!}','${config.configKey!}')\">\n                          删除\n                        </button>\n                      </div>\n                    </div>\n                  </div>\n                  <#if currentVersion?? && (currentVersion.name??)>\n                    <#assign currentVersionNameValue = currentVersion.name>\n                  <#else>\n                    <#assign currentVersionNameValue = \"\">\n                  </#if>\n                  <input type=\"hidden\" id=\"currentVersionName\" value=\"${currentVersionNameValue!}\">\n                  <input type=\"hidden\" name=\"configKey\" value=\"${config.configKey!}\">\n                  <input type=\"hidden\" name=\"id\" value=\"${config.id!}\">\n                </form>\n                <!-- END FORM-->\n              </div>\n            </#list>\n            <!-- END TABLE PORTLET-->\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<!-- 添加redis配置 -->\n<div id=\"addRedisConfigModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">添加Redis配置</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    配置名:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"configKey\" id=\"configKey\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    配置值:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"configValue\" id=\"configValue\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    配置说明:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"info\" id=\"info\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    redis版本:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"type\" id=\"version\" class=\"form-select\">\n                      <#list resourceList as resource>\n                        <option <#if (currentVersion?? && currentVersion.id?? && (resource.id == currentVersion.id))>selected</#if> versionid=\"${resource.id!}\">${resource.name!}</option>\n                      </#list>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    类型:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"type\" id=\"type\" class=\"form-select\">\n                      <option value=\"6\" <#if type?? && (type == 6)>selected</#if> >\n                      Redis普通配置\n                      </option>\n                      <option value=\"2\" <#if type?? && (type == 2)>selected</#if> >\n                      Redis Cluster配置\n                      </option>\n                      <option value=\"5\" <#if type?? && (type == 5)>selected</#if> >\n                      Redis Sentinel配置\n                      </option>\n\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    取值类型:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"valueType\" id=\"valueType\" class=\"form-select\">\n                      <option value=\"0\" <#if valueType?? && (valueType == 0)>selected</#if> >\n                        默认值\n                      </option>\n                      <option value=\"1\" <#if valueType?? && (valueType == 1)>selected</#if> >\n                        优先从已有节点拷贝\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <div id=\"info\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" id=\"configBtn\" class=\"btn btn-danger\" onclick=\"saveRedisConfig()\">Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/redisConfig/preview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n  <title>配置预览</title>\n</head>\n\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n  <#if type?? && (type == 2)>\n      Redis Cluster配置，所用参数port=${port!}\n  <#elseif type?? && (type == 5)>\n      Redis Sentinel配置，所用参数masterName=${masterName!},host:port=${host!}:${port!}, sentinelPort=${sentinelPort!}\n  <#elseif type?? && (type == 6)>\n      Redis普通节点配置，所用参数host=${host!} port=${port!},maxmemory=${maxMemory!}\n  </#if>\n\n  <br/><br/>配置模板预览:<br/>\n\n  <#list configList as line>\n    <font color=\"white\">${line!}</font><br/>\n  </#list>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/addDir.html",
    "content": "<script type=\"text/javascript\">\n  function addResource(){\n    // 验证重复\n    $.post(\n            '${request.contextPath}/manage/app/resource/add.json',\n            {\n              resourceId: $('#resourceId').html(),\n              resourceName: $('#resourceName').val(),\n              resourceDesc: $('#resourceDesc').val(),\n              resourceType: 6,\n              resourceStatus: $('#resourceStatus').val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"新建成功\");\n              } else if(status == 2){\n                alert(\"修改成功\");\n              }else {\n                alert(\"新建失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n\n  $('#addResourceModal').on('shown.bs.modal', function (e) {\n    $(\"#resourceName\").removeAttr(\"readonly\", \"readonly\");\n    $(\"#resourceName\").val(\"\");\n    $('#resourceDesc').val(\"\");\n    $(\"#resourceDir option[value='/dir']\").prop(\"selected\", true);\n    $('#resourceUrl').val(\"\");\n    $('#resourceType').val(\"6\");\n    $('#resourceStatus').val(\"1\");\n    $('#modal-title').html(\"新建目录\");\n    $('#resourceId').html(\"\");\n    var resourceId = $(e.relatedTarget).data('resource_id');\n    console.log(resourceId);\n    if(typeof resourceId == 'undefined'){\n      return ;\n    }\n\n    $('#modal-title').html(\"修改目录\");\n    $('#resourceId').html(resourceId);\n    $(\"#resourceName\").attr(\"readonly\", \"readonly\");\n\n    $.post(\n            '${request.contextPath}/manage/app/resource/get.json',\n            {\n              resourceId: resourceId\n            },\n            function (data) {\n              if(data.status == 1){\n                $('#resourceName').val(data.resource.name);\n                $('#resourceDesc').val(data.resource.intro);\n                $(\"#resourceDir option[value='\"+data.resource.dir+\"']\").prop(\"selected\", true);\n                $('#resourceUrl').val(data.resource.url);\n                $('#resourceType').val(data.resource.type);\n                $('#resourceStatus').val(data.resource.status);\n              }\n            }\n    );\n  });\n</script>\n\n<div id=\"addResourceModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 id=\"modal-title\">新建资源</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <label id=\"resourceId\" style=\"display:none\"></label>\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    目录名称:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"resourceName\" id=\"resourceName\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    描述:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <textarea rows=\"5\"  name=\"resourceDesc\" id=\"resourceDesc\" placeholder=\"资源说明\" class=\"form-control\"></textarea>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    状态:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"resourceStatus\" id=\"resourceStatus\" class=\"form-select select2_category\">\n                      <option value=\"1\">\n                        有效\n                      </option>\n                      <option value=\"0\">\n                        无效\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n              </div>\n            </div>\n            <div id=\"info\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" id=\"versionBtn\" class=\"btn btn-danger\" onclick=\"addResource()\">Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/addRedis.html",
    "content": "<script type=\"text/javascript\">\n  function addResource(){\n    $.post(\n            '${request.contextPath}/manage/app/resource/add.json',\n            {\n              resourceId: $('#resourceId').html(),\n              resourceName: $('#resourceName').val(),\n              resourceDesc: $('#resourceDesc').val(),\n              resourceDir: $('#resourceDir').val(),\n              resourceType: $('#resourceType').val(),\n              resourceUrl: $('#resourceUrl').val(),\n              copyVersion: $('#copyVersion option:selected').attr(\"versionid\"),\n              resourceStatus: $('#resourceStatus').val(),\n              orderNum: $('#orderNum').val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"新建成功\");\n                window.location.reload();\n              } else if(status == 2){\n                alert(\"修改成功\");\n                window.location.reload();\n              }else {\n                $(\"#tips\").html(data.message);\n              }\n            }\n    );\n  }\n\n  $('#addResourceModal').on('shown.bs.modal', function (e) {\n\n    $(\"#resourceName\").removeAttr(\"readonly\", \"readonly\");\n    $('#config').attr(\"hidden\",\"hidden\");\n    $(\"#resourceName\").val(\"\");\n    $('#resourceDesc').val(\"\");\n    $(\"#resourceDir option[value='/redis']\").prop(\"selected\", true);\n    $('#resourceUrl').val(\"\");\n    $('#resourceType').val(\"3\");\n    $('#resourceStatus').val(\"1\");\n    $('#orderNum').val(0);\n    $('#modal-title').html(\"新建资源\");\n    $('#resourceId').html(\"\");\n\n    var resourceId = $(e.relatedTarget).data('resource_id');\n    if(typeof resourceId == 'undefined'){\n      $('#config').removeAttr(\"hidden\");\n      return ;\n    }\n    $('#modal-title').html(\"修改资源\");\n    $('#resourceId').html(resourceId);\n    $(\"#resourceName\").attr(\"readonly\", \"readonly\");\n\n    $.post(\n            '${request.contextPath}/manage/app/resource/get.json',\n            {\n              resourceId: resourceId\n            },\n            function (data) {\n              if(data.status == 1){\n                $('#resourceName').val(data.resource.name);\n                $('#resourceDesc').val(data.resource.intro);\n                $(\"#resourceDir option[value='\"+data.resource.dir+\"']\").prop(\"selected\", true);\n                $('#resourceUrl').val(data.resource.url);\n                $('#resourceType').val(data.resource.type);\n                $('#resourceStatus').val(data.resource.status);\n                $('#orderNum').val(data.resource.orderNum);\n              }\n            }\n    );\n  });\n\n  function updateUrl(){\n\n    // check redis格式\n    if ($('#resourceName').val().indexOf(\"redis-\") == -1){\n      alert(\"redis版本格式不正确!\");\n      resourceName.focus();\n      return false;\n    }\n    // 更新资源地址\n    $('#resourceUrl').val(\"http://download.redis.io/releases/\"+$('#resourceName').val()+\".tar.gz\");\n  }\n</script>\n\n<div id=\"addResourceModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 id=\"modal-title\">新建资源</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <label id=\"resourceId\" style=\"display:none\"></label>\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    资源名称:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <input type=\"text\" name=\"resourceName\" id=\"resourceName\"\n                           class=\"form-control\" placeholder=\"格式：redis-x.x.x\" onchange=\"updateUrl()\" />\n                  </div>\n                  <div><span id=\"tips\" style=\"color:red\"></span></div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    描述:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <textarea rows=\"5\"  name=\"resourceDesc\" id=\"resourceDesc\" placeholder=\"资源说明\" class=\"form-control\"></textarea>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    状态:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <select name=\"resourceStatus\" id=\"resourceStatus\" class=\"form-select select2_category\">\n                      <option value=\"1\">\n                        有效\n                      </option>\n                      <option value=\"0\">\n                        无效\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    目录:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <select id=\"resourceDir\" name=\"resourceDir\" class=\"form-select\">\n                      <#list dirList as dir>\n                        <option value=\"${dir.name!}\">${dir.name!}(${dir.intro!})</option>\n                      </#list>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    类型:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <select name=\"resourceType\" id=\"resourceType\" class=\"form-select select2_category\">\n                      <option value=\"2\">\n                        脚本\n                      </option>\n                      <option value=\"3\">\n                        Redis资源包\n                      </option>\n                      <option value=\"6\">\n                        目录\n                      </option>\n                      <option value=\"7\">\n                        迁移工具\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    源地址:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <input type=\"text\" name=\"resourceUrl\" id=\"resourceUrl\" value=\"\"\n                           class=\"form-control\" readonly=\"readonly\"/>\n                  </div>\n                </div>\n\n                <!-- 备份Redis配置 -->\n                <div class=\"form-group row\" id=\"config\" hidden>\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    备份配置:\n                  </label>\n                  <div class=\"col-md-4\">\n                    <select name=\"type\" id=\"copyVersion\" class=\"form-select select2_category\">\n                      <option versionid=\"-1\">不备份</option>\n                      <#list resourceList as resource>\n                        <#if (resource.status==1)>\n                          <option <#if (resource.id == 1)>selected</#if> versionid=\"${resource.id!}\">${resource.name!}</option>\n                        </#if>\n                      </#list>\n                    </select>\n                  </div>\n                  <div class=\"col-md-4\">(生成备份版本配置)</div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    排序:\n                  </label>\n                  <div class=\"col-md-8\">\n                    <input type=\"text\" name=\"orderNum\" id=\"orderNum\" placeholder=\"序号越大越靠前\"\n                           class=\"form-control\"/>\n                  </div>\n                </div>\n\n              </div>\n            </div>\n            <div id=\"info\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" id=\"versionBtn\" class=\"btn btn-danger\" onclick=\"addResource()\">Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/addScript.html",
    "content": "<script type=\"text/javascript\">\n  function addResource(){\n    $.post(\n            '${request.contextPath}/manage/app/resource/add.json',\n            {\n              resourceId: $('#resourceId').html(),\n              resourceName: $('#resourceName').val(),\n              resourceDesc: $('#resourceDesc').val(),\n              resourceDir: $('#resourceDir').val(),\n              resourceType: $('#resourceType').val(),\n              resourceUrl: $('#resourceUrl').val(),\n              resourceStatus: $('#resourceStatus').val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"新建成功\");\n              } else if(status == 2){\n                alert(\"修改成功\");\n              }else {\n                alert(\"新建失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n\n  $('#addResourceModal').on('shown.bs.modal', function (e) {\n\n    $(\"#resourceName\").removeAttr(\"readonly\", \"readonly\");\n    $(\"#resourceName\").val(\"\");\n    $('#resourceDesc').val(\"\");\n    $(\"#resourceDir option[value='/script']\").prop(\"selected\", true);\n    $('#resourceUrl').val(\"\");\n    $('#resourceType').val(\"2\");\n    $('#resourceStatus').val(\"1\");\n    $('#modal-title').html(\"新建脚本\");\n    $('#resourceId').html(\"\");\n\n    var resourceId = $(e.relatedTarget).data('resource_id');\n    if(typeof resourceId == 'undefined'){\n      return ;\n    }\n    $('#modal-title').html(\"修改脚本\");\n    $('#resourceId').html(resourceId);\n    $(\"#resourceName\").attr(\"readonly\", \"readonly\");\n\n    $.post(\n            '${request.contextPath}/manage/app/resource/get.json',\n            {\n              resourceId: resourceId\n            },\n            function (data) {\n              if(data.status == 1){\n                $('#resourceName').val(data.resource.name);\n                $('#resourceDesc').val(data.resource.intro);\n                $('#resourceDir').attr(\"dir\",data.resource.dir);\n                $(\"#resourceDir option[value='\"+data.resource.dir+\"']\").prop(\"selected\", true);\n                $('#resourceUrl').val(data.resource.url);\n                $('#resourceType').val(data.resource.type);\n                $('#resourceStatus').val(data.resource.status);\n              }\n            }\n    );\n  });\n</script>\n\n<div id=\"addResourceModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 id=\"modal-title\">新建脚本</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <label id=\"resourceId\" style=\"display:none\"></label>\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    脚本名称:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"resourceName\" id=\"resourceName\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    描述:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <textarea rows=\"5\"  name=\"resourceDesc\" id=\"resourceDesc\" placeholder=\"资源说明\" class=\"form-control\"></textarea>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    状态:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"resourceStatus\" id=\"resourceStatus\" class=\"form-select select2_category\">\n                      <option value=\"1\">\n                        有效\n                      </option>\n                      <option value=\"0\">\n                        无效\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    目录:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select id=\"resourceDir\" name=\"resourceDir\" class=\"form-select\">\n                      <#list dirList as dir>\n                        <option value=\"${dir.name!}\">${dir.name!}(${dir.intro!})</option>\n                      </#list>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    类型:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"resourceType\" id=\"resourceType\" class=\"form-select select2_category\">\n                      <option value=\"2\">\n                        脚本\n                      </option>\n                      <option value=\"3\">\n                        Redis资源包\n                      </option>\n                      <option value=\"6\">\n                        目录\n                      </option>\n                      <option value=\"7\">\n                        迁移工具\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n              </div>\n            </div>\n            <div id=\"info\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" id=\"versionBtn\" class=\"btn btn-danger\" onclick=\"addResource()\">Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/addTool.html",
    "content": "<script type=\"text/javascript\">\n  function addResource(){\n    $.post(\n            '${request.contextPath}/manage/app/resource/add.json',\n            {\n              resourceId: $('#resourceId').html(),\n              resourceName: $('#resourceName').val(),\n              resourceDesc: $('#resourceDesc').val(),\n              resourceDir: $('#resourceDir').val(),\n              resourceType: $('#resourceType').val(),\n              resourceUrl: $('#resourceUrl').val(),\n              resourceStatus: $('#resourceStatus').val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"新建成功\");\n              } else if(status == 2){\n                alert(\"修改成功\");\n              }else {\n                alert(\"新建失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n\n  $('#addResourceModal').on('shown.bs.modal', function (e) {\n\n    $(\"#resourceName\").removeAttr(\"readonly\", \"readonly\");\n    $(\"#resourceName\").val(\"\");\n    $('#resourceDesc').val(\"\");\n    $(\"#resourceDir option[value='/tool']\").prop(\"selected\", true);\n    $('#resourceUrl').val(\"\");\n    $('#resourceType').val(\"7\");\n    $('#resourceStatus').val(\"1\");\n    $('#modal-title').html(\"新建资源\");\n    $('#resourceId').html(\"\");\n\n    var resourceId = $(e.relatedTarget).data('resource_id');\n    if(typeof resourceId == 'undefined'){\n      return ;\n    }\n    $('#modal-title').html(\"修改资源\");\n    $('#resourceId').html(resourceId);\n    $(\"#resourceName\").attr(\"readonly\", \"readonly\");\n\n    $.post(\n            '${request.contextPath}/manage/app/resource/get.json',\n            {\n              resourceId: resourceId\n            },\n            function (data) {\n              if(data.status == 1){\n                $('#resourceName').val(data.resource.name);\n                $('#resourceDesc').val(data.resource.intro);\n                $(\"#resourceDir option[value='\"+data.resource.dir+\"']\").prop(\"selected\", true);\n                $('#resourceUrl').val(data.resource.url);\n                $('#resourceType').val(data.resource.type);\n                $('#resourceStatus').val(data.resource.status);\n              }\n            }\n    );\n  });\n\n  function updateUrl(){\n    // check redis格式\n    if ($('#resourceName').val().indexOf(\"redis-\") == -1){\n      alert(\"redis版本格式不正确!\");\n      resourceName.focus();\n      return false;\n    }\n    // 更新资源地址\n    $('#resourceUrl').val(\"http://download.redis.io/releases/\"+$('#resourceName').val()+\".tar.gz\");\n  }\n</script>\n\n<div id=\"addResourceModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 id=\"modal-title\">新建资源</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <label id=\"resourceId\" style=\"display:none\"></label>\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    资源名称:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"resourceName\" id=\"resourceName\"\n                           class=\"form-control\" placeholder=\"格式：redis-shake-x.x.x\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    描述:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <textarea rows=\"5\"  name=\"resourceDesc\" id=\"resourceDesc\" placeholder=\"资源说明\" class=\"form-control\"></textarea>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    状态:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"resourceStatus\" id=\"resourceStatus\" class=\"form-select select2_category\">\n                      <option value=\"1\">\n                        有效\n                      </option>\n                      <option value=\"0\">\n                        无效\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    目录:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select id=\"resourceDir\" name=\"resourceDir\" class=\"form-select\">\n                      <#list dirList as dir>\n                        <option value=\"${dir.name!}\">${dir.name!}(${dir.intro!})</option>\n                      </#list>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    类型:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <select name=\"resourceType\" id=\"resourceType\" class=\"form-select select2_category\">\n                      <option value=\"2\">\n                        脚本\n                      </option>\n                      <option value=\"3\">\n                        Redis资源包\n                      </option>\n                      <option value=\"6\">\n                        目录\n                      </option>\n                      <option value=\"7\">\n                        迁移工具\n                      </option>\n                    </select>\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    源地址:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"resourceUrl\" id=\"resourceUrl\" value=\"\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n              </div>\n            </div>\n            <div id=\"info\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n          <button type=\"button\" id=\"versionBtn\" class=\"btn btn-danger\" onclick=\"addResource()\">Ok</button>\n        </div>\n\n      </form>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/dir.html",
    "content": "<script type=\"text/javascript\">\n  function pushResource(resourceId){\n    var resource_id = (typeof resourceId == 'undefined') ? $('#resource_id').html() : resourceId;\n    $.post(\n            '${request.contextPath}/manage/app/resource/push.json',\n            {\n              repositoryId: $('#repositoryId').html(),\n              resourceId: resource_id\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"推送成功\");\n              } else {\n                alert(\"推送失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div id=\"respo-div\" class=\"col-md-12\">\n        <button id=\"redis_version\" class=\"btn btn-success btn-sm col-auto\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\" >\n          <i class=\"bi bi-plus\"></i>新建目录\n        </button>\n\n        <form class=\"row align-items-center float-end\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/resource/index?tab=dir\"\n              id=\"appList\" name=\"ec\">\n          <label id=\"repositoryId\" class=\"col-form-label col-auto\" style=\"display:none\">${repository.id!}</label>\n          <div class=\"col-auto\">\n            <input type=\"text\" class=\"form-control\" id=\"searchName\" name=\"searchName\"\n                   value=\"${searchName!}\" placeholder=\"脚本名称\">\n          </div>\n          <div class=\"col-auto\">\n            <button type=\"submit\" class=\"btn btn-success btn-sm\">查询</button>\n          </div>\n        </form>\n      </div>\n    </div>\n\n    <div class=\"row mt-3\">\n      <div class=\"table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n          <thead>\n          <tr>\n            <th scope=\"col\">序号</th>\n            <th scope=\"col\">目录名称</th>\n            <th scope=\"col\">说明</th>\n            <th scope=\"col\">最后更新时间</th>\n            <th scope=\"col\">操作人</th>\n            <th scope=\"col\">状态</th>\n            <th scope=\"col\">操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list resourceList as resource>\n            <tr>\n              <td>${resource.id!}</td>\n              <td>\n                <#if (resource.ispush == 0)>${resource.name!}</#if>\n                <#if (resource.ispush == 1)><a target=\"_blank\" href=\"${repository.url!}${resource.name!}\">${resource.name!}</a></#if>\n              </td>\n              <td>\n                ${resource.intro!}\n              </td>\n              <td>\n                ${resource.lastmodify?string(\"yyyy-MM-dd HH:mm:ss\")}\n              </td>\n              <td>\n                ${resource.username!}\n              </td>\n              <td>\n                <#if (resource.ispush == 0)>未推送</#if>\n                <#if (resource.ispush == 1)><span style=\"color:green\">已推送</span></#if>\n              </td>\n              <td>\n                <button type=\"button\" class=\"btn btn-info\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\"\n                        data-resource_id=\"${resource.id!}\" >\n                  修改\n                </button>\n                <#if (resource.ispush == 0)>\n                  <button id=\"resource\" class=\"btn btn-success\" onclick=\"pushResource('${resource.id!}')\" data-bs-toggle=\"modal\">推送</button>\n                </#if>\n              </td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n<#include \"addDir.html\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/index.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"tabbable-custom\">\n            <div class=\"card\">\n                <div class=\"card-body\">\n                    <nav class=\"nav\">\n                        <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"tabs\">\n                            <li id=\"respo\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/resource/redis/respo\">\n                                <a class=\"nav-link d-flex\" href=\"?tab=respo&\"><i class=\"bi bi-house-door\"></i>仓库配置</a>\n                            </li>\n                            <li id=\"dir\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/resource/redis/dir?searchName=${searchName!}\">\n                                <a class=\"nav-link d-flex\" href=\"?tab=dir\"><i class=\"bi bi-list-ul\"></i>目录管理</a>\n                            </li>\n                            <li id=\"script\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/resource/redis/script?searchName=${searchName!}\">\n                                <a class=\"nav-link d-flex\" href=\"?tab=script\"><i class=\"bi bi-file-earmark\"></i>脚本管理</a>\n                            </li>\n                            <li id=\"redis\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/resource/redis/redis?searchName=${searchName!}\">\n                                <a class=\"nav-link d-flex\" href=\"?tab=redis\"><i class=\"bi bi-folder2-open\"></i>Redis资源管理</a>\n                            </li>\n                            <li id=\"tool\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/app/resource/redis/tool?searchName=${searchName!}\">\n                                <a class=\"nav-link d-flex\" href=\"?tab=tool\"><i class=\"bi bi-folder2-open\"></i>迁移工具管理</a>\n                            </li>\n                        </ul>\n                    </nav>\n                </div>\n            </div>\n            <div class=\"tab-content\" id=\"tabContent\">\n                <div class=\"tab-pane active\" id=\"respoTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"dirTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"scriptTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"redisTab\">\n                </div>\n                <div class=\"tab-pane\" id=\"toolTab\">\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n<script type=\"text/javascript\">\n    function showTab(tab) {\n        $.get($(\"#\" + tab).attr(\"data-url\"), function (result) {\n            $(\"#\" + tab + \"Tab\").html(result);\n        });\n    }\n\n    function refreshActiveTab() {\n        var tab = getQueryString(\"tab\");\n        if (tab) {\n            $(\"#\" + tab + \" a\").addClass(\"active\");\n            $(\"#\" + tab + \"Tab\").addClass(\"active\").siblings().removeClass(\"active\");\n        } else {\n            tab = \"respo\";\n            $(\"#\" + tab + \" a\").addClass(\"active\");\n        }\n        showTab(tab);\n        $(\"#tabs li a\").tooltip({placement: \"bottom\"});\n    }\n\n    $(function () {\n        refreshActiveTab();\n    });\n\n    function getQueryString(name) {\n        var reg = new RegExp(\"(^|&)\" + name + \"=([^&]*)(&|$)\");\n        var r = window.location.search.substr(1).match(reg);\n        if (r != null) return unescape(r[2]);\n        return null;\n    }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"index.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/redis.html",
    "content": "<script type=\"text/javascript\">\n  // 编译&推送\n  function compile(resourceId,repositoryId){\n    $.post(\n            '${request.contextPath}/manage/app/resource/compile.json',\n            {\n              repositoryId: repositoryId,\n              resourceId: resourceId\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"编译成功\");\n              } else {\n                alert(\"编译失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div id=\"respo-div\" class=\"col-md-12\">\n        <button id=\"redis_version\" class=\"btn btn-success btn-sm col-auto\" type=\"button\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\">\n          <i class=\"bi bi-plus\"></i>新建资源包\n        </button>\n        <form class=\"row align-items-center float-end\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/resource/index?tab=redis\" id=\"appList\" name=\"ec\">\n            <div class=\"col-auto\">\n              <input type=\"text\" class=\"form-control\" id=\"searchName\" name=\"searchName\"\n                     value=\"${searchName!}\" placeholder=\"资源名称\">\n            </div>\n            <div class=\"col-auto\">\n              <button type=\"submit\" class=\"btn btn-success btn-sm\">查询</button>\n            </div>\n        </form>\n      </div>\n    </div>\n\n    <div class=\"row mt-3\">\n      <div class=\"table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\" style=\"white-space: nowrap\">\n          <thead>\n          <tr>\n            <th scope=\"col\">资源id</th>\n            <th scope=\"col\">资源名</th>\n            <th scope=\"col\">说明</th>\n            <th scope=\"col\">目录</th>\n            <th scope=\"col\">应用使用数量</th>\n            <th scope=\"col\">源地址</th>\n            <th scope=\"col\">最后修改时间</th>\n            <th scope=\"col\">操作人</th>\n            <th scope=\"col\">状态</th>\n            <th scope=\"col\">排序</th>\n            <th scope=\"col\">操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list resourceList as resource>\n            <#if (resource.type != 1) && (resource.type != 6)>\n              <tr>\n                <td>${resource.id!}</td>\n                <td>\n                  <#if (resource.ispush==1) || (resource.ispush==3)><a target=\"_blank\" href=\"${repository.url!}${resource.dir!}\">${resource.name!}</a></#if>\n                  <#if (resource.ispush==0) || (resource.ispush==2) || (resource.ispush==4)>${resource.name!}</#if>\n                </td>\n                <td>\n                  ${resource.intro!}\n                </td>\n                <td>\n                  ${resource.dir!}\n                </td>\n                <td>\n                  <a target=\"_target\" href=\"${request.contextPath}/admin/app/list?versionId=${resource.id!}\">${appUseMap?api.get(resource.id)!}</a>\n                </td>\n                <td>\n                  <#if resource.url??>\n                    <a target=\"_blank\" href=\"${resource.url!}\" title=\"${resource.url!}\">[下载]</a>\n                  </#if>\n                </td>\n                <td>\n                  ${resource.lastmodify?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </td>\n                <td>\n                  ${resource.username!}\n                </td>\n                <td>\n                  <#if (resource.ispush == 0)>未推送</#if>\n                  <#if (resource.ispush == 1)><span style=\"color:green\"><a href=\"${request.contextPath}/manage/task/flow?taskId=${resource.taskId!}\" target=\"_blank\">已推送</a></span></#if>\n                  <#if (resource.ispush == 4)><span style=\"color:orange\">编译中<a href=\"${request.contextPath}/manage/task/flow?taskId=${resource.taskId!}\" target=\"_blank\">[查看任务]</a></span></#if>\n                </td>\n                <td>\n                  ${resource.orderNum!}\n                </td>\n                <td>\n                  <button type=\"button\" class=\"btn btn-info btn-sm\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\"\n                          data-resource_id=\"${resource.id!}\" >\n                    修改\n                  </button>\n                  <#if (resource.ispush == 0)>\n                    <button type=\"button\" class=\"btn btn-success btn-sm\" onclick=\"compile('${resource.id!}','${repository.id!}')\" data-bs-toggle=\"modal\">编译&推送</button>\n                  </#if>\n                  <a target=\"_target\" type=\"button\" class=\"btn btn-sm btn-info\" href=\"${request.contextPath}/manage/redisConfig/init?versionId=${resource.id!}\">修改配置</a>\n                </td>\n              </tr>\n            </#if>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n<#include \"addRedis.html\">\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/respo.html",
    "content": "<script type=\"text/javascript\">\n  function setConfig(){\n    $.post(\n            '${request.contextPath}/manage/app/resource/add.json',\n            {\n              resourceId: $('#resourceId').html(),\n              resourceName: $('#resourceName').val(),\n              resourceDir: $('#resourceDir').val(),\n              resourceUrl: $('#resourceUrl').val(),\n              resourceType:   1,\n              resourceStatus: 1\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1 || status == 2) {\n                alert(\"设置成功\");\n              } else {\n                alert(\"设置异常！\");\n              }\n              window.location.reload();\n            }\n    );\n  }\n\n  function check(){\n    $.post(\n            '${request.contextPath}/manage/app/resource/add.json',\n            {\n              resourceId: $('#resourceId').html(),\n              resourceName: $('#resourceName').val(),\n              resourceDir: $('#resourceDir').val(),\n              resourceUrl: $('#resourceUrl').val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"设置成功\");\n              } else {\n                alert(\"设置异常！\");\n              }\n              window.location.reload();\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div id=\"respo-div\" class=\"col-md-12\">\n        <form class=\"row align-items-center\" role=\"form\" name=\"ec\">\n          <div class=\"form-group row\">\n            <label id=\"resourceId\" class=\"col-form-label col-auto\" style=\"display:none\">${repository.id!}</label>\n            <div class=\"form-group col-auto\">\n              <label id=\"respo\">远程仓库地址：</label>\n            </div>\n            <label class=\"col-auto\">ip:</label>\n            <div class=\"col-md-3\">\n              <input id=\"resourceName\" type=\"text\"\n                        value=\"${repository.name!}\" class=\"form-control\" name=\"pattern\" placeholder=\"仓库ip\">\n            </div>\n            <label class=\"col-auto\">根目录：</label>\n            <div class=\"col-md-4\">\n              <input id=\"resourceDir\" type=\"text\"\n                            value=\"${repository.dir!}\" class=\"form-control\" name=\"pattern\" placeholder=\"路径\">\n            </div>\n            <div class=\"form-group col-auto\">\n              <button type=\"button\" class=\"form-control btn btn-success\" onclick=\"setConfig()\">设置</button>\n            </div>\n          </div>\n          <div class=\"form-group row\">\n            <label class=\"col-auto\">资源下载地址：</label>\n            <div class=\"col-md-9\">\n              <input id=\"resourceUrl\" style=\"width: 70%\" type=\"text\"\n                     value=\"${repository.url!}\" class=\"form-control\" name=\"pattern\" placeholder=\"域名地址\">\n            </div>\n        </form>\n      </div>\n    </div>\n\n    <div class=\"row mt-3\">\n      <div class=\"table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n          <thead>\n          <tr>\n            <th scope=\"col\">目录</th>\n            <th scope=\"col\">资源名</th>\n            <th scope=\"col\">资源说明</th>\n            <th scope=\"col\">状态</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list resourceList as resource>\n            <#if (resource.type != 1) && (resource.type != 6)>\n              <tr>\n                <td>\n                  <a target=\"_blank\" href=\"${repository.url!}${resource.dir!}\">${resource.dir!}</a>\n                </td>\n                <td>\n                  <#if (resource.ispush==1) || (resource.ispush==3)><a target=\"_blank\" href=\"${repository.url!}${resource.dir!}/${resource.name!}\">${resource.name!}</a></#if>\n                  <#if (resource.ispush==0) || (resource.ispush==2) || (resource.ispush==4)>${resource.name!}</#if>\n                </td>\n                <td>\n                  ${resource.intro!}\n                </td>\n                <td>\n                  <#if (resource.ispush==1)><span style=\"color:green\">已推送</span></#if>\n                  <#if (resource.ispush==3)><span style=\"color:green\">已推送</span><span style=\"color:red\">(有新修改)</span></#if>\n                  <#if (resource.ispush==2)>未推送<span style=\"color:red\">(有新修改)</span></#if>\n                  <#if (resource.ispush==0)>未推送</#if>\n                  <#if (resource.ispush==4)><span style=\"color:orange\">编译中...</span></#if>\n                </td>\n              </tr>\n            </#if>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/script.html",
    "content": "<script type=\"text/javascript\">\n  $('#modal-script').on('shown.bs.modal', function (e) {\n    $('#editor2').html('');\n    var resourceName = $(e.relatedTarget).data('resource_name');\n    var resourceId = $(e.relatedTarget).data('resource_id');\n    var respoitoryId = $('#repositoryId').html();\n    $('#resource_id').html(resourceId);\n    $('#resource_name').html(resourceName);\n\n    $.post(\n            '${request.contextPath}/manage/app/resource/script/load',\n            {\n              resourceId: resourceId,\n              respositoryId:respoitoryId\n            },\n            function (data) {\n              if(data.status == 1){\n                $('#editor2').html(data.content);\n                if(data.source == 1){\n                  $('#file_source').html(\"<span style=color:red>(临时保存)</span>\");\n                }else if(data.source == 2){\n                  $('#file_source').html(\"<span style=color:green>(最新文件)</span>\");\n                }\n              }\n            }\n    );\n  });\n\n  function pushResource(resourceId){\n    var resource_id = (typeof resourceId == 'undefined') ? $('#resource_id').html() : resourceId;\n    $.post(\n            '${request.contextPath}/manage/app/resource/push.json',\n            {\n              repositoryId: $('#repositoryId').html(),\n              resourceId: resource_id,\n              content: $('#editor2').val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"推送成功\");\n              } else {\n                alert(\"推送失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n\n  function temporarySave(){\n    $.post(\n            '${request.contextPath}/manage/app/resource/temporarySave.json',\n            {\n              resourceId: $('#resource_id').html(),\n              content: $('#editor2').val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"保存成功\");\n              } else {\n                alert(\"保存失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div id=\"respo-div\" class=\"col-md-12\">\n        <button id=\"redis_version\" class=\"btn btn-success btn-sm col-auto\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\" >\n          <i class=\"bi bi-plus\"></i>新建脚本\n        </button>\n        <form class=\"row align-items-center float-end\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/resource/index?tab=script\"\n              id=\"appList\" name=\"ec\">\n          <label id=\"repositoryId\" class=\"col-form-label col-auto\" style=\"display:none\">${repository.id!}</label>\n          <div class=\"col-auto\">\n            <input type=\"text\" class=\"form-control\" id=\"searchName\" name=\"searchName\"\n                   value=\"${searchName!}\" placeholder=\"脚本名称\">\n          </div>\n          <button type=\"submit\" class=\"btn btn-success btn-sm col-auto\">查询</button>\n        </form>\n      </div>\n    </div>\n\n    <div class=\"row mt-3\">\n      <div class=\"table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\">\n          <thead>\n          <tr>\n            <th scope=\"col\">序号</th>\n            <th scope=\"col\">脚本名称</th>\n            <th scope=\"col\">脚本说明</th>\n            <th scope=\"col\">目录</th>\n            <th scope=\"col\">最后更新时间</th>\n            <th scope=\"col\">操作人</th>\n            <th scope=\"col\">状态</th>\n            <th scope=\"col\">操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list resourceList as resource>\n            <tr>\n              <td>${resource.id!}</td>\n              <td>\n                <a target=\"_blank\" href=\"${repository.url!}${resource.dir!}/${resource.name!}\">${resource.name!}</a>\n              </td>\n              <td>\n                ${resource.intro!}\n              </td>\n              <td>\n                ${resource.dir!}\n              </td>\n              <td>\n                ${resource.lastmodify?string(\"yyyy-MM-dd HH:mm:ss\")}\n              </td>\n              <td>\n                ${resource.username!}\n              </td>\n              <td>\n                <#if (resource.ispush==1)><span style=\"color:green\">已推送</span></#if>\n                <#if (resource.ispush==3)><span style=\"color:green\">已推送</span><span style=\"color:red\">(有新修改)</span></#if>\n                <#if (resource.ispush==2)>未推送<span style=\"color:red\">(有新修改)</span></#if>\n                <#if (resource.ispush==0)>未推送</#if>\n\n\n                <#if (resource.ispush == 0)>未推送</#if>\n                <#if (resource.ispush == 1)><span style=\"color:green\">已推送</span></#if>\n              </td>\n              <td>\n                <button type=\"button\" class=\"btn btn-info\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\"\n                        data-resource_id=\"${resource.id!}\" >\n                  修改\n                </button>\n                <button type=\"button\" class=\"btn btn-info\" data-bs-target=\"#modal-script\" data-bs-toggle=\"modal\"\n                        data-resource_id=\"${resource.id!}\" data-resource_name=\"${resource.name!}\" >\n                  编辑内容\n                </button>\n                <#if (resource.ispush == 0)>\n                  <button id=\"resource\" class=\"btn btn-success\" onclick=\"pushResource('${resource.id!}')\" data-bs-toggle=\"modal\">推送</button>\n                </#if>\n              </td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div id=\"modal-script\" class=\"modal fade\" tabindex=\"-1\">\n  <div class=\"modal-dialog modal-xl\" style=\"width:800px;\">\n    <div class=\"modal-content\">\n\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">\n          <label id=\"resource_name\"></label>脚本内容:\n          <label id=\"file_source\"></label>\n          <small><label id=\"modal-title\" style=\"color: #00BE67\"></label></small>\n        </h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\" style=\"width:800px;height:400px; overflow:scroll;\">\n          <label id=\"resource_id\" style=\"display:none\"></label>\n          <!-- 控件开始 -->\n          <textarea rows=\"20\"  name=\"editor2\" id=\"editor2\" placeholder=\"\" class=\"form-control\"></textarea>\n        </div>\n\n        <div class=\"modal-footer\">\n          <button class=\"btn btn-info\" onclick=\"pushResource()\" data-bs-toggle=\"modal\">推送</button>\n          <button class=\"btn btn-info\" onclick=\"temporarySave()\" data-bs-toggle=\"modal\">临时保存</button>\n          <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">关闭</button>\n        </div>\n      </form>\n    </div>\n  </div>\n</div>\n\n<#include \"addScript.html\">"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/resource/tool.html",
    "content": "<script type=\"text/javascript\">\n  // 编译&推送\n  function compile(resourceId,repositoryId,ispush){\n    if(ispush == 1 && !window.confirm(\"资源已推送，确认覆盖？\")){\n      return ;\n    }\n    $.post(\n            '${request.contextPath}/manage/app/resource/compile.json',\n            {\n              repositoryId: repositoryId,\n              resourceId: resourceId\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"编译成功\");\n              } else {\n                alert(\"编译失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n\n  function search(){\n    $.post(\n            '${request.contextPath}/manage/app/resource/redis/tool',\n            {\n              searchName: $(\"#searchName\").val()\n            },\n            function (data) {\n              var status = data.status;\n              if (status == 1) {\n                alert(\"编译成功\");\n              } else {\n                alert(\"编译失败！\" + data.message);\n              }\n              window.location.reload();\n            }\n    );\n  }\n</script>\n\n<div class=\"card\">\n  <div class=\"card-body\">\n    <div class=\"row\">\n      <div id=\"respo-div\" class=\"col-md-12\">\n        <button id=\"redis_version\" class=\"btn btn-success btn-sm col-auto\" type=\"button\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\">\n          <i class=\"bi bi-plus\"></i>新建迁移工具\n        </button>\n        <form class=\"row align-items-center float-end\" role=\"form\" method=\"post\" action=\"${request.contextPath}/manage/app/resource/index?tab=tool\" id=\"appList\" name=\"ec\">\n          <div class=\"col-auto\">\n            <input type=\"text\" class=\"form-control\" id=\"searchName\" name=\"searchName\"\n                   value=\"${searchName!}\" placeholder=\"资源名称\">\n          </div>\n          <div class=\"col-auto\">\n            <button type=\"submit\" class=\"btn btn-success btn-sm\" onclick=\"search()\">查询</button>\n          </div>\n        </form>\n      </div>\n    </div>\n\n    <div class=\"row mt-3\">\n      <div class=\"table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\" style=\"white-space: nowrap\">\n          <thead>\n          <tr>\n            <th scope=\"col\">资源id</th>\n            <th scope=\"col\">资源名</th>\n            <th scope=\"col\">说明</th>\n            <th scope=\"col\">目录</th>\n            <th scope=\"col\">源地址</th>\n            <th scope=\"col\">最后修改时间</th>\n            <th scope=\"col\">操作人</th>\n            <th scope=\"col\">状态</th>\n            <th scope=\"col\">操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list resourceList as resource>\n            <tr>\n              <td>${resource.id!}</td>\n              <td>\n                <#if (resource.ispush==1) || (resource.ispush==3)><a target=\"_blank\" href=\"${repository.url!}${resource.dir!}\">${resource.name!}</a></#if>\n                <#if (resource.ispush==0) || (resource.ispush==2) || (resource.ispush==4)>${resource.name!}</#if>\n              </td>\n              <td>\n                ${resource.intro!}\n              </td>\n              <td>\n                ${resource.dir!}\n              </td>\n              <td>\n                <#if resource.url?? && resource.url != ''>\n                  <a target=\"_blank\" href=\"${resource.url!}\" title=\"${resource.url!}\">[下载]</a>\n                </#if>\n              </td>\n              <td>\n                ${resource.lastmodify?string(\"yyyy-MM-dd HH:mm:ss\")}\n              </td>\n              <td>\n                ${resource.username!}\n              </td>\n              <td>\n                <#if (resource.ispush == 0)>未推送</#if>\n                <#if (resource.ispush == 1)><span style=\"color:green\"><a href=\"${request.contextPath}/manage/task/flow?taskId=${resource.taskId!}\" target=\"_blank\">已推送</a></span></#if>\n                <#if (resource.ispush == 4)><span style=\"color:orange\">编译中<a href=\"${request.contextPath}/manage/task/flow?taskId=${resource.taskId!}\" target=\"_blank\">[查看任务]</a></span></#if>\n              </td>\n              <td>\n                <button type=\"button\" class=\"btn btn-info btn-sm\" data-bs-target=\"#addResourceModal\" data-bs-toggle=\"modal\"\n                        data-resource_id=\"${resource.id!}\" >\n                  修改\n                </button>\n                <button type=\"button\" class=\"btn btn-success btn-sm\" onclick=\"compile('${resource.id!}','${repository.id!}')\" data-bs-toggle=\"modal\">推送</button>\n              </td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n</div>\n\n<#include \"addTool.html\">\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/task/flowList.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"taskFlowList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/task/queueList.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"taskQueueList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/task/taskFlowList.html",
    "content": "<script type=\"text/javascript\">\n  function skipTaskFlow(taskFlowId) {\n    var targetStatus = 5;\n    if(confirm(\"确认要将id=\" + taskFlowId + \"任务流跳过吗?\")){\n      $.post(\n              '${request.contextPath}/manage/task/changeTaskFlowStatus.json',\n              {\n                taskFlowId: taskFlowId,\n                status: targetStatus\n              },\n              function(data){\n                if(data.result){\n                  alert(\"操作成功\");\n                }else{\n                  alert(\"操作失败,message=\" + data.message);\n                  alert(data.message);\n                }\n              }\n      );\n      window.location.reload();\n    }\n  }\n\n  function changeParam(){\n    var prettyParamText = document.getElementById(\"prettyParam\");\n    if(prettyParamText.value == \"\"){\n      alert(\"配置参数不能为空\");\n      prettyParamText.focus();\n      return false;\n    }\n    var changeParamBtn = document.getElementById(\"changeParamBtn\");\n    changeParamBtn.disabled = true;\n\n    $.post(\n            '${request.contextPath}/manage/task/changeParam.json',\n            {\n              taskId: taskId.value,\n              prettyParamText: prettyParamText.value\n            },\n            function(data){\n              if(data.result){\n                $(\"#changeParamInfo\").html(\"<div class='alert alert-error' ><strong>Success!</strong>更新成功，窗口会自动关闭<button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n                var targetId = \"#changeParamModal\";\n                setTimeout(\"$('\" + targetId +\"').modal('hide');window.location.reload();\",1000);\n              }else{\n                alert(data.message);\n                changeParamBtn.disabled = false;\n                $(\"#changeParamInfo\").html(\"<div class='alert alert-error' ><strong>Error!</strong>更新失败！<button class='btn-close' data-bs-dismiss='alert'></button></div>\");\n              }\n            }\n    );\n  }\n</script>\n\n<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          任务流详情\n        </h3>\n      </div>\n      <div class=\"card-body\">\n        <div class=\"row\">\n          <div class=\"card-header\">\n            <h3 class=\"card-title\">\n              <i class=\"bi bi-globe\"></i>基本信息\n            </h3>\n          </div>\n          <div class=\"table-responsive\">\n            <table class=\"table table-striped table-bordered table-hover table-sm\">\n              <tr>\n                <td style=\"white-space: nowrap\">任务id</td>\n                <td>${taskQueue.id!}</td>\n                <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>\n              </tr>\n              <tr>\n                <td style=\"white-space: nowrap\">集群id</td>\n                <td>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/task/list?appId=${taskQueue.appId!}\">${taskQueue.appId!}</a>\n                  &nbsp;<a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${taskQueue.appId!}\">[前台]</a>\n                  &nbsp;<a target=\"_blank\" href=\"${request.contextPath}/manage/app/index?appId=${taskQueue.appId!}\">[运维]</a>\n                  <#if (taskQueue.status == 2)>\n                    &nbsp;<a href=\"${request.contextPath}/manage/task/execute?taskId=${taskQueue.id!}\">[重试任务]</a>\n                  </#if>\n                  <#if appDesc?? && ((appDesc.type == 1) || (appDesc.type == 4))>\n                    &nbsp;<a target=\"_blank\" href=\"${request.contextPath}/manage/app/appNutCrackerStat?appId=${taskQueue.appId!}\">[代理]</a>\n                  </#if>\n                </td>\n                <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>\n              </tr>\n              <tr>\n                <td style=\"white-space: nowrap\">重要信息</td>\n                <td>${taskQueue.importantInfo!}</td>\n                <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>\n              </tr>\n              <tr>\n                <td style=\"white-space: nowrap\">执行机器</td>\n                <td>\n                  ${taskQueue.executeIpPort!}\n                </td>\n                <td></td>\n              </tr>\n              <tr>\n                <td style=\"white-space: nowrap\">任务名</td>\n                <td>${taskQueue.className!}</td>\n                <td></td>\n              </tr>\n              <tr>\n                <td style=\"white-space: nowrap\">进度</td>\n                <td>\n                  ${taskQueue.progress!}\n                </td>\n                <td></td>\n              </tr>\n              <tr>\n                <td style=\"white-space: nowrap\">任务初始参数</td>\n                <td>\n                  ${taskQueue.initParam!}\n                </td>\n                <td>\n                </td>\n              </tr>\n              <tr>\n                <td style=\"white-space: nowrap\">任务动态参数</td>\n                <td>\n                  ${taskQueue.prettyParam!}\n                </td>\n                <td style=\"white-space: nowrap\">\n                  <button type=\"button\" class=\"btn btn-success\" data-bs-target=\"#changeParamModal\" data-bs-toggle=\"modal\">修改</button>\n                </td>\n              </tr>\n            </table>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          <i class=\"bi bi-globe\"></i>任务列表\n        </h3>\n      </div>\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\">\n          <thead>\n          <tr>\n            <th>序号</th>\n            <th>任务流id</th>\n            <th>步骤名称</th>\n            <th>超时时间(s)</th>\n            <th>执行时间(s)</th>\n            <th>执行IP</th>\n            <th>状态</th>\n            <th>日志</th>\n            <th>开始时间</th>\n            <th>结束时间</th>\n            <th>步骤描述</th>\n            <th>运维建议</th>\n            <th>操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list taskStepFlowList as taskStepFlow>\n            <tr>\n              <td>${taskStepFlow.orderNo!}</td>\n              <td>${taskStepFlow.id!}</td>\n              <td>${taskStepFlow.stepName!}</td>\n              <td>${taskStepFlow.taskStepMeta.timeout!}</td>\n              <td>${taskStepFlow.costSeconds!}</td>\n              <td>${taskStepFlow.executeIpPort!}</td>\n              <td>${taskStepFlow.statusDesc!}</td>\n              <td>\n                <a href=\"#${taskStepFlow.stepName!}\">查看</a>\n              </td>\n              <td>\n                ${taskStepFlow.startTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n              </td>\n              <td>\n                ${taskStepFlow.endTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n              </td>\n              <td>${taskStepFlow.taskStepMeta.stepDesc!}</td>\n              <td>${taskStepFlow.taskStepMeta.opsDevice!}</td>\n              <td>\n                <#if (taskStepFlow.status == 0) || (taskStepFlow.status == 1) || (taskStepFlow.status == 2)>\n                  <button type=\"button\" class=\"btn btn-success btn-sm\" onclick=\"skipTaskFlow('${taskStepFlow.id!}')\">跳过</button>\n                </#if>\n              </td>\n\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          <i class=\"bi bi-globe\"></i>详细日志\n        </h3>\n      </div>\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\">\n          <thead>\n          <tr>\n            <th>序号</th>\n            <th>步骤名称</th>\n            <th>日志</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list taskStepFlowList as taskStepFlow>\n            <#assign stepName = taskStepFlow.stepName/>\n            <tr id=\"${stepName!}\">\n              <td>${taskStepFlow_index + 1}</td>\n              <td>${stepName!}</td>\n              <td>\n                <#list stepLogListMap[stepName] as line>\n                  ${line!}<br/>\n                </#list>\n              </td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n      </div>\n    </div>\n\n    <div id=\"changeParamModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n      <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n\n          <div class=\"modal-header\">\n            <h4 class=\"modal-title\">动态配置修改</h4>\n            <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n          </div>\n\n          <form class=\"form-horizontal form-bordered form-row-stripped\">\n            <div class=\"modal-body\">\n              <div class=\"row\">\n                <!-- 控件开始 -->\n                <div class=\"col-md-12\">\n                  <!-- form-body开始 -->\n                  <div class=\"form-body\">\n                    <div class=\"form-group\">\n                      <textarea rows=\"20\" name=\"prettyParam\" id=\"prettyParam\" class=\"form-control\">${taskQueue.prettyParam!}</textarea>\n                    </div>\n                    <input type=\"hidden\" name=\"taskId\" id=\"taskId\" value=\"${taskQueue.id!}\"/>\n                  </div>\n                  <!-- form-body 结束 -->\n                </div>\n                <div id=\"changeParamInfo\"></div>\n                <!-- 控件结束 -->\n              </div>\n            </div>\n\n            <div class=\"modal-footer\">\n              <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n              <button type=\"button\" id=\"changeParamBtn\" class=\"btn btn-danger\" onclick=\"changeParam()\">Ok</button>\n            </div>\n\n          </form>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/task/taskQueueList.html",
    "content": "<div class=\"row\">\n  <div class=\"col-12\">\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <h3 class=\"card-title\">\n          任务管理\n        </h3>\n      </div>\n      <div class=\"card-body\">\n        <form id=\"pageForm\" class=\"row align-items-center\" action=\"${request.contextPath}/manage/task/list\" method=\"get\" role=\"form\">\n          <label class=\"col-form-label col-auto\">\n            任务id:\n          </label>\n          <div class=\"col-md-2\">\n            <input type=\"text\" name=\"searchTaskId\" class=\"form-control\" id=\"searchTaskId\" value=\"${searchTaskId!}\" placeholder=\"\" onchange=\"testisNum(this.id)\"/>\n          </div>\n          <label class=\"col-form-label col-auto\">\n            appId:\n          </label>\n          <div class=\"col-md-2\">\n            <input type=\"text\" name=\"appId\" id=\"appId\" class=\"form-control\"  value=\"${taskSearch.appId!}\" placeholder=\"\" onchange=\"testisNum(this.id)\"/>\n          </div>\n          <label class=\"col-form-label col-auto\">\n            类名:\n          </label>\n          <div class=\"col-md-2\">\n            <input type=\"text\" name=\"className\" class=\"form-control\"  id=\"className\" value=\"${taskSearch.className!}\" placeholder=\"\" />\n          </div>\n          <label class=\"col-form-label col-auto\">\n            状态:\n          </label>\n          <div class=\"col-auto\">\n            <select name=\"status\" class=\"form-select\">\n              <option value=\"-1\">\n                全部\n              </option>\n              <option value=\"0\" <#if (taskSearch.status == 0)>selected=\"selected\"</#if>>\n              新任务\n              </option>\n              <option value=\"1\" <#if (taskSearch.status == 1)>selected=\"selected\"</#if>>\n              运行中\n              </option>\n              <option value=\"2\" <#if (taskSearch.status == 2)>selected=\"selected\"</#if>>\n              中断\n              </option>\n              <option value=\"4\" <#if (taskSearch.status == 4)>selected=\"selected\"</#if>>\n              成功\n              </option>\n              <option value=\"5\" <#if (taskSearch.status == 5)>selected=\"selected\"</#if>>\n              准备\n              </option>\n            </select>\n          </div>\n          <input type=\"hidden\" name=\"pageNo\" id=\"pageNo\">\n          &nbsp;<button type=\"submit\" class=\"btn btn-primary btn-sm col-auto\">查询</button>\n        </form>\n      </div>\n    </div>\n    <div class=\"card\">\n      <div class=\"card-body table-responsive\">\n        <table class=\"table table-striped table-bordered table-hover table-sm\" id=\"tableDataList\" style=\"white-space: nowrap\">\n          <thead>\n          <tr>\n            <th scope=\"col\">任务id</th>\n            <th scope=\"col\">集群id</th>\n            <th scope=\"col\">类名</th>\n            <th scope=\"col\">信息</th>\n            <th scope=\"col\">状态</th>\n            <th scope=\"col\">进度</th>\n            <th scope=\"col\">父任务id</th>\n            <th scope=\"col\">耗时(s)</th>\n            <th scope=\"col\">开始时间</th>\n            <th scope=\"col\">结束时间</th>\n            <th scope=\"col\">创建时间</th>\n            <th scope=\"col\">备注</th>\n            <th scope=\"col\">操作</th>\n          </tr>\n          </thead>\n          <tbody>\n          <#list taskQueueList as taskQueue>\n            <tr>\n              <td>\n                <#if (taskQueue.className == 'MachineDownAndAppFixTask')>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/machine/taskInfo?taskId=${taskQueue.id!}\">${taskQueue.id!}</a>\n                <#else>\n                  ${taskQueue.id!}\n                </#if>\n              </td>\n              <td>${taskQueue.appId!}</td>\n              <td>${taskQueue.className!}</td>\n              <td>\n                <#if (taskQueue.className == 'TwemproxyToTwemproxyTaskV2')>\n                  <a target=\"_blank\" href=\"${request.contextPath}/manage/migrate/list\">${taskQueue.importantInfo!}</a>\n                <#else>\n                  ${taskQueue.importantInfo!}\n                </#if>\n              </td>\n              <td>\n                <#if (taskQueue.status == 2)>\n                  <font color='red'>${taskQueue.statusDesc!}</font>\n                <#else>\n                  ${taskQueue.statusDesc!}\n                </#if>\n              </td>\n              <td>${taskQueue.progress!}</td>\n              <td>\n                <#if (taskQueue.parentTaskId != 0)>\n                  ${taskQueue.parentTaskId!}\n                </#if>\n              </td>\n              <td>${taskQueue.costSeconds!}</td>\n              <td>\n                <#if (taskQueue.status != 0)>\n                  ${taskQueue.startTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </#if>\n              </td>\n              <td>\n                <#if (taskQueue.status == 4)>\n                  ${taskQueue.endTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n                </#if>\n              </td>\n              <td>\n                ${taskQueue.createTime!?string(\"yyyy-MM-dd HH:mm:ss\")}\n              </td>\n              <td>${taskQueue.taskNote!}</td>\n              <td>\n                <a href=\"${request.contextPath}/manage/task/flow?taskId=${taskQueue.id!}\">[执行步骤]</a>\n                <#if (taskQueue.status == 2)>\n                  <a href=\"${request.contextPath}/manage/task/execute?taskId=${taskQueue.id!}\">[重试任务]</a>\n                </#if>\n              </td>\n            </tr>\n          </#list>\n          </tbody>\n        </table>\n        <div style=\"margin-top: 10px;\">\n          <span>\n              <div id=\"pageDetail\" style=\"float:left;padding-top:7px;color:#4A64A4;\">\n                  <#if page??>\n                      共${page.totalPages!}页,${page.totalCount!}条\n                  </#if>\n              </div>\n              <nav id='ccPagenitor' aria-label=\"Page navigation example\" class=\"d-inline-flex float-end\">\n              </nav>\n          </span>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<#include \"/manage/inc/page.html\">\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/total/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"/manage/total/totalList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/total/totalList.html",
    "content": "\n<link href=\"${request.contextPath}/assets/css/common.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/toastr/toastr.min.css\" rel=\"stylesheet\" type=\"text/css\">\n<script type=\"text/javascript\" src=\"${request.contextPath}/assets/vendor/toastr/toastr.min.js\"></script>\n\n\n<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">应用运维</h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"col-md-12\">\n                    <div class=\"float-end\">\n                        <form class=\"row justify-content-end\" role=\"form\" action=\"${request.contextPath}/manage/total/list\" method=\"get\" id=\"pageForm\">\n                            <div class=\"col-md-4\">\n                                <input type=\"text\" id=\"appParam\" name=\"appParam\" value=\"${appParam!}\" class=\"form-control\"\n                                       placeholder=\"应用ID/应用名\">\n                            </div>\n                            <label class=\"col-form-label col-auto\">\n                                持久化类型:\n                            </label>\n                            <div class=\"col-md-4\">\n                                <select name=\"persistenceType\" id=\"persistenceType\" class=\"form-select\">\n                                    <option value=\"\" <#if !(persistenceType??)>selected=\"selected\"</#if>>\n                                    所有\n                                    </option>\n                                    <option value=\"0\" <#if persistenceType?? && (persistenceType == 0)>selected=\"selected\"</#if>>\n                                    常规型\n                                    </option>\n                                    <option value=\"1\" <#if persistenceType?? && (persistenceType == 1)>selected=\"selected\"</#if>>\n                                    性能优化型\n                                    </option>\n                                    <option value=\"2\" <#if persistenceType?? && (persistenceType == 2)>selected=\"selected\"</#if>>\n                                    性能增强型\n                                    </option>\n                                    <option value=\"3\" <#if persistenceType?? && (persistenceType == 3)>selected=\"selected\"</#if>>\n                                    不持久化\n                                    </option>\n                                </select>\n                            </div>\n                            <input type=\"hidden\" name=\"pageNo\" id=\"pageNo\">\n                            <div class=\"col-auto\">\n                                <button type=\"submit\" class=\"btn btn-info\">查询</button>\n                            </div>\n                        </form>\n                    </div>\n                </div>\n            </div>\n        </div>\n\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">应用列表</h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"table-responsive\">\n                    <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\" name=\"tableDataList\" style=\"white-space: nowrap\">\n                        <thead>\n                        <tr>\n                            <th scope=\"col\">应用ID</th>\n                            <th scope=\"col\">应用名</td>\n                            <th scope=\"col\">版本</td>\n                            <th scope=\"col\">应用类型</td>\n                            <th scope=\"col\">内存详情</td>\n                            <th scope=\"col\">空间详情</td>\n                            <th scope=\"col\">最大碎片率</td>\n                            <th scope=\"col\">命中率</td>\n                            <th scope=\"col\">天数</td>\n                            <th scope=\"col\">申请状态</td>\n                            <th scope=\"col\">持久化</td>\n                            <th scope=\"col\">淘汰策略</td>\n                            <th scope=\"col\">操作</td>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        <#list appDetailList as appDetail>\n                            <tr>\n                                <td>\n\n                                    <#if (appDetail.appDesc.status == 0) || (appDetail.appDesc.status == 1)>\n                                        ${appDetail.appDesc.appId!}\n                                    <#elseif (appDetail.appDesc.status == 2) || (appDetail.appDesc.status == 3) || (appDetail.appDesc.status == 4)>\n                                        <a target=\"_blank\"\n                                           href=\"${request.contextPath}/manage/app/index?appId=${appDetail.appDesc.appId!}\">${appDetail.appDesc.appId!}</a>\n                                    </#if>\n\n                                </td>\n\n                                <td>\n\n                                    <#if (appDetail.appDesc.status == 0) || (appDetail.appDesc.status == 1)>\n                                        ${appDetail.appDesc.name!}\n                                    <#elseif (appDetail.appDesc.status == 2) || (appDetail.appDesc.status == 3) || (appDetail.appDesc.status == 4)>\n                                        <a target=\"_blank\"\n                                           href=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId!}\"\n                                           title=\"${appDetail.appDesc.name!}\">\n                                            <#if (appDetail.appDesc.name?length < 20)>\n                                                ${appDetail.appDesc.name!}\n                                            <#else>\n                                                ${appDetail.appDesc.name?substring(0,20)}...\n                                            </#if>\n                                        </a>\n                                    </#if>\n                                </td>\n                                <td>${appDetail.appDesc.versionName!}</td>\n                                <td>\n                                    ${appDetail.appDesc.typeDesc!}\n                                </td>\n                                <td>\n                                    <span style=\"display:none\">${(appDetail.memUsePercent / 100)?string(\"0.00\")}</span>\n                                    <div class=\"progress progress-fs-1\">\n\n                                        <#if (appDetail.memUsePercent >= 80)>\n                                            <#assign progressBarStatus =\"bg-danger\">\n                                        <#else>\n                                            <#assign progressBarStatus=\"bg-success\">\n                                        </#if>\n\n                                        <div class=\"progress-bar ${progressBarStatus!}\"\n                                             role=\"progressbar\" aria-valuenow=\"${appDetail.memUsePercent!}\"\n                                             aria-valuemax=\"100\"\n                                             aria-valuemin=\"0\" style=\"width: ${appDetail.memUsePercent!}%; overflow: visible;\">\n                                            <span style=\"color: #000000; margin-bottom: 0\">\n                                                ${(appDetail.currentMem / 1024)?string(\"0.00\")}G&nbsp;&nbsp;Used\n                                                /${(appDetail.mem / 1024 * 1.0)?string(\"0.00\")}G&nbsp;&nbsp;Total\n                                            </span>\n                                        </div>\n                                    </div>\n                                </td>\n                                <td>\n                                    <div class=\"progress progress-fs-1\">\n                                        <div class=\"progress-bar bg-success\"\n                                             role=\"progressbar\" aria-valuenow=\"0\"\n                                             aria-valuemax=\"100\"\n                                             aria-valuemin=\"0\" style=\"width: 0%; overflow: visible;\">\n                                                <span style=\"color: #000000; margin-bottom: 0\">\n                                                    无数据\n                                                </span>\n                                        </div>\n                                    </div>\n                                </td>\n                                <td>\n                                    <a target=\"_blank\"\n                                       href=\"${request.contextPath}/admin/instance/index?instanceId=${appDetail.instIdWithHighestMemFragRatio!}\">${appDetail.highestMemFragRatio!}</a>\n                                </td>\n\n                                <td>\n                                    <span style=\"display:none\">${(appDetail.hitPercent / 100)?string(\"0.00\")}</span>\n                                    <#if (appDetail.hitPercent <= 0)>\n                                        无\n                                    <#elseif (appDetail.hitPercent <= 30)>\n                                        <label class=\"label label-danger\">${appDetail.hitPercent!}%</label>\n                                    <#elseif (appDetail.hitPercent >= 30) && (appDetail.hitPercent < 50)>\n                                        <label class=\"label label-warning\">${appDetail.hitPercent!}%</label>\n                                    <#elseif (appDetail.hitPercent >= 50) && (appDetail.hitPercent < 90)>\n                                        <label class=\"label label-info\">${appDetail.hitPercent!}%</label>\n                                    <#else>\n                                        <label class=\"label label-success\">${appDetail.hitPercent!}%</label>\n                                    </#if>\n                                </td>\n                                <td>${appDetail.appDesc.appRunDays!}</td>\n                                <td>\n\n                                    <#if (appDetail.appDesc.status == 0)>\n                                        <font color=\"red\">未申请</font>\n                                    <#elseif (appDetail.appDesc.status == 1)>\n                                        <font color=\"red\">申请中</font>\n                                    <#elseif (appDetail.appDesc.status == 2)>\n                                        运行中\n                                    <#elseif (appDetail.appDesc.status == 3)>\n                                        <font color=\"red\">已下线</font>\n                                    <#elseif (appDetail.appDesc.status == 4)>\n                                        <font color=\"red\">驳回</font>\n                                    </#if>\n\n                                </td>\n                                <td>\n\n                                    <#if (appDetail.appDesc.persistenceType == 0)>\n\n                                        <#if (appDetail.appDesc.type == 2) || (appDetail.appDesc.type == 5)>\n                                            <button type=\"button\" class=\"btn btn-sm btn-info\" style=\"width: 100px;\" data-bs-toggle=\"modal\" data-bs-target=\"#updatePersisModal\" onclick='values(\"${appDetail.appDesc.appId!}\", \"${appDetail.appDesc.name!}\", \"${appDetail.appDesc.persistenceType!}\")'>常规型</button>\n                                        <#else>\n                                            <button type=\"button\" class=\"btn btn-sm btn-info\" style=\"width: 100px;background:#CCCCCC\" disabled=\"disabled\">常规型</button>\n                                        </#if>\n\n                                    <#elseif (appDetail.appDesc.persistenceType == 1)>\n\n                                        <#if (appDetail.appDesc.type == 2) || (appDetail.appDesc.type == 5)>\n                                            <button type=\"button\" class=\"btn btn-sm btn-success\" style=\"width: 100px;\" data-bs-toggle=\"modal\" data-bs-target=\"#updatePersisModal\" onclick='values(\"${appDetail.appDesc.appId!}\", \"${appDetail.appDesc.name!}\", \"${appDetail.appDesc.persistenceType!}\")'>性能优化型</button>\n                                        <#else>\n                                            <button type=\"button\" class=\"btn btn-sm btn-info\" style=\"width: 100px;background:#CCCCCC\" disabled=\"disabled\">性能优化型</button>\n                                        </#if>\n\n                                    <#elseif (appDetail.appDesc.persistenceType == 2)>\n\n                                        <#if (appDetail.appDesc.type == 2) || (appDetail.appDesc.type == 5)>\n                                            <button type=\"button\" class=\"btn btn-sm btn-primary\" style=\"width: 100px;\" data-bs-toggle=\"modal\" data-bs-target=\"#updatePersisModal\" onclick='values(\"${appDetail.appDesc.appId!}\", \"${appDetail.appDesc.name!}\", \"${appDetail.appDesc.persistenceType!}\")'>性能增强型</button>\n                                        <#else>\n                                            <button type=\"button\" class=\"btn btn-sm btn-info\" style=\"width: 100px;background:#CCCCCC\" disabled=\"disabled\">性能增强型</button>\n                                        </#if>\n\n                                    <#elseif (appDetail.appDesc.persistenceType == 3)>\n\n                                        <#if (appDetail.appDesc.type == 2) || (appDetail.appDesc.type == 5)>\n                                            <button type=\"button\" class=\"btn btn-sm btn-danger\" style=\"width: 100px;\" data-bs-toggle=\"modal\" data-bs-target=\"#updatePersisModal\" onclick='values(\"${appDetail.appDesc.appId!}\", \"${appDetail.appDesc.name!}\", \"${appDetail.appDesc.persistenceType!}\")'>不持久化</button>\n                                        <#else>\n                                            <button type=\"button\" class=\"btn btn-sm btn-info\" style=\"width: 100px;background:#CCCCCC\" disabled=\"disabled\">不持久化</button>\n                                        </#if>\n\n                                    </#if>\n\n                                </td>\n                                <td>\n\n                                    <#if !(appDetail.appDesc.maxmemoryPolicyDesc??)>\n                                        默认\n                                    <#else>\n                                        ${appDetail.appDesc.maxmemoryPolicyDesc!}\n                                    </#if>\n\n                                </td>\n                                <td>\n\n                                    <div class=\"d-inline-flex\">\n                                        <#if (appDetail.appDesc.status == 2)>\n                                            <a target=\"_blank\" type=\"button\" class=\"btn btn-sm btn-success\"\n                                               href=\"${request.contextPath}/manage/app/index?appId=${appDetail.appDesc.appId!}\">应用运维</a>\n\n                                            <a target=\"_blank\" type=\"button\" class=\"btn btn-sm btn-info\"\n                                               href=\"${request.contextPath}/manage/app/migrate/init?appId=${appDetail.appDesc.appId!}\">应用迁移</a>\n\n                                            <button type=\"button\" class=\"btn btn-sm btn-danger\"\n                                                    id=\"offline${appDetail.appDesc.appId!}\"\n                                                    onclick=\"offLine(${appDetail.appDesc.appId!})\">应用下线\n                                            </button>\n                                            <#if (appDetail.appDesc.isVersionUpgrade==0)>\n                                                <button type=\"button\" id=\"install\" class=\"btn btn-sm btn-info\" data-bs-toggle=\"modal\"\n                                                        disabled=\"disabled\" style=\"background:#CCCCCC\">\n                                                    最新版本\n                                                </button>\n                                            </#if>\n                                            <#if (appDetail.appDesc.isVersionUpgrade==1)>\n                                                <button type=\"button\" class=\"btn btn-sm btn-primary\"\n                                                        data-bs-target=\"#upgradeRedisVersionModal${appDetail.appDesc.appId!}\"\n                                                        data-bs-toggle=\"modal\">\n                                                    Redis版本升级\n                                                </button>\n                                            </#if>\n\n                                        </#if>\n                                    </div>\n\n                                </td>\n                            </tr>\n                        </#list>\n                        </tbody>\n                    </table>\n                    <div style=\"margin-top: 10px;\">\n                        <span>\n                            <div id=\"pageDetail\"\n                                 style=\"float:left;padding-top:7px;color:#4A64A4;\">\n                                <#if page??>\n                                    共${page.totalPages!}页,${page.totalCount!}条\n                                </#if>\n                            </div>\n                            <nav id='ccPagenitor' aria-label=\"Page navigation example\" class=\"d-inline-flex float-end\">\n                            </nav>\n                        </span>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n\n<div id=\"updatePersisModal\" name=\"updatePersis-modal\" class=\"modal fade\" tabindex=\"-1\"\n     data-width=\"400\"\n     aria-hidden=\"true\">\n    <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">修改持久化配置</h4>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n            <div class=\"modal-header\">\n                <h5 id=\"updatePersisAppInfo\">【应用】</h5>\n            </div>\n\n            <form class=\"form-horizontal form-bordered form-row-stripped\">\n                <div class=\"modal-body\">\n                    <div class=\"row\">\n                        <!-- 控件开始 -->\n                        <div class=\"col-md-12\">\n                            <!-- form-body开始 -->\n                            <div class=\"form-body\">\n                                <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-3\">\n                                        持久化类型:\n                                    </label>\n                                    <div class=\"col-md-8\">\n                                        <select name=\"updatePersisPersistenceType\" id=\"updatePersisPersistenceType\" class=\"form-select\">\n                                            <option value=\"\" selected=\"selected\">\n                                                所有\n                                            </option>\n                                            <option value=\"0\">\n                                                常规型\n                                            </option>\n                                            <option value=\"1\">\n                                                性能优化型\n                                            </option>\n                                            <option value=\"2\">\n                                                性能增强型\n                                            </option>\n                                            <option value=\"3\">\n                                                不持久化\n                                            </option>\n                                        </select>\n                                        <input type=\"hidden\" name=\"updatePersisAppId\" id=\"updatePersisAppId\">\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n                <div class=\"modal-footer\">\n                    <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n                    <button type=\"button\" id=\"updatePersisBtn\" class=\"btn btn-danger\"\n                            onclick=\"updatePersis()\">\n                        Ok\n                    </button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n\n<#list appDetailList as appDetail>\n    <#if appDetail.appDesc.isVersionUpgrade == 1 || appDetail.appDesc.type == 12>\n        <#include '/manage/total/upgradeRedis.html'>\n    </#if>\n</#list>\n\n<#include \"/manage/inc/page.html\">\n\n<script type=\"text/javascript\">\n    function offLine(appId,appAuditId) {\n        if (confirm(\"确认要下线该应用？应用id=\" + appId)) {\n            $.ajax({\n                type: \"get\",\n                url: \"${request.contextPath}/manage/app/offLine.json\",\n                data: {appId: appId,appAuditId: appAuditId,},\n                success: function (result) {\n                    alert(result.message);\n                    setTimeout(\"reloadPage(\" + result.taskId + \");\", 0);\n                }\n            });\n        }\n    }\n\n    function reloadPage(taskid) {\n        location.href = \"${request.contextPath}/manage/task/flow?taskId=\" + taskid;\n    }\n\n</script>\n\n<script>\n    // 配置预览\n    function configPreview(appId) {\n        window.open(\"${request.contextPath}/manage/redisConfig/init?versionid=\" + $('#versionSelect' + appId + \" option:selected\").attr('versionid'));\n    }\n\n    // 升级配置对比\n    function configContrast(currentVerisonId, appId) {\n        window.open(\"${request.contextPath}/manage/redisConfig/contrast?currentVersionId=\" + currentVerisonId + \"&upgradeVersionId=\" + $('#versionSelect' + appId + \" option:selected\").attr('versionid'));\n    }\n</script>\n\n<script>\n    var installId = \"\";\n    var method = \"\";\n\n    /**\n     * 一键执行\n     */\n    function install(appId) {\n        $('#upgradeRedisVersionModal'+installId).modal(\"show\");\n        installId = appId;\n        if (!$('#versionSelect' + installId).val()) {\n            alert(\"请先选择升级版本\");\n            return;\n        }\n        if ($('#install' + installId).html() == \"继续\") {\n            // step forward\n            setTimeout(method + '()', 1000);\n        } else if ($('#install' + installId).html() == \"升级完成\") {\n            // step complete\n            window.location.reload();\n        } else {\n            instanceCheck(appId);\n        }\n    }\n\n    /**\n     * 继续 step forward\n     */\n    function goOn(m) {\n        $('#upgradeRedisVersionModal'+installId).modal(\"show\");\n        $(\"#install\" + installId).html(\"继续\");\n        method = m;\n    }\n\n    /**\n     * 1.实例检查\n     */\n    function instanceCheck() {\n        $('#upgradeRedisVersionModal'+installId).modal(\"show\");\n        var instanceId = \"instanceCheck\" + installId;\n        active(instanceId);\n        $(\"#install\" + installId).html(\"配置检查中...\");\n        $(\"#versionSelect\" + installId).attr(\"disabled\", \"disabled\");//不能修改版本\n        $.post('${request.contextPath}/manage/redis/upgrade/check/instance',\n            {\n                \"appId\": installId,\n                \"upgradeVersionId\": $('#versionSelect' + installId + \" option:selected\").attr('versionid'),\n                \"upgradeVersionName\": $('#versionSelect' + installId + \" option:selected\").val()\n            },\n            function (data) {\n                if (data.status == 1) {\n                    $(\"#instanceInfo\" + installId).html(data.instanceInfo);\n                    if (data.machineInstallInfo != \"\") {\n                        toastr.success(data.machineInstallInfo);\n                    }\n                    $(\"#div\" + installId).removeAttr(\"hidden\");\n                    complete(instanceId);\n                    // Slave更新配置&重启\n                    goOn(\"slaveUpdate\");\n                } else if (data.status == -1) {\n                    $(\"#instanceInfo\" + installId).html(data.instanceInfo);\n                    $(\"#div\" + installId).removeAttr(\"hidden\");\n                    toastr.error(\"异常信息：\" + data.message);\n                    warn(instanceId);\n                    goOn(\"instanceCheck\");\n                } else {\n                    toastr.error(\"操作失败,请查看日志!\");\n                    warn(instanceId);\n                    goOn(\"instanceCheck\");\n                }\n            }\n        );\n    }\n\n    /**\n     * 2.Slave配置替换重启\n     */\n    function slaveUpdate() {\n        $('#upgradeRedisVersionModal'+installId).modal(\"show\");\n        var slaveUpdateId = \"slaveUpdate\" + installId;\n        active(slaveUpdateId);\n        $(\"#install\" + installId).html(\"配置更新中...\");\n        $.post('${request.contextPath}/manage/redis/upgrade/slave/update/config',\n            {\n                \"appId\": installId,\n                \"upgradeVersionId\": $('#versionSelect' + installId + \" option:selected\").attr('versionid'),\n                \"upgradeVersionName\": $('#versionSelect' + installId + \" option:selected\").val()\n            },\n            function (data) {\n                if (data.status == 1) {\n                    $(\"#instanceInfo\" + installId).html(data.instanceInfo);\n                    $(\"#instanceLog\" + installId).html(data.instanceLog);\n                    complete(slaveUpdateId);\n                    // 下一步主从failover\n                    goOn(\"msFailover\");\n                } else if (data.status == -1) {\n                    toastr.error(\"异常信息：\" + data.message);\n                    warn(slaveUpdateId);\n                    goOn(\"slaveUpdate\");\n                } else {\n                    toastr.error(\"操作失败,请查看日志!\");\n                    warn(slaveUpdateId);\n                    goOn(\"slaveUpdate\");\n                }\n            });\n    }\n\n    /**\n     * 3.主从Failover\n     */\n    function msFailover() {\n        $('#upgradeRedisVersionModal'+installId).modal(\"show\");\n        var failoverId = \"msFailover\" + installId;\n        active(failoverId);\n        $(\"#install\" + installId).html(\"主从切换中...\");\n        $.post('${request.contextPath}/manage/redis/upgrade/slave/failover',\n            {\n                \"appId\": installId,\n                \"upgradeVersionId\": $('#versionSelect' + installId + \" option:selected\").attr('versionid')\n            },\n            function (data) {\n                if (data.status == 1) {\n                    $(\"#instanceInfo\" + installId).html(data.instanceInfo);\n                    $(\"#instanceLog\" + installId).html(data.instanceLog);\n                    complete(failoverId);\n                    // 下一步新的从节点替换配置\n                    goOn(\"newSlaveUpdate\");\n                } else if (data.status == -1) {\n                    toastr.error(\"异常信息：\" + data.message);\n                    warn(failoverId);\n                    goOn(\"msFailover\");\n                } else {\n                    toastr.error(\"操作失败,请查看日志!\");\n                    warn(failoverId);\n                    goOn(\"msFailover\");\n                }\n            });\n    }\n\n    /**\n     * 4.new Slave配置替换重启\n     */\n    function newSlaveUpdate() {\n        $('#upgradeRedisVersionModal'+installId).modal(\"show\");\n        var newSlaveUpdateId = \"newSlaveUpdate\" + installId;\n        active(newSlaveUpdateId);\n        $(\"#install\" + installId).html(\"配置更新中...\");\n        $.post('${request.contextPath}/manage/redis/upgrade/slave/update/config',\n            {\n                \"appId\": installId,\n                \"upgradeVersionId\": $('#versionSelect' + installId + \" option:selected\").attr('versionid'),\n                \"upgradeVersionName\": $('#versionSelect' + installId + \" option:selected\").val()\n            },\n            function (data) {\n                if (data.status == 1) {\n                    $(\"#instanceInfo\" + installId).html(data.instanceInfo);\n                    $(\"#instanceLog\" + installId).html(data.instanceLog);\n                    complete(newSlaveUpdateId);\n                    goOn(\"upgradeComplete\");\n                } else if (data.status == -1) {\n                    toastr.error(\"异常信息：\" + data.message);\n                    warn(newSlaveUpdateId);\n                    goOn(\"newSlaveUpdate\");\n                } else {\n                    toastr.error(\"操作失败,请查看日志!\");\n                    warn(newSlaveUpdateId);\n                    goOn(\"newSlaveUpdate\");\n                }\n            });\n    }\n\n    /**\n     * 5.升级完成\n     */\n    function upgradeComplete() {\n        $('#upgradeRedisVersionModal'+installId).modal(\"show\");\n        var upgradeCompleteId = \"upgradeComplete\" + installId;\n        $.post('${request.contextPath}/manage/redis/upgrade/complete/check',\n            {\n                \"appId\": installId,\n                \"upgradeVersionId\": $('#versionSelect' + installId + \" option:selected\").attr('versionid')\n            },\n            function (data) {\n                if (data.status == 1) {\n                    $(\"#instanceInfo\" + installId).html(data.instanceInfo);\n                    $(\"#instanceLog\" + installId).html(data.instanceLog);\n                    active(upgradeCompleteId);\n                    $(\"#install\" + installId).html(\"升级完成\");\n                } else if (data.status == -1) {\n                    toastr.error(\"异常信息：\" + data.message);\n                    warn(upgradeCompleteId);\n                } else {\n                    toastr.error(\"操作失败,请查看日志!\");\n                    warn(upgradeCompleteId);\n                }\n            });\n    }\n\n    function warn(id) {\n        $(\"#\" + id).addClass(\"warn\");\n    }\n\n    function disable(id) {\n        $(\"#\" + id).removeClass(\"active\").addClass(\"disabled\");\n    }\n\n    function active(id) {\n        $(\"#\" + id).removeClass(\"disabled\").removeClass(\"warn\").addClass(\"active\");\n    }\n\n    function complete(id) {\n        $(\"#\" + id).removeClass(\"active\").removeClass(\"warn\").addClass(\"complete\");\n    }\n</script>\n\n<script>\n    $(function(){\n        $('#updatePersisModal').modal(\"hide\");\n    });\n\n    function values(appId, appName, persistenceType){\n        $(\"#updatePersisAppInfo\").html(\"【应用】\" + appId + \":\" + appName);\n        $(\"#updatePersisPersistenceType\").val(persistenceType);\n        $(\"#updatePersisAppId\").val(appId);\n    }\n\n    function updatePersis() {\n        var persistenceType = $(\"#updatePersisPersistenceType\").val();\n        var appId = $(\"#updatePersisAppId\").val();\n        $.post('${request.contextPath}/manage/app/updateAppPersistenceType',\n            {\n                \"appId\": appId,\n                \"persistenceType\":persistenceType\n            },\n            function (data) {\n                if (data == 1) {\n                    alert(\"操作成功\");\n                    window.location.reload();\n                } else {\n                    alert(\"操作失败，请确认\");\n                }\n            });\n    }\n\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/total/upgradeRedis.html",
    "content": "<!-- redis版本升级 -->\n<div id=\"upgradeRedisVersionModal${appDetail.appDesc.appId}\" class=\"modal fade\" tabindex=\"-1\">\n    <div class=\"modal-dialog modal-xl\">\n        <div class=\"modal-content\">\n            <div class=\"modal-header\">\n                <h4 class=\"modal-title\">Redis版本升级流程</h4><b style=\"font-size:12px;color:red\"><i>只支持小版本号升级</i></b>\n                <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n            </div>\n            <div class=\"modal-body\">\n                <div class=\"row bs-wizard\" style=\"border-bottom:0;\">\n                    <div id=\"versionUpgrade${appDetail.appDesc.appId}\" class=\"col-sm-2 bs-wizard-step warn complete\">\n                        <div class=\"text-center bs-wizard-stepnum\">1.升级版本选择</div>\n                        <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                        <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                        <div class=\"bs-wizard-info text-center\">版本检查</div>\n                    </div>\n\n                    <div id=\"instanceCheck${appDetail.appDesc.appId}\" class=\"col-sm-2 bs-wizard-step disabled\">\n                        <div class=\"text-center bs-wizard-stepnum\">2.实例配置检查</div>\n                        <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                        <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                        <div class=\"bs-wizard-info text-center\">配置检查</div>\n                    </div>\n\n                    <div id=\"slaveUpdate${appDetail.appDesc.appId}\" class=\"col-sm-2 bs-wizard-step disabled\">\n                        <div class=\"text-center bs-wizard-stepnum\">3.Slave配置更新</div>\n                        <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                        <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                        <div class=\"bs-wizard-info text-center\">更新配置并重启</div>\n                    </div>\n\n                    <div id=\"msFailover${appDetail.appDesc.appId}\" class=\"col-sm-2 bs-wizard-step disabled\">\n                        <div class=\"text-center bs-wizard-stepnum\">4.主从节点Failover</div>\n                        <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                        <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                        <div class=\"bs-wizard-info text-center\">主从节点切换</div>\n                    </div>\n\n                    <div id=\"newSlaveUpdate${appDetail.appDesc.appId}\" class=\"col-sm-2 bs-wizard-step disabled\">\n                        <div class=\"text-center bs-wizard-stepnum\">5.新Slave配置更新</div>\n                        <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                        <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                        <div class=\"bs-wizard-info text-center\">更新配置并重启</div>\n                    </div>\n                    <div id=\"upgradeComplete${appDetail.appDesc.appId}\" class=\"col-sm-1 bs-wizard-step disabled\">\n                        <div class=\"text-center bs-wizard-stepnum\">6.升级完成</div>\n                        <div class=\"progress\"><div class=\"progress-bar\"></div></div>\n                        <a href=\"#\" class=\"bs-wizard-dot\"></a>\n                        <div class=\"bs-wizard-info text-center\">集群实例及版本信息</div>\n                    </div>\n                </div>\n                <form class=\"form-horizontal form-bordered form-row-stripped\" id=\"ns\">\n                    <div class=\"form-body\">\n                        <div class=\"form-group row\">\n                            <label class=\"col-form-label col-md-3 me-4 text-end\"> 应用ID: </label>\n                            <div class=\"col-md-8\">\n                                <label id=\"appId${appDetail.appDesc.appId}\" name=\"appId\" class=\"form-control\" >${appDetail.appDesc.appId}</label>\n                            </div>\n                        </div>\n                        <div class=\"form-group row\">\n                            <label class=\"col-form-label col-md-3 me-4 text-end\"> 当前版本号: </label>\n                            <div class=\"col-md-4\">\n                                <label id=\"appVersion${appDetail.appDesc.appId}\" name=\"appVersion\" class=\"form-control\" >${appDetail.appDesc.versionName}</label>\n                            </div>\n\n                        </div>\n                        <div class=\"form-group row\" hidden=\"hidden\" id=\"div${appDetail.appDesc.appId}\">\n                            <label class=\"col-form-label col-md-3 me-4 text-end\"> <a target=\"_blank\" href=\"${request.contextPath}/admin/app/index?appId=${appDetail.appDesc.appId}\">实例信息</a>: </label>\n                            <div class=\"col-md-6\">\n                                <textarea id=\"instanceInfo${appDetail.appDesc.appId}\" type=\"text\" rows=\"8\" class=\"form-control\" readonly></textarea>\n                            </div>\n                            <label id=\"instanceLog${appDetail.appDesc.appId}\" class=\"col-form-label col-md-2\"></label>\n                        </div>\n\n\n                        <div class=\"form-group row\">\n                            <#assign currentVersion = appDetail.appDesc.versionName?substring(0, appDetail.appDesc.versionName?lastIndexOf('.'))>\n                            <label class=\"col-form-label col-md-3 me-4 text-end\"> 可升级版本:</label>\n                            <div class=\"col-md-4\">\n                                <select id=\"versionSelect${appDetail.appDesc.appId}\" class=\"form-select select2_category\" title=\"请选择\" data-live-search-placeholder=\"搜索\" name=\"ip\" data-live-search=\"true\">\n                                    <#list redisVersionList as version>\n                                        <#if (version.name?indexOf(currentVersion)> -1) && (appDetail.appDesc.versionName != (version.name))>\n                                            <option versionid=\"${version.id!}\">${version.name!}</option>\n                                        </#if>\n                                    </#list>\n                                </select>\n                            </div>\n                             <label class=\"col-form-label col-md-auto ms-3\"><a class=\"button\" target=\"_blank\" onclick=\"configPreview(${appDetail.appDesc.appId})\"> Redis配置预览</a></label>&nbsp;&nbsp;&nbsp;\n                             <label class=\"col-form-label col-md-auto ms-3\"><a class=\"button\" target=\"_blank\" onclick=\"configContrast('${appDetail.appDesc.versionId}','${appDetail.appDesc.appId}')\"> 升级配置对比</a></label>\n                        </div>\n                        <div class=\"form-group row\">\n                            <label class=\"col-form-label col-md-3 me-4 text-end\"> 提示: </label>\n                            <div class=\"col-md-8\">\n                                <div class=\"form-control-static\">如果在升级过程中遇到错误警告，请登录服务器解决后再<b style=\"color:cornflowerblue\t\">继续</b>执行</div>\n                            </div>\n                        </div>\n                    </div>\n                </form>\n            </div>\n            <div class=\"modal-footer\">\n                <button type=\"button\" class=\"btn btn-primary\" data-bs-toggle=\"modal\" onclick=\"install(${appDetail.appDesc.appId})\"><span id=\"install${appDetail.appDesc.appId}\">一键升级</span></button>\n            </div>\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/totalstat/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <#include \"/manage/inc/backendResources.html\">\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"/manage/totalstat/totalList.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n<#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n\n  <!-- Control Sidebar -->\n  <aside class=\"control-sidebar control-sidebar-dark\">\n    <!-- Control sidebar content goes here -->\n  </aside>\n  <!-- /.control-sidebar -->\n</div>\n\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/totalstat/totalList.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">资源总览</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-bordered\">\n                    <tbody>\n                        <tr>\n                            <th scope=\"row\"><span style=\"font-weight:bold\">在线应用数</span></th>\n                            <td><a target=\"_blank\" href=\"${request.contextPath}/admin/app/list\">${totalRunningApps}</a></td>\n                            <th scope=\"row\"><span style=\"font-weight:bold\">在线实例数</span></th>\n                            <td>${totalRunningInstance}</td>\n                            <th scope=\"row\"><span style=\"font-weight:bold\">Redis版本数量</span></th>\n                            <td><a target=\"_blank\" href=\"${request.contextPath}/manage/app/resource/index?tab=redis\">${redisTypeCount}</a></td>\n                        </tr>\n                        <tr>\n                            <th scope=\"row\"><span style=\"font-weight:bold\">在线机器数量</span></th>\n                            <td><a target=\"_blank\" href=\"${request.contextPath}/manage/machine/index?tabTag=machine\">${totalMachineCount}</a></td>\n                            <th scope=\"row\"><span style=\"font-weight:bold\">Redis机器数量</span></th>\n                            <td><a target=\"_blank\" href=\"${request.contextPath}/manage/machine/index?tabTag=machine&type=0\">${machineRedisCount}</a></td>\n                            <th scope=\"row\"><span style=\"font-weight:bold\">RedisTool机器数量</span></th>\n                            <td><a target=\"_blank\" href=\"${request.contextPath}/manage/machine/index?tabTag=machine&type=2\">${machineRedisToolCount}</a></td>\n                        </tr>\n                        <tr>\n                            <th scope=\"row\"><span style=\"font-weight:bold\">机器总内存空间</span></th>\n                            <#list machineStatsVoList as statsVo>\n                                <#if statsVo_index == 0>\n                                    <td>${(statsVo.totalMachineMem/1024)?string('#.#')}G</td>\n                                    <th scope=\"row\"><span style=\"font-weight:bold\">总分配内存(机器/实例)</span></th>\n                                    <td>${((statsVo.totalMachineMem-statsVo.totalMachineFreeMem)/1024)?string('#.#')}G\n                                        /${(statsVo.totalInstanceMaxMem/1024/1024/1024)?string('#.#')}G\n                                    </td>\n                                    <th scope=\"row\"><span style=\"font-weight:bold\">实例总使用内存</span></th>\n                                    <td>${(statsVo.totalInstanceUsedMem/1024/1024/1024)?string('#.#')}G</td>\n                                </#if>\n                            </#list>\n                        </tr>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">分布统计</h3>\n            </div>\n            <div class=\"card-body\">\n                <div class=\"col-12\">\n                    <div class=\"row\">\n                        <div class=\"col-lg-6\">\n                            <div class=\"card\">\n                                <div class=\"card-body\">\n                                    <div id=\"redisDistributeContainer\" class=\"echart p-4\" style=\"min-height: 400px;\">\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"col-lg-6\">\n                            <div class=\"card\">\n                                <div class=\"card-body\">\n                                    <div id=\"roomDistributeContainer\" class=\"echart p-4\" style=\"min-height: 400px;\">\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"col-lg-6\">\n                            <div class=\"card\">\n                                <div class=\"card-body\">\n                                    <div id=\"machineMemoryDistributeContainer\"class=\"echart p-4\"  style=\"min-height: 400px;\">\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"col-lg-6\">\n                            <div class=\"card\">\n                                <div class=\"card-body\">\n                                    <div id=\"maxMemoryDistributeContainer\"class=\"echart p-4\"  style=\"min-height: 400px;\">\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">内存统计</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-bordered\">\n                    <thead>\n                    <tr>\n                        <th scope=\"col\">机房</th>\n                        <th scope=\"col\">机器内存(分配内存/总内存)</th>\n                        <th scope=\"col\">实例内存(使用内存/分配内存)</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                        <#list machineStatsVoList as machineStatsVo>\n                        <tr>\n                            <th scope=\"row\">${machineStatsVo.machineRoom}</th>\n                            <td>\n                                <div class=\"progress progress-fs-1\">\n                                    <#if (machineStatsVo.machineMemUsedRatio >= 70.00 && machineStatsVo.machineMemUsedRatio <= 90.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-warning'>\n                                    <#elseif (machineStatsVo.machineMemUsedRatio >= 90.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-danger'>\n                                    <#else>\n                                        <#assign memUsedProgressBarStatus = 'bg-success'>\n                                    </#if>\n                                    <div class=\"progress-bar ${memUsedProgressBarStatus}\"\n                                         role=\"progressbar\" aria-valuenow=\"${machineStatsVo.machineMemUsedRatio}\" aria-valuemax=\"100\"\n                                         aria-valuemin=\"0\" style=\"width: ${machineStatsVo.machineMemUsedRatio}%;overflow: visible;\">\n                                        <span style=\"color: #000000; margin-bottom: 0\">\n                                            ${((machineStatsVo.totalMachineMem-machineStatsVo.totalMachineFreeMem)/1024)?string('#.##')}G&nbsp;&nbsp;Used/${(machineStatsVo.totalMachineMem/1024)?string('#.##')}G&nbsp;&nbsp;Total\n                                        </span>\n                                    </div>\n                                </div>\n                            </td>\n                            <td>\n                                <div class=\"progress progress-fs-1\">\n                                    <#if machineStatsVo.instanceMemUsedRatio?? && (machineStatsVo.instanceMemUsedRatio >= 70.00 && machineStatsVo.instanceMemUsedRatio <= 90.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-warning'>\n                                    <#elseif machineStatsVo.instanceMemUsedRatio?? && (machineStatsVo.instanceMemUsedRatio >= 90.00)>\n                                        <#assign memUsedProgressBarStatus = 'bg-danger'>\n                                    <#else>\n                                        <#assign memUsedProgressBarStatus = 'bg-success'>\n                                    </#if>\n                                    <div class=\"progress-bar ${memUsedProgressBarStatus}\"\n                                         role=\"progressbar\" aria-valuenow=\"${machineStatsVo.instanceMemUsedRatio}\" aria-valuemax=\"100\"\n                                         aria-valuemin=\"0\" style=\"width: ${machineStatsVo.instanceMemUsedRatio}%;overflow: visible;\">\n                                        <span style=\"color: #000000; margin-bottom: 0\">\n                                            ${(machineStatsVo.totalInstanceUsedMem/1024/1024/1024)?string('#.##')}G&nbsp;&nbsp;Used/${(machineStatsVo.totalInstanceMaxMem/1024/1024/1024)?string('#.##')}G&nbsp;&nbsp;Total\n                                        </span>\n                                    </div>\n                                </div>\n                            </td>\n                        </tr>\n                        </#list>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">调度统计</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-bordered\">\n                    <tbody>\n                        <tr>\n                            <th scope=\"row\">trigger总数:</th>\n                            <td>\n                                <#if (triggerTotalCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/quartz/list\">${triggerTotalCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">waiting个数:</td>\n                            <td>\n                                <#if (triggerWaitingCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/quartz/list?triggerState=WAITING\">${triggerWaitingCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">error个数:</td>\n                            <td>\n                                <#if (triggerErrorCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/quartz/list?triggerState=ERROR\">${triggerErrorCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">paused个数:</td>\n                            <td>\n                                <#if (triggerPausedCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/quartz/list?triggerState=PAUSED\">${triggerPausedCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                        </tr>\n                        <tr>\n                            <th scope=\"row\">acquired个数:</td>\n                            <td>\n                                <#if (triggerAcquiredCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/quartz/list?triggerState=ACQUIRED\">${triggerAcquiredCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">blocked个数</td>\n                            <td>\n                                <#if (triggerBlockedCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/quartz/list?triggerState=BLOCKED\">${triggerBlockedCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">misfireCount个数:</td>\n                            <td>\n                                <#if (misfireCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/quartz/list?misFireState=1\">${misfireCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                        </tr>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"col-12\">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                <h3 class=\"card-title\">任务统计</h3>\n            </div>\n            <div class=\"card-body table-responsive\">\n                <table class=\"table table-bordered\">\n                    <tbody>\n                        <tr>\n                            <th scope=\"row\">任务总数:</td>\n                            <td>\n                                <#if (totalTaskCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/task/list\">${totalTaskCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">新任务数:</td>\n                            <td>\n                                <#if (newTaskCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/task/list?status=0\">${newTaskCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">运行中任务数:</td>\n                            <td>\n                                <#if (runningTaskCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/task/list?status=1\">${runningTaskCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">中断任务数:</td>\n                            <td>\n                                <#if (abortTaskCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/task/list?status=2\">${abortTaskCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                            <th scope=\"row\">成功任务数:</td>\n                            <td>\n                                <#if (successTaskCount > 0)>\n                                    <a target=\"_blank\" href=\"${request.contextPath}/manage/task/list?status=4\">${successTaskCount}</a>\n                                <#else>\n                                    0\n                                </#if>\n                            </td>\n                        </tr>\n                    </tbody>\n                </table>\n            </div>\n        </div>\n    </div>\n</div>\n\n\n<script>\n    function setFigureOption(title, dataArr){\n        var length = dataArr.length;\n        var series = [];\n        var labels = [];\n        var dataSeries = [];\n        for (var i = 0; i < length; i++) {\n            var data = dataArr[i];\n            labels.push(data.param);\n            series.push(data.count);\n            var dataPoint = {\n                name: data.param + \" : \" + data.count,\n                value: data.count,\n            };\n            dataSeries.push(dataPoint);\n        }\n        var options = {\n            title: {\n                text: title,\n                left: 'center'\n            },\n            tooltip: {\n                trigger: 'item'\n            },\n            legend: {\n                orient: 'vertical',\n                left: 'right'\n            },\n            series: [{\n                type: 'pie',\n                radius: '65%',\n                data: dataSeries,\n                emphasis: {\n                    itemStyle: {\n                        shadowBlur: 10,\n                        shadowOffsetX: 0,\n                        shadowColor: 'rgba(0, 0, 0, 0.5)'\n                    }\n                }\n            }]\n        };\n        return options;\n    }\n\n    function initFigure(id, title, dataArr){\n        echarts.init(document.querySelector(\"#\" + id)).setOption(setFigureOption(title, dataArr));\n    };\n\n    document.addEventListener(\"DOMContentLoaded\", () => {initFigure('machineMemoryDistributeContainer', '机器内存使用率', ${machineMemoryDistributeList})});\n    document.addEventListener(\"DOMContentLoaded\", () => {initFigure('maxMemoryDistributeContainer','机器内存分配率',${maxMemoryDistributeList})});\n    document.addEventListener(\"DOMContentLoaded\", () => {initFigure('redisDistributeContainer','应用Redis版本分布', ${redisDistributeList})});\n    document.addEventListener(\"DOMContentLoaded\", () => {initFigure('roomDistributeContainer', '机房机器分布',${roomDistributeList})});\n\n</script>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/user/addBiz.html",
    "content": "<div class=\"\">\n  <div class=\"\">\n    <#if biz??>\n      <#assign bizId = biz.id!>\n    <#else>\n      <#assign bizId = ''>\n    </#if>\n    <div id=\"addBizModal${bizId!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n      <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n          <div class=\"modal-header\">\n            <h4 class=\"modal-title\">管理业务组</h4>\n            <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n          </div>\n\n          <form class=\"form-horizontal form-bordered form-row-stripped\">\n            <div class=\"modal-body\">\n              <div class=\"row\">\n                <!-- 控件开始 -->\n                <div class=\"col-md-12\">\n                  <!-- form-body开始 -->\n                  <div class=\"form-body\">\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        名称:\n                      </label>\n                      <div class=\"col-md-7\">\n                        <#if biz??>\n                          <#assign nameValue = biz.name!>\n                        <#else>\n                          <#assign nameValue = ''>\n                        </#if>\n                        <input type=\"text\" name=\"name\" id=\"biz_name${bizId!}\"\n                               value=\"${nameValue!}\" placeholder=\"业务组名\"\n                               class=\"form-control\" />\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        描述:\n                      </label>\n                      <div class=\"col-md-7\">\n                        <#if biz??>\n                          <#assign bizDescValue = biz.bizDesc!>\n                        <#else>\n                          <#assign bizDescValue = ''>\n                        </#if>\n                        <input type=\"text\" name=\"desc\" id=\"biz_desc${bizId!}\"\n                               value=\"${bizDescValue!}\" placeholder=\"业务组描述\"\n                               class=\"form-control\" />\n                      </div>\n                    </div>\n\n                    <input type=\"hidden\" id=\"bizId${bizId!}\" name=\"bizId\" value=\"${bizId!}\"/>\n                  </div>\n                  <!-- form-body 结束 -->\n                </div>\n                <div id=\"info{bizId!}\"></div>\n                <!-- 控件结束 -->\n              </div>\n            </div>\n\n            <div class=\"modal-footer\">\n              <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n              <button type=\"button\" id=\"bizBtn${bizId!}\" class=\"btn btn-danger\" onclick=\"saveOrUpdateBiz('${bizId!}', '${request.contextPath}')\">Ok</button>\n            </div>\n\n          </form>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/user/addUser.html",
    "content": "  <#if user??>\n    <#assign userIdValue = user.id>\n    <#assign nameValue = user.name!>\n    <#assign chNameValue = user.chName!>\n    <#assign emailValue = user.email!>\n    <#assign mobileValue = user.mobile!>\n    <#assign weChatValue = (user.weChat!)>\n    <#assign companyValue = (user.company!)>\n    <#assign purposeValue = (user.purpose!)>\n    <#if user.type??>\n      <#assign userTypeValue = (user.type)>\n    </#if>\n    <#if user.bizId??>\n      <#assign userBizValue = (user.bizId)>\n    </#if>\n    <#if user.isAlert??>\n      <#assign isAlertValue = (user.isAlert)>\n    </#if>\n  <#else>\n    <#assign userIdValue = ''>\n    <#assign nameValue = ''>\n    <#assign chNameValue = ''>\n    <#assign emailValue = ''>\n    <#assign mobileValue = ''>\n    <#assign weChatValue = ''>\n    <#assign companyValue = ''>\n    <#assign purposeValue = ''>\n  </#if>\n\n  <div id=\"addUserModal${userIdValue!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n      <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n          <div class=\"modal-header\">\n            <h4 class=\"modal-title\">管理用户</h4>\n            <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n          </div>\n\n          <form class=\"form-horizontal form-bordered form-row-stripped\">\n            <div class=\"modal-body\">\n              <div class=\"row\">\n                <!-- 控件开始 -->\n                <div class=\"col-md-12\">\n                  <!-- form-body开始 -->\n                  <div class=\"form-body\">\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        域账户名:\n                      </label>\n                      <div class=\"col-md-7\">\n                        <input type=\"text\" name=\"name\" id=\"user_name${userIdValue!}\"\n                             value=\"${nameValue!}\" placeholder=\"域账户名(邮箱前缀)\"\n                             class=\"form-control\" />\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        中文名:\n                      </label>\n                      <div class=\"col-md-7\">\n                        <input type=\"text\" name=\"chName\" id=\"chName${userIdValue!}\"\n                             value=\"${chNameValue!}\" placeholder=\"中文名\"\n                             class=\"form-control\" />\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        邮箱:\n                      </label>\n                      <div class=\"col-md-7\">\n\n                        <input type=\"text\" name=\"email\" id=\"email${userIdValue!}\"\n                             value=\"${emailValue!}\" placeholder=\"邮箱\"\n                             class=\"form-control\" />\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        手机:\n                      </label>\n                      <div class=\"col-md-7\">\n\n                        <input type=\"text\" name=\"mobile\" id=\"mobile${userIdValue!}\"\n                             value=\"${mobileValue!}\" placeholder=\"手机\"\n                             class=\"form-control\" />\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        微信:\n                      </label>\n                      <div class=\"col-md-7\">\n\n                        <input type=\"text\" name=\"weChat\" id=\"weChat${userIdValue!}\"\n                             value=\"${weChatValue!}\" placeholder=\"微信\"\n                             class=\"form-control\" />\n                      </div>\n                    </div>\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        类型:\n                      </label>\n                      <div class=\"col-md-7\">\n                        <select name=\"type\" id=\"type${userIdValue!}\" class=\"form-select select2_category\">\n                          <option value=\"0\" <#if userTypeValue?? && (userTypeValue == 0)>selected=\"selected\"</#if>>\n                          管理员\n                          </option>\n                          <option value=\"2\" <#if userTypeValue?? && (userTypeValue == 2)>selected=\"selected\"</#if>>\n                          普通用户\n                          </option>\n                        </select>\n                      </div>\n                    </div>\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        是否收报警:\n                      </label>\n                      <div class=\"col-md-7\">\n                        <select name=\"isAlert\" id=\"isAlert${userIdValue!}\" class=\"form-select select2_category\">\n                          <option value=\"1\" <#if isAlertValue?? && (isAlertValue == 1)>selected</#if>>\n                          是\n                          </option>\n                          <option value=\"0\" <#if isAlertValue?? && (isAlertValue == 0)>selected</#if>>\n                          否\n                          </option>\n                        </select>\n                      </div>\n                    </div>\n\n                    <#if bizList?? && bizList?size gt 0>\n                      <div class=\"form-group row\">\n                        <label class=\"col-form-label col-md-3 text-end\">\n                          所属业务组:\n                        </label>\n                        <div class=\"col-md-7\">\n                          <select id=\"bizId${userIdValue!}\" name=\"bizId\" class=\"form-select select2_category\">\n                            <option value=\"\">业务组</option>\n                            <#list bizList as biz>\n                              <option value=\"${biz.id}\" <#if userBizValue?? && (biz.id == userBizValue)>selected</#if>>${biz.name}</option>\n                            </#list>\n                            </select>\n                        </div>\n                      </div>\n                    </#if>\n                    <input type=\"hidden\" id=\"userId${userIdValue!}\" name=\"userId\" value=\"${userIdValue!}\"/>\n                  </div>\n                  <!-- form-body 结束 -->\n                </div>\n                <div id=\"info{userIdValue!}\"></div>\n                <!-- 控件结束 -->\n              </div>\n            </div>\n            <div class=\"modal-footer\">\n              <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n              <button type=\"button\" id=\"userBtn${userIdValue!}\" class=\"btn btn-danger\" onclick=\"saveOrUpdateUser('${userIdValue!}', '${request.contextPath}')\">Ok</button>\n            </div>\n          </form>\n\n        </div>\n      </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/user/list.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n\n  <title>CacheCloud管理后台</title>\n  <meta content=\"\" name=\"description\">\n  <meta content=\"\" name=\"keywords\">\n  <#include \"/manage/inc/backendResources.html\">\n  <script src=\"${request.contextPath}/assets/js/custom/userManage.js\" type=\"text/javascript\"></script>\n</head>\n\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n\n<div class=\"wrapper\">\n  <#include \"/manage/inc/head.html\">\n  <#include \"/manage/inc/left.html\">\n\n  <div class=\"content-wrapper\">\n    <!-- Main content -->\n    <section class=\"content pt-3\">\n      <div class=\"container-fluid\">\n        <#include \"userList.html\">\n        <#include \"addUser.html\">\n        <#include \"overUser.html\">\n        <#include \"addBiz.html\">\n      </div>\n    </section>\n  </div><!-- End #main -->\n\n  <#include \"/manage/inc/footer.html\">\n\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/user/overUser.html",
    "content": "<div id=\"overUserModal\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n  <div class=\"modal-dialog\">\n    <div class=\"modal-content\">\n      <div class=\"modal-header\">\n        <h4 class=\"modal-title\">接手用户应用权限</h4>\n        <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n      </div>\n\n      <form class=\"form-horizontal form-bordered form-row-stripped\">\n        <div class=\"modal-body\">\n          <div class=\"row\">\n            <!-- 控件开始 -->\n            <div class=\"col-md-12\">\n              <!-- form-body开始 -->\n              <div class=\"form-body\">\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-4 text-end\">\n                    待移交用户:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"toRemoveUser\" id=\"toRemoveUser\"\n                           placeholder=\"域账户名(邮箱前缀)\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n\n                <div class=\"form-group row\">\n                  <label class=\"col-form-label col-md-4 text-end\">\n                    接手用户:\n                  </label>\n                  <div class=\"col-md-7\">\n                    <input type=\"text\" name=\"toChargeUser\" id=\"toChargeUser\"\n                           placeholder=\"域账户名(邮箱前缀)\"\n                           class=\"form-control\" />\n                  </div>\n                </div>\n              </div>\n              <!-- form-body 结束 -->\n            </div>\n            <div id=\"takeoverInfo\"></div>\n            <!-- 控件结束 -->\n          </div>\n        </div>\n      </form>\n\n      <div class=\"modal-footer\">\n        <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n        <button type=\"button\" id=\"overUserBtn\" class=\"btn btn-danger\" onclick=\"takeOverUser('${request.contextPath}')\">Ok</button>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/user/updateUser.html",
    "content": "  <#if headUser??>\n    <#assign userIdValue = headUser.id>\n    <#assign nameValue = headUser.name!>\n    <#assign chNameValue = headUser.chName!>\n    <#assign emailValue = headUser.email!>\n    <#assign mobileValue = headUser.mobile!>\n  <#else>\n    <#assign userIdValue = ''>\n    <#assign nameValue = ''>\n    <#assign chNameValue = ''>\n    <#assign emailValue = ''>\n    <#assign mobileValue = ''>\n  </#if>\n\n  <#if headUser??>\n    <#assign weChatValue = (headUser.weChat!)>\n    <#assign companyValue = (headUser.company!)>\n    <#assign purposeValue = (headUser.purpose!)>\n  <#else>\n    <#assign weChatValue = ''>\n    <#assign companyValue = ''>\n    <#assign purposeValue = ''>\n  </#if>\n\n  <#if headUser??>\n    <#if headUser.type??>\n      <#assign userTypeValue = (headUser.type)>\n    </#if>\n    <#if headUser.bizId??>\n      <#assign userBizValue = (headUser.bizId)>\n    </#if>\n    <#if headUser.isAlert??>\n      <#assign isAlertValue = (headUser.isAlert)>\n    </#if>\n  <#else>\n  </#if>\n  <div id=\"updateUserModal${userIdValue!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n    <div class=\"modal-dialog\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\">管理用户</h4>\n          <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n        </div>\n\n        <form class=\"form-horizontal form-bordered form-row-stripped\">\n          <div class=\"modal-body\">\n            <div class=\"row\">\n              <!-- 控件开始 -->\n              <div class=\"col-md-12\">\n                <!-- form-body开始 -->\n                <div class=\"form-body\">\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3 text-end\">\n                      域账户名:\n                    </label>\n                    <div class=\"col-md-7\">\n                      <input type=\"text\" name=\"name\" id=\"user_name${userIdValue!}\"\n                           value=\"${nameValue!}\" placeholder=\"域账户名(邮箱前缀)\"\n                           class=\"form-control\" />\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3 text-end\">\n                      中文名:\n                    </label>\n                    <div class=\"col-md-7\">\n                      <input type=\"text\" name=\"chName\" id=\"chName${userIdValue!}\"\n                           value=\"${chNameValue!}\" placeholder=\"中文名\"\n                           class=\"form-control\" />\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3 text-end\">\n                      邮箱:\n                    </label>\n                    <div class=\"col-md-7\">\n\n                      <input type=\"text\" name=\"email\" id=\"email${userIdValue!}\"\n                           value=\"${emailValue!}\" placeholder=\"邮箱\"\n                           class=\"form-control\" />\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3 text-end\">\n                      手机:\n                    </label>\n                    <div class=\"col-md-7\">\n\n                      <input type=\"text\" name=\"mobile\" id=\"mobile${userIdValue!}\"\n                           value=\"${mobileValue!}\" placeholder=\"手机\"\n                           class=\"form-control\" />\n                    </div>\n                  </div>\n\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3 text-end\">\n                      微信:\n                    </label>\n                    <div class=\"col-md-7\">\n\n                      <input type=\"text\" name=\"weChat\" id=\"weChat${userIdValue!}\"\n                           value=\"${weChatValue!}\" placeholder=\"微信\"\n                           class=\"form-control\" />\n                    </div>\n                  </div>\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3 text-end\">\n                      类型:\n                    </label>\n                    <div class=\"col-md-7\">\n                      <select name=\"type\" id=\"type${userIdValue!}\" class=\"form-select select2_category\">\n                        <option value=\"0\" <#if userTypeValue?? && (userTypeValue == 0)>selected=\"selected\"</#if>>\n                        管理员\n                        </option>\n                        <option value=\"2\" <#if userTypeValue?? && (userTypeValue == 2)>selected=\"selected\"</#if>>\n                        普通用户\n                        </option>\n                      </select>\n                    </div>\n                  </div>\n                  <div class=\"form-group row\">\n                    <label class=\"col-form-label col-md-3 text-end\">\n                      是否收报警:\n                    </label>\n                    <div class=\"col-md-7\">\n                      <select name=\"isAlert\" id=\"isAlert${userIdValue!}\" class=\"form-select select2_category\">\n                        <option value=\"1\" <#if isAlertValue?? && (isAlertValue == 1)>selected</#if>>\n                        是\n                        </option>\n                        <option value=\"0\" <#if isAlertValue?? && (isAlertValue == 0)>selected</#if>>\n                        否\n                        </option>\n                      </select>\n                    </div>\n                  </div>\n\n                  <#if bizList?? && bizList?size gt 0>\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        所属业务组:\n                      </label>\n                      <div class=\"col-md-7\">\n                        <select id=\"bizId${userIdValue!}\" name=\"bizId\" class=\"form-select select2_category\">\n                          <option value=\"\">业务组</option>\n                          <#list bizList as biz>\n                            <option value=\"${biz.id}\" <#if userBizValue?? && (biz.id == userBizValue)>selected</#if>>${biz.name}</option>\n                          </#list>\n                          </select>\n                      </div>\n                    </div>\n                  <#else>\n                    <input type=\"hidden\" id=\"bizId${userIdValue!}\" name=\"bizId\" value=\"${userBizValue!}\"/>\n                  </#if>\n                  <input type=\"hidden\" id=\"userId${userIdValue!}\" name=\"userId\" value=\"${userIdValue!}\"/>\n                </div>\n                <!-- form-body 结束 -->\n              </div>\n              <div id=\"info{userIdValue!}\"></div>\n              <!-- 控件结束 -->\n            </div>\n          </div>\n\n          <div class=\"modal-footer\">\n            <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\" >Close</button>\n            <button type=\"button\" id=\"userBtn${userIdValue!}\" class=\"btn btn-danger\" onclick=\"saveOrUpdateUser('${userIdValue!}', '${request.contextPath}')\">Ok</button>\n          </div>\n        </form>\n\n      </div>\n    </div>\n  </div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/user/updateUserPwd.html",
    "content": "<div class=\"\">\n  <div class=\"\">\n    <#if user??>\n      <#assign userId = user.id!>\n    <#else>\n      <#assign userId = ''>\n    </#if>\n    <div id=\"updateUserPwdModal${userId!}\" class=\"modal fade\" tabindex=\"-1\" data-width=\"400\">\n      <div class=\"modal-dialog\">\n        <div class=\"modal-content\">\n          <div class=\"modal-header\">\n            <h4 class=\"modal-title\">管理用户</h4>\n            <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-hidden=\"true\"></button>\n          </div>\n\n          <form class=\"form-horizontal form-bordered form-row-stripped\">\n            <div class=\"modal-body\">\n              <div class=\"row\">\n                <!-- 控件开始 -->\n                <div class=\"col-md-12\">\n                  <!-- form-body开始 -->\n                  <div class=\"form-body\">\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        域账户名:\n                      </label>\n                      <div class=\"col-md-5\">\n                        <#if user??>\n                          <#assign nameValue = user.name!>\n                        <#else>\n                          <#assign nameValue = ''>\n                        </#if>\n                      <input type=\"text\" name=\"name\" id=\"updateUserPwdName${userId!}\"\n                             value=\"${nameValue}\" placeholder=\"域账户名(邮箱前缀)\"\n                             class=\"form-control\" disabled/>\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        密码:\n                      </label>\n                      <div class=\"col-md-5\">\n                        <input type=\"password\" name=\"password\" id=\"updateUserPwdPassword${userId!}\" placeholder=\"输入密码\"\n                               class=\"form-control\" onchange=\"checkPassword('${userId!}')\"/>\n                        <span class=\"help-block\">密码中必须包含字母、数字，至少8个字符</span>\n                      </div>\n                    </div>\n\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        确认密码:\n                      </label>\n                      <div class=\"col-md-5\">\n                        <input type=\"password\" name=\"password0\" id=\"updateUserPwdPassword0${userId!}\" placeholder=\"再次输入密码\"\n                               class=\"form-control\" onchange=\"checkConfirmPassword('${userId!}')\"/>\n                      </div>\n                    </div>\n\n                    <input type=\"hidden\" id=\"updateUserPwdUserId{userId!}\" name=\"userId\" value=\"${userId!}\"/>\n                  </div>\n                  <!-- form-body 结束 -->\n                </div>\n                <div id=\"updateUserPwdInfo{userId!}\"></div>\n                <!-- 控件结束 -->\n              </div>\n            </div>\n\n            <div class=\"modal-footer\">\n              <button type=\"button\" data-bs-dismiss=\"modal\" class=\"btn btn-secondary\">Close</button>\n              <button type=\"button\" id=\"updateUserPwdBtn${userId!}\" class=\"btn btn-danger\" onclick=\"updateUserPwd('${userId!}', '${request.contextPath}')\">Ok</button>\n            </div>\n\n          </form>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/manage/user/userList.html",
    "content": "<div class=\"row\">\n    <div class=\"col-12\">\n        <div class=\"card top-selling overflow-auto\">\n            <div class=\"card-body pb-0\">\n                <div class=\"tabbable-custom\">\n                    <nav class=\"nav\">\n                        <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                            <li id=\"machine\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/user/list?tabId=1\">\n                                <a class=\"nav-link d-flex <#if tabId?? && (tabId == 1)>active</#if>\" href=\"?tabId=1\">用户管理</a>\n                            </li>\n                            <li id=\"room\" class=\"nav-item\" data-url=\"${request.contextPath}/manage/user/list?tabId=2\">\n                                <a class=\"nav-link d-flex <#if tabId?? && (tabId == 2)>active</#if>\" href=\"?tabId=2\">业务组管理</a>\n                            </li>\n                        </ul>\n                    </nav>\n                    <br>\n\n                    <div class=\"btn-group\" style=\"float:right\">\n                        <form class=\"form-inline\" role=\"form\">\n                            <input name=\"tabId\" id=\"tabId\" value=\"${tabId!}\" type=\"hidden\"/>\n                        </form>\n                    </div>\n                </div>\n\n                <div class=\"row\">\n                    <div class=\"col-md-12\">\n                        <div class=\"portlet box light-grey\" id=\"userIndex\">\n                            <div class=\"row\">\n                                <div class=\"col-md-12\">\n                                    <div class=\"portlet-body\">\n                                        <div class=\"table-toolbar\">\n                                            <div class=\"row mb-2\">\n                                                <div class=\"col-md-5\">\n                                                    <div class=\"row col-md-12\">\n                                                        <button id=\"sample_editable_1_new\" class=\"btn btn-success col-auto\" data-bs-target=\"#addUserModal\" data-bs-toggle=\"modal\">\n                                                            添加新用户 <i class=\"bi bi-plus\"></i>\n                                                        </button>\n                                                        <button id=\"over_user_id\" class=\"btn btn-success offset-md-1 col-auto\" data-bs-target=\"#overUserModal\" data-bs-toggle=\"modal\">\n                                                            接手用户 <i class=\"bi bi-hand-index-thumb\"></i>\n                                                        </button>\n                                                    </div>\n                                                </div>\n                                                <div class=\"btn-group offset-md-1 col-md-6 float-end\">\n                                                    <form action=\"${request.contextPath}/manage/user/list\" method=\"post\" class=\"row align-items-center float-end\">\n                                                        <label class=\"col-form-label col-auto\">\n                                                            用户名:\n                                                        </label>\n                                                        <div class=\"col-md-3\">\n                                                            <input type=\"text\" name=\"searchChName\" class=\"form-control\" id=\"searchChName\" value=\"${searchChName!}\" placeholder=\"中文名\"/>\n                                                        </div>\n                                                        <label class=\"col-form-label col-auto\">\n                                                            业务组:\n                                                        </label>\n                                                        <div class=\"col-md-4\">\n                                                            <select id=\"searchBizName\" name=\"searchBizName\" class=\"form-select select2_category\">\n                                                                <option value=\"\">业务组</option>\n                                                                <#list bizList as biz>\n                                                                    <option value=\"${biz.name!}\" <#if searchBizName?? && (biz.name == searchBizName)>selected</#if>>${biz.name!}</option>\n                                                                </#list>\n                                                            </select>\n                                                        </div>\n                                                        <button type=\"submit\" class=\"btn btn-primary col-auto\">查询</button>\n                                                    </form>\n                                                </div>\n                                            </div>\n                                        </div>\n                                        <div class=\"table-responsive\">\n                                            <table class=\"table table-striped table-bordered table-hover\" id=\"tableDataList\" style=\"white-space: nowrap\">\n                                                <thead>\n                                                <tr>\n                                                    <th>id</th>\n                                                    <th>域账户</th>\n                                                    <th>中文名</th>\n                                                    <th>邮箱</th>\n                                                    <th>手机</th>\n                                                    <th>微信</th>\n                                                    <th>是否报警</th>\n                                                    <th>类型</th>\n                                                    <th>注册时间</th>\n                                                    <th>业务组</th>\n                                                    <th>操作</th>\n                                                </tr>\n                                                </thead>\n                                                <tbody>\n                                                <#if users??>\n                                                    <#list users as user>\n                                                        <tr>\n                                                            <td>${user.id!}</td>\n                                                            <td>${user.name!}</td>\n                                                            <td>${user.chName!}</td>\n                                                            <td>${user.email!}</td>\n                                                            <td>${user.mobile!}</td>\n                                                            <td>${user.weChat!}</td>\n                                                            <td>\n                                                                <#if (user.isAlert==0)>否</#if>\n                                                                <#if (user.isAlert==1)>是</#if>\n                                                            </td>\n                                                            <td>\n                                                                <#if (user.type == 0)>管理员\n                                                                <#elseif (user.type == 2)>普通用户\n                                                                </#if>\n                                                            </td>\n                                                            <td>\n                                                                <#if user.registerTime??>\n                                                                    ${user.registerTime?string(\"yyyy-MM-dd HH:mm:ss\")}\n                                                                </#if>\n                                                            </td>\n                                                            <td>${user.bizName!}</td>\n                                                            <td>\n                                                                <a href=\"javascript:void(0);\" data-bs-target=\"#addUserModal${user.id!}\" data-bs-toggle=\"modal\">[修改]</a>\n                                                                <#if pwdswitch?? && pwdswitch>\n                                                                    <a href=\"javascript:void(0);\" data-bs-target=\"#updateUserPwdModal${user.id!}\" data-bs-toggle=\"modal\">[修改密码]</a>\n                                                                </#if>\n                                                                <a onclick=\"if(window.confirm('确认要删除该用户吗?!')){return true;}else{return false;}\" href=\"${request.contextPath}/manage/user/delete?userId=${user.id!}\">[删除]</a>\n                                                            </td>\n                                                        </tr>\n                                                    </#list>\n                                                </#if>\n                                                </tbody>\n                                            </table>\n                                        </div>\n                                    </div>\n                                </div>\n                            </div>\n                            <#if users??>\n                                <#list users as user>\n                                    <#include \"addUser.html\">\n                                    <#include \"updateUserPwd.html\">\n                                </#list>\n                            </#if>\n                        </div>\n\n\n                        <div class=\"portlet box light-grey\" id=\"bizIndex\">\n                            <div class=\"row\">\n                                <div class=\"row mb-2\">\n                                    <div class=\"btn-group col-auto\">\n                                        <button id=\"add_biz_id\" class=\"btn btn-success\" data-bs-target=\"#addBizModal\" data-bs-toggle=\"modal\">\n                                            添加业务组 <i class=\"bi bi-plus\"></i>\n                                        </button>\n                                    </div>\n                                </div>\n                                <div class=\"table-responsive\">\n                                    <table class=\"table table-striped table-bordered table-hover\" id=\"bizTableDataList\">\n                                        <thead>\n                                        <tr>\n                                            <th>id</th>\n                                            <th>名称</th>\n                                            <th>描述</th>\n                                            <th>操作</th>\n                                        </tr>\n                                        </thead>\n                                        <tbody>\n                                        <#list bizList as biz>\n                                            <tr>\n                                                <td>${biz.id!}</td>\n                                                <td>${biz.name!}</td>\n                                                <td>${biz.bizDesc!}</td>\n                                                <td>\n                                                    <a href=\"javascript:void(0);\" data-bs-target=\"#addBizModal${biz.id!}\" data-bs-toggle=\"modal\">[修改]</a>\n                                                    <a onclick=\"if(window.confirm('确认要删除该业务组吗?!')){return true;}else{return false;}\" href=\"${request.contextPath}/manage/user/biz/delete?bizId=${biz.id!}&tabId=2\">[删除]</a>\n                                                </td>\n                                            </tr>\n                                        </#list>\n                                        </tbody>\n                                    </table>\n                                </div>\n                                <#list bizList as biz>\n                                    <#include \"addBiz.html\">\n                                </#list>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n<script src=\"${request.contextPath}/assets/vendor/jquery/jquery.md5.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/paginator/bootstrap-paginator.js\"></script>\n<script type=\"text/javascript\">\n    var TableManaged = function () {\n        return {\n            //main function to initiate the module\n            init: function () {\n                $('#tableDataList').dataTable({\n                    \"searching\": true,\n                    \"lengthChange\": false,\n                    \"pageLength\": 15,\n                    \"language\": {\n                        \"lengthMenu\": \"Display _MENU_ records\",\n                        \"paginate\": {\n                            \"previous\": \"<\",\n                            \"next\": \">\"\n                        },\n                        \"info\": \"共_PAGES_页,_TOTAL_条\",\n                        \"infoFiltered\": \"\",\n                        \"infoEmpty\":\"共0页,0条\",\n                        \"zeroRecords\": \"没有找到符合条件的数据\",\n                    }\n                });\n                $('#tableDataList_wrapper>div:first-child').css(\"display\", \"none\");\n            }\n        };\n    }();\n\n    $(function () {\n        var tabVal = $('#tabId').val();\n        if (tabVal == 1) {\n            $('#userIndex').removeAttr(\"hidden\");\n            $('#bizIndex').attr(\"hidden\", \"hidden\");\n        } else if (tabVal == 2) {\n            $('#userIndex').attr(\"hidden\", \"hidden\");\n            $('#bizIndex').removeAttr(\"hidden\");\n        }\n        TableManaged.init();\n    })\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/migrate/checkData.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>采样校验源数据和目标数据</title>\n  <#include '/inc/frontResources.html'>\n</head>\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n  校验程序已启动<br/>\n  <#if checkDataResultList?? && (checkDataResultList?size > 0)>\n    <#list checkDataResultList as line>\n      <font color=\"white\">${line!}</font><br/>\n    </#list>\n  </#if>\n  <br/>\n  <#if checkDataCommand??>\n    如果发现不一致的情况，可以在查看校验日志或在目标机器执行<br/>\n    ${checkDataCommand!}<br/>\n    来看一下详细的key不一致情况\n  </#if>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/migrate/checkDataLog.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>数据校验日志</title>\n  <meta http-equiv=\"refresh\" content=\"5\">\n</head>\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n  <#if checkDatalogList?? && (checkDatalogList?size > 0)>\n    <#list checkDatalogList as line>\n      <font color=\"white\">${line!}</font><br/>\n    </#list>\n  </#if>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/migrate/config.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>迁移配置</title>\n</head>\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n<#if configList?? && (configList?size > 0)>\n<#list configList as line>\n<font color=\"white\">${line!}</font><br/>\n</#list>\n</#if>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/migrate/init.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>迁移数据</title>\n  <#include '/inc/frontResources.html'>\n  <script type=\"text/javascript\">\n    $(function () {\n      $('#migrateTool').change(function () {\n        if ($(this).val() == 0) {\n          $(\"#sourceRedisMigrateIndex\").hide();\n          $(\"#sourceRedisShakeIndex\").show();\n          $(\"#targetRedisMigrateIndex\").hide();\n          $(\"#targetRedisShakeIndex\").show();\n          $('#redis_shake_config').show();\n        } else {\n          $(\"#sourceRedisShakeIndex\").hide();\n          $(\"#sourceRedisMigrateIndex\").show();\n          $(\"#targetRedisMigrateIndex\").show();\n          $(\"#targetRedisShakeIndex\").hide();\n          $('#redis_shake_config').hide();\n        }\n      });\n\n      var targetAppId = document.getElementById('targetAppId').value;\n      if (targetAppId != '') {\n        fillAppInstanceList('targetRedisShakeIndex', 'redisTargetPass', 'redisTargetVersion', 'targetServers', 'targetRedisMigrateIndex', 'targetAppName', 'targetAppId');\n      }\n\n    });\n\n    function changeDataType(appIdId, serversId, choose) {\n      var dataType = choose.options[choose.selectedIndex].value;\n      var appId = document.getElementById(appIdId);\n      var servers = document.getElementById(serversId);\n      if (dataType == 0) {\n        appId.disabled = true;\n        servers.disabled = false;\n      } else if (dataType == 1) {\n        appId.disabled = false;\n        servers.disabled = true;\n      }\n    }\n\n    function fillAppInstanceList(redisShakeIndexId, redisPassId, redisVersionId, instanceDetailId, redisMigrateIndexId, appName, appIdInputId) {\n      var appId = document.getElementById(appIdInputId).value;\n      if (appId == \"\") {\n        //不能为空\n        return;\n      }\n      var redisPass = document.getElementById(redisPassId);\n      var instanceDetail = document.getElementById(instanceDetailId);\n      var migrateTool = document.getElementById(\"migrateTool\").value;\n      var appNameId = document.getElementById(appName);\n      var redisVersion = document.getElementById(redisVersionId);\n      $.get(\n              '${request.contextPath}/data/migrate/appInstanceList.json',\n              {\n                appId: appId,\n                migrateTool: migrateTool\n              },\n              function (data) {\n                var instances = data.instances;\n                instanceDetail.value = instances;\n\n                var password = data.password;\n                redisPass.value = password;\n\n                var version = data.redisVersion;\n                redisVersion.value = version;\n\n                appNameId.innerHTML = data.appName;\n                $('#' + appName).attr(\"style\", \"display:'';\");\n\n                var appType = data.appType;\n                var redisMigrateIndex = document.getElementById(migrateTool == 1 ? redisMigrateIndexId : redisShakeIndexId);\n                var options = redisMigrateIndex.options;\n                //修改select\n                if (appType == 2) {\n                  for (var i = 0; i < options.length; i++) {\n                    if (migrateTool == 0 && 7 == options[i].value) {\n                      options[i].selected = 'selected';\n                      break;\n                    }\n                    if (migrateTool == 1 && 1 == options[i].value) {\n                      options[i].selected = 'selected';\n                      break;\n                    }\n                  }\n                } else if (appType == 5) {\n                  for (var i = 0; i < options.length; i++) {\n                    if (migrateTool == 0 && 6 == options[i].value) {\n                      options[i].selected = 'selected';\n                      break;\n                    }\n                    if (migrateTool == 1 && 0 == options[i].value) {\n                      options[i].selected = 'selected';\n                      break;\n                    }\n                  }\n                } else if (appType == 6) {\n                  for (var i = 0; i < options.length; i++) {\n                    if (migrateTool == 0 && 5 == options[i].value) {\n                      options[i].selected = 'selected';\n                      break;\n                    }\n                    if (migrateTool == 1 && 0 == options[i].value) {\n                      options[i].selected = 'selected';\n                      break;\n                    }\n                  }\n                }\n              }\n      );\n    }\n\n    function checkMigrateFormat() {\n      var sourceRedisMigrateIndex;\n      var targetRedisMigrateIndex;\n      if ($('#migrateTool').val() == 0) {\n        sourceRedisMigrateIndex = document.getElementById(\"sourceRedisShakeIndex\").value;\n        targetRedisMigrateIndex = document.getElementById(\"targetRedisShakeIndex\").value;\n      } else {\n        sourceRedisMigrateIndex = document.getElementById(\"sourceRedisMigrateIndex\").value;\n        targetRedisMigrateIndex = document.getElementById(\"targetRedisMigrateIndex\").value;\n      }\n      var sourceServers = document.getElementById(\"sourceServers\");\n      var sourceAppId = document.getElementById(\"sourceAppId\");\n      var sourceDataType = document.getElementById(\"sourceDataType\").value;\n      var migrateMachineIp = document.getElementById(\"migrateMachineIp\").value;\n      var redisSourcePass = document.getElementById(\"redisSourcePass\");\n      var redisTargetPass = document.getElementById(\"redisTargetPass\");\n      var migrateTool = document.getElementById(\"migrateTool\");\n\n\n      //非cachecloud\n      if (sourceDataType == 0 && sourceServers.value == \"\") {\n        alert(\"源实例信息不能为空!\");\n        sourceServers.focus();\n        return false;\n        //cachecloud\n      } else if (sourceDataType == 1 && sourceAppId.value == \"\") {\n        alert(\"源appId不能为空!\");\n        sourceAppId.focus();\n        return false;\n      } else if (sourceDataType == 1 && sourceServers.value == \"\") {\n        alert(\"请确保appId=\" + sourceAppId.value + \"下有实例信息\");\n        sourceAppId.focus();\n        return false;\n      }\n\n      var targetAppId = document.getElementById(\"targetAppId\");\n      var targetServers = document.getElementById(\"targetServers\");\n      var targetDataType = document.getElementById(\"targetDataType\").value;\n      //非cachecloud\n      if (targetDataType == 1 && targetAppId.value == \"\") {\n        alert(\"目标appId不能为空!\");\n        targetAppId.focus();\n        return false;\n      } else if (targetDataType == 1 && targetServers.value == \"\") {\n        alert(\"请确保appId=\" + targetAppId.value + \"下有实例信息\");\n        targetAppId.focus();\n        return false;\n      }\n\n      $.get(\n              '${request.contextPath}/data/migrate/check.json',\n              {\n                sourceRedisMigrateIndex: sourceRedisMigrateIndex,\n                targetRedisMigrateIndex: targetRedisMigrateIndex,\n                sourceServers: sourceServers.value,\n                targetServers: targetServers.value,\n                migrateMachineIp: migrateMachineIp,\n                redisSourcePass: redisSourcePass.value,\n                redisTargetPass: redisTargetPass.value,\n                migrateTool: migrateTool.value,\n                versionid: $('#version option:selected').attr(\"versionid\")\n              },\n              function (data) {\n                var status = data.status;\n                alert(data.message);\n                if (status == 1) {\n                  var submitButton = document.getElementById(\"submitButton\");\n                  submitButton.disabled = false;\n\n                  var checkButton = document.getElementById(\"checkButton\");\n                  checkButton.disabled = true;\n                }\n              }\n      );\n    }\n\n    function startMigrate() {\n      var migrateTool = document.getElementById(\"migrateTool\").value;\n      var sourceRedisMigrateIndex = document.getElementById(migrateTool == 1 ? \"sourceRedisMigrateIndex\" : \"sourceRedisShakeIndex\").value;\n      var targetRedisMigrateIndex = document.getElementById(migrateTool == 1 ? \"targetRedisMigrateIndex\" : \"targetRedisShakeIndex\").value;\n      var sourceServers = document.getElementById(\"sourceServers\");\n      var targetServers = document.getElementById(\"targetServers\");\n      var migrateMachineIp = document.getElementById(\"migrateMachineIp\").value;\n      var sourceAppId = document.getElementById(\"sourceAppId\");\n      var targetAppId = document.getElementById(\"targetAppId\");\n      var redisSourcePass = document.getElementById(\"redisSourcePass\");\n      var redisTargetPass = document.getElementById(\"redisTargetPass\");\n      var redisSourceVersion = document.getElementById(\"redisSourceVersion\");\n      var redisTargetVersion = document.getElementById(\"redisTargetVersion\");\n      var source_rdb_parallel = document.getElementById('source_rdb_parallel');\n      var parallel = document.getElementById('parallel');\n\n\n      $.get(\n              '${request.contextPath}/data/migrate/start.json',\n              {\n                sourceRedisMigrateIndex: sourceRedisMigrateIndex,\n                targetRedisMigrateIndex: targetRedisMigrateIndex,\n                sourceServers: sourceServers.value,\n                targetServers: targetServers.value,\n                migrateMachineIp: migrateMachineIp,\n                versionid: $('#version option:selected').attr(\"versionid\"),\n                sourceAppId: sourceAppId.value,\n                targetAppId: targetAppId.value,\n                redisSourcePass: redisSourcePass.value,\n                redisTargetPass: redisTargetPass.value,\n                redisSourceVersion: redisSourceVersion.value,\n                redisTargetVersion: redisTargetVersion.value,\n                migrateTool: migrateTool,\n                source_rdb_parallel: source_rdb_parallel.value,\n                parallel: parallel.value\n              },\n              function (data) {\n                var status = data.status;\n                if (status == 1) {\n                  updateForImport(data.migrateId);\n                  alert(\"迁移程序已经启动，请返回迁移列表关注迁移进度!\");\n                  location.href = \"${request.contextPath}/data/migrate/index?status=-2\";\n                } else {\n                  alert(\"迁移失败,请查看日志分析原因!\");\n                }\n                var checkButton = document.getElementById(\"checkButton\");\n                checkButton.disabled = true;\n              }\n      );\n    }\n\n    function updateForImport(migrateId) {\n      console.log(\"updateForImport migrateId:\" + migrateId);\n      var importId = document.getElementById(\"importId\");\n      if (importId != null && importId.value != '') {\n        console.log(\"updateForImport importId:\" + importId.value);\n        $.get(\n                '/import/app/goOn.json',\n                {\n                  importId: importId.value,\n                  migrateId: migrateId\n                },\n                function (data) {\n                  var success = data.success;\n                  if (success == 1) {\n                    console.log(\"updateForImport success\");\n                  }\n                }\n        );\n      }\n    }\n  </script>\n</head>\n<body role=\"document dashboard\">\n<div class=\"container\">\n  <#include '/inc/head.html'>\n  <div id=\"systemAlert\">\n  </div>\n  <main id=\"main\" class=\"main px-5 ms-0\">\n    <div class=\"page-content\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">\n            迁移工具配置\n            <button type=\"button\" class=\"btn btn-success btn-sm bi bi-question-lg\"\n                    data-bs-container=\"body\" data-bs-toggle=\"popover\" data-bs-placement=\"top\" data-bs-html=\"true\"\n                    data-bs-content=\"<a href='http://cachecloud.github.io/2016/06/28/1.2.%20%E8%BF%81%E7%A7%BB%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E/'>使用文档</a>\"\n                    style=\"border-radius:100%\">\n            </button>\n          </h3>\n        </div>\n\n        <div class=\"form\">\n          <div action=\"\" method=\"post\"\n               class=\"form-horizontal form-bordered form-row-stripped\">\n            <div class=\"form-body\">\n\n              <div class=\"card-body\">\n\n                <div class=\"form-group row\">\n                  <div class=\"col-md-12\">\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        迁移工具:\n                      </label>\n                      <div class=\"col-md-5\">\n                        <select id=\"migrateTool\" name=\"migrateTool\"\n                                class=\"form-select\">\n                          <option value=\"0\">redis-shake</option>\n                          <option value=\"1\">redis-migrate-tool</option>\n                        </select>\n                      </div>\n                    </div>\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        迁移工具机器:\n                      </label>\n                      <div class=\"col-md-5\">\n                        <select id=\"migrateMachineIp\" name=\"migrateMachineIp\"\n                                class=\"form-select\">\n                          <#list machineInfoMap?keys as key>\n                          <option value=\"${key!}\">\n                            ip：${key!}&nbsp;&nbsp;任务数：${machineInfoMap[key]!}\n                          </option>\n                        </#list>\n                        </select>\n                      </div>\n                    </div>\n                    <div class=\"form-group row\">\n                      <label class=\"col-form-label col-md-3 text-end\">\n                        版本:\n                      </label>\n                      <div class=\"col-md-5\">\n                        <select name=\"type\" id=\"version\" class=\"form-select\">\n                          <#list resourcelist as resource>\n                          <#if (resource.status == 1)>\n                          <option\n                          <#if currentVersion?? && (resource.id == currentVersion.id)>selected</#if>\n                        versionid=\"${resource.id!}\">${resource.name!}</option>\n                      </#if>\n                    </#list>\n                    </select>\n                  </div>\n                </div>\n                <div class=\"form-group row\" id=\"redis_shake_config\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    参数配置:\n                  </label>\n                  <div class=\"col-md-4\">\n                    <label class=\"control-label\"\n                           title=\"源RDB文件同步并发数\">source.rdb.parallel (?):</label>\n                    <input type=\"text\" id=\"source_rdb_parallel\" class=\"form-control\"\n                           placeholder=\"源RDB文件同步并发数\" value=\"8\"/>\n                  </div>\n                  <div class=\"col-md-4\">\n                    <label class=\"control-label\" title=\"目标节点数据同步线程数\">parallel (?):</label>\n                    <input type=\"text\" id=\"parallel\" class=\"form-control\"\n                           placeholder=\"目标节点数据同步线程数\" value=\"16\"/>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"card-header\">\n            <h3 class=\"card-title\">\n              源和目标配置\n            </h3>\n          </div>\n\n          <div class=\"card-body\">\n            <div class=\"form-group row\">\n              <div class=\"col-md-6\">\n                <div class=\"row\">\n                  <label class=\"col-form-label col-md-3 text-end\">\n                    源类型:\n                  </label>\n                  <div class=\"col-md-5\">\n                    <select id=\"sourceRedisMigrateIndex\" name=\"sourceRedisMigrateIndex\"\n                            class=\"form-select\" style=\"display: none\">\n                      <option value=\"0\">\n                        Redis普通节点\n                      </option>\n                      <option value=\"1\">\n                        Redis-cluster\n                      </option>\n                      <option value=\"2\">\n                        RDB-file\n                      </option>\n                      <option value=\"4\">\n                        AOF-file\n                      </option>\n                    </select>\n                    <select id=\"sourceRedisShakeIndex\" name=\"sourceRedisMigrateIndex\"\n                            class=\"form-select\">\n                      <option value=\"0\">\n                        Redis普通节点\n                      </option>\n                      <option value=\"5\" <#if sourceType?? && (sourceType==5)>selected=\"selected\"</#if>>\n                    Redis-standalone\n                    </option>\n                    <option value=\"6\" <#if sourceType?? && (sourceType==6)>selected=\"selected\"</#if>>\n                  Redis-sentinel\n                  </option>\n                  <option value=\"7\" <#if sourceType?? && (sourceType==7)>selected=\"selected\"</#if>>\n                Redis-cluster\n                </option>\n                </select>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"col-md-6\">\n            <div class=\"row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                目标类型:\n              </label>\n              <div class=\"col-md-5\">\n                <select id=\"targetRedisMigrateIndex\" name=\"targetRedisMigrateIndex\"\n                        class=\"form-select\" style=\"display: none\">\n                  <option value=\"0\">\n                    Redis普通节点\n                  </option>\n                  <option value=\"1\">\n                    Redis-cluster\n                  </option>\n                  <option value=\"2\">\n                    RDB-file\n                  </option>\n                </select>\n                <select id=\"targetRedisShakeIndex\" name=\"sourceRedisMigrateIndex\"\n                        class=\"form-select\">\n                  <option value=\"0\">\n                    Redis普通节点\n                  </option>\n                  <option value=\"5\">\n                    Redis-standalone\n                  </option>\n                  <option value=\"6\">\n                    Redis-sentinel\n                  </option>\n                  <option value=\"7\">\n                    Redis-cluster\n                  </option>\n                </select>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <div class=\"col-md-6\">\n            <div class=\"row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                源redis版本:\n              </label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" id=\"redisSourceVersion\" name=\"redisSourceVersion\"\n                       value=\"${redisSourceVersion!}\"\n                       placeholder=\"redis-x.x.x\" class=\"form-control\"/>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"col-md-6\">\n            <div class=\"row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                目标redis版本:\n              </label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" id=\"redisTargetVersion\" name=\"redisTargetVersion\"\n                       placeholder=\"redis-x.x.x\" class=\"form-control\"/>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <div class=\"col-md-6\">\n            <div class=\"row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                数据源:\n              </label>\n              <div class=\"col-md-5\">\n                <select id=\"sourceDataType\" name=\"sourceDataType\"\n                        class=\"form-select\"\n                        onchange=\"changeDataType('sourceAppId','sourceServers',this)\">\n                  <option value=\"1\" selected=\"selected\">\n                    cachecloud\n                  </option>\n                  <option value=\"0\"\n                  <#if sourceDataType?? && (sourceDataType==0)>selected=\"selected\"</#if> >\n                非cachecloud\n                </option>\n                </select>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"col-md-6\">\n            <div class=\"row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                数据源:\n              </label>\n              <div class=\"col-md-5\">\n                <select id=\"targetDataType\" name=\"targetDataType\"\n                        class=\"form-select\"\n                        onchange=\"changeDataType('targetAppId','targetServers',this)\">\n                  <option value=\"1\" selected=\"selected\">\n                    cachecloud\n                  </option>\n                  <option value=\"0\">\n                    非cachecloud\n                  </option>\n                </select>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <div class=\"col-md-6\">\n            <div class=\"row\" id=\"sourceAppIdDiv\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                源appId:\n              </label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" id=\"sourceAppId\"\n                       class=\"form-control\"\n                       onchange=\"fillAppInstanceList('sourceRedisShakeIndex', 'redisSourcePass','redisSourceVersion', 'sourceServers', 'sourceRedisMigrateIndex', 'sourceAppName', this.id)\"/>\n                <label id=\"sourceAppName\" class=\"control-label\"\n                       style=\"display:none\"></label>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"col-md-6\">\n            <div class=\"row\" id=\"targetAppIdDiv\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                目标appId:\n              </label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" id=\"targetAppId\" class=\"form-control\"\n                       value=\"${targetAppId!}\"\n                       onchange=\"fillAppInstanceList('targetRedisShakeIndex', 'redisTargetPass','redisTargetVersion', 'targetServers', 'targetRedisMigrateIndex', 'targetAppName',this.id)\"/>\n                <label id=\"targetAppName\" class=\"control-label\"\n                       style=\"display:none\"></label>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <div class=\"col-md-6\">\n            <div class=\"row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                源密码:\n              </label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" id=\"redisSourcePass\" name=\"redisSourcePass\"\n                       value=\"${redisSourcePass!}\"\n                       placeholder=\"没有无需填写\" class=\"form-control\"/>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"col-md-6\">\n            <div class=\"row\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                目标密码:\n              </label>\n              <div class=\"col-md-5\">\n                <input type=\"text\" id=\"redisTargetPass\" name=\"redisTargetPass\"\n                       placeholder=\"没有无需填写\" class=\"form-control\"/>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"form-group row\">\n          <div class=\"col-md-6\">\n            <div class=\"row\" id=\"sourceServersDiv\">\n              <label class=\"col-form-label col-md-3 text-end\">\n                源实例详情:\n              </label>\n              <div class=\"col-md-8\">\n                <textarea\n                <#if !(sourceServers??) || (sourceServers=='')>disabled=\"disabled\"</#if>\n              rows=\"10\" name=\"sourceServers\"\n              id=\"sourceServers\"\n              placeholder=\"节点详情\"\n              class=\"form-control\">${sourceServers!}</textarea>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"col-md-6\">\n          <div class=\"row\" id=\"targetServersDiv\">\n            <label class=\"col-form-label col-md-3 text-end\">\n              目标实例详情:\n            </label>\n            <div class=\"col-md-8\">\n                                              <textarea disabled=\"disabled\" rows=\"10\" name=\"targetServers\"\n                                                        id=\"targetServers\" placeholder=\"节点详情\"\n                                                        class=\"form-control\"></textarea>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"form-group row mx-5 rounded\" style=\"border: 1px dashed #348f27\" id=\"help\">\n        <div class=\"offset-md-1 col-md-5\">\n                        <span class=\"help-block\">\n                                        <strong>redis-shake迁移工具实例样式说明</strong><br/>\n                                        每行格式都是:&nbsp;&nbsp;ip:port(例如：10.10.xx.xx:6379)<br/>\n                                        1. standalone类型：<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp:masterPort<br/>\n                                        2. sentinel类型：<br/>\n                                        &nbsp;&nbsp;&nbsp;master_name:master/slave<br/>\n                                        &nbsp;&nbsp;&nbsp;sentinelIp1:sentinelPort1<br/>\n                                        &nbsp;&nbsp;&nbsp;sentinelIp2:sentinelPort2<br/>\n                                        &nbsp;&nbsp;&nbsp;sentinelIp3:sentinelPort3<br/>\n                                        3. cluster类型：<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp1:masterPort1<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp2:masterPort2<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp3:masterPort3<br/>\n\n                        </span>\n        </div>\n        <div class=\"offset-md-1 col-md-5\">\n                        <span class=\"help-block\">\n                                        <strong>redis-migrate-tool迁移工具实例样式说明</strong><br/>\n                                        每行格式都是:&nbsp;&nbsp;ip:port(例如：10.10.xx.xx:6379)<br/>\n                                        1. standalone类型：<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp:masterPort<br/>\n                                        2. sentinel类型：<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;masterIp:masterPort<br/>\n                                        3. cluster类型：<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp1:masterPort1<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;slaveIp1:slavePort1<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;masterIp2:masterPort2<br/>\n                                        &nbsp;&nbsp;&nbsp;&nbsp;slaveIp2:slavePort2<br/>\n                                        (可以是多对主从，只要把所有的cluster节点都按照格式写就可以，程序会自动判断)<br/>\n                        </span>\n        </div>\n      </div>\n      <br/>\n      <div class=\"form-actions fluid\">\n        <div class=\"row\">\n          <div class=\"col-md-12\">\n            <div class=\"text-center\">\n              <button id=\"submitButton\" type=\"button\" onclick=\"startMigrate()\"\n                      class=\"btn btn-success\" disabled=\"disabled\">\n                <i class=\"bi bi-check\"></i>\n                开始迁移\n              </button>\n              <button id=\"checkButton\" type=\"button\" class=\"btn btn-success offset-md-1\"\n                      onclick=\"checkMigrateFormat()\">\n                <i class=\"bi bi-check\"></i>\n                检查格式\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <input id=\"importId\" type=\"hidden\" value=\"${importId!}\">\n    </div>\n    </form>\n</div>\n</div>\n</div>\n</div>\n</main>\n</div>\n<script>\n  const popoverTriggerList = document.querySelectorAll('[data-bs-toggle=\"popover\"]')\n  const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/migrate/log.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>迁移日志</title>\n  <meta http-equiv=\"refresh\" content=\"5\">\n</head>\n<body STYLE=\"BACKGROUND-COLOR:#000;color:#FFF\">\n  <#if logList?? && (logList?size > 0)>\n    <#list logList as line>\n      <font color=\"white\">${line!}</font><br/>\n    </#list>\n  </#if>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/migrate/process.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>迁移工具执行状态/迁移进度</title>\n  <#include '/inc/frontResources.html'>\n  <script type=\"text/javascript\">\n    $(document).ready(function(){\n      var process = document.getElementById(\"process\").value;\n      alert(process);\n      window.close();\n    })\n  </script>\n</head>\n\n<body role=\"document dashboard\">\n  <#if !(process??)>\n    <div class=\"container\">\n      <#include '/inc/head.html'>\n      <div id=\"systemAlert\">\n      </div>\n      <main id=\"main\" class=\"main px-5 ms-0\">\n        <div class=\"card\">\n          <div class=\"card-body\">\n            <div class=\"row\">\n              <div class=\"col-md-12\">\n                <div class=\"card-title\">\n                  <h3>迁移工具执行状态</h3>\n                </div>\n              </div>\n            </div>\n            <div class=\"row\">\n              <#list migrateToolStatMap?keys as key>\n                <div class=\"col-md-12\">\n                  <div class=\"page-header\">\n                    <h4>${key!}</h4>\n                  </div>\n                </div>\n                <div class=\"col-md-12\">\n                  <table class=\"table table-striped table-hover\" style=\"margin-top: 0px\">\n                    <thead>\n                    <tr>\n                      <td>key</td>\n                      <td>value</td>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    <#list migrateToolStatMap[key] as entry2>\n                      <tr>\n                        <td>${entry2.key!}</td>\n                        <td>${entry2.value!}</td>\n                      </tr>\n                    </#list>\n                    </tbody>\n                  </table>\n                </div>\n              </#list>\n            </div>\n          </div>\n          <br/><br/><br/><br/><br/><br/><br/>\n        </div>\n      </main>\n    </div>\n  <#else>\n    <input type=\"hidden\" name=\"process\" id=\"process\" value=\"${process!}\">\n  </#if>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/noPower.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\" />\n\t<title>无访问权限</title>\n\t<meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n\t<#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n\t<div class=\"wrapper\">\n\t\t<#include '/inc/head.html'>\n\t\t<div class=\"content-wrapper ml-0\">\n\t\t\t<div class=\"content\">\n\t\t\t\t<div class=\"container-fluid\">\n\t\t\t\t\t<div class=\"row\">\n\t\t\t\t\t\t<div class=\"col-12\">\n\t\t\t\t\t\t\t<div class=\"card\">\n\t\t\t\t\t\t\t\t<div class=\"card-body\">\n\t\t\t\t\t\t\t\t\t<h4>\n\t\t\t\t\t\t\t\t\t\t<img width=\"70\" height=\"60\" src=\"${request.contextPath}/assets/img/cry.jpg\">\n\t\t\t\t\t\t\t\t\t\t您没有应用${appId!}的使用权限\n\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/server/cpu.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"row\">\n    <#if !(cpu??)>\n      no data\n    </#if>\n    <#if cpu?? && (cpu?size > 0)>\n      <#list cpu as item>\n        <div class=\"col-md-6\">\n          <div class=\"card\">\n            <div class=\"card-body\">\n              <div id=\"container${item.name!}\"\n                   style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </#list>\n    </#if>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  $(document).ready(function() {\n    initSubCpuChart();\n  });\n\n  function initSubCpuChart(){\n    <#if cpu?? && (cpu?size > 0)>\n      <#list cpu as item>\n        var options = getOptions(\"${item.name!}\", \"container${item.name!}\");\n        options.subtitle.text = \"max user:${item.maxUser!}% sys:${item.maxSys!}% wa:${item.maxWa!}% avg user:${item.avgUser!}% sys:${item.avgSys!}% wa:${item.avgWa!}%\";\n        <#if item.userSeries??>\n          options.series.push(${item.userSeries.toJson()});\n        </#if>\n        <#if item.sysSeries??>\n          options.series.push(${item.sysSeries.toJson()});\n        </#if>\n        <#if item.waSeries??>\n          options.series.push(${item.waSeries.toJson()});\n        </#if>\n        new Highcharts.Chart(options);\n      </#list>\n    </#if>\n  }\n</script>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/server/disk.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"row\">\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-body\">\n          <div id=\"containerRead\"\n               style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-body\">\n          <div id=\"containerWrite\"\n              style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-body\">\n          <div id=\"containerBusy\"\n           style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-body\">\n          <div id=\"containerIops\"\n           style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-body\">\n          <div id=\"containerSpace\"\n           style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  $(document).ready(function() {\n    initReadChart();\n    initWriteChart();\n    initBusyChart();\n    initIopsChart();\n    initSpaceChart();\n  });\n\n  function initReadChart(){\n    var options = getOptions(\"read\", \"containerRead\");\n    options.subtitle.text = \"max:${read.max!}k/s avg:${read.avg!}k/s\";\n    <#list read.series as item>\n      options.series.push(${item.toJson()});\n    </#list>\n    new Highcharts.Chart(options);\n  }\n  function initWriteChart(){\n    var options = getOptions(\"write\", \"containerWrite\");\n    options.subtitle.text = \"max:${write.max!}k/s avg:${write.avg!}k/s\";\n    <#list write.series as item>\n      options.series.push(${item.toJson()});\n    </#list>\n    new Highcharts.Chart(options);\n  }\n  function initBusyChart(){\n    var options = getOptions(\"busy\", \"containerBusy\");\n    options.subtitle.text = \"max:${busy.max!}% avg:${busy.avg!}%\";\n    <#list busy.series as item>\n      options.series.push(${item.toJson()});\n    </#list>\n    new Highcharts.Chart(options);\n  }\n  function initIopsChart(){\n    var options = getOptions(\"iops\", \"containerIops\");\n    options.subtitle.text = \"max:${iops.max!}次/s avg:${iops.avg!}次/s\";\n    <#list iops.series as item>\n      options.series.push(${item.toJson()});\n    </#list>\n    new Highcharts.Chart(options);\n  }\n  function initSpaceChart(){\n    var options = getOptions(\"space use\", \"containerSpace\");\n    options.subtitle.text = \"max:${space.max!}% avg:${space.avg!}%\";\n    <#list space.series as item>\n      options.series.push(${item.toJson()});\n    </#list>\n    new Highcharts.Chart(options);\n  }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/server/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>CacheCloud服务器状态</title>\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n  <#include '/inc/frontResources.html'>\n</head>\n<body class=\"hold-transition sidebar-mini layout-navbar-fixed\">\n<div class=\"wrapper\">\n  <#include '/inc/head.html'>\n  <div class=\"content-wrapper ml-0\">\n    <div class=\"content\">\n      <div id=\"main\" class=\"container\">\n        <div class=\"row\">\n          <div class=\"col-12\">\n            <div class=\"card\">\n              <div class=\"card-body\">\n                <nav class=\"nav\">\n                  <ul class=\"nav nav-tabs d-flex align-items-center\" id=\"app_tabs\">\n                    <li id=\"overview\" class=\"nav-item\" data-url=\"${request.contextPath}/server/overview?ip=${ip!}&date=${date!}\">\n                      <a class=\"nav-link d-flex\" onclick=\"refreshActiveTab('overview')\">概览</a>\n                    </li>\n                    <li id=\"cpu\" class=\"nav-item\" data-url=\"${request.contextPath}/server/cpu?ip=${ip!}&date=${date!}\">\n                      <a class=\"nav-link d-flex\" onclick=\"refreshActiveTab('cpu')\">cpu</a>\n                    </li>\n                    <li id=\"net\" class=\"nav-item\" data-url=\"${request.contextPath}/server/net?ip=${ip!}&date=${date!}\">\n                      <a class=\"nav-link d-flex\" onclick=\"refreshActiveTab('net')\">net</a>\n                    </li>\n                    <li id=\"disk\" class=\"nav-item\" data-url=\"${request.contextPath}/server/disk?ip=${ip!}&date=${date!}\">\n                      <a class=\"nav-link d-flex\" onclick=\"refreshActiveTab('disk')\">disk</a>\n                    </li>\n                  </ul>\n                </nav>\n              </div>\n            </div>\n            <div class=\"card\">\n              <div class=\"card-body\">\n                <div class=\"tab-content\">\n                  <div class=\"tab-pane active\" id=\"overviewTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"cpuTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"netTab\">\n                  </div>\n                  <div class=\"tab-pane\" id=\"diskTab\">\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n  <#include \"/inc/footer.html\">\n</div>\n<script type=\"text/javascript\">\n  function showTab(tab) {\n    $.get($(\"#\" + tab).attr(\"data-url\"), function (result) {\n      $(\"#\" + tab + \"Tab\").html(result);\n    });\n  }\n\n  function refreshActiveTab(tab) {\n    if (tab == undefined || tab == null || tab.length == 0) {\n      tab = 'overview';\n    }\n    if (tab != undefined && tab != null && tab.length > 0) {\n      $(\"#app_tabs li a\").removeClass(\"active\");\n      $(\"#\" + tab + \" a\").addClass(\"active\");\n      $(\"#\" + tab + \"Tab\").addClass(\"active\").siblings().removeClass(\"active\");\n    } else {\n      tab = \"overview\";\n      $(\"#app_tabs li a\").removeClass(\"active\");\n      $(\"#\" + tab + \" a\").addClass(\"active\");\n    }\n    showTab(tab);\n    $(\"#app_tabs li a\").tooltip({placement: \"bottom\"});\n  }\n\n  $(function () {\n    refreshActiveTab();\n  });\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/server/net.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"row\">\n    <#if !(net??)>\n      no data\n    </#if>\n    <#if net?? && (net?size > 0)>\n      <#list net as item>\n        <div class=\"col-md-6\">\n          <div class=\"card\">\n            <div class=\"card-body\">\n              <div id=\"container${item.name!}\"\n                   style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\">\n              </div>\n            </div>\n          </div>\n        </div>\n      </#list>\n    </#if>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  $(document).ready(function() {\n    initSubNetChart();\n  });\n\n  function initSubNetChart(){\n    <#if net?? && (net?size > 0)>\n      <#list net as item>\n        var options = getOptions(\"${item.name!}\", \"container${item.name!}\");\n        options.subtitle.text = \"max in:${item.maxIn!}k/s out:${item.maxOut!}k/s avg in:${item.avgIn!}k/s out:${item.avgOut!}k/s\";\n        <#if item.inSeries??>\n          options.series.push(${item.inSeries.toJson()});\n        </#if>\n        <#if item.outSeries??>\n          options.series.push(${item.outSeries.toJson()});\n        </#if>\n        new Highcharts.Chart(options);\n      </#list>\n    </#if>\n  }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/server/overview.html",
    "content": "<div class=\"col-md-12\">\n  <div class=\"row\">\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h3 class=\"card-title\">\n            服务器信息\n          </h3>\n        </div>\n        <div class=\"card-body\">\n          <div class=\"tabbable-custom\">\n            <table class=\"table table-striped table-hover\">\n              <tr>\n                <td>ip</td>\n                <td>${info.ip!}</td>\n              </tr>\n              <tr>\n                <td>host</td>\n                <td>${info.host!}</td>\n              </tr>\n              <tr>\n                <td>cpu核数</td>\n                <td>${info.cpus!}</td>\n              </tr>\n              <tr>\n                <td>nmon版本</td>\n                <td>${info.nmon!}</td>\n              </tr>\n              <tr>\n                <td>max file</td>\n                <td>${file!}</td>\n              </tr>\n              <tr>\n                <td>max procs</td>\n                <td>${process!}</td>\n              </tr>\n              <tr>\n                <td>cpu型号</td>\n                <td>${info.cpuModel!}</td>\n              </tr>\n              <tr>\n                <td>发行版本</td>\n                <td>${info.dist!}</td>\n              </tr>\n              <tr>\n                <td>内核版本</td>\n                <td>${info.kernel!}</td>\n              </tr>\n            </table>\n          </div>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <div class=\"card-title\">\n            <div style=\"float:right\">\n              <form method=\"post\" action=\"${request.contextPath}/server/index\" id=\"ec\" name=\"ec\">\n                <label class=\"col-form-label\" style=\"font-weight:bold;text-align:left;\">\n                  日期:&nbsp;&nbsp;\n                </label>\n                <input type=\"date\" size=\"21\" name=\"date\" id=\"startDate\"  value=\"${date!}\">\n                <input type=\"hidden\" name=\"ip\" value=\"${info.ip!}\">\n                <label>&nbsp;<input type=\"submit\" class=\"btn-4\" value=\"查询\"/></label>\n              </form>\n            </div>\n            <h4>load</h4>\n          </div>\n        </div>\n        <div class=\"card-body\">\n          <div id=\"containerLoad\"\n               style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card\">\n        <div class=\"card-header\">\n          <h4 class=\"card-title\">cpu</h4>\n        </div>\n        <div class=\"card-body\">\n          <div id=\"containerCpu\"\n               style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n        </div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">memory</h4>\n      </div>\n      <div class=\"card-body\">\n        <div id=\"containerMemory\"\n             style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">swap</h4>\n      </div>\n      <div class=\"card-body\">\n        <div id=\"containerSwap\"\n             style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">net</h4>\n      </div>\n      <div class=\"card-body\">\n        <div id=\"containerNet\"\n             style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">tcp connection</h4>\n      </div>\n      <div class=\"card-body\">\n        <div id=\"containerTcp\"\n             style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n      </div>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"card-header\">\n        <h4 class=\"card-title\">disk</h4>\n      </div>\n      <div class=\"card-body\">\n        <div id=\"containerDisk\"\n             style=\"max-width: 100%; min-width: 310px; height: 400px; margin: 0 auto\"></div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<script type=\"text/javascript\">\n  $(document).ready(\n          function() {\n            initLoadChart();\n            initCpuChart();\n            initMemoryChart();\n            initSwapChart();\n            initNetChart();\n            initTcpChart();\n            initDiskChart();\n          });\n\n  function initLoadChart(){\n    var title = \"1-min-max:${maxLoad1!} 1-min-avg:${avgLoad1!}\";\n    var options = getOptions(title, \"containerLoad\");\n    push(options.series, ${load1!});\n    push(options.series, ${load5!});\n    push(options.series, ${load15!});\n    new Highcharts.Chart(options);\n  }\n\n  function initCpuChart(){\n    var title = \"max user:${maxUser!}% sys:${maxSys!}% wa:${maxWa!}%\";\n    var options = getOptions(title, \"containerCpu\");\n    push(options.series, ${user!});\n    push(options.series, ${sys!});\n    push(options.series, ${wa!});\n    new Highcharts.Chart(options);\n  }\n\n  function initMemoryChart(){\n    var title = \"now free:${curFree!}G max use:${maxUse!}G cache:${maxCache!}G buffer:${maxBuffer!}G\";\n    var options = getOptions(title, \"containerMemory\", \"area\");\n    push(options.series, ${mtotal!});\n    push(options.series, ${muse!});\n    push(options.series, ${mcache!});\n    push(options.series, ${mbuffer!});\n    new Highcharts.Chart(options);\n  }\n\n  function initSwapChart(){\n    var title = \"max use:${maxSwap!}M\";\n    var options = getOptions(title, \"containerSwap\");\n    push(options.series, ${mswap!});\n    push(options.series, ${mswapUse!});\n    new Highcharts.Chart(options);\n  }\n\n  function initNetChart(){\n    var title = \"max in:${maxNetIn!}M/s out:${maxNetOut!}M/s\";\n    var options = getOptions(title, \"containerNet\");\n    push(options.series, ${nin!});\n    push(options.series, ${nout!});\n    new Highcharts.Chart(options);\n  }\n\n  function initTcpChart(){\n    var title = \"max estab:${maxConn!} tw:${maxWait!} orphan:${maxOrphan!}\";\n    var options = getOptions(title, \"containerTcp\");\n    push(options.series, ${testab!});\n    push(options.series, ${twait!});\n    push(options.series, ${torph!});\n    new Highcharts.Chart(options);\n  }\n\n  function initDiskChart(){\n    var title = \"max read:${maxRead!}M/s write:${maxWrite!}M/s busy:${maxBusy!}% iops:${maxIops!}次/s\";\n    var options = getOptions(title, \"containerDisk\");\n    options.yAxis = [{title:{text:\"\"}},{opposite: true},{opposite: true}];\n    var dread = eval(${dread!});\n    dread.tooltip = {\n      valueSuffix: 'k/s'\n    };\n    push(options.series, dread);\n    var dwrite = eval(${dwrite!});\n    dwrite.tooltip = {\n      valueSuffix: 'k/s'\n    };\n    push(options.series, dwrite);\n    var dbusy = eval(${dbusy!});\n    dbusy.tooltip = {\n      valueSuffix: '%'\n    };\n    push(options.series, dbusy);\n    var diops = eval(${diops!});\n    diops.tooltip = {\n      valueSuffix: '次/s'\n    };\n    push(options.series, diops);\n    new Highcharts.Chart(options);\n  }\n\n  function push(series, value){\n    if(value){\n      series.push(value);\n    }\n  }\n\n  function getOptions(title, renderTo, chartType){\n    var showTitle = title;\n    if(!chartType){\n      chartType = \"\";\n    }\n    var marginRight = 10;\n    if(renderTo.indexOf(\"Disk\") != -1){\n      marginRight = 30;\n    }\n    var options = {\n      chart: {\n        renderTo: renderTo,\n        animation: Highcharts.svg,\n        // backgroundColor: '#E6F1F5',\n        borderColor: '#E6E6E6',\n        borderWidth: 2,\n        plotBackgroundColor:'#FFFFFF',\n        zoomType: \"x\",\n        type: chartType,\n        marginRight: marginRight\n      },\n      title: {\n        useHTML:true,\n        text: showTitle\n      },\n      subtitle: {\n        text: ''\n      },\n      xAxis: {\n        categories: ${xAxis!},\n        tickInterval: 24\n      },\n      yAxis: {\n        title: {\n          text: ''\n        },\n        plotLines: [{\n          value: 0,\n          width: 1,\n          color: '#808080'\n        }],\n        min: 0\n      },\n      plotOptions: {\n        line: {\n          dataLabels: {\n            enabled: true\n          }\n        },\n        series: {\n          cursor: 'pointer',\n          marker: {\n            enabled: false\n          }\n        }\n      },\n      tooltip: {\n        shared:true\n      },\n      legend: {\n        enabled: true\n      },\n      credits:{\n        enabled: false\n      },\n      exporting: {\n        enabled: true\n      },\n      series: []\n    };\n    if(renderTo.indexOf(\"Cpu\") != -1 || renderTo.indexOf(\"CPU\") != -1 || renderTo.indexOf(\"Busy\") != -1 || renderTo.indexOf(\"Space\") != -1){\n      options.tooltip.pointFormat = \"<span>{series.name!}</span>:{point.y:,.f}%<br/>\";\n    }\n    if(renderTo.indexOf(\"Memory\") != -1 || renderTo.indexOf(\"Swap\") != -1){\n      options.tooltip.pointFormat = \"<span>{series.name!}</span>:{point.y:,.f}M<br/>\";\n    }\n    if(renderTo.indexOf(\"Net\") != -1 || renderTo.indexOf(\"eth\") != -1 || renderTo.indexOf(\"Read\") != -1 || renderTo.indexOf(\"Write\") != -1){\n      options.tooltip.pointFormat = \"<span>{series.name!}</span>:{point.y:,.f}k/s<br/>\";\n    }\n    if(renderTo.indexOf(\"Iops\") != -1){\n      options.tooltip.pointFormat = \"<span>{series.name!}</span>:{point.y:,.f}次/s<br/>\";\n    }\n    return options;\n  }\n</script>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/topologyExam.ftl",
    "content": "<!DOCTYPE html>\n<head>\n    <meta charset=UTF-8/>\n    <title>应用拓扑检查日报</title>\n</head>\n<body>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<p>\n<table style=\"width:100%; font-size:12px;\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <colgroup>\n        <col style=\"width: 5px;\">\n    </colgroup>\n    <tr>\n        <td></td>\n        <td style=\"padding-top:20px; padding-left:27px;\">\n        \t<ul>\n                <li><span style=\"font-weight: bold; padding-top:20px; color:#3f3f3f;\">online应用拓扑检查情况：</span></li>\n            </ul>\n            <table style=\"table-layout:fixed;width: 872px;border-collapse: collapse;word-break: break-all;word-wrap:break-word;border-top: 1px dotted #676767;text-align: center;color: #000; font-family:'宋体'; font-size:12px; margin-top:10px; margin-left: 24px\">\n                <tr>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        appId\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        应用类型\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        问题\n                    </td>\n                    <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                        描述\n                    </td>\n                </tr>\n                <#list examResult as item>\n                    <tr>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item['appId']?c}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item['type']}\n                        </td>\n                        <td style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 50px;\">\n                            ${item['status']}\n                        </td>\n                        <td align=\"left\" style=\"border-right: 1px dotted #676767; border-bottom: 1px dotted #676767; height:33px; width: 100px;\">\n                            <#list item['desc']?split(\"\\n\") as str>\n                                ${str}<br/>\n                            </#list>\n                        </td>\n                    </tr>\n                </#list>\n            </table>\n            \n        </td>\n    </tr>\n\n</table>\n</p>\n</body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/user/userRegister.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>CacheCloud用户申请</title>\n    <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\" />\n    <#include '/inc/frontResources.html'>\n    <script type=\"text/javascript\">\n      //验证邮箱格式\n      var valEmails = /^(([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.|\\-]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3};){0,6}([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.|\\-]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$/;\n      //验证手机号格式\n      var valPhones = /^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$/;\n      //验证密码强度\n      var pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}');\n\n      function encrypt(data) {\n        var key  = CryptoJS.enc.Latin1.parse('CacheCloud123456'); //16位，自定义\n        var iv   = CryptoJS.enc.Latin1.parse('CacheCloud123456'); //16位，自定义\n        return CryptoJS.AES.encrypt(data, key, {iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding}).toString();\n      }\n\n      function checkUser() {\n        var name = document.getElementById(\"name\");\n        var chName = document.getElementById(\"chName\");\n        var email = document.getElementById(\"email\");\n        var mobile = document.getElementById(\"mobile\");\n        var weChat = document.getElementById(\"weChat\");\n        var isAlert = document.getElementById(\"isAlert\");\n        if (name.value == \"\") {\n          alert(\"域账户名不能为空!\");\n          name.focus();\n          return false;\n        }\n        if (chName.value == \"\") {\n          alert(\"中文名不能为空!\");\n          chName.focus();\n          return false;\n        }\n        if (email.value == \"\") {\n          alert(\"邮箱不能为空!\");\n          email.focus();\n          return false;\n        }\n        if (!valEmails.test(email.value)) {\n          alert(\"邮箱格式错误!\");\n          email.focus();\n          return false;\n        }\n        if (mobile.value == \"\") {\n          alert(\"手机号不能为空!\");\n          mobile.focus();\n          return false;\n        }\n        if (!valPhones.test(mobile.value)) {\n          alert(\"手机号格式错误!\");\n          mobile.focus();\n          return false;\n        }\n        if (weChat.value == \"\") {\n          alert(\"微信号不能为空!\");\n          weChat.focus();\n          return false;\n        }\n        return true;\n      }\n\n      function checkUserNameExist(id) {\n        var userName = document.getElementById(id).value;\n        if (userName != '') {\n          $.post(\n                  '${request.contextPath}/user/checkUserNameExist',\n                  {\n                    userName: userName,\n                  },\n                  function (data) {\n                    if (data == 1) {\n                      alert(\"用户名已经存在，请修改或者联系管理员\");\n                      document.getElementById(id).focus();\n                      document.getElementById(id).value = \"\";\n                    }\n                  }\n          );\n        }\n      }\n\n      function checkPassword() {\n\n        var password = $('#password').val();\n        if (!pwdRegex.test(password)) {\n          alert(\"您的密码复杂度太低，密码中必须包含字母、数字、特殊字符至少8个字符，请修改密码！\");\n          $('#password').focus();\n          return false;\n        }\n        return true;\n      }\n\n      function checkConfirmPassword() {\n        var password = $('#password').val();\n        var password1 = $('#password1').val();\n        if (password != password1) {\n          alert(\"两次密码输入不一致，请确认密码！\")\n          $('#password1').focus();\n          return false;\n        }\n        return true;\n      }\n\n      function registerUser(pwdFlag) {\n        var company = \"\";\n        var purpose = \"\";\n        var encryPwd = \"\";\n        if(pwdFlag){\n          if (!checkPassword()) {\n            return;\n          }\n          if (!checkConfirmPassword()) {\n            return;\n          }\n          var password = document.getElementById(\"password\");\n          encryPwd = $.md5(password.value);\n        }\n        if(!checkUser()){\n          return;\n        }\n\n        var name = document.getElementById(\"name\");\n        var chName = document.getElementById(\"chName\");\n        var email = document.getElementById(\"email\");\n        var mobile = document.getElementById(\"mobile\");\n        var weChat = document.getElementById(\"weChat\");\n        var type = document.getElementById(\"type\");\n        var isAlert = document.getElementById(\"isAlert\");\n        var bizId = document.getElementById(\"bizId\");\n\n        var userBtn = document.getElementById(\"registerUserBtn\");\n        userBtn.disabled = true;\n\n        $.post(\n                '${request.contextPath}/user/apply',\n                {\n                  name: name.value,\n                  chName: chName.value,\n                  email: email.value,\n                  mobile: mobile.value,\n                  weChat: weChat.value,\n                  type: type.value,\n                  isAlert: isAlert.value,\n                  password: encryPwd,\n                  company: company,\n                  purpose: purpose,\n                  bizId: bizId.value\n                },\n                function (data) {\n                  $(\"#allBody\").html(data);//刷新整个body页面的html\n                }\n        );\n      }\n\n    </script>\n    <script src=\"${request.contextPath}/assets/vendor/jquery/jquery.md5.js\" type=\"text/javascript\"></script>\n  </head>\n\n  <body class=\"hold-transition sidebar-mini layout-navbar-fixed\" id=\"allBody\">\n    <div class=\"wrapper\">\n    <#include \"/inc/head.html\">\n      <div class=\"content-wrapper ml-0\">\n        <div class=\"content\">\n          <div class=\"container\">\n            <div id=\"systemAlert\">\n            </div>\n        <!-- stat info start -->\n            <div class=\"row\">\n              <div class=\"col-12\">\n                <div class=\"card\">\n                  <div class=\"card-header\">\n                    <h3 class=\"card-title\">\n                      CacheCloud用户申请\n                      <font color='red' size=\"4\">\n                        <#if success?? && (success == 1)>(申请成功，请关注邮件中审批进度)\n                        <#elseif success?? && (success == 0)>(申请失败，请确认填写信息)\n                        </#if>\n                      </font>\n                    </h3>\n                  </div>\n                  <div class=\"card-body\">\n                    <div class=\"row\">\n                      <div class=\"col-md-12\">\n                          <div class=\"form\">\n                            <!-- BEGIN FORM-->\n                            <form method=\"post\"\n                                  class=\"form-horizontal form-bordered form-row-stripped\">\n                              <div class=\"form-body\">\n\n                                <div class=\"form-group row\">\n                                  <label class=\"col-form-label col-md-4 text-end\">\n                                    账户名:\n                                  </label>\n                                  <div class=\"col-md-6\">\n                                    <input type=\"text\" name=\"name\" id=\"name\" placeholder=\"域账户名(邮箱前缀)\"\n                                           class=\"form-control\" onchange=\"checkUserNameExist(this.id)\"/>\n                                  </div>\n                                </div>\n\n                                <#if pwdswitch?? && pwdswitch>\n                                  <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-4 text-end\">\n                                      密码:\n                                    </label>\n                                    <div class=\"col-md-6\">\n                                      <input type=\"password\" name=\"password\" id=\"password\" placeholder=\"输入密码\"\n                                             class=\"form-control\" onchange=\"checkPassword()\"/>\n                                      <span class=\"help-block\">密码中必须包含字母、数字，至少8个字符</span>\n                                    </div>\n                                  </div>\n\n                                  <div class=\"form-group row\">\n                                    <label class=\"col-form-label col-md-4 text-end\">\n                                      确认密码:\n                                    </label>\n                                    <div class=\"col-md-6\">\n                                      <input type=\"password\" name=\"password1\" id=\"password1\" placeholder=\"再次输入密码\"\n                                             class=\"form-control\" onchange=\"checkConfirmPassword()\"/>\n                                    </div>\n                                  </div>\n                                </#if>\n\n                                <div class=\"form-group row\">\n                                  <label class=\"col-form-label col-md-4 text-end\">\n                                    中文名:\n                                  </label>\n                                  <div class=\"col-md-6\">\n                                    <input type=\"text\" name=\"chName\" id=\"chName\" placeholder=\"中文名\"\n                                           class=\"form-control\"/>\n                                  </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                  <label class=\"col-form-label col-md-4 text-end\">\n                                    邮箱:\n                                  </label>\n                                  <div class=\"col-md-6\">\n                                    <input type=\"text\" name=\"email\" id=\"email\" placeholder=\"邮箱\"\n                                           class=\"form-control\"/>\n                                  </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                  <label class=\"col-form-label col-md-4 text-end\">\n                                    手机:\n                                  </label>\n                                  <div class=\"col-md-6\">\n                                    <input type=\"text\" id=\"mobile\" name=\"mobile\" placeholder=\"手机\"\n                                           class=\"form-control\"/>\n                                  </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                  <label class=\"col-form-label col-md-4 text-end\">\n                                    微信:\n                                  </label>\n                                  <div class=\"col-md-6\">\n                                    <input type=\"text\" id=\"weChat\" name=\"weChat\" placeholder=\"微信\"\n                                           class=\"form-control\"/>\n                                  </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                  <label class=\"col-form-label col-md-4 text-end\">\n                                    是否接收报警:\n                                  </label>\n                                  <div class=\"col-md-6\">\n                                    <select name=\"isAlert\" id=\"isAlert\" class=\"form-select\">\n                                      <option value=\"1\" selected>\n                                        是\n                                      </option>\n                                      <option value=\"0\">\n                                        否\n                                      </option>\n                                    </select>\n                                  </div>\n                                </div>\n\n                                <div class=\"form-group row\">\n                                  <label class=\"col-form-label col-md-4 text-end\">\n                                    所属业务组:\n                                  </label>\n                                  <div class=\"col-md-6\">\n                                    <select id=\"bizId\" name=\"bizId\" class=\"form-select\">\n                                      <option value=\"\">请选择归属业务组，如无您的业务组，请联系管理员</option>\n                                      <#list bizList as biz>\n                                        <option value=\"${biz.id!}\">${biz.name!}</option>\n                                      </#list>\n                                    </select>\n                                  </div>\n                                </div>\n\n                                <input type=\"hidden\" id=\"type\" name=\"type\" value=\"-1\">\n                                <br/><br/>\n                                <div class=\"form-group row\">\n                                  <div class=\"col-md-12\">\n                                    <div class=\"text-center\">\n                                      <button id=\"registerUserBtn\" type=\"submit\" class=\"btn btn-success\" onclick=\"registerUser(${pwdswitch?c})\"\n                                      <#if success?? && (success == 1)>disabled=\"disabled\"</#if>>\n                                      <i class=\"bi bi-check\"></i>\n                                      提交申请\n                                      </button>\n                                      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n                                      <button type=\"reset\" class=\"btn btn-success\">\n                                        <i class=\"bi bi-check\"></i>\n                                        &nbsp;&nbsp;重&nbsp;&nbsp;&nbsp;置&nbsp;&nbsp;\n                                      </button>\n                                    </div>\n                                  </div>\n                                </div>\n                              </div>\n                            </form>\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      <#include \"/inc/footer.html\">\n    </div>\n  </body>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/wikiAccessClientTemplate.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<meta charset=\"utf-8\">\n<meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n<title>CacheCloud wiki</title>\n<link href=\"${request.contextPath}/assets/vendor/bootstrap/css/bootstrap.min.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-icons/bootstrap-icons.css\" rel=\"stylesheet\">\n\n<!-- Google Font: Source Sans Pro -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/vendor/fonts/fonts-googleapis-source-sans-pro.css\">\n<!-- Font Awesome -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/fontawesome-free/css/all.min.css\">\n<!-- Theme style -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/dist/css/adminlte.min.css\">\n<!-- overlayScrollbars -->\n<link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/overlayScrollbars/css/OverlayScrollbars.min.css\">\n\n<link href=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.css\" rel=\"stylesheet\" />\n<!--<link href=\"${request.contextPath}/resources/prettify/prettify.css\" rel=\"stylesheet\">-->\n<link href=\"${request.contextPath}/assets/css/githubmd.css\" rel=\"stylesheet\">\n<link href=\"${request.contextPath}/assets/css/common.css\" rel=\"stylesheet\" type=\"text/css\"/>\n\n<script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/apexcharts/apexcharts.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap/js/bootstrap.bundle.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/chart.js/chart.umd.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/echarts/echarts.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/quill/quill.min.js\"></script>\n<script src=\"${request.contextPath}/assets/js/custom/appDetail.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/tinymce/tinymce.min.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.js\"></script>\n<script src=\"${request.contextPath}/assets/vendor/highchart/highcharts.js\"></script>\n<script src=\"${request.contextPath}/assets/js/myhighchart.js\"></script>\n<!-- 滚动插件 -->\n<!--  <script src=\"/resources/nicescroll/jquery.nicescroll.min.js\"></script>-->\n<script type=\"text/javascript\" src=\"${request.contextPath}/assets/plugins/overlayScrollbars/js/OverlayScrollbars.min.js\"></script>\n<!--<script type=\"text/javascript\" src=\"${request.contextPath}/resources/prettify/prettify.js\"></script>-->\n<head>\n  <title>CacheCloud</title>\n</head>\n<body>\n  <div class=\"card\">\n    <div class=\"card-body\">\n      ${response}\n    </div>\n  </div>\n</body>\n<script>\n  $(function(){\n    // $(\"body\").niceScroll({\n    //   cursorcolor:\"#ddd\",\n    //   cursorwidth:\"12px\",\n    //   cursorminheight: 64,\n    //   horizrailenabled : false\n    // });\n    $('pre').each(function(i, pre) {\n      $(pre).addClass(\"prettyprint\");\n    });\n    PR.prettyPrint();\n    if(window.location.hash && $(window.location.hash).offset()){\n      $(\"html,body\").animate({scrollTop: $(window.location.hash).offset().top - 52}, 500);\n    }\n    $(\"#introLi\").addClass(\"active\");\n  });\n</script>\n</html>"
  },
  {
    "path": "cachecloud-web/src/main/resources/templates/wikiTemplate.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\">\n  <title>CacheCloud wiki</title>\n\n  <link href=\"${request.contextPath}/assets/vendor/bootstrap/css/bootstrap.min.css\" rel=\"stylesheet\">\n  <link href=\"${request.contextPath}/assets/vendor/bootstrap-icons/bootstrap-icons.css\" rel=\"stylesheet\">\n\n  <!-- Google Font: Source Sans Pro -->\n  <link rel=\"stylesheet\" href=\"${request.contextPath}/assets/vendor/fonts/fonts-googleapis-source-sans-pro.css\">\n  <!-- Font Awesome -->\n  <link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/fontawesome-free/css/all.min.css\">\n  <!-- Theme style -->\n  <link rel=\"stylesheet\" href=\"${request.contextPath}/assets/dist/css/adminlte.min.css\">\n  <!-- overlayScrollbars -->\n  <link rel=\"stylesheet\" href=\"${request.contextPath}/assets/plugins/overlayScrollbars/css/OverlayScrollbars.min.css\">\n\n  <link href=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.css\" rel=\"stylesheet\" />\n\n  <link href=\"${request.contextPath}/assets/css/githubmd.css\" rel=\"stylesheet\">\n  <link href=\"${request.contextPath}/assets/css/common.css\" rel=\"stylesheet\" type=\"text/css\"/>\n\n  <script src=\"${request.contextPath}/assets/vendor/jquery/jquery-3.7.0.min.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/apexcharts/apexcharts.min.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/bootstrap/js/bootstrap.bundle.min.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/chart.js/chart.umd.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/echarts/echarts.min.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/quill/quill.min.js\"></script>\n  <script src=\"${request.contextPath}/assets/js/custom/appDetail.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/tinymce/tinymce.min.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/bootstrap-select/bootstrap-select.js\"></script>\n  <script src=\"${request.contextPath}/assets/vendor/highchart/highcharts.js\"></script>\n  <script src=\"${request.contextPath}/assets/js/myhighchart.js\"></script>\n  <!-- 滚动插件 -->\n<!--  <script src=\"/resources/nicescroll/jquery.nicescroll.min.js\"></script>-->\n  <script type=\"text/javascript\" src=\"${request.contextPath}/assets/plugins/overlayScrollbars/js/OverlayScrollbars.min.js\"></script>\n</head>\n<body class=\"hold-transition sidebar-mini layout-fixed\">\n  <div class=\"wrapper\">\n    <#include '/inc/wikiHead.html'>\n    <#include \"/inc/wikiLeft.html\">\n\n    <div class=\"content-wrapper\">\n      <section class=\"content pt-3\">\n        <div class=\"container-fluid\">\n          <div class=\"row\">\n            <div class=\"col-12\">\n              <div class=\"card\">\n                <div class=\"card-body\">\n                  <div class=\"row col-sm-12 col-xl-12 col-12 col-md-12\">\n                    <#if toc??>\n                      <div class=\"col-xl-9\">\n                    <#else>\n                      <div class=\"col-md-12\">\n                    </#if>\n                      <div class=\"markdown-body\">\n                        ${response!}\n                      </div>\n                    </div>\n                    <#if toc??>\n                      <div class=\"col-xl-3\">\n                        <div class=\"d-flex flex-column gap-2 justify-content-end\" style=\"position:fixed; z-index: 1030;\">${toc!}</div>\n                      </div>\n                    </#if>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n  <#include \"/manage/inc/footer.html\">\n  <a href=\"#\" class=\"back-to-top d-flex align-items-center justify-content-center\"><i class=\"bi bi-arrow-up-short\"></i></a>\n</div>\n<script>\n$(function(){\n  OverlayScrollbars($(\"#leftBox\"));\n  $('pre').each(function(i, pre) {\n    $(pre).addClass(\"prettyprint\");\n  });\n  PR.prettyPrint();\n  if(window.location.hash && $(window.location.hash).offset()){\n    $(\"html,body\").animate({scrollTop: $(window.location.hash).offset().top - 52}, 500);\n  }\n  $(\"#introLi\").addClass(\"active\");\n});\n</script>\n</body>\n<#include \"/manage/inc/backendEndResources.html\">\n</html>\n"
  },
  {
    "path": "cachecloud-web/src/main/webapp/WEB-INF/web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<web-app version=\"3.0\"\n         xmlns=\"http://java.sun.com/xml/ns/javaee\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee\n            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\">\n<!-- use empty -->\n</web-app>"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>com.sohu.tv</groupId>\n    <artifactId>cachecloud-parent</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0-SNAPSHOT</version>\n    <modules>\n        <module>cachecloud-web</module>\n        <module>cachecloud-custom</module>\n    </modules>\n\n    <properties>\n        <spring.boot.version>2.2.9.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR4</spring.cloud.version>\n        <logback.version>1.2.11</logback.version>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <quartz.version>2.2.1</quartz.version>\n        <mysql.java.version>8.0.29</mysql.java.version>\n        <mybatis-spring-boot.version>1.3.1</mybatis-spring-boot.version>\n        <junit.version>4.13.1</junit.version>\n        <jedis.version>3.7.0</jedis.version>\n        <protostuff.version>1.0.8</protostuff.version>\n        <jackson.version>2.3.2</jackson.version>\n        <httpcomponents.version>4.5.13</httpcomponents.version>\n        <apache.commons.collections>4.4</apache.commons.collections>\n        <maven.compiler.plugin>3.1</maven.compiler.plugin>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.surefire.plugin>2.16</maven.surefire.plugin>\n        <maven.clean.plugin>2.5</maven.clean.plugin>\n        <ganymed.ssh.version>build210</ganymed.ssh.version>\n        <sshj.version>0.26.0</sshj.version>\n        <commons.collections.version>3.2.1</commons.collections.version>\n        <guava.version>32.1.2-jre</guava.version>\n        <janino.version>3.0.6</janino.version>\n        <commons-lang.version>3.8.1</commons-lang.version>\n        <json-lib.version>2.4</json-lib.version>\n        <commons.configuration.version>1.6</commons.configuration.version>\n        <swagger-starter.version>1.0.3-SNAPSHOT</swagger-starter.version>\n        <flexmark.version>0.42.12</flexmark.version>\n        <fastjson.version>1.2.83</fastjson.version>\n        <hystrix.version>1.5.6</hystrix.version>\n        <maven.deloy.plugin>2.8.1</maven.deloy.plugin>\n        <maven-source-plugin>2.2.1</maven-source-plugin>\n        <maven-resources-plugin>2.6</maven-resources-plugin>\n        <logstash-logback-encoder.version>5.3</logstash-logback-encoder.version>\n        <!-- open -->\n        <cachecloud-cloud.version>1.0-SNAPSHOT</cachecloud-cloud.version>\n        <springfox-swagger2.version>2.9.2</springfox-swagger2.version>\n        <swagger-ui.version>2.9.2</swagger-ui.version>\n        <apache.ant.version>1.10.11</apache.ant.version>\n        <ssh-version>2.9.2</ssh-version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.quartz-scheduler</groupId>\n                <artifactId>quartz</artifactId>\n                <version>${quartz.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <artifactId>slf4j-api</artifactId>\n                        <groupId>org.slf4j</groupId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <dependency>\n                <groupId>ch.qos.logback</groupId>\n                <artifactId>logback-classic</artifactId>\n                <version>${logback.version}</version>\n                <scope>compile</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>mysql</groupId>\n                <artifactId>mysql-connector-java</artifactId>\n                <version>${mysql.java.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>junit</groupId>\n                <artifactId>junit</artifactId>\n                <version>${junit.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>javax.servlet</groupId>\n                <artifactId>javax.servlet-api</artifactId>\n                <version>3.1.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>redis.clients</groupId>\n                <artifactId>jedis</artifactId>\n                <version>${jedis.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.dyuproject.protostuff</groupId>\n                <artifactId>protostuff-runtime</artifactId>\n                <version>${protostuff.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.dyuproject.protostuff</groupId>\n                <artifactId>protostuff-core</artifactId>\n                <version>${protostuff.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.httpcomponents</groupId>\n                <artifactId>httpclient</artifactId>\n                <version>${httpcomponents.version}</version>\n            </dependency>\n\n            <!-- SSH远程 -->\n            <dependency>\n                <groupId>org.apache.sshd</groupId>\n                <artifactId>sshd-core</artifactId>\n                <version>${ssh-version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.sshd</groupId>\n                <artifactId>sshd-scp</artifactId>\n                <version>${ssh-version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-collections4</artifactId>\n                <version>${apache.commons.collections}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.guava</groupId>\n                <artifactId>guava</artifactId>\n                <version>${guava.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-lang3</artifactId>\n                <version>${commons-lang.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>net.sf.json-lib</groupId>\n                <artifactId>json-lib</artifactId>\n                <version>${json-lib.version}</version>\n                <classifier>jdk15</classifier>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-configuration</groupId>\n                <artifactId>commons-configuration</artifactId>\n                <version>${commons.configuration.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>${fastjson.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.netflix.hystrix</groupId>\n                <artifactId>hystrix-core</artifactId>\n                <version>${hystrix.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.codehaus.janino</groupId>\n                <artifactId>janino</artifactId>\n                <version>${janino.version}</version>\n            </dependency>\n\n            <!--markdown to html-->\n            <dependency>\n                <groupId>com.vladsch.flexmark</groupId>\n                <artifactId>flexmark</artifactId>\n                <version>${flexmark.version}</version>\n            </dependency>\n            <!--表格渲染插件-->\n            <dependency>\n                <groupId>com.vladsch.flexmark</groupId>\n                <artifactId>flexmark-ext-tables</artifactId>\n                <version>${flexmark.version}</version>\n            </dependency>\n            <!--markdown to html end-->\n\n            <dependency>\n                <groupId>org.mybatis.spring.boot</groupId>\n                <artifactId>mybatis-spring-boot-starter</artifactId>\n                <version>${mybatis-spring-boot.version}</version>\n            </dependency>\n\n            <!-- sohu dependency -->\n            <dependency>\n                <groupId>com.sohu.tv.custom</groupId>\n                <artifactId>cachecloud-custom</artifactId>\n                <version>${cachecloud-cloud.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.springfox</groupId>\n                <artifactId>springfox-swagger2</artifactId>\n                <version>${springfox-swagger2.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.springfox</groupId>\n                <artifactId>springfox-swagger-ui</artifactId>\n                <version>${swagger-ui.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.ant</groupId>\n                <artifactId>ant</artifactId>\n                <version>${apache.ant.version}</version>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>${maven.compiler.plugin}</version>\n                    <configuration>\n                        <source>${maven.compiler.target}</source>\n                        <target>${maven.compiler.target}</target>\n                    </configuration>\n                </plugin>\n\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>${maven.surefire.plugin}</version>\n                    <configuration>\n                        <skipTests>true</skipTests>\n                    </configuration>\n                </plugin>\n\n                <plugin>\n                    <artifactId>maven-clean-plugin</artifactId>\n                    <version>${maven.clean.plugin}</version>\n                    <configuration>\n                        <filesets>\n                            <fileset>\n                                <directory>${basedir}/src/main/webapp/WEB-INF/classes</directory>\n                            </fileset>\n                            <fileset>\n                                <directory>${basedir}/src/main/webapp/WEB-INF/lib</directory>\n                            </fileset>\n                        </filesets>\n                    </configuration>\n                </plugin>\n\n                <plugin>\n\t            \t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t<artifactId>maven-deploy-plugin</artifactId>\n\t\t\t\t\t<version>${maven.deloy.plugin}</version>\n\t\t\t\t</plugin>\n\n\t\t\t\t<plugin>\n\t\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t<artifactId>maven-source-plugin</artifactId>\n\t\t\t\t\t<version>${maven-source-plugin}</version>\n\t\t\t\t\t<configuration>\n\t\t\t\t\t\t<attach>true</attach>\n\t\t\t\t\t</configuration>\n\t\t\t\t\t<executions>\n\t\t\t\t\t\t<execution>\n\t\t\t\t\t\t\t<phase>compile</phase>\n\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t<goal>jar</goal>\n\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t</execution>\n\t\t\t\t\t</executions>\n\t\t\t\t</plugin>\n\n\t\t\t\t<plugin>\n\t                <groupId>org.apache.maven.plugins</groupId>\n\t                <artifactId>maven-resources-plugin</artifactId>\n\t                <version>${maven-resources-plugin}</version>\n\t                <configuration>\n\t                    <encoding>UTF-8</encoding>\n\t                </configuration>\n\t            </plugin>\n\n            </plugins>\n        </pluginManagement>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>central</id>\n            <url>http://central.maven.org/maven2/</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </repository>\n        <repository>\n            <id>com.springsource.repository.maven.release</id>\n            <url>http://repo.springsource.org/release/</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </repository>\n        <repository>\n            <id>aliyunmaven</id>\n            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "redis-ecs/help.md",
    "content": "### 资源说明\n\n由于github不能上传过大资源包，这里提供临时的下载地址：[资源下载](http://43.137.44.6/redis-ecs/)\n\n### 1. 准备Redis二进制资源包： \n\n- 1) 资源下载: [下载Redis资源包](http://download.redis.io/releases)；\n- 2) 在宿主/容器环境安装(make)redis资源包，对make编译完成资源重新打包redis-*.*.*-make.tar.gz；\n- 3) 当前资源编译环境：redis3/4/5的make包编译要求操作系统环境：CentOS release 6.9 (Final) gcc version 4.8.5，redis6的make包编译要求操作系统环境：CentOS release 6.9 (Final) gcc version 7.3.0 (提示：如果使用的centos操作系统版本较高，可以对redis进行重新编译，当前make资源包不一定能够允许成功)\n        \n### 2. 文件说明：\n\n- 1) cachecloud-web.war: 可直接启动cachecloud中台，详情参考： [快速接入](./cachecloud-web/src/main/resources/static/wiki/quickstart/index.md)\n\t\n\t\tnohup java -jar -Dspring.profiles.active=open cachecloud-web.war &\n\n- 2) script脚本：\n\t- cachecloud-init.sh: 初始化宿主环境(物理机/虚拟机)系统变量，Redis机器相关资源等，执行：sh cachecloud-init.sh [username];\n\t- redis-install.sh: 安装单个redis版本, 执行: sh redis-install.sh [username] [redisTarGz]；\n\t- redisShake-install.sh: 安装redis-shake-v2.0.3，执行：sh redisShake-install.sh [username]；\n\t- ssh-keygen.sh: 创建公钥私钥，执行：sh ssh-keygen.sh [username]；\n\n- 3) redis-*.*.*-make.tar.gz:\n\t- Redis各大版本的make资源包（已经编译好的包），如果需要到其他版本可自行添加。(提示：资源编译需要注意当前机器操作系统和gcc版本)\n\n- 4）相关Redis工具包: \n\t- redis-shake-v2.0.3.tar.gz：redis数据迁移工具包；\n\t- redis-full-check-1.4.4.tar.gz：redis数据校验包 ；\n\t- 如需升级版本可在官方下载最新 [Redis-shake](https://github.com/alibaba/RedisShake/releases) & [Redis-full-check](https://github.com/alibaba/RedisFullCheck/releases)  。\n  \n### 3. 部署流程:\n   \n  - 1) 使用root登录目标服务器;\n  - 2) 将cachecloud-init.sh脚本拷贝到目标服务器当前用户目录下/opt ;\n  - 3) 执行 sh cachecloud-init.sh [username]，默认username=\"cachecloud-open\"；\n  - 4) 设置密码;\n  - 5) 一路安装直到成功；\n  - 6) 通过cachecloud后台可以对当前机器进行Redis实例部署。\n"
  },
  {
    "path": "redis-ecs/script/cachecloud-init.sh",
    "content": "#!/bin/bash\n\n############################################################################\n# @desc:\n# 1. initial config\n# 2. add system config\n#\t3. create user;\n#\t4. create default directories and authorize;\n# 5. install sshpass tool\n# 6. install redis\n#\t@usage: sh cachecloud-init.sh [username]\n###########################################################################\n\nset -o nounset\nset -o errexit\n\n# Redis6.0及以上版本需要依赖操作系统gcc 4.9.0以上版本编译\nredis_array=(\"redis-3.0.7\" \"redis-3.2.12\" \"redis-4.0.14\" \"redis-5.0.9\")\n\n# initial config\ninitConfig() {\n  sysctl vm.overcommit_memory=1\n  echo 511 >/proc/sys/net/core/somaxconn\n  echo never >/sys/kernel/mm/transparent_hugepage/enabled\n  echo never >/sys/kernel/mm/transparent_hugepage/defrag\n  echo 0 >/proc/sys/vm/swappiness &\n  nohup swapoff -a >swap.out 2>&1\n  echo -e \"\\033[41;36m OK: initial config done. \\033[0m\"\n}\n\n# add system config\ninitSysConfig() {\n  echo \"vm.overcommit_memory = 1\" >>/etc/sysctl.conf\n  echo 'vm.swappiness=0' >>/etc/sysctl.conf\n  echo \"echo 511 > /proc/sys/net/core/somaxconn\" >>/etc/rc.d/rc.local\n  echo \"echo never >  /sys/kernel/mm/transparent_hugepage/enabled\" >>/etc/rc.d/rc.local\n  echo \"echo never >  /sys/kernel/mm/transparent_hugepage/defrag\" >>/etc/rc.d/rc.local\n  echo -e \"\\033[41;36m OK: add system config done. \\033[0m\"\n}\n\n# check if the user exists\ncheckExist() {\n  local num=$(cat /etc/passwd | grep -w $1 | wc -l)\n\n  #cat /etc/passwd | grep -q \"$1\"\n  if [[ $num == 1 ]]; then\n    echo -e \"\\033[41;36m user $1 exists, overwrite user and init all data*: [y/n]? \\033[0m\"\n    read replace\n    if [[ ${replace} == \"y\" ]]; then\n      echo -e \"\\033[41;36m delete existed user: $1. \\033[0m\"\n      userdel -r \"$1\"\n      createUser \"$1\" \"$2\"\n      init \"$1\"\n      return 0\n    fi\n  else\n    createUser \"$1\" \"$2\"\n    init \"$1\"\n  fi\n  return 0\n}\n\n# create the user\ncreateUser() {\n  # create a user\n  groupadd -g 400 $1\n  useradd -u 400 -g 400 -m -d /home/$1 -s /bin/bash $1\n\n  # give the user a password\n  echo $2 | passwd --stdin $1\n\n  # add the user to sudoers\n  #   echo \"$1   ALL=(ALL)   ALL\" >> /etc/sudoers\n\n  #  Maximum number of days between password change\n  chage -M 9999 $1\n  echo -e \"\\033[41;36m OK: create user: $1 done. \\033[0m\"\n}\n\n# create defautl dirs and authorize\ninit() {\n  # create working dirs and a tmp dir\n  mkdir -p /opt/cachecloud/conf\n  mkdir -p /opt/cachecloud/data\n  mkdir -p /opt/cachecloud/logs\n  mkdir -p /tmp/cachecloud\n  mkdir -p /data/redis\n\n  # change owner\n  chown -R $1:$1 /opt/cachecloud\n  chown -R $1:$1 /tmp/cachecloud\n  chown -R $1:$1 /home/$1\n  chown -R $1 /var/run\n  chown -R $1:$1 /data/redis\n  echo -e \"\\033[41;36m OK: init done. \\033[0m\"\n}\n\n# install sshpass tool\ninstallSshpass() {\n  yum install -y sshpass\n  echo -e \"\\033[41;36m OK: install sshpass tool done. \\033[0m\"\n}\n\n# output\noutput() {\n  echo \"somaxconn=\"\n  cat /proc/sys/net/core/somaxconn\n  echo \"overcommit_memory=\"\n  cat /proc/sys/vm/overcommit_memory\n  echo \"transparent_hugepage/enabled=\"\n  cat /sys/kernel/mm/transparent_hugepage/enabled\n  echo \"transparent_hugepage/defrag=\"\n  cat /sys/kernel/mm/transparent_hugepage/defrag\n  echo \"swappiness=\"\n  cat /proc/sys/vm/swappiness\n  echo \"user=\"\n  cat /etc/passwd | grep cachecloud\n  echo \"auth_key=\"\n  cat /home/$1/.ssh/authorized_keys\n  echo \"sshpass=\"\n  sshpass -V | head -1\n\n  echo -e \"\\033[41;36m OK: config output done. \\033[0m\"\n}\n\n# install redis\ninstallRedis() {\n  yum install -y gcc\n  for redisVersion in ${redis_array[*]}; do\n    local redisDir=\"/opt/cachecloud/${redisVersion}\"\n    local redisTarGz=\"${redisVersion}.tar.gz\"\n    mkdir -p ${redisDir} && cd ${redisDir}\n    wget http://download.redis.io/releases/${redisTarGz} && tar zxvf ${redisTarGz} --strip-component=1\n    make && make install\n\n    if [[ $? == 0 ]]; then\n      echo -e \"\\033[41;36m OK: ${redisTarGz} is installed, exit. \\033[0m\"\n      chown -R $1:$2 ${redisDir}\n      if [[ ${redisVersion} == \"redis-5.0.9\" ]]; then\n        export PATH=$PATH:${redisDir}/src\n        echo $PATH\n      fi\n    else\n      echo -e \"\\033[41;36m ERROR: ${redisTarGz} is NOT installed, exit. \\033[0m\"\n    fi\n  done\n  return\n}\n\n# let's go\nusername=\"cachecloud-open\"\npassword=\"cachecloud-open\"\nif [[ $# > 0 && -n \"$1\" ]]; then\n  username=\"$1\"\n  echo -e \"\\033[41;36m please set password for user: ${username} \\033[0m\"\n  stty -echo\n  read password\n  stty echo\nfi\necho -e \"\\033[41;36m use username: ${username}. \\033[0m\"\n# 1. initial config\ninitConfig\n# 2. add system config\ninitSysConfig\n# 3. check & create user\ncheckExist \"${username}\" \"${password}\"\n# 4.install sshpass tool\ninstallSshpass\n# 5.output\noutput \"${username}\"\n# 6.install install\ninstallRedis \"${username}\" \"${password}\"\n"
  },
  {
    "path": "redis-ecs/script/redis-install.sh",
    "content": "#!/bin/bash\n\n############################################################################\n# @desc:\n#\t- 1. install redis;\n#\t- 2. @usage: sh redis-install.sh [username] [redisTarGz]\n###########################################################################\n\nset -o nounset\nset -o errexit\n\n# install redis，Redis6.0及以上版本需要依赖操作系统gcc 4.9.0以上版本编译\ninstallRedis() {\n  yum install -y gcc\n  local redisDir=\"/opt/cachecloud/$3\"\n  local redisTarGz=\"$3.tar.gz\"\n  mkdir -p ${redisDir} && cd ${redisDir}\n  wget http://download.redis.io/releases/${redisTarGz} && tar zxvf ${redisTarGz} --strip-component=1\n  make && make install\n  if [[ $? == 0 ]]; then\n    echo -e \"\\033[41;36m OK: ${redisTarGz} is installed, exit. \\033[0m\"\n    chown -R $1:$2 ${redisDir}\n    return\n  fi\n  echo -e \"\\033[41;36m ERROR: ${redisTarGz} is NOT installed, exit. \\033[0m\"\n}\n\n# main entrance\nredisVersion=\"redis-5.0.9\"\nusername=\"cachecloud-open\"\npassword=\"cachecloud-open\"\nif [[ $# > 0 && -n \"$1\" ]]; then\n  username=\"$1\"\n  echo -e \"\\033[41;36m please input password for user: ${username} \\033[0m\"\n  stty -echo\n  read password\n  stty echo\nfi\necho -e \"\\033[41;36m use username: ${username} \\033[0m\"\nif [[ $# > 1 && -n \"$2\" ]]; then\n  redisVersion=\"$2\"\n  echo -e \"\\033[41;36m install redisVersion: $2 \\033[0m\"\nfi\ninstallRedis \"${username}\" \"${password}\" \"${redisVersion}\"\n"
  },
  {
    "path": "redis-ecs/script/redisShake-install.sh",
    "content": "#!/bin/bash\n\n############################################################################\n# @desc:\n#\t- 1. install redis-shake;\n#\t- 2. @usage: sh redisShake-install.sh [username]\n###########################################################################\n\nset -o nounset\nset -o errexit\n\n# install redis-shake\ninstallRedisShake() {\n   local redisShakeDir=\"/opt/cachecloud/redis-shake-2.0.3\"\n   local redisShakeTarGz=\"redis-shake-v2.0.3.tar.gz\"\n\t mkdir -p ${redisShakeDir} && cd ${redisShakeDir}\n   wget https://github.com/alibaba/RedisShake/releases/download/release-v2.0.3-20200724/${redisShakeTarGz} && tar zxvf ${redisShakeTarGz} --strip-component=2\n   if [[ $? == 0 ]]; then\n    echo -e \"\\033[41;36m OK: ${redisShakeTarGz} is installed, exit. \\033[0m\"\n\t\tchown -R $1:$2 ${redisShakeDir}\n\t\treturn\n\t fi\n\t echo -e \"\\033[41;36m ERROR: ${redisShakeTarGz} is NOT installed, exit. \\033[0m\"\n}\n\n\n# main entrance\nusername=\"cachecloud-open\"\npassword=\"cachecloud-open\"\nif [[ $# > 0 && -n \"$1\" ]]; then\n  username=\"$1\"\n  echo -e \"\\033[41;36m please input password for user: ${username} \\033[0m\"\n  stty -echo\n  read password\n  stty echo\nfi\necho -e \"\\033[41;36m use username: ${username} \\033[0m\"\ninstallRedisShake \"${username}\" \"${password}\"\n"
  },
  {
    "path": "redis-ecs/script/ssh-keygen.sh",
    "content": "# create public key & secret key\ncreateSshkey() {\n  mkdir -p /opt/ssh\n  chown -R $1:$2 /opt/ssh\n\n  ssh-keygen -t rsa -f /opt/ssh/id_rsa -P '' -C $1\n  local privateKeyFile=\"/opt/ssh/id_rsa\"\n  chmod 600 ${privateKeyFile}\n\n  mkdir -p /home/$1/.ssh\n  local publicKeyFile=\"/home/$1/.ssh/authorized_keys\"\n  cat /opt/ssh/id_rsa.pub >>${publicKeyFile}\n  chown -R $1:$2 /home/$1/.ssh\n  chmod 755 ${publicKeyFile}\n\n  echo -e \"\\033[41;36m OK: create public key & secret key done. \\033[0m\"\n}\n\n\n\n# let's go\nusername=\"cachecloud-open\"\npassword=\"cachecloud-open\"\nif [[ $# > 0 && -n \"$1\" ]]; then\n  username=\"$1\"\n  echo -e \"\\033[41;36m please set password for user: ${username} \\033[0m\"\n  stty -echo\n  read password\n  stty echo\nfi\necho -e \"\\033[41;36m use username: ${username}. \\033[0m\"\n# create public key & secret key\ncreateSshkey \"${username}\" \"${password}\""
  }
]